botmux 2.40.0 → 2.41.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.
Files changed (99) hide show
  1. package/README.en.md +4 -2
  2. package/README.md +4 -2
  3. package/dist/adapters/cli/coco.d.ts.map +1 -1
  4. package/dist/adapters/cli/coco.js +14 -0
  5. package/dist/adapters/cli/coco.js.map +1 -1
  6. package/dist/adapters/cli/hermes.d.ts +4 -0
  7. package/dist/adapters/cli/hermes.d.ts.map +1 -0
  8. package/dist/adapters/cli/hermes.js +40 -0
  9. package/dist/adapters/cli/hermes.js.map +1 -0
  10. package/dist/adapters/cli/registry.d.ts +2 -1
  11. package/dist/adapters/cli/registry.d.ts.map +1 -1
  12. package/dist/adapters/cli/registry.js +3 -1
  13. package/dist/adapters/cli/registry.js.map +1 -1
  14. package/dist/adapters/cli/shared-hints.d.ts +1 -1
  15. package/dist/adapters/cli/shared-hints.js +1 -1
  16. package/dist/adapters/cli/types.d.ts +6 -2
  17. package/dist/adapters/cli/types.d.ts.map +1 -1
  18. package/dist/bot-registry.d.ts +22 -0
  19. package/dist/bot-registry.d.ts.map +1 -1
  20. package/dist/bot-registry.js +11 -0
  21. package/dist/bot-registry.js.map +1 -1
  22. package/dist/cli.js +2 -2
  23. package/dist/cli.js.map +1 -1
  24. package/dist/core/command-handler.d.ts.map +1 -1
  25. package/dist/core/command-handler.js +19 -2
  26. package/dist/core/command-handler.js.map +1 -1
  27. package/dist/core/dashboard-ipc-server.d.ts.map +1 -1
  28. package/dist/core/dashboard-ipc-server.js +28 -0
  29. package/dist/core/dashboard-ipc-server.js.map +1 -1
  30. package/dist/core/session-discovery.d.ts.map +1 -1
  31. package/dist/core/session-discovery.js +2 -1
  32. package/dist/core/session-discovery.js.map +1 -1
  33. package/dist/core/types.d.ts +5 -0
  34. package/dist/core/types.d.ts.map +1 -1
  35. package/dist/core/types.js.map +1 -1
  36. package/dist/core/worker-pool.d.ts +14 -0
  37. package/dist/core/worker-pool.d.ts.map +1 -1
  38. package/dist/core/worker-pool.js +105 -8
  39. package/dist/core/worker-pool.js.map +1 -1
  40. package/dist/daemon.d.ts.map +1 -1
  41. package/dist/daemon.js +2 -2
  42. package/dist/daemon.js.map +1 -1
  43. package/dist/dashboard/web/bot-defaults.d.ts.map +1 -1
  44. package/dist/dashboard/web/bot-defaults.js +125 -34
  45. package/dist/dashboard/web/bot-defaults.js.map +1 -1
  46. package/dist/dashboard/web/i18n.d.ts.map +1 -1
  47. package/dist/dashboard/web/i18n.js +23 -5
  48. package/dist/dashboard/web/i18n.js.map +1 -1
  49. package/dist/dashboard/web/sessions.d.ts.map +1 -1
  50. package/dist/dashboard/web/sessions.js +1 -0
  51. package/dist/dashboard/web/sessions.js.map +1 -1
  52. package/dist/dashboard/web/workflows.js +2 -2
  53. package/dist/dashboard/web/workflows.js.map +1 -1
  54. package/dist/dashboard-web/app.js +258 -237
  55. package/dist/dashboard-web/index.html +1 -1
  56. package/dist/dashboard-web/style.css +16 -0
  57. package/dist/dashboard.js +20 -0
  58. package/dist/dashboard.js.map +1 -1
  59. package/dist/i18n/en.d.ts.map +1 -1
  60. package/dist/i18n/en.js +7 -2
  61. package/dist/i18n/en.js.map +1 -1
  62. package/dist/i18n/zh.d.ts.map +1 -1
  63. package/dist/i18n/zh.js +7 -2
  64. package/dist/i18n/zh.js.map +1 -1
  65. package/dist/im/lark/card-builder.d.ts +1 -1
  66. package/dist/im/lark/card-builder.d.ts.map +1 -1
  67. package/dist/im/lark/card-builder.js +22 -8
  68. package/dist/im/lark/card-builder.js.map +1 -1
  69. package/dist/im/lark/card-handler.d.ts.map +1 -1
  70. package/dist/im/lark/card-handler.js +22 -14
  71. package/dist/im/lark/card-handler.js.map +1 -1
  72. package/dist/im/lark/event-dispatcher.d.ts.map +1 -1
  73. package/dist/im/lark/event-dispatcher.js +27 -3
  74. package/dist/im/lark/event-dispatcher.js.map +1 -1
  75. package/dist/im/lark/grant-command.d.ts +2 -1
  76. package/dist/im/lark/grant-command.d.ts.map +1 -1
  77. package/dist/im/lark/grant-command.js +3 -2
  78. package/dist/im/lark/grant-command.js.map +1 -1
  79. package/dist/services/card-prefs-store.d.ts +19 -0
  80. package/dist/services/card-prefs-store.d.ts.map +1 -0
  81. package/dist/services/card-prefs-store.js +73 -0
  82. package/dist/services/card-prefs-store.js.map +1 -0
  83. package/dist/services/codex-bridge-queue.d.ts.map +1 -1
  84. package/dist/services/codex-bridge-queue.js +19 -0
  85. package/dist/services/codex-bridge-queue.js.map +1 -1
  86. package/dist/services/grant-store.d.ts +12 -2
  87. package/dist/services/grant-store.d.ts.map +1 -1
  88. package/dist/services/grant-store.js +51 -4
  89. package/dist/services/grant-store.js.map +1 -1
  90. package/dist/setup/bot-config-editor.d.ts +1 -1
  91. package/dist/setup/bot-config-editor.d.ts.map +1 -1
  92. package/dist/setup/bot-config-editor.js +3 -2
  93. package/dist/setup/bot-config-editor.js.map +1 -1
  94. package/dist/worker.js +47 -17
  95. package/dist/worker.js.map +1 -1
  96. package/dist/workflows/attempt-resume.d.ts.map +1 -1
  97. package/dist/workflows/attempt-resume.js +1 -1
  98. package/dist/workflows/attempt-resume.js.map +1 -1
  99. package/package.json +1 -1
@@ -1,27 +1,27 @@
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()}},B=new Ce;async function Je(){let[e,t]=await Promise.all([fetch("/api/sessions").then(a=>a.json()),fetch("/api/schedules").then(a=>a.json())]);B.upsertSessions(e.sessions??[]),B.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 a of r)o.addEventListener(a,s=>{try{let d=JSON.parse(s.data);B.applySse(a,d.body??d)}catch{}});o.onerror=()=>B.setOnline(!1),o.onopen=()=>B.setOnline(!0)}var Me="botmux.dashboard.locale",qt={"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","botDefaults.brandLabel":"\u4E2A\u6027\u7B7E\u540D\uFF08\u5361\u7247\u9875\u811A\uFF09","botDefaults.brandLabelHelp":"\u8BE5 bot \u53D1\u51FA\u7684\u5361\u7247\u9875\u811A\u7B7E\u540D\u3002\u7559\u7A7A\u4FDD\u5B58\uFF1D\u4E0D\u663E\u793A\uFF1B\u586B\u5199\uFF1D\u81EA\u5B9A\u4E49\uFF08\u652F\u6301 markdown\uFF0C\u5982 [Acme](https://\u2026)\uFF09\uFF1B\u6062\u590D\u9ED8\u8BA4\uFF1D\u663E\u793A botmux\u3002","botDefaults.brandLabelPlaceholder":"\u9ED8\u8BA4\uFF1Abotmux\uFF08\u7559\u7A7A\u5219\u4E0D\u663E\u793A\uFF09","botDefaults.brandSave":"\u4FDD\u5B58\u7B7E\u540D","botDefaults.brandReset":"\u6062\u590D\u9ED8\u8BA4","botDefaults.brandStateDefault":"\u5F53\u524D\uFF1A\u9ED8\u8BA4 botmux","botDefaults.brandStateOff":"\u5F53\u524D\uFF1A\u5DF2\u5173\u95ED","botDefaults.brandStateCustom":"\u5F53\u524D\uFF1A\u81EA\u5B9A\u4E49","nav.roles":"\u89D2\u8272\u7BA1\u7406","roles.title":"\u89D2\u8272\u7BA1\u7406","roles.subtitle":"\u4E3A\u6BCF\u4E2A\u7FA4\u7EC4\u7684\u6BCF\u4E2A Bot \u5355\u72EC\u8BBE\u7F6E\u89D2\u8272\u63D0\u793A\u8BCD\uFF0CBot \u5728\u8BE5\u7FA4\u4E2D\u4F1A\u4EE5\u6B64\u89D2\u8272\u884C\u4E8B\u3002","roles.search":"\u641C\u7D22\u7FA4\u540D/Bot/ID","roles.refresh":"\u5237\u65B0","roles.selectHint":"\u2190 \u5C55\u5F00\u7FA4\u7EC4\uFF0C\u9009\u62E9\u4E00\u4E2A Bot \u6765\u7F16\u8F91\u89D2\u8272","roles.editorPlaceholder":`\u8F93\u5165\u89D2\u8272\u63CF\u8FF0\uFF0C\u4F8B\u5982\uFF1A
1
+ "use strict";(()=>{var Me=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()}},q=new Me;async function Je(){let[e,t]=await Promise.all([fetch("/api/sessions").then(a=>a.json()),fetch("/api/schedules").then(a=>a.json())]);q.upsertSessions(e.sessions??[]),q.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 a of r)o.addEventListener(a,s=>{try{let d=JSON.parse(s.data);q.applySse(a,d.body??d)}catch{}});o.onerror=()=>q.setOnline(!1),o.onopen=()=>q.setOnline(!0)}var Ae="botmux.dashboard.locale",qt={"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":"Bot \u914D\u7F6E","status.live":"\u5B9E\u65F6\u8FDE\u63A5","status.disconnected":"\u8FDE\u63A5\u65AD\u5F00","status.system":"\u7CFB\u7EDF","status.light":"\u6D45\u8272","status.dark":"\u6697\u9ED1","status.language":"\u8BED\u8A00","status.theme":"\u4E3B\u9898","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\u914D\u7F6E","botDefaults.subtitle":"\u7BA1\u7406\u6BCF\u4E2A bot \u7684\u9ED8\u8BA4\u884C\u4E3A\uFF1A\u65B0\u7FA4 oncall \u81EA\u52A8\u7ED1\u5B9A\u3001\u5361\u7247\u7B7E\u540D\u7B49\u3002","botDefaults.search":"\u641C\u7D22 bot \u540D / app id","botDefaults.refresh":"\u5237\u65B0","botDefaults.sectionOncall":"\u65B0\u7FA4 Oncall","botDefaults.sectionBrand":"\u5361\u7247\u7B7E\u540D","botDefaults.warning":"\u5F00\u542F\u540E\uFF0C\u6CA1\u6709 oncall binding \u7684\u7FA4\u4F1A\u5728\u4E0B\u6B21\u5F00\u65B0\u8BDD\u9898\u65F6\u81EA\u52A8\u7ED1\u5B9A\u5230\u8BE5\u76EE\u5F55\uFF1B\u624B\u52A8\u7ED1\u5B9A\u6216\u624B\u52A8\u89E3\u7ED1\u8FC7\u7684\u7FA4\u4E0D\u4F1A\u88AB\u8986\u76D6\u3002","botDefaults.empty":"\u6CA1\u6709\u5728\u7EBF bot\u3002\u5148 botmux restart \u8BA9 daemon \u4E0A\u7EBF\u3002","botDefaults.defaultOncall":"\u9ED8\u8BA4\u8FDB\u5165 oncall \u6A21\u5F0F","botDefaults.defaultOncallHelp":"\u6240\u6709\u672A\u7ED1\u5B9A\u7684\u7FA4\u4E0B\u6B21\u5F00\u8BDD\u9898\u81EA\u52A8\u7ED1\u5B9A","botDefaults.workingDir":"\u9ED8\u8BA4\u5DE5\u4F5C\u76EE\u5F55","botDefaults.lastEnabled":"\u4E0A\u6B21\u542F\u7528\u65F6\u95F4","botDefaults.autobound":"\u5DF2\u81EA\u52A8\u7ED1\u5B9A {count} \u4E2A\u7FA4","botDefaults.save":"\u4FDD\u5B58","botDefaults.required":"\u5F00\u542F\u65F6\u5FC5\u987B\u586B\u5DE5\u4F5C\u76EE\u5F55","botDefaults.brandLabel":"\u4E2A\u6027\u7B7E\u540D\uFF08\u5361\u7247\u9875\u811A\uFF09","botDefaults.brandLabelHelp":"\u8BE5 bot \u53D1\u51FA\u7684\u5361\u7247\u9875\u811A\u7B7E\u540D\u3002\u7559\u7A7A\u4FDD\u5B58\uFF1D\u4E0D\u663E\u793A\uFF1B\u586B\u5199\uFF1D\u81EA\u5B9A\u4E49\uFF08\u652F\u6301 markdown\uFF0C\u5982 [Acme](https://\u2026)\uFF09\uFF1B\u6062\u590D\u9ED8\u8BA4\uFF1D\u663E\u793A botmux\u3002","botDefaults.brandLabelPlaceholder":"\u9ED8\u8BA4\uFF1Abotmux\uFF08\u7559\u7A7A\u5219\u4E0D\u663E\u793A\uFF09","botDefaults.brandSave":"\u4FDD\u5B58\u7B7E\u540D","botDefaults.brandReset":"\u6062\u590D\u9ED8\u8BA4","botDefaults.brandStateDefault":"\u5F53\u524D\uFF1A\u9ED8\u8BA4 botmux","botDefaults.brandStateOff":"\u5F53\u524D\uFF1A\u5DF2\u5173\u95ED","botDefaults.brandStateCustom":"\u5F53\u524D\uFF1A\u81EA\u5B9A\u4E49","botDefaults.sectionCard":"\u5361\u7247\u884C\u4E3A","botDefaults.disableStreaming":"\u5173\u95ED\u98DE\u4E66\u6D41\u5F0F\u5361\u7247","botDefaults.disableStreamingHelp":"\u4E0D\u518D\u53D1\u5B9E\u65F6\u5237\u65B0\u7684\u4F1A\u8BDD\u72B6\u6001\u5361\uFF08\u542B\u300C\u6253\u5F00\u7EC8\u7AEF\u300D\u5165\u53E3\uFF09\uFF1B\u4EFB\u52A1\u6700\u7EC8\u7ED3\u679C\u4ECD\u901A\u8FC7\u6D88\u606F\u9001\u8FBE\u3002\u9002\u5408\u5ACC\u6D41\u5F0F\u5361\u7247\u70E6\u7684\u573A\u666F\u3002","botDefaults.writableLink":"\u5361\u7247\u4E0A\u76F4\u63A5\u7ED9\u53EF\u64CD\u4F5C\uFF08\u53EF\u5199\uFF09\u7EC8\u7AEF\u94FE\u63A5","botDefaults.writableLinkHelp":"\u26A0\uFE0F \u5728\u6D41\u5F0F\u5361\u7247\u6B63\u6587\u76F4\u63A5\u8D34\u51FA\u53EF\u5199\u7EC8\u7AEF\u94FE\u63A5\uFF0C\u7FA4\u5185\u4EFB\u4F55\u4EBA\u90FD\u80FD\u70B9\u5F00\u5E76\u64CD\u63A7\u7EC8\u7AEF\u3002\u4E0D\u52FE\uFF1D\u4FDD\u6301\u73B0\u72B6\uFF08\u8D70\u300C\u83B7\u53D6\u64CD\u4F5C\u94FE\u63A5\u300D\u6309\u94AE\u79C1\u804A\u53D1\u7ED9\u70B9\u51FB\u8005\uFF09\u3002","botDefaults.writableLinkMoot":"\u5DF2\u5173\u95ED\u6D41\u5F0F\u5361\u7247\uFF0C\u672C\u9879\u4E0D\u751F\u6548","botDefaults.cardPrefSaved":"\u5DF2\u4FDD\u5B58","nav.roles":"\u89D2\u8272\u7BA1\u7406","roles.title":"\u89D2\u8272\u7BA1\u7406","roles.subtitle":"\u4E3A\u6BCF\u4E2A\u7FA4\u7EC4\u7684\u6BCF\u4E2A Bot \u5355\u72EC\u8BBE\u7F6E\u89D2\u8272\u63D0\u793A\u8BCD\uFF0CBot \u5728\u8BE5\u7FA4\u4E2D\u4F1A\u4EE5\u6B64\u89D2\u8272\u884C\u4E8B\u3002","roles.search":"\u641C\u7D22\u7FA4\u540D/Bot/ID","roles.refresh":"\u5237\u65B0","roles.selectHint":"\u2190 \u5C55\u5F00\u7FA4\u7EC4\uFF0C\u9009\u62E9\u4E00\u4E2A Bot \u6765\u7F16\u8F91\u89D2\u8272","roles.editorPlaceholder":`\u8F93\u5165\u89D2\u8272\u63CF\u8FF0\uFF0C\u4F8B\u5982\uFF1A
2
2
  \u4F60\u662F\u672C\u7FA4\u7684\u6280\u672F\u987E\u95EE\uFF0C\u8D1F\u8D23\u56DE\u7B54\u6240\u6709\u6280\u672F\u95EE\u9898...`,"roles.configured":"\u5DF2\u914D\u7F6E","roles.unconfigured":"\u672A\u914D\u7F6E","roles.noChats":"\u6682\u65E0\u7FA4\u7EC4","roles.preview":"\u9884\u89C8","roles.previewEmpty":"\uFF08\u7A7A\u5185\u5BB9\uFF09","roles.saved":"\u5DF2\u4FDD\u5B58","roles.delete":"\u5220\u9664","roles.save":"\u4FDD\u5B58","roles.confirmDelete":"\u786E\u8BA4\u5220\u9664\u8BE5 Bot \u5728\u6B64\u7FA4\u7684\u89D2\u8272\u914D\u7F6E\uFF1F","roles.botsWithRoles":"\u4E2A Bot \u5DF2\u914D\u7F6E\u89D2\u8272","roles.emptyError":"\u89D2\u8272\u5185\u5BB9\u4E0D\u80FD\u4E3A\u7A7A\uFF0C\u8BF7\u5148\u8F93\u5165\u5185\u5BB9","roles.saveFailed":"\u4FDD\u5B58\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5","common.none":"\u65E0","common.unknown":"\u672A\u77E5","common.now":"\u521A\u521A","common.never":"\u4ECE\u672A","nav.workflows":"\u5DE5\u4F5C\u6D41(beta)","nav.workflowCatalog":"\u76EE\u5F55","workflow.subnav.runs":"\u8FD0\u884C","workflow.subnav.catalog":"\u76EE\u5F55","workflow.searchPlaceholder":"\u641C\u7D22 runId / workflowId / chatId","workflow.filter.nonTerminal":"\u975E\u7EC8\u6001","workflow.filter.all":"\u5168\u90E8","workflow.status.pending":"\u5F85\u5F00\u59CB","workflow.status.running":"\u8FD0\u884C\u4E2D","workflow.status.waiting":"\u7B49\u5F85\u4E2D","workflow.status.effectAttempting":"\u526F\u4F5C\u7528\u4E2D","workflow.status.timedOut":"\u5DF2\u8D85\u65F6","workflow.status.succeeded":"\u6210\u529F","workflow.status.failed":"\u5931\u8D25","workflow.status.cancelled":"\u5DF2\u53D6\u6D88","workflow.table.run":"\u8FD0\u884C","workflow.table.workflow":"\u5DE5\u4F5C\u6D41","workflow.table.status":"\u72B6\u6001","workflow.table.lastSeq":"\u6700\u540E\u5E8F\u53F7","workflow.table.dangling":"\u60AC\u6302 dEf/dAct/dWait","workflow.table.updated":"\u66F4\u65B0\u65F6\u95F4","workflow.table.chatApp":"\u7FA4\u804A / \u5E94\u7528","workflow.list.failedLoad":"\u52A0\u8F7D\u5931\u8D25\uFF1A{error}","workflow.list.noRuns":"\u6CA1\u6709\u5339\u914D\u7684\u8FD0\u884C\u3002","workflow.list.noFilterMatch":"\u6CA1\u6709\u7B26\u5408\u7B5B\u9009\u6761\u4EF6\u7684\u8FD0\u884C\u3002","workflow.list.loaded":"{count} \u4E2A\u8FD0\u884C \xB7 \u5237\u65B0\u4E8E {time}","workflow.list.error":"\u9519\u8BEF\uFF1A{error}","workflow.detail.back":"\u8FD4\u56DE","workflow.detail.loading":"\u52A0\u8F7D\u4E2D...","workflow.detail.loadFailed":"\u52A0\u8F7D\u5931\u8D25","workflow.detail.cancel":"\u53D6\u6D88","workflow.detail.cliCancelOnly":"\u4EC5 CLI \u53EF\u53D6\u6D88","workflow.detail.cancelTitle":"\u53D6\u6D88\u8FD9\u4E2A\u5DE5\u4F5C\u6D41\u8FD0\u884C","workflow.detail.cliCancelTitle":"\u65E0\u6CD5\u5728\u9875\u9762\u53D6\u6D88\uFF1A\u8BF7\u4F7F\u7528 botmux workflow cancel {runId}","workflow.detail.nodes":"\u8282\u70B9 / Activity","workflow.detail.parallel":"\u5E76\u53D1\u6267\u884C","workflow.detail.parallelMeta":"{count} \u6B21\u5C1D\u8BD5 \xB7 \u6700\u9AD8\u5E76\u53D1 {max} \xB7 \u8FD0\u884C\u4E2D {running}","workflow.detail.noParallelData":"\u8FD8\u6CA1\u6709 attempt \u65F6\u95F4\u6570\u636E\u3002","workflow.detail.parallelNow":"\u73B0\u5728","workflow.detail.node":"\u8282\u70B9","workflow.detail.nodeStatus":"\u8282\u70B9\u72B6\u6001","workflow.detail.activity":"Activity","workflow.detail.activityStatus":"Activity \u72B6\u6001","workflow.detail.attempts":"\u5C1D\u8BD5\u6B21\u6570","workflow.detail.current":"\u5F53\u524D\u5C1D\u8BD5","workflow.detail.detail":"\u8BE6\u60C5","workflow.detail.nodeIO":"\u8282\u70B9\u8F93\u5165\u8F93\u51FA","workflow.detail.timeline":"\u65F6\u95F4\u7EBF","workflow.detail.loadOlder":"\u52A0\u8F7D\u66F4\u65E9\u4E8B\u4EF6","workflow.detail.seq":"\u5E8F\u53F7","workflow.detail.actor":"\u6267\u884C\u8005","workflow.detail.error":"\u9519\u8BEF","workflow.detail.event":"\u4E8B\u4EF6","workflow.detail.time":"\u65F6\u95F4","workflow.detail.refreshed":"\u5237\u65B0\u4E8E {time}","workflow.detail.unknownRun":"\u672A\u77E5\u8FD0\u884C","workflow.detail.snapshotHttp":"snapshot HTTP {status}","workflow.detail.eventsHttp":"events HTTP {status}","workflow.detail.cancelUnavailable":"\u65E0\u6CD5\u53D6\u6D88\uFF1A\u8BF7\u4F7F\u7528 botmux workflow cancel {runId}","workflow.detail.cancelConfirm":`\u786E\u8BA4\u53D6\u6D88\u5DE5\u4F5C\u6D41\u8FD0\u884C {runId}\uFF1F
3
3
 
4
4
  {total} \u4E2A\u60AC\u6302\u9879\u4F1A\u7531 cancel recovery \u5904\u7406\u3002
5
5
  effects={effects}, activities={activities}, waits={waits}, cancels={cancels}`,"workflow.detail.writeAccessCancel":"\u9700\u8981\u5199\u6743\u9650\uFF1A\u8BF7\u5728\u7EC8\u7AEF\u8FD0\u884C `botmux dashboard` \u83B7\u53D6\u4E00\u6B21\u6027 URL\uFF0C\u6253\u5F00\u540E\u5199\u5165 cookie\uFF0C\u518D\u56DE\u6765\u70B9\u51FB\u53D6\u6D88\u3002","workflow.detail.cancelHttp":"cancel HTTP {status}","workflow.detail.cancelPending":"\u53D6\u6D88\u5DF2\u63D0\u4EA4\uFF1B\u7B49\u5F85\u8FD0\u884C\u4E2D\u7684 activity \u6536\u655B","workflow.detail.writeAccessApproval":"\u9700\u8981\u5199\u6743\u9650\uFF1A\u8BF7\u5728\u7EC8\u7AEF\u8FD0\u884C `botmux dashboard` \u83B7\u53D6\u4E00\u6B21\u6027 URL\uFF0C\u6253\u5F00\u540E\u5199\u5165 cookie\uFF0C\u518D\u56DE\u6765\u5BA1\u6279\u3002","workflow.detail.actionHttp":"{action} HTTP {status}","workflow.detail.approved":"\u5DF2\u901A\u8FC7","workflow.detail.rejected":"\u5DF2\u62D2\u7EDD","workflow.detail.alreadyTerminal":"\u8FD0\u884C\u5DF2\u7EC8\u6001\uFF1B\u672A\u5E94\u7528\u201C{label}\u201D\u3002","workflow.detail.workflowContinue":"{label}\uFF1B\u7B49\u5F85\u5DE5\u4F5C\u6D41\u7EE7\u7EED\u6267\u884C\u3002","workflow.detail.workflowRefreshing":"{label}\uFF1B\u6B63\u5728\u5237\u65B0\u5DE5\u4F5C\u6D41\u72B6\u6001\u3002","workflow.detail.eventsLoaded":"\u5DF2\u52A0\u8F7D {loaded}/{total} \u4E2A\u4E8B\u4EF6","workflow.detail.dangling":"\u60AC\u6302\u9879","workflow.detail.noDangling":"\u6CA1\u6709\u60AC\u6302\u5DE5\u4F5C\u3002","workflow.detail.none":"\u65E0","workflow.detail.noNodes":"\u8FD8\u6CA1\u6709\u8282\u70B9\u3002","workflow.detail.idle":"\u7A7A\u95F2","workflow.detail.noNodeIO":"\u8FD8\u6CA1\u6709\u8282\u70B9\u8F93\u5165\u8F93\u51FA\u3002","workflow.detail.notDispatched":"\u5C1A\u672A\u6D3E\u53D1","workflow.detail.noAttempt":"\u8FD8\u6CA1\u6709\u5C1D\u8BD5","workflow.detail.attempt":"\u5C1D\u8BD5","workflow.detail.authoredInput":"\u539F\u59CB\u8F93\u5165","workflow.detail.resolvedInput":"\u89E3\u6790\u540E\u8F93\u5165","workflow.detail.output":"\u8F93\u51FA","workflow.detail.executionLog":"\u6267\u884C\u65E5\u5FD7","workflow.detail.liveTerminal":"\u5B9E\u65F6\u7EC8\u7AEF","workflow.detail.terminalLive":"\u5728\u7EBF","workflow.detail.terminalClosedShort":"\u5DF2\u5173\u95ED","workflow.detail.terminalClosed":"\u7EC8\u7AEF\u5DF2\u5173\u95ED\u3002\u8BF7\u67E5\u770B\u4E0B\u65B9\u6267\u884C\u65E5\u5FD7\u83B7\u53D6\u6700\u7EC8\u8BB0\u5F55\u3002","workflow.detail.openTerminalNewTab":"\u5728\u65B0\u6807\u7B7E\u9875\u6253\u5F00\u7EC8\u7AEF","workflow.detail.terminalReplay":"\u7EC8\u7AEF\u56DE\u653E","workflow.detail.openReplayNewTab":"\u5728\u65B0\u6807\u7B7E\u9875\u6253\u5F00\u56DE\u653E","workflow.detail.downloadFullLog":"\u4E0B\u8F7D\u5B8C\u6574\u65E5\u5FD7","workflow.detail.terminalResume":"\u8C03\u8BD5\u4F1A\u8BDD","workflow.detail.openResumeNewTab":"\u5728\u65B0\u6807\u7B7E\u9875\u6253\u5F00\u8C03\u8BD5\u4F1A\u8BDD","workflow.detail.resumeSession":"\u7EE7\u7EED\u4F1A\u8BDD","workflow.detail.resumeStarting":"\u6B63\u5728\u542F\u52A8\u2026","workflow.detail.endResumeSession":"\u7ED3\u675F\u8C03\u8BD5\u4F1A\u8BDD","workflow.detail.resumeEnding":"\u7ED3\u675F\u4E2D\u2026","workflow.detail.resumeUnsupportedCli":"{cliId} CLI \u4E0D\u652F\u6301 resume","workflow.detail.resumeMissingCliSession":"\u7F3A\u5C11 cliSessionId\uFF0C\u65E0\u6CD5 resume","workflow.detail.resumeStartFailed":"\u542F\u52A8\u8C03\u8BD5\u4F1A\u8BDD\u5931\u8D25 (HTTP {status})","workflow.detail.resumeEndFailed":"\u7ED3\u675F\u8C03\u8BD5\u4F1A\u8BDD\u5931\u8D25 (HTTP {status})","workflow.detail.writeAccessResume":"\u9700\u8981\u5199\u5165\u6743\u9650\u624D\u80FD resume \u4F1A\u8BDD\u3002","workflow.detail.waitPrompt":"\u7B49\u5F85\u63D0\u793A","workflow.detail.approvalComment":"\u5BA1\u6279\u5907\u6CE8","workflow.detail.optionalComment":"\u53EF\u9009\u5907\u6CE8","workflow.detail.approve":"\u901A\u8FC7","workflow.detail.reject":"\u62D2\u7EDD","workflow.detail.submitting":"\u63D0\u4EA4\u4E2D...","workflow.detail.empty":"\u7A7A","workflow.detail.truncated":"\u5DF2\u622A\u65AD","workflow.detail.noData":"\u6CA1\u6709\u6570\u636E\u3002","workflow.detail.noPreview":"\u6CA1\u6709\u9884\u89C8\u3002","workflow.detail.open":"\u6253\u5F00","workflow.detail.deadline":"\u622A\u6B62","workflow.detail.effect":"\u526F\u4F5C\u7528","workflow.detail.wait":"\u7B49\u5F85","workflow.detail.noEvents":"\u8FD8\u6CA1\u6709\u4E8B\u4EF6\u3002","workflow.summary.workflow":"\u5DE5\u4F5C\u6D41","workflow.summary.status":"\u72B6\u6001","workflow.summary.lastSeq":"\u6700\u540E\u5E8F\u53F7","workflow.summary.updated":"\u66F4\u65B0\u65F6\u95F4","workflow.summary.revision":"\u4FEE\u8BA2","workflow.summary.initiator":"\u53D1\u8D77\u4EBA","workflow.summary.failedNode":"\u5931\u8D25\u8282\u70B9","workflow.summary.cancelOrigin":"\u53D6\u6D88\u6765\u6E90","workflow.summary.chat":"\u7FA4\u804A","workflow.summary.app":"\u5E94\u7528","workflow.dangling.activities":"Activities","workflow.dangling.effects":"Effects","workflow.dangling.waits":"Waits","workflow.dangling.cancels":"Cancels","catalog.title":"\u5DE5\u4F5C\u6D41\u76EE\u5F55","catalog.subtitle":"\u4ECE\u5DF2\u4FDD\u5B58\u7684 workflow \u5B9A\u4E49\u521B\u5EFA\u4E00\u6B21\u8FD0\u884C\u3002","catalog.searchPlaceholder":"\u641C\u7D22 workflowId / \u8DEF\u5F84","catalog.refresh":"\u5237\u65B0","catalog.loading":"\u6B63\u5728\u52A0\u8F7D\u76EE\u5F55...","catalog.loadFailed":"\u76EE\u5F55\u52A0\u8F7D\u5931\u8D25\uFF1A{error}","catalog.noDefinitions":"\u6CA1\u6709\u627E\u5230 workflow \u5B9A\u4E49\u3002","catalog.noFilterMatch":"\u6CA1\u6709\u7B26\u5408\u7B5B\u9009\u6761\u4EF6\u7684\u5B9A\u4E49\u3002","catalog.table.workflow":"\u5DE5\u4F5C\u6D41","catalog.table.version":"\u7248\u672C","catalog.table.params":"\u53C2\u6570","catalog.table.nodes":"\u8282\u70B9","catalog.table.revision":"\u4FEE\u8BA2","catalog.table.path":"\u8DEF\u5F84","catalog.paramSummary":"{required}/{total} \u5FC5\u586B","catalog.back":"\u8FD4\u56DE\u76EE\u5F55","catalog.detailTitle":"\u5DE5\u4F5C\u6D41\u5B9A\u4E49","catalog.definitionLoadFailed":"\u5B9A\u4E49\u52A0\u8F7D\u5931\u8D25\uFF1A{error}","catalog.summary":"\u6458\u8981","catalog.paramsSchema":"\u53C2\u6570 Schema","catalog.definitionJson":"\u5B9A\u4E49 JSON","catalog.runPanel":"\u8FD0\u884C\u5DE5\u4F5C\u6D41","catalog.paramsJson":"\u53C2\u6570 JSON","catalog.paramsPlaceholder":`{
6
6
  "city": "\u5317\u4EAC"
7
- }`,"catalog.chatId":"\u7FA4\u804A ID","catalog.larkAppId":"\u98DE\u4E66\u5E94\u7528 ID","catalog.chatBindingHint":"\u5FC5\u586B\uFF0C\u7528\u4E8E\u786E\u5B9A humanGate \u5361\u7247\u53D1\u9001\u5230\u54EA\u4E2A\u98DE\u4E66\u7FA4\uFF0C\u4EE5\u53CA\u53D6\u6D88\u8DEF\u7531\u5F52\u5C5E\u3002","catalog.run":"\u8FD0\u884C","catalog.running":"\u542F\u52A8\u4E2D...","catalog.badParamsJson":"\u53C2\u6570\u5FC5\u987B\u662F JSON object\u3002","catalog.writeAccess":"\u9700\u8981\u5199\u6743\u9650\uFF1A\u8BF7\u5728\u7EC8\u7AEF\u8FD0\u884C `botmux dashboard` \u83B7\u53D6\u4E00\u6B21\u6027 URL\uFF0C\u6253\u5F00\u540E\u5199\u5165 cookie\uFF0C\u518D\u56DE\u6765\u8FD0\u884C\u3002","catalog.runHttp":"run HTTP {status}","catalog.runStarted":"\u8FD0\u884C\u5DF2\u542F\u52A8\uFF1B\u6B63\u5728\u6253\u5F00\u8BE6\u60C5\u9875...","catalog.invalidParams":"\u53C2\u6570\u65E0\u6548","catalog.issue":"{path}: {message}","catalog.noParams":"\u6CA1\u6709\u58F0\u660E\u53C2\u6570\u3002","catalog.required":"\u5FC5\u586B","catalog.optional":"\u53EF\u9009","catalog.default":"\u9ED8\u8BA4\u503C","catalog.description":"\u8BF4\u660E","catalog.path":"\u8DEF\u5F84","catalog.revision":"\u4FEE\u8BA2","catalog.nodeCount":"\u8282\u70B9\u6570"},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","botDefaults.brandLabel":"Signature (card footer)","botDefaults.brandLabelHelp":"Footer signature on cards this bot sends. Save empty = hide; fill in = custom (markdown ok, e.g. [Acme](https://\u2026)); Reset = show botmux.","botDefaults.brandLabelPlaceholder":"Default: botmux (empty = hidden)","botDefaults.brandSave":"Save Signature","botDefaults.brandReset":"Reset to default","botDefaults.brandStateDefault":"Current: default botmux","botDefaults.brandStateOff":"Current: off","botDefaults.brandStateCustom":"Current: custom","nav.roles":"Roles","roles.title":"Role Management","roles.subtitle":"Set per-bot role prompts for each group. Each bot adopts its own persona in the selected group.","roles.search":"Search group / bot / ID","roles.refresh":"Refresh","roles.selectHint":"\u2190 Expand a group and select a bot to edit its role","roles.editorPlaceholder":`Enter role description, e.g.:
7
+ }`,"catalog.chatId":"\u7FA4\u804A ID","catalog.larkAppId":"\u98DE\u4E66\u5E94\u7528 ID","catalog.chatBindingHint":"\u5FC5\u586B\uFF0C\u7528\u4E8E\u786E\u5B9A humanGate \u5361\u7247\u53D1\u9001\u5230\u54EA\u4E2A\u98DE\u4E66\u7FA4\uFF0C\u4EE5\u53CA\u53D6\u6D88\u8DEF\u7531\u5F52\u5C5E\u3002","catalog.run":"\u8FD0\u884C","catalog.running":"\u542F\u52A8\u4E2D...","catalog.badParamsJson":"\u53C2\u6570\u5FC5\u987B\u662F JSON object\u3002","catalog.writeAccess":"\u9700\u8981\u5199\u6743\u9650\uFF1A\u8BF7\u5728\u7EC8\u7AEF\u8FD0\u884C `botmux dashboard` \u83B7\u53D6\u4E00\u6B21\u6027 URL\uFF0C\u6253\u5F00\u540E\u5199\u5165 cookie\uFF0C\u518D\u56DE\u6765\u8FD0\u884C\u3002","catalog.runHttp":"run HTTP {status}","catalog.runStarted":"\u8FD0\u884C\u5DF2\u542F\u52A8\uFF1B\u6B63\u5728\u6253\u5F00\u8BE6\u60C5\u9875...","catalog.invalidParams":"\u53C2\u6570\u65E0\u6548","catalog.issue":"{path}: {message}","catalog.noParams":"\u6CA1\u6709\u58F0\u660E\u53C2\u6570\u3002","catalog.required":"\u5FC5\u586B","catalog.optional":"\u53EF\u9009","catalog.default":"\u9ED8\u8BA4\u503C","catalog.description":"\u8BF4\u660E","catalog.path":"\u8DEF\u5F84","catalog.revision":"\u4FEE\u8BA2","catalog.nodeCount":"\u8282\u70B9\u6570"},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 Defaults","botDefaults.subtitle":"Manage each bot's defaults: new-chat oncall auto-binding, card signature, and more.","botDefaults.search":"Search bot name / app id","botDefaults.refresh":"Refresh","botDefaults.sectionOncall":"New-chat Oncall","botDefaults.sectionBrand":"Card Signature","botDefaults.warning":"When enabled, chats without an oncall binding auto-bind to this directory on their next new topic. Manually bound or unbound chats are preserved.","botDefaults.empty":"No bots online. Run botmux restart first.","botDefaults.defaultOncall":"Default to oncall mode","botDefaults.defaultOncallHelp":"Unbound chats auto-bind on the next new topic","botDefaults.workingDir":"Default Working Directory","botDefaults.lastEnabled":"Last Enabled","botDefaults.autobound":"{count} chats auto-bound","botDefaults.save":"Save","botDefaults.required":"Working directory is required when enabled","botDefaults.brandLabel":"Signature (card footer)","botDefaults.brandLabelHelp":"Footer signature on cards this bot sends. Save empty = hide; fill in = custom (markdown ok, e.g. [Acme](https://\u2026)); Reset = show botmux.","botDefaults.brandLabelPlaceholder":"Default: botmux (empty = hidden)","botDefaults.brandSave":"Save Signature","botDefaults.brandReset":"Reset to default","botDefaults.brandStateDefault":"Current: default botmux","botDefaults.brandStateOff":"Current: off","botDefaults.brandStateCustom":"Current: custom","botDefaults.sectionCard":"Card Behavior","botDefaults.disableStreaming":"Disable streaming card","botDefaults.disableStreamingHelp":"Stop posting the live session status card (and its Open Terminal entry). The task's final result still arrives as a message. For those who find the live card noisy.","botDefaults.writableLink":"Put a writable terminal link on the card","botDefaults.writableLinkHelp":'\u26A0\uFE0F Embeds a writable terminal link in the streaming card body \u2014 anyone in the chat can open and drive the terminal. Off = current behavior (private DM via the "Get Write Link" button).',"botDefaults.writableLinkMoot":"Streaming card disabled \u2014 this has no effect","botDefaults.cardPrefSaved":"Saved","nav.roles":"Roles","roles.title":"Role Management","roles.subtitle":"Set per-bot role prompts for each group. Each bot adopts its own persona in the selected group.","roles.search":"Search group / bot / ID","roles.refresh":"Refresh","roles.selectHint":"\u2190 Expand a group and select a bot to edit its role","roles.editorPlaceholder":`Enter role description, e.g.:
8
8
  You are a code reviewer for this group...`,"roles.configured":"Configured","roles.unconfigured":"None","roles.noChats":"No groups","roles.preview":"Preview","roles.previewEmpty":"(empty)","roles.saved":"Saved","roles.delete":"Delete","roles.save":"Save","roles.confirmDelete":"Delete this bot's role config for this group?","roles.botsWithRoles":"bots configured","roles.emptyError":"Role content cannot be empty","roles.saveFailed":"Save failed, please retry","common.none":"None","common.unknown":"Unknown","common.now":"now","common.never":"never","nav.workflows":"Workflows (beta)","nav.workflowCatalog":"Catalog","workflow.subnav.runs":"Runs","workflow.subnav.catalog":"Catalog","workflow.searchPlaceholder":"search runId / workflowId / chatId","workflow.filter.nonTerminal":"non-terminal","workflow.filter.all":"all","workflow.status.pending":"pending","workflow.status.running":"running","workflow.status.waiting":"waiting","workflow.status.effectAttempting":"effect","workflow.status.timedOut":"timed out","workflow.status.succeeded":"succeeded","workflow.status.failed":"failed","workflow.status.cancelled":"cancelled","workflow.table.run":"run","workflow.table.workflow":"workflow","workflow.table.status":"status","workflow.table.lastSeq":"lastSeq","workflow.table.dangling":"dEf/dAct/dWait","workflow.table.updated":"updated","workflow.table.chatApp":"chat / app","workflow.list.failedLoad":"Failed to load: {error}","workflow.list.noRuns":"No runs match.","workflow.list.noFilterMatch":"No runs match this filter.","workflow.list.loaded":"{count} runs \xB7 refreshed {time}","workflow.list.error":"error: {error}","workflow.detail.back":"Back","workflow.detail.loading":"Loading...","workflow.detail.loadFailed":"Load failed","workflow.detail.cancel":"Cancel","workflow.detail.cliCancelOnly":"CLI cancel only","workflow.detail.cancelTitle":"Cancel this workflow run","workflow.detail.cliCancelTitle":"Cancel unavailable: use botmux workflow cancel {runId}","workflow.detail.nodes":"Nodes / Activities","workflow.detail.parallel":"Parallel execution","workflow.detail.parallelMeta":"{count} attempt(s) \xB7 max parallel {max} \xB7 running {running}","workflow.detail.noParallelData":"No attempt timing data yet.","workflow.detail.parallelNow":"now","workflow.detail.node":"node","workflow.detail.nodeStatus":"node status","workflow.detail.activity":"activity","workflow.detail.activityStatus":"activity status","workflow.detail.attempts":"attempts","workflow.detail.current":"current","workflow.detail.detail":"detail","workflow.detail.nodeIO":"Node I/O","workflow.detail.timeline":"Timeline","workflow.detail.loadOlder":"Load older","workflow.detail.seq":"seq","workflow.detail.actor":"actor","workflow.detail.error":"error","workflow.detail.event":"event","workflow.detail.time":"time","workflow.detail.refreshed":"refreshed {time}","workflow.detail.unknownRun":"unknown run","workflow.detail.snapshotHttp":"snapshot HTTP {status}","workflow.detail.eventsHttp":"events HTTP {status}","workflow.detail.cancelUnavailable":"cancel unavailable: use botmux workflow cancel {runId}","workflow.detail.cancelConfirm":`Cancel workflow run {runId}?
9
9
 
10
10
  {total} dangling item(s) will be handled by cancel-driven recovery.
11
11
  effects={effects}, activities={activities}, waits={waits}, cancels={cancels}`,"workflow.detail.writeAccessCancel":"write access required: run `botmux dashboard` in the terminal to get a one-time URL, open it once to set the cookie, then come back and click cancel again.","workflow.detail.cancelHttp":"cancel HTTP {status}","workflow.detail.cancelPending":"cancel pending; waiting for running activity to drain","workflow.detail.writeAccessApproval":"write access required: run `botmux dashboard` in the terminal to get a one-time URL, open it once to set the cookie, then come back and approve/reject again.","workflow.detail.actionHttp":"{action} HTTP {status}","workflow.detail.approved":"approved","workflow.detail.rejected":"rejected","workflow.detail.alreadyTerminal":"Run already terminal; {label} was not applied.","workflow.detail.workflowContinue":"{label}; waiting for workflow to continue.","workflow.detail.workflowRefreshing":"{label}; refreshing workflow state.","workflow.detail.eventsLoaded":"{loaded}/{total} events loaded","workflow.detail.dangling":"Dangling","workflow.detail.noDangling":"No dangling work.","workflow.detail.none":"none","workflow.detail.noNodes":"No nodes yet.","workflow.detail.idle":"idle","workflow.detail.noNodeIO":"No node I/O yet.","workflow.detail.notDispatched":"not dispatched","workflow.detail.noAttempt":"No attempt yet","workflow.detail.attempt":"attempt","workflow.detail.authoredInput":"Authored input","workflow.detail.resolvedInput":"Resolved input","workflow.detail.output":"Output","workflow.detail.executionLog":"Execution log","workflow.detail.liveTerminal":"Live terminal","workflow.detail.terminalLive":"live","workflow.detail.terminalClosedShort":"closed","workflow.detail.terminalClosed":"Terminal is closed. Use the execution log below for the final transcript.","workflow.detail.openTerminalNewTab":"Open terminal in new tab","workflow.detail.terminalReplay":"Terminal replay","workflow.detail.openReplayNewTab":"Open replay in new tab","workflow.detail.downloadFullLog":"Download full log","workflow.detail.terminalResume":"Debug session","workflow.detail.openResumeNewTab":"Open debug session in new tab","workflow.detail.resumeSession":"Resume session","workflow.detail.resumeStarting":"Starting\u2026","workflow.detail.endResumeSession":"End debug session","workflow.detail.resumeEnding":"Ending\u2026","workflow.detail.resumeUnsupportedCli":'CLI "{cliId}" does not support resume',"workflow.detail.resumeMissingCliSession":"Missing cliSessionId \u2014 cannot resume","workflow.detail.resumeStartFailed":"Failed to start debug session (HTTP {status})","workflow.detail.resumeEndFailed":"Failed to end debug session (HTTP {status})","workflow.detail.writeAccessResume":"Resume requires dashboard write access.","workflow.detail.waitPrompt":"Wait prompt","workflow.detail.approvalComment":"Approval comment","workflow.detail.optionalComment":"Optional comment","workflow.detail.approve":"Approve","workflow.detail.reject":"Reject","workflow.detail.submitting":"Submitting...","workflow.detail.empty":"empty","workflow.detail.truncated":"truncated","workflow.detail.noData":"No data.","workflow.detail.noPreview":"No preview.","workflow.detail.open":"open","workflow.detail.deadline":"deadline","workflow.detail.effect":"effect","workflow.detail.wait":"wait","workflow.detail.noEvents":"No events.","workflow.summary.workflow":"workflow","workflow.summary.status":"status","workflow.summary.lastSeq":"lastSeq","workflow.summary.updated":"updated","workflow.summary.revision":"revision","workflow.summary.initiator":"initiator","workflow.summary.failedNode":"failedNode","workflow.summary.cancelOrigin":"cancelOrigin","workflow.summary.chat":"chat","workflow.summary.app":"app","workflow.dangling.activities":"activities","workflow.dangling.effects":"effects","workflow.dangling.waits":"waits","workflow.dangling.cancels":"cancels","catalog.title":"Workflow catalog","catalog.subtitle":"Create a workflow run from a saved workflow definition.","catalog.searchPlaceholder":"search workflowId / path","catalog.refresh":"Refresh","catalog.loading":"Loading catalog...","catalog.loadFailed":"Failed to load catalog: {error}","catalog.noDefinitions":"No workflow definitions found.","catalog.noFilterMatch":"No definitions match this filter.","catalog.table.workflow":"workflow","catalog.table.version":"version","catalog.table.params":"params","catalog.table.nodes":"nodes","catalog.table.revision":"revision","catalog.table.path":"path","catalog.paramSummary":"{required}/{total} required","catalog.back":"Back to catalog","catalog.detailTitle":"Workflow definition","catalog.definitionLoadFailed":"Failed to load definition: {error}","catalog.summary":"Summary","catalog.paramsSchema":"Params schema","catalog.definitionJson":"Definition JSON","catalog.runPanel":"Run workflow","catalog.paramsJson":"Params JSON","catalog.paramsPlaceholder":`{
12
12
  "city": "\u5317\u4EAC"
13
- }`,"catalog.chatId":"Chat ID","catalog.larkAppId":"Lark app ID","catalog.chatBindingHint":"Required so humanGate cards and cancel routing know which Lark chat owns the run.","catalog.run":"Run","catalog.running":"Starting...","catalog.badParamsJson":"Params must be a JSON object.","catalog.writeAccess":"write access required: run `botmux dashboard` in the terminal to get a one-time URL, open it once to set the cookie, then come back and run again.","catalog.runHttp":"run HTTP {status}","catalog.runStarted":"Run started; opening detail page...","catalog.invalidParams":"Invalid params","catalog.issue":"{path}: {message}","catalog.noParams":"No params declared.","catalog.required":"required","catalog.optional":"optional","catalog.default":"default","catalog.description":"description","catalog.path":"path","catalog.revision":"revision","catalog.nodeCount":"nodes"},Ge={zh:qt,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,(a,s)=>{let d=o[s];return d==null?`{${s}}`: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 p(e){return e.replace(/[&<>"']/g,t=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"})[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-${p(e||"unknown")}`}function _t(e){return`<li class="overview-list-row">
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:qt,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 ve(e){return(t,o)=>{let r=Ge[e][t]??Ge.zh[t]??t;return o?r.replace(/\{(\w+)\}/g,(a,s)=>{let d=o[s];return d==null?`{${s}}`:String(d)}):r}}function Ke(e,t){return(e?ze(e.getItem(Ae)):null)??Nt(t)}var He="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(He))??"system"}var Re=class{locale="zh";themeMode="system";resolvedTheme="light";listeners=new Set;translate=ve(this.locale);mediaQuery=null;init(){let t=typeof window<"u"?window:void 0;this.locale=Ke(t?.localStorage,Ut()),this.translate=ve(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=ve(t),window.localStorage.setItem(Ae,t),this.applyLocale(),this.emit())}setThemeMode(t){this.themeMode!==t&&(this.themeMode=t,window.localStorage.setItem(He,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 X=new Re;function n(e,t){return X.t(e,t)}function m(e){return e.replace(/[&<>"']/g,t=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"})[t])}function ue(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 De={chats:[],bots:[]};async function Ft(){try{let e=await fetch("/api/groups");if(!e.ok)return;De=await e.json()}catch{}}function Wt(e){return`status status-${m(e||"unknown")}`}function _t(e){return`<li class="overview-list-row">
14
14
  <div>
15
- <strong>${p(e.title??e.sessionId)}</strong>
16
- <span>${p(e.botName??"")} \xB7 ${p(e.cliId??"unknown")}</span>
15
+ <strong>${m(e.title??e.sessionId)}</strong>
16
+ <span>${m(e.botName??"")} \xB7 ${m(e.cliId??"unknown")}</span>
17
17
  </div>
18
- <span class="${Wt(e.status)}">${p(e.status??"unknown")}</span>
18
+ <span class="${Wt(e.status)}">${m(e.status??"unknown")}</span>
19
19
  </li>`}function Jt(e){let t=e.nextRunAt?new Date(e.nextRunAt).toLocaleString():"-";return`<li class="overview-list-row">
20
20
  <div>
21
- <strong>${p(e.name??e.id)}</strong>
22
- <span>${p(e.botName??e.larkAppId??"")} \xB7 ${p(e.parsed?.display??"")}</span>
21
+ <strong>${m(e.name??e.id)}</strong>
22
+ <span>${m(e.botName??e.larkAppId??"")} \xB7 ${m(e.parsed?.display??"")}</span>
23
23
  </div>
24
- <span>${p(t)}</span>
24
+ <span>${m(t)}</span>
25
25
  </li>`}async function Qe(e){e.innerHTML=`<section class="page hero-page">
26
26
  <div class="page-heading">
27
27
  <div>
@@ -53,16 +53,16 @@ effects={effects}, activities={activities}, waits={waits}, cancels={cancels}`,"w
53
53
  <ul class="overview-list" id="next-schedules"></ul>
54
54
  </section>
55
55
  </div>
56
- </section>`;let t=e.querySelector("#overview-metrics"),o=e.querySelector("#recent-sessions"),r=e.querySelector("#next-schedules");function a(){let s=[...B.sessions.values()],d=[...B.schedules.values()],w=s.filter(E=>E.status!=="closed"),T=s.filter(E=>E.status==="working"||E.status==="analyzing"||E.status==="starting"),g=d.filter(E=>E.enabled),h=Re.bots?.length||new Set(s.map(E=>E.larkAppId).filter(Boolean)).size,v=[{label:n("overview.openSessions"),value:w.length,meta:`${s.length} ${n("overview.total")}`},{label:n("overview.workingSessions"),value:T.length,meta:`${w.length} ${n("overview.active")}`},{label:n("overview.onlineBots"),value:h,meta:n("overview.daemonRegistry")},{label:n("overview.schedules"),value:d.length,meta:`${g.length} ${n("overview.enabledSchedules")}`},{label:n("overview.groups"),value:Re.chats?.length??0,meta:n("overview.chatMatrix")}];t.innerHTML=v.map(E=>`<article class="metric-card">
57
- <span>${p(E.label)}</span>
56
+ </section>`;let t=e.querySelector("#overview-metrics"),o=e.querySelector("#recent-sessions"),r=e.querySelector("#next-schedules");function a(){let s=[...q.sessions.values()],d=[...q.schedules.values()],w=s.filter(E=>E.status!=="closed"),I=s.filter(E=>E.status==="working"||E.status==="analyzing"||E.status==="starting"),L=d.filter(E=>E.enabled),f=De.bots?.length||new Set(s.map(E=>E.larkAppId).filter(Boolean)).size,g=[{label:n("overview.openSessions"),value:w.length,meta:`${s.length} ${n("overview.total")}`},{label:n("overview.workingSessions"),value:I.length,meta:`${w.length} ${n("overview.active")}`},{label:n("overview.onlineBots"),value:f,meta:n("overview.daemonRegistry")},{label:n("overview.schedules"),value:d.length,meta:`${L.length} ${n("overview.enabledSchedules")}`},{label:n("overview.groups"),value:De.chats?.length??0,meta:n("overview.chatMatrix")}];t.innerHTML=g.map(E=>`<article class="metric-card">
57
+ <span>${m(E.label)}</span>
58
58
  <strong>${E.value}</strong>
59
- <small>${p(E.meta)}</small>
60
- </article>`).join("");let c=s.sort((E,k)=>Number(k.lastMessageAt??0)-Number(E.lastMessageAt??0)).slice(0,6);o.innerHTML=c.length?c.map(E=>_t({...E,title:E.title??`${re(E.lastMessageAt)} \xB7 ${E.sessionId}`})).join(""):`<li class="empty">${n("overview.noSessions")}</li>`;let L=d.filter(E=>E.nextRunAt).sort((E,k)=>Date.parse(E.nextRunAt)-Date.parse(k.nextRunAt)).slice(0,6);r.innerHTML=L.length?L.map(Jt).join(""):`<li class="empty">${n("overview.noSchedules")}</li>`}B.on(a),a(),Ft().then(a)}function V(e,t){return`<th data-sort="${e}" data-label="${p(t)}">${p(t)}</th>`}var Xe=["claude-code","codex","cursor","gemini","opencode","mtr","aiden","coco","unknown"];function Gt(){return`<div class="filter-check-group" role="group" aria-label="${n("sessions.cli")}">
59
+ <small>${m(E.meta)}</small>
60
+ </article>`).join("");let l=s.sort((E,b)=>Number(b.lastMessageAt??0)-Number(E.lastMessageAt??0)).slice(0,6);o.innerHTML=l.length?l.map(E=>_t({...E,title:E.title??`${ue(E.lastMessageAt)} \xB7 ${E.sessionId}`})).join(""):`<li class="empty">${n("overview.noSessions")}</li>`;let S=d.filter(E=>E.nextRunAt).sort((E,b)=>Date.parse(E.nextRunAt)-Date.parse(b.nextRunAt)).slice(0,6);r.innerHTML=S.length?S.map(Jt).join(""):`<li class="empty">${n("overview.noSchedules")}</li>`}q.on(a),a(),Ft().then(a)}function Z(e,t){return`<th data-sort="${e}" data-label="${m(t)}">${m(t)}</th>`}var Xe=["claude-code","codex","cursor","gemini","opencode","mtr","hermes","aiden","coco","unknown"];function Gt(){return`<div class="filter-check-group" role="group" aria-label="${n("sessions.cli")}">
61
61
  <span class="filter-check-label">${n("sessions.cli")}</span>
62
62
  ${Xe.map(e=>`
63
63
  <label class="filter-check">
64
- <input type="checkbox" name="cli" value="${p(e)}" checked>
65
- <span>${p(e)}</span>
64
+ <input type="checkbox" name="cli" value="${m(e)}" checked>
65
+ <span>${m(e)}</span>
66
66
  </label>
67
67
  `).join("")}
68
68
  </div>`}function zt(){return`<section class="page">
@@ -96,49 +96,49 @@ effects={effects}, activities={activities}, waits={waits}, cancels={cancels}`,"w
96
96
  <table id="sessions-table">
97
97
  <thead><tr>
98
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"))}
99
+ ${Z("botName",n("sessions.bot"))}
100
+ ${Z("cliId",n("sessions.cli"))}
101
+ ${Z("status",n("sessions.status"))}
102
+ ${Z("title",n("sessions.titleCol"))}
103
+ ${Z("workingDir",n("sessions.workingDir"))}
104
+ ${Z("spawnedAt",n("sessions.created"))}
105
+ ${Z("lastMessageAt",n("sessions.last"))}
106
+ ${Z("adopt",n("sessions.adopt"))}
107
107
  <th>${n("sessions.actions")}</th>
108
108
  </tr></thead>
109
109
  <tbody></tbody>
110
110
  </table>
111
111
  <dialog id="drawer"></dialog>
112
- </section>`}function Ze(e){e.innerHTML=zt();let t=e.querySelector("#sessions-table tbody"),o=e.querySelector("#filters"),r=e.querySelector("#drawer"),a=e.querySelector("#select-all"),s=e.querySelector("#bulk-bar"),d=e.querySelector("#bulk-count"),w=e.querySelector("#bulk-close"),T=e.querySelector("#bulk-clear"),g=e.querySelector("#sessions-table"),h=new Set,v="lastMessageAt",c="desc";function L(l){let f=l.status==="closed",m=h.has(l.sessionId)?"checked":"";return`<tr data-id="${p(l.sessionId)}">
113
- <td><input type="checkbox" class="row-select" ${m} ${f?"disabled":""}></td>
114
- <td>${p(l.botName??"")}</td>
115
- <td><span class="badge cli-${p(l.cliId??"unknown")}">${p(l.cliId??"unknown")}</span></td>
116
- <td><span class="status status-${p(l.status??"unknown")}">${p(l.status??"unknown")}</span></td>
117
- <td>${p((l.title??"").slice(0,48))}</td>
118
- <td title="${p(l.workingDir??"")}">${p((l.workingDir??"").slice(-34))}</td>
119
- <td>${re(l.spawnedAt)}</td>
120
- <td>${re(l.lastMessageAt)}</td>
121
- <td>${l.adopt?'<span class="badge">adopt</span>':""}</td>
112
+ </section>`}function Ze(e){e.innerHTML=zt();let t=e.querySelector("#sessions-table tbody"),o=e.querySelector("#filters"),r=e.querySelector("#drawer"),a=e.querySelector("#select-all"),s=e.querySelector("#bulk-bar"),d=e.querySelector("#bulk-count"),w=e.querySelector("#bulk-close"),I=e.querySelector("#bulk-clear"),L=e.querySelector("#sessions-table"),f=new Set,g="lastMessageAt",l="desc";function S(c){let p=c.status==="closed",h=f.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>${ue(c.spawnedAt)}</td>
120
+ <td>${ue(c.lastMessageAt)}</td>
121
+ <td>${c.adopt?'<span class="badge">adopt</span>':""}</td>
122
122
  <td><button class="open" type="button">${n("sessions.details")}</button></td>
123
- </tr>`}function E(){let l=new FormData(o),f=(l.get("q")??"").toLowerCase(),m=l.getAll("cli"),I=m.length>0&&m.length<Xe.length,A=l.get("status"),H=l.get("adopt"),O=!!l.get("active"),q=[...B.sessions.values()].filter(D=>!I||m.includes(D.cliId??"unknown")).filter(D=>!A||D.status===A).filter(D=>!H||H==="yes"==!!D.adopt).filter(D=>!O||D.status!=="closed").filter(D=>!f||JSON.stringify(D).toLowerCase().includes(f));return q.sort(u),q}function k(l,f){return f==="spawnedAt"||f==="lastMessageAt"?Number(l[f]??0):f==="adopt"?!!l.adopt:String(l[f]??"").toLowerCase()}function u(l,f){let m=k(l,v),I=k(f,v),A=0;return typeof m=="number"&&typeof I=="number"?A=m-I:typeof m=="boolean"&&typeof I=="boolean"?A=Number(m)-Number(I):A=String(m).localeCompare(String(I)),A===0&&(A=Number(l.lastMessageAt??0)-Number(f.lastMessageAt??0)),c==="asc"?A:-A}function S(){g.querySelectorAll("th[data-sort]").forEach(l=>{let f=l.dataset.sort===v;l.classList.toggle("sorted",f),l.setAttribute("aria-sort",f?c==="asc"?"ascending":"descending":"none");let m=l.dataset.label??l.textContent?.trim()??"";l.textContent=f?`${m} ${c==="asc"?"\u25B2":"\u25BC"}`:m})}function y(l){s.hidden=h.size===0,d.textContent=n("sessions.selectedCount",{count:h.size});let f=l.filter(I=>I.status!=="closed");if(f.length===0){a.checked=!1,a.indeterminate=!1,a.disabled=!0;return}a.disabled=!1;let m=f.filter(I=>h.has(I.sessionId)).length;a.checked=m===f.length,a.indeterminate=m>0&&m<f.length}function $(){let l=E();for(let f of[...h]){let m=B.sessions.get(f);(!m||m.status==="closed")&&h.delete(f)}t.innerHTML=l.length?l.map(L).join(""):`<tr><td colspan="10" class="empty">${n("sessions.empty")}</td></tr>`,S(),y(l)}function M(l){let f=l.status==="closed";r.innerHTML=`<article>
123
+ </tr>`}function E(){let c=new FormData(o),p=(c.get("q")??"").toLowerCase(),h=c.getAll("cli"),T=h.length>0&&h.length<Xe.length,H=c.get("status"),R=c.get("adopt"),O=!!c.get("active"),B=[...q.sessions.values()].filter(C=>!T||h.includes(C.cliId??"unknown")).filter(C=>!H||C.status===H).filter(C=>!R||R==="yes"==!!C.adopt).filter(C=>!O||C.status!=="closed").filter(C=>!p||JSON.stringify(C).toLowerCase().includes(p));return B.sort(u),B}function b(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=b(c,g),T=b(p,g),H=0;return typeof h=="number"&&typeof T=="number"?H=h-T:typeof h=="boolean"&&typeof T=="boolean"?H=Number(h)-Number(T):H=String(h).localeCompare(String(T)),H===0&&(H=Number(c.lastMessageAt??0)-Number(p.lastMessageAt??0)),l==="asc"?H:-H}function $(){L.querySelectorAll("th[data-sort]").forEach(c=>{let p=c.dataset.sort===g;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 k(c){s.hidden=f.size===0,d.textContent=n("sessions.selectedCount",{count:f.size});let p=c.filter(T=>T.status!=="closed");if(p.length===0){a.checked=!1,a.indeterminate=!1,a.disabled=!0;return}a.disabled=!1;let h=p.filter(T=>f.has(T.sessionId)).length;a.checked=h===p.length,a.indeterminate=h>0&&h<p.length}function v(){let c=E();for(let p of[...f]){let h=q.sessions.get(p);(!h||h.status==="closed")&&f.delete(p)}t.innerHTML=c.length?c.map(S).join(""):`<tr><td colspan="10" class="empty">${n("sessions.empty")}</td></tr>`,$(),k(c)}function A(c){let p=c.status==="closed";r.innerHTML=`<article>
124
124
  <header>
125
- <h3>${p(l.title??l.sessionId)}</h3>
126
- <span class="status status-${p(l.status??"unknown")}">${p(l.status??"unknown")}</span>
127
- <p><code>${p(l.sessionId)}</code> <button data-copy="${p(l.sessionId)}">${n("sessions.copy")}</button></p>
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>
128
128
  </header>
129
- <p><b>${n("sessions.bot")}:</b> ${p(l.botName??"-")} \xB7 <b>${n("sessions.cli")}:</b> ${p(l.cliId??"?")}</p>
130
- <p><b>chatId:</b> <code>${p(l.chatId??"")}</code> <button data-copy="${p(l.chatId??"")}">${n("sessions.copy")}</button></p>
131
- <p><b>rootMessageId:</b> <code>${p(l.rootMessageId??"")}</code> <button data-copy="${p(l.rootMessageId??"")}">${n("sessions.copy")}</button></p>
132
- ${l.threadId?`<p><b>threadId:</b> <code>${p(l.threadId)}</code></p>`:""}
133
- <p><b>${n("sessions.workingDir")}:</b> ${p(l.workingDir??"-")}</p>
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>
134
134
  <div class="actions">
135
135
  <button id="locate-btn" type="button">${n("sessions.locate")}</button>
136
- ${l.webPort?`<a class="btn-link primary" href="http://${p(location.hostname)}:${l.webPort}" target="_blank" rel="noopener">${n("sessions.openTerminal")}</a>`:""}
137
- ${f?`<button id="resume-btn" type="button" class="primary">${n("sessions.resume")}</button>`:""}
138
- ${f?"":`<button id="close-btn" type="button" class="contrast">${n("sessions.close")}</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>`}
139
139
  </div>
140
140
  <form method="dialog"><button>${n("sessions.dismiss")}</button></form>
141
- </article>`,r.querySelectorAll("[data-copy]").forEach(H=>{H.onclick=()=>{navigator.clipboard.writeText(H.dataset.copy??""),H.textContent=n("sessions.copied"),setTimeout(()=>{H.textContent=n("sessions.copy")},800)}});let m=r.querySelector("#locate-btn");m&&(m.onclick=async()=>{m.disabled=!0,m.textContent=n("sessions.locating");try{let H=await fetch(`/api/sessions/${encodeURIComponent(l.sessionId)}/locate`,{method:"POST"}),O=await H.json();if(O.ok){let q=30;m.textContent=n("sessions.cooldown",{seconds:q});let D=setInterval(()=>{q-=1,q<=0?(clearInterval(D),m.disabled=!1,m.textContent=n("sessions.locate")):m.textContent=n("sessions.cooldown",{seconds:q})},1e3)}else alert(`Locate failed: ${O.error??H.status}`),m.disabled=!1,m.textContent=n("sessions.locate")}catch(H){alert(`Locate error: ${H}`),m.disabled=!1,m.textContent=n("sessions.locate")}});let I=r.querySelector("#resume-btn");I&&(I.onclick=async()=>{I.disabled=!0;try{let H=await fetch(`/api/sessions/${encodeURIComponent(l.sessionId)}/resume`,{method:"POST"}),O=await H.json().catch(()=>({}));if(!H.ok||O.ok===!1){alert(`${n("sessions.resumeFailed")}: ${O?.error??H.status}`),I.disabled=!1;return}r.close()}catch(H){alert(`${n("sessions.resumeFailed")}: ${H}`),I.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(l.sessionId)}/close`,{method:"POST"})}finally{r.close()}}}),r.showModal()}t.addEventListener("click",l=>{let f=l.target;if(f.classList.contains("row-select")){let H=f.closest("tr[data-id]");if(!H)return;f.checked?h.add(H.dataset.id):h.delete(H.dataset.id),y(E());return}let m=f.closest("td");if(m&&m.querySelector(".row-select"))return;let I=f.closest("tr[data-id]");if(!I)return;let A=B.sessions.get(I.dataset.id);A&&M(A)}),a.addEventListener("change",()=>{let l=E().filter(f=>f.status!=="closed");for(let f of l)a.checked?h.add(f.sessionId):h.delete(f.sessionId);$()}),T.addEventListener("click",()=>{h.clear(),$()}),w.addEventListener("click",async()=>{let l=[...h];if(l.length===0||!confirm(n("sessions.closeBulkConfirm",{count:l.length})))return;w.disabled=!0,T.disabled=!0;let f=w.textContent,m=0,I=0,A=[...l];w.textContent=`0/${l.length}`;async function H(){for(;A.length;){let O=A.shift();try{let q=await fetch(`/api/sessions/${encodeURIComponent(O)}/close`,{method:"POST"}),D=await q.json().catch(()=>({}));(!q.ok||D?.ok===!1)&&(I+=1)}catch{I+=1}finally{m+=1,w.textContent=`${m}/${l.length}`}}}await Promise.all(Array.from({length:Math.min(6,l.length)},()=>H())),w.textContent=f,w.disabled=!1,T.disabled=!1,h.clear(),$(),I>0&&alert(`Failed: ${I}/${l.length}`)}),g.querySelectorAll("th[data-sort]").forEach(l=>{l.addEventListener("click",()=>{let f=l.dataset.sort;v===f?c=c==="asc"?"desc":"asc":(v=f,c=f==="spawnedAt"||f==="lastMessageAt"?"desc":"asc"),$()})}),o.addEventListener("input",$),B.on($),$()}function Kt(){return`<section class="page">
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"}),O=await R.json();if(O.ok){let B=30;h.textContent=n("sessions.cooldown",{seconds:B});let C=setInterval(()=>{B-=1,B<=0?(clearInterval(C),h.disabled=!1,h.textContent=n("sessions.locate")):h.textContent=n("sessions.cooldown",{seconds:B})},1e3)}else alert(`Locate failed: ${O.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 T=r.querySelector("#resume-btn");T&&(T.onclick=async()=>{T.disabled=!0;try{let R=await fetch(`/api/sessions/${encodeURIComponent(c.sessionId)}/resume`,{method:"POST"}),O=await R.json().catch(()=>({}));if(!R.ok||O.ok===!1){alert(`${n("sessions.resumeFailed")}: ${O?.error??R.status}`),T.disabled=!1;return}r.close()}catch(R){alert(`${n("sessions.resumeFailed")}: ${R}`),T.disabled=!1}});let H=r.querySelector("#close-btn");H&&(H.onclick=async()=>{if(confirm(n("sessions.closeConfirm"))){H.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?f.add(R.dataset.id):f.delete(R.dataset.id),k(E());return}let h=p.closest("td");if(h&&h.querySelector(".row-select"))return;let T=p.closest("tr[data-id]");if(!T)return;let H=q.sessions.get(T.dataset.id);H&&A(H)}),a.addEventListener("change",()=>{let c=E().filter(p=>p.status!=="closed");for(let p of c)a.checked?f.add(p.sessionId):f.delete(p.sessionId);v()}),I.addEventListener("click",()=>{f.clear(),v()}),w.addEventListener("click",async()=>{let c=[...f];if(c.length===0||!confirm(n("sessions.closeBulkConfirm",{count:c.length})))return;w.disabled=!0,I.disabled=!0;let p=w.textContent,h=0,T=0,H=[...c];w.textContent=`0/${c.length}`;async function R(){for(;H.length;){let O=H.shift();try{let B=await fetch(`/api/sessions/${encodeURIComponent(O)}/close`,{method:"POST"}),C=await B.json().catch(()=>({}));(!B.ok||C?.ok===!1)&&(T+=1)}catch{T+=1}finally{h+=1,w.textContent=`${h}/${c.length}`}}}await Promise.all(Array.from({length:Math.min(6,c.length)},()=>R())),w.textContent=p,w.disabled=!1,I.disabled=!1,f.clear(),v(),T>0&&alert(`Failed: ${T}/${c.length}`)}),L.querySelectorAll("th[data-sort]").forEach(c=>{c.addEventListener("click",()=>{let p=c.dataset.sort;g===p?l=l==="asc"?"desc":"asc":(g=p,l=p==="spawnedAt"||p==="lastMessageAt"?"desc":"asc"),v()})}),o.addEventListener("input",v),q.on(v),v()}function Kt(){return`<section class="page">
142
142
  <div class="page-heading">
143
143
  <div>
144
144
  <p class="eyebrow">${n("nav.schedules")}</p>
@@ -163,10 +163,10 @@ effects={effects}, activities={activities}, waits={waits}, cancels={cancels}`,"w
163
163
  </tr></thead>
164
164
  <tbody id="schedules-tbody"></tbody>
165
165
  </table>
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 s=new FormData(o),d=(s.get("q")??"").toLowerCase(),w=s.get("kind"),T=!!s.get("enabled");return[...B.schedules.values()].filter(g=>!w||g.parsed?.kind===w).filter(g=>!T||g.enabled).filter(g=>!d||JSON.stringify(g).toLowerCase().includes(d)).sort((g,h)=>{if(g.enabled!==h.enabled)return g.enabled?-1:1;let v=g.nextRunAt?Date.parse(g.nextRunAt):1/0,c=h.nextRunAt?Date.parse(h.nextRunAt):1/0;return v-c})}function a(){t.innerHTML=r().map(s=>`<tr data-id="${p(s.id)}">
167
- <td>${p(s.name??s.id)}</td>
168
- <td>${p(s.botName??s.larkAppId??"-")}</td>
169
- <td><code>${p(s.parsed?.display??"?")}</code></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 s=new FormData(o),d=(s.get("q")??"").toLowerCase(),w=s.get("kind"),I=!!s.get("enabled");return[...q.schedules.values()].filter(L=>!w||L.parsed?.kind===w).filter(L=>!I||L.enabled).filter(L=>!d||JSON.stringify(L).toLowerCase().includes(d)).sort((L,f)=>{if(L.enabled!==f.enabled)return L.enabled?-1:1;let g=L.nextRunAt?Date.parse(L.nextRunAt):1/0,l=f.nextRunAt?Date.parse(f.nextRunAt):1/0;return g-l})}function a(){t.innerHTML=r().map(s=>`<tr data-id="${m(s.id)}">
167
+ <td>${m(s.name??s.id)}</td>
168
+ <td>${m(s.botName??s.larkAppId??"-")}</td>
169
+ <td><code>${m(s.parsed?.display??"?")}</code></td>
170
170
  <td>${et(s.nextRunAt)}</td>
171
171
  <td>${et(s.lastRunAt)} ${s.lastStatus==="error"?"\u26A0\uFE0F":""}</td>
172
172
  <td>${s.repeat?`${s.repeat.completed}/${s.repeat.times??"\u221E"}`:"\u2014"}</td>
@@ -175,7 +175,7 @@ effects={effects}, activities={activities}, waits={waits}, cancels={cancels}`,"w
175
175
  <button data-op="run" type="button">${n("schedules.runNow")}</button>
176
176
  ${s.enabled?`<button data-op="pause" type="button">${n("schedules.pause")}</button>`:`<button data-op="resume" type="button">${n("schedules.resume")}</button>`}
177
177
  </td>
178
- </tr>`).join("")||`<tr><td colspan="8" class="empty">${n("schedules.empty")}</td></tr>`}t.addEventListener("click",async s=>{let d=s.target.closest("button[data-op]");if(!d)return;let w=d.closest("tr[data-id]");if(!w)return;let T=w.dataset.id,g=d.dataset.op;d.disabled=!0;let h=d.textContent;d.textContent="...";try{let v=await fetch(`/api/schedules/${encodeURIComponent(T)}/${g}`,{method:"POST"}),c=await v.json().catch(()=>({}));(!v.ok||c.ok===!1)&&alert(`Failed: ${v.status} ${c?.error??""}`.trim())}catch(v){alert("Network error: "+v)}finally{d.disabled=!1,d.textContent=h}}),o.addEventListener("input",a),B.on(a),a()}var N={chats:[],bots:[]};function Vt(){return`<section class="page">
178
+ </tr>`).join("")||`<tr><td colspan="8" class="empty">${n("schedules.empty")}</td></tr>`}t.addEventListener("click",async s=>{let d=s.target.closest("button[data-op]");if(!d)return;let w=d.closest("tr[data-id]");if(!w)return;let I=w.dataset.id,L=d.dataset.op;d.disabled=!0;let f=d.textContent;d.textContent="...";try{let g=await fetch(`/api/schedules/${encodeURIComponent(I)}/${L}`,{method:"POST"}),l=await g.json().catch(()=>({}));(!g.ok||l.ok===!1)&&alert(`Failed: ${g.status} ${l?.error??""}`.trim())}catch(g){alert("Network error: "+g)}finally{d.disabled=!1,d.textContent=f}}),o.addEventListener("input",a),q.on(a),a()}var j={chats:[],bots:[]};function Vt(){return`<section class="page">
179
179
  <div class="page-heading">
180
180
  <div>
181
181
  <p class="eyebrow">${n("nav.groups")}</p>
@@ -194,12 +194,12 @@ effects={effects}, activities={activities}, waits={waits}, cancels={cancels}`,"w
194
194
  <tbody id="g-body"></tbody>
195
195
  </table>
196
196
  <dialog id="g-drawer"></dialog>
197
- </section>`}async function ne(){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(a=>a.larkAppId===r&&a.inChat))return!1;return!0}function nt(e,t){return e.filter(o=>!t||!t.has(o.larkAppId)).map(o=>`
197
+ </section>`}async function ie(){j=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(a=>a.larkAppId===r&&a.inChat))return!1;return!0}function nt(e,t){return e.filter(o=>!t||!t.has(o.larkAppId)).map(o=>`
198
198
  <label class="checkbox-row">
199
- <input type="checkbox" name="bot" value="${p(o.larkAppId)}">
200
- ${p(o.botName??o.larkAppId)} <small>(${p(o.larkAppId)})</small>
199
+ <input type="checkbox" name="bot" value="${m(o.larkAppId)}">
200
+ ${m(o.botName??o.larkAppId)} <small>(${m(o.larkAppId)})</small>
201
201
  </label>
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"),a=e.querySelector("#g-refresh"),s=e.querySelector("#g-drawer");a.onclick=async()=>{a.disabled=!0;try{await ne(),h()}finally{a.disabled=!1}};let d=e.querySelector("#g-create");d.onclick=()=>w(),await ne();function w(){let c=N.bots;if(c.length===0){alert(n("groups.noBotsOnline"));return}s.innerHTML=`
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"),a=e.querySelector("#g-refresh"),s=e.querySelector("#g-drawer");a.onclick=async()=>{a.disabled=!0;try{await ie(),f()}finally{a.disabled=!1}};let d=e.querySelector("#g-create");d.onclick=()=>w(),await ie();function w(){let l=j.bots;if(l.length===0){alert(n("groups.noBotsOnline"));return}s.innerHTML=`
203
203
  <article>
204
204
  <header><h3>${n("groups.createTitle")}</h3></header>
205
205
  <p>${n("groups.createHelp")}</p>
@@ -215,70 +215,70 @@ effects={effects}, activities={activities}, waits={waits}, cancels={cancels}`,"w
215
215
  </label>
216
216
  <fieldset>
217
217
  <legend>${n("groups.botPicker")}</legend>
218
- ${nt(c)}
218
+ ${nt(l)}
219
219
  </fieldset>
220
220
  <div class="actions">
221
221
  <button type="submit" class="primary">${n("groups.createSubmit")}</button>
222
222
  <button type="button" id="g-create-cancel">${n("groups.cancel")}</button>
223
223
  </div>
224
224
  </form>
225
- </article>`,s.showModal(),s.querySelector("#g-create-cancel").onclick=()=>s.close(),s.querySelector("#g-createform").onsubmit=async k=>{k.preventDefault();let u=new FormData(k.target),S=(u.get("name")??"").trim(),y=(u.get("bindWorkingDir")??"").trim(),$=u.getAll("bot");if($.length===0){alert("Pick at least one bot.");return}let M=k.target.querySelector("button[type=submit]");M&&(M.disabled=!0,M.textContent="Creating...");try{let l=await fetch("/api/groups/create",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({name:S||void 0,larkAppIds:$,bindWorkingDir:y||void 0})}),f=await l.json();if(f.ok&&f.chatId){T(f);let m=Array.isArray(f.invalidBotIds)?f.invalidBotIds:[],I=$.filter(H=>!m.includes(H)),A=new Set(I);typeof f.creator=="string"&&f.creator&&A.add(f.creator),L(f.chatId,S||f.chatId,I,f.creator),h(),E(f.chatId,A).catch(()=>{})}else alert(`Failed: ${f.error??l.status}`),s.close()}catch(l){alert("Network error: "+l),s.close()}};function L(k,u,S,y){let $=new Set(S);y&&$.add(y);let M=N.bots.map(f=>({larkAppId:f.larkAppId,botName:f.botName,inChat:$.has(f.larkAppId),oncallChat:null})),l={chatId:k,name:u,ownerId:y??null,memberBots:M};N.chats=[l,...N.chats.filter(f=>f.chatId!==k)]}async function E(k,u){let S=[600,1200,1200,1200,1200,1200];for(let y of S){await new Promise(l=>setTimeout(l,y));let $;try{$=await Yt()}catch{continue}let M=($.chats??[]).find(l=>l.chatId===k);if(M&&Qt(M,u)){N=$,h();return}}}}function T(c){let L=String(c.chatId),E=`https://applink.feishu.cn/client/chat/open?openChatId=${encodeURIComponent(L)}`,k=c.invalidBotIds??[],u=c.invalidUserIds??[],S=c.autoInvitedOpenId,y=!!c.autoInviteRejected,$=c.ownerTransferredTo,M=c.transferError,l=c.notifyMessageId,f=c.notifyError,m=Array.isArray(c.oncallBindings)?c.oncallBindings:[],I=m.filter(D=>D?.ok).length,A=m.filter(D=>!D?.ok),H=m.length>0?A.length===0?`<p class="hint-ok">\u5DF2\u7ED1\u5B9A\u76EE\u5F55\uFF1A<code>${p(c.bindResolvedPath??"")}</code>\uFF08${I}/${m.length} bots\uFF09</p>`:`<p class="hint-warn">\u76EE\u5F55\u7ED1\u5B9A\u90E8\u5206\u5931\u8D25\uFF1A\u6210\u529F ${I}/${m.length}\u3002${A.map(D=>`<br><code>${p(D.larkAppId??"?")}</code>: ${p(D.error??"unknown")}`).join("")}</p>`:"",O;if(S){let D=$?"<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${p(M)}\uFF09\uFF0C\u4F60\u73B0\u5728\u662F\u6210\u5458\u4F46\u7FA4\u4E3B\u4ECD\u662F\u673A\u5668\u4EBA\u3002</small>`:"",Te=l?`<br><small>\u673A\u5668\u4EBA\u5DF2\u5728\u7FA4\u91CC @ \u4E86\u4F60\uFF08\u6D88\u606F id <code>${p(l)}</code>\uFF09\uFF0C\u770B\u98DE\u4E66\u901A\u77E5\u5C31\u80FD\u8FDB\u7FA4\u3002</small>`:f?`<br><small class="hint-warn-inline">\u26A0 \u81EA\u52A8 @ \u901A\u77E5\u5931\u8D25\uFF08${p(f)}\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>`:"";O=`<p class="hint-ok">\u5DF2\u81EA\u52A8\u9080\u8BF7\u4F60\uFF08<code>${p(S)}</code>\uFF09\u4F5C\u4E3A\u6210\u5458\u3002${D}${Te}</p>`}else y?O='<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>':O='<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 q=[k.length?`<li>\u65E0\u6548 bot id: <code>${k.map(p).join(", ")}</code></li>`:"",u.length?`<li>\u65E0\u6548\u7528\u6237 open_id: <code>${u.map(p).join(", ")}</code></li>`:""].filter(Boolean).join("");s.innerHTML=`
225
+ </article>`,s.showModal(),s.querySelector("#g-create-cancel").onclick=()=>s.close(),s.querySelector("#g-createform").onsubmit=async b=>{b.preventDefault();let u=new FormData(b.target),$=(u.get("name")??"").trim(),k=(u.get("bindWorkingDir")??"").trim(),v=u.getAll("bot");if(v.length===0){alert("Pick at least one bot.");return}let A=b.target.querySelector("button[type=submit]");A&&(A.disabled=!0,A.textContent="Creating...");try{let c=await fetch("/api/groups/create",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({name:$||void 0,larkAppIds:v,bindWorkingDir:k||void 0})}),p=await c.json();if(p.ok&&p.chatId){I(p);let h=Array.isArray(p.invalidBotIds)?p.invalidBotIds:[],T=v.filter(R=>!h.includes(R)),H=new Set(T);typeof p.creator=="string"&&p.creator&&H.add(p.creator),S(p.chatId,$||p.chatId,T,p.creator),f(),E(p.chatId,H).catch(()=>{})}else alert(`Failed: ${p.error??c.status}`),s.close()}catch(c){alert("Network error: "+c),s.close()}};function S(b,u,$,k){let v=new Set($);k&&v.add(k);let A=j.bots.map(p=>({larkAppId:p.larkAppId,botName:p.botName,inChat:v.has(p.larkAppId),oncallChat:null})),c={chatId:b,name:u,ownerId:k??null,memberBots:A};j.chats=[c,...j.chats.filter(p=>p.chatId!==b)]}async function E(b,u){let $=[600,1200,1200,1200,1200,1200];for(let k of $){await new Promise(c=>setTimeout(c,k));let v;try{v=await Yt()}catch{continue}let A=(v.chats??[]).find(c=>c.chatId===b);if(A&&Qt(A,u)){j=v,f();return}}}}function I(l){let S=String(l.chatId),E=`https://applink.feishu.cn/client/chat/open?openChatId=${encodeURIComponent(S)}`,b=l.invalidBotIds??[],u=l.invalidUserIds??[],$=l.autoInvitedOpenId,k=!!l.autoInviteRejected,v=l.ownerTransferredTo,A=l.transferError,c=l.notifyMessageId,p=l.notifyError,h=Array.isArray(l.oncallBindings)?l.oncallBindings:[],T=h.filter(C=>C?.ok).length,H=h.filter(C=>!C?.ok),R=h.length>0?H.length===0?`<p class="hint-ok">\u5DF2\u7ED1\u5B9A\u76EE\u5F55\uFF1A<code>${m(l.bindResolvedPath??"")}</code>\uFF08${T}/${h.length} bots\uFF09</p>`:`<p class="hint-warn">\u76EE\u5F55\u7ED1\u5B9A\u90E8\u5206\u5931\u8D25\uFF1A\u6210\u529F ${T}/${h.length}\u3002${H.map(C=>`<br><code>${m(C.larkAppId??"?")}</code>: ${m(C.error??"unknown")}`).join("")}</p>`:"",O;if($){let C=v?"<br><small>\u7FA4\u4E3B\u5DF2\u4ECE\u673A\u5668\u4EBA\u8F6C\u8BA9\u7ED9\u4F60\u3002</small>":A?`<br><small class="hint-warn-inline">\u26A0 \u81EA\u52A8\u8F6C\u8BA9\u7FA4\u4E3B\u5931\u8D25\uFF08${m(A)}\uFF09\uFF0C\u4F60\u73B0\u5728\u662F\u6210\u5458\u4F46\u7FA4\u4E3B\u4ECD\u662F\u673A\u5668\u4EBA\u3002</small>`:"",P=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>`:"";O=`<p class="hint-ok">\u5DF2\u81EA\u52A8\u9080\u8BF7\u4F60\uFF08<code>${m($)}</code>\uFF09\u4F5C\u4E3A\u6210\u5458\u3002${C}${P}</p>`}else k?O='<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>':O='<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=[b.length?`<li>\u65E0\u6548 bot id: <code>${b.map(m).join(", ")}</code></li>`:"",u.length?`<li>\u65E0\u6548\u7528\u6237 open_id: <code>${u.map(m).join(", ")}</code></li>`:""].filter(Boolean).join("");s.innerHTML=`
226
226
  <article>
227
227
  <header><h3>${n("groups.successTitle")}</h3></header>
228
- <p><b>chatId:</b> <code>${p(L)}</code> <button type="button" data-copy="${p(L)}">${n("sessions.copy")}</button></p>
229
- <p><b>\u521B\u5EFA\u8005:</b> <code>${p(c.creator??"?")}</code></p>
228
+ <p><b>chatId:</b> <code>${m(S)}</code> <button type="button" data-copy="${m(S)}">${n("sessions.copy")}</button></p>
229
+ <p><b>\u521B\u5EFA\u8005:</b> <code>${m(l.creator??"?")}</code></p>
230
230
  ${O}
231
- ${H}
232
- ${q?`<ul>${q}</ul>`:""}
231
+ ${R}
232
+ ${B?`<ul>${B}</ul>`:""}
233
233
  <div class="actions">
234
234
  <a class="btn-link primary" href="${E}" target="_blank" rel="noopener">${n("groups.openGroup")}</a>
235
235
  <button type="button" id="g-create-close">${n("sessions.dismiss")}</button>
236
236
  </div>
237
- </article>`,s.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)}}),s.querySelector("#g-create-close").onclick=()=>s.close()}function g(){t.innerHTML=`<tr>
237
+ </article>`,s.querySelectorAll("[data-copy]").forEach(C=>{C.onclick=()=>{navigator.clipboard.writeText(C.dataset.copy??""),C.textContent=n("sessions.copied"),setTimeout(()=>{C.textContent=n("sessions.copy")},800)}}),s.querySelector("#g-create-close").onclick=()=>s.close()}function L(){t.innerHTML=`<tr>
238
238
  <th>${n("groups.chat")}</th>
239
- ${N.bots.map(c=>`<th>${p(c.botName??c.larkAppId)}</th>`).join("")}
239
+ ${j.bots.map(l=>`<th>${m(l.botName??l.larkAppId)}</th>`).join("")}
240
240
  <th>${n("groups.actions")}</th>
241
- </tr>`}function h(){g();let c=new FormData(r),L=(c.get("q")??"").toLowerCase(),E=!!c.get("missing"),k=N.chats.filter(u=>!L||(u.name??"").toLowerCase().includes(L)||u.chatId.toLowerCase().includes(L)||(u.ownerId??"").toLowerCase().includes(L)).filter(u=>!E||u.memberBots.some(S=>!S.inChat));if(k.length===0){o.innerHTML=`<tr><td colspan="${N.bots.length+2}" class="empty">${n("groups.empty")}</td></tr>`;return}o.innerHTML=k.map(u=>`<tr data-chat="${p(u.chatId)}">
241
+ </tr>`}function f(){L();let l=new FormData(r),S=(l.get("q")??"").toLowerCase(),E=!!l.get("missing"),b=j.chats.filter(u=>!S||(u.name??"").toLowerCase().includes(S)||u.chatId.toLowerCase().includes(S)||(u.ownerId??"").toLowerCase().includes(S)).filter(u=>!E||u.memberBots.some($=>!$.inChat));if(b.length===0){o.innerHTML=`<tr><td colspan="${j.bots.length+2}" class="empty">${n("groups.empty")}</td></tr>`;return}o.innerHTML=b.map(u=>`<tr data-chat="${m(u.chatId)}">
242
242
  <td>
243
- <strong>${p(u.name??u.chatId)}</strong><br>
244
- <small><code>${p(u.chatId)}</code></small>
243
+ <strong>${m(u.name??u.chatId)}</strong><br>
244
+ <small><code>${m(u.chatId)}</code></small>
245
245
  </td>
246
- ${N.bots.map(S=>{let y=u.memberBots.find(l=>l.larkAppId===S.larkAppId),$=y?y.error?"!":y.inChat?"\u2713":"\u2717":"?";return`<td class="${y?y.error?"cell-error":y.inChat?"cell-in":"cell-out":"cell-unknown"}" title="${p(y?.error??"")}">${$}</td>`}).join("")}
246
+ ${j.bots.map($=>{let k=u.memberBots.find(c=>c.larkAppId===$.larkAppId),v=k?k.error?"!":k.inChat?"\u2713":"\u2717":"?";return`<td class="${k?k.error?"cell-error":k.inChat?"cell-in":"cell-out":"cell-unknown"}" title="${m(k?.error??"")}">${v}</td>`}).join("")}
247
247
  <td>
248
248
  <button class="add-bots" type="button">${n("groups.addBots")}</button>
249
249
  <button class="manage-chat" type="button">${n("groups.manage")}</button>
250
250
  </td>
251
- </tr>`).join("")}h(),o.addEventListener("click",async c=>{let L=c.target.closest("button.add-bots");if(!L)return;let k=L.closest("tr[data-chat]").dataset.chat,u=N.chats.find($=>$.chatId===k);if(!u)return;let S=new Set(u.memberBots.filter($=>$.inChat).map($=>$.larkAppId));if(!N.bots.filter($=>!S.has($.larkAppId)).length){alert("All configured bots are already in this chat.");return}s.innerHTML=`
251
+ </tr>`).join("")}f(),o.addEventListener("click",async l=>{let S=l.target.closest("button.add-bots");if(!S)return;let b=S.closest("tr[data-chat]").dataset.chat,u=j.chats.find(v=>v.chatId===b);if(!u)return;let $=new Set(u.memberBots.filter(v=>v.inChat).map(v=>v.larkAppId));if(!j.bots.filter(v=>!$.has(v.larkAppId)).length){alert("All configured bots are already in this chat.");return}s.innerHTML=`
252
252
  <article>
253
- <header><h3>${n("groups.addBots")} \xB7 ${p(u.name??u.chatId)}</h3></header>
253
+ <header><h3>${n("groups.addBots")} \xB7 ${m(u.name??u.chatId)}</h3></header>
254
254
  <p>${n("groups.createHelp")}</p>
255
255
  <form id="g-addform">
256
- ${nt(N.bots,S)}
256
+ ${nt(j.bots,$)}
257
257
  <div class="actions">
258
258
  <button type="submit" class="primary">${n("groups.addBots")}</button>
259
259
  <button type="button" id="g-cancel">${n("groups.cancel")}</button>
260
260
  </div>
261
261
  </form>
262
- </article>`,s.showModal(),s.querySelector("#g-cancel").onclick=()=>s.close(),s.querySelector("#g-addform").onsubmit=async $=>{$.preventDefault();let l=new FormData($.target).getAll("bot");if(l.length===0){alert("Pick at least one bot.");return}try{let m=await(await fetch(`/api/groups/${encodeURIComponent(k)}/add-bots`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({larkAppIds:l})})).json();if(m.error==="no_proxy_bot")alert("No bot is currently in this chat \u2014 add one manually in Feishu first, then retry.");else if(m.result){let I=m.result.map(A=>`${A.id}: ${A.ok?"OK":`failed (${A.error??"unknown"})`}`).join(`
263
- `);alert(I),await ne(),h()}else alert(`Unexpected response: ${JSON.stringify(m)}`)}catch(f){alert("Network error: "+f)}finally{s.close()}}}),o.addEventListener("click",async c=>{let L=c.target.closest("button.manage-chat");if(!L)return;let k=L.closest("tr[data-chat]").dataset.chat,u=N.chats.find(S=>S.chatId===k);u&&v(u)});function v(c){let L=c.memberBots.filter(k=>k.inChat),E=typeof c.ownerId=="string"?c.ownerId:"";s.innerHTML=`
262
+ </article>`,s.showModal(),s.querySelector("#g-cancel").onclick=()=>s.close(),s.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(b)}/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 T=h.result.map(H=>`${H.id}: ${H.ok?"OK":`failed (${H.error??"unknown"})`}`).join(`
263
+ `);alert(T),await ie(),f()}else alert(`Unexpected response: ${JSON.stringify(h)}`)}catch(p){alert("Network error: "+p)}finally{s.close()}}}),o.addEventListener("click",async l=>{let S=l.target.closest("button.manage-chat");if(!S)return;let b=S.closest("tr[data-chat]").dataset.chat,u=j.chats.find($=>$.chatId===b);u&&g(u)});function g(l){let S=l.memberBots.filter(b=>b.inChat),E=typeof l.ownerId=="string"?l.ownerId:"";s.innerHTML=`
264
264
  <article>
265
- <header><h3>${n("groups.manageTitle",{name:c.name??c.chatId})}</h3></header>
266
- <p><b>chatId:</b> <code>${p(c.chatId)}</code></p>
267
- <p><b>${n("groups.owner")}:</b> <code>${p(c.ownerId??n("common.unknown"))}</code></p>
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>
268
268
 
269
269
  <fieldset>
270
270
  <legend>${n("groups.oncall")}</legend>
271
271
  <p><small>${n("groups.oncallHelp")}</small></p>
272
- ${L.length===0?'<p class="empty">\u6CA1\u6709\u673A\u5668\u4EBA\u5728\u7FA4\u91CC</p>':L.map(k=>{let u=!!k.oncallChat,S=k.oncallChat?.workingDir??"";return`
273
- <div class="oncall-row" data-bot="${p(k.larkAppId)}">
272
+ ${S.length===0?'<p class="empty">\u6CA1\u6709\u673A\u5668\u4EBA\u5728\u7FA4\u91CC</p>':S.map(b=>{let u=!!b.oncallChat,$=b.oncallChat?.workingDir??"";return`
273
+ <div class="oncall-row" data-bot="${m(b.larkAppId)}">
274
274
  <label class="checkbox-row">
275
275
  <input type="checkbox" data-action="toggle" ${u?"checked":""}>
276
- <strong>${p(k.botName??k.larkAppId)}</strong>
277
- <small>(${p(k.larkAppId)})</small>
276
+ <strong>${m(b.botName??b.larkAppId)}</strong>
277
+ <small>(${m(b.larkAppId)})</small>
278
278
  </label>
279
279
  <div class="oncall-row-body">
280
280
  <input type="text" data-input="workingDir" placeholder="e.g. /root/iserver/botmux"
281
- value="${p(S)}" ${u?"":"disabled"}>
281
+ value="${m($)}" ${u?"":"disabled"}>
282
282
  <button type="button" data-action="save">${n("groups.save")}</button>
283
283
  <span class="oncall-status" data-status></span>
284
284
  </div>
@@ -288,29 +288,29 @@ effects={effects}, activities={activities}, waits={waits}, cancels={cancels}`,"w
288
288
 
289
289
  <fieldset>
290
290
  <legend>${n("groups.leaveTitle")}</legend>
291
- ${L.length===0?'<p class="empty">\u6CA1\u6709\u673A\u5668\u4EBA\u5728\u7FA4\u91CC</p>':L.map(k=>`
291
+ ${S.length===0?'<p class="empty">\u6CA1\u6709\u673A\u5668\u4EBA\u5728\u7FA4\u91CC</p>':S.map(b=>`
292
292
  <label class="checkbox-row">
293
- <input type="checkbox" name="leave-bot" value="${p(k.larkAppId)}">
294
- ${p(k.botName??k.larkAppId)}
295
- <small>${k.larkAppId===E?"\xB7 \u7FA4\u4E3B":""}</small>
293
+ <input type="checkbox" name="leave-bot" value="${m(b.larkAppId)}">
294
+ ${m(b.botName??b.larkAppId)}
295
+ <small>${b.larkAppId===E?"\xB7 \u7FA4\u4E3B":""}</small>
296
296
  </label>
297
297
  `).join("")}
298
298
  </fieldset>
299
299
 
300
300
  <div class="actions">
301
- <button id="g-leave-btn" type="button" ${L.length===0?"disabled":""}>${n("groups.leaveSelected")}</button>
302
- <button id="g-disband-btn" type="button" class="contrast" ${L.length===0?"disabled":""}>${n("groups.disband")}</button>
301
+ <button id="g-leave-btn" type="button" ${S.length===0?"disabled":""}>${n("groups.leaveSelected")}</button>
302
+ <button id="g-disband-btn" type="button" class="contrast" ${S.length===0?"disabled":""}>${n("groups.disband")}</button>
303
303
  </div>
304
304
  <p class="hint-warn"><small>${n("groups.dangerHint")}</small></p>
305
305
  <form method="dialog"><button>${n("sessions.dismiss")}</button></form>
306
- </article>`,s.showModal(),s.querySelectorAll(".oncall-row").forEach(k=>{let u=k.dataset.bot,S=k.querySelector("input[data-action=toggle]"),y=k.querySelector("input[data-input=workingDir]"),$=k.querySelector("button[data-action=save]"),M=k.querySelector("[data-status]");S.addEventListener("change",()=>{y.disabled=!S.checked,S.checked&&y.focus()}),$.addEventListener("click",async()=>{M.textContent="",M.className="oncall-status";let l=S.checked,f=y.value.trim();if(l&&!f){M.textContent=n("groups.needWorkingDir"),M.classList.add("hint-warn-inline");return}$.disabled=!0;try{let m=`/api/groups/${encodeURIComponent(c.chatId)}/oncall/${encodeURIComponent(u)}`,I=l?await fetch(m,{method:"PUT",headers:{"content-type":"application/json"},body:JSON.stringify({workingDir:f})}):await fetch(m,{method:"DELETE"}),A=await I.json().catch(()=>({}));if(I.ok&&A.ok){M.textContent=l?`\u2713 \u5DF2\u7ED1\u5B9A \u2192 ${A.resolvedPath??f}`:"\u2713 \u5DF2\u89E3\u7ED1",M.classList.add("hint-ok");try{await ne(),h()}catch{}}else M.textContent=`\u2717 ${A.error??I.status}`,M.classList.add("hint-warn-inline")}catch(m){M.textContent=`\u2717 ${m?.message??m}`,M.classList.add("hint-warn-inline")}finally{$.disabled=!1}})}),s.querySelector("#g-leave-btn").onclick=async()=>{let k=[...s.querySelectorAll("input[name=leave-bot]:checked")].map(u=>u.value);if(k.length===0){alert("\u81F3\u5C11\u9009\u4E00\u4E2A\u673A\u5668\u4EBA");return}if(confirm(`\u786E\u5B9A\u8BA9 ${k.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 S=await(await fetch(`/api/groups/${encodeURIComponent(c.chatId)}/leave`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({larkAppIds:k})})).json(),y=(S.result??[]).map($=>{if(!$.ok)return`${$.larkAppId}: \u5931\u8D25 (${$.error??"unknown"})`;let M=$.closedSessions??[],l=M.filter(I=>!I.ok).length,f=M.length-l,m=M.length===0?"":l===0?`\uFF08\u5173\u95ED ${f} \u4E2A\u4F1A\u8BDD\uFF09`:`\uFF08\u5173\u95ED ${f} \u4E2A\uFF0C${l} \u4E2A\u5931\u8D25\uFF09`;return`${$.larkAppId}: OK${m}`}).join(`
307
- `);alert(y||`Unexpected: ${JSON.stringify(S)}`),await ne(),h()}catch(u){alert("Network error: "+u)}finally{s.close()}},s.querySelector("#g-disband-btn").onclick=async()=>{if(L.length===0||!confirm(`\u786E\u5B9A\u89E3\u6563\u7FA4\u804A\u300C${c.name??c.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 k=[...L].sort((S,y)=>(y.larkAppId===E?1:0)-(S.larkAppId===E?1:0)),u=[];for(let S of k)try{let y=await fetch(`/api/groups/${encodeURIComponent(c.chatId)}/disband`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({larkAppId:S.larkAppId})}),$=await y.json();if($.ok){let M=$.closedSessions??[],l=M.filter(I=>!I.ok).length,f=M.length-l,m=M.length===0?"":l===0?`
308
- \u5173\u95ED\u4E86 ${f} \u4E2A\u4F1A\u8BDD\u3002`:`
309
- \u5173\u95ED\u4E86 ${f} \u4E2A\u4F1A\u8BDD\uFF0C${l} \u4E2A\u4F1A\u8BDD\u5173\u95ED\u5931\u8D25\u3002`;alert(`\u5DF2\u89E3\u6563\uFF08\u7531 ${S.botName??S.larkAppId} \u6267\u884C\uFF09${m}`),await ne(),h(),s.close();return}u.push(`${S.botName??S.larkAppId}: ${$.error??y.status}`)}catch(y){u.push(`${S.botName??S.larkAppId}: ${y}`)}alert(`\u6240\u6709\u5728\u7FA4\u673A\u5668\u4EBA\u5747\u65E0\u6CD5\u89E3\u6563\uFF1A
306
+ </article>`,s.showModal(),s.querySelectorAll(".oncall-row").forEach(b=>{let u=b.dataset.bot,$=b.querySelector("input[data-action=toggle]"),k=b.querySelector("input[data-input=workingDir]"),v=b.querySelector("button[data-action=save]"),A=b.querySelector("[data-status]");$.addEventListener("change",()=>{k.disabled=!$.checked,$.checked&&k.focus()}),v.addEventListener("click",async()=>{A.textContent="",A.className="oncall-status";let c=$.checked,p=k.value.trim();if(c&&!p){A.textContent=n("groups.needWorkingDir"),A.classList.add("hint-warn-inline");return}v.disabled=!0;try{let h=`/api/groups/${encodeURIComponent(l.chatId)}/oncall/${encodeURIComponent(u)}`,T=c?await fetch(h,{method:"PUT",headers:{"content-type":"application/json"},body:JSON.stringify({workingDir:p})}):await fetch(h,{method:"DELETE"}),H=await T.json().catch(()=>({}));if(T.ok&&H.ok){A.textContent=c?`\u2713 \u5DF2\u7ED1\u5B9A \u2192 ${H.resolvedPath??p}`:"\u2713 \u5DF2\u89E3\u7ED1",A.classList.add("hint-ok");try{await ie(),f()}catch{}}else A.textContent=`\u2717 ${H.error??T.status}`,A.classList.add("hint-warn-inline")}catch(h){A.textContent=`\u2717 ${h?.message??h}`,A.classList.add("hint-warn-inline")}finally{v.disabled=!1}})}),s.querySelector("#g-leave-btn").onclick=async()=>{let b=[...s.querySelectorAll("input[name=leave-bot]:checked")].map(u=>u.value);if(b.length===0){alert("\u81F3\u5C11\u9009\u4E00\u4E2A\u673A\u5668\u4EBA");return}if(confirm(`\u786E\u5B9A\u8BA9 ${b.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 $=await(await fetch(`/api/groups/${encodeURIComponent(l.chatId)}/leave`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({larkAppIds:b})})).json(),k=($.result??[]).map(v=>{if(!v.ok)return`${v.larkAppId}: \u5931\u8D25 (${v.error??"unknown"})`;let A=v.closedSessions??[],c=A.filter(T=>!T.ok).length,p=A.length-c,h=A.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(k||`Unexpected: ${JSON.stringify($)}`),await ie(),f()}catch(u){alert("Network error: "+u)}finally{s.close()}},s.querySelector("#g-disband-btn").onclick=async()=>{if(S.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 b=[...S].sort(($,k)=>(k.larkAppId===E?1:0)-($.larkAppId===E?1:0)),u=[];for(let $ of b)try{let k=await fetch(`/api/groups/${encodeURIComponent(l.chatId)}/disband`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({larkAppId:$.larkAppId})}),v=await k.json();if(v.ok){let A=v.closedSessions??[],c=A.filter(T=>!T.ok).length,p=A.length-c,h=A.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 ${$.botName??$.larkAppId} \u6267\u884C\uFF09${h}`),await ie(),f(),s.close();return}u.push(`${$.botName??$.larkAppId}: ${v.error??k.status}`)}catch(k){u.push(`${$.botName??$.larkAppId}: ${k}`)}alert(`\u6240\u6709\u5728\u7FA4\u673A\u5668\u4EBA\u5747\u65E0\u6CD5\u89E3\u6563\uFF1A
310
310
  ${u.join(`
311
311
  `)}
312
312
 
313
- \u5EFA\u8BAE\u6539\u7528\u300C\u9000\u51FA\u7FA4\u804A\u300D\u3002`)}}r.addEventListener("input",h)}var Z={bots:[]},oe=null;function Xt(){return`<section class="page">
313
+ \u5EFA\u8BAE\u6539\u7528\u300C\u9000\u51FA\u7FA4\u804A\u300D\u3002`)}}r.addEventListener("input",f)}var ee={bots:[]},le=null;function Xt(){return`<section class="page">
314
314
  <div class="page-heading">
315
315
  <div>
316
316
  <p class="eyebrow">${n("nav.botDefaults")}</p>
@@ -322,57 +322,78 @@ ${u.join(`
322
322
  <input type="search" name="q" placeholder="${n("botDefaults.search")}" />
323
323
  <button type="button" id="bd-refresh">${n("botDefaults.refresh")}</button>
324
324
  </form>
325
- <p class="hint-warn" style="max-width:760px">
326
- ${n("botDefaults.warning")}
327
- </p>
328
325
  <div id="bd-list"></div>
329
- </section>`}async function at(){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}`,Z={bots:[]};return}if(!t||!Array.isArray(t.bots)){oe="unexpected response shape (no `bots` array)",Z={bots:[]};return}oe=null,Z=t}catch(e){oe=e?.message??String(e),Z={bots:[]}}}function st(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 at(),a()}finally{r.disabled=!1}},await at();function a(){let h=(new FormData(o).get("q")??"").toLowerCase(),v=Z.bots.filter(c=>!h||(c.botName??"").toLowerCase().includes(h)||(c.larkAppId??"").toLowerCase().includes(h));if(oe){t.innerHTML=`<p class="hint-warn">\u65E0\u6CD5\u52A0\u8F7D bot \u5217\u8868\uFF1A${p(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(v.length===0){t.innerHTML=`<p class="empty">${n("botDefaults.empty")}</p>`;return}t.innerHTML=v.map(s).join(""),T()}function s(g){if(g.error)return`<article class="bd-card" data-appid="${p(g.larkAppId)}">
330
- <header><strong>${p(g.botName??g.larkAppId)}</strong>
331
- <small>${p(g.larkAppId)}</small></header>
332
- <p class="hint-warn-inline">\u67E5\u8BE2\u5931\u8D25\uFF1A${p(g.error)}</p>
333
- </article>`;let h=g.defaultOncall??{enabled:!1,workingDir:"",since:0},v=!!h.enabled;return`<article class="bd-card" data-appid="${p(g.larkAppId)}">
326
+ </section>`}async function at(){try{let e=await fetch("/api/bots"),t=await e.json().catch(()=>({}));if(!e.ok){le=t?.error?`HTTP ${e.status}: ${t.error}${t.path?` (${t.path})`:""}`:`HTTP ${e.status}`,ee={bots:[]};return}if(!t||!Array.isArray(t.bots)){le="unexpected response shape (no `bots` array)",ee={bots:[]};return}le=null,ee=t}catch(e){le=e?.message??String(e),ee={bots:[]}}}function st(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 at(),a()}finally{r.disabled=!1}},await at();function a(){let g=(new FormData(o).get("q")??"").toLowerCase(),l=ee.bots.filter(S=>!g||(S.botName??"").toLowerCase().includes(g)||(S.larkAppId??"").toLowerCase().includes(g));if(le){t.innerHTML=`<p class="hint-warn">\u65E0\u6CD5\u52A0\u8F7D bot \u5217\u8868\uFF1A${m(le)}<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(l.length===0){t.innerHTML=`<p class="empty">${n("botDefaults.empty")}</p>`;return}t.innerHTML=l.map(s).join(""),L()}function s(f){if(f.error)return`<article class="bd-card" data-appid="${m(f.larkAppId)}">
327
+ <header><strong>${m(f.botName??f.larkAppId)}</strong>
328
+ <small>${m(f.larkAppId)}</small></header>
329
+ <p class="hint-warn-inline">\u67E5\u8BE2\u5931\u8D25\uFF1A${m(f.error)}</p>
330
+ </article>`;let g=f.defaultOncall??{enabled:!1,workingDir:"",since:0},l=!!g.enabled;return`<article class="bd-card" data-appid="${m(f.larkAppId)}">
334
331
  <header>
335
- <strong>${p(g.botName??g.larkAppId)}</strong>
336
- <small>${p(g.larkAppId)}</small>
332
+ <strong>${m(f.botName??f.larkAppId)}</strong>
333
+ <small>${m(f.larkAppId)}</small>
337
334
  </header>
338
335
  <div class="bd-body">
339
- <label class="checkbox-row">
340
- <input type="checkbox" data-action="toggle" ${v?"checked":""}>
341
- <strong>${n("botDefaults.defaultOncall")}</strong>
342
- <small>${n("botDefaults.defaultOncallHelp")}</small>
343
- </label>
344
- <div class="bd-row">
345
- <label>
346
- <span>${n("botDefaults.workingDir")}</span>
347
- <input type="text" data-input="workingDir" placeholder="e.g. /root/iserver/botmux"
348
- value="${p(h.workingDir??"")}" ${v?"":"disabled"}>
336
+ <section class="bd-section">
337
+ <h3 class="bd-section-title">${n("botDefaults.sectionOncall")}</h3>
338
+ <label class="checkbox-row">
339
+ <input type="checkbox" data-action="toggle" ${l?"checked":""}>
340
+ <strong>${n("botDefaults.defaultOncall")}</strong>
341
+ <small>${n("botDefaults.defaultOncallHelp")}</small>
349
342
  </label>
350
- </div>
351
- <div class="bd-meta">
352
- <small>${n("botDefaults.lastEnabled")}: ${p(st(h.since??0))}</small>
353
- <small>${n("botDefaults.autobound",{count:g.autoboundChatCount??0})}</small>
354
- </div>
343
+ <div class="bd-row">
344
+ <label>
345
+ <span>${n("botDefaults.workingDir")}</span>
346
+ <input type="text" data-input="workingDir" placeholder="e.g. /root/iserver/botmux"
347
+ value="${m(g.workingDir??"")}" ${l?"":"disabled"}>
348
+ </label>
349
+ </div>
350
+ <p class="bd-section-note">${n("botDefaults.warning")}</p>
351
+ <div class="bd-meta">
352
+ <small>${n("botDefaults.lastEnabled")}: ${m(st(g.since??0))}</small>
353
+ <small>${n("botDefaults.autobound",{count:f.autoboundChatCount??0})}</small>
354
+ </div>
355
+ <div class="actions">
356
+ <button type="button" data-action="save">${n("botDefaults.save")}</button>
357
+ <span class="oncall-status" data-status></span>
358
+ </div>
359
+ </section>
360
+ ${w(f)}
361
+ ${I(f)}
362
+ </div>
363
+ </article>`}function d(f){return f==null?n("botDefaults.brandStateDefault"):f.trim()===""?n("botDefaults.brandStateOff"):n("botDefaults.brandStateCustom")}function w(f){let g=f.brandLabel??null;return`<section class="bd-section">
364
+ <h3 class="bd-section-title">${n("botDefaults.sectionBrand")}</h3>
365
+ <div class="bd-row bd-brand">
366
+ <label>
367
+ <span>${n("botDefaults.brandLabel")}</span>
368
+ <input type="text" data-input="brandLabel"
369
+ placeholder="${m(n("botDefaults.brandLabelPlaceholder"))}"
370
+ value="${m(g??"")}">
371
+ </label>
372
+ <small data-brand-state>${m(d(g))}</small>
373
+ <small>${n("botDefaults.brandLabelHelp")}</small>
355
374
  <div class="actions">
356
- <button type="button" data-action="save">${n("botDefaults.save")}</button>
357
- <span class="oncall-status" data-status></span>
375
+ <button type="button" data-action="save-brand">${n("botDefaults.brandSave")}</button>
376
+ <button type="button" data-action="reset-brand">${n("botDefaults.brandReset")}</button>
377
+ <span class="oncall-status" data-brand-status></span>
358
378
  </div>
359
- ${w(g)}
360
379
  </div>
361
- </article>`}function d(g){return g==null?n("botDefaults.brandStateDefault"):g.trim()===""?n("botDefaults.brandStateOff"):n("botDefaults.brandStateCustom")}function w(g){let h=g.brandLabel??null;return`<div class="bd-row bd-brand">
362
- <label>
363
- <span>${n("botDefaults.brandLabel")}</span>
364
- <input type="text" data-input="brandLabel"
365
- placeholder="${p(n("botDefaults.brandLabelPlaceholder"))}"
366
- value="${p(h??"")}">
380
+ </section>`}function I(f){let g=f.disableStreamingCard===!0,l=f.writableTerminalLinkInCard===!0;return`<section class="bd-section">
381
+ <h3 class="bd-section-title">${n("botDefaults.sectionCard")}</h3>
382
+ <label class="checkbox-row">
383
+ <input type="checkbox" data-action="toggle-disable-streaming" ${g?"checked":""}>
384
+ <strong>${n("botDefaults.disableStreaming")}</strong>
385
+ <small>${n("botDefaults.disableStreamingHelp")}</small>
386
+ </label>
387
+ <label class="checkbox-row">
388
+ <input type="checkbox" data-action="toggle-writable-link" ${l?"checked":""} ${g?"disabled":""}>
389
+ <strong>${n("botDefaults.writableLink")}</strong>
390
+ <small>${n("botDefaults.writableLinkHelp")}</small>
367
391
  </label>
368
- <small data-brand-state>${p(d(h))}</small>
369
- <small>${n("botDefaults.brandLabelHelp")}</small>
370
392
  <div class="actions">
371
- <button type="button" data-action="save-brand">${n("botDefaults.brandSave")}</button>
372
- <button type="button" data-action="reset-brand">${n("botDefaults.brandReset")}</button>
373
- <span class="oncall-status" data-brand-status></span>
393
+ <small data-card-pref-moot class="hint-warn-inline" ${g?"":"hidden"}>${n("botDefaults.writableLinkMoot")}</small>
394
+ <span class="oncall-status" data-card-pref-status></span>
374
395
  </div>
375
- </div>`}function T(){t.querySelectorAll(".bd-card").forEach(g=>{let h=g.dataset.appid,v=g.querySelector("input[data-action=toggle]"),c=g.querySelector("input[data-input=workingDir]"),L=g.querySelector("button[data-action=save]"),E=g.querySelector("[data-status]");if(!v||!c||!L||!E)return;v.addEventListener("change",()=>{c.disabled=!v.checked,v.checked&&c.focus()}),L.addEventListener("click",async()=>{E.textContent="",E.className="oncall-status";let l=v.checked,f=c.value.trim();if(l&&!f){E.textContent=n("botDefaults.required"),E.classList.add("hint-warn-inline");return}L.disabled=!0;try{let m=await fetch(`/api/bots/${encodeURIComponent(h)}/default-oncall`,{method:"PUT",headers:{"content-type":"application/json"},body:JSON.stringify({enabled:l,workingDir:f})}),I=await m.json().catch(()=>({}));if(m.ok&&I.ok){let A=I.resolvedPath?` \u2192 ${I.resolvedPath}`:"";E.textContent=l?`\u2713 \u5DF2\u5F00\u542F${A}\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",E.classList.add("hint-ok");let H=Z.bots.find(q=>q.larkAppId===h);H&&I.defaultOncall&&(H.defaultOncall=I.defaultOncall);let O=g.querySelector(".bd-meta small:first-child");O&&I.defaultOncall?.since!=null&&(O.textContent=`\u4E0A\u6B21\u542F\u7528\u65F6\u95F4\uFF1A${st(I.defaultOncall.since)}`)}else E.textContent=`\u2717 ${I.error??m.status}`,E.classList.add("hint-warn-inline")}catch(m){E.textContent=`\u2717 ${m?.message??m}`,E.classList.add("hint-warn-inline")}finally{L.disabled=!1}});let k=g.querySelector("input[data-input=brandLabel]"),u=g.querySelector("button[data-action=save-brand]"),S=g.querySelector("button[data-action=reset-brand]"),y=g.querySelector("[data-brand-status]"),$=g.querySelector("[data-brand-state]");async function M(l,f){if(y){y.textContent="",y.className="oncall-status",f.disabled=!0;try{let m=await fetch(`/api/bots/${encodeURIComponent(h)}/brand-label`,{method:"PUT",headers:{"content-type":"application/json"},body:JSON.stringify({brandLabel:l})}),I=await m.json().catch(()=>({}));if(m.ok&&I.ok){let A=I.brandLabel??null;y.textContent="\u2713",y.classList.add("hint-ok"),k&&(k.value=A??""),$&&($.textContent=d(A));let H=Z.bots.find(O=>O.larkAppId===h);H&&(H.brandLabel=A)}else y.textContent=`\u2717 ${I.error??m.status}`,y.classList.add("hint-warn-inline")}catch(m){y.textContent=`\u2717 ${m?.message??m}`,y.classList.add("hint-warn-inline")}finally{f.disabled=!1}}}k&&u&&u.addEventListener("click",()=>M(k.value,u)),S&&S.addEventListener("click",()=>M(null,S))})}a(),o.addEventListener("input",a)}var xe=4096,ve=[],W=null,_=null,j="",ae=new Set;function Zt(){return`<section class="page roles-page">
396
+ </section>`}function L(){t.querySelectorAll(".bd-card").forEach(f=>{let g=f.dataset.appid,l=f.querySelector("input[data-action=toggle]"),S=f.querySelector("input[data-input=workingDir]"),E=f.querySelector("button[data-action=save]"),b=f.querySelector("[data-status]");if(!l||!S||!E||!b)return;l.addEventListener("change",()=>{S.disabled=!l.checked,l.checked&&S.focus()}),E.addEventListener("click",async()=>{b.textContent="",b.className="oncall-status";let O=l.checked,B=S.value.trim();if(O&&!B){b.textContent=n("botDefaults.required"),b.classList.add("hint-warn-inline");return}E.disabled=!0;try{let C=await fetch(`/api/bots/${encodeURIComponent(g)}/default-oncall`,{method:"PUT",headers:{"content-type":"application/json"},body:JSON.stringify({enabled:O,workingDir:B})}),P=await C.json().catch(()=>({}));if(C.ok&&P.ok){let U=P.resolvedPath?` \u2192 ${P.resolvedPath}`:"";b.textContent=O?`\u2713 \u5DF2\u5F00\u542F${U}\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",b.classList.add("hint-ok");let F=ee.bots.find(re=>re.larkAppId===g);F&&P.defaultOncall&&(F.defaultOncall=P.defaultOncall);let V=f.querySelector(".bd-meta small:first-child");V&&P.defaultOncall?.since!=null&&(V.textContent=`\u4E0A\u6B21\u542F\u7528\u65F6\u95F4\uFF1A${st(P.defaultOncall.since)}`)}else b.textContent=`\u2717 ${P.error??C.status}`,b.classList.add("hint-warn-inline")}catch(C){b.textContent=`\u2717 ${C?.message??C}`,b.classList.add("hint-warn-inline")}finally{E.disabled=!1}});let u=f.querySelector("input[data-input=brandLabel]"),$=f.querySelector("button[data-action=save-brand]"),k=f.querySelector("button[data-action=reset-brand]"),v=f.querySelector("[data-brand-status]"),A=f.querySelector("[data-brand-state]");async function c(O,B){if(v){v.textContent="",v.className="oncall-status",B.disabled=!0;try{let C=await fetch(`/api/bots/${encodeURIComponent(g)}/brand-label`,{method:"PUT",headers:{"content-type":"application/json"},body:JSON.stringify({brandLabel:O})}),P=await C.json().catch(()=>({}));if(C.ok&&P.ok){let U=P.brandLabel??null;v.textContent="\u2713",v.classList.add("hint-ok"),u&&(u.value=U??""),A&&(A.textContent=d(U));let F=ee.bots.find(V=>V.larkAppId===g);F&&(F.brandLabel=U)}else v.textContent=`\u2717 ${P.error??C.status}`,v.classList.add("hint-warn-inline")}catch(C){v.textContent=`\u2717 ${C?.message??C}`,v.classList.add("hint-warn-inline")}finally{B.disabled=!1}}}u&&$&&$.addEventListener("click",()=>c(u.value,$)),k&&k.addEventListener("click",()=>c(null,k));let p=f.querySelector("input[data-action=toggle-disable-streaming]"),h=f.querySelector("input[data-action=toggle-writable-link]"),T=f.querySelector("[data-card-pref-status]"),H=f.querySelector("[data-card-pref-moot]");async function R(O,B){if(T){T.textContent="",T.className="oncall-status",B.disabled=!0;try{let C=await fetch(`/api/bots/${encodeURIComponent(g)}/card-prefs`,{method:"PUT",headers:{"content-type":"application/json"},body:JSON.stringify(O)}),P=await C.json().catch(()=>({}));if(C.ok&&P.ok){T.textContent=`\u2713 ${n("botDefaults.cardPrefSaved")}`,T.classList.add("hint-ok");let U=ee.bots.find(F=>F.larkAppId===g);U&&(U.disableStreamingCard=P.disableStreamingCard,U.writableTerminalLinkInCard=P.writableTerminalLinkInCard)}else T.textContent=`\u2717 ${P.error??C.status}`,T.classList.add("hint-warn-inline")}catch(C){T.textContent=`\u2717 ${C?.message??C}`,T.classList.add("hint-warn-inline")}finally{B===h?B.disabled=!!p?.checked:B.disabled=!1}}}p&&p.addEventListener("change",()=>{let O=p.checked;h&&(h.disabled=O),H&&(H.hidden=!O),R({disableStreamingCard:O},p)}),h&&h.addEventListener("change",()=>{R({writableTerminalLinkInCard:h.checked},h)})})}a(),o.addEventListener("input",a)}var xe=4096,ke=[],G=null,z=null,W="",de=new Set;function Zt(){return`<section class="page roles-page">
376
397
  <div class="page-heading">
377
398
  <div>
378
399
  <p class="eyebrow">${n("nav.roles")}</p>
@@ -411,35 +432,35 @@ ${u.join(`
411
432
  </div>
412
433
  </div>
413
434
  </div>
414
- </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 ee(e=""){let t=document.getElementById("roles-tree");if(!t)return;let o=e.toLowerCase(),r=ve.filter(a=>{if(!o)return!0;let s=a.chatId.toLowerCase().includes(o)||(a.name??"").toLowerCase().includes(o),d=a.memberBots.some(w=>w.larkAppId.toLowerCase().includes(o)||(w.botName??"").toLowerCase().includes(o));return s||d});if(r.length===0){t.innerHTML=`<div class="roles-empty">${n("roles.noChats")}</div>`;return}t.innerHTML=r.map(a=>{let s=ae.has(a.chatId),d=a.memberBots.filter(v=>v.inChat),w=s?"\u25BE":"\u25B8",T=dt(a),g=nn(a),h=s?d.map(v=>`
415
- <div class="roles-bot-row ${W===a.chatId&&_===v.larkAppId?"selected":""}"
416
- data-group-id="${p(a.chatId)}"
417
- data-bot-id="${p(v.larkAppId)}">
435
+ </section>`}async function ye(){ke=((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 ae(e=""){let t=document.getElementById("roles-tree");if(!t)return;let o=e.toLowerCase(),r=ke.filter(a=>{if(!o)return!0;let s=a.chatId.toLowerCase().includes(o)||(a.name??"").toLowerCase().includes(o),d=a.memberBots.some(w=>w.larkAppId.toLowerCase().includes(o)||(w.botName??"").toLowerCase().includes(o));return s||d});if(r.length===0){t.innerHTML=`<div class="roles-empty">${n("roles.noChats")}</div>`;return}t.innerHTML=r.map(a=>{let s=de.has(a.chatId),d=a.memberBots.filter(g=>g.inChat),w=s?"\u25BE":"\u25B8",I=dt(a),L=nn(a),f=s?d.map(g=>`
436
+ <div class="roles-bot-row ${G===a.chatId&&z===g.larkAppId?"selected":""}"
437
+ data-group-id="${m(a.chatId)}"
438
+ data-bot-id="${m(g.larkAppId)}">
418
439
  <span class="roles-bot-indent"></span>
419
440
  <span class="roles-bot-icon">\u{1F916}</span>
420
441
  <div class="roles-bot-info">
421
- <div class="roles-bot-name">${p(v.botName)}</div>
422
- <div class="roles-bot-id">${p(v.larkAppId)}</div>
442
+ <div class="roles-bot-name">${m(g.botName)}</div>
443
+ <div class="roles-bot-id">${m(g.larkAppId)}</div>
423
444
  </div>
424
- <span class="roles-badge ${v.hasRole?"has-role":"no-role"}">
425
- ${v.hasRole?n("roles.configured"):n("roles.unconfigured")}
445
+ <span class="roles-badge ${g.hasRole?"has-role":"no-role"}">
446
+ ${g.hasRole?n("roles.configured"):n("roles.unconfigured")}
426
447
  </span>
427
448
  </div>`).join(""):"";return`
428
449
  <div class="roles-group-section">
429
- <div class="roles-group-row ${s?"expanded":""} ${W===a.chatId&&!_?"selected":""}"
430
- data-group-id="${p(a.chatId)}">
450
+ <div class="roles-group-row ${s?"expanded":""} ${G===a.chatId&&!z?"selected":""}"
451
+ data-group-id="${m(a.chatId)}">
431
452
  <span class="roles-group-arrow">${w}</span>
432
453
  <span class="roles-group-icon">\u{1F4C1}</span>
433
454
  <div class="roles-group-info">
434
- <div class="roles-group-name">${p(a.name??a.chatId)}</div>
455
+ <div class="roles-group-name">${m(a.name??a.chatId)}</div>
435
456
  <div class="roles-group-meta">
436
- ${T}/${g} ${n("roles.botsWithRoles")}
457
+ ${I}/${L} ${n("roles.botsWithRoles")}
437
458
  </div>
438
459
  </div>
439
460
  <span class="roles-group-chevron"></span>
440
461
  </div>
441
- <div class="roles-bot-list">${h}</div>
442
- </div>`}).join(""),t.querySelectorAll(".roles-group-row").forEach(a=>{a.addEventListener("click",()=>{let s=a.dataset.groupId;s&&(ae.has(s)?ae.delete(s):ae.add(s),ee(document.getElementById("roles-search")?.value??""))})}),t.querySelectorAll(".roles-bot-row").forEach(a=>{a.addEventListener("click",s=>{s.stopPropagation();let d=a.dataset.groupId,w=a.dataset.botId;d&&w&&on(d,w)})})}async function on(e,t){W=e,_=t;let o=await lt(t,e),r=document.getElementById("roles-editor-empty"),a=document.getElementById("roles-editor-form"),s=document.getElementById("roles-editor-textarea"),d=document.getElementById("roles-editor-group-name"),w=document.getElementById("roles-editor-bot-name"),T=document.getElementById("roles-editor-chat-id");r&&(r.style.display="none"),a&&(a.style.display="");let g=ve.find(c=>c.chatId===e),h=g?.memberBots.find(c=>c.larkAppId===t);d&&(d.textContent=g?.name??e),w&&(w.textContent=h?.botName??t),T&&(T.textContent=`${e} \xB7 ${t}`),j=o.content??"",s&&(s.value=j,s.focus()),De(),Oe(),ee(document.getElementById("roles-search")?.value??"");let v=document.getElementById("roles-delete");v&&(v.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":""}`,an(t)}function an(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>${p(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(),ae.clear(),it(),await be();for(let t of ve)dt(t)>0&&ae.add(t.chatId);ee(),document.getElementById("roles-search")?.addEventListener("input",t=>{ee(t.target.value)}),document.getElementById("roles-refresh")?.addEventListener("click",async()=>{if(await be(),ee(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(),ee(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(),ee(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 sn(){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`
462
+ <div class="roles-bot-list">${f}</div>
463
+ </div>`}).join(""),t.querySelectorAll(".roles-group-row").forEach(a=>{a.addEventListener("click",()=>{let s=a.dataset.groupId;s&&(de.has(s)?de.delete(s):de.add(s),ae(document.getElementById("roles-search")?.value??""))})}),t.querySelectorAll(".roles-bot-row").forEach(a=>{a.addEventListener("click",s=>{s.stopPropagation();let d=a.dataset.groupId,w=a.dataset.botId;d&&w&&on(d,w)})})}async function on(e,t){G=e,z=t;let o=await lt(t,e),r=document.getElementById("roles-editor-empty"),a=document.getElementById("roles-editor-form"),s=document.getElementById("roles-editor-textarea"),d=document.getElementById("roles-editor-group-name"),w=document.getElementById("roles-editor-bot-name"),I=document.getElementById("roles-editor-chat-id");r&&(r.style.display="none"),a&&(a.style.display="");let L=ke.find(l=>l.chatId===e),f=L?.memberBots.find(l=>l.larkAppId===t);d&&(d.textContent=L?.name??e),w&&(w.textContent=f?.botName??t),I&&(I.textContent=`${e} \xB7 ${t}`),W=o.content??"",s&&(s.value=W,s.focus()),Oe(),Be(),ae(document.getElementById("roles-search")?.value??"");let g=document.getElementById("roles-delete");g&&(g.style.display=o.hasRole?"":"none")}function Oe(){let e=document.getElementById("roles-editor-bytecount");if(!e)return;let t=new TextEncoder().encode(W).length;e.textContent=`${t} / ${xe} bytes`,e.className=`roles-bytecount ${t>3800?"warn":""} ${t>xe?"over":""}`,an(t)}function an(e){let t=document.getElementById("roles-save");if(!t)return;let o=e??new TextEncoder().encode(W).length;t.disabled=o>xe||W.trim().length===0}function Be(){let e=document.getElementById("roles-preview");e&&(W.trim()?e.innerHTML=`<strong>${n("roles.preview")}</strong><pre>${m(W)}</pre>`:e.innerHTML=`<small>${n("roles.previewEmpty")}</small>`)}function it(){G=null,z=null,W="";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(),de.clear(),it(),await ye();for(let t of ke)dt(t)>0&&de.add(t.chatId);ae(),document.getElementById("roles-search")?.addEventListener("input",t=>{ae(t.target.value)}),document.getElementById("roles-refresh")?.addEventListener("click",async()=>{if(await ye(),ae(document.getElementById("roles-search")?.value??""),G&&z){let t=await lt(z,G),o=document.getElementById("roles-editor-textarea");o&&(o.value=t.content??""),W=t.content??"",Oe(),Be();let r=document.getElementById("roles-delete");r&&(r.style.display=t.hasRole?"":"none")}}),document.getElementById("roles-save")?.addEventListener("click",async function(){if(!(!G||!z)){this.disabled=!0,this.textContent="...";try{if(await en(z,G,W)){await ye(),ae(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=W.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(!(!G||!z)&&confirm(n("roles.confirmDelete"))){this.disabled=!0,this.textContent="...";try{await tn(z,G)&&(await ye(),it(),ae(document.getElementById("roles-search")?.value??""))}finally{this.disabled=!1,this.textContent=n("roles.delete")}}}),document.getElementById("roles-editor-textarea")?.addEventListener("input",t=>{W=t.target.value,Oe(),Be()})}function sn(){let e=[["",n("workflow.filter.nonTerminal")],["all",n("workflow.filter.all")],["pending",te("pending")],["running",te("running")],["waiting",te("waiting")],["succeeded",te("succeeded")],["failed",te("failed")],["cancelled",te("cancelled")]];return`
443
464
  <nav class="wf-subnav">
444
465
  <a href="#/workflows" class="active" data-i18n="workflow.subnav.runs">${i(n("workflow.subnav.runs"))}</a>
445
466
  <a href="#/workflows/catalog" data-i18n="workflow.subnav.catalog">${i(n("workflow.subnav.catalog"))}</a>
@@ -459,15 +480,15 @@ ${u.join(`
459
480
  </tr></thead>
460
481
  <tbody id="wf-tbody"></tbody>
461
482
  </table>
462
- `}var rn=5e3,ln=2e3,ie=new Set(["succeeded","failed","cancelled"]);function i(e){return e.replace(/[&<>"']/g,t=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"})[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=sn();let t=e.querySelector("#wf-tbody"),o=e.querySelector("#wf-filters"),r=e.querySelector("#wf-last-load"),a=[],s=null,d=!1,w=null,T=!1;function g(k){let S=(new FormData(o).get("q")??"").trim().toLowerCase();return S?k.filter(y=>y.runId.toLowerCase().includes(S)||y.workflowId.toLowerCase().includes(S)||(y.chatId??"").toLowerCase().includes(S)):k}function h(){let k=g(a);if(k.length===0){t.innerHTML=`<tr><td colspan="7" class="empty">${w?i(n("workflow.list.failedLoad",{error:w})):a.length===0?i(n("workflow.list.noRuns")):i(n("workflow.list.noFilterMatch"))}</td></tr>`;return}t.innerHTML=k.map(u=>{let S=`${u.dEf}/${u.dAct}/${u.dWait}`,y=u.dEf+u.dAct+u.dWait>0?"wf-dangling has":"wf-dangling none",$=[];u.chatId&&$.push(i(u.chatId)),u.larkAppId&&$.push(`<span class="muted">${i(u.larkAppId)}</span>`);let M=$.length>0?$.join("<br/>"):"\u2014",l=pn(u);return`<tr data-runid="${i(u.runId)}">
483
+ `}var rn=5e3,ln=2e3,fe=new Set(["succeeded","failed","cancelled"]);function i(e){return e.replace(/[&<>"']/g,t=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"})[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 Y(e){return`<span class="${fe.has(e)?"wf-status terminal":"wf-status live"} wf-status-${i(e)}">${i(te(e))}</span>`}function te(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=sn();let t=e.querySelector("#wf-tbody"),o=e.querySelector("#wf-filters"),r=e.querySelector("#wf-last-load"),a=[],s=null,d=!1,w=null,I=!1;function L(b){let $=(new FormData(o).get("q")??"").trim().toLowerCase();return $?b.filter(k=>k.runId.toLowerCase().includes($)||k.workflowId.toLowerCase().includes($)||(k.chatId??"").toLowerCase().includes($)):b}function f(){let b=L(a);if(b.length===0){t.innerHTML=`<tr><td colspan="7" class="empty">${w?i(n("workflow.list.failedLoad",{error:w})):a.length===0?i(n("workflow.list.noRuns")):i(n("workflow.list.noFilterMatch"))}</td></tr>`;return}t.innerHTML=b.map(u=>{let $=`${u.dEf}/${u.dAct}/${u.dWait}`,k=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 A=v.length>0?v.join("<br/>"):"\u2014",c=pn(u);return`<tr data-runid="${i(u.runId)}">
463
484
  <td><a href="#/workflows/${encodeURIComponent(u.runId)}"><code>${i(u.runId)}</code></a></td>
464
485
  <td>${i(u.workflowId)}</td>
465
- <td>${G(u.status)}${u.failedNodeId?` <span class="muted">(${i(u.failedNodeId)})</span>`:""}${l}</td>
486
+ <td>${Y(u.status)}${u.failedNodeId?` <span class="muted">(${i(u.failedNodeId)})</span>`:""}${c}</td>
466
487
  <td>${u.lastSeq}</td>
467
- <td class="${y}">${S}</td>
488
+ <td class="${k}">${$}</td>
468
489
  <td title="${i(new Date(u.updatedAt).toISOString())}">${dn(u.updatedAt)}</td>
469
- <td>${M}</td>
470
- </tr>`}).join("")}function v(){w?(r.textContent=n("workflow.list.error",{error:w}),r.classList.add("error")):(r.textContent=n("workflow.list.loaded",{count:a.length,time:new Date().toLocaleTimeString()}),r.classList.remove("error"))}async function c(){if(!(T||d)&&!document.hidden){d=!0;try{let k=o.elements.namedItem("status")?.value??"",u=new URLSearchParams;k==="all"?u.set("all","1"):k&&u.set("status",k);let S="/api/workflows/runs"+(u.toString()?`?${u}`:""),y=await fetch(S);y.ok?(a=(await y.json()).runs??[],w=null):(w=`HTTP ${y.status}`,a=[])}catch(k){w=k?.message??String(k),a=[]}finally{d=!1,T||(h(),v())}}}function L(){s!==null&&window.clearTimeout(s),s=window.setTimeout(async()=>{await c(),T||L()},rn)}function E(){document.hidden||c()}return o.addEventListener("input",()=>{h()}),o.addEventListener("change",k=>{k.target.getAttribute("name")==="status"&&c()}),document.addEventListener("visibilitychange",E),c().then(()=>{T||L()}),()=>{T=!0,s!==null&&window.clearTimeout(s),document.removeEventListener("visibilitychange",E)}}function un(e,t,o={}){e.innerHTML=`
490
+ <td>${A}</td>
491
+ </tr>`}).join("")}function g(){w?(r.textContent=n("workflow.list.error",{error:w}),r.classList.add("error")):(r.textContent=n("workflow.list.loaded",{count:a.length,time:new Date().toLocaleTimeString()}),r.classList.remove("error"))}async function l(){if(!(I||d)&&!document.hidden){d=!0;try{let b=o.elements.namedItem("status")?.value??"",u=new URLSearchParams;b==="all"?u.set("all","1"):b&&u.set("status",b);let $="/api/workflows/runs"+(u.toString()?`?${u}`:""),k=await fetch($);k.ok?(a=(await k.json()).runs??[],w=null):(w=`HTTP ${k.status}`,a=[])}catch(b){w=b?.message??String(b),a=[]}finally{d=!1,I||(f(),g())}}}function S(){s!==null&&window.clearTimeout(s),s=window.setTimeout(async()=>{await l(),I||S()},rn)}function E(){document.hidden||l()}return o.addEventListener("input",()=>{f()}),o.addEventListener("change",b=>{b.target.getAttribute("name")==="status"&&l()}),document.addEventListener("visibilitychange",E),l().then(()=>{I||S()}),()=>{I=!0,s!==null&&window.clearTimeout(s),document.removeEventListener("visibilitychange",E)}}function un(e,t,o={}){e.innerHTML=`
471
492
  <div class="wf-detail-head">
472
493
  <a class="btn-link" href="#/workflows">${i(n("workflow.detail.back"))}</a>
473
494
  <div>
@@ -523,84 +544,84 @@ ${u.join(`
523
544
  </div>
524
545
  <div id="wf-event-meta" class="muted"></div>
525
546
  </section>
526
- `;let r=e.querySelector("#wf-detail-subtitle"),a=e.querySelector("#wf-detail-refresh"),s=e.querySelector("#wf-detail-error"),d=e.querySelector("#wf-cancel-status"),w=e.querySelector("#wf-summary"),T=e.querySelector("#wf-dangling-panel"),g=e.querySelector("#wf-parallel-view"),h=e.querySelector("#wf-parallel-meta"),v=e.querySelector("#wf-node-tbody"),c=e.querySelector("#wf-io-list"),L=e.querySelector(".wf-timeline-scroll"),E=e.querySelector("#wf-event-tbody"),k=e.querySelector("#wf-event-meta"),u=e.querySelector("#wf-cancel-run"),S=e.querySelector("#wf-load-older"),y=null,$=[],M=new Set,l=null,f=null,m=!1,I=0,A=null,H=!1,O=!1,q=!1,D=new Set,Te=new Map,Ne=new Map,ue=new Map,fe=new Set,pe=new Map,X=new Set,se=new Map,At=new Map,je=0,Ue=o.focusAttemptId;function U(b){if(!b){s.hidden=!0,s.textContent="";return}s.hidden=!1,s.textContent=b}function Fe(b){if(!b){d.hidden=!0,d.textContent="";return}d.hidden=!1,d.textContent=b}async function We(){let b=await fetch(`/api/workflows/runs/${encodeURIComponent(t)}/snapshot`);if(b.status===404)throw new Error(n("workflow.detail.unknownRun"));if(!b.ok)throw new Error(n("workflow.detail.snapshotHttp",{status:b.status}));y=await b.json()}async function me(b){let P=await fetch(`/api/workflows/runs/${encodeURIComponent(t)}/events?${b}`);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(b,P){let x=b.filter(R=>M.has(R.eventId)?!1:(M.add(R.eventId),!0));x.length!==0&&($=P==="prepend"?[...x,...$]:[...$,...x],$.sort((R,z)=>le(R.eventId)-le(z.eventId)))}async function Ht(){await We();let b=await me(new URLSearchParams({tail:"100"}));$=[],M=new Set,we(b.events,"append"),l=b.oldestSeq,f=b.newestSeq,m=b.hasOlder,I=b.totalCount,F()}async function ge(){if(!(H||O||document.hidden)){O=!0;try{if(await We(),f!==null){let b=await me(new URLSearchParams({afterSeq:String(f),limit:"200"}));we(b.events,"append"),b.newestSeq!==null&&(f=b.newestSeq),l===null&&b.oldestSeq!==null&&(l=b.oldestSeq),I=b.totalCount}else{let b=await me(new URLSearchParams({tail:"1"}));we(b.events,"append"),l=b.oldestSeq,f=b.newestSeq,m=b.hasOlder,I=b.totalCount}U(null),F()}catch(b){U(b?.message??String(b))}finally{O=!1}}}async function Rt(){if(!(l===null||!m)){S.disabled=!0;try{let b=await me(new URLSearchParams({beforeSeq:String(l),limit:"100"}));we(b.events,"prepend"),b.oldestSeq!==null&&(l=b.oldestSeq),m=b.hasOlder,I=b.totalCount,U(null),F()}catch(b){U(b?.message??String(b))}finally{S.disabled=!1}}}async function xt(){if(!y||ie.has(y.run.status)||q)return;if(!y.chatBinding?.larkAppId){U(n("workflow.detail.cancelUnavailable",{runId:t}));return}let b=mn(y),P=n("workflow.detail.cancelConfirm",{runId:t,...b});if(window.confirm(P)){q=!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 R=await x.json().catch(()=>({}));if(!x.ok||!R.ok)throw new Error(R.hint??R.error??n("workflow.detail.cancelHttp",{status:x.status}));Fe(R.pending?n("workflow.detail.cancelPending"):null),U(null),await ge()}catch(x){U(x?.message??String(x))}finally{q=!1,u.disabled=!1,F()}}}async function Dt(b,P){if(!X.has(b)){X.add(b),se.delete(b),F();try{let x=await fetch(`/api/workflows/runs/${encodeURIComponent(t)}/attempts/${encodeURIComponent(P)}/${encodeURIComponent(b)}/resume`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({})});if(x.status===401)throw new Error(n("workflow.detail.writeAccessResume"));let R=await x.json().catch(()=>({}));if(!x.ok||!R.ok||!R.resumeId||!R.url)throw new Error(R.hint??R.message??R.error??n("workflow.detail.resumeStartFailed",{status:x.status}));pe.set(b,{resumeId:R.resumeId,url:R.url})}catch(x){let R=x?.message??String(x);se.set(b,R)}finally{X.delete(b),F()}}}async function Ot(b,P){if(!X.has(b)){X.add(b),se.delete(b),F();try{let x=await fetch(`/api/workflows/runs/${encodeURIComponent(t)}/attempts/${encodeURIComponent(P)}/${encodeURIComponent(b)}/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 R=await x.json().catch(()=>({}));if(!x.ok||!R.ok)if(R.error==="resume_not_running")pe.delete(b);else throw new Error(R.hint??R.message??R.error??n("workflow.detail.resumeEndFailed",{status:x.status}));else pe.delete(b)}catch(x){let R=x?.message??String(x);se.set(b,R)}finally{X.delete(b),F()}}}async function Bt(b,P){if(!fe.has(b)){fe.add(b),ue.delete(b),F();try{let x=Ne.get(b)?.trim()||void 0,R=await fetch(`/api/workflows/runs/${encodeURIComponent(t)}/${P}`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({comment:x})});if(R.status===401)throw new Error(n("workflow.detail.writeAccessApproval"));let z=await R.json().catch(()=>({}));if(!R.ok||!z.ok)throw new Error(z.hint??z.message??z.error??n("workflow.detail.actionHttp",{action:P,status:R.status}));let Ee=P==="approve"?n("workflow.detail.approved"):n("workflow.detail.rejected");ue.set(b,{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 R=x?.message??String(x);ue.set(b,{kind:"error",text:R}),U(R)}finally{fe.delete(b),F()}}}function F(){if(!y)return;je=L.scrollTop;let b=y.run;ie.has(b.status)&&Fe(null),r.innerHTML=`${i(b.workflowId??"?")} \xB7 ${G(b.status)} \xB7 lastSeq ${y.lastSeq}`,a.textContent=n("workflow.detail.refreshed",{time:new Date().toLocaleTimeString()}),u.hidden=ie.has(b.status),u.disabled=q||!y.chatBinding?.larkAppId,u.textContent=y.chatBinding?.larkAppId?n("workflow.detail.cancel"):n("workflow.detail.cliCancelOnly"),u.title=y.chatBinding?.larkAppId?n("workflow.detail.cancelTitle"):n("workflow.detail.cliCancelTitle",{runId:t}),fn(w,y),wn(T,y),gn(g,h,y,$),$n(v,y),Sn(c,y,D,Te,{comments:Ne,statuses:ue,resolving:fe,onResolve:Bt},{sessions:pe,pending:X,errors:se,onStart:Dt,onEnd:Ot},Ue,At)&&(Ue=void 0),Vn(E,$),L.scrollTop=je,S.hidden=!m,k.textContent=n("workflow.detail.eventsLoaded",{loaded:$.length,total:I})}function Le(){if(A!==null&&window.clearTimeout(A),y&&ie.has(y.run.status)){A=null;return}A=window.setTimeout(async()=>{await ge(),H||Le()},ln)}function _e(){document.hidden||ge().then(()=>{!H&&A===null&&Le()})}return S.addEventListener("click",()=>{Rt()}),u.addEventListener("click",()=>{xt()}),document.addEventListener("visibilitychange",_e),Ht().then(()=>{U(null),H||Le()}).catch(b=>{U(b?.message??String(b)),r.textContent=n("workflow.detail.loadFailed")}),()=>{H=!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(([a,s])=>`<div class="wf-summary-item"><span>${a}</span><strong>${s}</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">
547
+ `;let r=e.querySelector("#wf-detail-subtitle"),a=e.querySelector("#wf-detail-refresh"),s=e.querySelector("#wf-detail-error"),d=e.querySelector("#wf-cancel-status"),w=e.querySelector("#wf-summary"),I=e.querySelector("#wf-dangling-panel"),L=e.querySelector("#wf-parallel-view"),f=e.querySelector("#wf-parallel-meta"),g=e.querySelector("#wf-node-tbody"),l=e.querySelector("#wf-io-list"),S=e.querySelector(".wf-timeline-scroll"),E=e.querySelector("#wf-event-tbody"),b=e.querySelector("#wf-event-meta"),u=e.querySelector("#wf-cancel-run"),$=e.querySelector("#wf-load-older"),k=null,v=[],A=new Set,c=null,p=null,h=!1,T=0,H=null,R=!1,O=!1,B=!1,C=new Set,P=new Map,U=new Map,F=new Map,V=new Set,re=new Map,oe=new Set,ce=new Map,At=new Map,je=0,Ue=o.focusAttemptId;function _(y){if(!y){s.hidden=!0,s.textContent="";return}s.hidden=!1,s.textContent=y}function Fe(y){if(!y){d.hidden=!0,d.textContent="";return}d.hidden=!1,d.textContent=y}async function We(){let y=await fetch(`/api/workflows/runs/${encodeURIComponent(t)}/snapshot`);if(y.status===404)throw new Error(n("workflow.detail.unknownRun"));if(!y.ok)throw new Error(n("workflow.detail.snapshotHttp",{status:y.status}));k=await y.json()}async function ge(y){let N=await fetch(`/api/workflows/runs/${encodeURIComponent(t)}/events?${y}`);if(N.status===404)throw new Error(n("workflow.detail.unknownRun"));if(!N.ok)throw new Error(n("workflow.detail.eventsHttp",{status:N.status}));return await N.json()}function he(y,N){let x=y.filter(D=>A.has(D.eventId)?!1:(A.add(D.eventId),!0));x.length!==0&&(v=N==="prepend"?[...x,...v]:[...v,...x],v.sort((D,Q)=>pe(D.eventId)-pe(Q.eventId)))}async function Ht(){await We();let y=await ge(new URLSearchParams({tail:"100"}));v=[],A=new Set,he(y.events,"append"),c=y.oldestSeq,p=y.newestSeq,h=y.hasOlder,T=y.totalCount,J()}async function be(){if(!(R||O||document.hidden)){O=!0;try{if(await We(),p!==null){let y=await ge(new URLSearchParams({afterSeq:String(p),limit:"200"}));he(y.events,"append"),y.newestSeq!==null&&(p=y.newestSeq),c===null&&y.oldestSeq!==null&&(c=y.oldestSeq),T=y.totalCount}else{let y=await ge(new URLSearchParams({tail:"1"}));he(y.events,"append"),c=y.oldestSeq,p=y.newestSeq,h=y.hasOlder,T=y.totalCount}_(null),J()}catch(y){_(y?.message??String(y))}finally{O=!1}}}async function Rt(){if(!(c===null||!h)){$.disabled=!0;try{let y=await ge(new URLSearchParams({beforeSeq:String(c),limit:"100"}));he(y.events,"prepend"),y.oldestSeq!==null&&(c=y.oldestSeq),h=y.hasOlder,T=y.totalCount,_(null),J()}catch(y){_(y?.message??String(y))}finally{$.disabled=!1}}}async function Dt(){if(!k||fe.has(k.run.status)||B)return;if(!k.chatBinding?.larkAppId){_(n("workflow.detail.cancelUnavailable",{runId:t}));return}let y=mn(k),N=n("workflow.detail.cancelConfirm",{runId:t,...y});if(window.confirm(N)){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 D=await x.json().catch(()=>({}));if(!x.ok||!D.ok)throw new Error(D.hint??D.error??n("workflow.detail.cancelHttp",{status:x.status}));Fe(D.pending?n("workflow.detail.cancelPending"):null),_(null),await be()}catch(x){_(x?.message??String(x))}finally{B=!1,u.disabled=!1,J()}}}async function xt(y,N){if(!oe.has(y)){oe.add(y),ce.delete(y),J();try{let x=await fetch(`/api/workflows/runs/${encodeURIComponent(t)}/attempts/${encodeURIComponent(N)}/${encodeURIComponent(y)}/resume`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({})});if(x.status===401)throw new Error(n("workflow.detail.writeAccessResume"));let D=await x.json().catch(()=>({}));if(!x.ok||!D.ok||!D.resumeId||!D.url)throw new Error(D.hint??D.message??D.error??n("workflow.detail.resumeStartFailed",{status:x.status}));re.set(y,{resumeId:D.resumeId,url:D.url})}catch(x){let D=x?.message??String(x);ce.set(y,D)}finally{oe.delete(y),J()}}}async function Ot(y,N){if(!oe.has(y)){oe.add(y),ce.delete(y),J();try{let x=await fetch(`/api/workflows/runs/${encodeURIComponent(t)}/attempts/${encodeURIComponent(N)}/${encodeURIComponent(y)}/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 D=await x.json().catch(()=>({}));if(!x.ok||!D.ok)if(D.error==="resume_not_running")re.delete(y);else throw new Error(D.hint??D.message??D.error??n("workflow.detail.resumeEndFailed",{status:x.status}));else re.delete(y)}catch(x){let D=x?.message??String(x);ce.set(y,D)}finally{oe.delete(y),J()}}}async function Bt(y,N){if(!V.has(y)){V.add(y),F.delete(y),J();try{let x=U.get(y)?.trim()||void 0,D=await fetch(`/api/workflows/runs/${encodeURIComponent(t)}/${N}`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({comment:x})});if(D.status===401)throw new Error(n("workflow.detail.writeAccessApproval"));let Q=await D.json().catch(()=>({}));if(!D.ok||!Q.ok)throw new Error(Q.hint??Q.message??Q.error??n("workflow.detail.actionHttp",{action:N,status:D.status}));let Ce=N==="approve"?n("workflow.detail.approved"):n("workflow.detail.rejected");F.set(y,{kind:"ok",text:Q.alreadyTerminal?n("workflow.detail.alreadyTerminal",{label:Ce}):Q.pending?n("workflow.detail.workflowContinue",{label:Ce}):n("workflow.detail.workflowRefreshing",{label:Ce})}),_(null),await be()}catch(x){let D=x?.message??String(x);F.set(y,{kind:"error",text:D}),_(D)}finally{V.delete(y),J()}}}function J(){if(!k)return;je=S.scrollTop;let y=k.run;fe.has(y.status)&&Fe(null),r.innerHTML=`${i(y.workflowId??"?")} \xB7 ${Y(y.status)} \xB7 lastSeq ${k.lastSeq}`,a.textContent=n("workflow.detail.refreshed",{time:new Date().toLocaleTimeString()}),u.hidden=fe.has(y.status),u.disabled=B||!k.chatBinding?.larkAppId,u.textContent=k.chatBinding?.larkAppId?n("workflow.detail.cancel"):n("workflow.detail.cliCancelOnly"),u.title=k.chatBinding?.larkAppId?n("workflow.detail.cancelTitle"):n("workflow.detail.cliCancelTitle",{runId:t}),fn(w,k),wn(I,k),gn(L,f,k,v),$n(g,k),Sn(l,k,C,P,{comments:U,statuses:F,resolving:V,onResolve:Bt},{sessions:re,pending:oe,errors:ce,onStart:xt,onEnd:Ot},Ue,At)&&(Ue=void 0),Vn(E,v),S.scrollTop=je,$.hidden=!h,b.textContent=n("workflow.detail.eventsLoaded",{loaded:v.length,total:T})}function Ee(){if(H!==null&&window.clearTimeout(H),k&&fe.has(k.run.status)){H=null;return}H=window.setTimeout(async()=>{await be(),R||Ee()},ln)}function _e(){document.hidden||be().then(()=>{!R&&H===null&&Ee()})}return $.addEventListener("click",()=>{Rt()}),u.addEventListener("click",()=>{Dt()}),document.addEventListener("visibilitychange",_e),Ht().then(()=>{_(null),R||Ee()}).catch(y=>{_(y?.message??String(y)),r.textContent=n("workflow.detail.loadFailed")}),()=>{R=!0,H!==null&&window.clearTimeout(H),document.removeEventListener("visibilitychange",_e)}}function fn(e,t){let o=t.run,r=[[n("workflow.summary.workflow"),i(o.workflowId??"?")],[n("workflow.summary.status"),Y(o.status)],[n("workflow.summary.lastSeq"),String(t.lastSeq)],[n("workflow.summary.updated"),i(new Date(t.updatedAt).toLocaleString())],[n("workflow.summary.revision"),i(Ie(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(([a,s])=>`<div class="wf-summary-item"><span>${a}</span><strong>${s}</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">
527
548
  <span class="muted error">${i(e.errorCode)}</span>${i(t)}
528
549
  </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]],a=new Set(r.flatMap(([,s])=>s)).size;if(e.className=a>0?"wf-panel wf-dangling-panel has":"wf-panel wf-dangling-panel",a===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">${a}</span></div>
529
550
  <div class="wf-dangling-grid">
530
551
  ${r.map(([s,d])=>`<div><strong>${s}</strong>${d.length===0?`<div class="muted">${i(n("workflow.detail.none"))}</div>`:`<ul>${d.map(w=>`<li><code>${i(w)}</code></li>`).join("")}</ul>`}</div>`).join("")}
531
- </div>`}function gn(e,t,o,r){let a=hn(r,o);if(a.length===0){t.textContent="",e.innerHTML=`<div class="empty">${i(n("workflow.detail.noParallelData"))}</div>`;return}let s=Date.now(),d=Math.min(...a.map(c=>c.startedAt)),w=Math.max(...a.map(c=>c.endedAt??s),d+1e3),T=Math.max(1,w-d),g=vn(a,s),h=a.filter(c=>!c.endedAt&&(c.status==="running"||c.status==="effectAttempting")).length;t.textContent=n("workflow.detail.parallelMeta",{count:a.length,max:g,running:h});let v=a.sort((c,L)=>c.startedAt-L.startedAt||c.activityId.localeCompare(L.activityId)).map(c=>bn(c,d,T,s)).join("");e.innerHTML=`<div class="wf-parallel-axis">
532
- <span title="${i(new Date(d).toISOString())}">${i(ke(d))}</span>
533
- <span title="${i(new Date(w).toISOString())}">${i(ke(w))}</span>
552
+ </div>`}function gn(e,t,o,r){let a=hn(r,o);if(a.length===0){t.textContent="",e.innerHTML=`<div class="empty">${i(n("workflow.detail.noParallelData"))}</div>`;return}let s=Date.now(),d=Math.min(...a.map(l=>l.startedAt)),w=Math.max(...a.map(l=>l.endedAt??s),d+1e3),I=Math.max(1,w-d),L=vn(a,s),f=a.filter(l=>!l.endedAt&&(l.status==="running"||l.status==="effectAttempting")).length;t.textContent=n("workflow.detail.parallelMeta",{count:a.length,max:L,running:f});let g=a.sort((l,S)=>l.startedAt-S.startedAt||l.activityId.localeCompare(S.activityId)).map(l=>bn(l,d,I,s)).join("");e.innerHTML=`<div class="wf-parallel-axis">
553
+ <span title="${i(new Date(d).toISOString())}">${i(Se(d))}</span>
554
+ <span title="${i(new Date(w).toISOString())}">${i(Se(w))}</span>
534
555
  </div>
535
- <div class="wf-parallel-list">${v}</div>`}function hn(e,t){let o=new Map,r=new Map(t.activities.map(a=>[a.activityId,a.ownerNodeId]));for(let a of[...e].sort((s,d)=>le(s.eventId)-le(d.eventId))){let s=Xn(a);if(!s)continue;let d=typeof s.activityId=="string"?s.activityId:void 0,w=typeof s.attemptId=="string"?s.attemptId:void 0;if(!d||!w)continue;let T=o.get(w);if(a.type==="attemptCreated"){let g=typeof s.attemptNumber=="number"?s.attemptNumber:void 0;T={nodeId:typeof s.nodeId=="string"?s.nodeId:r.get(d),activityId:d,attemptId:w,attemptNumber:g,status:"pending",startedAt:a.timestamp},o.set(w,T);continue}T||(T={nodeId:r.get(d),activityId:d,attemptId:w,status:"pending",startedAt:a.timestamp},o.set(w,T)),a.type==="activityRunning"?(T.status="running",T.runningAt=a.timestamp):a.type==="effectAttempted"?T.status="effectAttempting":a.type==="activityWaiting"||a.type==="waitCreated"?T.status="waiting":yn(a.type)&&(T.status=kn(a.type),T.endedAt=a.timestamp,T.endType=a.type)}return[...o.values()]}function bn(e,t,o,r){let a=e.endedAt??r,s=pt((e.startedAt-t)/o*100,0,100),d=pt((Math.max(a,e.startedAt+1)-e.startedAt)/o*100,.7,100-s),w=e.nodeId??e.activityId,T=e.attemptNumber!==void 0?`#${e.attemptNumber}`:$e(e.attemptId),g=[`${w} ${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(`
556
+ <div class="wf-parallel-list">${g}</div>`}function hn(e,t){let o=new Map,r=new Map(t.activities.map(a=>[a.activityId,a.ownerNodeId]));for(let a of[...e].sort((s,d)=>pe(s.eventId)-pe(d.eventId))){let s=Xn(a);if(!s)continue;let d=typeof s.activityId=="string"?s.activityId:void 0,w=typeof s.attemptId=="string"?s.attemptId:void 0;if(!d||!w)continue;let I=o.get(w);if(a.type==="attemptCreated"){let L=typeof s.attemptNumber=="number"?s.attemptNumber:void 0;I={nodeId:typeof s.nodeId=="string"?s.nodeId:r.get(d),activityId:d,attemptId:w,attemptNumber:L,status:"pending",startedAt:a.timestamp},o.set(w,I);continue}I||(I={nodeId:r.get(d),activityId:d,attemptId:w,status:"pending",startedAt:a.timestamp},o.set(w,I)),a.type==="activityRunning"?(I.status="running",I.runningAt=a.timestamp):a.type==="effectAttempted"?I.status="effectAttempting":a.type==="activityWaiting"||a.type==="waitCreated"?I.status="waiting":yn(a.type)&&(I.status=kn(a.type),I.endedAt=a.timestamp,I.endType=a.type)}return[...o.values()]}function bn(e,t,o,r){let a=e.endedAt??r,s=pt((e.startedAt-t)/o*100,0,100),d=pt((Math.max(a,e.startedAt+1)-e.startedAt)/o*100,.7,100-s),w=e.nodeId??e.activityId,I=e.attemptNumber!==void 0?`#${e.attemptNumber}`:Ie(e.attemptId),L=[`${w} ${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(`
536
557
  `);return`<div class="wf-parallel-row">
537
558
  <div class="wf-parallel-label">
538
559
  <code>${i(w)}</code>
539
- <span class="muted">${i(e.activityId)} \xB7 ${i(T)}</span>
560
+ <span class="muted">${i(e.activityId)} \xB7 ${i(I)}</span>
540
561
  </div>
541
562
  <div class="wf-parallel-track">
542
- <div class="wf-parallel-bar wf-parallel-${i(e.status)}" style="left:${s.toFixed(3)}%;width:${d.toFixed(3)}%;" title="${i(g)}">
543
- <span>${i(Y(e.status))}</span>
563
+ <div class="wf-parallel-bar wf-parallel-${i(e.status)}" style="left:${s.toFixed(3)}%;width:${d.toFixed(3)}%;" title="${i(L)}">
564
+ <span>${i(te(e.status))}</span>
544
565
  </div>
545
566
  </div>
546
567
  </div>`}function vn(e,t){let o=[];for(let s of e)o.push({time:s.startedAt,delta:1}),o.push({time:s.endedAt??t,delta:-1});o.sort((s,d)=>s.time-d.time||d.delta-s.delta);let r=0,a=0;for(let s of o)r+=s.delta,a=Math.max(a,r);return a}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(s=>[s.activityId,s])),r=new Set,a=[];for(let s of t.nodes){let d=(s.activityId?o.get(s.activityId):void 0)??t.activities.find(w=>w.ownerNodeId===s.nodeId);d&&r.add(d.activityId),a.push(ut(s,d))}for(let s of t.activities)r.has(s.activityId)||a.push(ut(void 0,s));e.innerHTML=a.length>0?a.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>
547
568
  <td>${e?`<code>${i(e.nodeId)}</code>`:'<span class="muted">-</span>'}</td>
548
- <td>${e?G(e.status):'<span class="muted">-</span>'}</td>
569
+ <td>${e?Y(e.status):'<span class="muted">-</span>'}</td>
549
570
  <td>${t?`<code>${i(t.activityId)}</code>`:'<span class="muted">-</span>'}</td>
550
- <td>${t?G(t.status):'<span class="muted">-</span>'}</td>
571
+ <td>${t?Y(t.status):'<span class="muted">-</span>'}</td>
551
572
  <td>${t?.attempts.length??0}</td>
552
573
  <td>${o?`<code>${i(o.attemptId)}</code>`:'<span class="muted">-</span>'}</td>
553
574
  <td>${o?Kn(o):`<span class="muted">${i(n("workflow.detail.idle"))}</span>`}</td>
554
- </tr>`}function Sn(e,t,o,r,a,s,d,w){Fn(e,o,r),Pn(e,a.comments);let T=!!(d&&t.attemptIO?.[d]?.terminal);T&&d&&o.add(Be(d,n("workflow.detail.liveTerminal")));let g=In(t),h=new Set;if(w){for(let c of g){h.add(c.key);let L=w.get(c.key);L||(L=Tn(c.key),w.set(c.key,L),e.appendChild(L.article)),Ln(L,c,o,a,s,d)}for(let[c,L]of Array.from(w))h.has(c)||(L.article.remove(),w.delete(c));if(g.length===0){if(!e.querySelector(".wf-io-empty-placeholder")){let c=document.createElement("div");c.className="empty wf-io-empty-placeholder",c.textContent=n("workflow.detail.noNodeIO"),e.appendChild(c)}}else e.querySelector(".wf-io-empty-placeholder")?.remove()}else{let c=[];for(let L of g)c.push(Hn(L,o,a,s,d));e.innerHTML=c.length>0?c.join(""):`<div class="empty">${i(n("workflow.detail.noNodeIO"))}</div>`}_n(e,r);let v=Un(e,d);return Wn(e,o),Jn(e,r),jn(e,a),St(e,s),v&&T}function In(e){let t=new Map(e.activities.map(a=>[a.activityId,a])),o=new Set,r=[];for(let a of e.nodes){let s=(a.activityId?t.get(a.activityId):void 0)??e.activities.find(d=>d.ownerNodeId===a.nodeId);if(!s){r.push({key:`node:${a.nodeId}`,node:a});continue}o.add(s.activityId),r.push({key:`activity:${s.activityId}`,node:a,activity:s,io:e.attemptIO?.[ye(s)?.attemptId??""]})}for(let a of e.activities)o.has(a.activityId)||r.push({key:`activity:${a.activityId}`,activity:a,io:e.attemptIO?.[ye(a)?.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 a=document.createElement("div");return a.className="wf-io-grid",t.appendChild(o),t.appendChild(r),t.appendChild(a),{article:t,head:o,terminalSlot:r,grid:a,currentTerminalUrl:null}}function Ln(e,t,o,r,a,s){let d=ye(t.activity),w=t.node?.nodeId??t.activity?.ownerNodeId??t.activity?.activityId??"unknown",T=!!(d&&d.attemptId===s);e.article.classList.toggle("is-focused",T),d?e.article.dataset.wfAttemptCard=d.attemptId:delete e.article.dataset.wfAttemptCard;let g=$t(d,r);e.head.innerHTML=`
575
+ </tr>`}function Sn(e,t,o,r,a,s,d,w){Fn(e,o,r),Pn(e,a.comments);let I=!!(d&&t.attemptIO?.[d]?.terminal);I&&d&&o.add(qe(d,n("workflow.detail.liveTerminal")));let L=In(t),f=new Set;if(w){for(let l of L){f.add(l.key);let S=w.get(l.key);S||(S=Tn(l.key),w.set(l.key,S),e.appendChild(S.article)),Ln(S,l,o,a,s,d)}for(let[l,S]of Array.from(w))f.has(l)||(S.article.remove(),w.delete(l));if(L.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 S of L)l.push(Hn(S,o,a,s,d));e.innerHTML=l.length>0?l.join(""):`<div class="empty">${i(n("workflow.detail.noNodeIO"))}</div>`}_n(e,r);let g=Un(e,d);return Wn(e,o),Jn(e,r),jn(e,a),St(e,s),g&&I}function In(e){let t=new Map(e.activities.map(a=>[a.activityId,a])),o=new Set,r=[];for(let a of e.nodes){let s=(a.activityId?t.get(a.activityId):void 0)??e.activities.find(d=>d.ownerNodeId===a.nodeId);if(!s){r.push({key:`node:${a.nodeId}`,node:a});continue}o.add(s.activityId),r.push({key:`activity:${s.activityId}`,node:a,activity:s,io:e.attemptIO?.[$e(s)?.attemptId??""]})}for(let a of e.activities)o.has(a.activityId)||r.push({key:`activity:${a.activityId}`,activity:a,io:e.attemptIO?.[$e(a)?.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 a=document.createElement("div");return a.className="wf-io-grid",t.appendChild(o),t.appendChild(r),t.appendChild(a),{article:t,head:o,terminalSlot:r,grid:a,currentTerminalUrl:null}}function Ln(e,t,o,r,a,s){let d=$e(t.activity),w=t.node?.nodeId??t.activity?.ownerNodeId??t.activity?.activityId??"unknown",I=!!(d&&d.attemptId===s);e.article.classList.toggle("is-focused",I),d?e.article.dataset.wfAttemptCard=d.attemptId:delete e.article.dataset.wfAttemptCard;let L=$t(d,r);e.head.innerHTML=`
555
576
  <header>
556
577
  <div>
557
578
  <strong><code>${i(w)}</code></strong>
558
579
  <span class="muted">${t.activity?i(t.activity.activityId):i(n("workflow.detail.notDispatched"))}</span>
559
580
  </div>
560
- <div>${t.node?G(t.node.status):""} ${t.activity?G(t.activity.status):""}</div>
581
+ <div>${t.node?Y(t.node.status):""} ${t.activity?Y(t.activity.status):""}</div>
561
582
  </header>
562
583
  <div class="wf-io-meta">
563
584
  ${d?`${i(n("workflow.detail.attempt"))} <code>${i(d.attemptId)}</code>`:i(n("workflow.detail.noAttempt"))}
564
585
  </div>
565
- ${g}
566
- `;let h=wt(d,t.activity,t.io?.terminal,a),v=h?.url??null;if(v!==e.currentTerminalUrl)h===null?e.terminalSlot.innerHTML="":e.terminalSlot.innerHTML=gt(t.key,d,t.activity,t.io?.terminal,h,o,a),e.currentTerminalUrl=v;else if(h!==null&&t.io?.terminal){let L=e.terminalSlot.querySelector("details.wf-terminal-block > summary");if(L){let E=ht(h.kind);L.innerHTML=`${i(E)} ${kt(d,t.io.terminal)}`}d&&Nn(e.terminalSlot,d,t.activity,t.io.terminal,h,a)}let c=d?.attemptId??t.activity?.activityId??t.node?.nodeId??"unknown";e.grid.innerHTML=`
567
- ${J(c,n("workflow.detail.authoredInput"),t.io?.input,o)}
568
- ${J(c,n("workflow.detail.resolvedInput"),t.io?.resolvedInput,o)}
569
- ${J(c,n("workflow.detail.output"),t.io?.output,o)}
570
- ${J(c,n("workflow.detail.executionLog"),t.io?.log,o)}
571
- ${t.io?.waitPrompt?J(c,n("workflow.detail.waitPrompt"),t.io.waitPrompt,o):""}
572
- `}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 a=Bn();if(!a)return null;let s=r?.sessions.get(e.attemptId);return s?{kind:"resume",url:s.url,resumeId:s.resumeId,downloadUrl:ft(a,t.activityId,e.attemptId)}:{kind:"replay",url:On(a,t.activityId,e.attemptId,!!o.hasPtyLog),downloadUrl:ft(a,t.activityId,e.attemptId)}}function gt(e,t,o,r,a,s,d){if(!r)return"";let w=ht(a.kind),T=Be(e,w),g=kt(t,r),h=En(a.kind),v=a.kind==="replay"||a.kind==="resume"?`<a class="btn-link" href="${i(a.downloadUrl)}" download>${i(n("workflow.detail.downloadFullLog"))}</a>`:"",c=t?vt(t,o,r,a,d):"",L=t?yt(t.attemptId,d):"";return`<details class="wf-io-block wf-terminal-block" data-io-key="${i(T)}"${s.has(T)?" open":""}>
573
- <summary>${i(w)} ${g}</summary>
586
+ ${L}
587
+ `;let f=wt(d,t.activity,t.io?.terminal,a),g=f?.url??null;if(g!==e.currentTerminalUrl)f===null?e.terminalSlot.innerHTML="":e.terminalSlot.innerHTML=gt(t.key,d,t.activity,t.io?.terminal,f,o,a),e.currentTerminalUrl=g;else if(f!==null&&t.io?.terminal){let S=e.terminalSlot.querySelector("details.wf-terminal-block > summary");if(S){let E=ht(f.kind);S.innerHTML=`${i(E)} ${kt(d,t.io.terminal)}`}d&&Nn(e.terminalSlot,d,t.activity,t.io.terminal,f,a)}let l=d?.attemptId??t.activity?.activityId??t.node?.nodeId??"unknown";e.grid.innerHTML=`
588
+ ${K(l,n("workflow.detail.authoredInput"),t.io?.input,o)}
589
+ ${K(l,n("workflow.detail.resolvedInput"),t.io?.resolvedInput,o)}
590
+ ${K(l,n("workflow.detail.output"),t.io?.output,o)}
591
+ ${K(l,n("workflow.detail.executionLog"),t.io?.log,o)}
592
+ ${t.io?.waitPrompt?K(l,n("workflow.detail.waitPrompt"),t.io.waitPrompt,o):""}
593
+ `}function wt(e,t,o,r){if(!o||o.error)return null;if(Rn(e,o))return{kind:"live",url:xn(o)};if(!e||!t||!Dn(e,o))return null;let a=Bn();if(!a)return null;let s=r?.sessions.get(e.attemptId);return s?{kind:"resume",url:s.url,resumeId:s.resumeId,downloadUrl:ft(a,t.activityId,e.attemptId)}:{kind:"replay",url:On(a,t.activityId,e.attemptId,!!o.hasPtyLog),downloadUrl:ft(a,t.activityId,e.attemptId)}}function gt(e,t,o,r,a,s,d){if(!r)return"";let w=ht(a.kind),I=qe(e,w),L=kt(t,r),f=En(a.kind),g=a.kind==="replay"||a.kind==="resume"?`<a class="btn-link" href="${i(a.downloadUrl)}" download>${i(n("workflow.detail.downloadFullLog"))}</a>`:"",l=t?vt(t,o,r,a,d):"",S=t?yt(t.attemptId,d):"";return`<details class="wf-io-block wf-terminal-block" data-io-key="${i(I)}"${s.has(I)?" open":""}>
594
+ <summary>${i(w)} ${L}</summary>
574
595
  <div class="wf-terminal-actions">
575
- <a class="btn-link" href="${i(a.url)}" target="_blank" rel="noreferrer">${i(h)}</a>
576
- ${v}
577
- ${c}
596
+ <a class="btn-link" href="${i(a.url)}" target="_blank" rel="noreferrer">${i(f)}</a>
597
+ ${g}
598
+ ${l}
578
599
  </div>
579
- ${L}
600
+ ${S}
580
601
  <iframe class="wf-terminal-frame" src="${i(a.url)}" title="${i(w)}" loading="lazy"></iframe>
581
- </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","mtr"]);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,a){if(!a||r.kind==="live"||!t)return"";let s=r.kind==="resume",d=a.pending.has(e.attemptId),w=`data-wf-resume-attempt="${i(e.attemptId)}" data-wf-resume-activity="${i(t.activityId)}"`;return s?`<button type="button" class="btn-link" data-wf-resume-button="1" data-wf-resume-action="end" ${w}${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" ${w}${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,a){let s=ye(e.activity),d=e.node?.nodeId??e.activity?.ownerNodeId??e.activity?.activityId??"unknown",w=s?.attemptId??e.activity?.activityId??e.node?.nodeId??"unknown",T=$t(s,o),g=s?.attemptId===a?" is-focused":"",h=s?` data-wf-attempt-card="${i(s.attemptId)}"`:"",v=wt(s,e.activity,e.io?.terminal,r),c=v?gt(w,s,e.activity,e.io?.terminal,v,t,r):"";return`<article class="wf-io-card${g}" data-wf-card-key="${i(e.key)}"${h}>
602
+ </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","mtr","hermes"]);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,a){if(!a||r.kind==="live"||!t)return"";let s=r.kind==="resume",d=a.pending.has(e.attemptId),w=`data-wf-resume-attempt="${i(e.attemptId)}" data-wf-resume-activity="${i(t.activityId)}"`;return s?`<button type="button" class="btn-link" data-wf-resume-button="1" data-wf-resume-action="end" ${w}${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" ${w}${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,a){let s=$e(e.activity),d=e.node?.nodeId??e.activity?.ownerNodeId??e.activity?.activityId??"unknown",w=s?.attemptId??e.activity?.activityId??e.node?.nodeId??"unknown",I=$t(s,o),L=s?.attemptId===a?" is-focused":"",f=s?` data-wf-attempt-card="${i(s.attemptId)}"`:"",g=wt(s,e.activity,e.io?.terminal,r),l=g?gt(w,s,e.activity,e.io?.terminal,g,t,r):"";return`<article class="wf-io-card${L}" data-wf-card-key="${i(e.key)}"${f}>
582
603
  <div class="wf-io-card-head">
583
604
  <header>
584
605
  <div>
585
606
  <strong><code>${i(d)}</code></strong>
586
607
  <span class="muted">${e.activity?i(e.activity.activityId):i(n("workflow.detail.notDispatched"))}</span>
587
608
  </div>
588
- <div>${e.node?G(e.node.status):""} ${e.activity?G(e.activity.status):""}</div>
609
+ <div>${e.node?Y(e.node.status):""} ${e.activity?Y(e.activity.status):""}</div>
589
610
  </header>
590
611
  <div class="wf-io-meta">
591
612
  ${s?`${i(n("workflow.detail.attempt"))} <code>${i(s.attemptId)}</code>`:i(n("workflow.detail.noAttempt"))}
592
613
  </div>
593
- ${T}
614
+ ${I}
594
615
  </div>
595
- <div class="wf-io-terminal-slot">${c}</div>
616
+ <div class="wf-io-terminal-slot">${l}</div>
596
617
  <div class="wf-io-grid">
597
- ${J(w,n("workflow.detail.authoredInput"),e.io?.input,t)}
598
- ${J(w,n("workflow.detail.resolvedInput"),e.io?.resolvedInput,t)}
599
- ${J(w,n("workflow.detail.output"),e.io?.output,t)}
600
- ${J(w,n("workflow.detail.executionLog"),e.io?.log,t)}
601
- ${e.io?.waitPrompt?J(w,n("workflow.detail.waitPrompt"),e.io.waitPrompt,t):""}
618
+ ${K(w,n("workflow.detail.authoredInput"),e.io?.input,t)}
619
+ ${K(w,n("workflow.detail.resolvedInput"),e.io?.resolvedInput,t)}
620
+ ${K(w,n("workflow.detail.output"),e.io?.output,t)}
621
+ ${K(w,n("workflow.detail.executionLog"),e.io?.log,t)}
622
+ ${e.io?.waitPrompt?K(w,n("workflow.detail.waitPrompt"),e.io.waitPrompt,t):""}
602
623
  </div>
603
- </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 a=new URLSearchParams({runId:e,activityId:t,attemptId:o});return r&&a.set("hasPtyLog","1"),`/assets/terminal-replay.html?${a.toString()}`}function ft(e,t,o){return`/api/workflows/runs/${encodeURIComponent(e)}/attempts/${encodeURIComponent(t)}/${encodeURIComponent(o)}/terminal-log/raw?download=1`}function Bn(){let e=window.location.hash.match(/^#\/workflows\/([^/?#]+)/);if(!e)return null;try{return decodeURIComponent(e[1])}catch{return null}}function $t(e,t){if(!qn(e))return"";let o=e.attemptId,r=t.comments.get(o)??"",a=t.resolving.has(o),s=t.statuses.get(o),d=s?.kind==="error"?"hint-warn":"hint-ok";return`<div class="wf-approval-box" data-wf-approval="${i(o)}">
624
+ </article>`}function $e(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 Dn(e,t){return e.status==="succeeded"||e.status==="failed"||e.status==="cancelled"||e.status==="timedOut"?!!(t.sessionId||t.startedAt):!1}function xn(e){return`http://${window.location.hostname||"127.0.0.1"}:${e.webPort}`}function On(e,t,o,r){let a=new URLSearchParams({runId:e,activityId:t,attemptId:o});return r&&a.set("hasPtyLog","1"),`/assets/terminal-replay.html?${a.toString()}`}function ft(e,t,o){return`/api/workflows/runs/${encodeURIComponent(e)}/attempts/${encodeURIComponent(t)}/${encodeURIComponent(o)}/terminal-log/raw?download=1`}function Bn(){let e=window.location.hash.match(/^#\/workflows\/([^/?#]+)/);if(!e)return null;try{return decodeURIComponent(e[1])}catch{return null}}function $t(e,t){if(!qn(e))return"";let o=e.attemptId,r=t.comments.get(o)??"",a=t.resolving.has(o),s=t.statuses.get(o),d=s?.kind==="error"?"hint-warn":"hint-ok";return`<div class="wf-approval-box" data-wf-approval="${i(o)}">
604
625
  <label>
605
626
  <span>${i(n("workflow.detail.approvalComment"))}</span>
606
627
  <textarea class="wf-approval-comment" data-wf-approval-comment="${i(o)}" rows="2" placeholder="${i(n("workflow.detail.optionalComment"))}"${a?" disabled":""}>${i(r)}</textarea>
@@ -611,120 +632,120 @@ ${u.join(`
611
632
  ${a?`<span class="muted">${i(n("workflow.detail.submitting"))}</span>`:""}
612
633
  </div>
613
634
  ${s?`<div class="${d} wf-approval-status">${i(s.text)}</div>`:""}
614
- </div>`}function qn(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,a=o.dataset.wfResumeActivity,s=o.dataset.wfResumeAction;!r||!a||(s==="start"?t.onStart(r,a):s==="end"&&t.onEnd(r,a))}))})}function Nn(e,t,o,r,a,s){let d=e.querySelector(".wf-terminal-actions");if(!d)return;let w=d.querySelector('button[data-wf-resume-button="1"]'),T=vt(t,o,r,a,s);w?w.outerHTML=T:T&&d.insertAdjacentHTML("beforeend",T);let g=e.querySelector("details.wf-terminal-block");if(g){let h=g.querySelector(".wf-resume-status"),v=yt(t.attemptId,s);h?h.outerHTML=v:v&&d.insertAdjacentHTML("afterend",v)}St(e,s)}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,a=o.dataset.wfApprovalAction;!r||a!=="approve"&&a!=="reject"||t.onResolve(r,a)})})}function J(e,t,o,r){let a=Be(e,t);return`<details class="wf-io-block" data-io-key="${i(a)}"${r.has(a)?" open":""}>
635
+ </div>`}function qn(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,a=o.dataset.wfResumeActivity,s=o.dataset.wfResumeAction;!r||!a||(s==="start"?t.onStart(r,a):s==="end"&&t.onEnd(r,a))}))})}function Nn(e,t,o,r,a,s){let d=e.querySelector(".wf-terminal-actions");if(!d)return;let w=d.querySelector('button[data-wf-resume-button="1"]'),I=vt(t,o,r,a,s);w?w.outerHTML=I:I&&d.insertAdjacentHTML("beforeend",I);let L=e.querySelector("details.wf-terminal-block");if(L){let f=L.querySelector(".wf-resume-status"),g=yt(t.attemptId,s);f?f.outerHTML=g:g&&d.insertAdjacentHTML("afterend",g)}St(e,s)}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,a=o.dataset.wfApprovalAction;!r||a!=="approve"&&a!=="reject"||t.onResolve(r,a)})})}function K(e,t,o,r){let a=qe(e,t);return`<details class="wf-io-block" data-io-key="${i(a)}"${r.has(a)?" open":""}>
615
636
  <summary>${i(t)} ${Gn(o)}</summary>
616
637
  ${zn(o)}
617
- </details>`}function Be(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 a=r.dataset.ioKey;if(!a)return;r.open?t.add(a):t.delete(a);let s=r.querySelector(".wf-io-pre");s&&o.set(a,s.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 a=t.get(r);if(a===void 0)return;let s=o.querySelector(".wf-io-pre");s&&(s.scrollTop=a)})}function Jn(e,t){e.querySelectorAll("details.wf-io-block[data-io-key]").forEach(o=>{let r=o.dataset.ioKey;if(!r)return;let a=o.querySelector(".wf-io-pre");a&&a.dataset.ioScrollBound!=="1"&&(a.dataset.ioScrollBound="1",a.addEventListener("scroll",()=>{t.set(r,a.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>
618
- <td>${le(e.eventId)}</td>
638
+ </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 a=r.dataset.ioKey;if(!a)return;r.open?t.add(a):t.delete(a);let s=r.querySelector(".wf-io-pre");s&&o.set(a,s.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 a=t.get(r);if(a===void 0)return;let s=o.querySelector(".wf-io-pre");s&&(s.scrollTop=a)})}function Jn(e,t){e.querySelectorAll("details.wf-io-block[data-io-key]").forEach(o=>{let r=o.dataset.ioKey;if(!r)return;let a=o.querySelector(".wf-io-pre");a&&a.dataset.ioScrollBound!=="1"&&(a.dataset.ioScrollBound="1",a.addEventListener("scroll",()=>{t.set(r,a.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(Ie(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(Se(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(Ie(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>
639
+ <td>${pe(e.eventId)}</td>
619
640
  <td><code>${i(e.type)}</code></td>
620
641
  <td>${i(e.actor)}</td>
621
642
  <td>${t.nodeId?`<code>${i(t.nodeId)}</code>`:"-"}</td>
622
643
  <td>${t.activityId?`<code>${i(t.activityId)}</code>`:"-"}</td>
623
644
  <td>${t.errorCode?`<span class="muted error">${i(t.errorCode)}</span>`:"-"}</td>
624
- <td title="${i(new Date(e.timestamp).toISOString())}">${i(ke(e.timestamp))}</td>
625
- </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=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"})[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=`
645
+ <td title="${i(new Date(e.timestamp).toISOString())}">${i(Se(e.timestamp))}</td>
646
+ </tr>`}function pe(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 Ie(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 Se(e){return new Date(e).toLocaleTimeString([],{hour:"2-digit",minute:"2-digit",second:"2-digit"})}function M(e){return e.replace(/[&<>"']/g,t=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"})[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=`
626
647
  <nav class="wf-subnav">
627
- <a href="#/workflows" data-i18n="workflow.subnav.runs">${C(n("workflow.subnav.runs"))}</a>
628
- <a href="#/workflows/catalog" class="active" data-i18n="workflow.subnav.catalog">${C(n("workflow.subnav.catalog"))}</a>
648
+ <a href="#/workflows" data-i18n="workflow.subnav.runs">${M(n("workflow.subnav.runs"))}</a>
649
+ <a href="#/workflows/catalog" class="active" data-i18n="workflow.subnav.catalog">${M(n("workflow.subnav.catalog"))}</a>
629
650
  </nav>
630
651
  <section class="catalog-head">
631
652
  <div>
632
- <h2>${C(n("catalog.title"))}</h2>
633
- <p class="muted">${C(n("catalog.subtitle"))}</p>
653
+ <h2>${M(n("catalog.title"))}</h2>
654
+ <p class="muted">${M(n("catalog.subtitle"))}</p>
634
655
  </div>
635
- <button id="catalog-refresh" type="button">${C(n("catalog.refresh"))}</button>
656
+ <button id="catalog-refresh" type="button">${M(n("catalog.refresh"))}</button>
636
657
  </section>
637
658
  <form id="catalog-filters" class="filters">
638
- <input type="search" name="q" placeholder="${C(n("catalog.searchPlaceholder"))}" />
659
+ <input type="search" name="q" placeholder="${M(n("catalog.searchPlaceholder"))}" />
639
660
  <span id="catalog-status" class="muted"></span>
640
661
  </form>
641
662
  <div class="wf-table-scroll">
642
663
  <table>
643
664
  <thead><tr>
644
- <th>${C(n("catalog.table.workflow"))}</th>
645
- <th>${C(n("catalog.table.version"))}</th>
646
- <th>${C(n("catalog.table.params"))}</th>
647
- <th>${C(n("catalog.table.nodes"))}</th>
648
- <th>${C(n("catalog.table.revision"))}</th>
649
- <th>${C(n("catalog.table.path"))}</th>
665
+ <th>${M(n("catalog.table.workflow"))}</th>
666
+ <th>${M(n("catalog.table.version"))}</th>
667
+ <th>${M(n("catalog.table.params"))}</th>
668
+ <th>${M(n("catalog.table.nodes"))}</th>
669
+ <th>${M(n("catalog.table.revision"))}</th>
670
+ <th>${M(n("catalog.table.path"))}</th>
650
671
  </tr></thead>
651
672
  <tbody id="catalog-tbody"></tbody>
652
673
  </table>
653
674
  </div>
654
- `;let t=e.querySelector("#catalog-tbody"),o=e.querySelector("#catalog-status"),r=e.querySelector("#catalog-filters"),a=e.querySelector("#catalog-refresh"),s=[],d=null,w=!1;function T(){let v=(new FormData(r).get("q")??"").trim().toLowerCase();return v?s.filter(c=>c.workflowId.toLowerCase().includes(v)||c.path.toLowerCase().includes(v)):s}function g(){d?(o.textContent=n("catalog.loadFailed",{error:d}),o.classList.add("error")):(o.textContent=`${s.length}`,o.classList.remove("error"));let v=T();if(v.length===0){t.innerHTML=`<tr><td colspan="6" class="empty">${s.length===0?C(n("catalog.noDefinitions")):C(n("catalog.noFilterMatch"))}</td></tr>`;return}t.innerHTML=v.map(c=>`
675
+ `;let t=e.querySelector("#catalog-tbody"),o=e.querySelector("#catalog-status"),r=e.querySelector("#catalog-filters"),a=e.querySelector("#catalog-refresh"),s=[],d=null,w=!1;function I(){let g=(new FormData(r).get("q")??"").trim().toLowerCase();return g?s.filter(l=>l.workflowId.toLowerCase().includes(g)||l.path.toLowerCase().includes(g)):s}function L(){d?(o.textContent=n("catalog.loadFailed",{error:d}),o.classList.add("error")):(o.textContent=`${s.length}`,o.classList.remove("error"));let g=I();if(g.length===0){t.innerHTML=`<tr><td colspan="6" class="empty">${s.length===0?M(n("catalog.noDefinitions")):M(n("catalog.noFilterMatch"))}</td></tr>`;return}t.innerHTML=g.map(l=>`
655
676
  <tr>
656
- <td><a href="#/workflows/catalog/${encodeURIComponent(c.workflowId)}"><code>${C(c.workflowId)}</code></a></td>
657
- <td>${c.version}</td>
658
- <td>${C(n("catalog.paramSummary",{required:c.requiredParamCount,total:c.paramCount}))}</td>
659
- <td>${c.nodeCount}</td>
660
- <td><code>${C(It(c.revisionId))}</code></td>
661
- <td><code>${C(c.path)}</code></td>
677
+ <td><a href="#/workflows/catalog/${encodeURIComponent(l.workflowId)}"><code>${M(l.workflowId)}</code></a></td>
678
+ <td>${l.version}</td>
679
+ <td>${M(n("catalog.paramSummary",{required:l.requiredParamCount,total:l.paramCount}))}</td>
680
+ <td>${l.nodeCount}</td>
681
+ <td><code>${M(It(l.revisionId))}</code></td>
682
+ <td><code>${M(l.path)}</code></td>
662
683
  </tr>
663
- `).join("")}async function h(){a.disabled=!0,o.textContent=n("catalog.loading");try{let v=await fetch("/api/workflows/definitions");if(!v.ok)throw new Error(`HTTP ${v.status}`);s=(await v.json()).definitions??[],d=null}catch(v){d=v?.message??String(v),s=[]}finally{a.disabled=!1,w||g()}}return r.addEventListener("input",g),a.addEventListener("click",()=>{h()}),h(),()=>{w=!0}}function to(e,t){e.innerHTML=`
684
+ `).join("")}async function f(){a.disabled=!0,o.textContent=n("catalog.loading");try{let g=await fetch("/api/workflows/definitions");if(!g.ok)throw new Error(`HTTP ${g.status}`);s=(await g.json()).definitions??[],d=null}catch(g){d=g?.message??String(g),s=[]}finally{a.disabled=!1,w||L()}}return r.addEventListener("input",L),a.addEventListener("click",()=>{f()}),f(),()=>{w=!0}}function to(e,t){e.innerHTML=`
664
685
  <div class="catalog-detail-head">
665
- <a class="btn-link" href="#/workflows/catalog">${C(n("catalog.back"))}</a>
686
+ <a class="btn-link" href="#/workflows/catalog">${M(n("catalog.back"))}</a>
666
687
  <div>
667
- <h2><code>${C(t)}</code></h2>
668
- <div id="catalog-detail-subtitle" class="muted">${C(n("workflow.detail.loading"))}</div>
688
+ <h2><code>${M(t)}</code></h2>
689
+ <div id="catalog-detail-subtitle" class="muted">${M(n("workflow.detail.loading"))}</div>
669
690
  </div>
670
691
  </div>
671
692
  <section id="catalog-error" class="hint-warn" hidden></section>
672
693
  <section id="catalog-run-status" class="hint-ok" hidden></section>
673
694
  <div id="catalog-detail-body"></div>
674
- `;let o=e.querySelector("#catalog-detail-subtitle"),r=e.querySelector("#catalog-error"),a=e.querySelector("#catalog-run-status"),s=e.querySelector("#catalog-detail-body"),d=null,w=!1,T=!1;function g(u){r.hidden=!u,r.textContent=u??""}function h(u){a.hidden=!u,a.textContent=u??""}function v(u){let S={};for(let[y,$]of Object.entries(u??{}))"default"in $&&(S[y]=$.default);return S}function c(){if(!d)return;let u=d.definition;o.textContent=`${n("catalog.revision")} ${It(d.revisionId)} \xB7 ${d.path}`;let S=JSON.stringify(v(u.params),null,2);s.innerHTML=`
695
+ `;let o=e.querySelector("#catalog-detail-subtitle"),r=e.querySelector("#catalog-error"),a=e.querySelector("#catalog-run-status"),s=e.querySelector("#catalog-detail-body"),d=null,w=!1,I=!1;function L(u){r.hidden=!u,r.textContent=u??""}function f(u){a.hidden=!u,a.textContent=u??""}function g(u){let $={};for(let[k,v]of Object.entries(u??{}))"default"in v&&($[k]=v.default);return $}function l(){if(!d)return;let u=d.definition;o.textContent=`${n("catalog.revision")} ${It(d.revisionId)} \xB7 ${d.path}`;let $=JSON.stringify(g(u.params),null,2);s.innerHTML=`
675
696
  <section class="wf-panel">
676
- <div class="wf-panel-title"><h3>${C(n("catalog.summary"))}</h3></div>
697
+ <div class="wf-panel-title"><h3>${M(n("catalog.summary"))}</h3></div>
677
698
  <div class="wf-summary-grid">
678
- <div class="wf-summary-item"><span>${C(n("catalog.table.workflow"))}</span><strong><code>${C(u.workflowId)}</code></strong></div>
679
- <div class="wf-summary-item"><span>${C(n("catalog.table.version"))}</span><strong>${u.version}</strong></div>
680
- <div class="wf-summary-item"><span>${C(n("catalog.nodeCount"))}</span><strong>${Object.keys(u.nodes).length}</strong></div>
681
- <div class="wf-summary-item"><span>${C(n("catalog.path"))}</span><strong><code>${C(d.path)}</code></strong></div>
699
+ <div class="wf-summary-item"><span>${M(n("catalog.table.workflow"))}</span><strong><code>${M(u.workflowId)}</code></strong></div>
700
+ <div class="wf-summary-item"><span>${M(n("catalog.table.version"))}</span><strong>${u.version}</strong></div>
701
+ <div class="wf-summary-item"><span>${M(n("catalog.nodeCount"))}</span><strong>${Object.keys(u.nodes).length}</strong></div>
702
+ <div class="wf-summary-item"><span>${M(n("catalog.path"))}</span><strong><code>${M(d.path)}</code></strong></div>
682
703
  </div>
683
704
  </section>
684
705
 
685
706
  <section class="wf-panel">
686
- <div class="wf-panel-title"><h3>${C(n("catalog.runPanel"))}</h3></div>
707
+ <div class="wf-panel-title"><h3>${M(n("catalog.runPanel"))}</h3></div>
687
708
  <form id="catalog-run-form" class="catalog-run-form">
688
709
  <label>
689
- <span>${C(n("catalog.paramsJson"))}</span>
690
- <textarea id="catalog-params" rows="8" spellcheck="false" placeholder="${C(n("catalog.paramsPlaceholder"))}">${C(S)}</textarea>
710
+ <span>${M(n("catalog.paramsJson"))}</span>
711
+ <textarea id="catalog-params" rows="8" spellcheck="false" placeholder="${M(n("catalog.paramsPlaceholder"))}">${M($)}</textarea>
691
712
  </label>
692
713
  <div class="catalog-chat-grid">
693
714
  <label>
694
- <span>${C(n("catalog.chatId"))}</span>
715
+ <span>${M(n("catalog.chatId"))}</span>
695
716
  <input id="catalog-chat-id" type="text" autocomplete="off" />
696
717
  </label>
697
718
  <label>
698
- <span>${C(n("catalog.larkAppId"))}</span>
719
+ <span>${M(n("catalog.larkAppId"))}</span>
699
720
  <input id="catalog-lark-app-id" type="text" autocomplete="off" />
700
721
  </label>
701
722
  </div>
702
- <div class="muted">${C(n("catalog.chatBindingHint"))}</div>
723
+ <div class="muted">${M(n("catalog.chatBindingHint"))}</div>
703
724
  <div id="catalog-param-errors" class="catalog-param-errors" hidden></div>
704
- <button id="catalog-run-btn" type="submit" class="primary">${C(n("catalog.run"))}</button>
725
+ <button id="catalog-run-btn" type="submit" class="primary">${M(n("catalog.run"))}</button>
705
726
  </form>
706
727
  </section>
707
728
 
708
729
  <section class="wf-panel">
709
- <div class="wf-panel-title"><h3>${C(n("catalog.paramsSchema"))}</h3></div>
730
+ <div class="wf-panel-title"><h3>${M(n("catalog.paramsSchema"))}</h3></div>
710
731
  ${no(u.params)}
711
732
  </section>
712
733
 
713
734
  <section class="wf-panel">
714
- <div class="wf-panel-title"><h3>${C(n("catalog.definitionJson"))}</h3></div>
715
- <pre class="wf-io-pre">${C(JSON.stringify(u,null,2))}</pre>
735
+ <div class="wf-panel-title"><h3>${M(n("catalog.definitionJson"))}</h3></div>
736
+ <pre class="wf-io-pre">${M(JSON.stringify(u,null,2))}</pre>
716
737
  </section>
717
- `,E()}async function L(){if(!d||T)return;let u=s.querySelector("#catalog-params"),S=s.querySelector("#catalog-chat-id"),y=s.querySelector("#catalog-lark-app-id"),$=s.querySelector("#catalog-run-btn"),M=s.querySelector("#catalog-param-errors"),l;try{if(l=JSON.parse(u.value||"{}"),!l||typeof l!="object"||Array.isArray(l))throw new Error(n("catalog.badParamsJson"))}catch(f){M.hidden=!1,M.innerHTML=`<div class="muted error">${C(f?.message??String(f))}</div>`;return}T=!0,$.disabled=!0,$.textContent=n("catalog.running"),M.hidden=!0,g(null),h(null);try{let f=await fetch(`/api/workflows/definitions/${encodeURIComponent(d.definition.workflowId)}/run`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({params:l,chatBinding:{chatId:S.value.trim(),larkAppId:y.value.trim()}})});if(f.status===401)throw new Error(n("catalog.writeAccess"));let m=await f.json().catch(()=>({}));if(!f.ok||!m.ok)throw m.issues?.length&&(M.hidden=!1,M.innerHTML=`<strong>${C(n("catalog.invalidParams"))}</strong><ul>${m.issues.map(I=>`<li>${C(n("catalog.issue",{path:I.path.length?I.path.join("."):"(root)",message:I.message}))}</li>`).join("")}</ul>`),new Error(m.hint??m.message??m.error??n("catalog.runHttp",{status:f.status}));h(n("catalog.runStarted")),m.runId&&(location.hash=`#/workflows/${encodeURIComponent(m.runId)}`)}catch(f){g(f?.message??String(f))}finally{T=!1,$.disabled=!1,$.textContent=n("catalog.run")}}function E(){s.querySelector("#catalog-run-form")?.addEventListener("submit",S=>{S.preventDefault(),L()})}async function k(){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(),g(null),c()}catch(u){g(n("catalog.definitionLoadFailed",{error:u?.message??String(u)})),o.textContent=n("workflow.detail.loadFailed")}}return k().then(()=>{}),()=>{w=!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])=>`
738
+ `,E()}async function S(){if(!d||I)return;let u=s.querySelector("#catalog-params"),$=s.querySelector("#catalog-chat-id"),k=s.querySelector("#catalog-lark-app-id"),v=s.querySelector("#catalog-run-btn"),A=s.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){A.hidden=!1,A.innerHTML=`<div class="muted error">${M(p?.message??String(p))}</div>`;return}I=!0,v.disabled=!0,v.textContent=n("catalog.running"),A.hidden=!0,L(null),f(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:$.value.trim(),larkAppId:k.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&&(A.hidden=!1,A.innerHTML=`<strong>${M(n("catalog.invalidParams"))}</strong><ul>${h.issues.map(T=>`<li>${M(n("catalog.issue",{path:T.path.length?T.path.join("."):"(root)",message:T.message}))}</li>`).join("")}</ul>`),new Error(h.hint??h.message??h.error??n("catalog.runHttp",{status:p.status}));f(n("catalog.runStarted")),h.runId&&(location.hash=`#/workflows/${encodeURIComponent(h.runId)}`)}catch(p){L(p?.message??String(p))}finally{I=!1,v.disabled=!1,v.textContent=n("catalog.run")}}function E(){s.querySelector("#catalog-run-form")?.addEventListener("submit",$=>{$.preventDefault(),S()})}async function b(){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(),L(null),l()}catch(u){L(n("catalog.definitionLoadFailed",{error:u?.message??String(u)})),o.textContent=n("workflow.detail.loadFailed")}}return b().then(()=>{}),()=>{w=!0}}function no(e){let t=Object.entries(e??{});return t.length===0?`<div class="muted">${M(n("catalog.noParams"))}</div>`:`<div class="catalog-param-list">${t.map(([o,r])=>`
718
739
  <article class="catalog-param">
719
740
  <header>
720
- <code>${C(o)}</code>
721
- <span class="wf-status">${C(r.required?n("catalog.required"):n("catalog.optional"))}</span>
722
- <span class="muted">${C(r.type)}${r.format?` \xB7 ${C(r.format)}`:""}</span>
741
+ <code>${M(o)}</code>
742
+ <span class="wf-status">${M(r.required?n("catalog.required"):n("catalog.optional"))}</span>
743
+ <span class="muted">${M(r.type)}${r.format?` \xB7 ${M(r.format)}`:""}</span>
723
744
  </header>
724
- ${r.description?`<div class="muted">${C(n("catalog.description"))}: ${C(r.description)}</div>`:""}
725
- ${"default"in r?`<pre class="wf-io-pre">${C(`${n("catalog.default")}: ${JSON.stringify(r.default,null,2)}`)}</pre>`:""}
745
+ ${r.description?`<div class="muted">${M(n("catalog.description"))}: ${M(r.description)}</div>`:""}
746
+ ${"default"in r?`<pre class="wf-io-pre">${M(`${n("catalog.default")}: ${JSON.stringify(r.default,null,2)}`)}</pre>`:""}
726
747
  </article>
727
- `).join("")}</div>`}var te=null,Se=null;function Ie(){Se!==null&&(window.clearInterval(Se),Se=null)}function Lt(){return te||(te=document.createElement("dialog"),te.className="onboarding-dialog",document.body.appendChild(te),te.addEventListener("close",Ie),te)}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">
748
+ `).join("")}</div>`}var se=null,Te=null;function Le(){Te!==null&&(window.clearInterval(Te),Te=null)}function Lt(){return se||(se=document.createElement("dialog"),se.className="onboarding-dialog",document.body.appendChild(se),se.addEventListener("close",Le),se)}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 me(e){let t=Lt(),o=e.qrDataUrl?`<div class="qr-card">
728
749
  <img class="qr-image" src="${e.qrDataUrl}" alt="${n("botOnboarding.qrAlt")}">
729
750
  ${e.qrUrl?`<a class="onboarding-link" href="${e.qrUrl}" target="_blank" rel="noopener">${n("botOnboarding.openLink")}</a>`:""}
730
751
  </div>`:"",r=e.appId?`<p><b>App ID:</b> <code>${e.appId}</code></p>`:"",a=e.status==="completed"?`<p class="hint-ok">${n("botOnboarding.restartHint")}</p>`:"";t.innerHTML=`<article>
@@ -737,4 +758,4 @@ ${u.join(`
737
758
  ${r}
738
759
  ${a}
739
760
  <form method="dialog"><button>${n("botOnboarding.close")}</button></form>
740
- </article>`}async function ao(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 so(){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(()=>{ao(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=()=>{so()})}var Q=document.getElementById("root"),ce=null;function qe(){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=B.online?n("status.live"):n("status.disconnected"),Pe.className="connection-status "+(B.online?"online":"offline"))}B.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(),qe()}),Ct();try{await Je()}catch(e){console.error("botmux dashboard bootstrap failed",e),B.setOnline(!1)}window.addEventListener("hashchange",qe),qe()})();})();
761
+ </article>`}async function ao(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}`);me(o.job),(o.job.status==="completed"||o.job.status==="failed")&&Le()}async function so(){Le(),me({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}`);me(o.job),Te=window.setInterval(()=>{ao(o.job.id).catch(r=>{Le(),me({id:o.job.id,status:"failed",message:r instanceof Error?r.message:String(r)})})},1200)}catch(t){me({id:"",status:"failed",message:t instanceof Error?t.message:String(t)})}}function Et(){let e=document.getElementById("add-bot-btn");e&&(e.onclick=()=>{so()})}var ne=document.getElementById("root"),we=null;function Pe(){we&&(we(),we=null);let e=location.hash||"#/";e.startsWith("#/workflows/catalog")||e.startsWith("#/workflows-catalog")?we=Tt(ne):e.startsWith("#/workflows")?we=mt(ne):e.startsWith("#/groups")?ot(ne):e.startsWith("#/bot-defaults")?rt(ne):e.startsWith("#/roles")?ct(ne):e.startsWith("#/schedules")?tt(ne):e.startsWith("#/sessions")?Ze(ne):Qe(ne);for(let t of document.querySelectorAll(".sidebar-nav a")){let o=t.getAttribute("href")??"#/";t.classList.toggle("active",o===(e||"#/"))}}var Ne=document.getElementById("status");function Mt(){Ne&&(Ne.textContent=q.online?n("status.live"):n("status.disconnected"),Ne.className="connection-status "+(q.online?"online":"offline"))}q.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===X.locale)}),document.querySelectorAll("[data-theme-mode]").forEach(e=>{e.classList.toggle("active",e.dataset.themeMode===X.themeMode)}),Mt()}function ro(){document.querySelectorAll("[data-locale]").forEach(e=>{e.onclick=()=>X.setLocale(e.dataset.locale)}),document.querySelectorAll("[data-theme-mode]").forEach(e=>{e.onclick=()=>X.setThemeMode(e.dataset.themeMode)})}(async()=>{X.init(),ro(),Et(),X.on(()=>{Ct(),Pe()}),Ct();try{await Je()}catch(e){console.error("botmux dashboard bootstrap failed",e),q.setOnline(!1)}window.addEventListener("hashchange",Pe),Pe()})();})();