mu-agent-bridge 1.0.0 → 1.0.1
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 +5 -0
- package/dist/src-ts/control-server.js +2 -0
- package/dist/src-ts/plugin-catalog.js +36 -0
- package/dist/src-ts/plugin-manager.js +30 -14
- package/dist/src-ts/plugin-manifest.js +6 -1
- package/dist/src-ts/plugins/abweb/index.js +1 -1
- package/dist/src-ts/plugins/abweb/public/app.js +34 -3
- package/dist/src-ts/plugins/abweb/public/index.html +1 -1
- package/dist/src-ts/plugins/abweb/public/style.css +6 -0
- package/dist/src-ts/plugins/doubao/index.js +1 -1
- package/dist/src-ts/plugins/dsh/index.js +1 -1
- package/dist/src-ts/plugins/qwenwork/index.js +1 -1
- package/dist/tests-ts/plugin-catalog.test.js +30 -0
- package/package.json +1 -1
- package/plugins/abweb/plugin.json +2 -2
- package/plugins/qwenwork/plugin.json +2 -1
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,36 @@
|
|
|
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
|
+
return items.filter((item) => Boolean(item));
|
|
36
|
+
}
|
|
@@ -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
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
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
|
}
|
|
@@ -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
|
-
|
|
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-
|
|
192
|
-
|
|
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><
|
|
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; }
|
|
@@ -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.
|
|
9
|
+
version: "1.0.1",
|
|
10
10
|
displayName: "DeepSeek Harness",
|
|
11
11
|
entry: "dist/index.js",
|
|
12
12
|
apiVersion: "1",
|
|
@@ -0,0 +1,30 @@
|
|
|
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
|
+
});
|
|
25
|
+
(0, node_test_1.default)("插件清单类型从能力兼容识别,冲突类型拒绝", () => {
|
|
26
|
+
const base = { id: "sample", version: "1.0.0", entry: "index.js", apiVersion: "1", capabilities: ["chat"] };
|
|
27
|
+
strict_1.default.equal((0, plugin_manifest_1.validateManifest)(base).type, "agent");
|
|
28
|
+
strict_1.default.equal((0, plugin_manifest_1.validateManifest)({ ...base, capabilities: ["channel", "channel.receive", "channel.send"], type: "channel" }).type, "channel");
|
|
29
|
+
strict_1.default.throws(() => (0, plugin_manifest_1.validateManifest)({ ...base, type: "channel" }), /类型|type/);
|
|
30
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "abweb",
|
|
3
|
-
"version": "1.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
|
-
|