@xopcai/xopc 0.0.85 → 0.0.87

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 (407) hide show
  1. package/dist/browser-ext/manifest.json +1 -1
  2. package/dist/extensions/feishu/src/adapters/cli-login.js +3 -3
  3. package/dist/extensions/feishu/src/adapters/cli-login.js.map +1 -1
  4. package/dist/extensions/telegram/src/delivery-chat-id.d.ts +1 -1
  5. package/dist/extensions/telegram/src/delivery-chat-id.js +1 -1
  6. package/dist/extensions/telegram/src/delivery-chat-id.js.map +1 -1
  7. package/dist/extensions/telegram/src/routing-integration.js +1 -0
  8. package/dist/extensions/telegram/src/routing-integration.js.map +1 -1
  9. package/dist/extensions/telegram/xopc.extension.json +1 -1
  10. package/dist/extensions/weixin/src/__tests__/workflow-progress.test.js +2 -2
  11. package/dist/extensions/weixin/src/__tests__/workflow-progress.test.js.map +1 -1
  12. package/dist/extensions/weixin/src/api/api.js +2 -2
  13. package/dist/extensions/weixin/src/api/api.js.map +1 -1
  14. package/dist/extensions/weixin/src/auth/accounts.js +12 -12
  15. package/dist/extensions/weixin/src/auth/accounts.js.map +1 -1
  16. package/dist/extensions/weixin/src/delivery-to.js +2 -2
  17. package/dist/extensions/weixin/src/delivery-to.js.map +1 -1
  18. package/dist/extensions/weixin/src/messaging/debug-mode.js +5 -5
  19. package/dist/extensions/weixin/src/messaging/debug-mode.js.map +1 -1
  20. package/dist/extensions/weixin/src/messaging/inbound.js +11 -11
  21. package/dist/extensions/weixin/src/messaging/inbound.js.map +1 -1
  22. package/dist/extensions/weixin/src/storage/sync-buf.js +4 -4
  23. package/dist/extensions/weixin/src/storage/sync-buf.js.map +1 -1
  24. package/dist/extensions/weixin/src/workflow-progress.d.ts +1 -1
  25. package/dist/extensions/weixin/src/workflow-progress.js.map +1 -1
  26. package/dist/gateway/static/root/assets/agents-BEAbXpuP.js +222 -0
  27. package/dist/gateway/static/root/assets/{apps-page-D7v7649T.js → apps-page-Dg8R-Szf.js} +1 -1
  28. package/dist/gateway/static/root/assets/{channels-settings-nCaMb0a7.js → channels-settings-yohw9YSu.js} +1 -1
  29. package/dist/gateway/static/root/assets/{channels-status-swr-C1gZBcJV.js → channels-status-swr-BSHqqCF1.js} +1 -1
  30. package/dist/gateway/static/root/assets/{cron-api-CoYK0hlm.js → cron-api-0h_QT8U3.js} +1 -1
  31. package/dist/gateway/static/root/assets/{cron-page-DeGo-Vjc.js → cron-page-BkfKFfFk.js} +1 -1
  32. package/dist/gateway/static/root/assets/{dist-DaK4dsss.js → dist-Cmjp2APP.js} +1 -1
  33. package/dist/gateway/static/root/assets/{extension-debug-page-BZngZWbO.js → extension-debug-page-CFa9z_1N.js} +1 -1
  34. package/dist/gateway/static/root/assets/{extension-page-D6JSyV27.js → extension-page-BI8eaTPq.js} +1 -1
  35. package/dist/gateway/static/root/assets/extension-settings-page-x4BB7q1X.js +1 -0
  36. package/dist/gateway/static/root/assets/{fetch-B2MYHbWg.js → fetch-DRqwef_Q.js} +1 -1
  37. package/dist/gateway/static/root/assets/{field-primitives-Zzl22MvN.js → field-primitives-BiNHBo2Y.js} +1 -1
  38. package/dist/gateway/static/root/assets/{heartbeat-config-api-BtIcpG0O.js → heartbeat-config-api-ZRb8qhuz.js} +1 -1
  39. package/dist/gateway/static/root/assets/{index-D4vM3-P7.js → index-Cu7bKuUi.js} +96 -94
  40. package/dist/gateway/static/root/assets/index-a5gWIdZQ.css +1 -0
  41. package/dist/gateway/static/root/assets/{logs-page-_d4UJ-qQ.js → logs-page-BFZ8GgCv.js} +1 -1
  42. package/dist/gateway/static/root/assets/{sessions-page-5N4aF2Wk.js → sessions-page-CD7AfB-2.js} +1 -1
  43. package/dist/gateway/static/root/assets/settings-form-section-DiqqVs6m.js +1 -0
  44. package/dist/gateway/static/root/assets/settings-page-BBOjEQW3.js +3 -0
  45. package/dist/gateway/static/root/assets/{share-preview-page-D4EG_vM1.js → share-preview-page-n1Gprylk.js} +1 -1
  46. package/dist/gateway/static/root/assets/{skills-page-sPAXhh8w.js → skills-page-CcN_gj--.js} +1 -1
  47. package/dist/gateway/static/root/assets/{theme-store-DryYl3qD.js → theme-store-CZOh1nT3.js} +1 -1
  48. package/dist/gateway/static/root/assets/url-Dd8Q7kZZ.js +3 -0
  49. package/dist/gateway/static/root/assets/{utils-CYO9eTCM.js → utils-CkWBfxs4.js} +1 -1
  50. package/dist/gateway/static/root/assets/{voice-api-key-field-Ds51havm.js → voice-api-key-field-O6awz9hi.js} +1 -1
  51. package/dist/gateway/static/root/index.html +5 -5
  52. package/dist/package.js +1 -1
  53. package/dist/src/agent/agent-scope.d.ts +4 -0
  54. package/dist/src/agent/agent-scope.js +53 -10
  55. package/dist/src/agent/agent-scope.js.map +1 -1
  56. package/dist/src/agent/bootstrap/filter-bootstrap-files.js +2 -1
  57. package/dist/src/agent/bootstrap/filter-bootstrap-files.js.map +1 -1
  58. package/dist/src/agent/embedded/session-tool-result-guard.js +2 -1
  59. package/dist/src/agent/embedded/session-tool-result-guard.js.map +1 -1
  60. package/dist/src/agent/embedded/tool-result-truncation.js +2 -1
  61. package/dist/src/agent/embedded/tool-result-truncation.js.map +1 -1
  62. package/dist/src/agent/fallback/candidates.js +2 -2
  63. package/dist/src/agent/fallback/candidates.js.map +1 -1
  64. package/dist/src/agent/goals/persistent-goal-apis.d.ts +0 -2
  65. package/dist/src/agent/goals/persistent-goal-service.js +0 -1
  66. package/dist/src/agent/goals/persistent-goal-service.js.map +1 -1
  67. package/dist/src/agent/image/generation/normalization.js +2 -12
  68. package/dist/src/agent/image/generation/normalization.js.map +1 -1
  69. package/dist/src/agent/image/generation/provider-registry.d.ts +4 -8
  70. package/dist/src/agent/image/generation/provider-registry.js.map +1 -1
  71. package/dist/src/agent/image/generation/runtime.d.ts +2 -2
  72. package/dist/src/agent/image/generation/runtime.js.map +1 -1
  73. package/dist/src/agent/image/generation/types.d.ts +0 -18
  74. package/dist/src/agent/image/image-helpers.js +6 -1
  75. package/dist/src/agent/image/image-helpers.js.map +1 -1
  76. package/dist/src/agent/image/index.d.ts +1 -1
  77. package/dist/src/agent/inbound/inbound-loop.d.ts +5 -0
  78. package/dist/src/agent/inbound/inbound-loop.js +41 -10
  79. package/dist/src/agent/inbound/inbound-loop.js.map +1 -1
  80. package/dist/src/agent/inbound/turn-dispatcher.d.ts +4 -0
  81. package/dist/src/agent/inbound/turn-dispatcher.js +6 -4
  82. package/dist/src/agent/inbound/turn-dispatcher.js.map +1 -1
  83. package/dist/src/agent/mcp/bundle-mcp-materialize.js +2 -1
  84. package/dist/src/agent/mcp/bundle-mcp-materialize.js.map +1 -1
  85. package/dist/src/agent/mcp/bundle-mcp-names.js +2 -1
  86. package/dist/src/agent/mcp/bundle-mcp-names.js.map +1 -1
  87. package/dist/src/agent/mcp/bundle-mcp-runtime.js +2 -1
  88. package/dist/src/agent/mcp/bundle-mcp-runtime.js.map +1 -1
  89. package/dist/src/agent/mcp/mcp-transport-config.js +2 -1
  90. package/dist/src/agent/mcp/mcp-transport-config.js.map +1 -1
  91. package/dist/src/agent/mcp/mcp-transport.js +2 -1
  92. package/dist/src/agent/mcp/mcp-transport.js.map +1 -1
  93. package/dist/src/agent/media-generation/runtime-shared.js +2 -9
  94. package/dist/src/agent/media-generation/runtime-shared.js.map +1 -1
  95. package/dist/src/agent/messaging/command-handler.d.ts +6 -0
  96. package/dist/src/agent/messaging/command-handler.js +5 -0
  97. package/dist/src/agent/messaging/command-handler.js.map +1 -1
  98. package/dist/src/agent/prompt/safety.d.ts +0 -7
  99. package/dist/src/agent/prompt/safety.js +1 -20
  100. package/dist/src/agent/prompt/safety.js.map +1 -1
  101. package/dist/src/agent/service/build-direct-message-content.js +1 -1
  102. package/dist/src/agent/service/build-direct-message-content.js.map +1 -1
  103. package/dist/src/agent/service/direct-turn-helpers.d.ts +3 -1
  104. package/dist/src/agent/service/direct-turn-helpers.js +6 -1
  105. package/dist/src/agent/service/direct-turn-helpers.js.map +1 -1
  106. package/dist/src/agent/service/process-direct-one-shot.d.ts +4 -0
  107. package/dist/src/agent/service/process-direct-one-shot.js +15 -2
  108. package/dist/src/agent/service/process-direct-one-shot.js.map +1 -1
  109. package/dist/src/agent/service/process-direct-streaming.d.ts +4 -0
  110. package/dist/src/agent/service/process-direct-streaming.js +34 -4
  111. package/dist/src/agent/service/process-direct-streaming.js.map +1 -1
  112. package/dist/src/agent/service/webchat-tts.js +1 -1
  113. package/dist/src/agent/service/webchat-tts.js.map +1 -1
  114. package/dist/src/agent/service.d.ts +8 -0
  115. package/dist/src/agent/service.js +21 -1
  116. package/dist/src/agent/service.js.map +1 -1
  117. package/dist/src/agent/tools/create-share-tool.js +27 -20
  118. package/dist/src/agent/tools/create-share-tool.js.map +1 -1
  119. package/dist/src/agent/tools/factory.js +1 -1
  120. package/dist/src/agent/tools/index.d.ts +0 -1
  121. package/dist/src/agent/tools/index.js +4 -5
  122. package/dist/src/agent/tools/shell.js +0 -13
  123. package/dist/src/agent/tools/shell.js.map +1 -1
  124. package/dist/src/agent/tools/workflow-tool.js +10 -4
  125. package/dist/src/agent/tools/workflow-tool.js.map +1 -1
  126. package/dist/src/agent/workflow/builtins/audit-repo.d.ts +5 -1
  127. package/dist/src/agent/workflow/builtins/audit-repo.js +52 -11
  128. package/dist/src/agent/workflow/builtins/audit-repo.js.map +1 -1
  129. package/dist/src/agent/workflow/builtins/debug-incident.d.ts +13 -0
  130. package/dist/src/agent/workflow/builtins/debug-incident.js +155 -0
  131. package/dist/src/agent/workflow/builtins/debug-incident.js.map +1 -0
  132. package/dist/src/agent/workflow/builtins/index.d.ts +3 -1
  133. package/dist/src/agent/workflow/builtins/index.js +11 -1
  134. package/dist/src/agent/workflow/builtins/index.js.map +1 -1
  135. package/dist/src/agent/workflow/builtins/multi-perspective-review.d.ts +6 -1
  136. package/dist/src/agent/workflow/builtins/multi-perspective-review.js +66 -30
  137. package/dist/src/agent/workflow/builtins/multi-perspective-review.js.map +1 -1
  138. package/dist/src/agent/workflow/builtins/pr-review.d.ts +12 -0
  139. package/dist/src/agent/workflow/builtins/pr-review.js +156 -0
  140. package/dist/src/agent/workflow/builtins/pr-review.js.map +1 -0
  141. package/dist/src/agent/workflow/builtins/research.d.ts +5 -1
  142. package/dist/src/agent/workflow/builtins/research.js +37 -6
  143. package/dist/src/agent/workflow/builtins/research.js.map +1 -1
  144. package/dist/src/agent/workflow/catalog.d.ts +5 -0
  145. package/dist/src/agent/workflow/catalog.js +6 -2
  146. package/dist/src/agent/workflow/catalog.js.map +1 -1
  147. package/dist/src/agent/workflow/channel-capability.d.ts +3 -3
  148. package/dist/src/agent/workflow/index.d.ts +1 -1
  149. package/dist/src/agent/workflow/lint.d.ts +38 -0
  150. package/dist/src/agent/workflow/lint.js +74 -0
  151. package/dist/src/agent/workflow/lint.js.map +1 -0
  152. package/dist/src/agent/workflow/parser.js +13 -1
  153. package/dist/src/agent/workflow/parser.js.map +1 -1
  154. package/dist/src/agent/workflow/runtime.d.ts +3 -0
  155. package/dist/src/agent/workflow/runtime.js +76 -3
  156. package/dist/src/agent/workflow/runtime.js.map +1 -1
  157. package/dist/src/agent/workflow/types.d.ts +11 -1
  158. package/dist/src/browser/index.js +4 -4
  159. package/dist/src/browser/manager.d.ts +1 -3
  160. package/dist/src/browser/manager.js +0 -6
  161. package/dist/src/browser/manager.js.map +1 -1
  162. package/dist/src/browser/providers/browser-ext-install.d.ts +4 -4
  163. package/dist/src/browser/providers/browser-ext-install.js +38 -85
  164. package/dist/src/browser/providers/browser-ext-install.js.map +1 -1
  165. package/dist/src/browser/providers/cloakbrowser.d.ts +0 -5
  166. package/dist/src/browser/providers/cloakbrowser.js +2 -55
  167. package/dist/src/browser/providers/cloakbrowser.js.map +1 -1
  168. package/dist/src/channels/attachments/voice-stt-webchat.js +10 -8
  169. package/dist/src/channels/attachments/voice-stt-webchat.js.map +1 -1
  170. package/dist/src/channels/pairing/allow-from-file.js +9 -9
  171. package/dist/src/channels/pairing/allow-from-file.js.map +1 -1
  172. package/dist/src/channels/pairing/pairing-store.js +6 -6
  173. package/dist/src/channels/pairing/pairing-store.js.map +1 -1
  174. package/dist/src/chat-commands/builtins/session.js +1 -1
  175. package/dist/src/chat-commands/builtins/session.js.map +1 -1
  176. package/dist/src/chat-commands/builtins/tts.js +2 -2
  177. package/dist/src/chat-commands/builtins/tts.js.map +1 -1
  178. package/dist/src/chat-commands/builtins/workflow.js +7 -2
  179. package/dist/src/chat-commands/builtins/workflow.js.map +1 -1
  180. package/dist/src/chat-commands/context.d.ts +3 -0
  181. package/dist/src/chat-commands/context.js +21 -3
  182. package/dist/src/chat-commands/context.js.map +1 -1
  183. package/dist/src/chat-commands/session-key.d.ts +4 -37
  184. package/dist/src/chat-commands/session-key.js +49 -85
  185. package/dist/src/chat-commands/session-key.js.map +1 -1
  186. package/dist/src/chat-commands/types.d.ts +2 -0
  187. package/dist/src/cli/commands/agent/interactive.js +2 -2
  188. package/dist/src/cli/commands/agent/interactive.js.map +1 -1
  189. package/dist/src/cli/commands/agent/sessions.js +2 -2
  190. package/dist/src/cli/commands/agent/sessions.js.map +1 -1
  191. package/dist/src/cli/commands/agent.js +4 -5
  192. package/dist/src/cli/commands/agent.js.map +1 -1
  193. package/dist/src/cli/commands/channels.js +1 -5
  194. package/dist/src/cli/commands/channels.js.map +1 -1
  195. package/dist/src/cli/commands/gateway/lifecycle-core.js +1 -1
  196. package/dist/src/cli/commands/gateway/lifecycle-core.js.map +1 -1
  197. package/dist/src/cli/commands/gateway/logs.d.ts +9 -0
  198. package/dist/src/cli/commands/gateway/logs.js +50 -17
  199. package/dist/src/cli/commands/gateway/logs.js.map +1 -1
  200. package/dist/src/cli/commands/image.js +22 -21
  201. package/dist/src/cli/commands/image.js.map +1 -1
  202. package/dist/src/cli/commands/session/utils.js +2 -2
  203. package/dist/src/cli/commands/session/utils.js.map +1 -1
  204. package/dist/src/cli/commands/update.js +26 -46
  205. package/dist/src/cli/commands/update.js.map +1 -1
  206. package/dist/src/cli/utils/session.d.ts +0 -5
  207. package/dist/src/cli/utils/session.js +1 -6
  208. package/dist/src/cli/utils/session.js.map +1 -1
  209. package/dist/src/commands/agents.config.js +1 -1
  210. package/dist/src/commands/agents.config.js.map +1 -1
  211. package/dist/src/config/agent-profile.js +5 -27
  212. package/dist/src/config/agent-profile.js.map +1 -1
  213. package/dist/src/config/index.js +2 -2
  214. package/dist/src/config/model-input.js +2 -5
  215. package/dist/src/config/model-input.js.map +1 -1
  216. package/dist/src/config/schema.d.ts +201 -217
  217. package/dist/src/config/schema.js +54 -39
  218. package/dist/src/config/schema.js.map +1 -1
  219. package/dist/src/config/workspace-path-helpers.d.ts +1 -2
  220. package/dist/src/config/workspace-path-helpers.js.map +1 -1
  221. package/dist/src/daemon/install-plan.js +25 -1
  222. package/dist/src/daemon/install-plan.js.map +1 -1
  223. package/dist/src/daemon/launchd.d.ts +8 -0
  224. package/dist/src/daemon/launchd.js +5 -12
  225. package/dist/src/daemon/launchd.js.map +1 -1
  226. package/dist/src/daemon/schtasks.d.ts +25 -0
  227. package/dist/src/daemon/schtasks.js +166 -46
  228. package/dist/src/daemon/schtasks.js.map +1 -1
  229. package/dist/src/daemon/service.js +5 -4
  230. package/dist/src/daemon/service.js.map +1 -1
  231. package/dist/src/daemon/systemd.d.ts +6 -0
  232. package/dist/src/daemon/systemd.js +18 -3
  233. package/dist/src/daemon/systemd.js.map +1 -1
  234. package/dist/src/extensions/activation-context.js +0 -1
  235. package/dist/src/extensions/activation-context.js.map +1 -1
  236. package/dist/src/extensions/normalize-manifest.js +0 -1
  237. package/dist/src/extensions/normalize-manifest.js.map +1 -1
  238. package/dist/src/extensions/types/manifest.d.ts +0 -2
  239. package/dist/src/gateway/agent-builtin-tools.d.ts +1 -1
  240. package/dist/src/gateway/agent-builtin-tools.js +1 -0
  241. package/dist/src/gateway/agent-builtin-tools.js.map +1 -1
  242. package/dist/src/gateway/agents-admin.js +10 -2
  243. package/dist/src/gateway/agents-admin.js.map +1 -1
  244. package/dist/src/gateway/heartbeat/service.js +1 -1
  245. package/dist/src/gateway/heartbeat/service.js.map +1 -1
  246. package/dist/src/gateway/hono/app.js +1 -1
  247. package/dist/src/gateway/hono/lib/agent-model.d.ts +18 -10
  248. package/dist/src/gateway/hono/lib/agent-model.js +24 -35
  249. package/dist/src/gateway/hono/lib/agent-model.js.map +1 -1
  250. package/dist/src/gateway/hono/lib/config-payload.js +1 -1
  251. package/dist/src/gateway/hono/lib/config-payload.js.map +1 -1
  252. package/dist/src/gateway/hono/lib/safe-voice-config.js +14 -53
  253. package/dist/src/gateway/hono/lib/safe-voice-config.js.map +1 -1
  254. package/dist/src/gateway/hono/routes/config-patch/agents.js +17 -5
  255. package/dist/src/gateway/hono/routes/config-patch/agents.js.map +1 -1
  256. package/dist/src/gateway/hono/routes/config-patch/channels.js +0 -11
  257. package/dist/src/gateway/hono/routes/config-patch/channels.js.map +1 -1
  258. package/dist/src/gateway/hono/routes/goals.js +1 -1
  259. package/dist/src/gateway/hono/routes/goals.js.map +1 -1
  260. package/dist/src/gateway/hono/routes/sessions.js +28 -7
  261. package/dist/src/gateway/hono/routes/sessions.js.map +1 -1
  262. package/dist/src/gateway/hono/routes/shares.js +14 -12
  263. package/dist/src/gateway/hono/routes/shares.js.map +1 -1
  264. package/dist/src/gateway/hono/routes/tunnel.js +1 -1
  265. package/dist/src/gateway/hono/routes/update.js +4 -2
  266. package/dist/src/gateway/hono/routes/update.js.map +1 -1
  267. package/dist/src/gateway/hono/sse.js +16 -33
  268. package/dist/src/gateway/hono/sse.js.map +1 -1
  269. package/dist/src/gateway/lock.js +10 -10
  270. package/dist/src/gateway/lock.js.map +1 -1
  271. package/dist/src/gateway/ports.js +6 -6
  272. package/dist/src/gateway/ports.js.map +1 -1
  273. package/dist/src/gateway/resolve-webchat-session-key.d.ts +19 -0
  274. package/dist/src/gateway/resolve-webchat-session-key.js +46 -0
  275. package/dist/src/gateway/resolve-webchat-session-key.js.map +1 -0
  276. package/dist/src/gateway/service/run-gateway-agent.js +27 -11
  277. package/dist/src/gateway/service/run-gateway-agent.js.map +1 -1
  278. package/dist/src/gateway/service/sessions-api.d.ts +3 -0
  279. package/dist/src/gateway/service/sessions-api.js +8 -0
  280. package/dist/src/gateway/service/sessions-api.js.map +1 -1
  281. package/dist/src/gateway/service.d.ts +0 -2
  282. package/dist/src/gateway/service.js +2 -7
  283. package/dist/src/gateway/service.js.map +1 -1
  284. package/dist/src/gateway/session-reset-service.d.ts +20 -0
  285. package/dist/src/gateway/session-reset-service.js +54 -0
  286. package/dist/src/gateway/session-reset-service.js.map +1 -0
  287. package/dist/src/gateway/startup-readiness.d.ts +1 -1
  288. package/dist/src/gateway/startup-readiness.js +1 -0
  289. package/dist/src/gateway/startup-readiness.js.map +1 -1
  290. package/dist/src/infra/gateway-processes.js +2 -2
  291. package/dist/src/infra/gateway-processes.js.map +1 -1
  292. package/dist/src/infra/run-command.d.ts +16 -0
  293. package/dist/src/infra/run-command.js +67 -0
  294. package/dist/src/infra/run-command.js.map +1 -0
  295. package/dist/src/infra/update-global.d.ts +45 -0
  296. package/dist/src/infra/update-global.js +224 -0
  297. package/dist/src/infra/update-global.js.map +1 -0
  298. package/dist/src/mcp/channel-bridge.js +1 -1
  299. package/dist/src/mcp/channel-shared.js +2 -1
  300. package/dist/src/mcp/channel-shared.js.map +1 -1
  301. package/dist/src/providers/auth-runtime/auth-profile-store.js +1 -1
  302. package/dist/src/providers/auth-runtime/auth-profile-store.js.map +1 -1
  303. package/dist/src/providers/auth-runtime/resolve-auth.js +1 -12
  304. package/dist/src/providers/auth-runtime/resolve-auth.js.map +1 -1
  305. package/dist/src/providers/auth-runtime/types.d.ts +6 -12
  306. package/dist/src/routing/agent-session-key.d.ts +58 -0
  307. package/dist/src/routing/agent-session-key.js +164 -0
  308. package/dist/src/routing/agent-session-key.js.map +1 -0
  309. package/dist/src/routing/index.d.ts +1 -1
  310. package/dist/src/routing/index.js +4 -2
  311. package/dist/src/routing/index.js.map +1 -1
  312. package/dist/src/routing/resolve-route.d.ts +15 -0
  313. package/dist/src/routing/resolve-route.js +41 -20
  314. package/dist/src/routing/resolve-route.js.map +1 -1
  315. package/dist/src/routing/resolve-tui-session-key.d.ts +25 -0
  316. package/dist/src/routing/resolve-tui-session-key.js +54 -0
  317. package/dist/src/routing/resolve-tui-session-key.js.map +1 -0
  318. package/dist/src/routing/session-key-utils.d.ts +24 -0
  319. package/dist/src/routing/session-key-utils.js +92 -0
  320. package/dist/src/routing/session-key-utils.js.map +1 -0
  321. package/dist/src/routing/session-key.d.ts +19 -49
  322. package/dist/src/routing/session-key.js +143 -116
  323. package/dist/src/routing/session-key.js.map +1 -1
  324. package/dist/src/session/index.d.ts +6 -0
  325. package/dist/src/session/index.js +7 -1
  326. package/dist/src/session/init-session-turn.d.ts +30 -0
  327. package/dist/src/session/init-session-turn.js +102 -0
  328. package/dist/src/session/init-session-turn.js.map +1 -0
  329. package/dist/src/session/lifecycle-timestamps.d.ts +8 -0
  330. package/dist/src/session/lifecycle-timestamps.js +16 -0
  331. package/dist/src/session/lifecycle-timestamps.js.map +1 -0
  332. package/dist/src/session/manager.d.ts +7 -1
  333. package/dist/src/session/manager.js +8 -1
  334. package/dist/src/session/manager.js.map +1 -1
  335. package/dist/src/session/parity/transcript-paths.js +2 -2
  336. package/dist/src/session/parity/transcript-paths.js.map +1 -1
  337. package/dist/src/session/parity/xopc-session-disk-entry.d.ts +6 -0
  338. package/dist/src/session/reset-policy.d.ts +32 -0
  339. package/dist/src/session/reset-policy.js +65 -0
  340. package/dist/src/session/reset-policy.js.map +1 -0
  341. package/dist/src/session/reset-triggers.d.ts +20 -0
  342. package/dist/src/session/reset-triggers.js +63 -0
  343. package/dist/src/session/reset-triggers.js.map +1 -0
  344. package/dist/src/session/reset-type.d.ts +12 -0
  345. package/dist/src/session/reset-type.js +25 -0
  346. package/dist/src/session/reset-type.js.map +1 -0
  347. package/dist/src/session/resolve-session.d.ts +30 -0
  348. package/dist/src/session/resolve-session.js +93 -0
  349. package/dist/src/session/resolve-session.js.map +1 -0
  350. package/dist/src/session/session-title.js +3 -2
  351. package/dist/src/session/session-title.js.map +1 -1
  352. package/dist/src/session/store.d.ts +11 -4
  353. package/dist/src/session/store.js +57 -6
  354. package/dist/src/session/store.js.map +1 -1
  355. package/dist/src/session/transcript-events.js +2 -1
  356. package/dist/src/session/transcript-events.js.map +1 -1
  357. package/dist/src/share/share-url.d.ts +33 -0
  358. package/dist/src/share/share-url.js +56 -14
  359. package/dist/src/share/share-url.js.map +1 -1
  360. package/dist/src/tui/backends/embedded-backend.js +4 -9
  361. package/dist/src/tui/backends/embedded-backend.js.map +1 -1
  362. package/dist/src/tui/backends/gateway-sse-backend.js +1 -1
  363. package/dist/src/tui/backends/gateway-sse-backend.js.map +1 -1
  364. package/dist/src/tui/components/chat-log.js +3 -3
  365. package/dist/src/tui/components/chat-log.js.map +1 -1
  366. package/dist/src/tui/theme.d.ts +0 -2
  367. package/dist/src/tui/theme.js +1 -3
  368. package/dist/src/tui/theme.js.map +1 -1
  369. package/dist/src/tui/tui-commands.d.ts +3 -0
  370. package/dist/src/tui/tui-commands.js +45 -10
  371. package/dist/src/tui/tui-commands.js.map +1 -1
  372. package/dist/src/tui/tui-keybindings-file.js +1 -21
  373. package/dist/src/tui/tui-keybindings-file.js.map +1 -1
  374. package/dist/src/tui/tui-session-actions.d.ts +28 -0
  375. package/dist/src/tui/tui-session-actions.js +88 -0
  376. package/dist/src/tui/tui-session-actions.js.map +1 -0
  377. package/dist/src/tui/tui.js +52 -47
  378. package/dist/src/tui/tui.js.map +1 -1
  379. package/dist/src/utils/string-coerce.d.ts +2 -0
  380. package/dist/src/utils/string-coerce.js +10 -1
  381. package/dist/src/utils/string-coerce.js.map +1 -1
  382. package/dist/src/voice/stt/config-slice.d.ts +2 -5
  383. package/dist/src/voice/stt/config-slice.js +5 -26
  384. package/dist/src/voice/stt/config-slice.js.map +1 -1
  385. package/dist/src/voice/stt/types.d.ts +1 -18
  386. package/dist/src/voice/stt/types.js +4 -2
  387. package/dist/src/voice/stt/types.js.map +1 -1
  388. package/dist/src/voice/tts/config-slice.d.ts +3 -7
  389. package/dist/src/voice/tts/config-slice.js +7 -38
  390. package/dist/src/voice/tts/config-slice.js.map +1 -1
  391. package/dist/src/voice/tts/merge-config.js +2 -48
  392. package/dist/src/voice/tts/merge-config.js.map +1 -1
  393. package/dist/src/voice/tts/providers/alibaba-speech.js +1 -1
  394. package/dist/src/voice/tts/providers/alibaba-speech.js.map +1 -1
  395. package/dist/src/voice/tts/types.d.ts +1 -29
  396. package/dist/src/voice/tts/types.js +19 -17
  397. package/dist/src/voice/tts/types.js.map +1 -1
  398. package/package.json +1 -4
  399. package/dist/gateway/static/root/assets/agents-D3_-kNlZ.js +0 -222
  400. package/dist/gateway/static/root/assets/extension-settings-page-8PZcmWI7.js +0 -1
  401. package/dist/gateway/static/root/assets/index-ew_2L2We.css +0 -1
  402. package/dist/gateway/static/root/assets/settings-form-section-D_tgb8r2.js +0 -1
  403. package/dist/gateway/static/root/assets/settings-page-C18xBt4X.js +0 -3
  404. package/dist/gateway/static/root/assets/url-BwNL6Rgk.js +0 -3
  405. package/dist/src/agent/tools/browser-legacy-tools.d.ts +0 -17
  406. package/dist/src/agent/tools/browser-legacy-tools.js +0 -766
  407. package/dist/src/agent/tools/browser-legacy-tools.js.map +0 -1
@@ -1,95 +1,69 @@
1
- import { buildSessionKey, init_session_key, parseSessionKey as parseSessionKey$1 } from "../routing/session-key.js";
1
+ import { buildAgentMainSessionKey, buildAgentPeerSessionKey } from "../routing/agent-session-key.js";
2
+ import { init_session_key, parseSessionKey as parseSessionKey$1 } from "../routing/session-key.js";
2
3
  //#region src/chat-commands/session-key.ts
3
4
  init_session_key();
4
- /**
5
- * Generate a unified session key using the new routing format
6
- *
7
- * Format: {agentId}:{source}:{accountId}:{peerKind}:{peerId}[:thread:{threadId}]
8
- *
9
- * Examples:
10
- * - telegram DM: main:telegram:default:dm:123456
11
- * - telegram Group: main:telegram:default:group:-100123456
12
- * - gateway: main:gateway:default:direct:chat_abc123
13
- * - cli: main:cli:default:direct:cli
14
- */
15
5
  function generateSessionKey(ctx) {
16
- const { source, chatId, senderId, isGroup, threadId, agentId, accountId } = ctx;
17
- const effectiveAgentId = agentId ?? "main";
18
- const effectiveAccountId = accountId ?? "default";
19
- if (source === "cli") return buildSessionKey({
20
- agentId: effectiveAgentId,
21
- source: "cli",
22
- accountId: effectiveAccountId,
23
- peerKind: "direct",
24
- peerId: chatId === "direct" ? "cli" : chatId
25
- });
26
- if (source === "webui" || source === "gateway") return buildSessionKey({
27
- agentId: effectiveAgentId,
28
- source: source === "webui" ? "gateway" : source,
29
- accountId: effectiveAccountId,
30
- peerKind: "direct",
31
- peerId: chatId
32
- });
33
- if (source === "api") return buildSessionKey({
34
- agentId: effectiveAgentId,
35
- source: "api",
36
- accountId: effectiveAccountId,
37
- peerKind: "direct",
38
- peerId: chatId
39
- });
40
- if (source === "system") return buildSessionKey({
41
- agentId: effectiveAgentId,
42
- source: "system",
43
- accountId: effectiveAccountId,
44
- peerKind: "direct",
45
- peerId: chatId
46
- });
47
- if (!isGroup) return buildSessionKey({
48
- agentId: effectiveAgentId,
49
- source,
50
- accountId: effectiveAccountId,
51
- peerKind: "dm",
52
- peerId: senderId,
53
- threadId
54
- });
55
- return buildSessionKey({
6
+ const effectiveAgentId = ctx.agentId ?? "main";
7
+ const effectiveAccountId = ctx.accountId ?? "default";
8
+ const channel = ctx.source === "webui" ? "webchat" : ctx.source;
9
+ if (ctx.source === "cli") {
10
+ if (ctx.chatId === "direct" || ctx.chatId === "main") return buildAgentMainSessionKey({
11
+ agentId: effectiveAgentId,
12
+ mainKey: ctx.mainKey
13
+ });
14
+ return buildAgentPeerSessionKey({
15
+ agentId: effectiveAgentId,
16
+ mainKey: ctx.mainKey,
17
+ channel: "cli",
18
+ accountId: effectiveAccountId,
19
+ peerKind: "direct",
20
+ peerId: ctx.chatId,
21
+ dmScope: "per-peer"
22
+ });
23
+ }
24
+ if (!ctx.isGroup) {
25
+ const key = buildAgentPeerSessionKey({
26
+ agentId: effectiveAgentId,
27
+ mainKey: ctx.mainKey,
28
+ channel,
29
+ accountId: effectiveAccountId,
30
+ peerKind: "direct",
31
+ peerId: ctx.senderId,
32
+ identityLinks: ctx.identityLinks,
33
+ dmScope: ctx.dmScope ?? "per-account-channel-peer"
34
+ });
35
+ if (ctx.threadId) return `${key}:thread:${ctx.threadId.toLowerCase()}`;
36
+ return key;
37
+ }
38
+ let key = buildAgentPeerSessionKey({
56
39
  agentId: effectiveAgentId,
57
- source,
40
+ mainKey: ctx.mainKey,
41
+ channel,
58
42
  accountId: effectiveAccountId,
59
43
  peerKind: "group",
60
- peerId: chatId,
61
- threadId
44
+ peerId: ctx.chatId,
45
+ identityLinks: ctx.identityLinks
62
46
  });
47
+ if (ctx.threadId) key = `${key}:thread:${ctx.threadId.toLowerCase()}`;
48
+ return key;
63
49
  }
64
- /**
65
- * Parse a session key into its components
66
- *
67
- * Returns a UI-oriented shape (`type`, `chatId`, etc.) derived from the routing key.
68
- */
69
50
  function parseSessionKey(sessionKey) {
70
51
  const parsed = parseSessionKey$1(sessionKey);
71
- if (!parsed) {
72
- const parts = sessionKey.split(":");
73
- return {
74
- source: parts[0] || "system",
75
- type: "other",
76
- chatId: parts[parts.length - 1] || "unknown"
77
- };
78
- }
52
+ if (!parsed) return {
53
+ source: "system",
54
+ type: "other",
55
+ chatId: "unknown"
56
+ };
79
57
  let type;
80
58
  switch (parsed.peerKind) {
81
59
  case "dm":
82
- type = "dm";
60
+ case "direct":
61
+ type = parsed.peerKind === "direct" && parsed.peerId === "main" ? "direct" : "dm";
83
62
  break;
84
63
  case "group":
85
- type = parsed.threadId ? "thread" : "group";
86
- break;
87
64
  case "channel":
88
65
  type = parsed.threadId ? "thread" : "group";
89
66
  break;
90
- case "direct":
91
- type = "direct";
92
- break;
93
67
  default: type = "other";
94
68
  }
95
69
  return {
@@ -101,29 +75,19 @@ function parseSessionKey(sessionKey) {
101
75
  accountId: parsed.accountId
102
76
  };
103
77
  }
104
- /**
105
- * Check if a session key is valid
106
- */
107
78
  function isValidSessionKey(sessionKey) {
108
- if (!sessionKey || typeof sessionKey !== "string") return false;
109
79
  return parseSessionKey$1(sessionKey) !== null;
110
80
  }
111
- /**
112
- * Get display name for a session key
113
- */
114
81
  function getSessionDisplayName(sessionKey) {
115
82
  const parsed = parseSessionKey(sessionKey);
116
83
  switch (parsed.type) {
117
84
  case "dm": return `Private Chat (${parsed.source})`;
118
85
  case "group": return `Group (${parsed.source})`;
119
86
  case "thread": return `Thread (${parsed.source})`;
120
- case "direct": return parsed.source === "cli" ? "CLI Direct" : `Direct (${parsed.source})`;
87
+ case "direct": return parsed.chatId === "main" ? "Main session" : `Direct (${parsed.source})`;
121
88
  default: return `${parsed.source}:${parsed.chatId}`;
122
89
  }
123
90
  }
124
- /**
125
- * Extract channel info from session key for reply routing
126
- */
127
91
  function getRoutingInfo(sessionKey) {
128
92
  const parsed = parseSessionKey(sessionKey);
129
93
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"session-key.js","names":["parseRoutingSessionKey"],"sources":["../../../src/chat-commands/session-key.ts"],"sourcesContent":["/**\n * Session Key Generator\n * \n * Uses the new routing system session key format:\n * {agentId}:{source}:{accountId}:{peerKind}:{peerId}[:thread:{threadId}][:scope:{scopeId}]\n * \n * Examples:\n * - main:telegram:default:dm:123456\n * - main:telegram:default:group:-100123456\n * - main:gateway:default:direct:chat_abc123\n * - main:cli:default:direct:cli\n */\n\nimport type { MessageSource } from './types.js';\nimport { buildSessionKey, parseSessionKey as parseRoutingSessionKey } from '../routing/session-key.js';\n\nexport interface SessionKeyContext {\n source: MessageSource;\n channelId?: string; // e.g., 'telegram:default'\n chatId: string; // Platform-specific chat ID\n senderId: string; // Sender's platform ID\n isGroup: boolean;\n threadId?: string; // For forum/thread support\n /** Agent ID (defaults to 'main') */\n agentId?: string;\n /** Account ID (defaults to 'default') */\n accountId?: string;\n}\n\n/**\n * Generate a unified session key using the new routing format\n * \n * Format: {agentId}:{source}:{accountId}:{peerKind}:{peerId}[:thread:{threadId}]\n * \n * Examples:\n * - telegram DM: main:telegram:default:dm:123456\n * - telegram Group: main:telegram:default:group:-100123456\n * - gateway: main:gateway:default:direct:chat_abc123\n * - cli: main:cli:default:direct:cli\n */\nexport function generateSessionKey(ctx: SessionKeyContext): string {\n const { source, chatId, senderId, isGroup, threadId, agentId, accountId } = ctx;\n \n const effectiveAgentId = agentId ?? 'main';\n const effectiveAccountId = accountId ?? 'default';\n\n // CLI special handling\n if (source === 'cli') {\n return buildSessionKey({\n agentId: effectiveAgentId,\n source: 'cli',\n accountId: effectiveAccountId,\n peerKind: 'direct',\n peerId: chatId === 'direct' ? 'cli' : chatId,\n });\n }\n \n // Web UI / Gateway special handling\n if (source === 'webui' || source === 'gateway') {\n return buildSessionKey({\n agentId: effectiveAgentId,\n source: source === 'webui' ? 'gateway' : source,\n accountId: effectiveAccountId,\n peerKind: 'direct',\n peerId: chatId,\n });\n }\n \n // API special handling\n if (source === 'api') {\n return buildSessionKey({\n agentId: effectiveAgentId,\n source: 'api',\n accountId: effectiveAccountId,\n peerKind: 'direct',\n peerId: chatId,\n });\n }\n \n // System messages\n if (source === 'system') {\n return buildSessionKey({\n agentId: effectiveAgentId,\n source: 'system',\n accountId: effectiveAccountId,\n peerKind: 'direct',\n peerId: chatId,\n });\n }\n \n // Private/DM chat\n if (!isGroup) {\n // Use senderId for private chats (consistent regardless of who initiates)\n return buildSessionKey({\n agentId: effectiveAgentId,\n source,\n accountId: effectiveAccountId,\n peerKind: 'dm',\n peerId: senderId,\n threadId,\n });\n }\n \n // Group chat\n return buildSessionKey({\n agentId: effectiveAgentId,\n source,\n accountId: effectiveAccountId,\n peerKind: 'group',\n peerId: chatId,\n threadId,\n });\n}\n\n/**\n * Parse a session key into its components\n * \n * Returns a UI-oriented shape (`type`, `chatId`, etc.) derived from the routing key.\n */\nexport function parseSessionKey(sessionKey: string): {\n source: MessageSource;\n type: 'dm' | 'group' | 'thread' | 'direct' | 'other';\n chatId: string;\n threadId?: string;\n agentId?: string;\n accountId?: string;\n} {\n const parsed = parseRoutingSessionKey(sessionKey);\n \n if (!parsed) {\n // Fallback for unparseable keys\n const parts = sessionKey.split(':');\n return {\n source: (parts[0] as MessageSource) || 'system',\n type: 'other',\n chatId: parts[parts.length - 1] || 'unknown',\n };\n }\n \n // Map routing peer kind to coarse UI type\n let type: 'dm' | 'group' | 'thread' | 'direct' | 'other';\n switch (parsed.peerKind) {\n case 'dm':\n type = 'dm';\n break;\n case 'group':\n type = parsed.threadId ? 'thread' : 'group';\n break;\n case 'channel':\n type = parsed.threadId ? 'thread' : 'group';\n break;\n case 'direct':\n type = 'direct';\n break;\n default:\n type = 'other';\n }\n \n return {\n source: parsed.source as MessageSource,\n type,\n chatId: parsed.peerId,\n threadId: parsed.threadId,\n agentId: parsed.agentId,\n accountId: parsed.accountId,\n };\n}\n\n/**\n * Check if a session key is valid\n */\nexport function isValidSessionKey(sessionKey: string): boolean {\n if (!sessionKey || typeof sessionKey !== 'string') return false;\n \n const parsed = parseRoutingSessionKey(sessionKey);\n return parsed !== null;\n}\n\n/**\n * Get display name for a session key\n */\nexport function getSessionDisplayName(sessionKey: string): string {\n const parsed = parseSessionKey(sessionKey);\n \n switch (parsed.type) {\n case 'dm':\n return `Private Chat (${parsed.source})`;\n case 'group':\n return `Group (${parsed.source})`;\n case 'thread':\n return `Thread (${parsed.source})`;\n case 'direct':\n return parsed.source === 'cli' ? 'CLI Direct' : `Direct (${parsed.source})`;\n default:\n return `${parsed.source}:${parsed.chatId}`;\n }\n}\n\n/**\n * Extract channel info from session key for reply routing\n */\nexport function getRoutingInfo(sessionKey: string): {\n channel: string;\n chatId: string;\n threadId?: string;\n} {\n const parsed = parseSessionKey(sessionKey);\n \n return {\n channel: parsed.source,\n chatId: parsed.chatId,\n threadId: parsed.threadId,\n };\n}\n"],"mappings":";;kBAcuG;;;;;;;;;;;;AA0BvG,SAAgB,mBAAmB,KAAgC;CACjE,MAAM,EAAE,QAAQ,QAAQ,UAAU,SAAS,UAAU,SAAS,cAAc;CAE5E,MAAM,mBAAmB,WAAW;CACpC,MAAM,qBAAqB,aAAa;AAGxC,KAAI,WAAW,MACb,QAAO,gBAAgB;EACrB,SAAS;EACT,QAAQ;EACR,WAAW;EACX,UAAU;EACV,QAAQ,WAAW,WAAW,QAAQ;EACvC,CAAC;AAIJ,KAAI,WAAW,WAAW,WAAW,UACnC,QAAO,gBAAgB;EACrB,SAAS;EACT,QAAQ,WAAW,UAAU,YAAY;EACzC,WAAW;EACX,UAAU;EACV,QAAQ;EACT,CAAC;AAIJ,KAAI,WAAW,MACb,QAAO,gBAAgB;EACrB,SAAS;EACT,QAAQ;EACR,WAAW;EACX,UAAU;EACV,QAAQ;EACT,CAAC;AAIJ,KAAI,WAAW,SACb,QAAO,gBAAgB;EACrB,SAAS;EACT,QAAQ;EACR,WAAW;EACX,UAAU;EACV,QAAQ;EACT,CAAC;AAIJ,KAAI,CAAC,QAEH,QAAO,gBAAgB;EACrB,SAAS;EACT;EACA,WAAW;EACX,UAAU;EACV,QAAQ;EACR;EACD,CAAC;AAIJ,QAAO,gBAAgB;EACrB,SAAS;EACT;EACA,WAAW;EACX,UAAU;EACV,QAAQ;EACR;EACD,CAAC;;;;;;;AAQJ,SAAgB,gBAAgB,YAO9B;CACA,MAAM,SAASA,kBAAuB,WAAW;AAEjD,KAAI,CAAC,QAAQ;EAEX,MAAM,QAAQ,WAAW,MAAM,IAAI;AACnC,SAAO;GACL,QAAS,MAAM,MAAwB;GACvC,MAAM;GACN,QAAQ,MAAM,MAAM,SAAS,MAAM;GACpC;;CAIH,IAAI;AACJ,SAAQ,OAAO,UAAf;EACE,KAAK;AACH,UAAO;AACP;EACF,KAAK;AACH,UAAO,OAAO,WAAW,WAAW;AACpC;EACF,KAAK;AACH,UAAO,OAAO,WAAW,WAAW;AACpC;EACF,KAAK;AACH,UAAO;AACP;EACF,QACE,QAAO;;AAGX,QAAO;EACL,QAAQ,OAAO;EACf;EACA,QAAQ,OAAO;EACf,UAAU,OAAO;EACjB,SAAS,OAAO;EAChB,WAAW,OAAO;EACnB;;;;;AAMH,SAAgB,kBAAkB,YAA6B;AAC7D,KAAI,CAAC,cAAc,OAAO,eAAe,SAAU,QAAO;AAG1D,QADeA,kBAAuB,WACzB,KAAK;;;;;AAMpB,SAAgB,sBAAsB,YAA4B;CAChE,MAAM,SAAS,gBAAgB,WAAW;AAE1C,SAAQ,OAAO,MAAf;EACE,KAAK,KACH,QAAO,iBAAiB,OAAO,OAAO;EACxC,KAAK,QACH,QAAO,UAAU,OAAO,OAAO;EACjC,KAAK,SACH,QAAO,WAAW,OAAO,OAAO;EAClC,KAAK,SACH,QAAO,OAAO,WAAW,QAAQ,eAAe,WAAW,OAAO,OAAO;EAC3E,QACE,QAAO,GAAG,OAAO,OAAO,GAAG,OAAO;;;;;;AAOxC,SAAgB,eAAe,YAI7B;CACA,MAAM,SAAS,gBAAgB,WAAW;AAE1C,QAAO;EACL,SAAS,OAAO;EAChB,QAAQ,OAAO;EACf,UAAU,OAAO;EAClB"}
1
+ {"version":3,"file":"session-key.js","names":["parseRoutingSessionKey"],"sources":["../../../src/chat-commands/session-key.ts"],"sourcesContent":["/**\n * Session Key Generator OpenClaw `agent:{agentId}:{rest}` format.\n */\n\nimport type { MessageSource } from './types.js';\nimport {\n buildAgentMainSessionKey,\n buildAgentPeerSessionKey,\n parseSessionKey as parseRoutingSessionKey,\n} from '../routing/session-key.js';\n\nexport interface SessionKeyContext {\n source: MessageSource;\n channelId?: string;\n chatId: string;\n senderId: string;\n isGroup: boolean;\n threadId?: string;\n agentId?: string;\n accountId?: string;\n mainKey?: string;\n dmScope?: 'main' | 'per-peer' | 'per-channel-peer' | 'per-account-channel-peer';\n identityLinks?: Record<string, string[]>;\n}\n\nexport function generateSessionKey(ctx: SessionKeyContext): string {\n const effectiveAgentId = ctx.agentId ?? 'main';\n const effectiveAccountId = ctx.accountId ?? 'default';\n const channel = ctx.source === 'webui' ? 'webchat' : ctx.source;\n\n if (ctx.source === 'cli') {\n if (ctx.chatId === 'direct' || ctx.chatId === 'main') {\n return buildAgentMainSessionKey({ agentId: effectiveAgentId, mainKey: ctx.mainKey });\n }\n return buildAgentPeerSessionKey({\n agentId: effectiveAgentId,\n mainKey: ctx.mainKey,\n channel: 'cli',\n accountId: effectiveAccountId,\n peerKind: 'direct',\n peerId: ctx.chatId,\n dmScope: 'per-peer',\n });\n }\n\n if (!ctx.isGroup) {\n const key = buildAgentPeerSessionKey({\n agentId: effectiveAgentId,\n mainKey: ctx.mainKey,\n channel,\n accountId: effectiveAccountId,\n peerKind: 'direct',\n peerId: ctx.senderId,\n identityLinks: ctx.identityLinks,\n dmScope: ctx.dmScope ?? 'per-account-channel-peer',\n });\n if (ctx.threadId) {\n return `${key}:thread:${ctx.threadId.toLowerCase()}`;\n }\n return key;\n }\n\n let key = buildAgentPeerSessionKey({\n agentId: effectiveAgentId,\n mainKey: ctx.mainKey,\n channel,\n accountId: effectiveAccountId,\n peerKind: 'group',\n peerId: ctx.chatId,\n identityLinks: ctx.identityLinks,\n });\n if (ctx.threadId) {\n key = `${key}:thread:${ctx.threadId.toLowerCase()}`;\n }\n return key;\n}\n\nexport function parseSessionKey(sessionKey: string): {\n source: MessageSource;\n type: 'dm' | 'group' | 'thread' | 'direct' | 'other';\n chatId: string;\n threadId?: string;\n agentId?: string;\n accountId?: string;\n} {\n const parsed = parseRoutingSessionKey(sessionKey);\n\n if (!parsed) {\n return {\n source: 'system',\n type: 'other',\n chatId: 'unknown',\n };\n }\n\n let type: 'dm' | 'group' | 'thread' | 'direct' | 'other';\n switch (parsed.peerKind) {\n case 'dm':\n case 'direct':\n type = parsed.peerKind === 'direct' && parsed.peerId === 'main' ? 'direct' : 'dm';\n break;\n case 'group':\n case 'channel':\n type = parsed.threadId ? 'thread' : 'group';\n break;\n default:\n type = 'other';\n }\n\n return {\n source: parsed.source as MessageSource,\n type,\n chatId: parsed.peerId,\n threadId: parsed.threadId,\n agentId: parsed.agentId,\n accountId: parsed.accountId,\n };\n}\n\nexport function isValidSessionKey(sessionKey: string): boolean {\n return parseRoutingSessionKey(sessionKey) !== null;\n}\n\nexport function getSessionDisplayName(sessionKey: string): string {\n const parsed = parseSessionKey(sessionKey);\n\n switch (parsed.type) {\n case 'dm':\n return `Private Chat (${parsed.source})`;\n case 'group':\n return `Group (${parsed.source})`;\n case 'thread':\n return `Thread (${parsed.source})`;\n case 'direct':\n return parsed.chatId === 'main' ? 'Main session' : `Direct (${parsed.source})`;\n default:\n return `${parsed.source}:${parsed.chatId}`;\n }\n}\n\nexport function getRoutingInfo(sessionKey: string): {\n channel: string;\n chatId: string;\n threadId?: string;\n} {\n const parsed = parseSessionKey(sessionKey);\n\n return {\n channel: parsed.source,\n chatId: parsed.chatId,\n threadId: parsed.threadId,\n };\n}\n"],"mappings":";;;kBASmC;AAgBnC,SAAgB,mBAAmB,KAAgC;CACjE,MAAM,mBAAmB,IAAI,WAAW;CACxC,MAAM,qBAAqB,IAAI,aAAa;CAC5C,MAAM,UAAU,IAAI,WAAW,UAAU,YAAY,IAAI;AAEzD,KAAI,IAAI,WAAW,OAAO;AACxB,MAAI,IAAI,WAAW,YAAY,IAAI,WAAW,OAC5C,QAAO,yBAAyB;GAAE,SAAS;GAAkB,SAAS,IAAI;GAAS,CAAC;AAEtF,SAAO,yBAAyB;GAC9B,SAAS;GACT,SAAS,IAAI;GACb,SAAS;GACT,WAAW;GACX,UAAU;GACV,QAAQ,IAAI;GACZ,SAAS;GACV,CAAC;;AAGJ,KAAI,CAAC,IAAI,SAAS;EAChB,MAAM,MAAM,yBAAyB;GACnC,SAAS;GACT,SAAS,IAAI;GACb;GACA,WAAW;GACX,UAAU;GACV,QAAQ,IAAI;GACZ,eAAe,IAAI;GACnB,SAAS,IAAI,WAAW;GACzB,CAAC;AACF,MAAI,IAAI,SACN,QAAO,GAAG,IAAI,UAAU,IAAI,SAAS,aAAa;AAEpD,SAAO;;CAGT,IAAI,MAAM,yBAAyB;EACjC,SAAS;EACT,SAAS,IAAI;EACb;EACA,WAAW;EACX,UAAU;EACV,QAAQ,IAAI;EACZ,eAAe,IAAI;EACpB,CAAC;AACF,KAAI,IAAI,SACN,OAAM,GAAG,IAAI,UAAU,IAAI,SAAS,aAAa;AAEnD,QAAO;;AAGT,SAAgB,gBAAgB,YAO9B;CACA,MAAM,SAASA,kBAAuB,WAAW;AAEjD,KAAI,CAAC,OACH,QAAO;EACL,QAAQ;EACR,MAAM;EACN,QAAQ;EACT;CAGH,IAAI;AACJ,SAAQ,OAAO,UAAf;EACE,KAAK;EACL,KAAK;AACH,UAAO,OAAO,aAAa,YAAY,OAAO,WAAW,SAAS,WAAW;AAC7E;EACF,KAAK;EACL,KAAK;AACH,UAAO,OAAO,WAAW,WAAW;AACpC;EACF,QACE,QAAO;;AAGX,QAAO;EACL,QAAQ,OAAO;EACf;EACA,QAAQ,OAAO;EACf,UAAU,OAAO;EACjB,SAAS,OAAO;EAChB,WAAW,OAAO;EACnB;;AAGH,SAAgB,kBAAkB,YAA6B;AAC7D,QAAOA,kBAAuB,WAAW,KAAK;;AAGhD,SAAgB,sBAAsB,YAA4B;CAChE,MAAM,SAAS,gBAAgB,WAAW;AAE1C,SAAQ,OAAO,MAAf;EACE,KAAK,KACH,QAAO,iBAAiB,OAAO,OAAO;EACxC,KAAK,QACH,QAAO,UAAU,OAAO,OAAO;EACjC,KAAK,SACH,QAAO,WAAW,OAAO,OAAO;EAClC,KAAK,SACH,QAAO,OAAO,WAAW,SAAS,iBAAiB,WAAW,OAAO,OAAO;EAC9E,QACE,QAAO,GAAG,OAAO,OAAO,GAAG,OAAO;;;AAIxC,SAAgB,eAAe,YAI7B;CACA,MAAM,SAAS,gBAAgB,WAAW;AAE1C,QAAO;EACL,SAAS,OAAO;EAChB,QAAQ,OAAO;EACf,UAAU,OAAO;EAClB"}
@@ -128,6 +128,8 @@ export interface CommandContext {
128
128
  getSession(): Promise<AgentMessage[]>;
129
129
  /** Clear current session (start fresh) */
130
130
  clearSession(): Promise<void>;
131
+ /** Reset session in place (archive transcript, new session id; preserve overrides) */
132
+ resetSession(): Promise<void>;
131
133
  /** Archive current session */
132
134
  archiveSession(): Promise<void>;
133
135
  /** List user's sessions */
@@ -1,4 +1,4 @@
1
- import { getSessionManager } from "../../utils/session.js";
1
+ import { getSessionIndex } from "../../utils/session.js";
2
2
  import { listSessions } from "./sessions.js";
3
3
  import { renderStreamToTerminal } from "./stream-renderer.js";
4
4
  //#region src/cli/commands/agent/interactive.ts
@@ -26,7 +26,7 @@ async function startInteractiveChat(agent, options) {
26
26
  }
27
27
  if (trimmed.startsWith(":session ")) {
28
28
  const newSessionKey = trimmed.slice(9).trim();
29
- if (await (await getSessionManager()).getSessionMetadata(newSessionKey)) {
29
+ if (await (await getSessionIndex()).getSessionMetadata(newSessionKey)) {
30
30
  sessionKey = newSessionKey;
31
31
  console.log(`🔄 Switched to session: ${sessionKey}\n`);
32
32
  } else console.log(`❌ Session not found: ${newSessionKey}\n`);
@@ -1 +1 @@
1
- {"version":3,"file":"interactive.js","names":[],"sources":["../../../../../src/cli/commands/agent/interactive.ts"],"sourcesContent":["/**\n * Interactive chat mode for agent command\n */\n\nimport type { Interface as _Interface } from 'readline';\nimport type { AgentService } from '../../../agent/index.js';\nimport { getSessionManager } from '../../utils/session.js';\nimport { listSessions } from './sessions.js';\nimport { renderStreamToTerminal } from './stream-renderer.js';\n\nexport interface InteractiveOptions {\n workspace: string;\n sessionKey: string;\n continuingSession: boolean;\n}\n\n/**\n * Start interactive chat mode\n */\nexport async function startInteractiveChat(\n agent: AgentService,\n options: InteractiveOptions\n): Promise<void> {\n const { sessionKey: initialSessionKey, continuingSession } = options;\n \n let sessionKey = initialSessionKey;\n\n if (continuingSession) {\n console.log('🧠 Interactive chat mode - Continuing session\\n');\n } else {\n console.log('🧠 Interactive chat mode (Ctrl+C to exit)\\n');\n }\n\n const readline = await import('readline');\n const rl = readline.createInterface({\n input: process.stdin,\n output: process.stdout,\n terminal: true,\n });\n\n rl.on('line', async (input) => {\n const trimmed = input.trim();\n \n // Handle special commands\n if (trimmed === ':sessions' || trimmed === ':list') {\n rl.pause();\n await listSessions();\n rl.resume();\n rl.prompt();\n return;\n }\n \n if (trimmed.startsWith(':session ')) {\n const newSessionKey = trimmed.slice(9).trim();\n const manager = await getSessionManager();\n const session = await manager.getSessionMetadata(newSessionKey);\n if (session) {\n sessionKey = newSessionKey;\n console.log(`🔄 Switched to session: ${sessionKey}\\n`);\n } else {\n console.log(`❌ Session not found: ${newSessionKey}\\n`);\n }\n rl.prompt();\n return;\n }\n\n if (trimmed === ':help') {\n printHelp();\n rl.prompt();\n return;\n }\n\n if (trimmed === ':quit' || trimmed === ':exit') {\n rl.close();\n return;\n }\n\n rl.pause();\n await renderStreamToTerminal(agent, input, sessionKey);\n rl.resume();\n rl.prompt();\n });\n\n rl.on('close', async () => {\n console.log('\\n👋 Goodbye!');\n process.exit(0);\n });\n\n rl.setPrompt('You: ');\n rl.prompt();\n}\n\nfunction printHelp(): void {\n console.log(`\n📖 Available commands:\n :sessions, :list - List available sessions\n :session <key> - Switch to another session\n :quit, :exit - Exit interactive mode\n :help - Show this help\n`);\n}\n"],"mappings":";;;;;;;AAmBA,eAAsB,qBACpB,OACA,SACe;CACf,MAAM,EAAE,YAAY,mBAAmB,sBAAsB;CAE7D,IAAI,aAAa;AAEjB,KAAI,kBACF,SAAQ,IAAI,kDAAkD;KAE9D,SAAQ,IAAI,8CAA8C;CAI5D,MAAM,MAAK,MADY,OAAO,aACV,gBAAgB;EAClC,OAAO,QAAQ;EACf,QAAQ,QAAQ;EAChB,UAAU;EACX,CAAC;AAEF,IAAG,GAAG,QAAQ,OAAO,UAAU;EAC7B,MAAM,UAAU,MAAM,MAAM;AAG5B,MAAI,YAAY,eAAe,YAAY,SAAS;AAClD,MAAG,OAAO;AACV,SAAM,cAAc;AACpB,MAAG,QAAQ;AACX,MAAG,QAAQ;AACX;;AAGF,MAAI,QAAQ,WAAW,YAAY,EAAE;GACnC,MAAM,gBAAgB,QAAQ,MAAM,EAAE,CAAC,MAAM;AAG7C,OAAI,OADkB,MADA,mBAAmB,EACX,mBAAmB,cAAc,EAClD;AACX,iBAAa;AACb,YAAQ,IAAI,2BAA2B,WAAW,IAAI;SAEtD,SAAQ,IAAI,wBAAwB,cAAc,IAAI;AAExD,MAAG,QAAQ;AACX;;AAGF,MAAI,YAAY,SAAS;AACvB,cAAW;AACX,MAAG,QAAQ;AACX;;AAGF,MAAI,YAAY,WAAW,YAAY,SAAS;AAC9C,MAAG,OAAO;AACV;;AAGF,KAAG,OAAO;AACV,QAAM,uBAAuB,OAAO,OAAO,WAAW;AACtD,KAAG,QAAQ;AACX,KAAG,QAAQ;GACX;AAEF,IAAG,GAAG,SAAS,YAAY;AACzB,UAAQ,IAAI,gBAAgB;AAC5B,UAAQ,KAAK,EAAE;GACf;AAEF,IAAG,UAAU,QAAQ;AACrB,IAAG,QAAQ;;AAGb,SAAS,YAAkB;AACzB,SAAQ,IAAI;;;;;;EAMZ"}
1
+ {"version":3,"file":"interactive.js","names":[],"sources":["../../../../../src/cli/commands/agent/interactive.ts"],"sourcesContent":["/**\n * Interactive chat mode for agent command\n */\n\nimport type { Interface as _Interface } from 'readline';\nimport type { AgentService } from '../../../agent/index.js';\nimport { getSessionIndex } from '../../utils/session.js';\nimport { listSessions } from './sessions.js';\nimport { renderStreamToTerminal } from './stream-renderer.js';\n\nexport interface InteractiveOptions {\n workspace: string;\n sessionKey: string;\n continuingSession: boolean;\n}\n\n/**\n * Start interactive chat mode\n */\nexport async function startInteractiveChat(\n agent: AgentService,\n options: InteractiveOptions\n): Promise<void> {\n const { sessionKey: initialSessionKey, continuingSession } = options;\n \n let sessionKey = initialSessionKey;\n\n if (continuingSession) {\n console.log('🧠 Interactive chat mode - Continuing session\\n');\n } else {\n console.log('🧠 Interactive chat mode (Ctrl+C to exit)\\n');\n }\n\n const readline = await import('readline');\n const rl = readline.createInterface({\n input: process.stdin,\n output: process.stdout,\n terminal: true,\n });\n\n rl.on('line', async (input) => {\n const trimmed = input.trim();\n \n // Handle special commands\n if (trimmed === ':sessions' || trimmed === ':list') {\n rl.pause();\n await listSessions();\n rl.resume();\n rl.prompt();\n return;\n }\n \n if (trimmed.startsWith(':session ')) {\n const newSessionKey = trimmed.slice(9).trim();\n const manager = await getSessionIndex();\n const session = await manager.getSessionMetadata(newSessionKey);\n if (session) {\n sessionKey = newSessionKey;\n console.log(`🔄 Switched to session: ${sessionKey}\\n`);\n } else {\n console.log(`❌ Session not found: ${newSessionKey}\\n`);\n }\n rl.prompt();\n return;\n }\n\n if (trimmed === ':help') {\n printHelp();\n rl.prompt();\n return;\n }\n\n if (trimmed === ':quit' || trimmed === ':exit') {\n rl.close();\n return;\n }\n\n rl.pause();\n await renderStreamToTerminal(agent, input, sessionKey);\n rl.resume();\n rl.prompt();\n });\n\n rl.on('close', async () => {\n console.log('\\n👋 Goodbye!');\n process.exit(0);\n });\n\n rl.setPrompt('You: ');\n rl.prompt();\n}\n\nfunction printHelp(): void {\n console.log(`\n📖 Available commands:\n :sessions, :list - List available sessions\n :session <key> - Switch to another session\n :quit, :exit - Exit interactive mode\n :help - Show this help\n`);\n}\n"],"mappings":";;;;;;;AAmBA,eAAsB,qBACpB,OACA,SACe;CACf,MAAM,EAAE,YAAY,mBAAmB,sBAAsB;CAE7D,IAAI,aAAa;AAEjB,KAAI,kBACF,SAAQ,IAAI,kDAAkD;KAE9D,SAAQ,IAAI,8CAA8C;CAI5D,MAAM,MAAK,MADY,OAAO,aACV,gBAAgB;EAClC,OAAO,QAAQ;EACf,QAAQ,QAAQ;EAChB,UAAU;EACX,CAAC;AAEF,IAAG,GAAG,QAAQ,OAAO,UAAU;EAC7B,MAAM,UAAU,MAAM,MAAM;AAG5B,MAAI,YAAY,eAAe,YAAY,SAAS;AAClD,MAAG,OAAO;AACV,SAAM,cAAc;AACpB,MAAG,QAAQ;AACX,MAAG,QAAQ;AACX;;AAGF,MAAI,QAAQ,WAAW,YAAY,EAAE;GACnC,MAAM,gBAAgB,QAAQ,MAAM,EAAE,CAAC,MAAM;AAG7C,OAAI,OADkB,MADA,iBAAiB,EACT,mBAAmB,cAAc,EAClD;AACX,iBAAa;AACb,YAAQ,IAAI,2BAA2B,WAAW,IAAI;SAEtD,SAAQ,IAAI,wBAAwB,cAAc,IAAI;AAExD,MAAG,QAAQ;AACX;;AAGF,MAAI,YAAY,SAAS;AACvB,cAAW;AACX,MAAG,QAAQ;AACX;;AAGF,MAAI,YAAY,WAAW,YAAY,SAAS;AAC9C,MAAG,OAAO;AACV;;AAGF,KAAG,OAAO;AACV,QAAM,uBAAuB,OAAO,OAAO,WAAW;AACtD,KAAG,QAAQ;AACX,KAAG,QAAQ;GACX;AAEF,IAAG,GAAG,SAAS,YAAY;AACzB,UAAQ,IAAI,gBAAgB;AAC5B,UAAQ,KAAK,EAAE;GACf;AAEF,IAAG,UAAU,QAAQ;AACrB,IAAG,QAAQ;;AAGb,SAAS,YAAkB;AACzB,SAAQ,IAAI;;;;;;EAMZ"}
@@ -1,4 +1,4 @@
1
- import { getSessionManager } from "../../utils/session.js";
1
+ import { getSessionIndex } from "../../utils/session.js";
2
2
  //#region src/cli/commands/agent/sessions.ts
3
3
  /**
4
4
  * Session listing for agent command
@@ -7,7 +7,7 @@ import { getSessionManager } from "../../utils/session.js";
7
7
  * List available sessions in a table format
8
8
  */
9
9
  async function listSessions() {
10
- const result = await (await getSessionManager()).listSessions({
10
+ const result = await (await getSessionIndex()).listSessions({
11
11
  limit: 20,
12
12
  sortBy: "updatedAt",
13
13
  sortOrder: "desc"
@@ -1 +1 @@
1
- {"version":3,"file":"sessions.js","names":[],"sources":["../../../../../src/cli/commands/agent/sessions.ts"],"sourcesContent":["/**\n * Session listing for agent command\n */\n\nimport { getSessionManager } from '../../utils/session.js';\n\n/**\n * List available sessions in a table format\n */\nexport async function listSessions(): Promise<void> {\n const manager = await getSessionManager();\n \n const result = await manager.listSessions({ limit: 20, sortBy: 'updatedAt', sortOrder: 'desc' });\n \n console.log('\\n📋 Available Sessions:\\n');\n if (result.items.length === 0) {\n console.log('No sessions found.');\n return;\n }\n \n console.log('Key'.padEnd(35) + 'Name'.padEnd(20) + 'Messages'.padEnd(10) + 'Updated');\n console.log('─'.repeat(85));\n \n for (const session of result.items) {\n const name = (session.name || '-').slice(0, 18).padEnd(20);\n const messages = String(session.messageCount).padEnd(10);\n const updated = new Date(session.updatedAt).toLocaleDateString();\n console.log(`${session.key.slice(0, 33).padEnd(35)}${name}${messages}${updated}`);\n }\n console.log();\n}\n"],"mappings":";;;;;;;;AASA,eAAsB,eAA8B;CAGlD,MAAM,SAAS,OAAM,MAFC,mBAAmB,EAEZ,aAAa;EAAE,OAAO;EAAI,QAAQ;EAAa,WAAW;EAAQ,CAAC;AAEhG,SAAQ,IAAI,6BAA6B;AACzC,KAAI,OAAO,MAAM,WAAW,GAAG;AAC7B,UAAQ,IAAI,qBAAqB;AACjC;;AAGF,SAAQ,IAAI,MAAM,OAAO,GAAG,GAAG,OAAO,OAAO,GAAG,GAAG,WAAW,OAAO,GAAG,GAAG,UAAU;AACrF,SAAQ,IAAI,IAAI,OAAO,GAAG,CAAC;AAE3B,MAAK,MAAM,WAAW,OAAO,OAAO;EAClC,MAAM,QAAQ,QAAQ,QAAQ,KAAK,MAAM,GAAG,GAAG,CAAC,OAAO,GAAG;EAC1D,MAAM,WAAW,OAAO,QAAQ,aAAa,CAAC,OAAO,GAAG;EACxD,MAAM,UAAU,IAAI,KAAK,QAAQ,UAAU,CAAC,oBAAoB;AAChE,UAAQ,IAAI,GAAG,QAAQ,IAAI,MAAM,GAAG,GAAG,CAAC,OAAO,GAAG,GAAG,OAAO,WAAW,UAAU;;AAEnF,SAAQ,KAAK"}
1
+ {"version":3,"file":"sessions.js","names":[],"sources":["../../../../../src/cli/commands/agent/sessions.ts"],"sourcesContent":["/**\n * Session listing for agent command\n */\n\nimport { getSessionIndex } from '../../utils/session.js';\n\n/**\n * List available sessions in a table format\n */\nexport async function listSessions(): Promise<void> {\n const manager = await getSessionIndex();\n \n const result = await manager.listSessions({ limit: 20, sortBy: 'updatedAt', sortOrder: 'desc' });\n \n console.log('\\n📋 Available Sessions:\\n');\n if (result.items.length === 0) {\n console.log('No sessions found.');\n return;\n }\n \n console.log('Key'.padEnd(35) + 'Name'.padEnd(20) + 'Messages'.padEnd(10) + 'Updated');\n console.log('─'.repeat(85));\n \n for (const session of result.items) {\n const name = (session.name || '-').slice(0, 18).padEnd(20);\n const messages = String(session.messageCount).padEnd(10);\n const updated = new Date(session.updatedAt).toLocaleDateString();\n console.log(`${session.key.slice(0, 33).padEnd(35)}${name}${messages}${updated}`);\n }\n console.log();\n}\n"],"mappings":";;;;;;;;AASA,eAAsB,eAA8B;CAGlD,MAAM,SAAS,OAAM,MAFC,iBAAiB,EAEV,aAAa;EAAE,OAAO;EAAI,QAAQ;EAAa,WAAW;EAAQ,CAAC;AAEhG,SAAQ,IAAI,6BAA6B;AACzC,KAAI,OAAO,MAAM,WAAW,GAAG;AAC7B,UAAQ,IAAI,qBAAqB;AACjC;;AAGF,SAAQ,IAAI,MAAM,OAAO,GAAG,GAAG,OAAO,OAAO,GAAG,GAAG,WAAW,OAAO,GAAG,GAAG,UAAU;AACrF,SAAQ,IAAI,IAAI,OAAO,GAAG,CAAC;AAE3B,MAAK,MAAM,WAAW,OAAO,OAAO;EAClC,MAAM,QAAQ,QAAQ,QAAQ,KAAK,MAAM,GAAG,GAAG,CAAC,OAAO,GAAG;EAC1D,MAAM,WAAW,OAAO,QAAQ,aAAa,CAAC,OAAO,GAAG;EACxD,MAAM,UAAU,IAAI,KAAK,QAAQ,UAAU,CAAC,oBAAoB;AAChE,UAAQ,IAAI,GAAG,QAAQ,IAAI,MAAM,GAAG,GAAG,CAAC,OAAO,GAAG,GAAG,OAAO,WAAW,UAAU;;AAEnF,SAAQ,KAAK"}
@@ -34,8 +34,7 @@ function createAgentCommand(_ctx) {
34
34
  await listSessions();
35
35
  return;
36
36
  }
37
- const modelConfig = config.agents?.defaults?.model;
38
- const modelFromConfig = typeof modelConfig === "string" ? modelConfig : modelConfig?.primary;
37
+ const modelFromConfig = config.agents?.defaults?.model?.primary;
39
38
  const modelId = options.model?.trim() || modelFromConfig;
40
39
  const bus = new MessageBus();
41
40
  if (ctx.isVerbose) log.info({
@@ -43,10 +42,10 @@ function createAgentCommand(_ctx) {
43
42
  workspace,
44
43
  session: options.session
45
44
  }, "Starting agent");
46
- let sessionKey = options.session || "cli:direct";
45
+ let sessionKey = options.session || "agent:main:main";
47
46
  if (options.session) {
48
- const { getSessionManager } = await import("../utils/session.js");
49
- const session = await (await getSessionManager()).getSessionMetadata(options.session);
47
+ const { getSessionIndex } = await import("../utils/session.js");
48
+ const session = await (await getSessionIndex()).getSessionMetadata(options.session);
50
49
  if (!session) {
51
50
  console.error(`❌ Session not found: ${options.session}`);
52
51
  console.log("Use --list to see available sessions.");
@@ -1 +1 @@
1
- {"version":3,"file":"agent.js","names":[],"sources":["../../../../src/cli/commands/agent.ts"],"sourcesContent":["import { Command } from 'commander';\nimport { AgentService } from '../../agent/index.js';\nimport { loadConfig, getWorkspacePath } from '../../config/index.js';\nimport { MessageBus, MessageBusShutdownError } from '../../infra/bus/index.js';\nimport { createLogger } from '../../utils/logger.js';\nimport { register, formatExamples, type CLIContext } from '../registry.js';\nimport { getContextWithOpts } from '../context.js';\nimport { ExtensionLoader } from '../../extensions/index.js';\nimport { join } from 'path';\nimport { listSessions } from './agent/sessions.js';\nimport { startInteractiveChat } from './agent/interactive.js';\nimport { renderStreamToTerminal } from './agent/stream-renderer.js';\n\nconst log = createLogger('AgentCommand');\n\ninterface AgentCommandOptions {\n message?: string;\n model?: string;\n interactive?: boolean;\n session?: string;\n list?: boolean;\n}\n\nfunction createAgentCommand(_ctx: CLIContext): Command {\n const cmd = new Command('agent')\n .description('Chat with the AI agent')\n .addHelpText(\n 'after',\n formatExamples([\n 'xopc agent -m \"Hello\" # Single message',\n 'xopc agent --model demo/demo-chat-7b -m \"Hi\" # Override model for one shot',\n 'xopc agent -i # Interactive chat mode',\n 'xopc agent -i --session telegram:dm:123456 # Continue existing session',\n 'xopc agent --list # List available sessions',\n ])\n )\n .option('--model <id>', 'Model ref for this run (e.g. demo/demo-chat-7b, anthropic/claude-sonnet-4-5)')\n .option('-m, --message <text>', 'Single message to send')\n .option('-i, --interactive', 'Interactive chat mode')\n .option('-s, --session <key>', 'Continue an existing session (use --list to see available sessions)')\n .option('-l, --list', 'List available sessions and exit')\n .action(async (options: AgentCommandOptions) => {\n const ctx = getContextWithOpts();\n const config = loadConfig(ctx.configPath);\n const workspace = getWorkspacePath(config) || ctx.workspacePath;\n\n // Handle --list option\n if (options.list) {\n await listSessions();\n return;\n }\n\n const modelConfig = config.agents?.defaults?.model;\n const modelFromConfig = typeof modelConfig === 'string' ? modelConfig : modelConfig?.primary;\n const modelId = (options.model?.trim() || modelFromConfig) as string | undefined;\n const bus = new MessageBus();\n\n if (ctx.isVerbose) {\n log.info({ model: modelId, workspace, session: options.session }, 'Starting agent');\n }\n\n // Validate session key if provided\n let sessionKey = options.session || 'cli:direct';\n if (options.session) {\n const { getSessionManager } = await import('../utils/session.js');\n const manager = await getSessionManager();\n const session = await manager.getSessionMetadata(options.session);\n if (!session) {\n console.error(`❌ Session not found: ${options.session}`);\n console.log('Use --list to see available sessions.');\n process.exit(1);\n }\n console.log(`📂 Continuing session: ${options.session} (${session.messageCount} messages)\\n`);\n }\n\n // Initialize extension loader (manifest-first activation: env, channels, model, extensions.*)\n let extensionLoader: ExtensionLoader | null = null;\n try {\n extensionLoader = new ExtensionLoader({\n workspaceDir: workspace,\n extensionsDir: join(workspace, '.extensions'),\n });\n extensionLoader.setConfig(config as Parameters<ExtensionLoader['setConfig']>[0]);\n extensionLoader.setRuntimeContext({ bus });\n await extensionLoader.loadByActivationPlan();\n const n = extensionLoader.getRegistry().extensions.size;\n if (n > 0) {\n log.info({ count: n }, 'Extensions loaded');\n }\n } catch (error) {\n const em = error instanceof Error ? error.message : String(error);\n log.warn({ err: error, errorMessage: em }, `CLI agent: failed to load extensions: ${em}`);\n }\n\n const { createCliReadlineClarifyRequestFn } = await import('../../agent/tools/cli-clarify.js');\n\n const agent = new AgentService(bus, {\n workspace,\n model: modelId,\n config,\n extensionRegistry: extensionLoader?.getRegistry(),\n gatewayClarify: {\n requestClarification: createCliReadlineClarifyRequestFn(),\n },\n });\n\n // Start agent service in background\n agent.start().catch((err) => {\n const em = err instanceof Error ? err.message : String(err);\n log.error({ err, errorMessage: em }, `CLI agent service exited: ${em}`);\n });\n\n // Start outbound message processor for CLI mode\n let running = true;\n const _outboundProcessor = (async () => {\n while (running) {\n try {\n const msg = await bus.consumeOutbound();\n console.log(`\\n📤 [${msg.channel}] ${msg.chat_id}: ${msg.content.slice(0, 100)}...`);\n } catch (error) {\n if (error instanceof MessageBusShutdownError) {\n break;\n }\n const em = error instanceof Error ? error.message : String(error);\n log.error({ err: error, errorMessage: em }, `CLI outbound processor failed: ${em}`);\n await new Promise((resolve) => setTimeout(resolve, 1000));\n }\n }\n })();\n\n const shutdown = async () => {\n running = false;\n bus.shutdown();\n await agent.stop();\n };\n\n process.on('SIGINT', shutdown);\n process.on('SIGTERM', shutdown);\n\n if (options.message) {\n const oneShotModel = options.model?.trim();\n if (oneShotModel) {\n await agent.switchModelForSession(sessionKey, oneShotModel);\n }\n await renderStreamToTerminal(agent, options.message, sessionKey);\n if (oneShotModel) {\n await agent.resetSessionModelToAgentDefault(sessionKey);\n }\n await shutdown();\n } else if (options.interactive) {\n const interactiveModel = options.model?.trim();\n if (interactiveModel) {\n await agent.switchModelForSession(sessionKey, interactiveModel);\n }\n await startInteractiveChat(agent, {\n workspace,\n sessionKey,\n continuingSession: !!options.session,\n });\n } else {\n await shutdown();\n cmd.help();\n }\n });\n\n return cmd;\n}\n\nregister({\n id: 'agent',\n name: 'agent',\n description: 'Chat with the AI agent',\n factory: createAgentCommand,\n metadata: {\n category: 'runtime',\n examples: [\n 'xopc agent -m \"Hello\"',\n 'xopc agent --model demo/demo-chat-7b -m \"Hello demo!\"',\n 'xopc agent -i',\n ],\n },\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"agent.js","names":[],"sources":["../../../../src/cli/commands/agent.ts"],"sourcesContent":["import { Command } from 'commander';\nimport { AgentService } from '../../agent/index.js';\nimport { loadConfig, getWorkspacePath } from '../../config/index.js';\nimport { MessageBus, MessageBusShutdownError } from '../../infra/bus/index.js';\nimport { createLogger } from '../../utils/logger.js';\nimport { register, formatExamples, type CLIContext } from '../registry.js';\nimport { getContextWithOpts } from '../context.js';\nimport { ExtensionLoader } from '../../extensions/index.js';\nimport { join } from 'path';\nimport { listSessions } from './agent/sessions.js';\nimport { startInteractiveChat } from './agent/interactive.js';\nimport { renderStreamToTerminal } from './agent/stream-renderer.js';\n\nconst log = createLogger('AgentCommand');\n\ninterface AgentCommandOptions {\n message?: string;\n model?: string;\n interactive?: boolean;\n session?: string;\n list?: boolean;\n}\n\nfunction createAgentCommand(_ctx: CLIContext): Command {\n const cmd = new Command('agent')\n .description('Chat with the AI agent')\n .addHelpText(\n 'after',\n formatExamples([\n 'xopc agent -m \"Hello\" # Single message',\n 'xopc agent --model demo/demo-chat-7b -m \"Hi\" # Override model for one shot',\n 'xopc agent -i # Interactive chat mode',\n 'xopc agent -i --session telegram:dm:123456 # Continue existing session',\n 'xopc agent --list # List available sessions',\n ])\n )\n .option('--model <id>', 'Model ref for this run (e.g. demo/demo-chat-7b, anthropic/claude-sonnet-4-5)')\n .option('-m, --message <text>', 'Single message to send')\n .option('-i, --interactive', 'Interactive chat mode')\n .option('-s, --session <key>', 'Continue an existing session (use --list to see available sessions)')\n .option('-l, --list', 'List available sessions and exit')\n .action(async (options: AgentCommandOptions) => {\n const ctx = getContextWithOpts();\n const config = loadConfig(ctx.configPath);\n const workspace = getWorkspacePath(config) || ctx.workspacePath;\n\n // Handle --list option\n if (options.list) {\n await listSessions();\n return;\n }\n\n const modelFromConfig = config.agents?.defaults?.model?.primary;\n const modelId = (options.model?.trim() || modelFromConfig) as string | undefined;\n const bus = new MessageBus();\n\n if (ctx.isVerbose) {\n log.info({ model: modelId, workspace, session: options.session }, 'Starting agent');\n }\n\n // Validate session key if provided\n let sessionKey = options.session || 'agent:main:main';\n if (options.session) {\n const { getSessionIndex } = await import('../utils/session.js');\n const manager = await getSessionIndex();\n const session = await manager.getSessionMetadata(options.session);\n if (!session) {\n console.error(`❌ Session not found: ${options.session}`);\n console.log('Use --list to see available sessions.');\n process.exit(1);\n }\n console.log(`📂 Continuing session: ${options.session} (${session.messageCount} messages)\\n`);\n }\n\n // Initialize extension loader (manifest-first activation: env, channels, model, extensions.*)\n let extensionLoader: ExtensionLoader | null = null;\n try {\n extensionLoader = new ExtensionLoader({\n workspaceDir: workspace,\n extensionsDir: join(workspace, '.extensions'),\n });\n extensionLoader.setConfig(config as Parameters<ExtensionLoader['setConfig']>[0]);\n extensionLoader.setRuntimeContext({ bus });\n await extensionLoader.loadByActivationPlan();\n const n = extensionLoader.getRegistry().extensions.size;\n if (n > 0) {\n log.info({ count: n }, 'Extensions loaded');\n }\n } catch (error) {\n const em = error instanceof Error ? error.message : String(error);\n log.warn({ err: error, errorMessage: em }, `CLI agent: failed to load extensions: ${em}`);\n }\n\n const { createCliReadlineClarifyRequestFn } = await import('../../agent/tools/cli-clarify.js');\n\n const agent = new AgentService(bus, {\n workspace,\n model: modelId,\n config,\n extensionRegistry: extensionLoader?.getRegistry(),\n gatewayClarify: {\n requestClarification: createCliReadlineClarifyRequestFn(),\n },\n });\n\n // Start agent service in background\n agent.start().catch((err) => {\n const em = err instanceof Error ? err.message : String(err);\n log.error({ err, errorMessage: em }, `CLI agent service exited: ${em}`);\n });\n\n // Start outbound message processor for CLI mode\n let running = true;\n const _outboundProcessor = (async () => {\n while (running) {\n try {\n const msg = await bus.consumeOutbound();\n console.log(`\\n📤 [${msg.channel}] ${msg.chat_id}: ${msg.content.slice(0, 100)}...`);\n } catch (error) {\n if (error instanceof MessageBusShutdownError) {\n break;\n }\n const em = error instanceof Error ? error.message : String(error);\n log.error({ err: error, errorMessage: em }, `CLI outbound processor failed: ${em}`);\n await new Promise((resolve) => setTimeout(resolve, 1000));\n }\n }\n })();\n\n const shutdown = async () => {\n running = false;\n bus.shutdown();\n await agent.stop();\n };\n\n process.on('SIGINT', shutdown);\n process.on('SIGTERM', shutdown);\n\n if (options.message) {\n const oneShotModel = options.model?.trim();\n if (oneShotModel) {\n await agent.switchModelForSession(sessionKey, oneShotModel);\n }\n await renderStreamToTerminal(agent, options.message, sessionKey);\n if (oneShotModel) {\n await agent.resetSessionModelToAgentDefault(sessionKey);\n }\n await shutdown();\n } else if (options.interactive) {\n const interactiveModel = options.model?.trim();\n if (interactiveModel) {\n await agent.switchModelForSession(sessionKey, interactiveModel);\n }\n await startInteractiveChat(agent, {\n workspace,\n sessionKey,\n continuingSession: !!options.session,\n });\n } else {\n await shutdown();\n cmd.help();\n }\n });\n\n return cmd;\n}\n\nregister({\n id: 'agent',\n name: 'agent',\n description: 'Chat with the AI agent',\n factory: createAgentCommand,\n metadata: {\n category: 'runtime',\n examples: [\n 'xopc agent -m \"Hello\"',\n 'xopc agent --model demo/demo-chat-7b -m \"Hello demo!\"',\n 'xopc agent -i',\n ],\n },\n});\n"],"mappings":"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"}
@@ -279,7 +279,6 @@ function applyTelegramAccount(cfg, accountId, botToken, enable) {
279
279
  const channels = { ...cfg.channels ?? {} };
280
280
  const tg = { ...channels.telegram ?? {} };
281
281
  if (enable) tg.enabled = true;
282
- tg.botToken = botToken;
283
282
  const accounts = { ...tg.accounts ?? {} };
284
283
  accounts[accountId] = {
285
284
  ...accounts[accountId] ?? {},
@@ -302,10 +301,7 @@ function removeChannelAccount(cfg, channelId, accountId) {
302
301
  if (!(accountId in accounts)) throw new SetupValidationError([{ message: `No account "${accountId}" on channel "${channelId}".` }]);
303
302
  delete accounts[accountId];
304
303
  ch.accounts = accounts;
305
- if (Object.keys(accounts).length === 0) {
306
- ch.enabled = false;
307
- if (channelId === "telegram") delete ch.botToken;
308
- }
304
+ if (Object.keys(accounts).length === 0) ch.enabled = false;
309
305
  channels[channelId] = ch;
310
306
  return {
311
307
  ...cfg,
@@ -1 +1 @@
1
- {"version":3,"file":"channels.js","names":[],"sources":["../../../../src/cli/commands/channels.ts"],"sourcesContent":["import { Command } from 'commander';\n\nimport { approveChannelPairingFromCli, type PairingCliChannel } from '../../channels/pairing/index.js';\nimport { resolveConfigPath } from '../../config/paths.js';\nimport {\n getChannelPlugin,\n listChannelPlugins,\n syncChannelPluginsFromManager,\n} from '../../channels/plugins/registry.js';\nimport { bundledChannelPlugins } from '../../generated/bundled-channel-plugins.js';\nimport { loadConfig } from '../../config/loader.js';\nimport type { Config } from '../../config/schema.js';\nimport { register, formatExamples, type CLIContext } from '../registry.js';\nimport { colors } from '../utils/colors.js';\n\nimport {\n SETUP_EXIT,\n SetupValidationError,\n emitOutcome,\n isPromptCancelled,\n promptSecret,\n runSetup,\n type SetupOutcome,\n} from './setup-shared/index.js';\n\nfunction ensureChannelRegistryForCli(): void {\n if (listChannelPlugins().length === 0) {\n syncChannelPluginsFromManager(bundledChannelPlugins);\n }\n}\n\nfunction resolveConfigPathFromCommand(command: Command): string {\n const root =\n command.parent?.parent && command.parent.parent instanceof Command\n ? command.parent.parent\n : command.parent && command.parent instanceof Command\n ? command.parent\n : null;\n const globalOpts = (root && typeof root.opts === 'function'\n ? (root.opts() as { config?: string })\n : {}) as { config?: string };\n return (\n globalOpts.config?.trim() ||\n process.env.XOPC_CONFIG_PATH?.trim() ||\n process.env.XOPC_CONFIG?.trim() ||\n resolveConfigPath()\n );\n}\n\nfunction createChannelsCommand(ctx: CLIContext): Command {\n const cmd = new Command('channels')\n .description('Messaging channel login and credentials')\n .addHelpText(\n 'after',\n formatExamples([\n 'xopc channels login',\n 'xopc channels login --channel weixin',\n 'xopc channels login --channel feishu',\n 'xopc channels login --account my-bot-id',\n 'xopc channels pairing approve --channel telegram ABC12XYZ',\n ]),\n );\n\n const PAIRING_CHANNELS = new Set<PairingCliChannel>(['telegram', 'feishu', 'weixin']);\n\n cmd\n .command('login')\n .description('Log in with QR code or channel-specific credentials flow')\n .option(\n '--channel <id>',\n 'Channel id (auto-detected when only one login-capable channel is registered)',\n )\n .option('--account <id>', 'Optional account id when re-logging an existing bot')\n .option('--timeout <ms>', 'Max wait for scan (default 480000)', '480000')\n .option('--credentials-only', 'Only save token files; do not update xopc.json')\n .action(async (options, command) => {\n ensureChannelRegistryForCli();\n const explicitChannel = options.channel?.trim?.();\n let channelId: string;\n if (explicitChannel) {\n channelId = explicitChannel;\n } else {\n const loginCapable = listChannelPlugins().filter((p) => p.cliLogin);\n if (loginCapable.length === 1) {\n channelId = loginCapable[0].id;\n console.log(`Auto-detected channel: ${channelId}`);\n } else if (loginCapable.length === 0) {\n console.error('No channels with login support found.');\n process.exitCode = 1;\n return;\n } else {\n console.error(\n `Multiple channels support login: ${loginCapable.map((p) => p.id).join(', ')}. ` +\n 'Use --channel <id> to specify.',\n );\n process.exitCode = 1;\n return;\n }\n }\n const plugin = getChannelPlugin(channelId);\n if (!plugin?.cliLogin) {\n console.error(`Channel \"${channelId}\" does not support CLI login.`);\n const capable = listChannelPlugins()\n .filter((p) => p.cliLogin)\n .map((p) => p.id);\n if (capable.length > 0) {\n console.error(`Channels with login support: ${capable.join(', ')}`);\n }\n process.exitCode = 1;\n return;\n }\n\n const configPath = resolveConfigPathFromCommand(command);\n const timeoutMs = Math.max(60_000, Number.parseInt(String(options.timeout), 10) || 480_000);\n const verbose = ctx.isVerbose;\n\n const result = await plugin.cliLogin.runLogin({\n configPath,\n verbose,\n timeoutMs,\n accountId: options.account?.trim() || undefined,\n writeConfig: !options.credentialsOnly,\n });\n\n if (!result.ok) {\n if (result.cancelled) {\n process.exitCode = 130;\n return;\n }\n console.error(result.message || 'Login failed');\n process.exitCode = 1;\n }\n });\n\n cmd\n .command('list')\n .description('List configured channels and their account status')\n .option('--json', 'Output as JSON for agents/UIs', false)\n .action((opts: { json?: boolean }) => {\n ensureChannelRegistryForCli();\n const cfg = loadConfig(resolveConfigPathFromCommand(cmd));\n const channelsCfg = (cfg.channels ?? {}) as Record<string, Record<string, unknown>>;\n const entries = listChannelPlugins().map((plugin) => {\n const raw = channelsCfg[plugin.id] ?? {};\n const enabled = raw.enabled === true;\n const accounts = (raw.accounts ?? {}) as Record<string, Record<string, unknown>>;\n const accountSummaries = Object.entries(accounts).map(([id, acct]) => ({\n accountId: id,\n enabled: acct.enabled !== false,\n configured: typeof acct.botToken === 'string'\n ? Boolean((acct.botToken as string).trim())\n : false,\n }));\n return {\n id: plugin.id,\n name: plugin.meta?.label ?? plugin.id,\n enabled,\n hasLogin: Boolean(plugin.cliLogin),\n accountCount: accountSummaries.length,\n accounts: accountSummaries,\n };\n });\n if (opts.json) {\n process.stdout.write(JSON.stringify({ ok: true, channels: entries }) + '\\n');\n return;\n }\n console.log('');\n console.log(colors.bold('CHANNELS'));\n const idWidth = Math.max(...entries.map((e) => e.id.length));\n for (const e of entries) {\n const status = e.enabled\n ? colors.green('● enabled')\n : colors.gray('○ disabled');\n const accts = e.accountCount > 0 ? colors.gray(` (${e.accountCount} accounts)`) : '';\n console.log(` ${e.id.padEnd(idWidth)} ${status} ${colors.gray(e.name)}${accts}`);\n }\n console.log('');\n console.log(colors.gray('Use: xopc channels add <id> [--token <token>]'));\n });\n\n cmd\n .command('add <channel>')\n .description('Add or update a channel account (currently: telegram)')\n .option('--token <value>', 'Bot token (Telegram). Prompts securely if omitted.')\n .option('--account <id>', 'Account id (default: \"default\")')\n .option('--enable', 'Enable the channel after writing (default: true)', true)\n .option('--dry-run', 'Show the change without writing', false)\n .option('--json', 'Emit a single JSON outcome line', false)\n .action(\n async (\n channel: string,\n opts: {\n token?: string;\n account?: string;\n enable?: boolean;\n dryRun?: boolean;\n json?: boolean;\n },\n ) => {\n const channelId = channel.trim().toLowerCase();\n const accountId = opts.account?.trim() || 'default';\n const dryRun = Boolean(opts.dryRun);\n const json = Boolean(opts.json);\n\n if (channelId === 'feishu' || channelId === 'weixin') {\n const outcome: SetupOutcome = {\n ok: false,\n action: 'add',\n domain: `channels.${channelId}`,\n target: accountId,\n changedPaths: [],\n dryRun,\n errors: [\n {\n message:\n channelId === 'weixin'\n ? 'Weixin uses interactive QR login. Run: xopc channels login --channel weixin'\n : 'Feishu add via CLI is not yet implemented. Edit `~/.xopc/xopc.json` (channels.feishu) or run: xopc onboard --channels',\n },\n ],\n };\n emitOutcome(outcome, json);\n process.exitCode = SETUP_EXIT.ERROR;\n return;\n }\n\n if (channelId !== 'telegram') {\n const outcome: SetupOutcome = {\n ok: false,\n action: 'add',\n domain: `channels.${channelId}`,\n target: accountId,\n changedPaths: [],\n dryRun,\n errors: [{ message: `Unknown channel \"${channelId}\". Try: xopc channels list` }],\n };\n emitOutcome(outcome, json);\n process.exitCode = SETUP_EXIT.ERROR;\n return;\n }\n\n let token = opts.token?.trim();\n if (!token) {\n token = process.env.TELEGRAM_BOT_TOKEN?.trim();\n }\n if (!token) {\n try {\n token = await promptSecret('Telegram bot token (from @BotFather):');\n } catch (error) {\n if (isPromptCancelled(error)) {\n const outcome: SetupOutcome = {\n ok: false,\n action: 'add',\n domain: 'channels.telegram',\n target: accountId,\n changedPaths: [],\n dryRun,\n errors: [{ message: 'Cancelled by user' }],\n };\n emitOutcome(outcome, json);\n process.exitCode = SETUP_EXIT.CANCELLED;\n return;\n }\n throw error;\n }\n }\n\n if (!validateTelegramToken(token)) {\n const outcome: SetupOutcome = {\n ok: false,\n action: 'add',\n domain: 'channels.telegram',\n target: accountId,\n changedPaths: [],\n dryRun,\n errors: [{ path: 'token', message: 'Token does not look like a Telegram bot token (expected `<digits>:<35+ chars>`).' }],\n };\n emitOutcome(outcome, json);\n process.exitCode = SETUP_EXIT.ERROR;\n return;\n }\n\n await runSetup({\n configPath: resolveConfigPathFromCommand(cmd),\n options: { dryRun, json },\n mutator: {\n domain: 'channels.telegram',\n target: accountId,\n action: 'add',\n mutate(config) {\n return applyTelegramAccount(config, accountId, token!, opts.enable !== false);\n },\n resultValue: () => ({\n channel: 'telegram',\n accountId,\n tokenMask: maskToken(token!),\n }),\n notes: () => [\n opts.enable !== false\n ? 'Telegram channel enabled. Restart the gateway for the new bot to come online.'\n : 'Telegram credentials saved (channel disabled). Use `--enable` to start it.',\n ],\n },\n });\n },\n );\n\n cmd\n .command('remove <channel>')\n .description('Remove a channel account')\n .option('--account <id>', 'Account id (default: \"default\")')\n .option('--dry-run', 'Show the change without writing', false)\n .option('--json', 'Emit a single JSON outcome line', false)\n .action(\n async (\n channel: string,\n opts: { account?: string; dryRun?: boolean; json?: boolean },\n ) => {\n const channelId = channel.trim().toLowerCase();\n const accountId = opts.account?.trim() || 'default';\n const dryRun = Boolean(opts.dryRun);\n const json = Boolean(opts.json);\n\n await runSetup({\n configPath: resolveConfigPathFromCommand(cmd),\n options: { dryRun, json },\n mutator: {\n domain: `channels.${channelId}`,\n target: accountId,\n action: 'remove',\n mutate(config) {\n return removeChannelAccount(config, channelId, accountId);\n },\n },\n });\n },\n );\n\n cmd\n .command('schema [channel]')\n .description('Print credential schemas for channels (for agents / UIs)')\n .option('--json', 'JSON output (default human-readable JSON)', false)\n .action((channel: string | undefined, opts: { json?: boolean }) => {\n ensureChannelRegistryForCli();\n const all = listChannelPlugins().map((p) => ({\n id: p.id,\n name: p.meta?.label ?? p.id,\n hasLogin: Boolean(p.cliLogin),\n configSchema: p.configSchema?.schema,\n }));\n const payload = {\n ok: true,\n channels: channel ? all.filter((c) => c.id === channel) : all,\n };\n if (opts.json) {\n process.stdout.write(JSON.stringify(payload) + '\\n');\n } else {\n console.log(JSON.stringify(payload, null, 2));\n }\n });\n\n cmd\n .command('pairing')\n .description('Approve DM pairing requests (updates channel allowFrom credential files)')\n .addCommand(\n new Command('approve')\n .description('Approve a pairing code shown to the user in Telegram / Feishu / Weixin DMs')\n .requiredOption('--channel <id>', 'telegram | feishu | weixin')\n .option('--account <id>', 'Bot account id from config', 'default')\n .argument('<code>', 'Pairing code from the user message')\n .action((code: string, options: { channel?: string; account?: string }) => {\n const ch = (options.channel ?? '').trim().toLowerCase() as PairingCliChannel;\n if (!PAIRING_CHANNELS.has(ch)) {\n console.error('Invalid --channel. Use: telegram, feishu, or weixin.');\n process.exitCode = 1;\n return;\n }\n const accountId = (options.account ?? 'default').trim() || 'default';\n const result = approveChannelPairingFromCli({\n channel: ch,\n accountId,\n code: String(code ?? '').trim(),\n });\n if (result.ok === false) {\n console.error(result.error);\n process.exitCode = 1;\n return;\n }\n console.log(`Approved. Sender id added to allowFrom store: ${result.senderId}`);\n }),\n );\n\n return cmd;\n}\n\n// ---------- helpers ----------\n\nconst TELEGRAM_TOKEN_PATTERN = /^\\d{5,}:[A-Za-z0-9_-]{30,}$/;\n\nexport function validateTelegramToken(token: string): boolean {\n return TELEGRAM_TOKEN_PATTERN.test(token.trim());\n}\n\nexport function maskToken(token: string): string {\n const t = token.trim();\n if (t.length <= 8) return '*'.repeat(t.length);\n return `${t.slice(0, 4)}…${t.slice(-4)}`;\n}\n\nexport function applyTelegramAccount(\n cfg: Config,\n accountId: string,\n botToken: string,\n enable: boolean,\n): Config {\n const channels = { ...((cfg.channels ?? {}) as Record<string, unknown>) };\n const tg = { ...((channels.telegram ?? {}) as Record<string, unknown>) };\n if (enable) tg.enabled = true;\n // Preserve the legacy single-account top-level field for backwards\n // compatibility with older config readers.\n tg.botToken = botToken;\n const accounts = { ...((tg.accounts ?? {}) as Record<string, Record<string, unknown>>) };\n accounts[accountId] = {\n ...(accounts[accountId] ?? {}),\n accountId,\n enabled: true,\n botToken,\n };\n tg.accounts = accounts;\n channels.telegram = tg;\n return { ...cfg, channels } as Config;\n}\n\nexport function removeChannelAccount(\n cfg: Config,\n channelId: string,\n accountId: string,\n): Config {\n if (channelId === 'feishu' || channelId === 'weixin' || channelId === 'telegram') {\n const channels = { ...((cfg.channels ?? {}) as Record<string, unknown>) };\n const ch = { ...((channels[channelId] ?? {}) as Record<string, unknown>) };\n const accounts = { ...((ch.accounts ?? {}) as Record<string, unknown>) };\n if (!(accountId in accounts)) {\n throw new SetupValidationError([\n { message: `No account \"${accountId}\" on channel \"${channelId}\".` },\n ]);\n }\n delete accounts[accountId];\n ch.accounts = accounts;\n if (Object.keys(accounts).length === 0) {\n ch.enabled = false;\n // Drop legacy top-level token alongside the last removed account.\n if (channelId === 'telegram') {\n delete ch.botToken;\n }\n }\n channels[channelId] = ch;\n return { ...cfg, channels } as Config;\n }\n throw new SetupValidationError([\n { message: `Unknown channel \"${channelId}\". Try: xopc channels list` },\n ]);\n}\n\nregister({\n id: 'channels',\n name: 'channels',\n description: 'Messaging channel login',\n factory: createChannelsCommand,\n metadata: {\n category: 'setup',\n examples: [\n 'xopc channels list',\n 'xopc channels add telegram --token 123456:ABC...',\n 'xopc channels remove telegram',\n 'xopc channels login',\n 'xopc channels pairing approve --channel feishu --account default ABC12XYZ',\n ],\n },\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"channels.js","names":[],"sources":["../../../../src/cli/commands/channels.ts"],"sourcesContent":["import { Command } from 'commander';\n\nimport { approveChannelPairingFromCli, type PairingCliChannel } from '../../channels/pairing/index.js';\nimport { resolveConfigPath } from '../../config/paths.js';\nimport {\n getChannelPlugin,\n listChannelPlugins,\n syncChannelPluginsFromManager,\n} from '../../channels/plugins/registry.js';\nimport { bundledChannelPlugins } from '../../generated/bundled-channel-plugins.js';\nimport { loadConfig } from '../../config/loader.js';\nimport type { Config } from '../../config/schema.js';\nimport { register, formatExamples, type CLIContext } from '../registry.js';\nimport { colors } from '../utils/colors.js';\n\nimport {\n SETUP_EXIT,\n SetupValidationError,\n emitOutcome,\n isPromptCancelled,\n promptSecret,\n runSetup,\n type SetupOutcome,\n} from './setup-shared/index.js';\n\nfunction ensureChannelRegistryForCli(): void {\n if (listChannelPlugins().length === 0) {\n syncChannelPluginsFromManager(bundledChannelPlugins);\n }\n}\n\nfunction resolveConfigPathFromCommand(command: Command): string {\n const root =\n command.parent?.parent && command.parent.parent instanceof Command\n ? command.parent.parent\n : command.parent && command.parent instanceof Command\n ? command.parent\n : null;\n const globalOpts = (root && typeof root.opts === 'function'\n ? (root.opts() as { config?: string })\n : {}) as { config?: string };\n return (\n globalOpts.config?.trim() ||\n process.env.XOPC_CONFIG_PATH?.trim() ||\n process.env.XOPC_CONFIG?.trim() ||\n resolveConfigPath()\n );\n}\n\nfunction createChannelsCommand(ctx: CLIContext): Command {\n const cmd = new Command('channels')\n .description('Messaging channel login and credentials')\n .addHelpText(\n 'after',\n formatExamples([\n 'xopc channels login',\n 'xopc channels login --channel weixin',\n 'xopc channels login --channel feishu',\n 'xopc channels login --account my-bot-id',\n 'xopc channels pairing approve --channel telegram ABC12XYZ',\n ]),\n );\n\n const PAIRING_CHANNELS = new Set<PairingCliChannel>(['telegram', 'feishu', 'weixin']);\n\n cmd\n .command('login')\n .description('Log in with QR code or channel-specific credentials flow')\n .option(\n '--channel <id>',\n 'Channel id (auto-detected when only one login-capable channel is registered)',\n )\n .option('--account <id>', 'Optional account id when re-logging an existing bot')\n .option('--timeout <ms>', 'Max wait for scan (default 480000)', '480000')\n .option('--credentials-only', 'Only save token files; do not update xopc.json')\n .action(async (options, command) => {\n ensureChannelRegistryForCli();\n const explicitChannel = options.channel?.trim?.();\n let channelId: string;\n if (explicitChannel) {\n channelId = explicitChannel;\n } else {\n const loginCapable = listChannelPlugins().filter((p) => p.cliLogin);\n if (loginCapable.length === 1) {\n channelId = loginCapable[0].id;\n console.log(`Auto-detected channel: ${channelId}`);\n } else if (loginCapable.length === 0) {\n console.error('No channels with login support found.');\n process.exitCode = 1;\n return;\n } else {\n console.error(\n `Multiple channels support login: ${loginCapable.map((p) => p.id).join(', ')}. ` +\n 'Use --channel <id> to specify.',\n );\n process.exitCode = 1;\n return;\n }\n }\n const plugin = getChannelPlugin(channelId);\n if (!plugin?.cliLogin) {\n console.error(`Channel \"${channelId}\" does not support CLI login.`);\n const capable = listChannelPlugins()\n .filter((p) => p.cliLogin)\n .map((p) => p.id);\n if (capable.length > 0) {\n console.error(`Channels with login support: ${capable.join(', ')}`);\n }\n process.exitCode = 1;\n return;\n }\n\n const configPath = resolveConfigPathFromCommand(command);\n const timeoutMs = Math.max(60_000, Number.parseInt(String(options.timeout), 10) || 480_000);\n const verbose = ctx.isVerbose;\n\n const result = await plugin.cliLogin.runLogin({\n configPath,\n verbose,\n timeoutMs,\n accountId: options.account?.trim() || undefined,\n writeConfig: !options.credentialsOnly,\n });\n\n if (!result.ok) {\n if (result.cancelled) {\n process.exitCode = 130;\n return;\n }\n console.error(result.message || 'Login failed');\n process.exitCode = 1;\n }\n });\n\n cmd\n .command('list')\n .description('List configured channels and their account status')\n .option('--json', 'Output as JSON for agents/UIs', false)\n .action((opts: { json?: boolean }) => {\n ensureChannelRegistryForCli();\n const cfg = loadConfig(resolveConfigPathFromCommand(cmd));\n const channelsCfg = (cfg.channels ?? {}) as Record<string, Record<string, unknown>>;\n const entries = listChannelPlugins().map((plugin) => {\n const raw = channelsCfg[plugin.id] ?? {};\n const enabled = raw.enabled === true;\n const accounts = (raw.accounts ?? {}) as Record<string, Record<string, unknown>>;\n const accountSummaries = Object.entries(accounts).map(([id, acct]) => ({\n accountId: id,\n enabled: acct.enabled !== false,\n configured: typeof acct.botToken === 'string'\n ? Boolean((acct.botToken as string).trim())\n : false,\n }));\n return {\n id: plugin.id,\n name: plugin.meta?.label ?? plugin.id,\n enabled,\n hasLogin: Boolean(plugin.cliLogin),\n accountCount: accountSummaries.length,\n accounts: accountSummaries,\n };\n });\n if (opts.json) {\n process.stdout.write(JSON.stringify({ ok: true, channels: entries }) + '\\n');\n return;\n }\n console.log('');\n console.log(colors.bold('CHANNELS'));\n const idWidth = Math.max(...entries.map((e) => e.id.length));\n for (const e of entries) {\n const status = e.enabled\n ? colors.green('● enabled')\n : colors.gray('○ disabled');\n const accts = e.accountCount > 0 ? colors.gray(` (${e.accountCount} accounts)`) : '';\n console.log(` ${e.id.padEnd(idWidth)} ${status} ${colors.gray(e.name)}${accts}`);\n }\n console.log('');\n console.log(colors.gray('Use: xopc channels add <id> [--token <token>]'));\n });\n\n cmd\n .command('add <channel>')\n .description('Add or update a channel account (currently: telegram)')\n .option('--token <value>', 'Bot token (Telegram). Prompts securely if omitted.')\n .option('--account <id>', 'Account id (default: \"default\")')\n .option('--enable', 'Enable the channel after writing (default: true)', true)\n .option('--dry-run', 'Show the change without writing', false)\n .option('--json', 'Emit a single JSON outcome line', false)\n .action(\n async (\n channel: string,\n opts: {\n token?: string;\n account?: string;\n enable?: boolean;\n dryRun?: boolean;\n json?: boolean;\n },\n ) => {\n const channelId = channel.trim().toLowerCase();\n const accountId = opts.account?.trim() || 'default';\n const dryRun = Boolean(opts.dryRun);\n const json = Boolean(opts.json);\n\n if (channelId === 'feishu' || channelId === 'weixin') {\n const outcome: SetupOutcome = {\n ok: false,\n action: 'add',\n domain: `channels.${channelId}`,\n target: accountId,\n changedPaths: [],\n dryRun,\n errors: [\n {\n message:\n channelId === 'weixin'\n ? 'Weixin uses interactive QR login. Run: xopc channels login --channel weixin'\n : 'Feishu add via CLI is not yet implemented. Edit `~/.xopc/xopc.json` (channels.feishu) or run: xopc onboard --channels',\n },\n ],\n };\n emitOutcome(outcome, json);\n process.exitCode = SETUP_EXIT.ERROR;\n return;\n }\n\n if (channelId !== 'telegram') {\n const outcome: SetupOutcome = {\n ok: false,\n action: 'add',\n domain: `channels.${channelId}`,\n target: accountId,\n changedPaths: [],\n dryRun,\n errors: [{ message: `Unknown channel \"${channelId}\". Try: xopc channels list` }],\n };\n emitOutcome(outcome, json);\n process.exitCode = SETUP_EXIT.ERROR;\n return;\n }\n\n let token = opts.token?.trim();\n if (!token) {\n token = process.env.TELEGRAM_BOT_TOKEN?.trim();\n }\n if (!token) {\n try {\n token = await promptSecret('Telegram bot token (from @BotFather):');\n } catch (error) {\n if (isPromptCancelled(error)) {\n const outcome: SetupOutcome = {\n ok: false,\n action: 'add',\n domain: 'channels.telegram',\n target: accountId,\n changedPaths: [],\n dryRun,\n errors: [{ message: 'Cancelled by user' }],\n };\n emitOutcome(outcome, json);\n process.exitCode = SETUP_EXIT.CANCELLED;\n return;\n }\n throw error;\n }\n }\n\n if (!validateTelegramToken(token)) {\n const outcome: SetupOutcome = {\n ok: false,\n action: 'add',\n domain: 'channels.telegram',\n target: accountId,\n changedPaths: [],\n dryRun,\n errors: [{ path: 'token', message: 'Token does not look like a Telegram bot token (expected `<digits>:<35+ chars>`).' }],\n };\n emitOutcome(outcome, json);\n process.exitCode = SETUP_EXIT.ERROR;\n return;\n }\n\n await runSetup({\n configPath: resolveConfigPathFromCommand(cmd),\n options: { dryRun, json },\n mutator: {\n domain: 'channels.telegram',\n target: accountId,\n action: 'add',\n mutate(config) {\n return applyTelegramAccount(config, accountId, token!, opts.enable !== false);\n },\n resultValue: () => ({\n channel: 'telegram',\n accountId,\n tokenMask: maskToken(token!),\n }),\n notes: () => [\n opts.enable !== false\n ? 'Telegram channel enabled. Restart the gateway for the new bot to come online.'\n : 'Telegram credentials saved (channel disabled). Use `--enable` to start it.',\n ],\n },\n });\n },\n );\n\n cmd\n .command('remove <channel>')\n .description('Remove a channel account')\n .option('--account <id>', 'Account id (default: \"default\")')\n .option('--dry-run', 'Show the change without writing', false)\n .option('--json', 'Emit a single JSON outcome line', false)\n .action(\n async (\n channel: string,\n opts: { account?: string; dryRun?: boolean; json?: boolean },\n ) => {\n const channelId = channel.trim().toLowerCase();\n const accountId = opts.account?.trim() || 'default';\n const dryRun = Boolean(opts.dryRun);\n const json = Boolean(opts.json);\n\n await runSetup({\n configPath: resolveConfigPathFromCommand(cmd),\n options: { dryRun, json },\n mutator: {\n domain: `channels.${channelId}`,\n target: accountId,\n action: 'remove',\n mutate(config) {\n return removeChannelAccount(config, channelId, accountId);\n },\n },\n });\n },\n );\n\n cmd\n .command('schema [channel]')\n .description('Print credential schemas for channels (for agents / UIs)')\n .option('--json', 'JSON output (default human-readable JSON)', false)\n .action((channel: string | undefined, opts: { json?: boolean }) => {\n ensureChannelRegistryForCli();\n const all = listChannelPlugins().map((p) => ({\n id: p.id,\n name: p.meta?.label ?? p.id,\n hasLogin: Boolean(p.cliLogin),\n configSchema: p.configSchema?.schema,\n }));\n const payload = {\n ok: true,\n channels: channel ? all.filter((c) => c.id === channel) : all,\n };\n if (opts.json) {\n process.stdout.write(JSON.stringify(payload) + '\\n');\n } else {\n console.log(JSON.stringify(payload, null, 2));\n }\n });\n\n cmd\n .command('pairing')\n .description('Approve DM pairing requests (updates channel allowFrom credential files)')\n .addCommand(\n new Command('approve')\n .description('Approve a pairing code shown to the user in Telegram / Feishu / Weixin DMs')\n .requiredOption('--channel <id>', 'telegram | feishu | weixin')\n .option('--account <id>', 'Bot account id from config', 'default')\n .argument('<code>', 'Pairing code from the user message')\n .action((code: string, options: { channel?: string; account?: string }) => {\n const ch = (options.channel ?? '').trim().toLowerCase() as PairingCliChannel;\n if (!PAIRING_CHANNELS.has(ch)) {\n console.error('Invalid --channel. Use: telegram, feishu, or weixin.');\n process.exitCode = 1;\n return;\n }\n const accountId = (options.account ?? 'default').trim() || 'default';\n const result = approveChannelPairingFromCli({\n channel: ch,\n accountId,\n code: String(code ?? '').trim(),\n });\n if (result.ok === false) {\n console.error(result.error);\n process.exitCode = 1;\n return;\n }\n console.log(`Approved. Sender id added to allowFrom store: ${result.senderId}`);\n }),\n );\n\n return cmd;\n}\n\n// ---------- helpers ----------\n\nconst TELEGRAM_TOKEN_PATTERN = /^\\d{5,}:[A-Za-z0-9_-]{30,}$/;\n\nexport function validateTelegramToken(token: string): boolean {\n return TELEGRAM_TOKEN_PATTERN.test(token.trim());\n}\n\nexport function maskToken(token: string): string {\n const t = token.trim();\n if (t.length <= 8) return '*'.repeat(t.length);\n return `${t.slice(0, 4)}…${t.slice(-4)}`;\n}\n\nexport function applyTelegramAccount(\n cfg: Config,\n accountId: string,\n botToken: string,\n enable: boolean,\n): Config {\n const channels = { ...((cfg.channels ?? {}) as Record<string, unknown>) };\n const tg = { ...((channels.telegram ?? {}) as Record<string, unknown>) };\n if (enable) tg.enabled = true;\n const accounts = { ...((tg.accounts ?? {}) as Record<string, Record<string, unknown>>) };\n accounts[accountId] = {\n ...(accounts[accountId] ?? {}),\n accountId,\n enabled: true,\n botToken,\n };\n tg.accounts = accounts;\n channels.telegram = tg;\n return { ...cfg, channels } as Config;\n}\n\nexport function removeChannelAccount(\n cfg: Config,\n channelId: string,\n accountId: string,\n): Config {\n if (channelId === 'feishu' || channelId === 'weixin' || channelId === 'telegram') {\n const channels = { ...((cfg.channels ?? {}) as Record<string, unknown>) };\n const ch = { ...((channels[channelId] ?? {}) as Record<string, unknown>) };\n const accounts = { ...((ch.accounts ?? {}) as Record<string, unknown>) };\n if (!(accountId in accounts)) {\n throw new SetupValidationError([\n { message: `No account \"${accountId}\" on channel \"${channelId}\".` },\n ]);\n }\n delete accounts[accountId];\n ch.accounts = accounts;\n if (Object.keys(accounts).length === 0) {\n ch.enabled = false;\n }\n channels[channelId] = ch;\n return { ...cfg, channels } as Config;\n }\n throw new SetupValidationError([\n { message: `Unknown channel \"${channelId}\". Try: xopc channels list` },\n ]);\n}\n\nregister({\n id: 'channels',\n name: 'channels',\n description: 'Messaging channel login',\n factory: createChannelsCommand,\n metadata: {\n category: 'setup',\n examples: [\n 'xopc channels list',\n 'xopc channels add telegram --token 123456:ABC...',\n 'xopc channels remove telegram',\n 'xopc channels login',\n 'xopc channels pairing approve --channel feishu --account default ABC12XYZ',\n ],\n },\n});\n"],"mappings":"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"}
@@ -99,7 +99,7 @@ async function runServiceStop(params) {
99
99
  emitResult(options, {
100
100
  ok: true,
101
101
  result: "stopped",
102
- message: options.disable ? "Gateway stopped and disabled (will not respawn)." : "Gateway stop signal sent."
102
+ message: options.disable ? "Gateway stopped and disabled (will not respawn)." : "Gateway stopped."
103
103
  });
104
104
  } catch (err) {
105
105
  log.error({ err }, "Failed to stop gateway");
@@ -1 +1 @@
1
- {"version":3,"file":"lifecycle-core.js","names":[],"sources":["../../../../../src/cli/commands/gateway/lifecycle-core.ts"],"sourcesContent":["/**\n * Daemon lifecycle core — OpenClaw-aligned service start/stop/restart with onNotLoaded fallback.\n */\n\nimport { createLogger } from '../../../utils/logger.js';\nimport type {\n DaemonLifecycleOptions,\n GatewayService,\n} from '../../../daemon/types.js';\nimport {\n clearGatewayRestartIntentSync,\n writeGatewayRestartIntentSync,\n} from '../../../infra/restart.js';\n\nconst log = createLogger('DaemonLifecycle');\n\nexport type ServiceRecoveryResult = {\n result: 'started' | 'stopped' | 'restarted';\n message?: string;\n warnings?: string[];\n loaded?: boolean;\n};\n\ntype ServiceRecoveryContext = {\n json: boolean;\n fail: (message: string, hints?: string[], diagnostics?: string[], options?: DaemonLifecycleOptions) => void;\n};\n\ntype RestartPostCheckContext = {\n options: DaemonLifecycleOptions;\n fail: (message: string, hints?: string[], diagnostics?: string[], options?: DaemonLifecycleOptions) => void;\n};\n\nfunction emitResult(\n options: DaemonLifecycleOptions,\n payload: {\n ok: boolean;\n result?: string;\n message?: string;\n error?: string;\n hints?: string[];\n warnings?: string[];\n },\n): void {\n if (options.json) {\n console.log(JSON.stringify(payload, null, 2));\n return;\n }\n if (payload.ok) {\n if (payload.message) {\n console.log(`✅ ${payload.message}`);\n }\n for (const warning of payload.warnings ?? []) {\n console.warn(`⚠️ ${warning}`);\n }\n return;\n }\n if (payload.error) {\n console.error(`❌ ${payload.error}`);\n }\n for (const hint of payload.hints ?? []) {\n console.log(`💡 ${hint}`);\n }\n}\n\nfunction createFail(options: DaemonLifecycleOptions) {\n return (\n message: string,\n hints?: string[],\n diagnostics?: string[],\n opts: DaemonLifecycleOptions = options,\n ) => {\n emitResult(opts, { ok: false, error: message, hints });\n for (const line of diagnostics ?? []) {\n if (!opts.json) {\n console.log(` ${line}`);\n }\n }\n process.exit(1);\n };\n}\n\nasync function resolveServiceLoadedOrFail(\n service: GatewayService,\n fail: ServiceRecoveryContext['fail'],\n): Promise<boolean | null> {\n try {\n return await service.isLoaded({ env: process.env });\n } catch (err) {\n fail(`Gateway service check failed: ${String(err)}`);\n return null;\n }\n}\n\nasync function handleServiceNotLoaded(params: {\n service: GatewayService;\n renderStartHints: () => string[];\n options: DaemonLifecycleOptions;\n}): Promise<void> {\n emitResult(params.options, {\n ok: true,\n result: 'not-loaded',\n message: `Gateway service ${params.service.notLoadedText}.`,\n hints: params.renderStartHints(),\n });\n}\n\nasync function checkAndWarnTokenDrift(\n service: GatewayService,\n options: DaemonLifecycleOptions,\n): Promise<string[]> {\n const warnings: string[] = [];\n try {\n const command = await service.readCommand(process.env);\n const serviceToken = command?.environment?.XOPC_GATEWAY_TOKEN;\n if (!serviceToken) {\n return warnings;\n }\n const { loadConfig } = await import('../../../config/index.js');\n const { resolveConfigPath } = await import('../../../config/paths.js');\n const config = loadConfig(resolveConfigPath());\n const configToken = config?.gateway?.auth?.token;\n if (configToken && serviceToken !== configToken) {\n const warning =\n 'Token drift detected: service token differs from config. Run `xopc gateway service install --force` to sync.';\n warnings.push(warning);\n if (!options.json) {\n console.warn(`⚠️ ${warning}`);\n }\n }\n } catch {\n // Best-effort\n }\n return warnings;\n}\n\nexport async function runServiceStop(params: {\n service: GatewayService;\n opts?: DaemonLifecycleOptions;\n onNotLoaded?: (ctx: ServiceRecoveryContext) => Promise<ServiceRecoveryResult | null>;\n}): Promise<void> {\n const options = params.opts ?? {};\n const fail = createFail(options);\n const loaded = await resolveServiceLoadedOrFail(params.service, fail);\n if (loaded === null) {\n return;\n }\n\n if (!loaded) {\n try {\n const handled = await params.onNotLoaded?.({ json: Boolean(options.json), fail });\n if (handled) {\n emitResult(options, {\n ok: true,\n result: handled.result,\n message: handled.message,\n warnings: handled.warnings,\n });\n return;\n }\n } catch (err) {\n fail(`Gateway stop failed: ${String(err)}`);\n return;\n }\n await handleServiceNotLoaded({\n service: params.service,\n renderStartHints: () => ['xopc gateway service install', 'xopc gateway'],\n options,\n });\n return;\n }\n\n try {\n await params.service.stop({ env: process.env, disable: options.disable });\n emitResult(options, {\n ok: true,\n result: 'stopped',\n message: options.disable\n ? 'Gateway stopped and disabled (will not respawn).'\n : 'Gateway stop signal sent.',\n });\n } catch (err) {\n log.error({ err }, 'Failed to stop gateway');\n fail(`Gateway stop failed: ${String(err)}`);\n }\n}\n\nexport async function runServiceRestart(params: {\n service: GatewayService;\n opts?: DaemonLifecycleOptions;\n renderStartHints: () => string[];\n checkTokenDrift?: boolean;\n onNotLoaded?: (ctx: ServiceRecoveryContext) => Promise<ServiceRecoveryResult | null>;\n postRestartCheck?: (ctx: RestartPostCheckContext) => Promise<void>;\n}): Promise<void> {\n const options = params.opts ?? {};\n const fail = createFail(options);\n const warnings: string[] = [];\n let handledRecovery: ServiceRecoveryResult | null = null;\n\n const loaded = await resolveServiceLoadedOrFail(params.service, fail);\n if (loaded === null) {\n return;\n }\n\n if (!loaded) {\n try {\n handledRecovery = (await params.onNotLoaded?.({ json: Boolean(options.json), fail })) ?? null;\n } catch (err) {\n fail(`Gateway restart failed: ${String(err)}`);\n return;\n }\n if (!handledRecovery) {\n await handleServiceNotLoaded({\n service: params.service,\n renderStartHints: params.renderStartHints,\n options,\n });\n return;\n }\n if (handledRecovery.warnings?.length) {\n warnings.push(...handledRecovery.warnings);\n }\n }\n\n if (loaded && params.checkTokenDrift) {\n warnings.push(...(await checkAndWarnTokenDrift(params.service, options)));\n }\n\n try {\n if (loaded) {\n let wroteRestartIntent = false;\n const runtime = await params.service.readRuntime(process.env).catch(() => null);\n wroteRestartIntent = writeGatewayRestartIntentSync({ targetPid: runtime?.pid });\n try {\n await params.service.restart({ env: process.env });\n } catch (err) {\n if (wroteRestartIntent) {\n clearGatewayRestartIntentSync();\n }\n throw err;\n }\n }\n\n if (params.postRestartCheck) {\n await params.postRestartCheck({ options, fail });\n }\n\n if (options.wait) {\n const timeoutMs = parseWaitTimeout(options.wait);\n const { loadConfig } = await import('../../../config/index.js');\n const { resolveConfigPath } = await import('../../../config/paths.js');\n const config = loadConfig(resolveConfigPath());\n const port = typeof config.gateway?.port === 'number' ? config.gateway.port : 18790;\n const { waitForRestartHealth } = await import('./restart-health.js');\n const snapshot = await waitForRestartHealth({\n service: params.service,\n port,\n timeoutMs,\n onProgress: () => {\n if (!options.json) {\n process.stdout.write('.');\n }\n },\n });\n if (!options.json) {\n process.stdout.write('\\n');\n }\n if (!snapshot.healthy) {\n fail(`Restart health check failed: ${snapshot.waitOutcome ?? 'timeout'}`, [\n 'xopc gateway logs',\n ]);\n return;\n }\n emitResult(options, {\n ok: true,\n result: 'healthy',\n message: `Gateway restarted and healthy (pid ${snapshot.runtime?.pid ?? 'unknown'}, ${snapshot.elapsedMs ?? 0}ms).`,\n warnings: warnings.length ? warnings : undefined,\n });\n return;\n }\n\n emitResult(options, {\n ok: true,\n result: 'restarted',\n message: handledRecovery?.message ?? 'Gateway restart completed.',\n warnings: warnings.length ? warnings : undefined,\n });\n } catch (err) {\n log.error({ err }, 'Failed to restart gateway');\n fail(`Gateway restart failed: ${String(err)}`, params.renderStartHints());\n } finally {\n clearGatewayRestartIntentSync();\n }\n}\n\nexport async function executeDaemonUninstall(options: DaemonLifecycleOptions = {}): Promise<void> {\n const { resolveGatewayService, isDaemonAvailableAsync } = await import('../../../daemon/service.js');\n const available = await isDaemonAvailableAsync();\n if (!available) {\n emitResult(options, { ok: false, error: 'Daemon service not available on this platform' });\n process.exit(1);\n }\n const service = await resolveGatewayService();\n const loaded = await service.isLoaded({ env: process.env }).catch(() => false);\n if (loaded) {\n try {\n await service.stop({ env: process.env });\n } catch {\n // Best-effort\n }\n }\n try {\n await service.uninstall({ env: process.env });\n emitResult(options, { ok: true, result: 'uninstalled', message: 'Gateway service uninstalled.' });\n } catch (err) {\n log.error({ err }, 'Failed to uninstall gateway service');\n emitResult(options, { ok: false, error: `Failed to uninstall: ${String(err)}` });\n process.exit(1);\n }\n}\n\nfunction parseWaitTimeout(wait: string): number {\n const match = wait.match(/^(\\d+)(s|m|ms)?$/);\n if (!match) return 60_000;\n const value = parseInt(match[1], 10);\n const unit = match[2] || 's';\n switch (unit) {\n case 'ms':\n return value;\n case 'm':\n return value * 60_000;\n case 's':\n default:\n return value * 1000;\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"lifecycle-core.js","names":[],"sources":["../../../../../src/cli/commands/gateway/lifecycle-core.ts"],"sourcesContent":["/**\n * Daemon lifecycle core — OpenClaw-aligned service start/stop/restart with onNotLoaded fallback.\n */\n\nimport { createLogger } from '../../../utils/logger.js';\nimport type {\n DaemonLifecycleOptions,\n GatewayService,\n} from '../../../daemon/types.js';\nimport {\n clearGatewayRestartIntentSync,\n writeGatewayRestartIntentSync,\n} from '../../../infra/restart.js';\n\nconst log = createLogger('DaemonLifecycle');\n\nexport type ServiceRecoveryResult = {\n result: 'started' | 'stopped' | 'restarted';\n message?: string;\n warnings?: string[];\n loaded?: boolean;\n};\n\ntype ServiceRecoveryContext = {\n json: boolean;\n fail: (message: string, hints?: string[], diagnostics?: string[], options?: DaemonLifecycleOptions) => void;\n};\n\ntype RestartPostCheckContext = {\n options: DaemonLifecycleOptions;\n fail: (message: string, hints?: string[], diagnostics?: string[], options?: DaemonLifecycleOptions) => void;\n};\n\nfunction emitResult(\n options: DaemonLifecycleOptions,\n payload: {\n ok: boolean;\n result?: string;\n message?: string;\n error?: string;\n hints?: string[];\n warnings?: string[];\n },\n): void {\n if (options.json) {\n console.log(JSON.stringify(payload, null, 2));\n return;\n }\n if (payload.ok) {\n if (payload.message) {\n console.log(`✅ ${payload.message}`);\n }\n for (const warning of payload.warnings ?? []) {\n console.warn(`⚠️ ${warning}`);\n }\n return;\n }\n if (payload.error) {\n console.error(`❌ ${payload.error}`);\n }\n for (const hint of payload.hints ?? []) {\n console.log(`💡 ${hint}`);\n }\n}\n\nfunction createFail(options: DaemonLifecycleOptions) {\n return (\n message: string,\n hints?: string[],\n diagnostics?: string[],\n opts: DaemonLifecycleOptions = options,\n ) => {\n emitResult(opts, { ok: false, error: message, hints });\n for (const line of diagnostics ?? []) {\n if (!opts.json) {\n console.log(` ${line}`);\n }\n }\n process.exit(1);\n };\n}\n\nasync function resolveServiceLoadedOrFail(\n service: GatewayService,\n fail: ServiceRecoveryContext['fail'],\n): Promise<boolean | null> {\n try {\n return await service.isLoaded({ env: process.env });\n } catch (err) {\n fail(`Gateway service check failed: ${String(err)}`);\n return null;\n }\n}\n\nasync function handleServiceNotLoaded(params: {\n service: GatewayService;\n renderStartHints: () => string[];\n options: DaemonLifecycleOptions;\n}): Promise<void> {\n emitResult(params.options, {\n ok: true,\n result: 'not-loaded',\n message: `Gateway service ${params.service.notLoadedText}.`,\n hints: params.renderStartHints(),\n });\n}\n\nasync function checkAndWarnTokenDrift(\n service: GatewayService,\n options: DaemonLifecycleOptions,\n): Promise<string[]> {\n const warnings: string[] = [];\n try {\n const command = await service.readCommand(process.env);\n const serviceToken = command?.environment?.XOPC_GATEWAY_TOKEN;\n if (!serviceToken) {\n return warnings;\n }\n const { loadConfig } = await import('../../../config/index.js');\n const { resolveConfigPath } = await import('../../../config/paths.js');\n const config = loadConfig(resolveConfigPath());\n const configToken = config?.gateway?.auth?.token;\n if (configToken && serviceToken !== configToken) {\n const warning =\n 'Token drift detected: service token differs from config. Run `xopc gateway service install --force` to sync.';\n warnings.push(warning);\n if (!options.json) {\n console.warn(`⚠️ ${warning}`);\n }\n }\n } catch {\n // Best-effort\n }\n return warnings;\n}\n\nexport async function runServiceStop(params: {\n service: GatewayService;\n opts?: DaemonLifecycleOptions;\n onNotLoaded?: (ctx: ServiceRecoveryContext) => Promise<ServiceRecoveryResult | null>;\n}): Promise<void> {\n const options = params.opts ?? {};\n const fail = createFail(options);\n const loaded = await resolveServiceLoadedOrFail(params.service, fail);\n if (loaded === null) {\n return;\n }\n\n if (!loaded) {\n try {\n const handled = await params.onNotLoaded?.({ json: Boolean(options.json), fail });\n if (handled) {\n emitResult(options, {\n ok: true,\n result: handled.result,\n message: handled.message,\n warnings: handled.warnings,\n });\n return;\n }\n } catch (err) {\n fail(`Gateway stop failed: ${String(err)}`);\n return;\n }\n await handleServiceNotLoaded({\n service: params.service,\n renderStartHints: () => ['xopc gateway service install', 'xopc gateway'],\n options,\n });\n return;\n }\n\n try {\n await params.service.stop({ env: process.env, disable: options.disable });\n emitResult(options, {\n ok: true,\n result: 'stopped',\n message: options.disable\n ? 'Gateway stopped and disabled (will not respawn).'\n : 'Gateway stopped.',\n });\n } catch (err) {\n log.error({ err }, 'Failed to stop gateway');\n fail(`Gateway stop failed: ${String(err)}`);\n }\n}\n\nexport async function runServiceRestart(params: {\n service: GatewayService;\n opts?: DaemonLifecycleOptions;\n renderStartHints: () => string[];\n checkTokenDrift?: boolean;\n onNotLoaded?: (ctx: ServiceRecoveryContext) => Promise<ServiceRecoveryResult | null>;\n postRestartCheck?: (ctx: RestartPostCheckContext) => Promise<void>;\n}): Promise<void> {\n const options = params.opts ?? {};\n const fail = createFail(options);\n const warnings: string[] = [];\n let handledRecovery: ServiceRecoveryResult | null = null;\n\n const loaded = await resolveServiceLoadedOrFail(params.service, fail);\n if (loaded === null) {\n return;\n }\n\n if (!loaded) {\n try {\n handledRecovery = (await params.onNotLoaded?.({ json: Boolean(options.json), fail })) ?? null;\n } catch (err) {\n fail(`Gateway restart failed: ${String(err)}`);\n return;\n }\n if (!handledRecovery) {\n await handleServiceNotLoaded({\n service: params.service,\n renderStartHints: params.renderStartHints,\n options,\n });\n return;\n }\n if (handledRecovery.warnings?.length) {\n warnings.push(...handledRecovery.warnings);\n }\n }\n\n if (loaded && params.checkTokenDrift) {\n warnings.push(...(await checkAndWarnTokenDrift(params.service, options)));\n }\n\n try {\n if (loaded) {\n let wroteRestartIntent = false;\n const runtime = await params.service.readRuntime(process.env).catch(() => null);\n wroteRestartIntent = writeGatewayRestartIntentSync({ targetPid: runtime?.pid });\n try {\n await params.service.restart({ env: process.env });\n } catch (err) {\n if (wroteRestartIntent) {\n clearGatewayRestartIntentSync();\n }\n throw err;\n }\n }\n\n if (params.postRestartCheck) {\n await params.postRestartCheck({ options, fail });\n }\n\n if (options.wait) {\n const timeoutMs = parseWaitTimeout(options.wait);\n const { loadConfig } = await import('../../../config/index.js');\n const { resolveConfigPath } = await import('../../../config/paths.js');\n const config = loadConfig(resolveConfigPath());\n const port = typeof config.gateway?.port === 'number' ? config.gateway.port : 18790;\n const { waitForRestartHealth } = await import('./restart-health.js');\n const snapshot = await waitForRestartHealth({\n service: params.service,\n port,\n timeoutMs,\n onProgress: () => {\n if (!options.json) {\n process.stdout.write('.');\n }\n },\n });\n if (!options.json) {\n process.stdout.write('\\n');\n }\n if (!snapshot.healthy) {\n fail(`Restart health check failed: ${snapshot.waitOutcome ?? 'timeout'}`, [\n 'xopc gateway logs',\n ]);\n return;\n }\n emitResult(options, {\n ok: true,\n result: 'healthy',\n message: `Gateway restarted and healthy (pid ${snapshot.runtime?.pid ?? 'unknown'}, ${snapshot.elapsedMs ?? 0}ms).`,\n warnings: warnings.length ? warnings : undefined,\n });\n return;\n }\n\n emitResult(options, {\n ok: true,\n result: 'restarted',\n message: handledRecovery?.message ?? 'Gateway restart completed.',\n warnings: warnings.length ? warnings : undefined,\n });\n } catch (err) {\n log.error({ err }, 'Failed to restart gateway');\n fail(`Gateway restart failed: ${String(err)}`, params.renderStartHints());\n } finally {\n clearGatewayRestartIntentSync();\n }\n}\n\nexport async function executeDaemonUninstall(options: DaemonLifecycleOptions = {}): Promise<void> {\n const { resolveGatewayService, isDaemonAvailableAsync } = await import('../../../daemon/service.js');\n const available = await isDaemonAvailableAsync();\n if (!available) {\n emitResult(options, { ok: false, error: 'Daemon service not available on this platform' });\n process.exit(1);\n }\n const service = await resolveGatewayService();\n const loaded = await service.isLoaded({ env: process.env }).catch(() => false);\n if (loaded) {\n try {\n await service.stop({ env: process.env });\n } catch {\n // Best-effort\n }\n }\n try {\n await service.uninstall({ env: process.env });\n emitResult(options, { ok: true, result: 'uninstalled', message: 'Gateway service uninstalled.' });\n } catch (err) {\n log.error({ err }, 'Failed to uninstall gateway service');\n emitResult(options, { ok: false, error: `Failed to uninstall: ${String(err)}` });\n process.exit(1);\n }\n}\n\nfunction parseWaitTimeout(wait: string): number {\n const match = wait.match(/^(\\d+)(s|m|ms)?$/);\n if (!match) return 60_000;\n const value = parseInt(match[1], 10);\n const unit = match[2] || 's';\n switch (unit) {\n case 'ms':\n return value;\n case 'm':\n return value * 60_000;\n case 's':\n default:\n return value * 1000;\n }\n}\n"],"mappings":"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"}