mu-agent-bridge 1.0.71 → 1.0.78

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (195) hide show
  1. package/README.md +17 -10
  2. package/dist/src-ts/agent.js +35 -0
  3. package/dist/src-ts/channel-sessions.js +3 -2
  4. package/dist/src-ts/cli.js +67 -37
  5. package/dist/src-ts/control-server.js +12 -30
  6. package/dist/src-ts/core-updater.js +141 -33
  7. package/dist/src-ts/daemon-process.js +2 -1
  8. package/dist/src-ts/dsh-agent.js +31 -13
  9. package/dist/src-ts/http-agent.js +36 -0
  10. package/dist/src-ts/plugin-catalog.js +11 -2
  11. package/dist/src-ts/plugin-manager.js +351 -50
  12. package/dist/src-ts/plugins/abweb/index.js +1 -1
  13. package/dist/src-ts/plugins/abweb/public/app.js +73 -5
  14. package/dist/src-ts/plugins/abweb/public/index.html +15 -0
  15. package/dist/src-ts/plugins/abweb/public/style.css +4 -0
  16. package/dist/src-ts/plugins/aun/attachments.js +3 -21
  17. package/dist/src-ts/plugins/aun/index.js +155 -21
  18. package/dist/src-ts/plugins/aun/messages.js +4 -1
  19. package/dist/src-ts/plugins/aun/public/app.js +2 -1
  20. package/dist/src-ts/plugins/aun/public/index.html +1 -1
  21. package/dist/src-ts/plugins/cdp-draft.js +5 -2
  22. package/dist/src-ts/plugins/chatgpt/cdp-client.js +26 -12
  23. package/dist/src-ts/plugins/chatgpt/index.js +80 -19
  24. package/dist/src-ts/plugins/claude-cli/index.js +19 -2
  25. package/dist/src-ts/plugins/codex-cli/index.js +19 -2
  26. package/dist/src-ts/plugins/desktop-launch.js +44 -3
  27. package/dist/src-ts/plugins/doubao/cdp-client.js +234 -45
  28. package/dist/src-ts/plugins/doubao/index.js +152 -8
  29. package/dist/src-ts/plugins/doubao/native-task.js +134 -0
  30. package/dist/src-ts/plugins/dsh/cdp-client.js +334 -0
  31. package/dist/src-ts/plugins/dsh/index.js +172 -21
  32. package/dist/src-ts/plugins/dsh/session-history.js +31 -3
  33. package/dist/src-ts/plugins/feishu/index.js +1 -1
  34. package/dist/src-ts/plugins/local-cli/agent.js +74 -9
  35. package/dist/src-ts/plugins/personal-assistant/claude-cli-model.js +30 -0
  36. package/dist/src-ts/plugins/personal-assistant/coordination.js +57 -0
  37. package/dist/src-ts/plugins/personal-assistant/index.js +263 -0
  38. package/dist/src-ts/plugins/personal-assistant/model-client.js +181 -0
  39. package/dist/src-ts/plugins/personal-assistant/owner-context.js +56 -0
  40. package/dist/src-ts/plugins/personal-assistant/remote-ability.js +176 -0
  41. package/dist/src-ts/plugins/personal-assistant/session-memory.js +124 -0
  42. package/dist/src-ts/plugins/personal-assistant/tools.js +167 -0
  43. package/dist/src-ts/plugins/qwenwork/cdp-client.js +90 -18
  44. package/dist/src-ts/plugins/qwenwork/index.js +139 -24
  45. package/dist/src-ts/plugins/workbuddy/cdp-client.js +353 -82
  46. package/dist/src-ts/plugins/workbuddy/index.js +124 -41
  47. package/dist/src-ts/plugins/zcode/cdp-client.js +500 -0
  48. package/dist/src-ts/plugins/zcode/index.js +97 -0
  49. package/dist/src-ts/session-management.js +132 -14
  50. package/dist/src-ts/version-manager.js +6 -3
  51. package/dist/tests-ts/abweb-cli.test.js +10 -2
  52. package/dist/tests-ts/abweb-model-config.test.js +73 -0
  53. package/dist/tests-ts/agent.test.js +27 -0
  54. package/dist/tests-ts/aun-attachments.test.js +19 -17
  55. package/dist/tests-ts/aun-default-owner-page.test.js +2 -1
  56. package/dist/tests-ts/aun-messages.test.js +20 -0
  57. package/dist/tests-ts/aun-plugin.test.js +292 -19
  58. package/dist/tests-ts/bundled-package.test.js +12 -2
  59. package/dist/tests-ts/cdp-draft-recovery.test.js +182 -3
  60. package/dist/tests-ts/channel-sessions.test.js +27 -0
  61. package/dist/tests-ts/chatgpt-plugin.test.js +118 -0
  62. package/dist/tests-ts/control-server.test.js +56 -0
  63. package/dist/tests-ts/core-updater.test.js +215 -46
  64. package/dist/tests-ts/daemon-process.test.js +126 -1
  65. package/dist/tests-ts/desktop-launch.test.js +73 -0
  66. package/dist/tests-ts/doubao-native-task.test.js +237 -0
  67. package/dist/tests-ts/doubao-plugin.test.js +773 -12
  68. package/dist/tests-ts/dsh-agent.test.js +157 -8
  69. package/dist/tests-ts/dsh-cdp.test.js +148 -0
  70. package/dist/tests-ts/dsh-history.test.js +37 -0
  71. package/dist/tests-ts/feishu-plugin.test.js +1 -1
  72. package/dist/tests-ts/http-agent.test.js +18 -0
  73. package/dist/tests-ts/installer-assets.test.js +1 -1
  74. package/dist/tests-ts/local-cli.test.js +146 -1
  75. package/dist/tests-ts/model-provider-cancellation.test.js +152 -0
  76. package/dist/tests-ts/native-session-management.test.js +8 -7
  77. package/dist/tests-ts/personal-assistant-coordination.test.js +92 -0
  78. package/dist/tests-ts/personal-assistant-extensions.test.js +35 -0
  79. package/dist/tests-ts/personal-assistant-memory.test.js +117 -0
  80. package/dist/tests-ts/personal-assistant-owner-context.test.js +107 -0
  81. package/dist/tests-ts/personal-assistant-remote-ability.test.js +123 -0
  82. package/dist/tests-ts/personal-assistant.test.js +663 -0
  83. package/dist/tests-ts/platform-integration.test.js +183 -1
  84. package/dist/tests-ts/plugin-catalog.test.js +12 -2
  85. package/dist/tests-ts/plugin-manager.test.js +356 -4
  86. package/dist/tests-ts/plugin-upgrade-deadline.test.js +354 -0
  87. package/dist/tests-ts/publish-packages.test.js +42 -0
  88. package/dist/tests-ts/qwenwork-plugin.test.js +135 -1
  89. package/dist/tests-ts/qwenwork-reconnect.test.js +195 -0
  90. package/dist/tests-ts/qwenwork-sse.test.js +33 -0
  91. package/dist/tests-ts/session-access-defaults.test.js +112 -0
  92. package/dist/tests-ts/session-management.test.js +159 -4
  93. package/dist/tests-ts/version-manager.test.js +40 -0
  94. package/dist/tests-ts/workbuddy-cdp-recovery.test.js +311 -0
  95. package/dist/tests-ts/workbuddy-plugin.test.js +499 -4
  96. package/dist/tests-ts/zcode-cdp.test.js +894 -0
  97. package/dist/tests-ts/zcode-plugin.test.js +396 -0
  98. package/package.json +1 -1
  99. package/plugins/abweb/dist/src-ts/agent.js +35 -0
  100. package/plugins/abweb/dist/src-ts/plugins/abweb/index.js +1 -1
  101. package/plugins/abweb/dist/src-ts/plugins/abweb/public/app.js +73 -5
  102. package/plugins/abweb/dist/src-ts/plugins/abweb/public/app.ts +57 -5
  103. package/plugins/abweb/dist/src-ts/plugins/abweb/public/index.html +15 -0
  104. package/plugins/abweb/dist/src-ts/plugins/abweb/public/style.css +4 -0
  105. package/plugins/abweb/package.json +1 -1
  106. package/plugins/abweb/plugin.json +1 -1
  107. package/plugins/aun/config.schema.json +1 -1
  108. package/plugins/aun/dist/src-ts/agent.js +35 -0
  109. package/plugins/aun/dist/src-ts/plugins/aun/attachments.js +3 -21
  110. package/plugins/aun/dist/src-ts/plugins/aun/index.js +155 -21
  111. package/plugins/aun/dist/src-ts/plugins/aun/messages.js +4 -1
  112. package/plugins/aun/dist/src-ts/plugins/aun/public/app.js +2 -1
  113. package/plugins/aun/dist/src-ts/plugins/aun/public/index.html +1 -1
  114. package/plugins/aun/package.json +1 -1
  115. package/plugins/aun/plugin.json +1 -1
  116. package/plugins/chatgpt/dist/src-ts/agent.js +35 -0
  117. package/plugins/chatgpt/dist/src-ts/plugins/cdp-draft.js +5 -2
  118. package/plugins/chatgpt/dist/src-ts/plugins/chatgpt/cdp-client.js +26 -12
  119. package/plugins/chatgpt/dist/src-ts/plugins/chatgpt/index.js +80 -19
  120. package/plugins/chatgpt/dist/src-ts/plugins/desktop-launch.js +44 -3
  121. package/plugins/chatgpt/package.json +1 -1
  122. package/plugins/chatgpt/plugin.json +1 -1
  123. package/plugins/claude-cli/dist/src-ts/agent.js +35 -0
  124. package/plugins/claude-cli/dist/src-ts/plugins/claude-cli/index.js +19 -2
  125. package/plugins/claude-cli/dist/src-ts/plugins/local-cli/agent.js +74 -9
  126. package/plugins/claude-cli/package.json +1 -1
  127. package/plugins/claude-cli/plugin.json +3 -2
  128. package/plugins/codex-cli/dist/src-ts/agent.js +35 -0
  129. package/plugins/codex-cli/dist/src-ts/plugins/codex-cli/index.js +19 -2
  130. package/plugins/codex-cli/dist/src-ts/plugins/local-cli/agent.js +74 -9
  131. package/plugins/codex-cli/package.json +1 -1
  132. package/plugins/codex-cli/plugin.json +3 -2
  133. package/plugins/doubao/config.schema.json +2 -1
  134. package/plugins/doubao/dist/src-ts/agent.js +35 -0
  135. package/plugins/doubao/dist/src-ts/plugins/desktop-launch.js +44 -3
  136. package/plugins/doubao/dist/src-ts/plugins/doubao/cdp-client.js +234 -45
  137. package/plugins/doubao/dist/src-ts/plugins/doubao/index.js +152 -8
  138. package/plugins/doubao/dist/src-ts/plugins/doubao/native-task.js +134 -0
  139. package/plugins/doubao/package.json +1 -1
  140. package/plugins/doubao/plugin.json +3 -2
  141. package/plugins/dsh/config.schema.json +5 -2
  142. package/plugins/dsh/dist/src-ts/agent.js +35 -0
  143. package/plugins/dsh/dist/src-ts/dsh-agent.js +31 -13
  144. package/plugins/dsh/dist/src-ts/plugins/desktop-launch.js +44 -3
  145. package/plugins/dsh/dist/src-ts/plugins/dsh/cdp-client.js +334 -0
  146. package/plugins/dsh/dist/src-ts/plugins/dsh/index.js +172 -21
  147. package/plugins/dsh/dist/src-ts/plugins/dsh/session-history.js +31 -3
  148. package/plugins/dsh/package.json +1 -1
  149. package/plugins/dsh/plugin.json +3 -2
  150. package/plugins/feishu/dist/src-ts/agent.js +35 -0
  151. package/plugins/feishu/dist/src-ts/plugins/feishu/index.js +1 -1
  152. package/plugins/feishu/package.json +1 -1
  153. package/plugins/feishu/plugin.json +1 -1
  154. package/plugins/personal-assistant/config.schema.json +23 -0
  155. package/plugins/personal-assistant/dist/src-ts/agent.js +69 -0
  156. package/plugins/personal-assistant/dist/src-ts/attachments.js +62 -0
  157. package/plugins/personal-assistant/dist/src-ts/plugin.js +13 -0
  158. package/plugins/personal-assistant/dist/src-ts/plugins/personal-assistant/claude-cli-model.js +30 -0
  159. package/plugins/personal-assistant/dist/src-ts/plugins/personal-assistant/coordination.js +57 -0
  160. package/plugins/personal-assistant/dist/src-ts/plugins/personal-assistant/index.js +263 -0
  161. package/plugins/personal-assistant/dist/src-ts/plugins/personal-assistant/model-client.js +181 -0
  162. package/plugins/personal-assistant/dist/src-ts/plugins/personal-assistant/owner-context.js +56 -0
  163. package/plugins/personal-assistant/dist/src-ts/plugins/personal-assistant/remote-ability.js +176 -0
  164. package/plugins/personal-assistant/dist/src-ts/plugins/personal-assistant/session-memory.js +124 -0
  165. package/plugins/personal-assistant/dist/src-ts/plugins/personal-assistant/tools.js +167 -0
  166. package/plugins/personal-assistant/package.json +39 -0
  167. package/plugins/personal-assistant/plugin.json +15 -0
  168. package/plugins/qwenwork/dist/src-ts/agent.js +35 -0
  169. package/plugins/qwenwork/dist/src-ts/plugins/cdp-draft.js +5 -2
  170. package/plugins/qwenwork/dist/src-ts/plugins/desktop-launch.js +44 -3
  171. package/plugins/qwenwork/dist/src-ts/plugins/qwenwork/cdp-client.js +90 -18
  172. package/plugins/qwenwork/dist/src-ts/plugins/qwenwork/index.js +139 -24
  173. package/plugins/qwenwork/package.json +1 -1
  174. package/plugins/qwenwork/plugin.json +3 -2
  175. package/plugins/workbuddy/config.schema.json +1 -1
  176. package/plugins/workbuddy/dist/src-ts/agent.js +35 -0
  177. package/plugins/workbuddy/dist/src-ts/plugins/desktop-launch.js +44 -3
  178. package/plugins/workbuddy/dist/src-ts/plugins/workbuddy/cdp-client.js +353 -82
  179. package/plugins/workbuddy/dist/src-ts/plugins/workbuddy/index.js +124 -41
  180. package/plugins/workbuddy/package.json +1 -1
  181. package/plugins/workbuddy/plugin.json +3 -2
  182. package/plugins/zcode/config.schema.json +13 -0
  183. package/plugins/zcode/dist/src-ts/agent.js +69 -0
  184. package/plugins/zcode/dist/src-ts/attachments.js +62 -0
  185. package/plugins/zcode/dist/src-ts/plugin.js +13 -0
  186. package/plugins/zcode/dist/src-ts/plugins/desktop-launch.js +238 -0
  187. package/plugins/zcode/dist/src-ts/plugins/workbuddy/cdp-client.js +896 -0
  188. package/plugins/zcode/dist/src-ts/plugins/workbuddy/cli-client.js +39 -0
  189. package/plugins/zcode/dist/src-ts/plugins/workbuddy/desktop-launch.js +10 -0
  190. package/plugins/zcode/dist/src-ts/plugins/workbuddy/index.js +567 -0
  191. package/plugins/zcode/dist/src-ts/plugins/workbuddy/session-history.js +121 -0
  192. package/plugins/zcode/dist/src-ts/plugins/zcode/cdp-client.js +500 -0
  193. package/plugins/zcode/dist/src-ts/plugins/zcode/index.js +97 -0
  194. package/plugins/zcode/package.json +16 -0
  195. package/plugins/zcode/plugin.json +11 -0
@@ -6,6 +6,7 @@ const node_child_process_1 = require("node:child_process");
6
6
  const node_fs_1 = require("node:fs");
7
7
  const promises_1 = require("node:fs/promises");
8
8
  const node_path_1 = require("node:path");
9
+ const node_os_1 = require("node:os");
9
10
  const node_crypto_1 = require("node:crypto");
10
11
  const nativeId = /^[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}$/i;
11
12
  const maximumOutput = 4 * 1024 * 1024;
@@ -22,6 +23,21 @@ function diagnostic(value) {
22
23
  } })
23
24
  .slice(0, 2000);
24
25
  }
26
+ function modelDiagnostic(error) {
27
+ const message = String(error);
28
+ const code = /CLI 退出码 (?:\d+|[A-Z][A-Z0-9_]+)/.exec(message)?.[0];
29
+ if (code)
30
+ return `${code}:请检查 CLI 登录、权限和网络`;
31
+ if (message.includes("CLI 调用超时"))
32
+ return "CLI 模型调用超时,请检查登录、网络或权限审批";
33
+ if (message.includes("CLI 请求已取消"))
34
+ return "CLI 模型请求已取消";
35
+ if (message.includes("插件已停止"))
36
+ return "插件已停止,当前 CLI 模型任务已终止";
37
+ if (message.includes("CLI 输出超过"))
38
+ return "CLI 模型输出超过 4MB 限制";
39
+ return "CLI 模型调用或结构化输出失败,请检查 CLI 状态";
40
+ }
25
41
  async function resolveWindowsCliShim(file, provider) {
26
42
  const modules = (0, node_path_1.basename)((0, node_path_1.dirname)(file)) === ".bin" ? (0, node_path_1.dirname)((0, node_path_1.dirname)(file)) : (0, node_path_1.join)((0, node_path_1.dirname)(file), "node_modules");
27
43
  const root = (0, node_path_1.join)(modules, ...(provider === "codex" ? ["@openai", "codex"] : ["@anthropic-ai", "claude-code"]));
@@ -76,6 +92,8 @@ class LocalCliAgent {
76
92
  ready = false;
77
93
  message = "插件尚未启动";
78
94
  version = "";
95
+ modelModeSupported = false;
96
+ get modelInput() { return this.ready && this.modelModeSupported; }
79
97
  sessions = new Map();
80
98
  writes = Promise.resolve();
81
99
  pending = new Map();
@@ -108,6 +126,9 @@ class LocalCliAgent {
108
126
  const help = (await this.run(this.provider === "codex" ? ["exec", "--help"] : ["--help"], "", 10000)).stdout;
109
127
  if (this.provider === "codex" ? !help.includes("--json") || !help.includes("resume") : !help.includes("--output-format") || !help.includes("--resume"))
110
128
  throw new Error("CLI 版本不支持所需的结构化输出和会话恢复,请升级 CLI");
129
+ this.modelModeSupported = this.provider === "claude"
130
+ ? ["--json-schema", "--tools", "--strict-mcp-config", "--mcp-config", "--no-session-persistence", "--settings", "--setting-sources"].every(flag => help.includes(flag))
131
+ : ["--output-schema", "--ephemeral", "--sandbox", "--disable", "--config", "--ignore-rules"].every(flag => help.includes(flag));
111
132
  this.sessions = new Map();
112
133
  try {
113
134
  const data = JSON.parse(await (0, promises_1.readFile)((0, node_path_1.join)(context.dataDir, "cli-sessions.json"), "utf8"));
@@ -128,7 +149,9 @@ class LocalCliAgent {
128
149
  throw new Error(this.message);
129
150
  }
130
151
  }
131
- run(args, input, timeout) {
152
+ run(args, input, timeout, signal) {
153
+ if (signal?.aborted)
154
+ return Promise.reject(new Error("CLI 请求已取消"));
132
155
  const command = this.command;
133
156
  let child;
134
157
  let stop;
@@ -153,8 +176,10 @@ class LocalCliAgent {
153
176
  }
154
177
  };
155
178
  stop = () => terminate("插件已停止,当前 CLI 任务已终止");
179
+ const abort = () => terminate("CLI 请求已取消");
180
+ signal?.addEventListener("abort", abort, { once: true });
156
181
  const timer = setTimeout(() => terminate(`CLI 调用超时(${timeout}ms),请检查登录、网络或权限审批`), timeout);
157
- const cleanup = () => { clearTimeout(timer); this.children.delete(child); };
182
+ const cleanup = () => { clearTimeout(timer); signal?.removeEventListener("abort", abort); this.children.delete(child); };
158
183
  child.stdout.setEncoding("utf8");
159
184
  child.stderr.setEncoding("utf8");
160
185
  const collect = (value, error) => {
@@ -227,35 +252,70 @@ class LocalCliAgent {
227
252
  throw new Error(`${this.manifest.displayName} 本期不支持附件输入`);
228
253
  if (!request.task?.trim())
229
254
  throw new Error("CLI 任务文本不能为空");
255
+ return this.executeCli(request, request.modelResponseSchema);
256
+ }
257
+ /**
258
+ * 模型来源的独立入口。调用方只提供最小模型契约,不能通过普通 Agent handle 进入管理路径。
259
+ */
260
+ async completeStructuredModel(request, responseSchema) {
261
+ if (!request.prompt?.trim())
262
+ throw new Error("模型请求文本不能为空");
263
+ if (!request.sessionId?.trim())
264
+ throw new Error("模型请求会话标识不能为空");
265
+ const response = await this.executeCli({ task: request.prompt, sessionId: request.sessionId, timeoutMs: request.timeoutMs, signal: request.signal }, responseSchema);
266
+ return { text: response.text };
267
+ }
268
+ async executeCli(request, modelSchema) {
269
+ const modelMode = modelSchema !== undefined;
270
+ if (modelMode && !this.modelModeSupported)
271
+ throw new Error("当前 CLI 不支持无工具结构化模型请求,请升级 CLI");
272
+ const schema = modelMode ? JSON.stringify(modelSchema) : undefined;
273
+ if (modelMode && (!modelSchema || typeof modelSchema !== "object" || Array.isArray(modelSchema) || !schema || Buffer.byteLength(schema) > 64_000))
274
+ throw new Error("模型输出 Schema 无效或过大");
230
275
  const logical = request.sessionId || (0, node_crypto_1.randomUUID)();
231
276
  return this.serial(logical, async () => {
232
277
  this.requireReady();
233
- const id = request.newSession ? undefined : this.sessions.get(logical);
278
+ const id = modelMode || request.newSession ? undefined : this.sessions.get(logical);
279
+ let schemaFile;
280
+ let schemaDirectory;
234
281
  const args = this.provider === "codex"
235
282
  ? ["exec", ...(id ? ["resume", id] : []), "--json", "-"]
236
283
  : ["--print", "--output-format", "json", ...(id ? ["--resume", id] : [])];
284
+ if (modelMode && this.provider === "claude")
285
+ args.push("--json-schema", schema, "--tools", "", "--strict-mcp-config", "--mcp-config", '{"mcpServers":{}}', "--settings", '{"disableAllHooks":true}', "--setting-sources", "user", "--no-session-persistence");
237
286
  const timeout = request.timeoutMs ?? this.timeoutMs;
238
287
  if (!Number.isInteger(timeout) || timeout <= 0 || timeout > 2147483647)
239
288
  throw new Error("任务超时必须是有效的正整数毫秒数");
240
289
  const started = Date.now();
241
290
  this.context.logger.info("本地 CLI 任务开始", { plugin: this.manifest.id, sessionId: logical, resume: Boolean(id) });
242
291
  try {
243
- const output = await this.run(args, request.task, timeout);
244
- const response = this.parse(output.stdout);
292
+ if (modelMode && this.provider === "codex") {
293
+ schemaDirectory = await (0, promises_1.mkdtemp)((0, node_path_1.join)((0, node_os_1.tmpdir)(), "mab-codex-model-"));
294
+ schemaFile = (0, node_path_1.join)(schemaDirectory, "response-schema.json");
295
+ await (0, promises_1.writeFile)(schemaFile, schema, { flag: "wx", mode: 0o600 });
296
+ args.splice(-1, 0, "--ephemeral", "--sandbox", "read-only", "--output-schema", schemaFile, "--disable", "shell_tool", "--disable", "apps", "--disable", "plugins", "--disable", "hooks", "--ignore-rules", "--config", "mcp_servers={}");
297
+ }
298
+ const output = await this.run(args, request.task, timeout, request.signal);
299
+ const response = this.parse(output.stdout, modelMode);
245
300
  if (id && response.id !== id)
246
301
  throw new Error("CLI 恢复返回的会话 ID 与指定会话不一致,保留原会话绑定");
247
- await this.save(logical, response.id);
302
+ if (!modelMode)
303
+ await this.save(logical, response.id);
248
304
  this.context.logger.info("本地 CLI 任务完成", { plugin: this.manifest.id, sessionId: logical, durationMs: Date.now() - started, exitCode: 0 });
249
- return { text: response.text, sessionId: logical };
305
+ return { text: response.text, ...(!modelMode ? { sessionId: logical } : {}) };
250
306
  }
251
307
  catch (error) {
252
- const detail = diagnostic(String(error));
308
+ const detail = modelMode ? modelDiagnostic(error) : diagnostic(String(error));
253
309
  this.context.logger.error("本地 CLI 任务失败", { plugin: this.manifest.id, sessionId: logical, durationMs: Date.now() - started, error: detail });
254
310
  throw new Error(`${this.manifest.displayName}:${detail}`);
255
311
  }
312
+ finally {
313
+ if (schemaFile && schemaDirectory)
314
+ await (0, promises_1.rm)(schemaDirectory, { recursive: true, force: true });
315
+ }
256
316
  });
257
317
  }
258
- parse(stdout) {
318
+ parse(stdout, modelMode = false) {
259
319
  let id = "", text = "";
260
320
  try {
261
321
  if (this.provider === "codex") {
@@ -287,6 +347,11 @@ class LocalCliAgent {
287
347
  throw new Error(JSON.stringify(event.errors ?? event));
288
348
  id = event.session_id;
289
349
  text = event.result;
350
+ if (modelMode) {
351
+ if (!event.structured_output || typeof event.structured_output !== "object" || Array.isArray(event.structured_output))
352
+ throw new Error("缺少有效结构化模型输出");
353
+ text = JSON.stringify(event.structured_output);
354
+ }
290
355
  }
291
356
  if (typeof id !== "string" || !nativeId.test(id))
292
357
  throw new Error("缺少有效原生会话 ID");
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-bridge-claude-cli",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "mu-agent-bridge 的本地 Claude Code CLI 独立插件,沿用 CLI 默认模型与登录。",
5
5
  "keywords": [
6
6
  "mu-agent-bridge-plugin",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "id": "claude-cli",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "type": "agent",
5
5
  "executionMode": "cli",
6
6
  "displayName": "Claude Code CLI",
@@ -9,7 +9,8 @@
9
9
  "process": "in-process",
10
10
  "capabilities": [
11
11
  "chat",
12
- "health"
12
+ "health",
13
+ "model-provider"
13
14
  ],
14
15
  "configSchema": "config.schema.json"
15
16
  }
@@ -1,7 +1,42 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.AgentOperationError = exports.AgentError = void 0;
4
+ exports.abortable = abortable;
4
5
  exports.errorDetails = errorDetails;
6
+ async function abortable(promise, signal) {
7
+ if (!signal)
8
+ return promise;
9
+ if (signal.aborted) {
10
+ // promise 已在调用方启动,先观察其拒绝,避免预取消时产生未处理拒绝。
11
+ void promise.catch(() => undefined);
12
+ signal.throwIfAborted();
13
+ }
14
+ return new Promise((resolve, reject) => {
15
+ let settled = false;
16
+ const cleanup = () => signal.removeEventListener("abort", abort);
17
+ const abort = () => {
18
+ if (settled)
19
+ return;
20
+ settled = true;
21
+ cleanup();
22
+ reject(signal.reason);
23
+ };
24
+ signal.addEventListener("abort", abort, { once: true });
25
+ promise.then((value) => {
26
+ if (settled)
27
+ return;
28
+ settled = true;
29
+ cleanup();
30
+ resolve(value);
31
+ }, (error) => {
32
+ if (settled)
33
+ return;
34
+ settled = true;
35
+ cleanup();
36
+ reject(error);
37
+ });
38
+ });
39
+ }
5
40
  class AgentError extends Error {
6
41
  constructor(message, options) {
7
42
  super(message, options);
@@ -3,12 +3,29 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.CodexCliPlugin = exports.manifest = void 0;
4
4
  const agent_1 = require("../local-cli/agent");
5
5
  exports.manifest = {
6
- id: "codex-cli", version: "1.0.1", displayName: "Codex CLI", type: "agent", executionMode: "cli",
6
+ id: "codex-cli", version: "1.0.2", displayName: "Codex CLI", type: "agent", executionMode: "cli",
7
7
  entry: "dist/src-ts/plugins/codex-cli/index.js", apiVersion: "1", process: "in-process",
8
- capabilities: ["chat", "health"], configSchema: "config.schema.json",
8
+ capabilities: ["chat", "health", "model-provider"], configSchema: "config.schema.json",
9
9
  };
10
10
  class CodexCliPlugin extends agent_1.LocalCliAgent {
11
11
  constructor() { super(exports.manifest, "codex"); }
12
+ get modelProvider() {
13
+ if (!this.modelInput)
14
+ return undefined;
15
+ return { complete: (request) => this.completeModelProvider(request) };
16
+ }
17
+ async completeModelProvider(request) {
18
+ let responseSchema;
19
+ try {
20
+ responseSchema = JSON.parse(request.prompt)?.responseSchema;
21
+ }
22
+ catch {
23
+ throw new Error("Codex CLI 模型请求必须是包含 responseSchema 的 JSON 对象");
24
+ }
25
+ if (!responseSchema || typeof responseSchema !== "object" || Array.isArray(responseSchema))
26
+ throw new Error("Codex CLI 模型请求缺少有效 responseSchema");
27
+ return this.completeStructuredModel(request, responseSchema);
28
+ }
12
29
  }
13
30
  exports.CodexCliPlugin = CodexCliPlugin;
14
31
  exports.default = CodexCliPlugin;
@@ -6,6 +6,7 @@ const node_child_process_1 = require("node:child_process");
6
6
  const node_fs_1 = require("node:fs");
7
7
  const promises_1 = require("node:fs/promises");
8
8
  const node_path_1 = require("node:path");
9
+ const node_os_1 = require("node:os");
9
10
  const node_crypto_1 = require("node:crypto");
10
11
  const nativeId = /^[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}$/i;
11
12
  const maximumOutput = 4 * 1024 * 1024;
@@ -22,6 +23,21 @@ function diagnostic(value) {
22
23
  } })
23
24
  .slice(0, 2000);
24
25
  }
26
+ function modelDiagnostic(error) {
27
+ const message = String(error);
28
+ const code = /CLI 退出码 (?:\d+|[A-Z][A-Z0-9_]+)/.exec(message)?.[0];
29
+ if (code)
30
+ return `${code}:请检查 CLI 登录、权限和网络`;
31
+ if (message.includes("CLI 调用超时"))
32
+ return "CLI 模型调用超时,请检查登录、网络或权限审批";
33
+ if (message.includes("CLI 请求已取消"))
34
+ return "CLI 模型请求已取消";
35
+ if (message.includes("插件已停止"))
36
+ return "插件已停止,当前 CLI 模型任务已终止";
37
+ if (message.includes("CLI 输出超过"))
38
+ return "CLI 模型输出超过 4MB 限制";
39
+ return "CLI 模型调用或结构化输出失败,请检查 CLI 状态";
40
+ }
25
41
  async function resolveWindowsCliShim(file, provider) {
26
42
  const modules = (0, node_path_1.basename)((0, node_path_1.dirname)(file)) === ".bin" ? (0, node_path_1.dirname)((0, node_path_1.dirname)(file)) : (0, node_path_1.join)((0, node_path_1.dirname)(file), "node_modules");
27
43
  const root = (0, node_path_1.join)(modules, ...(provider === "codex" ? ["@openai", "codex"] : ["@anthropic-ai", "claude-code"]));
@@ -76,6 +92,8 @@ class LocalCliAgent {
76
92
  ready = false;
77
93
  message = "插件尚未启动";
78
94
  version = "";
95
+ modelModeSupported = false;
96
+ get modelInput() { return this.ready && this.modelModeSupported; }
79
97
  sessions = new Map();
80
98
  writes = Promise.resolve();
81
99
  pending = new Map();
@@ -108,6 +126,9 @@ class LocalCliAgent {
108
126
  const help = (await this.run(this.provider === "codex" ? ["exec", "--help"] : ["--help"], "", 10000)).stdout;
109
127
  if (this.provider === "codex" ? !help.includes("--json") || !help.includes("resume") : !help.includes("--output-format") || !help.includes("--resume"))
110
128
  throw new Error("CLI 版本不支持所需的结构化输出和会话恢复,请升级 CLI");
129
+ this.modelModeSupported = this.provider === "claude"
130
+ ? ["--json-schema", "--tools", "--strict-mcp-config", "--mcp-config", "--no-session-persistence", "--settings", "--setting-sources"].every(flag => help.includes(flag))
131
+ : ["--output-schema", "--ephemeral", "--sandbox", "--disable", "--config", "--ignore-rules"].every(flag => help.includes(flag));
111
132
  this.sessions = new Map();
112
133
  try {
113
134
  const data = JSON.parse(await (0, promises_1.readFile)((0, node_path_1.join)(context.dataDir, "cli-sessions.json"), "utf8"));
@@ -128,7 +149,9 @@ class LocalCliAgent {
128
149
  throw new Error(this.message);
129
150
  }
130
151
  }
131
- run(args, input, timeout) {
152
+ run(args, input, timeout, signal) {
153
+ if (signal?.aborted)
154
+ return Promise.reject(new Error("CLI 请求已取消"));
132
155
  const command = this.command;
133
156
  let child;
134
157
  let stop;
@@ -153,8 +176,10 @@ class LocalCliAgent {
153
176
  }
154
177
  };
155
178
  stop = () => terminate("插件已停止,当前 CLI 任务已终止");
179
+ const abort = () => terminate("CLI 请求已取消");
180
+ signal?.addEventListener("abort", abort, { once: true });
156
181
  const timer = setTimeout(() => terminate(`CLI 调用超时(${timeout}ms),请检查登录、网络或权限审批`), timeout);
157
- const cleanup = () => { clearTimeout(timer); this.children.delete(child); };
182
+ const cleanup = () => { clearTimeout(timer); signal?.removeEventListener("abort", abort); this.children.delete(child); };
158
183
  child.stdout.setEncoding("utf8");
159
184
  child.stderr.setEncoding("utf8");
160
185
  const collect = (value, error) => {
@@ -227,35 +252,70 @@ class LocalCliAgent {
227
252
  throw new Error(`${this.manifest.displayName} 本期不支持附件输入`);
228
253
  if (!request.task?.trim())
229
254
  throw new Error("CLI 任务文本不能为空");
255
+ return this.executeCli(request, request.modelResponseSchema);
256
+ }
257
+ /**
258
+ * 模型来源的独立入口。调用方只提供最小模型契约,不能通过普通 Agent handle 进入管理路径。
259
+ */
260
+ async completeStructuredModel(request, responseSchema) {
261
+ if (!request.prompt?.trim())
262
+ throw new Error("模型请求文本不能为空");
263
+ if (!request.sessionId?.trim())
264
+ throw new Error("模型请求会话标识不能为空");
265
+ const response = await this.executeCli({ task: request.prompt, sessionId: request.sessionId, timeoutMs: request.timeoutMs, signal: request.signal }, responseSchema);
266
+ return { text: response.text };
267
+ }
268
+ async executeCli(request, modelSchema) {
269
+ const modelMode = modelSchema !== undefined;
270
+ if (modelMode && !this.modelModeSupported)
271
+ throw new Error("当前 CLI 不支持无工具结构化模型请求,请升级 CLI");
272
+ const schema = modelMode ? JSON.stringify(modelSchema) : undefined;
273
+ if (modelMode && (!modelSchema || typeof modelSchema !== "object" || Array.isArray(modelSchema) || !schema || Buffer.byteLength(schema) > 64_000))
274
+ throw new Error("模型输出 Schema 无效或过大");
230
275
  const logical = request.sessionId || (0, node_crypto_1.randomUUID)();
231
276
  return this.serial(logical, async () => {
232
277
  this.requireReady();
233
- const id = request.newSession ? undefined : this.sessions.get(logical);
278
+ const id = modelMode || request.newSession ? undefined : this.sessions.get(logical);
279
+ let schemaFile;
280
+ let schemaDirectory;
234
281
  const args = this.provider === "codex"
235
282
  ? ["exec", ...(id ? ["resume", id] : []), "--json", "-"]
236
283
  : ["--print", "--output-format", "json", ...(id ? ["--resume", id] : [])];
284
+ if (modelMode && this.provider === "claude")
285
+ args.push("--json-schema", schema, "--tools", "", "--strict-mcp-config", "--mcp-config", '{"mcpServers":{}}', "--settings", '{"disableAllHooks":true}', "--setting-sources", "user", "--no-session-persistence");
237
286
  const timeout = request.timeoutMs ?? this.timeoutMs;
238
287
  if (!Number.isInteger(timeout) || timeout <= 0 || timeout > 2147483647)
239
288
  throw new Error("任务超时必须是有效的正整数毫秒数");
240
289
  const started = Date.now();
241
290
  this.context.logger.info("本地 CLI 任务开始", { plugin: this.manifest.id, sessionId: logical, resume: Boolean(id) });
242
291
  try {
243
- const output = await this.run(args, request.task, timeout);
244
- const response = this.parse(output.stdout);
292
+ if (modelMode && this.provider === "codex") {
293
+ schemaDirectory = await (0, promises_1.mkdtemp)((0, node_path_1.join)((0, node_os_1.tmpdir)(), "mab-codex-model-"));
294
+ schemaFile = (0, node_path_1.join)(schemaDirectory, "response-schema.json");
295
+ await (0, promises_1.writeFile)(schemaFile, schema, { flag: "wx", mode: 0o600 });
296
+ args.splice(-1, 0, "--ephemeral", "--sandbox", "read-only", "--output-schema", schemaFile, "--disable", "shell_tool", "--disable", "apps", "--disable", "plugins", "--disable", "hooks", "--ignore-rules", "--config", "mcp_servers={}");
297
+ }
298
+ const output = await this.run(args, request.task, timeout, request.signal);
299
+ const response = this.parse(output.stdout, modelMode);
245
300
  if (id && response.id !== id)
246
301
  throw new Error("CLI 恢复返回的会话 ID 与指定会话不一致,保留原会话绑定");
247
- await this.save(logical, response.id);
302
+ if (!modelMode)
303
+ await this.save(logical, response.id);
248
304
  this.context.logger.info("本地 CLI 任务完成", { plugin: this.manifest.id, sessionId: logical, durationMs: Date.now() - started, exitCode: 0 });
249
- return { text: response.text, sessionId: logical };
305
+ return { text: response.text, ...(!modelMode ? { sessionId: logical } : {}) };
250
306
  }
251
307
  catch (error) {
252
- const detail = diagnostic(String(error));
308
+ const detail = modelMode ? modelDiagnostic(error) : diagnostic(String(error));
253
309
  this.context.logger.error("本地 CLI 任务失败", { plugin: this.manifest.id, sessionId: logical, durationMs: Date.now() - started, error: detail });
254
310
  throw new Error(`${this.manifest.displayName}:${detail}`);
255
311
  }
312
+ finally {
313
+ if (schemaFile && schemaDirectory)
314
+ await (0, promises_1.rm)(schemaDirectory, { recursive: true, force: true });
315
+ }
256
316
  });
257
317
  }
258
- parse(stdout) {
318
+ parse(stdout, modelMode = false) {
259
319
  let id = "", text = "";
260
320
  try {
261
321
  if (this.provider === "codex") {
@@ -287,6 +347,11 @@ class LocalCliAgent {
287
347
  throw new Error(JSON.stringify(event.errors ?? event));
288
348
  id = event.session_id;
289
349
  text = event.result;
350
+ if (modelMode) {
351
+ if (!event.structured_output || typeof event.structured_output !== "object" || Array.isArray(event.structured_output))
352
+ throw new Error("缺少有效结构化模型输出");
353
+ text = JSON.stringify(event.structured_output);
354
+ }
290
355
  }
291
356
  if (typeof id !== "string" || !nativeId.test(id))
292
357
  throw new Error("缺少有效原生会话 ID");
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-bridge-codex-cli",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "description": "mu-agent-bridge 的本地 Codex CLI 独立插件,沿用 CLI 默认模型与登录。",
5
5
  "keywords": [
6
6
  "mu-agent-bridge-plugin",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "id": "codex-cli",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "type": "agent",
5
5
  "executionMode": "cli",
6
6
  "displayName": "Codex CLI",
@@ -9,7 +9,8 @@
9
9
  "process": "in-process",
10
10
  "capabilities": [
11
11
  "chat",
12
- "health"
12
+ "health",
13
+ "model-provider"
13
14
  ],
14
15
  "configSchema": "config.schema.json"
15
16
  }
@@ -8,8 +8,9 @@
8
8
  "startupTimeoutMs": { "type": "integer", "minimum": 1000, "default": 30000, "description": "等待豆包启动的超时时间(毫秒)" },
9
9
  "conversationId": { "type": "string", "description": "豆包会话 ID" },
10
10
  "botId": { "type": "string", "description": "豆包 bot ID" },
11
+ "mode": { "type": "string", "enum": ["local-computer", "chat"], "default": "local-computer", "description": "默认使用本地电脑任务模式,继承当前桌面登录态的工作区和授权;chat 为纯聊天" },
11
12
  "pluginList": { "type": "array", "items": { "type": "string" }, "description": "请求时显式启用的豆包伙伴标识,例如飞书伙伴" },
12
- "timeoutMs": { "type": "integer", "minimum": 1000, "description": "等待回复超时时间(毫秒)" },
13
+ "timeoutMs": { "type": "integer", "minimum": 1000, "description": "等待回复超时时间(毫秒),默认 180000;本地电脑模式按本轮新流事件续期" },
13
14
  "token": { "type": "string", "secret": true }
14
15
  },
15
16
  "additionalProperties": false
@@ -1,7 +1,42 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.AgentOperationError = exports.AgentError = void 0;
4
+ exports.abortable = abortable;
4
5
  exports.errorDetails = errorDetails;
6
+ async function abortable(promise, signal) {
7
+ if (!signal)
8
+ return promise;
9
+ if (signal.aborted) {
10
+ // promise 已在调用方启动,先观察其拒绝,避免预取消时产生未处理拒绝。
11
+ void promise.catch(() => undefined);
12
+ signal.throwIfAborted();
13
+ }
14
+ return new Promise((resolve, reject) => {
15
+ let settled = false;
16
+ const cleanup = () => signal.removeEventListener("abort", abort);
17
+ const abort = () => {
18
+ if (settled)
19
+ return;
20
+ settled = true;
21
+ cleanup();
22
+ reject(signal.reason);
23
+ };
24
+ signal.addEventListener("abort", abort, { once: true });
25
+ promise.then((value) => {
26
+ if (settled)
27
+ return;
28
+ settled = true;
29
+ cleanup();
30
+ resolve(value);
31
+ }, (error) => {
32
+ if (settled)
33
+ return;
34
+ settled = true;
35
+ cleanup();
36
+ reject(error);
37
+ });
38
+ });
39
+ }
5
40
  class AgentError extends Error {
6
41
  constructor(message, options) {
7
42
  super(message, options);
@@ -16,8 +16,8 @@ const node_path_1 = require("node:path");
16
16
  const node_util_1 = require("node:util");
17
17
  const execFileAsync = (0, node_util_1.promisify)(node_child_process_1.execFile);
18
18
  const versionDirectory = /^(?:app[-_ ]?)?v?\d+(?:[.-]\d+)+$/i;
19
- /** 在 Windows 常见安装目录中查找桌面程序,避免依赖单一盘符。 */
20
- async function findWindowsExecutable(fileName, configured) {
19
+ /** 先从 Windows 注册信息和开始菜单解析,失败时再查常见安装目录。 */
20
+ async function findWindowsExecutable(fileName, configured, run = execFileAsync) {
21
21
  if (configured?.trim()) {
22
22
  const configuredPath = (0, node_path_1.resolve)(configured);
23
23
  if (fileName.toLowerCase() === "doubao.exe") {
@@ -42,12 +42,16 @@ async function findWindowsExecutable(fileName, configured) {
42
42
  }
43
43
  if (process.platform !== "win32")
44
44
  return undefined;
45
- const pattern = /doubao|豆包|qwen|qwwork|千问|deepseek|harness|dsh/i;
45
+ const registered = await findRegisteredWindowsExecutable(fileName, run);
46
+ if (registered)
47
+ return registered;
48
+ const pattern = /doubao|豆包|qwen|qwwork|千问|deepseek|harness|dsh|zcode/i;
46
49
  const roots = [...new Set([
47
50
  process.env.DSH_INSTALL_DIR,
48
51
  "D:\\Programs\\DeepSeek Harness",
49
52
  "D:\\DSH",
50
53
  process.env.LOCALAPPDATA && (0, node_path_1.join)(process.env.LOCALAPPDATA, "Programs"),
54
+ process.env.LOCALAPPDATA,
51
55
  process.env.ProgramFiles,
52
56
  process.env["ProgramFiles(x86)"],
53
57
  ...Array.from({ length: 24 }, (_, index) => `${String.fromCharCode(67 + index)}:\\`),
@@ -59,6 +63,36 @@ async function findWindowsExecutable(fileName, configured) {
59
63
  }
60
64
  return undefined;
61
65
  }
66
+ async function findRegisteredWindowsExecutable(fileName, run) {
67
+ const target = fileName.replace(/'/g, "''");
68
+ const script = `$ErrorActionPreference='SilentlyContinue';[Console]::OutputEncoding=[Text.Encoding]::UTF8;$target='${target}'
69
+ function Emit-Path($value){if(-not $value){return};$value=[Environment]::ExpandEnvironmentVariables(([string]$value).Trim());if($value.StartsWith('"')){$end=$value.IndexOf('"',1);if($end -gt 1){$value=$value.Substring(1,$end-1)}}elseif($value -match '^(\S+?\.exe)(?:\s|$)'){$value=$Matches[1]};if($value -match '(?i)\.exe$'){[Console]::WriteLine($value)}}
70
+ $keys=@("HKCU:\\Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\$target","HKLM:\\Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\$target","HKLM:\\Software\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\App Paths\\$target","HKCU:\\Software\\Classes\\Applications\\$target\\shell\\open\\command","HKLM:\\Software\\Classes\\Applications\\$target\\shell\\open\\command")
71
+ foreach($key in $keys){$item=Get-Item $key -ErrorAction SilentlyContinue;if($item){Emit-Path $item.GetValue('')}}
72
+ $dirs=@();if($env:APPDATA){$dirs+=[IO.Path]::Combine($env:APPDATA,'Microsoft\\Windows\\Start Menu\\Programs')};if($env:ProgramData){$dirs+=[IO.Path]::Combine($env:ProgramData,'Microsoft\\Windows\\Start Menu\\Programs')};$shell=New-Object -ComObject WScript.Shell
73
+ foreach($dir in $dirs){Get-ChildItem $dir -Filter '*.lnk' -File -Recurse -ErrorAction SilentlyContinue|ForEach-Object{$link=$shell.CreateShortcut($_.FullName);if($link.TargetPath -and [IO.Path]::GetFileName($link.TargetPath) -ieq $target){Emit-Path $link.TargetPath}}}`;
74
+ try {
75
+ const { stdout } = await run("powershell.exe", ["-NoProfile", "-NonInteractive", "-EncodedCommand", Buffer.from(script, "utf16le").toString("base64")], { timeout: 10_000, windowsHide: true });
76
+ for (const candidate of stdout.split(/\r?\n/).map(value => value.trim()).filter(Boolean)) {
77
+ const found = await resolveRegisteredCandidate(fileName, candidate);
78
+ if (found)
79
+ return found;
80
+ }
81
+ }
82
+ catch { /* 注册表或开始菜单不可读时继续使用目录兜底 */ }
83
+ return undefined;
84
+ }
85
+ async function resolveRegisteredCandidate(fileName, candidate) {
86
+ const candidatePath = (0, node_path_1.resolve)(candidate.replace(/^"|"$/g, ""));
87
+ if (fileName.toLowerCase() === "doubao.exe" && (0, node_path_1.basename)((0, node_path_1.dirname)(candidatePath)).toLowerCase() === "application") {
88
+ const client = (0, node_path_1.join)((0, node_path_1.dirname)(candidatePath), "app", fileName);
89
+ if ((0, node_fs_1.existsSync)(client))
90
+ return client;
91
+ }
92
+ if ((0, node_fs_1.existsSync)(candidatePath) && (0, node_path_1.basename)(candidatePath).toLowerCase() === fileName.toLowerCase())
93
+ return candidatePath;
94
+ return findExecutableInTree((0, node_path_1.dirname)(candidatePath), fileName, /^/, 3);
95
+ }
62
96
  async function findExecutableInTree(directory, fileName, pattern, depth) {
63
97
  return search(directory, fileName, pattern, depth, pattern.test(directory));
64
98
  }
@@ -104,9 +138,16 @@ async function launchManagedAndWait(executable, args, profileDirectory, connect,
104
138
  if (profileDirectory)
105
139
  await (0, promises_1.mkdir)(profileDirectory, { recursive: true });
106
140
  const child = launch(executable, profileDirectory ? [...args, `--user-data-dir=${profileDirectory}`] : args);
141
+ // 先等待 spawn,避免异步 error 终止 daemon 或在启动失败后继续轮询。
142
+ const events = child;
143
+ const started = typeof events.once === "function" ? new Promise((resolve, reject) => {
144
+ events.once("error", reject);
145
+ events.once("spawn", resolve);
146
+ }) : Promise.resolve();
107
147
  child.unref();
108
148
  onProcess(child);
109
149
  try {
150
+ await started;
110
151
  return await waitFor(connect, timeoutMs, 250);
111
152
  }
112
153
  catch (error) {