agent-bridge-abweb 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/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/app.ts +30 -5
- package/dist/src-ts/plugins/abweb/public/index.html +1 -1
- package/dist/src-ts/plugins/abweb/public/style.css +6 -0
- package/package.json +4 -2
- package/plugin.json +2 -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
|
-
|
|
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
|
})();
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
(() => {
|
|
2
|
-
type Plugin = { id: string; displayName: string; version: string; status: string; error?: string; path: string };
|
|
2
|
+
type Plugin = { id: string; displayName: string; version: string; status: string; type?: string; capabilities?: string[]; error?: string; path: string };
|
|
3
3
|
const node = <T extends HTMLElement>(selector: string): T => document.querySelector<T>(selector)!;
|
|
4
4
|
const pluginsNode = node<HTMLDivElement>("#plugins");
|
|
5
5
|
const messagesNode = node<HTMLTableSectionElement>("#messages");
|
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
const tokenNode = node<HTMLInputElement>("#token");
|
|
9
9
|
const pairCodeNode = node<HTMLInputElement>("#pair-code");
|
|
10
10
|
const packageNode = node<HTMLSelectElement>("#package-name");
|
|
11
|
+
const packageSearchNode = node<HTMLInputElement>("#package-search");
|
|
12
|
+
const catalogNode = node<HTMLDivElement>("#catalog");
|
|
11
13
|
const pluginPathNode = node<HTMLInputElement>("#plugin-path");
|
|
12
14
|
const picker = node<HTMLSelectElement>("#plugin-picker");
|
|
13
15
|
const aidList = node<HTMLDivElement>("#aids");
|
|
@@ -19,6 +21,17 @@
|
|
|
19
21
|
let pluginVersions: Record<string, any> = {};
|
|
20
22
|
|
|
21
23
|
function error(message: string): void { errorNode.textContent = message; errorNode.hidden = !message; }
|
|
24
|
+
async function loadCatalog(): Promise<void> {
|
|
25
|
+
const result = await request(`/api/plugins/catalog?q=${encodeURIComponent(packageSearchNode.value.trim())}`);
|
|
26
|
+
const items: Array<{ name: string; version: string; description: string; type: string }> = result.plugins ?? [];
|
|
27
|
+
packageNode.replaceChildren(...[["Agent", "agent"], ["Channel", "channel"], ["管理", "management"]].flatMap(([label, type]) => {
|
|
28
|
+
const group = document.createElement("optgroup"); group.label = label;
|
|
29
|
+
group.append(...items.filter((item) => item.type === type).map((item) => new Option(`${item.name} · v${item.version}`, item.name)));
|
|
30
|
+
return group.children.length ? [group] : [];
|
|
31
|
+
}));
|
|
32
|
+
catalogNode.replaceChildren(...items.map((plugin) => { const item = document.createElement("div"); item.className = "catalog-row"; item.textContent = `${plugin.type} · ${plugin.name} · ${plugin.description || "无描述"}`; return item; }));
|
|
33
|
+
if (!(result.plugins ?? []).length) catalogNode.textContent = "未找到已声明类型的 npm 插件";
|
|
34
|
+
}
|
|
22
35
|
async function request(path: string, method = "GET", body?: unknown): Promise<any> {
|
|
23
36
|
const response = await fetch(path, { method, headers: {
|
|
24
37
|
accept: "application/json", ...(body ? { "content-type": "application/json" } : {}),
|
|
@@ -78,7 +91,12 @@
|
|
|
78
91
|
function renderPlugins(items: Plugin[]): void {
|
|
79
92
|
plugins = items;
|
|
80
93
|
const selected = picker.value || items[0]?.id;
|
|
81
|
-
|
|
94
|
+
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")))]] as const;
|
|
95
|
+
picker.replaceChildren(...groups.flatMap(([label, group]) => {
|
|
96
|
+
const options = document.createElement("optgroup"); options.label = label;
|
|
97
|
+
options.append(...group.map((plugin) => new Option(`${plugin.displayName || plugin.id} · v${plugin.version} · ${plugin.status}`, plugin.id)));
|
|
98
|
+
return group.length ? [options] : [];
|
|
99
|
+
}));
|
|
82
100
|
picker.value = items.some((plugin) => plugin.id === selected) ? selected : items[0]?.id ?? "";
|
|
83
101
|
pluginsNode.replaceChildren();
|
|
84
102
|
for (const plugin of items.filter((item) => item.id === picker.value)) {
|
|
@@ -140,11 +158,18 @@
|
|
|
140
158
|
tokenNode.value = result.token; sessionStorage.setItem("mab-token", result.token); pairCodeNode.value = ""; return load();
|
|
141
159
|
}).catch((cause) => error(String(cause)));
|
|
142
160
|
};
|
|
143
|
-
node<
|
|
144
|
-
|
|
161
|
+
node<HTMLButtonElement>("#install-package").onclick = () => {
|
|
162
|
+
if (!packageNode.value) return error("请先选择插件");
|
|
145
163
|
void request("/api/plugins/install", "POST", { packageName: packageNode.value })
|
|
146
164
|
.then(() => load()).catch((cause) => error(String(cause)));
|
|
147
165
|
};
|
|
166
|
+
node<HTMLFormElement>("#install-name-form").onsubmit = (event) => {
|
|
167
|
+
event.preventDefault();
|
|
168
|
+
const packageName = node<HTMLInputElement>("#manual-package-name").value.trim();
|
|
169
|
+
void request("/api/plugins/install", "POST", { packageName }).then(() => load()).catch((cause) => error(String(cause)));
|
|
170
|
+
};
|
|
171
|
+
node<HTMLButtonElement>("#search-packages").onclick = () => void loadCatalog().catch((cause) => error(String(cause)));
|
|
172
|
+
packageSearchNode.onkeydown = (event) => { if (event.key === "Enter") void loadCatalog().catch((cause) => error(String(cause))); };
|
|
148
173
|
node<HTMLFormElement>("#install-local-form").onsubmit = (event) => {
|
|
149
174
|
event.preventDefault();
|
|
150
175
|
void request("/api/plugins/install", "POST", { source: pluginPathNode.value.trim() })
|
|
@@ -161,5 +186,5 @@
|
|
|
161
186
|
return load();
|
|
162
187
|
}).catch((cause) => { result.textContent = ""; error(String(cause)); });
|
|
163
188
|
};
|
|
164
|
-
void load();
|
|
189
|
+
void load(); void loadCatalog().catch((cause) => error(String(cause)));
|
|
165
190
|
})();
|
|
@@ -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; }
|
package/package.json
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-bridge-abweb",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "agent-bridge 的网页管理插件。",
|
|
5
|
+
"keywords": ["mu-agent-bridge-plugin", "mu-agent-bridge-management"],
|
|
5
6
|
"repository": { "type": "git", "url": "https://codeup.aliyun.com/6808da56fb3da14b899afd54/agent_bridge.git", "directory": "packages/abweb" },
|
|
6
7
|
"main": "dist/src-ts/plugins/abweb/index.js",
|
|
7
8
|
"files": [
|
|
@@ -11,7 +12,8 @@
|
|
|
11
12
|
"README.md"
|
|
12
13
|
],
|
|
13
14
|
"agentBridge": {
|
|
14
|
-
"plugin": "./plugin.json"
|
|
15
|
+
"plugin": "./plugin.json",
|
|
16
|
+
"type": "management"
|
|
15
17
|
},
|
|
16
18
|
"publishConfig": {
|
|
17
19
|
"access": "public",
|