botmux 2.63.1 → 2.65.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +1 -1
- package/README.md +3 -3
- package/dist/adapters/backend/herdr-backend.d.ts +8 -1
- package/dist/adapters/backend/herdr-backend.d.ts.map +1 -1
- package/dist/adapters/backend/herdr-backend.js +15 -2
- package/dist/adapters/backend/herdr-backend.js.map +1 -1
- package/dist/adapters/backend/tmux-backend.d.ts +17 -1
- package/dist/adapters/backend/tmux-backend.d.ts.map +1 -1
- package/dist/adapters/backend/tmux-backend.js +25 -4
- package/dist/adapters/backend/tmux-backend.js.map +1 -1
- package/dist/adapters/backend/types.d.ts +14 -0
- package/dist/adapters/backend/types.d.ts.map +1 -1
- package/dist/adapters/backend/types.js.map +1 -1
- package/dist/adapters/backend/zellij-backend.d.ts +12 -1
- package/dist/adapters/backend/zellij-backend.d.ts.map +1 -1
- package/dist/adapters/backend/zellij-backend.js +25 -8
- package/dist/adapters/backend/zellij-backend.js.map +1 -1
- package/dist/bot-registry.d.ts +40 -0
- package/dist/bot-registry.d.ts.map +1 -1
- package/dist/bot-registry.js +30 -0
- package/dist/bot-registry.js.map +1 -1
- package/dist/cli/send-dispatch.d.ts +23 -0
- package/dist/cli/send-dispatch.d.ts.map +1 -0
- package/dist/cli/send-dispatch.js +23 -0
- package/dist/cli/send-dispatch.js.map +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +141 -58
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +8 -6
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +8 -6
- package/dist/config.js.map +1 -1
- package/dist/core/ask-broker.d.ts +33 -0
- package/dist/core/ask-broker.d.ts.map +1 -1
- package/dist/core/ask-broker.js +58 -0
- package/dist/core/ask-broker.js.map +1 -1
- package/dist/core/ask-hook/claude-code.d.ts.map +1 -1
- package/dist/core/ask-hook/claude-code.js +15 -9
- package/dist/core/ask-hook/claude-code.js.map +1 -1
- package/dist/core/ask-hook/codex.d.ts.map +1 -1
- package/dist/core/ask-hook/codex.js +2 -1
- package/dist/core/ask-hook/codex.js.map +1 -1
- package/dist/core/ask-hook/opencode.d.ts.map +1 -1
- package/dist/core/ask-hook/opencode.js +9 -6
- package/dist/core/ask-hook/opencode.js.map +1 -1
- package/dist/core/ask-hook/types.d.ts +3 -1
- package/dist/core/ask-hook/types.d.ts.map +1 -1
- package/dist/core/ask-types.d.ts +13 -6
- package/dist/core/ask-types.d.ts.map +1 -1
- package/dist/core/ask-types.js.map +1 -1
- package/dist/core/command-handler.d.ts.map +1 -1
- package/dist/core/command-handler.js +255 -4
- package/dist/core/command-handler.js.map +1 -1
- package/dist/core/daemon-heartbeat.d.ts +15 -0
- package/dist/core/daemon-heartbeat.d.ts.map +1 -0
- package/dist/core/daemon-heartbeat.js +83 -0
- package/dist/core/daemon-heartbeat.js.map +1 -0
- package/dist/core/dashboard-ipc-server.d.ts.map +1 -1
- package/dist/core/dashboard-ipc-server.js +80 -33
- package/dist/core/dashboard-ipc-server.js.map +1 -1
- package/dist/core/dispatch.d.ts +1 -23
- package/dist/core/dispatch.d.ts.map +1 -1
- package/dist/core/dispatch.js +1 -17
- package/dist/core/dispatch.js.map +1 -1
- package/dist/core/idle-worker-sweeper.d.ts +13 -0
- package/dist/core/idle-worker-sweeper.d.ts.map +1 -0
- package/dist/core/idle-worker-sweeper.js +42 -0
- package/dist/core/idle-worker-sweeper.js.map +1 -0
- package/dist/core/maintenance-schedule.d.ts +34 -0
- package/dist/core/maintenance-schedule.d.ts.map +1 -0
- package/dist/core/maintenance-schedule.js +72 -0
- package/dist/core/maintenance-schedule.js.map +1 -0
- package/dist/core/maintenance.d.ts +43 -0
- package/dist/core/maintenance.d.ts.map +1 -0
- package/dist/core/maintenance.js +160 -0
- package/dist/core/maintenance.js.map +1 -0
- package/dist/core/reply-target.d.ts +23 -0
- package/dist/core/reply-target.d.ts.map +1 -0
- package/dist/core/reply-target.js +47 -0
- package/dist/core/reply-target.js.map +1 -0
- package/dist/core/restart-report.d.ts +49 -0
- package/dist/core/restart-report.d.ts.map +1 -0
- package/dist/core/restart-report.js +98 -0
- package/dist/core/restart-report.js.map +1 -0
- package/dist/core/scheduler.d.ts.map +1 -1
- package/dist/core/scheduler.js +20 -0
- package/dist/core/scheduler.js.map +1 -1
- package/dist/core/session-manager.d.ts +26 -10
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +113 -31
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/session-marker.d.ts +13 -0
- package/dist/core/session-marker.d.ts.map +1 -0
- package/dist/core/session-marker.js +55 -0
- package/dist/core/session-marker.js.map +1 -0
- package/dist/core/types.d.ts +20 -1
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/core/worker-budget.d.ts +19 -0
- package/dist/core/worker-budget.d.ts.map +1 -0
- package/dist/core/worker-budget.js +50 -0
- package/dist/core/worker-budget.js.map +1 -0
- package/dist/core/worker-pool.d.ts +5 -2
- package/dist/core/worker-pool.d.ts.map +1 -1
- package/dist/core/worker-pool.js +105 -12
- package/dist/core/worker-pool.js.map +1 -1
- package/dist/daemon.d.ts.map +1 -1
- package/dist/daemon.js +252 -38
- package/dist/daemon.js.map +1 -1
- package/dist/dashboard/public-redact.d.ts +2 -1
- package/dist/dashboard/public-redact.d.ts.map +1 -1
- package/dist/dashboard/public-redact.js +3 -1
- package/dist/dashboard/public-redact.js.map +1 -1
- package/dist/dashboard/registry.d.ts +2 -0
- package/dist/dashboard/registry.d.ts.map +1 -1
- package/dist/dashboard/registry.js.map +1 -1
- package/dist/dashboard/web/bot-defaults.d.ts.map +1 -1
- package/dist/dashboard/web/bot-defaults.js +123 -4
- package/dist/dashboard/web/bot-defaults.js.map +1 -1
- package/dist/dashboard/web/groups.d.ts.map +1 -1
- package/dist/dashboard/web/groups.js +8 -3
- package/dist/dashboard/web/groups.js.map +1 -1
- package/dist/dashboard/web/i18n.d.ts.map +1 -1
- package/dist/dashboard/web/i18n.js +44 -0
- package/dist/dashboard/web/i18n.js.map +1 -1
- package/dist/dashboard/web/overview.d.ts.map +1 -1
- package/dist/dashboard/web/overview.js +6 -4
- package/dist/dashboard/web/overview.js.map +1 -1
- package/dist/dashboard/web/roles.d.ts.map +1 -1
- package/dist/dashboard/web/roles.js +3 -2
- package/dist/dashboard/web/roles.js.map +1 -1
- package/dist/dashboard/web/sessions.js +2 -2
- package/dist/dashboard/web/sessions.js.map +1 -1
- package/dist/dashboard/web/settings.d.ts.map +1 -1
- package/dist/dashboard/web/settings.js +84 -13
- package/dist/dashboard/web/settings.js.map +1 -1
- package/dist/dashboard/web/ui.d.ts +33 -0
- package/dist/dashboard/web/ui.d.ts.map +1 -1
- package/dist/dashboard/web/ui.js +84 -0
- package/dist/dashboard/web/ui.js.map +1 -1
- package/dist/dashboard-web/app.js +573 -503
- package/dist/dashboard-web/style.css +25 -0
- package/dist/dashboard.js +88 -8
- package/dist/dashboard.js.map +1 -1
- package/dist/global-config.d.ts +46 -0
- package/dist/global-config.d.ts.map +1 -1
- package/dist/global-config.js +115 -0
- package/dist/global-config.js.map +1 -1
- package/dist/i18n/en.d.ts.map +1 -1
- package/dist/i18n/en.js +72 -1
- package/dist/i18n/en.js.map +1 -1
- package/dist/i18n/zh.d.ts.map +1 -1
- package/dist/i18n/zh.js +72 -1
- package/dist/i18n/zh.js.map +1 -1
- package/dist/im/lark/ask-card.d.ts.map +1 -1
- package/dist/im/lark/ask-card.js +15 -1
- package/dist/im/lark/ask-card.js.map +1 -1
- package/dist/im/lark/card-builder.d.ts +17 -0
- package/dist/im/lark/card-builder.d.ts.map +1 -1
- package/dist/im/lark/card-builder.js +146 -0
- package/dist/im/lark/card-builder.js.map +1 -1
- package/dist/im/lark/card-handler.d.ts.map +1 -1
- package/dist/im/lark/card-handler.js +119 -4
- package/dist/im/lark/card-handler.js.map +1 -1
- package/dist/im/lark/client.d.ts +14 -2
- package/dist/im/lark/client.d.ts.map +1 -1
- package/dist/im/lark/client.js +59 -5
- package/dist/im/lark/client.js.map +1 -1
- package/dist/im/lark/event-dispatcher.d.ts +7 -17
- package/dist/im/lark/event-dispatcher.d.ts.map +1 -1
- package/dist/im/lark/event-dispatcher.js +174 -50
- package/dist/im/lark/event-dispatcher.js.map +1 -1
- package/dist/im/lark/forwarded-renderer.d.ts.map +1 -1
- package/dist/im/lark/forwarded-renderer.js +10 -3
- package/dist/im/lark/forwarded-renderer.js.map +1 -1
- package/dist/im/lark/merge-forward.d.ts.map +1 -1
- package/dist/im/lark/merge-forward.js +33 -8
- package/dist/im/lark/merge-forward.js.map +1 -1
- package/dist/im/lark/message-parser.d.ts +1 -0
- package/dist/im/lark/message-parser.d.ts.map +1 -1
- package/dist/im/lark/message-parser.js +1 -0
- package/dist/im/lark/message-parser.js.map +1 -1
- package/dist/im/lark/reply-mode-command.d.ts +2 -0
- package/dist/im/lark/reply-mode-command.d.ts.map +1 -0
- package/dist/im/lark/reply-mode-command.js +102 -0
- package/dist/im/lark/reply-mode-command.js.map +1 -0
- package/dist/services/bot-config-store.d.ts +144 -0
- package/dist/services/bot-config-store.d.ts.map +1 -0
- package/dist/services/bot-config-store.js +241 -0
- package/dist/services/bot-config-store.js.map +1 -0
- package/dist/services/card-prefs-store.d.ts +5 -0
- package/dist/services/card-prefs-store.d.ts.map +1 -1
- package/dist/services/card-prefs-store.js +47 -0
- package/dist/services/card-prefs-store.js.map +1 -1
- package/dist/services/chat-reply-mode-store.d.ts +28 -0
- package/dist/services/chat-reply-mode-store.d.ts.map +1 -0
- package/dist/services/chat-reply-mode-store.js +115 -0
- package/dist/services/chat-reply-mode-store.js.map +1 -0
- package/dist/services/groups-store.d.ts +2 -0
- package/dist/services/groups-store.d.ts.map +1 -1
- package/dist/services/groups-store.js +1 -0
- package/dist/services/groups-store.js.map +1 -1
- package/dist/services/hook-runner.d.ts +43 -0
- package/dist/services/hook-runner.d.ts.map +1 -0
- package/dist/services/hook-runner.js +394 -0
- package/dist/services/hook-runner.js.map +1 -0
- package/dist/services/observed-bots-store.d.ts.map +1 -1
- package/dist/services/observed-bots-store.js +5 -0
- package/dist/services/observed-bots-store.js.map +1 -1
- package/dist/services/restart-intent-store.d.ts +26 -0
- package/dist/services/restart-intent-store.d.ts.map +1 -0
- package/dist/services/restart-intent-store.js +84 -0
- package/dist/services/restart-intent-store.js.map +1 -0
- package/dist/services/session-lifecycle-hooks.d.ts +10 -0
- package/dist/services/session-lifecycle-hooks.d.ts.map +1 -0
- package/dist/services/session-lifecycle-hooks.js +66 -0
- package/dist/services/session-lifecycle-hooks.js.map +1 -0
- package/dist/services/session-store.d.ts +6 -0
- package/dist/services/session-store.d.ts.map +1 -1
- package/dist/services/session-store.js +25 -0
- package/dist/services/session-store.js.map +1 -1
- package/dist/skills/definitions.d.ts.map +1 -1
- package/dist/skills/definitions.js +28 -3
- package/dist/skills/definitions.js.map +1 -1
- package/dist/types.d.ts +33 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/install-info.d.ts +13 -0
- package/dist/utils/install-info.d.ts.map +1 -0
- package/dist/utils/install-info.js +56 -0
- package/dist/utils/install-info.js.map +1 -0
- package/dist/utils/listen-with-probe.d.ts +26 -0
- package/dist/utils/listen-with-probe.d.ts.map +1 -0
- package/dist/utils/listen-with-probe.js +64 -0
- package/dist/utils/listen-with-probe.js.map +1 -0
- package/dist/utils/web-terminal-listen.d.ts +30 -0
- package/dist/utils/web-terminal-listen.d.ts.map +1 -0
- package/dist/utils/web-terminal-listen.js +81 -0
- package/dist/utils/web-terminal-listen.js.map +1 -0
- package/dist/worker.js +71 -44
- package/dist/worker.js.map +1 -1
- package/dist/workflows/definition.d.ts +30 -30
- package/dist/workflows/events/payloads.d.ts +4 -4
- package/dist/workflows/events/schema.d.ts +156 -156
- package/package.json +1 -1
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
"use strict";(()=>{var kt=class{sessions=new Map;schedules=new Map;online=!0;listeners=new Set;upsertSessions(t){for(let n of t)this.sessions.set(n.sessionId,n);this.emit()}upsertSchedules(t){for(let n of t)this.schedules.set(n.id,n);this.emit()}applySse(t,n){if(t==="session.spawned")this.sessions.set(n.session.sessionId,n.session);else if(t==="session.update"){let s=this.sessions.get(n.sessionId);s&&this.sessions.set(n.sessionId,{...s,...n.patch})}else if(t==="session.exited"){let s=this.sessions.get(n.sessionId);s&&this.sessions.set(n.sessionId,{...s,status:"closed"})}else if(t==="schedule.created")this.schedules.set(n.schedule.id,n.schedule);else if(t==="schedule.updated"){let s=this.schedules.get(n.id);s&&this.schedules.set(n.id,{...s,...n.patch})}else if(t==="schedule.deleted")this.schedules.delete(n.id);else return;this.emit()}setOnline(t){this.online!==t&&(this.online=t,this.emit())}on(t){return this.listeners.add(t),()=>this.listeners.delete(t)}emit(){for(let t of this.listeners)t()}},G=new kt;async function an(){let[e,t]=await Promise.all([fetch("/api/sessions").then(a=>a.json()),fetch("/api/schedules").then(a=>a.json())]);G.upsertSessions(e.sessions??[]),G.upsertSchedules(t.schedules??[]);let n=new EventSource("/events"),s=["session.spawned","session.update","session.exited","schedule.created","schedule.updated","schedule.deleted","schedule.fired","heartbeat"];for(let a of s)n.addEventListener(a,i=>{try{let l=JSON.parse(i.data);G.applySse(a,l.body??l)}catch{}});n.onerror=()=>G.setOnline(!1),n.onopen=()=>G.setOnline(!0)}var $t="botmux.dashboard.locale",Io={"app.name":"botmux","app.subtitle":"\u98DE\u4E66 AI CLI \u63A7\u5236\u53F0","time.secondsAgo":"{value} \u79D2\u524D","time.minutesAgo":"{value} \u5206\u949F\u524D","time.hoursAgo":"{value} \u5C0F\u65F6\u524D","nav.overview":"\u603B\u89C8","nav.sessions":"\u4F1A\u8BDD","nav.groups":"\u7FA4\u7EC4","nav.schedules":"\u5B9A\u65F6","nav.settings":"\u8BBE\u7F6E","nav.botDefaults":"Bot \u914D\u7F6E","status.live":"\u5B9E\u65F6\u8FDE\u63A5","status.disconnected":"\u8FDE\u63A5\u65AD\u5F00","status.system":"\u7CFB\u7EDF","status.light":"\u6D45\u8272","status.dark":"\u6697\u9ED1","status.language":"\u8BED\u8A00","status.theme":"\u4E3B\u9898","theme.base":"\u57FA\u7840","theme.skins":"\u4E3B\u9898\u76AE\u80A4","skin.cyber":"2077","skin.genshin":"\u539F\u795E","skin.fallout":"Fallout","skin.prts":"PRTS","skin.bluearchive":"\u851A\u84DD\u6863\u6848","skin.zzz":"\u7EDD\u533A\u96F6","skin.dragonball":"\u4E03\u9F99\u73E0","skin.ikun":"ikun","botOnboarding.add":"\u6DFB\u52A0\u673A\u5668\u4EBA","botOnboarding.title":"\u6DFB\u52A0\u673A\u5668\u4EBA","botOnboarding.intro":"\u9009\u62E9 CLI \u4E0E\u5DE5\u4F5C\u76EE\u5F55\uFF0C\u626B\u7801\u521B\u5EFA PersonalAgent \u5E94\u7528\u540E\u5199\u5165\u672C\u673A bots.json\uFF0C\u5E76\u81EA\u52A8\u914D\u7F6E\u5F00\u653E\u5E73\u53F0\u6743\u9650\u3002","botOnboarding.cliLabel":"CLI \u9002\u914D\u5668","botOnboarding.dirLabel":"\u5DE5\u4F5C\u76EE\u5F55","botOnboarding.dirPlaceholder":"\u9ED8\u8BA4 ~\uFF08\u5BB6\u76EE\u5F55\uFF09\uFF0C\u9700\u4E3A daemon \u4E3B\u673A\u4E0A\u5DF2\u5B58\u5728\u7684\u76EE\u5F55","botOnboarding.modelLabel":"\u6A21\u578B\uFF08\u53EF\u9009\uFF09","botOnboarding.modelPlaceholder":"\u7559\u7A7A\u4F7F\u7528\u8BE5 CLI \u7684\u9ED8\u8BA4\u6A21\u578B","botOnboarding.startScan":"\u5F00\u59CB\u626B\u7801","botOnboarding.cancel":"\u53D6\u6D88","botOnboarding.starting":"\u6B63\u5728\u751F\u6210\u4E8C\u7EF4\u7801...","botOnboarding.waiting":"\u8BF7\u7528\u98DE\u4E66 App \u626B\u7801\u786E\u8BA4\u521B\u5EFA\u5E94\u7528\u3002","botOnboarding.verifying":"\u626B\u7801\u6210\u529F\uFF0C\u6B63\u5728\u6821\u9A8C\u51ED\u8BC1...","botOnboarding.configuringPermissions":"\u6B63\u5728\u81EA\u52A8\u914D\u7F6E\u5F00\u653E\u5E73\u53F0\u6743\u9650\u2026","botOnboarding.platformScanHint":"\u8BF7\u7528\u98DE\u4E66 App \u518D\u626B\u4E00\u6B21\u7801\u767B\u5F55\u5F00\u653E\u5E73\u53F0\uFF08\u7528\u4E8E\u81EA\u52A8\u5BFC\u5165\u6743\u9650\u3001\u914D\u7F6E\u56DE\u8C03\u3001\u63D0\u4EA4\u7248\u672C\uFF09\u3002","botOnboarding.platformQrAlt":"\u5F00\u653E\u5E73\u53F0\u767B\u5F55\u4E8C\u7EF4\u7801","botOnboarding.completed":"\u673A\u5668\u4EBA\u5DF2\u6DFB\u52A0\u3002","botOnboarding.permissionOk":"\u5DF2\u81EA\u52A8\u5BFC\u5165 {count} \u9879\u6743\u9650\u3002","botOnboarding.permissionSkipped":"\uFF08\u8DF3\u8FC7 {count} \u9879\u5F53\u524D\u79DF\u6237\u76EE\u5F55\u4E2D\u6CA1\u6709\u7684\u6743\u9650\uFF09","botOnboarding.permissionVersion":"\u5DF2\u63D0\u4EA4\u53D1\u5E03\u7248\u672C {version}\u3002","botOnboarding.permissionManual":"\u6743\u9650\u672A\u80FD\u81EA\u52A8\u914D\u7F6E\uFF0C\u8BF7\u624B\u52A8\u5B8C\u6210\u4EE5\u4E0B\u6B65\u9AA4\uFF1A","botOnboarding.metaDir":"\u76EE\u5F55","botOnboarding.failed":"\u6DFB\u52A0\u5931\u8D25","botOnboarding.openLink":"\u6253\u4E0D\u5F00\u626B\u7801\uFF1F\u5728\u6D4F\u89C8\u5668\u4E2D\u6253\u5F00","botOnboarding.close":"\u5173\u95ED","botOnboarding.restartHint":"\u5DF2\u5199\u5165 bots.json\u3002\u6267\u884C pnpm daemon:restart \u540E\u65B0\u673A\u5668\u4EBA\u751F\u6548\u3002","botOnboarding.qrAlt":"\u98DE\u4E66\u626B\u7801\u6DFB\u52A0\u673A\u5668\u4EBA\u4E8C\u7EF4\u7801","overview.title":"\u5DE5\u4F5C\u53F0","overview.subtitle":"\u6570\u5B57\u5458\u5DE5\u5B9E\u65F6\u72B6\u6001 \xB7 \u540C\u6B65\u98DE\u4E66\u8BDD\u9898","overview.team":"AI \u56E2\u961F","overview.teamHint":"\u6BCF\u4F4D\u6570\u5B57\u5458\u5DE5\u7684\u5B9E\u65F6\u72B6\u6001","overview.attention":"\u9700\u8981\u4F60\u5904\u7406","overview.attentionHint":"\u5361\u4F4F\u7684\u4EFB\u52A1\uFF0C\u56DE\u4E2A\u8BDD\u5C31\u80FD\u7EE7\u7EED\u8DD1","overview.activeSessions":"\u6D3B\u8DC3\u4F1A\u8BDD","overview.activeSessionsHint":"\u6B63\u5728\u8FD0\u8F6C\u7684\u4F1A\u8BDD","overview.today":"\u6B64\u523B\u6982\u89C8","overview.todayHint":"\u6D3B\u8DC3\u4F1A\u8BDD\u5206\u5E03","overview.viewAll":"\u67E5\u770B\u5168\u90E8 \u2192","overview.botIdle":"\u5F85\u547D\u4E2D\uFF0C\u968F\u65F6\u53EF\u63A5\u65B0\u4EFB\u52A1","overview.botOffline":"\u79BB\u7EBF \u2014 daemon \u672A\u4E0A\u7EBF","overview.botBusy":"\u6267\u884C\u4E2D \xB7 {count} \u4F1A\u8BDD","overview.botNeedsYou":"\u7B49\u4F60\u56DE\u590D","overview.botReady":"\u5C31\u7EEA","overview.botOff":"\u79BB\u7EBF","overview.sessionsCount":"{count} \u4F1A\u8BDD","overview.lastActive":"{time}\u524D\u6D3B\u8DC3","overview.noAttention":"\u6CA1\u6709\u7B49\u4F60\u5904\u7406\u7684\u4E8B\u9879","strip.pending":"{count} \u4EF6\u5F85\u5904\u7406","strip.longest":"\u6700\u4E45\u5DF2\u7B49 {time} \u2014 {bot} {reason}","strip.handle":"\u7ACB\u5373\u5904\u7406","overview.openSessions":"\u6D3B\u8DC3\u4F1A\u8BDD","overview.workingSessions":"\u5DE5\u4F5C\u4E2D","overview.onlineBots":"\u5728\u7EBF Bot","overview.schedules":"\u5B9A\u65F6\u4EFB\u52A1","overview.groups":"\u7FA4\u804A\u8986\u76D6","overview.enabledSchedules":"\u5DF2\u542F\u7528","overview.total":"\u603B\u8BA1","overview.active":"\u6D3B\u8DC3","overview.daemonRegistry":"daemon \u6CE8\u518C\u8868","overview.chatMatrix":"\u7FA4\u804A\u77E9\u9635","overview.recentSessions":"\u6700\u8FD1\u4F1A\u8BDD","overview.nextSchedules":"\u5373\u5C06\u6267\u884C","overview.noSessions":"\u6682\u65E0\u4F1A\u8BDD\u3002","overview.noSchedules":"\u6682\u65E0\u5B9A\u65F6\u4EFB\u52A1\u3002","sessions.title":"\u4F1A\u8BDD\u63A7\u5236","sessions.subtitle":"\u5B9A\u4F4D\u98DE\u4E66\u8BDD\u9898\u3001\u6253\u5F00 Web Terminal\u3001\u5173\u95ED\u6216\u6062\u590D CLI \u4F1A\u8BDD\u3002","sessions.search":"\u641C\u7D22\u5DE5\u4F5C\u76EE\u5F55 / \u6807\u9898 / ID","sessions.anyStatus":"\u5168\u90E8\u72B6\u6001","sessions.adoptAny":"adopt: \u5168\u90E8","sessions.adoptYes":"adopt: \u662F","sessions.adoptNo":"adopt: \u5426","sessions.activeOnly":"\u4EC5\u6D3B\u8DC3","sessions.closeSelected":"\u5173\u95ED\u9009\u4E2D","sessions.clearSelection":"\u53D6\u6D88\u9009\u62E9","sessions.selectedCount":"\u5DF2\u9009 {count} \u4E2A\u4F1A\u8BDD","sessions.bot":"bot","sessions.cli":"CLI","sessions.chat":"\u7FA4\u804A","sessions.openChat":"\u6253\u5F00\u7FA4\u804A","sessions.status":"\u72B6\u6001","sessions.titleCol":"\u6807\u9898","sessions.workingDir":"\u5DE5\u4F5C\u76EE\u5F55","sessions.created":"\u521B\u5EFA","sessions.last":"\u6700\u8FD1","sessions.adopt":"\u63A5\u5165","sessions.actions":"\u64CD\u4F5C","sessions.details":"\u8BE6\u60C5","sessions.copy":"\u590D\u5236","sessions.copied":"\u5DF2\u590D\u5236","sessions.locate":"\u5B9A\u4F4D\u8BDD\u9898","sessions.locating":"\u53D1\u9001\u4E2D...","sessions.cooldown":"\u51B7\u5374 {seconds}s","sessions.openTerminal":"\u7EC8\u7AEF","sessions.close":"\u5173\u95ED\u4F1A\u8BDD","sessions.resume":"\u6062\u590D\u4F1A\u8BDD","sessions.dismiss":"\u5173\u95ED","sessions.closeConfirm":"\u5173\u95ED\u8FD9\u4E2A\u4F1A\u8BDD\uFF1F","sessions.resumeFailed":"\u6062\u590D\u5931\u8D25","sessions.closeBulkConfirm":"\u5173\u95ED\u9009\u4E2D\u7684 {count} \u4E2A\u4F1A\u8BDD\uFF1F","sessions.empty":"\u6CA1\u6709\u7B26\u5408\u6761\u4EF6\u7684\u4F1A\u8BDD\u3002","sessions.viewMode":"\u4F1A\u8BDD\u89C6\u56FE","sessions.viewBoard":"\u72B6\u6001\u677F","sessions.viewTable":"\u8868\u683C","sessions.selectSession":"\u9009\u62E9\u4F1A\u8BDD","sessions.board.needsYou":"\u9700\u8981\u4F60","sessions.board.needsYouHint":"\u7B49\u5F85\u4ED3\u5E93\u3001TUI \u9009\u62E9\u6216\u989D\u5EA6\u5904\u7406","sessions.board.starting":"\u542F\u52A8\u4E2D","sessions.board.startingHint":"CLI \u6B63\u5728\u521B\u5EFA\u6216\u6062\u590D","sessions.board.working":"\u5E72\u6D3B\u4E2D","sessions.board.workingHint":"\u6B63\u5728\u8F93\u51FA\u3001\u5206\u6790\u6216\u6267\u884C\u5DE5\u5177","sessions.board.idle":"\u7A7A\u95F2","sessions.board.idleHint":"\u53EF\u7EE7\u7EED\u5BF9\u8BDD\u6216\u63A5\u65B0\u4EFB\u52A1","sessions.board.emptyColumn":"\u6682\u65E0\u4F1A\u8BDD","sessions.board.signalRepo":"\u5F85\u9009\u4ED3\u5E93","sessions.board.signalPrompt":"\u7B49\u5F85 TUI \u9009\u62E9","sessions.board.signalLimited":"\u989D\u5EA6\u53D7\u9650","sessions.board.dragHint":"\u62D6\u52A8\u5217\u5934\u8C03\u6574\u5217\u987A\u5E8F","sessions.board.moveLeft":"\u5DE6\u79FB\u6B64\u5217","sessions.board.moveRight":"\u53F3\u79FB\u6B64\u5217","groups.title":"\u7FA4\u7EC4\u4E0E Bot","groups.subtitle":"\u67E5\u770B chat x bot \u8986\u76D6\u77E9\u9635\uFF0C\u7BA1\u7406\u62C9\u7FA4\u3001oncall\u3001\u9000\u7FA4\u548C\u89E3\u6563\u3002","groups.search":"\u641C\u7D22\u7FA4\u540D / ID / owner","groups.missingOnly":"\u4EC5\u7F3A bot","groups.refresh":"\u5237\u65B0","groups.create":"\u65B0\u5EFA\u7FA4","groups.chat":"\u7FA4\u804A","groups.actions":"\u64CD\u4F5C","groups.addBots":"\u6DFB\u52A0 bot","groups.manage":"\u7BA1\u7406","groups.empty":"\u6CA1\u6709\u7B26\u5408\u6761\u4EF6\u7684\u7FA4\u804A\u3002","groups.createTitle":"\u65B0\u5EFA\u7FA4\u804A","groups.createHelp":"\u9009\u62E9\u8981\u9080\u8BF7\u7684 bot\u3002dashboard \u4F1A\u81EA\u52A8\u9009\u62E9\u5728\u7EBF daemon \u4F5C\u4E3A\u521B\u5EFA\u8005\u3002","groups.name":"\u7FA4\u540D","groups.namePlaceholder":"\u4F8B\u5982 AI ChangeLog","groups.bindDir":"\u7ED1\u5B9A\u76EE\u5F55","groups.bindDirHelp":"\u65B0\u8BDD\u9898\u76F4\u63A5\u7528\u8BE5\u76EE\u5F55\u542F\u52A8 CLI\uFF0C\u8DF3\u8FC7 repo \u9009\u62E9\u3002","groups.botPicker":"Bot","groups.createSubmit":"\u521B\u5EFA","groups.cancel":"\u53D6\u6D88","groups.successTitle":"\u7FA4\u521B\u5EFA\u6210\u529F","groups.openGroup":"\u6253\u5F00\u65B0\u7FA4","groups.manageTitle":"\u7BA1\u7406 {name}","groups.owner":"\u7FA4\u4E3B","groups.oncall":"Oncall \u6A21\u5F0F","groups.oncallHelp":"\u5F00\u542F\u540E\uFF0C\u7FA4\u5185\u6210\u5458\u53EF @ \u673A\u5668\u4EBA\uFF1B\u65B0\u8BDD\u9898\u76F4\u63A5\u4F7F\u7528\u7ED1\u5B9A\u76EE\u5F55\u3002","groups.leaveTitle":"\u9009\u62E9\u673A\u5668\u4EBA\u9000\u51FA\u7FA4\u804A","groups.leaveSelected":"\u9009\u4E2D\u673A\u5668\u4EBA\u9000\u51FA\u7FA4\u804A","groups.disband":"\u89E3\u6563\u7FA4\u804A","groups.dangerHint":"\u89E3\u6563\u4EC5\u5F53\u5728\u7FA4\u673A\u5668\u4EBA\u662F\u7FA4\u4E3B\u65F6\u624D\u4F1A\u6210\u529F\uFF0C\u5426\u5219\u5EFA\u8BAE\u4F7F\u7528\u9000\u51FA\u7FA4\u804A\u3002","groups.save":"\u4FDD\u5B58","groups.needWorkingDir":"\u8BF7\u586B\u5DE5\u4F5C\u76EE\u5F55","groups.noBotsOnline":"\u6CA1\u6709\u5728\u7EBF bot\u3002\u8BF7\u5148\u91CD\u542F daemon\u3002","schedules.title":"\u5B9A\u65F6\u4EFB\u52A1","schedules.subtitle":"\u8DE8 daemon \u67E5\u770B\u3001\u6682\u505C\u3001\u6062\u590D\u548C\u7ACB\u5373\u89E6\u53D1\u5B9A\u65F6\u4EFB\u52A1\u3002","schedules.search":"\u641C\u7D22\u540D\u79F0 / prompt / \u5DE5\u4F5C\u76EE\u5F55","schedules.anyKind":"\u5168\u90E8\u7C7B\u578B","schedules.enabledOnly":"\u4EC5\u542F\u7528","schedules.name":"\u540D\u79F0","schedules.bot":"bot","schedules.schedule":"\u89C4\u5219","schedules.next":"\u4E0B\u6B21","schedules.last":"\u4E0A\u6B21","schedules.repeat":"\u91CD\u590D","schedules.enabled":"\u542F\u7528","schedules.actions":"\u64CD\u4F5C","schedules.runNow":"\u7ACB\u5373\u8FD0\u884C","schedules.pause":"\u6682\u505C","schedules.resume":"\u6062\u590D","schedules.empty":"\u6682\u65E0\u5B9A\u65F6\u4EFB\u52A1\u3002","settings.title":"\u5168\u5C40\u8BBE\u7F6E","settings.subtitle":"\u7BA1\u7406\u672C\u673A botmux dashboard \u4E0E\u6240\u6709 bot \u5171\u4EAB\u7684\u5168\u5C40\u884C\u4E3A\u3002","settings.loading":"\u6B63\u5728\u52A0\u8F7D\u8BBE\u7F6E\u2026","settings.loadFailed":"\u8BBE\u7F6E\u52A0\u8F7D\u5931\u8D25","settings.readOnlyVisitor":"\u5F53\u524D\u662F\u53EA\u8BFB\u8BBF\u95EE\uFF0C\u4FEE\u6539\u8BBE\u7F6E\u9700\u8981\u6388\u6743\u94FE\u63A5\uFF08\u8FD0\u884C botmux dashboard \u83B7\u53D6\uFF09","settings.sectionAccess":"\u8BBF\u95EE\u6743\u9650","settings.publicReadOnly":"\u5141\u8BB8\u65E0 token \u53EA\u8BFB\u8BBF\u95EE dashboard","settings.publicReadOnlyHelp":"\u5F00\u542F\u540E\uFF0C\u6CA1\u6709 token \u7684\u8BBF\u95EE\u8005\u53EF\u4EE5\u67E5\u770B dashboard\u3001\u4F1A\u8BDD\u3001\u5B9A\u65F6\u4EFB\u52A1\u548C SSE\uFF1B\u5173\u95ED\u3001\u6682\u505C\u3001\u5BA1\u6279\u7B49\u5199\u64CD\u4F5C\u4ECD\u5FC5\u987B\u901A\u8FC7 token \u9274\u6743\u3002\u654F\u611F\u7EC8\u7AEF\u539F\u59CB\u65E5\u5FD7\u59CB\u7EC8\u9700\u8981 token\u3002","settings.sectionCards":"\u98DE\u4E66\u5361\u7247","settings.openTerminalInFeishu":"\u6D41\u5F0F\u5361\u7247\u7EC8\u7AEF\u6309\u94AE\u5728\u98DE\u4E66\u4FA7\u680F\u6253\u5F00","settings.openTerminalInFeishuHelp":"\u9ED8\u8BA4\u5173\u95ED\uFF1A\u7EC8\u7AEF\u6309\u94AE\u76F4\u63A5\u6253\u5F00 Web Terminal URL\u3002\u5F00\u542F\u540E\u4F7F\u7528\u98DE\u4E66 web_url/open \u5305\u88C5\uFF0C\u5728\u98DE\u4E66 PC \u4FA7\u680F\u91CC\u6253\u5F00\uFF1B\u5199\u6743\u9650\u4ECD\u7531\u7EC8\u7AEF token \u63A7\u5236\u3002","settings.saving":"\u4FDD\u5B58\u4E2D\u2026","settings.saved":"\u5DF2\u4FDD\u5B58","settings.saveFailed":"\u4FDD\u5B58\u5931\u8D25","botDefaults.title":"\u6570\u5B57\u5458\u5DE5\u6863\u6848","botDefaults.subtitle":"\u6BCF\u4F4D\u5458\u5DE5\u7684\u9ED8\u8BA4\u884C\u4E3A\uFF1Aoncall\u3001\u4E3B\u52A8\u5F00\u5DE5\u3001\u4EBA\u8BBE\u89D2\u8272\u3001\u5361\u7247\u4E0E\u6388\u6743\u3002","botDefaults.metaOnline":"\u5728\u7EBF \xB7 daemon \u6B63\u5E38","botDefaults.search":"\u641C\u7D22 bot \u540D / app id","botDefaults.refresh":"\u5237\u65B0","botDefaults.sectionOncall":"\u65B0\u7FA4 Oncall","botDefaults.sectionBrand":"\u5361\u7247\u7B7E\u540D","botDefaults.warning":"\u5F00\u542F\u540E\uFF0C\u6CA1\u6709 oncall binding \u7684\u7FA4\u4F1A\u5728\u4E0B\u6B21\u5F00\u65B0\u8BDD\u9898\u65F6\u81EA\u52A8\u7ED1\u5B9A\u5230\u8BE5\u76EE\u5F55\uFF1B\u624B\u52A8\u7ED1\u5B9A\u6216\u624B\u52A8\u89E3\u7ED1\u8FC7\u7684\u7FA4\u4E0D\u4F1A\u88AB\u8986\u76D6\u3002","botDefaults.empty":"\u6CA1\u6709\u5728\u7EBF bot\u3002\u5148 botmux restart \u8BA9 daemon \u4E0A\u7EBF\u3002","botDefaults.defaultOncall":"\u9ED8\u8BA4\u8FDB\u5165 oncall \u6A21\u5F0F","botDefaults.defaultOncallHelp":"\u6240\u6709\u672A\u7ED1\u5B9A\u7684\u7FA4\u4E0B\u6B21\u5F00\u8BDD\u9898\u81EA\u52A8\u7ED1\u5B9A","botDefaults.workingDir":"\u9ED8\u8BA4\u5DE5\u4F5C\u76EE\u5F55","botDefaults.lastEnabled":"\u4E0A\u6B21\u542F\u7528\u65F6\u95F4","botDefaults.autobound":"\u5DF2\u81EA\u52A8\u7ED1\u5B9A {count} \u4E2A\u7FA4","botDefaults.save":"\u4FDD\u5B58","botDefaults.required":"\u5F00\u542F\u65F6\u5FC5\u987B\u586B\u5DE5\u4F5C\u76EE\u5F55","botDefaults.brandLabel":"\u4E2A\u6027\u7B7E\u540D\uFF08\u5361\u7247\u9875\u811A\uFF09","botDefaults.brandLabelHelp":"\u8BE5 bot \u53D1\u51FA\u7684\u5361\u7247\u9875\u811A\u7B7E\u540D\u3002\u7559\u7A7A\u4FDD\u5B58\uFF1D\u4E0D\u663E\u793A\uFF1B\u586B\u5199\uFF1D\u81EA\u5B9A\u4E49\uFF08\u652F\u6301 markdown\uFF0C\u5982 [Acme](https://\u2026)\uFF09\uFF1B\u6062\u590D\u9ED8\u8BA4\uFF1D\u663E\u793A botmux\u3002","botDefaults.brandLabelPlaceholder":"\u9ED8\u8BA4\uFF1Abotmux\uFF08\u7559\u7A7A\u5219\u4E0D\u663E\u793A\uFF09","botDefaults.brandSave":"\u4FDD\u5B58\u7B7E\u540D","botDefaults.brandReset":"\u6062\u590D\u9ED8\u8BA4","botDefaults.brandStateDefault":"\u5F53\u524D\uFF1A\u9ED8\u8BA4 botmux","botDefaults.brandStateOff":"\u5F53\u524D\uFF1A\u5DF2\u5173\u95ED","botDefaults.brandStateCustom":"\u5F53\u524D\uFF1A\u81EA\u5B9A\u4E49","botDefaults.sectionCard":"\u5361\u7247\u884C\u4E3A","botDefaults.disableStreaming":"\u5173\u95ED\u98DE\u4E66\u6D41\u5F0F\u5361\u7247","botDefaults.disableStreamingHelp":"\u4E0D\u518D\u53D1\u5B9E\u65F6\u5237\u65B0\u7684\u4F1A\u8BDD\u72B6\u6001\u5361\uFF08\u542B\u300C\u6253\u5F00\u7EC8\u7AEF\u300D\u5165\u53E3\uFF09\uFF1B\u4EFB\u52A1\u6700\u7EC8\u7ED3\u679C\u4ECD\u901A\u8FC7\u6D88\u606F\u9001\u8FBE\u3002\u9002\u5408\u5ACC\u6D41\u5F0F\u5361\u7247\u70E6\u7684\u573A\u666F\u3002","botDefaults.writableLink":"\u5361\u7247\u4E0A\u76F4\u63A5\u7ED9\u53EF\u64CD\u4F5C\uFF08\u53EF\u5199\uFF09\u7EC8\u7AEF\u94FE\u63A5","botDefaults.writableLinkHelp":"\u26A0\uFE0F \u5728\u6D41\u5F0F\u5361\u7247\u6B63\u6587\u76F4\u63A5\u8D34\u51FA\u53EF\u5199\u7EC8\u7AEF\u94FE\u63A5\uFF0C\u7FA4\u5185\u4EFB\u4F55\u4EBA\u90FD\u80FD\u70B9\u5F00\u5E76\u64CD\u63A7\u7EC8\u7AEF\u3002\u4E0D\u52FE\uFF1D\u4FDD\u6301\u73B0\u72B6\uFF08\u8D70\u300C\u83B7\u53D6\u64CD\u4F5C\u94FE\u63A5\u300D\u6309\u94AE\u79C1\u804A\u53D1\u7ED9\u70B9\u51FB\u8005\uFF09\u3002","botDefaults.writableLinkMoot":"\u5DF2\u5173\u95ED\u6D41\u5F0F\u5361\u7247","botDefaults.privateCard":"/card \u53D1\u79C1\u5BC6\u5361\u7247\uFF08\u4EC5\u6388\u6743\u4EBA\u53EF\u89C1\uFF09","botDefaults.privateCardHelp":"\u5F00\u542F\u540E /card \u6539\u7528\u300C\u4EC5\u7279\u5B9A\u4EBA\u53EF\u89C1\u300D\u7684\u4E34\u65F6\u5361\u7247\uFF1A\u53EA\u53D1\u7ED9 owner\uFF08allowedUsers\uFF09\uFF0C/grant \u6388\u6743\u5BF9\u8BDD\u7684\u4EBA\u548C\u7FA4\u91CC\u5176\u4ED6\u4EBA\u90FD\u770B\u4E0D\u5230\u3002\u4EE3\u4EF7\uFF1A\u662F\u9759\u6001\u5FEB\u7167\u3001\u4E0D\u4F1A\u5B9E\u65F6\u5237\u65B0\uFF1B\u4E14\u4EC5\u666E\u901A\u7FA4\u53EF\u7528\uFF08\u8BDD\u9898\u7FA4 / \u5355\u804A\u4F1A\u5931\u8D25\uFF0C\u4E0D\u964D\u7EA7\uFF09\u3002\u53EA\u5F71\u54CD /card \u547D\u4EE4\uFF0C\u81EA\u52A8\u6D41\u5F0F\u5361\u4E0D\u53D8\u3002","botDefaults.cardPrefSaved":"\u5DF2\u4FDD\u5B58","botDefaults.sectionRole":"\u9ED8\u8BA4\u89D2\u8272","botDefaults.roleHelp":"\u8BE5 bot \u7684\u9ED8\u8BA4\u4EBA\u8BBE\uFF08\u8DE8\u7FA4\u751F\u6548\uFF09\uFF0C\u4F1A\u6CE8\u5165\u5230\u8BE5 bot \u5728\u5404\u7FA4\u7684\u4F1A\u8BDD\u91CC\uFF1B\u5355\u4E2A\u7FA4\u53EF\u5728\u300C\u89D2\u8272\u300D\u9875\u5355\u72EC\u8986\u76D6\u3002\u7559\u7A7A\u4FDD\u5B58\uFF1D\u5220\u9664\u3002","botDefaults.rolePlaceholder":"\u4F8B\u5982\uFF1A\u4F60\u662F\u540E\u7AEF\u6392\u969C\u52A9\u624B\uFF0C\u56DE\u7B54\u7B80\u6D01\u3001\u4F18\u5148\u7ED9\u53EF\u6267\u884C\u547D\u4EE4\u2026","botDefaults.roleSave":"\u4FDD\u5B58\u89D2\u8272","botDefaults.roleDelete":"\u5220\u9664","botDefaults.roleSaved":"\u5DF2\u4FDD\u5B58","botDefaults.roleDeleted":"\u5DF2\u5220\u9664","botDefaults.roleLoadErr":"\u89D2\u8272\u52A0\u8F7D\u5931\u8D25","botDefaults.sectionAutoStart":"\u4E3B\u52A8\u5F00\u5DE5","botDefaults.autoStartJoin":"\u88AB\u62C9\u8FDB\u65B0\u7FA4\u81EA\u52A8\u5F00\u5DE5","botDefaults.autoStartJoinHelp":"\u5F00\u542F\u540E\uFF0C\u673A\u5668\u4EBA\u4E00\u88AB\u62C9\u8FDB\u65B0\u7FA4\uFF08\u7FA4\u91CC\u6709\u6388\u6743\u7528\u6237 allowedUsers \u65F6\uFF09\u5C31\u81EA\u52A8\u5F00\u4E00\u4E2A\u4F1A\u8BDD\u5F00\u59CB\u5DE5\u4F5C\uFF0C\u65E0\u9700 @\u3002\u5728\u673A\u5668\u4EBA\u7684\u9ED8\u8BA4\u5DE5\u4F5C\u76EE\u5F55\u542F\u52A8\uFF1B\u672A\u914D\u7F6E\u9ED8\u8BA4\u76EE\u5F55\u65F6\u5148\u5F39\u4ED3\u5E93\u9009\u62E9\u5361\u8BA9\u4F60\u9009\u3002\u524D\u63D0\uFF1A\u9700\u5728\u98DE\u4E66\u5F00\u653E\u5E73\u53F0\u4E3A\u672C\u5E94\u7528\u8BA2\u9605\u300C\u673A\u5668\u4EBA\u8FDB\u7FA4\u300D\u4E8B\u4EF6 im.chat.member.bot.added_v1\uFF0C\u5E76\u5F00\u901A\u7FA4\u6210\u5458\u8BFB\u53D6\u6743\u9650\u3002","botDefaults.autoStartJoinPrompt":"\u5165\u7FA4\u9996\u8F6E prompt\uFF08\u53EF\u9009\uFF09","botDefaults.autoStartJoinPromptPlaceholder":"\u586B\u4E86\uFF1D\u4F5C\u4E3A\u5165\u7FA4\u540E\u7684\u7B2C\u4E00\u6761\u4EFB\u52A1\uFF1B\u7559\u7A7A\uFF1D\u673A\u5668\u4EBA\u81EA\u5DF1\u770B\u7FA4\u91CC\u4FE1\u606F\u51B3\u5B9A\u505A\u4EC0\u4E48","botDefaults.autoStartJoinPromptSave":"\u4FDD\u5B58 prompt","botDefaults.autoStartTopic":"\u8BDD\u9898\u7FA4\u65B0\u8BDD\u9898\u81EA\u52A8\u5F00\u5DE5","botDefaults.autoStartTopicHelp":"\u5F00\u542F\u540E\uFF0C\u5728\u8BDD\u9898\u7FA4\u91CC\u6BCF\u5F53\u6709\u4EBA\u65B0\u5F00\u4E00\u4E2A\u8BDD\u9898\uFF0C\u673A\u5668\u4EBA\u5C31\u4F1A\u81EA\u52A8\u63A5\u5165\u8BE5\u8BDD\u9898\u3001\u628A\u9996\u6761\u6D88\u606F\u5F53\u4F5C\u4EFB\u52A1\u5F00\u59CB\u5904\u7406\uFF0C\u65E0\u9700 @\u3002\u4EC5\u5BF9\u8BDD\u9898\u7FA4\u751F\u6548\uFF0C\u666E\u901A\u7FA4\u4E0D\u53D7\u5F71\u54CD\u3002","botDefaults.sectionGrant":"\u6388\u6743\u4E0E\u989D\u5EA6","botDefaults.restrictGrant":"\u9650\u5236\u88AB\u6388\u6743\u4EBA\u53EA\u80FD\u7EAF\u5BF9\u8BDD","botDefaults.restrictGrantHelp":"\u5F00\u542F\u540E\uFF0C\u88AB /grant \u6388\u6743\u7684\u4EBA\uFF08owner \u81EA\u5DF1\u4E0D\u53D7\u9650\uFF09\u53EA\u80FD\u53D1\u666E\u901A\u5BF9\u8BDD\uFF0C\u6240\u6709 slash \u547D\u4EE4\u4E00\u5F8B\u62E6\u622A\uFF1Abotmux \u81EA\u5E26\u547D\u4EE4\u3001\u900F\u4F20\u547D\u4EE4\u3001/workflow\u3001/introduce\u3001/t \u4EE5\u53CA CLI \u539F\u751F\u547D\u4EE4\uFF08/help \u7B49\uFF09\u3002\u5F62\u5982 /path/to/file \u7684\u5185\u5BB9\u4E0D\u4F1A\u88AB\u8BEF\u5224\u3002","botDefaults.quotaDefault":"\u9ED8\u8BA4\u6D88\u606F\u989D\u5EA6","botDefaults.quotaPlaceholder":"\u7559\u7A7A\uFF1D\u4E0D\u914D\u7F6E\u9ED8\u8BA4\u989D\u5EA6","botDefaults.quotaHelp":"\u4E0D\u5E26\u6570\u5B57\u7684 /grant @x \u9ED8\u8BA4\u7ED9\u7684\u6D88\u606F\u6761\u6570\u3002\u7559\u7A7A\u6216\u70B9\u300C\u5173\u95ED\u300D\u53EA\u662F\u4E0D\u518D\u7ED9\u88F8 /grant \u5957\u9ED8\u8BA4\u989D\u5EA6\uFF0C\u4E0D\u4F1A\u6E05\u6389\u5DF2\u6709\u7684\u989D\u5EA6\u8BA1\u6570\uFF0C\u4E5F\u4E0D\u5F71\u54CD\u663E\u5F0F /grant @x N\u2014\u2014\u5B83\u4EEC\u7167\u5E38\u7EE7\u7EED enforce\u3002\u989D\u5EA6\u7528\u5C3D\u4F1A\u81EA\u52A8\u64A4\u9500\u8BE5\u4EBA\u6388\u6743\u3002","botDefaults.quotaSave":"\u4FDD\u5B58\u989D\u5EA6","botDefaults.quotaOff":"\u5173\u95ED","botDefaults.quotaInvalid":"\u989D\u5EA6\u5FC5\u987B\u662F\u6B63\u6574\u6570","botDefaults.quotaStateOff":"\u5F53\u524D\uFF1A\u672A\u914D\u7F6E\u9ED8\u8BA4\u989D\u5EA6","botDefaults.quotaStateOn":"\u5F53\u524D\uFF1A\u6BCF\u4EBA {count} \u6761","nav.roles":"\u89D2\u8272\u7BA1\u7406","roles.title":"\u89D2\u8272\u7BA1\u7406","roles.subtitle":"\u4E3A\u6BCF\u4E2A\u7FA4\u7EC4\u7684\u6BCF\u4E2A Bot \u5355\u72EC\u8BBE\u7F6E\u89D2\u8272\u63D0\u793A\u8BCD\uFF0CBot \u5728\u8BE5\u7FA4\u4E2D\u4F1A\u4EE5\u6B64\u89D2\u8272\u884C\u4E8B\u3002","roles.search":"\u641C\u7D22\u7FA4\u540D/Bot/ID","roles.refresh":"\u5237\u65B0","roles.selectHint":"\u2190 \u5C55\u5F00\u7FA4\u7EC4\uFF0C\u9009\u62E9\u4E00\u4E2A Bot \u6765\u7F16\u8F91\u89D2\u8272","roles.editorPlaceholder":`\u8F93\u5165\u89D2\u8272\u63CF\u8FF0\uFF0C\u4F8B\u5982\uFF1A
|
|
1
|
+
"use strict";(()=>{var Ct=class{sessions=new Map;schedules=new Map;online=!0;listeners=new Set;upsertSessions(t){for(let n of t)this.sessions.set(n.sessionId,n);this.emit()}upsertSchedules(t){for(let n of t)this.schedules.set(n.id,n);this.emit()}applySse(t,n){if(t==="session.spawned")this.sessions.set(n.session.sessionId,n.session);else if(t==="session.update"){let s=this.sessions.get(n.sessionId);s&&this.sessions.set(n.sessionId,{...s,...n.patch})}else if(t==="session.exited"){let s=this.sessions.get(n.sessionId);s&&this.sessions.set(n.sessionId,{...s,status:"closed"})}else if(t==="schedule.created")this.schedules.set(n.schedule.id,n.schedule);else if(t==="schedule.updated"){let s=this.schedules.get(n.id);s&&this.schedules.set(n.id,{...s,...n.patch})}else if(t==="schedule.deleted")this.schedules.delete(n.id);else return;this.emit()}setOnline(t){this.online!==t&&(this.online=t,this.emit())}on(t){return this.listeners.add(t),()=>this.listeners.delete(t)}emit(){for(let t of this.listeners)t()}},K=new Ct;async function fn(){let[e,t]=await Promise.all([fetch("/api/sessions").then(o=>o.json()),fetch("/api/schedules").then(o=>o.json())]);K.upsertSessions(e.sessions??[]),K.upsertSchedules(t.schedules??[]);let n=new EventSource("/events"),s=["session.spawned","session.update","session.exited","schedule.created","schedule.updated","schedule.deleted","schedule.fired","heartbeat"];for(let o of s)n.addEventListener(o,i=>{try{let l=JSON.parse(i.data);K.applySse(o,l.body??l)}catch{}});n.onerror=()=>K.setOnline(!1),n.onopen=()=>K.setOnline(!0)}var Dt="botmux.dashboard.locale",Ba={"app.name":"botmux","app.subtitle":"\u98DE\u4E66 AI CLI \u63A7\u5236\u53F0","time.secondsAgo":"{value} \u79D2\u524D","time.minutesAgo":"{value} \u5206\u949F\u524D","time.hoursAgo":"{value} \u5C0F\u65F6\u524D","nav.overview":"\u603B\u89C8","nav.sessions":"\u4F1A\u8BDD","nav.groups":"\u7FA4\u7EC4","nav.schedules":"\u5B9A\u65F6","nav.settings":"\u8BBE\u7F6E","nav.botDefaults":"Bot \u914D\u7F6E","status.live":"\u5B9E\u65F6\u8FDE\u63A5","status.disconnected":"\u8FDE\u63A5\u65AD\u5F00","status.system":"\u7CFB\u7EDF","status.light":"\u6D45\u8272","status.dark":"\u6697\u9ED1","status.language":"\u8BED\u8A00","status.theme":"\u4E3B\u9898","theme.base":"\u57FA\u7840","theme.skins":"\u4E3B\u9898\u76AE\u80A4","skin.cyber":"2077","skin.genshin":"\u539F\u795E","skin.fallout":"Fallout","skin.prts":"PRTS","skin.bluearchive":"\u851A\u84DD\u6863\u6848","skin.zzz":"\u7EDD\u533A\u96F6","skin.dragonball":"\u4E03\u9F99\u73E0","skin.ikun":"ikun","botOnboarding.add":"\u6DFB\u52A0\u673A\u5668\u4EBA","botOnboarding.title":"\u6DFB\u52A0\u673A\u5668\u4EBA","botOnboarding.intro":"\u9009\u62E9 CLI \u4E0E\u5DE5\u4F5C\u76EE\u5F55\uFF0C\u626B\u7801\u521B\u5EFA PersonalAgent \u5E94\u7528\u540E\u5199\u5165\u672C\u673A bots.json\uFF0C\u5E76\u81EA\u52A8\u914D\u7F6E\u5F00\u653E\u5E73\u53F0\u6743\u9650\u3002","botOnboarding.cliLabel":"CLI \u9002\u914D\u5668","botOnboarding.dirLabel":"\u5DE5\u4F5C\u76EE\u5F55","botOnboarding.dirPlaceholder":"\u9ED8\u8BA4 ~\uFF08\u5BB6\u76EE\u5F55\uFF09\uFF0C\u9700\u4E3A daemon \u4E3B\u673A\u4E0A\u5DF2\u5B58\u5728\u7684\u76EE\u5F55","botOnboarding.modelLabel":"\u6A21\u578B\uFF08\u53EF\u9009\uFF09","botOnboarding.modelPlaceholder":"\u7559\u7A7A\u4F7F\u7528\u8BE5 CLI \u7684\u9ED8\u8BA4\u6A21\u578B","botOnboarding.startScan":"\u5F00\u59CB\u626B\u7801","botOnboarding.cancel":"\u53D6\u6D88","botOnboarding.starting":"\u6B63\u5728\u751F\u6210\u4E8C\u7EF4\u7801...","botOnboarding.waiting":"\u8BF7\u7528\u98DE\u4E66 App \u626B\u7801\u786E\u8BA4\u521B\u5EFA\u5E94\u7528\u3002","botOnboarding.verifying":"\u626B\u7801\u6210\u529F\uFF0C\u6B63\u5728\u6821\u9A8C\u51ED\u8BC1...","botOnboarding.configuringPermissions":"\u6B63\u5728\u81EA\u52A8\u914D\u7F6E\u5F00\u653E\u5E73\u53F0\u6743\u9650\u2026","botOnboarding.platformScanHint":"\u8BF7\u7528\u98DE\u4E66 App \u518D\u626B\u4E00\u6B21\u7801\u767B\u5F55\u5F00\u653E\u5E73\u53F0\uFF08\u7528\u4E8E\u81EA\u52A8\u5BFC\u5165\u6743\u9650\u3001\u914D\u7F6E\u56DE\u8C03\u3001\u63D0\u4EA4\u7248\u672C\uFF09\u3002","botOnboarding.platformQrAlt":"\u5F00\u653E\u5E73\u53F0\u767B\u5F55\u4E8C\u7EF4\u7801","botOnboarding.completed":"\u673A\u5668\u4EBA\u5DF2\u6DFB\u52A0\u3002","botOnboarding.permissionOk":"\u5DF2\u81EA\u52A8\u5BFC\u5165 {count} \u9879\u6743\u9650\u3002","botOnboarding.permissionSkipped":"\uFF08\u8DF3\u8FC7 {count} \u9879\u5F53\u524D\u79DF\u6237\u76EE\u5F55\u4E2D\u6CA1\u6709\u7684\u6743\u9650\uFF09","botOnboarding.permissionVersion":"\u5DF2\u63D0\u4EA4\u53D1\u5E03\u7248\u672C {version}\u3002","botOnboarding.permissionManual":"\u6743\u9650\u672A\u80FD\u81EA\u52A8\u914D\u7F6E\uFF0C\u8BF7\u624B\u52A8\u5B8C\u6210\u4EE5\u4E0B\u6B65\u9AA4\uFF1A","botOnboarding.metaDir":"\u76EE\u5F55","botOnboarding.failed":"\u6DFB\u52A0\u5931\u8D25","botOnboarding.openLink":"\u6253\u4E0D\u5F00\u626B\u7801\uFF1F\u5728\u6D4F\u89C8\u5668\u4E2D\u6253\u5F00","botOnboarding.close":"\u5173\u95ED","botOnboarding.restartHint":"\u5DF2\u5199\u5165 bots.json\u3002\u6267\u884C pnpm daemon:restart \u540E\u65B0\u673A\u5668\u4EBA\u751F\u6548\u3002","botOnboarding.qrAlt":"\u98DE\u4E66\u626B\u7801\u6DFB\u52A0\u673A\u5668\u4EBA\u4E8C\u7EF4\u7801","overview.title":"\u5DE5\u4F5C\u53F0","overview.subtitle":"\u6570\u5B57\u5458\u5DE5\u5B9E\u65F6\u72B6\u6001 \xB7 \u540C\u6B65\u98DE\u4E66\u8BDD\u9898","overview.team":"AI \u56E2\u961F","overview.teamHint":"\u6BCF\u4F4D\u6570\u5B57\u5458\u5DE5\u7684\u5B9E\u65F6\u72B6\u6001","overview.attention":"\u9700\u8981\u4F60\u5904\u7406","overview.attentionHint":"\u5361\u4F4F\u7684\u4EFB\u52A1\uFF0C\u56DE\u4E2A\u8BDD\u5C31\u80FD\u7EE7\u7EED\u8DD1","overview.activeSessions":"\u6D3B\u8DC3\u4F1A\u8BDD","overview.activeSessionsHint":"\u6B63\u5728\u8FD0\u8F6C\u7684\u4F1A\u8BDD","overview.today":"\u6B64\u523B\u6982\u89C8","overview.todayHint":"\u6D3B\u8DC3\u4F1A\u8BDD\u5206\u5E03","overview.viewAll":"\u67E5\u770B\u5168\u90E8 \u2192","overview.botIdle":"\u5F85\u547D\u4E2D\uFF0C\u968F\u65F6\u53EF\u63A5\u65B0\u4EFB\u52A1","overview.botOffline":"\u79BB\u7EBF \u2014 daemon \u672A\u4E0A\u7EBF","overview.botBusy":"\u6267\u884C\u4E2D \xB7 {count} \u4F1A\u8BDD","overview.botNeedsYou":"\u7B49\u4F60\u56DE\u590D","overview.botReady":"\u5C31\u7EEA","overview.botOff":"\u79BB\u7EBF","overview.sessionsCount":"{count} \u4F1A\u8BDD","overview.lastActive":"{time}\u524D\u6D3B\u8DC3","overview.noAttention":"\u6CA1\u6709\u7B49\u4F60\u5904\u7406\u7684\u4E8B\u9879","strip.pending":"{count} \u4EF6\u5F85\u5904\u7406","strip.longest":"\u6700\u4E45\u5DF2\u7B49 {time} \u2014 {bot} {reason}","strip.handle":"\u7ACB\u5373\u5904\u7406","overview.openSessions":"\u6D3B\u8DC3\u4F1A\u8BDD","overview.workingSessions":"\u5DE5\u4F5C\u4E2D","overview.onlineBots":"\u5728\u7EBF Bot","overview.schedules":"\u5B9A\u65F6\u4EFB\u52A1","overview.groups":"\u7FA4\u804A\u8986\u76D6","overview.enabledSchedules":"\u5DF2\u542F\u7528","overview.total":"\u603B\u8BA1","overview.active":"\u6D3B\u8DC3","overview.daemonRegistry":"daemon \u6CE8\u518C\u8868","overview.chatMatrix":"\u7FA4\u804A\u77E9\u9635","overview.recentSessions":"\u6700\u8FD1\u4F1A\u8BDD","overview.nextSchedules":"\u5373\u5C06\u6267\u884C","overview.noSessions":"\u6682\u65E0\u4F1A\u8BDD\u3002","overview.noSchedules":"\u6682\u65E0\u5B9A\u65F6\u4EFB\u52A1\u3002","sessions.title":"\u4F1A\u8BDD\u63A7\u5236","sessions.subtitle":"\u5B9A\u4F4D\u98DE\u4E66\u8BDD\u9898\u3001\u6253\u5F00 Web Terminal\u3001\u5173\u95ED\u6216\u6062\u590D CLI \u4F1A\u8BDD\u3002","sessions.search":"\u641C\u7D22\u5DE5\u4F5C\u76EE\u5F55 / \u6807\u9898 / ID","sessions.anyStatus":"\u5168\u90E8\u72B6\u6001","sessions.adoptAny":"adopt: \u5168\u90E8","sessions.adoptYes":"adopt: \u662F","sessions.adoptNo":"adopt: \u5426","sessions.activeOnly":"\u4EC5\u6D3B\u8DC3","sessions.closeSelected":"\u5173\u95ED\u9009\u4E2D","sessions.clearSelection":"\u53D6\u6D88\u9009\u62E9","sessions.selectedCount":"\u5DF2\u9009 {count} \u4E2A\u4F1A\u8BDD","sessions.bot":"bot","sessions.cli":"CLI","sessions.chat":"\u7FA4\u804A","sessions.openChat":"\u6253\u5F00\u7FA4\u804A","sessions.status":"\u72B6\u6001","sessions.titleCol":"\u6807\u9898","sessions.workingDir":"\u5DE5\u4F5C\u76EE\u5F55","sessions.created":"\u521B\u5EFA","sessions.last":"\u6700\u8FD1","sessions.adopt":"\u63A5\u5165","sessions.actions":"\u64CD\u4F5C","sessions.details":"\u8BE6\u60C5","sessions.copy":"\u590D\u5236","sessions.copied":"\u5DF2\u590D\u5236","sessions.locate":"\u5B9A\u4F4D\u8BDD\u9898","sessions.locating":"\u53D1\u9001\u4E2D...","sessions.cooldown":"\u51B7\u5374 {seconds}s","sessions.openTerminal":"\u7EC8\u7AEF","sessions.close":"\u5173\u95ED\u4F1A\u8BDD","sessions.resume":"\u6062\u590D\u4F1A\u8BDD","sessions.dismiss":"\u5173\u95ED","sessions.closeConfirm":"\u5173\u95ED\u8FD9\u4E2A\u4F1A\u8BDD\uFF1F","sessions.resumeFailed":"\u6062\u590D\u5931\u8D25","sessions.closeBulkConfirm":"\u5173\u95ED\u9009\u4E2D\u7684 {count} \u4E2A\u4F1A\u8BDD\uFF1F","sessions.empty":"\u6CA1\u6709\u7B26\u5408\u6761\u4EF6\u7684\u4F1A\u8BDD\u3002","sessions.viewMode":"\u4F1A\u8BDD\u89C6\u56FE","sessions.viewBoard":"\u72B6\u6001\u677F","sessions.viewTable":"\u8868\u683C","sessions.selectSession":"\u9009\u62E9\u4F1A\u8BDD","sessions.board.needsYou":"\u9700\u8981\u4F60","sessions.board.needsYouHint":"\u7B49\u5F85\u4ED3\u5E93\u3001TUI \u9009\u62E9\u6216\u989D\u5EA6\u5904\u7406","sessions.board.starting":"\u542F\u52A8\u4E2D","sessions.board.startingHint":"CLI \u6B63\u5728\u521B\u5EFA\u6216\u6062\u590D","sessions.board.working":"\u5E72\u6D3B\u4E2D","sessions.board.workingHint":"\u6B63\u5728\u8F93\u51FA\u3001\u5206\u6790\u6216\u6267\u884C\u5DE5\u5177","sessions.board.idle":"\u7A7A\u95F2","sessions.board.idleHint":"\u53EF\u7EE7\u7EED\u5BF9\u8BDD\u6216\u63A5\u65B0\u4EFB\u52A1","sessions.board.emptyColumn":"\u6682\u65E0\u4F1A\u8BDD","sessions.board.signalRepo":"\u5F85\u9009\u4ED3\u5E93","sessions.board.signalPrompt":"\u7B49\u5F85 TUI \u9009\u62E9","sessions.board.signalLimited":"\u989D\u5EA6\u53D7\u9650","sessions.board.dragHint":"\u62D6\u52A8\u5217\u5934\u8C03\u6574\u5217\u987A\u5E8F","sessions.board.moveLeft":"\u5DE6\u79FB\u6B64\u5217","sessions.board.moveRight":"\u53F3\u79FB\u6B64\u5217","groups.title":"\u7FA4\u7EC4\u4E0E Bot","groups.subtitle":"\u67E5\u770B chat x bot \u8986\u76D6\u77E9\u9635\uFF0C\u7BA1\u7406\u62C9\u7FA4\u3001oncall\u3001\u9000\u7FA4\u548C\u89E3\u6563\u3002","groups.search":"\u641C\u7D22\u7FA4\u540D / ID / owner","groups.missingOnly":"\u4EC5\u7F3A bot","groups.refresh":"\u5237\u65B0","groups.create":"\u65B0\u5EFA\u7FA4","groups.chat":"\u7FA4\u804A","groups.actions":"\u64CD\u4F5C","groups.addBots":"\u6DFB\u52A0 bot","groups.manage":"\u7BA1\u7406","groups.empty":"\u6CA1\u6709\u7B26\u5408\u6761\u4EF6\u7684\u7FA4\u804A\u3002","groups.createTitle":"\u65B0\u5EFA\u7FA4\u804A","groups.createHelp":"\u9009\u62E9\u8981\u9080\u8BF7\u7684 bot\u3002dashboard \u4F1A\u81EA\u52A8\u9009\u62E9\u5728\u7EBF daemon \u4F5C\u4E3A\u521B\u5EFA\u8005\u3002","groups.name":"\u7FA4\u540D","groups.namePlaceholder":"\u4F8B\u5982 AI ChangeLog","groups.bindDir":"\u7ED1\u5B9A\u76EE\u5F55","groups.bindDirHelp":"\u65B0\u8BDD\u9898\u76F4\u63A5\u7528\u8BE5\u76EE\u5F55\u542F\u52A8 CLI\uFF0C\u8DF3\u8FC7 repo \u9009\u62E9\u3002","groups.botPicker":"Bot","groups.createSubmit":"\u521B\u5EFA","groups.cancel":"\u53D6\u6D88","groups.successTitle":"\u7FA4\u521B\u5EFA\u6210\u529F","groups.openGroup":"\u6253\u5F00\u65B0\u7FA4","groups.manageTitle":"\u7BA1\u7406 {name}","groups.owner":"\u7FA4\u4E3B","groups.oncall":"Oncall \u6A21\u5F0F","groups.oncallHelp":"\u5F00\u542F\u540E\uFF0C\u7FA4\u5185\u6210\u5458\u53EF @ \u673A\u5668\u4EBA\uFF1B\u65B0\u8BDD\u9898\u76F4\u63A5\u4F7F\u7528\u7ED1\u5B9A\u76EE\u5F55\u3002","groups.leaveTitle":"\u9009\u62E9\u673A\u5668\u4EBA\u9000\u51FA\u7FA4\u804A","groups.leaveSelected":"\u9009\u4E2D\u673A\u5668\u4EBA\u9000\u51FA\u7FA4\u804A","groups.disband":"\u89E3\u6563\u7FA4\u804A","groups.dangerHint":"\u89E3\u6563\u4EC5\u5F53\u5728\u7FA4\u673A\u5668\u4EBA\u662F\u7FA4\u4E3B\u65F6\u624D\u4F1A\u6210\u529F\uFF0C\u5426\u5219\u5EFA\u8BAE\u4F7F\u7528\u9000\u51FA\u7FA4\u804A\u3002","groups.save":"\u4FDD\u5B58","groups.needWorkingDir":"\u8BF7\u586B\u5DE5\u4F5C\u76EE\u5F55","groups.noBotsOnline":"\u6CA1\u6709\u5728\u7EBF bot\u3002\u8BF7\u5148\u91CD\u542F daemon\u3002","schedules.title":"\u5B9A\u65F6\u4EFB\u52A1","schedules.subtitle":"\u8DE8 daemon \u67E5\u770B\u3001\u6682\u505C\u3001\u6062\u590D\u548C\u7ACB\u5373\u89E6\u53D1\u5B9A\u65F6\u4EFB\u52A1\u3002","schedules.search":"\u641C\u7D22\u540D\u79F0 / prompt / \u5DE5\u4F5C\u76EE\u5F55","schedules.anyKind":"\u5168\u90E8\u7C7B\u578B","schedules.enabledOnly":"\u4EC5\u542F\u7528","schedules.name":"\u540D\u79F0","schedules.bot":"bot","schedules.schedule":"\u89C4\u5219","schedules.next":"\u4E0B\u6B21","schedules.last":"\u4E0A\u6B21","schedules.repeat":"\u91CD\u590D","schedules.enabled":"\u542F\u7528","schedules.actions":"\u64CD\u4F5C","schedules.runNow":"\u7ACB\u5373\u8FD0\u884C","schedules.pause":"\u6682\u505C","schedules.resume":"\u6062\u590D","schedules.empty":"\u6682\u65E0\u5B9A\u65F6\u4EFB\u52A1\u3002","settings.title":"\u5168\u5C40\u8BBE\u7F6E","settings.subtitle":"\u7BA1\u7406\u672C\u673A botmux dashboard \u4E0E\u6240\u6709 bot \u5171\u4EAB\u7684\u5168\u5C40\u884C\u4E3A\u3002","settings.loading":"\u6B63\u5728\u52A0\u8F7D\u8BBE\u7F6E\u2026","settings.loadFailed":"\u8BBE\u7F6E\u52A0\u8F7D\u5931\u8D25","settings.readOnlyVisitor":"\u5F53\u524D\u662F\u53EA\u8BFB\u8BBF\u95EE\uFF0C\u4FEE\u6539\u8BBE\u7F6E\u9700\u8981\u6388\u6743\u94FE\u63A5\uFF08\u8FD0\u884C botmux dashboard \u83B7\u53D6\uFF09","settings.sectionAccess":"\u8BBF\u95EE\u6743\u9650","settings.publicReadOnly":"\u5141\u8BB8\u65E0 token \u53EA\u8BFB\u8BBF\u95EE dashboard","settings.publicReadOnlyHelp":"\u5F00\u542F\u540E\uFF0C\u6CA1\u6709 token \u7684\u8BBF\u95EE\u8005\u53EF\u4EE5\u67E5\u770B dashboard\u3001\u4F1A\u8BDD\u3001\u5B9A\u65F6\u4EFB\u52A1\u548C SSE\uFF1B\u5173\u95ED\u3001\u6682\u505C\u3001\u5BA1\u6279\u7B49\u5199\u64CD\u4F5C\u4ECD\u5FC5\u987B\u901A\u8FC7 token \u9274\u6743\u3002\u654F\u611F\u7EC8\u7AEF\u539F\u59CB\u65E5\u5FD7\u59CB\u7EC8\u9700\u8981 token\u3002","settings.sectionCards":"\u98DE\u4E66\u5361\u7247","settings.openTerminalInFeishu":"\u6D41\u5F0F\u5361\u7247\u7EC8\u7AEF\u6309\u94AE\u5728\u98DE\u4E66\u4FA7\u680F\u6253\u5F00","settings.openTerminalInFeishuHelp":"\u9ED8\u8BA4\u5173\u95ED\uFF1A\u7EC8\u7AEF\u6309\u94AE\u76F4\u63A5\u6253\u5F00 Web Terminal URL\u3002\u5F00\u542F\u540E\u4F7F\u7528\u98DE\u4E66 web_url/open \u5305\u88C5\uFF0C\u5728\u98DE\u4E66 PC \u4FA7\u680F\u91CC\u6253\u5F00\uFF1B\u5199\u6743\u9650\u4ECD\u7531\u7EC8\u7AEF token \u63A7\u5236\u3002","settings.saving":"\u4FDD\u5B58\u4E2D\u2026","settings.saved":"\u5DF2\u4FDD\u5B58","settings.saveFailed":"\u4FDD\u5B58\u5931\u8D25","settings.sectionMaintenance":"\u81EA\u52A8\u7EF4\u62A4","settings.autoUpdate":"\u81EA\u52A8\u66F4\u65B0","settings.autoUpdateHelp":"\u9ED8\u8BA4\u5173\u95ED\u3002\u5230\u70B9\u6267\u884C npm install -g botmux@latest \u5B89\u88C5\u6700\u65B0\u7248\u672C\uFF08\u53EA\u4E0B\u8F7D\u5B89\u88C5\u3001\u672C\u8EAB\u4E0D\u91CD\u542F\uFF09\u3002\u4EC5 npm \u5168\u5C40\u5B89\u88C5\u53EF\u7528\u3002","settings.autoRestart":"\u81EA\u52A8\u91CD\u542F","settings.autoRestartHelp":"\u9ED8\u8BA4\u5173\u95ED\uFF0C\u9700\u5148\u5F00\u542F\u81EA\u52A8\u66F4\u65B0\u3002\u81EA\u52A8\u66F4\u65B0\u88C5\u5230\u65B0\u7248\u672C\u540E\uFF0C\u82E5\u6CA1\u6709\u8FDB\u884C\u4E2D\u7684\u4F1A\u8BDD\u5219\u81EA\u52A8\u91CD\u542F\u4EE5\u751F\u6548\uFF08\u649E\u4E0A\u5FD9\u788C\u4F1A\u8BDD\u5219\u8DF3\u5230\u6B21\u65E5\uFF09\u3002","settings.autoUpdateLocalDev":"\u5F53\u524D\u4E3A\u672C\u5730\u5F00\u53D1\u5B89\u88C5\uFF08\u4ECE\u6E90\u7801\u8FD0\u884C\uFF09\uFF0C\u81EA\u52A8\u66F4\u65B0\u4E0D\u53EF\u7528\u3002","settings.maintenanceTime":"\u65F6\u95F4","botDefaults.title":"\u6570\u5B57\u5458\u5DE5\u6863\u6848","botDefaults.subtitle":"\u6BCF\u4F4D\u5458\u5DE5\u7684\u9ED8\u8BA4\u884C\u4E3A\uFF1Aoncall\u3001\u4E3B\u52A8\u5F00\u5DE5\u3001\u4EBA\u8BBE\u89D2\u8272\u3001\u5361\u7247\u4E0E\u6388\u6743\u3002","botDefaults.metaOnline":"\u5728\u7EBF \xB7 daemon \u6B63\u5E38","botDefaults.search":"\u641C\u7D22 bot \u540D / app id","botDefaults.refresh":"\u5237\u65B0","botDefaults.sectionOncall":"\u65B0\u7FA4 Oncall","botDefaults.sectionBrand":"\u5361\u7247\u7B7E\u540D","botDefaults.warning":"\u5F00\u542F\u540E\uFF0C\u6CA1\u6709 oncall binding \u7684\u7FA4\u4F1A\u5728\u4E0B\u6B21\u5F00\u65B0\u8BDD\u9898\u65F6\u81EA\u52A8\u7ED1\u5B9A\u5230\u8BE5\u76EE\u5F55\uFF1B\u624B\u52A8\u7ED1\u5B9A\u6216\u624B\u52A8\u89E3\u7ED1\u8FC7\u7684\u7FA4\u4E0D\u4F1A\u88AB\u8986\u76D6\u3002","botDefaults.empty":"\u6CA1\u6709\u5728\u7EBF bot\u3002\u5148 botmux restart \u8BA9 daemon \u4E0A\u7EBF\u3002","botDefaults.defaultOncall":"\u9ED8\u8BA4\u8FDB\u5165 oncall \u6A21\u5F0F","botDefaults.defaultOncallHelp":"\u6240\u6709\u672A\u7ED1\u5B9A\u7684\u7FA4\u4E0B\u6B21\u5F00\u8BDD\u9898\u81EA\u52A8\u7ED1\u5B9A","botDefaults.workingDir":"\u9ED8\u8BA4\u5DE5\u4F5C\u76EE\u5F55","botDefaults.lastEnabled":"\u4E0A\u6B21\u542F\u7528\u65F6\u95F4","botDefaults.autobound":"\u5DF2\u81EA\u52A8\u7ED1\u5B9A {count} \u4E2A\u7FA4","botDefaults.save":"\u4FDD\u5B58","botDefaults.required":"\u5F00\u542F\u65F6\u5FC5\u987B\u586B\u5DE5\u4F5C\u76EE\u5F55","botDefaults.brandLabel":"\u4E2A\u6027\u7B7E\u540D\uFF08\u5361\u7247\u9875\u811A\uFF09","botDefaults.brandLabelHelp":"\u8BE5 bot \u53D1\u51FA\u7684\u5361\u7247\u9875\u811A\u7B7E\u540D\u3002\u7559\u7A7A\u4FDD\u5B58\uFF1D\u4E0D\u663E\u793A\uFF1B\u586B\u5199\uFF1D\u81EA\u5B9A\u4E49\uFF08\u652F\u6301 markdown\uFF0C\u5982 [Acme](https://\u2026)\uFF09\uFF1B\u6062\u590D\u9ED8\u8BA4\uFF1D\u663E\u793A botmux\u3002","botDefaults.brandLabelPlaceholder":"\u9ED8\u8BA4\uFF1Abotmux\uFF08\u7559\u7A7A\u5219\u4E0D\u663E\u793A\uFF09","botDefaults.brandSave":"\u4FDD\u5B58\u7B7E\u540D","botDefaults.brandReset":"\u6062\u590D\u9ED8\u8BA4","botDefaults.brandStateDefault":"\u5F53\u524D\uFF1A\u9ED8\u8BA4 botmux","botDefaults.brandStateOff":"\u5F53\u524D\uFF1A\u5DF2\u5173\u95ED","botDefaults.brandStateCustom":"\u5F53\u524D\uFF1A\u81EA\u5B9A\u4E49","botDefaults.sectionCard":"\u5361\u7247\u884C\u4E3A","botDefaults.disableStreaming":"\u5173\u95ED\u98DE\u4E66\u6D41\u5F0F\u5361\u7247","botDefaults.disableStreamingHelp":"\u4E0D\u518D\u53D1\u5B9E\u65F6\u5237\u65B0\u7684\u4F1A\u8BDD\u72B6\u6001\u5361\uFF08\u542B\u300C\u6253\u5F00\u7EC8\u7AEF\u300D\u5165\u53E3\uFF09\uFF1B\u4EFB\u52A1\u6700\u7EC8\u7ED3\u679C\u4ECD\u901A\u8FC7\u6D88\u606F\u9001\u8FBE\u3002\u9002\u5408\u5ACC\u6D41\u5F0F\u5361\u7247\u70E6\u7684\u573A\u666F\u3002","botDefaults.writableLink":"\u5361\u7247\u4E0A\u76F4\u63A5\u7ED9\u53EF\u64CD\u4F5C\uFF08\u53EF\u5199\uFF09\u7EC8\u7AEF\u94FE\u63A5","botDefaults.writableLinkHelp":"\u26A0\uFE0F \u5728\u6D41\u5F0F\u5361\u7247\u6B63\u6587\u76F4\u63A5\u8D34\u51FA\u53EF\u5199\u7EC8\u7AEF\u94FE\u63A5\uFF0C\u7FA4\u5185\u4EFB\u4F55\u4EBA\u90FD\u80FD\u70B9\u5F00\u5E76\u64CD\u63A7\u7EC8\u7AEF\u3002\u4E0D\u52FE\uFF1D\u4FDD\u6301\u73B0\u72B6\uFF08\u8D70\u300C\u83B7\u53D6\u64CD\u4F5C\u94FE\u63A5\u300D\u6309\u94AE\u79C1\u804A\u53D1\u7ED9\u70B9\u51FB\u8005\uFF09\u3002","botDefaults.writableLinkMoot":"\u5DF2\u5173\u95ED\u6D41\u5F0F\u5361\u7247","botDefaults.privateCard":"/card \u53D1\u79C1\u5BC6\u5361\u7247\uFF08\u4EC5\u6388\u6743\u4EBA\u53EF\u89C1\uFF09","botDefaults.privateCardHelp":"\u5F00\u542F\u540E /card \u6539\u7528\u300C\u4EC5\u7279\u5B9A\u4EBA\u53EF\u89C1\u300D\u7684\u4E34\u65F6\u5361\u7247\uFF1A\u53EA\u53D1\u7ED9 owner\uFF08allowedUsers\uFF09\uFF0C/grant \u6388\u6743\u5BF9\u8BDD\u7684\u4EBA\u548C\u7FA4\u91CC\u5176\u4ED6\u4EBA\u90FD\u770B\u4E0D\u5230\u3002\u4EE3\u4EF7\uFF1A\u662F\u9759\u6001\u5FEB\u7167\u3001\u4E0D\u4F1A\u5B9E\u65F6\u5237\u65B0\uFF1B\u4E14\u4EC5\u666E\u901A\u7FA4\u53EF\u7528\uFF08\u8BDD\u9898\u7FA4 / \u5355\u804A\u4F1A\u5931\u8D25\uFF0C\u4E0D\u964D\u7EA7\uFF09\u3002\u53EA\u5F71\u54CD /card \u547D\u4EE4\uFF0C\u81EA\u52A8\u6D41\u5F0F\u5361\u4E0D\u53D8\u3002","botDefaults.cardPrefSaved":"\u5DF2\u4FDD\u5B58","botDefaults.sectionRole":"\u9ED8\u8BA4\u89D2\u8272","botDefaults.roleHelp":"\u8BE5 bot \u7684\u9ED8\u8BA4\u4EBA\u8BBE\uFF08\u8DE8\u7FA4\u751F\u6548\uFF09\uFF0C\u4F1A\u6CE8\u5165\u5230\u8BE5 bot \u5728\u5404\u7FA4\u7684\u4F1A\u8BDD\u91CC\uFF1B\u5355\u4E2A\u7FA4\u53EF\u5728\u300C\u89D2\u8272\u300D\u9875\u5355\u72EC\u8986\u76D6\u3002\u7559\u7A7A\u4FDD\u5B58\uFF1D\u5220\u9664\u3002","botDefaults.rolePlaceholder":"\u4F8B\u5982\uFF1A\u4F60\u662F\u540E\u7AEF\u6392\u969C\u52A9\u624B\uFF0C\u56DE\u7B54\u7B80\u6D01\u3001\u4F18\u5148\u7ED9\u53EF\u6267\u884C\u547D\u4EE4\u2026","botDefaults.roleSave":"\u4FDD\u5B58\u89D2\u8272","botDefaults.roleDelete":"\u5220\u9664","botDefaults.roleSaved":"\u5DF2\u4FDD\u5B58","botDefaults.roleDeleted":"\u5DF2\u5220\u9664","botDefaults.roleLoadErr":"\u89D2\u8272\u52A0\u8F7D\u5931\u8D25","botDefaults.sectionAutoStart":"\u4E3B\u52A8\u5F00\u5DE5","botDefaults.autoStartJoin":"\u88AB\u62C9\u8FDB\u65B0\u7FA4\u81EA\u52A8\u5F00\u5DE5","botDefaults.autoStartJoinHelp":"\u5F00\u542F\u540E\uFF0C\u673A\u5668\u4EBA\u4E00\u88AB\u62C9\u8FDB\u65B0\u7FA4\uFF08\u7FA4\u91CC\u6709\u6388\u6743\u7528\u6237 allowedUsers \u65F6\uFF09\u5C31\u81EA\u52A8\u5F00\u4E00\u4E2A\u4F1A\u8BDD\u5F00\u59CB\u5DE5\u4F5C\uFF0C\u65E0\u9700 @\u3002\u5728\u673A\u5668\u4EBA\u7684\u9ED8\u8BA4\u5DE5\u4F5C\u76EE\u5F55\u542F\u52A8\uFF1B\u672A\u914D\u7F6E\u9ED8\u8BA4\u76EE\u5F55\u65F6\u5148\u5F39\u4ED3\u5E93\u9009\u62E9\u5361\u8BA9\u4F60\u9009\u3002\u524D\u63D0\uFF1A\u9700\u5728\u98DE\u4E66\u5F00\u653E\u5E73\u53F0\u4E3A\u672C\u5E94\u7528\u8BA2\u9605\u300C\u673A\u5668\u4EBA\u8FDB\u7FA4\u300D\u4E8B\u4EF6 im.chat.member.bot.added_v1\uFF0C\u5E76\u5F00\u901A\u7FA4\u6210\u5458\u8BFB\u53D6\u6743\u9650\u3002","botDefaults.autoStartJoinPrompt":"\u5165\u7FA4\u9996\u8F6E prompt\uFF08\u53EF\u9009\uFF09","botDefaults.autoStartJoinPromptPlaceholder":"\u586B\u4E86\uFF1D\u4F5C\u4E3A\u5165\u7FA4\u540E\u7684\u7B2C\u4E00\u6761\u4EFB\u52A1\uFF1B\u7559\u7A7A\uFF1D\u673A\u5668\u4EBA\u81EA\u5DF1\u770B\u7FA4\u91CC\u4FE1\u606F\u51B3\u5B9A\u505A\u4EC0\u4E48","botDefaults.autoStartJoinPromptSave":"\u4FDD\u5B58 prompt","botDefaults.autoStartTopic":"\u8BDD\u9898\u7FA4\u65B0\u8BDD\u9898\u81EA\u52A8\u5F00\u5DE5","botDefaults.autoStartTopicHelp":"\u5F00\u542F\u540E\uFF0C\u5728\u8BDD\u9898\u7FA4\u91CC\u6BCF\u5F53\u6709\u4EBA\u65B0\u5F00\u4E00\u4E2A\u8BDD\u9898\uFF0C\u673A\u5668\u4EBA\u5C31\u4F1A\u81EA\u52A8\u63A5\u5165\u8BE5\u8BDD\u9898\u3001\u628A\u9996\u6761\u6D88\u606F\u5F53\u4F5C\u4EFB\u52A1\u5F00\u59CB\u5904\u7406\uFF0C\u65E0\u9700 @\u3002\u4EC5\u5BF9\u8BDD\u9898\u7FA4\u751F\u6548\uFF0C\u666E\u901A\u7FA4\u4E0D\u53D7\u5F71\u54CD\u3002","botDefaults.sectionSessionMode":"\u4F1A\u8BDD\u6A21\u5F0F","botDefaults.p2pMode":"\u79C1\u804A\u4F1A\u8BDD\u6A21\u5F0F","botDefaults.p2pThread":"thread\uFF08\u6BCF\u6761 DM \u72EC\u7ACB\u4F1A\u8BDD\uFF09","botDefaults.p2pChat":"chat\uFF08\u6241\u5E73\u8FDE\u7EED\u5355\u804A\u4F1A\u8BDD\uFF09","botDefaults.p2pHelp":"\u79C1\u804A\uFF081:1 DM\uFF09\u7684\u4F1A\u8BDD\u65B9\u5F0F\uFF1Athread = \u6BCF\u6761\u9876\u5C42\u6D88\u606F\u5404\u81EA\u8D77\u72EC\u7ACB\u4F1A\u8BDD\uFF08\u907F\u514D\u628A\u5BF9\u8BDD\u5806\u8FDB\u540C\u4E00\u4E2A CLI \u8FDB\u7A0B\uFF09\uFF1Bchat = \u6574\u6BB5 DM \u5171\u7528\u4E00\u4E2A\u8FDE\u7EED\u4F1A\u8BDD\u3001\u5171\u4EAB\u4E0A\u4E0B\u6587\uFF08\u7C7B Hermes\uFF09\u3002\u6539\u540E\u7ACB\u5373\u751F\u6548\u3002","botDefaults.regularGroupMode":"\u666E\u901A\u7FA4\u4F1A\u8BDD\u6A21\u5F0F","botDefaults.regularGroupModeChat":"chat\uFF08\u6241\u5E73\u8FDE\u7EED\u5355\u804A\u4F1A\u8BDD\uFF0C\u9ED8\u8BA4\uFF09","botDefaults.regularGroupModeNewTopic":"new-topic\uFF08\u6BCF\u6761\u9876\u5C42 @ \u5404\u5F00\u72EC\u7ACB\u8BDD\u9898\u4E0E\u4F1A\u8BDD\uFF09","botDefaults.regularGroupModeShared":"shared\uFF08\u8BDD\u9898\u6A21\u5F0F\u4F46\u590D\u7528\u540C\u4E00\u4E2A session\uFF09","botDefaults.regularGroupModeHelp":"\u666E\u901A\u7FA4\uFF08\u975E\u8BDD\u9898\u7FA4\uFF09\u91CC @ \u8BE5 bot \u7684\u65B0\u9876\u5C42\u6D88\u606F\u600E\u4E48\u5F00\u4F1A\u8BDD\uFF1Achat = \u6574\u7FA4\u5171\u7528\u4E00\u4E2A\u8FDE\u7EED\u4F1A\u8BDD\uFF08\u9ED8\u8BA4\uFF09\uFF1Bnew-topic = \u6BCF\u6761\u9876\u5C42 @ \u5728\u539F\u6D88\u606F\u4E0B\u5F00\u72EC\u7ACB\u8BDD\u9898\u3001\u5404\u81EA\u72EC\u7ACB\u4F1A\u8BDD\uFF1Bshared = \u8BDD\u9898\u6A21\u5F0F\u4F46\u590D\u7528\u540C\u4E00\u4E2A session\uFF08\u56DE\u590D\u843D\u5728\u8BDD\u9898\u91CC\u3001\u4F46\u5171\u7528\u672C\u7FA4\u4F1A\u8BDD\u4E0E\u4E0A\u4E0B\u6587\uFF09\u3002\u8FD9\u662F per-bot \u9ED8\u8BA4\uFF0C\u5177\u4F53\u67D0\u4E2A\u7FA4\u53EF\u7528 /reply-mode \u5355\u72EC\u8986\u76D6\u3002\u6539\u540E\u7ACB\u5373\u751F\u6548\u3002","botDefaults.mentionMode":"\u7FA4\u804A @ \u7B56\u7565","botDefaults.mentionModeAlways":"\u90FD\u9700\u8981 @\uFF08\u9ED8\u8BA4\uFF09","botDefaults.mentionModeTopic":"\u4EC5\u8BDD\u9898\u5185\u4E0D\u9700\u8981 @","botDefaults.mentionModeNever":"\u6240\u6709\u6D88\u606F\u90FD\u4E0D\u9700\u8981 @","botDefaults.mentionModeHelp":"\u8BE5 bot \u5168\u5C40\u751F\u6548\uFF0C\u51B3\u5B9A\u7FA4\u91CC\u8981\u4E0D\u8981 @ \u624D\u56DE\uFF1A\u300C\u90FD\u9700\u8981 @\u300D= \u4EFB\u4F55\u6D88\u606F\u90FD\u5F97 @\uFF08\u9ED8\u8BA4\uFF0C\u6700\u5B89\u5168\uFF1B\u591A\u4EBA\u7FA4\u91CC\u8BDD\u9898\u5185\u4E5F\u8981 @\uFF09\uFF1B\u300C\u4EC5\u8BDD\u9898\u5185\u4E0D\u9700\u8981 @\u300D= \u8D77\u65B0\u5BF9\u8BDD / \u9876\u5C42\u4ECD\u8981 @\uFF0C\u4F46\u5728\u8BE5 bot \u5DF2\u5F00\u7684\u4EFB\u4F55\u8BDD\u9898\u91CC\uFF08new-topic / shared / \u8BDD\u9898\u7FA4\uFF09\u540E\u7EED\u6D88\u606F\u514D @ \u7EED\u804A\uFF1B\u300C\u6240\u6709\u6D88\u606F\u90FD\u4E0D\u9700\u8981 @\u300D= \u8BE5 bot \u6709\u5BF9\u8BDD\u6743\u9650\u7684\u7FA4\u91CC\u975E @ \u6D88\u606F\u4E5F\u56DE\uFF08\u542B\u5168\u65B0\u6D88\u606F\u51B7\u542F\u52A8\uFF0C\u4EC5\u9002\u5408\u4E13\u7528 / \u503C\u73ED\u5C0F\u7FA4\uFF0C\u591A\u4EBA\u7FA4\u91CC\u4F1A\u89C1\u6D88\u606F\u5C31\u56DE\uFF09\u3002\u6CE8\uFF1A\u7FA4\u91CC\u53EA\u6709\u4F60\u548C\u8BE5 bot\uFF081 \u5BF9 1\uFF09\u65F6\u4E00\u76F4\u514D @\uFF0C\u4E0E\u672C\u8BBE\u7F6E\u65E0\u5173\u3002","botDefaults.sectionGrant":"\u6388\u6743\u4E0E\u989D\u5EA6","botDefaults.restrictGrant":"\u9650\u5236\u88AB\u6388\u6743\u4EBA\u53EA\u80FD\u7EAF\u5BF9\u8BDD","botDefaults.restrictGrantHelp":"\u5F00\u542F\u540E\uFF0C\u88AB /grant \u6388\u6743\u7684\u4EBA\uFF08owner \u81EA\u5DF1\u4E0D\u53D7\u9650\uFF09\u53EA\u80FD\u53D1\u666E\u901A\u5BF9\u8BDD\uFF0C\u6240\u6709 slash \u547D\u4EE4\u4E00\u5F8B\u62E6\u622A\uFF1Abotmux \u81EA\u5E26\u547D\u4EE4\u3001\u900F\u4F20\u547D\u4EE4\u3001/workflow\u3001/introduce\u3001/t \u4EE5\u53CA CLI \u539F\u751F\u547D\u4EE4\uFF08/help \u7B49\uFF09\u3002\u5F62\u5982 /path/to/file \u7684\u5185\u5BB9\u4E0D\u4F1A\u88AB\u8BEF\u5224\u3002","botDefaults.quotaDefault":"\u9ED8\u8BA4\u6D88\u606F\u989D\u5EA6","botDefaults.quotaPlaceholder":"\u7559\u7A7A\uFF1D\u4E0D\u914D\u7F6E\u9ED8\u8BA4\u989D\u5EA6","botDefaults.quotaHelp":"\u4E0D\u5E26\u6570\u5B57\u7684 /grant @x \u9ED8\u8BA4\u7ED9\u7684\u6D88\u606F\u6761\u6570\u3002\u7559\u7A7A\u6216\u70B9\u300C\u5173\u95ED\u300D\u53EA\u662F\u4E0D\u518D\u7ED9\u88F8 /grant \u5957\u9ED8\u8BA4\u989D\u5EA6\uFF0C\u4E0D\u4F1A\u6E05\u6389\u5DF2\u6709\u7684\u989D\u5EA6\u8BA1\u6570\uFF0C\u4E5F\u4E0D\u5F71\u54CD\u663E\u5F0F /grant @x N\u2014\u2014\u5B83\u4EEC\u7167\u5E38\u7EE7\u7EED enforce\u3002\u989D\u5EA6\u7528\u5C3D\u4F1A\u81EA\u52A8\u64A4\u9500\u8BE5\u4EBA\u6388\u6743\u3002","botDefaults.quotaSave":"\u4FDD\u5B58\u989D\u5EA6","botDefaults.quotaOff":"\u5173\u95ED","botDefaults.quotaInvalid":"\u989D\u5EA6\u5FC5\u987B\u662F\u6B63\u6574\u6570","botDefaults.quotaStateOff":"\u5F53\u524D\uFF1A\u672A\u914D\u7F6E\u9ED8\u8BA4\u989D\u5EA6","botDefaults.quotaStateOn":"\u5F53\u524D\uFF1A\u6BCF\u4EBA {count} \u6761","nav.roles":"\u89D2\u8272\u7BA1\u7406","roles.title":"\u89D2\u8272\u7BA1\u7406","roles.subtitle":"\u4E3A\u6BCF\u4E2A\u7FA4\u7EC4\u7684\u6BCF\u4E2A Bot \u5355\u72EC\u8BBE\u7F6E\u89D2\u8272\u63D0\u793A\u8BCD\uFF0CBot \u5728\u8BE5\u7FA4\u4E2D\u4F1A\u4EE5\u6B64\u89D2\u8272\u884C\u4E8B\u3002","roles.search":"\u641C\u7D22\u7FA4\u540D/Bot/ID","roles.refresh":"\u5237\u65B0","roles.selectHint":"\u2190 \u5C55\u5F00\u7FA4\u7EC4\uFF0C\u9009\u62E9\u4E00\u4E2A Bot \u6765\u7F16\u8F91\u89D2\u8272","roles.editorPlaceholder":`\u8F93\u5165\u89D2\u8272\u63CF\u8FF0\uFF0C\u4F8B\u5982\uFF1A
|
|
2
2
|
\u4F60\u662F\u672C\u7FA4\u7684\u6280\u672F\u987E\u95EE\uFF0C\u8D1F\u8D23\u56DE\u7B54\u6240\u6709\u6280\u672F\u95EE\u9898...`,"roles.configured":"\u5DF2\u914D\u7F6E","roles.unconfigured":"\u672A\u914D\u7F6E","roles.noChats":"\u6682\u65E0\u7FA4\u7EC4","roles.preview":"\u9884\u89C8","roles.previewEmpty":"\uFF08\u7A7A\u5185\u5BB9\uFF09","roles.saved":"\u5DF2\u4FDD\u5B58","roles.delete":"\u5220\u9664","roles.save":"\u4FDD\u5B58","roles.confirmDelete":"\u786E\u8BA4\u5220\u9664\u8BE5 Bot \u5728\u6B64\u7FA4\u7684\u89D2\u8272\u914D\u7F6E\uFF1F","roles.botsWithRoles":"\u4E2A Bot \u5DF2\u914D\u7F6E\u89D2\u8272","roles.emptyError":"\u89D2\u8272\u5185\u5BB9\u4E0D\u80FD\u4E3A\u7A7A\uFF0C\u8BF7\u5148\u8F93\u5165\u5185\u5BB9","roles.saveFailed":"\u4FDD\u5B58\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5","common.none":"\u65E0","common.unknown":"\u672A\u77E5","common.now":"\u521A\u521A","common.never":"\u4ECE\u672A","common.all":"\u5168\u90E8","nav.workflows":"\u5DE5\u4F5C\u6D41(beta)","nav.workflowCatalog":"\u76EE\u5F55","workflow.subnav.runs":"\u8FD0\u884C","workflow.subnav.catalog":"\u76EE\u5F55","workflow.searchPlaceholder":"\u641C\u7D22 runId / workflowId / chatId","workflow.filter.nonTerminal":"\u975E\u7EC8\u6001","workflow.filter.all":"\u5168\u90E8","workflow.status.pending":"\u5F85\u5F00\u59CB","workflow.status.running":"\u8FD0\u884C\u4E2D","workflow.status.waiting":"\u7B49\u5F85\u4E2D","workflow.status.effectAttempting":"\u526F\u4F5C\u7528\u4E2D","workflow.status.timedOut":"\u5DF2\u8D85\u65F6","workflow.status.succeeded":"\u6210\u529F","workflow.status.failed":"\u5931\u8D25","workflow.status.cancelled":"\u5DF2\u53D6\u6D88","workflow.table.run":"\u8FD0\u884C","workflow.table.workflow":"\u5DE5\u4F5C\u6D41","workflow.table.status":"\u72B6\u6001","workflow.table.lastSeq":"\u6700\u540E\u5E8F\u53F7","workflow.table.dangling":"\u60AC\u6302 dEf/dAct/dWait","workflow.table.updated":"\u66F4\u65B0\u65F6\u95F4","workflow.table.chatApp":"\u7FA4\u804A / \u5E94\u7528","workflow.list.failedLoad":"\u52A0\u8F7D\u5931\u8D25\uFF1A{error}","workflow.list.noRuns":"\u6CA1\u6709\u5339\u914D\u7684\u8FD0\u884C\u3002","workflow.list.noFilterMatch":"\u6CA1\u6709\u7B26\u5408\u7B5B\u9009\u6761\u4EF6\u7684\u8FD0\u884C\u3002","workflow.list.loaded":"{count} \u4E2A\u8FD0\u884C \xB7 \u5237\u65B0\u4E8E {time}","workflow.list.error":"\u9519\u8BEF\uFF1A{error}","workflow.detail.back":"\u8FD4\u56DE","workflow.detail.loading":"\u52A0\u8F7D\u4E2D...","workflow.detail.loadFailed":"\u52A0\u8F7D\u5931\u8D25","workflow.detail.cancel":"\u53D6\u6D88","workflow.detail.cliCancelOnly":"\u4EC5 CLI \u53EF\u53D6\u6D88","workflow.detail.cancelTitle":"\u53D6\u6D88\u8FD9\u4E2A\u5DE5\u4F5C\u6D41\u8FD0\u884C","workflow.detail.cliCancelTitle":"\u65E0\u6CD5\u5728\u9875\u9762\u53D6\u6D88\uFF1A\u8BF7\u4F7F\u7528 botmux workflow cancel {runId}","workflow.detail.nodes":"\u8282\u70B9 / Activity","workflow.detail.parallel":"\u5E76\u53D1\u6267\u884C","workflow.detail.parallelMeta":"{count} \u6B21\u5C1D\u8BD5 \xB7 \u6700\u9AD8\u5E76\u53D1 {max} \xB7 \u8FD0\u884C\u4E2D {running}","workflow.detail.noParallelData":"\u8FD8\u6CA1\u6709 attempt \u65F6\u95F4\u6570\u636E\u3002","workflow.detail.parallelNow":"\u73B0\u5728","workflow.detail.node":"\u8282\u70B9","workflow.detail.nodeStatus":"\u8282\u70B9\u72B6\u6001","workflow.detail.activity":"Activity","workflow.detail.activityStatus":"Activity \u72B6\u6001","workflow.detail.attempts":"\u5C1D\u8BD5\u6B21\u6570","workflow.detail.current":"\u5F53\u524D\u5C1D\u8BD5","workflow.detail.detail":"\u8BE6\u60C5","workflow.detail.nodeIO":"\u8282\u70B9\u8F93\u5165\u8F93\u51FA","workflow.detail.timeline":"\u65F6\u95F4\u7EBF","workflow.detail.loadOlder":"\u52A0\u8F7D\u66F4\u65E9\u4E8B\u4EF6","workflow.detail.seq":"\u5E8F\u53F7","workflow.detail.actor":"\u6267\u884C\u8005","workflow.detail.error":"\u9519\u8BEF","workflow.detail.event":"\u4E8B\u4EF6","workflow.detail.time":"\u65F6\u95F4","workflow.detail.refreshed":"\u5237\u65B0\u4E8E {time}","workflow.detail.unknownRun":"\u672A\u77E5\u8FD0\u884C","workflow.detail.snapshotHttp":"snapshot HTTP {status}","workflow.detail.eventsHttp":"events HTTP {status}","workflow.detail.cancelUnavailable":"\u65E0\u6CD5\u53D6\u6D88\uFF1A\u8BF7\u4F7F\u7528 botmux workflow cancel {runId}","workflow.detail.cancelConfirm":`\u786E\u8BA4\u53D6\u6D88\u5DE5\u4F5C\u6D41\u8FD0\u884C {runId}\uFF1F
|
|
3
3
|
|
|
4
4
|
{total} \u4E2A\u60AC\u6302\u9879\u4F1A\u7531 cancel recovery \u5904\u7406\u3002
|
|
5
5
|
effects={effects}, activities={activities}, waits={waits}, cancels={cancels}`,"workflow.detail.writeAccessCancel":"\u9700\u8981\u5199\u6743\u9650\uFF1A\u8BF7\u5728\u7EC8\u7AEF\u8FD0\u884C `botmux dashboard` \u83B7\u53D6\u4E00\u6B21\u6027 URL\uFF0C\u6253\u5F00\u540E\u5199\u5165 cookie\uFF0C\u518D\u56DE\u6765\u70B9\u51FB\u53D6\u6D88\u3002","workflow.detail.cancelHttp":"cancel HTTP {status}","workflow.detail.cancelPending":"\u53D6\u6D88\u5DF2\u63D0\u4EA4\uFF1B\u7B49\u5F85\u8FD0\u884C\u4E2D\u7684 activity \u6536\u655B","workflow.detail.writeAccessApproval":"\u9700\u8981\u5199\u6743\u9650\uFF1A\u8BF7\u5728\u7EC8\u7AEF\u8FD0\u884C `botmux dashboard` \u83B7\u53D6\u4E00\u6B21\u6027 URL\uFF0C\u6253\u5F00\u540E\u5199\u5165 cookie\uFF0C\u518D\u56DE\u6765\u5BA1\u6279\u3002","workflow.detail.actionHttp":"{action} HTTP {status}","workflow.detail.approved":"\u5DF2\u901A\u8FC7","workflow.detail.rejected":"\u5DF2\u62D2\u7EDD","workflow.detail.alreadyTerminal":"\u8FD0\u884C\u5DF2\u7EC8\u6001\uFF1B\u672A\u5E94\u7528\u201C{label}\u201D\u3002","workflow.detail.workflowContinue":"{label}\uFF1B\u7B49\u5F85\u5DE5\u4F5C\u6D41\u7EE7\u7EED\u6267\u884C\u3002","workflow.detail.workflowRefreshing":"{label}\uFF1B\u6B63\u5728\u5237\u65B0\u5DE5\u4F5C\u6D41\u72B6\u6001\u3002","workflow.detail.eventsLoaded":"\u5DF2\u52A0\u8F7D {loaded}/{total} \u4E2A\u4E8B\u4EF6","workflow.detail.dangling":"\u60AC\u6302\u9879","workflow.detail.noDangling":"\u6CA1\u6709\u60AC\u6302\u5DE5\u4F5C\u3002","workflow.detail.none":"\u65E0","workflow.detail.noNodes":"\u8FD8\u6CA1\u6709\u8282\u70B9\u3002","workflow.detail.idle":"\u7A7A\u95F2","workflow.detail.noNodeIO":"\u8FD8\u6CA1\u6709\u8282\u70B9\u8F93\u5165\u8F93\u51FA\u3002","workflow.detail.notDispatched":"\u5C1A\u672A\u6D3E\u53D1","workflow.detail.noAttempt":"\u8FD8\u6CA1\u6709\u5C1D\u8BD5","workflow.detail.attempt":"\u5C1D\u8BD5","workflow.detail.authoredInput":"\u539F\u59CB\u8F93\u5165","workflow.detail.resolvedInput":"\u89E3\u6790\u540E\u8F93\u5165","workflow.detail.output":"\u8F93\u51FA","workflow.detail.executionLog":"\u6267\u884C\u65E5\u5FD7","workflow.detail.liveTerminal":"\u5B9E\u65F6\u7EC8\u7AEF","workflow.detail.terminalLive":"\u5728\u7EBF","workflow.detail.terminalClosedShort":"\u5DF2\u5173\u95ED","workflow.detail.terminalClosed":"\u7EC8\u7AEF\u5DF2\u5173\u95ED\u3002\u8BF7\u67E5\u770B\u4E0B\u65B9\u6267\u884C\u65E5\u5FD7\u83B7\u53D6\u6700\u7EC8\u8BB0\u5F55\u3002","workflow.detail.openTerminalNewTab":"\u5728\u65B0\u6807\u7B7E\u9875\u6253\u5F00\u7EC8\u7AEF","workflow.detail.terminalReplay":"\u7EC8\u7AEF\u56DE\u653E","workflow.detail.openReplayNewTab":"\u5728\u65B0\u6807\u7B7E\u9875\u6253\u5F00\u56DE\u653E","workflow.detail.downloadFullLog":"\u4E0B\u8F7D\u5B8C\u6574\u65E5\u5FD7","workflow.detail.terminalResume":"\u8C03\u8BD5\u4F1A\u8BDD","workflow.detail.openResumeNewTab":"\u5728\u65B0\u6807\u7B7E\u9875\u6253\u5F00\u8C03\u8BD5\u4F1A\u8BDD","workflow.detail.resumeSession":"\u7EE7\u7EED\u4F1A\u8BDD","workflow.detail.resumeStarting":"\u6B63\u5728\u542F\u52A8\u2026","workflow.detail.endResumeSession":"\u7ED3\u675F\u8C03\u8BD5\u4F1A\u8BDD","workflow.detail.resumeEnding":"\u7ED3\u675F\u4E2D\u2026","workflow.detail.resumeUnsupportedCli":"{cliId} CLI \u4E0D\u652F\u6301 resume","workflow.detail.resumeMissingCliSession":"\u7F3A\u5C11 cliSessionId\uFF0C\u65E0\u6CD5 resume","workflow.detail.resumeStartFailed":"\u542F\u52A8\u8C03\u8BD5\u4F1A\u8BDD\u5931\u8D25 (HTTP {status})","workflow.detail.resumeEndFailed":"\u7ED3\u675F\u8C03\u8BD5\u4F1A\u8BDD\u5931\u8D25 (HTTP {status})","workflow.detail.writeAccessResume":"\u9700\u8981\u5199\u5165\u6743\u9650\u624D\u80FD resume \u4F1A\u8BDD\u3002","workflow.detail.waitPrompt":"\u7B49\u5F85\u63D0\u793A","workflow.detail.approvalComment":"\u5BA1\u6279\u5907\u6CE8","workflow.detail.optionalComment":"\u53EF\u9009\u5907\u6CE8","workflow.detail.approve":"\u901A\u8FC7","workflow.detail.reject":"\u62D2\u7EDD","workflow.detail.submitting":"\u63D0\u4EA4\u4E2D...","workflow.detail.empty":"\u7A7A","workflow.detail.truncated":"\u5DF2\u622A\u65AD","workflow.detail.noData":"\u6CA1\u6709\u6570\u636E\u3002","workflow.detail.noPreview":"\u6CA1\u6709\u9884\u89C8\u3002","workflow.detail.open":"\u6253\u5F00","workflow.detail.deadline":"\u622A\u6B62","workflow.detail.effect":"\u526F\u4F5C\u7528","workflow.detail.wait":"\u7B49\u5F85","workflow.detail.noEvents":"\u8FD8\u6CA1\u6709\u4E8B\u4EF6\u3002","workflow.summary.workflow":"\u5DE5\u4F5C\u6D41","workflow.summary.status":"\u72B6\u6001","workflow.summary.lastSeq":"\u6700\u540E\u5E8F\u53F7","workflow.summary.updated":"\u66F4\u65B0\u65F6\u95F4","workflow.summary.revision":"\u4FEE\u8BA2","workflow.summary.initiator":"\u53D1\u8D77\u4EBA","workflow.summary.failedNode":"\u5931\u8D25\u8282\u70B9","workflow.summary.cancelOrigin":"\u53D6\u6D88\u6765\u6E90","workflow.summary.chat":"\u7FA4\u804A","workflow.summary.app":"\u5E94\u7528","workflow.dangling.activities":"Activities","workflow.dangling.effects":"Effects","workflow.dangling.waits":"Waits","workflow.dangling.cancels":"Cancels","catalog.title":"\u5DE5\u4F5C\u6D41\u76EE\u5F55","catalog.subtitle":"\u4ECE\u5DF2\u4FDD\u5B58\u7684 workflow \u5B9A\u4E49\u521B\u5EFA\u4E00\u6B21\u8FD0\u884C\u3002","catalog.searchPlaceholder":"\u641C\u7D22 workflowId / \u8DEF\u5F84","catalog.refresh":"\u5237\u65B0","catalog.loading":"\u6B63\u5728\u52A0\u8F7D\u76EE\u5F55...","catalog.loadFailed":"\u76EE\u5F55\u52A0\u8F7D\u5931\u8D25\uFF1A{error}","catalog.noDefinitions":"\u6CA1\u6709\u627E\u5230 workflow \u5B9A\u4E49\u3002","catalog.noFilterMatch":"\u6CA1\u6709\u7B26\u5408\u7B5B\u9009\u6761\u4EF6\u7684\u5B9A\u4E49\u3002","catalog.table.workflow":"\u5DE5\u4F5C\u6D41","catalog.table.version":"\u7248\u672C","catalog.table.params":"\u53C2\u6570","catalog.table.nodes":"\u8282\u70B9","catalog.table.revision":"\u4FEE\u8BA2","catalog.table.path":"\u8DEF\u5F84","catalog.paramSummary":"{required}/{total} \u5FC5\u586B","catalog.back":"\u8FD4\u56DE\u76EE\u5F55","catalog.detailTitle":"\u5DE5\u4F5C\u6D41\u5B9A\u4E49","catalog.definitionLoadFailed":"\u5B9A\u4E49\u52A0\u8F7D\u5931\u8D25\uFF1A{error}","catalog.summary":"\u6458\u8981","catalog.paramsSchema":"\u53C2\u6570 Schema","catalog.definitionJson":"\u5B9A\u4E49 JSON","catalog.runPanel":"\u8FD0\u884C\u5DE5\u4F5C\u6D41","catalog.paramsJson":"\u53C2\u6570 JSON","catalog.paramsPlaceholder":`{
|
|
6
6
|
"city": "\u5317\u4EAC"
|
|
7
|
-
}`,"catalog.chatId":"\u7FA4\u804A ID","catalog.larkAppId":"\u98DE\u4E66\u5E94\u7528 ID","catalog.chatBindingHint":"\u5FC5\u586B\uFF0C\u7528\u4E8E\u786E\u5B9A humanGate \u5361\u7247\u53D1\u9001\u5230\u54EA\u4E2A\u98DE\u4E66\u7FA4\uFF0C\u4EE5\u53CA\u53D6\u6D88\u8DEF\u7531\u5F52\u5C5E\u3002","catalog.run":"\u8FD0\u884C","catalog.running":"\u542F\u52A8\u4E2D...","catalog.badParamsJson":"\u53C2\u6570\u5FC5\u987B\u662F JSON object\u3002","catalog.writeAccess":"\u9700\u8981\u5199\u6743\u9650\uFF1A\u8BF7\u5728\u7EC8\u7AEF\u8FD0\u884C `botmux dashboard` \u83B7\u53D6\u4E00\u6B21\u6027 URL\uFF0C\u6253\u5F00\u540E\u5199\u5165 cookie\uFF0C\u518D\u56DE\u6765\u8FD0\u884C\u3002","catalog.runHttp":"run HTTP {status}","catalog.runStarted":"\u8FD0\u884C\u5DF2\u542F\u52A8\uFF1B\u6B63\u5728\u6253\u5F00\u8BE6\u60C5\u9875...","catalog.invalidParams":"\u53C2\u6570\u65E0\u6548","catalog.issue":"{path}: {message}","catalog.noParams":"\u6CA1\u6709\u58F0\u660E\u53C2\u6570\u3002","catalog.required":"\u5FC5\u586B","catalog.optional":"\u53EF\u9009","catalog.default":"\u9ED8\u8BA4\u503C","catalog.description":"\u8BF4\u660E","catalog.path":"\u8DEF\u5F84","catalog.revision":"\u4FEE\u8BA2","catalog.nodeCount":"\u8282\u70B9\u6570"},Lo={"app.name":"botmux","app.subtitle":"Feishu AI CLI Control","time.secondsAgo":"{value}s ago","time.minutesAgo":"{value}m ago","time.hoursAgo":"{value}h ago","nav.overview":"Overview","nav.sessions":"Sessions","nav.groups":"Groups","nav.schedules":"Schedules","nav.settings":"Settings","nav.botDefaults":"Bot Defaults","status.live":"Live","status.disconnected":"Disconnected","status.system":"System","status.light":"Light","status.dark":"Dark","status.language":"Language","status.theme":"Theme","theme.base":"Basic","theme.skins":"Skins","skin.cyber":"2077","skin.genshin":"Genshin","skin.fallout":"Fallout","skin.prts":"PRTS","skin.bluearchive":"Blue Archive","skin.zzz":"ZZZ","skin.dragonball":"Dragon Ball","skin.ikun":"ikun","botOnboarding.add":"Add Bot","botOnboarding.title":"Add Bot","botOnboarding.intro":"Pick a CLI and working directory, scan to create a PersonalAgent app \u2014 the dashboard writes it to local bots.json and auto-configures Open Platform permissions.","botOnboarding.cliLabel":"CLI adapter","botOnboarding.dirLabel":"Working directory","botOnboarding.dirPlaceholder":"Default ~ (home); must exist on the daemon host","botOnboarding.modelLabel":"Model (optional)","botOnboarding.modelPlaceholder":"Leave empty for the CLI default model","botOnboarding.startScan":"Start scan","botOnboarding.cancel":"Cancel","botOnboarding.starting":"Generating QR code...","botOnboarding.waiting":"Scan with the Feishu app to create the app.","botOnboarding.verifying":"Scan accepted. Verifying credentials...","botOnboarding.configuringPermissions":"Auto-configuring Open Platform permissions\u2026","botOnboarding.platformScanHint":"Scan once more with the Feishu app to sign in to the Open Platform (to auto-import permissions, set the callback, and submit a version).","botOnboarding.platformQrAlt":"Open Platform login QR code","botOnboarding.completed":"Bot added.","botOnboarding.permissionOk":"Auto-imported {count} permissions.","botOnboarding.permissionSkipped":"({count} skipped \u2014 not in the tenant catalog)","botOnboarding.permissionVersion":"Submitted version {version}.","botOnboarding.permissionManual":"Permissions could not be auto-configured. Complete these steps manually:","botOnboarding.metaDir":"Dir","botOnboarding.failed":"Add failed","botOnboarding.openLink":"Open scan link in browser","botOnboarding.close":"Close","botOnboarding.restartHint":"bots.json has been updated. Run pnpm daemon:restart for the new bot to take effect.","botOnboarding.qrAlt":"Feishu bot onboarding QR code","overview.title":"Workbench","overview.subtitle":"Live status of your AI teammates \xB7 synced with Feishu topics","overview.team":"AI Team","overview.teamHint":"Live status of every digital teammate","overview.attention":"Needs You","overview.attentionHint":"Blocked tasks \u2014 one reply gets them moving again","overview.activeSessions":"Active Sessions","overview.activeSessionsHint":"Sessions currently running","overview.today":"Right Now","overview.todayHint":"Active session distribution","overview.viewAll":"View all \u2192","overview.botIdle":"Standing by, ready for new tasks","overview.botOffline":"Offline \u2014 daemon not running","overview.botBusy":"Working \xB7 {count} sessions","overview.botNeedsYou":"Waiting on you","overview.botReady":"Ready","overview.botOff":"Offline","overview.sessionsCount":"{count} sessions","overview.lastActive":"active {time} ago","overview.noAttention":"Nothing waiting on you","strip.pending":"{count} pending","strip.longest":"waiting {time} \u2014 {bot} {reason}","strip.handle":"Handle now","overview.openSessions":"Active Sessions","overview.workingSessions":"Working","overview.onlineBots":"Online Bots","overview.schedules":"Schedules","overview.groups":"Groups Seen","overview.enabledSchedules":"Enabled","overview.total":"total","overview.active":"active","overview.daemonRegistry":"daemon registry","overview.chatMatrix":"chat matrix","overview.recentSessions":"Recent Sessions","overview.nextSchedules":"Next Runs","overview.noSessions":"No sessions yet.","overview.noSchedules":"No schedules yet.","sessions.title":"Session Control","sessions.subtitle":"Locate Feishu topics, open Web Terminal, close or resume CLI sessions.","sessions.search":"Search working dir / title / IDs","sessions.anyStatus":"Any status","sessions.adoptAny":"adopt: any","sessions.adoptYes":"adopt: yes","sessions.adoptNo":"adopt: no","sessions.activeOnly":"Active only","sessions.closeSelected":"Close selected","sessions.clearSelection":"Clear","sessions.selectedCount":"{count} sessions selected","sessions.bot":"Bot","sessions.cli":"CLI","sessions.chat":"Chat","sessions.openChat":"Open chat","sessions.status":"Status","sessions.titleCol":"Title","sessions.workingDir":"Working Dir","sessions.created":"Created","sessions.last":"Last","sessions.adopt":"Adopt","sessions.actions":"Actions","sessions.details":"Details","sessions.copy":"Copy","sessions.copied":"Copied","sessions.locate":"Locate Topic","sessions.locating":"Sending...","sessions.cooldown":"Cooldown {seconds}s","sessions.openTerminal":"Terminal","sessions.close":"Close Session","sessions.resume":"Resume Session","sessions.dismiss":"Close","sessions.closeConfirm":"Close this session?","sessions.resumeFailed":"Resume failed","sessions.closeBulkConfirm":"Close {count} selected sessions?","sessions.empty":"No sessions match the filters.","sessions.viewMode":"Session view","sessions.viewBoard":"Board","sessions.viewTable":"Table","sessions.selectSession":"Select session","sessions.board.needsYou":"Needs You","sessions.board.needsYouHint":"Repo, TUI, or usage limit waiting","sessions.board.starting":"Starting","sessions.board.startingHint":"CLI is spawning or resuming","sessions.board.working":"Working","sessions.board.workingHint":"Streaming, analyzing, or using tools","sessions.board.idle":"Idle","sessions.board.idleHint":"Ready for the next message","sessions.board.emptyColumn":"No sessions","sessions.board.signalRepo":"Repo needed","sessions.board.signalPrompt":"TUI choice needed","sessions.board.signalLimited":"Usage limited","sessions.board.dragHint":"Drag header to reorder columns","sessions.board.moveLeft":"Move column left","sessions.board.moveRight":"Move column right","groups.title":"Groups & Bots","groups.subtitle":"Inspect the chat x bot matrix and manage group creation, oncall, leave, and disband flows.","groups.search":"Search chat name / ID / owner","groups.missingOnly":"Missing bot only","groups.refresh":"Refresh","groups.create":"New Group","groups.chat":"Chat","groups.actions":"Actions","groups.addBots":"Add Bots","groups.manage":"Manage","groups.empty":"No chats match the filters.","groups.createTitle":"Create New Group","groups.createHelp":"Pick bots to invite. The dashboard chooses an online daemon as creator.","groups.name":"Group Name","groups.namePlaceholder":"e.g. AI ChangeLog","groups.bindDir":"Bind Directory","groups.bindDirHelp":"New topics start the CLI here and skip repo selection.","groups.botPicker":"Bots","groups.createSubmit":"Create","groups.cancel":"Cancel","groups.successTitle":"Group Created","groups.openGroup":"Open Group","groups.manageTitle":"Manage {name}","groups.owner":"Owner","groups.oncall":"Oncall Mode","groups.oncallHelp":"When enabled, group members can @ the bot; new topics use the bound directory.","groups.leaveTitle":"Select Bots to Leave","groups.leaveSelected":"Selected Bots Leave","groups.disband":"Disband Group","groups.dangerHint":"Disband only works when an in-chat bot is the owner. Otherwise prefer leaving the chat.","groups.save":"Save","groups.needWorkingDir":"Working directory is required","groups.noBotsOnline":"No bots online. Restart the daemon first.","schedules.title":"Schedules","schedules.subtitle":"View, pause, resume, and run scheduled tasks across daemons.","schedules.search":"Search name / prompt / working dir","schedules.anyKind":"Any kind","schedules.enabledOnly":"Enabled only","schedules.name":"Name","schedules.bot":"Bot","schedules.schedule":"Schedule","schedules.next":"Next","schedules.last":"Last","schedules.repeat":"Repeat","schedules.enabled":"Enabled","schedules.actions":"Actions","schedules.runNow":"Run Now","schedules.pause":"Pause","schedules.resume":"Resume","schedules.empty":"No schedules.","settings.title":"Global Settings","settings.subtitle":"Manage machine-wide botmux dashboard behavior shared by every bot.","settings.loading":"Loading settings\u2026","settings.loadFailed":"Failed to load settings","settings.readOnlyVisitor":"Read-only access \u2014 changing settings requires an authorized link (run botmux dashboard).","settings.sectionAccess":"Access","settings.publicReadOnly":"Allow tokenless read-only dashboard access","settings.publicReadOnlyHelp":"When enabled, visitors without a token can view the dashboard, sessions, schedules, and SSE. Writes such as close, pause, and approvals still require token auth. Sensitive raw terminal logs always require a token.","settings.sectionCards":"Feishu Cards","settings.openTerminalInFeishu":"Open streaming-card terminals in the Feishu sidebar","settings.openTerminalInFeishuHelp":"Off by default: terminal buttons open the Web Terminal URL directly. When enabled, botmux wraps the URL with Feishu web_url/open so Feishu PC opens it in a sidebar. Terminal write access is still controlled by its token.","settings.saving":"Saving\u2026","settings.saved":"Saved","settings.saveFailed":"Save failed","botDefaults.title":"Bot Profiles","botDefaults.subtitle":"Per-bot defaults: oncall, proactive start, persona role, cards and grants.","botDefaults.metaOnline":"Online \xB7 daemon healthy","botDefaults.search":"Search bot name / app id","botDefaults.refresh":"Refresh","botDefaults.sectionOncall":"New-chat Oncall","botDefaults.sectionBrand":"Card Signature","botDefaults.warning":"When enabled, chats without an oncall binding auto-bind to this directory on their next new topic. Manually bound or unbound chats are preserved.","botDefaults.empty":"No bots online. Run botmux restart first.","botDefaults.defaultOncall":"Default to oncall mode","botDefaults.defaultOncallHelp":"Unbound chats auto-bind on the next new topic","botDefaults.workingDir":"Default Working Directory","botDefaults.lastEnabled":"Last Enabled","botDefaults.autobound":"{count} chats auto-bound","botDefaults.save":"Save","botDefaults.required":"Working directory is required when enabled","botDefaults.brandLabel":"Signature (card footer)","botDefaults.brandLabelHelp":"Footer signature on cards this bot sends. Save empty = hide; fill in = custom (markdown ok, e.g. [Acme](https://\u2026)); Reset = show botmux.","botDefaults.brandLabelPlaceholder":"Default: botmux (empty = hidden)","botDefaults.brandSave":"Save Signature","botDefaults.brandReset":"Reset to default","botDefaults.brandStateDefault":"Current: default botmux","botDefaults.brandStateOff":"Current: off","botDefaults.brandStateCustom":"Current: custom","botDefaults.sectionCard":"Card Behavior","botDefaults.disableStreaming":"Disable streaming card","botDefaults.disableStreamingHelp":"Stop posting the live session status card (and its Open Terminal entry). The task's final result still arrives as a message. For those who find the live card noisy.","botDefaults.writableLink":"Put a writable terminal link on the card","botDefaults.writableLinkHelp":'\u26A0\uFE0F Embeds a writable terminal link in the streaming card body \u2014 anyone in the chat can open and drive the terminal. Off = current behavior (private DM via the "Get Write Link" button).',"botDefaults.writableLinkMoot":"Streaming card disabled \u2014 this has no effect","botDefaults.privateCard":"/card sends a private card (authorized users only)","botDefaults.privateCardHelp":'Makes /card send an ephemeral "visible-to-specific-people" card: delivered only to the owner (allowedUsers); /grant-authorized talk users and everyone else in the chat cannot see it. Trade-off: it is a static snapshot (no live updates) and only works in regular group chats (topic groups / DMs fail, with no fallback). Affects only the /card command; the auto streaming card is unchanged.',"botDefaults.cardPrefSaved":"Saved","botDefaults.sectionRole":"Default Role","botDefaults.roleHelp":"This bot's default persona (applies across all chats), injected into the bot's sessions in every chat; a single group can override it on the Roles page. Save empty = delete.","botDefaults.rolePlaceholder":"e.g. You are a backend triage assistant; answer concisely, prefer runnable commands\u2026","botDefaults.roleSave":"Save role","botDefaults.roleDelete":"Delete","botDefaults.roleSaved":"Saved","botDefaults.roleDeleted":"Deleted","botDefaults.roleLoadErr":"Failed to load role","botDefaults.sectionAutoStart":"Proactive Start","botDefaults.autoStartJoin":"Auto-start when added to a new chat","botDefaults.autoStartJoinHelp":'When enabled, the bot auto-starts a session and gets to work the moment it is added to a new chat (when an authorized user / allowedUsers is a member), no @ needed. It launches in the bot default working dir; if none is configured it shows a repo-select card first. Prerequisite: subscribe the "bot joined chat" event im.chat.member.bot.added_v1 for this app and grant the member-read scope in the Feishu console.',"botDefaults.autoStartJoinPrompt":"First-turn prompt on join (optional)","botDefaults.autoStartJoinPromptPlaceholder":"Filled = used as the first task after joining; blank = the bot reads the chat and decides what to do","botDefaults.autoStartJoinPromptSave":"Save prompt","botDefaults.autoStartTopic":"Auto-start on new topics in topic groups","botDefaults.autoStartTopicHelp":"When enabled, in a topic group the bot automatically joins each newly opened topic and starts working on its first message, no @ needed. Topic groups only \u2014 regular groups are unaffected.","botDefaults.sectionGrant":"Authorization & Quota","botDefaults.restrictGrant":"Restrict grantees to plain conversation","botDefaults.restrictGrantHelp":"When enabled, /grant-authorized users (the owner is exempt) can only send plain messages; every slash command is blocked: botmux built-in commands, passthrough commands, /workflow, /introduce, /t, and CLI-native commands (/help, etc.). Text like /path/to/file is not misclassified.","botDefaults.quotaDefault":"Default message quota","botDefaults.quotaPlaceholder":"Empty = no default quota","botDefaults.quotaHelp":'Message count a bare /grant @x (no number) hands out. Empty or "Turn off" merely stops applying a default to bare /grant \u2014 it does NOT clear existing quota counters, nor affect an explicit /grant @x N; those keep being enforced. Authorization is auto-revoked once a quota runs out.',"botDefaults.quotaSave":"Save quota","botDefaults.quotaOff":"Turn off","botDefaults.quotaInvalid":"Quota must be a positive integer","botDefaults.quotaStateOff":"Current: no default quota","botDefaults.quotaStateOn":"Current: {count} per grantee","nav.roles":"Roles","roles.title":"Role Management","roles.subtitle":"Set per-bot role prompts for each group. Each bot adopts its own persona in the selected group.","roles.search":"Search group / bot / ID","roles.refresh":"Refresh","roles.selectHint":"\u2190 Expand a group and select a bot to edit its role","roles.editorPlaceholder":`Enter role description, e.g.:
|
|
7
|
+
}`,"catalog.chatId":"\u7FA4\u804A ID","catalog.larkAppId":"\u98DE\u4E66\u5E94\u7528 ID","catalog.chatBindingHint":"\u5FC5\u586B\uFF0C\u7528\u4E8E\u786E\u5B9A humanGate \u5361\u7247\u53D1\u9001\u5230\u54EA\u4E2A\u98DE\u4E66\u7FA4\uFF0C\u4EE5\u53CA\u53D6\u6D88\u8DEF\u7531\u5F52\u5C5E\u3002","catalog.run":"\u8FD0\u884C","catalog.running":"\u542F\u52A8\u4E2D...","catalog.badParamsJson":"\u53C2\u6570\u5FC5\u987B\u662F JSON object\u3002","catalog.writeAccess":"\u9700\u8981\u5199\u6743\u9650\uFF1A\u8BF7\u5728\u7EC8\u7AEF\u8FD0\u884C `botmux dashboard` \u83B7\u53D6\u4E00\u6B21\u6027 URL\uFF0C\u6253\u5F00\u540E\u5199\u5165 cookie\uFF0C\u518D\u56DE\u6765\u8FD0\u884C\u3002","catalog.runHttp":"run HTTP {status}","catalog.runStarted":"\u8FD0\u884C\u5DF2\u542F\u52A8\uFF1B\u6B63\u5728\u6253\u5F00\u8BE6\u60C5\u9875...","catalog.invalidParams":"\u53C2\u6570\u65E0\u6548","catalog.issue":"{path}: {message}","catalog.noParams":"\u6CA1\u6709\u58F0\u660E\u53C2\u6570\u3002","catalog.required":"\u5FC5\u586B","catalog.optional":"\u53EF\u9009","catalog.default":"\u9ED8\u8BA4\u503C","catalog.description":"\u8BF4\u660E","catalog.path":"\u8DEF\u5F84","catalog.revision":"\u4FEE\u8BA2","catalog.nodeCount":"\u8282\u70B9\u6570"},Na={"app.name":"botmux","app.subtitle":"Feishu AI CLI Control","time.secondsAgo":"{value}s ago","time.minutesAgo":"{value}m ago","time.hoursAgo":"{value}h ago","nav.overview":"Overview","nav.sessions":"Sessions","nav.groups":"Groups","nav.schedules":"Schedules","nav.settings":"Settings","nav.botDefaults":"Bot Defaults","status.live":"Live","status.disconnected":"Disconnected","status.system":"System","status.light":"Light","status.dark":"Dark","status.language":"Language","status.theme":"Theme","theme.base":"Basic","theme.skins":"Skins","skin.cyber":"2077","skin.genshin":"Genshin","skin.fallout":"Fallout","skin.prts":"PRTS","skin.bluearchive":"Blue Archive","skin.zzz":"ZZZ","skin.dragonball":"Dragon Ball","skin.ikun":"ikun","botOnboarding.add":"Add Bot","botOnboarding.title":"Add Bot","botOnboarding.intro":"Pick a CLI and working directory, scan to create a PersonalAgent app \u2014 the dashboard writes it to local bots.json and auto-configures Open Platform permissions.","botOnboarding.cliLabel":"CLI adapter","botOnboarding.dirLabel":"Working directory","botOnboarding.dirPlaceholder":"Default ~ (home); must exist on the daemon host","botOnboarding.modelLabel":"Model (optional)","botOnboarding.modelPlaceholder":"Leave empty for the CLI default model","botOnboarding.startScan":"Start scan","botOnboarding.cancel":"Cancel","botOnboarding.starting":"Generating QR code...","botOnboarding.waiting":"Scan with the Feishu app to create the app.","botOnboarding.verifying":"Scan accepted. Verifying credentials...","botOnboarding.configuringPermissions":"Auto-configuring Open Platform permissions\u2026","botOnboarding.platformScanHint":"Scan once more with the Feishu app to sign in to the Open Platform (to auto-import permissions, set the callback, and submit a version).","botOnboarding.platformQrAlt":"Open Platform login QR code","botOnboarding.completed":"Bot added.","botOnboarding.permissionOk":"Auto-imported {count} permissions.","botOnboarding.permissionSkipped":"({count} skipped \u2014 not in the tenant catalog)","botOnboarding.permissionVersion":"Submitted version {version}.","botOnboarding.permissionManual":"Permissions could not be auto-configured. Complete these steps manually:","botOnboarding.metaDir":"Dir","botOnboarding.failed":"Add failed","botOnboarding.openLink":"Open scan link in browser","botOnboarding.close":"Close","botOnboarding.restartHint":"bots.json has been updated. Run pnpm daemon:restart for the new bot to take effect.","botOnboarding.qrAlt":"Feishu bot onboarding QR code","overview.title":"Workbench","overview.subtitle":"Live status of your AI teammates \xB7 synced with Feishu topics","overview.team":"AI Team","overview.teamHint":"Live status of every digital teammate","overview.attention":"Needs You","overview.attentionHint":"Blocked tasks \u2014 one reply gets them moving again","overview.activeSessions":"Active Sessions","overview.activeSessionsHint":"Sessions currently running","overview.today":"Right Now","overview.todayHint":"Active session distribution","overview.viewAll":"View all \u2192","overview.botIdle":"Standing by, ready for new tasks","overview.botOffline":"Offline \u2014 daemon not running","overview.botBusy":"Working \xB7 {count} sessions","overview.botNeedsYou":"Waiting on you","overview.botReady":"Ready","overview.botOff":"Offline","overview.sessionsCount":"{count} sessions","overview.lastActive":"active {time} ago","overview.noAttention":"Nothing waiting on you","strip.pending":"{count} pending","strip.longest":"waiting {time} \u2014 {bot} {reason}","strip.handle":"Handle now","overview.openSessions":"Active Sessions","overview.workingSessions":"Working","overview.onlineBots":"Online Bots","overview.schedules":"Schedules","overview.groups":"Groups Seen","overview.enabledSchedules":"Enabled","overview.total":"total","overview.active":"active","overview.daemonRegistry":"daemon registry","overview.chatMatrix":"chat matrix","overview.recentSessions":"Recent Sessions","overview.nextSchedules":"Next Runs","overview.noSessions":"No sessions yet.","overview.noSchedules":"No schedules yet.","sessions.title":"Session Control","sessions.subtitle":"Locate Feishu topics, open Web Terminal, close or resume CLI sessions.","sessions.search":"Search working dir / title / IDs","sessions.anyStatus":"Any status","sessions.adoptAny":"adopt: any","sessions.adoptYes":"adopt: yes","sessions.adoptNo":"adopt: no","sessions.activeOnly":"Active only","sessions.closeSelected":"Close selected","sessions.clearSelection":"Clear","sessions.selectedCount":"{count} sessions selected","sessions.bot":"Bot","sessions.cli":"CLI","sessions.chat":"Chat","sessions.openChat":"Open chat","sessions.status":"Status","sessions.titleCol":"Title","sessions.workingDir":"Working Dir","sessions.created":"Created","sessions.last":"Last","sessions.adopt":"Adopt","sessions.actions":"Actions","sessions.details":"Details","sessions.copy":"Copy","sessions.copied":"Copied","sessions.locate":"Locate Topic","sessions.locating":"Sending...","sessions.cooldown":"Cooldown {seconds}s","sessions.openTerminal":"Terminal","sessions.close":"Close Session","sessions.resume":"Resume Session","sessions.dismiss":"Close","sessions.closeConfirm":"Close this session?","sessions.resumeFailed":"Resume failed","sessions.closeBulkConfirm":"Close {count} selected sessions?","sessions.empty":"No sessions match the filters.","sessions.viewMode":"Session view","sessions.viewBoard":"Board","sessions.viewTable":"Table","sessions.selectSession":"Select session","sessions.board.needsYou":"Needs You","sessions.board.needsYouHint":"Repo, TUI, or usage limit waiting","sessions.board.starting":"Starting","sessions.board.startingHint":"CLI is spawning or resuming","sessions.board.working":"Working","sessions.board.workingHint":"Streaming, analyzing, or using tools","sessions.board.idle":"Idle","sessions.board.idleHint":"Ready for the next message","sessions.board.emptyColumn":"No sessions","sessions.board.signalRepo":"Repo needed","sessions.board.signalPrompt":"TUI choice needed","sessions.board.signalLimited":"Usage limited","sessions.board.dragHint":"Drag header to reorder columns","sessions.board.moveLeft":"Move column left","sessions.board.moveRight":"Move column right","groups.title":"Groups & Bots","groups.subtitle":"Inspect the chat x bot matrix and manage group creation, oncall, leave, and disband flows.","groups.search":"Search chat name / ID / owner","groups.missingOnly":"Missing bot only","groups.refresh":"Refresh","groups.create":"New Group","groups.chat":"Chat","groups.actions":"Actions","groups.addBots":"Add Bots","groups.manage":"Manage","groups.empty":"No chats match the filters.","groups.createTitle":"Create New Group","groups.createHelp":"Pick bots to invite. The dashboard chooses an online daemon as creator.","groups.name":"Group Name","groups.namePlaceholder":"e.g. AI ChangeLog","groups.bindDir":"Bind Directory","groups.bindDirHelp":"New topics start the CLI here and skip repo selection.","groups.botPicker":"Bots","groups.createSubmit":"Create","groups.cancel":"Cancel","groups.successTitle":"Group Created","groups.openGroup":"Open Group","groups.manageTitle":"Manage {name}","groups.owner":"Owner","groups.oncall":"Oncall Mode","groups.oncallHelp":"When enabled, group members can @ the bot; new topics use the bound directory.","groups.leaveTitle":"Select Bots to Leave","groups.leaveSelected":"Selected Bots Leave","groups.disband":"Disband Group","groups.dangerHint":"Disband only works when an in-chat bot is the owner. Otherwise prefer leaving the chat.","groups.save":"Save","groups.needWorkingDir":"Working directory is required","groups.noBotsOnline":"No bots online. Restart the daemon first.","schedules.title":"Schedules","schedules.subtitle":"View, pause, resume, and run scheduled tasks across daemons.","schedules.search":"Search name / prompt / working dir","schedules.anyKind":"Any kind","schedules.enabledOnly":"Enabled only","schedules.name":"Name","schedules.bot":"Bot","schedules.schedule":"Schedule","schedules.next":"Next","schedules.last":"Last","schedules.repeat":"Repeat","schedules.enabled":"Enabled","schedules.actions":"Actions","schedules.runNow":"Run Now","schedules.pause":"Pause","schedules.resume":"Resume","schedules.empty":"No schedules.","settings.title":"Global Settings","settings.subtitle":"Manage machine-wide botmux dashboard behavior shared by every bot.","settings.loading":"Loading settings\u2026","settings.loadFailed":"Failed to load settings","settings.readOnlyVisitor":"Read-only access \u2014 changing settings requires an authorized link (run botmux dashboard).","settings.sectionAccess":"Access","settings.publicReadOnly":"Allow tokenless read-only dashboard access","settings.publicReadOnlyHelp":"When enabled, visitors without a token can view the dashboard, sessions, schedules, and SSE. Writes such as close, pause, and approvals still require token auth. Sensitive raw terminal logs always require a token.","settings.sectionCards":"Feishu Cards","settings.openTerminalInFeishu":"Open streaming-card terminals in the Feishu sidebar","settings.openTerminalInFeishuHelp":"Off by default: terminal buttons open the Web Terminal URL directly. When enabled, botmux wraps the URL with Feishu web_url/open so Feishu PC opens it in a sidebar. Terminal write access is still controlled by its token.","settings.saving":"Saving\u2026","settings.saved":"Saved","settings.saveFailed":"Save failed","settings.sectionMaintenance":"Auto Maintenance","settings.autoUpdate":"Auto update","settings.autoUpdateHelp":"Off by default. At the set time, runs npm install -g botmux@latest to install the latest version (download/install only \u2014 no restart). npm-global installs only.","settings.autoRestart":"Auto restart","settings.autoRestartHelp":"Off by default; requires auto-update. After auto-update installs a newer version, restart to apply it if no session is in progress (busy \u21D2 slips to the next day).","settings.autoUpdateLocalDev":"This is a local-dev install (running from source); auto-update is unavailable.","settings.maintenanceTime":"Time","botDefaults.title":"Bot Profiles","botDefaults.subtitle":"Per-bot defaults: oncall, proactive start, persona role, cards and grants.","botDefaults.metaOnline":"Online \xB7 daemon healthy","botDefaults.search":"Search bot name / app id","botDefaults.refresh":"Refresh","botDefaults.sectionOncall":"New-chat Oncall","botDefaults.sectionBrand":"Card Signature","botDefaults.warning":"When enabled, chats without an oncall binding auto-bind to this directory on their next new topic. Manually bound or unbound chats are preserved.","botDefaults.empty":"No bots online. Run botmux restart first.","botDefaults.defaultOncall":"Default to oncall mode","botDefaults.defaultOncallHelp":"Unbound chats auto-bind on the next new topic","botDefaults.workingDir":"Default Working Directory","botDefaults.lastEnabled":"Last Enabled","botDefaults.autobound":"{count} chats auto-bound","botDefaults.save":"Save","botDefaults.required":"Working directory is required when enabled","botDefaults.brandLabel":"Signature (card footer)","botDefaults.brandLabelHelp":"Footer signature on cards this bot sends. Save empty = hide; fill in = custom (markdown ok, e.g. [Acme](https://\u2026)); Reset = show botmux.","botDefaults.brandLabelPlaceholder":"Default: botmux (empty = hidden)","botDefaults.brandSave":"Save Signature","botDefaults.brandReset":"Reset to default","botDefaults.brandStateDefault":"Current: default botmux","botDefaults.brandStateOff":"Current: off","botDefaults.brandStateCustom":"Current: custom","botDefaults.sectionCard":"Card Behavior","botDefaults.disableStreaming":"Disable streaming card","botDefaults.disableStreamingHelp":"Stop posting the live session status card (and its Open Terminal entry). The task's final result still arrives as a message. For those who find the live card noisy.","botDefaults.writableLink":"Put a writable terminal link on the card","botDefaults.writableLinkHelp":'\u26A0\uFE0F Embeds a writable terminal link in the streaming card body \u2014 anyone in the chat can open and drive the terminal. Off = current behavior (private DM via the "Get Write Link" button).',"botDefaults.writableLinkMoot":"Streaming card disabled \u2014 this has no effect","botDefaults.privateCard":"/card sends a private card (authorized users only)","botDefaults.privateCardHelp":'Makes /card send an ephemeral "visible-to-specific-people" card: delivered only to the owner (allowedUsers); /grant-authorized talk users and everyone else in the chat cannot see it. Trade-off: it is a static snapshot (no live updates) and only works in regular group chats (topic groups / DMs fail, with no fallback). Affects only the /card command; the auto streaming card is unchanged.',"botDefaults.cardPrefSaved":"Saved","botDefaults.sectionRole":"Default Role","botDefaults.roleHelp":"This bot's default persona (applies across all chats), injected into the bot's sessions in every chat; a single group can override it on the Roles page. Save empty = delete.","botDefaults.rolePlaceholder":"e.g. You are a backend triage assistant; answer concisely, prefer runnable commands\u2026","botDefaults.roleSave":"Save role","botDefaults.roleDelete":"Delete","botDefaults.roleSaved":"Saved","botDefaults.roleDeleted":"Deleted","botDefaults.roleLoadErr":"Failed to load role","botDefaults.sectionAutoStart":"Proactive Start","botDefaults.autoStartJoin":"Auto-start when added to a new chat","botDefaults.autoStartJoinHelp":'When enabled, the bot auto-starts a session and gets to work the moment it is added to a new chat (when an authorized user / allowedUsers is a member), no @ needed. It launches in the bot default working dir; if none is configured it shows a repo-select card first. Prerequisite: subscribe the "bot joined chat" event im.chat.member.bot.added_v1 for this app and grant the member-read scope in the Feishu console.',"botDefaults.autoStartJoinPrompt":"First-turn prompt on join (optional)","botDefaults.autoStartJoinPromptPlaceholder":"Filled = used as the first task after joining; blank = the bot reads the chat and decides what to do","botDefaults.autoStartJoinPromptSave":"Save prompt","botDefaults.autoStartTopic":"Auto-start on new topics in topic groups","botDefaults.autoStartTopicHelp":"When enabled, in a topic group the bot automatically joins each newly opened topic and starts working on its first message, no @ needed. Topic groups only \u2014 regular groups are unaffected.","botDefaults.sectionSessionMode":"Session mode","botDefaults.p2pMode":"Private chat session mode","botDefaults.p2pThread":"thread (separate session per DM)","botDefaults.p2pChat":"chat (flat continuous session)","botDefaults.p2pHelp":"How 1:1 DMs are sessioned: thread = each top-level message starts its own session (keeps chatter out of one CLI process); chat = the whole DM shares one continuous session and context (Hermes-like). Takes effect immediately.","botDefaults.regularGroupMode":"Regular group session mode","botDefaults.regularGroupModeChat":"chat (flat continuous session, default)","botDefaults.regularGroupModeNewTopic":"new-topic (each top-level @ opens its own topic & session)","botDefaults.regularGroupModeShared":"shared (topic display, reusing one session)","botDefaults.regularGroupModeHelp":"How new top-level @mentions in regular (non-topic) groups are sessioned: chat = the whole group shares one continuous session (default); new-topic = each top-level @ opens its own topic with a separate session; shared = topic display but reuse the same session (replies fold into a topic yet share the group session & context). This is the per-bot default; a specific group can override it via /reply-mode. Takes effect immediately.","botDefaults.mentionMode":"Group @ policy","botDefaults.mentionModeAlways":"Always require @ (default)","botDefaults.mentionModeTopic":"No @ needed inside topics","botDefaults.mentionModeNever":"Never require @","botDefaults.mentionModeHelp":'Bot-global: controls when an @ is required to get a reply in groups. "Always require @" = every message needs an @ (default, safest; inside topics too, in multi-person groups); "No @ needed inside topics" = starting a new conversation / top-level still needs @, but follow-ups inside ANY topic this bot already drives (new-topic / shared / topic-group) continue without @; "Never require @" = non-@ messages are answered too wherever the bot has talk access (including cold-starting on a brand-new message \u2014 only suitable for dedicated / on-call small groups; in busy multi-person groups it replies to everything). Note: when you are alone with this bot (1:1) replies never need an @, independent of this setting.',"botDefaults.sectionGrant":"Authorization & Quota","botDefaults.restrictGrant":"Restrict grantees to plain conversation","botDefaults.restrictGrantHelp":"When enabled, /grant-authorized users (the owner is exempt) can only send plain messages; every slash command is blocked: botmux built-in commands, passthrough commands, /workflow, /introduce, /t, and CLI-native commands (/help, etc.). Text like /path/to/file is not misclassified.","botDefaults.quotaDefault":"Default message quota","botDefaults.quotaPlaceholder":"Empty = no default quota","botDefaults.quotaHelp":'Message count a bare /grant @x (no number) hands out. Empty or "Turn off" merely stops applying a default to bare /grant \u2014 it does NOT clear existing quota counters, nor affect an explicit /grant @x N; those keep being enforced. Authorization is auto-revoked once a quota runs out.',"botDefaults.quotaSave":"Save quota","botDefaults.quotaOff":"Turn off","botDefaults.quotaInvalid":"Quota must be a positive integer","botDefaults.quotaStateOff":"Current: no default quota","botDefaults.quotaStateOn":"Current: {count} per grantee","nav.roles":"Roles","roles.title":"Role Management","roles.subtitle":"Set per-bot role prompts for each group. Each bot adopts its own persona in the selected group.","roles.search":"Search group / bot / ID","roles.refresh":"Refresh","roles.selectHint":"\u2190 Expand a group and select a bot to edit its role","roles.editorPlaceholder":`Enter role description, e.g.:
|
|
8
8
|
You are a code reviewer for this group...`,"roles.configured":"Configured","roles.unconfigured":"None","roles.noChats":"No groups","roles.preview":"Preview","roles.previewEmpty":"(empty)","roles.saved":"Saved","roles.delete":"Delete","roles.save":"Save","roles.confirmDelete":"Delete this bot's role config for this group?","roles.botsWithRoles":"bots configured","roles.emptyError":"Role content cannot be empty","roles.saveFailed":"Save failed, please retry","common.none":"None","common.unknown":"Unknown","common.now":"now","common.never":"never","common.all":"All","nav.workflows":"Workflows (beta)","nav.workflowCatalog":"Catalog","workflow.subnav.runs":"Runs","workflow.subnav.catalog":"Catalog","workflow.searchPlaceholder":"search runId / workflowId / chatId","workflow.filter.nonTerminal":"non-terminal","workflow.filter.all":"all","workflow.status.pending":"pending","workflow.status.running":"running","workflow.status.waiting":"waiting","workflow.status.effectAttempting":"effect","workflow.status.timedOut":"timed out","workflow.status.succeeded":"succeeded","workflow.status.failed":"failed","workflow.status.cancelled":"cancelled","workflow.table.run":"run","workflow.table.workflow":"workflow","workflow.table.status":"status","workflow.table.lastSeq":"lastSeq","workflow.table.dangling":"dEf/dAct/dWait","workflow.table.updated":"updated","workflow.table.chatApp":"chat / app","workflow.list.failedLoad":"Failed to load: {error}","workflow.list.noRuns":"No runs match.","workflow.list.noFilterMatch":"No runs match this filter.","workflow.list.loaded":"{count} runs \xB7 refreshed {time}","workflow.list.error":"error: {error}","workflow.detail.back":"Back","workflow.detail.loading":"Loading...","workflow.detail.loadFailed":"Load failed","workflow.detail.cancel":"Cancel","workflow.detail.cliCancelOnly":"CLI cancel only","workflow.detail.cancelTitle":"Cancel this workflow run","workflow.detail.cliCancelTitle":"Cancel unavailable: use botmux workflow cancel {runId}","workflow.detail.nodes":"Nodes / Activities","workflow.detail.parallel":"Parallel execution","workflow.detail.parallelMeta":"{count} attempt(s) \xB7 max parallel {max} \xB7 running {running}","workflow.detail.noParallelData":"No attempt timing data yet.","workflow.detail.parallelNow":"now","workflow.detail.node":"node","workflow.detail.nodeStatus":"node status","workflow.detail.activity":"activity","workflow.detail.activityStatus":"activity status","workflow.detail.attempts":"attempts","workflow.detail.current":"current","workflow.detail.detail":"detail","workflow.detail.nodeIO":"Node I/O","workflow.detail.timeline":"Timeline","workflow.detail.loadOlder":"Load older","workflow.detail.seq":"seq","workflow.detail.actor":"actor","workflow.detail.error":"error","workflow.detail.event":"event","workflow.detail.time":"time","workflow.detail.refreshed":"refreshed {time}","workflow.detail.unknownRun":"unknown run","workflow.detail.snapshotHttp":"snapshot HTTP {status}","workflow.detail.eventsHttp":"events HTTP {status}","workflow.detail.cancelUnavailable":"cancel unavailable: use botmux workflow cancel {runId}","workflow.detail.cancelConfirm":`Cancel workflow run {runId}?
|
|
9
9
|
|
|
10
10
|
{total} dangling item(s) will be handled by cancel-driven recovery.
|
|
11
11
|
effects={effects}, activities={activities}, waits={waits}, cancels={cancels}`,"workflow.detail.writeAccessCancel":"write access required: run `botmux dashboard` in the terminal to get a one-time URL, open it once to set the cookie, then come back and click cancel again.","workflow.detail.cancelHttp":"cancel HTTP {status}","workflow.detail.cancelPending":"cancel pending; waiting for running activity to drain","workflow.detail.writeAccessApproval":"write access required: run `botmux dashboard` in the terminal to get a one-time URL, open it once to set the cookie, then come back and approve/reject again.","workflow.detail.actionHttp":"{action} HTTP {status}","workflow.detail.approved":"approved","workflow.detail.rejected":"rejected","workflow.detail.alreadyTerminal":"Run already terminal; {label} was not applied.","workflow.detail.workflowContinue":"{label}; waiting for workflow to continue.","workflow.detail.workflowRefreshing":"{label}; refreshing workflow state.","workflow.detail.eventsLoaded":"{loaded}/{total} events loaded","workflow.detail.dangling":"Dangling","workflow.detail.noDangling":"No dangling work.","workflow.detail.none":"none","workflow.detail.noNodes":"No nodes yet.","workflow.detail.idle":"idle","workflow.detail.noNodeIO":"No node I/O yet.","workflow.detail.notDispatched":"not dispatched","workflow.detail.noAttempt":"No attempt yet","workflow.detail.attempt":"attempt","workflow.detail.authoredInput":"Authored input","workflow.detail.resolvedInput":"Resolved input","workflow.detail.output":"Output","workflow.detail.executionLog":"Execution log","workflow.detail.liveTerminal":"Live terminal","workflow.detail.terminalLive":"live","workflow.detail.terminalClosedShort":"closed","workflow.detail.terminalClosed":"Terminal is closed. Use the execution log below for the final transcript.","workflow.detail.openTerminalNewTab":"Open terminal in new tab","workflow.detail.terminalReplay":"Terminal replay","workflow.detail.openReplayNewTab":"Open replay in new tab","workflow.detail.downloadFullLog":"Download full log","workflow.detail.terminalResume":"Debug session","workflow.detail.openResumeNewTab":"Open debug session in new tab","workflow.detail.resumeSession":"Resume session","workflow.detail.resumeStarting":"Starting\u2026","workflow.detail.endResumeSession":"End debug session","workflow.detail.resumeEnding":"Ending\u2026","workflow.detail.resumeUnsupportedCli":'CLI "{cliId}" does not support resume',"workflow.detail.resumeMissingCliSession":"Missing cliSessionId \u2014 cannot resume","workflow.detail.resumeStartFailed":"Failed to start debug session (HTTP {status})","workflow.detail.resumeEndFailed":"Failed to end debug session (HTTP {status})","workflow.detail.writeAccessResume":"Resume requires dashboard write access.","workflow.detail.waitPrompt":"Wait prompt","workflow.detail.approvalComment":"Approval comment","workflow.detail.optionalComment":"Optional comment","workflow.detail.approve":"Approve","workflow.detail.reject":"Reject","workflow.detail.submitting":"Submitting...","workflow.detail.empty":"empty","workflow.detail.truncated":"truncated","workflow.detail.noData":"No data.","workflow.detail.noPreview":"No preview.","workflow.detail.open":"open","workflow.detail.deadline":"deadline","workflow.detail.effect":"effect","workflow.detail.wait":"wait","workflow.detail.noEvents":"No events.","workflow.summary.workflow":"workflow","workflow.summary.status":"status","workflow.summary.lastSeq":"lastSeq","workflow.summary.updated":"updated","workflow.summary.revision":"revision","workflow.summary.initiator":"initiator","workflow.summary.failedNode":"failedNode","workflow.summary.cancelOrigin":"cancelOrigin","workflow.summary.chat":"chat","workflow.summary.app":"app","workflow.dangling.activities":"activities","workflow.dangling.effects":"effects","workflow.dangling.waits":"waits","workflow.dangling.cancels":"cancels","catalog.title":"Workflow catalog","catalog.subtitle":"Create a workflow run from a saved workflow definition.","catalog.searchPlaceholder":"search workflowId / path","catalog.refresh":"Refresh","catalog.loading":"Loading catalog...","catalog.loadFailed":"Failed to load catalog: {error}","catalog.noDefinitions":"No workflow definitions found.","catalog.noFilterMatch":"No definitions match this filter.","catalog.table.workflow":"workflow","catalog.table.version":"version","catalog.table.params":"params","catalog.table.nodes":"nodes","catalog.table.revision":"revision","catalog.table.path":"path","catalog.paramSummary":"{required}/{total} required","catalog.back":"Back to catalog","catalog.detailTitle":"Workflow definition","catalog.definitionLoadFailed":"Failed to load definition: {error}","catalog.summary":"Summary","catalog.paramsSchema":"Params schema","catalog.definitionJson":"Definition JSON","catalog.runPanel":"Run workflow","catalog.paramsJson":"Params JSON","catalog.paramsPlaceholder":`{
|
|
12
12
|
"city": "\u5317\u4EAC"
|
|
13
|
-
}`,"catalog.chatId":"Chat ID","catalog.larkAppId":"Lark app ID","catalog.chatBindingHint":"Required so humanGate cards and cancel routing know which Lark chat owns the run.","catalog.run":"Run","catalog.running":"Starting...","catalog.badParamsJson":"Params must be a JSON object.","catalog.writeAccess":"write access required: run `botmux dashboard` in the terminal to get a one-time URL, open it once to set the cookie, then come back and run again.","catalog.runHttp":"run HTTP {status}","catalog.runStarted":"Run started; opening detail page...","catalog.invalidParams":"Invalid params","catalog.issue":"{path}: {message}","catalog.noParams":"No params declared.","catalog.required":"required","catalog.optional":"optional","catalog.default":"default","catalog.description":"description","catalog.path":"path","catalog.revision":"revision","catalog.nodeCount":"nodes"},sn={zh:Io,en:Lo};function rn(e){if(typeof e!="string")return null;let t=e.trim().toLowerCase();return t==="zh"||t.startsWith("zh-")?"zh":t==="en"||t.startsWith("en-")?"en":null}function Eo(e=[]){for(let t of e){let n=rn(t);if(n)return n}return"zh"}function nt(e){return(t,n)=>{let s=sn[e][t]??sn.zh[t]??t;return n?s.replace(/\{(\w+)\}/g,(a,i)=>{let l=n[i];return l==null?`{${i}}`:String(l)}):s}}function ln(e,t){return(e?rn(e.getItem($t)):null)??Eo(t)}var St="botmux.dashboard.theme",dn="botmux.dashboard.sessions.view";function xo(e){return e==="system"||e==="light"||e==="dark"?e:null}function Mo(e){return e==="board"||e==="table"?e:null}function cn(e,t){return e==="system"?t?"dark":"light":e}function un(e){return xo(e?.getItem(St))??"dark"}function pn(e){return Mo(e?.getItem(dn))??"board"}var mn="botmux.dashboard.sessions.boardOrder",Fe=["needs-you","starting","working","idle"];function Ho(e){if(!Array.isArray(e)||e.length!==Fe.length)return null;let t=new Set(e);if(t.size!==e.length)return null;for(let n of Fe)if(!t.has(n))return null;return e.slice()}function fn(e){try{let t=e?.getItem(mn);return t?Ho(JSON.parse(t))??[...Fe]:[...Fe]}catch{return[...Fe]}}function Tt(e,t){try{e?.setItem(mn,JSON.stringify(t))}catch{}}function gn(e,t){try{e?.setItem(dn,t)}catch{}}var Co=["default","cyber","genshin","fallout","prts","bluearchive","zzz","dragonball","ikun"],It="botmux.dashboard.skin";function Ao(e){return typeof e=="string"&&Co.includes(e)?e:null}function bn(e){return Ao(e?.getItem(It))??"default"}var Do="\uFF71\uFF72\uFF73\uFF74\uFF75\uFF76\uFF77\uFF78\uFF79\uFF7A\uFF7B\uFF7C\uFF7D\uFF7E\uFF7F\uFF80\uFF81\uFF82\uFF830123456789\uFF8A\uFF8B\uFF8C\uFF8D\uFF8E$#%&@*+=<>/\\",hn=["186 100% 60%","56 97% 60%","330 100% 64%","150 80% 58%"],Et=[{key:"shake",dur:280},{key:"rgb",dur:340},{key:"tear",dur:400},{key:"invert",dur:220},{key:"slice",dur:320},{key:"blackout",dur:260}],wn=["ESTABLISHING NETLINK","BYPASSING ICE","DECRYPTING DATAFLOW","SYNCING BOTMUX TELEMETRY","ACCESS GRANTED"],yn="0123456789ABCDEF#<>*/=+\uFF71\uFF72\uFF73\uFF74\uFF75\uFF76\uFF77\uFF78\uFF85\uFF86\uFF87\uFF88\uFF89\uFF8A\uFF8B\uFF8C",Ro=3200,We=[],Be=0,ot=0;function Ht(){return typeof window<"u"&&!!window.matchMedia?.("(prefers-reduced-motion: reduce)").matches}function xt(e,t){let n="";for(let s=0;s<e;s++)n+=t[Math.floor(Math.random()*t.length)];return n}function Oo(e){let t=document.createElement("div");t.className="cyber-rain";let n=Math.max(10,Math.min(26,Math.round(window.innerWidth/70)));for(let s=0;s<n;s++){let a=document.createElement("span");a.className="cyber-rain-col",a.textContent=xt(28+Math.floor(Math.random()*22),Do);let i=hn[Math.floor(Math.random()*hn.length)];a.style.cssText=`left:${(s+.5)/n*100}%;--rc:${i};--sz:${10+Math.floor(Math.random()*9)}px;--op:${(.25+Math.random()*.45).toFixed(2)};--dur:${(6+Math.random()*9).toFixed(1)}s;--delay:${(-Math.random()*12).toFixed(1)}s;`,t.appendChild(a)}e.appendChild(t)}function qo(){if(Ht())return;let e=document.body,t=()=>{let n=Et[Math.floor(Math.random()*Et.length)],s=`cp-fx-${n.key}`;e.classList.add(s),We.push(window.setTimeout(()=>e.classList.remove(s),n.dur)),We.push(window.setTimeout(t,2400+Math.random()*4200))};We.push(window.setTimeout(t,1800+Math.random()*2600))}function Bo(){We.forEach(e=>window.clearTimeout(e)),We=[];for(let e of Et)document.body.classList.remove(`cp-fx-${e.key}`)}function Po(){if(Ht()||document.getElementById("cyber-boot"))return;let e=document.createElement("div");e.id="cyber-boot",e.className="cyber-boot",e.setAttribute("aria-hidden","true"),e.innerHTML='<div class="cyber-boot-grid"></div><div class="cyber-loader"><div class="cyber-loader-frame"><div class="cyber-loader-head"><span>KIROSHI NETLINK</span><span class="cyber-loader-jp">\u4FB5\u5165\u4E2D</span></div><div class="cyber-loader-line"><span class="cyber-loader-prompt">></span><span class="cyber-loader-text"></span><span class="cyber-loader-cursor">_</span></div><div class="cyber-loader-stream"></div></div></div>',document.body.appendChild(e);let t=e.querySelector(".cyber-loader-text"),n=e.querySelector(".cyber-loader-stream"),s=0,a=0,i=0;Be=window.setInterval(()=>{let l=wn[s];t&&(a<l.length?(a+=1,t.textContent=l.slice(0,a)+xt(l.length-a,yn),t.classList.remove("done")):i<16?(i+=1,t.textContent=l,t.classList.add("done")):(s=(s+1)%wn.length,a=0,i=0)),n&&(n.textContent=xt(26,yn))},50),ot=window.setTimeout(()=>{window.clearInterval(Be),Be=0,e.remove()},Ro)}function No(){Be&&(window.clearInterval(Be),Be=0),ot&&(window.clearTimeout(ot),ot=0),document.getElementById("cyber-boot")?.remove()}var Uo=320,vn=16,Ge=!0,qe=0,Lt=0,at=!1,st=[],Mt=[];function jo(){if(at)return;at=!0,Ge=!1,qe=0;let e=Ht(),t="";for(let s=0;s<vn;s++){let a=s%3===0?"186 100% 52%":s%3===1?"330 100% 58%":"56 97% 52%",i=(s%2===0?1:-1)*(8+s%5*7);t+=`<span class="cyber-breach-shard" style="top:${s/vn*100}%;height:${2+s%4*3}%;--shift:${i}px;--delay:${s%8*.09}s;--dur:${(.36+s%5*.12).toFixed(2)}s;--hue:${a}"></span>`}let n=document.createElement("div");n.id="cyber-breach",n.className="cyber-breach",n.setAttribute("aria-hidden","true"),n.innerHTML=`<span class="cyber-breach-flash"></span><span class="cyber-breach-grid"></span><div class="cyber-breach-shards">${t}</div><div class="cyber-breach-banner"><span class="cyber-breach-tag">// BREACH PROTOCOL \u2014 SYSTEM OVERRIDE</span><span class="cyber-breach-caption" data-text="SYSTEM BREACH">SYSTEM BREACH</span><span class="cyber-breach-sub" data-text="NETWATCH OVERRIDE ENGAGED">NETWATCH OVERRIDE ENGAGED</span></div>`,document.body.appendChild(n),e||document.body.classList.add("cyber-breach-quake"),st.push(window.setTimeout(()=>document.body.classList.remove("cyber-breach-quake"),760)),st.push(window.setTimeout(()=>{n.remove(),at=!1},e?2600:4200))}function zo(){Ge=!0,qe=0;let e=document.documentElement,t=()=>e.scrollHeight-(window.innerHeight+window.scrollY)<=4,n=v=>{v<=0||!t()||(qe+=v,qe>Uo&&Ge&&jo())},s=v=>n(v.deltaY),a=v=>{Lt=v.touches[0]?.clientY??0},i=v=>{let y=v.touches[0]?.clientY??0,h=Lt-y;Lt=y,n(h)},l=()=>{t()||(qe=0,Ge=!0)},p=(v,y)=>{window.addEventListener(v,y,{passive:!0}),Mt.push([v,y])};p("wheel",s),p("touchstart",a),p("touchmove",i),p("scroll",l)}function _o(){for(let[e,t]of Mt)window.removeEventListener(e,t);Mt=[],st.forEach(e=>window.clearTimeout(e)),st=[],document.body.classList.remove("cyber-breach-quake"),document.getElementById("cyber-breach")?.remove(),at=!1,Ge=!0,qe=0}function kn(e,t=!1){if(!(typeof document>"u")){if(!e){document.getElementById("cyber-fx")?.remove(),document.getElementById("cyber-hud")?.remove(),document.getElementById("cyber-glitch")?.remove(),Bo(),No(),_o();return}if(!document.getElementById("cyber-fx")){let n=document.createElement("div");n.id="cyber-fx",n.className="cyber-fx",n.setAttribute("aria-hidden","true"),n.innerHTML='<div class="cyber-fx-grid"></div><div class="cyber-fx-scan"></div><span class="cyber-flicker"></span><span class="cyber-rollline"></span>',Oo(n),document.body.appendChild(n);let s=document.createElement("div");s.id="cyber-hud",s.className="cyber-hud",s.setAttribute("aria-hidden","true"),s.innerHTML='<span class="cyber-hud-corner tl"></span><span class="cyber-hud-corner tr"></span><span class="cyber-hud-corner bl"></span><span class="cyber-hud-corner br"></span><span class="cyber-hud-tag">NIGHT CITY // NETWATCH</span>',document.body.appendChild(s);let a=document.createElement("div");a.id="cyber-glitch",a.className="cyber-glitch",a.setAttribute("aria-hidden","true"),document.body.appendChild(a),qo(),zo()}t&&Po()}}var Fo={genshin:2e3,zzz:1900,dragonball:1900,ikun:1900},Wo='<span class="si-bball" aria-hidden="true"><span class="si-bball-seams"><svg viewBox="0 0 100 100"><g fill="none" stroke="#3a1d08" stroke-width="3.4" stroke-linecap="round"><path d="M50 4V96"/><path d="M4 50H96"/><path d="M50 4 Q14 50 50 96"/><path d="M50 4 Q86 50 50 96"/></g></svg></span></span>';function Go(e){switch(e){case"genshin":return'<span class="si-gi-shine" aria-hidden="true"></span><span class="si-gi-text">\u539F\u795E\uFF0C\u542F\u52A8</span>';case"zzz":return'<span class="si-static" aria-hidden="true"></span><span class="si-roll" aria-hidden="true"></span><span class="si-now">NOW LOADING</span>';case"dragonball":return'<span class="si-speed" aria-hidden="true"></span><img class="si-cloud" src="/assets/skins/dragonball-wukong.webp" alt="">';case"ikun":return Wo;default:return""}}function Jo(){return typeof window<"u"&&!!window.matchMedia?.("(prefers-reduced-motion: reduce)").matches}function $n(e){if(typeof document>"u"||Jo())return;let t=Fo[e];if(!t)return;document.getElementById("skin-intro")?.remove();let n=document.createElement("div");n.id="skin-intro",n.className=`skin-intro si-${e}`,n.setAttribute("aria-hidden","true"),n.style.setProperty("--si-dur",`${t}ms`),n.innerHTML=Go(e),document.body.appendChild(n),window.setTimeout(()=>n.remove(),t+80)}var At=class{locale="zh";themeMode="system";resolvedTheme="light";skin="default";listeners=new Set;translate=nt(this.locale);mediaQuery=null;init(){let t=typeof window<"u"?window:void 0;this.locale=ln(t?.localStorage,Vo()),this.translate=nt(this.locale),this.themeMode=un(t?.localStorage),this.skin=bn(t?.localStorage),this.mediaQuery=t?.matchMedia?.("(prefers-color-scheme: dark)")??null,this.mediaQuery?.addEventListener("change",()=>{this.applyTheme(),this.emit()}),this.applyTheme(),this.applySkin(),this.applyLocale()}t(t,n){return this.translate(t,n)}setLocale(t){this.locale!==t&&(this.locale=t,this.translate=nt(t),window.localStorage.setItem($t,t),this.applyLocale(),this.emit())}get theme(){return this.skin==="default"?this.themeMode:this.skin}setTheme(t){let n=t==="system"||t==="light"||t==="dark",s=n?"default":t,a=s!==this.skin;n&&this.themeMode!==t&&(this.themeMode=t,window.localStorage.setItem(St,this.themeMode)),a&&(this.skin=s,window.localStorage.setItem(It,this.skin)),this.applyTheme(),this.applySkin(a),this.emit()}on(t){return this.listeners.add(t),()=>this.listeners.delete(t)}emit(){for(let t of this.listeners)t()}applyTheme(){this.resolvedTheme=cn(this.themeMode,!!this.mediaQuery?.matches);let t=this.skin==="default"?this.resolvedTheme:Ko[this.skin];document.documentElement.dataset.theme=t,document.documentElement.dataset.themeMode=this.themeMode}applySkin(t=!1){document.documentElement.dataset.skin=this.skin,kn(this.skin==="cyber",t),t&&this.skin!=="cyber"&&this.skin!=="default"&&$n(this.skin)}applyLocale(){document.documentElement.lang=this.locale==="zh"?"zh-CN":"en"}},Ko={default:"light",cyber:"dark",genshin:"light",fallout:"dark",prts:"dark",bluearchive:"dark",zzz:"dark",dragonball:"light",ikun:"dark"};function Vo(){return typeof navigator>"u"?[]:navigator.languages?.length?navigator.languages:[navigator.language].filter(Boolean)}var me=new At;function o(e,t){return me.t(e,t)}function r(e){return e.replace(/[&<>"']/g,t=>({"&":"&","<":"<",">":">",'"':""","'":"'"})[t])}function ue(e){if(!e)return"-";let t=Date.now()-e;return t<6e4?o("common.now"):t<36e5?Math.floor(t/6e4)+"m":t<864e5?Math.floor(t/36e5)+"h":Math.floor(t/864e5)+"d"}var Sn=[{c1:"#5be3ff",c2:"#4f8bff"},{c1:"#b89bff",c2:"#6b4df0"},{c1:"#7ce0c3",c2:"#2e9e8f"},{c1:"#8fb4ff",c2:"#3b62d8"},{c1:"#ffd28f",c2:"#d8783b"},{c1:"#7df0a8",c2:"#1f9e63"},{c1:"#9fd0ff",c2:"#4878c8"},{c1:"#ff9fb8",c2:"#d84a78"}];function de(e){let t=0,n=String(e??"");for(let i=0;i<n.length;i++)t=t*31+n.charCodeAt(i)>>>0;let{c1:s,c2:a}=Sn[t%Sn.length];return`--c1:${s};--c2:${a}`}var Tn=new Map,In=new Map,Ct=null;function Pe(){return Ct??=(async()=>{try{let e=await fetch("/api/groups");if(!e.ok)throw new Error(`HTTP ${e.status}`);let t=await e.json();for(let n of t.bots??[])n.larkAppId&&n.botName&&n.botName!==n.larkAppId&&Tn.set(n.larkAppId,String(n.botName));for(let n of t.chats??[])n.chatId&&n.name&&In.set(n.chatId,String(n.name))}catch{Ct=null}})(),Ct}function fe(e){let t=e.larkAppId?Tn.get(e.larkAppId):void 0;return t||(e.botName&&e.botName!==e.larkAppId?String(e.botName):String(e.botName??e.larkAppId??"-"))}function Ne(e){return e.chatId&&In.get(e.chatId)||null}function $e(e){let t=String(e??"");return t.replace(/^(?:@\S+\s*)+/,"").trim()||t}function Te(e){return e.status==="closed"?null:e.pendingRepo?o("sessions.board.signalRepo"):e.tuiPromptActive?o("sessions.board.signalPrompt"):e.status==="limited"?o("sessions.board.signalLimited"):null}var Ln={chats:[],bots:[]};async function Yo(){try{let e=await fetch("/api/groups");if(!e.ok)return;Ln=await e.json()}catch{}}var Dt=new Set(["working","analyzing","active","starting"]);function Qo(e){let t=new Map,n=a=>{let i=t.get(a);return i||(i={botName:a,larkAppId:a,cliId:"unknown",online:!1,sessions:[],active:[],busy:[],attention:[],lastActiveAt:0},t.set(a,i)),i};for(let a of Ln.bots??[]){let i=n(a.larkAppId??a.botName??"-");i.online=!0,a.botName&&(i.botName=a.botName)}let s=[...e].sort((a,i)=>+(a.status==="closed")-+(i.status==="closed"));for(let a of s){let i=a.larkAppId??a.botName??"-";if(a.status==="closed"&&!t.has(i))continue;let l=n(i);a.botName&&(l.botName===l.larkAppId||!l.botName)&&(l.botName=a.botName),l.sessions.push(a),a.cliId&&l.cliId==="unknown"&&(l.cliId=a.cliId),l.lastActiveAt=Math.max(l.lastActiveAt,Number(a.lastMessageAt??0)),a.status!=="closed"&&(l.active.push(a),Dt.has(a.status)&&l.busy.push(a),Te(a)&&l.attention.push(a))}return[...t.values()].sort((a,i)=>{let l=p=>p.attention.length?0:p.busy.length?1:p.online||p.active.length?2:3;return l(a)!==l(i)?l(a)-l(i):i.lastActiveAt-a.lastActiveAt})}function Xo(e){let t=!e.online&&e.active.length===0,n=e.attention.length>0,s=e.busy.length>0,a=n?"warn":s?"busy":t?"off":"ok",i;if(n){let p=e.attention[0];i=`<b>${r(($e(p.title)||p.sessionId).slice(0,60))}</b> \xB7 ${r(Te(p)??"")}`}else if(s){let p=[...e.busy].sort((v,y)=>Number(y.lastMessageAt??0)-Number(v.lastMessageAt??0))[0];i=`<b>${r(($e(p.title)||p.sessionId).slice(0,60))}</b>`}else t?i=r(o("overview.botOffline")):i=r(o("overview.botIdle"));let l=n?`<span class="tag tag-warn">${r(o("overview.botNeedsYou"))}</span>`:s?`<span class="tag tag-run">${r(o("overview.botBusy",{count:e.busy.length}))}</span>`:t?`<span class="tag tag-off">${r(o("overview.botOff"))}</span>`:`<span class="tag tag-ok">${r(o("overview.botReady"))}</span>`;return`<article class="mate${n?" mate-attn":""}${t?" mate-off":""}">
|
|
13
|
+
}`,"catalog.chatId":"Chat ID","catalog.larkAppId":"Lark app ID","catalog.chatBindingHint":"Required so humanGate cards and cancel routing know which Lark chat owns the run.","catalog.run":"Run","catalog.running":"Starting...","catalog.badParamsJson":"Params must be a JSON object.","catalog.writeAccess":"write access required: run `botmux dashboard` in the terminal to get a one-time URL, open it once to set the cookie, then come back and run again.","catalog.runHttp":"run HTTP {status}","catalog.runStarted":"Run started; opening detail page...","catalog.invalidParams":"Invalid params","catalog.issue":"{path}: {message}","catalog.noParams":"No params declared.","catalog.required":"required","catalog.optional":"optional","catalog.default":"default","catalog.description":"description","catalog.path":"path","catalog.revision":"revision","catalog.nodeCount":"nodes"},gn={zh:Ba,en:Na};function bn(e){if(typeof e!="string")return null;let t=e.trim().toLowerCase();return t==="zh"||t.startsWith("zh-")?"zh":t==="en"||t.startsWith("en-")?"en":null}function Pa(e=[]){for(let t of e){let n=bn(t);if(n)return n}return"zh"}function ut(e){return(t,n)=>{let s=gn[e][t]??gn.zh[t]??t;return n?s.replace(/\{(\w+)\}/g,(o,i)=>{let l=n[i];return l==null?`{${i}}`:String(l)}):s}}function hn(e,t){return(e?bn(e.getItem(Dt)):null)??Pa(t)}var Rt="botmux.dashboard.theme",wn="botmux.dashboard.sessions.view";function Ua(e){return e==="system"||e==="light"||e==="dark"?e:null}function ja(e){return e==="board"||e==="table"?e:null}function yn(e,t){return e==="system"?t?"dark":"light":e}function vn(e){return Ua(e?.getItem(Rt))??"dark"}function kn(e){return ja(e?.getItem(wn))??"board"}var $n="botmux.dashboard.sessions.boardOrder",Xe=["needs-you","starting","working","idle"];function za(e){if(!Array.isArray(e)||e.length!==Xe.length)return null;let t=new Set(e);if(t.size!==e.length)return null;for(let n of Xe)if(!t.has(n))return null;return e.slice()}function Sn(e){try{let t=e?.getItem($n);return t?za(JSON.parse(t))??[...Xe]:[...Xe]}catch{return[...Xe]}}function Ot(e,t){try{e?.setItem($n,JSON.stringify(t))}catch{}}function In(e,t){try{e?.setItem(wn,t)}catch{}}var _a=["default","cyber","genshin","fallout","prts","bluearchive","zzz","dragonball","ikun"],qt="botmux.dashboard.skin";function Fa(e){return typeof e=="string"&&_a.includes(e)?e:null}function Tn(e){return Fa(e?.getItem(qt))??"default"}var Ga="\uFF71\uFF72\uFF73\uFF74\uFF75\uFF76\uFF77\uFF78\uFF79\uFF7A\uFF7B\uFF7C\uFF7D\uFF7E\uFF7F\uFF80\uFF81\uFF82\uFF830123456789\uFF8A\uFF8B\uFF8C\uFF8D\uFF8E$#%&@*+=<>/\\",Ln=["186 100% 60%","56 97% 60%","330 100% 64%","150 80% 58%"],Nt=[{key:"shake",dur:280},{key:"rgb",dur:340},{key:"tear",dur:400},{key:"invert",dur:220},{key:"slice",dur:320},{key:"blackout",dur:260}],Mn=["ESTABLISHING NETLINK","BYPASSING ICE","DECRYPTING DATAFLOW","SYNCING BOTMUX TELEMETRY","ACCESS GRANTED"],En="0123456789ABCDEF#<>*/=+\uFF71\uFF72\uFF73\uFF74\uFF75\uFF76\uFF77\uFF78\uFF85\uFF86\uFF87\uFF88\uFF89\uFF8A\uFF8B\uFF8C",Wa=3200,Ze=[],Ge=0,pt=0;function jt(){return typeof window<"u"&&!!window.matchMedia?.("(prefers-reduced-motion: reduce)").matches}function Pt(e,t){let n="";for(let s=0;s<e;s++)n+=t[Math.floor(Math.random()*t.length)];return n}function Ja(e){let t=document.createElement("div");t.className="cyber-rain";let n=Math.max(10,Math.min(26,Math.round(window.innerWidth/70)));for(let s=0;s<n;s++){let o=document.createElement("span");o.className="cyber-rain-col",o.textContent=Pt(28+Math.floor(Math.random()*22),Ga);let i=Ln[Math.floor(Math.random()*Ln.length)];o.style.cssText=`left:${(s+.5)/n*100}%;--rc:${i};--sz:${10+Math.floor(Math.random()*9)}px;--op:${(.25+Math.random()*.45).toFixed(2)};--dur:${(6+Math.random()*9).toFixed(1)}s;--delay:${(-Math.random()*12).toFixed(1)}s;`,t.appendChild(o)}e.appendChild(t)}function Ka(){if(jt())return;let e=document.body,t=()=>{let n=Nt[Math.floor(Math.random()*Nt.length)],s=`cp-fx-${n.key}`;e.classList.add(s),Ze.push(window.setTimeout(()=>e.classList.remove(s),n.dur)),Ze.push(window.setTimeout(t,2400+Math.random()*4200))};Ze.push(window.setTimeout(t,1800+Math.random()*2600))}function Va(){Ze.forEach(e=>window.clearTimeout(e)),Ze=[];for(let e of Nt)document.body.classList.remove(`cp-fx-${e.key}`)}function Ya(){if(jt()||document.getElementById("cyber-boot"))return;let e=document.createElement("div");e.id="cyber-boot",e.className="cyber-boot",e.setAttribute("aria-hidden","true"),e.innerHTML='<div class="cyber-boot-grid"></div><div class="cyber-loader"><div class="cyber-loader-frame"><div class="cyber-loader-head"><span>KIROSHI NETLINK</span><span class="cyber-loader-jp">\u4FB5\u5165\u4E2D</span></div><div class="cyber-loader-line"><span class="cyber-loader-prompt">></span><span class="cyber-loader-text"></span><span class="cyber-loader-cursor">_</span></div><div class="cyber-loader-stream"></div></div></div>',document.body.appendChild(e);let t=e.querySelector(".cyber-loader-text"),n=e.querySelector(".cyber-loader-stream"),s=0,o=0,i=0;Ge=window.setInterval(()=>{let l=Mn[s];t&&(o<l.length?(o+=1,t.textContent=l.slice(0,o)+Pt(l.length-o,En),t.classList.remove("done")):i<16?(i+=1,t.textContent=l,t.classList.add("done")):(s=(s+1)%Mn.length,o=0,i=0)),n&&(n.textContent=Pt(26,En))},50),pt=window.setTimeout(()=>{window.clearInterval(Ge),Ge=0,e.remove()},Wa)}function Qa(){Ge&&(window.clearInterval(Ge),Ge=0),pt&&(window.clearTimeout(pt),pt=0),document.getElementById("cyber-boot")?.remove()}var Xa=320,xn=16,et=!0,Fe=0,Bt=0,mt=!1,ft=[],Ut=[];function Za(){if(mt)return;mt=!0,et=!1,Fe=0;let e=jt(),t="";for(let s=0;s<xn;s++){let o=s%3===0?"186 100% 52%":s%3===1?"330 100% 58%":"56 97% 52%",i=(s%2===0?1:-1)*(8+s%5*7);t+=`<span class="cyber-breach-shard" style="top:${s/xn*100}%;height:${2+s%4*3}%;--shift:${i}px;--delay:${s%8*.09}s;--dur:${(.36+s%5*.12).toFixed(2)}s;--hue:${o}"></span>`}let n=document.createElement("div");n.id="cyber-breach",n.className="cyber-breach",n.setAttribute("aria-hidden","true"),n.innerHTML=`<span class="cyber-breach-flash"></span><span class="cyber-breach-grid"></span><div class="cyber-breach-shards">${t}</div><div class="cyber-breach-banner"><span class="cyber-breach-tag">// BREACH PROTOCOL \u2014 SYSTEM OVERRIDE</span><span class="cyber-breach-caption" data-text="SYSTEM BREACH">SYSTEM BREACH</span><span class="cyber-breach-sub" data-text="NETWATCH OVERRIDE ENGAGED">NETWATCH OVERRIDE ENGAGED</span></div>`,document.body.appendChild(n),e||document.body.classList.add("cyber-breach-quake"),ft.push(window.setTimeout(()=>document.body.classList.remove("cyber-breach-quake"),760)),ft.push(window.setTimeout(()=>{n.remove(),mt=!1},e?2600:4200))}function eo(){et=!0,Fe=0;let e=document.documentElement,t=()=>e.scrollHeight-(window.innerHeight+window.scrollY)<=4,n=w=>{w<=0||!t()||(Fe+=w,Fe>Xa&&et&&Za())},s=w=>n(w.deltaY),o=w=>{Bt=w.touches[0]?.clientY??0},i=w=>{let h=w.touches[0]?.clientY??0,y=Bt-h;Bt=h,n(y)},l=()=>{t()||(Fe=0,et=!0)},c=(w,h)=>{window.addEventListener(w,h,{passive:!0}),Ut.push([w,h])};c("wheel",s),c("touchstart",o),c("touchmove",i),c("scroll",l)}function to(){for(let[e,t]of Ut)window.removeEventListener(e,t);Ut=[],ft.forEach(e=>window.clearTimeout(e)),ft=[],document.body.classList.remove("cyber-breach-quake"),document.getElementById("cyber-breach")?.remove(),mt=!1,et=!0,Fe=0}function Hn(e,t=!1){if(!(typeof document>"u")){if(!e){document.getElementById("cyber-fx")?.remove(),document.getElementById("cyber-hud")?.remove(),document.getElementById("cyber-glitch")?.remove(),Va(),Qa(),to();return}if(!document.getElementById("cyber-fx")){let n=document.createElement("div");n.id="cyber-fx",n.className="cyber-fx",n.setAttribute("aria-hidden","true"),n.innerHTML='<div class="cyber-fx-grid"></div><div class="cyber-fx-scan"></div><span class="cyber-flicker"></span><span class="cyber-rollline"></span>',Ja(n),document.body.appendChild(n);let s=document.createElement("div");s.id="cyber-hud",s.className="cyber-hud",s.setAttribute("aria-hidden","true"),s.innerHTML='<span class="cyber-hud-corner tl"></span><span class="cyber-hud-corner tr"></span><span class="cyber-hud-corner bl"></span><span class="cyber-hud-corner br"></span><span class="cyber-hud-tag">NIGHT CITY // NETWATCH</span>',document.body.appendChild(s);let o=document.createElement("div");o.id="cyber-glitch",o.className="cyber-glitch",o.setAttribute("aria-hidden","true"),document.body.appendChild(o),Ka(),eo()}t&&Ya()}}var no={genshin:2e3,zzz:1900,dragonball:1900,ikun:1900},ao='<span class="si-bball" aria-hidden="true"><span class="si-bball-seams"><svg viewBox="0 0 100 100"><g fill="none" stroke="#3a1d08" stroke-width="3.4" stroke-linecap="round"><path d="M50 4V96"/><path d="M4 50H96"/><path d="M50 4 Q14 50 50 96"/><path d="M50 4 Q86 50 50 96"/></g></svg></span></span>';function oo(e){switch(e){case"genshin":return'<span class="si-gi-shine" aria-hidden="true"></span><span class="si-gi-text">\u539F\u795E\uFF0C\u542F\u52A8</span>';case"zzz":return'<span class="si-static" aria-hidden="true"></span><span class="si-roll" aria-hidden="true"></span><span class="si-now">NOW LOADING</span>';case"dragonball":return'<span class="si-speed" aria-hidden="true"></span><img class="si-cloud" src="/assets/skins/dragonball-wukong.webp" alt="">';case"ikun":return ao;default:return""}}function so(){return typeof window<"u"&&!!window.matchMedia?.("(prefers-reduced-motion: reduce)").matches}function An(e){if(typeof document>"u"||so())return;let t=no[e];if(!t)return;document.getElementById("skin-intro")?.remove();let n=document.createElement("div");n.id="skin-intro",n.className=`skin-intro si-${e}`,n.setAttribute("aria-hidden","true"),n.style.setProperty("--si-dur",`${t}ms`),n.innerHTML=oo(e),document.body.appendChild(n),window.setTimeout(()=>n.remove(),t+80)}var _t=class{locale="zh";themeMode="system";resolvedTheme="light";skin="default";listeners=new Set;translate=ut(this.locale);mediaQuery=null;init(){let t=typeof window<"u"?window:void 0;this.locale=hn(t?.localStorage,ro()),this.translate=ut(this.locale),this.themeMode=vn(t?.localStorage),this.skin=Tn(t?.localStorage),this.mediaQuery=t?.matchMedia?.("(prefers-color-scheme: dark)")??null,this.mediaQuery?.addEventListener("change",()=>{this.applyTheme(),this.emit()}),this.applyTheme(),this.applySkin(),this.applyLocale()}t(t,n){return this.translate(t,n)}setLocale(t){this.locale!==t&&(this.locale=t,this.translate=ut(t),window.localStorage.setItem(Dt,t),this.applyLocale(),this.emit())}get theme(){return this.skin==="default"?this.themeMode:this.skin}setTheme(t){let n=t==="system"||t==="light"||t==="dark",s=n?"default":t,o=s!==this.skin;n&&this.themeMode!==t&&(this.themeMode=t,window.localStorage.setItem(Rt,this.themeMode)),o&&(this.skin=s,window.localStorage.setItem(qt,this.skin)),this.applyTheme(),this.applySkin(o),this.emit()}on(t){return this.listeners.add(t),()=>this.listeners.delete(t)}emit(){for(let t of this.listeners)t()}applyTheme(){this.resolvedTheme=yn(this.themeMode,!!this.mediaQuery?.matches);let t=this.skin==="default"?this.resolvedTheme:io[this.skin];document.documentElement.dataset.theme=t,document.documentElement.dataset.themeMode=this.themeMode}applySkin(t=!1){document.documentElement.dataset.skin=this.skin,Hn(this.skin==="cyber",t),t&&this.skin!=="cyber"&&this.skin!=="default"&&An(this.skin)}applyLocale(){document.documentElement.lang=this.locale==="zh"?"zh-CN":"en"}},io={default:"light",cyber:"dark",genshin:"light",fallout:"dark",prts:"dark",bluearchive:"dark",zzz:"dark",dragonball:"light",ikun:"dark"};function ro(){return typeof navigator>"u"?[]:navigator.languages?.length?navigator.languages:[navigator.language].filter(Boolean)}var ye=new _t;function a(e,t){return ye.t(e,t)}function r(e){return e.replace(/[&<>"']/g,t=>({"&":"&","<":"<",">":">",'"':""","'":"'"})[t])}function be(e){if(!e)return"-";let t=Date.now()-e;return t<6e4?a("common.now"):t<36e5?Math.floor(t/6e4)+"m":t<864e5?Math.floor(t/36e5)+"h":Math.floor(t/864e5)+"d"}var Cn=[{c1:"#5be3ff",c2:"#4f8bff"},{c1:"#b89bff",c2:"#6b4df0"},{c1:"#7ce0c3",c2:"#2e9e8f"},{c1:"#8fb4ff",c2:"#3b62d8"},{c1:"#ffd28f",c2:"#d8783b"},{c1:"#7df0a8",c2:"#1f9e63"},{c1:"#9fd0ff",c2:"#4878c8"},{c1:"#ff9fb8",c2:"#d84a78"}];function Dn(e){let t=0,n=String(e??"");for(let i=0;i<n.length;i++)t=t*31+n.charCodeAt(i)>>>0;let{c1:s,c2:o}=Cn[t%Cn.length];return`--c1:${s};--c2:${o}`}var gt=new Map,bt=new Map;function lo(e,t){return t?gt.get(t):e?bt.get(String(e)):void 0}function me(e){let t=e.name??"",n=e.avatarUrl??lo(e.name,e.larkAppId),s=e.size==="sm"?" orb-avatar-sm":"",o=n?" orb-has-img":"",i=e.dot?`<i class="orb-dot orb-dot-${e.dot}"></i>`:"",l=n?`<img class="orb-img" src="${r(n)}" alt="" decoding="async" referrerpolicy="no-referrer" onerror="this.closest('.orb-avatar')?.classList.remove('orb-has-img');this.remove()"/>`:"";return`<span class="orb-avatar${s}${o}" style="${Dn(t)}" aria-hidden="true">${l}${i}</span>`}function co(e){return e?ht.get(e):void 0}function Rn(e){let t=e.name??e.chatId??"",n=e.avatarUrl??co(e.chatId),s=e.size==="sm"?" orb-avatar-sm":"",o=n?" orb-has-img":"",i=n?`<img class="orb-img" src="${r(n)}" alt="" decoding="async" referrerpolicy="no-referrer" onerror="this.closest('.orb-avatar')?.classList.remove('orb-has-img');this.remove()"/>`:"";return`<span class="orb-avatar orb-square${s}${o}" style="${Dn(t)}" aria-hidden="true">${i}</span>`}var On=new Map,qn=new Map,ht=new Map,zt=null,Bn="botmux.avatarCache.v1";function uo(){try{let e=typeof window<"u"?window.localStorage.getItem(Bn):null;if(!e)return;let t=JSON.parse(e);for(let[n,s]of Object.entries(t.botByAppId??{}))gt.set(n,s);for(let[n,s]of Object.entries(t.botByName??{}))bt.set(n,s);for(let[n,s]of Object.entries(t.chatById??{}))ht.set(n,s)}catch{}}function po(){try{if(typeof window>"u")return;window.localStorage.setItem(Bn,JSON.stringify({botByAppId:Object.fromEntries(gt),botByName:Object.fromEntries(bt),chatById:Object.fromEntries(ht)}))}catch{}}uo();function ve(){return zt??=(async()=>{try{let e=await fetch("/api/groups");if(!e.ok)throw new Error(`HTTP ${e.status}`);let t=await e.json();for(let n of t.bots??[])n.larkAppId&&n.botName&&n.botName!==n.larkAppId&&On.set(n.larkAppId,String(n.botName)),n.botAvatarUrl&&(n.larkAppId&>.set(n.larkAppId,String(n.botAvatarUrl)),n.botName&&bt.set(String(n.botName),String(n.botAvatarUrl)));for(let n of t.chats??[])n.chatId&&n.name&&qn.set(n.chatId,String(n.name)),n.chatId&&n.avatar&&ht.set(n.chatId,String(n.avatar));po()}catch{zt=null}})(),zt}function ke(e){let t=e.larkAppId?On.get(e.larkAppId):void 0;return t||(e.botName&&e.botName!==e.larkAppId?String(e.botName):String(e.botName??e.larkAppId??"-"))}function We(e){return e.chatId&&qn.get(e.chatId)||null}function Ee(e){let t=String(e??"");return t.replace(/^(?:@\S+\s*)+/,"").trim()||t}function Ce(e){return e.status==="closed"?null:e.pendingRepo?a("sessions.board.signalRepo"):e.tuiPromptActive?a("sessions.board.signalPrompt"):e.status==="limited"?a("sessions.board.signalLimited"):null}var Nn={chats:[],bots:[]};async function mo(){try{let e=await fetch("/api/groups");if(!e.ok)return;Nn=await e.json()}catch{}}var Ft=new Set(["working","analyzing","active","starting"]);function fo(e){let t=new Map,n=o=>{let i=t.get(o);return i||(i={botName:o,larkAppId:o,cliId:"unknown",online:!1,sessions:[],active:[],busy:[],attention:[],lastActiveAt:0},t.set(o,i)),i};for(let o of Nn.bots??[]){let i=n(o.larkAppId??o.botName??"-");i.online=!0,o.botName&&(i.botName=o.botName),o.botAvatarUrl&&(i.botAvatarUrl=o.botAvatarUrl)}let s=[...e].sort((o,i)=>+(o.status==="closed")-+(i.status==="closed"));for(let o of s){let i=o.larkAppId??o.botName??"-";if(o.status==="closed"&&!t.has(i))continue;let l=n(i);o.botName&&(l.botName===l.larkAppId||!l.botName)&&(l.botName=o.botName),l.sessions.push(o),o.cliId&&l.cliId==="unknown"&&(l.cliId=o.cliId),l.lastActiveAt=Math.max(l.lastActiveAt,Number(o.lastMessageAt??0)),o.status!=="closed"&&(l.active.push(o),Ft.has(o.status)&&l.busy.push(o),Ce(o)&&l.attention.push(o))}return[...t.values()].sort((o,i)=>{let l=c=>c.attention.length?0:c.busy.length?1:c.online||c.active.length?2:3;return l(o)!==l(i)?l(o)-l(i):i.lastActiveAt-o.lastActiveAt})}function go(e){let t=!e.online&&e.active.length===0,n=e.attention.length>0,s=e.busy.length>0,o=n?"warn":s?"busy":t?"off":"ok",i;if(n){let c=e.attention[0];i=`<b>${r((Ee(c.title)||c.sessionId).slice(0,60))}</b> \xB7 ${r(Ce(c)??"")}`}else if(s){let c=[...e.busy].sort((w,h)=>Number(h.lastMessageAt??0)-Number(w.lastMessageAt??0))[0];i=`<b>${r((Ee(c.title)||c.sessionId).slice(0,60))}</b>`}else t?i=r(a("overview.botOffline")):i=r(a("overview.botIdle"));let l=n?`<span class="tag tag-warn">${r(a("overview.botNeedsYou"))}</span>`:s?`<span class="tag tag-run">${r(a("overview.botBusy",{count:e.busy.length}))}</span>`:t?`<span class="tag tag-off">${r(a("overview.botOff"))}</span>`:`<span class="tag tag-ok">${r(a("overview.botReady"))}</span>`;return`<article class="mate${n?" mate-attn":""}${t?" mate-off":""}">
|
|
14
14
|
<div class="mate-top">
|
|
15
|
-
|
|
15
|
+
${me({name:e.botName,larkAppId:e.larkAppId,avatarUrl:e.botAvatarUrl,dot:o})}
|
|
16
16
|
<div class="mate-id">
|
|
17
17
|
<b>${r(e.botName)}</b>
|
|
18
18
|
<span class="mate-role">${r(e.cliId)}</span>
|
|
@@ -21,50 +21,50 @@ effects={effects}, activities={activities}, waits={waits}, cancels={cancels}`,"w
|
|
|
21
21
|
<div class="mate-task">${i}</div>
|
|
22
22
|
<div class="mate-foot">
|
|
23
23
|
${l}
|
|
24
|
-
<span>${e.lastActiveAt?r(
|
|
24
|
+
<span>${e.lastActiveAt?r(a("overview.lastActive",{time:be(e.lastActiveAt)})):r(a("common.never"))}</span>
|
|
25
25
|
</div>
|
|
26
|
-
</article>`}function
|
|
27
|
-
|
|
26
|
+
</article>`}function bo(e){let t=ke(e);return`<article class="qcard" data-id="${r(e.sessionId)}">
|
|
27
|
+
${me({name:t,larkAppId:e.larkAppId,size:"sm"})}
|
|
28
28
|
<div class="qcard-tx">
|
|
29
|
-
<b>${r(t)} \xB7 ${r((
|
|
30
|
-
<span>${r(
|
|
29
|
+
<b>${r(t)} \xB7 ${r((Ee(e.title)||e.sessionId).slice(0,56))}</b>
|
|
30
|
+
<span>${r(Ce(e)??"")} \xB7 ${be(e.lastMessageAt)}</span>
|
|
31
31
|
</div>
|
|
32
|
-
<a class="qcard-go" href="#/sessions">${r(
|
|
33
|
-
</article>`}function
|
|
34
|
-
|
|
32
|
+
<a class="qcard-go" href="#/sessions">${r(a("strip.handle"))}</a>
|
|
33
|
+
</article>`}function ho(e){let t=ke(e);return`<li class="sess-row">
|
|
34
|
+
${me({name:t,larkAppId:e.larkAppId,size:"sm"})}
|
|
35
35
|
<div class="sess-tx">
|
|
36
|
-
<b>${r((
|
|
37
|
-
<span>${r(t)} \xB7 ${r(
|
|
36
|
+
<b>${r((Ee(e.title)||e.sessionId).slice(0,64))}</b>
|
|
37
|
+
<span>${r(t)} \xB7 ${r(We(e)??e.cliId??"unknown")} \xB7 ${be(e.lastMessageAt)}</span>
|
|
38
38
|
</div>
|
|
39
39
|
<span class="status status-${r(e.status??"unknown")}">${r(e.status??"unknown")}</span>
|
|
40
|
-
</li>`}function
|
|
40
|
+
</li>`}function wo(e){let t=e.nextRunAt?new Date(e.nextRunAt).toLocaleString():"-";return`<li class="overview-list-row">
|
|
41
41
|
<div>
|
|
42
42
|
<strong>${r(e.name??e.id)}</strong>
|
|
43
|
-
<span>${r(
|
|
43
|
+
<span>${r(ke(e))} \xB7 ${r(e.parsed?.display??"")}</span>
|
|
44
44
|
</div>
|
|
45
45
|
<span>${r(t)}</span>
|
|
46
|
-
</li>`}function
|
|
47
|
-
<div class="donut-center"><b>0</b><span>${r(
|
|
48
|
-
<div class="donut" style="background:conic-gradient(var(--accent) 0 ${
|
|
49
|
-
<div class="donut-center"><b>${s}</b><span>${r(
|
|
50
|
-
</div>`}async function
|
|
46
|
+
</li>`}function yo(e,t,n){let s=e+t+n;if(s===0)return`<div class="donut-wrap"><div class="donut" style="background:conic-gradient(var(--border) 0 360deg)"></div>
|
|
47
|
+
<div class="donut-center"><b>0</b><span>${r(a("overview.openSessions"))}</span></div></div>`;let o=e/s*360,i=o+t/s*360;return`<div class="donut-wrap">
|
|
48
|
+
<div class="donut" style="background:conic-gradient(var(--accent) 0 ${o}deg, var(--warning) ${o}deg ${i}deg, var(--success) ${i}deg 360deg)"></div>
|
|
49
|
+
<div class="donut-center"><b>${s}</b><span>${r(a("overview.openSessions"))}</span></div>
|
|
50
|
+
</div>`}async function Pn(e){e.innerHTML=`<section class="page hero-page">
|
|
51
51
|
<div class="page-heading">
|
|
52
52
|
<div>
|
|
53
|
-
<p class="eyebrow">${
|
|
54
|
-
<h1>${
|
|
55
|
-
<p id="overview-sub">${
|
|
53
|
+
<p class="eyebrow">${a("app.subtitle")}</p>
|
|
54
|
+
<h1>${a("overview.title")}</h1>
|
|
55
|
+
<p id="overview-sub">${a("overview.subtitle")}</p>
|
|
56
56
|
</div>
|
|
57
57
|
<div class="hero-pills" id="overview-pills"></div>
|
|
58
58
|
</div>
|
|
59
59
|
|
|
60
60
|
<div class="sect-head">
|
|
61
|
-
<h2>${
|
|
62
|
-
<a href="#/bot-defaults">${
|
|
61
|
+
<h2>${a("overview.team")}</h2><span>${a("overview.teamHint")}</span>
|
|
62
|
+
<a href="#/bot-defaults">${a("overview.viewAll")}</a>
|
|
63
63
|
</div>
|
|
64
64
|
<div class="team-grid" id="team-grid"></div>
|
|
65
65
|
|
|
66
66
|
<div class="sect-head" id="attention-head">
|
|
67
|
-
<h2>${
|
|
67
|
+
<h2>${a("overview.attention")}</h2><span>${a("overview.attentionHint")}</span>
|
|
68
68
|
</div>
|
|
69
69
|
<div class="qgrid" id="attention-list"></div>
|
|
70
70
|
|
|
@@ -72,10 +72,10 @@ effects={effects}, activities={activities}, waits={waits}, cancels={cancels}`,"w
|
|
|
72
72
|
<section class="panel">
|
|
73
73
|
<header class="panel-header">
|
|
74
74
|
<div>
|
|
75
|
-
<h2>${
|
|
76
|
-
<p>${
|
|
75
|
+
<h2>${a("overview.activeSessions")}</h2>
|
|
76
|
+
<p>${a("overview.activeSessionsHint")}</p>
|
|
77
77
|
</div>
|
|
78
|
-
<a class="btn-link" href="#/sessions">${
|
|
78
|
+
<a class="btn-link" href="#/sessions">${a("overview.viewAll")}</a>
|
|
79
79
|
</header>
|
|
80
80
|
<ul class="overview-list" id="recent-sessions"></ul>
|
|
81
81
|
</section>
|
|
@@ -83,8 +83,8 @@ effects={effects}, activities={activities}, waits={waits}, cancels={cancels}`,"w
|
|
|
83
83
|
<section class="panel">
|
|
84
84
|
<header class="panel-header">
|
|
85
85
|
<div>
|
|
86
|
-
<h2>${
|
|
87
|
-
<p>${
|
|
86
|
+
<h2>${a("overview.today")}</h2>
|
|
87
|
+
<p>${a("overview.todayHint")}</p>
|
|
88
88
|
</div>
|
|
89
89
|
</header>
|
|
90
90
|
<div class="donut-row" id="today-donut"></div>
|
|
@@ -92,296 +92,301 @@ effects={effects}, activities={activities}, waits={waits}, cancels={cancels}`,"w
|
|
|
92
92
|
<section class="panel">
|
|
93
93
|
<header class="panel-header">
|
|
94
94
|
<div>
|
|
95
|
-
<h2>${
|
|
96
|
-
<p>${
|
|
95
|
+
<h2>${a("overview.nextSchedules")}</h2>
|
|
96
|
+
<p>${a("schedules.subtitle")}</p>
|
|
97
97
|
</div>
|
|
98
|
-
<a class="btn-link" href="#/schedules">${
|
|
98
|
+
<a class="btn-link" href="#/schedules">${a("overview.viewAll")}</a>
|
|
99
99
|
</header>
|
|
100
100
|
<ul class="overview-list" id="next-schedules"></ul>
|
|
101
101
|
</section>
|
|
102
102
|
</div>
|
|
103
103
|
</div>
|
|
104
|
-
</section>`;let t=e.querySelector("#overview-pills"),n=e.querySelector("#team-grid"),s=e.querySelector("#attention-list"),
|
|
105
|
-
<span class="pill">${r(
|
|
106
|
-
<span class="pill${
|
|
107
|
-
<span class="pill">${r(
|
|
104
|
+
</section>`;let t=e.querySelector("#overview-pills"),n=e.querySelector("#team-grid"),s=e.querySelector("#attention-list"),o=e.querySelector("#recent-sessions"),i=e.querySelector("#today-donut"),l=e.querySelector("#next-schedules");function c(){let w=[...K.sessions.values()],h=w.filter(d=>d.status!=="closed"),y=h.filter(d=>Ce(d)).sort((d,g)=>Number(d.lastMessageAt??0)-Number(g.lastMessageAt??0)),v=h.filter(d=>Ft.has(d.status)&&!Ce(d)),p=h.length-y.length-v.length,T=fo(w),q=T.filter(d=>d.online||d.active.length>0).length;t.innerHTML=`
|
|
105
|
+
<span class="pill">${r(a("overview.workingSessions"))} <b>${v.length}</b></span>
|
|
106
|
+
<span class="pill${y.length?" pill-hot":""}">${r(a("overview.attention"))} <b>${y.length}</b></span>
|
|
107
|
+
<span class="pill">${r(a("overview.onlineBots"))} <b>${q}</b></span>`,n.innerHTML=T.length?T.map(go).join(""):`<div class="empty">${a("overview.noSessions")}</div>`,s.innerHTML=y.length?y.map(bo).join(""):`<div class="qcard qcard-empty">${a("overview.noAttention")}</div>`;let $=h.filter(d=>Ft.has(d.status)||d.status==="idle").sort((d,g)=>Number(g.lastMessageAt??0)-Number(d.lastMessageAt??0)).slice(0,7);o.innerHTML=$.length?$.map(ho).join(""):`<li class="empty">${a("overview.noSessions")}</li>`,i.innerHTML=`${yo(v.length,y.length,Math.max(0,p))}
|
|
108
108
|
<div class="donut-legend">
|
|
109
|
-
<span><i style="background:var(--accent)"></i>${r(
|
|
110
|
-
<span><i style="background:var(--warning)"></i>${r(
|
|
111
|
-
<span><i style="background:var(--success)"></i>${r(
|
|
112
|
-
</div>`;let
|
|
113
|
-
<summary>${
|
|
114
|
-
<div class="filter-cli-pop" role="group" aria-label="${
|
|
115
|
-
${
|
|
109
|
+
<span><i style="background:var(--accent)"></i>${r(a("overview.workingSessions"))} ${v.length}</span>
|
|
110
|
+
<span><i style="background:var(--warning)"></i>${r(a("overview.attention"))} ${y.length}</span>
|
|
111
|
+
<span><i style="background:var(--success)"></i>${r(a("sessions.board.idle"))} ${Math.max(0,p)}</span>
|
|
112
|
+
</div>`;let f=[...K.schedules.values()].filter(d=>d.nextRunAt).sort((d,g)=>Date.parse(d.nextRunAt)-Date.parse(g.nextRunAt)).slice(0,5);l.innerHTML=f.length?f.map(wo).join(""):`<li class="empty">${a("overview.noSchedules")}</li>`}K.on(c),c(),mo().then(c),ve().then(c)}function De(e,t){return`<th data-sort="${e}" data-label="${r(t)}">${r(t)}</th>`}var zn=["claude-code","seed","codex","codex-app","cursor","gemini","opencode","mtr","hermes","mira","pi","copilot","aiden","coco","unknown"],Un=[{id:"needs-you",labelKey:"sessions.board.needsYou",hintKey:"sessions.board.needsYouHint"},{id:"starting",labelKey:"sessions.board.starting",hintKey:"sessions.board.startingHint"},{id:"working",labelKey:"sessions.board.working",hintKey:"sessions.board.workingHint"},{id:"idle",labelKey:"sessions.board.idle",hintKey:"sessions.board.idleHint"}];function vo(e){return String(e??"unknown").toLowerCase().replace(/[^a-z0-9_-]/g,"-")}function ko(e){let t=String(e??"").trim();return t?t.replace(/\\/g,"/").split("/").filter(Boolean).at(-1)??t:"-"}function jn(e){if(!e.webPort)return null;let t=e.proxyPort??e.webPort,n=e.proxyPort?`/s/${encodeURIComponent(e.sessionId)}`:"";return`http://${location.hostname}:${t}${n}`}function $o(e){return e.status==="closed"?null:e.pendingRepo||e.tuiPromptActive||e.status==="limited"?"needs-you":e.status==="starting"?"starting":e.status==="working"||e.status==="analyzing"||e.status==="active"?"working":"idle"}function So(){return`<details class="filter-cli">
|
|
113
|
+
<summary>${a("sessions.cli")} \xB7 <b id="cli-filter-count">${a("common.all")}</b></summary>
|
|
114
|
+
<div class="filter-cli-pop" role="group" aria-label="${a("sessions.cli")}">
|
|
115
|
+
${zn.map(e=>`
|
|
116
116
|
<label class="filter-check">
|
|
117
117
|
<input type="checkbox" name="cli" value="${r(e)}" checked>
|
|
118
118
|
<span>${r(e)}</span>
|
|
119
119
|
</label>
|
|
120
120
|
`).join("")}
|
|
121
121
|
</div>
|
|
122
|
-
</details>`}function
|
|
122
|
+
</details>`}function Io(){return`<section class="page">
|
|
123
123
|
<div class="page-heading">
|
|
124
124
|
<div>
|
|
125
|
-
<p class="eyebrow">${
|
|
126
|
-
<h1>${
|
|
127
|
-
<p>${
|
|
125
|
+
<p class="eyebrow">${a("nav.sessions")}</p>
|
|
126
|
+
<h1>${a("sessions.title")}</h1>
|
|
127
|
+
<p>${a("sessions.subtitle")}</p>
|
|
128
128
|
</div>
|
|
129
|
-
<div class="segmented sessions-view-toggle" role="group" aria-label="${
|
|
130
|
-
<button type="button" data-view="board">${
|
|
131
|
-
<button type="button" data-view="table">${
|
|
129
|
+
<div class="segmented sessions-view-toggle" role="group" aria-label="${a("sessions.viewMode")}">
|
|
130
|
+
<button type="button" data-view="board">${a("sessions.viewBoard")}</button>
|
|
131
|
+
<button type="button" data-view="table">${a("sessions.viewTable")}</button>
|
|
132
132
|
</div>
|
|
133
133
|
</div>
|
|
134
134
|
<form id="filters" class="filters sessions-filters">
|
|
135
|
-
<input type="search" name="q" placeholder="${
|
|
135
|
+
<input type="search" name="q" placeholder="${a("sessions.search")}" />
|
|
136
136
|
<select name="status">
|
|
137
|
-
<option value="">${
|
|
137
|
+
<option value="">${a("sessions.anyStatus")}</option>
|
|
138
138
|
<option>starting</option><option>working</option><option>idle</option>
|
|
139
139
|
<option>analyzing</option><option>active</option><option>closed</option>
|
|
140
140
|
</select>
|
|
141
141
|
<select name="adopt">
|
|
142
|
-
<option value="">${
|
|
143
|
-
<option value="yes">${
|
|
144
|
-
<option value="no">${
|
|
142
|
+
<option value="">${a("sessions.adoptAny")}</option>
|
|
143
|
+
<option value="yes">${a("sessions.adoptYes")}</option>
|
|
144
|
+
<option value="no">${a("sessions.adoptNo")}</option>
|
|
145
145
|
</select>
|
|
146
|
-
${
|
|
147
|
-
<label class="filter-toggle"><input type="checkbox" name="active" checked> <span>${
|
|
146
|
+
${So()}
|
|
147
|
+
<label class="filter-toggle"><input type="checkbox" name="active" checked> <span>${a("sessions.activeOnly")}</span></label>
|
|
148
148
|
</form>
|
|
149
149
|
<div id="bulk-bar" class="bulk-bar" hidden>
|
|
150
150
|
<span id="bulk-count"></span>
|
|
151
|
-
<button type="button" id="bulk-close" class="contrast">${
|
|
152
|
-
<button type="button" id="bulk-clear">${
|
|
151
|
+
<button type="button" id="bulk-close" class="contrast">${a("sessions.closeSelected")}</button>
|
|
152
|
+
<button type="button" id="bulk-clear">${a("sessions.clearSelection")}</button>
|
|
153
153
|
</div>
|
|
154
154
|
<table id="sessions-table">
|
|
155
155
|
<thead><tr>
|
|
156
|
-
<th><input type="checkbox" id="select-all" title="${
|
|
157
|
-
${
|
|
158
|
-
${
|
|
159
|
-
${
|
|
160
|
-
${
|
|
161
|
-
${
|
|
162
|
-
${
|
|
163
|
-
${
|
|
164
|
-
${
|
|
165
|
-
<th>${
|
|
156
|
+
<th><input type="checkbox" id="select-all" title="${a("sessions.activeOnly")}"></th>
|
|
157
|
+
${De("botName",a("sessions.bot"))}
|
|
158
|
+
${De("cliId",a("sessions.cli"))}
|
|
159
|
+
${De("status",a("sessions.status"))}
|
|
160
|
+
${De("title",a("sessions.titleCol"))}
|
|
161
|
+
${De("workingDir",a("sessions.workingDir"))}
|
|
162
|
+
${De("spawnedAt",a("sessions.created"))}
|
|
163
|
+
${De("lastMessageAt",a("sessions.last"))}
|
|
164
|
+
${De("adopt",a("sessions.adopt"))}
|
|
165
|
+
<th>${a("sessions.actions")}</th>
|
|
166
166
|
</tr></thead>
|
|
167
167
|
<tbody></tbody>
|
|
168
168
|
</table>
|
|
169
169
|
<div id="sessions-board" class="sessions-board" hidden></div>
|
|
170
170
|
<dialog id="drawer"></dialog>
|
|
171
|
-
</section>`}function
|
|
172
|
-
<td><input type="checkbox" class="row-select" ${
|
|
173
|
-
<td>${r(
|
|
174
|
-
<td><span class="badge cli-${
|
|
175
|
-
<td><span class="status status-${r(
|
|
176
|
-
<td title="${r(String(
|
|
177
|
-
<td title="${r(
|
|
178
|
-
<td>${
|
|
179
|
-
<td>${
|
|
180
|
-
<td>${
|
|
181
|
-
<td><button class="open" type="button">${
|
|
182
|
-
</tr>`}function
|
|
171
|
+
</section>`}function _n(e){e.innerHTML=Io();let t=e.querySelector("#sessions-table tbody"),n=e.querySelector("#filters"),s=e.querySelector("#drawer"),o=e.querySelector("#select-all"),i=e.querySelector("#bulk-bar"),l=e.querySelector("#bulk-count"),c=e.querySelector("#bulk-close"),w=e.querySelector("#bulk-clear"),h=e.querySelector("#sessions-table"),y=e.querySelector("#sessions-board"),v=e.querySelectorAll("[data-view]"),p=new Set,T="lastMessageAt",q="desc",$=kn(window.localStorage),f=Sn(window.localStorage),d=null,g="",k="",H=!1;function E(){return f.map(u=>Un.find(b=>b.id===u)).filter(u=>!!u)}function x(u,b){let I=f.indexOf(u),L=I+b;if(I<0||L<0||L>=f.length)return;let M=[...f];M.splice(I,1),M.splice(L,0,u),f=M,Ot(window.localStorage,f),te()}function C(u,b){if(u===b)return;let I=f.indexOf(u),L=f.indexOf(b);if(I<0||L<0)return;let M=[...f];M.splice(I,1),M.splice(L,0,u),f=M,Ot(window.localStorage,f),te()}function U(u){let b=u.status==="closed",I=p.has(u.sessionId)?"checked":"";return`<tr data-id="${r(u.sessionId)}">
|
|
172
|
+
<td><input type="checkbox" class="row-select" ${I} ${b?"disabled":""}></td>
|
|
173
|
+
<td>${r(ke(u))}</td>
|
|
174
|
+
<td><span class="badge cli-${vo(u.cliId)}">${r(u.cliId??"unknown")}</span></td>
|
|
175
|
+
<td><span class="status status-${r(u.status??"unknown")}">${r(u.status??"unknown")}</span></td>
|
|
176
|
+
<td title="${r(String(u.title??""))}">${r(Ee(u.title??"").slice(0,48))}</td>
|
|
177
|
+
<td title="${r(u.workingDir??"")}">${r((u.workingDir??"").slice(-34))}</td>
|
|
178
|
+
<td>${be(u.spawnedAt)}</td>
|
|
179
|
+
<td>${be(u.lastMessageAt)}</td>
|
|
180
|
+
<td>${u.adopt?'<span class="badge">adopt</span>':""}</td>
|
|
181
|
+
<td><button class="open" type="button">${a("sessions.details")}</button></td>
|
|
182
|
+
</tr>`}function G(u){return u.scope!=="chat"||!u.feishuChatLink?null:`<a class="btn-link" href="${r(u.feishuChatLink)}" target="_blank" rel="noopener">${a("sessions.openChat")}</a>`}function Q(u){return u.pendingRepo?a("sessions.board.signalRepo"):u.tuiPromptActive?a("sessions.board.signalPrompt"):u.status==="limited"?a("sessions.board.signalLimited"):""}function le(u){let b=p.has(u.sessionId),I=Ee(u.title)||u.sessionId,L=ke(u),M=We(u),B=jn(u),P=Q(u);return`<article class="session-card${b?" selected":""}" data-id="${r(u.sessionId)}" aria-pressed="${b}">
|
|
183
183
|
<div class="session-card-top">
|
|
184
|
-
|
|
184
|
+
${me({name:L,larkAppId:u.larkAppId,size:"sm"})}
|
|
185
185
|
<div class="session-card-title">
|
|
186
|
-
<strong title="${r(String(
|
|
187
|
-
<span>${r(L)} \xB7 ${r(
|
|
186
|
+
<strong title="${r(String(u.title??I))}">${r(String(I).slice(0,72))}</strong>
|
|
187
|
+
<span>${r(L)} \xB7 ${r(M??u.cliId??"unknown")}</span>
|
|
188
188
|
</div>
|
|
189
|
-
<span class="status status-${r(
|
|
189
|
+
<span class="status status-${r(u.status??"unknown")}">${r(u.status??"unknown")}</span>
|
|
190
190
|
</div>
|
|
191
191
|
<div class="session-card-meta">
|
|
192
|
-
<span title="${r(
|
|
193
|
-
${
|
|
192
|
+
<span title="${r(u.workingDir??"")}">${r(ko(u.workingDir))}</span>
|
|
193
|
+
${u.adopt?'<span class="badge">adopt</span>':""}
|
|
194
194
|
</div>
|
|
195
195
|
<div class="session-card-time">
|
|
196
|
-
<span>${r(
|
|
197
|
-
${
|
|
196
|
+
<span>${r(a("sessions.last"))}: ${be(u.lastMessageAt)}</span>
|
|
197
|
+
${P?`<span class="session-signal">${r(P)} \xB7 ${be(u.lastMessageAt)}</span>`:""}
|
|
198
198
|
</div>
|
|
199
199
|
<div class="session-card-actions">
|
|
200
|
-
${
|
|
201
|
-
<button type="button" data-action="details">${
|
|
202
|
-
${
|
|
203
|
-
<button type="button" class="contrast" data-action="close">${
|
|
200
|
+
${G(u)??`<button type="button" data-action="locate">${a("sessions.locate")}</button>`}
|
|
201
|
+
<button type="button" data-action="details">${a("sessions.details")}</button>
|
|
202
|
+
${B?`<a class="btn-link primary" href="${r(B)}" target="_blank" rel="noopener">${a("sessions.openTerminal")}</a>`:""}
|
|
203
|
+
<button type="button" class="contrast" data-action="close">${a("sessions.close")}</button>
|
|
204
204
|
</div>
|
|
205
|
-
</article>`}function
|
|
206
|
-
<header draggable="true" title="${r(
|
|
205
|
+
</article>`}function ue(u,b,I){let L=Number(u.lastMessageAt??0),M=Number(b.lastMessageAt??0);return L!==M?I==="needs-you"?L-M:M-L:String(u.title??u.sessionId).localeCompare(String(b.title??b.sessionId))}function V(u){let b=new Map(Un.map(M=>[M.id,[]]));for(let M of u){let B=$o(M);B&&b.get(B).push(M)}let I=E(),L=I.map((M,B)=>{let P=(b.get(M.id)??[]).sort((Z,_)=>ue(Z,_,M.id));return`<section class="session-board-column session-board-${M.id}" data-col="${M.id}">
|
|
206
|
+
<header draggable="true" title="${r(a("sessions.board.dragHint"))}">
|
|
207
207
|
<div>
|
|
208
|
-
<h2>${r(
|
|
209
|
-
<p>${r(
|
|
208
|
+
<h2>${r(a(M.labelKey))}</h2>
|
|
209
|
+
<p>${r(a(M.hintKey))}</p>
|
|
210
210
|
</div>
|
|
211
211
|
<span class="session-board-head-right">
|
|
212
212
|
<span class="session-board-move">
|
|
213
|
-
<button type="button" data-move-col="${
|
|
214
|
-
aria-label="${r(
|
|
215
|
-
<button type="button" data-move-col="${
|
|
216
|
-
aria-label="${r(
|
|
213
|
+
<button type="button" data-move-col="${M.id}" data-dir="-1"
|
|
214
|
+
aria-label="${r(a("sessions.board.moveLeft"))}" ${B===0?"disabled":""}>\u2039</button>
|
|
215
|
+
<button type="button" data-move-col="${M.id}" data-dir="1"
|
|
216
|
+
aria-label="${r(a("sessions.board.moveRight"))}" ${B===I.length-1?"disabled":""}>\u203A</button>
|
|
217
217
|
</span>
|
|
218
|
-
<span class="session-board-count">${
|
|
218
|
+
<span class="session-board-count">${P.length}</span>
|
|
219
219
|
</span>
|
|
220
220
|
</header>
|
|
221
221
|
<div class="session-board-list">
|
|
222
|
-
${
|
|
222
|
+
${P.length?P.map(le).join(""):`<div class="session-board-empty">${a("sessions.board.emptyColumn")}</div>`}
|
|
223
223
|
</div>
|
|
224
|
-
</section>`}).join("");L!==
|
|
224
|
+
</section>`}).join("");L!==g&&(g=L,y.innerHTML=L,y.classList.toggle("board-enter",!H),H=!0)}function ne(){let u=new FormData(n),b=(u.get("q")??"").toLowerCase(),I=u.getAll("cli"),L=I.length>0&&I.length<zn.length,M=u.get("status"),B=u.get("adopt"),P=!!u.get("active"),Z=[...K.sessions.values()].filter(_=>!L||I.includes(_.cliId??"unknown")).filter(_=>!M||_.status===M).filter(_=>!B||B==="yes"==!!_.adopt).filter(_=>!P||_.status!=="closed").filter(_=>!b||JSON.stringify(_).toLowerCase().includes(b));return Z.sort(He),Z}function we(u,b){return b==="spawnedAt"||b==="lastMessageAt"?Number(u[b]??0):b==="adopt"?!!u.adopt:String(u[b]??"").toLowerCase()}function He(u,b){let I=we(u,T),L=we(b,T),M=0;return typeof I=="number"&&typeof L=="number"?M=I-L:typeof I=="boolean"&&typeof L=="boolean"?M=Number(I)-Number(L):M=String(I).localeCompare(String(L)),M===0&&(M=Number(u.lastMessageAt??0)-Number(b.lastMessageAt??0)),q==="asc"?M:-M}function Te(){h.querySelectorAll("th[data-sort]").forEach(u=>{let b=u.dataset.sort===T;u.classList.toggle("sorted",b),u.setAttribute("aria-sort",b?q==="asc"?"ascending":"descending":"none");let I=u.dataset.label??u.textContent?.trim()??"";u.textContent=b?`${I} ${q==="asc"?"\u25B2":"\u25BC"}`:I})}function ee(u){i.hidden=p.size===0,l.textContent=a("sessions.selectedCount",{count:p.size});let b=u.filter(L=>L.status!=="closed");if(b.length===0){o.checked=!1,o.indeterminate=!1,o.disabled=!0;return}o.disabled=!1;let I=b.filter(L=>p.has(L.sessionId)).length;o.checked=I===b.length,o.indeterminate=I>0&&I<b.length}function se(){v.forEach(u=>{let b=u.dataset.view===$;u.classList.toggle("active",b),u.setAttribute("aria-pressed",String(b))})}function pe(){let u=n.querySelector("#cli-filter-count");if(!u)return;let b=[...n.querySelectorAll('input[name="cli"]')],I=b.filter(L=>L.checked).length;u.textContent=I===b.length?a("common.all"):`${I}/${b.length}`,u.classList.toggle("cli-filter-active",I!==b.length)}function te(){let u=ne();for(let L of[...p]){let M=K.sessions.get(L);(!M||M.status==="closed")&&p.delete(L)}let b=u.filter(L=>L.status!=="closed"),I=$==="board"?b:u;if(h.hidden=$!=="table",y.hidden=$!=="board",$==="table"){let L=u.length?u.map(U).join(""):`<tr><td colspan="10" class="empty">${a("sessions.empty")}</td></tr>`;L!==k&&(k=L,t.innerHTML=L)}else V(b);se(),Te(),pe(),ee(I)}async function Le(u,b){b&&(b.disabled=!0,b.textContent=a("sessions.locating"));try{let I=await fetch(`/api/sessions/${encodeURIComponent(u.sessionId)}/locate`,{method:"POST"}),L=await I.json();if(L.ok){if(!b)return;let M=30;b.textContent=a("sessions.cooldown",{seconds:M});let B=setInterval(()=>{M-=1,M<=0?(clearInterval(B),b.disabled=!1,b.textContent=a("sessions.locate")):b.textContent=a("sessions.cooldown",{seconds:M})},1e3)}else alert(`Locate failed: ${L.error??I.status}`),b&&(b.disabled=!1,b.textContent=a("sessions.locate"))}catch(I){alert(`Locate error: ${I}`),b&&(b.disabled=!1,b.textContent=a("sessions.locate"))}}async function Me(u,b){if(!confirm(a("sessions.closeConfirm")))return!1;b&&(b.disabled=!0);try{let I=await fetch(`/api/sessions/${encodeURIComponent(u.sessionId)}/close`,{method:"POST"}),L=await I.json().catch(()=>({}));return!I.ok||L?.ok===!1?(I.status!==401&&alert(`Close failed: ${L?.error??I.status}`),!1):!0}catch(I){return alert(`Close error: ${I}`),!1}finally{b&&(b.disabled=!1)}}function X(u){let b=u.status==="closed",I=jn(u);s.innerHTML=`<article>
|
|
225
225
|
<header>
|
|
226
|
-
<h3>${r(
|
|
227
|
-
<span class="status status-${r(
|
|
228
|
-
<p><code>${r(
|
|
226
|
+
<h3>${r(Ee(u.title)||u.sessionId)}</h3>
|
|
227
|
+
<span class="status status-${r(u.status??"unknown")}">${r(u.status??"unknown")}</span>
|
|
228
|
+
<p><code>${r(u.sessionId)}</code> <button data-copy="${r(u.sessionId)}">${a("sessions.copy")}</button></p>
|
|
229
229
|
</header>
|
|
230
|
-
<p><b>${
|
|
231
|
-
${
|
|
232
|
-
<p><b>chatId:</b> <code>${r(
|
|
233
|
-
<p><b>rootMessageId:</b> <code>${r(
|
|
234
|
-
${
|
|
235
|
-
<p><b>${
|
|
230
|
+
<p><b>${a("sessions.bot")}:</b> ${r(ke(u))} \xB7 <b>${a("sessions.cli")}:</b> ${r(u.cliId??"?")}</p>
|
|
231
|
+
${We(u)?`<p><b>${a("sessions.chat")}:</b> ${r(We(u))}</p>`:""}
|
|
232
|
+
<p><b>chatId:</b> <code>${r(u.chatId??"")}</code> <button data-copy="${r(u.chatId??"")}">${a("sessions.copy")}</button></p>
|
|
233
|
+
<p><b>rootMessageId:</b> <code>${r(u.rootMessageId??"")}</code> <button data-copy="${r(u.rootMessageId??"")}">${a("sessions.copy")}</button></p>
|
|
234
|
+
${u.threadId?`<p><b>threadId:</b> <code>${r(u.threadId)}</code></p>`:""}
|
|
235
|
+
<p><b>${a("sessions.workingDir")}:</b> ${r(u.workingDir??"-")}</p>
|
|
236
236
|
<div class="actions">
|
|
237
|
-
${
|
|
238
|
-
${
|
|
239
|
-
${
|
|
240
|
-
${
|
|
237
|
+
${G(u)??`<button id="locate-btn" type="button">${a("sessions.locate")}</button>`}
|
|
238
|
+
${I?`<a class="btn-link primary" href="${r(I)}" target="_blank" rel="noopener">${a("sessions.openTerminal")}</a>`:""}
|
|
239
|
+
${b?`<button id="resume-btn" type="button" class="primary">${a("sessions.resume")}</button>`:""}
|
|
240
|
+
${b?"":`<button id="close-btn" type="button" class="contrast">${a("sessions.close")}</button>`}
|
|
241
241
|
</div>
|
|
242
|
-
<form method="dialog"><button>${
|
|
243
|
-
</article>`,s.querySelectorAll("[data-copy]").forEach(
|
|
242
|
+
<form method="dialog"><button>${a("sessions.dismiss")}</button></form>
|
|
243
|
+
</article>`,s.querySelectorAll("[data-copy]").forEach(P=>{P.onclick=()=>{navigator.clipboard.writeText(P.dataset.copy??""),P.textContent=a("sessions.copied"),setTimeout(()=>{P.textContent=a("sessions.copy")},800)}});let L=s.querySelector("#locate-btn");L&&(L.onclick=()=>{Le(u,L)});let M=s.querySelector("#resume-btn");M&&(M.onclick=async()=>{M.disabled=!0;try{let P=await fetch(`/api/sessions/${encodeURIComponent(u.sessionId)}/resume`,{method:"POST"}),Z=await P.json().catch(()=>({}));if(!P.ok||Z.ok===!1){alert(`${a("sessions.resumeFailed")}: ${Z?.error??P.status}`),M.disabled=!1;return}s.close()}catch(P){alert(`${a("sessions.resumeFailed")}: ${P}`),M.disabled=!1}});let B=s.querySelector("#close-btn");B&&(B.onclick=async()=>{await Me(u,B)&&s.close()}),s.showModal()}t.addEventListener("click",u=>{let b=u.target;if(b.classList.contains("row-select")){let B=b.closest("tr[data-id]");if(!B)return;b.checked?p.add(B.dataset.id):p.delete(B.dataset.id),ee(ne());return}let I=b.closest("td");if(I&&I.querySelector(".row-select"))return;let L=b.closest("tr[data-id]");if(!L)return;let M=K.sessions.get(L.dataset.id);M&&X(M)}),y.addEventListener("click",u=>{let b=u.target,I=b.closest("button[data-move-col]");if(I){x(I.dataset.moveCol,Number(I.dataset.dir));return}let L=b.closest(".session-card[data-id]");if(!L)return;let M=K.sessions.get(L.dataset.id);if(!M)return;let B=b.closest("button[data-action]");if(B){let P=B.dataset.action;P==="details"?X(M):P==="locate"?Le(M,B):P==="close"&&Me(M,B).then(Z=>{Z&&(p.delete(M.sessionId),te())});return}b.closest("a, button, input, label")||(p.has(M.sessionId)?p.delete(M.sessionId):p.add(M.sessionId),L.classList.toggle("selected",p.has(M.sessionId)),L.setAttribute("aria-pressed",String(p.has(M.sessionId))),ee(ne().filter(P=>P.status!=="closed")))}),y.addEventListener("dragstart",u=>{let I=u.target.closest(".session-board-column > header[draggable]")?.closest(".session-board-column");I?.dataset.col&&(d=I.dataset.col,I.classList.add("dragging"),u.dataTransfer&&(u.dataTransfer.effectAllowed="move",u.dataTransfer.setData("text/plain",d)))}),y.addEventListener("dragover",u=>{if(!d)return;u.preventDefault(),u.dataTransfer&&(u.dataTransfer.dropEffect="move");let b=u.target.closest(".session-board-column");y.querySelectorAll(".drag-over").forEach(I=>I.classList.remove("drag-over")),b&&b.dataset.col!==d&&b.classList.add("drag-over")}),y.addEventListener("drop",u=>{if(!d)return;u.preventDefault();let b=u.target.closest(".session-board-column"),I=d;d=null,b?.dataset.col&&C(I,b.dataset.col)}),y.addEventListener("dragend",()=>{d=null,y.querySelectorAll(".drag-over, .dragging").forEach(u=>u.classList.remove("drag-over","dragging"))}),v.forEach(u=>{u.addEventListener("click",()=>{let b=u.dataset.view==="table"?"table":"board";b!==$&&($=b,In(window.localStorage,$),te())})}),o.addEventListener("change",()=>{let u=ne().filter(b=>b.status!=="closed");for(let b of u)o.checked?p.add(b.sessionId):p.delete(b.sessionId);te()}),w.addEventListener("click",()=>{p.clear(),te()}),c.addEventListener("click",async()=>{let u=[...p];if(u.length===0||!confirm(a("sessions.closeBulkConfirm",{count:u.length})))return;c.disabled=!0,w.disabled=!0;let b=c.textContent,I=0,L=0,M=[...u];c.textContent=`0/${u.length}`;async function B(){for(;M.length;){let P=M.shift();try{let Z=await fetch(`/api/sessions/${encodeURIComponent(P)}/close`,{method:"POST"}),_=await Z.json().catch(()=>({}));(!Z.ok||_?.ok===!1)&&(L+=1)}catch{L+=1}finally{I+=1,c.textContent=`${I}/${u.length}`}}}await Promise.all(Array.from({length:Math.min(6,u.length)},()=>B())),c.textContent=b,c.disabled=!1,w.disabled=!1,p.clear(),te(),L>0&&alert(`Failed: ${L}/${u.length}`)}),h.querySelectorAll("th[data-sort]").forEach(u=>{u.addEventListener("click",()=>{let b=u.dataset.sort;T===b?q=q==="asc"?"desc":"asc":(T=b,q=b==="spawnedAt"||b==="lastMessageAt"?"desc":"asc"),te()})}),n.addEventListener("input",te),K.on(te),te(),ve().then(te)}function To(){return`<section class="page">
|
|
244
244
|
<div class="page-heading">
|
|
245
245
|
<div>
|
|
246
|
-
<p class="eyebrow">${
|
|
247
|
-
<h1>${
|
|
248
|
-
<p>${
|
|
246
|
+
<p class="eyebrow">${a("nav.schedules")}</p>
|
|
247
|
+
<h1>${a("schedules.title")}</h1>
|
|
248
|
+
<p>${a("schedules.subtitle")}</p>
|
|
249
249
|
</div>
|
|
250
250
|
</div>
|
|
251
251
|
<form id="sched-filters" class="filters">
|
|
252
|
-
<input type="search" name="q" placeholder="${
|
|
252
|
+
<input type="search" name="q" placeholder="${a("schedules.search")}" />
|
|
253
253
|
<select name="kind">
|
|
254
|
-
<option value="">${
|
|
254
|
+
<option value="">${a("schedules.anyKind")}</option>
|
|
255
255
|
<option>cron</option>
|
|
256
256
|
<option>interval</option>
|
|
257
257
|
<option>once</option>
|
|
258
258
|
</select>
|
|
259
|
-
<label><input type="checkbox" name="enabled"> ${
|
|
259
|
+
<label><input type="checkbox" name="enabled"> ${a("schedules.enabledOnly")}</label>
|
|
260
260
|
</form>
|
|
261
261
|
<table>
|
|
262
262
|
<thead><tr>
|
|
263
|
-
<th>${
|
|
264
|
-
<th>${
|
|
263
|
+
<th>${a("schedules.name")}</th><th>${a("schedules.bot")}</th><th>${a("schedules.schedule")}</th><th>${a("schedules.next")}</th><th>${a("schedules.last")}</th>
|
|
264
|
+
<th>${a("schedules.repeat")}</th><th>${a("schedules.enabled")}</th><th>${a("schedules.actions")}</th>
|
|
265
265
|
</tr></thead>
|
|
266
266
|
<tbody id="schedules-tbody"></tbody>
|
|
267
267
|
</table>
|
|
268
|
-
</section>`}function
|
|
268
|
+
</section>`}function Fn(e){if(!e)return"\u2014";try{return new Date(e).toLocaleString()}catch{return e}}function Gn(e){e.innerHTML=To();let t=e.querySelector("#schedules-tbody"),n=e.querySelector("#sched-filters");function s(){let i=new FormData(n),l=(i.get("q")??"").toLowerCase(),c=i.get("kind"),w=!!i.get("enabled");return[...K.schedules.values()].filter(h=>!c||h.parsed?.kind===c).filter(h=>!w||h.enabled).filter(h=>!l||JSON.stringify(h).toLowerCase().includes(l)).sort((h,y)=>{if(h.enabled!==y.enabled)return h.enabled?-1:1;let v=h.nextRunAt?Date.parse(h.nextRunAt):1/0,p=y.nextRunAt?Date.parse(y.nextRunAt):1/0;return v-p})}function o(){t.innerHTML=s().map(i=>`<tr data-id="${r(i.id)}">
|
|
269
269
|
<td>${r(i.name??i.id)}</td>
|
|
270
270
|
<td>${r(i.botName??i.larkAppId??"-")}</td>
|
|
271
271
|
<td><code>${r(i.parsed?.display??"?")}</code></td>
|
|
272
|
-
<td>${
|
|
273
|
-
<td>${
|
|
272
|
+
<td>${Fn(i.nextRunAt)}</td>
|
|
273
|
+
<td>${Fn(i.lastRunAt)} ${i.lastStatus==="error"?"\u26A0\uFE0F":""}</td>
|
|
274
274
|
<td>${i.repeat?`${i.repeat.completed}/${i.repeat.times??"\u221E"}`:"\u2014"}</td>
|
|
275
275
|
<td>${i.enabled?"\u2713":"\u2717"}</td>
|
|
276
276
|
<td class="actions-cell">
|
|
277
|
-
<button data-op="run" type="button">${
|
|
278
|
-
${i.enabled?`<button data-op="pause" type="button">${
|
|
277
|
+
<button data-op="run" type="button">${a("schedules.runNow")}</button>
|
|
278
|
+
${i.enabled?`<button data-op="pause" type="button">${a("schedules.pause")}</button>`:`<button data-op="resume" type="button">${a("schedules.resume")}</button>`}
|
|
279
279
|
</td>
|
|
280
|
-
</tr>`).join("")||`<tr><td colspan="8" class="empty">${
|
|
280
|
+
</tr>`).join("")||`<tr><td colspan="8" class="empty">${a("schedules.empty")}</td></tr>`}t.addEventListener("click",async i=>{let l=i.target.closest("button[data-op]");if(!l)return;let c=l.closest("tr[data-id]");if(!c)return;let w=c.dataset.id,h=l.dataset.op;l.disabled=!0;let y=l.textContent;l.textContent="...";try{let v=await fetch(`/api/schedules/${encodeURIComponent(w)}/${h}`,{method:"POST"}),p=await v.json().catch(()=>({}));(!v.ok||p.ok===!1)&&alert(`Failed: ${v.status} ${p?.error??""}`.trim())}catch(v){alert("Network error: "+v)}finally{l.disabled=!1,l.textContent=y}}),n.addEventListener("input",o),K.on(o),o()}var re={chats:[],bots:[]};function Lo(){return`<section class="page">
|
|
281
281
|
<div class="page-heading">
|
|
282
282
|
<div>
|
|
283
|
-
<p class="eyebrow">${
|
|
284
|
-
<h1>${
|
|
285
|
-
<p>${
|
|
283
|
+
<p class="eyebrow">${a("nav.groups")}</p>
|
|
284
|
+
<h1>${a("groups.title")}</h1>
|
|
285
|
+
<p>${a("groups.subtitle")}</p>
|
|
286
286
|
</div>
|
|
287
287
|
</div>
|
|
288
288
|
<form id="g-filters" class="filters">
|
|
289
|
-
<input type="search" name="q" placeholder="${
|
|
290
|
-
<label><input type="checkbox" name="missing"> ${
|
|
291
|
-
<button type="button" id="g-refresh">${
|
|
292
|
-
<button type="button" id="g-create" class="primary">${
|
|
289
|
+
<input type="search" name="q" placeholder="${a("groups.search")}" />
|
|
290
|
+
<label><input type="checkbox" name="missing"> ${a("groups.missingOnly")}</label>
|
|
291
|
+
<button type="button" id="g-refresh">${a("groups.refresh")}</button>
|
|
292
|
+
<button type="button" id="g-create" class="primary">${a("groups.create")}</button>
|
|
293
293
|
</form>
|
|
294
294
|
<table>
|
|
295
295
|
<thead id="g-head"></thead>
|
|
296
296
|
<tbody id="g-body"></tbody>
|
|
297
297
|
</table>
|
|
298
298
|
<dialog id="g-drawer"></dialog>
|
|
299
|
-
</section>`}async function
|
|
299
|
+
</section>`}async function Je(){re=await(await fetch("/api/groups")).json()}async function Mo(){return(await fetch("/api/groups")).json()}function Eo(e,t){if(t.size===0)return!0;let n=e?.memberBots??[];for(let s of t)if(!n.some(o=>o.larkAppId===s&&o.inChat))return!1;return!0}function Wn(e,t){return e.filter(n=>!t||!t.has(n.larkAppId)).map(n=>`
|
|
300
300
|
<label class="checkbox-row">
|
|
301
301
|
<input type="checkbox" name="bot" value="${r(n.larkAppId)}">
|
|
302
302
|
${r(n.botName??n.larkAppId)} <small>(${r(n.larkAppId)})</small>
|
|
303
303
|
</label>
|
|
304
|
-
`).join("")}async function
|
|
304
|
+
`).join("")}async function Jn(e){e.innerHTML=Lo();let t=e.querySelector("#g-head"),n=e.querySelector("#g-body"),s=e.querySelector("#g-filters"),o=e.querySelector("#g-refresh"),i=e.querySelector("#g-drawer");o.onclick=async()=>{o.disabled=!0;try{await Je(),y()}finally{o.disabled=!1}};let l=e.querySelector("#g-create");l.onclick=()=>c(),await Je();function c(){let p=re.bots;if(p.length===0){alert(a("groups.noBotsOnline"));return}i.innerHTML=`
|
|
305
305
|
<article>
|
|
306
|
-
<header><h3>${
|
|
307
|
-
<p>${
|
|
306
|
+
<header><h3>${a("groups.createTitle")}</h3></header>
|
|
307
|
+
<p>${a("groups.createHelp")}</p>
|
|
308
308
|
<form id="g-createform">
|
|
309
309
|
<label class="form-row">
|
|
310
|
-
<span>${
|
|
311
|
-
<input type="text" name="name" placeholder="${
|
|
310
|
+
<span>${a("groups.name")}</span>
|
|
311
|
+
<input type="text" name="name" placeholder="${a("groups.namePlaceholder")}" maxlength="60">
|
|
312
312
|
</label>
|
|
313
313
|
<label class="form-row">
|
|
314
|
-
<span>${
|
|
314
|
+
<span>${a("groups.bindDir")}</span>
|
|
315
315
|
<input type="text" name="bindWorkingDir" placeholder="e.g. ~/projects/botmux">
|
|
316
|
-
<small>${
|
|
316
|
+
<small>${a("groups.bindDirHelp")}</small>
|
|
317
317
|
</label>
|
|
318
318
|
<fieldset>
|
|
319
|
-
<legend>${
|
|
320
|
-
${
|
|
319
|
+
<legend>${a("groups.botPicker")}</legend>
|
|
320
|
+
${Wn(p)}
|
|
321
321
|
</fieldset>
|
|
322
322
|
<div class="actions">
|
|
323
|
-
<button type="submit" class="primary">${
|
|
324
|
-
<button type="button" id="g-create-cancel">${
|
|
323
|
+
<button type="submit" class="primary">${a("groups.createSubmit")}</button>
|
|
324
|
+
<button type="button" id="g-create-cancel">${a("groups.cancel")}</button>
|
|
325
325
|
</div>
|
|
326
326
|
</form>
|
|
327
|
-
</article>`,i.showModal(),i.querySelector("#g-create-cancel").onclick=()=>i.close(),i.querySelector("#g-createform").onsubmit=async $=>{$.preventDefault();let
|
|
327
|
+
</article>`,i.showModal(),i.querySelector("#g-create-cancel").onclick=()=>i.close(),i.querySelector("#g-createform").onsubmit=async $=>{$.preventDefault();let f=new FormData($.target),d=(f.get("name")??"").trim(),g=(f.get("bindWorkingDir")??"").trim(),k=f.getAll("bot");if(k.length===0){alert("Pick at least one bot.");return}let H=$.target.querySelector("button[type=submit]");H&&(H.disabled=!0,H.textContent="Creating...");try{let E=await fetch("/api/groups/create",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({name:d||void 0,larkAppIds:k,bindWorkingDir:g||void 0})}),x=await E.json();if(x.ok&&x.chatId){w(x);let C=Array.isArray(x.invalidBotIds)?x.invalidBotIds:[],U=k.filter(Q=>!C.includes(Q)),G=new Set(U);typeof x.creator=="string"&&x.creator&&G.add(x.creator),T(x.chatId,d||x.chatId,U,x.creator),y(),q(x.chatId,G).catch(()=>{})}else alert(`Failed: ${x.error??E.status}`),i.close()}catch(E){alert("Network error: "+E),i.close()}};function T($,f,d,g){let k=new Set(d);g&&k.add(g);let H=re.bots.map(x=>({larkAppId:x.larkAppId,botName:x.botName,inChat:k.has(x.larkAppId),oncallChat:null})),E={chatId:$,name:f,ownerId:g??null,memberBots:H};re.chats=[E,...re.chats.filter(x=>x.chatId!==$)]}async function q($,f){let d=[600,1200,1200,1200,1200,1200];for(let g of d){await new Promise(E=>setTimeout(E,g));let k;try{k=await Mo()}catch{continue}let H=(k.chats??[]).find(E=>E.chatId===$);if(H&&Eo(H,f)){re=k,y();return}}}}function w(p){let T=String(p.chatId),q=typeof p.shareLink=="string"&&p.shareLink?p.shareLink:`https://applink.feishu.cn/client/chat/open?openChatId=${encodeURIComponent(T)}`,$=p.invalidBotIds??[],f=p.invalidUserIds??[],d=p.autoInvitedOpenId,g=!!p.autoInviteRejected,k=p.ownerTransferredTo,H=p.transferError,E=p.notifyMessageId,x=p.notifyError,C=Array.isArray(p.oncallBindings)?p.oncallBindings:[],U=C.filter(V=>V?.ok).length,G=C.filter(V=>!V?.ok),Q=C.length>0?G.length===0?`<p class="hint-ok">\u5DF2\u7ED1\u5B9A\u76EE\u5F55\uFF1A<code>${r(p.bindResolvedPath??"")}</code>\uFF08${U}/${C.length} bots\uFF09</p>`:`<p class="hint-warn">\u76EE\u5F55\u7ED1\u5B9A\u90E8\u5206\u5931\u8D25\uFF1A\u6210\u529F ${U}/${C.length}\u3002${G.map(V=>`<br><code>${r(V.larkAppId??"?")}</code>: ${r(V.error??"unknown")}`).join("")}</p>`:"",le;if(d){let V=k?"<br><small>\u7FA4\u4E3B\u5DF2\u4ECE\u673A\u5668\u4EBA\u8F6C\u8BA9\u7ED9\u4F60\u3002</small>":H?`<br><small class="hint-warn-inline">\u26A0 \u81EA\u52A8\u8F6C\u8BA9\u7FA4\u4E3B\u5931\u8D25\uFF08${r(H)}\uFF09\uFF0C\u4F60\u73B0\u5728\u662F\u6210\u5458\u4F46\u7FA4\u4E3B\u4ECD\u662F\u673A\u5668\u4EBA\u3002</small>`:"",ne=E?`<br><small>\u673A\u5668\u4EBA\u5DF2\u5728\u7FA4\u91CC @ \u4E86\u4F60\uFF08\u6D88\u606F id <code>${r(E)}</code>\uFF09\uFF0C\u770B\u98DE\u4E66\u901A\u77E5\u5C31\u80FD\u8FDB\u7FA4\u3002</small>`:x?`<br><small class="hint-warn-inline">\u26A0 \u81EA\u52A8 @ \u901A\u77E5\u5931\u8D25\uFF08${r(x)}\uFF09\uFF0C\u65B0\u7FA4\u53EF\u80FD\u4E0D\u4F1A\u4E3B\u52A8\u51FA\u73B0\u5728\u4F60\u4FA7\u8FB9\u680F\uFF0C\u5EFA\u8BAE\u4ECE\u4E0B\u9762\u6309\u94AE\u8DF3\u8FDB\u53BB\u3002</small>`:"";le=`<p class="hint-ok">\u5DF2\u81EA\u52A8\u9080\u8BF7\u4F60\uFF08<code>${r(d)}</code>\uFF09\u4F5C\u4E3A\u6210\u5458\u3002${V}${ne}</p>`}else g?le='<p class="hint-warn">\u98DE\u4E66\u62D2\u7EDD\u4E86\u81EA\u52A8\u9080\u8BF7\uFF08\u4F60\u7684 open_id \u5728\u521B\u5EFA\u8005 bot \u7684 scope \u4E0B\u4E0D\u53EF\u7528\uFF09\u3002<strong>\u4F60\u76EE\u524D\u4E0D\u662F\u65B0\u7FA4\u6210\u5458</strong>\uFF0C\u9700\u8981\u8BA9\u7FA4\u91CC\u7684\u67D0\u4E2A\u673A\u5668\u4EBA\u624B\u52A8\u628A\u4F60\u52A0\u8FDB\u6765\u3002</p>':le='<p class="hint-warn">\u6CA1\u5728 dashboard \u7F13\u5B58\u91CC\u627E\u5230 ownerOpenId\uFF0C<strong>\u6CA1\u6709\u81EA\u52A8\u9080\u8BF7\u4F60</strong>\u3002\u70B9\u5F00\u4E0B\u9762\u94FE\u63A5\u524D\uFF0C\u5148\u8BA9\u7FA4\u91CC\u4EFB\u4E00\u673A\u5668\u4EBA\u624B\u52A8\u628A\u4F60\u52A0\u8FDB\u53BB\u3002</p>';let ue=[$.length?`<li>\u65E0\u6548 bot id: <code>${$.map(r).join(", ")}</code></li>`:"",f.length?`<li>\u65E0\u6548\u7528\u6237 open_id: <code>${f.map(r).join(", ")}</code></li>`:""].filter(Boolean).join("");i.innerHTML=`
|
|
328
328
|
<article>
|
|
329
|
-
<header><h3>${
|
|
330
|
-
<p><b>chatId:</b> <code>${r(
|
|
331
|
-
<p><b>\u521B\u5EFA\u8005:</b> <code>${r(
|
|
332
|
-
${
|
|
333
|
-
${
|
|
334
|
-
${
|
|
329
|
+
<header><h3>${a("groups.successTitle")}</h3></header>
|
|
330
|
+
<p><b>chatId:</b> <code>${r(T)}</code> <button type="button" data-copy="${r(T)}">${a("sessions.copy")}</button></p>
|
|
331
|
+
<p><b>\u521B\u5EFA\u8005:</b> <code>${r(p.creator??"?")}</code></p>
|
|
332
|
+
${le}
|
|
333
|
+
${Q}
|
|
334
|
+
${ue?`<ul>${ue}</ul>`:""}
|
|
335
335
|
<div class="actions">
|
|
336
|
-
<a class="btn-link primary" href="${
|
|
337
|
-
<button type="button" id="g-create-close">${
|
|
336
|
+
<a class="btn-link primary" href="${q}" target="_blank" rel="noopener">${a("groups.openGroup")}</a>
|
|
337
|
+
<button type="button" id="g-create-close">${a("sessions.dismiss")}</button>
|
|
338
338
|
</div>
|
|
339
|
-
</article>`,i.querySelectorAll("[data-copy]").forEach(
|
|
340
|
-
<th>${
|
|
341
|
-
${
|
|
342
|
-
<th>${
|
|
343
|
-
</tr>`}function
|
|
339
|
+
</article>`,i.querySelectorAll("[data-copy]").forEach(V=>{V.onclick=()=>{navigator.clipboard.writeText(V.dataset.copy??""),V.textContent=a("sessions.copied"),setTimeout(()=>{V.textContent=a("sessions.copy")},800)}}),i.querySelector("#g-create-close").onclick=()=>i.close()}function h(){t.innerHTML=`<tr>
|
|
340
|
+
<th>${a("groups.chat")}</th>
|
|
341
|
+
${re.bots.map(p=>`<th>${r(p.botName??p.larkAppId)}</th>`).join("")}
|
|
342
|
+
<th>${a("groups.actions")}</th>
|
|
343
|
+
</tr>`}function y(){h();let p=new FormData(s),T=(p.get("q")??"").toLowerCase(),q=!!p.get("missing"),$=re.chats.filter(f=>!T||(f.name??"").toLowerCase().includes(T)||f.chatId.toLowerCase().includes(T)||(f.ownerId??"").toLowerCase().includes(T)).filter(f=>!q||f.memberBots.some(d=>!d.inChat));if($.length===0){n.innerHTML=`<tr><td colspan="${re.bots.length+2}" class="empty">${a("groups.empty")}</td></tr>`;return}n.innerHTML=$.map(f=>`<tr data-chat="${r(f.chatId)}">
|
|
344
344
|
<td>
|
|
345
|
-
<
|
|
346
|
-
|
|
345
|
+
<div class="g-chat-cell">
|
|
346
|
+
${Rn({chatId:f.chatId,name:f.name,avatarUrl:f.avatar,size:"sm"})}
|
|
347
|
+
<div class="g-chat-meta">
|
|
348
|
+
<strong>${r(f.name??f.chatId)}</strong><br>
|
|
349
|
+
<small><code>${r(f.chatId)}</code></small>
|
|
350
|
+
</div>
|
|
351
|
+
</div>
|
|
347
352
|
</td>
|
|
348
|
-
${
|
|
353
|
+
${re.bots.map(d=>{let g=f.memberBots.find(E=>E.larkAppId===d.larkAppId),k=g?g.error?"!":g.inChat?"\u2713":"\u2717":"?";return`<td class="${g?g.error?"cell-error":g.inChat?"cell-in":"cell-out":"cell-unknown"}" title="${r(g?.error??"")}">${k}</td>`}).join("")}
|
|
349
354
|
<td>
|
|
350
|
-
<button class="add-bots" type="button">${
|
|
351
|
-
<button class="manage-chat" type="button">${
|
|
355
|
+
<button class="add-bots" type="button">${a("groups.addBots")}</button>
|
|
356
|
+
<button class="manage-chat" type="button">${a("groups.manage")}</button>
|
|
352
357
|
</td>
|
|
353
|
-
</tr>`).join("")}
|
|
358
|
+
</tr>`).join("")}y(),n.addEventListener("click",async p=>{let T=p.target.closest("button.add-bots");if(!T)return;let $=T.closest("tr[data-chat]").dataset.chat,f=re.chats.find(k=>k.chatId===$);if(!f)return;let d=new Set(f.memberBots.filter(k=>k.inChat).map(k=>k.larkAppId));if(!re.bots.filter(k=>!d.has(k.larkAppId)).length){alert("All configured bots are already in this chat.");return}i.innerHTML=`
|
|
354
359
|
<article>
|
|
355
|
-
<header><h3>${
|
|
356
|
-
<p>${
|
|
360
|
+
<header><h3>${a("groups.addBots")} \xB7 ${r(f.name??f.chatId)}</h3></header>
|
|
361
|
+
<p>${a("groups.createHelp")}</p>
|
|
357
362
|
<form id="g-addform">
|
|
358
|
-
${
|
|
363
|
+
${Wn(re.bots,d)}
|
|
359
364
|
<div class="actions">
|
|
360
|
-
<button type="submit" class="primary">${
|
|
361
|
-
<button type="button" id="g-cancel">${
|
|
365
|
+
<button type="submit" class="primary">${a("groups.addBots")}</button>
|
|
366
|
+
<button type="button" id="g-cancel">${a("groups.cancel")}</button>
|
|
362
367
|
</div>
|
|
363
368
|
</form>
|
|
364
|
-
</article>`,i.showModal(),i.querySelector("#g-cancel").onclick=()=>i.close(),i.querySelector("#g-addform").onsubmit=async
|
|
365
|
-
`);alert(
|
|
369
|
+
</article>`,i.showModal(),i.querySelector("#g-cancel").onclick=()=>i.close(),i.querySelector("#g-addform").onsubmit=async k=>{k.preventDefault();let E=new FormData(k.target).getAll("bot");if(E.length===0){alert("Pick at least one bot.");return}try{let C=await(await fetch(`/api/groups/${encodeURIComponent($)}/add-bots`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({larkAppIds:E})})).json();if(C.error==="no_proxy_bot")alert("No bot is currently in this chat \u2014 add one manually in Feishu first, then retry.");else if(C.result){let U=C.result.map(G=>`${G.id}: ${G.ok?"OK":`failed (${G.error??"unknown"})`}`).join(`
|
|
370
|
+
`);alert(U),await Je(),y()}else alert(`Unexpected response: ${JSON.stringify(C)}`)}catch(x){alert("Network error: "+x)}finally{i.close()}}}),n.addEventListener("click",async p=>{let T=p.target.closest("button.manage-chat");if(!T)return;let $=T.closest("tr[data-chat]").dataset.chat,f=re.chats.find(d=>d.chatId===$);f&&v(f)});function v(p){let T=p.memberBots.filter($=>$.inChat),q=typeof p.ownerId=="string"?p.ownerId:"";i.innerHTML=`
|
|
366
371
|
<article>
|
|
367
|
-
<header><h3>${
|
|
368
|
-
<p><b>chatId:</b> <code>${r(
|
|
369
|
-
<p><b>${
|
|
372
|
+
<header><h3>${a("groups.manageTitle",{name:p.name??p.chatId})}</h3></header>
|
|
373
|
+
<p><b>chatId:</b> <code>${r(p.chatId)}</code></p>
|
|
374
|
+
<p><b>${a("groups.owner")}:</b> <code>${r(p.ownerId??a("common.unknown"))}</code></p>
|
|
370
375
|
|
|
371
376
|
<fieldset>
|
|
372
|
-
<legend>${
|
|
373
|
-
<p><small>${
|
|
374
|
-
${
|
|
377
|
+
<legend>${a("groups.oncall")}</legend>
|
|
378
|
+
<p><small>${a("groups.oncallHelp")}</small></p>
|
|
379
|
+
${T.length===0?'<p class="empty">\u6CA1\u6709\u673A\u5668\u4EBA\u5728\u7FA4\u91CC</p>':T.map($=>{let f=!!$.oncallChat,d=$.oncallChat?.workingDir??"";return`
|
|
375
380
|
<div class="oncall-row" data-bot="${r($.larkAppId)}">
|
|
376
381
|
<label class="checkbox-row">
|
|
377
|
-
<input type="checkbox" data-action="toggle" ${
|
|
382
|
+
<input type="checkbox" data-action="toggle" ${f?"checked":""}>
|
|
378
383
|
<strong>${r($.botName??$.larkAppId)}</strong>
|
|
379
384
|
<small>(${r($.larkAppId)})</small>
|
|
380
385
|
</label>
|
|
381
386
|
<div class="oncall-row-body">
|
|
382
387
|
<input type="text" data-input="workingDir" placeholder="e.g. /root/iserver/botmux"
|
|
383
|
-
value="${r(
|
|
384
|
-
<button type="button" data-action="save">${
|
|
388
|
+
value="${r(d)}" ${f?"":"disabled"}>
|
|
389
|
+
<button type="button" data-action="save">${a("groups.save")}</button>
|
|
385
390
|
<span class="oncall-status" data-status></span>
|
|
386
391
|
</div>
|
|
387
392
|
</div>
|
|
@@ -389,221 +394,265 @@ effects={effects}, activities={activities}, waits={waits}, cancels={cancels}`,"w
|
|
|
389
394
|
</fieldset>
|
|
390
395
|
|
|
391
396
|
<fieldset>
|
|
392
|
-
<legend>${
|
|
393
|
-
${
|
|
397
|
+
<legend>${a("groups.leaveTitle")}</legend>
|
|
398
|
+
${T.length===0?'<p class="empty">\u6CA1\u6709\u673A\u5668\u4EBA\u5728\u7FA4\u91CC</p>':T.map($=>`
|
|
394
399
|
<label class="checkbox-row">
|
|
395
400
|
<input type="checkbox" name="leave-bot" value="${r($.larkAppId)}">
|
|
396
401
|
${r($.botName??$.larkAppId)}
|
|
397
|
-
<small>${$.larkAppId===
|
|
402
|
+
<small>${$.larkAppId===q?"\xB7 \u7FA4\u4E3B":""}</small>
|
|
398
403
|
</label>
|
|
399
404
|
`).join("")}
|
|
400
405
|
</fieldset>
|
|
401
406
|
|
|
402
407
|
<div class="actions">
|
|
403
|
-
<button id="g-leave-btn" type="button" ${
|
|
404
|
-
<button id="g-disband-btn" type="button" class="contrast" ${
|
|
408
|
+
<button id="g-leave-btn" type="button" ${T.length===0?"disabled":""}>${a("groups.leaveSelected")}</button>
|
|
409
|
+
<button id="g-disband-btn" type="button" class="contrast" ${T.length===0?"disabled":""}>${a("groups.disband")}</button>
|
|
405
410
|
</div>
|
|
406
|
-
<p class="hint-warn"><small>${
|
|
407
|
-
<form method="dialog"><button>${
|
|
408
|
-
</article>`,i.showModal(),i.querySelectorAll(".oncall-row").forEach($=>{let
|
|
409
|
-
`);alert(
|
|
410
|
-
\u5173\u95ED\u4E86 ${
|
|
411
|
-
\u5173\u95ED\u4E86 ${
|
|
412
|
-
${
|
|
411
|
+
<p class="hint-warn"><small>${a("groups.dangerHint")}</small></p>
|
|
412
|
+
<form method="dialog"><button>${a("sessions.dismiss")}</button></form>
|
|
413
|
+
</article>`,i.showModal(),i.querySelectorAll(".oncall-row").forEach($=>{let f=$.dataset.bot,d=$.querySelector("input[data-action=toggle]"),g=$.querySelector("input[data-input=workingDir]"),k=$.querySelector("button[data-action=save]"),H=$.querySelector("[data-status]");d.addEventListener("change",()=>{g.disabled=!d.checked,d.checked&&g.focus()}),k.addEventListener("click",async()=>{H.textContent="",H.className="oncall-status";let E=d.checked,x=g.value.trim();if(E&&!x){H.textContent=a("groups.needWorkingDir"),H.classList.add("hint-warn-inline");return}k.disabled=!0;try{let C=`/api/groups/${encodeURIComponent(p.chatId)}/oncall/${encodeURIComponent(f)}`,U=E?await fetch(C,{method:"PUT",headers:{"content-type":"application/json"},body:JSON.stringify({workingDir:x})}):await fetch(C,{method:"DELETE"}),G=await U.json().catch(()=>({}));if(U.ok&&G.ok){H.textContent=E?`\u2713 \u5DF2\u7ED1\u5B9A \u2192 ${G.resolvedPath??x}`:"\u2713 \u5DF2\u89E3\u7ED1",H.classList.add("hint-ok");try{await Je(),y()}catch{}}else H.textContent=`\u2717 ${G.error??U.status}`,H.classList.add("hint-warn-inline")}catch(C){H.textContent=`\u2717 ${C?.message??C}`,H.classList.add("hint-warn-inline")}finally{k.disabled=!1}})}),i.querySelector("#g-leave-btn").onclick=async()=>{let $=[...i.querySelectorAll("input[name=leave-bot]:checked")].map(f=>f.value);if($.length===0){alert("\u81F3\u5C11\u9009\u4E00\u4E2A\u673A\u5668\u4EBA");return}if(confirm(`\u786E\u5B9A\u8BA9 ${$.length} \u4E2A\u673A\u5668\u4EBA\u9000\u51FA\u7FA4\u804A\uFF1F\u8BE5 bot \u5728\u6B64\u7FA4\u7684\u4F1A\u8BDD\u4F1A\u4E00\u5E76\u5173\u95ED\u3002`))try{let d=await(await fetch(`/api/groups/${encodeURIComponent(p.chatId)}/leave`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({larkAppIds:$})})).json(),g=(d.result??[]).map(k=>{if(!k.ok)return`${k.larkAppId}: \u5931\u8D25 (${k.error??"unknown"})`;let H=k.closedSessions??[],E=H.filter(U=>!U.ok).length,x=H.length-E,C=H.length===0?"":E===0?`\uFF08\u5173\u95ED ${x} \u4E2A\u4F1A\u8BDD\uFF09`:`\uFF08\u5173\u95ED ${x} \u4E2A\uFF0C${E} \u4E2A\u5931\u8D25\uFF09`;return`${k.larkAppId}: OK${C}`}).join(`
|
|
414
|
+
`);alert(g||`Unexpected: ${JSON.stringify(d)}`),await Je(),y()}catch(f){alert("Network error: "+f)}finally{i.close()}},i.querySelector("#g-disband-btn").onclick=async()=>{if(T.length===0||!confirm(`\u786E\u5B9A\u89E3\u6563\u7FA4\u804A\u300C${p.name??p.chatId}\u300D\uFF1F\u6B64\u64CD\u4F5C\u4E0D\u53EF\u6062\u590D\uFF0C\u672C\u7FA4\u6240\u6709\u673A\u5668\u4EBA\u4F1A\u8BDD\u4E5F\u4F1A\u4E00\u5E76\u5173\u95ED\u3002`))return;let $=[...T].sort((d,g)=>(g.larkAppId===q?1:0)-(d.larkAppId===q?1:0)),f=[];for(let d of $)try{let g=await fetch(`/api/groups/${encodeURIComponent(p.chatId)}/disband`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({larkAppId:d.larkAppId})}),k=await g.json();if(k.ok){let H=k.closedSessions??[],E=H.filter(U=>!U.ok).length,x=H.length-E,C=H.length===0?"":E===0?`
|
|
415
|
+
\u5173\u95ED\u4E86 ${x} \u4E2A\u4F1A\u8BDD\u3002`:`
|
|
416
|
+
\u5173\u95ED\u4E86 ${x} \u4E2A\u4F1A\u8BDD\uFF0C${E} \u4E2A\u4F1A\u8BDD\u5173\u95ED\u5931\u8D25\u3002`;alert(`\u5DF2\u89E3\u6563\uFF08\u7531 ${d.botName??d.larkAppId} \u6267\u884C\uFF09${C}`),await Je(),y(),i.close();return}f.push(`${d.botName??d.larkAppId}: ${k.error??g.status}`)}catch(g){f.push(`${d.botName??d.larkAppId}: ${g}`)}alert(`\u6240\u6709\u5728\u7FA4\u673A\u5668\u4EBA\u5747\u65E0\u6CD5\u89E3\u6563\uFF1A
|
|
417
|
+
${f.join(`
|
|
413
418
|
`)}
|
|
414
419
|
|
|
415
|
-
\u5EFA\u8BAE\u6539\u7528\u300C\u9000\u51FA\u7FA4\u804A\u300D\u3002`)}}s.addEventListener("input",
|
|
420
|
+
\u5EFA\u8BAE\u6539\u7528\u300C\u9000\u51FA\u7FA4\u804A\u300D\u3002`)}}s.addEventListener("input",y)}var he={bots:[]},Ve=null,Ke=null;function Kn(e){let t=null;for(let n of K.sessions.values())n.larkAppId!==e||!n.cliId||(!t||Number(n.lastMessageAt??0)>Number(t.lastMessageAt??0))&&(t=n);return t?.cliId??""}function xo(){return`<section class="page">
|
|
416
421
|
<div class="page-heading">
|
|
417
422
|
<div>
|
|
418
|
-
<p class="eyebrow">${
|
|
419
|
-
<h1>${
|
|
420
|
-
<p>${
|
|
423
|
+
<p class="eyebrow">${a("nav.botDefaults")}</p>
|
|
424
|
+
<h1>${a("botDefaults.title")}</h1>
|
|
425
|
+
<p>${a("botDefaults.subtitle")}</p>
|
|
421
426
|
</div>
|
|
422
427
|
</div>
|
|
423
428
|
<form id="bd-filters" class="filters sessions-filters">
|
|
424
|
-
<input type="search" name="q" placeholder="${
|
|
425
|
-
<button type="button" id="bd-refresh">${
|
|
429
|
+
<input type="search" name="q" placeholder="${a("botDefaults.search")}" />
|
|
430
|
+
<button type="button" id="bd-refresh">${a("botDefaults.refresh")}</button>
|
|
426
431
|
</form>
|
|
427
432
|
<div class="bd-layout">
|
|
428
433
|
<aside id="bd-roster" class="bd-roster"></aside>
|
|
429
434
|
<div id="bd-list" class="bd-detail"></div>
|
|
430
435
|
</div>
|
|
431
|
-
</section>`}async function
|
|
432
|
-
|
|
436
|
+
</section>`}async function Vn(){try{let e=await fetch("/api/bots"),t=await e.json().catch(()=>({}));if(!e.ok){Ve=t?.error?`HTTP ${e.status}: ${t.error}${t.path?` (${t.path})`:""}`:`HTTP ${e.status}`,he={bots:[]};return}if(!t||!Array.isArray(t.bots)){Ve="unexpected response shape (no `bots` array)",he={bots:[]};return}Ve=null,he=t}catch(e){Ve=e?.message??String(e),he={bots:[]}}}function Yn(e){if(!e)return"\u2014";let t=new Date(e);return Number.isNaN(t.getTime())?"\u2014":t.toLocaleString()}async function Qn(e){e.innerHTML=xo();let t=e.querySelector("#bd-list"),n=e.querySelector("#bd-roster"),s=e.querySelector("#bd-filters"),o=e.querySelector("#bd-refresh");o.onclick=async()=>{o.disabled=!0;try{await Vn(),i()}finally{o.disabled=!1}},t.addEventListener("click",d=>{let g=d.target.closest(".toggle-tx small, small.bd-help");g&&(d.preventDefault(),g.classList.toggle("open"))}),await Vn();function i(){let g=(new FormData(s).get("q")??"").toLowerCase(),k=he.bots.filter(E=>!g||(E.botName??"").toLowerCase().includes(g)||(E.larkAppId??"").toLowerCase().includes(g));if(Ve){n.innerHTML="",t.innerHTML=`<p class="hint-warn">\u65E0\u6CD5\u52A0\u8F7D bot \u5217\u8868\uFF1A${r(Ve)}<br>\u5E38\u89C1\u539F\u56E0\uFF1Adashboard / daemon \u8FDB\u7A0B\u8FD8\u5728\u8DD1\u65E7\u4EE3\u7801\uFF0C\u6267\u884C <code>botmux restart</code> \u540E\u5237\u65B0\u3002</p>`;return}if(k.length===0){n.innerHTML="",t.innerHTML=`<p class="empty">${a("botDefaults.empty")}</p>`;return}(!Ke||!k.some(E=>E.larkAppId===Ke))&&(Ke=k[0].larkAppId),n.innerHTML=k.map(l).join(""),n.querySelectorAll(".bd-roster-item").forEach(E=>{E.onclick=()=>{Ke=E.dataset.appid,i()}});let H=k.find(E=>E.larkAppId===Ke);t.innerHTML=c(H),f()}function l(d){let g=d.botName??d.larkAppId,k=Kn(d.larkAppId),H=d.defaultOncall?.enabled?'<span class="bd-roster-flag">oncall</span>':"";return`<div class="bd-roster-item${d.larkAppId===Ke?" on":""}" data-appid="${r(d.larkAppId)}" role="button" tabindex="0">
|
|
437
|
+
${me({name:g,larkAppId:d.larkAppId,size:"sm"})}
|
|
433
438
|
<div class="bd-roster-tx">
|
|
434
|
-
<b>${r(
|
|
435
|
-
<span>${r(
|
|
439
|
+
<b>${r(g)}</b>
|
|
440
|
+
<span>${r(k||d.larkAppId.slice(0,14))}</span>
|
|
436
441
|
</div>
|
|
437
|
-
${
|
|
438
|
-
</div>`}function
|
|
442
|
+
${H}
|
|
443
|
+
</div>`}function c(d){if(d.error)return`<article class="bd-card bd-profile" data-appid="${r(d.larkAppId)}">
|
|
439
444
|
<header class="bd-profile-head">
|
|
440
|
-
|
|
445
|
+
${me({name:d.botName??d.larkAppId,larkAppId:d.larkAppId})}
|
|
441
446
|
<div class="bd-profile-id"><strong>${r(d.botName??d.larkAppId)}</strong>
|
|
442
447
|
<code>${r(d.larkAppId)}</code></div>
|
|
443
448
|
</header>
|
|
444
449
|
<p class="hint-warn-inline">\u67E5\u8BE2\u5931\u8D25\uFF1A${r(d.error)}</p>
|
|
445
|
-
</article>`;let
|
|
450
|
+
</article>`;let g=d.defaultOncall??{enabled:!1,workingDir:"",since:0},k=!!g.enabled,H=d.botName??d.larkAppId,E=Kn(d.larkAppId);return`<article class="bd-card bd-profile" data-appid="${r(d.larkAppId)}">
|
|
446
451
|
<header class="bd-profile-head">
|
|
447
|
-
|
|
452
|
+
${me({name:H,larkAppId:d.larkAppId,dot:"ok"})}
|
|
448
453
|
<div class="bd-profile-id">
|
|
449
|
-
<strong>${r(
|
|
450
|
-
${
|
|
454
|
+
<strong>${r(H)}</strong>
|
|
455
|
+
${E?`<span class="mate-role">${r(E)}</span>`:""}
|
|
451
456
|
<code>${r(d.larkAppId)}</code>
|
|
452
457
|
</div>
|
|
453
458
|
<div class="bd-profile-meta bd-meta">
|
|
454
|
-
<small class="bd-meta-ok">\u25CF ${
|
|
455
|
-
<small data-oncall-since>${
|
|
456
|
-
<small>${
|
|
459
|
+
<small class="bd-meta-ok">\u25CF ${a("botDefaults.metaOnline")}</small>
|
|
460
|
+
<small data-oncall-since>${a("botDefaults.lastEnabled")}: ${r(Yn(g.since??0))}</small>
|
|
461
|
+
<small>${a("botDefaults.autobound",{count:d.autoboundChatCount??0})}</small>
|
|
457
462
|
</div>
|
|
458
463
|
</header>
|
|
459
464
|
<div class="bd-body bd-grid">
|
|
460
465
|
<section class="bd-tile">
|
|
461
466
|
<section class="bd-section">
|
|
462
|
-
<h3 class="bd-section-title">${
|
|
467
|
+
<h3 class="bd-section-title">${a("botDefaults.sectionOncall")}</h3>
|
|
463
468
|
<label class="toggle-row">
|
|
464
|
-
<input type="checkbox" data-action="toggle" ${
|
|
469
|
+
<input type="checkbox" data-action="toggle" ${k?"checked":""}>
|
|
465
470
|
<span class="switch" aria-hidden="true"></span>
|
|
466
|
-
<span class="toggle-tx"><strong>${
|
|
467
|
-
<small>${
|
|
471
|
+
<span class="toggle-tx"><strong>${a("botDefaults.defaultOncall")}</strong>
|
|
472
|
+
<small>${a("botDefaults.defaultOncallHelp")}\u3002${a("botDefaults.warning")}</small></span>
|
|
468
473
|
</label>
|
|
469
474
|
<div class="bd-row">
|
|
470
475
|
<label>
|
|
471
|
-
<span>${
|
|
476
|
+
<span>${a("botDefaults.workingDir")}</span>
|
|
472
477
|
<input type="text" data-input="workingDir" placeholder="e.g. /root/iserver/botmux"
|
|
473
|
-
value="${r(
|
|
478
|
+
value="${r(g.workingDir??"")}" ${k?"":"disabled"}>
|
|
474
479
|
</label>
|
|
475
480
|
</div>
|
|
476
481
|
<div class="actions">
|
|
477
|
-
<button type="button" class="primary" data-action="save">${
|
|
482
|
+
<button type="button" class="primary" data-action="save">${a("botDefaults.save")}</button>
|
|
478
483
|
<span class="oncall-status" data-status></span>
|
|
479
484
|
</div>
|
|
480
|
-
${
|
|
485
|
+
${$(d)}
|
|
481
486
|
</section>
|
|
482
487
|
</section>
|
|
483
|
-
<section class="bd-tile">${
|
|
484
|
-
<section class="bd-tile">${
|
|
485
|
-
<section class="bd-tile">${
|
|
488
|
+
<section class="bd-tile">${w(d)}</section>
|
|
489
|
+
<section class="bd-tile">${p(d)}</section>
|
|
490
|
+
<section class="bd-tile">${v(d)}${y(d)}</section>
|
|
491
|
+
<section class="bd-tile">${q(d)}</section>
|
|
486
492
|
</div>
|
|
487
|
-
</article>`}function
|
|
488
|
-
<h3 class="bd-section-title">${
|
|
489
|
-
<p class="bd-section-note">${
|
|
493
|
+
</article>`}function w(d){let g=typeof d.teamRole=="string";return`<section class="bd-section">
|
|
494
|
+
<h3 class="bd-section-title">${a("botDefaults.sectionRole")}</h3>
|
|
495
|
+
<p class="bd-section-note">${a("botDefaults.roleHelp")}</p>
|
|
490
496
|
<textarea data-input="teamRole" rows="6"
|
|
491
|
-
placeholder="${r(
|
|
492
|
-
style="width:100%;box-sizing:border-box;font:13px/1.5 ui-monospace,Menlo,monospace;padding:10px"${
|
|
497
|
+
placeholder="${r(a("botDefaults.rolePlaceholder"))}"
|
|
498
|
+
style="width:100%;box-sizing:border-box;font:13px/1.5 ui-monospace,Menlo,monospace;padding:10px"${g?"":" disabled"}>${g?r(d.teamRole):""}</textarea>
|
|
493
499
|
<div class="actions">
|
|
494
|
-
<button type="button" class="primary" data-action="save-role"${
|
|
495
|
-
<button type="button" data-action="delete-role"${
|
|
500
|
+
<button type="button" class="primary" data-action="save-role"${g?"":" disabled"}>${a("botDefaults.roleSave")}</button>
|
|
501
|
+
<button type="button" data-action="delete-role"${g?"":" disabled"}>${a("botDefaults.roleDelete")}</button>
|
|
496
502
|
<span class="oncall-status" data-role-status></span>
|
|
497
503
|
</div>
|
|
498
|
-
</section>`}function
|
|
499
|
-
<h3 class="bd-section-title">${
|
|
504
|
+
</section>`}function h(d){return d==null?a("botDefaults.brandStateDefault"):d.trim()===""?a("botDefaults.brandStateOff"):a("botDefaults.brandStateCustom")}function y(d){let g=d.brandLabel??null;return`<section class="bd-section">
|
|
505
|
+
<h3 class="bd-section-title">${a("botDefaults.sectionBrand")}</h3>
|
|
500
506
|
<div class="bd-row bd-brand">
|
|
501
507
|
<label>
|
|
502
|
-
<span>${
|
|
508
|
+
<span>${a("botDefaults.brandLabel")}</span>
|
|
503
509
|
<input type="text" data-input="brandLabel"
|
|
504
|
-
placeholder="${r(
|
|
505
|
-
value="${r(
|
|
510
|
+
placeholder="${r(a("botDefaults.brandLabelPlaceholder"))}"
|
|
511
|
+
value="${r(g??"")}">
|
|
506
512
|
</label>
|
|
507
|
-
<small data-brand-state>${r(
|
|
508
|
-
<small class="bd-help">${
|
|
513
|
+
<small data-brand-state>${r(h(g))}</small>
|
|
514
|
+
<small class="bd-help">${a("botDefaults.brandLabelHelp")}</small>
|
|
509
515
|
<div class="actions">
|
|
510
|
-
<button type="button" class="primary" data-action="save-brand">${
|
|
511
|
-
<button type="button" data-action="reset-brand">${
|
|
516
|
+
<button type="button" class="primary" data-action="save-brand">${a("botDefaults.brandSave")}</button>
|
|
517
|
+
<button type="button" data-action="reset-brand">${a("botDefaults.brandReset")}</button>
|
|
512
518
|
<span class="oncall-status" data-brand-status></span>
|
|
513
519
|
</div>
|
|
514
520
|
</div>
|
|
515
|
-
</section>`}function
|
|
516
|
-
<h3 class="bd-section-title">${
|
|
521
|
+
</section>`}function v(d){let g=d.disableStreamingCard===!0,k=d.writableTerminalLinkInCard===!0,H=d.privateCard===!0;return`<section class="bd-section">
|
|
522
|
+
<h3 class="bd-section-title">${a("botDefaults.sectionCard")}</h3>
|
|
517
523
|
<label class="toggle-row">
|
|
518
|
-
<input type="checkbox" data-action="toggle-disable-streaming" ${
|
|
524
|
+
<input type="checkbox" data-action="toggle-disable-streaming" ${g?"checked":""}>
|
|
519
525
|
<span class="switch" aria-hidden="true"></span>
|
|
520
|
-
<span class="toggle-tx"><strong>${
|
|
521
|
-
<small>${
|
|
526
|
+
<span class="toggle-tx"><strong>${a("botDefaults.disableStreaming")}</strong>
|
|
527
|
+
<small>${a("botDefaults.disableStreamingHelp")}</small></span>
|
|
522
528
|
</label>
|
|
523
529
|
<label class="toggle-row">
|
|
524
|
-
<input type="checkbox" data-action="toggle-writable-link" ${
|
|
530
|
+
<input type="checkbox" data-action="toggle-writable-link" ${k?"checked":""} ${g?"disabled":""}>
|
|
525
531
|
<span class="switch" aria-hidden="true"></span>
|
|
526
|
-
<span class="toggle-tx"><strong>${
|
|
527
|
-
<small>${
|
|
532
|
+
<span class="toggle-tx"><strong>${a("botDefaults.writableLink")}</strong>
|
|
533
|
+
<small>${a("botDefaults.writableLinkHelp")}</small></span>
|
|
528
534
|
</label>
|
|
529
535
|
<label class="toggle-row">
|
|
530
|
-
<input type="checkbox" data-action="toggle-private-card" ${
|
|
536
|
+
<input type="checkbox" data-action="toggle-private-card" ${H?"checked":""}>
|
|
531
537
|
<span class="switch" aria-hidden="true"></span>
|
|
532
|
-
<span class="toggle-tx"><strong>${
|
|
533
|
-
<small>${
|
|
538
|
+
<span class="toggle-tx"><strong>${a("botDefaults.privateCard")}</strong>
|
|
539
|
+
<small>${a("botDefaults.privateCardHelp")}</small></span>
|
|
534
540
|
</label>
|
|
535
541
|
<div class="actions">
|
|
536
|
-
<small data-card-pref-moot class="hint-warn-inline" ${
|
|
542
|
+
<small data-card-pref-moot class="hint-warn-inline" ${g?"":"hidden"}>${a("botDefaults.writableLinkMoot")}</small>
|
|
537
543
|
<span class="oncall-status" data-card-pref-status></span>
|
|
538
544
|
</div>
|
|
539
|
-
</section>`}function
|
|
540
|
-
<h3 class="bd-section-title">${
|
|
545
|
+
</section>`}function p(d){let g=d.p2pMode==="chat"?"chat":"thread",k=d.regularGroupReplyMode==="new-topic"||d.regularGroupReplyMode==="shared"?d.regularGroupReplyMode:"chat",H=d.regularGroupMentionMode==="topic"||d.regularGroupMentionMode==="never"?d.regularGroupMentionMode:"always",E=(C,U)=>`<option value="${C}" ${k===C?"selected":""}>${r(U)}</option>`,x=(C,U)=>`<option value="${C}" ${H===C?"selected":""}>${r(U)}</option>`;return`<section class="bd-section">
|
|
546
|
+
<h3 class="bd-section-title">${a("botDefaults.sectionSessionMode")}</h3>
|
|
547
|
+
<div class="bd-row">
|
|
548
|
+
<label>
|
|
549
|
+
<span>${a("botDefaults.p2pMode")}</span>
|
|
550
|
+
<select data-input="p2pMode">
|
|
551
|
+
<option value="thread" ${g==="chat"?"":"selected"}>${r(a("botDefaults.p2pThread"))}</option>
|
|
552
|
+
<option value="chat" ${g==="chat"?"selected":""}>${r(a("botDefaults.p2pChat"))}</option>
|
|
553
|
+
</select>
|
|
554
|
+
</label>
|
|
555
|
+
<small class="bd-help">${a("botDefaults.p2pHelp")}</small>
|
|
556
|
+
<div class="actions">
|
|
557
|
+
<span class="oncall-status" data-p2p-status></span>
|
|
558
|
+
</div>
|
|
559
|
+
</div>
|
|
560
|
+
<div class="bd-row">
|
|
561
|
+
<label>
|
|
562
|
+
<span>${a("botDefaults.regularGroupMode")}</span>
|
|
563
|
+
<select data-input="regularGroupMode">
|
|
564
|
+
${E("chat",a("botDefaults.regularGroupModeChat"))}
|
|
565
|
+
${E("new-topic",a("botDefaults.regularGroupModeNewTopic"))}
|
|
566
|
+
${E("shared",a("botDefaults.regularGroupModeShared"))}
|
|
567
|
+
</select>
|
|
568
|
+
</label>
|
|
569
|
+
<small class="bd-help">${a("botDefaults.regularGroupModeHelp")}</small>
|
|
570
|
+
<div class="actions">
|
|
571
|
+
<span class="oncall-status" data-regular-group-status></span>
|
|
572
|
+
</div>
|
|
573
|
+
</div>
|
|
574
|
+
<div class="bd-row">
|
|
575
|
+
<label>
|
|
576
|
+
<span>${a("botDefaults.mentionMode")}</span>
|
|
577
|
+
<select data-input="regularGroupMentionMode">
|
|
578
|
+
${x("always",a("botDefaults.mentionModeAlways"))}
|
|
579
|
+
${x("topic",a("botDefaults.mentionModeTopic"))}
|
|
580
|
+
${x("never",a("botDefaults.mentionModeNever"))}
|
|
581
|
+
</select>
|
|
582
|
+
</label>
|
|
583
|
+
<small class="bd-help">${a("botDefaults.mentionModeHelp")}</small>
|
|
584
|
+
<div class="actions">
|
|
585
|
+
<span class="oncall-status" data-mention-mode-status></span>
|
|
586
|
+
</div>
|
|
587
|
+
</div>
|
|
588
|
+
</section>`}function T(d){return d==null?a("botDefaults.quotaStateOff"):a("botDefaults.quotaStateOn",{count:d})}function q(d){let g=d.restrictGrantCommands===!0,k=typeof d.messageQuotaDefaultLimit=="number"?d.messageQuotaDefaultLimit:null;return`<section class="bd-section">
|
|
589
|
+
<h3 class="bd-section-title">${a("botDefaults.sectionGrant")}</h3>
|
|
541
590
|
<label class="toggle-row">
|
|
542
|
-
<input type="checkbox" data-action="toggle-restrict-grant" ${
|
|
591
|
+
<input type="checkbox" data-action="toggle-restrict-grant" ${g?"checked":""}>
|
|
543
592
|
<span class="switch" aria-hidden="true"></span>
|
|
544
|
-
<span class="toggle-tx"><strong>${
|
|
545
|
-
<small>${
|
|
593
|
+
<span class="toggle-tx"><strong>${a("botDefaults.restrictGrant")}</strong>
|
|
594
|
+
<small>${a("botDefaults.restrictGrantHelp")}</small></span>
|
|
546
595
|
</label>
|
|
547
596
|
<div class="bd-row bd-quota">
|
|
548
597
|
<label>
|
|
549
|
-
<span>${
|
|
598
|
+
<span>${a("botDefaults.quotaDefault")}</span>
|
|
550
599
|
<input type="number" min="1" step="1" data-input="quotaLimit"
|
|
551
|
-
placeholder="${r(
|
|
552
|
-
value="${
|
|
600
|
+
placeholder="${r(a("botDefaults.quotaPlaceholder"))}"
|
|
601
|
+
value="${k??""}">
|
|
553
602
|
</label>
|
|
554
|
-
<small data-quota-state>${r(
|
|
555
|
-
<small class="bd-help">${
|
|
603
|
+
<small data-quota-state>${r(T(k))}</small>
|
|
604
|
+
<small class="bd-help">${a("botDefaults.quotaHelp")}</small>
|
|
556
605
|
<div class="actions">
|
|
557
|
-
<button type="button" class="primary" data-action="save-quota">${
|
|
558
|
-
<button type="button" data-action="off-quota">${
|
|
606
|
+
<button type="button" class="primary" data-action="save-quota">${a("botDefaults.quotaSave")}</button>
|
|
607
|
+
<button type="button" data-action="off-quota">${a("botDefaults.quotaOff")}</button>
|
|
559
608
|
<span class="oncall-status" data-grant-status></span>
|
|
560
609
|
</div>
|
|
561
610
|
</div>
|
|
562
|
-
</section>`}function
|
|
563
|
-
<h4 class="bd-subsection-title">${
|
|
611
|
+
</section>`}function $(d){let g=d.autoStartOnGroupJoin===!0,k=d.autoStartOnNewTopic===!0,H=typeof d.autoStartOnGroupJoinPrompt=="string"?d.autoStartOnGroupJoinPrompt:"";return`<div class="bd-subsection">
|
|
612
|
+
<h4 class="bd-subsection-title">${a("botDefaults.sectionAutoStart")}</h4>
|
|
564
613
|
<label class="toggle-row">
|
|
565
|
-
<input type="checkbox" data-action="toggle-auto-join" ${
|
|
614
|
+
<input type="checkbox" data-action="toggle-auto-join" ${g?"checked":""}>
|
|
566
615
|
<span class="switch" aria-hidden="true"></span>
|
|
567
|
-
<span class="toggle-tx"><strong>${
|
|
568
|
-
<small>${
|
|
616
|
+
<span class="toggle-tx"><strong>${a("botDefaults.autoStartJoin")}</strong>
|
|
617
|
+
<small>${a("botDefaults.autoStartJoinHelp")}</small></span>
|
|
569
618
|
</label>
|
|
570
619
|
<div class="bd-row">
|
|
571
620
|
<label>
|
|
572
|
-
<span>${
|
|
621
|
+
<span>${a("botDefaults.autoStartJoinPrompt")}</span>
|
|
573
622
|
<textarea data-input="autoJoinPrompt" rows="3"
|
|
574
|
-
placeholder="${r(
|
|
623
|
+
placeholder="${r(a("botDefaults.autoStartJoinPromptPlaceholder"))}">${r(H)}</textarea>
|
|
575
624
|
</label>
|
|
576
625
|
<div class="actions">
|
|
577
|
-
<button type="button" class="primary" data-action="save-auto-join-prompt">${
|
|
626
|
+
<button type="button" class="primary" data-action="save-auto-join-prompt">${a("botDefaults.autoStartJoinPromptSave")}</button>
|
|
578
627
|
</div>
|
|
579
628
|
</div>
|
|
580
629
|
<label class="toggle-row">
|
|
581
|
-
<input type="checkbox" data-action="toggle-auto-topic" ${
|
|
630
|
+
<input type="checkbox" data-action="toggle-auto-topic" ${k?"checked":""}>
|
|
582
631
|
<span class="switch" aria-hidden="true"></span>
|
|
583
|
-
<span class="toggle-tx"><strong>${
|
|
584
|
-
<small>${
|
|
632
|
+
<span class="toggle-tx"><strong>${a("botDefaults.autoStartTopic")}</strong>
|
|
633
|
+
<small>${a("botDefaults.autoStartTopicHelp")}</small></span>
|
|
585
634
|
</label>
|
|
586
635
|
<div class="actions">
|
|
587
636
|
<span class="oncall-status" data-auto-start-status></span>
|
|
588
637
|
</div>
|
|
589
|
-
</div>`}function
|
|
638
|
+
</div>`}function f(){t.querySelectorAll(".bd-card").forEach(d=>{let g=d.dataset.appid,k=d.querySelector("input[data-action=toggle]"),H=d.querySelector("input[data-input=workingDir]"),E=d.querySelector("button[data-action=save]"),x=d.querySelector("[data-status]");if(!k||!H||!E||!x)return;k.addEventListener("change",()=>{H.disabled=!k.checked,k.checked&&H.focus()}),E.addEventListener("click",async()=>{x.textContent="",x.className="oncall-status";let A=k.checked,R=H.value.trim();if(A&&!R){x.textContent=a("botDefaults.required"),x.classList.add("hint-warn-inline");return}E.disabled=!0;try{let O=await fetch(`/api/bots/${encodeURIComponent(g)}/default-oncall`,{method:"PUT",headers:{"content-type":"application/json"},body:JSON.stringify({enabled:A,workingDir:R})}),z=await O.json().catch(()=>({}));if(O.ok&&z.ok){let N=z.resolvedPath?` \u2192 ${z.resolvedPath}`:"";x.textContent=A?`\u2713 \u5DF2\u5F00\u542F${N}\uFF08\u672A\u7ED1\u5B9A\u7684\u7FA4\u4E0B\u6B21\u5F00\u8BDD\u9898\u81EA\u52A8 oncall\uFF09`:"\u2713 \u5DF2\u5173\u95ED\uFF08\u5DF2\u7ED1\u5B9A\u7684\u7FA4\u4E0D\u52A8\uFF09",x.classList.add("hint-ok");let F=he.bots.find(ge=>ge.larkAppId===g);F&&z.defaultOncall&&(F.defaultOncall=z.defaultOncall);let ie=d.querySelector("[data-oncall-since]");ie&&z.defaultOncall?.since!=null&&(ie.textContent=`\u4E0A\u6B21\u542F\u7528\u65F6\u95F4\uFF1A${Yn(z.defaultOncall.since)}`)}else x.textContent=`\u2717 ${z.error??O.status}`,x.classList.add("hint-warn-inline")}catch(O){x.textContent=`\u2717 ${O?.message??O}`,x.classList.add("hint-warn-inline")}finally{E.disabled=!1}});let C=d.querySelector("input[data-input=brandLabel]"),U=d.querySelector("button[data-action=save-brand]"),G=d.querySelector("button[data-action=reset-brand]"),Q=d.querySelector("[data-brand-status]"),le=d.querySelector("[data-brand-state]");async function ue(A,R){if(Q){Q.textContent="",Q.className="oncall-status",R.disabled=!0;try{let O=await fetch(`/api/bots/${encodeURIComponent(g)}/brand-label`,{method:"PUT",headers:{"content-type":"application/json"},body:JSON.stringify({brandLabel:A})}),z=await O.json().catch(()=>({}));if(O.ok&&z.ok){let N=z.brandLabel??null;Q.textContent="\u2713",Q.classList.add("hint-ok"),C&&(C.value=N??""),le&&(le.textContent=h(N));let F=he.bots.find(ie=>ie.larkAppId===g);F&&(F.brandLabel=N)}else Q.textContent=`\u2717 ${z.error??O.status}`,Q.classList.add("hint-warn-inline")}catch(O){Q.textContent=`\u2717 ${O?.message??O}`,Q.classList.add("hint-warn-inline")}finally{R.disabled=!1}}}C&&U&&U.addEventListener("click",()=>ue(C.value,U)),G&&G.addEventListener("click",()=>ue(null,G));let V=d.querySelector("input[data-action=toggle-disable-streaming]"),ne=d.querySelector("input[data-action=toggle-writable-link]"),we=d.querySelector("input[data-action=toggle-private-card]"),He=d.querySelector("[data-card-pref-status]"),Te=d.querySelector("[data-card-pref-moot]");async function ee(A,R,O=He){if(O){O.textContent="",O.className="oncall-status",R.disabled=!0;try{let z=await fetch(`/api/bots/${encodeURIComponent(g)}/card-prefs`,{method:"PUT",headers:{"content-type":"application/json"},body:JSON.stringify(A)}),N=await z.json().catch(()=>({}));if(z.ok&&N.ok){O.textContent=`\u2713 ${a("botDefaults.cardPrefSaved")}`,O.classList.add("hint-ok");let F=he.bots.find(ie=>ie.larkAppId===g);F&&(F.disableStreamingCard=N.disableStreamingCard,F.writableTerminalLinkInCard=N.writableTerminalLinkInCard,F.privateCard=N.privateCard,F.autoStartOnGroupJoin=N.autoStartOnGroupJoin,F.autoStartOnGroupJoinPrompt=N.autoStartOnGroupJoinPrompt,F.autoStartOnNewTopic=N.autoStartOnNewTopic,F.regularGroupReplyMode=N.regularGroupReplyMode,F.regularGroupMentionMode=N.regularGroupMentionMode)}else O.textContent=`\u2717 ${N.error??z.status}`,O.classList.add("hint-warn-inline")}catch(z){O.textContent=`\u2717 ${z?.message??z}`,O.classList.add("hint-warn-inline")}finally{R===ne?R.disabled=!!V?.checked:R.disabled=!1}}}V&&V.addEventListener("change",()=>{let A=V.checked;ne&&(ne.disabled=A),Te&&(Te.hidden=!A),ee({disableStreamingCard:A},V)}),ne&&ne.addEventListener("change",()=>{ee({writableTerminalLinkInCard:ne.checked},ne)}),we&&we.addEventListener("change",()=>{ee({privateCard:we.checked},we)});let se=d.querySelector("input[data-action=toggle-auto-join]"),pe=d.querySelector("input[data-action=toggle-auto-topic]"),te=d.querySelector("textarea[data-input=autoJoinPrompt]"),Le=d.querySelector("button[data-action=save-auto-join-prompt]"),Me=d.querySelector("[data-auto-start-status]");se&&se.addEventListener("change",()=>{ee({autoStartOnGroupJoin:se.checked},se,Me)}),pe&&pe.addEventListener("change",()=>{ee({autoStartOnNewTopic:pe.checked},pe,Me)}),te&&Le&&Le.addEventListener("click",()=>{ee({autoStartOnGroupJoinPrompt:te.value},Le,Me)});let X=d.querySelector("select[data-input=p2pMode]"),u=d.querySelector("[data-p2p-status]");X&&u&&X.addEventListener("change",async()=>{let A=X.value==="chat"?"chat":"thread";u.textContent="",u.className="oncall-status",X.disabled=!0;try{let R=await fetch(`/api/bots/${encodeURIComponent(g)}/p2p-mode`,{method:"PUT",headers:{"content-type":"application/json"},body:JSON.stringify({p2pMode:A})}),O=await R.json().catch(()=>({}));if(R.ok&&O.ok){u.textContent=`\u2713 ${a("botDefaults.cardPrefSaved")}`,u.classList.add("hint-ok");let z=he.bots.find(N=>N.larkAppId===g);z&&(z.p2pMode=O.p2pMode==="chat"?"chat":"thread")}else u.textContent=`\u2717 ${O.error??R.status}`,u.classList.add("hint-warn-inline")}catch(R){u.textContent=`\u2717 ${R?.message??R}`,u.classList.add("hint-warn-inline")}finally{X.disabled=!1}});let b=d.querySelector("select[data-input=regularGroupMode]"),I=d.querySelector("[data-regular-group-status]");b&&b.addEventListener("change",()=>{ee({regularGroupReplyMode:b.value},b,I)});let L=d.querySelector("select[data-input=regularGroupMentionMode]"),M=d.querySelector("[data-mention-mode-status]");L&&L.addEventListener("change",()=>{ee({regularGroupMentionMode:L.value},L,M)});let B=d.querySelector("textarea[data-input=teamRole]"),P=d.querySelector("button[data-action=save-role]"),Z=d.querySelector("button[data-action=delete-role]"),_=d.querySelector("[data-role-status]");if(B&&P&&Z&&_){let O=function(N){let F=t.querySelector(`.bd-card[data-appid="${CSS.escape(g)}"]`);if(!F)return;let ie=F.querySelector("textarea[data-input=teamRole]"),ge=F.querySelector("button[data-action=save-role]"),Qe=F.querySelector("button[data-action=delete-role]");ie&&(ie.value=N,ie.disabled=!1),ge&&(ge.disabled=!1),Qe&&(Qe.disabled=!1)};var j=O;let A=`/api/team/local-bots/${encodeURIComponent(g)}/role`,R=he.bots.find(N=>N.larkAppId===g);R&&typeof R.teamRole!="string"&&!R.teamRoleLoading&&(R.teamRoleLoading=!0,(async()=>{try{let N=await fetch(A),F=await N.json().catch(()=>({}));N.ok&&F.ok?(R.teamRole=F.role??"",O(R.teamRole)):(_.textContent=`\u2717 ${a("botDefaults.roleLoadErr")}: ${F.error??N.status}`,_.classList.add("hint-warn-inline"))}catch(N){_.textContent=`\u2717 ${a("botDefaults.roleLoadErr")}: ${N?.message??N}`,_.classList.add("hint-warn-inline")}finally{R.teamRoleLoading=!1}})());async function z(N,F,ie){if(_&&!(!R||typeof R.teamRole!="string")){_.textContent="",_.className="oncall-status",P.disabled=!0,Z.disabled=!0;try{let ge=await fetch(A,{method:"PUT",headers:{"content-type":"application/json"},body:JSON.stringify({role:N})}),Qe=await ge.json().catch(()=>({}));ge.ok&&Qe.ok?(R&&(R.teamRole=N.trim()),_.textContent=`\u2713 ${ie?a("botDefaults.roleDeleted"):a("botDefaults.roleSaved")}`,_.classList.add("hint-ok")):(_.textContent=`\u2717 ${Qe.error??ge.status}`,_.classList.add("hint-warn-inline"))}catch(ge){_.textContent=`\u2717 ${ge?.message??ge}`,_.classList.add("hint-warn-inline")}finally{P.disabled=!1,Z.disabled=!1}}}P.addEventListener("click",()=>z(B.value,P,!1)),Z.addEventListener("click",()=>{B.value="",z("",Z,!0)})}let _e=d.querySelector("input[data-action=toggle-restrict-grant]"),Ae=d.querySelector("input[data-input=quotaLimit]"),ae=d.querySelector("button[data-action=save-quota]"),qe=d.querySelector("button[data-action=off-quota]"),oe=d.querySelector("[data-grant-status]"),S=d.querySelector("[data-quota-state]");async function Y(A,R){if(oe){oe.textContent="",oe.className="oncall-status",R.disabled=!0;try{let O=await fetch(`/api/bots/${encodeURIComponent(g)}/grant-prefs`,{method:"PUT",headers:{"content-type":"application/json"},body:JSON.stringify(A)}),z=await O.json().catch(()=>({}));if(O.ok&&z.ok){oe.textContent=`\u2713 ${a("botDefaults.cardPrefSaved")}`,oe.classList.add("hint-ok");let N=typeof z.messageQuotaDefaultLimit=="number"?z.messageQuotaDefaultLimit:null,F=he.bots.find(ie=>ie.larkAppId===g);F&&(F.restrictGrantCommands=z.restrictGrantCommands===!0,F.messageQuotaDefaultLimit=N),S&&(S.textContent=T(N)),Ae&&"messageQuotaDefaultLimit"in A&&(Ae.value=N==null?"":String(N))}else oe.textContent=`\u2717 ${z.error??O.status}`,oe.classList.add("hint-warn-inline")}catch(O){oe.textContent=`\u2717 ${O?.message??O}`,oe.classList.add("hint-warn-inline")}finally{R.disabled=!1}}}_e&&_e.addEventListener("change",()=>{Y({restrictGrantCommands:_e.checked},_e)}),Ae&&ae&&ae.addEventListener("click",()=>{let A=Ae.value.trim();if(A===""){Y({messageQuotaDefaultLimit:null},ae);return}if(!/^[1-9]\d*$/.test(A)){oe&&(oe.textContent=`\u2717 ${a("botDefaults.quotaInvalid")}`,oe.className="oncall-status hint-warn-inline");return}Y({messageQuotaDefaultLimit:Number(A)},ae)}),Ae&&qe&&qe.addEventListener("click",()=>{Ae.value="",Y({messageQuotaDefaultLimit:null},qe)})})}i(),ve().then(i),s.addEventListener("input",i)}var Gt=4096,yt=[],$e=null,Se=null,fe="",Ye=new Set;function Ho(){return`<section class="page roles-page">
|
|
590
639
|
<div class="page-heading">
|
|
591
640
|
<div>
|
|
592
|
-
<p class="eyebrow">${
|
|
593
|
-
<h1>${
|
|
594
|
-
<p>${
|
|
641
|
+
<p class="eyebrow">${a("nav.roles")}</p>
|
|
642
|
+
<h1>${a("roles.title")}</h1>
|
|
643
|
+
<p>${a("roles.subtitle")}</p>
|
|
595
644
|
</div>
|
|
596
645
|
</div>
|
|
597
646
|
<div class="roles-layout">
|
|
598
647
|
<div class="roles-tree-panel">
|
|
599
648
|
<div class="roles-tree-header">
|
|
600
|
-
<input type="search" id="roles-search" placeholder="${
|
|
601
|
-
<button type="button" id="roles-refresh">${
|
|
649
|
+
<input type="search" id="roles-search" placeholder="${a("roles.search")}" />
|
|
650
|
+
<button type="button" id="roles-refresh">${a("roles.refresh")}</button>
|
|
602
651
|
</div>
|
|
603
652
|
<div id="roles-tree" class="roles-tree"></div>
|
|
604
653
|
</div>
|
|
605
654
|
<div class="roles-editor-panel">
|
|
606
|
-
<div id="roles-editor-empty" class="roles-editor-empty">${
|
|
655
|
+
<div id="roles-editor-empty" class="roles-editor-empty">${a("roles.selectHint")}</div>
|
|
607
656
|
<div id="roles-editor-form" class="roles-editor-form" style="display:none">
|
|
608
657
|
<div class="roles-editor-breadcrumb">
|
|
609
658
|
<span id="roles-editor-group-name"></span>
|
|
@@ -613,52 +662,52 @@ ${d.join(`
|
|
|
613
662
|
<div class="roles-editor-meta">
|
|
614
663
|
<span id="roles-editor-chat-id" class="roles-editor-meta-line"></span>
|
|
615
664
|
</div>
|
|
616
|
-
<textarea id="roles-editor-textarea" placeholder="${
|
|
665
|
+
<textarea id="roles-editor-textarea" placeholder="${a("roles.editorPlaceholder")}" rows="14"></textarea>
|
|
617
666
|
<div class="roles-editor-footer">
|
|
618
667
|
<span id="roles-editor-bytecount" class="roles-bytecount"></span>
|
|
619
668
|
<div class="roles-editor-actions">
|
|
620
|
-
<button type="button" id="roles-delete" class="danger">${
|
|
621
|
-
<button type="button" id="roles-save" class="primary">${
|
|
669
|
+
<button type="button" id="roles-delete" class="danger">${a("roles.delete")}</button>
|
|
670
|
+
<button type="button" id="roles-save" class="primary">${a("roles.save")}</button>
|
|
622
671
|
</div>
|
|
623
672
|
</div>
|
|
624
673
|
<div id="roles-preview" class="roles-preview"></div>
|
|
625
674
|
</div>
|
|
626
675
|
</div>
|
|
627
676
|
</div>
|
|
628
|
-
</section>`}async function
|
|
629
|
-
<div class="roles-bot-row ${
|
|
630
|
-
data-group-id="${r(
|
|
631
|
-
data-bot-id="${r(
|
|
677
|
+
</section>`}async function wt(){yt=((await(await fetch("/api/groups")).json()).chats??[]).map(n=>({chatId:n.chatId,name:n.name??n.chatId,memberBots:(n.memberBots??[]).map(s=>({larkAppId:s.larkAppId,botName:s.botName??s.larkAppId,inChat:s.inChat??!1,hasRole:s.hasRole??!1,oncallChat:s.oncallChat??null}))}))}async function Zn(e,t){return(await fetch(`/api/roles/${encodeURIComponent(e)}/${encodeURIComponent(t)}`)).json()}async function Ao(e,t,n){return(await fetch(`/api/roles/${encodeURIComponent(e)}/${encodeURIComponent(t)}`,{method:"PUT",headers:{"content-type":"application/json"},body:JSON.stringify({content:n})})).ok}async function Co(e,t){return(await fetch(`/api/roles/${encodeURIComponent(e)}/${encodeURIComponent(t)}`,{method:"DELETE"})).ok}function ea(e){return e.memberBots.filter(t=>t.inChat&&t.hasRole).length}function Do(e){return e.memberBots.filter(t=>t.inChat).length}function Be(e=""){let t=document.getElementById("roles-tree");if(!t)return;let n=e.toLowerCase(),s=yt.filter(o=>{if(!n)return!0;let i=o.chatId.toLowerCase().includes(n)||(o.name??"").toLowerCase().includes(n),l=o.memberBots.some(c=>c.larkAppId.toLowerCase().includes(n)||(c.botName??"").toLowerCase().includes(n));return i||l});if(s.length===0){t.innerHTML=`<div class="roles-empty">${a("roles.noChats")}</div>`;return}t.innerHTML=s.map(o=>{let i=Ye.has(o.chatId),l=o.memberBots.filter(v=>v.inChat),c=i?"\u25BE":"\u25B8",w=ea(o),h=Do(o),y=i?l.map(v=>`
|
|
678
|
+
<div class="roles-bot-row ${$e===o.chatId&&Se===v.larkAppId?"selected":""}"
|
|
679
|
+
data-group-id="${r(o.chatId)}"
|
|
680
|
+
data-bot-id="${r(v.larkAppId)}">
|
|
632
681
|
<span class="roles-bot-indent"></span>
|
|
633
|
-
|
|
682
|
+
${me({name:v.botName,larkAppId:v.larkAppId,size:"sm"})}
|
|
634
683
|
<div class="roles-bot-info">
|
|
635
|
-
<div class="roles-bot-name">${r(
|
|
636
|
-
<div class="roles-bot-id">${r(
|
|
684
|
+
<div class="roles-bot-name">${r(v.botName)}</div>
|
|
685
|
+
<div class="roles-bot-id">${r(v.larkAppId)}</div>
|
|
637
686
|
</div>
|
|
638
|
-
<span class="roles-badge ${
|
|
639
|
-
${
|
|
687
|
+
<span class="roles-badge ${v.hasRole?"has-role":"no-role"}">
|
|
688
|
+
${v.hasRole?a("roles.configured"):a("roles.unconfigured")}
|
|
640
689
|
</span>
|
|
641
690
|
</div>`).join(""):"";return`
|
|
642
691
|
<div class="roles-group-section">
|
|
643
|
-
<div class="roles-group-row ${i?"expanded":""} ${
|
|
644
|
-
data-group-id="${r(
|
|
645
|
-
<span class="roles-group-arrow">${
|
|
692
|
+
<div class="roles-group-row ${i?"expanded":""} ${$e===o.chatId&&!Se?"selected":""}"
|
|
693
|
+
data-group-id="${r(o.chatId)}">
|
|
694
|
+
<span class="roles-group-arrow">${c}</span>
|
|
646
695
|
<span class="roles-group-icon" aria-hidden="true"><svg viewBox="0 0 16 16"><circle cx="5.6" cy="5.8" r="2.4"/><path d="M1.8 13.2c.5-2.4 2-3.6 3.8-3.6s3.3 1.2 3.8 3.6"/><circle cx="11" cy="6.8" r="1.9"/><path d="M9.8 12.6c.4-1.7 1.5-2.6 2.8-2.6 1 0 1.9.5 2.4 1.6"/></svg></span>
|
|
647
696
|
<div class="roles-group-info">
|
|
648
|
-
<div class="roles-group-name">${r(
|
|
697
|
+
<div class="roles-group-name">${r(o.name??o.chatId)}</div>
|
|
649
698
|
<div class="roles-group-meta">
|
|
650
|
-
${
|
|
699
|
+
${w}/${h} ${a("roles.botsWithRoles")}
|
|
651
700
|
</div>
|
|
652
701
|
</div>
|
|
653
702
|
<span class="roles-group-chevron"></span>
|
|
654
703
|
</div>
|
|
655
|
-
<div class="roles-bot-list">${
|
|
656
|
-
</div>`}).join(""),t.querySelectorAll(".roles-group-row").forEach(
|
|
704
|
+
<div class="roles-bot-list">${y}</div>
|
|
705
|
+
</div>`}).join(""),t.querySelectorAll(".roles-group-row").forEach(o=>{o.addEventListener("click",()=>{let i=o.dataset.groupId;i&&(Ye.has(i)?Ye.delete(i):Ye.add(i),Be(document.getElementById("roles-search")?.value??""))})}),t.querySelectorAll(".roles-bot-row").forEach(o=>{o.addEventListener("click",i=>{i.stopPropagation();let l=o.dataset.groupId,c=o.dataset.botId;l&&c&&Ro(l,c)})})}async function Ro(e,t){$e=e,Se=t;let n=await Zn(t,e),s=document.getElementById("roles-editor-empty"),o=document.getElementById("roles-editor-form"),i=document.getElementById("roles-editor-textarea"),l=document.getElementById("roles-editor-group-name"),c=document.getElementById("roles-editor-bot-name"),w=document.getElementById("roles-editor-chat-id");s&&(s.style.display="none"),o&&(o.style.display="");let h=yt.find(p=>p.chatId===e),y=h?.memberBots.find(p=>p.larkAppId===t);l&&(l.textContent=h?.name??e),c&&(c.textContent=y?.botName??t),w&&(w.textContent=`${e} \xB7 ${t}`),fe=n.content??"",i&&(i.value=fe,i.focus()),Wt(),Jt(),Be(document.getElementById("roles-search")?.value??"");let v=document.getElementById("roles-delete");v&&(v.style.display=n.hasRole?"":"none")}function Wt(){let e=document.getElementById("roles-editor-bytecount");if(!e)return;let t=new TextEncoder().encode(fe).length;e.textContent=`${t} / ${Gt} bytes`,e.className=`roles-bytecount ${t>3800?"warn":""} ${t>Gt?"over":""}`,Oo(t)}function Oo(e){let t=document.getElementById("roles-save");if(!t)return;let n=e??new TextEncoder().encode(fe).length;t.disabled=n>Gt||fe.trim().length===0}function Jt(){let e=document.getElementById("roles-preview");e&&(fe.trim()?e.innerHTML=`<strong>${a("roles.preview")}</strong><pre>${r(fe)}</pre>`:e.innerHTML=`<small>${a("roles.previewEmpty")}</small>`)}function Xn(){$e=null,Se=null,fe="";let e=document.getElementById("roles-editor-empty"),t=document.getElementById("roles-editor-form"),n=document.getElementById("roles-editor-textarea"),s=document.getElementById("roles-delete");e&&(e.style.display=""),t&&(t.style.display="none"),n&&(n.value=""),s&&(s.style.display="none")}async function ta(e){e.innerHTML=Ho(),Ye.clear(),Xn(),await wt(),await ve();for(let t of yt)ea(t)>0&&Ye.add(t.chatId);Be(),document.getElementById("roles-search")?.addEventListener("input",t=>{Be(t.target.value)}),document.getElementById("roles-refresh")?.addEventListener("click",async()=>{if(await wt(),Be(document.getElementById("roles-search")?.value??""),$e&&Se){let t=await Zn(Se,$e),n=document.getElementById("roles-editor-textarea");n&&(n.value=t.content??""),fe=t.content??"",Wt(),Jt();let s=document.getElementById("roles-delete");s&&(s.style.display=t.hasRole?"":"none")}}),document.getElementById("roles-save")?.addEventListener("click",async function(){if(!(!$e||!Se)){this.disabled=!0,this.textContent="...";try{if(await Ao(Se,$e,fe)){await wt(),Be(document.getElementById("roles-search")?.value??"");let n=document.getElementById("roles-delete");n&&(n.style.display="");let s=document.createElement("span");s.className="roles-saved-flash",s.textContent=` ${a("roles.saved")}`,document.querySelector(".roles-editor-footer")?.appendChild(s),setTimeout(()=>s.remove(),2e3)}else{let n=document.createElement("span");n.className="roles-saved-flash roles-save-error",n.textContent=fe.trim().length===0?` ${a("roles.emptyError")}`:` ${a("roles.saveFailed")}`,document.querySelector(".roles-editor-footer")?.appendChild(n),setTimeout(()=>n.remove(),3e3)}}finally{this.disabled=!1,this.textContent=a("roles.save")}}}),document.getElementById("roles-delete")?.addEventListener("click",async function(){if(!(!$e||!Se)&&confirm(a("roles.confirmDelete"))){this.disabled=!0,this.textContent="...";try{await Co(Se,$e)&&(await wt(),Xn(),Be(document.getElementById("roles-search")?.value??""))}finally{this.disabled=!1,this.textContent=a("roles.delete")}}}),document.getElementById("roles-editor-textarea")?.addEventListener("input",t=>{fe=t.target.value,Wt(),Jt()})}async function $t(e){let t=await fetch(e);return{status:t.status,body:await t.json().catch(()=>({}))}}async function St(e,t,n){let s=await fetch(t,{method:e,headers:{"content-type":"application/json"},body:n?JSON.stringify(n):void 0});return{status:s.status,body:await s.json().catch(()=>({}))}}var Pe=(e,t)=>St("POST",e,t),qo=(e,t)=>St("PUT",e,t),Kt=[],na=[],aa="",vt="",Vt=new Map,kt=new Map,tt=new Set,nt=new Set;function W(e){return document.getElementById(e)}function It(){return[...Kt,...na]}function Ne(e){let t=Vt.get(e);return t||(t=new Set,Vt.set(e,t)),t}function Bo(e){return It().find(t=>t.key===e)}function oa(e){let t=(n,s,o)=>`<a href="${n}" style="padding:6px 14px;border-radius:8px;text-decoration:none;font-size:14px;font-weight:600;${o?"background:var(--accent);color:var(--on-accent)":"color:var(--muted);background:var(--surface-muted)"}">${s}</a>`;return`<div style="display:flex;gap:8px;margin-bottom:14px">${t("#/team","\u6211\u7684\u56E2\u961F",e==="home")}${t("#/team/manage","\u56E2\u961F\u7BA1\u7406",e==="manage")}</div>`}function No(){return`<section class="page">
|
|
657
706
|
<div class="page-heading"><div>
|
|
658
707
|
<p class="eyebrow">\u56E2\u961F</p><h1>\u56E2\u961F\u534F\u4F5C\uFF08\u8DE8\u90E8\u7F72\uFF09</h1>
|
|
659
708
|
<p class="tf-lede">\u628A\u522B\u7684\u90E8\u7F72\uFF08\u540C\u4E8B\u81EA\u5DF1\u8DD1\u7684 botmux\uFF09\u9080\u8BF7\u8FDB\u540C\u4E00\u4E2A\u56E2\u961F\uFF0C\u4E92\u76F8\u53D1\u73B0\u673A\u5668\u4EBA\u3001\u534F\u4F5C\u62C9\u7FA4\u3002</p>
|
|
660
709
|
</div></div>
|
|
661
|
-
${
|
|
710
|
+
${oa("home")}
|
|
662
711
|
<div class="card" style="margin-bottom:16px">
|
|
663
712
|
<h2 style="margin-top:0">\u672C\u90E8\u7F72</h2>
|
|
664
713
|
<p>\u6211\u7684\u98DE\u4E66\u8EAB\u4EFD\uFF1A<b id="tf-owner">\u672A\u7ED1\u5B9A</b>
|
|
@@ -687,12 +736,12 @@ ${Gn("home")}
|
|
|
687
736
|
</div>
|
|
688
737
|
</div>
|
|
689
738
|
</div>
|
|
690
|
-
</section>`}function $a(e){let t=(F("tf-search").value||"").trim().toLowerCase();if(t&&!((e.name||"")+" "+(e.cliId||"")+" "+(e.capability||"")).toLowerCase().includes(t))return!1;let n=F("tf-cli").value;return!(n&&e.cliId!==n||F("tf-fcap").checked&&!e.capability||F("tf-frole").checked&&!e.hasTeamRole)}function Sa(e,t){let n=[...e.deployments].sort((a,i)=>a.local===i.local?0:a.local?-1:1),s="";for(let a of n){let i=t.filter(u=>u.deployment.id===a.id);if(!i.length)continue;let l=a.id===Wn,p=l?"\u672C\u90E8\u7F72":a.stale?"\u8FDC\u7AEF\xB7\u79BB\u7EBF\uFF1F":"\u8FDC\u7AEF",v=e.kind==="local"&&!l?` <button class="tf-rmmember ghost" data-team="${r(e.teamId)}" data-dep="${r(a.id)}" data-name="${r(a.name)}" style="font-size:12px">\u79FB\u9664</button>`:"",y=`${e.key}::${a.id}`,h=Ke.has(y),k=i.filter(u=>Ce(e.key).has(u.larkAppId)).length;if(s+=`<div class="tf-dep-h" data-dk="${r(y)}" style="cursor:pointer;margin:10px 0 2px"><b>${h?"\u25BE":"\u25B8"} ${r(a.name)}</b> <span class="muted" style="font-size:12px">\uFF08${p}\uFF09\xB7 ${i.length} \u4E2A${k?`\uFF0C\u5DF2\u9009 ${k}`:""}</span>${v}</div>`,!!h){s+='<table style="width:100%;border-collapse:collapse;font-size:14px"><tbody>';for(let u of i){let E=r(u.larkAppId),B=Ce(e.key).has(u.larkAppId)?" checked":"",$=u.deployment.stale?"opacity:.55":"",d=l?`<input class="tf-cap" data-app="${E}" value="${r(u.capability||"")}" placeholder="\u80FD\u529B\u6807\u7B7E\u2026" style="width:92%;padding:3px 6px">`:u.capability?r(u.capability):'<span class="muted">\u2014</span>',f=u.hasTeamRole?l?`<button class="tf-role" data-app="${E}" data-name="${r(u.name)}">\u67E5\u770B</button>`:"\u6709\u89D2\u8272":'<span class="muted">\u2014</span>';s+=`<tr style="${$}"><td style="padding:4px 8px"><input type="checkbox" class="tf-pick" data-tk="${r(e.key)}" data-app="${E}"${B}></td><td style="padding:4px 8px">${r(u.name)}</td><td style="padding:4px 8px" class="muted">${r(u.cliId)}</td><td style="padding:4px 8px">${d}</td><td style="padding:4px 8px">${f}</td></tr>`}s+="</tbody></table>"}}return s||(s='<p class="muted" style="margin:8px 0 0">\u6CA1\u6709\u7B26\u5408\u6761\u4EF6\u7684\u673A\u5668\u4EBA\u3002</p>'),s+=`<div style="margin-top:12px;display:flex;gap:8px;flex-wrap:wrap;align-items:center"><input class="tf-gname" data-tk="${r(e.key)}" value="${r(dt.get(e.key)||"")}" placeholder="\u7FA4\u540D\uFF08\u5982\uFF1A\u8DE8\u56E2\u961F\u6392\u969C\uFF09" style="min-width:200px"><button class="tf-grp primary" data-tk="${r(e.key)}">\u628A\u52FE\u9009\u7684\u673A\u5668\u4EBA\u62C9\u4E00\u4E2A\u7FA4</button><span class="muted" style="font-size:13px">\u52FE\u9009\u673A\u5668\u4EBA \u2192 \u62C9\u5230\u4E00\u4E2A\u98DE\u4E66\u7FA4\uFF08\u81EA\u52A8\u542B owner\uFF09</span><span class="tf-gout" data-tk="${r(e.key)}" style="font-size:13px;display:block;flex-basis:100%"></span></div>`,s}function Le(){let e=F("tf-teams"),t=pt();if(!t.length){e.innerHTML='<p class="muted">\u8FD8\u6CA1\u6709\u56E2\u961F\u3002\u53BB\u300C\u56E2\u961F\u7BA1\u7406\u300D\u751F\u6210\u9080\u8BF7\u7801\u8BA9\u522B\u4EBA\u52A0\u5165\u4F60\uFF0C\u6216\u52A0\u5165\u522B\u4EBA\u7684\u56E2\u961F\u3002</p>',F("tf-count").textContent="";return}let n="",s=new Set,a=new Set;for(let l of t){let p=l.bots.filter($a);p.forEach(k=>s.add(k.larkAppId)),l.bots.forEach(k=>a.add(k.larkAppId));let v=new Set(p.map(k=>k.larkAppId));[...Ce(l.key)].forEach(k=>{v.has(k)||Ce(l.key).delete(k)});let y=!Je.has(l.key),h=l.kind==="remote"?l.ok?' <span class="ok" style="font-size:12px">\u5DF2\u8FDE\u63A5</span>':` <span class="err" style="font-size:12px">\u8FDE\u63A5\u5931\u8D25\uFF1A${r(l.error||"")}</span>`:' <span class="muted" style="font-size:12px">\u6211\u6258\u7BA1</span>';n+=`<div class="card" style="margin:0 0 12px;padding:12px 14px;background:var(--bg-soft,#f6f7f9)"><div class="tf-team-h" data-tk="${r(l.key)}" style="cursor:pointer;display:flex;align-items:center;gap:8px;flex-wrap:wrap"><b style="font-size:15px">${y?"\u25B8":"\u25BE"} ${r(l.label)}</b>`+(l.sub?` <span class="muted" style="font-size:12px">${r(l.sub)}</span>`:"")+h+` <span class="muted" style="font-size:12px">\xB7 ${l.deployments.length} \u4E2A\u90E8\u7F72 \xB7 ${l.bots.length} \u4E2A\u673A\u5668\u4EBA</span></div>`,y||(n+=l.kind==="remote"&&!l.ok?'<p class="muted" style="margin:8px 0 0">\u65E0\u6CD5\u83B7\u53D6\u8BE5\u56E2\u961F\u82B1\u540D\u518C\u3002</p>':Sa(l,p)),n+="</div>"}e.innerHTML=n;let i=t.length>1?`\uFF08\u8DE8 ${t.length} \u4E2A\u56E2\u961F\uFF0C\u53BB\u91CD\uFF09`:"";F("tf-count").textContent=`\xB7 ${s.size===a.size?`${a.size}`:`${s.size} / ${a.size}`} \u4E2A\u673A\u5668\u4EBA${i}`,Ta()}function Ta(){let e=F("tf-teams");e.querySelectorAll(".tf-team-h").forEach(t=>{t.onclick=()=>{let n=t.dataset.tk;Je.has(n)?Je.delete(n):Je.add(n),Le()}}),e.querySelectorAll(".tf-dep-h").forEach(t=>{t.onclick=()=>{let n=t.dataset.dk;Ke.has(n)?Ke.delete(n):Ke.add(n),Le()}}),e.querySelectorAll(".tf-pick").forEach(t=>{t.onchange=()=>{let n=Ce(t.dataset.tk);t.checked?n.add(t.dataset.app):n.delete(t.dataset.app)}}),e.querySelectorAll(".tf-gname").forEach(t=>{t.oninput=()=>{dt.set(t.dataset.tk,t.value)}}),e.querySelectorAll(".tf-cap").forEach(t=>{t.onchange=async()=>{let n=t.dataset.app,s=t.value;await ya("/api/team/local-bots/"+encodeURIComponent(n)+"/capability",{capability:s}),pt().forEach(a=>{let i=a.bots.find(l=>l.larkAppId===n);i&&(i.capability=s.trim()||null)})}}),e.querySelectorAll(".tf-role").forEach(t=>{t.onclick=()=>La(t.dataset.app,t.dataset.name||"")}),e.querySelectorAll(".tf-rmmember").forEach(t=>{t.onclick=async n=>{n.stopPropagation(),confirm(`\u628A\u300C${t.dataset.name}\u300D\u79FB\u51FA\u8FD9\u4E2A\u56E2\u961F\uFF1F\u5B83\u7684\u673A\u5668\u4EBA\u5C06\u4ECE\u672C\u56E2\u961F\u82B1\u540D\u518C\u6D88\u5931\uFF08\u4E0D\u5F71\u54CD\u5BF9\u65B9\u81EA\u5DF1\u7684\u90E8\u7F72\uFF09\u3002`)&&(await ut("DELETE",`/api/team/hosted/${encodeURIComponent(t.dataset.team)}/members/${encodeURIComponent(t.dataset.dep)}`),Ve())}}),e.querySelectorAll(".tf-grp").forEach(t=>{t.onclick=async()=>{let n=t.dataset.tk,s=va(n);if(!s)return;let a=[...Ce(n)],i=e.querySelector(`.tf-gout[data-tk="${CSS.escape(n)}"]`);if(!a.length){i.innerHTML='<span class="err">\u8BF7\u5148\u52FE\u9009\u81F3\u5C11\u4E00\u4E2A\u673A\u5668\u4EBA</span>';return}let l=(e.querySelector(`.tf-gname[data-tk="${CSS.escape(n)}"]`)?.value||"").trim()||"\u534F\u4F5C\u7FA4";i.innerHTML='<span class="muted">\u5EFA\u7FA4\u4E2D\u2026</span>';let p=s.kind==="local"?await Ae("/api/team/federated-group",{name:l,larkAppIds:a,teamId:s.teamId}):await Ae("/api/team/remote-group",{hubUrl:s.hubUrl,teamId:s.teamId,name:l,larkAppIds:a});if(Ia(i,p.body,p.status),p.body?.ok){Ce(n).clear(),dt.delete(n);let v=i.innerHTML,y=()=>{let h=e.querySelector(`.tf-gout[data-tk="${CSS.escape(n)}"]`);h&&(h.innerHTML=v)};s.kind==="local"?Ve().then(y):(Le(),y())}}})}function Ia(e,t,n){if(t?.ok&&t.chatId){let s=t.shareLink||"https://applink.feishu.cn/client/chat/open?openChatId="+encodeURIComponent(t.chatId),a=(t.invalidBotIds||[]).length?`<span class="err"> \xB7 \u672A\u52A0\u5165\u7684\u673A\u5668\u4EBA\uFF1A${r((t.invalidBotIds||[]).join(", "))}</span>`:"",i=(t.invalidOwnerUnionIds||[]).length?`<span class="err"> \xB7 ${(t.invalidOwnerUnionIds||[]).length} \u4E2A owner \u672A\u80FD\u62C9\u8FDB</span>`:"",l=t.missingOperatorIdentity?'<span class="err"> \xB7 \u4F60\u672A\u7ED1\u5B9A\u98DE\u4E66\u8EAB\u4EFD\uFF0C\u6CA1\u628A\u4F60\u81EA\u5DF1\u62C9\u8FDB\u7FA4\uFF08\u53BB\u300C\u6211\u7684\u56E2\u961F\u300D\u7ED1\u5B9A\uFF09</span>':"",p=(t.skippedNoOwner||[]).length?`<span class="err"> \xB7 ${(t.skippedNoOwner||[]).length} \u4E2A\u673A\u5668\u4EBA\u56E0\u8D1F\u8D23\u4EBA\u672A\u7ED1\u5B9A\u8EAB\u4EFD\u88AB\u8DF3\u8FC7\uFF08\u672A\u52A0\u5165\u7FA4\uFF09</span>`:"",v=t.delegatedTo?`\uFF08\u7531\u300C${r(t.delegatedTo)}\u300D\u5EFA\u7FA4\uFF09`:"";e.innerHTML=`<span class="ok">\u7FA4\u5DF2\u521B\u5EFA</span>${v} \xB7 <a href="${r(s)}" target="_blank">\u5728\u98DE\u4E66\u6253\u5F00</a>${a}${i}${l}${p}`}else{let s=t?.error||n,a=s==="no_local_online_bot"?"\u8BF7\u81F3\u5C11\u52FE\u9009\u4E00\u4E2A\u4F60\u81EA\u5DF1\uFF08\u672C\u90E8\u7F72\uFF09\u7684\u5728\u7EBF\u673A\u5668\u4EBA\u2014\u2014\u7FA4\u8981\u7531\u5B83\u521B\u5EFA\u5E76\u628A\u4F60\uFF08\u53D1\u8D77\u4EBA\uFF09\u62C9\u8FDB\u7FA4\u3002":s==="all_bots_skipped_no_owner"?"\u6240\u9009\u673A\u5668\u4EBA\u7684\u8D1F\u8D23\u4EBA\u90FD\u6CA1\u7ED1\u5B9A\u8EAB\u4EFD\uFF0C\u65E0\u6CD5\u62C9\u7FA4\uFF08\u673A\u5668\u4EBA\u4E0D\u80FD\u8FDB\u4E00\u4E2A owner \u4E0D\u5728\u7684\u7FA4\uFF09\u3002\u8BF7\u8BA9\u5BF9\u5E94\u90E8\u7F72\u5148\u7ED1\u5B9A\u8EAB\u4EFD\u3002":s==="no_creator_available"?"\u6CA1\u6709\u53EF\u7528\u7684\u5EFA\u7FA4\u53D1\u8D77\u65B9\uFF08\u76F8\u5173\u90E8\u7F72\u90FD\u6CA1\u6709\u5728\u7EBF\u673A\u5668\u4EBA\uFF0C\u6216\u4E0D\u53EF\u8FBE\uFF09":s==="delegation_timeout"?"\u59D4\u6258\u5BF9\u65B9\u90E8\u7F72\u5EFA\u7FA4\u8D85\u65F6\uFF08\u53EF\u80FD\u5DF2\u5EFA\uFF0C\u53BB\u98DE\u4E66\u786E\u8BA4\uFF0C\u52FF\u91CD\u590D\u70B9\uFF09":`\u5EFA\u7FA4\u5931\u8D25\uFF1A${s}`;e.innerHTML=`<span class="err">${r(String(a))}</span>`}}async function La(e,t){let n=await ct("/api/team/local-bots/"+encodeURIComponent(e)+"/role");F("tf-modal-title").textContent="\u9ED8\u8BA4\u89D2\u8272 \xB7 "+t,F("tf-modal-text").value=n.body?.role||"",F("tf-modal").dataset.app=e,F("tf-modal").style.display="flex"}function Jn(){let e=Array.from(new Set(pt().flatMap(s=>s.bots.map(a=>a.cliId)).filter(Boolean))).sort(),t=F("tf-cli"),n=t.value;t.innerHTML='<option value="">\u5168\u90E8 CLI</option>'+e.map(s=>`<option value="${r(s)}">${r(s)}</option>`).join(""),t.value=n}async function Ve(){let t=(await ct("/api/team/hosted")).body;if(!t?.ok){Bt=[],Le();return}Wn=t.deployment.deploymentId,lt=t.suggestedHubUrl||"",F("tf-owner").textContent=t.deployment.ownerName||(t.deployment.ownerUnionId?"\u5DF2\u7ED1\u5B9A":"\u672A\u7ED1\u5B9A"),Bt=(t.teams||[]).map(n=>({kind:"local",key:`local:${n.teamId}`,teamId:n.teamId,label:n.isDefault?"\u6211\u6258\u7BA1\u7684\u56E2\u961F":n.name,sub:"",ok:!0,deployments:n.deployments||[],bots:n.bots||[]})),Jn(),Le()}async function Ea(){Fn=((await ct("/api/team/remote-roster")).body?.memberships||[]).map(n=>{let s=n.roster?.deployments||[],a=s.find(l=>l.local),i=a?.name?`${a.name} \u7684\u56E2\u961F`:n.teamName||n.teamId;return{kind:"remote",key:`${n.hubUrl}::${n.teamId}`,teamId:n.teamId,label:i,sub:n.hubUrl,ok:!!n.ok,error:n.error,hubUrl:n.hubUrl,deployments:s,bots:n.roster?.bots||[]}}),Jn(),Le()}function Kn(e){e.innerHTML=ka(),Pt.clear(),dt.clear(),Je.clear(),Ke.clear(),["tf-search","tf-cli","tf-fcap","tf-frole"].forEach(t=>{let n=F(t);n.oninput=Le,n.onchange=Le}),F("tf-modal-cancel").onclick=()=>{F("tf-modal").style.display="none"},Ma(),Ve(),Ea()}function xa(){return`<section class="page">
|
|
739
|
+
</section>`}function Po(e){let t=(W("tf-search").value||"").trim().toLowerCase();if(t&&!((e.name||"")+" "+(e.cliId||"")+" "+(e.capability||"")).toLowerCase().includes(t))return!1;let n=W("tf-cli").value;return!(n&&e.cliId!==n||W("tf-fcap").checked&&!e.capability||W("tf-frole").checked&&!e.hasTeamRole)}function Uo(e,t){let n=[...e.deployments].sort((o,i)=>o.local===i.local?0:o.local?-1:1),s="";for(let o of n){let i=t.filter(p=>p.deployment.id===o.id);if(!i.length)continue;let l=o.id===aa,c=l?"\u672C\u90E8\u7F72":o.stale?"\u8FDC\u7AEF\xB7\u79BB\u7EBF\uFF1F":"\u8FDC\u7AEF",w=e.kind==="local"&&!l?` <button class="tf-rmmember ghost" data-team="${r(e.teamId)}" data-dep="${r(o.id)}" data-name="${r(o.name)}" style="font-size:12px">\u79FB\u9664</button>`:"",h=`${e.key}::${o.id}`,y=nt.has(h),v=i.filter(p=>Ne(e.key).has(p.larkAppId)).length;if(s+=`<div class="tf-dep-h" data-dk="${r(h)}" style="cursor:pointer;margin:10px 0 2px"><b>${y?"\u25BE":"\u25B8"} ${r(o.name)}</b> <span class="muted" style="font-size:12px">\uFF08${c}\uFF09\xB7 ${i.length} \u4E2A${v?`\uFF0C\u5DF2\u9009 ${v}`:""}</span>${w}</div>`,!!y){s+='<table style="width:100%;border-collapse:collapse;font-size:14px"><tbody>';for(let p of i){let T=r(p.larkAppId),q=Ne(e.key).has(p.larkAppId)?" checked":"",$=p.deployment.stale?"opacity:.55":"",f=l?`<input class="tf-cap" data-app="${T}" value="${r(p.capability||"")}" placeholder="\u80FD\u529B\u6807\u7B7E\u2026" style="width:92%;padding:3px 6px">`:p.capability?r(p.capability):'<span class="muted">\u2014</span>',d=p.hasTeamRole?l?`<button class="tf-role" data-app="${T}" data-name="${r(p.name)}">\u67E5\u770B</button>`:"\u6709\u89D2\u8272":'<span class="muted">\u2014</span>';s+=`<tr style="${$}"><td style="padding:4px 8px"><input type="checkbox" class="tf-pick" data-tk="${r(e.key)}" data-app="${T}"${q}></td><td style="padding:4px 8px">${r(p.name)}</td><td style="padding:4px 8px" class="muted">${r(p.cliId)}</td><td style="padding:4px 8px">${f}</td><td style="padding:4px 8px">${d}</td></tr>`}s+="</tbody></table>"}}return s||(s='<p class="muted" style="margin:8px 0 0">\u6CA1\u6709\u7B26\u5408\u6761\u4EF6\u7684\u673A\u5668\u4EBA\u3002</p>'),s+=`<div style="margin-top:12px;display:flex;gap:8px;flex-wrap:wrap;align-items:center"><input class="tf-gname" data-tk="${r(e.key)}" value="${r(kt.get(e.key)||"")}" placeholder="\u7FA4\u540D\uFF08\u5982\uFF1A\u8DE8\u56E2\u961F\u6392\u969C\uFF09" style="min-width:200px"><button class="tf-grp primary" data-tk="${r(e.key)}">\u628A\u52FE\u9009\u7684\u673A\u5668\u4EBA\u62C9\u4E00\u4E2A\u7FA4</button><span class="muted" style="font-size:13px">\u52FE\u9009\u673A\u5668\u4EBA \u2192 \u62C9\u5230\u4E00\u4E2A\u98DE\u4E66\u7FA4\uFF08\u81EA\u52A8\u542B owner\uFF09</span><span class="tf-gout" data-tk="${r(e.key)}" style="font-size:13px;display:block;flex-basis:100%"></span></div>`,s}function Re(){let e=W("tf-teams"),t=It();if(!t.length){e.innerHTML='<p class="muted">\u8FD8\u6CA1\u6709\u56E2\u961F\u3002\u53BB\u300C\u56E2\u961F\u7BA1\u7406\u300D\u751F\u6210\u9080\u8BF7\u7801\u8BA9\u522B\u4EBA\u52A0\u5165\u4F60\uFF0C\u6216\u52A0\u5165\u522B\u4EBA\u7684\u56E2\u961F\u3002</p>',W("tf-count").textContent="";return}let n="",s=new Set,o=new Set;for(let l of t){let c=l.bots.filter(Po);c.forEach(v=>s.add(v.larkAppId)),l.bots.forEach(v=>o.add(v.larkAppId));let w=new Set(c.map(v=>v.larkAppId));[...Ne(l.key)].forEach(v=>{w.has(v)||Ne(l.key).delete(v)});let h=!tt.has(l.key),y=l.kind==="remote"?l.ok?' <span class="ok" style="font-size:12px">\u5DF2\u8FDE\u63A5</span>':` <span class="err" style="font-size:12px">\u8FDE\u63A5\u5931\u8D25\uFF1A${r(l.error||"")}</span>`:' <span class="muted" style="font-size:12px">\u6211\u6258\u7BA1</span>';n+=`<div class="card" style="margin:0 0 12px;padding:12px 14px;background:var(--bg-soft,#f6f7f9)"><div class="tf-team-h" data-tk="${r(l.key)}" style="cursor:pointer;display:flex;align-items:center;gap:8px;flex-wrap:wrap"><b style="font-size:15px">${h?"\u25B8":"\u25BE"} ${r(l.label)}</b>`+(l.sub?` <span class="muted" style="font-size:12px">${r(l.sub)}</span>`:"")+y+` <span class="muted" style="font-size:12px">\xB7 ${l.deployments.length} \u4E2A\u90E8\u7F72 \xB7 ${l.bots.length} \u4E2A\u673A\u5668\u4EBA</span></div>`,h||(n+=l.kind==="remote"&&!l.ok?'<p class="muted" style="margin:8px 0 0">\u65E0\u6CD5\u83B7\u53D6\u8BE5\u56E2\u961F\u82B1\u540D\u518C\u3002</p>':Uo(l,c)),n+="</div>"}e.innerHTML=n;let i=t.length>1?`\uFF08\u8DE8 ${t.length} \u4E2A\u56E2\u961F\uFF0C\u53BB\u91CD\uFF09`:"";W("tf-count").textContent=`\xB7 ${s.size===o.size?`${o.size}`:`${s.size} / ${o.size}`} \u4E2A\u673A\u5668\u4EBA${i}`,jo()}function jo(){let e=W("tf-teams");e.querySelectorAll(".tf-team-h").forEach(t=>{t.onclick=()=>{let n=t.dataset.tk;tt.has(n)?tt.delete(n):tt.add(n),Re()}}),e.querySelectorAll(".tf-dep-h").forEach(t=>{t.onclick=()=>{let n=t.dataset.dk;nt.has(n)?nt.delete(n):nt.add(n),Re()}}),e.querySelectorAll(".tf-pick").forEach(t=>{t.onchange=()=>{let n=Ne(t.dataset.tk);t.checked?n.add(t.dataset.app):n.delete(t.dataset.app)}}),e.querySelectorAll(".tf-gname").forEach(t=>{t.oninput=()=>{kt.set(t.dataset.tk,t.value)}}),e.querySelectorAll(".tf-cap").forEach(t=>{t.onchange=async()=>{let n=t.dataset.app,s=t.value;await qo("/api/team/local-bots/"+encodeURIComponent(n)+"/capability",{capability:s}),It().forEach(o=>{let i=o.bots.find(l=>l.larkAppId===n);i&&(i.capability=s.trim()||null)})}}),e.querySelectorAll(".tf-role").forEach(t=>{t.onclick=()=>_o(t.dataset.app,t.dataset.name||"")}),e.querySelectorAll(".tf-rmmember").forEach(t=>{t.onclick=async n=>{n.stopPropagation(),confirm(`\u628A\u300C${t.dataset.name}\u300D\u79FB\u51FA\u8FD9\u4E2A\u56E2\u961F\uFF1F\u5B83\u7684\u673A\u5668\u4EBA\u5C06\u4ECE\u672C\u56E2\u961F\u82B1\u540D\u518C\u6D88\u5931\uFF08\u4E0D\u5F71\u54CD\u5BF9\u65B9\u81EA\u5DF1\u7684\u90E8\u7F72\uFF09\u3002`)&&(await St("DELETE",`/api/team/hosted/${encodeURIComponent(t.dataset.team)}/members/${encodeURIComponent(t.dataset.dep)}`),at())}}),e.querySelectorAll(".tf-grp").forEach(t=>{t.onclick=async()=>{let n=t.dataset.tk,s=Bo(n);if(!s)return;let o=[...Ne(n)],i=e.querySelector(`.tf-gout[data-tk="${CSS.escape(n)}"]`);if(!o.length){i.innerHTML='<span class="err">\u8BF7\u5148\u52FE\u9009\u81F3\u5C11\u4E00\u4E2A\u673A\u5668\u4EBA</span>';return}let l=(e.querySelector(`.tf-gname[data-tk="${CSS.escape(n)}"]`)?.value||"").trim()||"\u534F\u4F5C\u7FA4";i.innerHTML='<span class="muted">\u5EFA\u7FA4\u4E2D\u2026</span>';let c=s.kind==="local"?await Pe("/api/team/federated-group",{name:l,larkAppIds:o,teamId:s.teamId}):await Pe("/api/team/remote-group",{hubUrl:s.hubUrl,teamId:s.teamId,name:l,larkAppIds:o});if(zo(i,c.body,c.status),c.body?.ok){Ne(n).clear(),kt.delete(n);let w=i.innerHTML,h=()=>{let y=e.querySelector(`.tf-gout[data-tk="${CSS.escape(n)}"]`);y&&(y.innerHTML=w)};s.kind==="local"?at().then(h):(Re(),h())}}})}function zo(e,t,n){if(t?.ok&&t.chatId){let s=t.shareLink||"https://applink.feishu.cn/client/chat/open?openChatId="+encodeURIComponent(t.chatId),o=(t.invalidBotIds||[]).length?`<span class="err"> \xB7 \u672A\u52A0\u5165\u7684\u673A\u5668\u4EBA\uFF1A${r((t.invalidBotIds||[]).join(", "))}</span>`:"",i=(t.invalidOwnerUnionIds||[]).length?`<span class="err"> \xB7 ${(t.invalidOwnerUnionIds||[]).length} \u4E2A owner \u672A\u80FD\u62C9\u8FDB</span>`:"",l=t.missingOperatorIdentity?'<span class="err"> \xB7 \u4F60\u672A\u7ED1\u5B9A\u98DE\u4E66\u8EAB\u4EFD\uFF0C\u6CA1\u628A\u4F60\u81EA\u5DF1\u62C9\u8FDB\u7FA4\uFF08\u53BB\u300C\u6211\u7684\u56E2\u961F\u300D\u7ED1\u5B9A\uFF09</span>':"",c=(t.skippedNoOwner||[]).length?`<span class="err"> \xB7 ${(t.skippedNoOwner||[]).length} \u4E2A\u673A\u5668\u4EBA\u56E0\u8D1F\u8D23\u4EBA\u672A\u7ED1\u5B9A\u8EAB\u4EFD\u88AB\u8DF3\u8FC7\uFF08\u672A\u52A0\u5165\u7FA4\uFF09</span>`:"",w=t.delegatedTo?`\uFF08\u7531\u300C${r(t.delegatedTo)}\u300D\u5EFA\u7FA4\uFF09`:"";e.innerHTML=`<span class="ok">\u7FA4\u5DF2\u521B\u5EFA</span>${w} \xB7 <a href="${r(s)}" target="_blank">\u5728\u98DE\u4E66\u6253\u5F00</a>${o}${i}${l}${c}`}else{let s=t?.error||n,o=s==="no_local_online_bot"?"\u8BF7\u81F3\u5C11\u52FE\u9009\u4E00\u4E2A\u4F60\u81EA\u5DF1\uFF08\u672C\u90E8\u7F72\uFF09\u7684\u5728\u7EBF\u673A\u5668\u4EBA\u2014\u2014\u7FA4\u8981\u7531\u5B83\u521B\u5EFA\u5E76\u628A\u4F60\uFF08\u53D1\u8D77\u4EBA\uFF09\u62C9\u8FDB\u7FA4\u3002":s==="all_bots_skipped_no_owner"?"\u6240\u9009\u673A\u5668\u4EBA\u7684\u8D1F\u8D23\u4EBA\u90FD\u6CA1\u7ED1\u5B9A\u8EAB\u4EFD\uFF0C\u65E0\u6CD5\u62C9\u7FA4\uFF08\u673A\u5668\u4EBA\u4E0D\u80FD\u8FDB\u4E00\u4E2A owner \u4E0D\u5728\u7684\u7FA4\uFF09\u3002\u8BF7\u8BA9\u5BF9\u5E94\u90E8\u7F72\u5148\u7ED1\u5B9A\u8EAB\u4EFD\u3002":s==="no_creator_available"?"\u6CA1\u6709\u53EF\u7528\u7684\u5EFA\u7FA4\u53D1\u8D77\u65B9\uFF08\u76F8\u5173\u90E8\u7F72\u90FD\u6CA1\u6709\u5728\u7EBF\u673A\u5668\u4EBA\uFF0C\u6216\u4E0D\u53EF\u8FBE\uFF09":s==="delegation_timeout"?"\u59D4\u6258\u5BF9\u65B9\u90E8\u7F72\u5EFA\u7FA4\u8D85\u65F6\uFF08\u53EF\u80FD\u5DF2\u5EFA\uFF0C\u53BB\u98DE\u4E66\u786E\u8BA4\uFF0C\u52FF\u91CD\u590D\u70B9\uFF09":`\u5EFA\u7FA4\u5931\u8D25\uFF1A${s}`;e.innerHTML=`<span class="err">${r(String(o))}</span>`}}async function _o(e,t){let n=await $t("/api/team/local-bots/"+encodeURIComponent(e)+"/role");W("tf-modal-title").textContent="\u9ED8\u8BA4\u89D2\u8272 \xB7 "+t,W("tf-modal-text").value=n.body?.role||"",W("tf-modal").dataset.app=e,W("tf-modal").style.display="flex"}function sa(){let e=Array.from(new Set(It().flatMap(s=>s.bots.map(o=>o.cliId)).filter(Boolean))).sort(),t=W("tf-cli"),n=t.value;t.innerHTML='<option value="">\u5168\u90E8 CLI</option>'+e.map(s=>`<option value="${r(s)}">${r(s)}</option>`).join(""),t.value=n}async function at(){let t=(await $t("/api/team/hosted")).body;if(!t?.ok){Kt=[],Re();return}aa=t.deployment.deploymentId,vt=t.suggestedHubUrl||"",W("tf-owner").textContent=t.deployment.ownerName||(t.deployment.ownerUnionId?"\u5DF2\u7ED1\u5B9A":"\u672A\u7ED1\u5B9A"),Kt=(t.teams||[]).map(n=>({kind:"local",key:`local:${n.teamId}`,teamId:n.teamId,label:n.isDefault?"\u6211\u6258\u7BA1\u7684\u56E2\u961F":n.name,sub:"",ok:!0,deployments:n.deployments||[],bots:n.bots||[]})),sa(),Re()}async function Fo(){na=((await $t("/api/team/remote-roster")).body?.memberships||[]).map(n=>{let s=n.roster?.deployments||[],o=s.find(l=>l.local),i=o?.name?`${o.name} \u7684\u56E2\u961F`:n.teamName||n.teamId;return{kind:"remote",key:`${n.hubUrl}::${n.teamId}`,teamId:n.teamId,label:i,sub:n.hubUrl,ok:!!n.ok,error:n.error,hubUrl:n.hubUrl,deployments:s,bots:n.roster?.bots||[]}}),sa(),Re()}function ia(e){e.innerHTML=No(),Vt.clear(),kt.clear(),tt.clear(),nt.clear(),["tf-search","tf-cli","tf-fcap","tf-frole"].forEach(t=>{let n=W(t);n.oninput=Re,n.onchange=Re}),W("tf-modal-cancel").onclick=()=>{W("tf-modal").style.display="none"},Wo(),at(),Fo()}function Go(){return`<section class="page">
|
|
691
740
|
<div class="page-heading"><div>
|
|
692
741
|
<p class="eyebrow">\u56E2\u961F</p><h1>\u56E2\u961F\u7BA1\u7406</h1>
|
|
693
742
|
<p class="tf-lede">\u521B\u5EFA\u591A\u4E2A\u56E2\u961F\u3001\u7ED9\u6BCF\u4E2A\u56E2\u961F\u751F\u6210\u9080\u8BF7\u7801\u3001\u6216\u52A0\u5165\u522B\u4EBA\u7684\u56E2\u961F\u3002\u4E00\u4E2A\u56E2\u961F = \u4F60\u672C\u90E8\u7F72\u7684\u673A\u5668\u4EBA + \u52A0\u5165\u8BE5\u56E2\u961F\u7684\u5176\u5B83\u90E8\u7F72\u3002</p>
|
|
694
743
|
</div></div>
|
|
695
|
-
${
|
|
744
|
+
${oa("manage")}
|
|
696
745
|
<div class="card" style="margin-bottom:16px">
|
|
697
746
|
<h2 style="margin-top:0">\u6211\u6258\u7BA1\u7684\u56E2\u961F</h2>
|
|
698
747
|
<p style="display:flex;gap:8px;flex-wrap:wrap;align-items:center;margin-bottom:6px">
|
|
@@ -711,16 +760,16 @@ ${Gn("manage")}
|
|
|
711
760
|
</p>
|
|
712
761
|
<div id="tm-join-out" style="display:none;margin-top:6px"></div>
|
|
713
762
|
</div>
|
|
714
|
-
</section>`}async function
|
|
763
|
+
</section>`}async function Yt(){let t=(await $t("/api/team/hosted")).body,n=W("tm-list");vt=t?.suggestedHubUrl||vt;let s=t?.teams||[];if(!s.length){n.innerHTML='<p class="muted">\u8FD8\u6CA1\u6709\u56E2\u961F\u3002</p>';return}n.innerHTML=s.map(o=>{let i=(o.deployments||[]).filter(l=>!l.local).length;return`<div class="card" style="margin:0 0 8px;padding:10px 14px;background:var(--bg-soft,#f6f7f9)">
|
|
715
764
|
<div style="display:flex;align-items:center;gap:8px;flex-wrap:wrap">
|
|
716
|
-
<b>${r(
|
|
717
|
-
<span class="muted" style="font-size:12px">\xB7 ${(
|
|
765
|
+
<b>${r(o.name)}</b>${o.isDefault?' <span class="muted" style="font-size:12px">\u9ED8\u8BA4</span>':""}
|
|
766
|
+
<span class="muted" style="font-size:12px">\xB7 ${(o.deployments||[]).length} \u4E2A\u90E8\u7F72${i?`\uFF08\u542B ${i} \u8FDC\u7AEF\uFF09`:""} \xB7 ${(o.bots||[]).length} \u4E2A\u673A\u5668\u4EBA</span>
|
|
718
767
|
<span style="margin-left:auto;display:flex;gap:6px">
|
|
719
|
-
<button class="tm-invite ghost" data-team="${r(
|
|
720
|
-
${
|
|
768
|
+
<button class="tm-invite ghost" data-team="${r(o.teamId)}" style="font-size:12px">\u751F\u6210\u9080\u8BF7\u7801</button>
|
|
769
|
+
${o.isDefault?"":`<button class="tm-del ghost" data-team="${r(o.teamId)}" data-name="${r(o.name)}" style="font-size:12px">\u5220\u9664</button>`}
|
|
721
770
|
</span>
|
|
722
771
|
</div>
|
|
723
|
-
<div class="tm-inv-out" data-team="${r(
|
|
772
|
+
<div class="tm-inv-out" data-team="${r(o.teamId)}" style="display:none;margin-top:6px;font-size:13px"></div></div>`}).join(""),n.querySelectorAll(".tm-invite").forEach(o=>{o.onclick=async()=>{let i=o.dataset.team,l=n.querySelector(`.tm-inv-out[data-team="${CSS.escape(i)}"]`);l.style.display="",l.innerHTML='<span class="muted">\u751F\u6210\u4E2D\u2026</span>';let c=await Pe("/api/team/local-invite",{teamId:i});c.body?.code?l.innerHTML=`\u628A\u4E0B\u9762\u4E24\u9879\u53D1\u7ED9<b>\u522B\u7684\u90E8\u7F72</b>\u7684\u4EBA\uFF0824 \u5C0F\u65F6\u5185\u3001\u5355\u6B21\u6709\u6548\uFF09\uFF1A<br>Hub \u5730\u5740\uFF1A<code>${r(vt)}</code><br>\u9080\u8BF7\u7801\uFF1A<code style="font-size:15px">${r(c.body.code)}</code>`:l.innerHTML='<span class="err">\u751F\u6210\u5931\u8D25\u3002</span>'}}),n.querySelectorAll(".tm-del").forEach(o=>{o.onclick=async()=>{confirm(`\u5220\u9664\u56E2\u961F\u300C${o.dataset.name}\u300D\uFF1F\u5DF2\u52A0\u5165\u5B83\u7684\u90E8\u7F72\u4F1A\u88AB\u79FB\u9664\uFF08\u4E0D\u5F71\u54CD\u4ED6\u4EEC\u81EA\u5DF1\u7684\u90E8\u7F72\uFF09\u3002`)&&(await St("DELETE","/api/team/hosted/"+encodeURIComponent(o.dataset.team)),Yt())}})}function ra(e){e.innerHTML=Go(),W("tm-create").onclick=async()=>{let t=W("tm-newname").value.trim(),n=e.querySelector(".tm-cout");if(!t){n.innerHTML='<span class="err">\u8BF7\u586B\u56E2\u961F\u540D\u79F0</span>';return}n.innerHTML='<span class="muted">\u521B\u5EFA\u4E2D\u2026</span>';let s=await Pe("/api/team/hosted",{name:t});s.body?.ok?(n.innerHTML='<span class="ok">\u5DF2\u521B\u5EFA</span>',W("tm-newname").value="",Yt()):n.innerHTML=`<span class="err">\u521B\u5EFA\u5931\u8D25\uFF1A${r(String(s.body?.error||s.status))}</span>`},W("tm-join").onclick=async()=>{let t=W("tm-hub").value.trim(),n=W("tm-code").value.trim(),s=W("tm-join-out");if(s.style.display="",!t||!n){s.innerHTML='<span class="err">\u8BF7\u586B Hub \u5730\u5740\u548C\u9080\u8BF7\u7801\u3002</span>';return}s.innerHTML='<span class="muted">\u52A0\u5165\u4E2D\u2026</span>';let o=await Pe("/api/team/join-remote",{hubUrl:t,inviteCode:n});if(o.body?.ok)s.innerHTML=`<span class="ok">\u5DF2\u52A0\u5165\u300C${r(o.body.teamName||"")}\u300D\uFF0C\u53BB\u300C\u6211\u7684\u56E2\u961F\u300D\u67E5\u770B\u3002</span>`,W("tm-code").value="";else{let i=o.body?.error||o.status,l=i==="cannot_join_self"?"\u8FD9\u662F\u4F60\u81EA\u5DF1\u7684\u90E8\u7F72\uFF0C\u4E0D\u80FD\u52A0\u5165\u81EA\u5DF1\uFF08\u9080\u8BF7\u7801\u8981\u53D1\u7ED9\u522B\u7684\u90E8\u7F72\u7684\u4EBA\u7528\uFF09":i==="deployment_already_joined"?"\u4F60\u7684\u90E8\u7F72\u5DF2\u7ECF\u52A0\u5165\u8FC7\u8FD9\u4E2A\u56E2\u961F\u4E86":i==="hub_unreachable"?"\u8FDE\u4E0D\u4E0A\u5BF9\u65B9 Hub\uFF08\u68C0\u67E5\u5730\u5740/\u7F51\u7EDC\uFF09":i==="hub_timeout"?"\u5BF9\u65B9 Hub \u54CD\u5E94\u8D85\u65F6":`\u52A0\u5165\u5931\u8D25\uFF1A${i}`;s.innerHTML=`<span class="err">${r(String(l))}</span>`}},Yt()}function Wo(){W("tf-autobind").onclick=async()=>{let e=W("tf-bind-out");e.style.display="",e.innerHTML='<span class="muted">\u8BC6\u522B\u4E2D\u2026</span>';let n=(await Pe("/api/team/identity/auto-bind")).body;if(n?.ok&&n.owner){e.innerHTML=`<span class="ok">\u5DF2\u7ED1\u5B9A\uFF1A${r(n.owner.name||n.owner.unionId)}</span>`,at();return}if(n?.ok&&n.needChoice&&Array.isArray(n.candidates)){let s=n.candidates.map(o=>`<button class="tf-pickowner ghost" data-union="${r(o.unionId)}" style="margin:2px">${r(o.name||o.unionId)}</button>`).join(" ");e.innerHTML=`\u8BC6\u522B\u5230\u591A\u4E2A\u5019\u9009\uFF0C\u70B9\u4F60\u81EA\u5DF1\uFF1A<br>${s}`,e.querySelectorAll(".tf-pickowner").forEach(o=>{o.onclick=async()=>{e.innerHTML='<span class="muted">\u7ED1\u5B9A\u4E2D\u2026</span>';let l=(await Pe("/api/team/identity/auto-bind",{unionId:o.dataset.union})).body;l?.ok&&l.owner?(e.innerHTML=`<span class="ok">\u5DF2\u7ED1\u5B9A\uFF1A${r(l.owner.name||l.owner.unionId)}</span>`,at()):e.innerHTML=`<span class="err">\u7ED1\u5B9A\u5931\u8D25\uFF1A${r(String(l?.error||"unknown"))}</span>`}});return}if(n?.error==="no_candidates"){e.innerHTML='<span class="err">\u6CA1\u8BC6\u522B\u5230\u8EAB\u4EFD\uFF1A\u8BF7\u786E\u8BA4\u673A\u5668\u4EBA\u914D\u7F6E\u4E86 allowedUsers\uFF08\u5141\u8BB8\u4F7F\u7528\u8005\uFF09\uFF0C\u4E14\u673A\u5668\u4EBA\u6709\u901A\u8BAF\u5F55\u6743\u9650\u3002</span>';return}e.innerHTML=`<span class="err">\u7ED1\u5B9A\u5931\u8D25\uFF1A${r(String(n?.error||"unknown"))}</span>`}}async function Qt(e){let t=await fetch(e);return{status:t.status,body:await t.json().catch(()=>({}))}}async function Zt(e,t,n){let s=await fetch(t,{method:e,headers:{"content-type":"application/json"},body:n?JSON.stringify(n):void 0});return{status:s.status,body:await s.json().catch(()=>({}))}}function J(e){return document.getElementById(e)}function Ue(e){return(J(e).value||"").trim()}var en=[],nn=[];function Jo(){return`<section class="page">
|
|
724
773
|
<div class="page-heading">
|
|
725
774
|
<div>
|
|
726
775
|
<p class="eyebrow">\u63A5\u5165\u70B9 \xB7 beta</p>
|
|
@@ -782,68 +831,89 @@ ${Gn("manage")}
|
|
|
782
831
|
<h2 style="margin-top:0">\u5DF2\u6709\u63A5\u5165\u70B9 <span class="muted" id="cn-count" style="font-size:13px"></span></h2>
|
|
783
832
|
<div id="cn-list">\u52A0\u8F7D\u4E2D\u2026</div>
|
|
784
833
|
</div>
|
|
785
|
-
</section>`}function
|
|
834
|
+
</section>`}function Xt(){let e=J("cn-kind").value,t=J("cn-mode").value;document.querySelectorAll(".cn-wf").forEach(n=>{n.style.display=e==="workflow"?"":"none"}),document.querySelectorAll(".cn-fixed").forEach(n=>{n.style.display=t==="fixed"?"":"none"}),document.querySelectorAll(".cn-allow").forEach(n=>{n.style.display=t==="fixed"?"none":""}),document.querySelectorAll(".cn-dyn").forEach(n=>{n.style.display=t==="dynamic"?"":"none"}),document.querySelectorAll(".cn-life").forEach(n=>{n.style.display=t==="new-group"?"":"none"})}function la(e){return`${location.origin}/webhook/${encodeURIComponent(e)}`}function Ko(e){return e==="fixed"?"\u56FA\u5B9A\u7FA4":e==="new-group"?"\u6BCF\u6B21\u65B0\u5EFA\u7FA4":"\u8BF7\u6C42\u6307\u5B9A\u7FA4"}function Vo(e){return e==="workflow"?"\u5DE5\u4F5C\u6D41":"\u5355\u8F6E"}function tn(e){return nn.find(n=>n.chatId===e)?.name||e}function Yo(e){return nn.filter(t=>t.bots.includes(e))}function da(){let e=J("cn-bot").value,t=Yo(e),n=c=>`<option value="${r(c.chatId)}">${r(c.name||c.chatId)}</option>`,s=J("cn-chat-sel"),o=s.value;s.innerHTML=t.length?t.map(n).join(""):'<option value="">\uFF08\u8BE5\u673A\u5668\u4EBA\u6682\u65E0\u53EF\u89C1\u7FA4\uFF0C\u70B9\u53F3\u4FA7\u624B\u52A8\u586B ID\uFF09</option>',o&&t.some(c=>c.chatId===o)&&(s.value=o);let i=J("cn-allow-sel"),l=new Set(Array.from(i.selectedOptions).map(c=>c.value));i.innerHTML=t.map(n).join(""),Array.from(i.options).forEach(c=>{l.has(c.value)&&(c.selected=!0)})}function Qo(e){let t=J("cn-list");if(J("cn-count").textContent=e.length?`\xB7 ${e.length} \u4E2A`:"",!e.length){t.innerHTML='<p class="muted">\u8FD8\u6CA1\u6709\u63A5\u5165\u70B9\u3002\u7528\u4E0A\u9762\u7684\u8868\u5355\u521B\u5EFA\u4E00\u4E2A\u3002</p>';return}t.innerHTML=e.map(n=>{let s=en.find(w=>w.larkAppId===n.target.botId),o=la(n.id),i=(n.verify?.type??"token")==="token",l=i?"\u4EE4\u724C":"\u7B7E\u540D",c=n.target.mode==="fixed"&&n.target.chatId?` \xB7 \u6295\u9012\u300C${r(tn(n.target.chatId))}\u300D`:"";return`<div class="card" style="margin:0 0 10px;padding:12px 14px;background:var(--bg-soft,#f6f7f9)">
|
|
786
835
|
<div style="display:flex;align-items:center;gap:8px;flex-wrap:wrap">
|
|
787
836
|
<b style="font-size:15px">${r(n.name)}</b>
|
|
788
837
|
<span class="${n.enabled?"ok":"muted"}" style="font-size:12px">${n.enabled?"\u5DF2\u542F\u7528":"\u5DF2\u505C\u7528"}</span>
|
|
789
|
-
<span class="muted" style="font-size:12px">\xB7 ${r(s?.botName||n.target.botId)} \xB7 ${
|
|
838
|
+
<span class="muted" style="font-size:12px">\xB7 ${r(s?.botName||n.target.botId)} \xB7 ${Vo(n.target.kind)} \xB7 ${Ko(n.target.mode)}${c} \xB7 ${l}</span>
|
|
790
839
|
<span style="margin-left:auto;display:flex;gap:6px">
|
|
791
840
|
<button class="cn-toggle ghost" data-id="${r(n.id)}" data-on="${n.enabled}" style="font-size:12px">${n.enabled?"\u505C\u7528":"\u542F\u7528"}</button>
|
|
792
841
|
<button class="cn-del ghost" data-id="${r(n.id)}" style="font-size:12px">\u5220\u9664</button>
|
|
793
842
|
</span>
|
|
794
843
|
</div>
|
|
795
844
|
<div style="margin-top:6px;font-size:13px;display:flex;align-items:center;gap:8px;flex-wrap:wrap">
|
|
796
|
-
<span class="muted">Webhook URL\uFF1A</span><code style="font-size:12px;word-break:break-all">${r(
|
|
797
|
-
<button class="cn-copy ghost" data-url="${r(
|
|
798
|
-
</div>${i?'<div class="muted" style="font-size:12px;margin-top:4px">\u4EE4\u724C\u6A21\u5F0F\uFF1A\u8C03\u7528\u65F6\u5728 URL \u672B\u5C3E\u8FFD\u52A0 <code>/<\u4EE4\u724C></code>\uFF08\u4EE4\u724C\u4EC5\u521B\u5EFA/\u8F6E\u6362\u65F6\u663E\u793A\u4E00\u6B21\uFF09\u3002</div>':""}${n.target.mode==="dynamic"?'<div class="muted" style="font-size:12px;margin-top:4px">\u52A8\u6001\u6A21\u5F0F\uFF1A\u8BF7\u6C42\u9700\u5E26\u76EE\u6807\u7FA4 \u2014\u2014 <code>?chatId=<\u7FA4ID></code> \u6216\u5934 <code>x-botmux-chat-id</code> \u6216 body <code>{"chatId":"\u2026"}</code>\u3002</div>':""}${n.promptEnvelope?.instruction?`<div class="muted" style="font-size:12px;margin-top:4px">\u5904\u7406\u6307\u4EE4\uFF1A${r(n.promptEnvelope.instruction)}</div>`:""}</div>`}).join(""),t.querySelectorAll(".cn-copy").forEach(n=>{n.onclick=()=>{navigator.clipboard?.writeText(n.dataset.url),n.textContent="\u5DF2\u590D\u5236",setTimeout(()=>n.textContent="\u590D\u5236",1200)}}),t.querySelectorAll(".cn-toggle").forEach(n=>{n.onclick=async()=>{await
|
|
799
|
-
<pre style="margin:6px 0 0;font-size:12px;white-space:pre-wrap;word-break:break-all"><code>curl -X POST '${
|
|
800
|
-
<p class="muted" style="font-size:12px;margin:6px 0 0">\u7FA4\u4E5F\u53EF\u653E\u8BF7\u6C42\u5934 <code>x-botmux-chat-id</code> \u6216 body <code>{"chatId":"\u2026"}</code>\u3002\u26A0\uFE0F URL \u5373\u51ED\u8BC1\uFF0C\u52FF\u6CC4\u6F0F\u3002</p>`:
|
|
801
|
-
<pre style="margin:6px 0 0;font-size:12px;white-space:pre-wrap;word-break:break-all"><code>curl -X POST '${
|
|
802
|
-
<p class="muted" style="font-size:12px;margin:6px 0 0">\u26A0\uFE0F URL \u5373\u51ED\u8BC1\uFF0C\u8BF7\u52FF\u516C\u5F00\u6CC4\u6F0F\uFF1B\u53EF\u5728\u4E0B\u65B9\u5217\u8868\u5220\u9664\u6216\u8F6E\u6362\u3002</p>`:
|
|
803
|
-
<p class="ok" style="margin:0 0 6px">\u5DF2\u521B\u5EFA\u300C${r(n)}\u300D${i==="fixed"&&l.target.chatId?`<span class="muted" style="font-weight:400;font-size:13px"> \xB7 \u6295\u9012\u5230\u300C${r(
|
|
804
|
-
<p style="margin:4px 0;font-size:13px"><span class="muted">Webhook URL\uFF1A</span><code style="word-break:break-all">${r(
|
|
805
|
-
${
|
|
806
|
-
${
|
|
845
|
+
<span class="muted">Webhook URL\uFF1A</span><code style="font-size:12px;word-break:break-all">${r(o)}${i?"/<\u4EE4\u724C>":""}</code>
|
|
846
|
+
<button class="cn-copy ghost" data-url="${r(o)}" style="font-size:12px">\u590D\u5236</button>
|
|
847
|
+
</div>${i?'<div class="muted" style="font-size:12px;margin-top:4px">\u4EE4\u724C\u6A21\u5F0F\uFF1A\u8C03\u7528\u65F6\u5728 URL \u672B\u5C3E\u8FFD\u52A0 <code>/<\u4EE4\u724C></code>\uFF08\u4EE4\u724C\u4EC5\u521B\u5EFA/\u8F6E\u6362\u65F6\u663E\u793A\u4E00\u6B21\uFF09\u3002</div>':""}${n.target.mode==="dynamic"?'<div class="muted" style="font-size:12px;margin-top:4px">\u52A8\u6001\u6A21\u5F0F\uFF1A\u8BF7\u6C42\u9700\u5E26\u76EE\u6807\u7FA4 \u2014\u2014 <code>?chatId=<\u7FA4ID></code> \u6216\u5934 <code>x-botmux-chat-id</code> \u6216 body <code>{"chatId":"\u2026"}</code>\u3002</div>':""}${n.promptEnvelope?.instruction?`<div class="muted" style="font-size:12px;margin-top:4px">\u5904\u7406\u6307\u4EE4\uFF1A${r(n.promptEnvelope.instruction)}</div>`:""}</div>`}).join(""),t.querySelectorAll(".cn-copy").forEach(n=>{n.onclick=()=>{navigator.clipboard?.writeText(n.dataset.url),n.textContent="\u5DF2\u590D\u5236",setTimeout(()=>n.textContent="\u590D\u5236",1200)}}),t.querySelectorAll(".cn-toggle").forEach(n=>{n.onclick=async()=>{await Zt("PATCH","/api/connectors/"+encodeURIComponent(n.dataset.id),{enabled:n.dataset.on!=="true"}),Tt()}}),t.querySelectorAll(".cn-del").forEach(n=>{n.onclick=async()=>{confirm("\u5220\u9664\u8FD9\u4E2A\u63A5\u5165\u70B9\uFF1F\u5B83\u7684 webhook URL \u4F1A\u7ACB\u5373\u5931\u6548\u3002")&&(await Zt("DELETE","/api/connectors/"+encodeURIComponent(n.dataset.id)),Tt())}})}async function Tt(){let[e,t,n]=await Promise.all([Qt("/api/bots"),Qt("/api/connectors"),Qt("/api/groups")]);en=(e.body?.bots||[]).map(i=>({larkAppId:i.larkAppId,botName:i.botName||i.larkAppId})),nn=(n.body?.chats||[]).map(i=>({chatId:i.chatId,name:i.name||"",bots:(i.memberBots||[]).filter(l=>l.inChat).map(l=>l.larkAppId)}));let s=J("cn-bot"),o=s.value;s.innerHTML=en.map(i=>`<option value="${r(i.larkAppId)}">${r(i.botName)}</option>`).join("")||'<option value="">\uFF08\u6CA1\u6709\u5728\u7EBF\u673A\u5668\u4EBA\uFF09</option>',o&&(s.value=o),da(),Qo(t.body?.connectors||[])}function ca(e){e.innerHTML=Jo(),J("cn-kind").onchange=Xt,J("cn-mode").onchange=Xt,J("cn-bot").onchange=da,J("cn-chat-manual").onclick=t=>{t.preventDefault();let n=J("cn-chat"),s=J("cn-chat-sel"),o=n.style.display==="none";n.style.display=o?"":"none",s.style.display=o?"none":"",J("cn-chat-manual").textContent=o?"\u4ECE\u7FA4\u5217\u8868\u9009\u62E9 \u2190":"\u627E\u4E0D\u5230\u7FA4\uFF1F\u624B\u52A8\u586B ID \u2192"},Xt(),J("cn-create").onclick=async()=>{let t=J("cn-create-out"),n=Ue("cn-name"),s=J("cn-bot").value;if(!n){t.innerHTML='<span class="err">\u8BF7\u586B\u540D\u79F0</span>';return}if(!s){t.innerHTML='<span class="err">\u8BF7\u9009\u673A\u5668\u4EBA</span>';return}let o=J("cn-kind").value,i=J("cn-mode").value,l={name:n,enabled:!0,target:{kind:o,mode:i,botId:s},promptEnvelope:{sourceName:n}},c=Ue("cn-instruction");if(c&&(l.promptEnvelope.instruction=c),o==="workflow"){if(!Ue("cn-wf")){t.innerHTML='<span class="err">\u8BF7\u586B\u5DE5\u4F5C\u6D41 ID</span>';return}l.target.workflowId=Ue("cn-wf")}if(i==="fixed"){let v=J("cn-chat").style.display!=="none"?Ue("cn-chat"):J("cn-chat-sel").value;if(!v){t.innerHTML='<span class="err">\u8BF7\u9009\u62E9\uFF08\u6216\u624B\u52A8\u586B\uFF09\u6295\u9012\u7684\u7FA4</span>';return}l.target.chatId=v}else{let y=Array.from(J("cn-allow-sel").selectedOptions).map(v=>v.value).filter(Boolean);y.length&&(l.target.allowChats=y)}if(i==="new-group"){let y=Ue("cn-dedup");l.lifecycleExtractors=y?{dedupKey:y}:null}l.verify={type:J("cn-verify").value};let w=Ue("cn-secret");w&&(l.secret=w),t.innerHTML='<span class="muted">\u521B\u5EFA\u4E2D\u2026</span>';let h=await Zt("POST","/api/connectors",l);if(h.status===201&&h.body?.ok){t.innerHTML="";let y=J("cn-created");y.style.display="";let v=h.body.webhookUrl||la(h.body.connector.id),p=h.body.secret,T=(h.body.connector?.verify?.type??"token")==="token",q=i==="dynamic",$=q?l.target.allowChats?.[0]||"<\u7FA4ID>":"",f=q?`${r(v)}?chatId=${r($)}`:r(v),d;T&&q?d=`<p class="muted" style="font-size:12px;margin:6px 0 0">\u52A8\u6001\u6A21\u5F0F\uFF1AURL \u5DF2\u542B\u4EE4\u724C\uFF0C\u8C03\u7528\u65F6\u518D\u5E26\u4E0A\u76EE\u6807\u7FA4 ID${$!=="<\u7FA4ID>"?`\uFF08${r(tn(l.target.allowChats[0]))}\uFF09`:""}\uFF1A</p>
|
|
848
|
+
<pre style="margin:6px 0 0;font-size:12px;white-space:pre-wrap;word-break:break-all"><code>curl -X POST '${f}' -H 'content-type: application/json' -d '{}'</code></pre>
|
|
849
|
+
<p class="muted" style="font-size:12px;margin:6px 0 0">\u7FA4\u4E5F\u53EF\u653E\u8BF7\u6C42\u5934 <code>x-botmux-chat-id</code> \u6216 body <code>{"chatId":"\u2026"}</code>\u3002\u26A0\uFE0F URL \u5373\u51ED\u8BC1\uFF0C\u52FF\u6CC4\u6F0F\u3002</p>`:T?d=`<p class="muted" style="font-size:12px;margin:6px 0 0">\u6B64 URL \u5DF2\u542B\u4EE4\u724C\u3001\u4E14\u56FA\u5B9A\u6295\u9012\u5230\u6240\u9009\u7FA4\uFF0C\u76F4\u63A5 POST \u5373\u53EF\u89E6\u53D1\uFF1A</p>
|
|
850
|
+
<pre style="margin:6px 0 0;font-size:12px;white-space:pre-wrap;word-break:break-all"><code>curl -X POST '${f}' -H 'content-type: application/json' -d '{}'</code></pre>
|
|
851
|
+
<p class="muted" style="font-size:12px;margin:6px 0 0">\u26A0\uFE0F URL \u5373\u51ED\u8BC1\uFF0C\u8BF7\u52FF\u516C\u5F00\u6CC4\u6F0F\uFF1B\u53EF\u5728\u4E0B\u65B9\u5217\u8868\u5220\u9664\u6216\u8F6E\u6362\u3002</p>`:d=`<p class="muted" style="font-size:12px;margin:6px 0 0">\u5916\u90E8\u7CFB\u7EDF\u9700\u5BF9 <code>timestamp.body</code> \u505A HMAC-SHA256 \u7B7E\u540D\uFF0C\u5E76\u5E26\u4E0A <code>x-botmux-timestamp</code> / <code>x-botmux-nonce</code> / <code>x-botmux-signature</code> \u5934\u8C03\u7528${q?"\uFF0C\u540C\u65F6\u6309\u4E0A\u9762\u65B9\u5F0F\u5E26\u76EE\u6807\u7FA4 ID":""}\u3002</p>`,y.innerHTML=`<div class="card" style="padding:12px 14px;background:var(--bg-soft,#f6f7f9)">
|
|
852
|
+
<p class="ok" style="margin:0 0 6px">\u5DF2\u521B\u5EFA\u300C${r(n)}\u300D${i==="fixed"&&l.target.chatId?`<span class="muted" style="font-weight:400;font-size:13px"> \xB7 \u6295\u9012\u5230\u300C${r(tn(l.target.chatId))}\u300D</span>`:""}</p>
|
|
853
|
+
<p style="margin:4px 0;font-size:13px"><span class="muted">Webhook URL\uFF1A</span><code style="word-break:break-all">${r(v)}</code></p>
|
|
854
|
+
${p?`<p style="margin:4px 0;font-size:13px"><span class="muted">${T?"\u8BBF\u95EE\u4EE4\u724C":"\u7B7E\u540D\u5BC6\u94A5"}\uFF08\u53EA\u663E\u793A\u8FD9\u4E00\u6B21\uFF0C\u8BF7\u4FDD\u5B58\uFF09\uFF1A</span><code>${r(p)}</code></p>`:""}
|
|
855
|
+
${d}</div>`,["cn-name","cn-wf","cn-chat","cn-dedup","cn-secret","cn-instruction"].forEach(g=>{J(g).value=""}),J("cn-allow-sel").selectedIndex=-1,Tt()}else{let y=h.body?.error||h.status;t.innerHTML=`<span class="err">\u521B\u5EFA\u5931\u8D25\uFF1A${r(String(y))}</span>`}},Tt()}var de=null,st=null,ot=!0;function ua(e){return{publicReadOnly:e?.publicReadOnly===!0,openTerminalInFeishu:e?.openTerminalInFeishu===!0,maintenance:e?.maintenance&&typeof e.maintenance=="object"?e.maintenance:{},localDevInstall:e?.localDevInstall===!0}}function Xo(e,t){let n=e?.[t]??{};return{enabled:n.enabled===!0,time:typeof n.time=="string"?n.time:"04:00"}}function Zo(){return`<section class="page">
|
|
807
856
|
<div class="page-heading">
|
|
808
857
|
<div>
|
|
809
|
-
<p class="eyebrow">${
|
|
810
|
-
<h1>${
|
|
811
|
-
<p>${
|
|
858
|
+
<p class="eyebrow">${a("nav.settings")}</p>
|
|
859
|
+
<h1>${a("settings.title")}</h1>
|
|
860
|
+
<p>${a("settings.subtitle")}</p>
|
|
812
861
|
</div>
|
|
813
862
|
</div>
|
|
814
863
|
<div id="settings-body"></div>
|
|
815
|
-
</section>`}function
|
|
864
|
+
</section>`}function es(e){let{enabled:t,time:n}=Xo(de.maintenance,"autoUpdate"),s=e?"disabled":"";return`<label class="toggle-row">
|
|
865
|
+
<input type="checkbox" data-maint="autoUpdate" ${t?"checked":""} ${s}>
|
|
866
|
+
<span class="switch" aria-hidden="true"></span>
|
|
867
|
+
<span class="toggle-tx"><strong>${a("settings.autoUpdate")}</strong>
|
|
868
|
+
<small>${a("settings.autoUpdateHelp")}</small></span>
|
|
869
|
+
</label>
|
|
870
|
+
<div class="maint-time">
|
|
871
|
+
<label>${a("settings.maintenanceTime")}
|
|
872
|
+
<input type="time" data-maint-time="autoUpdate" value="${r(n)}" ${s}>
|
|
873
|
+
</label>
|
|
874
|
+
</div>`}function ts(e){return`<label class="toggle-row">
|
|
875
|
+
<input type="checkbox" data-maint="autoRestart" ${de.maintenance.autoRestart?.enabled===!0?"checked":""} ${e?"disabled":""}>
|
|
876
|
+
<span class="switch" aria-hidden="true"></span>
|
|
877
|
+
<span class="toggle-tx"><strong>${a("settings.autoRestart")}</strong>
|
|
878
|
+
<small>${a("settings.autoRestartHelp")}</small></span>
|
|
879
|
+
</label>`}function ns(){if(st)return`<p class="hint-warn">${a("settings.loadFailed")}: ${r(st)}</p>`;if(!de)return`<p class="empty">${a("settings.loading")}</p>`;let e=ot?"":"disabled",t=!ot||de.localDevInstall;return`<div class="settings-grid">
|
|
816
880
|
<article class="bd-card settings-card">
|
|
817
|
-
${
|
|
881
|
+
${ot?"":`<p class="hint-warn">${a("settings.readOnlyVisitor")}</p>`}
|
|
818
882
|
<section class="bd-section">
|
|
819
|
-
<h3 class="bd-section-title">${
|
|
883
|
+
<h3 class="bd-section-title">${a("settings.sectionAccess")}</h3>
|
|
820
884
|
<label class="toggle-row">
|
|
821
|
-
<input type="checkbox" data-setting="publicReadOnly" ${
|
|
885
|
+
<input type="checkbox" data-setting="publicReadOnly" ${de.publicReadOnly?"checked":""} ${e}>
|
|
822
886
|
<span class="switch" aria-hidden="true"></span>
|
|
823
|
-
<span class="toggle-tx"><strong>${
|
|
824
|
-
<small>${
|
|
887
|
+
<span class="toggle-tx"><strong>${a("settings.publicReadOnly")}</strong>
|
|
888
|
+
<small>${a("settings.publicReadOnlyHelp")}</small></span>
|
|
825
889
|
</label>
|
|
826
890
|
</section>
|
|
827
891
|
<section class="bd-section">
|
|
828
|
-
<h3 class="bd-section-title">${
|
|
892
|
+
<h3 class="bd-section-title">${a("settings.sectionCards")}</h3>
|
|
829
893
|
<label class="toggle-row">
|
|
830
|
-
<input type="checkbox" data-setting="openTerminalInFeishu" ${
|
|
894
|
+
<input type="checkbox" data-setting="openTerminalInFeishu" ${de.openTerminalInFeishu?"checked":""} ${e}>
|
|
831
895
|
<span class="switch" aria-hidden="true"></span>
|
|
832
|
-
<span class="toggle-tx"><strong>${
|
|
833
|
-
<small>${
|
|
896
|
+
<span class="toggle-tx"><strong>${a("settings.openTerminalInFeishu")}</strong>
|
|
897
|
+
<small>${a("settings.openTerminalInFeishuHelp")}</small></span>
|
|
834
898
|
</label>
|
|
835
899
|
</section>
|
|
900
|
+
<section class="bd-section">
|
|
901
|
+
<h3 class="bd-section-title">${a("settings.sectionMaintenance")}</h3>
|
|
902
|
+
${es(t)}
|
|
903
|
+
${de.localDevInstall?`<p class="hint-warn">${a("settings.autoUpdateLocalDev")}</p>`:""}
|
|
904
|
+
${ts(!ot||de.maintenance.autoUpdate?.enabled!==!0)}
|
|
905
|
+
</section>
|
|
836
906
|
<div class="actions settings-actions">
|
|
837
907
|
<span class="oncall-status" data-settings-status></span>
|
|
838
908
|
</div>
|
|
839
909
|
</article>
|
|
840
|
-
</div>`}async function
|
|
910
|
+
</div>`}async function as(){try{let e=await fetch("/api/settings"),t=await e.json().catch(()=>({}));if(!e.ok){de=null,st=t?.error??`HTTP ${e.status}`;return}de=ua(t.settings),ot=t.authed===!0,st=null}catch(e){de=null,st=e?.message??String(e)}}async function pa(e){e.innerHTML=Zo();let t=e.querySelector("#settings-body");function n(){t.innerHTML=ns(),i()}function s(){return t.querySelector("[data-settings-status]")}async function o(l,c,w){if(!de)return;w.disabled=!0;let h=s();h&&(h.textContent=a("settings.saving"),h.className="oncall-status");try{let y=await fetch("/api/settings",{method:"PUT",headers:{"content-type":"application/json"},body:JSON.stringify(l)}),v=await y.json().catch(()=>({}));if(!y.ok||v.ok===!1)throw new Error(v?.error??`HTTP ${y.status}`);de=ua(v.settings),h&&(h.textContent=a("settings.saved"),h.classList.add("hint-ok"))}catch(y){c(),h&&(h.textContent=`${a("settings.saveFailed")}: ${y?.message??y}`,h.classList.add("hint-warn-inline"))}finally{w.disabled=!1}}function i(){t.querySelectorAll("input[data-setting]").forEach(c=>{c.addEventListener("change",()=>{let w=c.dataset.setting,h=!c.checked;o({[w]:c.checked},()=>{c.checked=h},c)})});let l=(c,w,h)=>{let v=t.querySelector(`input[data-maint="${c}"]`)?.checked??!1,p;if(c==="autoUpdate"){let T=t.querySelector('input[data-maint-time="autoUpdate"]');p={enabled:v,time:T?.value||"04:00"}}else p={enabled:v};o({maintenance:{[c]:p}},h,w).then(()=>n())};t.querySelectorAll("input[data-maint]").forEach(c=>{c.addEventListener("change",()=>{let w=c.dataset.maint,h=!c.checked;l(w,c,()=>{c.checked=h})})}),t.querySelectorAll("input[data-maint-time]").forEach(c=>{c.addEventListener("change",()=>{let w=c.dataset.maintTime,h=c.defaultValue;l(w,c,()=>{c.value=h})})})}n(),await as(),n()}function os(){let e=[["",a("workflow.filter.nonTerminal")],["all",a("workflow.filter.all")],["pending",Oe("pending")],["running",Oe("running")],["waiting",Oe("waiting")],["succeeded",Oe("succeeded")],["failed",Oe("failed")],["cancelled",Oe("cancelled")]];return`
|
|
841
911
|
<nav class="wf-subnav">
|
|
842
|
-
<a href="#/workflows" class="active" data-i18n="workflow.subnav.runs">${m(
|
|
843
|
-
<a href="#/workflows/catalog" data-i18n="workflow.subnav.catalog">${m(
|
|
912
|
+
<a href="#/workflows" class="active" data-i18n="workflow.subnav.runs">${m(a("workflow.subnav.runs"))}</a>
|
|
913
|
+
<a href="#/workflows/catalog" data-i18n="workflow.subnav.catalog">${m(a("workflow.subnav.catalog"))}</a>
|
|
844
914
|
</nav>
|
|
845
915
|
<form id="wf-filters" class="filters">
|
|
846
|
-
<input type="search" name="q" placeholder="${m(
|
|
916
|
+
<input type="search" name="q" placeholder="${m(a("workflow.searchPlaceholder"))}" />
|
|
847
917
|
<select name="status">
|
|
848
918
|
${e.map(([t,n])=>`<option value="${m(t)}">${m(n)}</option>`).join("")}
|
|
849
919
|
</select>
|
|
@@ -851,28 +921,28 @@ ${Gn("manage")}
|
|
|
851
921
|
</form>
|
|
852
922
|
<table>
|
|
853
923
|
<thead><tr>
|
|
854
|
-
<th>${m(
|
|
855
|
-
<th>${m(
|
|
856
|
-
<th>${m(
|
|
924
|
+
<th>${m(a("workflow.table.run"))}</th><th>${m(a("workflow.table.workflow"))}</th><th>${m(a("workflow.table.status"))}</th>
|
|
925
|
+
<th>${m(a("workflow.table.lastSeq"))}</th><th>${m(a("workflow.table.dangling"))}</th><th>${m(a("workflow.table.updated"))}</th>
|
|
926
|
+
<th>${m(a("workflow.table.chatApp"))}</th>
|
|
857
927
|
</tr></thead>
|
|
858
928
|
<tbody id="wf-tbody"></tbody>
|
|
859
929
|
</table>
|
|
860
|
-
`}var
|
|
861
|
-
<td><a href="#/workflows/${encodeURIComponent(
|
|
862
|
-
<td>${m(
|
|
863
|
-
<td>${
|
|
864
|
-
<td>${
|
|
865
|
-
<td class="${
|
|
866
|
-
<td title="${m(new Date(
|
|
867
|
-
<td>${
|
|
868
|
-
</tr>`}).join("")}function
|
|
930
|
+
`}var ss=5e3,is=2e3,it=new Set(["succeeded","failed","cancelled"]);function m(e){return e.replace(/[&<>"']/g,t=>({"&":"&","<":"<",">":">",'"':""","'":"'"})[t])}function rs(e){let t=new Date(e),s=Date.now()-e;return s<6e4?a("time.secondsAgo",{value:Math.max(1,Math.floor(s/1e3))}):s<36e5?a("time.minutesAgo",{value:Math.floor(s/6e4)}):s<864e5?a("time.hoursAgo",{value:Math.floor(s/36e5)}):t.toISOString().slice(0,19).replace("T"," ")}function xe(e){return`<span class="${it.has(e)?"wf-status terminal":"wf-status live"} wf-status-${m(e)}">${m(Oe(e))}</span>`}function Oe(e){let t=`workflow.status.${e}`,n=a(t);return n===t?e:n}function ba(e){let t=location.hash.match(/^#\/workflows\/([^?#]+)(?:\?([^#]*))?$/);if(t){let n=new URLSearchParams(t[2]??"");return ds(e,decodeURIComponent(t[1]),{focusAttemptId:n.get("attempt")??void 0})}return ls(e)}function ls(e){e.innerHTML=os();let t=e.querySelector("#wf-tbody"),n=e.querySelector("#wf-filters"),s=e.querySelector("#wf-last-load"),o=[],i=null,l=!1,c=null,w=!1;function h($){let d=(new FormData(n).get("q")??"").trim().toLowerCase();return d?$.filter(g=>g.runId.toLowerCase().includes(d)||g.workflowId.toLowerCase().includes(d)||(g.chatId??"").toLowerCase().includes(d)):$}function y(){let $=h(o);if($.length===0){t.innerHTML=`<tr><td colspan="7" class="empty">${c?m(a("workflow.list.failedLoad",{error:c})):o.length===0?m(a("workflow.list.noRuns")):m(a("workflow.list.noFilterMatch"))}</td></tr>`;return}t.innerHTML=$.map(f=>{let d=`${f.dEf}/${f.dAct}/${f.dWait}`,g=f.dEf+f.dAct+f.dWait>0?"wf-dangling has":"wf-dangling none",k=[];f.chatId&&k.push(m(f.chatId)),f.larkAppId&&k.push(`<span class="muted">${m(f.larkAppId)}</span>`);let H=k.length>0?k.join("<br/>"):"\u2014",E=us(f);return`<tr data-runid="${m(f.runId)}">
|
|
931
|
+
<td><a href="#/workflows/${encodeURIComponent(f.runId)}"><code>${m(f.runId)}</code></a></td>
|
|
932
|
+
<td>${m(f.workflowId)}</td>
|
|
933
|
+
<td>${xe(f.status)}${f.failedNodeId?` <span class="muted">(${m(f.failedNodeId)})</span>`:""}${E}</td>
|
|
934
|
+
<td>${f.lastSeq}</td>
|
|
935
|
+
<td class="${g}">${d}</td>
|
|
936
|
+
<td title="${m(new Date(f.updatedAt).toISOString())}">${rs(f.updatedAt)}</td>
|
|
937
|
+
<td>${H}</td>
|
|
938
|
+
</tr>`}).join("")}function v(){c?(s.textContent=a("workflow.list.error",{error:c}),s.classList.add("error")):(s.textContent=a("workflow.list.loaded",{count:o.length,time:new Date().toLocaleTimeString()}),s.classList.remove("error"))}async function p(){if(!(w||l)&&!document.hidden){l=!0;try{let $=n.elements.namedItem("status")?.value??"",f=new URLSearchParams;$==="all"?f.set("all","1"):$&&f.set("status",$);let d="/api/workflows/runs"+(f.toString()?`?${f}`:""),g=await fetch(d);g.ok?(o=(await g.json()).runs??[],c=null):(c=`HTTP ${g.status}`,o=[])}catch($){c=$?.message??String($),o=[]}finally{l=!1,w||(y(),v())}}}function T(){i!==null&&window.clearTimeout(i),i=window.setTimeout(async()=>{await p(),w||T()},ss)}function q(){document.hidden||p()}return n.addEventListener("input",()=>{y()}),n.addEventListener("change",$=>{$.target.getAttribute("name")==="status"&&p()}),document.addEventListener("visibilitychange",q),p().then(()=>{w||T()}),()=>{w=!0,i!==null&&window.clearTimeout(i),document.removeEventListener("visibilitychange",q)}}function ds(e,t,n={}){e.innerHTML=`
|
|
869
939
|
<div class="wf-detail-head">
|
|
870
|
-
<a class="btn-link" href="#/workflows">${m(
|
|
940
|
+
<a class="btn-link" href="#/workflows">${m(a("workflow.detail.back"))}</a>
|
|
871
941
|
<div>
|
|
872
942
|
<h2><code>${m(t)}</code></h2>
|
|
873
|
-
<div id="wf-detail-subtitle" class="muted">${m(
|
|
943
|
+
<div id="wf-detail-subtitle" class="muted">${m(a("workflow.detail.loading"))}</div>
|
|
874
944
|
</div>
|
|
875
|
-
<button id="wf-cancel-run" type="button" class="contrast" hidden>${m(
|
|
945
|
+
<button id="wf-cancel-run" type="button" class="contrast" hidden>${m(a("workflow.detail.cancel"))}</button>
|
|
876
946
|
<span id="wf-detail-refresh" class="muted"></span>
|
|
877
947
|
</div>
|
|
878
948
|
<section id="wf-detail-error" class="hint-warn" hidden></section>
|
|
@@ -881,20 +951,20 @@ ${Gn("manage")}
|
|
|
881
951
|
<section id="wf-dangling-panel"></section>
|
|
882
952
|
<section class="wf-panel">
|
|
883
953
|
<div class="wf-panel-title">
|
|
884
|
-
<h3>${m(
|
|
954
|
+
<h3>${m(a("workflow.detail.parallel"))}</h3>
|
|
885
955
|
<span id="wf-parallel-meta" class="muted"></span>
|
|
886
956
|
</div>
|
|
887
957
|
<div id="wf-parallel-view"></div>
|
|
888
958
|
</section>
|
|
889
959
|
<section class="wf-panel">
|
|
890
960
|
<div class="wf-panel-title">
|
|
891
|
-
<h3>${m(
|
|
961
|
+
<h3>${m(a("workflow.detail.nodes"))}</h3>
|
|
892
962
|
</div>
|
|
893
963
|
<div class="wf-table-scroll">
|
|
894
964
|
<table>
|
|
895
965
|
<thead><tr>
|
|
896
|
-
<th>${m(
|
|
897
|
-
<th>${m(
|
|
966
|
+
<th>${m(a("workflow.detail.node"))}</th><th>${m(a("workflow.detail.nodeStatus"))}</th><th>${m(a("workflow.detail.activity"))}</th><th>${m(a("workflow.detail.activityStatus"))}</th>
|
|
967
|
+
<th>${m(a("workflow.detail.attempts"))}</th><th>${m(a("workflow.detail.current"))}</th><th>${m(a("workflow.detail.detail"))}</th>
|
|
898
968
|
</tr></thead>
|
|
899
969
|
<tbody id="wf-node-tbody"></tbody>
|
|
900
970
|
</table>
|
|
@@ -902,269 +972,269 @@ ${Gn("manage")}
|
|
|
902
972
|
</section>
|
|
903
973
|
<section class="wf-panel">
|
|
904
974
|
<div class="wf-panel-title">
|
|
905
|
-
<h3>${m(
|
|
975
|
+
<h3>${m(a("workflow.detail.nodeIO"))}</h3>
|
|
906
976
|
</div>
|
|
907
977
|
<div id="wf-io-list" class="wf-io-list"></div>
|
|
908
978
|
</section>
|
|
909
979
|
<section class="wf-panel">
|
|
910
980
|
<div class="wf-panel-title">
|
|
911
|
-
<h3>${m(
|
|
912
|
-
<button id="wf-load-older" type="button" hidden>${m(
|
|
981
|
+
<h3>${m(a("workflow.detail.timeline"))}</h3>
|
|
982
|
+
<button id="wf-load-older" type="button" hidden>${m(a("workflow.detail.loadOlder"))}</button>
|
|
913
983
|
</div>
|
|
914
984
|
<div class="wf-table-scroll wf-timeline-scroll">
|
|
915
985
|
<table>
|
|
916
986
|
<thead><tr>
|
|
917
|
-
<th>${m(
|
|
987
|
+
<th>${m(a("workflow.detail.seq"))}</th><th>${m(a("workflow.detail.event"))}</th><th>${m(a("workflow.detail.actor"))}</th><th>${m(a("workflow.detail.node"))}</th><th>${m(a("workflow.detail.activity"))}</th><th>${m(a("workflow.detail.error"))}</th><th>${m(a("workflow.detail.time"))}</th>
|
|
918
988
|
</tr></thead>
|
|
919
989
|
<tbody id="wf-event-tbody"></tbody>
|
|
920
990
|
</table>
|
|
921
991
|
</div>
|
|
922
992
|
<div id="wf-event-meta" class="muted"></div>
|
|
923
993
|
</section>
|
|
924
|
-
`;let s=e.querySelector("#wf-detail-subtitle"),
|
|
994
|
+
`;let s=e.querySelector("#wf-detail-subtitle"),o=e.querySelector("#wf-detail-refresh"),i=e.querySelector("#wf-detail-error"),l=e.querySelector("#wf-cancel-status"),c=e.querySelector("#wf-summary"),w=e.querySelector("#wf-dangling-panel"),h=e.querySelector("#wf-parallel-view"),y=e.querySelector("#wf-parallel-meta"),v=e.querySelector("#wf-node-tbody"),p=e.querySelector("#wf-io-list"),T=e.querySelector(".wf-timeline-scroll"),q=e.querySelector("#wf-event-tbody"),$=e.querySelector("#wf-event-meta"),f=e.querySelector("#wf-cancel-run"),d=e.querySelector("#wf-load-older"),g=null,k=[],H=new Set,E=null,x=null,C=!1,U=0,G=null,Q=!1,le=!1,ue=!1,V=new Set,ne=new Map,we=new Map,He=new Map,Te=new Set,ee=new Map,se=new Set,pe=new Map,te=new Map,Le=0,Me=n.focusAttemptId;function X(S){if(!S){i.hidden=!0,i.textContent="";return}i.hidden=!1,i.textContent=S}function u(S){if(!S){l.hidden=!0,l.textContent="";return}l.hidden=!1,l.textContent=S}async function b(){let S=await fetch(`/api/workflows/runs/${encodeURIComponent(t)}/snapshot`);if(S.status===404)throw new Error(a("workflow.detail.unknownRun"));if(!S.ok)throw new Error(a("workflow.detail.snapshotHttp",{status:S.status}));g=await S.json()}async function I(S){let Y=await fetch(`/api/workflows/runs/${encodeURIComponent(t)}/events?${S}`);if(Y.status===404)throw new Error(a("workflow.detail.unknownRun"));if(!Y.ok)throw new Error(a("workflow.detail.eventsHttp",{status:Y.status}));return await Y.json()}function L(S,Y){let j=S.filter(A=>H.has(A.eventId)?!1:(H.add(A.eventId),!0));j.length!==0&&(k=Y==="prepend"?[...j,...k]:[...k,...j],k.sort((A,R)=>rt(A.eventId)-rt(R.eventId)))}async function M(){await b();let S=await I(new URLSearchParams({tail:"100"}));k=[],H=new Set,L(S.events,"append"),E=S.oldestSeq,x=S.newestSeq,C=S.hasOlder,U=S.totalCount,ae()}async function B(){if(!(Q||le||document.hidden)){le=!0;try{if(await b(),x!==null){let S=await I(new URLSearchParams({afterSeq:String(x),limit:"200"}));L(S.events,"append"),S.newestSeq!==null&&(x=S.newestSeq),E===null&&S.oldestSeq!==null&&(E=S.oldestSeq),U=S.totalCount}else{let S=await I(new URLSearchParams({tail:"1"}));L(S.events,"append"),E=S.oldestSeq,x=S.newestSeq,C=S.hasOlder,U=S.totalCount}X(null),ae()}catch(S){X(S?.message??String(S))}finally{le=!1}}}async function P(){if(!(E===null||!C)){d.disabled=!0;try{let S=await I(new URLSearchParams({beforeSeq:String(E),limit:"100"}));L(S.events,"prepend"),S.oldestSeq!==null&&(E=S.oldestSeq),C=S.hasOlder,U=S.totalCount,X(null),ae()}catch(S){X(S?.message??String(S))}finally{d.disabled=!1}}}async function Z(){if(!g||it.has(g.run.status)||ue)return;if(!g.chatBinding?.larkAppId){X(a("workflow.detail.cancelUnavailable",{runId:t}));return}let S=ps(g),Y=a("workflow.detail.cancelConfirm",{runId:t,...S});if(window.confirm(Y)){ue=!0,f.disabled=!0;try{let j=await fetch(`/api/workflows/runs/${encodeURIComponent(t)}/cancel`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({reason:"cancelled via dashboard"})});if(j.status===401)throw new Error(a("workflow.detail.writeAccessCancel"));let A=await j.json().catch(()=>({}));if(!j.ok||!A.ok)throw new Error(A.hint??A.error??a("workflow.detail.cancelHttp",{status:j.status}));u(A.pending?a("workflow.detail.cancelPending"):null),X(null),await B()}catch(j){X(j?.message??String(j))}finally{ue=!1,f.disabled=!1,ae()}}}async function _(S,Y){if(!se.has(S)){se.add(S),pe.delete(S),ae();try{let j=await fetch(`/api/workflows/runs/${encodeURIComponent(t)}/attempts/${encodeURIComponent(Y)}/${encodeURIComponent(S)}/resume`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({})});if(j.status===401)throw new Error(a("workflow.detail.writeAccessResume"));let A=await j.json().catch(()=>({}));if(!j.ok||!A.ok||!A.resumeId||!A.url)throw new Error(A.hint??A.message??A.error??a("workflow.detail.resumeStartFailed",{status:j.status}));ee.set(S,{resumeId:A.resumeId,url:A.url})}catch(j){let A=j?.message??String(j);pe.set(S,A)}finally{se.delete(S),ae()}}}async function _e(S,Y){if(!se.has(S)){se.add(S),pe.delete(S),ae();try{let j=await fetch(`/api/workflows/runs/${encodeURIComponent(t)}/attempts/${encodeURIComponent(Y)}/${encodeURIComponent(S)}/resume/end`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({reason:"ended_by_dashboard"})});if(j.status===401)throw new Error(a("workflow.detail.writeAccessResume"));let A=await j.json().catch(()=>({}));if(!j.ok||!A.ok)if(A.error==="resume_not_running")ee.delete(S);else throw new Error(A.hint??A.message??A.error??a("workflow.detail.resumeEndFailed",{status:j.status}));else ee.delete(S)}catch(j){let A=j?.message??String(j);pe.set(S,A)}finally{se.delete(S),ae()}}}async function Ae(S,Y){if(!Te.has(S)){Te.add(S),He.delete(S),ae();try{let j=we.get(S)?.trim()||void 0,A=await fetch(`/api/workflows/runs/${encodeURIComponent(t)}/${Y}`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({comment:j})});if(A.status===401)throw new Error(a("workflow.detail.writeAccessApproval"));let R=await A.json().catch(()=>({}));if(!A.ok||!R.ok)throw new Error(R.hint??R.message??R.error??a("workflow.detail.actionHttp",{action:Y,status:A.status}));let O=Y==="approve"?a("workflow.detail.approved"):a("workflow.detail.rejected");He.set(S,{kind:"ok",text:R.alreadyTerminal?a("workflow.detail.alreadyTerminal",{label:O}):R.pending?a("workflow.detail.workflowContinue",{label:O}):a("workflow.detail.workflowRefreshing",{label:O})}),X(null),await B()}catch(j){let A=j?.message??String(j);He.set(S,{kind:"error",text:A}),X(A)}finally{Te.delete(S),ae()}}}function ae(){if(!g)return;Le=T.scrollTop;let S=g.run;it.has(S.status)&&u(null),s.innerHTML=`${m(S.workflowId??"?")} \xB7 ${xe(S.status)} \xB7 lastSeq ${g.lastSeq}`,o.textContent=a("workflow.detail.refreshed",{time:new Date().toLocaleTimeString()}),f.hidden=it.has(S.status),f.disabled=ue||!g.chatBinding?.larkAppId,f.textContent=g.chatBinding?.larkAppId?a("workflow.detail.cancel"):a("workflow.detail.cliCancelOnly"),f.title=g.chatBinding?.larkAppId?a("workflow.detail.cancelTitle"):a("workflow.detail.cliCancelTitle",{runId:t}),cs(c,g),ms(w,g),fs(h,y,g,k),vs(v,g),ks(p,g,V,ne,{comments:we,statuses:He,resolving:Te,onResolve:Ae},{sessions:ee,pending:se,errors:pe,onStart:_,onEnd:_e},Me,te)&&(Me=void 0),Js(q,k),T.scrollTop=Le,d.hidden=!C,$.textContent=a("workflow.detail.eventsLoaded",{loaded:k.length,total:U})}function qe(){if(G!==null&&window.clearTimeout(G),g&&it.has(g.run.status)){G=null;return}G=window.setTimeout(async()=>{await B(),Q||qe()},is)}function oe(){document.hidden||B().then(()=>{!Q&&G===null&&qe()})}return d.addEventListener("click",()=>{P()}),f.addEventListener("click",()=>{Z()}),document.addEventListener("visibilitychange",oe),M().then(()=>{X(null),Q||qe()}).catch(S=>{X(S?.message??String(S)),s.textContent=a("workflow.detail.loadFailed")}),()=>{Q=!0,G!==null&&window.clearTimeout(G),document.removeEventListener("visibilitychange",oe)}}function cs(e,t){let n=t.run,s=[[a("workflow.summary.workflow"),m(n.workflowId??"?")],[a("workflow.summary.status"),xe(n.status)],[a("workflow.summary.lastSeq"),String(t.lastSeq)],[a("workflow.summary.updated"),m(new Date(t.updatedAt).toLocaleString())],[a("workflow.summary.revision"),m(Et(n.revisionId))],[a("workflow.summary.initiator"),m(n.initiator??"-")]];n.failedNodeId&&s.push([a("workflow.summary.failedNode"),m(n.failedNodeId)]),n.cancelOriginEventId&&s.push([a("workflow.summary.cancelOrigin"),m(n.cancelOriginEventId)]),t.chatBinding&&(s.push([a("workflow.summary.chat"),`<code>${m(t.chatBinding.chatId)}</code>`]),s.push([a("workflow.summary.app"),`<code>${m(t.chatBinding.larkAppId)}</code>`])),e.innerHTML=s.map(([o,i])=>`<div class="wf-summary-item"><span>${o}</span><strong>${i}</strong></div>`).join("")}function us(e){if(!e.errorCode)return"";let t=e.errorMessage?` \u2014 ${Qs(e.errorMessage,96)}`:"";return`<div class="wf-run-error">
|
|
925
995
|
<span class="muted error">${m(e.errorCode)}</span>${m(t)}
|
|
926
|
-
</div>`}function
|
|
996
|
+
</div>`}function ps(e){let t=e.dangling;return{total:new Set([...t.activities,...t.effectAttempted,...t.waits,...t.cancels]).size,effects:t.effectAttempted.length,activities:t.activities.length,waits:t.waits.length,cancels:t.cancels.length}}function ms(e,t){let n=t.dangling,s=[[a("workflow.dangling.activities"),n.activities],[a("workflow.dangling.effects"),n.effectAttempted],[a("workflow.dangling.waits"),n.waits],[a("workflow.dangling.cancels"),n.cancels]],o=new Set(s.flatMap(([,i])=>i)).size;if(e.className=o>0?"wf-panel wf-dangling-panel has":"wf-panel wf-dangling-panel",o===0){e.innerHTML=`<div class="wf-panel-title"><h3>${m(a("workflow.detail.dangling"))}</h3></div><div class="muted">${m(a("workflow.detail.noDangling"))}</div>`;return}e.innerHTML=`<div class="wf-panel-title"><h3>${m(a("workflow.detail.dangling"))}</h3><span class="wf-dangling has">${o}</span></div>
|
|
927
997
|
<div class="wf-dangling-grid">
|
|
928
|
-
${s.map(([i,l])=>`<div><strong>${i}</strong>${l.length===0?`<div class="muted">${m(
|
|
929
|
-
</div>`}function
|
|
930
|
-
<span title="${m(new Date(l).toISOString())}">${m(
|
|
931
|
-
<span title="${m(new Date(
|
|
998
|
+
${s.map(([i,l])=>`<div><strong>${i}</strong>${l.length===0?`<div class="muted">${m(a("workflow.detail.none"))}</div>`:`<ul>${l.map(c=>`<li><code>${m(c)}</code></li>`).join("")}</ul>`}</div>`).join("")}
|
|
999
|
+
</div>`}function fs(e,t,n,s){let o=gs(s,n);if(o.length===0){t.textContent="",e.innerHTML=`<div class="empty">${m(a("workflow.detail.noParallelData"))}</div>`;return}let i=Date.now(),l=Math.min(...o.map(p=>p.startedAt)),c=Math.max(...o.map(p=>p.endedAt??i),l+1e3),w=Math.max(1,c-l),h=hs(o,i),y=o.filter(p=>!p.endedAt&&(p.status==="running"||p.status==="effectAttempting")).length;t.textContent=a("workflow.detail.parallelMeta",{count:o.length,max:h,running:y});let v=o.sort((p,T)=>p.startedAt-T.startedAt||p.activityId.localeCompare(T.activityId)).map(p=>bs(p,l,w,i)).join("");e.innerHTML=`<div class="wf-parallel-axis">
|
|
1000
|
+
<span title="${m(new Date(l).toISOString())}">${m(Mt(l))}</span>
|
|
1001
|
+
<span title="${m(new Date(c).toISOString())}">${m(Mt(c))}</span>
|
|
932
1002
|
</div>
|
|
933
|
-
<div class="wf-parallel-list">${
|
|
1003
|
+
<div class="wf-parallel-list">${v}</div>`}function gs(e,t){let n=new Map,s=new Map(t.activities.map(o=>[o.activityId,o.ownerNodeId]));for(let o of[...e].sort((i,l)=>rt(i.eventId)-rt(l.eventId))){let i=Ys(o);if(!i)continue;let l=typeof i.activityId=="string"?i.activityId:void 0,c=typeof i.attemptId=="string"?i.attemptId:void 0;if(!l||!c)continue;let w=n.get(c);if(o.type==="attemptCreated"){let h=typeof i.attemptNumber=="number"?i.attemptNumber:void 0;w={nodeId:typeof i.nodeId=="string"?i.nodeId:s.get(l),activityId:l,attemptId:c,attemptNumber:h,status:"pending",startedAt:o.timestamp},n.set(c,w);continue}w||(w={nodeId:s.get(l),activityId:l,attemptId:c,status:"pending",startedAt:o.timestamp},n.set(c,w)),o.type==="activityRunning"?(w.status="running",w.runningAt=o.timestamp):o.type==="effectAttempted"?w.status="effectAttempting":o.type==="activityWaiting"||o.type==="waitCreated"?w.status="waiting":ws(o.type)&&(w.status=ys(o.type),w.endedAt=o.timestamp,w.endType=o.type)}return[...n.values()]}function bs(e,t,n,s){let o=e.endedAt??s,i=ga((e.startedAt-t)/n*100,0,100),l=ga((Math.max(o,e.startedAt+1)-e.startedAt)/n*100,.7,100-i),c=e.nodeId??e.activityId,w=e.attemptNumber!==void 0?`#${e.attemptNumber}`:Et(e.attemptId),h=[`${c} ${e.status}`,`${new Date(e.startedAt).toISOString()} \u2192 ${e.endedAt?new Date(e.endedAt).toISOString():a("workflow.detail.parallelNow")}`,e.endType?`end: ${e.endType}`:void 0].filter(Boolean).join(`
|
|
934
1004
|
`);return`<div class="wf-parallel-row">
|
|
935
1005
|
<div class="wf-parallel-label">
|
|
936
|
-
<code>${m(
|
|
937
|
-
<span class="muted">${m(e.activityId)} \xB7 ${m(
|
|
1006
|
+
<code>${m(c)}</code>
|
|
1007
|
+
<span class="muted">${m(e.activityId)} \xB7 ${m(w)}</span>
|
|
938
1008
|
</div>
|
|
939
1009
|
<div class="wf-parallel-track">
|
|
940
|
-
<div class="wf-parallel-bar wf-parallel-${m(e.status)}" style="left:${i.toFixed(3)}%;width:${l.toFixed(3)}%;" title="${m(
|
|
941
|
-
<span>${m(
|
|
1010
|
+
<div class="wf-parallel-bar wf-parallel-${m(e.status)}" style="left:${i.toFixed(3)}%;width:${l.toFixed(3)}%;" title="${m(h)}">
|
|
1011
|
+
<span>${m(Oe(e.status))}</span>
|
|
942
1012
|
</div>
|
|
943
1013
|
</div>
|
|
944
|
-
</div>`}function
|
|
1014
|
+
</div>`}function hs(e,t){let n=[];for(let i of e)n.push({time:i.startedAt,delta:1}),n.push({time:i.endedAt??t,delta:-1});n.sort((i,l)=>i.time-l.time||l.delta-i.delta);let s=0,o=0;for(let i of n)s+=i.delta,o=Math.max(o,s);return o}function ws(e){return e==="activitySucceeded"||e==="activityFailed"||e==="activityTimedOut"||e==="activityCanceled"}function ys(e){return e==="activitySucceeded"?"succeeded":e==="activityCanceled"?"cancelled":e==="activityTimedOut"?"timedOut":"failed"}function vs(e,t){let n=new Map(t.activities.map(i=>[i.activityId,i])),s=new Set,o=[];for(let i of t.nodes){let l=(i.activityId?n.get(i.activityId):void 0)??t.activities.find(c=>c.ownerNodeId===i.nodeId);l&&s.add(l.activityId),o.push(ma(i,l))}for(let i of t.activities)s.has(i.activityId)||o.push(ma(void 0,i));e.innerHTML=o.length>0?o.join(""):`<tr><td colspan="7" class="empty">${m(a("workflow.detail.noNodes"))}</td></tr>`}function ma(e,t){let n=t?.attempts[t.attempts.length-1];return`<tr>
|
|
945
1015
|
<td>${e?`<code>${m(e.nodeId)}</code>`:'<span class="muted">-</span>'}</td>
|
|
946
|
-
<td>${e?
|
|
1016
|
+
<td>${e?xe(e.status):'<span class="muted">-</span>'}</td>
|
|
947
1017
|
<td>${t?`<code>${m(t.activityId)}</code>`:'<span class="muted">-</span>'}</td>
|
|
948
|
-
<td>${t?
|
|
1018
|
+
<td>${t?xe(t.status):'<span class="muted">-</span>'}</td>
|
|
949
1019
|
<td>${t?.attempts.length??0}</td>
|
|
950
1020
|
<td>${n?`<code>${m(n.attemptId)}</code>`:'<span class="muted">-</span>'}</td>
|
|
951
|
-
<td>${n?
|
|
952
|
-
</tr>`}function
|
|
1021
|
+
<td>${n?Ws(n):`<span class="muted">${m(a("workflow.detail.idle"))}</span>`}</td>
|
|
1022
|
+
</tr>`}function ks(e,t,n,s,o,i,l,c){Us(e,n,s),qs(e,o.comments);let w=!!(l&&t.attemptIO?.[l]?.terminal);w&&l&&n.add(an(l,a("workflow.detail.liveTerminal")));let h=$s(t),y=new Set;if(c){for(let p of h){y.add(p.key);let T=c.get(p.key);T||(T=Ss(p.key),c.set(p.key,T),e.appendChild(T.article)),Is(T,p,n,o,i,l)}for(let[p,T]of Array.from(c))y.has(p)||(T.article.remove(),c.delete(p));if(h.length===0){if(!e.querySelector(".wf-io-empty-placeholder")){let p=document.createElement("div");p.className="empty wf-io-empty-placeholder",p.textContent=a("workflow.detail.noNodeIO"),e.appendChild(p)}}else e.querySelector(".wf-io-empty-placeholder")?.remove()}else{let p=[];for(let T of h)p.push(xs(T,n,o,i,l));e.innerHTML=p.length>0?p.join(""):`<div class="empty">${m(a("workflow.detail.noNodeIO"))}</div>`}zs(e,s);let v=Ps(e,l);return js(e,n),_s(e,s),Ns(e,o),Ta(e,i),v&&w}function $s(e){let t=new Map(e.activities.map(o=>[o.activityId,o])),n=new Set,s=[];for(let o of e.nodes){let i=(o.activityId?t.get(o.activityId):void 0)??e.activities.find(l=>l.ownerNodeId===o.nodeId);if(!i){s.push({key:`node:${o.nodeId}`,node:o});continue}n.add(i.activityId),s.push({key:`activity:${i.activityId}`,node:o,activity:i,io:e.attemptIO?.[Lt(i)?.attemptId??""]})}for(let o of e.activities)n.has(o.activityId)||s.push({key:`activity:${o.activityId}`,activity:o,io:e.attemptIO?.[Lt(o)?.attemptId??""]});return s}function Ss(e){let t=document.createElement("article");t.className="wf-io-card",t.dataset.wfCardKey=e;let n=document.createElement("div");n.className="wf-io-card-head";let s=document.createElement("div");s.className="wf-io-terminal-slot";let o=document.createElement("div");return o.className="wf-io-grid",t.appendChild(n),t.appendChild(s),t.appendChild(o),{article:t,head:n,terminalSlot:s,grid:o,currentTerminalUrl:null}}function Is(e,t,n,s,o,i){let l=Lt(t.activity),c=t.node?.nodeId??t.activity?.ownerNodeId??t.activity?.activityId??"unknown",w=!!(l&&l.attemptId===i);e.article.classList.toggle("is-focused",w),l?e.article.dataset.wfAttemptCard=l.attemptId:delete e.article.dataset.wfAttemptCard;let h=Ia(l,s);e.head.innerHTML=`
|
|
953
1023
|
<header>
|
|
954
1024
|
<div>
|
|
955
|
-
<strong><code>${m(
|
|
956
|
-
<span class="muted">${t.activity?m(t.activity.activityId):m(
|
|
1025
|
+
<strong><code>${m(c)}</code></strong>
|
|
1026
|
+
<span class="muted">${t.activity?m(t.activity.activityId):m(a("workflow.detail.notDispatched"))}</span>
|
|
957
1027
|
</div>
|
|
958
|
-
<div>${t.node?
|
|
1028
|
+
<div>${t.node?xe(t.node.status):""} ${t.activity?xe(t.activity.status):""}</div>
|
|
959
1029
|
</header>
|
|
960
1030
|
<div class="wf-io-meta">
|
|
961
|
-
${l?`${m(
|
|
1031
|
+
${l?`${m(a("workflow.detail.attempt"))} <code>${m(l.attemptId)}</code>`:m(a("workflow.detail.noAttempt"))}
|
|
962
1032
|
</div>
|
|
963
|
-
${
|
|
964
|
-
`;let
|
|
965
|
-
${
|
|
966
|
-
${
|
|
967
|
-
${
|
|
968
|
-
${
|
|
969
|
-
${t.io?.waitPrompt?
|
|
970
|
-
`}function
|
|
971
|
-
<summary>${m(
|
|
1033
|
+
${h}
|
|
1034
|
+
`;let y=ha(l,t.activity,t.io?.terminal,o),v=y?.url??null;if(v!==e.currentTerminalUrl)y===null?e.terminalSlot.innerHTML="":e.terminalSlot.innerHTML=wa(t.key,l,t.activity,t.io?.terminal,y,n,o),e.currentTerminalUrl=v;else if(y!==null&&t.io?.terminal){let T=e.terminalSlot.querySelector("details.wf-terminal-block > summary");if(T){let q=ya(y.kind);T.innerHTML=`${m(q)} ${Sa(l,t.io.terminal)}`}l&&Bs(e.terminalSlot,l,t.activity,t.io.terminal,y,o)}let p=l?.attemptId??t.activity?.activityId??t.node?.nodeId??"unknown";e.grid.innerHTML=`
|
|
1035
|
+
${Ie(p,a("workflow.detail.authoredInput"),t.io?.input,n)}
|
|
1036
|
+
${Ie(p,a("workflow.detail.resolvedInput"),t.io?.resolvedInput,n)}
|
|
1037
|
+
${Ie(p,a("workflow.detail.output"),t.io?.output,n)}
|
|
1038
|
+
${Ie(p,a("workflow.detail.executionLog"),t.io?.log,n)}
|
|
1039
|
+
${t.io?.waitPrompt?Ie(p,a("workflow.detail.waitPrompt"),t.io.waitPrompt,n):""}
|
|
1040
|
+
`}function ha(e,t,n,s){if(!n||n.error)return null;if(Hs(e,n))return{kind:"live",url:Cs(n)};if(!e||!t||!As(e,n))return null;let o=Rs();if(!o)return null;let i=s?.sessions.get(e.attemptId);return i?{kind:"resume",url:i.url,resumeId:i.resumeId,downloadUrl:fa(o,t.activityId,e.attemptId)}:{kind:"replay",url:Ds(o,t.activityId,e.attemptId,!!n.hasPtyLog),downloadUrl:fa(o,t.activityId,e.attemptId)}}function wa(e,t,n,s,o,i,l){if(!s)return"";let c=ya(o.kind),w=an(e,c),h=Sa(t,s),y=Ts(o.kind),v=o.kind==="replay"||o.kind==="resume"?`<a class="btn-link" href="${m(o.downloadUrl)}" download>${m(a("workflow.detail.downloadFullLog"))}</a>`:"",p=t?ka(t,n,s,o,l):"",T=t?$a(t.attemptId,l):"";return`<details class="wf-io-block wf-terminal-block" data-io-key="${m(w)}"${i.has(w)?" open":""}>
|
|
1041
|
+
<summary>${m(c)} ${h}</summary>
|
|
972
1042
|
<div class="wf-terminal-actions">
|
|
973
|
-
<a class="btn-link" href="${m(
|
|
974
|
-
${
|
|
975
|
-
${
|
|
1043
|
+
<a class="btn-link" href="${m(o.url)}" target="_blank" rel="noreferrer">${m(y)}</a>
|
|
1044
|
+
${v}
|
|
1045
|
+
${p}
|
|
976
1046
|
</div>
|
|
977
|
-
${
|
|
978
|
-
<iframe class="wf-terminal-frame" src="${m(
|
|
979
|
-
</details>`}function
|
|
1047
|
+
${T}
|
|
1048
|
+
<iframe class="wf-terminal-frame" src="${m(o.url)}" title="${m(c)}" loading="lazy"></iframe>
|
|
1049
|
+
</details>`}function ya(e){return e==="live"?a("workflow.detail.liveTerminal"):e==="resume"?a("workflow.detail.terminalResume"):a("workflow.detail.terminalReplay")}function Ts(e){return e==="live"?a("workflow.detail.openTerminalNewTab"):e==="resume"?a("workflow.detail.openResumeNewTab"):a("workflow.detail.openReplayNewTab")}var va=new Set(["antigravity","codex-app","cursor","mira"]),Ls=new Set(["aiden","coco","claude-code","seed","codex","mtr","hermes","pi"]);function Ms(e){return!!e&&(Ls.has(e)||va.has(e))}function Es(e){return!!e&&va.has(e)}function ka(e,t,n,s,o){if(!o||s.kind==="live"||!t)return"";let i=s.kind==="resume",l=o.pending.has(e.attemptId),c=`data-wf-resume-attempt="${m(e.attemptId)}" data-wf-resume-activity="${m(t.activityId)}"`;return i?`<button type="button" class="btn-link" data-wf-resume-button="1" data-wf-resume-action="end" ${c}${l?" disabled":""}>${m(l?a("workflow.detail.resumeEnding"):a("workflow.detail.endResumeSession"))}</button>`:Ms(n.cliId)?Es(n.cliId)&&!n.cliSessionId?`<button type="button" class="btn-link" data-wf-resume-button="1" disabled title="${m(a("workflow.detail.resumeMissingCliSession"))}">${m(a("workflow.detail.resumeSession"))}</button>`:`<button type="button" class="btn-link" data-wf-resume-button="1" data-wf-resume-action="start" ${c}${l?" disabled":""}>${m(l?a("workflow.detail.resumeStarting"):a("workflow.detail.resumeSession"))}</button>`:`<button type="button" class="btn-link" data-wf-resume-button="1" disabled title="${m(a("workflow.detail.resumeUnsupportedCli",{cliId:n.cliId??"?"}))}">${m(a("workflow.detail.resumeSession"))}</button>`}function $a(e,t){if(!t)return"";let n=t.errors.get(e);return n?`<div class="hint-warn wf-resume-status" data-wf-resume-status="${m(e)}">${m(n)}</div>`:""}function xs(e,t,n,s,o){let i=Lt(e.activity),l=e.node?.nodeId??e.activity?.ownerNodeId??e.activity?.activityId??"unknown",c=i?.attemptId??e.activity?.activityId??e.node?.nodeId??"unknown",w=Ia(i,n),h=i?.attemptId===o?" is-focused":"",y=i?` data-wf-attempt-card="${m(i.attemptId)}"`:"",v=ha(i,e.activity,e.io?.terminal,s),p=v?wa(c,i,e.activity,e.io?.terminal,v,t,s):"";return`<article class="wf-io-card${h}" data-wf-card-key="${m(e.key)}"${y}>
|
|
980
1050
|
<div class="wf-io-card-head">
|
|
981
1051
|
<header>
|
|
982
1052
|
<div>
|
|
983
1053
|
<strong><code>${m(l)}</code></strong>
|
|
984
|
-
<span class="muted">${e.activity?m(e.activity.activityId):m(
|
|
1054
|
+
<span class="muted">${e.activity?m(e.activity.activityId):m(a("workflow.detail.notDispatched"))}</span>
|
|
985
1055
|
</div>
|
|
986
|
-
<div>${e.node?
|
|
1056
|
+
<div>${e.node?xe(e.node.status):""} ${e.activity?xe(e.activity.status):""}</div>
|
|
987
1057
|
</header>
|
|
988
1058
|
<div class="wf-io-meta">
|
|
989
|
-
${i?`${m(
|
|
1059
|
+
${i?`${m(a("workflow.detail.attempt"))} <code>${m(i.attemptId)}</code>`:m(a("workflow.detail.noAttempt"))}
|
|
990
1060
|
</div>
|
|
991
|
-
${
|
|
1061
|
+
${w}
|
|
992
1062
|
</div>
|
|
993
|
-
<div class="wf-io-terminal-slot">${
|
|
1063
|
+
<div class="wf-io-terminal-slot">${p}</div>
|
|
994
1064
|
<div class="wf-io-grid">
|
|
995
|
-
${
|
|
996
|
-
${
|
|
997
|
-
${
|
|
998
|
-
${
|
|
999
|
-
${e.io?.waitPrompt?
|
|
1065
|
+
${Ie(c,a("workflow.detail.authoredInput"),e.io?.input,t)}
|
|
1066
|
+
${Ie(c,a("workflow.detail.resolvedInput"),e.io?.resolvedInput,t)}
|
|
1067
|
+
${Ie(c,a("workflow.detail.output"),e.io?.output,t)}
|
|
1068
|
+
${Ie(c,a("workflow.detail.executionLog"),e.io?.log,t)}
|
|
1069
|
+
${e.io?.waitPrompt?Ie(c,a("workflow.detail.waitPrompt"),e.io.waitPrompt,t):""}
|
|
1000
1070
|
</div>
|
|
1001
|
-
</article>`}function
|
|
1071
|
+
</article>`}function Lt(e){return e?.attempts[e.attempts.length-1]}function Sa(e,t){let n=[];return t.error?n.push(a("workflow.detail.error")):n.push(t.status==="live"?a("workflow.detail.terminalLive"):a("workflow.detail.terminalClosedShort")),e?.status&&n.push(e.status),t.webPort>0&&n.push(`:${t.webPort}`),`<span class="muted">${m(n.join(" \xB7 "))}</span>`}function Hs(e,t){return t.status==="live"&&t.webPort>0&&(e?.status==="pending"||e?.status==="running"||e?.status==="effectAttempting")}function As(e,t){return e.status==="succeeded"||e.status==="failed"||e.status==="cancelled"||e.status==="timedOut"?!!(t.sessionId||t.startedAt):!1}function Cs(e){return`http://${window.location.hostname||"127.0.0.1"}:${e.webPort}`}function Ds(e,t,n,s){let o=new URLSearchParams({runId:e,activityId:t,attemptId:n});return s&&o.set("hasPtyLog","1"),`/assets/terminal-replay.html?${o.toString()}`}function fa(e,t,n){return`/api/workflows/runs/${encodeURIComponent(e)}/attempts/${encodeURIComponent(t)}/${encodeURIComponent(n)}/terminal-log/raw?download=1`}function Rs(){let e=window.location.hash.match(/^#\/workflows\/([^/?#]+)/);if(!e)return null;try{return decodeURIComponent(e[1])}catch{return null}}function Ia(e,t){if(!Os(e))return"";let n=e.attemptId,s=t.comments.get(n)??"",o=t.resolving.has(n),i=t.statuses.get(n),l=i?.kind==="error"?"hint-warn":"hint-ok";return`<div class="wf-approval-box" data-wf-approval="${m(n)}">
|
|
1002
1072
|
<label>
|
|
1003
|
-
<span>${m(
|
|
1004
|
-
<textarea class="wf-approval-comment" data-wf-approval-comment="${m(n)}" rows="2" placeholder="${m(
|
|
1073
|
+
<span>${m(a("workflow.detail.approvalComment"))}</span>
|
|
1074
|
+
<textarea class="wf-approval-comment" data-wf-approval-comment="${m(n)}" rows="2" placeholder="${m(a("workflow.detail.optionalComment"))}"${o?" disabled":""}>${m(s)}</textarea>
|
|
1005
1075
|
</label>
|
|
1006
1076
|
<div class="wf-approval-actions">
|
|
1007
|
-
<button type="button" class="primary" data-wf-approval-action="approve" data-wf-attempt-id="${m(n)}"${
|
|
1008
|
-
<button type="button" data-wf-approval-action="reject" data-wf-attempt-id="${m(n)}"${
|
|
1009
|
-
${
|
|
1077
|
+
<button type="button" class="primary" data-wf-approval-action="approve" data-wf-attempt-id="${m(n)}"${o?" disabled":""}>${m(a("workflow.detail.approve"))}</button>
|
|
1078
|
+
<button type="button" data-wf-approval-action="reject" data-wf-attempt-id="${m(n)}"${o?" disabled":""}>${m(a("workflow.detail.reject"))}</button>
|
|
1079
|
+
${o?`<span class="muted">${m(a("workflow.detail.submitting"))}</span>`:""}
|
|
1010
1080
|
</div>
|
|
1011
1081
|
${i?`<div class="${l} wf-approval-status">${m(i.text)}</div>`:""}
|
|
1012
|
-
</div>`}function
|
|
1013
|
-
<summary>${m(t)} ${
|
|
1014
|
-
${
|
|
1015
|
-
</details>`}function
|
|
1016
|
-
<td>${
|
|
1082
|
+
</div>`}function Os(e){return!!e&&e.status==="waiting"&&e.wait?.waitKind==="human-gate"&&!e.wait.resolution}function qs(e,t){e.querySelectorAll("textarea[data-wf-approval-comment]").forEach(n=>{let s=n.dataset.wfApprovalComment;s&&t.set(s,n.value)})}function Ta(e,t){e.querySelectorAll("button[data-wf-resume-action][data-wf-resume-attempt][data-wf-resume-activity]").forEach(n=>{n.dataset.wfResumeBound!=="1"&&(n.dataset.wfResumeBound="1",n.addEventListener("click",()=>{let s=n.dataset.wfResumeAttempt,o=n.dataset.wfResumeActivity,i=n.dataset.wfResumeAction;!s||!o||(i==="start"?t.onStart(s,o):i==="end"&&t.onEnd(s,o))}))})}function Bs(e,t,n,s,o,i){let l=e.querySelector(".wf-terminal-actions");if(!l)return;let c=l.querySelector('button[data-wf-resume-button="1"]'),w=ka(t,n,s,o,i);c?c.outerHTML=w:w&&l.insertAdjacentHTML("beforeend",w);let h=e.querySelector("details.wf-terminal-block");if(h){let y=h.querySelector(".wf-resume-status"),v=$a(t.attemptId,i);y?y.outerHTML=v:v&&l.insertAdjacentHTML("afterend",v)}Ta(e,i)}function Ns(e,t){e.querySelectorAll("textarea[data-wf-approval-comment]").forEach(n=>{let s=n.dataset.wfApprovalComment;s&&n.addEventListener("input",()=>{t.comments.set(s,n.value)})}),e.querySelectorAll("button[data-wf-approval-action][data-wf-attempt-id]").forEach(n=>{n.addEventListener("click",()=>{let s=n.dataset.wfAttemptId,o=n.dataset.wfApprovalAction;!s||o!=="approve"&&o!=="reject"||t.onResolve(s,o)})})}function Ie(e,t,n,s){let o=an(e,t);return`<details class="wf-io-block" data-io-key="${m(o)}"${s.has(o)?" open":""}>
|
|
1083
|
+
<summary>${m(t)} ${Fs(n)}</summary>
|
|
1084
|
+
${Gs(n)}
|
|
1085
|
+
</details>`}function an(e,t){return`${e}:${t}`}function Ps(e,t){if(!t)return!1;for(let n of e.querySelectorAll("[data-wf-attempt-card]"))if(n.dataset.wfAttemptCard===t)return n.scrollIntoView({block:"center"}),!0;return!1}function Us(e,t,n){e.querySelectorAll("details.wf-io-block[data-io-key]").forEach(s=>{let o=s.dataset.ioKey;if(!o)return;s.open?t.add(o):t.delete(o);let i=s.querySelector(".wf-io-pre");i&&n.set(o,i.scrollTop)})}function js(e,t){e.querySelectorAll("details.wf-io-block[data-io-key]").forEach(n=>{n.dataset.ioToggleBound!=="1"&&(n.dataset.ioToggleBound="1",n.addEventListener("toggle",()=>{let s=n.dataset.ioKey;s&&(n.open?t.add(s):t.delete(s))}))})}function zs(e,t){e.querySelectorAll("details.wf-io-block[data-io-key]").forEach(n=>{let s=n.dataset.ioKey;if(!s)return;let o=t.get(s);if(o===void 0)return;let i=n.querySelector(".wf-io-pre");i&&(i.scrollTop=o)})}function _s(e,t){e.querySelectorAll("details.wf-io-block[data-io-key]").forEach(n=>{let s=n.dataset.ioKey;if(!s)return;let o=n.querySelector(".wf-io-pre");o&&o.dataset.ioScrollBound!=="1"&&(o.dataset.ioScrollBound="1",o.addEventListener("scroll",()=>{t.set(s,o.scrollTop)}))})}function Fs(e){if(!e)return`<span class="muted">${m(a("workflow.detail.empty"))}</span>`;let t=[];return e.outputBytes!==void 0&&t.push(`${e.outputBytes}B`),e.truncated&&t.push(a("workflow.detail.truncated")),e.error&&t.push(a("workflow.detail.error")),e.outputHash&&t.push(Et(e.outputHash)),t.length?`<span class="muted">${m(t.join(" \xB7 "))}</span>`:""}function Gs(e){if(!e)return`<div class="muted wf-io-empty">${m(a("workflow.detail.noData"))}</div>`;let t=e.value!==void 0?JSON.stringify(e.value,null,2):e.text??"",n=e.error?`<div class="muted error">${m(e.error)}</div>`:"";return t?`${n}<pre class="wf-io-pre">${m(t)}</pre>`:`${n}<div class="muted wf-io-empty">${m(a("workflow.detail.noPreview"))}</div>`}function Ws(e){let t=[];if(e.effectAttempted&&t.push(`${m(a("workflow.detail.effect"))} ${m(e.effectAttempted.provider)}`),e.wait){let n=e.wait.resolution?`${e.wait.resolution.kind}${e.wait.resolution.resolution?":"+e.wait.resolution.resolution:""}`:a("workflow.detail.open");t.push(`${m(a("workflow.detail.wait"))} ${m(e.wait.waitKind)} ${m(n)}`),e.wait.deadlineAt!==void 0&&t.push(`${m(a("workflow.detail.deadline"))} ${m(Mt(e.wait.deadlineAt))}`)}if(e.error){let n=`${e.error.errorCode}${e.error.errorClass?` \xB7 ${e.error.errorClass}`:""}`;t.push(`<span class="muted error">${m(n)}</span>`),e.error.errorMessage&&t.push(`<span class="error wf-error-msg">${m(e.error.errorMessage)}</span>`)}return e.output&&t.push(`${m(a("workflow.detail.output"))} ${m(Et(e.output.outputHash))}`),e.runningMs!==void 0&&t.push(`${e.runningMs}ms`),t.length>0?t.join("<br/>"):'<span class="muted">-</span>'}function Js(e,t){e.innerHTML=t.length>0?t.map(Ks).join(""):`<tr><td colspan="7" class="empty">${m(a("workflow.detail.noEvents"))}</td></tr>`}function Ks(e){let t=Vs(e.payload);return`<tr>
|
|
1086
|
+
<td>${rt(e.eventId)}</td>
|
|
1017
1087
|
<td><code>${m(e.type)}</code></td>
|
|
1018
1088
|
<td>${m(e.actor)}</td>
|
|
1019
1089
|
<td>${t.nodeId?`<code>${m(t.nodeId)}</code>`:"-"}</td>
|
|
1020
1090
|
<td>${t.activityId?`<code>${m(t.activityId)}</code>`:"-"}</td>
|
|
1021
1091
|
<td>${t.errorCode?`<span class="muted error">${m(t.errorCode)}</span>`:"-"}</td>
|
|
1022
|
-
<td title="${m(new Date(e.timestamp).toISOString())}">${m(
|
|
1023
|
-
</tr>`}function
|
|
1092
|
+
<td title="${m(new Date(e.timestamp).toISOString())}">${m(Mt(e.timestamp))}</td>
|
|
1093
|
+
</tr>`}function rt(e){let t=e.lastIndexOf("-");if(t<0)return 0;let n=Number(e.slice(t+1));return Number.isFinite(n)?n:0}function Vs(e){if(!e||typeof e!="object"||"ref"in e)return{};let t=e,n={};typeof t.nodeId=="string"&&(n.nodeId=t.nodeId),typeof t.activityId=="string"&&(n.activityId=t.activityId),typeof t.failedNodeId=="string"&&(n.nodeId=t.failedNodeId);let s=t.error;return s&&typeof s=="object"&&"errorCode"in s&&(n.errorCode=String(s.errorCode)),n}function Ys(e){return!e.payload||typeof e.payload!="object"||"ref"in e.payload?null:e.payload}function ga(e,t,n){return Math.min(n,Math.max(t,e))}function Et(e){return e?e.length>18?e.slice(0,10)+"..."+e.slice(-6):e:"-"}function Qs(e,t){return e.length>t?e.slice(0,t-1)+"\u2026":e}function Mt(e){return new Date(e).toLocaleTimeString([],{hour:"2-digit",minute:"2-digit",second:"2-digit"})}function D(e){return e.replace(/[&<>"']/g,t=>({"&":"&","<":"<",">":">",'"':""","'":"'"})[t])}function La(e){return e?e.length>18?e.slice(0,10)+"..."+e.slice(-6):e:"-"}function Ma(e){let t=location.hash.match(/^#\/(?:workflows\/catalog|workflows-catalog)\/([^/?#]+)$/);return t?Zs(e,decodeURIComponent(t[1])):Xs(e)}function Xs(e){e.innerHTML=`
|
|
1024
1094
|
<nav class="wf-subnav">
|
|
1025
|
-
<a href="#/workflows" data-i18n="workflow.subnav.runs">${
|
|
1026
|
-
<a href="#/workflows/catalog" class="active" data-i18n="workflow.subnav.catalog">${
|
|
1095
|
+
<a href="#/workflows" data-i18n="workflow.subnav.runs">${D(a("workflow.subnav.runs"))}</a>
|
|
1096
|
+
<a href="#/workflows/catalog" class="active" data-i18n="workflow.subnav.catalog">${D(a("workflow.subnav.catalog"))}</a>
|
|
1027
1097
|
</nav>
|
|
1028
1098
|
<section class="catalog-head">
|
|
1029
1099
|
<div>
|
|
1030
|
-
<h2>${
|
|
1031
|
-
<p class="muted">${
|
|
1100
|
+
<h2>${D(a("catalog.title"))}</h2>
|
|
1101
|
+
<p class="muted">${D(a("catalog.subtitle"))}</p>
|
|
1032
1102
|
</div>
|
|
1033
|
-
<button id="catalog-refresh" type="button">${
|
|
1103
|
+
<button id="catalog-refresh" type="button">${D(a("catalog.refresh"))}</button>
|
|
1034
1104
|
</section>
|
|
1035
1105
|
<form id="catalog-filters" class="filters">
|
|
1036
|
-
<input type="search" name="q" placeholder="${
|
|
1106
|
+
<input type="search" name="q" placeholder="${D(a("catalog.searchPlaceholder"))}" />
|
|
1037
1107
|
<span id="catalog-status" class="muted"></span>
|
|
1038
1108
|
</form>
|
|
1039
1109
|
<div class="wf-table-scroll">
|
|
1040
1110
|
<table>
|
|
1041
1111
|
<thead><tr>
|
|
1042
|
-
<th>${
|
|
1043
|
-
<th>${
|
|
1044
|
-
<th>${
|
|
1045
|
-
<th>${
|
|
1046
|
-
<th>${
|
|
1047
|
-
<th>${
|
|
1112
|
+
<th>${D(a("catalog.table.workflow"))}</th>
|
|
1113
|
+
<th>${D(a("catalog.table.version"))}</th>
|
|
1114
|
+
<th>${D(a("catalog.table.params"))}</th>
|
|
1115
|
+
<th>${D(a("catalog.table.nodes"))}</th>
|
|
1116
|
+
<th>${D(a("catalog.table.revision"))}</th>
|
|
1117
|
+
<th>${D(a("catalog.table.path"))}</th>
|
|
1048
1118
|
</tr></thead>
|
|
1049
1119
|
<tbody id="catalog-tbody"></tbody>
|
|
1050
1120
|
</table>
|
|
1051
1121
|
</div>
|
|
1052
|
-
`;let t=e.querySelector("#catalog-tbody"),n=e.querySelector("#catalog-status"),s=e.querySelector("#catalog-filters"),
|
|
1122
|
+
`;let t=e.querySelector("#catalog-tbody"),n=e.querySelector("#catalog-status"),s=e.querySelector("#catalog-filters"),o=e.querySelector("#catalog-refresh"),i=[],l=null,c=!1;function w(){let v=(new FormData(s).get("q")??"").trim().toLowerCase();return v?i.filter(p=>p.workflowId.toLowerCase().includes(v)||p.path.toLowerCase().includes(v)):i}function h(){l?(n.textContent=a("catalog.loadFailed",{error:l}),n.classList.add("error")):(n.textContent=`${i.length}`,n.classList.remove("error"));let v=w();if(v.length===0){t.innerHTML=`<tr><td colspan="6" class="empty">${i.length===0?D(a("catalog.noDefinitions")):D(a("catalog.noFilterMatch"))}</td></tr>`;return}t.innerHTML=v.map(p=>`
|
|
1053
1123
|
<tr>
|
|
1054
|
-
<td><a href="#/workflows/catalog/${encodeURIComponent(
|
|
1055
|
-
<td>${
|
|
1056
|
-
<td>${
|
|
1057
|
-
<td>${
|
|
1058
|
-
<td><code>${
|
|
1059
|
-
<td><code>${
|
|
1124
|
+
<td><a href="#/workflows/catalog/${encodeURIComponent(p.workflowId)}"><code>${D(p.workflowId)}</code></a></td>
|
|
1125
|
+
<td>${p.version}</td>
|
|
1126
|
+
<td>${D(a("catalog.paramSummary",{required:p.requiredParamCount,total:p.paramCount}))}</td>
|
|
1127
|
+
<td>${p.nodeCount}</td>
|
|
1128
|
+
<td><code>${D(La(p.revisionId))}</code></td>
|
|
1129
|
+
<td><code>${D(p.path)}</code></td>
|
|
1060
1130
|
</tr>
|
|
1061
|
-
`).join("")}async function
|
|
1131
|
+
`).join("")}async function y(){o.disabled=!0,n.textContent=a("catalog.loading");try{let v=await fetch("/api/workflows/definitions");if(!v.ok)throw new Error(`HTTP ${v.status}`);i=(await v.json()).definitions??[],l=null}catch(v){l=v?.message??String(v),i=[]}finally{o.disabled=!1,c||h()}}return s.addEventListener("input",h),o.addEventListener("click",()=>{y()}),y(),()=>{c=!0}}function Zs(e,t){e.innerHTML=`
|
|
1062
1132
|
<div class="catalog-detail-head">
|
|
1063
|
-
<a class="btn-link" href="#/workflows/catalog">${
|
|
1133
|
+
<a class="btn-link" href="#/workflows/catalog">${D(a("catalog.back"))}</a>
|
|
1064
1134
|
<div>
|
|
1065
|
-
<h2><code>${
|
|
1066
|
-
<div id="catalog-detail-subtitle" class="muted">${
|
|
1135
|
+
<h2><code>${D(t)}</code></h2>
|
|
1136
|
+
<div id="catalog-detail-subtitle" class="muted">${D(a("workflow.detail.loading"))}</div>
|
|
1067
1137
|
</div>
|
|
1068
1138
|
</div>
|
|
1069
1139
|
<section id="catalog-error" class="hint-warn" hidden></section>
|
|
1070
1140
|
<section id="catalog-run-status" class="hint-ok" hidden></section>
|
|
1071
1141
|
<div id="catalog-detail-body"></div>
|
|
1072
|
-
`;let n=e.querySelector("#catalog-detail-subtitle"),s=e.querySelector("#catalog-error"),
|
|
1142
|
+
`;let n=e.querySelector("#catalog-detail-subtitle"),s=e.querySelector("#catalog-error"),o=e.querySelector("#catalog-run-status"),i=e.querySelector("#catalog-detail-body"),l=null,c=!1,w=!1;function h(f){s.hidden=!f,s.textContent=f??""}function y(f){o.hidden=!f,o.textContent=f??""}function v(f){let d={};for(let[g,k]of Object.entries(f??{}))"default"in k&&(d[g]=k.default);return d}function p(){if(!l)return;let f=l.definition;n.textContent=`${a("catalog.revision")} ${La(l.revisionId)} \xB7 ${l.path}`;let d=JSON.stringify(v(f.params),null,2);i.innerHTML=`
|
|
1073
1143
|
<section class="wf-panel">
|
|
1074
|
-
<div class="wf-panel-title"><h3>${
|
|
1144
|
+
<div class="wf-panel-title"><h3>${D(a("catalog.summary"))}</h3></div>
|
|
1075
1145
|
<div class="wf-summary-grid">
|
|
1076
|
-
<div class="wf-summary-item"><span>${
|
|
1077
|
-
<div class="wf-summary-item"><span>${
|
|
1078
|
-
<div class="wf-summary-item"><span>${
|
|
1079
|
-
<div class="wf-summary-item"><span>${
|
|
1146
|
+
<div class="wf-summary-item"><span>${D(a("catalog.table.workflow"))}</span><strong><code>${D(f.workflowId)}</code></strong></div>
|
|
1147
|
+
<div class="wf-summary-item"><span>${D(a("catalog.table.version"))}</span><strong>${f.version}</strong></div>
|
|
1148
|
+
<div class="wf-summary-item"><span>${D(a("catalog.nodeCount"))}</span><strong>${Object.keys(f.nodes).length}</strong></div>
|
|
1149
|
+
<div class="wf-summary-item"><span>${D(a("catalog.path"))}</span><strong><code>${D(l.path)}</code></strong></div>
|
|
1080
1150
|
</div>
|
|
1081
1151
|
</section>
|
|
1082
1152
|
|
|
1083
1153
|
<section class="wf-panel">
|
|
1084
|
-
<div class="wf-panel-title"><h3>${
|
|
1154
|
+
<div class="wf-panel-title"><h3>${D(a("catalog.runPanel"))}</h3></div>
|
|
1085
1155
|
<form id="catalog-run-form" class="catalog-run-form">
|
|
1086
1156
|
<label>
|
|
1087
|
-
<span>${
|
|
1088
|
-
<textarea id="catalog-params" rows="8" spellcheck="false" placeholder="${
|
|
1157
|
+
<span>${D(a("catalog.paramsJson"))}</span>
|
|
1158
|
+
<textarea id="catalog-params" rows="8" spellcheck="false" placeholder="${D(a("catalog.paramsPlaceholder"))}">${D(d)}</textarea>
|
|
1089
1159
|
</label>
|
|
1090
1160
|
<div class="catalog-chat-grid">
|
|
1091
1161
|
<label>
|
|
1092
|
-
<span>${
|
|
1162
|
+
<span>${D(a("catalog.chatId"))}</span>
|
|
1093
1163
|
<input id="catalog-chat-id" type="text" autocomplete="off" />
|
|
1094
1164
|
</label>
|
|
1095
1165
|
<label>
|
|
1096
|
-
<span>${
|
|
1166
|
+
<span>${D(a("catalog.larkAppId"))}</span>
|
|
1097
1167
|
<input id="catalog-lark-app-id" type="text" autocomplete="off" />
|
|
1098
1168
|
</label>
|
|
1099
1169
|
</div>
|
|
1100
|
-
<div class="muted">${
|
|
1170
|
+
<div class="muted">${D(a("catalog.chatBindingHint"))}</div>
|
|
1101
1171
|
<div id="catalog-param-errors" class="catalog-param-errors" hidden></div>
|
|
1102
|
-
<button id="catalog-run-btn" type="submit" class="primary">${
|
|
1172
|
+
<button id="catalog-run-btn" type="submit" class="primary">${D(a("catalog.run"))}</button>
|
|
1103
1173
|
</form>
|
|
1104
1174
|
</section>
|
|
1105
1175
|
|
|
1106
1176
|
<section class="wf-panel">
|
|
1107
|
-
<div class="wf-panel-title"><h3>${
|
|
1108
|
-
${
|
|
1177
|
+
<div class="wf-panel-title"><h3>${D(a("catalog.paramsSchema"))}</h3></div>
|
|
1178
|
+
${ei(f.params)}
|
|
1109
1179
|
</section>
|
|
1110
1180
|
|
|
1111
1181
|
<section class="wf-panel">
|
|
1112
|
-
<div class="wf-panel-title"><h3>${
|
|
1113
|
-
<pre class="wf-io-pre">${
|
|
1182
|
+
<div class="wf-panel-title"><h3>${D(a("catalog.definitionJson"))}</h3></div>
|
|
1183
|
+
<pre class="wf-io-pre">${D(JSON.stringify(f,null,2))}</pre>
|
|
1114
1184
|
</section>
|
|
1115
|
-
`,
|
|
1185
|
+
`,q()}async function T(){if(!l||w)return;let f=i.querySelector("#catalog-params"),d=i.querySelector("#catalog-chat-id"),g=i.querySelector("#catalog-lark-app-id"),k=i.querySelector("#catalog-run-btn"),H=i.querySelector("#catalog-param-errors"),E;try{if(E=JSON.parse(f.value||"{}"),!E||typeof E!="object"||Array.isArray(E))throw new Error(a("catalog.badParamsJson"))}catch(x){H.hidden=!1,H.innerHTML=`<div class="muted error">${D(x?.message??String(x))}</div>`;return}w=!0,k.disabled=!0,k.textContent=a("catalog.running"),H.hidden=!0,h(null),y(null);try{let x=await fetch(`/api/workflows/definitions/${encodeURIComponent(l.definition.workflowId)}/run`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({params:E,chatBinding:{chatId:d.value.trim(),larkAppId:g.value.trim()}})});if(x.status===401)throw new Error(a("catalog.writeAccess"));let C=await x.json().catch(()=>({}));if(!x.ok||!C.ok)throw C.issues?.length&&(H.hidden=!1,H.innerHTML=`<strong>${D(a("catalog.invalidParams"))}</strong><ul>${C.issues.map(U=>`<li>${D(a("catalog.issue",{path:U.path.length?U.path.join("."):"(root)",message:U.message}))}</li>`).join("")}</ul>`),new Error(C.hint??C.message??C.error??a("catalog.runHttp",{status:x.status}));y(a("catalog.runStarted")),C.runId&&(location.hash=`#/workflows/${encodeURIComponent(C.runId)}`)}catch(x){h(x?.message??String(x))}finally{w=!1,k.disabled=!1,k.textContent=a("catalog.run")}}function q(){i.querySelector("#catalog-run-form")?.addEventListener("submit",d=>{d.preventDefault(),T()})}async function $(){try{let f=await fetch(`/api/workflows/definitions/${encodeURIComponent(t)}`);if(f.status===404)throw new Error("unknown_workflow");if(!f.ok)throw new Error(`HTTP ${f.status}`);l=await f.json(),h(null),p()}catch(f){h(a("catalog.definitionLoadFailed",{error:f?.message??String(f)})),n.textContent=a("workflow.detail.loadFailed")}}return $().then(()=>{}),()=>{c=!0}}function ei(e){let t=Object.entries(e??{});return t.length===0?`<div class="muted">${D(a("catalog.noParams"))}</div>`:`<div class="catalog-param-list">${t.map(([n,s])=>`
|
|
1116
1186
|
<article class="catalog-param">
|
|
1117
1187
|
<header>
|
|
1118
|
-
<code>${
|
|
1119
|
-
<span class="wf-status">${
|
|
1120
|
-
<span class="muted">${
|
|
1188
|
+
<code>${D(n)}</code>
|
|
1189
|
+
<span class="wf-status">${D(s.required?a("catalog.required"):a("catalog.optional"))}</span>
|
|
1190
|
+
<span class="muted">${D(s.type)}${s.format?` \xB7 ${D(s.format)}`:""}</span>
|
|
1121
1191
|
</header>
|
|
1122
|
-
${s.description?`<div class="muted">${
|
|
1123
|
-
${"default"in s?`<pre class="wf-io-pre">${
|
|
1192
|
+
${s.description?`<div class="muted">${D(a("catalog.description"))}: ${D(s.description)}</div>`:""}
|
|
1193
|
+
${"default"in s?`<pre class="wf-io-pre">${D(`${a("catalog.default")}: ${JSON.stringify(s.default,null,2)}`)}</pre>`:""}
|
|
1124
1194
|
</article>
|
|
1125
|
-
`).join("")}</div>`}var
|
|
1126
|
-
<img class="qr-image" src="${e.qrDataUrl}" alt="${
|
|
1127
|
-
${e.qrUrl?`<a class="onboarding-link" href="${r(e.qrUrl)}" target="_blank" rel="noopener">${
|
|
1195
|
+
`).join("")}</div>`}var je=null,xt=null;function dt(){xt!==null&&(window.clearInterval(xt),xt=null)}function sn(){return je||(je=document.createElement("dialog"),je.className="onboarding-dialog",document.body.appendChild(je),je.addEventListener("close",dt),je)}function ti(e){return e.status==="waiting_for_scan"?a("botOnboarding.waiting"):e.status==="verifying"?a("botOnboarding.verifying"):e.status==="configuring_permissions"?e.permissionStatusMsg?`${a("botOnboarding.configuringPermissions")} ${e.permissionStatusMsg}`:a("botOnboarding.configuringPermissions"):e.status==="waiting_for_platform_scan"?a("botOnboarding.platformScanHint"):e.status==="completed"?a("botOnboarding.completed"):e.status==="failed"?`${a("botOnboarding.failed")}: ${r(e.message??e.error??"unknown")}`:a("botOnboarding.starting")}function ni(e){if(e.status!=="completed"||!e.permission)return"";let t=e.permission;if(t.ok){let s=[a("botOnboarding.permissionOk",{count:t.scopeCount??0})];t.skippedScopeCount&&t.skippedScopeCount>0&&s.push(a("botOnboarding.permissionSkipped",{count:t.skippedScopeCount})),t.versionId&&s.push(a("botOnboarding.permissionVersion",{version:r(t.versionId)}));let o=`<p class="hint-ok">\u2705 ${s.join(" ")}</p>`;return t.scopeWarning&&(o+=`<p class="hint-warn">\u26A0\uFE0F ${r(t.scopeWarning)}</p>`),o}let n=(e.remainingSteps??[]).map(s=>`<li><a href="${r(s.url)}" target="_blank" rel="noopener">${r(s.title)}</a></li>`).join("");return`<p class="hint-warn">\u26A0\uFE0F ${a("botOnboarding.permissionManual")}${t.message?`\uFF08${r(t.message)}\uFF09`:""}</p>`+(n?`<ol class="onboarding-steps">${n}</ol>`:"")}function lt(e){let t=sn(),n=e.status==="waiting_for_scan"&&e.qrDataUrl?`<div class="qr-card">
|
|
1196
|
+
<img class="qr-image" src="${e.qrDataUrl}" alt="${a("botOnboarding.qrAlt")}">
|
|
1197
|
+
${e.qrUrl?`<a class="onboarding-link" href="${r(e.qrUrl)}" target="_blank" rel="noopener">${a("botOnboarding.openLink")}</a>`:""}
|
|
1128
1198
|
</div>`:"",s=e.status==="waiting_for_platform_scan"&&e.platformQrDataUrl?`<div class="qr-card">
|
|
1129
|
-
<img class="qr-image" src="${e.platformQrDataUrl}" alt="${
|
|
1130
|
-
</div>`:"",
|
|
1199
|
+
<img class="qr-image" src="${e.platformQrDataUrl}" alt="${a("botOnboarding.platformQrAlt")}">
|
|
1200
|
+
</div>`:"",o=e.appId?`<p><b>App ID:</b> <code>${r(e.appId)}</code>`+(e.cliId?` \uFF5C <b>CLI:</b> <code>${r(e.cliId)}</code>`:"")+(e.workingDir?` \uFF5C <b>${a("botOnboarding.metaDir")}:</b> <code>${r(e.workingDir)}</code>`:"")+"</p>":"",i=e.status==="completed"?`<p class="hint-ok">${a("botOnboarding.restartHint")}</p>`:"";t.innerHTML=`<article>
|
|
1131
1201
|
<header>
|
|
1132
|
-
<h3>${
|
|
1133
|
-
<p>${
|
|
1202
|
+
<h3>${a("botOnboarding.title")}</h3>
|
|
1203
|
+
<p>${a("botOnboarding.intro")}</p>
|
|
1134
1204
|
</header>
|
|
1135
|
-
<p class="onboarding-status status-${e.status}">${
|
|
1205
|
+
<p class="onboarding-status status-${e.status}">${ti(e)}</p>
|
|
1136
1206
|
${n}
|
|
1137
1207
|
${s}
|
|
1138
|
-
${
|
|
1139
|
-
${
|
|
1208
|
+
${o}
|
|
1209
|
+
${ni(e)}
|
|
1140
1210
|
${i}
|
|
1141
|
-
<form method="dialog"><button>${
|
|
1142
|
-
</article>`}async function
|
|
1211
|
+
<form method="dialog"><button>${a("botOnboarding.close")}</button></form>
|
|
1212
|
+
</article>`}async function ai(){try{let e=await fetch("/api/cli-options"),t=await e.json();if(e.ok&&Array.isArray(t?.options))return t.options}catch{}return[{id:"claude-code",label:"Claude"}]}function on(e,t){let n=sn(),s=e.map(c=>`<option value="${r(c.id)}">${r(c.label)}\uFF08${r(c.id)}\uFF09</option>`).join(""),o=t?`<p class="form-error">${r(t)}</p>`:"";n.innerHTML=`<article>
|
|
1143
1213
|
<header>
|
|
1144
|
-
<h3>${
|
|
1145
|
-
<p>${
|
|
1214
|
+
<h3>${a("botOnboarding.title")}</h3>
|
|
1215
|
+
<p>${a("botOnboarding.intro")}</p>
|
|
1146
1216
|
</header>
|
|
1147
1217
|
<form id="onboarding-form" class="onboarding-form">
|
|
1148
1218
|
<label class="onboarding-field">
|
|
1149
|
-
<span>${
|
|
1219
|
+
<span>${a("botOnboarding.cliLabel")}</span>
|
|
1150
1220
|
<select id="ob-cli">${s}</select>
|
|
1151
1221
|
</label>
|
|
1152
1222
|
<label class="onboarding-field">
|
|
1153
|
-
<span>${
|
|
1154
|
-
<input id="ob-dir" type="text" value="~" placeholder="${
|
|
1223
|
+
<span>${a("botOnboarding.dirLabel")}</span>
|
|
1224
|
+
<input id="ob-dir" type="text" value="~" placeholder="${a("botOnboarding.dirPlaceholder")}" autocomplete="off" spellcheck="false">
|
|
1155
1225
|
</label>
|
|
1156
1226
|
<label class="onboarding-field">
|
|
1157
|
-
<span>${
|
|
1158
|
-
<input id="ob-model" type="text" placeholder="${
|
|
1227
|
+
<span>${a("botOnboarding.modelLabel")}</span>
|
|
1228
|
+
<input id="ob-model" type="text" placeholder="${a("botOnboarding.modelPlaceholder")}" autocomplete="off" spellcheck="false">
|
|
1159
1229
|
</label>
|
|
1160
|
-
${
|
|
1230
|
+
${o}
|
|
1161
1231
|
<menu class="onboarding-actions">
|
|
1162
|
-
<button type="button" id="ob-cancel">${
|
|
1163
|
-
<button type="submit" class="primary">${
|
|
1232
|
+
<button type="button" id="ob-cancel">${a("botOnboarding.cancel")}</button>
|
|
1233
|
+
<button type="submit" class="primary">${a("botOnboarding.startScan")}</button>
|
|
1164
1234
|
</menu>
|
|
1165
1235
|
</form>
|
|
1166
|
-
</article>`;let i=n.querySelector("#onboarding-form");n.querySelector("#ob-cancel")?.addEventListener("click",()=>n.close()),i?.addEventListener("submit",
|
|
1236
|
+
</article>`;let i=n.querySelector("#onboarding-form");n.querySelector("#ob-cancel")?.addEventListener("click",()=>n.close()),i?.addEventListener("submit",c=>{c.preventDefault();let w=n.querySelector("#ob-cli")?.value??"",h=n.querySelector("#ob-dir")?.value??"",y=n.querySelector("#ob-model")?.value??"";oi({cliId:w,workingDir:h,model:y},e)})}async function oi(e,t){dt(),lt({id:"",status:"starting"});try{let n=await fetch("/api/bot-onboarding/start",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({cliId:e.cliId,workingDir:e.workingDir.trim(),model:e.model.trim()||void 0})}),s=await n.json();if(n.status===400){on(t,s?.message??s?.error??"invalid_input");return}if(!n.ok||!s?.job?.id)throw new Error(s?.error??`http_${n.status}`);lt(s.job),xt=window.setInterval(()=>{si(s.job.id).catch(o=>{dt(),lt({id:s.job.id,status:"failed",message:o instanceof Error?o.message:String(o)})})},1200)}catch(n){lt({id:"",status:"failed",message:n instanceof Error?n.message:String(n)})}}async function si(e){let t=await fetch(`/api/bot-onboarding/${encodeURIComponent(e)}`),n=await t.json();if(!t.ok||!n?.job)throw new Error(n?.error??`http_${t.status}`);lt(n.job),(n.job.status==="completed"||n.job.status==="failed")&&dt()}async function ii(){dt();let e=sn();on([{id:"claude-code",label:"Claude"}]),e.open||e.showModal();let t=await ai();e.open&&e.querySelector("#onboarding-form")&&on(t)}function Ea(){let e=document.getElementById("add-bot-btn");e&&(e.onclick=()=>{ii()})}var ri={monitor:'<rect width="20" height="14" x="2" y="3" rx="2"/><line x1="8" x2="16" y1="21" y2="21"/><line x1="12" x2="12" y1="17" y2="21"/>',sun:'<circle cx="12" cy="12" r="4"/><path d="M12 2v2"/><path d="M12 20v2"/><path d="m4.93 4.93 1.41 1.41"/><path d="m17.66 17.66 1.41 1.41"/><path d="M2 12h2"/><path d="M20 12h2"/><path d="m6.34 17.66-1.41 1.41"/><path d="m19.07 4.93-1.41 1.41"/>',moon:'<path d="M20.985 12.486a9 9 0 1 1-9.473-9.472c.405-.022.617.46.402.803a6 6 0 0 0 8.268 8.268c.344-.215.825-.004.803.401"/>',cpu:'<path d="M12 20v2"/><path d="M12 2v2"/><path d="M17 20v2"/><path d="M17 2v2"/><path d="M2 12h2"/><path d="M2 17h2"/><path d="M2 7h2"/><path d="M20 12h2"/><path d="M20 17h2"/><path d="M20 7h2"/><path d="M7 20v2"/><path d="M7 2v2"/><rect x="4" y="4" width="16" height="16" rx="2"/><rect x="8" y="8" width="8" height="8" rx="1"/>',sparkles:'<path d="M11.017 2.814a1 1 0 0 1 1.966 0l1.051 5.558a2 2 0 0 0 1.594 1.594l5.558 1.051a1 1 0 0 1 0 1.966l-5.558 1.051a2 2 0 0 0-1.594 1.594l-1.051 5.558a1 1 0 0 1-1.966 0l-1.051-5.558a2 2 0 0 0-1.594-1.594l-5.558-1.051a1 1 0 0 1 0-1.966l5.558-1.051a2 2 0 0 0 1.594-1.594z"/><path d="M20 2v4"/><path d="M22 4h-4"/><circle cx="4" cy="20" r="2"/>',zap:'<path d="M4 14a1 1 0 0 1-.78-1.63l9.9-10.2a.5.5 0 0 1 .86.46l-1.92 6.02A1 1 0 0 0 13 10h7a1 1 0 0 1 .78 1.63l-9.9 10.2a.5.5 0 0 1-.86-.46l1.92-6.02A1 1 0 0 0 11 14z"/>',terminal:'<path d="M12 19h8"/><path d="m4 17 6-6-6-6"/>',keyRound:'<path d="M2.586 17.414A2 2 0 0 0 2 18.828V21a1 1 0 0 0 1 1h3a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1h1a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1h.172a2 2 0 0 0 1.414-.586l.814-.814a6.5 6.5 0 1 0-4-4z"/><circle cx="16.5" cy="7.5" r=".5" fill="currentColor"/>',network:'<rect x="16" y="16" width="6" height="6" rx="1"/><rect x="2" y="16" width="6" height="6" rx="1"/><rect x="9" y="2" width="6" height="6" rx="1"/><path d="M5 16v-3a1 1 0 0 1 1-1h12a1 1 0 0 1 1 1v3"/><path d="M12 12V8"/>',swords:'<polyline points="14.5 17.5 3 6 3 3 6 3 17.5 14.5"/><line x1="13" x2="19" y1="19" y2="13"/><line x1="16" x2="20" y1="16" y2="20"/><line x1="19" x2="21" y1="21" y2="19"/><polyline points="14.5 6.5 18 3 21 3 21 6 17.5 9.5"/><line x1="5" x2="9" y1="14" y2="18"/><line x1="7" x2="4" y1="17" y2="20"/><line x1="3" x2="5" y1="19" y2="21"/>',flame:'<path d="M12 3q1 4 4 6.5t3 5.5a1 1 0 0 1-14 0 5 5 0 0 1 1-3 1 1 0 0 0 5 0c0-2-1.5-3-1.5-5q0-2 2.5-4"/>',ball:'<path d="M11 7a16 16 20 0 1 10.98 4.362"/><path d="M12 12a13 13 0 0 1-8.66 5"/><path d="M16.83 13.634a16 16 0 0 1-9.267 7.328"/><path d="M20.66 17A13 13 0 0 0 12 12a13 13 0 0 1 0-10"/><path d="M8.17 15.366a16 16 0 0 1-1.713-11.69"/><circle cx="12" cy="12" r="10"/>',chevron:'<path d="m6 9 6 6 6-6"/>'},Ha=[{labelKey:"theme.base",options:[{value:"system",labelKey:"status.system",icon:"monitor"},{value:"light",labelKey:"status.light",icon:"sun"},{value:"dark",labelKey:"status.dark",icon:"moon"}]},{labelKey:"theme.skins",options:[{value:"cyber",labelKey:"skin.cyber",icon:"cpu"},{value:"genshin",labelKey:"skin.genshin",icon:"sparkles"},{value:"fallout",labelKey:"skin.fallout",icon:"zap"},{value:"prts",labelKey:"skin.prts",icon:"terminal"},{value:"bluearchive",labelKey:"skin.bluearchive",icon:"keyRound"},{value:"zzz",labelKey:"skin.zzz",icon:"network"},{value:"dragonball",labelKey:"skin.dragonball",icon:"flame"},{value:"ikun",labelKey:"skin.ikun",icon:"ball"}]}],xa=Ha.flatMap(e=>e.options),ze=!1;function rn(e){return`<svg class="tm-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">${ri[e]??""}</svg>`}function li(e){return e.replace(/[&<>"]/g,t=>({"&":"&","<":"<",">":">",'"':"""})[t])}function Aa(){let e=document.getElementById("theme-menu"),t=document.getElementById("theme-menu-btn"),n=document.getElementById("theme-menu-pop");if(!e||!t||!n)return;n.innerHTML=Ha.map(o=>`<div class="tm-group" data-label-key="${o.labelKey}"></div>`+o.options.map(i=>`<button type="button" class="tm-item" role="option" data-value="${i.value}"><span class="tm-ic">${rn(i.icon)}</span><span class="tm-label" data-label-key="${i.labelKey}"></span></button>`).join("")).join("");let s=o=>{ze=o,n.hidden=!ze,t.setAttribute("aria-expanded",String(ze)),e.classList.toggle("open",ze)};t.addEventListener("click",o=>{o.stopPropagation(),s(!ze)}),n.addEventListener("click",o=>{let i=o.target.closest(".tm-item");i&&(ye.setTheme(i.dataset.value??"system"),s(!1))}),document.addEventListener("click",o=>{ze&&!e.contains(o.target)&&s(!1)}),document.addEventListener("keydown",o=>{o.key==="Escape"&&ze&&s(!1)}),ln()}function ln(){let e=document.getElementById("theme-menu-btn");if(!e)return;let t=xa.find(n=>n.value===ye.theme)??xa[0];e.innerHTML=`<span class="tm-ic">${rn(t.icon)}</span><span class="tm-current">${li(a(t.labelKey))}</span><span class="tm-chev">${rn("chevron")}</span>`,document.querySelectorAll("#theme-menu-pop [data-label-key]").forEach(n=>{n.textContent=a(n.dataset.labelKey??"")}),document.querySelectorAll("#theme-menu-pop .tm-item").forEach(n=>{n.classList.toggle("active",n.dataset.value===ye.theme)})}var ce=document.getElementById("root"),Ht=!0,Ra=!1,Oa=["roles","bot-defaults","team","connectors"],dn=!1;function di(){if(dn)return;dn=!0;let e=document.createElement("div");e.id="auth-expired-overlay",e.style.cssText="position:fixed;inset:0;background:rgba(0,0,0,.65);display:flex;align-items:center;justify-content:center;z-index:9999",e.innerHTML='<div style="background:var(--surface);color:var(--fg);border:1px solid var(--border);border-radius:12px;padding:36px 40px;max-width:460px;width:90vw;text-align:center;box-shadow:0 12px 40px rgba(0,0,0,.35)"><h2 style="margin:0 0 14px;font-size:19px;color:var(--fg)">\u8BBF\u95EE\u94FE\u63A5\u5DF2\u5931\u6548</h2><p style="margin:0 0 24px;line-height:1.7;color:var(--muted,#8f959e);font-size:14px">\u5F53\u524D\u94FE\u63A5/\u8BBF\u95EE\u5DF2\u5931\u6548\uFF0C\u8BF7\u4F7F\u7528\u6700\u65B0\u6388\u6743\u94FE\u63A5\u91CD\u65B0\u8FDB\u5165\uFF08\u8FD0\u884C botmux dashboard \u83B7\u53D6\uFF09\u3002</p><button id="auth-expired-dismiss" type="button" style="padding:8px 22px;background:var(--accent);color:var(--on-accent);border:none;border-radius:8px;cursor:pointer;font-size:14px">\u77E5\u9053\u4E86</button></div>',document.body.appendChild(e);let t=()=>{e.remove(),dn=!1};e.querySelector("#auth-expired-dismiss")?.addEventListener("click",t),e.addEventListener("click",n=>{n.target===e&&t()})}var cn;function ci(){let e=document.getElementById("readonly-toast");e||(e=document.createElement("div"),e.id="readonly-toast",e.style.cssText="position:fixed;left:50%;bottom:28px;transform:translateX(-50%);z-index:9999;background:var(--fg,#1f2329);color:var(--bg,#fff);padding:10px 18px;border-radius:8px;font-size:13px;box-shadow:0 8px 24px rgba(0,0,0,.25)",document.body.appendChild(e)),e.textContent="\u5F53\u524D\u662F\u53EA\u8BFB\u8BBF\u95EE\uFF0C\u6B64\u64CD\u4F5C\u9700\u8981\u6388\u6743\u94FE\u63A5\uFF08\u8FD0\u884C botmux dashboard \u83B7\u53D6\uFF09",e.style.display="block",cn&&window.clearTimeout(cn),cn=window.setTimeout(()=>{e.style.display="none"},4e3)}var ui=window.fetch.bind(window);window.fetch=async function(...t){let n=await ui(...t);if(n.status===401){let s=(t[1]?.method??"GET").toUpperCase();(s==="GET"||s==="HEAD")&&!Ra?di():ci()}return n};var un="";function At(){let e=document.getElementById("attention-strip");if(!e)return;let t=[...K.sessions.values()].map(o=>({s:o,reason:Ce(o)})).filter(o=>!!o.reason).sort((o,i)=>Number(o.s.lastMessageAt??0)-Number(i.s.lastMessageAt??0));if(t.length===0){e.hidden=!0,e.innerHTML="",un="";return}let n=t[0],s=`
|
|
1167
1237
|
<span class="attention-strip-ic" aria-hidden="true">!</span>
|
|
1168
|
-
<b>${r(
|
|
1169
|
-
<span class="attention-strip-longest">${r(
|
|
1170
|
-
<a class="attention-strip-go" href="#/sessions">${r(
|
|
1238
|
+
<b>${r(a("strip.pending",{count:t.length}))}</b>
|
|
1239
|
+
<span class="attention-strip-longest">${r(a("strip.longest",{time:be(n.s.lastMessageAt),bot:ke(n.s),reason:n.reason}))}</span>
|
|
1240
|
+
<a class="attention-strip-go" href="#/sessions">${r(a("strip.handle"))}</a>`;e.hidden=!1,s!==un&&(un=s,e.innerHTML=s)}K.on(At);ve().then(At);async function pi(){try{let e=await fetch("/api/settings");if(e.ok){let t=await e.json();Ht=!!t.authed,Ra=!!(t.settings&&t.settings.publicReadOnly)}}catch{}}function mi(){for(let t of Oa){let n=document.querySelector(`.sidebar-nav a[data-route="${t}"]`);n&&(n.style.display=Ht?"":"none")}let e=document.getElementById("add-bot-btn");e&&(e.style.display=Ht?"":"none")}function fi(e){e.innerHTML='<section class="auth-required" style="max-width:520px;margin:64px auto;text-align:center;background:var(--surface);color:var(--fg);border:1px solid var(--border);border-radius:14px;padding:40px 36px;box-shadow:0 8px 28px rgba(0,0,0,.12)"><h2 style="margin:0 0 12px;font-size:20px;color:var(--fg)">\u6B64\u9875\u9700\u8981\u6388\u6743\u94FE\u63A5</h2><p style="margin:0 0 24px;line-height:1.7;color:var(--muted);font-size:14px">\u4F60\u5F53\u524D\u662F\u53EA\u8BFB\u8BBF\u95EE\uFF0C\u7BA1\u7406\u9875\uFF08\u89D2\u8272 / Bot \u914D\u7F6E / \u56E2\u961F / \u63A5\u5165\u70B9\uFF09\u9700\u8981\u6388\u6743\u94FE\u63A5\u3002\u8FD0\u884C <code>botmux dashboard</code> \u83B7\u53D6\u6700\u65B0\u94FE\u63A5\u540E\u5373\u53EF\u7BA1\u7406\u3002</p><a href="#/" style="display:inline-block;padding:8px 22px;background:var(--accent);color:var(--on-accent);border-radius:8px;text-decoration:none;font-size:14px">\u8FD4\u56DE\u603B\u89C8</a></section>'}var ct=null;function Ca(e){for(let t of document.querySelectorAll(".sidebar-nav a")){let n=t.getAttribute("href")??"#/";t.classList.toggle("active",n===(e||"#/"))}}function pn(){ct&&(ct(),ct=null);let e=location.hash||"#/";if(!Ht&&Oa.some(t=>e.startsWith("#/"+t))){fi(ce),Ca(e);return}e.startsWith("#/workflows/catalog")||e.startsWith("#/workflows-catalog")?ct=Ma(ce):e.startsWith("#/workflows")?ct=ba(ce):e.startsWith("#/groups")?Jn(ce):e.startsWith("#/settings")?pa(ce):e.startsWith("#/bot-defaults")?Qn(ce):e.startsWith("#/connectors")?ca(ce):e.startsWith("#/team/manage")?ra(ce):e.startsWith("#/team")?ia(ce):e.startsWith("#/roles")?ta(ce):e.startsWith("#/schedules")?Gn(ce):e.startsWith("#/sessions")?_n(ce):Pn(ce),Ca(e)}var mn=document.getElementById("status");function qa(){mn&&(mn.textContent=K.online?a("status.live"):a("status.disconnected"),mn.className="connection-status "+(K.online?"online":"offline"))}K.on(qa);function Da(){document.querySelectorAll("[data-i18n]").forEach(e=>{e.textContent=a(e.dataset.i18n??"")}),document.querySelectorAll("[data-locale]").forEach(e=>{e.classList.toggle("active",e.dataset.locale===ye.locale)}),ln(),qa()}function gi(){document.querySelectorAll("[data-locale]").forEach(e=>{e.onclick=()=>ye.setLocale(e.dataset.locale)}),Aa()}(async()=>{ye.init(),gi(),Ea(),ye.on(()=>{Da(),At(),pn()}),Da(),At(),await pi(),mi();try{await fn()}catch(e){console.error("botmux dashboard bootstrap failed",e),K.setOnline(!1)}window.addEventListener("hashchange",pn),pn()})();})();
|