Skip to content

Commit 20f768e

Browse files
committed
fix: 修复调试模块无法识别 agent custom 的问题 #84
1 parent 6fe5285 commit 20f768e

4 files changed

Lines changed: 169 additions & 35 deletions

File tree

LocalBridge/internal/debug/runtime/agent_pool.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import (
1010
"github.com/kqcoxn/MaaPipelineEditor/LocalBridge/internal/mfw"
1111
)
1212

13+
// AgentPool 缓存 AgentClient 实例及其绑定的 Resource。
14+
// Pool 拥有 Resource 的生命周期,确保 agent 连接不会因 Runtime 销毁而断开。
1315
type AgentPool struct {
1416
mu sync.Mutex
1517
clients map[string]*agentEntry
@@ -27,6 +29,7 @@ func NewAgentPool() *AgentPool {
2729
}
2830
}
2931

32+
// Acquire 获取或创建一个 AgentClient 实例(不做 Resource 绑定)。
3033
func (p *AgentPool) Acquire(agent protocol.AgentProfile) (*maa.AgentClient, error) {
3134
key, err := agentPoolKey(agent)
3235
if err != nil {
@@ -49,6 +52,8 @@ func (p *AgentPool) Acquire(agent protocol.AgentProfile) (*maa.AgentClient, erro
4952
return client, nil
5053
}
5154

55+
// EnsureBound 确保 agent 已绑定到指定 resourcePaths 对应的 Resource 上。
56+
// Pool 拥有该 Resource 的生命周期,不会随 Runtime 销毁。
5257
func (p *AgentPool) EnsureBound(agent protocol.AgentProfile, resourcePaths []string) (*maa.AgentClient, error) {
5358
key, err := agentPoolKey(agent)
5459
if err != nil {
@@ -104,6 +109,24 @@ func (p *AgentPool) EnsureBound(agent protocol.AgentProfile, resourcePaths []str
104109
return entry.client, nil
105110
}
106111

112+
// GetResource 获取指定 agent 在 Pool 中绑定的 Resource。
113+
// 如果 agent 未绑定 Resource,返回 nil。
114+
func (p *AgentPool) GetResource(agent protocol.AgentProfile) *maa.Resource {
115+
key, err := agentPoolKey(agent)
116+
if err != nil {
117+
return nil
118+
}
119+
120+
p.mu.Lock()
121+
defer p.mu.Unlock()
122+
123+
entry := p.clients[key]
124+
if entry == nil || entry.resourceAdapter == nil {
125+
return nil
126+
}
127+
return entry.resourceAdapter.GetResource()
128+
}
129+
107130
func normalizeAgentProfile(agent protocol.AgentProfile) (protocol.AgentProfile, error) {
108131
prepared := agent
109132
prepared.Transport = strings.TrimSpace(prepared.Transport)

LocalBridge/internal/debug/runtime/runtime.go

Lines changed: 128 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -91,12 +91,74 @@ func New(
9191
}
9292
adapter.SetController(controller, controllerInfo.Type, controllerInfo.UUID)
9393

94-
emitResourceLoadDiagnostics(sessionID, runID, emit, "starting", resourcePaths, nil)
95-
if err := adapter.LoadResourcesWithProgress(resourcePaths, func(index int, total int, resolution mfw.ResourceBundleResolution, status string, err error) {
96-
emitResourceLoadDiagnostic(sessionID, runID, emit, index, total, resolution, status, err)
97-
}); err != nil {
98-
adapter.Destroy()
99-
return nil, fmt.Errorf("加载资源失败: %w", err)
94+
// 判断是否有启用的 agent,决定 Resource 的来源
95+
enabledAgents := filterEnabledAgents(req.Profile.Agents)
96+
usePoolResource := len(enabledAgents) > 0 && agentPool != nil
97+
98+
if usePoolResource {
99+
// 通过 Pool 确保 agent 已绑定 Resource。
100+
// Pool 拥有 Resource 生命周期,agent 连接不会因 Runtime 销毁而断开。
101+
for _, agent := range enabledAgents {
102+
prepared, prepErr := normalizeAgentProfile(agent)
103+
if prepErr != nil {
104+
continue
105+
}
106+
if _, ensureErr := agentPool.EnsureBound(prepared, resourcePaths); ensureErr != nil {
107+
if requiredAgent(prepared) {
108+
adapter.Destroy()
109+
return nil, fmt.Errorf("agent EnsureBound 失败: %w", ensureErr)
110+
}
111+
}
112+
}
113+
// 获取 Pool 的 Resource 供 Tasker 使用(与 agent 共享同一个 Resource)
114+
var poolResource *maa.Resource
115+
for _, agent := range enabledAgents {
116+
prepared, prepErr := normalizeAgentProfile(agent)
117+
if prepErr != nil {
118+
continue
119+
}
120+
if r := agentPool.GetResource(prepared); r != nil {
121+
poolResource = r
122+
break
123+
}
124+
}
125+
if poolResource == nil {
126+
adapter.Destroy()
127+
return nil, fmt.Errorf("agent pool resource 为空")
128+
}
129+
adapter.SetBorrowedResource(poolResource)
130+
131+
// Connect agent(必须在 InitTasker 之前,确保 handlers 已注册到 Resource)
132+
for _, agent := range enabledAgents {
133+
prepared, prepErr := normalizeAgentProfile(agent)
134+
if prepErr != nil {
135+
continue
136+
}
137+
client, _ := agentPool.Acquire(prepared)
138+
if client == nil {
139+
continue
140+
}
141+
if !client.Connected() {
142+
if prepared.TimeoutMS > 0 {
143+
_ = client.SetTimeout(time.Duration(prepared.TimeoutMS) * time.Millisecond)
144+
}
145+
if err := connectAgent(prepared, client); err != nil {
146+
if requiredAgent(prepared) {
147+
adapter.Destroy()
148+
return nil, fmt.Errorf("agent 连接失败: %w", err)
149+
}
150+
}
151+
}
152+
}
153+
} else {
154+
// 无 agent 时,adapter 自己创建并拥有 Resource
155+
emitResourceLoadDiagnostics(sessionID, runID, emit, "starting", resourcePaths, nil)
156+
if err := adapter.LoadResourcesWithProgress(resourcePaths, func(index int, total int, resolution mfw.ResourceBundleResolution, status string, err error) {
157+
emitResourceLoadDiagnostic(sessionID, runID, emit, index, total, resolution, status, err)
158+
}); err != nil {
159+
adapter.Destroy()
160+
return nil, fmt.Errorf("加载资源失败: %w", err)
161+
}
100162
}
101163

102164
overrideBundle, err := buildPipelineOverrideBundle(root, req)
@@ -144,9 +206,24 @@ func New(
144206
emit: emit,
145207
}
146208

147-
if err := r.connectAgents(req.Profile.Agents); err != nil {
148-
r.Destroy()
149-
return nil, err
209+
if usePoolResource {
210+
// agent 已在 InitTasker 之前连接,记录到 Runtime
211+
for _, agent := range enabledAgents {
212+
prepared, prepErr := normalizeAgentProfile(agent)
213+
if prepErr != nil {
214+
continue
215+
}
216+
client, _ := agentPool.Acquire(prepared)
217+
if client != nil && client.Connected() && client.Alive() {
218+
r.agentClients = append(r.agentClients, client)
219+
r.emitAgentDiagnostic(prepared, "info", "debug.agent.connected", "agent 复用已有连接", nil, nil)
220+
}
221+
}
222+
} else if len(req.Profile.Agents) > 0 {
223+
if err := r.connectAgents(req.Profile.Agents); err != nil {
224+
r.Destroy()
225+
return nil, err
226+
}
150227
}
151228

152229
return r, nil
@@ -435,6 +512,8 @@ func (r *Runtime) Destroy() {
435512
r.mu.Lock()
436513
defer r.mu.Unlock()
437514

515+
// 不 Disconnect agent —— Pool 拥有连接生命周期,agent server 进程需要保持运行。
516+
// adapter 使用的是 Pool 的 Resource(borrowed),Destroy 时不会释放它。
438517
r.agentClients = nil
439518

440519
if r.adapter == nil {
@@ -539,6 +618,8 @@ func (r *Runtime) directModeOverride(mode protocol.RunMode) (map[string]interfac
539618
}
540619

541620
func (r *Runtime) connectAgents(agents []protocol.AgentProfile) error {
621+
resource := r.adapter.GetResource()
622+
542623
for _, agent := range agents {
543624
if !agent.Enabled {
544625
continue
@@ -560,31 +641,45 @@ func (r *Runtime) connectAgents(agents []protocol.AgentProfile) error {
560641
r.emitAgentDiagnostic(prepared, "warning", "debug.agent.create_failed", err.Error(), nil, nil)
561642
continue
562643
}
563-
alreadyConnected := client.Connected()
564-
if !alreadyConnected && prepared.TimeoutMS > 0 {
565-
if err := client.SetTimeout(time.Duration(prepared.TimeoutMS) * time.Millisecond); err != nil {
644+
645+
// 如果 agent 已连接(来自 Pool 复用),需要先断开再重连,
646+
// 因为 handlers 只在 Connect() 时注册到当时绑定的 Resource 上。
647+
if client.Connected() {
648+
_ = client.Disconnect()
649+
}
650+
651+
// 绑定到 Runtime 的 Resource(Tasker 使用的同一个)
652+
if resource != nil {
653+
if err := client.BindResource(resource); err != nil {
566654
if requiredAgent(prepared) {
567655
return err
568656
}
569-
r.emitAgentDiagnostic(prepared, "warning", "debug.agent.timeout_failed", err.Error(), nil, nil)
657+
r.emitAgentDiagnostic(prepared, "warning", "debug.agent.bind_failed", err.Error(), nil, nil)
570658
continue
571659
}
572660
}
573-
if !alreadyConnected {
574-
if err := connectAgent(prepared, client); err != nil {
661+
662+
if prepared.TimeoutMS > 0 {
663+
if err := client.SetTimeout(time.Duration(prepared.TimeoutMS) * time.Millisecond); err != nil {
575664
if requiredAgent(prepared) {
576665
return err
577666
}
578-
r.emitAgentDiagnostic(prepared, "warning", "debug.agent.connect_failed", err.Error(), nil, nil)
667+
r.emitAgentDiagnostic(prepared, "warning", "debug.agent.timeout_failed", err.Error(), nil, nil)
579668
continue
580669
}
581670
}
582-
r.agentClients = append(r.agentClients, client)
583-
message := "agent 已连接"
584-
if alreadyConnected {
585-
message = "agent 复用已有连接"
671+
672+
// Connect 会将 agent server 的 custom handlers 注册到当前绑定的 Resource 上
673+
if err := connectAgent(prepared, client); err != nil {
674+
if requiredAgent(prepared) {
675+
return err
676+
}
677+
r.emitAgentDiagnostic(prepared, "warning", "debug.agent.connect_failed", err.Error(), nil, nil)
678+
continue
586679
}
587-
r.emitAgentDiagnostic(prepared, "info", "debug.agent.connected", message, nil, nil)
680+
681+
r.agentClients = append(r.agentClients, client)
682+
r.emitAgentDiagnostic(prepared, "info", "debug.agent.connected", "agent 已连接", nil, nil)
588683
}
589684
return nil
590685
}
@@ -619,18 +714,9 @@ func (r *Runtime) emitAgentDiagnostic(agent protocol.AgentProfile, severity stri
619714

620715
func (r *Runtime) acquireAgentClient(agent protocol.AgentProfile) (*maa.AgentClient, error) {
621716
if r.agentPool != nil {
622-
return r.agentPool.EnsureBound(agent, r.resourcePaths)
623-
}
624-
client, err := createAgentClient(agent)
625-
if err != nil {
626-
return nil, err
627-
}
628-
if resource := r.adapter.GetResource(); resource != nil {
629-
if err := client.BindResource(resource); err != nil {
630-
return nil, err
631-
}
717+
return r.agentPool.Acquire(agent)
632718
}
633-
return client, nil
719+
return createAgentClient(agent)
634720
}
635721

636722
func (r *Runtime) labelForRuntimeName(runtimeName string) string {
@@ -851,6 +937,16 @@ func requiredAgent(agent protocol.AgentProfile) bool {
851937
return agent.Required == nil || *agent.Required
852938
}
853939

940+
func filterEnabledAgents(agents []protocol.AgentProfile) []protocol.AgentProfile {
941+
var enabled []protocol.AgentProfile
942+
for _, agent := range agents {
943+
if agent.Enabled {
944+
enabled = append(enabled, agent)
945+
}
946+
}
947+
return enabled
948+
}
949+
854950
func emitResourceLoadDiagnostics(sessionID string, runID string, emit events.EmitFunc, status string, paths []string, err error) {
855951
if emit == nil {
856952
return

LocalBridge/internal/mfw/adapter.go

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ type MaaFWAdapter struct {
3939

4040
// 所有权标记
4141
ownsController bool // 是否拥有控制器(true=自己创建的,false=借用的共享控制器)
42+
ownsResource bool // 是否拥有资源(true=自己创建的,false=借用的共享资源)
4243

4344
// 元信息
4445
controllerType string // ADB/Win32/WlRoots
@@ -305,6 +306,7 @@ func (a *MaaFWAdapter) loadResolvedResourcesLocked(resolutions []ResourceBundleR
305306
}
306307

307308
a.resource = res
309+
a.ownsResource = true
308310
a.resourceLoaded = true
309311

310312
logger.Debug("MaaFW", "资源已加载(共 %d 个路径)", len(resolutions))
@@ -382,6 +384,18 @@ func (a *MaaFWAdapter) GetResource() *maa.Resource {
382384
return a.resource
383385
}
384386

387+
// SetBorrowedResource 设置借用的外部 Resource(不拥有所有权,Destroy 时不释放)
388+
func (a *MaaFWAdapter) SetBorrowedResource(res *maa.Resource) {
389+
a.mu.Lock()
390+
defer a.mu.Unlock()
391+
if a.resource != nil && a.ownsResource {
392+
a.resource.Destroy()
393+
}
394+
a.resource = res
395+
a.ownsResource = false
396+
a.resourceLoaded = res != nil
397+
}
398+
385399
// IsResourceLoaded 检查资源是否已加载
386400
func (a *MaaFWAdapter) IsResourceLoaded() bool {
387401
a.mu.RLock()
@@ -842,11 +856,11 @@ func (a *MaaFWAdapter) Destroy() {
842856
}
843857
a.initialized = false
844858

845-
// 销毁 Resource
846-
if a.resource != nil {
859+
// 销毁 Resource(只销毁自己拥有的)
860+
if a.resource != nil && a.ownsResource {
847861
a.resource.Destroy()
848-
a.resource = nil
849862
}
863+
a.resource = nil
850864
a.resourceLoaded = false
851865

852866
// 只销毁自己拥有的 Controller,借用的不销毁

src/data/updateLogs.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ export const updateLogs: UpdateLogItem[] = [
122122
"调试事件线 trace 支持毫秒级时间戳",
123123
],
124124
fixes: [
125+
"修复调试模块无法识别 agent custom 的问题",
125126
"修复调试模块点击 JSON 查看时错误出现图片的问题",
126127
"修复多资源加载会导致预期外的重名节点提示问题",
127128
],

0 commit comments

Comments
 (0)