mu-agent-bridge 1.0.42 → 1.0.44
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 +4 -0
- package/dist/src-ts/control-server.js +4 -1
- package/dist/src-ts/headless-account.patch.yml +10 -0
- package/dist/src-ts/plugin-manifest.js +3 -1
- package/dist/src-ts/plugins/abweb/public/app.js +34 -0
- package/dist/src-ts/plugins/abweb/public/index.html +10 -0
- package/dist/src-ts/plugins/abweb/public/style.css +3 -0
- package/dist/src-ts/plugins/aun/index.js +67 -37
- package/dist/src-ts/plugins/aun/public/app.js +9 -7
- package/dist/src-ts/plugins/aun/public/index.html +1 -1
- package/dist/src-ts/plugins/claude-cli/index.js +14 -0
- package/dist/src-ts/plugins/codex-cli/index.js +14 -0
- package/dist/src-ts/plugins/feishu/index.js +5 -0
- package/dist/src-ts/plugins/local-cli/agent.js +321 -0
- package/dist/src-ts/plugins/workbuddy/cdp-client.js +51 -0
- package/dist/src-ts/plugins/workbuddy/desktop-launch.js +10 -0
- package/dist/src-ts/plugins/workbuddy/index.js +100 -9
- package/dist/tests-ts/abweb-cli.test.js +65 -0
- package/dist/tests-ts/aun-default-owner-page.test.js +1 -1
- package/dist/tests-ts/aun-plugin.test.js +46 -6
- package/dist/tests-ts/bundled-package.test.js +1 -1
- package/dist/tests-ts/local-cli.test.js +170 -0
- package/dist/tests-ts/platform-integration.test.js +1 -1
- package/dist/tests-ts/workbuddy-plugin.test.js +201 -3
- package/package.json +1 -1
- package/plugins/abweb/dist/src-ts/plugins/abweb/public/app.js +34 -0
- package/plugins/abweb/dist/src-ts/plugins/abweb/public/app.ts +30 -1
- package/plugins/abweb/dist/src-ts/plugins/abweb/public/index.html +10 -0
- package/plugins/abweb/dist/src-ts/plugins/abweb/public/style.css +3 -0
- package/plugins/aun/config.schema.json +1 -1
- package/plugins/aun/dist/src-ts/plugins/aun/index.js +67 -37
- package/plugins/aun/dist/src-ts/plugins/aun/public/app.js +9 -7
- package/plugins/aun/dist/src-ts/plugins/aun/public/index.html +1 -1
- package/plugins/claude-cli/config.schema.json +37 -0
- package/plugins/claude-cli/dist/src-ts/agent.js +10 -0
- package/plugins/claude-cli/dist/src-ts/plugin.js +13 -0
- package/plugins/claude-cli/dist/src-ts/plugins/claude-cli/index.js +14 -0
- package/plugins/claude-cli/dist/src-ts/plugins/local-cli/agent.js +321 -0
- package/plugins/claude-cli/package.json +35 -0
- package/plugins/claude-cli/plugin.json +15 -0
- package/plugins/codex-cli/config.schema.json +37 -0
- package/plugins/codex-cli/dist/src-ts/agent.js +10 -0
- package/plugins/codex-cli/dist/src-ts/plugin.js +13 -0
- package/plugins/codex-cli/dist/src-ts/plugins/codex-cli/index.js +14 -0
- package/plugins/codex-cli/dist/src-ts/plugins/local-cli/agent.js +321 -0
- package/plugins/codex-cli/package.json +35 -0
- package/plugins/codex-cli/plugin.json +15 -0
- package/plugins/dsh/dist/src-ts/headless-account.patch.yml +10 -0
- package/plugins/feishu/dist/src-ts/plugins/feishu/index.js +5 -0
- package/plugins/workbuddy/config.schema.json +4 -1
- package/plugins/workbuddy/dist/src-ts/plugins/workbuddy/cdp-client.js +51 -0
- package/plugins/workbuddy/dist/src-ts/plugins/workbuddy/desktop-launch.js +10 -0
- package/plugins/workbuddy/dist/src-ts/plugins/workbuddy/index.js +100 -9
- package/plugins/workbuddy/package.json +1 -1
package/README.md
CHANGED
|
@@ -162,3 +162,7 @@ mab feishu unbind cli_你的应用ID
|
|
|
162
162
|
```
|
|
163
163
|
|
|
164
164
|
`configure` 保存凭据及可选关联后,需要在 ABWeb 重载飞书插件以连接机器人。非交互终端请使用网页密码框;不要把 Secret 写进命令行、脚本或仓库。
|
|
165
|
+
|
|
166
|
+
### 本地 CLI Agent
|
|
167
|
+
|
|
168
|
+
新增两个独立插件:`agent-bridge-codex-cli`(Codex CLI)与 `agent-bridge-claude-cli`(Claude Code CLI),随核心默认打包。支持在 abweb 配置、启停及通过通道关联 AID,沿用本机 CLI 的登录和默认模型。详见 [本地 CLI 插件使用说明](docs/local-cli-agents.md)。
|
|
@@ -300,10 +300,13 @@ class ControlServer {
|
|
|
300
300
|
const identity = this.identity();
|
|
301
301
|
if (body.aid && !identity)
|
|
302
302
|
return this.json(res, 409, { error: "请先安装并启动 AUN 插件,才能关联 AID" });
|
|
303
|
+
if (!body.aid && identity)
|
|
304
|
+
return this.json(res, 400, { error: "请使用 AUN 身份页指定要解除关联的 AID" });
|
|
303
305
|
try {
|
|
304
306
|
if (identity)
|
|
305
307
|
await identity.bind(id, body.aid, this.address.port);
|
|
306
|
-
|
|
308
|
+
else
|
|
309
|
+
await this.options.manager.setAid(id, body.aid);
|
|
307
310
|
this.options.manager.log("info", "AID 关联已保存", { aid: body.aid, pluginId: id });
|
|
308
311
|
}
|
|
309
312
|
catch (error) {
|
|
@@ -1,4 +1,14 @@
|
|
|
1
1
|
# 复用 DeepSeek Harness 桌面端登录账号的模型。
|
|
2
|
+
# 新版 headless 默认禁用凭据及账号服务,桥接需要显式恢复才能复用桌面登录。
|
|
3
|
+
- id: credentials
|
|
4
|
+
disabled: false
|
|
5
|
+
|
|
6
|
+
- id: authorization
|
|
7
|
+
disabled: false
|
|
8
|
+
|
|
9
|
+
- id: deepseek-account
|
|
10
|
+
disabled: false
|
|
11
|
+
|
|
2
12
|
- id: agent-default-model
|
|
3
13
|
config:
|
|
4
14
|
provider: deepseek-account
|
|
@@ -21,6 +21,8 @@ function validateManifest(value, baseDir) {
|
|
|
21
21
|
throw new agent_1.AgentError("invalid plugin capabilities");
|
|
22
22
|
if (m.type && !["agent", "channel", "management", "other"].includes(m.type))
|
|
23
23
|
throw new agent_1.AgentError("invalid plugin type");
|
|
24
|
+
if (m.executionMode !== undefined && m.executionMode !== "cli" && m.executionMode !== "cdp")
|
|
25
|
+
throw new agent_1.AgentError("插件运行方式无效");
|
|
24
26
|
const inferredType = m.capabilities.includes("channel") ? "channel" : m.capabilities.includes("chat") ? "agent" : m.capabilities.includes("management") ? "management" : "other";
|
|
25
27
|
if (m.type && inferredType !== "other" && m.type !== inferredType)
|
|
26
28
|
throw new agent_1.AgentError("插件类型与能力不匹配");
|
|
@@ -39,5 +41,5 @@ function validateManifest(value, baseDir) {
|
|
|
39
41
|
if ((0, node_path_1.isAbsolute)(m.entry) || rel.startsWith(`..${node_path_1.sep}`) || rel === "..")
|
|
40
42
|
throw new agent_1.AgentError("plugin entry path escapes plugin directory");
|
|
41
43
|
}
|
|
42
|
-
return { id: m.id, version: m.version, displayName: m.displayName ?? m.id, type: m.type ?? inferredType, entry: m.entry, apiVersion: m.apiVersion, process: m.process ?? "in-process", capabilities: [...m.capabilities], configSchema: m.configSchema, routes: m.routes, ...(m.dependencies ? { dependencies: { ...m.dependencies } } : {}), ...(m.managementPage ? { managementPage: m.managementPage } : {}), ...(m.clientDownloadUrl ? { clientDownloadUrl: m.clientDownloadUrl } : {}) };
|
|
44
|
+
return { id: m.id, version: m.version, displayName: m.displayName ?? m.id, type: m.type ?? inferredType, entry: m.entry, apiVersion: m.apiVersion, process: m.process ?? "in-process", capabilities: [...m.capabilities], ...(m.executionMode ? { executionMode: m.executionMode } : {}), configSchema: m.configSchema, routes: m.routes, ...(m.dependencies ? { dependencies: { ...m.dependencies } } : {}), ...(m.managementPage ? { managementPage: m.managementPage } : {}), ...(m.clientDownloadUrl ? { clientDownloadUrl: m.clientDownloadUrl } : {}) };
|
|
43
45
|
}
|
|
@@ -308,6 +308,8 @@
|
|
|
308
308
|
const status = document.createElement("span");
|
|
309
309
|
status.className = `status status-${plugin.status}`;
|
|
310
310
|
status.textContent = plugin.status;
|
|
311
|
+
if (plugin.type === "agent")
|
|
312
|
+
type.textContent += ` · ${{ cli: "CLI", cdp: "CDP" }[plugin.manifest?.executionMode ?? ""] ?? "未声明"}`;
|
|
311
313
|
const dependencies = Object.entries(plugin.manifest?.dependencies ?? {});
|
|
312
314
|
if (dependencies.length) {
|
|
313
315
|
const detail = document.createElement("small");
|
|
@@ -340,6 +342,8 @@
|
|
|
340
342
|
}
|
|
341
343
|
const controls = document.createElement("td");
|
|
342
344
|
controls.className = "controls";
|
|
345
|
+
if (plugin.manifest?.executionMode === "cli")
|
|
346
|
+
controls.append(button("配置", `配置 ${plugin.id}`, () => configureCli(plugin)));
|
|
343
347
|
if (pluginVersions[plugin.id]?.state === "update-available" && plugin.path.includes("node_modules")) {
|
|
344
348
|
controlsUpgrade(plugin.id);
|
|
345
349
|
controls.append(button("下载", `下载 ${plugin.id}`, async () => { await request(`/api/plugins/${encodeURIComponent(plugin.id)}/download`, "POST", { packageName: plugin.packageName, version: pluginVersions[plugin.id]?.latestVersion }); await load(); }));
|
|
@@ -367,6 +371,36 @@
|
|
|
367
371
|
pluginsNode.append(row);
|
|
368
372
|
}
|
|
369
373
|
}
|
|
374
|
+
let configuringCli = "";
|
|
375
|
+
async function configureCli(plugin) {
|
|
376
|
+
const config = await request(`/api/plugins/${encodeURIComponent(plugin.id)}/config`);
|
|
377
|
+
configuringCli = plugin.id;
|
|
378
|
+
node("#cli-config-title").textContent = `${plugin.displayName} 配置`;
|
|
379
|
+
node("#cli-config-cwd").value = config.cwd ?? "";
|
|
380
|
+
node("#cli-config-executable").value = config.executablePath ?? "";
|
|
381
|
+
node("#cli-config-timeout").value = String(config.timeoutMs ?? 240000);
|
|
382
|
+
node("#cli-config-error").hidden = true;
|
|
383
|
+
node("#cli-config-dialog").showModal();
|
|
384
|
+
}
|
|
385
|
+
node("#cli-config-cancel").onclick = () => node("#cli-config-dialog").close();
|
|
386
|
+
node("#cli-config-form").onsubmit = event => {
|
|
387
|
+
event.preventDefault();
|
|
388
|
+
const save = node("#cli-config-save");
|
|
389
|
+
save.disabled = true;
|
|
390
|
+
void request(`/api/plugins/${encodeURIComponent(configuringCli)}/config`, "PATCH", {
|
|
391
|
+
cwd: node("#cli-config-cwd").value.trim(),
|
|
392
|
+
executablePath: node("#cli-config-executable").value.trim(),
|
|
393
|
+
timeoutMs: Number(node("#cli-config-timeout").value),
|
|
394
|
+
}).then(() => {
|
|
395
|
+
node("#cli-config-dialog").close();
|
|
396
|
+
showResult("CLI 配置已保存", "请启动插件,然后在 Channel 设置中关联 AID。", false);
|
|
397
|
+
return load(false);
|
|
398
|
+
}).catch(cause => {
|
|
399
|
+
const detail = node("#cli-config-error");
|
|
400
|
+
detail.textContent = String(cause);
|
|
401
|
+
detail.hidden = false;
|
|
402
|
+
}).finally(() => { save.disabled = false; });
|
|
403
|
+
};
|
|
370
404
|
function renderMessages(messages) {
|
|
371
405
|
messagesNode.replaceChildren();
|
|
372
406
|
messagesEmpty.hidden = messages.length > 0;
|
|
@@ -21,6 +21,16 @@
|
|
|
21
21
|
<div id="logs-panel" role="tabpanel" aria-labelledby="logs-tab" hidden><section><div class="section-heading"><h2>运行日志</h2><button id="logs-refresh" type="button">刷新日志</button></div><div class="table-scroll"><table><thead><tr><th>时间</th><th>级别</th><th>来源</th><th>消息</th><th>详细字段</th></tr></thead><tbody id="logs"></tbody></table></div><p id="logs-empty" class="muted" hidden>暂无运行日志</p><div class="section-heading"><button id="logs-prev" type="button">上一页</button><span id="logs-page" class="muted">第 1 页</span><button id="logs-next" type="button">下一页</button></div></section></div>
|
|
22
22
|
<p id="error" class="error" role="alert" hidden></p>
|
|
23
23
|
</main>
|
|
24
|
+
<dialog id="cli-config-dialog" aria-labelledby="cli-config-title">
|
|
25
|
+
<h2 id="cli-config-title">CLI 配置</h2><p class="muted">沿用后台运行用户的 CLI 登录、默认模型及权限。AID 请在 Channel 设置关联。</p>
|
|
26
|
+
<form id="cli-config-form">
|
|
27
|
+
<label for="cli-config-cwd">工作目录(必填,绝对路径)</label><input id="cli-config-cwd" required placeholder="C:\Users\用户名\projects\demo">
|
|
28
|
+
<label for="cli-config-executable">CLI 程序路径(可选,留空使用 PATH)</label><input id="cli-config-executable" placeholder="例如 C:\Users\用户名\AppData\Roaming\npm\codex.cmd">
|
|
29
|
+
<label for="cli-config-timeout">任务超时(毫秒)</label><input id="cli-config-timeout" type="number" min="1" max="2147483647" required value="240000">
|
|
30
|
+
<p id="cli-config-error" class="error" role="alert" hidden></p>
|
|
31
|
+
<div><button id="cli-config-save" type="submit">保存配置</button><button id="cli-config-cancel" type="button">取消</button></div>
|
|
32
|
+
</form>
|
|
33
|
+
</dialog>
|
|
24
34
|
<dialog id="operation-result" aria-labelledby="operation-result-title" aria-describedby="operation-result-message">
|
|
25
35
|
<h2 id="operation-result-title">操作结果</h2><p id="operation-result-message"></p><button id="operation-result-close" type="button" autofocus>确定</button>
|
|
26
36
|
</dialog>
|
|
@@ -60,3 +60,6 @@ th:nth-child(1) { width: 145px; } th:nth-child(2) { width: 100px; } th:nth-child
|
|
|
60
60
|
#operation-result { width: min(520px, calc(100% - 32px)); border: 1px solid #bfc9c5; border-radius: 8px; padding: 24px; }
|
|
61
61
|
#operation-result::backdrop { background: rgb(0 0 0 / 35%); }
|
|
62
62
|
#operation-result-message { white-space: pre-wrap; overflow-wrap: anywhere; margin: 16px 0; }
|
|
63
|
+
|
|
64
|
+
#cli-config-form { display: grid; min-width: min(70vw, 480px); }
|
|
65
|
+
#cli-config-form input { width: 100%; }
|
|
@@ -41,7 +41,7 @@ const cliCommands = [
|
|
|
41
41
|
{ name: "default-owner", summary: "查询或设置默认 Owner AID", usage: "mab aun default-owner [set <owner-aid>]", examples: ["mab aun default-owner", "mab aun default-owner set owner.agentid.cn"] },
|
|
42
42
|
{ name: "create", summary: "创建并连接 AID,可选关联 Agent", usage: "mab aun create <aid> [--owner <owner-aid>] [--agent <plugin-id>]", examples: ["mab aun create new.agentid.cn --owner owner.agentid.cn --agent qwenwork"] },
|
|
43
43
|
{ name: "bind", summary: "将 AID 关联到 Agent", usage: "mab aun bind <aid> <plugin-id>", examples: ["mab aun bind new.agentid.cn qwenwork"] },
|
|
44
|
-
{ name: "unbind", summary: "
|
|
44
|
+
{ name: "unbind", summary: "解除指定 AID 与 Agent 的关联", usage: "mab aun unbind <aid>", examples: ["mab aun unbind old.agentid.cn"] },
|
|
45
45
|
];
|
|
46
46
|
class AunCommandError extends Error {
|
|
47
47
|
statusCode;
|
|
@@ -140,11 +140,6 @@ class AunPlugin {
|
|
|
140
140
|
await this.connectAid(aid);
|
|
141
141
|
if (!this.clients.has(aid))
|
|
142
142
|
throw new Error("AID 当前未连接");
|
|
143
|
-
for (const [identity, target] of Object.entries(this.bindings))
|
|
144
|
-
if (target === pluginId) {
|
|
145
|
-
delete this.bindings[identity];
|
|
146
|
-
delete this.bindingTimes[identity];
|
|
147
|
-
}
|
|
148
143
|
this.bindings[aid] = pluginId;
|
|
149
144
|
}
|
|
150
145
|
else {
|
|
@@ -157,21 +152,11 @@ class AunPlugin {
|
|
|
157
152
|
await this.connectAid(aid);
|
|
158
153
|
if (!this.clients.has(aid))
|
|
159
154
|
throw new Error("AID 当前未连接");
|
|
160
|
-
for (const [identity, target] of Object.entries(this.bindings))
|
|
161
|
-
if (target === pluginId) {
|
|
162
|
-
delete this.bindings[identity];
|
|
163
|
-
delete this.bindingTimes[identity];
|
|
164
|
-
}
|
|
165
155
|
this.bindings[aid] = pluginId;
|
|
166
156
|
}
|
|
167
157
|
}
|
|
168
|
-
else
|
|
169
|
-
|
|
170
|
-
if (target === pluginId) {
|
|
171
|
-
delete this.bindings[identity];
|
|
172
|
-
delete this.bindingTimes[identity];
|
|
173
|
-
}
|
|
174
|
-
}
|
|
158
|
+
else
|
|
159
|
+
throw new Error("必须指定要关联的 AID");
|
|
175
160
|
if (aid)
|
|
176
161
|
this.bindingTimes[aid] = new Date().toISOString();
|
|
177
162
|
await this.persist();
|
|
@@ -186,6 +171,29 @@ class AunPlugin {
|
|
|
186
171
|
}
|
|
187
172
|
this.context?.logger.info("AUN 身份关联已更新", { aid, pluginId });
|
|
188
173
|
},
|
|
174
|
+
unbind: async (pluginId, aid) => {
|
|
175
|
+
if (!aidPattern.test(aid))
|
|
176
|
+
throw new Error("AID 格式无效");
|
|
177
|
+
if (this.bindings[aid] !== pluginId)
|
|
178
|
+
throw new Error("指定 AID 当前未关联到该 Agent");
|
|
179
|
+
const previous = this.bindings;
|
|
180
|
+
const previousTimes = this.bindingTimes;
|
|
181
|
+
this.bindings = { ...previous };
|
|
182
|
+
this.bindingTimes = { ...previousTimes };
|
|
183
|
+
delete this.bindings[aid];
|
|
184
|
+
delete this.bindingTimes[aid];
|
|
185
|
+
try {
|
|
186
|
+
await this.persist();
|
|
187
|
+
}
|
|
188
|
+
catch (error) {
|
|
189
|
+
this.bindings = previous;
|
|
190
|
+
this.bindingTimes = previousTimes;
|
|
191
|
+
this.config.bindings = previous;
|
|
192
|
+
this.config.binding_times = previousTimes;
|
|
193
|
+
throw error;
|
|
194
|
+
}
|
|
195
|
+
this.context?.logger.info("AUN 单个身份关联已解除", { aid, pluginId });
|
|
196
|
+
},
|
|
189
197
|
};
|
|
190
198
|
async persist() {
|
|
191
199
|
this.config.identities = [...this.identities].sort();
|
|
@@ -214,7 +222,7 @@ class AunPlugin {
|
|
|
214
222
|
if (typeof aid === "string" && aidPattern.test(aid))
|
|
215
223
|
this.createdAids.add(aid);
|
|
216
224
|
}
|
|
217
|
-
for (const key of ["aid", "owner_aid", "aun_path", "encryption_seed", "root_ca_path", "allow_from"
|
|
225
|
+
for (const key of ["aid", "owner_aid", "aun_path", "encryption_seed", "root_ca_path", "allow_from"]) {
|
|
218
226
|
if (this.config[key] !== undefined && typeof this.config[key] !== "string")
|
|
219
227
|
throw new Error(`${key} 必须是字符串`);
|
|
220
228
|
}
|
|
@@ -236,6 +244,7 @@ class AunPlugin {
|
|
|
236
244
|
["POST", "/api/plugins/aun/aid", this.createAid],
|
|
237
245
|
["GET", "/api/plugins/aun/identities", this.listIdentities],
|
|
238
246
|
["POST", "/api/plugins/aun/bind", this.bindAgent],
|
|
247
|
+
["POST", "/api/plugins/aun/unbind", this.unbindAgent],
|
|
239
248
|
["POST", "/api/plugins/aun/settings", this.saveSettings],
|
|
240
249
|
["GET", "/api/plugins/aun/commands", this.listCommands],
|
|
241
250
|
["POST", "/api/plugins/aun/commands", this.runCommand],
|
|
@@ -419,12 +428,12 @@ class AunPlugin {
|
|
|
419
428
|
throw new Error("请求内容过大");
|
|
420
429
|
}
|
|
421
430
|
const body = JSON.parse(raw || "{}");
|
|
422
|
-
for (const key of ["
|
|
431
|
+
for (const key of ["allow_from", "aun_path"])
|
|
423
432
|
if (typeof body[key] !== "string")
|
|
424
433
|
return this.json(response, 400, { error: `${key} 必须是字符串` });
|
|
425
434
|
if (body.encryption_seed !== undefined && typeof body.encryption_seed !== "string")
|
|
426
435
|
return this.json(response, 400, { error: "encryption_seed 必须是字符串" });
|
|
427
|
-
this.config = { ...this.config,
|
|
436
|
+
this.config = { ...this.config, allow_from: body.allow_from, aun_path: body.aun_path, ...(body.encryption_seed ? { encryption_seed: body.encryption_seed } : {}) };
|
|
428
437
|
await this.persist();
|
|
429
438
|
this.context?.logger.info("AUN 通道配置已保存");
|
|
430
439
|
this.json(response, 200, { ok: true });
|
|
@@ -455,6 +464,28 @@ class AunPlugin {
|
|
|
455
464
|
this.json(response, 400, { error: safeError(error) });
|
|
456
465
|
}
|
|
457
466
|
};
|
|
467
|
+
unbindAgent = async (request, response) => {
|
|
468
|
+
try {
|
|
469
|
+
let raw = "";
|
|
470
|
+
for await (const chunk of request) {
|
|
471
|
+
raw += chunk;
|
|
472
|
+
if (raw.length > 65536)
|
|
473
|
+
throw new Error("请求内容过大");
|
|
474
|
+
}
|
|
475
|
+
const body = JSON.parse(raw || "{}");
|
|
476
|
+
const aid = typeof body.aid === "string" ? body.aid.trim() : "";
|
|
477
|
+
if (!aidPattern.test(aid))
|
|
478
|
+
return this.json(response, 400, { error: "AID 格式无效" });
|
|
479
|
+
const pluginId = this.bindings[aid];
|
|
480
|
+
if (!pluginId)
|
|
481
|
+
return this.json(response, 404, { error: "指定 AID 当前未关联 Agent" });
|
|
482
|
+
await this.identity.unbind(pluginId, aid, Number(this.context?.runtime?.ports?.aun ?? 0));
|
|
483
|
+
this.json(response, 200, { ok: true, aid, pluginId });
|
|
484
|
+
}
|
|
485
|
+
catch (error) {
|
|
486
|
+
this.json(response, 400, { error: safeError(error) });
|
|
487
|
+
}
|
|
488
|
+
};
|
|
458
489
|
listCommands = async (_request, response) => {
|
|
459
490
|
response.setHeader("cache-control", "no-store");
|
|
460
491
|
this.json(response, 200, { protocolVersion: 1, plugin: "aun", version: manifest.version, commands: cliCommands });
|
|
@@ -515,7 +546,6 @@ class AunPlugin {
|
|
|
515
546
|
throw new AunCommandError("AID 格式无效");
|
|
516
547
|
this.validatePluginId(pluginId);
|
|
517
548
|
await this.requireAgent(pluginId);
|
|
518
|
-
const replacedAids = (await this.agentBindings(pluginId)).filter((previousAid) => previousAid !== aid);
|
|
519
549
|
try {
|
|
520
550
|
await this.patchAgentAid(pluginId, aid);
|
|
521
551
|
}
|
|
@@ -524,23 +554,25 @@ class AunPlugin {
|
|
|
524
554
|
throw new AunCommandError(`AID 关联请求失败,已回读当前状态:${safeError(error)}`, error instanceof AunCommandError ? error.statusCode : 502, { aid, agent: pluginId, ...(bindingState ? { bindingState } : { bindingState: "unknown" }) });
|
|
525
555
|
}
|
|
526
556
|
return {
|
|
527
|
-
message:
|
|
528
|
-
data: { aid, agent: pluginId
|
|
557
|
+
message: `AID ${aid} 已关联到 ${pluginId}`,
|
|
558
|
+
data: { aid, agent: pluginId },
|
|
529
559
|
};
|
|
530
560
|
}
|
|
531
561
|
if (command === "unbind" && args.length === 1) {
|
|
532
|
-
const [
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
const
|
|
562
|
+
const [aid] = args;
|
|
563
|
+
if (!aidPattern.test(aid))
|
|
564
|
+
throw new AunCommandError("AID 格式无效");
|
|
565
|
+
const pluginId = this.bindings[aid];
|
|
566
|
+
if (!pluginId)
|
|
567
|
+
throw new AunCommandError("指定 AID 当前未关联 Agent");
|
|
536
568
|
try {
|
|
537
|
-
await this.
|
|
569
|
+
await this.identity.unbind(pluginId, aid, 0);
|
|
538
570
|
}
|
|
539
571
|
catch (error) {
|
|
540
572
|
const bindingState = await this.bindingState(pluginId).catch(() => undefined);
|
|
541
|
-
throw new AunCommandError(`解除关联请求失败,已回读当前状态:${safeError(error)}`, error instanceof AunCommandError ? error.statusCode : 502, { agent: pluginId, ...(bindingState ? { bindingState } : { bindingState: "unknown" }) });
|
|
573
|
+
throw new AunCommandError(`解除关联请求失败,已回读当前状态:${safeError(error)}`, error instanceof AunCommandError ? error.statusCode : 502, { aid, agent: pluginId, ...(bindingState ? { bindingState } : { bindingState: "unknown" }) });
|
|
542
574
|
}
|
|
543
|
-
return { message: `已解除 ${
|
|
575
|
+
return { message: `已解除 AID ${aid} 与 ${pluginId} 的关联`, data: { aid, agent: pluginId } };
|
|
544
576
|
}
|
|
545
577
|
if (!["status", "list", "agents", "default-owner", "create", "bind", "unbind"].includes(command ?? ""))
|
|
546
578
|
throw new AunCommandError(`未知 AUN 命令:${command ?? ""}`);
|
|
@@ -604,10 +636,8 @@ class AunPlugin {
|
|
|
604
636
|
const data = { created: true, aid, connection: created.connection ?? "ready" };
|
|
605
637
|
if (created.connection !== "ready")
|
|
606
638
|
throw new AunCommandError("AID 已创建,但连接失败;请先执行 mab aun list 确认,不要重试创建", 502, { ...data, connectionError: created.connectionError });
|
|
607
|
-
let replacedAids = [];
|
|
608
639
|
if (agent) {
|
|
609
640
|
try {
|
|
610
|
-
replacedAids = (await this.agentBindings(agent)).filter((previousAid) => previousAid !== aid);
|
|
611
641
|
await this.patchAgentAid(agent, aid);
|
|
612
642
|
}
|
|
613
643
|
catch (error) {
|
|
@@ -616,8 +646,8 @@ class AunPlugin {
|
|
|
616
646
|
}
|
|
617
647
|
}
|
|
618
648
|
return {
|
|
619
|
-
message: agent ?
|
|
620
|
-
data: { ...data, ...(agent ? { agent
|
|
649
|
+
message: agent ? `AID 已创建并关联到 ${agent}` : "AID 已创建并连接",
|
|
650
|
+
data: { ...data, ...(agent ? { agent } : {}) },
|
|
621
651
|
};
|
|
622
652
|
}
|
|
623
653
|
async agents(readyOnly = true) {
|
|
@@ -741,7 +771,7 @@ class AunPlugin {
|
|
|
741
771
|
}
|
|
742
772
|
};
|
|
743
773
|
async onMessage(payload, aid) {
|
|
744
|
-
if (!this.acceptingMessages)
|
|
774
|
+
if (!this.acceptingMessages || !this.bindings[aid])
|
|
745
775
|
return;
|
|
746
776
|
const message = (0, messages_1.normalizeAunMessage)(payload, aid, typeof this.config.allow_from === "string" ? this.config.allow_from : undefined);
|
|
747
777
|
if (!message || !this.seen.add(message.messageId ? `${aid}:${message.messageId}` : ""))
|
|
@@ -793,7 +823,7 @@ class AunPlugin {
|
|
|
793
823
|
const channelMessage = {
|
|
794
824
|
channel: "aun", recipientId: aid, peerId: message.peerId, messageId: message.messageId, text: message.text, threadId: message.threadId,
|
|
795
825
|
...(attachments.length ? { attachments } : {}),
|
|
796
|
-
agentPlugin: this.bindings[aid]
|
|
826
|
+
agentPlugin: this.bindings[aid],
|
|
797
827
|
reply: async (text) => {
|
|
798
828
|
if (!text.trim())
|
|
799
829
|
return;
|
|
@@ -28,11 +28,7 @@
|
|
|
28
28
|
const [identities, plugins, config] = await Promise.all([request("/api/plugins/aun/identities"), request("/api/plugins"), request("/api/plugins/aun/config")]);
|
|
29
29
|
agents = (plugins.plugins || []).filter((plugin) => plugin.type === "agent");
|
|
30
30
|
if (refreshForm) createForm.elements.ownerAid.value = config.owner_aid || "";
|
|
31
|
-
await Promise.all(agents.map(async (agent) => { agent.configAid = (await request(`/api/plugins/${encodeURIComponent(agent.id)}/config`)).aid || ""; }));
|
|
32
|
-
const target = configForm.elements.agent_plugin;
|
|
33
31
|
if (refreshForm) {
|
|
34
|
-
target.replaceChildren(new Option("默认 Agent", ""), ...agents.map((agent) => new Option(agent.displayName || agent.id, agent.id)));
|
|
35
|
-
target.value = config.agent_plugin || "";
|
|
36
32
|
for (const key of ["allow_from", "aun_path"]) configForm.elements[key].value = config[key] || "";
|
|
37
33
|
configForm.elements.encryption_seed.value = "";
|
|
38
34
|
}
|
|
@@ -43,7 +39,7 @@
|
|
|
43
39
|
for (const aid of values) {
|
|
44
40
|
if (seen.has(aid)) continue;
|
|
45
41
|
seen.add(aid);
|
|
46
|
-
entries.push({ aid, label, group, bound: identities.bindings?.[aid] ||
|
|
42
|
+
entries.push({ aid, label, group, bound: identities.bindings?.[aid] || "", time: Date.parse(identities.bindingTimes?.[aid] || "") || 0 });
|
|
47
43
|
}
|
|
48
44
|
}
|
|
49
45
|
entries.sort((a, b) => Number(Boolean(b.bound)) - Number(Boolean(a.bound)) || a.group - b.group || b.time - a.time || a.aid.localeCompare(b.aid));
|
|
@@ -52,7 +48,7 @@
|
|
|
52
48
|
for (const value of [aid, label, bound || "未关联"]) { const cell = document.createElement("td"); cell.textContent = value; row.append(cell); }
|
|
53
49
|
const cell = document.createElement("td"); const select = document.createElement("select"); select.setAttribute("aria-label", `${aid} 关联 Agent`);
|
|
54
50
|
select.append(new Option("选择 Agent", ""), ...agents.map((agent) => new Option(agent.displayName || agent.id, agent.id)));
|
|
55
|
-
select.value = bound; select.onchange = () => { if (select.value) void bind(aid, select.value); };
|
|
51
|
+
select.value = bound; select.onchange = () => { if (select.value) void bind(aid, select.value); else if (bound) void unbind(aid); };
|
|
56
52
|
cell.append(select); row.append(cell); list.append(row);
|
|
57
53
|
}
|
|
58
54
|
if (!list.children.length) { const row = document.createElement("tr"); const cell = document.createElement("td"); cell.colSpan = 4; cell.textContent = "暂无已连接 AID"; row.append(cell); list.append(row); }
|
|
@@ -66,6 +62,12 @@
|
|
|
66
62
|
await load(); notify("关联成功", `${aid} 已关联 ${pluginId}`);
|
|
67
63
|
} catch (error) { notify("关联失败", errorMessage(error), true); }
|
|
68
64
|
}
|
|
65
|
+
async function unbind(aid) {
|
|
66
|
+
try {
|
|
67
|
+
await request("/api/plugins/aun/unbind", "POST", { aid });
|
|
68
|
+
await load(); notify("解除关联成功", `${aid} 已解除关联`);
|
|
69
|
+
} catch (error) { notify("解除关联失败", errorMessage(error), true); }
|
|
70
|
+
}
|
|
69
71
|
createForm.onsubmit = (event) => { event.preventDefault(); const aid = createForm.elements.aid.value.trim(); const owner = createForm.elements.ownerAid.value.trim();
|
|
70
72
|
show("正在创建 AID 并连接,请稍候……");
|
|
71
73
|
void request("/api/plugins/aun/aid", "POST", { aid, ownerAid: owner }).then((result) => {
|
|
@@ -76,7 +78,7 @@
|
|
|
76
78
|
return load();
|
|
77
79
|
}).catch((error) => notify("创建 AID 失败", errorMessage(error), true));
|
|
78
80
|
};
|
|
79
|
-
configForm.onsubmit = (event) => { event.preventDefault(); const changes = {
|
|
81
|
+
configForm.onsubmit = (event) => { event.preventDefault(); const changes = { allow_from: configForm.elements.allow_from.value, aun_path: configForm.elements.aun_path.value };
|
|
80
82
|
if (configForm.elements.encryption_seed.value) changes.encryption_seed = configForm.elements.encryption_seed.value;
|
|
81
83
|
void request("/api/plugins/aun/settings", "POST", changes).then(() => request("/api/plugins/aun/reload", "POST")).then(() => load()).then(() => notify("保存成功", "配置已保存")).catch((error) => notify("保存配置失败", errorMessage(error), true));
|
|
82
84
|
};
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
<p id="status" role="status"></p>
|
|
8
8
|
<div class="table-scroll"><table><thead><tr><th>AID</th><th>来源</th><th>已关联 Agent</th><th>操作</th></tr></thead><tbody id="identities"></tbody></table></div>
|
|
9
9
|
<form id="create-form"><h2>创建 AID</h2><div class="fields"><label>新 AID<input name="aid" required placeholder="例如:example.agentid.cn"></label><label>Owner AID<input name="ownerAid" placeholder="例如:owner.agentid.cn"><small class="hint">留空时使用已配置的默认 Owner;未配置时需填写。Owner 保存为本地归属配置;不代表 AUN 协议中的 Owner 关系已激活。</small></label></div><button type="submit">创建</button></form>
|
|
10
|
-
<form id="config-form"><h2>通道配置</h2><div class="fields"><label
|
|
10
|
+
<form id="config-form"><h2>通道配置</h2><div class="fields"><label>允许的来源<input name="allow_from"></label><label>AUN 存储路径<input name="aun_path"></label><label>加密种子<input name="encryption_seed" type="password" autocomplete="new-password" placeholder="留空则不修改"></label></div><button type="submit">保存配置</button></form>
|
|
11
11
|
</main>
|
|
12
12
|
<script src="/abweb/channels/aun/app.js"></script>
|
|
13
13
|
</body>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ClaudeCliPlugin = exports.manifest = void 0;
|
|
4
|
+
const agent_1 = require("../local-cli/agent");
|
|
5
|
+
exports.manifest = {
|
|
6
|
+
id: "claude-cli", version: "1.0.0", displayName: "Claude Code CLI", type: "agent", executionMode: "cli",
|
|
7
|
+
entry: "dist/src-ts/plugins/claude-cli/index.js", apiVersion: "1", process: "in-process",
|
|
8
|
+
capabilities: ["chat", "health"], configSchema: "config.schema.json",
|
|
9
|
+
};
|
|
10
|
+
class ClaudeCliPlugin extends agent_1.LocalCliAgent {
|
|
11
|
+
constructor() { super(exports.manifest, "claude"); }
|
|
12
|
+
}
|
|
13
|
+
exports.ClaudeCliPlugin = ClaudeCliPlugin;
|
|
14
|
+
exports.default = ClaudeCliPlugin;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CodexCliPlugin = exports.manifest = void 0;
|
|
4
|
+
const agent_1 = require("../local-cli/agent");
|
|
5
|
+
exports.manifest = {
|
|
6
|
+
id: "codex-cli", version: "1.0.0", displayName: "Codex CLI", type: "agent", executionMode: "cli",
|
|
7
|
+
entry: "dist/src-ts/plugins/codex-cli/index.js", apiVersion: "1", process: "in-process",
|
|
8
|
+
capabilities: ["chat", "health"], configSchema: "config.schema.json",
|
|
9
|
+
};
|
|
10
|
+
class CodexCliPlugin extends agent_1.LocalCliAgent {
|
|
11
|
+
constructor() { super(exports.manifest, "codex"); }
|
|
12
|
+
}
|
|
13
|
+
exports.CodexCliPlugin = CodexCliPlugin;
|
|
14
|
+
exports.default = CodexCliPlugin;
|
|
@@ -43,6 +43,11 @@ class FeishuPlugin {
|
|
|
43
43
|
create: async (aid) => { if (!aid.trim())
|
|
44
44
|
throw new Error("机器人标识不能为空"); return { aid, status: "请通过配置接口提供 app_id 和 app_secret" }; },
|
|
45
45
|
bind: async (pluginId, aid) => { await this.bindBot(aid || String(this.config.app_id || ""), aid ? pluginId : ""); },
|
|
46
|
+
unbind: async (pluginId, aid) => {
|
|
47
|
+
if (this.bindings[aid] !== pluginId)
|
|
48
|
+
throw new FeishuBindingError("指定飞书机器人当前未关联到该 Agent");
|
|
49
|
+
await this.bindBot(aid, "");
|
|
50
|
+
},
|
|
46
51
|
};
|
|
47
52
|
async activate(context) {
|
|
48
53
|
this.context = context;
|