@johpaz/hive-agents 0.0.35 → 0.0.37

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 (441) hide show
  1. package/README.md +64 -39
  2. package/dist/hive.js +3231 -3189
  3. package/dist/tool-worker.js +218406 -0
  4. package/dist/ui/assets/{AgentCreateForm-B4eK7efF.js → AgentCreateForm-tJZv9FZC.js} +1 -1
  5. package/dist/ui/assets/{AgentDetailPage-BD2uoJWk.js → AgentDetailPage-Du-mRcAX.js} +1 -1
  6. package/dist/ui/assets/AgentNewPage-DIFYd_Ys.js +1 -0
  7. package/dist/ui/assets/{AgentsPage-4JUZXvkA.js → AgentsPage-YvSgWRiw.js} +6 -6
  8. package/dist/ui/assets/CanvasPage-DtMwGvxf.js +33 -0
  9. package/dist/ui/assets/{ChannelsPage-BUn7-nhV.js → ChannelsPage-BdBXWHjj.js} +1 -1
  10. package/dist/ui/assets/DashboardPage-ghl1ZguH.js +6 -0
  11. package/dist/ui/assets/{LoginPage-C8j_urUD.js → LoginPage-CAmSI9Vy.js} +1 -1
  12. package/dist/ui/assets/LogsPage-DAPBHkwK.js +1 -0
  13. package/dist/ui/assets/MeetingPage-WjjGOqqU.js +1 -0
  14. package/dist/ui/assets/{NotFound-Drh-sJPN.js → NotFound-BMeQSGcG.js} +1 -1
  15. package/dist/ui/assets/ProvidersPage-Ct6HsAi1.js +1 -0
  16. package/dist/ui/assets/{RecoverPage-DNb1Pr8h.js → RecoverPage-DpW3l-yv.js} +1 -1
  17. package/dist/ui/assets/SettingsPage-DBJ7_E6C.js +9 -0
  18. package/dist/ui/assets/SetupPage-DKmLVUaj.js +1 -0
  19. package/dist/ui/assets/{WebChatPage-R-YOwA4F.js → WebChatPage-CVRcKept.js} +2 -2
  20. package/dist/ui/assets/accordion-C5d5Rm5z.js +1 -0
  21. package/dist/ui/assets/{alert-U8FsgWi7.js → alert-C-NE-P3s.js} +1 -1
  22. package/dist/ui/assets/{alert-dialog-CRdMkkmk.js → alert-dialog-C5mzbHdP.js} +1 -1
  23. package/dist/ui/assets/{badge-Cli1jnH5.js → badge-ChpACfWO.js} +1 -1
  24. package/dist/ui/assets/chevron-up-BYhk0K2J.js +1 -0
  25. package/dist/ui/assets/{dialog-DQ3s-LuO.js → dialog-QnZ0ad8O.js} +1 -1
  26. package/dist/ui/assets/dropdown-menu-BK-CO3Od.js +1 -0
  27. package/dist/ui/assets/{es-DcMjrpbA.js → es-NQNoaWDx.js} +1 -1
  28. package/dist/ui/assets/index-B2fCYtTS.css +2 -0
  29. package/dist/ui/assets/index-DMCjjdqf.js +116 -0
  30. package/dist/ui/assets/{label-0BvGVXvZ.js → label-D2H1IR_J.js} +1 -1
  31. package/dist/ui/assets/progress-BherYzY6.js +1 -0
  32. package/dist/ui/assets/scroll-area-DkeyX32e.js +1 -0
  33. package/dist/ui/assets/{slider-D47dOrRa.js → slider-CsiUDxc3.js} +1 -1
  34. package/dist/ui/assets/switch-BDwN8RYV.js +1 -0
  35. package/dist/ui/assets/{table-DhowbNxQ.js → table-CSc8ubon.js} +1 -1
  36. package/dist/ui/assets/terminal-DN38Q456.js +1 -0
  37. package/dist/ui/assets/useProviders-C6_QHsEi.js +1 -0
  38. package/dist/ui/assets/{vendor-radix-JY4ncZrD.js → vendor-radix-cw1bQaVC.js} +4 -4
  39. package/dist/ui/assets/{vendor-react-CscwQerf.js → vendor-react-D4s9E-zj.js} +1 -1
  40. package/dist/ui/dist/assets/AgentCreateForm-tJZv9FZC.js +1 -0
  41. package/dist/ui/dist/assets/AgentDetailPage-Du-mRcAX.js +1 -0
  42. package/dist/ui/dist/assets/AgentNewPage-DIFYd_Ys.js +1 -0
  43. package/dist/ui/dist/assets/AgentsPage-YvSgWRiw.js +10 -0
  44. package/dist/ui/dist/assets/CanvasPage-DtMwGvxf.js +33 -0
  45. package/dist/ui/dist/assets/ChannelsPage-BdBXWHjj.js +8 -0
  46. package/dist/ui/dist/assets/DashboardPage-ghl1ZguH.js +6 -0
  47. package/dist/ui/dist/assets/LoginPage-CAmSI9Vy.js +1 -0
  48. package/dist/ui/dist/assets/LogsPage-DAPBHkwK.js +1 -0
  49. package/dist/ui/dist/assets/MeetingPage-WjjGOqqU.js +1 -0
  50. package/dist/ui/dist/assets/NotFound-BMeQSGcG.js +1 -0
  51. package/dist/ui/dist/assets/ProvidersPage-Ct6HsAi1.js +1 -0
  52. package/dist/ui/dist/assets/RecoverPage-DpW3l-yv.js +1 -0
  53. package/dist/ui/dist/assets/SettingsPage-DBJ7_E6C.js +9 -0
  54. package/dist/ui/dist/assets/SetupPage-DKmLVUaj.js +1 -0
  55. package/dist/ui/dist/assets/WebChatPage-CVRcKept.js +16 -0
  56. package/dist/ui/dist/assets/accordion-C5d5Rm5z.js +1 -0
  57. package/dist/ui/dist/assets/activity-c3pNngT_.js +1 -0
  58. package/dist/ui/dist/assets/alert-C-NE-P3s.js +1 -0
  59. package/dist/ui/dist/assets/alert-dialog-C5mzbHdP.js +1 -0
  60. package/dist/ui/dist/assets/arrow-left-CBcbX5EZ.js +1 -0
  61. package/dist/ui/dist/assets/badge-ChpACfWO.js +1 -0
  62. package/dist/ui/dist/assets/calendar-B-KZ9RQO.js +1 -0
  63. package/dist/ui/dist/assets/card-CNf6BS2e.js +1 -0
  64. package/dist/ui/dist/assets/chevron-left-D4U-5A27.js +1 -0
  65. package/dist/ui/dist/assets/chevron-right-CR4Skrf3.js +1 -0
  66. package/dist/ui/dist/assets/chevron-up-BYhk0K2J.js +1 -0
  67. package/dist/ui/dist/assets/circle-alert-CyHDwUj8.js +1 -0
  68. package/dist/ui/dist/assets/circle-check-Bb54Ebmu.js +1 -0
  69. package/dist/ui/dist/assets/cpu-Cdgc_B1K.js +1 -0
  70. package/dist/ui/dist/assets/dialog-QnZ0ad8O.js +1 -0
  71. package/dist/ui/dist/assets/download-C3ifGMjJ.js +1 -0
  72. package/dist/ui/dist/assets/dropdown-menu-BK-CO3Od.js +1 -0
  73. package/dist/ui/dist/assets/es-NQNoaWDx.js +1 -0
  74. package/dist/ui/dist/assets/external-link-BvxYeTP1.js +1 -0
  75. package/dist/ui/dist/assets/eye-DqNTU_GD.js +1 -0
  76. package/dist/ui/dist/assets/file-text-BT_9S9SM.js +1 -0
  77. package/dist/ui/dist/assets/folder-open-BhH8y9ac.js +1 -0
  78. package/dist/ui/dist/assets/format-GVHeOyWI.js +1 -0
  79. package/dist/ui/dist/assets/gateway-url-COCbW0IR.js +1 -0
  80. package/dist/ui/dist/assets/gauge-D_TMa4i9.js +1 -0
  81. package/dist/ui/dist/assets/globe-DeCQTCDJ.js +1 -0
  82. package/dist/ui/dist/assets/hexagon-DsGOUl-H.js +1 -0
  83. package/dist/ui/dist/assets/history-BSG-Ypqf.js +1 -0
  84. package/dist/ui/dist/assets/index-B2fCYtTS.css +2 -0
  85. package/dist/ui/dist/assets/index-DMCjjdqf.js +116 -0
  86. package/dist/ui/dist/assets/info-NwLoa2Mj.js +1 -0
  87. package/dist/ui/dist/assets/key-3EP0dhkT.js +1 -0
  88. package/dist/ui/dist/assets/label-D2H1IR_J.js +1 -0
  89. package/dist/ui/dist/assets/loader-circle-CZNax6kS.js +1 -0
  90. package/dist/ui/dist/assets/lock-Ei1_J-Nq.js +1 -0
  91. package/dist/ui/dist/assets/pause-BUqah9Bi.js +1 -0
  92. package/dist/ui/dist/assets/play-NcZ4swwL.js +1 -0
  93. package/dist/ui/dist/assets/plus-CX1xyhp5.js +1 -0
  94. package/dist/ui/dist/assets/progress-BherYzY6.js +1 -0
  95. package/dist/ui/dist/assets/refresh-cw-DaYdjQFk.js +1 -0
  96. package/dist/ui/dist/assets/rolldown-runtime-S-ySWqyJ.js +1 -0
  97. package/dist/ui/dist/assets/save-CUdYyHNy.js +1 -0
  98. package/dist/ui/dist/assets/scroll-area-DkeyX32e.js +1 -0
  99. package/dist/ui/dist/assets/send-B0H5SEIE.js +1 -0
  100. package/dist/ui/dist/assets/settings-Ds4SqD8s.js +1 -0
  101. package/dist/ui/dist/assets/slider-CsiUDxc3.js +14 -0
  102. package/dist/ui/dist/assets/sparkles-yUEb-7oH.js +1 -0
  103. package/dist/ui/dist/assets/square-BD81nFtN.js +1 -0
  104. package/dist/ui/dist/assets/switch-BDwN8RYV.js +1 -0
  105. package/dist/ui/dist/assets/table-CSc8ubon.js +1 -0
  106. package/dist/ui/dist/assets/terminal-DN38Q456.js +1 -0
  107. package/dist/ui/dist/assets/textarea-CXgXWKrT.js +1 -0
  108. package/dist/ui/dist/assets/trash-2-CNjMkoq6.js +1 -0
  109. package/dist/ui/dist/assets/triangle-alert-C9Y8Ub4X.js +1 -0
  110. package/dist/ui/dist/assets/useProviders-C6_QHsEi.js +1 -0
  111. package/dist/ui/dist/assets/utils-3pnRFmFe.js +1 -0
  112. package/dist/ui/dist/assets/vendor-charts-Bu2lyBKP.js +65 -0
  113. package/dist/ui/dist/assets/vendor-query-DsWPbQdG.js +1 -0
  114. package/dist/ui/dist/assets/vendor-radix-cw1bQaVC.js +63 -0
  115. package/dist/ui/dist/assets/vendor-react-D4s9E-zj.js +1 -0
  116. package/dist/ui/dist/assets/vendor-router-C9pIYwbJ.js +3 -0
  117. package/dist/ui/dist/assets/volume-2-CeSXNDv4.js +1 -0
  118. package/dist/ui/dist/assets/zap-hlXjpSeA.js +1 -0
  119. package/dist/ui/dist/favicon.ico +0 -0
  120. package/dist/ui/dist/index.html +40 -0
  121. package/dist/ui/dist/placeholder.svg +1 -0
  122. package/dist/ui/index.html +6 -6
  123. package/package.json +138 -13
  124. package/packages/cli/src/adapters/binary.ts +461 -0
  125. package/packages/cli/src/adapters/bun-global.ts +378 -0
  126. package/packages/cli/src/adapters/config.ts +314 -0
  127. package/packages/cli/src/adapters/docker.ts +308 -0
  128. package/packages/cli/src/adapters/factory.ts +168 -0
  129. package/packages/cli/src/adapters/index.ts +80 -0
  130. package/packages/cli/src/adapters/types.ts +218 -0
  131. package/packages/cli/src/commands/agent-run.ts +168 -0
  132. package/packages/cli/src/commands/agents.ts +398 -0
  133. package/packages/cli/src/commands/chat.ts +142 -0
  134. package/packages/cli/src/commands/config.ts +49 -0
  135. package/packages/cli/src/commands/cron.ts +487 -0
  136. package/packages/cli/src/commands/dev.ts +58 -0
  137. package/packages/cli/src/commands/doctor.ts +320 -0
  138. package/packages/cli/src/commands/gateway.ts +719 -0
  139. package/packages/cli/src/commands/logs.ts +57 -0
  140. package/packages/cli/src/commands/mcp.ts +175 -0
  141. package/packages/cli/src/commands/message.ts +77 -0
  142. package/packages/cli/src/commands/migrate.ts +90 -0
  143. package/packages/cli/src/commands/onboard.ts +1656 -0
  144. package/packages/cli/src/commands/security.ts +144 -0
  145. package/packages/cli/src/commands/service.ts +50 -0
  146. package/packages/cli/src/commands/sessions.ts +116 -0
  147. package/packages/cli/src/commands/skills.ts +215 -0
  148. package/packages/cli/src/commands/update.ts +203 -0
  149. package/packages/cli/src/index.ts +210 -0
  150. package/packages/cli/src/ui-bundle.generated.ts +3 -0
  151. package/packages/cli/src/utils/token.ts +6 -0
  152. package/packages/core/src/agent/agent-loop.ts +691 -0
  153. package/packages/core/src/agent/compaction.ts +240 -0
  154. package/packages/core/src/agent/context-compiler.ts +467 -0
  155. package/packages/core/src/agent/context-guard.ts +91 -0
  156. package/packages/core/src/agent/conversation-store.ts +244 -0
  157. package/packages/core/src/agent/curator.ts +158 -0
  158. package/packages/core/src/agent/hooks.ts +166 -0
  159. package/packages/core/src/agent/llm-client.ts +167 -0
  160. package/packages/core/src/agent/llm-providers/anthropic.ts +212 -0
  161. package/packages/core/src/agent/llm-providers/deepseek.ts +8 -0
  162. package/packages/core/src/agent/llm-providers/gemini.ts +215 -0
  163. package/packages/core/src/agent/llm-providers/groq.ts +5 -0
  164. package/packages/core/src/agent/llm-providers/interface.ts +195 -0
  165. package/packages/core/src/agent/llm-providers/kimi.ts +8 -0
  166. package/packages/core/src/agent/llm-providers/local-llama.ts +37 -0
  167. package/packages/core/src/agent/llm-providers/mistral.ts +5 -0
  168. package/packages/core/src/agent/llm-providers/nvidia.ts +5 -0
  169. package/packages/core/src/agent/llm-providers/ollama.ts +175 -0
  170. package/packages/core/src/agent/llm-providers/openai-compat-base.ts +379 -0
  171. package/packages/core/src/agent/llm-providers/openai.ts +5 -0
  172. package/packages/core/src/agent/llm-providers/openrouter.ts +5 -0
  173. package/packages/core/src/agent/llm-providers/qwen.ts +5 -0
  174. package/packages/core/src/agent/native-tools.ts +31 -0
  175. package/packages/core/src/agent/playbook-selector.ts +147 -0
  176. package/packages/core/src/agent/prompt-builder.ts +169 -0
  177. package/packages/core/src/agent/providers/index.ts +204 -0
  178. package/packages/core/src/agent/providers.ts +1 -0
  179. package/packages/core/src/agent/reflector.ts +200 -0
  180. package/packages/core/src/agent/service.ts +267 -0
  181. package/packages/core/src/agent/skill-selector.ts +479 -0
  182. package/packages/core/src/agent/stuck-loop.ts +133 -0
  183. package/packages/core/src/agent/tool-selector.ts +569 -0
  184. package/packages/core/src/agent/tracer.ts +100 -0
  185. package/packages/core/src/auth/auth.ts +108 -0
  186. package/packages/core/src/auth/index.ts +1 -0
  187. package/packages/core/src/canvas/a2ui-tools.ts +255 -0
  188. package/packages/core/src/canvas/canvas-manager.ts +390 -0
  189. package/packages/core/src/canvas/canvas-tools.ts +448 -0
  190. package/packages/core/src/canvas/emitter.ts +149 -0
  191. package/packages/core/src/canvas/index.ts +3 -0
  192. package/packages/core/src/channels/base.ts +154 -0
  193. package/packages/core/src/channels/discord.ts +273 -0
  194. package/packages/core/src/channels/index.ts +7 -0
  195. package/packages/core/src/channels/manager.ts +450 -0
  196. package/packages/core/src/channels/slack.ts +323 -0
  197. package/packages/core/src/channels/telegram.ts +612 -0
  198. package/packages/core/src/channels/webchat.ts +139 -0
  199. package/packages/core/src/channels/whatsapp.ts +548 -0
  200. package/packages/core/src/config/index.ts +12 -0
  201. package/packages/core/src/config/loader.ts +569 -0
  202. package/packages/core/src/events/agent-bus.ts +460 -0
  203. package/packages/core/src/events/event-bus.ts +169 -0
  204. package/packages/core/src/gateway/channel-notify.ts +64 -0
  205. package/packages/core/src/gateway/helpers/cors.ts +32 -0
  206. package/packages/core/src/gateway/helpers/index.ts +4 -0
  207. package/packages/core/src/gateway/helpers/narration.ts +57 -0
  208. package/packages/core/src/gateway/helpers/path.ts +13 -0
  209. package/packages/core/src/gateway/helpers/redact.ts +61 -0
  210. package/packages/core/src/gateway/index.ts +5 -0
  211. package/packages/core/src/gateway/initializer.ts +363 -0
  212. package/packages/core/src/gateway/lane-queue.ts +169 -0
  213. package/packages/core/src/gateway/llm-local/client.ts +94 -0
  214. package/packages/core/src/gateway/llm-local/detector.ts +321 -0
  215. package/packages/core/src/gateway/llm-local/downloader.ts +216 -0
  216. package/packages/core/src/gateway/llm-local/index.ts +34 -0
  217. package/packages/core/src/gateway/llm-local/manager.ts +186 -0
  218. package/packages/core/src/gateway/llm-local/models.ts +149 -0
  219. package/packages/core/src/gateway/llm-local/server.ts +179 -0
  220. package/packages/core/src/gateway/resolver.ts +108 -0
  221. package/packages/core/src/gateway/router.ts +124 -0
  222. package/packages/core/src/gateway/routes/agents.ts +210 -0
  223. package/packages/core/src/gateway/routes/auth.ts +244 -0
  224. package/packages/core/src/gateway/routes/channels.ts +484 -0
  225. package/packages/core/src/gateway/routes/chat.ts +241 -0
  226. package/packages/core/src/gateway/routes/config.ts +12 -0
  227. package/packages/core/src/gateway/routes/cron-api.ts +544 -0
  228. package/packages/core/src/gateway/routes/ethics.ts +46 -0
  229. package/packages/core/src/gateway/routes/llm-local.ts +271 -0
  230. package/packages/core/src/gateway/routes/mcp.ts +319 -0
  231. package/packages/core/src/gateway/routes/meeting.ts +232 -0
  232. package/packages/core/src/gateway/routes/models.ts +163 -0
  233. package/packages/core/src/gateway/routes/multimodal.ts +93 -0
  234. package/packages/core/src/gateway/routes/providers.ts +220 -0
  235. package/packages/core/src/gateway/routes/setup.ts +441 -0
  236. package/packages/core/src/gateway/routes/skills.ts +115 -0
  237. package/packages/core/src/gateway/routes/system.ts +469 -0
  238. package/packages/core/src/gateway/routes/tasks.ts +44 -0
  239. package/packages/core/src/gateway/routes/tools.ts +59 -0
  240. package/packages/core/src/gateway/routes/tts-local.ts +388 -0
  241. package/packages/core/src/gateway/routes/users.ts +122 -0
  242. package/packages/core/src/gateway/routes/voice.ts +189 -0
  243. package/packages/core/src/gateway/routes/workspace.ts +281 -0
  244. package/packages/core/src/gateway/server.ts +2744 -0
  245. package/packages/core/src/gateway/session.ts +95 -0
  246. package/packages/core/src/gateway/slash-commands.ts +207 -0
  247. package/packages/core/src/gateway/tts/README.md +94 -0
  248. package/packages/core/src/gateway/tts/package.json +25 -0
  249. package/packages/core/src/gateway/tts/src/client.ts +59 -0
  250. package/packages/core/src/gateway/tts/src/detect.ts +42 -0
  251. package/packages/core/src/gateway/tts/src/index.ts +15 -0
  252. package/packages/core/src/gateway/tts/src/install.ts +129 -0
  253. package/packages/core/src/gateway/tts/src/models.ts +50 -0
  254. package/packages/core/src/gateway/tts/src/server.ts +252 -0
  255. package/packages/core/src/gateway/tts/voices/.gitkeep +0 -0
  256. package/packages/core/src/heartbeat/index.ts +157 -0
  257. package/packages/core/src/index.ts +56 -0
  258. package/packages/core/src/mcp/hot-reload.ts +148 -0
  259. package/packages/core/src/mcp/singleton.ts +21 -0
  260. package/packages/core/src/mcp/tool-sync.ts +176 -0
  261. package/packages/core/src/multimodal/index.ts +2 -0
  262. package/packages/core/src/multimodal/types.ts +28 -0
  263. package/packages/core/src/multimodal/vision-service.ts +283 -0
  264. package/packages/core/src/plugins/api.ts +128 -0
  265. package/packages/core/src/plugins/index.ts +2 -0
  266. package/packages/core/src/plugins/loader.ts +365 -0
  267. package/packages/core/src/resilience/circuit-breaker.ts +225 -0
  268. package/packages/core/src/scheduler/CronScheduler.ts +699 -0
  269. package/packages/core/src/scheduler/dag/AgentExecutor.ts +53 -0
  270. package/packages/core/src/scheduler/dag/DAGScheduler.ts +250 -0
  271. package/packages/core/src/scheduler/dag/EventBridge.ts +122 -0
  272. package/packages/core/src/scheduler/dag/TaskGraph.ts +192 -0
  273. package/packages/core/src/scheduler/dag/TaskNode.ts +97 -0
  274. package/packages/core/src/scheduler/dag/TaskResult.ts +22 -0
  275. package/packages/core/src/scheduler/dag/errors.ts +37 -0
  276. package/packages/core/src/scheduler/dag/index.ts +26 -0
  277. package/packages/core/src/scheduler/dag/presets/ResearchPreset.ts +97 -0
  278. package/packages/core/src/scheduler/dag/strategies/ParallelStrategy.ts +21 -0
  279. package/packages/core/src/scheduler/dag/strategies/PriorityStrategy.ts +46 -0
  280. package/packages/core/src/scheduler/index.ts +22 -0
  281. package/packages/core/src/scheduler/integration.ts +237 -0
  282. package/packages/core/src/scheduler/types.ts +164 -0
  283. package/packages/core/src/security/google-chat.ts +269 -0
  284. package/packages/core/src/security/index.ts +192 -0
  285. package/packages/core/src/security/pairing.ts +250 -0
  286. package/packages/core/src/security/rate-limit.ts +270 -0
  287. package/packages/core/src/security/signal.ts +321 -0
  288. package/packages/core/src/state/store.ts +312 -0
  289. package/packages/core/src/storage/crypto.ts +197 -0
  290. package/packages/core/src/storage/migrate.ts +147 -0
  291. package/packages/core/src/storage/onboarding.ts +1506 -0
  292. package/packages/core/src/storage/schema.ts +666 -0
  293. package/packages/core/src/storage/seed.ts +628 -0
  294. package/packages/core/src/storage/sqlite.ts +407 -0
  295. package/packages/core/src/storage/usage.ts +374 -0
  296. package/packages/core/src/tool-runtime/index.ts +502 -0
  297. package/packages/core/src/tool-runtime/tool-worker.ts +125 -0
  298. package/packages/core/src/tools/agents/get-available-models.ts +118 -0
  299. package/packages/core/src/tools/agents/index.ts +610 -0
  300. package/packages/core/src/tools/canvas/index.ts +420 -0
  301. package/packages/core/src/tools/cli/index.ts +142 -0
  302. package/packages/core/src/tools/core/index.ts +478 -0
  303. package/packages/core/src/tools/cron/index.ts +635 -0
  304. package/packages/core/src/tools/filesystem/fs-delete.ts +78 -0
  305. package/packages/core/src/tools/filesystem/fs-edit.ts +106 -0
  306. package/packages/core/src/tools/filesystem/fs-exists.ts +63 -0
  307. package/packages/core/src/tools/filesystem/fs-glob.ts +108 -0
  308. package/packages/core/src/tools/filesystem/fs-list.ts +129 -0
  309. package/packages/core/src/tools/filesystem/fs-read.ts +72 -0
  310. package/packages/core/src/tools/filesystem/fs-write.ts +67 -0
  311. package/packages/core/src/tools/filesystem/index.ts +34 -0
  312. package/packages/core/src/tools/filesystem/workspace-guard.ts +62 -0
  313. package/packages/core/src/tools/index.ts +197 -0
  314. package/packages/core/src/tools/meeting/index.ts +363 -0
  315. package/packages/core/src/tools/office/index.ts +47 -0
  316. package/packages/core/src/tools/office/office-escribir-docx.ts +192 -0
  317. package/packages/core/src/tools/office/office-escribir-pdf.ts +172 -0
  318. package/packages/core/src/tools/office/office-escribir-pptx.ts +174 -0
  319. package/packages/core/src/tools/office/office-escribir-xlsx.ts +116 -0
  320. package/packages/core/src/tools/office/office-leer-docx.ts +93 -0
  321. package/packages/core/src/tools/office/office-leer-pdf.ts +114 -0
  322. package/packages/core/src/tools/office/office-leer-pptx.ts +136 -0
  323. package/packages/core/src/tools/office/office-leer-xlsx.ts +124 -0
  324. package/packages/core/src/tools/types.ts +39 -0
  325. package/packages/core/src/tools/voice/index.ts +104 -0
  326. package/packages/core/src/tools/web/browser-click.ts +78 -0
  327. package/packages/core/src/tools/web/browser-extract.ts +139 -0
  328. package/packages/core/src/tools/web/browser-navigate.ts +106 -0
  329. package/packages/core/src/tools/web/browser-screenshot.ts +87 -0
  330. package/packages/core/src/tools/web/browser-script.ts +88 -0
  331. package/packages/core/src/tools/web/browser-service.ts +554 -0
  332. package/packages/core/src/tools/web/browser-type.ts +101 -0
  333. package/packages/core/src/tools/web/browser-wait.ts +136 -0
  334. package/packages/core/src/tools/web/index.ts +41 -0
  335. package/packages/core/src/tools/web/web-fetch.ts +78 -0
  336. package/packages/core/src/tools/web/web-search.ts +123 -0
  337. package/packages/core/src/utils/benchmark.ts +80 -0
  338. package/packages/core/src/utils/crypto.ts +73 -0
  339. package/packages/core/src/utils/date.ts +42 -0
  340. package/packages/core/src/utils/index.ts +5 -0
  341. package/packages/core/src/utils/logger.ts +389 -0
  342. package/packages/core/src/utils/retry.ts +70 -0
  343. package/packages/core/src/utils/toon.ts +253 -0
  344. package/packages/core/src/voice/index.ts +643 -0
  345. package/packages/mcp/src/config.ts +13 -0
  346. package/packages/mcp/src/index.ts +1 -0
  347. package/packages/mcp/src/logger.ts +47 -0
  348. package/packages/mcp/src/manager.ts +439 -0
  349. package/packages/mcp/src/transports/index.ts +67 -0
  350. package/packages/mcp/src/transports/sse.ts +238 -0
  351. package/packages/mcp/src/transports/websocket.ts +159 -0
  352. package/packages/skills/src/bundled/agents/agent_spawner/SKILL.md +167 -0
  353. package/packages/skills/src/bundled/agents/code_delegator/SKILL.md +156 -0
  354. package/packages/skills/src/bundled/agents/memory_manager/SKILL.md +143 -0
  355. package/packages/skills/src/bundled/agents/research_and_remember/SKILL.md +139 -0
  356. package/packages/skills/src/bundled/agents/task_orchestrator/SKILL.md +198 -0
  357. package/packages/skills/src/bundled/canvas/a2ui_dashboard/SKILL.md +176 -0
  358. package/packages/skills/src/bundled/canvas/a2ui_form/SKILL.md +202 -0
  359. package/packages/skills/src/bundled/canvas/a2ui_interactive/SKILL.md +206 -0
  360. package/packages/skills/src/bundled/canvas/canvas_dashboard/SKILL.md +146 -0
  361. package/packages/skills/src/bundled/canvas/canvas_interact/SKILL.md +148 -0
  362. package/packages/skills/src/bundled/canvas/canvas_report/SKILL.md +146 -0
  363. package/packages/skills/src/bundled/cli/cli_pipeline/SKILL.md +136 -0
  364. package/packages/skills/src/bundled/cli/cli_safe_exec/SKILL.md +125 -0
  365. package/packages/skills/src/bundled/cron_manager/SKILL.md +188 -0
  366. package/packages/skills/src/bundled/cron_reminder/SKILL.md +112 -0
  367. package/packages/skills/src/bundled/filesystem/file_manager/SKILL.md +118 -0
  368. package/packages/skills/src/bundled/filesystem/file_read_and_summarize/SKILL.md +108 -0
  369. package/packages/skills/src/bundled/filesystem/file_writer/SKILL.md +135 -0
  370. package/packages/skills/src/bundled/meeting/meeting_transcription/SKILL.md +213 -0
  371. package/packages/skills/src/bundled/office/office_document_manager/SKILL.md +262 -0
  372. package/packages/skills/src/bundled/search_knowledge/busqueda_fts5/SKILL.md +74 -0
  373. package/packages/skills/src/bundled/voice/voice_assistant/SKILL.md +174 -0
  374. package/packages/skills/src/bundled/voice/voice_input/SKILL.md +146 -0
  375. package/packages/skills/src/bundled/voice/voice_output/SKILL.md +151 -0
  376. package/packages/skills/src/bundled/web/browser_automate/SKILL.md +120 -0
  377. package/packages/skills/src/bundled/web/browser_scrape/SKILL.md +109 -0
  378. package/packages/skills/src/bundled/web/web_monitor/SKILL.md +127 -0
  379. package/packages/skills/src/bundled/web/web_research/SKILL.md +119 -0
  380. package/packages/skills/src/bundled-data.generated.ts +1964 -0
  381. package/packages/skills/src/index.ts +1 -0
  382. package/packages/skills/src/loader.ts +388 -0
  383. package/dist/ui/assets/AgentNewPage-GB-tVN50.js +0 -1
  384. package/dist/ui/assets/BridgePage-DDcDILKu.js +0 -1
  385. package/dist/ui/assets/CanvasPage-oOk2sGOD.js +0 -33
  386. package/dist/ui/assets/DashboardPage-DV_2qWYJ.js +0 -6
  387. package/dist/ui/assets/LogsPage-DayYjh01.js +0 -1
  388. package/dist/ui/assets/MeetingPage-C01uPuqj.js +0 -1
  389. package/dist/ui/assets/ProjectsPage-B8_am_Ib.js +0 -1
  390. package/dist/ui/assets/ProvidersPage-DBzi66e4.js +0 -1
  391. package/dist/ui/assets/SettingsPage-CFA_Tknl.js +0 -9
  392. package/dist/ui/assets/SetupPage-BrUWbhvT.js +0 -1
  393. package/dist/ui/assets/accordion-DdAEfIXR.js +0 -1
  394. package/dist/ui/assets/chevron-down-DIosfU_U.js +0 -1
  395. package/dist/ui/assets/chevron-up-CI-W21Fy.js +0 -1
  396. package/dist/ui/assets/circle-S0-ouLz-.js +0 -1
  397. package/dist/ui/assets/circle-minus-CE0iJrl8.js +0 -1
  398. package/dist/ui/assets/circle-x-jUJ5zZvQ.js +0 -1
  399. package/dist/ui/assets/dropdown-menu-C2CXM1VE.js +0 -1
  400. package/dist/ui/assets/index-BN0875JH.css +0 -2
  401. package/dist/ui/assets/index-CH6sBa3Q.js +0 -116
  402. package/dist/ui/assets/pencil-5VdSj-h5.js +0 -1
  403. package/dist/ui/assets/progress-JN30I5fF.js +0 -1
  404. package/dist/ui/assets/scroll-area-BQQPitM8.js +0 -1
  405. package/dist/ui/assets/search-ChPgnVKj.js +0 -1
  406. package/dist/ui/assets/switch-C7W2-KEx.js +0 -1
  407. package/dist/ui/assets/terminal-C-R5Fckz.js +0 -1
  408. package/dist/ui/assets/useProviders-TBnWn-Hq.js +0 -1
  409. /package/dist/ui/assets/{card-DFKnZ6ky.js → card-CNf6BS2e.js} +0 -0
  410. /package/dist/ui/assets/{circle-alert-KuAm2FWh.js → circle-alert-CyHDwUj8.js} +0 -0
  411. /package/dist/ui/assets/{circle-check-6Ard1-2z.js → circle-check-Bb54Ebmu.js} +0 -0
  412. /package/dist/ui/assets/{cpu-KDy6-FAI.js → cpu-Cdgc_B1K.js} +0 -0
  413. /package/dist/ui/assets/{download-Cjbk4Rek.js → download-C3ifGMjJ.js} +0 -0
  414. /package/dist/ui/assets/{external-link-HtrFM63g.js → external-link-BvxYeTP1.js} +0 -0
  415. /package/dist/ui/assets/{eye-D1dB40_o.js → eye-DqNTU_GD.js} +0 -0
  416. /package/dist/ui/assets/{file-text-CE58EfH0.js → file-text-BT_9S9SM.js} +0 -0
  417. /package/dist/ui/assets/{folder-open-DIPKeiI_.js → folder-open-BhH8y9ac.js} +0 -0
  418. /package/dist/ui/assets/{format-BwdV8bB5.js → format-GVHeOyWI.js} +0 -0
  419. /package/dist/ui/assets/{gateway-url-D5uj6Nxg.js → gateway-url-COCbW0IR.js} +0 -0
  420. /package/dist/ui/assets/{gauge-DmQmJHEg.js → gauge-D_TMa4i9.js} +0 -0
  421. /package/dist/ui/assets/{globe-_hUGxQF4.js → globe-DeCQTCDJ.js} +0 -0
  422. /package/dist/ui/assets/{hexagon-BaNGQlQj.js → hexagon-DsGOUl-H.js} +0 -0
  423. /package/dist/ui/assets/{history-BfZVGlZa.js → history-BSG-Ypqf.js} +0 -0
  424. /package/dist/ui/assets/{info-CBZ5-AlC.js → info-NwLoa2Mj.js} +0 -0
  425. /package/dist/ui/assets/{key-Bv5DdTPh.js → key-3EP0dhkT.js} +0 -0
  426. /package/dist/ui/assets/{loader-circle-C4hhXLgp.js → loader-circle-CZNax6kS.js} +0 -0
  427. /package/dist/ui/assets/{lock-CkZYexqw.js → lock-Ei1_J-Nq.js} +0 -0
  428. /package/dist/ui/assets/{pause-Bpy1_s7y.js → pause-BUqah9Bi.js} +0 -0
  429. /package/dist/ui/assets/{play-Cj4osqJZ.js → play-NcZ4swwL.js} +0 -0
  430. /package/dist/ui/assets/{plus-BQhgZN3A.js → plus-CX1xyhp5.js} +0 -0
  431. /package/dist/ui/assets/{refresh-cw-BfREHVQM.js → refresh-cw-DaYdjQFk.js} +0 -0
  432. /package/dist/ui/assets/{save-FFTD4dMp.js → save-CUdYyHNy.js} +0 -0
  433. /package/dist/ui/assets/{settings-BdHKUL92.js → settings-Ds4SqD8s.js} +0 -0
  434. /package/dist/ui/assets/{sparkles-r4uJbJAl.js → sparkles-yUEb-7oH.js} +0 -0
  435. /package/dist/ui/assets/{square-G7Hyufqm.js → square-BD81nFtN.js} +0 -0
  436. /package/dist/ui/assets/{textarea-5kyuD04X.js → textarea-CXgXWKrT.js} +0 -0
  437. /package/dist/ui/assets/{trash-2-DXVBRWfh.js → trash-2-CNjMkoq6.js} +0 -0
  438. /package/dist/ui/assets/{triangle-alert-Bu5seg9O.js → triangle-alert-C9Y8Ub4X.js} +0 -0
  439. /package/dist/ui/assets/{vendor-router-CCECILJ0.js → vendor-router-C9pIYwbJ.js} +0 -0
  440. /package/dist/ui/assets/{volume-2-s9DuS696.js → volume-2-CeSXNDv4.js} +0 -0
  441. /package/dist/ui/assets/{zap-BPHZzXKV.js → zap-hlXjpSeA.js} +0 -0
@@ -0,0 +1,1656 @@
1
+ import * as p from "@clack/prompts";
2
+ import * as fs from "fs";
3
+ import * as path from "path";
4
+ import {
5
+ initOnboardingDb,
6
+ saveUserProfile,
7
+ saveProviderConfig,
8
+ saveAgentConfig,
9
+ saveOnboardingProgress,
10
+ activateChannel,
11
+ activateEthics,
12
+ getAllEthics,
13
+ saveVoiceConfig,
14
+ } from "@johpaz/hive-agents-core/storage/onboarding";
15
+ import { generateAuthToken } from "../utils/token";
16
+ import { getHiveDir } from "../../../core/src/config/loader";
17
+
18
+ // Log helper for @clack/prompts v0.5.1 (log export not available)
19
+ const log = {
20
+ success: (msg: string) => console.log(`\x1b[32m✔\x1b[0m ${msg}`),
21
+ error: (msg: string) => console.log(`\x1b[31m✖\x1b[0m ${msg}`),
22
+ warn: (msg: string) => console.log(`\x1b[33m⚠\x1b[0m ${msg}`),
23
+ info: (msg: string) => console.log(`\x1b[36mℹ\x1b[0m ${msg}`),
24
+ step: (msg: string) => console.log(`\n\x1b[36m›\x1b[0m ${msg}`),
25
+ };
26
+
27
+ function getEnvApiKey(keyName: string): string | null {
28
+ const hiveDir = getHiveDir();
29
+ const envPath = path.join(hiveDir, ".env");
30
+ if (!fs.existsSync(envPath)) return null;
31
+
32
+ const envContent = fs.readFileSync(envPath, "utf-8");
33
+ const match = envContent.match(new RegExp(`${keyName}=(.+)`));
34
+ return match ? match[1].trim() : null;
35
+ }
36
+
37
+ function reloadEnvToProcess(hiveDir: string): void {
38
+ const envPath = path.join(hiveDir, ".env");
39
+ if (!fs.existsSync(envPath)) return;
40
+
41
+ const envContent = fs.readFileSync(envPath, "utf-8");
42
+ const lines = envContent.split("\n");
43
+ for (const line of lines) {
44
+ const trimmed = line.trim();
45
+ if (!trimmed || trimmed.startsWith("#")) continue;
46
+ const [key, ...valueParts] = trimmed.split("=");
47
+ if (key && valueParts.length > 0) {
48
+ process.env[key] = valueParts.join("=").trim();
49
+ }
50
+ }
51
+ }
52
+
53
+ const VERSION = "0.0.37";
54
+
55
+ const DEFAULT_MODELS: Record<string, string> = {
56
+ anthropic: "claude-sonnet-4-6",
57
+ openai: "gpt-5.2",
58
+ gemini: "gemini-3.1-flash-preview",
59
+ mistral: "mistral-large-latest",
60
+ deepseek: "deepseek-chat",
61
+ kimi: "kimi-k2.5",
62
+ openrouter: "meta-llama/llama-3.3-70b-instruct",
63
+ ollama: "llama3.3:8b",
64
+ nvidia: "meta/llama-3.3-70b-instruct",
65
+ };
66
+
67
+ const PROVIDER_BASE_URLS: Record<string, string> = {
68
+ anthropic: "https://api.anthropic.com",
69
+ openai: "https://api.openai.com",
70
+ gemini: "https://generativelanguage.googleapis.com",
71
+ mistral: "https://api.mistral.ai/v1",
72
+ deepseek: "https://api.deepseek.com",
73
+ kimi: "https://api.moonshot.cn",
74
+ openrouter: "https://openrouter.ai/api",
75
+ ollama: "http://localhost:11434",
76
+ nvidia: "https://integrate.api.nvidia.com/v1",
77
+ };
78
+
79
+ const API_KEY_PLACEHOLDERS: Record<string, string> = {
80
+ anthropic: "sk-ant-...",
81
+ openai: "sk-...",
82
+ gemini: "AIza...",
83
+ mistral: "sk-...",
84
+ deepseek: "sk-...",
85
+ kimi: "sk-...",
86
+ openrouter: "sk-or-...",
87
+ ollama: "",
88
+ nvidia: "nvapi-...",
89
+ };
90
+
91
+ const API_KEY_LINKS: Record<string, string> = {
92
+ anthropic: "https://console.anthropic.com/keys",
93
+ openai: "https://platform.openai.com/api-keys",
94
+ gemini: "https://aistudio.google.com/app/apikey",
95
+ mistral: "https://console.mistral.ai/api-keys",
96
+ deepseek: "https://platform.deepseek.com/api_keys",
97
+ kimi: "https://platform.moonshot.cn/console/api-keys",
98
+ openrouter: "https://openrouter.ai/keys",
99
+ ollama: "",
100
+ nvidia: "https://build.nvidia.com/",
101
+ };
102
+
103
+ const AVAILABLE_MODELS: Record<string, Array<{ value: string; label: string; hint?: string }>> = {
104
+ anthropic: [
105
+ { value: "claude-sonnet-4-6", label: "Claude Sonnet 4.6", hint: "Recomendado — mejor equilibrio, 1M contexto" },
106
+ { value: "claude-opus-4-6", label: "Claude Opus 4.6", hint: "Más potente — agentic coding, 1M contexto" },
107
+ { value: "claude-haiku-4-6", label: "Claude Haiku 4.6", hint: "Más rápido y económico" },
108
+ ],
109
+ openai: [
110
+ { value: "gpt-5.2", label: "GPT-5.2", hint: "Recomendado — 400K contexto, latest" },
111
+ { value: "gpt-5.1", label: "GPT-5.1", hint: "Versión anterior estable" },
112
+ { value: "gpt-5.2-codex", label: "GPT-5.2 Codex", hint: "Especializado en código" },
113
+ { value: "o4-mini", label: "o4-mini", hint: "Razonamiento avanzado, económico" },
114
+ ],
115
+ gemini: [
116
+ { value: "gemini-3.1-flash-lite-preview", label: "Gemini 3.1 Flash (Preview)", hint: "Frontier-class, muy económico" },
117
+ { value: "gemini-3-flash-preview", label: "Gemini 3 Flash (Preview)", hint: "Frontier-class, muy económico" },
118
+ { value: "gemini-2.5-flash", label: "Gemini 2.5 Flash", hint: "Recomendado — estable, rápido" },
119
+ { value: "gemini-2.5-pro", label: "Gemini 2.5 Pro", hint: "Más potente — razonamiento profundo" },
120
+ { value: "gemini-3.1-pro-preview", label: "Gemini 3.1 Pro (Preview)", hint: "Latest — tareas complejas" },
121
+ ],
122
+ mistral: [
123
+ { value: "mistral-large-latest", label: "Mistral Large", hint: "Recomendado — potente, 1M contexto" },
124
+ { value: "mistral-small-latest", label: "Mistral Small", hint: "Económico y rápido" },
125
+ { value: "pixtral-large-latest", label: "Pixtral Large", hint: "Multimodal — visión" },
126
+ ],
127
+ deepseek: [
128
+ { value: "deepseek-chat", label: "DeepSeek-V3", hint: "Recomendado — muy económico, capaz" },
129
+ { value: "deepseek-reasoner", label: "DeepSeek-R1", hint: "Razonamiento profundo" },
130
+ { value: "deepseek-coder", label: "DeepSeek Coder", hint: "Especializado en código" },
131
+ ],
132
+ kimi: [
133
+ { value: "kimi-k2.5", label: "Kimi K2.5", hint: "Recomendado — multimodal, agentic, 1T params" },
134
+ { value: "kimi-k2-thinking", label: "Kimi K2 Thinking", hint: "Largo razonamiento" },
135
+ { value: "kimi-k2-turbo-preview", label: "Kimi K2 Turbo", hint: "Rápido, preview" },
136
+ ],
137
+ openrouter: [
138
+ { value: "meta-llama/llama-3.3-70b-instruct", label: "Llama 3.3 70B", hint: "Gratis — GPT-4 level" },
139
+ { value: "google/gemini-2.0-flash-exp:free", label: "Gemini 2.0 Flash", hint: "Gratis — 1M contexto" },
140
+ { value: "deepseek/deepseek-r1:free", label: "DeepSeek R1", hint: "Gratis — razonamiento fuerte" },
141
+ { value: "anthropic/claude-sonnet-4-6", label: "Claude Sonnet 4.6", hint: "Vía OpenRouter" },
142
+ ],
143
+ ollama: [
144
+ { value: "llama3.3:8b", label: "Llama 3.3 8B", hint: "Recomendado — general, ~5GB RAM" },
145
+ { value: "qwen2.5:7b", label: "Qwen 2.5 7B", hint: "Multilingual, código, ~4.5GB RAM" },
146
+ { value: "mistral:7b", label: "Mistral 7B", hint: "Rápido, ~4GB RAM" },
147
+ { value: "phi4:14b", label: "Phi-4 14B", hint: "Mejor calidad, ~8GB RAM" },
148
+ ],
149
+ nvidia: [
150
+ { value: "meta/llama-3.3-70b-instruct", label: "Llama 3.3 70B", hint: "General — 128K contexto" },
151
+ { value: "nvidia/llama-3.1-nemotron-ultra-253b-v1", label: "Nemotron Ultra 253B", hint: "NVIDIA flagship — reasoning" },
152
+ { value: "deepseek-ai/deepseek-v3.2", label: "DeepSeek V3.2", hint: "Multimodal, código — 128K" },
153
+ { value: "qwen/qwen3-coder-480b-a35b-instruct", label: "Qwen3 Coder 480B", hint: "Especializado en código" },
154
+ { value: "google/gemma-4-31b-it", label: "Gemma 4 31B", hint: "Multimodal — 256K contexto" },
155
+ { value: "moonshotai/kimi-k2-thinking", label: "Kimi K2 Thinking", hint: "Razonamiento profundo — 256K" },
156
+ ],
157
+ };
158
+
159
+ const BUNDLED_SKILLS = [
160
+ { name: "web_search", label: "Web Search", hint: "Buscar en la web", default: true },
161
+ { name: "shell", label: "Shell", hint: "Ejecutar comandos", default: true },
162
+ { name: "file_manager", label: "File Manager", hint: "Operaciones de archivos", default: true },
163
+ { name: "http_client", label: "HTTP Client", hint: "Peticiones HTTP", default: true },
164
+ { name: "memory", label: "Memory", hint: "Memoria persistente", default: true },
165
+ { name: "cron_manager", label: "Cron Manager", hint: "Tareas programadas", default: false },
166
+ { name: "system_notify", label: "System Notify", hint: "Notificaciones desktop", default: false },
167
+ { name: "browser_automation", label: "Browser Automation", hint: "Automatizar navegador", default: false },
168
+ { name: "context_compact", label: "Context Compact", hint: "Compactar contexto", default: false },
169
+ ];
170
+
171
+ type NavAction = "next" | "prev" | "cancel";
172
+
173
+ async function askNavigation(): Promise<NavAction> {
174
+ const action = await p.select({
175
+ message: "Acciones:",
176
+ options: [
177
+ { value: "next", label: "➡️ Siguiente", hint: "Continuar" },
178
+ { value: "prev", label: "⬅️ Anterior", hint: "Volver atrás" },
179
+ { value: "cancel", label: "❌ Cancelar", hint: "Salir del onboarding" },
180
+ ],
181
+ });
182
+
183
+ if (p.isCancel(action) || action === "cancel") {
184
+ return "cancel";
185
+ }
186
+ return action as NavAction;
187
+ }
188
+
189
+ function showProgress(current: number, total: number, title: string): void {
190
+ const filled = "█".repeat(current);
191
+ const empty = "░".repeat(total - current);
192
+ p.note(`${filled}${empty} Sección ${current}/${total}: ${title}`, "Progreso");
193
+ }
194
+
195
+ interface OnboardConfig {
196
+ agentName: string;
197
+ agentId: string;
198
+ userName: string;
199
+ userId: string;
200
+ provider: string;
201
+ model: string;
202
+ apiKey: string;
203
+ channel: string;
204
+ channelToken: string;
205
+ workspace: string;
206
+ tools: string[];
207
+ mcp?: { servers: Record<string, unknown> };
208
+ agentTone: "formal" | "friendly" | "direct";
209
+
210
+ userLanguage?: string;
211
+ userTimezone?: string;
212
+ userOccupation?: string;
213
+ userNotes?: string;
214
+ ethicsChoice?: string;
215
+ agentDescription: string;
216
+ }
217
+
218
+ async function checkCommand(cmd: string): Promise<boolean> {
219
+ const proc = Bun.spawn(["which", cmd], { stdout: "pipe", stderr: "pipe" })
220
+ const code = await proc.exited
221
+ return code === 0
222
+ }
223
+
224
+
225
+
226
+
227
+
228
+
229
+
230
+
231
+
232
+ function generateToken(): string {
233
+ const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
234
+ let token = "";
235
+ for (let i = 0; i < 32; i++) {
236
+ token += chars.charAt(Math.floor(Math.random() * chars.length));
237
+ }
238
+ return token;
239
+ }
240
+
241
+ async function verifyTelegramToken(token: string): Promise<{ ok: boolean; username?: string }> {
242
+ try {
243
+ const res = await fetch(
244
+ `https://api.telegram.org/bot${token}/getMe`,
245
+ { signal: AbortSignal.timeout(5000) }
246
+ );
247
+ const data = (await res.json()) as {
248
+ ok: boolean;
249
+ result?: { username: string; first_name: string };
250
+ };
251
+ return {
252
+ ok: data.ok,
253
+ username: data.result?.username,
254
+ };
255
+ } catch {
256
+ return { ok: false };
257
+ }
258
+ }
259
+
260
+ function validateDiscordToken(token: string): boolean {
261
+ return token.length >= 50;
262
+ }
263
+
264
+ function validateSlackToken(token: string): boolean {
265
+ return token.startsWith("xoxb-") || token.startsWith("xoxp-");
266
+ }
267
+
268
+ async function testLLMConnection(provider: string, apiKey: string, model: string): Promise<boolean> {
269
+ if (provider === "ollama") {
270
+ try {
271
+ const response = await fetch("http://localhost:11434/api/tags");
272
+ return response.ok;
273
+ } catch {
274
+ return false;
275
+ }
276
+ }
277
+
278
+ const testMessages = [{ role: "user" as const, content: "Say 'ok' if you can read this." }];
279
+
280
+ try {
281
+ if (provider === "anthropic") {
282
+ const response = await fetch("https://api.anthropic.com/v1/messages", {
283
+ method: "POST",
284
+ headers: {
285
+ "Content-Type": "application/json",
286
+ "x-api-key": apiKey,
287
+ "anthropic-version": "2023-06-01",
288
+ "anthropic-dangerous-direct-browser-access": "true",
289
+ },
290
+ body: JSON.stringify({
291
+ model: model,
292
+ max_tokens: 10,
293
+ messages: testMessages,
294
+ }),
295
+ });
296
+ return response.ok;
297
+ }
298
+
299
+ if (provider === "openai") {
300
+ const response = await fetch("https://api.openai.com/v1/chat/completions", {
301
+ method: "POST",
302
+ headers: {
303
+ "Content-Type": "application/json",
304
+ Authorization: `Bearer ${apiKey}`,
305
+ },
306
+ body: JSON.stringify({
307
+ model: model,
308
+ max_tokens: 10,
309
+ messages: testMessages,
310
+ }),
311
+ });
312
+ return response.ok;
313
+ }
314
+
315
+ if (provider === "gemini") {
316
+ const response = await fetch(
317
+ `https://generativelanguage.googleapis.com/v1beta/models/${model}:generateContent?key=${apiKey}`,
318
+ {
319
+ method: "POST",
320
+ headers: { "Content-Type": "application/json" },
321
+ body: JSON.stringify({
322
+ contents: [{ parts: [{ text: "Say ok" }] }],
323
+ }),
324
+ }
325
+ );
326
+ return response.ok;
327
+ }
328
+
329
+ if (provider === "deepseek") {
330
+ const response = await fetch("https://api.deepseek.com/v1/chat/completions", {
331
+ method: "POST",
332
+ headers: {
333
+ "Content-Type": "application/json",
334
+ Authorization: `Bearer ${apiKey}`,
335
+ },
336
+ body: JSON.stringify({
337
+ model: model,
338
+ max_tokens: 10,
339
+ messages: testMessages,
340
+ }),
341
+ });
342
+ return response.ok;
343
+ }
344
+
345
+ if (provider === "kimi") {
346
+ const response = await fetch("https://api.moonshot.cn/v1/chat/completions", {
347
+ method: "POST",
348
+ headers: {
349
+ "Content-Type": "application/json",
350
+ Authorization: `Bearer ${apiKey}`,
351
+ },
352
+ body: JSON.stringify({
353
+ model: model,
354
+ max_tokens: 10,
355
+ messages: testMessages,
356
+ }),
357
+ });
358
+ return response.ok;
359
+ }
360
+
361
+ if (provider === "openrouter") {
362
+ const response = await fetch("https://openrouter.ai/api/v1/chat/completions", {
363
+ method: "POST",
364
+ headers: {
365
+ "Content-Type": "application/json",
366
+ Authorization: `Bearer ${apiKey}`,
367
+ },
368
+ body: JSON.stringify({
369
+ model: model,
370
+ max_tokens: 10,
371
+ messages: testMessages,
372
+ }),
373
+ });
374
+ return response.ok;
375
+ }
376
+
377
+ if (provider === "nvidia") {
378
+ const response = await fetch("https://integrate.api.nvidia.com/v1/chat/completions", {
379
+ method: "POST",
380
+ headers: {
381
+ "Content-Type": "application/json",
382
+ Authorization: `Bearer ${apiKey}`,
383
+ },
384
+ body: JSON.stringify({
385
+ model: model,
386
+ max_tokens: 10,
387
+ messages: testMessages,
388
+ }),
389
+ });
390
+ return response.ok;
391
+ }
392
+
393
+ return false;
394
+ } catch {
395
+ return false;
396
+ }
397
+ }
398
+
399
+ async function generateWorkspace(workspace: string, agentName: string, userName: string, userId: string, userLanguage: string, userTimezone: string, ethicsChoice: string): Promise<void> {
400
+ // Create workspace directory - .md files are now stored in SQLite
401
+ if (!fs.existsSync(workspace)) {
402
+ fs.mkdirSync(workspace, { recursive: true });
403
+ }
404
+
405
+ // Files are loaded from SQLite via the agent service
406
+ // Workspace directory is created but .md files are not generated here
407
+ // The system will use default prompts from the database
408
+ }
409
+
410
+ async function installSystemdService(): Promise<void> {
411
+ const home = process.env.HOME || "";
412
+ const systemdDir = path.join(home, ".config", "systemd", "user");
413
+
414
+ if (!fs.existsSync(systemdDir)) {
415
+ fs.mkdirSync(systemdDir, { recursive: true });
416
+ }
417
+
418
+ const serviceContent = `[Unit]
419
+ Description=Hive Personal AI Gateway
420
+ After=network-online.target
421
+ Wants=network-online.target
422
+
423
+ [Service]
424
+ Type=simple
425
+ ExecStart=${home}/.bun/bin/hive start
426
+ ExecStop=${home}/.bun/bin/hive stop
427
+ Restart=on-failure
428
+ RestartSec=5
429
+ Environment=PATH=${home}/.bun/bin:${home}/.npm-global/bin:/usr/local/bin:/usr/bin:/bin
430
+ WorkingDirectory=${home}
431
+
432
+ [Install]
433
+ WantedBy=default.target
434
+ `;
435
+
436
+ const servicePath = path.join(systemdDir, "hive.service");
437
+ fs.writeFileSync(servicePath, serviceContent, "utf-8");
438
+
439
+ const { spawnSync } = require("child_process");
440
+ spawnSync("systemctl", ["--user", "daemon-reload"], { stdio: "inherit" });
441
+ spawnSync("systemctl", ["--user", "enable", "hive"], { stdio: "inherit" });
442
+ }
443
+
444
+ async function isGatewayRunning(): Promise<boolean> {
445
+ const pidFile = path.join(getHiveDir(), "gateway.pid");
446
+ if (!fs.existsSync(pidFile)) return false;
447
+
448
+ try {
449
+ const pid = parseInt(fs.readFileSync(pidFile, "utf-8").trim(), 10);
450
+ process.kill(pid, 0);
451
+ return true;
452
+ } catch {
453
+ return false;
454
+ }
455
+ }
456
+
457
+ async function reloadGateway(): Promise<void> {
458
+ const pidFile = path.join(getHiveDir(), "gateway.pid");
459
+ if (!fs.existsSync(pidFile)) return;
460
+
461
+ try {
462
+ const pid = parseInt(fs.readFileSync(pidFile, "utf-8").trim(), 10);
463
+ process.kill(pid, "SIGHUP");
464
+ } catch {
465
+ // Gateway not running
466
+ }
467
+ }
468
+
469
+ interface ExistingConfig {
470
+ agentName: string;
471
+ provider: string;
472
+ model: string;
473
+ channels: string[];
474
+ apiKey: string;
475
+ workspace: string;
476
+ skills: string[];
477
+ raw: Record<string, unknown>;
478
+ }
479
+
480
+ function parseExistingConfig(raw: Record<string, unknown>): ExistingConfig {
481
+ const agents = (raw.agents as Record<string, unknown>)?.list as Array<Record<string, unknown>> | undefined;
482
+ const models = raw.models as Record<string, unknown> | undefined;
483
+ const providers = models?.providers as Record<string, Record<string, unknown>> | undefined;
484
+ const defaultProvider = models?.defaultProvider as string | undefined;
485
+ const providerConfig = providers?.[defaultProvider || ""] as Record<string, unknown> | undefined;
486
+
487
+ return {
488
+ agentName: (agents?.[0]?.name as string) ?? "Hive",
489
+ provider: defaultProvider ?? "gemini",
490
+ model: (models?.defaults as Record<string, string>)?.[defaultProvider || ""] ?? "",
491
+ channels: Object.keys((raw.channels as Record<string, unknown>) ?? {}),
492
+ apiKey: (providerConfig?.apiKey as string) ?? "",
493
+ workspace: (agents?.[0]?.workspace as string) ?? path.join(getHiveDir(), "workspace"),
494
+ skills: ((raw.skills as Record<string, unknown>)?.allowBundled as string[]) ?? [],
495
+ raw,
496
+ };
497
+ }
498
+
499
+ async function runUpdateWizard(existing: ExistingConfig): Promise<void> {
500
+ p.note(
501
+ "Presiona Enter para mantener el valor actual de cada campo.",
502
+ "✏️ Modo actualización"
503
+ );
504
+
505
+ // Nombre del agente
506
+ const agentName = await p.text({
507
+ message: "Nombre del agente:",
508
+ placeholder: existing.agentName,
509
+ defaultValue: existing.agentName,
510
+ });
511
+
512
+ if (p.isCancel(agentName)) {
513
+ p.cancel("Actualización cancelada.");
514
+ process.exit(0);
515
+ }
516
+
517
+ // Proveedor LLM
518
+ const changeProvider = await p.confirm({
519
+ message: `Proveedor actual: ${existing.provider} (${existing.model || "no configurado"}). ¿Cambiar?`,
520
+ initialValue: false,
521
+ });
522
+
523
+ if (p.isCancel(changeProvider)) {
524
+ p.cancel("Actualización cancelada.");
525
+ process.exit(0);
526
+ }
527
+
528
+ let provider = existing.provider;
529
+ let model = existing.model;
530
+ let apiKey = existing.apiKey;
531
+
532
+ if (changeProvider) {
533
+ provider = await p.select({
534
+ message: "Nuevo proveedor LLM:",
535
+ options: [
536
+ { value: "anthropic", label: "Anthropic (Claude)", hint: "Recomendado — Claude 4.6" },
537
+ { value: "openai", label: "OpenAI (GPT-5)", hint: "GPT-5.2" },
538
+ { value: "gemini", label: "Google Gemini", hint: "Gemini 3 Flash" },
539
+ { value: "mistral", label: "Mistral AI", hint: "Mistral Large" },
540
+ { value: "deepseek", label: "DeepSeek", hint: "Muy económico" },
541
+ { value: "kimi", label: "Kimi (Moonshot AI)", hint: "Contexto largo" },
542
+ { value: "nvidia", label: "NVIDIA NIM", hint: "Modelos gratuitos" },
543
+ { value: "openrouter", label: "OpenRouter", hint: "Multi-modelo" },
544
+ { value: "ollama", label: "Ollama (local)", hint: "Sin costo" },
545
+ ],
546
+ }) as string;
547
+
548
+ if (p.isCancel(provider)) {
549
+ p.cancel("Actualización cancelada.");
550
+ process.exit(0);
551
+ }
552
+
553
+ const models = AVAILABLE_MODELS[provider] || [{ value: DEFAULT_MODELS[provider], label: DEFAULT_MODELS[provider] }];
554
+
555
+ if (models.length > 1) {
556
+ model = await p.select({
557
+ message: `Modelo de ${provider}:`,
558
+ options: models,
559
+ }) as string;
560
+
561
+ if (p.isCancel(model)) {
562
+ p.cancel("Actualización cancelada.");
563
+ process.exit(0);
564
+ }
565
+ } else {
566
+ model = models[0].value;
567
+ }
568
+
569
+ if (provider !== "ollama") {
570
+ const link = API_KEY_LINKS[provider];
571
+ if (link) {
572
+ p.note(`Obtén tu API key en:\n${link}`, `API key de ${provider}`);
573
+ }
574
+
575
+ const keyResult = await p.password({
576
+ message: `API key de ${provider}:`,
577
+ validate: (v) => (!v || v.length < 10 ? "La key parece muy corta" : undefined),
578
+ });
579
+
580
+ if (p.isCancel(keyResult)) {
581
+ p.cancel("Actualización cancelada.");
582
+ process.exit(0);
583
+ }
584
+ apiKey = keyResult;
585
+
586
+ const spinner = p.spinner();
587
+ spinner.start(`Verificando conexión con ${provider}...`);
588
+
589
+ const connected = await testLLMConnection(provider, apiKey, model as string);
590
+
591
+ if (!connected) {
592
+ spinner.stop(`❌ Error conectando con ${provider}`);
593
+ p.outro("API key inválida. Ejecuta 'hive onboard' de nuevo con la key correcta.");
594
+ process.exit(1);
595
+ }
596
+
597
+ spinner.stop(`✅ Conexión con ${provider} verificada`);
598
+ }
599
+ }
600
+
601
+ // Canales
602
+ const currentChannels = existing.channels.length > 0 ? existing.channels.join(", ") : "ninguno";
603
+ const changeChannel = await p.confirm({
604
+ message: `Canales configurados: ${currentChannels}. ¿Añadir o cambiar?`,
605
+ initialValue: false,
606
+ });
607
+
608
+ if (p.isCancel(changeChannel)) {
609
+ p.cancel("Actualización cancelada.");
610
+ process.exit(0);
611
+ }
612
+
613
+ let channels = existing.raw.channels as Record<string, unknown> | undefined;
614
+
615
+ if (changeChannel) {
616
+ const channel = await p.select({
617
+ message: "Canal a configurar:",
618
+ options: [
619
+ { value: "telegram", label: "Telegram", hint: "Recomendado" },
620
+ { value: "discord", label: "Discord" },
621
+ { value: "webchat", label: "WebChat (local)" },
622
+ { value: "none", label: "Ninguno" },
623
+ ],
624
+ }) as string;
625
+
626
+ if (p.isCancel(channel)) {
627
+ p.cancel("Actualización cancelada.");
628
+ process.exit(0);
629
+ }
630
+
631
+ if (channel === "telegram") {
632
+ p.note(
633
+ "1. Abre Telegram y busca @BotFather\n" +
634
+ "2. Escribe /newbot y sigue las instrucciones\n" +
635
+ "3. Copia el token que te da BotFather",
636
+ "Cómo obtener el token de Telegram"
637
+ );
638
+
639
+ const tokenResult = await p.password({
640
+ message: "Token de Telegram BotFather:",
641
+ validate: (v) => (!v?.trim() ? "El token no puede estar vacío" : undefined),
642
+ });
643
+
644
+ if (p.isCancel(tokenResult)) {
645
+ p.cancel("Actualización cancelada.");
646
+ process.exit(0);
647
+ }
648
+
649
+ const spinner = p.spinner();
650
+ spinner.start("Verificando token de Telegram...");
651
+ const tg = await verifyTelegramToken(tokenResult as string);
652
+ if (tg.ok && tg.username) {
653
+ spinner.stop(`✅ Bot verificado: @${tg.username}`);
654
+ } else {
655
+ spinner.stop("⚠️ Token no verificado — se guardará de todas formas");
656
+ p.note(
657
+ "El token se guardó pero no pudo verificarse.\n" +
658
+ "Si es incorrecto, ejecuta: hive onboard (opción actualizar)",
659
+ "Aviso"
660
+ );
661
+ }
662
+
663
+ const telegramAccount: Record<string, unknown> = {
664
+ botToken: tokenResult,
665
+ dmPolicy: "open",
666
+ };
667
+
668
+ channels = {
669
+ telegram: {
670
+ accounts: {
671
+ default: telegramAccount,
672
+ },
673
+ },
674
+ };
675
+ } else if (channel === "discord") {
676
+ p.note(
677
+ "1. Ve a https://discord.com/developers/applications\n" +
678
+ "2. Crea una nueva aplicación\n" +
679
+ "3. Ve a Bot → Reset Token\n" +
680
+ "4. Habilita 'Message Content Intent'",
681
+ "Cómo obtener el token de Discord"
682
+ );
683
+
684
+ const tokenResult = await p.password({
685
+ message: "Token del bot de Discord:",
686
+ validate: (v) => (!v?.trim() ? "El token no puede estar vacío" : undefined),
687
+ });
688
+
689
+ if (p.isCancel(tokenResult)) {
690
+ p.cancel("Actualización cancelada.");
691
+ process.exit(0);
692
+ }
693
+
694
+ channels = {
695
+ discord: {
696
+ accounts: {
697
+ default: {
698
+ token: tokenResult,
699
+ },
700
+ },
701
+ },
702
+ };
703
+ }
704
+ }
705
+
706
+ // Guardar cambios
707
+ const spinner = p.spinner();
708
+ spinner.start("Guardando cambios...");
709
+
710
+ const baseUrlMap: Record<string, string> = {
711
+ gemini: "https://generativelanguage.googleapis.com/v1beta",
712
+ deepseek: "https://api.deepseek.com/v1",
713
+ kimi: "https://api.moonshot.cn/v1",
714
+ ollama: "http://localhost:11434/api",
715
+ nvidia: "https://integrate.api.nvidia.com/v1",
716
+ };
717
+
718
+ const providersConfig: Record<string, Record<string, unknown>> = {};
719
+ if (provider !== "ollama" && apiKey) {
720
+ providersConfig[provider] = { apiKey };
721
+ if (baseUrlMap[provider]) {
722
+ providersConfig[provider].baseUrl = baseUrlMap[provider];
723
+ }
724
+ }
725
+
726
+ const updatedConfig: Record<string, unknown> = {
727
+ ...existing.raw,
728
+ name: agentName,
729
+ agents: {
730
+ list: [
731
+ {
732
+ id: "main",
733
+ default: true,
734
+ name: agentName,
735
+ workspace: existing.workspace,
736
+ agentDir: path.join(getHiveDir(), "agents", "main", "agent"),
737
+ },
738
+ ],
739
+ },
740
+ models: {
741
+ defaultProvider: provider,
742
+ defaults: {
743
+ [provider]: model,
744
+ },
745
+ providers: providersConfig,
746
+ },
747
+ };
748
+
749
+ if (channels) {
750
+ updatedConfig.channels = channels;
751
+ }
752
+
753
+ log.success("✅ Configuración actualizada en BD");
754
+ // Also update SQLite
755
+ try {
756
+ const { initializeDatabase, getDb } = await import("../../../core/src/storage/sqlite");
757
+ initializeDatabase();
758
+ const sqliteDb = getDb();
759
+
760
+ const apiKeyVal = (providersConfig[provider]?.apiKey as string) || null;
761
+ const baseUrlVal = (providersConfig[provider]?.baseUrl as string) || null;
762
+
763
+ sqliteDb.query(`
764
+ UPDATE providers SET api_key = ?, base_url = ?
765
+ WHERE id = ?
766
+ `).run(apiKeyVal, baseUrlVal, provider);
767
+
768
+ sqliteDb.query(`
769
+ UPDATE agents SET name = ?, provider_id = ?, model_id = ?
770
+ WHERE id = 'main'
771
+ `).run(agentName, provider, model);
772
+
773
+ console.log("✅ SQLite actualizado");
774
+ } catch (error) {
775
+ console.log("⚠️ Error actualizando SQLite:", (error as Error).message);
776
+ }
777
+
778
+ spinner.stop("Cambios guardados ✅");
779
+
780
+ // Recargar Gateway si está corriendo
781
+ const running = await isGatewayRunning();
782
+ if (running) {
783
+ const reload = await p.confirm({
784
+ message: "El Gateway está corriendo. ¿Recargar configuración ahora?",
785
+ initialValue: true,
786
+ });
787
+
788
+ if (reload) {
789
+ await reloadGateway();
790
+ log.success("Configuración recargada ✅");
791
+ }
792
+ }
793
+
794
+ const channelDisplay = channels ? Object.keys(channels).join(", ") || "ninguno" : existing.channels.join(", ") || "ninguno";
795
+
796
+ p.outro(
797
+ `✅ Configuración actualizada.\n\n` +
798
+ ` Agente: ${agentName}\n` +
799
+ ` Proveedor: ${provider} (${model})\n` +
800
+ ` Canales: ${channelDisplay}\n\n` +
801
+ ` hive status → ver estado actual\n` +
802
+ ` hive reload → recargar manualmente`
803
+ );
804
+ }
805
+
806
+ async function runFullWizard(): Promise<void> {
807
+ p.intro("🐝 Bienvenido a Hive — Personal AI Gateway");
808
+
809
+ // Initialize DB at start
810
+ initOnboardingDb();
811
+
812
+ const TOTAL_STEPS = 7;
813
+ let step = 1;
814
+
815
+ // Shared wizard state — pre-populated with defaults
816
+ const state = {
817
+ // Step 1: User Profile
818
+ agentName: "Hive",
819
+ userName: "Usuario",
820
+ userId: "", // Se genera automáticamente en la BD
821
+ userLanguage: "Spanish",
822
+ userTimezone: Intl.DateTimeFormat().resolvedOptions().timeZone || "America/Bogota",
823
+ userOccupation: "",
824
+ userNotes: "",
825
+ sessionToken: "", // For webchat user_identity
826
+ // Step 2: Agent Profile
827
+ agentId: "", // Se genera automáticamente en la BD
828
+ agentTone: "friendly" as "formal" | "friendly" | "direct",
829
+ agentDescription: "Asistente personal inteligente y desarrollador senior.",
830
+ // Step 3: Ethics
831
+ ethicsId: "default",
832
+ // Step 4: Tools
833
+ tools: [] as string[],
834
+ // Step 5: Provider
835
+ provider: "gemini",
836
+ model: "gemini-2.5-flash",
837
+ apiKey: "",
838
+ // Voice config
839
+ voiceEnabled: false,
840
+ sttProvider: "",
841
+ ttsProvider: "",
842
+ ttsVoiceId: "",
843
+ elevenlabsApiKey: "",
844
+ // Step 6: Channel
845
+ channel: "webchat",
846
+ channelToken: "",
847
+ // Step 7: Code Bridge
848
+ };
849
+
850
+ const hiveDir = getHiveDir();
851
+
852
+ // ─────────────────────────────────────
853
+ // Step machine: 9 steps in correct order
854
+ // ─────────────────────────────────────
855
+
856
+ while (step <= TOTAL_STEPS) {
857
+ switch (step) {
858
+
859
+ // ═══════════════════════════════════
860
+ // STEP 1: User Profile
861
+ // ═══════════════════════════════════
862
+ case 1: {
863
+ showProgress(step, TOTAL_STEPS, "Tu perfil");
864
+
865
+ const userName = await p.text({
866
+ message: "👤 ¿Cómo te llamas?",
867
+ placeholder: "Usuario",
868
+ defaultValue: state.userName,
869
+ });
870
+ if (p.isCancel(userName)) { p.cancel("Onboarding cancelado."); process.exit(0); }
871
+ state.userName = userName;
872
+
873
+ const userLanguage = await p.select({
874
+ message: "🌐 ¿En qué idioma prefieres que te responda?",
875
+ options: [
876
+ { value: "Spanish", label: "Español" },
877
+ { value: "English", label: "English" },
878
+ { value: "Spanish, English", label: "Ambos / Both" },
879
+ ],
880
+ initialValue: state.userLanguage,
881
+ });
882
+ if (p.isCancel(userLanguage)) { p.cancel("Onboarding cancelado."); process.exit(0); }
883
+ state.userLanguage = userLanguage as string;
884
+
885
+ const userTimezone = await p.text({
886
+ message: "🕐 ¿Cuál es tu zona horaria?",
887
+ placeholder: "America/Bogota",
888
+ defaultValue: state.userTimezone,
889
+ });
890
+ if (p.isCancel(userTimezone)) { p.cancel("Onboarding cancelado."); process.exit(0); }
891
+ state.userTimezone = userTimezone as string;
892
+
893
+ const userOccupation = await p.text({
894
+ message: "💼 ¿A qué te dedicas?",
895
+ placeholder: "desarrollador de software",
896
+ defaultValue: state.userOccupation || undefined,
897
+ });
898
+ if (p.isCancel(userOccupation)) { p.cancel("Onboarding cancelado."); process.exit(0); }
899
+ state.userOccupation = userOccupation || "";
900
+
901
+ const userNotes = await p.text({
902
+ message: "💡 ¿Algo importante que el agente deba recordar de ti?",
903
+ placeholder: "Prefiero respuestas directas.",
904
+ defaultValue: state.userNotes || undefined,
905
+ });
906
+ if (p.isCancel(userNotes)) { p.cancel("Onboarding cancelado."); process.exit(0); }
907
+ state.userNotes = userNotes || "";
908
+
909
+ // Generate session token for webchat (user_identity)
910
+ state.sessionToken = generateAuthToken();
911
+
912
+ // ✅ Save user to DB (agent will be saved in Step 2)
913
+ // userId se genera automáticamente en la BD (randomblob)
914
+ state.userId = saveUserProfile({
915
+ userName: state.userName,
916
+ userLanguage: state.userLanguage,
917
+ userTimezone: state.userTimezone,
918
+ userOccupation: state.userOccupation,
919
+ userNotes: state.userNotes,
920
+ channelUserId: state.sessionToken, // For webchat user_identity
921
+ });
922
+
923
+ saveOnboardingProgress({
924
+ step: "user",
925
+ userId: state.userId,
926
+ data: { userName: state.userName, userLanguage: state.userLanguage, userTimezone: state.userTimezone, userOccupation: state.userOccupation, userNotes: state.userNotes },
927
+ });
928
+
929
+ log.success(`✅ Usuario guardado con ID: ${state.userId}`);
930
+
931
+ const nav = await askNavigation();
932
+ if (nav === "cancel") { p.cancel("Onboarding cancelado."); process.exit(0); }
933
+ if (nav === "prev") { step = 1; break; }
934
+ step = 2;
935
+ break;
936
+ }
937
+
938
+ // ═══════════════════════════════════
939
+ // STEP 2: Perfil del Agente
940
+ // ═══════════════════════════════════
941
+ case 2: {
942
+ showProgress(step, TOTAL_STEPS, "Tu agente");
943
+
944
+ const agentName = await p.text({
945
+ message: "🤖 ¿Cómo se llamará tu agente?",
946
+ placeholder: "Bee",
947
+ defaultValue: state.agentName,
948
+ });
949
+ if (p.isCancel(agentName)) { p.cancel("Onboarding cancelado."); process.exit(0); }
950
+ state.agentName = agentName;
951
+
952
+ const agentDescription = await p.text({
953
+ message: "📝 ¿Qué quieres que haga este agente? (Su objetivo principal)",
954
+ placeholder: "Ser un asistente personal inteligente y experto en desarrollo.",
955
+ defaultValue: state.agentDescription,
956
+ });
957
+ if (p.isCancel(agentDescription)) { p.cancel("Onboarding cancelado."); process.exit(0); }
958
+ state.agentDescription = agentDescription;
959
+
960
+ const agentTone = await p.select({
961
+ message: "🎭 Elige el tono de comunicación del agente:",
962
+ options: [
963
+ { value: "friendly", label: "Amigable y cercano" },
964
+ { value: "professional", label: "Profesional y formal" },
965
+ { value: "direct", label: "Directo y conciso" },
966
+ { value: "casual", label: "Casual y relajado" },
967
+ ],
968
+ initialValue: state.agentTone,
969
+ });
970
+ if (p.isCancel(agentTone)) { p.cancel("Onboarding cancelado."); process.exit(0); }
971
+ state.agentTone = agentTone as "formal" | "friendly" | "direct";
972
+
973
+ // ✅ Save agent to DB (agentId se genera automáticamente)
974
+ state.agentId = saveAgentConfig({
975
+ userId: state.userId,
976
+ agentName: state.agentName,
977
+ description: state.agentDescription,
978
+ tone: state.agentTone,
979
+ providerId: "",
980
+ modelId: "",
981
+ });
982
+
983
+ // Activate default ethics
984
+ activateEthics(state.userId, "default");
985
+
986
+ saveOnboardingProgress({
987
+ step: "agent",
988
+ userId: state.userId,
989
+ data: { agentName: state.agentName, agentDescription: state.agentDescription, agentTone: state.agentTone, agentId: state.agentId },
990
+ });
991
+
992
+ log.success(`✅ Agente creado con ID: ${state.agentId}`);
993
+
994
+ const nav = await askNavigation();
995
+ if (nav === "cancel") { p.cancel("Onboarding cancelado."); process.exit(0); }
996
+ if (nav === "prev") { step = 1; break; }
997
+ step = 3;
998
+ break;
999
+ }
1000
+
1001
+ // ═══════════════════════════════════
1002
+ // STEP 3: Ethics
1003
+ // ═══════════════════════════════════
1004
+ case 3: {
1005
+ showProgress(step, TOTAL_STEPS, "Ética");
1006
+
1007
+ p.note("Los lineamientos éticos tienen MÁXIMA prioridad.", "Ética del agente");
1008
+
1009
+ const ethicsOptions = getAllEthics();
1010
+ console.log("📋 Ethics options:", ethicsOptions);
1011
+
1012
+ if (ethicsOptions.length === 0) {
1013
+ log.warn("No hay éticas disponibles en la BD. Usando ética por defecto.");
1014
+ state.ethicsId = "default";
1015
+ } else {
1016
+ const selectedEthics = await p.select({
1017
+ message: "¿Qué lineamientos éticos prefieres?",
1018
+ options: ethicsOptions.map(e => ({
1019
+ value: e.id,
1020
+ label: e.name,
1021
+ hint: e.isDefault ? "Recomendado" : e.description,
1022
+ })),
1023
+ initialValue: state.ethicsId,
1024
+ });
1025
+ if (p.isCancel(selectedEthics)) { p.cancel("Onboarding cancelado."); process.exit(0); }
1026
+ state.ethicsId = selectedEthics as string;
1027
+ }
1028
+
1029
+ // ✅ Activate ethics in DB
1030
+ activateEthics(state.userId, state.ethicsId);
1031
+
1032
+ saveOnboardingProgress({
1033
+ step: "ethics",
1034
+ userId: state.userId,
1035
+ data: { ethicsId: state.ethicsId },
1036
+ });
1037
+
1038
+ const nav = await askNavigation();
1039
+ if (nav === "cancel") { p.cancel("Onboarding cancelado."); process.exit(0); }
1040
+ if (nav === "prev") { step = 2; break; }
1041
+ step = 4;
1042
+ break;
1043
+ }
1044
+
1045
+ // ═══════════════════════════════════
1046
+ // STEP 4: Provider & Model
1047
+ // ═══════════════════════════════════
1048
+ case 4: {
1049
+ showProgress(step, TOTAL_STEPS, "Proveedor LLM");
1050
+
1051
+ const provider = await p.select({
1052
+ message: "¿Qué proveedor de LLM quieres usar?",
1053
+ options: [
1054
+ { value: "gemini", label: "Google Gemini", hint: "Recomendado — económico, gran contexto" },
1055
+ { value: "anthropic", label: "Anthropic Claude", hint: "Premium — mejor calidad" },
1056
+ { value: "openai", label: "OpenAI", hint: "Estándar de la industria" },
1057
+ { value: "ollama", label: "Ollama (Local)", hint: "Gratis, corre localmente" },
1058
+ { value: "nvidia", label: "NVIDIA NIM", hint: "Modelos gratuitos en build.nvidia.com" },
1059
+ { value: "openrouter", label: "OpenRouter", hint: "Múltiples proveedores" },
1060
+ ],
1061
+ initialValue: state.provider,
1062
+ });
1063
+ if (p.isCancel(provider)) { p.cancel("Onboarding cancelado."); process.exit(0); }
1064
+ state.provider = provider as string;
1065
+
1066
+ const models = AVAILABLE_MODELS[state.provider] || [];
1067
+ const model = await p.select({
1068
+ message: `¿Qué modelo de ${provider}?`,
1069
+ options: models,
1070
+ initialValue: DEFAULT_MODELS[state.provider],
1071
+ });
1072
+ if (p.isCancel(model)) { p.cancel("Onboarding cancelado."); process.exit(0); }
1073
+ state.model = model as string;
1074
+
1075
+ let apiKey = "";
1076
+ if (state.provider !== "ollama") {
1077
+ const link = API_KEY_LINKS[state.provider];
1078
+ if (link) {
1079
+ p.note(`Obtén tu API key en: ${link}`, "API Key");
1080
+ }
1081
+ const keyResult = await p.password({
1082
+ message: `API key de ${state.provider}:`,
1083
+ validate: (v) => (!v || v.length < 10 ? "La key parece muy corta" : undefined),
1084
+ });
1085
+ if (p.isCancel(keyResult)) { p.cancel("Onboarding cancelado."); process.exit(0); }
1086
+ apiKey = keyResult;
1087
+ }
1088
+ state.apiKey = apiKey;
1089
+
1090
+ // ✅ Save provider & activate model in DB
1091
+ await saveProviderConfig({
1092
+ userId: state.userId,
1093
+ provider: state.provider,
1094
+ model: state.model,
1095
+ apiKey: state.apiKey,
1096
+ baseUrl: PROVIDER_BASE_URLS[state.provider],
1097
+ });
1098
+
1099
+ saveOnboardingProgress({
1100
+ step: "provider",
1101
+ userId: state.userId,
1102
+ data: { provider: state.provider, model: state.model },
1103
+ });
1104
+
1105
+ const nav = await askNavigation();
1106
+ if (nav === "cancel") { p.cancel("Onboarding cancelado."); process.exit(0); }
1107
+ if (nav === "prev") { step = 3; break; }
1108
+ step = 5;
1109
+ break;
1110
+ }
1111
+
1112
+ // ═══════════════════════════════════
1113
+ // STEP 5: Voice Configuration
1114
+ // ═══════════════════════════════════
1115
+ case 5: {
1116
+ showProgress(step, TOTAL_STEPS, "Voz");
1117
+
1118
+ p.note("Configura el reconocimiento de voz y síntesis de audio.", "Voz");
1119
+
1120
+ const wantsVoice = await p.confirm({
1121
+ message: "¿Quieres habilitar voz en tu agente?",
1122
+ initialValue: false,
1123
+ });
1124
+ if (p.isCancel(wantsVoice)) { p.cancel("Onboarding cancelado."); process.exit(0); }
1125
+
1126
+ state.voiceEnabled = wantsVoice as boolean;
1127
+
1128
+ if (wantsVoice) {
1129
+ // Variables to store API keys for saving to DB later
1130
+ let sttApiKeyValue: string | null = null;
1131
+ let ttsApiKeyValue: string | null = null;
1132
+
1133
+ // Ask for STT provider (speech-to-text)
1134
+ const sttProvider = await p.select({
1135
+ message: "¿Qué servicio de transcripción de voz (STT)?",
1136
+ options: [
1137
+ { value: "whisper-large-v3-turbo", label: "Groq Whisper Large V3 Turbo", hint: "Recomendado - rápido" },
1138
+ { value: "whisper-large-v3", label: "Groq Whisper Large V3", hint: "Máxima precisión" },
1139
+ { value: "distil-whisper-large-v3-en", label: "Groq Distil Whisper Large V3", hint: "Solo inglés" },
1140
+ { value: "whisper-1", label: "OpenAI Whisper 1", hint: "Alternativa" },
1141
+ ],
1142
+ initialValue: state.sttProvider || "whisper-large-v3-turbo",
1143
+ });
1144
+ if (p.isCancel(sttProvider)) { p.cancel("Onboarding cancelado."); process.exit(0); }
1145
+ state.sttProvider = sttProvider as string;
1146
+
1147
+ // Ask for API key based on STT provider
1148
+ const isGroqStt = ["whisper-large-v3", "whisper-large-v3-turbo", "distil-whisper-large-v3-en"].includes(sttProvider);
1149
+ const isOpenAiStt = sttProvider === "whisper-1";
1150
+
1151
+ if (isGroqStt) {
1152
+ const existingGroqKey = getEnvApiKey("GROQ_API_KEY");
1153
+ let groqKey: string | null = existingGroqKey;
1154
+
1155
+ if (!groqKey) {
1156
+ const groqKeyInput = await p.password({
1157
+ message: "Ingresa tu GROQ_API_KEY:",
1158
+ validate: (value) => {
1159
+ if (!value || value.length < 10) return "API key inválida";
1160
+ },
1161
+ });
1162
+ if (p.isCancel(groqKeyInput)) { p.cancel("Onboarding cancelado."); process.exit(0); }
1163
+ groqKey = groqKeyInput as string;
1164
+ sttApiKeyValue = groqKey;
1165
+
1166
+ // Save Groq API key to .env
1167
+ const hiveDir = getHiveDir();
1168
+ const envPath = path.join(hiveDir, ".env");
1169
+ const envContent = fs.existsSync(envPath) ? fs.readFileSync(envPath, "utf-8") : "";
1170
+ const newEnv = envContent.includes("GROQ_API_KEY")
1171
+ ? envContent.replace(/GROQ_API_KEY=.*/g, `GROQ_API_KEY=${groqKey}`)
1172
+ : envContent + `\nGROQ_API_KEY=${groqKey}`;
1173
+ fs.writeFileSync(envPath, newEnv);
1174
+ reloadEnvToProcess(hiveDir);
1175
+ log.success("✅ API key de Groq guardada en .env");
1176
+ } else {
1177
+ log.info("✅ GROQ_API_KEY ya configurada en .env");
1178
+ sttApiKeyValue = groqKey;
1179
+ }
1180
+ } else if (isOpenAiStt) {
1181
+ const existingOpenAiKey = getEnvApiKey("OPENAI_API_KEY");
1182
+ let openaiKey: string | null = existingOpenAiKey;
1183
+
1184
+ if (!openaiKey) {
1185
+ const openaiKeyInput = await p.password({
1186
+ message: "Ingresa tu OPENAI_API_KEY:",
1187
+ validate: (value) => {
1188
+ if (!value || value.length < 10) return "API key inválida";
1189
+ },
1190
+ });
1191
+ if (p.isCancel(openaiKeyInput)) { p.cancel("Onboarding cancelado."); process.exit(0); }
1192
+ openaiKey = openaiKeyInput as string;
1193
+ sttApiKeyValue = openaiKey;
1194
+
1195
+ // Save OpenAI API key to .env
1196
+ const hiveDir = getHiveDir();
1197
+ const envPath = path.join(hiveDir, ".env");
1198
+ const envContent = fs.existsSync(envPath) ? fs.readFileSync(envPath, "utf-8") : "";
1199
+ const newEnv = envContent.includes("OPENAI_API_KEY")
1200
+ ? envContent.replace(/OPENAI_API_KEY=.*/g, `OPENAI_API_KEY=${openaiKey}`)
1201
+ : envContent + `\nOPENAI_API_KEY=${openaiKey}`;
1202
+ fs.writeFileSync(envPath, newEnv);
1203
+ reloadEnvToProcess(hiveDir);
1204
+ log.success("✅ API key de OpenAI guardada en .env");
1205
+ } else {
1206
+ log.info("✅ OPENAI_API_KEY ya configurada en .env");
1207
+ sttApiKeyValue = openaiKey;
1208
+ }
1209
+ }
1210
+
1211
+ // Ask for TTS provider (text-to-speech)
1212
+ const ttsProvider = await p.select({
1213
+ message: "¿Qué servicio de síntesis de voz (TTS)?",
1214
+ options: [
1215
+ { value: "eleven_flash_v2_5", label: "ElevenLabs Flash V2.5", hint: "Tiempo real - recomendado" },
1216
+ { value: "eleven_turbo_v2_5", label: "ElevenLabs Turbo V2.5", hint: "Balance calidad/velocidad" },
1217
+ { value: "eleven_multilingual_v2", label: "ElevenLabs Multilingual V2", hint: "Alta calidad" },
1218
+ { value: "eleven_v3", label: "ElevenLabs V3", hint: "Más expresivo" },
1219
+ { value: "qwen3-tts-instruct-flash", label: "Qwen TTS Flash", hint: "Gratis - recomendado" },
1220
+ { value: "gpt-4o-mini-tts", label: "OpenAI GPT-4o Mini TTS", hint: "Usa tu API key" },
1221
+ { value: "tts-1", label: "OpenAI TTS-1", hint: "Estándar" },
1222
+ { value: "tts-1-hd", label: "OpenAI TTS-1 HD", hint: "Alta calidad" },
1223
+ { value: "gemini-2.5-flash-preview-tts", label: "Gemini 2.5 Flash TTS", hint: "Google" },
1224
+ { value: "gemini-2.5-pro-preview-tts", label: "Gemini 2.5 Pro TTS", hint: "Google Pro" },
1225
+ ],
1226
+ initialValue: state.ttsProvider || "eleven_flash_v2_5",
1227
+ });
1228
+ if (p.isCancel(ttsProvider)) { p.cancel("Onboarding cancelado."); process.exit(0); }
1229
+ state.ttsProvider = ttsProvider as string;
1230
+
1231
+ // Ask for API key based on TTS provider
1232
+ const isElevenLabs = ["eleven_flash_v2_5", "eleven_turbo_v2_5", "eleven_multilingual_v2", "eleven_v3"].includes(ttsProvider);
1233
+ const isOpenAI = ["gpt-4o-mini-tts", "tts-1", "tts-1-hd"].includes(ttsProvider);
1234
+ const isGemini = ["gemini-2.5-flash-preview-tts", "gemini-2.5-pro-preview-tts"].includes(ttsProvider);
1235
+ const isQwen = ["qwen3-tts-instruct-flash", "qwen3-tts-flash", "qwen-tts"].includes(ttsProvider);
1236
+
1237
+ if (isElevenLabs) {
1238
+ const existingElevenKey = getEnvApiKey("ELEVENLABS_API_KEY");
1239
+ let elevenKey: string | null = existingElevenKey;
1240
+
1241
+ if (!elevenKey) {
1242
+ const elevenKeyInput = await p.password({
1243
+ message: "Ingresa tu ELEVENLABS_API_KEY:",
1244
+ validate: (value) => {
1245
+ if (!value || value.length < 10) return "API key inválida";
1246
+ },
1247
+ });
1248
+ if (p.isCancel(elevenKeyInput)) { p.cancel("Onboarding cancelado."); process.exit(0); }
1249
+ elevenKey = elevenKeyInput as string;
1250
+ state.elevenlabsApiKey = elevenKey;
1251
+
1252
+ // Save to .env
1253
+ const hiveDir = getHiveDir();
1254
+ const envPath = path.join(hiveDir, ".env");
1255
+ const envContent = fs.existsSync(envPath) ? fs.readFileSync(envPath, "utf-8") : "";
1256
+ const newEnv = envContent.includes("ELEVENLABS_API_KEY")
1257
+ ? envContent.replace(/ELEVENLABS_API_KEY=.*/g, `ELEVENLABS_API_KEY=${elevenKey}`)
1258
+ : envContent + `\nELEVENLABS_API_KEY=${elevenKey}`;
1259
+ fs.writeFileSync(envPath, newEnv);
1260
+ reloadEnvToProcess(hiveDir);
1261
+ log.success("✅ API key de ElevenLabs guardada en .env");
1262
+ } else {
1263
+ log.info("✅ ELEVENLABS_API_KEY ya configurada en .env");
1264
+ state.elevenlabsApiKey = elevenKey;
1265
+ }
1266
+ ttsApiKeyValue = elevenKey;
1267
+ } else if (isOpenAI) {
1268
+ const existingOpenAiKey = getEnvApiKey("OPENAI_API_KEY");
1269
+ let openaiKey: string | null = existingOpenAiKey;
1270
+
1271
+ if (!openaiKey) {
1272
+ const openaiKeyInput = await p.password({
1273
+ message: "Ingresa tu OPENAI_API_KEY:",
1274
+ validate: (value) => {
1275
+ if (!value || value.length < 10) return "API key inválida";
1276
+ },
1277
+ });
1278
+ if (p.isCancel(openaiKeyInput)) { p.cancel("Onboarding cancelado."); process.exit(0); }
1279
+ openaiKey = openaiKeyInput as string;
1280
+
1281
+ // Save to .env
1282
+ const hiveDir = getHiveDir();
1283
+ const envPath = path.join(hiveDir, ".env");
1284
+ const envContent = fs.existsSync(envPath) ? fs.readFileSync(envPath, "utf-8") : "";
1285
+ const newEnv = envContent.includes("OPENAI_API_KEY")
1286
+ ? envContent.replace(/OPENAI_API_KEY=.*/g, `OPENAI_API_KEY=${openaiKey}`)
1287
+ : envContent + `\nOPENAI_API_KEY=${openaiKey}`;
1288
+ fs.writeFileSync(envPath, newEnv);
1289
+ reloadEnvToProcess(hiveDir);
1290
+ log.success("✅ API key de OpenAI guardada en .env");
1291
+ } else {
1292
+ log.info("✅ OPENAI_API_KEY ya configurada en .env");
1293
+ }
1294
+ ttsApiKeyValue = openaiKey;
1295
+ } else if (isGemini) {
1296
+ const existingGeminiKey = getEnvApiKey("GEMINI_API_KEY");
1297
+ let geminiKey: string | null = existingGeminiKey;
1298
+
1299
+ if (!geminiKey) {
1300
+ const geminiKeyInput = await p.password({
1301
+ message: "Ingresa tu GEMINI_API_KEY:",
1302
+ validate: (value) => {
1303
+ if (!value || value.length < 10) return "API key inválida";
1304
+ },
1305
+ });
1306
+ if (p.isCancel(geminiKeyInput)) { p.cancel("Onboarding cancelado."); process.exit(0); }
1307
+ geminiKey = geminiKeyInput as string;
1308
+
1309
+ // Save to .env
1310
+ const hiveDir = getHiveDir();
1311
+ const envPath = path.join(hiveDir, ".env");
1312
+ const envContent = fs.existsSync(envPath) ? fs.readFileSync(envPath, "utf-8") : "";
1313
+ const newEnv = envContent.includes("GEMINI_API_KEY")
1314
+ ? envContent.replace(/GEMINI_API_KEY=.*/g, `GEMINI_API_KEY=${geminiKey}`)
1315
+ : envContent + `\nGEMINI_API_KEY=${geminiKey}`;
1316
+ fs.writeFileSync(envPath, newEnv);
1317
+ reloadEnvToProcess(hiveDir);
1318
+ log.success("✅ API key de Gemini guardada en .env");
1319
+ } else {
1320
+ log.info("✅ GEMINI_API_KEY ya configurada en .env");
1321
+ }
1322
+ ttsApiKeyValue = geminiKey;
1323
+ } else if (isQwen) {
1324
+ const existingQwenKey = getEnvApiKey("DASHSCOPE_API_KEY");
1325
+ let qwenKey: string | null = existingQwenKey;
1326
+
1327
+ if (!qwenKey) {
1328
+ const qwenKeyInput = await p.password({
1329
+ message: "Ingresa tu DASHSCOPE_API_KEY (Qwen/Alibaba):",
1330
+ validate: (value) => {
1331
+ if (!value || value.length < 10) return "API key inválida";
1332
+ },
1333
+ });
1334
+ if (p.isCancel(qwenKeyInput)) { p.cancel("Onboarding cancelado."); process.exit(0); }
1335
+ qwenKey = qwenKeyInput as string;
1336
+
1337
+ // Save to .env
1338
+ const hiveDir = getHiveDir();
1339
+ const envPath = path.join(hiveDir, ".env");
1340
+ const envContent = fs.existsSync(envPath) ? fs.readFileSync(envPath, "utf-8") : "";
1341
+ const newEnv = envContent.includes("DASHSCOPE_API_KEY")
1342
+ ? envContent.replace(/DASHSCOPE_API_KEY=.*/g, `DASHSCOPE_API_KEY=${qwenKey}`)
1343
+ : envContent + `\nDASHSCOPE_API_KEY=${qwenKey}`;
1344
+ fs.writeFileSync(envPath, newEnv);
1345
+ reloadEnvToProcess(hiveDir);
1346
+ log.success("✅ API key de Qwen guardada en .env");
1347
+ } else {
1348
+ log.info("✅ DASHSCOPE_API_KEY ya configurada en .env");
1349
+ }
1350
+ ttsApiKeyValue = qwenKey;
1351
+ }
1352
+
1353
+ // Save voice config to channel (for webchat as default)
1354
+ // API keys are saved in .env and encrypted in BD
1355
+ await saveVoiceConfig({
1356
+ userId: state.userId,
1357
+ channelId: "webchat",
1358
+ voiceEnabled: true,
1359
+ sttProvider: sttProvider as string,
1360
+ ttsProvider: ttsProvider as string,
1361
+ sttApiKey: sttApiKeyValue || undefined,
1362
+ ttsApiKey: ttsApiKeyValue || undefined,
1363
+ });
1364
+ log.success("✅ Configuración de voz guardada en BD");
1365
+ }
1366
+
1367
+ const nav2 = await askNavigation();
1368
+ if (nav2 === "cancel") { p.cancel("Onboarding cancelado."); process.exit(0); }
1369
+ if (nav2 === "prev") { step = 4; break; }
1370
+ step = 6;
1371
+ break;
1372
+ }
1373
+
1374
+ // ═══════════════════════════════════
1375
+ // STEP 6: Channels
1376
+ // ═══════════════════════════════════
1377
+ case 6: {
1378
+ showProgress(step, TOTAL_STEPS, "Canales");
1379
+
1380
+ // WebChat siempre activado por defecto
1381
+ state.channel = "webchat";
1382
+
1383
+ // ✅ Activar WebChat en BD (UPDATE del seed)
1384
+ await activateChannel(state.userId, {
1385
+ channelId: "webchat",
1386
+ channelUserId: state.sessionToken,
1387
+ });
1388
+
1389
+ log.success("✅ WebChat activado por defecto (http://localhost:18790/ui)");
1390
+
1391
+ // Preguntar si quiere activar Telegram (recomendado)
1392
+ const activateTelegram = await p.confirm({
1393
+ message: "¿Quieres activar Telegram? (recomendado para notificaciones)",
1394
+ initialValue: false,
1395
+ });
1396
+ if (p.isCancel(activateTelegram)) { p.cancel("Onboarding cancelado."); process.exit(0); }
1397
+
1398
+ if (activateTelegram) {
1399
+ p.note(
1400
+ "1. Abre Telegram y busca @BotFather\n" +
1401
+ "2. Escribe /newbot y sigue las instrucciones\n" +
1402
+ "3. Copia el token",
1403
+ "Cómo obtener token de Telegram"
1404
+ );
1405
+ const tokenResult = await p.password({
1406
+ message: "Token de Telegram:",
1407
+ validate: (v) => (!v?.trim() ? "El token no puede estar vacío" : undefined),
1408
+ });
1409
+ if (p.isCancel(tokenResult)) { p.cancel("Onboarding cancelado."); process.exit(0); }
1410
+ state.channelToken = tokenResult;
1411
+
1412
+ // ✅ Activar Telegram en BD (UPDATE del seed)
1413
+ await activateChannel(state.userId, {
1414
+ channelId: "telegram",
1415
+ config: { botToken: state.channelToken, dmPolicy: "open" },
1416
+ });
1417
+
1418
+ log.success("✅ Telegram activado");
1419
+ }
1420
+
1421
+ saveOnboardingProgress({
1422
+ step: "channel",
1423
+ userId: state.userId,
1424
+ data: { channel: "webchat", telegram: activateTelegram },
1425
+ });
1426
+
1427
+ const nav = await askNavigation();
1428
+ if (nav === "cancel") { p.cancel("Onboarding cancelado."); process.exit(0); }
1429
+ if (nav === "prev") { step = 4; break; }
1430
+ step = 7;
1431
+ break;
1432
+ }
1433
+
1434
+ // ═══════════════════════════════════
1435
+ // STEP 7: Resumen Final
1436
+ // ═══════════════════════════════════
1437
+ case 7: {
1438
+ showProgress(step, TOTAL_STEPS, "Resumen final");
1439
+
1440
+
1441
+ // Show summary
1442
+ p.note(
1443
+ ` Agente: ${state.agentName}\n` +
1444
+ ` Usuario: ${state.userName}\n` +
1445
+ ` Idioma: ${state.userLanguage}\n` +
1446
+ ` Proveedor: ${state.provider} (${state.model})\n` +
1447
+ ` Voz: ${state.voiceEnabled ? `STT: ${state.sttProvider}, TTS: ${state.ttsProvider}` : "no"}\n` +
1448
+ ` Canal: ${state.channel}${state.channelToken ? ' (Telegram configurado)' : ''}`,
1449
+ "📋 Resumen final"
1450
+ );
1451
+
1452
+ const confirm = await p.select({
1453
+ message: "¿Confirmar configuración?",
1454
+ options: [
1455
+ { value: "confirm", label: "✅ Confirmar", hint: "Crear agente" },
1456
+ { value: "prev", label: "⬅️ Atrás", hint: "Editar" },
1457
+ { value: "cancel", label: "❌ Cancelar", hint: "Salir" },
1458
+ ],
1459
+ });
1460
+
1461
+ if (p.isCancel(confirm) || confirm === "cancel") {
1462
+ p.cancel("Onboarding cancelado.");
1463
+ process.exit(0);
1464
+ }
1465
+
1466
+ if (confirm === "prev") {
1467
+ step = 6;
1468
+ break;
1469
+ }
1470
+
1471
+ // ✅ Create agent in DB (FINAL STEP)
1472
+ saveAgentConfig({
1473
+ userId: state.userId,
1474
+ agentId: state.agentId,
1475
+ agentName: state.agentName,
1476
+ providerId: state.provider,
1477
+ modelId: state.model,
1478
+ tone: state.agentTone,
1479
+ description: state.agentDescription,
1480
+ });
1481
+
1482
+ saveOnboardingProgress({
1483
+ step: "agent",
1484
+ userId: state.userId,
1485
+ data: { agentId: state.agentId, agentName: state.agentName },
1486
+ });
1487
+
1488
+ // 📝 Guardar IDs en .env
1489
+ const envPath = path.join(hiveDir, ".env");
1490
+ let envContent = "";
1491
+ if (fs.existsSync(envPath)) {
1492
+ envContent = fs.readFileSync(envPath, "utf-8");
1493
+ }
1494
+
1495
+ const envVars = {
1496
+ HIVE_USER_ID: state.userId,
1497
+ HIVE_AGENT_ID: state.agentId,
1498
+ };
1499
+
1500
+ let newEnvContent = envContent;
1501
+ for (const [key, value] of Object.entries(envVars)) {
1502
+ const regex = new RegExp(`^${key}=.*`, "m");
1503
+ if (regex.test(newEnvContent)) {
1504
+ newEnvContent = newEnvContent.replace(regex, `${key}=${value}`);
1505
+ } else {
1506
+ newEnvContent += `\n${key}=${value}`;
1507
+ }
1508
+ }
1509
+ fs.writeFileSync(envPath, newEnvContent.trim() + "\n");
1510
+ log.info(`IDs de identidad persistidos en ${envPath}`);
1511
+
1512
+ step = TOTAL_STEPS + 1;
1513
+ break;
1514
+ }
1515
+ }
1516
+ }
1517
+
1518
+ // ═══════════════════════════════════
1519
+ // FINAL: Show success message
1520
+ // ═══════════════════════════════════
1521
+
1522
+ p.outro(
1523
+ `✅ ¡Configuración completada!\n\n` +
1524
+ ` 🤖 Agente: ${state.agentName}\n` +
1525
+ ` 👤 Usuario: ${state.userName}\n` +
1526
+ ` 🧠 Provider: ${state.provider}/${state.model}\n` +
1527
+ ` 📢 Canal: WebChat (UI web)\n` +
1528
+ `${state.channelToken ? ` ✈️ Telegram: Configurado\n` : ''}` +
1529
+ `${state.voiceEnabled ? ` 🎤 Voz: STT (${state.sttProvider}) + TTS (${state.ttsProvider})\n` : ''}` +
1530
+ `\n` +
1531
+ `🌐 ABRE TU DASHBOARD:\n` +
1532
+ ` 👉 http://localhost:5173\n\n` +
1533
+ `📋 COMANDOS:\n` +
1534
+ ` hive start → Arrancar gateway\n` +
1535
+ ` hive chat → Chatear en terminal\n\n` +
1536
+ `💡 DESDE EL DASHBOARD puedes activar:\n` +
1537
+ ` • Canales adicionales (Discord, Slack, WhatsApp, etc.)\n` +
1538
+ ` • Voz (STT/TTS) para audio en tiempo real\n` +
1539
+ ` • Code Bridge para delegar tareas de código\n` +
1540
+ ` • MCP servers para herramientas externas\n` +
1541
+ ` • Tools y funciones personalizadas\n` +
1542
+ `\n` +
1543
+ `🎉 ¡Gracias por configurar Hive!`
1544
+ );
1545
+
1546
+ // Ask if user wants to start the gateway
1547
+ const shouldStart = await p.confirm({
1548
+ message: "¿Quieres iniciar el gateway ahora?",
1549
+ initialValue: true,
1550
+ });
1551
+
1552
+ if (p.isCancel(shouldStart) || !shouldStart) {
1553
+ log.info("¡Perfecto! Ejecuta 'hive start' cuando quieras iniciar.");
1554
+ return;
1555
+ }
1556
+
1557
+ log.info("🚀 Iniciando Hive gateway...");
1558
+ }
1559
+
1560
+ export async function onboard(): Promise<void> {
1561
+ const hiveDir = getHiveDir();
1562
+
1563
+ // Create Hive directory if it doesn't exist
1564
+ if (!fs.existsSync(hiveDir)) {
1565
+ log.info(`📁 Creando carpeta de configuración: ${hiveDir}`);
1566
+ fs.mkdirSync(hiveDir, { recursive: true });
1567
+ }
1568
+
1569
+ // Initialize DB first to check existing configuration
1570
+ initOnboardingDb();
1571
+
1572
+ // Check if there's already an agent in the database
1573
+ const { getDb } = await import("../../../core/src/storage/sqlite");
1574
+ const db = getDb();
1575
+ // We look for the coordinator agent. In a personal gateway, there is usually only one.
1576
+ const existingAgent = db.query("SELECT id, name, provider_id, model_id, status FROM agents WHERE role = 'coordinator' ORDER BY created_at DESC LIMIT 1").get() as any;
1577
+
1578
+ // If agent exists and is completed (status = 'idle'), don't show onboarding menu
1579
+ if (existingAgent && existingAgent.status === 'idle') {
1580
+ p.intro("✅ Configuración completada detectada");
1581
+
1582
+ p.note(
1583
+ ` Agente: ${existingAgent.name}\n` +
1584
+ ` ID: ${existingAgent.id}\n` +
1585
+ ` Provider: ${existingAgent.provider_id || "no configurado"}\n` +
1586
+ ` Model: ${existingAgent.model_id || "no configurado"}\n` +
1587
+ ` Status: idle (listo para usar)`,
1588
+ "Config actual en BD"
1589
+ );
1590
+
1591
+ console.log("\n💡 Para iniciar el gateway: hive start\n");
1592
+ return;
1593
+ }
1594
+
1595
+ if (existingAgent) {
1596
+ p.intro("🔍 Configuración existente detectada");
1597
+
1598
+ p.note(
1599
+ ` Agente: ${existingAgent.name}\n` +
1600
+ ` ID: ${existingAgent.id}\n` +
1601
+ ` Provider: ${existingAgent.provider_id || "no configurado"}\n` +
1602
+ ` Model: ${existingAgent.model_id || "no configurado"}\n` +
1603
+ ` Status: ${existingAgent.status}`,
1604
+ "Config actual en BD"
1605
+ );
1606
+
1607
+ const action = await p.select({
1608
+ message: "¿Qué quieres hacer?",
1609
+ options: [
1610
+ { value: "update", label: "Actualizar", hint: "Modificar configuración" },
1611
+ { value: "reset", label: "Reiniciar", hint: "Borrar BD y crear desde cero" },
1612
+ { value: "cancel", label: "Cancelar", hint: "Salir sin cambios" },
1613
+ ],
1614
+ });
1615
+
1616
+ if (p.isCancel(action) || action === "cancel") {
1617
+ p.cancel("Operación cancelada.");
1618
+ process.exit(0);
1619
+ }
1620
+
1621
+ if (action === "reset") {
1622
+ const confirm = await p.confirm({
1623
+ message: "⚠️ Esto BORRARÁ toda la base de datos y reiniciará la configuración. ¿Continuar?",
1624
+ initialValue: false,
1625
+ });
1626
+
1627
+ if (p.isCancel(confirm) || !confirm) {
1628
+ p.cancel("Operación cancelada.");
1629
+ process.exit(0);
1630
+ }
1631
+
1632
+ // Backup and delete database
1633
+ const dbPath = path.join(hiveDir, "data", "hive.db");
1634
+ if (fs.existsSync(dbPath)) {
1635
+ const backupPath = path.join(hiveDir, `hive.db.backup.${Date.now()}`);
1636
+ fs.copyFileSync(dbPath, backupPath);
1637
+ log.info(`Backup BD creado: ${backupPath}`);
1638
+
1639
+ // Delete the database file
1640
+ fs.unlinkSync(dbPath);
1641
+ log.info("Base de datos eliminada");
1642
+ }
1643
+
1644
+ // Reinitialize DB (creates fresh with seed)
1645
+ initOnboardingDb();
1646
+
1647
+ log.success("BD reiniciada con datos del sistema");
1648
+ }
1649
+
1650
+ // After reset or update, run the wizard
1651
+ await runFullWizard();
1652
+ } else {
1653
+ // No existing configuration - run full wizard
1654
+ await runFullWizard();
1655
+ }
1656
+ }