agent-bridge-aun 1.0.30 → 1.0.32
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 +2 -2
- package/config.schema.json +1 -1
- package/dist/src-ts/agent.js +25 -1
- package/dist/src-ts/plugins/aun/attachments.js +5 -2
- package/dist/src-ts/plugins/aun/index.js +108 -53
- package/dist/src-ts/plugins/aun/public/app.js +9 -7
- package/dist/src-ts/plugins/aun/public/index.html +1 -1
- package/package.json +1 -1
- package/plugin.json +1 -1
package/README.md
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
`agent-bridge-aun` 为 mu-agent-bridge 提供 AUN 消息通道,支持使用已有 AID 或通过管理 API 注册 AID,并将收到的文本交给指定 Agent 插件处理。
|
|
4
4
|
|
|
5
|
-
安装后在 ABWeb 的插件管理中启动 AUN
|
|
5
|
+
安装后在 ABWeb 的插件管理中启动 AUN 通道。收到的 AID 消息仅投递给用户在 AUN 身份页中明确关联的 Agent;未关联的 AID 消息会被忽略。旧配置中的 `agent_plugin` 字段不再作为默认路由。配置字段 `owner_aid` 是可选字符串,用作默认 Owner。AID 创建接口为 `POST /api/plugins/aun/aid`,请求内容为 `{"aid":"新AID.agentid.cn","owner_aid":"所有者AID.agentid.cn"}`。
|
|
6
6
|
|
|
7
7
|
创建使用 SDK 的兼容注册流程;Owner AID 只保存为本地归属配置,不代表 AUN 协议 Owner 关系已签名或激活。Gateway 信任根使用随插件提供并校验指纹的 AUN 根证书,不从 Gateway 下载未经验证的 CA 链。
|
|
8
8
|
|
|
9
|
-
核心支持插件命令后,可运行 `mab aun status`、`mab aun list`、`mab aun agents`、`mab aun default-owner`、`mab aun default-owner set <owner-aid>`、`mab aun create <aid> [--owner <owner-aid>] [--agent <plugin-id>]`、`mab aun bind <aid> <plugin-id>` 和 `mab aun unbind <
|
|
9
|
+
核心支持插件命令后,可运行 `mab aun status`、`mab aun list`、`mab aun agents`、`mab aun default-owner`、`mab aun default-owner set <owner-aid>`、`mab aun create <aid> [--owner <owner-aid>] [--agent <plugin-id>]`、`mab aun bind <aid> <plugin-id>` 和 `mab aun unbind <aid>`。关联或解绑只修改指定 AID,不会影响同一 Agent 下的其它 AID,也不会写入 Agent 全局 AID 配置。`default-owner` 不带参数显示当前配置,`default-owner set <owner-aid>` 校验格式并持久化新的默认值。`create` 明确传入 `--owner` 时优先使用该值;省略时回退到同一配置中的 `owner_aid`,两者都没有时返回错误,不会创建 AID。创建新身份时,实际使用的 Owner 会保存为下次默认值。ABWeb 从同一插件配置读取 `owner_aid` 并预填创建表单,因此 CLI 和 ABWeb 共用默认 Owner。Agent 从当前已就绪的插件动态发现。创建后连接或关联失败时,响应会标明 AID 已创建;先用 `mab aun list` 检查,不要重试创建。运行日志不记录原始参数或 Owner AID;默认 Owner 查询会在终端显示当前值。
|
|
10
10
|
|
|
11
11
|
该插件依赖 `@agentunion/fastaun`,要求 Node.js 22.16 或更高版本。
|
|
12
12
|
|
package/config.schema.json
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
"binding_times": { "type": "object", "additionalProperties": { "type": "string" } },
|
|
8
8
|
"aid": { "type": "string", "minLength": 1 },
|
|
9
9
|
"owner_aid": { "type": "string", "minLength": 1 },
|
|
10
|
-
"agent_plugin": { "type": "string", "
|
|
10
|
+
"agent_plugin": { "type": "string", "description": "兼容旧配置;消息只按 AID 的显式关联路由。" },
|
|
11
11
|
"aun_path": { "type": "string", "minLength": 1 },
|
|
12
12
|
"encryption_seed": { "type": "string" },
|
|
13
13
|
"verify_ssl": { "type": "boolean", "default": true },
|
package/dist/src-ts/agent.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.AgentError = void 0;
|
|
3
|
+
exports.AgentOperationError = exports.AgentError = void 0;
|
|
4
|
+
exports.errorDetails = errorDetails;
|
|
4
5
|
class AgentError extends Error {
|
|
5
6
|
constructor(message, options) {
|
|
6
7
|
super(message, options);
|
|
@@ -8,3 +9,26 @@ class AgentError extends Error {
|
|
|
8
9
|
}
|
|
9
10
|
}
|
|
10
11
|
exports.AgentError = AgentError;
|
|
12
|
+
/** 保留底层错误码,避免只留下 fetch failed 或超时这类表层提示。 */
|
|
13
|
+
function errorDetails(error) {
|
|
14
|
+
const parts = [];
|
|
15
|
+
const seen = new Set();
|
|
16
|
+
for (let current = error; current && !seen.has(current) && seen.size < 5;) {
|
|
17
|
+
seen.add(current);
|
|
18
|
+
const value = current;
|
|
19
|
+
const message = typeof value.message === "string" ? value.message : String(current);
|
|
20
|
+
parts.push(`${message}${value.code ? `(${String(value.code)})` : ""}`);
|
|
21
|
+
current = value.cause;
|
|
22
|
+
}
|
|
23
|
+
return parts.join(";原因:") || String(error);
|
|
24
|
+
}
|
|
25
|
+
class AgentOperationError extends AgentError {
|
|
26
|
+
code;
|
|
27
|
+
stage;
|
|
28
|
+
constructor(code, stage, cause) {
|
|
29
|
+
super(`${code}:${stage}失败:${errorDetails(cause)}`, { cause });
|
|
30
|
+
this.code = code;
|
|
31
|
+
this.stage = stage;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
exports.AgentOperationError = AgentOperationError;
|
|
@@ -8,6 +8,7 @@ exports.uploadAunAttachments = uploadAunAttachments;
|
|
|
8
8
|
const node_crypto_1 = require("node:crypto");
|
|
9
9
|
const node_util_1 = require("node:util");
|
|
10
10
|
const attachments_1 = require("../../attachments");
|
|
11
|
+
const agent_1 = require("../../agent");
|
|
11
12
|
const aidPattern = /^[a-z0-9][a-z0-9_-]{2,63}\.[a-z0-9-]+(?:\.[a-z0-9-]+)+$/;
|
|
12
13
|
const mimePattern = /^[A-Za-z0-9!#$&^_.+-]+\/[A-Za-z0-9!#$&^_.+-]+$/;
|
|
13
14
|
const imageTypes = new Set(["image/png", "image/jpeg", "image/webp"]);
|
|
@@ -252,13 +253,15 @@ async function uploadAunAttachments(client, ownerAid, peerAid, items) {
|
|
|
252
253
|
// 视频播放器不携带 AID 凭据,沿用 EvolCore 的标准可读对象上传方式。
|
|
253
254
|
const video = contentType.startsWith("video/");
|
|
254
255
|
const ref = { ownerAid, bucket: "default", objectKey, filename, contentType, sizeBytes: item.data.byteLength, sha256 };
|
|
255
|
-
const uploaded = await client.storage.writeBytes(`/${objectKey}`, item.data, { contentType, public: video, overwrite: video })
|
|
256
|
+
const uploaded = await client.storage.writeBytes(`/${objectKey}`, item.data, { contentType, public: video, overwrite: video })
|
|
257
|
+
.catch(error => { throw new agent_1.AgentOperationError("AUN_FS_UPLOAD_FAILED", `上传 AUN FS 文件“${filename}”`, error); });
|
|
256
258
|
checkedMetadata({ ...uploaded, size: uploaded.size ?? uploaded.size_bytes ?? item.data.byteLength }, ref);
|
|
257
259
|
if (video) {
|
|
258
260
|
result.push({ owner_aid: ownerAid, object_key: objectKey, filename, content_type: contentType, size_bytes: item.data.byteLength, sha256 });
|
|
259
261
|
continue;
|
|
260
262
|
}
|
|
261
|
-
const share = await client.storage.lowlevel.createShareLink({ owner: ownerAid, bucket: "default", objectKey, allowedAids: [peerAid], expireInSeconds: 86400 })
|
|
263
|
+
const share = await client.storage.lowlevel.createShareLink({ owner: ownerAid, bucket: "default", objectKey, allowedAids: [peerAid], expireInSeconds: 86400 })
|
|
264
|
+
.catch(error => { throw new agent_1.AgentOperationError("AUN_FS_SHARE_FAILED", `创建 AUN FS 文件“${filename}”的接收方授权`, error); });
|
|
262
265
|
if (share.owner_aid && share.owner_aid !== ownerAid || share.bucket && share.bucket !== "default" || share.object_key && share.object_key !== objectKey)
|
|
263
266
|
throw new Error("AUN 附件分享对象不一致");
|
|
264
267
|
if (share.allowed_aids !== undefined && (!Array.isArray(share.allowed_aids) || share.allowed_aids.length !== 1 || share.allowed_aids[0] !== peerAid))
|
|
@@ -8,10 +8,11 @@ const node_module_1 = require("node:module");
|
|
|
8
8
|
const node_path_1 = require("node:path");
|
|
9
9
|
const node_os_1 = require("node:os");
|
|
10
10
|
const node_url_1 = require("node:url");
|
|
11
|
+
const agent_1 = require("../../agent");
|
|
11
12
|
const messages_1 = require("./messages");
|
|
12
13
|
const attachments_1 = require("./attachments");
|
|
13
14
|
const manifest = {
|
|
14
|
-
id: "aun", version: "1.0.
|
|
15
|
+
id: "aun", version: "1.0.32", type: "channel", displayName: "AUN 通道",
|
|
15
16
|
entry: "dist/src-ts/plugins/aun/index.js", apiVersion: "1", process: "in-process",
|
|
16
17
|
capabilities: ["channel", "channel.identity.create", "channel.receive", "channel.send", "health", "cli"],
|
|
17
18
|
configSchema: "config.schema.json",
|
|
@@ -41,7 +42,7 @@ const cliCommands = [
|
|
|
41
42
|
{ 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
43
|
{ 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
44
|
{ name: "bind", summary: "将 AID 关联到 Agent", usage: "mab aun bind <aid> <plugin-id>", examples: ["mab aun bind new.agentid.cn qwenwork"] },
|
|
44
|
-
{ name: "unbind", summary: "
|
|
45
|
+
{ name: "unbind", summary: "解除指定 AID 与 Agent 的关联", usage: "mab aun unbind <aid>", examples: ["mab aun unbind old.agentid.cn"] },
|
|
45
46
|
];
|
|
46
47
|
class AunCommandError extends Error {
|
|
47
48
|
statusCode;
|
|
@@ -140,11 +141,6 @@ class AunPlugin {
|
|
|
140
141
|
await this.connectAid(aid);
|
|
141
142
|
if (!this.clients.has(aid))
|
|
142
143
|
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
144
|
this.bindings[aid] = pluginId;
|
|
149
145
|
}
|
|
150
146
|
else {
|
|
@@ -157,21 +153,11 @@ class AunPlugin {
|
|
|
157
153
|
await this.connectAid(aid);
|
|
158
154
|
if (!this.clients.has(aid))
|
|
159
155
|
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
156
|
this.bindings[aid] = pluginId;
|
|
166
157
|
}
|
|
167
158
|
}
|
|
168
|
-
else
|
|
169
|
-
|
|
170
|
-
if (target === pluginId) {
|
|
171
|
-
delete this.bindings[identity];
|
|
172
|
-
delete this.bindingTimes[identity];
|
|
173
|
-
}
|
|
174
|
-
}
|
|
159
|
+
else
|
|
160
|
+
throw new Error("必须指定要关联的 AID");
|
|
175
161
|
if (aid)
|
|
176
162
|
this.bindingTimes[aid] = new Date().toISOString();
|
|
177
163
|
await this.persist();
|
|
@@ -186,6 +172,29 @@ class AunPlugin {
|
|
|
186
172
|
}
|
|
187
173
|
this.context?.logger.info("AUN 身份关联已更新", { aid, pluginId });
|
|
188
174
|
},
|
|
175
|
+
unbind: async (pluginId, aid) => {
|
|
176
|
+
if (!aidPattern.test(aid))
|
|
177
|
+
throw new Error("AID 格式无效");
|
|
178
|
+
if (this.bindings[aid] !== pluginId)
|
|
179
|
+
throw new Error("指定 AID 当前未关联到该 Agent");
|
|
180
|
+
const previous = this.bindings;
|
|
181
|
+
const previousTimes = this.bindingTimes;
|
|
182
|
+
this.bindings = { ...previous };
|
|
183
|
+
this.bindingTimes = { ...previousTimes };
|
|
184
|
+
delete this.bindings[aid];
|
|
185
|
+
delete this.bindingTimes[aid];
|
|
186
|
+
try {
|
|
187
|
+
await this.persist();
|
|
188
|
+
}
|
|
189
|
+
catch (error) {
|
|
190
|
+
this.bindings = previous;
|
|
191
|
+
this.bindingTimes = previousTimes;
|
|
192
|
+
this.config.bindings = previous;
|
|
193
|
+
this.config.binding_times = previousTimes;
|
|
194
|
+
throw error;
|
|
195
|
+
}
|
|
196
|
+
this.context?.logger.info("AUN 单个身份关联已解除", { aid, pluginId });
|
|
197
|
+
},
|
|
189
198
|
};
|
|
190
199
|
async persist() {
|
|
191
200
|
this.config.identities = [...this.identities].sort();
|
|
@@ -214,7 +223,7 @@ class AunPlugin {
|
|
|
214
223
|
if (typeof aid === "string" && aidPattern.test(aid))
|
|
215
224
|
this.createdAids.add(aid);
|
|
216
225
|
}
|
|
217
|
-
for (const key of ["aid", "owner_aid", "aun_path", "encryption_seed", "root_ca_path", "allow_from"
|
|
226
|
+
for (const key of ["aid", "owner_aid", "aun_path", "encryption_seed", "root_ca_path", "allow_from"]) {
|
|
218
227
|
if (this.config[key] !== undefined && typeof this.config[key] !== "string")
|
|
219
228
|
throw new Error(`${key} 必须是字符串`);
|
|
220
229
|
}
|
|
@@ -236,6 +245,7 @@ class AunPlugin {
|
|
|
236
245
|
["POST", "/api/plugins/aun/aid", this.createAid],
|
|
237
246
|
["GET", "/api/plugins/aun/identities", this.listIdentities],
|
|
238
247
|
["POST", "/api/plugins/aun/bind", this.bindAgent],
|
|
248
|
+
["POST", "/api/plugins/aun/unbind", this.unbindAgent],
|
|
239
249
|
["POST", "/api/plugins/aun/settings", this.saveSettings],
|
|
240
250
|
["GET", "/api/plugins/aun/commands", this.listCommands],
|
|
241
251
|
["POST", "/api/plugins/aun/commands", this.runCommand],
|
|
@@ -419,12 +429,12 @@ class AunPlugin {
|
|
|
419
429
|
throw new Error("请求内容过大");
|
|
420
430
|
}
|
|
421
431
|
const body = JSON.parse(raw || "{}");
|
|
422
|
-
for (const key of ["
|
|
432
|
+
for (const key of ["allow_from", "aun_path"])
|
|
423
433
|
if (typeof body[key] !== "string")
|
|
424
434
|
return this.json(response, 400, { error: `${key} 必须是字符串` });
|
|
425
435
|
if (body.encryption_seed !== undefined && typeof body.encryption_seed !== "string")
|
|
426
436
|
return this.json(response, 400, { error: "encryption_seed 必须是字符串" });
|
|
427
|
-
this.config = { ...this.config,
|
|
437
|
+
this.config = { ...this.config, allow_from: body.allow_from, aun_path: body.aun_path, ...(body.encryption_seed ? { encryption_seed: body.encryption_seed } : {}) };
|
|
428
438
|
await this.persist();
|
|
429
439
|
this.context?.logger.info("AUN 通道配置已保存");
|
|
430
440
|
this.json(response, 200, { ok: true });
|
|
@@ -455,6 +465,28 @@ class AunPlugin {
|
|
|
455
465
|
this.json(response, 400, { error: safeError(error) });
|
|
456
466
|
}
|
|
457
467
|
};
|
|
468
|
+
unbindAgent = async (request, response) => {
|
|
469
|
+
try {
|
|
470
|
+
let raw = "";
|
|
471
|
+
for await (const chunk of request) {
|
|
472
|
+
raw += chunk;
|
|
473
|
+
if (raw.length > 65536)
|
|
474
|
+
throw new Error("请求内容过大");
|
|
475
|
+
}
|
|
476
|
+
const body = JSON.parse(raw || "{}");
|
|
477
|
+
const aid = typeof body.aid === "string" ? body.aid.trim() : "";
|
|
478
|
+
if (!aidPattern.test(aid))
|
|
479
|
+
return this.json(response, 400, { error: "AID 格式无效" });
|
|
480
|
+
const pluginId = this.bindings[aid];
|
|
481
|
+
if (!pluginId)
|
|
482
|
+
return this.json(response, 404, { error: "指定 AID 当前未关联 Agent" });
|
|
483
|
+
await this.identity.unbind(pluginId, aid, Number(this.context?.runtime?.ports?.aun ?? 0));
|
|
484
|
+
this.json(response, 200, { ok: true, aid, pluginId });
|
|
485
|
+
}
|
|
486
|
+
catch (error) {
|
|
487
|
+
this.json(response, 400, { error: safeError(error) });
|
|
488
|
+
}
|
|
489
|
+
};
|
|
458
490
|
listCommands = async (_request, response) => {
|
|
459
491
|
response.setHeader("cache-control", "no-store");
|
|
460
492
|
this.json(response, 200, { protocolVersion: 1, plugin: "aun", version: manifest.version, commands: cliCommands });
|
|
@@ -515,7 +547,6 @@ class AunPlugin {
|
|
|
515
547
|
throw new AunCommandError("AID 格式无效");
|
|
516
548
|
this.validatePluginId(pluginId);
|
|
517
549
|
await this.requireAgent(pluginId);
|
|
518
|
-
const replacedAids = (await this.agentBindings(pluginId)).filter((previousAid) => previousAid !== aid);
|
|
519
550
|
try {
|
|
520
551
|
await this.patchAgentAid(pluginId, aid);
|
|
521
552
|
}
|
|
@@ -524,23 +555,25 @@ class AunPlugin {
|
|
|
524
555
|
throw new AunCommandError(`AID 关联请求失败,已回读当前状态:${safeError(error)}`, error instanceof AunCommandError ? error.statusCode : 502, { aid, agent: pluginId, ...(bindingState ? { bindingState } : { bindingState: "unknown" }) });
|
|
525
556
|
}
|
|
526
557
|
return {
|
|
527
|
-
message:
|
|
528
|
-
data: { aid, agent: pluginId
|
|
558
|
+
message: `AID ${aid} 已关联到 ${pluginId}`,
|
|
559
|
+
data: { aid, agent: pluginId },
|
|
529
560
|
};
|
|
530
561
|
}
|
|
531
562
|
if (command === "unbind" && args.length === 1) {
|
|
532
|
-
const [
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
const
|
|
563
|
+
const [aid] = args;
|
|
564
|
+
if (!aidPattern.test(aid))
|
|
565
|
+
throw new AunCommandError("AID 格式无效");
|
|
566
|
+
const pluginId = this.bindings[aid];
|
|
567
|
+
if (!pluginId)
|
|
568
|
+
throw new AunCommandError("指定 AID 当前未关联 Agent");
|
|
536
569
|
try {
|
|
537
|
-
await this.
|
|
570
|
+
await this.identity.unbind(pluginId, aid, 0);
|
|
538
571
|
}
|
|
539
572
|
catch (error) {
|
|
540
573
|
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" }) });
|
|
574
|
+
throw new AunCommandError(`解除关联请求失败,已回读当前状态:${safeError(error)}`, error instanceof AunCommandError ? error.statusCode : 502, { aid, agent: pluginId, ...(bindingState ? { bindingState } : { bindingState: "unknown" }) });
|
|
542
575
|
}
|
|
543
|
-
return { message: `已解除 ${
|
|
576
|
+
return { message: `已解除 AID ${aid} 与 ${pluginId} 的关联`, data: { aid, agent: pluginId } };
|
|
544
577
|
}
|
|
545
578
|
if (!["status", "list", "agents", "default-owner", "create", "bind", "unbind"].includes(command ?? ""))
|
|
546
579
|
throw new AunCommandError(`未知 AUN 命令:${command ?? ""}`);
|
|
@@ -604,10 +637,8 @@ class AunPlugin {
|
|
|
604
637
|
const data = { created: true, aid, connection: created.connection ?? "ready" };
|
|
605
638
|
if (created.connection !== "ready")
|
|
606
639
|
throw new AunCommandError("AID 已创建,但连接失败;请先执行 mab aun list 确认,不要重试创建", 502, { ...data, connectionError: created.connectionError });
|
|
607
|
-
let replacedAids = [];
|
|
608
640
|
if (agent) {
|
|
609
641
|
try {
|
|
610
|
-
replacedAids = (await this.agentBindings(agent)).filter((previousAid) => previousAid !== aid);
|
|
611
642
|
await this.patchAgentAid(agent, aid);
|
|
612
643
|
}
|
|
613
644
|
catch (error) {
|
|
@@ -616,8 +647,8 @@ class AunPlugin {
|
|
|
616
647
|
}
|
|
617
648
|
}
|
|
618
649
|
return {
|
|
619
|
-
message: agent ?
|
|
620
|
-
data: { ...data, ...(agent ? { agent
|
|
650
|
+
message: agent ? `AID 已创建并关联到 ${agent}` : "AID 已创建并连接",
|
|
651
|
+
data: { ...data, ...(agent ? { agent } : {}) },
|
|
621
652
|
};
|
|
622
653
|
}
|
|
623
654
|
async agents(readyOnly = true) {
|
|
@@ -671,7 +702,7 @@ class AunPlugin {
|
|
|
671
702
|
method,
|
|
672
703
|
headers: { accept: "application/json", ...(body === undefined ? {} : { "content-type": "application/json" }) },
|
|
673
704
|
...(body === undefined ? {} : { body: JSON.stringify(body) }),
|
|
674
|
-
signal: AbortSignal.timeout(5000),
|
|
705
|
+
signal: AbortSignal.timeout(method === "PATCH" ? 120_000 : 5000),
|
|
675
706
|
redirect: "error",
|
|
676
707
|
});
|
|
677
708
|
}
|
|
@@ -741,7 +772,8 @@ class AunPlugin {
|
|
|
741
772
|
}
|
|
742
773
|
};
|
|
743
774
|
async onMessage(payload, aid) {
|
|
744
|
-
|
|
775
|
+
const agentPlugin = this.bindings[aid];
|
|
776
|
+
if (!this.acceptingMessages || !agentPlugin)
|
|
745
777
|
return;
|
|
746
778
|
const message = (0, messages_1.normalizeAunMessage)(payload, aid, typeof this.config.allow_from === "string" ? this.config.allow_from : undefined);
|
|
747
779
|
if (!message || !this.seen.add(message.messageId ? `${aid}:${message.messageId}` : ""))
|
|
@@ -757,7 +789,7 @@ class AunPlugin {
|
|
|
757
789
|
}
|
|
758
790
|
return;
|
|
759
791
|
}
|
|
760
|
-
const current = (pending ?? Promise.resolve()).catch(() => undefined).then(() => this.processMessage(message, aid));
|
|
792
|
+
const current = (pending ?? Promise.resolve()).catch(() => undefined).then(() => this.processMessage(message, aid, agentPlugin));
|
|
761
793
|
this.pendingMessages.set(sourceKey, current);
|
|
762
794
|
try {
|
|
763
795
|
await current;
|
|
@@ -767,12 +799,24 @@ class AunPlugin {
|
|
|
767
799
|
this.pendingMessages.delete(sourceKey);
|
|
768
800
|
}
|
|
769
801
|
}
|
|
770
|
-
async processMessage(message, aid) {
|
|
771
|
-
let
|
|
802
|
+
async processMessage(message, aid, agentPlugin) {
|
|
803
|
+
let deliveryFailed = false;
|
|
804
|
+
const trace = { aid, peer: message.peerId, threadId: message.threadId ?? "", messageId: message.messageId ?? "", agentPlugin };
|
|
805
|
+
this.context?.logger.info("AUN 收到任务", { ...trace, stage: "接收消息", attachments: message.attachments?.length ?? 0 });
|
|
772
806
|
const client = this.clients.get(aid)?.client;
|
|
773
807
|
let attachments = [];
|
|
774
808
|
let uploadedItems;
|
|
775
809
|
let uploadedRefs;
|
|
810
|
+
if (this.context?.pluginStatus(agentPlugin) !== "ready") {
|
|
811
|
+
this.context?.logger.warn("AUN 关联 Agent 尚未就绪,消息未转发", { aid, agentPlugin, status: this.context?.pluginStatus(agentPlugin) });
|
|
812
|
+
try {
|
|
813
|
+
await this.sendText(aid, message.peerId, `绑定的 Agent 插件 ${agentPlugin} 尚未就绪,消息未转发;AID 关联保持不变。`, message.threadId);
|
|
814
|
+
}
|
|
815
|
+
catch (error) {
|
|
816
|
+
this.context?.logger.warn("AUN Agent 未就绪提示发送失败", { aid, agentPlugin, error: safeError(error) });
|
|
817
|
+
}
|
|
818
|
+
return;
|
|
819
|
+
}
|
|
776
820
|
try {
|
|
777
821
|
if (message.attachmentError)
|
|
778
822
|
throw new Error(message.attachmentError);
|
|
@@ -783,9 +827,9 @@ class AunPlugin {
|
|
|
783
827
|
}
|
|
784
828
|
}
|
|
785
829
|
catch (error) {
|
|
786
|
-
this.context?.logger.warn("AUN 附件下载失败", { error: safeError(error), ...(error instanceof attachments_1.AunDownloadUrlError ? { downloadUrl: error.diagnostic } : {}) });
|
|
830
|
+
this.context?.logger.warn("AUN 附件下载失败", { ...trace, stage: "下载入站附件", error: safeError((0, agent_1.errorDetails)(error)), ...(error instanceof attachments_1.AunDownloadUrlError ? { downloadUrl: error.diagnostic } : {}) });
|
|
787
831
|
try {
|
|
788
|
-
await this.sendText(aid, message.peerId,
|
|
832
|
+
await this.sendText(aid, message.peerId, `附件接收失败:下载入站附件失败:${safeError((0, agent_1.errorDetails)(error))}`, message.threadId);
|
|
789
833
|
}
|
|
790
834
|
catch { /* 尽力反馈 */ }
|
|
791
835
|
return;
|
|
@@ -793,17 +837,22 @@ class AunPlugin {
|
|
|
793
837
|
const channelMessage = {
|
|
794
838
|
channel: "aun", recipientId: aid, peerId: message.peerId, messageId: message.messageId, text: message.text, threadId: message.threadId,
|
|
795
839
|
...(attachments.length ? { attachments } : {}),
|
|
796
|
-
agentPlugin
|
|
840
|
+
agentPlugin,
|
|
797
841
|
reply: async (text) => {
|
|
798
842
|
if (!text.trim())
|
|
799
843
|
return;
|
|
800
|
-
|
|
801
|
-
|
|
844
|
+
try {
|
|
845
|
+
await this.sendText(aid, message.peerId, text, message.threadId);
|
|
846
|
+
this.context?.logger.info("AUN 文本已发送", { ...trace, stage: "发送文本消息" });
|
|
847
|
+
}
|
|
848
|
+
catch (error) {
|
|
849
|
+
deliveryFailed = true;
|
|
850
|
+
throw new agent_1.AgentOperationError("AUN_TEXT_SEND_FAILED", "发送 AUN 文本消息(结果未确认,不自动重发)", error);
|
|
851
|
+
}
|
|
802
852
|
},
|
|
803
853
|
replyAttachments: async (items, text) => {
|
|
804
854
|
if (!items.length)
|
|
805
855
|
return;
|
|
806
|
-
replyAttempted = true;
|
|
807
856
|
let sent = 0;
|
|
808
857
|
let groupsCount = 0;
|
|
809
858
|
let sending = false;
|
|
@@ -811,6 +860,7 @@ class AunPlugin {
|
|
|
811
860
|
if (!client?.storage)
|
|
812
861
|
throw new Error("AUN SDK 不支持附件存储接口");
|
|
813
862
|
const refs = uploadedRefs && uploadedItems === items ? uploadedRefs : await (0, attachments_1.uploadAunAttachments)({ storage: client.storage }, aid, message.peerId, items);
|
|
863
|
+
this.context?.logger.info("AUN FS 文件上传与授权完成", { ...trace, stage: "上传并授权附件", files: items.map(item => ({ filename: item.filename, sizeBytes: item.data.byteLength })) });
|
|
814
864
|
if (!uploadedRefs || uploadedItems !== items) {
|
|
815
865
|
uploadedItems = items;
|
|
816
866
|
uploadedRefs = refs;
|
|
@@ -832,32 +882,37 @@ class AunPlugin {
|
|
|
832
882
|
sending = true;
|
|
833
883
|
// message.send 失败时可能已送达,不自动重试或降级明文,避免重复发送和泄露引用。
|
|
834
884
|
await client.call("message.send", { to: message.peerId, payload, encrypt: true });
|
|
885
|
+
this.context?.logger.info("AUN 附件消息已发送", { ...trace, stage: "发送附件消息", kind, count: group.length });
|
|
835
886
|
sending = false;
|
|
836
887
|
sent++;
|
|
837
888
|
}
|
|
838
889
|
}
|
|
839
890
|
catch (error) {
|
|
891
|
+
deliveryFailed = true;
|
|
892
|
+
const failure = sending ? new agent_1.AgentOperationError("AUN_ATTACHMENT_SEND_FAILED", "发送 AUN 附件消息(FS 上传已完成)", error) : error;
|
|
840
893
|
const progress = sending ? `(已确认发送 ${sent}/${groupsCount} 组,当前组结果未确认,请勿重复发送)` : "";
|
|
841
894
|
try {
|
|
842
|
-
await this.sendText(aid, message.peerId, `附件回传失败${progress}:${safeError(
|
|
895
|
+
await this.sendText(aid, message.peerId, `附件回传失败${progress}:${safeError((0, agent_1.errorDetails)(failure))}`, message.threadId);
|
|
843
896
|
}
|
|
844
897
|
catch { /* 尽力反馈 */ }
|
|
845
|
-
throw
|
|
898
|
+
throw failure;
|
|
846
899
|
}
|
|
847
900
|
},
|
|
848
901
|
};
|
|
849
902
|
try {
|
|
903
|
+
this.context?.logger.info("AUN 开始调用 Agent", { ...trace, stage: "调用 Agent" });
|
|
850
904
|
await this.ingress.publish(channelMessage);
|
|
905
|
+
this.context?.logger.info("AUN 任务处理完成", { ...trace, stage: "回复已发送" });
|
|
851
906
|
}
|
|
852
907
|
catch (error) {
|
|
853
|
-
this.context?.logger.warn("AUN 消息处理失败", { error: safeError(error) });
|
|
854
|
-
if (!
|
|
908
|
+
this.context?.logger.warn("AUN 消息处理失败", { ...trace, stage: deliveryFailed ? "回传回复" : "执行 Agent 任务", error: safeError((0, agent_1.errorDetails)(error)) });
|
|
909
|
+
if (!deliveryFailed) {
|
|
855
910
|
const action = attachments.length > 0 ? "附件处理失败" : newSessionCommand.test(message.text.trim()) ? "新建会话失败" : "消息处理失败";
|
|
856
911
|
try {
|
|
857
|
-
await channelMessage.reply(`${action}:${safeError(error)}`);
|
|
912
|
+
await channelMessage.reply(`${action}:${safeError((0, agent_1.errorDetails)(error))}`);
|
|
858
913
|
}
|
|
859
914
|
catch (replyError) {
|
|
860
|
-
this.context?.logger.warn("AUN 错误回复失败", { error: safeError(replyError) });
|
|
915
|
+
this.context?.logger.warn("AUN 错误回复失败", { ...trace, stage: "发送最终错误消息", error: safeError((0, agent_1.errorDetails)(replyError)) });
|
|
861
916
|
}
|
|
862
917
|
}
|
|
863
918
|
}
|
|
@@ -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>
|
package/package.json
CHANGED