mu-agent-bridge 1.0.24 → 1.0.26
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 +2 -2
- package/dist/src-ts/cli.js +12 -7
- package/dist/src-ts/daemon-process.js +8 -0
- package/dist/src-ts/plugins/aun/index.js +3 -4
- package/dist/src-ts/plugins/aun/public/index.html +1 -1
- package/dist/tests-ts/aun-plugin.test.js +4 -3
- package/dist/tests-ts/daemon-process.test.js +20 -0
- package/package.json +1 -1
- package/plugins/aun/dist/src-ts/plugins/aun/index.js +3 -4
- package/plugins/aun/dist/src-ts/plugins/aun/public/index.html +1 -1
package/README.md
CHANGED
|
@@ -20,7 +20,7 @@ OpenAI 兼容 API 为 `/v1/models` 和 `/v1/chat/completions`。管理页面位
|
|
|
20
20
|
安装后使用 `mab start` 启动。旧桥仍可使用 `17893`,不会与管理页面冲突。局域网访问时设置
|
|
21
21
|
`AGENT_BRIDGE_HOST=0.0.0.0` 和 `AGENT_BRIDGE_TOKEN`,然后访问
|
|
22
22
|
`http://<Windows 主机 IP>:26107/abweb/`。页面使用一次性配置码配对后管理插件;请只在可信网络使用。
|
|
23
|
-
如果 26107 被旧的 mab 进程占用,可执行 `mab restart
|
|
23
|
+
如果 26107 被旧的 mab 进程占用,可执行 `mab restart`;Windows 会扫描并结束同一 CLI 安装路径的全部旧 daemon,并重新启动。`mab stop` 只停止 daemon,
|
|
24
24
|
`mab update` 会结束旧 daemon、安装最新核心包并启动新版本。不会把自己的旧进程误当成新的 daemon,也不会因此默认改用临时端口。
|
|
25
25
|
已有 AID 的连接状态由 AUN 插件自己建立和查询,只有当前已连接的身份才能关联 Agent。
|
|
26
26
|
AUN 插件通过 SDK 枚举 EvolCore 的本地 AID,并逐个建立连接;ABWeb 只展示已连接 AID,并按 EvolCore 与 mu-agent-bridge 分组,未连接身份不显示。状态检测不调用 `ec agent list/show`。
|
|
@@ -112,7 +112,7 @@ $env:CODEBUDDY_NPM_DIR = 'C:\path\to\node_modules\@tencent-ai\codebuddy-code'
|
|
|
112
112
|
|
|
113
113
|
千问工作区默认使用 `%APPDATA%\QwenWorkCN\workspace`,可用 `$env:QWEN_WORKSPACE`
|
|
114
114
|
覆盖;AUN 数据默认使用 `$env:EVOLCORE_HOME` 或用户目录下的 `.evolcore`。
|
|
115
|
-
千问 CDP 端口默认为 `9228`,也可用 `$env:QWEN_CDP_PORT` 或 `$env:AGENT_BRIDGE_QWEN_CDP_PORT`
|
|
115
|
+
千问 CDP 端口默认为 `9228`,也可用 `$env:QWEN_CDP_PORT` 或 `$env:AGENT_BRIDGE_QWEN_CDP_PORT` 覆盖。守护进程注入端口优先级最高,用来覆盖插件配置中的冲突端口;之后依次读取插件配置、环境变量和默认值。守护进程默认保持监听端口 26107;被其他程序占用时报告错误,仅显式配置 `AGENT_BRIDGE_FALLBACK_PORTS` 才允许回退。豆包和 WorkBuddy 分别使用 `DOUBAO_CDP_PORT`、`WORKBUDDY_CDP_PORT`,也支持对应的 `AGENT_BRIDGE_*_CDP_PORT`。
|
|
116
116
|
|
|
117
117
|
## 自检与运行
|
|
118
118
|
|
package/dist/src-ts/cli.js
CHANGED
|
@@ -46,6 +46,7 @@ const dsh_agent_1 = require("./dsh-agent");
|
|
|
46
46
|
const registry_1 = require("./registry");
|
|
47
47
|
const bridge_1 = require("./bridge");
|
|
48
48
|
const plugin_manager_1 = require("./plugin-manager");
|
|
49
|
+
const daemon_process_1 = require("./daemon-process");
|
|
49
50
|
const execFileAsync = (0, node_util_1.promisify)(node_child_process_1.execFile);
|
|
50
51
|
let activeManager;
|
|
51
52
|
const logger = {
|
|
@@ -115,7 +116,7 @@ async function update() {
|
|
|
115
116
|
return 2;
|
|
116
117
|
}
|
|
117
118
|
await stopDaemon();
|
|
118
|
-
await execFileAsync("npm.cmd", ["install", "-g", "mu-agent-bridge@latest", "--no-fund", "--no-audit"], { timeout: 120_000 });
|
|
119
|
+
await execFileAsync("npm.cmd", ["install", "-g", "mu-agent-bridge@latest", "--no-fund", "--no-audit"], { timeout: 120_000, shell: true });
|
|
119
120
|
await startDaemon();
|
|
120
121
|
console.log("核心已更新,旧 daemon 已结束并启动新版本");
|
|
121
122
|
return 0;
|
|
@@ -128,11 +129,17 @@ async function stopDaemon(stopTask = true) {
|
|
|
128
129
|
pid = address.pid;
|
|
129
130
|
}
|
|
130
131
|
catch { /* 地址文件不存在时继续尝试计划任务 */ }
|
|
131
|
-
if (
|
|
132
|
-
|
|
133
|
-
|
|
132
|
+
if (process.platform === "win32") {
|
|
133
|
+
const script = "Get-CimInstance Win32_Process -Filter \"name = 'node.exe'\" | Select-Object ProcessId,CommandLine | ConvertTo-Json -Compress";
|
|
134
|
+
const { stdout } = await execFileAsync("powershell.exe", ["-NoProfile", "-EncodedCommand", Buffer.from(script, "utf16le").toString("base64")], { timeout: 15_000 });
|
|
135
|
+
const value = stdout.trim() ? JSON.parse(stdout) : [];
|
|
136
|
+
const processes = Array.isArray(value) ? value : [value];
|
|
137
|
+
for (const old of processes) {
|
|
138
|
+
if (old.ProcessId === process.pid || !(0, daemon_process_1.isDaemonCommand)(old.CommandLine ?? "", (0, node_path_1.resolve)(__dirname, "cli.js")))
|
|
139
|
+
continue;
|
|
140
|
+
await execFileAsync("taskkill.exe", ["/PID", String(old.ProcessId), "/F"], { timeout: 15_000 });
|
|
141
|
+
console.log(`已结束旧 daemon:${old.ProcessId}`);
|
|
134
142
|
}
|
|
135
|
-
catch { /* 进程已退出 */ }
|
|
136
143
|
}
|
|
137
144
|
else if (pid) {
|
|
138
145
|
try {
|
|
@@ -234,8 +241,6 @@ async function serve() {
|
|
|
234
241
|
throw new Error("局域网监听必须设置 AGENT_BRIDGE_TOKEN");
|
|
235
242
|
}
|
|
236
243
|
const fallbackPorts = (process.env.AGENT_BRIDGE_FALLBACK_PORTS ?? "").split(",").map((value) => Number(value.trim())).filter((value) => Number.isInteger(value) && value >= 0);
|
|
237
|
-
if (port > 0 && !fallbackPorts.length)
|
|
238
|
-
fallbackPorts.push(0);
|
|
239
244
|
const server = new control_server_1.ControlServer({ manager, host, port, fallbackPorts, token: process.env.AGENT_BRIDGE_TOKEN, sessionFile: (0, node_path_1.resolve)(stateDirectory(), "paired-sessions.json") });
|
|
240
245
|
const address = await server.start();
|
|
241
246
|
const addressFile = (0, node_path_1.resolve)(stateDirectory(), "daemon-address.json");
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isDaemonCommand = isDaemonCommand;
|
|
4
|
+
/** 只识别直接运行本安装 CLI 的后台进程,避免旧地址文件中的 PID 被其他程序复用。 */
|
|
5
|
+
function isDaemonCommand(commandLine, cli) {
|
|
6
|
+
const args = [...commandLine.matchAll(/"([^"]*)"|(\S+)/g)].map((match) => (match[1] ?? match[2]).replaceAll("\\", "/").toLowerCase());
|
|
7
|
+
return args[1] === cli.replaceAll("\\", "/").toLowerCase() && ["start", "--serve"].includes(args[2]);
|
|
8
|
+
}
|
|
@@ -72,12 +72,11 @@ class AunPlugin {
|
|
|
72
72
|
this.creating = true;
|
|
73
73
|
try {
|
|
74
74
|
if (!this.identities.has(aid)) {
|
|
75
|
-
|
|
75
|
+
// 创建身份沿用 EvolCore 的 legacy 注册流程。Owner AID 是本地归属配置,
|
|
76
|
+
// 只有明确执行 Owner 关系绑定时才进入 aid_register_* 双签流程。
|
|
77
|
+
const result = await this.store.register(aid);
|
|
76
78
|
if (!result.ok)
|
|
77
79
|
throw new Error(redact(result.error?.message ?? result.error?.code ?? "AID 注册失败"));
|
|
78
|
-
const ownership = result.data?.ownership_status ?? result.data?.state ?? result.data?.operation_state;
|
|
79
|
-
if (ownership && ownership !== "active")
|
|
80
|
-
return { aid, status: "等待 Owner 确认", operation_id: result.data?.operation_id };
|
|
81
80
|
const loaded = this.store.load(aid);
|
|
82
81
|
if (!loaded.ok || !loaded.data)
|
|
83
82
|
throw new Error(loaded.error?.message ?? "注册后未能加载本地身份");
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
<header><h1>AUN 身份</h1><button id="refresh" type="button">刷新</button></header>
|
|
7
7
|
<p id="status" role="status"></p>
|
|
8
8
|
<div class="table-scroll"><table><thead><tr><th>AID</th><th>来源</th><th>已关联 Agent</th><th>操作</th></tr></thead><tbody id="identities"></tbody></table></div>
|
|
9
|
-
<form id="create-form"><h2>创建 AID</h2><div class="fields"><label>新 AID<input name="aid" required placeholder="
|
|
9
|
+
<form id="create-form"><h2>创建 AID</h2><div class="fields"><label>新 AID<input name="aid" required placeholder="例如:example.agentid.cn"></label><label>Owner AID<input name="ownerAid" required placeholder="例如:owner.agentid.cn(本地归属配置)"><small class="hint">Owner AID 用于本地归属和 Agent 关联;创建身份本身沿用 AUN 的普通注册流程</small></label></div><button type="submit">创建</button></form>
|
|
10
10
|
<form id="config-form"><h2>通道配置</h2><div class="fields"><label>默认 Agent<select name="agent_plugin"></select></label><label>允许的来源<input name="allow_from"></label><label>AUN 存储路径<input name="aun_path"></label><label>加密种子<input name="encryption_seed" type="password" autocomplete="new-password" placeholder="留空则不修改"></label></div><button type="submit">保存配置</button></form>
|
|
11
11
|
</main>
|
|
12
12
|
<script src="/abweb/channels/aun/app.js"></script>
|
|
@@ -142,7 +142,7 @@ const aun_1 = require("../src-ts/plugins/aun");
|
|
|
142
142
|
const sdk = {
|
|
143
143
|
AIDStore: class {
|
|
144
144
|
registered = [];
|
|
145
|
-
async register(input) { calls.push(`register:${input
|
|
145
|
+
async register(input) { calls.push(`register:${input}`); this.registered.push(input); return registration; }
|
|
146
146
|
load(aid) { return !loadOk || aid === loadFailAid ? { ok: false, error: { message: "本地身份不存在" } } : { ok: true, data: { aid } }; }
|
|
147
147
|
list() { return { ok: true, data: { identities: this.registered.map((aid) => ({ aid })) } }; }
|
|
148
148
|
close() { calls.push("store.close"); }
|
|
@@ -199,8 +199,9 @@ const aun_1 = require("../src-ts/plugins/aun");
|
|
|
199
199
|
await strict_1.default.rejects(plugin.identity.bind("dsh", "bad", 26107), /AID 格式无效/);
|
|
200
200
|
await routes.find((entry) => entry.path === "/api/plugins/aun/identities").handler({}, response);
|
|
201
201
|
strict_1.default.equal(response.body.bindingTimes["bridge.agentid.pub"], boundAt);
|
|
202
|
-
registration = { ok: true, data: {
|
|
203
|
-
strict_1.default.deepEqual(await plugin.identity.create("pending.agentid.pub", "owner.agentid.pub"), { aid: "pending.agentid.pub", status: "
|
|
202
|
+
registration = { ok: true, data: {} };
|
|
203
|
+
strict_1.default.deepEqual(await plugin.identity.create("pending.agentid.pub", "owner.agentid.pub"), { aid: "pending.agentid.pub", status: "已创建", connection: "ready" });
|
|
204
|
+
strict_1.default.ok(calls.includes("register:pending.agentid.pub"));
|
|
204
205
|
strict_1.default.equal((await plugin.health()).details?.aids instanceof Array, true);
|
|
205
206
|
registration = { ok: false, error: { message: "注册失败" } };
|
|
206
207
|
await strict_1.default.rejects(plugin.identity.create("failed.agentid.pub", "owner.agentid.pub"), /注册失败/);
|
|
@@ -0,0 +1,20 @@
|
|
|
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 node_test_1 = __importDefault(require("node:test"));
|
|
8
|
+
const daemon_process_1 = require("../src-ts/daemon-process");
|
|
9
|
+
const cli = "C:\\Users\\molian\\AppData\\Roaming\\npm\\node_modules\\mu-agent-bridge\\dist\\src-ts\\cli.js";
|
|
10
|
+
(0, node_test_1.default)("识别同一安装的全部旧 daemon,兼容 Windows 路径与引号", () => {
|
|
11
|
+
strict_1.default.equal((0, daemon_process_1.isDaemonCommand)(`"C:\\Program Files\\nodejs\\node.exe" ${cli.replaceAll("\\", "/")} start`, cli), true);
|
|
12
|
+
strict_1.default.equal((0, daemon_process_1.isDaemonCommand)(`node.exe "${cli}" --serve`, cli), true);
|
|
13
|
+
});
|
|
14
|
+
(0, node_test_1.default)("清理进程时保留 CLI 操作、其他安装及普通 Node 程序", () => {
|
|
15
|
+
for (const args of ["restart", "stop", "update", "status"])
|
|
16
|
+
strict_1.default.equal((0, daemon_process_1.isDaemonCommand)(`node.exe "${cli}" ${args}`, cli), false);
|
|
17
|
+
strict_1.default.equal((0, daemon_process_1.isDaemonCommand)('node.exe C:\\other\\cli.js start', cli), false);
|
|
18
|
+
strict_1.default.equal((0, daemon_process_1.isDaemonCommand)(`node.exe -e "${cli} start"`, cli), false);
|
|
19
|
+
strict_1.default.equal((0, daemon_process_1.isDaemonCommand)(`node.exe "${cli}.backup" start`, cli), false);
|
|
20
|
+
});
|
package/package.json
CHANGED
|
@@ -72,12 +72,11 @@ class AunPlugin {
|
|
|
72
72
|
this.creating = true;
|
|
73
73
|
try {
|
|
74
74
|
if (!this.identities.has(aid)) {
|
|
75
|
-
|
|
75
|
+
// 创建身份沿用 EvolCore 的 legacy 注册流程。Owner AID 是本地归属配置,
|
|
76
|
+
// 只有明确执行 Owner 关系绑定时才进入 aid_register_* 双签流程。
|
|
77
|
+
const result = await this.store.register(aid);
|
|
76
78
|
if (!result.ok)
|
|
77
79
|
throw new Error(redact(result.error?.message ?? result.error?.code ?? "AID 注册失败"));
|
|
78
|
-
const ownership = result.data?.ownership_status ?? result.data?.state ?? result.data?.operation_state;
|
|
79
|
-
if (ownership && ownership !== "active")
|
|
80
|
-
return { aid, status: "等待 Owner 确认", operation_id: result.data?.operation_id };
|
|
81
80
|
const loaded = this.store.load(aid);
|
|
82
81
|
if (!loaded.ok || !loaded.data)
|
|
83
82
|
throw new Error(loaded.error?.message ?? "注册后未能加载本地身份");
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
<header><h1>AUN 身份</h1><button id="refresh" type="button">刷新</button></header>
|
|
7
7
|
<p id="status" role="status"></p>
|
|
8
8
|
<div class="table-scroll"><table><thead><tr><th>AID</th><th>来源</th><th>已关联 Agent</th><th>操作</th></tr></thead><tbody id="identities"></tbody></table></div>
|
|
9
|
-
<form id="create-form"><h2>创建 AID</h2><div class="fields"><label>新 AID<input name="aid" required placeholder="
|
|
9
|
+
<form id="create-form"><h2>创建 AID</h2><div class="fields"><label>新 AID<input name="aid" required placeholder="例如:example.agentid.cn"></label><label>Owner AID<input name="ownerAid" required placeholder="例如:owner.agentid.cn(本地归属配置)"><small class="hint">Owner AID 用于本地归属和 Agent 关联;创建身份本身沿用 AUN 的普通注册流程</small></label></div><button type="submit">创建</button></form>
|
|
10
10
|
<form id="config-form"><h2>通道配置</h2><div class="fields"><label>默认 Agent<select name="agent_plugin"></select></label><label>允许的来源<input name="allow_from"></label><label>AUN 存储路径<input name="aun_path"></label><label>加密种子<input name="encryption_seed" type="password" autocomplete="new-password" placeholder="留空则不修改"></label></div><button type="submit">保存配置</button></form>
|
|
11
11
|
</main>
|
|
12
12
|
<script src="/abweb/channels/aun/app.js"></script>
|