agent-bridge-aun 1.0.35 → 1.0.36
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/dist/src-ts/agent.js
CHANGED
|
@@ -1,7 +1,42 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.AgentOperationError = exports.AgentError = void 0;
|
|
4
|
+
exports.abortable = abortable;
|
|
4
5
|
exports.errorDetails = errorDetails;
|
|
6
|
+
async function abortable(promise, signal) {
|
|
7
|
+
if (!signal)
|
|
8
|
+
return promise;
|
|
9
|
+
if (signal.aborted) {
|
|
10
|
+
// promise 已在调用方启动,先观察其拒绝,避免预取消时产生未处理拒绝。
|
|
11
|
+
void promise.catch(() => undefined);
|
|
12
|
+
signal.throwIfAborted();
|
|
13
|
+
}
|
|
14
|
+
return new Promise((resolve, reject) => {
|
|
15
|
+
let settled = false;
|
|
16
|
+
const cleanup = () => signal.removeEventListener("abort", abort);
|
|
17
|
+
const abort = () => {
|
|
18
|
+
if (settled)
|
|
19
|
+
return;
|
|
20
|
+
settled = true;
|
|
21
|
+
cleanup();
|
|
22
|
+
reject(signal.reason);
|
|
23
|
+
};
|
|
24
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
25
|
+
promise.then((value) => {
|
|
26
|
+
if (settled)
|
|
27
|
+
return;
|
|
28
|
+
settled = true;
|
|
29
|
+
cleanup();
|
|
30
|
+
resolve(value);
|
|
31
|
+
}, (error) => {
|
|
32
|
+
if (settled)
|
|
33
|
+
return;
|
|
34
|
+
settled = true;
|
|
35
|
+
cleanup();
|
|
36
|
+
reject(error);
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
}
|
|
5
40
|
class AgentError extends Error {
|
|
6
41
|
constructor(message, options) {
|
|
7
42
|
super(message, options);
|
|
@@ -13,13 +13,31 @@ const evolcore_1 = require("./evolcore");
|
|
|
13
13
|
const messages_1 = require("./messages");
|
|
14
14
|
const attachments_1 = require("./attachments");
|
|
15
15
|
const manifest = {
|
|
16
|
-
id: "aun", version: "1.0.
|
|
16
|
+
id: "aun", version: "1.0.36", type: "channel", displayName: "AUN 通道",
|
|
17
17
|
entry: "dist/src-ts/plugins/aun/index.js", apiVersion: "1", process: "in-process",
|
|
18
18
|
capabilities: ["channel", "channel.identity.create", "channel.receive", "channel.send", "health", "cli"],
|
|
19
19
|
configSchema: "config.schema.json",
|
|
20
20
|
managementPage: "/abweb/channels/aun",
|
|
21
21
|
};
|
|
22
22
|
const newSessionCommand = /^\/new(?:[ \t]+[^\r\n]+)?$/;
|
|
23
|
+
const successfulSendStatuses = new Set(["accepted", "sent", "delivered", "duplicate"]);
|
|
24
|
+
function attachmentDownloadLinks(refs) {
|
|
25
|
+
return refs.flatMap((ref) => typeof ref.url === "string" && ref.url ? [ref.url] : []);
|
|
26
|
+
}
|
|
27
|
+
function withAttachmentDownloadLinks(text, kind, refs) {
|
|
28
|
+
if (kind !== "image")
|
|
29
|
+
return text;
|
|
30
|
+
const links = attachmentDownloadLinks(refs);
|
|
31
|
+
if (!links.length)
|
|
32
|
+
return text;
|
|
33
|
+
return `${text || "图片已生成。"}\n图片下载链接(预览不可用时):\n${links.join("\n")}`;
|
|
34
|
+
}
|
|
35
|
+
function withFailureDownloadLinks(text, refs) {
|
|
36
|
+
const links = attachmentDownloadLinks(refs);
|
|
37
|
+
if (!links.length || text?.includes("下载链接"))
|
|
38
|
+
return text;
|
|
39
|
+
return `${text || "附件已生成。"}\n附件下载链接(消息未确认时):\n${links.join("\n")}`;
|
|
40
|
+
}
|
|
23
41
|
async function loadSdk() {
|
|
24
42
|
const resolver = (0, node_module_1.createRequire)(__filename);
|
|
25
43
|
const dynamicImport = new Function("url", "return import(url)");
|
|
@@ -81,6 +99,60 @@ class AunPlugin {
|
|
|
81
99
|
this.sdk = options.sdk;
|
|
82
100
|
this.ingress = options.ingress;
|
|
83
101
|
}
|
|
102
|
+
aunManagement = {
|
|
103
|
+
list: async () => {
|
|
104
|
+
const groups = await this.identity.list();
|
|
105
|
+
const aids = [...new Set([...groups.evolcore, ...groups.muAgentBridge, ...(groups.localMuAgentBridge ?? []), ...this.listStoreAids()])]
|
|
106
|
+
.filter((aid) => aidPattern.test(aid)).sort();
|
|
107
|
+
const bindings = Object.fromEntries(Object.entries(this.bindings)
|
|
108
|
+
.filter(([aid, agentId]) => aidPattern.test(aid) && /^[a-z][a-z0-9-]{1,63}$/.test(agentId) && agentId !== "aun" && agentId !== "abweb")
|
|
109
|
+
.sort(([left], [right]) => left.localeCompare(right)));
|
|
110
|
+
return { aids, bindings };
|
|
111
|
+
},
|
|
112
|
+
create: async ({ aid, ownerAid }) => {
|
|
113
|
+
if (typeof aid !== "string" || !aidPattern.test(aid.trim()))
|
|
114
|
+
throw new AunCommandError("AID 格式无效");
|
|
115
|
+
const validAid = aid.trim();
|
|
116
|
+
const owner = typeof ownerAid === "string" && ownerAid.trim()
|
|
117
|
+
? ownerAid.trim()
|
|
118
|
+
: typeof this.config.owner_aid === "string" ? this.config.owner_aid.trim() : "";
|
|
119
|
+
if (!owner)
|
|
120
|
+
throw new AunCommandError("未提供 Owner AID,且未配置默认 Owner");
|
|
121
|
+
if (!aidPattern.test(owner))
|
|
122
|
+
throw new AunCommandError("Owner AID 格式无效");
|
|
123
|
+
const result = await this.createIdentity(validAid, owner);
|
|
124
|
+
const connection = result.connection === "ready" ? "ready" : "degraded";
|
|
125
|
+
if (connection !== "ready")
|
|
126
|
+
throw new AunCommandError("AID 已创建但连接失败,请先查询 AID 列表确认状态,不要重试创建", 502, { created: true, aid: validAid, connection });
|
|
127
|
+
return { aid: validAid, status: result.status, connection };
|
|
128
|
+
},
|
|
129
|
+
bind: async ({ aid, agentId, replace }) => {
|
|
130
|
+
if (typeof aid !== "string" || !aidPattern.test(aid.trim()))
|
|
131
|
+
throw new AunCommandError("AID 格式无效");
|
|
132
|
+
if (typeof agentId !== "string" || !/^[a-z][a-z0-9-]{1,63}$/.test(agentId) || agentId === "aun" || agentId === "abweb")
|
|
133
|
+
throw new AunCommandError("Agent 插件 ID 无效");
|
|
134
|
+
if (typeof replace !== "boolean")
|
|
135
|
+
throw new AunCommandError("必须明确指定是否替换现有绑定");
|
|
136
|
+
const validAid = aid.trim();
|
|
137
|
+
const previousAgent = this.bindings[validAid];
|
|
138
|
+
const existing = Object.entries(this.bindings)
|
|
139
|
+
.filter(([, target]) => target === agentId)
|
|
140
|
+
.map(([boundAid]) => boundAid)
|
|
141
|
+
.filter((previousAid) => previousAid !== validAid)
|
|
142
|
+
.sort();
|
|
143
|
+
if (previousAgent && previousAgent !== agentId)
|
|
144
|
+
throw new AunCommandError("该 AID 已绑定到其他 Agent,不能通过替换转移");
|
|
145
|
+
if (existing.length && !replace)
|
|
146
|
+
throw new AunCommandError("目标 Agent 已有其他 AID 绑定;如需替换请明确设置 replace");
|
|
147
|
+
try {
|
|
148
|
+
await this.patchAgentAid(agentId, validAid);
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
throw new AunCommandError("AUN 身份关联失败", 502);
|
|
152
|
+
}
|
|
153
|
+
return { aid: validAid, agent: agentId, replacedAids: existing };
|
|
154
|
+
},
|
|
155
|
+
};
|
|
84
156
|
identity = {
|
|
85
157
|
list: async () => {
|
|
86
158
|
const own = new Set([...this.createdAids, ...this.legacyCreatedAids]);
|
|
@@ -672,16 +744,7 @@ class AunPlugin {
|
|
|
672
744
|
this.validatePluginId(agent);
|
|
673
745
|
await this.requireAgent(agent);
|
|
674
746
|
}
|
|
675
|
-
|
|
676
|
-
try {
|
|
677
|
-
created = await this.identity.create(aid, owner);
|
|
678
|
-
}
|
|
679
|
-
catch (error) {
|
|
680
|
-
const exists = this.identities.has(aid) || this.listStoreAids().includes(aid);
|
|
681
|
-
if (exists)
|
|
682
|
-
throw new AunCommandError("AID 可能已创建,但后续连接或保存失败;请先执行 mab aun list 确认,不要重试创建", 502, { created: true, aid });
|
|
683
|
-
throw new AunCommandError(`AID 创建失败:${safeError(error).replaceAll(owner, "[已隐藏]")}`, 502);
|
|
684
|
-
}
|
|
747
|
+
const created = await this.createIdentity(aid, owner);
|
|
685
748
|
const data = { created: true, aid, connection: created.connection ?? "ready" };
|
|
686
749
|
if (created.connection !== "ready")
|
|
687
750
|
throw new AunCommandError("AID 已创建,但连接失败;请先执行 mab aun list 确认,不要重试创建", 502, { ...data, connectionError: created.connectionError });
|
|
@@ -699,6 +762,17 @@ class AunPlugin {
|
|
|
699
762
|
data: { ...data, ...(agent ? { agent } : {}) },
|
|
700
763
|
};
|
|
701
764
|
}
|
|
765
|
+
async createIdentity(aid, owner) {
|
|
766
|
+
try {
|
|
767
|
+
return await this.identity.create(aid, owner);
|
|
768
|
+
}
|
|
769
|
+
catch (error) {
|
|
770
|
+
if (this.identities.has(aid) || this.listStoreAids().includes(aid)) {
|
|
771
|
+
throw new AunCommandError("AID 可能已创建,但后续连接或保存失败;请先执行 mab aun list 确认,不要重试创建", 502, { created: true, aid });
|
|
772
|
+
}
|
|
773
|
+
throw new AunCommandError(`AID 创建失败:${safeError(error).replaceAll(owner, "[已隐藏]")}`, 502);
|
|
774
|
+
}
|
|
775
|
+
}
|
|
702
776
|
async agents(readyOnly = true) {
|
|
703
777
|
const payload = await this.controlRequest("/api/plugins");
|
|
704
778
|
if (!Array.isArray(payload.plugins))
|
|
@@ -904,6 +978,8 @@ class AunPlugin {
|
|
|
904
978
|
let sent = 0;
|
|
905
979
|
let groupsCount = 0;
|
|
906
980
|
let sending = false;
|
|
981
|
+
let fallbackText;
|
|
982
|
+
let fallbackLinks = [];
|
|
907
983
|
try {
|
|
908
984
|
if (!client?.storage)
|
|
909
985
|
throw new Error("AUN SDK 不支持附件存储接口");
|
|
@@ -922,14 +998,19 @@ class AunPlugin {
|
|
|
922
998
|
}
|
|
923
999
|
groupsCount = groups.size;
|
|
924
1000
|
for (const [kind, group] of groups) {
|
|
925
|
-
const
|
|
1001
|
+
const payloadText = withAttachmentDownloadLinks(text || (kind === "image" ? "图片已生成。" : kind === "video" ? "视频已生成。" : "文件已生成。"), kind, group);
|
|
1002
|
+
const payload = { type: kind, text: payloadText, attachments: group };
|
|
926
1003
|
if (message.threadId)
|
|
927
1004
|
payload.thread_id = message.threadId;
|
|
928
1005
|
if (message.messageId)
|
|
929
1006
|
payload.ref_message_id = message.messageId;
|
|
1007
|
+
fallbackText = payloadText;
|
|
1008
|
+
fallbackLinks = attachmentDownloadLinks(group);
|
|
930
1009
|
sending = true;
|
|
931
1010
|
// message.send 失败时可能已送达,不自动重试或降级明文,避免重复发送和泄露引用。
|
|
932
|
-
await client.call("message.send", { to: message.peerId, payload, encrypt: true });
|
|
1011
|
+
const sendResult = await client.call("message.send", { to: message.peerId, payload, encrypt: true });
|
|
1012
|
+
if (typeof sendResult?.status !== "string" || !successfulSendStatuses.has(sendResult.status))
|
|
1013
|
+
throw new Error(`AUN 附件发送状态未确认:${sendResult?.status === undefined ? "未返回状态" : String(sendResult.status)}`);
|
|
933
1014
|
this.context?.logger.info("AUN 附件消息已发送", { ...trace, stage: "发送附件消息", kind, count: group.length });
|
|
934
1015
|
sending = false;
|
|
935
1016
|
sent++;
|
|
@@ -939,8 +1020,9 @@ class AunPlugin {
|
|
|
939
1020
|
deliveryFailed = true;
|
|
940
1021
|
const failure = sending ? new agent_1.AgentOperationError("AUN_ATTACHMENT_SEND_FAILED", "发送 AUN 附件消息(FS 上传已完成)", error) : error;
|
|
941
1022
|
const progress = sending ? `(已确认发送 ${sent}/${groupsCount} 组,当前组结果未确认,请勿重复发送)` : "";
|
|
1023
|
+
const fallback = fallbackText && fallbackLinks.length ? `\n${withFailureDownloadLinks(fallbackText, fallbackLinks.map((url) => ({ url })))}` : "";
|
|
942
1024
|
try {
|
|
943
|
-
await this.sendText(aid, message.peerId, `附件回传失败${progress}:${safeError((0, agent_1.errorDetails)(failure))}`, message.threadId);
|
|
1025
|
+
await this.sendText(aid, message.peerId, `附件回传失败${progress}:${safeError((0, agent_1.errorDetails)(failure))}${fallback}`, message.threadId, true);
|
|
944
1026
|
}
|
|
945
1027
|
catch { /* 尽力反馈 */ }
|
|
946
1028
|
throw failure;
|
|
@@ -965,7 +1047,7 @@ class AunPlugin {
|
|
|
965
1047
|
}
|
|
966
1048
|
}
|
|
967
1049
|
}
|
|
968
|
-
async sendText(aid, peerId, text, threadId) {
|
|
1050
|
+
async sendText(aid, peerId, text, threadId, encryptedOnly = false) {
|
|
969
1051
|
const payload = { type: "text", text };
|
|
970
1052
|
if (threadId)
|
|
971
1053
|
payload.thread_id = threadId;
|
|
@@ -974,7 +1056,7 @@ class AunPlugin {
|
|
|
974
1056
|
await this.clients.get(aid).client.call("message.send", params);
|
|
975
1057
|
}
|
|
976
1058
|
catch (error) {
|
|
977
|
-
if (!this.isE2eeError(error))
|
|
1059
|
+
if (encryptedOnly || !this.isE2eeError(error))
|
|
978
1060
|
throw error;
|
|
979
1061
|
await this.clients.get(aid).client.call("message.send", { ...params, encrypt: false });
|
|
980
1062
|
}
|
|
@@ -13,6 +13,9 @@ function normalizeAunMessage(payload, selfAid, allowFrom) {
|
|
|
13
13
|
if (!peerId || peerId === selfAid || (rules.length > 0 && !rules.includes(peerId)))
|
|
14
14
|
return undefined;
|
|
15
15
|
const body = data.payload && typeof data.payload === "object" ? data.payload : {};
|
|
16
|
+
// 对端的系统事件只作通知,转为任务回复会导致两个 Agent 循环互答。
|
|
17
|
+
if (body.type === "error" || body.type === "notice")
|
|
18
|
+
return undefined;
|
|
16
19
|
const text = typeof data.payload === "string" ? data.payload : typeof body.text === "string" ? body.text : "";
|
|
17
20
|
let attachments = [];
|
|
18
21
|
let attachmentError;
|
package/package.json
CHANGED