mu-agent-bridge 1.0.43 → 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 +1 -1
- package/dist/src-ts/cli.js +41 -0
- package/dist/src-ts/control-server.js +4 -1
- package/dist/src-ts/core-updater.js +77 -0
- package/dist/src-ts/plugins/aun/index.js +62 -32
- package/dist/src-ts/plugins/aun/public/app.js +8 -3
- package/dist/src-ts/plugins/feishu/index.js +5 -0
- package/dist/src-ts/plugins/workbuddy/cdp-client.js +51 -0
- package/dist/src-ts/plugins/workbuddy/index.js +91 -3
- package/dist/tests-ts/aun-plugin.test.js +54 -19
- package/dist/tests-ts/control-server.test.js +3 -1
- package/dist/tests-ts/core-updater.test.js +86 -0
- package/dist/tests-ts/workbuddy-plugin.test.js +145 -3
- package/package.json +1 -1
- package/plugins/aun/dist/src-ts/plugins/aun/index.js +62 -32
- package/plugins/aun/dist/src-ts/plugins/aun/public/app.js +8 -3
- package/plugins/feishu/dist/src-ts/plugins/feishu/index.js +5 -0
- package/plugins/workbuddy/config.schema.json +4 -1
- package/plugins/workbuddy/dist/src-ts/plugins/workbuddy/cdp-client.js +51 -0
- package/plugins/workbuddy/dist/src-ts/plugins/workbuddy/index.js +91 -3
- package/plugins/workbuddy/package.json +1 -1
|
@@ -387,6 +387,36 @@ async function withAunAttachmentPlugin(run, publish, config = { allow_from: "pee
|
|
|
387
387
|
await (0, promises_1.rm)(root, { recursive: true, force: true });
|
|
388
388
|
}
|
|
389
389
|
});
|
|
390
|
+
(0, node_test_1.default)("AUN 重绑和解绑只修改指定 AID,同一 Agent 的其它 AID 保持不变", async () => {
|
|
391
|
+
const root = await (0, promises_1.mkdtemp)((0, node_path_1.join)((0, node_os_1.tmpdir)(), "aun-binding-isolation-"));
|
|
392
|
+
const aids = ["first.agentid.cn", "second.agentid.cn"];
|
|
393
|
+
const sdk = {
|
|
394
|
+
AIDStore: class {
|
|
395
|
+
list() { return { ok: true, data: { identities: aids.map((aid) => ({ aid })) } }; }
|
|
396
|
+
load(aid) { return { ok: true, data: { aid } }; }
|
|
397
|
+
close() { }
|
|
398
|
+
},
|
|
399
|
+
AUNClient: class {
|
|
400
|
+
on() { return { unsubscribe() { } }; }
|
|
401
|
+
async connect() { }
|
|
402
|
+
async close() { }
|
|
403
|
+
},
|
|
404
|
+
};
|
|
405
|
+
const plugin = new aun_1.AunPlugin({ sdk, ingress: { publish: async () => undefined } });
|
|
406
|
+
try {
|
|
407
|
+
await plugin.activate({ dataDir: root, config: { bindings: { [aids[0]]: "qwenwork", [aids[1]]: "qwenwork" }, binding_times: { [aids[0]]: "old-1", [aids[1]]: "old-2" } }, logger: { info() { }, warn() { }, error() { } }, callPlugin: async () => ({ text: "" }), pluginStatus: () => "ready", http: { register() { }, unregister() { } } });
|
|
408
|
+
await plugin.identity.bind("workbuddy", aids[0], 26107);
|
|
409
|
+
strict_1.default.deepEqual((await plugin.health()).details?.bindings, { [aids[0]]: "workbuddy", [aids[1]]: "qwenwork" });
|
|
410
|
+
await plugin.identity.unbind("workbuddy", aids[0], 26107);
|
|
411
|
+
strict_1.default.deepEqual((await plugin.health()).details?.bindings, { [aids[1]]: "qwenwork" });
|
|
412
|
+
const saved = JSON.parse(await (0, promises_1.readFile)((0, node_path_1.join)(root, "config.json"), "utf8"));
|
|
413
|
+
strict_1.default.deepEqual(saved.binding_times, { [aids[1]]: "old-2" });
|
|
414
|
+
}
|
|
415
|
+
finally {
|
|
416
|
+
await plugin.deactivate("stop");
|
|
417
|
+
await (0, promises_1.rm)(root, { recursive: true, force: true });
|
|
418
|
+
}
|
|
419
|
+
});
|
|
390
420
|
(0, node_test_1.default)("AUN 关联保留 ControlServer 的业务错误并回读状态", async () => {
|
|
391
421
|
const root = await (0, promises_1.mkdtemp)((0, node_path_1.join)((0, node_os_1.tmpdir)(), "aun-cli-business-error-"));
|
|
392
422
|
const routes = new Map();
|
|
@@ -432,13 +462,13 @@ async function withAunAttachmentPlugin(run, publish, config = { allow_from: "pee
|
|
|
432
462
|
strict_1.default.match(bound.body.message, /AID 不在本地密钥库中/);
|
|
433
463
|
strict_1.default.deepEqual(bound.body.data, { aid: "missing.agentid.cn", agent: "qwenwork", bindingState: { aunAids: [], agentAid: "old.agentid.cn" } });
|
|
434
464
|
const created = await runAunCommand(routes, commandResponse(), ["create", "new.agentid.cn", "--owner", "owner.agentid.cn", "--agent", "qwenwork"]);
|
|
435
|
-
strict_1.default.equal(created.statusCode,
|
|
465
|
+
strict_1.default.equal(created.statusCode, 200);
|
|
436
466
|
strict_1.default.equal(created.body.data.created, true);
|
|
437
|
-
strict_1.default.
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
strict_1.default.equal(unbound.
|
|
441
|
-
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, {});
|
|
442
472
|
}
|
|
443
473
|
finally {
|
|
444
474
|
await server.stop();
|
|
@@ -606,8 +636,13 @@ async function withAunAttachmentPlugin(run, publish, config = { allow_from: "pee
|
|
|
606
636
|
return new Response(JSON.stringify({ ok: true, bindings: currentAid ? { [currentAid]: "qwenwork" } : {} }), { status: 200 });
|
|
607
637
|
if (url.endsWith("/config"))
|
|
608
638
|
return new Response(JSON.stringify({ aid: currentAid }), { status: 200 });
|
|
609
|
-
if (init?.method === "PATCH")
|
|
639
|
+
if (init?.method === "PATCH") {
|
|
610
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];
|
|
611
646
|
return new Response(JSON.stringify({ ok: true }), { status: 200 });
|
|
612
647
|
});
|
|
613
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: {
|
|
@@ -620,19 +655,18 @@ async function withAunAttachmentPlugin(run, publish, config = { allow_from: "pee
|
|
|
620
655
|
const created = await runAunCommand(routes, commandResponse(), ["create", "new.agentid.cn", "--owner", "owner.agentid.cn", "--agent", "qwenwork"]);
|
|
621
656
|
strict_1.default.equal(created.body.ok, true);
|
|
622
657
|
strict_1.default.equal(created.body.data.created, true);
|
|
623
|
-
strict_1.default.
|
|
624
|
-
strict_1.default.
|
|
658
|
+
strict_1.default.equal(created.body.data.agent, "qwenwork");
|
|
659
|
+
strict_1.default.doesNotMatch(created.body.message, /old\.agentid\.cn/);
|
|
625
660
|
const bound = await runAunCommand(routes, commandResponse(), ["bind", "new.agentid.cn", "qwenwork"]);
|
|
626
661
|
strict_1.default.equal(bound.body.ok, true);
|
|
627
|
-
strict_1.default.deepEqual(bound.body.data.
|
|
628
|
-
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"]);
|
|
629
664
|
strict_1.default.equal(unbound.body.ok, true);
|
|
630
|
-
strict_1.default.deepEqual(unbound.body.data
|
|
665
|
+
strict_1.default.deepEqual(unbound.body.data, { aid: "new.agentid.cn", agent: "qwenwork" });
|
|
631
666
|
const patches = requests.filter((request) => request.method === "PATCH");
|
|
632
667
|
strict_1.default.deepEqual(patches.map(({ url, body }) => [url, JSON.parse(body)]), [
|
|
633
668
|
["http://127.0.0.1:3021/api/plugins/qwenwork/aid", { aid: "new.agentid.cn" }],
|
|
634
669
|
["http://127.0.0.1:3021/api/plugins/qwenwork/aid", { aid: "new.agentid.cn" }],
|
|
635
|
-
["http://127.0.0.1:3021/api/plugins/qwenwork/aid", { aid: "" }],
|
|
636
670
|
]);
|
|
637
671
|
strict_1.default.ok(requests.every(({ url }) => new URL(url).hostname === "127.0.0.1"));
|
|
638
672
|
strict_1.default.ok(requests.every(({ signal }) => signal instanceof AbortSignal));
|
|
@@ -700,10 +734,11 @@ async function withAunAttachmentPlugin(run, publish, config = { allow_from: "pee
|
|
|
700
734
|
const failedBind = await runAunCommand(routes, commandResponse(), ["bind", "existing.agentid.cn", "qwenwork"]);
|
|
701
735
|
strict_1.default.equal(failedBind.statusCode, 502);
|
|
702
736
|
strict_1.default.deepEqual(failedBind.body.data.bindingState, { aunAids: ["new.agentid.cn"], agentAid: "old.agentid.cn" });
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
strict_1.default.
|
|
706
|
-
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 全局配置");
|
|
707
742
|
const malformed = await runAunCommand(routes, commandResponse(), ["unknown"]);
|
|
708
743
|
strict_1.default.equal(malformed.statusCode, 400);
|
|
709
744
|
strict_1.default.equal(malformed.body.ok, false);
|
|
@@ -738,7 +773,7 @@ async function withAunAttachmentPlugin(run, publish, config = { allow_from: "pee
|
|
|
738
773
|
const plugin = new aun_1.AunPlugin({ sdk, ingress: { publish: async () => undefined } });
|
|
739
774
|
try {
|
|
740
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() { } } });
|
|
741
|
-
for (const argv of [["agents"], ["bind", "valid.agentid.cn", "qwenwork"]
|
|
776
|
+
for (const argv of [["agents"], ["bind", "valid.agentid.cn", "qwenwork"]]) {
|
|
742
777
|
const result = await runAunCommand(routes, commandResponse(), argv);
|
|
743
778
|
strict_1.default.equal(result.statusCode, 502);
|
|
744
779
|
strict_1.default.equal(result.body.ok, false);
|
|
@@ -822,7 +857,7 @@ async function withAunAttachmentPlugin(run, publish, config = { allow_from: "pee
|
|
|
822
857
|
fetch: async (path) => ({ ok: true, json: async () => path.endsWith("/identities") ? identities : path === "/api/plugins" ? { plugins: agents } : path.includes("qwenwork") ? { aid: "external.agentid.cn" } : {} }),
|
|
823
858
|
});
|
|
824
859
|
await new Promise((resolve) => setImmediate(resolve));
|
|
825
|
-
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"]);
|
|
826
861
|
});
|
|
827
862
|
(0, node_test_1.default)("AUN 页面跨来源重复 AID 只显示本工程归属", async () => {
|
|
828
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
|
+
});
|
|
@@ -55,6 +55,145 @@ const cdp_client_1 = require("../src-ts/plugins/workbuddy/cdp-client");
|
|
|
55
55
|
const client = new cdp_client_1.WorkbuddyCdpClient({ fetchImpl: async () => new Response(JSON.stringify([{ type: "page", title: "Other App", webSocketDebuggerUrl: "ws://127.0.0.1:9229/devtools/page/1" }])) });
|
|
56
56
|
await strict_1.default.rejects(() => client.connect(), /未找到 WorkBuddy 页面 target/);
|
|
57
57
|
});
|
|
58
|
+
(0, node_test_1.default)("workbuddy 积分领取只点击免费领取按钮并跳过购买入口", async () => {
|
|
59
|
+
const clicked = [];
|
|
60
|
+
const buttons = [
|
|
61
|
+
["每日签到", "每日签到领取积分"], ["领取积分", "积分奖励"], ["限时加购", "积分购买"],
|
|
62
|
+
["购买积分", "积分商城"], ["去完成", "每日积分任务"], ["签到", "通知设置"], ["去完成", "充值积分任务"],
|
|
63
|
+
].map(([label, context]) => ({
|
|
64
|
+
innerText: label,
|
|
65
|
+
textContent: label,
|
|
66
|
+
ariaLabel: "",
|
|
67
|
+
title: "",
|
|
68
|
+
disabled: false,
|
|
69
|
+
getAttribute: (name) => name === "aria-label" ? "" : null,
|
|
70
|
+
click: () => clicked.push(label),
|
|
71
|
+
getClientRects: () => [{}],
|
|
72
|
+
parentElement: { innerText: context, parentElement: null },
|
|
73
|
+
}));
|
|
74
|
+
const client = new cdp_client_1.WorkbuddyCdpClient();
|
|
75
|
+
const internal = client;
|
|
76
|
+
internal.evaluate = async (expression) => (0, node_vm_1.runInNewContext)(expression, {
|
|
77
|
+
document: { querySelectorAll: () => buttons, querySelector: () => null },
|
|
78
|
+
});
|
|
79
|
+
const result = await internal.claimDailyPoints();
|
|
80
|
+
strict_1.default.deepEqual([...result.clicked], ["每日签到", "领取积分", "去完成"]);
|
|
81
|
+
strict_1.default.equal(result.skipped, 3);
|
|
82
|
+
strict_1.default.deepEqual(clicked, ["每日签到", "领取积分", "去完成"]);
|
|
83
|
+
});
|
|
84
|
+
(0, node_test_1.default)("workbuddy 积分领取先切到 Buddy 加油站并点击每日立即领取", async () => {
|
|
85
|
+
const clicks = [];
|
|
86
|
+
const card = { innerText: "1024 造物挑战开启!" };
|
|
87
|
+
const action = {
|
|
88
|
+
innerText: "立即投稿",
|
|
89
|
+
disabled: false,
|
|
90
|
+
getAttribute: (name) => name === "data-track-props" ? JSON.stringify({ index: 1, type: "notice" }) : null,
|
|
91
|
+
getClientRects: () => [{}],
|
|
92
|
+
click: () => clicks.push("action"),
|
|
93
|
+
};
|
|
94
|
+
const dots = ["Buddy加油站", "活动通知", "成长计划"].map((label, index) => ({
|
|
95
|
+
getAttribute: (name) => name === "aria-label" ? label : null,
|
|
96
|
+
click: () => {
|
|
97
|
+
clicks.push(`dot-${index}`);
|
|
98
|
+
card.innerText = "Buddy加油站\n每日可领 100 积分";
|
|
99
|
+
action.innerText = "立即领取";
|
|
100
|
+
action.getAttribute = (name) => name === "data-track-props" ? JSON.stringify({ index: 0, type: "checkin" }) : null;
|
|
101
|
+
},
|
|
102
|
+
}));
|
|
103
|
+
const root = { querySelector: (selector) => selector === "#rw" ? card : selector === "#go" ? action : null, querySelectorAll: (selector) => selector === ".dot" ? dots : [] };
|
|
104
|
+
const client = new cdp_client_1.WorkbuddyCdpClient();
|
|
105
|
+
const internal = client;
|
|
106
|
+
internal.evaluate = async (expression) => (0, node_vm_1.runInNewContext)(expression, { document: { querySelectorAll: () => [], querySelector: () => ({ shadowRoot: root }) } });
|
|
107
|
+
const result = await internal.claimDailyPoints();
|
|
108
|
+
strict_1.default.deepEqual([...result.clicked], ["Buddy加油站:立即领取"]);
|
|
109
|
+
strict_1.default.deepEqual(clicks, ["dot-0", "action"]);
|
|
110
|
+
});
|
|
111
|
+
(0, node_test_1.default)("workbuddy 每日积分调度同一天只执行一次", async () => {
|
|
112
|
+
let claims = 0;
|
|
113
|
+
const client = {
|
|
114
|
+
connect: async () => undefined, close() { }, connected: () => true,
|
|
115
|
+
claimDailyPoints: async () => { claims++; return { clicked: ["每日签到"], skipped: 0 }; },
|
|
116
|
+
direct: async () => "回复",
|
|
117
|
+
};
|
|
118
|
+
const dataDir = await (0, promises_1.mkdtemp)((0, node_path_1.join)((0, node_os_1.tmpdir)(), "wb-daily-points-"));
|
|
119
|
+
const plugin = new workbuddy_1.WorkbuddyPlugin({ client });
|
|
120
|
+
try {
|
|
121
|
+
await plugin.activate({ dataDir, config: { transport: "cdp", dailyPoints: true, dailyPointsHour: 0, dailyPointsMinute: 0 }, logger: { info() { }, warn() { }, error() { } } });
|
|
122
|
+
for (let index = 0; index < 100 && claims === 0; index++)
|
|
123
|
+
await new Promise(resolve => setTimeout(resolve, 5));
|
|
124
|
+
strict_1.default.equal(claims, 1);
|
|
125
|
+
await plugin.deactivate("stop");
|
|
126
|
+
const restarted = new workbuddy_1.WorkbuddyPlugin({ client });
|
|
127
|
+
await restarted.activate({ dataDir, config: { transport: "cdp", dailyPoints: true, dailyPointsHour: 0, dailyPointsMinute: 0 }, logger: { info() { }, warn() { }, error() { } } });
|
|
128
|
+
await new Promise(resolve => setTimeout(resolve, 20));
|
|
129
|
+
strict_1.default.equal(claims, 1);
|
|
130
|
+
await restarted.deactivate("stop");
|
|
131
|
+
}
|
|
132
|
+
finally {
|
|
133
|
+
await (0, promises_1.rm)(dataDir, { recursive: true, force: true });
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
(0, node_test_1.default)("workbuddy 每日积分检查失败后同一天重启会重试", async () => {
|
|
137
|
+
let attempts = 0;
|
|
138
|
+
const client = {
|
|
139
|
+
connect: async () => undefined, close() { }, connected: () => true,
|
|
140
|
+
claimDailyPoints: async () => {
|
|
141
|
+
attempts++;
|
|
142
|
+
if (attempts === 1)
|
|
143
|
+
throw new Error("CDP 暂时不可用");
|
|
144
|
+
return { clicked: ["每日签到"], skipped: 0 };
|
|
145
|
+
},
|
|
146
|
+
direct: async () => "回复",
|
|
147
|
+
};
|
|
148
|
+
const dataDir = await (0, promises_1.mkdtemp)((0, node_path_1.join)((0, node_os_1.tmpdir)(), "wb-daily-points-retry-"));
|
|
149
|
+
const config = { transport: "cdp", dailyPoints: true, dailyPointsHour: 0, dailyPointsMinute: 0 };
|
|
150
|
+
const logger = { info() { }, warn() { }, error() { } };
|
|
151
|
+
try {
|
|
152
|
+
const first = new workbuddy_1.WorkbuddyPlugin({ client });
|
|
153
|
+
await first.activate({ dataDir, config, logger });
|
|
154
|
+
for (let index = 0; index < 100 && attempts === 0; index++)
|
|
155
|
+
await new Promise(resolve => setTimeout(resolve, 5));
|
|
156
|
+
strict_1.default.equal(attempts, 1);
|
|
157
|
+
await first.deactivate("stop");
|
|
158
|
+
const restarted = new workbuddy_1.WorkbuddyPlugin({ client });
|
|
159
|
+
await restarted.activate({ dataDir, config, logger });
|
|
160
|
+
for (let index = 0; index < 100 && attempts === 1; index++)
|
|
161
|
+
await new Promise(resolve => setTimeout(resolve, 5));
|
|
162
|
+
strict_1.default.equal(attempts, 2);
|
|
163
|
+
await restarted.deactivate("stop");
|
|
164
|
+
}
|
|
165
|
+
finally {
|
|
166
|
+
await (0, promises_1.rm)(dataDir, { recursive: true, force: true });
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
(0, node_test_1.default)("workbuddy 只有进入任务入口时重启仍会重试领取", async () => {
|
|
170
|
+
let attempts = 0;
|
|
171
|
+
const client = {
|
|
172
|
+
connect: async () => undefined, close() { }, connected: () => true,
|
|
173
|
+
claimDailyPoints: async () => { attempts++; return { clicked: ["去完成"], skipped: 0 }; },
|
|
174
|
+
direct: async () => "回复",
|
|
175
|
+
};
|
|
176
|
+
const dataDir = await (0, promises_1.mkdtemp)((0, node_path_1.join)((0, node_os_1.tmpdir)(), "wb-daily-points-entry-"));
|
|
177
|
+
const config = { transport: "cdp", dailyPoints: true, dailyPointsHour: 0, dailyPointsMinute: 0 };
|
|
178
|
+
const logger = { info() { }, warn() { }, error() { } };
|
|
179
|
+
try {
|
|
180
|
+
const first = new workbuddy_1.WorkbuddyPlugin({ client });
|
|
181
|
+
await first.activate({ dataDir, config, logger });
|
|
182
|
+
for (let index = 0; index < 100 && attempts === 0; index++)
|
|
183
|
+
await new Promise(resolve => setTimeout(resolve, 5));
|
|
184
|
+
strict_1.default.equal(attempts, 1);
|
|
185
|
+
await first.deactivate("stop");
|
|
186
|
+
const restarted = new workbuddy_1.WorkbuddyPlugin({ client });
|
|
187
|
+
await restarted.activate({ dataDir, config, logger });
|
|
188
|
+
for (let index = 0; index < 100 && attempts === 1; index++)
|
|
189
|
+
await new Promise(resolve => setTimeout(resolve, 5));
|
|
190
|
+
strict_1.default.equal(attempts, 2);
|
|
191
|
+
await restarted.deactivate("stop");
|
|
192
|
+
}
|
|
193
|
+
finally {
|
|
194
|
+
await (0, promises_1.rm)(dataDir, { recursive: true, force: true });
|
|
195
|
+
}
|
|
196
|
+
});
|
|
58
197
|
(0, node_test_1.default)("workbuddy 插件通过已连接 CDP 返回桌面端回复", async () => {
|
|
59
198
|
const calls = [];
|
|
60
199
|
const plugin = new workbuddy_1.WorkbuddyPlugin({ client: { connect: async () => undefined, close() { }, connected: () => true, createTask: async () => undefined, direct: async (text) => { calls.push(text); return "WB_DESKTOP_REPLY"; } } });
|
|
@@ -927,14 +1066,15 @@ const cdp_client_1 = require("../src-ts/plugins/workbuddy/cdp-client");
|
|
|
927
1066
|
await (0, promises_1.rm)(dataDir, { recursive: true, force: true });
|
|
928
1067
|
}
|
|
929
1068
|
});
|
|
930
|
-
(0, node_test_1.default)("workbuddy
|
|
1069
|
+
(0, node_test_1.default)("workbuddy 重载后恢复原桌面任务并继续同一会话", async () => {
|
|
931
1070
|
const dataDir = await (0, promises_1.mkdtemp)((0, node_path_1.join)((0, node_os_1.tmpdir)(), "wb-active-session-"));
|
|
932
1071
|
let creates = 0;
|
|
933
1072
|
let desktopId = "desktop-a";
|
|
934
1073
|
const client = {
|
|
935
1074
|
connect: async () => undefined, close() { }, connected: () => true,
|
|
936
1075
|
conversationId: async () => desktopId,
|
|
937
|
-
|
|
1076
|
+
activateConversation: async (id) => { desktopId = id; },
|
|
1077
|
+
createTask: async () => { creates++; desktopId = `desktop-${creates}`; }, direct: async (task) => task,
|
|
938
1078
|
};
|
|
939
1079
|
const context = { dataDir, config: { transport: "cdp" }, logger: { info() { }, warn() { }, error() { } } };
|
|
940
1080
|
try {
|
|
@@ -950,7 +1090,9 @@ const cdp_client_1 = require("../src-ts/plugins/workbuddy/cdp-client");
|
|
|
950
1090
|
desktopId = "desktop-b";
|
|
951
1091
|
const third = new workbuddy_1.WorkbuddyPlugin({ client });
|
|
952
1092
|
await third.activate(context);
|
|
953
|
-
|
|
1093
|
+
strict_1.default.equal((await third.handle({ task: "恢复原任务后继续", sessionId: "session-a" })).text, "恢复原任务后继续");
|
|
1094
|
+
strict_1.default.equal(desktopId, "desktop-1");
|
|
1095
|
+
strict_1.default.equal(creates, 1);
|
|
954
1096
|
await third.deactivate("stop");
|
|
955
1097
|
}
|
|
956
1098
|
finally {
|
package/package.json
CHANGED
|
@@ -41,7 +41,7 @@ const cliCommands = [
|
|
|
41
41
|
{ name: "default-owner", summary: "查询或设置默认 Owner AID", usage: "mab aun default-owner [set <owner-aid>]", examples: ["mab aun default-owner", "mab aun default-owner set owner.agentid.cn"] },
|
|
42
42
|
{ name: "create", summary: "创建并连接 AID,可选关联 Agent", usage: "mab aun create <aid> [--owner <owner-aid>] [--agent <plugin-id>]", examples: ["mab aun create new.agentid.cn --owner owner.agentid.cn --agent qwenwork"] },
|
|
43
43
|
{ name: "bind", summary: "将 AID 关联到 Agent", usage: "mab aun bind <aid> <plugin-id>", examples: ["mab aun bind new.agentid.cn qwenwork"] },
|
|
44
|
-
{ name: "unbind", summary: "
|
|
44
|
+
{ name: "unbind", summary: "解除指定 AID 与 Agent 的关联", usage: "mab aun unbind <aid>", examples: ["mab aun unbind old.agentid.cn"] },
|
|
45
45
|
];
|
|
46
46
|
class AunCommandError extends Error {
|
|
47
47
|
statusCode;
|
|
@@ -140,11 +140,6 @@ class AunPlugin {
|
|
|
140
140
|
await this.connectAid(aid);
|
|
141
141
|
if (!this.clients.has(aid))
|
|
142
142
|
throw new Error("AID 当前未连接");
|
|
143
|
-
for (const [identity, target] of Object.entries(this.bindings))
|
|
144
|
-
if (target === pluginId) {
|
|
145
|
-
delete this.bindings[identity];
|
|
146
|
-
delete this.bindingTimes[identity];
|
|
147
|
-
}
|
|
148
143
|
this.bindings[aid] = pluginId;
|
|
149
144
|
}
|
|
150
145
|
else {
|
|
@@ -157,21 +152,11 @@ class AunPlugin {
|
|
|
157
152
|
await this.connectAid(aid);
|
|
158
153
|
if (!this.clients.has(aid))
|
|
159
154
|
throw new Error("AID 当前未连接");
|
|
160
|
-
for (const [identity, target] of Object.entries(this.bindings))
|
|
161
|
-
if (target === pluginId) {
|
|
162
|
-
delete this.bindings[identity];
|
|
163
|
-
delete this.bindingTimes[identity];
|
|
164
|
-
}
|
|
165
155
|
this.bindings[aid] = pluginId;
|
|
166
156
|
}
|
|
167
157
|
}
|
|
168
|
-
else
|
|
169
|
-
|
|
170
|
-
if (target === pluginId) {
|
|
171
|
-
delete this.bindings[identity];
|
|
172
|
-
delete this.bindingTimes[identity];
|
|
173
|
-
}
|
|
174
|
-
}
|
|
158
|
+
else
|
|
159
|
+
throw new Error("必须指定要关联的 AID");
|
|
175
160
|
if (aid)
|
|
176
161
|
this.bindingTimes[aid] = new Date().toISOString();
|
|
177
162
|
await this.persist();
|
|
@@ -186,6 +171,29 @@ class AunPlugin {
|
|
|
186
171
|
}
|
|
187
172
|
this.context?.logger.info("AUN 身份关联已更新", { aid, pluginId });
|
|
188
173
|
},
|
|
174
|
+
unbind: async (pluginId, aid) => {
|
|
175
|
+
if (!aidPattern.test(aid))
|
|
176
|
+
throw new Error("AID 格式无效");
|
|
177
|
+
if (this.bindings[aid] !== pluginId)
|
|
178
|
+
throw new Error("指定 AID 当前未关联到该 Agent");
|
|
179
|
+
const previous = this.bindings;
|
|
180
|
+
const previousTimes = this.bindingTimes;
|
|
181
|
+
this.bindings = { ...previous };
|
|
182
|
+
this.bindingTimes = { ...previousTimes };
|
|
183
|
+
delete this.bindings[aid];
|
|
184
|
+
delete this.bindingTimes[aid];
|
|
185
|
+
try {
|
|
186
|
+
await this.persist();
|
|
187
|
+
}
|
|
188
|
+
catch (error) {
|
|
189
|
+
this.bindings = previous;
|
|
190
|
+
this.bindingTimes = previousTimes;
|
|
191
|
+
this.config.bindings = previous;
|
|
192
|
+
this.config.binding_times = previousTimes;
|
|
193
|
+
throw error;
|
|
194
|
+
}
|
|
195
|
+
this.context?.logger.info("AUN 单个身份关联已解除", { aid, pluginId });
|
|
196
|
+
},
|
|
189
197
|
};
|
|
190
198
|
async persist() {
|
|
191
199
|
this.config.identities = [...this.identities].sort();
|
|
@@ -236,6 +244,7 @@ class AunPlugin {
|
|
|
236
244
|
["POST", "/api/plugins/aun/aid", this.createAid],
|
|
237
245
|
["GET", "/api/plugins/aun/identities", this.listIdentities],
|
|
238
246
|
["POST", "/api/plugins/aun/bind", this.bindAgent],
|
|
247
|
+
["POST", "/api/plugins/aun/unbind", this.unbindAgent],
|
|
239
248
|
["POST", "/api/plugins/aun/settings", this.saveSettings],
|
|
240
249
|
["GET", "/api/plugins/aun/commands", this.listCommands],
|
|
241
250
|
["POST", "/api/plugins/aun/commands", this.runCommand],
|
|
@@ -455,6 +464,28 @@ class AunPlugin {
|
|
|
455
464
|
this.json(response, 400, { error: safeError(error) });
|
|
456
465
|
}
|
|
457
466
|
};
|
|
467
|
+
unbindAgent = async (request, response) => {
|
|
468
|
+
try {
|
|
469
|
+
let raw = "";
|
|
470
|
+
for await (const chunk of request) {
|
|
471
|
+
raw += chunk;
|
|
472
|
+
if (raw.length > 65536)
|
|
473
|
+
throw new Error("请求内容过大");
|
|
474
|
+
}
|
|
475
|
+
const body = JSON.parse(raw || "{}");
|
|
476
|
+
const aid = typeof body.aid === "string" ? body.aid.trim() : "";
|
|
477
|
+
if (!aidPattern.test(aid))
|
|
478
|
+
return this.json(response, 400, { error: "AID 格式无效" });
|
|
479
|
+
const pluginId = this.bindings[aid];
|
|
480
|
+
if (!pluginId)
|
|
481
|
+
return this.json(response, 404, { error: "指定 AID 当前未关联 Agent" });
|
|
482
|
+
await this.identity.unbind(pluginId, aid, Number(this.context?.runtime?.ports?.aun ?? 0));
|
|
483
|
+
this.json(response, 200, { ok: true, aid, pluginId });
|
|
484
|
+
}
|
|
485
|
+
catch (error) {
|
|
486
|
+
this.json(response, 400, { error: safeError(error) });
|
|
487
|
+
}
|
|
488
|
+
};
|
|
458
489
|
listCommands = async (_request, response) => {
|
|
459
490
|
response.setHeader("cache-control", "no-store");
|
|
460
491
|
this.json(response, 200, { protocolVersion: 1, plugin: "aun", version: manifest.version, commands: cliCommands });
|
|
@@ -515,7 +546,6 @@ class AunPlugin {
|
|
|
515
546
|
throw new AunCommandError("AID 格式无效");
|
|
516
547
|
this.validatePluginId(pluginId);
|
|
517
548
|
await this.requireAgent(pluginId);
|
|
518
|
-
const replacedAids = (await this.agentBindings(pluginId)).filter((previousAid) => previousAid !== aid);
|
|
519
549
|
try {
|
|
520
550
|
await this.patchAgentAid(pluginId, aid);
|
|
521
551
|
}
|
|
@@ -524,23 +554,25 @@ class AunPlugin {
|
|
|
524
554
|
throw new AunCommandError(`AID 关联请求失败,已回读当前状态:${safeError(error)}`, error instanceof AunCommandError ? error.statusCode : 502, { aid, agent: pluginId, ...(bindingState ? { bindingState } : { bindingState: "unknown" }) });
|
|
525
555
|
}
|
|
526
556
|
return {
|
|
527
|
-
message:
|
|
528
|
-
data: { aid, agent: pluginId
|
|
557
|
+
message: `AID ${aid} 已关联到 ${pluginId}`,
|
|
558
|
+
data: { aid, agent: pluginId },
|
|
529
559
|
};
|
|
530
560
|
}
|
|
531
561
|
if (command === "unbind" && args.length === 1) {
|
|
532
|
-
const [
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
const
|
|
562
|
+
const [aid] = args;
|
|
563
|
+
if (!aidPattern.test(aid))
|
|
564
|
+
throw new AunCommandError("AID 格式无效");
|
|
565
|
+
const pluginId = this.bindings[aid];
|
|
566
|
+
if (!pluginId)
|
|
567
|
+
throw new AunCommandError("指定 AID 当前未关联 Agent");
|
|
536
568
|
try {
|
|
537
|
-
await this.
|
|
569
|
+
await this.identity.unbind(pluginId, aid, 0);
|
|
538
570
|
}
|
|
539
571
|
catch (error) {
|
|
540
572
|
const bindingState = await this.bindingState(pluginId).catch(() => undefined);
|
|
541
|
-
throw new AunCommandError(`解除关联请求失败,已回读当前状态:${safeError(error)}`, error instanceof AunCommandError ? error.statusCode : 502, { agent: pluginId, ...(bindingState ? { bindingState } : { bindingState: "unknown" }) });
|
|
573
|
+
throw new AunCommandError(`解除关联请求失败,已回读当前状态:${safeError(error)}`, error instanceof AunCommandError ? error.statusCode : 502, { aid, agent: pluginId, ...(bindingState ? { bindingState } : { bindingState: "unknown" }) });
|
|
542
574
|
}
|
|
543
|
-
return { message: `已解除 ${
|
|
575
|
+
return { message: `已解除 AID ${aid} 与 ${pluginId} 的关联`, data: { aid, agent: pluginId } };
|
|
544
576
|
}
|
|
545
577
|
if (!["status", "list", "agents", "default-owner", "create", "bind", "unbind"].includes(command ?? ""))
|
|
546
578
|
throw new AunCommandError(`未知 AUN 命令:${command ?? ""}`);
|
|
@@ -604,10 +636,8 @@ class AunPlugin {
|
|
|
604
636
|
const data = { created: true, aid, connection: created.connection ?? "ready" };
|
|
605
637
|
if (created.connection !== "ready")
|
|
606
638
|
throw new AunCommandError("AID 已创建,但连接失败;请先执行 mab aun list 确认,不要重试创建", 502, { ...data, connectionError: created.connectionError });
|
|
607
|
-
let replacedAids = [];
|
|
608
639
|
if (agent) {
|
|
609
640
|
try {
|
|
610
|
-
replacedAids = (await this.agentBindings(agent)).filter((previousAid) => previousAid !== aid);
|
|
611
641
|
await this.patchAgentAid(agent, aid);
|
|
612
642
|
}
|
|
613
643
|
catch (error) {
|
|
@@ -616,8 +646,8 @@ class AunPlugin {
|
|
|
616
646
|
}
|
|
617
647
|
}
|
|
618
648
|
return {
|
|
619
|
-
message: agent ?
|
|
620
|
-
data: { ...data, ...(agent ? { agent
|
|
649
|
+
message: agent ? `AID 已创建并关联到 ${agent}` : "AID 已创建并连接",
|
|
650
|
+
data: { ...data, ...(agent ? { agent } : {}) },
|
|
621
651
|
};
|
|
622
652
|
}
|
|
623
653
|
async agents(readyOnly = true) {
|
|
@@ -28,7 +28,6 @@
|
|
|
28
28
|
const [identities, plugins, config] = await Promise.all([request("/api/plugins/aun/identities"), request("/api/plugins"), request("/api/plugins/aun/config")]);
|
|
29
29
|
agents = (plugins.plugins || []).filter((plugin) => plugin.type === "agent");
|
|
30
30
|
if (refreshForm) createForm.elements.ownerAid.value = config.owner_aid || "";
|
|
31
|
-
await Promise.all(agents.map(async (agent) => { agent.configAid = (await request(`/api/plugins/${encodeURIComponent(agent.id)}/config`)).aid || ""; }));
|
|
32
31
|
if (refreshForm) {
|
|
33
32
|
for (const key of ["allow_from", "aun_path"]) configForm.elements[key].value = config[key] || "";
|
|
34
33
|
configForm.elements.encryption_seed.value = "";
|
|
@@ -40,7 +39,7 @@
|
|
|
40
39
|
for (const aid of values) {
|
|
41
40
|
if (seen.has(aid)) continue;
|
|
42
41
|
seen.add(aid);
|
|
43
|
-
entries.push({ aid, label, group, bound: identities.bindings?.[aid] ||
|
|
42
|
+
entries.push({ aid, label, group, bound: identities.bindings?.[aid] || "", time: Date.parse(identities.bindingTimes?.[aid] || "") || 0 });
|
|
44
43
|
}
|
|
45
44
|
}
|
|
46
45
|
entries.sort((a, b) => Number(Boolean(b.bound)) - Number(Boolean(a.bound)) || a.group - b.group || b.time - a.time || a.aid.localeCompare(b.aid));
|
|
@@ -49,7 +48,7 @@
|
|
|
49
48
|
for (const value of [aid, label, bound || "未关联"]) { const cell = document.createElement("td"); cell.textContent = value; row.append(cell); }
|
|
50
49
|
const cell = document.createElement("td"); const select = document.createElement("select"); select.setAttribute("aria-label", `${aid} 关联 Agent`);
|
|
51
50
|
select.append(new Option("选择 Agent", ""), ...agents.map((agent) => new Option(agent.displayName || agent.id, agent.id)));
|
|
52
|
-
select.value = bound; select.onchange = () => { if (select.value) void bind(aid, select.value); };
|
|
51
|
+
select.value = bound; select.onchange = () => { if (select.value) void bind(aid, select.value); else if (bound) void unbind(aid); };
|
|
53
52
|
cell.append(select); row.append(cell); list.append(row);
|
|
54
53
|
}
|
|
55
54
|
if (!list.children.length) { const row = document.createElement("tr"); const cell = document.createElement("td"); cell.colSpan = 4; cell.textContent = "暂无已连接 AID"; row.append(cell); list.append(row); }
|
|
@@ -63,6 +62,12 @@
|
|
|
63
62
|
await load(); notify("关联成功", `${aid} 已关联 ${pluginId}`);
|
|
64
63
|
} catch (error) { notify("关联失败", errorMessage(error), true); }
|
|
65
64
|
}
|
|
65
|
+
async function unbind(aid) {
|
|
66
|
+
try {
|
|
67
|
+
await request("/api/plugins/aun/unbind", "POST", { aid });
|
|
68
|
+
await load(); notify("解除关联成功", `${aid} 已解除关联`);
|
|
69
|
+
} catch (error) { notify("解除关联失败", errorMessage(error), true); }
|
|
70
|
+
}
|
|
66
71
|
createForm.onsubmit = (event) => { event.preventDefault(); const aid = createForm.elements.aid.value.trim(); const owner = createForm.elements.ownerAid.value.trim();
|
|
67
72
|
show("正在创建 AID 并连接,请稍候……");
|
|
68
73
|
void request("/api/plugins/aun/aid", "POST", { aid, ownerAid: owner }).then((result) => {
|