mu-agent-bridge 1.0.71 → 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 +17 -10
- package/dist/src-ts/agent.js +35 -0
- package/dist/src-ts/channel-sessions.js +3 -2
- package/dist/src-ts/cli.js +67 -37
- package/dist/src-ts/control-server.js +12 -30
- 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 +31 -13
- package/dist/src-ts/http-agent.js +36 -0
- package/dist/src-ts/plugin-catalog.js +11 -2
- package/dist/src-ts/plugin-manager.js +351 -50
- package/dist/src-ts/plugins/abweb/index.js +1 -1
- package/dist/src-ts/plugins/abweb/public/app.js +73 -5
- package/dist/src-ts/plugins/abweb/public/index.html +15 -0
- package/dist/src-ts/plugins/abweb/public/style.css +4 -0
- package/dist/src-ts/plugins/aun/attachments.js +3 -21
- package/dist/src-ts/plugins/aun/index.js +155 -21
- package/dist/src-ts/plugins/aun/messages.js +4 -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/cdp-client.js +26 -12
- package/dist/src-ts/plugins/chatgpt/index.js +80 -19
- package/dist/src-ts/plugins/claude-cli/index.js +19 -2
- package/dist/src-ts/plugins/codex-cli/index.js +19 -2
- package/dist/src-ts/plugins/desktop-launch.js +44 -3
- package/dist/src-ts/plugins/doubao/cdp-client.js +234 -45
- package/dist/src-ts/plugins/doubao/index.js +152 -8
- 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 +172 -21
- 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/local-cli/agent.js +74 -9
- package/dist/src-ts/plugins/personal-assistant/claude-cli-model.js +30 -0
- package/dist/src-ts/plugins/personal-assistant/coordination.js +57 -0
- package/dist/src-ts/plugins/personal-assistant/index.js +263 -0
- package/dist/src-ts/plugins/personal-assistant/model-client.js +181 -0
- package/dist/src-ts/plugins/personal-assistant/owner-context.js +56 -0
- package/dist/src-ts/plugins/personal-assistant/remote-ability.js +176 -0
- package/dist/src-ts/plugins/personal-assistant/session-memory.js +124 -0
- package/dist/src-ts/plugins/personal-assistant/tools.js +167 -0
- package/dist/src-ts/plugins/qwenwork/cdp-client.js +90 -18
- package/dist/src-ts/plugins/qwenwork/index.js +139 -24
- package/dist/src-ts/plugins/workbuddy/cdp-client.js +353 -82
- package/dist/src-ts/plugins/workbuddy/index.js +124 -41
- 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 +132 -14
- package/dist/src-ts/version-manager.js +6 -3
- package/dist/tests-ts/abweb-cli.test.js +10 -2
- package/dist/tests-ts/abweb-model-config.test.js +73 -0
- package/dist/tests-ts/agent.test.js +27 -0
- 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 +20 -0
- package/dist/tests-ts/aun-plugin.test.js +292 -19
- package/dist/tests-ts/bundled-package.test.js +12 -2
- package/dist/tests-ts/cdp-draft-recovery.test.js +182 -3
- package/dist/tests-ts/channel-sessions.test.js +27 -0
- package/dist/tests-ts/chatgpt-plugin.test.js +118 -0
- package/dist/tests-ts/control-server.test.js +56 -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 +773 -12
- package/dist/tests-ts/dsh-agent.test.js +157 -8
- 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/installer-assets.test.js +1 -1
- package/dist/tests-ts/local-cli.test.js +146 -1
- package/dist/tests-ts/model-provider-cancellation.test.js +152 -0
- package/dist/tests-ts/native-session-management.test.js +8 -7
- package/dist/tests-ts/personal-assistant-coordination.test.js +92 -0
- package/dist/tests-ts/personal-assistant-extensions.test.js +35 -0
- package/dist/tests-ts/personal-assistant-memory.test.js +117 -0
- package/dist/tests-ts/personal-assistant-owner-context.test.js +107 -0
- package/dist/tests-ts/personal-assistant-remote-ability.test.js +123 -0
- package/dist/tests-ts/personal-assistant.test.js +663 -0
- package/dist/tests-ts/platform-integration.test.js +183 -1
- package/dist/tests-ts/plugin-catalog.test.js +12 -2
- package/dist/tests-ts/plugin-manager.test.js +356 -4
- package/dist/tests-ts/plugin-upgrade-deadline.test.js +354 -0
- package/dist/tests-ts/publish-packages.test.js +42 -0
- package/dist/tests-ts/qwenwork-plugin.test.js +135 -1
- 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 +159 -4
- 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 +499 -4
- 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/agent.js +35 -0
- package/plugins/abweb/dist/src-ts/plugins/abweb/index.js +1 -1
- package/plugins/abweb/dist/src-ts/plugins/abweb/public/app.js +73 -5
- package/plugins/abweb/dist/src-ts/plugins/abweb/public/app.ts +57 -5
- package/plugins/abweb/dist/src-ts/plugins/abweb/public/index.html +15 -0
- package/plugins/abweb/dist/src-ts/plugins/abweb/public/style.css +4 -0
- 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/agent.js +35 -0
- package/plugins/aun/dist/src-ts/plugins/aun/attachments.js +3 -21
- package/plugins/aun/dist/src-ts/plugins/aun/index.js +155 -21
- package/plugins/aun/dist/src-ts/plugins/aun/messages.js +4 -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/agent.js +35 -0
- package/plugins/chatgpt/dist/src-ts/plugins/cdp-draft.js +5 -2
- package/plugins/chatgpt/dist/src-ts/plugins/chatgpt/cdp-client.js +26 -12
- package/plugins/chatgpt/dist/src-ts/plugins/chatgpt/index.js +80 -19
- package/plugins/chatgpt/dist/src-ts/plugins/desktop-launch.js +44 -3
- package/plugins/chatgpt/package.json +1 -1
- package/plugins/chatgpt/plugin.json +1 -1
- package/plugins/claude-cli/dist/src-ts/agent.js +35 -0
- package/plugins/claude-cli/dist/src-ts/plugins/claude-cli/index.js +19 -2
- package/plugins/claude-cli/dist/src-ts/plugins/local-cli/agent.js +74 -9
- package/plugins/claude-cli/package.json +1 -1
- package/plugins/claude-cli/plugin.json +3 -2
- package/plugins/codex-cli/dist/src-ts/agent.js +35 -0
- package/plugins/codex-cli/dist/src-ts/plugins/codex-cli/index.js +19 -2
- package/plugins/codex-cli/dist/src-ts/plugins/local-cli/agent.js +74 -9
- package/plugins/codex-cli/package.json +1 -1
- package/plugins/codex-cli/plugin.json +3 -2
- package/plugins/doubao/config.schema.json +2 -1
- package/plugins/doubao/dist/src-ts/agent.js +35 -0
- package/plugins/doubao/dist/src-ts/plugins/desktop-launch.js +44 -3
- package/plugins/doubao/dist/src-ts/plugins/doubao/cdp-client.js +234 -45
- package/plugins/doubao/dist/src-ts/plugins/doubao/index.js +152 -8
- 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 +3 -2
- package/plugins/dsh/config.schema.json +5 -2
- package/plugins/dsh/dist/src-ts/agent.js +35 -0
- package/plugins/dsh/dist/src-ts/dsh-agent.js +31 -13
- 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 +172 -21
- 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 +3 -2
- package/plugins/feishu/dist/src-ts/agent.js +35 -0
- 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 +23 -0
- package/plugins/personal-assistant/dist/src-ts/agent.js +69 -0
- package/plugins/personal-assistant/dist/src-ts/attachments.js +62 -0
- package/plugins/personal-assistant/dist/src-ts/plugin.js +13 -0
- package/plugins/personal-assistant/dist/src-ts/plugins/personal-assistant/claude-cli-model.js +30 -0
- package/plugins/personal-assistant/dist/src-ts/plugins/personal-assistant/coordination.js +57 -0
- package/plugins/personal-assistant/dist/src-ts/plugins/personal-assistant/index.js +263 -0
- package/plugins/personal-assistant/dist/src-ts/plugins/personal-assistant/model-client.js +181 -0
- package/plugins/personal-assistant/dist/src-ts/plugins/personal-assistant/owner-context.js +56 -0
- package/plugins/personal-assistant/dist/src-ts/plugins/personal-assistant/remote-ability.js +176 -0
- 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 +167 -0
- package/plugins/personal-assistant/package.json +39 -0
- package/plugins/personal-assistant/plugin.json +15 -0
- package/plugins/qwenwork/dist/src-ts/agent.js +35 -0
- 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 +90 -18
- package/plugins/qwenwork/dist/src-ts/plugins/qwenwork/index.js +139 -24
- package/plugins/qwenwork/package.json +1 -1
- package/plugins/qwenwork/plugin.json +3 -2
- package/plugins/workbuddy/config.schema.json +1 -1
- package/plugins/workbuddy/dist/src-ts/agent.js +35 -0
- package/plugins/workbuddy/dist/src-ts/plugins/desktop-launch.js +44 -3
- package/plugins/workbuddy/dist/src-ts/plugins/workbuddy/cdp-client.js +353 -82
- package/plugins/workbuddy/dist/src-ts/plugins/workbuddy/index.js +124 -41
- package/plugins/workbuddy/package.json +1 -1
- package/plugins/workbuddy/plugin.json +3 -2
- 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
|
@@ -10,7 +10,7 @@ const session_summary_1 = require("./session-summary");
|
|
|
10
10
|
class ManagementError extends Error {
|
|
11
11
|
}
|
|
12
12
|
const dailyPlugins = ["qwenwork", "workbuddy", "doubao", "chatgpt", "dsh"];
|
|
13
|
-
const usage = "用法:/sessions today [插件ID] [--summary];/sessions last <1
|
|
13
|
+
const usage = "用法:/sessions today [插件ID] [--summary];/sessions last <1-24>h [插件ID] [--summary];/sessions list [插件ID] [--page 页码 --snapshot 编号];/sessions show <编号>;/sessions native-list <插件ID>;/sessions native-show <编号>;/sessions summary --from <编号,...> [主题]";
|
|
14
14
|
function isDailySummaryRequest(text) {
|
|
15
15
|
return /(?:今天|今日).{0,20}(?:聊了什么|聊了哪些|话题|小结|总结)|(?:小结|总结).{0,20}(?:今天|今日|对话)|最近\s*\d+\s*(?:小时|h).{0,20}(?:聊|话题|小结|总结|对话|消息)/i.test(text.trim());
|
|
16
16
|
}
|
|
@@ -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,111 @@ 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 };
|
|
134
|
+
}
|
|
135
|
+
async summarizeToday(context, now = new Date()) {
|
|
136
|
+
await this.load();
|
|
137
|
+
validateSource(context.source);
|
|
138
|
+
if (!context.sessionId)
|
|
139
|
+
throw new ManagementError("当前逻辑会话无效");
|
|
140
|
+
if (!(now instanceof Date) || !Number.isFinite(now.getTime()))
|
|
141
|
+
throw new ManagementError("总结时间无效");
|
|
142
|
+
const revision = this.revision;
|
|
143
|
+
const start = new Date(now.getFullYear(), now.getMonth(), now.getDate());
|
|
144
|
+
const end = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1);
|
|
145
|
+
const cutoff = Math.min(now.getTime(), end.getTime());
|
|
146
|
+
const zone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
147
|
+
const range = `${formatLocalDate(start)} 至 ${formatLocalDate(new Date(cutoff))} (${zone})`;
|
|
148
|
+
const limitation = "原生插件历史未纳入今日总结:缺少可验证的历史时间,仅统计归档正文";
|
|
149
|
+
const selected = [];
|
|
150
|
+
let characters = 0;
|
|
151
|
+
try {
|
|
152
|
+
const catalog = await this.catalog(this.sources(context));
|
|
153
|
+
this.checkRevision(revision);
|
|
154
|
+
for (const session of catalog.sessions) {
|
|
155
|
+
const turns = [];
|
|
156
|
+
const reasons = new Set([
|
|
157
|
+
...(session.missingReasons ?? []),
|
|
158
|
+
...(catalog.complete ? [] : ["归档目录不完整"]),
|
|
159
|
+
limitation,
|
|
160
|
+
]);
|
|
161
|
+
let complete = true;
|
|
162
|
+
for (let page = 1; page <= 20; page++) {
|
|
163
|
+
const result = await this.archive.readSession(session.sessionId, { page, limit: 100 });
|
|
164
|
+
this.checkRevision(revision);
|
|
165
|
+
if (!result.session) {
|
|
166
|
+
complete = false;
|
|
167
|
+
reasons.add("会话正文不存在");
|
|
168
|
+
break;
|
|
169
|
+
}
|
|
170
|
+
for (const reason of result.missingReasons)
|
|
171
|
+
reasons.add(reason);
|
|
172
|
+
complete &&= result.complete;
|
|
173
|
+
for (const turn of result.turns) {
|
|
174
|
+
const timestamp = Date.parse(turn.time);
|
|
175
|
+
if (!Number.isFinite(timestamp)) {
|
|
176
|
+
complete = false;
|
|
177
|
+
reasons.add("部分正文时间未知");
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
if (timestamp >= start.getTime() && timestamp <= cutoff) {
|
|
181
|
+
characters += turn.text.length;
|
|
182
|
+
if (characters > session_summary_1.MAX_SUMMARY_CHARACTERS)
|
|
183
|
+
throw new ManagementError("总结输入超过60000字符,请缩小范围");
|
|
184
|
+
turns.push({ role: turn.role, time: turn.time, text: turn.text });
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
if (!result.hasNext)
|
|
188
|
+
break;
|
|
189
|
+
if (page === 20) {
|
|
190
|
+
complete = false;
|
|
191
|
+
reasons.add("正文超过20页,未完整读取");
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
if (!turns.length)
|
|
195
|
+
continue;
|
|
196
|
+
if (this.bindings?.isBusy(session.source))
|
|
197
|
+
reasons.add("源会话正在运行,仅包含已完成轮次");
|
|
198
|
+
if (selected.length >= session_summary_1.MAX_SUMMARY_SESSIONS)
|
|
199
|
+
throw new ManagementError(`今日会话超过 ${session_summary_1.MAX_SUMMARY_SESSIONS} 个,请缩小来源范围`);
|
|
200
|
+
selected.push({
|
|
201
|
+
sessionId: session.sessionId, agentPlugin: session.agentPlugin, channel: session.channel,
|
|
202
|
+
...(session.title ? { title: session.title } : {}), firstSeenAt: session.firstSeenAt, updatedAt: session.updatedAt,
|
|
203
|
+
complete: complete && reasons.size === 0, incompleteReasons: [...reasons], turns,
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
this.checkRevision(revision);
|
|
207
|
+
if (!selected.length) {
|
|
208
|
+
const result = { supported: false, text: `今日没有可总结的已归档会话(${range})。${limitation}`, complete: false, sources: [] };
|
|
209
|
+
this.audit("今日总结", { actor: context.source, count: 0, status: "empty" });
|
|
210
|
+
return result;
|
|
211
|
+
}
|
|
212
|
+
const summarizer = this.settings.summarizer?.model;
|
|
213
|
+
const result = await new session_summary_1.SessionSummarizer(this.settings.summarizer).summarizeSessions({ sessions: selected }).catch(error => {
|
|
214
|
+
if (error instanceof session_summary_1.SessionSummaryError && error.code === "limit")
|
|
215
|
+
throw new ManagementError(error.message);
|
|
216
|
+
throw new ManagementError("只读总结失败,请检查本机配置与模型服务");
|
|
217
|
+
});
|
|
218
|
+
this.checkRevision(revision);
|
|
219
|
+
const output = { ...result, text: `今日工作小结(${range}):\n${result.text}\n\n说明:${limitation}` };
|
|
220
|
+
this.audit("今日总结", { actor: context.source, count: selected.length, status: result.supported ? "ok" : "unsupported", refs: selected.map(item => item.sessionId), summarizer });
|
|
221
|
+
return output;
|
|
222
|
+
}
|
|
223
|
+
catch (error) {
|
|
224
|
+
this.audit("今日总结", { actor: context.source, count: selected.length, status: "failed", ...(selected.length ? { refs: selected.map(item => item.sessionId) } : {}) });
|
|
225
|
+
throw error;
|
|
226
|
+
}
|
|
121
227
|
}
|
|
122
228
|
async command(text, context) {
|
|
123
229
|
const dailyRequest = isDailySummaryRequest(text);
|
|
@@ -137,15 +243,15 @@ class SessionManagement {
|
|
|
137
243
|
const recent = input.match(/^\/sessions last (\d+)h(?: (qwenwork|workbuddy|doubao|chatgpt|dsh))?( --summary)?$/i);
|
|
138
244
|
const naturalRecent = dailyRequest ? input.match(/最近\s*(\d+)\s*(?:小时|h)/i) : undefined;
|
|
139
245
|
if (naturalRecent)
|
|
140
|
-
return await this.
|
|
246
|
+
return await this.summarizeNativeToday(context, undefined, Boolean(this.settings.summarizer), Number(naturalRecent[1]));
|
|
141
247
|
if (recent)
|
|
142
|
-
return await this.
|
|
248
|
+
return await this.summarizeNativeToday(context, recent[2], Boolean(recent[3]), Number(recent[1]));
|
|
143
249
|
if (/^\/sessions last\b/i.test(input))
|
|
144
250
|
throw new ManagementError("时间范围须为最近1至24小时,例如 /sessions last 4h");
|
|
145
251
|
if (dailyRequest)
|
|
146
|
-
return await this.
|
|
252
|
+
return await this.summarizeNativeToday(context, undefined, Boolean(this.settings.summarizer));
|
|
147
253
|
if (today)
|
|
148
|
-
return await this.
|
|
254
|
+
return await this.summarizeNativeToday(context, today[1], Boolean(today[2]));
|
|
149
255
|
const nativeList = input.match(/^\/sessions native-list ([a-z][a-z0-9-]*)(?: --page ([1-9]\d*))?$/);
|
|
150
256
|
if (nativeList)
|
|
151
257
|
return await this.listNative(context, nativeList[1], nativeList[2] ? Number(nativeList[2]) : undefined);
|
|
@@ -240,7 +346,7 @@ class SessionManagement {
|
|
|
240
346
|
providers() {
|
|
241
347
|
return this.nativeProviders().map(({ pluginId, provider, status }) => ({ pluginId, scope: provider.scope, mode: provider.mode, limitation: provider.limitation, status }));
|
|
242
348
|
}
|
|
243
|
-
async
|
|
349
|
+
async summarizeNativeToday(context, onlyPlugin, summarize = false, hours) {
|
|
244
350
|
const revision = this.revision;
|
|
245
351
|
const cutoff = Date.now();
|
|
246
352
|
const configured = this.settings.summarizer?.model;
|
|
@@ -370,6 +476,8 @@ class SessionManagement {
|
|
|
370
476
|
return `${header}\n${result.text}`;
|
|
371
477
|
}
|
|
372
478
|
authorizedNative(context, pluginId, scope) {
|
|
479
|
+
if (this.allowsAllHistory(context))
|
|
480
|
+
return;
|
|
373
481
|
const grant = this.settings.nativeGrants?.find(item => item.managerSource === context.source && item.managerSessionId === context.sessionId);
|
|
374
482
|
if (!grant?.providers.some(item => item.pluginId === pluginId && (scope === undefined || item.scope === scope)))
|
|
375
483
|
throw new ManagementError("无权读取该原生插件历史,请由本机管理员精确授权");
|
|
@@ -518,7 +626,7 @@ class SessionManagement {
|
|
|
518
626
|
const catalog = await this.catalog(sources, plugin);
|
|
519
627
|
const archived = catalog.sessions;
|
|
520
628
|
const known = new Set(archived.map(item => item.sessionId));
|
|
521
|
-
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 }));
|
|
522
630
|
const sessions = [...archived, ...bound].filter(item => item.sessionId !== context.sessionId).sort((a, b) => b.updatedAt.localeCompare(a.updatedAt) || a.sessionId.localeCompare(b.sessionId));
|
|
523
631
|
this.checkRevision(revision);
|
|
524
632
|
const key = (0, node_crypto_1.randomUUID)().replace(/-/g, "").slice(0, 12);
|
|
@@ -543,7 +651,8 @@ class SessionManagement {
|
|
|
543
651
|
if (!snapshot || snapshot.revision !== this.revision || snapshot.expires < Date.now() || snapshot.context.source !== context.source || snapshot.context.sessionId !== context.sessionId)
|
|
544
652
|
throw new ManagementError("编号无效或已过期,请重新执行 /sessions list");
|
|
545
653
|
const session = snapshot.sessions[Number(match[2]) - 1];
|
|
546
|
-
|
|
654
|
+
const sources = this.sources(context);
|
|
655
|
+
if (!session || (sources && !sources.includes(session.source)) || session.sessionId === context.sessionId)
|
|
547
656
|
throw new ManagementError("无权读取所选会话,请重新列举");
|
|
548
657
|
return session;
|
|
549
658
|
}
|
|
@@ -551,8 +660,11 @@ class SessionManagement {
|
|
|
551
660
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
552
661
|
throw new ManagementError("归档设置必须是对象");
|
|
553
662
|
const input = value;
|
|
554
|
-
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)
|
|
555
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 来源白名单无效");
|
|
556
668
|
const managers = new Set();
|
|
557
669
|
const grants = input.grants.map((raw) => {
|
|
558
670
|
if (!raw || typeof raw !== "object" || Array.isArray(raw))
|
|
@@ -614,7 +726,7 @@ class SessionManagement {
|
|
|
614
726
|
const apiKey = config.apiKey === undefined && config.endpoint === previousEndpoint ? previousKey : config.apiKey;
|
|
615
727
|
summarizer = { endpoint: config.endpoint, model: config.model, ...(apiKey ? { apiKey } : {}) };
|
|
616
728
|
}
|
|
617
|
-
return { enabled: input.enabled, grants, ...(nativeGrants ? { nativeGrants } : {}), ...(summarizer ? { summarizer } : {}) };
|
|
729
|
+
return { enabled: input.enabled, allowFrom, grants, ...(nativeGrants ? { nativeGrants } : {}), ...(summarizer ? { summarizer } : {}) };
|
|
618
730
|
}
|
|
619
731
|
}
|
|
620
732
|
exports.SessionManagement = SessionManagement;
|
|
@@ -631,6 +743,12 @@ function validateSource(value) {
|
|
|
631
743
|
if (!Array.isArray(source) || source.length !== 5 || source.some(item => typeof item !== "string" || /[\u0000-\u001f]/.test(item)) || !["aun", "feishu"].includes(source[0]) || !source[3] || JSON.stringify(source) !== value)
|
|
632
744
|
throw new ManagementError("来源格式无效");
|
|
633
745
|
}
|
|
746
|
+
function formatLocalDate(value) {
|
|
747
|
+
const pad = (part) => String(part).padStart(2, "0");
|
|
748
|
+
const offset = -value.getTimezoneOffset();
|
|
749
|
+
const sign = offset >= 0 ? "+" : "-";
|
|
750
|
+
return `${value.getFullYear()}-${pad(value.getMonth() + 1)}-${pad(value.getDate())} ${pad(value.getHours())}:${pad(value.getMinutes())}${sign}${pad(Math.floor(Math.abs(offset) / 60))}:${pad(Math.abs(offset) % 60)}`;
|
|
751
|
+
}
|
|
634
752
|
function exactIdentifier(value, maximum) {
|
|
635
753
|
return typeof value === "string" && value.length > 0 && value.length <= maximum && !/[\u0000-\u001f*]/.test(value);
|
|
636
754
|
}
|
|
@@ -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();
|
|
@@ -35,7 +35,12 @@ class Element {
|
|
|
35
35
|
fetch: async (path, options) => {
|
|
36
36
|
let data = {};
|
|
37
37
|
if (path === "/api/plugins")
|
|
38
|
-
data = { plugins: [
|
|
38
|
+
data = { plugins: [
|
|
39
|
+
{ id: "codex-cli", displayName: "Codex CLI", type: "agent", version: "1.0.0", status: "installed", path: "plugins/codex-cli", manifest: { executionMode: "cli" } },
|
|
40
|
+
{ id: "qwenwork", displayName: "千问办公", type: "agent", version: "1.0.0", status: "ready", path: "plugins/qwenwork" },
|
|
41
|
+
{ id: "doubao", displayName: "豆包", type: "agent", version: "1.0.0", status: "ready", path: "plugins/doubao", manifest: { executionMode: "cdp" } },
|
|
42
|
+
{ id: "aun", displayName: "AUN", type: "channel", version: "1.0.0", status: "ready", path: "plugins/aun" },
|
|
43
|
+
] };
|
|
39
44
|
if (path.endsWith("/config")) {
|
|
40
45
|
if (options.method === "PATCH")
|
|
41
46
|
saved = JSON.parse(options.body);
|
|
@@ -46,7 +51,10 @@ class Element {
|
|
|
46
51
|
});
|
|
47
52
|
await new Promise(resolve => setImmediate(resolve));
|
|
48
53
|
const row = node("#plugins").children[0];
|
|
49
|
-
strict_1.default.
|
|
54
|
+
strict_1.default.equal(row.children[1].textContent, "Agent · CLI");
|
|
55
|
+
strict_1.default.equal(node("#plugins").children[1].children[1].textContent, "Agent · 桌面");
|
|
56
|
+
strict_1.default.equal(node("#plugins").children[2].children[1].textContent, "Agent · 桌面");
|
|
57
|
+
strict_1.default.equal(node("#plugins").children[3].children[1].textContent, "Channel");
|
|
50
58
|
const configure = row.children.at(-1).children.find(child => child.textContent === "配置");
|
|
51
59
|
strict_1.default.ok(configure, "CLI 插件必须提供配置按钮");
|
|
52
60
|
await configure.onclick();
|
|
@@ -0,0 +1,73 @@
|
|
|
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_vm_1 = require("node:vm");
|
|
10
|
+
class Element {
|
|
11
|
+
hidden = false;
|
|
12
|
+
disabled = false;
|
|
13
|
+
value = "";
|
|
14
|
+
textContent = "";
|
|
15
|
+
placeholder = "";
|
|
16
|
+
open = false;
|
|
17
|
+
children = [];
|
|
18
|
+
options = [];
|
|
19
|
+
selectedIndex = 0;
|
|
20
|
+
onclick;
|
|
21
|
+
onchange;
|
|
22
|
+
onsubmit;
|
|
23
|
+
append(...children) { this.children.push(...children); this.options = this.children; }
|
|
24
|
+
replaceChildren(...children) { this.children = children; this.options = this.children; }
|
|
25
|
+
setAttribute() { }
|
|
26
|
+
showModal() { this.open = true; }
|
|
27
|
+
close() { this.open = false; }
|
|
28
|
+
}
|
|
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"));
|
|
73
|
+
});
|
|
@@ -0,0 +1,27 @@
|
|
|
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 agent_1 = require("../src-ts/agent");
|
|
9
|
+
(0, node_test_1.default)("取消只结束调用方等待,不会停止仍在运行的桌面操作", async () => {
|
|
10
|
+
const controller = new AbortController();
|
|
11
|
+
let finish;
|
|
12
|
+
let operationFinished = false;
|
|
13
|
+
const operation = new Promise(resolve => { finish = () => { operationFinished = true; resolve("迟到结果"); }; });
|
|
14
|
+
const waiting = (0, agent_1.abortable)(operation, controller.signal);
|
|
15
|
+
controller.abort(new Error("请求已取消"));
|
|
16
|
+
await strict_1.default.rejects(waiting, /请求已取消/);
|
|
17
|
+
strict_1.default.equal(operationFinished, false);
|
|
18
|
+
finish();
|
|
19
|
+
strict_1.default.equal(await operation, "迟到结果");
|
|
20
|
+
strict_1.default.equal(operationFinished, true);
|
|
21
|
+
});
|
|
22
|
+
(0, node_test_1.default)("预取消仍观察已经启动的操作拒绝", async () => {
|
|
23
|
+
const controller = new AbortController();
|
|
24
|
+
controller.abort(new Error("预取消"));
|
|
25
|
+
await strict_1.default.rejects((0, agent_1.abortable)(Promise.reject(new Error("底层迟到失败")), controller.signal), /预取消/);
|
|
26
|
+
await new Promise(resolve => setImmediate(resolve));
|
|
27
|
+
});
|
|
@@ -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,19 @@ 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
|
+
});
|
|
24
|
+
(0, node_test_1.default)("AUN 错误和通知事件不作为任务触发自动互答", () => {
|
|
25
|
+
for (const type of ["error", "notice"]) {
|
|
26
|
+
strict_1.default.equal((0, messages_1.normalizeAunMessage)({ from: "peer.agentid.cn", payload: { type, text: "今日额度已用完", thread_id: "test-thread" } }, "self.agentid.cn"), undefined);
|
|
27
|
+
}
|
|
28
|
+
strict_1.default.equal((0, messages_1.normalizeAunMessage)({ from: "peer.agentid.cn", payload: { type: "text", text: "请分析今日额度已用完的原因" } }, "self.agentid.cn")?.text, "请分析今日额度已用完的原因");
|
|
29
|
+
});
|
|
17
30
|
(0, node_test_1.default)("AUN 纯附件消息保留可定位引用并拒绝任意 URL", () => {
|
|
18
31
|
const message = (0, messages_1.normalizeAunMessage)({
|
|
19
32
|
from: "peer.agentid.cn", message_id: "attachment-1", payload: {
|
|
@@ -61,3 +74,10 @@ const messages_1 = require("../src-ts/plugins/aun/messages");
|
|
|
61
74
|
strict_1.default.equal(message?.attachments?.[0].ownerAid, "third.agentid.cn");
|
|
62
75
|
strict_1.default.equal(message?.attachments?.[0].token, "Ab3xK9mZ2q");
|
|
63
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
|
+
});
|