botmux 2.33.1 → 2.35.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 +5 -3
- package/README.md +5 -3
- package/dist/bot-registry.d.ts +1 -0
- package/dist/bot-registry.d.ts.map +1 -1
- package/dist/bot-registry.js +7 -0
- package/dist/bot-registry.js.map +1 -1
- package/dist/cli.js +50 -6
- package/dist/cli.js.map +1 -1
- package/dist/core/command-handler.d.ts.map +1 -1
- package/dist/core/command-handler.js +69 -6
- package/dist/core/command-handler.js.map +1 -1
- package/dist/core/dashboard-ipc-server.d.ts.map +1 -1
- package/dist/core/dashboard-ipc-server.js +45 -1
- package/dist/core/dashboard-ipc-server.js.map +1 -1
- package/dist/core/role-resolver.d.ts +28 -0
- package/dist/core/role-resolver.d.ts.map +1 -0
- package/dist/core/role-resolver.js +120 -0
- package/dist/core/role-resolver.js.map +1 -0
- package/dist/core/session-manager.d.ts +6 -1
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +21 -2
- package/dist/core/session-manager.js.map +1 -1
- package/dist/daemon.d.ts.map +1 -1
- package/dist/daemon.js +8 -4
- package/dist/daemon.js.map +1 -1
- package/dist/dashboard/web/app.js +3 -0
- package/dist/dashboard/web/app.js.map +1 -1
- package/dist/dashboard/web/i18n.d.ts.map +1 -1
- package/dist/dashboard/web/i18n.js +38 -0
- package/dist/dashboard/web/i18n.js.map +1 -1
- package/dist/dashboard/web/roles.d.ts +2 -0
- package/dist/dashboard/web/roles.d.ts.map +1 -0
- package/dist/dashboard/web/roles.js +352 -0
- package/dist/dashboard/web/roles.js.map +1 -0
- package/dist/dashboard-web/app.js +393 -324
- package/dist/dashboard-web/index.html +1 -0
- package/dist/dashboard-web/style.css +269 -0
- package/dist/dashboard.js +38 -2
- package/dist/dashboard.js.map +1 -1
- package/dist/i18n/en.d.ts.map +1 -1
- package/dist/i18n/en.js +24 -9
- package/dist/i18n/en.js.map +1 -1
- package/dist/i18n/zh.d.ts.map +1 -1
- package/dist/i18n/zh.js +24 -9
- package/dist/i18n/zh.js.map +1 -1
- package/dist/im/lark/card-builder.d.ts.map +1 -1
- package/dist/im/lark/card-builder.js +0 -1
- package/dist/im/lark/card-builder.js.map +1 -1
- package/dist/im/lark/card-handler.js +15 -17
- package/dist/im/lark/card-handler.js.map +1 -1
- package/dist/im/lark/client.d.ts.map +1 -1
- package/dist/im/lark/client.js.map +1 -1
- package/dist/im/lark/event-dispatcher.d.ts.map +1 -1
- package/dist/im/lark/event-dispatcher.js +15 -5
- package/dist/im/lark/event-dispatcher.js.map +1 -1
- package/dist/im/lark/grant-command.d.ts.map +1 -1
- package/dist/im/lark/grant-command.js +24 -3
- package/dist/im/lark/grant-command.js.map +1 -1
- package/dist/services/allowed-chat-groups.d.ts +11 -0
- package/dist/services/allowed-chat-groups.d.ts.map +1 -0
- package/dist/services/allowed-chat-groups.js +20 -0
- package/dist/services/allowed-chat-groups.js.map +1 -0
- package/dist/services/grant-store.d.ts +11 -2
- package/dist/services/grant-store.d.ts.map +1 -1
- package/dist/services/grant-store.js +50 -18
- package/dist/services/grant-store.js.map +1 -1
- package/dist/setup/bot-config-editor.d.ts +22 -0
- package/dist/setup/bot-config-editor.d.ts.map +1 -1
- package/dist/setup/bot-config-editor.js +47 -1
- package/dist/setup/bot-config-editor.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,31 +1,33 @@
|
|
|
1
|
-
"use strict";(()=>{var ye=class{sessions=new Map;schedules=new Map;online=!0;listeners=new Set;upsertSessions(n){for(let o of n)this.sessions.set(o.sessionId,o);this.emit()}upsertSchedules(n){for(let o of n)this.schedules.set(o.id,o);this.emit()}applySse(n,o){if(n==="session.spawned")this.sessions.set(o.session.sessionId,o.session);else if(n==="session.update"){let r=this.sessions.get(o.sessionId);r&&this.sessions.set(o.sessionId,{...r,...o.patch})}else if(n==="session.exited"){let r=this.sessions.get(o.sessionId);r&&this.sessions.set(o.sessionId,{...r,status:"closed"})}else if(n==="schedule.created")this.schedules.set(o.schedule.id,o.schedule);else if(n==="schedule.updated"){let r=this.schedules.get(o.id);r&&this.schedules.set(o.id,{...r,...o.patch})}else if(n==="schedule.deleted")this.schedules.delete(o.id);else return;this.emit()}setOnline(n){this.online!==n&&(this.online=n,this.emit())}on(n){return this.listeners.add(n),()=>this.listeners.delete(n)}emit(){for(let n of this.listeners)n()}},D=new ye;async function De(){let[e,n]=await Promise.all([fetch("/api/sessions").then(s=>s.json()),fetch("/api/schedules").then(s=>s.json())]);D.upsertSessions(e.sessions??[]),D.upsertSchedules(n.schedules??[]);let o=new EventSource("/events"),r=["session.spawned","session.update","session.exited","schedule.created","schedule.updated","schedule.deleted","schedule.fired","heartbeat"];for(let s of r)o.addEventListener(s,a=>{try{let c=JSON.parse(a.data);D.applySse(s,c.body??c)}catch{}});o.onerror=()=>D.setOnline(!1),o.onopen=()=>D.setOnline(!0)}var $e="botmux.dashboard.locale",St={"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.botDefaults":"\u9ED8\u8BA4 Bot","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","botOnboarding.add":"\u6DFB\u52A0\u673A\u5668\u4EBA","botOnboarding.title":"\u626B\u7801\u6DFB\u52A0\u673A\u5668\u4EBA","botOnboarding.intro":"\u7528\u98DE\u4E66 App \u626B\u7801\u521B\u5EFA PersonalAgent \u5E94\u7528\uFF0C\u6210\u529F\u540E\u4F1A\u5199\u5165\u672C\u673A bots.json\u3002","botOnboarding.starting":"\u6B63\u5728\u751F\u6210\u4E8C\u7EF4\u7801...","botOnboarding.waiting":"\u8BF7\u7528\u98DE\u4E66 App \u626B\u7801\u786E\u8BA4\u3002","botOnboarding.verifying":"\u626B\u7801\u6210\u529F\uFF0C\u6B63\u5728\u6821\u9A8C\u51ED\u8BC1...","botOnboarding.completed":"\u673A\u5668\u4EBA\u5DF2\u6DFB\u52A0\u3002","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":"\u63A7\u5236\u53F0\u603B\u89C8","overview.subtitle":"\u8DE8 bot\u3001\u7FA4\u804A\u3001\u4F1A\u8BDD\u548C\u5B9A\u65F6\u4EFB\u52A1\u7684\u5B9E\u65F6\u7BA1\u63A7\u9762\u3002","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.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","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","botDefaults.title":"Bot \u9ED8\u8BA4 Oncall","botDefaults.subtitle":"\u914D\u7F6E\u6BCF\u4E2A bot \u5728\u65B0\u7FA4\u91CC\u7684\u9ED8\u8BA4 oncall \u884C\u4E3A\u3002","botDefaults.search":"\u641C\u7D22 bot \u540D / app id","botDefaults.refresh":"\u5237\u65B0","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","common.none":"\u65E0","common.unknown":"\u672A\u77E5","common.now":"\u521A\u521A","common.never":"\u4ECE\u672A","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
|
|
1
|
+
"use strict";(()=>{var Ce=class{sessions=new Map;schedules=new Map;online=!0;listeners=new Set;upsertSessions(t){for(let o of t)this.sessions.set(o.sessionId,o);this.emit()}upsertSchedules(t){for(let o of t)this.schedules.set(o.id,o);this.emit()}applySse(t,o){if(t==="session.spawned")this.sessions.set(o.session.sessionId,o.session);else if(t==="session.update"){let r=this.sessions.get(o.sessionId);r&&this.sessions.set(o.sessionId,{...r,...o.patch})}else if(t==="session.exited"){let r=this.sessions.get(o.sessionId);r&&this.sessions.set(o.sessionId,{...r,status:"closed"})}else if(t==="schedule.created")this.schedules.set(o.schedule.id,o.schedule);else if(t==="schedule.updated"){let r=this.schedules.get(o.id);r&&this.schedules.set(o.id,{...r,...o.patch})}else if(t==="schedule.deleted")this.schedules.delete(o.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()}},O=new Ce;async function Je(){let[e,t]=await Promise.all([fetch("/api/sessions").then(s=>s.json()),fetch("/api/schedules").then(s=>s.json())]);O.upsertSessions(e.sessions??[]),O.upsertSchedules(t.schedules??[]);let o=new EventSource("/events"),r=["session.spawned","session.update","session.exited","schedule.created","schedule.updated","schedule.deleted","schedule.fired","heartbeat"];for(let s of r)o.addEventListener(s,a=>{try{let d=JSON.parse(a.data);O.applySse(s,d.body??d)}catch{}});o.onerror=()=>O.setOnline(!1),o.onopen=()=>O.setOnline(!0)}var Me="botmux.dashboard.locale",Bt={"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.botDefaults":"\u9ED8\u8BA4 Bot","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","botOnboarding.add":"\u6DFB\u52A0\u673A\u5668\u4EBA","botOnboarding.title":"\u626B\u7801\u6DFB\u52A0\u673A\u5668\u4EBA","botOnboarding.intro":"\u7528\u98DE\u4E66 App \u626B\u7801\u521B\u5EFA PersonalAgent \u5E94\u7528\uFF0C\u6210\u529F\u540E\u4F1A\u5199\u5165\u672C\u673A bots.json\u3002","botOnboarding.starting":"\u6B63\u5728\u751F\u6210\u4E8C\u7EF4\u7801...","botOnboarding.waiting":"\u8BF7\u7528\u98DE\u4E66 App \u626B\u7801\u786E\u8BA4\u3002","botOnboarding.verifying":"\u626B\u7801\u6210\u529F\uFF0C\u6B63\u5728\u6821\u9A8C\u51ED\u8BC1...","botOnboarding.completed":"\u673A\u5668\u4EBA\u5DF2\u6DFB\u52A0\u3002","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":"\u63A7\u5236\u53F0\u603B\u89C8","overview.subtitle":"\u8DE8 bot\u3001\u7FA4\u804A\u3001\u4F1A\u8BDD\u548C\u5B9A\u65F6\u4EFB\u52A1\u7684\u5B9E\u65F6\u7BA1\u63A7\u9762\u3002","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.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","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","botDefaults.title":"Bot \u9ED8\u8BA4 Oncall","botDefaults.subtitle":"\u914D\u7F6E\u6BCF\u4E2A bot \u5728\u65B0\u7FA4\u91CC\u7684\u9ED8\u8BA4 oncall \u884C\u4E3A\u3002","botDefaults.search":"\u641C\u7D22 bot \u540D / app id","botDefaults.refresh":"\u5237\u65B0","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","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
|
+
\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","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
|
|
2
3
|
|
|
3
4
|
{total} \u4E2A\u60AC\u6302\u9879\u4F1A\u7531 cancel recovery \u5904\u7406\u3002
|
|
4
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":`{
|
|
5
6
|
"city": "\u5317\u4EAC"
|
|
6
|
-
}`,"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"},
|
|
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"},Pt={"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.botDefaults":"Bot Defaults","status.live":"Live","status.disconnected":"Disconnected","status.system":"System","status.light":"Light","status.dark":"Dark","status.language":"Language","status.theme":"Theme","botOnboarding.add":"Add Bot","botOnboarding.title":"Scan to Add Bot","botOnboarding.intro":"Scan with the Feishu app to create a PersonalAgent app. The dashboard writes it to local bots.json after success.","botOnboarding.starting":"Generating QR code...","botOnboarding.waiting":"Scan with the Feishu app to continue.","botOnboarding.verifying":"Scan accepted. Verifying credentials...","botOnboarding.completed":"Bot added.","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":"Control Overview","overview.subtitle":"A realtime control plane across bots, chats, CLI sessions, and schedules.","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.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.","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.","botDefaults.title":"Bot Default Oncall","botDefaults.subtitle":"Configure each bot's default oncall behavior in new chats.","botDefaults.search":"Search bot name / app id","botDefaults.refresh":"Refresh","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","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
|
+
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","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}?
|
|
7
9
|
|
|
8
10
|
{total} dangling item(s) will be handled by cancel-driven recovery.
|
|
9
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":`{
|
|
10
12
|
"city": "\u5317\u4EAC"
|
|
11
|
-
}`,"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"},
|
|
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"},Ge={zh:Bt,en:Pt};function ze(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 Nt(e=[]){for(let t of e){let o=ze(t);if(o)return o}return"zh"}function he(e){return(t,o)=>{let r=Ge[e][t]??Ge.zh[t]??t;return o?r.replace(/\{(\w+)\}/g,(s,a)=>{let d=o[a];return d==null?`{${a}}`:String(d)}):r}}function Ke(e,t){return(e?ze(e.getItem(Me)):null)??Nt(t)}var Ae="botmux.dashboard.theme";function jt(e){return e==="system"||e==="light"||e==="dark"?e:null}function Ve(e,t){return e==="system"?t?"dark":"light":e}function Ye(e){return jt(e?.getItem(Ae))??"system"}var He=class{locale="zh";themeMode="system";resolvedTheme="light";listeners=new Set;translate=he(this.locale);mediaQuery=null;init(){let t=typeof window<"u"?window:void 0;this.locale=Ke(t?.localStorage,Ut()),this.translate=he(this.locale),this.themeMode=Ye(t?.localStorage),this.mediaQuery=t?.matchMedia?.("(prefers-color-scheme: dark)")??null,this.mediaQuery?.addEventListener("change",()=>{this.applyTheme(),this.emit()}),this.applyTheme(),this.applyLocale()}t(t,o){return this.translate(t,o)}setLocale(t){this.locale!==t&&(this.locale=t,this.translate=he(t),window.localStorage.setItem(Me,t),this.applyLocale(),this.emit())}setThemeMode(t){this.themeMode!==t&&(this.themeMode=t,window.localStorage.setItem(Ae,t),this.applyTheme(),this.emit())}on(t){return this.listeners.add(t),()=>this.listeners.delete(t)}emit(){for(let t of this.listeners)t()}applyTheme(){this.resolvedTheme=Ve(this.themeMode,!!this.mediaQuery?.matches),document.documentElement.dataset.theme=this.resolvedTheme,document.documentElement.dataset.themeMode=this.themeMode}applyLocale(){document.documentElement.lang=this.locale==="zh"?"zh-CN":"en"}};function Ut(){return typeof navigator>"u"?[]:navigator.languages?.length?navigator.languages:[navigator.language].filter(Boolean)}var K=new He;function n(e,t){return K.t(e,t)}function m(e){return e.replace(/[&<>"']/g,t=>({"&":"&","<":"<",">":">",'"':""","'":"'"})[t])}function re(e){if(!e)return"-";let t=Date.now()-e;return t<6e4?n("common.now"):t<36e5?Math.floor(t/6e4)+"m":t<864e5?Math.floor(t/36e5)+"h":Math.floor(t/864e5)+"d"}var Re={chats:[],bots:[]};async function Ft(){try{let e=await fetch("/api/groups");if(!e.ok)return;Re=await e.json()}catch{}}function Wt(e){return`status status-${m(e||"unknown")}`}function _t(e){return`<li class="overview-list-row">
|
|
12
14
|
<div>
|
|
13
|
-
<strong>${
|
|
14
|
-
<span>${
|
|
15
|
+
<strong>${m(e.title??e.sessionId)}</strong>
|
|
16
|
+
<span>${m(e.botName??"")} \xB7 ${m(e.cliId??"unknown")}</span>
|
|
15
17
|
</div>
|
|
16
|
-
<span class="${
|
|
17
|
-
</li>`}function
|
|
18
|
+
<span class="${Wt(e.status)}">${m(e.status??"unknown")}</span>
|
|
19
|
+
</li>`}function Jt(e){let t=e.nextRunAt?new Date(e.nextRunAt).toLocaleString():"-";return`<li class="overview-list-row">
|
|
18
20
|
<div>
|
|
19
|
-
<strong>${
|
|
20
|
-
<span>${
|
|
21
|
+
<strong>${m(e.name??e.id)}</strong>
|
|
22
|
+
<span>${m(e.botName??e.larkAppId??"")} \xB7 ${m(e.parsed?.display??"")}</span>
|
|
21
23
|
</div>
|
|
22
|
-
<span>${
|
|
23
|
-
</li>`}async function
|
|
24
|
+
<span>${m(t)}</span>
|
|
25
|
+
</li>`}async function Qe(e){e.innerHTML=`<section class="page hero-page">
|
|
24
26
|
<div class="page-heading">
|
|
25
27
|
<div>
|
|
26
|
-
<p class="eyebrow">${
|
|
27
|
-
<h1>${
|
|
28
|
-
<p>${
|
|
28
|
+
<p class="eyebrow">${n("app.subtitle")}</p>
|
|
29
|
+
<h1>${n("overview.title")}</h1>
|
|
30
|
+
<p>${n("overview.subtitle")}</p>
|
|
29
31
|
</div>
|
|
30
32
|
</div>
|
|
31
33
|
<div class="metric-grid" id="overview-metrics"></div>
|
|
@@ -33,251 +35,251 @@ effects={effects}, activities={activities}, waits={waits}, cancels={cancels}`,"w
|
|
|
33
35
|
<section class="panel">
|
|
34
36
|
<header class="panel-header">
|
|
35
37
|
<div>
|
|
36
|
-
<h2>${
|
|
37
|
-
<p>${
|
|
38
|
+
<h2>${n("overview.recentSessions")}</h2>
|
|
39
|
+
<p>${n("sessions.subtitle")}</p>
|
|
38
40
|
</div>
|
|
39
|
-
<a class="btn-link" href="#/sessions">${
|
|
41
|
+
<a class="btn-link" href="#/sessions">${n("nav.sessions")}</a>
|
|
40
42
|
</header>
|
|
41
43
|
<ul class="overview-list" id="recent-sessions"></ul>
|
|
42
44
|
</section>
|
|
43
45
|
<section class="panel">
|
|
44
46
|
<header class="panel-header">
|
|
45
47
|
<div>
|
|
46
|
-
<h2>${
|
|
47
|
-
<p>${
|
|
48
|
+
<h2>${n("overview.nextSchedules")}</h2>
|
|
49
|
+
<p>${n("schedules.subtitle")}</p>
|
|
48
50
|
</div>
|
|
49
|
-
<a class="btn-link" href="#/schedules">${
|
|
51
|
+
<a class="btn-link" href="#/schedules">${n("nav.schedules")}</a>
|
|
50
52
|
</header>
|
|
51
53
|
<ul class="overview-list" id="next-schedules"></ul>
|
|
52
54
|
</section>
|
|
53
55
|
</div>
|
|
54
|
-
</section>`;let
|
|
55
|
-
<span>${
|
|
56
|
+
</section>`;let t=e.querySelector("#overview-metrics"),o=e.querySelector("#recent-sessions"),r=e.querySelector("#next-schedules");function s(){let a=[...O.sessions.values()],d=[...O.schedules.values()],f=a.filter(L=>L.status!=="closed"),y=a.filter(L=>L.status==="working"||L.status==="analyzing"||L.status==="starting"),S=d.filter(L=>L.enabled),b=Re.bots?.length||new Set(a.map(L=>L.larkAppId).filter(Boolean)).size,k=[{label:n("overview.openSessions"),value:f.length,meta:`${a.length} ${n("overview.total")}`},{label:n("overview.workingSessions"),value:y.length,meta:`${f.length} ${n("overview.active")}`},{label:n("overview.onlineBots"),value:b,meta:n("overview.daemonRegistry")},{label:n("overview.schedules"),value:d.length,meta:`${S.length} ${n("overview.enabledSchedules")}`},{label:n("overview.groups"),value:Re.chats?.length??0,meta:n("overview.chatMatrix")}];t.innerHTML=k.map(L=>`<article class="metric-card">
|
|
57
|
+
<span>${m(L.label)}</span>
|
|
56
58
|
<strong>${L.value}</strong>
|
|
57
|
-
<small>${
|
|
58
|
-
</article>`).join("");let l=a.sort((L,g)=>Number(g.lastMessageAt??0)-Number(L.lastMessageAt??0)).slice(0,6);o.innerHTML=l.length?l.map(L=>
|
|
59
|
-
<span class="filter-check-label">${
|
|
60
|
-
${
|
|
59
|
+
<small>${m(L.meta)}</small>
|
|
60
|
+
</article>`).join("");let l=a.sort((L,g)=>Number(g.lastMessageAt??0)-Number(L.lastMessageAt??0)).slice(0,6);o.innerHTML=l.length?l.map(L=>_t({...L,title:L.title??`${re(L.lastMessageAt)} \xB7 ${L.sessionId}`})).join(""):`<li class="empty">${n("overview.noSessions")}</li>`;let T=d.filter(L=>L.nextRunAt).sort((L,g)=>Date.parse(L.nextRunAt)-Date.parse(g.nextRunAt)).slice(0,6);r.innerHTML=T.length?T.map(Jt).join(""):`<li class="empty">${n("overview.noSchedules")}</li>`}O.on(s),s(),Ft().then(s)}function V(e,t){return`<th data-sort="${e}" data-label="${m(t)}">${m(t)}</th>`}var Xe=["claude-code","codex","cursor","gemini","opencode","aiden","coco","unknown"];function Gt(){return`<div class="filter-check-group" role="group" aria-label="${n("sessions.cli")}">
|
|
61
|
+
<span class="filter-check-label">${n("sessions.cli")}</span>
|
|
62
|
+
${Xe.map(e=>`
|
|
61
63
|
<label class="filter-check">
|
|
62
|
-
<input type="checkbox" name="cli" value="${
|
|
63
|
-
<span>${
|
|
64
|
+
<input type="checkbox" name="cli" value="${m(e)}" checked>
|
|
65
|
+
<span>${m(e)}</span>
|
|
64
66
|
</label>
|
|
65
67
|
`).join("")}
|
|
66
|
-
</div>`}function
|
|
68
|
+
</div>`}function zt(){return`<section class="page">
|
|
67
69
|
<div class="page-heading">
|
|
68
70
|
<div>
|
|
69
|
-
<p class="eyebrow">${
|
|
70
|
-
<h1>${
|
|
71
|
-
<p>${
|
|
71
|
+
<p class="eyebrow">${n("nav.sessions")}</p>
|
|
72
|
+
<h1>${n("sessions.title")}</h1>
|
|
73
|
+
<p>${n("sessions.subtitle")}</p>
|
|
72
74
|
</div>
|
|
73
75
|
</div>
|
|
74
76
|
<form id="filters" class="filters sessions-filters">
|
|
75
|
-
<input type="search" name="q" placeholder="${
|
|
77
|
+
<input type="search" name="q" placeholder="${n("sessions.search")}" />
|
|
76
78
|
<select name="status">
|
|
77
|
-
<option value="">${
|
|
79
|
+
<option value="">${n("sessions.anyStatus")}</option>
|
|
78
80
|
<option>starting</option><option>working</option><option>idle</option>
|
|
79
81
|
<option>analyzing</option><option>active</option><option>closed</option>
|
|
80
82
|
</select>
|
|
81
83
|
<select name="adopt">
|
|
82
|
-
<option value="">${
|
|
83
|
-
<option value="yes">${
|
|
84
|
-
<option value="no">${
|
|
84
|
+
<option value="">${n("sessions.adoptAny")}</option>
|
|
85
|
+
<option value="yes">${n("sessions.adoptYes")}</option>
|
|
86
|
+
<option value="no">${n("sessions.adoptNo")}</option>
|
|
85
87
|
</select>
|
|
86
|
-
<label class="filter-toggle"><input type="checkbox" name="active" checked> ${
|
|
87
|
-
${
|
|
88
|
+
<label class="filter-toggle"><input type="checkbox" name="active" checked> ${n("sessions.activeOnly")}</label>
|
|
89
|
+
${Gt()}
|
|
88
90
|
</form>
|
|
89
91
|
<div id="bulk-bar" class="bulk-bar" hidden>
|
|
90
92
|
<span id="bulk-count"></span>
|
|
91
|
-
<button type="button" id="bulk-close" class="contrast">${
|
|
92
|
-
<button type="button" id="bulk-clear">${
|
|
93
|
+
<button type="button" id="bulk-close" class="contrast">${n("sessions.closeSelected")}</button>
|
|
94
|
+
<button type="button" id="bulk-clear">${n("sessions.clearSelection")}</button>
|
|
93
95
|
</div>
|
|
94
96
|
<table id="sessions-table">
|
|
95
97
|
<thead><tr>
|
|
96
|
-
<th><input type="checkbox" id="select-all" title="${
|
|
97
|
-
${
|
|
98
|
-
${
|
|
99
|
-
${
|
|
100
|
-
${
|
|
101
|
-
${
|
|
102
|
-
${
|
|
103
|
-
${
|
|
104
|
-
${
|
|
105
|
-
<th>${
|
|
98
|
+
<th><input type="checkbox" id="select-all" title="${n("sessions.activeOnly")}"></th>
|
|
99
|
+
${V("botName",n("sessions.bot"))}
|
|
100
|
+
${V("cliId",n("sessions.cli"))}
|
|
101
|
+
${V("status",n("sessions.status"))}
|
|
102
|
+
${V("title",n("sessions.titleCol"))}
|
|
103
|
+
${V("workingDir",n("sessions.workingDir"))}
|
|
104
|
+
${V("spawnedAt",n("sessions.created"))}
|
|
105
|
+
${V("lastMessageAt",n("sessions.last"))}
|
|
106
|
+
${V("adopt",n("sessions.adopt"))}
|
|
107
|
+
<th>${n("sessions.actions")}</th>
|
|
106
108
|
</tr></thead>
|
|
107
109
|
<tbody></tbody>
|
|
108
110
|
</table>
|
|
109
111
|
<dialog id="drawer"></dialog>
|
|
110
|
-
</section>`}function
|
|
111
|
-
<td><input type="checkbox" class="row-select" ${h} ${
|
|
112
|
-
<td>${
|
|
113
|
-
<td><span class="badge cli-${
|
|
114
|
-
<td><span class="status status-${
|
|
115
|
-
<td>${
|
|
116
|
-
<td title="${
|
|
117
|
-
<td>${
|
|
118
|
-
<td>${
|
|
119
|
-
<td>${
|
|
120
|
-
<td><button class="open" type="button">${
|
|
121
|
-
</tr>`}function L(){let
|
|
112
|
+
</section>`}function Ze(e){e.innerHTML=zt();let t=e.querySelector("#sessions-table tbody"),o=e.querySelector("#filters"),r=e.querySelector("#drawer"),s=e.querySelector("#select-all"),a=e.querySelector("#bulk-bar"),d=e.querySelector("#bulk-count"),f=e.querySelector("#bulk-close"),y=e.querySelector("#bulk-clear"),S=e.querySelector("#sessions-table"),b=new Set,k="lastMessageAt",l="desc";function T(c){let p=c.status==="closed",h=b.has(c.sessionId)?"checked":"";return`<tr data-id="${m(c.sessionId)}">
|
|
113
|
+
<td><input type="checkbox" class="row-select" ${h} ${p?"disabled":""}></td>
|
|
114
|
+
<td>${m(c.botName??"")}</td>
|
|
115
|
+
<td><span class="badge cli-${m(c.cliId??"unknown")}">${m(c.cliId??"unknown")}</span></td>
|
|
116
|
+
<td><span class="status status-${m(c.status??"unknown")}">${m(c.status??"unknown")}</span></td>
|
|
117
|
+
<td>${m((c.title??"").slice(0,48))}</td>
|
|
118
|
+
<td title="${m(c.workingDir??"")}">${m((c.workingDir??"").slice(-34))}</td>
|
|
119
|
+
<td>${re(c.spawnedAt)}</td>
|
|
120
|
+
<td>${re(c.lastMessageAt)}</td>
|
|
121
|
+
<td>${c.adopt?'<span class="badge">adopt</span>':""}</td>
|
|
122
|
+
<td><button class="open" type="button">${n("sessions.details")}</button></td>
|
|
123
|
+
</tr>`}function L(){let c=new FormData(o),p=(c.get("q")??"").toLowerCase(),h=c.getAll("cli"),E=h.length>0&&h.length<Xe.length,A=c.get("status"),R=c.get("adopt"),q=!!c.get("active"),B=[...O.sessions.values()].filter(D=>!E||h.includes(D.cliId??"unknown")).filter(D=>!A||D.status===A).filter(D=>!R||R==="yes"==!!D.adopt).filter(D=>!q||D.status!=="closed").filter(D=>!p||JSON.stringify(D).toLowerCase().includes(p));return B.sort(u),B}function g(c,p){return p==="spawnedAt"||p==="lastMessageAt"?Number(c[p]??0):p==="adopt"?!!c.adopt:String(c[p]??"").toLowerCase()}function u(c,p){let h=g(c,k),E=g(p,k),A=0;return typeof h=="number"&&typeof E=="number"?A=h-E:typeof h=="boolean"&&typeof E=="boolean"?A=Number(h)-Number(E):A=String(h).localeCompare(String(E)),A===0&&(A=Number(c.lastMessageAt??0)-Number(p.lastMessageAt??0)),l==="asc"?A:-A}function I(){S.querySelectorAll("th[data-sort]").forEach(c=>{let p=c.dataset.sort===k;c.classList.toggle("sorted",p),c.setAttribute("aria-sort",p?l==="asc"?"ascending":"descending":"none");let h=c.dataset.label??c.textContent?.trim()??"";c.textContent=p?`${h} ${l==="asc"?"\u25B2":"\u25BC"}`:h})}function $(c){a.hidden=b.size===0,d.textContent=n("sessions.selectedCount",{count:b.size});let p=c.filter(E=>E.status!=="closed");if(p.length===0){s.checked=!1,s.indeterminate=!1,s.disabled=!0;return}s.disabled=!1;let h=p.filter(E=>b.has(E.sessionId)).length;s.checked=h===p.length,s.indeterminate=h>0&&h<p.length}function v(){let c=L();for(let p of[...b]){let h=O.sessions.get(p);(!h||h.status==="closed")&&b.delete(p)}t.innerHTML=c.length?c.map(T).join(""):`<tr><td colspan="10" class="empty">${n("sessions.empty")}</td></tr>`,I(),$(c)}function M(c){let p=c.status==="closed";r.innerHTML=`<article>
|
|
122
124
|
<header>
|
|
123
|
-
<h3>${
|
|
124
|
-
<span class="status status-${
|
|
125
|
-
<p><code>${
|
|
125
|
+
<h3>${m(c.title??c.sessionId)}</h3>
|
|
126
|
+
<span class="status status-${m(c.status??"unknown")}">${m(c.status??"unknown")}</span>
|
|
127
|
+
<p><code>${m(c.sessionId)}</code> <button data-copy="${m(c.sessionId)}">${n("sessions.copy")}</button></p>
|
|
126
128
|
</header>
|
|
127
|
-
<p><b>${
|
|
128
|
-
<p><b>chatId:</b> <code>${
|
|
129
|
-
<p><b>rootMessageId:</b> <code>${
|
|
130
|
-
${
|
|
131
|
-
<p><b>${
|
|
129
|
+
<p><b>${n("sessions.bot")}:</b> ${m(c.botName??"-")} \xB7 <b>${n("sessions.cli")}:</b> ${m(c.cliId??"?")}</p>
|
|
130
|
+
<p><b>chatId:</b> <code>${m(c.chatId??"")}</code> <button data-copy="${m(c.chatId??"")}">${n("sessions.copy")}</button></p>
|
|
131
|
+
<p><b>rootMessageId:</b> <code>${m(c.rootMessageId??"")}</code> <button data-copy="${m(c.rootMessageId??"")}">${n("sessions.copy")}</button></p>
|
|
132
|
+
${c.threadId?`<p><b>threadId:</b> <code>${m(c.threadId)}</code></p>`:""}
|
|
133
|
+
<p><b>${n("sessions.workingDir")}:</b> ${m(c.workingDir??"-")}</p>
|
|
132
134
|
<div class="actions">
|
|
133
|
-
<button id="locate-btn" type="button">${
|
|
134
|
-
${
|
|
135
|
-
${
|
|
136
|
-
${
|
|
135
|
+
<button id="locate-btn" type="button">${n("sessions.locate")}</button>
|
|
136
|
+
${c.webPort?`<a class="btn-link primary" href="http://${m(location.hostname)}:${c.webPort}" target="_blank" rel="noopener">${n("sessions.openTerminal")}</a>`:""}
|
|
137
|
+
${p?`<button id="resume-btn" type="button" class="primary">${n("sessions.resume")}</button>`:""}
|
|
138
|
+
${p?"":`<button id="close-btn" type="button" class="contrast">${n("sessions.close")}</button>`}
|
|
137
139
|
</div>
|
|
138
|
-
<form method="dialog"><button>${
|
|
139
|
-
</article>`,r.querySelectorAll("[data-copy]").forEach(R=>{R.onclick=()=>{navigator.clipboard.writeText(R.dataset.copy??""),R.textContent=
|
|
140
|
+
<form method="dialog"><button>${n("sessions.dismiss")}</button></form>
|
|
141
|
+
</article>`,r.querySelectorAll("[data-copy]").forEach(R=>{R.onclick=()=>{navigator.clipboard.writeText(R.dataset.copy??""),R.textContent=n("sessions.copied"),setTimeout(()=>{R.textContent=n("sessions.copy")},800)}});let h=r.querySelector("#locate-btn");h&&(h.onclick=async()=>{h.disabled=!0,h.textContent=n("sessions.locating");try{let R=await fetch(`/api/sessions/${encodeURIComponent(c.sessionId)}/locate`,{method:"POST"}),q=await R.json();if(q.ok){let B=30;h.textContent=n("sessions.cooldown",{seconds:B});let D=setInterval(()=>{B-=1,B<=0?(clearInterval(D),h.disabled=!1,h.textContent=n("sessions.locate")):h.textContent=n("sessions.cooldown",{seconds:B})},1e3)}else alert(`Locate failed: ${q.error??R.status}`),h.disabled=!1,h.textContent=n("sessions.locate")}catch(R){alert(`Locate error: ${R}`),h.disabled=!1,h.textContent=n("sessions.locate")}});let E=r.querySelector("#resume-btn");E&&(E.onclick=async()=>{E.disabled=!0;try{let R=await fetch(`/api/sessions/${encodeURIComponent(c.sessionId)}/resume`,{method:"POST"}),q=await R.json().catch(()=>({}));if(!R.ok||q.ok===!1){alert(`${n("sessions.resumeFailed")}: ${q?.error??R.status}`),E.disabled=!1;return}r.close()}catch(R){alert(`${n("sessions.resumeFailed")}: ${R}`),E.disabled=!1}});let A=r.querySelector("#close-btn");A&&(A.onclick=async()=>{if(confirm(n("sessions.closeConfirm"))){A.disabled=!0;try{await fetch(`/api/sessions/${encodeURIComponent(c.sessionId)}/close`,{method:"POST"})}finally{r.close()}}}),r.showModal()}t.addEventListener("click",c=>{let p=c.target;if(p.classList.contains("row-select")){let R=p.closest("tr[data-id]");if(!R)return;p.checked?b.add(R.dataset.id):b.delete(R.dataset.id),$(L());return}let h=p.closest("td");if(h&&h.querySelector(".row-select"))return;let E=p.closest("tr[data-id]");if(!E)return;let A=O.sessions.get(E.dataset.id);A&&M(A)}),s.addEventListener("change",()=>{let c=L().filter(p=>p.status!=="closed");for(let p of c)s.checked?b.add(p.sessionId):b.delete(p.sessionId);v()}),y.addEventListener("click",()=>{b.clear(),v()}),f.addEventListener("click",async()=>{let c=[...b];if(c.length===0||!confirm(n("sessions.closeBulkConfirm",{count:c.length})))return;f.disabled=!0,y.disabled=!0;let p=f.textContent,h=0,E=0,A=[...c];f.textContent=`0/${c.length}`;async function R(){for(;A.length;){let q=A.shift();try{let B=await fetch(`/api/sessions/${encodeURIComponent(q)}/close`,{method:"POST"}),D=await B.json().catch(()=>({}));(!B.ok||D?.ok===!1)&&(E+=1)}catch{E+=1}finally{h+=1,f.textContent=`${h}/${c.length}`}}}await Promise.all(Array.from({length:Math.min(6,c.length)},()=>R())),f.textContent=p,f.disabled=!1,y.disabled=!1,b.clear(),v(),E>0&&alert(`Failed: ${E}/${c.length}`)}),S.querySelectorAll("th[data-sort]").forEach(c=>{c.addEventListener("click",()=>{let p=c.dataset.sort;k===p?l=l==="asc"?"desc":"asc":(k=p,l=p==="spawnedAt"||p==="lastMessageAt"?"desc":"asc"),v()})}),o.addEventListener("input",v),O.on(v),v()}function Kt(){return`<section class="page">
|
|
140
142
|
<div class="page-heading">
|
|
141
143
|
<div>
|
|
142
|
-
<p class="eyebrow">${
|
|
143
|
-
<h1>${
|
|
144
|
-
<p>${
|
|
144
|
+
<p class="eyebrow">${n("nav.schedules")}</p>
|
|
145
|
+
<h1>${n("schedules.title")}</h1>
|
|
146
|
+
<p>${n("schedules.subtitle")}</p>
|
|
145
147
|
</div>
|
|
146
148
|
</div>
|
|
147
149
|
<form id="sched-filters" class="filters">
|
|
148
|
-
<input type="search" name="q" placeholder="${
|
|
150
|
+
<input type="search" name="q" placeholder="${n("schedules.search")}" />
|
|
149
151
|
<select name="kind">
|
|
150
|
-
<option value="">${
|
|
152
|
+
<option value="">${n("schedules.anyKind")}</option>
|
|
151
153
|
<option>cron</option>
|
|
152
154
|
<option>interval</option>
|
|
153
155
|
<option>once</option>
|
|
154
156
|
</select>
|
|
155
|
-
<label><input type="checkbox" name="enabled"> ${
|
|
157
|
+
<label><input type="checkbox" name="enabled"> ${n("schedules.enabledOnly")}</label>
|
|
156
158
|
</form>
|
|
157
159
|
<table>
|
|
158
160
|
<thead><tr>
|
|
159
|
-
<th>${
|
|
160
|
-
<th>${
|
|
161
|
+
<th>${n("schedules.name")}</th><th>${n("schedules.bot")}</th><th>${n("schedules.schedule")}</th><th>${n("schedules.next")}</th><th>${n("schedules.last")}</th>
|
|
162
|
+
<th>${n("schedules.repeat")}</th><th>${n("schedules.enabled")}</th><th>${n("schedules.actions")}</th>
|
|
161
163
|
</tr></thead>
|
|
162
164
|
<tbody id="schedules-tbody"></tbody>
|
|
163
165
|
</table>
|
|
164
|
-
</section>`}function
|
|
165
|
-
<td>${
|
|
166
|
-
<td>${
|
|
167
|
-
<td><code>${
|
|
168
|
-
<td>${
|
|
169
|
-
<td>${
|
|
166
|
+
</section>`}function et(e){if(!e)return"\u2014";try{return new Date(e).toLocaleString()}catch{return e}}function tt(e){e.innerHTML=Kt();let t=e.querySelector("#schedules-tbody"),o=e.querySelector("#sched-filters");function r(){let a=new FormData(o),d=(a.get("q")??"").toLowerCase(),f=a.get("kind"),y=!!a.get("enabled");return[...O.schedules.values()].filter(S=>!f||S.parsed?.kind===f).filter(S=>!y||S.enabled).filter(S=>!d||JSON.stringify(S).toLowerCase().includes(d)).sort((S,b)=>{if(S.enabled!==b.enabled)return S.enabled?-1:1;let k=S.nextRunAt?Date.parse(S.nextRunAt):1/0,l=b.nextRunAt?Date.parse(b.nextRunAt):1/0;return k-l})}function s(){t.innerHTML=r().map(a=>`<tr data-id="${m(a.id)}">
|
|
167
|
+
<td>${m(a.name??a.id)}</td>
|
|
168
|
+
<td>${m(a.botName??a.larkAppId??"-")}</td>
|
|
169
|
+
<td><code>${m(a.parsed?.display??"?")}</code></td>
|
|
170
|
+
<td>${et(a.nextRunAt)}</td>
|
|
171
|
+
<td>${et(a.lastRunAt)} ${a.lastStatus==="error"?"\u26A0\uFE0F":""}</td>
|
|
170
172
|
<td>${a.repeat?`${a.repeat.completed}/${a.repeat.times??"\u221E"}`:"\u2014"}</td>
|
|
171
173
|
<td>${a.enabled?"\u2713":"\u2717"}</td>
|
|
172
174
|
<td class="actions-cell">
|
|
173
|
-
<button data-op="run" type="button">${
|
|
174
|
-
${a.enabled?`<button data-op="pause" type="button">${
|
|
175
|
+
<button data-op="run" type="button">${n("schedules.runNow")}</button>
|
|
176
|
+
${a.enabled?`<button data-op="pause" type="button">${n("schedules.pause")}</button>`:`<button data-op="resume" type="button">${n("schedules.resume")}</button>`}
|
|
175
177
|
</td>
|
|
176
|
-
</tr>`).join("")||`<tr><td colspan="8" class="empty">${
|
|
178
|
+
</tr>`).join("")||`<tr><td colspan="8" class="empty">${n("schedules.empty")}</td></tr>`}t.addEventListener("click",async a=>{let d=a.target.closest("button[data-op]");if(!d)return;let f=d.closest("tr[data-id]");if(!f)return;let y=f.dataset.id,S=d.dataset.op;d.disabled=!0;let b=d.textContent;d.textContent="...";try{let k=await fetch(`/api/schedules/${encodeURIComponent(y)}/${S}`,{method:"POST"}),l=await k.json().catch(()=>({}));(!k.ok||l.ok===!1)&&alert(`Failed: ${k.status} ${l?.error??""}`.trim())}catch(k){alert("Network error: "+k)}finally{d.disabled=!1,d.textContent=b}}),o.addEventListener("input",s),O.on(s),s()}var N={chats:[],bots:[]};function Vt(){return`<section class="page">
|
|
177
179
|
<div class="page-heading">
|
|
178
180
|
<div>
|
|
179
|
-
<p class="eyebrow">${
|
|
180
|
-
<h1>${
|
|
181
|
-
<p>${
|
|
181
|
+
<p class="eyebrow">${n("nav.groups")}</p>
|
|
182
|
+
<h1>${n("groups.title")}</h1>
|
|
183
|
+
<p>${n("groups.subtitle")}</p>
|
|
182
184
|
</div>
|
|
183
185
|
</div>
|
|
184
186
|
<form id="g-filters" class="filters">
|
|
185
|
-
<input type="search" name="q" placeholder="${
|
|
186
|
-
<label><input type="checkbox" name="missing"> ${
|
|
187
|
-
<button type="button" id="g-refresh">${
|
|
188
|
-
<button type="button" id="g-create" class="primary">${
|
|
187
|
+
<input type="search" name="q" placeholder="${n("groups.search")}" />
|
|
188
|
+
<label><input type="checkbox" name="missing"> ${n("groups.missingOnly")}</label>
|
|
189
|
+
<button type="button" id="g-refresh">${n("groups.refresh")}</button>
|
|
190
|
+
<button type="button" id="g-create" class="primary">${n("groups.create")}</button>
|
|
189
191
|
</form>
|
|
190
192
|
<table>
|
|
191
193
|
<thead id="g-head"></thead>
|
|
192
194
|
<tbody id="g-body"></tbody>
|
|
193
195
|
</table>
|
|
194
196
|
<dialog id="g-drawer"></dialog>
|
|
195
|
-
</section>`}async function
|
|
197
|
+
</section>`}async function te(){N=await(await fetch("/api/groups")).json()}async function Yt(){return(await fetch("/api/groups")).json()}function Qt(e,t){if(t.size===0)return!0;let o=e?.memberBots??[];for(let r of t)if(!o.some(s=>s.larkAppId===r&&s.inChat))return!1;return!0}function nt(e,t){return e.filter(o=>!t||!t.has(o.larkAppId)).map(o=>`
|
|
196
198
|
<label class="checkbox-row">
|
|
197
|
-
<input type="checkbox" name="bot" value="${
|
|
198
|
-
${
|
|
199
|
+
<input type="checkbox" name="bot" value="${m(o.larkAppId)}">
|
|
200
|
+
${m(o.botName??o.larkAppId)} <small>(${m(o.larkAppId)})</small>
|
|
199
201
|
</label>
|
|
200
|
-
`).join("")}async function
|
|
202
|
+
`).join("")}async function ot(e){e.innerHTML=Vt();let t=e.querySelector("#g-head"),o=e.querySelector("#g-body"),r=e.querySelector("#g-filters"),s=e.querySelector("#g-refresh"),a=e.querySelector("#g-drawer");s.onclick=async()=>{s.disabled=!0;try{await te(),b()}finally{s.disabled=!1}};let d=e.querySelector("#g-create");d.onclick=()=>f(),await te();function f(){let l=N.bots;if(l.length===0){alert(n("groups.noBotsOnline"));return}a.innerHTML=`
|
|
201
203
|
<article>
|
|
202
|
-
<header><h3>${
|
|
203
|
-
<p>${
|
|
204
|
+
<header><h3>${n("groups.createTitle")}</h3></header>
|
|
205
|
+
<p>${n("groups.createHelp")}</p>
|
|
204
206
|
<form id="g-createform">
|
|
205
207
|
<label class="form-row">
|
|
206
|
-
<span>${
|
|
207
|
-
<input type="text" name="name" placeholder="${
|
|
208
|
+
<span>${n("groups.name")}</span>
|
|
209
|
+
<input type="text" name="name" placeholder="${n("groups.namePlaceholder")}" maxlength="60">
|
|
208
210
|
</label>
|
|
209
211
|
<label class="form-row">
|
|
210
|
-
<span>${
|
|
212
|
+
<span>${n("groups.bindDir")}</span>
|
|
211
213
|
<input type="text" name="bindWorkingDir" placeholder="e.g. ~/projects/botmux">
|
|
212
|
-
<small>${
|
|
214
|
+
<small>${n("groups.bindDirHelp")}</small>
|
|
213
215
|
</label>
|
|
214
216
|
<fieldset>
|
|
215
|
-
<legend>${
|
|
216
|
-
${
|
|
217
|
+
<legend>${n("groups.botPicker")}</legend>
|
|
218
|
+
${nt(l)}
|
|
217
219
|
</fieldset>
|
|
218
220
|
<div class="actions">
|
|
219
|
-
<button type="submit" class="primary">${
|
|
220
|
-
<button type="button" id="g-create-cancel">${
|
|
221
|
+
<button type="submit" class="primary">${n("groups.createSubmit")}</button>
|
|
222
|
+
<button type="button" id="g-create-cancel">${n("groups.cancel")}</button>
|
|
221
223
|
</div>
|
|
222
224
|
</form>
|
|
223
|
-
</article>`,a.showModal(),a.querySelector("#g-create-cancel").onclick=()=>a.close(),a.querySelector("#g-createform").onsubmit=async g=>{g.preventDefault();let u=new FormData(g.target)
|
|
225
|
+
</article>`,a.showModal(),a.querySelector("#g-create-cancel").onclick=()=>a.close(),a.querySelector("#g-createform").onsubmit=async g=>{g.preventDefault();let u=new FormData(g.target),I=(u.get("name")??"").trim(),$=(u.get("bindWorkingDir")??"").trim(),v=u.getAll("bot");if(v.length===0){alert("Pick at least one bot.");return}let M=g.target.querySelector("button[type=submit]");M&&(M.disabled=!0,M.textContent="Creating...");try{let c=await fetch("/api/groups/create",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({name:I||void 0,larkAppIds:v,bindWorkingDir:$||void 0})}),p=await c.json();if(p.ok&&p.chatId){y(p);let h=Array.isArray(p.invalidBotIds)?p.invalidBotIds:[],E=v.filter(R=>!h.includes(R)),A=new Set(E);typeof p.creator=="string"&&p.creator&&A.add(p.creator),T(p.chatId,I||p.chatId,E,p.creator),b(),L(p.chatId,A).catch(()=>{})}else alert(`Failed: ${p.error??c.status}`),a.close()}catch(c){alert("Network error: "+c),a.close()}};function T(g,u,I,$){let v=new Set(I);$&&v.add($);let M=N.bots.map(p=>({larkAppId:p.larkAppId,botName:p.botName,inChat:v.has(p.larkAppId),oncallChat:null})),c={chatId:g,name:u,ownerId:$??null,memberBots:M};N.chats=[c,...N.chats.filter(p=>p.chatId!==g)]}async function L(g,u){let I=[600,1200,1200,1200,1200,1200];for(let $ of I){await new Promise(c=>setTimeout(c,$));let v;try{v=await Yt()}catch{continue}let M=(v.chats??[]).find(c=>c.chatId===g);if(M&&Qt(M,u)){N=v,b();return}}}}function y(l){let T=String(l.chatId),L=`https://applink.feishu.cn/client/chat/open?openChatId=${encodeURIComponent(T)}`,g=l.invalidBotIds??[],u=l.invalidUserIds??[],I=l.autoInvitedOpenId,$=!!l.autoInviteRejected,v=l.ownerTransferredTo,M=l.transferError,c=l.notifyMessageId,p=l.notifyError,h=Array.isArray(l.oncallBindings)?l.oncallBindings:[],E=h.filter(D=>D?.ok).length,A=h.filter(D=>!D?.ok),R=h.length>0?A.length===0?`<p class="hint-ok">\u5DF2\u7ED1\u5B9A\u76EE\u5F55\uFF1A<code>${m(l.bindResolvedPath??"")}</code>\uFF08${E}/${h.length} bots\uFF09</p>`:`<p class="hint-warn">\u76EE\u5F55\u7ED1\u5B9A\u90E8\u5206\u5931\u8D25\uFF1A\u6210\u529F ${E}/${h.length}\u3002${A.map(D=>`<br><code>${m(D.larkAppId??"?")}</code>: ${m(D.error??"unknown")}`).join("")}</p>`:"",q;if(I){let D=v?"<br><small>\u7FA4\u4E3B\u5DF2\u4ECE\u673A\u5668\u4EBA\u8F6C\u8BA9\u7ED9\u4F60\u3002</small>":M?`<br><small class="hint-warn-inline">\u26A0 \u81EA\u52A8\u8F6C\u8BA9\u7FA4\u4E3B\u5931\u8D25\uFF08${m(M)}\uFF09\uFF0C\u4F60\u73B0\u5728\u662F\u6210\u5458\u4F46\u7FA4\u4E3B\u4ECD\u662F\u673A\u5668\u4EBA\u3002</small>`:"",Te=c?`<br><small>\u673A\u5668\u4EBA\u5DF2\u5728\u7FA4\u91CC @ \u4E86\u4F60\uFF08\u6D88\u606F id <code>${m(c)}</code>\uFF09\uFF0C\u770B\u98DE\u4E66\u901A\u77E5\u5C31\u80FD\u8FDB\u7FA4\u3002</small>`:p?`<br><small class="hint-warn-inline">\u26A0 \u81EA\u52A8 @ \u901A\u77E5\u5931\u8D25\uFF08${m(p)}\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>`:"";q=`<p class="hint-ok">\u5DF2\u81EA\u52A8\u9080\u8BF7\u4F60\uFF08<code>${m(I)}</code>\uFF09\u4F5C\u4E3A\u6210\u5458\u3002${D}${Te}</p>`}else $?q='<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>':q='<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 B=[g.length?`<li>\u65E0\u6548 bot id: <code>${g.map(m).join(", ")}</code></li>`:"",u.length?`<li>\u65E0\u6548\u7528\u6237 open_id: <code>${u.map(m).join(", ")}</code></li>`:""].filter(Boolean).join("");a.innerHTML=`
|
|
224
226
|
<article>
|
|
225
|
-
<header><h3>${
|
|
226
|
-
<p><b>chatId:</b> <code>${
|
|
227
|
-
<p><b>\u521B\u5EFA\u8005:</b> <code>${
|
|
227
|
+
<header><h3>${n("groups.successTitle")}</h3></header>
|
|
228
|
+
<p><b>chatId:</b> <code>${m(T)}</code> <button type="button" data-copy="${m(T)}">${n("sessions.copy")}</button></p>
|
|
229
|
+
<p><b>\u521B\u5EFA\u8005:</b> <code>${m(l.creator??"?")}</code></p>
|
|
228
230
|
${q}
|
|
229
231
|
${R}
|
|
230
|
-
${
|
|
232
|
+
${B?`<ul>${B}</ul>`:""}
|
|
231
233
|
<div class="actions">
|
|
232
|
-
<a class="btn-link primary" href="${L}" target="_blank" rel="noopener">${
|
|
233
|
-
<button type="button" id="g-create-close">${
|
|
234
|
+
<a class="btn-link primary" href="${L}" target="_blank" rel="noopener">${n("groups.openGroup")}</a>
|
|
235
|
+
<button type="button" id="g-create-close">${n("sessions.dismiss")}</button>
|
|
234
236
|
</div>
|
|
235
|
-
</article>`,a.querySelectorAll("[data-copy]").forEach(
|
|
236
|
-
<th>${
|
|
237
|
-
${
|
|
238
|
-
<th>${
|
|
239
|
-
</tr>`}function
|
|
237
|
+
</article>`,a.querySelectorAll("[data-copy]").forEach(D=>{D.onclick=()=>{navigator.clipboard.writeText(D.dataset.copy??""),D.textContent=n("sessions.copied"),setTimeout(()=>{D.textContent=n("sessions.copy")},800)}}),a.querySelector("#g-create-close").onclick=()=>a.close()}function S(){t.innerHTML=`<tr>
|
|
238
|
+
<th>${n("groups.chat")}</th>
|
|
239
|
+
${N.bots.map(l=>`<th>${m(l.botName??l.larkAppId)}</th>`).join("")}
|
|
240
|
+
<th>${n("groups.actions")}</th>
|
|
241
|
+
</tr>`}function b(){S();let l=new FormData(r),T=(l.get("q")??"").toLowerCase(),L=!!l.get("missing"),g=N.chats.filter(u=>!T||(u.name??"").toLowerCase().includes(T)||u.chatId.toLowerCase().includes(T)||(u.ownerId??"").toLowerCase().includes(T)).filter(u=>!L||u.memberBots.some(I=>!I.inChat));if(g.length===0){o.innerHTML=`<tr><td colspan="${N.bots.length+2}" class="empty">${n("groups.empty")}</td></tr>`;return}o.innerHTML=g.map(u=>`<tr data-chat="${m(u.chatId)}">
|
|
240
242
|
<td>
|
|
241
|
-
<strong>${
|
|
242
|
-
<small><code>${
|
|
243
|
+
<strong>${m(u.name??u.chatId)}</strong><br>
|
|
244
|
+
<small><code>${m(u.chatId)}</code></small>
|
|
243
245
|
</td>
|
|
244
|
-
${
|
|
246
|
+
${N.bots.map(I=>{let $=u.memberBots.find(c=>c.larkAppId===I.larkAppId),v=$?$.error?"!":$.inChat?"\u2713":"\u2717":"?";return`<td class="${$?$.error?"cell-error":$.inChat?"cell-in":"cell-out":"cell-unknown"}" title="${m($?.error??"")}">${v}</td>`}).join("")}
|
|
245
247
|
<td>
|
|
246
|
-
<button class="add-bots" type="button">${
|
|
247
|
-
<button class="manage-chat" type="button">${
|
|
248
|
+
<button class="add-bots" type="button">${n("groups.addBots")}</button>
|
|
249
|
+
<button class="manage-chat" type="button">${n("groups.manage")}</button>
|
|
248
250
|
</td>
|
|
249
|
-
</tr>`).join("")}
|
|
251
|
+
</tr>`).join("")}b(),o.addEventListener("click",async l=>{let T=l.target.closest("button.add-bots");if(!T)return;let g=T.closest("tr[data-chat]").dataset.chat,u=N.chats.find(v=>v.chatId===g);if(!u)return;let I=new Set(u.memberBots.filter(v=>v.inChat).map(v=>v.larkAppId));if(!N.bots.filter(v=>!I.has(v.larkAppId)).length){alert("All configured bots are already in this chat.");return}a.innerHTML=`
|
|
250
252
|
<article>
|
|
251
|
-
<header><h3>${
|
|
252
|
-
<p>${
|
|
253
|
+
<header><h3>${n("groups.addBots")} \xB7 ${m(u.name??u.chatId)}</h3></header>
|
|
254
|
+
<p>${n("groups.createHelp")}</p>
|
|
253
255
|
<form id="g-addform">
|
|
254
|
-
${
|
|
256
|
+
${nt(N.bots,I)}
|
|
255
257
|
<div class="actions">
|
|
256
|
-
<button type="submit" class="primary">${
|
|
257
|
-
<button type="button" id="g-cancel">${
|
|
258
|
+
<button type="submit" class="primary">${n("groups.addBots")}</button>
|
|
259
|
+
<button type="button" id="g-cancel">${n("groups.cancel")}</button>
|
|
258
260
|
</div>
|
|
259
261
|
</form>
|
|
260
|
-
</article>`,a.showModal(),a.querySelector("#g-cancel").onclick=()=>a.close(),a.querySelector("#g-addform").onsubmit=async
|
|
261
|
-
`);alert(E),await
|
|
262
|
+
</article>`,a.showModal(),a.querySelector("#g-cancel").onclick=()=>a.close(),a.querySelector("#g-addform").onsubmit=async v=>{v.preventDefault();let c=new FormData(v.target).getAll("bot");if(c.length===0){alert("Pick at least one bot.");return}try{let h=await(await fetch(`/api/groups/${encodeURIComponent(g)}/add-bots`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({larkAppIds:c})})).json();if(h.error==="no_proxy_bot")alert("No bot is currently in this chat \u2014 add one manually in Feishu first, then retry.");else if(h.result){let E=h.result.map(A=>`${A.id}: ${A.ok?"OK":`failed (${A.error??"unknown"})`}`).join(`
|
|
263
|
+
`);alert(E),await te(),b()}else alert(`Unexpected response: ${JSON.stringify(h)}`)}catch(p){alert("Network error: "+p)}finally{a.close()}}}),o.addEventListener("click",async l=>{let T=l.target.closest("button.manage-chat");if(!T)return;let g=T.closest("tr[data-chat]").dataset.chat,u=N.chats.find(I=>I.chatId===g);u&&k(u)});function k(l){let T=l.memberBots.filter(g=>g.inChat),L=typeof l.ownerId=="string"?l.ownerId:"";a.innerHTML=`
|
|
262
264
|
<article>
|
|
263
|
-
<header><h3>${
|
|
264
|
-
<p><b>chatId:</b> <code>${
|
|
265
|
-
<p><b>${
|
|
265
|
+
<header><h3>${n("groups.manageTitle",{name:l.name??l.chatId})}</h3></header>
|
|
266
|
+
<p><b>chatId:</b> <code>${m(l.chatId)}</code></p>
|
|
267
|
+
<p><b>${n("groups.owner")}:</b> <code>${m(l.ownerId??n("common.unknown"))}</code></p>
|
|
266
268
|
|
|
267
269
|
<fieldset>
|
|
268
|
-
<legend>${
|
|
269
|
-
<p><small>${
|
|
270
|
-
${
|
|
271
|
-
<div class="oncall-row" data-bot="${
|
|
270
|
+
<legend>${n("groups.oncall")}</legend>
|
|
271
|
+
<p><small>${n("groups.oncallHelp")}</small></p>
|
|
272
|
+
${T.length===0?'<p class="empty">\u6CA1\u6709\u673A\u5668\u4EBA\u5728\u7FA4\u91CC</p>':T.map(g=>{let u=!!g.oncallChat,I=g.oncallChat?.workingDir??"";return`
|
|
273
|
+
<div class="oncall-row" data-bot="${m(g.larkAppId)}">
|
|
272
274
|
<label class="checkbox-row">
|
|
273
275
|
<input type="checkbox" data-action="toggle" ${u?"checked":""}>
|
|
274
|
-
<strong>${
|
|
275
|
-
<small>(${
|
|
276
|
+
<strong>${m(g.botName??g.larkAppId)}</strong>
|
|
277
|
+
<small>(${m(g.larkAppId)})</small>
|
|
276
278
|
</label>
|
|
277
279
|
<div class="oncall-row-body">
|
|
278
280
|
<input type="text" data-input="workingDir" placeholder="e.g. /root/iserver/botmux"
|
|
279
|
-
value="${
|
|
280
|
-
<button type="button" data-action="save">${
|
|
281
|
+
value="${m(I)}" ${u?"":"disabled"}>
|
|
282
|
+
<button type="button" data-action="save">${n("groups.save")}</button>
|
|
281
283
|
<span class="oncall-status" data-status></span>
|
|
282
284
|
</div>
|
|
283
285
|
</div>
|
|
@@ -285,112 +287,179 @@ effects={effects}, activities={activities}, waits={waits}, cancels={cancels}`,"w
|
|
|
285
287
|
</fieldset>
|
|
286
288
|
|
|
287
289
|
<fieldset>
|
|
288
|
-
<legend>${
|
|
289
|
-
${
|
|
290
|
+
<legend>${n("groups.leaveTitle")}</legend>
|
|
291
|
+
${T.length===0?'<p class="empty">\u6CA1\u6709\u673A\u5668\u4EBA\u5728\u7FA4\u91CC</p>':T.map(g=>`
|
|
290
292
|
<label class="checkbox-row">
|
|
291
|
-
<input type="checkbox" name="leave-bot" value="${
|
|
292
|
-
${
|
|
293
|
+
<input type="checkbox" name="leave-bot" value="${m(g.larkAppId)}">
|
|
294
|
+
${m(g.botName??g.larkAppId)}
|
|
293
295
|
<small>${g.larkAppId===L?"\xB7 \u7FA4\u4E3B":""}</small>
|
|
294
296
|
</label>
|
|
295
297
|
`).join("")}
|
|
296
298
|
</fieldset>
|
|
297
299
|
|
|
298
300
|
<div class="actions">
|
|
299
|
-
<button id="g-leave-btn" type="button" ${
|
|
300
|
-
<button id="g-disband-btn" type="button" class="contrast" ${
|
|
301
|
+
<button id="g-leave-btn" type="button" ${T.length===0?"disabled":""}>${n("groups.leaveSelected")}</button>
|
|
302
|
+
<button id="g-disband-btn" type="button" class="contrast" ${T.length===0?"disabled":""}>${n("groups.disband")}</button>
|
|
301
303
|
</div>
|
|
302
|
-
<p class="hint-warn"><small>${
|
|
303
|
-
<form method="dialog"><button>${
|
|
304
|
-
</article>`,a.showModal(),a.querySelectorAll(".oncall-row").forEach(g=>{let u=g.dataset.bot
|
|
305
|
-
`);alert(
|
|
306
|
-
\u5173\u95ED\u4E86 ${
|
|
307
|
-
\u5173\u95ED\u4E86 ${
|
|
304
|
+
<p class="hint-warn"><small>${n("groups.dangerHint")}</small></p>
|
|
305
|
+
<form method="dialog"><button>${n("sessions.dismiss")}</button></form>
|
|
306
|
+
</article>`,a.showModal(),a.querySelectorAll(".oncall-row").forEach(g=>{let u=g.dataset.bot,I=g.querySelector("input[data-action=toggle]"),$=g.querySelector("input[data-input=workingDir]"),v=g.querySelector("button[data-action=save]"),M=g.querySelector("[data-status]");I.addEventListener("change",()=>{$.disabled=!I.checked,I.checked&&$.focus()}),v.addEventListener("click",async()=>{M.textContent="",M.className="oncall-status";let c=I.checked,p=$.value.trim();if(c&&!p){M.textContent=n("groups.needWorkingDir"),M.classList.add("hint-warn-inline");return}v.disabled=!0;try{let h=`/api/groups/${encodeURIComponent(l.chatId)}/oncall/${encodeURIComponent(u)}`,E=c?await fetch(h,{method:"PUT",headers:{"content-type":"application/json"},body:JSON.stringify({workingDir:p})}):await fetch(h,{method:"DELETE"}),A=await E.json().catch(()=>({}));if(E.ok&&A.ok){M.textContent=c?`\u2713 \u5DF2\u7ED1\u5B9A \u2192 ${A.resolvedPath??p}`:"\u2713 \u5DF2\u89E3\u7ED1",M.classList.add("hint-ok");try{await te(),b()}catch{}}else M.textContent=`\u2717 ${A.error??E.status}`,M.classList.add("hint-warn-inline")}catch(h){M.textContent=`\u2717 ${h?.message??h}`,M.classList.add("hint-warn-inline")}finally{v.disabled=!1}})}),a.querySelector("#g-leave-btn").onclick=async()=>{let g=[...a.querySelectorAll("input[name=leave-bot]:checked")].map(u=>u.value);if(g.length===0){alert("\u81F3\u5C11\u9009\u4E00\u4E2A\u673A\u5668\u4EBA");return}if(confirm(`\u786E\u5B9A\u8BA9 ${g.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 I=await(await fetch(`/api/groups/${encodeURIComponent(l.chatId)}/leave`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({larkAppIds:g})})).json(),$=(I.result??[]).map(v=>{if(!v.ok)return`${v.larkAppId}: \u5931\u8D25 (${v.error??"unknown"})`;let M=v.closedSessions??[],c=M.filter(E=>!E.ok).length,p=M.length-c,h=M.length===0?"":c===0?`\uFF08\u5173\u95ED ${p} \u4E2A\u4F1A\u8BDD\uFF09`:`\uFF08\u5173\u95ED ${p} \u4E2A\uFF0C${c} \u4E2A\u5931\u8D25\uFF09`;return`${v.larkAppId}: OK${h}`}).join(`
|
|
307
|
+
`);alert($||`Unexpected: ${JSON.stringify(I)}`),await te(),b()}catch(u){alert("Network error: "+u)}finally{a.close()}},a.querySelector("#g-disband-btn").onclick=async()=>{if(T.length===0||!confirm(`\u786E\u5B9A\u89E3\u6563\u7FA4\u804A\u300C${l.name??l.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 g=[...T].sort((I,$)=>($.larkAppId===L?1:0)-(I.larkAppId===L?1:0)),u=[];for(let I of g)try{let $=await fetch(`/api/groups/${encodeURIComponent(l.chatId)}/disband`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({larkAppId:I.larkAppId})}),v=await $.json();if(v.ok){let M=v.closedSessions??[],c=M.filter(E=>!E.ok).length,p=M.length-c,h=M.length===0?"":c===0?`
|
|
308
|
+
\u5173\u95ED\u4E86 ${p} \u4E2A\u4F1A\u8BDD\u3002`:`
|
|
309
|
+
\u5173\u95ED\u4E86 ${p} \u4E2A\u4F1A\u8BDD\uFF0C${c} \u4E2A\u4F1A\u8BDD\u5173\u95ED\u5931\u8D25\u3002`;alert(`\u5DF2\u89E3\u6563\uFF08\u7531 ${I.botName??I.larkAppId} \u6267\u884C\uFF09${h}`),await te(),b(),a.close();return}u.push(`${I.botName??I.larkAppId}: ${v.error??$.status}`)}catch($){u.push(`${I.botName??I.larkAppId}: ${$}`)}alert(`\u6240\u6709\u5728\u7FA4\u673A\u5668\u4EBA\u5747\u65E0\u6CD5\u89E3\u6563\uFF1A
|
|
308
310
|
${u.join(`
|
|
309
311
|
`)}
|
|
310
312
|
|
|
311
|
-
\u5EFA\u8BAE\u6539\u7528\u300C\u9000\u51FA\u7FA4\u804A\u300D\u3002`)}}r.addEventListener("input",
|
|
313
|
+
\u5EFA\u8BAE\u6539\u7528\u300C\u9000\u51FA\u7FA4\u804A\u300D\u3002`)}}r.addEventListener("input",b)}var ne={bots:[]},oe=null;function Xt(){return`<section class="page">
|
|
312
314
|
<div class="page-heading">
|
|
313
315
|
<div>
|
|
314
|
-
<p class="eyebrow">${
|
|
315
|
-
<h1>${
|
|
316
|
-
<p>${
|
|
316
|
+
<p class="eyebrow">${n("nav.botDefaults")}</p>
|
|
317
|
+
<h1>${n("botDefaults.title")}</h1>
|
|
318
|
+
<p>${n("botDefaults.subtitle")}</p>
|
|
317
319
|
</div>
|
|
318
320
|
</div>
|
|
319
321
|
<form id="bd-filters" class="filters">
|
|
320
|
-
<input type="search" name="q" placeholder="${
|
|
321
|
-
<button type="button" id="bd-refresh">${
|
|
322
|
+
<input type="search" name="q" placeholder="${n("botDefaults.search")}" />
|
|
323
|
+
<button type="button" id="bd-refresh">${n("botDefaults.refresh")}</button>
|
|
322
324
|
</form>
|
|
323
325
|
<p class="hint-warn" style="max-width:760px">
|
|
324
|
-
${
|
|
326
|
+
${n("botDefaults.warning")}
|
|
325
327
|
</p>
|
|
326
328
|
<div id="bd-list"></div>
|
|
327
|
-
</section>`}async function
|
|
328
|
-
<header><strong>${
|
|
329
|
-
<small>${
|
|
330
|
-
<p class="hint-warn-inline">\u67E5\u8BE2\u5931\u8D25\uFF1A${
|
|
331
|
-
</article>`;let y=
|
|
329
|
+
</section>`}async function st(){try{let e=await fetch("/api/bots"),t=await e.json().catch(()=>({}));if(!e.ok){oe=t?.error?`HTTP ${e.status}: ${t.error}${t.path?` (${t.path})`:""}`:`HTTP ${e.status}`,ne={bots:[]};return}if(!t||!Array.isArray(t.bots)){oe="unexpected response shape (no `bots` array)",ne={bots:[]};return}oe=null,ne=t}catch(e){oe=e?.message??String(e),ne={bots:[]}}}function at(e){if(!e)return"\u2014";let t=new Date(e);return Number.isNaN(t.getTime())?"\u2014":t.toLocaleString()}async function rt(e){e.innerHTML=Xt();let t=e.querySelector("#bd-list"),o=e.querySelector("#bd-filters"),r=e.querySelector("#bd-refresh");r.onclick=async()=>{r.disabled=!0;try{await st(),s()}finally{r.disabled=!1}},await st();function s(){let y=(new FormData(o).get("q")??"").toLowerCase(),S=ne.bots.filter(b=>!y||(b.botName??"").toLowerCase().includes(y)||(b.larkAppId??"").toLowerCase().includes(y));if(oe){t.innerHTML=`<p class="hint-warn">\u65E0\u6CD5\u52A0\u8F7D bot \u5217\u8868\uFF1A${m(oe)}<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(S.length===0){t.innerHTML=`<p class="empty">${n("botDefaults.empty")}</p>`;return}t.innerHTML=S.map(a).join(""),d()}function a(f){if(f.error)return`<article class="bd-card" data-appid="${m(f.larkAppId)}">
|
|
330
|
+
<header><strong>${m(f.botName??f.larkAppId)}</strong>
|
|
331
|
+
<small>${m(f.larkAppId)}</small></header>
|
|
332
|
+
<p class="hint-warn-inline">\u67E5\u8BE2\u5931\u8D25\uFF1A${m(f.error)}</p>
|
|
333
|
+
</article>`;let y=f.defaultOncall??{enabled:!1,workingDir:"",since:0},S=!!y.enabled;return`<article class="bd-card" data-appid="${m(f.larkAppId)}">
|
|
332
334
|
<header>
|
|
333
|
-
<strong>${
|
|
334
|
-
<small>${
|
|
335
|
+
<strong>${m(f.botName??f.larkAppId)}</strong>
|
|
336
|
+
<small>${m(f.larkAppId)}</small>
|
|
335
337
|
</header>
|
|
336
338
|
<div class="bd-body">
|
|
337
339
|
<label class="checkbox-row">
|
|
338
340
|
<input type="checkbox" data-action="toggle" ${S?"checked":""}>
|
|
339
|
-
<strong>${
|
|
340
|
-
<small>${
|
|
341
|
+
<strong>${n("botDefaults.defaultOncall")}</strong>
|
|
342
|
+
<small>${n("botDefaults.defaultOncallHelp")}</small>
|
|
341
343
|
</label>
|
|
342
344
|
<div class="bd-row">
|
|
343
345
|
<label>
|
|
344
|
-
<span>${
|
|
346
|
+
<span>${n("botDefaults.workingDir")}</span>
|
|
345
347
|
<input type="text" data-input="workingDir" placeholder="e.g. /root/iserver/botmux"
|
|
346
|
-
value="${
|
|
348
|
+
value="${m(y.workingDir??"")}" ${S?"":"disabled"}>
|
|
347
349
|
</label>
|
|
348
350
|
</div>
|
|
349
351
|
<div class="bd-meta">
|
|
350
|
-
<small>${
|
|
351
|
-
<small>${
|
|
352
|
+
<small>${n("botDefaults.lastEnabled")}: ${m(at(y.since??0))}</small>
|
|
353
|
+
<small>${n("botDefaults.autobound",{count:f.autoboundChatCount??0})}</small>
|
|
352
354
|
</div>
|
|
353
355
|
<div class="actions">
|
|
354
|
-
<button type="button" data-action="save">${
|
|
356
|
+
<button type="button" data-action="save">${n("botDefaults.save")}</button>
|
|
355
357
|
<span class="oncall-status" data-status></span>
|
|
356
358
|
</div>
|
|
357
359
|
</div>
|
|
358
|
-
</article>`}function
|
|
360
|
+
</article>`}function d(){t.querySelectorAll(".bd-card").forEach(f=>{let y=f.dataset.appid,S=f.querySelector("input[data-action=toggle]"),b=f.querySelector("input[data-input=workingDir]"),k=f.querySelector("button[data-action=save]"),l=f.querySelector("[data-status]");!S||!b||!k||!l||(S.addEventListener("change",()=>{b.disabled=!S.checked,S.checked&&b.focus()}),k.addEventListener("click",async()=>{l.textContent="",l.className="oncall-status";let T=S.checked,L=b.value.trim();if(T&&!L){l.textContent=n("botDefaults.required"),l.classList.add("hint-warn-inline");return}k.disabled=!0;try{let g=await fetch(`/api/bots/${encodeURIComponent(y)}/default-oncall`,{method:"PUT",headers:{"content-type":"application/json"},body:JSON.stringify({enabled:T,workingDir:L})}),u=await g.json().catch(()=>({}));if(g.ok&&u.ok){let I=u.resolvedPath?` \u2192 ${u.resolvedPath}`:"";l.textContent=T?`\u2713 \u5DF2\u5F00\u542F${I}\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",l.classList.add("hint-ok");let $=ne.bots.find(M=>M.larkAppId===y);$&&u.defaultOncall&&($.defaultOncall=u.defaultOncall);let v=f.querySelector(".bd-meta small:first-child");v&&u.defaultOncall?.since!=null&&(v.textContent=`\u4E0A\u6B21\u542F\u7528\u65F6\u95F4\uFF1A${at(u.defaultOncall.since)}`)}else l.textContent=`\u2717 ${u.error??g.status}`,l.classList.add("hint-warn-inline")}catch(g){l.textContent=`\u2717 ${g?.message??g}`,l.classList.add("hint-warn-inline")}finally{k.disabled=!1}}))})}s(),o.addEventListener("input",s)}var xe=4096,ve=[],W=null,_=null,j="",se=new Set;function Zt(){return`<section class="page roles-page">
|
|
361
|
+
<div class="page-heading">
|
|
362
|
+
<div>
|
|
363
|
+
<p class="eyebrow">${n("nav.roles")}</p>
|
|
364
|
+
<h1>${n("roles.title")}</h1>
|
|
365
|
+
<p>${n("roles.subtitle")}</p>
|
|
366
|
+
</div>
|
|
367
|
+
</div>
|
|
368
|
+
<div class="roles-layout">
|
|
369
|
+
<div class="roles-tree-panel">
|
|
370
|
+
<div class="roles-tree-header">
|
|
371
|
+
<input type="search" id="roles-search" placeholder="${n("roles.search")}" />
|
|
372
|
+
<button type="button" id="roles-refresh">${n("roles.refresh")}</button>
|
|
373
|
+
</div>
|
|
374
|
+
<div id="roles-tree" class="roles-tree"></div>
|
|
375
|
+
</div>
|
|
376
|
+
<div class="roles-editor-panel">
|
|
377
|
+
<div id="roles-editor-empty" class="roles-editor-empty">${n("roles.selectHint")}</div>
|
|
378
|
+
<div id="roles-editor-form" class="roles-editor-form" style="display:none">
|
|
379
|
+
<div class="roles-editor-breadcrumb">
|
|
380
|
+
<span id="roles-editor-group-name"></span>
|
|
381
|
+
<span class="roles-breadcrumb-sep">\u203A</span>
|
|
382
|
+
<span id="roles-editor-bot-name"></span>
|
|
383
|
+
</div>
|
|
384
|
+
<div class="roles-editor-meta">
|
|
385
|
+
<span id="roles-editor-chat-id" class="roles-editor-meta-line"></span>
|
|
386
|
+
</div>
|
|
387
|
+
<textarea id="roles-editor-textarea" placeholder="${n("roles.editorPlaceholder")}" rows="14"></textarea>
|
|
388
|
+
<div class="roles-editor-footer">
|
|
389
|
+
<span id="roles-editor-bytecount" class="roles-bytecount"></span>
|
|
390
|
+
<div class="roles-editor-actions">
|
|
391
|
+
<button type="button" id="roles-delete" class="danger">${n("roles.delete")}</button>
|
|
392
|
+
<button type="button" id="roles-save" class="primary">${n("roles.save")}</button>
|
|
393
|
+
</div>
|
|
394
|
+
</div>
|
|
395
|
+
<div id="roles-preview" class="roles-preview"></div>
|
|
396
|
+
</div>
|
|
397
|
+
</div>
|
|
398
|
+
</div>
|
|
399
|
+
</section>`}async function be(){ve=((await(await fetch("/api/groups")).json()).chats??[]).map(o=>({chatId:o.chatId,name:o.name??o.chatId,memberBots:(o.memberBots??[]).map(r=>({larkAppId:r.larkAppId,botName:r.botName??r.larkAppId,inChat:r.inChat??!1,hasRole:r.hasRole??!1,oncallChat:r.oncallChat??null}))}))}async function lt(e,t){return(await fetch(`/api/roles/${encodeURIComponent(e)}/${encodeURIComponent(t)}`)).json()}async function en(e,t,o){return(await fetch(`/api/roles/${encodeURIComponent(e)}/${encodeURIComponent(t)}`,{method:"PUT",headers:{"content-type":"application/json"},body:JSON.stringify({content:o})})).ok}async function tn(e,t){return(await fetch(`/api/roles/${encodeURIComponent(e)}/${encodeURIComponent(t)}`,{method:"DELETE"})).ok}function dt(e){return e.memberBots.filter(t=>t.inChat&&t.hasRole).length}function nn(e){return e.memberBots.filter(t=>t.inChat).length}function Z(e=""){let t=document.getElementById("roles-tree");if(!t)return;let o=e.toLowerCase(),r=ve.filter(s=>{if(!o)return!0;let a=s.chatId.toLowerCase().includes(o)||(s.name??"").toLowerCase().includes(o),d=s.memberBots.some(f=>f.larkAppId.toLowerCase().includes(o)||(f.botName??"").toLowerCase().includes(o));return a||d});if(r.length===0){t.innerHTML=`<div class="roles-empty">${n("roles.noChats")}</div>`;return}t.innerHTML=r.map(s=>{let a=se.has(s.chatId),d=s.memberBots.filter(k=>k.inChat),f=a?"\u25BE":"\u25B8",y=dt(s),S=nn(s),b=a?d.map(k=>`
|
|
400
|
+
<div class="roles-bot-row ${W===s.chatId&&_===k.larkAppId?"selected":""}"
|
|
401
|
+
data-group-id="${m(s.chatId)}"
|
|
402
|
+
data-bot-id="${m(k.larkAppId)}">
|
|
403
|
+
<span class="roles-bot-indent"></span>
|
|
404
|
+
<span class="roles-bot-icon">\u{1F916}</span>
|
|
405
|
+
<div class="roles-bot-info">
|
|
406
|
+
<div class="roles-bot-name">${m(k.botName)}</div>
|
|
407
|
+
<div class="roles-bot-id">${m(k.larkAppId)}</div>
|
|
408
|
+
</div>
|
|
409
|
+
<span class="roles-badge ${k.hasRole?"has-role":"no-role"}">
|
|
410
|
+
${k.hasRole?n("roles.configured"):n("roles.unconfigured")}
|
|
411
|
+
</span>
|
|
412
|
+
</div>`).join(""):"";return`
|
|
413
|
+
<div class="roles-group-section">
|
|
414
|
+
<div class="roles-group-row ${a?"expanded":""} ${W===s.chatId&&!_?"selected":""}"
|
|
415
|
+
data-group-id="${m(s.chatId)}">
|
|
416
|
+
<span class="roles-group-arrow">${f}</span>
|
|
417
|
+
<span class="roles-group-icon">\u{1F4C1}</span>
|
|
418
|
+
<div class="roles-group-info">
|
|
419
|
+
<div class="roles-group-name">${m(s.name??s.chatId)}</div>
|
|
420
|
+
<div class="roles-group-meta">
|
|
421
|
+
${y}/${S} ${n("roles.botsWithRoles")}
|
|
422
|
+
</div>
|
|
423
|
+
</div>
|
|
424
|
+
<span class="roles-group-chevron"></span>
|
|
425
|
+
</div>
|
|
426
|
+
<div class="roles-bot-list">${b}</div>
|
|
427
|
+
</div>`}).join(""),t.querySelectorAll(".roles-group-row").forEach(s=>{s.addEventListener("click",()=>{let a=s.dataset.groupId;a&&(se.has(a)?se.delete(a):se.add(a),Z(document.getElementById("roles-search")?.value??""))})}),t.querySelectorAll(".roles-bot-row").forEach(s=>{s.addEventListener("click",a=>{a.stopPropagation();let d=s.dataset.groupId,f=s.dataset.botId;d&&f&&on(d,f)})})}async function on(e,t){W=e,_=t;let o=await lt(t,e),r=document.getElementById("roles-editor-empty"),s=document.getElementById("roles-editor-form"),a=document.getElementById("roles-editor-textarea"),d=document.getElementById("roles-editor-group-name"),f=document.getElementById("roles-editor-bot-name"),y=document.getElementById("roles-editor-chat-id");r&&(r.style.display="none"),s&&(s.style.display="");let S=ve.find(l=>l.chatId===e),b=S?.memberBots.find(l=>l.larkAppId===t);d&&(d.textContent=S?.name??e),f&&(f.textContent=b?.botName??t),y&&(y.textContent=`${e} \xB7 ${t}`),j=o.content??"",a&&(a.value=j,a.focus()),De(),Oe(),Z(document.getElementById("roles-search")?.value??"");let k=document.getElementById("roles-delete");k&&(k.style.display=o.hasRole?"":"none")}function De(){let e=document.getElementById("roles-editor-bytecount");if(!e)return;let t=new TextEncoder().encode(j).length;e.textContent=`${t} / ${xe} bytes`,e.className=`roles-bytecount ${t>3800?"warn":""} ${t>xe?"over":""}`,sn(t)}function sn(e){let t=document.getElementById("roles-save");if(!t)return;let o=e??new TextEncoder().encode(j).length;t.disabled=o>xe||j.trim().length===0}function Oe(){let e=document.getElementById("roles-preview");e&&(j.trim()?e.innerHTML=`<strong>${n("roles.preview")}</strong><pre>${m(j)}</pre>`:e.innerHTML=`<small>${n("roles.previewEmpty")}</small>`)}function it(){W=null,_=null,j="";let e=document.getElementById("roles-editor-empty"),t=document.getElementById("roles-editor-form"),o=document.getElementById("roles-editor-textarea"),r=document.getElementById("roles-delete");e&&(e.style.display=""),t&&(t.style.display="none"),o&&(o.value=""),r&&(r.style.display="none")}async function ct(e){e.innerHTML=Zt(),se.clear(),it(),await be();for(let t of ve)dt(t)>0&&se.add(t.chatId);Z(),document.getElementById("roles-search")?.addEventListener("input",t=>{Z(t.target.value)}),document.getElementById("roles-refresh")?.addEventListener("click",async()=>{if(await be(),Z(document.getElementById("roles-search")?.value??""),W&&_){let t=await lt(_,W),o=document.getElementById("roles-editor-textarea");o&&(o.value=t.content??""),j=t.content??"",De(),Oe();let r=document.getElementById("roles-delete");r&&(r.style.display=t.hasRole?"":"none")}}),document.getElementById("roles-save")?.addEventListener("click",async function(){if(!(!W||!_)){this.disabled=!0,this.textContent="...";try{if(await en(_,W,j)){await be(),Z(document.getElementById("roles-search")?.value??"");let o=document.getElementById("roles-delete");o&&(o.style.display="");let r=document.createElement("span");r.className="roles-saved-flash",r.textContent=` ${n("roles.saved")}`,document.querySelector(".roles-editor-footer")?.appendChild(r),setTimeout(()=>r.remove(),2e3)}else{let o=document.createElement("span");o.className="roles-saved-flash roles-save-error",o.textContent=j.trim().length===0?` ${n("roles.emptyError")}`:` ${n("roles.saveFailed")}`,document.querySelector(".roles-editor-footer")?.appendChild(o),setTimeout(()=>o.remove(),3e3)}}finally{this.disabled=!1,this.textContent=n("roles.save")}}}),document.getElementById("roles-delete")?.addEventListener("click",async function(){if(!(!W||!_)&&confirm(n("roles.confirmDelete"))){this.disabled=!0,this.textContent="...";try{await tn(_,W)&&(await be(),it(),Z(document.getElementById("roles-search")?.value??""))}finally{this.disabled=!1,this.textContent=n("roles.delete")}}}),document.getElementById("roles-editor-textarea")?.addEventListener("input",t=>{j=t.target.value,De(),Oe()})}function an(){let e=[["",n("workflow.filter.nonTerminal")],["all",n("workflow.filter.all")],["pending",Y("pending")],["running",Y("running")],["waiting",Y("waiting")],["succeeded",Y("succeeded")],["failed",Y("failed")],["cancelled",Y("cancelled")]];return`
|
|
359
428
|
<nav class="wf-subnav">
|
|
360
|
-
<a href="#/workflows" class="active" data-i18n="workflow.subnav.runs">${i(
|
|
361
|
-
<a href="#/workflows/catalog" data-i18n="workflow.subnav.catalog">${i(
|
|
429
|
+
<a href="#/workflows" class="active" data-i18n="workflow.subnav.runs">${i(n("workflow.subnav.runs"))}</a>
|
|
430
|
+
<a href="#/workflows/catalog" data-i18n="workflow.subnav.catalog">${i(n("workflow.subnav.catalog"))}</a>
|
|
362
431
|
</nav>
|
|
363
432
|
<form id="wf-filters" class="filters">
|
|
364
|
-
<input type="search" name="q" placeholder="${i(
|
|
433
|
+
<input type="search" name="q" placeholder="${i(n("workflow.searchPlaceholder"))}" />
|
|
365
434
|
<select name="status">
|
|
366
|
-
${e.map(([
|
|
435
|
+
${e.map(([t,o])=>`<option value="${i(t)}">${i(o)}</option>`).join("")}
|
|
367
436
|
</select>
|
|
368
437
|
<span id="wf-last-load" class="muted"></span>
|
|
369
438
|
</form>
|
|
370
439
|
<table>
|
|
371
440
|
<thead><tr>
|
|
372
|
-
<th>${i(
|
|
373
|
-
<th>${i(
|
|
374
|
-
<th>${i(
|
|
441
|
+
<th>${i(n("workflow.table.run"))}</th><th>${i(n("workflow.table.workflow"))}</th><th>${i(n("workflow.table.status"))}</th>
|
|
442
|
+
<th>${i(n("workflow.table.lastSeq"))}</th><th>${i(n("workflow.table.dangling"))}</th><th>${i(n("workflow.table.updated"))}</th>
|
|
443
|
+
<th>${i(n("workflow.table.chatApp"))}</th>
|
|
375
444
|
</tr></thead>
|
|
376
445
|
<tbody id="wf-tbody"></tbody>
|
|
377
446
|
</table>
|
|
378
|
-
`}var
|
|
447
|
+
`}var rn=5e3,ln=2e3,ie=new Set(["succeeded","failed","cancelled"]);function i(e){return e.replace(/[&<>"']/g,t=>({"&":"&","<":"<",">":">",'"':""","'":"'"})[t])}function dn(e){let t=new Date(e),r=Date.now()-e;return r<6e4?n("time.secondsAgo",{value:Math.max(1,Math.floor(r/1e3))}):r<36e5?n("time.minutesAgo",{value:Math.floor(r/6e4)}):r<864e5?n("time.hoursAgo",{value:Math.floor(r/36e5)}):t.toISOString().slice(0,19).replace("T"," ")}function G(e){return`<span class="${ie.has(e)?"wf-status terminal":"wf-status live"} wf-status-${i(e)}">${i(Y(e))}</span>`}function Y(e){let t=`workflow.status.${e}`,o=n(t);return o===t?e:o}function mt(e){let t=location.hash.match(/^#\/workflows\/([^?#]+)(?:\?([^#]*))?$/);if(t){let o=new URLSearchParams(t[2]??"");return un(e,decodeURIComponent(t[1]),{focusAttemptId:o.get("attempt")??void 0})}return cn(e)}function cn(e){e.innerHTML=an();let t=e.querySelector("#wf-tbody"),o=e.querySelector("#wf-filters"),r=e.querySelector("#wf-last-load"),s=[],a=null,d=!1,f=null,y=!1;function S(g){let I=(new FormData(o).get("q")??"").trim().toLowerCase();return I?g.filter($=>$.runId.toLowerCase().includes(I)||$.workflowId.toLowerCase().includes(I)||($.chatId??"").toLowerCase().includes(I)):g}function b(){let g=S(s);if(g.length===0){t.innerHTML=`<tr><td colspan="7" class="empty">${f?i(n("workflow.list.failedLoad",{error:f})):s.length===0?i(n("workflow.list.noRuns")):i(n("workflow.list.noFilterMatch"))}</td></tr>`;return}t.innerHTML=g.map(u=>{let I=`${u.dEf}/${u.dAct}/${u.dWait}`,$=u.dEf+u.dAct+u.dWait>0?"wf-dangling has":"wf-dangling none",v=[];u.chatId&&v.push(i(u.chatId)),u.larkAppId&&v.push(`<span class="muted">${i(u.larkAppId)}</span>`);let M=v.length>0?v.join("<br/>"):"\u2014",c=pn(u);return`<tr data-runid="${i(u.runId)}">
|
|
379
448
|
<td><a href="#/workflows/${encodeURIComponent(u.runId)}"><code>${i(u.runId)}</code></a></td>
|
|
380
449
|
<td>${i(u.workflowId)}</td>
|
|
381
|
-
<td>${
|
|
450
|
+
<td>${G(u.status)}${u.failedNodeId?` <span class="muted">(${i(u.failedNodeId)})</span>`:""}${c}</td>
|
|
382
451
|
<td>${u.lastSeq}</td>
|
|
383
|
-
<td class="${
|
|
384
|
-
<td title="${i(new Date(u.updatedAt).toISOString())}">${
|
|
385
|
-
<td>${
|
|
386
|
-
</tr>`}).join("")}function
|
|
452
|
+
<td class="${$}">${I}</td>
|
|
453
|
+
<td title="${i(new Date(u.updatedAt).toISOString())}">${dn(u.updatedAt)}</td>
|
|
454
|
+
<td>${M}</td>
|
|
455
|
+
</tr>`}).join("")}function k(){f?(r.textContent=n("workflow.list.error",{error:f}),r.classList.add("error")):(r.textContent=n("workflow.list.loaded",{count:s.length,time:new Date().toLocaleTimeString()}),r.classList.remove("error"))}async function l(){if(!(y||d)&&!document.hidden){d=!0;try{let g=o.elements.namedItem("status")?.value??"",u=new URLSearchParams;g==="all"?u.set("all","1"):g&&u.set("status",g);let I="/api/workflows/runs"+(u.toString()?`?${u}`:""),$=await fetch(I);$.ok?(s=(await $.json()).runs??[],f=null):(f=`HTTP ${$.status}`,s=[])}catch(g){f=g?.message??String(g),s=[]}finally{d=!1,y||(b(),k())}}}function T(){a!==null&&window.clearTimeout(a),a=window.setTimeout(async()=>{await l(),y||T()},rn)}function L(){document.hidden||l()}return o.addEventListener("input",()=>{b()}),o.addEventListener("change",g=>{g.target.getAttribute("name")==="status"&&l()}),document.addEventListener("visibilitychange",L),l().then(()=>{y||T()}),()=>{y=!0,a!==null&&window.clearTimeout(a),document.removeEventListener("visibilitychange",L)}}function un(e,t,o={}){e.innerHTML=`
|
|
387
456
|
<div class="wf-detail-head">
|
|
388
|
-
<a class="btn-link" href="#/workflows">${i(
|
|
457
|
+
<a class="btn-link" href="#/workflows">${i(n("workflow.detail.back"))}</a>
|
|
389
458
|
<div>
|
|
390
|
-
<h2><code>${i(
|
|
391
|
-
<div id="wf-detail-subtitle" class="muted">${i(
|
|
459
|
+
<h2><code>${i(t)}</code></h2>
|
|
460
|
+
<div id="wf-detail-subtitle" class="muted">${i(n("workflow.detail.loading"))}</div>
|
|
392
461
|
</div>
|
|
393
|
-
<button id="wf-cancel-run" type="button" class="contrast" hidden>${i(
|
|
462
|
+
<button id="wf-cancel-run" type="button" class="contrast" hidden>${i(n("workflow.detail.cancel"))}</button>
|
|
394
463
|
<span id="wf-detail-refresh" class="muted"></span>
|
|
395
464
|
</div>
|
|
396
465
|
<section id="wf-detail-error" class="hint-warn" hidden></section>
|
|
@@ -399,20 +468,20 @@ ${u.join(`
|
|
|
399
468
|
<section id="wf-dangling-panel"></section>
|
|
400
469
|
<section class="wf-panel">
|
|
401
470
|
<div class="wf-panel-title">
|
|
402
|
-
<h3>${i(
|
|
471
|
+
<h3>${i(n("workflow.detail.parallel"))}</h3>
|
|
403
472
|
<span id="wf-parallel-meta" class="muted"></span>
|
|
404
473
|
</div>
|
|
405
474
|
<div id="wf-parallel-view"></div>
|
|
406
475
|
</section>
|
|
407
476
|
<section class="wf-panel">
|
|
408
477
|
<div class="wf-panel-title">
|
|
409
|
-
<h3>${i(
|
|
478
|
+
<h3>${i(n("workflow.detail.nodes"))}</h3>
|
|
410
479
|
</div>
|
|
411
480
|
<div class="wf-table-scroll">
|
|
412
481
|
<table>
|
|
413
482
|
<thead><tr>
|
|
414
|
-
<th>${i(
|
|
415
|
-
<th>${i(
|
|
483
|
+
<th>${i(n("workflow.detail.node"))}</th><th>${i(n("workflow.detail.nodeStatus"))}</th><th>${i(n("workflow.detail.activity"))}</th><th>${i(n("workflow.detail.activityStatus"))}</th>
|
|
484
|
+
<th>${i(n("workflow.detail.attempts"))}</th><th>${i(n("workflow.detail.current"))}</th><th>${i(n("workflow.detail.detail"))}</th>
|
|
416
485
|
</tr></thead>
|
|
417
486
|
<tbody id="wf-node-tbody"></tbody>
|
|
418
487
|
</table>
|
|
@@ -420,237 +489,237 @@ ${u.join(`
|
|
|
420
489
|
</section>
|
|
421
490
|
<section class="wf-panel">
|
|
422
491
|
<div class="wf-panel-title">
|
|
423
|
-
<h3>${i(
|
|
492
|
+
<h3>${i(n("workflow.detail.nodeIO"))}</h3>
|
|
424
493
|
</div>
|
|
425
494
|
<div id="wf-io-list" class="wf-io-list"></div>
|
|
426
495
|
</section>
|
|
427
496
|
<section class="wf-panel">
|
|
428
497
|
<div class="wf-panel-title">
|
|
429
|
-
<h3>${i(
|
|
430
|
-
<button id="wf-load-older" type="button" hidden>${i(
|
|
498
|
+
<h3>${i(n("workflow.detail.timeline"))}</h3>
|
|
499
|
+
<button id="wf-load-older" type="button" hidden>${i(n("workflow.detail.loadOlder"))}</button>
|
|
431
500
|
</div>
|
|
432
501
|
<div class="wf-table-scroll wf-timeline-scroll">
|
|
433
502
|
<table>
|
|
434
503
|
<thead><tr>
|
|
435
|
-
<th>${i(
|
|
504
|
+
<th>${i(n("workflow.detail.seq"))}</th><th>${i(n("workflow.detail.event"))}</th><th>${i(n("workflow.detail.actor"))}</th><th>${i(n("workflow.detail.node"))}</th><th>${i(n("workflow.detail.activity"))}</th><th>${i(n("workflow.detail.error"))}</th><th>${i(n("workflow.detail.time"))}</th>
|
|
436
505
|
</tr></thead>
|
|
437
506
|
<tbody id="wf-event-tbody"></tbody>
|
|
438
507
|
</table>
|
|
439
508
|
</div>
|
|
440
509
|
<div id="wf-event-meta" class="muted"></div>
|
|
441
510
|
</section>
|
|
442
|
-
`;let r=e.querySelector("#wf-detail-subtitle"),s=e.querySelector("#wf-detail-refresh"),a=e.querySelector("#wf-detail-error"),
|
|
443
|
-
<span class="muted error">${i(e.errorCode)}</span>${i(
|
|
444
|
-
</div>`}function
|
|
511
|
+
`;let r=e.querySelector("#wf-detail-subtitle"),s=e.querySelector("#wf-detail-refresh"),a=e.querySelector("#wf-detail-error"),d=e.querySelector("#wf-cancel-status"),f=e.querySelector("#wf-summary"),y=e.querySelector("#wf-dangling-panel"),S=e.querySelector("#wf-parallel-view"),b=e.querySelector("#wf-parallel-meta"),k=e.querySelector("#wf-node-tbody"),l=e.querySelector("#wf-io-list"),T=e.querySelector(".wf-timeline-scroll"),L=e.querySelector("#wf-event-tbody"),g=e.querySelector("#wf-event-meta"),u=e.querySelector("#wf-cancel-run"),I=e.querySelector("#wf-load-older"),$=null,v=[],M=new Set,c=null,p=null,h=!1,E=0,A=null,R=!1,q=!1,B=!1,D=new Set,Te=new Map,Ne=new Map,ue=new Map,fe=new Set,pe=new Map,X=new Set,ae=new Map,At=new Map,je=0,Ue=o.focusAttemptId;function U(w){if(!w){a.hidden=!0,a.textContent="";return}a.hidden=!1,a.textContent=w}function Fe(w){if(!w){d.hidden=!0,d.textContent="";return}d.hidden=!1,d.textContent=w}async function We(){let w=await fetch(`/api/workflows/runs/${encodeURIComponent(t)}/snapshot`);if(w.status===404)throw new Error(n("workflow.detail.unknownRun"));if(!w.ok)throw new Error(n("workflow.detail.snapshotHttp",{status:w.status}));$=await w.json()}async function me(w){let P=await fetch(`/api/workflows/runs/${encodeURIComponent(t)}/events?${w}`);if(P.status===404)throw new Error(n("workflow.detail.unknownRun"));if(!P.ok)throw new Error(n("workflow.detail.eventsHttp",{status:P.status}));return await P.json()}function we(w,P){let x=w.filter(H=>M.has(H.eventId)?!1:(M.add(H.eventId),!0));x.length!==0&&(v=P==="prepend"?[...x,...v]:[...v,...x],v.sort((H,z)=>le(H.eventId)-le(z.eventId)))}async function Ht(){await We();let w=await me(new URLSearchParams({tail:"100"}));v=[],M=new Set,we(w.events,"append"),c=w.oldestSeq,p=w.newestSeq,h=w.hasOlder,E=w.totalCount,F()}async function ge(){if(!(R||q||document.hidden)){q=!0;try{if(await We(),p!==null){let w=await me(new URLSearchParams({afterSeq:String(p),limit:"200"}));we(w.events,"append"),w.newestSeq!==null&&(p=w.newestSeq),c===null&&w.oldestSeq!==null&&(c=w.oldestSeq),E=w.totalCount}else{let w=await me(new URLSearchParams({tail:"1"}));we(w.events,"append"),c=w.oldestSeq,p=w.newestSeq,h=w.hasOlder,E=w.totalCount}U(null),F()}catch(w){U(w?.message??String(w))}finally{q=!1}}}async function Rt(){if(!(c===null||!h)){I.disabled=!0;try{let w=await me(new URLSearchParams({beforeSeq:String(c),limit:"100"}));we(w.events,"prepend"),w.oldestSeq!==null&&(c=w.oldestSeq),h=w.hasOlder,E=w.totalCount,U(null),F()}catch(w){U(w?.message??String(w))}finally{I.disabled=!1}}}async function xt(){if(!$||ie.has($.run.status)||B)return;if(!$.chatBinding?.larkAppId){U(n("workflow.detail.cancelUnavailable",{runId:t}));return}let w=mn($),P=n("workflow.detail.cancelConfirm",{runId:t,...w});if(window.confirm(P)){B=!0,u.disabled=!0;try{let x=await fetch(`/api/workflows/runs/${encodeURIComponent(t)}/cancel`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({reason:"cancelled via dashboard"})});if(x.status===401)throw new Error(n("workflow.detail.writeAccessCancel"));let H=await x.json().catch(()=>({}));if(!x.ok||!H.ok)throw new Error(H.hint??H.error??n("workflow.detail.cancelHttp",{status:x.status}));Fe(H.pending?n("workflow.detail.cancelPending"):null),U(null),await ge()}catch(x){U(x?.message??String(x))}finally{B=!1,u.disabled=!1,F()}}}async function Dt(w,P){if(!X.has(w)){X.add(w),ae.delete(w),F();try{let x=await fetch(`/api/workflows/runs/${encodeURIComponent(t)}/attempts/${encodeURIComponent(P)}/${encodeURIComponent(w)}/resume`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({})});if(x.status===401)throw new Error(n("workflow.detail.writeAccessResume"));let H=await x.json().catch(()=>({}));if(!x.ok||!H.ok||!H.resumeId||!H.url)throw new Error(H.hint??H.message??H.error??n("workflow.detail.resumeStartFailed",{status:x.status}));pe.set(w,{resumeId:H.resumeId,url:H.url})}catch(x){let H=x?.message??String(x);ae.set(w,H)}finally{X.delete(w),F()}}}async function Ot(w,P){if(!X.has(w)){X.add(w),ae.delete(w),F();try{let x=await fetch(`/api/workflows/runs/${encodeURIComponent(t)}/attempts/${encodeURIComponent(P)}/${encodeURIComponent(w)}/resume/end`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({reason:"ended_by_dashboard"})});if(x.status===401)throw new Error(n("workflow.detail.writeAccessResume"));let H=await x.json().catch(()=>({}));if(!x.ok||!H.ok)if(H.error==="resume_not_running")pe.delete(w);else throw new Error(H.hint??H.message??H.error??n("workflow.detail.resumeEndFailed",{status:x.status}));else pe.delete(w)}catch(x){let H=x?.message??String(x);ae.set(w,H)}finally{X.delete(w),F()}}}async function qt(w,P){if(!fe.has(w)){fe.add(w),ue.delete(w),F();try{let x=Ne.get(w)?.trim()||void 0,H=await fetch(`/api/workflows/runs/${encodeURIComponent(t)}/${P}`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({comment:x})});if(H.status===401)throw new Error(n("workflow.detail.writeAccessApproval"));let z=await H.json().catch(()=>({}));if(!H.ok||!z.ok)throw new Error(z.hint??z.message??z.error??n("workflow.detail.actionHttp",{action:P,status:H.status}));let Ee=P==="approve"?n("workflow.detail.approved"):n("workflow.detail.rejected");ue.set(w,{kind:"ok",text:z.alreadyTerminal?n("workflow.detail.alreadyTerminal",{label:Ee}):z.pending?n("workflow.detail.workflowContinue",{label:Ee}):n("workflow.detail.workflowRefreshing",{label:Ee})}),U(null),await ge()}catch(x){let H=x?.message??String(x);ue.set(w,{kind:"error",text:H}),U(H)}finally{fe.delete(w),F()}}}function F(){if(!$)return;je=T.scrollTop;let w=$.run;ie.has(w.status)&&Fe(null),r.innerHTML=`${i(w.workflowId??"?")} \xB7 ${G(w.status)} \xB7 lastSeq ${$.lastSeq}`,s.textContent=n("workflow.detail.refreshed",{time:new Date().toLocaleTimeString()}),u.hidden=ie.has(w.status),u.disabled=B||!$.chatBinding?.larkAppId,u.textContent=$.chatBinding?.larkAppId?n("workflow.detail.cancel"):n("workflow.detail.cliCancelOnly"),u.title=$.chatBinding?.larkAppId?n("workflow.detail.cancelTitle"):n("workflow.detail.cliCancelTitle",{runId:t}),fn(f,$),wn(y,$),gn(S,b,$,v),$n(k,$),Sn(l,$,D,Te,{comments:Ne,statuses:ue,resolving:fe,onResolve:qt},{sessions:pe,pending:X,errors:ae,onStart:Dt,onEnd:Ot},Ue,At)&&(Ue=void 0),Vn(L,v),T.scrollTop=je,I.hidden=!h,g.textContent=n("workflow.detail.eventsLoaded",{loaded:v.length,total:E})}function Le(){if(A!==null&&window.clearTimeout(A),$&&ie.has($.run.status)){A=null;return}A=window.setTimeout(async()=>{await ge(),R||Le()},ln)}function _e(){document.hidden||ge().then(()=>{!R&&A===null&&Le()})}return I.addEventListener("click",()=>{Rt()}),u.addEventListener("click",()=>{xt()}),document.addEventListener("visibilitychange",_e),Ht().then(()=>{U(null),R||Le()}).catch(w=>{U(w?.message??String(w)),r.textContent=n("workflow.detail.loadFailed")}),()=>{R=!0,A!==null&&window.clearTimeout(A),document.removeEventListener("visibilitychange",_e)}}function fn(e,t){let o=t.run,r=[[n("workflow.summary.workflow"),i(o.workflowId??"?")],[n("workflow.summary.status"),G(o.status)],[n("workflow.summary.lastSeq"),String(t.lastSeq)],[n("workflow.summary.updated"),i(new Date(t.updatedAt).toLocaleString())],[n("workflow.summary.revision"),i($e(o.revisionId))],[n("workflow.summary.initiator"),i(o.initiator??"-")]];o.failedNodeId&&r.push([n("workflow.summary.failedNode"),i(o.failedNodeId)]),o.cancelOriginEventId&&r.push([n("workflow.summary.cancelOrigin"),i(o.cancelOriginEventId)]),t.chatBinding&&(r.push([n("workflow.summary.chat"),`<code>${i(t.chatBinding.chatId)}</code>`]),r.push([n("workflow.summary.app"),`<code>${i(t.chatBinding.larkAppId)}</code>`])),e.innerHTML=r.map(([s,a])=>`<div class="wf-summary-item"><span>${s}</span><strong>${a}</strong></div>`).join("")}function pn(e){if(!e.errorCode)return"";let t=e.errorMessage?` \u2014 ${Zn(e.errorMessage,96)}`:"";return`<div class="wf-run-error">
|
|
512
|
+
<span class="muted error">${i(e.errorCode)}</span>${i(t)}
|
|
513
|
+
</div>`}function mn(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 wn(e,t){let o=t.dangling,r=[[n("workflow.dangling.activities"),o.activities],[n("workflow.dangling.effects"),o.effectAttempted],[n("workflow.dangling.waits"),o.waits],[n("workflow.dangling.cancels"),o.cancels]],s=new Set(r.flatMap(([,a])=>a)).size;if(e.className=s>0?"wf-panel wf-dangling-panel has":"wf-panel wf-dangling-panel",s===0){e.innerHTML=`<div class="wf-panel-title"><h3>${i(n("workflow.detail.dangling"))}</h3></div><div class="muted">${i(n("workflow.detail.noDangling"))}</div>`;return}e.innerHTML=`<div class="wf-panel-title"><h3>${i(n("workflow.detail.dangling"))}</h3><span class="wf-dangling has">${s}</span></div>
|
|
445
514
|
<div class="wf-dangling-grid">
|
|
446
|
-
${r.map(([a,
|
|
447
|
-
</div>`}function
|
|
448
|
-
<span title="${i(new Date(
|
|
449
|
-
<span title="${i(new Date(
|
|
515
|
+
${r.map(([a,d])=>`<div><strong>${a}</strong>${d.length===0?`<div class="muted">${i(n("workflow.detail.none"))}</div>`:`<ul>${d.map(f=>`<li><code>${i(f)}</code></li>`).join("")}</ul>`}</div>`).join("")}
|
|
516
|
+
</div>`}function gn(e,t,o,r){let s=hn(r,o);if(s.length===0){t.textContent="",e.innerHTML=`<div class="empty">${i(n("workflow.detail.noParallelData"))}</div>`;return}let a=Date.now(),d=Math.min(...s.map(l=>l.startedAt)),f=Math.max(...s.map(l=>l.endedAt??a),d+1e3),y=Math.max(1,f-d),S=vn(s,a),b=s.filter(l=>!l.endedAt&&(l.status==="running"||l.status==="effectAttempting")).length;t.textContent=n("workflow.detail.parallelMeta",{count:s.length,max:S,running:b});let k=s.sort((l,T)=>l.startedAt-T.startedAt||l.activityId.localeCompare(T.activityId)).map(l=>bn(l,d,y,a)).join("");e.innerHTML=`<div class="wf-parallel-axis">
|
|
517
|
+
<span title="${i(new Date(d).toISOString())}">${i(ke(d))}</span>
|
|
518
|
+
<span title="${i(new Date(f).toISOString())}">${i(ke(f))}</span>
|
|
450
519
|
</div>
|
|
451
|
-
<div class="wf-parallel-list">${
|
|
520
|
+
<div class="wf-parallel-list">${k}</div>`}function hn(e,t){let o=new Map,r=new Map(t.activities.map(s=>[s.activityId,s.ownerNodeId]));for(let s of[...e].sort((a,d)=>le(a.eventId)-le(d.eventId))){let a=Xn(s);if(!a)continue;let d=typeof a.activityId=="string"?a.activityId:void 0,f=typeof a.attemptId=="string"?a.attemptId:void 0;if(!d||!f)continue;let y=o.get(f);if(s.type==="attemptCreated"){let S=typeof a.attemptNumber=="number"?a.attemptNumber:void 0;y={nodeId:typeof a.nodeId=="string"?a.nodeId:r.get(d),activityId:d,attemptId:f,attemptNumber:S,status:"pending",startedAt:s.timestamp},o.set(f,y);continue}y||(y={nodeId:r.get(d),activityId:d,attemptId:f,status:"pending",startedAt:s.timestamp},o.set(f,y)),s.type==="activityRunning"?(y.status="running",y.runningAt=s.timestamp):s.type==="effectAttempted"?y.status="effectAttempting":s.type==="activityWaiting"||s.type==="waitCreated"?y.status="waiting":yn(s.type)&&(y.status=kn(s.type),y.endedAt=s.timestamp,y.endType=s.type)}return[...o.values()]}function bn(e,t,o,r){let s=e.endedAt??r,a=pt((e.startedAt-t)/o*100,0,100),d=pt((Math.max(s,e.startedAt+1)-e.startedAt)/o*100,.7,100-a),f=e.nodeId??e.activityId,y=e.attemptNumber!==void 0?`#${e.attemptNumber}`:$e(e.attemptId),S=[`${f} ${e.status}`,`${new Date(e.startedAt).toISOString()} \u2192 ${e.endedAt?new Date(e.endedAt).toISOString():n("workflow.detail.parallelNow")}`,e.endType?`end: ${e.endType}`:void 0].filter(Boolean).join(`
|
|
452
521
|
`);return`<div class="wf-parallel-row">
|
|
453
522
|
<div class="wf-parallel-label">
|
|
454
|
-
<code>${i(
|
|
523
|
+
<code>${i(f)}</code>
|
|
455
524
|
<span class="muted">${i(e.activityId)} \xB7 ${i(y)}</span>
|
|
456
525
|
</div>
|
|
457
526
|
<div class="wf-parallel-track">
|
|
458
|
-
<div class="wf-parallel-bar wf-parallel-${i(e.status)}" style="left:${a.toFixed(3)}%;width:${
|
|
459
|
-
<span>${i(
|
|
527
|
+
<div class="wf-parallel-bar wf-parallel-${i(e.status)}" style="left:${a.toFixed(3)}%;width:${d.toFixed(3)}%;" title="${i(S)}">
|
|
528
|
+
<span>${i(Y(e.status))}</span>
|
|
460
529
|
</div>
|
|
461
530
|
</div>
|
|
462
|
-
</div>`}function
|
|
531
|
+
</div>`}function vn(e,t){let o=[];for(let a of e)o.push({time:a.startedAt,delta:1}),o.push({time:a.endedAt??t,delta:-1});o.sort((a,d)=>a.time-d.time||d.delta-a.delta);let r=0,s=0;for(let a of o)r+=a.delta,s=Math.max(s,r);return s}function yn(e){return e==="activitySucceeded"||e==="activityFailed"||e==="activityTimedOut"||e==="activityCanceled"}function kn(e){return e==="activitySucceeded"?"succeeded":e==="activityCanceled"?"cancelled":e==="activityTimedOut"?"timedOut":"failed"}function $n(e,t){let o=new Map(t.activities.map(a=>[a.activityId,a])),r=new Set,s=[];for(let a of t.nodes){let d=(a.activityId?o.get(a.activityId):void 0)??t.activities.find(f=>f.ownerNodeId===a.nodeId);d&&r.add(d.activityId),s.push(ut(a,d))}for(let a of t.activities)r.has(a.activityId)||s.push(ut(void 0,a));e.innerHTML=s.length>0?s.join(""):`<tr><td colspan="7" class="empty">${i(n("workflow.detail.noNodes"))}</td></tr>`}function ut(e,t){let o=t?.attempts[t.attempts.length-1];return`<tr>
|
|
463
532
|
<td>${e?`<code>${i(e.nodeId)}</code>`:'<span class="muted">-</span>'}</td>
|
|
464
|
-
<td>${e?
|
|
465
|
-
<td>${
|
|
466
|
-
<td>${
|
|
467
|
-
<td>${
|
|
533
|
+
<td>${e?G(e.status):'<span class="muted">-</span>'}</td>
|
|
534
|
+
<td>${t?`<code>${i(t.activityId)}</code>`:'<span class="muted">-</span>'}</td>
|
|
535
|
+
<td>${t?G(t.status):'<span class="muted">-</span>'}</td>
|
|
536
|
+
<td>${t?.attempts.length??0}</td>
|
|
468
537
|
<td>${o?`<code>${i(o.attemptId)}</code>`:'<span class="muted">-</span>'}</td>
|
|
469
|
-
<td>${o?
|
|
470
|
-
</tr>`}function
|
|
538
|
+
<td>${o?Kn(o):`<span class="muted">${i(n("workflow.detail.idle"))}</span>`}</td>
|
|
539
|
+
</tr>`}function Sn(e,t,o,r,s,a,d,f){Fn(e,o,r),Pn(e,s.comments);let y=!!(d&&t.attemptIO?.[d]?.terminal);y&&d&&o.add(qe(d,n("workflow.detail.liveTerminal")));let S=In(t),b=new Set;if(f){for(let l of S){b.add(l.key);let T=f.get(l.key);T||(T=Tn(l.key),f.set(l.key,T),e.appendChild(T.article)),Ln(T,l,o,s,a,d)}for(let[l,T]of Array.from(f))b.has(l)||(T.article.remove(),f.delete(l));if(S.length===0){if(!e.querySelector(".wf-io-empty-placeholder")){let l=document.createElement("div");l.className="empty wf-io-empty-placeholder",l.textContent=n("workflow.detail.noNodeIO"),e.appendChild(l)}}else e.querySelector(".wf-io-empty-placeholder")?.remove()}else{let l=[];for(let T of S)l.push(Hn(T,o,s,a,d));e.innerHTML=l.length>0?l.join(""):`<div class="empty">${i(n("workflow.detail.noNodeIO"))}</div>`}_n(e,r);let k=Un(e,d);return Wn(e,o),Jn(e,r),jn(e,s),St(e,a),k&&y}function In(e){let t=new Map(e.activities.map(s=>[s.activityId,s])),o=new Set,r=[];for(let s of e.nodes){let a=(s.activityId?t.get(s.activityId):void 0)??e.activities.find(d=>d.ownerNodeId===s.nodeId);if(!a){r.push({key:`node:${s.nodeId}`,node:s});continue}o.add(a.activityId),r.push({key:`activity:${a.activityId}`,node:s,activity:a,io:e.attemptIO?.[ye(a)?.attemptId??""]})}for(let s of e.activities)o.has(s.activityId)||r.push({key:`activity:${s.activityId}`,activity:s,io:e.attemptIO?.[ye(s)?.attemptId??""]});return r}function Tn(e){let t=document.createElement("article");t.className="wf-io-card",t.dataset.wfCardKey=e;let o=document.createElement("div");o.className="wf-io-card-head";let r=document.createElement("div");r.className="wf-io-terminal-slot";let s=document.createElement("div");return s.className="wf-io-grid",t.appendChild(o),t.appendChild(r),t.appendChild(s),{article:t,head:o,terminalSlot:r,grid:s,currentTerminalUrl:null}}function Ln(e,t,o,r,s,a){let d=ye(t.activity),f=t.node?.nodeId??t.activity?.ownerNodeId??t.activity?.activityId??"unknown",y=!!(d&&d.attemptId===a);e.article.classList.toggle("is-focused",y),d?e.article.dataset.wfAttemptCard=d.attemptId:delete e.article.dataset.wfAttemptCard;let S=$t(d,r);e.head.innerHTML=`
|
|
471
540
|
<header>
|
|
472
541
|
<div>
|
|
473
|
-
<strong><code>${i(
|
|
474
|
-
<span class="muted">${
|
|
542
|
+
<strong><code>${i(f)}</code></strong>
|
|
543
|
+
<span class="muted">${t.activity?i(t.activity.activityId):i(n("workflow.detail.notDispatched"))}</span>
|
|
475
544
|
</div>
|
|
476
|
-
<div>${
|
|
545
|
+
<div>${t.node?G(t.node.status):""} ${t.activity?G(t.activity.status):""}</div>
|
|
477
546
|
</header>
|
|
478
547
|
<div class="wf-io-meta">
|
|
479
|
-
${
|
|
548
|
+
${d?`${i(n("workflow.detail.attempt"))} <code>${i(d.attemptId)}</code>`:i(n("workflow.detail.noAttempt"))}
|
|
480
549
|
</div>
|
|
481
550
|
${S}
|
|
482
|
-
`;let
|
|
483
|
-
${
|
|
484
|
-
${
|
|
485
|
-
${
|
|
486
|
-
${
|
|
487
|
-
${
|
|
488
|
-
`}function
|
|
489
|
-
<summary>${i(
|
|
551
|
+
`;let b=wt(d,t.activity,t.io?.terminal,s),k=b?.url??null;if(k!==e.currentTerminalUrl)b===null?e.terminalSlot.innerHTML="":e.terminalSlot.innerHTML=gt(t.key,d,t.activity,t.io?.terminal,b,o,s),e.currentTerminalUrl=k;else if(b!==null&&t.io?.terminal){let T=e.terminalSlot.querySelector("details.wf-terminal-block > summary");if(T){let L=ht(b.kind);T.innerHTML=`${i(L)} ${kt(d,t.io.terminal)}`}d&&Nn(e.terminalSlot,d,t.activity,t.io.terminal,b,s)}let l=d?.attemptId??t.activity?.activityId??t.node?.nodeId??"unknown";e.grid.innerHTML=`
|
|
552
|
+
${J(l,n("workflow.detail.authoredInput"),t.io?.input,o)}
|
|
553
|
+
${J(l,n("workflow.detail.resolvedInput"),t.io?.resolvedInput,o)}
|
|
554
|
+
${J(l,n("workflow.detail.output"),t.io?.output,o)}
|
|
555
|
+
${J(l,n("workflow.detail.executionLog"),t.io?.log,o)}
|
|
556
|
+
${t.io?.waitPrompt?J(l,n("workflow.detail.waitPrompt"),t.io.waitPrompt,o):""}
|
|
557
|
+
`}function wt(e,t,o,r){if(!o||o.error)return null;if(Rn(e,o))return{kind:"live",url:Dn(o)};if(!e||!t||!xn(e,o))return null;let s=qn();if(!s)return null;let a=r?.sessions.get(e.attemptId);return a?{kind:"resume",url:a.url,resumeId:a.resumeId,downloadUrl:ft(s,t.activityId,e.attemptId)}:{kind:"replay",url:On(s,t.activityId,e.attemptId,!!o.hasPtyLog),downloadUrl:ft(s,t.activityId,e.attemptId)}}function gt(e,t,o,r,s,a,d){if(!r)return"";let f=ht(s.kind),y=qe(e,f),S=kt(t,r),b=En(s.kind),k=s.kind==="replay"||s.kind==="resume"?`<a class="btn-link" href="${i(s.downloadUrl)}" download>${i(n("workflow.detail.downloadFullLog"))}</a>`:"",l=t?vt(t,o,r,s,d):"",T=t?yt(t.attemptId,d):"";return`<details class="wf-io-block wf-terminal-block" data-io-key="${i(y)}"${a.has(y)?" open":""}>
|
|
558
|
+
<summary>${i(f)} ${S}</summary>
|
|
490
559
|
<div class="wf-terminal-actions">
|
|
491
|
-
<a class="btn-link" href="${i(s.url)}" target="_blank" rel="noreferrer">${i(
|
|
492
|
-
${
|
|
560
|
+
<a class="btn-link" href="${i(s.url)}" target="_blank" rel="noreferrer">${i(b)}</a>
|
|
561
|
+
${k}
|
|
493
562
|
${l}
|
|
494
563
|
</div>
|
|
495
|
-
${
|
|
496
|
-
<iframe class="wf-terminal-frame" src="${i(s.url)}" title="${i(
|
|
497
|
-
</details>`}function
|
|
564
|
+
${T}
|
|
565
|
+
<iframe class="wf-terminal-frame" src="${i(s.url)}" title="${i(f)}" loading="lazy"></iframe>
|
|
566
|
+
</details>`}function ht(e){return e==="live"?n("workflow.detail.liveTerminal"):e==="resume"?n("workflow.detail.terminalResume"):n("workflow.detail.terminalReplay")}function En(e){return e==="live"?n("workflow.detail.openTerminalNewTab"):e==="resume"?n("workflow.detail.openResumeNewTab"):n("workflow.detail.openReplayNewTab")}var bt=new Set(["antigravity","cursor"]),Cn=new Set(["aiden","coco","claude-code","codex"]);function Mn(e){return!!e&&(Cn.has(e)||bt.has(e))}function An(e){return!!e&&bt.has(e)}function vt(e,t,o,r,s){if(!s||r.kind==="live"||!t)return"";let a=r.kind==="resume",d=s.pending.has(e.attemptId),f=`data-wf-resume-attempt="${i(e.attemptId)}" data-wf-resume-activity="${i(t.activityId)}"`;return a?`<button type="button" class="btn-link" data-wf-resume-button="1" data-wf-resume-action="end" ${f}${d?" disabled":""}>${i(d?n("workflow.detail.resumeEnding"):n("workflow.detail.endResumeSession"))}</button>`:Mn(o.cliId)?An(o.cliId)&&!o.cliSessionId?`<button type="button" class="btn-link" data-wf-resume-button="1" disabled title="${i(n("workflow.detail.resumeMissingCliSession"))}">${i(n("workflow.detail.resumeSession"))}</button>`:`<button type="button" class="btn-link" data-wf-resume-button="1" data-wf-resume-action="start" ${f}${d?" disabled":""}>${i(d?n("workflow.detail.resumeStarting"):n("workflow.detail.resumeSession"))}</button>`:`<button type="button" class="btn-link" data-wf-resume-button="1" disabled title="${i(n("workflow.detail.resumeUnsupportedCli",{cliId:o.cliId??"?"}))}">${i(n("workflow.detail.resumeSession"))}</button>`}function yt(e,t){if(!t)return"";let o=t.errors.get(e);return o?`<div class="hint-warn wf-resume-status" data-wf-resume-status="${i(e)}">${i(o)}</div>`:""}function Hn(e,t,o,r,s){let a=ye(e.activity),d=e.node?.nodeId??e.activity?.ownerNodeId??e.activity?.activityId??"unknown",f=a?.attemptId??e.activity?.activityId??e.node?.nodeId??"unknown",y=$t(a,o),S=a?.attemptId===s?" is-focused":"",b=a?` data-wf-attempt-card="${i(a.attemptId)}"`:"",k=wt(a,e.activity,e.io?.terminal,r),l=k?gt(f,a,e.activity,e.io?.terminal,k,t,r):"";return`<article class="wf-io-card${S}" data-wf-card-key="${i(e.key)}"${b}>
|
|
498
567
|
<div class="wf-io-card-head">
|
|
499
568
|
<header>
|
|
500
569
|
<div>
|
|
501
|
-
<strong><code>${i(
|
|
502
|
-
<span class="muted">${e.activity?i(e.activity.activityId):i(
|
|
570
|
+
<strong><code>${i(d)}</code></strong>
|
|
571
|
+
<span class="muted">${e.activity?i(e.activity.activityId):i(n("workflow.detail.notDispatched"))}</span>
|
|
503
572
|
</div>
|
|
504
|
-
<div>${e.node?
|
|
573
|
+
<div>${e.node?G(e.node.status):""} ${e.activity?G(e.activity.status):""}</div>
|
|
505
574
|
</header>
|
|
506
575
|
<div class="wf-io-meta">
|
|
507
|
-
${a?`${i(
|
|
576
|
+
${a?`${i(n("workflow.detail.attempt"))} <code>${i(a.attemptId)}</code>`:i(n("workflow.detail.noAttempt"))}
|
|
508
577
|
</div>
|
|
509
578
|
${y}
|
|
510
579
|
</div>
|
|
511
580
|
<div class="wf-io-terminal-slot">${l}</div>
|
|
512
581
|
<div class="wf-io-grid">
|
|
513
|
-
${
|
|
514
|
-
${
|
|
515
|
-
${
|
|
516
|
-
${
|
|
517
|
-
${e.io?.waitPrompt?
|
|
582
|
+
${J(f,n("workflow.detail.authoredInput"),e.io?.input,t)}
|
|
583
|
+
${J(f,n("workflow.detail.resolvedInput"),e.io?.resolvedInput,t)}
|
|
584
|
+
${J(f,n("workflow.detail.output"),e.io?.output,t)}
|
|
585
|
+
${J(f,n("workflow.detail.executionLog"),e.io?.log,t)}
|
|
586
|
+
${e.io?.waitPrompt?J(f,n("workflow.detail.waitPrompt"),e.io.waitPrompt,t):""}
|
|
518
587
|
</div>
|
|
519
|
-
</article>`}function
|
|
588
|
+
</article>`}function ye(e){return e?.attempts[e.attempts.length-1]}function kt(e,t){let o=[];return t.error?o.push(n("workflow.detail.error")):o.push(t.status==="live"?n("workflow.detail.terminalLive"):n("workflow.detail.terminalClosedShort")),e?.status&&o.push(e.status),t.webPort>0&&o.push(`:${t.webPort}`),`<span class="muted">${i(o.join(" \xB7 "))}</span>`}function Rn(e,t){return t.status==="live"&&t.webPort>0&&(e?.status==="pending"||e?.status==="running"||e?.status==="effectAttempting")}function xn(e,t){return e.status==="succeeded"||e.status==="failed"||e.status==="cancelled"||e.status==="timedOut"?!!(t.sessionId||t.startedAt):!1}function Dn(e){return`http://${window.location.hostname||"127.0.0.1"}:${e.webPort}`}function On(e,t,o,r){let s=new URLSearchParams({runId:e,activityId:t,attemptId:o});return r&&s.set("hasPtyLog","1"),`/assets/terminal-replay.html?${s.toString()}`}function ft(e,t,o){return`/api/workflows/runs/${encodeURIComponent(e)}/attempts/${encodeURIComponent(t)}/${encodeURIComponent(o)}/terminal-log/raw?download=1`}function qn(){let e=window.location.hash.match(/^#\/workflows\/([^/?#]+)/);if(!e)return null;try{return decodeURIComponent(e[1])}catch{return null}}function $t(e,t){if(!Bn(e))return"";let o=e.attemptId,r=t.comments.get(o)??"",s=t.resolving.has(o),a=t.statuses.get(o),d=a?.kind==="error"?"hint-warn":"hint-ok";return`<div class="wf-approval-box" data-wf-approval="${i(o)}">
|
|
520
589
|
<label>
|
|
521
|
-
<span>${i(
|
|
522
|
-
<textarea class="wf-approval-comment" data-wf-approval-comment="${i(o)}" rows="2" placeholder="${i(
|
|
590
|
+
<span>${i(n("workflow.detail.approvalComment"))}</span>
|
|
591
|
+
<textarea class="wf-approval-comment" data-wf-approval-comment="${i(o)}" rows="2" placeholder="${i(n("workflow.detail.optionalComment"))}"${s?" disabled":""}>${i(r)}</textarea>
|
|
523
592
|
</label>
|
|
524
593
|
<div class="wf-approval-actions">
|
|
525
|
-
<button type="button" class="primary" data-wf-approval-action="approve" data-wf-attempt-id="${i(o)}"${s?" disabled":""}>${i(
|
|
526
|
-
<button type="button" data-wf-approval-action="reject" data-wf-attempt-id="${i(o)}"${s?" disabled":""}>${i(
|
|
527
|
-
${s?`<span class="muted">${i(
|
|
594
|
+
<button type="button" class="primary" data-wf-approval-action="approve" data-wf-attempt-id="${i(o)}"${s?" disabled":""}>${i(n("workflow.detail.approve"))}</button>
|
|
595
|
+
<button type="button" data-wf-approval-action="reject" data-wf-attempt-id="${i(o)}"${s?" disabled":""}>${i(n("workflow.detail.reject"))}</button>
|
|
596
|
+
${s?`<span class="muted">${i(n("workflow.detail.submitting"))}</span>`:""}
|
|
528
597
|
</div>
|
|
529
|
-
${a?`<div class="${
|
|
530
|
-
</div>`}function
|
|
531
|
-
<summary>${i(
|
|
532
|
-
${
|
|
533
|
-
</details>`}function
|
|
534
|
-
<td>${
|
|
598
|
+
${a?`<div class="${d} wf-approval-status">${i(a.text)}</div>`:""}
|
|
599
|
+
</div>`}function Bn(e){return!!e&&e.status==="waiting"&&e.wait?.waitKind==="human-gate"&&!e.wait.resolution}function Pn(e,t){e.querySelectorAll("textarea[data-wf-approval-comment]").forEach(o=>{let r=o.dataset.wfApprovalComment;r&&t.set(r,o.value)})}function St(e,t){e.querySelectorAll("button[data-wf-resume-action][data-wf-resume-attempt][data-wf-resume-activity]").forEach(o=>{o.dataset.wfResumeBound!=="1"&&(o.dataset.wfResumeBound="1",o.addEventListener("click",()=>{let r=o.dataset.wfResumeAttempt,s=o.dataset.wfResumeActivity,a=o.dataset.wfResumeAction;!r||!s||(a==="start"?t.onStart(r,s):a==="end"&&t.onEnd(r,s))}))})}function Nn(e,t,o,r,s,a){let d=e.querySelector(".wf-terminal-actions");if(!d)return;let f=d.querySelector('button[data-wf-resume-button="1"]'),y=vt(t,o,r,s,a);f?f.outerHTML=y:y&&d.insertAdjacentHTML("beforeend",y);let S=e.querySelector("details.wf-terminal-block");if(S){let b=S.querySelector(".wf-resume-status"),k=yt(t.attemptId,a);b?b.outerHTML=k:k&&d.insertAdjacentHTML("afterend",k)}St(e,a)}function jn(e,t){e.querySelectorAll("textarea[data-wf-approval-comment]").forEach(o=>{let r=o.dataset.wfApprovalComment;r&&o.addEventListener("input",()=>{t.comments.set(r,o.value)})}),e.querySelectorAll("button[data-wf-approval-action][data-wf-attempt-id]").forEach(o=>{o.addEventListener("click",()=>{let r=o.dataset.wfAttemptId,s=o.dataset.wfApprovalAction;!r||s!=="approve"&&s!=="reject"||t.onResolve(r,s)})})}function J(e,t,o,r){let s=qe(e,t);return`<details class="wf-io-block" data-io-key="${i(s)}"${r.has(s)?" open":""}>
|
|
600
|
+
<summary>${i(t)} ${Gn(o)}</summary>
|
|
601
|
+
${zn(o)}
|
|
602
|
+
</details>`}function qe(e,t){return`${e}:${t}`}function Un(e,t){if(!t)return!1;for(let o of e.querySelectorAll("[data-wf-attempt-card]"))if(o.dataset.wfAttemptCard===t)return o.scrollIntoView({block:"center"}),!0;return!1}function Fn(e,t,o){e.querySelectorAll("details.wf-io-block[data-io-key]").forEach(r=>{let s=r.dataset.ioKey;if(!s)return;r.open?t.add(s):t.delete(s);let a=r.querySelector(".wf-io-pre");a&&o.set(s,a.scrollTop)})}function Wn(e,t){e.querySelectorAll("details.wf-io-block[data-io-key]").forEach(o=>{o.dataset.ioToggleBound!=="1"&&(o.dataset.ioToggleBound="1",o.addEventListener("toggle",()=>{let r=o.dataset.ioKey;r&&(o.open?t.add(r):t.delete(r))}))})}function _n(e,t){e.querySelectorAll("details.wf-io-block[data-io-key]").forEach(o=>{let r=o.dataset.ioKey;if(!r)return;let s=t.get(r);if(s===void 0)return;let a=o.querySelector(".wf-io-pre");a&&(a.scrollTop=s)})}function Jn(e,t){e.querySelectorAll("details.wf-io-block[data-io-key]").forEach(o=>{let r=o.dataset.ioKey;if(!r)return;let s=o.querySelector(".wf-io-pre");s&&s.dataset.ioScrollBound!=="1"&&(s.dataset.ioScrollBound="1",s.addEventListener("scroll",()=>{t.set(r,s.scrollTop)}))})}function Gn(e){if(!e)return`<span class="muted">${i(n("workflow.detail.empty"))}</span>`;let t=[];return e.outputBytes!==void 0&&t.push(`${e.outputBytes}B`),e.truncated&&t.push(n("workflow.detail.truncated")),e.error&&t.push(n("workflow.detail.error")),e.outputHash&&t.push($e(e.outputHash)),t.length?`<span class="muted">${i(t.join(" \xB7 "))}</span>`:""}function zn(e){if(!e)return`<div class="muted wf-io-empty">${i(n("workflow.detail.noData"))}</div>`;let t=e.value!==void 0?JSON.stringify(e.value,null,2):e.text??"",o=e.error?`<div class="muted error">${i(e.error)}</div>`:"";return t?`${o}<pre class="wf-io-pre">${i(t)}</pre>`:`${o}<div class="muted wf-io-empty">${i(n("workflow.detail.noPreview"))}</div>`}function Kn(e){let t=[];if(e.effectAttempted&&t.push(`${i(n("workflow.detail.effect"))} ${i(e.effectAttempted.provider)}`),e.wait){let o=e.wait.resolution?`${e.wait.resolution.kind}${e.wait.resolution.resolution?":"+e.wait.resolution.resolution:""}`:n("workflow.detail.open");t.push(`${i(n("workflow.detail.wait"))} ${i(e.wait.waitKind)} ${i(o)}`),e.wait.deadlineAt!==void 0&&t.push(`${i(n("workflow.detail.deadline"))} ${i(ke(e.wait.deadlineAt))}`)}if(e.error){let o=`${e.error.errorCode}${e.error.errorClass?` \xB7 ${e.error.errorClass}`:""}`;t.push(`<span class="muted error">${i(o)}</span>`),e.error.errorMessage&&t.push(`<span class="error wf-error-msg">${i(e.error.errorMessage)}</span>`)}return e.output&&t.push(`${i(n("workflow.detail.output"))} ${i($e(e.output.outputHash))}`),e.runningMs!==void 0&&t.push(`${e.runningMs}ms`),t.length>0?t.join("<br/>"):'<span class="muted">-</span>'}function Vn(e,t){e.innerHTML=t.length>0?t.map(Yn).join(""):`<tr><td colspan="7" class="empty">${i(n("workflow.detail.noEvents"))}</td></tr>`}function Yn(e){let t=Qn(e.payload);return`<tr>
|
|
603
|
+
<td>${le(e.eventId)}</td>
|
|
535
604
|
<td><code>${i(e.type)}</code></td>
|
|
536
605
|
<td>${i(e.actor)}</td>
|
|
537
|
-
<td>${
|
|
538
|
-
<td>${
|
|
539
|
-
<td>${
|
|
540
|
-
<td title="${i(new Date(e.timestamp).toISOString())}">${i(
|
|
541
|
-
</tr>`}function
|
|
606
|
+
<td>${t.nodeId?`<code>${i(t.nodeId)}</code>`:"-"}</td>
|
|
607
|
+
<td>${t.activityId?`<code>${i(t.activityId)}</code>`:"-"}</td>
|
|
608
|
+
<td>${t.errorCode?`<span class="muted error">${i(t.errorCode)}</span>`:"-"}</td>
|
|
609
|
+
<td title="${i(new Date(e.timestamp).toISOString())}">${i(ke(e.timestamp))}</td>
|
|
610
|
+
</tr>`}function le(e){let t=e.lastIndexOf("-");if(t<0)return 0;let o=Number(e.slice(t+1));return Number.isFinite(o)?o:0}function Qn(e){if(!e||typeof e!="object"||"ref"in e)return{};let t=e,o={};typeof t.nodeId=="string"&&(o.nodeId=t.nodeId),typeof t.activityId=="string"&&(o.activityId=t.activityId),typeof t.failedNodeId=="string"&&(o.nodeId=t.failedNodeId);let r=t.error;return r&&typeof r=="object"&&"errorCode"in r&&(o.errorCode=String(r.errorCode)),o}function Xn(e){return!e.payload||typeof e.payload!="object"||"ref"in e.payload?null:e.payload}function pt(e,t,o){return Math.min(o,Math.max(t,e))}function $e(e){return e?e.length>18?e.slice(0,10)+"..."+e.slice(-6):e:"-"}function Zn(e,t){return e.length>t?e.slice(0,t-1)+"\u2026":e}function ke(e){return new Date(e).toLocaleTimeString([],{hour:"2-digit",minute:"2-digit",second:"2-digit"})}function C(e){return e.replace(/[&<>"']/g,t=>({"&":"&","<":"<",">":">",'"':""","'":"'"})[t])}function It(e){return e?e.length>18?e.slice(0,10)+"..."+e.slice(-6):e:"-"}function Tt(e){let t=location.hash.match(/^#\/(?:workflows\/catalog|workflows-catalog)\/([^/?#]+)$/);return t?to(e,decodeURIComponent(t[1])):eo(e)}function eo(e){e.innerHTML=`
|
|
542
611
|
<nav class="wf-subnav">
|
|
543
|
-
<a href="#/workflows" data-i18n="workflow.subnav.runs">${
|
|
544
|
-
<a href="#/workflows/catalog" class="active" data-i18n="workflow.subnav.catalog">${
|
|
612
|
+
<a href="#/workflows" data-i18n="workflow.subnav.runs">${C(n("workflow.subnav.runs"))}</a>
|
|
613
|
+
<a href="#/workflows/catalog" class="active" data-i18n="workflow.subnav.catalog">${C(n("workflow.subnav.catalog"))}</a>
|
|
545
614
|
</nav>
|
|
546
615
|
<section class="catalog-head">
|
|
547
616
|
<div>
|
|
548
|
-
<h2>${
|
|
549
|
-
<p class="muted">${
|
|
617
|
+
<h2>${C(n("catalog.title"))}</h2>
|
|
618
|
+
<p class="muted">${C(n("catalog.subtitle"))}</p>
|
|
550
619
|
</div>
|
|
551
|
-
<button id="catalog-refresh" type="button">${
|
|
620
|
+
<button id="catalog-refresh" type="button">${C(n("catalog.refresh"))}</button>
|
|
552
621
|
</section>
|
|
553
622
|
<form id="catalog-filters" class="filters">
|
|
554
|
-
<input type="search" name="q" placeholder="${
|
|
623
|
+
<input type="search" name="q" placeholder="${C(n("catalog.searchPlaceholder"))}" />
|
|
555
624
|
<span id="catalog-status" class="muted"></span>
|
|
556
625
|
</form>
|
|
557
626
|
<div class="wf-table-scroll">
|
|
558
627
|
<table>
|
|
559
628
|
<thead><tr>
|
|
560
|
-
<th>${
|
|
561
|
-
<th>${
|
|
562
|
-
<th>${
|
|
563
|
-
<th>${
|
|
564
|
-
<th>${
|
|
565
|
-
<th>${
|
|
629
|
+
<th>${C(n("catalog.table.workflow"))}</th>
|
|
630
|
+
<th>${C(n("catalog.table.version"))}</th>
|
|
631
|
+
<th>${C(n("catalog.table.params"))}</th>
|
|
632
|
+
<th>${C(n("catalog.table.nodes"))}</th>
|
|
633
|
+
<th>${C(n("catalog.table.revision"))}</th>
|
|
634
|
+
<th>${C(n("catalog.table.path"))}</th>
|
|
566
635
|
</tr></thead>
|
|
567
636
|
<tbody id="catalog-tbody"></tbody>
|
|
568
637
|
</table>
|
|
569
638
|
</div>
|
|
570
|
-
`;let
|
|
639
|
+
`;let t=e.querySelector("#catalog-tbody"),o=e.querySelector("#catalog-status"),r=e.querySelector("#catalog-filters"),s=e.querySelector("#catalog-refresh"),a=[],d=null,f=!1;function y(){let k=(new FormData(r).get("q")??"").trim().toLowerCase();return k?a.filter(l=>l.workflowId.toLowerCase().includes(k)||l.path.toLowerCase().includes(k)):a}function S(){d?(o.textContent=n("catalog.loadFailed",{error:d}),o.classList.add("error")):(o.textContent=`${a.length}`,o.classList.remove("error"));let k=y();if(k.length===0){t.innerHTML=`<tr><td colspan="6" class="empty">${a.length===0?C(n("catalog.noDefinitions")):C(n("catalog.noFilterMatch"))}</td></tr>`;return}t.innerHTML=k.map(l=>`
|
|
571
640
|
<tr>
|
|
572
|
-
<td><a href="#/workflows/catalog/${encodeURIComponent(l.workflowId)}"><code>${
|
|
641
|
+
<td><a href="#/workflows/catalog/${encodeURIComponent(l.workflowId)}"><code>${C(l.workflowId)}</code></a></td>
|
|
573
642
|
<td>${l.version}</td>
|
|
574
|
-
<td>${
|
|
643
|
+
<td>${C(n("catalog.paramSummary",{required:l.requiredParamCount,total:l.paramCount}))}</td>
|
|
575
644
|
<td>${l.nodeCount}</td>
|
|
576
|
-
<td><code>${
|
|
577
|
-
<td><code>${
|
|
645
|
+
<td><code>${C(It(l.revisionId))}</code></td>
|
|
646
|
+
<td><code>${C(l.path)}</code></td>
|
|
578
647
|
</tr>
|
|
579
|
-
`).join("")}async function
|
|
648
|
+
`).join("")}async function b(){s.disabled=!0,o.textContent=n("catalog.loading");try{let k=await fetch("/api/workflows/definitions");if(!k.ok)throw new Error(`HTTP ${k.status}`);a=(await k.json()).definitions??[],d=null}catch(k){d=k?.message??String(k),a=[]}finally{s.disabled=!1,f||S()}}return r.addEventListener("input",S),s.addEventListener("click",()=>{b()}),b(),()=>{f=!0}}function to(e,t){e.innerHTML=`
|
|
580
649
|
<div class="catalog-detail-head">
|
|
581
|
-
<a class="btn-link" href="#/workflows/catalog">${
|
|
650
|
+
<a class="btn-link" href="#/workflows/catalog">${C(n("catalog.back"))}</a>
|
|
582
651
|
<div>
|
|
583
|
-
<h2><code>${
|
|
584
|
-
<div id="catalog-detail-subtitle" class="muted">${
|
|
652
|
+
<h2><code>${C(t)}</code></h2>
|
|
653
|
+
<div id="catalog-detail-subtitle" class="muted">${C(n("workflow.detail.loading"))}</div>
|
|
585
654
|
</div>
|
|
586
655
|
</div>
|
|
587
656
|
<section id="catalog-error" class="hint-warn" hidden></section>
|
|
588
657
|
<section id="catalog-run-status" class="hint-ok" hidden></section>
|
|
589
658
|
<div id="catalog-detail-body"></div>
|
|
590
|
-
`;let o=e.querySelector("#catalog-detail-subtitle"),r=e.querySelector("#catalog-error"),s=e.querySelector("#catalog-run-status"),a=e.querySelector("#catalog-detail-body"),
|
|
659
|
+
`;let o=e.querySelector("#catalog-detail-subtitle"),r=e.querySelector("#catalog-error"),s=e.querySelector("#catalog-run-status"),a=e.querySelector("#catalog-detail-body"),d=null,f=!1,y=!1;function S(u){r.hidden=!u,r.textContent=u??""}function b(u){s.hidden=!u,s.textContent=u??""}function k(u){let I={};for(let[$,v]of Object.entries(u??{}))"default"in v&&(I[$]=v.default);return I}function l(){if(!d)return;let u=d.definition;o.textContent=`${n("catalog.revision")} ${It(d.revisionId)} \xB7 ${d.path}`;let I=JSON.stringify(k(u.params),null,2);a.innerHTML=`
|
|
591
660
|
<section class="wf-panel">
|
|
592
|
-
<div class="wf-panel-title"><h3>${
|
|
661
|
+
<div class="wf-panel-title"><h3>${C(n("catalog.summary"))}</h3></div>
|
|
593
662
|
<div class="wf-summary-grid">
|
|
594
|
-
<div class="wf-summary-item"><span>${
|
|
595
|
-
<div class="wf-summary-item"><span>${
|
|
596
|
-
<div class="wf-summary-item"><span>${
|
|
597
|
-
<div class="wf-summary-item"><span>${
|
|
663
|
+
<div class="wf-summary-item"><span>${C(n("catalog.table.workflow"))}</span><strong><code>${C(u.workflowId)}</code></strong></div>
|
|
664
|
+
<div class="wf-summary-item"><span>${C(n("catalog.table.version"))}</span><strong>${u.version}</strong></div>
|
|
665
|
+
<div class="wf-summary-item"><span>${C(n("catalog.nodeCount"))}</span><strong>${Object.keys(u.nodes).length}</strong></div>
|
|
666
|
+
<div class="wf-summary-item"><span>${C(n("catalog.path"))}</span><strong><code>${C(d.path)}</code></strong></div>
|
|
598
667
|
</div>
|
|
599
668
|
</section>
|
|
600
669
|
|
|
601
670
|
<section class="wf-panel">
|
|
602
|
-
<div class="wf-panel-title"><h3>${
|
|
671
|
+
<div class="wf-panel-title"><h3>${C(n("catalog.runPanel"))}</h3></div>
|
|
603
672
|
<form id="catalog-run-form" class="catalog-run-form">
|
|
604
673
|
<label>
|
|
605
|
-
<span>${
|
|
606
|
-
<textarea id="catalog-params" rows="8" spellcheck="false" placeholder="${
|
|
674
|
+
<span>${C(n("catalog.paramsJson"))}</span>
|
|
675
|
+
<textarea id="catalog-params" rows="8" spellcheck="false" placeholder="${C(n("catalog.paramsPlaceholder"))}">${C(I)}</textarea>
|
|
607
676
|
</label>
|
|
608
677
|
<div class="catalog-chat-grid">
|
|
609
678
|
<label>
|
|
610
|
-
<span>${
|
|
679
|
+
<span>${C(n("catalog.chatId"))}</span>
|
|
611
680
|
<input id="catalog-chat-id" type="text" autocomplete="off" />
|
|
612
681
|
</label>
|
|
613
682
|
<label>
|
|
614
|
-
<span>${
|
|
683
|
+
<span>${C(n("catalog.larkAppId"))}</span>
|
|
615
684
|
<input id="catalog-lark-app-id" type="text" autocomplete="off" />
|
|
616
685
|
</label>
|
|
617
686
|
</div>
|
|
618
|
-
<div class="muted">${
|
|
687
|
+
<div class="muted">${C(n("catalog.chatBindingHint"))}</div>
|
|
619
688
|
<div id="catalog-param-errors" class="catalog-param-errors" hidden></div>
|
|
620
|
-
<button id="catalog-run-btn" type="submit" class="primary">${
|
|
689
|
+
<button id="catalog-run-btn" type="submit" class="primary">${C(n("catalog.run"))}</button>
|
|
621
690
|
</form>
|
|
622
691
|
</section>
|
|
623
692
|
|
|
624
693
|
<section class="wf-panel">
|
|
625
|
-
<div class="wf-panel-title"><h3>${
|
|
626
|
-
${
|
|
694
|
+
<div class="wf-panel-title"><h3>${C(n("catalog.paramsSchema"))}</h3></div>
|
|
695
|
+
${no(u.params)}
|
|
627
696
|
</section>
|
|
628
697
|
|
|
629
698
|
<section class="wf-panel">
|
|
630
|
-
<div class="wf-panel-title"><h3>${
|
|
631
|
-
<pre class="wf-io-pre">${
|
|
699
|
+
<div class="wf-panel-title"><h3>${C(n("catalog.definitionJson"))}</h3></div>
|
|
700
|
+
<pre class="wf-io-pre">${C(JSON.stringify(u,null,2))}</pre>
|
|
632
701
|
</section>
|
|
633
|
-
`,L()}async function
|
|
702
|
+
`,L()}async function T(){if(!d||y)return;let u=a.querySelector("#catalog-params"),I=a.querySelector("#catalog-chat-id"),$=a.querySelector("#catalog-lark-app-id"),v=a.querySelector("#catalog-run-btn"),M=a.querySelector("#catalog-param-errors"),c;try{if(c=JSON.parse(u.value||"{}"),!c||typeof c!="object"||Array.isArray(c))throw new Error(n("catalog.badParamsJson"))}catch(p){M.hidden=!1,M.innerHTML=`<div class="muted error">${C(p?.message??String(p))}</div>`;return}y=!0,v.disabled=!0,v.textContent=n("catalog.running"),M.hidden=!0,S(null),b(null);try{let p=await fetch(`/api/workflows/definitions/${encodeURIComponent(d.definition.workflowId)}/run`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({params:c,chatBinding:{chatId:I.value.trim(),larkAppId:$.value.trim()}})});if(p.status===401)throw new Error(n("catalog.writeAccess"));let h=await p.json().catch(()=>({}));if(!p.ok||!h.ok)throw h.issues?.length&&(M.hidden=!1,M.innerHTML=`<strong>${C(n("catalog.invalidParams"))}</strong><ul>${h.issues.map(E=>`<li>${C(n("catalog.issue",{path:E.path.length?E.path.join("."):"(root)",message:E.message}))}</li>`).join("")}</ul>`),new Error(h.hint??h.message??h.error??n("catalog.runHttp",{status:p.status}));b(n("catalog.runStarted")),h.runId&&(location.hash=`#/workflows/${encodeURIComponent(h.runId)}`)}catch(p){S(p?.message??String(p))}finally{y=!1,v.disabled=!1,v.textContent=n("catalog.run")}}function L(){a.querySelector("#catalog-run-form")?.addEventListener("submit",I=>{I.preventDefault(),T()})}async function g(){try{let u=await fetch(`/api/workflows/definitions/${encodeURIComponent(t)}`);if(u.status===404)throw new Error("unknown_workflow");if(!u.ok)throw new Error(`HTTP ${u.status}`);d=await u.json(),S(null),l()}catch(u){S(n("catalog.definitionLoadFailed",{error:u?.message??String(u)})),o.textContent=n("workflow.detail.loadFailed")}}return g().then(()=>{}),()=>{f=!0}}function no(e){let t=Object.entries(e??{});return t.length===0?`<div class="muted">${C(n("catalog.noParams"))}</div>`:`<div class="catalog-param-list">${t.map(([o,r])=>`
|
|
634
703
|
<article class="catalog-param">
|
|
635
704
|
<header>
|
|
636
|
-
<code>${
|
|
637
|
-
<span class="wf-status">${
|
|
638
|
-
<span class="muted">${
|
|
705
|
+
<code>${C(o)}</code>
|
|
706
|
+
<span class="wf-status">${C(r.required?n("catalog.required"):n("catalog.optional"))}</span>
|
|
707
|
+
<span class="muted">${C(r.type)}${r.format?` \xB7 ${C(r.format)}`:""}</span>
|
|
639
708
|
</header>
|
|
640
|
-
${r.description?`<div class="muted">${
|
|
641
|
-
${"default"in r?`<pre class="wf-io-pre">${
|
|
709
|
+
${r.description?`<div class="muted">${C(n("catalog.description"))}: ${C(r.description)}</div>`:""}
|
|
710
|
+
${"default"in r?`<pre class="wf-io-pre">${C(`${n("catalog.default")}: ${JSON.stringify(r.default,null,2)}`)}</pre>`:""}
|
|
642
711
|
</article>
|
|
643
|
-
`).join("")}</div>`}var
|
|
644
|
-
<img class="qr-image" src="${e.qrDataUrl}" alt="${
|
|
645
|
-
${e.qrUrl?`<a class="onboarding-link" href="${e.qrUrl}" target="_blank" rel="noopener">${
|
|
646
|
-
</div>`:"",r=e.appId?`<p><b>App ID:</b> <code>${e.appId}</code></p>`:"",s=e.status==="completed"?`<p class="hint-ok">${
|
|
712
|
+
`).join("")}</div>`}var ee=null,Se=null;function Ie(){Se!==null&&(window.clearInterval(Se),Se=null)}function Lt(){return ee||(ee=document.createElement("dialog"),ee.className="onboarding-dialog",document.body.appendChild(ee),ee.addEventListener("close",Ie),ee)}function oo(e){return e.status==="waiting_for_scan"?n("botOnboarding.waiting"):e.status==="verifying"?n("botOnboarding.verifying"):e.status==="completed"?n("botOnboarding.completed"):e.status==="failed"?`${n("botOnboarding.failed")}: ${e.message??e.error??"unknown"}`:n("botOnboarding.starting")}function de(e){let t=Lt(),o=e.qrDataUrl?`<div class="qr-card">
|
|
713
|
+
<img class="qr-image" src="${e.qrDataUrl}" alt="${n("botOnboarding.qrAlt")}">
|
|
714
|
+
${e.qrUrl?`<a class="onboarding-link" href="${e.qrUrl}" target="_blank" rel="noopener">${n("botOnboarding.openLink")}</a>`:""}
|
|
715
|
+
</div>`:"",r=e.appId?`<p><b>App ID:</b> <code>${e.appId}</code></p>`:"",s=e.status==="completed"?`<p class="hint-ok">${n("botOnboarding.restartHint")}</p>`:"";t.innerHTML=`<article>
|
|
647
716
|
<header>
|
|
648
|
-
<h3>${
|
|
649
|
-
<p>${
|
|
717
|
+
<h3>${n("botOnboarding.title")}</h3>
|
|
718
|
+
<p>${n("botOnboarding.intro")}</p>
|
|
650
719
|
</header>
|
|
651
|
-
<p class="onboarding-status status-${e.status}">${
|
|
720
|
+
<p class="onboarding-status status-${e.status}">${oo(e)}</p>
|
|
652
721
|
${o}
|
|
653
722
|
${r}
|
|
654
723
|
${s}
|
|
655
|
-
<form method="dialog"><button>${
|
|
656
|
-
</article>`}async function
|
|
724
|
+
<form method="dialog"><button>${n("botOnboarding.close")}</button></form>
|
|
725
|
+
</article>`}async function so(e){let t=await fetch(`/api/bot-onboarding/${encodeURIComponent(e)}`),o=await t.json();if(!t.ok||!o?.job)throw new Error(o?.error??`http_${t.status}`);de(o.job),(o.job.status==="completed"||o.job.status==="failed")&&Ie()}async function ao(){Ie(),de({id:"",status:"starting"});let e=Lt();e.open||e.showModal();try{let t=await fetch("/api/bot-onboarding/start",{method:"POST"}),o=await t.json();if(!t.ok||!o?.job?.id)throw new Error(o?.error??`http_${t.status}`);de(o.job),Se=window.setInterval(()=>{so(o.job.id).catch(r=>{Ie(),de({id:o.job.id,status:"failed",message:r instanceof Error?r.message:String(r)})})},1200)}catch(t){de({id:"",status:"failed",message:t instanceof Error?t.message:String(t)})}}function Et(){let e=document.getElementById("add-bot-btn");e&&(e.onclick=()=>{ao()})}var Q=document.getElementById("root"),ce=null;function Be(){ce&&(ce(),ce=null);let e=location.hash||"#/";e.startsWith("#/workflows/catalog")||e.startsWith("#/workflows-catalog")?ce=Tt(Q):e.startsWith("#/workflows")?ce=mt(Q):e.startsWith("#/groups")?ot(Q):e.startsWith("#/bot-defaults")?rt(Q):e.startsWith("#/roles")?ct(Q):e.startsWith("#/schedules")?tt(Q):e.startsWith("#/sessions")?Ze(Q):Qe(Q);for(let t of document.querySelectorAll(".sidebar-nav a")){let o=t.getAttribute("href")??"#/";t.classList.toggle("active",o===(e||"#/"))}}var Pe=document.getElementById("status");function Mt(){Pe&&(Pe.textContent=O.online?n("status.live"):n("status.disconnected"),Pe.className="connection-status "+(O.online?"online":"offline"))}O.on(Mt);function Ct(){document.querySelectorAll("[data-i18n]").forEach(e=>{e.textContent=n(e.dataset.i18n??"")}),document.querySelectorAll("[data-locale]").forEach(e=>{e.classList.toggle("active",e.dataset.locale===K.locale)}),document.querySelectorAll("[data-theme-mode]").forEach(e=>{e.classList.toggle("active",e.dataset.themeMode===K.themeMode)}),Mt()}function ro(){document.querySelectorAll("[data-locale]").forEach(e=>{e.onclick=()=>K.setLocale(e.dataset.locale)}),document.querySelectorAll("[data-theme-mode]").forEach(e=>{e.onclick=()=>K.setThemeMode(e.dataset.themeMode)})}(async()=>{K.init(),ro(),Et(),K.on(()=>{Ct(),Be()}),Ct();try{await Je()}catch(e){console.error("botmux dashboard bootstrap failed",e),O.setOnline(!1)}window.addEventListener("hashchange",Be),Be()})();})();
|