mu-agent-bridge 1.0.73 → 1.0.78
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -8
- package/dist/src-ts/cli.js +67 -37
- package/dist/src-ts/control-server.js +7 -28
- package/dist/src-ts/core-updater.js +141 -33
- package/dist/src-ts/daemon-process.js +2 -1
- package/dist/src-ts/dsh-agent.js +7 -2
- package/dist/src-ts/http-agent.js +36 -0
- package/dist/src-ts/plugin-manager.js +185 -58
- package/dist/src-ts/plugins/abweb/index.js +1 -1
- package/dist/src-ts/plugins/abweb/public/app.js +8 -6
- package/dist/src-ts/plugins/abweb/public/index.html +3 -3
- package/dist/src-ts/plugins/aun/attachments.js +3 -21
- package/dist/src-ts/plugins/aun/index.js +89 -11
- package/dist/src-ts/plugins/aun/messages.js +1 -1
- package/dist/src-ts/plugins/aun/public/app.js +2 -1
- package/dist/src-ts/plugins/aun/public/index.html +1 -1
- package/dist/src-ts/plugins/cdp-draft.js +5 -2
- package/dist/src-ts/plugins/chatgpt/index.js +3 -2
- package/dist/src-ts/plugins/claude-cli/index.js +1 -1
- package/dist/src-ts/plugins/desktop-launch.js +44 -3
- package/dist/src-ts/plugins/doubao/cdp-client.js +231 -45
- package/dist/src-ts/plugins/doubao/index.js +119 -7
- package/dist/src-ts/plugins/doubao/native-task.js +134 -0
- package/dist/src-ts/plugins/dsh/cdp-client.js +334 -0
- package/dist/src-ts/plugins/dsh/index.js +102 -23
- package/dist/src-ts/plugins/dsh/session-history.js +31 -3
- package/dist/src-ts/plugins/feishu/index.js +1 -1
- package/dist/src-ts/plugins/personal-assistant/index.js +29 -8
- package/dist/src-ts/plugins/personal-assistant/model-client.js +20 -6
- package/dist/src-ts/plugins/personal-assistant/session-memory.js +124 -0
- package/dist/src-ts/plugins/personal-assistant/tools.js +4 -3
- package/dist/src-ts/plugins/qwenwork/cdp-client.js +43 -7
- package/dist/src-ts/plugins/qwenwork/index.js +52 -18
- package/dist/src-ts/plugins/workbuddy/cdp-client.js +349 -83
- package/dist/src-ts/plugins/workbuddy/index.js +24 -12
- package/dist/src-ts/plugins/zcode/cdp-client.js +500 -0
- package/dist/src-ts/plugins/zcode/index.js +97 -0
- package/dist/src-ts/session-management.js +27 -8
- package/dist/src-ts/version-manager.js +6 -3
- package/dist/tests-ts/abweb-model-config.test.js +44 -41
- package/dist/tests-ts/aun-attachments.test.js +19 -17
- package/dist/tests-ts/aun-default-owner-page.test.js +2 -1
- package/dist/tests-ts/aun-messages.test.js +14 -0
- package/dist/tests-ts/aun-plugin.test.js +207 -19
- package/dist/tests-ts/bundled-package.test.js +1 -1
- package/dist/tests-ts/cdp-draft-recovery.test.js +182 -3
- package/dist/tests-ts/chatgpt-plugin.test.js +19 -0
- package/dist/tests-ts/control-server.test.js +20 -0
- package/dist/tests-ts/core-updater.test.js +215 -46
- package/dist/tests-ts/daemon-process.test.js +126 -1
- package/dist/tests-ts/desktop-launch.test.js +73 -0
- package/dist/tests-ts/doubao-native-task.test.js +237 -0
- package/dist/tests-ts/doubao-plugin.test.js +688 -19
- package/dist/tests-ts/dsh-agent.test.js +91 -19
- package/dist/tests-ts/dsh-cdp.test.js +148 -0
- package/dist/tests-ts/dsh-history.test.js +37 -0
- package/dist/tests-ts/feishu-plugin.test.js +1 -1
- package/dist/tests-ts/http-agent.test.js +18 -0
- package/dist/tests-ts/local-cli.test.js +29 -2
- package/dist/tests-ts/model-provider-cancellation.test.js +2 -0
- package/dist/tests-ts/native-session-management.test.js +8 -7
- package/dist/tests-ts/personal-assistant-coordination.test.js +16 -0
- package/dist/tests-ts/personal-assistant-extensions.test.js +7 -1
- package/dist/tests-ts/personal-assistant-memory.test.js +117 -0
- package/dist/tests-ts/personal-assistant.test.js +277 -3
- package/dist/tests-ts/platform-integration.test.js +60 -1
- package/dist/tests-ts/plugin-manager.test.js +78 -6
- package/dist/tests-ts/plugin-upgrade-deadline.test.js +354 -0
- package/dist/tests-ts/publish-packages.test.js +1 -1
- package/dist/tests-ts/qwenwork-plugin.test.js +30 -0
- package/dist/tests-ts/qwenwork-reconnect.test.js +195 -0
- package/dist/tests-ts/qwenwork-sse.test.js +33 -0
- package/dist/tests-ts/session-access-defaults.test.js +112 -0
- package/dist/tests-ts/session-management.test.js +8 -8
- package/dist/tests-ts/version-manager.test.js +40 -0
- package/dist/tests-ts/workbuddy-cdp-recovery.test.js +311 -0
- package/dist/tests-ts/workbuddy-plugin.test.js +356 -13
- package/dist/tests-ts/zcode-cdp.test.js +894 -0
- package/dist/tests-ts/zcode-plugin.test.js +396 -0
- package/package.json +1 -1
- package/plugins/abweb/dist/src-ts/plugins/abweb/index.js +1 -1
- package/plugins/abweb/dist/src-ts/plugins/abweb/public/app.js +8 -6
- package/plugins/abweb/dist/src-ts/plugins/abweb/public/app.ts +7 -5
- package/plugins/abweb/dist/src-ts/plugins/abweb/public/index.html +3 -3
- package/plugins/abweb/package.json +1 -1
- package/plugins/abweb/plugin.json +1 -1
- package/plugins/aun/config.schema.json +1 -1
- package/plugins/aun/dist/src-ts/plugins/aun/attachments.js +3 -21
- package/plugins/aun/dist/src-ts/plugins/aun/index.js +89 -11
- package/plugins/aun/dist/src-ts/plugins/aun/messages.js +1 -1
- package/plugins/aun/dist/src-ts/plugins/aun/public/app.js +2 -1
- package/plugins/aun/dist/src-ts/plugins/aun/public/index.html +1 -1
- package/plugins/aun/package.json +1 -1
- package/plugins/aun/plugin.json +1 -1
- package/plugins/chatgpt/dist/src-ts/plugins/cdp-draft.js +5 -2
- package/plugins/chatgpt/dist/src-ts/plugins/chatgpt/index.js +3 -2
- package/plugins/chatgpt/dist/src-ts/plugins/desktop-launch.js +44 -3
- package/plugins/claude-cli/dist/src-ts/plugins/claude-cli/index.js +1 -1
- package/plugins/claude-cli/package.json +1 -1
- package/plugins/claude-cli/plugin.json +1 -1
- package/plugins/doubao/config.schema.json +2 -1
- package/plugins/doubao/dist/src-ts/plugins/desktop-launch.js +44 -3
- package/plugins/doubao/dist/src-ts/plugins/doubao/cdp-client.js +231 -45
- package/plugins/doubao/dist/src-ts/plugins/doubao/index.js +119 -7
- package/plugins/doubao/dist/src-ts/plugins/doubao/native-task.js +134 -0
- package/plugins/doubao/package.json +1 -1
- package/plugins/doubao/plugin.json +1 -1
- package/plugins/dsh/config.schema.json +5 -2
- package/plugins/dsh/dist/src-ts/dsh-agent.js +7 -2
- package/plugins/dsh/dist/src-ts/plugins/desktop-launch.js +44 -3
- package/plugins/dsh/dist/src-ts/plugins/dsh/cdp-client.js +334 -0
- package/plugins/dsh/dist/src-ts/plugins/dsh/index.js +102 -23
- package/plugins/dsh/dist/src-ts/plugins/dsh/session-history.js +31 -3
- package/plugins/dsh/package.json +1 -1
- package/plugins/dsh/plugin.json +1 -1
- package/plugins/feishu/dist/src-ts/plugins/feishu/index.js +1 -1
- package/plugins/feishu/package.json +1 -1
- package/plugins/feishu/plugin.json +1 -1
- package/plugins/personal-assistant/config.schema.json +3 -2
- package/plugins/personal-assistant/dist/src-ts/attachments.js +62 -0
- package/plugins/personal-assistant/dist/src-ts/plugins/personal-assistant/index.js +29 -8
- package/plugins/personal-assistant/dist/src-ts/plugins/personal-assistant/model-client.js +20 -6
- package/plugins/personal-assistant/dist/src-ts/plugins/personal-assistant/session-memory.js +124 -0
- package/plugins/personal-assistant/dist/src-ts/plugins/personal-assistant/tools.js +4 -3
- package/plugins/personal-assistant/package.json +2 -2
- package/plugins/personal-assistant/plugin.json +1 -1
- package/plugins/qwenwork/dist/src-ts/plugins/cdp-draft.js +5 -2
- package/plugins/qwenwork/dist/src-ts/plugins/desktop-launch.js +44 -3
- package/plugins/qwenwork/dist/src-ts/plugins/qwenwork/cdp-client.js +43 -7
- package/plugins/qwenwork/dist/src-ts/plugins/qwenwork/index.js +52 -18
- package/plugins/qwenwork/package.json +1 -1
- package/plugins/qwenwork/plugin.json +1 -1
- package/plugins/workbuddy/config.schema.json +1 -1
- package/plugins/workbuddy/dist/src-ts/plugins/desktop-launch.js +44 -3
- package/plugins/workbuddy/dist/src-ts/plugins/workbuddy/cdp-client.js +349 -83
- package/plugins/workbuddy/dist/src-ts/plugins/workbuddy/index.js +24 -12
- package/plugins/workbuddy/package.json +1 -1
- package/plugins/workbuddy/plugin.json +1 -1
- package/plugins/zcode/config.schema.json +13 -0
- package/plugins/zcode/dist/src-ts/agent.js +69 -0
- package/plugins/zcode/dist/src-ts/attachments.js +62 -0
- package/plugins/zcode/dist/src-ts/plugin.js +13 -0
- package/plugins/zcode/dist/src-ts/plugins/desktop-launch.js +238 -0
- package/plugins/zcode/dist/src-ts/plugins/workbuddy/cdp-client.js +896 -0
- package/plugins/zcode/dist/src-ts/plugins/workbuddy/cli-client.js +39 -0
- package/plugins/zcode/dist/src-ts/plugins/workbuddy/desktop-launch.js +10 -0
- package/plugins/zcode/dist/src-ts/plugins/workbuddy/index.js +567 -0
- package/plugins/zcode/dist/src-ts/plugins/workbuddy/session-history.js +121 -0
- package/plugins/zcode/dist/src-ts/plugins/zcode/cdp-client.js +500 -0
- package/plugins/zcode/dist/src-ts/plugins/zcode/index.js +97 -0
- package/plugins/zcode/package.json +16 -0
- package/plugins/zcode/plugin.json +11 -0
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ZcodePlugin = exports.zcodeManifest = void 0;
|
|
4
|
+
const node_child_process_1 = require("node:child_process");
|
|
5
|
+
const agent_1 = require("../../agent");
|
|
6
|
+
const plugin_1 = require("../../plugin");
|
|
7
|
+
const cdp_client_1 = require("./cdp-client");
|
|
8
|
+
const workbuddy_1 = require("../workbuddy");
|
|
9
|
+
const desktop_launch_1 = require("../desktop-launch");
|
|
10
|
+
exports.zcodeManifest = {
|
|
11
|
+
id: "zcode", version: "1.0.8", displayName: "ZCode", type: "agent",
|
|
12
|
+
entry: "dist/src-ts/plugins/zcode/index.js", apiVersion: "1", process: "in-process",
|
|
13
|
+
capabilities: ["chat", "health"], configSchema: "config.schema.json",
|
|
14
|
+
};
|
|
15
|
+
class ZcodePlugin {
|
|
16
|
+
options;
|
|
17
|
+
manifest = exports.zcodeManifest;
|
|
18
|
+
delegate;
|
|
19
|
+
process;
|
|
20
|
+
constructor(options = {}) {
|
|
21
|
+
this.options = options;
|
|
22
|
+
}
|
|
23
|
+
get sessionOperations() { return this.delegate?.sessionOperations ?? []; }
|
|
24
|
+
async activate(context) {
|
|
25
|
+
const port = (0, plugin_1.resolveRuntimePort)(context.config, context.runtime, "zcode", "ZCODE_CDP_PORT", this.options.cdpPort ?? 9231);
|
|
26
|
+
const args = context.config.startupArgs ?? [];
|
|
27
|
+
if (!Array.isArray(args) || args.some(arg => typeof arg !== "string" || /^--remote-debugging-(?:port|address)(?:=|$)/i.test(arg)))
|
|
28
|
+
throw new Error("startupArgs 必须为字符串数组,CDP 地址和端口由插件配置管理");
|
|
29
|
+
const client = this.options.client ?? new cdp_client_1.ZcodeCdpClient({ port });
|
|
30
|
+
if (!this.options.client) {
|
|
31
|
+
const connect = client.connect.bind(client);
|
|
32
|
+
client.connect = async () => {
|
|
33
|
+
try {
|
|
34
|
+
await connect();
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
const failure = error;
|
|
38
|
+
if (context.config.autoStart === false || process.platform !== "win32" || failure.code !== "ECONNREFUSED" && failure.cause?.code !== "ECONNREFUSED")
|
|
39
|
+
throw error;
|
|
40
|
+
const executable = await (0, desktop_launch_1.findWindowsExecutable)("ZCode.exe", typeof context.config.executablePath === "string" ? context.config.executablePath : undefined);
|
|
41
|
+
if (!executable)
|
|
42
|
+
throw new Error("未找到 ZCode.exe,请检查开始菜单安装记录或配置 executablePath");
|
|
43
|
+
context.logger.info("正在启动 ZCode 并等待 CDP", { executable, port });
|
|
44
|
+
const env = { ...process.env };
|
|
45
|
+
delete env.ELECTRON_RUN_AS_NODE;
|
|
46
|
+
try {
|
|
47
|
+
await (0, desktop_launch_1.launchManagedAndWait)(executable, [...args, "--remote-debugging-address=127.0.0.1", `--remote-debugging-port=${port}`], undefined, connect, typeof context.config.startupTimeoutMs === "number" ? context.config.startupTimeoutMs : 30_000, child => { this.process = child; }, (file, argv) => (0, node_child_process_1.spawn)(file, argv, { env, detached: true, stdio: "ignore", windowsHide: false }));
|
|
48
|
+
}
|
|
49
|
+
catch (cause) {
|
|
50
|
+
const code = cause.code ?? (cause.cause?.code);
|
|
51
|
+
if (code === "ENOENT" || code === "EACCES")
|
|
52
|
+
throw new Error(`无法启动 ZCode:${cause instanceof Error ? cause.message : String(cause)}`, { cause });
|
|
53
|
+
throw new Error(`ZCode 已启动,但 CDP ${port} 未就绪;若桌面已打开,请先退出 ZCode,再重载插件使调试参数生效`, { cause });
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
this.delegate = new workbuddy_1.WorkbuddyPlugin({ client, expertSelection: false, replyTimeoutLimitMs: Infinity });
|
|
59
|
+
await this.delegate.activate({ ...context, config: { ...context.config, transport: "cdp", cdpPort: port, autoStart: false, dailyPoints: false } });
|
|
60
|
+
}
|
|
61
|
+
async deactivate(reason) {
|
|
62
|
+
try {
|
|
63
|
+
await this.delegate?.deactivate(reason);
|
|
64
|
+
}
|
|
65
|
+
finally {
|
|
66
|
+
const child = this.process;
|
|
67
|
+
this.process = undefined;
|
|
68
|
+
this.delegate = undefined;
|
|
69
|
+
await (0, desktop_launch_1.terminateOwnedProcess)(child);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
async health() {
|
|
73
|
+
return this.delegate ? this.delegate.health() : { status: "failed", message: "ZCode 插件尚未激活" };
|
|
74
|
+
}
|
|
75
|
+
async handle(request) {
|
|
76
|
+
if (!this.delegate)
|
|
77
|
+
throw new Error("ZCode 插件尚未激活");
|
|
78
|
+
try {
|
|
79
|
+
return await this.delegate.handle(request);
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
if (!(error instanceof agent_1.AgentOperationError) || error.code !== "WORKBUDDY_TASK_FAILED")
|
|
83
|
+
throw error;
|
|
84
|
+
const cause = error.cause;
|
|
85
|
+
const detail = cause instanceof Error && cause.message.includes("WorkBuddy")
|
|
86
|
+
? Object.assign(new Error(cause.message.replaceAll("WorkBuddy", "ZCode"), { cause: cause.cause }), { code: cause.code }) : cause;
|
|
87
|
+
throw new agent_1.AgentOperationError("ZCODE_TASK_FAILED", error.stage.replaceAll("WorkBuddy", "ZCode"), detail);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
async controlSession(request) {
|
|
91
|
+
if (!this.delegate)
|
|
92
|
+
throw new Error("ZCode 插件尚未激活");
|
|
93
|
+
return this.delegate.controlSession(request);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
exports.ZcodePlugin = ZcodePlugin;
|
|
97
|
+
exports.default = ZcodePlugin;
|
|
@@ -20,7 +20,7 @@ class SessionManagement {
|
|
|
20
20
|
bindings;
|
|
21
21
|
nativeProviders;
|
|
22
22
|
archive;
|
|
23
|
-
settings = { enabled: false, grants: [] };
|
|
23
|
+
settings = { enabled: false, allowFrom: [], grants: [] };
|
|
24
24
|
loaded;
|
|
25
25
|
writes = Promise.resolve();
|
|
26
26
|
snapshots = new Map();
|
|
@@ -41,12 +41,20 @@ class SessionManagement {
|
|
|
41
41
|
this.settings = this.validate(JSON.parse(await (0, promises_1.readFile)((0, node_path_1.join)(this.directory, "session-management.json"), "utf8")));
|
|
42
42
|
}
|
|
43
43
|
catch (error) {
|
|
44
|
-
if (error.code !== "ENOENT")
|
|
44
|
+
if (error.code !== "ENOENT") {
|
|
45
45
|
this.audit("配置读取", { actor: "local", count: 0, status: "invalid" });
|
|
46
|
+
throw new ManagementError("会话设置读取失败,请检查本机配置");
|
|
47
|
+
}
|
|
46
48
|
}
|
|
47
49
|
this.archive.setEnabled(this.settings.enabled);
|
|
48
50
|
})();
|
|
49
|
-
|
|
51
|
+
try {
|
|
52
|
+
await this.loaded;
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
this.loaded = undefined;
|
|
56
|
+
throw error;
|
|
57
|
+
}
|
|
50
58
|
}
|
|
51
59
|
async config() {
|
|
52
60
|
await this.load();
|
|
@@ -111,13 +119,18 @@ class SessionManagement {
|
|
|
111
119
|
if (revision !== this.revision)
|
|
112
120
|
throw new ManagementError("权限或归档设置已变更,请重新执行 /sessions list");
|
|
113
121
|
}
|
|
122
|
+
allowsAllHistory(context) {
|
|
123
|
+
return this.settings.allowFrom.length === 0 || this.settings.allowFrom.includes("*") || this.settings.allowFrom.includes(JSON.parse(context.source)[3]);
|
|
124
|
+
}
|
|
114
125
|
sources(context) {
|
|
126
|
+
if (this.allowsAllHistory(context))
|
|
127
|
+
return undefined;
|
|
115
128
|
const grant = this.settings.grants.find(item => item.managerSource === context.source && item.managerSessionId === context.sessionId);
|
|
116
129
|
return [...new Set([context.source, ...(grant?.sources ?? [])])];
|
|
117
130
|
}
|
|
118
131
|
async catalog(sources, agentPlugin) {
|
|
119
132
|
const result = await this.archive.snapshotSessions();
|
|
120
|
-
return { sessions: result.sessions.filter(item => sources.includes(item.source) && (!agentPlugin || item.agentPlugin === agentPlugin)), complete: result.complete };
|
|
133
|
+
return { sessions: result.sessions.filter(item => (!sources || sources.includes(item.source)) && (!agentPlugin || item.agentPlugin === agentPlugin)), complete: result.complete };
|
|
121
134
|
}
|
|
122
135
|
async summarizeToday(context, now = new Date()) {
|
|
123
136
|
await this.load();
|
|
@@ -463,6 +476,8 @@ class SessionManagement {
|
|
|
463
476
|
return `${header}\n${result.text}`;
|
|
464
477
|
}
|
|
465
478
|
authorizedNative(context, pluginId, scope) {
|
|
479
|
+
if (this.allowsAllHistory(context))
|
|
480
|
+
return;
|
|
466
481
|
const grant = this.settings.nativeGrants?.find(item => item.managerSource === context.source && item.managerSessionId === context.sessionId);
|
|
467
482
|
if (!grant?.providers.some(item => item.pluginId === pluginId && (scope === undefined || item.scope === scope)))
|
|
468
483
|
throw new ManagementError("无权读取该原生插件历史,请由本机管理员精确授权");
|
|
@@ -611,7 +626,7 @@ class SessionManagement {
|
|
|
611
626
|
const catalog = await this.catalog(sources, plugin);
|
|
612
627
|
const archived = catalog.sessions;
|
|
613
628
|
const known = new Set(archived.map(item => item.sessionId));
|
|
614
|
-
const bound = (await this.bindings?.list() ?? []).filter(item => sources.includes(item.source) && (!plugin || item.agentPlugin === plugin) && !known.has(item.logicalSessionId)).map(item => ({ sessionId: item.logicalSessionId, source: item.source, agentPlugin: item.agentPlugin, channel: JSON.parse(item.source)[0], updatedAt: item.updatedAt }));
|
|
629
|
+
const bound = (await this.bindings?.list() ?? []).filter(item => (!sources || sources.includes(item.source)) && (!plugin || item.agentPlugin === plugin) && !known.has(item.logicalSessionId)).map(item => ({ sessionId: item.logicalSessionId, source: item.source, agentPlugin: item.agentPlugin, channel: JSON.parse(item.source)[0], updatedAt: item.updatedAt }));
|
|
615
630
|
const sessions = [...archived, ...bound].filter(item => item.sessionId !== context.sessionId).sort((a, b) => b.updatedAt.localeCompare(a.updatedAt) || a.sessionId.localeCompare(b.sessionId));
|
|
616
631
|
this.checkRevision(revision);
|
|
617
632
|
const key = (0, node_crypto_1.randomUUID)().replace(/-/g, "").slice(0, 12);
|
|
@@ -636,7 +651,8 @@ class SessionManagement {
|
|
|
636
651
|
if (!snapshot || snapshot.revision !== this.revision || snapshot.expires < Date.now() || snapshot.context.source !== context.source || snapshot.context.sessionId !== context.sessionId)
|
|
637
652
|
throw new ManagementError("编号无效或已过期,请重新执行 /sessions list");
|
|
638
653
|
const session = snapshot.sessions[Number(match[2]) - 1];
|
|
639
|
-
|
|
654
|
+
const sources = this.sources(context);
|
|
655
|
+
if (!session || (sources && !sources.includes(session.source)) || session.sessionId === context.sessionId)
|
|
640
656
|
throw new ManagementError("无权读取所选会话,请重新列举");
|
|
641
657
|
return session;
|
|
642
658
|
}
|
|
@@ -644,8 +660,11 @@ class SessionManagement {
|
|
|
644
660
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
645
661
|
throw new ManagementError("归档设置必须是对象");
|
|
646
662
|
const input = value;
|
|
647
|
-
if (Object.keys(input).some(key => !["enabled", "grants", "nativeGrants", "summarizer"].includes(key)) || typeof input.enabled !== "boolean" || !Array.isArray(input.grants) || input.grants.length > 500)
|
|
663
|
+
if (Object.keys(input).some(key => !["enabled", "allowFrom", "grants", "nativeGrants", "summarizer"].includes(key)) || typeof input.enabled !== "boolean" || !Array.isArray(input.grants) || input.grants.length > 500)
|
|
648
664
|
throw new ManagementError("归档设置字段无效");
|
|
665
|
+
const allowFrom = input.allowFrom === undefined ? [] : input.allowFrom;
|
|
666
|
+
if (!Array.isArray(allowFrom) || allowFrom.length > 500 || allowFrom.some(value => value !== "*" && !exactIdentifier(value, 512)) || new Set(allowFrom).size !== allowFrom.length)
|
|
667
|
+
throw new ManagementError("allowFrom 来源白名单无效");
|
|
649
668
|
const managers = new Set();
|
|
650
669
|
const grants = input.grants.map((raw) => {
|
|
651
670
|
if (!raw || typeof raw !== "object" || Array.isArray(raw))
|
|
@@ -707,7 +726,7 @@ class SessionManagement {
|
|
|
707
726
|
const apiKey = config.apiKey === undefined && config.endpoint === previousEndpoint ? previousKey : config.apiKey;
|
|
708
727
|
summarizer = { endpoint: config.endpoint, model: config.model, ...(apiKey ? { apiKey } : {}) };
|
|
709
728
|
}
|
|
710
|
-
return { enabled: input.enabled, grants, ...(nativeGrants ? { nativeGrants } : {}), ...(summarizer ? { summarizer } : {}) };
|
|
729
|
+
return { enabled: input.enabled, allowFrom, grants, ...(nativeGrants ? { nativeGrants } : {}), ...(summarizer ? { summarizer } : {}) };
|
|
711
730
|
}
|
|
712
731
|
}
|
|
713
732
|
exports.SessionManagement = SessionManagement;
|
|
@@ -59,14 +59,17 @@ class VersionManager {
|
|
|
59
59
|
if (!parseVersion(currentVersion))
|
|
60
60
|
throw new Error("当前版本号无效");
|
|
61
61
|
const raw = await (this.options.npm ?? this.queryNpm)(["view", packageName, "version", "dist-tags", "--json"]);
|
|
62
|
-
const
|
|
63
|
-
const
|
|
62
|
+
const parsed = JSON.parse(raw);
|
|
63
|
+
const value = (Array.isArray(parsed) && parsed.length === 1 ? parsed[0] : parsed);
|
|
64
|
+
const latestVersion = typeof value === "string" ? value : String(value?.version ?? value?.latest ?? "");
|
|
64
65
|
if (!parseVersion(latestVersion))
|
|
65
66
|
throw new Error("npm 返回的版本号无效");
|
|
66
67
|
status = { packageName, currentVersion, latestVersion, state: compareVersions(latestVersion, currentVersion) > 0 ? "update-available" : "latest", checkedAt: new Date().toISOString() };
|
|
67
68
|
}
|
|
68
69
|
catch (error) {
|
|
69
|
-
|
|
70
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
71
|
+
const unpublished = /npm\s+error\s+code\s+E404\b|404\s+Not Found/i.test(message);
|
|
72
|
+
status = { packageName, currentVersion, state: unpublished ? "unpublished" : "error", error: unpublished ? "npm 包未发布或当前无权访问" : message, checkedAt: new Date().toISOString() };
|
|
70
73
|
}
|
|
71
74
|
this.statuses.set(packageName, status);
|
|
72
75
|
await this.save();
|
|
@@ -26,45 +26,48 @@ class Element {
|
|
|
26
26
|
showModal() { this.open = true; }
|
|
27
27
|
close() { this.open = false; }
|
|
28
28
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
29
|
+
for (const legacyWildcard of [false, true])
|
|
30
|
+
(0, node_test_1.default)(`ABWeb 个人助理模型设置保留配置,旧通配名单显示为空白(兼容模式:${legacyWildcard})`, async () => {
|
|
31
|
+
const script = await (0, promises_1.readFile)("src-ts/plugins/abweb/public/app.js", "utf8");
|
|
32
|
+
const nodes = new Map();
|
|
33
|
+
const node = (selector) => { if (!nodes.has(selector))
|
|
34
|
+
nodes.set(selector, new Element()); return nodes.get(selector); };
|
|
35
|
+
const calls = [];
|
|
36
|
+
const plugin = { id: "personal-assistant", type: "agent", displayName: "个人助理", status: "ready", version: "1.0.0", path: "/plugins/personal-assistant" };
|
|
37
|
+
const config = { modelProvider: "plugin", modelPlugin: "workbuddy", apiKey: "******", endpoint: "http://127.0.0.1:1234/v1/chat/completions", model: "local", allowedPeers: legacyWildcard ? ["*"] : ["peer.agentid.cn"], allowedAgents: legacyWildcard ? ["*"] : ["dsh"], ownerAid: "owner.agentid.cn", timeoutMs: 120000 };
|
|
38
|
+
(0, node_vm_1.runInNewContext)(script, {
|
|
39
|
+
document: { querySelector: node, createElement: () => new Element(), hidden: false },
|
|
40
|
+
localStorage: { getItem: () => null, setItem() { }, removeItem() { } },
|
|
41
|
+
window: { addEventListener() { } }, setInterval() { }, confirm: () => false, structuredClone,
|
|
42
|
+
fetch: async (path, init) => {
|
|
43
|
+
calls.push({ path, method: init.method, body: init.body ? JSON.parse(init.body) : undefined });
|
|
44
|
+
const data = path.endsWith("/config") ? config : path === "/api/model-providers" ? { providers: [
|
|
45
|
+
{ id: "workbuddy", displayName: "WorkBuddy", status: "failed", available: false, reason: "桌面未连接" },
|
|
46
|
+
{ id: "qwenwork", displayName: "千问办公", status: "ready", available: true, reason: "" },
|
|
47
|
+
] } : { plugins: [plugin], messages: [], sessions: [], mode: "auto-install", config: { providers: {}, plugins: {} } };
|
|
48
|
+
return { ok: true, status: 200, json: async () => data };
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
await new Promise(resolve => setImmediate(resolve));
|
|
52
|
+
const buttons = (element) => [element, ...element.children.flatMap(buttons)];
|
|
53
|
+
const configure = buttons(node("#plugins")).find(item => item.textContent === "配置模型");
|
|
54
|
+
strict_1.default.ok(configure, "个人助理应显示配置入口");
|
|
55
|
+
configure.onclick();
|
|
56
|
+
await new Promise(resolve => setImmediate(resolve));
|
|
57
|
+
strict_1.default.equal(node("#assistant-config-dialog").open, true);
|
|
58
|
+
strict_1.default.equal(node("#assistant-model-provider").value, "plugin:workbuddy");
|
|
59
|
+
const choices = node("#assistant-model-provider").children;
|
|
60
|
+
strict_1.default.ok(choices.some(option => option.value === "plugin:qwenwork" && !option.disabled));
|
|
61
|
+
strict_1.default.ok(choices.some(option => option.value === "plugin:workbuddy" && option.disabled && /桌面未连接/.test(option.textContent)));
|
|
62
|
+
strict_1.default.ok(choices.every(option => option.value !== "claude-cli"), "未安装的 Claude CLI 不应被硬编码为可用来源");
|
|
63
|
+
strict_1.default.equal(node("#assistant-api-key").value, "");
|
|
64
|
+
strict_1.default.equal(node("#assistant-allowed-peers").value, legacyWildcard ? "" : "peer.agentid.cn");
|
|
65
|
+
strict_1.default.equal(node("#assistant-allowed-agents").value, legacyWildcard ? "" : "dsh");
|
|
66
|
+
node("#assistant-model-provider").value = "plugin:qwenwork";
|
|
67
|
+
node("#assistant-config-form").onsubmit({ preventDefault() { } });
|
|
68
|
+
await new Promise(resolve => setImmediate(resolve));
|
|
69
|
+
const saved = calls.find(call => call.method === "PATCH" && call.path.endsWith("personal-assistant/config"));
|
|
70
|
+
strict_1.default.ok(saved);
|
|
71
|
+
strict_1.default.deepEqual(saved.body, { modelPlugin: "qwenwork", ...(legacyWildcard ? { allowedPeers: [], allowedAgents: [] } : {}) });
|
|
72
|
+
strict_1.default.ok(calls.some(call => call.path === "/api/model-providers"));
|
|
49
73
|
});
|
|
50
|
-
await new Promise(resolve => setImmediate(resolve));
|
|
51
|
-
const buttons = (element) => [element, ...element.children.flatMap(buttons)];
|
|
52
|
-
const configure = buttons(node("#plugins")).find(item => item.textContent === "配置模型");
|
|
53
|
-
strict_1.default.ok(configure, "个人助理应显示配置入口");
|
|
54
|
-
configure.onclick();
|
|
55
|
-
await new Promise(resolve => setImmediate(resolve));
|
|
56
|
-
strict_1.default.equal(node("#assistant-config-dialog").open, true);
|
|
57
|
-
strict_1.default.equal(node("#assistant-model-provider").value, "plugin:workbuddy");
|
|
58
|
-
const choices = node("#assistant-model-provider").children;
|
|
59
|
-
strict_1.default.ok(choices.some(option => option.value === "plugin:qwenwork" && !option.disabled));
|
|
60
|
-
strict_1.default.ok(choices.some(option => option.value === "plugin:workbuddy" && option.disabled && /桌面未连接/.test(option.textContent)));
|
|
61
|
-
strict_1.default.ok(choices.every(option => option.value !== "claude-cli"), "未安装的 Claude CLI 不应被硬编码为可用来源");
|
|
62
|
-
strict_1.default.equal(node("#assistant-api-key").value, "");
|
|
63
|
-
node("#assistant-model-provider").value = "plugin:qwenwork";
|
|
64
|
-
node("#assistant-config-form").onsubmit({ preventDefault() { } });
|
|
65
|
-
await new Promise(resolve => setImmediate(resolve));
|
|
66
|
-
const saved = calls.find(call => call.method === "PATCH" && call.path.endsWith("personal-assistant/config"));
|
|
67
|
-
strict_1.default.ok(saved);
|
|
68
|
-
strict_1.default.deepEqual(saved.body, { modelPlugin: "qwenwork" });
|
|
69
|
-
strict_1.default.ok(calls.some(call => call.path === "/api/model-providers"));
|
|
70
|
-
});
|
|
@@ -43,17 +43,20 @@ const sha256 = (0, node_crypto_1.createHash)("sha256").update(data).digest("hex"
|
|
|
43
43
|
strict_1.default.match(calls[0], /peer\.agentid\.cn/);
|
|
44
44
|
strict_1.default.match(calls[1], /limit[^0-9]*33554433/);
|
|
45
45
|
});
|
|
46
|
-
(0, node_test_1.default)("AUN
|
|
46
|
+
(0, node_test_1.default)("AUN 出站上传使用公开标准对象引用,不创建私有分享", async () => {
|
|
47
47
|
const calls = [];
|
|
48
48
|
const storage = {
|
|
49
49
|
async writeBytes(path, bytes, options) { calls.push(["write", { path, bytes, options }]); return { contentType: "text/plain", size_bytes: 3, sha256 }; },
|
|
50
50
|
lowlevel: { async createShareLink(options) { calls.push(["share", options]); return { owner_aid: "self.agentid.cn", bucket: "default", object_key: options.objectKey, share_id: "Ab3xK9mZ2q", path_url: `https://self.agentid.cn/${options.objectKey}?t=Ab3xK9mZ2q`, allowed_aids: options.allowedAids }; } },
|
|
51
51
|
};
|
|
52
52
|
const result = await (0, attachments_1.uploadAunAttachments)({ storage }, "self.agentid.cn", "peer.agentid.cn", [{ kind: "file", data, filename: "报告.txt", contentType: "text/plain" }]);
|
|
53
|
-
strict_1.default.
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
53
|
+
strict_1.default.deepEqual(result[0], {
|
|
54
|
+
owner_aid: "self.agentid.cn", object_key: (calls.find(([kind]) => kind === "write")?.[1]).path.slice(1),
|
|
55
|
+
filename: "报告.txt", content_type: "text/plain", size_bytes: 3, sha256,
|
|
56
|
+
});
|
|
57
|
+
strict_1.default.equal((calls.find(([kind]) => kind === "write")?.[1]).options.public, true);
|
|
58
|
+
strict_1.default.equal((calls.find(([kind]) => kind === "write")?.[1]).options.overwrite, true);
|
|
59
|
+
strict_1.default.equal(calls.filter(([kind]) => kind === "share").length, 0);
|
|
57
60
|
});
|
|
58
61
|
(0, node_test_1.default)("AUN 视频上传遵循 EvolCore 标准对象引用,不创建私有分享", async () => {
|
|
59
62
|
const writes = [];
|
|
@@ -66,14 +69,14 @@ const sha256 = (0, node_crypto_1.createHash)("sha256").update(data).digest("hex"
|
|
|
66
69
|
strict_1.default.deepEqual(writes[0].options, { contentType: "video/mp4", public: true, overwrite: true });
|
|
67
70
|
strict_1.default.deepEqual(result, [{ owner_aid: "self.agentid.cn", object_key: writes[0].path.slice(1), filename: "视频.mp4", content_type: "video/mp4", size_bytes: 3, sha256 }]);
|
|
68
71
|
});
|
|
69
|
-
(0, node_test_1.default)("AUN
|
|
70
|
-
let
|
|
72
|
+
(0, node_test_1.default)("AUN 标准附件上传失败时不会创建分享", async () => {
|
|
73
|
+
let shareCalls = 0;
|
|
71
74
|
const storage = {
|
|
72
|
-
async writeBytes(
|
|
73
|
-
lowlevel: { async createShareLink() { throw new Error("
|
|
75
|
+
async writeBytes() { throw new Error("存储不可用"); },
|
|
76
|
+
lowlevel: { async createShareLink() { shareCalls++; throw new Error("不应创建分享"); } },
|
|
74
77
|
};
|
|
75
|
-
await strict_1.default.rejects(() => (0, attachments_1.uploadAunAttachments)({ storage }, "self.agentid.cn", "peer.agentid.cn", [{ kind: "file", data, filename: "a.txt", contentType: "text/plain" }]),
|
|
76
|
-
strict_1.default.equal(
|
|
78
|
+
await strict_1.default.rejects(() => (0, attachments_1.uploadAunAttachments)({ storage }, "self.agentid.cn", "peer.agentid.cn", [{ kind: "file", data, filename: "a.txt", contentType: "text/plain" }]), /上传 AUN FS 文件/);
|
|
79
|
+
strict_1.default.equal(shareCalls, 0);
|
|
77
80
|
});
|
|
78
81
|
(0, node_test_1.default)("AUN 元数据与正文的哈希不符时拒绝入站附件", async () => {
|
|
79
82
|
const storage = { async stat() { return { size: 3, sha256: "0".repeat(64), contentType: "text/plain", type: "file" }; }, async readBytes() { return data; } };
|
|
@@ -86,12 +89,11 @@ const sha256 = (0, node_crypto_1.createHash)("sha256").update(data).digest("hex"
|
|
|
86
89
|
await strict_1.default.rejects(() => (0, attachments_1.downloadAunAttachment)({ storage }, ref), /引用|路径/);
|
|
87
90
|
strict_1.default.equal(reads, 0);
|
|
88
91
|
});
|
|
89
|
-
(0, node_test_1.default)("AUN
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
await strict_1.default.rejects(() => (0, attachments_1.uploadAunAttachments)(client, "self.agentid.cn", "peer.agentid.cn", [{ kind: "file", data, filename: "a.txt", contentType: "text/plain" }]), /分享/);
|
|
92
|
+
(0, node_test_1.default)("AUN 标准附件拒绝上传结果中的错配对象或哈希", async () => {
|
|
93
|
+
for (const metadata of [{ owner_aid: "other.agentid.cn" }, { object_key: "shared/other.txt" }, { sha256: "0".repeat(64) }]) {
|
|
94
|
+
const client = { storage: { async writeBytes() { return { size: 3, sha256, contentType: "text/plain", ...metadata }; } } };
|
|
95
|
+
await strict_1.default.rejects(() => (0, attachments_1.uploadAunAttachments)(client, "self.agentid.cn", "peer.agentid.cn", [{ kind: "file", data, filename: "a.txt", contentType: "text/plain" }]), /引用|哈希/);
|
|
96
|
+
}
|
|
95
97
|
});
|
|
96
98
|
(0, node_test_1.default)("AUN 通用图片类型按普通文件传入", async () => {
|
|
97
99
|
const storage = { async stat() { return { size: 3 }; }, async readBytes() { return data; } };
|
|
@@ -51,7 +51,7 @@ const flush = () => new Promise((resolve) => setImmediate(resolve));
|
|
|
51
51
|
posts.push(JSON.parse(options.body));
|
|
52
52
|
return { ok: true, json: async () => ({ status: "已创建", connection: "ready" }) };
|
|
53
53
|
}
|
|
54
|
-
const data = path === "/api/plugins/aun/config" ? { owner_aid: owner }
|
|
54
|
+
const data = path === "/api/plugins/aun/config" ? { owner_aid: owner, allow_from: "*" }
|
|
55
55
|
: path === "/api/plugins/aun/identities" ? { groups: { muAgentBridge: [], evolcore: [] } }
|
|
56
56
|
: { plugins: [] };
|
|
57
57
|
return { ok: true, json: async () => data };
|
|
@@ -60,6 +60,7 @@ const flush = () => new Promise((resolve) => setImmediate(resolve));
|
|
|
60
60
|
const create = node("#create-form");
|
|
61
61
|
await flush();
|
|
62
62
|
strict_1.default.equal(create.elements.ownerAid.value, "first.agentid.cn");
|
|
63
|
+
strict_1.default.equal(node("#config-form").elements.allow_from.value, "", "旧通配名单显示为空白,沿用接受所有来源的语义");
|
|
63
64
|
owner = "second.agentid.cn";
|
|
64
65
|
node("#refresh").onclick();
|
|
65
66
|
await flush();
|
|
@@ -14,6 +14,13 @@ const messages_1 = require("../src-ts/plugins/aun/messages");
|
|
|
14
14
|
peerId: "other.agentid.cn", messageId: "m1", text: "你好", threadId: "t1",
|
|
15
15
|
});
|
|
16
16
|
});
|
|
17
|
+
(0, node_test_1.default)("AUN 来源白名单使用 * 时接受所有 Peer", () => {
|
|
18
|
+
strict_1.default.equal((0, messages_1.normalizeAunMessage)({ from: "peer.agentid.cn", payload: { text: "你好" } }, "self.agentid.cn", "*")?.peerId, "peer.agentid.cn");
|
|
19
|
+
});
|
|
20
|
+
(0, node_test_1.default)("AUN 来源白名单可用逗号分隔限制来源,包含 * 时仍接受所有 Peer", () => {
|
|
21
|
+
strict_1.default.equal((0, messages_1.normalizeAunMessage)({ from: "peer.agentid.cn", payload: { text: "你好" } }, "self.agentid.cn", "other.agentid.cn, *")?.peerId, "peer.agentid.cn");
|
|
22
|
+
strict_1.default.equal((0, messages_1.normalizeAunMessage)({ from: "peer.agentid.cn", payload: { text: "你好" } }, "self.agentid.cn", "other.agentid.cn"), undefined);
|
|
23
|
+
});
|
|
17
24
|
(0, node_test_1.default)("AUN 错误和通知事件不作为任务触发自动互答", () => {
|
|
18
25
|
for (const type of ["error", "notice"]) {
|
|
19
26
|
strict_1.default.equal((0, messages_1.normalizeAunMessage)({ from: "peer.agentid.cn", payload: { type, text: "今日额度已用完", thread_id: "test-thread" } }, "self.agentid.cn"), undefined);
|
|
@@ -67,3 +74,10 @@ const messages_1 = require("../src-ts/plugins/aun/messages");
|
|
|
67
74
|
strict_1.default.equal(message?.attachments?.[0].ownerAid, "third.agentid.cn");
|
|
68
75
|
strict_1.default.equal(message?.attachments?.[0].token, "Ab3xK9mZ2q");
|
|
69
76
|
});
|
|
77
|
+
(0, node_test_1.default)("AUN 来源留空或未配置时接受所有 Peer,仍忽略自身与空来源", () => {
|
|
78
|
+
for (const rule of [undefined, "", " "]) {
|
|
79
|
+
strict_1.default.equal((0, messages_1.normalizeAunMessage)({ from: "peer.agentid.cn", payload: { text: "你好" } }, "self.agentid.cn", rule)?.peerId, "peer.agentid.cn");
|
|
80
|
+
strict_1.default.equal((0, messages_1.normalizeAunMessage)({ from: "self.agentid.cn", payload: { text: "你好" } }, "self.agentid.cn", rule), undefined);
|
|
81
|
+
strict_1.default.equal((0, messages_1.normalizeAunMessage)({ from: "", payload: { text: "你好" } }, "self.agentid.cn", rule), undefined);
|
|
82
|
+
}
|
|
83
|
+
});
|