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.
Files changed (152) hide show
  1. package/README.md +12 -8
  2. package/dist/src-ts/cli.js +67 -37
  3. package/dist/src-ts/control-server.js +7 -28
  4. package/dist/src-ts/core-updater.js +141 -33
  5. package/dist/src-ts/daemon-process.js +2 -1
  6. package/dist/src-ts/dsh-agent.js +7 -2
  7. package/dist/src-ts/http-agent.js +36 -0
  8. package/dist/src-ts/plugin-manager.js +185 -58
  9. package/dist/src-ts/plugins/abweb/index.js +1 -1
  10. package/dist/src-ts/plugins/abweb/public/app.js +8 -6
  11. package/dist/src-ts/plugins/abweb/public/index.html +3 -3
  12. package/dist/src-ts/plugins/aun/attachments.js +3 -21
  13. package/dist/src-ts/plugins/aun/index.js +89 -11
  14. package/dist/src-ts/plugins/aun/messages.js +1 -1
  15. package/dist/src-ts/plugins/aun/public/app.js +2 -1
  16. package/dist/src-ts/plugins/aun/public/index.html +1 -1
  17. package/dist/src-ts/plugins/cdp-draft.js +5 -2
  18. package/dist/src-ts/plugins/chatgpt/index.js +3 -2
  19. package/dist/src-ts/plugins/claude-cli/index.js +1 -1
  20. package/dist/src-ts/plugins/desktop-launch.js +44 -3
  21. package/dist/src-ts/plugins/doubao/cdp-client.js +231 -45
  22. package/dist/src-ts/plugins/doubao/index.js +119 -7
  23. package/dist/src-ts/plugins/doubao/native-task.js +134 -0
  24. package/dist/src-ts/plugins/dsh/cdp-client.js +334 -0
  25. package/dist/src-ts/plugins/dsh/index.js +102 -23
  26. package/dist/src-ts/plugins/dsh/session-history.js +31 -3
  27. package/dist/src-ts/plugins/feishu/index.js +1 -1
  28. package/dist/src-ts/plugins/personal-assistant/index.js +29 -8
  29. package/dist/src-ts/plugins/personal-assistant/model-client.js +20 -6
  30. package/dist/src-ts/plugins/personal-assistant/session-memory.js +124 -0
  31. package/dist/src-ts/plugins/personal-assistant/tools.js +4 -3
  32. package/dist/src-ts/plugins/qwenwork/cdp-client.js +43 -7
  33. package/dist/src-ts/plugins/qwenwork/index.js +52 -18
  34. package/dist/src-ts/plugins/workbuddy/cdp-client.js +349 -83
  35. package/dist/src-ts/plugins/workbuddy/index.js +24 -12
  36. package/dist/src-ts/plugins/zcode/cdp-client.js +500 -0
  37. package/dist/src-ts/plugins/zcode/index.js +97 -0
  38. package/dist/src-ts/session-management.js +27 -8
  39. package/dist/src-ts/version-manager.js +6 -3
  40. package/dist/tests-ts/abweb-model-config.test.js +44 -41
  41. package/dist/tests-ts/aun-attachments.test.js +19 -17
  42. package/dist/tests-ts/aun-default-owner-page.test.js +2 -1
  43. package/dist/tests-ts/aun-messages.test.js +14 -0
  44. package/dist/tests-ts/aun-plugin.test.js +207 -19
  45. package/dist/tests-ts/bundled-package.test.js +1 -1
  46. package/dist/tests-ts/cdp-draft-recovery.test.js +182 -3
  47. package/dist/tests-ts/chatgpt-plugin.test.js +19 -0
  48. package/dist/tests-ts/control-server.test.js +20 -0
  49. package/dist/tests-ts/core-updater.test.js +215 -46
  50. package/dist/tests-ts/daemon-process.test.js +126 -1
  51. package/dist/tests-ts/desktop-launch.test.js +73 -0
  52. package/dist/tests-ts/doubao-native-task.test.js +237 -0
  53. package/dist/tests-ts/doubao-plugin.test.js +688 -19
  54. package/dist/tests-ts/dsh-agent.test.js +91 -19
  55. package/dist/tests-ts/dsh-cdp.test.js +148 -0
  56. package/dist/tests-ts/dsh-history.test.js +37 -0
  57. package/dist/tests-ts/feishu-plugin.test.js +1 -1
  58. package/dist/tests-ts/http-agent.test.js +18 -0
  59. package/dist/tests-ts/local-cli.test.js +29 -2
  60. package/dist/tests-ts/model-provider-cancellation.test.js +2 -0
  61. package/dist/tests-ts/native-session-management.test.js +8 -7
  62. package/dist/tests-ts/personal-assistant-coordination.test.js +16 -0
  63. package/dist/tests-ts/personal-assistant-extensions.test.js +7 -1
  64. package/dist/tests-ts/personal-assistant-memory.test.js +117 -0
  65. package/dist/tests-ts/personal-assistant.test.js +277 -3
  66. package/dist/tests-ts/platform-integration.test.js +60 -1
  67. package/dist/tests-ts/plugin-manager.test.js +78 -6
  68. package/dist/tests-ts/plugin-upgrade-deadline.test.js +354 -0
  69. package/dist/tests-ts/publish-packages.test.js +1 -1
  70. package/dist/tests-ts/qwenwork-plugin.test.js +30 -0
  71. package/dist/tests-ts/qwenwork-reconnect.test.js +195 -0
  72. package/dist/tests-ts/qwenwork-sse.test.js +33 -0
  73. package/dist/tests-ts/session-access-defaults.test.js +112 -0
  74. package/dist/tests-ts/session-management.test.js +8 -8
  75. package/dist/tests-ts/version-manager.test.js +40 -0
  76. package/dist/tests-ts/workbuddy-cdp-recovery.test.js +311 -0
  77. package/dist/tests-ts/workbuddy-plugin.test.js +356 -13
  78. package/dist/tests-ts/zcode-cdp.test.js +894 -0
  79. package/dist/tests-ts/zcode-plugin.test.js +396 -0
  80. package/package.json +1 -1
  81. package/plugins/abweb/dist/src-ts/plugins/abweb/index.js +1 -1
  82. package/plugins/abweb/dist/src-ts/plugins/abweb/public/app.js +8 -6
  83. package/plugins/abweb/dist/src-ts/plugins/abweb/public/app.ts +7 -5
  84. package/plugins/abweb/dist/src-ts/plugins/abweb/public/index.html +3 -3
  85. package/plugins/abweb/package.json +1 -1
  86. package/plugins/abweb/plugin.json +1 -1
  87. package/plugins/aun/config.schema.json +1 -1
  88. package/plugins/aun/dist/src-ts/plugins/aun/attachments.js +3 -21
  89. package/plugins/aun/dist/src-ts/plugins/aun/index.js +89 -11
  90. package/plugins/aun/dist/src-ts/plugins/aun/messages.js +1 -1
  91. package/plugins/aun/dist/src-ts/plugins/aun/public/app.js +2 -1
  92. package/plugins/aun/dist/src-ts/plugins/aun/public/index.html +1 -1
  93. package/plugins/aun/package.json +1 -1
  94. package/plugins/aun/plugin.json +1 -1
  95. package/plugins/chatgpt/dist/src-ts/plugins/cdp-draft.js +5 -2
  96. package/plugins/chatgpt/dist/src-ts/plugins/chatgpt/index.js +3 -2
  97. package/plugins/chatgpt/dist/src-ts/plugins/desktop-launch.js +44 -3
  98. package/plugins/claude-cli/dist/src-ts/plugins/claude-cli/index.js +1 -1
  99. package/plugins/claude-cli/package.json +1 -1
  100. package/plugins/claude-cli/plugin.json +1 -1
  101. package/plugins/doubao/config.schema.json +2 -1
  102. package/plugins/doubao/dist/src-ts/plugins/desktop-launch.js +44 -3
  103. package/plugins/doubao/dist/src-ts/plugins/doubao/cdp-client.js +231 -45
  104. package/plugins/doubao/dist/src-ts/plugins/doubao/index.js +119 -7
  105. package/plugins/doubao/dist/src-ts/plugins/doubao/native-task.js +134 -0
  106. package/plugins/doubao/package.json +1 -1
  107. package/plugins/doubao/plugin.json +1 -1
  108. package/plugins/dsh/config.schema.json +5 -2
  109. package/plugins/dsh/dist/src-ts/dsh-agent.js +7 -2
  110. package/plugins/dsh/dist/src-ts/plugins/desktop-launch.js +44 -3
  111. package/plugins/dsh/dist/src-ts/plugins/dsh/cdp-client.js +334 -0
  112. package/plugins/dsh/dist/src-ts/plugins/dsh/index.js +102 -23
  113. package/plugins/dsh/dist/src-ts/plugins/dsh/session-history.js +31 -3
  114. package/plugins/dsh/package.json +1 -1
  115. package/plugins/dsh/plugin.json +1 -1
  116. package/plugins/feishu/dist/src-ts/plugins/feishu/index.js +1 -1
  117. package/plugins/feishu/package.json +1 -1
  118. package/plugins/feishu/plugin.json +1 -1
  119. package/plugins/personal-assistant/config.schema.json +3 -2
  120. package/plugins/personal-assistant/dist/src-ts/attachments.js +62 -0
  121. package/plugins/personal-assistant/dist/src-ts/plugins/personal-assistant/index.js +29 -8
  122. package/plugins/personal-assistant/dist/src-ts/plugins/personal-assistant/model-client.js +20 -6
  123. package/plugins/personal-assistant/dist/src-ts/plugins/personal-assistant/session-memory.js +124 -0
  124. package/plugins/personal-assistant/dist/src-ts/plugins/personal-assistant/tools.js +4 -3
  125. package/plugins/personal-assistant/package.json +2 -2
  126. package/plugins/personal-assistant/plugin.json +1 -1
  127. package/plugins/qwenwork/dist/src-ts/plugins/cdp-draft.js +5 -2
  128. package/plugins/qwenwork/dist/src-ts/plugins/desktop-launch.js +44 -3
  129. package/plugins/qwenwork/dist/src-ts/plugins/qwenwork/cdp-client.js +43 -7
  130. package/plugins/qwenwork/dist/src-ts/plugins/qwenwork/index.js +52 -18
  131. package/plugins/qwenwork/package.json +1 -1
  132. package/plugins/qwenwork/plugin.json +1 -1
  133. package/plugins/workbuddy/config.schema.json +1 -1
  134. package/plugins/workbuddy/dist/src-ts/plugins/desktop-launch.js +44 -3
  135. package/plugins/workbuddy/dist/src-ts/plugins/workbuddy/cdp-client.js +349 -83
  136. package/plugins/workbuddy/dist/src-ts/plugins/workbuddy/index.js +24 -12
  137. package/plugins/workbuddy/package.json +1 -1
  138. package/plugins/workbuddy/plugin.json +1 -1
  139. package/plugins/zcode/config.schema.json +13 -0
  140. package/plugins/zcode/dist/src-ts/agent.js +69 -0
  141. package/plugins/zcode/dist/src-ts/attachments.js +62 -0
  142. package/plugins/zcode/dist/src-ts/plugin.js +13 -0
  143. package/plugins/zcode/dist/src-ts/plugins/desktop-launch.js +238 -0
  144. package/plugins/zcode/dist/src-ts/plugins/workbuddy/cdp-client.js +896 -0
  145. package/plugins/zcode/dist/src-ts/plugins/workbuddy/cli-client.js +39 -0
  146. package/plugins/zcode/dist/src-ts/plugins/workbuddy/desktop-launch.js +10 -0
  147. package/plugins/zcode/dist/src-ts/plugins/workbuddy/index.js +567 -0
  148. package/plugins/zcode/dist/src-ts/plugins/workbuddy/session-history.js +121 -0
  149. package/plugins/zcode/dist/src-ts/plugins/zcode/cdp-client.js +500 -0
  150. package/plugins/zcode/dist/src-ts/plugins/zcode/index.js +97 -0
  151. package/plugins/zcode/package.json +16 -0
  152. 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
- await this.loaded;
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
- if (!session || !this.sources(context).includes(session.source) || session.sessionId === context.sessionId)
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 value = JSON.parse(raw);
63
- const latestVersion = typeof value === "string" ? value : String(value.version ?? value.latest ?? "");
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
- status = { packageName, currentVersion, state: "error", error: error instanceof Error ? error.message : String(error), checkedAt: new Date().toISOString() };
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
- (0, node_test_1.default)("ABWeb 个人助理动态列出模型来源,保留不可用选择及脱敏密钥和隐藏配置", async () => {
30
- const script = await (0, promises_1.readFile)("src-ts/plugins/abweb/public/app.js", "utf8");
31
- const nodes = new Map();
32
- const node = (selector) => { if (!nodes.has(selector))
33
- nodes.set(selector, new Element()); return nodes.get(selector); };
34
- const calls = [];
35
- const plugin = { id: "personal-assistant", type: "agent", displayName: "个人助理", status: "ready", version: "1.0.0", path: "/plugins/personal-assistant" };
36
- const config = { modelProvider: "plugin", modelPlugin: "workbuddy", apiKey: "******", endpoint: "http://127.0.0.1:1234/v1/chat/completions", model: "local", allowedPeers: ["peer.agentid.cn"], allowedAgents: ["dsh"], ownerAid: "owner.agentid.cn", timeoutMs: 120000 };
37
- (0, node_vm_1.runInNewContext)(script, {
38
- document: { querySelector: node, createElement: () => new Element(), hidden: false },
39
- localStorage: { getItem: () => null, setItem() { }, removeItem() { } },
40
- window: { addEventListener() { } }, setInterval() { }, confirm: () => false, structuredClone,
41
- fetch: async (path, init) => {
42
- calls.push({ path, method: init.method, body: init.body ? JSON.parse(init.body) : undefined });
43
- const data = path.endsWith("/config") ? config : path === "/api/model-providers" ? { providers: [
44
- { id: "workbuddy", displayName: "WorkBuddy", status: "failed", available: false, reason: "桌面未连接" },
45
- { id: "qwenwork", displayName: "千问办公", status: "ready", available: true, reason: "" },
46
- ] } : { plugins: [plugin], messages: [], sessions: [], mode: "auto-install", config: { providers: {}, plugins: {} } };
47
- return { ok: true, status: 200, json: async () => data };
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 出站上传保持私有并按对端创建 24 小时分享", async () => {
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.equal(result[0].url, "https://self.agentid.cn/" + result[0].object_key + "?t=Ab3xK9mZ2q");
54
- strict_1.default.deepEqual((calls.find(([kind]) => kind === "share")?.[1]).allowedAids, ["peer.agentid.cn"]);
55
- strict_1.default.equal((calls.find(([kind]) => kind === "share")?.[1]).expireInSeconds, 86400);
56
- strict_1.default.equal((calls.find(([kind]) => kind === "write")?.[1]).options.public, false);
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 分享失败不会回退为公开上传", async () => {
70
- let writeOptions;
72
+ (0, node_test_1.default)("AUN 标准附件上传失败时不会创建分享", async () => {
73
+ let shareCalls = 0;
71
74
  const storage = {
72
- async writeBytes(_path, _bytes, options) { writeOptions = options; return {}; },
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(writeOptions?.public, false);
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 私有分享拒绝服务端返回的公开授权或不匹配元数据", async () => {
90
- const client = { storage: {
91
- async writeBytes() { return { size: 3, sha256, contentType: "text/plain" }; },
92
- lowlevel: { async createShareLink(options) { return { object_key: options.objectKey, share_id: "Ab3xK9mZ2q", allowed_aids: ["*"], path_url: `https://self.agentid.cn/${options.objectKey}?t=Ab3xK9mZ2q` }; } },
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
+ });