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,21 +1,1417 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useEffect, useRef } from 'react';
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
3
+ import { openBotOnboarding } from './bot-onboarding.js';
4
+ import { agentSelectionKey, cliIdOf, displayCliId, fallbackCliOptionsState, fetchBotDefaults, fetchCliOptions, fmtSince, modelSuggestionsForOption, selectedCliOption, } from './bot-defaults.js';
3
5
  import { mountReactPage } from './react-mount.js';
4
6
  import { useT } from './react-hooks.js';
5
- import { wireBotDefaultsPage } from './bot-defaults.js';
6
- // Render-once scaffold: React owns the route shell, while wireBotDefaultsPage
7
- // owns the roster/detail subtree with delegated events and targeted DOM
8
- // updates. Keep this component free of useState/useStoreSelector until those
9
- // controller sections are migrated to React components.
7
+ import { CreateActionButton, DropdownMenu, Html, InfoTip as BaseInfoTip, LoadingState, RefreshIconButton, dropdownLabel, } from './dashboard-components.js';
8
+ import { botAvatarHtml, loadNameMaps, ui } from './ui.js';
9
+ function statusClass(status, extra = '') {
10
+ const suffix = status ? ` ${status.ok ? 'hint-ok' : 'hint-warn-inline'}` : '';
11
+ return `oncall-status${extra ? ` ${extra}` : ''}${suffix}`;
12
+ }
13
+ function StatusSpan(props) {
14
+ return _jsx("span", { className: statusClass(props.status), ...(props.attr ?? {}), children: props.status?.text ?? '' });
15
+ }
16
+ function InfoTip(props) {
17
+ const ariaLabel = typeof props.children === 'string' ? props.children : undefined;
18
+ return _jsx(BaseInfoTip, { className: "bd-info-tip", label: ariaLabel, children: props.children });
19
+ }
20
+ function FieldTitle(props) {
21
+ return (_jsxs("span", { className: "bd-field-title", children: [_jsx("span", { className: "bd-field-title-text", children: props.children }), props.help ? _jsx(InfoTip, { children: props.help }) : null] }));
22
+ }
23
+ function DropdownField(props) {
24
+ return (_jsxs(_Fragment, { children: [_jsx(DropdownMenu, { id: `bd-menu-${props.dataInput}`, className: ['bd-field-menu', props.className].filter(Boolean).join(' '), ariaLabel: props.ariaLabel, disabled: props.disabled, label: dropdownLabel(props.options, props.value), value: props.value, options: props.options, onChange: props.onChange }), _jsx("input", { type: "hidden", "data-input": props.dataInput, value: props.value, readOnly: true })] }));
25
+ }
26
+ function ToggleRow(props) {
27
+ return (_jsxs("label", { className: "toggle-row", children: [_jsx("input", { type: "checkbox", "data-action": props.dataAction, checked: props.checked, disabled: props.disabled, onChange: event => props.onChange(event.currentTarget.checked) }), _jsx("span", { className: "switch", "aria-hidden": "true" }), _jsx("span", { className: "toggle-tx", children: _jsx("strong", { children: _jsx(FieldTitle, { help: props.help, children: props.title }) }) })] }));
28
+ }
29
+ async function sendJson(method, url, body) {
30
+ const r = await fetch(url, {
31
+ method,
32
+ headers: { 'content-type': 'application/json' },
33
+ body: body === undefined ? undefined : JSON.stringify(body),
34
+ });
35
+ const parsed = await r.json().catch(() => ({}));
36
+ return { ok: r.ok && parsed?.ok !== false, status: r.status, body: parsed };
37
+ }
38
+ function responseErrorText(res) {
39
+ return String(res.body?.error ?? res.status);
40
+ }
41
+ function caughtErrorText(e) {
42
+ return e?.message ?? String(e);
43
+ }
44
+ function positiveIntegerOrNull(raw) {
45
+ const value = raw.trim();
46
+ if (!value)
47
+ return null;
48
+ if (!/^[1-9]\d*$/.test(value))
49
+ return 'invalid';
50
+ return Number(value);
51
+ }
52
+ function nonNegativeInteger(raw, fallback) {
53
+ const value = raw.trim();
54
+ if (value === '')
55
+ return fallback;
56
+ if (!/^(0|[1-9]\d*)$/.test(value))
57
+ return null;
58
+ return Number(value);
59
+ }
60
+ const substituteTargetIdFields = ['email', 'openId', 'userId', 'unionId'];
61
+ function substituteTargetIdField(target) {
62
+ return substituteTargetIdFields.find(field => target?.[field]?.trim()) ?? 'email';
63
+ }
64
+ /**
65
+ * Build the substitute target to PUT for one edited row. Returns null when the id value is
66
+ * blank. When the id value/field was edited, every carried-over resolved id is dropped so the
67
+ * server re-resolves the new value — otherwise `persisted` keeps a previously-resolved openId
68
+ * alongside the email and the server (which prefers openId) would substitute the stale person.
69
+ * An unchanged row keeps its resolved ids so the stable id is preserved.
70
+ */
71
+ export function buildSubstituteTarget(row) {
72
+ const idValue = row.idValue.trim();
73
+ if (!idValue)
74
+ return null;
75
+ const target = { ...row.persisted };
76
+ const idEdited = row.persisted[row.idField] !== idValue
77
+ || (row.originalIdField != null && row.originalIdField !== row.idField);
78
+ if (idEdited) {
79
+ for (const field of substituteTargetIdFields)
80
+ delete target[field];
81
+ }
82
+ target[row.idField] = idValue;
83
+ const name = row.name.trim();
84
+ if (name)
85
+ target.name = name;
86
+ else
87
+ delete target.name;
88
+ return target;
89
+ }
90
+ function brandStateLabel(brand, tr) {
91
+ if (brand == null)
92
+ return tr('botDefaults.brandStateDefault');
93
+ return brand.trim() === '' ? tr('botDefaults.brandStateOff') : tr('botDefaults.brandStateCustom');
94
+ }
95
+ function quotaStateLabel(quota, tr) {
96
+ return quota == null
97
+ ? tr('botDefaults.quotaStateOff')
98
+ : tr('botDefaults.quotaStateOn', { count: quota });
99
+ }
100
+ function sessionCapStateLabel(cap, tr) {
101
+ return cap == null
102
+ ? tr('botDefaults.maxLiveWorkersStateDefault')
103
+ : tr('botDefaults.maxLiveWorkersStateOn', { count: cap });
104
+ }
105
+ function patchCardPrefsFromBody(bot, body) {
106
+ return {
107
+ ...bot,
108
+ disableStreamingCard: body.disableStreamingCard,
109
+ silentTurnReactions: body.silentTurnReactions,
110
+ writableTerminalLinkInCard: body.writableTerminalLinkInCard,
111
+ privateCard: body.privateCard,
112
+ botToBotSameDir: body.botToBotSameDir,
113
+ autoStartOnGroupJoin: body.autoStartOnGroupJoin,
114
+ autoStartOnGroupJoinPrompt: body.autoStartOnGroupJoinPrompt,
115
+ autoStartOnNewTopic: body.autoStartOnNewTopic,
116
+ regularGroupReplyMode: body.regularGroupReplyMode,
117
+ regularGroupMentionMode: body.regularGroupMentionMode,
118
+ docSubscribeDefaultMode: body.docSubscribeDefaultMode,
119
+ };
120
+ }
10
121
  function BotDefaultsPage() {
11
122
  const tr = useT();
12
- const rootRef = useRef(null);
123
+ const mountedRef = useRef(true);
124
+ const [bots, setBots] = useState([]);
125
+ const [cliState, setCliState] = useState(fallbackCliOptionsState);
126
+ const [loadError, setLoadError] = useState(null);
127
+ const [loading, setLoading] = useState(true);
128
+ const [refreshing, setRefreshing] = useState(false);
129
+ const [query, setQuery] = useState('');
130
+ const [selectedAppId, setSelectedAppId] = useState(null);
131
+ const [profileRoleVersion, setProfileRoleVersion] = useState(0);
132
+ const [, setAvatarVersion] = useState(0);
133
+ const [onboardingBusy, setOnboardingBusy] = useState(false);
134
+ const refresh = useCallback(async (clearProfileRoles = false) => {
135
+ if (clearProfileRoles)
136
+ setProfileRoleVersion(version => version + 1);
137
+ setLoading(true);
138
+ try {
139
+ const [nextBots, nextCli] = await Promise.all([fetchBotDefaults(), fetchCliOptions()]);
140
+ if (!mountedRef.current)
141
+ return;
142
+ setBots(nextBots.bots);
143
+ setLoadError(nextBots.error);
144
+ setCliState(nextCli);
145
+ }
146
+ finally {
147
+ if (mountedRef.current)
148
+ setLoading(false);
149
+ }
150
+ }, []);
151
+ useEffect(() => {
152
+ mountedRef.current = true;
153
+ void refresh();
154
+ void loadNameMaps().then(() => {
155
+ if (mountedRef.current)
156
+ setAvatarVersion(value => value + 1);
157
+ });
158
+ return () => { mountedRef.current = false; };
159
+ }, [refresh]);
160
+ const filtered = useMemo(() => {
161
+ const q = query.trim().toLowerCase();
162
+ return bots.filter(bot => !q ||
163
+ (bot.botName ?? '').toLowerCase().includes(q) ||
164
+ (bot.larkAppId ?? '').toLowerCase().includes(q));
165
+ }, [bots, query]);
13
166
  useEffect(() => {
14
- if (!rootRef.current)
15
- return undefined;
16
- return wireBotDefaultsPage(rootRef.current);
167
+ if (loadError || loading)
168
+ return;
169
+ if (filtered.length === 0) {
170
+ if (selectedAppId !== null)
171
+ setSelectedAppId(null);
172
+ return;
173
+ }
174
+ if (!selectedAppId || !filtered.some(bot => bot.larkAppId === selectedAppId)) {
175
+ setSelectedAppId(filtered[0].larkAppId);
176
+ }
177
+ }, [filtered, loadError, loading, selectedAppId]);
178
+ const selectedBot = selectedAppId ? filtered.find(bot => bot.larkAppId === selectedAppId) ?? null : null;
179
+ const patchBot = useCallback((appId, patch) => {
180
+ setBots(rows => rows.map(bot => {
181
+ if (bot.larkAppId !== appId)
182
+ return bot;
183
+ return typeof patch === 'function' ? patch(bot) : { ...bot, ...patch };
184
+ }));
17
185
  }, []);
18
- return (_jsxs("section", { className: "page", ref: rootRef, children: [_jsx("div", { className: "page-heading", children: _jsxs("div", { children: [_jsx("p", { className: "eyebrow", children: tr('nav.botDefaults') }), _jsx("h1", { children: tr('botDefaults.title') }), _jsx("p", { children: tr('botDefaults.subtitle') })] }) }), _jsxs("form", { id: "bd-filters", className: "filters sessions-filters", children: [_jsx("input", { type: "search", name: "q", placeholder: tr('botDefaults.search') }), _jsx("button", { type: "button", id: "bd-refresh", children: tr('botDefaults.refresh') })] }), _jsxs("div", { className: "bd-layout", children: [_jsx("aside", { id: "bd-roster", className: "bd-roster" }), _jsx("div", { id: "bd-list", className: "bd-detail" })] })] }));
186
+ const reload = async () => {
187
+ setRefreshing(true);
188
+ try {
189
+ await refresh(true);
190
+ }
191
+ finally {
192
+ if (mountedRef.current)
193
+ setRefreshing(false);
194
+ }
195
+ };
196
+ let detail;
197
+ if (loading) {
198
+ detail = _jsx(LoadingState, { label: tr('common.loading') });
199
+ }
200
+ else if (loadError) {
201
+ detail = (_jsxs("p", { className: "hint-warn", children: ["\u65E0\u6CD5\u52A0\u8F7D bot \u5217\u8868\uFF1A", loadError, _jsx("br", {}), "\u5E38\u89C1\u539F\u56E0\uFF1Adashboard / daemon \u8FDB\u7A0B\u8FD8\u5728\u8DD1\u65E7\u4EE3\u7801\uFF0C\u6267\u884C ", _jsx("code", { children: "botmux restart" }), " \u540E\u5237\u65B0\u3002"] }));
202
+ }
203
+ else if (filtered.length === 0) {
204
+ detail = _jsx("p", { className: "empty", children: tr('botDefaults.empty') });
205
+ }
206
+ else if (selectedBot) {
207
+ detail = (_jsx(BotDefaultsCard, { bot: selectedBot, cliState: cliState, patchBot: patchBot }, `${selectedBot.larkAppId}:${profileRoleVersion}`));
208
+ }
209
+ else {
210
+ detail = null;
211
+ }
212
+ return (_jsxs("section", { className: "page bot-defaults-page", children: [_jsxs("div", { className: "page-heading", children: [_jsxs("div", { children: [_jsx("p", { className: "eyebrow", children: tr('nav.botDefaults') }), _jsx("h1", { children: tr('botDefaults.title') })] }), _jsxs("div", { className: "page-heading-actions", children: [_jsx(RefreshIconButton, { id: "bd-refresh", label: tr('botDefaults.refresh'), busy: refreshing, disabled: refreshing, onClick: () => void reload() }), ui.authed ? (_jsx(CreateActionButton, { className: "page-primary-action add-bot-btn", disabled: onboardingBusy, onClick: () => {
213
+ setOnboardingBusy(true);
214
+ void openBotOnboarding().finally(() => setOnboardingBusy(false));
215
+ }, children: tr('botOnboarding.add') })) : null] })] }), _jsxs("div", { className: "bd-layout", children: [_jsxs("aside", { id: "bd-roster", className: "bd-roster", children: [_jsx("form", { id: "bd-filters", className: "filters dashboard-toolbar", onSubmit: event => event.preventDefault(), children: _jsx("input", { type: "search", name: "q", placeholder: tr('botDefaults.search'), value: query, onChange: event => setQuery(event.currentTarget.value) }) }), _jsx("div", { className: "bd-roster-list", children: !loadError && filtered.map(bot => (_jsx(RosterItem, { bot: bot, selected: bot.larkAppId === selectedAppId, onSelect: () => setSelectedAppId(bot.larkAppId) }, bot.larkAppId))) })] }), _jsx("div", { id: "bd-list", className: "bd-detail", children: detail })] })] }));
216
+ }
217
+ function RosterItem(props) {
218
+ const { bot } = props;
219
+ const name = bot.botName ?? bot.larkAppId;
220
+ const cli = displayCliId(bot, cliIdOf(bot.larkAppId));
221
+ return (_jsxs("div", { className: `bd-roster-item${props.selected ? ' on' : ''}`, "data-appid": bot.larkAppId, role: "button", tabIndex: 0, onClick: props.onSelect, onKeyDown: event => {
222
+ if (event.key === 'Enter' || event.key === ' ') {
223
+ event.preventDefault();
224
+ props.onSelect();
225
+ }
226
+ }, children: [_jsx(Html, { html: botAvatarHtml({ name, larkAppId: bot.larkAppId, size: 'sm' }) }), _jsxs("div", { className: "bd-roster-tx", children: [_jsx("b", { children: name }), _jsx("span", { children: cli || bot.larkAppId.slice(0, 14) })] }), bot.defaultOncall?.enabled ? _jsx("span", { className: "bd-roster-flag", children: "oncall" }) : null] }));
227
+ }
228
+ function BotDefaultsCard(props) {
229
+ const tr = useT();
230
+ const { bot, cliState, patchBot } = props;
231
+ const name = bot.botName ?? bot.larkAppId;
232
+ const cli = displayCliId(bot, cliIdOf(bot.larkAppId));
233
+ const putCardPref = useCallback(async (patch) => {
234
+ const res = await sendJson('PUT', `/api/bots/${encodeURIComponent(bot.larkAppId)}/card-prefs`, patch);
235
+ if (res.ok) {
236
+ patchBot(bot.larkAppId, current => patchCardPrefsFromBody(current, res.body));
237
+ }
238
+ return res;
239
+ }, [bot.larkAppId, patchBot]);
240
+ if (bot.error) {
241
+ return (_jsxs("article", { className: "bd-card bd-profile", "data-appid": bot.larkAppId, children: [_jsxs("header", { className: "bd-profile-head", children: [_jsx(Html, { html: botAvatarHtml({ name, larkAppId: bot.larkAppId }) }), _jsxs("div", { className: "bd-profile-id", children: [_jsx("strong", { children: name }), _jsx("code", { children: bot.larkAppId })] })] }), _jsxs("p", { className: "hint-warn-inline", children: ["\u67E5\u8BE2\u5931\u8D25\uFF1A", bot.error] })] }));
242
+ }
243
+ const def = bot.defaultOncall ?? { enabled: false, workingDir: '', since: 0 };
244
+ return (_jsxs("article", { className: "bd-card bd-profile", "data-appid": bot.larkAppId, children: [_jsxs("header", { className: "bd-profile-head", children: [_jsx("div", { className: "bd-profile-avatar", children: _jsx(Html, { html: botAvatarHtml({ name, larkAppId: bot.larkAppId, dot: 'ok' }) }) }), _jsx("div", { className: "bd-profile-main", children: _jsx(BotProfileIdentity, { bot: bot, cli: cli, patchBot: patchBot, meta: (_jsxs(_Fragment, { children: [_jsxs("small", { className: "bd-meta-ok", children: ["\u25CF ", tr('botDefaults.metaOnline')] }), _jsxs("small", { "data-oncall-since": true, children: [tr('botDefaults.lastEnabled'), ": ", fmtSince(def.since ?? 0)] }), _jsx("small", { children: tr('botDefaults.autobound', { count: bot.autoboundChatCount ?? 0 }) })] })) }) })] }), _jsxs("div", { className: "bd-body bd-grid", children: [_jsxs("div", { className: "bd-column", children: [_jsxs("section", { className: "bd-tile", children: [_jsx(BotAgentSection, { bot: bot, sessionFallback: cli, cliState: cliState, patchBot: patchBot }), _jsx(WorkingDirSection, { bot: bot, patchBot: patchBot, putCardPref: putCardPref }), _jsx(SandboxSection, { bot: bot, patchBot: patchBot })] }), _jsx("section", { className: "bd-tile", children: _jsx(RuntimeEnvironmentSection, { bot: bot, patchBot: patchBot }) }), _jsx("section", { className: "bd-tile", children: _jsx(GrantSection, { bot: bot, patchBot: patchBot }) })] }), _jsxs("div", { className: "bd-column", children: [_jsxs("section", { className: "bd-tile", children: [_jsx(SessionModeSection, { bot: bot, patchBot: patchBot, putCardPref: putCardPref }), _jsx(SubstituteModeSection, { bot: bot, patchBot: patchBot }), _jsx(CrossBotSection, { bot: bot, putCardPref: putCardPref }), _jsx(SessionCapSection, { bot: bot, patchBot: patchBot })] }), _jsxs("section", { className: "bd-tile", children: [_jsx(CardBehaviorSection, { bot: bot, putCardPref: putCardPref }), _jsx(SummaryTriggerSection, { bot: bot, patchBot: patchBot }), _jsx(BrandSection, { bot: bot, patchBot: patchBot })] }), _jsx("section", { className: "bd-tile", children: _jsx(RoleSection, { bot: bot, patchBot: patchBot }) })] })] })] }));
245
+ }
246
+ function RuntimeEnvironmentSection(props) {
247
+ const tr = useT();
248
+ return (_jsxs("section", { className: "bd-section bd-runtime-env", children: [_jsx("h3", { className: "bd-section-title", children: tr('botDefaults.sectionRuntimeEnv') }), _jsx(StartupCommandsSection, { bot: props.bot, patchBot: props.patchBot }), _jsx(LaunchShellSection, { bot: props.bot, patchBot: props.patchBot }), _jsx(EnvSection, { bot: props.bot, patchBot: props.patchBot })] }));
249
+ }
250
+ function BotProfileIdentity(props) {
251
+ const tr = useT();
252
+ const { bot, cli, patchBot } = props;
253
+ const name = bot.botName ?? bot.larkAppId;
254
+ const [editing, setEditing] = useState(false);
255
+ const [draft, setDraft] = useState(name);
256
+ const [busy, setBusy] = useState(false);
257
+ const [status, setStatus] = useState(null);
258
+ const [loginVisible, setLoginVisible] = useState(false);
259
+ const [loginOpen, setLoginOpen] = useState(false);
260
+ useEffect(() => {
261
+ if (!editing)
262
+ setDraft(name);
263
+ }, [editing, name]);
264
+ function setEditMode(on) {
265
+ setEditing(on);
266
+ if (on) {
267
+ setDraft(name);
268
+ setStatus(null);
269
+ setLoginVisible(false);
270
+ }
271
+ }
272
+ function renameWarningText(warning, message) {
273
+ const known = ['no_session', 'session_expired', 'no_access', 'unsupported_brand'];
274
+ const detail = known.includes(warning)
275
+ ? tr(`botDefaults.renameWarn.${warning}`)
276
+ : (message || warning);
277
+ return tr('botDefaults.renameLocalOnly', { reason: detail });
278
+ }
279
+ const submitRename = useCallback(async () => {
280
+ const nextName = draft.trim();
281
+ if (!nextName) {
282
+ setStatus({ text: `✗ ${tr('botDefaults.renameEmpty')}` });
283
+ return;
284
+ }
285
+ setBusy(true);
286
+ setStatus({ text: `⏳ ${tr('botDefaults.renaming')}`, ok: true });
287
+ try {
288
+ const res = await sendJson('PUT', `/api/bots/${encodeURIComponent(bot.larkAppId)}/rename`, { name: nextName });
289
+ if (res.ok && res.body.ok) {
290
+ const effective = typeof res.body.botName === 'string' && res.body.botName ? res.body.botName : nextName;
291
+ patchBot(bot.larkAppId, current => ({
292
+ ...current,
293
+ botName: effective,
294
+ larkBotName: res.body.mode === 'feishu' ? nextName : current.larkBotName,
295
+ displayName: res.body.mode === 'feishu' ? null : nextName,
296
+ }));
297
+ setEditMode(false);
298
+ if (res.body.mode === 'feishu') {
299
+ setStatus({ text: `✓ ${tr('botDefaults.renameOkFeishu')}`, ok: true });
300
+ setLoginVisible(false);
301
+ }
302
+ else {
303
+ setStatus({ text: `⚠ ${renameWarningText(String(res.body.warning ?? ''), res.body.message)}` });
304
+ setLoginVisible(res.body.warning === 'no_session' || res.body.warning === 'session_expired');
305
+ }
306
+ }
307
+ else {
308
+ setStatus({ text: `✗ ${tr('botDefaults.renameFailed', { error: responseErrorText(res) })}` });
309
+ }
310
+ }
311
+ catch (e) {
312
+ setStatus({ text: `✗ ${tr('botDefaults.renameFailed', { error: caughtErrorText(e) })}` });
313
+ }
314
+ finally {
315
+ setBusy(false);
316
+ }
317
+ }, [bot.larkAppId, draft, patchBot, tr]);
318
+ return (_jsxs("div", { className: "bd-profile-id", children: [!editing ? (_jsxs("div", { className: "bd-profile-title-row", "data-name-row": true, children: [_jsxs("div", { className: "bd-profile-title-content", children: [_jsx("strong", { "data-bot-name": true, children: name }), cli ? _jsx("span", { className: "mate-role bd-profile-cli-tag", children: cli }) : null, props.meta ? _jsx("span", { className: "bd-profile-meta bd-meta", children: props.meta }) : null] }), _jsx("button", { type: "button", className: "bd-name-edit", "data-action": "edit-bot-name", title: tr('botDefaults.renameTitle'), "aria-label": tr('botDefaults.renameTitle'), onClick: () => setEditMode(true), children: tr('botDefaults.renameAction') })] })) : (_jsxs("span", { className: "bd-name-editor", "data-name-editor": true, children: [_jsx("input", { type: "text", className: "bd-name-input", "data-input": "botRename", maxLength: 64, value: draft, disabled: busy, autoFocus: true, onChange: event => setDraft(event.currentTarget.value), onKeyDown: event => {
319
+ if (event.key === 'Enter') {
320
+ event.preventDefault();
321
+ void submitRename();
322
+ }
323
+ else if (event.key === 'Escape') {
324
+ setEditMode(false);
325
+ }
326
+ } }), _jsx("button", { type: "button", className: "primary", "data-action": "save-bot-name", disabled: busy, onClick: () => void submitRename(), children: tr('botDefaults.renameSave') }), _jsx("button", { type: "button", "data-action": "cancel-bot-name", disabled: busy, onClick: () => setEditMode(false), children: tr('botDefaults.renameCancel') })] })), _jsx("code", { children: bot.larkAppId }), _jsx("small", { className: statusClass(status, 'bd-name-status'), "data-name-status": true, children: status?.text ?? '' }), _jsx("button", { type: "button", className: "bd-feishu-login", "data-action": "feishu-login", hidden: !loginVisible, onClick: () => setLoginOpen(true), children: tr('feishuLogin.entry') }), loginOpen ? (_jsx(FeishuLoginModal, { onClose: () => setLoginOpen(false), onSuccess: () => {
327
+ setLoginVisible(false);
328
+ setLoginOpen(false);
329
+ void submitRename();
330
+ } })) : null] }));
331
+ }
332
+ function FeishuLoginModal(props) {
333
+ const tr = useT();
334
+ const { onClose, onSuccess } = props;
335
+ const timerRef = useRef(null);
336
+ const successTimerRef = useRef(null);
337
+ const [hint, setHint] = useState(tr('feishuLogin.starting'));
338
+ const [qrDataUrl, setQrDataUrl] = useState(null);
339
+ const [retry, setRetry] = useState(false);
340
+ const stopTimer = useCallback(() => {
341
+ if (timerRef.current !== null) {
342
+ window.clearInterval(timerRef.current);
343
+ timerRef.current = null;
344
+ }
345
+ }, []);
346
+ const renderLogin = useCallback((login) => {
347
+ if (!login)
348
+ return 'active';
349
+ if (login.status === 'awaiting_scan' && login.qrDataUrl) {
350
+ setQrDataUrl(login.qrDataUrl);
351
+ setHint(login.message || tr('feishuLogin.scanHint'));
352
+ setRetry(false);
353
+ return 'active';
354
+ }
355
+ if (login.status === 'starting') {
356
+ setHint(login.message || tr('feishuLogin.starting'));
357
+ setQrDataUrl(null);
358
+ setRetry(false);
359
+ return 'active';
360
+ }
361
+ if (login.status === 'success') {
362
+ stopTimer();
363
+ setQrDataUrl(null);
364
+ setRetry(false);
365
+ setHint(tr('feishuLogin.success'));
366
+ successTimerRef.current = window.setTimeout(() => onSuccess(), 900);
367
+ return 'done';
368
+ }
369
+ stopTimer();
370
+ setQrDataUrl(null);
371
+ setHint(tr('feishuLogin.failed', { reason: login.message || login.reason || '' }));
372
+ setRetry(true);
373
+ return 'done';
374
+ }, [onSuccess, stopTimer, tr]);
375
+ const poll = useCallback(async () => {
376
+ try {
377
+ const r = await fetch('/api/feishu-login/status');
378
+ const body = await r.json().catch(() => ({}));
379
+ renderLogin(body.login);
380
+ }
381
+ catch {
382
+ // transient; keep polling
383
+ }
384
+ }, [renderLogin]);
385
+ const begin = useCallback(async () => {
386
+ stopTimer();
387
+ setHint(tr('feishuLogin.starting'));
388
+ setQrDataUrl(null);
389
+ setRetry(false);
390
+ let phase = 'active';
391
+ try {
392
+ const r = await fetch('/api/feishu-login/start', { method: 'POST' });
393
+ const body = await r.json().catch(() => ({}));
394
+ phase = renderLogin(body.login);
395
+ }
396
+ catch (e) {
397
+ setHint(tr('feishuLogin.failed', { reason: caughtErrorText(e) }));
398
+ setRetry(true);
399
+ return;
400
+ }
401
+ if (phase === 'active' && timerRef.current === null) {
402
+ timerRef.current = window.setInterval(() => void poll(), 1500);
403
+ }
404
+ }, [poll, renderLogin, stopTimer, tr]);
405
+ useEffect(() => {
406
+ void begin();
407
+ return () => {
408
+ stopTimer();
409
+ if (successTimerRef.current !== null)
410
+ window.clearTimeout(successTimerRef.current);
411
+ };
412
+ }, [begin, stopTimer]);
413
+ return (_jsx("div", { className: "feishu-login-overlay", onClick: event => {
414
+ if (event.currentTarget === event.target)
415
+ onClose();
416
+ }, children: _jsxs("div", { className: "feishu-login-modal", role: "dialog", "aria-modal": "true", children: [_jsx("button", { type: "button", className: "feishu-login-close", "data-close": true, "aria-label": tr('feishuLogin.close'), onClick: onClose, children: "x" }), _jsx("h3", { className: "feishu-login-title", children: tr('feishuLogin.title') }), _jsx("p", { className: "feishu-login-hint", "data-hint": true, children: hint }), _jsx("div", { className: "feishu-login-qr", "data-qr": true, children: qrDataUrl ? _jsx("img", { className: "qr-image", src: qrDataUrl, alt: tr('feishuLogin.qrAlt') }) : null }), _jsx("div", { className: "feishu-login-actions", children: _jsx("button", { type: "button", className: "primary", "data-retry": true, hidden: !retry, onClick: () => void begin(), children: tr('feishuLogin.retry') }) })] }) }));
417
+ }
418
+ export function BotAgentSection(props) {
419
+ const tr = useT();
420
+ const { bot, cliState, patchBot } = props;
421
+ const initialKey = agentSelectionKey(bot, props.sessionFallback);
422
+ const [cliKey, setCliKey] = useState(initialKey);
423
+ const [model, setModel] = useState(typeof bot.model === 'string' ? bot.model : '');
424
+ const [agentStatus, setAgentStatus] = useState(null);
425
+ const [agentBusy, setAgentBusy] = useState(false);
426
+ const [skillValue, setSkillValue] = useState(skillInjectionResolved(bot));
427
+ const [skillStatus, setSkillStatus] = useState(null);
428
+ const [skillBusy, setSkillBusy] = useState(false);
429
+ useEffect(() => {
430
+ setCliKey(agentSelectionKey(bot, props.sessionFallback));
431
+ setModel(typeof bot.model === 'string' ? bot.model : '');
432
+ setSkillValue(skillInjectionResolved(bot));
433
+ }, [
434
+ bot.agentSelectionKey,
435
+ bot.cliId,
436
+ bot.larkAppId,
437
+ bot.model,
438
+ bot.skillInjection,
439
+ bot.skillInjectionDefault,
440
+ props.sessionFallback,
441
+ ]);
442
+ const option = selectedCliOption(cliState.options, cliKey);
443
+ const suggestions = modelSuggestionsForOption(option, cliState);
444
+ const modelDisabledByCli = option?.gateway === 'ttadk' && option.acceptsModel === false;
445
+ const modelPlaceholder = modelDisabledByCli
446
+ ? tr('botOnboarding.modelTtadkCocoPlaceholder')
447
+ : option?.gateway === 'ttadk'
448
+ ? tr('botOnboarding.modelTtadkPlaceholder').replace('{model}', cliState.ttadkModelDefault)
449
+ : tr('botDefaults.agentModelPlaceholder');
450
+ function updateCli(nextKey) {
451
+ setCliKey(nextKey);
452
+ const nextOption = selectedCliOption(cliState.options, nextKey);
453
+ const isTtadk = nextOption?.gateway === 'ttadk';
454
+ const acceptsModel = isTtadk && nextOption.acceptsModel !== false;
455
+ if (isTtadk && !acceptsModel) {
456
+ setModel('');
457
+ }
458
+ else if (acceptsModel) {
459
+ setModel(current => current.trim() ? current : cliState.ttadkModelDefault);
460
+ }
461
+ else {
462
+ setModel(current => current.trim() === cliState.ttadkModelDefault ? '' : current);
463
+ }
464
+ }
465
+ async function saveAgent() {
466
+ setAgentStatus(null);
467
+ setAgentBusy(true);
468
+ try {
469
+ const res = await sendJson('PUT', `/api/bots/${encodeURIComponent(bot.larkAppId)}/agent`, { cliId: cliKey, model });
470
+ if (res.ok && res.body.ok) {
471
+ setAgentStatus({ text: `✓ ${tr('botDefaults.agentSaved')}`, ok: true });
472
+ patchBot(bot.larkAppId, {
473
+ cliId: res.body.cliId,
474
+ wrapperCli: res.body.wrapperCli ?? null,
475
+ model: res.body.model ?? '',
476
+ agentSelectionKey: res.body.selectionKey ?? cliKey,
477
+ });
478
+ }
479
+ else {
480
+ setAgentStatus({ text: `✗ ${responseErrorText(res)}` });
481
+ }
482
+ }
483
+ catch (e) {
484
+ setAgentStatus({ text: `✗ ${caughtErrorText(e)}` });
485
+ }
486
+ finally {
487
+ setAgentBusy(false);
488
+ }
489
+ }
490
+ async function saveSkillInjection(next) {
491
+ setSkillValue(next);
492
+ setSkillStatus(null);
493
+ setSkillBusy(true);
494
+ try {
495
+ const res = await sendJson('PUT', `/api/bots/${encodeURIComponent(bot.larkAppId)}/skill-injection`, { skillInjection: next });
496
+ if (res.ok && res.body.ok) {
497
+ setSkillStatus({ text: `✓ ${tr('botDefaults.cardPrefSaved')}`, ok: true });
498
+ patchBot(bot.larkAppId, { skillInjection: res.body.skillInjection ?? null });
499
+ }
500
+ else {
501
+ setSkillStatus({ text: `✗ ${responseErrorText(res)}` });
502
+ }
503
+ }
504
+ catch (e) {
505
+ setSkillStatus({ text: `✗ ${caughtErrorText(e)}` });
506
+ }
507
+ finally {
508
+ setSkillBusy(false);
509
+ }
510
+ }
511
+ const siSupport = bot.skillInjectionSupport === 'dynamic' ? 'dynamic' : bot.skillInjectionSupport === 'global' ? 'global' : 'none';
512
+ const cliOptions = cliState.options.map(option => ({
513
+ value: option.id,
514
+ label: `${option.label}(${option.id})`,
515
+ }));
516
+ const dynamicSkillOptions = [
517
+ { value: 'dynamic', label: tr('botDefaults.skillInjectionDynamic') },
518
+ ];
519
+ const skillOptions = [
520
+ // Non-selectable cue: dynamic injection isn't available for this CLI (parity with the old UI).
521
+ { value: 'dynamic', label: tr('botDefaults.skillInjectionDynamicUnsupported'), disabled: true },
522
+ { value: 'prompt', label: tr('botDefaults.skillInjectionPrompt') },
523
+ { value: 'global', label: tr('botDefaults.skillInjectionGlobal') },
524
+ { value: 'off', label: tr('botDefaults.skillInjectionOff') },
525
+ ];
526
+ return (_jsxs("section", { className: "bd-section", children: [_jsx("h3", { className: "bd-section-title", children: tr('botDefaults.sectionAgent') }), _jsx("div", { className: "bd-row", children: _jsxs("div", { className: "bd-field", children: [_jsx("span", { children: tr('botDefaults.agentCli') }), _jsx(DropdownField, { dataInput: "agentCliId", ariaLabel: tr('botDefaults.agentCli'), value: cliKey, disabled: agentBusy, options: cliOptions, onChange: updateCli })] }) }), _jsx("div", { className: "bd-row", children: _jsxs("label", { children: [_jsx(FieldTitle, { help: tr('botDefaults.agentHelp'), children: tr('botDefaults.agentModel') }), _jsx("input", { type: "text", "data-input": "agentModel", list: `agent-model-suggestions-${bot.larkAppId}`, placeholder: modelPlaceholder, value: model, disabled: agentBusy || modelDisabledByCli, onChange: event => setModel(event.currentTarget.value) }), _jsx("datalist", { id: `agent-model-suggestions-${bot.larkAppId}`, children: suggestions.map(item => _jsx("option", { value: item }, item)) })] }) }), siSupport === 'dynamic' ? (_jsx("div", { className: "bd-row", children: _jsxs("div", { className: "bd-field", children: [_jsx(FieldTitle, { help: tr('botDefaults.skillInjectionHelpDynamic'), children: tr('botDefaults.skillInjection') }), _jsx(DropdownField, { dataInput: "skillInjection", ariaLabel: tr('botDefaults.skillInjection'), value: "dynamic", disabled: true, options: dynamicSkillOptions, onChange: () => undefined })] }) })) : siSupport === 'global' ? (_jsxs("div", { className: "bd-row", children: [_jsxs("div", { className: "bd-field", children: [_jsx(FieldTitle, { help: tr('botDefaults.skillInjectionHelp'), children: tr('botDefaults.skillInjection') }), _jsx(DropdownField, { dataInput: "skillInjection", ariaLabel: tr('botDefaults.skillInjection'), value: skillValue, disabled: skillBusy, options: skillOptions, onChange: next => void saveSkillInjection(next) })] }), _jsx("div", { className: "actions", children: _jsx(StatusSpan, { status: skillStatus, attr: { 'data-skill-injection-status': '' } }) })] })) : null, _jsxs("div", { className: "actions bd-section-actions", children: [_jsx("button", { type: "button", className: "primary", "data-action": "save-agent", disabled: agentBusy, onClick: () => void saveAgent(), children: tr('botDefaults.agentSave') }), _jsx(StatusSpan, { status: agentStatus, attr: { 'data-agent-status': '' } })] })] }));
527
+ }
528
+ function skillInjectionResolved(bot) {
529
+ const override = bot.skillInjection === 'global' || bot.skillInjection === 'prompt' || bot.skillInjection === 'off' ? bot.skillInjection : '';
530
+ const def = bot.skillInjectionDefault === 'global' || bot.skillInjectionDefault === 'off' ? bot.skillInjectionDefault : 'prompt';
531
+ return override || def;
532
+ }
533
+ function WorkingDirSection(props) {
534
+ const tr = useT();
535
+ const { bot, patchBot } = props;
536
+ const initial = workingDirState(bot);
537
+ const [mode, setMode] = useState(initial.mode);
538
+ const [workingDir, setWorkingDir] = useState(initial.workingDir);
539
+ const [autoWorktree, setAutoWorktree] = useState(bot.defaultWorkingDirAutoWorktree === true);
540
+ const [status, setStatus] = useState(null);
541
+ const [busy, setBusy] = useState(false);
542
+ useEffect(() => {
543
+ const next = workingDirState(bot);
544
+ setMode(next.mode);
545
+ setWorkingDir(next.workingDir);
546
+ setAutoWorktree(bot.defaultWorkingDirAutoWorktree === true);
547
+ }, [
548
+ bot.defaultOncall?.enabled,
549
+ bot.defaultOncall?.workingDir,
550
+ bot.defaultWorkingDir,
551
+ bot.defaultWorkingDirAutoWorktree,
552
+ ]);
553
+ async function save() {
554
+ setStatus(null);
555
+ const dir = workingDir.trim();
556
+ if (mode !== 'off' && !dir) {
557
+ setStatus({ text: tr('botDefaults.required') });
558
+ return;
559
+ }
560
+ const nextAutoWorktree = mode === 'default' && autoWorktree;
561
+ setBusy(true);
562
+ try {
563
+ const res = await sendJson('PUT', `/api/bots/${encodeURIComponent(bot.larkAppId)}/working-dir-mode`, {
564
+ mode,
565
+ workingDir: dir,
566
+ autoWorktree: nextAutoWorktree,
567
+ });
568
+ if (res.ok && res.body.ok) {
569
+ const resolvedNote = res.body.resolvedPath ? ` → ${res.body.resolvedPath}` : '';
570
+ setStatus({ text: `✓ ${tr('botDefaults.workingDirSaved')}${resolvedNote}`, ok: true });
571
+ patchBot(bot.larkAppId, {
572
+ defaultOncall: res.body.defaultOncall ?? bot.defaultOncall,
573
+ defaultWorkingDir: res.body.defaultWorkingDir ?? null,
574
+ defaultWorkingDirAutoWorktree: res.body.defaultWorkingDirAutoWorktree === true,
575
+ });
576
+ }
577
+ else {
578
+ setStatus({ text: `✗ ${responseErrorText(res)}` });
579
+ }
580
+ }
581
+ catch (e) {
582
+ setStatus({ text: `✗ ${caughtErrorText(e)}` });
583
+ }
584
+ finally {
585
+ setBusy(false);
586
+ }
587
+ }
588
+ const modeOptions = [
589
+ { value: 'off', label: tr('botDefaults.workingDirModeOff') },
590
+ { value: 'default', label: tr('botDefaults.workingDirModeDefault') },
591
+ { value: 'oncall', label: tr('botDefaults.workingDirModeOncall') },
592
+ ];
593
+ return (_jsxs("section", { className: "bd-section", children: [_jsx("h3", { className: "bd-section-title", children: tr('botDefaults.sectionWorkingDir') }), _jsx("div", { className: "bd-row", children: _jsxs("div", { className: "bd-field", children: [_jsx(FieldTitle, { help: tr('botDefaults.workingDirModeHelp'), children: tr('botDefaults.workingDirMode') }), _jsx(DropdownField, { dataInput: "workingDirMode", ariaLabel: tr('botDefaults.workingDirMode'), value: mode, disabled: busy, options: modeOptions, onChange: next => setMode(next) })] }) }), _jsx("div", { className: "bd-row", "data-wd-dir-row": true, hidden: mode === 'off', children: _jsxs("label", { children: [_jsx("span", { children: tr('botDefaults.workingDirField') }), _jsx("input", { type: "text", "data-input": "workingDir", placeholder: "e.g. /root/iserver/botmux", value: workingDir, disabled: busy, onChange: event => setWorkingDir(event.currentTarget.value) })] }) }), _jsxs("label", { className: "toggle-row", "data-wd-worktree-row": true, hidden: mode !== 'default', children: [_jsx("input", { type: "checkbox", "data-input": "autoWorktree", checked: autoWorktree, disabled: busy, onChange: event => setAutoWorktree(event.currentTarget.checked) }), _jsx("span", { className: "switch", "aria-hidden": "true" }), _jsx("span", { className: "toggle-tx", children: _jsx("strong", { children: _jsx(FieldTitle, { help: tr('botDefaults.autoWorktreeHelp'), children: tr('botDefaults.autoWorktree') }) }) })] }), _jsxs("div", { className: "actions", children: [_jsx("button", { type: "button", className: "primary", "data-action": "save-working-dir", disabled: busy, onClick: () => void save(), children: tr('botDefaults.save') }), _jsx(StatusSpan, { status: status, attr: { 'data-status': '' } })] }), _jsx(AutoStartControls, { bot: bot, putCardPref: props.putCardPref })] }));
594
+ }
595
+ function workingDirState(bot) {
596
+ const def = bot.defaultOncall ?? { enabled: false, workingDir: '' };
597
+ const mode = def.enabled ? 'oncall' : (bot.defaultWorkingDir ? 'default' : 'off');
598
+ return { mode, workingDir: bot.defaultWorkingDir || def.workingDir || '' };
599
+ }
600
+ function AutoStartControls(props) {
601
+ const tr = useT();
602
+ const { bot, putCardPref } = props;
603
+ const [onJoin, setOnJoin] = useState(bot.autoStartOnGroupJoin === true);
604
+ const [onTopic, setOnTopic] = useState(bot.autoStartOnNewTopic === true);
605
+ const [prompt, setPrompt] = useState(typeof bot.autoStartOnGroupJoinPrompt === 'string' ? bot.autoStartOnGroupJoinPrompt : '');
606
+ const [status, setStatus] = useState(null);
607
+ const [busy, setBusy] = useState(null);
608
+ useEffect(() => {
609
+ setOnJoin(bot.autoStartOnGroupJoin === true);
610
+ setOnTopic(bot.autoStartOnNewTopic === true);
611
+ setPrompt(typeof bot.autoStartOnGroupJoinPrompt === 'string' ? bot.autoStartOnGroupJoinPrompt : '');
612
+ }, [bot.autoStartOnGroupJoin, bot.autoStartOnGroupJoinPrompt, bot.autoStartOnNewTopic]);
613
+ async function savePatch(patch, key) {
614
+ setBusy(key);
615
+ setStatus(null);
616
+ try {
617
+ const res = await putCardPref(patch);
618
+ setStatus(res.ok ? { text: `✓ ${tr('botDefaults.cardPrefSaved')}`, ok: true } : { text: `✗ ${responseErrorText(res)}` });
619
+ }
620
+ catch (e) {
621
+ setStatus({ text: `✗ ${caughtErrorText(e)}` });
622
+ }
623
+ finally {
624
+ setBusy(null);
625
+ }
626
+ }
627
+ return (_jsxs("div", { className: "bd-subsection", children: [_jsx("h4", { className: "bd-subsection-title", children: tr('botDefaults.sectionAutoStart') }), _jsx(ToggleRow, { checked: onJoin, disabled: busy === 'join', dataAction: "toggle-auto-join", title: tr('botDefaults.autoStartJoin'), help: tr('botDefaults.autoStartJoinHelp'), onChange: checked => {
628
+ setOnJoin(checked);
629
+ void savePatch({ autoStartOnGroupJoin: checked }, 'join');
630
+ } }), _jsx("div", { className: "bd-row", children: _jsxs("label", { children: [_jsx("span", { children: tr('botDefaults.autoStartJoinPrompt') }), _jsx("textarea", { "data-input": "autoJoinPrompt", rows: 3, placeholder: tr('botDefaults.autoStartJoinPromptPlaceholder'), value: prompt, onChange: event => setPrompt(event.currentTarget.value) })] }) }), _jsx(ToggleRow, { checked: onTopic, disabled: busy === 'topic', dataAction: "toggle-auto-topic", title: tr('botDefaults.autoStartTopic'), help: tr('botDefaults.autoStartTopicHelp'), onChange: checked => {
631
+ setOnTopic(checked);
632
+ void savePatch({ autoStartOnNewTopic: checked }, 'topic');
633
+ } }), _jsxs("div", { className: "actions", children: [_jsx("button", { type: "button", className: "primary", "data-action": "save-auto-join-prompt", disabled: busy === 'prompt', onClick: () => void savePatch({ autoStartOnGroupJoinPrompt: prompt }, 'prompt'), children: tr('botDefaults.autoStartJoinPromptSave') }), _jsx(StatusSpan, { status: status, attr: { 'data-auto-start-status': '' } })] })] }));
634
+ }
635
+ function SandboxSection(props) {
636
+ const tr = useT();
637
+ const { bot, patchBot } = props;
638
+ const [enabled, setEnabled] = useState(bot.sandbox === true);
639
+ const [status, setStatus] = useState(null);
640
+ const [busy, setBusy] = useState(false);
641
+ useEffect(() => setEnabled(bot.sandbox === true), [bot.sandbox]);
642
+ async function toggle(next) {
643
+ setEnabled(next);
644
+ setStatus(null);
645
+ setBusy(true);
646
+ try {
647
+ const res = await sendJson('PUT', `/api/bots/${encodeURIComponent(bot.larkAppId)}/sandbox`, { enabled: next });
648
+ if (res.ok && res.body.ok) {
649
+ setStatus({ text: `✓ ${tr('botDefaults.sandboxSaved')}`, ok: true });
650
+ patchBot(bot.larkAppId, { sandbox: res.body.sandbox === true });
651
+ }
652
+ else {
653
+ setStatus({ text: `✗ ${responseErrorText(res)}` });
654
+ setEnabled(!next);
655
+ }
656
+ }
657
+ catch (e) {
658
+ setStatus({ text: `✗ ${caughtErrorText(e)}` });
659
+ setEnabled(!next);
660
+ }
661
+ finally {
662
+ setBusy(false);
663
+ }
664
+ }
665
+ return (_jsxs("section", { className: "bd-section", children: [_jsx("h3", { className: "bd-section-title", children: tr('botDefaults.sectionSandbox') }), _jsx(ToggleRow, { checked: enabled, disabled: busy, dataAction: "toggle-sandbox", title: tr('botDefaults.sandboxToggle'), help: tr('botDefaults.sandboxHelp'), onChange: checked => void toggle(checked) }), _jsx("div", { className: "actions", children: _jsx(StatusSpan, { status: status, attr: { 'data-sandbox-status': '' } }) })] }));
666
+ }
667
+ function RoleSection(props) {
668
+ const tr = useT();
669
+ const { bot, patchBot } = props;
670
+ const [loaded, setLoaded] = useState(typeof bot.teamRole === 'string');
671
+ const [role, setRole] = useState(typeof bot.teamRole === 'string' ? bot.teamRole : '');
672
+ const [status, setStatus] = useState(null);
673
+ const [busy, setBusy] = useState(false);
674
+ useEffect(() => {
675
+ let active = true;
676
+ const roleUrl = `/api/team/local-bots/${encodeURIComponent(bot.larkAppId)}/role`;
677
+ if (typeof bot.teamRole === 'string') {
678
+ // Already resolved (incl. right after our own save, which patchBot's teamRole
679
+ // re-fires this effect) — sync the field but DON'T clear status, or the freshly
680
+ // set "✓ 已保存/已删除" toast gets wiped a frame later.
681
+ setLoaded(true);
682
+ setRole(bot.teamRole);
683
+ return () => { active = false; };
684
+ }
685
+ setStatus(null);
686
+ setLoaded(false);
687
+ setRole('');
688
+ void (async () => {
689
+ try {
690
+ const r = await fetch(roleUrl);
691
+ const body = await r.json().catch(() => ({}));
692
+ if (!active)
693
+ return;
694
+ if (r.ok && body.ok) {
695
+ const next = body.role ?? '';
696
+ setRole(next);
697
+ setLoaded(true);
698
+ patchBot(bot.larkAppId, { teamRole: next });
699
+ }
700
+ else {
701
+ setStatus({ text: `✗ ${tr('botDefaults.roleLoadErr')}: ${body.error ?? r.status}` });
702
+ }
703
+ }
704
+ catch (e) {
705
+ if (active)
706
+ setStatus({ text: `✗ ${tr('botDefaults.roleLoadErr')}: ${caughtErrorText(e)}` });
707
+ }
708
+ })();
709
+ return () => { active = false; };
710
+ }, [bot.larkAppId, bot.teamRole, patchBot, tr]);
711
+ async function putRole(nextRole, deleted) {
712
+ if (!loaded)
713
+ return;
714
+ setStatus(null);
715
+ setBusy(true);
716
+ try {
717
+ const res = await sendJson('PUT', `/api/team/local-bots/${encodeURIComponent(bot.larkAppId)}/role`, { role: nextRole });
718
+ if (res.ok && res.body.ok) {
719
+ const stored = nextRole.trim();
720
+ setRole(stored);
721
+ patchBot(bot.larkAppId, { teamRole: stored });
722
+ setStatus({ text: `✓ ${deleted ? tr('botDefaults.roleDeleted') : tr('botDefaults.roleSaved')}`, ok: true });
723
+ }
724
+ else {
725
+ setStatus({ text: `✗ ${responseErrorText(res)}` });
726
+ }
727
+ }
728
+ catch (e) {
729
+ setStatus({ text: `✗ ${caughtErrorText(e)}` });
730
+ }
731
+ finally {
732
+ setBusy(false);
733
+ }
734
+ }
735
+ return (_jsxs("section", { className: "bd-section", children: [_jsx("h3", { className: "bd-section-title", children: _jsx(FieldTitle, { help: tr('botDefaults.roleHelp'), children: tr('botDefaults.sectionRole') }) }), _jsx("textarea", { "data-input": "teamRole", rows: 6, placeholder: tr('botDefaults.rolePlaceholder'), disabled: !loaded || busy, value: role, onChange: event => setRole(event.currentTarget.value) }), _jsxs("div", { className: "actions", children: [_jsx("button", { type: "button", className: "primary", "data-action": "save-role", disabled: !loaded || busy, onClick: () => void putRole(role, role.trim() === ''), children: tr('botDefaults.roleSave') }), _jsx(StatusSpan, { status: status, attr: { 'data-role-status': '' } })] }), _jsx(ProfileRoles, { appId: bot.larkAppId })] }));
736
+ }
737
+ function ProfileRoles(props) {
738
+ const tr = useT();
739
+ const [state, setState] = useState({ loaded: false, loading: true, items: [] });
740
+ useEffect(() => {
741
+ let active = true;
742
+ setState({ loaded: false, loading: true, items: [] });
743
+ void (async () => {
744
+ try {
745
+ const r = await fetch('/api/role-profiles');
746
+ const body = await r.json().catch(() => ({}));
747
+ if (!active)
748
+ return;
749
+ if (!r.ok)
750
+ throw new Error(body?.error ?? String(r.status));
751
+ const profiles = Array.isArray(body.profiles) ? body.profiles : [];
752
+ const items = profiles
753
+ .filter((profile) => (profile.botEntries ?? []).some((entry) => entry?.larkAppId === props.appId && entry?.hasEntry))
754
+ .map((profile) => ({ profileId: String(profile.profileId) }));
755
+ setState({
756
+ loaded: true,
757
+ loading: false,
758
+ items,
759
+ });
760
+ }
761
+ catch (e) {
762
+ if (active)
763
+ setState({ loaded: true, loading: false, error: caughtErrorText(e), items: [] });
764
+ }
765
+ })();
766
+ return () => { active = false; };
767
+ }, [props.appId]);
768
+ async function loadDetail(profileId) {
769
+ const item = state.items.find(entry => entry.profileId === profileId);
770
+ if (!item || item.loaded || item.loading)
771
+ return;
772
+ setState(current => ({
773
+ ...current,
774
+ items: current.items.map(entry => entry.profileId === profileId ? { ...entry, loading: true } : entry),
775
+ }));
776
+ try {
777
+ const r = await fetch(`/api/role-profiles/${encodeURIComponent(profileId)}/${encodeURIComponent(props.appId)}`);
778
+ const body = await r.json().catch(() => ({}));
779
+ if (!r.ok)
780
+ throw new Error(body?.error ?? String(r.status));
781
+ setState(current => ({
782
+ ...current,
783
+ items: current.items.map(entry => entry.profileId === profileId
784
+ ? { ...entry, loading: false, loaded: true, content: body?.hasEntry ? String(body.content ?? '') : '' }
785
+ : entry),
786
+ }));
787
+ }
788
+ catch (e) {
789
+ setState(current => ({
790
+ ...current,
791
+ items: current.items.map(entry => entry.profileId === profileId ? { ...entry, loading: false, error: caughtErrorText(e) } : entry),
792
+ }));
793
+ }
794
+ }
795
+ let body;
796
+ if (state.loading)
797
+ body = _jsx(LoadingState, { label: tr('common.loading'), compact: true });
798
+ else if (state.error)
799
+ body = _jsx("p", { className: "hint-warn-inline", children: tr('botDefaults.profileRolesLoadFailed', { error: state.error }) });
800
+ else if (state.items.length === 0)
801
+ body = _jsx("p", { className: "empty", children: tr('botDefaults.profileRolesEmpty') });
802
+ else {
803
+ body = state.items.map(item => (_jsxs("details", { className: "bd-profile-role-entry", "data-profile-id": item.profileId, onToggle: event => {
804
+ if (event.currentTarget.open)
805
+ void loadDetail(item.profileId);
806
+ }, children: [_jsx("summary", { children: _jsx("span", { className: "bd-profile-role-id", children: item.profileId }) }), _jsx("div", { className: "bd-profile-role-content", "data-profile-role-body": item.profileId, children: item.loading ? _jsx(LoadingState, { label: tr('common.loading'), compact: true }) : item.error ? (_jsx("p", { className: "hint-warn-inline", children: tr('botDefaults.profileRoleDetailLoadFailed', { error: item.error }) })) : item.loaded ? (_jsx("pre", { children: item.content ?? '' })) : (_jsx("p", { className: "empty", children: tr('botDefaults.profileRoleClickToLoad') })) })] }, item.profileId)));
807
+ }
808
+ return (_jsxs("div", { className: "bd-profile-roles", "data-profile-roles": true, children: [_jsx("h4", { className: "bd-subsection-title", children: _jsx(FieldTitle, { help: tr('botDefaults.profileRolesHelp'), children: tr('botDefaults.profileRoles') }) }), _jsx("div", { className: "bd-profile-role-list", "data-profile-role-list": true, children: body })] }));
809
+ }
810
+ function CardBehaviorSection(props) {
811
+ const tr = useT();
812
+ const { bot, putCardPref } = props;
813
+ const [disableStreaming, setDisableStreaming] = useState(bot.disableStreamingCard === true);
814
+ const [silentReactions, setSilentReactions] = useState(bot.silentTurnReactions === true);
815
+ const [writableLink, setWritableLink] = useState(bot.writableTerminalLinkInCard === true);
816
+ const [privateCard, setPrivateCard] = useState(bot.privateCard === true);
817
+ const [status, setStatus] = useState(null);
818
+ const [busy, setBusy] = useState(null);
819
+ useEffect(() => {
820
+ setDisableStreaming(bot.disableStreamingCard === true);
821
+ setSilentReactions(bot.silentTurnReactions === true);
822
+ setWritableLink(bot.writableTerminalLinkInCard === true);
823
+ setPrivateCard(bot.privateCard === true);
824
+ }, [bot.disableStreamingCard, bot.privateCard, bot.silentTurnReactions, bot.writableTerminalLinkInCard]);
825
+ async function savePatch(patch, key) {
826
+ setBusy(key);
827
+ setStatus(null);
828
+ try {
829
+ const res = await putCardPref(patch);
830
+ setStatus(res.ok ? { text: `✓ ${tr('botDefaults.cardPrefSaved')}`, ok: true } : { text: `✗ ${responseErrorText(res)}` });
831
+ }
832
+ catch (e) {
833
+ setStatus({ text: `✗ ${caughtErrorText(e)}` });
834
+ }
835
+ finally {
836
+ setBusy(null);
837
+ }
838
+ }
839
+ return (_jsxs("section", { className: "bd-section", children: [_jsx("h3", { className: "bd-section-title", children: tr('botDefaults.sectionCard') }), _jsxs("div", { className: "bd-toggle-grid bd-card-behavior-grid", children: [_jsx(ToggleRow, { checked: disableStreaming, disabled: busy === 'streaming', dataAction: "toggle-disable-streaming", title: tr('botDefaults.disableStreaming'), help: tr('botDefaults.disableStreamingHelp'), onChange: checked => {
840
+ setDisableStreaming(checked);
841
+ void savePatch({ disableStreamingCard: checked }, 'streaming');
842
+ } }), _jsx(ToggleRow, { checked: silentReactions, disabled: !disableStreaming || busy === 'silent', dataAction: "toggle-silent-reactions", title: tr('botDefaults.silentTurnReactions'), help: tr('botDefaults.silentTurnReactionsHelp'), onChange: checked => {
843
+ setSilentReactions(checked);
844
+ void savePatch({ silentTurnReactions: checked }, 'silent');
845
+ } }), _jsx(ToggleRow, { checked: writableLink, disabled: disableStreaming || busy === 'writable', dataAction: "toggle-writable-link", title: tr('botDefaults.writableLink'), help: tr('botDefaults.writableLinkHelp'), onChange: checked => {
846
+ setWritableLink(checked);
847
+ void savePatch({ writableTerminalLinkInCard: checked }, 'writable');
848
+ } }), _jsx(ToggleRow, { checked: privateCard, disabled: busy === 'private', dataAction: "toggle-private-card", title: tr('botDefaults.privateCard'), help: tr('botDefaults.privateCardHelp'), onChange: checked => {
849
+ setPrivateCard(checked);
850
+ void savePatch({ privateCard: checked }, 'private');
851
+ } })] }), _jsxs("div", { className: "actions", children: [_jsx("small", { "data-card-pref-moot": true, className: "hint-warn-inline", hidden: !disableStreaming, children: tr('botDefaults.writableLinkMoot') }), _jsx(StatusSpan, { status: status, attr: { 'data-card-pref-status': '' } })] })] }));
852
+ }
853
+ function CrossBotSection(props) {
854
+ const tr = useT();
855
+ const [sameDir, setSameDir] = useState(props.bot.botToBotSameDir !== false);
856
+ const [status, setStatus] = useState(null);
857
+ const [busy, setBusy] = useState(false);
858
+ useEffect(() => setSameDir(props.bot.botToBotSameDir !== false), [props.bot.botToBotSameDir]);
859
+ async function save(next) {
860
+ setSameDir(next);
861
+ setBusy(true);
862
+ setStatus(null);
863
+ try {
864
+ const res = await props.putCardPref({ botToBotSameDir: next });
865
+ setStatus(res.ok ? { text: `✓ ${tr('botDefaults.cardPrefSaved')}`, ok: true } : { text: `✗ ${responseErrorText(res)}` });
866
+ }
867
+ catch (e) {
868
+ setStatus({ text: `✗ ${caughtErrorText(e)}` });
869
+ }
870
+ finally {
871
+ setBusy(false);
872
+ }
873
+ }
874
+ return (_jsxs("section", { className: "bd-section", children: [_jsx("h3", { className: "bd-section-title", children: tr('botDefaults.sectionCrossBot') }), _jsx(ToggleRow, { checked: sameDir, disabled: busy, dataAction: "toggle-cross-bot-samedir", title: tr('botDefaults.botToBotSameDir'), help: tr('botDefaults.botToBotSameDirHelp'), onChange: checked => void save(checked) }), _jsx("div", { className: "actions", children: _jsx(StatusSpan, { status: status, attr: { 'data-crossbot-status': '' } }) })] }));
875
+ }
876
+ function SummaryTriggerSection(props) {
877
+ const tr = useT();
878
+ const initial = summaryRange(props.bot);
879
+ const [limit, setLimit] = useState(String(initial.limit));
880
+ const [sinceHours, setSinceHours] = useState(String(initial.sinceHours));
881
+ const [status, setStatus] = useState(null);
882
+ const [busy, setBusy] = useState(false);
883
+ useEffect(() => {
884
+ const next = summaryRange(props.bot);
885
+ setLimit(String(next.limit));
886
+ setSinceHours(String(next.sinceHours));
887
+ }, [props.bot.summaryRange?.limit, props.bot.summaryRange?.sinceHours]);
888
+ async function save() {
889
+ setStatus(null);
890
+ const nextLimit = nonNegativeInteger(limit, 50);
891
+ const nextSinceHours = nonNegativeInteger(sinceHours, 24);
892
+ if (nextLimit == null || nextSinceHours == null) {
893
+ setStatus({ text: `✗ ${tr('botDefaults.summaryNumberInvalid')}` });
894
+ return;
895
+ }
896
+ setBusy(true);
897
+ try {
898
+ const res = await sendJson('PUT', `/api/bots/${encodeURIComponent(props.bot.larkAppId)}/summary-range`, {
899
+ limit: nextLimit,
900
+ sinceHours: nextSinceHours,
901
+ });
902
+ if (res.ok && res.body.ok) {
903
+ const next = res.body.summaryRange ?? { limit: nextLimit, sinceHours: nextSinceHours };
904
+ const normalized = summaryRange({ ...props.bot, summaryRange: next });
905
+ setLimit(String(normalized.limit));
906
+ setSinceHours(String(normalized.sinceHours));
907
+ props.patchBot(props.bot.larkAppId, { summaryRange: next });
908
+ setStatus({ text: `✓ ${tr('botDefaults.cardPrefSaved')}`, ok: true });
909
+ }
910
+ else {
911
+ setStatus({ text: `✗ ${responseErrorText(res)}` });
912
+ }
913
+ }
914
+ catch (e) {
915
+ setStatus({ text: `✗ ${caughtErrorText(e)}` });
916
+ }
917
+ finally {
918
+ setBusy(false);
919
+ }
920
+ }
921
+ return (_jsxs("section", { className: "bd-section", children: [_jsx("h3", { className: "bd-section-title", children: _jsx(FieldTitle, { help: tr('botDefaults.summaryLimitHelp'), children: tr('botDefaults.sectionSummaryTrigger') }) }), _jsxs("div", { className: "bd-row bd-summary-limits", children: [_jsxs("label", { children: [_jsx("span", { children: tr('botDefaults.summaryLimit') }), _jsx("input", { type: "number", min: 0, step: 1, "data-input": "summaryLimit", value: limit, disabled: busy, onChange: event => setLimit(event.currentTarget.value) })] }), _jsxs("label", { children: [_jsx("span", { children: tr('botDefaults.summarySinceHours') }), _jsx("input", { type: "number", min: 0, step: 1, "data-input": "summarySinceHours", value: sinceHours, disabled: busy, onChange: event => setSinceHours(event.currentTarget.value) })] })] }), _jsxs("div", { className: "actions", children: [_jsx("button", { type: "button", className: "primary", "data-action": "save-summary-trigger", disabled: busy, onClick: () => void save(), children: tr('botDefaults.summarySave') }), _jsx(StatusSpan, { status: status, attr: { 'data-summary-trigger-status': '' } })] })] }));
922
+ }
923
+ function summaryRange(bot) {
924
+ const range = bot.summaryRange ?? { limit: 50, sinceHours: 24 };
925
+ return {
926
+ limit: Number.isInteger(range.limit) && Number(range.limit) >= 0 ? Number(range.limit) : 50,
927
+ sinceHours: Number.isInteger(range.sinceHours) && Number(range.sinceHours) >= 0 ? Number(range.sinceHours) : 24,
928
+ };
929
+ }
930
+ function SessionModeSection(props) {
931
+ const tr = useT();
932
+ const [p2p, setP2p] = useState(props.bot.p2pMode === 'chat' ? 'chat' : 'thread');
933
+ const [regular, setRegular] = useState(regularGroupMode(props.bot));
934
+ const [mention, setMention] = useState(mentionMode(props.bot));
935
+ const [docMode, setDocMode] = useState(props.bot.docSubscribeDefaultMode === 'all' ? 'all' : 'mention-only');
936
+ const [busy, setBusy] = useState(null);
937
+ const [p2pStatus, setP2pStatus] = useState(null);
938
+ const [regularStatus, setRegularStatus] = useState(null);
939
+ const [mentionStatus, setMentionStatus] = useState(null);
940
+ const [docStatus, setDocStatus] = useState(null);
941
+ useEffect(() => {
942
+ setP2p(props.bot.p2pMode === 'chat' ? 'chat' : 'thread');
943
+ setRegular(regularGroupMode(props.bot));
944
+ setMention(mentionMode(props.bot));
945
+ setDocMode(props.bot.docSubscribeDefaultMode === 'all' ? 'all' : 'mention-only');
946
+ }, [
947
+ props.bot.docSubscribeDefaultMode,
948
+ props.bot.p2pMode,
949
+ props.bot.regularGroupMentionMode,
950
+ props.bot.regularGroupReplyMode,
951
+ ]);
952
+ async function saveP2p(next) {
953
+ const mode = next === 'chat' ? 'chat' : 'thread';
954
+ setP2p(mode);
955
+ setBusy('p2p');
956
+ setP2pStatus(null);
957
+ try {
958
+ const res = await sendJson('PUT', `/api/bots/${encodeURIComponent(props.bot.larkAppId)}/p2p-mode`, { p2pMode: mode });
959
+ if (res.ok && res.body.ok) {
960
+ props.patchBot(props.bot.larkAppId, { p2pMode: res.body.p2pMode === 'chat' ? 'chat' : 'thread' });
961
+ setP2pStatus({ text: `✓ ${tr('botDefaults.cardPrefSaved')}`, ok: true });
962
+ }
963
+ else {
964
+ setP2pStatus({ text: `✗ ${responseErrorText(res)}` });
965
+ }
966
+ }
967
+ catch (e) {
968
+ setP2pStatus({ text: `✗ ${caughtErrorText(e)}` });
969
+ }
970
+ finally {
971
+ setBusy(null);
972
+ }
973
+ }
974
+ async function saveCardMode(key, patch, setStatus) {
975
+ setBusy(key);
976
+ setStatus(null);
977
+ try {
978
+ const res = await props.putCardPref(patch);
979
+ setStatus(res.ok ? { text: `✓ ${tr('botDefaults.cardPrefSaved')}`, ok: true } : { text: `✗ ${responseErrorText(res)}` });
980
+ }
981
+ catch (e) {
982
+ setStatus({ text: `✗ ${caughtErrorText(e)}` });
983
+ }
984
+ finally {
985
+ setBusy(null);
986
+ }
987
+ }
988
+ const p2pOptions = [
989
+ { value: 'thread', label: tr('botDefaults.p2pThread') },
990
+ { value: 'chat', label: tr('botDefaults.p2pChat') },
991
+ ];
992
+ const regularOptions = [
993
+ { value: 'chat', label: tr('botDefaults.regularGroupModeChat') },
994
+ { value: 'chat-topic', label: tr('botDefaults.regularGroupModeChatTopic') },
995
+ { value: 'new-topic', label: tr('botDefaults.regularGroupModeNewTopic') },
996
+ { value: 'shared', label: tr('botDefaults.regularGroupModeShared') },
997
+ ];
998
+ const mentionOptions = [
999
+ { value: 'always', label: tr('botDefaults.mentionModeAlways') },
1000
+ { value: 'topic', label: tr('botDefaults.mentionModeTopic') },
1001
+ { value: 'never', label: tr('botDefaults.mentionModeNever') },
1002
+ { value: 'ambient', label: tr('botDefaults.mentionModeAmbient') },
1003
+ ];
1004
+ const docOptions = [
1005
+ { value: 'mention-only', label: tr('botDefaults.docSubscribeModeMention') },
1006
+ { value: 'all', label: tr('botDefaults.docSubscribeModeAll') },
1007
+ ];
1008
+ return (_jsxs("section", { className: "bd-section", children: [_jsx("h3", { className: "bd-section-title", children: tr('botDefaults.sectionSessionMode') }), _jsxs("div", { className: "bd-row", children: [_jsxs("div", { className: "bd-field", children: [_jsx(FieldTitle, { help: tr('botDefaults.p2pHelp'), children: tr('botDefaults.p2pMode') }), _jsx(DropdownField, { dataInput: "p2pMode", ariaLabel: tr('botDefaults.p2pMode'), value: p2p, disabled: busy === 'p2p', options: p2pOptions, onChange: next => void saveP2p(next) })] }), _jsx("div", { className: "actions", children: _jsx(StatusSpan, { status: p2pStatus, attr: { 'data-p2p-status': '' } }) })] }), _jsxs("div", { className: "bd-row", children: [_jsxs("div", { className: "bd-field", children: [_jsx(FieldTitle, { help: tr('botDefaults.regularGroupModeHelp'), children: tr('botDefaults.regularGroupMode') }), _jsx(DropdownField, { dataInput: "regularGroupMode", ariaLabel: tr('botDefaults.regularGroupMode'), value: regular, disabled: busy === 'regular', options: regularOptions, onChange: next => {
1009
+ setRegular(next);
1010
+ void saveCardMode('regular', { regularGroupReplyMode: next }, setRegularStatus);
1011
+ } })] }), _jsx("div", { className: "actions", children: _jsx(StatusSpan, { status: regularStatus, attr: { 'data-regular-group-status': '' } }) })] }), _jsxs("div", { className: "bd-row", children: [_jsxs("div", { className: "bd-field", children: [_jsx(FieldTitle, { help: tr('botDefaults.mentionModeHelp'), children: tr('botDefaults.mentionMode') }), _jsx(DropdownField, { dataInput: "regularGroupMentionMode", ariaLabel: tr('botDefaults.mentionMode'), value: mention, disabled: busy === 'mention', options: mentionOptions, onChange: next => {
1012
+ setMention(next);
1013
+ void saveCardMode('mention', { regularGroupMentionMode: next }, setMentionStatus);
1014
+ } })] }), _jsx("div", { className: "actions", children: _jsx(StatusSpan, { status: mentionStatus, attr: { 'data-mention-mode-status': '' } }) })] }), _jsxs("div", { className: "bd-row", children: [_jsxs("div", { className: "bd-field", children: [_jsx(FieldTitle, { help: tr('botDefaults.docSubscribeModeHelp'), children: tr('botDefaults.docSubscribeMode') }), _jsx(DropdownField, { dataInput: "docSubscribeDefaultMode", ariaLabel: tr('botDefaults.docSubscribeMode'), value: docMode, disabled: busy === 'doc', options: docOptions, onChange: next => {
1015
+ setDocMode(next);
1016
+ void saveCardMode('doc', { docSubscribeDefaultMode: next }, setDocStatus);
1017
+ } })] }), _jsx("div", { className: "actions", children: _jsx(StatusSpan, { status: docStatus, attr: { 'data-doc-subscribe-mode-status': '' } }) })] })] }));
1018
+ }
1019
+ function SubstituteModeSection(props) {
1020
+ const tr = useT();
1021
+ const initial = props.bot.substituteMode ?? null;
1022
+ const [enabled, setEnabled] = useState(initial?.enabled === true);
1023
+ const [disclosure, setDisclosure] = useState(initial?.disclosure === 'none' ? 'none' : 'prefix');
1024
+ const [status, setStatus] = useState(null);
1025
+ const [busy, setBusy] = useState(false);
1026
+ const targetSequence = useRef(0);
1027
+ const skipModeSync = useRef(false);
1028
+ function makeTargetDraft(target) {
1029
+ const idField = substituteTargetIdField(target);
1030
+ return {
1031
+ key: ++targetSequence.current,
1032
+ idField,
1033
+ idValue: target?.[idField] ?? '',
1034
+ name: target?.name ?? '',
1035
+ persisted: target ? { ...target } : {},
1036
+ originalIdField: target ? idField : undefined,
1037
+ };
1038
+ }
1039
+ const [targetRows, setTargetRows] = useState(() => {
1040
+ const targets = initial?.targets ?? [];
1041
+ return targets.length ? targets.map(target => makeTargetDraft(target)) : [makeTargetDraft()];
1042
+ });
1043
+ useEffect(() => {
1044
+ if (skipModeSync.current) {
1045
+ skipModeSync.current = false;
1046
+ return;
1047
+ }
1048
+ const next = props.bot.substituteMode ?? null;
1049
+ setEnabled(next?.enabled === true);
1050
+ setDisclosure(next?.disclosure === 'none' ? 'none' : 'prefix');
1051
+ const targets = next?.targets ?? [];
1052
+ setTargetRows(targets.length ? targets.map(target => makeTargetDraft(target)) : [makeTargetDraft()]);
1053
+ }, [props.bot.larkAppId, props.bot.substituteMode]);
1054
+ async function save(body) {
1055
+ setBusy(true);
1056
+ setStatus(null);
1057
+ try {
1058
+ const res = await sendJson('PUT', `/api/bots/${encodeURIComponent(props.bot.larkAppId)}/substitute-mode`, body);
1059
+ if (res.ok && res.body.ok) {
1060
+ const next = res.body.substituteMode && typeof res.body.substituteMode === 'object'
1061
+ ? res.body.substituteMode
1062
+ : null;
1063
+ const resolution = Array.isArray(res.body?.resolution)
1064
+ ? res.body.resolution
1065
+ : [];
1066
+ const unresolved = resolution
1067
+ .filter(entry => entry?.ok === false)
1068
+ .map(entry => String(entry.input ?? '').trim())
1069
+ .filter(Boolean);
1070
+ setEnabled(next?.enabled === true);
1071
+ setDisclosure(next?.disclosure === 'none' ? 'none' : 'prefix');
1072
+ if (resolution.length) {
1073
+ skipModeSync.current = true;
1074
+ setTargetRows(rows => {
1075
+ const pending = [...resolution];
1076
+ return rows.map(row => {
1077
+ const input = row.idValue.trim();
1078
+ const index = pending.findIndex(entry => String(entry.input ?? '').trim() === input);
1079
+ if (index < 0)
1080
+ return row;
1081
+ const entry = pending.splice(index, 1)[0];
1082
+ if (entry?.ok !== true)
1083
+ return row;
1084
+ const persisted = { ...row.persisted };
1085
+ if (entry.openId)
1086
+ persisted.openId = entry.openId;
1087
+ if (row.idField === 'email')
1088
+ persisted.email = input;
1089
+ if (entry.name)
1090
+ persisted.name = entry.name;
1091
+ return { ...row, name: entry.name ?? row.name, persisted };
1092
+ });
1093
+ });
1094
+ }
1095
+ else {
1096
+ const targets = next?.targets ?? [];
1097
+ setTargetRows(targets.length ? targets.map(target => makeTargetDraft(target)) : [makeTargetDraft()]);
1098
+ }
1099
+ props.patchBot(props.bot.larkAppId, { substituteMode: next });
1100
+ setStatus(unresolved.length
1101
+ ? { text: `✗ ${tr('botDefaults.substituteTargetsInvalid')}: ${unresolved.join(', ')}` }
1102
+ : { text: `✓ ${tr('botDefaults.cardPrefSaved')}`, ok: true });
1103
+ }
1104
+ else {
1105
+ const unresolved = Array.isArray(res.body?.resolution)
1106
+ ? res.body.resolution
1107
+ .filter((entry) => entry?.ok === false)
1108
+ .map((entry) => String(entry.input ?? '').trim())
1109
+ .filter(Boolean)
1110
+ : [];
1111
+ setStatus({ text: unresolved.length
1112
+ ? `✗ ${tr('botDefaults.substituteTargetsInvalid')}: ${unresolved.join(', ')}`
1113
+ : `✗ ${responseErrorText(res)}` });
1114
+ }
1115
+ }
1116
+ catch (error) {
1117
+ setStatus({ text: `✗ ${caughtErrorText(error)}` });
1118
+ }
1119
+ finally {
1120
+ setBusy(false);
1121
+ }
1122
+ }
1123
+ function saveCurrent() {
1124
+ const targets = [];
1125
+ let invalid = false;
1126
+ for (const row of targetRows) {
1127
+ const target = buildSubstituteTarget(row);
1128
+ if (!target) {
1129
+ invalid ||= Boolean(row.name.trim());
1130
+ continue;
1131
+ }
1132
+ targets.push(target);
1133
+ }
1134
+ if (invalid || (enabled && targets.length === 0)) {
1135
+ setStatus({ text: `✗ ${tr('botDefaults.substituteTargetsInvalid')}` });
1136
+ return;
1137
+ }
1138
+ void save({ enabled, targets, disclosure });
1139
+ }
1140
+ const disclosureOptions = [
1141
+ { value: 'prefix', label: tr('botDefaults.substituteDisclosurePrefix') },
1142
+ { value: 'none', label: tr('botDefaults.substituteDisclosureNone') },
1143
+ ];
1144
+ return (_jsxs("section", { className: "bd-section bd-substitute-section", children: [_jsx("h3", { className: "bd-section-title", children: tr('botDefaults.sectionSubstitute') }), _jsx(ToggleRow, { checked: enabled, disabled: busy, dataAction: "toggle-substitute-mode", title: tr('botDefaults.substituteEnabled'), help: tr('botDefaults.substituteHelp'), onChange: setEnabled }), _jsx("div", { className: "bd-row", children: _jsxs("div", { className: "bd-field", children: [_jsx(FieldTitle, { children: tr('botDefaults.substituteDisclosure') }), _jsx(DropdownField, { dataInput: "substituteDisclosure", ariaLabel: tr('botDefaults.substituteDisclosure'), value: disclosure, disabled: busy, options: disclosureOptions, onChange: value => setDisclosure(value) })] }) }), _jsxs("div", { className: "bd-row bd-substitute-targets", children: [_jsx(FieldTitle, { help: tr('botDefaults.substituteTargetsHelp'), children: tr('botDefaults.substituteTargets') }), _jsxs("div", { className: "bd-substitute-target-list", "data-input": "substituteTargets", children: [targetRows.map((target, index) => (_jsxs("div", { className: "bd-substitute-target-row", children: [_jsx(DropdownField, { dataInput: `substituteTargetType-${target.key}`, className: "bd-substitute-target-type", ariaLabel: `${tr('botDefaults.substituteTargetType')} ${index + 1}`, value: target.idField, disabled: busy, options: substituteTargetIdFields.map(value => ({
1145
+ value,
1146
+ label: tr(`botDefaults.substituteTarget${value[0].toUpperCase()}${value.slice(1)}`),
1147
+ })), onChange: idField => {
1148
+ setTargetRows(rows => rows.map(row => row.key === target.key
1149
+ ? { ...row, idField, idValue: row.persisted[idField] ?? '' }
1150
+ : row));
1151
+ } }), _jsx("input", { className: "bd-substitute-target-id", type: "text", "data-input": `substituteTargetId-${target.key}`, "aria-label": `${tr('botDefaults.substituteTargetType')} ${index + 1}`, placeholder: tr('botDefaults.substituteTargetIdPlaceholder'), value: target.idValue, disabled: busy, onChange: event => {
1152
+ const idValue = event.currentTarget.value;
1153
+ setTargetRows(rows => rows.map(row => row.key === target.key ? { ...row, idValue } : row));
1154
+ } }), _jsx("input", { className: "bd-substitute-target-name", type: "text", "data-input": `substituteTargetName-${target.key}`, "aria-label": `${tr('botDefaults.substituteTargetName')} ${index + 1}`, placeholder: tr('botDefaults.substituteTargetNamePlaceholder'), value: target.name, disabled: busy, onChange: event => {
1155
+ const name = event.currentTarget.value;
1156
+ setTargetRows(rows => rows.map(row => row.key === target.key ? { ...row, name } : row));
1157
+ } }), _jsx("button", { type: "button", className: "bd-substitute-target-remove", "data-action": "remove-substitute-target", title: tr('botDefaults.substituteTargetRemove'), "aria-label": tr('botDefaults.substituteTargetRemove'), disabled: busy, onClick: () => {
1158
+ setTargetRows(rows => {
1159
+ const remaining = rows.filter(row => row.key !== target.key);
1160
+ return remaining.length ? remaining : [makeTargetDraft()];
1161
+ });
1162
+ }, children: _jsx("span", { "aria-hidden": "true", children: "\u00D7" }) })] }, target.key))), _jsx("button", { type: "button", className: "bd-substitute-target-add", "data-action": "add-substitute-target", title: tr('botDefaults.substituteTargetAdd'), "aria-label": tr('botDefaults.substituteTargetAdd'), disabled: busy, onClick: () => setTargetRows(rows => [...rows, makeTargetDraft()]), children: _jsx("span", { "aria-hidden": "true", children: "+" }) })] })] }), _jsxs("div", { className: "actions", children: [_jsx("button", { type: "button", className: "primary", "data-action": "save-substitute-mode", disabled: busy, onClick: saveCurrent, children: tr('botDefaults.substituteSave') }), _jsx("button", { type: "button", "data-action": "off-substitute-mode", disabled: busy, onClick: () => void save({ enabled: false, targets: [] }), children: tr('botDefaults.substituteOff') }), _jsx(StatusSpan, { status: status, attr: { 'data-substitute-status': '' } })] })] }));
1163
+ }
1164
+ function regularGroupMode(bot) {
1165
+ return bot.regularGroupReplyMode === 'new-topic' || bot.regularGroupReplyMode === 'shared' || bot.regularGroupReplyMode === 'chat-topic'
1166
+ ? bot.regularGroupReplyMode
1167
+ : 'chat';
1168
+ }
1169
+ function mentionMode(bot) {
1170
+ return bot.regularGroupMentionMode === 'topic' || bot.regularGroupMentionMode === 'never' || bot.regularGroupMentionMode === 'ambient'
1171
+ ? bot.regularGroupMentionMode
1172
+ : 'always';
1173
+ }
1174
+ function SessionCapSection(props) {
1175
+ const tr = useT();
1176
+ const initial = typeof props.bot.maxLiveWorkers === 'number' ? props.bot.maxLiveWorkers : null;
1177
+ const logical = Number.isFinite(props.bot.logicalSessionCount) ? Number(props.bot.logicalSessionCount) : 0;
1178
+ const resident = Number.isFinite(props.bot.residentSessionCount) ? Number(props.bot.residentSessionCount) : 0;
1179
+ const dormant = Number.isFinite(props.bot.dormantSessionCount) ? Number(props.bot.dormantSessionCount) : 0;
1180
+ const [cap, setCap] = useState(initial);
1181
+ const effectiveCap = cap ?? 30;
1182
+ const [input, setInput] = useState(initial == null ? '' : String(initial));
1183
+ const [status, setStatus] = useState(null);
1184
+ const [busy, setBusy] = useState(false);
1185
+ useEffect(() => {
1186
+ const next = typeof props.bot.maxLiveWorkers === 'number' ? props.bot.maxLiveWorkers : null;
1187
+ setCap(next);
1188
+ setInput(next == null ? '' : String(next));
1189
+ }, [props.bot.maxLiveWorkers]);
1190
+ async function save(value) {
1191
+ setStatus(null);
1192
+ setBusy(true);
1193
+ try {
1194
+ const res = await sendJson('PUT', `/api/bots/${encodeURIComponent(props.bot.larkAppId)}/max-live-workers`, { maxLiveWorkers: value });
1195
+ if (res.ok && res.body.ok) {
1196
+ const next = typeof res.body.maxLiveWorkers === 'number' ? res.body.maxLiveWorkers : null;
1197
+ setCap(next);
1198
+ setInput(next == null ? '' : String(next));
1199
+ props.patchBot(props.bot.larkAppId, { maxLiveWorkers: next });
1200
+ setStatus({ text: `✓ ${tr('botDefaults.cardPrefSaved')}`, ok: true });
1201
+ }
1202
+ else {
1203
+ setStatus({ text: `✗ ${responseErrorText(res)}` });
1204
+ }
1205
+ }
1206
+ catch (e) {
1207
+ setStatus({ text: `✗ ${caughtErrorText(e)}` });
1208
+ }
1209
+ finally {
1210
+ setBusy(false);
1211
+ }
1212
+ }
1213
+ function saveInput() {
1214
+ const parsed = positiveIntegerOrNull(input);
1215
+ if (parsed === 'invalid') {
1216
+ setStatus({ text: `✗ ${tr('botDefaults.maxLiveWorkersInvalid')}` });
1217
+ return;
1218
+ }
1219
+ void save(parsed);
1220
+ }
1221
+ return (_jsxs("div", { className: "bd-subsection", children: [_jsx("h4", { className: "bd-subsection-title", children: tr('botDefaults.sectionSessionCap') }), _jsxs("div", { className: "bd-row bd-quota", children: [_jsxs("label", { children: [_jsx(FieldTitle, { help: tr('botDefaults.maxLiveWorkersHelp'), children: tr('botDefaults.maxLiveWorkers') }), _jsx("input", { type: "number", min: 1, step: 1, "data-input": "maxLiveWorkers", placeholder: tr('botDefaults.maxLiveWorkersPlaceholder'), value: input, disabled: busy, onChange: event => setInput(event.currentTarget.value) })] }), _jsx("small", { "data-session-cap-state": true, children: sessionCapStateLabel(cap, tr) }), _jsx("small", { className: "bd-help bd-session-residency", children: tr('botDefaults.maxLiveWorkersUsage', {
1222
+ resident,
1223
+ cap: effectiveCap,
1224
+ dormant,
1225
+ logical,
1226
+ }) })] }), _jsxs("div", { className: "actions", children: [_jsx("button", { type: "button", className: "primary", "data-action": "save-session-cap", disabled: busy, onClick: saveInput, children: tr('botDefaults.maxLiveWorkersSave') }), _jsx("button", { type: "button", "data-action": "off-session-cap", disabled: busy, onClick: () => { setInput(''); void save(null); }, children: tr('botDefaults.maxLiveWorkersOff') }), _jsx(StatusSpan, { status: status, attr: { 'data-session-cap-status': '' } })] })] }));
1227
+ }
1228
+ function StartupCommandsSection(props) {
1229
+ const tr = useT();
1230
+ const [value, setValue] = useState(typeof props.bot.startupCommands === 'string' ? props.bot.startupCommands : '');
1231
+ const [status, setStatus] = useState(null);
1232
+ const [busy, setBusy] = useState(false);
1233
+ useEffect(() => setValue(typeof props.bot.startupCommands === 'string' ? props.bot.startupCommands : ''), [props.bot.startupCommands]);
1234
+ async function save() {
1235
+ setStatus(null);
1236
+ setBusy(true);
1237
+ try {
1238
+ const res = await sendJson('PUT', `/api/bots/${encodeURIComponent(props.bot.larkAppId)}/startup-commands`, { startupCommands: value });
1239
+ if (res.ok && res.body.ok) {
1240
+ const next = typeof res.body.startupCommands === 'string' ? res.body.startupCommands : '';
1241
+ setValue(next);
1242
+ props.patchBot(props.bot.larkAppId, { startupCommands: next });
1243
+ setStatus({ text: `✓ ${tr('botDefaults.cardPrefSaved')}`, ok: true });
1244
+ }
1245
+ else {
1246
+ setStatus({ text: `✗ ${responseErrorText(res)}` });
1247
+ }
1248
+ }
1249
+ catch (e) {
1250
+ setStatus({ text: `✗ ${caughtErrorText(e)}` });
1251
+ }
1252
+ finally {
1253
+ setBusy(false);
1254
+ }
1255
+ }
1256
+ return (_jsxs("div", { className: "bd-subsection", children: [_jsx("h4", { className: "bd-subsection-title", children: _jsx(FieldTitle, { help: tr('botDefaults.startupCommandsHelp'), children: tr('botDefaults.sectionStartupCommands') }) }), _jsx("textarea", { "data-input": "startupCommands", rows: 3, placeholder: tr('botDefaults.startupCommandsPlaceholder'), value: value, disabled: busy, onChange: event => setValue(event.currentTarget.value) }), _jsxs("div", { className: "actions", children: [_jsx("button", { type: "button", className: "primary", "data-action": "save-startup-commands", disabled: busy, onClick: () => void save(), children: tr('botDefaults.startupCommandsSave') }), _jsx(StatusSpan, { status: status, attr: { 'data-startup-commands-status': '' } })] })] }));
1257
+ }
1258
+ function LaunchShellSection(props) {
1259
+ const tr = useT();
1260
+ const [value, setValue] = useState(typeof props.bot.launchShell === 'string' ? props.bot.launchShell : '');
1261
+ const [status, setStatus] = useState(null);
1262
+ const [busy, setBusy] = useState(false);
1263
+ useEffect(() => setValue(typeof props.bot.launchShell === 'string' ? props.bot.launchShell : ''), [props.bot.launchShell]);
1264
+ async function save() {
1265
+ setStatus(null);
1266
+ setBusy(true);
1267
+ try {
1268
+ const res = await sendJson('PUT', `/api/bots/${encodeURIComponent(props.bot.larkAppId)}/launch-shell`, { launchShell: value });
1269
+ if (res.ok && res.body.ok) {
1270
+ const next = typeof res.body.launchShell === 'string' ? res.body.launchShell : '';
1271
+ setValue(next);
1272
+ props.patchBot(props.bot.larkAppId, { launchShell: next });
1273
+ setStatus({ text: `✓ ${tr('botDefaults.cardPrefSaved')}`, ok: true });
1274
+ }
1275
+ else {
1276
+ setStatus({ text: `✗ ${responseErrorText(res)}` });
1277
+ }
1278
+ }
1279
+ catch (e) {
1280
+ setStatus({ text: `✗ ${caughtErrorText(e)}` });
1281
+ }
1282
+ finally {
1283
+ setBusy(false);
1284
+ }
1285
+ }
1286
+ return (_jsxs("div", { className: "bd-subsection", children: [_jsx("h4", { className: "bd-subsection-title", children: _jsx(FieldTitle, { help: tr('botDefaults.launchShellHelp'), children: tr('botDefaults.sectionLaunchShell') }) }), _jsx("input", { type: "text", "data-input": "launchShell", placeholder: tr('botDefaults.launchShellPlaceholder'), value: value, disabled: busy, onChange: event => setValue(event.currentTarget.value) }), _jsxs("div", { className: "actions", children: [_jsx("button", { type: "button", className: "primary", "data-action": "save-launch-shell", disabled: busy, onClick: () => void save(), children: tr('botDefaults.launchShellSave') }), _jsx(StatusSpan, { status: status, attr: { 'data-launch-shell-status': '' } })] })] }));
1287
+ }
1288
+ function EnvSection(props) {
1289
+ const tr = useT();
1290
+ const [value, setValue] = useState(typeof props.bot.env === 'string' ? props.bot.env : '');
1291
+ const [status, setStatus] = useState(null);
1292
+ const [busy, setBusy] = useState(false);
1293
+ useEffect(() => setValue(typeof props.bot.env === 'string' ? props.bot.env : ''), [props.bot.env]);
1294
+ async function save() {
1295
+ setStatus(null);
1296
+ setBusy(true);
1297
+ try {
1298
+ const res = await sendJson('PUT', `/api/bots/${encodeURIComponent(props.bot.larkAppId)}/env`, { env: value });
1299
+ if (res.ok && res.body.ok) {
1300
+ const next = typeof res.body.env === 'string' ? res.body.env : '';
1301
+ setValue(next);
1302
+ props.patchBot(props.bot.larkAppId, { env: next });
1303
+ setStatus({ text: `✓ ${tr('botDefaults.cardPrefSaved')}`, ok: true });
1304
+ }
1305
+ else {
1306
+ setStatus({ text: `✗ ${responseErrorText(res)}` });
1307
+ }
1308
+ }
1309
+ catch (e) {
1310
+ setStatus({ text: `✗ ${caughtErrorText(e)}` });
1311
+ }
1312
+ finally {
1313
+ setBusy(false);
1314
+ }
1315
+ }
1316
+ return (_jsxs("div", { className: "bd-subsection", children: [_jsx("h4", { className: "bd-subsection-title", children: _jsx(FieldTitle, { help: tr('botDefaults.envHelp'), children: tr('botDefaults.sectionEnv') }) }), _jsx("textarea", { "data-input": "env", rows: 5, placeholder: tr('botDefaults.envPlaceholder'), value: value, disabled: busy, onChange: event => setValue(event.currentTarget.value) }), _jsxs("div", { className: "actions", children: [_jsx("button", { type: "button", className: "primary", "data-action": "save-env", disabled: busy, onClick: () => void save(), children: tr('botDefaults.envSave') }), _jsx(StatusSpan, { status: status, attr: { 'data-env-status': '' } })] })] }));
1317
+ }
1318
+ function BrandSection(props) {
1319
+ const tr = useT();
1320
+ const initial = props.bot.brandLabel ?? null;
1321
+ const [brand, setBrand] = useState(initial);
1322
+ const [input, setInput] = useState(initial ?? '');
1323
+ const [status, setStatus] = useState(null);
1324
+ const [busy, setBusy] = useState(false);
1325
+ useEffect(() => {
1326
+ const next = props.bot.brandLabel ?? null;
1327
+ setBrand(next);
1328
+ setInput(next ?? '');
1329
+ }, [props.bot.brandLabel]);
1330
+ async function save(nextBrand) {
1331
+ setStatus(null);
1332
+ setBusy(true);
1333
+ try {
1334
+ const res = await sendJson('PUT', `/api/bots/${encodeURIComponent(props.bot.larkAppId)}/brand-label`, { brandLabel: nextBrand });
1335
+ if (res.ok && res.body.ok) {
1336
+ const next = res.body.brandLabel ?? null;
1337
+ setBrand(next);
1338
+ setInput(next ?? '');
1339
+ props.patchBot(props.bot.larkAppId, { brandLabel: next });
1340
+ setStatus({ text: '✓', ok: true });
1341
+ }
1342
+ else {
1343
+ setStatus({ text: `✗ ${responseErrorText(res)}` });
1344
+ }
1345
+ }
1346
+ catch (e) {
1347
+ setStatus({ text: `✗ ${caughtErrorText(e)}` });
1348
+ }
1349
+ finally {
1350
+ setBusy(false);
1351
+ }
1352
+ }
1353
+ return (_jsxs("section", { className: "bd-section", children: [_jsx("h3", { className: "bd-section-title", children: tr('botDefaults.sectionBrand') }), _jsxs("div", { className: "bd-row bd-brand", children: [_jsxs("label", { children: [_jsx(FieldTitle, { help: tr('botDefaults.brandLabelHelp'), children: tr('botDefaults.brandLabel') }), _jsx("input", { type: "text", "data-input": "brandLabel", placeholder: tr('botDefaults.brandLabelPlaceholder'), value: input, disabled: busy, onChange: event => setInput(event.currentTarget.value) })] }), _jsx("small", { "data-brand-state": true, children: brandStateLabel(brand, tr) })] }), _jsxs("div", { className: "actions", children: [_jsx("button", { type: "button", className: "primary", "data-action": "save-brand", disabled: busy, onClick: () => void save(input), children: tr('botDefaults.brandSave') }), _jsx("button", { type: "button", "data-action": "reset-brand", disabled: busy, onClick: () => void save(null), children: tr('botDefaults.brandReset') }), _jsx(StatusSpan, { status: status, attr: { 'data-brand-status': '' } })] })] }));
1354
+ }
1355
+ function GrantSection(props) {
1356
+ const tr = useT();
1357
+ const [autoCard, setAutoCard] = useState(props.bot.autoGrantRequestCards !== false);
1358
+ const [restrict, setRestrict] = useState(props.bot.restrictGrantCommands === true);
1359
+ const [quota, setQuota] = useState(typeof props.bot.messageQuotaDefaultLimit === 'number' ? props.bot.messageQuotaDefaultLimit : null);
1360
+ const [quotaInput, setQuotaInput] = useState(typeof props.bot.messageQuotaDefaultLimit === 'number' ? String(props.bot.messageQuotaDefaultLimit) : '');
1361
+ const [status, setStatus] = useState(null);
1362
+ const [busy, setBusy] = useState(null);
1363
+ useEffect(() => {
1364
+ setAutoCard(props.bot.autoGrantRequestCards !== false);
1365
+ setRestrict(props.bot.restrictGrantCommands === true);
1366
+ const next = typeof props.bot.messageQuotaDefaultLimit === 'number' ? props.bot.messageQuotaDefaultLimit : null;
1367
+ setQuota(next);
1368
+ setQuotaInput(next == null ? '' : String(next));
1369
+ }, [props.bot.autoGrantRequestCards, props.bot.messageQuotaDefaultLimit, props.bot.restrictGrantCommands]);
1370
+ async function savePatch(patch, key) {
1371
+ setBusy(key);
1372
+ setStatus(null);
1373
+ try {
1374
+ const res = await sendJson('PUT', `/api/bots/${encodeURIComponent(props.bot.larkAppId)}/grant-prefs`, patch);
1375
+ if (res.ok && res.body.ok) {
1376
+ const nextQuota = typeof res.body.messageQuotaDefaultLimit === 'number' ? res.body.messageQuotaDefaultLimit : null;
1377
+ setAutoCard(res.body.autoGrantRequestCards !== false);
1378
+ setRestrict(res.body.restrictGrantCommands === true);
1379
+ setQuota(nextQuota);
1380
+ if ('messageQuotaDefaultLimit' in patch)
1381
+ setQuotaInput(nextQuota == null ? '' : String(nextQuota));
1382
+ props.patchBot(props.bot.larkAppId, {
1383
+ autoGrantRequestCards: res.body.autoGrantRequestCards !== false,
1384
+ restrictGrantCommands: res.body.restrictGrantCommands === true,
1385
+ messageQuotaDefaultLimit: nextQuota,
1386
+ });
1387
+ setStatus({ text: `✓ ${tr('botDefaults.cardPrefSaved')}`, ok: true });
1388
+ }
1389
+ else {
1390
+ setStatus({ text: `✗ ${responseErrorText(res)}` });
1391
+ }
1392
+ }
1393
+ catch (e) {
1394
+ setStatus({ text: `✗ ${caughtErrorText(e)}` });
1395
+ }
1396
+ finally {
1397
+ setBusy(null);
1398
+ }
1399
+ }
1400
+ function saveQuota() {
1401
+ const parsed = positiveIntegerOrNull(quotaInput);
1402
+ if (parsed === 'invalid') {
1403
+ setStatus({ text: `✗ ${tr('botDefaults.quotaInvalid')}` });
1404
+ return;
1405
+ }
1406
+ void savePatch({ messageQuotaDefaultLimit: parsed }, 'quota');
1407
+ }
1408
+ return (_jsxs("section", { className: "bd-section", children: [_jsx("h3", { className: "bd-section-title", children: tr('botDefaults.sectionGrant') }), _jsxs("div", { className: "bd-toggle-grid bd-grant-toggle-grid", children: [_jsx(ToggleRow, { checked: autoCard, disabled: busy === 'autoGrant', dataAction: "toggle-auto-grant-card", title: tr('botDefaults.autoGrantCard'), help: tr('botDefaults.autoGrantCardHelp'), onChange: checked => {
1409
+ setAutoCard(checked);
1410
+ void savePatch({ autoGrantRequestCards: checked }, 'autoGrant');
1411
+ } }), _jsx(ToggleRow, { checked: restrict, disabled: busy === 'restrict', dataAction: "toggle-restrict-grant", title: tr('botDefaults.restrictGrant'), help: tr('botDefaults.restrictGrantHelp'), onChange: checked => {
1412
+ setRestrict(checked);
1413
+ void savePatch({ restrictGrantCommands: checked }, 'restrict');
1414
+ } })] }), _jsxs("div", { className: "bd-row bd-quota", children: [_jsxs("label", { children: [_jsx(FieldTitle, { help: tr('botDefaults.quotaHelp'), children: tr('botDefaults.quotaDefault') }), _jsx("input", { type: "number", min: 1, step: 1, "data-input": "quotaLimit", placeholder: tr('botDefaults.quotaPlaceholder'), value: quotaInput, disabled: busy === 'quota', onChange: event => setQuotaInput(event.currentTarget.value) })] }), _jsx("small", { "data-quota-state": true, children: quotaStateLabel(quota, tr) })] }), _jsxs("div", { className: "actions", children: [_jsx("button", { type: "button", className: "primary", "data-action": "save-quota", disabled: busy === 'quota', onClick: saveQuota, children: tr('botDefaults.quotaSave') }), _jsx(StatusSpan, { status: status, attr: { 'data-grant-status': '' } })] })] }));
19
1415
  }
20
1416
  export function renderBotDefaultsPage(root) {
21
1417
  return mountReactPage(root, _jsx(BotDefaultsPage, {}));