botmux 2.43.0 → 2.45.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 (192) hide show
  1. package/README.en.md +1 -2
  2. package/README.md +1 -2
  3. package/dist/bot-registry.d.ts +11 -0
  4. package/dist/bot-registry.d.ts.map +1 -1
  5. package/dist/bot-registry.js +1 -0
  6. package/dist/bot-registry.js.map +1 -1
  7. package/dist/cli/bots-list-output.d.ts +8 -0
  8. package/dist/cli/bots-list-output.d.ts.map +1 -1
  9. package/dist/cli/bots-list-output.js +9 -0
  10. package/dist/cli/bots-list-output.js.map +1 -1
  11. package/dist/cli.d.ts.map +1 -1
  12. package/dist/cli.js +6 -0
  13. package/dist/cli.js.map +1 -1
  14. package/dist/core/command-handler.d.ts.map +1 -1
  15. package/dist/core/command-handler.js +169 -51
  16. package/dist/core/command-handler.js.map +1 -1
  17. package/dist/core/dashboard-ipc-server.d.ts +2 -0
  18. package/dist/core/dashboard-ipc-server.d.ts.map +1 -1
  19. package/dist/core/dashboard-ipc-server.js +61 -2
  20. package/dist/core/dashboard-ipc-server.js.map +1 -1
  21. package/dist/core/role-resolver.d.ts +17 -1
  22. package/dist/core/role-resolver.d.ts.map +1 -1
  23. package/dist/core/role-resolver.js +64 -10
  24. package/dist/core/role-resolver.js.map +1 -1
  25. package/dist/core/session-manager.d.ts.map +1 -1
  26. package/dist/core/session-manager.js +14 -9
  27. package/dist/core/session-manager.js.map +1 -1
  28. package/dist/core/trigger-session.d.ts +9 -0
  29. package/dist/core/trigger-session.d.ts.map +1 -0
  30. package/dist/core/trigger-session.js +158 -0
  31. package/dist/core/trigger-session.js.map +1 -0
  32. package/dist/core/worker-pool.d.ts +22 -0
  33. package/dist/core/worker-pool.d.ts.map +1 -1
  34. package/dist/core/worker-pool.js +54 -2
  35. package/dist/core/worker-pool.js.map +1 -1
  36. package/dist/daemon.d.ts.map +1 -1
  37. package/dist/daemon.js +73 -44
  38. package/dist/daemon.js.map +1 -1
  39. package/dist/dashboard/connector-api.d.ts +3 -0
  40. package/dist/dashboard/connector-api.d.ts.map +1 -0
  41. package/dist/dashboard/connector-api.js +351 -0
  42. package/dist/dashboard/connector-api.js.map +1 -0
  43. package/dist/dashboard/federated-group-core.d.ts +54 -0
  44. package/dist/dashboard/federated-group-core.d.ts.map +1 -0
  45. package/dist/dashboard/federated-group-core.js +165 -0
  46. package/dist/dashboard/federated-group-core.js.map +1 -0
  47. package/dist/dashboard/federation-api.d.ts +42 -0
  48. package/dist/dashboard/federation-api.d.ts.map +1 -0
  49. package/dist/dashboard/federation-api.js +408 -0
  50. package/dist/dashboard/federation-api.js.map +1 -0
  51. package/dist/dashboard/federation-spoke-api.d.ts +76 -0
  52. package/dist/dashboard/federation-spoke-api.d.ts.map +1 -0
  53. package/dist/dashboard/federation-spoke-api.js +618 -0
  54. package/dist/dashboard/federation-spoke-api.js.map +1 -0
  55. package/dist/dashboard/team-group.d.ts +18 -0
  56. package/dist/dashboard/team-group.d.ts.map +1 -0
  57. package/dist/dashboard/team-group.js +7 -0
  58. package/dist/dashboard/team-group.js.map +1 -0
  59. package/dist/dashboard/trigger-api.d.ts +13 -0
  60. package/dist/dashboard/trigger-api.d.ts.map +1 -0
  61. package/dist/dashboard/trigger-api.js +77 -0
  62. package/dist/dashboard/trigger-api.js.map +1 -0
  63. package/dist/dashboard/web/app.js +8 -0
  64. package/dist/dashboard/web/app.js.map +1 -1
  65. package/dist/dashboard/web/bot-defaults.d.ts.map +1 -1
  66. package/dist/dashboard/web/bot-defaults.js +13 -0
  67. package/dist/dashboard/web/bot-defaults.js.map +1 -1
  68. package/dist/dashboard/web/connectors.d.ts +2 -0
  69. package/dist/dashboard/web/connectors.d.ts.map +1 -0
  70. package/dist/dashboard/web/connectors.js +187 -0
  71. package/dist/dashboard/web/connectors.js.map +1 -0
  72. package/dist/dashboard/web/i18n.d.ts.map +1 -1
  73. package/dist/dashboard/web/i18n.js +4 -0
  74. package/dist/dashboard/web/i18n.js.map +1 -1
  75. package/dist/dashboard/web/team-federation.d.ts +3 -0
  76. package/dist/dashboard/web/team-federation.d.ts.map +1 -0
  77. package/dist/dashboard/web/team-federation.js +472 -0
  78. package/dist/dashboard/web/team-federation.js.map +1 -0
  79. package/dist/dashboard/webhook-routes.d.ts +19 -0
  80. package/dist/dashboard/webhook-routes.d.ts.map +1 -0
  81. package/dist/dashboard/webhook-routes.js +321 -0
  82. package/dist/dashboard/webhook-routes.js.map +1 -0
  83. package/dist/dashboard-web/app.js +512 -384
  84. package/dist/dashboard-web/index.html +2 -0
  85. package/dist/dashboard.js +153 -1
  86. package/dist/dashboard.js.map +1 -1
  87. package/dist/i18n/en.d.ts.map +1 -1
  88. package/dist/i18n/en.js +31 -9
  89. package/dist/i18n/en.js.map +1 -1
  90. package/dist/i18n/zh.d.ts.map +1 -1
  91. package/dist/i18n/zh.js +31 -9
  92. package/dist/i18n/zh.js.map +1 -1
  93. package/dist/im/lark/card-builder.d.ts +25 -2
  94. package/dist/im/lark/card-builder.d.ts.map +1 -1
  95. package/dist/im/lark/card-builder.js +141 -43
  96. package/dist/im/lark/card-builder.js.map +1 -1
  97. package/dist/im/lark/card-handler.d.ts.map +1 -1
  98. package/dist/im/lark/card-handler.js +20 -4
  99. package/dist/im/lark/card-handler.js.map +1 -1
  100. package/dist/im/lark/client.d.ts +41 -0
  101. package/dist/im/lark/client.d.ts.map +1 -1
  102. package/dist/im/lark/client.js +174 -48
  103. package/dist/im/lark/client.js.map +1 -1
  104. package/dist/services/bot-owner-store.d.ts +28 -0
  105. package/dist/services/bot-owner-store.d.ts.map +1 -0
  106. package/dist/services/bot-owner-store.js +82 -0
  107. package/dist/services/bot-owner-store.js.map +1 -0
  108. package/dist/services/bot-profile-store.d.ts +16 -0
  109. package/dist/services/bot-profile-store.d.ts.map +1 -0
  110. package/dist/services/bot-profile-store.js +98 -0
  111. package/dist/services/bot-profile-store.js.map +1 -0
  112. package/dist/services/card-prefs-store.d.ts +2 -1
  113. package/dist/services/card-prefs-store.d.ts.map +1 -1
  114. package/dist/services/card-prefs-store.js +13 -4
  115. package/dist/services/card-prefs-store.js.map +1 -1
  116. package/dist/services/connector-store.d.ts +58 -0
  117. package/dist/services/connector-store.d.ts.map +1 -0
  118. package/dist/services/connector-store.js +79 -0
  119. package/dist/services/connector-store.js.map +1 -0
  120. package/dist/services/deployment-identity.d.ts +22 -0
  121. package/dist/services/deployment-identity.d.ts.map +1 -0
  122. package/dist/services/deployment-identity.js +67 -0
  123. package/dist/services/deployment-identity.js.map +1 -0
  124. package/dist/services/federation-membership-store.d.ts +23 -0
  125. package/dist/services/federation-membership-store.d.ts.map +1 -0
  126. package/dist/services/federation-membership-store.js +66 -0
  127. package/dist/services/federation-membership-store.js.map +1 -0
  128. package/dist/services/federation-roster.d.ts +54 -0
  129. package/dist/services/federation-roster.d.ts.map +1 -0
  130. package/dist/services/federation-roster.js +51 -0
  131. package/dist/services/federation-roster.js.map +1 -0
  132. package/dist/services/federation-store.d.ts +76 -0
  133. package/dist/services/federation-store.d.ts.map +1 -0
  134. package/dist/services/federation-store.js +133 -0
  135. package/dist/services/federation-store.js.map +1 -0
  136. package/dist/services/group-creator.d.ts +6 -0
  137. package/dist/services/group-creator.d.ts.map +1 -1
  138. package/dist/services/group-creator.js +10 -1
  139. package/dist/services/group-creator.js.map +1 -1
  140. package/dist/services/groups-store.d.ts +11 -0
  141. package/dist/services/groups-store.d.ts.map +1 -1
  142. package/dist/services/groups-store.js +42 -0
  143. package/dist/services/groups-store.js.map +1 -1
  144. package/dist/services/invite-store.d.ts +28 -0
  145. package/dist/services/invite-store.d.ts.map +1 -0
  146. package/dist/services/invite-store.js +85 -0
  147. package/dist/services/invite-store.js.map +1 -0
  148. package/dist/services/pairing-store.d.ts +47 -0
  149. package/dist/services/pairing-store.d.ts.map +1 -0
  150. package/dist/services/pairing-store.js +132 -0
  151. package/dist/services/pairing-store.js.map +1 -0
  152. package/dist/services/team-roster.d.ts +38 -0
  153. package/dist/services/team-roster.d.ts.map +1 -0
  154. package/dist/services/team-roster.js +82 -0
  155. package/dist/services/team-roster.js.map +1 -0
  156. package/dist/services/team-store.d.ts +54 -0
  157. package/dist/services/team-store.d.ts.map +1 -0
  158. package/dist/services/team-store.js +156 -0
  159. package/dist/services/team-store.js.map +1 -0
  160. package/dist/services/trigger-log-store.d.ts +46 -0
  161. package/dist/services/trigger-log-store.d.ts.map +1 -0
  162. package/dist/services/trigger-log-store.js +132 -0
  163. package/dist/services/trigger-log-store.js.map +1 -0
  164. package/dist/services/trigger-types.d.ts +57 -0
  165. package/dist/services/trigger-types.d.ts.map +1 -0
  166. package/dist/services/trigger-types.js +28 -0
  167. package/dist/services/trigger-types.js.map +1 -0
  168. package/dist/services/webhook-key.d.ts +16 -0
  169. package/dist/services/webhook-key.d.ts.map +1 -0
  170. package/dist/services/webhook-key.js +123 -0
  171. package/dist/services/webhook-key.js.map +1 -0
  172. package/dist/services/webhook-lifecycle-extractors.d.ts +15 -0
  173. package/dist/services/webhook-lifecycle-extractors.d.ts.map +1 -0
  174. package/dist/services/webhook-lifecycle-extractors.js +59 -0
  175. package/dist/services/webhook-lifecycle-extractors.js.map +1 -0
  176. package/dist/services/webhook-lifecycle-store.d.ts +45 -0
  177. package/dist/services/webhook-lifecycle-store.d.ts.map +1 -0
  178. package/dist/services/webhook-lifecycle-store.js +159 -0
  179. package/dist/services/webhook-lifecycle-store.js.map +1 -0
  180. package/dist/setup/verify-permissions.d.ts.map +1 -1
  181. package/dist/setup/verify-permissions.js +4 -0
  182. package/dist/setup/verify-permissions.js.map +1 -1
  183. package/dist/skills/definitions.d.ts.map +1 -1
  184. package/dist/skills/definitions.js +64 -10
  185. package/dist/skills/definitions.js.map +1 -1
  186. package/dist/workflows/events/payloads.d.ts +2 -2
  187. package/dist/workflows/events/schema.d.ts +8 -8
  188. package/dist/workflows/trigger-from-envelope.d.ts +13 -0
  189. package/dist/workflows/trigger-from-envelope.d.ts.map +1 -0
  190. package/dist/workflows/trigger-from-envelope.js +67 -0
  191. package/dist/workflows/trigger-from-envelope.js.map +1 -0
  192. package/package.json +1 -1
@@ -1,33 +1,33 @@
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
1
+ "use strict";(()=>{var _e=class{sessions=new Map;schedules=new Map;online=!0;listeners=new Set;upsertSessions(t){for(let n of t)this.sessions.set(n.sessionId,n);this.emit()}upsertSchedules(t){for(let n of t)this.schedules.set(n.id,n);this.emit()}applySse(t,n){if(t==="session.spawned")this.sessions.set(n.session.sessionId,n.session);else if(t==="session.update"){let s=this.sessions.get(n.sessionId);s&&this.sessions.set(n.sessionId,{...s,...n.patch})}else if(t==="session.exited"){let s=this.sessions.get(n.sessionId);s&&this.sessions.set(n.sessionId,{...s,status:"closed"})}else if(t==="schedule.created")this.schedules.set(n.schedule.id,n.schedule);else if(t==="schedule.updated"){let s=this.schedules.get(n.id);s&&this.schedules.set(n.id,{...s,...n.patch})}else if(t==="schedule.deleted")this.schedules.delete(n.id);else return;this.emit()}setOnline(t){this.online!==t&&(this.online=t,this.emit())}on(t){return this.listeners.add(t),()=>this.listeners.delete(t)}emit(){for(let t of this.listeners)t()}},N=new _e;async function ut(){let[e,t]=await Promise.all([fetch("/api/sessions").then(a=>a.json()),fetch("/api/schedules").then(a=>a.json())]);N.upsertSessions(e.sessions??[]),N.upsertSchedules(t.schedules??[]);let n=new EventSource("/events"),s=["session.spawned","session.update","session.exited","schedule.created","schedule.updated","schedule.deleted","schedule.fired","heartbeat"];for(let a of s)n.addEventListener(a,r=>{try{let i=JSON.parse(r.data);N.applySse(a,i.body??i)}catch{}});n.onerror=()=>N.setOnline(!1),n.onopen=()=>N.setOnline(!0)}var We="botmux.dashboard.locale",mn={"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.privateCard":"/card \u53D1\u79C1\u5BC6\u5361\u7247\uFF08\u4EC5\u6388\u6743\u4EBA\u53EF\u89C1\uFF09","botDefaults.privateCardHelp":"\u5F00\u542F\u540E /card \u6539\u7528\u300C\u4EC5\u7279\u5B9A\u4EBA\u53EF\u89C1\u300D\u7684\u4E34\u65F6\u5361\u7247\uFF1A\u53EA\u53D1\u7ED9 owner\uFF08allowedUsers\uFF09\uFF0C/grant \u6388\u6743\u5BF9\u8BDD\u7684\u4EBA\u548C\u7FA4\u91CC\u5176\u4ED6\u4EBA\u90FD\u770B\u4E0D\u5230\u3002\u4EE3\u4EF7\uFF1A\u662F\u9759\u6001\u5FEB\u7167\u3001\u4E0D\u4F1A\u5B9E\u65F6\u5237\u65B0\uFF1B\u4E14\u4EC5\u666E\u901A\u7FA4\u53EF\u7528\uFF08\u8BDD\u9898\u7FA4 / \u5355\u804A\u4F1A\u5931\u8D25\uFF0C\u4E0D\u964D\u7EA7\uFF09\u3002\u53EA\u5F71\u54CD /card \u547D\u4EE4\uFF0C\u81EA\u52A8\u6D41\u5F0F\u5361\u4E0D\u53D8\u3002","botDefaults.cardPrefSaved":"\u5DF2\u4FDD\u5B58","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 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.:
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"},wn={"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.privateCard":"/card sends a private card (authorized users only)","botDefaults.privateCardHelp":'Makes /card send an ephemeral "visible-to-specific-people" card: delivered only to the owner (allowedUsers); /grant-authorized talk users and everyone else in the chat cannot see it. Trade-off: it is a static snapshot (no live updates) and only works in regular group chats (topic groups / DMs fail, with no fallback). Affects only the /card command; the auto streaming card is unchanged.',"botDefaults.cardPrefSaved":"Saved","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 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">
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"},pt={zh:mn,en:wn};function ft(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 gn(e=[]){for(let t of e){let n=ft(t);if(n)return n}return"zh"}function Me(e){return(t,n)=>{let s=pt[e][t]??pt.zh[t]??t;return n?s.replace(/\{(\w+)\}/g,(a,r)=>{let i=n[r];return i==null?`{${r}}`:String(i)}):s}}function mt(e,t){return(e?ft(e.getItem(We)):null)??gn(t)}var ze="botmux.dashboard.theme";function hn(e){return e==="system"||e==="light"||e==="dark"?e:null}function wt(e,t){return e==="system"?t?"dark":"light":e}function gt(e){return hn(e?.getItem(ze))??"system"}var Je=class{locale="zh";themeMode="system";resolvedTheme="light";listeners=new Set;translate=Me(this.locale);mediaQuery=null;init(){let t=typeof window<"u"?window:void 0;this.locale=mt(t?.localStorage,bn()),this.translate=Me(this.locale),this.themeMode=gt(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,n){return this.translate(t,n)}setLocale(t){this.locale!==t&&(this.locale=t,this.translate=Me(t),window.localStorage.setItem(We,t),this.applyLocale(),this.emit())}setThemeMode(t){this.themeMode!==t&&(this.themeMode=t,window.localStorage.setItem(ze,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=wt(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 bn(){return typeof navigator>"u"?[]:navigator.languages?.length?navigator.languages:[navigator.language].filter(Boolean)}var ae=new Je;function o(e,t){return ae.t(e,t)}function d(e){return e.replace(/[&<>"']/g,t=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"})[t])}function be(e){if(!e)return"-";let t=Date.now()-e;return t<6e4?o("common.now"):t<36e5?Math.floor(t/6e4)+"m":t<864e5?Math.floor(t/36e5)+"h":Math.floor(t/864e5)+"d"}var Ge={chats:[],bots:[]};async function yn(){try{let e=await fetch("/api/groups");if(!e.ok)return;Ge=await e.json()}catch{}}function vn(e){return`status status-${d(e||"unknown")}`}function kn(e){return`<li class="overview-list-row">
14
14
  <div>
15
- <strong>${m(e.title??e.sessionId)}</strong>
16
- <span>${m(e.botName??"")} \xB7 ${m(e.cliId??"unknown")}</span>
15
+ <strong>${d(e.title??e.sessionId)}</strong>
16
+ <span>${d(e.botName??"")} \xB7 ${d(e.cliId??"unknown")}</span>
17
17
  </div>
18
- <span class="${Wt(e.status)}">${m(e.status??"unknown")}</span>
19
- </li>`}function Jt(e){let t=e.nextRunAt?new Date(e.nextRunAt).toLocaleString():"-";return`<li class="overview-list-row">
18
+ <span class="${vn(e.status)}">${d(e.status??"unknown")}</span>
19
+ </li>`}function $n(e){let t=e.nextRunAt?new Date(e.nextRunAt).toLocaleString():"-";return`<li class="overview-list-row">
20
20
  <div>
21
- <strong>${m(e.name??e.id)}</strong>
22
- <span>${m(e.botName??e.larkAppId??"")} \xB7 ${m(e.parsed?.display??"")}</span>
21
+ <strong>${d(e.name??e.id)}</strong>
22
+ <span>${d(e.botName??e.larkAppId??"")} \xB7 ${d(e.parsed?.display??"")}</span>
23
23
  </div>
24
- <span>${m(t)}</span>
25
- </li>`}async function Qe(e){e.innerHTML=`<section class="page hero-page">
24
+ <span>${d(t)}</span>
25
+ </li>`}async function ht(e){e.innerHTML=`<section class="page hero-page">
26
26
  <div class="page-heading">
27
27
  <div>
28
- <p class="eyebrow">${n("app.subtitle")}</p>
29
- <h1>${n("overview.title")}</h1>
30
- <p>${n("overview.subtitle")}</p>
28
+ <p class="eyebrow">${o("app.subtitle")}</p>
29
+ <h1>${o("overview.title")}</h1>
30
+ <p>${o("overview.subtitle")}</p>
31
31
  </div>
32
32
  </div>
33
33
  <div class="metric-grid" id="overview-metrics"></div>
@@ -35,251 +35,251 @@ effects={effects}, activities={activities}, waits={waits}, cancels={cancels}`,"w
35
35
  <section class="panel">
36
36
  <header class="panel-header">
37
37
  <div>
38
- <h2>${n("overview.recentSessions")}</h2>
39
- <p>${n("sessions.subtitle")}</p>
38
+ <h2>${o("overview.recentSessions")}</h2>
39
+ <p>${o("sessions.subtitle")}</p>
40
40
  </div>
41
- <a class="btn-link" href="#/sessions">${n("nav.sessions")}</a>
41
+ <a class="btn-link" href="#/sessions">${o("nav.sessions")}</a>
42
42
  </header>
43
43
  <ul class="overview-list" id="recent-sessions"></ul>
44
44
  </section>
45
45
  <section class="panel">
46
46
  <header class="panel-header">
47
47
  <div>
48
- <h2>${n("overview.nextSchedules")}</h2>
49
- <p>${n("schedules.subtitle")}</p>
48
+ <h2>${o("overview.nextSchedules")}</h2>
49
+ <p>${o("schedules.subtitle")}</p>
50
50
  </div>
51
- <a class="btn-link" href="#/schedules">${n("nav.schedules")}</a>
51
+ <a class="btn-link" href="#/schedules">${o("nav.schedules")}</a>
52
52
  </header>
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=[...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
- <strong>${E.value}</strong>
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","codex-app","cursor","gemini","opencode","mtr","hermes","mira","aiden","coco","unknown"];function Gt(){return`<div class="filter-check-group" role="group" aria-label="${n("sessions.cli")}">
61
- <span class="filter-check-label">${n("sessions.cli")}</span>
62
- ${Xe.map(e=>`
56
+ </section>`;let t=e.querySelector("#overview-metrics"),n=e.querySelector("#recent-sessions"),s=e.querySelector("#next-schedules");function a(){let r=[...N.sessions.values()],i=[...N.schedules.values()],m=r.filter(L=>L.status!=="closed"),k=r.filter(L=>L.status==="working"||L.status==="analyzing"||L.status==="starting"),$=i.filter(L=>L.enabled),p=Ge.bots?.length||new Set(r.map(L=>L.larkAppId).filter(Boolean)).size,g=[{label:o("overview.openSessions"),value:m.length,meta:`${r.length} ${o("overview.total")}`},{label:o("overview.workingSessions"),value:k.length,meta:`${m.length} ${o("overview.active")}`},{label:o("overview.onlineBots"),value:p,meta:o("overview.daemonRegistry")},{label:o("overview.schedules"),value:i.length,meta:`${$.length} ${o("overview.enabledSchedules")}`},{label:o("overview.groups"),value:Ge.chats?.length??0,meta:o("overview.chatMatrix")}];t.innerHTML=g.map(L=>`<article class="metric-card">
57
+ <span>${d(L.label)}</span>
58
+ <strong>${L.value}</strong>
59
+ <small>${d(L.meta)}</small>
60
+ </article>`).join("");let l=r.sort((L,b)=>Number(b.lastMessageAt??0)-Number(L.lastMessageAt??0)).slice(0,6);n.innerHTML=l.length?l.map(L=>kn({...L,title:L.title??`${be(L.lastMessageAt)} \xB7 ${L.sessionId}`})).join(""):`<li class="empty">${o("overview.noSessions")}</li>`;let I=i.filter(L=>L.nextRunAt).sort((L,b)=>Date.parse(L.nextRunAt)-Date.parse(b.nextRunAt)).slice(0,6);s.innerHTML=I.length?I.map($n).join(""):`<li class="empty">${o("overview.noSchedules")}</li>`}N.on(a),a(),yn().then(a)}function se(e,t){return`<th data-sort="${e}" data-label="${d(t)}">${d(t)}</th>`}var bt=["claude-code","codex","codex-app","cursor","gemini","opencode","mtr","hermes","mira","aiden","coco","unknown"];function Sn(){return`<div class="filter-check-group" role="group" aria-label="${o("sessions.cli")}">
61
+ <span class="filter-check-label">${o("sessions.cli")}</span>
62
+ ${bt.map(e=>`
63
63
  <label class="filter-check">
64
- <input type="checkbox" name="cli" value="${m(e)}" checked>
65
- <span>${m(e)}</span>
64
+ <input type="checkbox" name="cli" value="${d(e)}" checked>
65
+ <span>${d(e)}</span>
66
66
  </label>
67
67
  `).join("")}
68
- </div>`}function zt(){return`<section class="page">
68
+ </div>`}function In(){return`<section class="page">
69
69
  <div class="page-heading">
70
70
  <div>
71
- <p class="eyebrow">${n("nav.sessions")}</p>
72
- <h1>${n("sessions.title")}</h1>
73
- <p>${n("sessions.subtitle")}</p>
71
+ <p class="eyebrow">${o("nav.sessions")}</p>
72
+ <h1>${o("sessions.title")}</h1>
73
+ <p>${o("sessions.subtitle")}</p>
74
74
  </div>
75
75
  </div>
76
76
  <form id="filters" class="filters sessions-filters">
77
- <input type="search" name="q" placeholder="${n("sessions.search")}" />
77
+ <input type="search" name="q" placeholder="${o("sessions.search")}" />
78
78
  <select name="status">
79
- <option value="">${n("sessions.anyStatus")}</option>
79
+ <option value="">${o("sessions.anyStatus")}</option>
80
80
  <option>starting</option><option>working</option><option>idle</option>
81
81
  <option>analyzing</option><option>active</option><option>closed</option>
82
82
  </select>
83
83
  <select name="adopt">
84
- <option value="">${n("sessions.adoptAny")}</option>
85
- <option value="yes">${n("sessions.adoptYes")}</option>
86
- <option value="no">${n("sessions.adoptNo")}</option>
84
+ <option value="">${o("sessions.adoptAny")}</option>
85
+ <option value="yes">${o("sessions.adoptYes")}</option>
86
+ <option value="no">${o("sessions.adoptNo")}</option>
87
87
  </select>
88
- <label class="filter-toggle"><input type="checkbox" name="active" checked> ${n("sessions.activeOnly")}</label>
89
- ${Gt()}
88
+ <label class="filter-toggle"><input type="checkbox" name="active" checked> ${o("sessions.activeOnly")}</label>
89
+ ${Sn()}
90
90
  </form>
91
91
  <div id="bulk-bar" class="bulk-bar" hidden>
92
92
  <span id="bulk-count"></span>
93
- <button type="button" id="bulk-close" class="contrast">${n("sessions.closeSelected")}</button>
94
- <button type="button" id="bulk-clear">${n("sessions.clearSelection")}</button>
93
+ <button type="button" id="bulk-close" class="contrast">${o("sessions.closeSelected")}</button>
94
+ <button type="button" id="bulk-clear">${o("sessions.clearSelection")}</button>
95
95
  </div>
96
96
  <table id="sessions-table">
97
97
  <thead><tr>
98
- <th><input type="checkbox" id="select-all" title="${n("sessions.activeOnly")}"></th>
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
- <th>${n("sessions.actions")}</th>
98
+ <th><input type="checkbox" id="select-all" title="${o("sessions.activeOnly")}"></th>
99
+ ${se("botName",o("sessions.bot"))}
100
+ ${se("cliId",o("sessions.cli"))}
101
+ ${se("status",o("sessions.status"))}
102
+ ${se("title",o("sessions.titleCol"))}
103
+ ${se("workingDir",o("sessions.workingDir"))}
104
+ ${se("spawnedAt",o("sessions.created"))}
105
+ ${se("lastMessageAt",o("sessions.last"))}
106
+ ${se("adopt",o("sessions.adopt"))}
107
+ <th>${o("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"),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
- <td><button class="open" type="button">${n("sessions.details")}</button></td>
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>
112
+ </section>`}function yt(e){e.innerHTML=In();let t=e.querySelector("#sessions-table tbody"),n=e.querySelector("#filters"),s=e.querySelector("#drawer"),a=e.querySelector("#select-all"),r=e.querySelector("#bulk-bar"),i=e.querySelector("#bulk-count"),m=e.querySelector("#bulk-close"),k=e.querySelector("#bulk-clear"),$=e.querySelector("#sessions-table"),p=new Set,g="lastMessageAt",l="desc";function I(u){let w=u.status==="closed",h=p.has(u.sessionId)?"checked":"";return`<tr data-id="${d(u.sessionId)}">
113
+ <td><input type="checkbox" class="row-select" ${h} ${w?"disabled":""}></td>
114
+ <td>${d(u.botName??"")}</td>
115
+ <td><span class="badge cli-${d(u.cliId??"unknown")}">${d(u.cliId??"unknown")}</span></td>
116
+ <td><span class="status status-${d(u.status??"unknown")}">${d(u.status??"unknown")}</span></td>
117
+ <td>${d((u.title??"").slice(0,48))}</td>
118
+ <td title="${d(u.workingDir??"")}">${d((u.workingDir??"").slice(-34))}</td>
119
+ <td>${be(u.spawnedAt)}</td>
120
+ <td>${be(u.lastMessageAt)}</td>
121
+ <td>${u.adopt?'<span class="badge">adopt</span>':""}</td>
122
+ <td><button class="open" type="button">${o("sessions.details")}</button></td>
123
+ </tr>`}function L(){let u=new FormData(n),w=(u.get("q")??"").toLowerCase(),h=u.getAll("cli"),E=h.length>0&&h.length<bt.length,M=u.get("status"),A=u.get("adopt"),P=!!u.get("active"),O=[...N.sessions.values()].filter(H=>!E||h.includes(H.cliId??"unknown")).filter(H=>!M||H.status===M).filter(H=>!A||A==="yes"==!!H.adopt).filter(H=>!P||H.status!=="closed").filter(H=>!w||JSON.stringify(H).toLowerCase().includes(w));return O.sort(f),O}function b(u,w){return w==="spawnedAt"||w==="lastMessageAt"?Number(u[w]??0):w==="adopt"?!!u.adopt:String(u[w]??"").toLowerCase()}function f(u,w){let h=b(u,g),E=b(w,g),M=0;return typeof h=="number"&&typeof E=="number"?M=h-E:typeof h=="boolean"&&typeof E=="boolean"?M=Number(h)-Number(E):M=String(h).localeCompare(String(E)),M===0&&(M=Number(u.lastMessageAt??0)-Number(w.lastMessageAt??0)),l==="asc"?M:-M}function T(){$.querySelectorAll("th[data-sort]").forEach(u=>{let w=u.dataset.sort===g;u.classList.toggle("sorted",w),u.setAttribute("aria-sort",w?l==="asc"?"ascending":"descending":"none");let h=u.dataset.label??u.textContent?.trim()??"";u.textContent=w?`${h} ${l==="asc"?"\u25B2":"\u25BC"}`:h})}function S(u){r.hidden=p.size===0,i.textContent=o("sessions.selectedCount",{count:p.size});let w=u.filter(E=>E.status!=="closed");if(w.length===0){a.checked=!1,a.indeterminate=!1,a.disabled=!0;return}a.disabled=!1;let h=w.filter(E=>p.has(E.sessionId)).length;a.checked=h===w.length,a.indeterminate=h>0&&h<w.length}function y(){let u=L();for(let w of[...p]){let h=N.sessions.get(w);(!h||h.status==="closed")&&p.delete(w)}t.innerHTML=u.length?u.map(I).join(""):`<tr><td colspan="10" class="empty">${o("sessions.empty")}</td></tr>`,T(),S(u)}function x(u){let w=u.status==="closed";s.innerHTML=`<article>
124
124
  <header>
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>
125
+ <h3>${d(u.title??u.sessionId)}</h3>
126
+ <span class="status status-${d(u.status??"unknown")}">${d(u.status??"unknown")}</span>
127
+ <p><code>${d(u.sessionId)}</code> <button data-copy="${d(u.sessionId)}">${o("sessions.copy")}</button></p>
128
128
  </header>
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>
129
+ <p><b>${o("sessions.bot")}:</b> ${d(u.botName??"-")} \xB7 <b>${o("sessions.cli")}:</b> ${d(u.cliId??"?")}</p>
130
+ <p><b>chatId:</b> <code>${d(u.chatId??"")}</code> <button data-copy="${d(u.chatId??"")}">${o("sessions.copy")}</button></p>
131
+ <p><b>rootMessageId:</b> <code>${d(u.rootMessageId??"")}</code> <button data-copy="${d(u.rootMessageId??"")}">${o("sessions.copy")}</button></p>
132
+ ${u.threadId?`<p><b>threadId:</b> <code>${d(u.threadId)}</code></p>`:""}
133
+ <p><b>${o("sessions.workingDir")}:</b> ${d(u.workingDir??"-")}</p>
134
134
  <div class="actions">
135
- <button id="locate-btn" type="button">${n("sessions.locate")}</button>
136
- ${c.webPort?`<a class="btn-link primary" href="http://${m(location.hostname)}:${c.webPort}" target="_blank" rel="noopener">${n("sessions.openTerminal")}</a>`:""}
137
- ${p?`<button id="resume-btn" type="button" class="primary">${n("sessions.resume")}</button>`:""}
138
- ${p?"":`<button id="close-btn" type="button" class="contrast">${n("sessions.close")}</button>`}
135
+ <button id="locate-btn" type="button">${o("sessions.locate")}</button>
136
+ ${u.webPort?`<a class="btn-link primary" href="http://${d(location.hostname)}:${u.webPort}" target="_blank" rel="noopener">${o("sessions.openTerminal")}</a>`:""}
137
+ ${w?`<button id="resume-btn" type="button" class="primary">${o("sessions.resume")}</button>`:""}
138
+ ${w?"":`<button id="close-btn" type="button" class="contrast">${o("sessions.close")}</button>`}
139
139
  </div>
140
- <form method="dialog"><button>${n("sessions.dismiss")}</button></form>
141
- </article>`,r.querySelectorAll("[data-copy]").forEach(R=>{R.onclick=()=>{navigator.clipboard.writeText(R.dataset.copy??""),R.textContent=n("sessions.copied"),setTimeout(()=>{R.textContent=n("sessions.copy")},800)}});let h=r.querySelector("#locate-btn");h&&(h.onclick=async()=>{h.disabled=!0,h.textContent=n("sessions.locating");try{let R=await fetch(`/api/sessions/${encodeURIComponent(c.sessionId)}/locate`,{method:"POST"}),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">
140
+ <form method="dialog"><button>${o("sessions.dismiss")}</button></form>
141
+ </article>`,s.querySelectorAll("[data-copy]").forEach(A=>{A.onclick=()=>{navigator.clipboard.writeText(A.dataset.copy??""),A.textContent=o("sessions.copied"),setTimeout(()=>{A.textContent=o("sessions.copy")},800)}});let h=s.querySelector("#locate-btn");h&&(h.onclick=async()=>{h.disabled=!0,h.textContent=o("sessions.locating");try{let A=await fetch(`/api/sessions/${encodeURIComponent(u.sessionId)}/locate`,{method:"POST"}),P=await A.json();if(P.ok){let O=30;h.textContent=o("sessions.cooldown",{seconds:O});let H=setInterval(()=>{O-=1,O<=0?(clearInterval(H),h.disabled=!1,h.textContent=o("sessions.locate")):h.textContent=o("sessions.cooldown",{seconds:O})},1e3)}else alert(`Locate failed: ${P.error??A.status}`),h.disabled=!1,h.textContent=o("sessions.locate")}catch(A){alert(`Locate error: ${A}`),h.disabled=!1,h.textContent=o("sessions.locate")}});let E=s.querySelector("#resume-btn");E&&(E.onclick=async()=>{E.disabled=!0;try{let A=await fetch(`/api/sessions/${encodeURIComponent(u.sessionId)}/resume`,{method:"POST"}),P=await A.json().catch(()=>({}));if(!A.ok||P.ok===!1){alert(`${o("sessions.resumeFailed")}: ${P?.error??A.status}`),E.disabled=!1;return}s.close()}catch(A){alert(`${o("sessions.resumeFailed")}: ${A}`),E.disabled=!1}});let M=s.querySelector("#close-btn");M&&(M.onclick=async()=>{if(confirm(o("sessions.closeConfirm"))){M.disabled=!0;try{await fetch(`/api/sessions/${encodeURIComponent(u.sessionId)}/close`,{method:"POST"})}finally{s.close()}}}),s.showModal()}t.addEventListener("click",u=>{let w=u.target;if(w.classList.contains("row-select")){let A=w.closest("tr[data-id]");if(!A)return;w.checked?p.add(A.dataset.id):p.delete(A.dataset.id),S(L());return}let h=w.closest("td");if(h&&h.querySelector(".row-select"))return;let E=w.closest("tr[data-id]");if(!E)return;let M=N.sessions.get(E.dataset.id);M&&x(M)}),a.addEventListener("change",()=>{let u=L().filter(w=>w.status!=="closed");for(let w of u)a.checked?p.add(w.sessionId):p.delete(w.sessionId);y()}),k.addEventListener("click",()=>{p.clear(),y()}),m.addEventListener("click",async()=>{let u=[...p];if(u.length===0||!confirm(o("sessions.closeBulkConfirm",{count:u.length})))return;m.disabled=!0,k.disabled=!0;let w=m.textContent,h=0,E=0,M=[...u];m.textContent=`0/${u.length}`;async function A(){for(;M.length;){let P=M.shift();try{let O=await fetch(`/api/sessions/${encodeURIComponent(P)}/close`,{method:"POST"}),H=await O.json().catch(()=>({}));(!O.ok||H?.ok===!1)&&(E+=1)}catch{E+=1}finally{h+=1,m.textContent=`${h}/${u.length}`}}}await Promise.all(Array.from({length:Math.min(6,u.length)},()=>A())),m.textContent=w,m.disabled=!1,k.disabled=!1,p.clear(),y(),E>0&&alert(`Failed: ${E}/${u.length}`)}),$.querySelectorAll("th[data-sort]").forEach(u=>{u.addEventListener("click",()=>{let w=u.dataset.sort;g===w?l=l==="asc"?"desc":"asc":(g=w,l=w==="spawnedAt"||w==="lastMessageAt"?"desc":"asc"),y()})}),n.addEventListener("input",y),N.on(y),y()}function Tn(){return`<section class="page">
142
142
  <div class="page-heading">
143
143
  <div>
144
- <p class="eyebrow">${n("nav.schedules")}</p>
145
- <h1>${n("schedules.title")}</h1>
146
- <p>${n("schedules.subtitle")}</p>
144
+ <p class="eyebrow">${o("nav.schedules")}</p>
145
+ <h1>${o("schedules.title")}</h1>
146
+ <p>${o("schedules.subtitle")}</p>
147
147
  </div>
148
148
  </div>
149
149
  <form id="sched-filters" class="filters">
150
- <input type="search" name="q" placeholder="${n("schedules.search")}" />
150
+ <input type="search" name="q" placeholder="${o("schedules.search")}" />
151
151
  <select name="kind">
152
- <option value="">${n("schedules.anyKind")}</option>
152
+ <option value="">${o("schedules.anyKind")}</option>
153
153
  <option>cron</option>
154
154
  <option>interval</option>
155
155
  <option>once</option>
156
156
  </select>
157
- <label><input type="checkbox" name="enabled"> ${n("schedules.enabledOnly")}</label>
157
+ <label><input type="checkbox" name="enabled"> ${o("schedules.enabledOnly")}</label>
158
158
  </form>
159
159
  <table>
160
160
  <thead><tr>
161
- <th>${n("schedules.name")}</th><th>${n("schedules.bot")}</th><th>${n("schedules.schedule")}</th><th>${n("schedules.next")}</th><th>${n("schedules.last")}</th>
162
- <th>${n("schedules.repeat")}</th><th>${n("schedules.enabled")}</th><th>${n("schedules.actions")}</th>
161
+ <th>${o("schedules.name")}</th><th>${o("schedules.bot")}</th><th>${o("schedules.schedule")}</th><th>${o("schedules.next")}</th><th>${o("schedules.last")}</th>
162
+ <th>${o("schedules.repeat")}</th><th>${o("schedules.enabled")}</th><th>${o("schedules.actions")}</th>
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"),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
- <td>${et(s.nextRunAt)}</td>
171
- <td>${et(s.lastRunAt)} ${s.lastStatus==="error"?"\u26A0\uFE0F":""}</td>
172
- <td>${s.repeat?`${s.repeat.completed}/${s.repeat.times??"\u221E"}`:"\u2014"}</td>
173
- <td>${s.enabled?"\u2713":"\u2717"}</td>
166
+ </section>`}function vt(e){if(!e)return"\u2014";try{return new Date(e).toLocaleString()}catch{return e}}function kt(e){e.innerHTML=Tn();let t=e.querySelector("#schedules-tbody"),n=e.querySelector("#sched-filters");function s(){let r=new FormData(n),i=(r.get("q")??"").toLowerCase(),m=r.get("kind"),k=!!r.get("enabled");return[...N.schedules.values()].filter($=>!m||$.parsed?.kind===m).filter($=>!k||$.enabled).filter($=>!i||JSON.stringify($).toLowerCase().includes(i)).sort(($,p)=>{if($.enabled!==p.enabled)return $.enabled?-1:1;let g=$.nextRunAt?Date.parse($.nextRunAt):1/0,l=p.nextRunAt?Date.parse(p.nextRunAt):1/0;return g-l})}function a(){t.innerHTML=s().map(r=>`<tr data-id="${d(r.id)}">
167
+ <td>${d(r.name??r.id)}</td>
168
+ <td>${d(r.botName??r.larkAppId??"-")}</td>
169
+ <td><code>${d(r.parsed?.display??"?")}</code></td>
170
+ <td>${vt(r.nextRunAt)}</td>
171
+ <td>${vt(r.lastRunAt)} ${r.lastStatus==="error"?"\u26A0\uFE0F":""}</td>
172
+ <td>${r.repeat?`${r.repeat.completed}/${r.repeat.times??"\u221E"}`:"\u2014"}</td>
173
+ <td>${r.enabled?"\u2713":"\u2717"}</td>
174
174
  <td class="actions-cell">
175
- <button data-op="run" type="button">${n("schedules.runNow")}</button>
176
- ${s.enabled?`<button data-op="pause" type="button">${n("schedules.pause")}</button>`:`<button data-op="resume" type="button">${n("schedules.resume")}</button>`}
175
+ <button data-op="run" type="button">${o("schedules.runNow")}</button>
176
+ ${r.enabled?`<button data-op="pause" type="button">${o("schedules.pause")}</button>`:`<button data-op="resume" type="button">${o("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 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">
178
+ </tr>`).join("")||`<tr><td colspan="8" class="empty">${o("schedules.empty")}</td></tr>`}t.addEventListener("click",async r=>{let i=r.target.closest("button[data-op]");if(!i)return;let m=i.closest("tr[data-id]");if(!m)return;let k=m.dataset.id,$=i.dataset.op;i.disabled=!0;let p=i.textContent;i.textContent="...";try{let g=await fetch(`/api/schedules/${encodeURIComponent(k)}/${$}`,{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{i.disabled=!1,i.textContent=p}}),n.addEventListener("input",a),N.on(a),a()}var W={chats:[],bots:[]};function Ln(){return`<section class="page">
179
179
  <div class="page-heading">
180
180
  <div>
181
- <p class="eyebrow">${n("nav.groups")}</p>
182
- <h1>${n("groups.title")}</h1>
183
- <p>${n("groups.subtitle")}</p>
181
+ <p class="eyebrow">${o("nav.groups")}</p>
182
+ <h1>${o("groups.title")}</h1>
183
+ <p>${o("groups.subtitle")}</p>
184
184
  </div>
185
185
  </div>
186
186
  <form id="g-filters" class="filters">
187
- <input type="search" name="q" placeholder="${n("groups.search")}" />
188
- <label><input type="checkbox" name="missing"> ${n("groups.missingOnly")}</label>
189
- <button type="button" id="g-refresh">${n("groups.refresh")}</button>
190
- <button type="button" id="g-create" class="primary">${n("groups.create")}</button>
187
+ <input type="search" name="q" placeholder="${o("groups.search")}" />
188
+ <label><input type="checkbox" name="missing"> ${o("groups.missingOnly")}</label>
189
+ <button type="button" id="g-refresh">${o("groups.refresh")}</button>
190
+ <button type="button" id="g-create" class="primary">${o("groups.create")}</button>
191
191
  </form>
192
192
  <table>
193
193
  <thead id="g-head"></thead>
194
194
  <tbody id="g-body"></tbody>
195
195
  </table>
196
196
  <dialog id="g-drawer"></dialog>
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=>`
197
+ </section>`}async function fe(){W=await(await fetch("/api/groups")).json()}async function En(){return(await fetch("/api/groups")).json()}function Mn(e,t){if(t.size===0)return!0;let n=e?.memberBots??[];for(let s of t)if(!n.some(a=>a.larkAppId===s&&a.inChat))return!1;return!0}function $t(e,t){return e.filter(n=>!t||!t.has(n.larkAppId)).map(n=>`
198
198
  <label class="checkbox-row">
199
- <input type="checkbox" name="bot" value="${m(o.larkAppId)}">
200
- ${m(o.botName??o.larkAppId)} <small>(${m(o.larkAppId)})</small>
199
+ <input type="checkbox" name="bot" value="${d(n.larkAppId)}">
200
+ ${d(n.botName??n.larkAppId)} <small>(${d(n.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 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=`
202
+ `).join("")}async function St(e){e.innerHTML=Ln();let t=e.querySelector("#g-head"),n=e.querySelector("#g-body"),s=e.querySelector("#g-filters"),a=e.querySelector("#g-refresh"),r=e.querySelector("#g-drawer");a.onclick=async()=>{a.disabled=!0;try{await fe(),p()}finally{a.disabled=!1}};let i=e.querySelector("#g-create");i.onclick=()=>m(),await fe();function m(){let l=W.bots;if(l.length===0){alert(o("groups.noBotsOnline"));return}r.innerHTML=`
203
203
  <article>
204
- <header><h3>${n("groups.createTitle")}</h3></header>
205
- <p>${n("groups.createHelp")}</p>
204
+ <header><h3>${o("groups.createTitle")}</h3></header>
205
+ <p>${o("groups.createHelp")}</p>
206
206
  <form id="g-createform">
207
207
  <label class="form-row">
208
- <span>${n("groups.name")}</span>
209
- <input type="text" name="name" placeholder="${n("groups.namePlaceholder")}" maxlength="60">
208
+ <span>${o("groups.name")}</span>
209
+ <input type="text" name="name" placeholder="${o("groups.namePlaceholder")}" maxlength="60">
210
210
  </label>
211
211
  <label class="form-row">
212
- <span>${n("groups.bindDir")}</span>
212
+ <span>${o("groups.bindDir")}</span>
213
213
  <input type="text" name="bindWorkingDir" placeholder="e.g. ~/projects/botmux">
214
- <small>${n("groups.bindDirHelp")}</small>
214
+ <small>${o("groups.bindDirHelp")}</small>
215
215
  </label>
216
216
  <fieldset>
217
- <legend>${n("groups.botPicker")}</legend>
218
- ${nt(l)}
217
+ <legend>${o("groups.botPicker")}</legend>
218
+ ${$t(l)}
219
219
  </fieldset>
220
220
  <div class="actions">
221
- <button type="submit" class="primary">${n("groups.createSubmit")}</button>
222
- <button type="button" id="g-create-cancel">${n("groups.cancel")}</button>
221
+ <button type="submit" class="primary">${o("groups.createSubmit")}</button>
222
+ <button type="button" id="g-create-cancel">${o("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 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=`
225
+ </article>`,r.showModal(),r.querySelector("#g-create-cancel").onclick=()=>r.close(),r.querySelector("#g-createform").onsubmit=async b=>{b.preventDefault();let f=new FormData(b.target),T=(f.get("name")??"").trim(),S=(f.get("bindWorkingDir")??"").trim(),y=f.getAll("bot");if(y.length===0){alert("Pick at least one bot.");return}let x=b.target.querySelector("button[type=submit]");x&&(x.disabled=!0,x.textContent="Creating...");try{let u=await fetch("/api/groups/create",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({name:T||void 0,larkAppIds:y,bindWorkingDir:S||void 0})}),w=await u.json();if(w.ok&&w.chatId){k(w);let h=Array.isArray(w.invalidBotIds)?w.invalidBotIds:[],E=y.filter(A=>!h.includes(A)),M=new Set(E);typeof w.creator=="string"&&w.creator&&M.add(w.creator),I(w.chatId,T||w.chatId,E,w.creator),p(),L(w.chatId,M).catch(()=>{})}else alert(`Failed: ${w.error??u.status}`),r.close()}catch(u){alert("Network error: "+u),r.close()}};function I(b,f,T,S){let y=new Set(T);S&&y.add(S);let x=W.bots.map(w=>({larkAppId:w.larkAppId,botName:w.botName,inChat:y.has(w.larkAppId),oncallChat:null})),u={chatId:b,name:f,ownerId:S??null,memberBots:x};W.chats=[u,...W.chats.filter(w=>w.chatId!==b)]}async function L(b,f){let T=[600,1200,1200,1200,1200,1200];for(let S of T){await new Promise(u=>setTimeout(u,S));let y;try{y=await En()}catch{continue}let x=(y.chats??[]).find(u=>u.chatId===b);if(x&&Mn(x,f)){W=y,p();return}}}}function k(l){let I=String(l.chatId),L=`https://applink.feishu.cn/client/chat/open?openChatId=${encodeURIComponent(I)}`,b=l.invalidBotIds??[],f=l.invalidUserIds??[],T=l.autoInvitedOpenId,S=!!l.autoInviteRejected,y=l.ownerTransferredTo,x=l.transferError,u=l.notifyMessageId,w=l.notifyError,h=Array.isArray(l.oncallBindings)?l.oncallBindings:[],E=h.filter(H=>H?.ok).length,M=h.filter(H=>!H?.ok),A=h.length>0?M.length===0?`<p class="hint-ok">\u5DF2\u7ED1\u5B9A\u76EE\u5F55\uFF1A<code>${d(l.bindResolvedPath??"")}</code>\uFF08${E}/${h.length} bots\uFF09</p>`:`<p class="hint-warn">\u76EE\u5F55\u7ED1\u5B9A\u90E8\u5206\u5931\u8D25\uFF1A\u6210\u529F ${E}/${h.length}\u3002${M.map(H=>`<br><code>${d(H.larkAppId??"?")}</code>: ${d(H.error??"unknown")}`).join("")}</p>`:"",P;if(T){let H=y?"<br><small>\u7FA4\u4E3B\u5DF2\u4ECE\u673A\u5668\u4EBA\u8F6C\u8BA9\u7ED9\u4F60\u3002</small>":x?`<br><small class="hint-warn-inline">\u26A0 \u81EA\u52A8\u8F6C\u8BA9\u7FA4\u4E3B\u5931\u8D25\uFF08${d(x)}\uFF09\uFF0C\u4F60\u73B0\u5728\u662F\u6210\u5458\u4F46\u7FA4\u4E3B\u4ECD\u662F\u673A\u5668\u4EBA\u3002</small>`:"",q=u?`<br><small>\u673A\u5668\u4EBA\u5DF2\u5728\u7FA4\u91CC @ \u4E86\u4F60\uFF08\u6D88\u606F id <code>${d(u)}</code>\uFF09\uFF0C\u770B\u98DE\u4E66\u901A\u77E5\u5C31\u80FD\u8FDB\u7FA4\u3002</small>`:w?`<br><small class="hint-warn-inline">\u26A0 \u81EA\u52A8 @ \u901A\u77E5\u5931\u8D25\uFF08${d(w)}\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>`:"";P=`<p class="hint-ok">\u5DF2\u81EA\u52A8\u9080\u8BF7\u4F60\uFF08<code>${d(T)}</code>\uFF09\u4F5C\u4E3A\u6210\u5458\u3002${H}${q}</p>`}else S?P='<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>':P='<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 O=[b.length?`<li>\u65E0\u6548 bot id: <code>${b.map(d).join(", ")}</code></li>`:"",f.length?`<li>\u65E0\u6548\u7528\u6237 open_id: <code>${f.map(d).join(", ")}</code></li>`:""].filter(Boolean).join("");r.innerHTML=`
226
226
  <article>
227
- <header><h3>${n("groups.successTitle")}</h3></header>
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
- ${O}
231
- ${R}
232
- ${B?`<ul>${B}</ul>`:""}
227
+ <header><h3>${o("groups.successTitle")}</h3></header>
228
+ <p><b>chatId:</b> <code>${d(I)}</code> <button type="button" data-copy="${d(I)}">${o("sessions.copy")}</button></p>
229
+ <p><b>\u521B\u5EFA\u8005:</b> <code>${d(l.creator??"?")}</code></p>
230
+ ${P}
231
+ ${A}
232
+ ${O?`<ul>${O}</ul>`:""}
233
233
  <div class="actions">
234
- <a class="btn-link primary" href="${E}" target="_blank" rel="noopener">${n("groups.openGroup")}</a>
235
- <button type="button" id="g-create-close">${n("sessions.dismiss")}</button>
234
+ <a class="btn-link primary" href="${L}" target="_blank" rel="noopener">${o("groups.openGroup")}</a>
235
+ <button type="button" id="g-create-close">${o("sessions.dismiss")}</button>
236
236
  </div>
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
- <th>${n("groups.chat")}</th>
239
- ${j.bots.map(l=>`<th>${m(l.botName??l.larkAppId)}</th>`).join("")}
240
- <th>${n("groups.actions")}</th>
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)}">
237
+ </article>`,r.querySelectorAll("[data-copy]").forEach(H=>{H.onclick=()=>{navigator.clipboard.writeText(H.dataset.copy??""),H.textContent=o("sessions.copied"),setTimeout(()=>{H.textContent=o("sessions.copy")},800)}}),r.querySelector("#g-create-close").onclick=()=>r.close()}function $(){t.innerHTML=`<tr>
238
+ <th>${o("groups.chat")}</th>
239
+ ${W.bots.map(l=>`<th>${d(l.botName??l.larkAppId)}</th>`).join("")}
240
+ <th>${o("groups.actions")}</th>
241
+ </tr>`}function p(){$();let l=new FormData(s),I=(l.get("q")??"").toLowerCase(),L=!!l.get("missing"),b=W.chats.filter(f=>!I||(f.name??"").toLowerCase().includes(I)||f.chatId.toLowerCase().includes(I)||(f.ownerId??"").toLowerCase().includes(I)).filter(f=>!L||f.memberBots.some(T=>!T.inChat));if(b.length===0){n.innerHTML=`<tr><td colspan="${W.bots.length+2}" class="empty">${o("groups.empty")}</td></tr>`;return}n.innerHTML=b.map(f=>`<tr data-chat="${d(f.chatId)}">
242
242
  <td>
243
- <strong>${m(u.name??u.chatId)}</strong><br>
244
- <small><code>${m(u.chatId)}</code></small>
243
+ <strong>${d(f.name??f.chatId)}</strong><br>
244
+ <small><code>${d(f.chatId)}</code></small>
245
245
  </td>
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("")}
246
+ ${W.bots.map(T=>{let S=f.memberBots.find(u=>u.larkAppId===T.larkAppId),y=S?S.error?"!":S.inChat?"\u2713":"\u2717":"?";return`<td class="${S?S.error?"cell-error":S.inChat?"cell-in":"cell-out":"cell-unknown"}" title="${d(S?.error??"")}">${y}</td>`}).join("")}
247
247
  <td>
248
- <button class="add-bots" type="button">${n("groups.addBots")}</button>
249
- <button class="manage-chat" type="button">${n("groups.manage")}</button>
248
+ <button class="add-bots" type="button">${o("groups.addBots")}</button>
249
+ <button class="manage-chat" type="button">${o("groups.manage")}</button>
250
250
  </td>
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=`
251
+ </tr>`).join("")}p(),n.addEventListener("click",async l=>{let I=l.target.closest("button.add-bots");if(!I)return;let b=I.closest("tr[data-chat]").dataset.chat,f=W.chats.find(y=>y.chatId===b);if(!f)return;let T=new Set(f.memberBots.filter(y=>y.inChat).map(y=>y.larkAppId));if(!W.bots.filter(y=>!T.has(y.larkAppId)).length){alert("All configured bots are already in this chat.");return}r.innerHTML=`
252
252
  <article>
253
- <header><h3>${n("groups.addBots")} \xB7 ${m(u.name??u.chatId)}</h3></header>
254
- <p>${n("groups.createHelp")}</p>
253
+ <header><h3>${o("groups.addBots")} \xB7 ${d(f.name??f.chatId)}</h3></header>
254
+ <p>${o("groups.createHelp")}</p>
255
255
  <form id="g-addform">
256
- ${nt(j.bots,$)}
256
+ ${$t(W.bots,T)}
257
257
  <div class="actions">
258
- <button type="submit" class="primary">${n("groups.addBots")}</button>
259
- <button type="button" id="g-cancel">${n("groups.cancel")}</button>
258
+ <button type="submit" class="primary">${o("groups.addBots")}</button>
259
+ <button type="button" id="g-cancel">${o("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 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=`
262
+ </article>`,r.showModal(),r.querySelector("#g-cancel").onclick=()=>r.close(),r.querySelector("#g-addform").onsubmit=async y=>{y.preventDefault();let u=new FormData(y.target).getAll("bot");if(u.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:u})})).json();if(h.error==="no_proxy_bot")alert("No bot is currently in this chat \u2014 add one manually in Feishu first, then retry.");else if(h.result){let E=h.result.map(M=>`${M.id}: ${M.ok?"OK":`failed (${M.error??"unknown"})`}`).join(`
263
+ `);alert(E),await fe(),p()}else alert(`Unexpected response: ${JSON.stringify(h)}`)}catch(w){alert("Network error: "+w)}finally{r.close()}}}),n.addEventListener("click",async l=>{let I=l.target.closest("button.manage-chat");if(!I)return;let b=I.closest("tr[data-chat]").dataset.chat,f=W.chats.find(T=>T.chatId===b);f&&g(f)});function g(l){let I=l.memberBots.filter(b=>b.inChat),L=typeof l.ownerId=="string"?l.ownerId:"";r.innerHTML=`
264
264
  <article>
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>
265
+ <header><h3>${o("groups.manageTitle",{name:l.name??l.chatId})}</h3></header>
266
+ <p><b>chatId:</b> <code>${d(l.chatId)}</code></p>
267
+ <p><b>${o("groups.owner")}:</b> <code>${d(l.ownerId??o("common.unknown"))}</code></p>
268
268
 
269
269
  <fieldset>
270
- <legend>${n("groups.oncall")}</legend>
271
- <p><small>${n("groups.oncallHelp")}</small></p>
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)}">
270
+ <legend>${o("groups.oncall")}</legend>
271
+ <p><small>${o("groups.oncallHelp")}</small></p>
272
+ ${I.length===0?'<p class="empty">\u6CA1\u6709\u673A\u5668\u4EBA\u5728\u7FA4\u91CC</p>':I.map(b=>{let f=!!b.oncallChat,T=b.oncallChat?.workingDir??"";return`
273
+ <div class="oncall-row" data-bot="${d(b.larkAppId)}">
274
274
  <label class="checkbox-row">
275
- <input type="checkbox" data-action="toggle" ${u?"checked":""}>
276
- <strong>${m(b.botName??b.larkAppId)}</strong>
277
- <small>(${m(b.larkAppId)})</small>
275
+ <input type="checkbox" data-action="toggle" ${f?"checked":""}>
276
+ <strong>${d(b.botName??b.larkAppId)}</strong>
277
+ <small>(${d(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="${m($)}" ${u?"":"disabled"}>
282
- <button type="button" data-action="save">${n("groups.save")}</button>
281
+ value="${d(T)}" ${f?"":"disabled"}>
282
+ <button type="button" data-action="save">${o("groups.save")}</button>
283
283
  <span class="oncall-status" data-status></span>
284
284
  </div>
285
285
  </div>
@@ -287,130 +287,135 @@ effects={effects}, activities={activities}, waits={waits}, cancels={cancels}`,"w
287
287
  </fieldset>
288
288
 
289
289
  <fieldset>
290
- <legend>${n("groups.leaveTitle")}</legend>
291
- ${S.length===0?'<p class="empty">\u6CA1\u6709\u673A\u5668\u4EBA\u5728\u7FA4\u91CC</p>':S.map(b=>`
290
+ <legend>${o("groups.leaveTitle")}</legend>
291
+ ${I.length===0?'<p class="empty">\u6CA1\u6709\u673A\u5668\u4EBA\u5728\u7FA4\u91CC</p>':I.map(b=>`
292
292
  <label class="checkbox-row">
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>
293
+ <input type="checkbox" name="leave-bot" value="${d(b.larkAppId)}">
294
+ ${d(b.botName??b.larkAppId)}
295
+ <small>${b.larkAppId===L?"\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" ${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>
301
+ <button id="g-leave-btn" type="button" ${I.length===0?"disabled":""}>${o("groups.leaveSelected")}</button>
302
+ <button id="g-disband-btn" type="button" class="contrast" ${I.length===0?"disabled":""}>${o("groups.disband")}</button>
303
303
  </div>
304
- <p class="hint-warn"><small>${n("groups.dangerHint")}</small></p>
305
- <form method="dialog"><button>${n("sessions.dismiss")}</button></form>
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
- ${u.join(`
304
+ <p class="hint-warn"><small>${o("groups.dangerHint")}</small></p>
305
+ <form method="dialog"><button>${o("sessions.dismiss")}</button></form>
306
+ </article>`,r.showModal(),r.querySelectorAll(".oncall-row").forEach(b=>{let f=b.dataset.bot,T=b.querySelector("input[data-action=toggle]"),S=b.querySelector("input[data-input=workingDir]"),y=b.querySelector("button[data-action=save]"),x=b.querySelector("[data-status]");T.addEventListener("change",()=>{S.disabled=!T.checked,T.checked&&S.focus()}),y.addEventListener("click",async()=>{x.textContent="",x.className="oncall-status";let u=T.checked,w=S.value.trim();if(u&&!w){x.textContent=o("groups.needWorkingDir"),x.classList.add("hint-warn-inline");return}y.disabled=!0;try{let h=`/api/groups/${encodeURIComponent(l.chatId)}/oncall/${encodeURIComponent(f)}`,E=u?await fetch(h,{method:"PUT",headers:{"content-type":"application/json"},body:JSON.stringify({workingDir:w})}):await fetch(h,{method:"DELETE"}),M=await E.json().catch(()=>({}));if(E.ok&&M.ok){x.textContent=u?`\u2713 \u5DF2\u7ED1\u5B9A \u2192 ${M.resolvedPath??w}`:"\u2713 \u5DF2\u89E3\u7ED1",x.classList.add("hint-ok");try{await fe(),p()}catch{}}else x.textContent=`\u2717 ${M.error??E.status}`,x.classList.add("hint-warn-inline")}catch(h){x.textContent=`\u2717 ${h?.message??h}`,x.classList.add("hint-warn-inline")}finally{y.disabled=!1}})}),r.querySelector("#g-leave-btn").onclick=async()=>{let b=[...r.querySelectorAll("input[name=leave-bot]:checked")].map(f=>f.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 T=await(await fetch(`/api/groups/${encodeURIComponent(l.chatId)}/leave`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({larkAppIds:b})})).json(),S=(T.result??[]).map(y=>{if(!y.ok)return`${y.larkAppId}: \u5931\u8D25 (${y.error??"unknown"})`;let x=y.closedSessions??[],u=x.filter(E=>!E.ok).length,w=x.length-u,h=x.length===0?"":u===0?`\uFF08\u5173\u95ED ${w} \u4E2A\u4F1A\u8BDD\uFF09`:`\uFF08\u5173\u95ED ${w} \u4E2A\uFF0C${u} \u4E2A\u5931\u8D25\uFF09`;return`${y.larkAppId}: OK${h}`}).join(`
307
+ `);alert(S||`Unexpected: ${JSON.stringify(T)}`),await fe(),p()}catch(f){alert("Network error: "+f)}finally{r.close()}},r.querySelector("#g-disband-btn").onclick=async()=>{if(I.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=[...I].sort((T,S)=>(S.larkAppId===L?1:0)-(T.larkAppId===L?1:0)),f=[];for(let T of b)try{let S=await fetch(`/api/groups/${encodeURIComponent(l.chatId)}/disband`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({larkAppId:T.larkAppId})}),y=await S.json();if(y.ok){let x=y.closedSessions??[],u=x.filter(E=>!E.ok).length,w=x.length-u,h=x.length===0?"":u===0?`
308
+ \u5173\u95ED\u4E86 ${w} \u4E2A\u4F1A\u8BDD\u3002`:`
309
+ \u5173\u95ED\u4E86 ${w} \u4E2A\u4F1A\u8BDD\uFF0C${u} \u4E2A\u4F1A\u8BDD\u5173\u95ED\u5931\u8D25\u3002`;alert(`\u5DF2\u89E3\u6563\uFF08\u7531 ${T.botName??T.larkAppId} \u6267\u884C\uFF09${h}`),await fe(),p(),r.close();return}f.push(`${T.botName??T.larkAppId}: ${y.error??S.status}`)}catch(S){f.push(`${T.botName??T.larkAppId}: ${S}`)}alert(`\u6240\u6709\u5728\u7FA4\u673A\u5668\u4EBA\u5747\u65E0\u6CD5\u89E3\u6563\uFF1A
310
+ ${f.join(`
311
311
  `)}
312
312
 
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">
313
+ \u5EFA\u8BAE\u6539\u7528\u300C\u9000\u51FA\u7FA4\u804A\u300D\u3002`)}}s.addEventListener("input",p)}var re={bots:[]},me=null;function Cn(){return`<section class="page">
314
314
  <div class="page-heading">
315
315
  <div>
316
- <p class="eyebrow">${n("nav.botDefaults")}</p>
317
- <h1>${n("botDefaults.title")}</h1>
318
- <p>${n("botDefaults.subtitle")}</p>
316
+ <p class="eyebrow">${o("nav.botDefaults")}</p>
317
+ <h1>${o("botDefaults.title")}</h1>
318
+ <p>${o("botDefaults.subtitle")}</p>
319
319
  </div>
320
320
  </div>
321
321
  <form id="bd-filters" class="filters">
322
- <input type="search" name="q" placeholder="${n("botDefaults.search")}" />
323
- <button type="button" id="bd-refresh">${n("botDefaults.refresh")}</button>
322
+ <input type="search" name="q" placeholder="${o("botDefaults.search")}" />
323
+ <button type="button" id="bd-refresh">${o("botDefaults.refresh")}</button>
324
324
  </form>
325
325
  <div id="bd-list"></div>
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)}">
326
+ </section>`}async function It(){try{let e=await fetch("/api/bots"),t=await e.json().catch(()=>({}));if(!e.ok){me=t?.error?`HTTP ${e.status}: ${t.error}${t.path?` (${t.path})`:""}`:`HTTP ${e.status}`,re={bots:[]};return}if(!t||!Array.isArray(t.bots)){me="unexpected response shape (no `bots` array)",re={bots:[]};return}me=null,re=t}catch(e){me=e?.message??String(e),re={bots:[]}}}function Tt(e){if(!e)return"\u2014";let t=new Date(e);return Number.isNaN(t.getTime())?"\u2014":t.toLocaleString()}async function Lt(e){e.innerHTML=Cn();let t=e.querySelector("#bd-list"),n=e.querySelector("#bd-filters"),s=e.querySelector("#bd-refresh");s.onclick=async()=>{s.disabled=!0;try{await It(),a()}finally{s.disabled=!1}},await It();function a(){let g=(new FormData(n).get("q")??"").toLowerCase(),l=re.bots.filter(I=>!g||(I.botName??"").toLowerCase().includes(g)||(I.larkAppId??"").toLowerCase().includes(g));if(me){t.innerHTML=`<p class="hint-warn">\u65E0\u6CD5\u52A0\u8F7D bot \u5217\u8868\uFF1A${d(me)}<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">${o("botDefaults.empty")}</p>`;return}t.innerHTML=l.map(r).join(""),$()}function r(p){if(p.error)return`<article class="bd-card" data-appid="${d(p.larkAppId)}">
327
+ <header><strong>${d(p.botName??p.larkAppId)}</strong>
328
+ <small>${d(p.larkAppId)}</small></header>
329
+ <p class="hint-warn-inline">\u67E5\u8BE2\u5931\u8D25\uFF1A${d(p.error)}</p>
330
+ </article>`;let g=p.defaultOncall??{enabled:!1,workingDir:"",since:0},l=!!g.enabled;return`<article class="bd-card" data-appid="${d(p.larkAppId)}">
331
331
  <header>
332
- <strong>${m(f.botName??f.larkAppId)}</strong>
333
- <small>${m(f.larkAppId)}</small>
332
+ <strong>${d(p.botName??p.larkAppId)}</strong>
333
+ <small>${d(p.larkAppId)}</small>
334
334
  </header>
335
335
  <div class="bd-body">
336
336
  <section class="bd-section">
337
- <h3 class="bd-section-title">${n("botDefaults.sectionOncall")}</h3>
337
+ <h3 class="bd-section-title">${o("botDefaults.sectionOncall")}</h3>
338
338
  <label class="checkbox-row">
339
339
  <input type="checkbox" data-action="toggle" ${l?"checked":""}>
340
- <strong>${n("botDefaults.defaultOncall")}</strong>
341
- <small>${n("botDefaults.defaultOncallHelp")}</small>
340
+ <strong>${o("botDefaults.defaultOncall")}</strong>
341
+ <small>${o("botDefaults.defaultOncallHelp")}</small>
342
342
  </label>
343
343
  <div class="bd-row">
344
344
  <label>
345
- <span>${n("botDefaults.workingDir")}</span>
345
+ <span>${o("botDefaults.workingDir")}</span>
346
346
  <input type="text" data-input="workingDir" placeholder="e.g. /root/iserver/botmux"
347
- value="${m(g.workingDir??"")}" ${l?"":"disabled"}>
347
+ value="${d(g.workingDir??"")}" ${l?"":"disabled"}>
348
348
  </label>
349
349
  </div>
350
- <p class="bd-section-note">${n("botDefaults.warning")}</p>
350
+ <p class="bd-section-note">${o("botDefaults.warning")}</p>
351
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>
352
+ <small>${o("botDefaults.lastEnabled")}: ${d(Tt(g.since??0))}</small>
353
+ <small>${o("botDefaults.autobound",{count:p.autoboundChatCount??0})}</small>
354
354
  </div>
355
355
  <div class="actions">
356
- <button type="button" data-action="save">${n("botDefaults.save")}</button>
356
+ <button type="button" data-action="save">${o("botDefaults.save")}</button>
357
357
  <span class="oncall-status" data-status></span>
358
358
  </div>
359
359
  </section>
360
- ${w(f)}
361
- ${I(f)}
360
+ ${m(p)}
361
+ ${k(p)}
362
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>
363
+ </article>`}function i(p){return p==null?o("botDefaults.brandStateDefault"):p.trim()===""?o("botDefaults.brandStateOff"):o("botDefaults.brandStateCustom")}function m(p){let g=p.brandLabel??null;return`<section class="bd-section">
364
+ <h3 class="bd-section-title">${o("botDefaults.sectionBrand")}</h3>
365
365
  <div class="bd-row bd-brand">
366
366
  <label>
367
- <span>${n("botDefaults.brandLabel")}</span>
367
+ <span>${o("botDefaults.brandLabel")}</span>
368
368
  <input type="text" data-input="brandLabel"
369
- placeholder="${m(n("botDefaults.brandLabelPlaceholder"))}"
370
- value="${m(g??"")}">
369
+ placeholder="${d(o("botDefaults.brandLabelPlaceholder"))}"
370
+ value="${d(g??"")}">
371
371
  </label>
372
- <small data-brand-state>${m(d(g))}</small>
373
- <small>${n("botDefaults.brandLabelHelp")}</small>
372
+ <small data-brand-state>${d(i(g))}</small>
373
+ <small>${o("botDefaults.brandLabelHelp")}</small>
374
374
  <div class="actions">
375
- <button type="button" data-action="save-brand">${n("botDefaults.brandSave")}</button>
376
- <button type="button" data-action="reset-brand">${n("botDefaults.brandReset")}</button>
375
+ <button type="button" data-action="save-brand">${o("botDefaults.brandSave")}</button>
376
+ <button type="button" data-action="reset-brand">${o("botDefaults.brandReset")}</button>
377
377
  <span class="oncall-status" data-brand-status></span>
378
378
  </div>
379
379
  </div>
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>
380
+ </section>`}function k(p){let g=p.disableStreamingCard===!0,l=p.writableTerminalLinkInCard===!0,I=p.privateCard===!0;return`<section class="bd-section">
381
+ <h3 class="bd-section-title">${o("botDefaults.sectionCard")}</h3>
382
382
  <label class="checkbox-row">
383
383
  <input type="checkbox" data-action="toggle-disable-streaming" ${g?"checked":""}>
384
- <strong>${n("botDefaults.disableStreaming")}</strong>
385
- <small>${n("botDefaults.disableStreamingHelp")}</small>
384
+ <strong>${o("botDefaults.disableStreaming")}</strong>
385
+ <small>${o("botDefaults.disableStreamingHelp")}</small>
386
386
  </label>
387
387
  <label class="checkbox-row">
388
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>
389
+ <strong>${o("botDefaults.writableLink")}</strong>
390
+ <small>${o("botDefaults.writableLinkHelp")}</small>
391
+ </label>
392
+ <label class="checkbox-row">
393
+ <input type="checkbox" data-action="toggle-private-card" ${I?"checked":""}>
394
+ <strong>${o("botDefaults.privateCard")}</strong>
395
+ <small>${o("botDefaults.privateCardHelp")}</small>
391
396
  </label>
392
397
  <div class="actions">
393
- <small data-card-pref-moot class="hint-warn-inline" ${g?"":"hidden"}>${n("botDefaults.writableLinkMoot")}</small>
398
+ <small data-card-pref-moot class="hint-warn-inline" ${g?"":"hidden"}>${o("botDefaults.writableLinkMoot")}</small>
394
399
  <span class="oncall-status" data-card-pref-status></span>
395
400
  </div>
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">
401
+ </section>`}function $(){t.querySelectorAll(".bd-card").forEach(p=>{let g=p.dataset.appid,l=p.querySelector("input[data-action=toggle]"),I=p.querySelector("input[data-input=workingDir]"),L=p.querySelector("button[data-action=save]"),b=p.querySelector("[data-status]");if(!l||!I||!L||!b)return;l.addEventListener("change",()=>{I.disabled=!l.checked,l.checked&&I.focus()}),L.addEventListener("click",async()=>{b.textContent="",b.className="oncall-status";let O=l.checked,H=I.value.trim();if(O&&!H){b.textContent=o("botDefaults.required"),b.classList.add("hint-warn-inline");return}L.disabled=!0;try{let q=await fetch(`/api/bots/${encodeURIComponent(g)}/default-oncall`,{method:"PUT",headers:{"content-type":"application/json"},body:JSON.stringify({enabled:O,workingDir:H})}),j=await q.json().catch(()=>({}));if(q.ok&&j.ok){let F=j.resolvedPath?` \u2192 ${j.resolvedPath}`:"";b.textContent=O?`\u2713 \u5DF2\u5F00\u542F${F}\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 z=re.bots.find(te=>te.larkAppId===g);z&&j.defaultOncall&&(z.defaultOncall=j.defaultOncall);let ee=p.querySelector(".bd-meta small:first-child");ee&&j.defaultOncall?.since!=null&&(ee.textContent=`\u4E0A\u6B21\u542F\u7528\u65F6\u95F4\uFF1A${Tt(j.defaultOncall.since)}`)}else b.textContent=`\u2717 ${j.error??q.status}`,b.classList.add("hint-warn-inline")}catch(q){b.textContent=`\u2717 ${q?.message??q}`,b.classList.add("hint-warn-inline")}finally{L.disabled=!1}});let f=p.querySelector("input[data-input=brandLabel]"),T=p.querySelector("button[data-action=save-brand]"),S=p.querySelector("button[data-action=reset-brand]"),y=p.querySelector("[data-brand-status]"),x=p.querySelector("[data-brand-state]");async function u(O,H){if(y){y.textContent="",y.className="oncall-status",H.disabled=!0;try{let q=await fetch(`/api/bots/${encodeURIComponent(g)}/brand-label`,{method:"PUT",headers:{"content-type":"application/json"},body:JSON.stringify({brandLabel:O})}),j=await q.json().catch(()=>({}));if(q.ok&&j.ok){let F=j.brandLabel??null;y.textContent="\u2713",y.classList.add("hint-ok"),f&&(f.value=F??""),x&&(x.textContent=i(F));let z=re.bots.find(ee=>ee.larkAppId===g);z&&(z.brandLabel=F)}else y.textContent=`\u2717 ${j.error??q.status}`,y.classList.add("hint-warn-inline")}catch(q){y.textContent=`\u2717 ${q?.message??q}`,y.classList.add("hint-warn-inline")}finally{H.disabled=!1}}}f&&T&&T.addEventListener("click",()=>u(f.value,T)),S&&S.addEventListener("click",()=>u(null,S));let w=p.querySelector("input[data-action=toggle-disable-streaming]"),h=p.querySelector("input[data-action=toggle-writable-link]"),E=p.querySelector("input[data-action=toggle-private-card]"),M=p.querySelector("[data-card-pref-status]"),A=p.querySelector("[data-card-pref-moot]");async function P(O,H){if(M){M.textContent="",M.className="oncall-status",H.disabled=!0;try{let q=await fetch(`/api/bots/${encodeURIComponent(g)}/card-prefs`,{method:"PUT",headers:{"content-type":"application/json"},body:JSON.stringify(O)}),j=await q.json().catch(()=>({}));if(q.ok&&j.ok){M.textContent=`\u2713 ${o("botDefaults.cardPrefSaved")}`,M.classList.add("hint-ok");let F=re.bots.find(z=>z.larkAppId===g);F&&(F.disableStreamingCard=j.disableStreamingCard,F.writableTerminalLinkInCard=j.writableTerminalLinkInCard,F.privateCard=j.privateCard)}else M.textContent=`\u2717 ${j.error??q.status}`,M.classList.add("hint-warn-inline")}catch(q){M.textContent=`\u2717 ${q?.message??q}`,M.classList.add("hint-warn-inline")}finally{H===h?H.disabled=!!w?.checked:H.disabled=!1}}}w&&w.addEventListener("change",()=>{let O=w.checked;h&&(h.disabled=O),A&&(A.hidden=!O),P({disableStreamingCard:O},w)}),h&&h.addEventListener("change",()=>{P({writableTerminalLinkInCard:h.checked},h)}),E&&E.addEventListener("change",()=>{P({privateCard:E.checked},E)})})}a(),n.addEventListener("input",a)}var Ke=4096,xe=[],Q=null,X=null,J="",we=new Set;function xn(){return`<section class="page roles-page">
397
402
  <div class="page-heading">
398
403
  <div>
399
- <p class="eyebrow">${n("nav.roles")}</p>
400
- <h1>${n("roles.title")}</h1>
401
- <p>${n("roles.subtitle")}</p>
404
+ <p class="eyebrow">${o("nav.roles")}</p>
405
+ <h1>${o("roles.title")}</h1>
406
+ <p>${o("roles.subtitle")}</p>
402
407
  </div>
403
408
  </div>
404
409
  <div class="roles-layout">
405
410
  <div class="roles-tree-panel">
406
411
  <div class="roles-tree-header">
407
- <input type="search" id="roles-search" placeholder="${n("roles.search")}" />
408
- <button type="button" id="roles-refresh">${n("roles.refresh")}</button>
412
+ <input type="search" id="roles-search" placeholder="${o("roles.search")}" />
413
+ <button type="button" id="roles-refresh">${o("roles.refresh")}</button>
409
414
  </div>
410
415
  <div id="roles-tree" class="roles-tree"></div>
411
416
  </div>
412
417
  <div class="roles-editor-panel">
413
- <div id="roles-editor-empty" class="roles-editor-empty">${n("roles.selectHint")}</div>
418
+ <div id="roles-editor-empty" class="roles-editor-empty">${o("roles.selectHint")}</div>
414
419
  <div id="roles-editor-form" class="roles-editor-form" style="display:none">
415
420
  <div class="roles-editor-breadcrumb">
416
421
  <span id="roles-editor-group-name"></span>
@@ -420,82 +425,205 @@ ${u.join(`
420
425
  <div class="roles-editor-meta">
421
426
  <span id="roles-editor-chat-id" class="roles-editor-meta-line"></span>
422
427
  </div>
423
- <textarea id="roles-editor-textarea" placeholder="${n("roles.editorPlaceholder")}" rows="14"></textarea>
428
+ <textarea id="roles-editor-textarea" placeholder="${o("roles.editorPlaceholder")}" rows="14"></textarea>
424
429
  <div class="roles-editor-footer">
425
430
  <span id="roles-editor-bytecount" class="roles-bytecount"></span>
426
431
  <div class="roles-editor-actions">
427
- <button type="button" id="roles-delete" class="danger">${n("roles.delete")}</button>
428
- <button type="button" id="roles-save" class="primary">${n("roles.save")}</button>
432
+ <button type="button" id="roles-delete" class="danger">${o("roles.delete")}</button>
433
+ <button type="button" id="roles-save" class="primary">${o("roles.save")}</button>
429
434
  </div>
430
435
  </div>
431
436
  <div id="roles-preview" class="roles-preview"></div>
432
437
  </div>
433
438
  </div>
434
439
  </div>
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)}">
440
+ </section>`}async function Ce(){xe=((await(await fetch("/api/groups")).json()).chats??[]).map(n=>({chatId:n.chatId,name:n.name??n.chatId,memberBots:(n.memberBots??[]).map(s=>({larkAppId:s.larkAppId,botName:s.botName??s.larkAppId,inChat:s.inChat??!1,hasRole:s.hasRole??!1,oncallChat:s.oncallChat??null}))}))}async function Mt(e,t){return(await fetch(`/api/roles/${encodeURIComponent(e)}/${encodeURIComponent(t)}`)).json()}async function Hn(e,t,n){return(await fetch(`/api/roles/${encodeURIComponent(e)}/${encodeURIComponent(t)}`,{method:"PUT",headers:{"content-type":"application/json"},body:JSON.stringify({content:n})})).ok}async function An(e,t){return(await fetch(`/api/roles/${encodeURIComponent(e)}/${encodeURIComponent(t)}`,{method:"DELETE"})).ok}function Ct(e){return e.memberBots.filter(t=>t.inChat&&t.hasRole).length}function Rn(e){return e.memberBots.filter(t=>t.inChat).length}function le(e=""){let t=document.getElementById("roles-tree");if(!t)return;let n=e.toLowerCase(),s=xe.filter(a=>{if(!n)return!0;let r=a.chatId.toLowerCase().includes(n)||(a.name??"").toLowerCase().includes(n),i=a.memberBots.some(m=>m.larkAppId.toLowerCase().includes(n)||(m.botName??"").toLowerCase().includes(n));return r||i});if(s.length===0){t.innerHTML=`<div class="roles-empty">${o("roles.noChats")}</div>`;return}t.innerHTML=s.map(a=>{let r=we.has(a.chatId),i=a.memberBots.filter(g=>g.inChat),m=r?"\u25BE":"\u25B8",k=Ct(a),$=Rn(a),p=r?i.map(g=>`
441
+ <div class="roles-bot-row ${Q===a.chatId&&X===g.larkAppId?"selected":""}"
442
+ data-group-id="${d(a.chatId)}"
443
+ data-bot-id="${d(g.larkAppId)}">
439
444
  <span class="roles-bot-indent"></span>
440
445
  <span class="roles-bot-icon">\u{1F916}</span>
441
446
  <div class="roles-bot-info">
442
- <div class="roles-bot-name">${m(g.botName)}</div>
443
- <div class="roles-bot-id">${m(g.larkAppId)}</div>
447
+ <div class="roles-bot-name">${d(g.botName)}</div>
448
+ <div class="roles-bot-id">${d(g.larkAppId)}</div>
444
449
  </div>
445
450
  <span class="roles-badge ${g.hasRole?"has-role":"no-role"}">
446
- ${g.hasRole?n("roles.configured"):n("roles.unconfigured")}
451
+ ${g.hasRole?o("roles.configured"):o("roles.unconfigured")}
447
452
  </span>
448
453
  </div>`).join(""):"";return`
449
454
  <div class="roles-group-section">
450
- <div class="roles-group-row ${s?"expanded":""} ${G===a.chatId&&!z?"selected":""}"
451
- data-group-id="${m(a.chatId)}">
452
- <span class="roles-group-arrow">${w}</span>
455
+ <div class="roles-group-row ${r?"expanded":""} ${Q===a.chatId&&!X?"selected":""}"
456
+ data-group-id="${d(a.chatId)}">
457
+ <span class="roles-group-arrow">${m}</span>
453
458
  <span class="roles-group-icon">\u{1F4C1}</span>
454
459
  <div class="roles-group-info">
455
- <div class="roles-group-name">${m(a.name??a.chatId)}</div>
460
+ <div class="roles-group-name">${d(a.name??a.chatId)}</div>
456
461
  <div class="roles-group-meta">
457
- ${I}/${L} ${n("roles.botsWithRoles")}
462
+ ${k}/${$} ${o("roles.botsWithRoles")}
458
463
  </div>
459
464
  </div>
460
465
  <span class="roles-group-chevron"></span>
461
466
  </div>
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`
467
+ <div class="roles-bot-list">${p}</div>
468
+ </div>`}).join(""),t.querySelectorAll(".roles-group-row").forEach(a=>{a.addEventListener("click",()=>{let r=a.dataset.groupId;r&&(we.has(r)?we.delete(r):we.add(r),le(document.getElementById("roles-search")?.value??""))})}),t.querySelectorAll(".roles-bot-row").forEach(a=>{a.addEventListener("click",r=>{r.stopPropagation();let i=a.dataset.groupId,m=a.dataset.botId;i&&m&&Dn(i,m)})})}async function Dn(e,t){Q=e,X=t;let n=await Mt(t,e),s=document.getElementById("roles-editor-empty"),a=document.getElementById("roles-editor-form"),r=document.getElementById("roles-editor-textarea"),i=document.getElementById("roles-editor-group-name"),m=document.getElementById("roles-editor-bot-name"),k=document.getElementById("roles-editor-chat-id");s&&(s.style.display="none"),a&&(a.style.display="");let $=xe.find(l=>l.chatId===e),p=$?.memberBots.find(l=>l.larkAppId===t);i&&(i.textContent=$?.name??e),m&&(m.textContent=p?.botName??t),k&&(k.textContent=`${e} \xB7 ${t}`),J=n.content??"",r&&(r.value=J,r.focus()),Ve(),Ye(),le(document.getElementById("roles-search")?.value??"");let g=document.getElementById("roles-delete");g&&(g.style.display=n.hasRole?"":"none")}function Ve(){let e=document.getElementById("roles-editor-bytecount");if(!e)return;let t=new TextEncoder().encode(J).length;e.textContent=`${t} / ${Ke} bytes`,e.className=`roles-bytecount ${t>3800?"warn":""} ${t>Ke?"over":""}`,Bn(t)}function Bn(e){let t=document.getElementById("roles-save");if(!t)return;let n=e??new TextEncoder().encode(J).length;t.disabled=n>Ke||J.trim().length===0}function Ye(){let e=document.getElementById("roles-preview");e&&(J.trim()?e.innerHTML=`<strong>${o("roles.preview")}</strong><pre>${d(J)}</pre>`:e.innerHTML=`<small>${o("roles.previewEmpty")}</small>`)}function Et(){Q=null,X=null,J="";let e=document.getElementById("roles-editor-empty"),t=document.getElementById("roles-editor-form"),n=document.getElementById("roles-editor-textarea"),s=document.getElementById("roles-delete");e&&(e.style.display=""),t&&(t.style.display="none"),n&&(n.value=""),s&&(s.style.display="none")}async function xt(e){e.innerHTML=xn(),we.clear(),Et(),await Ce();for(let t of xe)Ct(t)>0&&we.add(t.chatId);le(),document.getElementById("roles-search")?.addEventListener("input",t=>{le(t.target.value)}),document.getElementById("roles-refresh")?.addEventListener("click",async()=>{if(await Ce(),le(document.getElementById("roles-search")?.value??""),Q&&X){let t=await Mt(X,Q),n=document.getElementById("roles-editor-textarea");n&&(n.value=t.content??""),J=t.content??"",Ve(),Ye();let s=document.getElementById("roles-delete");s&&(s.style.display=t.hasRole?"":"none")}}),document.getElementById("roles-save")?.addEventListener("click",async function(){if(!(!Q||!X)){this.disabled=!0,this.textContent="...";try{if(await Hn(X,Q,J)){await Ce(),le(document.getElementById("roles-search")?.value??"");let n=document.getElementById("roles-delete");n&&(n.style.display="");let s=document.createElement("span");s.className="roles-saved-flash",s.textContent=` ${o("roles.saved")}`,document.querySelector(".roles-editor-footer")?.appendChild(s),setTimeout(()=>s.remove(),2e3)}else{let n=document.createElement("span");n.className="roles-saved-flash roles-save-error",n.textContent=J.trim().length===0?` ${o("roles.emptyError")}`:` ${o("roles.saveFailed")}`,document.querySelector(".roles-editor-footer")?.appendChild(n),setTimeout(()=>n.remove(),3e3)}}finally{this.disabled=!1,this.textContent=o("roles.save")}}}),document.getElementById("roles-delete")?.addEventListener("click",async function(){if(!(!Q||!X)&&confirm(o("roles.confirmDelete"))){this.disabled=!0,this.textContent="...";try{await An(X,Q)&&(await Ce(),Et(),le(document.getElementById("roles-search")?.value??""))}finally{this.disabled=!1,this.textContent=o("roles.delete")}}}),document.getElementById("roles-editor-textarea")?.addEventListener("input",t=>{J=t.target.value,Ve(),Ye()})}async function Ae(e){let t=await fetch(e);return{status:t.status,body:await t.json().catch(()=>({}))}}async function Re(e,t,n){let s=await fetch(t,{method:e,headers:{"content-type":"application/json"},body:n?JSON.stringify(n):void 0});return{status:s.status,body:await s.json().catch(()=>({}))}}var ce=(e,t)=>Re("POST",e,t),Ht=(e,t)=>Re("PUT",e,t),Qe=[],At=[],Rt="",He="",Xe=new Map,ye=new Set,ve=new Set;function B(e){return document.getElementById(e)}function De(){return[...Qe,...At]}function de(e){let t=Xe.get(e);return t||(t=new Set,Xe.set(e,t)),t}function On(e){return De().find(t=>t.key===e)}function Dt(e){let t=(n,s,a)=>`<a href="${n}" style="padding:6px 14px;border-radius:8px;text-decoration:none;font-size:14px;${a?"background:var(--accent,#3370ff);color:#fff":"color:var(--text,#1f2329)"}">${s}</a>`;return`<div style="display:flex;gap:8px;margin-bottom:14px">${t("#/team","\u6211\u7684\u56E2\u961F",e==="home")}${t("#/team/manage","\u56E2\u961F\u7BA1\u7406",e==="manage")}</div>`}function qn(){return`<section class="page">
469
+ <div class="page-heading"><div>
470
+ <p class="eyebrow">\u56E2\u961F</p><h1>\u56E2\u961F\u534F\u4F5C\uFF08\u8DE8\u90E8\u7F72\uFF09</h1>
471
+ <p>\u628A\u522B\u7684\u90E8\u7F72\uFF08\u540C\u4E8B\u81EA\u5DF1\u8DD1\u7684 botmux\uFF09\u9080\u8BF7\u8FDB\u540C\u4E00\u4E2A\u56E2\u961F\uFF0C\u4E92\u76F8\u53D1\u73B0\u673A\u5668\u4EBA\u3001\u534F\u4F5C\u62C9\u7FA4\u3002</p>
472
+ </div></div>
473
+ ${Dt("home")}
474
+ <div class="card" style="margin-bottom:16px">
475
+ <h2 style="margin-top:0">\u672C\u90E8\u7F72</h2>
476
+ <p>\u6211\u7684\u98DE\u4E66\u8EAB\u4EFD\uFF1A<b id="tf-owner">\u672A\u7ED1\u5B9A</b>
477
+ <button id="tf-autobind" class="primary" style="margin-left:8px">\u7ED1\u5B9A</button>
478
+ <span class="muted" style="font-size:13px">\uFF08\u7528\u673A\u5668\u4EBA\u51ED\u8BC1\u81EA\u52A8\u8BC6\u522B\u4F60\uFF1B\u7ED1\u5B9A\u540E\u62C9\u7FA4\u4F1A\u628A\u4F60\u62C9\u8FDB\u7FA4\u3001\u673A\u5668\u4EBA\u4E5F\u5F52\u5230\u4F60\u540D\u4E0B\uFF09</span></p>
479
+ <div id="tf-bind-out" style="display:none;margin-top:6px"></div>
480
+ </div>
481
+ <div class="card">
482
+ <h2 style="margin-top:0">\u6211\u7684\u56E2\u961F <span class="muted" id="tf-count" style="font-size:13px"></span></h2>
483
+ <div style="display:flex;gap:8px;align-items:center;flex-wrap:wrap;margin-bottom:8px;font-size:13px">
484
+ <input id="tf-search" placeholder="\u641C\u7D22 \u540D\u79F0/\u80FD\u529B/CLI\u2026" style="padding:5px 9px;min-width:180px">
485
+ <select id="tf-cli" style="padding:5px"><option value="">\u5168\u90E8 CLI</option></select>
486
+ <label><input type="checkbox" id="tf-fcap"> \u6709\u80FD\u529B\u6807\u7B7E</label>
487
+ <label><input type="checkbox" id="tf-frole"> \u6709\u56E2\u961F\u89D2\u8272</label>
488
+ </div>
489
+ <p class="muted" style="font-size:13px;margin:0 0 4px">\u6BCF\u4E2A\u56E2\u961F\u91CC\u52FE\u9009\u673A\u5668\u4EBA\u5373\u53EF\u5355\u72EC\u62C9\u7FA4\uFF08\u81EA\u52A8\u5E26\u4E0A\u5404\u81EA\u8D1F\u8D23\u4EBA\uFF09\u3002\u8981\u65B0\u5EFA\u56E2\u961F / \u751F\u6210\u9080\u8BF7\u7801 / \u52A0\u5165\u522B\u4EBA\u7684\u56E2\u961F\uFF0C\u53BB\u300C\u56E2\u961F\u7BA1\u7406\u300D\u3002</p>
490
+ <div id="tf-teams">\u52A0\u8F7D\u4E2D\u2026</div>
491
+ </div>
492
+ <div id="tf-modal" style="display:none;position:fixed;inset:0;background:rgba(0,0,0,.5);align-items:center;justify-content:center;z-index:50">
493
+ <div style="background:var(--card,#fff);color:var(--text,#1f2329);border-radius:10px;padding:18px 20px;width:min(560px,92vw)">
494
+ <h2 id="tf-modal-title" style="margin-top:0">\u56E2\u961F\u89D2\u8272</h2>
495
+ <p class="muted" style="font-size:13px">\u56E2\u961F\u7EA7\u89D2\u8272\uFF08\u8BE5\u673A\u5668\u4EBA\u8DE8\u7FA4\u7684\u9ED8\u8BA4\u4EBA\u8BBE\uFF09\u3002\u7559\u7A7A\u4FDD\u5B58\u5373\u5220\u9664\u3002\u4EC5\u672C\u90E8\u7F72\u7684\u673A\u5668\u4EBA\u53EF\u7F16\u8F91\u3002</p>
496
+ <textarea id="tf-modal-text" style="width:100%;min-height:200px;font:13px/1.5 ui-monospace,Menlo,monospace;padding:10px;box-sizing:border-box"></textarea>
497
+ <div style="display:flex;justify-content:flex-end;gap:8px;margin-top:12px">
498
+ <button id="tf-modal-cancel">\u53D6\u6D88</button><button id="tf-modal-save" class="primary">\u4FDD\u5B58</button>
499
+ </div>
500
+ </div>
501
+ </div>
502
+ </section>`}function Pn(e){let t=(B("tf-search").value||"").trim().toLowerCase();if(t&&!((e.name||"")+" "+(e.cliId||"")+" "+(e.capability||"")).toLowerCase().includes(t))return!1;let n=B("tf-cli").value;return!(n&&e.cliId!==n||B("tf-fcap").checked&&!e.capability||B("tf-frole").checked&&!e.hasTeamRole)}function Nn(e,t){let n=[...e.deployments].sort((a,r)=>a.local===r.local?0:a.local?-1:1),s="";for(let a of n){let r=t.filter(l=>l.deployment.id===a.id);if(!r.length)continue;let i=a.id===Rt,m=i?"\u672C\u90E8\u7F72":a.stale?"\u8FDC\u7AEF\xB7\u79BB\u7EBF\uFF1F":"\u8FDC\u7AEF",k=e.kind==="local"&&!i?` <button class="tf-rmmember ghost" data-team="${d(e.teamId)}" data-dep="${d(a.id)}" data-name="${d(a.name)}" style="font-size:12px">\u79FB\u9664</button>`:"",$=`${e.key}::${a.id}`,p=ve.has($),g=r.filter(l=>de(e.key).has(l.larkAppId)).length;if(s+=`<div class="tf-dep-h" data-dk="${d($)}" style="cursor:pointer;margin:10px 0 2px"><b>${p?"\u25BE":"\u25B8"} ${d(a.name)}</b> <span class="muted" style="font-size:12px">\uFF08${m}\uFF09\xB7 ${r.length} \u4E2A${g?`\uFF0C\u5DF2\u9009 ${g}`:""}</span>${k}</div>`,!!p){s+='<table style="width:100%;border-collapse:collapse;font-size:14px"><tbody>';for(let l of r){let I=d(l.larkAppId),L=de(e.key).has(l.larkAppId)?" checked":"",b=l.deployment.stale?"opacity:.55":"",f=i?`<input class="tf-cap" data-app="${I}" value="${d(l.capability||"")}" placeholder="\u80FD\u529B\u6807\u7B7E\u2026" style="width:92%;padding:3px 6px">`:l.capability?d(l.capability):'<span class="muted">\u2014</span>',T=i?`<button class="tf-role" data-app="${I}" data-name="${d(l.name)}">${l.hasTeamRole?"\u5DF2\u8BBE\xB7\u6539":"\u8BBE\u7F6E"}</button>`:l.hasTeamRole?"\u6709\u89D2\u8272":'<span class="muted">\u2014</span>';s+=`<tr style="${b}"><td style="padding:4px 8px"><input type="checkbox" class="tf-pick" data-tk="${d(e.key)}" data-app="${I}"${L}></td><td style="padding:4px 8px">${d(l.name)}</td><td style="padding:4px 8px" class="muted">${d(l.cliId)}</td><td style="padding:4px 8px">${f}</td><td style="padding:4px 8px">${T}</td></tr>`}s+="</tbody></table>"}}return s||(s='<p class="muted" style="margin:8px 0 0">\u6CA1\u6709\u7B26\u5408\u6761\u4EF6\u7684\u673A\u5668\u4EBA\u3002</p>'),s+=`<div style="margin-top:12px;display:flex;gap:8px;flex-wrap:wrap;align-items:center"><input class="tf-gname" data-tk="${d(e.key)}" placeholder="\u7FA4\u540D\uFF08\u5982\uFF1A\u8DE8\u56E2\u961F\u6392\u969C\uFF09" style="min-width:200px"><button class="tf-grp primary" data-tk="${d(e.key)}">\u628A\u52FE\u9009\u7684\u673A\u5668\u4EBA\u62C9\u4E00\u4E2A\u7FA4</button><span class="muted" style="font-size:13px">\u52FE\u9009\u673A\u5668\u4EBA \u2192 \u62C9\u5230\u4E00\u4E2A\u98DE\u4E66\u7FA4\uFF08\u81EA\u52A8\u542B owner\uFF09</span><span class="tf-gout" data-tk="${d(e.key)}" style="font-size:13px;display:block;flex-basis:100%"></span></div>`,s}function ue(){let e=B("tf-teams"),t=De();if(!t.length){e.innerHTML='<p class="muted">\u8FD8\u6CA1\u6709\u56E2\u961F\u3002\u53BB\u300C\u56E2\u961F\u7BA1\u7406\u300D\u751F\u6210\u9080\u8BF7\u7801\u8BA9\u522B\u4EBA\u52A0\u5165\u4F60\uFF0C\u6216\u52A0\u5165\u522B\u4EBA\u7684\u56E2\u961F\u3002</p>',B("tf-count").textContent="";return}let n="",s=new Set,a=new Set;for(let i of t){let m=i.bots.filter(Pn);m.forEach(g=>s.add(g.larkAppId)),i.bots.forEach(g=>a.add(g.larkAppId));let k=new Set(m.map(g=>g.larkAppId));[...de(i.key)].forEach(g=>{k.has(g)||de(i.key).delete(g)});let $=!ye.has(i.key),p=i.kind==="remote"?i.ok?' <span class="ok" style="font-size:12px">\u5DF2\u8FDE\u63A5</span>':` <span class="err" style="font-size:12px">\u8FDE\u63A5\u5931\u8D25\uFF1A${d(i.error||"")}</span>`:' <span class="muted" style="font-size:12px">\u6211\u6258\u7BA1</span>';n+=`<div class="card" style="margin:0 0 12px;padding:12px 14px;background:var(--bg-soft,#f6f7f9)"><div class="tf-team-h" data-tk="${d(i.key)}" style="cursor:pointer;display:flex;align-items:center;gap:8px;flex-wrap:wrap"><b style="font-size:15px">${$?"\u25B8":"\u25BE"} ${d(i.label)}</b>`+(i.sub?` <span class="muted" style="font-size:12px">${d(i.sub)}</span>`:"")+p+` <span class="muted" style="font-size:12px">\xB7 ${i.deployments.length} \u4E2A\u90E8\u7F72 \xB7 ${i.bots.length} \u4E2A\u673A\u5668\u4EBA</span></div>`,$||(n+=i.kind==="remote"&&!i.ok?'<p class="muted" style="margin:8px 0 0">\u65E0\u6CD5\u83B7\u53D6\u8BE5\u56E2\u961F\u82B1\u540D\u518C\u3002</p>':Nn(i,m)),n+="</div>"}e.innerHTML=n;let r=t.length>1?`\uFF08\u8DE8 ${t.length} \u4E2A\u56E2\u961F\uFF0C\u53BB\u91CD\uFF09`:"";B("tf-count").textContent=`\xB7 ${s.size===a.size?`${a.size}`:`${s.size} / ${a.size}`} \u4E2A\u673A\u5668\u4EBA${r}`,jn()}function jn(){let e=B("tf-teams");e.querySelectorAll(".tf-team-h").forEach(t=>{t.onclick=()=>{let n=t.dataset.tk;ye.has(n)?ye.delete(n):ye.add(n),ue()}}),e.querySelectorAll(".tf-dep-h").forEach(t=>{t.onclick=()=>{let n=t.dataset.dk;ve.has(n)?ve.delete(n):ve.add(n),ue()}}),e.querySelectorAll(".tf-pick").forEach(t=>{t.onchange=()=>{let n=de(t.dataset.tk);t.checked?n.add(t.dataset.app):n.delete(t.dataset.app)}}),e.querySelectorAll(".tf-cap").forEach(t=>{t.onchange=async()=>{let n=t.dataset.app,s=t.value;await Ht("/api/team/local-bots/"+encodeURIComponent(n)+"/capability",{capability:s}),De().forEach(a=>{let r=a.bots.find(i=>i.larkAppId===n);r&&(r.capability=s.trim()||null)})}}),e.querySelectorAll(".tf-role").forEach(t=>{t.onclick=()=>Fn(t.dataset.app,t.dataset.name||"")}),e.querySelectorAll(".tf-rmmember").forEach(t=>{t.onclick=async n=>{n.stopPropagation(),confirm(`\u628A\u300C${t.dataset.name}\u300D\u79FB\u51FA\u8FD9\u4E2A\u56E2\u961F\uFF1F\u5B83\u7684\u673A\u5668\u4EBA\u5C06\u4ECE\u672C\u56E2\u961F\u82B1\u540D\u518C\u6D88\u5931\uFF08\u4E0D\u5F71\u54CD\u5BF9\u65B9\u81EA\u5DF1\u7684\u90E8\u7F72\uFF09\u3002`)&&(await Re("DELETE",`/api/team/hosted/${encodeURIComponent(t.dataset.team)}/members/${encodeURIComponent(t.dataset.dep)}`),ge())}}),e.querySelectorAll(".tf-grp").forEach(t=>{t.onclick=async()=>{let n=t.dataset.tk,s=On(n);if(!s)return;let a=[...de(n)],r=e.querySelector(`.tf-gout[data-tk="${CSS.escape(n)}"]`);if(!a.length){r.innerHTML='<span class="err">\u8BF7\u5148\u52FE\u9009\u81F3\u5C11\u4E00\u4E2A\u673A\u5668\u4EBA</span>';return}let i=(e.querySelector(`.tf-gname[data-tk="${CSS.escape(n)}"]`)?.value||"").trim()||"\u534F\u4F5C\u7FA4";r.innerHTML='<span class="muted">\u5EFA\u7FA4\u4E2D\u2026</span>';let m=s.kind==="local"?await ce("/api/team/federated-group",{name:i,larkAppIds:a,teamId:s.teamId}):await ce("/api/team/remote-group",{hubUrl:s.hubUrl,teamId:s.teamId,name:i,larkAppIds:a});Un(r,m.body,m.status),m.body?.ok&&(de(n).clear(),s.kind==="local"&&ge())}})}function Un(e,t,n){if(t?.ok&&t.chatId){let s=t.shareLink||"https://applink.feishu.cn/client/chat/open?openChatId="+encodeURIComponent(t.chatId),a=(t.invalidBotIds||[]).length?`<span class="err"> \xB7 \u672A\u52A0\u5165\u7684\u673A\u5668\u4EBA\uFF1A${d((t.invalidBotIds||[]).join(", "))}</span>`:"",r=(t.invalidOwnerUnionIds||[]).length?`<span class="err"> \xB7 ${(t.invalidOwnerUnionIds||[]).length} \u4E2A owner \u672A\u80FD\u62C9\u8FDB</span>`:"",i=t.missingOperatorIdentity?'<span class="err"> \xB7 \u4F60\u672A\u7ED1\u5B9A\u98DE\u4E66\u8EAB\u4EFD\uFF0C\u6CA1\u628A\u4F60\u81EA\u5DF1\u62C9\u8FDB\u7FA4\uFF08\u53BB\u300C\u6211\u7684\u56E2\u961F\u300D\u7ED1\u5B9A\uFF09</span>':"",m=(t.skippedNoOwner||[]).length?`<span class="err"> \xB7 ${(t.skippedNoOwner||[]).length} \u4E2A\u673A\u5668\u4EBA\u56E0\u8D1F\u8D23\u4EBA\u672A\u7ED1\u5B9A\u8EAB\u4EFD\u88AB\u8DF3\u8FC7\uFF08\u672A\u52A0\u5165\u7FA4\uFF09</span>`:"",k=t.delegatedTo?`\uFF08\u7531\u300C${d(t.delegatedTo)}\u300D\u5EFA\u7FA4\uFF09`:"";e.innerHTML=`<span class="ok">\u7FA4\u5DF2\u521B\u5EFA</span>${k} \xB7 <a href="${d(s)}" target="_blank">\u5728\u98DE\u4E66\u6253\u5F00</a>${a}${r}${i}${m}`}else{let s=t?.error||n,a=s==="no_local_online_bot"?"\u8BF7\u81F3\u5C11\u52FE\u9009\u4E00\u4E2A\u4F60\u81EA\u5DF1\uFF08\u672C\u90E8\u7F72\uFF09\u7684\u5728\u7EBF\u673A\u5668\u4EBA\u2014\u2014\u7FA4\u8981\u7531\u5B83\u521B\u5EFA\u5E76\u628A\u4F60\uFF08\u53D1\u8D77\u4EBA\uFF09\u62C9\u8FDB\u7FA4\u3002":s==="all_bots_skipped_no_owner"?"\u6240\u9009\u673A\u5668\u4EBA\u7684\u8D1F\u8D23\u4EBA\u90FD\u6CA1\u7ED1\u5B9A\u8EAB\u4EFD\uFF0C\u65E0\u6CD5\u62C9\u7FA4\uFF08\u673A\u5668\u4EBA\u4E0D\u80FD\u8FDB\u4E00\u4E2A owner \u4E0D\u5728\u7684\u7FA4\uFF09\u3002\u8BF7\u8BA9\u5BF9\u5E94\u90E8\u7F72\u5148\u7ED1\u5B9A\u8EAB\u4EFD\u3002":s==="no_creator_available"?"\u6CA1\u6709\u53EF\u7528\u7684\u5EFA\u7FA4\u53D1\u8D77\u65B9\uFF08\u76F8\u5173\u90E8\u7F72\u90FD\u6CA1\u6709\u5728\u7EBF\u673A\u5668\u4EBA\uFF0C\u6216\u4E0D\u53EF\u8FBE\uFF09":s==="delegation_timeout"?"\u59D4\u6258\u5BF9\u65B9\u90E8\u7F72\u5EFA\u7FA4\u8D85\u65F6\uFF08\u53EF\u80FD\u5DF2\u5EFA\uFF0C\u53BB\u98DE\u4E66\u786E\u8BA4\uFF0C\u52FF\u91CD\u590D\u70B9\uFF09":`\u5EFA\u7FA4\u5931\u8D25\uFF1A${s}`;e.innerHTML=`<span class="err">${d(String(a))}</span>`}}async function Fn(e,t){let n=await Ae("/api/team/local-bots/"+encodeURIComponent(e)+"/role");B("tf-modal-title").textContent="\u56E2\u961F\u89D2\u8272 \xB7 "+t,B("tf-modal-text").value=n.body?.role||"",B("tf-modal").dataset.app=e,B("tf-modal").style.display="flex"}function Bt(){let e=Array.from(new Set(De().flatMap(s=>s.bots.map(a=>a.cliId)).filter(Boolean))).sort(),t=B("tf-cli"),n=t.value;t.innerHTML='<option value="">\u5168\u90E8 CLI</option>'+e.map(s=>`<option value="${d(s)}">${d(s)}</option>`).join(""),t.value=n}async function ge(){let t=(await Ae("/api/team/hosted")).body;if(!t?.ok){Qe=[],ue();return}Rt=t.deployment.deploymentId,He=t.suggestedHubUrl||"",B("tf-owner").textContent=t.deployment.ownerName||(t.deployment.ownerUnionId?"\u5DF2\u7ED1\u5B9A":"\u672A\u7ED1\u5B9A"),Qe=(t.teams||[]).map(n=>({kind:"local",key:`local:${n.teamId}`,teamId:n.teamId,label:n.isDefault?"\u6211\u6258\u7BA1\u7684\u56E2\u961F":n.name,sub:"",ok:!0,deployments:n.deployments||[],bots:n.bots||[]})),Bt(),ue()}async function _n(){At=((await Ae("/api/team/remote-roster")).body?.memberships||[]).map(n=>{let s=n.roster?.deployments||[],a=s.find(i=>i.local),r=a?.name?`${a.name} \u7684\u56E2\u961F`:n.teamName||n.teamId;return{kind:"remote",key:`${n.hubUrl}::${n.teamId}`,teamId:n.teamId,label:r,sub:n.hubUrl,ok:!!n.ok,error:n.error,hubUrl:n.hubUrl,deployments:s,bots:n.roster?.bots||[]}}),Bt(),ue()}function Ot(e){e.innerHTML=qn(),Xe.clear(),ye.clear(),ve.clear(),["tf-search","tf-cli","tf-fcap","tf-frole"].forEach(t=>{let n=B(t);n.oninput=ue,n.onchange=ue}),B("tf-modal-cancel").onclick=()=>{B("tf-modal").style.display="none"},B("tf-modal-save").onclick=async()=>{let t=B("tf-modal").dataset.app;await Ht("/api/team/local-bots/"+encodeURIComponent(t)+"/role",{role:B("tf-modal-text").value}),B("tf-modal").style.display="none",ge()},zn(),ge(),_n()}function Wn(){return`<section class="page">
503
+ <div class="page-heading"><div>
504
+ <p class="eyebrow">\u56E2\u961F</p><h1>\u56E2\u961F\u7BA1\u7406</h1>
505
+ <p>\u521B\u5EFA\u591A\u4E2A\u56E2\u961F\u3001\u7ED9\u6BCF\u4E2A\u56E2\u961F\u751F\u6210\u9080\u8BF7\u7801\u3001\u6216\u52A0\u5165\u522B\u4EBA\u7684\u56E2\u961F\u3002\u4E00\u4E2A\u56E2\u961F = \u4F60\u672C\u90E8\u7F72\u7684\u673A\u5668\u4EBA + \u52A0\u5165\u8BE5\u56E2\u961F\u7684\u5176\u5B83\u90E8\u7F72\u3002</p>
506
+ </div></div>
507
+ ${Dt("manage")}
508
+ <div class="card" style="margin-bottom:16px">
509
+ <h2 style="margin-top:0">\u6211\u6258\u7BA1\u7684\u56E2\u961F</h2>
510
+ <p style="display:flex;gap:8px;flex-wrap:wrap;align-items:center;margin-bottom:6px">
511
+ <input id="tm-newname" placeholder="\u65B0\u56E2\u961F\u540D\u79F0" style="min-width:200px">
512
+ <button id="tm-create" class="primary">\u521B\u5EFA\u56E2\u961F</button>
513
+ <span class="muted tm-cout" style="font-size:13px"></span>
514
+ </p>
515
+ <div id="tm-list">\u52A0\u8F7D\u4E2D\u2026</div>
516
+ </div>
517
+ <div class="card">
518
+ <h2 style="margin-top:0">\u52A0\u5165\u522B\u4EBA\u7684\u56E2\u961F</h2>
519
+ <p style="display:flex;gap:8px;flex-wrap:wrap;align-items:center">
520
+ <input id="tm-hub" placeholder="Hub \u5730\u5740\uFF0C\u5982 http://10.0.0.5:7891" style="flex:1;min-width:240px">
521
+ <input id="tm-code" placeholder="\u9080\u8BF7\u7801" style="min-width:160px">
522
+ <button id="tm-join" class="primary">\u52A0\u5165</button>
523
+ </p>
524
+ <div id="tm-join-out" style="display:none;margin-top:6px"></div>
525
+ </div>
526
+ </section>`}async function Ze(){let t=(await Ae("/api/team/hosted")).body,n=B("tm-list");He=t?.suggestedHubUrl||He;let s=t?.teams||[];if(!s.length){n.innerHTML='<p class="muted">\u8FD8\u6CA1\u6709\u56E2\u961F\u3002</p>';return}n.innerHTML=s.map(a=>{let r=(a.deployments||[]).filter(i=>!i.local).length;return`<div class="card" style="margin:0 0 8px;padding:10px 14px;background:var(--bg-soft,#f6f7f9)">
527
+ <div style="display:flex;align-items:center;gap:8px;flex-wrap:wrap">
528
+ <b>${d(a.name)}</b>${a.isDefault?' <span class="muted" style="font-size:12px">\u9ED8\u8BA4</span>':""}
529
+ <span class="muted" style="font-size:12px">\xB7 ${(a.deployments||[]).length} \u4E2A\u90E8\u7F72${r?`\uFF08\u542B ${r} \u8FDC\u7AEF\uFF09`:""} \xB7 ${(a.bots||[]).length} \u4E2A\u673A\u5668\u4EBA</span>
530
+ <span style="margin-left:auto;display:flex;gap:6px">
531
+ <button class="tm-invite ghost" data-team="${d(a.teamId)}" style="font-size:12px">\u751F\u6210\u9080\u8BF7\u7801</button>
532
+ ${a.isDefault?"":`<button class="tm-del ghost" data-team="${d(a.teamId)}" data-name="${d(a.name)}" style="font-size:12px">\u5220\u9664</button>`}
533
+ </span>
534
+ </div>
535
+ <div class="tm-inv-out" data-team="${d(a.teamId)}" style="display:none;margin-top:6px;font-size:13px"></div></div>`}).join(""),n.querySelectorAll(".tm-invite").forEach(a=>{a.onclick=async()=>{let r=a.dataset.team,i=n.querySelector(`.tm-inv-out[data-team="${CSS.escape(r)}"]`);i.style.display="",i.innerHTML='<span class="muted">\u751F\u6210\u4E2D\u2026</span>';let m=await ce("/api/team/local-invite",{teamId:r});m.body?.code?i.innerHTML=`\u628A\u4E0B\u9762\u4E24\u9879\u53D1\u7ED9<b>\u522B\u7684\u90E8\u7F72</b>\u7684\u4EBA\uFF0824 \u5C0F\u65F6\u5185\u3001\u5355\u6B21\u6709\u6548\uFF09\uFF1A<br>Hub \u5730\u5740\uFF1A<code>${d(He)}</code><br>\u9080\u8BF7\u7801\uFF1A<code style="font-size:15px">${d(m.body.code)}</code>`:i.innerHTML='<span class="err">\u751F\u6210\u5931\u8D25\u3002</span>'}}),n.querySelectorAll(".tm-del").forEach(a=>{a.onclick=async()=>{confirm(`\u5220\u9664\u56E2\u961F\u300C${a.dataset.name}\u300D\uFF1F\u5DF2\u52A0\u5165\u5B83\u7684\u90E8\u7F72\u4F1A\u88AB\u79FB\u9664\uFF08\u4E0D\u5F71\u54CD\u4ED6\u4EEC\u81EA\u5DF1\u7684\u90E8\u7F72\uFF09\u3002`)&&(await Re("DELETE","/api/team/hosted/"+encodeURIComponent(a.dataset.team)),Ze())}})}function qt(e){e.innerHTML=Wn(),B("tm-create").onclick=async()=>{let t=B("tm-newname").value.trim(),n=e.querySelector(".tm-cout");if(!t){n.innerHTML='<span class="err">\u8BF7\u586B\u56E2\u961F\u540D\u79F0</span>';return}n.innerHTML='<span class="muted">\u521B\u5EFA\u4E2D\u2026</span>';let s=await ce("/api/team/hosted",{name:t});s.body?.ok?(n.innerHTML='<span class="ok">\u5DF2\u521B\u5EFA</span>',B("tm-newname").value="",Ze()):n.innerHTML=`<span class="err">\u521B\u5EFA\u5931\u8D25\uFF1A${d(String(s.body?.error||s.status))}</span>`},B("tm-join").onclick=async()=>{let t=B("tm-hub").value.trim(),n=B("tm-code").value.trim(),s=B("tm-join-out");if(s.style.display="",!t||!n){s.innerHTML='<span class="err">\u8BF7\u586B Hub \u5730\u5740\u548C\u9080\u8BF7\u7801\u3002</span>';return}s.innerHTML='<span class="muted">\u52A0\u5165\u4E2D\u2026</span>';let a=await ce("/api/team/join-remote",{hubUrl:t,inviteCode:n});if(a.body?.ok)s.innerHTML=`<span class="ok">\u5DF2\u52A0\u5165\u300C${d(a.body.teamName||"")}\u300D\uFF0C\u53BB\u300C\u6211\u7684\u56E2\u961F\u300D\u67E5\u770B\u3002</span>`,B("tm-code").value="";else{let r=a.body?.error||a.status,i=r==="cannot_join_self"?"\u8FD9\u662F\u4F60\u81EA\u5DF1\u7684\u90E8\u7F72\uFF0C\u4E0D\u80FD\u52A0\u5165\u81EA\u5DF1\uFF08\u9080\u8BF7\u7801\u8981\u53D1\u7ED9\u522B\u7684\u90E8\u7F72\u7684\u4EBA\u7528\uFF09":r==="deployment_already_joined"?"\u4F60\u7684\u90E8\u7F72\u5DF2\u7ECF\u52A0\u5165\u8FC7\u8FD9\u4E2A\u56E2\u961F\u4E86":r==="hub_unreachable"?"\u8FDE\u4E0D\u4E0A\u5BF9\u65B9 Hub\uFF08\u68C0\u67E5\u5730\u5740/\u7F51\u7EDC\uFF09":r==="hub_timeout"?"\u5BF9\u65B9 Hub \u54CD\u5E94\u8D85\u65F6":`\u52A0\u5165\u5931\u8D25\uFF1A${r}`;s.innerHTML=`<span class="err">${d(String(i))}</span>`}},Ze()}function zn(){B("tf-autobind").onclick=async()=>{let e=B("tf-bind-out");e.style.display="",e.innerHTML='<span class="muted">\u8BC6\u522B\u4E2D\u2026</span>';let n=(await ce("/api/team/identity/auto-bind")).body;if(n?.ok&&n.owner){e.innerHTML=`<span class="ok">\u5DF2\u7ED1\u5B9A\uFF1A${d(n.owner.name||n.owner.unionId)}</span>`,ge();return}if(n?.ok&&n.needChoice&&Array.isArray(n.candidates)){let s=n.candidates.map(a=>`<button class="tf-pickowner ghost" data-union="${d(a.unionId)}" style="margin:2px">${d(a.name||a.unionId)}</button>`).join(" ");e.innerHTML=`\u8BC6\u522B\u5230\u591A\u4E2A\u5019\u9009\uFF0C\u70B9\u4F60\u81EA\u5DF1\uFF1A<br>${s}`,e.querySelectorAll(".tf-pickowner").forEach(a=>{a.onclick=async()=>{e.innerHTML='<span class="muted">\u7ED1\u5B9A\u4E2D\u2026</span>';let i=(await ce("/api/team/identity/auto-bind",{unionId:a.dataset.union})).body;i?.ok&&i.owner?(e.innerHTML=`<span class="ok">\u5DF2\u7ED1\u5B9A\uFF1A${d(i.owner.name||i.owner.unionId)}</span>`,ge()):e.innerHTML=`<span class="err">\u7ED1\u5B9A\u5931\u8D25\uFF1A${d(String(i?.error||"unknown"))}</span>`}});return}if(n?.error==="no_candidates"){e.innerHTML='<span class="err">\u6CA1\u8BC6\u522B\u5230\u8EAB\u4EFD\uFF1A\u8BF7\u786E\u8BA4\u673A\u5668\u4EBA\u914D\u7F6E\u4E86 allowedUsers\uFF08\u5141\u8BB8\u4F7F\u7528\u8005\uFF09\uFF0C\u4E14\u673A\u5668\u4EBA\u6709\u901A\u8BAF\u5F55\u6743\u9650\u3002</span>';return}e.innerHTML=`<span class="err">\u7ED1\u5B9A\u5931\u8D25\uFF1A${d(String(n?.error||"unknown"))}</span>`}}async function Pt(e){let t=await fetch(e);return{status:t.status,body:await t.json().catch(()=>({}))}}async function tt(e,t,n){let s=await fetch(t,{method:e,headers:{"content-type":"application/json"},body:n?JSON.stringify(n):void 0});return{status:s.status,body:await s.json().catch(()=>({}))}}function _(e){return document.getElementById(e)}function K(e){return(_(e).value||"").trim()}var nt=[];function Jn(){return`<section class="page">
536
+ <div class="page-heading">
537
+ <div>
538
+ <p class="eyebrow">\u63A5\u5165\u70B9 \xB7 beta</p>
539
+ <h1>\u63A5\u5165\u70B9\uFF08Webhook\uFF09<span class="muted" style="font-size:14px;font-weight:400">beta</span></h1>
540
+ <p>\u8BA9\u5916\u90E8\u7CFB\u7EDF\uFF08\u76D1\u63A7\u544A\u8B66\u3001CI\u3001\u5DE5\u5355\u2026\uFF09\u901A\u8FC7\u4E00\u4E2A webhook \u89E6\u53D1\u673A\u5668\u4EBA\u5728\u7FA4\u91CC\u8BF4\u8BDD\u6216\u8DD1\u5DE5\u4F5C\u6D41\u3002<span class="muted">\uFF08beta\uFF1A\u5C1A\u672A\u5145\u5206\u6D4B\u8BD5\uFF0C\u6B22\u8FCE\u53CD\u9988\uFF09</span></p>
541
+ </div>
542
+ </div>
543
+
544
+ <div class="card" style="margin-bottom:16px">
545
+ <h2 style="margin-top:0">\u65B0\u5EFA\u63A5\u5165\u70B9</h2>
546
+ <div class="cn-form" style="display:grid;grid-template-columns:140px 1fr;gap:10px 14px;align-items:center;max-width:680px">
547
+ <label>\u540D\u79F0</label><input id="cn-name" placeholder="\u5982\uFF1A\u7EBF\u4E0A\u544A\u8B66">
548
+ <label>\u89E6\u53D1\u7684\u673A\u5668\u4EBA</label><select id="cn-bot"></select>
549
+ <label>\u89E6\u53D1\u65B9\u5F0F</label>
550
+ <select id="cn-kind"><option value="turn">\u5355\u8F6E\u5BF9\u8BDD\uFF08\u8BA9\u673A\u5668\u4EBA\u56DE\u5E94\u4E00\u6B21\uFF09</option><option value="workflow">\u5DE5\u4F5C\u6D41</option></select>
551
+ <label class="cn-wf" style="display:none">\u5DE5\u4F5C\u6D41 ID</label><input class="cn-wf" id="cn-wf" style="display:none" placeholder="workflowId">
552
+ <label>\u6295\u9012\u5230\u54EA\u4E2A\u7FA4</label>
553
+ <select id="cn-mode">
554
+ <option value="dynamic">\u7531\u8BF7\u6C42\u6307\u5B9A\uFF08\u7FA4 ID \u968F\u8BF7\u6C42\u4F20\u5165\uFF09</option>
555
+ <option value="fixed">\u56FA\u5B9A\u7FA4</option>
556
+ <option value="new-group">\u6BCF\u6B21\u65B0\u5EFA\u7FA4</option>
557
+ </select>
558
+ <label class="cn-fixed" style="display:none">\u7FA4 ID</label><input class="cn-fixed" id="cn-chat" style="display:none" placeholder="oc_\u2026">
559
+ <label class="cn-allow">\u5141\u8BB8\u7684\u7FA4<span class="muted" style="font-weight:400">\uFF08\u53EF\u9009\uFF09</span></label>
560
+ <input class="cn-allow" id="cn-allow" placeholder="oc_xxx,oc_yyy\uFF08\u9017\u53F7\u5206\u9694\uFF0C\u7559\u7A7A=\u4E0D\u9650\uFF09">
561
+ <label class="cn-life" style="display:none">\u53BB\u91CD\u5B57\u6BB5</label><input class="cn-life" id="cn-dedup" style="display:none" placeholder="\u5982 payload.alert.id">
562
+ <label class="cn-life" style="display:none">\u72B6\u6001\u5B57\u6BB5</label><input class="cn-life" id="cn-status" style="display:none" placeholder="\u5982 payload.status">
563
+ <label>\u7B7E\u540D\u5BC6\u94A5</label><input id="cn-secret" placeholder="\u7559\u7A7A\u81EA\u52A8\u751F\u6210\uFF08\u53EA\u663E\u793A\u4E00\u6B21\uFF09">
564
+ </div>
565
+ <div style="margin-top:14px"><button id="cn-create" class="primary">\u521B\u5EFA</button>
566
+ <span class="muted" id="cn-create-out" style="margin-left:10px;font-size:13px"></span></div>
567
+ <div id="cn-created" style="display:none;margin-top:12px"></div>
568
+ </div>
569
+
570
+ <div class="card">
571
+ <h2 style="margin-top:0">\u5DF2\u6709\u63A5\u5165\u70B9 <span class="muted" id="cn-count" style="font-size:13px"></span></h2>
572
+ <div id="cn-list">\u52A0\u8F7D\u4E2D\u2026</div>
573
+ </div>
574
+ </section>`}function et(){let e=_("cn-kind").value,t=_("cn-mode").value;document.querySelectorAll(".cn-wf").forEach(n=>{n.style.display=e==="workflow"?"":"none"}),document.querySelectorAll(".cn-fixed").forEach(n=>{n.style.display=t==="fixed"?"":"none"}),document.querySelectorAll(".cn-allow").forEach(n=>{n.style.display=t==="fixed"?"none":""}),document.querySelectorAll(".cn-life").forEach(n=>{n.style.display=t==="new-group"?"":"none"})}function Nt(e){return`${location.origin}/webhook/${encodeURIComponent(e)}`}function Gn(e){return e==="fixed"?"\u56FA\u5B9A\u7FA4":e==="new-group"?"\u6BCF\u6B21\u65B0\u5EFA\u7FA4":"\u8BF7\u6C42\u6307\u5B9A\u7FA4"}function Kn(e){return e==="workflow"?"\u5DE5\u4F5C\u6D41":"\u5355\u8F6E"}function Vn(e){let t=_("cn-list");if(_("cn-count").textContent=e.length?`\xB7 ${e.length} \u4E2A`:"",!e.length){t.innerHTML='<p class="muted">\u8FD8\u6CA1\u6709\u63A5\u5165\u70B9\u3002\u7528\u4E0A\u9762\u7684\u8868\u5355\u521B\u5EFA\u4E00\u4E2A\u3002</p>';return}t.innerHTML=e.map(n=>{let s=nt.find(r=>r.larkAppId===n.target.botId),a=Nt(n.id);return`<div class="card" style="margin:0 0 10px;padding:12px 14px;background:var(--bg-soft,#f6f7f9)">
575
+ <div style="display:flex;align-items:center;gap:8px;flex-wrap:wrap">
576
+ <b style="font-size:15px">${d(n.name)}</b>
577
+ <span class="${n.enabled?"ok":"muted"}" style="font-size:12px">${n.enabled?"\u5DF2\u542F\u7528":"\u5DF2\u505C\u7528"}</span>
578
+ <span class="muted" style="font-size:12px">\xB7 ${d(s?.botName||n.target.botId)} \xB7 ${Kn(n.target.kind)} \xB7 ${Gn(n.target.mode)}</span>
579
+ <span style="margin-left:auto;display:flex;gap:6px">
580
+ <button class="cn-toggle ghost" data-id="${d(n.id)}" data-on="${n.enabled}" style="font-size:12px">${n.enabled?"\u505C\u7528":"\u542F\u7528"}</button>
581
+ <button class="cn-del ghost" data-id="${d(n.id)}" style="font-size:12px">\u5220\u9664</button>
582
+ </span>
583
+ </div>
584
+ <div style="margin-top:6px;font-size:13px;display:flex;align-items:center;gap:8px;flex-wrap:wrap">
585
+ <span class="muted">Webhook URL\uFF1A</span><code style="font-size:12px;word-break:break-all">${d(a)}</code>
586
+ <button class="cn-copy ghost" data-url="${d(a)}" style="font-size:12px">\u590D\u5236</button>
587
+ </div></div>`}).join(""),t.querySelectorAll(".cn-copy").forEach(n=>{n.onclick=()=>{navigator.clipboard?.writeText(n.dataset.url),n.textContent="\u5DF2\u590D\u5236",setTimeout(()=>n.textContent="\u590D\u5236",1200)}}),t.querySelectorAll(".cn-toggle").forEach(n=>{n.onclick=async()=>{await tt("PATCH","/api/connectors/"+encodeURIComponent(n.dataset.id),{enabled:n.dataset.on!=="true"}),Be()}}),t.querySelectorAll(".cn-del").forEach(n=>{n.onclick=async()=>{confirm("\u5220\u9664\u8FD9\u4E2A\u63A5\u5165\u70B9\uFF1F\u5B83\u7684 webhook URL \u4F1A\u7ACB\u5373\u5931\u6548\u3002")&&(await tt("DELETE","/api/connectors/"+encodeURIComponent(n.dataset.id)),Be())}})}async function Be(){let[e,t]=await Promise.all([Pt("/api/bots"),Pt("/api/connectors")]);nt=(e.body?.bots||[]).map(a=>({larkAppId:a.larkAppId,botName:a.botName||a.larkAppId}));let n=_("cn-bot"),s=n.value;n.innerHTML=nt.map(a=>`<option value="${d(a.larkAppId)}">${d(a.botName)}</option>`).join("")||'<option value="">\uFF08\u6CA1\u6709\u5728\u7EBF\u673A\u5668\u4EBA\uFF09</option>',s&&(n.value=s),Vn(t.body?.connectors||[])}function jt(e){e.innerHTML=Jn(),_("cn-kind").onchange=et,_("cn-mode").onchange=et,et(),_("cn-create").onclick=async()=>{let t=_("cn-create-out"),n=K("cn-name"),s=_("cn-bot").value;if(!n){t.innerHTML='<span class="err">\u8BF7\u586B\u540D\u79F0</span>';return}if(!s){t.innerHTML='<span class="err">\u8BF7\u9009\u673A\u5668\u4EBA</span>';return}let a=_("cn-kind").value,r=_("cn-mode").value,i={name:n,enabled:!0,target:{kind:a,mode:r,botId:s},promptEnvelope:{sourceName:n}};if(a==="workflow"){if(!K("cn-wf")){t.innerHTML='<span class="err">\u8BF7\u586B\u5DE5\u4F5C\u6D41 ID</span>';return}i.target.workflowId=K("cn-wf")}if(r==="fixed"){if(!K("cn-chat")){t.innerHTML='<span class="err">\u56FA\u5B9A\u7FA4\u9700\u8981\u586B\u7FA4 ID</span>';return}i.target.chatId=K("cn-chat")}else{let $=K("cn-allow");$&&(i.target.allowChats=$.split(",").map(p=>p.trim()).filter(Boolean))}if(r==="new-group"){if(!K("cn-dedup")||!K("cn-status")){t.innerHTML='<span class="err">\u300C\u6BCF\u6B21\u65B0\u5EFA\u7FA4\u300D\u9700\u8981\u586B\u53BB\u91CD\u5B57\u6BB5\u548C\u72B6\u6001\u5B57\u6BB5</span>';return}i.lifecycleExtractors={dedupKey:K("cn-dedup"),status:K("cn-status")}}let m=K("cn-secret");m&&(i.secret=m),t.innerHTML='<span class="muted">\u521B\u5EFA\u4E2D\u2026</span>';let k=await tt("POST","/api/connectors",i);if(k.status===201&&k.body?.ok){t.innerHTML="";let $=_("cn-created");$.style.display="";let p=k.body.webhookUrl||Nt(k.body.connector.id),g=k.body.secret;$.innerHTML=`<div class="card" style="padding:12px 14px;background:var(--bg-soft,#f6f7f9)">
588
+ <p class="ok" style="margin:0 0 6px">\u5DF2\u521B\u5EFA\u300C${d(n)}\u300D</p>
589
+ <p style="margin:4px 0;font-size:13px"><span class="muted">Webhook URL\uFF1A</span><code style="word-break:break-all">${d(p)}</code></p>
590
+ ${g?`<p style="margin:4px 0;font-size:13px"><span class="muted">\u7B7E\u540D\u5BC6\u94A5\uFF08\u53EA\u663E\u793A\u8FD9\u4E00\u6B21\uFF0C\u8BF7\u4FDD\u5B58\uFF09\uFF1A</span><code>${d(g)}</code></p>`:""}
591
+ <p class="muted" style="font-size:12px;margin:6px 0 0">\u5916\u90E8\u7CFB\u7EDF\u7528\u6B64 URL + \u5BC6\u94A5\uFF08HMAC-SHA256 \u7B7E\u540D\uFF09\u8C03\u7528\u5373\u53EF\u89E6\u53D1\u3002</p></div>`,["cn-name","cn-wf","cn-chat","cn-allow","cn-dedup","cn-status","cn-secret"].forEach(l=>{_(l).value=""}),Be()}else{let $=k.body?.error||k.status;t.innerHTML=`<span class="err">\u521B\u5EFA\u5931\u8D25\uFF1A${d(String($))}</span>`}},Be()}function Yn(){let e=[["",o("workflow.filter.nonTerminal")],["all",o("workflow.filter.all")],["pending",ie("pending")],["running",ie("running")],["waiting",ie("waiting")],["succeeded",ie("succeeded")],["failed",ie("failed")],["cancelled",ie("cancelled")]];return`
464
592
  <nav class="wf-subnav">
465
- <a href="#/workflows" class="active" data-i18n="workflow.subnav.runs">${i(n("workflow.subnav.runs"))}</a>
466
- <a href="#/workflows/catalog" data-i18n="workflow.subnav.catalog">${i(n("workflow.subnav.catalog"))}</a>
593
+ <a href="#/workflows" class="active" data-i18n="workflow.subnav.runs">${c(o("workflow.subnav.runs"))}</a>
594
+ <a href="#/workflows/catalog" data-i18n="workflow.subnav.catalog">${c(o("workflow.subnav.catalog"))}</a>
467
595
  </nav>
468
596
  <form id="wf-filters" class="filters">
469
- <input type="search" name="q" placeholder="${i(n("workflow.searchPlaceholder"))}" />
597
+ <input type="search" name="q" placeholder="${c(o("workflow.searchPlaceholder"))}" />
470
598
  <select name="status">
471
- ${e.map(([t,o])=>`<option value="${i(t)}">${i(o)}</option>`).join("")}
599
+ ${e.map(([t,n])=>`<option value="${c(t)}">${c(n)}</option>`).join("")}
472
600
  </select>
473
601
  <span id="wf-last-load" class="muted"></span>
474
602
  </form>
475
603
  <table>
476
604
  <thead><tr>
477
- <th>${i(n("workflow.table.run"))}</th><th>${i(n("workflow.table.workflow"))}</th><th>${i(n("workflow.table.status"))}</th>
478
- <th>${i(n("workflow.table.lastSeq"))}</th><th>${i(n("workflow.table.dangling"))}</th><th>${i(n("workflow.table.updated"))}</th>
479
- <th>${i(n("workflow.table.chatApp"))}</th>
605
+ <th>${c(o("workflow.table.run"))}</th><th>${c(o("workflow.table.workflow"))}</th><th>${c(o("workflow.table.status"))}</th>
606
+ <th>${c(o("workflow.table.lastSeq"))}</th><th>${c(o("workflow.table.dangling"))}</th><th>${c(o("workflow.table.updated"))}</th>
607
+ <th>${c(o("workflow.table.chatApp"))}</th>
480
608
  </tr></thead>
481
609
  <tbody id="wf-tbody"></tbody>
482
610
  </table>
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)}">
484
- <td><a href="#/workflows/${encodeURIComponent(u.runId)}"><code>${i(u.runId)}</code></a></td>
485
- <td>${i(u.workflowId)}</td>
486
- <td>${Y(u.status)}${u.failedNodeId?` <span class="muted">(${i(u.failedNodeId)})</span>`:""}${c}</td>
487
- <td>${u.lastSeq}</td>
488
- <td class="${k}">${$}</td>
489
- <td title="${i(new Date(u.updatedAt).toISOString())}">${dn(u.updatedAt)}</td>
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=`
611
+ `}var Qn=5e3,Xn=2e3,ke=new Set(["succeeded","failed","cancelled"]);function c(e){return e.replace(/[&<>"']/g,t=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"})[t])}function Zn(e){let t=new Date(e),s=Date.now()-e;return s<6e4?o("time.secondsAgo",{value:Math.max(1,Math.floor(s/1e3))}):s<36e5?o("time.minutesAgo",{value:Math.floor(s/6e4)}):s<864e5?o("time.hoursAgo",{value:Math.floor(s/36e5)}):t.toISOString().slice(0,19).replace("T"," ")}function ne(e){return`<span class="${ke.has(e)?"wf-status terminal":"wf-status live"} wf-status-${c(e)}">${c(ie(e))}</span>`}function ie(e){let t=`workflow.status.${e}`,n=o(t);return n===t?e:n}function Wt(e){let t=location.hash.match(/^#\/workflows\/([^?#]+)(?:\?([^#]*))?$/);if(t){let n=new URLSearchParams(t[2]??"");return to(e,decodeURIComponent(t[1]),{focusAttemptId:n.get("attempt")??void 0})}return eo(e)}function eo(e){e.innerHTML=Yn();let t=e.querySelector("#wf-tbody"),n=e.querySelector("#wf-filters"),s=e.querySelector("#wf-last-load"),a=[],r=null,i=!1,m=null,k=!1;function $(b){let T=(new FormData(n).get("q")??"").trim().toLowerCase();return T?b.filter(S=>S.runId.toLowerCase().includes(T)||S.workflowId.toLowerCase().includes(T)||(S.chatId??"").toLowerCase().includes(T)):b}function p(){let b=$(a);if(b.length===0){t.innerHTML=`<tr><td colspan="7" class="empty">${m?c(o("workflow.list.failedLoad",{error:m})):a.length===0?c(o("workflow.list.noRuns")):c(o("workflow.list.noFilterMatch"))}</td></tr>`;return}t.innerHTML=b.map(f=>{let T=`${f.dEf}/${f.dAct}/${f.dWait}`,S=f.dEf+f.dAct+f.dWait>0?"wf-dangling has":"wf-dangling none",y=[];f.chatId&&y.push(c(f.chatId)),f.larkAppId&&y.push(`<span class="muted">${c(f.larkAppId)}</span>`);let x=y.length>0?y.join("<br/>"):"\u2014",u=oo(f);return`<tr data-runid="${c(f.runId)}">
612
+ <td><a href="#/workflows/${encodeURIComponent(f.runId)}"><code>${c(f.runId)}</code></a></td>
613
+ <td>${c(f.workflowId)}</td>
614
+ <td>${ne(f.status)}${f.failedNodeId?` <span class="muted">(${c(f.failedNodeId)})</span>`:""}${u}</td>
615
+ <td>${f.lastSeq}</td>
616
+ <td class="${S}">${T}</td>
617
+ <td title="${c(new Date(f.updatedAt).toISOString())}">${Zn(f.updatedAt)}</td>
618
+ <td>${x}</td>
619
+ </tr>`}).join("")}function g(){m?(s.textContent=o("workflow.list.error",{error:m}),s.classList.add("error")):(s.textContent=o("workflow.list.loaded",{count:a.length,time:new Date().toLocaleTimeString()}),s.classList.remove("error"))}async function l(){if(!(k||i)&&!document.hidden){i=!0;try{let b=n.elements.namedItem("status")?.value??"",f=new URLSearchParams;b==="all"?f.set("all","1"):b&&f.set("status",b);let T="/api/workflows/runs"+(f.toString()?`?${f}`:""),S=await fetch(T);S.ok?(a=(await S.json()).runs??[],m=null):(m=`HTTP ${S.status}`,a=[])}catch(b){m=b?.message??String(b),a=[]}finally{i=!1,k||(p(),g())}}}function I(){r!==null&&window.clearTimeout(r),r=window.setTimeout(async()=>{await l(),k||I()},Qn)}function L(){document.hidden||l()}return n.addEventListener("input",()=>{p()}),n.addEventListener("change",b=>{b.target.getAttribute("name")==="status"&&l()}),document.addEventListener("visibilitychange",L),l().then(()=>{k||I()}),()=>{k=!0,r!==null&&window.clearTimeout(r),document.removeEventListener("visibilitychange",L)}}function to(e,t,n={}){e.innerHTML=`
492
620
  <div class="wf-detail-head">
493
- <a class="btn-link" href="#/workflows">${i(n("workflow.detail.back"))}</a>
621
+ <a class="btn-link" href="#/workflows">${c(o("workflow.detail.back"))}</a>
494
622
  <div>
495
- <h2><code>${i(t)}</code></h2>
496
- <div id="wf-detail-subtitle" class="muted">${i(n("workflow.detail.loading"))}</div>
623
+ <h2><code>${c(t)}</code></h2>
624
+ <div id="wf-detail-subtitle" class="muted">${c(o("workflow.detail.loading"))}</div>
497
625
  </div>
498
- <button id="wf-cancel-run" type="button" class="contrast" hidden>${i(n("workflow.detail.cancel"))}</button>
626
+ <button id="wf-cancel-run" type="button" class="contrast" hidden>${c(o("workflow.detail.cancel"))}</button>
499
627
  <span id="wf-detail-refresh" class="muted"></span>
500
628
  </div>
501
629
  <section id="wf-detail-error" class="hint-warn" hidden></section>
@@ -504,20 +632,20 @@ ${u.join(`
504
632
  <section id="wf-dangling-panel"></section>
505
633
  <section class="wf-panel">
506
634
  <div class="wf-panel-title">
507
- <h3>${i(n("workflow.detail.parallel"))}</h3>
635
+ <h3>${c(o("workflow.detail.parallel"))}</h3>
508
636
  <span id="wf-parallel-meta" class="muted"></span>
509
637
  </div>
510
638
  <div id="wf-parallel-view"></div>
511
639
  </section>
512
640
  <section class="wf-panel">
513
641
  <div class="wf-panel-title">
514
- <h3>${i(n("workflow.detail.nodes"))}</h3>
642
+ <h3>${c(o("workflow.detail.nodes"))}</h3>
515
643
  </div>
516
644
  <div class="wf-table-scroll">
517
645
  <table>
518
646
  <thead><tr>
519
- <th>${i(n("workflow.detail.node"))}</th><th>${i(n("workflow.detail.nodeStatus"))}</th><th>${i(n("workflow.detail.activity"))}</th><th>${i(n("workflow.detail.activityStatus"))}</th>
520
- <th>${i(n("workflow.detail.attempts"))}</th><th>${i(n("workflow.detail.current"))}</th><th>${i(n("workflow.detail.detail"))}</th>
647
+ <th>${c(o("workflow.detail.node"))}</th><th>${c(o("workflow.detail.nodeStatus"))}</th><th>${c(o("workflow.detail.activity"))}</th><th>${c(o("workflow.detail.activityStatus"))}</th>
648
+ <th>${c(o("workflow.detail.attempts"))}</th><th>${c(o("workflow.detail.current"))}</th><th>${c(o("workflow.detail.detail"))}</th>
521
649
  </tr></thead>
522
650
  <tbody id="wf-node-tbody"></tbody>
523
651
  </table>
@@ -525,237 +653,237 @@ ${u.join(`
525
653
  </section>
526
654
  <section class="wf-panel">
527
655
  <div class="wf-panel-title">
528
- <h3>${i(n("workflow.detail.nodeIO"))}</h3>
656
+ <h3>${c(o("workflow.detail.nodeIO"))}</h3>
529
657
  </div>
530
658
  <div id="wf-io-list" class="wf-io-list"></div>
531
659
  </section>
532
660
  <section class="wf-panel">
533
661
  <div class="wf-panel-title">
534
- <h3>${i(n("workflow.detail.timeline"))}</h3>
535
- <button id="wf-load-older" type="button" hidden>${i(n("workflow.detail.loadOlder"))}</button>
662
+ <h3>${c(o("workflow.detail.timeline"))}</h3>
663
+ <button id="wf-load-older" type="button" hidden>${c(o("workflow.detail.loadOlder"))}</button>
536
664
  </div>
537
665
  <div class="wf-table-scroll wf-timeline-scroll">
538
666
  <table>
539
667
  <thead><tr>
540
- <th>${i(n("workflow.detail.seq"))}</th><th>${i(n("workflow.detail.event"))}</th><th>${i(n("workflow.detail.actor"))}</th><th>${i(n("workflow.detail.node"))}</th><th>${i(n("workflow.detail.activity"))}</th><th>${i(n("workflow.detail.error"))}</th><th>${i(n("workflow.detail.time"))}</th>
668
+ <th>${c(o("workflow.detail.seq"))}</th><th>${c(o("workflow.detail.event"))}</th><th>${c(o("workflow.detail.actor"))}</th><th>${c(o("workflow.detail.node"))}</th><th>${c(o("workflow.detail.activity"))}</th><th>${c(o("workflow.detail.error"))}</th><th>${c(o("workflow.detail.time"))}</th>
541
669
  </tr></thead>
542
670
  <tbody id="wf-event-tbody"></tbody>
543
671
  </table>
544
672
  </div>
545
673
  <div id="wf-event-meta" class="muted"></div>
546
674
  </section>
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">
548
- <span class="muted error">${i(e.errorCode)}</span>${i(t)}
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>
675
+ `;let s=e.querySelector("#wf-detail-subtitle"),a=e.querySelector("#wf-detail-refresh"),r=e.querySelector("#wf-detail-error"),i=e.querySelector("#wf-cancel-status"),m=e.querySelector("#wf-summary"),k=e.querySelector("#wf-dangling-panel"),$=e.querySelector("#wf-parallel-view"),p=e.querySelector("#wf-parallel-meta"),g=e.querySelector("#wf-node-tbody"),l=e.querySelector("#wf-io-list"),I=e.querySelector(".wf-timeline-scroll"),L=e.querySelector("#wf-event-tbody"),b=e.querySelector("#wf-event-meta"),f=e.querySelector("#wf-cancel-run"),T=e.querySelector("#wf-load-older"),S=null,y=[],x=new Set,u=null,w=null,h=!1,E=0,M=null,A=!1,P=!1,O=!1,H=new Set,q=new Map,j=new Map,F=new Map,z=new Set,ee=new Map,te=new Set,he=new Map,rn=new Map,rt=0,it=n.focusAttemptId;function G(v){if(!v){r.hidden=!0,r.textContent="";return}r.hidden=!1,r.textContent=v}function lt(v){if(!v){i.hidden=!0,i.textContent="";return}i.hidden=!1,i.textContent=v}async function dt(){let v=await fetch(`/api/workflows/runs/${encodeURIComponent(t)}/snapshot`);if(v.status===404)throw new Error(o("workflow.detail.unknownRun"));if(!v.ok)throw new Error(o("workflow.detail.snapshotHttp",{status:v.status}));S=await v.json()}async function Te(v){let U=await fetch(`/api/workflows/runs/${encodeURIComponent(t)}/events?${v}`);if(U.status===404)throw new Error(o("workflow.detail.unknownRun"));if(!U.ok)throw new Error(o("workflow.detail.eventsHttp",{status:U.status}));return await U.json()}function Le(v,U){let D=v.filter(R=>x.has(R.eventId)?!1:(x.add(R.eventId),!0));D.length!==0&&(y=U==="prepend"?[...D,...y]:[...y,...D],y.sort((R,oe)=>$e(R.eventId)-$e(oe.eventId)))}async function ln(){await dt();let v=await Te(new URLSearchParams({tail:"100"}));y=[],x=new Set,Le(v.events,"append"),u=v.oldestSeq,w=v.newestSeq,h=v.hasOlder,E=v.totalCount,Y()}async function Ee(){if(!(A||P||document.hidden)){P=!0;try{if(await dt(),w!==null){let v=await Te(new URLSearchParams({afterSeq:String(w),limit:"200"}));Le(v.events,"append"),v.newestSeq!==null&&(w=v.newestSeq),u===null&&v.oldestSeq!==null&&(u=v.oldestSeq),E=v.totalCount}else{let v=await Te(new URLSearchParams({tail:"1"}));Le(v.events,"append"),u=v.oldestSeq,w=v.newestSeq,h=v.hasOlder,E=v.totalCount}G(null),Y()}catch(v){G(v?.message??String(v))}finally{P=!1}}}async function dn(){if(!(u===null||!h)){T.disabled=!0;try{let v=await Te(new URLSearchParams({beforeSeq:String(u),limit:"100"}));Le(v.events,"prepend"),v.oldestSeq!==null&&(u=v.oldestSeq),h=v.hasOlder,E=v.totalCount,G(null),Y()}catch(v){G(v?.message??String(v))}finally{T.disabled=!1}}}async function cn(){if(!S||ke.has(S.run.status)||O)return;if(!S.chatBinding?.larkAppId){G(o("workflow.detail.cancelUnavailable",{runId:t}));return}let v=ao(S),U=o("workflow.detail.cancelConfirm",{runId:t,...v});if(window.confirm(U)){O=!0,f.disabled=!0;try{let D=await fetch(`/api/workflows/runs/${encodeURIComponent(t)}/cancel`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({reason:"cancelled via dashboard"})});if(D.status===401)throw new Error(o("workflow.detail.writeAccessCancel"));let R=await D.json().catch(()=>({}));if(!D.ok||!R.ok)throw new Error(R.hint??R.error??o("workflow.detail.cancelHttp",{status:D.status}));lt(R.pending?o("workflow.detail.cancelPending"):null),G(null),await Ee()}catch(D){G(D?.message??String(D))}finally{O=!1,f.disabled=!1,Y()}}}async function un(v,U){if(!te.has(v)){te.add(v),he.delete(v),Y();try{let D=await fetch(`/api/workflows/runs/${encodeURIComponent(t)}/attempts/${encodeURIComponent(U)}/${encodeURIComponent(v)}/resume`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({})});if(D.status===401)throw new Error(o("workflow.detail.writeAccessResume"));let R=await D.json().catch(()=>({}));if(!D.ok||!R.ok||!R.resumeId||!R.url)throw new Error(R.hint??R.message??R.error??o("workflow.detail.resumeStartFailed",{status:D.status}));ee.set(v,{resumeId:R.resumeId,url:R.url})}catch(D){let R=D?.message??String(D);he.set(v,R)}finally{te.delete(v),Y()}}}async function pn(v,U){if(!te.has(v)){te.add(v),he.delete(v),Y();try{let D=await fetch(`/api/workflows/runs/${encodeURIComponent(t)}/attempts/${encodeURIComponent(U)}/${encodeURIComponent(v)}/resume/end`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({reason:"ended_by_dashboard"})});if(D.status===401)throw new Error(o("workflow.detail.writeAccessResume"));let R=await D.json().catch(()=>({}));if(!D.ok||!R.ok)if(R.error==="resume_not_running")ee.delete(v);else throw new Error(R.hint??R.message??R.error??o("workflow.detail.resumeEndFailed",{status:D.status}));else ee.delete(v)}catch(D){let R=D?.message??String(D);he.set(v,R)}finally{te.delete(v),Y()}}}async function fn(v,U){if(!z.has(v)){z.add(v),F.delete(v),Y();try{let D=j.get(v)?.trim()||void 0,R=await fetch(`/api/workflows/runs/${encodeURIComponent(t)}/${U}`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({comment:D})});if(R.status===401)throw new Error(o("workflow.detail.writeAccessApproval"));let oe=await R.json().catch(()=>({}));if(!R.ok||!oe.ok)throw new Error(oe.hint??oe.message??oe.error??o("workflow.detail.actionHttp",{action:U,status:R.status}));let Fe=U==="approve"?o("workflow.detail.approved"):o("workflow.detail.rejected");F.set(v,{kind:"ok",text:oe.alreadyTerminal?o("workflow.detail.alreadyTerminal",{label:Fe}):oe.pending?o("workflow.detail.workflowContinue",{label:Fe}):o("workflow.detail.workflowRefreshing",{label:Fe})}),G(null),await Ee()}catch(D){let R=D?.message??String(D);F.set(v,{kind:"error",text:R}),G(R)}finally{z.delete(v),Y()}}}function Y(){if(!S)return;rt=I.scrollTop;let v=S.run;ke.has(v.status)&&lt(null),s.innerHTML=`${c(v.workflowId??"?")} \xB7 ${ne(v.status)} \xB7 lastSeq ${S.lastSeq}`,a.textContent=o("workflow.detail.refreshed",{time:new Date().toLocaleTimeString()}),f.hidden=ke.has(v.status),f.disabled=O||!S.chatBinding?.larkAppId,f.textContent=S.chatBinding?.larkAppId?o("workflow.detail.cancel"):o("workflow.detail.cliCancelOnly"),f.title=S.chatBinding?.larkAppId?o("workflow.detail.cancelTitle"):o("workflow.detail.cliCancelTitle",{runId:t}),no(m,S),so(k,S),ro($,p,S,y),fo(g,S),mo(l,S,H,q,{comments:j,statuses:F,resolving:z,onResolve:fn},{sessions:ee,pending:te,errors:he,onStart:un,onEnd:pn},it,rn)&&(it=void 0),jo(L,y),I.scrollTop=rt,T.hidden=!h,b.textContent=o("workflow.detail.eventsLoaded",{loaded:y.length,total:E})}function Ue(){if(M!==null&&window.clearTimeout(M),S&&ke.has(S.run.status)){M=null;return}M=window.setTimeout(async()=>{await Ee(),A||Ue()},Xn)}function ct(){document.hidden||Ee().then(()=>{!A&&M===null&&Ue()})}return T.addEventListener("click",()=>{dn()}),f.addEventListener("click",()=>{cn()}),document.addEventListener("visibilitychange",ct),ln().then(()=>{G(null),A||Ue()}).catch(v=>{G(v?.message??String(v)),s.textContent=o("workflow.detail.loadFailed")}),()=>{A=!0,M!==null&&window.clearTimeout(M),document.removeEventListener("visibilitychange",ct)}}function no(e,t){let n=t.run,s=[[o("workflow.summary.workflow"),c(n.workflowId??"?")],[o("workflow.summary.status"),ne(n.status)],[o("workflow.summary.lastSeq"),String(t.lastSeq)],[o("workflow.summary.updated"),c(new Date(t.updatedAt).toLocaleString())],[o("workflow.summary.revision"),c(Pe(n.revisionId))],[o("workflow.summary.initiator"),c(n.initiator??"-")]];n.failedNodeId&&s.push([o("workflow.summary.failedNode"),c(n.failedNodeId)]),n.cancelOriginEventId&&s.push([o("workflow.summary.cancelOrigin"),c(n.cancelOriginEventId)]),t.chatBinding&&(s.push([o("workflow.summary.chat"),`<code>${c(t.chatBinding.chatId)}</code>`]),s.push([o("workflow.summary.app"),`<code>${c(t.chatBinding.larkAppId)}</code>`])),e.innerHTML=s.map(([a,r])=>`<div class="wf-summary-item"><span>${a}</span><strong>${r}</strong></div>`).join("")}function oo(e){if(!e.errorCode)return"";let t=e.errorMessage?` \u2014 ${Wo(e.errorMessage,96)}`:"";return`<div class="wf-run-error">
676
+ <span class="muted error">${c(e.errorCode)}</span>${c(t)}
677
+ </div>`}function ao(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 so(e,t){let n=t.dangling,s=[[o("workflow.dangling.activities"),n.activities],[o("workflow.dangling.effects"),n.effectAttempted],[o("workflow.dangling.waits"),n.waits],[o("workflow.dangling.cancels"),n.cancels]],a=new Set(s.flatMap(([,r])=>r)).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>${c(o("workflow.detail.dangling"))}</h3></div><div class="muted">${c(o("workflow.detail.noDangling"))}</div>`;return}e.innerHTML=`<div class="wf-panel-title"><h3>${c(o("workflow.detail.dangling"))}</h3><span class="wf-dangling has">${a}</span></div>
550
678
  <div class="wf-dangling-grid">
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("")}
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>
679
+ ${s.map(([r,i])=>`<div><strong>${r}</strong>${i.length===0?`<div class="muted">${c(o("workflow.detail.none"))}</div>`:`<ul>${i.map(m=>`<li><code>${c(m)}</code></li>`).join("")}</ul>`}</div>`).join("")}
680
+ </div>`}function ro(e,t,n,s){let a=io(s,n);if(a.length===0){t.textContent="",e.innerHTML=`<div class="empty">${c(o("workflow.detail.noParallelData"))}</div>`;return}let r=Date.now(),i=Math.min(...a.map(l=>l.startedAt)),m=Math.max(...a.map(l=>l.endedAt??r),i+1e3),k=Math.max(1,m-i),$=co(a,r),p=a.filter(l=>!l.endedAt&&(l.status==="running"||l.status==="effectAttempting")).length;t.textContent=o("workflow.detail.parallelMeta",{count:a.length,max:$,running:p});let g=a.sort((l,I)=>l.startedAt-I.startedAt||l.activityId.localeCompare(I.activityId)).map(l=>lo(l,i,k,r)).join("");e.innerHTML=`<div class="wf-parallel-axis">
681
+ <span title="${c(new Date(i).toISOString())}">${c(qe(i))}</span>
682
+ <span title="${c(new Date(m).toISOString())}">${c(qe(m))}</span>
555
683
  </div>
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(`
684
+ <div class="wf-parallel-list">${g}</div>`}function io(e,t){let n=new Map,s=new Map(t.activities.map(a=>[a.activityId,a.ownerNodeId]));for(let a of[...e].sort((r,i)=>$e(r.eventId)-$e(i.eventId))){let r=_o(a);if(!r)continue;let i=typeof r.activityId=="string"?r.activityId:void 0,m=typeof r.attemptId=="string"?r.attemptId:void 0;if(!i||!m)continue;let k=n.get(m);if(a.type==="attemptCreated"){let $=typeof r.attemptNumber=="number"?r.attemptNumber:void 0;k={nodeId:typeof r.nodeId=="string"?r.nodeId:s.get(i),activityId:i,attemptId:m,attemptNumber:$,status:"pending",startedAt:a.timestamp},n.set(m,k);continue}k||(k={nodeId:s.get(i),activityId:i,attemptId:m,status:"pending",startedAt:a.timestamp},n.set(m,k)),a.type==="activityRunning"?(k.status="running",k.runningAt=a.timestamp):a.type==="effectAttempted"?k.status="effectAttempting":a.type==="activityWaiting"||a.type==="waitCreated"?k.status="waiting":uo(a.type)&&(k.status=po(a.type),k.endedAt=a.timestamp,k.endType=a.type)}return[...n.values()]}function lo(e,t,n,s){let a=e.endedAt??s,r=_t((e.startedAt-t)/n*100,0,100),i=_t((Math.max(a,e.startedAt+1)-e.startedAt)/n*100,.7,100-r),m=e.nodeId??e.activityId,k=e.attemptNumber!==void 0?`#${e.attemptNumber}`:Pe(e.attemptId),$=[`${m} ${e.status}`,`${new Date(e.startedAt).toISOString()} \u2192 ${e.endedAt?new Date(e.endedAt).toISOString():o("workflow.detail.parallelNow")}`,e.endType?`end: ${e.endType}`:void 0].filter(Boolean).join(`
557
685
  `);return`<div class="wf-parallel-row">
558
686
  <div class="wf-parallel-label">
559
- <code>${i(w)}</code>
560
- <span class="muted">${i(e.activityId)} \xB7 ${i(I)}</span>
687
+ <code>${c(m)}</code>
688
+ <span class="muted">${c(e.activityId)} \xB7 ${c(k)}</span>
561
689
  </div>
562
690
  <div class="wf-parallel-track">
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>
691
+ <div class="wf-parallel-bar wf-parallel-${c(e.status)}" style="left:${r.toFixed(3)}%;width:${i.toFixed(3)}%;" title="${c($)}">
692
+ <span>${c(ie(e.status))}</span>
565
693
  </div>
566
694
  </div>
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>
568
- <td>${e?`<code>${i(e.nodeId)}</code>`:'<span class="muted">-</span>'}</td>
569
- <td>${e?Y(e.status):'<span class="muted">-</span>'}</td>
570
- <td>${t?`<code>${i(t.activityId)}</code>`:'<span class="muted">-</span>'}</td>
571
- <td>${t?Y(t.status):'<span class="muted">-</span>'}</td>
695
+ </div>`}function co(e,t){let n=[];for(let r of e)n.push({time:r.startedAt,delta:1}),n.push({time:r.endedAt??t,delta:-1});n.sort((r,i)=>r.time-i.time||i.delta-r.delta);let s=0,a=0;for(let r of n)s+=r.delta,a=Math.max(a,s);return a}function uo(e){return e==="activitySucceeded"||e==="activityFailed"||e==="activityTimedOut"||e==="activityCanceled"}function po(e){return e==="activitySucceeded"?"succeeded":e==="activityCanceled"?"cancelled":e==="activityTimedOut"?"timedOut":"failed"}function fo(e,t){let n=new Map(t.activities.map(r=>[r.activityId,r])),s=new Set,a=[];for(let r of t.nodes){let i=(r.activityId?n.get(r.activityId):void 0)??t.activities.find(m=>m.ownerNodeId===r.nodeId);i&&s.add(i.activityId),a.push(Ut(r,i))}for(let r of t.activities)s.has(r.activityId)||a.push(Ut(void 0,r));e.innerHTML=a.length>0?a.join(""):`<tr><td colspan="7" class="empty">${c(o("workflow.detail.noNodes"))}</td></tr>`}function Ut(e,t){let n=t?.attempts[t.attempts.length-1];return`<tr>
696
+ <td>${e?`<code>${c(e.nodeId)}</code>`:'<span class="muted">-</span>'}</td>
697
+ <td>${e?ne(e.status):'<span class="muted">-</span>'}</td>
698
+ <td>${t?`<code>${c(t.activityId)}</code>`:'<span class="muted">-</span>'}</td>
699
+ <td>${t?ne(t.status):'<span class="muted">-</span>'}</td>
572
700
  <td>${t?.attempts.length??0}</td>
573
- <td>${o?`<code>${i(o.attemptId)}</code>`:'<span class="muted">-</span>'}</td>
574
- <td>${o?Kn(o):`<span class="muted">${i(n("workflow.detail.idle"))}</span>`}</td>
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=`
701
+ <td>${n?`<code>${c(n.attemptId)}</code>`:'<span class="muted">-</span>'}</td>
702
+ <td>${n?No(n):`<span class="muted">${c(o("workflow.detail.idle"))}</span>`}</td>
703
+ </tr>`}function mo(e,t,n,s,a,r,i,m){Ro(e,n,s),Co(e,a.comments);let k=!!(i&&t.attemptIO?.[i]?.terminal);k&&i&&n.add(ot(i,o("workflow.detail.liveTerminal")));let $=wo(t),p=new Set;if(m){for(let l of $){p.add(l.key);let I=m.get(l.key);I||(I=go(l.key),m.set(l.key,I),e.appendChild(I.article)),ho(I,l,n,a,r,i)}for(let[l,I]of Array.from(m))p.has(l)||(I.article.remove(),m.delete(l));if($.length===0){if(!e.querySelector(".wf-io-empty-placeholder")){let l=document.createElement("div");l.className="empty wf-io-empty-placeholder",l.textContent=o("workflow.detail.noNodeIO"),e.appendChild(l)}}else e.querySelector(".wf-io-empty-placeholder")?.remove()}else{let l=[];for(let I of $)l.push($o(I,n,a,r,i));e.innerHTML=l.length>0?l.join(""):`<div class="empty">${c(o("workflow.detail.noNodeIO"))}</div>`}Bo(e,s);let g=Ao(e,i);return Do(e,n),Oo(e,s),Ho(e,a),Zt(e,r),g&&k}function wo(e){let t=new Map(e.activities.map(a=>[a.activityId,a])),n=new Set,s=[];for(let a of e.nodes){let r=(a.activityId?t.get(a.activityId):void 0)??e.activities.find(i=>i.ownerNodeId===a.nodeId);if(!r){s.push({key:`node:${a.nodeId}`,node:a});continue}n.add(r.activityId),s.push({key:`activity:${r.activityId}`,node:a,activity:r,io:e.attemptIO?.[Oe(r)?.attemptId??""]})}for(let a of e.activities)n.has(a.activityId)||s.push({key:`activity:${a.activityId}`,activity:a,io:e.attemptIO?.[Oe(a)?.attemptId??""]});return s}function go(e){let t=document.createElement("article");t.className="wf-io-card",t.dataset.wfCardKey=e;let n=document.createElement("div");n.className="wf-io-card-head";let s=document.createElement("div");s.className="wf-io-terminal-slot";let a=document.createElement("div");return a.className="wf-io-grid",t.appendChild(n),t.appendChild(s),t.appendChild(a),{article:t,head:n,terminalSlot:s,grid:a,currentTerminalUrl:null}}function ho(e,t,n,s,a,r){let i=Oe(t.activity),m=t.node?.nodeId??t.activity?.ownerNodeId??t.activity?.activityId??"unknown",k=!!(i&&i.attemptId===r);e.article.classList.toggle("is-focused",k),i?e.article.dataset.wfAttemptCard=i.attemptId:delete e.article.dataset.wfAttemptCard;let $=Xt(i,s);e.head.innerHTML=`
576
704
  <header>
577
705
  <div>
578
- <strong><code>${i(w)}</code></strong>
579
- <span class="muted">${t.activity?i(t.activity.activityId):i(n("workflow.detail.notDispatched"))}</span>
706
+ <strong><code>${c(m)}</code></strong>
707
+ <span class="muted">${t.activity?c(t.activity.activityId):c(o("workflow.detail.notDispatched"))}</span>
580
708
  </div>
581
- <div>${t.node?Y(t.node.status):""} ${t.activity?Y(t.activity.status):""}</div>
709
+ <div>${t.node?ne(t.node.status):""} ${t.activity?ne(t.activity.status):""}</div>
582
710
  </header>
583
711
  <div class="wf-io-meta">
584
- ${d?`${i(n("workflow.detail.attempt"))} <code>${i(d.attemptId)}</code>`:i(n("workflow.detail.noAttempt"))}
712
+ ${i?`${c(o("workflow.detail.attempt"))} <code>${c(i.attemptId)}</code>`:c(o("workflow.detail.noAttempt"))}
585
713
  </div>
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>
714
+ ${$}
715
+ `;let p=zt(i,t.activity,t.io?.terminal,a),g=p?.url??null;if(g!==e.currentTerminalUrl)p===null?e.terminalSlot.innerHTML="":e.terminalSlot.innerHTML=Jt(t.key,i,t.activity,t.io?.terminal,p,n,a),e.currentTerminalUrl=g;else if(p!==null&&t.io?.terminal){let I=e.terminalSlot.querySelector("details.wf-terminal-block > summary");if(I){let L=Gt(p.kind);I.innerHTML=`${c(L)} ${Qt(i,t.io.terminal)}`}i&&xo(e.terminalSlot,i,t.activity,t.io.terminal,p,a)}let l=i?.attemptId??t.activity?.activityId??t.node?.nodeId??"unknown";e.grid.innerHTML=`
716
+ ${Z(l,o("workflow.detail.authoredInput"),t.io?.input,n)}
717
+ ${Z(l,o("workflow.detail.resolvedInput"),t.io?.resolvedInput,n)}
718
+ ${Z(l,o("workflow.detail.output"),t.io?.output,n)}
719
+ ${Z(l,o("workflow.detail.executionLog"),t.io?.log,n)}
720
+ ${t.io?.waitPrompt?Z(l,o("workflow.detail.waitPrompt"),t.io.waitPrompt,n):""}
721
+ `}function zt(e,t,n,s){if(!n||n.error)return null;if(So(e,n))return{kind:"live",url:To(n)};if(!e||!t||!Io(e,n))return null;let a=Eo();if(!a)return null;let r=s?.sessions.get(e.attemptId);return r?{kind:"resume",url:r.url,resumeId:r.resumeId,downloadUrl:Ft(a,t.activityId,e.attemptId)}:{kind:"replay",url:Lo(a,t.activityId,e.attemptId,!!n.hasPtyLog),downloadUrl:Ft(a,t.activityId,e.attemptId)}}function Jt(e,t,n,s,a,r,i){if(!s)return"";let m=Gt(a.kind),k=ot(e,m),$=Qt(t,s),p=bo(a.kind),g=a.kind==="replay"||a.kind==="resume"?`<a class="btn-link" href="${c(a.downloadUrl)}" download>${c(o("workflow.detail.downloadFullLog"))}</a>`:"",l=t?Vt(t,n,s,a,i):"",I=t?Yt(t.attemptId,i):"";return`<details class="wf-io-block wf-terminal-block" data-io-key="${c(k)}"${r.has(k)?" open":""}>
722
+ <summary>${c(m)} ${$}</summary>
595
723
  <div class="wf-terminal-actions">
596
- <a class="btn-link" href="${i(a.url)}" target="_blank" rel="noreferrer">${i(f)}</a>
724
+ <a class="btn-link" href="${c(a.url)}" target="_blank" rel="noreferrer">${c(p)}</a>
597
725
  ${g}
598
726
  ${l}
599
727
  </div>
600
- ${S}
601
- <iframe class="wf-terminal-frame" src="${i(a.url)}" title="${i(w)}" loading="lazy"></iframe>
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","codex-app","cursor","mira"]),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}>
728
+ ${I}
729
+ <iframe class="wf-terminal-frame" src="${c(a.url)}" title="${c(m)}" loading="lazy"></iframe>
730
+ </details>`}function Gt(e){return e==="live"?o("workflow.detail.liveTerminal"):e==="resume"?o("workflow.detail.terminalResume"):o("workflow.detail.terminalReplay")}function bo(e){return e==="live"?o("workflow.detail.openTerminalNewTab"):e==="resume"?o("workflow.detail.openResumeNewTab"):o("workflow.detail.openReplayNewTab")}var Kt=new Set(["antigravity","codex-app","cursor","mira"]),yo=new Set(["aiden","coco","claude-code","codex","mtr","hermes"]);function vo(e){return!!e&&(yo.has(e)||Kt.has(e))}function ko(e){return!!e&&Kt.has(e)}function Vt(e,t,n,s,a){if(!a||s.kind==="live"||!t)return"";let r=s.kind==="resume",i=a.pending.has(e.attemptId),m=`data-wf-resume-attempt="${c(e.attemptId)}" data-wf-resume-activity="${c(t.activityId)}"`;return r?`<button type="button" class="btn-link" data-wf-resume-button="1" data-wf-resume-action="end" ${m}${i?" disabled":""}>${c(i?o("workflow.detail.resumeEnding"):o("workflow.detail.endResumeSession"))}</button>`:vo(n.cliId)?ko(n.cliId)&&!n.cliSessionId?`<button type="button" class="btn-link" data-wf-resume-button="1" disabled title="${c(o("workflow.detail.resumeMissingCliSession"))}">${c(o("workflow.detail.resumeSession"))}</button>`:`<button type="button" class="btn-link" data-wf-resume-button="1" data-wf-resume-action="start" ${m}${i?" disabled":""}>${c(i?o("workflow.detail.resumeStarting"):o("workflow.detail.resumeSession"))}</button>`:`<button type="button" class="btn-link" data-wf-resume-button="1" disabled title="${c(o("workflow.detail.resumeUnsupportedCli",{cliId:n.cliId??"?"}))}">${c(o("workflow.detail.resumeSession"))}</button>`}function Yt(e,t){if(!t)return"";let n=t.errors.get(e);return n?`<div class="hint-warn wf-resume-status" data-wf-resume-status="${c(e)}">${c(n)}</div>`:""}function $o(e,t,n,s,a){let r=Oe(e.activity),i=e.node?.nodeId??e.activity?.ownerNodeId??e.activity?.activityId??"unknown",m=r?.attemptId??e.activity?.activityId??e.node?.nodeId??"unknown",k=Xt(r,n),$=r?.attemptId===a?" is-focused":"",p=r?` data-wf-attempt-card="${c(r.attemptId)}"`:"",g=zt(r,e.activity,e.io?.terminal,s),l=g?Jt(m,r,e.activity,e.io?.terminal,g,t,s):"";return`<article class="wf-io-card${$}" data-wf-card-key="${c(e.key)}"${p}>
603
731
  <div class="wf-io-card-head">
604
732
  <header>
605
733
  <div>
606
- <strong><code>${i(d)}</code></strong>
607
- <span class="muted">${e.activity?i(e.activity.activityId):i(n("workflow.detail.notDispatched"))}</span>
734
+ <strong><code>${c(i)}</code></strong>
735
+ <span class="muted">${e.activity?c(e.activity.activityId):c(o("workflow.detail.notDispatched"))}</span>
608
736
  </div>
609
- <div>${e.node?Y(e.node.status):""} ${e.activity?Y(e.activity.status):""}</div>
737
+ <div>${e.node?ne(e.node.status):""} ${e.activity?ne(e.activity.status):""}</div>
610
738
  </header>
611
739
  <div class="wf-io-meta">
612
- ${s?`${i(n("workflow.detail.attempt"))} <code>${i(s.attemptId)}</code>`:i(n("workflow.detail.noAttempt"))}
740
+ ${r?`${c(o("workflow.detail.attempt"))} <code>${c(r.attemptId)}</code>`:c(o("workflow.detail.noAttempt"))}
613
741
  </div>
614
- ${I}
742
+ ${k}
615
743
  </div>
616
744
  <div class="wf-io-terminal-slot">${l}</div>
617
745
  <div class="wf-io-grid">
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):""}
746
+ ${Z(m,o("workflow.detail.authoredInput"),e.io?.input,t)}
747
+ ${Z(m,o("workflow.detail.resolvedInput"),e.io?.resolvedInput,t)}
748
+ ${Z(m,o("workflow.detail.output"),e.io?.output,t)}
749
+ ${Z(m,o("workflow.detail.executionLog"),e.io?.log,t)}
750
+ ${e.io?.waitPrompt?Z(m,o("workflow.detail.waitPrompt"),e.io.waitPrompt,t):""}
623
751
  </div>
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)}">
752
+ </article>`}function Oe(e){return e?.attempts[e.attempts.length-1]}function Qt(e,t){let n=[];return t.error?n.push(o("workflow.detail.error")):n.push(t.status==="live"?o("workflow.detail.terminalLive"):o("workflow.detail.terminalClosedShort")),e?.status&&n.push(e.status),t.webPort>0&&n.push(`:${t.webPort}`),`<span class="muted">${c(n.join(" \xB7 "))}</span>`}function So(e,t){return t.status==="live"&&t.webPort>0&&(e?.status==="pending"||e?.status==="running"||e?.status==="effectAttempting")}function Io(e,t){return e.status==="succeeded"||e.status==="failed"||e.status==="cancelled"||e.status==="timedOut"?!!(t.sessionId||t.startedAt):!1}function To(e){return`http://${window.location.hostname||"127.0.0.1"}:${e.webPort}`}function Lo(e,t,n,s){let a=new URLSearchParams({runId:e,activityId:t,attemptId:n});return s&&a.set("hasPtyLog","1"),`/assets/terminal-replay.html?${a.toString()}`}function Ft(e,t,n){return`/api/workflows/runs/${encodeURIComponent(e)}/attempts/${encodeURIComponent(t)}/${encodeURIComponent(n)}/terminal-log/raw?download=1`}function Eo(){let e=window.location.hash.match(/^#\/workflows\/([^/?#]+)/);if(!e)return null;try{return decodeURIComponent(e[1])}catch{return null}}function Xt(e,t){if(!Mo(e))return"";let n=e.attemptId,s=t.comments.get(n)??"",a=t.resolving.has(n),r=t.statuses.get(n),i=r?.kind==="error"?"hint-warn":"hint-ok";return`<div class="wf-approval-box" data-wf-approval="${c(n)}">
625
753
  <label>
626
- <span>${i(n("workflow.detail.approvalComment"))}</span>
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>
754
+ <span>${c(o("workflow.detail.approvalComment"))}</span>
755
+ <textarea class="wf-approval-comment" data-wf-approval-comment="${c(n)}" rows="2" placeholder="${c(o("workflow.detail.optionalComment"))}"${a?" disabled":""}>${c(s)}</textarea>
628
756
  </label>
629
757
  <div class="wf-approval-actions">
630
- <button type="button" class="primary" data-wf-approval-action="approve" data-wf-attempt-id="${i(o)}"${a?" disabled":""}>${i(n("workflow.detail.approve"))}</button>
631
- <button type="button" data-wf-approval-action="reject" data-wf-attempt-id="${i(o)}"${a?" disabled":""}>${i(n("workflow.detail.reject"))}</button>
632
- ${a?`<span class="muted">${i(n("workflow.detail.submitting"))}</span>`:""}
758
+ <button type="button" class="primary" data-wf-approval-action="approve" data-wf-attempt-id="${c(n)}"${a?" disabled":""}>${c(o("workflow.detail.approve"))}</button>
759
+ <button type="button" data-wf-approval-action="reject" data-wf-attempt-id="${c(n)}"${a?" disabled":""}>${c(o("workflow.detail.reject"))}</button>
760
+ ${a?`<span class="muted">${c(o("workflow.detail.submitting"))}</span>`:""}
633
761
  </div>
634
- ${s?`<div class="${d} wf-approval-status">${i(s.text)}</div>`:""}
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":""}>
636
- <summary>${i(t)} ${Gn(o)}</summary>
637
- ${zn(o)}
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>
640
- <td><code>${i(e.type)}</code></td>
641
- <td>${i(e.actor)}</td>
642
- <td>${t.nodeId?`<code>${i(t.nodeId)}</code>`:"-"}</td>
643
- <td>${t.activityId?`<code>${i(t.activityId)}</code>`:"-"}</td>
644
- <td>${t.errorCode?`<span class="muted error">${i(t.errorCode)}</span>`:"-"}</td>
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=`
762
+ ${r?`<div class="${i} wf-approval-status">${c(r.text)}</div>`:""}
763
+ </div>`}function Mo(e){return!!e&&e.status==="waiting"&&e.wait?.waitKind==="human-gate"&&!e.wait.resolution}function Co(e,t){e.querySelectorAll("textarea[data-wf-approval-comment]").forEach(n=>{let s=n.dataset.wfApprovalComment;s&&t.set(s,n.value)})}function Zt(e,t){e.querySelectorAll("button[data-wf-resume-action][data-wf-resume-attempt][data-wf-resume-activity]").forEach(n=>{n.dataset.wfResumeBound!=="1"&&(n.dataset.wfResumeBound="1",n.addEventListener("click",()=>{let s=n.dataset.wfResumeAttempt,a=n.dataset.wfResumeActivity,r=n.dataset.wfResumeAction;!s||!a||(r==="start"?t.onStart(s,a):r==="end"&&t.onEnd(s,a))}))})}function xo(e,t,n,s,a,r){let i=e.querySelector(".wf-terminal-actions");if(!i)return;let m=i.querySelector('button[data-wf-resume-button="1"]'),k=Vt(t,n,s,a,r);m?m.outerHTML=k:k&&i.insertAdjacentHTML("beforeend",k);let $=e.querySelector("details.wf-terminal-block");if($){let p=$.querySelector(".wf-resume-status"),g=Yt(t.attemptId,r);p?p.outerHTML=g:g&&i.insertAdjacentHTML("afterend",g)}Zt(e,r)}function Ho(e,t){e.querySelectorAll("textarea[data-wf-approval-comment]").forEach(n=>{let s=n.dataset.wfApprovalComment;s&&n.addEventListener("input",()=>{t.comments.set(s,n.value)})}),e.querySelectorAll("button[data-wf-approval-action][data-wf-attempt-id]").forEach(n=>{n.addEventListener("click",()=>{let s=n.dataset.wfAttemptId,a=n.dataset.wfApprovalAction;!s||a!=="approve"&&a!=="reject"||t.onResolve(s,a)})})}function Z(e,t,n,s){let a=ot(e,t);return`<details class="wf-io-block" data-io-key="${c(a)}"${s.has(a)?" open":""}>
764
+ <summary>${c(t)} ${qo(n)}</summary>
765
+ ${Po(n)}
766
+ </details>`}function ot(e,t){return`${e}:${t}`}function Ao(e,t){if(!t)return!1;for(let n of e.querySelectorAll("[data-wf-attempt-card]"))if(n.dataset.wfAttemptCard===t)return n.scrollIntoView({block:"center"}),!0;return!1}function Ro(e,t,n){e.querySelectorAll("details.wf-io-block[data-io-key]").forEach(s=>{let a=s.dataset.ioKey;if(!a)return;s.open?t.add(a):t.delete(a);let r=s.querySelector(".wf-io-pre");r&&n.set(a,r.scrollTop)})}function Do(e,t){e.querySelectorAll("details.wf-io-block[data-io-key]").forEach(n=>{n.dataset.ioToggleBound!=="1"&&(n.dataset.ioToggleBound="1",n.addEventListener("toggle",()=>{let s=n.dataset.ioKey;s&&(n.open?t.add(s):t.delete(s))}))})}function Bo(e,t){e.querySelectorAll("details.wf-io-block[data-io-key]").forEach(n=>{let s=n.dataset.ioKey;if(!s)return;let a=t.get(s);if(a===void 0)return;let r=n.querySelector(".wf-io-pre");r&&(r.scrollTop=a)})}function Oo(e,t){e.querySelectorAll("details.wf-io-block[data-io-key]").forEach(n=>{let s=n.dataset.ioKey;if(!s)return;let a=n.querySelector(".wf-io-pre");a&&a.dataset.ioScrollBound!=="1"&&(a.dataset.ioScrollBound="1",a.addEventListener("scroll",()=>{t.set(s,a.scrollTop)}))})}function qo(e){if(!e)return`<span class="muted">${c(o("workflow.detail.empty"))}</span>`;let t=[];return e.outputBytes!==void 0&&t.push(`${e.outputBytes}B`),e.truncated&&t.push(o("workflow.detail.truncated")),e.error&&t.push(o("workflow.detail.error")),e.outputHash&&t.push(Pe(e.outputHash)),t.length?`<span class="muted">${c(t.join(" \xB7 "))}</span>`:""}function Po(e){if(!e)return`<div class="muted wf-io-empty">${c(o("workflow.detail.noData"))}</div>`;let t=e.value!==void 0?JSON.stringify(e.value,null,2):e.text??"",n=e.error?`<div class="muted error">${c(e.error)}</div>`:"";return t?`${n}<pre class="wf-io-pre">${c(t)}</pre>`:`${n}<div class="muted wf-io-empty">${c(o("workflow.detail.noPreview"))}</div>`}function No(e){let t=[];if(e.effectAttempted&&t.push(`${c(o("workflow.detail.effect"))} ${c(e.effectAttempted.provider)}`),e.wait){let n=e.wait.resolution?`${e.wait.resolution.kind}${e.wait.resolution.resolution?":"+e.wait.resolution.resolution:""}`:o("workflow.detail.open");t.push(`${c(o("workflow.detail.wait"))} ${c(e.wait.waitKind)} ${c(n)}`),e.wait.deadlineAt!==void 0&&t.push(`${c(o("workflow.detail.deadline"))} ${c(qe(e.wait.deadlineAt))}`)}if(e.error){let n=`${e.error.errorCode}${e.error.errorClass?` \xB7 ${e.error.errorClass}`:""}`;t.push(`<span class="muted error">${c(n)}</span>`),e.error.errorMessage&&t.push(`<span class="error wf-error-msg">${c(e.error.errorMessage)}</span>`)}return e.output&&t.push(`${c(o("workflow.detail.output"))} ${c(Pe(e.output.outputHash))}`),e.runningMs!==void 0&&t.push(`${e.runningMs}ms`),t.length>0?t.join("<br/>"):'<span class="muted">-</span>'}function jo(e,t){e.innerHTML=t.length>0?t.map(Uo).join(""):`<tr><td colspan="7" class="empty">${c(o("workflow.detail.noEvents"))}</td></tr>`}function Uo(e){let t=Fo(e.payload);return`<tr>
767
+ <td>${$e(e.eventId)}</td>
768
+ <td><code>${c(e.type)}</code></td>
769
+ <td>${c(e.actor)}</td>
770
+ <td>${t.nodeId?`<code>${c(t.nodeId)}</code>`:"-"}</td>
771
+ <td>${t.activityId?`<code>${c(t.activityId)}</code>`:"-"}</td>
772
+ <td>${t.errorCode?`<span class="muted error">${c(t.errorCode)}</span>`:"-"}</td>
773
+ <td title="${c(new Date(e.timestamp).toISOString())}">${c(qe(e.timestamp))}</td>
774
+ </tr>`}function $e(e){let t=e.lastIndexOf("-");if(t<0)return 0;let n=Number(e.slice(t+1));return Number.isFinite(n)?n:0}function Fo(e){if(!e||typeof e!="object"||"ref"in e)return{};let t=e,n={};typeof t.nodeId=="string"&&(n.nodeId=t.nodeId),typeof t.activityId=="string"&&(n.activityId=t.activityId),typeof t.failedNodeId=="string"&&(n.nodeId=t.failedNodeId);let s=t.error;return s&&typeof s=="object"&&"errorCode"in s&&(n.errorCode=String(s.errorCode)),n}function _o(e){return!e.payload||typeof e.payload!="object"||"ref"in e.payload?null:e.payload}function _t(e,t,n){return Math.min(n,Math.max(t,e))}function Pe(e){return e?e.length>18?e.slice(0,10)+"..."+e.slice(-6):e:"-"}function Wo(e,t){return e.length>t?e.slice(0,t-1)+"\u2026":e}function qe(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 en(e){return e?e.length>18?e.slice(0,10)+"..."+e.slice(-6):e:"-"}function tn(e){let t=location.hash.match(/^#\/(?:workflows\/catalog|workflows-catalog)\/([^/?#]+)$/);return t?Jo(e,decodeURIComponent(t[1])):zo(e)}function zo(e){e.innerHTML=`
647
775
  <nav class="wf-subnav">
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>
776
+ <a href="#/workflows" data-i18n="workflow.subnav.runs">${C(o("workflow.subnav.runs"))}</a>
777
+ <a href="#/workflows/catalog" class="active" data-i18n="workflow.subnav.catalog">${C(o("workflow.subnav.catalog"))}</a>
650
778
  </nav>
651
779
  <section class="catalog-head">
652
780
  <div>
653
- <h2>${M(n("catalog.title"))}</h2>
654
- <p class="muted">${M(n("catalog.subtitle"))}</p>
781
+ <h2>${C(o("catalog.title"))}</h2>
782
+ <p class="muted">${C(o("catalog.subtitle"))}</p>
655
783
  </div>
656
- <button id="catalog-refresh" type="button">${M(n("catalog.refresh"))}</button>
784
+ <button id="catalog-refresh" type="button">${C(o("catalog.refresh"))}</button>
657
785
  </section>
658
786
  <form id="catalog-filters" class="filters">
659
- <input type="search" name="q" placeholder="${M(n("catalog.searchPlaceholder"))}" />
787
+ <input type="search" name="q" placeholder="${C(o("catalog.searchPlaceholder"))}" />
660
788
  <span id="catalog-status" class="muted"></span>
661
789
  </form>
662
790
  <div class="wf-table-scroll">
663
791
  <table>
664
792
  <thead><tr>
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>
793
+ <th>${C(o("catalog.table.workflow"))}</th>
794
+ <th>${C(o("catalog.table.version"))}</th>
795
+ <th>${C(o("catalog.table.params"))}</th>
796
+ <th>${C(o("catalog.table.nodes"))}</th>
797
+ <th>${C(o("catalog.table.revision"))}</th>
798
+ <th>${C(o("catalog.table.path"))}</th>
671
799
  </tr></thead>
672
800
  <tbody id="catalog-tbody"></tbody>
673
801
  </table>
674
802
  </div>
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=>`
803
+ `;let t=e.querySelector("#catalog-tbody"),n=e.querySelector("#catalog-status"),s=e.querySelector("#catalog-filters"),a=e.querySelector("#catalog-refresh"),r=[],i=null,m=!1;function k(){let g=(new FormData(s).get("q")??"").trim().toLowerCase();return g?r.filter(l=>l.workflowId.toLowerCase().includes(g)||l.path.toLowerCase().includes(g)):r}function $(){i?(n.textContent=o("catalog.loadFailed",{error:i}),n.classList.add("error")):(n.textContent=`${r.length}`,n.classList.remove("error"));let g=k();if(g.length===0){t.innerHTML=`<tr><td colspan="6" class="empty">${r.length===0?C(o("catalog.noDefinitions")):C(o("catalog.noFilterMatch"))}</td></tr>`;return}t.innerHTML=g.map(l=>`
676
804
  <tr>
677
- <td><a href="#/workflows/catalog/${encodeURIComponent(l.workflowId)}"><code>${M(l.workflowId)}</code></a></td>
805
+ <td><a href="#/workflows/catalog/${encodeURIComponent(l.workflowId)}"><code>${C(l.workflowId)}</code></a></td>
678
806
  <td>${l.version}</td>
679
- <td>${M(n("catalog.paramSummary",{required:l.requiredParamCount,total:l.paramCount}))}</td>
807
+ <td>${C(o("catalog.paramSummary",{required:l.requiredParamCount,total:l.paramCount}))}</td>
680
808
  <td>${l.nodeCount}</td>
681
- <td><code>${M(It(l.revisionId))}</code></td>
682
- <td><code>${M(l.path)}</code></td>
809
+ <td><code>${C(en(l.revisionId))}</code></td>
810
+ <td><code>${C(l.path)}</code></td>
683
811
  </tr>
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=`
812
+ `).join("")}async function p(){a.disabled=!0,n.textContent=o("catalog.loading");try{let g=await fetch("/api/workflows/definitions");if(!g.ok)throw new Error(`HTTP ${g.status}`);r=(await g.json()).definitions??[],i=null}catch(g){i=g?.message??String(g),r=[]}finally{a.disabled=!1,m||$()}}return s.addEventListener("input",$),a.addEventListener("click",()=>{p()}),p(),()=>{m=!0}}function Jo(e,t){e.innerHTML=`
685
813
  <div class="catalog-detail-head">
686
- <a class="btn-link" href="#/workflows/catalog">${M(n("catalog.back"))}</a>
814
+ <a class="btn-link" href="#/workflows/catalog">${C(o("catalog.back"))}</a>
687
815
  <div>
688
- <h2><code>${M(t)}</code></h2>
689
- <div id="catalog-detail-subtitle" class="muted">${M(n("workflow.detail.loading"))}</div>
816
+ <h2><code>${C(t)}</code></h2>
817
+ <div id="catalog-detail-subtitle" class="muted">${C(o("workflow.detail.loading"))}</div>
690
818
  </div>
691
819
  </div>
692
820
  <section id="catalog-error" class="hint-warn" hidden></section>
693
821
  <section id="catalog-run-status" class="hint-ok" hidden></section>
694
822
  <div id="catalog-detail-body"></div>
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=`
823
+ `;let n=e.querySelector("#catalog-detail-subtitle"),s=e.querySelector("#catalog-error"),a=e.querySelector("#catalog-run-status"),r=e.querySelector("#catalog-detail-body"),i=null,m=!1,k=!1;function $(f){s.hidden=!f,s.textContent=f??""}function p(f){a.hidden=!f,a.textContent=f??""}function g(f){let T={};for(let[S,y]of Object.entries(f??{}))"default"in y&&(T[S]=y.default);return T}function l(){if(!i)return;let f=i.definition;n.textContent=`${o("catalog.revision")} ${en(i.revisionId)} \xB7 ${i.path}`;let T=JSON.stringify(g(f.params),null,2);r.innerHTML=`
696
824
  <section class="wf-panel">
697
- <div class="wf-panel-title"><h3>${M(n("catalog.summary"))}</h3></div>
825
+ <div class="wf-panel-title"><h3>${C(o("catalog.summary"))}</h3></div>
698
826
  <div class="wf-summary-grid">
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>
827
+ <div class="wf-summary-item"><span>${C(o("catalog.table.workflow"))}</span><strong><code>${C(f.workflowId)}</code></strong></div>
828
+ <div class="wf-summary-item"><span>${C(o("catalog.table.version"))}</span><strong>${f.version}</strong></div>
829
+ <div class="wf-summary-item"><span>${C(o("catalog.nodeCount"))}</span><strong>${Object.keys(f.nodes).length}</strong></div>
830
+ <div class="wf-summary-item"><span>${C(o("catalog.path"))}</span><strong><code>${C(i.path)}</code></strong></div>
703
831
  </div>
704
832
  </section>
705
833
 
706
834
  <section class="wf-panel">
707
- <div class="wf-panel-title"><h3>${M(n("catalog.runPanel"))}</h3></div>
835
+ <div class="wf-panel-title"><h3>${C(o("catalog.runPanel"))}</h3></div>
708
836
  <form id="catalog-run-form" class="catalog-run-form">
709
837
  <label>
710
- <span>${M(n("catalog.paramsJson"))}</span>
711
- <textarea id="catalog-params" rows="8" spellcheck="false" placeholder="${M(n("catalog.paramsPlaceholder"))}">${M($)}</textarea>
838
+ <span>${C(o("catalog.paramsJson"))}</span>
839
+ <textarea id="catalog-params" rows="8" spellcheck="false" placeholder="${C(o("catalog.paramsPlaceholder"))}">${C(T)}</textarea>
712
840
  </label>
713
841
  <div class="catalog-chat-grid">
714
842
  <label>
715
- <span>${M(n("catalog.chatId"))}</span>
843
+ <span>${C(o("catalog.chatId"))}</span>
716
844
  <input id="catalog-chat-id" type="text" autocomplete="off" />
717
845
  </label>
718
846
  <label>
719
- <span>${M(n("catalog.larkAppId"))}</span>
847
+ <span>${C(o("catalog.larkAppId"))}</span>
720
848
  <input id="catalog-lark-app-id" type="text" autocomplete="off" />
721
849
  </label>
722
850
  </div>
723
- <div class="muted">${M(n("catalog.chatBindingHint"))}</div>
851
+ <div class="muted">${C(o("catalog.chatBindingHint"))}</div>
724
852
  <div id="catalog-param-errors" class="catalog-param-errors" hidden></div>
725
- <button id="catalog-run-btn" type="submit" class="primary">${M(n("catalog.run"))}</button>
853
+ <button id="catalog-run-btn" type="submit" class="primary">${C(o("catalog.run"))}</button>
726
854
  </form>
727
855
  </section>
728
856
 
729
857
  <section class="wf-panel">
730
- <div class="wf-panel-title"><h3>${M(n("catalog.paramsSchema"))}</h3></div>
731
- ${no(u.params)}
858
+ <div class="wf-panel-title"><h3>${C(o("catalog.paramsSchema"))}</h3></div>
859
+ ${Go(f.params)}
732
860
  </section>
733
861
 
734
862
  <section class="wf-panel">
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>
863
+ <div class="wf-panel-title"><h3>${C(o("catalog.definitionJson"))}</h3></div>
864
+ <pre class="wf-io-pre">${C(JSON.stringify(f,null,2))}</pre>
737
865
  </section>
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])=>`
866
+ `,L()}async function I(){if(!i||k)return;let f=r.querySelector("#catalog-params"),T=r.querySelector("#catalog-chat-id"),S=r.querySelector("#catalog-lark-app-id"),y=r.querySelector("#catalog-run-btn"),x=r.querySelector("#catalog-param-errors"),u;try{if(u=JSON.parse(f.value||"{}"),!u||typeof u!="object"||Array.isArray(u))throw new Error(o("catalog.badParamsJson"))}catch(w){x.hidden=!1,x.innerHTML=`<div class="muted error">${C(w?.message??String(w))}</div>`;return}k=!0,y.disabled=!0,y.textContent=o("catalog.running"),x.hidden=!0,$(null),p(null);try{let w=await fetch(`/api/workflows/definitions/${encodeURIComponent(i.definition.workflowId)}/run`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({params:u,chatBinding:{chatId:T.value.trim(),larkAppId:S.value.trim()}})});if(w.status===401)throw new Error(o("catalog.writeAccess"));let h=await w.json().catch(()=>({}));if(!w.ok||!h.ok)throw h.issues?.length&&(x.hidden=!1,x.innerHTML=`<strong>${C(o("catalog.invalidParams"))}</strong><ul>${h.issues.map(E=>`<li>${C(o("catalog.issue",{path:E.path.length?E.path.join("."):"(root)",message:E.message}))}</li>`).join("")}</ul>`),new Error(h.hint??h.message??h.error??o("catalog.runHttp",{status:w.status}));p(o("catalog.runStarted")),h.runId&&(location.hash=`#/workflows/${encodeURIComponent(h.runId)}`)}catch(w){$(w?.message??String(w))}finally{k=!1,y.disabled=!1,y.textContent=o("catalog.run")}}function L(){r.querySelector("#catalog-run-form")?.addEventListener("submit",T=>{T.preventDefault(),I()})}async function b(){try{let f=await fetch(`/api/workflows/definitions/${encodeURIComponent(t)}`);if(f.status===404)throw new Error("unknown_workflow");if(!f.ok)throw new Error(`HTTP ${f.status}`);i=await f.json(),$(null),l()}catch(f){$(o("catalog.definitionLoadFailed",{error:f?.message??String(f)})),n.textContent=o("workflow.detail.loadFailed")}}return b().then(()=>{}),()=>{m=!0}}function Go(e){let t=Object.entries(e??{});return t.length===0?`<div class="muted">${C(o("catalog.noParams"))}</div>`:`<div class="catalog-param-list">${t.map(([n,s])=>`
739
867
  <article class="catalog-param">
740
868
  <header>
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>
869
+ <code>${C(n)}</code>
870
+ <span class="wf-status">${C(s.required?o("catalog.required"):o("catalog.optional"))}</span>
871
+ <span class="muted">${C(s.type)}${s.format?` \xB7 ${C(s.format)}`:""}</span>
744
872
  </header>
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>`:""}
873
+ ${s.description?`<div class="muted">${C(o("catalog.description"))}: ${C(s.description)}</div>`:""}
874
+ ${"default"in s?`<pre class="wf-io-pre">${C(`${o("catalog.default")}: ${JSON.stringify(s.default,null,2)}`)}</pre>`:""}
747
875
  </article>
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">
749
- <img class="qr-image" src="${e.qrDataUrl}" alt="${n("botOnboarding.qrAlt")}">
750
- ${e.qrUrl?`<a class="onboarding-link" href="${e.qrUrl}" target="_blank" rel="noopener">${n("botOnboarding.openLink")}</a>`:""}
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>
876
+ `).join("")}</div>`}var pe=null,Ne=null;function je(){Ne!==null&&(window.clearInterval(Ne),Ne=null)}function nn(){return pe||(pe=document.createElement("dialog"),pe.className="onboarding-dialog",document.body.appendChild(pe),pe.addEventListener("close",je),pe)}function Ko(e){return e.status==="waiting_for_scan"?o("botOnboarding.waiting"):e.status==="verifying"?o("botOnboarding.verifying"):e.status==="completed"?o("botOnboarding.completed"):e.status==="failed"?`${o("botOnboarding.failed")}: ${e.message??e.error??"unknown"}`:o("botOnboarding.starting")}function Se(e){let t=nn(),n=e.qrDataUrl?`<div class="qr-card">
877
+ <img class="qr-image" src="${e.qrDataUrl}" alt="${o("botOnboarding.qrAlt")}">
878
+ ${e.qrUrl?`<a class="onboarding-link" href="${e.qrUrl}" target="_blank" rel="noopener">${o("botOnboarding.openLink")}</a>`:""}
879
+ </div>`:"",s=e.appId?`<p><b>App ID:</b> <code>${e.appId}</code></p>`:"",a=e.status==="completed"?`<p class="hint-ok">${o("botOnboarding.restartHint")}</p>`:"";t.innerHTML=`<article>
752
880
  <header>
753
- <h3>${n("botOnboarding.title")}</h3>
754
- <p>${n("botOnboarding.intro")}</p>
881
+ <h3>${o("botOnboarding.title")}</h3>
882
+ <p>${o("botOnboarding.intro")}</p>
755
883
  </header>
756
- <p class="onboarding-status status-${e.status}">${oo(e)}</p>
757
- ${o}
758
- ${r}
884
+ <p class="onboarding-status status-${e.status}">${Ko(e)}</p>
885
+ ${n}
886
+ ${s}
759
887
  ${a}
760
- <form method="dialog"><button>${n("botOnboarding.close")}</button></form>
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()})();})();
888
+ <form method="dialog"><button>${o("botOnboarding.close")}</button></form>
889
+ </article>`}async function Vo(e){let t=await fetch(`/api/bot-onboarding/${encodeURIComponent(e)}`),n=await t.json();if(!t.ok||!n?.job)throw new Error(n?.error??`http_${t.status}`);Se(n.job),(n.job.status==="completed"||n.job.status==="failed")&&je()}async function Yo(){je(),Se({id:"",status:"starting"});let e=nn();e.open||e.showModal();try{let t=await fetch("/api/bot-onboarding/start",{method:"POST"}),n=await t.json();if(!t.ok||!n?.job?.id)throw new Error(n?.error??`http_${t.status}`);Se(n.job),Ne=window.setInterval(()=>{Vo(n.job.id).catch(s=>{je(),Se({id:n.job.id,status:"failed",message:s instanceof Error?s.message:String(s)})})},1200)}catch(t){Se({id:"",status:"failed",message:t instanceof Error?t.message:String(t)})}}function on(){let e=document.getElementById("add-bot-btn");e&&(e.onclick=()=>{Yo()})}var V=document.getElementById("root"),Ie=null;function at(){Ie&&(Ie(),Ie=null);let e=location.hash||"#/";e.startsWith("#/workflows/catalog")||e.startsWith("#/workflows-catalog")?Ie=tn(V):e.startsWith("#/workflows")?Ie=Wt(V):e.startsWith("#/groups")?St(V):e.startsWith("#/bot-defaults")?Lt(V):e.startsWith("#/connectors")?jt(V):e.startsWith("#/team/manage")?qt(V):e.startsWith("#/team")?Ot(V):e.startsWith("#/roles")?xt(V):e.startsWith("#/schedules")?kt(V):e.startsWith("#/sessions")?yt(V):ht(V);for(let t of document.querySelectorAll(".sidebar-nav a")){let n=t.getAttribute("href")??"#/";t.classList.toggle("active",n===(e||"#/"))}}var st=document.getElementById("status");function sn(){st&&(st.textContent=N.online?o("status.live"):o("status.disconnected"),st.className="connection-status "+(N.online?"online":"offline"))}N.on(sn);function an(){document.querySelectorAll("[data-i18n]").forEach(e=>{e.textContent=o(e.dataset.i18n??"")}),document.querySelectorAll("[data-locale]").forEach(e=>{e.classList.toggle("active",e.dataset.locale===ae.locale)}),document.querySelectorAll("[data-theme-mode]").forEach(e=>{e.classList.toggle("active",e.dataset.themeMode===ae.themeMode)}),sn()}function Qo(){document.querySelectorAll("[data-locale]").forEach(e=>{e.onclick=()=>ae.setLocale(e.dataset.locale)}),document.querySelectorAll("[data-theme-mode]").forEach(e=>{e.onclick=()=>ae.setThemeMode(e.dataset.themeMode)})}(async()=>{ae.init(),Qo(),on(),ae.on(()=>{an(),at()}),an();try{await ut()}catch(e){console.error("botmux dashboard bootstrap failed",e),N.setOnline(!1)}window.addEventListener("hashchange",at),at()})();})();