mu-agent-bridge 1.0.28 → 1.0.29

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.
@@ -5,7 +5,7 @@ const node_fs_1 = require("node:fs");
5
5
  const node_path_1 = require("node:path");
6
6
  const MANIFEST = {
7
7
  id: "abweb",
8
- version: "1.0.6",
8
+ version: "1.0.7",
9
9
  displayName: "Agent Bridge Web",
10
10
  entry: "dist/index.js",
11
11
  apiVersion: "1",
@@ -35,6 +35,25 @@
35
35
  let loading = false;
36
36
  const installingPackages = new Set();
37
37
  function error(message) { errorNode.textContent = message; errorNode.hidden = !message; }
38
+ function showResult(title, message, failed) {
39
+ node("#operation-result-title").textContent = title;
40
+ const detail = node("#operation-result-message");
41
+ detail.textContent = message;
42
+ detail.className = failed ? "error" : "";
43
+ const dialog = node("#operation-result");
44
+ if (!dialog.open)
45
+ dialog.showModal();
46
+ }
47
+ node("#operation-result-close").onclick = () => node("#operation-result").close();
48
+ window.addEventListener("message", (event) => {
49
+ const frame = channelPanel.querySelector("iframe.channel-frame");
50
+ if (event.origin !== window.location.origin || !frame || event.source !== frame.contentWindow)
51
+ return;
52
+ const result = event.data;
53
+ if (!result || result.type !== "mab:operation-result" || typeof result.title !== "string" || typeof result.message !== "string" || typeof result.failed !== "boolean")
54
+ return;
55
+ showResult(result.title, result.message, result.failed);
56
+ });
38
57
  async function loadCatalog() {
39
58
  const result = await request(`/api/plugins/catalog?q=${encodeURIComponent(packageSearchNode.value.trim())}`);
40
59
  const items = result.plugins ?? [];
@@ -113,7 +132,7 @@
113
132
  item.type = "button";
114
133
  item.textContent = label;
115
134
  item.title = title;
116
- item.onclick = () => { item.disabled = true; void run().catch((cause) => error(String(cause))).finally(() => { item.disabled = false; void load(false); }); };
135
+ item.onclick = () => { item.disabled = true; void run().catch((cause) => { const message = cause instanceof Error ? cause.message : String(cause); error(message); showResult(`${title}失败`, message, true); }).finally(() => { item.disabled = false; void load(false); }); };
117
136
  return item;
118
137
  }
119
138
  async function load(refreshCatalog = true) {
@@ -21,6 +21,9 @@
21
21
  <div id="logs-panel" role="tabpanel" aria-labelledby="logs-tab" hidden><section><div class="section-heading"><h2>运行日志</h2><button id="logs-refresh" type="button">刷新日志</button></div><div class="table-scroll"><table><thead><tr><th>时间</th><th>级别</th><th>来源</th><th>消息</th><th>详细字段</th></tr></thead><tbody id="logs"></tbody></table></div><p id="logs-empty" class="muted" hidden>暂无运行日志</p><div class="section-heading"><button id="logs-prev" type="button">上一页</button><span id="logs-page" class="muted">第 1 页</span><button id="logs-next" type="button">下一页</button></div></section></div>
22
22
  <p id="error" class="error" role="alert" hidden></p>
23
23
  </main>
24
+ <dialog id="operation-result" aria-labelledby="operation-result-title" aria-describedby="operation-result-message">
25
+ <h2 id="operation-result-title">操作结果</h2><p id="operation-result-message"></p><button id="operation-result-close" type="button" autofocus>确定</button>
26
+ </dialog>
24
27
  <script src="/abweb/app.js"></script>
25
28
  </body>
26
29
  </html>
@@ -56,3 +56,7 @@ th { color: #566761; font-weight: 600; background: #edf1ef; }
56
56
  th:nth-child(1) { width: 145px; } th:nth-child(2) { width: 100px; } th:nth-child(3), th:nth-child(4) { width: 145px; } th:nth-child(5) { width: 62px; } th:nth-child(7) { width: 140px; }
57
57
  .message-error td:last-child { color: #a83427; }
58
58
  @media (max-width: 620px) { .shell { padding: 16px; } .section-heading { align-items: flex-start; flex-direction: column; } form, form input { width: 100%; } }
59
+
60
+ #operation-result { width: min(520px, calc(100% - 32px)); border: 1px solid #bfc9c5; border-radius: 8px; padding: 24px; }
61
+ #operation-result::backdrop { background: rgb(0 0 0 / 35%); }
62
+ #operation-result-message { white-space: pre-wrap; overflow-wrap: anywhere; margin: 16px 0; }
@@ -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.15", type: "channel", displayName: "AUN 通道",
13
+ id: "aun", version: "1.0.17", 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",
@@ -87,9 +87,8 @@ class AunPlugin {
87
87
  await this.persist();
88
88
  this.context?.logger.info("AUN 身份已创建", { aid, owner });
89
89
  }
90
- if (!this.clients.has(aid))
91
- await this.connectAid(aid);
92
- return { aid, status: "已创建", connection: this.clients.has(aid) ? "ready" : "degraded" };
90
+ const connectionError = !this.clients.has(aid) ? await this.connectAid(aid) : undefined;
91
+ return { aid, status: "已创建", connection: this.clients.has(aid) ? "ready" : "degraded", ...(connectionError ? { connectionError } : {}) };
93
92
  }
94
93
  finally {
95
94
  this.creating = false;
@@ -244,7 +243,8 @@ class AunPlugin {
244
243
  catch (error) {
245
244
  this.status = "degraded";
246
245
  this.healthMessage = safeError(error);
247
- this.context?.logger.warn("AUN 通道连接失败", { error: this.healthMessage });
246
+ this.context?.logger.warn("AUN 通道连接失败", { aid, error: this.healthMessage });
247
+ return this.healthMessage;
248
248
  }
249
249
  }
250
250
  async deactivate(_reason) {
@@ -15,6 +15,12 @@
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);
18
24
  async function load(refreshForm = true) {
19
25
  if (loading) return;
20
26
  loading = true;
@@ -50,15 +56,22 @@
50
56
  async function bind(aid, pluginId) {
51
57
  try {
52
58
  await request(`/api/plugins/${encodeURIComponent(pluginId)}/aid`, "PATCH", { aid });
53
- await load(); show(`${aid} 已关联 ${pluginId}`);
54
- } catch (error) { show(`关联失败:${String(error)}`, true); }
59
+ await load(); notify("关联成功", `${aid} 已关联 ${pluginId}`);
60
+ } catch (error) { notify("关联失败", errorMessage(error), true); }
55
61
  }
56
62
  createForm.onsubmit = (event) => { event.preventDefault(); const aid = createForm.elements.aid.value.trim(); const owner = createForm.elements.ownerAid.value.trim();
57
- 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));
63
+ show("正在创建 AID 并连接,请稍候……");
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));
58
71
  };
59
72
  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 };
60
73
  if (configForm.elements.encryption_seed.value) changes.encryption_seed = configForm.elements.encryption_seed.value;
61
- 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));
74
+ 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));
62
75
  };
63
76
  find("#refresh").onclick = () => void load();
64
77
  void load();
@@ -34,6 +34,7 @@ class Element {
34
34
  Date: class extends Date {
35
35
  static now() { return now; }
36
36
  },
37
+ window: { addEventListener() { } },
37
38
  setInterval() { }, confirm: () => false,
38
39
  fetch: async (path, options) => {
39
40
  calls.push({ path, token: options.headers.authorization });
@@ -137,6 +137,7 @@ const aun_1 = require("../src-ts/plugins/aun");
137
137
  const calls = [];
138
138
  let registration = { ok: true, data: {} };
139
139
  let loadOk = true;
140
+ let connectError = "";
140
141
  let loadFailAid = "";
141
142
  const clients = [];
142
143
  const sdk = {
@@ -150,7 +151,8 @@ const aun_1 = require("../src-ts/plugins/aun");
150
151
  AUNClient: class {
151
152
  constructor() { const current = this; current.emit = (payload) => current.handler?.(payload); clients.push(current); }
152
153
  on(_event, handler) { this.handler = handler; return { unsubscribe() { } }; }
153
- async connect() { calls.push("client.connect"); }
154
+ async connect() { calls.push("client.connect"); if (connectError)
155
+ throw new Error(connectError); }
154
156
  async close() { calls.push("client.close"); }
155
157
  async call(method, params) { calls.push(`${method}:${params.to}:${params.encrypt}`); }
156
158
  },
@@ -203,6 +205,9 @@ const aun_1 = require("../src-ts/plugins/aun");
203
205
  strict_1.default.deepEqual(await plugin.identity.create("pending.agentid.pub", "owner.agentid.pub"), { aid: "pending.agentid.pub", status: "已创建", connection: "ready" });
204
206
  strict_1.default.ok(calls.includes("register:pending.agentid.pub"));
205
207
  strict_1.default.equal((await plugin.health()).details?.aids instanceof Array, true);
208
+ connectError = "no trusted roots available for auth certificate verification";
209
+ strict_1.default.deepEqual(await plugin.identity.create("unconnected.agentid.pub", "owner.agentid.pub"), { aid: "unconnected.agentid.pub", status: "已创建", connection: "degraded", connectionError: connectError });
210
+ connectError = "";
206
211
  registration = { ok: false, error: { message: "注册失败" } };
207
212
  await strict_1.default.rejects(plugin.identity.create("failed.agentid.pub", "owner.agentid.pub"), /注册失败/);
208
213
  registration = { ok: true, data: {} };
@@ -0,0 +1,99 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const strict_1 = __importDefault(require("node:assert/strict"));
7
+ const node_test_1 = __importDefault(require("node:test"));
8
+ const promises_1 = require("node:fs/promises");
9
+ const node_vm_1 = require("node:vm");
10
+ class Element {
11
+ hidden = true;
12
+ value = "";
13
+ textContent = "";
14
+ className = "";
15
+ open = false;
16
+ options = [];
17
+ selectedIndex = 0;
18
+ children = [];
19
+ elements = {};
20
+ onclick;
21
+ onsubmit;
22
+ replaceChildren(...items) { this.children = items; }
23
+ append(...items) { this.children.push(...items); }
24
+ setAttribute() { }
25
+ querySelector() { return this.children.find(item => item.className === "channel-frame"); }
26
+ showModal() { this.open = true; }
27
+ close() { this.open = false; }
28
+ }
29
+ const flush = () => new Promise(resolve => setImmediate(resolve));
30
+ (0, node_test_1.default)("ABWeb只接受当前同源通道页面的结果,刷新不关闭弹窗", async () => {
31
+ const nodes = new Map();
32
+ const node = (selector) => { if (!nodes.has(selector))
33
+ nodes.set(selector, new Element()); return nodes.get(selector); };
34
+ const source = {};
35
+ const frame = new Element();
36
+ frame.className = "channel-frame";
37
+ frame.contentWindow = source;
38
+ let receive;
39
+ (0, node_vm_1.runInNewContext)(await (0, promises_1.readFile)("src-ts/plugins/abweb/public/app.js", "utf8"), {
40
+ document: { querySelector: node, createElement: () => new Element(), hidden: false },
41
+ window: { location: { origin: "http://localhost:26107" }, addEventListener(name, handler) { if (name === "message")
42
+ receive = handler; } },
43
+ localStorage: { getItem: () => null, removeItem() { } }, setInterval() { }, confirm: () => false,
44
+ fetch: async () => ({ ok: true, status: 200, json: async () => ({ plugins: [], messages: [], sessions: [] }) }),
45
+ });
46
+ await flush();
47
+ node("#channel-panel").children = [frame];
48
+ const data = { type: "mab:operation-result", title: "创建 AID 失败", message: "owner AID has no active owner profile", failed: true };
49
+ strict_1.default.equal(typeof receive, "function");
50
+ receive({ origin: "http://other.invalid", source, data });
51
+ receive({ origin: "http://localhost:26107", source: {}, data });
52
+ strict_1.default.equal(node("#operation-result").open, false);
53
+ receive({ origin: "http://localhost:26107", source, data });
54
+ strict_1.default.equal(node("#operation-result").open, true);
55
+ strict_1.default.equal(node("#operation-result-title").textContent, data.title);
56
+ strict_1.default.equal(node("#operation-result-message").textContent, data.message);
57
+ node("#refresh").onclick();
58
+ await flush();
59
+ strict_1.default.equal(node("#operation-result").open, true);
60
+ node("#operation-result-close").onclick();
61
+ strict_1.default.equal(node("#operation-result").open, false);
62
+ });
63
+ for (const scenario of [
64
+ { ok: false, result: { error: "注册被拒绝" }, expected: "注册被拒绝", failed: true },
65
+ { ok: true, result: { status: "已创建", connection: "degraded", connectionError: "no trusted roots available" }, expected: "已创建,但连接失败:no trusted roots available", failed: true },
66
+ { ok: true, result: { status: "已创建", connection: "ready" }, expected: "已创建并连接", failed: false },
67
+ ])
68
+ (0, node_test_1.default)(`AUN创建结果通知ABWeb:${scenario.expected}`, async () => {
69
+ const nodes = new Map();
70
+ const node = (selector) => { if (!nodes.has(selector))
71
+ nodes.set(selector, new Element()); return nodes.get(selector); };
72
+ for (const [selector, keys] of [["#create-form", ["aid", "ownerAid"]], ["#config-form", ["agent_plugin", "allow_from", "aun_path", "encryption_seed"]]]) {
73
+ for (const key of keys)
74
+ node(selector).elements[key] = new Element();
75
+ }
76
+ const messages = [];
77
+ let refresh;
78
+ (0, node_vm_1.runInNewContext)(await (0, promises_1.readFile)("src-ts/plugins/aun/public/app.js", "utf8"), {
79
+ document: { querySelector: node, createElement: () => new Element(), hidden: false },
80
+ window: { parent: { postMessage(data, origin) { messages.push({ data, origin }); } }, location: { origin: "http://localhost:26107" } },
81
+ Option: function () { return new Element(); }, localStorage: { getItem: () => null }, setInterval(handler) { refresh = handler; },
82
+ fetch: async (path, options) => options.method === "POST"
83
+ ? { ok: scenario.ok, status: scenario.ok ? 201 : 502, json: async () => scenario.result }
84
+ : { ok: true, json: async () => path.endsWith("/identities") ? { groups: { muAgentBridge: [], evolcore: [] } } : { plugins: [] } },
85
+ });
86
+ await flush();
87
+ node("#create-form").elements.aid.value = "new.agentid.cn";
88
+ node("#create-form").elements.ownerAid.value = "owner.agentid.cn";
89
+ node("#create-form").onsubmit({ preventDefault() { } });
90
+ await flush();
91
+ strict_1.default.equal(messages.length, 1);
92
+ strict_1.default.equal(messages[0].data.type, "mab:operation-result");
93
+ strict_1.default.equal(messages[0].data.failed, scenario.failed);
94
+ strict_1.default.ok(messages[0].data.message.includes(scenario.expected));
95
+ strict_1.default.equal(messages[0].origin, "http://localhost:26107");
96
+ refresh();
97
+ await flush();
98
+ strict_1.default.equal(messages.length, 1, "自动刷新不反复弹窗");
99
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mu-agent-bridge",
3
- "version": "1.0.28",
3
+ "version": "1.0.29",
4
4
  "description": "将 AUN Agent 连接到本地桌面 AI 客户端的插件平台。",
5
5
  "main": "dist/src-ts/cli.js",
6
6
  "bin": {
@@ -5,7 +5,7 @@ const node_fs_1 = require("node:fs");
5
5
  const node_path_1 = require("node:path");
6
6
  const MANIFEST = {
7
7
  id: "abweb",
8
- version: "1.0.6",
8
+ version: "1.0.7",
9
9
  displayName: "Agent Bridge Web",
10
10
  entry: "dist/index.js",
11
11
  apiVersion: "1",
@@ -35,6 +35,25 @@
35
35
  let loading = false;
36
36
  const installingPackages = new Set();
37
37
  function error(message) { errorNode.textContent = message; errorNode.hidden = !message; }
38
+ function showResult(title, message, failed) {
39
+ node("#operation-result-title").textContent = title;
40
+ const detail = node("#operation-result-message");
41
+ detail.textContent = message;
42
+ detail.className = failed ? "error" : "";
43
+ const dialog = node("#operation-result");
44
+ if (!dialog.open)
45
+ dialog.showModal();
46
+ }
47
+ node("#operation-result-close").onclick = () => node("#operation-result").close();
48
+ window.addEventListener("message", (event) => {
49
+ const frame = channelPanel.querySelector("iframe.channel-frame");
50
+ if (event.origin !== window.location.origin || !frame || event.source !== frame.contentWindow)
51
+ return;
52
+ const result = event.data;
53
+ if (!result || result.type !== "mab:operation-result" || typeof result.title !== "string" || typeof result.message !== "string" || typeof result.failed !== "boolean")
54
+ return;
55
+ showResult(result.title, result.message, result.failed);
56
+ });
38
57
  async function loadCatalog() {
39
58
  const result = await request(`/api/plugins/catalog?q=${encodeURIComponent(packageSearchNode.value.trim())}`);
40
59
  const items = result.plugins ?? [];
@@ -113,7 +132,7 @@
113
132
  item.type = "button";
114
133
  item.textContent = label;
115
134
  item.title = title;
116
- item.onclick = () => { item.disabled = true; void run().catch((cause) => error(String(cause))).finally(() => { item.disabled = false; void load(false); }); };
135
+ item.onclick = () => { item.disabled = true; void run().catch((cause) => { const message = cause instanceof Error ? cause.message : String(cause); error(message); showResult(`${title}失败`, message, true); }).finally(() => { item.disabled = false; void load(false); }); };
117
136
  return item;
118
137
  }
119
138
  async function load(refreshCatalog = true) {
@@ -34,6 +34,21 @@
34
34
  const installingPackages = new Set<string>();
35
35
 
36
36
  function error(message: string): void { errorNode.textContent = message; errorNode.hidden = !message; }
37
+ function showResult(title: string, message: string, failed: boolean): void {
38
+ node<HTMLElement>("#operation-result-title").textContent = title;
39
+ const detail = node<HTMLElement>("#operation-result-message");
40
+ detail.textContent = message; detail.className = failed ? "error" : "";
41
+ const dialog = node<HTMLDialogElement>("#operation-result");
42
+ if (!dialog.open) dialog.showModal();
43
+ }
44
+ node<HTMLButtonElement>("#operation-result-close").onclick = () => node<HTMLDialogElement>("#operation-result").close();
45
+ window.addEventListener("message", (event: MessageEvent) => {
46
+ const frame = channelPanel.querySelector<HTMLIFrameElement>("iframe.channel-frame");
47
+ if (event.origin !== window.location.origin || !frame || event.source !== frame.contentWindow) return;
48
+ const result = event.data;
49
+ if (!result || result.type !== "mab:operation-result" || typeof result.title !== "string" || typeof result.message !== "string" || typeof result.failed !== "boolean") return;
50
+ showResult(result.title, result.message, result.failed);
51
+ });
37
52
  async function loadCatalog(): Promise<void> {
38
53
  const result = await request(`/api/plugins/catalog?q=${encodeURIComponent(packageSearchNode.value.trim())}`);
39
54
  const items: Array<{ name: string; version: string; description: string; type: string }> = result.plugins ?? [];
@@ -80,7 +95,7 @@
80
95
  }
81
96
  function button(label: string, title: string, run: () => Promise<void>): HTMLButtonElement {
82
97
  const item = document.createElement("button"); item.type = "button"; item.textContent = label; item.title = title;
83
- item.onclick = () => { item.disabled = true; void run().catch((cause) => error(String(cause))).finally(() => { item.disabled = false; void load(false); }); }; return item;
98
+ item.onclick = () => { item.disabled = true; void run().catch((cause) => { const message = cause instanceof Error ? cause.message : String(cause); error(message); showResult(`${title}失败`, message, true); }).finally(() => { item.disabled = false; void load(false); }); }; return item;
84
99
  }
85
100
  async function load(refreshCatalog = true): Promise<void> {
86
101
  if (loading) return;
@@ -21,6 +21,9 @@
21
21
  <div id="logs-panel" role="tabpanel" aria-labelledby="logs-tab" hidden><section><div class="section-heading"><h2>运行日志</h2><button id="logs-refresh" type="button">刷新日志</button></div><div class="table-scroll"><table><thead><tr><th>时间</th><th>级别</th><th>来源</th><th>消息</th><th>详细字段</th></tr></thead><tbody id="logs"></tbody></table></div><p id="logs-empty" class="muted" hidden>暂无运行日志</p><div class="section-heading"><button id="logs-prev" type="button">上一页</button><span id="logs-page" class="muted">第 1 页</span><button id="logs-next" type="button">下一页</button></div></section></div>
22
22
  <p id="error" class="error" role="alert" hidden></p>
23
23
  </main>
24
+ <dialog id="operation-result" aria-labelledby="operation-result-title" aria-describedby="operation-result-message">
25
+ <h2 id="operation-result-title">操作结果</h2><p id="operation-result-message"></p><button id="operation-result-close" type="button" autofocus>确定</button>
26
+ </dialog>
24
27
  <script src="/abweb/app.js"></script>
25
28
  </body>
26
29
  </html>
@@ -56,3 +56,7 @@ th { color: #566761; font-weight: 600; background: #edf1ef; }
56
56
  th:nth-child(1) { width: 145px; } th:nth-child(2) { width: 100px; } th:nth-child(3), th:nth-child(4) { width: 145px; } th:nth-child(5) { width: 62px; } th:nth-child(7) { width: 140px; }
57
57
  .message-error td:last-child { color: #a83427; }
58
58
  @media (max-width: 620px) { .shell { padding: 16px; } .section-heading { align-items: flex-start; flex-direction: column; } form, form input { width: 100%; } }
59
+
60
+ #operation-result { width: min(520px, calc(100% - 32px)); border: 1px solid #bfc9c5; border-radius: 8px; padding: 24px; }
61
+ #operation-result::backdrop { background: rgb(0 0 0 / 35%); }
62
+ #operation-result-message { white-space: pre-wrap; overflow-wrap: anywhere; margin: 16px 0; }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-bridge-abweb",
3
- "version": "1.0.6",
3
+ "version": "1.0.7",
4
4
  "description": "agent-bridge 的网页管理插件。",
5
5
  "keywords": [
6
6
  "mu-agent-bridge-plugin",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "id": "abweb",
3
- "version": "1.0.6",
3
+ "version": "1.0.7",
4
4
  "type": "management",
5
5
  "displayName": "Agent Bridge Web",
6
6
  "entry": "dist/src-ts/plugins/abweb/index.js",
@@ -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.15", type: "channel", displayName: "AUN 通道",
13
+ id: "aun", version: "1.0.17", 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",
@@ -87,9 +87,8 @@ class AunPlugin {
87
87
  await this.persist();
88
88
  this.context?.logger.info("AUN 身份已创建", { aid, owner });
89
89
  }
90
- if (!this.clients.has(aid))
91
- await this.connectAid(aid);
92
- return { aid, status: "已创建", connection: this.clients.has(aid) ? "ready" : "degraded" };
90
+ const connectionError = !this.clients.has(aid) ? await this.connectAid(aid) : undefined;
91
+ return { aid, status: "已创建", connection: this.clients.has(aid) ? "ready" : "degraded", ...(connectionError ? { connectionError } : {}) };
93
92
  }
94
93
  finally {
95
94
  this.creating = false;
@@ -244,7 +243,8 @@ class AunPlugin {
244
243
  catch (error) {
245
244
  this.status = "degraded";
246
245
  this.healthMessage = safeError(error);
247
- this.context?.logger.warn("AUN 通道连接失败", { error: this.healthMessage });
246
+ this.context?.logger.warn("AUN 通道连接失败", { aid, error: this.healthMessage });
247
+ return this.healthMessage;
248
248
  }
249
249
  }
250
250
  async deactivate(_reason) {
@@ -15,6 +15,12 @@
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);
18
24
  async function load(refreshForm = true) {
19
25
  if (loading) return;
20
26
  loading = true;
@@ -50,15 +56,22 @@
50
56
  async function bind(aid, pluginId) {
51
57
  try {
52
58
  await request(`/api/plugins/${encodeURIComponent(pluginId)}/aid`, "PATCH", { aid });
53
- await load(); show(`${aid} 已关联 ${pluginId}`);
54
- } catch (error) { show(`关联失败:${String(error)}`, true); }
59
+ await load(); notify("关联成功", `${aid} 已关联 ${pluginId}`);
60
+ } catch (error) { notify("关联失败", errorMessage(error), true); }
55
61
  }
56
62
  createForm.onsubmit = (event) => { event.preventDefault(); const aid = createForm.elements.aid.value.trim(); const owner = createForm.elements.ownerAid.value.trim();
57
- 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));
63
+ show("正在创建 AID 并连接,请稍候……");
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));
58
71
  };
59
72
  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 };
60
73
  if (configForm.elements.encryption_seed.value) changes.encryption_seed = configForm.elements.encryption_seed.value;
61
- 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));
74
+ 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));
62
75
  };
63
76
  find("#refresh").onclick = () => void load();
64
77
  void load();
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-bridge-aun",
3
- "version": "1.0.15",
3
+ "version": "1.0.17",
4
4
  "description": "mu-agent-bridge 的 AUN 通道插件。",
5
5
  "keywords": [
6
6
  "mu-agent-bridge-plugin",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "id": "aun",
3
- "version": "1.0.15",
3
+ "version": "1.0.17",
4
4
  "type": "channel",
5
5
  "displayName": "AUN 通道",
6
6
  "entry": "dist/src-ts/plugins/aun/index.js",