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.
@@ -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.15",
8
+ version: "1.0.16",
9
9
  displayName: "Agent Bridge Web",
10
10
  entry: "dist/index.js",
11
11
  apiVersion: "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" ? "配置已保存,需要重启后台后生效。" : "模型来源配置已更新。", false);
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) { renderLogs(await request(`/api/logs?page=${page}&pageSize=100`)); }
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" ? "配置已保存,需要重启后台后生效。" : "模型来源配置已更新。", false); return load(false);
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> { renderLogs(await request(`/api/logs?page=${page}&pageSize=100`)); }
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">个人助理模型配置</h2>
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:&#92;Users&#92;用户名&#92;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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-bridge-abweb",
3
- "version": "1.0.15",
3
+ "version": "1.0.16",
4
4
  "description": "agent-bridge 的网页管理插件。",
5
5
  "keywords": [
6
6
  "mu-agent-bridge-plugin",
package/plugin.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "id": "abweb",
3
- "version": "1.0.15",
3
+ "version": "1.0.16",
4
4
  "type": "management",
5
5
  "displayName": "Agent Bridge Web",
6
6
  "entry": "dist/src-ts/plugins/abweb/index.js",