@theowlops/boombot 0.0.0-stage โ†’ 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (544) hide show
  1. package/README.md +69 -3
  2. package/dist/LICENSE +21 -0
  3. package/dist/bin/boombot.js +16 -0
  4. package/dist/cli/api-key.js +123 -0
  5. package/dist/cli/bin-bun.js +17 -0
  6. package/dist/cli/bin.js +34 -0
  7. package/dist/cli/cli/cli-error.js +61 -0
  8. package/dist/cli/cli/dispatch.js +138 -0
  9. package/dist/cli/cli/help.js +83 -0
  10. package/dist/cli/cli/parser.js +89 -0
  11. package/dist/cli/cli/registry.js +185 -0
  12. package/dist/cli/cli/spec.js +41 -0
  13. package/dist/cli/cli/suggest.js +125 -0
  14. package/dist/cli/cli/version.js +30 -0
  15. package/dist/cli/cli/vocabulary.js +87 -0
  16. package/dist/cli/commands/doctor.js +365 -0
  17. package/dist/cli/commands/gateway.js +290 -0
  18. package/dist/cli/commands/migrate.js +270 -0
  19. package/dist/cli/commands/plugin.js +97 -0
  20. package/dist/cli/commands/setup.js +230 -0
  21. package/dist/cli/config-file.js +133 -0
  22. package/dist/cli/master-key.js +78 -0
  23. package/dist/cli/open-browser.js +59 -0
  24. package/dist/cli/web-assets.js +58 -0
  25. package/dist/gateway/auth.js +46 -0
  26. package/dist/gateway/config-format.js +202 -0
  27. package/dist/gateway/config.js +427 -0
  28. package/dist/gateway/context.js +1 -0
  29. package/dist/gateway/env-file.js +129 -0
  30. package/dist/gateway/events.js +172 -0
  31. package/dist/gateway/guard/tagging.js +505 -0
  32. package/dist/gateway/handlers/index.js +66 -0
  33. package/dist/gateway/handlers/memory.js +54 -0
  34. package/dist/gateway/handlers/providers.js +73 -0
  35. package/dist/gateway/handlers/registry.js +39 -0
  36. package/dist/gateway/handlers/sessions.js +188 -0
  37. package/dist/gateway/handlers/system.js +12 -0
  38. package/dist/gateway/handlers/tasks.js +191 -0
  39. package/dist/gateway/handlers/turn.js +100 -0
  40. package/dist/gateway/index.js +126 -0
  41. package/dist/gateway/main.js +23 -0
  42. package/dist/gateway/server.js +354 -0
  43. package/dist/gateway/services/context-limits.js +75 -0
  44. package/dist/gateway/services/history-normalizer.js +42 -0
  45. package/dist/gateway/services/image-pipeline.js +56 -0
  46. package/dist/gateway/services/key-roulette-client.js +43 -0
  47. package/dist/gateway/services/llm-client.js +593 -0
  48. package/dist/gateway/services/llm-decoder.js +200 -0
  49. package/dist/gateway/services/local-fallback.js +135 -0
  50. package/dist/gateway/services/memory.js +511 -0
  51. package/dist/gateway/services/persistent-memory.js +196 -0
  52. package/dist/gateway/services/providers.js +44 -0
  53. package/dist/gateway/services/tasks.js +237 -0
  54. package/dist/gateway/services/verification.js +124 -0
  55. package/dist/gateway/services/vision-payload.js +48 -0
  56. package/dist/gateway/services.js +257 -0
  57. package/dist/gateway/session-snapshot.js +19 -0
  58. package/dist/gateway/sessions.js +701 -0
  59. package/dist/gateway/static.js +239 -0
  60. package/dist/gateway/turns/clarify-tool.js +57 -0
  61. package/dist/gateway/turns/cronjob-tool.js +96 -0
  62. package/dist/gateway/turns/deferred-tools.js +154 -0
  63. package/dist/gateway/turns/execute-code-tool.js +84 -0
  64. package/dist/gateway/turns/file-tool-defs.js +178 -0
  65. package/dist/gateway/turns/file-tool-ops.js +178 -0
  66. package/dist/gateway/turns/file-tool-scan.js +182 -0
  67. package/dist/gateway/turns/file-tool.js +302 -0
  68. package/dist/gateway/turns/index.js +416 -0
  69. package/dist/gateway/turns/jev-middleware.js +91 -0
  70. package/dist/gateway/turns/local-reply.js +156 -0
  71. package/dist/gateway/turns/mcp-client.js +643 -0
  72. package/dist/gateway/turns/memory-tool.js +171 -0
  73. package/dist/gateway/turns/middleware.js +96 -0
  74. package/dist/gateway/turns/python-core.js +397 -0
  75. package/dist/gateway/turns/routing/index.js +3 -0
  76. package/dist/gateway/turns/routing/intent-classifier.js +60 -0
  77. package/dist/gateway/turns/routing/planner-adapter.js +91 -0
  78. package/dist/gateway/turns/routing/types.js +1 -0
  79. package/dist/gateway/turns/runtime-deps.js +228 -0
  80. package/dist/gateway/turns/self-config-tool.js +277 -0
  81. package/dist/gateway/turns/service-types.js +11 -0
  82. package/dist/gateway/turns/service.js +820 -0
  83. package/dist/gateway/turns/session-adapter.js +158 -0
  84. package/dist/gateway/turns/session-tool.js +49 -0
  85. package/dist/gateway/turns/shell-tool.js +190 -0
  86. package/dist/gateway/turns/skill-tool.js +239 -0
  87. package/dist/gateway/turns/spillover.js +201 -0
  88. package/dist/gateway/turns/subagent-tool.js +140 -0
  89. package/dist/gateway/turns/system-prompt.js +121 -0
  90. package/dist/gateway/turns/tool-schema.js +90 -0
  91. package/dist/gateway/turns/turn-utils.js +58 -0
  92. package/dist/gateway/turns/vision-tool.js +120 -0
  93. package/dist/gateway/types.js +1 -0
  94. package/dist/native/win32-x64/boombot-native-host.exe +0 -0
  95. package/dist/node_modules/@boombot/channels/handlers.d.ts +9 -0
  96. package/dist/node_modules/@boombot/channels/handlers.js +109 -0
  97. package/dist/node_modules/@boombot/channels/index.d.ts +12 -0
  98. package/dist/node_modules/@boombot/channels/index.js +12 -0
  99. package/dist/node_modules/@boombot/channels/package.json +9 -0
  100. package/dist/node_modules/@boombot/channels/service.d.ts +62 -0
  101. package/dist/node_modules/@boombot/channels/service.js +608 -0
  102. package/dist/node_modules/@boombot/channels/tool.d.ts +10 -0
  103. package/dist/node_modules/@boombot/channels/tool.js +46 -0
  104. package/dist/node_modules/@boombot/channels/transports/common/dedup.d.ts +17 -0
  105. package/dist/node_modules/@boombot/channels/transports/common/dedup.js +42 -0
  106. package/dist/node_modules/@boombot/channels/transports/common/handoff.d.ts +19 -0
  107. package/dist/node_modules/@boombot/channels/transports/common/handoff.js +32 -0
  108. package/dist/node_modules/@boombot/channels/transports/common/index.d.ts +3 -0
  109. package/dist/node_modules/@boombot/channels/transports/common/index.js +3 -0
  110. package/dist/node_modules/@boombot/channels/transports/common/types.d.ts +99 -0
  111. package/dist/node_modules/@boombot/channels/transports/common/types.js +54 -0
  112. package/dist/node_modules/@boombot/channels/transports/common.d.ts +2 -0
  113. package/dist/node_modules/@boombot/channels/transports/common.js +2 -0
  114. package/dist/node_modules/@boombot/channels/transports/dedup/index.d.ts +1 -0
  115. package/dist/node_modules/@boombot/channels/transports/dedup/index.js +1 -0
  116. package/dist/node_modules/@boombot/channels/transports/dedup.d.ts +2 -0
  117. package/dist/node_modules/@boombot/channels/transports/dedup.js +2 -0
  118. package/dist/node_modules/@boombot/channels/transports/discord/index.d.ts +1 -0
  119. package/dist/node_modules/@boombot/channels/transports/discord/index.js +1 -0
  120. package/dist/node_modules/@boombot/channels/transports/discord/transport.d.ts +36 -0
  121. package/dist/node_modules/@boombot/channels/transports/discord/transport.js +199 -0
  122. package/dist/node_modules/@boombot/channels/transports/discord.d.ts +2 -0
  123. package/dist/node_modules/@boombot/channels/transports/discord.js +2 -0
  124. package/dist/node_modules/@boombot/channels/transports/handoff.d.ts +2 -0
  125. package/dist/node_modules/@boombot/channels/transports/handoff.js +2 -0
  126. package/dist/node_modules/@boombot/channels/transports/messenger/index.d.ts +1 -0
  127. package/dist/node_modules/@boombot/channels/transports/messenger/index.js +1 -0
  128. package/dist/node_modules/@boombot/channels/transports/messenger/transport.d.ts +34 -0
  129. package/dist/node_modules/@boombot/channels/transports/messenger/transport.js +180 -0
  130. package/dist/node_modules/@boombot/channels/transports/messenger.d.ts +2 -0
  131. package/dist/node_modules/@boombot/channels/transports/messenger.js +2 -0
  132. package/dist/node_modules/@boombot/channels/transports/slack/index.d.ts +1 -0
  133. package/dist/node_modules/@boombot/channels/transports/slack/index.js +1 -0
  134. package/dist/node_modules/@boombot/channels/transports/slack/transport.d.ts +35 -0
  135. package/dist/node_modules/@boombot/channels/transports/slack/transport.js +183 -0
  136. package/dist/node_modules/@boombot/channels/transports/slack.d.ts +2 -0
  137. package/dist/node_modules/@boombot/channels/transports/slack.js +2 -0
  138. package/dist/node_modules/@boombot/channels/transports/streamer/index.d.ts +1 -0
  139. package/dist/node_modules/@boombot/channels/transports/streamer/index.js +1 -0
  140. package/dist/node_modules/@boombot/channels/transports/streamer/streamer.d.ts +31 -0
  141. package/dist/node_modules/@boombot/channels/transports/streamer/streamer.js +141 -0
  142. package/dist/node_modules/@boombot/channels/transports/streamer.d.ts +2 -0
  143. package/dist/node_modules/@boombot/channels/transports/streamer.js +2 -0
  144. package/dist/node_modules/@boombot/channels/transports/telegram/index.d.ts +1 -0
  145. package/dist/node_modules/@boombot/channels/transports/telegram/index.js +1 -0
  146. package/dist/node_modules/@boombot/channels/transports/telegram/transport.d.ts +75 -0
  147. package/dist/node_modules/@boombot/channels/transports/telegram/transport.js +395 -0
  148. package/dist/node_modules/@boombot/channels/transports/telegram.d.ts +2 -0
  149. package/dist/node_modules/@boombot/channels/transports/telegram.js +2 -0
  150. package/dist/node_modules/@boombot/channels/transports/tiktok/index.d.ts +1 -0
  151. package/dist/node_modules/@boombot/channels/transports/tiktok/index.js +1 -0
  152. package/dist/node_modules/@boombot/channels/transports/tiktok/transport.d.ts +27 -0
  153. package/dist/node_modules/@boombot/channels/transports/tiktok/transport.js +97 -0
  154. package/dist/node_modules/@boombot/channels/transports/tiktok.d.ts +2 -0
  155. package/dist/node_modules/@boombot/channels/transports/tiktok.js +2 -0
  156. package/dist/node_modules/@boombot/channels/transports/whatsapp/index.d.ts +1 -0
  157. package/dist/node_modules/@boombot/channels/transports/whatsapp/index.js +1 -0
  158. package/dist/node_modules/@boombot/channels/transports/whatsapp/transport.d.ts +38 -0
  159. package/dist/node_modules/@boombot/channels/transports/whatsapp/transport.js +189 -0
  160. package/dist/node_modules/@boombot/channels/transports/whatsapp.d.ts +2 -0
  161. package/dist/node_modules/@boombot/channels/transports/whatsapp.js +2 -0
  162. package/dist/node_modules/@boombot/channels/transports/zalo/index.d.ts +2 -0
  163. package/dist/node_modules/@boombot/channels/transports/zalo/index.js +2 -0
  164. package/dist/node_modules/@boombot/channels/transports/zalo/oa.d.ts +28 -0
  165. package/dist/node_modules/@boombot/channels/transports/zalo/oa.js +94 -0
  166. package/dist/node_modules/@boombot/channels/transports/zalo/personal.d.ts +106 -0
  167. package/dist/node_modules/@boombot/channels/transports/zalo/personal.js +171 -0
  168. package/dist/node_modules/@boombot/channels/transports/zalo-personal.d.ts +2 -0
  169. package/dist/node_modules/@boombot/channels/transports/zalo-personal.js +2 -0
  170. package/dist/node_modules/@boombot/channels/transports/zalo.d.ts +2 -0
  171. package/dist/node_modules/@boombot/channels/transports/zalo.js +2 -0
  172. package/dist/node_modules/@boombot/core/agent-core/index.d.ts +12 -0
  173. package/dist/node_modules/@boombot/core/agent-core/index.js +12 -0
  174. package/dist/node_modules/@boombot/core/agent-core/middleware.d.ts +60 -0
  175. package/dist/node_modules/@boombot/core/agent-core/middleware.js +152 -0
  176. package/dist/node_modules/@boombot/core/agent-core/routing.d.ts +128 -0
  177. package/dist/node_modules/@boombot/core/agent-core/routing.js +437 -0
  178. package/dist/node_modules/@boombot/core/agent-core/runtime-deps.d.ts +88 -0
  179. package/dist/node_modules/@boombot/core/agent-core/runtime-deps.js +8 -0
  180. package/dist/node_modules/@boombot/core/agent-core/tool-errors.d.ts +44 -0
  181. package/dist/node_modules/@boombot/core/agent-core/tool-errors.js +60 -0
  182. package/dist/node_modules/@boombot/core/agent-core/turn-loop.d.ts +21 -0
  183. package/dist/node_modules/@boombot/core/agent-core/turn-loop.js +561 -0
  184. package/dist/node_modules/@boombot/core/agent-core/types.d.ts +189 -0
  185. package/dist/node_modules/@boombot/core/agent-core/types.js +7 -0
  186. package/dist/node_modules/@boombot/core/compaction/compactor.d.ts +19 -0
  187. package/dist/node_modules/@boombot/core/compaction/compactor.js +127 -0
  188. package/dist/node_modules/@boombot/core/compaction/estimator.d.ts +10 -0
  189. package/dist/node_modules/@boombot/core/compaction/estimator.js +48 -0
  190. package/dist/node_modules/@boombot/core/compaction/index.d.ts +3 -0
  191. package/dist/node_modules/@boombot/core/compaction/index.js +3 -0
  192. package/dist/node_modules/@boombot/core/compaction/types.d.ts +31 -0
  193. package/dist/node_modules/@boombot/core/compaction/types.js +7 -0
  194. package/dist/node_modules/@boombot/core/index.d.ts +9 -0
  195. package/dist/node_modules/@boombot/core/index.js +9 -0
  196. package/dist/node_modules/@boombot/core/jev/index.d.ts +2 -0
  197. package/dist/node_modules/@boombot/core/jev/index.js +2 -0
  198. package/dist/node_modules/@boombot/core/jev/jev.d.ts +30 -0
  199. package/dist/node_modules/@boombot/core/jev/jev.js +164 -0
  200. package/dist/node_modules/@boombot/core/jev/types.d.ts +22 -0
  201. package/dist/node_modules/@boombot/core/jev/types.js +1 -0
  202. package/dist/node_modules/@boombot/core/memory/consolidation/budget.d.ts +63 -0
  203. package/dist/node_modules/@boombot/core/memory/consolidation/budget.js +94 -0
  204. package/dist/node_modules/@boombot/core/memory/consolidation/consolidator.d.ts +152 -0
  205. package/dist/node_modules/@boombot/core/memory/consolidation/consolidator.js +318 -0
  206. package/dist/node_modules/@boombot/core/memory/consolidation/index.d.ts +12 -0
  207. package/dist/node_modules/@boombot/core/memory/consolidation/index.js +12 -0
  208. package/dist/node_modules/@boombot/core/memory/curation.d.ts +86 -0
  209. package/dist/node_modules/@boombot/core/memory/curation.js +120 -0
  210. package/dist/node_modules/@boombot/core/memory/errors.d.ts +12 -0
  211. package/dist/node_modules/@boombot/core/memory/errors.js +20 -0
  212. package/dist/node_modules/@boombot/core/memory/hybrid-search.d.ts +29 -0
  213. package/dist/node_modules/@boombot/core/memory/hybrid-search.js +52 -0
  214. package/dist/node_modules/@boombot/core/memory/index.d.ts +23 -0
  215. package/dist/node_modules/@boombot/core/memory/index.js +23 -0
  216. package/dist/node_modules/@boombot/core/memory/indexer.d.ts +47 -0
  217. package/dist/node_modules/@boombot/core/memory/indexer.js +138 -0
  218. package/dist/node_modules/@boombot/core/memory/lifecycle.d.ts +12 -0
  219. package/dist/node_modules/@boombot/core/memory/lifecycle.js +66 -0
  220. package/dist/node_modules/@boombot/core/memory/provider.d.ts +84 -0
  221. package/dist/node_modules/@boombot/core/memory/provider.js +117 -0
  222. package/dist/node_modules/@boombot/core/memory/recall.d.ts +7 -0
  223. package/dist/node_modules/@boombot/core/memory/recall.js +111 -0
  224. package/dist/node_modules/@boombot/core/memory/scope-resolver.d.ts +27 -0
  225. package/dist/node_modules/@boombot/core/memory/scope-resolver.js +60 -0
  226. package/dist/node_modules/@boombot/core/memory/search.d.ts +37 -0
  227. package/dist/node_modules/@boombot/core/memory/search.js +158 -0
  228. package/dist/node_modules/@boombot/core/memory/store.d.ts +60 -0
  229. package/dist/node_modules/@boombot/core/memory/store.js +289 -0
  230. package/dist/node_modules/@boombot/core/memory/types.d.ts +121 -0
  231. package/dist/node_modules/@boombot/core/memory/types.js +21 -0
  232. package/dist/node_modules/@boombot/core/memory/vector-index.d.ts +34 -0
  233. package/dist/node_modules/@boombot/core/memory/vector-index.js +65 -0
  234. package/dist/node_modules/@boombot/core/package.json +9 -0
  235. package/dist/node_modules/@boombot/core/reflection/classifier.d.ts +13 -0
  236. package/dist/node_modules/@boombot/core/reflection/classifier.js +77 -0
  237. package/dist/node_modules/@boombot/core/reflection/index.d.ts +2 -0
  238. package/dist/node_modules/@boombot/core/reflection/index.js +2 -0
  239. package/dist/node_modules/@boombot/core/reflection/types.d.ts +13 -0
  240. package/dist/node_modules/@boombot/core/reflection/types.js +4 -0
  241. package/dist/node_modules/@boombot/core/research/config.d.ts +89 -0
  242. package/dist/node_modules/@boombot/core/research/config.js +206 -0
  243. package/dist/node_modules/@boombot/core/research/crawl.d.ts +36 -0
  244. package/dist/node_modules/@boombot/core/research/crawl.js +179 -0
  245. package/dist/node_modules/@boombot/core/research/factory.d.ts +26 -0
  246. package/dist/node_modules/@boombot/core/research/factory.js +46 -0
  247. package/dist/node_modules/@boombot/core/research/http.d.ts +87 -0
  248. package/dist/node_modules/@boombot/core/research/http.js +250 -0
  249. package/dist/node_modules/@boombot/core/research/index.d.ts +17 -0
  250. package/dist/node_modules/@boombot/core/research/index.js +10 -0
  251. package/dist/node_modules/@boombot/core/research/orchestrator.d.ts +113 -0
  252. package/dist/node_modules/@boombot/core/research/orchestrator.js +252 -0
  253. package/dist/node_modules/@boombot/core/research/parse/docx.d.ts +24 -0
  254. package/dist/node_modules/@boombot/core/research/parse/docx.js +183 -0
  255. package/dist/node_modules/@boombot/core/research/parse/guards.d.ts +89 -0
  256. package/dist/node_modules/@boombot/core/research/parse/guards.js +171 -0
  257. package/dist/node_modules/@boombot/core/research/parse/index.d.ts +68 -0
  258. package/dist/node_modules/@boombot/core/research/parse/index.js +219 -0
  259. package/dist/node_modules/@boombot/core/research/parse/pdf.d.ts +38 -0
  260. package/dist/node_modules/@boombot/core/research/parse/pdf.js +567 -0
  261. package/dist/node_modules/@boombot/core/research/providers/arxiv.d.ts +9 -0
  262. package/dist/node_modules/@boombot/core/research/providers/arxiv.js +72 -0
  263. package/dist/node_modules/@boombot/core/research/providers/brave.d.ts +11 -0
  264. package/dist/node_modules/@boombot/core/research/providers/brave.js +36 -0
  265. package/dist/node_modules/@boombot/core/research/providers/duckduckgo.d.ts +9 -0
  266. package/dist/node_modules/@boombot/core/research/providers/duckduckgo.js +78 -0
  267. package/dist/node_modules/@boombot/core/research/providers/exa.d.ts +11 -0
  268. package/dist/node_modules/@boombot/core/research/providers/exa.js +34 -0
  269. package/dist/node_modules/@boombot/core/research/providers/firecrawl.d.ts +16 -0
  270. package/dist/node_modules/@boombot/core/research/providers/firecrawl.js +47 -0
  271. package/dist/node_modules/@boombot/core/research/providers/index.d.ts +17 -0
  272. package/dist/node_modules/@boombot/core/research/providers/index.js +11 -0
  273. package/dist/node_modules/@boombot/core/research/providers/jina.d.ts +13 -0
  274. package/dist/node_modules/@boombot/core/research/providers/jina.js +114 -0
  275. package/dist/node_modules/@boombot/core/research/providers/openalex.d.ts +14 -0
  276. package/dist/node_modules/@boombot/core/research/providers/openalex.js +66 -0
  277. package/dist/node_modules/@boombot/core/research/providers/semantic-scholar.d.ts +9 -0
  278. package/dist/node_modules/@boombot/core/research/providers/semantic-scholar.js +56 -0
  279. package/dist/node_modules/@boombot/core/research/providers/tavily.d.ts +11 -0
  280. package/dist/node_modules/@boombot/core/research/providers/tavily.js +35 -0
  281. package/dist/node_modules/@boombot/core/research/providers/wikipedia.d.ts +12 -0
  282. package/dist/node_modules/@boombot/core/research/providers/wikipedia.js +47 -0
  283. package/dist/node_modules/@boombot/core/research/tools.d.ts +27 -0
  284. package/dist/node_modules/@boombot/core/research/tools.js +251 -0
  285. package/dist/node_modules/@boombot/core/research/types.d.ts +133 -0
  286. package/dist/node_modules/@boombot/core/research/types.js +8 -0
  287. package/dist/node_modules/@boombot/core/research/wiring.d.ts +25 -0
  288. package/dist/node_modules/@boombot/core/research/wiring.js +51 -0
  289. package/dist/node_modules/@boombot/core/scheduler/cron.d.ts +34 -0
  290. package/dist/node_modules/@boombot/core/scheduler/cron.js +147 -0
  291. package/dist/node_modules/@boombot/core/scheduler/evaluator.d.ts +42 -0
  292. package/dist/node_modules/@boombot/core/scheduler/evaluator.js +148 -0
  293. package/dist/node_modules/@boombot/core/scheduler/index.d.ts +15 -0
  294. package/dist/node_modules/@boombot/core/scheduler/index.js +15 -0
  295. package/dist/node_modules/@boombot/core/scheduler/registry.d.ts +81 -0
  296. package/dist/node_modules/@boombot/core/scheduler/registry.js +263 -0
  297. package/dist/node_modules/@boombot/core/scheduler/runner.d.ts +59 -0
  298. package/dist/node_modules/@boombot/core/scheduler/runner.js +257 -0
  299. package/dist/node_modules/@boombot/core/scheduler/store.d.ts +150 -0
  300. package/dist/node_modules/@boombot/core/scheduler/store.js +526 -0
  301. package/dist/node_modules/@boombot/core/scheduler/timezone.d.ts +35 -0
  302. package/dist/node_modules/@boombot/core/scheduler/timezone.js +96 -0
  303. package/dist/node_modules/@boombot/core/scheduler/types.d.ts +157 -0
  304. package/dist/node_modules/@boombot/core/scheduler/types.js +89 -0
  305. package/dist/node_modules/@boombot/core/task-runner/index.d.ts +7 -0
  306. package/dist/node_modules/@boombot/core/task-runner/index.js +5 -0
  307. package/dist/node_modules/@boombot/core/task-runner/planner.d.ts +37 -0
  308. package/dist/node_modules/@boombot/core/task-runner/planner.js +154 -0
  309. package/dist/node_modules/@boombot/core/task-runner/runner.d.ts +8 -0
  310. package/dist/node_modules/@boombot/core/task-runner/runner.js +564 -0
  311. package/dist/node_modules/@boombot/core/task-runner/store.d.ts +28 -0
  312. package/dist/node_modules/@boombot/core/task-runner/store.js +126 -0
  313. package/dist/node_modules/@boombot/core/task-runner/types.d.ts +138 -0
  314. package/dist/node_modules/@boombot/core/task-runner/types.js +11 -0
  315. package/dist/node_modules/@boombot/core/workflow/dag.d.ts +16 -0
  316. package/dist/node_modules/@boombot/core/workflow/dag.js +109 -0
  317. package/dist/node_modules/@boombot/core/workflow/engine.d.ts +34 -0
  318. package/dist/node_modules/@boombot/core/workflow/engine.js +304 -0
  319. package/dist/node_modules/@boombot/core/workflow/index.d.ts +3 -0
  320. package/dist/node_modules/@boombot/core/workflow/index.js +3 -0
  321. package/dist/node_modules/@boombot/core/workflow/types.d.ts +98 -0
  322. package/dist/node_modules/@boombot/core/workflow/types.js +21 -0
  323. package/dist/node_modules/@boombot/cronjobs/handlers.d.ts +11 -0
  324. package/dist/node_modules/@boombot/cronjobs/handlers.js +174 -0
  325. package/dist/node_modules/@boombot/cronjobs/index.d.ts +2 -0
  326. package/dist/node_modules/@boombot/cronjobs/index.js +2 -0
  327. package/dist/node_modules/@boombot/cronjobs/package.json +9 -0
  328. package/dist/node_modules/@boombot/cronjobs/service.d.ts +53 -0
  329. package/dist/node_modules/@boombot/cronjobs/service.js +200 -0
  330. package/dist/node_modules/@boombot/plugins/index.d.ts +2 -0
  331. package/dist/node_modules/@boombot/plugins/index.js +2 -0
  332. package/dist/node_modules/@boombot/plugins/installer.d.ts +45 -0
  333. package/dist/node_modules/@boombot/plugins/installer.js +487 -0
  334. package/dist/node_modules/@boombot/plugins/manager.d.ts +23 -0
  335. package/dist/node_modules/@boombot/plugins/manager.js +243 -0
  336. package/dist/node_modules/@boombot/plugins/package.json +9 -0
  337. package/dist/node_modules/@boombot/protocol/gateway-protocol/commands-channels.d.ts +125 -0
  338. package/dist/node_modules/@boombot/protocol/gateway-protocol/commands-channels.js +76 -0
  339. package/dist/node_modules/@boombot/protocol/gateway-protocol/commands-plugins.d.ts +220 -0
  340. package/dist/node_modules/@boombot/protocol/gateway-protocol/commands-plugins.js +60 -0
  341. package/dist/node_modules/@boombot/protocol/gateway-protocol/commands-scheduler.d.ts +33 -0
  342. package/dist/node_modules/@boombot/protocol/gateway-protocol/commands-scheduler.js +25 -0
  343. package/dist/node_modules/@boombot/protocol/gateway-protocol/commands.d.ts +4349 -0
  344. package/dist/node_modules/@boombot/protocol/gateway-protocol/commands.js +563 -0
  345. package/dist/node_modules/@boombot/protocol/gateway-protocol/envelope.d.ts +32 -0
  346. package/dist/node_modules/@boombot/protocol/gateway-protocol/envelope.js +61 -0
  347. package/dist/node_modules/@boombot/protocol/gateway-protocol/errors.d.ts +13 -0
  348. package/dist/node_modules/@boombot/protocol/gateway-protocol/errors.js +16 -0
  349. package/dist/node_modules/@boombot/protocol/gateway-protocol/events.d.ts +567 -0
  350. package/dist/node_modules/@boombot/protocol/gateway-protocol/events.js +395 -0
  351. package/dist/node_modules/@boombot/protocol/gateway-protocol/index.d.ts +20 -0
  352. package/dist/node_modules/@boombot/protocol/gateway-protocol/index.js +13 -0
  353. package/dist/node_modules/@boombot/protocol/gateway-protocol/types.d.ts +91 -0
  354. package/dist/node_modules/@boombot/protocol/gateway-protocol/types.js +7 -0
  355. package/dist/node_modules/@boombot/protocol/index.d.ts +4 -0
  356. package/dist/node_modules/@boombot/protocol/index.js +3 -0
  357. package/dist/node_modules/@boombot/protocol/package.json +9 -0
  358. package/dist/node_modules/@boombot/protocol/plugin-sdk/channel.d.ts +41 -0
  359. package/dist/node_modules/@boombot/protocol/plugin-sdk/channel.js +17 -0
  360. package/dist/node_modules/@boombot/protocol/plugin-sdk/index.d.ts +10 -0
  361. package/dist/node_modules/@boombot/protocol/plugin-sdk/index.js +10 -0
  362. package/dist/node_modules/@boombot/protocol/plugin-sdk/manifest.d.ts +58 -0
  363. package/dist/node_modules/@boombot/protocol/plugin-sdk/manifest.js +116 -0
  364. package/dist/node_modules/@boombot/protocol/plugin-sdk/plugin.d.ts +25 -0
  365. package/dist/node_modules/@boombot/protocol/plugin-sdk/plugin.js +1 -0
  366. package/dist/node_modules/@boombot/protocol/providers/cipher.d.ts +7 -0
  367. package/dist/node_modules/@boombot/protocol/providers/cipher.js +52 -0
  368. package/dist/node_modules/@boombot/protocol/providers/index.d.ts +19 -0
  369. package/dist/node_modules/@boombot/protocol/providers/index.js +19 -0
  370. package/dist/node_modules/@boombot/protocol/providers/key-roulette.d.ts +79 -0
  371. package/dist/node_modules/@boombot/protocol/providers/key-roulette.js +81 -0
  372. package/dist/node_modules/@boombot/protocol/providers/probe.d.ts +29 -0
  373. package/dist/node_modules/@boombot/protocol/providers/probe.js +96 -0
  374. package/dist/node_modules/@boombot/protocol/providers/profiles/anthropic.d.ts +11 -0
  375. package/dist/node_modules/@boombot/protocol/providers/profiles/anthropic.js +11 -0
  376. package/dist/node_modules/@boombot/protocol/providers/profiles/custom.d.ts +22 -0
  377. package/dist/node_modules/@boombot/protocol/providers/profiles/custom.js +222 -0
  378. package/dist/node_modules/@boombot/protocol/providers/profiles/deepseek.d.ts +7 -0
  379. package/dist/node_modules/@boombot/protocol/providers/profiles/deepseek.js +9 -0
  380. package/dist/node_modules/@boombot/protocol/providers/profiles/google.d.ts +10 -0
  381. package/dist/node_modules/@boombot/protocol/providers/profiles/google.js +10 -0
  382. package/dist/node_modules/@boombot/protocol/providers/profiles/index.d.ts +31 -0
  383. package/dist/node_modules/@boombot/protocol/providers/profiles/index.js +56 -0
  384. package/dist/node_modules/@boombot/protocol/providers/profiles/local.d.ts +10 -0
  385. package/dist/node_modules/@boombot/protocol/providers/profiles/local.js +11 -0
  386. package/dist/node_modules/@boombot/protocol/providers/profiles/mistral.d.ts +7 -0
  387. package/dist/node_modules/@boombot/protocol/providers/profiles/mistral.js +9 -0
  388. package/dist/node_modules/@boombot/protocol/providers/profiles/openai-compatible.d.ts +10 -0
  389. package/dist/node_modules/@boombot/protocol/providers/profiles/openai-compatible.js +10 -0
  390. package/dist/node_modules/@boombot/protocol/providers/profiles/openai.d.ts +7 -0
  391. package/dist/node_modules/@boombot/protocol/providers/profiles/openai.js +11 -0
  392. package/dist/node_modules/@boombot/protocol/providers/profiles/types.d.ts +58 -0
  393. package/dist/node_modules/@boombot/protocol/providers/profiles/types.js +1 -0
  394. package/dist/node_modules/@boombot/protocol/providers/profiles/xai.d.ts +7 -0
  395. package/dist/node_modules/@boombot/protocol/providers/profiles/xai.js +10 -0
  396. package/dist/node_modules/@boombot/protocol/providers/request-profile.d.ts +102 -0
  397. package/dist/node_modules/@boombot/protocol/providers/request-profile.js +118 -0
  398. package/dist/node_modules/@boombot/protocol/providers/service.d.ts +2 -0
  399. package/dist/node_modules/@boombot/protocol/providers/service.js +418 -0
  400. package/dist/node_modules/@boombot/protocol/providers/types.d.ts +141 -0
  401. package/dist/node_modules/@boombot/protocol/providers/types.js +16 -0
  402. package/dist/node_modules/@boombot/sandbox/fs-path.d.ts +13 -0
  403. package/dist/node_modules/@boombot/sandbox/fs-path.js +79 -0
  404. package/dist/node_modules/@boombot/sandbox/index.d.ts +13 -0
  405. package/dist/node_modules/@boombot/sandbox/index.js +13 -0
  406. package/dist/node_modules/@boombot/sandbox/native.d.ts +39 -0
  407. package/dist/node_modules/@boombot/sandbox/native.js +224 -0
  408. package/dist/node_modules/@boombot/sandbox/package.json +9 -0
  409. package/dist/node_modules/@boombot/sandbox/sandbox.d.ts +92 -0
  410. package/dist/node_modules/@boombot/sandbox/sandbox.js +229 -0
  411. package/dist/node_modules/@boombot/security/harness-guard/boundary.d.ts +22 -0
  412. package/dist/node_modules/@boombot/security/harness-guard/boundary.js +105 -0
  413. package/dist/node_modules/@boombot/security/harness-guard/gates.d.ts +90 -0
  414. package/dist/node_modules/@boombot/security/harness-guard/gates.js +791 -0
  415. package/dist/node_modules/@boombot/security/harness-guard/index.d.ts +23 -0
  416. package/dist/node_modules/@boombot/security/harness-guard/index.js +23 -0
  417. package/dist/node_modules/@boombot/security/harness-guard/policy.d.ts +20 -0
  418. package/dist/node_modules/@boombot/security/harness-guard/policy.js +70 -0
  419. package/dist/node_modules/@boombot/security/harness-guard/sanitizer.d.ts +53 -0
  420. package/dist/node_modules/@boombot/security/harness-guard/sanitizer.js +145 -0
  421. package/dist/node_modules/@boombot/security/harness-guard/types.d.ts +71 -0
  422. package/dist/node_modules/@boombot/security/harness-guard/types.js +10 -0
  423. package/dist/node_modules/@boombot/security/harness-guard/verifier.d.ts +59 -0
  424. package/dist/node_modules/@boombot/security/harness-guard/verifier.js +169 -0
  425. package/dist/node_modules/@boombot/security/index.d.ts +5 -0
  426. package/dist/node_modules/@boombot/security/index.js +9 -0
  427. package/dist/node_modules/@boombot/security/package.json +9 -0
  428. package/dist/node_modules/@boombot/security/policy/approval.d.ts +117 -0
  429. package/dist/node_modules/@boombot/security/policy/approval.js +223 -0
  430. package/dist/node_modules/@boombot/security/reasoning-guard/claim-extractor.d.ts +10 -0
  431. package/dist/node_modules/@boombot/security/reasoning-guard/claim-extractor.js +124 -0
  432. package/dist/node_modules/@boombot/security/reasoning-guard/evidence-ledger.d.ts +25 -0
  433. package/dist/node_modules/@boombot/security/reasoning-guard/evidence-ledger.js +70 -0
  434. package/dist/node_modules/@boombot/security/reasoning-guard/gate.d.ts +38 -0
  435. package/dist/node_modules/@boombot/security/reasoning-guard/gate.js +61 -0
  436. package/dist/node_modules/@boombot/security/reasoning-guard/grounding-check.d.ts +12 -0
  437. package/dist/node_modules/@boombot/security/reasoning-guard/grounding-check.js +234 -0
  438. package/dist/node_modules/@boombot/security/reasoning-guard/index.d.ts +17 -0
  439. package/dist/node_modules/@boombot/security/reasoning-guard/index.js +13 -0
  440. package/dist/node_modules/@boombot/security/reasoning-guard/policy.d.ts +15 -0
  441. package/dist/node_modules/@boombot/security/reasoning-guard/policy.js +39 -0
  442. package/dist/node_modules/@boombot/security/reasoning-guard/types.d.ts +46 -0
  443. package/dist/node_modules/@boombot/security/reasoning-guard/types.js +7 -0
  444. package/dist/node_modules/@boombot/security/security/approval.d.ts +63 -0
  445. package/dist/node_modules/@boombot/security/security/approval.js +140 -0
  446. package/dist/node_modules/@boombot/security/security/audit.d.ts +39 -0
  447. package/dist/node_modules/@boombot/security/security/audit.js +70 -0
  448. package/dist/node_modules/@boombot/security/security/index.d.ts +12 -0
  449. package/dist/node_modules/@boombot/security/security/index.js +12 -0
  450. package/dist/node_modules/@boombot/security/security/trust.d.ts +79 -0
  451. package/dist/node_modules/@boombot/security/security/trust.js +87 -0
  452. package/dist/package.json +5 -0
  453. package/dist/proto/agent_core.proto +56 -0
  454. package/dist/python/agent-core-python/pyproject.toml +40 -0
  455. package/dist/python/agent-core-python/src/boombot_agent/__init__.py +5 -0
  456. package/dist/python/agent-core-python/src/boombot_agent/generated/__init__.py +0 -0
  457. package/dist/python/agent-core-python/src/boombot_agent/generated/agent_core_pb2.py +54 -0
  458. package/dist/python/agent-core-python/src/boombot_agent/generated/agent_core_pb2_grpc.py +140 -0
  459. package/dist/python/agent-core-python/src/boombot_agent/rpc.py +265 -0
  460. package/dist/python/agent-core-python/src/boombot_agent/turn.py +577 -0
  461. package/dist/python/agent-core-python/src/boombot_ml/__init__.py +12 -0
  462. package/dist/python/agent-core-python/src/boombot_ml/embeddings.py +58 -0
  463. package/dist/python/agent-core-python/src/boombot_ml/training.py +156 -0
  464. package/dist/python/agent-core-python/uv.lock +2856 -0
  465. package/dist/web/assets/ChatView-BZrSD-64.js +262 -0
  466. package/dist/web/assets/ChatView-Cahbjez6.css +1 -0
  467. package/dist/web/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  468. package/dist/web/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  469. package/dist/web/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  470. package/dist/web/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  471. package/dist/web/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  472. package/dist/web/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  473. package/dist/web/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  474. package/dist/web/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  475. package/dist/web/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  476. package/dist/web/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  477. package/dist/web/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  478. package/dist/web/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  479. package/dist/web/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  480. package/dist/web/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  481. package/dist/web/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  482. package/dist/web/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  483. package/dist/web/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  484. package/dist/web/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  485. package/dist/web/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  486. package/dist/web/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  487. package/dist/web/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  488. package/dist/web/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  489. package/dist/web/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  490. package/dist/web/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  491. package/dist/web/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  492. package/dist/web/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  493. package/dist/web/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  494. package/dist/web/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  495. package/dist/web/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  496. package/dist/web/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  497. package/dist/web/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  498. package/dist/web/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  499. package/dist/web/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  500. package/dist/web/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  501. package/dist/web/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  502. package/dist/web/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  503. package/dist/web/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  504. package/dist/web/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  505. package/dist/web/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  506. package/dist/web/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  507. package/dist/web/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  508. package/dist/web/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  509. package/dist/web/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  510. package/dist/web/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  511. package/dist/web/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  512. package/dist/web/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  513. package/dist/web/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  514. package/dist/web/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  515. package/dist/web/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  516. package/dist/web/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  517. package/dist/web/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  518. package/dist/web/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  519. package/dist/web/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  520. package/dist/web/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  521. package/dist/web/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  522. package/dist/web/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  523. package/dist/web/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  524. package/dist/web/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  525. package/dist/web/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  526. package/dist/web/assets/MemoryView-BJbXqqs7.css +1 -0
  527. package/dist/web/assets/MemoryView-BfMq_6CD.js +1 -0
  528. package/dist/web/assets/SchedulesView-B22QmPR-.js +1 -0
  529. package/dist/web/assets/SchedulesView-C6XK7A8a.css +1 -0
  530. package/dist/web/assets/SessionsView-C1dqdhxI.css +1 -0
  531. package/dist/web/assets/SessionsView-DS0-aH2a.js +1 -0
  532. package/dist/web/assets/TasksView-Dtasg4hd.css +1 -0
  533. package/dist/web/assets/TasksView-ey0FLsW8.js +2 -0
  534. package/dist/web/assets/Textarea-DN3Zybl1.js +1 -0
  535. package/dist/web/assets/Tooltip-Bn2cqFPz.css +1 -0
  536. package/dist/web/assets/Tooltip-MzV2Gqnc.js +1 -0
  537. package/dist/web/assets/browser-Bnt73-Ub.js +8 -0
  538. package/dist/web/assets/index-BL8bi7Yt.css +1 -0
  539. package/dist/web/assets/index-CuPR73Tn.js +73 -0
  540. package/dist/web/favicon.svg +4 -0
  541. package/dist/web/index.html +26 -0
  542. package/dist/web/logo.jpg +0 -0
  543. package/dist/web/logo.png +0 -0
  544. package/package.json +51 -4
@@ -0,0 +1,270 @@
1
+ /**
2
+ * `boombot migrate --from openclaw` โ€” best-effort import of an OpenClaw config.
3
+ *
4
+ * Never touches the network. Never overwrites existing BoomBot files: when
5
+ * the target config already exists the migrated one is written next to it
6
+ * as `config.migrated.json`. Provider details are stored AES-256-GCM
7
+ * encrypted in `<dataDir>/providers.json` via `@boombot/protocol` โ€” the
8
+ * same store the gateway reads (no staging files).
9
+ */
10
+ import { existsSync, readFileSync } from 'node:fs';
11
+ import { homedir } from 'node:os';
12
+ import path from 'node:path';
13
+ import { createProviderService, PROVIDER_KINDS, } from '@boombot/protocol';
14
+ import { expandTilde, resolveConfigPath, saveConfig, } from "../config-file.js";
15
+ import { ensureMasterKey, resolveMasterKeyPath } from "../master-key.js";
16
+ const CANDIDATE_PATHS = [
17
+ '~/.openclaw/openclaw.json',
18
+ '~/.config/openclaw/openclaw.json',
19
+ '~/.config/openclaw/config.json',
20
+ '~/.openclaw.json',
21
+ ];
22
+ /** Best-effort search for an OpenClaw config file. Returns the path or null. */
23
+ export function findOpenClawConfig(explicitPath) {
24
+ if (explicitPath !== undefined && explicitPath !== '') {
25
+ const p = expandTilde(explicitPath);
26
+ return existsSync(p) ? p : null;
27
+ }
28
+ for (const candidate of CANDIDATE_PATHS) {
29
+ const p = path.join(homedir(), candidate.slice(2));
30
+ if (existsSync(p))
31
+ return p;
32
+ }
33
+ return null;
34
+ }
35
+ /**
36
+ * Whether a `--from` value is acceptable: the keyword `openclaw`, an
37
+ * absolute path, or a `~`/`.`-prefixed path.
38
+ *
39
+ * Absolute-ness is checked against BOTH path flavors deliberately:
40
+ * `path.isAbsolute` follows the host platform, so on POSIX it rejects
41
+ * `C:\...` and on Windows it rejects `/etc/...`. Checking `path.posix`
42
+ * and `path.win32` explicitly accepts absolute paths from either world,
43
+ * which is what a CLI flag should do.
44
+ */
45
+ export function isSupportedMigrateSource(from) {
46
+ return (from === 'openclaw' ||
47
+ from.startsWith('~') ||
48
+ from.startsWith('.') ||
49
+ path.posix.isAbsolute(from) ||
50
+ path.win32.isAbsolute(from));
51
+ }
52
+ function getPath(obj, dotted) {
53
+ let cur = obj;
54
+ for (const part of dotted.split('.')) {
55
+ if (typeof cur !== 'object' || cur === null)
56
+ return undefined;
57
+ cur = cur[part];
58
+ }
59
+ return cur;
60
+ }
61
+ function asNonEmptyString(value) {
62
+ return typeof value === 'string' && value.trim() !== '' ? value : undefined;
63
+ }
64
+ function asStringArray(value) {
65
+ if (!Array.isArray(value))
66
+ return undefined;
67
+ const out = value.filter((v) => typeof v === 'string' && v !== '');
68
+ return out.length > 0 ? out : undefined;
69
+ }
70
+ function preview(value, secret) {
71
+ if (!secret)
72
+ return value.length > 60 ? value.slice(0, 57) + 'โ€ฆ' : value;
73
+ return `*** (${value.length} chars)`;
74
+ }
75
+ const SLOTS = [
76
+ { target: 'provider.kind', candidates: ['provider', 'defaultProvider', 'llm.provider', 'modelProvider'], secret: false },
77
+ { target: 'provider.model', candidates: ['model', 'defaultModel', 'agent.model', 'llm.model'], secret: false },
78
+ { target: 'provider.apiKey', candidates: ['apiKey', 'api_key', 'openaiApiKey', 'anthropicApiKey', 'googleApiKey', 'providers.openai.apiKey', 'providers.anthropic.apiKey'], secret: true },
79
+ { target: 'provider.baseUrl', candidates: ['baseUrl', 'baseURL', 'apiBase', 'apiBaseUrl', 'llm.baseUrl'], secret: false },
80
+ { target: 'telegram.botToken', candidates: ['telegram.botToken', 'telegram.token', 'telegramBotToken', 'channels.telegram.botToken'], secret: true },
81
+ { target: 'telegram.allowlistChatIds', candidates: ['telegram.allowlist', 'telegram.allowlistChatIds', 'telegram.allowedChatIds', 'channels.telegram.allowlist'], secret: false },
82
+ ];
83
+ /**
84
+ * Map recognizable OpenClaw fields to BoomBot targets. Pure โ€” safe to unit test.
85
+ * Unrecognized shapes are reported under `skipped`, never crash.
86
+ */
87
+ export function mapOpenClawConfig(src) {
88
+ const mapped = [];
89
+ const skipped = [];
90
+ const provider = {};
91
+ let telegramBotToken;
92
+ let telegramAllowlistChatIds = [];
93
+ for (const slot of SLOTS) {
94
+ if (slot.target === 'telegram.allowlistChatIds') {
95
+ const allow = findAllowlist(src, slot.candidates);
96
+ if (allow === undefined) {
97
+ skipped.push(slot.target);
98
+ }
99
+ else {
100
+ mapped.push({ target: slot.target, source: allow.source, valuePreview: preview(allow.ids.join(','), false) });
101
+ telegramAllowlistChatIds = allow.ids;
102
+ }
103
+ continue;
104
+ }
105
+ let found;
106
+ for (const candidate of slot.candidates) {
107
+ const value = asNonEmptyString(getPath(src, candidate));
108
+ if (value !== undefined) {
109
+ found = { source: candidate, value };
110
+ break;
111
+ }
112
+ }
113
+ if (found === undefined) {
114
+ skipped.push(slot.target);
115
+ continue;
116
+ }
117
+ mapped.push({ target: slot.target, source: found.source, valuePreview: preview(found.value, slot.secret) });
118
+ switch (slot.target) {
119
+ case 'provider.kind':
120
+ provider.kind = found.value;
121
+ break;
122
+ case 'provider.model':
123
+ provider.model = found.value;
124
+ break;
125
+ case 'provider.apiKey':
126
+ provider.apiKey = found.value;
127
+ break;
128
+ case 'provider.baseUrl':
129
+ provider.baseUrl = found.value;
130
+ break;
131
+ case 'telegram.botToken':
132
+ telegramBotToken = found.value;
133
+ break;
134
+ }
135
+ }
136
+ return { mapped, skipped, provider, telegramBotToken, telegramAllowlistChatIds };
137
+ }
138
+ /** Allowlist may be an array or a comma-separated string. */
139
+ function findAllowlist(src, candidates) {
140
+ for (const candidate of candidates) {
141
+ const raw = getPath(src, candidate);
142
+ const arr = asStringArray(raw);
143
+ if (arr !== undefined)
144
+ return { source: candidate, ids: arr };
145
+ const str = asNonEmptyString(raw);
146
+ if (str !== undefined) {
147
+ const ids = str.split(',').map((s) => s.trim()).filter((s) => s !== '');
148
+ if (ids.length > 0)
149
+ return { source: candidate, ids };
150
+ }
151
+ }
152
+ return undefined;
153
+ }
154
+ function uniqueTargetPath(basePath) {
155
+ if (!existsSync(basePath))
156
+ return basePath;
157
+ const dir = path.dirname(basePath);
158
+ const migrated = path.join(dir, 'config.migrated.json');
159
+ if (!existsSync(migrated))
160
+ return migrated;
161
+ return path.join(dir, `config.migrated.${Date.now()}.json`);
162
+ }
163
+ export async function runMigrateFromOpenClaw(options = {}) {
164
+ const warnings = [];
165
+ const sourcePath = findOpenClawConfig(options.from);
166
+ if (sourcePath === null) {
167
+ return {
168
+ sourcePath: null,
169
+ mapping: null,
170
+ configWrittenTo: null,
171
+ providersWrittenTo: null,
172
+ warnings: [
173
+ 'no OpenClaw config found in the standard locations ' +
174
+ CANDIDATE_PATHS.join(', ') +
175
+ ' โ€” pass an explicit path with `--from <path>`',
176
+ ],
177
+ };
178
+ }
179
+ let src;
180
+ try {
181
+ src = JSON.parse(readFileSync(sourcePath, 'utf8'));
182
+ }
183
+ catch (err) {
184
+ return {
185
+ sourcePath,
186
+ mapping: null,
187
+ configWrittenTo: null,
188
+ providersWrittenTo: null,
189
+ warnings: [`${sourcePath} is not valid JSON: ${err.message}`],
190
+ };
191
+ }
192
+ const mapping = mapOpenClawConfig(src);
193
+ const config = {
194
+ host: '127.0.0.1',
195
+ port: 8787,
196
+ dataDir: options.dataDir ?? '~/.boombot/data',
197
+ ...(mapping.telegramBotToken !== undefined
198
+ ? {
199
+ telegram: {
200
+ botToken: mapping.telegramBotToken,
201
+ allowlistChatIds: mapping.telegramAllowlistChatIds,
202
+ },
203
+ }
204
+ : {}),
205
+ };
206
+ const targetPath = uniqueTargetPath(options.configPath ?? resolveConfigPath());
207
+ if (targetPath !== (options.configPath ?? resolveConfigPath())) {
208
+ warnings.push(`existing config preserved โ€” migrated config written to ${targetPath} instead`);
209
+ }
210
+ saveConfig(config, targetPath);
211
+ let providersWrittenTo = null;
212
+ const provider = mapping.provider;
213
+ if (provider.kind !== undefined || provider.model !== undefined || provider.apiKey !== undefined || provider.baseUrl !== undefined) {
214
+ // Unknown/missing kinds fall back to the generic compatible kind; the
215
+ // gateway probes connectivity with `provider.test` either way.
216
+ const kind = provider.kind !== undefined && PROVIDER_KINDS.includes(provider.kind)
217
+ ? provider.kind
218
+ : 'openai-compatible';
219
+ if (provider.kind !== undefined && kind !== provider.kind) {
220
+ warnings.push(`unknown provider kind '${provider.kind}' โ€” imported as 'openai-compatible'`);
221
+ }
222
+ const masterKey = ensureMasterKey(options.masterKeyPath ?? resolveMasterKeyPath());
223
+ const service = createProviderService({
224
+ storagePath: path.join(expandTilde(config.dataDir), 'providers.json'),
225
+ masterKey,
226
+ });
227
+ await service.load();
228
+ await service.save({
229
+ name: kind,
230
+ kind,
231
+ ...(provider.baseUrl !== undefined ? { baseUrl: provider.baseUrl } : {}),
232
+ ...(provider.model !== undefined ? { model: provider.model } : {}),
233
+ ...(provider.apiKey !== undefined ? { apiKey: provider.apiKey } : {}),
234
+ });
235
+ providersWrittenTo = path.join(expandTilde(config.dataDir), 'providers.json');
236
+ }
237
+ else {
238
+ warnings.push('no provider fields recognized โ€” configure one with `boombot setup`');
239
+ }
240
+ if (provider.apiKey === undefined) {
241
+ warnings.push('no API key found in the OpenClaw config โ€” you will need to paste it during `boombot setup`');
242
+ }
243
+ return { sourcePath, mapping, configWrittenTo: targetPath, providersWrittenTo, warnings };
244
+ }
245
+ /** Print the mapped/skipped table. */
246
+ export function printMigrateReport(report) {
247
+ if (report.sourcePath === null || report.mapping === null) {
248
+ for (const w of report.warnings)
249
+ console.log(`warning: ${w}`);
250
+ return;
251
+ }
252
+ console.log(`source: ${report.sourcePath}\n`);
253
+ console.log('mapped:');
254
+ for (const m of report.mapping.mapped) {
255
+ console.log(` ${m.target} <- ${m.source} (${m.valuePreview})`);
256
+ }
257
+ if (report.mapping.skipped.length > 0) {
258
+ console.log('\nskipped (not found in source):');
259
+ for (const s of report.mapping.skipped)
260
+ console.log(` ${s}`);
261
+ }
262
+ console.log('');
263
+ if (report.configWrittenTo !== null)
264
+ console.log(`wrote BoomBot config: ${report.configWrittenTo}`);
265
+ if (report.providersWrittenTo !== null) {
266
+ console.log(`wrote encrypted provider store: ${report.providersWrittenTo}`);
267
+ }
268
+ for (const w of report.warnings)
269
+ console.log(`warning: ${w}`);
270
+ }
@@ -0,0 +1,97 @@
1
+ /**
2
+ * `boombot plugin` โ€” install, uninstall, list plugins dynamically at runtime.
3
+ *
4
+ * install sources:
5
+ * boombot plugin install browser โ†’ marketplace alias
6
+ * boombot plugin install boombot.browser โ†’ full marketplace id
7
+ * boombot plugin install /path/to/dir โ†’ local directory
8
+ * boombot plugin install https://github.com/org/repo โ†’ git clone
9
+ * boombot plugin install npm:@scope/pkg โ†’ npm package
10
+ *
11
+ * uninstall:
12
+ * boombot plugin uninstall boombot.browser
13
+ *
14
+ * list:
15
+ * boombot plugin list
16
+ * boombot plugin list --marketplace
17
+ */
18
+ import path from 'node:path';
19
+ import { expandTilde } from "../config-file.js";
20
+ import { loadConfig } from "../config-file.js";
21
+ import { PluginInstaller } from '@boombot/plugins';
22
+ const DEFAULT_DATA_DIR = '~/.boombot/data';
23
+ function resolvePluginRoot() {
24
+ try {
25
+ const config = loadConfig();
26
+ const dataDir = expandTilde(config?.dataDir ?? DEFAULT_DATA_DIR);
27
+ return path.join(dataDir, '..', 'plugins');
28
+ }
29
+ catch {
30
+ return path.join(expandTilde(DEFAULT_DATA_DIR), '..', 'plugins');
31
+ }
32
+ }
33
+ export async function runPluginList(opts) {
34
+ if (opts.marketplace) {
35
+ const catalog = PluginInstaller.getMarketplace();
36
+ console.log(`\nPlugin Marketplace (${catalog.length} available):\n`);
37
+ for (const p of catalog) {
38
+ const tags = p.tags.join(', ');
39
+ console.log(` ${p.id.padEnd(30)} v${p.version.padEnd(8)} ${p.description}`);
40
+ console.log(` Tags: ${tags}\n`);
41
+ }
42
+ return;
43
+ }
44
+ const root = resolvePluginRoot();
45
+ const installed = await PluginInstaller.listInstalled(root);
46
+ if (installed.length === 0) {
47
+ console.log('\nNo plugins installed.');
48
+ console.log('Run `boombot plugin install <name>` to get started.');
49
+ console.log('Run `boombot plugin list --marketplace` to browse available plugins.\n');
50
+ return;
51
+ }
52
+ console.log(`\nInstalled Plugins (${installed.length}):\n`);
53
+ for (const p of installed) {
54
+ const status = p.isValid ? 'โœ“' : 'โœ—';
55
+ console.log(` ${status} ${p.id.padEnd(30)} v${p.version.padEnd(8)} ${p.displayName ?? ''}`);
56
+ if (p.description)
57
+ console.log(` ${p.description}`);
58
+ if (!p.isValid && p.error)
59
+ console.log(` [Error] ${p.error}`);
60
+ }
61
+ console.log();
62
+ }
63
+ export async function runPluginInstall(spec, force = false) {
64
+ if (!spec) {
65
+ console.error('[plugin install] Usage: boombot plugin install <name|path|url>');
66
+ return 1;
67
+ }
68
+ const root = resolvePluginRoot();
69
+ console.log(`Installing plugin '${spec}'...`);
70
+ const result = await PluginInstaller.install(spec, { root, force });
71
+ if (result.ok) {
72
+ console.log(`โœ“ ${result.message}`);
73
+ console.log(` Restart the gateway to load the plugin: pm2 restart boombot`);
74
+ }
75
+ else {
76
+ console.error(`โœ— ${result.message}`);
77
+ return 1;
78
+ }
79
+ return 0;
80
+ }
81
+ export async function runPluginUninstall(pluginId) {
82
+ if (!pluginId) {
83
+ console.error('[plugin uninstall] Usage: boombot plugin uninstall <plugin-id>');
84
+ return 1;
85
+ }
86
+ const root = resolvePluginRoot();
87
+ const result = await PluginInstaller.uninstall(pluginId, { root });
88
+ if (result.ok) {
89
+ console.log(`โœ“ ${result.message}`);
90
+ console.log(` Restart the gateway to unload: pm2 restart boombot`);
91
+ }
92
+ else {
93
+ console.error(`โœ— ${result.message}`);
94
+ return 1;
95
+ }
96
+ return 0;
97
+ }
@@ -0,0 +1,230 @@
1
+ /**
2
+ * `boombot setup` โ€” interactive first-run wizard (node:readline/promises).
3
+ *
4
+ * Walks through host/port, data dir, API key generation (printed ONCE),
5
+ * master-key generation (encrypts provider API keys at rest), optional
6
+ * Telegram pairing, and the default provider. The provider is stored
7
+ * AES-256-GCM encrypted in `<dataDir>/providers.json` via
8
+ * `@boombot/protocol` โ€” the same store the gateway reads, so the key the
9
+ * user pastes once is immediately usable (no staging files).
10
+ */
11
+ import path from 'node:path';
12
+ import { createInterface } from 'node:readline/promises';
13
+ import { createProviderService, PROVIDER_KINDS } from '@boombot/protocol';
14
+ import { ensureApiKey } from "../api-key.js";
15
+ import { expandTilde, loadConfig, resolveConfigPath, saveConfig, validateConfig, } from "../config-file.js";
16
+ import { ensureMasterKey, resolveMasterKeyPath } from "../master-key.js";
17
+ export function parsePort(raw, fallback) {
18
+ const trimmed = raw.trim();
19
+ if (trimmed === '')
20
+ return fallback;
21
+ const parsed = Number.parseInt(trimmed, 10);
22
+ return Number.isInteger(parsed) && parsed >= 1 && parsed <= 65535 ? parsed : fallback;
23
+ }
24
+ export function parseAllowlist(raw) {
25
+ return raw
26
+ .split(',')
27
+ .map((s) => s.trim())
28
+ .filter((s) => s !== '');
29
+ }
30
+ async function ask(question) {
31
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
32
+ try {
33
+ return await rl.question(question);
34
+ }
35
+ finally {
36
+ rl.close();
37
+ }
38
+ }
39
+ async function askDefault(question, def) {
40
+ const answer = await ask(`${question} [${def}]: `);
41
+ return answer.trim() === '' ? def : answer.trim();
42
+ }
43
+ async function askRequired(question) {
44
+ for (;;) {
45
+ const answer = (await ask(question)).trim();
46
+ if (answer !== '')
47
+ return answer;
48
+ console.log('This value is required.');
49
+ }
50
+ }
51
+ async function askYesNo(question, def) {
52
+ const hint = def ? 'Y/n' : 'y/N';
53
+ const answer = (await ask(`${question} [${hint}]: `)).trim().toLowerCase();
54
+ if (answer === '')
55
+ return def;
56
+ return answer === 'y' || answer === 'yes';
57
+ }
58
+ /** Read a secret with echo disabled (masked). Falls back to plain ask when not a TTY. */
59
+ async function readSecret(prompt) {
60
+ if (!process.stdin.isTTY) {
61
+ return (await ask(prompt)).trim();
62
+ }
63
+ return new Promise((resolve) => {
64
+ process.stdout.write(prompt);
65
+ const stdin = process.stdin;
66
+ const wasRaw = stdin.isRaw === true;
67
+ stdin.setRawMode(true);
68
+ stdin.resume();
69
+ let value = '';
70
+ const done = (result) => {
71
+ stdin.off('data', onData);
72
+ stdin.setRawMode(wasRaw);
73
+ stdin.pause();
74
+ process.stdout.write('\n');
75
+ if (result === null)
76
+ process.exit(130);
77
+ resolve(result);
78
+ };
79
+ const onData = (data) => {
80
+ for (const ch of data.toString('utf8')) {
81
+ if (ch === '\r' || ch === '\n' || ch === '\u0004') {
82
+ done(value);
83
+ return;
84
+ }
85
+ if (ch === '\u0003') {
86
+ done(null); // Ctrl+C
87
+ return;
88
+ }
89
+ if (ch === '\u007f' || ch === '\b') {
90
+ value = value.slice(0, -1);
91
+ }
92
+ else if (ch >= ' ' || ch === '\t') {
93
+ value += ch;
94
+ }
95
+ }
96
+ };
97
+ stdin.on('data', onData);
98
+ });
99
+ }
100
+ async function readRequiredSecret(prompt) {
101
+ for (;;) {
102
+ const answer = (await readSecret(prompt)).trim();
103
+ if (answer !== '')
104
+ return answer;
105
+ console.log('This value is required.');
106
+ }
107
+ }
108
+ function isProviderKind(value) {
109
+ return PROVIDER_KINDS.includes(value);
110
+ }
111
+ /**
112
+ * Ask for a provider kind. Empty input skips provider setup entirely
113
+ * (returns `null`); anything else must be a known kind.
114
+ */
115
+ async function askProviderKind() {
116
+ for (;;) {
117
+ const raw = (await ask(`Provider kind (${PROVIDER_KINDS.join('/')}) [empty = skip]: `))
118
+ .trim()
119
+ .toLowerCase();
120
+ if (raw === '')
121
+ return null;
122
+ if (isProviderKind(raw))
123
+ return raw;
124
+ console.log(`Unknown kind '${raw}'. Choose one of: ${PROVIDER_KINDS.join(', ')}.`);
125
+ }
126
+ }
127
+ /** Host as typed for the browser: 0.0.0.0/:: mean "this machine". Exported for tests. */
128
+ export function browserHost(host) {
129
+ return host === '0.0.0.0' || host === '::' ? '127.0.0.1' : host;
130
+ }
131
+ export async function runSetup() {
132
+ console.log('๐Ÿฆ‰ BoomBot setup\n');
133
+ const existing = loadConfig();
134
+ const host = await askDefault('Gateway host', existing?.host ?? '127.0.0.1');
135
+ const defaultPort = existing?.port ?? 8787;
136
+ const port = parsePort(await ask(`Gateway port [${defaultPort}]: `), defaultPort);
137
+ const dataDir = await askDefault('Data directory', existing?.dataDir ?? '~/.boombot/data');
138
+ // API key โ€” kept at <config-dir>/api.key (0600) so it stays recoverable; the
139
+ // config only ever holds its sha256. An existing key is reused, never rotated.
140
+ const { apiKey, apiKeyHash, keyPath, created, fromEnv } = ensureApiKey();
141
+ if (fromEnv) {
142
+ console.log('\nUsing the API key from BOOMBOT_API_KEY (no key file written).\n');
143
+ }
144
+ else if (created) {
145
+ console.log('\nโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€');
146
+ console.log('Your gateway API key (save it somewhere safe):');
147
+ console.log(`\n ${apiKey}\n`);
148
+ console.log(`Also stored at ${keyPath} (mode 0600) โ€” \`boombot gateway\` reads it from`);
149
+ console.log('there and signs your browser in automatically, so you can ignore this');
150
+ console.log('notice. Only the sha256 hash is written to config.json.');
151
+ console.log('โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n');
152
+ }
153
+ else {
154
+ console.log(`\nKeeping the existing gateway API key from ${keyPath}.`);
155
+ console.log('(delete that file and re-run setup to rotate it)\n');
156
+ }
157
+ // Master key โ€” encrypts provider API keys at rest. The gateway needs it
158
+ // as BOOMBOT_MASTER_KEY; `boombot gateway` loads this file automatically.
159
+ const masterKeyPath = resolveMasterKeyPath();
160
+ const masterKey = process.env['BOOMBOT_MASTER_KEY'] || ensureMasterKey(masterKeyPath);
161
+ console.log(`Master key for provider encryption: ${masterKeyPath}`);
162
+ console.log('(mode 0600 โ€” it decrypts your provider keys, keep it safe)\n');
163
+ // Telegram (optional).
164
+ let telegram = existing?.telegram;
165
+ if (await askYesNo('Configure Telegram bot?', false)) {
166
+ const botToken = await readSecret('Telegram bot token: ');
167
+ const allowlist = parseAllowlist(await ask('Allowlisted chat IDs (comma-separated, empty = none): '));
168
+ telegram = { botToken: botToken.trim(), allowlistChatIds: allowlist };
169
+ }
170
+ // Default provider โ†’ the real encrypted store (<dataDir>/providers.json),
171
+ // the same store the gateway reads. The key is pasted once, masked, and
172
+ // never written in plaintext anywhere.
173
+ let providerSaved = false;
174
+ if (await askYesNo('Configure a default LLM provider?', true)) {
175
+ const kind = await askProviderKind();
176
+ if (kind !== null) {
177
+ const model = await askRequired('Default model: ');
178
+ const needsBaseUrl = kind === 'openai-compatible' || kind === 'local';
179
+ const baseUrl = needsBaseUrl
180
+ ? await askRequired('Base URL (required for this provider kind): ')
181
+ : (await ask('Base URL (empty = provider default): ')).trim();
182
+ const providerApiKey = kind === 'local'
183
+ ? (await readSecret('Provider API key (empty = none): ')).trim()
184
+ : await readRequiredSecret('Provider API key: ');
185
+ const service = createProviderService({
186
+ storagePath: path.join(expandTilde(dataDir), 'providers.json'),
187
+ masterKey,
188
+ });
189
+ await service.load();
190
+ const saved = await service.save({
191
+ name: kind,
192
+ kind,
193
+ ...(baseUrl !== '' ? { baseUrl } : {}),
194
+ model,
195
+ ...(providerApiKey !== '' ? { apiKey: providerApiKey } : {}),
196
+ active: true,
197
+ });
198
+ providerSaved = true;
199
+ const keyNote = providerApiKey !== '' ? ' โ€” API key stored encrypted' : ' โ€” no API key stored';
200
+ console.log(`\nSaved default provider '${saved.name}' (${saved.kind})${keyNote}.`);
201
+ }
202
+ }
203
+ const config = {
204
+ ...existing,
205
+ host,
206
+ port,
207
+ dataDir,
208
+ apiKeyHash,
209
+ ...(telegram !== undefined ? { telegram } : {}),
210
+ };
211
+ const problems = validateConfig(config);
212
+ if (problems.length > 0) {
213
+ console.error(`\nInvalid config: ${problems.join('; ')}`);
214
+ process.exitCode = 1;
215
+ return;
216
+ }
217
+ const configPath = resolveConfigPath();
218
+ saveConfig(config, configPath);
219
+ const permsNote = process.platform === 'win32'
220
+ ? 'permissions NOT enforced on Windows (chmod is a no-op) โ€” see SECURITY.md ยง5'
221
+ : 'mode 0600 โ€” secrets protected';
222
+ console.log(`\nSaved config to ${configPath} (${permsNote}).`);
223
+ const url = `http://${browserHost(host)}:${port}`;
224
+ console.log('\nNext steps:');
225
+ console.log(` 1. boombot gateway # start the gateway โ€” opens ${url} in your browser`);
226
+ console.log(' 2. boombot doctor # health check, if anything looks off');
227
+ if (!providerSaved) {
228
+ console.log(' (tip: re-run `boombot setup` any time to add a default provider)');
229
+ }
230
+ }