agent-bridge-aun 1.0.35 → 1.0.39
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
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
该插件依赖 `@agentunion/fastaun`,要求 Node.js 22.16 或更高版本。
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
附件回复按图片、视频和普通文件分组发送,并保留原话题和请求引用。图片使用不带普通说明正文的 `type: "image"` 消息,说明和下载链接另发加密文本,兼容 AUN 网页的图片卡片显示规则;MIME 类型为 `video/*` 的附件使用 `type: "video"`,普通文件使用 `type: "file"`,二者正文均附下载链接。三类附件参考 EvolCore 的标准附件格式,由插件直接通过 AUN SDK 上传可公开读取的对象,发送不带受限分享 URL 的标准对象引用,便于接收端取得下载票据及播放器分段读取。持有对象地址者可读取附件;旧版本已发送的私有分享不会自动改为公开。发送失败或状态未确认时仍向原 Peer 提供下载链接,不自动重发附件。图片已发送而说明失败时明确报告说明发送阶段,不误报 FS 上传失败。附件链路不调用 `ec` 命令,也不依赖 EvolCore 运行。
|
|
14
14
|
|
|
15
15
|
ABWeb 的 AUN 设置只展示已连接 AID,并按来源分组。成功注册并记录在插件 `created_aids` 中的 AID 属于本工程;共享密钥库中其余 AID 属于 EvolCore。旧版 `created-aids.json` 记录继续兼容。来源只用于展示,连接和关联由 AUN 插件直接管理。列表先显示已关联 Agent 的身份,各组按最近成功关联的时间倒序排列,时间相同或旧记录没有时间时按 AID 名称排序。关联时间保存在插件配置的 `binding_times` 中,刷新或重启后保留;旧记录不补造时间,再次关联成功后开始记录。
|
|
16
16
|
|
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);
|
|
@@ -250,30 +250,12 @@ async function uploadAunAttachments(client, ownerAid, peerAid, items) {
|
|
|
250
250
|
const objectKey = `shared/${(0, node_crypto_1.randomUUID)()}/${filename.replace(/[^A-Za-z0-9._-]/g, "_")}`;
|
|
251
251
|
const sha256 = digest(item.data);
|
|
252
252
|
const contentType = item.contentType.toLowerCase();
|
|
253
|
-
//
|
|
254
|
-
const video = contentType.startsWith("video/");
|
|
253
|
+
// 会话媒体控件不携带 AID 凭据,沿用 ec msg 的标准可读对象上传方式。
|
|
255
254
|
const ref = { ownerAid, bucket: "default", objectKey, filename, contentType, sizeBytes: item.data.byteLength, sha256 };
|
|
256
|
-
const uploaded = await client.storage.writeBytes(`/${objectKey}`, item.data, { contentType, public:
|
|
255
|
+
const uploaded = await client.storage.writeBytes(`/${objectKey}`, item.data, { contentType, public: true, overwrite: true })
|
|
257
256
|
.catch(error => { throw new agent_1.AgentOperationError("AUN_FS_UPLOAD_FAILED", `上传 AUN FS 文件“${filename}”`, error); });
|
|
258
257
|
checkedMetadata({ ...uploaded, size: uploaded.size ?? uploaded.size_bytes ?? item.data.byteLength }, ref);
|
|
259
|
-
|
|
260
|
-
result.push({ owner_aid: ownerAid, object_key: objectKey, filename, content_type: contentType, size_bytes: item.data.byteLength, sha256 });
|
|
261
|
-
continue;
|
|
262
|
-
}
|
|
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); });
|
|
265
|
-
if (share.owner_aid && share.owner_aid !== ownerAid || share.bucket && share.bucket !== "default" || share.object_key && share.object_key !== objectKey)
|
|
266
|
-
throw new Error("AUN 附件分享对象不一致");
|
|
267
|
-
if (share.allowed_aids !== undefined && (!Array.isArray(share.allowed_aids) || share.allowed_aids.length !== 1 || share.allowed_aids[0] !== peerAid))
|
|
268
|
-
throw new Error("AUN 附件分享授权不一致");
|
|
269
|
-
const shareId = typeof share.share_id === "string" && /^[A-Za-z0-9]{10}$/.test(share.share_id) ? share.share_id : undefined;
|
|
270
|
-
if (share.share_id !== undefined && !shareId)
|
|
271
|
-
throw new Error("AUN 附件分享标识无效");
|
|
272
|
-
const url = typeof share.path_url === "string" && share.path_url ? share.path_url : shareId ? `https://${ownerAid}/${objectKey}?t=${shareId}` : "";
|
|
273
|
-
const token = url ? pathToken(url, ownerAid, objectKey) : undefined;
|
|
274
|
-
if (!token || shareId && shareId !== token)
|
|
275
|
-
throw new Error("AUN 附件分享链接无效");
|
|
276
|
-
result.push({ owner_aid: ownerAid, bucket: "default", object_key: objectKey, url, filename, content_type: item.contentType, size_bytes: item.data.byteLength, sha256 });
|
|
258
|
+
result.push({ owner_aid: ownerAid, object_key: objectKey, filename, content_type: contentType, size_bytes: item.data.byteLength, sha256 });
|
|
277
259
|
}
|
|
278
260
|
return result;
|
|
279
261
|
}
|
|
@@ -13,13 +13,30 @@ 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.39", 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
|
+
: typeof ref.owner_aid === "string" && typeof ref.object_key === "string" ? [`https://${ref.owner_aid}/${ref.object_key}`] : []);
|
|
27
|
+
}
|
|
28
|
+
function withAttachmentDownloadLinks(text, kind, refs) {
|
|
29
|
+
const links = attachmentDownloadLinks(refs);
|
|
30
|
+
if (!links.length)
|
|
31
|
+
return text;
|
|
32
|
+
return `${text || "附件已生成。"}\n${kind === "file" ? "文件" : "附件"}下载链接:\n${links.join("\n")}`;
|
|
33
|
+
}
|
|
34
|
+
function withFailureDownloadLinks(text, refs) {
|
|
35
|
+
const links = attachmentDownloadLinks(refs);
|
|
36
|
+
if (!links.length || text?.includes("下载链接"))
|
|
37
|
+
return text;
|
|
38
|
+
return `${text || "附件已生成。"}\n附件下载链接(消息未确认时):\n${links.join("\n")}`;
|
|
39
|
+
}
|
|
23
40
|
async function loadSdk() {
|
|
24
41
|
const resolver = (0, node_module_1.createRequire)(__filename);
|
|
25
42
|
const dynamicImport = new Function("url", "return import(url)");
|
|
@@ -81,6 +98,60 @@ class AunPlugin {
|
|
|
81
98
|
this.sdk = options.sdk;
|
|
82
99
|
this.ingress = options.ingress;
|
|
83
100
|
}
|
|
101
|
+
aunManagement = {
|
|
102
|
+
list: async () => {
|
|
103
|
+
const groups = await this.identity.list();
|
|
104
|
+
const aids = [...new Set([...groups.evolcore, ...groups.muAgentBridge, ...(groups.localMuAgentBridge ?? []), ...this.listStoreAids()])]
|
|
105
|
+
.filter((aid) => aidPattern.test(aid)).sort();
|
|
106
|
+
const bindings = Object.fromEntries(Object.entries(this.bindings)
|
|
107
|
+
.filter(([aid, agentId]) => aidPattern.test(aid) && /^[a-z][a-z0-9-]{1,63}$/.test(agentId) && agentId !== "aun" && agentId !== "abweb")
|
|
108
|
+
.sort(([left], [right]) => left.localeCompare(right)));
|
|
109
|
+
return { aids, bindings };
|
|
110
|
+
},
|
|
111
|
+
create: async ({ aid, ownerAid }) => {
|
|
112
|
+
if (typeof aid !== "string" || !aidPattern.test(aid.trim()))
|
|
113
|
+
throw new AunCommandError("AID 格式无效");
|
|
114
|
+
const validAid = aid.trim();
|
|
115
|
+
const owner = typeof ownerAid === "string" && ownerAid.trim()
|
|
116
|
+
? ownerAid.trim()
|
|
117
|
+
: typeof this.config.owner_aid === "string" ? this.config.owner_aid.trim() : "";
|
|
118
|
+
if (!owner)
|
|
119
|
+
throw new AunCommandError("未提供 Owner AID,且未配置默认 Owner");
|
|
120
|
+
if (!aidPattern.test(owner))
|
|
121
|
+
throw new AunCommandError("Owner AID 格式无效");
|
|
122
|
+
const result = await this.createIdentity(validAid, owner);
|
|
123
|
+
const connection = result.connection === "ready" ? "ready" : "degraded";
|
|
124
|
+
if (connection !== "ready")
|
|
125
|
+
throw new AunCommandError("AID 已创建但连接失败,请先查询 AID 列表确认状态,不要重试创建", 502, { created: true, aid: validAid, connection });
|
|
126
|
+
return { aid: validAid, status: result.status, connection };
|
|
127
|
+
},
|
|
128
|
+
bind: async ({ aid, agentId, replace }) => {
|
|
129
|
+
if (typeof aid !== "string" || !aidPattern.test(aid.trim()))
|
|
130
|
+
throw new AunCommandError("AID 格式无效");
|
|
131
|
+
if (typeof agentId !== "string" || !/^[a-z][a-z0-9-]{1,63}$/.test(agentId) || agentId === "aun" || agentId === "abweb")
|
|
132
|
+
throw new AunCommandError("Agent 插件 ID 无效");
|
|
133
|
+
if (typeof replace !== "boolean")
|
|
134
|
+
throw new AunCommandError("必须明确指定是否替换现有绑定");
|
|
135
|
+
const validAid = aid.trim();
|
|
136
|
+
const previousAgent = this.bindings[validAid];
|
|
137
|
+
const existing = Object.entries(this.bindings)
|
|
138
|
+
.filter(([, target]) => target === agentId)
|
|
139
|
+
.map(([boundAid]) => boundAid)
|
|
140
|
+
.filter((previousAid) => previousAid !== validAid)
|
|
141
|
+
.sort();
|
|
142
|
+
if (previousAgent && previousAgent !== agentId)
|
|
143
|
+
throw new AunCommandError("该 AID 已绑定到其他 Agent,不能通过替换转移");
|
|
144
|
+
if (existing.length && !replace)
|
|
145
|
+
throw new AunCommandError("目标 Agent 已有其他 AID 绑定;如需替换请明确设置 replace");
|
|
146
|
+
try {
|
|
147
|
+
await this.patchAgentAid(agentId, validAid);
|
|
148
|
+
}
|
|
149
|
+
catch {
|
|
150
|
+
throw new AunCommandError("AUN 身份关联失败", 502);
|
|
151
|
+
}
|
|
152
|
+
return { aid: validAid, agent: agentId, replacedAids: existing };
|
|
153
|
+
},
|
|
154
|
+
};
|
|
84
155
|
identity = {
|
|
85
156
|
list: async () => {
|
|
86
157
|
const own = new Set([...this.createdAids, ...this.legacyCreatedAids]);
|
|
@@ -672,16 +743,7 @@ class AunPlugin {
|
|
|
672
743
|
this.validatePluginId(agent);
|
|
673
744
|
await this.requireAgent(agent);
|
|
674
745
|
}
|
|
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
|
-
}
|
|
746
|
+
const created = await this.createIdentity(aid, owner);
|
|
685
747
|
const data = { created: true, aid, connection: created.connection ?? "ready" };
|
|
686
748
|
if (created.connection !== "ready")
|
|
687
749
|
throw new AunCommandError("AID 已创建,但连接失败;请先执行 mab aun list 确认,不要重试创建", 502, { ...data, connectionError: created.connectionError });
|
|
@@ -699,6 +761,17 @@ class AunPlugin {
|
|
|
699
761
|
data: { ...data, ...(agent ? { agent } : {}) },
|
|
700
762
|
};
|
|
701
763
|
}
|
|
764
|
+
async createIdentity(aid, owner) {
|
|
765
|
+
try {
|
|
766
|
+
return await this.identity.create(aid, owner);
|
|
767
|
+
}
|
|
768
|
+
catch (error) {
|
|
769
|
+
if (this.identities.has(aid) || this.listStoreAids().includes(aid)) {
|
|
770
|
+
throw new AunCommandError("AID 可能已创建,但后续连接或保存失败;请先执行 mab aun list 确认,不要重试创建", 502, { created: true, aid });
|
|
771
|
+
}
|
|
772
|
+
throw new AunCommandError(`AID 创建失败:${safeError(error).replaceAll(owner, "[已隐藏]")}`, 502);
|
|
773
|
+
}
|
|
774
|
+
}
|
|
702
775
|
async agents(readyOnly = true) {
|
|
703
776
|
const payload = await this.controlRequest("/api/plugins");
|
|
704
777
|
if (!Array.isArray(payload.plugins))
|
|
@@ -904,11 +977,13 @@ class AunPlugin {
|
|
|
904
977
|
let sent = 0;
|
|
905
978
|
let groupsCount = 0;
|
|
906
979
|
let sending = false;
|
|
980
|
+
let fallbackText;
|
|
981
|
+
let fallbackLinks = [];
|
|
907
982
|
try {
|
|
908
983
|
if (!client?.storage)
|
|
909
984
|
throw new Error("AUN SDK 不支持附件存储接口");
|
|
910
985
|
const refs = uploadedRefs && uploadedItems === items ? uploadedRefs : await (0, attachments_1.uploadAunAttachments)({ storage: client.storage }, aid, message.peerId, items);
|
|
911
|
-
this.context?.logger.info("AUN FS
|
|
986
|
+
this.context?.logger.info("AUN FS 标准附件上传完成", { ...trace, stage: "上传附件", files: items.map(item => ({ filename: item.filename, sizeBytes: item.data.byteLength })) });
|
|
912
987
|
if (!uploadedRefs || uploadedItems !== items) {
|
|
913
988
|
uploadedItems = items;
|
|
914
989
|
uploadedRefs = refs;
|
|
@@ -922,25 +997,44 @@ class AunPlugin {
|
|
|
922
997
|
}
|
|
923
998
|
groupsCount = groups.size;
|
|
924
999
|
for (const [kind, group] of groups) {
|
|
925
|
-
const
|
|
1000
|
+
const payloadText = withAttachmentDownloadLinks(text || (kind === "image" ? "图片已生成。" : kind === "video" ? "视频已生成。" : "文件已生成。"), kind, group);
|
|
1001
|
+
// AUN 网页会隐藏带普通正文的单图卡片,图片说明需另发文本。
|
|
1002
|
+
const payload = { type: kind, ...(kind === "image" ? {} : { 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
|
|
1011
|
+
const sendResult = await this.callAunWithRecovery(aid, { 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++;
|
|
1017
|
+
if (kind === "image" && payloadText) {
|
|
1018
|
+
try {
|
|
1019
|
+
const { attachments: _attachments, ...caption } = payload;
|
|
1020
|
+
const result = await this.callAunWithRecovery(aid, { to: message.peerId, payload: { ...caption, type: "text", text: payloadText }, encrypt: true });
|
|
1021
|
+
if (typeof result?.status !== "string" || !successfulSendStatuses.has(result.status))
|
|
1022
|
+
throw new Error(`AUN 图片说明发送状态未确认:${result?.status === undefined ? "未返回状态" : String(result.status)}`);
|
|
1023
|
+
this.context?.logger.info("AUN 图片说明已发送", { ...trace, stage: "发送图片说明" });
|
|
1024
|
+
}
|
|
1025
|
+
catch (error) {
|
|
1026
|
+
throw new agent_1.AgentOperationError("AUN_ATTACHMENT_CAPTION_SEND_FAILED", "图片已发送,发送 AUN 图片说明(结果未确认,不自动重发)", error);
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
936
1029
|
}
|
|
937
1030
|
}
|
|
938
1031
|
catch (error) {
|
|
939
1032
|
deliveryFailed = true;
|
|
940
1033
|
const failure = sending ? new agent_1.AgentOperationError("AUN_ATTACHMENT_SEND_FAILED", "发送 AUN 附件消息(FS 上传已完成)", error) : error;
|
|
941
1034
|
const progress = sending ? `(已确认发送 ${sent}/${groupsCount} 组,当前组结果未确认,请勿重复发送)` : "";
|
|
1035
|
+
const fallback = fallbackText && fallbackLinks.length ? `\n${withFailureDownloadLinks(fallbackText, fallbackLinks.map((url) => ({ url })))}` : "";
|
|
942
1036
|
try {
|
|
943
|
-
await this.sendText(aid, message.peerId, `附件回传失败${progress}:${safeError((0, agent_1.errorDetails)(failure))}`, message.threadId);
|
|
1037
|
+
await this.sendText(aid, message.peerId, `附件回传失败${progress}:${safeError((0, agent_1.errorDetails)(failure))}${fallback}`, message.threadId, true);
|
|
944
1038
|
}
|
|
945
1039
|
catch { /* 尽力反馈 */ }
|
|
946
1040
|
throw failure;
|
|
@@ -965,18 +1059,44 @@ class AunPlugin {
|
|
|
965
1059
|
}
|
|
966
1060
|
}
|
|
967
1061
|
}
|
|
968
|
-
async sendText(aid, peerId, text, threadId) {
|
|
1062
|
+
async sendText(aid, peerId, text, threadId, encryptedOnly = false) {
|
|
969
1063
|
const payload = { type: "text", text };
|
|
970
1064
|
if (threadId)
|
|
971
1065
|
payload.thread_id = threadId;
|
|
972
1066
|
const params = { to: peerId, payload, encrypt: true };
|
|
973
1067
|
try {
|
|
974
|
-
await this.
|
|
1068
|
+
await this.callAunWithRecovery(aid, params);
|
|
1069
|
+
}
|
|
1070
|
+
catch (error) {
|
|
1071
|
+
if (encryptedOnly || !this.isE2eeError(error))
|
|
1072
|
+
throw error;
|
|
1073
|
+
await this.callAunWithRecovery(aid, { ...params, encrypt: false });
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
async callAunWithRecovery(aid, params) {
|
|
1077
|
+
const connection = this.clients.get(aid);
|
|
1078
|
+
if (!connection)
|
|
1079
|
+
throw new agent_1.AgentOperationError("AUN_CONNECTION_MISSING", "查找 AUN 身份连接", new Error(`AID ${aid} 当前没有已连接的客户端`));
|
|
1080
|
+
try {
|
|
1081
|
+
return await connection.client.call("message.send", params);
|
|
975
1082
|
}
|
|
976
1083
|
catch (error) {
|
|
977
|
-
if (!
|
|
1084
|
+
if (!isAunWriteNotStarted(error))
|
|
978
1085
|
throw error;
|
|
979
|
-
|
|
1086
|
+
this.context?.logger.warn("AUN 发送前连接已断开,正在恢复 AUN 到 Peer 的连接并重试一次", { aid, peer: params.to, stage: "恢复 AUN 连接", error: safeError((0, agent_1.errorDetails)(error)) });
|
|
1087
|
+
try {
|
|
1088
|
+
connection.reconnecting ??= connection.client.connect({ auto_reconnect: true }).finally(() => { connection.reconnecting = undefined; });
|
|
1089
|
+
await connection.reconnecting;
|
|
1090
|
+
}
|
|
1091
|
+
catch (cause) {
|
|
1092
|
+
throw new agent_1.AgentOperationError("AUN_RECONNECT_FAILED", "恢复 AUN 到 Peer 的连接(消息确认未写出)", cause);
|
|
1093
|
+
}
|
|
1094
|
+
try {
|
|
1095
|
+
return await connection.client.call("message.send", params);
|
|
1096
|
+
}
|
|
1097
|
+
catch (cause) {
|
|
1098
|
+
throw new agent_1.AgentOperationError("AUN_SEND_FAILED", "AUN 重连后发送消息(仅重试一次)", cause);
|
|
1099
|
+
}
|
|
980
1100
|
}
|
|
981
1101
|
}
|
|
982
1102
|
isE2eeError(error) {
|
|
@@ -995,6 +1115,14 @@ exports.default = AunPlugin;
|
|
|
995
1115
|
function safeError(error) {
|
|
996
1116
|
return redact(error instanceof Error ? error.message : String(error));
|
|
997
1117
|
}
|
|
1118
|
+
function isAunWriteNotStarted(error) {
|
|
1119
|
+
for (let current = error; current && typeof current === "object"; current = current.cause) {
|
|
1120
|
+
const value = current;
|
|
1121
|
+
if (value.name === "ConnectionError" && typeof value.message === "string" && /^(?:client is not connected|transport not connected|send cancelled before write:|transport closed$)/i.test(value.message))
|
|
1122
|
+
return true;
|
|
1123
|
+
}
|
|
1124
|
+
return false;
|
|
1125
|
+
}
|
|
998
1126
|
async function readResponse(response, limit) {
|
|
999
1127
|
if (!response.body) {
|
|
1000
1128
|
const raw = await response.text();
|
|
@@ -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