agent-bridge-abweb 1.0.15 → 1.0.16
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 +39 -2
- package/dist/src-ts/plugins/abweb/public/app.ts +31 -2
- package/dist/src-ts/plugins/abweb/public/index.html +10 -2
- package/dist/src-ts/plugins/abweb/public/style.css +10 -0
- package/package.json +1 -1
- package/plugin.json +1 -1
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
const logsNode = node("#logs");
|
|
7
7
|
const logsEmpty = node("#logs-empty");
|
|
8
8
|
const logsPageNode = node("#logs-page");
|
|
9
|
+
const logsLevelNode = node("#logs-level");
|
|
10
|
+
const logsQueryNode = node("#logs-query");
|
|
9
11
|
const errorNode = node("#error");
|
|
10
12
|
const pairCodeNode = node("#pair-code");
|
|
11
13
|
const packageSearchNode = node("#package-search");
|
|
@@ -430,6 +432,11 @@
|
|
|
430
432
|
node("#assistant-timeout").value = String(config.timeoutMs ?? 120000);
|
|
431
433
|
node("#assistant-allowed-peers").value = config.allowedPeers?.includes("*") ? "" : (config.allowedPeers ?? []).join("\n");
|
|
432
434
|
node("#assistant-allowed-agents").value = config.allowedAgents?.includes("*") ? "" : (config.allowedAgents ?? []).join("\n");
|
|
435
|
+
node("#assistant-skills-enabled").checked = config.skills?.enabled ?? true;
|
|
436
|
+
node("#assistant-skills-learn-mode").value = config.skills?.learnMode ?? "draft";
|
|
437
|
+
node("#assistant-skills-read-dirs").value = (config.skills?.readDirs ?? []).join("\n");
|
|
438
|
+
node("#assistant-skills-share-dir").value = config.skills?.shareDir ?? "";
|
|
439
|
+
node("#assistant-memory-mode").value = config.memory?.mode ?? "stated";
|
|
433
440
|
node("#assistant-config-error").hidden = true;
|
|
434
441
|
node("#assistant-config-dialog").showModal();
|
|
435
442
|
}
|
|
@@ -463,9 +470,30 @@
|
|
|
463
470
|
if (JSON.stringify(values) !== JSON.stringify(assistantOriginal[field]))
|
|
464
471
|
body[field] = values;
|
|
465
472
|
}
|
|
473
|
+
const previousSkills = assistantOriginal.skills;
|
|
474
|
+
const shareDir = node("#assistant-skills-share-dir").value.trim();
|
|
475
|
+
const shareDirChanged = shareDir !== (previousSkills?.shareDir ?? "");
|
|
476
|
+
const skills = {
|
|
477
|
+
...previousSkills,
|
|
478
|
+
enabled: node("#assistant-skills-enabled").checked,
|
|
479
|
+
learnMode: node("#assistant-skills-learn-mode").value,
|
|
480
|
+
readDirs: node("#assistant-skills-read-dirs").value.split(/\r?\n/).map(value => value.trim()).filter(Boolean),
|
|
481
|
+
...(shareDirChanged ? { shareDir } : {}),
|
|
482
|
+
};
|
|
483
|
+
if (skills.enabled !== (previousSkills?.enabled ?? true) || skills.learnMode !== (previousSkills?.learnMode ?? "draft") || JSON.stringify(skills.readDirs) !== JSON.stringify(previousSkills?.readDirs ?? []) || shareDirChanged)
|
|
484
|
+
body.skills = skills;
|
|
485
|
+
const previousMemory = assistantOriginal.memory;
|
|
486
|
+
const mode = node("#assistant-memory-mode").value;
|
|
487
|
+
if (mode !== (previousMemory?.mode ?? "stated"))
|
|
488
|
+
body.memory = { ...previousMemory, mode };
|
|
489
|
+
if (!Object.keys(body).length) {
|
|
490
|
+
node("#assistant-config-dialog").close();
|
|
491
|
+
save.disabled = false;
|
|
492
|
+
return;
|
|
493
|
+
}
|
|
466
494
|
void request("/api/plugins/personal-assistant/config", "PATCH", body).then(result => {
|
|
467
495
|
node("#assistant-config-dialog").close();
|
|
468
|
-
showResult("个人助理配置已保存", result.restartRequired || result.action === "restart" ? "配置已保存,需要重启后台后生效。" : "
|
|
496
|
+
showResult("个人助理配置已保存", result.restartRequired || result.action === "restart" ? "配置已保存,需要重启后台后生效。" : "个人助理配置已更新。", false);
|
|
469
497
|
return load(false);
|
|
470
498
|
}).catch(cause => { const detail = node("#assistant-config-error"); detail.textContent = String(cause); detail.hidden = false; }).finally(() => { save.disabled = false; });
|
|
471
499
|
};
|
|
@@ -500,7 +528,12 @@
|
|
|
500
528
|
logsNode.append(row);
|
|
501
529
|
}
|
|
502
530
|
}
|
|
503
|
-
async function loadLogs(page = logPage) {
|
|
531
|
+
async function loadLogs(page = logPage) {
|
|
532
|
+
const params = new URLSearchParams({ page: String(page), pageSize: "100", level: logsLevelNode.value });
|
|
533
|
+
if (logsQueryNode.value.trim())
|
|
534
|
+
params.set("q", logsQueryNode.value.trim());
|
|
535
|
+
renderLogs(await request(`/api/logs?${params}`));
|
|
536
|
+
}
|
|
504
537
|
for (const [tabId, panelId] of [["settings-tab", "settings-panel"], ["messages-tab", "messages-panel"], ["logs-tab", "logs-panel"]]) {
|
|
505
538
|
node(`#${tabId}`).onclick = () => {
|
|
506
539
|
for (const [otherTab, otherPanel] of [["settings-tab", "settings-panel"], ["messages-tab", "messages-panel"], ["logs-tab", "logs-panel"]]) {
|
|
@@ -513,6 +546,10 @@
|
|
|
513
546
|
};
|
|
514
547
|
}
|
|
515
548
|
node("#logs-refresh").onclick = () => void loadLogs().catch((cause) => error(String(cause)));
|
|
549
|
+
node("#logs-search").onclick = () => void loadLogs(1).catch((cause) => error(String(cause)));
|
|
550
|
+
logsLevelNode.onchange = () => void loadLogs(1).catch((cause) => error(String(cause)));
|
|
551
|
+
logsQueryNode.onkeydown = event => { if (event.key === "Enter")
|
|
552
|
+
void loadLogs(1).catch((cause) => error(String(cause))); };
|
|
516
553
|
node("#logs-prev").onclick = () => { if (logPage > 1)
|
|
517
554
|
void loadLogs(logPage - 1).catch((cause) => error(String(cause))); };
|
|
518
555
|
node("#logs-next").onclick = () => { if (logHasNext)
|
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
const logsNode = node<HTMLTableSectionElement>("#logs");
|
|
8
8
|
const logsEmpty = node<HTMLParagraphElement>("#logs-empty");
|
|
9
9
|
const logsPageNode = node<HTMLSpanElement>("#logs-page");
|
|
10
|
+
const logsLevelNode = node<HTMLSelectElement>("#logs-level");
|
|
11
|
+
const logsQueryNode = node<HTMLInputElement>("#logs-query");
|
|
10
12
|
const errorNode = node<HTMLParagraphElement>("#error");
|
|
11
13
|
const pairCodeNode = node<HTMLInputElement>("#pair-code");
|
|
12
14
|
const packageSearchNode = node<HTMLInputElement>("#package-search");
|
|
@@ -271,6 +273,11 @@
|
|
|
271
273
|
node<HTMLInputElement>("#assistant-timeout").value = String(config.timeoutMs ?? 120000);
|
|
272
274
|
node<HTMLTextAreaElement>("#assistant-allowed-peers").value = config.allowedPeers?.includes("*") ? "" : (config.allowedPeers ?? []).join("\n");
|
|
273
275
|
node<HTMLTextAreaElement>("#assistant-allowed-agents").value = config.allowedAgents?.includes("*") ? "" : (config.allowedAgents ?? []).join("\n");
|
|
276
|
+
node<HTMLInputElement>("#assistant-skills-enabled").checked = config.skills?.enabled ?? true;
|
|
277
|
+
node<HTMLSelectElement>("#assistant-skills-learn-mode").value = config.skills?.learnMode ?? "draft";
|
|
278
|
+
node<HTMLTextAreaElement>("#assistant-skills-read-dirs").value = (config.skills?.readDirs ?? []).join("\n");
|
|
279
|
+
node<HTMLInputElement>("#assistant-skills-share-dir").value = config.skills?.shareDir ?? "";
|
|
280
|
+
node<HTMLSelectElement>("#assistant-memory-mode").value = config.memory?.mode ?? "stated";
|
|
274
281
|
node<HTMLElement>("#assistant-config-error").hidden = true;
|
|
275
282
|
node<HTMLDialogElement>("#assistant-config-dialog").showModal();
|
|
276
283
|
}
|
|
@@ -294,8 +301,23 @@
|
|
|
294
301
|
const values = entries.includes("*") ? [] : entries;
|
|
295
302
|
if (JSON.stringify(values) !== JSON.stringify(assistantOriginal[field])) body[field] = values;
|
|
296
303
|
}
|
|
304
|
+
const previousSkills = assistantOriginal.skills as { enabled?: boolean; learnMode?: string; readDirs?: string[]; shareDir?: string } | undefined;
|
|
305
|
+
const shareDir = node<HTMLInputElement>("#assistant-skills-share-dir").value.trim();
|
|
306
|
+
const shareDirChanged = shareDir !== (previousSkills?.shareDir ?? "");
|
|
307
|
+
const skills = {
|
|
308
|
+
...previousSkills,
|
|
309
|
+
enabled: node<HTMLInputElement>("#assistant-skills-enabled").checked,
|
|
310
|
+
learnMode: node<HTMLSelectElement>("#assistant-skills-learn-mode").value,
|
|
311
|
+
readDirs: node<HTMLTextAreaElement>("#assistant-skills-read-dirs").value.split(/\r?\n/).map(value => value.trim()).filter(Boolean),
|
|
312
|
+
...(shareDirChanged ? { shareDir } : {}),
|
|
313
|
+
};
|
|
314
|
+
if (skills.enabled !== (previousSkills?.enabled ?? true) || skills.learnMode !== (previousSkills?.learnMode ?? "draft") || JSON.stringify(skills.readDirs) !== JSON.stringify(previousSkills?.readDirs ?? []) || shareDirChanged) body.skills = skills;
|
|
315
|
+
const previousMemory = assistantOriginal.memory as { mode?: string } | undefined;
|
|
316
|
+
const mode = node<HTMLSelectElement>("#assistant-memory-mode").value;
|
|
317
|
+
if (mode !== (previousMemory?.mode ?? "stated")) body.memory = { ...previousMemory, mode };
|
|
318
|
+
if (!Object.keys(body).length) { node<HTMLDialogElement>("#assistant-config-dialog").close(); save.disabled = false; return; }
|
|
297
319
|
void request("/api/plugins/personal-assistant/config", "PATCH", body).then(result => {
|
|
298
|
-
node<HTMLDialogElement>("#assistant-config-dialog").close(); showResult("个人助理配置已保存", result.restartRequired || result.action === "restart" ? "配置已保存,需要重启后台后生效。" : "
|
|
320
|
+
node<HTMLDialogElement>("#assistant-config-dialog").close(); showResult("个人助理配置已保存", result.restartRequired || result.action === "restart" ? "配置已保存,需要重启后台后生效。" : "个人助理配置已更新。", false); return load(false);
|
|
299
321
|
}).catch(cause => { const detail = node<HTMLElement>("#assistant-config-error"); detail.textContent = String(cause); detail.hidden = false; }).finally(() => { save.disabled = false; });
|
|
300
322
|
};
|
|
301
323
|
function renderMessages(messages: Array<{ time: string; pluginId: string; direction: "request" | "reply" | "error"; text: string; peer?: string; agentAid?: string }>): void {
|
|
@@ -314,7 +336,11 @@
|
|
|
314
336
|
logsNode.replaceChildren(); logsEmpty.hidden = data.logs.length > 0; logPage = data.page; logHasNext = data.hasNext; logsPageNode.textContent = `第 ${data.page} 页`;
|
|
315
337
|
for (const log of data.logs) { const row = document.createElement("tr"); for (const value of [new Date(log.time).toLocaleString(), log.level, log.source, log.message, log.fields ? JSON.stringify(log.fields) : ""]) { const cell = document.createElement("td"); cell.textContent = value; row.append(cell); } logsNode.append(row); }
|
|
316
338
|
}
|
|
317
|
-
async function loadLogs(page = logPage): Promise<void> {
|
|
339
|
+
async function loadLogs(page = logPage): Promise<void> {
|
|
340
|
+
const params = new URLSearchParams({ page: String(page), pageSize: "100", level: logsLevelNode.value });
|
|
341
|
+
if (logsQueryNode.value.trim()) params.set("q", logsQueryNode.value.trim());
|
|
342
|
+
renderLogs(await request(`/api/logs?${params}`));
|
|
343
|
+
}
|
|
318
344
|
for (const [tabId, panelId] of [["settings-tab", "settings-panel"], ["messages-tab", "messages-panel"], ["logs-tab", "logs-panel"]]) {
|
|
319
345
|
node<HTMLButtonElement>(`#${tabId}`).onclick = () => {
|
|
320
346
|
for (const [otherTab, otherPanel] of [["settings-tab", "settings-panel"], ["messages-tab", "messages-panel"], ["logs-tab", "logs-panel"]]) {
|
|
@@ -326,6 +352,9 @@
|
|
|
326
352
|
};
|
|
327
353
|
}
|
|
328
354
|
node<HTMLButtonElement>("#logs-refresh").onclick = () => void loadLogs().catch((cause) => error(String(cause)));
|
|
355
|
+
node<HTMLButtonElement>("#logs-search").onclick = () => void loadLogs(1).catch((cause) => error(String(cause)));
|
|
356
|
+
logsLevelNode.onchange = () => void loadLogs(1).catch((cause) => error(String(cause)));
|
|
357
|
+
logsQueryNode.onkeydown = event => { if (event.key === "Enter") void loadLogs(1).catch((cause) => error(String(cause))); };
|
|
329
358
|
node<HTMLButtonElement>("#logs-prev").onclick = () => { if (logPage > 1) void loadLogs(logPage - 1).catch((cause) => error(String(cause))); };
|
|
330
359
|
node<HTMLButtonElement>("#logs-next").onclick = () => { if (logHasNext) void loadLogs(logPage + 1).catch((cause) => error(String(cause))); };
|
|
331
360
|
node<HTMLButtonElement>("#refresh").onclick = () => void load();
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
<section><div class="section-heading"><h2>Channel 设置</h2></div><nav id="channel-tabs" class="tabs channel-tabs" role="tablist" aria-label="Channel 设置"></nav><div id="channel-panel" role="tabpanel" aria-live="polite"></div></section>
|
|
19
19
|
</div>
|
|
20
20
|
<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><h2>通道会话</h2><div class="table-scroll"><table><thead><tr><th>来源</th><th>Agent</th><th>会话 ID</th><th>最近活跃时间</th></tr></thead><tbody id="sessions"></tbody></table></div></section></div>
|
|
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>
|
|
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="log-filters"><label for="logs-level">最低级别</label><select id="logs-level"><option value="info">Info 及以上</option><option value="warn">Warn 及以上</option><option value="error">仅 Error</option></select><label for="logs-query">内容</label><input id="logs-query" type="search" placeholder="搜索来源、消息或详细字段"><button id="logs-search" type="button">筛选</button></div><div class="table-scroll"><table class="logs-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
24
|
<dialog id="cli-config-dialog" aria-labelledby="cli-config-title">
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
</form>
|
|
33
33
|
</dialog>
|
|
34
34
|
<dialog id="assistant-config-dialog" aria-labelledby="assistant-config-title">
|
|
35
|
-
<h2 id="assistant-config-title"
|
|
35
|
+
<h2 id="assistant-config-title">个人助理配置</h2>
|
|
36
36
|
<form id="assistant-config-form">
|
|
37
37
|
<label for="assistant-model-provider">模型来源</label><select id="assistant-model-provider" required></select>
|
|
38
38
|
<label for="assistant-model">OpenAI 兼容模型名称</label><input id="assistant-model" placeholder="仅 HTTP 来源需要">
|
|
@@ -42,6 +42,14 @@
|
|
|
42
42
|
<label for="assistant-allowed-peers">允许使用助理的 Peer AID(每行一个,留空接受所有)</label><textarea id="assistant-allowed-peers" rows="3" placeholder="留空接受所有 Peer"></textarea>
|
|
43
43
|
<label for="assistant-allowed-agents">允许调用的 Agent 插件 ID(每行一个,留空允许所有)</label><textarea id="assistant-allowed-agents" rows="3" placeholder="留空允许所有已就绪 Agent"></textarea>
|
|
44
44
|
<p class="muted">默认留空,接受所有 Peer 并允许调用所有已就绪的普通 Agent;填写具体名单后才限制。插件来源沿用客户端登录及当前模型,AID 关联请在 Channel 设置中完成。</p>
|
|
45
|
+
<h3>技能与习惯</h3>
|
|
46
|
+
<label for="assistant-skills-enabled"><input id="assistant-skills-enabled" type="checkbox" checked style="width: auto; min-height: auto"> 启用技能</label>
|
|
47
|
+
<label for="assistant-skills-learn-mode">技能学习方式</label><select id="assistant-skills-learn-mode"><option value="draft">生成草稿,确认后采用</option><option value="auto">自动学习并采用</option><option value="off">关闭学习</option></select>
|
|
48
|
+
<label for="assistant-skills-read-dirs">只读技能目录(每行一个,最多 8 个)</label><textarea id="assistant-skills-read-dirs" rows="3" placeholder="C:\Users\用户名\skills"></textarea>
|
|
49
|
+
<p class="muted">填写后台主机上的规范绝对路径,由运行时校验;留空仅使用该用户已学习的个人技能。</p>
|
|
50
|
+
<label for="assistant-skills-share-dir">技能分享目录</label><input id="assistant-skills-share-dir" type="text" placeholder="/opt/shared-skills">
|
|
51
|
+
<p class="muted">仅导出用户明确分享的技能正文和引用;填写后台主机上已存在的规范绝对路径,留空关闭分享。</p>
|
|
52
|
+
<label for="assistant-memory-mode">习惯记录方式</label><select id="assistant-memory-mode"><option value="stated">仅记录明确表达的习惯</option><option value="off">关闭习惯记录</option></select>
|
|
45
53
|
<p id="assistant-config-error" class="error" role="alert" hidden></p>
|
|
46
54
|
<div><button id="assistant-config-save" type="submit">保存配置</button><button id="assistant-config-cancel" type="button">取消</button></div>
|
|
47
55
|
</form>
|
|
@@ -50,6 +50,16 @@ form { margin-top: 14px; flex-wrap: wrap; }
|
|
|
50
50
|
.status-failed { color: #a83427; background: #fce9e5; }
|
|
51
51
|
.error, .error-text { color: #a83427; }
|
|
52
52
|
.table-scroll { overflow-x: auto; margin-top: 13px; }
|
|
53
|
+
.log-filters { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-top: 12px; }
|
|
54
|
+
.log-filters input { min-width: min(360px, 100%); flex: 1; }
|
|
55
|
+
.shell:has(#logs-panel:not([hidden])) { max-width: none; }
|
|
56
|
+
.logs-table { min-width: 900px; font-size: 12px; }
|
|
57
|
+
.logs-table th, .logs-table td { padding: 5px 7px; }
|
|
58
|
+
.logs-table th:nth-child(1) { width: 130px; }
|
|
59
|
+
.logs-table th:nth-child(2) { width: 65px; }
|
|
60
|
+
.logs-table th:nth-child(3) { width: 150px; }
|
|
61
|
+
.logs-table th:nth-child(4) { width: auto; }
|
|
62
|
+
.logs-table th:nth-child(5) { width: 260px; }
|
|
53
63
|
table { width: 100%; border-collapse: collapse; table-layout: fixed; min-width: 900px; font-size: 13px; }
|
|
54
64
|
th, td { padding: 10px 9px; text-align: left; vertical-align: top; border-bottom: 1px solid #dce3df; overflow-wrap: anywhere; white-space: pre-wrap; }
|
|
55
65
|
th { color: #566761; font-weight: 600; background: #edf1ef; }
|
package/package.json
CHANGED