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,146 +1,89 @@
1
- // Roles page: group role editor + reusable role profile management.
2
- import { botAvatarHtml, escapeHtml, loadNameMaps, loadingHtml, t } from './ui.js';
3
- import { hasExplicitChatRole, summarizeGroupProfileMatches, } from './role-profile-match.js';
1
+ import { effectiveRoleKey, loadEffectiveRoleMap } from './role-batch.js';
4
2
  // Keep in sync with MAX_ROLE_BYTES in core/role-resolver.ts (this is a browser
5
3
  // bundle, so it can't import the Node module — mirror the value here).
6
- const MAX_ROLE_BYTES = 32768;
7
- const ROLE_WARN_BYTES = Math.floor(MAX_ROLE_BYTES * 0.95);
4
+ export const MAX_ROLE_BYTES = 32768;
5
+ export const ROLE_WARN_BYTES = Math.floor(MAX_ROLE_BYTES * 0.95);
8
6
  const PROFILE_ID_RE = /^[A-Za-z0-9._-]{1,64}$/;
9
- let cache = [];
10
- let allBots = [];
11
- let profiles = [];
12
- let profileEntries = [];
13
- let groupProfileEntriesById = new Map();
14
- let groupEffectiveRolesByBot = new Map();
15
- let groupProfileContextLoaded = false;
16
- let activeTab = 'groups';
17
- let selectedGroupId = null;
18
- let selectedBotId = null;
19
- let editingContent = '';
20
- let editingInjectMode = 'every';
21
- let expandedGroups = new Set();
22
- let selectedProfileId = null;
23
- let selectedProfileBotId = null;
24
- let profileEditingContent = '';
25
- let selectedApplyGroupId = null;
26
- let activeRolesGeneration = 0;
27
- let activeRolesTimerState = null;
28
- function isRolesGeneration(generation) {
29
- return activeRolesGeneration === generation;
30
- }
31
- function scheduleRolesTimer(fn, ms) {
32
- const state = activeRolesTimerState;
33
- const generation = state?.generation ?? activeRolesGeneration;
34
- const id = window.setTimeout(() => {
35
- state?.timers.delete(id);
36
- if (isRolesGeneration(generation))
37
- fn();
38
- }, ms);
39
- state?.timers.add(id);
40
- return id;
41
- }
42
- function isValidProfileId(profileId) {
7
+ export function isValidProfileId(profileId) {
43
8
  return PROFILE_ID_RE.test(profileId) && profileId !== '.' && profileId !== '..';
44
9
  }
45
- function hashChatId() {
46
- const [, query = ''] = location.hash.split('?');
10
+ export function hashChatId(hash = location.hash) {
11
+ const [, query = ''] = hash.split('?');
47
12
  const chatId = new URLSearchParams(query).get('chatId')?.trim();
48
13
  return chatId || null;
49
14
  }
50
- async function loadGroups() {
15
+ export function roleKey(larkAppId, chatId) {
16
+ return effectiveRoleKey(larkAppId, chatId);
17
+ }
18
+ export function byteLength(s) {
19
+ return new TextEncoder().encode(s).length;
20
+ }
21
+ export function botRoleCount(group) {
22
+ return group.memberBots.filter(bot => bot.inChat && bot.hasRole).length;
23
+ }
24
+ export function botInChatCount(group) {
25
+ return group.memberBots.filter(bot => bot.inChat).length;
26
+ }
27
+ export function profileHasEntry(profile, larkAppId) {
28
+ return (profile.botEntries ?? []).some(entry => entry.larkAppId === larkAppId && entry.hasEntry);
29
+ }
30
+ export function entryForBot(entries, larkAppId) {
31
+ return larkAppId ? entries.find(entry => entry.larkAppId === larkAppId) : undefined;
32
+ }
33
+ export function filterRoleGroups(groups, filter) {
34
+ const q = filter.toLowerCase();
35
+ if (!q)
36
+ return groups;
37
+ return groups.filter(group => {
38
+ const matchGroup = group.chatId.toLowerCase().includes(q) || (group.name ?? '').toLowerCase().includes(q);
39
+ const matchBot = group.memberBots.some(bot => bot.larkAppId.toLowerCase().includes(q) || (bot.botName ?? '').toLowerCase().includes(q));
40
+ return matchGroup || matchBot;
41
+ });
42
+ }
43
+ export function filterRoleProfiles(profiles, filter) {
44
+ const q = filter.toLowerCase();
45
+ return profiles.filter(profile => !q || profile.profileId.toLowerCase().includes(q));
46
+ }
47
+ async function readJson(r) {
48
+ return r.json().catch(() => ({}));
49
+ }
50
+ export async function loadGroups() {
51
51
  const r = await fetch('/api/groups');
52
- const data = await r.json();
53
- allBots = (data.bots ?? []).map((b) => ({
54
- larkAppId: b.larkAppId,
55
- botName: b.botName ?? b.larkAppId,
56
- botAvatarUrl: b.botAvatarUrl,
57
- }));
58
- cache = (data.chats ?? []).map((c) => ({
59
- chatId: c.chatId,
60
- name: c.name ?? c.chatId,
61
- memberBots: (c.memberBots ?? []).map((m) => ({
62
- larkAppId: m.larkAppId,
63
- botName: m.botName ?? m.larkAppId,
64
- inChat: m.inChat ?? false,
65
- hasRole: m.hasRole ?? false,
66
- oncallChat: m.oncallChat ?? null,
52
+ const data = await readJson(r);
53
+ return {
54
+ bots: (data.bots ?? []).map((bot) => ({
55
+ larkAppId: bot.larkAppId,
56
+ botName: bot.botName ?? bot.larkAppId,
57
+ botAvatarUrl: bot.botAvatarUrl,
67
58
  })),
68
- }));
59
+ groups: (data.chats ?? []).map((chat) => ({
60
+ chatId: chat.chatId,
61
+ name: chat.name ?? chat.chatId,
62
+ memberBots: (chat.memberBots ?? []).map((member) => ({
63
+ larkAppId: member.larkAppId,
64
+ botName: member.botName ?? member.larkAppId,
65
+ inChat: member.inChat ?? false,
66
+ hasRole: member.hasRole ?? false,
67
+ oncallChat: member.oncallChat ?? null,
68
+ })),
69
+ })),
70
+ };
69
71
  }
70
- async function loadProfiles() {
72
+ export async function loadProfiles() {
71
73
  const r = await fetch('/api/role-profiles');
72
- const data = await r.json();
73
- profiles = data.profiles ?? [];
74
+ const data = await readJson(r);
75
+ return data.profiles ?? [];
74
76
  }
75
- async function loadProfileEntries(profileId) {
77
+ export async function loadProfileEntries(profileId) {
76
78
  const r = await fetch(`/api/role-profiles/${encodeURIComponent(profileId)}`);
77
- const data = await r.json();
78
- profileEntries = data.entries ?? [];
79
+ const data = await readJson(r);
80
+ return data.entries ?? [];
79
81
  }
80
- async function loadRole(larkAppId, chatId) {
82
+ export async function loadRole(larkAppId, chatId) {
81
83
  const r = await fetch(`/api/roles/${encodeURIComponent(larkAppId)}/${encodeURIComponent(chatId)}`);
82
- return r.json();
83
- }
84
- function roleKey(larkAppId, chatId) {
85
- return `${larkAppId}\u0000${chatId}`;
84
+ return readJson(r);
86
85
  }
87
- async function loadGroupProfileContext(generation = activeRolesGeneration) {
88
- const detailPairs = await Promise.all(profiles.map(async (profile) => {
89
- try {
90
- const r = await fetch(`/api/role-profiles/${encodeURIComponent(profile.profileId)}`);
91
- const body = await r.json().catch(() => ({}));
92
- return [profile.profileId, Array.isArray(body.entries) ? body.entries : []];
93
- }
94
- catch {
95
- return [profile.profileId, []];
96
- }
97
- }));
98
- if (!isRolesGeneration(generation))
99
- return false;
100
- const nextEffectiveRoles = new Map();
101
- const seen = new Set();
102
- await Promise.all(cache.flatMap(group => group.memberBots
103
- .filter(bot => bot.inChat)
104
- .map(async (bot) => {
105
- const key = roleKey(bot.larkAppId, group.chatId);
106
- if (seen.has(key))
107
- return;
108
- seen.add(key);
109
- try {
110
- const role = await loadRole(bot.larkAppId, group.chatId);
111
- const hasEffectiveRole = role.hasEffectiveRole ?? role.hasRole;
112
- const effectiveContent = 'effectiveContent' in role ? role.effectiveContent : role.content;
113
- nextEffectiveRoles.set(key, {
114
- content: hasEffectiveRole ? String(effectiveContent ?? '') : null,
115
- source: role.effectiveSource ?? (role.hasRole ? 'chat' : 'none'),
116
- });
117
- }
118
- catch {
119
- nextEffectiveRoles.set(key, null);
120
- }
121
- })));
122
- if (!isRolesGeneration(generation))
123
- return false;
124
- groupProfileEntriesById = new Map(detailPairs);
125
- groupEffectiveRolesByBot = nextEffectiveRoles;
126
- return true;
127
- }
128
- async function refreshGroupProfileContext(generation = activeRolesGeneration) {
129
- try {
130
- const ok = await loadGroupProfileContext(generation);
131
- if (!ok || !isRolesGeneration(generation))
132
- return;
133
- }
134
- catch {
135
- if (!isRolesGeneration(generation))
136
- return;
137
- groupProfileEntriesById = new Map();
138
- groupEffectiveRolesByBot = new Map();
139
- }
140
- groupProfileContextLoaded = true;
141
- renderTree(document.getElementById('roles-search')?.value ?? '');
142
- }
143
- async function saveRole(larkAppId, chatId, content, injectMode) {
86
+ export async function saveRole(larkAppId, chatId, content, injectMode) {
144
87
  const r = await fetch(`/api/roles/${encodeURIComponent(larkAppId)}/${encodeURIComponent(chatId)}`, {
145
88
  method: 'PUT',
146
89
  headers: { 'content-type': 'application/json' },
@@ -150,7 +93,7 @@ async function saveRole(larkAppId, chatId, content, injectMode) {
150
93
  }
151
94
  /** Persist only the injection mode (no content) — used when toggling the mode
152
95
  * select, which can apply even to a chat whose role comes from the team default. */
153
- async function saveInjectMode(larkAppId, chatId, injectMode) {
96
+ export async function saveInjectMode(larkAppId, chatId, injectMode) {
154
97
  const r = await fetch(`/api/roles/${encodeURIComponent(larkAppId)}/${encodeURIComponent(chatId)}`, {
155
98
  method: 'PUT',
156
99
  headers: { 'content-type': 'application/json' },
@@ -158,15 +101,15 @@ async function saveInjectMode(larkAppId, chatId, injectMode) {
158
101
  });
159
102
  return r.ok;
160
103
  }
161
- async function deleteRole(larkAppId, chatId) {
104
+ export async function deleteRole(larkAppId, chatId) {
162
105
  const r = await fetch(`/api/roles/${encodeURIComponent(larkAppId)}/${encodeURIComponent(chatId)}`, { method: 'DELETE' });
163
106
  return r.ok;
164
107
  }
165
- async function loadProfileEntry(profileId, larkAppId) {
108
+ export async function loadProfileEntry(profileId, larkAppId) {
166
109
  const r = await fetch(`/api/role-profiles/${encodeURIComponent(profileId)}/${encodeURIComponent(larkAppId)}`);
167
- return r.json();
110
+ return readJson(r);
168
111
  }
169
- async function saveProfileEntry(profileId, larkAppId, content) {
112
+ export async function saveProfileEntry(profileId, larkAppId, content) {
170
113
  const r = await fetch(`/api/role-profiles/${encodeURIComponent(profileId)}/${encodeURIComponent(larkAppId)}`, {
171
114
  method: 'PUT',
172
115
  headers: { 'content-type': 'application/json' },
@@ -174,787 +117,58 @@ async function saveProfileEntry(profileId, larkAppId, content) {
174
117
  });
175
118
  return r.ok;
176
119
  }
177
- async function deleteProfileEntry(profileId, larkAppId) {
120
+ export async function deleteProfileEntry(profileId, larkAppId) {
178
121
  const r = await fetch(`/api/role-profiles/${encodeURIComponent(profileId)}/${encodeURIComponent(larkAppId)}`, { method: 'DELETE' });
179
122
  return r.ok;
180
123
  }
181
- function byteLength(s) {
182
- return new TextEncoder().encode(s).length;
183
- }
184
- function botRoleCount(group) {
185
- return group.memberBots.filter(b => b.inChat && b.hasRole).length;
186
- }
187
- function botInChatCount(group) {
188
- return group.memberBots.filter(b => b.inChat).length;
189
- }
190
- function profileHasEntry(profile, larkAppId) {
191
- return (profile.botEntries ?? []).some(entry => entry.larkAppId === larkAppId && entry.hasEntry);
192
- }
193
- function entryForBot(larkAppId) {
194
- return profileEntries.find(entry => entry.larkAppId === larkAppId);
195
- }
196
- function switchTab(tab) {
197
- activeTab = tab;
198
- document.getElementById('roles-by-group-view')?.toggleAttribute('hidden', tab !== 'groups');
199
- document.getElementById('roles-profiles-view')?.toggleAttribute('hidden', tab !== 'profiles');
200
- }
201
- function renderRolesGroupProfileStatus(group) {
202
- if (!profiles.length || !groupProfileContextLoaded)
203
- return '';
204
- const rolesByBot = new Map();
205
- for (const bot of group.memberBots) {
206
- if (!bot.inChat)
207
- continue;
208
- rolesByBot.set(bot.larkAppId, groupEffectiveRolesByBot.get(roleKey(bot.larkAppId, group.chatId)) ?? null);
209
- }
210
- if (!hasExplicitChatRole(rolesByBot))
211
- return '';
212
- const best = summarizeGroupProfileMatches(group.memberBots, profiles, groupProfileEntriesById, rolesByBot)[0];
213
- if (!best)
214
- return `<div class="roles-profile-match muted">${t('groups.profileStatusUnmatched')}</div>`;
215
- const key = best.kind === 'full' ? 'groups.profileStatusFullChat' : 'groups.profileStatusPartial';
216
- return `<div class="roles-profile-match ${best.kind}">
217
- ${escapeHtml(t(key, {
218
- name: best.profileId,
219
- matched: best.matched,
220
- total: best.total,
221
- chat: best.chatMatched,
222
- }))}
223
- </div>`;
224
- }
225
- function renderTree(filter = '') {
226
- const tree = document.getElementById('roles-tree');
227
- if (!tree)
228
- return;
229
- const q = filter.toLowerCase();
230
- const filtered = cache.filter(g => {
231
- if (!q)
232
- return true;
233
- const matchGroup = g.chatId.toLowerCase().includes(q) || (g.name ?? '').toLowerCase().includes(q);
234
- const matchBot = g.memberBots.some(b => b.larkAppId.toLowerCase().includes(q) || (b.botName ?? '').toLowerCase().includes(q));
235
- return matchGroup || matchBot;
236
- });
237
- if (filtered.length === 0) {
238
- tree.innerHTML = `<div class="roles-empty">${t('roles.noChats')}</div>`;
239
- return;
240
- }
241
- tree.innerHTML = filtered.map(g => {
242
- const expanded = expandedGroups.has(g.chatId);
243
- const inChatBots = g.memberBots.filter(b => b.inChat);
244
- const arrow = expanded ? '▾' : '▸';
245
- const roleCount = botRoleCount(g);
246
- const totalInChat = botInChatCount(g);
247
- const botRows = expanded
248
- ? inChatBots.map(b => {
249
- const isSelected = selectedGroupId === g.chatId && selectedBotId === b.larkAppId;
250
- return `
251
- <div class="roles-bot-row ${isSelected ? 'selected' : ''}"
252
- data-group-id="${escapeHtml(g.chatId)}"
253
- data-bot-id="${escapeHtml(b.larkAppId)}">
254
- <span class="roles-bot-indent"></span>
255
- ${botAvatarHtml({ name: b.botName, larkAppId: b.larkAppId, size: 'sm' })}
256
- <div class="roles-bot-info">
257
- <div class="roles-bot-name">${escapeHtml(b.botName)}</div>
258
- <div class="roles-bot-id">${escapeHtml(b.larkAppId)}</div>
259
- </div>
260
- <span class="roles-badge ${b.hasRole ? 'has-role' : 'no-role'}">
261
- ${b.hasRole ? t('roles.configured') : t('roles.unconfigured')}
262
- </span>
263
- </div>`;
264
- }).join('')
265
- : '';
266
- return `
267
- <div class="roles-group-section">
268
- <div class="roles-group-row ${expanded ? 'expanded' : ''} ${selectedGroupId === g.chatId && !selectedBotId ? 'selected' : ''}"
269
- data-group-id="${escapeHtml(g.chatId)}">
270
- <span class="roles-group-arrow">${arrow}</span>
271
- <span class="roles-group-icon" aria-hidden="true"><svg viewBox="0 0 16 16"><circle cx="5.6" cy="5.8" r="2.4"/><path d="M1.8 13.2c.5-2.4 2-3.6 3.8-3.6s3.3 1.2 3.8 3.6"/><circle cx="11" cy="6.8" r="1.9"/><path d="M9.8 12.6c.4-1.7 1.5-2.6 2.8-2.6 1 0 1.9.5 2.4 1.6"/></svg></span>
272
- <div class="roles-group-info">
273
- <div class="roles-group-name">${escapeHtml(g.name ?? g.chatId)}</div>
274
- <div class="roles-group-meta">
275
- ${roleCount}/${totalInChat} ${t('roles.botsWithRoles')}
276
- </div>
277
- ${renderRolesGroupProfileStatus(g)}
278
- </div>
279
- <span class="roles-group-chevron"></span>
280
- </div>
281
- <div class="roles-bot-list">${botRows}</div>
282
- </div>`;
283
- }).join('');
284
- tree.querySelectorAll('.roles-group-row').forEach(row => {
285
- row.addEventListener('click', () => {
286
- const gid = row.dataset.groupId;
287
- if (!gid)
288
- return;
289
- if (expandedGroups.has(gid))
290
- expandedGroups.delete(gid);
291
- else
292
- expandedGroups.add(gid);
293
- renderTree(document.getElementById('roles-search')?.value ?? '');
294
- });
295
- });
296
- tree.querySelectorAll('.roles-bot-row').forEach(row => {
297
- row.addEventListener('click', (e) => {
298
- e.stopPropagation();
299
- const gid = row.dataset.groupId;
300
- const bid = row.dataset.botId;
301
- if (gid && bid)
302
- selectBot(gid, bid);
303
- });
304
- });
305
- }
306
- async function selectBot(groupId, botId, generation = activeRolesGeneration) {
307
- selectedGroupId = groupId;
308
- selectedBotId = botId;
309
- const role = await loadRole(botId, groupId);
310
- if (!isRolesGeneration(generation))
311
- return;
312
- const empty = document.getElementById('roles-editor-empty');
313
- const form = document.getElementById('roles-editor-form');
314
- const textarea = document.getElementById('roles-editor-textarea');
315
- const groupName = document.getElementById('roles-editor-group-name');
316
- const botName = document.getElementById('roles-editor-bot-name');
317
- const chatIdEl = document.getElementById('roles-editor-chat-id');
318
- if (empty)
319
- empty.style.display = 'none';
320
- if (form)
321
- form.style.display = '';
322
- const group = cache.find(g => g.chatId === groupId);
323
- const bot = group?.memberBots.find(b => b.larkAppId === botId);
324
- if (groupName)
325
- groupName.textContent = group?.name ?? groupId;
326
- if (botName)
327
- botName.textContent = bot?.botName ?? botId;
328
- if (chatIdEl)
329
- chatIdEl.textContent = `${groupId} · ${botId}`;
330
- editingContent = role.content ?? '';
331
- editingInjectMode = role.injectMode === 'once' ? 'once' : 'every';
332
- if (textarea) {
333
- textarea.value = editingContent;
334
- textarea.focus();
335
- }
336
- const injectSel = document.getElementById('roles-editor-inject-mode');
337
- if (injectSel)
338
- injectSel.value = editingInjectMode;
339
- updateByteCount();
340
- updatePreview();
341
- renderTree(document.getElementById('roles-search')?.value ?? '');
342
- const delBtn = document.getElementById('roles-delete');
343
- if (delBtn)
344
- delBtn.style.display = role.hasRole ? '' : 'none';
345
- }
346
- function updateByteCount() {
347
- const el = document.getElementById('roles-editor-bytecount');
348
- if (!el)
349
- return;
350
- const len = byteLength(editingContent);
351
- el.textContent = `${len} / ${MAX_ROLE_BYTES} bytes`;
352
- el.className = `roles-bytecount ${len > ROLE_WARN_BYTES ? 'warn' : ''} ${len > MAX_ROLE_BYTES ? 'over' : ''}`;
353
- updateSaveButton(len);
354
- }
355
- function updateSaveButton(byteLen) {
356
- const btn = document.getElementById('roles-save');
357
- if (!btn)
358
- return;
359
- const len = byteLen ?? byteLength(editingContent);
360
- btn.disabled = len > MAX_ROLE_BYTES || editingContent.trim().length === 0;
361
- }
362
- function updatePreview() {
363
- const preview = document.getElementById('roles-preview');
364
- if (!preview)
365
- return;
366
- if (!editingContent.trim()) {
367
- preview.innerHTML = `<small>${t('roles.previewEmpty')}</small>`;
368
- }
369
- else {
370
- preview.innerHTML = `<strong>${t('roles.preview')}</strong><pre>${escapeHtml(editingContent)}</pre>`;
371
- }
372
- }
373
- function resetEditor() {
374
- selectedGroupId = null;
375
- selectedBotId = null;
376
- editingContent = '';
377
- editingInjectMode = 'every';
378
- const empty = document.getElementById('roles-editor-empty');
379
- const form = document.getElementById('roles-editor-form');
380
- const textarea = document.getElementById('roles-editor-textarea');
381
- const delBtn = document.getElementById('roles-delete');
382
- if (empty)
383
- empty.style.display = '';
384
- if (form)
385
- form.style.display = 'none';
386
- if (textarea)
387
- textarea.value = '';
388
- if (delBtn)
389
- delBtn.style.display = 'none';
390
- }
391
- function renderProfileList(filter = '') {
392
- const list = document.getElementById('roles-profile-list');
393
- if (!list)
394
- return;
395
- const q = filter.toLowerCase();
396
- const filtered = profiles.filter(p => !q || p.profileId.toLowerCase().includes(q));
397
- if (filtered.length === 0) {
398
- list.innerHTML = `<div class="roles-empty">${t('roles.profileEmpty')}</div>`;
399
- return;
400
- }
401
- list.innerHTML = filtered.map(p => {
402
- const selected = selectedProfileId === p.profileId;
403
- const hasAnyLocal = (p.botEntries ?? []).some(entry => entry.hasEntry);
404
- return `
405
- <div class="roles-profile-row ${selected ? 'selected' : ''}" data-profile-id="${escapeHtml(p.profileId)}">
406
- <div class="roles-profile-row-main">
407
- <div class="roles-profile-name">${escapeHtml(p.profileId)}</div>
408
- <div class="roles-group-meta">${p.entryCount} ${t('roles.profileEntries')}</div>
409
- </div>
410
- <span class="roles-badge ${hasAnyLocal ? 'has-role' : 'no-role'}">${hasAnyLocal ? t('roles.configured') : t('roles.profileMissing')}</span>
411
- </div>`;
412
- }).join('');
413
- list.querySelectorAll('.roles-profile-row').forEach(row => {
414
- row.addEventListener('click', () => {
415
- const profileId = row.dataset.profileId;
416
- if (profileId)
417
- void selectProfile(profileId);
418
- });
419
- });
420
- }
421
- async function selectProfile(profileId, generation = activeRolesGeneration) {
422
- if (!isValidProfileId(profileId.trim()))
423
- return;
424
- selectedProfileId = profileId.trim();
425
- selectedProfileBotId = null;
426
- profileEditingContent = '';
427
- selectedApplyGroupId = selectedApplyGroupId ?? cache[0]?.chatId ?? null;
428
- await loadProfileEntries(selectedProfileId);
429
- if (!isRolesGeneration(generation))
430
- return;
431
- renderProfileList(document.getElementById('roles-profile-search')?.value ?? '');
432
- renderProfileDetail();
433
- }
434
- function renderProfileDetail() {
435
- const empty = document.getElementById('roles-profile-empty');
436
- const detail = document.getElementById('roles-profile-detail');
437
- if (!empty || !detail)
438
- return;
439
- if (!selectedProfileId) {
440
- empty.style.display = '';
441
- detail.style.display = 'none';
442
- detail.innerHTML = '';
443
- return;
444
- }
445
- empty.style.display = 'none';
446
- detail.style.display = '';
447
- const selectedBot = allBots.find(b => b.larkAppId === selectedProfileBotId);
448
- const entry = selectedProfileBotId ? entryForBot(selectedProfileBotId) : undefined;
449
- detail.innerHTML = `
450
- <div class="roles-profile-title">
451
- <div>
452
- <div class="roles-editor-breadcrumb">
453
- <span>${escapeHtml(selectedProfileId)}</span>
454
- ${selectedBot ? `<span class="roles-breadcrumb-sep">›</span><span>${escapeHtml(selectedBot.botName ?? selectedBot.larkAppId)}</span>` : ''}
455
- </div>
456
- <div class="roles-editor-meta-line">${t('roles.profileRuntimeHint')}</div>
457
- </div>
458
- </div>
459
- <div class="roles-profile-grid">
460
- <div class="roles-profile-bots">
461
- <div class="roles-profile-section-title">${t('roles.profileBots')}</div>
462
- <div class="roles-profile-bot-list">
463
- ${allBots.map(bot => {
464
- const hasEntry = !!entryForBot(bot.larkAppId);
465
- const selected = selectedProfileBotId === bot.larkAppId;
466
- return `
467
- <div class="roles-bot-row roles-profile-bot-row ${selected ? 'selected' : ''}" data-profile-bot-id="${escapeHtml(bot.larkAppId)}">
468
- ${botAvatarHtml({ name: bot.botName, larkAppId: bot.larkAppId, size: 'sm' })}
469
- <div class="roles-bot-info">
470
- <div class="roles-bot-name">${escapeHtml(bot.botName ?? bot.larkAppId)}</div>
471
- <div class="roles-bot-id">${escapeHtml(bot.larkAppId)}</div>
472
- </div>
473
- <span class="roles-badge ${hasEntry ? 'has-role' : 'no-role'}">${hasEntry ? t('roles.configured') : t('roles.unconfigured')}</span>
474
- </div>`;
475
- }).join('')}
476
- </div>
477
- </div>
478
- <div class="roles-profile-editor">
479
- ${selectedProfileBotId ? `
480
- <textarea id="roles-profile-textarea" placeholder="${t('roles.profileEditorPlaceholder')}" rows="12">${escapeHtml(profileEditingContent || entry?.content || '')}</textarea>
481
- <div class="roles-editor-footer">
482
- <span id="roles-profile-bytecount" class="roles-bytecount"></span>
483
- <div class="roles-editor-actions">
484
- <button type="button" id="roles-profile-delete" class="danger" ${entry ? '' : 'style="display:none"'}>${t('roles.delete')}</button>
485
- <button type="button" id="roles-profile-save" class="primary">${t('roles.saveEntry')}</button>
486
- </div>
487
- </div>
488
- <div id="roles-profile-preview" class="roles-preview"></div>
489
- ` : `<div class="roles-editor-empty roles-profile-inline-empty">${t('roles.profileBotSelectHint')}</div>`}
490
- </div>
491
- </div>
492
- <div class="roles-profile-apply">
493
- <div class="roles-profile-section-title">${t('roles.applyToGroup')}</div>
494
- <div class="roles-profile-apply-controls">
495
- <select id="roles-profile-apply-group">
496
- ${cache.map(g => `<option value="${escapeHtml(g.chatId)}" ${selectedApplyGroupId === g.chatId ? 'selected' : ''}>${escapeHtml(g.name ?? g.chatId)}</option>`).join('')}
497
- </select>
498
- <label class="roles-profile-force"><input type="checkbox" id="roles-profile-apply-force"> ${t('roles.applyForce')}</label>
499
- </div>
500
- <div id="roles-profile-apply-bots"></div>
501
- <div class="roles-editor-actions">
502
- <button type="button" id="roles-profile-preview-apply">${t('roles.previewApply')}</button>
503
- <button type="button" id="roles-profile-apply" class="primary">${t('roles.applyProfile')}</button>
504
- </div>
505
- <div id="roles-profile-apply-status" class="roles-profile-status"></div>
506
- </div>
507
- `;
508
- detail.querySelectorAll('.roles-profile-bot-row').forEach(row => {
509
- row.addEventListener('click', () => {
510
- const botId = row.dataset.profileBotId;
511
- if (botId)
512
- void selectProfileBot(botId);
513
- });
514
- });
515
- renderProfileApplyBots();
516
- bindProfileEditor();
517
- }
518
- async function selectProfileBot(botId, generation = activeRolesGeneration) {
519
- if (!selectedProfileId)
520
- return;
521
- selectedProfileBotId = botId;
522
- const entry = await loadProfileEntry(selectedProfileId, botId);
523
- if (!isRolesGeneration(generation))
524
- return;
525
- profileEditingContent = entry.content ?? '';
526
- await loadProfileEntries(selectedProfileId);
527
- if (!isRolesGeneration(generation))
528
- return;
529
- renderProfileDetail();
530
- }
531
- function bindProfileEditor() {
532
- const generation = activeRolesGeneration;
533
- const textarea = document.getElementById('roles-profile-textarea');
534
- if (textarea) {
535
- profileEditingContent = textarea.value;
536
- updateProfileByteCount();
537
- updateProfilePreview();
538
- textarea.addEventListener('input', (e) => {
539
- profileEditingContent = e.target.value;
540
- updateProfileByteCount();
541
- updateProfilePreview();
542
- });
543
- }
544
- document.getElementById('roles-profile-save')?.addEventListener('click', async function () {
545
- if (!selectedProfileId || !selectedProfileBotId)
546
- return;
547
- this.disabled = true;
548
- this.textContent = '...';
549
- try {
550
- const ok = await saveProfileEntry(selectedProfileId, selectedProfileBotId, profileEditingContent);
551
- if (!isRolesGeneration(generation))
552
- return;
553
- await loadProfiles();
554
- if (!isRolesGeneration(generation))
555
- return;
556
- await loadProfileEntries(selectedProfileId);
557
- if (!isRolesGeneration(generation))
558
- return;
559
- renderProfileList(document.getElementById('roles-profile-search')?.value ?? '');
560
- void refreshGroupProfileContext(generation);
561
- renderProfileDetail();
562
- flashProfileStatus(ok ? t('roles.saved') : t('roles.saveFailed'), !ok);
563
- }
564
- finally {
565
- if (!isRolesGeneration(generation))
566
- return;
567
- this.disabled = false;
568
- this.textContent = t('roles.saveEntry');
569
- }
570
- });
571
- document.getElementById('roles-profile-delete')?.addEventListener('click', async function () {
572
- if (!selectedProfileId || !selectedProfileBotId)
573
- return;
574
- if (!confirm(t('roles.confirmDeleteProfileEntry')))
575
- return;
576
- this.disabled = true;
124
+ export async function loadRoleProfileContext(groups, profiles) {
125
+ const detailPairs = await Promise.all(profiles.map(async (profile) => {
577
126
  try {
578
- await deleteProfileEntry(selectedProfileId, selectedProfileBotId);
579
- if (!isRolesGeneration(generation))
580
- return;
581
- profileEditingContent = '';
582
- await loadProfiles();
583
- if (!isRolesGeneration(generation))
584
- return;
585
- await loadProfileEntries(selectedProfileId);
586
- if (!isRolesGeneration(generation))
587
- return;
588
- renderProfileList(document.getElementById('roles-profile-search')?.value ?? '');
589
- void refreshGroupProfileContext(generation);
590
- renderProfileDetail();
127
+ const r = await fetch(`/api/role-profiles/${encodeURIComponent(profile.profileId)}`);
128
+ const body = await readJson(r);
129
+ return [profile.profileId, Array.isArray(body.entries) ? body.entries : []];
591
130
  }
592
- finally {
593
- if (!isRolesGeneration(generation))
594
- return;
595
- this.disabled = false;
131
+ catch {
132
+ return [profile.profileId, []];
596
133
  }
597
- });
598
- document.getElementById('roles-profile-apply-group')?.addEventListener('change', (e) => {
599
- selectedApplyGroupId = e.target.value;
600
- renderProfileApplyBots();
601
- });
602
- document.getElementById('roles-profile-preview-apply')?.addEventListener('click', () => runProfileApply(true, generation));
603
- document.getElementById('roles-profile-apply')?.addEventListener('click', () => runProfileApply(false, generation));
604
- }
605
- function updateProfileByteCount() {
606
- const el = document.getElementById('roles-profile-bytecount');
607
- const btn = document.getElementById('roles-profile-save');
608
- if (!el)
609
- return;
610
- const len = byteLength(profileEditingContent);
611
- el.textContent = `${len} / ${MAX_ROLE_BYTES} bytes`;
612
- el.className = `roles-bytecount ${len > ROLE_WARN_BYTES ? 'warn' : ''} ${len > MAX_ROLE_BYTES ? 'over' : ''}`;
613
- if (btn)
614
- btn.disabled = len > MAX_ROLE_BYTES || profileEditingContent.trim().length === 0;
615
- }
616
- function updateProfilePreview() {
617
- const preview = document.getElementById('roles-profile-preview');
618
- if (!preview)
619
- return;
620
- preview.innerHTML = profileEditingContent.trim()
621
- ? `<strong>${t('roles.preview')}</strong><pre>${escapeHtml(profileEditingContent)}</pre>`
622
- : `<small>${t('roles.previewEmpty')}</small>`;
623
- }
624
- function renderProfileApplyBots() {
625
- const wrap = document.getElementById('roles-profile-apply-bots');
626
- if (!wrap)
627
- return;
628
- const groupId = selectedApplyGroupId ?? cache[0]?.chatId ?? '';
629
- const group = cache.find(g => g.chatId === groupId);
630
- const bots = group?.memberBots.filter(b => b.inChat) ?? [];
631
- if (!group || bots.length === 0) {
632
- wrap.innerHTML = `<div class="roles-empty">${t('roles.noChats')}</div>`;
633
- return;
634
- }
635
- wrap.innerHTML = bots.map(bot => {
636
- const hasEntry = !!entryForBot(bot.larkAppId);
637
- return `
638
- <label class="checkbox-row roles-profile-apply-bot">
639
- <input type="checkbox" name="profile-apply-bot" value="${escapeHtml(bot.larkAppId)}" ${hasEntry ? 'checked' : ''}>
640
- <span>${escapeHtml(bot.botName ?? bot.larkAppId)}</span>
641
- <small>${hasEntry ? t('roles.configured') : t('roles.profileMissing')}</small>
642
- </label>`;
643
- }).join('');
644
- }
645
- async function runProfileApply(preview, generation = activeRolesGeneration) {
646
- const profileId = selectedProfileId;
647
- if (!profileId)
648
- return;
649
- const groupId = selectedApplyGroupId ?? cache[0]?.chatId;
650
- if (!groupId)
651
- return;
652
- const force = document.getElementById('roles-profile-apply-force')?.checked === true;
653
- const selected = [...document.querySelectorAll('input[name=profile-apply-bot]:checked')].map(i => i.value);
654
- const status = document.getElementById('roles-profile-apply-status');
655
- if (selected.length === 0) {
656
- if (status)
657
- status.textContent = t('roles.applyPickBots');
658
- return;
659
- }
660
- if (status)
661
- status.textContent = '...';
662
- const results = await Promise.all(selected.map(async (larkAppId) => {
663
- const r = await fetch(`/api/role-profiles/${encodeURIComponent(profileId)}/apply`, {
664
- method: 'POST',
665
- headers: { 'content-type': 'application/json' },
666
- body: JSON.stringify({ chatId: groupId, larkAppId, force, preview }),
667
- });
668
- const body = await r.json().catch(() => ({}));
669
- return { larkAppId, ok: r.ok && body.ok !== false, status: r.status, error: body.error, wouldRefuse: body.wouldRefuse };
670
134
  }));
671
- if (!isRolesGeneration(generation))
672
- return;
673
- if (status) {
674
- status.innerHTML = results.map(r => {
675
- const bot = allBots.find(b => b.larkAppId === r.larkAppId);
676
- const label = escapeHtml(bot?.botName ?? r.larkAppId);
677
- const outcome = r.ok
678
- ? (preview ? (r.wouldRefuse ? t('roles.applyWouldRefuse') : t('roles.applyPreviewOk')) : t('roles.applyOk'))
679
- : `${t('roles.applyFailed')}: ${escapeHtml(r.error ?? `HTTP ${r.status}`)}`;
680
- return `<div>${label}: ${outcome}</div>`;
681
- }).join('');
682
- }
683
- if (!preview) {
684
- await loadGroups();
685
- if (!isRolesGeneration(generation))
686
- return;
687
- renderTree(document.getElementById('roles-search')?.value ?? '');
688
- void refreshGroupProfileContext(generation);
135
+ const seen = new Set();
136
+ const roleTargets = [];
137
+ for (const group of groups) {
138
+ for (const bot of group.memberBots) {
139
+ if (!bot.inChat || !bot.hasRole)
140
+ continue;
141
+ const key = roleKey(bot.larkAppId, group.chatId);
142
+ if (seen.has(key))
143
+ continue;
144
+ seen.add(key);
145
+ roleTargets.push({ larkAppId: bot.larkAppId, chatId: group.chatId });
146
+ }
689
147
  }
690
- }
691
- function flashProfileStatus(text, isError = false) {
692
- const footer = document.querySelector('#roles-profile-detail .roles-editor-footer');
693
- if (!footer)
694
- return;
695
- const statusEl = document.createElement('span');
696
- statusEl.className = `roles-saved-flash ${isError ? 'roles-save-error' : ''}`;
697
- statusEl.textContent = ` ${text}`;
698
- footer.appendChild(statusEl);
699
- scheduleRolesTimer(() => statusEl.remove(), isError ? 3000 : 2000);
700
- }
701
- export function wireRolesSurface(root, tab) {
702
- const generation = activeRolesGeneration + 1;
703
- activeRolesGeneration = generation;
704
- const timers = new Set();
705
- activeRolesTimerState = { generation, timers };
706
- const cleanups = [];
707
- let disposed = false;
708
- const isLive = () => !disposed && isRolesGeneration(generation);
709
- const on = (el, type, listener) => {
710
- if (!el)
711
- return;
712
- el.addEventListener(type, listener);
713
- cleanups.push(() => el.removeEventListener(type, listener));
148
+ const nextEffectiveRoles = await loadEffectiveRoleMap(roleTargets);
149
+ return {
150
+ entriesByProfile: new Map(detailPairs),
151
+ effectiveRolesByBot: nextEffectiveRoles,
714
152
  };
715
- activeTab = tab;
716
- expandedGroups.clear();
717
- groupProfileContextLoaded = false;
718
- resetEditor();
719
- switchTab(activeTab);
720
- const treeEl = root.querySelector('#roles-tree');
721
- const profileListEl = root.querySelector('#roles-profile-list');
722
- if (treeEl)
723
- treeEl.innerHTML = loadingHtml();
724
- if (profileListEl)
725
- profileListEl.innerHTML = loadingHtml();
726
- void (async () => {
727
- await loadGroups();
728
- if (!isLive())
729
- return;
730
- await loadProfiles();
731
- if (!isLive())
732
- return;
733
- await loadNameMaps();
734
- if (!isLive())
735
- return;
736
- if (tab === 'profiles') {
737
- const requestedChatId = hashChatId();
738
- if (requestedChatId && cache.some(g => g.chatId === requestedChatId)) {
739
- selectedApplyGroupId = requestedChatId;
740
- }
741
- }
742
- for (const g of cache) {
743
- if (botRoleCount(g) > 0)
744
- expandedGroups.add(g.chatId);
745
- }
746
- renderTree();
747
- renderProfileList();
748
- if (selectedProfileId)
749
- await selectProfile(selectedProfileId, generation);
750
- if (!isLive())
751
- return;
752
- void refreshGroupProfileContext(generation);
753
- })();
754
- on(root.querySelector('#roles-search'), 'input', (e) => {
755
- if (!isLive())
756
- return;
757
- renderTree(e.target.value);
758
- });
759
- on(root.querySelector('#roles-refresh'), 'click', async () => {
760
- if (!isLive())
761
- return;
762
- await loadGroups();
763
- if (!isLive())
764
- return;
765
- renderTree(document.getElementById('roles-search')?.value ?? '');
766
- void refreshGroupProfileContext(generation);
767
- if (selectedGroupId && selectedBotId) {
768
- const role = await loadRole(selectedBotId, selectedGroupId);
769
- if (!isLive())
770
- return;
771
- const textarea = document.getElementById('roles-editor-textarea');
772
- if (textarea)
773
- textarea.value = role.content ?? '';
774
- editingContent = role.content ?? '';
775
- editingInjectMode = role.injectMode === 'once' ? 'once' : 'every';
776
- const injectSel = document.getElementById('roles-editor-inject-mode');
777
- if (injectSel)
778
- injectSel.value = editingInjectMode;
779
- updateByteCount();
780
- updatePreview();
781
- const delBtn = document.getElementById('roles-delete');
782
- if (delBtn)
783
- delBtn.style.display = role.hasRole ? '' : 'none';
784
- }
785
- });
786
- on(root.querySelector('#roles-save'), 'click', async (e) => {
787
- if (!selectedGroupId || !selectedBotId)
788
- return;
789
- const btn = e.currentTarget;
790
- btn.disabled = true;
791
- btn.textContent = '...';
792
- try {
793
- const ok = await saveRole(selectedBotId, selectedGroupId, editingContent, editingInjectMode);
794
- if (!isLive())
795
- return;
796
- if (ok) {
797
- await loadGroups();
798
- if (!isLive())
799
- return;
800
- renderTree(document.getElementById('roles-search')?.value ?? '');
801
- void refreshGroupProfileContext(generation);
802
- const delBtn = document.getElementById('roles-delete');
803
- if (delBtn)
804
- delBtn.style.display = '';
805
- const statusEl = document.createElement('span');
806
- statusEl.className = 'roles-saved-flash';
807
- statusEl.textContent = ` ${t('roles.saved')}`;
808
- const footer = document.querySelector('.roles-editor-footer');
809
- footer?.appendChild(statusEl);
810
- scheduleRolesTimer(() => statusEl.remove(), 2000);
811
- }
812
- else {
813
- const statusEl = document.createElement('span');
814
- statusEl.className = 'roles-saved-flash roles-save-error';
815
- statusEl.textContent = editingContent.trim().length === 0
816
- ? ` ${t('roles.emptyError')}`
817
- : ` ${t('roles.saveFailed')}`;
818
- const footer = document.querySelector('.roles-editor-footer');
819
- footer?.appendChild(statusEl);
820
- scheduleRolesTimer(() => statusEl.remove(), 3000);
821
- }
822
- }
823
- finally {
824
- if (!isLive())
825
- return;
826
- btn.disabled = false;
827
- btn.textContent = t('roles.save');
828
- }
829
- });
830
- on(root.querySelector('#roles-delete'), 'click', async (e) => {
831
- if (!selectedGroupId || !selectedBotId)
832
- return;
833
- if (!confirm(t('roles.confirmDelete')))
834
- return;
835
- const btn = e.currentTarget;
836
- btn.disabled = true;
837
- btn.textContent = '...';
838
- try {
839
- const ok = await deleteRole(selectedBotId, selectedGroupId);
840
- if (!isLive())
841
- return;
842
- if (ok) {
843
- await loadGroups();
844
- if (!isLive())
845
- return;
846
- resetEditor();
847
- renderTree(document.getElementById('roles-search')?.value ?? '');
848
- void refreshGroupProfileContext(generation);
849
- }
850
- }
851
- finally {
852
- if (!isLive())
853
- return;
854
- btn.disabled = false;
855
- btn.textContent = t('roles.delete');
856
- }
857
- });
858
- on(root.querySelector('#roles-editor-textarea'), 'input', (e) => {
859
- if (!isLive())
860
- return;
861
- editingContent = e.target.value;
862
- updateByteCount();
863
- updatePreview();
864
- });
865
- // Injection-mode select auto-saves on change (it can apply even when the chat
866
- // has no own role and inherits the team default, so it doesn't ride only on
867
- // the content Save button).
868
- on(root.querySelector('#roles-editor-inject-mode'), 'change', async (e) => {
869
- if (!isLive())
870
- return;
871
- if (!selectedGroupId || !selectedBotId)
872
- return;
873
- const sel = e.target;
874
- const mode = sel.value === 'once' ? 'once' : 'every';
875
- const prev = editingInjectMode;
876
- editingInjectMode = mode;
877
- sel.disabled = true;
878
- try {
879
- const ok = await saveInjectMode(selectedBotId, selectedGroupId, mode);
880
- if (!isLive())
881
- return;
882
- if (!ok) {
883
- editingInjectMode = prev;
884
- sel.value = prev;
885
- }
886
- const statusEl = document.createElement('span');
887
- statusEl.className = `roles-saved-flash ${ok ? '' : 'roles-save-error'}`;
888
- statusEl.textContent = ` ${ok ? t('roles.saved') : t('roles.saveFailed')}`;
889
- root.querySelector('.roles-editor-inject')?.appendChild(statusEl);
890
- scheduleRolesTimer(() => statusEl.remove(), ok ? 2000 : 3000);
891
- }
892
- finally {
893
- if (!isLive())
894
- return;
895
- sel.disabled = false;
896
- }
897
- });
898
- on(root.querySelector('#roles-profile-search'), 'input', (e) => {
899
- if (!isLive())
900
- return;
901
- renderProfileList(e.target.value);
902
- });
903
- on(root.querySelector('#roles-profile-refresh'), 'click', async () => {
904
- if (!isLive())
905
- return;
906
- await loadGroups();
907
- if (!isLive())
908
- return;
909
- await loadProfiles();
910
- if (!isLive())
911
- return;
912
- void refreshGroupProfileContext(generation);
913
- if (selectedProfileId)
914
- await loadProfileEntries(selectedProfileId);
915
- if (!isLive())
916
- return;
917
- renderProfileList(document.getElementById('roles-profile-search')?.value ?? '');
918
- renderProfileDetail();
919
- });
920
- on(root.querySelector('#roles-profile-select'), 'click', async () => {
921
- if (!isLive())
922
- return;
923
- const input = document.getElementById('roles-profile-id');
924
- const profileId = input?.value.trim();
925
- if (!profileId)
926
- return;
927
- if (!isValidProfileId(profileId)) {
928
- input?.setCustomValidity(t('roles.profileIdInvalid'));
929
- input?.reportValidity();
930
- return;
931
- }
932
- input?.setCustomValidity('');
933
- await selectProfile(profileId, generation);
934
- if (!isLive())
935
- return;
936
- if (!location.hash.startsWith('#/roles/profile')) {
937
- location.hash = '#/roles/profile';
938
- }
939
- else {
940
- switchTab('profiles');
941
- }
942
- });
943
- on(root.querySelector('#roles-profile-id'), 'input', (e) => {
944
- e.target.setCustomValidity('');
945
- });
946
- return () => {
947
- disposed = true;
948
- if (isRolesGeneration(generation))
949
- activeRolesGeneration += 1;
950
- for (const cleanup of cleanups.splice(0))
951
- cleanup();
952
- for (const timer of timers)
953
- window.clearTimeout(timer);
954
- timers.clear();
955
- if (activeRolesTimerState?.generation === generation) {
956
- activeRolesTimerState = null;
957
- }
153
+ }
154
+ export async function applyRoleProfile(input) {
155
+ const r = await fetch(`/api/role-profiles/${encodeURIComponent(input.profileId)}/apply`, {
156
+ method: 'POST',
157
+ headers: { 'content-type': 'application/json' },
158
+ body: JSON.stringify({
159
+ chatId: input.chatId,
160
+ larkAppId: input.larkAppId,
161
+ force: input.force,
162
+ preview: input.preview,
163
+ }),
164
+ });
165
+ const body = await readJson(r);
166
+ return {
167
+ larkAppId: input.larkAppId,
168
+ ok: r.ok && body.ok !== false,
169
+ status: r.status,
170
+ error: body.error,
171
+ wouldRefuse: body.wouldRefuse,
958
172
  };
959
173
  }
960
174
  //# sourceMappingURL=roles.js.map