agent-bridge-aun 1.0.11 → 1.0.13
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 +1 -1
- package/dist/src-ts/plugin.js +13 -0
- package/dist/src-ts/plugins/aun/evolcore.js +43 -0
- package/dist/src-ts/plugins/aun/index.js +38 -32
- package/dist/src-ts/plugins/aun/public/app.js +3 -3
- 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
|
@@ -6,4 +6,4 @@
|
|
|
6
6
|
|
|
7
7
|
该插件依赖 `@agentunion/fastaun`,要求 Node.js 22.16 或更高版本。
|
|
8
8
|
|
|
9
|
-
ABWeb 的 AUN
|
|
9
|
+
ABWeb 的 AUN 设置只展示已连接 AID,并按来源分组。插件通过 SDK 密钥库枚举 AID,再逐个建立连接;只有连接成功的身份才会显示。EvolCore 来源身份只用于连接状态检测,不订阅消息,避免重复收信;本工程创建的身份负责消息收发。连接状态检测不调用 `ec agent list/show`。将 EvolCore 身份关联到 Agent 时仍使用既有 `ec agent set/reload` 路由配置,该配置不参与连接状态检测。列表先显示已关联 Agent 的身份,各组按最近成功关联的时间倒序排列,时间相同或旧记录没有时间时按 AID 名称排序。关联时间保存在插件配置的 `binding_times` 中,刷新或重启后保留;旧记录不补造时间,再次关联成功后开始记录。
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolveRuntimePort = resolveRuntimePort;
|
|
4
|
+
function resolveRuntimePort(config, runtime, pluginId, envName, fallback) {
|
|
5
|
+
const injected = runtime?.ports?.[pluginId];
|
|
6
|
+
if (typeof injected === "number" && Number.isInteger(injected) && injected > 0)
|
|
7
|
+
return injected;
|
|
8
|
+
const configured = config.cdpPort;
|
|
9
|
+
if (typeof configured === "number" && Number.isInteger(configured) && configured > 0)
|
|
10
|
+
return configured;
|
|
11
|
+
const environment = Number(process.env[envName] ?? "");
|
|
12
|
+
return Number.isInteger(environment) && environment > 0 ? environment : fallback;
|
|
13
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.runEc = runEc;
|
|
4
|
+
exports.configureAidForPlugin = configureAidForPlugin;
|
|
5
|
+
const node_child_process_1 = require("node:child_process");
|
|
6
|
+
const node_util_1 = require("node:util");
|
|
7
|
+
const execFileAsync = (0, node_util_1.promisify)(node_child_process_1.execFile);
|
|
8
|
+
async function runEc(args) {
|
|
9
|
+
try {
|
|
10
|
+
const { stdout } = await execFileAsync("ec", args, { shell: process.platform === "win32", timeout: 15_000, maxBuffer: 1024 * 1024 });
|
|
11
|
+
return JSON.parse(stdout);
|
|
12
|
+
}
|
|
13
|
+
catch (error) {
|
|
14
|
+
const stdout = error.stdout;
|
|
15
|
+
if (stdout) {
|
|
16
|
+
try {
|
|
17
|
+
return JSON.parse(stdout);
|
|
18
|
+
}
|
|
19
|
+
catch { /* 保留原始命令错误 */ }
|
|
20
|
+
}
|
|
21
|
+
throw error;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
async function configureAidForPlugin(pluginId, aid, port, run = runEc) {
|
|
25
|
+
if (!aid)
|
|
26
|
+
return;
|
|
27
|
+
if (!/^[a-z0-9_-]+$/.test(pluginId))
|
|
28
|
+
throw new Error("插件 ID 格式无效");
|
|
29
|
+
const path = pluginId === "dsh" ? "/dsh/v1" : pluginId === "qwenwork" ? "/v1" : `/api/agents/${pluginId}/v1`;
|
|
30
|
+
for (const [key, value] of [
|
|
31
|
+
["baseagents.ecagent.baseUrl", `http://127.0.0.1:${port}${path}`],
|
|
32
|
+
["baseagents.ecagent.apiKey", "mab-local"],
|
|
33
|
+
["baseagents.ecagent.model", pluginId],
|
|
34
|
+
["active_baseagent", "ecagent"],
|
|
35
|
+
]) {
|
|
36
|
+
const result = await run(["agent", "set", aid, key, value, "--format", "json"]);
|
|
37
|
+
if (!result?.ok)
|
|
38
|
+
throw new Error(`配置 AID ${aid} 失败:${key};${result?.code ?? ""} ${result?.error ?? "未知错误"}`);
|
|
39
|
+
}
|
|
40
|
+
const result = await run(["agent", "reload", aid, "--format", "json"]);
|
|
41
|
+
if (!result?.ok)
|
|
42
|
+
throw new Error(`重载 AID ${aid} 失败:${result?.code ?? ""} ${result?.error ?? "未知错误"}`);
|
|
43
|
+
}
|
|
@@ -8,8 +8,9 @@ const node_path_1 = require("node:path");
|
|
|
8
8
|
const node_os_1 = require("node:os");
|
|
9
9
|
const node_url_1 = require("node:url");
|
|
10
10
|
const messages_1 = require("./messages");
|
|
11
|
+
const evolcore_1 = require("./evolcore");
|
|
11
12
|
const manifest = {
|
|
12
|
-
id: "aun", version: "1.0.
|
|
13
|
+
id: "aun", version: "1.0.10", type: "channel", displayName: "AUN 通道",
|
|
13
14
|
entry: "dist/src-ts/plugins/aun/index.js", apiVersion: "1", process: "in-process",
|
|
14
15
|
capabilities: ["channel", "channel.identity.create", "channel.receive", "channel.send", "health"],
|
|
15
16
|
configSchema: "config.schema.json",
|
|
@@ -38,6 +39,7 @@ class AunPlugin {
|
|
|
38
39
|
store;
|
|
39
40
|
clients = new Map();
|
|
40
41
|
identities = new Set();
|
|
42
|
+
legacyCreatedAids = new Set();
|
|
41
43
|
bindings = {};
|
|
42
44
|
bindingTimes = {};
|
|
43
45
|
creating = false;
|
|
@@ -55,13 +57,11 @@ class AunPlugin {
|
|
|
55
57
|
}
|
|
56
58
|
identity = {
|
|
57
59
|
list: async () => {
|
|
58
|
-
|
|
59
|
-
const local = this.listStoreAids();
|
|
60
|
+
const connected = [...this.clients.keys()];
|
|
60
61
|
return {
|
|
61
|
-
evolcore:
|
|
62
|
-
muAgentBridge:
|
|
62
|
+
evolcore: connected.filter((aid) => !this.identities.has(aid) && !this.legacyCreatedAids.has(aid)).sort(),
|
|
63
|
+
muAgentBridge: connected.filter((aid) => this.identities.has(aid) || this.legacyCreatedAids.has(aid)).sort(),
|
|
63
64
|
localMuAgentBridge: [...this.identities].filter((aid) => !this.clients.has(aid)).sort(),
|
|
64
|
-
local,
|
|
65
65
|
};
|
|
66
66
|
},
|
|
67
67
|
create: async (aid, owner) => {
|
|
@@ -72,12 +72,11 @@ class AunPlugin {
|
|
|
72
72
|
this.creating = true;
|
|
73
73
|
try {
|
|
74
74
|
if (!this.identities.has(aid)) {
|
|
75
|
-
|
|
75
|
+
// 创建身份沿用 EvolCore 的 legacy 注册流程。Owner AID 是本地归属配置,
|
|
76
|
+
// 只有明确执行 Owner 关系绑定时才进入 aid_register_* 双签流程。
|
|
77
|
+
const result = await this.store.register(aid);
|
|
76
78
|
if (!result.ok)
|
|
77
79
|
throw new Error(redact(result.error?.message ?? result.error?.code ?? "AID 注册失败"));
|
|
78
|
-
const ownership = result.data?.ownership_status ?? result.data?.state ?? result.data?.operation_state;
|
|
79
|
-
if (ownership && ownership !== "active")
|
|
80
|
-
return { aid, status: "等待 Owner 确认", operation_id: result.data?.operation_id };
|
|
81
80
|
const loaded = this.store.load(aid);
|
|
82
81
|
if (!loaded.ok || !loaded.data)
|
|
83
82
|
throw new Error(loaded.error?.message ?? "注册后未能加载本地身份");
|
|
@@ -96,7 +95,7 @@ class AunPlugin {
|
|
|
96
95
|
this.creating = false;
|
|
97
96
|
}
|
|
98
97
|
},
|
|
99
|
-
bind: async (pluginId, aid,
|
|
98
|
+
bind: async (pluginId, aid, port) => {
|
|
100
99
|
if (pluginId === "aun" || pluginId === "abweb")
|
|
101
100
|
throw new Error("只能关联 Agent 插件");
|
|
102
101
|
const previous = this.bindings;
|
|
@@ -109,7 +108,7 @@ class AunPlugin {
|
|
|
109
108
|
throw new Error("AID 格式无效");
|
|
110
109
|
if (this.context?.pluginStatus(pluginId) !== "ready")
|
|
111
110
|
throw new Error("目标 Agent 插件未启动");
|
|
112
|
-
if (this.identities.has(aid)) {
|
|
111
|
+
if (this.identities.has(aid) || this.legacyCreatedAids.has(aid)) {
|
|
113
112
|
if (!this.clients.has(aid))
|
|
114
113
|
await this.connectAid(aid);
|
|
115
114
|
if (!this.clients.has(aid))
|
|
@@ -122,20 +121,15 @@ class AunPlugin {
|
|
|
122
121
|
this.bindings[aid] = pluginId;
|
|
123
122
|
}
|
|
124
123
|
else {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
if (!loaded.ok || !loaded.data)
|
|
128
|
-
throw new Error("AID 不在本地密钥库中,无法关联");
|
|
129
|
-
await this.connectAid(aid);
|
|
130
|
-
if (!this.clients.has(aid))
|
|
124
|
+
const connected = await this.identity.list();
|
|
125
|
+
if (![...connected.evolcore, ...connected.muAgentBridge].includes(aid))
|
|
131
126
|
throw new Error("AID 当前未连接");
|
|
127
|
+
await (0, evolcore_1.configureAidForPlugin)(pluginId, aid, port);
|
|
132
128
|
for (const [identity, target] of Object.entries(this.bindings))
|
|
133
129
|
if (target === pluginId) {
|
|
134
130
|
delete this.bindings[identity];
|
|
135
131
|
delete this.bindingTimes[identity];
|
|
136
132
|
}
|
|
137
|
-
this.bindings[aid] = pluginId;
|
|
138
|
-
this.identities.add(aid);
|
|
139
133
|
}
|
|
140
134
|
}
|
|
141
135
|
else {
|
|
@@ -169,6 +163,18 @@ class AunPlugin {
|
|
|
169
163
|
async activate(context) {
|
|
170
164
|
this.context = context;
|
|
171
165
|
this.config = { ...context.config };
|
|
166
|
+
this.legacyCreatedAids.clear();
|
|
167
|
+
try {
|
|
168
|
+
const created = JSON.parse(await (0, promises_1.readFile)((0, node_path_1.join)(context.dataDir, "..", "created-aids.json"), "utf8"));
|
|
169
|
+
if (Array.isArray(created))
|
|
170
|
+
for (const aid of created)
|
|
171
|
+
if (typeof aid === "string" && aidPattern.test(aid))
|
|
172
|
+
this.legacyCreatedAids.add(aid);
|
|
173
|
+
}
|
|
174
|
+
catch (error) {
|
|
175
|
+
if (error.code !== "ENOENT")
|
|
176
|
+
context.logger.warn("读取旧版身份创建记录失败", { error: safeError(error) });
|
|
177
|
+
}
|
|
172
178
|
for (const key of ["aid", "owner_aid", "aun_path", "encryption_seed", "root_ca_path", "allow_from", "agent_plugin"]) {
|
|
173
179
|
if (this.config[key] !== undefined && typeof this.config[key] !== "string")
|
|
174
180
|
throw new Error(`${key} 必须是字符串`);
|
|
@@ -209,12 +215,10 @@ class AunPlugin {
|
|
|
209
215
|
this.identities.add(this.aid);
|
|
210
216
|
for (const aid of this.identities)
|
|
211
217
|
await this.connectAid(aid);
|
|
212
|
-
//
|
|
213
|
-
for (const aid of this.listStoreAids())
|
|
214
|
-
this.identities.add(aid);
|
|
218
|
+
// AUN 插件直接连接本地密钥库中的所有 AID,不依赖 ec 进程或 CLI。
|
|
219
|
+
for (const aid of this.listStoreAids())
|
|
215
220
|
if (!this.clients.has(aid))
|
|
216
221
|
await this.connectAid(aid);
|
|
217
|
-
}
|
|
218
222
|
}
|
|
219
223
|
async connectAid(aid) {
|
|
220
224
|
try {
|
|
@@ -222,12 +226,14 @@ class AunPlugin {
|
|
|
222
226
|
if (!loaded.ok || !loaded.data)
|
|
223
227
|
throw new Error(loaded.error?.message ?? "本地密钥库中不存在该 AID");
|
|
224
228
|
const client = new this.sdk.AUNClient(loaded.data.aid);
|
|
225
|
-
const subscription =
|
|
229
|
+
const subscription = this.identities.has(aid) || this.legacyCreatedAids.has(aid)
|
|
230
|
+
? client.on("message.received", (payload) => this.onMessage(payload, aid))
|
|
231
|
+
: undefined;
|
|
226
232
|
try {
|
|
227
233
|
await client.connect();
|
|
228
234
|
}
|
|
229
235
|
catch (error) {
|
|
230
|
-
subscription
|
|
236
|
+
subscription?.unsubscribe();
|
|
231
237
|
await client.close();
|
|
232
238
|
throw error;
|
|
233
239
|
}
|
|
@@ -244,7 +250,7 @@ class AunPlugin {
|
|
|
244
250
|
async deactivate(_reason) {
|
|
245
251
|
this.status = "degraded";
|
|
246
252
|
for (const { client, subscription } of this.clients.values()) {
|
|
247
|
-
subscription
|
|
253
|
+
subscription?.unsubscribe();
|
|
248
254
|
await client.close();
|
|
249
255
|
}
|
|
250
256
|
this.clients.clear();
|
|
@@ -261,11 +267,11 @@ class AunPlugin {
|
|
|
261
267
|
async handle(_request) { throw new Error("AUN 是入站通道插件"); }
|
|
262
268
|
path() {
|
|
263
269
|
const configured = this.config.aun_path;
|
|
264
|
-
|
|
265
|
-
|
|
270
|
+
if (typeof configured === "string" && configured.trim())
|
|
271
|
+
return configured;
|
|
272
|
+
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"));
|
|
266
273
|
}
|
|
267
274
|
newStore() {
|
|
268
|
-
// 与 EvolCore 共用密钥库时沿用其固定 seed 和 CA 证书路径
|
|
269
275
|
const rootCaPath = typeof this.config.root_ca_path === "string" && this.config.root_ca_path ? this.config.root_ca_path : (0, node_path_1.join)(this.path(), "CA", "root", "root.crt");
|
|
270
276
|
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 });
|
|
271
277
|
}
|
|
@@ -312,8 +318,8 @@ class AunPlugin {
|
|
|
312
318
|
}
|
|
313
319
|
listIdentities = async (_request, response) => {
|
|
314
320
|
const groups = await this.identity.list();
|
|
315
|
-
const connected = new Set([...groups.evolcore, ...groups.muAgentBridge
|
|
316
|
-
const localAids = (
|
|
321
|
+
const connected = new Set([...groups.evolcore, ...groups.muAgentBridge]);
|
|
322
|
+
const localAids = this.listStoreAids().filter((aid) => !connected.has(aid));
|
|
317
323
|
this.json(response, 200, { groups, localAids, bindings: this.bindings, bindingTimes: this.bindingTimes });
|
|
318
324
|
};
|
|
319
325
|
saveSettings = async (request, response) => {
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
configForm.elements.encryption_seed.value = "";
|
|
32
32
|
}
|
|
33
33
|
list.replaceChildren();
|
|
34
|
-
const entries = [["本工程 · 已连接", identities.groups.muAgentBridge || []], ["
|
|
34
|
+
const entries = [["本工程 · 已连接", identities.groups.muAgentBridge || []], ["EvolCore · 已连接", identities.groups.evolcore || []]]
|
|
35
35
|
.flatMap(([label, values], group) => values.map((aid) => ({ aid, label, group, bound: identities.bindings?.[aid] || agents.find((agent) => agent.id && agent.configAid === aid)?.id || "", time: Date.parse(identities.bindingTimes?.[aid] || "") || 0 })));
|
|
36
36
|
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));
|
|
37
37
|
for (const { aid, label, bound } of entries) {
|
|
@@ -42,8 +42,8 @@
|
|
|
42
42
|
select.value = bound; select.onchange = () => { if (select.value) void bind(aid, select.value); };
|
|
43
43
|
cell.append(select); row.append(cell); list.append(row);
|
|
44
44
|
}
|
|
45
|
-
if (!list.children.length) { const row = document.createElement("tr"); const cell = document.createElement("td"); cell.colSpan = 4; cell.textContent = "
|
|
46
|
-
show(`${(identities.groups.
|
|
45
|
+
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); }
|
|
46
|
+
show(`${(identities.groups.evolcore || []).length + (identities.groups.muAgentBridge || []).length} 个已连接 AID`);
|
|
47
47
|
} catch (error) { show(String(error), true); }
|
|
48
48
|
finally { loading = false; }
|
|
49
49
|
}
|
|
@@ -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="例如:example.agentid.cn"></label><label>Owner AID<input name="ownerAid" required placeholder="例如:owner.agentid.cn(本地归属配置)"><small class="hint">Owner AID 用于本地归属和 Agent 关联;创建身份本身沿用 AUN 的普通注册流程</small></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