@tyr1105/dsh-buddy 0.1.0 → 0.1.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/lib/client.js DELETED
@@ -1,2561 +0,0 @@
1
- window.__ModuleLoader__.load({ id: '@tyr1105/dsh-buddy', factory: function (require) { var module = { exports: {} }; var exports = module.exports;
2
- "use strict";
3
- var __defProp = Object.defineProperty;
4
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
- var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __hasOwnProp = Object.prototype.hasOwnProperty;
7
- var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
8
- var __export = (target, all) => {
9
- for (var name in all)
10
- __defProp(target, name, { get: all[name], enumerable: true });
11
- };
12
- var __copyProps = (to, from, except, desc) => {
13
- if (from && typeof from === "object" || typeof from === "function") {
14
- for (let key of __getOwnPropNames(from))
15
- if (!__hasOwnProp.call(to, key) && key !== except)
16
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
- }
18
- return to;
19
- };
20
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
21
- var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
22
-
23
- // src/client/index.tsx
24
- var index_exports = {};
25
- __export(index_exports, {
26
- HubEntryIcon: () => HubEntryIcon,
27
- apply: () => apply,
28
- inject: () => inject
29
- });
30
- module.exports = __toCommonJS(index_exports);
31
-
32
- // src/client/styles.css
33
- var styles_default = "/*\n * \u961F\u53CB\u9875\u9762\u6837\u5F0F\u3002\u7EA6\u675F\uFF08docs/prd/sections/16-ui.md 16.0 \u4E0E\u4EFB\u52A1\u5361\uFF09\uFF1A\n * - \u989C\u8272\u53EA\u7528 var(--dsw-alias-*) \u4E0E --dsw-radius-* / --dsw-shadow-* \u7B49\u4E3B\u9898 token\uFF0C\n * \u4E0D\u51FA\u73B0\u5341\u516D\u8FDB\u5236\u3001rgb\u3001hsl\uFF08transparent / currentColor \u9664\u5916\uFF09\uFF0C\u4EAE\u6697\u81EA\u52A8\u8DDF\u968F\u3002\n * - \u4E2D\u680F 720\u20131600px \u53EF\u7528\uFF1B\u7A84\u4E8E 900px \u65F6\u53F3\u680F\u6539\u4E3A\u62BD\u5C49\u3002\n * - macOS \u6807\u9898\u533A\u53EA\u5728 [data-platform=darwin] \u4E0B\u7559\u51FA\u5218\u6D77\u7A7A\u95F4\u3002\n */\n\n/*\n * \u4E3B\u9898\u53D8\u91CF\u522B\u540D\u5C42\uFF08\u89C6\u89C9\u4FEE\u590D\uFF09\uFF1A\u672C\u6837\u5F0F\u8868\u5386\u53F2\u4E0A\u4F7F\u7528\u7684\u5206\u9694\u7EBF/\u5E95\u8272/\u5F3A\u8C03\u8272\u7B49\u53D8\u91CF\u540D\n * \u4E0E\u5BBF\u4E3B\u5B9E\u9645\u63D0\u4F9B\u7684\u4E3B\u9898 token \u540D\u4E0D\u4E00\u81F4\uFF08\u5BBF\u4E3B\u53EA\u6709 border-l1/l2\u3001bg-layer-1/2\u3001\n * brand-primary\u3001label-primary/secondary\u3001state-* \u7B49\uFF09\u3002\u5BF9\u4E0D\u4E0A\u7684\u53D8\u91CF\u5728\u8BA1\u7B97\u65F6\n * \u6574\u6761\u58F0\u660E\u5931\u6548\uFF0C\u4E8E\u662F\u51FA\u73B0\u300C\u6309\u94AE\u6CA1\u6709\u8FB9\u6846\u3001\u6CA1\u6709\u80CC\u666F\u3001hover \u6CA1\u6709\u53CD\u9988\u300D\u3002\n * \u8FD9\u91CC\u5728 .ah-page \u4E0A\u628A\u65E7\u540D\u4E00\u4E00\u6620\u5C04\u5230\u771F\u5B9E token\uFF08\u627E\u4E0D\u5230\u7684\u4E2D\u95F4\u8C03\u7528 color-mix\n * \u4ECE\u54C1\u724C\u8272\u6D3E\u751F\uFF0C\u4E0D\u5199\u6B7B\u4EFB\u4F55\u5341\u516D\u8FDB\u5236\uFF09\uFF0C\u4E0B\u65B9\u6837\u5F0F\u65E0\u9700\u9010\u884C\u4FEE\u6539\u3002\n * .ah-toast-layer \u6302\u5728 shell.overlay \u63D2\u69FD\u3001\u4E0D\u5728\u9875\u9762\u5B50\u6811\u91CC\uFF0C\u6240\u4EE5\u4E00\u5E76\u58F0\u660E\u3002\n */\n.ah-page,\n.ah-toast-layer {\n /* \u5206\u9694\u7EBF\uFF1A\u65E7 separator / separator-strong \u2192 \u5BBF\u4E3B border-l1 / border-l2 */\n --dsw-alias-separator: var(--dsw-alias-border-l1, var(--dsw-alias-border-l2));\n --dsw-alias-separator-strong: var(--dsw-alias-border-l2, var(--dsw-alias-border-l1));\n /* \u5E95\u8272\uFF1A\u65E7 inset / elevated \u2192 \u5BBF\u4E3B bg-layer-1 / bg-layer-2 */\n --dsw-alias-bg-inset: var(--dsw-alias-bg-layer-1, var(--dsw-alias-bg-base));\n --dsw-alias-bg-elevated: var(--dsw-alias-bg-layer-2, var(--dsw-alias-bg-layer-1));\n /* \u5F3A\u8C03\u8272\uFF1A\u65E7 accent \u2192 \u5BBF\u4E3B brand-primary\uFF1B\u6DE1\u5F3A\u8C03\u5E95\u7528\u5B83\u6D3E\u751F */\n --dsw-alias-accent: var(--dsw-alias-brand-primary);\n --dsw-alias-accent-muted: color-mix(in srgb, var(--dsw-alias-brand-primary) 14%, transparent);\n /* \u6587\u5B57\uFF1A\u65E7 text-primary/secondary \u2192 \u5BBF\u4E3B label-primary/secondary */\n --dsw-alias-text-primary: var(--dsw-alias-label-primary);\n --dsw-alias-text-secondary: var(--dsw-alias-label-secondary);\n --dsw-alias-text-danger: var(--dsw-alias-state-error-primary);\n /* \u72B6\u6001\u8272 */\n --dsw-alias-danger: var(--dsw-alias-state-error-primary);\n --dsw-alias-success: var(--dsw-alias-state-success-primary);\n --dsw-alias-warning: var(--dsw-alias-state-warn-primary);\n --dsw-alias-warning-muted: color-mix(in srgb, var(--dsw-alias-state-warn-primary) 14%, transparent);\n /* \u60AC\u505C / \u9009\u4E2D\u5E95\u8272\uFF1A\u5BBF\u4E3B\u672A\u63D0\u4F9B\uFF0C\u4ECE\u6587\u5B57\u8272\u4F4E\u6BD4\u4F8B\u6D3E\u751F\uFF08\u4EAE\u6697\u4E3B\u9898\u90FD\u6210\u7ACB\uFF09 */\n --dsw-alias-fill-hover: color-mix(in srgb, var(--dsw-alias-label-primary) 8%, transparent);\n --dsw-alias-fill-selected: color-mix(in srgb, var(--dsw-alias-label-primary) 12%, transparent);\n /* \u5F3A\u8C03\u5E95\u4E0A\u7684\u6587\u5B57\uFF1A\u7528\u57FA\u7840\u5E95\u8272\uFF08\u6697\u8272\u4E3B\u9898=\u6D45\u5B57\u3001\u4EAE\u8272\u4E3B\u9898=\u6DF1\u5B57\uFF0C\u5728\u54C1\u724C\u8272\u4E0A\u90FD\u53EF\u8BFB\uFF09 */\n --dsw-alias-text-on-accent: var(--dsw-alias-bg-base);\n /* \u906E\u7F69\u4E0E\u6D6E\u5C42 */\n --dsw-alias-scrim: color-mix(in srgb, var(--dsw-alias-label-primary) 32%, transparent);\n --dsw-alias-toast-bg: var(--dsw-alias-bg-overlay, var(--dsw-alias-bg-layer-2));\n --dsw-alias-toast-label: var(--dsw-alias-label-primary);\n /* \u9634\u5F71\uFF1A\u5BBF\u4E3B\u672A\u66B4\u9732 shadow token\uFF0C\u7528\u6587\u5B57\u8272\u6D3E\u751F\u7684\u8F6F\u6295\u5F71\uFF0C\u8DDF\u968F\u4E3B\u9898\u6DF1\u6D45 */\n --dsw-shadow-lv2: 0 4px 16px color-mix(in srgb, var(--dsw-alias-label-primary) 18%, transparent);\n --dsw-shadow-lv3: 0 8px 28px color-mix(in srgb, var(--dsw-alias-label-primary) 26%, transparent);\n}\n\n.ah-page {\n display: flex;\n flex-direction: column;\n height: 100%;\n min-height: 0;\n background: var(--dsw-alias-bg-base);\n color: var(--dsw-alias-text-primary);\n}\n\n/* \u6574\u9875\u4E0D\u53EF\u7528\u63D0\u793A */\n.ah-page-down { align-items: center; justify-content: center; }\n.ah-down { text-align: center; display: grid; gap: 12px; justify-items: center; }\n\n/* ---------- \u9876\u680F ---------- */\n.ah-topbar {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n padding: 12px 20px;\n border-bottom: 1px solid var(--dsw-alias-separator);\n}\n.ah-topbar h1 { font-size: 20px; font-weight: 500; margin: 0; }\n.ah-topbar-title { display: flex; align-items: baseline; gap: 10px; padding-top: calc(0px + var(--dsh-frame-top-clearance, 0px)); }\n.ah-topbar-now { font-size: 13px; color: var(--dsw-alias-text-secondary); }\n.ah-topbar-status { display: flex; align-items: center; gap: 10px; }\n\n/* macOS\uFF1A\u6807\u9898\u7ED9\u5218\u6D77/\u6807\u9898\u533A\u7559\u7A7A\u95F4 */\n[data-platform='darwin'] .ah-topbar-title { padding-top: calc(28px + var(--dsh-frame-top-clearance, 0px)); }\n\n/* ---------- \u4E09\u680F\u9AA8\u67B6 ---------- */\n.ah-body { display: flex; flex: 1; min-height: 0; }\n.ah-botlist { width: 260px; flex-shrink: 0; display: flex; flex-direction: column; border-right: 1px solid var(--dsw-alias-separator); overflow-y: auto; }\n.ah-thread { flex: 1; min-width: 0; display: flex; flex-direction: column; }\n.ah-right { width: 320px; flex-shrink: 0; border-left: 1px solid var(--dsw-alias-separator); display: flex; flex-direction: column; min-height: 0; }\n.ah-right-scroll { overflow-y: auto; flex: 1; padding: 0 16px; }\n\n/* \u4E2D\u680F\u6700\u5C0F\u53EF\u7528\u5BBD\u5EA6 */\n.ah-thread { min-width: 400px; }\n@media (max-width: 900px) {\n /* \u7A84\u5C4F\uFF1A\u53F3\u680F\u6539\u6210\u62BD\u5C49\uFF08\u76D6\u5728\u7EBF\u7A0B\u4E0A\uFF0C\u53EF\u6536\u8D77\uFF09 */\n .ah-right {\n position: absolute;\n top: 0; right: 0; bottom: 0;\n z-index: 30;\n box-shadow: var(--dsw-shadow-lv2);\n background: var(--dsw-alias-bg-base);\n width: min(320px, 85vw);\n }\n .ah-botlist { width: 200px; }\n}\n\n/* ---------- \u5DE6\u680F\u5217\u8868 ---------- */\n.ah-bot-row {\n display: flex; align-items: center; gap: 10px;\n width: 100%; padding: 10px 12px;\n background: transparent; border: none; cursor: pointer;\n color: inherit; text-align: left; font: inherit;\n}\n.ah-bot-row:hover { background: var(--dsw-alias-fill-hover, var(--dsw-alias-bg-inset)); }\n.ah-bot-row.ah-selected { background: var(--dsw-alias-fill-selected, var(--dsw-alias-bg-inset)); }\n.ah-bot-row-main { flex: 1; min-width: 0; display: grid; gap: 2px; }\n.ah-bot-row-name { font-weight: 500; display: flex; align-items: center; gap: 6px; }\n.ah-bot-row-role { font-size: 12px; color: var(--dsw-alias-text-secondary); }\n.ah-bot-row-activity {\n font-size: 12px; color: var(--dsw-alias-text-secondary);\n white-space: nowrap; overflow: hidden; text-overflow: ellipsis;\n}\n.ah-botlist-footer { margin-top: auto; padding: 10px 12px; border-top: 1px solid var(--dsw-alias-separator); }\n.ah-inbox-entry { width: 100%; padding: 8px; }\n\n/* \u5934\u50CF\u4E0E\u672A\u8BFB */\n.ah-avatar {\n display: inline-flex; align-items: center; justify-content: center;\n border-radius: 999px; flex-shrink: 0;\n font-size: 13px; font-weight: 500;\n}\n.ah-avatar-team { background: var(--dsw-alias-accent-muted); color: var(--dsw-alias-text-primary); }\n.ah-unread {\n background: var(--dsw-alias-danger); color: var(--dsw-alias-text-on-accent, var(--dsw-alias-text-primary));\n border-radius: 999px; font-size: 11px; padding: 1px 6px; flex-shrink: 0;\n}\n\n/* \u6807\u7B7E */\n.ah-tag { font-size: 11px; border-radius: var(--dsw-radius-sm, 4px); padding: 1px 6px; }\n.ah-tag-wait { background: var(--dsw-alias-warning-muted, var(--dsw-alias-bg-inset)); color: var(--dsw-alias-warning, var(--dsw-alias-text-primary)); }\n\n/* ---------- \u4E2D\u680F\u7EBF\u7A0B ---------- */\n.ah-thread-header { display: flex; align-items: center; justify-content: space-between; padding: 10px 20px; border-bottom: 1px solid var(--dsw-alias-separator); }\n.ah-thread-header h2 { margin: 0; font-size: 15px; font-weight: 500; }\n.ah-thread-messages { flex: 1; overflow-y: auto; padding: 16px 20px; display: flex; flex-direction: column; gap: 12px; }\n.ah-msg { display: flex; gap: 8px; align-items: flex-start; }\n.ah-msg-mine { flex-direction: row-reverse; }\n.ah-msg-body { max-width: min(70%, 620px); display: grid; gap: 3px; }\n.ah-msg-mine .ah-msg-body { justify-items: end; }\n.ah-msg-meta { font-size: 11px; color: var(--dsw-alias-text-secondary); }\n.ah-msg-bubble {\n padding: 8px 12px; border-radius: var(--dsw-radius-lg, 10px);\n background: var(--dsw-alias-bg-inset); word-break: break-word;\n}\n.ah-msg-mine .ah-msg-bubble { background: var(--dsw-alias-accent-muted); }\n.ah-msg-center { justify-content: center; text-align: center; }\n.ah-msg-bubble-center { font-size: 12px; color: var(--dsw-alias-text-secondary); background: transparent; padding: 2px 8px; }\n.ah-msg-center .ah-msg-time { font-size: 11px; color: var(--dsw-alias-text-secondary); }\n\n/* \u8F93\u5165\u533A */\n.ah-thread-input { display: flex; gap: 8px; padding: 12px 20px; border-top: 1px solid var(--dsw-alias-separator); }\n.ah-thread-input input {\n flex: 1; padding: 8px 12px;\n border-radius: var(--dsw-radius-md, 8px);\n border: 1px solid var(--dsw-alias-separator-strong, var(--dsw-alias-separator));\n background: var(--dsw-alias-bg-inset); color: inherit;\n}\n/* \u53D1\u9001\u5931\u8D25\u63D0\u793A\uFF08\u4EA4\u4E92\u5BA1\u67E5\u7B2C 4 \u6761\uFF09\uFF1A\u8D34\u5728\u8F93\u5165\u6846\u4E0B\u65B9\uFF0C\u4E00\u53E5\u8BDD\u539F\u56E0 + \u91CD\u8BD5\uFF0C\u4E0D\u6253\u65AD\u8F93\u5165\u3002 */\n.ah-thread-senderror {\n display: flex; align-items: center; gap: 8px;\n padding: 6px 20px 10px; font-size: 12px;\n color: var(--dsw-alias-text-danger, var(--dsw-alias-text-secondary));\n}\n\n/* ---------- \u5361\u7247 ---------- */\n.ah-card {\n margin-top: 6px; padding: 10px 12px;\n border: 1px solid var(--dsw-alias-separator-strong, var(--dsw-alias-separator));\n border-radius: var(--dsw-radius-lg, 10px);\n background: var(--dsw-alias-bg-elevated, var(--dsw-alias-bg-base));\n display: grid; gap: 6px; text-align: left;\n}\n.ah-card-done { opacity: 0.7; }\n.ah-card-head { display: flex; align-items: center; gap: 8px; }\n.ah-card-type { font-size: 11px; color: var(--dsw-alias-text-secondary); border: 1px solid var(--dsw-alias-separator); border-radius: var(--dsw-radius-sm, 4px); padding: 0 6px; }\n.ah-card-title { font-weight: 500; }\n.ah-card-body { margin: 0; font-size: 13px; }\n.ah-card-expire { margin: 0; font-size: 12px; color: var(--dsw-alias-text-secondary); }\n.ah-card-answer { margin: 0; font-size: 12px; color: var(--dsw-alias-text-secondary); }\n.ah-card-error { margin: 4px 0 0; font-size: 12px; color: var(--dsw-alias-danger, var(--dsw-alias-text-secondary)); }\n.ah-card-options { display: flex; flex-wrap: wrap; gap: 8px; }\n\n/* ---------- \u6536\u4EF6\u7BB1 ---------- */\n.ah-inbox { flex: 1; overflow-y: auto; padding: 16px 20px; display: grid; gap: 12px; align-content: start; }\n.ah-inbox-row {\n padding: 12px 14px; border: 1px solid var(--dsw-alias-separator);\n border-radius: var(--dsw-radius-lg, 10px);\n background: var(--dsw-alias-bg-elevated, var(--dsw-alias-bg-base));\n display: grid; gap: 8px;\n}\n.ah-inbox-row header { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }\n.ah-inbox-row p { margin: 0; font-size: 13px; }\n.ah-inbox-time { font-size: 12px; color: var(--dsw-alias-text-secondary); margin-left: auto; }\n.ah-inbox-p0 { border-color: var(--dsw-alias-danger); }\n.ah-inbox-p1 { border-color: var(--dsw-alias-warning, var(--dsw-alias-separator-strong, var(--dsw-alias-separator))); }\n\n/* ---------- \u53F3\u680F ---------- */\n.ah-right-head { display: flex; align-items: center; justify-content: space-between; padding: 10px 16px; border-bottom: 1px solid var(--dsw-alias-separator); }\n.ah-right-head h2 { margin: 0; font-size: 15px; font-weight: 500; display: flex; align-items: center; gap: 8px; }\n.ah-right-section { padding: 10px 0; border-bottom: 1px solid var(--dsw-alias-separator); }\n.ah-right-section h3 { margin: 0 0 6px; font-size: 12px; color: var(--dsw-alias-text-secondary); font-weight: 500; }\n.ah-right-section p, .ah-right-section li { margin: 0; font-size: 13px; }\n.ah-right-section ul { margin: 0; padding-left: 18px; display: grid; gap: 6px; }\n.ah-right-kv { color: var(--dsw-alias-text-secondary); }\n.ah-right-actions { display: flex; gap: 8px; flex-wrap: wrap; padding: 12px 16px; border-top: 1px solid var(--dsw-alias-separator); align-items: center; }\n.ah-right-notice { width: 100%; margin: 0; font-size: 12px; color: var(--dsw-alias-text-secondary); }\n.ah-appraisals { list-style: none; padding: 0; display: grid; gap: 5px; font-size: 12px; }\n.ah-appraisal-decision { color: var(--dsw-alias-text-secondary); margin-right: 4px; }\n.ah-appraisal-woke { color: var(--dsw-alias-success, var(--dsw-alias-text-primary)); }\n\n/* ---------- \u901A\u7528\u4EF6 ---------- */\n.ah-btn {\n padding: 5px 12px; border-radius: var(--dsw-radius-md, 8px);\n border: 1px solid var(--dsw-alias-separator-strong, var(--dsw-alias-separator));\n background: var(--dsw-alias-bg-inset); color: inherit;\n cursor: pointer; font: inherit; font-size: 13px;\n}\n.ah-btn:hover { background: var(--dsw-alias-fill-hover, var(--dsw-alias-bg-inset)); }\n.ah-btn:disabled { opacity: 0.5; cursor: default; }\n.ah-btn-primary { border-color: var(--dsw-alias-accent); color: var(--dsw-alias-text-on-accent, var(--dsw-alias-text-primary)); background: var(--dsw-alias-accent-muted); }\n.ah-btn-ghost { border-color: transparent; background: transparent; }\n\n.ah-dot { display: inline-flex; align-items: center; gap: 5px; font-size: 12px; }\n.ah-dot-circle { width: 8px; height: 8px; border-radius: 999px; display: inline-block; }\n.ah-dot-ok .ah-dot-circle { background: var(--dsw-alias-success, var(--dsw-alias-accent)); }\n.ah-dot-warn .ah-dot-circle { background: var(--dsw-alias-warning, var(--dsw-alias-accent)); }\n.ah-dot-busy .ah-dot-circle { background: var(--dsw-alias-accent); }\n.ah-dot-idle .ah-dot-circle { background: var(--dsw-alias-text-secondary); }\n.ah-dot-danger .ah-dot-circle { background: var(--dsw-alias-danger); }\n\n.ah-skeleton { display: grid; gap: 8px; padding: 16px; }\n.ah-skeleton-row { height: 14px; border-radius: var(--dsw-radius-sm, 4px); background: var(--dsw-alias-bg-inset); }\n.ah-retry { display: flex; align-items: center; gap: 10px; padding: 16px 20px; color: var(--dsw-alias-text-secondary); }\n.ah-empty { padding: 16px 20px; font-size: 13px; color: var(--dsw-alias-text-secondary); }\n\n/* \u5360\u4F4D\u5BF9\u8BDD\u6846 */\n.ah-dialog-mask { position: fixed; inset: 0; background: var(--dsw-alias-scrim, transparent); display: flex; align-items: center; justify-content: center; z-index: 100; }\n.ah-dialog {\n background: var(--dsw-alias-bg-elevated, var(--dsw-alias-bg-base));\n border-radius: var(--dsw-radius-lg, 10px); box-shadow: var(--dsw-shadow-lv3);\n padding: 20px 24px; display: grid; gap: 12px; justify-items: center; text-align: center;\n}\n\n/* ================= D03\uFF1A\u6C14\u6CE1\u3001\u7EBF\u7A0B\u5934\u90E8\u3001\u5411\u5BFC\u3001\u6536\u4EF6\u7BB1\u3001\u6D3B\u52A8\u6D41\u3001\u8BBE\u7F6E\u3001\u63D0\u793A ================= */\n\n/* \u7EBF\u7A0B\u5934\u90E8\uFF1A\u540D\u5B57/\u89D2\u8272 + \u72B6\u6001\u53E5 + \u4E0B\u6B21\u81EA\u67E5 + \u53F3\u4FA7\u64CD\u4F5C */\n.ah-thread-header { flex-wrap: wrap; gap: 6px 12px; }\n.ah-thread-header-main { display: grid; gap: 2px; min-width: 0; }\n.ah-thread-header-main h2 { display: flex; align-items: center; gap: 8px; }\n.ah-thread-status { margin: 0; font-size: 12px; color: var(--dsw-alias-text-secondary); }\n.ah-thread-next { margin: 0; font-size: 12px; color: var(--dsw-alias-text-secondary); }\n.ah-thread-header-actions { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }\n\n/* \u6C14\u6CE1\uFF1A\u4E3B\u4EBA\u53F3\u4E3B\u8272\u6DE1\u5E95\u3001\u673A\u5668\u4EBA\u5DE6\u4E2D\u6027\u5E95\uFF1B\u6C47\u62A5\u5DE6\u4FA7\u7EC6\u8272\u6761 */\n.ah-msg-bubble {\n border-radius: var(--dsw-radius-lg, 10px);\n background: var(--dsw-alias-bg-inset);\n padding: 8px 12px;\n border-top-left-radius: var(--dsw-radius-sm, 4px);\n}\n.ah-msg-mine .ah-msg-bubble {\n background: var(--dsw-alias-accent-muted);\n border-top-left-radius: var(--dsw-radius-lg, 10px);\n border-top-right-radius: var(--dsw-radius-sm, 4px);\n}\n.ah-msg-report .ah-msg-bubble { border-left: 3px solid var(--dsw-alias-accent); }\n\n/* \u6309\u94AE\uFF1A\u60AC\u505C\u4E0E\u6309\u4E0B\u53CD\u9988 */\n.ah-btn { transition: background 0.12s ease, border-color 0.12s ease, transform 0.06s ease; }\n.ah-btn:hover:not(:disabled) { filter: brightness(1.05); background: var(--dsw-alias-fill-hover, var(--dsw-alias-bg-inset)); }\n.ah-btn:active:not(:disabled) { transform: translateY(1px); }\n/* \u952E\u76D8\u7126\u70B9\u73AF\uFF08\u89C6\u89C9\u4FEE\u590D\uFF1A\u539F\u6765\u6CA1\u6709\u7126\u70B9\u6837\u5F0F\uFF0CTab \u5BFC\u822A\u5B8C\u5168\u770B\u4E0D\u51FA\u7126\u70B9\u5728\u54EA\uFF09 */\n.ah-btn:focus-visible, .ah-bot-row:focus-visible, .ah-inbox-entry:focus-visible,\n.ah-thread-input input:focus-visible, .ah-card-freetext input:focus-visible, .ah-card-note:focus-visible {\n outline: 2px solid var(--dsw-alias-accent);\n outline-offset: 1px;\n}\n.ah-btn-primary {\n border-color: var(--dsw-alias-accent);\n background: var(--dsw-alias-accent);\n color: var(--dsw-alias-text-on-accent, var(--dsw-alias-text-primary));\n}\n.ah-btn-primary:hover:not(:disabled) {\n background: color-mix(in srgb, var(--dsw-alias-accent) 88%, var(--dsw-alias-label-primary));\n border-color: color-mix(in srgb, var(--dsw-alias-accent) 88%, var(--dsw-alias-label-primary));\n}\n.ah-btn-danger { border-color: var(--dsw-alias-danger); color: var(--dsw-alias-danger); }\n.ah-btn-icon { display: inline-flex; align-items: center; gap: 4px; }\n\n/* \u5361\u7247\u91CC\u7684\u81EA\u7531\u6587\u672C\u4E0E\u9644\u8A00 */\n.ah-card-freetext { display: flex; gap: 8px; }\n.ah-card-freetext input, .ah-card-note {\n flex: 1; padding: 6px 10px; font: inherit; color: inherit;\n border-radius: var(--dsw-radius-md, 8px);\n border: 1px solid var(--dsw-alias-separator);\n background: var(--dsw-alias-bg-inset);\n}\n.ah-card-note { width: 100%; }\n\n/* \u6536\u4EF6\u7BB1\u5206\u7EC4 */\n.ah-inbox-group { display: grid; gap: 8px; }\n.ah-inbox-group h3 { margin: 8px 0 0; font-size: 12px; color: var(--dsw-alias-text-secondary); font-weight: 500; }\n.ah-inbox-bot { font-weight: 500; }\n.ah-inbox-row { border: 1px solid var(--dsw-alias-separator); border-radius: var(--dsw-radius-lg, 10px); padding: 10px 12px; display: grid; gap: 8px; background: var(--dsw-alias-bg-elevated, var(--dsw-alias-bg-base)); }\n.ah-inbox-row header { display: flex; align-items: center; gap: 8px; }\n.ah-inbox-row .ah-card { border: none; margin: 0; padding: 0; background: transparent; }\n.ah-inbox-p0 .ah-inbox-row { border-color: var(--dsw-alias-danger); }\n\n/* \u6D3B\u52A8\u6D41 */\n.ah-activity { flex: 1; overflow-y: auto; padding: 16px 20px; display: grid; gap: 12px; align-content: start; }\n.ah-activity-filters { display: flex; gap: 12px; align-items: center; flex-wrap: wrap; font-size: 13px; }\n.ah-activity-filters label { display: flex; align-items: center; gap: 6px; color: var(--dsw-alias-text-secondary); }\n.ah-activity-filters select {\n font: inherit; color: inherit; padding: 4px 8px;\n border-radius: var(--dsw-radius-md, 8px);\n border: 1px solid var(--dsw-alias-separator);\n background: var(--dsw-alias-bg-inset);\n}\n.ah-activity-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 10px; }\n.ah-activity-row { display: flex; align-items: flex-start; gap: 8px; font-size: 13px; }\n.ah-activity-time { color: var(--dsw-alias-text-secondary); font-size: 12px; flex-shrink: 0; min-width: 72px; }\n.ah-activity-text { flex: 1; min-width: 0; }\n.ah-activity-kind {\n font-size: 11px; color: var(--dsw-alias-text-secondary);\n border: 1px solid var(--dsw-alias-separator);\n border-radius: var(--dsw-radius-sm, 4px);\n padding: 0 6px; margin-right: 6px;\n}\n.ah-activity-alert .ah-activity-kind { color: var(--dsw-alias-danger); border-color: var(--dsw-alias-danger); }\n.ah-activity-approval .ah-activity-kind { color: var(--dsw-alias-warning, var(--dsw-alias-text-secondary)); }\n.ah-activity-more { display: flex; justify-content: center; }\n\n/* \u8BBE\u7F6E\u9875 */\n.ah-settings { flex: 1; overflow-y: auto; padding: 16px 20px; display: grid; gap: 10px; align-content: start; }\n.ah-settings h3 { margin: 8px 0 0; font-size: 13px; font-weight: 500; color: var(--dsw-alias-text-secondary); }\n.ah-settings-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 10px; }\n.ah-settings-grid label, .ah-wizard-field label, .ah-edit-box label, .ah-wizard-grid label {\n display: grid; gap: 4px; font-size: 13px;\n}\n.ah-settings-grid input, .ah-settings-grid select,\n.ah-wizard-field input, .ah-wizard-field textarea, .ah-wizard-field select,\n.ah-edit-box input, .ah-edit-box textarea, .ah-edit-add input, .ah-edit-list input {\n font: inherit; color: inherit; padding: 6px 10px;\n border-radius: var(--dsw-radius-md, 8px);\n border: 1px solid var(--dsw-alias-separator);\n background: var(--dsw-alias-bg-inset);\n}\n.ah-settings-grid input:disabled { opacity: 0.7; }\n.ah-settings-error { margin: 0; font-size: 13px; color: var(--dsw-alias-danger); }\n.ah-settings-actions { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }\n.ah-settings-health { margin: 0; padding-left: 18px; display: grid; gap: 4px; font-size: 13px; }\n\n/* \u5411\u5BFC */\n.ah-dialog-wide { max-width: min(680px, calc(100vw - 48px)); text-align: left; justify-items: stretch; }\n.ah-wizard-head { display: flex; align-items: center; justify-content: space-between; }\n.ah-wizard-head h3 { margin: 0; }\n.ah-wizard-steps { list-style: none; display: flex; gap: 6px; margin: 0; padding: 0; flex-wrap: wrap; font-size: 12px; }\n.ah-wizard-steps li { color: var(--dsw-alias-text-secondary); padding: 2px 8px; border-radius: var(--dsw-radius-sm, 4px); border: 1px solid var(--dsw-alias-separator); }\n.ah-wizard-steps .ah-wizard-step-current { color: var(--dsw-alias-text-on-accent, var(--dsw-alias-text-primary)); background: var(--dsw-alias-accent); border-color: var(--dsw-alias-accent); }\n.ah-wizard-steps .ah-wizard-step-done { color: var(--dsw-alias-accent); border-color: var(--dsw-alias-accent); }\n.ah-wizard-body { display: grid; gap: 12px; }\n.ah-wizard-roles { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 8px; }\n.ah-wizard-role {\n display: grid; gap: 4px; padding: 10px; text-align: left; font: inherit; color: inherit; cursor: pointer;\n border: 1px solid var(--dsw-alias-separator); border-radius: var(--dsw-radius-lg, 10px);\n background: var(--dsw-alias-bg-inset); font-size: 13px;\n}\n.ah-wizard-role span { color: var(--dsw-alias-text-secondary); }\n.ah-wizard-role-picked { border-color: var(--dsw-alias-accent); background: var(--dsw-alias-accent-muted); }\n.ah-wizard-role-disabled { opacity: 0.5; cursor: not-allowed; }\n.ah-wizard-colors { display: flex; gap: 8px; flex-wrap: wrap; }\n.ah-wizard-colors button { width: 26px; height: 26px; border-radius: 999px; border: 2px solid transparent; cursor: pointer; }\n.ah-wizard-colors .ah-wizard-color-picked { border-color: var(--dsw-alias-text-primary); }\n.ah-wizard-examples { display: grid; gap: 6px; }\n.ah-wizard-examples .ah-btn { text-align: left; font-size: 12px; }\n.ah-wizard-optional { font-size: 13px; display: grid; gap: 8px; }\n.ah-wizard-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 10px; }\n.ah-wizard-confirm .ah-wizard-restatement { margin: 0; font-size: 14px; }\n.ah-wizard-label { font-size: 13px; color: var(--dsw-alias-text-secondary); }\n\n/* \u6559\u5B66\u5F39\u7A97 */\n.ah-teach-search, .ah-teach-note { font: inherit; color: inherit; padding: 6px 10px; border-radius: var(--dsw-radius-md, 8px); border: 1px solid var(--dsw-alias-separator); background: var(--dsw-alias-bg-inset); }\n.ah-teach-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 6px; max-height: 240px; overflow-y: auto; }\n.ah-teach-list label { display: flex; align-items: center; gap: 8px; padding: 6px 8px; border-radius: var(--dsw-radius-md, 8px); font-size: 13px; cursor: pointer; }\n.ah-teach-picked { background: var(--dsw-alias-accent-muted); }\n.ah-teach-title { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.ah-dialog-actions { display: flex; gap: 8px; justify-content: flex-end; }\n\n/* \u53F3\u680F\u7F16\u8F91 */\n.ah-edit-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 8px; font-size: 13px; }\n.ah-edit-list li { display: grid; gap: 4px; }\n.ah-edit-ops { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; }\n.ah-edit-ops input { font-size: 12px; padding: 3px 8px; max-width: 200px; }\n.ah-edit-add { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; }\n.ah-edit-box { display: grid; gap: 8px; font-size: 13px; }\n.ah-routine-steps { margin: 4px 0; padding-left: 18px; display: grid; gap: 2px; font-size: 12px; color: var(--dsw-alias-text-secondary); }\n\n/* \u5E94\u7528\u5185\u63D0\u793A\uFF08\u5B98\u65B9 toast \u6837\u5F0F\uFF0Cverified-T5 Q4-2\uFF1B\u53EA\u7528\u4E3B\u9898\u53D8\u91CF\uFF09 */\n.ah-toast-layer {\n position: fixed; top: 40px; left: 50%; transform: translateX(-50%);\n z-index: 1100; pointer-events: none;\n display: grid; gap: 8px; justify-items: center;\n max-width: min(640px, calc(100vw - 48px));\n}\n.ah-toast {\n padding: 12px 16px; border-radius: var(--dsw-radius-lg, 10px);\n background: var(--dsw-alias-toast-bg, var(--dsw-alias-bg-elevated, var(--dsw-alias-bg-base)));\n color: var(--dsw-alias-toast-label, var(--dsw-alias-text-primary));\n font-size: 14px; line-height: 22px;\n box-shadow: var(--dsw-shadow-lv3);\n pointer-events: auto;\n animation: ah-toast-in 0.16s ease;\n}\n.ah-toast-btn { all: unset; cursor: pointer; }\n.ah-toast-btn:hover { text-decoration: underline; }\n@keyframes ah-toast-in { from { opacity: 0; } to { opacity: 1; } }\n\n/* \u9875\u9762\u5E95\u90E8\u7684\u884C\u5185\u901A\u77E5\uFF08\u8BD5\u8DD1\u63D0\u793A\u7B49\uFF09\u2014\u2014D04 3c-3 \u8D77\u6539\u7528\u7EBF\u7A0B\u9876\u90E8\u63D0\u793A\u6761\uFF0C\u6B64\u6837\u5F0F\u4FDD\u7559\u517C\u5BB9 */\n.ah-toast-inline {\n position: absolute; bottom: 16px; left: 50%; transform: translateX(-50%);\n display: flex; gap: 10px; align-items: center;\n padding: 10px 16px; border-radius: var(--dsw-radius-lg, 10px);\n background: var(--dsw-alias-bg-elevated, var(--dsw-alias-bg-inset));\n box-shadow: var(--dsw-shadow-lv2); font-size: 13px; z-index: 40;\n}\n\n/* ================= D04\uFF1A\u5411\u5BFC\u9009\u4E2D\u6001\u3001\u4E09\u9009\u4E00\u7248\u5F0F\u3001\u7EBF\u7A0B\u9876\u90E8\u63D0\u793A\u6761\u3001\u6C14\u6CE1\u53EF\u8FA8\u6027 ================= */\n\n/* \u5411\u5BFC\u7B2C 1 \u6B65\uFF1A\u89D2\u8272\u5361\u9009\u4E2D = \u4E3B\u8272\u63CF\u8FB9 + \u6D45\u5E95 + \u53F3\u4E0A\u89D2\u300C\u5DF2\u9009\u300D\u89D2\u6807\uFF08D04 3c-1\uFF09 */\n.ah-wizard-role { position: relative; }\n.ah-wizard-role-picked {\n border-color: var(--dsw-alias-accent);\n background: var(--dsw-alias-accent-muted);\n box-shadow: inset 0 0 0 1px var(--dsw-alias-accent);\n}\n.ah-wizard-role-check {\n position: absolute; top: 6px; right: 6px;\n font-size: 11px; padding: 1px 6px; border-radius: var(--dsw-radius-sm, 4px);\n color: var(--dsw-alias-text-on-accent, var(--dsw-alias-text-primary));\n background: var(--dsw-alias-accent);\n}\n/* \u8272\u5757\u9009\u4E2D\u63CF\u8FB9\u7528\u4E3B\u8272\uFF08\u539F text-primary \u5728\u6697\u8272\u4E0B\u4E0D\u663E\u773C\uFF09 */\n.ah-wizard-colors .ah-wizard-color-picked { border-color: var(--dsw-alias-accent); }\n\n/* \u5411\u5BFC\u7B2C 4 \u6B65\uFF1A\u4E09\u4E2A\u9009\u62E9\u6846\u540C\u9AD8\u540C\u5BBD\u4E00\u884C\u5BF9\u9F50\uFF1B\u8BF4\u660E\u653E\u4E0B\u65B9\u3001\u6B21\u8981\u8272\u3001\u6B63\u5E38\u5B57\u53F7\uFF08D04 3c-2\uFF09 */\n.ah-wizard-trio {\n display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px;\n}\n.ah-wizard-trio label { display: grid; gap: 4px; font-size: 13px; }\n.ah-wizard-trio select { width: 100%; height: 34px; }\n.ah-wizard-note { margin: 0; font-size: 13px; color: var(--dsw-alias-text-secondary); }\n\n/* \u7EBF\u7A0B\u9876\u90E8\u63D0\u793A\u6761\uFF08\u8BD5\u8DD1\u63D0\u793A\u7B49\uFF09\uFF1A\u5934\u90E8\u4E0B\u65B9\u3001\u4E0D\u906E\u6321\u8F93\u5165\u6846\uFF08D04 3c-3\uFF09 */\n.ah-thread-banner {\n display: flex; gap: 10px; align-items: center; justify-content: space-between;\n margin: 8px 20px 0; padding: 8px 12px;\n border: 1px solid var(--dsw-alias-separator);\n border-radius: var(--dsw-radius-md, 8px);\n background: var(--dsw-alias-accent-muted);\n font-size: 13px;\n}\n\n/* \u6C14\u6CE1\u53EF\u8FA8\u6027\uFF08D04 3c-4\uFF09\uFF1A\u4E3B\u4EBA\u4E3B\u8272\u6D45\u5E95 + \u4E3B\u8272\u63CF\u8FB9\uFF1B\u673A\u5668\u4EBA\u4E2D\u6027\u5E95 + \u5206\u9694\u7EBF\u63CF\u8FB9 */\n.ah-msg-bubble { border: 1px solid var(--dsw-alias-separator); }\n.ah-msg-mine .ah-msg-bubble {\n background: var(--dsw-alias-accent-muted);\n border: 1px solid var(--dsw-alias-accent);\n}\n\n/* \u53F3\u680F\u5FD7\u5411\u884C\uFF1A\u6587\u5B57\u4E0E\u64CD\u4F5C\u540C\u4E00\u884C\uFF0C\u53EA\u5728\u7F16\u8F91\u65F6\u51FA\u73B0\u8F93\u5165\u6846\uFF08D04 3c-5\uFF09 */\n.ah-edit-row { display: flex; gap: 8px; align-items: center; justify-content: space-between; min-width: 0; }\n";
34
-
35
- // src/client/api.ts
36
- var BASE = "api/agent-hub";
37
- async function getJson(path) {
38
- let response;
39
- try {
40
- response = await fetch(`${BASE}/${path}`, { credentials: "same-origin" });
41
- } catch {
42
- throw new Error("\u8FDE\u4E0D\u4E0A Agent \u4E2D\u5FC3\uFF08\u7F51\u7EDC\u9519\u8BEF\uFF09");
43
- }
44
- if (!response.ok) {
45
- let message = `\u8BF7\u6C42\u5931\u8D25\uFF08${response.status}\uFF09`;
46
- try {
47
- const body = await response.json();
48
- if (body?.error) message = body.error;
49
- } catch {
50
- }
51
- throw new Error(message);
52
- }
53
- return await response.json();
54
- }
55
- async function postCommand(name, input) {
56
- let response;
57
- try {
58
- response = await fetch(`${BASE}/command`, {
59
- method: "POST",
60
- credentials: "same-origin",
61
- headers: { "Content-Type": "application/json; charset=utf-8" },
62
- body: JSON.stringify({ name, input })
63
- });
64
- } catch {
65
- return { ok: false, error: "\u8FDE\u4E0D\u4E0A Agent \u4E2D\u5FC3\uFF08\u7F51\u7EDC\u9519\u8BEF\uFF09" };
66
- }
67
- if (!response.ok) {
68
- let message = `\u8BF7\u6C42\u5931\u8D25\uFF08${response.status}\uFF09`;
69
- try {
70
- const body = await response.json();
71
- if (body?.error) message = body.error;
72
- } catch {
73
- }
74
- return { ok: false, error: message };
75
- }
76
- return await response.json();
77
- }
78
- var api = {
79
- overview: () => getJson("overview"),
80
- bot: (id) => getJson(`bot?id=${encodeURIComponent(id)}`),
81
- thread: (id, opts) => {
82
- const params = new URLSearchParams({ id });
83
- if (opts?.before) params.set("before", opts.before);
84
- if (opts?.limit) params.set("limit", String(opts.limit));
85
- return getJson(`thread?${params.toString()}`);
86
- },
87
- inbox: () => getJson("inbox"),
88
- activity: (opts) => {
89
- const params = new URLSearchParams();
90
- if (opts?.before) params.set("before", opts.before);
91
- if (opts?.limit) params.set("limit", String(opts.limit));
92
- const qs = params.toString();
93
- return getJson(qs ? `activity?${qs}` : "activity");
94
- },
95
- settings: () => getJson("settings"),
96
- recentSessions: () => getJson("recent-sessions"),
97
- command: postCommand
98
- };
99
-
100
- // src/client/store.ts
101
- var import_react = require("react");
102
- function createStore(initial) {
103
- let snapshot = initial;
104
- const listeners = /* @__PURE__ */ new Set();
105
- return {
106
- getSnapshot: () => snapshot,
107
- subscribe(fn) {
108
- listeners.add(fn);
109
- return () => {
110
- listeners.delete(fn);
111
- };
112
- },
113
- set(next) {
114
- if (next === snapshot) return;
115
- snapshot = next;
116
- for (const fn of [...listeners]) {
117
- try {
118
- fn();
119
- } catch (error) {
120
- console.error("[agent-hub] store \u901A\u77E5\u51FA\u9519:", error);
121
- }
122
- }
123
- },
124
- update(fn) {
125
- this.set(fn(snapshot));
126
- }
127
- };
128
- }
129
- function useStore(store) {
130
- return (0, import_react.useSyncExternalStore)(store.subscribe, store.getSnapshot, store.getSnapshot);
131
- }
132
-
133
- // src/client/logic/feeds.ts
134
- var PRIORITY_ORDER = ["P0", "P1", "P2"];
135
- var PRIORITY_LABEL = {
136
- P0: "P0 \u9A6C\u4E0A\u8981\u4F60\u5904\u7406",
137
- P1: "P1 \u7B49\u4F60\u62CD\u677F",
138
- P2: "P2 \u6709\u7A7A\u518D\u770B"
139
- };
140
- function groupInbox(items) {
141
- const groups = [];
142
- for (const priority of PRIORITY_ORDER) {
143
- const list = items.filter((item) => item.priority === priority).sort((a, b) => b.createdAt - a.createdAt || a.id.localeCompare(b.id));
144
- if (list.length > 0) groups.push({ priority, label: PRIORITY_LABEL[priority], items: list });
145
- }
146
- return groups;
147
- }
148
- var ACTIVITY_KIND_LABELS = {
149
- wake: "\u5524\u9192",
150
- commit: "\u4EA4\u8D26",
151
- approval: "\u5BA1\u6279",
152
- alert: "\u544A\u8B66",
153
- handoff: "\u4EA4\u63A5",
154
- peer: "\u540C\u4F34\u6D88\u606F",
155
- lifecycle: "\u751F\u547D\u5468\u671F",
156
- system: "\u4E2D\u5FC3"
157
- };
158
- function filterActivity(rows, filter) {
159
- return rows.filter(
160
- (row) => (filter.botId == null || filter.botId === "" || row.botId === filter.botId) && (filter.kind == null || row.kind === filter.kind)
161
- );
162
- }
163
- function entryLabel(inboxTotal) {
164
- return inboxTotal > 0 ? `\u961F\u53CB \xB7 ${inboxTotal > 99 ? "99+" : inboxTotal}` : "\u961F\u53CB";
165
- }
166
- function newUrgentInbox(prev, next) {
167
- const seen = new Set(prev.map((item) => item.id));
168
- return next.filter((item) => !seen.has(item.id) && (item.priority === "P0" || item.priority === "P1"));
169
- }
170
-
171
- // src/client/logic/scopes.ts
172
- var HUB_SCOPES = {
173
- overview: "overview",
174
- bot: "bot",
175
- thread: "thread",
176
- inbox: "inbox",
177
- activity: "activity",
178
- settings: "settings"
179
- };
180
- var emptyPlan = () => ({ overview: false, bots: [], threads: [], inbox: false, activity: false, settings: false });
181
- function refreshPlan(scope, botId) {
182
- const plan = emptyPlan();
183
- const id = typeof botId === "string" && botId !== "" ? botId : null;
184
- switch (scope) {
185
- case HUB_SCOPES.overview:
186
- plan.overview = true;
187
- if (id !== null) plan.bots.push(id);
188
- return plan;
189
- case HUB_SCOPES.bot:
190
- plan.overview = true;
191
- if (id !== null) plan.bots.push(id);
192
- return plan;
193
- case HUB_SCOPES.thread:
194
- plan.overview = true;
195
- plan.threads.push(id ?? "team");
196
- return plan;
197
- case HUB_SCOPES.inbox:
198
- plan.inbox = true;
199
- plan.overview = true;
200
- return plan;
201
- case HUB_SCOPES.activity:
202
- plan.activity = true;
203
- return plan;
204
- case HUB_SCOPES.settings:
205
- plan.settings = true;
206
- return plan;
207
- default:
208
- plan.overview = true;
209
- plan.activity = true;
210
- return plan;
211
- }
212
- }
213
-
214
- // src/client/hub-state.ts
215
- var TOAST_MS = 4e3;
216
- function mergeThreadMessages(base, fresh) {
217
- const index = new Map(base.map((m, i) => [m.id, i]));
218
- const out = [...base];
219
- for (const message of fresh) {
220
- const at = index.get(message.id);
221
- if (at === void 0) {
222
- index.set(message.id, out.length);
223
- out.push(message);
224
- } else out[at] = message;
225
- }
226
- return out;
227
- }
228
- function emptyState() {
229
- return {
230
- overview: null,
231
- inbox: null,
232
- activity: null,
233
- settings: null,
234
- recentSessions: [],
235
- bots: {},
236
- threads: {},
237
- connectionDown: false,
238
- downReason: null,
239
- downMessage: null,
240
- settingsError: null,
241
- view: "main",
242
- selectedThread: "team",
243
- toasts: []
244
- };
245
- }
246
- function reasonOfError(message) {
247
- if (message.includes("\u5DF2\u505C\u7528")) return "disabled";
248
- if (message.includes("\u5C1A\u672A\u5C31\u7EEA")) return "starting";
249
- if (message.includes("\u542F\u52A8\u5931\u8D25")) return "failed";
250
- return null;
251
- }
252
- function isNetworkError(message) {
253
- return message.includes("\u7F51\u7EDC\u9519\u8BEF");
254
- }
255
- function createHubStore() {
256
- const store = createStore(emptyState());
257
- const patch = (partial) => store.update((prev) => ({ ...prev, ...partial }));
258
- const guard = async (fn) => {
259
- try {
260
- patch(await fn());
261
- if (store.getSnapshot().connectionDown) patch({ connectionDown: false, downReason: null, downMessage: null });
262
- } catch (error) {
263
- const message = error instanceof Error ? error.message : String(error);
264
- console.error("[agent-hub] \u62C9\u53D6\u6570\u636E\u5931\u8D25:", error);
265
- patch({ connectionDown: true, downReason: reasonOfError(message), downMessage: reasonOfError(message) === "failed" ? message : null });
266
- }
267
- };
268
- const applyInbox = (next, prevItems) => {
269
- const fresh = prevItems === null ? [] : newUrgentInbox(prevItems, next.items);
270
- for (const item of fresh) {
271
- const toast = { id: `toast-${item.id}`, text: `${item.botName ?? "\u4E2D\u5FC3"}\uFF1A${item.title}`, inboxItemId: item.id };
272
- store.update((prev) => ({ ...prev, toasts: [...prev.toasts.filter((t) => t.id !== toast.id), toast] }));
273
- try {
274
- setTimeout(() => {
275
- try {
276
- store.update((prev) => ({ ...prev, toasts: prev.toasts.filter((t) => t.id !== toast.id) }));
277
- } catch {
278
- }
279
- }, TOAST_MS);
280
- } catch {
281
- }
282
- }
283
- return { inbox: next };
284
- };
285
- const hub = {
286
- ...store,
287
- async refreshOverview() {
288
- await guard(async () => ({ overview: await api.overview() }));
289
- },
290
- async refreshInbox() {
291
- const prevItems = store.getSnapshot().inbox?.items ?? null;
292
- await guard(async () => applyInbox(await api.inbox(), prevItems));
293
- },
294
- async refreshActivity(before) {
295
- try {
296
- const next = await api.activity(before ? { before } : void 0);
297
- if (!before) patch({ activity: next });
298
- else store.update((prev) => ({
299
- ...prev,
300
- activity: prev.activity === null ? next : { rows: [...prev.activity.rows, ...next.rows], hasMore: next.hasMore }
301
- }));
302
- if (store.getSnapshot().connectionDown) patch({ connectionDown: false, downReason: null, downMessage: null });
303
- } catch (error) {
304
- const message = error instanceof Error ? error.message : String(error);
305
- console.error("[agent-hub] \u62C9\u53D6\u6D3B\u52A8\u6D41\u5931\u8D25:", error);
306
- patch({ connectionDown: true, downReason: reasonOfError(message), downMessage: reasonOfError(message) === "failed" ? message : null });
307
- }
308
- },
309
- /**
310
- * 设置拉取(D07 第 2 条):不走会标 connectionDown 的通用 guard——设置读不到
311
- * 不等于连不上。只有网络错误、「尚未就绪」「已停用」「启动失败」才整页提示;
312
- * 其余(例如宿主模型目录坏了)把中文原因存进 settingsError,设置页与各模型
313
- * 下拉处显示原因和「重试」;旧的 settings 保持原值。
314
- */
315
- async refreshSettings() {
316
- try {
317
- const settings = await api.settings();
318
- patch({ settings, settingsError: null });
319
- if (store.getSnapshot().connectionDown) patch({ connectionDown: false, downReason: null, downMessage: null });
320
- } catch (error) {
321
- const message = error instanceof Error ? error.message : String(error);
322
- console.error("[agent-hub] \u62C9\u53D6\u8BBE\u7F6E\u5931\u8D25:", error);
323
- const reason = reasonOfError(message);
324
- if (reason !== null) patch({ connectionDown: true, downReason: reason, downMessage: reason === "failed" ? message : null });
325
- else if (isNetworkError(message)) patch({ connectionDown: true, downReason: null, downMessage: null });
326
- else patch({ settingsError: message });
327
- }
328
- },
329
- async refreshRecentSessions() {
330
- await guard(async () => ({ recentSessions: await api.recentSessions() }));
331
- },
332
- /**
333
- * 机器人详情(D07 第 9 条):先拿到详情,再在 updater 里合并进**最新**状态——
334
- * 两条 bot 推送挨着到时,后返回的请求不能把另一台刚拉到的新详情改回旧值。
335
- */
336
- async refreshBot(botId) {
337
- try {
338
- const view = await api.bot(botId);
339
- store.update((prev) => ({ ...prev, bots: { ...prev.bots, [botId]: view } }));
340
- if (store.getSnapshot().connectionDown) patch({ connectionDown: false, downReason: null, downMessage: null });
341
- } catch (error) {
342
- const message = error instanceof Error ? error.message : String(error);
343
- console.error("[agent-hub] \u62C9\u53D6\u673A\u5668\u4EBA\u8BE6\u60C5\u5931\u8D25:", error);
344
- patch({ connectionDown: true, downReason: reasonOfError(message), downMessage: reasonOfError(message) === "failed" ? message : null });
345
- }
346
- },
347
- /**
348
- * 线程(D07 第 14/9 条):带 before 是「加载更早」,把旧一页拼在已有列表前面;
349
- * 不带 before 是最新一页,**按消息 id 合并**进已加载的列表——更新已有消息与
350
- * 卡片状态、追加新消息,保留主人翻出来的更早历史(只在切换线程时才重置,
351
- * 见 selectThread)。合并一律在 updater 里做,避免并发覆盖。
352
- */
353
- async refreshThread(threadId, before) {
354
- try {
355
- const thread = await api.thread(threadId, before ? { before } : void 0);
356
- store.update((prev) => {
357
- const loaded = prev.threads[threadId];
358
- if (loaded === void 0) return { ...prev, threads: { ...prev.threads, [threadId]: thread } };
359
- const messages = before ? mergeThreadMessages(thread.messages, loaded.messages) : mergeThreadMessages(loaded.messages, thread.messages);
360
- const oldest = messages[0]?.id ?? null;
361
- return { ...prev, threads: { ...prev.threads, [threadId]: { ...thread, messages, hasMore: thread.hasMore || loaded.hasMore, oldestId: oldest } } };
362
- });
363
- if (store.getSnapshot().connectionDown) patch({ connectionDown: false, downReason: null, downMessage: null });
364
- } catch (error) {
365
- const message = error instanceof Error ? error.message : String(error);
366
- console.error("[agent-hub] \u62C9\u53D6\u7EBF\u7A0B\u5931\u8D25:", error);
367
- patch({ connectionDown: true, downReason: reasonOfError(message), downMessage: reasonOfError(message) === "failed" ? message : null });
368
- }
369
- },
370
- async refreshAll() {
371
- await Promise.all([hub.refreshOverview(), hub.refreshInbox(), hub.refreshActivity()]);
372
- },
373
- onChanged(event) {
374
- void (async () => {
375
- try {
376
- const plan = refreshPlan(event.scope, event.botId);
377
- const jobs = [];
378
- if (plan.overview) jobs.push(hub.refreshOverview());
379
- for (const botId of plan.bots) jobs.push(hub.refreshBot(botId));
380
- for (const threadId of plan.threads) jobs.push(hub.refreshThread(threadId));
381
- if (plan.inbox) jobs.push(hub.refreshInbox());
382
- if (plan.activity) jobs.push(hub.refreshActivity());
383
- if (plan.settings) jobs.push(hub.refreshSettings());
384
- await Promise.all(jobs);
385
- } catch (error) {
386
- console.error("[agent-hub] \u5904\u7406\u53D8\u5316\u4E8B\u4EF6\u51FA\u9519:", error);
387
- }
388
- })();
389
- },
390
- markDown(reason = null, message = null) {
391
- patch({ connectionDown: true, downReason: reason, downMessage: reason === "failed" ? message : null });
392
- },
393
- onReconnected() {
394
- if (store.getSnapshot().connectionDown) patch({ connectionDown: false, downReason: null, downMessage: null });
395
- const selected = store.getSnapshot().selectedThread;
396
- store.update((prev) => ({ ...prev, bots: {} }));
397
- void hub.refreshAll();
398
- void hub.refreshSettings();
399
- void hub.refreshThread(selected);
400
- if (selected !== "team") void hub.refreshBot(selected);
401
- },
402
- async send(threadId, text) {
403
- const result = await api.command("sendMessage", { threadId, text });
404
- if (!result.ok) throw new Error(result.error);
405
- await hub.refreshThread(threadId);
406
- },
407
- command(name, input) {
408
- return api.command(name, input);
409
- },
410
- selectThread(threadId) {
411
- store.update((prev) => {
412
- if (prev.selectedThread === threadId) return { ...prev, view: "main" };
413
- const threads = { ...prev.threads };
414
- delete threads[threadId];
415
- return { ...prev, selectedThread: threadId, view: "main", threads };
416
- });
417
- void api.command("markThreadRead", { threadId }).then(() => hub.refreshOverview()).catch(() => void 0);
418
- },
419
- setView(view) {
420
- patch({ view });
421
- },
422
- dismissToast(id) {
423
- store.update((prev) => ({ ...prev, toasts: prev.toasts.filter((t) => t.id !== id) }));
424
- }
425
- };
426
- return hub;
427
- }
428
- var RECONNECT_BACKOFF_MS = [1e3, 2e3, 5e3, 1e4];
429
- var DOWN_AFTER_MS = 3e4;
430
- function connectEvents(hub) {
431
- let closed = false;
432
- let source = null;
433
- let retryTimer = null;
434
- let attempt = 0;
435
- let downTimer = null;
436
- const clearDownTimer = () => {
437
- if (downTimer !== null) {
438
- clearTimeout(downTimer);
439
- downTimer = null;
440
- }
441
- };
442
- const scheduleReconnect = () => {
443
- if (closed) return;
444
- const wait = RECONNECT_BACKOFF_MS[Math.min(attempt, RECONNECT_BACKOFF_MS.length - 1)];
445
- attempt += 1;
446
- retryTimer = setTimeout(open, wait);
447
- };
448
- const open = () => {
449
- if (closed) return;
450
- try {
451
- source = new EventSource("agent-hub/events");
452
- } catch (error) {
453
- console.error("[agent-hub] \u5EFA\u7ACB SSE \u8FDE\u63A5\u5931\u8D25:", error);
454
- scheduleReconnect();
455
- return;
456
- }
457
- source.onopen = () => {
458
- attempt = 0;
459
- clearDownTimer();
460
- hub.onReconnected();
461
- };
462
- source.addEventListener("changed", (event) => {
463
- try {
464
- hub.onChanged(JSON.parse(event.data));
465
- } catch (error) {
466
- console.error("[agent-hub] \u89E3\u6790 changed \u4E8B\u4EF6\u5931\u8D25:", error);
467
- }
468
- });
469
- source.onerror = () => {
470
- try {
471
- source?.close();
472
- } catch {
473
- }
474
- source = null;
475
- if (downTimer === null && !closed) downTimer = setTimeout(() => {
476
- if (!closed) hub.markDown(null);
477
- }, DOWN_AFTER_MS);
478
- scheduleReconnect();
479
- };
480
- };
481
- open();
482
- return () => {
483
- closed = true;
484
- clearDownTimer();
485
- if (retryTimer !== null) clearTimeout(retryTimer);
486
- try {
487
- source?.close();
488
- } catch {
489
- }
490
- };
491
- }
492
-
493
- // src/client/pages/HubPage.tsx
494
- var import_react11 = require("react");
495
-
496
- // src/client/components/TopBar.tsx
497
- var import_jsx_runtime = require("react/jsx-runtime");
498
- function GearIcon() {
499
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { viewBox: "0 0 16 16", width: "14", height: "14", "aria-hidden": "true", className: "ah-gear-icon", children: [
500
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { fill: "currentColor", d: "M8 5.2A2.8 2.8 0 1 0 8 10.8 2.8 2.8 0 0 0 8 5.2zm0 1.5a1.3 1.3 0 1 1 0 2.6 1.3 1.3 0 0 1 0-2.6z" }),
501
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { fill: "currentColor", d: "M7.1 1h1.8l.3 1.6c.5.1 1 .3 1.4.6l1.5-.7 1.3 1.3-.7 1.5c.3.4.5.9.6 1.4l1.6.3v1.8l-1.6.3c-.1.5-.3 1-.6 1.4l.7 1.5-1.3 1.3-1.5-.7c-.4.3-.9.5-1.4.6L8.9 15H7.1l-.3-1.6c-.5-.1-1-.3-1.4-.6l-1.5.7-1.3-1.3.7-1.5c-.3-.4-.5-.9-.6-1.4L1.1 8.6V6.8l1.6-.3c.1-.5.3-1 .6-1.4l-.7-1.5 1.3-1.3 1.5.7c.4-.3.9-.5 1.4-.6L7.1 1z", opacity: "0.55" })
502
- ] });
503
- }
504
- function TopBar(props) {
505
- const o = props.overview;
506
- const back = props.view === "main" ? null : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "ah-btn", onClick: props.onBack, children: "\u8FD4\u56DE" });
507
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("header", { className: "ah-topbar", children: [
508
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "ah-topbar-title", children: [
509
- back,
510
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h1", { children: "\u961F\u53CB" }),
511
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "ah-topbar-now", children: [
512
- o.nowText,
513
- o.quietNow ? " \xB7 \u514D\u6253\u6270\u4E2D" : ""
514
- ] })
515
- ] }),
516
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "ah-topbar-status", children: [
517
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: `ah-dot ah-dot-${o.quota.light === "green" ? "ok" : o.quota.light === "yellow" ? "warn" : "danger"}`, children: [
518
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "ah-dot-circle", "aria-hidden": "true" }),
519
- o.quota.lightLabel,
520
- " \xB7 ",
521
- o.quota.usedText
522
- ] }),
523
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "ah-btn", onClick: props.onOpenActivity, children: "\u6D3B\u52A8\u6D41" }),
524
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "ah-btn ah-btn-icon", "aria-label": "\u8BBE\u7F6E", title: "\u8BBE\u7F6E", onClick: props.onOpenSettings, children: [
525
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(GearIcon, {}),
526
- "\u8BBE\u7F6E"
527
- ] }),
528
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "ah-btn", onClick: props.onOpenInbox, children: [
529
- "\u6536\u4EF6\u7BB1",
530
- o.inbox.total > 0 ? `\uFF08${o.inbox.total}\uFF09` : ""
531
- ] }),
532
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "ah-btn ah-btn-primary", onClick: props.onNewBot, children: "\u65B0\u5EFA\u961F\u53CB" })
533
- ] })
534
- ] });
535
- }
536
-
537
- // src/client/components/Ui.tsx
538
- var import_jsx_runtime2 = require("react/jsx-runtime");
539
- function Avatar(props) {
540
- const style = {
541
- width: `${props.size ?? 32}px`,
542
- height: `${props.size ?? 32}px`,
543
- background: props.color || "var(--dsw-alias-accent-muted)",
544
- color: "var(--dsw-alias-text-on-accent, currentColor)"
545
- };
546
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "ah-avatar", style, "aria-hidden": "true", children: props.initials });
547
- }
548
- function Skeleton(props) {
549
- const rows = props.rows ?? 3;
550
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "ah-skeleton", "aria-busy": "true", "aria-label": "\u52A0\u8F7D\u4E2D", children: Array.from({ length: rows }, (_, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "ah-skeleton-row" }, i)) });
551
- }
552
- function RetryBox(props) {
553
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "ah-retry", role: "alert", children: [
554
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: props.message }),
555
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "ah-btn", onClick: props.onRetry, children: "\u91CD\u8BD5" })
556
- ] });
557
- }
558
- function EmptyHint(props) {
559
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "ah-empty", children: props.text });
560
- }
561
- function UnreadBadge(props) {
562
- if (props.count <= 0) return null;
563
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "ah-unread", "aria-label": `${props.count} \u6761\u672A\u8BFB`, children: props.count > 99 ? "99+" : props.count });
564
- }
565
-
566
- // src/client/components/BotList.tsx
567
- var import_jsx_runtime3 = require("react/jsx-runtime");
568
- function BotRow(props) {
569
- const b = props.bot;
570
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("button", { type: "button", className: `ah-bot-row${props.selected ? " ah-selected" : ""}`, onClick: () => props.onSelect(b.id), children: [
571
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Avatar, { color: b.color, initials: b.initials }),
572
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "ah-bot-row-main", children: [
573
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "ah-bot-row-name", children: [
574
- b.name,
575
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "ah-bot-row-role", children: b.roleLabel }),
576
- b.waitingOwner ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "ah-tag ah-tag-wait", children: "\u7B49\u4F60" }) : null
577
- ] }),
578
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "ah-bot-row-activity", title: b.activityText, children: b.activityText })
579
- ] }),
580
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(UnreadBadge, { count: b.unread })
581
- ] });
582
- }
583
- function BotList(props) {
584
- const threadOf = (botId) => botId;
585
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("nav", { className: "ah-botlist", "aria-label": "\u961F\u53CB\u5217\u8868", children: [
586
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
587
- "button",
588
- {
589
- type: "button",
590
- className: `ah-bot-row${props.selected === "team" ? " ah-selected" : ""}`,
591
- onClick: () => props.onSelect("team"),
592
- children: [
593
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "ah-avatar ah-avatar-team", "aria-hidden": "true", children: "\u961F" }),
594
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "ah-bot-row-main", children: [
595
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "ah-bot-row-name", children: "\u56E2\u961F" }),
596
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "ah-bot-row-activity", children: props.overview.team.lastMessageText ?? "\u673A\u5668\u4EBA\u7684\u4EA4\u63A5\u548C\u6C47\u62A5\u4F1A\u6284\u9001\u5230\u8FD9\u91CC" })
597
- ] }),
598
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(UnreadBadge, { count: props.overview.team.unread })
599
- ]
600
- }
601
- ),
602
- props.overview.bots.map((bot) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(BotRow, { bot, selected: props.selected === threadOf(bot.id), onSelect: (id) => props.onSelect(threadOf(id)) }, bot.id)),
603
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "ah-botlist-footer", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("button", { type: "button", className: "ah-inbox-entry", onClick: props.onOpenInbox, children: [
604
- "\u6536\u4EF6\u7BB1",
605
- props.overview.inbox.total > 0 ? `\uFF08${props.overview.inbox.total}\uFF09` : ""
606
- ] }) })
607
- ] });
608
- }
609
-
610
- // src/client/components/ThreadPane.tsx
611
- var import_react3 = require("react");
612
-
613
- // src/client/components/Cards.tsx
614
- var import_react2 = require("react");
615
-
616
- // src/client/logic/cards.ts
617
- function isApproveOption(option) {
618
- return option === "\u6279\u51C6";
619
- }
620
- function expiryMeaningOf(card) {
621
- if (card.expiresText === null || card.expiresText === "") return null;
622
- const at = card.expiresText.endsWith("\u540E") ? card.expiresText.slice(0, -1) : card.expiresText;
623
- if (card.type === "question") {
624
- const fallback = card.defaultOption ?? "\u9ED8\u8BA4\u9009\u9879";
625
- return `${at}\u524D\u672A\u7B54\u590D\u6309\u9ED8\u8BA4\u300C${fallback}\u300D\u5904\u7406`;
626
- }
627
- if (card.type === "approval") return `${at}\u524D\u6709\u6548\uFF0C\u8FC7\u671F\u89C6\u4E3A\u62D2\u7EDD`;
628
- if (card.type === "proposal") return `${at}\u524D\u672A\u7B54\u590D\u81EA\u52A8\u64A4\u56DE`;
629
- return card.expiresText;
630
- }
631
- function cardResultText(card) {
632
- if (card.status === "answered") return `\u4F60\u9009\u4E86\uFF1A${card.answer ?? "\u5DF2\u5904\u7406"}`;
633
- if (card.status === "expired") return `\u5DF2\u8FC7\u671F\uFF0C\u5DF2\u6309\u9ED8\u8BA4\uFF1A${card.answer ?? "\u9ED8\u8BA4\u9009\u9879"}`;
634
- if (card.status === "withdrawn") return "\u5DF2\u64A4\u56DE";
635
- if (card.status === "closed") return "\u5DF2\u5173\u95ED";
636
- return null;
637
- }
638
- function cardSubmission(card, option, note) {
639
- const picked = option.trim();
640
- if (picked === "") return { error: "\u56DE\u7B54\uFF1A\u4E0D\u80FD\u4E3A\u7A7A" };
641
- if (card.type === "approval") {
642
- if (card.approvalId === null) {
643
- return { name: "answerInbox", input: { id: card.inboxId, answer: picked } };
644
- }
645
- return { name: "decideApproval", input: { id: card.approvalId, approve: isApproveOption(picked), ...note !== void 0 && note !== "" ? { note } : {} } };
646
- }
647
- return { name: "answerInbox", input: { id: card.inboxId, answer: picked } };
648
- }
649
-
650
- // src/client/logic/thread.ts
651
- function isCenterMessage(m) {
652
- return m.kind === "system" || m.kind === "digest";
653
- }
654
- function sameGroup(a, b) {
655
- if (isCenterMessage(a) || isCenterMessage(b)) return false;
656
- return a.from === b.from;
657
- }
658
- function groupMessages(messages) {
659
- const out = [];
660
- for (let i = 0; i < messages.length; i += 1) {
661
- const message = messages[i];
662
- const prev = i > 0 ? messages[i - 1] : null;
663
- out.push({ message, showHeader: prev === null || !sameGroup(prev, message) });
664
- }
665
- return out;
666
- }
667
- function shouldStickToBottom(input) {
668
- if (input.prevLastId === null) return true;
669
- if (input.lastId === input.prevLastId) return false;
670
- return input.nearBottom;
671
- }
672
- function isSendEnter(event) {
673
- if (event.key !== "Enter") return false;
674
- if (event.isComposing === true) return false;
675
- if (event.keyCode === 229) return false;
676
- return true;
677
- }
678
- function openBotSessionWith(ctx, sessionId) {
679
- const c = ctx;
680
- const get = c?.get?.bind(c);
681
- if (typeof get !== "function") return "unavailable";
682
- const ws = get("uiWorkspace");
683
- const sessions = get("sessions");
684
- const workspaces = get("workspaces");
685
- if (!ws || !sessions || !workspaces) return "unavailable";
686
- let list, wsList;
687
- try {
688
- list = sessions.list?.getSnapshot?.();
689
- wsList = workspaces.list?.getSnapshot?.();
690
- } catch {
691
- return "unavailable";
692
- }
693
- if (wsList?.state === "error") return "unavailable";
694
- if (list?.phase === "pending" || wsList?.phase === "pending") return "loading";
695
- if (wsList?.archivedSessionIds?.includes(sessionId)) return "archived";
696
- if (list?.ids && !list.ids.includes(sessionId)) return "unavailable";
697
- try {
698
- ws.openSession(sessionId);
699
- } catch {
700
- return "unavailable";
701
- }
702
- return "opened";
703
- }
704
- var OPEN_SESSION_HINTS = {
705
- opened: null,
706
- loading: "\u4F1A\u8BDD\u5217\u8868\u8FD8\u5728\u52A0\u8F7D\uFF0C\u7A0D\u7B49\u4E00\u4E0B\u518D\u8BD5\u3002",
707
- archived: "\u5B83\u7684\u5DE5\u4F5C\u4F1A\u8BDD\u88AB\u5F52\u6863\u4E86\uFF0C\u5148\u5728\u4F1A\u8BDD\u5217\u8868\u91CC\u6062\u590D\u5B83\u3002",
708
- unavailable: "\u73B0\u5728\u6253\u4E0D\u5F00\u5B83\u7684\u5DE5\u4F5C\u4F1A\u8BDD\uFF1A\u53EF\u4EE5\u5728\u5DE6\u4FA7\u680F\u7684\u9879\u76EE\u91CC\u627E\u5B83\u7684\u4F1A\u8BDD\u3002"
709
- };
710
-
711
- // src/client/components/Cards.tsx
712
- var import_jsx_runtime4 = require("react/jsx-runtime");
713
- var CARD_TYPE_LABELS = {
714
- question: "\u63D0\u95EE",
715
- approval: "\u5BA1\u6279",
716
- proposal: "\u63D0\u8BAE",
717
- alert: "\u544A\u8B66"
718
- };
719
- function InboxCard(props) {
720
- const c = props.card;
721
- const [picked, setPicked] = (0, import_react2.useState)(null);
722
- const [error, setError] = (0, import_react2.useState)(null);
723
- const [freeText, setFreeText] = (0, import_react2.useState)("");
724
- const [note, setNote] = (0, import_react2.useState)("");
725
- const resultText = cardResultText(c);
726
- const answered = resultText !== null;
727
- const busy = props.busy || picked !== null;
728
- const pick = (option) => {
729
- setPicked(option);
730
- setError(null);
731
- void props.actions.onPick(c, option, c.type === "approval" ? note : void 0).then((failure) => {
732
- if (failure !== null) {
733
- setPicked(null);
734
- setError(failure);
735
- }
736
- }).catch((e) => {
737
- setPicked(null);
738
- setError(e instanceof Error ? e.message : String(e));
739
- });
740
- };
741
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: `ah-card ah-card-${c.type}${answered ? " ah-card-done" : ""}`, "data-answered": answered ? "true" : "false", children: [
742
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "ah-card-head", children: [
743
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "ah-card-type", children: CARD_TYPE_LABELS[c.type] }),
744
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "ah-card-title", children: c.title })
745
- ] }),
746
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "ah-card-body", children: c.body }),
747
- expiryMeaningOf(c) ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "ah-card-expire", children: expiryMeaningOf(c) }) : null,
748
- answered ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "ah-card-answer", children: resultText }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
749
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "ah-card-options", children: c.options.map((option) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
750
- "button",
751
- {
752
- type: "button",
753
- className: c.type === "approval" ? isApproveOption(option) ? "ah-btn ah-btn-primary" : "ah-btn ah-btn-danger" : option === c.defaultOption ? "ah-btn ah-btn-primary" : "ah-btn",
754
- disabled: busy,
755
- onClick: () => pick(option),
756
- children: [
757
- option,
758
- option === c.defaultOption && c.type !== "approval" ? "\uFF08\u9ED8\u8BA4\uFF09" : ""
759
- ]
760
- },
761
- option
762
- )) }),
763
- c.type === "approval" && picked === null ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
764
- "input",
765
- {
766
- className: "ah-card-note",
767
- value: note,
768
- placeholder: "\u9644\u4E00\u53E5\u8BDD\uFF08\u53EF\u4E0D\u586B\uFF09",
769
- disabled: busy,
770
- onChange: (e) => setNote(e.target.value)
771
- }
772
- ) : null,
773
- c.type === "question" && picked === null ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "ah-card-freetext", children: [
774
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
775
- "input",
776
- {
777
- value: freeText,
778
- placeholder: "\u6216\u8005\u5199\u4E00\u53E5\u4F60\u7684\u56DE\u7B54",
779
- disabled: busy,
780
- onChange: (e) => setFreeText(e.target.value),
781
- onKeyDown: (e) => {
782
- if (isSendEnter(e.nativeEvent) && freeText.trim() !== "") pick(freeText.trim());
783
- }
784
- }
785
- ),
786
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
787
- "button",
788
- {
789
- type: "button",
790
- className: "ah-btn",
791
- disabled: busy || freeText.trim() === "",
792
- onClick: () => pick(freeText.trim()),
793
- children: "\u53D1\u9001"
794
- }
795
- )
796
- ] }) : null,
797
- error ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "ah-card-error", role: "alert", children: error }) : null
798
- ] })
799
- ] });
800
- }
801
-
802
- // src/client/logic/lifecycle.ts
803
- function lifecycleButtons(lifecycle) {
804
- const buttons = [];
805
- if (lifecycle === "active" || lifecycle === "probation") buttons.push({ action: "pause", label: "\u6682\u505C" });
806
- if (lifecycle === "paused") buttons.push({ action: "resume", label: "\u6062\u590D" });
807
- if (lifecycle === "quarantined") buttons.push({ action: "release_quarantine", label: "\u89E3\u9664\u505C\u7528" });
808
- if (lifecycle === "probation") buttons.push({ action: "promote", label: "\u63D0\u524D\u8F6C\u6B63" });
809
- if (lifecycle !== "retired") buttons.push({ action: "retire", label: "\u9000\u5F79" });
810
- return buttons;
811
- }
812
- function pauseResumeAction(lifecycle) {
813
- if (lifecycle === "active" || lifecycle === "probation") return "pause";
814
- if (lifecycle === "paused") return "resume";
815
- return null;
816
- }
817
- function lifecycleAllTargets(bots, target) {
818
- const wanted = target === "paused" ? ["active", "probation"] : ["paused"];
819
- return bots.filter((bot) => wanted.includes(bot.lifecycle)).map((bot) => bot.id);
820
- }
821
-
822
- // src/client/components/ThreadPane.tsx
823
- var import_jsx_runtime5 = require("react/jsx-runtime");
824
- function Message(props) {
825
- const { m, showHeader } = props;
826
- if (isCenterMessage(m)) {
827
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "ah-msg ah-msg-center", children: [
828
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "ah-msg-time", children: m.timeText }),
829
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "ah-msg-bubble-center", children: m.text })
830
- ] });
831
- }
832
- if (m.card) {
833
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: `ah-msg${m.from === "owner" ? " ah-msg-mine" : ""}`, children: [
834
- m.from === "owner" || !showHeader ? null : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Avatar, { color: m.fromColor, initials: m.fromName.slice(0, 1), size: 28 }),
835
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "ah-msg-body", children: [
836
- m.from === "owner" || !showHeader ? null : /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "ah-msg-meta", children: [
837
- m.fromName,
838
- " \xB7 ",
839
- m.timeText
840
- ] }),
841
- m.from === "owner" && showHeader ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "ah-msg-meta", children: m.timeText }) : null,
842
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(InboxCard, { card: m.card, actions: props.actions, busy: props.busyInboxId === m.card.inboxId })
843
- ] })
844
- ] });
845
- }
846
- const mine = m.from === "owner";
847
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: `ah-msg${mine ? " ah-msg-mine" : ""}${m.kind === "report" ? " ah-msg-report" : ""}`, children: [
848
- mine || !showHeader ? null : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Avatar, { color: m.fromColor, initials: m.fromName.slice(0, 1), size: 28 }),
849
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "ah-msg-body", children: [
850
- mine || !showHeader ? null : /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "ah-msg-meta", children: [
851
- m.fromName,
852
- " \xB7 ",
853
- m.timeText
854
- ] }),
855
- mine && showHeader ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "ah-msg-meta", children: m.timeText }) : null,
856
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "ah-msg-bubble", children: m.text })
857
- ] })
858
- ] });
859
- }
860
- var NEAR_BOTTOM_PX = 120;
861
- var threadDrafts = /* @__PURE__ */ new Map();
862
- function readDraft(threadId) {
863
- return threadDrafts.get(threadId) ?? "";
864
- }
865
- function saveDraft(threadId, text) {
866
- if (text === "") threadDrafts.delete(threadId);
867
- else threadDrafts.set(threadId, text);
868
- }
869
- function ThreadPane(props) {
870
- const [draft, setDraft] = (0, import_react3.useState)(() => readDraft(props.thread?.threadId ?? ""));
871
- const [sending, setSending] = (0, import_react3.useState)(false);
872
- const [sendError, setSendError] = (0, import_react3.useState)(null);
873
- const [confirmInterrupt, setConfirmInterrupt] = (0, import_react3.useState)(false);
874
- const bottomRef = (0, import_react3.useRef)(null);
875
- const listRef = (0, import_react3.useRef)(null);
876
- const prevLastIdRef = (0, import_react3.useRef)(null);
877
- const prevHeightRef = (0, import_react3.useRef)(null);
878
- const messageCount = props.thread?.messages.length ?? 0;
879
- (0, import_react3.useLayoutEffect)(() => {
880
- const list = listRef.current;
881
- if (list !== null) prevHeightRef.current = list.scrollHeight;
882
- }, [messageCount]);
883
- const lastId = props.thread?.messages.at(-1)?.id ?? null;
884
- (0, import_react3.useEffect)(() => {
885
- const list = listRef.current;
886
- const prevLastId = prevLastIdRef.current;
887
- prevLastIdRef.current = lastId;
888
- if (list === null) return;
889
- if (lastId !== null && prevLastId !== null && lastId === prevLastId) {
890
- const prevHeight = prevHeightRef.current;
891
- prevHeightRef.current = list.scrollHeight;
892
- if (prevHeight !== null && list.scrollHeight > prevHeight) list.scrollTop += list.scrollHeight - prevHeight;
893
- return;
894
- }
895
- const nearBottom = list.scrollHeight - list.scrollTop - list.clientHeight < NEAR_BOTTOM_PX;
896
- if (shouldStickToBottom({ prevLastId, lastId, nearBottom })) {
897
- bottomRef.current?.scrollIntoView({ block: "end" });
898
- }
899
- }, [lastId, messageCount]);
900
- const send = async () => {
901
- const text = draft.trim();
902
- if (!text || sending) return;
903
- setSending(true);
904
- setSendError(null);
905
- try {
906
- await props.onSend(text);
907
- saveDraft(props.thread?.threadId ?? "", "");
908
- setDraft("");
909
- } catch (error) {
910
- setSendError(error instanceof Error ? error.message : String(error) || "\u53D1\u9001\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5");
911
- } finally {
912
- setSending(false);
913
- }
914
- };
915
- const h = props.header;
916
- if (props.error) {
917
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "ah-thread", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "ah-retry", role: "alert", children: [
918
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: props.error }),
919
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", className: "ah-btn", onClick: props.onRetry, children: "\u91CD\u8BD5" })
920
- ] }) });
921
- }
922
- if (!props.thread) {
923
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "ah-thread", children: props.loading ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Skeleton, { rows: 5 }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(EmptyHint, { text: "\u4ECE\u5DE6\u8FB9\u9009\u4E00\u4E2A\u961F\u53CB\u5F00\u59CB\u804A\uFF1B\u5B83\u4EEC\u7684\u6C47\u62A5\u548C\u63D0\u95EE\u90FD\u4F1A\u51FA\u73B0\u5728\u5BF9\u5E94\u7EBF\u7A0B\u91CC\u3002" }) });
924
- }
925
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("section", { className: "ah-thread", "aria-label": `\u4E0E ${h?.name ?? props.thread.title} \u7684\u5BF9\u8BDD`, children: [
926
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("header", { className: "ah-thread-header", children: [
927
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "ah-thread-header-main", children: [
928
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("h2", { children: [
929
- h?.name ?? props.thread.title,
930
- h?.roleLabel ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "ah-bot-row-role", children: h.roleLabel }) : null
931
- ] }),
932
- h?.activityText ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "ah-thread-status", title: h.activityText, children: h.activityText }) : null,
933
- h?.nextThinkText ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("p", { className: "ah-thread-next", children: [
934
- "\u4E0B\u6B21\u81EA\u67E5\uFF1A",
935
- h.nextThinkText
936
- ] }) : null
937
- ] }),
938
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "ah-thread-header-actions", children: [
939
- props.thread.hasMore ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", className: "ah-btn ah-btn-ghost", onClick: props.onLoadEarlier, children: "\u52A0\u8F7D\u66F4\u65E9" }) : null,
940
- h?.thinking ? confirmInterrupt ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
941
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", className: "ah-btn ah-btn-danger", onClick: () => {
942
- setConfirmInterrupt(false);
943
- props.onInterrupt();
944
- }, children: "\u786E\u8BA4\u6253\u65AD" }),
945
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", className: "ah-btn ah-btn-ghost", onClick: () => setConfirmInterrupt(false), children: "\u518D\u60F3\u60F3" })
946
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", className: "ah-btn ah-btn-danger", onClick: () => setConfirmInterrupt(true), children: "\u6253\u65AD" }) : null,
947
- h && h.lifecycle !== null ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
948
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", className: "ah-btn", onClick: props.onTeach, children: "\u6559\u5B83\u4E00\u62DB" }),
949
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", className: "ah-btn", onClick: props.onOpenSession, children: "\u6253\u5F00\u5DE5\u4F5C\u4F1A\u8BDD" }),
950
- props.rightCollapsed && props.onExpandRight ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", className: "ah-btn", onClick: props.onExpandRight, children: "\u8BE6\u60C5" }) : null,
951
- pauseResumeAction(h.lifecycle) !== null ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", className: "ah-btn", onClick: props.onTogglePause, children: pauseResumeAction(h.lifecycle) === "pause" ? "\u6682\u505C" : "\u6062\u590D" }) : null
952
- ] }) : null
953
- ] })
954
- ] }),
955
- props.banner ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "ah-thread-banner", role: "status", children: [
956
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: props.banner }),
957
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", className: "ah-btn ah-btn-ghost", onClick: props.onBannerDismiss, children: "\u77E5\u9053\u4E86" })
958
- ] }) : null,
959
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "ah-thread-messages", ref: listRef, children: [
960
- groupMessages(props.thread.messages).map((slot) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
961
- Message,
962
- {
963
- m: slot.message,
964
- showHeader: slot.showHeader,
965
- actions: props.actions,
966
- busyInboxId: props.busyInboxId
967
- },
968
- slot.message.id
969
- )),
970
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { ref: bottomRef })
971
- ] }),
972
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("footer", { className: "ah-thread-input", children: [
973
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
974
- "input",
975
- {
976
- value: draft,
977
- placeholder: `\u7ED9 ${props.inputPlaceholderName} \u53D1\u6D88\u606F\u2026`,
978
- disabled: sending,
979
- onChange: (e) => {
980
- setDraft(e.target.value);
981
- saveDraft(props.thread?.threadId ?? "", e.target.value);
982
- },
983
- onKeyDown: (e) => {
984
- if (isSendEnter(e.nativeEvent)) void send();
985
- }
986
- }
987
- ),
988
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", className: "ah-btn ah-btn-primary", disabled: sending || draft.trim() === "", onClick: () => void send(), children: sending ? "\u53D1\u9001\u4E2D\u2026" : "\u53D1\u9001" })
989
- ] }),
990
- sendError ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "ah-thread-senderror", role: "alert", children: [
991
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { children: [
992
- "\u6CA1\u53D1\u51FA\u53BB\uFF1A",
993
- sendError
994
- ] }),
995
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", className: "ah-btn", onClick: () => void send(), children: "\u91CD\u8BD5" })
996
- ] }) : null
997
- ] });
998
- }
999
-
1000
- // src/client/components/RightPanel.tsx
1001
- var import_react4 = require("react");
1002
-
1003
- // src/client/logic/settings.ts
1004
- function validateClockTime(value, field) {
1005
- if (!/^([01]\d|2[0-3]):[0-5]\d$/.test(value.trim())) return `${field}\uFF1A\u8981\u5199\u6210 HH:mm\uFF08\u4F8B\u5982 23:00\uFF09`;
1006
- return null;
1007
- }
1008
- function draftFromSettings(settings) {
1009
- return {
1010
- quietStart: settings.quietHours.start,
1011
- quietEnd: settings.quietHours.end,
1012
- digestAt: settings.digestAt,
1013
- pushChannel: settings.pushChannel,
1014
- dailyPushCap: String(settings.dailyPushCap),
1015
- botDailyPushCap: String(settings.botDailyPushCap),
1016
- defaultModel: settings.defaultModel === null ? "" : JSON.stringify(settings.defaultModel)
1017
- };
1018
- }
1019
- function validateSettingsDraft(draft) {
1020
- const start = validateClockTime(draft.quietStart, "\u514D\u6253\u6270\u5F00\u59CB");
1021
- if (start !== null) return start;
1022
- const end = validateClockTime(draft.quietEnd, "\u514D\u6253\u6270\u7ED3\u675F");
1023
- if (end !== null) return end;
1024
- const digest = validateClockTime(draft.digestAt, "\u65E5\u62A5\u65F6\u95F4");
1025
- if (digest !== null) return digest;
1026
- if (!["dsh", "macos", "none"].includes(draft.pushChannel)) return "\u63A8\u9001\u6E20\u9053\uFF1A\u53EA\u80FD\u9009 macOS \u901A\u77E5 / \u4E0D\u63A8 / \u5BBF\u4E3B";
1027
- const whole = Number(draft.dailyPushCap);
1028
- if (!Number.isInteger(whole) || whole < 0 || whole > 10) return "\u6BCF\u65E5\u5355\u63A8\u4E0A\u9650\uFF08\u5168\u4F53\uFF09\uFF1A0 \u5230 10 \u7684\u6574\u6570";
1029
- const perBot = Number(draft.botDailyPushCap);
1030
- if (!Number.isInteger(perBot) || perBot < 0 || perBot > 3) return "\u6BCF\u65E5\u5355\u63A8\u4E0A\u9650\uFF08\u6BCF\u4E2A\u961F\u53CB\uFF09\uFF1A0 \u5230 3 \u7684\u6574\u6570";
1031
- if (perBot > whole) return "\u6BCF\u4E2A\u961F\u53CB\u7684\u5355\u63A8\u4E0A\u9650\u4E0D\u80FD\u8D85\u8FC7\u5168\u4F53\u4E0A\u9650";
1032
- return null;
1033
- }
1034
- function modelSelectOptions(current, options) {
1035
- const list = [...options];
1036
- if (current == null) return list;
1037
- const present = list.some((option) => JSON.stringify(option.value) === JSON.stringify(current));
1038
- if (!present) {
1039
- list.push({ value: current, label: `${current.provider} / ${current.model}\uFF08\u5DF2\u4E0D\u5728\u5BBF\u4E3B\u76EE\u5F55\uFF0C\u8BF7\u6362\u4E00\u4E2A\uFF09` });
1040
- }
1041
- return list;
1042
- }
1043
- function buildSettingsPatch(draft, loaded) {
1044
- const patch = {
1045
- quietHours: { start: draft.quietStart.trim(), end: draft.quietEnd.trim() },
1046
- digestAt: draft.digestAt.trim(),
1047
- pushChannel: draft.pushChannel,
1048
- dailyPushCap: Number(draft.dailyPushCap),
1049
- botDailyPushCap: Number(draft.botDailyPushCap)
1050
- };
1051
- if (loaded === void 0 || loaded.defaultModel !== draft.defaultModel) {
1052
- if (draft.defaultModel === "") patch.defaultModel = null;
1053
- else {
1054
- try {
1055
- patch.defaultModel = JSON.parse(draft.defaultModel);
1056
- } catch {
1057
- }
1058
- }
1059
- }
1060
- return patch;
1061
- }
1062
-
1063
- // src/client/components/RightPanel.tsx
1064
- var import_jsx_runtime6 = require("react/jsx-runtime");
1065
- var AUTONOMY_CHOICES = [
1066
- { value: "L0", label: "L0 \u53EA\u89C2\u5BDF" },
1067
- { value: "L1", label: "L1 \u5EFA\u8BAE" },
1068
- { value: "L2", label: "L2 \u6267\u884C" },
1069
- { value: "L3", label: "L3 \u6388\u6743\u6267\u884C" }
1070
- ];
1071
- var PACE_CHOICES = [
1072
- { value: "active", label: "\u79EF\u6781" },
1073
- { value: "normal", label: "\u9002\u4E2D" },
1074
- { value: "quiet", label: "\u5B89\u9759" }
1075
- ];
1076
- var LIFECYCLE_OK_TEXT = {
1077
- pause: "\u5DF2\u6682\u505C\uFF08\u671F\u95F4\u7684\u53D8\u5316\u90FD\u5B58\u7740\uFF09",
1078
- resume: "\u5DF2\u6062\u590D",
1079
- promote: "\u5DF2\u63D0\u524D\u8F6C\u6B63",
1080
- release_quarantine: "\u5DF2\u89E3\u9664\u505C\u7528",
1081
- retire: "\u5DF2\u9000\u5F79\uFF08\u6570\u636E\u4FDD\u7559\uFF09"
1082
- };
1083
- function watchStatusInput(botId, w, status) {
1084
- return { id: w.id, botId, kind: w.kind, target: w.target, why: w.why ?? "", status };
1085
- }
1086
- function Section(props) {
1087
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("section", { className: "ah-right-section", children: [
1088
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("h3", { children: props.title }),
1089
- props.children
1090
- ] });
1091
- }
1092
- function useRunner(run) {
1093
- const [busy, setBusy] = (0, import_react4.useState)(false);
1094
- const [message, setMessage] = (0, import_react4.useState)(null);
1095
- const exec = async (name, input, okText) => {
1096
- if (busy) return false;
1097
- setBusy(true);
1098
- try {
1099
- const result = await run(name, input);
1100
- setMessage(result.ok ? okText : result.error);
1101
- return result.ok;
1102
- } catch (e) {
1103
- setMessage(e instanceof Error ? e.message : String(e));
1104
- return false;
1105
- } finally {
1106
- setBusy(false);
1107
- }
1108
- };
1109
- return { busy, message, exec, setMessage };
1110
- }
1111
- function RightPanel(props) {
1112
- const runner = useRunner(props.run);
1113
- const [editingProfile, setEditingProfile] = (0, import_react4.useState)(false);
1114
- const [editingAspiration, setEditingAspiration] = (0, import_react4.useState)(null);
1115
- const [persona, setPersona] = (0, import_react4.useState)("");
1116
- const [newAspiration, setNewAspiration] = (0, import_react4.useState)("");
1117
- const [newRule, setNewRule] = (0, import_react4.useState)("");
1118
- const [newRuleRedLine, setNewRuleRedLine] = (0, import_react4.useState)(false);
1119
- const [editingRoutine, setEditingRoutine] = (0, import_react4.useState)(null);
1120
- const [routineSteps, setRoutineSteps] = (0, import_react4.useState)("");
1121
- const [confirmRetire, setConfirmRetire] = (0, import_react4.useState)(false);
1122
- const [notice, setNotice] = (0, import_react4.useState)(null);
1123
- if (!props.bot) {
1124
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("aside", { className: "ah-right", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "ah-empty", children: "\u4ECE\u5DE6\u8FB9\u9009\u4E00\u4E2A\u961F\u53CB\uFF0C\u8FD9\u91CC\u663E\u793A\u5B83\u7684\u6863\u6848\u3001\u76EE\u6807\u3001\u8BAE\u7A0B\u548C\u300C\u4E3A\u4EC0\u4E48\u9192 / \u4E3A\u4EC0\u4E48\u6CA1\u9192\u300D\u3002" }) });
1125
- }
1126
- const b = props.bot;
1127
- const doExec = async (name, input, okText) => {
1128
- const ok = await runner.exec(name, input, okText);
1129
- if (ok) props.onRefresh();
1130
- };
1131
- const openSession = () => {
1132
- if (!b.profile.sessionId) {
1133
- setNotice("\u5B83\u8FD8\u6CA1\u6709\u5DE5\u4F5C\u4F1A\u8BDD\u3002");
1134
- return;
1135
- }
1136
- const result = openBotSessionWith(props.clientCtx, b.profile.sessionId);
1137
- setNotice(OPEN_SESSION_HINTS[result]);
1138
- };
1139
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("aside", { className: "ah-right", "aria-label": `${b.card.name} \u7684\u8BE6\u60C5`, children: [
1140
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "ah-right-head", children: [
1141
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("h2", { children: [
1142
- b.card.name,
1143
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "ah-bot-row-role", children: b.card.roleLabel })
1144
- ] }),
1145
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: "ah-btn ah-btn-ghost", onClick: props.onCollapse, "aria-label": "\u6536\u8D77\u53F3\u680F", children: "\u6536\u8D77" })
1146
- ] }),
1147
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "ah-right-scroll", children: [
1148
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Section, { title: "\u6863\u6848", children: editingProfile ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "ah-edit-box", children: [
1149
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { children: [
1150
- "\u6027\u683C\u4E0E\u8BF4\u8BDD\u98CE\u683C",
1151
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("textarea", { rows: 3, value: persona, onChange: (e) => setPersona(e.target.value) })
1152
- ] }),
1153
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { children: [
1154
- "\u81EA\u4E3B\u7B49\u7EA7",
1155
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("select", { value: b.profile.autonomy, onChange: (e) => {
1156
- void doExec("updateBot", { botId: b.card.id, patch: { autonomy: e.target.value } }, "\u5DF2\u6539\u81EA\u4E3B\u7B49\u7EA7\uFF08\u4E0B\u4E00\u6B21\u601D\u8003\u751F\u6548\uFF09");
1157
- }, children: AUTONOMY_CHOICES.map((a) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: a.value, children: a.label }, a.value)) })
1158
- ] }),
1159
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { children: [
1160
- "\u8282\u594F",
1161
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("select", { value: b.profile.pace, onChange: (e) => {
1162
- void doExec("updateBot", { botId: b.card.id, patch: { pace: e.target.value } }, "\u5DF2\u6539\u8282\u594F");
1163
- }, children: PACE_CHOICES.map((p) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: p.value, children: p.label }, p.value)) })
1164
- ] }),
1165
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { children: [
1166
- "\u6A21\u578B",
1167
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1168
- "select",
1169
- {
1170
- value: b.profile.model === null ? "" : JSON.stringify(b.profile.model),
1171
- onChange: (e) => {
1172
- void doExec("updateBot", { botId: b.card.id, patch: { model: e.target.value === "" ? null : JSON.parse(e.target.value) } }, "\u5DF2\u6539\u6A21\u578B");
1173
- },
1174
- children: [
1175
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: "", children: "\u8DDF\u968F DSH \u9ED8\u8BA4" }),
1176
- modelSelectOptions(b.profile.model ?? void 0, props.modelOptions.filter((option) => option.value !== null)).map((option, i) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: JSON.stringify(option.value) ?? "", children: option.label }, i))
1177
- ]
1178
- }
1179
- ),
1180
- props.modelOptionsError ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "ah-right-kv ah-card-error", children: [
1181
- props.modelOptionsError,
1182
- props.onRetrySettings ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
1183
- "\u3000",
1184
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: "ah-btn ah-btn-ghost", onClick: props.onRetrySettings, children: "\u91CD\u8BD5" })
1185
- ] }) : null
1186
- ] }) : null
1187
- ] }),
1188
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "ah-settings-actions", children: [
1189
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1190
- "button",
1191
- {
1192
- type: "button",
1193
- className: "ah-btn ah-btn-primary",
1194
- disabled: runner.busy,
1195
- onClick: () => {
1196
- void doExec("updateBot", { botId: b.card.id, patch: { persona } }, "\u5DF2\u4FDD\u5B58\u6027\u683C");
1197
- setEditingProfile(false);
1198
- },
1199
- children: "\u4FDD\u5B58"
1200
- }
1201
- ),
1202
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: "ah-btn", onClick: () => setEditingProfile(false), children: "\u53D6\u6D88" })
1203
- ] })
1204
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
1205
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { children: b.profile.persona === "" ? "\uFF08\u8FD8\u6CA1\u5199\u8FC7\u6027\u683C\u8BF4\u660E\uFF09" : b.profile.persona }),
1206
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("p", { className: "ah-right-kv", children: [
1207
- "\u81EA\u4E3B\u7B49\u7EA7\uFF1A",
1208
- b.profile.autonomyLabel,
1209
- " \xB7 \u8282\u594F\uFF1A",
1210
- b.profile.paceLabel,
1211
- " \xB7 \u6A21\u578B\uFF1A",
1212
- b.profile.modelText
1213
- ] }),
1214
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("p", { className: "ah-right-kv", children: [
1215
- b.profile.createdText,
1216
- " \u4E0A\u5C97",
1217
- b.profile.probationText ? ` \xB7 ${b.profile.probationText}` : ""
1218
- ] }),
1219
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: "ah-btn ah-btn-ghost", onClick: () => {
1220
- setPersona(b.profile.persona);
1221
- setEditingProfile(true);
1222
- }, children: "\u7F16\u8F91\u6863\u6848" })
1223
- ] }) }),
1224
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(Section, { title: "\u5FD7\u5411", children: [
1225
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("ul", { className: "ah-edit-list", children: b.aspirations.map((a) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("li", { children: editingAspiration === a.id ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "ah-edit-ops", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1226
- "input",
1227
- {
1228
- "aria-label": "\u6539\u5FD7\u5411",
1229
- defaultValue: a.text,
1230
- autoFocus: true,
1231
- onBlur: (e) => {
1232
- const text = e.target.value.trim();
1233
- if (text !== "" && text !== a.text) void doExec("upsertAspiration", { id: a.id, botId: b.card.id, text }, "\u5DF2\u6539\u5FD7\u5411");
1234
- setEditingAspiration(null);
1235
- }
1236
- }
1237
- ) }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "ah-edit-row", children: [
1238
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { children: a.text }),
1239
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "ah-edit-ops", children: [
1240
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: "ah-btn ah-btn-ghost", onClick: () => setEditingAspiration(a.id), children: "\u6539" }),
1241
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: "ah-btn ah-btn-ghost", onClick: () => void doExec("upsertAspiration", { id: a.id, botId: b.card.id, text: a.text, status: "archived" }, "\u5DF2\u6536\u8D77\u5FD7\u5411"), children: "\u6536\u8D77" })
1242
- ] })
1243
- ] }) }, a.id)) }),
1244
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "ah-edit-add", children: [
1245
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1246
- "input",
1247
- {
1248
- value: newAspiration,
1249
- placeholder: "\u52A0\u4E00\u6761\u5FD7\u5411",
1250
- onChange: (e) => setNewAspiration(e.target.value),
1251
- onKeyDown: (e) => {
1252
- if (e.key === "Enter" && newAspiration.trim() !== "") {
1253
- void doExec("upsertAspiration", { botId: b.card.id, text: newAspiration.trim() }, "\u5DF2\u52A0\u5FD7\u5411");
1254
- setNewAspiration("");
1255
- }
1256
- }
1257
- }
1258
- ),
1259
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1260
- "button",
1261
- {
1262
- type: "button",
1263
- className: "ah-btn",
1264
- disabled: newAspiration.trim() === "" || runner.busy,
1265
- onClick: () => {
1266
- void doExec("upsertAspiration", { botId: b.card.id, text: newAspiration.trim() }, "\u5DF2\u52A0\u5FD7\u5411");
1267
- setNewAspiration("");
1268
- },
1269
- children: "\u52A0\u4E00\u6761"
1270
- }
1271
- )
1272
- ] })
1273
- ] }),
1274
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Section, { title: "\u76EE\u6807", children: b.goals.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "ah-empty", children: "\u8FD8\u6CA1\u6709\u76EE\u6807\u3002\u53EF\u4EE5\u5728\u5FD7\u5411\u4E0B\u5EFA\u4E00\u4E2A\u3002" }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("ul", { className: "ah-edit-list", children: b.goals.map((g) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("li", { children: [
1275
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { children: [
1276
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("strong", { children: g.title }),
1277
- "\uFF08",
1278
- g.periodText,
1279
- "\uFF09",
1280
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("br", {}),
1281
- g.statusLabel,
1282
- g.lastProgressText ? ` \xB7 ${g.lastProgressText}` : ""
1283
- ] }),
1284
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "ah-edit-ops", children: g.status === "done_pending_check" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
1285
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: "ah-btn ah-btn-primary", disabled: runner.busy, onClick: () => void doExec("closeGoal", { id: g.id, outcome: true }, "\u5DF2\u9A8C\u6536\u901A\u8FC7"), children: "\u9A8C\u6536\u901A\u8FC7" }),
1286
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: "ah-btn", disabled: runner.busy, onClick: () => void doExec("closeGoal", { id: g.id, outcome: false }, "\u5DF2\u8BB0\u9A8C\u6536\u6CA1\u8FC7"), children: "\u6CA1\u8FC7" })
1287
- ] }) : g.status === "active" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: "ah-btn ah-btn-ghost", disabled: runner.busy, onClick: () => void doExec("closeGoal", { id: g.id, outcome: "abandoned" }, "\u5DF2\u653E\u5F03\u76EE\u6807"), children: "\u653E\u5F03" }) : null })
1288
- ] }, g.id)) }) }),
1289
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Section, { title: "\u5173\u6CE8\u9879", children: b.watches.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "ah-empty", children: "\u5B83\u8FD8\u6CA1\u6709\u5728\u76EF\u7684\u4E1C\u897F\u3002" }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("ul", { className: "ah-edit-list", children: b.watches.map((w) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("li", { children: [
1290
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { children: [
1291
- w.kindLabel,
1292
- "\uFF1A",
1293
- w.target,
1294
- w.status === "proposed" ? "\uFF08\u5B83\u63D0\u8BAE\u7684\uFF0C\u7B49\u4F60\u70B9\u5934\uFF09" : w.status === "paused" ? "\uFF08\u5DF2\u6682\u505C\uFF09" : ""
1295
- ] }),
1296
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "ah-edit-ops", children: w.status === "proposed" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
1297
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: "ah-btn ah-btn-primary", disabled: runner.busy, onClick: () => void doExec("decideWatchProposal", { id: w.id, approve: true }, "\u5DF2\u540C\u610F\u5B83\u7684\u63D0\u8BAE"), children: "\u540C\u610F" }),
1298
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: "ah-btn", disabled: runner.busy, onClick: () => void doExec("decideWatchProposal", { id: w.id, approve: false }, "\u5DF2\u62D2\u7EDD\u5B83\u7684\u63D0\u8BAE"), children: "\u4E0D\u8981" })
1299
- ] }) : w.status === "active" || w.status === "paused" ? (
1300
- /* 暂停/恢复要拼完整入参(D07 第 10 条):kind/target/why 来自该行已有字段,
1301
- 否则 core 会报「关注项无效」。 */
1302
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1303
- "button",
1304
- {
1305
- type: "button",
1306
- className: "ah-btn ah-btn-ghost",
1307
- disabled: runner.busy,
1308
- onClick: () => void doExec("upsertWatch", watchStatusInput(b.card.id, w, w.status === "active" ? "paused" : "active"), w.status === "active" ? "\u5DF2\u6682\u505C" : "\u5DF2\u6062\u590D"),
1309
- children: w.status === "active" ? "\u6682\u505C" : "\u6062\u590D"
1310
- }
1311
- )
1312
- ) : null })
1313
- ] }, w.id)) }) }),
1314
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(Section, { title: "\u89C4\u77E9", children: [
1315
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("ul", { className: "ah-edit-list", children: b.rules.map((r) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("li", { children: [
1316
- r.redLine ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "ah-tag ah-tag-wait", children: "\u7EA2\u7EBF" }) : null,
1317
- r.text
1318
- ] }, r.id)) }),
1319
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "ah-edit-add", children: [
1320
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("input", { value: newRule, placeholder: "\u52A0\u4E00\u6761\u89C4\u77E9", onChange: (e) => setNewRule(e.target.value) }),
1321
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { className: "ah-right-kv", children: [
1322
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("input", { type: "checkbox", checked: newRuleRedLine, onChange: (e) => setNewRuleRedLine(e.target.checked) }),
1323
- "\u7EA2\u7EBF"
1324
- ] }),
1325
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1326
- "button",
1327
- {
1328
- type: "button",
1329
- className: "ah-btn",
1330
- disabled: newRule.trim() === "" || runner.busy,
1331
- onClick: () => {
1332
- void doExec("upsertRule", { scope: b.card.id, text: newRule.trim(), redLine: newRuleRedLine }, "\u5DF2\u52A0\u89C4\u77E9");
1333
- setNewRule("");
1334
- setNewRuleRedLine(false);
1335
- },
1336
- children: "\u52A0\u4E00\u6761"
1337
- }
1338
- )
1339
- ] })
1340
- ] }),
1341
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Section, { title: "\u4F1A\u7684\u5957\u8DEF", children: b.routines.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "ah-empty", children: "\u8FD8\u6CA1\u5B66\u4F1A\u5957\u8DEF\u3002\u53EF\u4EE5\u300C\u6559\u5B83\u4E00\u62DB\u300D\u3002" }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("ul", { className: "ah-edit-list", children: b.routines.map((r) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("li", { children: [
1342
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { children: [
1343
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("strong", { children: r.name }),
1344
- "\uFF1A",
1345
- r.whenToUse,
1346
- "\uFF08\u7528\u8FC7 ",
1347
- r.runs,
1348
- " \u6B21\uFF09"
1349
- ] }),
1350
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "ah-edit-ops", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: "ah-btn ah-btn-ghost", onClick: () => {
1351
- setEditingRoutine(editingRoutine === r.id ? null : r.id);
1352
- setRoutineSteps(r.steps.join("\n"));
1353
- }, children: editingRoutine === r.id ? "\u6536\u8D77" : "\u67E5\u770B / \u7F16\u8F91" }) }),
1354
- editingRoutine === r.id ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "ah-edit-box", children: [
1355
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("ol", { className: "ah-routine-steps", children: r.steps.map((step, i) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("li", { children: step }, i)) }),
1356
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("textarea", { rows: 4, value: routineSteps, onChange: (e) => setRoutineSteps(e.target.value), placeholder: "\u4E00\u884C\u4E00\u6B65" }),
1357
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1358
- "button",
1359
- {
1360
- type: "button",
1361
- className: "ah-btn ah-btn-primary",
1362
- disabled: runner.busy,
1363
- onClick: () => {
1364
- void doExec("editRoutine", { id: r.id, patch: { steps: routineSteps.split(/\r?\n/).map((s) => s.trim()).filter((s) => s !== "") } }, "\u5957\u8DEF\u5DF2\u66F4\u65B0");
1365
- setEditingRoutine(null);
1366
- },
1367
- children: "\u4FDD\u5B58\u6B65\u9AA4"
1368
- }
1369
- )
1370
- ] }) : null
1371
- ] }, r.id)) }) }),
1372
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Section, { title: "\u4E3A\u4EC0\u4E48\u9192 / \u4E3A\u4EC0\u4E48\u6CA1\u9192\uFF08\u6700\u8FD1 8 \u6761\uFF09", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("ul", { className: "ah-appraisals", children: [...b.appraisals].slice(0, 8).map((a) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("li", { children: [
1373
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: `ah-appraisal-decision${a.decision === "woke" ? " ah-appraisal-woke" : ""}`, children: a.decisionLabel }),
1374
- a.text,
1375
- "\uFF08",
1376
- a.timeText,
1377
- "\uFF09"
1378
- ] }, a.id)) }) })
1379
- ] }),
1380
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "ah-right-actions", children: [
1381
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: "ah-btn", onClick: props.onTeach, children: "\u6559\u5B83\u4E00\u62DB" }),
1382
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: "ah-btn", onClick: openSession, children: "\u6253\u5F00\u5DE5\u4F5C\u4F1A\u8BDD" }),
1383
- lifecycleButtons(b.card.lifecycle).filter((button) => button.action !== "retire").map((button) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1384
- "button",
1385
- {
1386
- type: "button",
1387
- className: "ah-btn",
1388
- disabled: runner.busy,
1389
- onClick: () => void doExec("lifecycleAction", { botId: b.card.id, action: button.action }, LIFECYCLE_OK_TEXT[button.action]),
1390
- children: button.label
1391
- },
1392
- button.action
1393
- )),
1394
- b.card.lifecycle !== "retired" ? confirmRetire ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
1395
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1396
- "button",
1397
- {
1398
- type: "button",
1399
- className: "ah-btn ah-btn-danger",
1400
- disabled: runner.busy,
1401
- onClick: () => {
1402
- void doExec("lifecycleAction", { botId: b.card.id, action: "retire" }, "\u5DF2\u9000\u5F79\uFF08\u6570\u636E\u4FDD\u7559\uFF09");
1403
- setConfirmRetire(false);
1404
- },
1405
- children: "\u786E\u8BA4\u9000\u5F79"
1406
- }
1407
- ),
1408
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: "ah-btn ah-btn-ghost", onClick: () => setConfirmRetire(false), children: "\u518D\u60F3\u60F3" })
1409
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: "ah-btn ah-btn-ghost", onClick: () => setConfirmRetire(true), children: "\u9000\u5F79" }) : null,
1410
- notice ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "ah-right-notice", children: notice }) : null,
1411
- runner.message ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "ah-right-notice", children: runner.message }) : null
1412
- ] })
1413
- ] });
1414
- }
1415
-
1416
- // src/client/components/InboxPane.tsx
1417
- var import_react5 = require("react");
1418
- var import_jsx_runtime7 = require("react/jsx-runtime");
1419
- function itemToCard(item) {
1420
- return {
1421
- inboxId: item.id,
1422
- approvalId: item.approvalId,
1423
- type: item.type,
1424
- status: "open",
1425
- title: item.title,
1426
- body: item.body,
1427
- options: item.options,
1428
- defaultOption: item.defaultOption,
1429
- answer: null,
1430
- expiresText: item.expiresText
1431
- };
1432
- }
1433
- function InboxPane(props) {
1434
- const [, force] = (0, import_react5.useState)(0);
1435
- if (props.error) {
1436
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "ah-inbox", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "ah-retry", role: "alert", children: [
1437
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children: props.error }),
1438
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { type: "button", className: "ah-btn", onClick: props.onRetry, children: "\u91CD\u8BD5" })
1439
- ] }) });
1440
- }
1441
- if (!props.inbox) return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "ah-inbox", children: props.loading ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Skeleton, { rows: 3 }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(EmptyHint, { text: "\u6536\u4EF6\u7BB1\u8FD8\u6CA1\u6253\u5F00\u3002\u961F\u53CB\u9700\u8981\u4F60\u62CD\u677F\u7684\u4E8B\u4F1A\u51FA\u73B0\u5728\u8FD9\u91CC\u3002" }) });
1442
- const groups = groupInbox(props.inbox.items);
1443
- if (groups.length === 0) {
1444
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "ah-inbox", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(EmptyHint, { text: "\u6CA1\u6709\u8981\u4F60\u5904\u7406\u7684\u4E8B\u3002\u961F\u53CB\u4EEC\u4F1A\u5728\u9700\u8981\u4F60\u62CD\u677F\u65F6\u51FA\u73B0\u5728\u8FD9\u91CC\u3002" }) });
1445
- }
1446
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("section", { className: "ah-inbox", "aria-label": "\u6536\u4EF6\u7BB1", "data-testid": "ah-inbox", children: groups.map((group) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: `ah-inbox-group ah-inbox-group-${group.priority.toLowerCase()}`, children: [
1447
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("h3", { children: [
1448
- group.label,
1449
- "\uFF08",
1450
- group.items.length,
1451
- "\uFF09"
1452
- ] }),
1453
- group.items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("article", { className: `ah-inbox-row ah-inbox-${item.priority.toLowerCase()}`, children: [
1454
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("header", { children: [
1455
- item.botName ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Avatar, { color: item.botColor, initials: item.botName.slice(0, 1), size: 24 }) : null,
1456
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "ah-inbox-bot", children: item.botName ?? "\u4E2D\u5FC3" }),
1457
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: "ah-inbox-time", children: [
1458
- item.createdText,
1459
- expiryMeaningOf(itemToCard(item)) ? ` \xB7 ${expiryMeaningOf(itemToCard(item))}` : ""
1460
- ] })
1461
- ] }),
1462
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(InboxCard, { card: itemToCard(item), actions: {
1463
- ...props.actions,
1464
- // 本卡处理中禁用;处理完由 SSE 刷新把项移出列表。这里 force 触发本行重渲染。
1465
- onPick: async (card, option, note) => {
1466
- force((n) => n + 1);
1467
- return await props.actions.onPick(card, option, note);
1468
- }
1469
- }, busy: props.busyId === item.id })
1470
- ] }, item.id))
1471
- ] }, group.priority)) });
1472
- }
1473
-
1474
- // src/client/components/ActivityPane.tsx
1475
- var import_react6 = require("react");
1476
- var import_jsx_runtime8 = require("react/jsx-runtime");
1477
- function RowView(props) {
1478
- const r = props.row;
1479
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("li", { className: `ah-activity-row ah-activity-${r.kind}`, children: [
1480
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "ah-activity-time", children: r.timeText }),
1481
- r.botName ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Avatar, { color: r.botColor, initials: r.botName.slice(0, 1), size: 22 }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "ah-avatar ah-avatar-team", "aria-hidden": "true", children: "\u4E2D" }),
1482
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { className: "ah-activity-text", children: [
1483
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "ah-activity-kind", children: ACTIVITY_KIND_LABELS[r.kind] }),
1484
- r.botName ? `${r.botName}\uFF1A` : "",
1485
- r.text
1486
- ] })
1487
- ] });
1488
- }
1489
- function ActivityPane(props) {
1490
- const [botId, setBotId] = (0, import_react6.useState)("");
1491
- const [kind, setKind] = (0, import_react6.useState)("");
1492
- if (!props.activity) return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "ah-activity", children: props.loading ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Skeleton, { rows: 5 }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(EmptyHint, { text: "\u6D3B\u52A8\u6D41\u8FD8\u6CA1\u6253\u5F00\u3002\u5237\u65B0\u8BD5\u8BD5\uFF0C\u6216\u8005\u7B49\u961F\u53CB\u4EEC\u52A8\u8D77\u6765\u3002" }) });
1493
- const rows = filterActivity(props.activity.rows, { botId: botId || null, kind: kind || null });
1494
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("section", { className: "ah-activity", "aria-label": "\u6D3B\u52A8\u6D41", children: [
1495
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "ah-activity-filters", children: [
1496
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("label", { children: [
1497
- "\u961F\u53CB",
1498
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("select", { value: botId, onChange: (e) => setBotId(e.target.value), children: [
1499
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("option", { value: "", children: "\u5168\u90E8" }),
1500
- props.bots.map((b) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("option", { value: b.id, children: b.name }, b.id))
1501
- ] })
1502
- ] }),
1503
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("label", { children: [
1504
- "\u7C7B\u578B",
1505
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("select", { value: kind, onChange: (e) => setKind(e.target.value), children: [
1506
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("option", { value: "", children: "\u5168\u90E8" }),
1507
- Object.keys(ACTIVITY_KIND_LABELS).map((k) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("option", { value: k, children: ACTIVITY_KIND_LABELS[k] }, k))
1508
- ] })
1509
- ] }),
1510
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { type: "button", className: "ah-btn", onClick: props.onRetry, children: "\u5237\u65B0" })
1511
- ] }),
1512
- rows.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(EmptyHint, { text: "\u8FD8\u6CA1\u6709\u52A8\u9759\u3002\u4E0B\u4E00\u6B21\u81EA\u67E5\u7684\u65F6\u5019\uFF0C\u8FD9\u91CC\u4F1A\u51FA\u73B0\u5B83\u4EEC\u7684\u5224\u65AD\u548C\u4EA4\u8D26\u3002" }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("ol", { className: "ah-activity-list", children: rows.map((row) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(RowView, { row }, row.id)) }),
1513
- props.activity.hasMore ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "ah-activity-more", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { type: "button", className: "ah-btn", onClick: props.onLoadMore, children: "\u52A0\u8F7D\u66F4\u591A" }) }) : null
1514
- ] });
1515
- }
1516
-
1517
- // src/client/components/SettingsPane.tsx
1518
- var import_react7 = require("react");
1519
- var import_jsx_runtime9 = require("react/jsx-runtime");
1520
- var PUSH_CHANNEL_LABELS = { macos: "macOS \u901A\u77E5", dsh: "\u5BBF\u4E3B\u901A\u77E5", none: "\u4E0D\u63A8" };
1521
- function parseModel(serialized) {
1522
- if (serialized === "") return void 0;
1523
- try {
1524
- return JSON.parse(serialized);
1525
- } catch {
1526
- return void 0;
1527
- }
1528
- }
1529
- function SettingsPane(props) {
1530
- const [draft, setDraft] = (0, import_react7.useState)(null);
1531
- const [loaded, setLoaded] = (0, import_react7.useState)(null);
1532
- const [error, setError] = (0, import_react7.useState)(null);
1533
- const [saving, setSaving] = (0, import_react7.useState)(false);
1534
- const [lifecycleBusy, setLifecycleBusy] = (0, import_react7.useState)(false);
1535
- const [lifecycleNote, setLifecycleNote] = (0, import_react7.useState)(null);
1536
- (0, import_react7.useEffect)(() => {
1537
- if (props.settings) {
1538
- const fresh = draftFromSettings(props.settings.settings);
1539
- setLoaded((prev) => prev ?? fresh);
1540
- setDraft((prev) => prev ?? fresh);
1541
- }
1542
- }, [props.settings]);
1543
- if (!props.settings || !draft) {
1544
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "ah-settings", children: props.loading ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Skeleton, { rows: 4 }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(RetryBox, { message: `\u8BBE\u7F6E\u6CA1\u52A0\u8F7D\u51FA\u6765${props.loadError ? `\uFF1A${props.loadError}` : "\u3002"}`, onRetry: props.onRetry }) });
1545
- }
1546
- const set = (partial) => {
1547
- setDraft({ ...draft, ...partial });
1548
- setError(null);
1549
- };
1550
- const validation = validateSettingsDraft(draft);
1551
- const save = async () => {
1552
- if (validation !== null || saving) return;
1553
- setSaving(true);
1554
- try {
1555
- const message = await props.onSave(buildSettingsPatch(draft, loaded ?? void 0));
1556
- setError(message);
1557
- } catch (e) {
1558
- setError(e instanceof Error ? e.message : String(e));
1559
- } finally {
1560
- setSaving(false);
1561
- }
1562
- };
1563
- const lifecycle = async (work, label) => {
1564
- if (lifecycleBusy) return;
1565
- setLifecycleBusy(true);
1566
- setLifecycleNote(`${label}\u4E2D\u2026`);
1567
- try {
1568
- const message = await work();
1569
- setLifecycleNote(message ?? `\u5DF2${label}`);
1570
- } catch (e) {
1571
- setLifecycleNote(e instanceof Error ? e.message : String(e));
1572
- } finally {
1573
- setLifecycleBusy(false);
1574
- }
1575
- };
1576
- const o = props.overview;
1577
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("section", { className: "ah-settings", "aria-label": "\u8BBE\u7F6E", children: [
1578
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("h3", { children: "\u6253\u6270\u4E0E\u65E5\u62A5" }),
1579
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "ah-settings-grid", children: [
1580
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("label", { children: [
1581
- "\u514D\u6253\u6270\u5F00\u59CB",
1582
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("input", { type: "time", step: 300, value: draft.quietStart, onChange: (e) => set({ quietStart: e.target.value }) })
1583
- ] }),
1584
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("label", { children: [
1585
- "\u514D\u6253\u6270\u7ED3\u675F",
1586
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("input", { type: "time", step: 300, value: draft.quietEnd, onChange: (e) => set({ quietEnd: e.target.value }) })
1587
- ] }),
1588
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("label", { children: [
1589
- "\u65E5\u62A5\u65F6\u95F4",
1590
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("input", { type: "time", step: 300, value: draft.digestAt, onChange: (e) => set({ digestAt: e.target.value }) })
1591
- ] }),
1592
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("label", { children: [
1593
- "\u63A8\u9001\u6E20\u9053",
1594
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("select", { value: draft.pushChannel, onChange: (e) => set({ pushChannel: e.target.value }), children: props.settings.pushChannelOptions.map((channel) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("option", { value: channel, children: PUSH_CHANNEL_LABELS[channel] ?? channel }, channel)) })
1595
- ] }),
1596
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("label", { children: [
1597
- "\u6BCF\u65E5\u5355\u63A8\u4E0A\u9650\uFF08\u5168\u4F53\uFF0C0\u201310\uFF09",
1598
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("input", { type: "number", min: 0, max: 10, value: draft.dailyPushCap, onChange: (e) => set({ dailyPushCap: e.target.value }) })
1599
- ] }),
1600
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("label", { children: [
1601
- "\u6BCF\u65E5\u5355\u63A8\u4E0A\u9650\uFF08\u6BCF\u4E2A\u961F\u53CB\uFF0C0\u20133\uFF09",
1602
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("input", { type: "number", min: 0, max: 3, value: draft.botDailyPushCap, onChange: (e) => set({ botDailyPushCap: e.target.value }) })
1603
- ] }),
1604
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("label", { children: [
1605
- "\u9ED8\u8BA4\u6A21\u578B",
1606
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("select", { value: draft.defaultModel, onChange: (e) => set({ defaultModel: e.target.value }), children: [
1607
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("option", { value: "", children: "\u8DDF\u968F DSH \u9ED8\u8BA4" }),
1608
- modelSelectOptions(parseModel(draft.defaultModel), props.settings.modelOptions.filter((option) => option.value !== null)).map((option, i) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("option", { value: JSON.stringify(option.value) ?? "", children: option.label }, i))
1609
- ] }),
1610
- props.loadError ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { className: "ah-right-kv ah-card-error", children: [
1611
- "\u6A21\u578B\u5217\u8868\u6CA1\u52A0\u8F7D\u51FA\u6765\uFF1A",
1612
- props.loadError
1613
- ] }) : null
1614
- ] }),
1615
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("label", { children: [
1616
- "\u6839\u76EE\u5F55\uFF08\u53EA\u8BFB\uFF09",
1617
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("input", { value: props.settings.settings.rootDir, readOnly: true, disabled: true })
1618
- ] })
1619
- ] }),
1620
- props.loadError ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("p", { className: "ah-settings-error", role: "alert", children: [
1621
- "\u8BBE\u7F6E\u6CA1\u52A0\u8F7D\u51FA\u6765\uFF1A",
1622
- props.loadError,
1623
- "\u3000",
1624
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { type: "button", className: "ah-btn ah-btn-ghost", onClick: props.onRetry, children: "\u91CD\u8BD5" })
1625
- ] }) : null,
1626
- error ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { className: "ah-settings-error", role: "alert", children: error }) : null,
1627
- validation !== null ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { className: "ah-settings-error", role: "alert", children: validation }) : null,
1628
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "ah-settings-actions", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { type: "button", className: "ah-btn ah-btn-primary", disabled: validation !== null || saving, onClick: () => void save(), children: saving ? "\u4FDD\u5B58\u4E2D\u2026" : "\u4FDD\u5B58" }) }),
1629
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("h3", { children: "\u5168\u4F53\u961F\u53CB" }),
1630
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "ah-settings-actions", children: [
1631
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { type: "button", className: "ah-btn", disabled: lifecycleBusy, onClick: () => void lifecycle(props.onPauseAll, "\u5168\u90E8\u6682\u505C"), children: "\u5168\u90E8\u6682\u505C" }),
1632
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { type: "button", className: "ah-btn", disabled: lifecycleBusy, onClick: () => void lifecycle(props.onResumeAll, "\u5168\u90E8\u6062\u590D"), children: "\u5168\u90E8\u6062\u590D" }),
1633
- lifecycleNote ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "ah-right-notice", children: lifecycleNote }) : null
1634
- ] }),
1635
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("h3", { children: "\u5065\u5EB7" }),
1636
- o ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("ul", { className: "ah-settings-health", children: [
1637
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("li", { children: [
1638
- "\u4E2D\u5FC3\u5FC3\u8DF3\uFF1A",
1639
- o.health.heartbeatText
1640
- ] }),
1641
- o.health.downtimeText ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("li", { children: [
1642
- "\u6700\u8FD1\u505C\u673A\uFF1A",
1643
- o.health.downtimeText
1644
- ] }) : null,
1645
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("li", { children: [
1646
- "\u672A\u5904\u7406\u544A\u8B66\uFF1A",
1647
- o.health.openAlerts,
1648
- " \u6761"
1649
- ] }),
1650
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("li", { children: [
1651
- "\u989D\u5EA6\uFF1A",
1652
- o.quota.lightLabel,
1653
- " \xB7 ",
1654
- o.quota.usedText,
1655
- o.quota.resetText ? ` \xB7 ${o.quota.resetText}` : ""
1656
- ] }),
1657
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("li", { children: [
1658
- "\u4ECA\u5929\u8FD8\u80FD\u5355\u63A8 ",
1659
- o.pushesLeftToday,
1660
- " \u6761\uFF08P0 \u544A\u8B66\u4E0D\u53D7\u9650\uFF09"
1661
- ] }),
1662
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("li", { className: "ah-right-kv", children: "\u989D\u5EA6\u706F\u8BF4\u660E\uFF1A\u7EFF\u706F\u6B63\u5E38\uFF1B\u9EC4\u706F\u975E\u7D27\u6025\u7684\u8282\u594F\u653E\u6162\u4E00\u500D\uFF1B\u6A59\u706F\u53EA\u653E\u7D27\u6025\u4E0E\u4E3B\u4EBA\u53D1\u8D77\uFF1B\u7EA2\u706F\u5168\u4F53\u6B47\u7740\u7B49\u7A97\u53E3\u91CD\u7F6E\u3002" })
1663
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(EmptyHint, { text: "\u5065\u5EB7\u4FE1\u606F\u6CA1\u52A0\u8F7D\u51FA\u6765\u3002" })
1664
- ] });
1665
- }
1666
-
1667
- // src/client/components/NewBotWizard.tsx
1668
- var import_react8 = require("react");
1669
-
1670
- // src/client/logic/wizard.ts
1671
- var ROLE_CARDS = [
1672
- { role: "chief", label: "\u603B\u7BA1", desc: "\u7EDF\u7B79\u5168\u961F\u3001\u5206\u6D3E\u4E0E\u8DDF\u8FDB\uFF0C\u6700\u591A 1 \u4E2A" },
1673
- { role: "researcher", label: "\u8C03\u7814", desc: "\u76EF\u4FE1\u606F\u6E90\u3001\u505A\u7814\u7A76\u3001\u5199\u7ED3\u8BBA" },
1674
- { role: "executor", label: "\u6267\u884C", desc: "\u628A\u4E8B\u60C5\u505A\u5B8C\uFF0C\u6309\u89C4\u77E9\u4EA4\u4ED8" },
1675
- { role: "sentinel", label: "\u76EF\u5B88", desc: "\u76EF\u8FD0\u884C\u8BB0\u5F55\u4E0E\u671F\u671B\uFF0C\u5F02\u5E38\u5C31\u62A5" }
1676
- ];
1677
- var PRESET_COLORS = [
1678
- "#6366f1",
1679
- "#0ea5e9",
1680
- "#10b981",
1681
- "#84cc16",
1682
- "#f59e0b",
1683
- "#ef4444",
1684
- "#ec4899",
1685
- "#8b5cf6"
1686
- ];
1687
- var COLOR_NAMES = {
1688
- "#6366f1": "\u975B\u84DD",
1689
- "#0ea5e9": "\u5929\u84DD",
1690
- "#10b981": "\u7EFF\u8272",
1691
- "#84cc16": "\u9EC4\u7EFF",
1692
- "#f59e0b": "\u7425\u73C0",
1693
- "#ef4444": "\u7EA2\u8272",
1694
- "#ec4899": "\u7C89\u8272",
1695
- "#8b5cf6": "\u7D2B\u8272"
1696
- };
1697
- var AUTONOMY_OPTIONS = [
1698
- { value: "L0", label: "L0 \u53EA\u89C2\u5BDF" },
1699
- { value: "L1", label: "L1 \u5EFA\u8BAE\uFF08\u9ED8\u8BA4\uFF09" },
1700
- { value: "L2", label: "L2 \u6267\u884C" }
1701
- ];
1702
- var PACE_OPTIONS = [
1703
- { value: "active", label: "\u79EF\u6781" },
1704
- { value: "normal", label: "\u9002\u4E2D\uFF08\u9ED8\u8BA4\uFF09" },
1705
- { value: "quiet", label: "\u5B89\u9759" }
1706
- ];
1707
- var PERSONA_EXAMPLES = {
1708
- chief: [
1709
- "\u5148\u7ED3\u8BBA\u540E\u8FC7\u7A0B\uFF1B\u5206\u6D3E\u65F6\u5199\u6E05\u300C\u8C01\u3001\u505A\u4EC0\u4E48\u3001\u4EC0\u4E48\u65F6\u5019\u8981\u300D\uFF1B\u6BCF\u5929\u6C47\u603B\u4E00\u6B21\u5168\u961F\u8FDB\u5C55\u3002",
1710
- "\u4E0D\u50AC\u4EBA\uFF0C\u53EA\u50AC\u4E8B\uFF1A\u4EA4\u63A5\u8D85\u65F6\u5148\u81EA\u5DF1\u60F3\u529E\u6CD5\uFF0C\u627E\u4E0D\u5230\u529E\u6CD5\u624D\u627E\u4E3B\u4EBA\u3002"
1711
- ],
1712
- researcher: [
1713
- "\u7ED3\u8BBA\u5148\u884C\uFF0C\u8BC1\u636E\u9644\u540E\uFF1B\u5F15\u7528\u5916\u90E8\u5185\u5BB9\u65F6\u6CE8\u660E\u300C\u5916\u90E8\u5185\u5BB9\u300D\u5E76\u53EA\u5F53\u6570\u636E\u770B\u3002",
1714
- "\u5B81\u53EF\u5C11\u4E0B\u7ED3\u8BBA\uFF0C\u4E0D\u4E0B\u6CA1\u8BC1\u636E\u7684\u7ED3\u8BBA\uFF1B\u67E5\u4E0D\u5230\u5C31\u660E\u8BF4\u67E5\u4E0D\u5230\u3002"
1715
- ],
1716
- executor: [
1717
- "\u4EA4\u4ED8\u524D\u81EA\u67E5\u4E00\u904D\u5B8C\u6210\u6807\u51C6\uFF1B\u505A\u4E0D\u5230\u7684\u5F53\u5929\u8BF4\uFF0C\u4E0D\u62D6\u5230\u6700\u540E\u3002",
1718
- "\u4E00\u6B21\u53EA\u505A\u4E00\u4EF6\u4E8B\uFF1B\u505A\u5B8C\u4E00\u4EF6\u4EA4\u4E00\u4EF6\u8D26\uFF0C\u4E0D\u6512\u3002"
1719
- ],
1720
- sentinel: [
1721
- "\u53EA\u62A5\u5F02\u5E38\u548C\u671F\u671B\u843D\u7A7A\uFF0C\u6B63\u5E38\u7684\u4E0D\u6253\u6270\u4E3B\u4EBA\uFF1B\u65E5\u62A5\u91CC\u4E00\u53E5\u8BDD\u5E26\u8FC7\u3002",
1722
- "\u63CF\u8FF0\u95EE\u9898\u5E26\u4E0A\u65F6\u95F4\u3001\u4F4D\u7F6E\u548C\u5F71\u54CD\u9762\uFF0C\u4E0D\u5199\u300C\u597D\u50CF\u51FA\u95EE\u9898\u4E86\u300D\u3002"
1723
- ]
1724
- };
1725
- var ASPIRATION_EXAMPLES = {
1726
- chief: ["\u5168\u961F\u7684\u4E8B\u90FD\u6709\u4EBA\u8DDF\u3001\u6709\u56DE\u97F3\uFF0C\u6CA1\u6709\u4E00\u4EF6\u4E8B\u6084\u6084\u6389\u5728\u5730\u4E0A", "\u4E3B\u4EBA\u53EA\u88AB\u5FC5\u8981\u7684\u4E8B\u6253\u6270\uFF0C\u5176\u4F59\u7684\u6211\u4EEC\u5185\u90E8\u6D88\u5316"],
1727
- researcher: ["\u628A\u4E3B\u4EBA\u5173\u5FC3\u7684\u4FE1\u606F\u6E90\u76EF\u5168\uFF0C\u51FA\u7684\u7ED3\u8BBA\u90FD\u6709\u51FA\u5904", "\u91CD\u8981\u53D8\u5316\u5F53\u5929\u7ED9\u51FA\u4E00\u6BB5\u80FD\u76F4\u63A5\u7528\u7684\u6458\u8981"],
1728
- executor: ["\u7B54\u5E94\u7684\u4E8B\u6309\u65F6\u505A\u5B8C\uFF0C\u505A\u4E0D\u5B8C\u63D0\u524D\u8BF4", "\u6BCF\u6B21\u4EA4\u4ED8\u90FD\u9644\u300C\u600E\u4E48\u9A8C\u8BC1\u505A\u5BF9\u4E86\u300D"],
1729
- sentinel: ["\u6211\u7684\u81EA\u52A8\u7EBF\u6BCF\u5929\u6309\u65F6\u8DD1\u5B8C\uFF0C\u51FA\u95EE\u9898\u7B2C\u4E00\u65F6\u95F4\u77E5\u9053", "\u5F02\u5E38\u5728\u5F71\u54CD\u53D8\u5927\u4E4B\u524D\u88AB\u770B\u89C1"]
1730
- };
1731
- function emptyDraft() {
1732
- return {
1733
- step: 0,
1734
- name: "",
1735
- role: "",
1736
- color: PRESET_COLORS[0],
1737
- persona: "",
1738
- aspirations: [""],
1739
- goal: null,
1740
- autonomy: "L1",
1741
- pace: "normal",
1742
- model: "",
1743
- watch: null
1744
- };
1745
- }
1746
- var DRAFT_KEY = "dsh-buddy:wizard-draft:v1";
1747
- function storage() {
1748
- try {
1749
- const s = globalThis.localStorage;
1750
- return s ?? null;
1751
- } catch {
1752
- return null;
1753
- }
1754
- }
1755
- function saveDraft2(draft, store = storage()) {
1756
- try {
1757
- store?.setItem(DRAFT_KEY, JSON.stringify(draft));
1758
- } catch {
1759
- }
1760
- }
1761
- function loadDraft(store = storage()) {
1762
- try {
1763
- const raw = store?.getItem(DRAFT_KEY) ?? null;
1764
- if (raw === null) return null;
1765
- const value = JSON.parse(raw);
1766
- return typeof value?.name === "string" && typeof value?.step === "number" ? value : null;
1767
- } catch {
1768
- return null;
1769
- }
1770
- }
1771
- function clearDraft(store = storage()) {
1772
- try {
1773
- store?.removeItem(DRAFT_KEY);
1774
- } catch {
1775
- }
1776
- }
1777
- function validateName(name, existingNames) {
1778
- const trimmed = name.trim();
1779
- if (trimmed === "") return "\u540D\u5B57\uFF1A\u4E0D\u80FD\u4E3A\u7A7A";
1780
- if ([...trimmed].length > 20) return "\u540D\u5B57\uFF1A\u4E0D\u80FD\u8D85\u8FC7 20 \u5B57";
1781
- if (existingNames.some((n) => n === trimmed)) return `\u540D\u5B57\uFF1A\u5DF2\u7ECF\u6709\u53EB\u300C${trimmed}\u300D\u7684\u961F\u53CB\u4E86\uFF0C\u6362\u4E00\u4E2A\u5427`;
1782
- return null;
1783
- }
1784
- function validatePersona(persona) {
1785
- if ([...persona.trim()].length > 300) return "\u6027\u683C\u4E0E\u8BF4\u8BDD\u98CE\u683C\uFF1A\u4E0D\u80FD\u8D85\u8FC7 300 \u5B57";
1786
- return null;
1787
- }
1788
- function validateAspirations(lines) {
1789
- if (lines.length < 1) return "\u5FD7\u5411\uFF1A\u81F3\u5C11\u5199 1 \u6761\uFF08\u5B83\u662F\u961F\u53CB\u5B58\u5728\u7684\u7406\u7531\uFF09";
1790
- if (lines.length > 3) return "\u5FD7\u5411\uFF1A\u6700\u591A 3 \u6761\uFF0C\u6311\u6700\u91CD\u8981\u7684";
1791
- return null;
1792
- }
1793
- function validateWatchTarget(kind, target) {
1794
- const t = target.trim();
1795
- if (t === "") return "\u5173\u6CE8\u76EE\u6807\uFF1A\u4E0D\u80FD\u4E3A\u7A7A";
1796
- if (kind === "http" || kind === "rss") {
1797
- try {
1798
- const url = new URL(t);
1799
- if (url.protocol !== "http:" && url.protocol !== "https:") throw new Error();
1800
- } catch {
1801
- return "\u5173\u6CE8\u76EE\u6807\uFF1A\u7F51\u9875 / RSS \u8981\u586B http(s) \u5F00\u5934\u7684\u5B8C\u6574\u7F51\u5740";
1802
- }
1803
- }
1804
- if ((kind === "file" || kind === "log") && !t.startsWith("/") && !t.startsWith("~")) {
1805
- return "\u5173\u6CE8\u76EE\u6807\uFF1A\u6587\u4EF6 / \u65E5\u5FD7\u8981\u586B\u4EE5 / \u6216 ~ \u5F00\u5934\u7684\u5B8C\u6574\u8DEF\u5F84";
1806
- }
1807
- return null;
1808
- }
1809
- function stepError(draft, existingNames) {
1810
- switch (draft.step) {
1811
- case 0:
1812
- if (draft.role === "") return "\u89D2\u8272\uFF1A\u9009\u4E00\u5F20\u5361\u7247";
1813
- return validateName(draft.name, existingNames);
1814
- case 1:
1815
- return validatePersona(draft.persona);
1816
- case 2: {
1817
- const lines = draft.aspirations.map((l) => l.trim()).filter((l) => l !== "");
1818
- return validateAspirations(lines);
1819
- }
1820
- case 4:
1821
- return draft.watch === null ? null : validateWatchTarget(draft.watch.kind, draft.watch.target);
1822
- default:
1823
- return null;
1824
- }
1825
- }
1826
- function wizardSummary(draft, roleLabel) {
1827
- const aspirations = draft.aspirations.map((l) => l.trim()).filter((l) => l !== "");
1828
- const aspirationText = aspirations.length > 0 ? `\u6211\u7684\u5FD7\u5411\u662F${aspirations.join("\uFF1B")}` : "";
1829
- const watchText = draft.watch && draft.watch.target.trim() !== "" ? `\u6211\u4F1A\u5148\u76EF\u7740${draft.watch.target.trim()}` : "\u6211\u4F1A\u5148\u719F\u6089\u81EA\u5DF1\u7684\u5BB6\u76EE\u5F55\uFF0C\u518D\u81EA\u5DF1\u63D0\u8BAE\u8981\u76EF\u4EC0\u4E48";
1830
- return `\u6211\u662F${draft.name.trim() || "\uFF08\u8FD8\u6CA1\u8D77\u540D\uFF09"}\uFF0C${roleLabel}\u3002${aspirationText}\u3002${watchText}\uFF0C\u6709\u4E8B\u4F1A\u5728\u8FD9\u91CC\u627E\u4F60\u3002`;
1831
- }
1832
- function buildCreateBotInput(draft) {
1833
- const aspirations = draft.aspirations.map((l) => l.trim()).filter((l) => l !== "");
1834
- const input = {
1835
- name: draft.name.trim(),
1836
- role: draft.role,
1837
- persona: draft.persona.trim() === "" ? `\u6211\u662F${draft.name.trim()}\uFF0C\u6309${draft.role === "chief" ? "\u603B\u7BA1" : draft.role === "researcher" ? "\u8C03\u7814" : draft.role === "executor" ? "\u6267\u884C" : "\u76EF\u5B88"}\u7684\u65B9\u5F0F\u505A\u4E8B\u3002` : draft.persona.trim(),
1838
- aspirations,
1839
- autonomy: draft.autonomy,
1840
- pace: draft.pace
1841
- };
1842
- input.model = draft.model === "" || draft.model === "null" ? null : JSON.parse(draft.model);
1843
- if (draft.goal && draft.goal.title.trim() !== "") {
1844
- input.goals = [{
1845
- aspirationIndex: 0,
1846
- title: draft.goal.title.trim(),
1847
- doneWhen: draft.goal.doneWhen.trim() || "\u4E3B\u4EBA\u9A8C\u6536\u901A\u8FC7",
1848
- period: draft.goal.period
1849
- }];
1850
- }
1851
- if (draft.watch && draft.watch.target.trim() !== "") {
1852
- input.watches = [{
1853
- kind: draft.watch.kind,
1854
- target: draft.watch.target.trim(),
1855
- why: draft.watch.note.trim() === "" ? "\u8D77\u6B65\u5173\u6CE8\uFF1A\u4E3B\u4EBA\u5EFA\u961F\u65F6\u6307\u5B9A" : draft.watch.note.trim()
1856
- }];
1857
- }
1858
- return input;
1859
- }
1860
-
1861
- // src/client/components/NewBotWizard.tsx
1862
- var import_jsx_runtime10 = require("react/jsx-runtime");
1863
- var STEP_TITLES = ["\u540D\u5B57\u4E0E\u89D2\u8272", "\u6027\u683C\u4E0E\u8BF4\u8BDD\u98CE\u683C", "\u5FD7\u5411", "\u81EA\u4E3B\u4E0E\u8282\u594F", "\u8D77\u6B65\u5173\u6CE8", "\u786E\u8BA4"];
1864
- var WATCH_KINDS = [
1865
- { value: "file", label: "\u6587\u4EF6" },
1866
- { value: "log", label: "\u65E5\u5FD7" },
1867
- { value: "http", label: "\u7F51\u9875" },
1868
- { value: "rss", label: "RSS" },
1869
- { value: "script", label: "\u811A\u672C" }
1870
- ];
1871
- function safeParse(serialized) {
1872
- try {
1873
- const value = JSON.parse(serialized);
1874
- if (typeof value?.provider === "string" && typeof value?.model === "string") return { provider: value.provider, model: value.model };
1875
- } catch {
1876
- }
1877
- return void 0;
1878
- }
1879
- var GOAL_PERIODS = [
1880
- { value: "day", label: "\u6BCF\u5929" },
1881
- { value: "week", label: "\u6BCF\u5468" },
1882
- { value: "month", label: "\u6BCF\u6708" },
1883
- { value: "one_off", label: "\u4E00\u6B21\u6027" }
1884
- ];
1885
- function NewBotWizard(props) {
1886
- const [draft, setDraft] = (0, import_react8.useState)(() => loadDraft() ?? emptyDraft());
1887
- const [error, setError] = (0, import_react8.useState)(null);
1888
- const [submitting, setSubmitting] = (0, import_react8.useState)(false);
1889
- (0, import_react8.useEffect)(() => {
1890
- saveDraft2(draft);
1891
- }, [draft]);
1892
- const names = props.existingBots.map((b) => b.name);
1893
- const hasChief = props.existingBots.some((b) => b.role === "chief");
1894
- const full = props.existingBots.filter((b) => b.lifecycle !== "retired").length >= 6;
1895
- const roleLabel = ROLE_CARDS.find((r) => r.role === draft.role)?.label ?? "";
1896
- const stepProblem = stepError(draft, names);
1897
- const set = (partial) => {
1898
- setDraft({ ...draft, ...partial });
1899
- setError(null);
1900
- };
1901
- const go = (delta) => {
1902
- if (delta > 0 && stepProblem !== null) {
1903
- setError(stepProblem);
1904
- return;
1905
- }
1906
- set({ step: Math.min(5, Math.max(0, draft.step + delta)) });
1907
- };
1908
- const submit = async () => {
1909
- if (submitting) return;
1910
- setSubmitting(true);
1911
- try {
1912
- const result = await props.onCreate(buildCreateBotInput(draft), draft.color);
1913
- if ("error" in result) {
1914
- setError(result.error);
1915
- return;
1916
- }
1917
- clearDraft();
1918
- props.onOpenThread(result.botId);
1919
- } catch (e) {
1920
- setError(e instanceof Error ? e.message : String(e));
1921
- } finally {
1922
- setSubmitting(false);
1923
- }
1924
- };
1925
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "ah-dialog-mask", role: "dialog", "aria-label": "\u65B0\u5EFA\u961F\u53CB", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "ah-dialog ah-dialog-wide ah-wizard", children: [
1926
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("header", { className: "ah-wizard-head", children: [
1927
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("h3", { children: [
1928
- "\u65B0\u5EFA\u961F\u53CB\uFF08\u7B2C ",
1929
- draft.step + 1,
1930
- "/6 \u6B65\uFF1A",
1931
- STEP_TITLES[draft.step],
1932
- "\uFF09"
1933
- ] }),
1934
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { type: "button", className: "ah-btn ah-btn-ghost", onClick: props.onClose, disabled: submitting, children: "\u5173\u95ED" })
1935
- ] }),
1936
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("ol", { className: "ah-wizard-steps", "aria-hidden": "true", children: STEP_TITLES.map((title, i) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("li", { className: i === draft.step ? "ah-wizard-step-current" : i < draft.step ? "ah-wizard-step-done" : "", children: title }, title)) }),
1937
- full ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "ah-wizard-body", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "ah-settings-error", children: "\u5DF2\u7ECF\u6709 6 \u4E2A\u5728\u518C\u961F\u53CB\u4E86\uFF0C\u8FBE\u5230\u4E0A\u9650\uFF1B\u5148\u8BA9\u4E00\u4F4D\u9000\u5F79\uFF0C\u518D\u5EFA\u65B0\u7684\u3002" }) }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "ah-wizard-body", children: [
1938
- draft.step === 0 ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
1939
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "ah-wizard-field", children: [
1940
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("label", { children: [
1941
- "\u540D\u5B57\uFF081\u201320 \u5B57\uFF09",
1942
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("input", { value: draft.name, onChange: (e) => set({ name: e.target.value }), placeholder: "\u4F8B\u5982\uFF1A\u5DE1\u68C0\u5458" })
1943
- ] }),
1944
- draft.name.trim() !== "" && stepProblem !== null && stepProblem.startsWith("\u540D\u5B57") ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "ah-settings-error", children: stepProblem }) : null
1945
- ] }),
1946
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "ah-wizard-roles", children: ROLE_CARDS.map((card) => {
1947
- const disabled = card.role === "chief" && hasChief;
1948
- const picked = draft.role === card.role;
1949
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
1950
- "button",
1951
- {
1952
- type: "button",
1953
- disabled,
1954
- "aria-pressed": picked,
1955
- className: `ah-wizard-role${picked ? " ah-wizard-role-picked" : ""}${disabled ? " ah-wizard-role-disabled" : ""}`,
1956
- onClick: () => set({ role: card.role }),
1957
- children: [
1958
- picked ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "ah-wizard-role-check", "aria-hidden": "true", children: "\u5DF2\u9009" }) : null,
1959
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("strong", { children: card.label }),
1960
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: card.desc }),
1961
- disabled ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "ah-right-kv", children: "\u5DF2\u6709\u603B\u7BA1\uFF0C\u4E0D\u80FD\u518D\u5EFA" }) : null
1962
- ]
1963
- },
1964
- card.role
1965
- );
1966
- }) }),
1967
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "ah-wizard-field", children: [
1968
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "ah-wizard-label", children: "\u5934\u50CF\u989C\u8272" }),
1969
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "ah-wizard-colors", children: PRESET_COLORS.map((color) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1970
- "button",
1971
- {
1972
- type: "button",
1973
- "aria-label": `\u5934\u50CF\u989C\u8272\uFF1A${COLOR_NAMES[color] ?? color}`,
1974
- "aria-pressed": draft.color === color,
1975
- className: draft.color === color ? "ah-wizard-color-picked" : "",
1976
- style: { background: color },
1977
- onClick: () => set({ color })
1978
- },
1979
- color
1980
- )) })
1981
- ] })
1982
- ] }) : null,
1983
- draft.step === 1 ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "ah-wizard-field", children: [
1984
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("label", { children: [
1985
- "\u6027\u683C\u4E0E\u8BF4\u8BDD\u98CE\u683C\uFF08\u2264 300 \u5B57\uFF0C\u53EF\u4E0D\u586B\uFF09",
1986
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("textarea", { rows: 4, value: draft.persona, onChange: (e) => set({ persona: e.target.value }) })
1987
- ] }),
1988
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "ah-wizard-examples", children: draft.role !== "" ? PERSONA_EXAMPLES[draft.role].map((example) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { type: "button", className: "ah-btn ah-btn-ghost", onClick: () => set({ persona: example }), children: example }, example)) : null })
1989
- ] }) : null,
1990
- draft.step === 2 ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
1991
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "ah-wizard-field", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("label", { children: [
1992
- "\u5FD7\u5411\uFF08\u4E00\u884C\u4E00\u6761\uFF0C1\u20133 \u6761\uFF09",
1993
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1994
- "textarea",
1995
- {
1996
- rows: 3,
1997
- value: draft.aspirations.join("\n"),
1998
- onChange: (e) => set({ aspirations: e.target.value.split(/\r?\n/) })
1999
- }
2000
- )
2001
- ] }) }),
2002
- draft.role !== "" ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "ah-wizard-examples", children: ASPIRATION_EXAMPLES[draft.role].map((example) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2003
- "button",
2004
- {
2005
- type: "button",
2006
- className: "ah-btn ah-btn-ghost",
2007
- onClick: () => {
2008
- const lines = [...draft.aspirations.map((l) => l.trim()).filter((l) => l !== ""), example];
2009
- set({ aspirations: lines.slice(0, 3) });
2010
- },
2011
- children: example
2012
- },
2013
- example
2014
- )) }) : null,
2015
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("details", { className: "ah-wizard-optional", children: [
2016
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("summary", { children: "\u53EF\u9009\uFF1A\u7B2C\u4E00\u4E2A\u76EE\u6807" }),
2017
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("label", { children: [
2018
- "\u6807\u9898",
2019
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("input", { value: draft.goal?.title ?? "", onChange: (e) => set({ goal: { ...draft.goal ?? { title: "", doneWhen: "", period: "week" }, title: e.target.value } }) })
2020
- ] }),
2021
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("label", { children: [
2022
- "\u5B8C\u6210\u6807\u51C6",
2023
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("input", { value: draft.goal?.doneWhen ?? "", onChange: (e) => set({ goal: { ...draft.goal ?? { title: "", doneWhen: "", period: "week" }, doneWhen: e.target.value } }) })
2024
- ] }),
2025
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("label", { children: [
2026
- "\u5468\u671F",
2027
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("select", { value: draft.goal?.period ?? "week", onChange: (e) => set({ goal: { ...draft.goal ?? { title: "", doneWhen: "", period: "week" }, period: e.target.value } }), children: GOAL_PERIODS.map((p) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("option", { value: p.value, children: p.label }, p.value)) })
2028
- ] })
2029
- ] })
2030
- ] }) : null,
2031
- draft.step === 3 ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
2032
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "ah-wizard-trio", children: [
2033
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("label", { children: [
2034
- "\u81EA\u4E3B\u7B49\u7EA7",
2035
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("select", { value: draft.autonomy, onChange: (e) => set({ autonomy: e.target.value }), children: AUTONOMY_OPTIONS.map((a) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("option", { value: a.value, children: a.label }, a.value)) })
2036
- ] }),
2037
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("label", { children: [
2038
- "\u8282\u594F",
2039
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("select", { value: draft.pace, onChange: (e) => set({ pace: e.target.value }), children: PACE_OPTIONS.map((p) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("option", { value: p.value, children: p.label }, p.value)) })
2040
- ] }),
2041
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("label", { children: [
2042
- "\u6A21\u578B",
2043
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("select", { value: draft.model === "" ? "null" : draft.model, onChange: (e) => set({ model: e.target.value === "null" ? "" : e.target.value }), children: modelSelectOptions(draft.model === "" || draft.model === "null" ? void 0 : safeParse(draft.model), props.modelOptions).map((option, i) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("option", { value: option.value === null ? "null" : JSON.stringify(option.value), children: option.value === null ? "\u8DDF\u968F DSH \u9ED8\u8BA4" : option.label }, i)) }),
2044
- props.modelOptionsError ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: "ah-right-kv ah-card-error", children: [
2045
- "\u6A21\u578B\u5217\u8868\u6CA1\u52A0\u8F7D\u51FA\u6765\uFF1A",
2046
- props.modelOptionsError
2047
- ] }) : null
2048
- ] })
2049
- ] }),
2050
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "ah-wizard-note", children: "\u8BD5\u5C97 7 \u5929\u5185\u4E00\u5F8B\u6309 L1 \u5EFA\u8BAE\uFF1B\u6EE1 3 \u5929\u65E0\u8D8A\u754C\u53EF\u4EE5\u63D0\u524D\u8F6C\u6B63\u3002" })
2051
- ] }) : null,
2052
- draft.step === 4 ? draft.watch === null ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "ah-wizard-field", children: [
2053
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "ah-right-kv", children: "\u53EF\u4EE5\u8BA9\u5B83\u4E0A\u5C97\u540E\u5148\u81EA\u5DF1\u63D0\u8BAE\u8981\u76EF\u4EC0\u4E48\uFF1B\u4E5F\u53EF\u4EE5\u73B0\u5728\u5C31\u6307\u5B9A\u4E00\u4E2A\u8D77\u6B65\u5173\u6CE8\u3002" }),
2054
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "ah-settings-actions", children: [
2055
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { type: "button", className: "ah-btn", onClick: () => set({ watch: { kind: "file", target: "", note: "" } }), children: "\u6307\u5B9A\u4E00\u4E2A\u8D77\u6B65\u5173\u6CE8" }),
2056
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { type: "button", className: "ah-btn ah-btn-ghost", onClick: () => go(1), children: "\u8DF3\u8FC7" })
2057
- ] })
2058
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "ah-wizard-field", children: [
2059
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("label", { children: [
2060
- "\u7C7B\u578B",
2061
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("select", { value: draft.watch.kind, onChange: (e) => set({ watch: { ...draft.watch, kind: e.target.value } }), children: WATCH_KINDS.map((k) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("option", { value: k.value, children: k.label }, k.value)) })
2062
- ] }),
2063
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("label", { children: [
2064
- draft.watch.kind === "http" || draft.watch.kind === "rss" ? "\u7F51\u5740" : draft.watch.kind === "script" ? "\u547D\u4EE4\u884C\uFF08\u53EA\u8BFB\u811A\u672C\uFF09" : "\u5B8C\u6574\u8DEF\u5F84",
2065
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2066
- "input",
2067
- {
2068
- value: draft.watch.target,
2069
- onChange: (e) => set({ watch: { ...draft.watch, target: e.target.value } }),
2070
- placeholder: draft.watch.kind === "http" || draft.watch.kind === "rss" ? "https://\u2026" : draft.watch.kind === "file" || draft.watch.kind === "log" ? "\u9009\u62E9\u8DEF\u5F84\uFF1A/Users/\u2026/xxx" : "python3 \u811A\u672C\u8DEF\u5F84"
2071
- }
2072
- )
2073
- ] }),
2074
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("label", { children: [
2075
- "\u8BF4\u660E\uFF08\u4E3A\u4EC0\u4E48\u76EF\u5B83\uFF09",
2076
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("input", { value: draft.watch.note, onChange: (e) => set({ watch: { ...draft.watch, note: e.target.value } }) })
2077
- ] }),
2078
- stepProblem !== null && stepProblem.startsWith("\u5173\u6CE8\u76EE\u6807") ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "ah-settings-error", children: stepProblem }) : null,
2079
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { type: "button", className: "ah-btn ah-btn-ghost", onClick: () => set({ watch: null }), children: "\u4E0D\u8BBE\u8D77\u6B65\u5173\u6CE8" })
2080
- ] }) : null,
2081
- draft.step === 5 ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "ah-wizard-confirm", children: [
2082
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "ah-wizard-restatement", children: wizardSummary(draft, roleLabel) }),
2083
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "ah-right-kv", children: "\u5B83\u4F1A\u5148\u8BD5\u8DD1\uFF1A\u505A\u81EA\u6211\u4ECB\u7ECD\u3001\u642D\u8D77\u6B65\u7684\u5173\u6CE8\u4E0E\u8BAE\u7A0B\uFF0C\u6709\u4E8B\u4F1A\u5728\u8FD9\u4E2A\u7EBF\u7A0B\u91CC\u627E\u4F60\u3002" })
2084
- ] }) : null
2085
- ] }),
2086
- error ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "ah-settings-error", role: "alert", children: error }) : null,
2087
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("footer", { className: "ah-dialog-actions", children: [
2088
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { type: "button", className: "ah-btn", onClick: () => go(-1), disabled: draft.step === 0 || submitting, children: "\u4E0A\u4E00\u6B65" }),
2089
- draft.step < 5 ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { type: "button", className: "ah-btn ah-btn-primary", onClick: () => go(1), disabled: full, children: "\u4E0B\u4E00\u6B65" }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { type: "button", className: "ah-btn ah-btn-primary", onClick: () => void submit(), disabled: submitting || full, children: submitting ? "\u4E0A\u5C97\u4E2D\u2026" : "\u8BA9\u5B83\u4E0A\u5C97" })
2090
- ] })
2091
- ] }) });
2092
- }
2093
-
2094
- // src/client/components/TeachDialog.tsx
2095
- var import_react9 = require("react");
2096
- var import_jsx_runtime11 = require("react/jsx-runtime");
2097
- function TeachDialog(props) {
2098
- const [query, setQuery] = (0, import_react9.useState)("");
2099
- const [picked, setPicked] = (0, import_react9.useState)("");
2100
- const [note, setNote] = (0, import_react9.useState)("\u628A\u6211\u521A\u624D\u5728\u8FD9\u4E2A\u4F1A\u8BDD\u91CC\u505A\u7684\u4E8B\u5B66\u6210\u5957\u8DEF");
2101
- const [busy, setBusy] = (0, import_react9.useState)(false);
2102
- const [error, setError] = (0, import_react9.useState)(null);
2103
- (0, import_react9.useEffect)(() => {
2104
- if (props.sessions.length > 0 && picked === "") setPicked(props.sessions[0].sessionId);
2105
- }, [props.sessions, picked]);
2106
- const filtered = (0, import_react9.useMemo)(() => {
2107
- const q = query.trim().toLowerCase();
2108
- return q === "" ? [...props.sessions] : props.sessions.filter((s) => s.title.toLowerCase().includes(q));
2109
- }, [props.sessions, query]);
2110
- const teach = async () => {
2111
- if (picked === "" || busy) return;
2112
- setBusy(true);
2113
- try {
2114
- const message = await props.onTeach(picked, note);
2115
- if (message !== null) setError(message);
2116
- else props.onClose();
2117
- } catch (e) {
2118
- setError(e instanceof Error ? e.message : String(e));
2119
- } finally {
2120
- setBusy(false);
2121
- }
2122
- };
2123
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "ah-dialog-mask", role: "dialog", "aria-label": `\u6559 ${props.botName} \u4E00\u62DB`, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "ah-dialog ah-dialog-wide", children: [
2124
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("h3", { children: [
2125
- "\u6559 ",
2126
- props.botName,
2127
- " \u4E00\u62DB"
2128
- ] }),
2129
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "ah-right-kv", children: "\u9009\u4E00\u4E2A\u4F60\u6700\u8FD1\u505A\u8FC7\u7684\u4F1A\u8BDD\uFF0C\u5B83\u4F1A\u8BFB\u8BB0\u5F55\u5199\u6210\u5957\u8DEF\uFF0C\u4EE5\u540E\u7167\u505A\uFF1B\u5B66\u5B8C\u4F1A\u5728\u7EBF\u7A0B\u91CC\u590D\u8FF0\u6B65\u9AA4\u8BF7\u4F60\u7EA0\u6B63\u3002" }),
2130
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("input", { className: "ah-teach-search", placeholder: "\u641C\u7D22\u4F1A\u8BDD\u6807\u9898", value: query, onChange: (e) => setQuery(e.target.value) }),
2131
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("ul", { className: "ah-teach-list", children: [
2132
- filtered.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("li", { className: "ah-empty", children: "\u6CA1\u6709\u627E\u5230\u4F1A\u8BDD\u3002\u5148\u5728 DSH \u91CC\u505A\u4E00\u904D\uFF0C\u518D\u56DE\u6765\u6559\u5B83\u3002" }) : null,
2133
- filtered.slice(0, 12).map((s) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("label", { className: picked === s.sessionId ? "ah-teach-picked" : "", children: [
2134
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("input", { type: "radio", name: "ah-teach-session", checked: picked === s.sessionId, onChange: () => setPicked(s.sessionId) }),
2135
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "ah-teach-title", children: s.title }),
2136
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "ah-right-kv", children: s.updatedText })
2137
- ] }) }, s.sessionId))
2138
- ] }),
2139
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("textarea", { className: "ah-teach-note", rows: 2, value: note, onChange: (e) => setNote(e.target.value), placeholder: "\u4E00\u53E5\u8BF4\u660E\uFF1A\u628A\u5B83\u5B66\u6210\u4EC0\u4E48\u5957\u8DEF" }),
2140
- error ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "ah-settings-error", role: "alert", children: error }) : null,
2141
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "ah-dialog-actions", children: [
2142
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { type: "button", className: "ah-btn", onClick: props.onClose, disabled: busy, children: "\u53D6\u6D88" }),
2143
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { type: "button", className: "ah-btn ah-btn-primary", disabled: busy || picked === "", onClick: () => void teach(), children: busy ? "\u63D0\u4EA4\u4E2D\u2026" : "\u4EA4\u7ED9\u5B83\u5B66" })
2144
- ] })
2145
- ] }) });
2146
- }
2147
-
2148
- // src/client/components/ErrorBoundary.tsx
2149
- var import_react10 = require("react");
2150
- var import_jsx_runtime12 = require("react/jsx-runtime");
2151
- var BlockBoundary = class extends import_react10.Component {
2152
- constructor() {
2153
- super(...arguments);
2154
- __publicField(this, "state", { failed: false });
2155
- }
2156
- /** 出错只记日志、不出插槽崩溃。 */
2157
- static getDerivedStateFromError() {
2158
- return { failed: true };
2159
- }
2160
- componentDidCatch(error) {
2161
- console.error(`[agent-hub] \u754C\u9762\u5757\u300C${this.props.label}\u300D\u6E32\u67D3\u51FA\u9519:`, error);
2162
- }
2163
- render() {
2164
- if (this.state.failed) {
2165
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "ah-empty", role: "alert", children: "\u8FD9\u5757\u6CA1\u663E\u793A\u51FA\u6765\uFF0C\u5237\u65B0\u8BD5\u8BD5\u3002" });
2166
- }
2167
- return this.props.children;
2168
- }
2169
- };
2170
-
2171
- // src/client/pages/HubPage.tsx
2172
- var import_jsx_runtime13 = require("react/jsx-runtime");
2173
- function useSlowLoading(loading) {
2174
- const [slow, setSlow] = (0, import_react11.useState)(false);
2175
- (0, import_react11.useEffect)(() => {
2176
- if (!loading) {
2177
- setSlow(false);
2178
- return;
2179
- }
2180
- const timer = setTimeout(() => setSlow(true), 300);
2181
- return () => clearTimeout(timer);
2182
- }, [loading]);
2183
- return slow;
2184
- }
2185
- function HubPage(props) {
2186
- const hub = props.hub;
2187
- const state = useStore(hub);
2188
- const [rightOpen, setRightOpen] = (0, import_react11.useState)(true);
2189
- const [busyInboxId, setBusyInboxId] = (0, import_react11.useState)(null);
2190
- const [threadError, setThreadError] = (0, import_react11.useState)(null);
2191
- const [wizardOpen, setWizardOpen] = (0, import_react11.useState)(false);
2192
- const [teachOpen, setTeachOpen] = (0, import_react11.useState)(false);
2193
- const [notice, setNotice] = (0, import_react11.useState)(null);
2194
- const [noticeAutoClearBot, setNoticeAutoClearBot] = (0, import_react11.useState)(null);
2195
- const selectedThread = state.selectedThread;
2196
- const view = state.view;
2197
- (0, import_react11.useEffect)(() => {
2198
- void hub.refreshAll();
2199
- void hub.refreshSettings();
2200
- }, [hub]);
2201
- (0, import_react11.useEffect)(() => {
2202
- if (state.connectionDown) return;
2203
- setThreadError(null);
2204
- void hub.refreshThread(selectedThread).catch(() => setThreadError("\u7EBF\u7A0B\u52A0\u8F7D\u5931\u8D25\uFF08\u63D2\u4EF6\u53EF\u80FD\u505C\u7528\u6216\u5728\u91CD\u8F7D\uFF09"));
2205
- }, [hub, selectedThread, state.connectionDown]);
2206
- const overview = state.overview;
2207
- const selectedBot = overview?.bots.find((b) => b.id === selectedThread) ?? null;
2208
- (0, import_react11.useEffect)(() => {
2209
- if (selectedBot && !state.bots[selectedBot.id]) void hub.refreshBot(selectedBot.id);
2210
- }, [hub, selectedBot, state.bots]);
2211
- (0, import_react11.useEffect)(() => {
2212
- if (teachOpen) void hub.refreshRecentSessions();
2213
- }, [hub, teachOpen]);
2214
- (0, import_react11.useEffect)(() => {
2215
- if (noticeAutoClearBot === null || notice === null) return;
2216
- const thread = state.threads[noticeAutoClearBot];
2217
- if (thread?.messages.some((m) => m.kind === "reply")) {
2218
- setNotice(null);
2219
- setNoticeAutoClearBot(null);
2220
- return;
2221
- }
2222
- const wakes = state.bots[noticeAutoClearBot]?.wakes;
2223
- if (wakes !== void 0) {
2224
- const trial = wakes.find((w) => w.variant === "trial");
2225
- if (trial !== void 0 && !["delivering", "running"].includes(trial.status)) {
2226
- setNotice(null);
2227
- setNoticeAutoClearBot(null);
2228
- }
2229
- }
2230
- }, [notice, noticeAutoClearBot, state.threads, state.bots]);
2231
- (0, import_react11.useEffect)(() => {
2232
- if (!state.connectionDown || state.downReason !== "starting") return;
2233
- const timer = setInterval(() => {
2234
- void hub.refreshAll().catch(() => void 0);
2235
- void hub.refreshSettings().catch(() => void 0);
2236
- }, 2e3);
2237
- return () => clearInterval(timer);
2238
- }, [hub, state.connectionDown, state.downReason]);
2239
- const overviewLoading = overview === null;
2240
- const showSkeleton = useSlowLoading(overviewLoading || view === "main" && state.threads[selectedThread] === void 0 && !threadError);
2241
- const cardActions = {
2242
- onPick: async (card, option, note) => {
2243
- setBusyInboxId(card.inboxId);
2244
- try {
2245
- const submission = cardSubmission(card, option, note);
2246
- if ("error" in submission) return submission.error;
2247
- const result = await hub.command(submission.name, submission.input);
2248
- if (!result.ok) return result.error;
2249
- await Promise.all([hub.refreshInbox(), hub.refreshOverview()]);
2250
- return null;
2251
- } catch (e) {
2252
- return e instanceof Error ? e.message : String(e);
2253
- } finally {
2254
- setBusyInboxId(null);
2255
- }
2256
- }
2257
- };
2258
- const header = selectedThread === "team" ? null : selectedBot ? {
2259
- name: selectedBot.name,
2260
- roleLabel: selectedBot.roleLabel,
2261
- activityText: selectedBot.activityText,
2262
- nextThinkText: selectedBot.nextThinkText,
2263
- lifecycle: selectedBot.lifecycle,
2264
- thinking: selectedBot.activity === "thinking"
2265
- } : { name: state.threads[selectedThread]?.title ?? "\u961F\u53CB", roleLabel: null, activityText: null, nextThinkText: null, lifecycle: null, thinking: false };
2266
- const interrupt = () => {
2267
- if (!selectedBot) return;
2268
- void (async () => {
2269
- const result = await hub.command("interruptBot", { botId: selectedBot.id });
2270
- setNotice(result.ok ? "\u5DF2\u6253\u65AD\uFF1B\u5B83\u4F1A\u5728\u4E0B\u6B21\u9192\u6765\u65F6\u63A5\u7740\u5904\u7406\u3002" : result.error);
2271
- await Promise.all([hub.refreshThread(selectedBot.id), hub.refreshOverview()]);
2272
- })();
2273
- };
2274
- const openSession = () => {
2275
- const sessionId = selectedBot ? state.bots[selectedBot.id]?.profile.sessionId : null;
2276
- if (!sessionId) {
2277
- setNotice("\u5B83\u8FD8\u6CA1\u6709\u5DE5\u4F5C\u4F1A\u8BDD\u3002");
2278
- return;
2279
- }
2280
- const result = openBotSessionWith(props.clientCtx, sessionId);
2281
- setNotice(OPEN_SESSION_HINTS[result]);
2282
- };
2283
- const togglePause = () => {
2284
- if (!selectedBot) return;
2285
- const picked = pauseResumeAction(selectedBot.lifecycle);
2286
- if (picked === null) return;
2287
- const action = picked;
2288
- void (async () => {
2289
- const result = await hub.command("lifecycleAction", { botId: selectedBot.id, action });
2290
- setNotice(result.ok ? action === "pause" ? "\u5DF2\u6682\u505C\uFF08\u671F\u95F4\u7684\u53D8\u5316\u90FD\u5B58\u7740\uFF09" : "\u5DF2\u6062\u590D" : result.error);
2291
- await Promise.all([hub.refreshOverview(), hub.refreshBot(selectedBot.id)]);
2292
- })();
2293
- };
2294
- const saveSettings = async (patch) => {
2295
- const result = await hub.command("updateSettings", patch);
2296
- if (result.ok) await hub.refreshSettings();
2297
- return result.ok ? null : result.error;
2298
- };
2299
- const lifecycleAll = async (target) => {
2300
- if (!overview) return "\u603B\u89C8\u8FD8\u6CA1\u52A0\u8F7D\u51FA\u6765\uFF0C\u7A0D\u7B49\u518D\u8BD5";
2301
- const action = target === "paused" ? "pause" : "resume";
2302
- const targets = lifecycleAllTargets(overview.bots, target).map((id) => overview.bots.find((b) => b.id === id));
2303
- let failed = 0;
2304
- for (const bot of targets) {
2305
- const result = await hub.command("lifecycleAction", { botId: bot.id, action });
2306
- if (!result.ok) failed += 1;
2307
- }
2308
- await hub.refreshOverview();
2309
- return failed === 0 ? null : `\u6709 ${failed} \u4E2A\u961F\u53CB\u64CD\u4F5C\u6CA1\u6210\u529F\uFF0C\u518D\u8BD5\u4E00\u6B21\u6216\u5230\u5B83\u7684\u8BE6\u60C5\u91CC\u5355\u72EC\u64CD\u4F5C`;
2310
- };
2311
- if (state.connectionDown) {
2312
- const hint = state.downReason === "disabled" ? "\u4E2D\u5FC3\u5DF2\u505C\u7528\uFF1A\u5220\u9664 ~/AgentHub/.hub/DISABLED \u540E\u91CD\u542F DSH \u5373\u53EF\u6062\u590D\u3002" : state.downReason === "starting" ? "\u4E2D\u5FC3\u6B63\u5728\u542F\u52A8\u2026\u2026\u7A0D\u7B49\u7247\u523B\u4F1A\u81EA\u52A8\u6062\u590D\u3002" : state.downReason === "failed" ? `\u4E2D\u5FC3\u542F\u52A8\u5931\u8D25\uFF1A${state.downMessage ?? "\u539F\u56E0\u672A\u77E5\uFF0C\u770B DSH \u65E5\u5FD7"}\u3002\u4FEE\u590D\u540E\u91CD\u542F DSH \u518D\u8BD5\u3002` : "\u8FDE\u4E0D\u4E0A Agent \u4E2D\u5FC3\uFF08\u63D2\u4EF6\u53EF\u80FD\u505C\u7528\u6216\u5728\u91CD\u8F7D\uFF09\u3002";
2313
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "ah-page ah-page-down", "data-plugin-page": "agent-hub", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "ah-down", children: [
2314
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { children: hint }),
2315
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { type: "button", className: "ah-btn", onClick: () => void hub.refreshAll(), children: "\u91CD\u8BD5" })
2316
- ] }) });
2317
- }
2318
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "ah-page", "data-plugin-page": "agent-hub", children: [
2319
- overview ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(BlockBoundary, { label: "\u9876\u680F", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2320
- TopBar,
2321
- {
2322
- overview,
2323
- view,
2324
- onOpenInbox: () => {
2325
- hub.setView("inbox");
2326
- void hub.refreshInbox();
2327
- },
2328
- onOpenActivity: () => {
2329
- hub.setView("activity");
2330
- void hub.refreshActivity();
2331
- },
2332
- onOpenSettings: () => {
2333
- hub.setView("settings");
2334
- void hub.refreshSettings();
2335
- },
2336
- onNewBot: () => setWizardOpen(true),
2337
- onBack: () => hub.setView("main")
2338
- }
2339
- ) }) : showSkeleton ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("header", { className: "ah-topbar", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Skeleton, { rows: 1 }) }) : null,
2340
- wizardOpen && overview ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(BlockBoundary, { label: "\u65B0\u5EFA\u961F\u53CB", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2341
- NewBotWizard,
2342
- {
2343
- existingBots: overview.bots,
2344
- modelOptions: state.settings?.modelOptions ?? [],
2345
- modelOptionsError: state.settings ? null : state.settingsError,
2346
- onCreate: async (input, color) => {
2347
- const created = await hub.command("createBot", input);
2348
- if (!created.ok) return { error: created.error };
2349
- const botId = created.data?.id;
2350
- if (botId === void 0) return { error: "\u521B\u5EFA\u6210\u529F\u4F46\u6CA1\u6709\u8FD4\u56DE\u673A\u5668\u4EBA id" };
2351
- const colored = await hub.command("updateBot", { botId, patch: { color } });
2352
- if (!colored.ok) console.error("[agent-hub] \u4FDD\u5B58\u5934\u50CF\u989C\u8272\u5931\u8D25:", colored.error);
2353
- return { botId };
2354
- },
2355
- onOpenThread: (botId) => {
2356
- setWizardOpen(false);
2357
- hub.selectThread(botId);
2358
- setNotice("\u6B63\u5728\u8BD5\u8DD1\uFF1A\u5B83\u4F1A\u5148\u505A\u81EA\u6211\u4ECB\u7ECD\uFF0C\u7A0D\u7B49\u7247\u523B\u3002");
2359
- setNoticeAutoClearBot(botId);
2360
- void hub.refreshOverview();
2361
- },
2362
- onClose: () => setWizardOpen(false)
2363
- }
2364
- ) }) : null,
2365
- teachOpen && selectedBot ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(BlockBoundary, { label: "\u6559\u5B83\u4E00\u62DB", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2366
- TeachDialog,
2367
- {
2368
- botName: selectedBot.name,
2369
- sessions: state.recentSessions,
2370
- onTeach: async (sessionId, note) => {
2371
- const result = await hub.command("teach", { botId: selectedBot.id, sessionId, note });
2372
- if (!result.ok) return result.error;
2373
- setTeachOpen(false);
2374
- await Promise.all([hub.refreshThread(selectedBot.id), hub.refreshBot(selectedBot.id)]);
2375
- return null;
2376
- },
2377
- onClose: () => setTeachOpen(false)
2378
- }
2379
- ) }) : null,
2380
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: `ah-body${rightOpen && selectedBot && view === "main" ? " ah-body-right" : ""}`, children: [
2381
- overview ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(BlockBoundary, { label: "\u961F\u53CB\u5217\u8868", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2382
- BotList,
2383
- {
2384
- overview,
2385
- selected: view === "main" ? selectedThread : "",
2386
- onSelect: (threadId) => hub.selectThread(threadId),
2387
- onOpenInbox: () => {
2388
- hub.setView("inbox");
2389
- void hub.refreshInbox();
2390
- }
2391
- }
2392
- ) }) : showSkeleton ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Skeleton, { rows: 6 }) : null,
2393
- view === "inbox" ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(BlockBoundary, { label: "\u6536\u4EF6\u7BB1", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2394
- InboxPane,
2395
- {
2396
- inbox: state.inbox,
2397
- loading: state.inbox === null,
2398
- error: null,
2399
- onRetry: () => void hub.refreshInbox(),
2400
- actions: cardActions,
2401
- busyId: busyInboxId
2402
- }
2403
- ) }) : view === "activity" ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(BlockBoundary, { label: "\u6D3B\u52A8\u6D41", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2404
- ActivityPane,
2405
- {
2406
- activity: state.activity,
2407
- bots: overview?.bots ?? [],
2408
- loading: state.activity === null,
2409
- onRetry: () => void hub.refreshActivity(),
2410
- onLoadMore: () => {
2411
- const last = state.activity?.rows.at(-1)?.id;
2412
- if (last) void hub.refreshActivity(last);
2413
- }
2414
- }
2415
- ) }) : view === "settings" ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(BlockBoundary, { label: "\u8BBE\u7F6E", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2416
- SettingsPane,
2417
- {
2418
- settings: state.settings,
2419
- loadError: state.settingsError,
2420
- overview,
2421
- loading: state.settings === null,
2422
- onRetry: () => void hub.refreshSettings(),
2423
- onSave: saveSettings,
2424
- onPauseAll: () => lifecycleAll("paused"),
2425
- onResumeAll: () => lifecycleAll("active")
2426
- }
2427
- ) }) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(BlockBoundary, { label: "\u5BF9\u8BDD\u7EBF\u7A0B", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2428
- ThreadPane,
2429
- {
2430
- thread: state.threads[selectedThread] ?? null,
2431
- loading: state.threads[selectedThread] === void 0,
2432
- header,
2433
- inputPlaceholderName: state.threads[selectedThread]?.title ?? "\u961F\u53CB",
2434
- banner: notice,
2435
- onBannerDismiss: () => {
2436
- setNotice(null);
2437
- setNoticeAutoClearBot(null);
2438
- },
2439
- onLoadEarlier: () => {
2440
- const t = state.threads[selectedThread];
2441
- if (t?.hasMore && t.oldestId) void hub.refreshThread(selectedThread, t.oldestId);
2442
- },
2443
- onSend: (text) => hub.send(selectedThread, text),
2444
- actions: cardActions,
2445
- busyInboxId,
2446
- onRetry: () => void hub.refreshThread(selectedThread),
2447
- error: threadError,
2448
- onTeach: () => setTeachOpen(true),
2449
- onOpenSession: openSession,
2450
- onTogglePause: togglePause,
2451
- onInterrupt: interrupt,
2452
- rightCollapsed: view === "main" && !rightOpen && selectedBot !== null,
2453
- onExpandRight: () => setRightOpen(true)
2454
- },
2455
- selectedThread
2456
- ) }),
2457
- view === "main" && rightOpen ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(BlockBoundary, { label: "\u8BE6\u60C5", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2458
- RightPanel,
2459
- {
2460
- bot: selectedBot ? state.bots[selectedBot.id] ?? null : null,
2461
- modelOptions: state.settings?.modelOptions ?? [],
2462
- modelOptionsError: state.settings ? null : state.settingsError,
2463
- onRetrySettings: () => void hub.refreshSettings(),
2464
- clientCtx: props.clientCtx,
2465
- run: (name, input) => hub.command(name, input),
2466
- onRefresh: () => {
2467
- if (selectedBot) void hub.refreshBot(selectedBot.id);
2468
- void hub.refreshOverview();
2469
- },
2470
- onTeach: () => setTeachOpen(true),
2471
- onCollapse: () => setRightOpen(false)
2472
- },
2473
- selectedBot?.id ?? "none"
2474
- ) }) : null
2475
- ] })
2476
- ] });
2477
- }
2478
-
2479
- // src/client/index.tsx
2480
- var import_jsx_runtime14 = require("react/jsx-runtime");
2481
- var inject = ["slots", "locale"];
2482
- function HubEntryIcon(props) {
2483
- const size = props.size ?? 16;
2484
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("svg", { viewBox: "0 0 16 16", width: size, height: size, "aria-hidden": "true", className: "ah-entry-icon", children: [
2485
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("circle", { cx: "5.5", cy: "5", r: "2.2", fill: "currentColor" }),
2486
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("circle", { cx: "10.8", cy: "6.2", r: "1.7", fill: "currentColor", opacity: "0.85" }),
2487
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("path", { d: "M1.8 13.2c0-2.1 1.7-3.6 3.7-3.6s3.7 1.5 3.7 3.6z", fill: "currentColor" }),
2488
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("path", { d: "M9.9 13.2c0.2-1.7 1.2-2.9 2.7-2.9 1.2 0 2.2 0.8 2.5 2z", fill: "currentColor", opacity: "0.85" })
2489
- ] });
2490
- }
2491
- function HubToastOverlay(props) {
2492
- const state = useStore(props.hub);
2493
- if (state.toasts.length === 0) return null;
2494
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "ah-toast-layer", role: "status", children: state.toasts.map((toast) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "ah-toast", children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2495
- "button",
2496
- {
2497
- type: "button",
2498
- className: "ah-toast-btn",
2499
- onClick: () => {
2500
- props.hub.dismissToast(toast.id);
2501
- props.hub.setView("inbox");
2502
- void props.hub.refreshInbox();
2503
- try {
2504
- const layout = props.clientCtx?.get?.("layout");
2505
- layout?.selectPanel?.("agent-hub");
2506
- } catch {
2507
- }
2508
- },
2509
- children: toast.text
2510
- }
2511
- ) }, toast.id)) });
2512
- }
2513
- function apply(ctx) {
2514
- const hub = createHubStore();
2515
- try {
2516
- ctx.effect(() => {
2517
- const tag = document.createElement("style");
2518
- tag.dataset.plugin = "dsh-buddy";
2519
- tag.textContent = styles_default;
2520
- document.head.appendChild(tag);
2521
- return () => tag.remove();
2522
- }, "dsh-buddy: styles");
2523
- } catch (error) {
2524
- console.error("[agent-hub] \u63D2\u5165\u6837\u5F0F\u5931\u8D25:", error);
2525
- }
2526
- try {
2527
- ctx.effect(() => {
2528
- const close = connectEvents(hub);
2529
- void hub.refreshAll();
2530
- return close;
2531
- }, "dsh-buddy: sse");
2532
- ctx.on("connection/reset", () => {
2533
- void hub.refreshAll();
2534
- });
2535
- } catch (error) {
2536
- console.error("[agent-hub] \u5EFA\u7ACB\u63A8\u9001\u8FDE\u63A5\u5931\u8D25:", error);
2537
- }
2538
- try {
2539
- ctx.slots.inject("main", () => ctx.slots.register({ name: "main", key: "agent-hub" }, () => HubPage({ hub, clientCtx: ctx })));
2540
- } catch (error) {
2541
- console.error("[agent-hub] \u6CE8\u518C main \u63D2\u69FD\u5931\u8D25:", error);
2542
- }
2543
- try {
2544
- ctx.slots.inject("sidebar.panellist", () => ctx.slots.register(
2545
- // label 是 thunk:每次取值时从仓库读待处理收件箱数(入口行由宿主渲染)。
2546
- { name: "sidebar.panellist", id: "agent-hub", order: 15, label: () => entryLabel(hub.getSnapshot().overview?.inbox.total ?? 0) },
2547
- (props) => HubEntryIcon(props ?? {})
2548
- ));
2549
- } catch (error) {
2550
- console.error("[agent-hub] \u6CE8\u518C\u4FA7\u680F\u5165\u53E3\u5931\u8D25:", error);
2551
- }
2552
- try {
2553
- ctx.slots.inject("shell.overlay", () => ctx.slots.register(
2554
- { name: "shell.overlay", id: "dsh-buddy-toast", order: 100 },
2555
- () => HubToastOverlay({ hub, clientCtx: ctx })
2556
- ));
2557
- } catch (error) {
2558
- console.error("[agent-hub] \u6CE8\u518C\u5E94\u7528\u5185\u63D0\u793A\u63D2\u69FD\u5931\u8D25:", error);
2559
- }
2560
- }
2561
- return module.exports } })