botmux 2.64.0 → 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 +39 -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 +104 -26
- 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 +243 -38
- package/dist/daemon.js.map +1 -1
- package/dist/dashboard/web/bot-defaults.d.ts.map +1 -1
- package/dist/dashboard/web/bot-defaults.js +118 -0
- package/dist/dashboard/web/bot-defaults.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/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/app.js +568 -503
- package/dist/dashboard.js +87 -7
- 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 +3 -2
- package/dist/im/lark/client.d.ts.map +1 -1
- package/dist/im/lark/client.js +28 -2
- 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 +170 -50
- package/dist/im/lark/event-dispatcher.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/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/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 It=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 It;async function ln(){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 Tt="botmux.dashboard.locale",Ao={"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"},Co={"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"},dn={zh:Ao,en:Co};function cn(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 Do(e=[]){for(let t of e){let n=cn(t);if(n)return n}return"zh"}function nt(e){return(t,n)=>{let s=dn[e][t]??dn.zh[t]??t;return n?s.replace(/\{(\w+)\}/g,(a,i)=>{let l=n[i];return l==null?`{${i}}`:String(l)}):s}}function un(e,t){return(e?cn(e.getItem(Tt)):null)??Do(t)}var Lt="botmux.dashboard.theme",pn="botmux.dashboard.sessions.view";function Ro(e){return e==="system"||e==="light"||e==="dark"?e:null}function Oo(e){return e==="board"||e==="table"?e:null}function mn(e,t){return e==="system"?t?"dark":"light":e}function fn(e){return Ro(e?.getItem(Lt))??"dark"}function gn(e){return Oo(e?.getItem(pn))??"board"}var bn="botmux.dashboard.sessions.boardOrder",Fe=["needs-you","starting","working","idle"];function Bo(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 hn(e){try{let t=e?.getItem(bn);return t?Bo(JSON.parse(t))??[...Fe]:[...Fe]}catch{return[...Fe]}}function Et(e,t){try{e?.setItem(bn,JSON.stringify(t))}catch{}}function wn(e,t){try{e?.setItem(pn,t)}catch{}}var qo=["default","cyber","genshin","fallout","prts","bluearchive","zzz","dragonball","ikun"],xt="botmux.dashboard.skin";function Po(e){return typeof e=="string"&&qo.includes(e)?e:null}function yn(e){return Po(e?.getItem(xt))??"default"}var No="\uFF71\uFF72\uFF73\uFF74\uFF75\uFF76\uFF77\uFF78\uFF79\uFF7A\uFF7B\uFF7C\uFF7D\uFF7E\uFF7F\uFF80\uFF81\uFF82\uFF830123456789\uFF8A\uFF8B\uFF8C\uFF8D\uFF8E$#%&@*+=<>/\\",vn=["186 100% 60%","56 97% 60%","330 100% 64%","150 80% 58%"],Ht=[{key:"shake",dur:280},{key:"rgb",dur:340},{key:"tear",dur:400},{key:"invert",dur:220},{key:"slice",dur:320},{key:"blackout",dur:260}],kn=["ESTABLISHING NETLINK","BYPASSING ICE","DECRYPTING DATAFLOW","SYNCING BOTMUX TELEMETRY","ACCESS GRANTED"],$n="0123456789ABCDEF#<>*/=+\uFF71\uFF72\uFF73\uFF74\uFF75\uFF76\uFF77\uFF78\uFF85\uFF86\uFF87\uFF88\uFF89\uFF8A\uFF8B\uFF8C",Uo=3200,We=[],Pe=0,ot=0;function Dt(){return typeof window<"u"&&!!window.matchMedia?.("(prefers-reduced-motion: reduce)").matches}function At(e,t){let n="";for(let s=0;s<e;s++)n+=t[Math.floor(Math.random()*t.length)];return n}function jo(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=At(28+Math.floor(Math.random()*22),No);let i=vn[Math.floor(Math.random()*vn.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 zo(){if(Dt())return;let e=document.body,t=()=>{let n=Ht[Math.floor(Math.random()*Ht.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 _o(){We.forEach(e=>window.clearTimeout(e)),We=[];for(let e of Ht)document.body.classList.remove(`cp-fx-${e.key}`)}function Fo(){if(Dt()||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;Pe=window.setInterval(()=>{let l=kn[s];t&&(a<l.length?(a+=1,t.textContent=l.slice(0,a)+At(l.length-a,$n),t.classList.remove("done")):i<16?(i+=1,t.textContent=l,t.classList.add("done")):(s=(s+1)%kn.length,a=0,i=0)),n&&(n.textContent=At(26,$n))},50),ot=window.setTimeout(()=>{window.clearInterval(Pe),Pe=0,e.remove()},Uo)}function Wo(){Pe&&(window.clearInterval(Pe),Pe=0),ot&&(window.clearTimeout(ot),ot=0),document.getElementById("cyber-boot")?.remove()}var Go=320,Sn=16,Ge=!0,qe=0,Mt=0,at=!1,st=[],Ct=[];function Jo(){if(at)return;at=!0,Ge=!1,qe=0;let e=Dt(),t="";for(let s=0;s<Sn;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/Sn*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 Ko(){Ge=!0,qe=0;let e=document.documentElement,t=()=>e.scrollHeight-(window.innerHeight+window.scrollY)<=4,n=v=>{v<=0||!t()||(qe+=v,qe>Go&&Ge&&Jo())},s=v=>n(v.deltaY),a=v=>{Mt=v.touches[0]?.clientY??0},i=v=>{let y=v.touches[0]?.clientY??0,h=Mt-y;Mt=y,n(h)},l=()=>{t()||(qe=0,Ge=!0)},p=(v,y)=>{window.addEventListener(v,y,{passive:!0}),Ct.push([v,y])};p("wheel",s),p("touchstart",a),p("touchmove",i),p("scroll",l)}function Vo(){for(let[e,t]of Ct)window.removeEventListener(e,t);Ct=[],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 In(e,t=!1){if(!(typeof document>"u")){if(!e){document.getElementById("cyber-fx")?.remove(),document.getElementById("cyber-hud")?.remove(),document.getElementById("cyber-glitch")?.remove(),_o(),Wo(),Vo();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>',jo(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),zo(),Ko()}t&&Fo()}}var Yo={genshin:2e3,zzz:1900,dragonball:1900,ikun:1900},Qo='<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 Xo(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 Qo;default:return""}}function Zo(){return typeof window<"u"&&!!window.matchMedia?.("(prefers-reduced-motion: reduce)").matches}function Tn(e){if(typeof document>"u"||Zo())return;let t=Yo[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=Xo(e),document.body.appendChild(n),window.setTimeout(()=>n.remove(),t+80)}var Ot=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=un(t?.localStorage,ta()),this.translate=nt(this.locale),this.themeMode=fn(t?.localStorage),this.skin=yn(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(Tt,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(Lt,this.themeMode)),a&&(this.skin=s,window.localStorage.setItem(xt,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=mn(this.themeMode,!!this.mediaQuery?.matches);let t=this.skin==="default"?this.resolvedTheme:ea[this.skin];document.documentElement.dataset.theme=t,document.documentElement.dataset.themeMode=this.themeMode}applySkin(t=!1){document.documentElement.dataset.skin=this.skin,In(this.skin==="cyber",t),t&&this.skin!=="cyber"&&this.skin!=="default"&&Tn(this.skin)}applyLocale(){document.documentElement.lang=this.locale==="zh"?"zh-CN":"en"}},ea={default:"light",cyber:"dark",genshin:"light",fallout:"dark",prts:"dark",bluearchive:"dark",zzz:"dark",dragonball:"light",ikun:"dark"};function ta(){return typeof navigator>"u"?[]:navigator.languages?.length?navigator.languages:[navigator.language].filter(Boolean)}var me=new Ot;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 Ln=[{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 En(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}=Ln[t%Ln.length];return`--c1:${s};--c2:${a}`}var it=new Map,rt=new Map;function na(e,t){return t?it.get(t):e?rt.get(String(e)):void 0}function de(e){let t=e.name??"",n=e.avatarUrl??na(e.name,e.larkAppId),s=e.size==="sm"?" orb-avatar-sm":"",a=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}${a}" style="${En(t)}" aria-hidden="true">${l}${i}</span>`}function oa(e){return e?lt.get(e):void 0}function xn(e){let t=e.name??e.chatId??"",n=e.avatarUrl??oa(e.chatId),s=e.size==="sm"?" orb-avatar-sm":"",a=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}${a}" style="${En(t)}" aria-hidden="true">${i}</span>`}var Mn=new Map,Hn=new Map,lt=new Map,Rt=null,An="botmux.avatarCache.v1";function aa(){try{let e=typeof window<"u"?window.localStorage.getItem(An):null;if(!e)return;let t=JSON.parse(e);for(let[n,s]of Object.entries(t.botByAppId??{}))it.set(n,s);for(let[n,s]of Object.entries(t.botByName??{}))rt.set(n,s);for(let[n,s]of Object.entries(t.chatById??{}))lt.set(n,s)}catch{}}function sa(){try{if(typeof window>"u")return;window.localStorage.setItem(An,JSON.stringify({botByAppId:Object.fromEntries(it),botByName:Object.fromEntries(rt),chatById:Object.fromEntries(lt)}))}catch{}}aa();function fe(){return Rt??=(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&&Mn.set(n.larkAppId,String(n.botName)),n.botAvatarUrl&&(n.larkAppId&&it.set(n.larkAppId,String(n.botAvatarUrl)),n.botName&&rt.set(String(n.botName),String(n.botAvatarUrl)));for(let n of t.chats??[])n.chatId&&n.name&&Hn.set(n.chatId,String(n.name)),n.chatId&&n.avatar&<.set(n.chatId,String(n.avatar));sa()}catch{Rt=null}})(),Rt}function ge(e){let t=e.larkAppId?Mn.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&&Hn.get(e.chatId)||null}function Se(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 Cn={chats:[],bots:[]};async function ia(){try{let e=await fetch("/api/groups");if(!e.ok)return;Cn=await e.json()}catch{}}var Bt=new Set(["working","analyzing","active","starting"]);function ra(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 Cn.bots??[]){let i=n(a.larkAppId??a.botName??"-");i.online=!0,a.botName&&(i.botName=a.botName),a.botAvatarUrl&&(i.botAvatarUrl=a.botAvatarUrl)}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),Bt.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 la(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((Se(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((Se(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,301 +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="${q}" target="_blank" rel="noopener">${
|
|
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
345
|
<div class="g-chat-cell">
|
|
346
|
-
${
|
|
346
|
+
${Rn({chatId:f.chatId,name:f.name,avatarUrl:f.avatar,size:"sm"})}
|
|
347
347
|
<div class="g-chat-meta">
|
|
348
|
-
<strong>${r(
|
|
349
|
-
<small><code>${r(
|
|
348
|
+
<strong>${r(f.name??f.chatId)}</strong><br>
|
|
349
|
+
<small><code>${r(f.chatId)}</code></small>
|
|
350
350
|
</div>
|
|
351
351
|
</div>
|
|
352
352
|
</td>
|
|
353
|
-
${
|
|
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("")}
|
|
354
354
|
<td>
|
|
355
|
-
<button class="add-bots" type="button">${
|
|
356
|
-
<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>
|
|
357
357
|
</td>
|
|
358
|
-
</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=`
|
|
359
359
|
<article>
|
|
360
|
-
<header><h3>${
|
|
361
|
-
<p>${
|
|
360
|
+
<header><h3>${a("groups.addBots")} \xB7 ${r(f.name??f.chatId)}</h3></header>
|
|
361
|
+
<p>${a("groups.createHelp")}</p>
|
|
362
362
|
<form id="g-addform">
|
|
363
|
-
${
|
|
363
|
+
${Wn(re.bots,d)}
|
|
364
364
|
<div class="actions">
|
|
365
|
-
<button type="submit" class="primary">${
|
|
366
|
-
<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>
|
|
367
367
|
</div>
|
|
368
368
|
</form>
|
|
369
|
-
</article>`,i.showModal(),i.querySelector("#g-cancel").onclick=()=>i.close(),i.querySelector("#g-addform").onsubmit=async
|
|
370
|
-
`);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=`
|
|
371
371
|
<article>
|
|
372
|
-
<header><h3>${
|
|
373
|
-
<p><b>chatId:</b> <code>${r(
|
|
374
|
-
<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>
|
|
375
375
|
|
|
376
376
|
<fieldset>
|
|
377
|
-
<legend>${
|
|
378
|
-
<p><small>${
|
|
379
|
-
${
|
|
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`
|
|
380
380
|
<div class="oncall-row" data-bot="${r($.larkAppId)}">
|
|
381
381
|
<label class="checkbox-row">
|
|
382
|
-
<input type="checkbox" data-action="toggle" ${
|
|
382
|
+
<input type="checkbox" data-action="toggle" ${f?"checked":""}>
|
|
383
383
|
<strong>${r($.botName??$.larkAppId)}</strong>
|
|
384
384
|
<small>(${r($.larkAppId)})</small>
|
|
385
385
|
</label>
|
|
386
386
|
<div class="oncall-row-body">
|
|
387
387
|
<input type="text" data-input="workingDir" placeholder="e.g. /root/iserver/botmux"
|
|
388
|
-
value="${r(
|
|
389
|
-
<button type="button" data-action="save">${
|
|
388
|
+
value="${r(d)}" ${f?"":"disabled"}>
|
|
389
|
+
<button type="button" data-action="save">${a("groups.save")}</button>
|
|
390
390
|
<span class="oncall-status" data-status></span>
|
|
391
391
|
</div>
|
|
392
392
|
</div>
|
|
@@ -394,8 +394,8 @@ effects={effects}, activities={activities}, waits={waits}, cancels={cancels}`,"w
|
|
|
394
394
|
</fieldset>
|
|
395
395
|
|
|
396
396
|
<fieldset>
|
|
397
|
-
<legend>${
|
|
398
|
-
${
|
|
397
|
+
<legend>${a("groups.leaveTitle")}</legend>
|
|
398
|
+
${T.length===0?'<p class="empty">\u6CA1\u6709\u673A\u5668\u4EBA\u5728\u7FA4\u91CC</p>':T.map($=>`
|
|
399
399
|
<label class="checkbox-row">
|
|
400
400
|
<input type="checkbox" name="leave-bot" value="${r($.larkAppId)}">
|
|
401
401
|
${r($.botName??$.larkAppId)}
|
|
@@ -405,210 +405,254 @@ effects={effects}, activities={activities}, waits={waits}, cancels={cancels}`,"w
|
|
|
405
405
|
</fieldset>
|
|
406
406
|
|
|
407
407
|
<div class="actions">
|
|
408
|
-
<button id="g-leave-btn" type="button" ${
|
|
409
|
-
<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>
|
|
410
410
|
</div>
|
|
411
|
-
<p class="hint-warn"><small>${
|
|
412
|
-
<form method="dialog"><button>${
|
|
413
|
-
</article>`,i.showModal(),i.querySelectorAll(".oncall-row").forEach($=>{let
|
|
414
|
-
`);alert(
|
|
415
|
-
\u5173\u95ED\u4E86 ${
|
|
416
|
-
\u5173\u95ED\u4E86 ${
|
|
417
|
-
${
|
|
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(`
|
|
418
418
|
`)}
|
|
419
419
|
|
|
420
|
-
\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">
|
|
421
421
|
<div class="page-heading">
|
|
422
422
|
<div>
|
|
423
|
-
<p class="eyebrow">${
|
|
424
|
-
<h1>${
|
|
425
|
-
<p>${
|
|
423
|
+
<p class="eyebrow">${a("nav.botDefaults")}</p>
|
|
424
|
+
<h1>${a("botDefaults.title")}</h1>
|
|
425
|
+
<p>${a("botDefaults.subtitle")}</p>
|
|
426
426
|
</div>
|
|
427
427
|
</div>
|
|
428
428
|
<form id="bd-filters" class="filters sessions-filters">
|
|
429
|
-
<input type="search" name="q" placeholder="${
|
|
430
|
-
<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>
|
|
431
431
|
</form>
|
|
432
432
|
<div class="bd-layout">
|
|
433
433
|
<aside id="bd-roster" class="bd-roster"></aside>
|
|
434
434
|
<div id="bd-list" class="bd-detail"></div>
|
|
435
435
|
</div>
|
|
436
|
-
</section>`}async function
|
|
437
|
-
${
|
|
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"})}
|
|
438
438
|
<div class="bd-roster-tx">
|
|
439
|
-
<b>${r(
|
|
440
|
-
<span>${r(
|
|
439
|
+
<b>${r(g)}</b>
|
|
440
|
+
<span>${r(k||d.larkAppId.slice(0,14))}</span>
|
|
441
441
|
</div>
|
|
442
|
-
${
|
|
443
|
-
</div>`}function
|
|
442
|
+
${H}
|
|
443
|
+
</div>`}function c(d){if(d.error)return`<article class="bd-card bd-profile" data-appid="${r(d.larkAppId)}">
|
|
444
444
|
<header class="bd-profile-head">
|
|
445
|
-
${
|
|
445
|
+
${me({name:d.botName??d.larkAppId,larkAppId:d.larkAppId})}
|
|
446
446
|
<div class="bd-profile-id"><strong>${r(d.botName??d.larkAppId)}</strong>
|
|
447
447
|
<code>${r(d.larkAppId)}</code></div>
|
|
448
448
|
</header>
|
|
449
449
|
<p class="hint-warn-inline">\u67E5\u8BE2\u5931\u8D25\uFF1A${r(d.error)}</p>
|
|
450
|
-
</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)}">
|
|
451
451
|
<header class="bd-profile-head">
|
|
452
|
-
${
|
|
452
|
+
${me({name:H,larkAppId:d.larkAppId,dot:"ok"})}
|
|
453
453
|
<div class="bd-profile-id">
|
|
454
|
-
<strong>${r(
|
|
455
|
-
${
|
|
454
|
+
<strong>${r(H)}</strong>
|
|
455
|
+
${E?`<span class="mate-role">${r(E)}</span>`:""}
|
|
456
456
|
<code>${r(d.larkAppId)}</code>
|
|
457
457
|
</div>
|
|
458
458
|
<div class="bd-profile-meta bd-meta">
|
|
459
|
-
<small class="bd-meta-ok">\u25CF ${
|
|
460
|
-
<small data-oncall-since>${
|
|
461
|
-
<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>
|
|
462
462
|
</div>
|
|
463
463
|
</header>
|
|
464
464
|
<div class="bd-body bd-grid">
|
|
465
465
|
<section class="bd-tile">
|
|
466
466
|
<section class="bd-section">
|
|
467
|
-
<h3 class="bd-section-title">${
|
|
467
|
+
<h3 class="bd-section-title">${a("botDefaults.sectionOncall")}</h3>
|
|
468
468
|
<label class="toggle-row">
|
|
469
|
-
<input type="checkbox" data-action="toggle" ${
|
|
469
|
+
<input type="checkbox" data-action="toggle" ${k?"checked":""}>
|
|
470
470
|
<span class="switch" aria-hidden="true"></span>
|
|
471
|
-
<span class="toggle-tx"><strong>${
|
|
472
|
-
<small>${
|
|
471
|
+
<span class="toggle-tx"><strong>${a("botDefaults.defaultOncall")}</strong>
|
|
472
|
+
<small>${a("botDefaults.defaultOncallHelp")}\u3002${a("botDefaults.warning")}</small></span>
|
|
473
473
|
</label>
|
|
474
474
|
<div class="bd-row">
|
|
475
475
|
<label>
|
|
476
|
-
<span>${
|
|
476
|
+
<span>${a("botDefaults.workingDir")}</span>
|
|
477
477
|
<input type="text" data-input="workingDir" placeholder="e.g. /root/iserver/botmux"
|
|
478
|
-
value="${r(
|
|
478
|
+
value="${r(g.workingDir??"")}" ${k?"":"disabled"}>
|
|
479
479
|
</label>
|
|
480
480
|
</div>
|
|
481
481
|
<div class="actions">
|
|
482
|
-
<button type="button" class="primary" data-action="save">${
|
|
482
|
+
<button type="button" class="primary" data-action="save">${a("botDefaults.save")}</button>
|
|
483
483
|
<span class="oncall-status" data-status></span>
|
|
484
484
|
</div>
|
|
485
|
-
${
|
|
485
|
+
${$(d)}
|
|
486
486
|
</section>
|
|
487
487
|
</section>
|
|
488
|
-
<section class="bd-tile">${
|
|
489
|
-
<section class="bd-tile">${
|
|
490
|
-
<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>
|
|
491
492
|
</div>
|
|
492
|
-
</article>`}function
|
|
493
|
-
<h3 class="bd-section-title">${
|
|
494
|
-
<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>
|
|
495
496
|
<textarea data-input="teamRole" rows="6"
|
|
496
|
-
placeholder="${r(
|
|
497
|
-
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>
|
|
498
499
|
<div class="actions">
|
|
499
|
-
<button type="button" class="primary" data-action="save-role"${
|
|
500
|
-
<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>
|
|
501
502
|
<span class="oncall-status" data-role-status></span>
|
|
502
503
|
</div>
|
|
503
|
-
</section>`}function
|
|
504
|
-
<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>
|
|
505
506
|
<div class="bd-row bd-brand">
|
|
506
507
|
<label>
|
|
507
|
-
<span>${
|
|
508
|
+
<span>${a("botDefaults.brandLabel")}</span>
|
|
508
509
|
<input type="text" data-input="brandLabel"
|
|
509
|
-
placeholder="${r(
|
|
510
|
-
value="${r(
|
|
510
|
+
placeholder="${r(a("botDefaults.brandLabelPlaceholder"))}"
|
|
511
|
+
value="${r(g??"")}">
|
|
511
512
|
</label>
|
|
512
|
-
<small data-brand-state>${r(
|
|
513
|
-
<small class="bd-help">${
|
|
513
|
+
<small data-brand-state>${r(h(g))}</small>
|
|
514
|
+
<small class="bd-help">${a("botDefaults.brandLabelHelp")}</small>
|
|
514
515
|
<div class="actions">
|
|
515
|
-
<button type="button" class="primary" data-action="save-brand">${
|
|
516
|
-
<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>
|
|
517
518
|
<span class="oncall-status" data-brand-status></span>
|
|
518
519
|
</div>
|
|
519
520
|
</div>
|
|
520
|
-
</section>`}function
|
|
521
|
-
<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>
|
|
522
523
|
<label class="toggle-row">
|
|
523
|
-
<input type="checkbox" data-action="toggle-disable-streaming" ${
|
|
524
|
+
<input type="checkbox" data-action="toggle-disable-streaming" ${g?"checked":""}>
|
|
524
525
|
<span class="switch" aria-hidden="true"></span>
|
|
525
|
-
<span class="toggle-tx"><strong>${
|
|
526
|
-
<small>${
|
|
526
|
+
<span class="toggle-tx"><strong>${a("botDefaults.disableStreaming")}</strong>
|
|
527
|
+
<small>${a("botDefaults.disableStreamingHelp")}</small></span>
|
|
527
528
|
</label>
|
|
528
529
|
<label class="toggle-row">
|
|
529
|
-
<input type="checkbox" data-action="toggle-writable-link" ${
|
|
530
|
+
<input type="checkbox" data-action="toggle-writable-link" ${k?"checked":""} ${g?"disabled":""}>
|
|
530
531
|
<span class="switch" aria-hidden="true"></span>
|
|
531
|
-
<span class="toggle-tx"><strong>${
|
|
532
|
-
<small>${
|
|
532
|
+
<span class="toggle-tx"><strong>${a("botDefaults.writableLink")}</strong>
|
|
533
|
+
<small>${a("botDefaults.writableLinkHelp")}</small></span>
|
|
533
534
|
</label>
|
|
534
535
|
<label class="toggle-row">
|
|
535
|
-
<input type="checkbox" data-action="toggle-private-card" ${
|
|
536
|
+
<input type="checkbox" data-action="toggle-private-card" ${H?"checked":""}>
|
|
536
537
|
<span class="switch" aria-hidden="true"></span>
|
|
537
|
-
<span class="toggle-tx"><strong>${
|
|
538
|
-
<small>${
|
|
538
|
+
<span class="toggle-tx"><strong>${a("botDefaults.privateCard")}</strong>
|
|
539
|
+
<small>${a("botDefaults.privateCardHelp")}</small></span>
|
|
539
540
|
</label>
|
|
540
541
|
<div class="actions">
|
|
541
|
-
<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>
|
|
542
543
|
<span class="oncall-status" data-card-pref-status></span>
|
|
543
544
|
</div>
|
|
544
|
-
</section>`}function
|
|
545
|
-
<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>
|
|
546
590
|
<label class="toggle-row">
|
|
547
|
-
<input type="checkbox" data-action="toggle-restrict-grant" ${
|
|
591
|
+
<input type="checkbox" data-action="toggle-restrict-grant" ${g?"checked":""}>
|
|
548
592
|
<span class="switch" aria-hidden="true"></span>
|
|
549
|
-
<span class="toggle-tx"><strong>${
|
|
550
|
-
<small>${
|
|
593
|
+
<span class="toggle-tx"><strong>${a("botDefaults.restrictGrant")}</strong>
|
|
594
|
+
<small>${a("botDefaults.restrictGrantHelp")}</small></span>
|
|
551
595
|
</label>
|
|
552
596
|
<div class="bd-row bd-quota">
|
|
553
597
|
<label>
|
|
554
|
-
<span>${
|
|
598
|
+
<span>${a("botDefaults.quotaDefault")}</span>
|
|
555
599
|
<input type="number" min="1" step="1" data-input="quotaLimit"
|
|
556
|
-
placeholder="${r(
|
|
557
|
-
value="${
|
|
600
|
+
placeholder="${r(a("botDefaults.quotaPlaceholder"))}"
|
|
601
|
+
value="${k??""}">
|
|
558
602
|
</label>
|
|
559
|
-
<small data-quota-state>${r(
|
|
560
|
-
<small class="bd-help">${
|
|
603
|
+
<small data-quota-state>${r(T(k))}</small>
|
|
604
|
+
<small class="bd-help">${a("botDefaults.quotaHelp")}</small>
|
|
561
605
|
<div class="actions">
|
|
562
|
-
<button type="button" class="primary" data-action="save-quota">${
|
|
563
|
-
<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>
|
|
564
608
|
<span class="oncall-status" data-grant-status></span>
|
|
565
609
|
</div>
|
|
566
610
|
</div>
|
|
567
|
-
</section>`}function
|
|
568
|
-
<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>
|
|
569
613
|
<label class="toggle-row">
|
|
570
|
-
<input type="checkbox" data-action="toggle-auto-join" ${
|
|
614
|
+
<input type="checkbox" data-action="toggle-auto-join" ${g?"checked":""}>
|
|
571
615
|
<span class="switch" aria-hidden="true"></span>
|
|
572
|
-
<span class="toggle-tx"><strong>${
|
|
573
|
-
<small>${
|
|
616
|
+
<span class="toggle-tx"><strong>${a("botDefaults.autoStartJoin")}</strong>
|
|
617
|
+
<small>${a("botDefaults.autoStartJoinHelp")}</small></span>
|
|
574
618
|
</label>
|
|
575
619
|
<div class="bd-row">
|
|
576
620
|
<label>
|
|
577
|
-
<span>${
|
|
621
|
+
<span>${a("botDefaults.autoStartJoinPrompt")}</span>
|
|
578
622
|
<textarea data-input="autoJoinPrompt" rows="3"
|
|
579
|
-
placeholder="${r(
|
|
623
|
+
placeholder="${r(a("botDefaults.autoStartJoinPromptPlaceholder"))}">${r(H)}</textarea>
|
|
580
624
|
</label>
|
|
581
625
|
<div class="actions">
|
|
582
|
-
<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>
|
|
583
627
|
</div>
|
|
584
628
|
</div>
|
|
585
629
|
<label class="toggle-row">
|
|
586
|
-
<input type="checkbox" data-action="toggle-auto-topic" ${
|
|
630
|
+
<input type="checkbox" data-action="toggle-auto-topic" ${k?"checked":""}>
|
|
587
631
|
<span class="switch" aria-hidden="true"></span>
|
|
588
|
-
<span class="toggle-tx"><strong>${
|
|
589
|
-
<small>${
|
|
632
|
+
<span class="toggle-tx"><strong>${a("botDefaults.autoStartTopic")}</strong>
|
|
633
|
+
<small>${a("botDefaults.autoStartTopicHelp")}</small></span>
|
|
590
634
|
</label>
|
|
591
635
|
<div class="actions">
|
|
592
636
|
<span class="oncall-status" data-auto-start-status></span>
|
|
593
637
|
</div>
|
|
594
|
-
</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">
|
|
595
639
|
<div class="page-heading">
|
|
596
640
|
<div>
|
|
597
|
-
<p class="eyebrow">${
|
|
598
|
-
<h1>${
|
|
599
|
-
<p>${
|
|
641
|
+
<p class="eyebrow">${a("nav.roles")}</p>
|
|
642
|
+
<h1>${a("roles.title")}</h1>
|
|
643
|
+
<p>${a("roles.subtitle")}</p>
|
|
600
644
|
</div>
|
|
601
645
|
</div>
|
|
602
646
|
<div class="roles-layout">
|
|
603
647
|
<div class="roles-tree-panel">
|
|
604
648
|
<div class="roles-tree-header">
|
|
605
|
-
<input type="search" id="roles-search" placeholder="${
|
|
606
|
-
<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>
|
|
607
651
|
</div>
|
|
608
652
|
<div id="roles-tree" class="roles-tree"></div>
|
|
609
653
|
</div>
|
|
610
654
|
<div class="roles-editor-panel">
|
|
611
|
-
<div id="roles-editor-empty" class="roles-editor-empty">${
|
|
655
|
+
<div id="roles-editor-empty" class="roles-editor-empty">${a("roles.selectHint")}</div>
|
|
612
656
|
<div id="roles-editor-form" class="roles-editor-form" style="display:none">
|
|
613
657
|
<div class="roles-editor-breadcrumb">
|
|
614
658
|
<span id="roles-editor-group-name"></span>
|
|
@@ -618,52 +662,52 @@ ${d.join(`
|
|
|
618
662
|
<div class="roles-editor-meta">
|
|
619
663
|
<span id="roles-editor-chat-id" class="roles-editor-meta-line"></span>
|
|
620
664
|
</div>
|
|
621
|
-
<textarea id="roles-editor-textarea" placeholder="${
|
|
665
|
+
<textarea id="roles-editor-textarea" placeholder="${a("roles.editorPlaceholder")}" rows="14"></textarea>
|
|
622
666
|
<div class="roles-editor-footer">
|
|
623
667
|
<span id="roles-editor-bytecount" class="roles-bytecount"></span>
|
|
624
668
|
<div class="roles-editor-actions">
|
|
625
|
-
<button type="button" id="roles-delete" class="danger">${
|
|
626
|
-
<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>
|
|
627
671
|
</div>
|
|
628
672
|
</div>
|
|
629
673
|
<div id="roles-preview" class="roles-preview"></div>
|
|
630
674
|
</div>
|
|
631
675
|
</div>
|
|
632
676
|
</div>
|
|
633
|
-
</section>`}async function
|
|
634
|
-
<div class="roles-bot-row ${
|
|
635
|
-
data-group-id="${r(
|
|
636
|
-
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)}">
|
|
637
681
|
<span class="roles-bot-indent"></span>
|
|
638
|
-
${
|
|
682
|
+
${me({name:v.botName,larkAppId:v.larkAppId,size:"sm"})}
|
|
639
683
|
<div class="roles-bot-info">
|
|
640
|
-
<div class="roles-bot-name">${r(
|
|
641
|
-
<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>
|
|
642
686
|
</div>
|
|
643
|
-
<span class="roles-badge ${
|
|
644
|
-
${
|
|
687
|
+
<span class="roles-badge ${v.hasRole?"has-role":"no-role"}">
|
|
688
|
+
${v.hasRole?a("roles.configured"):a("roles.unconfigured")}
|
|
645
689
|
</span>
|
|
646
690
|
</div>`).join(""):"";return`
|
|
647
691
|
<div class="roles-group-section">
|
|
648
|
-
<div class="roles-group-row ${i?"expanded":""} ${
|
|
649
|
-
data-group-id="${r(
|
|
650
|
-
<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>
|
|
651
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>
|
|
652
696
|
<div class="roles-group-info">
|
|
653
|
-
<div class="roles-group-name">${r(
|
|
697
|
+
<div class="roles-group-name">${r(o.name??o.chatId)}</div>
|
|
654
698
|
<div class="roles-group-meta">
|
|
655
|
-
${
|
|
699
|
+
${w}/${h} ${a("roles.botsWithRoles")}
|
|
656
700
|
</div>
|
|
657
701
|
</div>
|
|
658
702
|
<span class="roles-group-chevron"></span>
|
|
659
703
|
</div>
|
|
660
|
-
<div class="roles-bot-list">${
|
|
661
|
-
</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">
|
|
662
706
|
<div class="page-heading"><div>
|
|
663
707
|
<p class="eyebrow">\u56E2\u961F</p><h1>\u56E2\u961F\u534F\u4F5C\uFF08\u8DE8\u90E8\u7F72\uFF09</h1>
|
|
664
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>
|
|
665
709
|
</div></div>
|
|
666
|
-
${
|
|
710
|
+
${oa("home")}
|
|
667
711
|
<div class="card" style="margin-bottom:16px">
|
|
668
712
|
<h2 style="margin-top:0">\u672C\u90E8\u7F72</h2>
|
|
669
713
|
<p>\u6211\u7684\u98DE\u4E66\u8EAB\u4EFD\uFF1A<b id="tf-owner">\u672A\u7ED1\u5B9A</b>
|
|
@@ -692,12 +736,12 @@ ${Xn("home")}
|
|
|
692
736
|
</div>
|
|
693
737
|
</div>
|
|
694
738
|
</div>
|
|
695
|
-
</section>`}function Ca(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 Da(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===Qn,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),q=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}"${q}></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(pt.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 Ee(){let e=F("tf-teams"),t=gt();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(Ca);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>':Da(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}`,Ra()}function Ra(){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),Ee()}}),e.querySelectorAll(".tf-dep-h").forEach(t=>{t.onclick=()=>{let n=t.dataset.dk;Ke.has(n)?Ke.delete(n):Ke.add(n),Ee()}}),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=()=>{pt.set(t.dataset.tk,t.value)}}),e.querySelectorAll(".tf-cap").forEach(t=>{t.onchange=async()=>{let n=t.dataset.app,s=t.value;await Ma("/api/team/local-bots/"+encodeURIComponent(n)+"/capability",{capability:s}),gt().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=()=>Ba(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 ft("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=Ha(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 De("/api/team/federated-group",{name:l,larkAppIds:a,teamId:s.teamId}):await De("/api/team/remote-group",{hubUrl:s.hubUrl,teamId:s.teamId,name:l,larkAppIds:a});if(Oa(i,p.body,p.status),p.body?.ok){Ce(n).clear(),pt.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):(Ee(),y())}}})}function Oa(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 Ba(e,t){let n=await mt("/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 Zn(){let e=Array.from(new Set(gt().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 mt("/api/team/hosted")).body;if(!t?.ok){Ut=[],Ee();return}Qn=t.deployment.deploymentId,ut=t.suggestedHubUrl||"",F("tf-owner").textContent=t.deployment.ownerName||(t.deployment.ownerUnionId?"\u5DF2\u7ED1\u5B9A":"\u672A\u7ED1\u5B9A"),Ut=(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||[]})),Zn(),Ee()}async function qa(){Yn=((await mt("/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||[]}}),Zn(),Ee()}function eo(e){e.innerHTML=Aa(),jt.clear(),pt.clear(),Je.clear(),Ke.clear(),["tf-search","tf-cli","tf-fcap","tf-frole"].forEach(t=>{let n=F(t);n.oninput=Ee,n.onchange=Ee}),F("tf-modal-cancel").onclick=()=>{F("tf-modal").style.display="none"},Na(),Ve(),qa()}function Pa(){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">
|
|
696
740
|
<div class="page-heading"><div>
|
|
697
741
|
<p class="eyebrow">\u56E2\u961F</p><h1>\u56E2\u961F\u7BA1\u7406</h1>
|
|
698
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>
|
|
699
743
|
</div></div>
|
|
700
|
-
${
|
|
744
|
+
${oa("manage")}
|
|
701
745
|
<div class="card" style="margin-bottom:16px">
|
|
702
746
|
<h2 style="margin-top:0">\u6211\u6258\u7BA1\u7684\u56E2\u961F</h2>
|
|
703
747
|
<p style="display:flex;gap:8px;flex-wrap:wrap;align-items:center;margin-bottom:6px">
|
|
@@ -716,16 +760,16 @@ ${Xn("manage")}
|
|
|
716
760
|
</p>
|
|
717
761
|
<div id="tm-join-out" style="display:none;margin-top:6px"></div>
|
|
718
762
|
</div>
|
|
719
|
-
</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)">
|
|
720
764
|
<div style="display:flex;align-items:center;gap:8px;flex-wrap:wrap">
|
|
721
|
-
<b>${r(
|
|
722
|
-
<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>
|
|
723
767
|
<span style="margin-left:auto;display:flex;gap:6px">
|
|
724
|
-
<button class="tm-invite ghost" data-team="${r(
|
|
725
|
-
${
|
|
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>`}
|
|
726
770
|
</span>
|
|
727
771
|
</div>
|
|
728
|
-
<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">
|
|
729
773
|
<div class="page-heading">
|
|
730
774
|
<div>
|
|
731
775
|
<p class="eyebrow">\u63A5\u5165\u70B9 \xB7 beta</p>
|
|
@@ -787,68 +831,89 @@ ${Xn("manage")}
|
|
|
787
831
|
<h2 style="margin-top:0">\u5DF2\u6709\u63A5\u5165\u70B9 <span class="muted" id="cn-count" style="font-size:13px"></span></h2>
|
|
788
832
|
<div id="cn-list">\u52A0\u8F7D\u4E2D\u2026</div>
|
|
789
833
|
</div>
|
|
790
|
-
</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)">
|
|
791
835
|
<div style="display:flex;align-items:center;gap:8px;flex-wrap:wrap">
|
|
792
836
|
<b style="font-size:15px">${r(n.name)}</b>
|
|
793
837
|
<span class="${n.enabled?"ok":"muted"}" style="font-size:12px">${n.enabled?"\u5DF2\u542F\u7528":"\u5DF2\u505C\u7528"}</span>
|
|
794
|
-
<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>
|
|
795
839
|
<span style="margin-left:auto;display:flex;gap:6px">
|
|
796
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>
|
|
797
841
|
<button class="cn-del ghost" data-id="${r(n.id)}" style="font-size:12px">\u5220\u9664</button>
|
|
798
842
|
</span>
|
|
799
843
|
</div>
|
|
800
844
|
<div style="margin-top:6px;font-size:13px;display:flex;align-items:center;gap:8px;flex-wrap:wrap">
|
|
801
|
-
<span class="muted">Webhook URL\uFF1A</span><code style="font-size:12px;word-break:break-all">${r(
|
|
802
|
-
<button class="cn-copy ghost" data-url="${r(
|
|
803
|
-
</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
|
|
804
|
-
<pre style="margin:6px 0 0;font-size:12px;white-space:pre-wrap;word-break:break-all"><code>curl -X POST '${
|
|
805
|
-
<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>`:
|
|
806
|
-
<pre style="margin:6px 0 0;font-size:12px;white-space:pre-wrap;word-break:break-all"><code>curl -X POST '${
|
|
807
|
-
<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>`:
|
|
808
|
-
<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(
|
|
809
|
-
<p style="margin:4px 0;font-size:13px"><span class="muted">Webhook URL\uFF1A</span><code style="word-break:break-all">${r(
|
|
810
|
-
${
|
|
811
|
-
${
|
|
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">
|
|
812
856
|
<div class="page-heading">
|
|
813
857
|
<div>
|
|
814
|
-
<p class="eyebrow">${
|
|
815
|
-
<h1>${
|
|
816
|
-
<p>${
|
|
858
|
+
<p class="eyebrow">${a("nav.settings")}</p>
|
|
859
|
+
<h1>${a("settings.title")}</h1>
|
|
860
|
+
<p>${a("settings.subtitle")}</p>
|
|
817
861
|
</div>
|
|
818
862
|
</div>
|
|
819
863
|
<div id="settings-body"></div>
|
|
820
|
-
</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">
|
|
821
880
|
<article class="bd-card settings-card">
|
|
822
|
-
${
|
|
881
|
+
${ot?"":`<p class="hint-warn">${a("settings.readOnlyVisitor")}</p>`}
|
|
823
882
|
<section class="bd-section">
|
|
824
|
-
<h3 class="bd-section-title">${
|
|
883
|
+
<h3 class="bd-section-title">${a("settings.sectionAccess")}</h3>
|
|
825
884
|
<label class="toggle-row">
|
|
826
|
-
<input type="checkbox" data-setting="publicReadOnly" ${
|
|
885
|
+
<input type="checkbox" data-setting="publicReadOnly" ${de.publicReadOnly?"checked":""} ${e}>
|
|
827
886
|
<span class="switch" aria-hidden="true"></span>
|
|
828
|
-
<span class="toggle-tx"><strong>${
|
|
829
|
-
<small>${
|
|
887
|
+
<span class="toggle-tx"><strong>${a("settings.publicReadOnly")}</strong>
|
|
888
|
+
<small>${a("settings.publicReadOnlyHelp")}</small></span>
|
|
830
889
|
</label>
|
|
831
890
|
</section>
|
|
832
891
|
<section class="bd-section">
|
|
833
|
-
<h3 class="bd-section-title">${
|
|
892
|
+
<h3 class="bd-section-title">${a("settings.sectionCards")}</h3>
|
|
834
893
|
<label class="toggle-row">
|
|
835
|
-
<input type="checkbox" data-setting="openTerminalInFeishu" ${
|
|
894
|
+
<input type="checkbox" data-setting="openTerminalInFeishu" ${de.openTerminalInFeishu?"checked":""} ${e}>
|
|
836
895
|
<span class="switch" aria-hidden="true"></span>
|
|
837
|
-
<span class="toggle-tx"><strong>${
|
|
838
|
-
<small>${
|
|
896
|
+
<span class="toggle-tx"><strong>${a("settings.openTerminalInFeishu")}</strong>
|
|
897
|
+
<small>${a("settings.openTerminalInFeishuHelp")}</small></span>
|
|
839
898
|
</label>
|
|
840
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>
|
|
841
906
|
<div class="actions settings-actions">
|
|
842
907
|
<span class="oncall-status" data-settings-status></span>
|
|
843
908
|
</div>
|
|
844
909
|
</article>
|
|
845
|
-
</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`
|
|
846
911
|
<nav class="wf-subnav">
|
|
847
|
-
<a href="#/workflows" class="active" data-i18n="workflow.subnav.runs">${m(
|
|
848
|
-
<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>
|
|
849
914
|
</nav>
|
|
850
915
|
<form id="wf-filters" class="filters">
|
|
851
|
-
<input type="search" name="q" placeholder="${m(
|
|
916
|
+
<input type="search" name="q" placeholder="${m(a("workflow.searchPlaceholder"))}" />
|
|
852
917
|
<select name="status">
|
|
853
918
|
${e.map(([t,n])=>`<option value="${m(t)}">${m(n)}</option>`).join("")}
|
|
854
919
|
</select>
|
|
@@ -856,28 +921,28 @@ ${Xn("manage")}
|
|
|
856
921
|
</form>
|
|
857
922
|
<table>
|
|
858
923
|
<thead><tr>
|
|
859
|
-
<th>${m(
|
|
860
|
-
<th>${m(
|
|
861
|
-
<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>
|
|
862
927
|
</tr></thead>
|
|
863
928
|
<tbody id="wf-tbody"></tbody>
|
|
864
929
|
</table>
|
|
865
|
-
`}var
|
|
866
|
-
<td><a href="#/workflows/${encodeURIComponent(
|
|
867
|
-
<td>${m(
|
|
868
|
-
<td>${
|
|
869
|
-
<td>${
|
|
870
|
-
<td class="${
|
|
871
|
-
<td title="${m(new Date(
|
|
872
|
-
<td>${
|
|
873
|
-
</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=`
|
|
874
939
|
<div class="wf-detail-head">
|
|
875
|
-
<a class="btn-link" href="#/workflows">${m(
|
|
940
|
+
<a class="btn-link" href="#/workflows">${m(a("workflow.detail.back"))}</a>
|
|
876
941
|
<div>
|
|
877
942
|
<h2><code>${m(t)}</code></h2>
|
|
878
|
-
<div id="wf-detail-subtitle" class="muted">${m(
|
|
943
|
+
<div id="wf-detail-subtitle" class="muted">${m(a("workflow.detail.loading"))}</div>
|
|
879
944
|
</div>
|
|
880
|
-
<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>
|
|
881
946
|
<span id="wf-detail-refresh" class="muted"></span>
|
|
882
947
|
</div>
|
|
883
948
|
<section id="wf-detail-error" class="hint-warn" hidden></section>
|
|
@@ -886,20 +951,20 @@ ${Xn("manage")}
|
|
|
886
951
|
<section id="wf-dangling-panel"></section>
|
|
887
952
|
<section class="wf-panel">
|
|
888
953
|
<div class="wf-panel-title">
|
|
889
|
-
<h3>${m(
|
|
954
|
+
<h3>${m(a("workflow.detail.parallel"))}</h3>
|
|
890
955
|
<span id="wf-parallel-meta" class="muted"></span>
|
|
891
956
|
</div>
|
|
892
957
|
<div id="wf-parallel-view"></div>
|
|
893
958
|
</section>
|
|
894
959
|
<section class="wf-panel">
|
|
895
960
|
<div class="wf-panel-title">
|
|
896
|
-
<h3>${m(
|
|
961
|
+
<h3>${m(a("workflow.detail.nodes"))}</h3>
|
|
897
962
|
</div>
|
|
898
963
|
<div class="wf-table-scroll">
|
|
899
964
|
<table>
|
|
900
965
|
<thead><tr>
|
|
901
|
-
<th>${m(
|
|
902
|
-
<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>
|
|
903
968
|
</tr></thead>
|
|
904
969
|
<tbody id="wf-node-tbody"></tbody>
|
|
905
970
|
</table>
|
|
@@ -907,269 +972,269 @@ ${Xn("manage")}
|
|
|
907
972
|
</section>
|
|
908
973
|
<section class="wf-panel">
|
|
909
974
|
<div class="wf-panel-title">
|
|
910
|
-
<h3>${m(
|
|
975
|
+
<h3>${m(a("workflow.detail.nodeIO"))}</h3>
|
|
911
976
|
</div>
|
|
912
977
|
<div id="wf-io-list" class="wf-io-list"></div>
|
|
913
978
|
</section>
|
|
914
979
|
<section class="wf-panel">
|
|
915
980
|
<div class="wf-panel-title">
|
|
916
|
-
<h3>${m(
|
|
917
|
-
<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>
|
|
918
983
|
</div>
|
|
919
984
|
<div class="wf-table-scroll wf-timeline-scroll">
|
|
920
985
|
<table>
|
|
921
986
|
<thead><tr>
|
|
922
|
-
<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>
|
|
923
988
|
</tr></thead>
|
|
924
989
|
<tbody id="wf-event-tbody"></tbody>
|
|
925
990
|
</table>
|
|
926
991
|
</div>
|
|
927
992
|
<div id="wf-event-meta" class="muted"></div>
|
|
928
993
|
</section>
|
|
929
|
-
`;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">
|
|
930
995
|
<span class="muted error">${m(e.errorCode)}</span>${m(t)}
|
|
931
|
-
</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>
|
|
932
997
|
<div class="wf-dangling-grid">
|
|
933
|
-
${s.map(([i,l])=>`<div><strong>${i}</strong>${l.length===0?`<div class="muted">${m(
|
|
934
|
-
</div>`}function
|
|
935
|
-
<span title="${m(new Date(l).toISOString())}">${m(
|
|
936
|
-
<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>
|
|
937
1002
|
</div>
|
|
938
|
-
<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(`
|
|
939
1004
|
`);return`<div class="wf-parallel-row">
|
|
940
1005
|
<div class="wf-parallel-label">
|
|
941
|
-
<code>${m(
|
|
942
|
-
<span class="muted">${m(e.activityId)} \xB7 ${m(
|
|
1006
|
+
<code>${m(c)}</code>
|
|
1007
|
+
<span class="muted">${m(e.activityId)} \xB7 ${m(w)}</span>
|
|
943
1008
|
</div>
|
|
944
1009
|
<div class="wf-parallel-track">
|
|
945
|
-
<div class="wf-parallel-bar wf-parallel-${m(e.status)}" style="left:${i.toFixed(3)}%;width:${l.toFixed(3)}%;" title="${m(
|
|
946
|
-
<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>
|
|
947
1012
|
</div>
|
|
948
1013
|
</div>
|
|
949
|
-
</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>
|
|
950
1015
|
<td>${e?`<code>${m(e.nodeId)}</code>`:'<span class="muted">-</span>'}</td>
|
|
951
|
-
<td>${e?
|
|
1016
|
+
<td>${e?xe(e.status):'<span class="muted">-</span>'}</td>
|
|
952
1017
|
<td>${t?`<code>${m(t.activityId)}</code>`:'<span class="muted">-</span>'}</td>
|
|
953
|
-
<td>${t?
|
|
1018
|
+
<td>${t?xe(t.status):'<span class="muted">-</span>'}</td>
|
|
954
1019
|
<td>${t?.attempts.length??0}</td>
|
|
955
1020
|
<td>${n?`<code>${m(n.attemptId)}</code>`:'<span class="muted">-</span>'}</td>
|
|
956
|
-
<td>${n?
|
|
957
|
-
</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=`
|
|
958
1023
|
<header>
|
|
959
1024
|
<div>
|
|
960
|
-
<strong><code>${m(
|
|
961
|
-
<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>
|
|
962
1027
|
</div>
|
|
963
|
-
<div>${t.node?
|
|
1028
|
+
<div>${t.node?xe(t.node.status):""} ${t.activity?xe(t.activity.status):""}</div>
|
|
964
1029
|
</header>
|
|
965
1030
|
<div class="wf-io-meta">
|
|
966
|
-
${l?`${m(
|
|
1031
|
+
${l?`${m(a("workflow.detail.attempt"))} <code>${m(l.attemptId)}</code>`:m(a("workflow.detail.noAttempt"))}
|
|
967
1032
|
</div>
|
|
968
|
-
${
|
|
969
|
-
`;let
|
|
970
|
-
${
|
|
971
|
-
${
|
|
972
|
-
${
|
|
973
|
-
${
|
|
974
|
-
${t.io?.waitPrompt?
|
|
975
|
-
`}function
|
|
976
|
-
<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>
|
|
977
1042
|
<div class="wf-terminal-actions">
|
|
978
|
-
<a class="btn-link" href="${m(
|
|
979
|
-
${
|
|
980
|
-
${
|
|
1043
|
+
<a class="btn-link" href="${m(o.url)}" target="_blank" rel="noreferrer">${m(y)}</a>
|
|
1044
|
+
${v}
|
|
1045
|
+
${p}
|
|
981
1046
|
</div>
|
|
982
|
-
${
|
|
983
|
-
<iframe class="wf-terminal-frame" src="${m(
|
|
984
|
-
</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}>
|
|
985
1050
|
<div class="wf-io-card-head">
|
|
986
1051
|
<header>
|
|
987
1052
|
<div>
|
|
988
1053
|
<strong><code>${m(l)}</code></strong>
|
|
989
|
-
<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>
|
|
990
1055
|
</div>
|
|
991
|
-
<div>${e.node?
|
|
1056
|
+
<div>${e.node?xe(e.node.status):""} ${e.activity?xe(e.activity.status):""}</div>
|
|
992
1057
|
</header>
|
|
993
1058
|
<div class="wf-io-meta">
|
|
994
|
-
${i?`${m(
|
|
1059
|
+
${i?`${m(a("workflow.detail.attempt"))} <code>${m(i.attemptId)}</code>`:m(a("workflow.detail.noAttempt"))}
|
|
995
1060
|
</div>
|
|
996
|
-
${
|
|
1061
|
+
${w}
|
|
997
1062
|
</div>
|
|
998
|
-
<div class="wf-io-terminal-slot">${
|
|
1063
|
+
<div class="wf-io-terminal-slot">${p}</div>
|
|
999
1064
|
<div class="wf-io-grid">
|
|
1000
|
-
${
|
|
1001
|
-
${
|
|
1002
|
-
${
|
|
1003
|
-
${
|
|
1004
|
-
${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):""}
|
|
1005
1070
|
</div>
|
|
1006
|
-
</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)}">
|
|
1007
1072
|
<label>
|
|
1008
|
-
<span>${m(
|
|
1009
|
-
<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>
|
|
1010
1075
|
</label>
|
|
1011
1076
|
<div class="wf-approval-actions">
|
|
1012
|
-
<button type="button" class="primary" data-wf-approval-action="approve" data-wf-attempt-id="${m(n)}"${
|
|
1013
|
-
<button type="button" data-wf-approval-action="reject" data-wf-attempt-id="${m(n)}"${
|
|
1014
|
-
${
|
|
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>`:""}
|
|
1015
1080
|
</div>
|
|
1016
1081
|
${i?`<div class="${l} wf-approval-status">${m(i.text)}</div>`:""}
|
|
1017
|
-
</div>`}function
|
|
1018
|
-
<summary>${m(t)} ${
|
|
1019
|
-
${
|
|
1020
|
-
</details>`}function
|
|
1021
|
-
<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>
|
|
1022
1087
|
<td><code>${m(e.type)}</code></td>
|
|
1023
1088
|
<td>${m(e.actor)}</td>
|
|
1024
1089
|
<td>${t.nodeId?`<code>${m(t.nodeId)}</code>`:"-"}</td>
|
|
1025
1090
|
<td>${t.activityId?`<code>${m(t.activityId)}</code>`:"-"}</td>
|
|
1026
1091
|
<td>${t.errorCode?`<span class="muted error">${m(t.errorCode)}</span>`:"-"}</td>
|
|
1027
|
-
<td title="${m(new Date(e.timestamp).toISOString())}">${m(
|
|
1028
|
-
</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=`
|
|
1029
1094
|
<nav class="wf-subnav">
|
|
1030
|
-
<a href="#/workflows" data-i18n="workflow.subnav.runs">${
|
|
1031
|
-
<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>
|
|
1032
1097
|
</nav>
|
|
1033
1098
|
<section class="catalog-head">
|
|
1034
1099
|
<div>
|
|
1035
|
-
<h2>${
|
|
1036
|
-
<p class="muted">${
|
|
1100
|
+
<h2>${D(a("catalog.title"))}</h2>
|
|
1101
|
+
<p class="muted">${D(a("catalog.subtitle"))}</p>
|
|
1037
1102
|
</div>
|
|
1038
|
-
<button id="catalog-refresh" type="button">${
|
|
1103
|
+
<button id="catalog-refresh" type="button">${D(a("catalog.refresh"))}</button>
|
|
1039
1104
|
</section>
|
|
1040
1105
|
<form id="catalog-filters" class="filters">
|
|
1041
|
-
<input type="search" name="q" placeholder="${
|
|
1106
|
+
<input type="search" name="q" placeholder="${D(a("catalog.searchPlaceholder"))}" />
|
|
1042
1107
|
<span id="catalog-status" class="muted"></span>
|
|
1043
1108
|
</form>
|
|
1044
1109
|
<div class="wf-table-scroll">
|
|
1045
1110
|
<table>
|
|
1046
1111
|
<thead><tr>
|
|
1047
|
-
<th>${
|
|
1048
|
-
<th>${
|
|
1049
|
-
<th>${
|
|
1050
|
-
<th>${
|
|
1051
|
-
<th>${
|
|
1052
|
-
<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>
|
|
1053
1118
|
</tr></thead>
|
|
1054
1119
|
<tbody id="catalog-tbody"></tbody>
|
|
1055
1120
|
</table>
|
|
1056
1121
|
</div>
|
|
1057
|
-
`;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=>`
|
|
1058
1123
|
<tr>
|
|
1059
|
-
<td><a href="#/workflows/catalog/${encodeURIComponent(
|
|
1060
|
-
<td>${
|
|
1061
|
-
<td>${
|
|
1062
|
-
<td>${
|
|
1063
|
-
<td><code>${
|
|
1064
|
-
<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>
|
|
1065
1130
|
</tr>
|
|
1066
|
-
`).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=`
|
|
1067
1132
|
<div class="catalog-detail-head">
|
|
1068
|
-
<a class="btn-link" href="#/workflows/catalog">${
|
|
1133
|
+
<a class="btn-link" href="#/workflows/catalog">${D(a("catalog.back"))}</a>
|
|
1069
1134
|
<div>
|
|
1070
|
-
<h2><code>${
|
|
1071
|
-
<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>
|
|
1072
1137
|
</div>
|
|
1073
1138
|
</div>
|
|
1074
1139
|
<section id="catalog-error" class="hint-warn" hidden></section>
|
|
1075
1140
|
<section id="catalog-run-status" class="hint-ok" hidden></section>
|
|
1076
1141
|
<div id="catalog-detail-body"></div>
|
|
1077
|
-
`;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=`
|
|
1078
1143
|
<section class="wf-panel">
|
|
1079
|
-
<div class="wf-panel-title"><h3>${
|
|
1144
|
+
<div class="wf-panel-title"><h3>${D(a("catalog.summary"))}</h3></div>
|
|
1080
1145
|
<div class="wf-summary-grid">
|
|
1081
|
-
<div class="wf-summary-item"><span>${
|
|
1082
|
-
<div class="wf-summary-item"><span>${
|
|
1083
|
-
<div class="wf-summary-item"><span>${
|
|
1084
|
-
<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>
|
|
1085
1150
|
</div>
|
|
1086
1151
|
</section>
|
|
1087
1152
|
|
|
1088
1153
|
<section class="wf-panel">
|
|
1089
|
-
<div class="wf-panel-title"><h3>${
|
|
1154
|
+
<div class="wf-panel-title"><h3>${D(a("catalog.runPanel"))}</h3></div>
|
|
1090
1155
|
<form id="catalog-run-form" class="catalog-run-form">
|
|
1091
1156
|
<label>
|
|
1092
|
-
<span>${
|
|
1093
|
-
<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>
|
|
1094
1159
|
</label>
|
|
1095
1160
|
<div class="catalog-chat-grid">
|
|
1096
1161
|
<label>
|
|
1097
|
-
<span>${
|
|
1162
|
+
<span>${D(a("catalog.chatId"))}</span>
|
|
1098
1163
|
<input id="catalog-chat-id" type="text" autocomplete="off" />
|
|
1099
1164
|
</label>
|
|
1100
1165
|
<label>
|
|
1101
|
-
<span>${
|
|
1166
|
+
<span>${D(a("catalog.larkAppId"))}</span>
|
|
1102
1167
|
<input id="catalog-lark-app-id" type="text" autocomplete="off" />
|
|
1103
1168
|
</label>
|
|
1104
1169
|
</div>
|
|
1105
|
-
<div class="muted">${
|
|
1170
|
+
<div class="muted">${D(a("catalog.chatBindingHint"))}</div>
|
|
1106
1171
|
<div id="catalog-param-errors" class="catalog-param-errors" hidden></div>
|
|
1107
|
-
<button id="catalog-run-btn" type="submit" class="primary">${
|
|
1172
|
+
<button id="catalog-run-btn" type="submit" class="primary">${D(a("catalog.run"))}</button>
|
|
1108
1173
|
</form>
|
|
1109
1174
|
</section>
|
|
1110
1175
|
|
|
1111
1176
|
<section class="wf-panel">
|
|
1112
|
-
<div class="wf-panel-title"><h3>${
|
|
1113
|
-
${
|
|
1177
|
+
<div class="wf-panel-title"><h3>${D(a("catalog.paramsSchema"))}</h3></div>
|
|
1178
|
+
${ei(f.params)}
|
|
1114
1179
|
</section>
|
|
1115
1180
|
|
|
1116
1181
|
<section class="wf-panel">
|
|
1117
|
-
<div class="wf-panel-title"><h3>${
|
|
1118
|
-
<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>
|
|
1119
1184
|
</section>
|
|
1120
|
-
`,q()}async function
|
|
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])=>`
|
|
1121
1186
|
<article class="catalog-param">
|
|
1122
1187
|
<header>
|
|
1123
|
-
<code>${
|
|
1124
|
-
<span class="wf-status">${
|
|
1125
|
-
<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>
|
|
1126
1191
|
</header>
|
|
1127
|
-
${s.description?`<div class="muted">${
|
|
1128
|
-
${"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>`:""}
|
|
1129
1194
|
</article>
|
|
1130
|
-
`).join("")}</div>`}var
|
|
1131
|
-
<img class="qr-image" src="${e.qrDataUrl}" alt="${
|
|
1132
|
-
${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>`:""}
|
|
1133
1198
|
</div>`:"",s=e.status==="waiting_for_platform_scan"&&e.platformQrDataUrl?`<div class="qr-card">
|
|
1134
|
-
<img class="qr-image" src="${e.platformQrDataUrl}" alt="${
|
|
1135
|
-
</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>
|
|
1136
1201
|
<header>
|
|
1137
|
-
<h3>${
|
|
1138
|
-
<p>${
|
|
1202
|
+
<h3>${a("botOnboarding.title")}</h3>
|
|
1203
|
+
<p>${a("botOnboarding.intro")}</p>
|
|
1139
1204
|
</header>
|
|
1140
|
-
<p class="onboarding-status status-${e.status}">${
|
|
1205
|
+
<p class="onboarding-status status-${e.status}">${ti(e)}</p>
|
|
1141
1206
|
${n}
|
|
1142
1207
|
${s}
|
|
1143
|
-
${
|
|
1144
|
-
${
|
|
1208
|
+
${o}
|
|
1209
|
+
${ni(e)}
|
|
1145
1210
|
${i}
|
|
1146
|
-
<form method="dialog"><button>${
|
|
1147
|
-
</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>
|
|
1148
1213
|
<header>
|
|
1149
|
-
<h3>${
|
|
1150
|
-
<p>${
|
|
1214
|
+
<h3>${a("botOnboarding.title")}</h3>
|
|
1215
|
+
<p>${a("botOnboarding.intro")}</p>
|
|
1151
1216
|
</header>
|
|
1152
1217
|
<form id="onboarding-form" class="onboarding-form">
|
|
1153
1218
|
<label class="onboarding-field">
|
|
1154
|
-
<span>${
|
|
1219
|
+
<span>${a("botOnboarding.cliLabel")}</span>
|
|
1155
1220
|
<select id="ob-cli">${s}</select>
|
|
1156
1221
|
</label>
|
|
1157
1222
|
<label class="onboarding-field">
|
|
1158
|
-
<span>${
|
|
1159
|
-
<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">
|
|
1160
1225
|
</label>
|
|
1161
1226
|
<label class="onboarding-field">
|
|
1162
|
-
<span>${
|
|
1163
|
-
<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">
|
|
1164
1229
|
</label>
|
|
1165
|
-
${
|
|
1230
|
+
${o}
|
|
1166
1231
|
<menu class="onboarding-actions">
|
|
1167
|
-
<button type="button" id="ob-cancel">${
|
|
1168
|
-
<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>
|
|
1169
1234
|
</menu>
|
|
1170
1235
|
</form>
|
|
1171
|
-
</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=`
|
|
1172
1237
|
<span class="attention-strip-ic" aria-hidden="true">!</span>
|
|
1173
|
-
<b>${r(
|
|
1174
|
-
<span class="attention-strip-longest">${r(
|
|
1175
|
-
<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()})();})();
|