botmux 2.46.1 → 2.47.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (468) hide show
  1. package/README.en.md +5 -10
  2. package/README.md +5 -10
  3. package/dist/adapters/backend/tmux-backend.d.ts.map +1 -1
  4. package/dist/adapters/backend/tmux-backend.js +0 -11
  5. package/dist/adapters/backend/tmux-backend.js.map +1 -1
  6. package/dist/adapters/cli/claude-code.d.ts.map +1 -1
  7. package/dist/adapters/cli/claude-code.js +9 -36
  8. package/dist/adapters/cli/claude-code.js.map +1 -1
  9. package/dist/adapters/cli/coco.d.ts.map +1 -1
  10. package/dist/adapters/cli/coco.js +1 -26
  11. package/dist/adapters/cli/coco.js.map +1 -1
  12. package/dist/adapters/cli/codex.d.ts.map +1 -1
  13. package/dist/adapters/cli/codex.js +1 -6
  14. package/dist/adapters/cli/codex.js.map +1 -1
  15. package/dist/adapters/cli/cursor.d.ts.map +1 -1
  16. package/dist/adapters/cli/cursor.js +12 -58
  17. package/dist/adapters/cli/cursor.js.map +1 -1
  18. package/dist/adapters/cli/gemini.d.ts.map +1 -1
  19. package/dist/adapters/cli/gemini.js +1 -5
  20. package/dist/adapters/cli/gemini.js.map +1 -1
  21. package/dist/adapters/cli/opencode.d.ts.map +1 -1
  22. package/dist/adapters/cli/opencode.js +1 -19
  23. package/dist/adapters/cli/opencode.js.map +1 -1
  24. package/dist/adapters/cli/registry.d.ts +1 -5
  25. package/dist/adapters/cli/registry.d.ts.map +1 -1
  26. package/dist/adapters/cli/registry.js +2 -22
  27. package/dist/adapters/cli/registry.js.map +1 -1
  28. package/dist/adapters/cli/shared-hints.d.ts +1 -1
  29. package/dist/adapters/cli/shared-hints.d.ts.map +1 -1
  30. package/dist/adapters/cli/shared-hints.js +1 -2
  31. package/dist/adapters/cli/shared-hints.js.map +1 -1
  32. package/dist/adapters/cli/types.d.ts +2 -35
  33. package/dist/adapters/cli/types.d.ts.map +1 -1
  34. package/dist/bot-registry.d.ts +0 -59
  35. package/dist/bot-registry.d.ts.map +1 -1
  36. package/dist/bot-registry.js +0 -67
  37. package/dist/bot-registry.js.map +1 -1
  38. package/dist/cli/bots-list-output.d.ts +0 -8
  39. package/dist/cli/bots-list-output.d.ts.map +1 -1
  40. package/dist/cli/bots-list-output.js +0 -9
  41. package/dist/cli/bots-list-output.js.map +1 -1
  42. package/dist/cli.d.ts +1 -15
  43. package/dist/cli.d.ts.map +1 -1
  44. package/dist/cli.js +106 -603
  45. package/dist/cli.js.map +1 -1
  46. package/dist/config.d.ts +2 -11
  47. package/dist/config.d.ts.map +1 -1
  48. package/dist/config.js +4 -17
  49. package/dist/config.js.map +1 -1
  50. package/dist/core/command-handler.d.ts +0 -20
  51. package/dist/core/command-handler.d.ts.map +1 -1
  52. package/dist/core/command-handler.js +313 -762
  53. package/dist/core/command-handler.js.map +1 -1
  54. package/dist/core/dashboard-ipc-server.d.ts +0 -2
  55. package/dist/core/dashboard-ipc-server.d.ts.map +1 -1
  56. package/dist/core/dashboard-ipc-server.js +2 -222
  57. package/dist/core/dashboard-ipc-server.js.map +1 -1
  58. package/dist/core/role-resolver.d.ts +1 -17
  59. package/dist/core/role-resolver.d.ts.map +1 -1
  60. package/dist/core/role-resolver.js +10 -64
  61. package/dist/core/role-resolver.js.map +1 -1
  62. package/dist/core/session-discovery.d.ts.map +1 -1
  63. package/dist/core/session-discovery.js +5 -19
  64. package/dist/core/session-discovery.js.map +1 -1
  65. package/dist/core/session-manager.d.ts +1 -1
  66. package/dist/core/session-manager.d.ts.map +1 -1
  67. package/dist/core/session-manager.js +20 -37
  68. package/dist/core/session-manager.js.map +1 -1
  69. package/dist/core/types.d.ts +0 -5
  70. package/dist/core/types.d.ts.map +1 -1
  71. package/dist/core/types.js.map +1 -1
  72. package/dist/core/worker-pool.d.ts +0 -141
  73. package/dist/core/worker-pool.d.ts.map +1 -1
  74. package/dist/core/worker-pool.js +24 -543
  75. package/dist/core/worker-pool.js.map +1 -1
  76. package/dist/daemon.d.ts.map +1 -1
  77. package/dist/daemon.js +58 -213
  78. package/dist/daemon.js.map +1 -1
  79. package/dist/dashboard/auth.d.ts +1 -6
  80. package/dist/dashboard/auth.d.ts.map +1 -1
  81. package/dist/dashboard/auth.js +1 -9
  82. package/dist/dashboard/auth.js.map +1 -1
  83. package/dist/dashboard/web/app.js +0 -8
  84. package/dist/dashboard/web/app.js.map +1 -1
  85. package/dist/dashboard/web/bot-defaults.d.ts.map +1 -1
  86. package/dist/dashboard/web/bot-defaults.js +21 -205
  87. package/dist/dashboard/web/bot-defaults.js.map +1 -1
  88. package/dist/dashboard/web/i18n.d.ts.map +1 -1
  89. package/dist/dashboard/web/i18n.js +5 -43
  90. package/dist/dashboard/web/i18n.js.map +1 -1
  91. package/dist/dashboard/web/sessions.d.ts.map +1 -1
  92. package/dist/dashboard/web/sessions.js +0 -4
  93. package/dist/dashboard/web/sessions.js.map +1 -1
  94. package/dist/dashboard/web/workflows.js +3 -3
  95. package/dist/dashboard/web/workflows.js.map +1 -1
  96. package/dist/dashboard/workflow-api.d.ts +1 -8
  97. package/dist/dashboard/workflow-api.d.ts.map +1 -1
  98. package/dist/dashboard/workflow-api.js +4 -19
  99. package/dist/dashboard/workflow-api.js.map +1 -1
  100. package/dist/dashboard-web/app.js +375 -539
  101. package/dist/dashboard-web/index.html +1 -3
  102. package/dist/dashboard-web/style.css +0 -22
  103. package/dist/dashboard.js +2 -199
  104. package/dist/dashboard.js.map +1 -1
  105. package/dist/i18n/en.d.ts.map +1 -1
  106. package/dist/i18n/en.js +11 -104
  107. package/dist/i18n/en.js.map +1 -1
  108. package/dist/i18n/zh.d.ts.map +1 -1
  109. package/dist/i18n/zh.js +11 -104
  110. package/dist/i18n/zh.js.map +1 -1
  111. package/dist/im/lark/card-builder.d.ts +3 -108
  112. package/dist/im/lark/card-builder.d.ts.map +1 -1
  113. package/dist/im/lark/card-builder.js +50 -480
  114. package/dist/im/lark/card-builder.js.map +1 -1
  115. package/dist/im/lark/card-handler.d.ts.map +1 -1
  116. package/dist/im/lark/card-handler.js +18 -241
  117. package/dist/im/lark/card-handler.js.map +1 -1
  118. package/dist/im/lark/client.d.ts +0 -83
  119. package/dist/im/lark/client.d.ts.map +1 -1
  120. package/dist/im/lark/client.js +70 -286
  121. package/dist/im/lark/client.js.map +1 -1
  122. package/dist/im/lark/event-dispatcher.d.ts.map +1 -1
  123. package/dist/im/lark/event-dispatcher.js +4 -29
  124. package/dist/im/lark/event-dispatcher.js.map +1 -1
  125. package/dist/im/lark/grant-command.d.ts +1 -2
  126. package/dist/im/lark/grant-command.d.ts.map +1 -1
  127. package/dist/im/lark/grant-command.js +2 -3
  128. package/dist/im/lark/grant-command.js.map +1 -1
  129. package/dist/im/lark/identity-cache.d.ts.map +1 -1
  130. package/dist/im/lark/identity-cache.js +3 -3
  131. package/dist/im/lark/identity-cache.js.map +1 -1
  132. package/dist/im/lark/md-card.d.ts +2 -20
  133. package/dist/im/lark/md-card.d.ts.map +1 -1
  134. package/dist/im/lark/md-card.js +17 -49
  135. package/dist/im/lark/md-card.js.map +1 -1
  136. package/dist/im/lark/message-parser.d.ts.map +1 -1
  137. package/dist/im/lark/message-parser.js +31 -87
  138. package/dist/im/lark/message-parser.js.map +1 -1
  139. package/dist/im/lark/workflow-card-handler.d.ts +2 -2
  140. package/dist/im/lark/workflow-card-handler.d.ts.map +1 -1
  141. package/dist/im/lark/workflow-card-handler.js +1 -12
  142. package/dist/im/lark/workflow-card-handler.js.map +1 -1
  143. package/dist/im/lark/workflow-progress-card.d.ts.map +1 -1
  144. package/dist/im/lark/workflow-progress-card.js +0 -53
  145. package/dist/im/lark/workflow-progress-card.js.map +1 -1
  146. package/dist/services/codex-bridge-queue.d.ts +0 -1
  147. package/dist/services/codex-bridge-queue.d.ts.map +1 -1
  148. package/dist/services/codex-bridge-queue.js +0 -23
  149. package/dist/services/codex-bridge-queue.js.map +1 -1
  150. package/dist/services/codex-transcript.d.ts +0 -1
  151. package/dist/services/codex-transcript.d.ts.map +1 -1
  152. package/dist/services/codex-transcript.js.map +1 -1
  153. package/dist/services/feishu-task-client.d.ts +28 -0
  154. package/dist/services/feishu-task-client.d.ts.map +1 -0
  155. package/dist/services/feishu-task-client.js +123 -0
  156. package/dist/services/feishu-task-client.js.map +1 -0
  157. package/dist/services/grant-store.d.ts +2 -12
  158. package/dist/services/grant-store.d.ts.map +1 -1
  159. package/dist/services/grant-store.js +4 -51
  160. package/dist/services/grant-store.js.map +1 -1
  161. package/dist/services/group-creator.d.ts +0 -10
  162. package/dist/services/group-creator.d.ts.map +1 -1
  163. package/dist/services/group-creator.js +1 -26
  164. package/dist/services/group-creator.js.map +1 -1
  165. package/dist/services/groups-store.d.ts +0 -30
  166. package/dist/services/groups-store.d.ts.map +1 -1
  167. package/dist/services/groups-store.js +12 -85
  168. package/dist/services/groups-store.js.map +1 -1
  169. package/dist/services/project-scanner.d.ts +0 -10
  170. package/dist/services/project-scanner.d.ts.map +1 -1
  171. package/dist/services/project-scanner.js +0 -11
  172. package/dist/services/project-scanner.js.map +1 -1
  173. package/dist/services/session-store.js +1 -1
  174. package/dist/services/session-store.js.map +1 -1
  175. package/dist/services/task-store.d.ts +37 -0
  176. package/dist/services/task-store.d.ts.map +1 -0
  177. package/dist/services/task-store.js +115 -0
  178. package/dist/services/task-store.js.map +1 -0
  179. package/dist/setup/bot-config-editor.d.ts +1 -8
  180. package/dist/setup/bot-config-editor.d.ts.map +1 -1
  181. package/dist/setup/bot-config-editor.js +2 -20
  182. package/dist/setup/bot-config-editor.js.map +1 -1
  183. package/dist/setup/ensure-tmux.d.ts +22 -0
  184. package/dist/setup/ensure-tmux.d.ts.map +1 -1
  185. package/dist/setup/ensure-tmux.js +1 -25
  186. package/dist/setup/ensure-tmux.js.map +1 -1
  187. package/dist/setup/verify-permissions.d.ts.map +1 -1
  188. package/dist/setup/verify-permissions.js +1 -15
  189. package/dist/setup/verify-permissions.js.map +1 -1
  190. package/dist/skills/definitions.d.ts +0 -2
  191. package/dist/skills/definitions.d.ts.map +1 -1
  192. package/dist/skills/definitions.js +12 -178
  193. package/dist/skills/definitions.js.map +1 -1
  194. package/dist/skills/installer.d.ts +0 -34
  195. package/dist/skills/installer.d.ts.map +1 -1
  196. package/dist/skills/installer.js +2 -119
  197. package/dist/skills/installer.js.map +1 -1
  198. package/dist/types.d.ts +0 -25
  199. package/dist/types.d.ts.map +1 -1
  200. package/dist/utils/bot-routing.d.ts +0 -50
  201. package/dist/utils/bot-routing.d.ts.map +1 -1
  202. package/dist/utils/bot-routing.js +0 -83
  203. package/dist/utils/bot-routing.js.map +1 -1
  204. package/dist/utils/user-token.d.ts.map +1 -1
  205. package/dist/utils/user-token.js +2 -0
  206. package/dist/utils/user-token.js.map +1 -1
  207. package/dist/worker.js +27 -198
  208. package/dist/worker.js.map +1 -1
  209. package/dist/workflows/attempt-resume.d.ts.map +1 -1
  210. package/dist/workflows/attempt-resume.js +2 -2
  211. package/dist/workflows/attempt-resume.js.map +1 -1
  212. package/dist/workflows/definition.d.ts +9 -412
  213. package/dist/workflows/definition.d.ts.map +1 -1
  214. package/dist/workflows/definition.js +3 -238
  215. package/dist/workflows/definition.js.map +1 -1
  216. package/dist/workflows/events/payloads.d.ts +11 -114
  217. package/dist/workflows/events/payloads.d.ts.map +1 -1
  218. package/dist/workflows/events/payloads.js +0 -46
  219. package/dist/workflows/events/payloads.js.map +1 -1
  220. package/dist/workflows/events/replay.d.ts +0 -21
  221. package/dist/workflows/events/replay.d.ts.map +1 -1
  222. package/dist/workflows/events/replay.js +0 -103
  223. package/dist/workflows/events/replay.js.map +1 -1
  224. package/dist/workflows/events/schema.d.ts +1017 -1712
  225. package/dist/workflows/events/schema.d.ts.map +1 -1
  226. package/dist/workflows/events/schema.js +1 -37
  227. package/dist/workflows/events/schema.js.map +1 -1
  228. package/dist/workflows/events/types.d.ts +1 -5
  229. package/dist/workflows/events/types.d.ts.map +1 -1
  230. package/dist/workflows/loader.d.ts +0 -14
  231. package/dist/workflows/loader.d.ts.map +1 -1
  232. package/dist/workflows/loader.js +0 -27
  233. package/dist/workflows/loader.js.map +1 -1
  234. package/dist/workflows/loop.js +0 -58
  235. package/dist/workflows/loop.js.map +1 -1
  236. package/dist/workflows/ops-projection.d.ts +0 -58
  237. package/dist/workflows/ops-projection.d.ts.map +1 -1
  238. package/dist/workflows/ops-projection.js +0 -74
  239. package/dist/workflows/ops-projection.js.map +1 -1
  240. package/dist/workflows/orchestrator.d.ts +1 -65
  241. package/dist/workflows/orchestrator.d.ts.map +1 -1
  242. package/dist/workflows/orchestrator.js +74 -486
  243. package/dist/workflows/orchestrator.js.map +1 -1
  244. package/dist/workflows/output-binding.d.ts +1 -8
  245. package/dist/workflows/output-binding.d.ts.map +1 -1
  246. package/dist/workflows/output-binding.js +11 -75
  247. package/dist/workflows/output-binding.js.map +1 -1
  248. package/dist/workflows/runtime.d.ts +1 -1
  249. package/dist/workflows/runtime.d.ts.map +1 -1
  250. package/dist/workflows/runtime.js +4 -39
  251. package/dist/workflows/runtime.js.map +1 -1
  252. package/dist/workflows/wait.d.ts +2 -23
  253. package/dist/workflows/wait.d.ts.map +1 -1
  254. package/dist/workflows/wait.js +17 -39
  255. package/dist/workflows/wait.js.map +1 -1
  256. package/package.json +1 -1
  257. package/dist/adapters/adopt-route.d.ts +0 -63
  258. package/dist/adapters/adopt-route.d.ts.map +0 -1
  259. package/dist/adapters/adopt-route.js +0 -195
  260. package/dist/adapters/adopt-route.js.map +0 -1
  261. package/dist/adapters/cli/codex-app.d.ts +0 -4
  262. package/dist/adapters/cli/codex-app.d.ts.map +0 -1
  263. package/dist/adapters/cli/codex-app.js +0 -72
  264. package/dist/adapters/cli/codex-app.js.map +0 -1
  265. package/dist/adapters/cli/hermes.d.ts +0 -4
  266. package/dist/adapters/cli/hermes.d.ts.map +0 -1
  267. package/dist/adapters/cli/hermes.js +0 -40
  268. package/dist/adapters/cli/hermes.js.map +0 -1
  269. package/dist/adapters/cli/mira.d.ts +0 -4
  270. package/dist/adapters/cli/mira.d.ts.map +0 -1
  271. package/dist/adapters/cli/mira.js +0 -67
  272. package/dist/adapters/cli/mira.js.map +0 -1
  273. package/dist/adapters/cli/mtr.d.ts +0 -5
  274. package/dist/adapters/cli/mtr.d.ts.map +0 -1
  275. package/dist/adapters/cli/mtr.js +0 -62
  276. package/dist/adapters/cli/mtr.js.map +0 -1
  277. package/dist/adapters/hook-command.d.ts +0 -18
  278. package/dist/adapters/hook-command.d.ts.map +0 -1
  279. package/dist/adapters/hook-command.js +0 -38
  280. package/dist/adapters/hook-command.js.map +0 -1
  281. package/dist/adapters/hook-installer.d.ts +0 -14
  282. package/dist/adapters/hook-installer.d.ts.map +0 -1
  283. package/dist/adapters/hook-installer.js +0 -192
  284. package/dist/adapters/hook-installer.js.map +0 -1
  285. package/dist/codex-app-runner.d.ts +0 -3
  286. package/dist/codex-app-runner.d.ts.map +0 -1
  287. package/dist/codex-app-runner.js +0 -512
  288. package/dist/codex-app-runner.js.map +0 -1
  289. package/dist/core/ask-api.d.ts +0 -47
  290. package/dist/core/ask-api.d.ts.map +0 -1
  291. package/dist/core/ask-api.js +0 -139
  292. package/dist/core/ask-api.js.map +0 -1
  293. package/dist/core/ask-args.d.ts +0 -53
  294. package/dist/core/ask-args.d.ts.map +0 -1
  295. package/dist/core/ask-args.js +0 -122
  296. package/dist/core/ask-args.js.map +0 -1
  297. package/dist/core/ask-broker.d.ts +0 -98
  298. package/dist/core/ask-broker.d.ts.map +0 -1
  299. package/dist/core/ask-broker.js +0 -329
  300. package/dist/core/ask-broker.js.map +0 -1
  301. package/dist/core/ask-hook/claude-code.d.ts +0 -50
  302. package/dist/core/ask-hook/claude-code.d.ts.map +0 -1
  303. package/dist/core/ask-hook/claude-code.js +0 -145
  304. package/dist/core/ask-hook/claude-code.js.map +0 -1
  305. package/dist/core/ask-hook/codex.d.ts +0 -43
  306. package/dist/core/ask-hook/codex.d.ts.map +0 -1
  307. package/dist/core/ask-hook/codex.js +0 -69
  308. package/dist/core/ask-hook/codex.js.map +0 -1
  309. package/dist/core/ask-hook/opencode.d.ts +0 -41
  310. package/dist/core/ask-hook/opencode.d.ts.map +0 -1
  311. package/dist/core/ask-hook/opencode.js +0 -108
  312. package/dist/core/ask-hook/opencode.js.map +0 -1
  313. package/dist/core/ask-hook/registry.d.ts +0 -3
  314. package/dist/core/ask-hook/registry.d.ts.map +0 -1
  315. package/dist/core/ask-hook/registry.js +0 -12
  316. package/dist/core/ask-hook/registry.js.map +0 -1
  317. package/dist/core/ask-hook/types.d.ts +0 -26
  318. package/dist/core/ask-hook/types.d.ts.map +0 -1
  319. package/dist/core/ask-hook/types.js +0 -2
  320. package/dist/core/ask-hook/types.js.map +0 -1
  321. package/dist/core/ask-types.d.ts +0 -146
  322. package/dist/core/ask-types.d.ts.map +0 -1
  323. package/dist/core/ask-types.js +0 -18
  324. package/dist/core/ask-types.js.map +0 -1
  325. package/dist/core/trigger-session.d.ts +0 -9
  326. package/dist/core/trigger-session.d.ts.map +0 -1
  327. package/dist/core/trigger-session.js +0 -158
  328. package/dist/core/trigger-session.js.map +0 -1
  329. package/dist/dashboard/connector-api.d.ts +0 -3
  330. package/dist/dashboard/connector-api.d.ts.map +0 -1
  331. package/dist/dashboard/connector-api.js +0 -351
  332. package/dist/dashboard/connector-api.js.map +0 -1
  333. package/dist/dashboard/federated-group-core.d.ts +0 -54
  334. package/dist/dashboard/federated-group-core.d.ts.map +0 -1
  335. package/dist/dashboard/federated-group-core.js +0 -165
  336. package/dist/dashboard/federated-group-core.js.map +0 -1
  337. package/dist/dashboard/federation-api.d.ts +0 -42
  338. package/dist/dashboard/federation-api.d.ts.map +0 -1
  339. package/dist/dashboard/federation-api.js +0 -408
  340. package/dist/dashboard/federation-api.js.map +0 -1
  341. package/dist/dashboard/federation-spoke-api.d.ts +0 -76
  342. package/dist/dashboard/federation-spoke-api.d.ts.map +0 -1
  343. package/dist/dashboard/federation-spoke-api.js +0 -618
  344. package/dist/dashboard/federation-spoke-api.js.map +0 -1
  345. package/dist/dashboard/team-group.d.ts +0 -18
  346. package/dist/dashboard/team-group.d.ts.map +0 -1
  347. package/dist/dashboard/team-group.js +0 -7
  348. package/dist/dashboard/team-group.js.map +0 -1
  349. package/dist/dashboard/trigger-api.d.ts +0 -13
  350. package/dist/dashboard/trigger-api.d.ts.map +0 -1
  351. package/dist/dashboard/trigger-api.js +0 -77
  352. package/dist/dashboard/trigger-api.js.map +0 -1
  353. package/dist/dashboard/web/connectors.d.ts +0 -2
  354. package/dist/dashboard/web/connectors.d.ts.map +0 -1
  355. package/dist/dashboard/web/connectors.js +0 -187
  356. package/dist/dashboard/web/connectors.js.map +0 -1
  357. package/dist/dashboard/web/team-federation.d.ts +0 -3
  358. package/dist/dashboard/web/team-federation.d.ts.map +0 -1
  359. package/dist/dashboard/web/team-federation.js +0 -487
  360. package/dist/dashboard/web/team-federation.js.map +0 -1
  361. package/dist/dashboard/webhook-routes.d.ts +0 -19
  362. package/dist/dashboard/webhook-routes.d.ts.map +0 -1
  363. package/dist/dashboard/webhook-routes.js +0 -321
  364. package/dist/dashboard/webhook-routes.js.map +0 -1
  365. package/dist/im/lark/ask-card.d.ts +0 -55
  366. package/dist/im/lark/ask-card.d.ts.map +0 -1
  367. package/dist/im/lark/ask-card.js +0 -328
  368. package/dist/im/lark/ask-card.js.map +0 -1
  369. package/dist/mira-output.d.ts +0 -3
  370. package/dist/mira-output.d.ts.map +0 -1
  371. package/dist/mira-output.js +0 -136
  372. package/dist/mira-output.js.map +0 -1
  373. package/dist/mira-runner.d.ts +0 -3
  374. package/dist/mira-runner.d.ts.map +0 -1
  375. package/dist/mira-runner.js +0 -534
  376. package/dist/mira-runner.js.map +0 -1
  377. package/dist/services/bot-owner-store.d.ts +0 -28
  378. package/dist/services/bot-owner-store.d.ts.map +0 -1
  379. package/dist/services/bot-owner-store.js +0 -82
  380. package/dist/services/bot-owner-store.js.map +0 -1
  381. package/dist/services/bot-profile-store.d.ts +0 -16
  382. package/dist/services/bot-profile-store.d.ts.map +0 -1
  383. package/dist/services/bot-profile-store.js +0 -98
  384. package/dist/services/bot-profile-store.js.map +0 -1
  385. package/dist/services/brand-store.d.ts +0 -15
  386. package/dist/services/brand-store.d.ts.map +0 -1
  387. package/dist/services/brand-store.js +0 -47
  388. package/dist/services/brand-store.js.map +0 -1
  389. package/dist/services/card-prefs-store.d.ts +0 -20
  390. package/dist/services/card-prefs-store.d.ts.map +0 -1
  391. package/dist/services/card-prefs-store.js +0 -82
  392. package/dist/services/card-prefs-store.js.map +0 -1
  393. package/dist/services/connector-store.d.ts +0 -58
  394. package/dist/services/connector-store.d.ts.map +0 -1
  395. package/dist/services/connector-store.js +0 -79
  396. package/dist/services/connector-store.js.map +0 -1
  397. package/dist/services/deployment-identity.d.ts +0 -22
  398. package/dist/services/deployment-identity.d.ts.map +0 -1
  399. package/dist/services/deployment-identity.js +0 -67
  400. package/dist/services/deployment-identity.js.map +0 -1
  401. package/dist/services/federation-membership-store.d.ts +0 -23
  402. package/dist/services/federation-membership-store.d.ts.map +0 -1
  403. package/dist/services/federation-membership-store.js +0 -66
  404. package/dist/services/federation-membership-store.js.map +0 -1
  405. package/dist/services/federation-roster.d.ts +0 -54
  406. package/dist/services/federation-roster.d.ts.map +0 -1
  407. package/dist/services/federation-roster.js +0 -51
  408. package/dist/services/federation-roster.js.map +0 -1
  409. package/dist/services/federation-store.d.ts +0 -76
  410. package/dist/services/federation-store.d.ts.map +0 -1
  411. package/dist/services/federation-store.js +0 -133
  412. package/dist/services/federation-store.js.map +0 -1
  413. package/dist/services/hermes-transcript.d.ts +0 -7
  414. package/dist/services/hermes-transcript.d.ts.map +0 -1
  415. package/dist/services/hermes-transcript.js +0 -117
  416. package/dist/services/hermes-transcript.js.map +0 -1
  417. package/dist/services/invite-store.d.ts +0 -28
  418. package/dist/services/invite-store.d.ts.map +0 -1
  419. package/dist/services/invite-store.js +0 -85
  420. package/dist/services/invite-store.js.map +0 -1
  421. package/dist/services/pairing-store.d.ts +0 -47
  422. package/dist/services/pairing-store.d.ts.map +0 -1
  423. package/dist/services/pairing-store.js +0 -132
  424. package/dist/services/pairing-store.js.map +0 -1
  425. package/dist/services/relay-picker.d.ts +0 -22
  426. package/dist/services/relay-picker.d.ts.map +0 -1
  427. package/dist/services/relay-picker.js +0 -62
  428. package/dist/services/relay-picker.js.map +0 -1
  429. package/dist/services/send-policy.d.ts +0 -55
  430. package/dist/services/send-policy.d.ts.map +0 -1
  431. package/dist/services/send-policy.js +0 -47
  432. package/dist/services/send-policy.js.map +0 -1
  433. package/dist/services/team-roster.d.ts +0 -38
  434. package/dist/services/team-roster.d.ts.map +0 -1
  435. package/dist/services/team-roster.js +0 -82
  436. package/dist/services/team-roster.js.map +0 -1
  437. package/dist/services/team-store.d.ts +0 -54
  438. package/dist/services/team-store.d.ts.map +0 -1
  439. package/dist/services/team-store.js +0 -156
  440. package/dist/services/team-store.js.map +0 -1
  441. package/dist/services/trigger-log-store.d.ts +0 -46
  442. package/dist/services/trigger-log-store.d.ts.map +0 -1
  443. package/dist/services/trigger-log-store.js +0 -132
  444. package/dist/services/trigger-log-store.js.map +0 -1
  445. package/dist/services/trigger-types.d.ts +0 -57
  446. package/dist/services/trigger-types.d.ts.map +0 -1
  447. package/dist/services/trigger-types.js +0 -28
  448. package/dist/services/trigger-types.js.map +0 -1
  449. package/dist/services/webhook-key.d.ts +0 -16
  450. package/dist/services/webhook-key.d.ts.map +0 -1
  451. package/dist/services/webhook-key.js +0 -123
  452. package/dist/services/webhook-key.js.map +0 -1
  453. package/dist/services/webhook-lifecycle-extractors.d.ts +0 -15
  454. package/dist/services/webhook-lifecycle-extractors.d.ts.map +0 -1
  455. package/dist/services/webhook-lifecycle-extractors.js +0 -59
  456. package/dist/services/webhook-lifecycle-extractors.js.map +0 -1
  457. package/dist/services/webhook-lifecycle-store.d.ts +0 -45
  458. package/dist/services/webhook-lifecycle-store.d.ts.map +0 -1
  459. package/dist/services/webhook-lifecycle-store.js +0 -159
  460. package/dist/services/webhook-lifecycle-store.js.map +0 -1
  461. package/dist/utils/daemon-discovery.d.ts +0 -11
  462. package/dist/utils/daemon-discovery.d.ts.map +0 -1
  463. package/dist/utils/daemon-discovery.js +0 -59
  464. package/dist/utils/daemon-discovery.js.map +0 -1
  465. package/dist/workflows/trigger-from-envelope.d.ts +0 -13
  466. package/dist/workflows/trigger-from-envelope.d.ts.map +0 -1
  467. package/dist/workflows/trigger-from-envelope.js +0 -67
  468. package/dist/workflows/trigger-from-envelope.js.map +0 -1
@@ -4,14 +4,10 @@ const cliDisplayNames = {
4
4
  'aiden': 'Aiden',
5
5
  'coco': 'CoCo',
6
6
  'codex': 'Codex',
7
- 'codex-app': 'Codex App',
8
7
  'cursor': 'Cursor',
9
8
  'gemini': 'Gemini',
10
9
  'opencode': 'OpenCode',
11
10
  'antigravity': 'Antigravity',
12
- 'mtr': 'MTR',
13
- 'hermes': 'Hermes',
14
- 'mira': 'Mira',
15
11
  };
16
12
  export function getCliDisplayName(cliId) {
17
13
  return cliDisplayNames[cliId] ?? cliId;
@@ -133,70 +129,14 @@ export function buildSessionClosedCard(sessionId, rootId, title, cliId, workingD
133
129
  };
134
130
  return JSON.stringify(card);
135
131
  }
136
- /**
137
- * Build a frozen-snapshot card to PATCH onto the source-chat streaming card
138
- * after `/relay` moves the session elsewhere.
139
- *
140
- * Why this exists: a live streaming card carries action buttons (close /
141
- * toggle display / get write link). Those buttons identify their session by
142
- * `session_id` in the value payload, so clicking them after relay still
143
- * reaches the now-relocated session — closing it, toggling its display
144
- * mode, etc. — but the visible feedback all lands on the NEW card in the
145
- * target chat, not this one. The source-chat card then looks like a "live
146
- * console" while actually being a footgun. PATCH it to an inert snapshot
147
- * so the user sees clearly it's historical.
148
- *
149
- * Last-frame rendering:
150
- * - imageKey present (session was in 'screenshot' / expanded mode at
151
- * relay time) → embed the same img element the live card had.
152
- * img_key is a Lark server resource independent of the card it lived
153
- * on, so the PATCHed card can still reference it.
154
- * - imageKey absent (hidden / collapsed mode) → render nothing extra.
155
- * The header + body notice already convey the state; raw tmux pane
156
- * text as a code-block is too long and noisy (王皓 caught this in
157
- * testing).
158
- *
159
- * No action buttons are rendered in either case.
160
- */
161
- export function buildRelayedFrozenCard(title, cliId, imageKey, locale) {
162
- const cliName = getCliDisplayName(cliId ?? 'claude-code');
163
- const body = `**${escapeMd(title || cliName)}**\n` +
164
- `${t('card.body.relay_frozen', undefined, locale)}`;
165
- const elements = [
166
- { tag: 'markdown', content: body },
167
- ];
168
- if (imageKey) {
169
- elements.push({
170
- tag: 'img',
171
- img_key: imageKey,
172
- alt: { tag: 'plain_text', content: '' },
173
- mode: 'fit_horizontal',
174
- preview: true,
175
- });
176
- }
177
- const card = {
178
- config: { wide_screen_mode: true },
179
- header: {
180
- title: { tag: 'plain_text', content: t('card.status.relay_frozen', undefined, locale) },
181
- template: 'grey',
182
- },
183
- elements,
184
- };
185
- return JSON.stringify(card);
186
- }
187
132
  /**
188
133
  * Feishu card API rejects payloads exceeding ~109 KB (error 230025).
189
134
  * Cap markdown content byte size with headroom for card JSON overhead.
190
135
  */
191
136
  const MAX_CONTENT_BYTES = 100_000;
192
- /**
193
- * Truncate content to fit within `maxBytes`, keeping the tail (most recent
194
- * output). Defaults to {@link MAX_CONTENT_BYTES}; callers that wrap the content
195
- * in additional card JSON (e.g. the private snapshot's code fence) pass a
196
- * tighter budget so the whole card stays under Feishu's ~109 KB hard limit.
197
- */
198
- export function truncateContent(content, locale, maxBytes = MAX_CONTENT_BYTES) {
199
- if (Buffer.byteLength(content, 'utf-8') <= maxBytes)
137
+ /** Truncate content to fit within MAX_CONTENT_BYTES, keeping the tail (most recent output). */
138
+ export function truncateContent(content, locale) {
139
+ if (Buffer.byteLength(content, 'utf-8') <= MAX_CONTENT_BYTES)
200
140
  return content;
201
141
  // Binary search for the longest suffix that fits
202
142
  const lines = content.split('\n');
@@ -205,7 +145,7 @@ export function truncateContent(content, locale, maxBytes = MAX_CONTENT_BYTES) {
205
145
  while (lo < hi) {
206
146
  const mid = Math.floor((lo + hi) / 2);
207
147
  const candidate = lines.slice(mid).join('\n');
208
- if (Buffer.byteLength(candidate, 'utf-8') <= maxBytes - 30) {
148
+ if (Buffer.byteLength(candidate, 'utf-8') <= MAX_CONTENT_BYTES - 30) {
209
149
  hi = mid;
210
150
  }
211
151
  else {
@@ -214,29 +154,36 @@ export function truncateContent(content, locale, maxBytes = MAX_CONTENT_BYTES) {
214
154
  }
215
155
  return `${t('card.status.truncated_prefix', undefined, locale)}\n${lines.slice(lo).join('\n')}`;
216
156
  }
217
- /** Byte budget for the private snapshot's text fallback. Well under the ~109 KB
218
- * card limit, leaving room for JSON escaping + the card's structural overhead. */
219
- const PRIVATE_SNAPSHOT_TEXT_MAX = 50_000;
220
- const STREAM_TEMPLATE_MAP = {
221
- starting: 'yellow', working: 'blue', idle: 'green', analyzing: 'purple', limited: 'red', retry_ready: 'green',
222
- };
223
- /** Header status label for a streaming/snapshot card. Shared by the live card
224
- * and the private snapshot so the two never drift. */
225
- function streamStatusLabel(status, usageLimit, locale) {
226
- switch (status) {
227
- case 'starting': return t('card.status.starting', undefined, locale);
228
- case 'working': return t('card.status.working', undefined, locale);
229
- case 'idle': return t('card.status.idle', undefined, locale);
230
- case 'analyzing': return t('card.status.analyzing', undefined, locale);
231
- case 'limited': return usageLimit?.retryReady
232
- ? t('card.status.retry_ready', undefined, locale)
233
- : t('card.status.limited', undefined, locale);
234
- }
235
- }
236
- /** Push the shared "output body" elements (usage-limit notice + screenshot) used
237
- * by both {@link buildStreamingCard} and {@link buildPrivateSnapshotCard}. */
238
- function pushStreamBody(elements, opts) {
239
- const { status, usageLimit, displayMode, imageKey, cliName, locale } = opts;
157
+ /**
158
+ * Build a Feishu streaming card that shows live terminal output + controls.
159
+ * This card is PATCHed in-place as the CLI works.
160
+ *
161
+ * displayMode:
162
+ * - 'hidden' — body collapsed; only header + main controls visible.
163
+ * - 'screenshot' img element (rendered server-side, uploaded for img_key).
164
+ *
165
+ * Quick-action buttons (Esc, ^C, Tab, Space, Enter, ←↑↓→, ½屏 ↑/↓) appear
166
+ * whenever displayMode !== 'hidden'.
167
+ */
168
+ export function buildStreamingCard(sessionId, rootId, terminalUrl, title, screenContent, status, cliId, displayMode = 'hidden', cardNonce, imageKey, adoptMode, showTakeover, locale, usageLimit) {
169
+ const effectiveCliId = cliId ?? 'claude-code';
170
+ const cliName = getCliDisplayName(effectiveCliId);
171
+ const actionBase = { root_id: rootId, session_id: sessionId, cli_id: effectiveCliId, ...(cardNonce ? { card_nonce: cardNonce } : {}) };
172
+ const displayStatus = status === 'limited' && usageLimit?.retryReady ? 'retry_ready' : status;
173
+ const templateMap = { starting: 'yellow', working: 'blue', idle: 'green', analyzing: 'purple', limited: 'red', retry_ready: 'green' };
174
+ const statusLabel = (s) => {
175
+ switch (s) {
176
+ case 'starting': return t('card.status.starting', undefined, locale);
177
+ case 'working': return t('card.status.working', undefined, locale);
178
+ case 'idle': return t('card.status.idle', undefined, locale);
179
+ case 'analyzing': return t('card.status.analyzing', undefined, locale);
180
+ case 'limited': return usageLimit?.retryReady
181
+ ? t('card.status.retry_ready', undefined, locale)
182
+ : t('card.status.limited', undefined, locale);
183
+ }
184
+ };
185
+ const elements = [];
186
+ // ── Output body ─────────────────────────────────────────────────────────
240
187
  if (status === 'limited' && usageLimit) {
241
188
  elements.push({
242
189
  tag: 'markdown',
@@ -248,33 +195,19 @@ function pushStreamBody(elements, opts) {
248
195
  }
249
196
  if (displayMode === 'screenshot') {
250
197
  if (imageKey) {
251
- elements.push({ tag: 'img', img_key: imageKey, alt: { tag: 'plain_text', content: '' }, mode: 'fit_horizontal', preview: true });
198
+ elements.push({
199
+ tag: 'img',
200
+ img_key: imageKey,
201
+ alt: { tag: 'plain_text', content: '' },
202
+ mode: 'fit_horizontal',
203
+ preview: true,
204
+ });
252
205
  }
253
206
  else {
254
207
  elements.push({ tag: 'markdown', content: t('card.status.waiting_screenshot', undefined, locale) });
255
208
  }
256
209
  elements.push({ tag: 'hr' });
257
210
  }
258
- }
259
- /**
260
- * Build a Feishu streaming card that shows live terminal output + controls.
261
- * This card is PATCHed in-place as the CLI works.
262
- *
263
- * displayMode:
264
- * - 'hidden' — body collapsed; only header + main controls visible.
265
- * - 'screenshot' — img element (rendered server-side, uploaded for img_key).
266
- *
267
- * Quick-action buttons (Esc, ^C, Tab, Space, Enter, ←↑↓→, ½屏 ↑/↓) appear
268
- * whenever displayMode !== 'hidden'.
269
- */
270
- export function buildStreamingCard(sessionId, rootId, terminalUrl, title, screenContent, status, cliId, displayMode = 'hidden', cardNonce, imageKey, adoptMode, showTakeover, locale, usageLimit, writableTerminalUrl) {
271
- const effectiveCliId = cliId ?? 'claude-code';
272
- const cliName = getCliDisplayName(effectiveCliId);
273
- const actionBase = { root_id: rootId, session_id: sessionId, cli_id: effectiveCliId, ...(cardNonce ? { card_nonce: cardNonce } : {}) };
274
- const displayStatus = status === 'limited' && usageLimit?.retryReady ? 'retry_ready' : status;
275
- const elements = [];
276
- // ── Output body (shared with the private snapshot card) ──────────────────
277
- pushStreamBody(elements, { status, usageLimit, displayMode, imageKey, cliName, locale });
278
211
  // ── Main control row: display toggle, mode toggle, terminal, manage ─────
279
212
  const headerActions = [];
280
213
  headerActions.push({
@@ -344,16 +277,6 @@ export function buildStreamingCard(sessionId, rootId, terminalUrl, title, screen
344
277
  });
345
278
  }
346
279
  elements.push({ tag: 'action', actions: headerActions });
347
- // ── Writable terminal link (opt-in) ─────────────────────────────────────
348
- // When the bot enables `writableTerminalLinkInCard`, embed the token-bearing
349
- // link right in the card so anyone here can open a writable terminal without
350
- // the get-write-link → DM round-trip. The link is intentionally group-visible.
351
- if (writableTerminalUrl) {
352
- elements.push({
353
- tag: 'markdown',
354
- content: t('card.writable_terminal_link', { url: writableTerminalUrl }, locale),
355
- });
356
- }
357
280
  // ── Quick-action keys (only when the screenshot is visible — in text mode
358
281
  // there's no visible cursor/input, so these keys would fire blindly) ──
359
282
  if (displayMode === 'screenshot') {
@@ -388,94 +311,8 @@ export function buildStreamingCard(sessionId, rootId, terminalUrl, title, screen
388
311
  const card = {
389
312
  config: { wide_screen_mode: true },
390
313
  header: {
391
- title: { tag: 'plain_text', content: `🖥️ ${cliName} · ${escapeMd(title)} — ${streamStatusLabel(status, usageLimit, locale)}` },
392
- template: STREAM_TEMPLATE_MAP[displayStatus],
393
- },
394
- elements,
395
- };
396
- return JSON.stringify(card);
397
- }
398
- /**
399
- * Build a static "private snapshot" card for `/card` in private mode — sent via
400
- * the ephemeral API to one user at a time. Unlike {@link buildStreamingCard} it
401
- * is **never PATCH-updated** (ephemeral cards can't be), so it carries only a
402
- * one-shot snapshot of the terminal screenshot plus three buttons:
403
- * • read-only "open terminal" link (a plain URL button — no callback);
404
- * • "get write link", whose callback DMs the writable link to the clicker;
405
- * • "close session", whose callback kills the session and (in private mode)
406
- * sends the "closed" card ephemeral to the owner audience too — so the
407
- * session title / CLI name / workingDir on it don't leak to the group.
408
- * The last two have callbacks but neither patches THIS card (one DMs, the other
409
- * sends a fresh card), so both work fine on an ephemeral card. Both are
410
- * `canOperate`-gated in the handler — talk-only viewers who tap them are denied.
411
- * The patch-driven controls (toggle/refresh/export/term keys) and the inline
412
- * writable link are still omitted: those need to update this card, which
413
- * ephemeral cards can't do.
414
- */
415
- export function buildPrivateSnapshotCard(terminalUrl, title, status, cliId, imageKey, screenContent, sessionId, rootId, locale, usageLimit) {
416
- const effectiveCliId = cliId ?? 'claude-code';
417
- const cliName = getCliDisplayName(effectiveCliId);
418
- const displayStatus = status === 'limited' && usageLimit?.retryReady ? 'retry_ready' : status;
419
- // `visibility: 'private'` pins this card's privacy intent onto the action
420
- // itself, so a later callback (notably `close`) keeps sending ephemeral even
421
- // if the bot's `privateCard` config is toggled off after the card was sent —
422
- // otherwise the closed card (session title / workingDir / resume command)
423
- // could leak to the group. See the `close` handler in card-handler.ts.
424
- const actionBase = { root_id: rootId, session_id: sessionId, cli_id: effectiveCliId, visibility: 'private' };
425
- const elements = [];
426
- // Show the terminal once: prefer the rendered screenshot when present;
427
- // otherwise fall back to a code-block of the latest screen text so the
428
- // snapshot isn't empty (common when the bot has the streaming card disabled
429
- // or display mode never flipped to screenshot — `lastScreenContent` is still
430
- // kept up to date regardless). pushStreamBody also emits the usage-limit
431
- // notice, which applies in either case.
432
- pushStreamBody(elements, {
433
- status, usageLimit, displayMode: imageKey ? 'screenshot' : 'hidden', imageKey, cliName, locale,
434
- });
435
- if (!imageKey) {
436
- const text = (screenContent ?? '').replace(/[ \t\r\n]+$/, '');
437
- if (text) {
438
- const body = truncateContent(text, locale, PRIVATE_SNAPSHOT_TEXT_MAX);
439
- // Fence must be longer than the longest backtick run in the body, else
440
- // terminal output containing ``` would break out of the code block.
441
- const maxRun = (body.match(/`+/g) ?? []).reduce((m, r) => Math.max(m, r.length), 0);
442
- const fence = '`'.repeat(Math.max(3, maxRun + 1));
443
- elements.push({ tag: 'markdown', content: `${fence}\n${body}\n${fence}` });
444
- elements.push({ tag: 'hr' });
445
- }
446
- }
447
- elements.push({
448
- tag: 'action',
449
- actions: [
450
- {
451
- tag: 'button',
452
- text: { tag: 'plain_text', content: t('card.btn.open_terminal', undefined, locale) },
453
- type: 'primary',
454
- multi_url: { url: terminalUrl, pc_url: terminalUrl, android_url: terminalUrl, ios_url: terminalUrl },
455
- },
456
- {
457
- tag: 'button',
458
- text: { tag: 'plain_text', content: t('card.btn.get_write_link', undefined, locale) },
459
- type: 'default',
460
- value: { action: 'get_write_link', ...actionBase },
461
- },
462
- {
463
- tag: 'button',
464
- text: { tag: 'plain_text', content: t('card.btn.close_session', undefined, locale) },
465
- type: 'danger',
466
- value: { action: 'close', ...actionBase },
467
- },
468
- ],
469
- });
470
- elements.push({
471
- tag: 'note',
472
- elements: [{ tag: 'lark_md', content: t('card.private.snapshot_note', undefined, locale) }],
473
- });
474
- const card = {
475
- config: { wide_screen_mode: true },
476
- header: {
477
- title: { tag: 'plain_text', content: `🔒 ${cliName} · ${escapeMd(title)} — ${streamStatusLabel(status, usageLimit, locale)}` },
478
- template: STREAM_TEMPLATE_MAP[displayStatus],
314
+ title: { tag: 'plain_text', content: `🖥️ ${cliName} · ${escapeMd(title)} — ${statusLabel(status)}` },
315
+ template: templateMap[displayStatus],
479
316
  },
480
317
  elements,
481
318
  };
@@ -548,22 +385,19 @@ export function buildGrantCard(o, locale) {
548
385
  ? t('card.grant.body_request', { name: escapeMd(o.requesterName), owner: o.ownerOpenId }, locale)
549
386
  : t('card.grant.body_owner', { name: escapeMd(o.requesterName), owner: o.ownerOpenId }, locale);
550
387
  const v = { target_open_id: o.requesterOpenId, chat_id: o.chatId, nonce: o.nonce };
551
- // 「全局授权对话」只在 owner 主动发卡时出现:owner 一眼明确要给全局;request 模式(成员
552
- // 自助申请)只提供「本群」,避免成员把自己申请到全局。两个授权按钮都是 talk-only。
553
- const grantButtons = [
554
- { tag: 'button', type: 'primary', text: { tag: 'plain_text', content: t('card.grant.btn_chat', undefined, locale) }, value: { action: 'grant_chat', ...v } },
555
- ];
556
- if (o.mode === 'owner') {
557
- grantButtons.push({ tag: 'button', type: 'default', text: { tag: 'plain_text', content: t('card.grant.btn_global', undefined, locale) }, value: { action: 'grant_global', ...v } });
558
- }
559
- grantButtons.push({ tag: 'button', type: 'danger', text: { tag: 'plain_text', content: t('card.grant.btn_deny', undefined, locale) }, value: { action: 'grant_deny', ...v } });
560
388
  const card = {
561
389
  config: { wide_screen_mode: true },
562
390
  header: { template: 'orange', title: { tag: 'plain_text', content: t('card.grant.title', undefined, locale) } },
563
391
  elements: [
564
392
  { tag: 'div', text: { tag: 'lark_md', content: body } },
565
393
  { tag: 'hr' },
566
- { tag: 'action', actions: grantButtons },
394
+ {
395
+ tag: 'action',
396
+ actions: [
397
+ { tag: 'button', type: 'primary', text: { tag: 'plain_text', content: t('card.grant.btn_chat', undefined, locale) }, value: { action: 'grant_chat', ...v } },
398
+ { tag: 'button', type: 'danger', text: { tag: 'plain_text', content: t('card.grant.btn_deny', undefined, locale) }, value: { action: 'grant_deny', ...v } },
399
+ ],
400
+ },
567
401
  { tag: 'note', elements: [{ tag: 'lark_md', content: t('card.grant.note', undefined, locale) }] },
568
402
  ],
569
403
  };
@@ -736,270 +570,6 @@ function formatDuration(ms) {
736
570
  const d = Math.floor(h / 24);
737
571
  return `${d}d${h % 24}h`;
738
572
  }
739
- function relayPickerTypeTag(mode, locale) {
740
- switch (mode) {
741
- case 'p2p': return t('card.relay.type_p2p', undefined, locale);
742
- case 'topic': return t('card.relay.type_topic', undefined, locale);
743
- default: return t('card.relay.type_group', undefined, locale); // 'group' or undefined
744
- }
745
- }
746
- const RELAY_PICKER_PAGE_SIZE = 5;
747
- const RELAY_SEARCH_FIELD = 'search';
748
- /**
749
- * Match against title / chatLabel / workingDir / cliId. Case-insensitive
750
- * substring. Empty / whitespace query matches everything.
751
- */
752
- function relayPickerFilter(entries, query) {
753
- const q = (query ?? '').trim().toLowerCase();
754
- if (!q)
755
- return entries;
756
- return entries.filter((e) => {
757
- const haystack = [e.title, e.chatLabel, e.workingDir, e.cliId]
758
- .filter(Boolean)
759
- .join(' ')
760
- .toLowerCase();
761
- return haystack.includes(q);
762
- });
763
- }
764
- /**
765
- * Card listing the operator's relayable sessions, paginated 5 per page with
766
- * a search box at the top and a confirm button at the bottom. Layout:
767
- *
768
- * ┌──────────────────────────────────────┐
769
- * │ 📋 选择要接力的会话 │ header
770
- * ├──────────────────────────────────────┤
771
- * │ 🔍 [______________] [搜索] │ form: input + submit button
772
- * ├──────────────────────────────────────┤
773
- * │ [interactive_container 1] │ current page (≤5 cards),
774
- * │ [interactive_container 2] │ each clickable for selection
775
- * │ ... │
776
- * ├──────────────────────────────────────┤
777
- * │ [← 上一页] 1 / 4 [下一页 →] │ paginator row
778
- * ├──────────────────────────────────────┤
779
- * │ [确认接力到本群] │ primary button (only when
780
- * │ │ a selected session is on
781
- * │ │ the current filtered set)
782
- * └──────────────────────────────────────┘
783
- *
784
- * State (search / page / selected) is propagated entirely via the value
785
- * objects on each button and container — Lark cards are stateless, so any
786
- * server-side re-render must reconstruct from what the click sent us.
787
- * That's why every interactive value here carries `search`, `page`,
788
- * `target_chat_id`, `root_id`.
789
- *
790
- * Note: typing into the search box without clicking 搜索 does NOT update
791
- * the in-callback state — container/paginator clicks use whatever search
792
- * was applied at card-render time. To apply a new filter, click 搜索.
793
- */
794
- export function buildRelayPickerCard(entries, targetChatId, targetRootMessageId, invokerOpenId, locale, state) {
795
- const searchQuery = state?.searchQuery ?? '';
796
- const requestedPage = state?.page ?? 0;
797
- const selectedSessionId = state?.selectedSessionId;
798
- const elements = [];
799
- // ─── Filter & paginate ───────────────────────────────────────────────
800
- const filtered = relayPickerFilter(entries, searchQuery);
801
- const totalPages = Math.max(1, Math.ceil(filtered.length / RELAY_PICKER_PAGE_SIZE));
802
- const page = Math.min(Math.max(0, requestedPage), totalPages - 1);
803
- const start = page * RELAY_PICKER_PAGE_SIZE;
804
- const visible = filtered.slice(start, start + RELAY_PICKER_PAGE_SIZE);
805
- // Common state object carried by every interactive value so re-renders
806
- // can reconstruct what the user was looking at. `invoker_open_id` pins the
807
- // card to the user who originally summoned it — card-handler refuses
808
- // re-render / confirm clicks from anyone else, so the menu doesn't get
809
- // silently swapped to a passer-by's session list.
810
- const stateValue = {
811
- target_chat_id: targetChatId,
812
- root_id: targetRootMessageId,
813
- invoker_open_id: invokerOpenId,
814
- search: searchQuery,
815
- page,
816
- selected: selectedSessionId ?? '',
817
- };
818
- // ─── Search box ─────────────────────────────────────────────────────
819
- // v2 input supports `behaviors` natively — pressing Enter or clicking
820
- // the built-in submit icon inside the input fires the callback. No
821
- // separate 搜索 button needed (王皓 reported that button rendered as
822
- // "..." due to cramped column width; the auto-submit input avoids the
823
- // problem entirely AND removes the manual click).
824
- //
825
- // On submit the callback delivers `action.input_value` (the typed
826
- // string) and `action.value` (our state object). card-handler reads
827
- // input_value to update search, resets page to 0 and clears selection.
828
- elements.push({
829
- tag: 'input',
830
- name: RELAY_SEARCH_FIELD,
831
- placeholder: { tag: 'plain_text', content: t('card.relay.search_placeholder', undefined, locale) },
832
- default_value: searchQuery,
833
- width: 'fill',
834
- behaviors: [
835
- {
836
- type: 'callback',
837
- value: { action: 'relay_search', ...stateValue, selected: '' /* new search → reset selection */ },
838
- },
839
- ],
840
- });
841
- elements.push({ tag: 'hr' });
842
- // ─── Empty / no-match notice ────────────────────────────────────────
843
- if (entries.length === 0) {
844
- elements.push({ tag: 'markdown', content: t('card.relay.empty', undefined, locale) });
845
- return JSON.stringify(wrapCard(elements, locale));
846
- }
847
- if (filtered.length === 0) {
848
- elements.push({
849
- tag: 'markdown',
850
- content: t('card.relay.empty_filtered', { query: searchQuery }, locale),
851
- });
852
- return JSON.stringify(wrapCard(elements, locale));
853
- }
854
- // ─── Session cards (current page) ───────────────────────────────────
855
- const p2pLocationLabel = t('card.relay.type_p2p', undefined, locale);
856
- const labelType = t('card.relay.field_type', undefined, locale);
857
- const labelLocation = t('card.relay.field_location', undefined, locale);
858
- const labelTime = t('card.relay.field_time', undefined, locale);
859
- const selectedTag = t('card.relay.selected_tag', undefined, locale);
860
- const hasValidSelection = !!selectedSessionId && filtered.some(e => e.sessionId === selectedSessionId);
861
- visible.forEach((e) => {
862
- const isSelected = e.sessionId === selectedSessionId;
863
- const typeTag = relayPickerTypeTag(e.chatMode, locale);
864
- const locationLine = e.chatMode === 'p2p' ? p2pLocationLabel : e.chatLabel;
865
- const titleLine = isSelected
866
- ? `**✅ ${escapeMd(e.title)}** \`${selectedTag}\``
867
- : `**${escapeMd(e.title)}**`;
868
- const lines = [
869
- titleLine,
870
- `${labelType}: ${typeTag}`,
871
- `${labelLocation}: ${escapeMd(locationLine)}`,
872
- ];
873
- if (e.lastMessageAt) {
874
- lines.push(`${labelTime}: ${formatDuration(Date.now() - e.lastMessageAt)}`);
875
- }
876
- elements.push({
877
- tag: 'interactive_container',
878
- width: 'fill',
879
- padding: '8px 12px',
880
- background_style: isSelected ? 'laser' : 'default',
881
- has_border: true,
882
- border_color: isSelected ? 'blue-500' : 'grey-200',
883
- corner_radius: '8px',
884
- behaviors: [
885
- {
886
- type: 'callback',
887
- value: { action: 'relay_select', session_id: e.sessionId, ...stateValue },
888
- },
889
- ],
890
- elements: [{ tag: 'markdown', content: lines.join('\n') }],
891
- });
892
- });
893
- // ─── Paginator (only when more than one page) ───────────────────────
894
- if (totalPages > 1) {
895
- elements.push({
896
- tag: 'column_set',
897
- flex_mode: 'none',
898
- horizontal_spacing: 'default',
899
- columns: [
900
- {
901
- tag: 'column',
902
- width: 'weighted',
903
- weight: 1,
904
- vertical_align: 'center',
905
- elements: [
906
- {
907
- tag: 'button',
908
- text: { tag: 'plain_text', content: t('card.relay.btn_prev_page', undefined, locale) },
909
- type: 'default',
910
- disabled: page === 0,
911
- behaviors: [
912
- {
913
- type: 'callback',
914
- value: { action: 'relay_page', ...stateValue, page: Math.max(0, page - 1) },
915
- },
916
- ],
917
- },
918
- ],
919
- },
920
- {
921
- tag: 'column',
922
- width: 'weighted',
923
- weight: 2,
924
- vertical_align: 'center',
925
- elements: [
926
- {
927
- tag: 'markdown',
928
- text_align: 'center',
929
- content: t('card.relay.page_indicator', { current: page + 1, total: totalPages }, locale),
930
- },
931
- ],
932
- },
933
- {
934
- tag: 'column',
935
- width: 'weighted',
936
- weight: 1,
937
- vertical_align: 'center',
938
- elements: [
939
- {
940
- tag: 'button',
941
- text: { tag: 'plain_text', content: t('card.relay.btn_next_page', undefined, locale) },
942
- type: 'default',
943
- disabled: page === totalPages - 1,
944
- behaviors: [
945
- {
946
- type: 'callback',
947
- value: { action: 'relay_page', ...stateValue, page: Math.min(totalPages - 1, page + 1) },
948
- },
949
- ],
950
- },
951
- ],
952
- },
953
- ],
954
- });
955
- }
956
- // ─── Confirm button or hint ─────────────────────────────────────────
957
- elements.push({ tag: 'hr' });
958
- if (hasValidSelection) {
959
- elements.push({
960
- tag: 'column_set',
961
- flex_mode: 'none',
962
- columns: [
963
- {
964
- tag: 'column',
965
- width: 'weighted',
966
- weight: 1,
967
- elements: [
968
- {
969
- tag: 'button',
970
- text: { tag: 'plain_text', content: t('card.relay.btn_confirm', undefined, locale) },
971
- type: 'primary',
972
- behaviors: [
973
- {
974
- type: 'callback',
975
- value: { action: 'relay_confirm', session_id: selectedSessionId, ...stateValue },
976
- },
977
- ],
978
- },
979
- ],
980
- },
981
- ],
982
- });
983
- }
984
- else {
985
- elements.push({
986
- tag: 'markdown',
987
- content: `<font color='grey'>${t('card.relay.hint_pick_first', undefined, locale)}</font>`,
988
- });
989
- }
990
- return JSON.stringify(wrapCard(elements, locale));
991
- }
992
- function wrapCard(elements, locale) {
993
- return {
994
- schema: '2.0',
995
- config: { update_multi: true },
996
- header: {
997
- title: { tag: 'plain_text', content: t('card.relay.title', undefined, locale) },
998
- template: 'blue',
999
- },
1000
- body: { direction: 'vertical', elements },
1001
- };
1002
- }
1003
573
  export function buildAdoptSelectCard(sessions, rootMessageId, locale) {
1004
574
  const unknownUptime = t('card.adopt.uptime_unknown', undefined, locale);
1005
575
  const options = sessions.map((s) => {