mu-agent-bridge 1.0.69 → 1.0.71

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 (34) hide show
  1. package/dist/src-ts/cli.js +1 -1
  2. package/dist/src-ts/core-updater.js +1 -1
  3. package/dist/src-ts/dsh-agent.js +6 -3
  4. package/dist/src-ts/dsh-paths.js +7 -7
  5. package/dist/src-ts/plugin-manager.js +3 -3
  6. package/dist/src-ts/plugins/abweb/index.js +1 -1
  7. package/dist/src-ts/plugins/aun/evolcore.js +93 -17
  8. package/dist/src-ts/plugins/aun/index.js +11 -2
  9. package/dist/src-ts/plugins/doubao/index.js +1 -1
  10. package/dist/src-ts/plugins/dsh/index.js +5 -2
  11. package/dist/src-ts/version-manager.js +1 -1
  12. package/dist/tests-ts/aun-plugin.test.js +64 -0
  13. package/dist/tests-ts/bundled-package.test.js +3 -1
  14. package/dist/tests-ts/control-server.test.js +52 -2
  15. package/dist/tests-ts/dsh-agent.test.js +38 -8
  16. package/dist/tests-ts/installer-assets.test.js +3 -3
  17. package/package.json +1 -1
  18. package/plugins/abweb/dist/src-ts/plugins/abweb/index.js +1 -1
  19. package/plugins/abweb/package.json +1 -1
  20. package/plugins/abweb/plugin.json +1 -1
  21. package/plugins/aun/dist/src-ts/plugins/aun/evolcore.js +93 -17
  22. package/plugins/aun/dist/src-ts/plugins/aun/index.js +11 -2
  23. package/plugins/aun/package.json +1 -1
  24. package/plugins/aun/plugin.json +1 -1
  25. package/plugins/doubao/dist/src-ts/plugins/doubao/index.js +1 -1
  26. package/plugins/doubao/package.json +1 -1
  27. package/plugins/doubao/plugin.json +1 -1
  28. package/plugins/dsh/config.schema.json +1 -0
  29. package/plugins/dsh/dist/src-ts/dsh-agent.js +6 -3
  30. package/plugins/dsh/dist/src-ts/dsh-paths.js +7 -7
  31. package/plugins/dsh/dist/src-ts/plugins/desktop-launch.js +197 -0
  32. package/plugins/dsh/dist/src-ts/plugins/dsh/index.js +5 -2
  33. package/plugins/dsh/package.json +1 -1
  34. package/plugins/dsh/plugin.json +1 -1
@@ -119,7 +119,7 @@ async function update() {
119
119
  return 2;
120
120
  }
121
121
  await stopDaemon();
122
- await execFileAsync("npm.cmd", ["install", "-g", "mu-agent-bridge@latest", "--no-fund", "--no-audit"], { timeout: 120_000, shell: true });
122
+ await execFileAsync("npm.cmd", ["install", "-g", "mu-agent-bridge@latest", "--no-fund", "--no-audit"], { timeout: 120_000, shell: true, windowsHide: true });
123
123
  await startDaemon();
124
124
  console.log("核心已更新,旧 daemon 已结束并启动新版本");
125
125
  return 0;
@@ -21,7 +21,7 @@ async function sameDirectory(left, right) {
21
21
  }
22
22
  async function runNpm(args) {
23
23
  const command = process.platform === "win32" ? "npm.cmd" : "npm";
24
- const { stdout } = await execFileAsync(command, args, { timeout: 180_000, maxBuffer: 4 * 1024 * 1024, ...(process.platform === "win32" ? { shell: true } : {}) });
24
+ const { stdout } = await execFileAsync(command, args, { timeout: 180_000, maxBuffer: 4 * 1024 * 1024, windowsHide: true, ...(process.platform === "win32" ? { shell: true } : {}) });
25
25
  return stdout.trim();
26
26
  }
27
27
  async function updateCoreAtStartup(options) {
@@ -61,12 +61,14 @@ async function readPresentedFiles(directory, paths, cwd, inputs, returnPaths) {
61
61
  return result;
62
62
  }
63
63
  class DshAgent {
64
+ options;
64
65
  name = "dsh";
65
66
  paths;
66
67
  spawnProcess;
67
68
  defaultTimeoutMs;
68
69
  constructor(options) {
69
- this.paths = options.paths ?? (0, dsh_paths_1.resolveDshPaths)(options.patchPath);
70
+ this.options = options;
71
+ this.paths = options.paths;
70
72
  this.spawnProcess = options.spawn ?? node_child_process_1.spawn;
71
73
  this.defaultTimeoutMs = options.defaultTimeoutMs ?? 300_000;
72
74
  }
@@ -74,6 +76,7 @@ class DshAgent {
74
76
  if (!request.task.trim())
75
77
  throw new agent_1.AgentError("DSH task must not be empty");
76
78
  (0, attachments_1.validateAttachments)(request.attachments);
79
+ const paths = this.paths ?? await (0, dsh_paths_1.resolveDshPaths)(this.options.patchPath);
77
80
  const workDirectory = await (0, promises_1.mkdtemp)((0, node_path_1.join)((0, node_os_1.tmpdir)(), "agent-bridge-dsh-"));
78
81
  let failed = false;
79
82
  try {
@@ -85,11 +88,11 @@ class DshAgent {
85
88
  for (const [index, item] of (request.attachments ?? []).entries())
86
89
  await (0, promises_1.writeFile)(inputPaths[index].returnPath, item.data, { mode: 0o600 });
87
90
  const task = `${request.task}\n\n请使用真实文件工具读取输入附件;生成的回传文件必须写入 outputDirectory 的直接子文件,并调用 present 工具声明文件。原附件已准备 returnPath 副本,原样回传时直接 present 此路径,无需 shell 复制。图片或视频无法理解时请明确说明,仍可原样回传。\n附件传输:\n${JSON.stringify({ inputs: inputPaths, outputDirectory })}`;
88
- const args = ["--expose-internals", this.paths.cli, "--profile", "headless", "--patch", this.paths.patch, "--json"];
91
+ const args = ["--expose-internals", paths.cli, "--profile", "headless", "--patch", paths.patch, "--json"];
89
92
  if (request.sessionId && !request.newSession)
90
93
  args.push("--session-id", request.sessionId);
91
94
  args.push("-");
92
- const child = this.spawnProcess(this.paths.executable, args, {
95
+ const child = this.spawnProcess(paths.executable, args, {
93
96
  cwd: request.cwd,
94
97
  env: { ...process.env, ELECTRON_RUN_AS_NODE: "1" },
95
98
  stdio: ["pipe", "pipe", "pipe"],
@@ -1,15 +1,15 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.resolveDshPaths = resolveDshPaths;
4
- const node_fs_1 = require("node:fs");
5
4
  const node_path_1 = require("node:path");
6
- function resolveDshPaths(patch) {
7
- const override = process.env.DSH_INSTALL_DIR;
8
- const candidates = [override, "D:\\Programs\\DeepSeek Harness", "D:\\DSH"].filter((value) => Boolean(value));
9
- const root = candidates.find((candidate) => (0, node_fs_1.existsSync)((0, node_path_1.join)(candidate, "DeepSeek Harness.exe"))) ?? candidates[0];
5
+ const desktop_launch_1 = require("./plugins/desktop-launch");
6
+ async function resolveDshPaths(patch, configured) {
7
+ const executable = await (0, desktop_launch_1.findWindowsExecutable)("DeepSeek Harness.exe", configured ?? (process.env.DSH_INSTALL_DIR ? (0, node_path_1.join)(process.env.DSH_INSTALL_DIR, "DeepSeek Harness.exe") : undefined));
8
+ if (!executable)
9
+ throw new Error("未找到 DeepSeek Harness 可执行文件,请先安装 DSH 或重新检测客户端路径");
10
10
  return {
11
- executable: (0, node_path_1.join)(root, "DeepSeek Harness.exe"),
12
- cli: (0, node_path_1.join)(root, "resources", "app.asar", "dsh", "node_modules", "@deepseek-ai", "dsh-desktop-host", "lib", "cli.js"),
11
+ executable,
12
+ cli: (0, node_path_1.join)((0, node_path_1.dirname)(executable), "resources", "app.asar", "dsh", "node_modules", "@deepseek-ai", "dsh-desktop-host", "lib", "cli.js"),
13
13
  patch,
14
14
  };
15
15
  }
@@ -102,7 +102,7 @@ class PluginManager {
102
102
  try {
103
103
  const spec = version ? `${packageName}@${version}` : packageName;
104
104
  if (process.platform === "win32")
105
- await execFileAsync("npm.cmd", ["install", "--prefix", target, "--no-save", "--ignore-scripts", spec], { shell: true });
105
+ await execFileAsync("npm.cmd", ["install", "--prefix", target, "--no-save", "--ignore-scripts", spec], { shell: true, windowsHide: true });
106
106
  else
107
107
  await execFileAsync("npm", ["install", "--prefix", target, "--no-save", "--ignore-scripts", spec]);
108
108
  const candidate = (0, node_path_1.join)(target, "node_modules", packageName);
@@ -276,7 +276,7 @@ class PluginManager {
276
276
  this.log("info", `开始安装插件:${packageName}`, undefined, "插件安装");
277
277
  try {
278
278
  if (process.platform === "win32")
279
- await execFileAsync("npm.cmd", ["install", "--prefix", prefix, "--no-save", "--ignore-scripts", packageName], { shell: true });
279
+ await execFileAsync("npm.cmd", ["install", "--prefix", prefix, "--no-save", "--ignore-scripts", packageName], { shell: true, windowsHide: true });
280
280
  else
281
281
  await execFileAsync("npm", ["install", "--prefix", prefix, "--no-save", "--ignore-scripts", packageName]);
282
282
  const directory = (0, node_path_1.join)(prefix, "node_modules", name);
@@ -765,7 +765,7 @@ class PluginManager {
765
765
  const req = (0, node_module_1.createRequire)((0, node_path_1.resolve)(process.cwd(), "package.json"));
766
766
  const roots = [process.cwd()];
767
767
  try {
768
- roots.push((await execFileAsync("npm", ["root", "-g"])).stdout.trim());
768
+ roots.push((await execFileAsync("npm", ["root", "-g"], { windowsHide: true })).stdout.trim());
769
769
  }
770
770
  catch { /* npm 不可用时仍扫描本地依赖 */ }
771
771
  for (const name of names) {
@@ -5,7 +5,7 @@ const node_fs_1 = require("node:fs");
5
5
  const node_path_1 = require("node:path");
6
6
  const MANIFEST = {
7
7
  id: "abweb",
8
- version: "1.0.9",
8
+ version: "1.0.11",
9
9
  displayName: "Agent Bridge Web",
10
10
  entry: "dist/index.js",
11
11
  apiVersion: "1",
@@ -4,10 +4,14 @@ exports.runEc = runEc;
4
4
  exports.configureAidForPlugin = configureAidForPlugin;
5
5
  const node_child_process_1 = require("node:child_process");
6
6
  const node_util_1 = require("node:util");
7
+ const promises_1 = require("node:fs/promises");
8
+ const node_crypto_1 = require("node:crypto");
9
+ const node_os_1 = require("node:os");
10
+ const node_path_1 = require("node:path");
7
11
  const execFileAsync = (0, node_util_1.promisify)(node_child_process_1.execFile);
8
- async function runEc(args) {
12
+ async function runEc(args, home) {
9
13
  try {
10
- const { stdout } = await execFileAsync("ec", args, { shell: process.platform === "win32", timeout: 15_000, maxBuffer: 1024 * 1024 });
14
+ const { stdout } = await execFileAsync("ec", args, { shell: process.platform === "win32", timeout: 60_000, env: { ...process.env, ...(home ? { EVOLCORE_HOME: home } : {}) }, windowsHide: true, maxBuffer: 1024 * 1024 });
11
15
  return JSON.parse(stdout);
12
16
  }
13
17
  catch (error) {
@@ -21,24 +25,96 @@ async function runEc(args) {
21
25
  throw error;
22
26
  }
23
27
  }
24
- async function configureAidForPlugin(pluginId, aid, port, run = runEc) {
25
- if (!aid)
26
- return;
28
+ async function ecIsRunning(home) {
29
+ const records = [];
30
+ try {
31
+ records.push(Number(await (0, promises_1.readFile)((0, node_path_1.join)(home, "daemon.pid"), "utf8")));
32
+ }
33
+ catch (error) {
34
+ if (error.code !== "ENOENT")
35
+ throw error;
36
+ }
37
+ const directory = (0, node_path_1.join)(home, "data", "instance");
38
+ let names;
39
+ try {
40
+ names = await (0, promises_1.readdir)(directory);
41
+ }
42
+ catch (error) {
43
+ if (error.code !== "ENOENT")
44
+ throw error;
45
+ names = [];
46
+ }
47
+ for (const name of names.filter(name => /^main-\d+\.json$/.test(name))) {
48
+ records.push(JSON.parse(await (0, promises_1.readFile)((0, node_path_1.join)(directory, name), "utf8")).pid);
49
+ }
50
+ return records.some(pid => {
51
+ if (!Number.isSafeInteger(pid) || pid <= 0)
52
+ return false;
53
+ try {
54
+ process.kill(pid, 0);
55
+ return true;
56
+ }
57
+ catch (error) {
58
+ if (error.code === "ESRCH")
59
+ return false;
60
+ throw error;
61
+ }
62
+ });
63
+ }
64
+ async function configureAidForPlugin(pluginId, aid, port, run = runEc, home = process.env.EVOLCORE_HOME ?? (0, node_path_1.join)((0, node_os_1.homedir)(), ".evolcore")) {
65
+ if (!/^[a-z0-9][a-z0-9_.-]+$/i.test(aid) || !aid.includes("."))
66
+ throw new Error("AID 格式无效");
27
67
  if (!/^[a-z0-9_-]+$/.test(pluginId))
28
68
  throw new Error("插件 ID 格式无效");
29
- // 通过 daemon 的统一插件代理访问所有 Agent;否则 qwenwork 的裸 /v1 路由在有插件 HTTP 注册表时不存在。
69
+ if (!Number.isInteger(port) || port < 1 || port > 65535)
70
+ throw new Error("后台端口无效");
30
71
  const path = pluginId === "dsh" ? "/dsh/v1" : `/api/agents/${pluginId}/v1`;
31
- for (const [key, value] of [
32
- ["baseagents.ecagent.baseUrl", `http://127.0.0.1:${port}${path}`],
33
- ["baseagents.ecagent.apiKey", "mab-local"],
34
- ["baseagents.ecagent.model", pluginId],
35
- ["active_baseagent", "ecagent"],
36
- ]) {
37
- const result = await run(["agent", "set", aid, key, value, "--format", "json"]);
72
+ const ecagent = { baseUrl: `http://127.0.0.1:${port}${path}`, apiKey: "mab-local", model: pluginId };
73
+ try {
74
+ // MAB 直接接收 AUN 消息,先停用该 AID 的 EvolCore Agent,避免双重回复。
75
+ const disabled = await run(["agent", "disable", aid, "--format", "json"], home);
76
+ if (!disabled?.ok)
77
+ throw new Error(`停用原 Agent 失败:${disabled?.code ?? ""} ${disabled?.error ?? "未知错误"}`);
78
+ for (const [key, value] of [
79
+ ...Object.entries(ecagent).map(([key, value]) => [`baseagents.ecagent.${key}`, value]),
80
+ ["active_baseagent", "ecagent"],
81
+ ]) {
82
+ const result = await run(["agent", "set", aid, key, value, "--format", "json"], home);
83
+ if (!result?.ok)
84
+ throw new Error(`配置 ${key} 失败:${result?.code ?? ""} ${result?.error ?? "未知错误"}`);
85
+ }
86
+ const result = await run(["agent", "reload", aid, "--format", "json"], home);
38
87
  if (!result?.ok)
39
- throw new Error(`配置 AID ${aid} 失败:${key};${result?.code ?? ""} ${result?.error ?? "未知错误"}`);
88
+ throw new Error(`重载失败:${result?.code ?? ""} ${result?.error ?? "未知错误"}`);
89
+ return "ec";
90
+ }
91
+ catch (commandError) {
92
+ const file = (0, node_path_1.join)(home, "agents", aid, "config.json");
93
+ const config = JSON.parse(await (0, promises_1.readFile)(file, "utf8"));
94
+ if (!config || typeof config !== "object" || Array.isArray(config) || config.baseagents && (typeof config.baseagents !== "object" || Array.isArray(config.baseagents)))
95
+ throw new Error(`AID ${aid} 原配置格式无效,未覆盖文件`);
96
+ config.enabled = false;
97
+ config.active_baseagent = "ecagent";
98
+ config.baseagents = { ...config.baseagents, ecagent: { ...config.baseagents?.ecagent, ...ecagent } };
99
+ const temporary = `${file}.mab-${(0, node_crypto_1.randomUUID)()}.tmp`;
100
+ try {
101
+ await (0, promises_1.writeFile)(temporary, `${JSON.stringify(config, null, 2)}\n`, { mode: 0o600 });
102
+ await (0, promises_1.rename)(temporary, file);
103
+ }
104
+ finally {
105
+ await (0, promises_1.rm)(temporary, { force: true });
106
+ }
107
+ // 写盘不能关闭运行中的旧 Agent;存在活跃实例时必须确认重载成功。
108
+ if (await ecIsRunning(home)) {
109
+ try {
110
+ const result = await run(["agent", "reload", aid, "--format", "json"], home);
111
+ if (!result?.ok)
112
+ throw new Error(result?.error ?? "重载失败");
113
+ }
114
+ catch (error) {
115
+ throw new AggregateError([commandError, error], `AID ${aid} 配置已保存,但 EvolCore 仍在运行,原 Agent 未确认停用:${error instanceof Error ? error.message : error}`);
116
+ }
117
+ }
118
+ return "json";
40
119
  }
41
- const result = await run(["agent", "reload", aid, "--format", "json"]);
42
- if (!result?.ok)
43
- throw new Error(`重载 AID ${aid} 失败:${result?.code ?? ""} ${result?.error ?? "未知错误"}`);
44
120
  }
@@ -9,10 +9,11 @@ const node_path_1 = require("node:path");
9
9
  const node_os_1 = require("node:os");
10
10
  const node_url_1 = require("node:url");
11
11
  const agent_1 = require("../../agent");
12
+ const evolcore_1 = require("./evolcore");
12
13
  const messages_1 = require("./messages");
13
14
  const attachments_1 = require("./attachments");
14
15
  const manifest = {
15
- id: "aun", version: "1.0.34", type: "channel", displayName: "AUN 通道",
16
+ id: "aun", version: "1.0.35", type: "channel", displayName: "AUN 通道",
16
17
  entry: "dist/src-ts/plugins/aun/index.js", apiVersion: "1", process: "in-process",
17
18
  capabilities: ["channel", "channel.identity.create", "channel.receive", "channel.send", "health", "cli"],
18
19
  configSchema: "config.schema.json",
@@ -124,7 +125,7 @@ class AunPlugin {
124
125
  this.creating = false;
125
126
  }
126
127
  },
127
- bind: async (pluginId, aid, _port) => {
128
+ bind: async (pluginId, aid, port) => {
128
129
  if (!aid || !aidPattern.test(aid))
129
130
  throw new Error("AID 格式无效,必须指定要关联的 AID");
130
131
  if (pluginId === "aun" || pluginId === "abweb")
@@ -140,6 +141,14 @@ class AunPlugin {
140
141
  const loaded = this.store.load(aid);
141
142
  if (!loaded.ok || !loaded.data)
142
143
  throw new Error("AID 不在本地密钥库中,无法关联");
144
+ // 仅接管用户本次选中的 EvolCore Agent;纯密钥库身份不调用 ec。
145
+ if ((0, node_fs_1.existsSync)((0, node_path_1.join)(this.path(), "agents", aid, "config.json"))) {
146
+ const method = await (0, evolcore_1.configureAidForPlugin)(pluginId, aid, port, this.options.runEc ?? evolcore_1.runEc, this.path());
147
+ if (method === "json")
148
+ this.context?.logger.warn("ec 命令不可用或失败,已通过 JSON 停用原 Agent 并配置 ecagent", { aid, pluginId });
149
+ else
150
+ this.context?.logger.info("已通过 ec 命令停用原 Agent 并切换为 ecagent", { aid, pluginId });
151
+ }
143
152
  this.identities.add(aid);
144
153
  if (!connection)
145
154
  await this.connectAid(aid);
@@ -10,7 +10,7 @@ const desktop_launch_1 = require("../desktop-launch");
10
10
  const node_os_1 = require("node:os");
11
11
  exports.doubaoManifest = {
12
12
  id: "doubao",
13
- version: "1.0.21",
13
+ version: "1.0.23",
14
14
  displayName: "豆包",
15
15
  entry: "dist/index.js",
16
16
  apiVersion: "1",
@@ -3,11 +3,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.DshPlugin = exports.dshManifest = void 0;
4
4
  const node_path_1 = require("node:path");
5
5
  const dsh_agent_1 = require("../../dsh-agent");
6
+ const dsh_paths_1 = require("../../dsh-paths");
6
7
  const openai_adapter_1 = require("../qwenwork/openai-adapter");
7
8
  const session_history_1 = require("./session-history");
8
9
  exports.dshManifest = {
9
10
  id: "dsh",
10
- version: "1.0.10",
11
+ version: "1.0.11",
11
12
  displayName: "DeepSeek Harness",
12
13
  entry: "dist/index.js",
13
14
  apiVersion: "1",
@@ -31,7 +32,9 @@ class DshPlugin {
31
32
  const patchPath = typeof configured === "string" && configured.trim()
32
33
  ? configured
33
34
  : (0, node_path_1.resolve)(__dirname, "..", "..", "headless-account.patch.yml");
34
- this.agent = new dsh_agent_1.DshAgent({ patchPath });
35
+ const paths = await (0, dsh_paths_1.resolveDshPaths)(patchPath, typeof context.config.executablePath === "string" ? context.config.executablePath : undefined);
36
+ this.agent = new dsh_agent_1.DshAgent({ patchPath, paths });
37
+ context.logger?.info?.("DSH 客户端路径已确认", { executablePath: paths.executable });
35
38
  this.sessionHistory = new session_history_1.DshSessionHistoryProvider({
36
39
  sessionsRoot: typeof context.config.sessionsRoot === "string" ? context.config.sessionsRoot : undefined,
37
40
  unavailableReason: () => this.status !== "ready" ? "DSH 插件尚未就绪" : this.activeRequests ? "DSH 正在处理任务,暂不读取持久化历史" : "",
@@ -76,6 +76,6 @@ class VersionManager {
76
76
  this.saveTask = this.saveTask.catch(() => undefined).then(async () => { await (0, promises_1.mkdir)(this.options.stateDir, { recursive: true }); await (0, promises_1.writeFile)((0, node_path_1.join)(this.options.stateDir, "versions.json"), JSON.stringify(this.list(), null, 2)); });
77
77
  await this.saveTask;
78
78
  }
79
- async queryNpm(args) { return (await execFileAsync(process.platform === "win32" ? "npm.cmd" : "npm", args, { timeout: 30_000, maxBuffer: 1024 * 1024, ...(process.platform === "win32" ? { shell: true } : {}) })).stdout; }
79
+ async queryNpm(args) { return (await execFileAsync(process.platform === "win32" ? "npm.cmd" : "npm", args, { timeout: 30_000, maxBuffer: 1024 * 1024, windowsHide: true, ...(process.platform === "win32" ? { shell: true } : {}) })).stdout; }
80
80
  }
81
81
  exports.VersionManager = VersionManager;
@@ -1497,3 +1497,67 @@ async function withAunAttachmentPlugin(run, publish, config = { allow_from: "pee
1497
1497
  await message.reply("任务最终回复");
1498
1498
  });
1499
1499
  });
1500
+ (0, node_test_1.default)("AUN 显式关联先停用所选 EvolCore Agent 并切换 ecagent,不改其它身份或纯密钥库身份", async () => {
1501
+ const root = await (0, promises_1.mkdtemp)((0, node_path_1.join)((0, node_os_1.tmpdir)(), "aun-ec-handoff-"));
1502
+ const selected = "selected.agentid.cn";
1503
+ const other = "other.agentid.cn";
1504
+ const local = "local.agentid.cn";
1505
+ const calls = [];
1506
+ const events = [];
1507
+ let fail = false;
1508
+ const sdk = {
1509
+ AIDStore: class {
1510
+ list() { return { ok: true, data: { identities: [selected, other, local].map(aid => ({ aid })) } }; }
1511
+ load(aid) { return { ok: true, data: { aid } }; }
1512
+ close() { }
1513
+ },
1514
+ AUNClient: class {
1515
+ aid;
1516
+ constructor(aid) {
1517
+ this.aid = aid;
1518
+ }
1519
+ on() { return { unsubscribe() { } }; }
1520
+ async connect() { events.push(`connect:${this.aid}`); }
1521
+ async close() { }
1522
+ },
1523
+ };
1524
+ const run = async (args, home) => {
1525
+ strict_1.default.equal(home, root);
1526
+ calls.push(args);
1527
+ events.push(args[1]);
1528
+ return fail ? { ok: false, error: "停用失败" } : { ok: true };
1529
+ };
1530
+ const plugin = new aun_1.AunPlugin({ sdk, runEc: run, ingress: { publish: async () => undefined } });
1531
+ const original = JSON.stringify({ enabled: true, active_baseagent: "claude" });
1532
+ try {
1533
+ for (const aid of [selected, other]) {
1534
+ await (0, promises_1.mkdir)((0, node_path_1.join)(root, "agents", aid), { recursive: true });
1535
+ await (0, promises_1.writeFile)((0, node_path_1.join)(root, "agents", aid, "config.json"), original);
1536
+ }
1537
+ await plugin.activate({ dataDir: (0, node_path_1.join)(root, "state"), config: { aun_path: root }, logger: { info() { }, warn() { }, error() { } }, callPlugin: async () => ({ text: "" }), pluginStatus: () => "ready", http: { register() { }, unregister() { } } });
1538
+ await (0, promises_1.mkdir)((0, node_path_1.join)(root, "data", "instance"), { recursive: true });
1539
+ const instance = (0, node_path_1.join)(root, "data", "instance", `main-${process.pid}.json`);
1540
+ await (0, promises_1.writeFile)(instance, JSON.stringify({ pid: process.pid }));
1541
+ fail = true;
1542
+ await strict_1.default.rejects(plugin.identity.bind("workbuddy", selected, 26107), /仍在运行.*未确认停用/);
1543
+ await (0, promises_1.rm)(instance);
1544
+ strict_1.default.deepEqual((await plugin.health()).details?.bindings, {});
1545
+ strict_1.default.equal(events.some(value => value.startsWith("connect:")), false);
1546
+ calls.length = 0;
1547
+ events.length = 0;
1548
+ fail = false;
1549
+ await plugin.identity.bind("workbuddy", selected, 26107);
1550
+ strict_1.default.deepEqual(events, ["disable", "set", "set", "set", "set", "reload", `connect:${selected}`]);
1551
+ strict_1.default.ok(calls.every(args => args[2] === selected));
1552
+ strict_1.default.equal(calls[1][4], "http://127.0.0.1:26107/api/agents/workbuddy/v1");
1553
+ strict_1.default.equal(calls[4][4], "ecagent");
1554
+ strict_1.default.equal(await (0, promises_1.readFile)((0, node_path_1.join)(root, "agents", other, "config.json"), "utf8"), original);
1555
+ await plugin.identity.bind("dsh", local, 26107);
1556
+ strict_1.default.equal(calls.length, 6);
1557
+ strict_1.default.deepEqual((await plugin.health()).details?.bindings, { [selected]: "workbuddy", [local]: "dsh" });
1558
+ }
1559
+ finally {
1560
+ await plugin.deactivate("stop");
1561
+ await (0, promises_1.rm)(root, { recursive: true, force: true });
1562
+ }
1563
+ });
@@ -9,13 +9,15 @@ const node_path_1 = require("node:path");
9
9
  const node_module_1 = require("node:module");
10
10
  const node_test_1 = __importDefault(require("node:test"));
11
11
  const ids = ["abweb", "aun", "chatgpt", "claude-cli", "codex-cli", "doubao", "dsh", "feishu", "qwenwork", "workbuddy"];
12
- (0, node_test_1.default)("独立包和默认插件的运行时依赖可加载", async () => {
12
+ (0, node_test_1.default)("独立包和默认插件的运行时依赖可加载且代码版本一致", async () => {
13
13
  const load = (0, node_module_1.createRequire)(__filename);
14
14
  for (const id of ids) {
15
15
  for (const root of ["packages", "plugins"]) {
16
16
  const directory = (0, node_path_1.join)(process.cwd(), root, id);
17
17
  const manifest = JSON.parse(await (0, promises_1.readFile)((0, node_path_1.join)(directory, "plugin.json"), "utf8"));
18
18
  strict_1.default.doesNotThrow(() => load((0, node_path_1.join)(directory, manifest.entry)), `${root}/${id} 的运行时依赖必须完整`);
19
+ const instance = new (load((0, node_path_1.join)(directory, manifest.entry)).default)();
20
+ strict_1.default.equal(instance.manifest.version, manifest.version, `${root}/${id} 的运行时代码版本必须与包声明一致`);
19
21
  }
20
22
  }
21
23
  });
@@ -72,16 +72,17 @@ const plugin_manager_1 = require("../src-ts/plugin-manager");
72
72
  const calls = [];
73
73
  await (0, evolcore_1.configureAidForPlugin)("dsh", "test.agentid.cn", 26107, async (args) => { calls.push(args); return { ok: true }; });
74
74
  strict_1.default.deepEqual(calls.map((args) => args.slice(0, 4)), [
75
+ ["agent", "disable", "test.agentid.cn", "--format"],
75
76
  ["agent", "set", "test.agentid.cn", "baseagents.ecagent.baseUrl"],
76
77
  ["agent", "set", "test.agentid.cn", "baseagents.ecagent.apiKey"],
77
78
  ["agent", "set", "test.agentid.cn", "baseagents.ecagent.model"],
78
79
  ["agent", "set", "test.agentid.cn", "active_baseagent"],
79
80
  ["agent", "reload", "test.agentid.cn", "--format"],
80
81
  ]);
81
- strict_1.default.equal(calls[0][4], "http://127.0.0.1:26107/dsh/v1");
82
+ strict_1.default.equal(calls[1][4], "http://127.0.0.1:26107/dsh/v1");
82
83
  calls.length = 0;
83
84
  await (0, evolcore_1.configureAidForPlugin)("qwenwork", "qwen.agentid.cn", 26107, async (args) => { calls.push(args); return { ok: true }; });
84
- strict_1.default.equal(calls[0][4], "http://127.0.0.1:26107/api/agents/qwenwork/v1");
85
+ strict_1.default.equal(calls[1][4], "http://127.0.0.1:26107/api/agents/qwenwork/v1");
85
86
  });
86
87
  (0, node_test_1.default)("通用 Agent 路由调用插件且关联失败进入日志", async () => {
87
88
  const logs = [];
@@ -522,3 +523,52 @@ const plugin_manager_1 = require("../src-ts/plugin-manager");
522
523
  await anonymousServer.handle({ method: "DELETE", url: "/api/session-history", headers: {}, socket: { remoteAddress: "192.168.31.10" } }, response);
523
524
  strict_1.default.equal(response.statusCode, 401);
524
525
  });
526
+ (0, node_test_1.default)("ec 命令缺失或失败且 EvolCore 未运行时,仅以 JSON 接管指定 AID", async () => {
527
+ const root = await (0, promises_1.mkdtemp)((0, node_path_1.join)((0, node_os_1.tmpdir)(), "ec-fallback-"));
528
+ const aid = "selected.agentid.cn";
529
+ const configPath = (0, node_path_1.join)(root, "agents", aid, "config.json");
530
+ const other = (0, node_path_1.join)(root, "agents", "other.agentid.cn", "config.json");
531
+ const original = { enabled: true, active_baseagent: "claude", owner: "owner.agentid.cn", baseagents: { claude: { model: "原模型" }, ecagent: { effort: "high" } } };
532
+ try {
533
+ await (0, promises_1.mkdir)((0, node_path_1.join)(root, "agents", aid), { recursive: true });
534
+ await (0, promises_1.mkdir)((0, node_path_1.join)(root, "agents", "other.agentid.cn"), { recursive: true });
535
+ await (0, promises_1.writeFile)(other, JSON.stringify(original));
536
+ for (const mode of ["missing", "disable", "reload"]) {
537
+ await (0, promises_1.writeFile)(configPath, JSON.stringify(original));
538
+ const run = async (args) => {
539
+ if (mode === "missing")
540
+ throw Object.assign(new Error("ec 不存在"), { code: "ENOENT" });
541
+ return { ok: mode === "reload" && args[1] !== "reload", error: "ec 未运行" };
542
+ };
543
+ const result = await (0, evolcore_1.configureAidForPlugin)("workbuddy", aid, 26107, run, root);
544
+ strict_1.default.equal(result, "json");
545
+ strict_1.default.deepEqual(JSON.parse(await (0, promises_1.readFile)(configPath, "utf8")), { ...original, enabled: false, active_baseagent: "ecagent", baseagents: { ...original.baseagents, ecagent: { effort: "high", baseUrl: "http://127.0.0.1:26107/api/agents/workbuddy/v1", apiKey: "mab-local", model: "workbuddy" } } });
546
+ strict_1.default.equal(await (0, promises_1.readFile)(other, "utf8"), JSON.stringify(original));
547
+ }
548
+ await (0, promises_1.writeFile)(configPath, "无效 JSON");
549
+ await strict_1.default.rejects((0, evolcore_1.configureAidForPlugin)("workbuddy", aid, 26107, async () => ({ ok: false }), root));
550
+ strict_1.default.equal(await (0, promises_1.readFile)(configPath, "utf8"), "无效 JSON");
551
+ }
552
+ finally {
553
+ await (0, promises_1.rm)(root, { recursive: true, force: true });
554
+ }
555
+ });
556
+ (0, node_test_1.default)("ec 仍在运行时 JSON 兜底后必须重载成功,不能误报原 Agent 已停用", async () => {
557
+ const root = await (0, promises_1.mkdtemp)((0, node_path_1.join)((0, node_os_1.tmpdir)(), "ec-live-fallback-"));
558
+ const aid = "selected.agentid.cn";
559
+ const configPath = (0, node_path_1.join)(root, "agents", aid, "config.json");
560
+ try {
561
+ await (0, promises_1.mkdir)((0, node_path_1.join)(root, "agents", aid), { recursive: true });
562
+ await (0, promises_1.writeFile)(configPath, JSON.stringify({ enabled: true, active_baseagent: "claude" }));
563
+ await (0, promises_1.mkdir)((0, node_path_1.join)(root, "data", "instance"), { recursive: true });
564
+ await (0, promises_1.writeFile)((0, node_path_1.join)(root, "data", "instance", `main-${process.pid}.json`), JSON.stringify({ pid: process.pid }));
565
+ await strict_1.default.rejects((0, evolcore_1.configureAidForPlugin)("workbuddy", aid, 26107, async () => ({ ok: false, error: "停用失败" }), root), /仍在运行.*未确认停用/);
566
+ strict_1.default.equal(JSON.parse(await (0, promises_1.readFile)(configPath, "utf8")).enabled, false);
567
+ const calls = [];
568
+ strict_1.default.equal(await (0, evolcore_1.configureAidForPlugin)("workbuddy", aid, 26107, async (args) => { calls.push(args); return { ok: args[1] === "reload" }; }, root), "json");
569
+ strict_1.default.equal(calls.at(-1)?.[1], "reload");
570
+ }
571
+ finally {
572
+ await (0, promises_1.rm)(root, { recursive: true, force: true });
573
+ }
574
+ });
@@ -40,6 +40,9 @@ const strict_1 = __importDefault(require("node:assert/strict"));
40
40
  const node_events_1 = require("node:events");
41
41
  const node_test_1 = __importDefault(require("node:test"));
42
42
  const node_path_1 = require("node:path");
43
+ const promises_1 = require("node:fs/promises");
44
+ const node_os_1 = require("node:os");
45
+ const dsh_paths_1 = require("../src-ts/dsh-paths");
43
46
  const dsh_agent_1 = require("../src-ts/dsh-agent");
44
47
  const dsh_1 = require("../src-ts/plugins/dsh");
45
48
  (0, node_test_1.default)("DSH 插件声明由通用层处理的新会话能力", () => {
@@ -115,7 +118,7 @@ function fakeSpawn(_file, _args, _options) {
115
118
  });
116
119
  (0, node_test_1.default)("DSH 无效附件在启动进程前拒绝", async () => {
117
120
  let called = false;
118
- const agent = new dsh_agent_1.DshAgent({ patchPath: "patch.yml", spawn: (...args) => { called = true; return fakeSpawn(...args); } });
121
+ const agent = new dsh_agent_1.DshAgent({ patchPath: "patch.yml", paths: { executable: "dsh.exe", cli: "cli.js", patch: "patch.yml" }, spawn: (...args) => { called = true; return fakeSpawn(...args); } });
119
122
  await strict_1.default.rejects(agent.send({ task: "读取", attachments: [{ kind: "file", filename: "x", contentType: "text/plain", data: new Uint8Array() }] }), /附件内容不能为空/);
120
123
  strict_1.default.equal(called, false);
121
124
  });
@@ -170,9 +173,9 @@ function presentSpawn(prepare, status = "completed", text = "已完成") {
170
173
  strict_1.default.deepEqual(await (await Promise.resolve().then(() => __importStar(require("node:fs/promises")))).readFile(block.inputs[0].returnPath), input.data);
171
174
  return [block.inputs[0].returnPath];
172
175
  });
173
- const response = await new dsh_agent_1.DshAgent({ patchPath: "patch.yml", spawn }).send({ task: "原样回传", attachments: [input] });
176
+ const response = await new dsh_agent_1.DshAgent({ patchPath: "patch.yml", paths: { executable: "dsh.exe", cli: "cli.js", patch: "patch.yml" }, spawn }).send({ task: "原样回传", attachments: [input] });
174
177
  strict_1.default.deepEqual(response.attachments, [{ ...input, data: new Uint8Array(input.data) }]);
175
- const withoutPresent = await new dsh_agent_1.DshAgent({ patchPath: "patch.yml", spawn: fakeSpawn }).send({ task: "只读取", attachments: [input] });
178
+ const withoutPresent = await new dsh_agent_1.DshAgent({ patchPath: "patch.yml", paths: { executable: "dsh.exe", cli: "cli.js", patch: "patch.yml" }, spawn: fakeSpawn }).send({ task: "只读取", attachments: [input] });
176
179
  strict_1.default.equal(withoutPresent.attachments, undefined);
177
180
  });
178
181
  (0, node_test_1.default)("DSH 无输入附件时仍可生成并回传文件", async () => {
@@ -181,7 +184,7 @@ function presentSpawn(prepare, status = "completed", text = "已完成") {
181
184
  await (await Promise.resolve().then(() => __importStar(require("node:fs/promises")))).writeFile(path, "生成文件");
182
185
  return [path];
183
186
  });
184
- const response = await new dsh_agent_1.DshAgent({ patchPath: "patch.yml", spawn }).send({ task: "创建文本文件并回传" });
187
+ const response = await new dsh_agent_1.DshAgent({ patchPath: "patch.yml", paths: { executable: "dsh.exe", cli: "cli.js", patch: "patch.yml" }, spawn }).send({ task: "创建文本文件并回传" });
185
188
  strict_1.default.equal(Buffer.from(response.attachments[0].data).toString(), "生成文件");
186
189
  });
187
190
  (0, node_test_1.default)("DSH 允许只有成功交付附件的空正文回复", async () => {
@@ -190,13 +193,13 @@ function presentSpawn(prepare, status = "completed", text = "已完成") {
190
193
  await (await Promise.resolve().then(() => __importStar(require("node:fs/promises")))).writeFile(path, "附件正文");
191
194
  return [path];
192
195
  }, "completed", "");
193
- const response = await new dsh_agent_1.DshAgent({ patchPath: "patch.yml", spawn }).send({ task: "只回传文件" });
196
+ const response = await new dsh_agent_1.DshAgent({ patchPath: "patch.yml", paths: { executable: "dsh.exe", cli: "cli.js", patch: "patch.yml" }, spawn }).send({ task: "只回传文件" });
194
197
  strict_1.default.equal(response.text, "");
195
198
  strict_1.default.equal(response.attachments?.length, 1);
196
199
  });
197
200
  (0, node_test_1.default)("DSH 不回传失败 present 调用的文件", async () => {
198
201
  const spawn = presentSpawn(async () => ["/不存在的路径"], "error");
199
- const response = await new dsh_agent_1.DshAgent({ patchPath: "patch.yml", spawn }).send({ task: "创建文件", attachments: [{ kind: "file", data: Buffer.from("x"), filename: "x.txt", contentType: "text/plain" }] });
202
+ const response = await new dsh_agent_1.DshAgent({ patchPath: "patch.yml", paths: { executable: "dsh.exe", cli: "cli.js", patch: "patch.yml" }, spawn }).send({ task: "创建文件", attachments: [{ kind: "file", data: Buffer.from("x"), filename: "x.txt", contentType: "text/plain" }] });
200
203
  strict_1.default.equal(response.attachments, undefined);
201
204
  });
202
205
  (0, node_test_1.default)("DSH 拒绝回传输出目录外的文件与符号链接", async () => {
@@ -209,7 +212,7 @@ function presentSpawn(prepare, status = "completed", text = "已完成") {
209
212
  await (await Promise.resolve().then(() => __importStar(require("node:fs/promises")))).symlink(source, path);
210
213
  return [path];
211
214
  });
212
- await strict_1.default.rejects(new dsh_agent_1.DshAgent({ patchPath: "patch.yml", spawn }).send({ task: "回传文件", attachments: [{ kind: "file", data: Buffer.from("x"), filename: "x.txt", contentType: "text/plain" }] }), /输出目录|普通文件/);
215
+ await strict_1.default.rejects(new dsh_agent_1.DshAgent({ patchPath: "patch.yml", paths: { executable: "dsh.exe", cli: "cli.js", patch: "patch.yml" }, spawn }).send({ task: "回传文件", attachments: [{ kind: "file", data: Buffer.from("x"), filename: "x.txt", contentType: "text/plain" }] }), /输出目录|普通文件/);
213
216
  }
214
217
  });
215
218
  (0, node_test_1.default)("DSH 拒绝回传超量和超大附件", async () => {
@@ -222,6 +225,33 @@ function presentSpawn(prepare, status = "completed", text = "已完成") {
222
225
  await truncate(path, 32 * 1024 * 1024 + 1);
223
226
  return limit === "count" ? Array.from({ length: 9 }, (_, index) => (0, node_path_1.join)(block.outputDirectory, `${index}.txt`)) : [path];
224
227
  });
225
- await strict_1.default.rejects(new dsh_agent_1.DshAgent({ patchPath: "patch.yml", spawn }).send({ task: "回传文件" }), /最多|大小/);
228
+ await strict_1.default.rejects(new dsh_agent_1.DshAgent({ patchPath: "patch.yml", paths: { executable: "dsh.exe", cli: "cli.js", patch: "patch.yml" }, spawn }).send({ task: "回传文件" }), /最多|大小/);
229
+ }
230
+ });
231
+ (0, node_test_1.default)("DSH 使用发现的真实安装路径构建 CLI 路径,启动前拒绝不存在的程序", async () => {
232
+ const root = await (0, promises_1.mkdtemp)((0, node_path_1.join)((0, node_os_1.tmpdir)(), "dsh-install-"));
233
+ const executable = (0, node_path_1.join)(root, "DeepSeek Harness.exe");
234
+ const before = process.env.DSH_INSTALL_DIR;
235
+ try {
236
+ await (0, promises_1.writeFile)(executable, "");
237
+ const paths = await (0, dsh_paths_1.resolveDshPaths)("patch.yml", executable);
238
+ strict_1.default.equal(paths.executable, executable);
239
+ strict_1.default.equal(paths.cli, (0, node_path_1.join)(root, "resources", "app.asar", "dsh", "node_modules", "@deepseek-ai", "dsh-desktop-host", "lib", "cli.js"));
240
+ const plugin = new dsh_1.DshPlugin();
241
+ await plugin.activate({ config: { executablePath: executable } });
242
+ strict_1.default.equal(plugin.agent.paths.executable, executable);
243
+ await plugin.deactivate();
244
+ process.env.DSH_INSTALL_DIR = root;
245
+ strict_1.default.equal((await (0, dsh_paths_1.resolveDshPaths)("patch.yml")).executable, executable);
246
+ await (0, promises_1.rm)(executable);
247
+ if (process.platform !== "win32")
248
+ await strict_1.default.rejects((0, dsh_paths_1.resolveDshPaths)("patch.yml", executable), /未找到.*DeepSeek Harness/);
249
+ }
250
+ finally {
251
+ if (before === undefined)
252
+ delete process.env.DSH_INSTALL_DIR;
253
+ else
254
+ process.env.DSH_INSTALL_DIR = before;
255
+ await (0, promises_1.rm)(root, { recursive: true, force: true });
226
256
  }
227
257
  });
@@ -8,7 +8,7 @@ const promises_1 = require("node:fs/promises");
8
8
  const node_test_1 = __importDefault(require("node:test"));
9
9
  const node_child_process_1 = require("node:child_process");
10
10
  const node_util_1 = require("node:util");
11
- (0, node_test_1.default)("Windows 向导识别自定义安装目录并跳过 DSH 不支持的配置", { skip: process.platform !== "win32" }, async () => {
11
+ (0, node_test_1.default)("Windows 向导识别自定义安装目录并保存 DSH 真实路径", { skip: process.platform !== "win32" }, async () => {
12
12
  const { stdout } = await (0, node_util_1.promisify)(node_child_process_1.execFile)("powershell.exe", ["-NoProfile", "-ExecutionPolicy", "Bypass", "-File", "tests/windows-installer-detection.test.ps1"]);
13
13
  strict_1.default.match(stdout, /自定义安装目录检测验证通过/);
14
14
  strict_1.default.match(stdout, /DSH GUI 配置验证通过/);
@@ -78,7 +78,7 @@ const node_util_1 = require("node:util");
78
78
  strict_1.default.match(setup, /环境与桥接服务/);
79
79
  strict_1.default.match(setup, /Get-ChildItem -LiteralPath \$root -Filter \$fileName -File -Recurse/);
80
80
  strict_1.default.match(setup, /api\/plugins\/\$agent\/config/);
81
- strict_1.default.match(setup, /TopMost = \$true/);
81
+ strict_1.default.doesNotMatch(setup, /TopMost = \$true/);
82
82
  strict_1.default.match(setup, /BringToFront\(\)/);
83
83
  strict_1.default.match(setup, /Invoke-AunCommand @\('list'\)/);
84
84
  strict_1.default.match(setup, /自动解除未安装千问办公的残留绑定/);
@@ -109,7 +109,7 @@ const node_util_1 = require("node:util");
109
109
  strict_1.default.match(setup, /AddSeconds\(180\)/);
110
110
  strict_1.default.match(setup, /System.Windows.Forms.LinkLabel/);
111
111
  strict_1.default.match(setup, /重新检测/);
112
- for (const domain of ["qwenwork.cn/download", "www.doubao.com", "www.workbuddy.ai", "github.com/deepseek-ai/deepseek-harness"])
112
+ for (const domain of ["qwenwork.cn/download", "www.doubao.com", "www.workbuddy.ai", "download.deepseek.com/desktop/dsh-latest-windows-x64.exe"])
113
113
  strict_1.default.ok(setup.includes(domain));
114
114
  });
115
115
  (0, node_test_1.default)("先检测并显示缺失 App,再配置 AID,最后单独绑定 Agent", async () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mu-agent-bridge",
3
- "version": "1.0.69",
3
+ "version": "1.0.71",
4
4
  "description": "将 AUN Agent 连接到本地桌面 AI 客户端的插件平台。",
5
5
  "main": "dist/src-ts/cli.js",
6
6
  "bin": {
@@ -5,7 +5,7 @@ const node_fs_1 = require("node:fs");
5
5
  const node_path_1 = require("node:path");
6
6
  const MANIFEST = {
7
7
  id: "abweb",
8
- version: "1.0.9",
8
+ version: "1.0.11",
9
9
  displayName: "Agent Bridge Web",
10
10
  entry: "dist/index.js",
11
11
  apiVersion: "1",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-bridge-abweb",
3
- "version": "1.0.10",
3
+ "version": "1.0.11",
4
4
  "description": "agent-bridge 的网页管理插件。",
5
5
  "keywords": [
6
6
  "mu-agent-bridge-plugin",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "id": "abweb",
3
- "version": "1.0.10",
3
+ "version": "1.0.11",
4
4
  "type": "management",
5
5
  "displayName": "Agent Bridge Web",
6
6
  "entry": "dist/src-ts/plugins/abweb/index.js",
@@ -4,10 +4,14 @@ exports.runEc = runEc;
4
4
  exports.configureAidForPlugin = configureAidForPlugin;
5
5
  const node_child_process_1 = require("node:child_process");
6
6
  const node_util_1 = require("node:util");
7
+ const promises_1 = require("node:fs/promises");
8
+ const node_crypto_1 = require("node:crypto");
9
+ const node_os_1 = require("node:os");
10
+ const node_path_1 = require("node:path");
7
11
  const execFileAsync = (0, node_util_1.promisify)(node_child_process_1.execFile);
8
- async function runEc(args) {
12
+ async function runEc(args, home) {
9
13
  try {
10
- const { stdout } = await execFileAsync("ec", args, { shell: process.platform === "win32", timeout: 15_000, maxBuffer: 1024 * 1024 });
14
+ const { stdout } = await execFileAsync("ec", args, { shell: process.platform === "win32", timeout: 60_000, env: { ...process.env, ...(home ? { EVOLCORE_HOME: home } : {}) }, windowsHide: true, maxBuffer: 1024 * 1024 });
11
15
  return JSON.parse(stdout);
12
16
  }
13
17
  catch (error) {
@@ -21,24 +25,96 @@ async function runEc(args) {
21
25
  throw error;
22
26
  }
23
27
  }
24
- async function configureAidForPlugin(pluginId, aid, port, run = runEc) {
25
- if (!aid)
26
- return;
28
+ async function ecIsRunning(home) {
29
+ const records = [];
30
+ try {
31
+ records.push(Number(await (0, promises_1.readFile)((0, node_path_1.join)(home, "daemon.pid"), "utf8")));
32
+ }
33
+ catch (error) {
34
+ if (error.code !== "ENOENT")
35
+ throw error;
36
+ }
37
+ const directory = (0, node_path_1.join)(home, "data", "instance");
38
+ let names;
39
+ try {
40
+ names = await (0, promises_1.readdir)(directory);
41
+ }
42
+ catch (error) {
43
+ if (error.code !== "ENOENT")
44
+ throw error;
45
+ names = [];
46
+ }
47
+ for (const name of names.filter(name => /^main-\d+\.json$/.test(name))) {
48
+ records.push(JSON.parse(await (0, promises_1.readFile)((0, node_path_1.join)(directory, name), "utf8")).pid);
49
+ }
50
+ return records.some(pid => {
51
+ if (!Number.isSafeInteger(pid) || pid <= 0)
52
+ return false;
53
+ try {
54
+ process.kill(pid, 0);
55
+ return true;
56
+ }
57
+ catch (error) {
58
+ if (error.code === "ESRCH")
59
+ return false;
60
+ throw error;
61
+ }
62
+ });
63
+ }
64
+ async function configureAidForPlugin(pluginId, aid, port, run = runEc, home = process.env.EVOLCORE_HOME ?? (0, node_path_1.join)((0, node_os_1.homedir)(), ".evolcore")) {
65
+ if (!/^[a-z0-9][a-z0-9_.-]+$/i.test(aid) || !aid.includes("."))
66
+ throw new Error("AID 格式无效");
27
67
  if (!/^[a-z0-9_-]+$/.test(pluginId))
28
68
  throw new Error("插件 ID 格式无效");
29
- // 通过 daemon 的统一插件代理访问所有 Agent;否则 qwenwork 的裸 /v1 路由在有插件 HTTP 注册表时不存在。
69
+ if (!Number.isInteger(port) || port < 1 || port > 65535)
70
+ throw new Error("后台端口无效");
30
71
  const path = pluginId === "dsh" ? "/dsh/v1" : `/api/agents/${pluginId}/v1`;
31
- for (const [key, value] of [
32
- ["baseagents.ecagent.baseUrl", `http://127.0.0.1:${port}${path}`],
33
- ["baseagents.ecagent.apiKey", "mab-local"],
34
- ["baseagents.ecagent.model", pluginId],
35
- ["active_baseagent", "ecagent"],
36
- ]) {
37
- const result = await run(["agent", "set", aid, key, value, "--format", "json"]);
72
+ const ecagent = { baseUrl: `http://127.0.0.1:${port}${path}`, apiKey: "mab-local", model: pluginId };
73
+ try {
74
+ // MAB 直接接收 AUN 消息,先停用该 AID 的 EvolCore Agent,避免双重回复。
75
+ const disabled = await run(["agent", "disable", aid, "--format", "json"], home);
76
+ if (!disabled?.ok)
77
+ throw new Error(`停用原 Agent 失败:${disabled?.code ?? ""} ${disabled?.error ?? "未知错误"}`);
78
+ for (const [key, value] of [
79
+ ...Object.entries(ecagent).map(([key, value]) => [`baseagents.ecagent.${key}`, value]),
80
+ ["active_baseagent", "ecagent"],
81
+ ]) {
82
+ const result = await run(["agent", "set", aid, key, value, "--format", "json"], home);
83
+ if (!result?.ok)
84
+ throw new Error(`配置 ${key} 失败:${result?.code ?? ""} ${result?.error ?? "未知错误"}`);
85
+ }
86
+ const result = await run(["agent", "reload", aid, "--format", "json"], home);
38
87
  if (!result?.ok)
39
- throw new Error(`配置 AID ${aid} 失败:${key};${result?.code ?? ""} ${result?.error ?? "未知错误"}`);
88
+ throw new Error(`重载失败:${result?.code ?? ""} ${result?.error ?? "未知错误"}`);
89
+ return "ec";
90
+ }
91
+ catch (commandError) {
92
+ const file = (0, node_path_1.join)(home, "agents", aid, "config.json");
93
+ const config = JSON.parse(await (0, promises_1.readFile)(file, "utf8"));
94
+ if (!config || typeof config !== "object" || Array.isArray(config) || config.baseagents && (typeof config.baseagents !== "object" || Array.isArray(config.baseagents)))
95
+ throw new Error(`AID ${aid} 原配置格式无效,未覆盖文件`);
96
+ config.enabled = false;
97
+ config.active_baseagent = "ecagent";
98
+ config.baseagents = { ...config.baseagents, ecagent: { ...config.baseagents?.ecagent, ...ecagent } };
99
+ const temporary = `${file}.mab-${(0, node_crypto_1.randomUUID)()}.tmp`;
100
+ try {
101
+ await (0, promises_1.writeFile)(temporary, `${JSON.stringify(config, null, 2)}\n`, { mode: 0o600 });
102
+ await (0, promises_1.rename)(temporary, file);
103
+ }
104
+ finally {
105
+ await (0, promises_1.rm)(temporary, { force: true });
106
+ }
107
+ // 写盘不能关闭运行中的旧 Agent;存在活跃实例时必须确认重载成功。
108
+ if (await ecIsRunning(home)) {
109
+ try {
110
+ const result = await run(["agent", "reload", aid, "--format", "json"], home);
111
+ if (!result?.ok)
112
+ throw new Error(result?.error ?? "重载失败");
113
+ }
114
+ catch (error) {
115
+ throw new AggregateError([commandError, error], `AID ${aid} 配置已保存,但 EvolCore 仍在运行,原 Agent 未确认停用:${error instanceof Error ? error.message : error}`);
116
+ }
117
+ }
118
+ return "json";
40
119
  }
41
- const result = await run(["agent", "reload", aid, "--format", "json"]);
42
- if (!result?.ok)
43
- throw new Error(`重载 AID ${aid} 失败:${result?.code ?? ""} ${result?.error ?? "未知错误"}`);
44
120
  }
@@ -9,10 +9,11 @@ const node_path_1 = require("node:path");
9
9
  const node_os_1 = require("node:os");
10
10
  const node_url_1 = require("node:url");
11
11
  const agent_1 = require("../../agent");
12
+ const evolcore_1 = require("./evolcore");
12
13
  const messages_1 = require("./messages");
13
14
  const attachments_1 = require("./attachments");
14
15
  const manifest = {
15
- id: "aun", version: "1.0.34", type: "channel", displayName: "AUN 通道",
16
+ id: "aun", version: "1.0.35", type: "channel", displayName: "AUN 通道",
16
17
  entry: "dist/src-ts/plugins/aun/index.js", apiVersion: "1", process: "in-process",
17
18
  capabilities: ["channel", "channel.identity.create", "channel.receive", "channel.send", "health", "cli"],
18
19
  configSchema: "config.schema.json",
@@ -124,7 +125,7 @@ class AunPlugin {
124
125
  this.creating = false;
125
126
  }
126
127
  },
127
- bind: async (pluginId, aid, _port) => {
128
+ bind: async (pluginId, aid, port) => {
128
129
  if (!aid || !aidPattern.test(aid))
129
130
  throw new Error("AID 格式无效,必须指定要关联的 AID");
130
131
  if (pluginId === "aun" || pluginId === "abweb")
@@ -140,6 +141,14 @@ class AunPlugin {
140
141
  const loaded = this.store.load(aid);
141
142
  if (!loaded.ok || !loaded.data)
142
143
  throw new Error("AID 不在本地密钥库中,无法关联");
144
+ // 仅接管用户本次选中的 EvolCore Agent;纯密钥库身份不调用 ec。
145
+ if ((0, node_fs_1.existsSync)((0, node_path_1.join)(this.path(), "agents", aid, "config.json"))) {
146
+ const method = await (0, evolcore_1.configureAidForPlugin)(pluginId, aid, port, this.options.runEc ?? evolcore_1.runEc, this.path());
147
+ if (method === "json")
148
+ this.context?.logger.warn("ec 命令不可用或失败,已通过 JSON 停用原 Agent 并配置 ecagent", { aid, pluginId });
149
+ else
150
+ this.context?.logger.info("已通过 ec 命令停用原 Agent 并切换为 ecagent", { aid, pluginId });
151
+ }
143
152
  this.identities.add(aid);
144
153
  if (!connection)
145
154
  await this.connectAid(aid);
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-bridge-aun",
3
- "version": "1.0.34",
3
+ "version": "1.0.35",
4
4
  "description": "mu-agent-bridge 的 AUN 通道插件。",
5
5
  "keywords": [
6
6
  "mu-agent-bridge-plugin",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "id": "aun",
3
- "version": "1.0.34",
3
+ "version": "1.0.35",
4
4
  "type": "channel",
5
5
  "displayName": "AUN 通道",
6
6
  "entry": "dist/src-ts/plugins/aun/index.js",
@@ -10,7 +10,7 @@ const desktop_launch_1 = require("../desktop-launch");
10
10
  const node_os_1 = require("node:os");
11
11
  exports.doubaoManifest = {
12
12
  id: "doubao",
13
- version: "1.0.21",
13
+ version: "1.0.23",
14
14
  displayName: "豆包",
15
15
  entry: "dist/index.js",
16
16
  apiVersion: "1",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-bridge-doubao",
3
- "version": "1.0.22",
3
+ "version": "1.0.23",
4
4
  "description": "agent-bridge 的豆包插件。",
5
5
  "keywords": [
6
6
  "mu-agent-bridge-plugin",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "id": "doubao",
3
- "version": "1.0.22",
3
+ "version": "1.0.23",
4
4
  "type": "agent",
5
5
  "displayName": "豆包",
6
6
  "entry": "dist/src-ts/plugins/doubao/index.js",
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "type": "object",
3
3
  "properties": {
4
+ "executablePath": { "type": "string", "description": "DeepSeek Harness 客户端路径;升级后自动重新检测" },
4
5
  "aid": { "type": "string", "description": "复用的 EvolCore 已有 AID" },
5
6
  "patchPath": { "type": "string", "description": "DeepSeek Harness patch 文件路径" },
6
7
  "sessionsRoot": { "type": "string", "description": "DSH 官方持久化会话目录;默认使用 DSH_HOME/sessions 或用户目录下的 .dsh/sessions" }
@@ -61,12 +61,14 @@ async function readPresentedFiles(directory, paths, cwd, inputs, returnPaths) {
61
61
  return result;
62
62
  }
63
63
  class DshAgent {
64
+ options;
64
65
  name = "dsh";
65
66
  paths;
66
67
  spawnProcess;
67
68
  defaultTimeoutMs;
68
69
  constructor(options) {
69
- this.paths = options.paths ?? (0, dsh_paths_1.resolveDshPaths)(options.patchPath);
70
+ this.options = options;
71
+ this.paths = options.paths;
70
72
  this.spawnProcess = options.spawn ?? node_child_process_1.spawn;
71
73
  this.defaultTimeoutMs = options.defaultTimeoutMs ?? 300_000;
72
74
  }
@@ -74,6 +76,7 @@ class DshAgent {
74
76
  if (!request.task.trim())
75
77
  throw new agent_1.AgentError("DSH task must not be empty");
76
78
  (0, attachments_1.validateAttachments)(request.attachments);
79
+ const paths = this.paths ?? await (0, dsh_paths_1.resolveDshPaths)(this.options.patchPath);
77
80
  const workDirectory = await (0, promises_1.mkdtemp)((0, node_path_1.join)((0, node_os_1.tmpdir)(), "agent-bridge-dsh-"));
78
81
  let failed = false;
79
82
  try {
@@ -85,11 +88,11 @@ class DshAgent {
85
88
  for (const [index, item] of (request.attachments ?? []).entries())
86
89
  await (0, promises_1.writeFile)(inputPaths[index].returnPath, item.data, { mode: 0o600 });
87
90
  const task = `${request.task}\n\n请使用真实文件工具读取输入附件;生成的回传文件必须写入 outputDirectory 的直接子文件,并调用 present 工具声明文件。原附件已准备 returnPath 副本,原样回传时直接 present 此路径,无需 shell 复制。图片或视频无法理解时请明确说明,仍可原样回传。\n附件传输:\n${JSON.stringify({ inputs: inputPaths, outputDirectory })}`;
88
- const args = ["--expose-internals", this.paths.cli, "--profile", "headless", "--patch", this.paths.patch, "--json"];
91
+ const args = ["--expose-internals", paths.cli, "--profile", "headless", "--patch", paths.patch, "--json"];
89
92
  if (request.sessionId && !request.newSession)
90
93
  args.push("--session-id", request.sessionId);
91
94
  args.push("-");
92
- const child = this.spawnProcess(this.paths.executable, args, {
95
+ const child = this.spawnProcess(paths.executable, args, {
93
96
  cwd: request.cwd,
94
97
  env: { ...process.env, ELECTRON_RUN_AS_NODE: "1" },
95
98
  stdio: ["pipe", "pipe", "pipe"],
@@ -1,15 +1,15 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.resolveDshPaths = resolveDshPaths;
4
- const node_fs_1 = require("node:fs");
5
4
  const node_path_1 = require("node:path");
6
- function resolveDshPaths(patch) {
7
- const override = process.env.DSH_INSTALL_DIR;
8
- const candidates = [override, "D:\\Programs\\DeepSeek Harness", "D:\\DSH"].filter((value) => Boolean(value));
9
- const root = candidates.find((candidate) => (0, node_fs_1.existsSync)((0, node_path_1.join)(candidate, "DeepSeek Harness.exe"))) ?? candidates[0];
5
+ const desktop_launch_1 = require("./plugins/desktop-launch");
6
+ async function resolveDshPaths(patch, configured) {
7
+ const executable = await (0, desktop_launch_1.findWindowsExecutable)("DeepSeek Harness.exe", configured ?? (process.env.DSH_INSTALL_DIR ? (0, node_path_1.join)(process.env.DSH_INSTALL_DIR, "DeepSeek Harness.exe") : undefined));
8
+ if (!executable)
9
+ throw new Error("未找到 DeepSeek Harness 可执行文件,请先安装 DSH 或重新检测客户端路径");
10
10
  return {
11
- executable: (0, node_path_1.join)(root, "DeepSeek Harness.exe"),
12
- cli: (0, node_path_1.join)(root, "resources", "app.asar", "dsh", "node_modules", "@deepseek-ai", "dsh-desktop-host", "lib", "cli.js"),
11
+ executable,
12
+ cli: (0, node_path_1.join)((0, node_path_1.dirname)(executable), "resources", "app.asar", "dsh", "node_modules", "@deepseek-ai", "dsh-desktop-host", "lib", "cli.js"),
13
13
  patch,
14
14
  };
15
15
  }
@@ -0,0 +1,197 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.findWindowsExecutable = findWindowsExecutable;
4
+ exports.findExecutableInTree = findExecutableInTree;
5
+ exports.launchAndWait = launchAndWait;
6
+ exports.launchManagedAndWait = launchManagedAndWait;
7
+ exports.restartOwnedAndWait = restartOwnedAndWait;
8
+ exports.terminateWindowsDesktop = terminateWindowsDesktop;
9
+ exports.terminateOwnedProcess = terminateOwnedProcess;
10
+ exports.findChatgptExecutable = findChatgptExecutable;
11
+ exports.waitFor = waitFor;
12
+ const node_child_process_1 = require("node:child_process");
13
+ const promises_1 = require("node:fs/promises");
14
+ const node_fs_1 = require("node:fs");
15
+ const node_path_1 = require("node:path");
16
+ const node_util_1 = require("node:util");
17
+ const execFileAsync = (0, node_util_1.promisify)(node_child_process_1.execFile);
18
+ const versionDirectory = /^(?:app[-_ ]?)?v?\d+(?:[.-]\d+)+$/i;
19
+ /** 在 Windows 常见安装目录中查找桌面程序,避免依赖单一盘符。 */
20
+ async function findWindowsExecutable(fileName, configured) {
21
+ if (configured?.trim()) {
22
+ const configuredPath = (0, node_path_1.resolve)(configured);
23
+ if (fileName.toLowerCase() === "doubao.exe") {
24
+ let root = (0, node_path_1.dirname)(configuredPath);
25
+ while (/^(?:app|launcher|doubao_update)$/i.test((0, node_path_1.basename)(root)) || versionDirectory.test((0, node_path_1.basename)(root)))
26
+ root = (0, node_path_1.dirname)(root);
27
+ const updated = (0, node_path_1.join)(root, "Doubao_update", fileName);
28
+ if ((0, node_fs_1.existsSync)(updated))
29
+ return updated;
30
+ const latest = await findExecutableInTree(root, fileName, /^/, 3);
31
+ if (latest)
32
+ return latest;
33
+ }
34
+ // 千问升级会保留或删除旧版本目录,从同一安装位置重新定位新版。
35
+ if (fileName.toLowerCase() === "qwenworkcn.exe" && /^v?\d+(?:[.-]\d+)+$/i.test((0, node_path_1.basename)((0, node_path_1.dirname)(configuredPath)))) {
36
+ const latest = await findExecutableInTree((0, node_path_1.dirname)((0, node_path_1.dirname)(configuredPath)), fileName, /^/, 1);
37
+ if (latest)
38
+ return latest;
39
+ }
40
+ if ((0, node_fs_1.existsSync)(configuredPath))
41
+ return configuredPath;
42
+ }
43
+ if (process.platform !== "win32")
44
+ return undefined;
45
+ const pattern = /doubao|豆包|qwen|qwwork|千问|deepseek|harness|dsh/i;
46
+ const roots = [...new Set([
47
+ process.env.DSH_INSTALL_DIR,
48
+ "D:\\Programs\\DeepSeek Harness",
49
+ "D:\\DSH",
50
+ process.env.LOCALAPPDATA && (0, node_path_1.join)(process.env.LOCALAPPDATA, "Programs"),
51
+ process.env.ProgramFiles,
52
+ process.env["ProgramFiles(x86)"],
53
+ ...Array.from({ length: 24 }, (_, index) => `${String.fromCharCode(67 + index)}:\\`),
54
+ ].filter((value) => typeof value === "string" && (0, node_fs_1.existsSync)(value)))];
55
+ for (const root of roots) {
56
+ const found = await findExecutableInTree(root, fileName, pattern, 6);
57
+ if (found)
58
+ return found;
59
+ }
60
+ return undefined;
61
+ }
62
+ async function findExecutableInTree(directory, fileName, pattern, depth) {
63
+ return search(directory, fileName, pattern, depth, pattern.test(directory));
64
+ }
65
+ async function search(directory, fileName, pattern, depth, insideProduct) {
66
+ try {
67
+ const entries = await (0, promises_1.readdir)(directory, { withFileTypes: true });
68
+ // 豆包上层文件是启动器,优先查找新版目录或 app 中的实际程序。
69
+ if (insideProduct && depth > 0 && fileName.toLowerCase() === "doubao.exe") {
70
+ const versions = entries.filter(entry => entry.isDirectory() && (versionDirectory.test(entry.name) || /^(?:app|doubao_update)$/i.test(entry.name)));
71
+ versions.sort((left, right) => {
72
+ const rank = (name) => /^doubao_update$/i.test(name) ? 2 : /^app$/i.test(name) ? 0 : 1;
73
+ return rank(right.name) - rank(left.name) || right.name.replace(/^\D+/, "").localeCompare(left.name.replace(/^\D+/, ""), "en", { numeric: true });
74
+ });
75
+ for (const entry of versions) {
76
+ const found = await search((0, node_path_1.join)(directory, entry.name), fileName, pattern, depth - 1, true);
77
+ if (found)
78
+ return found;
79
+ }
80
+ }
81
+ for (const entry of entries)
82
+ if (entry.isFile() && insideProduct && entry.name.toLowerCase() === fileName.toLowerCase() && !(fileName.toLowerCase() === "doubao.exe" && /^(?:launcher|doubao)$/i.test((0, node_path_1.basename)(directory))))
83
+ return (0, node_path_1.join)(directory, entry.name);
84
+ if (depth <= 0)
85
+ return undefined;
86
+ // 按数字比较版本目录,避免 1.2.5 排在 1.2.10 之前。
87
+ entries.sort((left, right) => right.name.localeCompare(left.name, "en", { numeric: true }));
88
+ for (const entry of entries)
89
+ if (entry.isDirectory() && !entry.name.startsWith(".") && (insideProduct || pattern.test(entry.name) || /^(?:Program Files(?: \(x86\))?|Programs)$/i.test(entry.name))) {
90
+ const found = await search((0, node_path_1.join)(directory, entry.name), fileName, pattern, depth - 1, insideProduct || pattern.test(entry.name));
91
+ if (found)
92
+ return found;
93
+ }
94
+ }
95
+ catch { /* 无权限或目录在扫描期间消失时继续查找 */ }
96
+ return undefined;
97
+ }
98
+ async function launchAndWait(executable, args, connect, timeoutMs, launch = (file, argv) => (0, node_child_process_1.spawn)(file, argv, { detached: true, stdio: "ignore", windowsHide: false })) {
99
+ const child = launch(executable, args);
100
+ child.unref();
101
+ return waitFor(connect, timeoutMs, 250);
102
+ }
103
+ async function launchManagedAndWait(executable, args, profileDirectory, connect, timeoutMs, onProcess, launch = (file, argv) => (0, node_child_process_1.spawn)(file, argv, { detached: true, stdio: "ignore", windowsHide: false })) {
104
+ if (profileDirectory)
105
+ await (0, promises_1.mkdir)(profileDirectory, { recursive: true });
106
+ const child = launch(executable, profileDirectory ? [...args, `--user-data-dir=${profileDirectory}`] : args);
107
+ child.unref();
108
+ onProcess(child);
109
+ try {
110
+ return await waitFor(connect, timeoutMs, 250);
111
+ }
112
+ catch (error) {
113
+ await terminateOwnedProcess(child).catch(() => undefined);
114
+ onProcess(undefined);
115
+ throw error;
116
+ }
117
+ }
118
+ async function restartOwnedAndWait(child, executable, args, profileDirectory, connect, timeoutMs, onProcess) {
119
+ await terminateOwnedProcess(child);
120
+ onProcess(undefined);
121
+ return launchManagedAndWait(executable, args, profileDirectory, connect, timeoutMs, onProcess);
122
+ }
123
+ /** 仅在用户确认重启后清理同一用户、同一安装路径的桌面进程。 */
124
+ async function terminateWindowsDesktop(executable) {
125
+ if (process.platform !== "win32")
126
+ return [];
127
+ const script = `
128
+ $ErrorActionPreference = 'Stop'
129
+ $target = '${(0, node_path_1.resolve)(executable).replace(/'/g, "''")}'
130
+ $sid = [Security.Principal.WindowsIdentity]::GetCurrent().User.Value
131
+ $processes = @(Get-CimInstance Win32_Process | Where-Object { $_.ExecutablePath -ieq $target } | Where-Object { (Invoke-CimMethod -InputObject $_ -MethodName GetOwnerSid).Sid -eq $sid })
132
+ $ids = @($processes.ProcessId)
133
+ $roots = @($processes | Where-Object { $ids -notcontains $_.ParentProcessId })
134
+ foreach ($item in $roots) {
135
+ $current = Get-CimInstance Win32_Process -Filter "ProcessId=$($item.ProcessId)"
136
+ if (-not $current -or $current.CreationDate -ne $item.CreationDate) { continue }
137
+ & taskkill.exe /PID $item.ProcessId /T /F | Out-Null
138
+ if ($LASTEXITCODE -ne 0 -and (Get-Process -Id $item.ProcessId -ErrorAction SilentlyContinue)) { throw "无法结束桌面进程 $($item.ProcessId)" }
139
+ $remaining = Get-Process -Id $item.ProcessId -ErrorAction SilentlyContinue
140
+ if ($remaining -and -not $remaining.WaitForExit(10000)) { throw "等待桌面进程退出超时 $($item.ProcessId)" }
141
+ }
142
+ ConvertTo-Json -Compress -InputObject @($roots | ForEach-Object { [int]$_.ProcessId })
143
+ `;
144
+ const { stdout } = await execFileAsync("powershell.exe", ["-NoProfile", "-NonInteractive", "-EncodedCommand", Buffer.from(script, "utf16le").toString("base64")], { timeout: 30_000, windowsHide: true });
145
+ return JSON.parse(stdout.trim());
146
+ }
147
+ async function terminateOwnedProcess(child, platform = process.platform, run = async (file, args) => execFileAsync(file, args, { timeout: 10_000 })) {
148
+ const pid = child?.pid;
149
+ if (!Number.isSafeInteger(pid) || !pid || child?.exitCode !== null && child?.exitCode !== undefined || child?.signalCode)
150
+ return;
151
+ if (platform === "win32") {
152
+ try {
153
+ await run("taskkill.exe", ["/PID", String(pid), "/T", "/F"]);
154
+ }
155
+ catch (error) {
156
+ if (!child.exitCode && !child.signalCode && error.code !== "ESRCH")
157
+ throw error;
158
+ }
159
+ }
160
+ else {
161
+ try {
162
+ process.kill(pid, "SIGTERM");
163
+ }
164
+ catch (error) {
165
+ if (error.code !== "ESRCH")
166
+ throw error;
167
+ }
168
+ }
169
+ }
170
+ async function findChatgptExecutable(configured) {
171
+ if (configured?.trim() && (0, node_fs_1.existsSync)((0, node_path_1.resolve)(configured)))
172
+ return (0, node_path_1.resolve)(configured);
173
+ if (process.platform !== "win32")
174
+ return undefined;
175
+ const script = "$candidates = foreach ($package in (Get-AppxPackage | Where-Object { $_.Name -like 'OpenAI.Codex*' })) { $appDir = Join-Path $package.InstallLocation 'app'; $path = Join-Path $appDir 'ChatGPT.exe'; if (-not (Test-Path -LiteralPath $path)) { $path = Get-ChildItem -LiteralPath $appDir -Filter 'ChatGPT*.exe' -File -ErrorAction SilentlyContinue | Select-Object -First 1 -ExpandProperty FullName }; if ($path) { [pscustomobject]@{ Version = [version]$package.Version; Executable = $path } } }; $candidate = $candidates | Sort-Object Version -Descending | Select-Object -First 1; if ($candidate) { $candidate.Executable }";
176
+ try {
177
+ const { stdout } = await execFileAsync("powershell.exe", ["-NoProfile", "-NonInteractive", "-Command", script], { timeout: 10_000 });
178
+ return stdout.trim().split(/\r?\n/).at(-1)?.trim() || undefined;
179
+ }
180
+ catch {
181
+ return undefined;
182
+ }
183
+ }
184
+ async function waitFor(action, timeoutMs, intervalMs = 500) {
185
+ const deadline = Date.now() + timeoutMs;
186
+ let last;
187
+ while (Date.now() < deadline) {
188
+ try {
189
+ return await action();
190
+ }
191
+ catch (error) {
192
+ last = error;
193
+ await new Promise((resolve) => setTimeout(resolve, intervalMs));
194
+ }
195
+ }
196
+ throw last instanceof Error ? last : new Error("桌面程序启动超时");
197
+ }
@@ -3,11 +3,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.DshPlugin = exports.dshManifest = void 0;
4
4
  const node_path_1 = require("node:path");
5
5
  const dsh_agent_1 = require("../../dsh-agent");
6
+ const dsh_paths_1 = require("../../dsh-paths");
6
7
  const openai_adapter_1 = require("../qwenwork/openai-adapter");
7
8
  const session_history_1 = require("./session-history");
8
9
  exports.dshManifest = {
9
10
  id: "dsh",
10
- version: "1.0.10",
11
+ version: "1.0.11",
11
12
  displayName: "DeepSeek Harness",
12
13
  entry: "dist/index.js",
13
14
  apiVersion: "1",
@@ -31,7 +32,9 @@ class DshPlugin {
31
32
  const patchPath = typeof configured === "string" && configured.trim()
32
33
  ? configured
33
34
  : (0, node_path_1.resolve)(__dirname, "..", "..", "headless-account.patch.yml");
34
- this.agent = new dsh_agent_1.DshAgent({ patchPath });
35
+ const paths = await (0, dsh_paths_1.resolveDshPaths)(patchPath, typeof context.config.executablePath === "string" ? context.config.executablePath : undefined);
36
+ this.agent = new dsh_agent_1.DshAgent({ patchPath, paths });
37
+ context.logger?.info?.("DSH 客户端路径已确认", { executablePath: paths.executable });
35
38
  this.sessionHistory = new session_history_1.DshSessionHistoryProvider({
36
39
  sessionsRoot: typeof context.config.sessionsRoot === "string" ? context.config.sessionsRoot : undefined,
37
40
  unavailableReason: () => this.status !== "ready" ? "DSH 插件尚未就绪" : this.activeRequests ? "DSH 正在处理任务,暂不读取持久化历史" : "",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-bridge-dsh",
3
- "version": "1.0.10",
3
+ "version": "1.0.11",
4
4
  "description": "agent-bridge 的 DeepSeek Harness 插件,支持后台定时自动升级验证。",
5
5
  "keywords": [
6
6
  "mu-agent-bridge-plugin",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "id": "dsh",
3
- "version": "1.0.10",
3
+ "version": "1.0.11",
4
4
  "type": "agent",
5
5
  "displayName": "DeepSeek Harness",
6
6
  "entry": "dist/src-ts/plugins/dsh/index.js",