botmux 2.107.0 → 2.108.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 (515) hide show
  1. package/README.en.md +17 -14
  2. package/README.md +19 -14
  3. package/dist/adapters/backend/sandbox.d.ts +3 -1
  4. package/dist/adapters/backend/sandbox.d.ts.map +1 -1
  5. package/dist/adapters/backend/sandbox.js +19 -3
  6. package/dist/adapters/backend/sandbox.js.map +1 -1
  7. package/dist/adapters/backend/tmux-backend.d.ts.map +1 -1
  8. package/dist/adapters/backend/tmux-backend.js +16 -1
  9. package/dist/adapters/backend/tmux-backend.js.map +1 -1
  10. package/dist/adapters/backend/tmux-pipe-backend.d.ts +24 -15
  11. package/dist/adapters/backend/tmux-pipe-backend.d.ts.map +1 -1
  12. package/dist/adapters/backend/tmux-pipe-backend.js +75 -43
  13. package/dist/adapters/backend/tmux-pipe-backend.js.map +1 -1
  14. package/dist/adapters/cli/genius.d.ts.map +1 -1
  15. package/dist/adapters/cli/genius.js +9 -15
  16. package/dist/adapters/cli/genius.js.map +1 -1
  17. package/dist/adapters/cli/grok.d.ts +73 -0
  18. package/dist/adapters/cli/grok.d.ts.map +1 -0
  19. package/dist/adapters/cli/grok.js +298 -0
  20. package/dist/adapters/cli/grok.js.map +1 -0
  21. package/dist/adapters/cli/oh-my-pi.d.ts.map +1 -1
  22. package/dist/adapters/cli/oh-my-pi.js +133 -12
  23. package/dist/adapters/cli/oh-my-pi.js.map +1 -1
  24. package/dist/adapters/cli/registry.d.ts +2 -1
  25. package/dist/adapters/cli/registry.d.ts.map +1 -1
  26. package/dist/adapters/cli/registry.js +3 -1
  27. package/dist/adapters/cli/registry.js.map +1 -1
  28. package/dist/adapters/cli/shared-hints.d.ts +8 -4
  29. package/dist/adapters/cli/shared-hints.d.ts.map +1 -1
  30. package/dist/adapters/cli/shared-hints.js +8 -4
  31. package/dist/adapters/cli/shared-hints.js.map +1 -1
  32. package/dist/adapters/cli/types.d.ts +18 -16
  33. package/dist/adapters/cli/types.d.ts.map +1 -1
  34. package/dist/adapters/hook-installer.d.ts +5 -4
  35. package/dist/adapters/hook-installer.d.ts.map +1 -1
  36. package/dist/adapters/hook-installer.js +42 -0
  37. package/dist/adapters/hook-installer.js.map +1 -1
  38. package/dist/cli/send-dispatch.d.ts +21 -0
  39. package/dist/cli/send-dispatch.d.ts.map +1 -1
  40. package/dist/cli/send-dispatch.js +159 -0
  41. package/dist/cli/send-dispatch.js.map +1 -1
  42. package/dist/cli/session-list-liveness.d.ts +22 -0
  43. package/dist/cli/session-list-liveness.d.ts.map +1 -0
  44. package/dist/cli/session-list-liveness.js +22 -0
  45. package/dist/cli/session-list-liveness.js.map +1 -0
  46. package/dist/cli.d.ts.map +1 -1
  47. package/dist/cli.js +182 -33
  48. package/dist/cli.js.map +1 -1
  49. package/dist/core/ask-types.d.ts +4 -0
  50. package/dist/core/ask-types.d.ts.map +1 -1
  51. package/dist/core/ask-types.js +10 -0
  52. package/dist/core/ask-types.js.map +1 -1
  53. package/dist/core/cost-calculator.d.ts.map +1 -1
  54. package/dist/core/cost-calculator.js +35 -13
  55. package/dist/core/cost-calculator.js.map +1 -1
  56. package/dist/core/dashboard-ipc-server.d.ts.map +1 -1
  57. package/dist/core/dashboard-ipc-server.js +89 -16
  58. package/dist/core/dashboard-ipc-server.js.map +1 -1
  59. package/dist/core/dashboard-rows.d.ts.map +1 -1
  60. package/dist/core/dashboard-rows.js +6 -2
  61. package/dist/core/dashboard-rows.js.map +1 -1
  62. package/dist/core/dashboard-url.d.ts +6 -5
  63. package/dist/core/dashboard-url.d.ts.map +1 -1
  64. package/dist/core/dashboard-url.js +11 -8
  65. package/dist/core/dashboard-url.js.map +1 -1
  66. package/dist/core/maintenance.d.ts +11 -8
  67. package/dist/core/maintenance.d.ts.map +1 -1
  68. package/dist/core/maintenance.js +45 -20
  69. package/dist/core/maintenance.js.map +1 -1
  70. package/dist/core/per-bot-env.d.ts.map +1 -1
  71. package/dist/core/per-bot-env.js +4 -0
  72. package/dist/core/per-bot-env.js.map +1 -1
  73. package/dist/core/session-discovery.d.ts.map +1 -1
  74. package/dist/core/session-discovery.js +4 -0
  75. package/dist/core/session-discovery.js.map +1 -1
  76. package/dist/core/skills/cli-admin-command.d.ts.map +1 -1
  77. package/dist/core/skills/cli-admin-command.js +8 -2
  78. package/dist/core/skills/cli-admin-command.js.map +1 -1
  79. package/dist/core/skills/im-command.d.ts.map +1 -1
  80. package/dist/core/skills/im-command.js +3 -1
  81. package/dist/core/skills/im-command.js.map +1 -1
  82. package/dist/core/skills/sources.d.ts +33 -1
  83. package/dist/core/skills/sources.d.ts.map +1 -1
  84. package/dist/core/skills/sources.js +97 -0
  85. package/dist/core/skills/sources.js.map +1 -1
  86. package/dist/core/skills/types.d.ts +8 -0
  87. package/dist/core/skills/types.d.ts.map +1 -1
  88. package/dist/core/terminal-url.d.ts.map +1 -1
  89. package/dist/core/terminal-url.js +12 -2
  90. package/dist/core/terminal-url.js.map +1 -1
  91. package/dist/core/terminal-write-auth.d.ts +34 -0
  92. package/dist/core/terminal-write-auth.d.ts.map +1 -0
  93. package/dist/core/terminal-write-auth.js +76 -0
  94. package/dist/core/terminal-write-auth.js.map +1 -0
  95. package/dist/core/tmux-recovery.d.ts +11 -0
  96. package/dist/core/tmux-recovery.d.ts.map +1 -0
  97. package/dist/core/tmux-recovery.js +19 -0
  98. package/dist/core/tmux-recovery.js.map +1 -0
  99. package/dist/core/types.d.ts +10 -4
  100. package/dist/core/types.d.ts.map +1 -1
  101. package/dist/core/types.js.map +1 -1
  102. package/dist/core/update-check.d.ts +2 -2
  103. package/dist/core/update-check.js +2 -2
  104. package/dist/core/worker-pool.d.ts +3 -0
  105. package/dist/core/worker-pool.d.ts.map +1 -1
  106. package/dist/core/worker-pool.js +70 -3
  107. package/dist/core/worker-pool.js.map +1 -1
  108. package/dist/daemon.d.ts.map +1 -1
  109. package/dist/daemon.js +15 -8
  110. package/dist/daemon.js.map +1 -1
  111. package/dist/dashboard/auth.d.ts +1 -1
  112. package/dist/dashboard/auth.d.ts.map +1 -1
  113. package/dist/dashboard/auth.js +7 -2
  114. package/dist/dashboard/auth.js.map +1 -1
  115. package/dist/dashboard/bot-payload.d.ts +3 -0
  116. package/dist/dashboard/bot-payload.d.ts.map +1 -1
  117. package/dist/dashboard/bot-payload.js +3 -0
  118. package/dist/dashboard/bot-payload.js.map +1 -1
  119. package/dist/dashboard/connector-api.d.ts.map +1 -1
  120. package/dist/dashboard/connector-api.js +39 -13
  121. package/dist/dashboard/connector-api.js.map +1 -1
  122. package/dist/dashboard/history-senders.d.ts +37 -0
  123. package/dist/dashboard/history-senders.d.ts.map +1 -0
  124. package/dist/dashboard/history-senders.js +60 -0
  125. package/dist/dashboard/history-senders.js.map +1 -0
  126. package/dist/dashboard/roles-batch.d.ts +31 -0
  127. package/dist/dashboard/roles-batch.d.ts.map +1 -0
  128. package/dist/dashboard/roles-batch.js +87 -0
  129. package/dist/dashboard/roles-batch.js.map +1 -0
  130. package/dist/dashboard/settings-card-model.d.ts +4 -2
  131. package/dist/dashboard/settings-card-model.d.ts.map +1 -1
  132. package/dist/dashboard/settings-card-model.js +13 -5
  133. package/dist/dashboard/settings-card-model.js.map +1 -1
  134. package/dist/dashboard/settings-write-applier.d.ts +4 -1
  135. package/dist/dashboard/settings-write-applier.d.ts.map +1 -1
  136. package/dist/dashboard/settings-write-applier.js +6 -1
  137. package/dist/dashboard/settings-write-applier.js.map +1 -1
  138. package/dist/dashboard/skill-install-request.d.ts +4 -0
  139. package/dist/dashboard/skill-install-request.d.ts.map +1 -1
  140. package/dist/dashboard/skill-install-request.js +11 -1
  141. package/dist/dashboard/skill-install-request.js.map +1 -1
  142. package/dist/dashboard/trigger-api.d.ts +9 -1
  143. package/dist/dashboard/trigger-api.d.ts.map +1 -1
  144. package/dist/dashboard/trigger-api.js +48 -10
  145. package/dist/dashboard/trigger-api.js.map +1 -1
  146. package/dist/dashboard/web/app.d.ts.map +1 -1
  147. package/dist/dashboard/web/app.js +286 -271
  148. package/dist/dashboard/web/app.js.map +1 -1
  149. package/dist/dashboard/web/bot-defaults-page.d.ts +26 -0
  150. package/dist/dashboard/web/bot-defaults-page.d.ts.map +1 -1
  151. package/dist/dashboard/web/bot-defaults-page.js +1408 -12
  152. package/dist/dashboard/web/bot-defaults-page.js.map +1 -1
  153. package/dist/dashboard/web/bot-defaults.d.ts +91 -4
  154. package/dist/dashboard/web/bot-defaults.d.ts.map +1 -1
  155. package/dist/dashboard/web/bot-defaults.js +43 -2141
  156. package/dist/dashboard/web/bot-defaults.js.map +1 -1
  157. package/dist/dashboard/web/bot-onboarding.d.ts +6 -1
  158. package/dist/dashboard/web/bot-onboarding.d.ts.map +1 -1
  159. package/dist/dashboard/web/bot-onboarding.js +261 -306
  160. package/dist/dashboard/web/bot-onboarding.js.map +1 -1
  161. package/dist/dashboard/web/connectors-page.d.ts +4 -0
  162. package/dist/dashboard/web/connectors-page.d.ts.map +1 -1
  163. package/dist/dashboard/web/connectors-page.js +228 -48
  164. package/dist/dashboard/web/connectors-page.js.map +1 -1
  165. package/dist/dashboard/web/dashboard-api.d.ts +9 -0
  166. package/dist/dashboard/web/dashboard-api.d.ts.map +1 -0
  167. package/dist/dashboard/web/dashboard-api.js +18 -0
  168. package/dist/dashboard/web/dashboard-api.js.map +1 -0
  169. package/dist/dashboard/web/dashboard-components.d.ts +92 -0
  170. package/dist/dashboard/web/dashboard-components.d.ts.map +1 -0
  171. package/dist/dashboard/web/dashboard-components.js +250 -0
  172. package/dist/dashboard/web/dashboard-components.js.map +1 -0
  173. package/dist/dashboard/web/dashboard-routes.d.ts.map +1 -1
  174. package/dist/dashboard/web/dashboard-routes.js +30 -183
  175. package/dist/dashboard/web/dashboard-routes.js.map +1 -1
  176. package/dist/dashboard/web/floating-scrollbars.d.ts +2 -0
  177. package/dist/dashboard/web/floating-scrollbars.d.ts.map +1 -0
  178. package/dist/dashboard/web/floating-scrollbars.js +264 -0
  179. package/dist/dashboard/web/floating-scrollbars.js.map +1 -0
  180. package/dist/dashboard/web/groups-api.d.ts +36 -0
  181. package/dist/dashboard/web/groups-api.d.ts.map +1 -0
  182. package/dist/dashboard/web/groups-api.js +44 -0
  183. package/dist/dashboard/web/groups-api.js.map +1 -0
  184. package/dist/dashboard/web/groups-page.d.ts.map +1 -1
  185. package/dist/dashboard/web/groups-page.js +746 -15
  186. package/dist/dashboard/web/groups-page.js.map +1 -1
  187. package/dist/dashboard/web/groups.d.ts +51 -18
  188. package/dist/dashboard/web/groups.d.ts.map +1 -1
  189. package/dist/dashboard/web/groups.js +145 -1096
  190. package/dist/dashboard/web/groups.js.map +1 -1
  191. package/dist/dashboard/web/i18n.d.ts.map +1 -1
  192. package/dist/dashboard/web/i18n.js +545 -255
  193. package/dist/dashboard/web/i18n.js.map +1 -1
  194. package/dist/dashboard/web/insights-page.d.ts.map +1 -1
  195. package/dist/dashboard/web/insights-page.js +1047 -14
  196. package/dist/dashboard/web/insights-page.js.map +1 -1
  197. package/dist/dashboard/web/insights.d.ts +177 -7
  198. package/dist/dashboard/web/insights.d.ts.map +1 -1
  199. package/dist/dashboard/web/insights.js +187 -1926
  200. package/dist/dashboard/web/insights.js.map +1 -1
  201. package/dist/dashboard/web/legacy-workflow-page.d.ts +3 -0
  202. package/dist/dashboard/web/legacy-workflow-page.d.ts.map +1 -0
  203. package/dist/dashboard/web/legacy-workflow-page.js +717 -0
  204. package/dist/dashboard/web/legacy-workflow-page.js.map +1 -0
  205. package/dist/dashboard/web/monitor-room.d.ts.map +1 -1
  206. package/dist/dashboard/web/monitor-room.js +10 -8
  207. package/dist/dashboard/web/monitor-room.js.map +1 -1
  208. package/dist/dashboard/web/monitoring-page.d.ts.map +1 -1
  209. package/dist/dashboard/web/monitoring-page.js +188 -14
  210. package/dist/dashboard/web/monitoring-page.js.map +1 -1
  211. package/dist/dashboard/web/office-page.d.ts.map +1 -1
  212. package/dist/dashboard/web/office-page.js +11 -23
  213. package/dist/dashboard/web/office-page.js.map +1 -1
  214. package/dist/dashboard/web/overview-page.d.ts.map +1 -1
  215. package/dist/dashboard/web/overview-page.js +141 -147
  216. package/dist/dashboard/web/overview-page.js.map +1 -1
  217. package/dist/dashboard/web/overview.d.ts +0 -1
  218. package/dist/dashboard/web/overview.d.ts.map +1 -1
  219. package/dist/dashboard/web/overview.js +3 -275
  220. package/dist/dashboard/web/overview.js.map +1 -1
  221. package/dist/dashboard/web/preferences.d.ts +1 -6
  222. package/dist/dashboard/web/preferences.d.ts.map +1 -1
  223. package/dist/dashboard/web/preferences.js +0 -21
  224. package/dist/dashboard/web/preferences.js.map +1 -1
  225. package/dist/dashboard/web/role-batch.d.ts +9 -0
  226. package/dist/dashboard/web/role-batch.d.ts.map +1 -0
  227. package/dist/dashboard/web/role-batch.js +42 -0
  228. package/dist/dashboard/web/role-batch.js.map +1 -0
  229. package/dist/dashboard/web/roles-page.d.ts.map +1 -1
  230. package/dist/dashboard/web/roles-page.js +538 -13
  231. package/dist/dashboard/web/roles-page.js.map +1 -1
  232. package/dist/dashboard/web/roles.d.ts +98 -1
  233. package/dist/dashboard/web/roles.d.ts.map +1 -1
  234. package/dist/dashboard/web/roles.js +117 -903
  235. package/dist/dashboard/web/roles.js.map +1 -1
  236. package/dist/dashboard/web/schedules-page.d.ts.map +1 -1
  237. package/dist/dashboard/web/schedules-page.js +82 -24
  238. package/dist/dashboard/web/schedules-page.js.map +1 -1
  239. package/dist/dashboard/web/sessions-kanban.d.ts +67 -0
  240. package/dist/dashboard/web/sessions-kanban.d.ts.map +1 -0
  241. package/dist/dashboard/web/sessions-kanban.js +481 -0
  242. package/dist/dashboard/web/sessions-kanban.js.map +1 -0
  243. package/dist/dashboard/web/sessions-page.d.ts +4 -0
  244. package/dist/dashboard/web/sessions-page.d.ts.map +1 -1
  245. package/dist/dashboard/web/sessions-page.js +1633 -18
  246. package/dist/dashboard/web/sessions-page.js.map +1 -1
  247. package/dist/dashboard/web/sessions.d.ts +45 -4
  248. package/dist/dashboard/web/sessions.d.ts.map +1 -1
  249. package/dist/dashboard/web/sessions.js +53 -2535
  250. package/dist/dashboard/web/sessions.js.map +1 -1
  251. package/dist/dashboard/web/settings-page.d.ts +0 -2
  252. package/dist/dashboard/web/settings-page.d.ts.map +1 -1
  253. package/dist/dashboard/web/settings-page.js +127 -117
  254. package/dist/dashboard/web/settings-page.js.map +1 -1
  255. package/dist/dashboard/web/skills-page.d.ts +10 -1
  256. package/dist/dashboard/web/skills-page.d.ts.map +1 -1
  257. package/dist/dashboard/web/skills-page.js +235 -93
  258. package/dist/dashboard/web/skills-page.js.map +1 -1
  259. package/dist/dashboard/web/team-federation-page.d.ts.map +1 -1
  260. package/dist/dashboard/web/team-federation-page.js +446 -28
  261. package/dist/dashboard/web/team-federation-page.js.map +1 -1
  262. package/dist/dashboard/web/team-federation.d.ts +91 -2
  263. package/dist/dashboard/web/team-federation.d.ts.map +1 -1
  264. package/dist/dashboard/web/team-federation.js +87 -474
  265. package/dist/dashboard/web/team-federation.js.map +1 -1
  266. package/dist/dashboard/web/theme-menu.d.ts +1 -0
  267. package/dist/dashboard/web/theme-menu.d.ts.map +1 -1
  268. package/dist/dashboard/web/theme-menu.js +79 -20
  269. package/dist/dashboard/web/theme-menu.js.map +1 -1
  270. package/dist/dashboard/web/ui.d.ts +1 -2
  271. package/dist/dashboard/web/ui.d.ts.map +1 -1
  272. package/dist/dashboard/web/ui.js +8 -20
  273. package/dist/dashboard/web/ui.js.map +1 -1
  274. package/dist/dashboard/web/v3-api.d.ts +16 -0
  275. package/dist/dashboard/web/v3-api.d.ts.map +1 -0
  276. package/dist/dashboard/web/v3-api.js +21 -0
  277. package/dist/dashboard/web/v3-api.js.map +1 -0
  278. package/dist/dashboard/web/v3-components.d.ts +2 -0
  279. package/dist/dashboard/web/v3-components.d.ts.map +1 -0
  280. package/dist/dashboard/web/v3-components.js +290 -0
  281. package/dist/dashboard/web/v3-components.js.map +1 -0
  282. package/dist/dashboard/web/v3-model.d.ts +80 -0
  283. package/dist/dashboard/web/v3-model.d.ts.map +1 -0
  284. package/dist/dashboard/web/v3-model.js +240 -0
  285. package/dist/dashboard/web/v3-model.js.map +1 -0
  286. package/dist/dashboard/web/v3-page.d.ts.map +1 -1
  287. package/dist/dashboard/web/v3-page.js +2 -22
  288. package/dist/dashboard/web/v3-page.js.map +1 -1
  289. package/dist/dashboard/web/v3-terminal.d.ts +14 -0
  290. package/dist/dashboard/web/v3-terminal.d.ts.map +1 -1
  291. package/dist/dashboard/web/v3-terminal.js +31 -19
  292. package/dist/dashboard/web/v3-terminal.js.map +1 -1
  293. package/dist/dashboard/web/webhook-logs-page.d.ts +16 -0
  294. package/dist/dashboard/web/webhook-logs-page.d.ts.map +1 -0
  295. package/dist/dashboard/web/webhook-logs-page.js +195 -0
  296. package/dist/dashboard/web/webhook-logs-page.js.map +1 -0
  297. package/dist/dashboard/web/whiteboards-page.d.ts.map +1 -1
  298. package/dist/dashboard/web/whiteboards-page.js +18 -23
  299. package/dist/dashboard/web/whiteboards-page.js.map +1 -1
  300. package/dist/dashboard/web/workflow-version-switch.d.ts +8 -0
  301. package/dist/dashboard/web/workflow-version-switch.d.ts.map +1 -0
  302. package/dist/dashboard/web/workflow-version-switch.js +9 -0
  303. package/dist/dashboard/web/workflow-version-switch.js.map +1 -0
  304. package/dist/dashboard/web/workflows.d.ts +272 -1
  305. package/dist/dashboard/web/workflows.d.ts.map +1 -1
  306. package/dist/dashboard/web/workflows.js +211 -1397
  307. package/dist/dashboard/web/workflows.js.map +1 -1
  308. package/dist/dashboard/webhook-routes.d.ts.map +1 -1
  309. package/dist/dashboard/webhook-routes.js +92 -39
  310. package/dist/dashboard/webhook-routes.js.map +1 -1
  311. package/dist/dashboard-web/app.js +1 -65
  312. package/dist/dashboard-web/brand-logo.png +0 -0
  313. package/dist/dashboard-web/chunks/bot-defaults-page-HSQ6HR3R.js +1 -0
  314. package/dist/dashboard-web/chunks/chunk-6TPODDDC.js +1 -0
  315. package/dist/dashboard-web/chunks/chunk-73EJP2MO.js +1 -0
  316. package/dist/dashboard-web/chunks/chunk-BRTZFELS.js +1 -0
  317. package/dist/dashboard-web/chunks/chunk-EVK77ASG.js +1 -0
  318. package/dist/dashboard-web/chunks/chunk-R3FXBFQH.js +1 -0
  319. package/dist/dashboard-web/chunks/chunk-VRLL7SNU.js +1 -0
  320. package/dist/dashboard-web/chunks/chunk-XCPSHD3G.js +37 -0
  321. package/dist/dashboard-web/chunks/chunk-XJ7BGPJH.js +1 -0
  322. package/dist/dashboard-web/chunks/chunk-YVRWYGXR.js +1 -0
  323. package/dist/dashboard-web/chunks/chunk-ZJING6FY.js +66 -0
  324. package/dist/dashboard-web/chunks/connectors-page-ODUA5UCW.js +1 -0
  325. package/dist/dashboard-web/chunks/groups-page-OYX5M6NK.js +8 -0
  326. package/dist/dashboard-web/chunks/insights-page-YCQO6OCR.js +22 -0
  327. package/dist/dashboard-web/chunks/legacy-workflow-page-XJAUNONP.js +2 -0
  328. package/dist/dashboard-web/chunks/{monitor-room-RFX4HPS5.js → monitor-room-WTZNYAFV.js} +17 -15
  329. package/dist/dashboard-web/chunks/monitoring-page-R6LJUK7G.js +2 -0
  330. package/dist/dashboard-web/chunks/office-page-4OXK3GNR.js +1 -0
  331. package/dist/dashboard-web/chunks/overview-page-4MVEJY6U.js +1 -0
  332. package/dist/dashboard-web/chunks/roles-page-NQDW27VV.js +1 -0
  333. package/dist/dashboard-web/chunks/schedules-page-JRNKYHOX.js +1 -0
  334. package/dist/dashboard-web/chunks/sessions-page-LF7UVI7B.js +3 -0
  335. package/dist/dashboard-web/chunks/settings-page-WGW7KQQS.js +2 -0
  336. package/dist/dashboard-web/chunks/skills-page-AMD4XP3T.js +3 -0
  337. package/dist/dashboard-web/chunks/team-federation-page-SYG3K4GG.js +1 -0
  338. package/dist/dashboard-web/chunks/v3-page-6NFFWNUK.js +94 -0
  339. package/dist/dashboard-web/chunks/whiteboards-page-FBRUK2AS.js +1 -0
  340. package/dist/dashboard-web/favicon.png +0 -0
  341. package/dist/dashboard-web/index.html +5 -69
  342. package/dist/dashboard-web/style.css +18835 -7211
  343. package/dist/dashboard.js +150 -32
  344. package/dist/dashboard.js.map +1 -1
  345. package/dist/global-config.d.ts +2 -2
  346. package/dist/global-config.d.ts.map +1 -1
  347. package/dist/i18n/en.d.ts.map +1 -1
  348. package/dist/i18n/en.js +3 -0
  349. package/dist/i18n/en.js.map +1 -1
  350. package/dist/i18n/zh.d.ts.map +1 -1
  351. package/dist/i18n/zh.js +3 -0
  352. package/dist/i18n/zh.js.map +1 -1
  353. package/dist/im/lark/card-builder.d.ts +7 -5
  354. package/dist/im/lark/card-builder.d.ts.map +1 -1
  355. package/dist/im/lark/card-builder.js +10 -7
  356. package/dist/im/lark/card-builder.js.map +1 -1
  357. package/dist/im/lark/card-handler.d.ts.map +1 -1
  358. package/dist/im/lark/card-handler.js +25 -1
  359. package/dist/im/lark/card-handler.js.map +1 -1
  360. package/dist/im/lark/event-dispatcher.d.ts.map +1 -1
  361. package/dist/im/lark/event-dispatcher.js +84 -0
  362. package/dist/im/lark/event-dispatcher.js.map +1 -1
  363. package/dist/platform/binding.d.ts +9 -0
  364. package/dist/platform/binding.d.ts.map +1 -1
  365. package/dist/platform/binding.js +14 -0
  366. package/dist/platform/binding.js.map +1 -1
  367. package/dist/platform/tunnel-client.d.ts.map +1 -1
  368. package/dist/platform/tunnel-client.js +9 -0
  369. package/dist/platform/tunnel-client.js.map +1 -1
  370. package/dist/services/bot-config-store.d.ts +1 -1
  371. package/dist/services/bot-config-store.d.ts.map +1 -1
  372. package/dist/services/bot-config-store.js +11 -5
  373. package/dist/services/bot-config-store.js.map +1 -1
  374. package/dist/services/file-bridge-path.d.ts +8 -0
  375. package/dist/services/file-bridge-path.d.ts.map +1 -0
  376. package/dist/services/file-bridge-path.js +75 -0
  377. package/dist/services/file-bridge-path.js.map +1 -0
  378. package/dist/services/grok-paths.d.ts +27 -0
  379. package/dist/services/grok-paths.d.ts.map +1 -0
  380. package/dist/services/grok-paths.js +106 -0
  381. package/dist/services/grok-paths.js.map +1 -0
  382. package/dist/services/grok-transcript.d.ts +67 -0
  383. package/dist/services/grok-transcript.d.ts.map +1 -0
  384. package/dist/services/grok-transcript.js +630 -0
  385. package/dist/services/grok-transcript.js.map +1 -0
  386. package/dist/services/hermes-session-filter.d.ts +28 -0
  387. package/dist/services/hermes-session-filter.d.ts.map +1 -0
  388. package/dist/services/hermes-session-filter.js +53 -0
  389. package/dist/services/hermes-session-filter.js.map +1 -0
  390. package/dist/services/hermes-transcript.d.ts +3 -0
  391. package/dist/services/hermes-transcript.d.ts.map +1 -1
  392. package/dist/services/hermes-transcript.js +20 -2
  393. package/dist/services/hermes-transcript.js.map +1 -1
  394. package/dist/services/resumable-session-discovery.d.ts +3 -0
  395. package/dist/services/resumable-session-discovery.d.ts.map +1 -1
  396. package/dist/services/resumable-session-discovery.js +6 -1
  397. package/dist/services/resumable-session-discovery.js.map +1 -1
  398. package/dist/services/skill-registry-store.d.ts +9 -0
  399. package/dist/services/skill-registry-store.d.ts.map +1 -1
  400. package/dist/services/skill-registry-store.js +304 -1
  401. package/dist/services/skill-registry-store.js.map +1 -1
  402. package/dist/services/structured-bridge-clis.d.ts +29 -0
  403. package/dist/services/structured-bridge-clis.d.ts.map +1 -0
  404. package/dist/services/structured-bridge-clis.js +76 -0
  405. package/dist/services/structured-bridge-clis.js.map +1 -0
  406. package/dist/services/trigger-log-store.d.ts +59 -0
  407. package/dist/services/trigger-log-store.d.ts.map +1 -1
  408. package/dist/services/trigger-log-store.js +81 -10
  409. package/dist/services/trigger-log-store.js.map +1 -1
  410. package/dist/services/usage-ledger.d.ts +4 -1
  411. package/dist/services/usage-ledger.d.ts.map +1 -1
  412. package/dist/services/usage-ledger.js +48 -10
  413. package/dist/services/usage-ledger.js.map +1 -1
  414. package/dist/services/webhook-audit.d.ts +10 -0
  415. package/dist/services/webhook-audit.d.ts.map +1 -0
  416. package/dist/services/webhook-audit.js +116 -0
  417. package/dist/services/webhook-audit.js.map +1 -0
  418. package/dist/setup/bot-config-editor.d.ts.map +1 -1
  419. package/dist/setup/bot-config-editor.js +2 -0
  420. package/dist/setup/bot-config-editor.js.map +1 -1
  421. package/dist/setup/ensure-tmux.d.ts +26 -12
  422. package/dist/setup/ensure-tmux.d.ts.map +1 -1
  423. package/dist/setup/ensure-tmux.js +85 -18
  424. package/dist/setup/ensure-tmux.js.map +1 -1
  425. package/dist/skills/definitions.d.ts.map +1 -1
  426. package/dist/skills/definitions.js +14 -1
  427. package/dist/skills/definitions.js.map +1 -1
  428. package/dist/skills/injection-mode.d.ts +16 -0
  429. package/dist/skills/injection-mode.d.ts.map +1 -1
  430. package/dist/skills/injection-mode.js +25 -0
  431. package/dist/skills/injection-mode.js.map +1 -1
  432. package/dist/types.d.ts +9 -0
  433. package/dist/types.d.ts.map +1 -1
  434. package/dist/utils/global-install.d.ts +22 -0
  435. package/dist/utils/global-install.d.ts.map +1 -0
  436. package/dist/utils/global-install.js +106 -0
  437. package/dist/utils/global-install.js.map +1 -0
  438. package/dist/utils/install-diagnostics.d.ts +1 -1
  439. package/dist/utils/install-diagnostics.d.ts.map +1 -1
  440. package/dist/utils/install-diagnostics.js +6 -4
  441. package/dist/utils/install-diagnostics.js.map +1 -1
  442. package/dist/utils/install-info.d.ts +2 -0
  443. package/dist/utils/install-info.d.ts.map +1 -1
  444. package/dist/utils/install-info.js +10 -4
  445. package/dist/utils/install-info.js.map +1 -1
  446. package/dist/worker.js +293 -172
  447. package/dist/worker.js.map +1 -1
  448. package/dist/workflows/events/payloads.d.ts +10 -10
  449. package/dist/workflows/events/schema.d.ts +28 -28
  450. package/package.json +8 -7
  451. package/dist/dashboard/web/connectors.d.ts +0 -2
  452. package/dist/dashboard/web/connectors.d.ts.map +0 -1
  453. package/dist/dashboard/web/connectors.js +0 -2
  454. package/dist/dashboard/web/connectors.js.map +0 -1
  455. package/dist/dashboard/web/office.d.ts +0 -2
  456. package/dist/dashboard/web/office.d.ts.map +0 -1
  457. package/dist/dashboard/web/office.js +0 -2
  458. package/dist/dashboard/web/office.js.map +0 -1
  459. package/dist/dashboard/web/schedules.d.ts +0 -2
  460. package/dist/dashboard/web/schedules.d.ts.map +0 -1
  461. package/dist/dashboard/web/schedules.js +0 -2
  462. package/dist/dashboard/web/schedules.js.map +0 -1
  463. package/dist/dashboard/web/settings.d.ts +0 -2
  464. package/dist/dashboard/web/settings.d.ts.map +0 -1
  465. package/dist/dashboard/web/settings.js +0 -2
  466. package/dist/dashboard/web/settings.js.map +0 -1
  467. package/dist/dashboard/web/skills.d.ts +0 -2
  468. package/dist/dashboard/web/skills.d.ts.map +0 -1
  469. package/dist/dashboard/web/skills.js +0 -2
  470. package/dist/dashboard/web/skills.js.map +0 -1
  471. package/dist/dashboard/web/skin-intro.d.ts +0 -4
  472. package/dist/dashboard/web/skin-intro.d.ts.map +0 -1
  473. package/dist/dashboard/web/skin-intro.js +0 -49
  474. package/dist/dashboard/web/skin-intro.js.map +0 -1
  475. package/dist/dashboard/web/v3.d.ts +0 -3
  476. package/dist/dashboard/web/v3.d.ts.map +0 -1
  477. package/dist/dashboard/web/v3.js +0 -539
  478. package/dist/dashboard/web/v3.js.map +0 -1
  479. package/dist/dashboard/web/whiteboards.d.ts +0 -2
  480. package/dist/dashboard/web/whiteboards.d.ts.map +0 -1
  481. package/dist/dashboard/web/whiteboards.js +0 -2
  482. package/dist/dashboard/web/whiteboards.js.map +0 -1
  483. package/dist/dashboard-web/chunks/bot-defaults-page-OCIHPGEU.js +0 -438
  484. package/dist/dashboard-web/chunks/chunk-3N2PPQVD.js +0 -1
  485. package/dist/dashboard-web/chunks/chunk-3UJYRR2Q.js +0 -37
  486. package/dist/dashboard-web/chunks/chunk-5E3MJJL4.js +0 -1
  487. package/dist/dashboard-web/chunks/chunk-7SZRL6FY.js +0 -1
  488. package/dist/dashboard-web/chunks/chunk-JTSE5VP6.js +0 -54
  489. package/dist/dashboard-web/chunks/chunk-LXIK7OUJ.js +0 -1
  490. package/dist/dashboard-web/chunks/chunk-PE6W3C2C.js +0 -1
  491. package/dist/dashboard-web/chunks/chunk-QW54DWU3.js +0 -325
  492. package/dist/dashboard-web/chunks/connectors-IGETYWCP.js +0 -1
  493. package/dist/dashboard-web/chunks/groups-page-MYBS4ZYJ.js +0 -207
  494. package/dist/dashboard-web/chunks/insights-page-UPA32V2S.js +0 -309
  495. package/dist/dashboard-web/chunks/monitoring-page-7T3BESZ2.js +0 -1
  496. package/dist/dashboard-web/chunks/office-page-6DXYOG55.js +0 -1
  497. package/dist/dashboard-web/chunks/overview-page-LJSBCQW4.js +0 -1
  498. package/dist/dashboard-web/chunks/roles-page-ESURTVQH.js +0 -98
  499. package/dist/dashboard-web/chunks/schedules-LML2ISOT.js +0 -1
  500. package/dist/dashboard-web/chunks/sessions-YB73XOTU.js +0 -1
  501. package/dist/dashboard-web/chunks/sessions-page-MCO5QR7Z.js +0 -1
  502. package/dist/dashboard-web/chunks/settings-page-OSDKNVS3.js +0 -2
  503. package/dist/dashboard-web/chunks/skills-page-PXBZJ6P6.js +0 -2
  504. package/dist/dashboard-web/chunks/team-federation-page-TTPVHFPX.js +0 -10
  505. package/dist/dashboard-web/chunks/v3-page-FVPUL665.js +0 -149
  506. package/dist/dashboard-web/chunks/whiteboards-page-HLCTYSSV.js +0 -1
  507. package/dist/dashboard-web/chunks/workflows-FCPM4S4D.js +0 -184
  508. package/dist/dashboard-web/skins/bluearchive-hero.webp +0 -0
  509. package/dist/dashboard-web/skins/dragonball-goku.webp +0 -0
  510. package/dist/dashboard-web/skins/dragonball-wukong.webp +0 -0
  511. package/dist/dashboard-web/skins/genshin-breeze.webp +0 -0
  512. package/dist/dashboard-web/skins/ikun-hero.webp +0 -0
  513. package/dist/dashboard-web/skins/prts-priestess.webp +0 -0
  514. package/dist/dashboard-web/skins/zzz-hero.webp +0 -0
  515. package/dist/dashboard-web/skins/zzz-pattern.svg +0 -25
@@ -1,24 +1,755 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useEffect, useRef } from 'react';
3
- import { loadingHtml } from './ui.js';
4
- import { useT } from './react-hooks.js';
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { memo, useCallback, useEffect, useMemo, useRef, useState, } from 'react';
5
3
  import { mountReactPage } from './react-mount.js';
6
- import { wireGroupsPage } from './groups.js';
7
- function Html(props) {
8
- return _jsx("span", { style: { display: 'contents' }, dangerouslySetInnerHTML: { __html: props.html } });
4
+ import { useT } from './react-hooks.js';
5
+ import { botOrbStyle, chatAvatarUrlFor } from './ui.js';
6
+ import { CreateActionButton, DropdownMenu, LoadingState, OverviewList, OverviewListItem, OverviewListMain, OverviewListTail, RefreshIconButton, } from './dashboard-components.js';
7
+ import { hasExplicitChatRole, summarizeGroupProfileMatches, } from './role-profile-match.js';
8
+ import { allExpectedInChat, availableBotsForPicker, collectGroupProfileEntries, emptyGroupsSnapshot, fetchGroupsSnapshot, fetchRoleProfileSummaries, filterGroupChats, injectOptimisticChat, isValidProfileId, loadGroupRoleProfileContext, paginateGroupRows, roleKey, roleProfileBootstrapStatus, summarizeAddBotsResult, suggestRoleProfileIdFromChat, } from './groups.js';
9
+ function emptyRoleContext() {
10
+ return {
11
+ profiles: [],
12
+ entriesById: new Map(),
13
+ groupRoleContentByBot: new Map(),
14
+ loaded: false,
15
+ };
16
+ }
17
+ function orbVars(name) {
18
+ const style = {};
19
+ for (const part of botOrbStyle(name).split(';')) {
20
+ if (!part)
21
+ continue;
22
+ const i = part.indexOf(':');
23
+ if (i <= 0)
24
+ continue;
25
+ style[part.slice(0, i)] = part.slice(i + 1);
26
+ }
27
+ return style;
28
+ }
29
+ function ChatAvatar(props) {
30
+ const chat = props.chat;
31
+ const url = chat.avatar ?? chatAvatarUrlFor(chat.chatId);
32
+ const [broken, setBroken] = useState(false);
33
+ useEffect(() => { setBroken(false); }, [url]);
34
+ const name = chat.name ?? chat.chatId;
35
+ const hasImage = !!url && !broken;
36
+ return (_jsx("span", { className: `orb-avatar orb-square orb-avatar-sm${hasImage ? ' orb-has-img' : ''}`, style: orbVars(name), "aria-hidden": "true", children: hasImage ? (_jsx("img", { className: "orb-img", src: url, alt: "", decoding: "async", referrerPolicy: "no-referrer", onError: () => setBroken(true) })) : null }));
37
+ }
38
+ function DialogError(props) {
39
+ return (_jsxs("p", { className: "hint-warn", children: [_jsx("strong", { children: props.title }), _jsx("br", {}), _jsx("small", { children: String(props.reason ?? 'unknown') })] }));
40
+ }
41
+ function BotCheckboxes(props) {
42
+ return (_jsx(_Fragment, { children: availableBotsForPicker(props.bots, props.excludeIds).map(bot => (_jsxs("label", { className: "checkbox-row", children: [_jsx("input", { type: "checkbox", name: "bot", value: bot.larkAppId }), _jsxs("span", { className: "checkbox-row-main", children: [_jsx("strong", { children: bot.botName ?? bot.larkAppId }), _jsxs("small", { children: ["(", bot.larkAppId, ")"] })] })] }, bot.larkAppId))) }));
43
+ }
44
+ function AddBotsResult(props) {
45
+ const summary = props.summary;
46
+ if (!summary.rows.length) {
47
+ return _jsx("p", { className: "hint-warn", children: "\u6CA1\u6709\u8FD4\u56DE\u6DFB\u52A0\u7ED3\u679C\u3002" });
48
+ }
49
+ return (_jsxs("div", { className: summary.failed ? 'hint-warn' : 'hint-ok', children: [_jsxs("strong", { children: ["\u6DFB\u52A0\u7ED3\u679C\uFF1A\u6210\u529F ", summary.okCount, "/", summary.rows.length, summary.failed ? `,失败 ${summary.failed}` : ''] }), _jsx("ul", { children: summary.rows.map((row, index) => {
50
+ const id = String(row?.id ?? '?');
51
+ return (_jsxs("li", { children: [_jsx("code", { children: id }), ": ", row?.ok ? 'OK' : `failed (${String(row?.error ?? 'unknown')})`] }, `${id}-${index}`));
52
+ }) })] }));
53
+ }
54
+ function GroupProfileStatus(props) {
55
+ const { chat, context, tr } = props;
56
+ if (!context.profiles.length || !context.loaded)
57
+ return null;
58
+ const rolesByBot = new Map();
59
+ for (const bot of chat.memberBots ?? []) {
60
+ if (!bot?.inChat)
61
+ continue;
62
+ rolesByBot.set(bot.larkAppId, context.groupRoleContentByBot.get(roleKey(bot.larkAppId, chat.chatId)) ?? null);
63
+ }
64
+ if (!hasExplicitChatRole(rolesByBot))
65
+ return null;
66
+ const matches = summarizeGroupProfileMatches(chat.memberBots ?? [], context.profiles, context.entriesById, rolesByBot);
67
+ const best = matches[0];
68
+ if (!best) {
69
+ return _jsx("div", { className: "g-profile-status muted", children: tr('groups.profileStatusUnmatched') });
70
+ }
71
+ const key = best.kind === 'full' ? 'groups.profileStatusFullChat' : 'groups.profileStatusPartial';
72
+ return (_jsx("div", { className: `g-profile-status ${best.kind}`, children: tr(key, {
73
+ name: best.profileId,
74
+ matched: best.matched,
75
+ total: best.total,
76
+ chat: best.chatMatched,
77
+ }) }));
78
+ }
79
+ function groupBotStatus(member) {
80
+ if (!member)
81
+ return 'unknown';
82
+ if (member.error)
83
+ return 'error';
84
+ return member.inChat ? 'in' : 'out';
85
+ }
86
+ function groupBotStatusLabel(status, tr) {
87
+ switch (status) {
88
+ case 'in': return tr('groups.botStatusIn');
89
+ case 'out': return tr('groups.botStatusOut');
90
+ case 'error': return tr('groups.botStatusError');
91
+ case 'unknown':
92
+ default:
93
+ return tr('groups.botStatusUnknown');
94
+ }
95
+ }
96
+ function GroupBotCoverage(props) {
97
+ const members = new Map((props.chat.memberBots ?? []).map(member => [member.larkAppId, member]));
98
+ return (_jsx("div", { className: "groups-bot-strip", "aria-label": props.tr('groups.botCoverage'), children: props.bots.map(bot => {
99
+ const member = members.get(bot.larkAppId);
100
+ const status = groupBotStatus(member);
101
+ const label = groupBotStatusLabel(status, props.tr);
102
+ const name = bot.botName ?? bot.larkAppId;
103
+ return (_jsxs("span", { className: `groups-bot-pill groups-bot-${status}`, title: `${name}: ${label}${member?.error ? ` (${String(member.error)})` : ''}`, children: [_jsx("i", { "aria-hidden": "true" }), _jsx("span", { className: "groups-bot-name", children: name }), _jsx("span", { className: "groups-bot-state", children: label })] }, bot.larkAppId));
104
+ }) }));
105
+ }
106
+ const GroupListRow = memo(function GroupListRow(props) {
107
+ const { chat, tr } = props;
108
+ const members = chat.memberBots ?? [];
109
+ const inCount = members.filter(member => member.inChat).length;
110
+ return (_jsxs(OverviewListItem, { kind: "group", className: "groups-list-row", "data-chat": chat.chatId, children: [_jsx(ChatAvatar, { chat: chat }), _jsxs(OverviewListMain, { children: [_jsxs("div", { className: "groups-row-head", children: [_jsx("b", { children: chat.name ?? chat.chatId }), _jsxs("span", { className: "groups-row-meta", children: [_jsx("span", { className: "groups-row-tag", children: _jsx("code", { children: chat.chatId }) }), chat.ownerId ? (_jsxs("span", { className: "groups-row-tag groups-row-owner-tag", children: [_jsx("span", { children: tr('groups.owner') }), _jsx("code", { children: chat.ownerId })] })) : null] })] }), _jsx(GroupProfileStatus, { chat: chat, context: props.roleContext, tr: tr })] }), _jsx("span", { className: "groups-row-count", children: tr('groups.memberSummary', { count: inCount, total: props.bots.length }) }), _jsxs("div", { className: "groups-row-lower", children: [_jsx(GroupBotCoverage, { chat: chat, bots: props.bots, tr: tr }), _jsxs(OverviewListTail, { children: [_jsx(CreateActionButton, { className: "add-bots", onClick: () => props.onAddBots(chat), children: tr('groups.addBots') }), _jsx("button", { className: "save-profile", type: "button", onClick: () => props.onSaveProfile(chat), children: tr('groups.saveAsProfile') }), _jsx("button", { className: "manage-chat", type: "button", onClick: () => props.onManage(chat), children: tr('groups.manage') })] })] })] }));
111
+ });
112
+ function CreateDialog(props) {
113
+ const { tr } = props;
114
+ const [submitting, setSubmitting] = useState(false);
115
+ const [error, setError] = useState(null);
116
+ const [success, setSuccess] = useState(null);
117
+ const [copied, setCopied] = useState(false);
118
+ const [roleProfileId, setRoleProfileId] = useState('');
119
+ async function submit(ev) {
120
+ ev.preventDefault();
121
+ const fd = new FormData(ev.currentTarget);
122
+ const name = String(fd.get('name') ?? '').trim();
123
+ const bindWorkingDir = String(fd.get('bindWorkingDir') ?? '').trim();
124
+ const roleProfileId = String(fd.get('roleProfileId') ?? '').trim();
125
+ const ids = fd.getAll('bot');
126
+ if (ids.length === 0) {
127
+ setError({ title: '请选择 bot', reason: '至少选择一个 bot 后再创建群聊。' });
128
+ return;
129
+ }
130
+ setSubmitting(true);
131
+ setError(null);
132
+ try {
133
+ const r = await fetch('/api/groups/create', {
134
+ method: 'POST',
135
+ headers: { 'content-type': 'application/json' },
136
+ body: JSON.stringify({
137
+ name: name || undefined,
138
+ larkAppIds: ids,
139
+ bindWorkingDir: bindWorkingDir || undefined,
140
+ roleProfileId: roleProfileId || undefined,
141
+ }),
142
+ });
143
+ const respBody = await r.json().catch(() => ({ ok: false, error: `HTTP ${r.status}` }));
144
+ if (respBody.ok && respBody.chatId) {
145
+ setSuccess(respBody);
146
+ props.onCreated(respBody, ids, name);
147
+ }
148
+ else {
149
+ setError({ title: '创建失败', reason: respBody.error ?? `HTTP ${r.status}` });
150
+ setSubmitting(false);
151
+ }
152
+ }
153
+ catch (err) {
154
+ setError({ title: '网络错误', reason: err });
155
+ setSubmitting(false);
156
+ }
157
+ }
158
+ if (success) {
159
+ const chatId = String(success.chatId);
160
+ const appLink = typeof success.shareLink === 'string' && success.shareLink
161
+ ? success.shareLink
162
+ : `https://applink.feishu.cn/client/chat/open?openChatId=${encodeURIComponent(chatId)}`;
163
+ const invalidBots = (success.invalidBotIds ?? []);
164
+ const invalidUsers = (success.invalidUserIds ?? []);
165
+ const binds = Array.isArray(success.oncallBindings) ? success.oncallBindings : [];
166
+ const bindOk = binds.filter(b => b?.ok).length;
167
+ const bindFailed = binds.filter(b => !b?.ok);
168
+ const profileStatus = roleProfileBootstrapStatus(typeof success.roleProfileId === 'string' ? success.roleProfileId : '', success.roleProfileBootstrapMessageId, success.roleProfileBootstrapError);
169
+ return (_jsxs("article", { className: "g-create-dialog g-create-success", children: [_jsx("header", { children: _jsx("h3", { children: tr('groups.successTitle') }) }), _jsxs("p", { children: [_jsx("b", { children: "chatId:" }), " ", _jsx("code", { children: chatId }), ' ', _jsx("button", { type: "button", "data-copy": chatId, onClick: () => {
170
+ void navigator.clipboard.writeText(chatId);
171
+ setCopied(true);
172
+ props.setTimer(() => setCopied(false), 800);
173
+ }, children: copied ? tr('sessions.copied') : tr('sessions.copy') })] }), _jsxs("p", { children: [_jsx("b", { children: "\u521B\u5EFA\u8005:" }), " ", _jsx("code", { children: String(success.creator ?? '?') })] }), _jsx(CreateInviteNote, { resp: success }), binds.length > 0 ? (bindFailed.length === 0 ? (_jsxs("p", { className: "hint-ok", children: ["\u5DF2\u7ED1\u5B9A\u76EE\u5F55\uFF1A", _jsx("code", { children: String(success.bindResolvedPath ?? '') }), "\uFF08", bindOk, "/", binds.length, " bots\uFF09"] })) : (_jsxs("p", { className: "hint-warn", children: ["\u76EE\u5F55\u7ED1\u5B9A\u90E8\u5206\u5931\u8D25\uFF1A\u6210\u529F ", bindOk, "/", binds.length, "\u3002", bindFailed.map((b, index) => (_jsxs("span", { children: [_jsx("br", {}), _jsx("code", { children: String(b?.larkAppId ?? '?') }), ": ", String(b?.error ?? 'unknown')] }, `${b?.larkAppId ?? '?'}-${index}`)))] }))) : null, profileStatus ? _jsx("p", { className: profileStatus.kind === 'ok' ? 'hint-ok' : 'hint-warn', children: profileStatus.text }) : null, invalidBots.length || invalidUsers.length ? (_jsxs("ul", { children: [invalidBots.length ? _jsxs("li", { children: ["\u65E0\u6548 bot id: ", _jsx("code", { children: invalidBots.join(', ') })] }) : null, invalidUsers.length ? _jsxs("li", { children: ["\u65E0\u6548\u7528\u6237 open_id: ", _jsx("code", { children: invalidUsers.join(', ') })] }) : null] })) : null, _jsxs("div", { className: "actions", children: [_jsx("button", { type: "button", id: "g-create-close", onClick: props.onClose, children: tr('sessions.dismiss') }), _jsx("a", { className: "btn-link primary", href: appLink, target: "_blank", rel: "noopener", children: tr('groups.openGroup') })] })] }));
174
+ }
175
+ return (_jsxs("article", { className: "g-create-dialog", children: [_jsxs("header", { className: "g-create-head", children: [_jsx("h3", { children: tr('groups.createTitle') }), _jsx("p", { children: tr('groups.createHelp') })] }), _jsxs("form", { id: "g-createform", className: "g-create-form", onSubmit: ev => void submit(ev), children: [_jsxs("fieldset", { className: "g-modal-field g-create-bots", children: [_jsx("legend", { children: tr('groups.botPicker') }), _jsx("div", { className: "g-bot-picker", children: _jsx(BotCheckboxes, { bots: props.bots }) })] }), _jsxs("div", { className: "g-create-fields", children: [_jsxs("fieldset", { className: "g-modal-field", children: [_jsx("legend", { children: tr('groups.name') }), _jsx("input", { type: "text", name: "name", placeholder: tr('groups.namePlaceholder'), maxLength: 60 })] }), _jsxs("fieldset", { className: "g-modal-field", children: [_jsx("legend", { children: tr('groups.bindDir') }), _jsx("input", { type: "text", name: "bindWorkingDir", placeholder: "e.g. ~/projects/botmux" }), _jsx("small", { children: tr('groups.bindDirHelp') })] }), _jsxs("fieldset", { className: "g-modal-field g-profile-field", children: [_jsx("legend", { children: tr('groups.roleProfile') }), _jsx("input", { type: "hidden", name: "roleProfileId", value: roleProfileId }), _jsx(DropdownMenu, { className: "g-profile-menu", ariaLabel: tr('groups.roleProfile'), label: roleProfileId || tr('groups.roleProfileNone'), value: roleProfileId, options: [
176
+ { value: '', label: tr('groups.roleProfileNone') },
177
+ ...props.roleProfiles.map(profile => ({ value: profile.profileId, label: profile.profileId })),
178
+ ], onChange: setRoleProfileId }), _jsx("small", { children: tr('groups.roleProfileHelp') })] })] }), _jsx("div", { className: "g-create-status", "data-create-status": true, "aria-live": "polite", children: error ? _jsx(DialogError, { ...error }) : null }), _jsxs("div", { className: "actions g-create-actions", children: [_jsx("button", { type: "button", id: "g-create-cancel", onClick: props.onClose, children: tr('groups.cancel') }), _jsx("button", { type: "submit", className: "primary", disabled: submitting, children: submitting ? _jsxs(_Fragment, { children: [_jsx("i", { className: "button-spinner", "aria-hidden": "true" }), tr('groups.createSubmitting')] }) : tr('groups.createSubmit') })] })] })] }));
179
+ }
180
+ function CreateInviteNote(props) {
181
+ const resp = props.resp;
182
+ const auto = resp.autoInvitedOpenId;
183
+ const rejected = !!resp.autoInviteRejected;
184
+ const ownerTo = resp.ownerTransferredTo;
185
+ const transferErr = resp.transferError;
186
+ const notifyMsgId = resp.notifyMessageId;
187
+ const notifyErr = resp.notifyError;
188
+ if (auto) {
189
+ return (_jsxs("p", { className: "hint-ok", children: ["\u5DF2\u81EA\u52A8\u9080\u8BF7\u4F60\uFF08", _jsx("code", { children: auto }), "\uFF09\u4F5C\u4E3A\u6210\u5458\u3002", ownerTo ? _jsxs(_Fragment, { children: [_jsx("br", {}), _jsx("small", { children: "\u7FA4\u4E3B\u5DF2\u4ECE\u673A\u5668\u4EBA\u8F6C\u8BA9\u7ED9\u4F60\u3002" })] }) : null, transferErr ? _jsxs(_Fragment, { children: [_jsx("br", {}), _jsxs("small", { className: "hint-warn-inline", children: ["\u26A0 \u81EA\u52A8\u8F6C\u8BA9\u7FA4\u4E3B\u5931\u8D25\uFF08", transferErr, "\uFF09\uFF0C\u4F60\u73B0\u5728\u662F\u6210\u5458\u4F46\u7FA4\u4E3B\u4ECD\u662F\u673A\u5668\u4EBA\u3002"] })] }) : null, notifyMsgId ? _jsxs(_Fragment, { children: [_jsx("br", {}), _jsxs("small", { children: ["\u673A\u5668\u4EBA\u5DF2\u5728\u7FA4\u91CC @ \u4E86\u4F60\uFF08\u6D88\u606F id ", _jsx("code", { children: notifyMsgId }), "\uFF09\uFF0C\u770B\u98DE\u4E66\u901A\u77E5\u5C31\u80FD\u8FDB\u7FA4\u3002"] })] }) : null, notifyErr ? _jsxs(_Fragment, { children: [_jsx("br", {}), _jsxs("small", { className: "hint-warn-inline", children: ["\u26A0 \u81EA\u52A8 @ \u901A\u77E5\u5931\u8D25\uFF08", notifyErr, "\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"] })] }) : null] }));
190
+ }
191
+ if (rejected) {
192
+ return (_jsxs("p", { className: "hint-warn", children: ["\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", _jsx("strong", { children: "\u4F60\u76EE\u524D\u4E0D\u662F\u65B0\u7FA4\u6210\u5458" }), "\uFF0C\u9700\u8981\u8BA9\u7FA4\u91CC\u7684\u67D0\u4E2A\u673A\u5668\u4EBA\u624B\u52A8\u628A\u4F60\u52A0\u8FDB\u6765\u3002"] }));
193
+ }
194
+ return (_jsxs("p", { className: "hint-warn", children: ["\u6CA1\u5728 dashboard \u7F13\u5B58\u91CC\u627E\u5230 ownerOpenId\uFF0C", _jsx("strong", { children: "\u6CA1\u6709\u81EA\u52A8\u9080\u8BF7\u4F60" }), "\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"] }));
195
+ }
196
+ function AddBotsDialog(props) {
197
+ const { chat, tr } = props;
198
+ const [submitting, setSubmitting] = useState(false);
199
+ const [error, setError] = useState(null);
200
+ const [summary, setSummary] = useState(null);
201
+ const inChatSet = useMemo(() => new Set((chat.memberBots ?? []).filter(member => member.inChat).map(member => member.larkAppId)), [chat]);
202
+ async function submit(ev) {
203
+ ev.preventDefault();
204
+ const fd = new FormData(ev.currentTarget);
205
+ const ids = fd.getAll('bot');
206
+ if (ids.length === 0) {
207
+ setError({ title: '请选择 bot', reason: '至少选择一个 bot 后再添加。' });
208
+ setSummary(null);
209
+ return;
210
+ }
211
+ setSubmitting(true);
212
+ setError(null);
213
+ setSummary(null);
214
+ try {
215
+ const r = await fetch(`/api/groups/${encodeURIComponent(chat.chatId)}/add-bots`, {
216
+ method: 'POST',
217
+ headers: { 'content-type': 'application/json' },
218
+ body: JSON.stringify({ larkAppIds: ids }),
219
+ });
220
+ const respBody = await r.json();
221
+ if (respBody.error === 'no_proxy_bot') {
222
+ setError({
223
+ title: '无法添加 bot',
224
+ reason: '当前群里没有可代理操作的 bot。请先在飞书里手动拉入一个 bot,然后重试。',
225
+ });
226
+ }
227
+ else if (respBody.result) {
228
+ const result = summarizeAddBotsResult(respBody.result);
229
+ try {
230
+ await props.onReloadGroups({ force: true });
231
+ setSummary({ result });
232
+ }
233
+ catch (err) {
234
+ setSummary({ result, refreshError: `添加结果已返回,但刷新群组列表失败:${err}` });
235
+ }
236
+ }
237
+ else {
238
+ setError({ title: '响应异常', reason: JSON.stringify(respBody) });
239
+ }
240
+ }
241
+ catch (err) {
242
+ setError({ title: '网络错误', reason: err });
243
+ }
244
+ finally {
245
+ setSubmitting(false);
246
+ }
247
+ }
248
+ return (_jsxs("article", { className: "g-add-bots-dialog", children: [_jsx("header", { children: _jsxs("h3", { children: [tr('groups.addBots'), " \u00B7 ", chat.name ?? chat.chatId] }) }), _jsx("p", { children: tr('groups.createHelp') }), _jsxs("form", { id: "g-addform", onSubmit: ev => void submit(ev), children: [_jsx(BotCheckboxes, { bots: props.bots, excludeIds: inChatSet }), _jsxs("div", { "data-add-status": true, "aria-live": "polite", children: [error ? _jsx(DialogError, { ...error }) : null, summary ? (_jsxs(_Fragment, { children: [_jsx(AddBotsResult, { summary: summary.result }), summary.refreshError ? _jsx(DialogError, { title: "\u5237\u65B0\u5931\u8D25", reason: summary.refreshError }) : null] })) : null] }), _jsxs("div", { className: "actions", children: [_jsx("button", { type: "button", id: "g-cancel", onClick: props.onClose, children: tr('groups.cancel') }), _jsx("button", { type: "submit", className: "primary", disabled: submitting, children: submitting ? 'Adding...' : tr('groups.addBots') })] })] })] }));
249
+ }
250
+ function SaveProfileDialog(props) {
251
+ const { tr } = props;
252
+ const [entries, setEntries] = useState([]);
253
+ const [profiles, setProfiles] = useState([]);
254
+ const [loadingSnapshot, setLoadingSnapshot] = useState(true);
255
+ const [loadError, setLoadError] = useState(null);
256
+ const sortedProfiles = useMemo(() => [...profiles].sort((a, b) => a.profileId.localeCompare(b.profileId)), [profiles]);
257
+ const hasExistingProfiles = sortedProfiles.length > 0;
258
+ const emptyCount = entries.filter(entry => entry.status === 'empty').length;
259
+ const failedCount = entries.filter(entry => entry.status === 'error').length;
260
+ const canSubmitSnapshot = !loadingSnapshot && !loadError && entries.length > 0 && failedCount === 0;
261
+ const [selectedMode, setSelectedMode] = useState('new');
262
+ const [profileId, setProfileId] = useState(props.suggestedProfileId);
263
+ const [selectedExistingProfileId, setSelectedExistingProfileId] = useState('');
264
+ const [submitting, setSubmitting] = useState(false);
265
+ const [status, setStatus] = useState(null);
266
+ const currentProfileId = selectedMode === 'overwrite' ? selectedExistingProfileId : profileId.trim();
267
+ const submitText = selectedMode === 'overwrite'
268
+ ? tr('groups.saveProfileOverwriteSubmit')
269
+ : tr('groups.saveProfileSubmit');
270
+ const snapshotSummary = loadError
271
+ ? loadError
272
+ : loadingSnapshot
273
+ ? tr('groups.saveProfilePreparing')
274
+ : failedCount
275
+ ? tr('groups.saveProfileFailedLoadSummary', { count: failedCount })
276
+ : entries.length
277
+ ? emptyCount
278
+ ? tr('groups.saveProfileEntrySummaryWithEmpty', { count: entries.length, emptyCount })
279
+ : tr('groups.saveProfileEntrySummary', { count: entries.length })
280
+ : tr('groups.saveProfileNoRoles');
281
+ useEffect(() => {
282
+ let alive = true;
283
+ setLoadingSnapshot(true);
284
+ setLoadError(null);
285
+ setStatus(null);
286
+ void (async () => {
287
+ try {
288
+ const [nextEntries, nextProfiles] = await Promise.all([
289
+ collectGroupProfileEntries(props.chat),
290
+ fetchRoleProfileSummaries().catch(() => []),
291
+ ]);
292
+ if (!alive)
293
+ return;
294
+ const sortedNextProfiles = [...nextProfiles].sort((a, b) => a.profileId.localeCompare(b.profileId));
295
+ setEntries(nextEntries);
296
+ setProfiles(nextProfiles);
297
+ setSelectedExistingProfileId(cur => sortedNextProfiles.some(profile => profile.profileId === cur)
298
+ ? cur
299
+ : sortedNextProfiles[0]?.profileId ?? '');
300
+ }
301
+ catch (err) {
302
+ if (!alive)
303
+ return;
304
+ setEntries([]);
305
+ setProfiles([]);
306
+ setSelectedExistingProfileId('');
307
+ setLoadError(err instanceof Error ? err.message : String(err));
308
+ }
309
+ finally {
310
+ if (alive)
311
+ setLoadingSnapshot(false);
312
+ }
313
+ })();
314
+ return () => { alive = false; };
315
+ }, [props.chat]);
316
+ async function submit(ev) {
317
+ ev.preventDefault();
318
+ if (!canSubmitSnapshot)
319
+ return;
320
+ if (!isValidProfileId(currentProfileId)) {
321
+ setStatus({ text: tr('groups.saveProfileInvalid'), className: 'error' });
322
+ return;
323
+ }
324
+ setSubmitting(true);
325
+ setStatus({ text: tr('groups.saveProfileSaving') });
326
+ try {
327
+ const results = await Promise.all(entries.map(async (entry) => {
328
+ const r = await fetch(`/api/role-profiles/${encodeURIComponent(currentProfileId)}/${encodeURIComponent(entry.larkAppId)}`, {
329
+ method: 'PUT',
330
+ headers: { 'content-type': 'application/json' },
331
+ body: JSON.stringify({ content: entry.content, allowEmpty: true }),
332
+ });
333
+ return r.ok;
334
+ }));
335
+ const saved = results.filter(Boolean).length;
336
+ if (saved !== entries.length) {
337
+ setStatus({
338
+ text: tr('groups.saveProfileFailed', { saved, total: entries.length }),
339
+ className: 'error',
340
+ });
341
+ setSubmitting(false);
342
+ return;
343
+ }
344
+ setStatus({
345
+ text: tr('groups.saveProfileDone', { name: currentProfileId, count: saved }),
346
+ className: 'ok',
347
+ });
348
+ await props.onRefreshRoleContext();
349
+ props.setTimer(props.onClose, 700);
350
+ }
351
+ catch (err) {
352
+ setStatus({ text: String(err), className: 'error' });
353
+ setSubmitting(false);
354
+ }
355
+ }
356
+ function pickMode(mode) {
357
+ if (mode === 'overwrite' && !hasExistingProfiles)
358
+ return;
359
+ setSelectedMode(mode);
360
+ setStatus(null);
361
+ }
362
+ return (_jsxs("article", { className: "g-save-profile-dialog", children: [_jsxs("header", { children: [_jsx("h3", { children: tr('groups.saveProfileTitle') }), _jsx("p", { children: tr('groups.saveProfileIntro', {
363
+ name: props.chat.name ?? props.chat.chatId,
364
+ count: loadingSnapshot ? '-' : entries.length,
365
+ }) })] }), _jsxs("form", { id: "g-save-profile-form", onSubmit: ev => void submit(ev), children: [_jsxs("section", { className: "g-save-profile-panel", children: [_jsxs("div", { className: "g-save-profile-section-head", children: [_jsx("span", { children: tr('groups.saveProfileScope') }), _jsx("small", { children: tr('groups.saveProfileScopeHelp') })] }), _jsxs("div", { className: "g-save-profile-stats", children: [_jsxs("span", { children: [tr('groups.saveProfileBotCount'), " ", _jsx("strong", { children: loadingSnapshot ? '-' : entries.length })] }), failedCount ? _jsxs("span", { className: "warn", children: [tr('groups.saveProfileLoadFailed'), " ", _jsx("strong", { children: failedCount })] }) : null] }), _jsx("div", { className: "g-save-profile-entry-list", children: loadingSnapshot ? (_jsx("div", { className: "g-save-profile-loading", children: _jsx(LoadingState, { label: tr('common.loading') }) })) : loadError ? (_jsx("div", { className: "g-save-profile-empty", children: loadError })) : entries.length ? entries.map(entry => (_jsxs("div", { className: `g-save-profile-entry ${entry.status === 'error' ? 'error' : ''}`, children: [_jsxs("div", { children: [_jsx("strong", { children: entry.botName ?? entry.larkAppId }), _jsx("code", { children: entry.larkAppId })] }), _jsx("span", { className: `g-save-profile-entry-status ${entry.status === 'error' ? 'error' : 'ok'}`, children: tr(entry.status === 'error' ? 'groups.saveProfileStatus.error' : 'groups.saveProfileStatus.entry') })] }, entry.larkAppId))) : _jsx("div", { className: "g-save-profile-empty", children: tr('groups.saveProfileNoRoles') }) })] }), _jsxs("div", { className: "form-row", children: [_jsx("span", { children: tr('groups.saveProfileMode') }), _jsxs("div", { className: "g-save-profile-switch", role: "tablist", "aria-label": tr('groups.saveProfileMode'), children: [_jsx("button", { type: "button", className: selectedMode === 'new' ? 'active' : '', "data-save-profile-mode": "new", "aria-pressed": selectedMode === 'new', onClick: () => pickMode('new'), children: tr('groups.saveProfileNew') }), _jsx("button", { type: "button", className: selectedMode === 'overwrite' ? 'active' : '', "data-save-profile-mode": "overwrite", "aria-pressed": selectedMode === 'overwrite', disabled: !hasExistingProfiles, onClick: () => pickMode('overwrite'), children: tr('groups.saveProfileOverwrite') })] })] }), _jsxs("label", { className: "form-row", "data-profile-mode-row": "new", hidden: selectedMode !== 'new', children: [_jsx("span", { children: tr('groups.saveProfileIdLabel') }), _jsx("input", { type: "text", name: "profileId", value: profileId, maxLength: 64, autoComplete: "off", onChange: ev => {
366
+ setProfileId(ev.currentTarget.value);
367
+ setStatus(null);
368
+ } }), _jsx("small", { children: tr('groups.saveProfileInvalid') })] }), _jsxs("div", { className: "form-row", "data-profile-mode-row": "overwrite", hidden: selectedMode !== 'overwrite', children: [_jsx("span", { children: tr('groups.saveProfileExistingLabel') }), hasExistingProfiles ? (_jsx("div", { className: "g-save-profile-picker", children: sortedProfiles.map(profile => (_jsxs("button", { type: "button", className: `g-save-profile-pick ${profile.profileId === selectedExistingProfileId ? 'selected' : ''}`, "data-profile-id": profile.profileId, "aria-pressed": profile.profileId === selectedExistingProfileId, onClick: () => {
369
+ setSelectedExistingProfileId(profile.profileId);
370
+ setSelectedMode('overwrite');
371
+ setStatus(null);
372
+ }, children: [_jsx("span", { children: profile.profileId }), _jsx("small", { children: tr('groups.saveProfileExistingMeta', { count: profile.entryCount ?? 0 }) })] }, profile.profileId))) })) : _jsx("div", { className: "g-save-profile-summary warn", children: tr('groups.saveProfileExistingEmpty') }), _jsx("small", { children: tr('groups.saveProfileOverwriteHelp') })] }), _jsxs("div", { className: "g-save-profile-target", children: [_jsx("span", { children: tr('groups.saveProfileTarget') }), _jsx("code", { "data-save-profile-target": true, children: currentProfileId || '-' }), _jsx("small", { "data-save-profile-target-mode": true, children: selectedMode === 'overwrite' ? tr('groups.saveProfileTargetOverwrite') : tr('groups.saveProfileTargetNew') })] }), _jsx("div", { className: `g-save-profile-summary ${canSubmitSnapshot ? '' : 'warn'}`, children: snapshotSummary }), _jsx("div", { className: `g-save-profile-status ${status?.className ?? ''}`, "data-save-profile-status": true, children: status?.text ?? '' }), _jsxs("div", { className: "actions", children: [_jsx("button", { type: "button", id: "g-save-profile-cancel", onClick: props.onClose, children: tr('groups.cancel') }), _jsx("button", { type: "submit", className: "primary", disabled: !canSubmitSnapshot || submitting || (selectedMode === 'overwrite' && !selectedExistingProfileId), children: submitting ? tr('groups.saveProfileSaving') : submitText })] })] })] }));
373
+ }
374
+ function OncallRow(props) {
375
+ const { member, tr } = props;
376
+ const inputRef = useRef(null);
377
+ const [enabled, setEnabled] = useState(!!member.oncallChat);
378
+ const [workingDir, setWorkingDir] = useState(member.oncallChat?.workingDir ?? '');
379
+ const [saving, setSaving] = useState(false);
380
+ const [status, setStatus] = useState(null);
381
+ async function save() {
382
+ setStatus(null);
383
+ const wd = workingDir.trim();
384
+ if (enabled && !wd) {
385
+ setStatus({ text: tr('groups.needWorkingDir'), className: 'hint-warn-inline' });
386
+ return;
387
+ }
388
+ setSaving(true);
389
+ try {
390
+ const url = `/api/groups/${encodeURIComponent(props.chat.chatId)}/oncall/${encodeURIComponent(member.larkAppId)}`;
391
+ const r = enabled
392
+ ? await fetch(url, {
393
+ method: 'PUT',
394
+ headers: { 'content-type': 'application/json' },
395
+ body: JSON.stringify({ workingDir: wd }),
396
+ })
397
+ : await fetch(url, { method: 'DELETE' });
398
+ const body = await r.json().catch(() => ({}));
399
+ if (r.ok && body.ok) {
400
+ setStatus({
401
+ text: enabled ? `✓ 已绑定 → ${body.resolvedPath ?? wd}` : '✓ 已解绑',
402
+ className: 'hint-ok',
403
+ });
404
+ try {
405
+ await props.onSaved();
406
+ }
407
+ catch { /* tolerate */ }
408
+ }
409
+ else {
410
+ setStatus({ text: `✗ ${body.error ?? r.status}`, className: 'hint-warn-inline' });
411
+ }
412
+ }
413
+ catch (err) {
414
+ setStatus({ text: `✗ ${err?.message ?? err}`, className: 'hint-warn-inline' });
415
+ }
416
+ finally {
417
+ setSaving(false);
418
+ }
419
+ }
420
+ return (_jsxs("div", { className: "oncall-row", "data-bot": member.larkAppId, children: [_jsxs("label", { className: "checkbox-row", children: [_jsx("input", { type: "checkbox", "data-action": "toggle", checked: enabled, onChange: ev => {
421
+ setEnabled(ev.currentTarget.checked);
422
+ if (ev.currentTarget.checked)
423
+ window.setTimeout(() => inputRef.current?.focus(), 0);
424
+ } }), _jsx("strong", { children: member.botName ?? member.larkAppId }), _jsxs("small", { children: ["(", member.larkAppId, ")"] })] }), _jsxs("div", { className: "oncall-row-body", children: [_jsx("input", { ref: inputRef, type: "text", "data-input": "workingDir", placeholder: "e.g. /root/iserver/botmux", value: workingDir, disabled: !enabled, onChange: ev => setWorkingDir(ev.currentTarget.value) }), _jsx("button", { type: "button", "data-action": "save", disabled: saving, onClick: () => void save(), children: tr('groups.save') }), _jsx("span", { className: `oncall-status ${status?.className ?? ''}`, "data-status": true, children: status?.text ?? '' })] })] }));
425
+ }
426
+ function ManageDialog(props) {
427
+ const { chat, tr } = props;
428
+ const inChat = (chat.memberBots ?? []).filter(member => member.inChat);
429
+ const ownerAppId = typeof chat.ownerId === 'string' ? chat.ownerId : '';
430
+ const [leaveSelection, setLeaveSelection] = useState(() => new Set());
431
+ function toggleLeave(appId, checked) {
432
+ setLeaveSelection(cur => {
433
+ const next = new Set(cur);
434
+ if (checked)
435
+ next.add(appId);
436
+ else
437
+ next.delete(appId);
438
+ return next;
439
+ });
440
+ }
441
+ async function leaveSelected() {
442
+ const checked = [...leaveSelection];
443
+ if (checked.length === 0) {
444
+ alert('至少选一个机器人');
445
+ return;
446
+ }
447
+ if (!confirm(`确定让 ${checked.length} 个机器人退出群聊?该 bot 在此群的会话会一并关闭。`))
448
+ return;
449
+ try {
450
+ const r = await fetch(`/api/groups/${encodeURIComponent(chat.chatId)}/leave`, {
451
+ method: 'POST',
452
+ headers: { 'content-type': 'application/json' },
453
+ body: JSON.stringify({ larkAppIds: checked }),
454
+ });
455
+ const respBody = await r.json();
456
+ const lines = (respBody.result ?? []).map((x) => {
457
+ if (!x.ok)
458
+ return `${x.larkAppId}: 失败 (${x.error ?? 'unknown'})`;
459
+ const closed = (x.closedSessions ?? []);
460
+ const failed = closed.filter(c => !c.ok).length;
461
+ const ok = closed.length - failed;
462
+ const note = closed.length === 0
463
+ ? ''
464
+ : failed === 0 ? `(关闭 ${ok} 个会话)` : `(关闭 ${ok} 个,${failed} 个失败)`;
465
+ return `${x.larkAppId}: OK${note}`;
466
+ }).join('\n');
467
+ alert(lines || `Unexpected: ${JSON.stringify(respBody)}`);
468
+ await props.onReloadGroups({ force: true });
469
+ }
470
+ catch (err) {
471
+ alert('Network error: ' + err);
472
+ }
473
+ finally {
474
+ props.onClose();
475
+ }
476
+ }
477
+ async function disband() {
478
+ if (inChat.length === 0)
479
+ return;
480
+ if (!confirm(`确定解散群聊「${chat.name ?? chat.chatId}」?此操作不可恢复,本群所有机器人会话也会一并关闭。`))
481
+ return;
482
+ const ordered = [...inChat].sort((a, b) => (b.larkAppId === ownerAppId ? 1 : 0) - (a.larkAppId === ownerAppId ? 1 : 0));
483
+ const errs = [];
484
+ for (const member of ordered) {
485
+ try {
486
+ const r = await fetch(`/api/groups/${encodeURIComponent(chat.chatId)}/disband`, {
487
+ method: 'POST',
488
+ headers: { 'content-type': 'application/json' },
489
+ body: JSON.stringify({ larkAppId: member.larkAppId }),
490
+ });
491
+ const respBody = await r.json();
492
+ if (respBody.ok) {
493
+ const closed = (respBody.closedSessions ?? []);
494
+ const failed = closed.filter(c => !c.ok).length;
495
+ const ok = closed.length - failed;
496
+ const closedNote = closed.length === 0
497
+ ? ''
498
+ : failed === 0 ? `\n关闭了 ${ok} 个会话。` : `\n关闭了 ${ok} 个会话,${failed} 个会话关闭失败。`;
499
+ alert(`已解散(由 ${member.botName ?? member.larkAppId} 执行)${closedNote}`);
500
+ await props.onReloadGroups({ force: true });
501
+ props.onClose();
502
+ return;
503
+ }
504
+ errs.push(`${member.botName ?? member.larkAppId}: ${respBody.error ?? r.status}`);
505
+ }
506
+ catch (err) {
507
+ errs.push(`${member.botName ?? member.larkAppId}: ${err}`);
508
+ }
509
+ }
510
+ alert(`所有在群机器人均无法解散:\n${errs.join('\n')}\n\n建议改用「退出群聊」。`);
511
+ }
512
+ return (_jsxs("article", { className: "g-manage-dialog", children: [_jsx("header", { children: _jsx("h3", { children: tr('groups.manageTitle', { name: chat.name ?? chat.chatId }) }) }), _jsxs("div", { className: "g-manage-meta", children: [_jsxs("span", { children: [_jsx("b", { children: "chatId" }), _jsx("code", { children: chat.chatId })] }), _jsxs("span", { children: [_jsx("b", { children: tr('groups.owner') }), _jsx("code", { children: chat.ownerId ?? tr('common.unknown') })] })] }), _jsxs("fieldset", { children: [_jsx("legend", { children: tr('groups.oncall') }), _jsx("p", { children: _jsx("small", { children: tr('groups.oncallHelp') }) }), inChat.length === 0 ? (_jsx("p", { className: "empty", children: "\u6CA1\u6709\u673A\u5668\u4EBA\u5728\u7FA4\u91CC" })) : inChat.map(member => (_jsx(OncallRow, { chat: chat, member: member, tr: tr, onSaved: async () => { await props.onReloadGroups({ force: true }); } }, member.larkAppId)))] }), _jsxs("fieldset", { children: [_jsx("legend", { children: tr('groups.leaveTitle') }), inChat.length === 0 ? (_jsx("p", { className: "empty", children: "\u6CA1\u6709\u673A\u5668\u4EBA\u5728\u7FA4\u91CC" })) : (_jsx("div", { className: "g-leave-picker", children: inChat.map(member => (_jsxs("label", { className: "checkbox-row", children: [_jsx("input", { type: "checkbox", name: "leave-bot", value: member.larkAppId, checked: leaveSelection.has(member.larkAppId), onChange: ev => toggleLeave(member.larkAppId, ev.currentTarget.checked) }), _jsxs("span", { className: "checkbox-row-main", children: [_jsx("strong", { children: member.botName ?? member.larkAppId }), member.larkAppId === ownerAppId ? _jsx("small", { children: "\u00B7 \u7FA4\u4E3B" }) : null] })] }, member.larkAppId))) }))] }), _jsx("p", { className: "g-manage-danger-hint", children: tr('groups.dangerHint') }), _jsxs("div", { className: "actions", children: [_jsx("button", { type: "button", onClick: props.onClose, children: tr('sessions.dismiss') }), _jsx("button", { id: "g-leave-btn", type: "button", disabled: inChat.length === 0, onClick: () => void leaveSelected(), children: tr('groups.leaveSelected') }), _jsx("button", { id: "g-disband-btn", type: "button", className: "contrast", disabled: inChat.length === 0, onClick: () => void disband(), children: tr('groups.disband') })] })] }));
513
+ }
514
+ function DialogHost(props) {
515
+ const dialogRef = useRef(null);
516
+ useEffect(() => {
517
+ const dialog = dialogRef.current;
518
+ if (!dialog)
519
+ return undefined;
520
+ const onClose = () => props.onClose();
521
+ dialog.addEventListener('close', onClose);
522
+ return () => dialog.removeEventListener('close', onClose);
523
+ }, [props.onClose]);
524
+ useEffect(() => {
525
+ const dialog = dialogRef.current;
526
+ if (!dialog)
527
+ return;
528
+ if (props.dialog && !dialog.open)
529
+ dialog.showModal();
530
+ if (!props.dialog && dialog.open)
531
+ dialog.close();
532
+ }, [props.dialog]);
533
+ let content = null;
534
+ if (props.dialog?.type === 'create') {
535
+ content = (_jsx(CreateDialog, { bots: props.snapshot.bots, roleProfiles: props.dialog.roleProfiles, tr: props.tr, onClose: props.onClose, onCreated: props.onCreated, setTimer: props.setTimer }));
536
+ }
537
+ else if (props.dialog?.type === 'add-bots') {
538
+ content = (_jsx(AddBotsDialog, { chat: props.dialog.chat, bots: props.snapshot.bots, tr: props.tr, onClose: props.onClose, onReloadGroups: props.onReloadGroups }));
539
+ }
540
+ else if (props.dialog?.type === 'save-profile') {
541
+ content = (_jsx(SaveProfileDialog, { chat: props.dialog.chat, suggestedProfileId: props.dialog.suggestedProfileId, tr: props.tr, onClose: props.onClose, onRefreshRoleContext: props.onRefreshRoleContext, setTimer: props.setTimer }));
542
+ }
543
+ else if (props.dialog?.type === 'manage') {
544
+ content = (_jsx(ManageDialog, { chat: props.dialog.chat, tr: props.tr, onClose: props.onClose, onReloadGroups: props.onReloadGroups }));
545
+ }
546
+ const className = props.dialog?.type === 'create' ? 'groups-create-modal' : undefined;
547
+ return (_jsx("dialog", { id: "g-drawer", className: className, ref: dialogRef, onMouseDown: event => {
548
+ if (event.target === event.currentTarget)
549
+ event.currentTarget.close();
550
+ }, children: content }));
9
551
  }
10
- // Render-once scaffold: React owns the translated route shell, while
11
- // wireGroupsPage owns the matrix and dialog flows until those controllers are
12
- // split into reactive components. Keep ui-change rerenders as full remounts.
13
552
  function GroupsPage() {
14
553
  const tr = useT();
15
- const rootRef = useRef(null);
554
+ const mountedRef = useRef(false);
555
+ const snapshotRef = useRef(emptyGroupsSnapshot);
556
+ const timersRef = useRef(new Set());
557
+ const delayResolversRef = useRef(new Map());
558
+ const roleContextRunRef = useRef(0);
559
+ const [snapshot, setSnapshotState] = useState(emptyGroupsSnapshot);
560
+ const [roleContext, setRoleContext] = useState(() => emptyRoleContext());
561
+ const [filters, setFilters] = useState({ q: '', missingOnly: false });
562
+ const [loading, setLoading] = useState(true);
563
+ const [loadError, setLoadError] = useState(null);
564
+ const [refreshing, setRefreshing] = useState(false);
565
+ const [dialog, setDialog] = useState(null);
566
+ const [page, setPage] = useState(1);
567
+ const setSnapshot = useCallback((next) => {
568
+ setSnapshotState(cur => {
569
+ const resolved = typeof next === 'function' ? next(cur) : next;
570
+ snapshotRef.current = resolved;
571
+ return resolved;
572
+ });
573
+ }, []);
574
+ const setTimer = useCallback((fn, ms) => {
575
+ const id = window.setTimeout(() => {
576
+ timersRef.current.delete(id);
577
+ if (mountedRef.current)
578
+ fn();
579
+ }, ms);
580
+ timersRef.current.add(id);
581
+ return id;
582
+ }, []);
583
+ const delay = useCallback((ms) => new Promise(resolve => {
584
+ const id = window.setTimeout(() => {
585
+ timersRef.current.delete(id);
586
+ delayResolversRef.current.delete(id);
587
+ resolve();
588
+ }, ms);
589
+ timersRef.current.add(id);
590
+ delayResolversRef.current.set(id, resolve);
591
+ }), []);
592
+ const refreshRoleProfileContext = useCallback(async (source) => {
593
+ const runId = ++roleContextRunRef.current;
594
+ try {
595
+ const context = await loadGroupRoleProfileContext(source ?? snapshotRef.current);
596
+ if (mountedRef.current && runId === roleContextRunRef.current)
597
+ setRoleContext(context);
598
+ }
599
+ catch {
600
+ if (mountedRef.current && runId === roleContextRunRef.current) {
601
+ setRoleContext({ ...emptyRoleContext(), loaded: true });
602
+ }
603
+ }
604
+ }, []);
605
+ const reloadGroups = useCallback(async (options) => {
606
+ const next = await fetchGroupsSnapshot({ force: options?.force });
607
+ if (!mountedRef.current)
608
+ return next;
609
+ setSnapshot(next);
610
+ setLoadError(null);
611
+ void refreshRoleProfileContext(next);
612
+ return next;
613
+ }, [refreshRoleProfileContext, setSnapshot]);
614
+ const refreshUntilSeen = useCallback(async (chatId, expectedBotIds) => {
615
+ const delays = [600, 1200, 1200, 1200, 1200, 1200];
616
+ for (const ms of delays) {
617
+ await delay(ms);
618
+ if (!mountedRef.current)
619
+ return;
620
+ let next;
621
+ try {
622
+ next = await fetchGroupsSnapshot({ force: true });
623
+ }
624
+ catch {
625
+ continue;
626
+ }
627
+ if (!mountedRef.current)
628
+ return;
629
+ const row = (next.chats ?? []).find(chat => chat.chatId === chatId);
630
+ if (row && allExpectedInChat(row, expectedBotIds)) {
631
+ setSnapshot(next);
632
+ void refreshRoleProfileContext(next);
633
+ return;
634
+ }
635
+ }
636
+ }, [delay, refreshRoleProfileContext, setSnapshot]);
16
637
  useEffect(() => {
17
- if (!rootRef.current)
18
- return undefined;
19
- return wireGroupsPage(rootRef.current);
638
+ mountedRef.current = true;
639
+ void (async () => {
640
+ setLoading(true);
641
+ try {
642
+ await reloadGroups();
643
+ }
644
+ catch (err) {
645
+ if (mountedRef.current) {
646
+ setSnapshot(emptyGroupsSnapshot);
647
+ setLoadError(err instanceof Error ? err.message : String(err));
648
+ void refreshRoleProfileContext(emptyGroupsSnapshot);
649
+ }
650
+ }
651
+ finally {
652
+ if (mountedRef.current)
653
+ setLoading(false);
654
+ }
655
+ })();
656
+ return () => {
657
+ mountedRef.current = false;
658
+ roleContextRunRef.current += 1;
659
+ for (const id of timersRef.current)
660
+ window.clearTimeout(id);
661
+ timersRef.current.clear();
662
+ for (const resolve of delayResolversRef.current.values())
663
+ resolve();
664
+ delayResolversRef.current.clear();
665
+ };
666
+ }, [reloadGroups]);
667
+ const rows = useMemo(() => filterGroupChats(snapshot.chats, filters), [snapshot.chats, filters]);
668
+ const pageWindow = useMemo(() => paginateGroupRows(rows, page), [rows, page]);
669
+ useEffect(() => {
670
+ if (page !== pageWindow.page)
671
+ setPage(pageWindow.page);
672
+ }, [page, pageWindow.page]);
673
+ async function refresh() {
674
+ setRefreshing(true);
675
+ try {
676
+ await reloadGroups({ force: true });
677
+ }
678
+ catch (err) {
679
+ setLoadError(err instanceof Error ? err.message : String(err));
680
+ }
681
+ finally {
682
+ if (mountedRef.current)
683
+ setRefreshing(false);
684
+ }
685
+ }
686
+ async function openCreateDialog() {
687
+ if (snapshotRef.current.bots.length === 0) {
688
+ alert(tr('groups.noBotsOnline'));
689
+ return;
690
+ }
691
+ let roleProfiles = [];
692
+ try {
693
+ roleProfiles = await fetchRoleProfileSummaries();
694
+ }
695
+ catch { /* profile selector is optional */ }
696
+ if (mountedRef.current)
697
+ setDialog({ type: 'create', roleProfiles });
698
+ }
699
+ function handleCreated(resp, selectedIds, name) {
700
+ const chatId = String(resp.chatId);
701
+ const invalidBotIds = Array.isArray(resp.invalidBotIds) ? resp.invalidBotIds : [];
702
+ const validIds = selectedIds.filter(id => !invalidBotIds.includes(id));
703
+ const expectedBotIds = new Set(validIds);
704
+ if (typeof resp.creator === 'string' && resp.creator)
705
+ expectedBotIds.add(resp.creator);
706
+ // Compute the optimistic snapshot from committed state (setSnapshot keeps snapshotRef in
707
+ // sync) so the profile-context refresh always sees it — not a null from a deferred updater.
708
+ const optimistic = injectOptimisticChat(snapshotRef.current, chatId, name || chatId, validIds, resp.creator);
709
+ setSnapshot(optimistic);
710
+ void refreshRoleProfileContext(optimistic);
711
+ void refreshUntilSeen(chatId, expectedBotIds).catch(() => { });
712
+ }
713
+ const openAddBotsDialog = useCallback((chat) => {
714
+ const inChatSet = new Set((chat.memberBots ?? []).filter(member => member.inChat).map(member => member.larkAppId));
715
+ const missing = snapshotRef.current.bots.filter(bot => !inChatSet.has(bot.larkAppId));
716
+ if (!missing.length) {
717
+ alert('All configured bots are already in this chat.');
718
+ return;
719
+ }
720
+ setDialog({ type: 'add-bots', chat });
721
+ }, []);
722
+ const openSaveProfileDialog = useCallback((chat) => {
723
+ const suggestedByName = suggestRoleProfileIdFromChat(chat.name ?? '');
724
+ const suggestedProfileId = suggestedByName === 'profile'
725
+ ? suggestRoleProfileIdFromChat(chat.chatId)
726
+ : suggestedByName;
727
+ setDialog({ type: 'save-profile', chat, suggestedProfileId });
728
+ }, []);
729
+ const openManageDialog = useCallback((chat) => {
730
+ setDialog({ type: 'manage', chat });
20
731
  }, []);
21
- return (_jsxs("section", { className: "page", ref: rootRef, children: [_jsx("div", { className: "page-heading", children: _jsxs("div", { children: [_jsx("p", { className: "eyebrow", children: tr('nav.groups') }), _jsx("h1", { children: tr('groups.title') }), _jsx("p", { children: tr('groups.subtitle') })] }) }), _jsxs("form", { id: "g-filters", className: "filters", children: [_jsx("input", { type: "search", name: "q", placeholder: tr('groups.search') }), _jsxs("label", { children: [_jsx("input", { type: "checkbox", name: "missing" }), " ", tr('groups.missingOnly')] }), _jsx("button", { type: "button", id: "g-refresh", children: tr('groups.refresh') }), _jsx("button", { type: "button", id: "g-create", className: "primary", children: tr('groups.create') })] }), _jsx("div", { id: "g-loading", children: _jsx(Html, { html: loadingHtml() }) }), _jsx("div", { className: "table-scroll matrix-scroll", id: "g-table-wrap", hidden: true, children: _jsxs("table", { children: [_jsx("thead", { id: "g-head" }), _jsx("tbody", { id: "g-body" })] }) }), _jsx("dialog", { id: "g-drawer" })] }));
732
+ function goToPage(nextPage) {
733
+ const list = document.getElementById('g-body');
734
+ if (list)
735
+ list.scrollTop = 0;
736
+ setPage(nextPage);
737
+ }
738
+ return (_jsxs("section", { className: "page groups-page", children: [_jsxs("div", { className: "page-heading", children: [_jsxs("div", { children: [_jsx("p", { className: "eyebrow", children: tr('nav.groups') }), _jsx("h1", { children: tr('groups.title') })] }), _jsx("div", { className: "page-heading-actions", children: _jsx(CreateActionButton, { id: "g-create", className: "page-primary-action", onClick: () => void openCreateDialog(), children: tr('groups.create') }) })] }), _jsxs("form", { id: "g-filters", className: "filters dashboard-toolbar groups-toolbar", onSubmit: ev => ev.preventDefault(), children: [_jsx("input", { type: "search", name: "q", placeholder: tr('groups.search'), value: filters.q, onChange: ev => {
739
+ const q = ev.currentTarget.value;
740
+ setPage(1);
741
+ setFilters(cur => ({ ...cur, q }));
742
+ } }), _jsxs("label", { className: "filter-toggle", children: [_jsx("input", { type: "checkbox", name: "missing", checked: filters.missingOnly, onChange: ev => {
743
+ const missingOnly = ev.currentTarget.checked;
744
+ setPage(1);
745
+ setFilters(cur => ({ ...cur, missingOnly }));
746
+ } }), _jsx("span", { className: "filter-toggle-label", children: tr('groups.missingOnly') }), _jsx("span", { className: "filter-toggle-switch", "aria-hidden": "true" })] }), _jsx("span", { className: "groups-toolbar-spacer", "aria-hidden": "true" }), _jsxs("span", { className: "groups-toolbar-count", children: [tr('groups.matrixTitle'), " ", loading ? '-/-' : `${rows.length}/${snapshot.chats.length}`] }), _jsx(RefreshIconButton, { id: "g-refresh", label: tr('groups.refresh'), busy: refreshing, disabled: refreshing, onClick: () => void refresh() })] }), loadError ? _jsxs("p", { className: "hint-warn", children: ["\u52A0\u8F7D\u7FA4\u7EC4\u5931\u8D25\uFF1A", loadError] }) : null, _jsx("section", { className: "overview-block groups-matrix-section", children: loading ? (_jsx("div", { id: "g-loading", children: _jsx(LoadingState, { label: tr('common.loading') }) })) : (_jsxs("div", { className: "groups-list-wrap", id: "g-table-wrap", children: [rows.length === 0 ? (_jsx("div", { className: "empty groups-list-empty", id: "g-body", children: tr('groups.empty') })) : (_jsx(OverviewList, { id: "g-body", className: "groups-list", children: pageWindow.rows.map(chat => (_jsx(GroupListRow, { chat: chat, bots: snapshot.bots, roleContext: roleContext, tr: tr, onAddBots: openAddBotsDialog, onSaveProfile: openSaveProfileDialog, onManage: openManageDialog }, chat.chatId))) })), rows.length > 0 && pageWindow.totalPages > 1 ? (_jsxs("nav", { className: "groups-pagination", "aria-label": tr('groups.paginationLabel'), children: [_jsx("span", { className: "groups-pagination-status", "aria-live": "polite", children: tr('groups.pageStatus', {
747
+ page: pageWindow.page,
748
+ pages: pageWindow.totalPages,
749
+ from: pageWindow.from,
750
+ to: pageWindow.to,
751
+ total: pageWindow.total,
752
+ }) }), _jsxs("div", { className: "groups-pagination-actions", children: [_jsx("button", { type: "button", disabled: pageWindow.page <= 1, onClick: () => goToPage(pageWindow.page - 1), children: tr('groups.prevPage') }), _jsx("button", { type: "button", disabled: pageWindow.page >= pageWindow.totalPages, onClick: () => goToPage(pageWindow.page + 1), children: tr('groups.nextPage') })] })] })) : null] })) }), _jsx(DialogHost, { dialog: dialog, snapshot: snapshot, tr: tr, onClose: () => setDialog(null), onCreated: handleCreated, onReloadGroups: reloadGroups, onRefreshRoleContext: () => refreshRoleProfileContext(), setTimer: setTimer })] }));
22
753
  }
23
754
  export function renderGroupsPage(root) {
24
755
  return mountReactPage(root, _jsx(GroupsPage, {}));