mu-agent-bridge 1.0.44 → 1.0.45
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.
package/README.md
CHANGED
|
@@ -77,7 +77,7 @@ mab start
|
|
|
77
77
|
abweb 插件目录从 npm 搜索带 `mu-agent-bridge-plugin` 关键词的包,按包元数据中的
|
|
78
78
|
`agentBridge.type` 展示 Agent、Channel 和管理插件;安装后以 `plugin.json` 的类型和能力
|
|
79
79
|
再次校验,不根据包名推断类型。搜索结果有收录延迟,可在页面直接输入 `agent-bridge-*`
|
|
80
|
-
包名安装。已安装插件按清单能力兼容旧版分类。AUN 通道插件包名为 `agent-bridge-aun
|
|
80
|
+
包名安装。已安装插件按清单能力兼容旧版分类。AUN 通道插件包名为 `agent-bridge-aun`,安装后在 AUN 身份页手动关联每个 AID;未关联的 AID 不会自动投递,关联操作只修改指定 AID,不影响其它身份。`workbuddy` 插件包名为 `agent-bridge-workbuddy`,显式关联到 WorkBuddy 后即可按对端保持 CodeBuddy 会话。默认问答使用独立的 `@tencent-ai/codebuddy-code` CLI,须单独登录;没有 CLI 时使用 WorkBuddy 桌面端 CDP,并默认自动启动桌面应用。CLI 未登录时不会自动切换到桌面端账号;可设置 `transport` 为 `cdp` 显式使用桌面端。
|
|
81
81
|
|
|
82
82
|
插件仍在 daemon 的同一个 Node.js 进程内运行。修改插件入口依赖后应重启 daemon,避免模块缓存
|
|
83
83
|
影响新代码。
|
package/dist/src-ts/cli.js
CHANGED
|
@@ -48,6 +48,8 @@ const bridge_1 = require("./bridge");
|
|
|
48
48
|
const plugin_manager_1 = require("./plugin-manager");
|
|
49
49
|
const daemon_process_1 = require("./daemon-process");
|
|
50
50
|
const cli_commands_1 = require("./cli-commands");
|
|
51
|
+
const core_updater_1 = require("./core-updater");
|
|
52
|
+
const log_store_1 = require("./log-store");
|
|
51
53
|
const execFileAsync = (0, node_util_1.promisify)(node_child_process_1.execFile);
|
|
52
54
|
let activeManager;
|
|
53
55
|
const logger = {
|
|
@@ -170,6 +172,31 @@ async function startDaemon() {
|
|
|
170
172
|
const child = (0, node_child_process_1.spawn)(process.execPath, [(0, node_path_1.resolve)(__dirname, "cli.js"), "start"], { detached: true, stdio: "ignore", windowsHide: true });
|
|
171
173
|
child.unref();
|
|
172
174
|
}
|
|
175
|
+
async function startUpdatedDaemon() {
|
|
176
|
+
const child = (0, node_child_process_1.spawn)(process.execPath, [(0, node_path_1.resolve)(__dirname, "cli.js"), "start"], { detached: true, stdio: "ignore", windowsHide: true });
|
|
177
|
+
let launchFailed = false;
|
|
178
|
+
child.once("error", () => { launchFailed = true; });
|
|
179
|
+
child.unref();
|
|
180
|
+
const healthy = async () => {
|
|
181
|
+
for (let attempt = 0; attempt < 30; attempt++) {
|
|
182
|
+
if (launchFailed || child.exitCode !== null || child.signalCode !== null)
|
|
183
|
+
return false;
|
|
184
|
+
const address = await daemonAddress();
|
|
185
|
+
try {
|
|
186
|
+
const response = await fetch(`http://127.0.0.1:${address.port}/api/health`, {
|
|
187
|
+
headers: process.env.AGENT_BRIDGE_TOKEN ? { authorization: `Bearer ${process.env.AGENT_BRIDGE_TOKEN}` } : undefined,
|
|
188
|
+
signal: AbortSignal.timeout(1000),
|
|
189
|
+
});
|
|
190
|
+
if (response.ok && (await response.json()).status === "ok")
|
|
191
|
+
return true;
|
|
192
|
+
}
|
|
193
|
+
catch { /* daemon 尚未就绪 */ }
|
|
194
|
+
await new Promise((done) => setTimeout(done, 1000));
|
|
195
|
+
}
|
|
196
|
+
return false;
|
|
197
|
+
};
|
|
198
|
+
return { child, healthy };
|
|
199
|
+
}
|
|
173
200
|
async function stop() {
|
|
174
201
|
await stopDaemon();
|
|
175
202
|
console.log("旧 daemon 已停止");
|
|
@@ -344,6 +371,20 @@ async function main() {
|
|
|
344
371
|
if (args[0] === "--self-test")
|
|
345
372
|
return selfTest(args[1] ?? "hi");
|
|
346
373
|
if (args[0] === "--serve" || args[0] === "start") {
|
|
374
|
+
const logs = new log_store_1.LogStore((0, node_path_1.resolve)(stateDirectory(), "logs"));
|
|
375
|
+
let launched;
|
|
376
|
+
const updated = await (0, core_updater_1.updateCoreAtStartup)({
|
|
377
|
+
packageRoot: (0, node_path_1.resolve)(__dirname, "..", ".."),
|
|
378
|
+
stateDir: stateDirectory(),
|
|
379
|
+
stopDaemon: () => stopDaemon(false),
|
|
380
|
+
startDaemon: async () => { launched = await startUpdatedDaemon(); },
|
|
381
|
+
waitForHealth: async () => launched?.healthy() ?? false,
|
|
382
|
+
log: (level, message, fields) => logs.append({ time: new Date().toISOString(), level, source: "核心升级", message, fields }),
|
|
383
|
+
});
|
|
384
|
+
if (updated) {
|
|
385
|
+
await logs.flush();
|
|
386
|
+
return 0;
|
|
387
|
+
}
|
|
347
388
|
await stopDaemon(false);
|
|
348
389
|
return serve();
|
|
349
390
|
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.updateCoreAtStartup = updateCoreAtStartup;
|
|
4
|
+
const promises_1 = require("node:fs/promises");
|
|
5
|
+
const node_path_1 = require("node:path");
|
|
6
|
+
const node_child_process_1 = require("node:child_process");
|
|
7
|
+
const node_util_1 = require("node:util");
|
|
8
|
+
const version_manager_1 = require("./version-manager");
|
|
9
|
+
const execFileAsync = (0, node_util_1.promisify)(node_child_process_1.execFile);
|
|
10
|
+
function normalizePath(value) {
|
|
11
|
+
const path = (0, node_path_1.resolve)(value).replace(/[\\/]+$/, "");
|
|
12
|
+
return process.platform === "win32" ? path.toLowerCase() : path;
|
|
13
|
+
}
|
|
14
|
+
async function sameDirectory(left, right) {
|
|
15
|
+
try {
|
|
16
|
+
return normalizePath(await (0, promises_1.realpath)(left)) === normalizePath(await (0, promises_1.realpath)(right));
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return normalizePath(left) === normalizePath(right);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
async function runNpm(args) {
|
|
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 } : {}) });
|
|
25
|
+
return stdout.trim();
|
|
26
|
+
}
|
|
27
|
+
async function updateCoreAtStartup(options) {
|
|
28
|
+
const npm = options.npm ?? runNpm;
|
|
29
|
+
try {
|
|
30
|
+
const globalRoot = options.globalRoot ?? await npm(["root", "-g"]);
|
|
31
|
+
const packageRoot = (0, node_path_1.resolve)(options.packageRoot);
|
|
32
|
+
if (!await sameDirectory(packageRoot, (0, node_path_1.join)(globalRoot, "mu-agent-bridge")))
|
|
33
|
+
return false;
|
|
34
|
+
const metadata = JSON.parse(await (0, promises_1.readFile)((0, node_path_1.join)(packageRoot, "package.json"), "utf8"));
|
|
35
|
+
if (metadata.name !== "mu-agent-bridge" || !metadata.version)
|
|
36
|
+
throw new Error("当前全局核心包信息无效");
|
|
37
|
+
const rawVersion = await npm(["view", "mu-agent-bridge", "version", "--json"]);
|
|
38
|
+
const latestVersion = (() => { try {
|
|
39
|
+
return String(JSON.parse(rawVersion));
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return rawVersion.replace(/^['\"]|['\"]$/g, "");
|
|
43
|
+
} })();
|
|
44
|
+
if ((0, version_manager_1.compareVersions)(latestVersion, metadata.version) <= 0)
|
|
45
|
+
return false;
|
|
46
|
+
options.log?.("info", "发现 daemon 新版本,开始自动升级", { currentVersion: metadata.version, latestVersion });
|
|
47
|
+
await (0, promises_1.mkdir)(options.stateDir, { recursive: true });
|
|
48
|
+
const backupRoot = await (0, promises_1.mkdtemp)((0, node_path_1.join)(options.stateDir, "mab-core-update-"));
|
|
49
|
+
const backup = (0, node_path_1.join)(backupRoot, "package");
|
|
50
|
+
await (0, promises_1.cp)(packageRoot, backup, { recursive: true });
|
|
51
|
+
try {
|
|
52
|
+
await options.stopDaemon();
|
|
53
|
+
await npm(["install", "-g", `mu-agent-bridge@${latestVersion}`, "--no-fund", "--no-audit"]);
|
|
54
|
+
const installed = JSON.parse(await (0, promises_1.readFile)((0, node_path_1.join)(packageRoot, "package.json"), "utf8"));
|
|
55
|
+
if (installed.name !== "mu-agent-bridge" || installed.version !== latestVersion)
|
|
56
|
+
throw new Error(`核心安装结果不匹配:${installed.version ?? "未知版本"}`);
|
|
57
|
+
await options.startDaemon();
|
|
58
|
+
if (!await options.waitForHealth())
|
|
59
|
+
throw new Error("新 daemon 健康检查失败");
|
|
60
|
+
options.log?.("info", "daemon 自动升级成功", { version: latestVersion });
|
|
61
|
+
await (0, promises_1.rm)(backupRoot, { recursive: true, force: true });
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
await options.stopDaemon().catch(() => undefined);
|
|
66
|
+
await (0, promises_1.rm)(packageRoot, { recursive: true, force: true });
|
|
67
|
+
await (0, promises_1.cp)(backup, packageRoot, { recursive: true });
|
|
68
|
+
options.log?.("error", "daemon 自动升级失败,已恢复旧版本", { error: error instanceof Error ? error.message : String(error), version: metadata.version });
|
|
69
|
+
await (0, promises_1.rm)(backupRoot, { recursive: true, force: true });
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
options.log?.("warn", "daemon 自动升级检查失败,继续使用当前版本", { error: error instanceof Error ? error.message : String(error) });
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -462,13 +462,13 @@ async function withAunAttachmentPlugin(run, publish, config = { allow_from: "pee
|
|
|
462
462
|
strict_1.default.match(bound.body.message, /AID 不在本地密钥库中/);
|
|
463
463
|
strict_1.default.deepEqual(bound.body.data, { aid: "missing.agentid.cn", agent: "qwenwork", bindingState: { aunAids: [], agentAid: "old.agentid.cn" } });
|
|
464
464
|
const created = await runAunCommand(routes, commandResponse(), ["create", "new.agentid.cn", "--owner", "owner.agentid.cn", "--agent", "qwenwork"]);
|
|
465
|
-
strict_1.default.equal(created.statusCode,
|
|
465
|
+
strict_1.default.equal(created.statusCode, 200);
|
|
466
466
|
strict_1.default.equal(created.body.data.created, true);
|
|
467
|
-
strict_1.default.
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
strict_1.default.equal(unbound.
|
|
471
|
-
strict_1.default.deepEqual(
|
|
467
|
+
strict_1.default.equal(created.body.data.agent, "qwenwork");
|
|
468
|
+
strict_1.default.equal((await plugin.health()).details?.bindings?.["new.agentid.cn"], "qwenwork");
|
|
469
|
+
const unbound = await runAunCommand(routes, commandResponse(), ["unbind", "new.agentid.cn"]);
|
|
470
|
+
strict_1.default.equal(unbound.statusCode, 200);
|
|
471
|
+
strict_1.default.deepEqual((await plugin.health()).details?.bindings, {});
|
|
472
472
|
}
|
|
473
473
|
finally {
|
|
474
474
|
await server.stop();
|
|
@@ -636,8 +636,13 @@ async function withAunAttachmentPlugin(run, publish, config = { allow_from: "pee
|
|
|
636
636
|
return new Response(JSON.stringify({ ok: true, bindings: currentAid ? { [currentAid]: "qwenwork" } : {} }), { status: 200 });
|
|
637
637
|
if (url.endsWith("/config"))
|
|
638
638
|
return new Response(JSON.stringify({ aid: currentAid }), { status: 200 });
|
|
639
|
-
if (init?.method === "PATCH")
|
|
639
|
+
if (init?.method === "PATCH") {
|
|
640
640
|
currentAid = JSON.parse(String(init.body)).aid;
|
|
641
|
+
if (currentAid)
|
|
642
|
+
plugin.bindings[currentAid] = "qwenwork";
|
|
643
|
+
}
|
|
644
|
+
if (init?.method === "POST" && url.endsWith("/api/plugins/aun/unbind"))
|
|
645
|
+
delete plugin.bindings[JSON.parse(String(init.body)).aid];
|
|
641
646
|
return new Response(JSON.stringify({ ok: true }), { status: 200 });
|
|
642
647
|
});
|
|
643
648
|
await plugin.activate({ dataDir: root, config: {}, logger: { info(message, fields) { logs.push(JSON.stringify([message, fields])); }, warn(message, fields) { logs.push(JSON.stringify([message, fields])); }, error(message, fields) { logs.push(JSON.stringify([message, fields])); } }, callPlugin: async () => ({ text: "" }), pluginStatus: () => "ready", http: {
|
|
@@ -650,19 +655,18 @@ async function withAunAttachmentPlugin(run, publish, config = { allow_from: "pee
|
|
|
650
655
|
const created = await runAunCommand(routes, commandResponse(), ["create", "new.agentid.cn", "--owner", "owner.agentid.cn", "--agent", "qwenwork"]);
|
|
651
656
|
strict_1.default.equal(created.body.ok, true);
|
|
652
657
|
strict_1.default.equal(created.body.data.created, true);
|
|
653
|
-
strict_1.default.
|
|
654
|
-
strict_1.default.
|
|
658
|
+
strict_1.default.equal(created.body.data.agent, "qwenwork");
|
|
659
|
+
strict_1.default.doesNotMatch(created.body.message, /old\.agentid\.cn/);
|
|
655
660
|
const bound = await runAunCommand(routes, commandResponse(), ["bind", "new.agentid.cn", "qwenwork"]);
|
|
656
661
|
strict_1.default.equal(bound.body.ok, true);
|
|
657
|
-
strict_1.default.deepEqual(bound.body.data.
|
|
658
|
-
const unbound = await runAunCommand(routes, commandResponse(), ["unbind", "
|
|
662
|
+
strict_1.default.deepEqual(bound.body.data, { aid: "new.agentid.cn", agent: "qwenwork" });
|
|
663
|
+
const unbound = await runAunCommand(routes, commandResponse(), ["unbind", "new.agentid.cn"]);
|
|
659
664
|
strict_1.default.equal(unbound.body.ok, true);
|
|
660
|
-
strict_1.default.deepEqual(unbound.body.data
|
|
665
|
+
strict_1.default.deepEqual(unbound.body.data, { aid: "new.agentid.cn", agent: "qwenwork" });
|
|
661
666
|
const patches = requests.filter((request) => request.method === "PATCH");
|
|
662
667
|
strict_1.default.deepEqual(patches.map(({ url, body }) => [url, JSON.parse(body)]), [
|
|
663
668
|
["http://127.0.0.1:3021/api/plugins/qwenwork/aid", { aid: "new.agentid.cn" }],
|
|
664
669
|
["http://127.0.0.1:3021/api/plugins/qwenwork/aid", { aid: "new.agentid.cn" }],
|
|
665
|
-
["http://127.0.0.1:3021/api/plugins/qwenwork/aid", { aid: "" }],
|
|
666
670
|
]);
|
|
667
671
|
strict_1.default.ok(requests.every(({ url }) => new URL(url).hostname === "127.0.0.1"));
|
|
668
672
|
strict_1.default.ok(requests.every(({ signal }) => signal instanceof AbortSignal));
|
|
@@ -730,10 +734,11 @@ async function withAunAttachmentPlugin(run, publish, config = { allow_from: "pee
|
|
|
730
734
|
const failedBind = await runAunCommand(routes, commandResponse(), ["bind", "existing.agentid.cn", "qwenwork"]);
|
|
731
735
|
strict_1.default.equal(failedBind.statusCode, 502);
|
|
732
736
|
strict_1.default.deepEqual(failedBind.body.data.bindingState, { aunAids: ["new.agentid.cn"], agentAid: "old.agentid.cn" });
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
strict_1.default.
|
|
736
|
-
strict_1.default.
|
|
737
|
+
plugin.bindings["new.agentid.cn"] = "qwenwork";
|
|
738
|
+
const unbound = await runAunCommand(routes, commandResponse(), ["unbind", "new.agentid.cn"]);
|
|
739
|
+
strict_1.default.equal(unbound.statusCode, 200);
|
|
740
|
+
strict_1.default.deepEqual((await plugin.health()).details?.bindings, {});
|
|
741
|
+
strict_1.default.equal(patchCalls, 2, "解绑 AUN 映射不得改写 Agent 全局配置");
|
|
737
742
|
const malformed = await runAunCommand(routes, commandResponse(), ["unknown"]);
|
|
738
743
|
strict_1.default.equal(malformed.statusCode, 400);
|
|
739
744
|
strict_1.default.equal(malformed.body.ok, false);
|
|
@@ -768,7 +773,7 @@ async function withAunAttachmentPlugin(run, publish, config = { allow_from: "pee
|
|
|
768
773
|
const plugin = new aun_1.AunPlugin({ sdk, ingress: { publish: async () => undefined } });
|
|
769
774
|
try {
|
|
770
775
|
await plugin.activate({ dataDir: root, config: {}, logger: { info() { }, warn() { }, error() { } }, callPlugin: async () => ({ text: "" }), pluginStatus: () => "ready", http: { register(route) { routes.set(`${route.method} ${route.path}`, route.handler); return () => undefined; }, unregister() { } } });
|
|
771
|
-
for (const argv of [["agents"], ["bind", "valid.agentid.cn", "qwenwork"]
|
|
776
|
+
for (const argv of [["agents"], ["bind", "valid.agentid.cn", "qwenwork"]]) {
|
|
772
777
|
const result = await runAunCommand(routes, commandResponse(), argv);
|
|
773
778
|
strict_1.default.equal(result.statusCode, 502);
|
|
774
779
|
strict_1.default.equal(result.body.ok, false);
|
|
@@ -852,7 +857,7 @@ async function withAunAttachmentPlugin(run, publish, config = { allow_from: "pee
|
|
|
852
857
|
fetch: async (path) => ({ ok: true, json: async () => path.endsWith("/identities") ? identities : path === "/api/plugins" ? { plugins: agents } : path.includes("qwenwork") ? { aid: "external.agentid.cn" } : {} }),
|
|
853
858
|
});
|
|
854
859
|
await new Promise((resolve) => setImmediate(resolve));
|
|
855
|
-
strict_1.default.deepEqual(elements.get("#identities").children.map((row) => row.children[0].textContent), ["new.agentid.cn", "old.agentid.cn", "unknown.agentid.cn", "
|
|
860
|
+
strict_1.default.deepEqual(elements.get("#identities").children.map((row) => row.children[0].textContent), ["new.agentid.cn", "old.agentid.cn", "unknown.agentid.cn", "offline-order.agentid.cn", "external.agentid.cn"]);
|
|
856
861
|
});
|
|
857
862
|
(0, node_test_1.default)("AUN 页面跨来源重复 AID 只显示本工程归属", async () => {
|
|
858
863
|
const node = () => ({ children: [], elements: new Proxy({}, { get: () => ({ value: "", replaceChildren() { } }) }), append(...items) { this.children.push(...items); }, replaceChildren(...items) { this.children = items; }, setAttribute() { } });
|
|
@@ -281,7 +281,9 @@ const plugin_manager_1 = require("../src-ts/plugin-manager");
|
|
|
281
281
|
const bound = await fetch(`${base}/api/plugins/dsh/aid`, { method: "PATCH", headers: { "content-type": "application/json" }, body: JSON.stringify({ aid: "new.agentid.cn" }) });
|
|
282
282
|
strict_1.default.equal(bound.status, 200);
|
|
283
283
|
strict_1.default.deepEqual(calls[1], ["dsh", "new.agentid.cn", address.port]);
|
|
284
|
-
strict_1.default.
|
|
284
|
+
strict_1.default.equal(calls.length, 2, "AUN 关联不得改写 Agent 插件的全局 AID 配置");
|
|
285
|
+
const broadUnbind = await fetch(`${base}/api/plugins/dsh/aid`, { method: "PATCH", headers: { "content-type": "application/json" }, body: JSON.stringify({ aid: "" }) });
|
|
286
|
+
strict_1.default.equal(broadUnbind.status, 400, "解绑必须指定 AID");
|
|
285
287
|
}
|
|
286
288
|
finally {
|
|
287
289
|
await server.stop();
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const strict_1 = __importDefault(require("node:assert/strict"));
|
|
7
|
+
const promises_1 = require("node:fs/promises");
|
|
8
|
+
const node_os_1 = require("node:os");
|
|
9
|
+
const node_path_1 = require("node:path");
|
|
10
|
+
const node_test_1 = __importDefault(require("node:test"));
|
|
11
|
+
const core_updater_1 = require("../src-ts/core-updater");
|
|
12
|
+
async function fixture() {
|
|
13
|
+
const root = await (0, promises_1.mkdtemp)((0, node_path_1.join)((0, node_os_1.tmpdir)(), "mab-core-update-"));
|
|
14
|
+
const packageRoot = (0, node_path_1.join)(root, "node_modules", "mu-agent-bridge");
|
|
15
|
+
const globalRoot = (0, node_path_1.join)(root, "node_modules");
|
|
16
|
+
await (0, promises_1.mkdir)((0, node_path_1.join)(packageRoot, "dist", "src-ts"), { recursive: true });
|
|
17
|
+
await (0, promises_1.writeFile)((0, node_path_1.join)(packageRoot, "package.json"), JSON.stringify({ name: "mu-agent-bridge", version: "1.0.0" }));
|
|
18
|
+
await (0, promises_1.writeFile)((0, node_path_1.join)(packageRoot, "dist", "src-ts", "cli.js"), "old");
|
|
19
|
+
return { root, packageRoot, globalRoot };
|
|
20
|
+
}
|
|
21
|
+
(0, node_test_1.default)("mab start 在核心有新版本时安装、启动并等待健康检查", async () => {
|
|
22
|
+
const value = await fixture();
|
|
23
|
+
const events = [];
|
|
24
|
+
try {
|
|
25
|
+
const updated = await (0, core_updater_1.updateCoreAtStartup)({
|
|
26
|
+
packageRoot: value.packageRoot,
|
|
27
|
+
globalRoot: value.globalRoot,
|
|
28
|
+
stateDir: (0, node_path_1.join)(value.root, "state"),
|
|
29
|
+
npm: async (args) => {
|
|
30
|
+
events.push(args.join(" "));
|
|
31
|
+
if (args[0] === "view")
|
|
32
|
+
return '"1.1.0"';
|
|
33
|
+
await (0, promises_1.writeFile)((0, node_path_1.join)(value.packageRoot, "package.json"), JSON.stringify({ name: "mu-agent-bridge", version: "1.1.0" }));
|
|
34
|
+
await (0, promises_1.writeFile)((0, node_path_1.join)(value.packageRoot, "dist", "src-ts", "cli.js"), "new");
|
|
35
|
+
return "";
|
|
36
|
+
},
|
|
37
|
+
stopDaemon: async () => { events.push("stop"); },
|
|
38
|
+
startDaemon: async () => { events.push("start"); },
|
|
39
|
+
waitForHealth: async () => true,
|
|
40
|
+
});
|
|
41
|
+
strict_1.default.equal(updated, true);
|
|
42
|
+
strict_1.default.equal(JSON.parse(await (0, promises_1.readFile)((0, node_path_1.join)(value.packageRoot, "package.json"), "utf8")).version, "1.1.0");
|
|
43
|
+
strict_1.default.deepEqual(events, ["view mu-agent-bridge version --json", "stop", "install -g mu-agent-bridge@1.1.0 --no-fund --no-audit", "start"]);
|
|
44
|
+
}
|
|
45
|
+
finally {
|
|
46
|
+
await (0, promises_1.rm)(value.root, { recursive: true, force: true });
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
(0, node_test_1.default)("新 daemon 健康检查失败时恢复旧核心包并继续旧进程", async () => {
|
|
50
|
+
const value = await fixture();
|
|
51
|
+
try {
|
|
52
|
+
const updated = await (0, core_updater_1.updateCoreAtStartup)({
|
|
53
|
+
packageRoot: value.packageRoot,
|
|
54
|
+
globalRoot: value.globalRoot,
|
|
55
|
+
stateDir: (0, node_path_1.join)(value.root, "state"),
|
|
56
|
+
npm: async (args) => {
|
|
57
|
+
if (args[0] === "view")
|
|
58
|
+
return '"1.1.0"';
|
|
59
|
+
await (0, promises_1.writeFile)((0, node_path_1.join)(value.packageRoot, "package.json"), JSON.stringify({ name: "mu-agent-bridge", version: "1.1.0" }));
|
|
60
|
+
return "";
|
|
61
|
+
},
|
|
62
|
+
stopDaemon: async () => undefined,
|
|
63
|
+
startDaemon: async () => undefined,
|
|
64
|
+
waitForHealth: async () => false,
|
|
65
|
+
});
|
|
66
|
+
strict_1.default.equal(updated, false);
|
|
67
|
+
strict_1.default.equal(JSON.parse(await (0, promises_1.readFile)((0, node_path_1.join)(value.packageRoot, "package.json"), "utf8")).version, "1.0.0");
|
|
68
|
+
}
|
|
69
|
+
finally {
|
|
70
|
+
await (0, promises_1.rm)(value.root, { recursive: true, force: true });
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
(0, node_test_1.default)("核心已是最新或不是 npm 全局安装时不停止 daemon", async () => {
|
|
74
|
+
const value = await fixture();
|
|
75
|
+
let stops = 0;
|
|
76
|
+
try {
|
|
77
|
+
const current = await (0, core_updater_1.updateCoreAtStartup)({ packageRoot: value.packageRoot, globalRoot: value.globalRoot, stateDir: (0, node_path_1.join)(value.root, "state"), npm: async () => '"1.0.0"', stopDaemon: async () => { stops++; }, startDaemon: async () => undefined, waitForHealth: async () => true });
|
|
78
|
+
const local = await (0, core_updater_1.updateCoreAtStartup)({ packageRoot: value.packageRoot, globalRoot: value.root, stateDir: (0, node_path_1.join)(value.root, "state"), npm: async () => '"2.0.0"', stopDaemon: async () => { stops++; }, startDaemon: async () => undefined, waitForHealth: async () => true });
|
|
79
|
+
strict_1.default.equal(current, false);
|
|
80
|
+
strict_1.default.equal(local, false);
|
|
81
|
+
strict_1.default.equal(stops, 0);
|
|
82
|
+
}
|
|
83
|
+
finally {
|
|
84
|
+
await (0, promises_1.rm)(value.root, { recursive: true, force: true });
|
|
85
|
+
}
|
|
86
|
+
});
|