@johpaz/hive-agents 0.0.38 → 0.0.39

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 (214) hide show
  1. package/README.md +18 -18
  2. package/dist/hive.js +3428 -2603
  3. package/dist/tool-worker.js +2085 -1820
  4. package/dist/ui/assets/AgentCreateForm-BTCzFbca.js +1 -0
  5. package/dist/ui/assets/AgentDetailPage-o27TRSVw.js +1 -0
  6. package/dist/ui/assets/AgentNewPage-400cCpYt.js +1 -0
  7. package/dist/ui/{dist/assets/AgentsPage-DGNLDXjR.js → assets/AgentsPage-C-GSRk-N.js} +5 -5
  8. package/dist/ui/assets/ApiClientPage-BOTpz6oP.js +3 -0
  9. package/dist/ui/assets/{CanvasPage-CnMO1FN8.js → CanvasPage-Cvs5ctza.js} +7 -7
  10. package/dist/ui/assets/ChannelsPage-C5m_L7P9.js +8 -0
  11. package/dist/ui/{dist/assets/DashboardPage-VyXXp3U1.js → assets/DashboardPage-CztbRQdm.js} +2 -2
  12. package/dist/ui/assets/{LoginPage-DPj2s2Qq.js → LoginPage-OMsrx5oj.js} +1 -1
  13. package/dist/ui/assets/LogsPage-CcYYwjgF.js +1 -0
  14. package/dist/ui/{dist/assets/MeetingPage-2ky_hKiG.js → assets/MeetingPage-CrKVAfe6.js} +1 -1
  15. package/dist/ui/assets/{NotFound-BMeQSGcG.js → NotFound-GbAJDgoD.js} +1 -1
  16. package/dist/ui/assets/ProvidersPage-uqPcZUSV.js +1 -0
  17. package/dist/ui/{dist/assets/RecoverPage-B-hDZUM2.js → assets/RecoverPage-CwB2ByCU.js} +1 -1
  18. package/dist/ui/assets/SettingsPage-DKLlye0z.js +9 -0
  19. package/dist/ui/assets/SetupPage-DOVh1ldK.js +1 -0
  20. package/dist/ui/{dist/assets/WebChatPage-BuGT2AL0.js → assets/WebChatPage-c-7S9jnT.js} +2 -2
  21. package/dist/ui/assets/accordion-DAbcVQCn.js +1 -0
  22. package/dist/ui/{dist/assets/alert-Bq6awLlW.js → assets/alert-D_2Y3qjL.js} +1 -1
  23. package/dist/ui/{dist/assets/alert-dialog-DQvltYmf.js → assets/alert-dialog-CpMxaNcu.js} +1 -1
  24. package/dist/ui/assets/{badge-DXUDdTed.js → badge-CxTPR6_t.js} +1 -1
  25. package/dist/ui/assets/bell-8BqRYmzf.js +1 -0
  26. package/dist/ui/assets/circle-x-Bv6WrUJo.js +1 -0
  27. package/dist/ui/assets/copy-dU94ZGsi.js +1 -0
  28. package/dist/ui/{dist/assets/dialog-bI9jImCS.js → assets/dialog-DfS3idb3.js} +1 -1
  29. package/dist/ui/{dist/assets/dropdown-menu-BK-CO3Od.js → assets/dropdown-menu-BdCbAW1z.js} +1 -1
  30. package/dist/ui/assets/{es-Cg8zdT52.js → es-Cz5h9_84.js} +1 -1
  31. package/dist/ui/assets/index-CmGm_r89.js +116 -0
  32. package/dist/ui/assets/index-T7HgphSn.css +2 -0
  33. package/dist/ui/{dist/assets/label-CrH0Jj3v.js → assets/label-byJkqOYq.js} +1 -1
  34. package/dist/ui/assets/progress-Dtz-Mzys.js +1 -0
  35. package/dist/ui/assets/scroll-area-BXtLsE9E.js +1 -0
  36. package/dist/ui/assets/search-BGmPJ-6L.js +1 -0
  37. package/dist/ui/assets/select-Cl16QYa_.js +1 -0
  38. package/dist/ui/assets/send-BuQcUO-R.js +1 -0
  39. package/dist/ui/assets/shield-C-05qB-2.js +1 -0
  40. package/dist/ui/assets/{slider-CsiUDxc3.js → slider-D2I0qven.js} +1 -1
  41. package/dist/ui/assets/switch-h2SfQX4B.js +1 -0
  42. package/dist/ui/assets/table-CVkIRJKK.js +1 -0
  43. package/dist/ui/assets/tabs-C619jxbO.js +1 -0
  44. package/dist/ui/assets/textarea-wvA-FDjO.js +1 -0
  45. package/dist/ui/assets/useProviders-eEri6BAc.js +1 -0
  46. package/dist/ui/{dist/assets/vendor-radix-cw1bQaVC.js → assets/vendor-radix-D6rA7xKY.js} +4 -4
  47. package/dist/ui/assets/{vendor-react-D4s9E-zj.js → vendor-react-BU5iQU4f.js} +1 -1
  48. package/dist/ui/dist/assets/AgentCreateForm-BTCzFbca.js +1 -0
  49. package/dist/ui/dist/assets/AgentDetailPage-o27TRSVw.js +1 -0
  50. package/dist/ui/dist/assets/AgentNewPage-400cCpYt.js +1 -0
  51. package/dist/ui/{assets/AgentsPage-DGNLDXjR.js → dist/assets/AgentsPage-C-GSRk-N.js} +5 -5
  52. package/dist/ui/dist/assets/ApiClientPage-BOTpz6oP.js +3 -0
  53. package/dist/ui/dist/assets/{CanvasPage-CnMO1FN8.js → CanvasPage-Cvs5ctza.js} +7 -7
  54. package/dist/ui/dist/assets/ChannelsPage-C5m_L7P9.js +8 -0
  55. package/dist/ui/{assets/DashboardPage-VyXXp3U1.js → dist/assets/DashboardPage-CztbRQdm.js} +2 -2
  56. package/dist/ui/dist/assets/{LoginPage-DPj2s2Qq.js → LoginPage-OMsrx5oj.js} +1 -1
  57. package/dist/ui/dist/assets/LogsPage-CcYYwjgF.js +1 -0
  58. package/dist/ui/{assets/MeetingPage-2ky_hKiG.js → dist/assets/MeetingPage-CrKVAfe6.js} +1 -1
  59. package/dist/ui/dist/assets/{NotFound-BMeQSGcG.js → NotFound-GbAJDgoD.js} +1 -1
  60. package/dist/ui/dist/assets/ProvidersPage-uqPcZUSV.js +1 -0
  61. package/dist/ui/{assets/RecoverPage-B-hDZUM2.js → dist/assets/RecoverPage-CwB2ByCU.js} +1 -1
  62. package/dist/ui/dist/assets/SettingsPage-DKLlye0z.js +9 -0
  63. package/dist/ui/dist/assets/SetupPage-DOVh1ldK.js +1 -0
  64. package/dist/ui/{assets/WebChatPage-BuGT2AL0.js → dist/assets/WebChatPage-c-7S9jnT.js} +2 -2
  65. package/dist/ui/dist/assets/accordion-DAbcVQCn.js +1 -0
  66. package/dist/ui/{assets/alert-Bq6awLlW.js → dist/assets/alert-D_2Y3qjL.js} +1 -1
  67. package/dist/ui/{assets/alert-dialog-DQvltYmf.js → dist/assets/alert-dialog-CpMxaNcu.js} +1 -1
  68. package/dist/ui/dist/assets/{badge-DXUDdTed.js → badge-CxTPR6_t.js} +1 -1
  69. package/dist/ui/dist/assets/bell-8BqRYmzf.js +1 -0
  70. package/dist/ui/dist/assets/circle-x-Bv6WrUJo.js +1 -0
  71. package/dist/ui/dist/assets/copy-dU94ZGsi.js +1 -0
  72. package/dist/ui/{assets/dialog-bI9jImCS.js → dist/assets/dialog-DfS3idb3.js} +1 -1
  73. package/dist/ui/{assets/dropdown-menu-BK-CO3Od.js → dist/assets/dropdown-menu-BdCbAW1z.js} +1 -1
  74. package/dist/ui/dist/assets/{es-Cg8zdT52.js → es-Cz5h9_84.js} +1 -1
  75. package/dist/ui/dist/assets/index-CmGm_r89.js +116 -0
  76. package/dist/ui/dist/assets/index-T7HgphSn.css +2 -0
  77. package/dist/ui/{assets/label-CrH0Jj3v.js → dist/assets/label-byJkqOYq.js} +1 -1
  78. package/dist/ui/dist/assets/progress-Dtz-Mzys.js +1 -0
  79. package/dist/ui/dist/assets/scroll-area-BXtLsE9E.js +1 -0
  80. package/dist/ui/dist/assets/search-BGmPJ-6L.js +1 -0
  81. package/dist/ui/dist/assets/select-Cl16QYa_.js +1 -0
  82. package/dist/ui/dist/assets/send-BuQcUO-R.js +1 -0
  83. package/dist/ui/dist/assets/shield-C-05qB-2.js +1 -0
  84. package/dist/ui/dist/assets/{slider-CsiUDxc3.js → slider-D2I0qven.js} +1 -1
  85. package/dist/ui/dist/assets/switch-h2SfQX4B.js +1 -0
  86. package/dist/ui/dist/assets/table-CVkIRJKK.js +1 -0
  87. package/dist/ui/dist/assets/tabs-C619jxbO.js +1 -0
  88. package/dist/ui/dist/assets/textarea-wvA-FDjO.js +1 -0
  89. package/dist/ui/dist/assets/useProviders-eEri6BAc.js +1 -0
  90. package/dist/ui/{assets/vendor-radix-cw1bQaVC.js → dist/assets/vendor-radix-D6rA7xKY.js} +4 -4
  91. package/dist/ui/dist/assets/{vendor-react-D4s9E-zj.js → vendor-react-BU5iQU4f.js} +1 -1
  92. package/dist/ui/dist/index.html +6 -6
  93. package/dist/ui/index.html +6 -6
  94. package/package.json +1 -1
  95. package/packages/cli/src/adapters/binary.ts +8 -4
  96. package/packages/cli/src/adapters/bun-global.ts +5 -1
  97. package/packages/cli/src/adapters/config.ts +4 -3
  98. package/packages/cli/src/adapters/docker.ts +2 -1
  99. package/packages/cli/src/commands/gateway.ts +123 -9
  100. package/packages/cli/src/commands/logs.ts +2 -1
  101. package/packages/cli/src/commands/onboard.ts +1 -1
  102. package/packages/cli/src/commands/sessions.ts +2 -1
  103. package/packages/cli/src/commands/skills.ts +2 -1
  104. package/packages/core/src/agent/llm-client.ts +4 -0
  105. package/packages/core/src/agent/llm-providers/anthropic.ts +23 -8
  106. package/packages/core/src/agent/llm-providers/interface.ts +5 -1
  107. package/packages/core/src/agent/llm-providers/minimax.ts +13 -0
  108. package/packages/core/src/agent/llm-providers/opencode-go.ts +9 -0
  109. package/packages/core/src/channels/whatsapp.ts +13 -1
  110. package/packages/core/src/config/loader.ts +7 -7
  111. package/packages/core/src/gateway/helpers/path.ts +2 -1
  112. package/packages/core/src/gateway/initializer.ts +4 -4
  113. package/packages/core/src/gateway/llm-local/downloader.ts +130 -11
  114. package/packages/core/src/gateway/llm-local/index.ts +2 -0
  115. package/packages/core/src/gateway/llm-local/models.ts +4 -3
  116. package/packages/core/src/gateway/router.ts +7 -5
  117. package/packages/core/src/gateway/routes/http-client.ts +16 -0
  118. package/packages/core/src/gateway/routes/llm-local.ts +51 -5
  119. package/packages/core/src/gateway/routes/providers.ts +43 -2
  120. package/packages/core/src/gateway/server.ts +11 -2
  121. package/packages/core/src/gateway/tts/src/install.ts +17 -9
  122. package/packages/core/src/storage/crypto.ts +152 -20
  123. package/packages/core/src/storage/migrate.ts +51 -18
  124. package/packages/core/src/storage/seed.ts +38 -2
  125. package/packages/core/src/tool-runtime/index.ts +22 -1
  126. package/packages/core/src/tools/api/api-request.ts +174 -0
  127. package/packages/core/src/tools/api/index.ts +16 -0
  128. package/packages/core/src/tools/index.ts +12 -0
  129. package/packages/core/src/tools/web/browser-click.ts +2 -2
  130. package/packages/core/src/tools/web/browser-extract.ts +22 -6
  131. package/packages/core/src/tools/web/browser-navigate.ts +34 -18
  132. package/packages/core/src/tools/web/browser-screenshot.ts +40 -8
  133. package/packages/core/src/tools/web/browser-script.ts +2 -2
  134. package/packages/core/src/tools/web/browser-service.ts +295 -341
  135. package/packages/core/src/tools/web/browser-type.ts +5 -10
  136. package/packages/core/src/tools/web/browser-wait.ts +2 -2
  137. package/packages/core/src/tools/web/index.ts +1 -1
  138. package/packages/core/src/utils/logger.ts +2 -1
  139. package/packages/mcp/src/manager.ts +2 -1
  140. package/packages/skills/src/bundled/api/api_client/SKILL.md +132 -0
  141. package/packages/skills/src/bundled-data.generated.ts +1274 -1217
  142. package/packages/skills/src/loader.ts +2 -1
  143. package/dist/ui/assets/AgentCreateForm-0oFbN3gj.js +0 -1
  144. package/dist/ui/assets/AgentDetailPage-BJ4L2fNJ.js +0 -1
  145. package/dist/ui/assets/AgentNewPage-B3n0LUck.js +0 -1
  146. package/dist/ui/assets/ChannelsPage-fbF8K4MR.js +0 -8
  147. package/dist/ui/assets/LogsPage-B2lY9maY.js +0 -1
  148. package/dist/ui/assets/ProvidersPage-CEyUM2tD.js +0 -1
  149. package/dist/ui/assets/SettingsPage-eO0i3g8p.js +0 -9
  150. package/dist/ui/assets/SetupPage-ByYqTELb.js +0 -1
  151. package/dist/ui/assets/accordion-C5d5Rm5z.js +0 -1
  152. package/dist/ui/assets/globe-DeCQTCDJ.js +0 -1
  153. package/dist/ui/assets/index-B2fCYtTS.css +0 -2
  154. package/dist/ui/assets/index-CQ7fn00w.js +0 -116
  155. package/dist/ui/assets/progress-BherYzY6.js +0 -1
  156. package/dist/ui/assets/scroll-area-DkeyX32e.js +0 -1
  157. package/dist/ui/assets/send-B0H5SEIE.js +0 -1
  158. package/dist/ui/assets/switch-BDwN8RYV.js +0 -1
  159. package/dist/ui/assets/table-CSc8ubon.js +0 -1
  160. package/dist/ui/assets/textarea-CXgXWKrT.js +0 -1
  161. package/dist/ui/assets/useProviders-CnlC_qCS.js +0 -1
  162. package/dist/ui/dist/assets/AgentCreateForm-0oFbN3gj.js +0 -1
  163. package/dist/ui/dist/assets/AgentDetailPage-BJ4L2fNJ.js +0 -1
  164. package/dist/ui/dist/assets/AgentNewPage-B3n0LUck.js +0 -1
  165. package/dist/ui/dist/assets/ChannelsPage-fbF8K4MR.js +0 -8
  166. package/dist/ui/dist/assets/LogsPage-B2lY9maY.js +0 -1
  167. package/dist/ui/dist/assets/ProvidersPage-CEyUM2tD.js +0 -1
  168. package/dist/ui/dist/assets/SettingsPage-eO0i3g8p.js +0 -9
  169. package/dist/ui/dist/assets/SetupPage-ByYqTELb.js +0 -1
  170. package/dist/ui/dist/assets/accordion-C5d5Rm5z.js +0 -1
  171. package/dist/ui/dist/assets/globe-DeCQTCDJ.js +0 -1
  172. package/dist/ui/dist/assets/index-B2fCYtTS.css +0 -2
  173. package/dist/ui/dist/assets/index-CQ7fn00w.js +0 -116
  174. package/dist/ui/dist/assets/progress-BherYzY6.js +0 -1
  175. package/dist/ui/dist/assets/scroll-area-DkeyX32e.js +0 -1
  176. package/dist/ui/dist/assets/send-B0H5SEIE.js +0 -1
  177. package/dist/ui/dist/assets/switch-BDwN8RYV.js +0 -1
  178. package/dist/ui/dist/assets/table-CSc8ubon.js +0 -1
  179. package/dist/ui/dist/assets/textarea-CXgXWKrT.js +0 -1
  180. package/dist/ui/dist/assets/useProviders-CnlC_qCS.js +0 -1
  181. /package/dist/ui/assets/{card-CNf6BS2e.js → card-CXAm46at.js} +0 -0
  182. /package/dist/ui/assets/{cpu-Cdgc_B1K.js → cpu-DSpPVLAz.js} +0 -0
  183. /package/dist/ui/assets/{download-C3ifGMjJ.js → download-D9ZyUZZR.js} +0 -0
  184. /package/dist/ui/assets/{external-link-BvxYeTP1.js → external-link-CHPbUorN.js} +0 -0
  185. /package/dist/ui/assets/{eye-DqNTU_GD.js → eye-epHJZ_nQ.js} +0 -0
  186. /package/dist/ui/assets/{file-text-BT_9S9SM.js → file-text-BEjEmgby.js} +0 -0
  187. /package/dist/ui/assets/{folder-open-BhH8y9ac.js → folder-open-iQMHVEqS.js} +0 -0
  188. /package/dist/ui/assets/{format-GVHeOyWI.js → format-oFACFaca.js} +0 -0
  189. /package/dist/ui/assets/{gateway-url-COCbW0IR.js → gateway-url-iG-C6Agn.js} +0 -0
  190. /package/dist/ui/assets/{gauge-D_TMa4i9.js → gauge-D0_GMEcq.js} +0 -0
  191. /package/dist/ui/assets/{settings-Ds4SqD8s.js → settings-CcMGI1iU.js} +0 -0
  192. /package/dist/ui/assets/{sparkles-yUEb-7oH.js → sparkles-D6fx8JC5.js} +0 -0
  193. /package/dist/ui/assets/{trash-2-CNjMkoq6.js → trash-2-BHRa5ft9.js} +0 -0
  194. /package/dist/ui/assets/{triangle-alert-C9Y8Ub4X.js → triangle-alert-D4nwAVbc.js} +0 -0
  195. /package/dist/ui/assets/{vendor-router-C9pIYwbJ.js → vendor-router-pCP7sjma.js} +0 -0
  196. /package/dist/ui/assets/{volume-2-CeSXNDv4.js → volume-2-B6tkRy2u.js} +0 -0
  197. /package/dist/ui/assets/{zap-hlXjpSeA.js → zap-QO7iWMRg.js} +0 -0
  198. /package/dist/ui/dist/assets/{card-CNf6BS2e.js → card-CXAm46at.js} +0 -0
  199. /package/dist/ui/dist/assets/{cpu-Cdgc_B1K.js → cpu-DSpPVLAz.js} +0 -0
  200. /package/dist/ui/dist/assets/{download-C3ifGMjJ.js → download-D9ZyUZZR.js} +0 -0
  201. /package/dist/ui/dist/assets/{external-link-BvxYeTP1.js → external-link-CHPbUorN.js} +0 -0
  202. /package/dist/ui/dist/assets/{eye-DqNTU_GD.js → eye-epHJZ_nQ.js} +0 -0
  203. /package/dist/ui/dist/assets/{file-text-BT_9S9SM.js → file-text-BEjEmgby.js} +0 -0
  204. /package/dist/ui/dist/assets/{folder-open-BhH8y9ac.js → folder-open-iQMHVEqS.js} +0 -0
  205. /package/dist/ui/dist/assets/{format-GVHeOyWI.js → format-oFACFaca.js} +0 -0
  206. /package/dist/ui/dist/assets/{gateway-url-COCbW0IR.js → gateway-url-iG-C6Agn.js} +0 -0
  207. /package/dist/ui/dist/assets/{gauge-D_TMa4i9.js → gauge-D0_GMEcq.js} +0 -0
  208. /package/dist/ui/dist/assets/{settings-Ds4SqD8s.js → settings-CcMGI1iU.js} +0 -0
  209. /package/dist/ui/dist/assets/{sparkles-yUEb-7oH.js → sparkles-D6fx8JC5.js} +0 -0
  210. /package/dist/ui/dist/assets/{trash-2-CNjMkoq6.js → trash-2-BHRa5ft9.js} +0 -0
  211. /package/dist/ui/dist/assets/{triangle-alert-C9Y8Ub4X.js → triangle-alert-D4nwAVbc.js} +0 -0
  212. /package/dist/ui/dist/assets/{vendor-router-C9pIYwbJ.js → vendor-router-pCP7sjma.js} +0 -0
  213. /package/dist/ui/dist/assets/{volume-2-CeSXNDv4.js → volume-2-B6tkRy2u.js} +0 -0
  214. /package/dist/ui/dist/assets/{zap-hlXjpSeA.js → zap-QO7iWMRg.js} +0 -0
@@ -250,11 +250,11 @@ export async function initializeGateway(
250
250
  const agent = createAgentService();
251
251
  await agent.initialize();
252
252
 
253
- // 5b. Initialize Browser Service (Chrome via Bun.WebView nativo)
253
+ // 5b. Initialize Browser Service (agent-browser CLI)
254
254
  let browserAvailable = false;
255
255
 
256
256
  try {
257
- log.info("Detecting browser (lazy launch — will open on first agent use)...");
257
+ log.info("Initializing browser automation (agent-browser)...");
258
258
 
259
259
  const browserService = initializeBrowserService(config);
260
260
  browserAvailable = await browserService.start();
@@ -262,8 +262,8 @@ export async function initializeGateway(
262
262
  if (browserAvailable) {
263
263
  activateBrowserTools();
264
264
  } else {
265
- log.warn("⚠️ No se encontró Chrome/Chromium - browser tools desactivadas");
266
- log.warn(" Linux: sudo dnf install chromium | macOS: brew install --cask google-chrome");
265
+ log.warn("⚠️ agent-browser no disponible - browser tools desactivadas");
266
+ log.warn(" Se instalará automáticamente en primer uso o manual: bun add -g agent-browser");
267
267
  }
268
268
  } catch (error) {
269
269
  log.warn(`Browser Service initialization skipped: ${(error as Error).message}`);
@@ -18,22 +18,116 @@ export const MODELS_DIR = join(LLM_ROOT, "models")
18
18
  if (!existsSync(BIN_DIR)) mkdirSync(BIN_DIR, { recursive: true })
19
19
  if (!existsSync(MODELS_DIR)) mkdirSync(MODELS_DIR, { recursive: true })
20
20
 
21
- /** URLs de modelos en HuggingFace (Gemma 4 según GUIA-SERVIDOR.md) */
21
+ /** URLs de modelos en HuggingFace (Gemma 4 y Qwen 3.5 según Unsloth) */
22
22
  export const HF_MODEL_URLS = {
23
+ // mmproj legacy (E4B)
23
24
  mmproj: "https://huggingface.co/unsloth/gemma-4-E4B-it-GGUF/resolve/main/mmproj-BF16.gguf",
25
+ // mmproj Gemma 4
26
+ mmproj_gemma4_12b: "https://huggingface.co/unsloth/gemma-4-12b-it-GGUF/resolve/main/mmproj-BF16.gguf",
27
+ mmproj_gemma4_26b: "https://huggingface.co/unsloth/gemma-4-26B-A4B-it-GGUF/resolve/main/mmproj-BF16.gguf",
28
+ mmproj_gemma4_31b: "https://huggingface.co/unsloth/gemma-4-31B-it-GGUF/resolve/main/mmproj-BF16.gguf",
29
+ // mmproj Qwen 3.5
30
+ mmproj_qwen3_5_2b: "https://huggingface.co/unsloth/Qwen3.5-2B-GGUF/resolve/main/mmproj-F16.gguf",
31
+ mmproj_qwen3_5_4b: "https://huggingface.co/unsloth/Qwen3.5-4B-GGUF/resolve/main/mmproj-F16.gguf",
32
+ mmproj_qwen3_5_9b: "https://huggingface.co/unsloth/Qwen3.5-9B-GGUF/resolve/main/mmproj-F16.gguf",
33
+ mmproj_qwen3_5_27b: "https://huggingface.co/unsloth/Qwen3.5-27B-GGUF/resolve/main/mmproj-F16.gguf",
34
+ mmproj_qwen3_5_35b: "https://huggingface.co/unsloth/Qwen3.5-35B-A3B-GGUF/resolve/main/mmproj-F16.gguf",
35
+ // modelos Gemma 4
24
36
  e2b_Q4_K_XL: "https://huggingface.co/unsloth/gemma-4-E2B-it-GGUF/resolve/main/gemma-4-E2B-it-UD-Q4_K_XL.gguf",
25
- e4b_Q4_K_XL: "https://huggingface.co/unsloth/gemma-4-E4B-it-GGUF/resolve/main/gemma-4-E4B-it-UD-Q4_K_XL.gguf"
37
+ e4b_Q4_K_XL: "https://huggingface.co/unsloth/gemma-4-E4B-it-GGUF/resolve/main/gemma-4-E4B-it-UD-Q4_K_XL.gguf",
38
+ gemma4_12b_Q4_K_XL: "https://huggingface.co/unsloth/gemma-4-12b-it-GGUF/resolve/main/gemma-4-12b-it-UD-Q4_K_XL.gguf",
39
+ gemma4_26b_Q4_K_M: "https://huggingface.co/unsloth/gemma-4-26B-A4B-it-GGUF/resolve/main/gemma-4-26B-A4B-it-UD-Q4_K_M.gguf",
40
+ gemma4_31b_Q4_K_XL: "https://huggingface.co/unsloth/gemma-4-31B-it-GGUF/resolve/main/gemma-4-31B-it-UD-Q4_K_XL.gguf",
41
+ // modelos Qwen 3.5
42
+ qwen3_5_2b_Q4_K_XL: "https://huggingface.co/unsloth/Qwen3.5-2B-GGUF/resolve/main/Qwen3.5-2B-UD-Q4_K_XL.gguf",
43
+ qwen3_5_4b_Q4_K_XL: "https://huggingface.co/unsloth/Qwen3.5-4B-GGUF/resolve/main/Qwen3.5-4B-UD-Q4_K_XL.gguf",
44
+ qwen3_5_9b_Q4_K_XL: "https://huggingface.co/unsloth/Qwen3.5-9B-GGUF/resolve/main/Qwen3.5-9B-UD-Q4_K_XL.gguf",
45
+ qwen3_5_27b_Q4_K_XL: "https://huggingface.co/unsloth/Qwen3.5-27B-GGUF/resolve/main/Qwen3.5-27B-UD-Q4_K_XL.gguf",
46
+ qwen3_5_35b_Q4_K_XL: "https://huggingface.co/unsloth/Qwen3.5-35B-A3B-GGUF/resolve/main/Qwen3.5-35B-A3B-UD-Q4_K_XL.gguf",
26
47
  }
27
48
 
28
- export type ModelId = "mmproj" | "e2b_Q4_K_XL" | "e4b_Q4_K_XL"
49
+ export type ModelId =
50
+ | "mmproj"
51
+ | "mmproj_gemma4_12b"
52
+ | "mmproj_gemma4_26b"
53
+ | "mmproj_gemma4_31b"
54
+ | "mmproj_qwen3_5_2b"
55
+ | "mmproj_qwen3_5_4b"
56
+ | "mmproj_qwen3_5_9b"
57
+ | "mmproj_qwen3_5_27b"
58
+ | "mmproj_qwen3_5_35b"
59
+ | "e2b_Q4_K_XL"
60
+ | "e4b_Q4_K_XL"
61
+ | "gemma4_12b_Q4_K_XL"
62
+ | "gemma4_26b_Q4_K_M"
63
+ | "gemma4_31b_Q4_K_XL"
64
+ | "qwen3_5_2b_Q4_K_XL"
65
+ | "qwen3_5_4b_Q4_K_XL"
66
+ | "qwen3_5_9b_Q4_K_XL"
67
+ | "qwen3_5_27b_Q4_K_XL"
68
+ | "qwen3_5_35b_Q4_K_XL"
29
69
 
30
70
  /** Modelos que requieren proyector de visión (mmproj) */
31
- export const VISION_MODELS: ModelId[] = ["e4b_Q4_K_XL"]
71
+ export const VISION_MODELS: ModelId[] = [
72
+ "e4b_Q4_K_XL",
73
+ "gemma4_12b_Q4_K_XL",
74
+ "gemma4_26b_Q4_K_M",
75
+ "gemma4_31b_Q4_K_XL",
76
+ "qwen3_5_2b_Q4_K_XL",
77
+ "qwen3_5_4b_Q4_K_XL",
78
+ "qwen3_5_9b_Q4_K_XL",
79
+ "qwen3_5_27b_Q4_K_XL",
80
+ "qwen3_5_35b_Q4_K_XL",
81
+ ]
82
+
83
+ /** Mapeo de cada modelo a su mmproj correspondiente */
84
+ export const MODEL_MMPROJ_MAP: Record<ModelId, ModelId | undefined> = {
85
+ mmproj: undefined,
86
+ mmproj_gemma4_12b: undefined,
87
+ mmproj_gemma4_26b: undefined,
88
+ mmproj_gemma4_31b: undefined,
89
+ mmproj_qwen3_5_2b: undefined,
90
+ mmproj_qwen3_5_4b: undefined,
91
+ mmproj_qwen3_5_9b: undefined,
92
+ mmproj_qwen3_5_27b: undefined,
93
+ mmproj_qwen3_5_35b: undefined,
94
+ e2b_Q4_K_XL: undefined,
95
+ e4b_Q4_K_XL: "mmproj",
96
+ gemma4_12b_Q4_K_XL: "mmproj_gemma4_12b",
97
+ gemma4_26b_Q4_K_M: "mmproj_gemma4_26b",
98
+ gemma4_31b_Q4_K_XL: "mmproj_gemma4_31b",
99
+ qwen3_5_2b_Q4_K_XL: "mmproj_qwen3_5_2b",
100
+ qwen3_5_4b_Q4_K_XL: "mmproj_qwen3_5_4b",
101
+ qwen3_5_9b_Q4_K_XL: "mmproj_qwen3_5_9b",
102
+ qwen3_5_27b_Q4_K_XL: "mmproj_qwen3_5_27b",
103
+ qwen3_5_35b_Q4_K_XL: "mmproj_qwen3_5_35b",
104
+ }
105
+
106
+ /** Devuelve el ID del mmproj asociado a un modelo, o undefined si no requiere visión */
107
+ export function getModelMMProjId(modelId: ModelId): ModelId | undefined {
108
+ return MODEL_MMPROJ_MAP[modelId]
109
+ }
32
110
 
33
111
  export const MODEL_FILES: Record<ModelId, string> = {
34
112
  mmproj: "mmproj-BF16.gguf",
113
+ mmproj_gemma4_12b: "mmproj-gemma4-12b-BF16.gguf",
114
+ mmproj_gemma4_26b: "mmproj-gemma4-26b-BF16.gguf",
115
+ mmproj_gemma4_31b: "mmproj-gemma4-31b-BF16.gguf",
116
+ mmproj_qwen3_5_2b: "mmproj-qwen3.5-2b-F16.gguf",
117
+ mmproj_qwen3_5_4b: "mmproj-qwen3.5-4b-F16.gguf",
118
+ mmproj_qwen3_5_9b: "mmproj-qwen3.5-9b-F16.gguf",
119
+ mmproj_qwen3_5_27b: "mmproj-qwen3.5-27b-F16.gguf",
120
+ mmproj_qwen3_5_35b: "mmproj-qwen3.5-35b-F16.gguf",
35
121
  e2b_Q4_K_XL: "gemma-4-E2B-it-UD-Q4_K_XL.gguf",
36
122
  e4b_Q4_K_XL: "gemma-4-E4B-it-UD-Q4_K_XL.gguf",
123
+ gemma4_12b_Q4_K_XL: "gemma-4-12b-it-UD-Q4_K_XL.gguf",
124
+ gemma4_26b_Q4_K_M: "gemma-4-26B-A4B-it-UD-Q4_K_M.gguf",
125
+ gemma4_31b_Q4_K_XL: "gemma-4-31B-it-UD-Q4_K_XL.gguf",
126
+ qwen3_5_2b_Q4_K_XL: "Qwen3.5-2B-UD-Q4_K_XL.gguf",
127
+ qwen3_5_4b_Q4_K_XL: "Qwen3.5-4B-UD-Q4_K_XL.gguf",
128
+ qwen3_5_9b_Q4_K_XL: "Qwen3.5-9B-UD-Q4_K_XL.gguf",
129
+ qwen3_5_27b_Q4_K_XL: "Qwen3.5-27B-UD-Q4_K_XL.gguf",
130
+ qwen3_5_35b_Q4_K_XL: "Qwen3.5-35B-A3B-UD-Q4_K_XL.gguf",
37
131
  }
38
132
 
39
133
  export function getModelPath(modelId: ModelId): string {
@@ -65,13 +159,14 @@ export async function downloadFile(
65
159
  const { done, value } = await reader.read()
66
160
  if (done) break
67
161
 
68
- writer.write(value)
162
+ await writer.write(value)
69
163
  downloaded += value.byteLength
70
164
  if (onProgress) onProgress(downloaded, total)
71
165
  }
166
+ await writer.flush()
72
167
  await writer.end()
73
168
  } catch (err) {
74
- await writer.end()
169
+ try { await writer.end() } catch { /* ignore */ }
75
170
  throw err
76
171
  }
77
172
  }
@@ -191,9 +286,10 @@ export async function downloadModel(
191
286
  await downloadFile(url, dest, onProgress)
192
287
  console.log(`[hive-local] ✓ Modelo ${modelId} descargado`)
193
288
 
194
- // Advertir si el modelo requiere visión y no tenemos mmproj (NO descargar automáticamente)
195
- if (VISION_MODELS.includes(modelId) && !isModelDownloaded("mmproj")) {
196
- console.warn(`[hive-local] ⚠️ El modelo ${modelId} requiere el proyector de visión (mmproj). Descárgalo manualmente desde la UI.`)
289
+ // Advertir si el modelo requiere visión y no tenemos su mmproj (NO descargar automáticamente)
290
+ const mmprojId = getModelMMProjId(modelId)
291
+ if (mmprojId && !isModelDownloaded(mmprojId)) {
292
+ console.warn(`[hive-local] ⚠️ El modelo ${modelId} requiere el proyector de visión (${mmprojId}). Descárgalo manualmente desde la UI.`)
197
293
  }
198
294
 
199
295
  return dest
@@ -206,11 +302,34 @@ export async function installMMProj(): Promise<string> {
206
302
 
207
303
  /** Lista modelos disponibles localmente (solo modelos de texto/visión finales) */
208
304
  export function listLocalModels(): { id: ModelId; name: string; size: string; downloaded: boolean }[] {
209
- const models: ModelId[] = ["e2b_Q4_K_XL", "e4b_Q4_K_XL"]
305
+ const models: ModelId[] = [
306
+ "e2b_Q4_K_XL",
307
+ "e4b_Q4_K_XL",
308
+ "gemma4_12b_Q4_K_XL",
309
+ "gemma4_26b_Q4_K_M",
310
+ "gemma4_31b_Q4_K_XL",
311
+ "qwen3_5_2b_Q4_K_XL",
312
+ "qwen3_5_4b_Q4_K_XL",
313
+ "qwen3_5_9b_Q4_K_XL",
314
+ "qwen3_5_27b_Q4_K_XL",
315
+ "qwen3_5_35b_Q4_K_XL",
316
+ ]
317
+ const sizeMap: Record<string, string> = {
318
+ e2b_Q4_K_XL: "~3 GB",
319
+ e4b_Q4_K_XL: "~5 GB",
320
+ gemma4_12b_Q4_K_XL: "~7 GB",
321
+ gemma4_26b_Q4_K_M: "~10 GB",
322
+ gemma4_31b_Q4_K_XL: "~14 GB",
323
+ qwen3_5_2b_Q4_K_XL: "~1.2 GB",
324
+ qwen3_5_4b_Q4_K_XL: "~2.5 GB",
325
+ qwen3_5_9b_Q4_K_XL: "~5.4 GB",
326
+ qwen3_5_27b_Q4_K_XL: "~16 GB",
327
+ qwen3_5_35b_Q4_K_XL: "~21 GB",
328
+ }
210
329
  return models.map((id) => ({
211
330
  id,
212
331
  name: MODEL_FILES[id],
213
- size: id.includes("Q4_K_XL") ? "~5-8 GB" : "~2-3 GB",
332
+ size: sizeMap[id] || "~?",
214
333
  downloaded: isModelDownloaded(id),
215
334
  }))
216
335
  }
@@ -15,9 +15,11 @@ export {
15
15
  listLocalModels,
16
16
  isModelDownloaded,
17
17
  getModelPath,
18
+ getModelMMProjId,
18
19
  BIN_DIR,
19
20
  MODELS_DIR,
20
21
  HF_MODEL_URLS,
22
+ MODEL_MMPROJ_MAP,
21
23
  } from "./downloader"
22
24
  export type { ModelId } from "./downloader"
23
25
 
@@ -5,7 +5,7 @@
5
5
 
6
6
 
7
7
  import { existsSync } from "fs"
8
- import { MODELS_DIR, getModelPath, type ModelId } from "./downloader"
8
+ import { MODELS_DIR, getModelPath, getModelMMProjId, type ModelId } from "./downloader"
9
9
 
10
10
  export interface ModelConfig {
11
11
  modelPath: string
@@ -43,8 +43,9 @@ export function getModelConfig(modelId: ModelId, port: number = 8081): ModelConf
43
43
  throw new Error(`Modelo no descargado: ${modelId}. Ejecuta downloadModel("${modelId}") primero.`)
44
44
  }
45
45
 
46
- const mmprojPath = getModelPath("mmproj")
47
- const hasMMProj = existsSync(mmprojPath)
46
+ const mmprojId = getModelMMProjId(modelId)
47
+ const mmprojPath = mmprojId ? getModelPath(mmprojId) : undefined
48
+ const hasMMProj = mmprojPath ? existsSync(mmprojPath) : false
48
49
 
49
50
  return {
50
51
  modelPath,
@@ -1,4 +1,6 @@
1
1
  import type { Config, Binding } from "../config/loader.ts";
2
+ import { homedir } from "node:os";
3
+ import * as path from "node:path";
2
4
 
3
5
  export interface RoutingContext {
4
6
  channel: string;
@@ -113,12 +115,12 @@ export class Router {
113
115
  const agent = agents.find((a) => a.id === agentId);
114
116
 
115
117
  if (agent?.workspace) {
116
- return agent.workspace.replace(/^~/, process.env.HOME ?? "");
118
+ return agent.workspace.replace(/^~/, homedir());
117
119
  }
118
120
 
119
- const baseDir = this.config.agent?.baseDir?.replace(/^~/, process.env.HOME ?? "")
120
- ?? `${process.env.HOME}/.hive/agents`;
121
-
122
- return `${baseDir}/${agentId}/workspace`;
121
+ const baseDir = this.config.agent?.baseDir?.replace(/^~/, homedir())
122
+ ?? path.join(homedir(), ".hive", "agents");
123
+
124
+ return path.join(baseDir, agentId, "workspace");
123
125
  }
124
126
  }
@@ -0,0 +1,16 @@
1
+ import { apiRequestTool } from "../../tools/api/api-request.ts";
2
+
3
+ export async function handleHttpRequest(req: Request, addCorsHeaders: (r: Response, req: Request) => Response): Promise<Response> {
4
+ try {
5
+ const body = await req.json().catch(() => ({}));
6
+
7
+ const result = await apiRequestTool.execute(body, { configurable: {} });
8
+
9
+ return addCorsHeaders(Response.json(result), req);
10
+ } catch (error) {
11
+ return addCorsHeaders(Response.json({
12
+ ok: false,
13
+ error: `Request failed: ${(error as Error).message}`,
14
+ }), req);
15
+ }
16
+ }
@@ -19,6 +19,15 @@ let installing = false
19
19
  let installLogs: string[] = []
20
20
  let downloadingModelId: string | null = null
21
21
 
22
+ interface DownloadProgress {
23
+ modelId: string
24
+ downloaded: number
25
+ total: number
26
+ percent: number
27
+ }
28
+
29
+ let currentDownloadProgress: DownloadProgress | null = null
30
+
22
31
  async function getInstalledStatus() {
23
32
  const { LLAMA_CPP_DEFAULT_VER } = await import("../llm-local/detector")
24
33
  const gpu = await detectGPU()
@@ -183,7 +192,12 @@ export async function handleDownloadLLMModel(
183
192
  ;(async () => {
184
193
  try {
185
194
  await downloadModel(modelId, (d, t) => {
186
- // Opcional: Podríamos emitir eventos por WS si quisiéramos progreso real en UI
195
+ currentDownloadProgress = {
196
+ modelId,
197
+ downloaded: d,
198
+ total: t,
199
+ percent: t > 0 ? Math.round((d / t) * 100) : 0,
200
+ }
187
201
  })
188
202
  installLogs.push(`✓ Modelo ${modelId} descargado exitosamente`)
189
203
  } catch (err) {
@@ -191,6 +205,7 @@ export async function handleDownloadLLMModel(
191
205
  installLogs.push(`[error] Error descargando ${modelId}: ${msg}`)
192
206
  } finally {
193
207
  downloadingModelId = null
208
+ currentDownloadProgress = null
194
209
  }
195
210
  })()
196
211
 
@@ -207,7 +222,15 @@ export async function handleStartLocalLLM(
207
222
  } catch { /* ignore if no body */ }
208
223
 
209
224
  const mode = body.mode || "TEXT"
210
- const modelId = body.modelId || getRecommendedModel(mode.toLowerCase() as any)
225
+ // If no modelId specified, prefer the recommended model; fall back to the
226
+ // first downloaded model so the user doesn't get an error when they only
227
+ // downloaded a non-default model.
228
+ const recommended = getRecommendedModel(mode.toLowerCase() as any)
229
+ const allModels = listLocalModels()
230
+ const downloadedModels = allModels.filter(m => m.downloaded)
231
+ const resolvedModelId: string =
232
+ body.modelId ||
233
+ (downloadedModels.find(m => m.id === recommended) ? recommended : (downloadedModels[0]?.id ?? recommended))
211
234
 
212
235
  const status = await getInstalledStatus()
213
236
  if (!status.installed && !status.binaryExists) {
@@ -217,11 +240,18 @@ export async function handleStartLocalLLM(
217
240
  )
218
241
  }
219
242
 
243
+ if (downloadedModels.length === 0) {
244
+ return addCors(
245
+ Response.json({ started: false, reason: "No hay modelos descargados. Descarga un modelo primero desde la sección de modelos." }, { status: 400 }),
246
+ req
247
+ )
248
+ }
249
+
220
250
  try {
221
- installLogs.push(`[llm-server] Iniciando servidor en modo ${mode} con modelo ${modelId}...`)
222
- await llamaManager.start(mode, modelId as any)
251
+ installLogs.push(`[llm-server] Iniciando servidor en modo ${mode} con modelo ${resolvedModelId}...`)
252
+ await llamaManager.start(mode, resolvedModelId as any)
223
253
  installLogs.push(`[llm-server] Servidor ${mode} iniciado correctamente.`)
224
- return addCors(Response.json({ started: true, mode }), req)
254
+ return addCors(Response.json({ started: true, mode, modelId: resolvedModelId }), req)
225
255
  } catch (err) {
226
256
  const msg = err instanceof Error ? err.message : String(err)
227
257
  installLogs.push(`[error] Servidor LLM falló al iniciar: ${msg}`)
@@ -251,6 +281,22 @@ export async function handleStopLocalLLM(
251
281
  * Verifica el estado del LLM local al iniciar el gateway.
252
282
  * NO auto-inicia el servidor ni descarga nada — el usuario debe hacerlo manualmente.
253
283
  */
284
+ export async function handleGetDownloadProgress(
285
+ req: Request,
286
+ addCors: (r: Response, req: Request) => Response
287
+ ): Promise<Response> {
288
+ if (!currentDownloadProgress) {
289
+ return addCors(Response.json({ active: false }), req)
290
+ }
291
+ return addCors(
292
+ Response.json({
293
+ active: true,
294
+ ...currentDownloadProgress,
295
+ }),
296
+ req
297
+ )
298
+ }
299
+
254
300
  export async function initializeLocalLLM() {
255
301
  try {
256
302
  const status = await getInstalledStatus()
@@ -4,6 +4,7 @@ import {
4
4
  loadProviderApiKey, storeProviderApiKey,
5
5
  loadProviderHeaders, storeProviderHeaders,
6
6
  } from "../../storage/crypto"
7
+ import { listLocalModels } from "../llm-local/downloader"
7
8
 
8
9
  export async function handleGetProviders(req: Request, addCorsHeaders: (r: Response, req: Request) => Response): Promise<Response> {
9
10
  const rawProviders = getDb().query(`
@@ -24,20 +25,60 @@ export async function handleGetProviders(req: Request, addCorsHeaders: (r: Respo
24
25
  })
25
26
  }
26
27
 
28
+ // Inyectar modelos locales descargados dinámicamente para local-llama
29
+ try {
30
+ const localModels = listLocalModels()
31
+ const downloadedLocalModels = localModels.filter(m => m.downloaded)
32
+ if (downloadedLocalModels.length > 0) {
33
+ const existingLocalModels = modelsByProvider["local-llama"] || []
34
+ for (const localModel of downloadedLocalModels) {
35
+ const existingIdx = existingLocalModels.findIndex((m: any) => m.id === localModel.id)
36
+ if (existingIdx === -1) {
37
+ existingLocalModels.push({
38
+ id: localModel.id,
39
+ name: localModel.name,
40
+ provider_id: "local-llama",
41
+ model_type: "llm",
42
+ context_window: 16000,
43
+ capabilities: JSON.stringify(["chat", "local"]),
44
+ enabled: true,
45
+ active: true,
46
+ })
47
+ } else {
48
+ // Modelo del seed ya existe pero está descargado: forzar activo
49
+ existingLocalModels[existingIdx] = {
50
+ ...existingLocalModels[existingIdx],
51
+ enabled: true,
52
+ active: true,
53
+ }
54
+ }
55
+ }
56
+ modelsByProvider["local-llama"] = existingLocalModels
57
+ }
58
+ } catch {
59
+ // Ignorar si no se pueden obtener modelos locales
60
+ }
61
+
27
62
  const providers = await Promise.all(rawProviders.map(async (p) => {
28
63
  const apiKey = await loadProviderApiKey(p.id as string)
29
64
  const headers = await loadProviderHeaders(p.id as string)
65
+ const providerModels = modelsByProvider[p.id as string] || []
66
+ // Auto-activar local-llama si tiene modelos descargados dinámicamente
67
+ let active = p.active
68
+ if (p.id === "local-llama" && providerModels.some((m: any) => m.active || m.enabled)) {
69
+ active = true
70
+ }
30
71
  return {
31
72
  id: p.id,
32
73
  name: p.name,
33
74
  base_url: p.base_url,
34
75
  enabled: p.enabled,
35
- active: p.active,
76
+ active,
36
77
  num_ctx: p.num_ctx ?? null,
37
78
  has_api_key: apiKey ? 1 : 0,
38
79
  has_headers: Object.keys(headers).length > 0 ? 1 : 0,
39
80
  masked_api_key: apiKey ? maskApiKey(apiKey) : null,
40
- models: modelsByProvider[p.id as string] || [],
81
+ models: providerModels,
41
82
  }
42
83
  }))
43
84
 
@@ -73,12 +73,13 @@ import { handleGetModels, handleCreateModel, handleToggleModel, handleGetModelsC
73
73
  import { handleGetVoiceProviders, handleGetConfiguredVoiceProviders, handleSaveVoiceProviderKey, handleTestVoice, handleGetChannelVoice, handleUpdateChannelVoice, handleGetVoiceProviderVoices } from "./routes/voice";
74
74
  import { handleGetVisionProviders, handleGetChannelVision, handleUpdateChannelVision, handleOcrImage } from "./routes/multimodal";
75
75
  import { handleGetLocalTTSStatus, handleGetLocalTTSLogs, handleInstallLocalTTS, handleStartLocalTTS, handleStopLocalTTS, handleSpeakLocalTTS, handleGetAvailableModels, handleGetInstalledVoices, handleDownloadModel, handleGetDownloadLogs, initializeLocalTTS } from "./routes/tts-local";
76
- import { handleGetLocalLLMStatus, handleGetLocalLLMLogs, handleInstallLocalLLM, handleStartLocalLLM, handleStopLocalLLM, handleDownloadLLMModel, initializeLocalLLM } from "./routes/llm-local";
76
+ import { handleGetLocalLLMStatus, handleGetLocalLLMLogs, handleInstallLocalLLM, handleStartLocalLLM, handleStopLocalLLM, handleDownloadLLMModel, handleGetDownloadProgress, initializeLocalLLM } from "./routes/llm-local";
77
77
  import { handleCreateMeeting, handleListMeetings, handleGetMeeting, handleAddMeetingSegment, handleStopMeeting } from "./routes/meeting";
78
78
  import { handleGetActivityStats, handleGetSystemStats, handleGetUsageStats, handleSystemReload, handleApiReload, handleGetVersion, handleTriggerUpdate } from "./routes/system";
79
79
  import { handleGetChatHistory, handleGetCanvas, handleGetNotes, handleUpdateNote } from "./routes/chat";
80
80
  import { handleChat as handlePostChat } from "./routes/chat";
81
81
  import { handleGetConfig } from "./routes/config";
82
+ import { handleHttpRequest } from "./routes/http-client";
82
83
  import { handleGetWorkspace, handleUpdateWorkspace, handleValidateWorkspace, handleCreateWorkspace, handleOpenWorkspace } from "./routes/workspace";
83
84
  import { getNarration, expandPath, addCorsHeaders, CORS_ORIGINS } from "./helpers";
84
85
  import { redactConfig } from "./helpers";
@@ -961,6 +962,11 @@ export async function startGateway(config: Config): Promise<void> {
961
962
  }
962
963
  }
963
964
 
965
+ // ── HTTP Client API ────────────────────────────────────────────────
966
+ if ((url.pathname === "/api/http-request" || url.pathname === "/api/http-request/") && req.method === "POST") {
967
+ return await handleHttpRequest(req, addCorsHeaders)
968
+ }
969
+
964
970
  // ── Tasks API ─────────────────────────────────────────────────────
965
971
  if ((url.pathname === "/api/tasks" || url.pathname === "/api/tasks/") && req.method === "GET") {
966
972
  return await handleGetTasks(req, addCorsHeaders)
@@ -1626,6 +1632,9 @@ export async function startGateway(config: Config): Promise<void> {
1626
1632
  if (url.pathname === "/api/llm-local/download-model" && req.method === "POST") {
1627
1633
  return await handleDownloadLLMModel(req, addCorsHeaders)
1628
1634
  }
1635
+ if (url.pathname === "/api/llm-local/download-progress" && req.method === "GET") {
1636
+ return await handleGetDownloadProgress(req, addCorsHeaders)
1637
+ }
1629
1638
 
1630
1639
  // ── Meeting Transcription API ────────────────────────────────────────
1631
1640
  if (url.pathname === "/api/meetings" && req.method === "POST") {
@@ -2729,7 +2738,7 @@ export async function startGateway(config: Config): Promise<void> {
2729
2738
  process.exit(0);
2730
2739
  });
2731
2740
 
2732
- process.on("SIGHUP", async () => {
2741
+ if (process.platform !== "win32") process.on("SIGHUP", async () => {
2733
2742
  log.info("Received SIGHUP, reloading configuration...");
2734
2743
  try {
2735
2744
  const newConfig = await loadConfig();
@@ -4,7 +4,7 @@
4
4
  * Los datos se guardan en $HIVE_HOME/tts/ (por defecto ~/.hive/tts/).
5
5
  */
6
6
 
7
- import { existsSync, mkdirSync, readdirSync, renameSync } from "fs"
7
+ import { existsSync, mkdirSync, readdirSync, renameSync, unlinkSync, rmSync } from "fs"
8
8
  import { join } from "path"
9
9
  import { homedir } from "os"
10
10
  import {
@@ -37,16 +37,24 @@ async function extractTarGz(archivePath: string, destDir: string): Promise<void>
37
37
  stderr: "inherit",
38
38
  }).exited
39
39
  if (code !== 0) throw new Error(`tar falló con código ${code}`)
40
- await Bun.spawn(["rm", "-f", archivePath]).exited
40
+ unlinkSync(archivePath)
41
41
  }
42
42
 
43
43
  async function extractZip(archivePath: string, destDir: string): Promise<void> {
44
- const code = await Bun.spawn(["unzip", "-q", archivePath, "-d", destDir], {
45
- stdout: "inherit",
46
- stderr: "inherit",
47
- }).exited
48
- if (code !== 0) throw new Error(`unzip falló con código ${code}`)
49
- await Bun.spawn(["rm", "-f", archivePath]).exited
44
+ let code: number
45
+ if (process.platform === "win32") {
46
+ code = await Bun.spawn(
47
+ ["powershell", "-Command", `Expand-Archive -Path "${archivePath}" -DestinationPath "${destDir}" -Force`],
48
+ { stdout: "inherit", stderr: "inherit" }
49
+ ).exited
50
+ } else {
51
+ code = await Bun.spawn(["unzip", "-q", archivePath, "-d", destDir], {
52
+ stdout: "inherit",
53
+ stderr: "inherit",
54
+ }).exited
55
+ }
56
+ if (code !== 0) throw new Error(`Extracción ZIP falló con código ${code}`)
57
+ unlinkSync(archivePath)
50
58
  }
51
59
 
52
60
  /**
@@ -86,7 +94,7 @@ export async function runInstall(ttsRoot: string): Promise<void> {
86
94
  for (const entry of readdirSync(tempDir)) {
87
95
  renameSync(join(tempDir, entry), join(BIN_DIR, entry))
88
96
  }
89
- await Bun.spawn(["rm", "-rf", tempDir]).exited
97
+ rmSync(tempDir, { recursive: true, force: true })
90
98
  }
91
99
 
92
100
  if (!existsSync(binaryPath)) {