agent-bridge-abweb 1.0.11 → 1.0.15
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 +2 -0
- package/dist/src-ts/agent.js +35 -0
- package/dist/src-ts/plugins/abweb/index.js +1 -1
- package/dist/src-ts/plugins/abweb/public/app.js +73 -5
- package/dist/src-ts/plugins/abweb/public/app.ts +57 -5
- package/dist/src-ts/plugins/abweb/public/index.html +15 -0
- package/dist/src-ts/plugins/abweb/public/style.css +4 -0
- package/package.json +1 -1
- package/plugin.json +1 -1
package/README.md
CHANGED
package/dist/src-ts/agent.js
CHANGED
|
@@ -1,7 +1,42 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.AgentOperationError = exports.AgentError = void 0;
|
|
4
|
+
exports.abortable = abortable;
|
|
4
5
|
exports.errorDetails = errorDetails;
|
|
6
|
+
async function abortable(promise, signal) {
|
|
7
|
+
if (!signal)
|
|
8
|
+
return promise;
|
|
9
|
+
if (signal.aborted) {
|
|
10
|
+
// promise 已在调用方启动,先观察其拒绝,避免预取消时产生未处理拒绝。
|
|
11
|
+
void promise.catch(() => undefined);
|
|
12
|
+
signal.throwIfAborted();
|
|
13
|
+
}
|
|
14
|
+
return new Promise((resolve, reject) => {
|
|
15
|
+
let settled = false;
|
|
16
|
+
const cleanup = () => signal.removeEventListener("abort", abort);
|
|
17
|
+
const abort = () => {
|
|
18
|
+
if (settled)
|
|
19
|
+
return;
|
|
20
|
+
settled = true;
|
|
21
|
+
cleanup();
|
|
22
|
+
reject(signal.reason);
|
|
23
|
+
};
|
|
24
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
25
|
+
promise.then((value) => {
|
|
26
|
+
if (settled)
|
|
27
|
+
return;
|
|
28
|
+
settled = true;
|
|
29
|
+
cleanup();
|
|
30
|
+
resolve(value);
|
|
31
|
+
}, (error) => {
|
|
32
|
+
if (settled)
|
|
33
|
+
return;
|
|
34
|
+
settled = true;
|
|
35
|
+
cleanup();
|
|
36
|
+
reject(error);
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
}
|
|
5
40
|
class AgentError extends Error {
|
|
6
41
|
constructor(message, options) {
|
|
7
42
|
super(message, options);
|
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
let logHasNext = false;
|
|
35
35
|
let loading = false;
|
|
36
36
|
const installingPackages = new Set();
|
|
37
|
+
let assistantOriginal = {};
|
|
37
38
|
function error(message) { errorNode.textContent = message; errorNode.hidden = !message; }
|
|
38
39
|
function showResult(title, message, failed) {
|
|
39
40
|
node("#operation-result-title").textContent = title;
|
|
@@ -76,7 +77,7 @@
|
|
|
76
77
|
head.append(headRow);
|
|
77
78
|
const body = document.createElement("tbody");
|
|
78
79
|
for (const plugin of items) {
|
|
79
|
-
const installed = plugins.find((item) => item.id === plugin.name.replace(/^agent-bridge-/, ""));
|
|
80
|
+
const installed = plugins.find((item) => item.id === (plugin.id ?? plugin.name.replace(/^agent-bridge-/, "")));
|
|
80
81
|
const versionStatus = installed ? pluginVersions[installed.id] : undefined;
|
|
81
82
|
const isUpgrade = Boolean(installed && versionStatus?.packageName === plugin.name && newerVersion(plugin.version, installed.version));
|
|
82
83
|
const row = document.createElement("tr");
|
|
@@ -218,7 +219,8 @@
|
|
|
218
219
|
versionsNode.replaceChildren(...entries.map((entry) => {
|
|
219
220
|
const row = document.createElement("div");
|
|
220
221
|
row.className = "version-row";
|
|
221
|
-
|
|
222
|
+
const state = entry.state === "unpublished" ? "未发布" : entry.state;
|
|
223
|
+
row.textContent = `${entry.packageName} · 当前 ${entry.currentVersion} · ${entry.latestVersion ? `最新 ${entry.latestVersion}` : "最新版本未知"} · ${state}${entry.error ? ` · ${entry.error}` : ""}`;
|
|
222
224
|
return row;
|
|
223
225
|
}));
|
|
224
226
|
if (!entries.length)
|
|
@@ -309,7 +311,7 @@
|
|
|
309
311
|
status.className = `status status-${plugin.status}`;
|
|
310
312
|
status.textContent = plugin.status;
|
|
311
313
|
if (plugin.type === "agent")
|
|
312
|
-
type.textContent +=
|
|
314
|
+
type.textContent += plugin.manifest?.executionMode === "cli" ? " · CLI" : " · 桌面";
|
|
313
315
|
const dependencies = Object.entries(plugin.manifest?.dependencies ?? {});
|
|
314
316
|
if (dependencies.length) {
|
|
315
317
|
const detail = document.createElement("small");
|
|
@@ -344,6 +346,8 @@
|
|
|
344
346
|
controls.className = "controls";
|
|
345
347
|
if (plugin.manifest?.executionMode === "cli")
|
|
346
348
|
controls.append(button("配置", `配置 ${plugin.id}`, () => configureCli(plugin)));
|
|
349
|
+
if (plugin.id === "personal-assistant")
|
|
350
|
+
controls.append(button("配置模型", "配置个人助理模型来源", () => configureAssistant(plugin)));
|
|
347
351
|
if (pluginVersions[plugin.id]?.state === "update-available" && plugin.path.includes("node_modules")) {
|
|
348
352
|
controlsUpgrade(plugin.id);
|
|
349
353
|
controls.append(button("下载", `下载 ${plugin.id}`, async () => { await request(`/api/plugins/${encodeURIComponent(plugin.id)}/download`, "POST", { packageName: plugin.packageName, version: pluginVersions[plugin.id]?.latestVersion }); await load(); }));
|
|
@@ -401,6 +405,70 @@
|
|
|
401
405
|
detail.hidden = false;
|
|
402
406
|
}).finally(() => { save.disabled = false; });
|
|
403
407
|
};
|
|
408
|
+
node("#assistant-config-cancel").onclick = () => node("#assistant-config-dialog").close();
|
|
409
|
+
async function configureAssistant(plugin) {
|
|
410
|
+
const [config, sourceResult] = await Promise.all([
|
|
411
|
+
request(`/api/plugins/${encodeURIComponent(plugin.id)}/config`),
|
|
412
|
+
request("/api/model-providers"),
|
|
413
|
+
]);
|
|
414
|
+
assistantOriginal = config;
|
|
415
|
+
const select = node("#assistant-model-provider");
|
|
416
|
+
select.replaceChildren();
|
|
417
|
+
const add = (value, label, disabled = false) => { const option = document.createElement("option"); option.value = value; option.textContent = label; option.disabled = disabled; select.append(option); };
|
|
418
|
+
add("openai", "OpenAI 兼容接口");
|
|
419
|
+
if (config.modelProvider === "claude-cli")
|
|
420
|
+
add("claude-cli", "Claude CLI(原有配置,用户默认模型)");
|
|
421
|
+
for (const provider of sourceResult.providers ?? [])
|
|
422
|
+
add(`plugin:${provider.id}`, `${provider.displayName || provider.id}${provider.available ? "" : `(不可用:${provider.reason || provider.status})`}`, !provider.available);
|
|
423
|
+
if (config.modelProvider === "plugin" && config.modelPlugin && !(sourceResult.providers ?? []).some((item) => item.id === config.modelPlugin))
|
|
424
|
+
add(`plugin:${config.modelPlugin}`, `${config.modelPlugin}(未安装或未声明模型来源)`, true);
|
|
425
|
+
select.value = config.modelProvider === "plugin" && config.modelPlugin ? `plugin:${config.modelPlugin}` : config.modelProvider ?? "openai";
|
|
426
|
+
node("#assistant-model").value = config.model ?? "";
|
|
427
|
+
node("#assistant-endpoint").value = config.endpoint ?? "";
|
|
428
|
+
node("#assistant-api-key").value = "";
|
|
429
|
+
node("#assistant-api-key").placeholder = config.apiKey ? "已配置,留空保留" : "可选,本地免认证服务可留空";
|
|
430
|
+
node("#assistant-timeout").value = String(config.timeoutMs ?? 120000);
|
|
431
|
+
node("#assistant-allowed-peers").value = config.allowedPeers?.includes("*") ? "" : (config.allowedPeers ?? []).join("\n");
|
|
432
|
+
node("#assistant-allowed-agents").value = config.allowedAgents?.includes("*") ? "" : (config.allowedAgents ?? []).join("\n");
|
|
433
|
+
node("#assistant-config-error").hidden = true;
|
|
434
|
+
node("#assistant-config-dialog").showModal();
|
|
435
|
+
}
|
|
436
|
+
node("#assistant-config-form").onsubmit = event => {
|
|
437
|
+
event.preventDefault();
|
|
438
|
+
const save = node("#assistant-config-save");
|
|
439
|
+
save.disabled = true;
|
|
440
|
+
const source = node("#assistant-model-provider").value;
|
|
441
|
+
const provider = source.startsWith("plugin:") ? "plugin" : source;
|
|
442
|
+
const modelPlugin = source.startsWith("plugin:") ? source.slice("plugin:".length) : undefined;
|
|
443
|
+
const body = {};
|
|
444
|
+
if (provider !== (assistantOriginal.modelProvider ?? "openai"))
|
|
445
|
+
body.modelProvider = provider;
|
|
446
|
+
if (provider === "plugin" && modelPlugin !== assistantOriginal.modelPlugin)
|
|
447
|
+
body.modelPlugin = modelPlugin;
|
|
448
|
+
const model = node("#assistant-model").value.trim();
|
|
449
|
+
const endpoint = node("#assistant-endpoint").value.trim();
|
|
450
|
+
const apiKey = node("#assistant-api-key").value;
|
|
451
|
+
if (model && model !== String(assistantOriginal.model ?? ""))
|
|
452
|
+
body.model = model;
|
|
453
|
+
if (endpoint && endpoint !== String(assistantOriginal.endpoint ?? ""))
|
|
454
|
+
body.endpoint = endpoint;
|
|
455
|
+
if (apiKey.trim())
|
|
456
|
+
body.apiKey = apiKey.trim();
|
|
457
|
+
const timeoutMs = Number(node("#assistant-timeout").value);
|
|
458
|
+
if (timeoutMs !== Number(assistantOriginal.timeoutMs ?? 120000))
|
|
459
|
+
body.timeoutMs = timeoutMs;
|
|
460
|
+
for (const [field, selector] of [["allowedPeers", "#assistant-allowed-peers"], ["allowedAgents", "#assistant-allowed-agents"]]) {
|
|
461
|
+
const entries = [...new Set(node(selector).value.split(/[\s,,]+/).filter(Boolean))];
|
|
462
|
+
const values = entries.includes("*") ? [] : entries;
|
|
463
|
+
if (JSON.stringify(values) !== JSON.stringify(assistantOriginal[field]))
|
|
464
|
+
body[field] = values;
|
|
465
|
+
}
|
|
466
|
+
void request("/api/plugins/personal-assistant/config", "PATCH", body).then(result => {
|
|
467
|
+
node("#assistant-config-dialog").close();
|
|
468
|
+
showResult("个人助理配置已保存", result.restartRequired || result.action === "restart" ? "配置已保存,需要重启后台后生效。" : "模型来源配置已更新。", false);
|
|
469
|
+
return load(false);
|
|
470
|
+
}).catch(cause => { const detail = node("#assistant-config-error"); detail.textContent = String(cause); detail.hidden = false; }).finally(() => { save.disabled = false; });
|
|
471
|
+
};
|
|
404
472
|
function renderMessages(messages) {
|
|
405
473
|
messagesNode.replaceChildren();
|
|
406
474
|
messagesEmpty.hidden = messages.length > 0;
|
|
@@ -453,8 +521,8 @@
|
|
|
453
521
|
node("#check-versions").onclick = () => void request("/api/versions/check", "POST", {}).then(() => load()).catch((cause) => error(String(cause)));
|
|
454
522
|
node("#save-update-policy").onclick = () => void request("/api/update-policy", "PATCH", { mode: policyNode.value }).then(() => load()).catch((cause) => error(String(cause)));
|
|
455
523
|
node("#save-plugin-trust").onclick = () => { const button = node("#save-plugin-trust"); button.disabled = true; trustStatusNode.textContent = "正在保存…"; void request("/api/plugin-trust", "PATCH", pluginTrustConfig).then(() => { pluginTrustDirty = false; trustStatusNode.textContent = "信任设置已保存"; return load(false); }).catch((cause) => { trustStatusNode.textContent = String(cause); }).finally(() => { button.disabled = false; }); };
|
|
456
|
-
node("#update-core").onclick = () => { if (confirm("
|
|
457
|
-
void request("/api/update", "POST", {}).then(() => { error("
|
|
524
|
+
node("#update-core").onclick = () => { if (confirm("确认检查后台更新?有更高版本时将升级并自动重启"))
|
|
525
|
+
void request("/api/update", "POST", {}).then(() => { error("后台正在检查更新,有更高版本时升级并重启;结果请查看运行日志"); }).catch((cause) => error(String(cause))); };
|
|
458
526
|
node("#pair-button").onclick = () => {
|
|
459
527
|
const code = pairCodeNode.value.trim();
|
|
460
528
|
if (!/^\d{6}$/.test(code))
|
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
let logHasNext = false;
|
|
33
33
|
let loading = false;
|
|
34
34
|
const installingPackages = new Set<string>();
|
|
35
|
+
let assistantOriginal: Record<string, unknown> = {};
|
|
35
36
|
|
|
36
37
|
function error(message: string): void { errorNode.textContent = message; errorNode.hidden = !message; }
|
|
37
38
|
function showResult(title: string, message: string, failed: boolean): void {
|
|
@@ -51,7 +52,7 @@
|
|
|
51
52
|
});
|
|
52
53
|
async function loadCatalog(): Promise<void> {
|
|
53
54
|
const result = await request(`/api/plugins/catalog?q=${encodeURIComponent(packageSearchNode.value.trim())}`);
|
|
54
|
-
const items: Array<{ name: string; version: string; description: string; type: string }> = result.plugins ?? [];
|
|
55
|
+
const items: Array<{ id?: string; name: string; version: string; description: string; type: string }> = result.plugins ?? [];
|
|
55
56
|
catalogNode.replaceChildren();
|
|
56
57
|
if (!items.length) { catalogNode.textContent = "未找到已声明类型的 npm 插件"; return; }
|
|
57
58
|
const typeNames: Record<string, string> = { agent: "Agent", channel: "通道", management: "管理" };
|
|
@@ -62,7 +63,7 @@
|
|
|
62
63
|
head.append(headRow);
|
|
63
64
|
const body = document.createElement("tbody");
|
|
64
65
|
for (const plugin of items) {
|
|
65
|
-
const installed = plugins.find((item) => item.id === plugin.name.replace(/^agent-bridge-/, ""));
|
|
66
|
+
const installed = plugins.find((item) => item.id === (plugin.id ?? plugin.name.replace(/^agent-bridge-/, "")));
|
|
66
67
|
const versionStatus = installed ? pluginVersions[installed.id] : undefined;
|
|
67
68
|
const isUpgrade = Boolean(installed && versionStatus?.packageName === plugin.name && newerVersion(plugin.version, installed.version));
|
|
68
69
|
const row = document.createElement("tr");
|
|
@@ -149,7 +150,8 @@
|
|
|
149
150
|
const entries = [data.core, ...(data.plugins ?? []).map((plugin: any) => plugin.versionStatus)].filter(Boolean);
|
|
150
151
|
versionsNode.replaceChildren(...entries.map((entry: any) => {
|
|
151
152
|
const row = document.createElement("div"); row.className = "version-row";
|
|
152
|
-
|
|
153
|
+
const state = entry.state === "unpublished" ? "未发布" : entry.state;
|
|
154
|
+
row.textContent = `${entry.packageName} · 当前 ${entry.currentVersion} · ${entry.latestVersion ? `最新 ${entry.latestVersion}` : "最新版本未知"} · ${state}${entry.error ? ` · ${entry.error}` : ""}`;
|
|
153
155
|
return row;
|
|
154
156
|
}));
|
|
155
157
|
if (!entries.length) versionsNode.textContent = "尚未检查版本";
|
|
@@ -194,7 +196,7 @@
|
|
|
194
196
|
const version = document.createElement("td"); version.textContent = `v${plugin.version}`;
|
|
195
197
|
const state = document.createElement("td");
|
|
196
198
|
const status = document.createElement("span"); status.className = `status status-${plugin.status}`; status.textContent = plugin.status;
|
|
197
|
-
if (plugin.type === "agent") type.textContent +=
|
|
199
|
+
if (plugin.type === "agent") type.textContent += plugin.manifest?.executionMode === "cli" ? " · CLI" : " · 桌面";
|
|
198
200
|
const dependencies = Object.entries(plugin.manifest?.dependencies ?? {});
|
|
199
201
|
if (dependencies.length) { const detail = document.createElement("small"); detail.className = "muted plugin-id"; detail.textContent = `依赖:${dependencies.map(([id, range]) => `${id} ${range}`).join(";")}`; name.append(detail); }
|
|
200
202
|
state.append(status);
|
|
@@ -207,6 +209,7 @@
|
|
|
207
209
|
}
|
|
208
210
|
const controls = document.createElement("td"); controls.className = "controls";
|
|
209
211
|
if (plugin.manifest?.executionMode === "cli") controls.append(button("配置", `配置 ${plugin.id}`, () => configureCli(plugin)));
|
|
212
|
+
if (plugin.id === "personal-assistant") controls.append(button("配置模型", "配置个人助理模型来源", () => configureAssistant(plugin)));
|
|
210
213
|
if (pluginVersions[plugin.id]?.state === "update-available" && plugin.path.includes("node_modules")) { controlsUpgrade(plugin.id); controls.append(button("下载", `下载 ${plugin.id}`, async () => { await request(`/api/plugins/${encodeURIComponent(plugin.id)}/download`, "POST", { packageName: plugin.packageName, version: pluginVersions[plugin.id]?.latestVersion }); await load(); })); }
|
|
211
214
|
for (const [label, action] of [["启动", "start"], ["停止", "stop"], ["重载", "reload"]]) {
|
|
212
215
|
controls.append(button(label, `${label} ${plugin.id}`, async () => { await request(`/api/plugins/${encodeURIComponent(plugin.id)}/${action}`, "POST"); await load(); }));
|
|
@@ -246,6 +249,55 @@
|
|
|
246
249
|
const detail = node<HTMLElement>("#cli-config-error"); detail.textContent = String(cause); detail.hidden = false;
|
|
247
250
|
}).finally(() => { save.disabled = false; });
|
|
248
251
|
};
|
|
252
|
+
node<HTMLButtonElement>("#assistant-config-cancel").onclick = () => node<HTMLDialogElement>("#assistant-config-dialog").close();
|
|
253
|
+
async function configureAssistant(plugin: Plugin): Promise<void> {
|
|
254
|
+
const [config, sourceResult] = await Promise.all([
|
|
255
|
+
request(`/api/plugins/${encodeURIComponent(plugin.id)}/config`),
|
|
256
|
+
request("/api/model-providers"),
|
|
257
|
+
]);
|
|
258
|
+
assistantOriginal = config;
|
|
259
|
+
const select = node<HTMLSelectElement>("#assistant-model-provider");
|
|
260
|
+
select.replaceChildren();
|
|
261
|
+
const add = (value: string, label: string, disabled = false) => { const option = document.createElement("option"); option.value = value; option.textContent = label; option.disabled = disabled; select.append(option); };
|
|
262
|
+
add("openai", "OpenAI 兼容接口");
|
|
263
|
+
if (config.modelProvider === "claude-cli") add("claude-cli", "Claude CLI(原有配置,用户默认模型)");
|
|
264
|
+
for (const provider of sourceResult.providers ?? []) add(`plugin:${provider.id}`, `${provider.displayName || provider.id}${provider.available ? "" : `(不可用:${provider.reason || provider.status})`}`, !provider.available);
|
|
265
|
+
if (config.modelProvider === "plugin" && config.modelPlugin && !(sourceResult.providers ?? []).some((item: any) => item.id === config.modelPlugin)) add(`plugin:${config.modelPlugin}`, `${config.modelPlugin}(未安装或未声明模型来源)`, true);
|
|
266
|
+
select.value = config.modelProvider === "plugin" && config.modelPlugin ? `plugin:${config.modelPlugin}` : config.modelProvider ?? "openai";
|
|
267
|
+
node<HTMLInputElement>("#assistant-model").value = config.model ?? "";
|
|
268
|
+
node<HTMLInputElement>("#assistant-endpoint").value = config.endpoint ?? "";
|
|
269
|
+
node<HTMLInputElement>("#assistant-api-key").value = "";
|
|
270
|
+
node<HTMLInputElement>("#assistant-api-key").placeholder = config.apiKey ? "已配置,留空保留" : "可选,本地免认证服务可留空";
|
|
271
|
+
node<HTMLInputElement>("#assistant-timeout").value = String(config.timeoutMs ?? 120000);
|
|
272
|
+
node<HTMLTextAreaElement>("#assistant-allowed-peers").value = config.allowedPeers?.includes("*") ? "" : (config.allowedPeers ?? []).join("\n");
|
|
273
|
+
node<HTMLTextAreaElement>("#assistant-allowed-agents").value = config.allowedAgents?.includes("*") ? "" : (config.allowedAgents ?? []).join("\n");
|
|
274
|
+
node<HTMLElement>("#assistant-config-error").hidden = true;
|
|
275
|
+
node<HTMLDialogElement>("#assistant-config-dialog").showModal();
|
|
276
|
+
}
|
|
277
|
+
node<HTMLFormElement>("#assistant-config-form").onsubmit = event => {
|
|
278
|
+
event.preventDefault();
|
|
279
|
+
const save = node<HTMLButtonElement>("#assistant-config-save"); save.disabled = true;
|
|
280
|
+
const source = node<HTMLSelectElement>("#assistant-model-provider").value;
|
|
281
|
+
const provider = source.startsWith("plugin:") ? "plugin" : source;
|
|
282
|
+
const modelPlugin = source.startsWith("plugin:") ? source.slice("plugin:".length) : undefined;
|
|
283
|
+
const body: Record<string, unknown> = {};
|
|
284
|
+
if (provider !== (assistantOriginal.modelProvider ?? "openai")) body.modelProvider = provider;
|
|
285
|
+
if (provider === "plugin" && modelPlugin !== assistantOriginal.modelPlugin) body.modelPlugin = modelPlugin;
|
|
286
|
+
const model = node<HTMLInputElement>("#assistant-model").value.trim(); const endpoint = node<HTMLInputElement>("#assistant-endpoint").value.trim(); const apiKey = node<HTMLInputElement>("#assistant-api-key").value;
|
|
287
|
+
if (model && model !== String(assistantOriginal.model ?? "")) body.model = model;
|
|
288
|
+
if (endpoint && endpoint !== String(assistantOriginal.endpoint ?? "")) body.endpoint = endpoint;
|
|
289
|
+
if (apiKey.trim()) body.apiKey = apiKey.trim();
|
|
290
|
+
const timeoutMs = Number(node<HTMLInputElement>("#assistant-timeout").value);
|
|
291
|
+
if (timeoutMs !== Number(assistantOriginal.timeoutMs ?? 120000)) body.timeoutMs = timeoutMs;
|
|
292
|
+
for (const [field, selector] of [["allowedPeers", "#assistant-allowed-peers"], ["allowedAgents", "#assistant-allowed-agents"]]) {
|
|
293
|
+
const entries = [...new Set(node<HTMLTextAreaElement>(selector).value.split(/[\s,,]+/).filter(Boolean))];
|
|
294
|
+
const values = entries.includes("*") ? [] : entries;
|
|
295
|
+
if (JSON.stringify(values) !== JSON.stringify(assistantOriginal[field])) body[field] = values;
|
|
296
|
+
}
|
|
297
|
+
void request("/api/plugins/personal-assistant/config", "PATCH", body).then(result => {
|
|
298
|
+
node<HTMLDialogElement>("#assistant-config-dialog").close(); showResult("个人助理配置已保存", result.restartRequired || result.action === "restart" ? "配置已保存,需要重启后台后生效。" : "模型来源配置已更新。", false); return load(false);
|
|
299
|
+
}).catch(cause => { const detail = node<HTMLElement>("#assistant-config-error"); detail.textContent = String(cause); detail.hidden = false; }).finally(() => { save.disabled = false; });
|
|
300
|
+
};
|
|
249
301
|
function renderMessages(messages: Array<{ time: string; pluginId: string; direction: "request" | "reply" | "error"; text: string; peer?: string; agentAid?: string }>): void {
|
|
250
302
|
messagesNode.replaceChildren();
|
|
251
303
|
messagesEmpty.hidden = messages.length > 0;
|
|
@@ -280,7 +332,7 @@
|
|
|
280
332
|
node<HTMLButtonElement>("#check-versions").onclick = () => void request("/api/versions/check", "POST", {}).then(() => load()).catch((cause) => error(String(cause)));
|
|
281
333
|
node<HTMLButtonElement>("#save-update-policy").onclick = () => void request("/api/update-policy", "PATCH", { mode: policyNode.value }).then(() => load()).catch((cause) => error(String(cause)));
|
|
282
334
|
node<HTMLButtonElement>("#save-plugin-trust").onclick = () => { const button = node<HTMLButtonElement>("#save-plugin-trust"); button.disabled = true; trustStatusNode.textContent = "正在保存…"; void request("/api/plugin-trust", "PATCH", pluginTrustConfig).then(() => { pluginTrustDirty = false; trustStatusNode.textContent = "信任设置已保存"; return load(false); }).catch((cause) => { trustStatusNode.textContent = String(cause); }).finally(() => { button.disabled = false; }); };
|
|
283
|
-
node<HTMLButtonElement>("#update-core").onclick = () => { if (confirm("
|
|
335
|
+
node<HTMLButtonElement>("#update-core").onclick = () => { if (confirm("确认检查后台更新?有更高版本时将升级并自动重启")) void request("/api/update", "POST", {}).then(() => { error("后台正在检查更新,有更高版本时升级并重启;结果请查看运行日志"); }).catch((cause) => error(String(cause))); };
|
|
284
336
|
node<HTMLButtonElement>("#pair-button").onclick = () => {
|
|
285
337
|
const code = pairCodeNode.value.trim();
|
|
286
338
|
if (!/^\d{6}$/.test(code)) return error("请输入 6 位配置码");
|
|
@@ -31,6 +31,21 @@
|
|
|
31
31
|
<div><button id="cli-config-save" type="submit">保存配置</button><button id="cli-config-cancel" type="button">取消</button></div>
|
|
32
32
|
</form>
|
|
33
33
|
</dialog>
|
|
34
|
+
<dialog id="assistant-config-dialog" aria-labelledby="assistant-config-title">
|
|
35
|
+
<h2 id="assistant-config-title">个人助理模型配置</h2>
|
|
36
|
+
<form id="assistant-config-form">
|
|
37
|
+
<label for="assistant-model-provider">模型来源</label><select id="assistant-model-provider" required></select>
|
|
38
|
+
<label for="assistant-model">OpenAI 兼容模型名称</label><input id="assistant-model" placeholder="仅 HTTP 来源需要">
|
|
39
|
+
<label for="assistant-endpoint">OpenAI 兼容地址</label><input id="assistant-endpoint" type="url" placeholder="http://127.0.0.1:11434/v1/chat/completions">
|
|
40
|
+
<label for="assistant-api-key">API 密钥(留空保留已有密钥)</label><input id="assistant-api-key" type="password" autocomplete="new-password">
|
|
41
|
+
<label for="assistant-timeout">模型超时(毫秒)</label><input id="assistant-timeout" type="number" min="1000" max="120000" required>
|
|
42
|
+
<label for="assistant-allowed-peers">允许使用助理的 Peer AID(每行一个,留空接受所有)</label><textarea id="assistant-allowed-peers" rows="3" placeholder="留空接受所有 Peer"></textarea>
|
|
43
|
+
<label for="assistant-allowed-agents">允许调用的 Agent 插件 ID(每行一个,留空允许所有)</label><textarea id="assistant-allowed-agents" rows="3" placeholder="留空允许所有已就绪 Agent"></textarea>
|
|
44
|
+
<p class="muted">默认留空,接受所有 Peer 并允许调用所有已就绪的普通 Agent;填写具体名单后才限制。插件来源沿用客户端登录及当前模型,AID 关联请在 Channel 设置中完成。</p>
|
|
45
|
+
<p id="assistant-config-error" class="error" role="alert" hidden></p>
|
|
46
|
+
<div><button id="assistant-config-save" type="submit">保存配置</button><button id="assistant-config-cancel" type="button">取消</button></div>
|
|
47
|
+
</form>
|
|
48
|
+
</dialog>
|
|
34
49
|
<dialog id="operation-result" aria-labelledby="operation-result-title" aria-describedby="operation-result-message">
|
|
35
50
|
<h2 id="operation-result-title">操作结果</h2><p id="operation-result-message"></p><button id="operation-result-close" type="button" autofocus>确定</button>
|
|
36
51
|
</dialog>
|
|
@@ -63,3 +63,7 @@ th:nth-child(1) { width: 145px; } th:nth-child(2) { width: 100px; } th:nth-child
|
|
|
63
63
|
|
|
64
64
|
#cli-config-form { display: grid; min-width: min(70vw, 480px); }
|
|
65
65
|
#cli-config-form input { width: 100%; }
|
|
66
|
+
#assistant-config-form { display: grid; min-width: min(70vw, 480px); }
|
|
67
|
+
#assistant-config-form input, #assistant-config-form select, #assistant-config-form textarea { width: 100%; }
|
|
68
|
+
#assistant-config-dialog { max-height: 90vh; overflow: auto; }
|
|
69
|
+
#assistant-config-form textarea { font: inherit; padding: 6px 9px; border: 1px solid #bfc9c5; border-radius: 5px; }
|
package/package.json
CHANGED