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,195 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const strict_1 = __importDefault(require("node:assert/strict"));
7
+ const node_test_1 = __importDefault(require("node:test"));
8
+ const qwenwork_1 = require("../src-ts/plugins/qwenwork");
9
+ for (const structured of [true, false]) {
10
+ (0, node_test_1.default)(`千问${structured ? "结构化" : "纯文本"}请求提交前重连,排队请求仅各发送一次`, async () => {
11
+ let connected = true;
12
+ const calls = [];
13
+ const response = { text: "回复", sessionId: "桌面会话" };
14
+ const client = {
15
+ connect: async () => { calls.push("connect"); connected = true; },
16
+ close() { calls.push("close"); connected = false; }, connected: () => connected,
17
+ send: async () => { throw new Error("不得额外发送消息"); },
18
+ direct: async (text) => { calls.push(text); return response.text; },
19
+ ...(structured ? { complete: async (text) => { calls.push(text); return response; } } : {}),
20
+ healthCheck: async () => { throw new Error("不得探测页面"); },
21
+ };
22
+ const plugin = new qwenwork_1.QwenworkPlugin({ client, httpPort: 0, healthIntervalMs: 60_000,
23
+ restartDesktop: async () => { throw new Error("不得重启桌面"); } });
24
+ await plugin.activate({ config: {}, dataDir: "." });
25
+ try {
26
+ await plugin.requestQueue;
27
+ connected = false;
28
+ const replies = await Promise.all([plugin.handle({ task: "第一条" }), plugin.handle({ task: "第二条" })]);
29
+ strict_1.default.deepEqual(replies, structured ? [response, response] : [{ text: "回复" }, { text: "回复" }]);
30
+ strict_1.default.deepEqual(calls, ["connect", "close", "connect", "第一条", "第二条"]);
31
+ strict_1.default.equal((await plugin.health()).status, "ready");
32
+ }
33
+ finally {
34
+ await plugin.deactivate("stop");
35
+ }
36
+ });
37
+ }
38
+ for (const fails of [true, false]) {
39
+ (0, node_test_1.default)(`千问请求重连${fails ? "报错时保留端口与错误码" : "未建立连接时拒绝提交"}`, async () => {
40
+ let connected = true;
41
+ let connects = 0;
42
+ let sends = 0;
43
+ const error = new Error("无法连接千问办公 CDP 端口 9228:ECONNREFUSED");
44
+ const plugin = new qwenwork_1.QwenworkPlugin({ httpPort: 0, healthIntervalMs: 60_000,
45
+ restartDesktop: async () => { throw new Error("不得重启桌面"); }, client: {
46
+ connect: async () => { if (++connects > 1) {
47
+ if (fails)
48
+ throw error;
49
+ return;
50
+ } connected = true; },
51
+ close() { connected = false; }, connected: () => connected,
52
+ send: async () => undefined, direct: async () => { sends++; return "回复"; },
53
+ } });
54
+ await plugin.activate({ config: {}, dataDir: "." });
55
+ try {
56
+ await plugin.requestQueue;
57
+ connected = false;
58
+ await strict_1.default.rejects(plugin.handle({ task: "不得发送" }), fails ? /9228.*ECONNREFUSED/ : /连接恢复失败/);
59
+ strict_1.default.equal(connects, 2);
60
+ strict_1.default.equal(sends, 0);
61
+ strict_1.default.match((await plugin.health()).message, fails ? /9228.*ECONNREFUSED/ : /连接恢复失败/);
62
+ }
63
+ finally {
64
+ await plugin.deactivate("stop");
65
+ }
66
+ });
67
+ }
68
+ (0, node_test_1.default)("千问提交后断线不重发,下一条独立请求可重连", async () => {
69
+ let connected = true;
70
+ let connects = 0;
71
+ const tasks = [];
72
+ const plugin = new qwenwork_1.QwenworkPlugin({ httpPort: 0, healthIntervalMs: 60_000, client: {
73
+ connect: async () => { connects++; connected = true; }, close() { connected = false; }, connected: () => connected,
74
+ send: async () => undefined, direct: async (text) => {
75
+ tasks.push(text);
76
+ if (tasks.length === 1) {
77
+ connected = false;
78
+ throw new Error("提交后 CDP 断开");
79
+ }
80
+ return "新请求回复";
81
+ },
82
+ } });
83
+ await plugin.activate({ config: {}, dataDir: "." });
84
+ try {
85
+ await strict_1.default.rejects(plugin.handle({ task: "可能已执行" }), /提交后 CDP 断开/);
86
+ strict_1.default.deepEqual(tasks, ["可能已执行"]);
87
+ strict_1.default.equal(connects, 1);
88
+ strict_1.default.deepEqual(await plugin.handle({ task: "独立的新请求" }), { text: "新请求回复" });
89
+ strict_1.default.deepEqual(tasks, ["可能已执行", "独立的新请求"]);
90
+ strict_1.default.equal(connects, 2);
91
+ }
92
+ finally {
93
+ await plugin.deactivate("stop");
94
+ }
95
+ });
96
+ (0, node_test_1.default)("千问 /new 在创建会话前重连,不发送聊天消息", async () => {
97
+ let connected = true;
98
+ let connects = 0;
99
+ const names = [];
100
+ const plugin = new qwenwork_1.QwenworkPlugin({ httpPort: 0, healthIntervalMs: 60_000, client: {
101
+ connect: async () => { connects++; connected = true; }, close() { connected = false; }, connected: () => connected,
102
+ send: async () => { throw new Error("不得发送聊天消息"); }, direct: async () => { throw new Error("不得发送聊天消息"); },
103
+ createSession: async (name) => { names.push(name); },
104
+ } });
105
+ await plugin.activate({ config: {}, dataDir: "." });
106
+ try {
107
+ await plugin.requestQueue;
108
+ connected = false;
109
+ strict_1.default.deepEqual(await plugin.controlSession({ operation: "new", sessionId: "新会话", name: "测试" }), { text: "千问办公已创建新会话", sessionId: "新会话" });
110
+ strict_1.default.equal(connects, 2);
111
+ strict_1.default.deepEqual(names, ["测试"]);
112
+ }
113
+ finally {
114
+ await plugin.deactivate("stop");
115
+ }
116
+ });
117
+ for (const disconnectOnCreate of [false, true]) {
118
+ (0, node_test_1.default)(`千问模型在${disconnectOnCreate ? "准备会话后" : "排队前"}断线时不重连或提交`, async () => {
119
+ let connected = true;
120
+ let connects = 0;
121
+ let creates = 0;
122
+ let sends = 0;
123
+ const plugin = new qwenwork_1.QwenworkPlugin({ httpPort: 0, healthIntervalMs: 60_000, client: {
124
+ connect: async () => { connects++; connected = true; }, close() { connected = false; }, connected: () => connected,
125
+ send: async () => undefined, direct: async () => "",
126
+ createSession: async () => { creates++; connected = false; return "模型会话"; }, activateSession: async () => undefined,
127
+ complete: async () => { sends++; return { text: "不得发送" }; },
128
+ } });
129
+ await plugin.activate({ config: {}, dataDir: "." });
130
+ try {
131
+ await plugin.requestQueue;
132
+ connected = disconnectOnCreate;
133
+ await strict_1.default.rejects(plugin.modelProvider.complete({ prompt: "模型请求", sessionId: "模型会话", timeoutMs: 1000, signal: new AbortController().signal }), /未连接|CDP 已断开/);
134
+ strict_1.default.equal(connects, 1);
135
+ strict_1.default.equal(creates, disconnectOnCreate ? 1 : 0);
136
+ strict_1.default.equal(sends, 0);
137
+ }
138
+ finally {
139
+ await plugin.deactivate("stop");
140
+ }
141
+ });
142
+ }
143
+ for (const cancelDuringConnect of [false, true]) {
144
+ (0, node_test_1.default)(`千问请求在${cancelDuringConnect ? "重连中" : "排队前"}取消后不提交`, async () => {
145
+ let connected = true;
146
+ let connects = 0;
147
+ let sends = 0;
148
+ const controller = new AbortController();
149
+ const plugin = new qwenwork_1.QwenworkPlugin({ httpPort: 0, healthIntervalMs: 60_000, client: {
150
+ connect: async () => { if (++connects > 1)
151
+ controller.abort(new Error("用户取消")); connected = true; },
152
+ close() { connected = false; }, connected: () => connected,
153
+ send: async () => undefined, direct: async () => { sends++; return "不得发送"; },
154
+ } });
155
+ await plugin.activate({ config: {}, dataDir: "." });
156
+ try {
157
+ await plugin.requestQueue;
158
+ connected = false;
159
+ if (!cancelDuringConnect)
160
+ controller.abort(new Error("用户取消"));
161
+ await strict_1.default.rejects(plugin.handle({ task: "取消请求", signal: controller.signal }), /用户取消/);
162
+ strict_1.default.equal(connects, cancelDuringConnect ? 2 : 1);
163
+ strict_1.default.equal(sends, 0);
164
+ }
165
+ finally {
166
+ await plugin.deactivate("stop");
167
+ }
168
+ });
169
+ }
170
+ (0, node_test_1.default)("千问重连期间停用后不提交,也不恢复就绪状态", async () => {
171
+ let connected = true;
172
+ let connects = 0;
173
+ let sends = 0;
174
+ let stopping;
175
+ const plugin = new qwenwork_1.QwenworkPlugin({ httpPort: 0, healthIntervalMs: 60_000, client: {
176
+ connect: async () => { if (++connects > 1)
177
+ stopping = plugin.deactivate("stop"); connected = true; },
178
+ close() { connected = false; }, connected: () => connected,
179
+ send: async () => undefined, direct: async () => { sends++; return "不得发送"; },
180
+ } });
181
+ await plugin.activate({ config: {}, dataDir: "." });
182
+ try {
183
+ await plugin.requestQueue;
184
+ connected = false;
185
+ await strict_1.default.rejects(plugin.handle({ task: "停用请求" }), /未就绪/);
186
+ strict_1.default.ok(stopping);
187
+ await stopping;
188
+ strict_1.default.equal(sends, 0);
189
+ strict_1.default.equal(connected, false);
190
+ strict_1.default.equal((await plugin.health()).status, "stopped");
191
+ }
192
+ finally {
193
+ await plugin.deactivate("stop");
194
+ }
195
+ });
@@ -49,6 +49,39 @@ async function listen(server) {
49
49
  strict_1.default.ok(address && typeof address === "object");
50
50
  return address.port;
51
51
  }
52
+ (0, node_test_1.default)("千问 HTTP 请求在提交前恢复 CDP,消息只发送一次", async () => {
53
+ let connected = true;
54
+ let connects = 0;
55
+ const tasks = [];
56
+ let handler;
57
+ const plugin = new qwenwork_1.QwenworkPlugin({ httpPort: 0, healthIntervalMs: 60_000, client: {
58
+ connect: async () => { connects++; connected = true; }, close() { connected = false; }, connected: () => connected,
59
+ send: async () => undefined, direct: async (text) => { tasks.push(text); return "恢复后的回复"; },
60
+ } });
61
+ await plugin.activate({ config: {}, dataDir: ".", http: {
62
+ register(route) { if (route.method === "POST" && route.path === "/v1/chat/completions")
63
+ handler = route.handler; },
64
+ } });
65
+ const server = (0, node_http_1.createServer)((req, res) => void handler(req, res));
66
+ const port = await listen(server);
67
+ try {
68
+ await plugin.requestQueue;
69
+ connected = false;
70
+ const response = await fetch(`http://127.0.0.1:${port}/v1/chat/completions`, {
71
+ method: "POST", headers: { "content-type": "application/json" },
72
+ body: JSON.stringify({ messages: [{ role: "user", content: "断线后的第一条" }] }),
73
+ });
74
+ const body = await response.json();
75
+ strict_1.default.equal(response.status, 200);
76
+ strict_1.default.equal(body.choices[0].message.content, "恢复后的回复");
77
+ strict_1.default.deepEqual(tasks, ["断线后的第一条"]);
78
+ strict_1.default.equal(connects, 2);
79
+ }
80
+ finally {
81
+ await new Promise(resolve => server.close(() => resolve()));
82
+ await plugin.deactivate("stop");
83
+ }
84
+ });
52
85
  (0, node_test_1.default)("OpenAI adapter separates SSE events with real blank lines", async () => {
53
86
  const exchanges = [];
54
87
  const adapter = new openai_adapter_1.OpenAIAdapter(async () => "hello from qwen", { onExchange: (direction, text) => exchanges.push(`${direction}:${text}`) });
@@ -0,0 +1,112 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const strict_1 = __importDefault(require("node:assert/strict"));
7
+ const node_test_1 = __importDefault(require("node:test"));
8
+ const promises_1 = require("node:fs/promises");
9
+ const node_os_1 = require("node:os");
10
+ const node_path_1 = require("node:path");
11
+ const node_http_1 = require("node:http");
12
+ const session_management_1 = require("../src-ts/session-management");
13
+ const source = (peer) => JSON.stringify(["aun", "assistant.agentid.cn", "", peer, ""]);
14
+ const context = { source: source("any.agentid.cn"), sessionId: "manager" };
15
+ const refOf = (text) => text.match(/[a-f0-9]{12}\.1/)[0];
16
+ const desktops = ["workbuddy", "qwenwork", "doubao", "dsh", "chatgpt"];
17
+ async function fixture(run) {
18
+ const root = await (0, promises_1.mkdtemp)((0, node_path_1.join)((0, node_os_1.tmpdir)(), "mab-session-access-"));
19
+ const now = new Date().toISOString();
20
+ const providers = desktops.map(pluginId => ({
21
+ pluginId, status: "ready", identity: {}, provider: {
22
+ scope: `${pluginId}:local`, mode: "all", limitation: "测试历史",
23
+ listSessions: async () => ({ supported: true, sessions: [{ id: `${pluginId}-old`, title: `${pluginId} 历史`, updatedAt: now }], complete: true, missingReasons: [], readAt: now }),
24
+ readSession: async ({ sessionId }) => ({ supported: true, session: { id: sessionId, title: "历史" }, turns: [{ role: "assistant", text: `${pluginId} 对话材料`, time: now }], complete: true, missingReasons: [], readAt: now }),
25
+ },
26
+ }));
27
+ try {
28
+ await run(new session_management_1.SessionManagement(root, undefined, undefined, () => providers), root);
29
+ }
30
+ finally {
31
+ await (0, promises_1.rm)(root, { recursive: true, force: true });
32
+ }
33
+ }
34
+ (0, node_test_1.default)("会话查询默认允许任意 Peer 读取五个插件历史,换逻辑会话无需重新授权", async () => {
35
+ await fixture(async (service) => {
36
+ strict_1.default.deepEqual((await service.config()).allowFrom, []);
37
+ for (const pluginId of desktops) {
38
+ for (const sessionId of ["manager", "after-new"]) {
39
+ const caller = { ...context, sessionId };
40
+ const listing = (await service.command(`/sessions native-list ${pluginId}`, caller));
41
+ strict_1.default.match(listing, new RegExp(`${pluginId}-old`));
42
+ strict_1.default.match((await service.command(`/sessions native-show ${refOf(listing)}`, caller)), /对话材料/);
43
+ }
44
+ strict_1.default.match((await service.command(`/sessions today ${pluginId}`, context)), /对话材料/);
45
+ strict_1.default.match((await service.command(`/sessions last 4h ${pluginId}`, context)), /对话材料/);
46
+ }
47
+ const all = (await service.command("/sessions today", context));
48
+ for (const pluginId of desktops)
49
+ strict_1.default.match(all, new RegExp(`${pluginId} 对话材料`));
50
+ });
51
+ });
52
+ (0, node_test_1.default)("旧配置没有 allowFrom 且 grants 为空时也允许查询原生历史", async () => {
53
+ await fixture(async (service, root) => {
54
+ await (0, promises_1.writeFile)((0, node_path_1.join)(root, "session-management.json"), JSON.stringify({ enabled: false, grants: [], nativeGrants: [] }));
55
+ const listing = (await service.command("/sessions native-list doubao", context));
56
+ strict_1.default.match(listing, /doubao-old/);
57
+ strict_1.default.deepEqual((await service.config()).allowFrom, []);
58
+ });
59
+ });
60
+ (0, node_test_1.default)("默认跨来源查询与个人助理归档小结可读取其他 Peer 的已归档记录", async () => {
61
+ let summarized = false;
62
+ const server = (0, node_http_1.createServer)(async (request, response) => {
63
+ let body = "";
64
+ for await (const chunk of request)
65
+ body += chunk;
66
+ summarized = body.includes("其他来源的工作记录");
67
+ response.setHeader("content-type", "application/json");
68
+ response.end(JSON.stringify({ choices: [{ message: { content: "跨来源小结" } }] }));
69
+ });
70
+ await new Promise(resolve => server.listen(0, "127.0.0.1", resolve));
71
+ try {
72
+ await fixture(async (service) => {
73
+ await service.saveConfig({ enabled: true, grants: [], summarizer: { endpoint: `http://127.0.0.1:${server.address().port}/v1/chat/completions`, model: "test" } });
74
+ await service.archive.recordTurn({ source: source("other.agentid.cn"), principal: source("other.agentid.cn"), sessionId: "other-history", agentPlugin: "workbuddy", channel: "aun", role: "user", text: "其他来源的工作记录", completed: true, time: new Date().toISOString() });
75
+ const listing = (await service.command("/sessions list", context));
76
+ strict_1.default.match(listing, /other-history/);
77
+ const ref = refOf(listing);
78
+ strict_1.default.match((await service.command(`/sessions show ${ref}`, context)), /其他来源的工作记录/);
79
+ strict_1.default.match((await service.command(`/sessions show ${ref}`, { ...context, sessionId: "after-new" })), /编号/);
80
+ const result = await service.summarizeToday(context);
81
+ strict_1.default.equal(summarized, true);
82
+ strict_1.default.equal(result.supported, true);
83
+ strict_1.default.equal(result.sources[0]?.sessionId, "other-history");
84
+ });
85
+ }
86
+ finally {
87
+ await new Promise(resolve => server.close(() => resolve()));
88
+ }
89
+ });
90
+ (0, node_test_1.default)("allowFrom 可限制来源,清空名单恢复全部允许并使旧编号失效", async () => {
91
+ await fixture(async (service) => {
92
+ await service.saveConfig({ enabled: false, allowFrom: ["any.agentid.cn"], grants: [] });
93
+ const listing = (await service.command("/sessions native-list dsh", context));
94
+ strict_1.default.match(listing, /dsh-old/);
95
+ strict_1.default.match((await service.command("/sessions native-list dsh", { ...context, source: source("other.agentid.cn") })), /无权/);
96
+ await service.saveConfig({ enabled: false, allowFrom: [], grants: [] });
97
+ strict_1.default.match((await service.command(`/sessions native-show ${refOf(listing)}`, context)), /编号/);
98
+ strict_1.default.match((await service.command("/sessions native-list dsh", context)), /dsh-old/);
99
+ for (const allowFrom of [true, ["any*"], ["*", "*"], [""], [42]]) {
100
+ await strict_1.default.rejects(service.saveConfig({ enabled: false, allowFrom, grants: [] }), /allowFrom|来源/);
101
+ }
102
+ });
103
+ });
104
+ (0, node_test_1.default)("历史权限配置损坏时拒绝查询,修复文件后可重新加载", async () => {
105
+ await fixture(async (service, root) => {
106
+ const file = (0, node_path_1.join)(root, "session-management.json");
107
+ await (0, promises_1.writeFile)(file, JSON.stringify({ enabled: false, allowFrom: 42, grants: [] }));
108
+ await strict_1.default.rejects(service.command("/sessions native-list doubao", context), /设置读取失败/);
109
+ await (0, promises_1.writeFile)(file, JSON.stringify({ enabled: false, allowFrom: ["other.agentid.cn"], grants: [] }));
110
+ strict_1.default.match((await service.command("/sessions native-list doubao", context)), /无权/);
111
+ });
112
+ });
@@ -18,7 +18,7 @@ function localDate(year, month, day, hour = 12) {
18
18
  const root = await (0, promises_1.mkdtemp)((0, node_path_1.join)((0, node_os_1.tmpdir)(), "mab-management-"));
19
19
  try {
20
20
  const service = new session_management_1.SessionManagement(root);
21
- strict_1.default.deepEqual(await service.config(), { enabled: false, grants: [] });
21
+ strict_1.default.deepEqual(await service.config(), { enabled: false, allowFrom: [], grants: [] });
22
22
  await service.saveConfig({ enabled: true, grants: [], summarizer: { endpoint: "https://example.test/v1", model: "summary", apiKey: "secret" } });
23
23
  const config = await new session_management_1.SessionManagement(root).config();
24
24
  strict_1.default.equal(config.enabled, true);
@@ -32,11 +32,11 @@ function localDate(year, month, day, hour = 12) {
32
32
  await (0, promises_1.rm)(root, { recursive: true, force: true });
33
33
  }
34
34
  });
35
- (0, node_test_1.default)("命令精确拦截,跨来源授权绑定当前逻辑会话并且快照不会串用", async () => {
35
+ (0, node_test_1.default)("命令精确拦截,限制模式下跨来源授权绑定当前逻辑会话且快照不会串用", async () => {
36
36
  const root = await (0, promises_1.mkdtemp)((0, node_path_1.join)((0, node_os_1.tmpdir)(), "mab-management-auth-"));
37
37
  try {
38
38
  const service = new session_management_1.SessionManagement(root);
39
- await service.saveConfig({ enabled: true, grants: [] });
39
+ await service.saveConfig({ enabled: true, allowFrom: ["history-admin.agentid.cn"], grants: [] });
40
40
  for (const peer of ["admin", "other"])
41
41
  await service.archive.recordTurn({ sessionId: `old-${peer}`, agentPlugin: "demo", channel: "aun", source: source(peer), principal: source(peer), role: "user", text: `秘密-${peer}`, time: new Date().toISOString(), completed: true });
42
42
  const context = { source: source("admin"), sessionId: "current" };
@@ -44,7 +44,7 @@ function localDate(year, month, day, hour = 12) {
44
44
  strict_1.default.match((await service.command("/sessions invalid", context)), /用法/);
45
45
  let listing = (await service.command("/sessions list", context));
46
46
  strict_1.default.ok(!listing.includes("old-other"));
47
- await service.saveConfig({ enabled: true, grants: [{ managerSource: source("admin"), managerSessionId: "current", sources: [source("other")] }] });
47
+ await service.saveConfig({ enabled: true, allowFrom: ["history-admin.agentid.cn"], grants: [{ managerSource: source("admin"), managerSessionId: "current", sources: [source("other")] }] });
48
48
  listing = (await service.command("/sessions list", context));
49
49
  strict_1.default.match(listing, /old-other/);
50
50
  const ref = listing.split("\n").find(line => line.includes("old-other")).split(" ")[0];
@@ -358,7 +358,7 @@ function localDate(year, month, day, hour = 12) {
358
358
  await (0, promises_1.rm)(root, { recursive: true, force: true });
359
359
  }
360
360
  });
361
- (0, node_test_1.default)("今日总结按本地日历和正文时间筛选,跨页读取且不纳入未来或其他来源", async () => {
361
+ (0, node_test_1.default)("限制模式下今日总结按本地日历和正文时间筛选,跨页读取且不纳入未来或其他来源", async () => {
362
362
  const root = await (0, promises_1.mkdtemp)((0, node_path_1.join)((0, node_os_1.tmpdir)(), "mab-management-today-"));
363
363
  let requests = 0;
364
364
  let prompt = "";
@@ -376,7 +376,7 @@ function localDate(year, month, day, hour = 12) {
376
376
  try {
377
377
  const endpoint = `http://127.0.0.1:${server.address().port}/v1/chat/completions`;
378
378
  const service = new session_management_1.SessionManagement(root);
379
- await service.saveConfig({ enabled: true, grants: [], summarizer: { endpoint, model: "today" } });
379
+ await service.saveConfig({ enabled: true, allowFrom: ["history-admin.agentid.cn"], grants: [], summarizer: { endpoint, model: "today" } });
380
380
  const context = { source: source("peer"), sessionId: "manager" };
381
381
  const now = new Date();
382
382
  const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
@@ -428,7 +428,7 @@ function localDate(year, month, day, hour = 12) {
428
428
  try {
429
429
  const service = new session_management_1.SessionManagement(root);
430
430
  const context = { source: source("admin"), sessionId: "manager" };
431
- await service.saveConfig({ enabled: true, grants: [{ managerSource: context.source, managerSessionId: context.sessionId, sources: [source("other")] }] });
431
+ await service.saveConfig({ enabled: true, allowFrom: ["history-admin.agentid.cn"], grants: [{ managerSource: context.source, managerSessionId: context.sessionId, sources: [source("other")] }] });
432
432
  await service.archive.recordTurn({ sessionId: "today", source: source("other"), principal: source("other"), agentPlugin: "demo", channel: "aun", role: "user", completed: true, time: new Date().toISOString(), text: "撤销后不能发送" });
433
433
  let entered;
434
434
  let release;
@@ -438,7 +438,7 @@ function localDate(year, month, day, hour = 12) {
438
438
  service.archive.readSession = async (...args) => { entered(); await gate; return read(...args); };
439
439
  const result = service.summarizeToday(context);
440
440
  await reading;
441
- await service.saveConfig({ enabled: true, grants: [] });
441
+ await service.saveConfig({ enabled: true, allowFrom: ["history-admin.agentid.cn"], grants: [] });
442
442
  release();
443
443
  await strict_1.default.rejects(result, /权限或归档设置已变更/);
444
444
  }
@@ -36,3 +36,43 @@ const version_manager_1 = require("../src-ts/version-manager");
36
36
  await (0, promises_1.rm)(root, { recursive: true, force: true });
37
37
  }
38
38
  });
39
+ (0, node_test_1.default)("npm 包不存在时标记为未发布", async () => {
40
+ const root = await (0, promises_1.mkdtemp)((0, node_path_1.join)((0, node_os_1.tmpdir)(), "mab-version-"));
41
+ try {
42
+ const manager = new version_manager_1.VersionManager({ stateDir: root, npm: async () => { throw new Error("Command failed: npm.cmd view agent-bridge-pa version dist-tags --json\nnpm error code E404\nnpm error 404 Not Found"); } });
43
+ const status = await manager.checkPackage("agent-bridge-pa", "1.0.1");
44
+ strict_1.default.equal(status.state, "unpublished");
45
+ strict_1.default.equal(status.error, "npm 包未发布或当前无权访问");
46
+ }
47
+ finally {
48
+ await (0, promises_1.rm)(root, { recursive: true, force: true });
49
+ }
50
+ });
51
+ (0, node_test_1.default)("npm 12 的单元素数组返回正确版本", async () => {
52
+ const root = await (0, promises_1.mkdtemp)((0, node_path_1.join)((0, node_os_1.tmpdir)(), "mab-version-"));
53
+ try {
54
+ const manager = new version_manager_1.VersionManager({ stateDir: root, npm: async () => JSON.stringify([{ version: "1.0.73", "dist-tags": { latest: "1.0.73" } }]) });
55
+ const status = await manager.checkPackage("mu-agent-bridge", "1.0.66");
56
+ strict_1.default.equal(status.state, "update-available");
57
+ strict_1.default.equal(status.latestVersion, "1.0.73");
58
+ strict_1.default.equal(JSON.parse(await (0, promises_1.readFile)((0, node_path_1.join)(root, "versions.json"), "utf8"))[0].latestVersion, "1.0.73");
59
+ }
60
+ finally {
61
+ await (0, promises_1.rm)(root, { recursive: true, force: true });
62
+ }
63
+ });
64
+ (0, node_test_1.default)("npm 无效返回不产生可升级版本", async () => {
65
+ const root = await (0, promises_1.mkdtemp)((0, node_path_1.join)((0, node_os_1.tmpdir)(), "mab-version-"));
66
+ try {
67
+ for (const value of [[], [{ version: "1.0.1" }, { version: "1.0.2" }], [{ version: "invalid" }], null]) {
68
+ const manager = new version_manager_1.VersionManager({ stateDir: root, npm: async () => JSON.stringify(value) });
69
+ const status = await manager.checkPackage("demo", "1.0.0");
70
+ strict_1.default.equal(status.state, "error");
71
+ strict_1.default.equal(status.latestVersion, undefined);
72
+ strict_1.default.equal(status.error, "npm 返回的版本号无效");
73
+ }
74
+ }
75
+ finally {
76
+ await (0, promises_1.rm)(root, { recursive: true, force: true });
77
+ }
78
+ });