agent-bridge-aun 1.0.17 → 1.0.19
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
|
@@ -7,5 +7,3 @@
|
|
|
7
7
|
该插件依赖 `@agentunion/fastaun`,要求 Node.js 22.16 或更高版本。
|
|
8
8
|
|
|
9
9
|
ABWeb 的 AUN 设置只展示已连接 AID,并按来源分组。插件通过 SDK 密钥库枚举 AID,再逐个建立连接;只有连接成功的身份才会显示。EvolCore 来源身份只用于连接状态检测,不订阅消息,避免重复收信;本工程创建的身份负责消息收发。连接状态检测不调用 `ec agent list/show`。将 EvolCore 身份关联到 Agent 时仍使用既有 `ec agent set/reload` 路由配置,该配置不参与连接状态检测。列表先显示已关联 Agent 的身份,各组按最近成功关联的时间倒序排列,时间相同或旧记录没有时间时按 AID 名称排序。关联时间保存在插件配置的 `binding_times` 中,刷新或重启后保留;旧记录不补造时间,再次关联成功后开始记录。
|
|
10
|
-
|
|
11
|
-
AUN 独立包支持核心后台每五分钟检测更新;可信提供者在自动安装策略下会下载新副本并重载通道。
|
|
@@ -10,7 +10,7 @@ const node_url_1 = require("node:url");
|
|
|
10
10
|
const messages_1 = require("./messages");
|
|
11
11
|
const evolcore_1 = require("./evolcore");
|
|
12
12
|
const manifest = {
|
|
13
|
-
id: "aun", version: "1.0.
|
|
13
|
+
id: "aun", version: "1.0.19", type: "channel", displayName: "AUN 通道",
|
|
14
14
|
entry: "dist/src-ts/plugins/aun/index.js", apiVersion: "1", process: "in-process",
|
|
15
15
|
capabilities: ["channel", "channel.identity.create", "channel.receive", "channel.send", "health"],
|
|
16
16
|
configSchema: "config.schema.json",
|
|
@@ -72,8 +72,8 @@ class AunPlugin {
|
|
|
72
72
|
this.creating = true;
|
|
73
73
|
try {
|
|
74
74
|
if (!this.identities.has(aid)) {
|
|
75
|
-
//
|
|
76
|
-
|
|
75
|
+
// Owner AID 是本地归属配置;身份签发沿用 EvolCore 的兼容注册流程。
|
|
76
|
+
await this.prepareTrustRoots(aid);
|
|
77
77
|
const result = await this.store.register(aid);
|
|
78
78
|
if (!result.ok)
|
|
79
79
|
throw new Error(redact(result.error?.message ?? result.error?.code ?? "AID 注册失败"));
|
|
@@ -87,8 +87,9 @@ class AunPlugin {
|
|
|
87
87
|
await this.persist();
|
|
88
88
|
this.context?.logger.info("AUN 身份已创建", { aid, owner });
|
|
89
89
|
}
|
|
90
|
-
|
|
91
|
-
|
|
90
|
+
if (!this.clients.has(aid))
|
|
91
|
+
await this.connectAid(aid);
|
|
92
|
+
return { aid, status: "已创建", connection: this.clients.has(aid) ? "ready" : "degraded" };
|
|
92
93
|
}
|
|
93
94
|
finally {
|
|
94
95
|
this.creating = false;
|
|
@@ -221,6 +222,7 @@ class AunPlugin {
|
|
|
221
222
|
}
|
|
222
223
|
async connectAid(aid) {
|
|
223
224
|
try {
|
|
225
|
+
await this.prepareTrustRoots(aid);
|
|
224
226
|
const loaded = this.store.load(aid);
|
|
225
227
|
if (!loaded.ok || !loaded.data)
|
|
226
228
|
throw new Error(loaded.error?.message ?? "本地密钥库中不存在该 AID");
|
|
@@ -243,8 +245,7 @@ class AunPlugin {
|
|
|
243
245
|
catch (error) {
|
|
244
246
|
this.status = "degraded";
|
|
245
247
|
this.healthMessage = safeError(error);
|
|
246
|
-
this.context?.logger.warn("AUN 通道连接失败", {
|
|
247
|
-
return this.healthMessage;
|
|
248
|
+
this.context?.logger.warn("AUN 通道连接失败", { error: this.healthMessage });
|
|
248
249
|
}
|
|
249
250
|
}
|
|
250
251
|
async deactivate(_reason) {
|
|
@@ -272,9 +273,50 @@ class AunPlugin {
|
|
|
272
273
|
return (0, node_path_1.resolve)(process.env.AUN_HOME ?? process.env.EVOLCORE_HOME ?? (0, node_path_1.join)((0, node_os_1.homedir)(), ".evolcore"));
|
|
273
274
|
}
|
|
274
275
|
newStore() {
|
|
275
|
-
const rootCaPath =
|
|
276
|
+
const rootCaPath = this.rootCaPath();
|
|
276
277
|
return new this.sdk.AIDStore({ aunPath: this.path(), encryptionSeed: typeof this.config.encryption_seed === "string" && this.config.encryption_seed ? this.config.encryption_seed : "evol", verifySsl: typeof this.config.verify_ssl === "boolean" ? this.config.verify_ssl : true, rootCaPath: (0, node_fs_1.existsSync)(rootCaPath) ? rootCaPath : undefined });
|
|
277
278
|
}
|
|
279
|
+
rootCaPath() {
|
|
280
|
+
return typeof this.config.root_ca_path === "string" && this.config.root_ca_path
|
|
281
|
+
? this.config.root_ca_path
|
|
282
|
+
: (0, node_path_1.join)(this.path(), "CA", "root", "root.crt");
|
|
283
|
+
}
|
|
284
|
+
async prepareTrustRoots(aid) {
|
|
285
|
+
const rootCaPath = this.rootCaPath();
|
|
286
|
+
if ((0, node_fs_1.existsSync)(rootCaPath))
|
|
287
|
+
return;
|
|
288
|
+
if (!this.sdk?.GatewayDiscovery) {
|
|
289
|
+
this.context?.logger.warn("AUN SDK 未提供 GatewayDiscovery,无法预先下载信任根", { aid });
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
try {
|
|
293
|
+
const discovery = new this.sdk.GatewayDiscovery({ verifySsl: typeof this.config.verify_ssl === "boolean" ? this.config.verify_ssl : true });
|
|
294
|
+
const gatewayUrl = await discovery.discover(`https://${aid}/.well-known/aun-gateway`);
|
|
295
|
+
const gatewayHttpUrl = gatewayUrl.replace(/^wss?:/i, "https:").replace(/\/aun\/?$/i, "");
|
|
296
|
+
const response = await fetch(`${gatewayHttpUrl}/pki/chain`, { redirect: "follow", signal: AbortSignal.timeout(10_000) });
|
|
297
|
+
if (!response.ok)
|
|
298
|
+
throw new Error(`下载 Gateway CA 链失败:HTTP ${response.status}`);
|
|
299
|
+
const chain = await response.text();
|
|
300
|
+
if (!chain.includes("-----BEGIN CERTIFICATE-----"))
|
|
301
|
+
throw new Error("Gateway CA 链响应中没有证书");
|
|
302
|
+
await (0, promises_1.mkdir)((0, node_path_1.dirname)(rootCaPath), { recursive: true });
|
|
303
|
+
await (0, promises_1.writeFile)(rootCaPath, chain, { mode: 0o600 });
|
|
304
|
+
const oldStore = this.store;
|
|
305
|
+
const newStore = this.newStore();
|
|
306
|
+
this.store = newStore;
|
|
307
|
+
try {
|
|
308
|
+
await oldStore?.close();
|
|
309
|
+
}
|
|
310
|
+
catch (error) {
|
|
311
|
+
this.context?.logger.warn("关闭旧 AUN Store 失败", { error: safeError(error) });
|
|
312
|
+
}
|
|
313
|
+
this.context?.logger.info("AUN Gateway 信任根证书已下载", { aid });
|
|
314
|
+
}
|
|
315
|
+
catch (error) {
|
|
316
|
+
this.context?.logger.warn("AUN Gateway 信任根证书初始化失败", { aid, error: safeError(error) });
|
|
317
|
+
throw new Error(`AUN Gateway 信任根证书初始化失败:${safeError(error)}`);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
278
320
|
createAid = async (request, response) => {
|
|
279
321
|
let input;
|
|
280
322
|
try {
|
|
@@ -15,12 +15,6 @@
|
|
|
15
15
|
return result;
|
|
16
16
|
}
|
|
17
17
|
function show(message, failed = false) { status.textContent = message; status.className = failed ? "error" : ""; }
|
|
18
|
-
function notify(title, message, failed = false) {
|
|
19
|
-
show(message, failed);
|
|
20
|
-
if (window.parent !== window) window.parent.postMessage({ type: "mab:operation-result", title, message, failed }, window.location.origin);
|
|
21
|
-
else window.alert(`${title}\n${message}`);
|
|
22
|
-
}
|
|
23
|
-
const errorMessage = (error) => error instanceof Error ? error.message : String(error);
|
|
24
18
|
async function load(refreshForm = true) {
|
|
25
19
|
if (loading) return;
|
|
26
20
|
loading = true;
|
|
@@ -37,8 +31,15 @@
|
|
|
37
31
|
configForm.elements.encryption_seed.value = "";
|
|
38
32
|
}
|
|
39
33
|
list.replaceChildren();
|
|
40
|
-
const entries = [
|
|
41
|
-
|
|
34
|
+
const entries = [];
|
|
35
|
+
const seen = new Set();
|
|
36
|
+
for (const [group, label, values] of [[0, "本工程 · 已连接", identities.groups.muAgentBridge || []], [1, "EvolCore · 已连接", identities.groups.evolcore || []]]) {
|
|
37
|
+
for (const aid of values) {
|
|
38
|
+
if (seen.has(aid)) continue;
|
|
39
|
+
seen.add(aid);
|
|
40
|
+
entries.push({ aid, label, group, bound: identities.bindings?.[aid] || agents.find((agent) => agent.id && agent.configAid === aid)?.id || "", time: Date.parse(identities.bindingTimes?.[aid] || "") || 0 });
|
|
41
|
+
}
|
|
42
|
+
}
|
|
42
43
|
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));
|
|
43
44
|
for (const { aid, label, bound } of entries) {
|
|
44
45
|
const row = document.createElement("tr");
|
|
@@ -49,29 +50,22 @@
|
|
|
49
50
|
cell.append(select); row.append(cell); list.append(row);
|
|
50
51
|
}
|
|
51
52
|
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); }
|
|
52
|
-
show(`${
|
|
53
|
+
show(`${entries.length} 个已连接 AID`);
|
|
53
54
|
} catch (error) { show(String(error), true); }
|
|
54
55
|
finally { loading = false; }
|
|
55
56
|
}
|
|
56
57
|
async function bind(aid, pluginId) {
|
|
57
58
|
try {
|
|
58
59
|
await request(`/api/plugins/${encodeURIComponent(pluginId)}/aid`, "PATCH", { aid });
|
|
59
|
-
await load();
|
|
60
|
-
} catch (error) {
|
|
60
|
+
await load(); show(`${aid} 已关联 ${pluginId}`);
|
|
61
|
+
} catch (error) { show(`关联失败:${String(error)}`, true); }
|
|
61
62
|
}
|
|
62
63
|
createForm.onsubmit = (event) => { event.preventDefault(); const aid = createForm.elements.aid.value.trim(); const owner = createForm.elements.ownerAid.value.trim();
|
|
63
|
-
show("
|
|
64
|
-
void request("/api/plugins/aun/aid", "POST", { aid, ownerAid: owner }).then((result) => {
|
|
65
|
-
const failed = result.connection === "degraded";
|
|
66
|
-
const message = failed ? `${aid} 已创建,但连接失败:${result.connectionError || "未提供连接失败原因,请查看运行日志"}\n身份已经保存在本地,请勿重复注册。`
|
|
67
|
-
: result.status === "等待 Owner 确认" ? `${aid} 等待 Owner 确认` : `${aid} 已创建并连接`;
|
|
68
|
-
notify(failed ? "AID 创建完成,连接失败" : "AID 创建结果", message, failed);
|
|
69
|
-
return load();
|
|
70
|
-
}).catch((error) => notify("创建 AID 失败", errorMessage(error), true));
|
|
64
|
+
void request("/api/plugins/aun/aid", "POST", { aid, ownerAid: owner }).then((result) => load().then(() => show(result.status === "等待 Owner 确认" ? `${aid} 等待 Owner 确认` : `${aid} 已创建`))).catch((error) => show(String(error), true));
|
|
71
65
|
};
|
|
72
66
|
configForm.onsubmit = (event) => { event.preventDefault(); const changes = { agent_plugin: configForm.elements.agent_plugin.value, allow_from: configForm.elements.allow_from.value, aun_path: configForm.elements.aun_path.value };
|
|
73
67
|
if (configForm.elements.encryption_seed.value) changes.encryption_seed = configForm.elements.encryption_seed.value;
|
|
74
|
-
void request("/api/plugins/aun/settings", "POST", changes).then(() => request("/api/plugins/aun/reload", "POST")).then(() => load()).then(() =>
|
|
68
|
+
void request("/api/plugins/aun/settings", "POST", changes).then(() => request("/api/plugins/aun/reload", "POST")).then(() => load()).then(() => show("配置已保存")).catch((error) => show(String(error), true));
|
|
75
69
|
};
|
|
76
70
|
find("#refresh").onclick = () => void load();
|
|
77
71
|
void load();
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
<header><h1>AUN 身份</h1><button id="refresh" type="button">刷新</button></header>
|
|
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
|
-
<form id="create-form"><h2>创建 AID</h2><div class="fields"><label>新 AID<input name="aid" required placeholder="
|
|
9
|
+
<form id="create-form"><h2>创建 AID</h2><div class="fields"><label>新 AID<input name="aid" required placeholder="new.agentid.cn"></label><label>Owner AID<input name="ownerAid" required></label></div><button type="submit">创建</button></form>
|
|
10
10
|
<form id="config-form"><h2>通道配置</h2><div class="fields"><label>默认 Agent<select name="agent_plugin"></select></label><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>
|
package/package.json
CHANGED