mu-agent-bridge 1.0.0 → 1.0.2

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
@@ -56,6 +56,11 @@ abweb 的“配置码”栏;配对码 10 分钟有效,只能使用一次,
56
56
  `package.json` 中声明 `agentBridge.plugin`,平台复用统一的 `activate`、`deactivate`、`health`、
57
57
  `handle` 生命周期。已加载插件可以通过管理 API 重载;新增 npm 包后重启 daemon 最可靠。
58
58
 
59
+ abweb 插件目录从 npm 搜索带 `mu-agent-bridge-plugin` 关键词的包,按包元数据中的
60
+ `agentBridge.type` 展示 Agent、Channel 和管理插件;安装后以 `plugin.json` 的类型和能力
61
+ 再次校验,不根据包名推断类型。搜索结果有收录延迟,可在页面直接输入 `agent-bridge-*`
62
+ 包名安装。已安装插件按清单能力兼容旧版分类;AUN Channel 仍需单独实现并发布后才会出现。
63
+
59
64
  插件仍在 daemon 的同一个 Node.js 进程内运行。修改插件入口依赖后应重启 daemon,避免模块缓存
60
65
  影响新代码。
61
66
 
@@ -126,6 +126,8 @@ class ControlServer {
126
126
  }
127
127
  if (method === "GET" && url.pathname === "/api/plugins")
128
128
  return this.json(res, 200, { ok: true, plugins: this.options.manager.list() });
129
+ if (method === "GET" && url.pathname === "/api/plugins/catalog")
130
+ return this.json(res, 200, { ok: true, plugins: await this.options.manager.searchPackages(url.searchParams.get("q") ?? "") });
129
131
  if (method === "GET" && url.pathname === "/api/routes")
130
132
  return this.json(res, 200, []);
131
133
  if (parts[0] === "api" && parts[1] === "plugins" && parts[2]) {
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.searchPluginCatalog = searchPluginCatalog;
4
+ const registry = "https://registry.npmjs.org";
5
+ const pluginName = /^agent-bridge-[a-z0-9-]+$/;
6
+ async function getJson(url) {
7
+ const response = await fetch(url, { signal: AbortSignal.timeout(10_000) });
8
+ if (!response.ok)
9
+ throw new Error(`npm 查询失败:HTTP ${response.status}`);
10
+ return response.json();
11
+ }
12
+ async function searchPluginCatalog(query = "", get = getJson) {
13
+ if (query.length > 80)
14
+ throw new Error("搜索词过长");
15
+ const params = new URLSearchParams({ text: `keywords:mu-agent-bridge-plugin ${query.trim()}`.trim(), size: "30" });
16
+ const result = await get(`${registry}/-/v1/search?${params}`);
17
+ const candidates = Array.isArray(result?.objects) ? result.objects.slice(0, 30) : [];
18
+ const items = await Promise.all(candidates.map(async (entry) => {
19
+ const name = entry?.package?.name;
20
+ if (typeof name !== "string" || !pluginName.test(name))
21
+ return;
22
+ try {
23
+ const metadata = await get(`${registry}/${encodeURIComponent(name)}`);
24
+ const version = metadata?.["dist-tags"]?.latest;
25
+ const pkg = metadata?.versions?.[version];
26
+ const type = pkg?.agentBridge?.type;
27
+ if (!pkg?.agentBridge?.plugin || !["agent", "channel", "management"].includes(type))
28
+ return;
29
+ return { name, version, description: typeof entry.package.description === "string" ? entry.package.description : "", type };
30
+ }
31
+ catch {
32
+ return;
33
+ }
34
+ }));
35
+ const needle = query.trim().toLowerCase();
36
+ return items.filter((item) => Boolean(item)).filter((item) => !needle || `${item.name} ${item.description}`.toLowerCase().includes(needle));
37
+ }
@@ -10,6 +10,7 @@ const node_path_1 = require("node:path");
10
10
  const agent_1 = require("./agent");
11
11
  const plugin_manifest_1 = require("./plugin-manifest");
12
12
  const version_manager_1 = require("./version-manager");
13
+ const plugin_catalog_1 = require("./plugin-catalog");
13
14
  const logger = { info() { }, warn() { }, error() { } };
14
15
  const execFileAsync = (0, node_util_1.promisify)(node_child_process_1.execFile);
15
16
  class PluginManager {
@@ -135,21 +136,36 @@ class PluginManager {
135
136
  if (this.plugins.has(name.replace(/^agent-bridge-/, "")))
136
137
  throw new agent_1.AgentError("插件已经存在");
137
138
  const prefix = (0, node_path_1.join)(this.options.stateDir, "npm", name);
139
+ if (await this.existsDir(prefix))
140
+ throw new agent_1.AgentError("插件包已经安装");
138
141
  await (0, promises_1.mkdir)(prefix, { recursive: true });
139
- if (process.platform === "win32")
140
- await execFileAsync(process.env.ComSpec ?? "cmd.exe", ["/d", "/s", "/c", `npm install --prefix "${prefix}" --no-save --ignore-scripts ${packageName}`]);
141
- else
142
- await execFileAsync("npm", ["install", "--prefix", prefix, "--no-save", "--ignore-scripts", packageName]);
143
- const directory = (0, node_path_1.join)(prefix, "node_modules", name);
144
- const meta = JSON.parse(await (0, promises_1.readFile)((0, node_path_1.join)(directory, "package.json"), "utf8"));
145
- if (!meta.agentBridge?.plugin)
146
- throw new agent_1.AgentError("npm 包未声明 agentBridge.plugin");
147
- await this.discoverDirectory(directory, name, (0, node_path_1.resolve)(directory, meta.agentBridge.plugin));
148
- const record = this.plugins.get(JSON.parse(await (0, promises_1.readFile)((0, node_path_1.resolve)(directory, meta.agentBridge.plugin), "utf8")).id)?.record;
149
- if (!record)
150
- throw new agent_1.AgentError("插件发现失败");
151
- return record;
142
+ try {
143
+ if (process.platform === "win32")
144
+ await execFileAsync(process.env.ComSpec ?? "cmd.exe", ["/d", "/s", "/c", `npm install --prefix "${prefix}" --no-save --ignore-scripts ${packageName}`]);
145
+ else
146
+ await execFileAsync("npm", ["install", "--prefix", prefix, "--no-save", "--ignore-scripts", packageName]);
147
+ const directory = (0, node_path_1.join)(prefix, "node_modules", name);
148
+ const meta = JSON.parse(await (0, promises_1.readFile)((0, node_path_1.join)(directory, "package.json"), "utf8"));
149
+ if (meta.name !== name || !meta.version || !meta.agentBridge?.plugin)
150
+ throw new agent_1.AgentError("npm 包名称或插件声明无效");
151
+ const manifestFile = (0, node_path_1.resolve)(directory, meta.agentBridge.plugin);
152
+ if (!manifestFile.startsWith(`${(0, node_path_1.resolve)(directory)}${node_path_1.sep}`))
153
+ throw new agent_1.AgentError("插件清单路径越界");
154
+ const manifest = (0, plugin_manifest_1.validateManifest)(JSON.parse(await (0, promises_1.readFile)(manifestFile, "utf8")), directory);
155
+ if (manifest.id !== name.slice("agent-bridge-".length) || manifest.version !== meta.version || (meta.agentBridge.type && meta.agentBridge.type !== manifest.type))
156
+ throw new agent_1.AgentError("npm 包与插件清单不匹配");
157
+ await this.discoverDirectory(directory, name, manifestFile);
158
+ const record = this.plugins.get(manifest.id)?.record;
159
+ if (!record)
160
+ throw new agent_1.AgentError("插件发现失败");
161
+ return record;
162
+ }
163
+ catch (error) {
164
+ await (0, promises_1.rm)(prefix, { recursive: true, force: true });
165
+ throw error;
166
+ }
152
167
  }
168
+ async searchPackages(query = "") { return (0, plugin_catalog_1.searchPluginCatalog)(query); }
153
169
  async install(source) {
154
170
  const manifest = (0, plugin_manifest_1.validateManifest)(JSON.parse(await (0, promises_1.readFile)((0, node_path_1.join)(source, "plugin.json"), "utf8")), source);
155
171
  const target = (0, node_path_1.join)(this.root, manifest.id, manifest.version);
@@ -416,7 +432,7 @@ class PluginManager {
416
432
  const manifest = (0, plugin_manifest_1.validateManifest)(JSON.parse(await (0, promises_1.readFile)(manifestFile, "utf8")), directory);
417
433
  if (this.plugins.has(manifest.id))
418
434
  return;
419
- this.plugins.set(manifest.id, { record: { id: manifest.id, version: manifest.version, displayName: manifest.displayName ?? manifest.id, status: "discovered", capabilities: manifest.capabilities, manifest, path: directory } });
435
+ this.plugins.set(manifest.id, { record: { id: manifest.id, version: manifest.version, type: manifest.type ?? "other", displayName: manifest.displayName ?? manifest.id, status: "discovered", capabilities: manifest.capabilities, manifest, path: directory } });
420
436
  }
421
437
  catch (error) {
422
438
  throw new agent_1.AgentError(`invalid plugin ${label}: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
@@ -18,6 +18,11 @@ function validateManifest(value, baseDir) {
18
18
  throw new agent_1.AgentError("unsupported plugin apiVersion");
19
19
  if (!Array.isArray(m.capabilities) || m.capabilities.some((c) => typeof c !== "string" || !c.trim()))
20
20
  throw new agent_1.AgentError("invalid plugin capabilities");
21
+ if (m.type && !["agent", "channel", "management", "other"].includes(m.type))
22
+ throw new agent_1.AgentError("invalid plugin type");
23
+ const inferredType = m.capabilities.includes("channel") ? "channel" : m.capabilities.includes("chat") ? "agent" : m.capabilities.includes("management") ? "management" : "other";
24
+ if (m.type && inferredType !== "other" && m.type !== inferredType)
25
+ throw new agent_1.AgentError("插件类型与能力不匹配");
21
26
  if (m.process && m.process !== "in-process" && m.process !== "isolated")
22
27
  throw new agent_1.AgentError("invalid plugin process");
23
28
  if (baseDir) {
@@ -27,5 +32,5 @@ function validateManifest(value, baseDir) {
27
32
  if ((0, node_path_1.isAbsolute)(m.entry) || rel.startsWith(`..${node_path_1.sep}`) || rel === "..")
28
33
  throw new agent_1.AgentError("plugin entry path escapes plugin directory");
29
34
  }
30
- return { id: m.id, version: m.version, displayName: m.displayName ?? m.id, entry: m.entry, apiVersion: m.apiVersion, process: m.process ?? "in-process", capabilities: [...m.capabilities], configSchema: m.configSchema, routes: m.routes };
35
+ return { id: m.id, version: m.version, displayName: m.displayName ?? m.id, type: m.type ?? inferredType, entry: m.entry, apiVersion: m.apiVersion, process: m.process ?? "in-process", capabilities: [...m.capabilities], configSchema: m.configSchema, routes: m.routes };
31
36
  }
@@ -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.0",
8
+ version: "1.0.1",
9
9
  displayName: "Agent Bridge Web",
10
10
  entry: "dist/index.js",
11
11
  apiVersion: "1",
@@ -7,6 +7,8 @@
7
7
  const tokenNode = node("#token");
8
8
  const pairCodeNode = node("#pair-code");
9
9
  const packageNode = node("#package-name");
10
+ const packageSearchNode = node("#package-search");
11
+ const catalogNode = node("#catalog");
10
12
  const pluginPathNode = node("#plugin-path");
11
13
  const picker = node("#plugin-picker");
12
14
  const aidList = node("#aids");
@@ -17,6 +19,19 @@
17
19
  let pluginConfigs = {};
18
20
  let pluginVersions = {};
19
21
  function error(message) { errorNode.textContent = message; errorNode.hidden = !message; }
22
+ async function loadCatalog() {
23
+ const result = await request(`/api/plugins/catalog?q=${encodeURIComponent(packageSearchNode.value.trim())}`);
24
+ const items = result.plugins ?? [];
25
+ packageNode.replaceChildren(...[["Agent", "agent"], ["Channel", "channel"], ["管理", "management"]].flatMap(([label, type]) => {
26
+ const group = document.createElement("optgroup");
27
+ group.label = label;
28
+ group.append(...items.filter((item) => item.type === type).map((item) => new Option(`${item.name} · v${item.version}`, item.name)));
29
+ return group.children.length ? [group] : [];
30
+ }));
31
+ catalogNode.replaceChildren(...items.map((plugin) => { const item = document.createElement("div"); item.className = "catalog-row"; item.textContent = `${plugin.type} · ${plugin.name} · ${plugin.description || "无描述"}`; return item; }));
32
+ if (!(result.plugins ?? []).length)
33
+ catalogNode.textContent = "未找到已声明类型的 npm 插件";
34
+ }
20
35
  async function request(path, method = "GET", body) {
21
36
  const response = await fetch(path, { method, headers: {
22
37
  accept: "application/json", ...(body ? { "content-type": "application/json" } : {}),
@@ -96,7 +111,13 @@
96
111
  function renderPlugins(items) {
97
112
  plugins = items;
98
113
  const selected = picker.value || items[0]?.id;
99
- picker.replaceChildren(...items.map((plugin) => new Option(`${plugin.displayName || plugin.id} · v${plugin.version} · ${plugin.status}`, plugin.id)));
114
+ const groups = [["Agent", items.filter((plugin) => plugin.type === "agent" || (!plugin.type && plugin.capabilities?.includes("chat")))], ["Channel", items.filter((plugin) => plugin.type === "channel" || plugin.capabilities?.includes("channel"))], ["管理", items.filter((plugin) => plugin.type === "management" || (!plugin.type && plugin.capabilities?.includes("management")))]];
115
+ picker.replaceChildren(...groups.flatMap(([label, group]) => {
116
+ const options = document.createElement("optgroup");
117
+ options.label = label;
118
+ options.append(...group.map((plugin) => new Option(`${plugin.displayName || plugin.id} · v${plugin.version} · ${plugin.status}`, plugin.id)));
119
+ return group.length ? [options] : [];
120
+ }));
100
121
  picker.value = items.some((plugin) => plugin.id === selected) ? selected : items[0]?.id ?? "";
101
122
  pluginsNode.replaceChildren();
102
123
  for (const plugin of items.filter((item) => item.id === picker.value)) {
@@ -188,11 +209,20 @@
188
209
  return load();
189
210
  }).catch((cause) => error(String(cause)));
190
211
  };
191
- node("#install-form").onsubmit = (event) => {
192
- event.preventDefault();
212
+ node("#install-package").onclick = () => {
213
+ if (!packageNode.value)
214
+ return error("请先选择插件");
193
215
  void request("/api/plugins/install", "POST", { packageName: packageNode.value })
194
216
  .then(() => load()).catch((cause) => error(String(cause)));
195
217
  };
218
+ node("#install-name-form").onsubmit = (event) => {
219
+ event.preventDefault();
220
+ const packageName = node("#manual-package-name").value.trim();
221
+ void request("/api/plugins/install", "POST", { packageName }).then(() => load()).catch((cause) => error(String(cause)));
222
+ };
223
+ node("#search-packages").onclick = () => void loadCatalog().catch((cause) => error(String(cause)));
224
+ packageSearchNode.onkeydown = (event) => { if (event.key === "Enter")
225
+ void loadCatalog().catch((cause) => error(String(cause))); };
196
226
  node("#install-local-form").onsubmit = (event) => {
197
227
  event.preventDefault();
198
228
  void request("/api/plugins/install", "POST", { source: pluginPathNode.value.trim() })
@@ -210,4 +240,5 @@
210
240
  }).catch((cause) => { result.textContent = ""; error(String(cause)); });
211
241
  };
212
242
  void load();
243
+ void loadCatalog().catch((cause) => error(String(cause)));
213
244
  })();
@@ -13,7 +13,7 @@
13
13
  <nav class="tabs" role="tablist" aria-label="管理视图"><button id="settings-tab" type="button" role="tab" aria-selected="true" aria-controls="settings-panel">插件设置</button><button id="messages-tab" type="button" role="tab" aria-selected="false" aria-controls="messages-panel">通信记录</button></nav>
14
14
  <div id="settings-panel" role="tabpanel" aria-labelledby="settings-tab">
15
15
  <section><div class="section-heading"><h2>版本管理</h2><button id="check-versions" type="button">检查更新</button></div><div id="versions" class="muted"></div></section>
16
- <section><div class="section-heading"><h2>插件</h2><select id="plugin-picker" aria-label="选择插件"></select></div><div id="plugins" class="plugins"></div><form id="install-form"><select id="package-name" aria-label="选择 npm 插件"><option value="agent-bridge-qwenwork">千问办公</option><option value="agent-bridge-abweb">管理页面</option><option value="agent-bridge-dsh">DeepSeek Harness</option><option value="agent-bridge-doubao">豆包</option></select><button type="submit">安装 npm 插件</button></form><form id="install-local-form"><input id="plugin-path" required placeholder="Windows 上的插件目录路径" aria-label="本地插件目录"><button type="submit">安装本地插件</button></form></section>
16
+ <section><div class="section-heading"><h2>插件</h2><select id="plugin-picker" aria-label="选择插件"></select></div><div id="plugins" class="plugins"></div><div class="plugin-catalog"><div class="section-heading"><h3>npm 插件目录</h3><button id="search-packages" type="button">刷新目录</button></div><input id="package-search" placeholder="搜索插件名称或功能" aria-label="搜索 npm 插件"><select id="package-name" aria-label="选择 npm 插件"></select><button id="install-package" type="button">安装选中插件</button><form id="install-name-form"><input id="manual-package-name" required pattern="agent-bridge-[a-z0-9-]+" placeholder="输入 npm 包名,例如 agent-bridge-workbuddy" aria-label="直接输入 npm 包名"><button type="submit">按包名安装</button></form><div id="catalog" class="catalog"></div></div><form id="install-local-form"><input id="plugin-path" required placeholder="Windows 上的插件目录路径" aria-label="本地插件目录"><button type="submit">安装本地插件</button></form></section>
17
17
  <section><div class="section-heading"><h2>已连接 AID</h2><span id="aid-count" class="muted"></span></div><div id="aids" class="aid-list"></div><form id="create-aid-form"><input id="new-aid" required placeholder="新 AID,例如 new.agentid.cn" aria-label="新 AID"><input id="owner-aid" required placeholder="owner AID" aria-label="owner AID"><button type="submit">创建 AID</button></form><p id="aid-result" class="muted" role="status"></p><p class="muted">在插件列表中选择插件,再用“关联 AID”下拉框保存对应身份。</p></section>
18
18
  </div>
19
19
  <div id="messages-panel" role="tabpanel" aria-labelledby="messages-tab" hidden><section><div class="section-heading"><h2>最近通信</h2></div><div class="table-scroll"><table><thead><tr><th scope="col">时间</th><th scope="col">插件</th><th scope="col">接收 AID</th><th scope="col">来源 Peer</th><th scope="col">方向</th><th scope="col">消息</th><th scope="col">错误</th></tr></thead><tbody id="messages"></tbody></table></div><p id="messages-empty" class="muted" hidden>暂无通信记录</p></section></div>
@@ -29,6 +29,12 @@ form { margin-top: 14px; flex-wrap: wrap; }
29
29
  .plugin-main { flex-wrap: wrap; }
30
30
  .controls { flex-wrap: wrap; }
31
31
  .controls select { max-width: 210px; }
32
+ .plugin-catalog { border-top: 1px solid #e0e5e2; margin-top: 18px; padding-top: 14px; }
33
+ .plugin-catalog h3 { font-size: 14px; margin: 0; }
34
+ .plugin-catalog input { width: min(100%, 420px); margin: 10px 0; }
35
+ .catalog { margin-top: 10px; }
36
+ .catalog-row { padding: 7px 0; color: #566761; font-size: 13px; border-bottom: 1px solid #edf1ef; }
37
+ .plugin-catalog form { margin-top: 8px; }
32
38
  .muted { color: #65736e; font-size: 13px; }
33
39
  .status { font-size: 12px; padding: 3px 7px; background: #e7ebea; border-radius: 4px; }
34
40
  .status-ready { color: #136a44; background: #def3e5; }
@@ -4,7 +4,7 @@ exports.DoubaoPlugin = exports.doubaoManifest = void 0;
4
4
  const cdp_client_1 = require("./cdp-client");
5
5
  exports.doubaoManifest = {
6
6
  id: "doubao",
7
- version: "1.0.0",
7
+ version: "1.0.1",
8
8
  displayName: "豆包",
9
9
  entry: "dist/index.js",
10
10
  apiVersion: "1",
@@ -6,7 +6,7 @@ const dsh_agent_1 = require("../../dsh-agent");
6
6
  const openai_adapter_1 = require("../qwenwork/openai-adapter");
7
7
  exports.dshManifest = {
8
8
  id: "dsh",
9
- version: "1.0.0",
9
+ version: "1.0.1",
10
10
  displayName: "DeepSeek Harness",
11
11
  entry: "dist/index.js",
12
12
  apiVersion: "1",
@@ -6,7 +6,7 @@ const openai_adapter_1 = require("./openai-adapter");
6
6
  const cdp_client_1 = require("./cdp-client");
7
7
  exports.qwenworkManifest = {
8
8
  id: "qwenwork",
9
- version: "1.0.0",
9
+ version: "1.0.1",
10
10
  displayName: "千问办公",
11
11
  entry: "dist/index.js",
12
12
  apiVersion: "1",
@@ -25,6 +25,19 @@ const plugin_manager_1 = require("../src-ts/plugin-manager");
25
25
  await server.stop();
26
26
  }
27
27
  });
28
+ (0, node_test_1.default)("插件目录 API 返回远程候选包", async () => {
29
+ const manager = { list: () => [], searchPackages: async (query) => query === "workbuddy" ? [{ name: "agent-bridge-workbuddy", version: "1.0.0", type: "agent", description: "办公 Agent" }] : [] };
30
+ const server = new control_server_1.ControlServer({ manager, host: "127.0.0.1", port: 0 });
31
+ const address = await server.start();
32
+ try {
33
+ const response = await fetch(`http://${address.host}:${address.port}/api/plugins/catalog?q=workbuddy`);
34
+ strict_1.default.equal(response.status, 200);
35
+ strict_1.default.equal((await response.json()).plugins[0].type, "agent");
36
+ }
37
+ finally {
38
+ await server.stop();
39
+ }
40
+ });
28
41
  (0, node_test_1.default)("control server dispatches plugin HTTP routes and unregisters them on stop", async () => {
29
42
  const root = await (0, promises_1.mkdtemp)((0, node_path_1.join)((0, node_os_1.tmpdir)(), "agent-bridge-http-plugin-"));
30
43
  try {
@@ -0,0 +1,31 @@
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 plugin_catalog_1 = require("../src-ts/plugin-catalog");
9
+ const plugin_manifest_1 = require("../src-ts/plugin-manifest");
10
+ (0, node_test_1.default)("远程目录只返回声明平台类型的插件包", async () => {
11
+ const get = async (url) => {
12
+ if (url.includes("/-/v1/search"))
13
+ return { objects: [
14
+ { package: { name: "agent-bridge-workbuddy", version: "1.2.0", description: "办公 Agent" } },
15
+ { package: { name: "agent-bridge-aun", version: "1.0.0", description: "AUN 通道" } },
16
+ { package: { name: "agent-bridge-fake", version: "1.0.0" } },
17
+ ] };
18
+ const name = decodeURIComponent(url.split("/").pop());
19
+ return { "dist-tags": { latest: name === "agent-bridge-workbuddy" ? "1.2.0" : "1.0.0" }, versions: { [name === "agent-bridge-workbuddy" ? "1.2.0" : "1.0.0"]: { agentBridge: name === "agent-bridge-fake" ? undefined : { plugin: "./plugin.json", type: name === "agent-bridge-aun" ? "channel" : "agent" } } } };
20
+ };
21
+ strict_1.default.deepEqual((await (0, plugin_catalog_1.searchPluginCatalog)("", get)).map(({ name, type }) => [name, type]), [
22
+ ["agent-bridge-workbuddy", "agent"], ["agent-bridge-aun", "channel"],
23
+ ]);
24
+ strict_1.default.deepEqual((await (0, plugin_catalog_1.searchPluginCatalog)("aun", get)).map(({ name }) => name), ["agent-bridge-aun"]);
25
+ });
26
+ (0, node_test_1.default)("插件清单类型从能力兼容识别,冲突类型拒绝", () => {
27
+ const base = { id: "sample", version: "1.0.0", entry: "index.js", apiVersion: "1", capabilities: ["chat"] };
28
+ strict_1.default.equal((0, plugin_manifest_1.validateManifest)(base).type, "agent");
29
+ strict_1.default.equal((0, plugin_manifest_1.validateManifest)({ ...base, capabilities: ["channel", "channel.receive", "channel.send"], type: "channel" }).type, "channel");
30
+ strict_1.default.throws(() => (0, plugin_manifest_1.validateManifest)({ ...base, type: "channel" }), /类型|type/);
31
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mu-agent-bridge",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "将 AUN Agent 连接到本地桌面 AI 客户端的插件平台。",
5
5
  "main": "dist/src-ts/cli.js",
6
6
  "bin": { "mab": "dist/src-ts/cli.js", "agent-bridge": "dist/src-ts/cli.js" },
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "id": "abweb",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
+ "type": "management",
4
5
  "displayName": "Agent Bridge Web",
5
6
  "entry": "dist/index.js",
6
7
  "apiVersion": "1",
@@ -8,4 +9,3 @@
8
9
  "capabilities": ["web", "management", "health"],
9
10
  "configSchema": "config.schema.json"
10
11
  }
11
-
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "id": "qwenwork",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
+ "type": "agent",
4
5
  "displayName": "千问办公",
5
6
  "entry": "dist/index.js",
6
7
  "apiVersion": "1",