botmux 2.46.1 → 2.47.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 (468) hide show
  1. package/README.en.md +5 -10
  2. package/README.md +5 -10
  3. package/dist/adapters/backend/tmux-backend.d.ts.map +1 -1
  4. package/dist/adapters/backend/tmux-backend.js +0 -11
  5. package/dist/adapters/backend/tmux-backend.js.map +1 -1
  6. package/dist/adapters/cli/claude-code.d.ts.map +1 -1
  7. package/dist/adapters/cli/claude-code.js +9 -36
  8. package/dist/adapters/cli/claude-code.js.map +1 -1
  9. package/dist/adapters/cli/coco.d.ts.map +1 -1
  10. package/dist/adapters/cli/coco.js +1 -26
  11. package/dist/adapters/cli/coco.js.map +1 -1
  12. package/dist/adapters/cli/codex.d.ts.map +1 -1
  13. package/dist/adapters/cli/codex.js +1 -6
  14. package/dist/adapters/cli/codex.js.map +1 -1
  15. package/dist/adapters/cli/cursor.d.ts.map +1 -1
  16. package/dist/adapters/cli/cursor.js +12 -58
  17. package/dist/adapters/cli/cursor.js.map +1 -1
  18. package/dist/adapters/cli/gemini.d.ts.map +1 -1
  19. package/dist/adapters/cli/gemini.js +1 -5
  20. package/dist/adapters/cli/gemini.js.map +1 -1
  21. package/dist/adapters/cli/opencode.d.ts.map +1 -1
  22. package/dist/adapters/cli/opencode.js +1 -19
  23. package/dist/adapters/cli/opencode.js.map +1 -1
  24. package/dist/adapters/cli/registry.d.ts +1 -5
  25. package/dist/adapters/cli/registry.d.ts.map +1 -1
  26. package/dist/adapters/cli/registry.js +2 -22
  27. package/dist/adapters/cli/registry.js.map +1 -1
  28. package/dist/adapters/cli/shared-hints.d.ts +1 -1
  29. package/dist/adapters/cli/shared-hints.d.ts.map +1 -1
  30. package/dist/adapters/cli/shared-hints.js +1 -2
  31. package/dist/adapters/cli/shared-hints.js.map +1 -1
  32. package/dist/adapters/cli/types.d.ts +2 -35
  33. package/dist/adapters/cli/types.d.ts.map +1 -1
  34. package/dist/bot-registry.d.ts +0 -59
  35. package/dist/bot-registry.d.ts.map +1 -1
  36. package/dist/bot-registry.js +0 -67
  37. package/dist/bot-registry.js.map +1 -1
  38. package/dist/cli/bots-list-output.d.ts +0 -8
  39. package/dist/cli/bots-list-output.d.ts.map +1 -1
  40. package/dist/cli/bots-list-output.js +0 -9
  41. package/dist/cli/bots-list-output.js.map +1 -1
  42. package/dist/cli.d.ts +1 -15
  43. package/dist/cli.d.ts.map +1 -1
  44. package/dist/cli.js +106 -603
  45. package/dist/cli.js.map +1 -1
  46. package/dist/config.d.ts +2 -11
  47. package/dist/config.d.ts.map +1 -1
  48. package/dist/config.js +4 -17
  49. package/dist/config.js.map +1 -1
  50. package/dist/core/command-handler.d.ts +0 -20
  51. package/dist/core/command-handler.d.ts.map +1 -1
  52. package/dist/core/command-handler.js +313 -762
  53. package/dist/core/command-handler.js.map +1 -1
  54. package/dist/core/dashboard-ipc-server.d.ts +0 -2
  55. package/dist/core/dashboard-ipc-server.d.ts.map +1 -1
  56. package/dist/core/dashboard-ipc-server.js +2 -222
  57. package/dist/core/dashboard-ipc-server.js.map +1 -1
  58. package/dist/core/role-resolver.d.ts +1 -17
  59. package/dist/core/role-resolver.d.ts.map +1 -1
  60. package/dist/core/role-resolver.js +10 -64
  61. package/dist/core/role-resolver.js.map +1 -1
  62. package/dist/core/session-discovery.d.ts.map +1 -1
  63. package/dist/core/session-discovery.js +5 -19
  64. package/dist/core/session-discovery.js.map +1 -1
  65. package/dist/core/session-manager.d.ts +1 -1
  66. package/dist/core/session-manager.d.ts.map +1 -1
  67. package/dist/core/session-manager.js +20 -37
  68. package/dist/core/session-manager.js.map +1 -1
  69. package/dist/core/types.d.ts +0 -5
  70. package/dist/core/types.d.ts.map +1 -1
  71. package/dist/core/types.js.map +1 -1
  72. package/dist/core/worker-pool.d.ts +0 -141
  73. package/dist/core/worker-pool.d.ts.map +1 -1
  74. package/dist/core/worker-pool.js +24 -543
  75. package/dist/core/worker-pool.js.map +1 -1
  76. package/dist/daemon.d.ts.map +1 -1
  77. package/dist/daemon.js +58 -213
  78. package/dist/daemon.js.map +1 -1
  79. package/dist/dashboard/auth.d.ts +1 -6
  80. package/dist/dashboard/auth.d.ts.map +1 -1
  81. package/dist/dashboard/auth.js +1 -9
  82. package/dist/dashboard/auth.js.map +1 -1
  83. package/dist/dashboard/web/app.js +0 -8
  84. package/dist/dashboard/web/app.js.map +1 -1
  85. package/dist/dashboard/web/bot-defaults.d.ts.map +1 -1
  86. package/dist/dashboard/web/bot-defaults.js +21 -205
  87. package/dist/dashboard/web/bot-defaults.js.map +1 -1
  88. package/dist/dashboard/web/i18n.d.ts.map +1 -1
  89. package/dist/dashboard/web/i18n.js +5 -43
  90. package/dist/dashboard/web/i18n.js.map +1 -1
  91. package/dist/dashboard/web/sessions.d.ts.map +1 -1
  92. package/dist/dashboard/web/sessions.js +0 -4
  93. package/dist/dashboard/web/sessions.js.map +1 -1
  94. package/dist/dashboard/web/workflows.js +3 -3
  95. package/dist/dashboard/web/workflows.js.map +1 -1
  96. package/dist/dashboard/workflow-api.d.ts +1 -8
  97. package/dist/dashboard/workflow-api.d.ts.map +1 -1
  98. package/dist/dashboard/workflow-api.js +4 -19
  99. package/dist/dashboard/workflow-api.js.map +1 -1
  100. package/dist/dashboard-web/app.js +375 -539
  101. package/dist/dashboard-web/index.html +1 -3
  102. package/dist/dashboard-web/style.css +0 -22
  103. package/dist/dashboard.js +2 -199
  104. package/dist/dashboard.js.map +1 -1
  105. package/dist/i18n/en.d.ts.map +1 -1
  106. package/dist/i18n/en.js +11 -104
  107. package/dist/i18n/en.js.map +1 -1
  108. package/dist/i18n/zh.d.ts.map +1 -1
  109. package/dist/i18n/zh.js +11 -104
  110. package/dist/i18n/zh.js.map +1 -1
  111. package/dist/im/lark/card-builder.d.ts +3 -108
  112. package/dist/im/lark/card-builder.d.ts.map +1 -1
  113. package/dist/im/lark/card-builder.js +50 -480
  114. package/dist/im/lark/card-builder.js.map +1 -1
  115. package/dist/im/lark/card-handler.d.ts.map +1 -1
  116. package/dist/im/lark/card-handler.js +18 -241
  117. package/dist/im/lark/card-handler.js.map +1 -1
  118. package/dist/im/lark/client.d.ts +0 -83
  119. package/dist/im/lark/client.d.ts.map +1 -1
  120. package/dist/im/lark/client.js +70 -286
  121. package/dist/im/lark/client.js.map +1 -1
  122. package/dist/im/lark/event-dispatcher.d.ts.map +1 -1
  123. package/dist/im/lark/event-dispatcher.js +4 -29
  124. package/dist/im/lark/event-dispatcher.js.map +1 -1
  125. package/dist/im/lark/grant-command.d.ts +1 -2
  126. package/dist/im/lark/grant-command.d.ts.map +1 -1
  127. package/dist/im/lark/grant-command.js +2 -3
  128. package/dist/im/lark/grant-command.js.map +1 -1
  129. package/dist/im/lark/identity-cache.d.ts.map +1 -1
  130. package/dist/im/lark/identity-cache.js +3 -3
  131. package/dist/im/lark/identity-cache.js.map +1 -1
  132. package/dist/im/lark/md-card.d.ts +2 -20
  133. package/dist/im/lark/md-card.d.ts.map +1 -1
  134. package/dist/im/lark/md-card.js +17 -49
  135. package/dist/im/lark/md-card.js.map +1 -1
  136. package/dist/im/lark/message-parser.d.ts.map +1 -1
  137. package/dist/im/lark/message-parser.js +31 -87
  138. package/dist/im/lark/message-parser.js.map +1 -1
  139. package/dist/im/lark/workflow-card-handler.d.ts +2 -2
  140. package/dist/im/lark/workflow-card-handler.d.ts.map +1 -1
  141. package/dist/im/lark/workflow-card-handler.js +1 -12
  142. package/dist/im/lark/workflow-card-handler.js.map +1 -1
  143. package/dist/im/lark/workflow-progress-card.d.ts.map +1 -1
  144. package/dist/im/lark/workflow-progress-card.js +0 -53
  145. package/dist/im/lark/workflow-progress-card.js.map +1 -1
  146. package/dist/services/codex-bridge-queue.d.ts +0 -1
  147. package/dist/services/codex-bridge-queue.d.ts.map +1 -1
  148. package/dist/services/codex-bridge-queue.js +0 -23
  149. package/dist/services/codex-bridge-queue.js.map +1 -1
  150. package/dist/services/codex-transcript.d.ts +0 -1
  151. package/dist/services/codex-transcript.d.ts.map +1 -1
  152. package/dist/services/codex-transcript.js.map +1 -1
  153. package/dist/services/feishu-task-client.d.ts +28 -0
  154. package/dist/services/feishu-task-client.d.ts.map +1 -0
  155. package/dist/services/feishu-task-client.js +123 -0
  156. package/dist/services/feishu-task-client.js.map +1 -0
  157. package/dist/services/grant-store.d.ts +2 -12
  158. package/dist/services/grant-store.d.ts.map +1 -1
  159. package/dist/services/grant-store.js +4 -51
  160. package/dist/services/grant-store.js.map +1 -1
  161. package/dist/services/group-creator.d.ts +0 -10
  162. package/dist/services/group-creator.d.ts.map +1 -1
  163. package/dist/services/group-creator.js +1 -26
  164. package/dist/services/group-creator.js.map +1 -1
  165. package/dist/services/groups-store.d.ts +0 -30
  166. package/dist/services/groups-store.d.ts.map +1 -1
  167. package/dist/services/groups-store.js +12 -85
  168. package/dist/services/groups-store.js.map +1 -1
  169. package/dist/services/project-scanner.d.ts +0 -10
  170. package/dist/services/project-scanner.d.ts.map +1 -1
  171. package/dist/services/project-scanner.js +0 -11
  172. package/dist/services/project-scanner.js.map +1 -1
  173. package/dist/services/session-store.js +1 -1
  174. package/dist/services/session-store.js.map +1 -1
  175. package/dist/services/task-store.d.ts +37 -0
  176. package/dist/services/task-store.d.ts.map +1 -0
  177. package/dist/services/task-store.js +115 -0
  178. package/dist/services/task-store.js.map +1 -0
  179. package/dist/setup/bot-config-editor.d.ts +1 -8
  180. package/dist/setup/bot-config-editor.d.ts.map +1 -1
  181. package/dist/setup/bot-config-editor.js +2 -20
  182. package/dist/setup/bot-config-editor.js.map +1 -1
  183. package/dist/setup/ensure-tmux.d.ts +22 -0
  184. package/dist/setup/ensure-tmux.d.ts.map +1 -1
  185. package/dist/setup/ensure-tmux.js +1 -25
  186. package/dist/setup/ensure-tmux.js.map +1 -1
  187. package/dist/setup/verify-permissions.d.ts.map +1 -1
  188. package/dist/setup/verify-permissions.js +1 -15
  189. package/dist/setup/verify-permissions.js.map +1 -1
  190. package/dist/skills/definitions.d.ts +0 -2
  191. package/dist/skills/definitions.d.ts.map +1 -1
  192. package/dist/skills/definitions.js +12 -178
  193. package/dist/skills/definitions.js.map +1 -1
  194. package/dist/skills/installer.d.ts +0 -34
  195. package/dist/skills/installer.d.ts.map +1 -1
  196. package/dist/skills/installer.js +2 -119
  197. package/dist/skills/installer.js.map +1 -1
  198. package/dist/types.d.ts +0 -25
  199. package/dist/types.d.ts.map +1 -1
  200. package/dist/utils/bot-routing.d.ts +0 -50
  201. package/dist/utils/bot-routing.d.ts.map +1 -1
  202. package/dist/utils/bot-routing.js +0 -83
  203. package/dist/utils/bot-routing.js.map +1 -1
  204. package/dist/utils/user-token.d.ts.map +1 -1
  205. package/dist/utils/user-token.js +2 -0
  206. package/dist/utils/user-token.js.map +1 -1
  207. package/dist/worker.js +27 -198
  208. package/dist/worker.js.map +1 -1
  209. package/dist/workflows/attempt-resume.d.ts.map +1 -1
  210. package/dist/workflows/attempt-resume.js +2 -2
  211. package/dist/workflows/attempt-resume.js.map +1 -1
  212. package/dist/workflows/definition.d.ts +9 -412
  213. package/dist/workflows/definition.d.ts.map +1 -1
  214. package/dist/workflows/definition.js +3 -238
  215. package/dist/workflows/definition.js.map +1 -1
  216. package/dist/workflows/events/payloads.d.ts +11 -114
  217. package/dist/workflows/events/payloads.d.ts.map +1 -1
  218. package/dist/workflows/events/payloads.js +0 -46
  219. package/dist/workflows/events/payloads.js.map +1 -1
  220. package/dist/workflows/events/replay.d.ts +0 -21
  221. package/dist/workflows/events/replay.d.ts.map +1 -1
  222. package/dist/workflows/events/replay.js +0 -103
  223. package/dist/workflows/events/replay.js.map +1 -1
  224. package/dist/workflows/events/schema.d.ts +1017 -1712
  225. package/dist/workflows/events/schema.d.ts.map +1 -1
  226. package/dist/workflows/events/schema.js +1 -37
  227. package/dist/workflows/events/schema.js.map +1 -1
  228. package/dist/workflows/events/types.d.ts +1 -5
  229. package/dist/workflows/events/types.d.ts.map +1 -1
  230. package/dist/workflows/loader.d.ts +0 -14
  231. package/dist/workflows/loader.d.ts.map +1 -1
  232. package/dist/workflows/loader.js +0 -27
  233. package/dist/workflows/loader.js.map +1 -1
  234. package/dist/workflows/loop.js +0 -58
  235. package/dist/workflows/loop.js.map +1 -1
  236. package/dist/workflows/ops-projection.d.ts +0 -58
  237. package/dist/workflows/ops-projection.d.ts.map +1 -1
  238. package/dist/workflows/ops-projection.js +0 -74
  239. package/dist/workflows/ops-projection.js.map +1 -1
  240. package/dist/workflows/orchestrator.d.ts +1 -65
  241. package/dist/workflows/orchestrator.d.ts.map +1 -1
  242. package/dist/workflows/orchestrator.js +74 -486
  243. package/dist/workflows/orchestrator.js.map +1 -1
  244. package/dist/workflows/output-binding.d.ts +1 -8
  245. package/dist/workflows/output-binding.d.ts.map +1 -1
  246. package/dist/workflows/output-binding.js +11 -75
  247. package/dist/workflows/output-binding.js.map +1 -1
  248. package/dist/workflows/runtime.d.ts +1 -1
  249. package/dist/workflows/runtime.d.ts.map +1 -1
  250. package/dist/workflows/runtime.js +4 -39
  251. package/dist/workflows/runtime.js.map +1 -1
  252. package/dist/workflows/wait.d.ts +2 -23
  253. package/dist/workflows/wait.d.ts.map +1 -1
  254. package/dist/workflows/wait.js +17 -39
  255. package/dist/workflows/wait.js.map +1 -1
  256. package/package.json +1 -1
  257. package/dist/adapters/adopt-route.d.ts +0 -63
  258. package/dist/adapters/adopt-route.d.ts.map +0 -1
  259. package/dist/adapters/adopt-route.js +0 -195
  260. package/dist/adapters/adopt-route.js.map +0 -1
  261. package/dist/adapters/cli/codex-app.d.ts +0 -4
  262. package/dist/adapters/cli/codex-app.d.ts.map +0 -1
  263. package/dist/adapters/cli/codex-app.js +0 -72
  264. package/dist/adapters/cli/codex-app.js.map +0 -1
  265. package/dist/adapters/cli/hermes.d.ts +0 -4
  266. package/dist/adapters/cli/hermes.d.ts.map +0 -1
  267. package/dist/adapters/cli/hermes.js +0 -40
  268. package/dist/adapters/cli/hermes.js.map +0 -1
  269. package/dist/adapters/cli/mira.d.ts +0 -4
  270. package/dist/adapters/cli/mira.d.ts.map +0 -1
  271. package/dist/adapters/cli/mira.js +0 -67
  272. package/dist/adapters/cli/mira.js.map +0 -1
  273. package/dist/adapters/cli/mtr.d.ts +0 -5
  274. package/dist/adapters/cli/mtr.d.ts.map +0 -1
  275. package/dist/adapters/cli/mtr.js +0 -62
  276. package/dist/adapters/cli/mtr.js.map +0 -1
  277. package/dist/adapters/hook-command.d.ts +0 -18
  278. package/dist/adapters/hook-command.d.ts.map +0 -1
  279. package/dist/adapters/hook-command.js +0 -38
  280. package/dist/adapters/hook-command.js.map +0 -1
  281. package/dist/adapters/hook-installer.d.ts +0 -14
  282. package/dist/adapters/hook-installer.d.ts.map +0 -1
  283. package/dist/adapters/hook-installer.js +0 -192
  284. package/dist/adapters/hook-installer.js.map +0 -1
  285. package/dist/codex-app-runner.d.ts +0 -3
  286. package/dist/codex-app-runner.d.ts.map +0 -1
  287. package/dist/codex-app-runner.js +0 -512
  288. package/dist/codex-app-runner.js.map +0 -1
  289. package/dist/core/ask-api.d.ts +0 -47
  290. package/dist/core/ask-api.d.ts.map +0 -1
  291. package/dist/core/ask-api.js +0 -139
  292. package/dist/core/ask-api.js.map +0 -1
  293. package/dist/core/ask-args.d.ts +0 -53
  294. package/dist/core/ask-args.d.ts.map +0 -1
  295. package/dist/core/ask-args.js +0 -122
  296. package/dist/core/ask-args.js.map +0 -1
  297. package/dist/core/ask-broker.d.ts +0 -98
  298. package/dist/core/ask-broker.d.ts.map +0 -1
  299. package/dist/core/ask-broker.js +0 -329
  300. package/dist/core/ask-broker.js.map +0 -1
  301. package/dist/core/ask-hook/claude-code.d.ts +0 -50
  302. package/dist/core/ask-hook/claude-code.d.ts.map +0 -1
  303. package/dist/core/ask-hook/claude-code.js +0 -145
  304. package/dist/core/ask-hook/claude-code.js.map +0 -1
  305. package/dist/core/ask-hook/codex.d.ts +0 -43
  306. package/dist/core/ask-hook/codex.d.ts.map +0 -1
  307. package/dist/core/ask-hook/codex.js +0 -69
  308. package/dist/core/ask-hook/codex.js.map +0 -1
  309. package/dist/core/ask-hook/opencode.d.ts +0 -41
  310. package/dist/core/ask-hook/opencode.d.ts.map +0 -1
  311. package/dist/core/ask-hook/opencode.js +0 -108
  312. package/dist/core/ask-hook/opencode.js.map +0 -1
  313. package/dist/core/ask-hook/registry.d.ts +0 -3
  314. package/dist/core/ask-hook/registry.d.ts.map +0 -1
  315. package/dist/core/ask-hook/registry.js +0 -12
  316. package/dist/core/ask-hook/registry.js.map +0 -1
  317. package/dist/core/ask-hook/types.d.ts +0 -26
  318. package/dist/core/ask-hook/types.d.ts.map +0 -1
  319. package/dist/core/ask-hook/types.js +0 -2
  320. package/dist/core/ask-hook/types.js.map +0 -1
  321. package/dist/core/ask-types.d.ts +0 -146
  322. package/dist/core/ask-types.d.ts.map +0 -1
  323. package/dist/core/ask-types.js +0 -18
  324. package/dist/core/ask-types.js.map +0 -1
  325. package/dist/core/trigger-session.d.ts +0 -9
  326. package/dist/core/trigger-session.d.ts.map +0 -1
  327. package/dist/core/trigger-session.js +0 -158
  328. package/dist/core/trigger-session.js.map +0 -1
  329. package/dist/dashboard/connector-api.d.ts +0 -3
  330. package/dist/dashboard/connector-api.d.ts.map +0 -1
  331. package/dist/dashboard/connector-api.js +0 -351
  332. package/dist/dashboard/connector-api.js.map +0 -1
  333. package/dist/dashboard/federated-group-core.d.ts +0 -54
  334. package/dist/dashboard/federated-group-core.d.ts.map +0 -1
  335. package/dist/dashboard/federated-group-core.js +0 -165
  336. package/dist/dashboard/federated-group-core.js.map +0 -1
  337. package/dist/dashboard/federation-api.d.ts +0 -42
  338. package/dist/dashboard/federation-api.d.ts.map +0 -1
  339. package/dist/dashboard/federation-api.js +0 -408
  340. package/dist/dashboard/federation-api.js.map +0 -1
  341. package/dist/dashboard/federation-spoke-api.d.ts +0 -76
  342. package/dist/dashboard/federation-spoke-api.d.ts.map +0 -1
  343. package/dist/dashboard/federation-spoke-api.js +0 -618
  344. package/dist/dashboard/federation-spoke-api.js.map +0 -1
  345. package/dist/dashboard/team-group.d.ts +0 -18
  346. package/dist/dashboard/team-group.d.ts.map +0 -1
  347. package/dist/dashboard/team-group.js +0 -7
  348. package/dist/dashboard/team-group.js.map +0 -1
  349. package/dist/dashboard/trigger-api.d.ts +0 -13
  350. package/dist/dashboard/trigger-api.d.ts.map +0 -1
  351. package/dist/dashboard/trigger-api.js +0 -77
  352. package/dist/dashboard/trigger-api.js.map +0 -1
  353. package/dist/dashboard/web/connectors.d.ts +0 -2
  354. package/dist/dashboard/web/connectors.d.ts.map +0 -1
  355. package/dist/dashboard/web/connectors.js +0 -187
  356. package/dist/dashboard/web/connectors.js.map +0 -1
  357. package/dist/dashboard/web/team-federation.d.ts +0 -3
  358. package/dist/dashboard/web/team-federation.d.ts.map +0 -1
  359. package/dist/dashboard/web/team-federation.js +0 -487
  360. package/dist/dashboard/web/team-federation.js.map +0 -1
  361. package/dist/dashboard/webhook-routes.d.ts +0 -19
  362. package/dist/dashboard/webhook-routes.d.ts.map +0 -1
  363. package/dist/dashboard/webhook-routes.js +0 -321
  364. package/dist/dashboard/webhook-routes.js.map +0 -1
  365. package/dist/im/lark/ask-card.d.ts +0 -55
  366. package/dist/im/lark/ask-card.d.ts.map +0 -1
  367. package/dist/im/lark/ask-card.js +0 -328
  368. package/dist/im/lark/ask-card.js.map +0 -1
  369. package/dist/mira-output.d.ts +0 -3
  370. package/dist/mira-output.d.ts.map +0 -1
  371. package/dist/mira-output.js +0 -136
  372. package/dist/mira-output.js.map +0 -1
  373. package/dist/mira-runner.d.ts +0 -3
  374. package/dist/mira-runner.d.ts.map +0 -1
  375. package/dist/mira-runner.js +0 -534
  376. package/dist/mira-runner.js.map +0 -1
  377. package/dist/services/bot-owner-store.d.ts +0 -28
  378. package/dist/services/bot-owner-store.d.ts.map +0 -1
  379. package/dist/services/bot-owner-store.js +0 -82
  380. package/dist/services/bot-owner-store.js.map +0 -1
  381. package/dist/services/bot-profile-store.d.ts +0 -16
  382. package/dist/services/bot-profile-store.d.ts.map +0 -1
  383. package/dist/services/bot-profile-store.js +0 -98
  384. package/dist/services/bot-profile-store.js.map +0 -1
  385. package/dist/services/brand-store.d.ts +0 -15
  386. package/dist/services/brand-store.d.ts.map +0 -1
  387. package/dist/services/brand-store.js +0 -47
  388. package/dist/services/brand-store.js.map +0 -1
  389. package/dist/services/card-prefs-store.d.ts +0 -20
  390. package/dist/services/card-prefs-store.d.ts.map +0 -1
  391. package/dist/services/card-prefs-store.js +0 -82
  392. package/dist/services/card-prefs-store.js.map +0 -1
  393. package/dist/services/connector-store.d.ts +0 -58
  394. package/dist/services/connector-store.d.ts.map +0 -1
  395. package/dist/services/connector-store.js +0 -79
  396. package/dist/services/connector-store.js.map +0 -1
  397. package/dist/services/deployment-identity.d.ts +0 -22
  398. package/dist/services/deployment-identity.d.ts.map +0 -1
  399. package/dist/services/deployment-identity.js +0 -67
  400. package/dist/services/deployment-identity.js.map +0 -1
  401. package/dist/services/federation-membership-store.d.ts +0 -23
  402. package/dist/services/federation-membership-store.d.ts.map +0 -1
  403. package/dist/services/federation-membership-store.js +0 -66
  404. package/dist/services/federation-membership-store.js.map +0 -1
  405. package/dist/services/federation-roster.d.ts +0 -54
  406. package/dist/services/federation-roster.d.ts.map +0 -1
  407. package/dist/services/federation-roster.js +0 -51
  408. package/dist/services/federation-roster.js.map +0 -1
  409. package/dist/services/federation-store.d.ts +0 -76
  410. package/dist/services/federation-store.d.ts.map +0 -1
  411. package/dist/services/federation-store.js +0 -133
  412. package/dist/services/federation-store.js.map +0 -1
  413. package/dist/services/hermes-transcript.d.ts +0 -7
  414. package/dist/services/hermes-transcript.d.ts.map +0 -1
  415. package/dist/services/hermes-transcript.js +0 -117
  416. package/dist/services/hermes-transcript.js.map +0 -1
  417. package/dist/services/invite-store.d.ts +0 -28
  418. package/dist/services/invite-store.d.ts.map +0 -1
  419. package/dist/services/invite-store.js +0 -85
  420. package/dist/services/invite-store.js.map +0 -1
  421. package/dist/services/pairing-store.d.ts +0 -47
  422. package/dist/services/pairing-store.d.ts.map +0 -1
  423. package/dist/services/pairing-store.js +0 -132
  424. package/dist/services/pairing-store.js.map +0 -1
  425. package/dist/services/relay-picker.d.ts +0 -22
  426. package/dist/services/relay-picker.d.ts.map +0 -1
  427. package/dist/services/relay-picker.js +0 -62
  428. package/dist/services/relay-picker.js.map +0 -1
  429. package/dist/services/send-policy.d.ts +0 -55
  430. package/dist/services/send-policy.d.ts.map +0 -1
  431. package/dist/services/send-policy.js +0 -47
  432. package/dist/services/send-policy.js.map +0 -1
  433. package/dist/services/team-roster.d.ts +0 -38
  434. package/dist/services/team-roster.d.ts.map +0 -1
  435. package/dist/services/team-roster.js +0 -82
  436. package/dist/services/team-roster.js.map +0 -1
  437. package/dist/services/team-store.d.ts +0 -54
  438. package/dist/services/team-store.d.ts.map +0 -1
  439. package/dist/services/team-store.js +0 -156
  440. package/dist/services/team-store.js.map +0 -1
  441. package/dist/services/trigger-log-store.d.ts +0 -46
  442. package/dist/services/trigger-log-store.d.ts.map +0 -1
  443. package/dist/services/trigger-log-store.js +0 -132
  444. package/dist/services/trigger-log-store.js.map +0 -1
  445. package/dist/services/trigger-types.d.ts +0 -57
  446. package/dist/services/trigger-types.d.ts.map +0 -1
  447. package/dist/services/trigger-types.js +0 -28
  448. package/dist/services/trigger-types.js.map +0 -1
  449. package/dist/services/webhook-key.d.ts +0 -16
  450. package/dist/services/webhook-key.d.ts.map +0 -1
  451. package/dist/services/webhook-key.js +0 -123
  452. package/dist/services/webhook-key.js.map +0 -1
  453. package/dist/services/webhook-lifecycle-extractors.d.ts +0 -15
  454. package/dist/services/webhook-lifecycle-extractors.d.ts.map +0 -1
  455. package/dist/services/webhook-lifecycle-extractors.js +0 -59
  456. package/dist/services/webhook-lifecycle-extractors.js.map +0 -1
  457. package/dist/services/webhook-lifecycle-store.d.ts +0 -45
  458. package/dist/services/webhook-lifecycle-store.d.ts.map +0 -1
  459. package/dist/services/webhook-lifecycle-store.js +0 -159
  460. package/dist/services/webhook-lifecycle-store.js.map +0 -1
  461. package/dist/utils/daemon-discovery.d.ts +0 -11
  462. package/dist/utils/daemon-discovery.d.ts.map +0 -1
  463. package/dist/utils/daemon-discovery.js +0 -59
  464. package/dist/utils/daemon-discovery.js.map +0 -1
  465. package/dist/workflows/trigger-from-envelope.d.ts +0 -13
  466. package/dist/workflows/trigger-from-envelope.d.ts.map +0 -1
  467. package/dist/workflows/trigger-from-envelope.js +0 -67
  468. package/dist/workflows/trigger-from-envelope.js.map +0 -1
@@ -1,33 +1,33 @@
1
- "use strict";(()=>{var We=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 We;async function pt(){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 ze="botmux.dashboard.locale",wn={"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
1
+ "use strict";(()=>{var Ce=class{sessions=new Map;schedules=new Map;online=!0;listeners=new Set;upsertSessions(t){for(let o of t)this.sessions.set(o.sessionId,o);this.emit()}upsertSchedules(t){for(let o of t)this.schedules.set(o.id,o);this.emit()}applySse(t,o){if(t==="session.spawned")this.sessions.set(o.session.sessionId,o.session);else if(t==="session.update"){let r=this.sessions.get(o.sessionId);r&&this.sessions.set(o.sessionId,{...r,...o.patch})}else if(t==="session.exited"){let r=this.sessions.get(o.sessionId);r&&this.sessions.set(o.sessionId,{...r,status:"closed"})}else if(t==="schedule.created")this.schedules.set(o.schedule.id,o.schedule);else if(t==="schedule.updated"){let r=this.schedules.get(o.id);r&&this.schedules.set(o.id,{...r,...o.patch})}else if(t==="schedule.deleted")this.schedules.delete(o.id);else return;this.emit()}setOnline(t){this.online!==t&&(this.online=t,this.emit())}on(t){return this.listeners.add(t),()=>this.listeners.delete(t)}emit(){for(let t of this.listeners)t()}},O=new Ce;async function Je(){let[e,t]=await Promise.all([fetch("/api/sessions").then(s=>s.json()),fetch("/api/schedules").then(s=>s.json())]);O.upsertSessions(e.sessions??[]),O.upsertSchedules(t.schedules??[]);let o=new EventSource("/events"),r=["session.spawned","session.update","session.exited","schedule.created","schedule.updated","schedule.deleted","schedule.fired","heartbeat"];for(let s of r)o.addEventListener(s,a=>{try{let d=JSON.parse(a.data);O.applySse(s,d.body??d)}catch{}});o.onerror=()=>O.setOnline(!1),o.onopen=()=>O.setOnline(!0)}var Me="botmux.dashboard.locale",Bt={"app.name":"botmux","app.subtitle":"\u98DE\u4E66 AI CLI \u63A7\u5236\u53F0","time.secondsAgo":"{value} \u79D2\u524D","time.minutesAgo":"{value} \u5206\u949F\u524D","time.hoursAgo":"{value} \u5C0F\u65F6\u524D","nav.overview":"\u603B\u89C8","nav.sessions":"\u4F1A\u8BDD","nav.groups":"\u7FA4\u7EC4","nav.schedules":"\u5B9A\u65F6","nav.botDefaults":"\u9ED8\u8BA4 Bot","status.live":"\u5B9E\u65F6\u8FDE\u63A5","status.disconnected":"\u8FDE\u63A5\u65AD\u5F00","status.system":"\u7CFB\u7EDF","status.light":"\u6D45\u8272","status.dark":"\u6697\u9ED1","status.language":"\u8BED\u8A00","status.theme":"\u4E3B\u9898","botOnboarding.add":"\u6DFB\u52A0\u673A\u5668\u4EBA","botOnboarding.title":"\u626B\u7801\u6DFB\u52A0\u673A\u5668\u4EBA","botOnboarding.intro":"\u7528\u98DE\u4E66 App \u626B\u7801\u521B\u5EFA PersonalAgent \u5E94\u7528\uFF0C\u6210\u529F\u540E\u4F1A\u5199\u5165\u672C\u673A bots.json\u3002","botOnboarding.starting":"\u6B63\u5728\u751F\u6210\u4E8C\u7EF4\u7801...","botOnboarding.waiting":"\u8BF7\u7528\u98DE\u4E66 App \u626B\u7801\u786E\u8BA4\u3002","botOnboarding.verifying":"\u626B\u7801\u6210\u529F\uFF0C\u6B63\u5728\u6821\u9A8C\u51ED\u8BC1...","botOnboarding.completed":"\u673A\u5668\u4EBA\u5DF2\u6DFB\u52A0\u3002","botOnboarding.failed":"\u6DFB\u52A0\u5931\u8D25","botOnboarding.openLink":"\u6253\u4E0D\u5F00\u626B\u7801\uFF1F\u5728\u6D4F\u89C8\u5668\u4E2D\u6253\u5F00","botOnboarding.close":"\u5173\u95ED","botOnboarding.restartHint":"\u5DF2\u5199\u5165 bots.json\u3002\u6267\u884C pnpm daemon:restart \u540E\u65B0\u673A\u5668\u4EBA\u751F\u6548\u3002","botOnboarding.qrAlt":"\u98DE\u4E66\u626B\u7801\u6DFB\u52A0\u673A\u5668\u4EBA\u4E8C\u7EF4\u7801","overview.title":"\u63A7\u5236\u53F0\u603B\u89C8","overview.subtitle":"\u8DE8 bot\u3001\u7FA4\u804A\u3001\u4F1A\u8BDD\u548C\u5B9A\u65F6\u4EFB\u52A1\u7684\u5B9E\u65F6\u7BA1\u63A7\u9762\u3002","overview.openSessions":"\u6D3B\u8DC3\u4F1A\u8BDD","overview.workingSessions":"\u5DE5\u4F5C\u4E2D","overview.onlineBots":"\u5728\u7EBF Bot","overview.schedules":"\u5B9A\u65F6\u4EFB\u52A1","overview.groups":"\u7FA4\u804A\u8986\u76D6","overview.enabledSchedules":"\u5DF2\u542F\u7528","overview.total":"\u603B\u8BA1","overview.active":"\u6D3B\u8DC3","overview.daemonRegistry":"daemon \u6CE8\u518C\u8868","overview.chatMatrix":"\u7FA4\u804A\u77E9\u9635","overview.recentSessions":"\u6700\u8FD1\u4F1A\u8BDD","overview.nextSchedules":"\u5373\u5C06\u6267\u884C","overview.noSessions":"\u6682\u65E0\u4F1A\u8BDD\u3002","overview.noSchedules":"\u6682\u65E0\u5B9A\u65F6\u4EFB\u52A1\u3002","sessions.title":"\u4F1A\u8BDD\u63A7\u5236","sessions.subtitle":"\u5B9A\u4F4D\u98DE\u4E66\u8BDD\u9898\u3001\u6253\u5F00 Web Terminal\u3001\u5173\u95ED\u6216\u6062\u590D CLI \u4F1A\u8BDD\u3002","sessions.search":"\u641C\u7D22\u5DE5\u4F5C\u76EE\u5F55 / \u6807\u9898 / ID","sessions.anyStatus":"\u5168\u90E8\u72B6\u6001","sessions.adoptAny":"adopt: \u5168\u90E8","sessions.adoptYes":"adopt: \u662F","sessions.adoptNo":"adopt: \u5426","sessions.activeOnly":"\u4EC5\u6D3B\u8DC3","sessions.closeSelected":"\u5173\u95ED\u9009\u4E2D","sessions.clearSelection":"\u53D6\u6D88\u9009\u62E9","sessions.selectedCount":"\u5DF2\u9009 {count} \u4E2A\u4F1A\u8BDD","sessions.bot":"bot","sessions.cli":"CLI","sessions.status":"\u72B6\u6001","sessions.titleCol":"\u6807\u9898","sessions.workingDir":"\u5DE5\u4F5C\u76EE\u5F55","sessions.created":"\u521B\u5EFA","sessions.last":"\u6700\u8FD1","sessions.adopt":"\u63A5\u5165","sessions.actions":"\u64CD\u4F5C","sessions.details":"\u8BE6\u60C5","sessions.copy":"\u590D\u5236","sessions.copied":"\u5DF2\u590D\u5236","sessions.locate":"\u5B9A\u4F4D\u8BDD\u9898","sessions.locating":"\u53D1\u9001\u4E2D...","sessions.cooldown":"\u51B7\u5374 {seconds}s","sessions.openTerminal":"\u7EC8\u7AEF","sessions.close":"\u5173\u95ED\u4F1A\u8BDD","sessions.resume":"\u6062\u590D\u4F1A\u8BDD","sessions.dismiss":"\u5173\u95ED","sessions.closeConfirm":"\u5173\u95ED\u8FD9\u4E2A\u4F1A\u8BDD\uFF1F","sessions.resumeFailed":"\u6062\u590D\u5931\u8D25","sessions.closeBulkConfirm":"\u5173\u95ED\u9009\u4E2D\u7684 {count} \u4E2A\u4F1A\u8BDD\uFF1F","sessions.empty":"\u6CA1\u6709\u7B26\u5408\u6761\u4EF6\u7684\u4F1A\u8BDD\u3002","groups.title":"\u7FA4\u7EC4\u4E0E Bot","groups.subtitle":"\u67E5\u770B chat x bot \u8986\u76D6\u77E9\u9635\uFF0C\u7BA1\u7406\u62C9\u7FA4\u3001oncall\u3001\u9000\u7FA4\u548C\u89E3\u6563\u3002","groups.search":"\u641C\u7D22\u7FA4\u540D / ID / owner","groups.missingOnly":"\u4EC5\u7F3A bot","groups.refresh":"\u5237\u65B0","groups.create":"\u65B0\u5EFA\u7FA4","groups.chat":"\u7FA4\u804A","groups.actions":"\u64CD\u4F5C","groups.addBots":"\u6DFB\u52A0 bot","groups.manage":"\u7BA1\u7406","groups.empty":"\u6CA1\u6709\u7B26\u5408\u6761\u4EF6\u7684\u7FA4\u804A\u3002","groups.createTitle":"\u65B0\u5EFA\u7FA4\u804A","groups.createHelp":"\u9009\u62E9\u8981\u9080\u8BF7\u7684 bot\u3002dashboard \u4F1A\u81EA\u52A8\u9009\u62E9\u5728\u7EBF daemon \u4F5C\u4E3A\u521B\u5EFA\u8005\u3002","groups.name":"\u7FA4\u540D","groups.namePlaceholder":"\u4F8B\u5982 AI ChangeLog","groups.bindDir":"\u7ED1\u5B9A\u76EE\u5F55","groups.bindDirHelp":"\u65B0\u8BDD\u9898\u76F4\u63A5\u7528\u8BE5\u76EE\u5F55\u542F\u52A8 CLI\uFF0C\u8DF3\u8FC7 repo \u9009\u62E9\u3002","groups.botPicker":"Bot","groups.createSubmit":"\u521B\u5EFA","groups.cancel":"\u53D6\u6D88","groups.successTitle":"\u7FA4\u521B\u5EFA\u6210\u529F","groups.openGroup":"\u6253\u5F00\u65B0\u7FA4","groups.manageTitle":"\u7BA1\u7406 {name}","groups.owner":"\u7FA4\u4E3B","groups.oncall":"Oncall \u6A21\u5F0F","groups.oncallHelp":"\u5F00\u542F\u540E\uFF0C\u7FA4\u5185\u6210\u5458\u53EF @ \u673A\u5668\u4EBA\uFF1B\u65B0\u8BDD\u9898\u76F4\u63A5\u4F7F\u7528\u7ED1\u5B9A\u76EE\u5F55\u3002","groups.leaveTitle":"\u9009\u62E9\u673A\u5668\u4EBA\u9000\u51FA\u7FA4\u804A","groups.leaveSelected":"\u9009\u4E2D\u673A\u5668\u4EBA\u9000\u51FA\u7FA4\u804A","groups.disband":"\u89E3\u6563\u7FA4\u804A","groups.dangerHint":"\u89E3\u6563\u4EC5\u5F53\u5728\u7FA4\u673A\u5668\u4EBA\u662F\u7FA4\u4E3B\u65F6\u624D\u4F1A\u6210\u529F\uFF0C\u5426\u5219\u5EFA\u8BAE\u4F7F\u7528\u9000\u51FA\u7FA4\u804A\u3002","groups.save":"\u4FDD\u5B58","groups.needWorkingDir":"\u8BF7\u586B\u5DE5\u4F5C\u76EE\u5F55","groups.noBotsOnline":"\u6CA1\u6709\u5728\u7EBF bot\u3002\u8BF7\u5148\u91CD\u542F daemon\u3002","schedules.title":"\u5B9A\u65F6\u4EFB\u52A1","schedules.subtitle":"\u8DE8 daemon \u67E5\u770B\u3001\u6682\u505C\u3001\u6062\u590D\u548C\u7ACB\u5373\u89E6\u53D1\u5B9A\u65F6\u4EFB\u52A1\u3002","schedules.search":"\u641C\u7D22\u540D\u79F0 / prompt / \u5DE5\u4F5C\u76EE\u5F55","schedules.anyKind":"\u5168\u90E8\u7C7B\u578B","schedules.enabledOnly":"\u4EC5\u542F\u7528","schedules.name":"\u540D\u79F0","schedules.bot":"bot","schedules.schedule":"\u89C4\u5219","schedules.next":"\u4E0B\u6B21","schedules.last":"\u4E0A\u6B21","schedules.repeat":"\u91CD\u590D","schedules.enabled":"\u542F\u7528","schedules.actions":"\u64CD\u4F5C","schedules.runNow":"\u7ACB\u5373\u8FD0\u884C","schedules.pause":"\u6682\u505C","schedules.resume":"\u6062\u590D","schedules.empty":"\u6682\u65E0\u5B9A\u65F6\u4EFB\u52A1\u3002","botDefaults.title":"Bot \u9ED8\u8BA4 Oncall","botDefaults.subtitle":"\u914D\u7F6E\u6BCF\u4E2A bot \u5728\u65B0\u7FA4\u91CC\u7684\u9ED8\u8BA4 oncall \u884C\u4E3A\u3002","botDefaults.search":"\u641C\u7D22 bot \u540D / app id","botDefaults.refresh":"\u5237\u65B0","botDefaults.warning":"\u5F00\u542F\u540E\uFF0C\u6CA1\u6709 oncall binding \u7684\u7FA4\u4F1A\u5728\u4E0B\u6B21\u5F00\u65B0\u8BDD\u9898\u65F6\u81EA\u52A8\u7ED1\u5B9A\u5230\u8BE5\u76EE\u5F55\uFF1B\u624B\u52A8\u7ED1\u5B9A\u6216\u624B\u52A8\u89E3\u7ED1\u8FC7\u7684\u7FA4\u4E0D\u4F1A\u88AB\u8986\u76D6\u3002","botDefaults.empty":"\u6CA1\u6709\u5728\u7EBF bot\u3002\u5148 botmux restart \u8BA9 daemon \u4E0A\u7EBF\u3002","botDefaults.defaultOncall":"\u9ED8\u8BA4\u8FDB\u5165 oncall \u6A21\u5F0F","botDefaults.defaultOncallHelp":"\u6240\u6709\u672A\u7ED1\u5B9A\u7684\u7FA4\u4E0B\u6B21\u5F00\u8BDD\u9898\u81EA\u52A8\u7ED1\u5B9A","botDefaults.workingDir":"\u9ED8\u8BA4\u5DE5\u4F5C\u76EE\u5F55","botDefaults.lastEnabled":"\u4E0A\u6B21\u542F\u7528\u65F6\u95F4","botDefaults.autobound":"\u5DF2\u81EA\u52A8\u7ED1\u5B9A {count} \u4E2A\u7FA4","botDefaults.save":"\u4FDD\u5B58","botDefaults.required":"\u5F00\u542F\u65F6\u5FC5\u987B\u586B\u5DE5\u4F5C\u76EE\u5F55","nav.roles":"\u89D2\u8272\u7BA1\u7406","roles.title":"\u89D2\u8272\u7BA1\u7406","roles.subtitle":"\u4E3A\u6BCF\u4E2A\u7FA4\u7EC4\u7684\u6BCF\u4E2A Bot \u5355\u72EC\u8BBE\u7F6E\u89D2\u8272\u63D0\u793A\u8BCD\uFF0CBot \u5728\u8BE5\u7FA4\u4E2D\u4F1A\u4EE5\u6B64\u89D2\u8272\u884C\u4E8B\u3002","roles.search":"\u641C\u7D22\u7FA4\u540D/Bot/ID","roles.refresh":"\u5237\u65B0","roles.selectHint":"\u2190 \u5C55\u5F00\u7FA4\u7EC4\uFF0C\u9009\u62E9\u4E00\u4E2A Bot \u6765\u7F16\u8F91\u89D2\u8272","roles.editorPlaceholder":`\u8F93\u5165\u89D2\u8272\u63CF\u8FF0\uFF0C\u4F8B\u5982\uFF1A
2
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"},gn={"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.:
7
+ }`,"catalog.chatId":"\u7FA4\u804A ID","catalog.larkAppId":"\u98DE\u4E66\u5E94\u7528 ID","catalog.chatBindingHint":"\u5FC5\u586B\uFF0C\u7528\u4E8E\u786E\u5B9A humanGate \u5361\u7247\u53D1\u9001\u5230\u54EA\u4E2A\u98DE\u4E66\u7FA4\uFF0C\u4EE5\u53CA\u53D6\u6D88\u8DEF\u7531\u5F52\u5C5E\u3002","catalog.run":"\u8FD0\u884C","catalog.running":"\u542F\u52A8\u4E2D...","catalog.badParamsJson":"\u53C2\u6570\u5FC5\u987B\u662F JSON object\u3002","catalog.writeAccess":"\u9700\u8981\u5199\u6743\u9650\uFF1A\u8BF7\u5728\u7EC8\u7AEF\u8FD0\u884C `botmux dashboard` \u83B7\u53D6\u4E00\u6B21\u6027 URL\uFF0C\u6253\u5F00\u540E\u5199\u5165 cookie\uFF0C\u518D\u56DE\u6765\u8FD0\u884C\u3002","catalog.runHttp":"run HTTP {status}","catalog.runStarted":"\u8FD0\u884C\u5DF2\u542F\u52A8\uFF1B\u6B63\u5728\u6253\u5F00\u8BE6\u60C5\u9875...","catalog.invalidParams":"\u53C2\u6570\u65E0\u6548","catalog.issue":"{path}: {message}","catalog.noParams":"\u6CA1\u6709\u58F0\u660E\u53C2\u6570\u3002","catalog.required":"\u5FC5\u586B","catalog.optional":"\u53EF\u9009","catalog.default":"\u9ED8\u8BA4\u503C","catalog.description":"\u8BF4\u660E","catalog.path":"\u8DEF\u5F84","catalog.revision":"\u4FEE\u8BA2","catalog.nodeCount":"\u8282\u70B9\u6570"},Pt={"app.name":"botmux","app.subtitle":"Feishu AI CLI Control","time.secondsAgo":"{value}s ago","time.minutesAgo":"{value}m ago","time.hoursAgo":"{value}h ago","nav.overview":"Overview","nav.sessions":"Sessions","nav.groups":"Groups","nav.schedules":"Schedules","nav.botDefaults":"Bot Defaults","status.live":"Live","status.disconnected":"Disconnected","status.system":"System","status.light":"Light","status.dark":"Dark","status.language":"Language","status.theme":"Theme","botOnboarding.add":"Add Bot","botOnboarding.title":"Scan to Add Bot","botOnboarding.intro":"Scan with the Feishu app to create a PersonalAgent app. The dashboard writes it to local bots.json after success.","botOnboarding.starting":"Generating QR code...","botOnboarding.waiting":"Scan with the Feishu app to continue.","botOnboarding.verifying":"Scan accepted. Verifying credentials...","botOnboarding.completed":"Bot added.","botOnboarding.failed":"Add failed","botOnboarding.openLink":"Open scan link in browser","botOnboarding.close":"Close","botOnboarding.restartHint":"bots.json has been updated. Run pnpm daemon:restart for the new bot to take effect.","botOnboarding.qrAlt":"Feishu bot onboarding QR code","overview.title":"Control Overview","overview.subtitle":"A realtime control plane across bots, chats, CLI sessions, and schedules.","overview.openSessions":"Active Sessions","overview.workingSessions":"Working","overview.onlineBots":"Online Bots","overview.schedules":"Schedules","overview.groups":"Groups Seen","overview.enabledSchedules":"Enabled","overview.total":"total","overview.active":"active","overview.daemonRegistry":"daemon registry","overview.chatMatrix":"chat matrix","overview.recentSessions":"Recent Sessions","overview.nextSchedules":"Next Runs","overview.noSessions":"No sessions yet.","overview.noSchedules":"No schedules yet.","sessions.title":"Session Control","sessions.subtitle":"Locate Feishu topics, open Web Terminal, close or resume CLI sessions.","sessions.search":"Search working dir / title / IDs","sessions.anyStatus":"Any status","sessions.adoptAny":"adopt: any","sessions.adoptYes":"adopt: yes","sessions.adoptNo":"adopt: no","sessions.activeOnly":"Active only","sessions.closeSelected":"Close selected","sessions.clearSelection":"Clear","sessions.selectedCount":"{count} sessions selected","sessions.bot":"Bot","sessions.cli":"CLI","sessions.status":"Status","sessions.titleCol":"Title","sessions.workingDir":"Working Dir","sessions.created":"Created","sessions.last":"Last","sessions.adopt":"Adopt","sessions.actions":"Actions","sessions.details":"Details","sessions.copy":"Copy","sessions.copied":"Copied","sessions.locate":"Locate Topic","sessions.locating":"Sending...","sessions.cooldown":"Cooldown {seconds}s","sessions.openTerminal":"Terminal","sessions.close":"Close Session","sessions.resume":"Resume Session","sessions.dismiss":"Close","sessions.closeConfirm":"Close this session?","sessions.resumeFailed":"Resume failed","sessions.closeBulkConfirm":"Close {count} selected sessions?","sessions.empty":"No sessions match the filters.","groups.title":"Groups & Bots","groups.subtitle":"Inspect the chat x bot matrix and manage group creation, oncall, leave, and disband flows.","groups.search":"Search chat name / ID / owner","groups.missingOnly":"Missing bot only","groups.refresh":"Refresh","groups.create":"New Group","groups.chat":"Chat","groups.actions":"Actions","groups.addBots":"Add Bots","groups.manage":"Manage","groups.empty":"No chats match the filters.","groups.createTitle":"Create New Group","groups.createHelp":"Pick bots to invite. The dashboard chooses an online daemon as creator.","groups.name":"Group Name","groups.namePlaceholder":"e.g. AI ChangeLog","groups.bindDir":"Bind Directory","groups.bindDirHelp":"New topics start the CLI here and skip repo selection.","groups.botPicker":"Bots","groups.createSubmit":"Create","groups.cancel":"Cancel","groups.successTitle":"Group Created","groups.openGroup":"Open Group","groups.manageTitle":"Manage {name}","groups.owner":"Owner","groups.oncall":"Oncall Mode","groups.oncallHelp":"When enabled, group members can @ the bot; new topics use the bound directory.","groups.leaveTitle":"Select Bots to Leave","groups.leaveSelected":"Selected Bots Leave","groups.disband":"Disband Group","groups.dangerHint":"Disband only works when an in-chat bot is the owner. Otherwise prefer leaving the chat.","groups.save":"Save","groups.needWorkingDir":"Working directory is required","groups.noBotsOnline":"No bots online. Restart the daemon first.","schedules.title":"Schedules","schedules.subtitle":"View, pause, resume, and run scheduled tasks across daemons.","schedules.search":"Search name / prompt / working dir","schedules.anyKind":"Any kind","schedules.enabledOnly":"Enabled only","schedules.name":"Name","schedules.bot":"Bot","schedules.schedule":"Schedule","schedules.next":"Next","schedules.last":"Last","schedules.repeat":"Repeat","schedules.enabled":"Enabled","schedules.actions":"Actions","schedules.runNow":"Run Now","schedules.pause":"Pause","schedules.resume":"Resume","schedules.empty":"No schedules.","botDefaults.title":"Bot Default Oncall","botDefaults.subtitle":"Configure each bot's default oncall behavior in new chats.","botDefaults.search":"Search bot name / app id","botDefaults.refresh":"Refresh","botDefaults.warning":"When enabled, chats without an oncall binding auto-bind to this directory on their next new topic. Manually bound or unbound chats are preserved.","botDefaults.empty":"No bots online. Run botmux restart first.","botDefaults.defaultOncall":"Default to oncall mode","botDefaults.defaultOncallHelp":"Unbound chats auto-bind on the next new topic","botDefaults.workingDir":"Default Working Directory","botDefaults.lastEnabled":"Last Enabled","botDefaults.autobound":"{count} chats auto-bound","botDefaults.save":"Save","botDefaults.required":"Working directory is required when enabled","nav.roles":"Roles","roles.title":"Role Management","roles.subtitle":"Set per-bot role prompts for each group. Each bot adopts its own persona in the selected group.","roles.search":"Search group / bot / ID","roles.refresh":"Refresh","roles.selectHint":"\u2190 Expand a group and select a bot to edit its role","roles.editorPlaceholder":`Enter role description, e.g.:
8
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"},ft={zh:wn,en:gn};function mt(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 hn(e=[]){for(let t of e){let n=mt(t);if(n)return n}return"zh"}function Me(e){return(t,n)=>{let s=ft[e][t]??ft.zh[t]??t;return n?s.replace(/\{(\w+)\}/g,(a,r)=>{let i=n[r];return i==null?`{${r}}`:String(i)}):s}}function wt(e,t){return(e?mt(e.getItem(ze)):null)??hn(t)}var Je="botmux.dashboard.theme";function bn(e){return e==="system"||e==="light"||e==="dark"?e:null}function gt(e,t){return e==="system"?t?"dark":"light":e}function ht(e){return bn(e?.getItem(Je))??"system"}var Ge=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=wt(t?.localStorage,yn()),this.translate=Me(this.locale),this.themeMode=ht(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(ze,t),this.applyLocale(),this.emit())}setThemeMode(t){this.themeMode!==t&&(this.themeMode=t,window.localStorage.setItem(Je,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=gt(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 yn(){return typeof navigator>"u"?[]:navigator.languages?.length?navigator.languages:[navigator.language].filter(Boolean)}var ae=new Ge;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 Ke={chats:[],bots:[]};async function vn(){try{let e=await fetch("/api/groups");if(!e.ok)return;Ke=await e.json()}catch{}}function kn(e){return`status status-${d(e||"unknown")}`}function $n(e){return`<li class="overview-list-row">
13
+ }`,"catalog.chatId":"Chat ID","catalog.larkAppId":"Lark app ID","catalog.chatBindingHint":"Required so humanGate cards and cancel routing know which Lark chat owns the run.","catalog.run":"Run","catalog.running":"Starting...","catalog.badParamsJson":"Params must be a JSON object.","catalog.writeAccess":"write access required: run `botmux dashboard` in the terminal to get a one-time URL, open it once to set the cookie, then come back and run again.","catalog.runHttp":"run HTTP {status}","catalog.runStarted":"Run started; opening detail page...","catalog.invalidParams":"Invalid params","catalog.issue":"{path}: {message}","catalog.noParams":"No params declared.","catalog.required":"required","catalog.optional":"optional","catalog.default":"default","catalog.description":"description","catalog.path":"path","catalog.revision":"revision","catalog.nodeCount":"nodes"},Ge={zh:Bt,en:Pt};function ze(e){if(typeof e!="string")return null;let t=e.trim().toLowerCase();return t==="zh"||t.startsWith("zh-")?"zh":t==="en"||t.startsWith("en-")?"en":null}function Nt(e=[]){for(let t of e){let o=ze(t);if(o)return o}return"zh"}function he(e){return(t,o)=>{let r=Ge[e][t]??Ge.zh[t]??t;return o?r.replace(/\{(\w+)\}/g,(s,a)=>{let d=o[a];return d==null?`{${a}}`:String(d)}):r}}function Ke(e,t){return(e?ze(e.getItem(Me)):null)??Nt(t)}var Ae="botmux.dashboard.theme";function jt(e){return e==="system"||e==="light"||e==="dark"?e:null}function Ve(e,t){return e==="system"?t?"dark":"light":e}function Ye(e){return jt(e?.getItem(Ae))??"system"}var He=class{locale="zh";themeMode="system";resolvedTheme="light";listeners=new Set;translate=he(this.locale);mediaQuery=null;init(){let t=typeof window<"u"?window:void 0;this.locale=Ke(t?.localStorage,Ut()),this.translate=he(this.locale),this.themeMode=Ye(t?.localStorage),this.mediaQuery=t?.matchMedia?.("(prefers-color-scheme: dark)")??null,this.mediaQuery?.addEventListener("change",()=>{this.applyTheme(),this.emit()}),this.applyTheme(),this.applyLocale()}t(t,o){return this.translate(t,o)}setLocale(t){this.locale!==t&&(this.locale=t,this.translate=he(t),window.localStorage.setItem(Me,t),this.applyLocale(),this.emit())}setThemeMode(t){this.themeMode!==t&&(this.themeMode=t,window.localStorage.setItem(Ae,t),this.applyTheme(),this.emit())}on(t){return this.listeners.add(t),()=>this.listeners.delete(t)}emit(){for(let t of this.listeners)t()}applyTheme(){this.resolvedTheme=Ve(this.themeMode,!!this.mediaQuery?.matches),document.documentElement.dataset.theme=this.resolvedTheme,document.documentElement.dataset.themeMode=this.themeMode}applyLocale(){document.documentElement.lang=this.locale==="zh"?"zh-CN":"en"}};function Ut(){return typeof navigator>"u"?[]:navigator.languages?.length?navigator.languages:[navigator.language].filter(Boolean)}var K=new He;function n(e,t){return K.t(e,t)}function m(e){return e.replace(/[&<>"']/g,t=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"})[t])}function re(e){if(!e)return"-";let t=Date.now()-e;return t<6e4?n("common.now"):t<36e5?Math.floor(t/6e4)+"m":t<864e5?Math.floor(t/36e5)+"h":Math.floor(t/864e5)+"d"}var Re={chats:[],bots:[]};async function Ft(){try{let e=await fetch("/api/groups");if(!e.ok)return;Re=await e.json()}catch{}}function Wt(e){return`status status-${m(e||"unknown")}`}function _t(e){return`<li class="overview-list-row">
14
14
  <div>
15
- <strong>${d(e.title??e.sessionId)}</strong>
16
- <span>${d(e.botName??"")} \xB7 ${d(e.cliId??"unknown")}</span>
15
+ <strong>${m(e.title??e.sessionId)}</strong>
16
+ <span>${m(e.botName??"")} \xB7 ${m(e.cliId??"unknown")}</span>
17
17
  </div>
18
- <span class="${kn(e.status)}">${d(e.status??"unknown")}</span>
19
- </li>`}function Sn(e){let t=e.nextRunAt?new Date(e.nextRunAt).toLocaleString():"-";return`<li class="overview-list-row">
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">
20
20
  <div>
21
- <strong>${d(e.name??e.id)}</strong>
22
- <span>${d(e.botName??e.larkAppId??"")} \xB7 ${d(e.parsed?.display??"")}</span>
21
+ <strong>${m(e.name??e.id)}</strong>
22
+ <span>${m(e.botName??e.larkAppId??"")} \xB7 ${m(e.parsed?.display??"")}</span>
23
23
  </div>
24
- <span>${d(t)}</span>
25
- </li>`}async function bt(e){e.innerHTML=`<section class="page hero-page">
24
+ <span>${m(t)}</span>
25
+ </li>`}async function Qe(e){e.innerHTML=`<section class="page hero-page">
26
26
  <div class="page-heading">
27
27
  <div>
28
- <p class="eyebrow">${o("app.subtitle")}</p>
29
- <h1>${o("overview.title")}</h1>
30
- <p>${o("overview.subtitle")}</p>
28
+ <p class="eyebrow">${n("app.subtitle")}</p>
29
+ <h1>${n("overview.title")}</h1>
30
+ <p>${n("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>${o("overview.recentSessions")}</h2>
39
- <p>${o("sessions.subtitle")}</p>
38
+ <h2>${n("overview.recentSessions")}</h2>
39
+ <p>${n("sessions.subtitle")}</p>
40
40
  </div>
41
- <a class="btn-link" href="#/sessions">${o("nav.sessions")}</a>
41
+ <a class="btn-link" href="#/sessions">${n("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>${o("overview.nextSchedules")}</h2>
49
- <p>${o("schedules.subtitle")}</p>
48
+ <h2>${n("overview.nextSchedules")}</h2>
49
+ <p>${n("schedules.subtitle")}</p>
50
50
  </div>
51
- <a class="btn-link" href="#/schedules">${o("nav.schedules")}</a>
51
+ <a class="btn-link" href="#/schedules">${n("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"),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=Ke.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:Ke.chats?.length??0,meta:o("overview.chatMatrix")}];t.innerHTML=g.map(L=>`<article class="metric-card">
57
- <span>${d(L.label)}</span>
56
+ </section>`;let t=e.querySelector("#overview-metrics"),o=e.querySelector("#recent-sessions"),r=e.querySelector("#next-schedules");function s(){let a=[...O.sessions.values()],d=[...O.schedules.values()],f=a.filter(L=>L.status!=="closed"),y=a.filter(L=>L.status==="working"||L.status==="analyzing"||L.status==="starting"),S=d.filter(L=>L.enabled),b=Re.bots?.length||new Set(a.map(L=>L.larkAppId).filter(Boolean)).size,k=[{label:n("overview.openSessions"),value:f.length,meta:`${a.length} ${n("overview.total")}`},{label:n("overview.workingSessions"),value:y.length,meta:`${f.length} ${n("overview.active")}`},{label:n("overview.onlineBots"),value:b,meta:n("overview.daemonRegistry")},{label:n("overview.schedules"),value:d.length,meta:`${S.length} ${n("overview.enabledSchedules")}`},{label:n("overview.groups"),value:Re.chats?.length??0,meta:n("overview.chatMatrix")}];t.innerHTML=k.map(L=>`<article class="metric-card">
57
+ <span>${m(L.label)}</span>
58
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=>$n({...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(Sn).join(""):`<li class="empty">${o("overview.noSchedules")}</li>`}N.on(a),a(),vn().then(a)}function se(e,t){return`<th data-sort="${e}" data-label="${d(t)}">${d(t)}</th>`}var yt=["claude-code","codex","codex-app","cursor","gemini","opencode","mtr","hermes","mira","aiden","coco","unknown"];function In(){return`<div class="filter-check-group" role="group" aria-label="${o("sessions.cli")}">
61
- <span class="filter-check-label">${o("sessions.cli")}</span>
62
- ${yt.map(e=>`
59
+ <small>${m(L.meta)}</small>
60
+ </article>`).join("");let l=a.sort((L,g)=>Number(g.lastMessageAt??0)-Number(L.lastMessageAt??0)).slice(0,6);o.innerHTML=l.length?l.map(L=>_t({...L,title:L.title??`${re(L.lastMessageAt)} \xB7 ${L.sessionId}`})).join(""):`<li class="empty">${n("overview.noSessions")}</li>`;let T=d.filter(L=>L.nextRunAt).sort((L,g)=>Date.parse(L.nextRunAt)-Date.parse(g.nextRunAt)).slice(0,6);r.innerHTML=T.length?T.map(Jt).join(""):`<li class="empty">${n("overview.noSchedules")}</li>`}O.on(s),s(),Ft().then(s)}function V(e,t){return`<th data-sort="${e}" data-label="${m(t)}">${m(t)}</th>`}var Xe=["claude-code","codex","cursor","gemini","opencode","aiden","coco","unknown"];function Gt(){return`<div class="filter-check-group" role="group" aria-label="${n("sessions.cli")}">
61
+ <span class="filter-check-label">${n("sessions.cli")}</span>
62
+ ${Xe.map(e=>`
63
63
  <label class="filter-check">
64
- <input type="checkbox" name="cli" value="${d(e)}" checked>
65
- <span>${d(e)}</span>
64
+ <input type="checkbox" name="cli" value="${m(e)}" checked>
65
+ <span>${m(e)}</span>
66
66
  </label>
67
67
  `).join("")}
68
- </div>`}function Tn(){return`<section class="page">
68
+ </div>`}function zt(){return`<section class="page">
69
69
  <div class="page-heading">
70
70
  <div>
71
- <p class="eyebrow">${o("nav.sessions")}</p>
72
- <h1>${o("sessions.title")}</h1>
73
- <p>${o("sessions.subtitle")}</p>
71
+ <p class="eyebrow">${n("nav.sessions")}</p>
72
+ <h1>${n("sessions.title")}</h1>
73
+ <p>${n("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="${o("sessions.search")}" />
77
+ <input type="search" name="q" placeholder="${n("sessions.search")}" />
78
78
  <select name="status">
79
- <option value="">${o("sessions.anyStatus")}</option>
79
+ <option value="">${n("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="">${o("sessions.adoptAny")}</option>
85
- <option value="yes">${o("sessions.adoptYes")}</option>
86
- <option value="no">${o("sessions.adoptNo")}</option>
84
+ <option value="">${n("sessions.adoptAny")}</option>
85
+ <option value="yes">${n("sessions.adoptYes")}</option>
86
+ <option value="no">${n("sessions.adoptNo")}</option>
87
87
  </select>
88
- <label class="filter-toggle"><input type="checkbox" name="active" checked> ${o("sessions.activeOnly")}</label>
89
- ${In()}
88
+ <label class="filter-toggle"><input type="checkbox" name="active" checked> ${n("sessions.activeOnly")}</label>
89
+ ${Gt()}
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">${o("sessions.closeSelected")}</button>
94
- <button type="button" id="bulk-clear">${o("sessions.clearSelection")}</button>
93
+ <button type="button" id="bulk-close" class="contrast">${n("sessions.closeSelected")}</button>
94
+ <button type="button" id="bulk-clear">${n("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="${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>
98
+ <th><input type="checkbox" id="select-all" title="${n("sessions.activeOnly")}"></th>
99
+ ${V("botName",n("sessions.bot"))}
100
+ ${V("cliId",n("sessions.cli"))}
101
+ ${V("status",n("sessions.status"))}
102
+ ${V("title",n("sessions.titleCol"))}
103
+ ${V("workingDir",n("sessions.workingDir"))}
104
+ ${V("spawnedAt",n("sessions.created"))}
105
+ ${V("lastMessageAt",n("sessions.last"))}
106
+ ${V("adopt",n("sessions.adopt"))}
107
+ <th>${n("sessions.actions")}</th>
108
108
  </tr></thead>
109
109
  <tbody></tbody>
110
110
  </table>
111
111
  <dialog id="drawer"></dialog>
112
- </section>`}function vt(e){e.innerHTML=Tn();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<yt.length,M=u.get("status"),A=u.get("adopt"),P=!!u.get("active"),O=[...N.sessions.values()].filter(x=>!E||h.includes(x.cliId??"unknown")).filter(x=>!M||x.status===M).filter(x=>!A||A==="yes"==!!x.adopt).filter(x=>!P||x.status!=="closed").filter(x=>!w||JSON.stringify(x).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 H(u){let w=u.status==="closed";s.innerHTML=`<article>
112
+ </section>`}function Ze(e){e.innerHTML=zt();let t=e.querySelector("#sessions-table tbody"),o=e.querySelector("#filters"),r=e.querySelector("#drawer"),s=e.querySelector("#select-all"),a=e.querySelector("#bulk-bar"),d=e.querySelector("#bulk-count"),f=e.querySelector("#bulk-close"),y=e.querySelector("#bulk-clear"),S=e.querySelector("#sessions-table"),b=new Set,k="lastMessageAt",l="desc";function T(c){let p=c.status==="closed",h=b.has(c.sessionId)?"checked":"";return`<tr data-id="${m(c.sessionId)}">
113
+ <td><input type="checkbox" class="row-select" ${h} ${p?"disabled":""}></td>
114
+ <td>${m(c.botName??"")}</td>
115
+ <td><span class="badge cli-${m(c.cliId??"unknown")}">${m(c.cliId??"unknown")}</span></td>
116
+ <td><span class="status status-${m(c.status??"unknown")}">${m(c.status??"unknown")}</span></td>
117
+ <td>${m((c.title??"").slice(0,48))}</td>
118
+ <td title="${m(c.workingDir??"")}">${m((c.workingDir??"").slice(-34))}</td>
119
+ <td>${re(c.spawnedAt)}</td>
120
+ <td>${re(c.lastMessageAt)}</td>
121
+ <td>${c.adopt?'<span class="badge">adopt</span>':""}</td>
122
+ <td><button class="open" type="button">${n("sessions.details")}</button></td>
123
+ </tr>`}function L(){let c=new FormData(o),p=(c.get("q")??"").toLowerCase(),h=c.getAll("cli"),E=h.length>0&&h.length<Xe.length,A=c.get("status"),R=c.get("adopt"),q=!!c.get("active"),B=[...O.sessions.values()].filter(D=>!E||h.includes(D.cliId??"unknown")).filter(D=>!A||D.status===A).filter(D=>!R||R==="yes"==!!D.adopt).filter(D=>!q||D.status!=="closed").filter(D=>!p||JSON.stringify(D).toLowerCase().includes(p));return B.sort(u),B}function g(c,p){return p==="spawnedAt"||p==="lastMessageAt"?Number(c[p]??0):p==="adopt"?!!c.adopt:String(c[p]??"").toLowerCase()}function u(c,p){let h=g(c,k),E=g(p,k),A=0;return typeof h=="number"&&typeof E=="number"?A=h-E:typeof h=="boolean"&&typeof E=="boolean"?A=Number(h)-Number(E):A=String(h).localeCompare(String(E)),A===0&&(A=Number(c.lastMessageAt??0)-Number(p.lastMessageAt??0)),l==="asc"?A:-A}function I(){S.querySelectorAll("th[data-sort]").forEach(c=>{let p=c.dataset.sort===k;c.classList.toggle("sorted",p),c.setAttribute("aria-sort",p?l==="asc"?"ascending":"descending":"none");let h=c.dataset.label??c.textContent?.trim()??"";c.textContent=p?`${h} ${l==="asc"?"\u25B2":"\u25BC"}`:h})}function $(c){a.hidden=b.size===0,d.textContent=n("sessions.selectedCount",{count:b.size});let p=c.filter(E=>E.status!=="closed");if(p.length===0){s.checked=!1,s.indeterminate=!1,s.disabled=!0;return}s.disabled=!1;let h=p.filter(E=>b.has(E.sessionId)).length;s.checked=h===p.length,s.indeterminate=h>0&&h<p.length}function v(){let c=L();for(let p of[...b]){let h=O.sessions.get(p);(!h||h.status==="closed")&&b.delete(p)}t.innerHTML=c.length?c.map(T).join(""):`<tr><td colspan="10" class="empty">${n("sessions.empty")}</td></tr>`,I(),$(c)}function M(c){let p=c.status==="closed";r.innerHTML=`<article>
124
124
  <header>
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>
125
+ <h3>${m(c.title??c.sessionId)}</h3>
126
+ <span class="status status-${m(c.status??"unknown")}">${m(c.status??"unknown")}</span>
127
+ <p><code>${m(c.sessionId)}</code> <button data-copy="${m(c.sessionId)}">${n("sessions.copy")}</button></p>
128
128
  </header>
129
- <p><b>${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>
129
+ <p><b>${n("sessions.bot")}:</b> ${m(c.botName??"-")} \xB7 <b>${n("sessions.cli")}:</b> ${m(c.cliId??"?")}</p>
130
+ <p><b>chatId:</b> <code>${m(c.chatId??"")}</code> <button data-copy="${m(c.chatId??"")}">${n("sessions.copy")}</button></p>
131
+ <p><b>rootMessageId:</b> <code>${m(c.rootMessageId??"")}</code> <button data-copy="${m(c.rootMessageId??"")}">${n("sessions.copy")}</button></p>
132
+ ${c.threadId?`<p><b>threadId:</b> <code>${m(c.threadId)}</code></p>`:""}
133
+ <p><b>${n("sessions.workingDir")}:</b> ${m(c.workingDir??"-")}</p>
134
134
  <div class="actions">
135
- <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>`}
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>`}
139
139
  </div>
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 x=setInterval(()=>{O-=1,O<=0?(clearInterval(x),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&&H(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"}),x=await O.json().catch(()=>({}));(!O.ok||x?.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 Ln(){return`<section class="page">
140
+ <form method="dialog"><button>${n("sessions.dismiss")}</button></form>
141
+ </article>`,r.querySelectorAll("[data-copy]").forEach(R=>{R.onclick=()=>{navigator.clipboard.writeText(R.dataset.copy??""),R.textContent=n("sessions.copied"),setTimeout(()=>{R.textContent=n("sessions.copy")},800)}});let h=r.querySelector("#locate-btn");h&&(h.onclick=async()=>{h.disabled=!0,h.textContent=n("sessions.locating");try{let R=await fetch(`/api/sessions/${encodeURIComponent(c.sessionId)}/locate`,{method:"POST"}),q=await R.json();if(q.ok){let B=30;h.textContent=n("sessions.cooldown",{seconds:B});let D=setInterval(()=>{B-=1,B<=0?(clearInterval(D),h.disabled=!1,h.textContent=n("sessions.locate")):h.textContent=n("sessions.cooldown",{seconds:B})},1e3)}else alert(`Locate failed: ${q.error??R.status}`),h.disabled=!1,h.textContent=n("sessions.locate")}catch(R){alert(`Locate error: ${R}`),h.disabled=!1,h.textContent=n("sessions.locate")}});let E=r.querySelector("#resume-btn");E&&(E.onclick=async()=>{E.disabled=!0;try{let R=await fetch(`/api/sessions/${encodeURIComponent(c.sessionId)}/resume`,{method:"POST"}),q=await R.json().catch(()=>({}));if(!R.ok||q.ok===!1){alert(`${n("sessions.resumeFailed")}: ${q?.error??R.status}`),E.disabled=!1;return}r.close()}catch(R){alert(`${n("sessions.resumeFailed")}: ${R}`),E.disabled=!1}});let A=r.querySelector("#close-btn");A&&(A.onclick=async()=>{if(confirm(n("sessions.closeConfirm"))){A.disabled=!0;try{await fetch(`/api/sessions/${encodeURIComponent(c.sessionId)}/close`,{method:"POST"})}finally{r.close()}}}),r.showModal()}t.addEventListener("click",c=>{let p=c.target;if(p.classList.contains("row-select")){let R=p.closest("tr[data-id]");if(!R)return;p.checked?b.add(R.dataset.id):b.delete(R.dataset.id),$(L());return}let h=p.closest("td");if(h&&h.querySelector(".row-select"))return;let E=p.closest("tr[data-id]");if(!E)return;let A=O.sessions.get(E.dataset.id);A&&M(A)}),s.addEventListener("change",()=>{let c=L().filter(p=>p.status!=="closed");for(let p of c)s.checked?b.add(p.sessionId):b.delete(p.sessionId);v()}),y.addEventListener("click",()=>{b.clear(),v()}),f.addEventListener("click",async()=>{let c=[...b];if(c.length===0||!confirm(n("sessions.closeBulkConfirm",{count:c.length})))return;f.disabled=!0,y.disabled=!0;let p=f.textContent,h=0,E=0,A=[...c];f.textContent=`0/${c.length}`;async function R(){for(;A.length;){let q=A.shift();try{let B=await fetch(`/api/sessions/${encodeURIComponent(q)}/close`,{method:"POST"}),D=await B.json().catch(()=>({}));(!B.ok||D?.ok===!1)&&(E+=1)}catch{E+=1}finally{h+=1,f.textContent=`${h}/${c.length}`}}}await Promise.all(Array.from({length:Math.min(6,c.length)},()=>R())),f.textContent=p,f.disabled=!1,y.disabled=!1,b.clear(),v(),E>0&&alert(`Failed: ${E}/${c.length}`)}),S.querySelectorAll("th[data-sort]").forEach(c=>{c.addEventListener("click",()=>{let p=c.dataset.sort;k===p?l=l==="asc"?"desc":"asc":(k=p,l=p==="spawnedAt"||p==="lastMessageAt"?"desc":"asc"),v()})}),o.addEventListener("input",v),O.on(v),v()}function Kt(){return`<section class="page">
142
142
  <div class="page-heading">
143
143
  <div>
144
- <p class="eyebrow">${o("nav.schedules")}</p>
145
- <h1>${o("schedules.title")}</h1>
146
- <p>${o("schedules.subtitle")}</p>
144
+ <p class="eyebrow">${n("nav.schedules")}</p>
145
+ <h1>${n("schedules.title")}</h1>
146
+ <p>${n("schedules.subtitle")}</p>
147
147
  </div>
148
148
  </div>
149
149
  <form id="sched-filters" class="filters">
150
- <input type="search" name="q" placeholder="${o("schedules.search")}" />
150
+ <input type="search" name="q" placeholder="${n("schedules.search")}" />
151
151
  <select name="kind">
152
- <option value="">${o("schedules.anyKind")}</option>
152
+ <option value="">${n("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"> ${o("schedules.enabledOnly")}</label>
157
+ <label><input type="checkbox" name="enabled"> ${n("schedules.enabledOnly")}</label>
158
158
  </form>
159
159
  <table>
160
160
  <thead><tr>
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>
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>
163
163
  </tr></thead>
164
164
  <tbody id="schedules-tbody"></tbody>
165
165
  </table>
166
- </section>`}function kt(e){if(!e)return"\u2014";try{return new Date(e).toLocaleString()}catch{return e}}function $t(e){e.innerHTML=Ln();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>${kt(r.nextRunAt)}</td>
171
- <td>${kt(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>
166
+ </section>`}function et(e){if(!e)return"\u2014";try{return new Date(e).toLocaleString()}catch{return e}}function tt(e){e.innerHTML=Kt();let t=e.querySelector("#schedules-tbody"),o=e.querySelector("#sched-filters");function r(){let a=new FormData(o),d=(a.get("q")??"").toLowerCase(),f=a.get("kind"),y=!!a.get("enabled");return[...O.schedules.values()].filter(S=>!f||S.parsed?.kind===f).filter(S=>!y||S.enabled).filter(S=>!d||JSON.stringify(S).toLowerCase().includes(d)).sort((S,b)=>{if(S.enabled!==b.enabled)return S.enabled?-1:1;let k=S.nextRunAt?Date.parse(S.nextRunAt):1/0,l=b.nextRunAt?Date.parse(b.nextRunAt):1/0;return k-l})}function s(){t.innerHTML=r().map(a=>`<tr data-id="${m(a.id)}">
167
+ <td>${m(a.name??a.id)}</td>
168
+ <td>${m(a.botName??a.larkAppId??"-")}</td>
169
+ <td><code>${m(a.parsed?.display??"?")}</code></td>
170
+ <td>${et(a.nextRunAt)}</td>
171
+ <td>${et(a.lastRunAt)} ${a.lastStatus==="error"?"\u26A0\uFE0F":""}</td>
172
+ <td>${a.repeat?`${a.repeat.completed}/${a.repeat.times??"\u221E"}`:"\u2014"}</td>
173
+ <td>${a.enabled?"\u2713":"\u2717"}</td>
174
174
  <td class="actions-cell">
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>`}
175
+ <button data-op="run" type="button">${n("schedules.runNow")}</button>
176
+ ${a.enabled?`<button data-op="pause" type="button">${n("schedules.pause")}</button>`:`<button data-op="resume" type="button">${n("schedules.resume")}</button>`}
177
177
  </td>
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 En(){return`<section class="page">
178
+ </tr>`).join("")||`<tr><td colspan="8" class="empty">${n("schedules.empty")}</td></tr>`}t.addEventListener("click",async a=>{let d=a.target.closest("button[data-op]");if(!d)return;let f=d.closest("tr[data-id]");if(!f)return;let y=f.dataset.id,S=d.dataset.op;d.disabled=!0;let b=d.textContent;d.textContent="...";try{let k=await fetch(`/api/schedules/${encodeURIComponent(y)}/${S}`,{method:"POST"}),l=await k.json().catch(()=>({}));(!k.ok||l.ok===!1)&&alert(`Failed: ${k.status} ${l?.error??""}`.trim())}catch(k){alert("Network error: "+k)}finally{d.disabled=!1,d.textContent=b}}),o.addEventListener("input",s),O.on(s),s()}var N={chats:[],bots:[]};function Vt(){return`<section class="page">
179
179
  <div class="page-heading">
180
180
  <div>
181
- <p class="eyebrow">${o("nav.groups")}</p>
182
- <h1>${o("groups.title")}</h1>
183
- <p>${o("groups.subtitle")}</p>
181
+ <p class="eyebrow">${n("nav.groups")}</p>
182
+ <h1>${n("groups.title")}</h1>
183
+ <p>${n("groups.subtitle")}</p>
184
184
  </div>
185
185
  </div>
186
186
  <form id="g-filters" class="filters">
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>
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>
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 fe(){W=await(await fetch("/api/groups")).json()}async function Mn(){return(await fetch("/api/groups")).json()}function Cn(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 St(e,t){return e.filter(n=>!t||!t.has(n.larkAppId)).map(n=>`
197
+ </section>`}async function te(){N=await(await fetch("/api/groups")).json()}async function Yt(){return(await fetch("/api/groups")).json()}function Qt(e,t){if(t.size===0)return!0;let o=e?.memberBots??[];for(let r of t)if(!o.some(s=>s.larkAppId===r&&s.inChat))return!1;return!0}function nt(e,t){return e.filter(o=>!t||!t.has(o.larkAppId)).map(o=>`
198
198
  <label class="checkbox-row">
199
- <input type="checkbox" name="bot" value="${d(n.larkAppId)}">
200
- ${d(n.botName??n.larkAppId)} <small>(${d(n.larkAppId)})</small>
199
+ <input type="checkbox" name="bot" value="${m(o.larkAppId)}">
200
+ ${m(o.botName??o.larkAppId)} <small>(${m(o.larkAppId)})</small>
201
201
  </label>
202
- `).join("")}async function It(e){e.innerHTML=En();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=`
202
+ `).join("")}async function ot(e){e.innerHTML=Vt();let t=e.querySelector("#g-head"),o=e.querySelector("#g-body"),r=e.querySelector("#g-filters"),s=e.querySelector("#g-refresh"),a=e.querySelector("#g-drawer");s.onclick=async()=>{s.disabled=!0;try{await te(),b()}finally{s.disabled=!1}};let d=e.querySelector("#g-create");d.onclick=()=>f(),await te();function f(){let l=N.bots;if(l.length===0){alert(n("groups.noBotsOnline"));return}a.innerHTML=`
203
203
  <article>
204
- <header><h3>${o("groups.createTitle")}</h3></header>
205
- <p>${o("groups.createHelp")}</p>
204
+ <header><h3>${n("groups.createTitle")}</h3></header>
205
+ <p>${n("groups.createHelp")}</p>
206
206
  <form id="g-createform">
207
207
  <label class="form-row">
208
- <span>${o("groups.name")}</span>
209
- <input type="text" name="name" placeholder="${o("groups.namePlaceholder")}" maxlength="60">
208
+ <span>${n("groups.name")}</span>
209
+ <input type="text" name="name" placeholder="${n("groups.namePlaceholder")}" maxlength="60">
210
210
  </label>
211
211
  <label class="form-row">
212
- <span>${o("groups.bindDir")}</span>
212
+ <span>${n("groups.bindDir")}</span>
213
213
  <input type="text" name="bindWorkingDir" placeholder="e.g. ~/projects/botmux">
214
- <small>${o("groups.bindDirHelp")}</small>
214
+ <small>${n("groups.bindDirHelp")}</small>
215
215
  </label>
216
216
  <fieldset>
217
- <legend>${o("groups.botPicker")}</legend>
218
- ${St(l)}
217
+ <legend>${n("groups.botPicker")}</legend>
218
+ ${nt(l)}
219
219
  </fieldset>
220
220
  <div class="actions">
221
- <button type="submit" class="primary">${o("groups.createSubmit")}</button>
222
- <button type="button" id="g-create-cancel">${o("groups.cancel")}</button>
221
+ <button type="submit" class="primary">${n("groups.createSubmit")}</button>
222
+ <button type="button" id="g-create-cancel">${n("groups.cancel")}</button>
223
223
  </div>
224
224
  </form>
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 H=b.target.querySelector("button[type=submit]");H&&(H.disabled=!0,H.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 H=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:H};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 Mn()}catch{continue}let H=(y.chats??[]).find(u=>u.chatId===b);if(H&&Cn(H,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,H=l.transferError,u=l.notifyMessageId,w=l.notifyError,h=Array.isArray(l.oncallBindings)?l.oncallBindings:[],E=h.filter(x=>x?.ok).length,M=h.filter(x=>!x?.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(x=>`<br><code>${d(x.larkAppId??"?")}</code>: ${d(x.error??"unknown")}`).join("")}</p>`:"",P;if(T){let x=y?"<br><small>\u7FA4\u4E3B\u5DF2\u4ECE\u673A\u5668\u4EBA\u8F6C\u8BA9\u7ED9\u4F60\u3002</small>":H?`<br><small class="hint-warn-inline">\u26A0 \u81EA\u52A8\u8F6C\u8BA9\u7FA4\u4E3B\u5931\u8D25\uFF08${d(H)}\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${x}${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=`
225
+ </article>`,a.showModal(),a.querySelector("#g-create-cancel").onclick=()=>a.close(),a.querySelector("#g-createform").onsubmit=async g=>{g.preventDefault();let u=new FormData(g.target),I=(u.get("name")??"").trim(),$=(u.get("bindWorkingDir")??"").trim(),v=u.getAll("bot");if(v.length===0){alert("Pick at least one bot.");return}let M=g.target.querySelector("button[type=submit]");M&&(M.disabled=!0,M.textContent="Creating...");try{let c=await fetch("/api/groups/create",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({name:I||void 0,larkAppIds:v,bindWorkingDir:$||void 0})}),p=await c.json();if(p.ok&&p.chatId){y(p);let h=Array.isArray(p.invalidBotIds)?p.invalidBotIds:[],E=v.filter(R=>!h.includes(R)),A=new Set(E);typeof p.creator=="string"&&p.creator&&A.add(p.creator),T(p.chatId,I||p.chatId,E,p.creator),b(),L(p.chatId,A).catch(()=>{})}else alert(`Failed: ${p.error??c.status}`),a.close()}catch(c){alert("Network error: "+c),a.close()}};function T(g,u,I,$){let v=new Set(I);$&&v.add($);let M=N.bots.map(p=>({larkAppId:p.larkAppId,botName:p.botName,inChat:v.has(p.larkAppId),oncallChat:null})),c={chatId:g,name:u,ownerId:$??null,memberBots:M};N.chats=[c,...N.chats.filter(p=>p.chatId!==g)]}async function L(g,u){let I=[600,1200,1200,1200,1200,1200];for(let $ of I){await new Promise(c=>setTimeout(c,$));let v;try{v=await Yt()}catch{continue}let M=(v.chats??[]).find(c=>c.chatId===g);if(M&&Qt(M,u)){N=v,b();return}}}}function y(l){let T=String(l.chatId),L=`https://applink.feishu.cn/client/chat/open?openChatId=${encodeURIComponent(T)}`,g=l.invalidBotIds??[],u=l.invalidUserIds??[],I=l.autoInvitedOpenId,$=!!l.autoInviteRejected,v=l.ownerTransferredTo,M=l.transferError,c=l.notifyMessageId,p=l.notifyError,h=Array.isArray(l.oncallBindings)?l.oncallBindings:[],E=h.filter(D=>D?.ok).length,A=h.filter(D=>!D?.ok),R=h.length>0?A.length===0?`<p class="hint-ok">\u5DF2\u7ED1\u5B9A\u76EE\u5F55\uFF1A<code>${m(l.bindResolvedPath??"")}</code>\uFF08${E}/${h.length} bots\uFF09</p>`:`<p class="hint-warn">\u76EE\u5F55\u7ED1\u5B9A\u90E8\u5206\u5931\u8D25\uFF1A\u6210\u529F ${E}/${h.length}\u3002${A.map(D=>`<br><code>${m(D.larkAppId??"?")}</code>: ${m(D.error??"unknown")}`).join("")}</p>`:"",q;if(I){let D=v?"<br><small>\u7FA4\u4E3B\u5DF2\u4ECE\u673A\u5668\u4EBA\u8F6C\u8BA9\u7ED9\u4F60\u3002</small>":M?`<br><small class="hint-warn-inline">\u26A0 \u81EA\u52A8\u8F6C\u8BA9\u7FA4\u4E3B\u5931\u8D25\uFF08${m(M)}\uFF09\uFF0C\u4F60\u73B0\u5728\u662F\u6210\u5458\u4F46\u7FA4\u4E3B\u4ECD\u662F\u673A\u5668\u4EBA\u3002</small>`:"",Te=c?`<br><small>\u673A\u5668\u4EBA\u5DF2\u5728\u7FA4\u91CC @ \u4E86\u4F60\uFF08\u6D88\u606F id <code>${m(c)}</code>\uFF09\uFF0C\u770B\u98DE\u4E66\u901A\u77E5\u5C31\u80FD\u8FDB\u7FA4\u3002</small>`:p?`<br><small class="hint-warn-inline">\u26A0 \u81EA\u52A8 @ \u901A\u77E5\u5931\u8D25\uFF08${m(p)}\uFF09\uFF0C\u65B0\u7FA4\u53EF\u80FD\u4E0D\u4F1A\u4E3B\u52A8\u51FA\u73B0\u5728\u4F60\u4FA7\u8FB9\u680F\uFF0C\u5EFA\u8BAE\u4ECE\u4E0B\u9762\u6309\u94AE\u8DF3\u8FDB\u53BB\u3002</small>`:"";q=`<p class="hint-ok">\u5DF2\u81EA\u52A8\u9080\u8BF7\u4F60\uFF08<code>${m(I)}</code>\uFF09\u4F5C\u4E3A\u6210\u5458\u3002${D}${Te}</p>`}else $?q='<p class="hint-warn">\u98DE\u4E66\u62D2\u7EDD\u4E86\u81EA\u52A8\u9080\u8BF7\uFF08\u4F60\u7684 open_id \u5728\u521B\u5EFA\u8005 bot \u7684 scope \u4E0B\u4E0D\u53EF\u7528\uFF09\u3002<strong>\u4F60\u76EE\u524D\u4E0D\u662F\u65B0\u7FA4\u6210\u5458</strong>\uFF0C\u9700\u8981\u8BA9\u7FA4\u91CC\u7684\u67D0\u4E2A\u673A\u5668\u4EBA\u624B\u52A8\u628A\u4F60\u52A0\u8FDB\u6765\u3002</p>':q='<p class="hint-warn">\u6CA1\u5728 dashboard \u7F13\u5B58\u91CC\u627E\u5230 ownerOpenId\uFF0C<strong>\u6CA1\u6709\u81EA\u52A8\u9080\u8BF7\u4F60</strong>\u3002\u70B9\u5F00\u4E0B\u9762\u94FE\u63A5\u524D\uFF0C\u5148\u8BA9\u7FA4\u91CC\u4EFB\u4E00\u673A\u5668\u4EBA\u624B\u52A8\u628A\u4F60\u52A0\u8FDB\u53BB\u3002</p>';let B=[g.length?`<li>\u65E0\u6548 bot id: <code>${g.map(m).join(", ")}</code></li>`:"",u.length?`<li>\u65E0\u6548\u7528\u6237 open_id: <code>${u.map(m).join(", ")}</code></li>`:""].filter(Boolean).join("");a.innerHTML=`
226
226
  <article>
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>`:""}
227
+ <header><h3>${n("groups.successTitle")}</h3></header>
228
+ <p><b>chatId:</b> <code>${m(T)}</code> <button type="button" data-copy="${m(T)}">${n("sessions.copy")}</button></p>
229
+ <p><b>\u521B\u5EFA\u8005:</b> <code>${m(l.creator??"?")}</code></p>
230
+ ${q}
231
+ ${R}
232
+ ${B?`<ul>${B}</ul>`:""}
233
233
  <div class="actions">
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>
234
+ <a class="btn-link primary" href="${L}" target="_blank" rel="noopener">${n("groups.openGroup")}</a>
235
+ <button type="button" id="g-create-close">${n("sessions.dismiss")}</button>
236
236
  </div>
237
- </article>`,r.querySelectorAll("[data-copy]").forEach(x=>{x.onclick=()=>{navigator.clipboard.writeText(x.dataset.copy??""),x.textContent=o("sessions.copied"),setTimeout(()=>{x.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)}">
237
+ </article>`,a.querySelectorAll("[data-copy]").forEach(D=>{D.onclick=()=>{navigator.clipboard.writeText(D.dataset.copy??""),D.textContent=n("sessions.copied"),setTimeout(()=>{D.textContent=n("sessions.copy")},800)}}),a.querySelector("#g-create-close").onclick=()=>a.close()}function S(){t.innerHTML=`<tr>
238
+ <th>${n("groups.chat")}</th>
239
+ ${N.bots.map(l=>`<th>${m(l.botName??l.larkAppId)}</th>`).join("")}
240
+ <th>${n("groups.actions")}</th>
241
+ </tr>`}function b(){S();let l=new FormData(r),T=(l.get("q")??"").toLowerCase(),L=!!l.get("missing"),g=N.chats.filter(u=>!T||(u.name??"").toLowerCase().includes(T)||u.chatId.toLowerCase().includes(T)||(u.ownerId??"").toLowerCase().includes(T)).filter(u=>!L||u.memberBots.some(I=>!I.inChat));if(g.length===0){o.innerHTML=`<tr><td colspan="${N.bots.length+2}" class="empty">${n("groups.empty")}</td></tr>`;return}o.innerHTML=g.map(u=>`<tr data-chat="${m(u.chatId)}">
242
242
  <td>
243
- <strong>${d(f.name??f.chatId)}</strong><br>
244
- <small><code>${d(f.chatId)}</code></small>
243
+ <strong>${m(u.name??u.chatId)}</strong><br>
244
+ <small><code>${m(u.chatId)}</code></small>
245
245
  </td>
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("")}
246
+ ${N.bots.map(I=>{let $=u.memberBots.find(c=>c.larkAppId===I.larkAppId),v=$?$.error?"!":$.inChat?"\u2713":"\u2717":"?";return`<td class="${$?$.error?"cell-error":$.inChat?"cell-in":"cell-out":"cell-unknown"}" title="${m($?.error??"")}">${v}</td>`}).join("")}
247
247
  <td>
248
- <button class="add-bots" type="button">${o("groups.addBots")}</button>
249
- <button class="manage-chat" type="button">${o("groups.manage")}</button>
248
+ <button class="add-bots" type="button">${n("groups.addBots")}</button>
249
+ <button class="manage-chat" type="button">${n("groups.manage")}</button>
250
250
  </td>
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=`
251
+ </tr>`).join("")}b(),o.addEventListener("click",async l=>{let T=l.target.closest("button.add-bots");if(!T)return;let g=T.closest("tr[data-chat]").dataset.chat,u=N.chats.find(v=>v.chatId===g);if(!u)return;let I=new Set(u.memberBots.filter(v=>v.inChat).map(v=>v.larkAppId));if(!N.bots.filter(v=>!I.has(v.larkAppId)).length){alert("All configured bots are already in this chat.");return}a.innerHTML=`
252
252
  <article>
253
- <header><h3>${o("groups.addBots")} \xB7 ${d(f.name??f.chatId)}</h3></header>
254
- <p>${o("groups.createHelp")}</p>
253
+ <header><h3>${n("groups.addBots")} \xB7 ${m(u.name??u.chatId)}</h3></header>
254
+ <p>${n("groups.createHelp")}</p>
255
255
  <form id="g-addform">
256
- ${St(W.bots,T)}
256
+ ${nt(N.bots,I)}
257
257
  <div class="actions">
258
- <button type="submit" class="primary">${o("groups.addBots")}</button>
259
- <button type="button" id="g-cancel">${o("groups.cancel")}</button>
258
+ <button type="submit" class="primary">${n("groups.addBots")}</button>
259
+ <button type="button" id="g-cancel">${n("groups.cancel")}</button>
260
260
  </div>
261
261
  </form>
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=`
262
+ </article>`,a.showModal(),a.querySelector("#g-cancel").onclick=()=>a.close(),a.querySelector("#g-addform").onsubmit=async v=>{v.preventDefault();let c=new FormData(v.target).getAll("bot");if(c.length===0){alert("Pick at least one bot.");return}try{let h=await(await fetch(`/api/groups/${encodeURIComponent(g)}/add-bots`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({larkAppIds:c})})).json();if(h.error==="no_proxy_bot")alert("No bot is currently in this chat \u2014 add one manually in Feishu first, then retry.");else if(h.result){let E=h.result.map(A=>`${A.id}: ${A.ok?"OK":`failed (${A.error??"unknown"})`}`).join(`
263
+ `);alert(E),await te(),b()}else alert(`Unexpected response: ${JSON.stringify(h)}`)}catch(p){alert("Network error: "+p)}finally{a.close()}}}),o.addEventListener("click",async l=>{let T=l.target.closest("button.manage-chat");if(!T)return;let g=T.closest("tr[data-chat]").dataset.chat,u=N.chats.find(I=>I.chatId===g);u&&k(u)});function k(l){let T=l.memberBots.filter(g=>g.inChat),L=typeof l.ownerId=="string"?l.ownerId:"";a.innerHTML=`
264
264
  <article>
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>
265
+ <header><h3>${n("groups.manageTitle",{name:l.name??l.chatId})}</h3></header>
266
+ <p><b>chatId:</b> <code>${m(l.chatId)}</code></p>
267
+ <p><b>${n("groups.owner")}:</b> <code>${m(l.ownerId??n("common.unknown"))}</code></p>
268
268
 
269
269
  <fieldset>
270
- <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)}">
270
+ <legend>${n("groups.oncall")}</legend>
271
+ <p><small>${n("groups.oncallHelp")}</small></p>
272
+ ${T.length===0?'<p class="empty">\u6CA1\u6709\u673A\u5668\u4EBA\u5728\u7FA4\u91CC</p>':T.map(g=>{let u=!!g.oncallChat,I=g.oncallChat?.workingDir??"";return`
273
+ <div class="oncall-row" data-bot="${m(g.larkAppId)}">
274
274
  <label class="checkbox-row">
275
- <input type="checkbox" data-action="toggle" ${f?"checked":""}>
276
- <strong>${d(b.botName??b.larkAppId)}</strong>
277
- <small>(${d(b.larkAppId)})</small>
275
+ <input type="checkbox" data-action="toggle" ${u?"checked":""}>
276
+ <strong>${m(g.botName??g.larkAppId)}</strong>
277
+ <small>(${m(g.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="${d(T)}" ${f?"":"disabled"}>
282
- <button type="button" data-action="save">${o("groups.save")}</button>
281
+ value="${m(I)}" ${u?"":"disabled"}>
282
+ <button type="button" data-action="save">${n("groups.save")}</button>
283
283
  <span class="oncall-status" data-status></span>
284
284
  </div>
285
285
  </div>
@@ -287,135 +287,94 @@ effects={effects}, activities={activities}, waits={waits}, cancels={cancels}`,"w
287
287
  </fieldset>
288
288
 
289
289
  <fieldset>
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=>`
290
+ <legend>${n("groups.leaveTitle")}</legend>
291
+ ${T.length===0?'<p class="empty">\u6CA1\u6709\u673A\u5668\u4EBA\u5728\u7FA4\u91CC</p>':T.map(g=>`
292
292
  <label class="checkbox-row">
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>
293
+ <input type="checkbox" name="leave-bot" value="${m(g.larkAppId)}">
294
+ ${m(g.botName??g.larkAppId)}
295
+ <small>${g.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" ${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>
301
+ <button id="g-leave-btn" type="button" ${T.length===0?"disabled":""}>${n("groups.leaveSelected")}</button>
302
+ <button id="g-disband-btn" type="button" class="contrast" ${T.length===0?"disabled":""}>${n("groups.disband")}</button>
303
303
  </div>
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]"),H=b.querySelector("[data-status]");T.addEventListener("change",()=>{S.disabled=!T.checked,T.checked&&S.focus()}),y.addEventListener("click",async()=>{H.textContent="",H.className="oncall-status";let u=T.checked,w=S.value.trim();if(u&&!w){H.textContent=o("groups.needWorkingDir"),H.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){H.textContent=u?`\u2713 \u5DF2\u7ED1\u5B9A \u2192 ${M.resolvedPath??w}`:"\u2713 \u5DF2\u89E3\u7ED1",H.classList.add("hint-ok");try{await fe(),p()}catch{}}else H.textContent=`\u2717 ${M.error??E.status}`,H.classList.add("hint-warn-inline")}catch(h){H.textContent=`\u2717 ${h?.message??h}`,H.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 H=y.closedSessions??[],u=H.filter(E=>!E.ok).length,w=H.length-u,h=H.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 H=y.closedSessions??[],u=H.filter(E=>!E.ok).length,w=H.length-u,h=H.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(`
304
+ <p class="hint-warn"><small>${n("groups.dangerHint")}</small></p>
305
+ <form method="dialog"><button>${n("sessions.dismiss")}</button></form>
306
+ </article>`,a.showModal(),a.querySelectorAll(".oncall-row").forEach(g=>{let u=g.dataset.bot,I=g.querySelector("input[data-action=toggle]"),$=g.querySelector("input[data-input=workingDir]"),v=g.querySelector("button[data-action=save]"),M=g.querySelector("[data-status]");I.addEventListener("change",()=>{$.disabled=!I.checked,I.checked&&$.focus()}),v.addEventListener("click",async()=>{M.textContent="",M.className="oncall-status";let c=I.checked,p=$.value.trim();if(c&&!p){M.textContent=n("groups.needWorkingDir"),M.classList.add("hint-warn-inline");return}v.disabled=!0;try{let h=`/api/groups/${encodeURIComponent(l.chatId)}/oncall/${encodeURIComponent(u)}`,E=c?await fetch(h,{method:"PUT",headers:{"content-type":"application/json"},body:JSON.stringify({workingDir:p})}):await fetch(h,{method:"DELETE"}),A=await E.json().catch(()=>({}));if(E.ok&&A.ok){M.textContent=c?`\u2713 \u5DF2\u7ED1\u5B9A \u2192 ${A.resolvedPath??p}`:"\u2713 \u5DF2\u89E3\u7ED1",M.classList.add("hint-ok");try{await te(),b()}catch{}}else M.textContent=`\u2717 ${A.error??E.status}`,M.classList.add("hint-warn-inline")}catch(h){M.textContent=`\u2717 ${h?.message??h}`,M.classList.add("hint-warn-inline")}finally{v.disabled=!1}})}),a.querySelector("#g-leave-btn").onclick=async()=>{let g=[...a.querySelectorAll("input[name=leave-bot]:checked")].map(u=>u.value);if(g.length===0){alert("\u81F3\u5C11\u9009\u4E00\u4E2A\u673A\u5668\u4EBA");return}if(confirm(`\u786E\u5B9A\u8BA9 ${g.length} \u4E2A\u673A\u5668\u4EBA\u9000\u51FA\u7FA4\u804A\uFF1F\u8BE5 bot \u5728\u6B64\u7FA4\u7684\u4F1A\u8BDD\u4F1A\u4E00\u5E76\u5173\u95ED\u3002`))try{let I=await(await fetch(`/api/groups/${encodeURIComponent(l.chatId)}/leave`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({larkAppIds:g})})).json(),$=(I.result??[]).map(v=>{if(!v.ok)return`${v.larkAppId}: \u5931\u8D25 (${v.error??"unknown"})`;let M=v.closedSessions??[],c=M.filter(E=>!E.ok).length,p=M.length-c,h=M.length===0?"":c===0?`\uFF08\u5173\u95ED ${p} \u4E2A\u4F1A\u8BDD\uFF09`:`\uFF08\u5173\u95ED ${p} \u4E2A\uFF0C${c} \u4E2A\u5931\u8D25\uFF09`;return`${v.larkAppId}: OK${h}`}).join(`
307
+ `);alert($||`Unexpected: ${JSON.stringify(I)}`),await te(),b()}catch(u){alert("Network error: "+u)}finally{a.close()}},a.querySelector("#g-disband-btn").onclick=async()=>{if(T.length===0||!confirm(`\u786E\u5B9A\u89E3\u6563\u7FA4\u804A\u300C${l.name??l.chatId}\u300D\uFF1F\u6B64\u64CD\u4F5C\u4E0D\u53EF\u6062\u590D\uFF0C\u672C\u7FA4\u6240\u6709\u673A\u5668\u4EBA\u4F1A\u8BDD\u4E5F\u4F1A\u4E00\u5E76\u5173\u95ED\u3002`))return;let g=[...T].sort((I,$)=>($.larkAppId===L?1:0)-(I.larkAppId===L?1:0)),u=[];for(let I of g)try{let $=await fetch(`/api/groups/${encodeURIComponent(l.chatId)}/disband`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({larkAppId:I.larkAppId})}),v=await $.json();if(v.ok){let M=v.closedSessions??[],c=M.filter(E=>!E.ok).length,p=M.length-c,h=M.length===0?"":c===0?`
308
+ \u5173\u95ED\u4E86 ${p} \u4E2A\u4F1A\u8BDD\u3002`:`
309
+ \u5173\u95ED\u4E86 ${p} \u4E2A\u4F1A\u8BDD\uFF0C${c} \u4E2A\u4F1A\u8BDD\u5173\u95ED\u5931\u8D25\u3002`;alert(`\u5DF2\u89E3\u6563\uFF08\u7531 ${I.botName??I.larkAppId} \u6267\u884C\uFF09${h}`),await te(),b(),a.close();return}u.push(`${I.botName??I.larkAppId}: ${v.error??$.status}`)}catch($){u.push(`${I.botName??I.larkAppId}: ${$}`)}alert(`\u6240\u6709\u5728\u7FA4\u673A\u5668\u4EBA\u5747\u65E0\u6CD5\u89E3\u6563\uFF1A
310
+ ${u.join(`
311
311
  `)}
312
312
 
313
- \u5EFA\u8BAE\u6539\u7528\u300C\u9000\u51FA\u7FA4\u804A\u300D\u3002`)}}s.addEventListener("input",p)}var re={bots:[]},me=null;function Hn(){return`<section class="page">
313
+ \u5EFA\u8BAE\u6539\u7528\u300C\u9000\u51FA\u7FA4\u804A\u300D\u3002`)}}r.addEventListener("input",b)}var ne={bots:[]},oe=null;function Xt(){return`<section class="page">
314
314
  <div class="page-heading">
315
315
  <div>
316
- <p class="eyebrow">${o("nav.botDefaults")}</p>
317
- <h1>${o("botDefaults.title")}</h1>
318
- <p>${o("botDefaults.subtitle")}</p>
316
+ <p class="eyebrow">${n("nav.botDefaults")}</p>
317
+ <h1>${n("botDefaults.title")}</h1>
318
+ <p>${n("botDefaults.subtitle")}</p>
319
319
  </div>
320
320
  </div>
321
321
  <form id="bd-filters" class="filters">
322
- <input type="search" name="q" placeholder="${o("botDefaults.search")}" />
323
- <button type="button" id="bd-refresh">${o("botDefaults.refresh")}</button>
322
+ <input type="search" name="q" placeholder="${n("botDefaults.search")}" />
323
+ <button type="button" id="bd-refresh">${n("botDefaults.refresh")}</button>
324
324
  </form>
325
+ <p class="hint-warn" style="max-width:760px">
326
+ ${n("botDefaults.warning")}
327
+ </p>
325
328
  <div id="bd-list"></div>
326
- </section>`}async function Tt(){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 Lt(e){if(!e)return"\u2014";let t=new Date(e);return Number.isNaN(t.getTime())?"\u2014":t.toLocaleString()}async function Et(e){e.innerHTML=Hn();let t=e.querySelector("#bd-list"),n=e.querySelector("#bd-filters"),s=e.querySelector("#bd-refresh");s.onclick=async()=>{s.disabled=!0;try{await Tt(),a()}finally{s.disabled=!1}},await Tt();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)}">
329
+ </section>`}async function st(){try{let e=await fetch("/api/bots"),t=await e.json().catch(()=>({}));if(!e.ok){oe=t?.error?`HTTP ${e.status}: ${t.error}${t.path?` (${t.path})`:""}`:`HTTP ${e.status}`,ne={bots:[]};return}if(!t||!Array.isArray(t.bots)){oe="unexpected response shape (no `bots` array)",ne={bots:[]};return}oe=null,ne=t}catch(e){oe=e?.message??String(e),ne={bots:[]}}}function at(e){if(!e)return"\u2014";let t=new Date(e);return Number.isNaN(t.getTime())?"\u2014":t.toLocaleString()}async function rt(e){e.innerHTML=Xt();let t=e.querySelector("#bd-list"),o=e.querySelector("#bd-filters"),r=e.querySelector("#bd-refresh");r.onclick=async()=>{r.disabled=!0;try{await st(),s()}finally{r.disabled=!1}},await st();function s(){let y=(new FormData(o).get("q")??"").toLowerCase(),S=ne.bots.filter(b=>!y||(b.botName??"").toLowerCase().includes(y)||(b.larkAppId??"").toLowerCase().includes(y));if(oe){t.innerHTML=`<p class="hint-warn">\u65E0\u6CD5\u52A0\u8F7D bot \u5217\u8868\uFF1A${m(oe)}<br>\u5E38\u89C1\u539F\u56E0\uFF1Adashboard / daemon \u8FDB\u7A0B\u8FD8\u5728\u8DD1\u65E7\u4EE3\u7801\uFF0C\u6267\u884C <code>botmux restart</code> \u540E\u5237\u65B0\u3002</p>`;return}if(S.length===0){t.innerHTML=`<p class="empty">${n("botDefaults.empty")}</p>`;return}t.innerHTML=S.map(a).join(""),d()}function a(f){if(f.error)return`<article class="bd-card" data-appid="${m(f.larkAppId)}">
330
+ <header><strong>${m(f.botName??f.larkAppId)}</strong>
331
+ <small>${m(f.larkAppId)}</small></header>
332
+ <p class="hint-warn-inline">\u67E5\u8BE2\u5931\u8D25\uFF1A${m(f.error)}</p>
333
+ </article>`;let y=f.defaultOncall??{enabled:!1,workingDir:"",since:0},S=!!y.enabled;return`<article class="bd-card" data-appid="${m(f.larkAppId)}">
331
334
  <header>
332
- <strong>${d(p.botName??p.larkAppId)}</strong>
333
- <small>${d(p.larkAppId)}</small>
335
+ <strong>${m(f.botName??f.larkAppId)}</strong>
336
+ <small>${m(f.larkAppId)}</small>
334
337
  </header>
335
338
  <div class="bd-body">
336
- <section class="bd-section">
337
- <h3 class="bd-section-title">${o("botDefaults.sectionOncall")}</h3>
338
- <label class="checkbox-row">
339
- <input type="checkbox" data-action="toggle" ${l?"checked":""}>
340
- <strong>${o("botDefaults.defaultOncall")}</strong>
341
- <small>${o("botDefaults.defaultOncallHelp")}</small>
342
- </label>
343
- <div class="bd-row">
344
- <label>
345
- <span>${o("botDefaults.workingDir")}</span>
346
- <input type="text" data-input="workingDir" placeholder="e.g. /root/iserver/botmux"
347
- value="${d(g.workingDir??"")}" ${l?"":"disabled"}>
348
- </label>
349
- </div>
350
- <p class="bd-section-note">${o("botDefaults.warning")}</p>
351
- <div class="bd-meta">
352
- <small>${o("botDefaults.lastEnabled")}: ${d(Lt(g.since??0))}</small>
353
- <small>${o("botDefaults.autobound",{count:p.autoboundChatCount??0})}</small>
354
- </div>
355
- <div class="actions">
356
- <button type="button" data-action="save">${o("botDefaults.save")}</button>
357
- <span class="oncall-status" data-status></span>
358
- </div>
359
- </section>
360
- ${m(p)}
361
- ${k(p)}
362
- </div>
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
- <div class="bd-row bd-brand">
366
- <label>
367
- <span>${o("botDefaults.brandLabel")}</span>
368
- <input type="text" data-input="brandLabel"
369
- placeholder="${d(o("botDefaults.brandLabelPlaceholder"))}"
370
- value="${d(g??"")}">
339
+ <label class="checkbox-row">
340
+ <input type="checkbox" data-action="toggle" ${S?"checked":""}>
341
+ <strong>${n("botDefaults.defaultOncall")}</strong>
342
+ <small>${n("botDefaults.defaultOncallHelp")}</small>
371
343
  </label>
372
- <small data-brand-state>${d(i(g))}</small>
373
- <small>${o("botDefaults.brandLabelHelp")}</small>
344
+ <div class="bd-row">
345
+ <label>
346
+ <span>${n("botDefaults.workingDir")}</span>
347
+ <input type="text" data-input="workingDir" placeholder="e.g. /root/iserver/botmux"
348
+ value="${m(y.workingDir??"")}" ${S?"":"disabled"}>
349
+ </label>
350
+ </div>
351
+ <div class="bd-meta">
352
+ <small>${n("botDefaults.lastEnabled")}: ${m(at(y.since??0))}</small>
353
+ <small>${n("botDefaults.autobound",{count:f.autoboundChatCount??0})}</small>
354
+ </div>
374
355
  <div class="actions">
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
- <span class="oncall-status" data-brand-status></span>
356
+ <button type="button" data-action="save">${n("botDefaults.save")}</button>
357
+ <span class="oncall-status" data-status></span>
378
358
  </div>
379
359
  </div>
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
- <label class="checkbox-row">
383
- <input type="checkbox" data-action="toggle-disable-streaming" ${g?"checked":""}>
384
- <strong>${o("botDefaults.disableStreaming")}</strong>
385
- <small>${o("botDefaults.disableStreamingHelp")}</small>
386
- </label>
387
- <label class="checkbox-row">
388
- <input type="checkbox" data-action="toggle-writable-link" ${l?"checked":""} ${g?"disabled":""}>
389
- <strong>${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>
396
- </label>
397
- <div class="actions">
398
- <small data-card-pref-moot class="hint-warn-inline" ${g?"":"hidden"}>${o("botDefaults.writableLinkMoot")}</small>
399
- <span class="oncall-status" data-card-pref-status></span>
400
- </div>
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,x=I.value.trim();if(O&&!x){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:x})}),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${Lt(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]"),H=p.querySelector("[data-brand-state]");async function u(O,x){if(y){y.textContent="",y.className="oncall-status",x.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??""),H&&(H.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{x.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,x){if(M){M.textContent="",M.className="oncall-status",x.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{x===h?x.disabled=!!w?.checked:x.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 Ve=4096,He=[],Q=null,X=null,J="",we=new Set;function xn(){return`<section class="page roles-page">
360
+ </article>`}function d(){t.querySelectorAll(".bd-card").forEach(f=>{let y=f.dataset.appid,S=f.querySelector("input[data-action=toggle]"),b=f.querySelector("input[data-input=workingDir]"),k=f.querySelector("button[data-action=save]"),l=f.querySelector("[data-status]");!S||!b||!k||!l||(S.addEventListener("change",()=>{b.disabled=!S.checked,S.checked&&b.focus()}),k.addEventListener("click",async()=>{l.textContent="",l.className="oncall-status";let T=S.checked,L=b.value.trim();if(T&&!L){l.textContent=n("botDefaults.required"),l.classList.add("hint-warn-inline");return}k.disabled=!0;try{let g=await fetch(`/api/bots/${encodeURIComponent(y)}/default-oncall`,{method:"PUT",headers:{"content-type":"application/json"},body:JSON.stringify({enabled:T,workingDir:L})}),u=await g.json().catch(()=>({}));if(g.ok&&u.ok){let I=u.resolvedPath?` \u2192 ${u.resolvedPath}`:"";l.textContent=T?`\u2713 \u5DF2\u5F00\u542F${I}\uFF08\u672A\u7ED1\u5B9A\u7684\u7FA4\u4E0B\u6B21\u5F00\u8BDD\u9898\u81EA\u52A8 oncall\uFF09`:"\u2713 \u5DF2\u5173\u95ED\uFF08\u5DF2\u7ED1\u5B9A\u7684\u7FA4\u4E0D\u52A8\uFF09",l.classList.add("hint-ok");let $=ne.bots.find(M=>M.larkAppId===y);$&&u.defaultOncall&&($.defaultOncall=u.defaultOncall);let v=f.querySelector(".bd-meta small:first-child");v&&u.defaultOncall?.since!=null&&(v.textContent=`\u4E0A\u6B21\u542F\u7528\u65F6\u95F4\uFF1A${at(u.defaultOncall.since)}`)}else l.textContent=`\u2717 ${u.error??g.status}`,l.classList.add("hint-warn-inline")}catch(g){l.textContent=`\u2717 ${g?.message??g}`,l.classList.add("hint-warn-inline")}finally{k.disabled=!1}}))})}s(),o.addEventListener("input",s)}var xe=4096,ve=[],W=null,_=null,j="",se=new Set;function Zt(){return`<section class="page roles-page">
402
361
  <div class="page-heading">
403
362
  <div>
404
- <p class="eyebrow">${o("nav.roles")}</p>
405
- <h1>${o("roles.title")}</h1>
406
- <p>${o("roles.subtitle")}</p>
363
+ <p class="eyebrow">${n("nav.roles")}</p>
364
+ <h1>${n("roles.title")}</h1>
365
+ <p>${n("roles.subtitle")}</p>
407
366
  </div>
408
367
  </div>
409
368
  <div class="roles-layout">
410
369
  <div class="roles-tree-panel">
411
370
  <div class="roles-tree-header">
412
- <input type="search" id="roles-search" placeholder="${o("roles.search")}" />
413
- <button type="button" id="roles-refresh">${o("roles.refresh")}</button>
371
+ <input type="search" id="roles-search" placeholder="${n("roles.search")}" />
372
+ <button type="button" id="roles-refresh">${n("roles.refresh")}</button>
414
373
  </div>
415
374
  <div id="roles-tree" class="roles-tree"></div>
416
375
  </div>
417
376
  <div class="roles-editor-panel">
418
- <div id="roles-editor-empty" class="roles-editor-empty">${o("roles.selectHint")}</div>
377
+ <div id="roles-editor-empty" class="roles-editor-empty">${n("roles.selectHint")}</div>
419
378
  <div id="roles-editor-form" class="roles-editor-form" style="display:none">
420
379
  <div class="roles-editor-breadcrumb">
421
380
  <span id="roles-editor-group-name"></span>
@@ -425,205 +384,82 @@ ${f.join(`
425
384
  <div class="roles-editor-meta">
426
385
  <span id="roles-editor-chat-id" class="roles-editor-meta-line"></span>
427
386
  </div>
428
- <textarea id="roles-editor-textarea" placeholder="${o("roles.editorPlaceholder")}" rows="14"></textarea>
387
+ <textarea id="roles-editor-textarea" placeholder="${n("roles.editorPlaceholder")}" rows="14"></textarea>
429
388
  <div class="roles-editor-footer">
430
389
  <span id="roles-editor-bytecount" class="roles-bytecount"></span>
431
390
  <div class="roles-editor-actions">
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>
391
+ <button type="button" id="roles-delete" class="danger">${n("roles.delete")}</button>
392
+ <button type="button" id="roles-save" class="primary">${n("roles.save")}</button>
434
393
  </div>
435
394
  </div>
436
395
  <div id="roles-preview" class="roles-preview"></div>
437
396
  </div>
438
397
  </div>
439
398
  </div>
440
- </section>`}async function Ce(){He=((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 Ct(e,t){return(await fetch(`/api/roles/${encodeURIComponent(e)}/${encodeURIComponent(t)}`)).json()}async function An(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 Rn(e,t){return(await fetch(`/api/roles/${encodeURIComponent(e)}/${encodeURIComponent(t)}`,{method:"DELETE"})).ok}function Ht(e){return e.memberBots.filter(t=>t.inChat&&t.hasRole).length}function Dn(e){return e.memberBots.filter(t=>t.inChat).length}function de(e=""){let t=document.getElementById("roles-tree");if(!t)return;let n=e.toLowerCase(),s=He.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=Ht(a),$=Dn(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)}">
399
+ </section>`}async function be(){ve=((await(await fetch("/api/groups")).json()).chats??[]).map(o=>({chatId:o.chatId,name:o.name??o.chatId,memberBots:(o.memberBots??[]).map(r=>({larkAppId:r.larkAppId,botName:r.botName??r.larkAppId,inChat:r.inChat??!1,hasRole:r.hasRole??!1,oncallChat:r.oncallChat??null}))}))}async function lt(e,t){return(await fetch(`/api/roles/${encodeURIComponent(e)}/${encodeURIComponent(t)}`)).json()}async function en(e,t,o){return(await fetch(`/api/roles/${encodeURIComponent(e)}/${encodeURIComponent(t)}`,{method:"PUT",headers:{"content-type":"application/json"},body:JSON.stringify({content:o})})).ok}async function tn(e,t){return(await fetch(`/api/roles/${encodeURIComponent(e)}/${encodeURIComponent(t)}`,{method:"DELETE"})).ok}function dt(e){return e.memberBots.filter(t=>t.inChat&&t.hasRole).length}function nn(e){return e.memberBots.filter(t=>t.inChat).length}function Z(e=""){let t=document.getElementById("roles-tree");if(!t)return;let o=e.toLowerCase(),r=ve.filter(s=>{if(!o)return!0;let a=s.chatId.toLowerCase().includes(o)||(s.name??"").toLowerCase().includes(o),d=s.memberBots.some(f=>f.larkAppId.toLowerCase().includes(o)||(f.botName??"").toLowerCase().includes(o));return a||d});if(r.length===0){t.innerHTML=`<div class="roles-empty">${n("roles.noChats")}</div>`;return}t.innerHTML=r.map(s=>{let a=se.has(s.chatId),d=s.memberBots.filter(k=>k.inChat),f=a?"\u25BE":"\u25B8",y=dt(s),S=nn(s),b=a?d.map(k=>`
400
+ <div class="roles-bot-row ${W===s.chatId&&_===k.larkAppId?"selected":""}"
401
+ data-group-id="${m(s.chatId)}"
402
+ data-bot-id="${m(k.larkAppId)}">
444
403
  <span class="roles-bot-indent"></span>
445
404
  <span class="roles-bot-icon">\u{1F916}</span>
446
405
  <div class="roles-bot-info">
447
- <div class="roles-bot-name">${d(g.botName)}</div>
448
- <div class="roles-bot-id">${d(g.larkAppId)}</div>
406
+ <div class="roles-bot-name">${m(k.botName)}</div>
407
+ <div class="roles-bot-id">${m(k.larkAppId)}</div>
449
408
  </div>
450
- <span class="roles-badge ${g.hasRole?"has-role":"no-role"}">
451
- ${g.hasRole?o("roles.configured"):o("roles.unconfigured")}
409
+ <span class="roles-badge ${k.hasRole?"has-role":"no-role"}">
410
+ ${k.hasRole?n("roles.configured"):n("roles.unconfigured")}
452
411
  </span>
453
412
  </div>`).join(""):"";return`
454
413
  <div class="roles-group-section">
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>
414
+ <div class="roles-group-row ${a?"expanded":""} ${W===s.chatId&&!_?"selected":""}"
415
+ data-group-id="${m(s.chatId)}">
416
+ <span class="roles-group-arrow">${f}</span>
458
417
  <span class="roles-group-icon">\u{1F4C1}</span>
459
418
  <div class="roles-group-info">
460
- <div class="roles-group-name">${d(a.name??a.chatId)}</div>
419
+ <div class="roles-group-name">${m(s.name??s.chatId)}</div>
461
420
  <div class="roles-group-meta">
462
- ${k}/${$} ${o("roles.botsWithRoles")}
421
+ ${y}/${S} ${n("roles.botsWithRoles")}
463
422
  </div>
464
423
  </div>
465
424
  <span class="roles-group-chevron"></span>
466
425
  </div>
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),de(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&&Bn(i,m)})})}async function Bn(e,t){Q=e,X=t;let n=await Ct(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 $=He.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()),Ye(),Qe(),de(document.getElementById("roles-search")?.value??"");let g=document.getElementById("roles-delete");g&&(g.style.display=n.hasRole?"":"none")}function Ye(){let e=document.getElementById("roles-editor-bytecount");if(!e)return;let t=new TextEncoder().encode(J).length;e.textContent=`${t} / ${Ve} bytes`,e.className=`roles-bytecount ${t>3800?"warn":""} ${t>Ve?"over":""}`,On(t)}function On(e){let t=document.getElementById("roles-save");if(!t)return;let n=e??new TextEncoder().encode(J).length;t.disabled=n>Ve||J.trim().length===0}function Qe(){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 Mt(){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(),Mt(),await Ce();for(let t of He)Ht(t)>0&&we.add(t.chatId);de(),document.getElementById("roles-search")?.addEventListener("input",t=>{de(t.target.value)}),document.getElementById("roles-refresh")?.addEventListener("click",async()=>{if(await Ce(),de(document.getElementById("roles-search")?.value??""),Q&&X){let t=await Ct(X,Q),n=document.getElementById("roles-editor-textarea");n&&(n.value=t.content??""),J=t.content??"",Ye(),Qe();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 An(X,Q,J)){await Ce(),de(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 Rn(X,Q)&&(await Ce(),Mt(),de(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,Ye(),Qe()})}async function Re(e){let t=await fetch(e);return{status:t.status,body:await t.json().catch(()=>({}))}}async function De(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 ue=(e,t)=>De("POST",e,t),At=(e,t)=>De("PUT",e,t),Xe=[],Rt=[],Dt="",xe="",Ze=new Map,Ae=new Map,ye=new Set,ve=new Set;function B(e){return document.getElementById(e)}function Be(){return[...Xe,...Rt]}function ce(e){let t=Ze.get(e);return t||(t=new Set,Ze.set(e,t)),t}function qn(e){return Be().find(t=>t.key===e)}function Bt(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 Pn(){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
- ${Bt("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 Nn(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 jn(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===Dt,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=>ce(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=ce(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)}" value="${d(Ae.get(e.key)||"")}" placeholder="\u7FA4\u540D\uFF08\u5982\uFF1A\u8DE8\u56E2\u961F\u6392\u969C\uFF09" style="min-width:200px"><button class="tf-grp primary" data-tk="${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 ie(){let e=B("tf-teams"),t=Be();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(Nn);m.forEach(g=>s.add(g.larkAppId)),i.bots.forEach(g=>a.add(g.larkAppId));let k=new Set(m.map(g=>g.larkAppId));[...ce(i.key)].forEach(g=>{k.has(g)||ce(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>':jn(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}`,Un()}function Un(){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),ie()}}),e.querySelectorAll(".tf-dep-h").forEach(t=>{t.onclick=()=>{let n=t.dataset.dk;ve.has(n)?ve.delete(n):ve.add(n),ie()}}),e.querySelectorAll(".tf-pick").forEach(t=>{t.onchange=()=>{let n=ce(t.dataset.tk);t.checked?n.add(t.dataset.app):n.delete(t.dataset.app)}}),e.querySelectorAll(".tf-gname").forEach(t=>{t.oninput=()=>{Ae.set(t.dataset.tk,t.value)}}),e.querySelectorAll(".tf-cap").forEach(t=>{t.onchange=async()=>{let n=t.dataset.app,s=t.value;await At("/api/team/local-bots/"+encodeURIComponent(n)+"/capability",{capability:s}),Be().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=()=>_n(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 De("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=qn(n);if(!s)return;let a=[...ce(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 ue("/api/team/federated-group",{name:i,larkAppIds:a,teamId:s.teamId}):await ue("/api/team/remote-group",{hubUrl:s.hubUrl,teamId:s.teamId,name:i,larkAppIds:a});if(Fn(r,m.body,m.status),m.body?.ok){ce(n).clear(),Ae.delete(n);let k=r.innerHTML,$=()=>{let p=e.querySelector(`.tf-gout[data-tk="${CSS.escape(n)}"]`);p&&(p.innerHTML=k)};s.kind==="local"?ge().then($):(ie(),$())}}})}function Fn(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 _n(e,t){let n=await Re("/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 Ot(){let e=Array.from(new Set(Be().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 Re("/api/team/hosted")).body;if(!t?.ok){Xe=[],ie();return}Dt=t.deployment.deploymentId,xe=t.suggestedHubUrl||"",B("tf-owner").textContent=t.deployment.ownerName||(t.deployment.ownerUnionId?"\u5DF2\u7ED1\u5B9A":"\u672A\u7ED1\u5B9A"),Xe=(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||[]})),Ot(),ie()}async function Wn(){Rt=((await Re("/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||[]}}),Ot(),ie()}function qt(e){e.innerHTML=Pn(),Ze.clear(),Ae.clear(),ye.clear(),ve.clear(),["tf-search","tf-cli","tf-fcap","tf-frole"].forEach(t=>{let n=B(t);n.oninput=ie,n.onchange=ie}),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 At("/api/team/local-bots/"+encodeURIComponent(t)+"/role",{role:B("tf-modal-text").value}),B("tf-modal").style.display="none",ge()},Jn(),ge(),Wn()}function zn(){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
- ${Bt("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 et(){let t=(await Re("/api/team/hosted")).body,n=B("tm-list");xe=t?.suggestedHubUrl||xe;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 ue("/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(xe)}</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 De("DELETE","/api/team/hosted/"+encodeURIComponent(a.dataset.team)),et())}})}function Pt(e){e.innerHTML=zn(),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 ue("/api/team/hosted",{name:t});s.body?.ok?(n.innerHTML='<span class="ok">\u5DF2\u521B\u5EFA</span>',B("tm-newname").value="",et()):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 ue("/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>`}},et()}function Jn(){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 ue("/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 ue("/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 Nt(e){let t=await fetch(e);return{status:t.status,body:await t.json().catch(()=>({}))}}async function nt(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 ot=[];function Gn(){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 tt(){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 jt(e){return`${location.origin}/webhook/${encodeURIComponent(e)}`}function Kn(e){return e==="fixed"?"\u56FA\u5B9A\u7FA4":e==="new-group"?"\u6BCF\u6B21\u65B0\u5EFA\u7FA4":"\u8BF7\u6C42\u6307\u5B9A\u7FA4"}function Vn(e){return e==="workflow"?"\u5DE5\u4F5C\u6D41":"\u5355\u8F6E"}function Yn(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=ot.find(r=>r.larkAppId===n.target.botId),a=jt(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 ${Vn(n.target.kind)} \xB7 ${Kn(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 nt("PATCH","/api/connectors/"+encodeURIComponent(n.dataset.id),{enabled:n.dataset.on!=="true"}),Oe()}}),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 nt("DELETE","/api/connectors/"+encodeURIComponent(n.dataset.id)),Oe())}})}async function Oe(){let[e,t]=await Promise.all([Nt("/api/bots"),Nt("/api/connectors")]);ot=(e.body?.bots||[]).map(a=>({larkAppId:a.larkAppId,botName:a.botName||a.larkAppId}));let n=_("cn-bot"),s=n.value;n.innerHTML=ot.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),Yn(t.body?.connectors||[])}function Ut(e){e.innerHTML=Gn(),_("cn-kind").onchange=tt,_("cn-mode").onchange=tt,tt(),_("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 nt("POST","/api/connectors",i);if(k.status===201&&k.body?.ok){t.innerHTML="";let $=_("cn-created");$.style.display="";let p=k.body.webhookUrl||jt(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=""}),Oe()}else{let $=k.body?.error||k.status;t.innerHTML=`<span class="err">\u521B\u5EFA\u5931\u8D25\uFF1A${d(String($))}</span>`}},Oe()}function Qn(){let e=[["",o("workflow.filter.nonTerminal")],["all",o("workflow.filter.all")],["pending",le("pending")],["running",le("running")],["waiting",le("waiting")],["succeeded",le("succeeded")],["failed",le("failed")],["cancelled",le("cancelled")]];return`
426
+ <div class="roles-bot-list">${b}</div>
427
+ </div>`}).join(""),t.querySelectorAll(".roles-group-row").forEach(s=>{s.addEventListener("click",()=>{let a=s.dataset.groupId;a&&(se.has(a)?se.delete(a):se.add(a),Z(document.getElementById("roles-search")?.value??""))})}),t.querySelectorAll(".roles-bot-row").forEach(s=>{s.addEventListener("click",a=>{a.stopPropagation();let d=s.dataset.groupId,f=s.dataset.botId;d&&f&&on(d,f)})})}async function on(e,t){W=e,_=t;let o=await lt(t,e),r=document.getElementById("roles-editor-empty"),s=document.getElementById("roles-editor-form"),a=document.getElementById("roles-editor-textarea"),d=document.getElementById("roles-editor-group-name"),f=document.getElementById("roles-editor-bot-name"),y=document.getElementById("roles-editor-chat-id");r&&(r.style.display="none"),s&&(s.style.display="");let S=ve.find(l=>l.chatId===e),b=S?.memberBots.find(l=>l.larkAppId===t);d&&(d.textContent=S?.name??e),f&&(f.textContent=b?.botName??t),y&&(y.textContent=`${e} \xB7 ${t}`),j=o.content??"",a&&(a.value=j,a.focus()),De(),Oe(),Z(document.getElementById("roles-search")?.value??"");let k=document.getElementById("roles-delete");k&&(k.style.display=o.hasRole?"":"none")}function De(){let e=document.getElementById("roles-editor-bytecount");if(!e)return;let t=new TextEncoder().encode(j).length;e.textContent=`${t} / ${xe} bytes`,e.className=`roles-bytecount ${t>3800?"warn":""} ${t>xe?"over":""}`,sn(t)}function sn(e){let t=document.getElementById("roles-save");if(!t)return;let o=e??new TextEncoder().encode(j).length;t.disabled=o>xe||j.trim().length===0}function Oe(){let e=document.getElementById("roles-preview");e&&(j.trim()?e.innerHTML=`<strong>${n("roles.preview")}</strong><pre>${m(j)}</pre>`:e.innerHTML=`<small>${n("roles.previewEmpty")}</small>`)}function it(){W=null,_=null,j="";let e=document.getElementById("roles-editor-empty"),t=document.getElementById("roles-editor-form"),o=document.getElementById("roles-editor-textarea"),r=document.getElementById("roles-delete");e&&(e.style.display=""),t&&(t.style.display="none"),o&&(o.value=""),r&&(r.style.display="none")}async function ct(e){e.innerHTML=Zt(),se.clear(),it(),await be();for(let t of ve)dt(t)>0&&se.add(t.chatId);Z(),document.getElementById("roles-search")?.addEventListener("input",t=>{Z(t.target.value)}),document.getElementById("roles-refresh")?.addEventListener("click",async()=>{if(await be(),Z(document.getElementById("roles-search")?.value??""),W&&_){let t=await lt(_,W),o=document.getElementById("roles-editor-textarea");o&&(o.value=t.content??""),j=t.content??"",De(),Oe();let r=document.getElementById("roles-delete");r&&(r.style.display=t.hasRole?"":"none")}}),document.getElementById("roles-save")?.addEventListener("click",async function(){if(!(!W||!_)){this.disabled=!0,this.textContent="...";try{if(await en(_,W,j)){await be(),Z(document.getElementById("roles-search")?.value??"");let o=document.getElementById("roles-delete");o&&(o.style.display="");let r=document.createElement("span");r.className="roles-saved-flash",r.textContent=` ${n("roles.saved")}`,document.querySelector(".roles-editor-footer")?.appendChild(r),setTimeout(()=>r.remove(),2e3)}else{let o=document.createElement("span");o.className="roles-saved-flash roles-save-error",o.textContent=j.trim().length===0?` ${n("roles.emptyError")}`:` ${n("roles.saveFailed")}`,document.querySelector(".roles-editor-footer")?.appendChild(o),setTimeout(()=>o.remove(),3e3)}}finally{this.disabled=!1,this.textContent=n("roles.save")}}}),document.getElementById("roles-delete")?.addEventListener("click",async function(){if(!(!W||!_)&&confirm(n("roles.confirmDelete"))){this.disabled=!0,this.textContent="...";try{await tn(_,W)&&(await be(),it(),Z(document.getElementById("roles-search")?.value??""))}finally{this.disabled=!1,this.textContent=n("roles.delete")}}}),document.getElementById("roles-editor-textarea")?.addEventListener("input",t=>{j=t.target.value,De(),Oe()})}function an(){let e=[["",n("workflow.filter.nonTerminal")],["all",n("workflow.filter.all")],["pending",Y("pending")],["running",Y("running")],["waiting",Y("waiting")],["succeeded",Y("succeeded")],["failed",Y("failed")],["cancelled",Y("cancelled")]];return`
592
428
  <nav class="wf-subnav">
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>
429
+ <a href="#/workflows" class="active" data-i18n="workflow.subnav.runs">${i(n("workflow.subnav.runs"))}</a>
430
+ <a href="#/workflows/catalog" data-i18n="workflow.subnav.catalog">${i(n("workflow.subnav.catalog"))}</a>
595
431
  </nav>
596
432
  <form id="wf-filters" class="filters">
597
- <input type="search" name="q" placeholder="${c(o("workflow.searchPlaceholder"))}" />
433
+ <input type="search" name="q" placeholder="${i(n("workflow.searchPlaceholder"))}" />
598
434
  <select name="status">
599
- ${e.map(([t,n])=>`<option value="${c(t)}">${c(n)}</option>`).join("")}
435
+ ${e.map(([t,o])=>`<option value="${i(t)}">${i(o)}</option>`).join("")}
600
436
  </select>
601
437
  <span id="wf-last-load" class="muted"></span>
602
438
  </form>
603
439
  <table>
604
440
  <thead><tr>
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>
441
+ <th>${i(n("workflow.table.run"))}</th><th>${i(n("workflow.table.workflow"))}</th><th>${i(n("workflow.table.status"))}</th>
442
+ <th>${i(n("workflow.table.lastSeq"))}</th><th>${i(n("workflow.table.dangling"))}</th><th>${i(n("workflow.table.updated"))}</th>
443
+ <th>${i(n("workflow.table.chatApp"))}</th>
608
444
  </tr></thead>
609
445
  <tbody id="wf-tbody"></tbody>
610
446
  </table>
611
- `}var Xn=5e3,Zn=2e3,ke=new Set(["succeeded","failed","cancelled"]);function c(e){return e.replace(/[&<>"']/g,t=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"})[t])}function eo(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(le(e))}</span>`}function le(e){let t=`workflow.status.${e}`,n=o(t);return n===t?e:n}function zt(e){let t=location.hash.match(/^#\/workflows\/([^?#]+)(?:\?([^#]*))?$/);if(t){let n=new URLSearchParams(t[2]??"");return no(e,decodeURIComponent(t[1]),{focusAttemptId:n.get("attempt")??void 0})}return to(e)}function to(e){e.innerHTML=Qn();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 H=y.length>0?y.join("<br/>"):"\u2014",u=ao(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())}">${eo(f.updatedAt)}</td>
618
- <td>${H}</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()},Xn)}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 no(e,t,n={}){e.innerHTML=`
447
+ `}var rn=5e3,ln=2e3,ie=new Set(["succeeded","failed","cancelled"]);function i(e){return e.replace(/[&<>"']/g,t=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"})[t])}function dn(e){let t=new Date(e),r=Date.now()-e;return r<6e4?n("time.secondsAgo",{value:Math.max(1,Math.floor(r/1e3))}):r<36e5?n("time.minutesAgo",{value:Math.floor(r/6e4)}):r<864e5?n("time.hoursAgo",{value:Math.floor(r/36e5)}):t.toISOString().slice(0,19).replace("T"," ")}function G(e){return`<span class="${ie.has(e)?"wf-status terminal":"wf-status live"} wf-status-${i(e)}">${i(Y(e))}</span>`}function Y(e){let t=`workflow.status.${e}`,o=n(t);return o===t?e:o}function mt(e){let t=location.hash.match(/^#\/workflows\/([^?#]+)(?:\?([^#]*))?$/);if(t){let o=new URLSearchParams(t[2]??"");return un(e,decodeURIComponent(t[1]),{focusAttemptId:o.get("attempt")??void 0})}return cn(e)}function cn(e){e.innerHTML=an();let t=e.querySelector("#wf-tbody"),o=e.querySelector("#wf-filters"),r=e.querySelector("#wf-last-load"),s=[],a=null,d=!1,f=null,y=!1;function S(g){let I=(new FormData(o).get("q")??"").trim().toLowerCase();return I?g.filter($=>$.runId.toLowerCase().includes(I)||$.workflowId.toLowerCase().includes(I)||($.chatId??"").toLowerCase().includes(I)):g}function b(){let g=S(s);if(g.length===0){t.innerHTML=`<tr><td colspan="7" class="empty">${f?i(n("workflow.list.failedLoad",{error:f})):s.length===0?i(n("workflow.list.noRuns")):i(n("workflow.list.noFilterMatch"))}</td></tr>`;return}t.innerHTML=g.map(u=>{let I=`${u.dEf}/${u.dAct}/${u.dWait}`,$=u.dEf+u.dAct+u.dWait>0?"wf-dangling has":"wf-dangling none",v=[];u.chatId&&v.push(i(u.chatId)),u.larkAppId&&v.push(`<span class="muted">${i(u.larkAppId)}</span>`);let M=v.length>0?v.join("<br/>"):"\u2014",c=pn(u);return`<tr data-runid="${i(u.runId)}">
448
+ <td><a href="#/workflows/${encodeURIComponent(u.runId)}"><code>${i(u.runId)}</code></a></td>
449
+ <td>${i(u.workflowId)}</td>
450
+ <td>${G(u.status)}${u.failedNodeId?` <span class="muted">(${i(u.failedNodeId)})</span>`:""}${c}</td>
451
+ <td>${u.lastSeq}</td>
452
+ <td class="${$}">${I}</td>
453
+ <td title="${i(new Date(u.updatedAt).toISOString())}">${dn(u.updatedAt)}</td>
454
+ <td>${M}</td>
455
+ </tr>`}).join("")}function k(){f?(r.textContent=n("workflow.list.error",{error:f}),r.classList.add("error")):(r.textContent=n("workflow.list.loaded",{count:s.length,time:new Date().toLocaleTimeString()}),r.classList.remove("error"))}async function l(){if(!(y||d)&&!document.hidden){d=!0;try{let g=o.elements.namedItem("status")?.value??"",u=new URLSearchParams;g==="all"?u.set("all","1"):g&&u.set("status",g);let I="/api/workflows/runs"+(u.toString()?`?${u}`:""),$=await fetch(I);$.ok?(s=(await $.json()).runs??[],f=null):(f=`HTTP ${$.status}`,s=[])}catch(g){f=g?.message??String(g),s=[]}finally{d=!1,y||(b(),k())}}}function T(){a!==null&&window.clearTimeout(a),a=window.setTimeout(async()=>{await l(),y||T()},rn)}function L(){document.hidden||l()}return o.addEventListener("input",()=>{b()}),o.addEventListener("change",g=>{g.target.getAttribute("name")==="status"&&l()}),document.addEventListener("visibilitychange",L),l().then(()=>{y||T()}),()=>{y=!0,a!==null&&window.clearTimeout(a),document.removeEventListener("visibilitychange",L)}}function un(e,t,o={}){e.innerHTML=`
620
456
  <div class="wf-detail-head">
621
- <a class="btn-link" href="#/workflows">${c(o("workflow.detail.back"))}</a>
457
+ <a class="btn-link" href="#/workflows">${i(n("workflow.detail.back"))}</a>
622
458
  <div>
623
- <h2><code>${c(t)}</code></h2>
624
- <div id="wf-detail-subtitle" class="muted">${c(o("workflow.detail.loading"))}</div>
459
+ <h2><code>${i(t)}</code></h2>
460
+ <div id="wf-detail-subtitle" class="muted">${i(n("workflow.detail.loading"))}</div>
625
461
  </div>
626
- <button id="wf-cancel-run" type="button" class="contrast" hidden>${c(o("workflow.detail.cancel"))}</button>
462
+ <button id="wf-cancel-run" type="button" class="contrast" hidden>${i(n("workflow.detail.cancel"))}</button>
627
463
  <span id="wf-detail-refresh" class="muted"></span>
628
464
  </div>
629
465
  <section id="wf-detail-error" class="hint-warn" hidden></section>
@@ -632,20 +468,20 @@ ${Bt("manage")}
632
468
  <section id="wf-dangling-panel"></section>
633
469
  <section class="wf-panel">
634
470
  <div class="wf-panel-title">
635
- <h3>${c(o("workflow.detail.parallel"))}</h3>
471
+ <h3>${i(n("workflow.detail.parallel"))}</h3>
636
472
  <span id="wf-parallel-meta" class="muted"></span>
637
473
  </div>
638
474
  <div id="wf-parallel-view"></div>
639
475
  </section>
640
476
  <section class="wf-panel">
641
477
  <div class="wf-panel-title">
642
- <h3>${c(o("workflow.detail.nodes"))}</h3>
478
+ <h3>${i(n("workflow.detail.nodes"))}</h3>
643
479
  </div>
644
480
  <div class="wf-table-scroll">
645
481
  <table>
646
482
  <thead><tr>
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>
483
+ <th>${i(n("workflow.detail.node"))}</th><th>${i(n("workflow.detail.nodeStatus"))}</th><th>${i(n("workflow.detail.activity"))}</th><th>${i(n("workflow.detail.activityStatus"))}</th>
484
+ <th>${i(n("workflow.detail.attempts"))}</th><th>${i(n("workflow.detail.current"))}</th><th>${i(n("workflow.detail.detail"))}</th>
649
485
  </tr></thead>
650
486
  <tbody id="wf-node-tbody"></tbody>
651
487
  </table>
@@ -653,237 +489,237 @@ ${Bt("manage")}
653
489
  </section>
654
490
  <section class="wf-panel">
655
491
  <div class="wf-panel-title">
656
- <h3>${c(o("workflow.detail.nodeIO"))}</h3>
492
+ <h3>${i(n("workflow.detail.nodeIO"))}</h3>
657
493
  </div>
658
494
  <div id="wf-io-list" class="wf-io-list"></div>
659
495
  </section>
660
496
  <section class="wf-panel">
661
497
  <div class="wf-panel-title">
662
- <h3>${c(o("workflow.detail.timeline"))}</h3>
663
- <button id="wf-load-older" type="button" hidden>${c(o("workflow.detail.loadOlder"))}</button>
498
+ <h3>${i(n("workflow.detail.timeline"))}</h3>
499
+ <button id="wf-load-older" type="button" hidden>${i(n("workflow.detail.loadOlder"))}</button>
664
500
  </div>
665
501
  <div class="wf-table-scroll wf-timeline-scroll">
666
502
  <table>
667
503
  <thead><tr>
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>
504
+ <th>${i(n("workflow.detail.seq"))}</th><th>${i(n("workflow.detail.event"))}</th><th>${i(n("workflow.detail.actor"))}</th><th>${i(n("workflow.detail.node"))}</th><th>${i(n("workflow.detail.activity"))}</th><th>${i(n("workflow.detail.error"))}</th><th>${i(n("workflow.detail.time"))}</th>
669
505
  </tr></thead>
670
506
  <tbody id="wf-event-tbody"></tbody>
671
507
  </table>
672
508
  </div>
673
509
  <div id="wf-event-meta" class="muted"></div>
674
510
  </section>
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=[],H=new Set,u=null,w=null,h=!1,E=0,M=null,A=!1,P=!1,O=!1,x=new Set,q=new Map,j=new Map,F=new Map,z=new Set,ee=new Map,te=new Set,he=new Map,ln=new Map,it=0,lt=n.focusAttemptId;function G(v){if(!v){r.hidden=!0,r.textContent="";return}r.hidden=!1,r.textContent=v}function dt(v){if(!v){i.hidden=!0,i.textContent="";return}i.hidden=!1,i.textContent=v}async function ct(){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=>H.has(R.eventId)?!1:(H.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 dn(){await ct();let v=await Te(new URLSearchParams({tail:"100"}));y=[],H=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 ct(),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 cn(){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 un(){if(!S||ke.has(S.run.status)||O)return;if(!S.chatBinding?.larkAppId){G(o("workflow.detail.cancelUnavailable",{runId:t}));return}let v=so(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}));dt(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 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`,{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 fn(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 mn(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 _e=U==="approve"?o("workflow.detail.approved"):o("workflow.detail.rejected");F.set(v,{kind:"ok",text:oe.alreadyTerminal?o("workflow.detail.alreadyTerminal",{label:_e}):oe.pending?o("workflow.detail.workflowContinue",{label:_e}):o("workflow.detail.workflowRefreshing",{label:_e})}),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;it=I.scrollTop;let v=S.run;ke.has(v.status)&&dt(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}),oo(m,S),ro(k,S),io($,p,S,y),mo(g,S),wo(l,S,x,q,{comments:j,statuses:F,resolving:z,onResolve:mn},{sessions:ee,pending:te,errors:he,onStart:pn,onEnd:fn},lt,ln)&&(lt=void 0),Uo(L,y),I.scrollTop=it,T.hidden=!h,b.textContent=o("workflow.detail.eventsLoaded",{loaded:y.length,total:E})}function Fe(){if(M!==null&&window.clearTimeout(M),S&&ke.has(S.run.status)){M=null;return}M=window.setTimeout(async()=>{await Ee(),A||Fe()},Zn)}function ut(){document.hidden||Ee().then(()=>{!A&&M===null&&Fe()})}return T.addEventListener("click",()=>{cn()}),f.addEventListener("click",()=>{un()}),document.addEventListener("visibilitychange",ut),dn().then(()=>{G(null),A||Fe()}).catch(v=>{G(v?.message??String(v)),s.textContent=o("workflow.detail.loadFailed")}),()=>{A=!0,M!==null&&window.clearTimeout(M),document.removeEventListener("visibilitychange",ut)}}function oo(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(Ne(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 ao(e){if(!e.errorCode)return"";let t=e.errorMessage?` \u2014 ${zo(e.errorMessage,96)}`:"";return`<div class="wf-run-error">
676
- <span class="muted error">${c(e.errorCode)}</span>${c(t)}
677
- </div>`}function so(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 ro(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>
511
+ `;let r=e.querySelector("#wf-detail-subtitle"),s=e.querySelector("#wf-detail-refresh"),a=e.querySelector("#wf-detail-error"),d=e.querySelector("#wf-cancel-status"),f=e.querySelector("#wf-summary"),y=e.querySelector("#wf-dangling-panel"),S=e.querySelector("#wf-parallel-view"),b=e.querySelector("#wf-parallel-meta"),k=e.querySelector("#wf-node-tbody"),l=e.querySelector("#wf-io-list"),T=e.querySelector(".wf-timeline-scroll"),L=e.querySelector("#wf-event-tbody"),g=e.querySelector("#wf-event-meta"),u=e.querySelector("#wf-cancel-run"),I=e.querySelector("#wf-load-older"),$=null,v=[],M=new Set,c=null,p=null,h=!1,E=0,A=null,R=!1,q=!1,B=!1,D=new Set,Te=new Map,Ne=new Map,ue=new Map,fe=new Set,pe=new Map,X=new Set,ae=new Map,At=new Map,je=0,Ue=o.focusAttemptId;function U(w){if(!w){a.hidden=!0,a.textContent="";return}a.hidden=!1,a.textContent=w}function Fe(w){if(!w){d.hidden=!0,d.textContent="";return}d.hidden=!1,d.textContent=w}async function We(){let w=await fetch(`/api/workflows/runs/${encodeURIComponent(t)}/snapshot`);if(w.status===404)throw new Error(n("workflow.detail.unknownRun"));if(!w.ok)throw new Error(n("workflow.detail.snapshotHttp",{status:w.status}));$=await w.json()}async function me(w){let P=await fetch(`/api/workflows/runs/${encodeURIComponent(t)}/events?${w}`);if(P.status===404)throw new Error(n("workflow.detail.unknownRun"));if(!P.ok)throw new Error(n("workflow.detail.eventsHttp",{status:P.status}));return await P.json()}function we(w,P){let x=w.filter(H=>M.has(H.eventId)?!1:(M.add(H.eventId),!0));x.length!==0&&(v=P==="prepend"?[...x,...v]:[...v,...x],v.sort((H,z)=>le(H.eventId)-le(z.eventId)))}async function Ht(){await We();let w=await me(new URLSearchParams({tail:"100"}));v=[],M=new Set,we(w.events,"append"),c=w.oldestSeq,p=w.newestSeq,h=w.hasOlder,E=w.totalCount,F()}async function ge(){if(!(R||q||document.hidden)){q=!0;try{if(await We(),p!==null){let w=await me(new URLSearchParams({afterSeq:String(p),limit:"200"}));we(w.events,"append"),w.newestSeq!==null&&(p=w.newestSeq),c===null&&w.oldestSeq!==null&&(c=w.oldestSeq),E=w.totalCount}else{let w=await me(new URLSearchParams({tail:"1"}));we(w.events,"append"),c=w.oldestSeq,p=w.newestSeq,h=w.hasOlder,E=w.totalCount}U(null),F()}catch(w){U(w?.message??String(w))}finally{q=!1}}}async function Rt(){if(!(c===null||!h)){I.disabled=!0;try{let w=await me(new URLSearchParams({beforeSeq:String(c),limit:"100"}));we(w.events,"prepend"),w.oldestSeq!==null&&(c=w.oldestSeq),h=w.hasOlder,E=w.totalCount,U(null),F()}catch(w){U(w?.message??String(w))}finally{I.disabled=!1}}}async function xt(){if(!$||ie.has($.run.status)||B)return;if(!$.chatBinding?.larkAppId){U(n("workflow.detail.cancelUnavailable",{runId:t}));return}let w=mn($),P=n("workflow.detail.cancelConfirm",{runId:t,...w});if(window.confirm(P)){B=!0,u.disabled=!0;try{let x=await fetch(`/api/workflows/runs/${encodeURIComponent(t)}/cancel`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({reason:"cancelled via dashboard"})});if(x.status===401)throw new Error(n("workflow.detail.writeAccessCancel"));let H=await x.json().catch(()=>({}));if(!x.ok||!H.ok)throw new Error(H.hint??H.error??n("workflow.detail.cancelHttp",{status:x.status}));Fe(H.pending?n("workflow.detail.cancelPending"):null),U(null),await ge()}catch(x){U(x?.message??String(x))}finally{B=!1,u.disabled=!1,F()}}}async function Dt(w,P){if(!X.has(w)){X.add(w),ae.delete(w),F();try{let x=await fetch(`/api/workflows/runs/${encodeURIComponent(t)}/attempts/${encodeURIComponent(P)}/${encodeURIComponent(w)}/resume`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({})});if(x.status===401)throw new Error(n("workflow.detail.writeAccessResume"));let H=await x.json().catch(()=>({}));if(!x.ok||!H.ok||!H.resumeId||!H.url)throw new Error(H.hint??H.message??H.error??n("workflow.detail.resumeStartFailed",{status:x.status}));pe.set(w,{resumeId:H.resumeId,url:H.url})}catch(x){let H=x?.message??String(x);ae.set(w,H)}finally{X.delete(w),F()}}}async function Ot(w,P){if(!X.has(w)){X.add(w),ae.delete(w),F();try{let x=await fetch(`/api/workflows/runs/${encodeURIComponent(t)}/attempts/${encodeURIComponent(P)}/${encodeURIComponent(w)}/resume/end`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({reason:"ended_by_dashboard"})});if(x.status===401)throw new Error(n("workflow.detail.writeAccessResume"));let H=await x.json().catch(()=>({}));if(!x.ok||!H.ok)if(H.error==="resume_not_running")pe.delete(w);else throw new Error(H.hint??H.message??H.error??n("workflow.detail.resumeEndFailed",{status:x.status}));else pe.delete(w)}catch(x){let H=x?.message??String(x);ae.set(w,H)}finally{X.delete(w),F()}}}async function qt(w,P){if(!fe.has(w)){fe.add(w),ue.delete(w),F();try{let x=Ne.get(w)?.trim()||void 0,H=await fetch(`/api/workflows/runs/${encodeURIComponent(t)}/${P}`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({comment:x})});if(H.status===401)throw new Error(n("workflow.detail.writeAccessApproval"));let z=await H.json().catch(()=>({}));if(!H.ok||!z.ok)throw new Error(z.hint??z.message??z.error??n("workflow.detail.actionHttp",{action:P,status:H.status}));let Ee=P==="approve"?n("workflow.detail.approved"):n("workflow.detail.rejected");ue.set(w,{kind:"ok",text:z.alreadyTerminal?n("workflow.detail.alreadyTerminal",{label:Ee}):z.pending?n("workflow.detail.workflowContinue",{label:Ee}):n("workflow.detail.workflowRefreshing",{label:Ee})}),U(null),await ge()}catch(x){let H=x?.message??String(x);ue.set(w,{kind:"error",text:H}),U(H)}finally{fe.delete(w),F()}}}function F(){if(!$)return;je=T.scrollTop;let w=$.run;ie.has(w.status)&&Fe(null),r.innerHTML=`${i(w.workflowId??"?")} \xB7 ${G(w.status)} \xB7 lastSeq ${$.lastSeq}`,s.textContent=n("workflow.detail.refreshed",{time:new Date().toLocaleTimeString()}),u.hidden=ie.has(w.status),u.disabled=B||!$.chatBinding?.larkAppId,u.textContent=$.chatBinding?.larkAppId?n("workflow.detail.cancel"):n("workflow.detail.cliCancelOnly"),u.title=$.chatBinding?.larkAppId?n("workflow.detail.cancelTitle"):n("workflow.detail.cliCancelTitle",{runId:t}),fn(f,$),wn(y,$),gn(S,b,$,v),$n(k,$),Sn(l,$,D,Te,{comments:Ne,statuses:ue,resolving:fe,onResolve:qt},{sessions:pe,pending:X,errors:ae,onStart:Dt,onEnd:Ot},Ue,At)&&(Ue=void 0),Vn(L,v),T.scrollTop=je,I.hidden=!h,g.textContent=n("workflow.detail.eventsLoaded",{loaded:v.length,total:E})}function Le(){if(A!==null&&window.clearTimeout(A),$&&ie.has($.run.status)){A=null;return}A=window.setTimeout(async()=>{await ge(),R||Le()},ln)}function _e(){document.hidden||ge().then(()=>{!R&&A===null&&Le()})}return I.addEventListener("click",()=>{Rt()}),u.addEventListener("click",()=>{xt()}),document.addEventListener("visibilitychange",_e),Ht().then(()=>{U(null),R||Le()}).catch(w=>{U(w?.message??String(w)),r.textContent=n("workflow.detail.loadFailed")}),()=>{R=!0,A!==null&&window.clearTimeout(A),document.removeEventListener("visibilitychange",_e)}}function fn(e,t){let o=t.run,r=[[n("workflow.summary.workflow"),i(o.workflowId??"?")],[n("workflow.summary.status"),G(o.status)],[n("workflow.summary.lastSeq"),String(t.lastSeq)],[n("workflow.summary.updated"),i(new Date(t.updatedAt).toLocaleString())],[n("workflow.summary.revision"),i($e(o.revisionId))],[n("workflow.summary.initiator"),i(o.initiator??"-")]];o.failedNodeId&&r.push([n("workflow.summary.failedNode"),i(o.failedNodeId)]),o.cancelOriginEventId&&r.push([n("workflow.summary.cancelOrigin"),i(o.cancelOriginEventId)]),t.chatBinding&&(r.push([n("workflow.summary.chat"),`<code>${i(t.chatBinding.chatId)}</code>`]),r.push([n("workflow.summary.app"),`<code>${i(t.chatBinding.larkAppId)}</code>`])),e.innerHTML=r.map(([s,a])=>`<div class="wf-summary-item"><span>${s}</span><strong>${a}</strong></div>`).join("")}function pn(e){if(!e.errorCode)return"";let t=e.errorMessage?` \u2014 ${Zn(e.errorMessage,96)}`:"";return`<div class="wf-run-error">
512
+ <span class="muted error">${i(e.errorCode)}</span>${i(t)}
513
+ </div>`}function mn(e){let t=e.dangling;return{total:new Set([...t.activities,...t.effectAttempted,...t.waits,...t.cancels]).size,effects:t.effectAttempted.length,activities:t.activities.length,waits:t.waits.length,cancels:t.cancels.length}}function wn(e,t){let o=t.dangling,r=[[n("workflow.dangling.activities"),o.activities],[n("workflow.dangling.effects"),o.effectAttempted],[n("workflow.dangling.waits"),o.waits],[n("workflow.dangling.cancels"),o.cancels]],s=new Set(r.flatMap(([,a])=>a)).size;if(e.className=s>0?"wf-panel wf-dangling-panel has":"wf-panel wf-dangling-panel",s===0){e.innerHTML=`<div class="wf-panel-title"><h3>${i(n("workflow.detail.dangling"))}</h3></div><div class="muted">${i(n("workflow.detail.noDangling"))}</div>`;return}e.innerHTML=`<div class="wf-panel-title"><h3>${i(n("workflow.detail.dangling"))}</h3><span class="wf-dangling has">${s}</span></div>
678
514
  <div class="wf-dangling-grid">
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 io(e,t,n,s){let a=lo(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),$=uo(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=>co(l,i,k,r)).join("");e.innerHTML=`<div class="wf-parallel-axis">
681
- <span title="${c(new Date(i).toISOString())}">${c(Pe(i))}</span>
682
- <span title="${c(new Date(m).toISOString())}">${c(Pe(m))}</span>
515
+ ${r.map(([a,d])=>`<div><strong>${a}</strong>${d.length===0?`<div class="muted">${i(n("workflow.detail.none"))}</div>`:`<ul>${d.map(f=>`<li><code>${i(f)}</code></li>`).join("")}</ul>`}</div>`).join("")}
516
+ </div>`}function gn(e,t,o,r){let s=hn(r,o);if(s.length===0){t.textContent="",e.innerHTML=`<div class="empty">${i(n("workflow.detail.noParallelData"))}</div>`;return}let a=Date.now(),d=Math.min(...s.map(l=>l.startedAt)),f=Math.max(...s.map(l=>l.endedAt??a),d+1e3),y=Math.max(1,f-d),S=vn(s,a),b=s.filter(l=>!l.endedAt&&(l.status==="running"||l.status==="effectAttempting")).length;t.textContent=n("workflow.detail.parallelMeta",{count:s.length,max:S,running:b});let k=s.sort((l,T)=>l.startedAt-T.startedAt||l.activityId.localeCompare(T.activityId)).map(l=>bn(l,d,y,a)).join("");e.innerHTML=`<div class="wf-parallel-axis">
517
+ <span title="${i(new Date(d).toISOString())}">${i(ke(d))}</span>
518
+ <span title="${i(new Date(f).toISOString())}">${i(ke(f))}</span>
683
519
  </div>
684
- <div class="wf-parallel-list">${g}</div>`}function lo(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=Wo(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":po(a.type)&&(k.status=fo(a.type),k.endedAt=a.timestamp,k.endType=a.type)}return[...n.values()]}function co(e,t,n,s){let a=e.endedAt??s,r=Wt((e.startedAt-t)/n*100,0,100),i=Wt((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}`:Ne(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(`
520
+ <div class="wf-parallel-list">${k}</div>`}function hn(e,t){let o=new Map,r=new Map(t.activities.map(s=>[s.activityId,s.ownerNodeId]));for(let s of[...e].sort((a,d)=>le(a.eventId)-le(d.eventId))){let a=Xn(s);if(!a)continue;let d=typeof a.activityId=="string"?a.activityId:void 0,f=typeof a.attemptId=="string"?a.attemptId:void 0;if(!d||!f)continue;let y=o.get(f);if(s.type==="attemptCreated"){let S=typeof a.attemptNumber=="number"?a.attemptNumber:void 0;y={nodeId:typeof a.nodeId=="string"?a.nodeId:r.get(d),activityId:d,attemptId:f,attemptNumber:S,status:"pending",startedAt:s.timestamp},o.set(f,y);continue}y||(y={nodeId:r.get(d),activityId:d,attemptId:f,status:"pending",startedAt:s.timestamp},o.set(f,y)),s.type==="activityRunning"?(y.status="running",y.runningAt=s.timestamp):s.type==="effectAttempted"?y.status="effectAttempting":s.type==="activityWaiting"||s.type==="waitCreated"?y.status="waiting":yn(s.type)&&(y.status=kn(s.type),y.endedAt=s.timestamp,y.endType=s.type)}return[...o.values()]}function bn(e,t,o,r){let s=e.endedAt??r,a=pt((e.startedAt-t)/o*100,0,100),d=pt((Math.max(s,e.startedAt+1)-e.startedAt)/o*100,.7,100-a),f=e.nodeId??e.activityId,y=e.attemptNumber!==void 0?`#${e.attemptNumber}`:$e(e.attemptId),S=[`${f} ${e.status}`,`${new Date(e.startedAt).toISOString()} \u2192 ${e.endedAt?new Date(e.endedAt).toISOString():n("workflow.detail.parallelNow")}`,e.endType?`end: ${e.endType}`:void 0].filter(Boolean).join(`
685
521
  `);return`<div class="wf-parallel-row">
686
522
  <div class="wf-parallel-label">
687
- <code>${c(m)}</code>
688
- <span class="muted">${c(e.activityId)} \xB7 ${c(k)}</span>
523
+ <code>${i(f)}</code>
524
+ <span class="muted">${i(e.activityId)} \xB7 ${i(y)}</span>
689
525
  </div>
690
526
  <div class="wf-parallel-track">
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(le(e.status))}</span>
527
+ <div class="wf-parallel-bar wf-parallel-${i(e.status)}" style="left:${a.toFixed(3)}%;width:${d.toFixed(3)}%;" title="${i(S)}">
528
+ <span>${i(Y(e.status))}</span>
693
529
  </div>
694
530
  </div>
695
- </div>`}function uo(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 po(e){return e==="activitySucceeded"||e==="activityFailed"||e==="activityTimedOut"||e==="activityCanceled"}function fo(e){return e==="activitySucceeded"?"succeeded":e==="activityCanceled"?"cancelled":e==="activityTimedOut"?"timedOut":"failed"}function mo(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(Ft(r,i))}for(let r of t.activities)s.has(r.activityId)||a.push(Ft(void 0,r));e.innerHTML=a.length>0?a.join(""):`<tr><td colspan="7" class="empty">${c(o("workflow.detail.noNodes"))}</td></tr>`}function Ft(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>
531
+ </div>`}function vn(e,t){let o=[];for(let a of e)o.push({time:a.startedAt,delta:1}),o.push({time:a.endedAt??t,delta:-1});o.sort((a,d)=>a.time-d.time||d.delta-a.delta);let r=0,s=0;for(let a of o)r+=a.delta,s=Math.max(s,r);return s}function yn(e){return e==="activitySucceeded"||e==="activityFailed"||e==="activityTimedOut"||e==="activityCanceled"}function kn(e){return e==="activitySucceeded"?"succeeded":e==="activityCanceled"?"cancelled":e==="activityTimedOut"?"timedOut":"failed"}function $n(e,t){let o=new Map(t.activities.map(a=>[a.activityId,a])),r=new Set,s=[];for(let a of t.nodes){let d=(a.activityId?o.get(a.activityId):void 0)??t.activities.find(f=>f.ownerNodeId===a.nodeId);d&&r.add(d.activityId),s.push(ut(a,d))}for(let a of t.activities)r.has(a.activityId)||s.push(ut(void 0,a));e.innerHTML=s.length>0?s.join(""):`<tr><td colspan="7" class="empty">${i(n("workflow.detail.noNodes"))}</td></tr>`}function ut(e,t){let o=t?.attempts[t.attempts.length-1];return`<tr>
532
+ <td>${e?`<code>${i(e.nodeId)}</code>`:'<span class="muted">-</span>'}</td>
533
+ <td>${e?G(e.status):'<span class="muted">-</span>'}</td>
534
+ <td>${t?`<code>${i(t.activityId)}</code>`:'<span class="muted">-</span>'}</td>
535
+ <td>${t?G(t.status):'<span class="muted">-</span>'}</td>
700
536
  <td>${t?.attempts.length??0}</td>
701
- <td>${n?`<code>${c(n.attemptId)}</code>`:'<span class="muted">-</span>'}</td>
702
- <td>${n?jo(n):`<span class="muted">${c(o("workflow.detail.idle"))}</span>`}</td>
703
- </tr>`}function wo(e,t,n,s,a,r,i,m){Do(e,n,s),Ho(e,a.comments);let k=!!(i&&t.attemptIO?.[i]?.terminal);k&&i&&n.add(at(i,o("workflow.detail.liveTerminal")));let $=go(t),p=new Set;if(m){for(let l of $){p.add(l.key);let I=m.get(l.key);I||(I=ho(l.key),m.set(l.key,I),e.appendChild(I.article)),bo(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(So(I,n,a,r,i));e.innerHTML=l.length>0?l.join(""):`<div class="empty">${c(o("workflow.detail.noNodeIO"))}</div>`}Oo(e,s);let g=Ro(e,i);return Bo(e,n),qo(e,s),Ao(e,a),en(e,r),g&&k}function go(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?.[qe(r)?.attemptId??""]})}for(let a of e.activities)n.has(a.activityId)||s.push({key:`activity:${a.activityId}`,activity:a,io:e.attemptIO?.[qe(a)?.attemptId??""]});return s}function ho(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 bo(e,t,n,s,a,r){let i=qe(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 $=Zt(i,s);e.head.innerHTML=`
537
+ <td>${o?`<code>${i(o.attemptId)}</code>`:'<span class="muted">-</span>'}</td>
538
+ <td>${o?Kn(o):`<span class="muted">${i(n("workflow.detail.idle"))}</span>`}</td>
539
+ </tr>`}function Sn(e,t,o,r,s,a,d,f){Fn(e,o,r),Pn(e,s.comments);let y=!!(d&&t.attemptIO?.[d]?.terminal);y&&d&&o.add(qe(d,n("workflow.detail.liveTerminal")));let S=In(t),b=new Set;if(f){for(let l of S){b.add(l.key);let T=f.get(l.key);T||(T=Tn(l.key),f.set(l.key,T),e.appendChild(T.article)),Ln(T,l,o,s,a,d)}for(let[l,T]of Array.from(f))b.has(l)||(T.article.remove(),f.delete(l));if(S.length===0){if(!e.querySelector(".wf-io-empty-placeholder")){let l=document.createElement("div");l.className="empty wf-io-empty-placeholder",l.textContent=n("workflow.detail.noNodeIO"),e.appendChild(l)}}else e.querySelector(".wf-io-empty-placeholder")?.remove()}else{let l=[];for(let T of S)l.push(Hn(T,o,s,a,d));e.innerHTML=l.length>0?l.join(""):`<div class="empty">${i(n("workflow.detail.noNodeIO"))}</div>`}_n(e,r);let k=Un(e,d);return Wn(e,o),Jn(e,r),jn(e,s),St(e,a),k&&y}function In(e){let t=new Map(e.activities.map(s=>[s.activityId,s])),o=new Set,r=[];for(let s of e.nodes){let a=(s.activityId?t.get(s.activityId):void 0)??e.activities.find(d=>d.ownerNodeId===s.nodeId);if(!a){r.push({key:`node:${s.nodeId}`,node:s});continue}o.add(a.activityId),r.push({key:`activity:${a.activityId}`,node:s,activity:a,io:e.attemptIO?.[ye(a)?.attemptId??""]})}for(let s of e.activities)o.has(s.activityId)||r.push({key:`activity:${s.activityId}`,activity:s,io:e.attemptIO?.[ye(s)?.attemptId??""]});return r}function Tn(e){let t=document.createElement("article");t.className="wf-io-card",t.dataset.wfCardKey=e;let o=document.createElement("div");o.className="wf-io-card-head";let r=document.createElement("div");r.className="wf-io-terminal-slot";let s=document.createElement("div");return s.className="wf-io-grid",t.appendChild(o),t.appendChild(r),t.appendChild(s),{article:t,head:o,terminalSlot:r,grid:s,currentTerminalUrl:null}}function Ln(e,t,o,r,s,a){let d=ye(t.activity),f=t.node?.nodeId??t.activity?.ownerNodeId??t.activity?.activityId??"unknown",y=!!(d&&d.attemptId===a);e.article.classList.toggle("is-focused",y),d?e.article.dataset.wfAttemptCard=d.attemptId:delete e.article.dataset.wfAttemptCard;let S=$t(d,r);e.head.innerHTML=`
704
540
  <header>
705
541
  <div>
706
- <strong><code>${c(m)}</code></strong>
707
- <span class="muted">${t.activity?c(t.activity.activityId):c(o("workflow.detail.notDispatched"))}</span>
542
+ <strong><code>${i(f)}</code></strong>
543
+ <span class="muted">${t.activity?i(t.activity.activityId):i(n("workflow.detail.notDispatched"))}</span>
708
544
  </div>
709
- <div>${t.node?ne(t.node.status):""} ${t.activity?ne(t.activity.status):""}</div>
545
+ <div>${t.node?G(t.node.status):""} ${t.activity?G(t.activity.status):""}</div>
710
546
  </header>
711
547
  <div class="wf-io-meta">
712
- ${i?`${c(o("workflow.detail.attempt"))} <code>${c(i.attemptId)}</code>`:c(o("workflow.detail.noAttempt"))}
548
+ ${d?`${i(n("workflow.detail.attempt"))} <code>${i(d.attemptId)}</code>`:i(n("workflow.detail.noAttempt"))}
713
549
  </div>
714
- ${$}
715
- `;let p=Jt(i,t.activity,t.io?.terminal,a),g=p?.url??null;if(g!==e.currentTerminalUrl)p===null?e.terminalSlot.innerHTML="":e.terminalSlot.innerHTML=Gt(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=Kt(p.kind);I.innerHTML=`${c(L)} ${Xt(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 Jt(e,t,n,s){if(!n||n.error)return null;if(Io(e,n))return{kind:"live",url:Lo(n)};if(!e||!t||!To(e,n))return null;let a=Mo();if(!a)return null;let r=s?.sessions.get(e.attemptId);return r?{kind:"resume",url:r.url,resumeId:r.resumeId,downloadUrl:_t(a,t.activityId,e.attemptId)}:{kind:"replay",url:Eo(a,t.activityId,e.attemptId,!!n.hasPtyLog),downloadUrl:_t(a,t.activityId,e.attemptId)}}function Gt(e,t,n,s,a,r,i){if(!s)return"";let m=Kt(a.kind),k=at(e,m),$=Xt(t,s),p=yo(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?Yt(t,n,s,a,i):"",I=t?Qt(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>
550
+ ${S}
551
+ `;let b=wt(d,t.activity,t.io?.terminal,s),k=b?.url??null;if(k!==e.currentTerminalUrl)b===null?e.terminalSlot.innerHTML="":e.terminalSlot.innerHTML=gt(t.key,d,t.activity,t.io?.terminal,b,o,s),e.currentTerminalUrl=k;else if(b!==null&&t.io?.terminal){let T=e.terminalSlot.querySelector("details.wf-terminal-block > summary");if(T){let L=ht(b.kind);T.innerHTML=`${i(L)} ${kt(d,t.io.terminal)}`}d&&Nn(e.terminalSlot,d,t.activity,t.io.terminal,b,s)}let l=d?.attemptId??t.activity?.activityId??t.node?.nodeId??"unknown";e.grid.innerHTML=`
552
+ ${J(l,n("workflow.detail.authoredInput"),t.io?.input,o)}
553
+ ${J(l,n("workflow.detail.resolvedInput"),t.io?.resolvedInput,o)}
554
+ ${J(l,n("workflow.detail.output"),t.io?.output,o)}
555
+ ${J(l,n("workflow.detail.executionLog"),t.io?.log,o)}
556
+ ${t.io?.waitPrompt?J(l,n("workflow.detail.waitPrompt"),t.io.waitPrompt,o):""}
557
+ `}function wt(e,t,o,r){if(!o||o.error)return null;if(Rn(e,o))return{kind:"live",url:Dn(o)};if(!e||!t||!xn(e,o))return null;let s=qn();if(!s)return null;let a=r?.sessions.get(e.attemptId);return a?{kind:"resume",url:a.url,resumeId:a.resumeId,downloadUrl:ft(s,t.activityId,e.attemptId)}:{kind:"replay",url:On(s,t.activityId,e.attemptId,!!o.hasPtyLog),downloadUrl:ft(s,t.activityId,e.attemptId)}}function gt(e,t,o,r,s,a,d){if(!r)return"";let f=ht(s.kind),y=qe(e,f),S=kt(t,r),b=En(s.kind),k=s.kind==="replay"||s.kind==="resume"?`<a class="btn-link" href="${i(s.downloadUrl)}" download>${i(n("workflow.detail.downloadFullLog"))}</a>`:"",l=t?vt(t,o,r,s,d):"",T=t?yt(t.attemptId,d):"";return`<details class="wf-io-block wf-terminal-block" data-io-key="${i(y)}"${a.has(y)?" open":""}>
558
+ <summary>${i(f)} ${S}</summary>
723
559
  <div class="wf-terminal-actions">
724
- <a class="btn-link" href="${c(a.url)}" target="_blank" rel="noreferrer">${c(p)}</a>
725
- ${g}
560
+ <a class="btn-link" href="${i(s.url)}" target="_blank" rel="noreferrer">${i(b)}</a>
561
+ ${k}
726
562
  ${l}
727
563
  </div>
728
- ${I}
729
- <iframe class="wf-terminal-frame" src="${c(a.url)}" title="${c(m)}" loading="lazy"></iframe>
730
- </details>`}function Kt(e){return e==="live"?o("workflow.detail.liveTerminal"):e==="resume"?o("workflow.detail.terminalResume"):o("workflow.detail.terminalReplay")}function yo(e){return e==="live"?o("workflow.detail.openTerminalNewTab"):e==="resume"?o("workflow.detail.openResumeNewTab"):o("workflow.detail.openReplayNewTab")}var Vt=new Set(["antigravity","codex-app","cursor","mira"]),vo=new Set(["aiden","coco","claude-code","codex","mtr","hermes"]);function ko(e){return!!e&&(vo.has(e)||Vt.has(e))}function $o(e){return!!e&&Vt.has(e)}function Yt(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>`:ko(n.cliId)?$o(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 Qt(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 So(e,t,n,s,a){let r=qe(e.activity),i=e.node?.nodeId??e.activity?.ownerNodeId??e.activity?.activityId??"unknown",m=r?.attemptId??e.activity?.activityId??e.node?.nodeId??"unknown",k=Zt(r,n),$=r?.attemptId===a?" is-focused":"",p=r?` data-wf-attempt-card="${c(r.attemptId)}"`:"",g=Jt(r,e.activity,e.io?.terminal,s),l=g?Gt(m,r,e.activity,e.io?.terminal,g,t,s):"";return`<article class="wf-io-card${$}" data-wf-card-key="${c(e.key)}"${p}>
564
+ ${T}
565
+ <iframe class="wf-terminal-frame" src="${i(s.url)}" title="${i(f)}" loading="lazy"></iframe>
566
+ </details>`}function ht(e){return e==="live"?n("workflow.detail.liveTerminal"):e==="resume"?n("workflow.detail.terminalResume"):n("workflow.detail.terminalReplay")}function En(e){return e==="live"?n("workflow.detail.openTerminalNewTab"):e==="resume"?n("workflow.detail.openResumeNewTab"):n("workflow.detail.openReplayNewTab")}var bt=new Set(["antigravity","cursor"]),Cn=new Set(["aiden","coco","claude-code","codex"]);function Mn(e){return!!e&&(Cn.has(e)||bt.has(e))}function An(e){return!!e&&bt.has(e)}function vt(e,t,o,r,s){if(!s||r.kind==="live"||!t)return"";let a=r.kind==="resume",d=s.pending.has(e.attemptId),f=`data-wf-resume-attempt="${i(e.attemptId)}" data-wf-resume-activity="${i(t.activityId)}"`;return a?`<button type="button" class="btn-link" data-wf-resume-button="1" data-wf-resume-action="end" ${f}${d?" disabled":""}>${i(d?n("workflow.detail.resumeEnding"):n("workflow.detail.endResumeSession"))}</button>`:Mn(o.cliId)?An(o.cliId)&&!o.cliSessionId?`<button type="button" class="btn-link" data-wf-resume-button="1" disabled title="${i(n("workflow.detail.resumeMissingCliSession"))}">${i(n("workflow.detail.resumeSession"))}</button>`:`<button type="button" class="btn-link" data-wf-resume-button="1" data-wf-resume-action="start" ${f}${d?" disabled":""}>${i(d?n("workflow.detail.resumeStarting"):n("workflow.detail.resumeSession"))}</button>`:`<button type="button" class="btn-link" data-wf-resume-button="1" disabled title="${i(n("workflow.detail.resumeUnsupportedCli",{cliId:o.cliId??"?"}))}">${i(n("workflow.detail.resumeSession"))}</button>`}function yt(e,t){if(!t)return"";let o=t.errors.get(e);return o?`<div class="hint-warn wf-resume-status" data-wf-resume-status="${i(e)}">${i(o)}</div>`:""}function Hn(e,t,o,r,s){let a=ye(e.activity),d=e.node?.nodeId??e.activity?.ownerNodeId??e.activity?.activityId??"unknown",f=a?.attemptId??e.activity?.activityId??e.node?.nodeId??"unknown",y=$t(a,o),S=a?.attemptId===s?" is-focused":"",b=a?` data-wf-attempt-card="${i(a.attemptId)}"`:"",k=wt(a,e.activity,e.io?.terminal,r),l=k?gt(f,a,e.activity,e.io?.terminal,k,t,r):"";return`<article class="wf-io-card${S}" data-wf-card-key="${i(e.key)}"${b}>
731
567
  <div class="wf-io-card-head">
732
568
  <header>
733
569
  <div>
734
- <strong><code>${c(i)}</code></strong>
735
- <span class="muted">${e.activity?c(e.activity.activityId):c(o("workflow.detail.notDispatched"))}</span>
570
+ <strong><code>${i(d)}</code></strong>
571
+ <span class="muted">${e.activity?i(e.activity.activityId):i(n("workflow.detail.notDispatched"))}</span>
736
572
  </div>
737
- <div>${e.node?ne(e.node.status):""} ${e.activity?ne(e.activity.status):""}</div>
573
+ <div>${e.node?G(e.node.status):""} ${e.activity?G(e.activity.status):""}</div>
738
574
  </header>
739
575
  <div class="wf-io-meta">
740
- ${r?`${c(o("workflow.detail.attempt"))} <code>${c(r.attemptId)}</code>`:c(o("workflow.detail.noAttempt"))}
576
+ ${a?`${i(n("workflow.detail.attempt"))} <code>${i(a.attemptId)}</code>`:i(n("workflow.detail.noAttempt"))}
741
577
  </div>
742
- ${k}
578
+ ${y}
743
579
  </div>
744
580
  <div class="wf-io-terminal-slot">${l}</div>
745
581
  <div class="wf-io-grid">
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):""}
582
+ ${J(f,n("workflow.detail.authoredInput"),e.io?.input,t)}
583
+ ${J(f,n("workflow.detail.resolvedInput"),e.io?.resolvedInput,t)}
584
+ ${J(f,n("workflow.detail.output"),e.io?.output,t)}
585
+ ${J(f,n("workflow.detail.executionLog"),e.io?.log,t)}
586
+ ${e.io?.waitPrompt?J(f,n("workflow.detail.waitPrompt"),e.io.waitPrompt,t):""}
751
587
  </div>
752
- </article>`}function qe(e){return e?.attempts[e.attempts.length-1]}function Xt(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 Io(e,t){return t.status==="live"&&t.webPort>0&&(e?.status==="pending"||e?.status==="running"||e?.status==="effectAttempting")}function To(e,t){return e.status==="succeeded"||e.status==="failed"||e.status==="cancelled"||e.status==="timedOut"?!!(t.sessionId||t.startedAt):!1}function Lo(e){return`http://${window.location.hostname||"127.0.0.1"}:${e.webPort}`}function Eo(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 _t(e,t,n){return`/api/workflows/runs/${encodeURIComponent(e)}/attempts/${encodeURIComponent(t)}/${encodeURIComponent(n)}/terminal-log/raw?download=1`}function Mo(){let e=window.location.hash.match(/^#\/workflows\/([^/?#]+)/);if(!e)return null;try{return decodeURIComponent(e[1])}catch{return null}}function Zt(e,t){if(!Co(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)}">
588
+ </article>`}function ye(e){return e?.attempts[e.attempts.length-1]}function kt(e,t){let o=[];return t.error?o.push(n("workflow.detail.error")):o.push(t.status==="live"?n("workflow.detail.terminalLive"):n("workflow.detail.terminalClosedShort")),e?.status&&o.push(e.status),t.webPort>0&&o.push(`:${t.webPort}`),`<span class="muted">${i(o.join(" \xB7 "))}</span>`}function Rn(e,t){return t.status==="live"&&t.webPort>0&&(e?.status==="pending"||e?.status==="running"||e?.status==="effectAttempting")}function xn(e,t){return e.status==="succeeded"||e.status==="failed"||e.status==="cancelled"||e.status==="timedOut"?!!(t.sessionId||t.startedAt):!1}function Dn(e){return`http://${window.location.hostname||"127.0.0.1"}:${e.webPort}`}function On(e,t,o,r){let s=new URLSearchParams({runId:e,activityId:t,attemptId:o});return r&&s.set("hasPtyLog","1"),`/assets/terminal-replay.html?${s.toString()}`}function ft(e,t,o){return`/api/workflows/runs/${encodeURIComponent(e)}/attempts/${encodeURIComponent(t)}/${encodeURIComponent(o)}/terminal-log/raw?download=1`}function qn(){let e=window.location.hash.match(/^#\/workflows\/([^/?#]+)/);if(!e)return null;try{return decodeURIComponent(e[1])}catch{return null}}function $t(e,t){if(!Bn(e))return"";let o=e.attemptId,r=t.comments.get(o)??"",s=t.resolving.has(o),a=t.statuses.get(o),d=a?.kind==="error"?"hint-warn":"hint-ok";return`<div class="wf-approval-box" data-wf-approval="${i(o)}">
753
589
  <label>
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>
590
+ <span>${i(n("workflow.detail.approvalComment"))}</span>
591
+ <textarea class="wf-approval-comment" data-wf-approval-comment="${i(o)}" rows="2" placeholder="${i(n("workflow.detail.optionalComment"))}"${s?" disabled":""}>${i(r)}</textarea>
756
592
  </label>
757
593
  <div class="wf-approval-actions">
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>`:""}
594
+ <button type="button" class="primary" data-wf-approval-action="approve" data-wf-attempt-id="${i(o)}"${s?" disabled":""}>${i(n("workflow.detail.approve"))}</button>
595
+ <button type="button" data-wf-approval-action="reject" data-wf-attempt-id="${i(o)}"${s?" disabled":""}>${i(n("workflow.detail.reject"))}</button>
596
+ ${s?`<span class="muted">${i(n("workflow.detail.submitting"))}</span>`:""}
761
597
  </div>
762
- ${r?`<div class="${i} wf-approval-status">${c(r.text)}</div>`:""}
763
- </div>`}function Co(e){return!!e&&e.status==="waiting"&&e.wait?.waitKind==="human-gate"&&!e.wait.resolution}function Ho(e,t){e.querySelectorAll("textarea[data-wf-approval-comment]").forEach(n=>{let s=n.dataset.wfApprovalComment;s&&t.set(s,n.value)})}function en(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=Yt(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=Qt(t.attemptId,r);p?p.outerHTML=g:g&&i.insertAdjacentHTML("afterend",g)}en(e,r)}function Ao(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=at(e,t);return`<details class="wf-io-block" data-io-key="${c(a)}"${s.has(a)?" open":""}>
764
- <summary>${c(t)} ${Po(n)}</summary>
765
- ${No(n)}
766
- </details>`}function at(e,t){return`${e}:${t}`}function Ro(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 Do(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 Bo(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 Oo(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 qo(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 Po(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(Ne(e.outputHash)),t.length?`<span class="muted">${c(t.join(" \xB7 "))}</span>`:""}function No(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 jo(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(Pe(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(Ne(e.output.outputHash))}`),e.runningMs!==void 0&&t.push(`${e.runningMs}ms`),t.length>0?t.join("<br/>"):'<span class="muted">-</span>'}function Uo(e,t){e.innerHTML=t.length>0?t.map(Fo).join(""):`<tr><td colspan="7" class="empty">${c(o("workflow.detail.noEvents"))}</td></tr>`}function Fo(e){let t=_o(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(Pe(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 _o(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 Wo(e){return!e.payload||typeof e.payload!="object"||"ref"in e.payload?null:e.payload}function Wt(e,t,n){return Math.min(n,Math.max(t,e))}function Ne(e){return e?e.length>18?e.slice(0,10)+"..."+e.slice(-6):e:"-"}function zo(e,t){return e.length>t?e.slice(0,t-1)+"\u2026":e}function Pe(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 tn(e){return e?e.length>18?e.slice(0,10)+"..."+e.slice(-6):e:"-"}function nn(e){let t=location.hash.match(/^#\/(?:workflows\/catalog|workflows-catalog)\/([^/?#]+)$/);return t?Go(e,decodeURIComponent(t[1])):Jo(e)}function Jo(e){e.innerHTML=`
598
+ ${a?`<div class="${d} wf-approval-status">${i(a.text)}</div>`:""}
599
+ </div>`}function Bn(e){return!!e&&e.status==="waiting"&&e.wait?.waitKind==="human-gate"&&!e.wait.resolution}function Pn(e,t){e.querySelectorAll("textarea[data-wf-approval-comment]").forEach(o=>{let r=o.dataset.wfApprovalComment;r&&t.set(r,o.value)})}function St(e,t){e.querySelectorAll("button[data-wf-resume-action][data-wf-resume-attempt][data-wf-resume-activity]").forEach(o=>{o.dataset.wfResumeBound!=="1"&&(o.dataset.wfResumeBound="1",o.addEventListener("click",()=>{let r=o.dataset.wfResumeAttempt,s=o.dataset.wfResumeActivity,a=o.dataset.wfResumeAction;!r||!s||(a==="start"?t.onStart(r,s):a==="end"&&t.onEnd(r,s))}))})}function Nn(e,t,o,r,s,a){let d=e.querySelector(".wf-terminal-actions");if(!d)return;let f=d.querySelector('button[data-wf-resume-button="1"]'),y=vt(t,o,r,s,a);f?f.outerHTML=y:y&&d.insertAdjacentHTML("beforeend",y);let S=e.querySelector("details.wf-terminal-block");if(S){let b=S.querySelector(".wf-resume-status"),k=yt(t.attemptId,a);b?b.outerHTML=k:k&&d.insertAdjacentHTML("afterend",k)}St(e,a)}function jn(e,t){e.querySelectorAll("textarea[data-wf-approval-comment]").forEach(o=>{let r=o.dataset.wfApprovalComment;r&&o.addEventListener("input",()=>{t.comments.set(r,o.value)})}),e.querySelectorAll("button[data-wf-approval-action][data-wf-attempt-id]").forEach(o=>{o.addEventListener("click",()=>{let r=o.dataset.wfAttemptId,s=o.dataset.wfApprovalAction;!r||s!=="approve"&&s!=="reject"||t.onResolve(r,s)})})}function J(e,t,o,r){let s=qe(e,t);return`<details class="wf-io-block" data-io-key="${i(s)}"${r.has(s)?" open":""}>
600
+ <summary>${i(t)} ${Gn(o)}</summary>
601
+ ${zn(o)}
602
+ </details>`}function qe(e,t){return`${e}:${t}`}function Un(e,t){if(!t)return!1;for(let o of e.querySelectorAll("[data-wf-attempt-card]"))if(o.dataset.wfAttemptCard===t)return o.scrollIntoView({block:"center"}),!0;return!1}function Fn(e,t,o){e.querySelectorAll("details.wf-io-block[data-io-key]").forEach(r=>{let s=r.dataset.ioKey;if(!s)return;r.open?t.add(s):t.delete(s);let a=r.querySelector(".wf-io-pre");a&&o.set(s,a.scrollTop)})}function Wn(e,t){e.querySelectorAll("details.wf-io-block[data-io-key]").forEach(o=>{o.dataset.ioToggleBound!=="1"&&(o.dataset.ioToggleBound="1",o.addEventListener("toggle",()=>{let r=o.dataset.ioKey;r&&(o.open?t.add(r):t.delete(r))}))})}function _n(e,t){e.querySelectorAll("details.wf-io-block[data-io-key]").forEach(o=>{let r=o.dataset.ioKey;if(!r)return;let s=t.get(r);if(s===void 0)return;let a=o.querySelector(".wf-io-pre");a&&(a.scrollTop=s)})}function Jn(e,t){e.querySelectorAll("details.wf-io-block[data-io-key]").forEach(o=>{let r=o.dataset.ioKey;if(!r)return;let s=o.querySelector(".wf-io-pre");s&&s.dataset.ioScrollBound!=="1"&&(s.dataset.ioScrollBound="1",s.addEventListener("scroll",()=>{t.set(r,s.scrollTop)}))})}function Gn(e){if(!e)return`<span class="muted">${i(n("workflow.detail.empty"))}</span>`;let t=[];return e.outputBytes!==void 0&&t.push(`${e.outputBytes}B`),e.truncated&&t.push(n("workflow.detail.truncated")),e.error&&t.push(n("workflow.detail.error")),e.outputHash&&t.push($e(e.outputHash)),t.length?`<span class="muted">${i(t.join(" \xB7 "))}</span>`:""}function zn(e){if(!e)return`<div class="muted wf-io-empty">${i(n("workflow.detail.noData"))}</div>`;let t=e.value!==void 0?JSON.stringify(e.value,null,2):e.text??"",o=e.error?`<div class="muted error">${i(e.error)}</div>`:"";return t?`${o}<pre class="wf-io-pre">${i(t)}</pre>`:`${o}<div class="muted wf-io-empty">${i(n("workflow.detail.noPreview"))}</div>`}function Kn(e){let t=[];if(e.effectAttempted&&t.push(`${i(n("workflow.detail.effect"))} ${i(e.effectAttempted.provider)}`),e.wait){let o=e.wait.resolution?`${e.wait.resolution.kind}${e.wait.resolution.resolution?":"+e.wait.resolution.resolution:""}`:n("workflow.detail.open");t.push(`${i(n("workflow.detail.wait"))} ${i(e.wait.waitKind)} ${i(o)}`),e.wait.deadlineAt!==void 0&&t.push(`${i(n("workflow.detail.deadline"))} ${i(ke(e.wait.deadlineAt))}`)}if(e.error){let o=`${e.error.errorCode}${e.error.errorClass?` \xB7 ${e.error.errorClass}`:""}`;t.push(`<span class="muted error">${i(o)}</span>`),e.error.errorMessage&&t.push(`<span class="error wf-error-msg">${i(e.error.errorMessage)}</span>`)}return e.output&&t.push(`${i(n("workflow.detail.output"))} ${i($e(e.output.outputHash))}`),e.runningMs!==void 0&&t.push(`${e.runningMs}ms`),t.length>0?t.join("<br/>"):'<span class="muted">-</span>'}function Vn(e,t){e.innerHTML=t.length>0?t.map(Yn).join(""):`<tr><td colspan="7" class="empty">${i(n("workflow.detail.noEvents"))}</td></tr>`}function Yn(e){let t=Qn(e.payload);return`<tr>
603
+ <td>${le(e.eventId)}</td>
604
+ <td><code>${i(e.type)}</code></td>
605
+ <td>${i(e.actor)}</td>
606
+ <td>${t.nodeId?`<code>${i(t.nodeId)}</code>`:"-"}</td>
607
+ <td>${t.activityId?`<code>${i(t.activityId)}</code>`:"-"}</td>
608
+ <td>${t.errorCode?`<span class="muted error">${i(t.errorCode)}</span>`:"-"}</td>
609
+ <td title="${i(new Date(e.timestamp).toISOString())}">${i(ke(e.timestamp))}</td>
610
+ </tr>`}function le(e){let t=e.lastIndexOf("-");if(t<0)return 0;let o=Number(e.slice(t+1));return Number.isFinite(o)?o:0}function Qn(e){if(!e||typeof e!="object"||"ref"in e)return{};let t=e,o={};typeof t.nodeId=="string"&&(o.nodeId=t.nodeId),typeof t.activityId=="string"&&(o.activityId=t.activityId),typeof t.failedNodeId=="string"&&(o.nodeId=t.failedNodeId);let r=t.error;return r&&typeof r=="object"&&"errorCode"in r&&(o.errorCode=String(r.errorCode)),o}function Xn(e){return!e.payload||typeof e.payload!="object"||"ref"in e.payload?null:e.payload}function pt(e,t,o){return Math.min(o,Math.max(t,e))}function $e(e){return e?e.length>18?e.slice(0,10)+"..."+e.slice(-6):e:"-"}function Zn(e,t){return e.length>t?e.slice(0,t-1)+"\u2026":e}function ke(e){return new Date(e).toLocaleTimeString([],{hour:"2-digit",minute:"2-digit",second:"2-digit"})}function C(e){return e.replace(/[&<>"']/g,t=>({"&":"&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=`
775
611
  <nav class="wf-subnav">
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>
612
+ <a href="#/workflows" data-i18n="workflow.subnav.runs">${C(n("workflow.subnav.runs"))}</a>
613
+ <a href="#/workflows/catalog" class="active" data-i18n="workflow.subnav.catalog">${C(n("workflow.subnav.catalog"))}</a>
778
614
  </nav>
779
615
  <section class="catalog-head">
780
616
  <div>
781
- <h2>${C(o("catalog.title"))}</h2>
782
- <p class="muted">${C(o("catalog.subtitle"))}</p>
617
+ <h2>${C(n("catalog.title"))}</h2>
618
+ <p class="muted">${C(n("catalog.subtitle"))}</p>
783
619
  </div>
784
- <button id="catalog-refresh" type="button">${C(o("catalog.refresh"))}</button>
620
+ <button id="catalog-refresh" type="button">${C(n("catalog.refresh"))}</button>
785
621
  </section>
786
622
  <form id="catalog-filters" class="filters">
787
- <input type="search" name="q" placeholder="${C(o("catalog.searchPlaceholder"))}" />
623
+ <input type="search" name="q" placeholder="${C(n("catalog.searchPlaceholder"))}" />
788
624
  <span id="catalog-status" class="muted"></span>
789
625
  </form>
790
626
  <div class="wf-table-scroll">
791
627
  <table>
792
628
  <thead><tr>
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>
629
+ <th>${C(n("catalog.table.workflow"))}</th>
630
+ <th>${C(n("catalog.table.version"))}</th>
631
+ <th>${C(n("catalog.table.params"))}</th>
632
+ <th>${C(n("catalog.table.nodes"))}</th>
633
+ <th>${C(n("catalog.table.revision"))}</th>
634
+ <th>${C(n("catalog.table.path"))}</th>
799
635
  </tr></thead>
800
636
  <tbody id="catalog-tbody"></tbody>
801
637
  </table>
802
638
  </div>
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=>`
639
+ `;let t=e.querySelector("#catalog-tbody"),o=e.querySelector("#catalog-status"),r=e.querySelector("#catalog-filters"),s=e.querySelector("#catalog-refresh"),a=[],d=null,f=!1;function y(){let k=(new FormData(r).get("q")??"").trim().toLowerCase();return k?a.filter(l=>l.workflowId.toLowerCase().includes(k)||l.path.toLowerCase().includes(k)):a}function S(){d?(o.textContent=n("catalog.loadFailed",{error:d}),o.classList.add("error")):(o.textContent=`${a.length}`,o.classList.remove("error"));let k=y();if(k.length===0){t.innerHTML=`<tr><td colspan="6" class="empty">${a.length===0?C(n("catalog.noDefinitions")):C(n("catalog.noFilterMatch"))}</td></tr>`;return}t.innerHTML=k.map(l=>`
804
640
  <tr>
805
641
  <td><a href="#/workflows/catalog/${encodeURIComponent(l.workflowId)}"><code>${C(l.workflowId)}</code></a></td>
806
642
  <td>${l.version}</td>
807
- <td>${C(o("catalog.paramSummary",{required:l.requiredParamCount,total:l.paramCount}))}</td>
643
+ <td>${C(n("catalog.paramSummary",{required:l.requiredParamCount,total:l.paramCount}))}</td>
808
644
  <td>${l.nodeCount}</td>
809
- <td><code>${C(tn(l.revisionId))}</code></td>
645
+ <td><code>${C(It(l.revisionId))}</code></td>
810
646
  <td><code>${C(l.path)}</code></td>
811
647
  </tr>
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 Go(e,t){e.innerHTML=`
648
+ `).join("")}async function b(){s.disabled=!0,o.textContent=n("catalog.loading");try{let k=await fetch("/api/workflows/definitions");if(!k.ok)throw new Error(`HTTP ${k.status}`);a=(await k.json()).definitions??[],d=null}catch(k){d=k?.message??String(k),a=[]}finally{s.disabled=!1,f||S()}}return r.addEventListener("input",S),s.addEventListener("click",()=>{b()}),b(),()=>{f=!0}}function to(e,t){e.innerHTML=`
813
649
  <div class="catalog-detail-head">
814
- <a class="btn-link" href="#/workflows/catalog">${C(o("catalog.back"))}</a>
650
+ <a class="btn-link" href="#/workflows/catalog">${C(n("catalog.back"))}</a>
815
651
  <div>
816
652
  <h2><code>${C(t)}</code></h2>
817
- <div id="catalog-detail-subtitle" class="muted">${C(o("workflow.detail.loading"))}</div>
653
+ <div id="catalog-detail-subtitle" class="muted">${C(n("workflow.detail.loading"))}</div>
818
654
  </div>
819
655
  </div>
820
656
  <section id="catalog-error" class="hint-warn" hidden></section>
821
657
  <section id="catalog-run-status" class="hint-ok" hidden></section>
822
658
  <div id="catalog-detail-body"></div>
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")} ${tn(i.revisionId)} \xB7 ${i.path}`;let T=JSON.stringify(g(f.params),null,2);r.innerHTML=`
659
+ `;let o=e.querySelector("#catalog-detail-subtitle"),r=e.querySelector("#catalog-error"),s=e.querySelector("#catalog-run-status"),a=e.querySelector("#catalog-detail-body"),d=null,f=!1,y=!1;function S(u){r.hidden=!u,r.textContent=u??""}function b(u){s.hidden=!u,s.textContent=u??""}function k(u){let I={};for(let[$,v]of Object.entries(u??{}))"default"in v&&(I[$]=v.default);return I}function l(){if(!d)return;let u=d.definition;o.textContent=`${n("catalog.revision")} ${It(d.revisionId)} \xB7 ${d.path}`;let I=JSON.stringify(k(u.params),null,2);a.innerHTML=`
824
660
  <section class="wf-panel">
825
- <div class="wf-panel-title"><h3>${C(o("catalog.summary"))}</h3></div>
661
+ <div class="wf-panel-title"><h3>${C(n("catalog.summary"))}</h3></div>
826
662
  <div class="wf-summary-grid">
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>
663
+ <div class="wf-summary-item"><span>${C(n("catalog.table.workflow"))}</span><strong><code>${C(u.workflowId)}</code></strong></div>
664
+ <div class="wf-summary-item"><span>${C(n("catalog.table.version"))}</span><strong>${u.version}</strong></div>
665
+ <div class="wf-summary-item"><span>${C(n("catalog.nodeCount"))}</span><strong>${Object.keys(u.nodes).length}</strong></div>
666
+ <div class="wf-summary-item"><span>${C(n("catalog.path"))}</span><strong><code>${C(d.path)}</code></strong></div>
831
667
  </div>
832
668
  </section>
833
669
 
834
670
  <section class="wf-panel">
835
- <div class="wf-panel-title"><h3>${C(o("catalog.runPanel"))}</h3></div>
671
+ <div class="wf-panel-title"><h3>${C(n("catalog.runPanel"))}</h3></div>
836
672
  <form id="catalog-run-form" class="catalog-run-form">
837
673
  <label>
838
- <span>${C(o("catalog.paramsJson"))}</span>
839
- <textarea id="catalog-params" rows="8" spellcheck="false" placeholder="${C(o("catalog.paramsPlaceholder"))}">${C(T)}</textarea>
674
+ <span>${C(n("catalog.paramsJson"))}</span>
675
+ <textarea id="catalog-params" rows="8" spellcheck="false" placeholder="${C(n("catalog.paramsPlaceholder"))}">${C(I)}</textarea>
840
676
  </label>
841
677
  <div class="catalog-chat-grid">
842
678
  <label>
843
- <span>${C(o("catalog.chatId"))}</span>
679
+ <span>${C(n("catalog.chatId"))}</span>
844
680
  <input id="catalog-chat-id" type="text" autocomplete="off" />
845
681
  </label>
846
682
  <label>
847
- <span>${C(o("catalog.larkAppId"))}</span>
683
+ <span>${C(n("catalog.larkAppId"))}</span>
848
684
  <input id="catalog-lark-app-id" type="text" autocomplete="off" />
849
685
  </label>
850
686
  </div>
851
- <div class="muted">${C(o("catalog.chatBindingHint"))}</div>
687
+ <div class="muted">${C(n("catalog.chatBindingHint"))}</div>
852
688
  <div id="catalog-param-errors" class="catalog-param-errors" hidden></div>
853
- <button id="catalog-run-btn" type="submit" class="primary">${C(o("catalog.run"))}</button>
689
+ <button id="catalog-run-btn" type="submit" class="primary">${C(n("catalog.run"))}</button>
854
690
  </form>
855
691
  </section>
856
692
 
857
693
  <section class="wf-panel">
858
- <div class="wf-panel-title"><h3>${C(o("catalog.paramsSchema"))}</h3></div>
859
- ${Ko(f.params)}
694
+ <div class="wf-panel-title"><h3>${C(n("catalog.paramsSchema"))}</h3></div>
695
+ ${no(u.params)}
860
696
  </section>
861
697
 
862
698
  <section class="wf-panel">
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>
699
+ <div class="wf-panel-title"><h3>${C(n("catalog.definitionJson"))}</h3></div>
700
+ <pre class="wf-io-pre">${C(JSON.stringify(u,null,2))}</pre>
865
701
  </section>
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"),H=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){H.hidden=!1,H.innerHTML=`<div class="muted error">${C(w?.message??String(w))}</div>`;return}k=!0,y.disabled=!0,y.textContent=o("catalog.running"),H.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&&(H.hidden=!1,H.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 Ko(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])=>`
702
+ `,L()}async function T(){if(!d||y)return;let u=a.querySelector("#catalog-params"),I=a.querySelector("#catalog-chat-id"),$=a.querySelector("#catalog-lark-app-id"),v=a.querySelector("#catalog-run-btn"),M=a.querySelector("#catalog-param-errors"),c;try{if(c=JSON.parse(u.value||"{}"),!c||typeof c!="object"||Array.isArray(c))throw new Error(n("catalog.badParamsJson"))}catch(p){M.hidden=!1,M.innerHTML=`<div class="muted error">${C(p?.message??String(p))}</div>`;return}y=!0,v.disabled=!0,v.textContent=n("catalog.running"),M.hidden=!0,S(null),b(null);try{let p=await fetch(`/api/workflows/definitions/${encodeURIComponent(d.definition.workflowId)}/run`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({params:c,chatBinding:{chatId:I.value.trim(),larkAppId:$.value.trim()}})});if(p.status===401)throw new Error(n("catalog.writeAccess"));let h=await p.json().catch(()=>({}));if(!p.ok||!h.ok)throw h.issues?.length&&(M.hidden=!1,M.innerHTML=`<strong>${C(n("catalog.invalidParams"))}</strong><ul>${h.issues.map(E=>`<li>${C(n("catalog.issue",{path:E.path.length?E.path.join("."):"(root)",message:E.message}))}</li>`).join("")}</ul>`),new Error(h.hint??h.message??h.error??n("catalog.runHttp",{status:p.status}));b(n("catalog.runStarted")),h.runId&&(location.hash=`#/workflows/${encodeURIComponent(h.runId)}`)}catch(p){S(p?.message??String(p))}finally{y=!1,v.disabled=!1,v.textContent=n("catalog.run")}}function L(){a.querySelector("#catalog-run-form")?.addEventListener("submit",I=>{I.preventDefault(),T()})}async function g(){try{let u=await fetch(`/api/workflows/definitions/${encodeURIComponent(t)}`);if(u.status===404)throw new Error("unknown_workflow");if(!u.ok)throw new Error(`HTTP ${u.status}`);d=await u.json(),S(null),l()}catch(u){S(n("catalog.definitionLoadFailed",{error:u?.message??String(u)})),o.textContent=n("workflow.detail.loadFailed")}}return g().then(()=>{}),()=>{f=!0}}function no(e){let t=Object.entries(e??{});return t.length===0?`<div class="muted">${C(n("catalog.noParams"))}</div>`:`<div class="catalog-param-list">${t.map(([o,r])=>`
867
703
  <article class="catalog-param">
868
704
  <header>
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>
705
+ <code>${C(o)}</code>
706
+ <span class="wf-status">${C(r.required?n("catalog.required"):n("catalog.optional"))}</span>
707
+ <span class="muted">${C(r.type)}${r.format?` \xB7 ${C(r.format)}`:""}</span>
872
708
  </header>
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>`:""}
709
+ ${r.description?`<div class="muted">${C(n("catalog.description"))}: ${C(r.description)}</div>`:""}
710
+ ${"default"in r?`<pre class="wf-io-pre">${C(`${n("catalog.default")}: ${JSON.stringify(r.default,null,2)}`)}</pre>`:""}
875
711
  </article>
876
- `).join("")}</div>`}var pe=null,je=null;function Ue(){je!==null&&(window.clearInterval(je),je=null)}function on(){return pe||(pe=document.createElement("dialog"),pe.className="onboarding-dialog",document.body.appendChild(pe),pe.addEventListener("close",Ue),pe)}function Vo(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=on(),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>
712
+ `).join("")}</div>`}var ee=null,Se=null;function Ie(){Se!==null&&(window.clearInterval(Se),Se=null)}function Lt(){return ee||(ee=document.createElement("dialog"),ee.className="onboarding-dialog",document.body.appendChild(ee),ee.addEventListener("close",Ie),ee)}function oo(e){return e.status==="waiting_for_scan"?n("botOnboarding.waiting"):e.status==="verifying"?n("botOnboarding.verifying"):e.status==="completed"?n("botOnboarding.completed"):e.status==="failed"?`${n("botOnboarding.failed")}: ${e.message??e.error??"unknown"}`:n("botOnboarding.starting")}function de(e){let t=Lt(),o=e.qrDataUrl?`<div class="qr-card">
713
+ <img class="qr-image" src="${e.qrDataUrl}" alt="${n("botOnboarding.qrAlt")}">
714
+ ${e.qrUrl?`<a class="onboarding-link" href="${e.qrUrl}" target="_blank" rel="noopener">${n("botOnboarding.openLink")}</a>`:""}
715
+ </div>`:"",r=e.appId?`<p><b>App ID:</b> <code>${e.appId}</code></p>`:"",s=e.status==="completed"?`<p class="hint-ok">${n("botOnboarding.restartHint")}</p>`:"";t.innerHTML=`<article>
880
716
  <header>
881
- <h3>${o("botOnboarding.title")}</h3>
882
- <p>${o("botOnboarding.intro")}</p>
717
+ <h3>${n("botOnboarding.title")}</h3>
718
+ <p>${n("botOnboarding.intro")}</p>
883
719
  </header>
884
- <p class="onboarding-status status-${e.status}">${Vo(e)}</p>
885
- ${n}
720
+ <p class="onboarding-status status-${e.status}">${oo(e)}</p>
721
+ ${o}
722
+ ${r}
886
723
  ${s}
887
- ${a}
888
- <form method="dialog"><button>${o("botOnboarding.close")}</button></form>
889
- </article>`}async function Yo(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")&&Ue()}async function Qo(){Ue(),Se({id:"",status:"starting"});let e=on();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),je=window.setInterval(()=>{Yo(n.job.id).catch(s=>{Ue(),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 an(){let e=document.getElementById("add-bot-btn");e&&(e.onclick=()=>{Qo()})}var V=document.getElementById("root"),Ie=null;function st(){Ie&&(Ie(),Ie=null);let e=location.hash||"#/";e.startsWith("#/workflows/catalog")||e.startsWith("#/workflows-catalog")?Ie=nn(V):e.startsWith("#/workflows")?Ie=zt(V):e.startsWith("#/groups")?It(V):e.startsWith("#/bot-defaults")?Et(V):e.startsWith("#/connectors")?Ut(V):e.startsWith("#/team/manage")?Pt(V):e.startsWith("#/team")?qt(V):e.startsWith("#/roles")?xt(V):e.startsWith("#/schedules")?$t(V):e.startsWith("#/sessions")?vt(V):bt(V);for(let t of document.querySelectorAll(".sidebar-nav a")){let n=t.getAttribute("href")??"#/";t.classList.toggle("active",n===(e||"#/"))}}var rt=document.getElementById("status");function rn(){rt&&(rt.textContent=N.online?o("status.live"):o("status.disconnected"),rt.className="connection-status "+(N.online?"online":"offline"))}N.on(rn);function sn(){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)}),rn()}function Xo(){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(),Xo(),an(),ae.on(()=>{sn(),st()}),sn();try{await pt()}catch(e){console.error("botmux dashboard bootstrap failed",e),N.setOnline(!1)}window.addEventListener("hashchange",st),st()})();})();
724
+ <form method="dialog"><button>${n("botOnboarding.close")}</button></form>
725
+ </article>`}async function so(e){let t=await fetch(`/api/bot-onboarding/${encodeURIComponent(e)}`),o=await t.json();if(!t.ok||!o?.job)throw new Error(o?.error??`http_${t.status}`);de(o.job),(o.job.status==="completed"||o.job.status==="failed")&&Ie()}async function ao(){Ie(),de({id:"",status:"starting"});let e=Lt();e.open||e.showModal();try{let t=await fetch("/api/bot-onboarding/start",{method:"POST"}),o=await t.json();if(!t.ok||!o?.job?.id)throw new Error(o?.error??`http_${t.status}`);de(o.job),Se=window.setInterval(()=>{so(o.job.id).catch(r=>{Ie(),de({id:o.job.id,status:"failed",message:r instanceof Error?r.message:String(r)})})},1200)}catch(t){de({id:"",status:"failed",message:t instanceof Error?t.message:String(t)})}}function Et(){let e=document.getElementById("add-bot-btn");e&&(e.onclick=()=>{ao()})}var Q=document.getElementById("root"),ce=null;function Be(){ce&&(ce(),ce=null);let e=location.hash||"#/";e.startsWith("#/workflows/catalog")||e.startsWith("#/workflows-catalog")?ce=Tt(Q):e.startsWith("#/workflows")?ce=mt(Q):e.startsWith("#/groups")?ot(Q):e.startsWith("#/bot-defaults")?rt(Q):e.startsWith("#/roles")?ct(Q):e.startsWith("#/schedules")?tt(Q):e.startsWith("#/sessions")?Ze(Q):Qe(Q);for(let t of document.querySelectorAll(".sidebar-nav a")){let o=t.getAttribute("href")??"#/";t.classList.toggle("active",o===(e||"#/"))}}var Pe=document.getElementById("status");function Mt(){Pe&&(Pe.textContent=O.online?n("status.live"):n("status.disconnected"),Pe.className="connection-status "+(O.online?"online":"offline"))}O.on(Mt);function Ct(){document.querySelectorAll("[data-i18n]").forEach(e=>{e.textContent=n(e.dataset.i18n??"")}),document.querySelectorAll("[data-locale]").forEach(e=>{e.classList.toggle("active",e.dataset.locale===K.locale)}),document.querySelectorAll("[data-theme-mode]").forEach(e=>{e.classList.toggle("active",e.dataset.themeMode===K.themeMode)}),Mt()}function ro(){document.querySelectorAll("[data-locale]").forEach(e=>{e.onclick=()=>K.setLocale(e.dataset.locale)}),document.querySelectorAll("[data-theme-mode]").forEach(e=>{e.onclick=()=>K.setThemeMode(e.dataset.themeMode)})}(async()=>{K.init(),ro(),Et(),K.on(()=>{Ct(),Be()}),Ct();try{await Je()}catch(e){console.error("botmux dashboard bootstrap failed",e),O.setOnline(!1)}window.addEventListener("hashchange",Be),Be()})();})();