nubot-lite 0.1.1 → 0.1.2

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 (271) hide show
  1. package/README.md +6 -5
  2. package/lib/api.mjs +6 -2
  3. package/lib/commands/init.mjs +260 -258
  4. package/package.json +1 -1
  5. package/template/.claude/skills/nubot-lite/SKILL.md +7 -1
  6. package/template/panel/PANEL.md +1 -1
  7. package/template/panel/assets/AgendarDemoPage-Bn_BvTp1.js +1 -0
  8. package/template/panel/assets/{AgentDatasetsPage-D8vQhRWO.js → AgentDatasetsPage-wLtBtSB2.js} +2 -2
  9. package/template/panel/assets/AgentKnowledgeGroupsPage-BGHNFj-e.js +1 -0
  10. package/template/panel/assets/AgentListPage-F3ja9_o_.js +1 -0
  11. package/template/panel/assets/AgentQaTrainingPage-CojiU8_W.js +1 -0
  12. package/template/panel/assets/AiAgentWhatsappPage-Bn_BvTp1.js +1 -0
  13. package/template/panel/assets/BlogPage-Bn_BvTp1.js +1 -0
  14. package/template/panel/assets/BlogPostPage-Bn_BvTp1.js +1 -0
  15. package/template/panel/assets/CalculadoraTiempoRespuestaPage-Bn_BvTp1.js +1 -0
  16. package/template/panel/assets/{CampaignDetailPage-DFMctNEj.js → CampaignDetailPage-DUNnckvn.js} +1 -1
  17. package/template/panel/assets/CampaignsPage-CS60yZBd.js +1 -0
  18. package/template/panel/assets/ChannelSelector-C7R9zqrt.js +1 -0
  19. package/template/panel/assets/ChatbotParaWhatsappPage-Bn_BvTp1.js +1 -0
  20. package/template/panel/assets/ConfirmModal-B27OBQdm.js +1 -0
  21. package/template/panel/assets/ContactsPage-CvoDlFUN.js +14 -0
  22. package/template/panel/assets/CreateAgentPage-CbhikMY1.js +19 -0
  23. package/template/panel/assets/CreditLimitBanner-j72L7jgK.js +1 -0
  24. package/template/panel/assets/CrmParaWhatsappPage-Bn_BvTp1.js +1 -0
  25. package/template/panel/assets/DashboardPage-BGWkXY7W.js +1 -0
  26. package/template/panel/assets/{EmojiPicker-BToAe-MT.js → EmojiPicker-ByTMSXTj.js} +1 -1
  27. package/template/panel/assets/{EventTypesSettingsPage-DCedeQzM.js → EventTypesSettingsPage-CLpO1VBL.js} +1 -1
  28. package/template/panel/assets/GeneradorEnlacesPage-Bn_BvTp1.js +1 -0
  29. package/template/panel/assets/GeneradorQrWhatsappPage-Bn_BvTp1.js +1 -0
  30. package/template/panel/assets/{GeneralSettingsPage-B04ozcHb.js → GeneralSettingsPage-CY-5aGgL.js} +1 -1
  31. package/template/panel/assets/GoogleCalendarManagePage-oX7DuErV.js +1 -0
  32. package/template/panel/assets/{InvitationAcceptancePage-BPgtBFl5.js → InvitationAcceptancePage-BYllrAYs.js} +1 -1
  33. package/template/panel/assets/IrAlSitioPublico-Bn_BvTp1.js +1 -0
  34. package/template/panel/assets/LiteAiUsagePage-Dzm8jBL2.js +1 -0
  35. package/template/panel/assets/{LiteSetupPage-42rgAdnG.js → LiteSetupPage-CMSAur_e.js} +1 -1
  36. package/template/panel/assets/MarkdownRenderer-C3TD9KYa.js +3 -0
  37. package/template/panel/assets/MediaProcessingService-CG2DdSyN.js +1 -0
  38. package/template/panel/assets/NewCampaignPage-BfSEd075.js +1 -0
  39. package/template/panel/assets/OrdersConfigPage-BAKKaX0L.js +1 -0
  40. package/template/panel/assets/OrdersPage-CXiPOCXP.js +1 -0
  41. package/template/panel/assets/{PasswordStrengthIndicator-HZK_oU0W.js → PasswordStrengthIndicator-4h-Y0s2r.js} +1 -1
  42. package/template/panel/assets/PlantillaDetailPage-Bn_BvTp1.js +1 -0
  43. package/template/panel/assets/PlantillasIndexPage-Bn_BvTp1.js +1 -0
  44. package/template/panel/assets/PrivacyPolicy-Bn_BvTp1.js +1 -0
  45. package/template/panel/assets/{ProductManagementPage-DNM7DEgt.js → ProductManagementPage-CRkNTicv.js} +4 -4
  46. package/template/panel/assets/PublicFormPage-BxPxJ3Pq.js +1 -0
  47. package/template/panel/assets/QaForm-Px-cmakA.js +5 -0
  48. package/template/panel/assets/SelectPlanPage-Gyo5C1Hq.js +1 -0
  49. package/template/panel/assets/SignatureEditor-DIhmrXow.js +1 -0
  50. package/template/panel/assets/{TagSelector-ChoKzvjW.js → TagSelector-o6tVpRs9.js} +1 -1
  51. package/template/panel/assets/TagsSettingsPage-DT8MfW1H.js +2 -0
  52. package/template/panel/assets/TeamsPage-BuWuGxZo.js +1 -0
  53. package/template/panel/assets/WhatsappBusinessApiPage-Bn_BvTp1.js +1 -0
  54. package/template/panel/assets/WhatsappMultiagentePage-Bn_BvTp1.js +1 -0
  55. package/template/panel/assets/{account-D2ltXC_U.js → account-G3WXxisG.js} +1 -1
  56. package/template/panel/assets/{admin-lEgqZTsa.js → admin-CSMlOMR8.js} +1 -1
  57. package/template/panel/assets/{agent.service-BcAkMU7j.js → agent.service-BE2rJG_o.js} +1 -1
  58. package/template/panel/assets/{alert-dialog-D8P69s-m.js → alert-dialog-gzZZoAiu.js} +2 -2
  59. package/template/panel/assets/{alert-DI3GBLi1.js → alert-seLKo73M.js} +1 -1
  60. package/template/panel/assets/api-keys-BF33GS2N.js +1 -0
  61. package/template/panel/assets/{archive-LUe5bKi5.js → archive-CxUKRJwu.js} +1 -1
  62. package/template/panel/assets/{arrow-left-BeDdBs_Q.js → arrow-left-CI7mH7bD.js} +1 -1
  63. package/template/panel/assets/{arrow-up-BiBq8Pvx.js → arrow-up-DJzXCc8_.js} +1 -1
  64. package/template/panel/assets/{arrow-up-down-CaG2BONN.js → arrow-up-down-DkSCsCjR.js} +1 -1
  65. package/template/panel/assets/audit-slljESxh.js +1 -0
  66. package/template/panel/assets/{authService-Cbm8LdlJ.js → authService-CtvlzCDa.js} +1 -1
  67. package/template/panel/assets/{avatar-CILB1zwS.js → avatar-BXhSKQGY.js} +1 -1
  68. package/template/panel/assets/{billing-IVLNopU2.js → billing-Cy2XUBeY.js} +1 -1
  69. package/template/panel/assets/{brain-B9z7jZ7A.js → brain-BmrjUpsb.js} +1 -1
  70. package/template/panel/assets/{calendar-CIHhMjNy.js → calendar-Bu0aDtW1.js} +1 -1
  71. package/template/panel/assets/{calendar-days-BwJfOz8M.js → calendar-days-CXtOZxOF.js} +1 -1
  72. package/template/panel/assets/{card-DcKsxjXK.js → card-DdbmSh-v.js} +1 -1
  73. package/template/panel/assets/{checkbox-pcbjC0d3.js → checkbox-CuJh5AMO.js} +1 -1
  74. package/template/panel/assets/{chevrons-up-down-DC-BWTrj.js → chevrons-up-down-DNNdNhV8.js} +1 -1
  75. package/template/panel/assets/{circle-check-big-DzPo5zZX.js → circle-check-big-BOZQ_2Kv.js} +1 -1
  76. package/template/panel/assets/{circle-x-BwMKIs8b.js → circle-x-decmm7ip.js} +1 -1
  77. package/template/panel/assets/cloud-B3m0_VpL.js +1 -0
  78. package/template/panel/assets/{code-BMAyHFJe.js → code-C-UH-ZD-.js} +1 -1
  79. package/template/panel/assets/{cog-M7j64wa2.js → cog-BS-_-U4F.js} +1 -1
  80. package/template/panel/assets/{collapsible-B22t1J-V.js → collapsible-Ce_8EeFJ.js} +1 -1
  81. package/template/panel/assets/{command-COdSmSIB.js → command-aHRTWaYV.js} +1 -1
  82. package/template/panel/assets/connectionsService-BJg4S1e9.js +1 -0
  83. package/template/panel/assets/contactsApi-DI5dcMLo.js +1 -0
  84. package/template/panel/assets/creditErrors-DSbswjsG.js +1 -0
  85. package/template/panel/assets/{database-C1qvt3Py.js → database-Brb7Omak.js} +1 -1
  86. package/template/panel/assets/{datapath-C12uI_pT.js → datapath-CHeKabeh.js} +1 -1
  87. package/template/panel/assets/{datetime-BuZosCVk.js → datetime-D72Uxhjs.js} +1 -1
  88. package/template/panel/assets/{download-CiVQHPeB.js → download-CyfdxFCW.js} +1 -1
  89. package/template/panel/assets/{ellipsis-CMEJnUfK.js → ellipsis-GLu0u4ze.js} +1 -1
  90. package/template/panel/assets/{ellipsis-vertical-DTqbEYYp.js → ellipsis-vertical-CskGwbgN.js} +1 -1
  91. package/template/panel/assets/email-inbox-kyJ2qML5.js +1 -0
  92. package/template/panel/assets/es-BDgg6unH.js +1 -0
  93. package/template/panel/assets/external-link-w7K66ae2.js +1 -0
  94. package/template/panel/assets/{eye-HfQCUjxJ.js → eye-CR5C6hVv.js} +1 -1
  95. package/template/panel/assets/{eye-off-v1Z_lKS0.js → eye-off-CBiJXw4j.js} +1 -1
  96. package/template/panel/assets/facebook-D0SQMea2.js +1 -0
  97. package/template/panel/assets/{file-DMzjdlTL.js → file-CTZ8-xmL.js} +1 -1
  98. package/template/panel/assets/file-braces-54MsOnzo.js +1 -0
  99. package/template/panel/assets/file-spreadsheet-DpekIaV8.js +1 -0
  100. package/template/panel/assets/{folder-D0GKakGp.js → folder-DxTHNhQ_.js} +1 -1
  101. package/template/panel/assets/{folder-open-DQFc4qTJ.js → folder-open-BM4rA96e.js} +1 -1
  102. package/template/panel/assets/format-BG1mN6C0.js +1 -0
  103. package/template/panel/assets/{grip-vertical-FN-eTC2C.js → grip-vertical-CS-dU0tJ.js} +1 -1
  104. package/template/panel/assets/{hash-BoTE56iw.js → hash-ByrjK6gt.js} +1 -1
  105. package/template/panel/assets/{help-center-B1MAIBWA.js → help-center-fmvvoAkD.js} +1 -1
  106. package/template/panel/assets/{history-DhHayxUg.js → history-97D1GT_9.js} +1 -1
  107. package/template/panel/assets/house-CXew5LB-.js +1 -0
  108. package/template/panel/assets/{index-DsyZ_FlA.js → index-43NA2pdZ.js} +23 -23
  109. package/template/panel/assets/index-BG59-Kyv.js +6 -0
  110. package/template/panel/assets/{index-DUnioqCT.js → index-BGvtgHY3.js} +1 -1
  111. package/template/panel/assets/index-BL76lyPt.js +2 -0
  112. package/template/panel/assets/index-BUA9lP4J.js +1 -0
  113. package/template/panel/assets/index-Bfvq4Q3v.js +1 -0
  114. package/template/panel/assets/index-CK793Wzl.css +1 -0
  115. package/template/panel/assets/{index-BTxe6gLJ.js → index-ChrvveQm.js} +1 -1
  116. package/template/panel/assets/index-CoJIvJ6y.js +1 -0
  117. package/template/panel/assets/{index-CMEeV9dO.js → index-CsV1u-Y7.js} +2 -2
  118. package/template/panel/assets/{index-BiSc3WD9.js → index-DIxON2Dm.js} +1 -1
  119. package/template/panel/assets/index-DtoSJTyl.js +1 -0
  120. package/template/panel/assets/index-FBuxpbGF.js +36 -0
  121. package/template/panel/assets/index-YR5X3nd4.js +1 -0
  122. package/template/panel/assets/{instagram-CC7iYt7V.js → instagram-Bm17x-Fs.js} +1 -1
  123. package/template/panel/assets/interactiveLimits-C7Sx7s9W.js +1 -0
  124. package/template/panel/assets/landing-CEUFhLJ9.js +1 -0
  125. package/template/panel/assets/{layout-grid-Cm6hfBWZ.js → layout-grid-DN-t2Wjd.js} +1 -1
  126. package/template/panel/assets/{list-BKc6EXnV.js → list-B0uLKf1b.js} +1 -1
  127. package/template/panel/assets/{map-pin-CPmcofD3.js → map-pin-COYHwobx.js} +1 -1
  128. package/template/panel/assets/{maximize-2-WdbM7Kdx.js → maximize-2-SbmXBBYe.js} +1 -1
  129. package/template/panel/assets/mediaLimits-CAYdWqeQ.js +1 -0
  130. package/template/panel/assets/mediaValidation-Bg0xHQrv.js +1 -0
  131. package/template/panel/assets/message-circle-tNSzOeOW.js +1 -0
  132. package/template/panel/assets/{message-square-Dfn16oHc.js → message-square-B1_OirdW.js} +1 -1
  133. package/template/panel/assets/{meta-templates-C7yEjSOI.js → meta-templates-hhDdxBOR.js} +1 -1
  134. package/template/panel/assets/metadataSchemasApi-BmJko2eC.js +1 -0
  135. package/template/panel/assets/{mic-CT4vyzLZ.js → mic-ClFq9hMZ.js} +1 -1
  136. package/template/panel/assets/minus-vh4a51Mn.js +1 -0
  137. package/template/panel/assets/{music-Cu8AQMFp.js → music-CgtkroYi.js} +1 -1
  138. package/template/panel/assets/{page-components-o2TF52HV.js → page-components-DXOKtt_M.js} +1 -1
  139. package/template/panel/assets/{pages-Cj5Qd1QO.js → pages-BcYbyh_-.js} +1 -1
  140. package/template/panel/assets/{pages-B7digtXi.js → pages-BuibyrnC.js} +1 -1
  141. package/template/panel/assets/{pages-Dw7dvmaS.js → pages-CGXCBdff.js} +1 -1
  142. package/template/panel/assets/paperclip-DVRbzE7C.js +1 -0
  143. package/template/panel/assets/{pen-line-DLnhkIuw.js → pen-line-Cr0YFjAs.js} +1 -1
  144. package/template/panel/assets/{pencil-Cx8u1N8t.js → pencil-aqjc7AWb.js} +1 -1
  145. package/template/panel/assets/{popover-MyuU-yAK.js → popover-cWIhNmod.js} +1 -1
  146. package/template/panel/assets/{power-CdR2OkIX.js → power-CVFzhVJx.js} +1 -1
  147. package/template/panel/assets/{product.service-CRLZOQsr.js → product.service-FClit10f.js} +1 -1
  148. package/template/panel/assets/products.types-nZIsk2w8.js +1 -0
  149. package/template/panel/assets/proxy-CvX2KM2u.js +1 -0
  150. package/template/panel/assets/{save-Dua2yhWl.js → save-mwkHDnDL.js} +1 -1
  151. package/template/panel/assets/scroll-area-CSCo4HHB.js +1 -0
  152. package/template/panel/assets/{select-DlB_BbfM.js → select-DCY_lg9j.js} +1 -1
  153. package/template/panel/assets/{send-Dxmdupsl.js → send-DBQH77K9.js} +1 -1
  154. package/template/panel/assets/{separator-CPT3uVRM.js → separator-DznqP68l.js} +1 -1
  155. package/template/panel/assets/{settings-2-C-c8ECWW.js → settings-2-PhKPMrsj.js} +1 -1
  156. package/template/panel/assets/shield-alert-Fv3j9Wbv.js +1 -0
  157. package/template/panel/assets/{skeleton-B0zh8s_y.js → skeleton-CN6xlRXP.js} +1 -1
  158. package/template/panel/assets/{slider-CY71MXOW.js → slider-D0QB_Lme.js} +1 -1
  159. package/template/panel/assets/{square-BuGqLxJB.js → square-Dm5VC71z.js} +1 -1
  160. package/template/panel/assets/{square-pen-CJajoyrm.js → square-pen-Dbn7xmc-.js} +1 -1
  161. package/template/panel/assets/{star-Cuegk1GF.js → star-BMD3HmS6.js} +1 -1
  162. package/template/panel/assets/stat-card-W7s_-fHW.js +1 -0
  163. package/template/panel/assets/storage.api-C0ZJBoSz.js +20 -0
  164. package/template/panel/assets/{switch-ODBa0fCT.js → switch-C7XmAU1c.js} +1 -1
  165. package/template/panel/assets/{table-CvSELOPi.js → table-7NGq0kRw.js} +1 -1
  166. package/template/panel/assets/{tabs-DqXhuAr6.js → tabs-BGCKrmVw.js} +1 -1
  167. package/template/panel/assets/{tags-By_tlh_r.js → tags-HU2fuPFH.js} +1 -1
  168. package/template/panel/assets/{target-DEuCdeUI.js → target-CG_g4_Fj.js} +1 -1
  169. package/template/panel/assets/{textarea-BYtcLsMD.js → textarea-B1anutjq.js} +1 -1
  170. package/template/panel/assets/{ticket-CWba7G1i.js → ticket-CHs1Xojr.js} +1 -1
  171. package/template/panel/assets/{trash-2-Du2vmQCH.js → trash-2-D_kmHMwn.js} +1 -1
  172. package/template/panel/assets/{trending-up-Ba4HSvuW.js → trending-up-BnHeLWFs.js} +1 -1
  173. package/template/panel/assets/{upload-C7TwpdyA.js → upload-CQg4_W5r.js} +1 -1
  174. package/template/panel/assets/{useAiAgentsQuery-Bm6aRHKj.js → useAiAgentsQuery-CXo6TmU6.js} +1 -1
  175. package/template/panel/assets/useFunnelStats-Bk8wTET5.js +1 -0
  176. package/template/panel/assets/useFunnelsQuery-vDrahaDr.js +1 -0
  177. package/template/panel/assets/{useInfiniteQuery-BXUM1Z9V.js → useInfiniteQuery-D0sFT2K2.js} +1 -1
  178. package/template/panel/assets/{useMutation-a5tIBvQY.js → useMutation-0ypmz8SO.js} +1 -1
  179. package/template/panel/assets/useOrgSyncedFetch-FSgwRhpR.js +1 -0
  180. package/template/panel/assets/usePipelineBoardQuery-BysscZXI.js +9 -0
  181. package/template/panel/assets/{useUploadConfig-BuiF0X-3.js → useUploadConfig-CvVmSc6S.js} +1 -1
  182. package/template/panel/assets/{user-DwCALzIG.js → user-l48WDfDH.js} +1 -1
  183. package/template/panel/assets/{user-plus-BiUjcHil.js → user-plus-CWaaUP-O.js} +1 -1
  184. package/template/panel/assets/{video-VfwKgB6z.js → video-CILE66CA.js} +1 -1
  185. package/template/panel/assets/{volume-2-C5DMdOVq.js → volume-2-DfFqN0dw.js} +1 -1
  186. package/template/panel/assets/wrench-Dcbk4scY.js +1 -0
  187. package/template/panel/index.html +2 -2
  188. package/template/panel/version.json +2 -2
  189. package/template/worker/migrations/0006_ai_usage.sql +33 -0
  190. package/template/worker/migrations/0007_channels.sql +18 -0
  191. package/template/worker/src/agent/pricing.ts +37 -0
  192. package/template/worker/src/agent/prompt.ts +137 -134
  193. package/template/worker/src/agent/run.ts +350 -243
  194. package/template/worker/src/agent/sandbox.ts +2 -2
  195. package/template/worker/src/agent/tools.ts +463 -463
  196. package/template/worker/src/channels/accounts.ts +177 -0
  197. package/template/worker/src/channels/zernio.ts +161 -148
  198. package/template/worker/src/inbox/contacts.ts +2 -0
  199. package/template/worker/src/inbox/store.ts +199 -187
  200. package/template/worker/src/index.ts +2 -0
  201. package/template/worker/src/nubot/account.ts +163 -178
  202. package/template/worker/src/nubot/ai-usage.ts +197 -0
  203. package/template/worker/src/nubot/channels.ts +159 -0
  204. package/template/worker/src/nubot/contacts.ts +387 -386
  205. package/template/worker/src/nubot/funnels.ts +672 -670
  206. package/template/worker/src/nubot/inbox.ts +623 -616
  207. package/template/worker/src/nubot/index.ts +127 -122
  208. package/template/worker/src/nubot/mappers.ts +12 -5
  209. package/template/worker/src/routes/channels.ts +13 -0
  210. package/template/worker/src/routes/system.ts +110 -100
  211. package/template/worker/src/webhooks/zernio.ts +154 -125
  212. package/template/panel/assets/AgendarDemoPage-DOiS40FL.js +0 -1
  213. package/template/panel/assets/AgentKnowledgeGroupsPage-DEPS1lsk.js +0 -1
  214. package/template/panel/assets/AgentListPage-DqY9rze5.js +0 -1
  215. package/template/panel/assets/AgentQaTrainingPage-Ecis7xoc.js +0 -1
  216. package/template/panel/assets/AiAgentWhatsappPage-DOiS40FL.js +0 -1
  217. package/template/panel/assets/AiMetricsPage-DEPS1lsk.js +0 -1
  218. package/template/panel/assets/BlogPage-DOiS40FL.js +0 -1
  219. package/template/panel/assets/BlogPostPage-DOiS40FL.js +0 -1
  220. package/template/panel/assets/CalculadoraTiempoRespuestaPage-DOiS40FL.js +0 -1
  221. package/template/panel/assets/CampaignsPage-CLRaXGlj.js +0 -1
  222. package/template/panel/assets/ChannelSelector-q9tTirNo.js +0 -1
  223. package/template/panel/assets/ChatbotParaWhatsappPage-DOiS40FL.js +0 -1
  224. package/template/panel/assets/ConfirmModal-W7VUI3Z_.js +0 -1
  225. package/template/panel/assets/ContactsPage-CsOFsOPw.js +0 -14
  226. package/template/panel/assets/CreateAgentPage-Ifxx6_UX.js +0 -19
  227. package/template/panel/assets/CreditLimitBanner-J49rSVEn.js +0 -1
  228. package/template/panel/assets/CrmParaWhatsappPage-DOiS40FL.js +0 -1
  229. package/template/panel/assets/DashboardPage-3Viy83dd.js +0 -1
  230. package/template/panel/assets/GeneradorEnlacesPage-DOiS40FL.js +0 -1
  231. package/template/panel/assets/GeneradorQrWhatsappPage-DOiS40FL.js +0 -1
  232. package/template/panel/assets/IrAlSitioPublico-DOiS40FL.js +0 -1
  233. package/template/panel/assets/MediaProcessingService-glHGiE_o.js +0 -1
  234. package/template/panel/assets/NewCampaignPage-Cw99m3W8.js +0 -1
  235. package/template/panel/assets/OrdersConfigPage-B6BfM3lf.js +0 -1
  236. package/template/panel/assets/OrdersPage-Du7CJjty.js +0 -1
  237. package/template/panel/assets/PlantillaDetailPage-DOiS40FL.js +0 -1
  238. package/template/panel/assets/PlantillasIndexPage-DOiS40FL.js +0 -1
  239. package/template/panel/assets/PrivacyPolicy-DOiS40FL.js +0 -1
  240. package/template/panel/assets/PublicFormPage-DDb4OXEM.js +0 -1
  241. package/template/panel/assets/QaForm-BDboHNKo.js +0 -5
  242. package/template/panel/assets/SelectPlanPage-DALaf8E5.js +0 -1
  243. package/template/panel/assets/TagsSettingsPage-DYCKveQq.js +0 -2
  244. package/template/panel/assets/TeamsPage-DPUcRdlh.js +0 -1
  245. package/template/panel/assets/WhatsappBusinessApiPage-DOiS40FL.js +0 -1
  246. package/template/panel/assets/WhatsappMultiagentePage-DOiS40FL.js +0 -1
  247. package/template/panel/assets/api-keys-B0UQNR5E.js +0 -1
  248. package/template/panel/assets/audit-BrdHcTNl.js +0 -1
  249. package/template/panel/assets/connections-Dqwd01UI.js +0 -1
  250. package/template/panel/assets/connectionsService-BsJf8pFz.js +0 -3
  251. package/template/panel/assets/contactsApi-B851NME_.js +0 -1
  252. package/template/panel/assets/creditErrors-CrpkwwUI.js +0 -1
  253. package/template/panel/assets/email-inbox-LbGeQCty.js +0 -1
  254. package/template/panel/assets/es-BG480iA5.js +0 -1
  255. package/template/panel/assets/facebook-RxMBweMB.js +0 -1
  256. package/template/panel/assets/index-B183O0S5.js +0 -1
  257. package/template/panel/assets/index-BJCPaymI.css +0 -1
  258. package/template/panel/assets/index-BtxJGfLL.js +0 -6
  259. package/template/panel/assets/index-Bz-ixwph.js +0 -1
  260. package/template/panel/assets/index-CjQSv8hF.js +0 -2
  261. package/template/panel/assets/index-D3vkHHjD.js +0 -9
  262. package/template/panel/assets/index-D4WhIkMN.js +0 -1
  263. package/template/panel/assets/interactiveLimits-CfLJ1O4_.js +0 -1
  264. package/template/panel/assets/landing-If3oVsS0.js +0 -1
  265. package/template/panel/assets/mediaValidation-BR-ZOj5w.js +0 -1
  266. package/template/panel/assets/minus-CyVEzdqL.js +0 -1
  267. package/template/panel/assets/products.types-46xSxBS8.js +0 -1
  268. package/template/panel/assets/scroll-area-CavEOIyz.js +0 -1
  269. package/template/panel/assets/shield-alert-04pSYngI.js +0 -1
  270. package/template/panel/assets/storage.api-CFU3KJNp.js +0 -20
  271. package/template/panel/assets/useFunnelStats-tvjpmWIC.js +0 -1
package/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # nubot-lite
2
2
 
3
- Instala **Nubot Lite** en tu cuenta de Cloudflare: una bandeja de WhatsApp con agente de IA (Claude) y embudo
4
- de ventas, para una sola empresa. Corre en la capa gratuita de Cloudflare, sin tarjeta. WhatsApp entra y sale
5
- por [Zernio](https://zernio.com).
3
+ Instala **Nubot Lite** en tu cuenta de Cloudflare: una bandeja de WhatsApp, Instagram y Facebook con agente de IA
4
+ (Claude) y embudo de ventas, para una sola empresa. Corre en la capa gratuita de Cloudflare, sin tarjeta. Los mensajes
5
+ entran y salen por [Zernio](https://zernio.com).
6
6
 
7
7
  ```
8
8
  npx nubot-lite init
@@ -21,7 +21,8 @@ hacen falta.
21
21
  | 4. Enseñarle el negocio al agente | `npx nubot-lite apply <carpeta>` |
22
22
 
23
23
  Los pasos 2 a 4 se corren dentro de la carpeta que crea `init`. Con Claude Code puedes pedirlo con tus palabras:
24
- «Instala Nubot Lite para mi empresa», «Conecta mi agente», «Conecta mi WhatsApp», «Arma el agente de mi empresa».
24
+ «Instala Nubot Lite para mi empresa con `npx nubot-lite init`», «Conecta mi agente», «Conecta mi WhatsApp»,
25
+ «Arma el agente de mi empresa». Instagram y Facebook se conectan después desde el panel, en Conexiones.
25
26
 
26
27
  | Comando | Qué hace |
27
28
  |---|---|
@@ -38,4 +39,4 @@ compilado que se instala con ellos tiene su propia licencia
38
39
  ([LICENSE-PANEL.md](https://github.com/aichain-research/nubot-lite/blob/main/LICENSE-PANEL.md)): se usa sin costo
39
40
  con Nubot Lite, pero no es código abierto.
40
41
 
41
- ¿Necesitas varios agentes, Instagram, Messenger, campañas o workflows? Eso es [Nubot](https://nubot.io).
42
+ ¿Necesitas varios agentes, campañas o workflows? Eso es [Nubot](https://nubot.io).
package/lib/api.mjs CHANGED
@@ -54,12 +54,16 @@ export async function whatsappAccounts(key) {
54
54
  return accounts;
55
55
  }
56
56
 
57
- /** Registra (o actualiza) el webhook que lleva los mensajes de WhatsApp al Worker. */
57
+ /**
58
+ * Registra (o actualiza) el webhook que lleva los mensajes al Worker. Suma la cuenta a las que ya escuchaba:
59
+ * el Instagram o el Facebook conectados desde el panel siguen llegando.
60
+ */
58
61
  export async function ensureWebhook(key, { url, secret, accountId }) {
59
62
  const target = `${url}/webhooks/zernio`;
60
63
  const { webhooks = [] } = await zernio(key, "GET", "/v1/webhooks/settings");
61
- const body = { name: "Nubot Lite", url: target, secret, events: WEBHOOK_EVENTS, isActive: true, accountIds: accountId ? [accountId] : [] };
62
64
  const existing = webhooks.find((w) => w.url === target);
65
+ const accountIds = [...new Set([...(existing?.accountIds ?? []), ...(accountId ? [accountId] : [])])];
66
+ const body = { name: "Nubot Lite", url: target, secret, events: WEBHOOK_EVENTS, isActive: true, accountIds };
63
67
  if (existing) {
64
68
  await zernio(key, "PUT", "/v1/webhooks/settings", { _id: existing._id, ...body });
65
69
  return "updated";
@@ -1,258 +1,260 @@
1
- import { existsSync, readdirSync, rmSync } from "node:fs";
2
- import { join, resolve } from "node:path";
3
- import { apiClient, ensureWebhook, whatsappAccounts } from "../api.mjs";
4
- import { slugify } from "../args.mjs";
5
- import { createD1, createKv, deploy, ensureLogin, migrate, NameTakenError, putSecrets } from "../cloudflare.mjs";
6
- import { copyTemplate, packageVersion, readState, secret, writeState, writeWranglerConfig } from "../project.mjs";
7
- import { npmInstall } from "../sh.mjs";
8
- import { ask, bold, choose, CliError, closePrompts, dim, info, ok, part, pause, warn } from "../ui.mjs";
9
-
10
- const PARTS = 2;
11
- const MODELS = [
12
- { value: "claude-sonnet-5-5", label: "Claude Sonnet 5.5 · recomendado: buen equilibrio entre calidad y costo" },
13
- { value: "claude-opus-5-5", label: "Claude Opus 5.5 · el más capaz, el más caro" },
14
- { value: "claude-haiku-5-5", label: "Claude Haiku 5.5 · el más económico" },
15
- ];
16
- const MAX_TRIES = 3;
17
-
18
- const given = (flags, key, env) => {
19
- const v = flags[key] ?? (env ? process.env[env] : undefined);
20
- return v && v !== true ? String(v) : undefined;
21
- };
22
-
23
- const wait = (ms) => new Promise((r) => setTimeout(r, ms));
24
-
25
- export function validTimezone(tz) {
26
- try {
27
- new Intl.DateTimeFormat("es", { timeZone: tz });
28
- return tz.includes("/") || tz === "UTC";
29
- } catch {
30
- return false;
31
- }
32
- }
33
-
34
- async function askTimezone(flags) {
35
- const flag = given(flags, "timezone");
36
- if (flag) {
37
- if (!validTimezone(flag)) throw new CliError(`La zona horaria ${flag} no existe. Ejemplos: America/Lima, America/Bogota, America/Mexico_City.`);
38
- return flag;
39
- }
40
- if (flags.yes) return "America/Lima";
41
- for (;;) {
42
- const tz = await ask("Zona horaria de tu negocio (Enter para America/Lima)");
43
- if (!tz) return "America/Lima";
44
- if (validTimezone(tz)) return tz;
45
- warn("Esa zona no existe. Escribe una como America/Bogota o America/Mexico_City, o presiona Enter para America/Lima.");
46
- }
47
- }
48
-
49
- /**
50
- * Instala por partes: cada dato se pide justo antes de usarlo y se comprueba en el momento.
51
- * Lo que ya quedó hecho se guarda en .nubot-lite/config.json; si se corta, se vuelve a correr y sigue.
52
- */
53
- export async function init(flags) {
54
- try {
55
- await install(flags);
56
- } finally {
57
- closePrompts();
58
- }
59
- }
60
-
61
- async function install(flags) {
62
- console.log(`
63
- ${bold("Nubot Lite")} · instalación en tu cuenta de Cloudflare`);
64
- const [major] = process.versions.node.split(".").map(Number);
65
- if (major < 20) throw new CliError(`Necesitas Node.js 20 o más nuevo; tienes ${process.versions.node}.`);
66
- console.log(`
67
- Este instalador levanta tu CRM en tu cuenta de Cloudflare. Tú solo:
68
- · escribes el nombre de tu empresa,
69
- · entras a tu cuenta de Cloudflare cuando se abra el navegador (si no tienes, la creas ahí; es gratis y no pide tarjeta).
70
-
71
- En tu cuenta voy a crear, en la capa gratuita: una base de datos (D1), un espacio para fotos y audios (KV)
72
- y tu CRM publicado en una dirección *.workers.dev.
73
-
74
- Conectar el agente de IA y WhatsApp va después, cuando tú quieras.`);
75
- if (!flags.yes && process.stdin.isTTY) {
76
- const answer = await pause("Presiona Enter para empezar, o escribe «no» para salir");
77
- if (/^n/i.test(answer)) throw new CliError("Instalación cancelada. No se creó nada.");
78
- }
79
-
80
- // Parte 1: la empresa y la carpeta
81
- part(1, PARTS, "Tu empresa", [dim("Con el nombre preparo una carpeta en tu computadora con todo lo necesario.")]);
82
- let dir = flags.dir && flags.dir !== true ? resolve(String(flags.dir)) : null;
83
- let state = dir ? readState(dir, "config.json") : null;
84
- const created = !state;
85
- if (state) {
86
- ok(`Retomo la instalación de ${state.business}`);
87
- } else {
88
- const business = given(flags, "business") ?? (await ask("Nombre de tu empresa"));
89
- if (!business) throw new CliError("Falta el nombre de la empresa.");
90
- const timezone = await askTimezone(flags);
91
- dir ??= resolve(`nubot-${slugify(business)}`);
92
- if (existsSync(dir) && readdirSync(dir).length > 0) throw new CliError(`La carpeta ${dir} ya existe y no está vacía. Elige otra con --dir.`);
93
- state = {
94
- business,
95
- timezone,
96
- name: given(flags, "name") ?? `nubot-${slugify(business)}`,
97
- adminToken: secret(24),
98
- sessionSecret: secret(),
99
- webhookSecret: secret(),
100
- version: packageVersion(),
101
- };
102
- const manifest = copyTemplate(dir);
103
- writeState(dir, "manifest.json", { version: packageVersion(), files: manifest });
104
- writeState(dir, "config.json", state);
105
- ok(`Carpeta lista: ${dir}`);
106
- }
107
- const save = () => writeState(dir, "config.json", { ...state, version: packageVersion() });
108
- const workerDir = join(dir, "worker");
109
- info("Instalando las herramientas (tarda uno o dos minutos)…");
110
- await npmInstall(workerDir);
111
- ok("Herramientas instaladas");
112
-
113
- // Parte 2: Cloudflare
114
- part(2, PARTS, "Cloudflare", [
115
- dim("Aquí vive tu CRM: la base de datos, los archivos y el panel. Es gratis y no pide tarjeta."),
116
- dim("Si no estás conectado, se abre el navegador para entrar o crear tu cuenta."),
117
- ]);
118
- await ensureLogin(workerDir);
119
- ok("Conectado a tu cuenta de Cloudflare");
120
- writeWranglerConfig(dir, { name: state.name, d1Id: state.d1Id ?? "00000000-0000-0000-0000-000000000000", kvId: state.kvId ?? "0".repeat(32) });
121
- try {
122
- if (!state.d1Id) {
123
- state.d1Id = await createD1(workerDir, state.name);
124
- save();
125
- }
126
- if (!state.kvId) {
127
- state.kvId = await createKv(workerDir, state.name);
128
- save();
129
- }
130
- } catch (err) {
131
- // Si el nombre ya estaba tomado y no se creó nada, la carpeta recién armada sobra: así se puede reintentar.
132
- if (err instanceof NameTakenError && created && !state.d1Id) rmSync(dir, { recursive: true, force: true });
133
- throw err;
134
- }
135
- writeWranglerConfig(dir, { name: state.name, d1Id: state.d1Id, kvId: state.kvId });
136
- ok("Base de datos y espacio para archivos creados");
137
- await migrate(workerDir);
138
- ok("Tablas listas");
139
- info("Publicando tu CRM en internet…");
140
- state.url = await deploy(workerDir);
141
- await putSecrets(workerDir, { ADMIN_TOKEN: state.adminToken, SESSION_SECRET: state.sessionSecret, ZERNIO_WEBHOOK_SECRET: state.webhookSecret });
142
- save();
143
- await apiClient(state).put("/api/settings/agent", { businessName: state.business, timezone: state.timezone });
144
-
145
- console.log(`
146
- ${bold("Tu CRM está listo.")}
147
- `);
148
- console.log(` Ábrelo aquí: ${bold(state.url)}`);
149
- console.log(` Llave de admin: ${state.adminToken}`);
150
- console.log(` Carpeta: ${dir}
151
- `);
152
- info("Abre el link y crea tu usuario con la llave de admin (también quedó guardada en la carpeta). Recórrelo con calma.");
153
- info("Cuando quieras seguir, en Claude Code dile: «conecta mi agente» y después «conecta mi WhatsApp».");
154
- info("Sin Claude Code: npx nubot-lite claude y npx nubot-lite whatsapp dentro de la carpeta.");
155
- }
156
-
157
- /** Conecta el agente: elige el modelo, pide la llave de Anthropic y la prueba en el momento. */
158
- export async function setupClaude(dir, state, workerDir, flags = {}) {
159
- const api = apiClient(state);
160
- console.log(`
161
- ${bold("Conectar el agente de IA (Claude)")}`);
162
- info("El agente que responde tus WhatsApp usa Claude, de Anthropic. Necesito una llave de su API.");
163
- info("Se consigue en console.anthropic.com → API Keys. Se paga por uso, con saldo prepagado.");
164
- const model =
165
- given(flags, "model") ?? (flags.yes ? MODELS[0].value : await choose("¿Qué modelo usa el agente? Puedes cambiarlo después en el panel", MODELS));
166
- if (!MODELS.some((m) => m.value === model)) throw new CliError(`Ese modelo no está disponible. Opciones: ${MODELS.map((m) => m.value).join(", ")}`);
167
- await api.put("/api/settings/agent", { model });
168
- info("Pega la llave. No se va a ver lo que pegues; es normal.");
169
- for (let attempt = 1; ; attempt++) {
170
- const key = attempt === 1 ? (given(flags, "anthropicKey", "ANTHROPIC_API_KEY") ?? (await ask("Llave de Anthropic", { secret: true }))) : await ask("Llave de Anthropic", { secret: true });
171
- if (!key) throw new CliError("Falta la llave de Anthropic.");
172
- await putSecrets(workerDir, { ANTHROPIC_API_KEY: key });
173
- info("Probando la llave…");
174
- const check = await claudeCheck(api);
175
- if (check.ok) {
176
- ok(check.detail);
177
- break;
178
- }
179
- warn(check.detail);
180
- if (attempt >= MAX_TRIES || !process.stdin.isTTY || flags.yes) throw new CliError("No pude validar la llave de Anthropic. Revísala y vuelve a correr: npx nubot-lite claude");
181
- info("Pega la llave otra vez (revisa que esté completa y que tu cuenta tenga saldo).");
182
- }
183
- state.claudeReady = true;
184
- state.model = model;
185
- save(dir, state);
186
- ok("Tu agente ya puede responder. Para probarlo sin WhatsApp: npx nubot-lite test \"Hola\"");
187
- }
188
-
189
- /** Pregunta al Worker si la llave de Claude funciona. Un secreto nuevo tarda unos segundos en aplicarse. */
190
- async function claudeCheck(api) {
191
- let last = { ok: false, detail: "No pude probar la llave." };
192
- for (let i = 0; i < 4; i++) {
193
- if (i > 0) await wait(3000);
194
- const report = await api.get("/api/system/health").catch(() => null);
195
- const check = report?.checks.find((c) => c.name === "Claude");
196
- if (check?.ok) return check;
197
- if (check) last = check;
198
- }
199
- return last;
200
- }
201
-
202
- /** Pide la API key de Zernio, la comprueba, espera a que haya un número conectado y registra el webhook. */
203
- export async function setupWhatsapp(dir, state, workerDir, flags = {}) {
204
- console.log(`
205
- ${bold("Conectar WhatsApp (Zernio)")}`);
206
- info("Zernio conecta tu número de WhatsApp con tu CRM. Necesito su API key.");
207
- info("Se consigue en zernio.com → API Keys. Las 2 primeras cuentas conectadas son gratis. No se va a ver lo que pegues.");
208
- let key = given(flags, "zernioKey", "ZERNIO_API_KEY");
209
- let accounts;
210
- for (let attempt = 1; ; attempt++) {
211
- key ??= await ask("API key de Zernio", { secret: true });
212
- if (!key) throw new CliError("Falta la API key de Zernio.");
213
- try {
214
- accounts = (await whatsappAccounts(key)).filter((a) => a.active);
215
- ok("La API key de Zernio funciona");
216
- break;
217
- } catch (err) {
218
- warn(err.message);
219
- if (attempt >= MAX_TRIES || !process.stdin.isTTY || flags.yes) throw err;
220
- key = undefined;
221
- info("Pégala otra vez.");
222
- }
223
- }
224
- await putSecrets(workerDir, { ZERNIO_API_KEY: key });
225
-
226
- while (!accounts.length) {
227
- warn("Tu cuenta de Zernio todavía no tiene un número de WhatsApp conectado.");
228
- if (!process.stdin.isTTY || flags.yes) {
229
- save(dir, state);
230
- return false;
231
- }
232
- info("Tienes dos caminos, los dos en zernio.com:");
233
- info(" · Usar un número que ya tienes: Accounts → WhatsApp, y sigue los pasos de Meta con ese número.");
234
- info(" · Comprar un número nuevo: Phone numbers → busca por país, cómpralo (desde unos US$ 3 al mes) y conéctalo a WhatsApp.");
235
- const answer = await pause("Presiona Enter cuando esté conectado, o escribe «después» para dejarlo para otro momento");
236
- if (/^desp/i.test(answer)) {
237
- save(dir, state);
238
- return false;
239
- }
240
- accounts = (await whatsappAccounts(key)).filter((a) => a.active);
241
- }
242
-
243
- const accountId =
244
- given(flags, "account") ??
245
- (accounts.length === 1 || flags.yes
246
- ? accounts[0].id
247
- : await choose("¿Qué número atiende tu CRM?", accounts.map((a) => ({ value: a.id, label: a.name }))));
248
- const result = await ensureWebhook(key, { url: state.url, secret: state.webhookSecret, accountId });
249
- state.zernioAccountId = accountId;
250
- save(dir, state);
251
- ok(`${result === "created" ? "Webhook registrado" : "Webhook actualizado"}: los mensajes de ${accounts.find((a) => a.id === accountId)?.name ?? "tu número"} llegan a tu CRM`);
252
- info("Pruébalo: escríbele a ese número desde otro celular y mira la conversación en el panel.");
253
- return true;
254
- }
255
-
256
- function save(dir, state) {
257
- writeState(dir, "config.json", { ...state, version: packageVersion() });
258
- }
1
+ import { existsSync, readdirSync, rmSync } from "node:fs";
2
+ import { join, resolve } from "node:path";
3
+ import { apiClient, ensureWebhook, whatsappAccounts } from "../api.mjs";
4
+ import { slugify } from "../args.mjs";
5
+ import { createD1, createKv, deploy, ensureLogin, migrate, NameTakenError, putSecrets } from "../cloudflare.mjs";
6
+ import { copyTemplate, packageVersion, readState, secret, writeState, writeWranglerConfig } from "../project.mjs";
7
+ import { npmInstall } from "../sh.mjs";
8
+ import { ask, bold, choose, CliError, closePrompts, dim, info, ok, part, pause, warn } from "../ui.mjs";
9
+
10
+ const PARTS = 2;
11
+ const MODELS = [
12
+ { value: "claude-sonnet-5-5", label: "Claude Sonnet 5.5 · recomendado: buen equilibrio entre calidad y costo" },
13
+ { value: "claude-opus-5-5", label: "Claude Opus 5.5 · el más capaz, el más caro" },
14
+ { value: "claude-haiku-5-5", label: "Claude Haiku 5.5 · el más económico" },
15
+ ];
16
+ const MAX_TRIES = 3;
17
+
18
+ const given = (flags, key, env) => {
19
+ const v = flags[key] ?? (env ? process.env[env] : undefined);
20
+ return v && v !== true ? String(v) : undefined;
21
+ };
22
+
23
+ const wait = (ms) => new Promise((r) => setTimeout(r, ms));
24
+
25
+ export function validTimezone(tz) {
26
+ try {
27
+ new Intl.DateTimeFormat("es", { timeZone: tz });
28
+ return tz.includes("/") || tz === "UTC";
29
+ } catch {
30
+ return false;
31
+ }
32
+ }
33
+
34
+ async function askTimezone(flags) {
35
+ const flag = given(flags, "timezone");
36
+ if (flag) {
37
+ if (!validTimezone(flag)) throw new CliError(`La zona horaria ${flag} no existe. Ejemplos: America/Lima, America/Bogota, America/Mexico_City.`);
38
+ return flag;
39
+ }
40
+ if (flags.yes) return "America/Lima";
41
+ for (;;) {
42
+ const tz = await ask("Zona horaria de tu negocio (Enter para America/Lima)");
43
+ if (!tz) return "America/Lima";
44
+ if (validTimezone(tz)) return tz;
45
+ warn("Esa zona no existe. Escribe una como America/Bogota o America/Mexico_City, o presiona Enter para America/Lima.");
46
+ }
47
+ }
48
+
49
+ /**
50
+ * Instala por partes: cada dato se pide justo antes de usarlo y se comprueba en el momento.
51
+ * Lo que ya quedó hecho se guarda en .nubot-lite/config.json; si se corta, se vuelve a correr y sigue.
52
+ */
53
+ export async function init(flags) {
54
+ try {
55
+ await install(flags);
56
+ } finally {
57
+ closePrompts();
58
+ }
59
+ }
60
+
61
+ async function install(flags) {
62
+ console.log(`
63
+ ${bold("Nubot Lite")} · instalación en tu cuenta de Cloudflare`);
64
+ const [major] = process.versions.node.split(".").map(Number);
65
+ if (major < 20) throw new CliError(`Necesitas Node.js 20 o más nuevo; tienes ${process.versions.node}.`);
66
+ console.log(`
67
+ Este instalador levanta tu CRM en tu cuenta de Cloudflare. Tú solo:
68
+ · escribes el nombre de tu empresa,
69
+ · entras a tu cuenta de Cloudflare cuando se abra el navegador (si no tienes, la creas ahí; es gratis y no pide tarjeta).
70
+
71
+ En tu cuenta voy a crear, en la capa gratuita: una base de datos (D1), un espacio para fotos y audios (KV)
72
+ y tu CRM publicado en una dirección *.workers.dev.
73
+
74
+ Conectar el agente de IA y WhatsApp va después, cuando tú quieras.`);
75
+ if (!flags.yes && process.stdin.isTTY) {
76
+ const answer = await pause("Presiona Enter para empezar, o escribe «no» para salir");
77
+ if (/^n/i.test(answer)) throw new CliError("Instalación cancelada. No se creó nada.");
78
+ }
79
+
80
+ // Parte 1: la empresa y la carpeta
81
+ part(1, PARTS, "Tu empresa", [dim("Con el nombre preparo una carpeta en tu computadora con todo lo necesario.")]);
82
+ let dir = flags.dir && flags.dir !== true ? resolve(String(flags.dir)) : null;
83
+ let state = dir ? readState(dir, "config.json") : null;
84
+ const created = !state;
85
+ if (state) {
86
+ ok(`Retomo la instalación de ${state.business}`);
87
+ } else {
88
+ const business = given(flags, "business") ?? (await ask("Nombre de tu empresa"));
89
+ if (!business) throw new CliError("Falta el nombre de la empresa.");
90
+ const timezone = await askTimezone(flags);
91
+ dir ??= resolve(`nubot-${slugify(business)}`);
92
+ if (existsSync(dir) && readdirSync(dir).length > 0) throw new CliError(`La carpeta ${dir} ya existe y no está vacía. Elige otra con --dir.`);
93
+ state = {
94
+ business,
95
+ timezone,
96
+ name: given(flags, "name") ?? `nubot-${slugify(business)}`,
97
+ adminToken: secret(24),
98
+ sessionSecret: secret(),
99
+ webhookSecret: secret(),
100
+ version: packageVersion(),
101
+ };
102
+ const manifest = copyTemplate(dir);
103
+ writeState(dir, "manifest.json", { version: packageVersion(), files: manifest });
104
+ writeState(dir, "config.json", state);
105
+ ok(`Carpeta lista: ${dir}`);
106
+ }
107
+ const save = () => writeState(dir, "config.json", { ...state, version: packageVersion() });
108
+ const workerDir = join(dir, "worker");
109
+ info("Instalando las herramientas (tarda uno o dos minutos)…");
110
+ await npmInstall(workerDir);
111
+ ok("Herramientas instaladas");
112
+
113
+ // Parte 2: Cloudflare
114
+ part(2, PARTS, "Cloudflare", [
115
+ dim("Aquí vive tu CRM: la base de datos, los archivos y el panel. Es gratis y no pide tarjeta."),
116
+ dim("Si no estás conectado, se abre el navegador para entrar o crear tu cuenta."),
117
+ ]);
118
+ await ensureLogin(workerDir);
119
+ ok("Conectado a tu cuenta de Cloudflare");
120
+ writeWranglerConfig(dir, { name: state.name, d1Id: state.d1Id ?? "00000000-0000-0000-0000-000000000000", kvId: state.kvId ?? "0".repeat(32) });
121
+ try {
122
+ if (!state.d1Id) {
123
+ state.d1Id = await createD1(workerDir, state.name);
124
+ save();
125
+ }
126
+ if (!state.kvId) {
127
+ state.kvId = await createKv(workerDir, state.name);
128
+ save();
129
+ }
130
+ } catch (err) {
131
+ // Si el nombre ya estaba tomado y no se creó nada, la carpeta recién armada sobra: así se puede reintentar.
132
+ if (err instanceof NameTakenError && created && !state.d1Id) rmSync(dir, { recursive: true, force: true });
133
+ throw err;
134
+ }
135
+ writeWranglerConfig(dir, { name: state.name, d1Id: state.d1Id, kvId: state.kvId });
136
+ ok("Base de datos y espacio para archivos creados");
137
+ await migrate(workerDir);
138
+ ok("Tablas listas");
139
+ info("Publicando tu CRM en internet…");
140
+ state.url = await deploy(workerDir);
141
+ await putSecrets(workerDir, { ADMIN_TOKEN: state.adminToken, SESSION_SECRET: state.sessionSecret, ZERNIO_WEBHOOK_SECRET: state.webhookSecret });
142
+ save();
143
+ await apiClient(state).put("/api/settings/agent", { businessName: state.business, timezone: state.timezone });
144
+
145
+ console.log(`
146
+ ${bold("Tu CRM está listo.")}
147
+ `);
148
+ console.log(` Ábrelo aquí: ${bold(state.url)}`);
149
+ console.log(` Llave de admin: ${state.adminToken}`);
150
+ console.log(` Carpeta: ${dir}
151
+ `);
152
+ info("Abre el link y crea tu usuario con la llave de admin (también quedó guardada en la carpeta). Recórrelo con calma.");
153
+ info("Cuando quieras seguir, en Claude Code dile: «conecta mi agente» y después «conecta mi WhatsApp».");
154
+ info("Sin Claude Code: npx nubot-lite claude y npx nubot-lite whatsapp dentro de la carpeta.");
155
+ }
156
+
157
+ /** Conecta el agente: elige el modelo, pide la llave de Anthropic y la prueba en el momento. */
158
+ export async function setupClaude(dir, state, workerDir, flags = {}) {
159
+ const api = apiClient(state);
160
+ console.log(`
161
+ ${bold("Conectar el agente de IA (Claude)")}`);
162
+ info("El agente que responde tus WhatsApp usa Claude, de Anthropic. Necesito una llave de su API.");
163
+ info("Se consigue en console.anthropic.com → API Keys. Se paga por uso, con saldo prepagado.");
164
+ const model =
165
+ given(flags, "model") ?? (flags.yes ? MODELS[0].value : await choose("¿Qué modelo usa el agente? Puedes cambiarlo después en el panel", MODELS));
166
+ if (!MODELS.some((m) => m.value === model)) throw new CliError(`Ese modelo no está disponible. Opciones: ${MODELS.map((m) => m.value).join(", ")}`);
167
+ await api.put("/api/settings/agent", { model });
168
+ info("Pega la llave. No se va a ver lo que pegues; es normal.");
169
+ for (let attempt = 1; ; attempt++) {
170
+ const key = attempt === 1 ? (given(flags, "anthropicKey", "ANTHROPIC_API_KEY") ?? (await ask("Llave de Anthropic", { secret: true }))) : await ask("Llave de Anthropic", { secret: true });
171
+ if (!key) throw new CliError("Falta la llave de Anthropic.");
172
+ await putSecrets(workerDir, { ANTHROPIC_API_KEY: key });
173
+ info("Probando la llave…");
174
+ const check = await claudeCheck(api);
175
+ if (check.ok) {
176
+ ok(check.detail);
177
+ break;
178
+ }
179
+ warn(check.detail);
180
+ if (attempt >= MAX_TRIES || !process.stdin.isTTY || flags.yes) throw new CliError("No pude validar la llave de Anthropic. Revísala y vuelve a correr: npx nubot-lite claude");
181
+ info("Pega la llave otra vez (revisa que esté completa y que tu cuenta tenga saldo).");
182
+ }
183
+ state.claudeReady = true;
184
+ state.model = model;
185
+ save(dir, state);
186
+ ok("Tu agente ya puede responder. Para probarlo sin WhatsApp: npx nubot-lite test \"Hola\"");
187
+ }
188
+
189
+ /** Pregunta al Worker si la llave de Claude funciona. Un secreto nuevo tarda unos segundos en aplicarse. */
190
+ async function claudeCheck(api) {
191
+ let last = { ok: false, detail: "No pude probar la llave." };
192
+ for (let i = 0; i < 4; i++) {
193
+ if (i > 0) await wait(3000);
194
+ const report = await api.get("/api/system/health").catch(() => null);
195
+ const check = report?.checks.find((c) => c.name === "Claude");
196
+ if (check?.ok) return check;
197
+ if (check) last = check;
198
+ }
199
+ return last;
200
+ }
201
+
202
+ /** Pide la API key de Zernio, la comprueba, espera a que haya un número conectado y registra el webhook. */
203
+ export async function setupWhatsapp(dir, state, workerDir, flags = {}) {
204
+ console.log(`
205
+ ${bold("Conectar WhatsApp (Zernio)")}`);
206
+ info("Zernio conecta tu número de WhatsApp con tu CRM. Necesito su API key.");
207
+ info("Se consigue en zernio.com → API Keys. Las 2 primeras cuentas conectadas son gratis. No se va a ver lo que pegues.");
208
+ let key = given(flags, "zernioKey", "ZERNIO_API_KEY");
209
+ let accounts;
210
+ for (let attempt = 1; ; attempt++) {
211
+ key ??= await ask("API key de Zernio", { secret: true });
212
+ if (!key) throw new CliError("Falta la API key de Zernio.");
213
+ try {
214
+ accounts = (await whatsappAccounts(key)).filter((a) => a.active);
215
+ ok("La API key de Zernio funciona");
216
+ break;
217
+ } catch (err) {
218
+ warn(err.message);
219
+ if (attempt >= MAX_TRIES || !process.stdin.isTTY || flags.yes) throw err;
220
+ key = undefined;
221
+ info("Pégala otra vez.");
222
+ }
223
+ }
224
+ await putSecrets(workerDir, { ZERNIO_API_KEY: key });
225
+
226
+ while (!accounts.length) {
227
+ warn("Tu cuenta de Zernio todavía no tiene un número de WhatsApp conectado.");
228
+ if (!process.stdin.isTTY || flags.yes) {
229
+ save(dir, state);
230
+ return false;
231
+ }
232
+ info("Tienes dos caminos, los dos en zernio.com:");
233
+ info(" · Usar un número que ya tienes: Accounts → WhatsApp, y sigue los pasos de Meta con ese número.");
234
+ info(" · Comprar un número nuevo: Phone numbers → busca por país, cómpralo (desde unos US$ 3 al mes) y conéctalo a WhatsApp.");
235
+ const answer = await pause("Presiona Enter cuando esté conectado, o escribe «después» para dejarlo para otro momento");
236
+ if (/^desp/i.test(answer)) {
237
+ save(dir, state);
238
+ return false;
239
+ }
240
+ accounts = (await whatsappAccounts(key)).filter((a) => a.active);
241
+ }
242
+
243
+ const accountId =
244
+ given(flags, "account") ??
245
+ (accounts.length === 1 || flags.yes
246
+ ? accounts[0].id
247
+ : await choose("¿Qué número atiende tu CRM?", accounts.map((a) => ({ value: a.id, label: a.name }))));
248
+ const result = await ensureWebhook(key, { url: state.url, secret: state.webhookSecret, accountId });
249
+ const accountName = accounts.find((a) => a.id === accountId)?.name;
250
+ await apiClient(state).put("/api/channels", { platform: "whatsapp", accountId, name: accountName ?? "WhatsApp" });
251
+ state.zernioAccountId = accountId;
252
+ save(dir, state);
253
+ ok(`${result === "created" ? "Webhook registrado" : "Webhook actualizado"}: los mensajes de ${accounts.find((a) => a.id === accountId)?.name ?? "tu número"} llegan a tu CRM`);
254
+ info("Pruébalo: escríbele a ese número desde otro celular y mira la conversación en el panel.");
255
+ return true;
256
+ }
257
+
258
+ function save(dir, state) {
259
+ writeState(dir, "config.json", { ...state, version: packageVersion() });
260
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nubot-lite",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "Instala Nubot Lite (bandeja de WhatsApp con agente de IA) en tu cuenta de Cloudflare.",
5
5
  "license": "(MIT AND LicenseRef-Nubot-Lite-Panel)",
6
6
  "type": "module",
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: nubot-lite
3
- description: Instala Nubot Lite (CRM de WhatsApp con agente de IA, en Cloudflare) paso a paso y arma el agente de una empresa conversando con la persona. Úsala cuando pidan instalar Nubot Lite, conectar su agente o su WhatsApp, crear o mejorar el agente de su negocio, cargar su catálogo, su web o sus fotos, o probar qué responde el agente.
3
+ description: Instala Nubot Lite (CRM de WhatsApp, Instagram y Facebook con agente de IA, en Cloudflare) paso a paso y arma el agente de una empresa conversando con la persona. Úsala cuando pidan instalar Nubot Lite, conectar su agente, su WhatsApp, su Instagram o su Facebook, crear o mejorar el agente de su negocio, cargar su catálogo, su web o sus fotos, probar qué responde el agente o saber cuánto gasta la IA.
4
4
  ---
5
5
 
6
6
  # Nubot Lite: instalar y armar el agente de una empresa
@@ -80,6 +80,12 @@ Pide la API key sin mostrarla, encuentra el número y deja registrado el webhook
80
80
  espera a que presione Enter. Al terminar, pídele que escriba a ese número desde otro celular y que mire la conversación
81
81
  en el CRM. Propón el paso 4.
82
82
 
83
+ **Instagram y Facebook** no se conectan con un comando: con la cuenta de Zernio ya conectada (este paso), la persona
84
+ entra al panel → **Conexiones** → **Conectar** en Instagram o Facebook, entra con su cuenta de Meta y vuelve al CRM.
85
+ Instagram tiene que ser Business o Creator. En Zernio las 2 primeras cuentas son gratis; desde la tercera cuestan
86
+ US$ 6 al mes y Zernio pide tarjeta. Si pregunta cuánto gasta la IA: panel → **Agentes IA → Métricas** (tiempo, tokens
87
+ y costo estimado de cada respuesta, con descarga en CSV).
88
+
83
89
  ## 4. Armar el agente con la información del negocio
84
90
 
85
91
  Los pasos 5 a 10 dejan al agente sabiendo de su negocio. Es lo que más cambia la calidad de las respuestas.
@@ -1,3 +1,3 @@
1
- Panel de Nubot Lite compilado desde el frontend de Nubot (edición Lite, commit 82e0168).
1
+ Panel de Nubot Lite compilado desde el frontend de Nubot (edición Lite, commit 29123df).
2
2
  No se edita a mano: se regenera con scripts/sync-panel.mjs.
3
3
  No es código abierto: su licencia está en LICENSE.md.
@@ -0,0 +1 @@
1
+ import{ay as e}from"./index-43NA2pdZ.js";import"./recharts-DrC5kNsx.js";export{e as default};