@johpaz/hive-agents 0.0.38 → 0.0.40
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.
- package/README.md +31 -71
- package/dist/hive.js +4318 -2898
- package/dist/tool-worker.js +2691 -1969
- package/dist/ui/assets/AgentCreateForm-b7xHyfNc.js +1 -0
- package/dist/ui/assets/AgentDetailPage-VHy3M7mI.js +1 -0
- package/dist/ui/assets/AgentNewPage-BnWImQMx.js +1 -0
- package/dist/ui/{dist/assets/AgentsPage-DGNLDXjR.js → assets/AgentsPage-WFy5abqx.js} +5 -5
- package/dist/ui/assets/ApiClientPage-BV7zLIL7.js +3 -0
- package/dist/ui/assets/{CanvasPage-CnMO1FN8.js → CanvasPage-B9zuIrTm.js} +7 -7
- package/dist/ui/assets/ChannelsPage-DiN3NvIM.js +8 -0
- package/dist/ui/{dist/assets/DashboardPage-VyXXp3U1.js → assets/DashboardPage-CHjARjVK.js} +2 -2
- package/dist/ui/assets/{LoginPage-DPj2s2Qq.js → LoginPage-Dwd_XxoU.js} +1 -1
- package/dist/ui/assets/LogsPage-DW8Nnqe6.js +1 -0
- package/dist/ui/assets/MeetingPage-D3bkiKYt.js +1 -0
- package/dist/ui/assets/{NotFound-BMeQSGcG.js → NotFound-BIUDlIXU.js} +1 -1
- package/dist/ui/assets/ProvidersPage-UqsDAxPQ.js +1 -0
- package/dist/ui/{dist/assets/RecoverPage-B-hDZUM2.js → assets/RecoverPage-8hTjr_JU.js} +1 -1
- package/dist/ui/assets/SettingsPage-DNa0jOkA.js +9 -0
- package/dist/ui/assets/SetupPage-Bdm2irQG.js +1 -0
- package/dist/ui/assets/WebChatPage-DElJg6P2.js +16 -0
- package/dist/ui/assets/accordion-CnLzKNHK.js +1 -0
- package/dist/ui/{dist/assets/alert-Bq6awLlW.js → assets/alert-DylmSCDJ.js} +1 -1
- package/dist/ui/{dist/assets/alert-dialog-DQvltYmf.js → assets/alert-dialog-DNNWN_SI.js} +1 -1
- package/dist/ui/assets/{badge-DXUDdTed.js → badge-ChENFgkC.js} +1 -1
- package/dist/ui/assets/bell-8BqRYmzf.js +1 -0
- package/dist/ui/assets/chevron-down-DIosfU_U.js +1 -0
- package/dist/ui/assets/chevron-up-CI-W21Fy.js +1 -0
- package/dist/ui/assets/circle-x-DjLkFDO8.js +1 -0
- package/dist/ui/assets/copy-Bu5d7C-0.js +1 -0
- package/dist/ui/{dist/assets/dialog-bI9jImCS.js → assets/dialog-BJ-npIv8.js} +1 -1
- package/dist/ui/{dist/assets/dropdown-menu-BK-CO3Od.js → assets/dropdown-menu-DDiaHg5y.js} +1 -1
- package/dist/ui/assets/{es-Cg8zdT52.js → es-ecSKCyB6.js} +1 -1
- package/dist/ui/assets/index-CawKP29y.js +116 -0
- package/dist/ui/assets/index-DIcsEkyd.css +2 -0
- package/dist/ui/{dist/assets/label-CrH0Jj3v.js → assets/label-Bi6udtSd.js} +1 -1
- package/dist/ui/assets/progress-D5c-Eilm.js +1 -0
- package/dist/ui/assets/scroll-area-CihOx0cb.js +1 -0
- package/dist/ui/assets/search-DzDptO9s.js +1 -0
- package/dist/ui/assets/select-BQCOjM2j.js +1 -0
- package/dist/ui/assets/send-BPk9XbIq.js +1 -0
- package/dist/ui/assets/shield-CxhcUT39.js +1 -0
- package/dist/ui/assets/{slider-CsiUDxc3.js → slider-bcUiUfx0.js} +1 -1
- package/dist/ui/assets/switch-BR30E4ej.js +1 -0
- package/dist/ui/assets/table-B3aGEaVp.js +1 -0
- package/dist/ui/assets/tabs-LQidMKRS.js +1 -0
- package/dist/ui/assets/textarea-B6Z1Zc6W.js +1 -0
- package/dist/ui/assets/useProviders-Dlizq_8q.js +1 -0
- package/dist/ui/{dist/assets/vendor-radix-cw1bQaVC.js → assets/vendor-radix-D6rA7xKY.js} +4 -4
- package/dist/ui/assets/{vendor-react-D4s9E-zj.js → vendor-react-BU5iQU4f.js} +1 -1
- package/dist/ui/dist/assets/AgentCreateForm-b7xHyfNc.js +1 -0
- package/dist/ui/dist/assets/AgentDetailPage-VHy3M7mI.js +1 -0
- package/dist/ui/dist/assets/AgentNewPage-BnWImQMx.js +1 -0
- package/dist/ui/{assets/AgentsPage-DGNLDXjR.js → dist/assets/AgentsPage-WFy5abqx.js} +5 -5
- package/dist/ui/dist/assets/ApiClientPage-BV7zLIL7.js +3 -0
- package/dist/ui/dist/assets/{CanvasPage-CnMO1FN8.js → CanvasPage-B9zuIrTm.js} +7 -7
- package/dist/ui/dist/assets/ChannelsPage-DiN3NvIM.js +8 -0
- package/dist/ui/{assets/DashboardPage-VyXXp3U1.js → dist/assets/DashboardPage-CHjARjVK.js} +2 -2
- package/dist/ui/dist/assets/{LoginPage-DPj2s2Qq.js → LoginPage-Dwd_XxoU.js} +1 -1
- package/dist/ui/dist/assets/LogsPage-DW8Nnqe6.js +1 -0
- package/dist/ui/dist/assets/MeetingPage-D3bkiKYt.js +1 -0
- package/dist/ui/dist/assets/{NotFound-BMeQSGcG.js → NotFound-BIUDlIXU.js} +1 -1
- package/dist/ui/dist/assets/ProvidersPage-UqsDAxPQ.js +1 -0
- package/dist/ui/{assets/RecoverPage-B-hDZUM2.js → dist/assets/RecoverPage-8hTjr_JU.js} +1 -1
- package/dist/ui/dist/assets/SettingsPage-DNa0jOkA.js +9 -0
- package/dist/ui/dist/assets/SetupPage-Bdm2irQG.js +1 -0
- package/dist/ui/dist/assets/WebChatPage-DElJg6P2.js +16 -0
- package/dist/ui/dist/assets/accordion-CnLzKNHK.js +1 -0
- package/dist/ui/{assets/alert-Bq6awLlW.js → dist/assets/alert-DylmSCDJ.js} +1 -1
- package/dist/ui/{assets/alert-dialog-DQvltYmf.js → dist/assets/alert-dialog-DNNWN_SI.js} +1 -1
- package/dist/ui/dist/assets/{badge-DXUDdTed.js → badge-ChENFgkC.js} +1 -1
- package/dist/ui/dist/assets/bell-8BqRYmzf.js +1 -0
- package/dist/ui/dist/assets/chevron-down-DIosfU_U.js +1 -0
- package/dist/ui/dist/assets/chevron-up-CI-W21Fy.js +1 -0
- package/dist/ui/dist/assets/circle-x-DjLkFDO8.js +1 -0
- package/dist/ui/dist/assets/copy-Bu5d7C-0.js +1 -0
- package/dist/ui/{assets/dialog-bI9jImCS.js → dist/assets/dialog-BJ-npIv8.js} +1 -1
- package/dist/ui/{assets/dropdown-menu-BK-CO3Od.js → dist/assets/dropdown-menu-DDiaHg5y.js} +1 -1
- package/dist/ui/dist/assets/{es-Cg8zdT52.js → es-ecSKCyB6.js} +1 -1
- package/dist/ui/dist/assets/index-CawKP29y.js +116 -0
- package/dist/ui/dist/assets/index-DIcsEkyd.css +2 -0
- package/dist/ui/{assets/label-CrH0Jj3v.js → dist/assets/label-Bi6udtSd.js} +1 -1
- package/dist/ui/dist/assets/progress-D5c-Eilm.js +1 -0
- package/dist/ui/dist/assets/scroll-area-CihOx0cb.js +1 -0
- package/dist/ui/dist/assets/search-DzDptO9s.js +1 -0
- package/dist/ui/dist/assets/select-BQCOjM2j.js +1 -0
- package/dist/ui/dist/assets/send-BPk9XbIq.js +1 -0
- package/dist/ui/dist/assets/shield-CxhcUT39.js +1 -0
- package/dist/ui/dist/assets/{slider-CsiUDxc3.js → slider-bcUiUfx0.js} +1 -1
- package/dist/ui/dist/assets/switch-BR30E4ej.js +1 -0
- package/dist/ui/dist/assets/table-B3aGEaVp.js +1 -0
- package/dist/ui/dist/assets/tabs-LQidMKRS.js +1 -0
- package/dist/ui/dist/assets/textarea-B6Z1Zc6W.js +1 -0
- package/dist/ui/dist/assets/useProviders-Dlizq_8q.js +1 -0
- package/dist/ui/{assets/vendor-radix-cw1bQaVC.js → dist/assets/vendor-radix-D6rA7xKY.js} +4 -4
- package/dist/ui/dist/assets/{vendor-react-D4s9E-zj.js → vendor-react-BU5iQU4f.js} +1 -1
- package/dist/ui/dist/index.html +6 -6
- package/dist/ui/index.html +6 -6
- package/package.json +1 -1
- package/packages/cli/src/adapters/binary.ts +8 -4
- package/packages/cli/src/adapters/bun-global.ts +5 -1
- package/packages/cli/src/adapters/config.ts +4 -3
- package/packages/cli/src/adapters/docker.ts +2 -1
- package/packages/cli/src/commands/gateway.ts +123 -9
- package/packages/cli/src/commands/logs.ts +2 -1
- package/packages/cli/src/commands/onboard.ts +27 -1
- package/packages/cli/src/commands/sessions.ts +2 -1
- package/packages/cli/src/commands/skills.ts +2 -1
- package/packages/core/src/agent/agent-loop.ts +104 -2
- package/packages/core/src/agent/context-compiler.ts +6 -0
- package/packages/core/src/agent/llm-client.ts +6 -0
- package/packages/core/src/agent/llm-providers/anthropic.ts +23 -8
- package/packages/core/src/agent/llm-providers/hiveagents.ts +248 -0
- package/packages/core/src/agent/llm-providers/interface.ts +7 -1
- package/packages/core/src/agent/llm-providers/minimax.ts +13 -0
- package/packages/core/src/agent/llm-providers/openai-compat-base.ts +49 -25
- package/packages/core/src/agent/llm-providers/opencode-go.ts +9 -0
- package/packages/core/src/agent/providers/index.ts +3 -2
- package/packages/core/src/agent/stuck-loop.ts +90 -14
- package/packages/core/src/channels/base.ts +7 -1
- package/packages/core/src/channels/whatsapp.ts +13 -1
- package/packages/core/src/config/loader.ts +8 -8
- package/packages/core/src/gateway/helpers/path.ts +2 -1
- package/packages/core/src/gateway/initializer.ts +4 -4
- package/packages/core/src/gateway/llm-local/downloader.ts +130 -11
- package/packages/core/src/gateway/llm-local/index.ts +2 -0
- package/packages/core/src/gateway/llm-local/models.ts +4 -3
- package/packages/core/src/gateway/router.ts +7 -5
- package/packages/core/src/gateway/routes/http-client.ts +16 -0
- package/packages/core/src/gateway/routes/llm-local.ts +51 -5
- package/packages/core/src/gateway/routes/providers.ts +99 -2
- package/packages/core/src/gateway/server.ts +131 -57
- package/packages/core/src/gateway/slash-commands.ts +7 -1
- package/packages/core/src/gateway/tts/src/install.ts +17 -9
- package/packages/core/src/storage/crypto.ts +152 -20
- package/packages/core/src/storage/migrate.ts +51 -18
- package/packages/core/src/storage/onboarding.ts +28 -0
- package/packages/core/src/storage/seed.ts +52 -2
- package/packages/core/src/tool-runtime/index.ts +22 -1
- package/packages/core/src/tools/api/api-request.ts +174 -0
- package/packages/core/src/tools/api/index.ts +16 -0
- package/packages/core/src/tools/index.ts +12 -0
- package/packages/core/src/tools/web/browser-click.ts +2 -2
- package/packages/core/src/tools/web/browser-extract.ts +22 -6
- package/packages/core/src/tools/web/browser-navigate.ts +34 -18
- package/packages/core/src/tools/web/browser-screenshot.ts +40 -8
- package/packages/core/src/tools/web/browser-script.ts +2 -2
- package/packages/core/src/tools/web/browser-service.ts +295 -341
- package/packages/core/src/tools/web/browser-type.ts +5 -10
- package/packages/core/src/tools/web/browser-wait.ts +2 -2
- package/packages/core/src/tools/web/index.ts +1 -1
- package/packages/core/src/utils/logger.ts +2 -1
- package/packages/mcp/src/manager.ts +2 -1
- package/packages/skills/src/bundled/api/api_client/SKILL.md +132 -0
- package/packages/skills/src/bundled-data.generated.ts +1191 -1134
- package/packages/skills/src/loader.ts +2 -1
- package/dist/ui/assets/AgentCreateForm-0oFbN3gj.js +0 -1
- package/dist/ui/assets/AgentDetailPage-BJ4L2fNJ.js +0 -1
- package/dist/ui/assets/AgentNewPage-B3n0LUck.js +0 -1
- package/dist/ui/assets/ChannelsPage-fbF8K4MR.js +0 -8
- package/dist/ui/assets/LogsPage-B2lY9maY.js +0 -1
- package/dist/ui/assets/MeetingPage-2ky_hKiG.js +0 -1
- package/dist/ui/assets/ProvidersPage-CEyUM2tD.js +0 -1
- package/dist/ui/assets/SettingsPage-eO0i3g8p.js +0 -9
- package/dist/ui/assets/SetupPage-ByYqTELb.js +0 -1
- package/dist/ui/assets/WebChatPage-BuGT2AL0.js +0 -16
- package/dist/ui/assets/accordion-C5d5Rm5z.js +0 -1
- package/dist/ui/assets/chevron-up-BYhk0K2J.js +0 -1
- package/dist/ui/assets/globe-DeCQTCDJ.js +0 -1
- package/dist/ui/assets/index-B2fCYtTS.css +0 -2
- package/dist/ui/assets/index-CQ7fn00w.js +0 -116
- package/dist/ui/assets/progress-BherYzY6.js +0 -1
- package/dist/ui/assets/scroll-area-DkeyX32e.js +0 -1
- package/dist/ui/assets/send-B0H5SEIE.js +0 -1
- package/dist/ui/assets/switch-BDwN8RYV.js +0 -1
- package/dist/ui/assets/table-CSc8ubon.js +0 -1
- package/dist/ui/assets/textarea-CXgXWKrT.js +0 -1
- package/dist/ui/assets/useProviders-CnlC_qCS.js +0 -1
- package/dist/ui/dist/assets/AgentCreateForm-0oFbN3gj.js +0 -1
- package/dist/ui/dist/assets/AgentDetailPage-BJ4L2fNJ.js +0 -1
- package/dist/ui/dist/assets/AgentNewPage-B3n0LUck.js +0 -1
- package/dist/ui/dist/assets/ChannelsPage-fbF8K4MR.js +0 -8
- package/dist/ui/dist/assets/LogsPage-B2lY9maY.js +0 -1
- package/dist/ui/dist/assets/MeetingPage-2ky_hKiG.js +0 -1
- package/dist/ui/dist/assets/ProvidersPage-CEyUM2tD.js +0 -1
- package/dist/ui/dist/assets/SettingsPage-eO0i3g8p.js +0 -9
- package/dist/ui/dist/assets/SetupPage-ByYqTELb.js +0 -1
- package/dist/ui/dist/assets/WebChatPage-BuGT2AL0.js +0 -16
- package/dist/ui/dist/assets/accordion-C5d5Rm5z.js +0 -1
- package/dist/ui/dist/assets/chevron-up-BYhk0K2J.js +0 -1
- package/dist/ui/dist/assets/globe-DeCQTCDJ.js +0 -1
- package/dist/ui/dist/assets/index-B2fCYtTS.css +0 -2
- package/dist/ui/dist/assets/index-CQ7fn00w.js +0 -116
- package/dist/ui/dist/assets/progress-BherYzY6.js +0 -1
- package/dist/ui/dist/assets/scroll-area-DkeyX32e.js +0 -1
- package/dist/ui/dist/assets/send-B0H5SEIE.js +0 -1
- package/dist/ui/dist/assets/switch-BDwN8RYV.js +0 -1
- package/dist/ui/dist/assets/table-CSc8ubon.js +0 -1
- package/dist/ui/dist/assets/textarea-CXgXWKrT.js +0 -1
- package/dist/ui/dist/assets/useProviders-CnlC_qCS.js +0 -1
- /package/dist/ui/assets/{card-CNf6BS2e.js → card-DFKnZ6ky.js} +0 -0
- /package/dist/ui/assets/{circle-alert-CyHDwUj8.js → circle-alert-KuAm2FWh.js} +0 -0
- /package/dist/ui/assets/{circle-check-Bb54Ebmu.js → circle-check-6Ard1-2z.js} +0 -0
- /package/dist/ui/assets/{cpu-Cdgc_B1K.js → cpu-KDy6-FAI.js} +0 -0
- /package/dist/ui/assets/{download-C3ifGMjJ.js → download-Cjbk4Rek.js} +0 -0
- /package/dist/ui/assets/{external-link-BvxYeTP1.js → external-link-6sTlRDUR.js} +0 -0
- /package/dist/ui/assets/{eye-DqNTU_GD.js → eye-Df8o0tkC.js} +0 -0
- /package/dist/ui/assets/{file-text-BT_9S9SM.js → file-text-lnxnjBp0.js} +0 -0
- /package/dist/ui/assets/{folder-open-BhH8y9ac.js → folder-open-DJBLDFjv.js} +0 -0
- /package/dist/ui/assets/{format-GVHeOyWI.js → format-BwdV8bB5.js} +0 -0
- /package/dist/ui/assets/{gateway-url-COCbW0IR.js → gateway-url-DwzPmoc8.js} +0 -0
- /package/dist/ui/assets/{gauge-D_TMa4i9.js → gauge-B8Tj43rC.js} +0 -0
- /package/dist/ui/assets/{hexagon-DsGOUl-H.js → hexagon-6L79pgVK.js} +0 -0
- /package/dist/ui/assets/{history-BSG-Ypqf.js → history-CAF_R34_.js} +0 -0
- /package/dist/ui/assets/{info-NwLoa2Mj.js → info-WjromB4Y.js} +0 -0
- /package/dist/ui/assets/{key-3EP0dhkT.js → key-DyKOoQh5.js} +0 -0
- /package/dist/ui/assets/{loader-circle-CZNax6kS.js → loader-circle-BmBOgYze.js} +0 -0
- /package/dist/ui/assets/{lock-Ei1_J-Nq.js → lock-BS6OLXPv.js} +0 -0
- /package/dist/ui/assets/{pause-BUqah9Bi.js → pause-VqeUmp2Z.js} +0 -0
- /package/dist/ui/assets/{play-NcZ4swwL.js → play-zJpWuhrr.js} +0 -0
- /package/dist/ui/assets/{plus-CX1xyhp5.js → plus-BZQX26Dr.js} +0 -0
- /package/dist/ui/assets/{refresh-cw-DaYdjQFk.js → refresh-cw-CCzDCAuz.js} +0 -0
- /package/dist/ui/assets/{save-CUdYyHNy.js → save-hUmZhceG.js} +0 -0
- /package/dist/ui/assets/{settings-Ds4SqD8s.js → settings-BGfrZ_zM.js} +0 -0
- /package/dist/ui/assets/{sparkles-yUEb-7oH.js → sparkles-BhwlS1pc.js} +0 -0
- /package/dist/ui/assets/{square-BD81nFtN.js → square-DMNWw4Hi.js} +0 -0
- /package/dist/ui/assets/{terminal-DN38Q456.js → terminal--7G943As.js} +0 -0
- /package/dist/ui/assets/{trash-2-CNjMkoq6.js → trash-2-xD2o4SgX.js} +0 -0
- /package/dist/ui/assets/{triangle-alert-C9Y8Ub4X.js → triangle-alert-pVIJGjga.js} +0 -0
- /package/dist/ui/assets/{vendor-router-C9pIYwbJ.js → vendor-router-gqiZ7xhx.js} +0 -0
- /package/dist/ui/assets/{volume-2-CeSXNDv4.js → volume-2-BekVQl6P.js} +0 -0
- /package/dist/ui/assets/{zap-hlXjpSeA.js → zap-B4RaNNO5.js} +0 -0
- /package/dist/ui/dist/assets/{card-CNf6BS2e.js → card-DFKnZ6ky.js} +0 -0
- /package/dist/ui/dist/assets/{circle-alert-CyHDwUj8.js → circle-alert-KuAm2FWh.js} +0 -0
- /package/dist/ui/dist/assets/{circle-check-Bb54Ebmu.js → circle-check-6Ard1-2z.js} +0 -0
- /package/dist/ui/dist/assets/{cpu-Cdgc_B1K.js → cpu-KDy6-FAI.js} +0 -0
- /package/dist/ui/dist/assets/{download-C3ifGMjJ.js → download-Cjbk4Rek.js} +0 -0
- /package/dist/ui/dist/assets/{external-link-BvxYeTP1.js → external-link-6sTlRDUR.js} +0 -0
- /package/dist/ui/dist/assets/{eye-DqNTU_GD.js → eye-Df8o0tkC.js} +0 -0
- /package/dist/ui/dist/assets/{file-text-BT_9S9SM.js → file-text-lnxnjBp0.js} +0 -0
- /package/dist/ui/dist/assets/{folder-open-BhH8y9ac.js → folder-open-DJBLDFjv.js} +0 -0
- /package/dist/ui/dist/assets/{format-GVHeOyWI.js → format-BwdV8bB5.js} +0 -0
- /package/dist/ui/dist/assets/{gateway-url-COCbW0IR.js → gateway-url-DwzPmoc8.js} +0 -0
- /package/dist/ui/dist/assets/{gauge-D_TMa4i9.js → gauge-B8Tj43rC.js} +0 -0
- /package/dist/ui/dist/assets/{hexagon-DsGOUl-H.js → hexagon-6L79pgVK.js} +0 -0
- /package/dist/ui/dist/assets/{history-BSG-Ypqf.js → history-CAF_R34_.js} +0 -0
- /package/dist/ui/dist/assets/{info-NwLoa2Mj.js → info-WjromB4Y.js} +0 -0
- /package/dist/ui/dist/assets/{key-3EP0dhkT.js → key-DyKOoQh5.js} +0 -0
- /package/dist/ui/dist/assets/{loader-circle-CZNax6kS.js → loader-circle-BmBOgYze.js} +0 -0
- /package/dist/ui/dist/assets/{lock-Ei1_J-Nq.js → lock-BS6OLXPv.js} +0 -0
- /package/dist/ui/dist/assets/{pause-BUqah9Bi.js → pause-VqeUmp2Z.js} +0 -0
- /package/dist/ui/dist/assets/{play-NcZ4swwL.js → play-zJpWuhrr.js} +0 -0
- /package/dist/ui/dist/assets/{plus-CX1xyhp5.js → plus-BZQX26Dr.js} +0 -0
- /package/dist/ui/dist/assets/{refresh-cw-DaYdjQFk.js → refresh-cw-CCzDCAuz.js} +0 -0
- /package/dist/ui/dist/assets/{save-CUdYyHNy.js → save-hUmZhceG.js} +0 -0
- /package/dist/ui/dist/assets/{settings-Ds4SqD8s.js → settings-BGfrZ_zM.js} +0 -0
- /package/dist/ui/dist/assets/{sparkles-yUEb-7oH.js → sparkles-BhwlS1pc.js} +0 -0
- /package/dist/ui/dist/assets/{square-BD81nFtN.js → square-DMNWw4Hi.js} +0 -0
- /package/dist/ui/dist/assets/{terminal-DN38Q456.js → terminal--7G943As.js} +0 -0
- /package/dist/ui/dist/assets/{trash-2-CNjMkoq6.js → trash-2-xD2o4SgX.js} +0 -0
- /package/dist/ui/dist/assets/{triangle-alert-C9Y8Ub4X.js → triangle-alert-pVIJGjga.js} +0 -0
- /package/dist/ui/dist/assets/{vendor-router-C9pIYwbJ.js → vendor-router-gqiZ7xhx.js} +0 -0
- /package/dist/ui/dist/assets/{volume-2-CeSXNDv4.js → volume-2-BekVQl6P.js} +0 -0
- /package/dist/ui/dist/assets/{zap-hlXjpSeA.js → zap-B4RaNNO5.js} +0 -0
|
@@ -1,234 +1,166 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* BrowserService —
|
|
2
|
+
* BrowserService — Browser automation via agent-browser CLI (Rust).
|
|
3
3
|
*
|
|
4
4
|
* Flujo:
|
|
5
|
-
* 1. Detecta
|
|
6
|
-
* 2.
|
|
7
|
-
* 3.
|
|
8
|
-
* 4.
|
|
5
|
+
* 1. Detecta si agent-browser está instalado (lazy install en primer uso).
|
|
6
|
+
* 2. Ejecuta comandos via CLI con --json para output estructurado.
|
|
7
|
+
* 3. El daemon de agent-browser maneja Chrome internamente via CDP.
|
|
8
|
+
* 4. Las herramientas de browser usan AgentBrowserView (API compatible con CDPClient).
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import { logger } from "../../utils/logger.ts";
|
|
12
12
|
import type { Config } from "../../config/loader.ts";
|
|
13
|
-
import { existsSync,
|
|
14
|
-
import { tmpdir } from "os";
|
|
13
|
+
import { existsSync, mkdirSync, readFileSync, rmSync } from "fs";
|
|
14
|
+
import { homedir, tmpdir } from "os";
|
|
15
|
+
import { dirname, join, resolve } from "path";
|
|
15
16
|
|
|
16
17
|
const log = logger.child("browser-service");
|
|
17
18
|
|
|
18
|
-
// ───
|
|
19
|
-
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
"
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
darwin: [
|
|
39
|
-
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
|
|
40
|
-
"/Applications/Brave Browser.app/Contents/MacOS/Brave Browser",
|
|
41
|
-
"/Applications/Chromium.app/Contents/MacOS/Chromium",
|
|
42
|
-
"/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge",
|
|
43
|
-
`${process.env.HOME}/Applications/Google Chrome.app/Contents/MacOS/Google Chrome`,
|
|
44
|
-
],
|
|
45
|
-
win32: [
|
|
46
|
-
`${process.env.LOCALAPPDATA}\\Google\\Chrome\\Application\\chrome.exe`,
|
|
47
|
-
`${process.env.PROGRAMFILES}\\Google\\Chrome\\Application\\chrome.exe`,
|
|
48
|
-
`${process.env["PROGRAMFILES(X86)"]}\\Google\\Chrome\\Application\\chrome.exe`,
|
|
49
|
-
`${process.env.LOCALAPPDATA}\\BraveSoftware\\Brave-Browser\\Application\\brave.exe`,
|
|
50
|
-
`${process.env.PROGRAMFILES}\\Microsoft\\Edge\\Application\\msedge.exe`,
|
|
51
|
-
],
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
export type LaunchSpec =
|
|
55
|
-
| { kind: "native"; path: string }
|
|
56
|
-
| { kind: "flatpak"; appId: string };
|
|
57
|
-
|
|
58
|
-
export function detectBrowser(): LaunchSpec | undefined {
|
|
59
|
-
if (process.env.BUN_CHROME_PATH && existsSync(process.env.BUN_CHROME_PATH)) {
|
|
60
|
-
return { kind: "native", path: process.env.BUN_CHROME_PATH };
|
|
61
|
-
}
|
|
62
|
-
const platform = process.platform as string;
|
|
63
|
-
const natives = (NATIVE_PATHS[platform] ?? NATIVE_PATHS.linux).filter(Boolean);
|
|
64
|
-
const found = natives.find(p => existsSync(p));
|
|
65
|
-
if (found) return { kind: "native", path: found };
|
|
66
|
-
|
|
67
|
-
if (platform === "linux" && existsSync("/usr/bin/flatpak")) {
|
|
68
|
-
for (const appId of FLATPAK_BROWSERS) {
|
|
69
|
-
const r = Bun.spawnSync(["flatpak", "info", appId], { stdout: "pipe", stderr: "pipe" });
|
|
70
|
-
if (r.exitCode === 0) return { kind: "flatpak", appId };
|
|
71
|
-
}
|
|
19
|
+
// ─── Instalación lazy de agent-browser ────────────────────────────────────────
|
|
20
|
+
|
|
21
|
+
const HIVE_DIR = join(homedir(), ".hive");
|
|
22
|
+
const AGENT_BROWSER_DIR = join(HIVE_DIR, "agent-browser");
|
|
23
|
+
const AGENT_BROWSER_PKG_JSON = join(AGENT_BROWSER_DIR, "package.json");
|
|
24
|
+
const DEFAULT_SESSION_NAME = "hive";
|
|
25
|
+
|
|
26
|
+
/** Check if agent-browser is installed in the cache dir by running --version */
|
|
27
|
+
async function isAgentBrowserInstalled(): Promise<boolean> {
|
|
28
|
+
if (!existsSync(AGENT_BROWSER_PKG_JSON)) return false;
|
|
29
|
+
try {
|
|
30
|
+
const proc = Bun.spawn(["bun", "run", "agent-browser", "--version"], {
|
|
31
|
+
cwd: AGENT_BROWSER_DIR,
|
|
32
|
+
stdout: "pipe",
|
|
33
|
+
stderr: "pipe",
|
|
34
|
+
});
|
|
35
|
+
const exitCode = await proc.exited;
|
|
36
|
+
return exitCode === 0;
|
|
37
|
+
} catch {
|
|
38
|
+
return false;
|
|
72
39
|
}
|
|
73
40
|
}
|
|
74
41
|
|
|
75
|
-
|
|
42
|
+
async function installAgentBrowser(): Promise<void> {
|
|
43
|
+
mkdirSync(AGENT_BROWSER_DIR, { recursive: true });
|
|
76
44
|
|
|
77
|
-
|
|
78
|
-
const
|
|
45
|
+
// Create minimal package.json
|
|
46
|
+
const pkg = { name: "hive-agent-browser", version: "1.0.0", dependencies: {} };
|
|
47
|
+
await Bun.write(AGENT_BROWSER_PKG_JSON, JSON.stringify(pkg, null, 2));
|
|
79
48
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
private _focusedSelector: string | null = null;
|
|
49
|
+
log.info("📦 Instalando agent-browser (primera vez, ~75MB)...");
|
|
50
|
+
const proc = Bun.spawn(["bun", "add", "agent-browser@latest"], {
|
|
51
|
+
cwd: AGENT_BROWSER_DIR,
|
|
52
|
+
stdout: "pipe",
|
|
53
|
+
stderr: "pipe",
|
|
54
|
+
});
|
|
87
55
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
get loading(): boolean { return false; }
|
|
91
|
-
get isConnected(): boolean { return this.ws !== null && this.ws.readyState === WebSocket.OPEN; }
|
|
92
|
-
|
|
93
|
-
// ── Launch ──────────────────────────────────────────────────────────────────
|
|
94
|
-
|
|
95
|
-
async launch(spec: LaunchSpec): Promise<void> {
|
|
96
|
-
const commonArgs = [
|
|
97
|
-
`--remote-debugging-port=${CDP_PORT}`,
|
|
98
|
-
"--no-first-run",
|
|
99
|
-
"--no-default-browser-check",
|
|
100
|
-
"--disable-popup-blocking",
|
|
101
|
-
`--user-data-dir=${tmpdir()}/hive-browser-profile`,
|
|
102
|
-
"about:blank",
|
|
103
|
-
];
|
|
104
|
-
|
|
105
|
-
if (spec.kind === "native") {
|
|
106
|
-
this.proc = Bun.spawn([spec.path, ...commonArgs], {
|
|
107
|
-
stdout: "ignore",
|
|
108
|
-
stderr: "ignore",
|
|
109
|
-
});
|
|
110
|
-
log.info(`Lanzando browser nativo: ${spec.path} (PID ${this.proc.pid})`);
|
|
111
|
-
} else {
|
|
112
|
-
this.proc = Bun.spawn(["flatpak", "run", spec.appId, ...commonArgs], {
|
|
113
|
-
stdout: "ignore",
|
|
114
|
-
stderr: "ignore",
|
|
115
|
-
});
|
|
116
|
-
log.info(`Lanzando Flatpak ${spec.appId} (PID ${this.proc.pid})`);
|
|
117
|
-
}
|
|
56
|
+
const exitCode = await proc.exited;
|
|
57
|
+
const stderr = await new Response(proc.stderr).text();
|
|
118
58
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
allInstances.add(this);
|
|
59
|
+
if (exitCode !== 0) {
|
|
60
|
+
throw new Error(`bun add agent-browser failed: ${stderr}`);
|
|
122
61
|
}
|
|
123
62
|
|
|
124
|
-
|
|
63
|
+
log.info("✅ agent-browser instalado.");
|
|
64
|
+
}
|
|
125
65
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
throw new Error(`CDP no respondió en ${timeout}ms en puerto ${CDP_PORT}`);
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
private async _connect(): Promise<void> {
|
|
139
|
-
const r = await fetch(`http://localhost:${CDP_PORT}/json`);
|
|
140
|
-
const targets = await r.json() as Array<{ type: string; webSocketDebuggerUrl: string }>;
|
|
141
|
-
const target = targets.find(t => t.type === "page") ?? targets[0];
|
|
142
|
-
if (!target?.webSocketDebuggerUrl) throw new Error("No hay target CDP disponible");
|
|
143
|
-
|
|
144
|
-
await new Promise<void>((resolve, reject) => {
|
|
145
|
-
const ws = new WebSocket(target.webSocketDebuggerUrl);
|
|
146
|
-
ws.onopen = () => {
|
|
147
|
-
this.ws = ws;
|
|
148
|
-
ws.onmessage = (ev: MessageEvent) => {
|
|
149
|
-
const msg = JSON.parse(ev.data as string) as {
|
|
150
|
-
id?: number;
|
|
151
|
-
result?: unknown;
|
|
152
|
-
error?: { message: string };
|
|
153
|
-
};
|
|
154
|
-
if (msg.id !== undefined) {
|
|
155
|
-
const p = this.pending.get(msg.id);
|
|
156
|
-
if (p) {
|
|
157
|
-
this.pending.delete(msg.id);
|
|
158
|
-
if (msg.error) p.reject(new Error(msg.error.message));
|
|
159
|
-
else p.resolve(msg.result ?? {});
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
};
|
|
163
|
-
resolve();
|
|
164
|
-
};
|
|
165
|
-
ws.onerror = () => reject(new Error("WebSocket CDP falló al conectar"));
|
|
166
|
-
ws.onclose = () => {
|
|
167
|
-
// Rechazar todos los pendientes
|
|
168
|
-
for (const p of this.pending.values()) p.reject(new Error("CDP WebSocket cerrado"));
|
|
169
|
-
this.pending.clear();
|
|
170
|
-
};
|
|
171
|
-
});
|
|
66
|
+
/** Run agent-browser CLI from the cache directory — cross-platform via bun run */
|
|
67
|
+
async function runAgentBrowser(
|
|
68
|
+
args: string[]
|
|
69
|
+
): Promise<{ success: boolean; data?: any; error?: string }> {
|
|
70
|
+
const proc = Bun.spawn(["bun", "run", "agent-browser", ...args], {
|
|
71
|
+
cwd: AGENT_BROWSER_DIR,
|
|
72
|
+
stdout: "pipe",
|
|
73
|
+
stderr: "pipe",
|
|
74
|
+
});
|
|
172
75
|
|
|
173
|
-
|
|
174
|
-
|
|
76
|
+
const stdout = await new Response(proc.stdout).text();
|
|
77
|
+
const stderr = await new Response(proc.stderr).text();
|
|
78
|
+
const exitCode = await proc.exited;
|
|
79
|
+
|
|
80
|
+
if (exitCode !== 0 && !stdout.trim()) {
|
|
81
|
+
throw new Error(stderr || `agent-browser ${args[0]} failed`);
|
|
175
82
|
}
|
|
176
83
|
|
|
177
|
-
|
|
84
|
+
try {
|
|
85
|
+
const result = JSON.parse(stdout.trim().split("\n").pop() || "{}");
|
|
86
|
+
return result;
|
|
87
|
+
} catch {
|
|
88
|
+
return { success: true, data: { raw: stdout.trim() } };
|
|
89
|
+
}
|
|
90
|
+
}
|
|
178
91
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
92
|
+
async function ensureChromeInstalled(): Promise<void> {
|
|
93
|
+
log.info("🔍 Verificando Chrome para agent-browser...");
|
|
94
|
+
const res = await runAgentBrowser(["open", "about:blank", "--session", DEFAULT_SESSION_NAME, "--json"]);
|
|
95
|
+
|
|
96
|
+
if (!res.success) {
|
|
97
|
+
const err = res.error || "";
|
|
98
|
+
// Chrome not installed — trigger install
|
|
99
|
+
if (err.includes("not found") || err.includes("install")) {
|
|
100
|
+
log.info("📥 Descargando Chrome (agent-browser install)...");
|
|
101
|
+
const installProc = Bun.spawn(["bun", "run", "agent-browser", "install"], {
|
|
102
|
+
cwd: AGENT_BROWSER_DIR,
|
|
103
|
+
stdout: "pipe",
|
|
104
|
+
stderr: "pipe",
|
|
186
105
|
});
|
|
187
|
-
|
|
188
|
-
|
|
106
|
+
const installExit = await installProc.exited;
|
|
107
|
+
if (installExit !== 0) {
|
|
108
|
+
const installErr = await new Response(installProc.stderr).text();
|
|
109
|
+
throw new Error(`agent-browser install failed: ${installErr}`);
|
|
110
|
+
}
|
|
111
|
+
log.info("✅ Chrome descargado.");
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
throw new Error(`agent-browser chrome check failed: ${err}`);
|
|
189
115
|
}
|
|
116
|
+
}
|
|
190
117
|
|
|
191
|
-
|
|
118
|
+
// ─── AgentBrowserView (API compatible con CDPClient) ──────────────────────────
|
|
192
119
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
});
|
|
205
|
-
if (res.result?.value === "complete") break;
|
|
206
|
-
} catch { /* continuar */ }
|
|
207
|
-
}
|
|
208
|
-
// Actualizar URL real (puede haber redirect)
|
|
209
|
-
try {
|
|
210
|
-
const res = await this.cdp<{ result: { value: string } }>("Runtime.evaluate", {
|
|
211
|
-
expression: "location.href",
|
|
212
|
-
returnByValue: true,
|
|
213
|
-
});
|
|
214
|
-
this._url = res.result?.value || url;
|
|
215
|
-
} catch {
|
|
216
|
-
this._url = url;
|
|
217
|
-
}
|
|
120
|
+
export class AgentBrowserView {
|
|
121
|
+
private sessionName: string;
|
|
122
|
+
private _url = "";
|
|
123
|
+
|
|
124
|
+
get url(): string { return this._url; }
|
|
125
|
+
get title(): string { return ""; }
|
|
126
|
+
get loading(): boolean { return false; }
|
|
127
|
+
get isConnected(): boolean { return true; }
|
|
128
|
+
|
|
129
|
+
constructor(sessionName: string = DEFAULT_SESSION_NAME) {
|
|
130
|
+
this.sessionName = sessionName;
|
|
218
131
|
}
|
|
219
132
|
|
|
220
|
-
|
|
133
|
+
protected async run(args: string[]): Promise<{ success: boolean; data?: any; error?: string }> {
|
|
134
|
+
return runAgentBrowser(["--session", this.sessionName, "--json", ...args]);
|
|
135
|
+
}
|
|
221
136
|
|
|
222
|
-
async
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
137
|
+
async navigate(url: string): Promise<void> {
|
|
138
|
+
// Ensure protocol
|
|
139
|
+
const target = /^https?:\/\//.test(url) ? url : `https://${url}`;
|
|
140
|
+
const res = await this.run(["open", target]);
|
|
141
|
+
if (!res.success) throw new Error(res.error || "navigate failed");
|
|
142
|
+
this._url = res.data?.url || target;
|
|
143
|
+
// Small delay to let JS settle (same as old implementation)
|
|
144
|
+
await new Promise(r => setTimeout(r, 500));
|
|
229
145
|
}
|
|
230
146
|
|
|
231
|
-
|
|
147
|
+
async evaluate<T = unknown>(script: string): Promise<T> {
|
|
148
|
+
let wrapped = script;
|
|
149
|
+
const trimmed = script.trim();
|
|
150
|
+
|
|
151
|
+
// If script contains top-level await, wrap in async IIFE to make it valid JS
|
|
152
|
+
if (/\bawait\b/.test(script) && !trimmed.startsWith("(async") && !trimmed.startsWith("async function")) {
|
|
153
|
+
if (trimmed.startsWith("return")) {
|
|
154
|
+
wrapped = `(async () => { ${script} })()`;
|
|
155
|
+
} else {
|
|
156
|
+
wrapped = `(async () => { return ${script}; })()`;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const res = await this.run(["eval", wrapped]);
|
|
161
|
+
if (!res.success) throw new Error(res.error || "eval failed");
|
|
162
|
+
return res.data?.result as T;
|
|
163
|
+
}
|
|
232
164
|
|
|
233
165
|
async screenshot(options?: {
|
|
234
166
|
encoding?: "blob" | "buffer" | "base64" | "shmem";
|
|
@@ -236,156 +168,177 @@ export class CDPClient {
|
|
|
236
168
|
quality?: number;
|
|
237
169
|
clip?: { x: number; y: number; width: number; height: number; scale: number };
|
|
238
170
|
}): Promise<string> {
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
};
|
|
242
|
-
if (options?.quality) params.quality = options.quality;
|
|
243
|
-
if (options?.clip) params.clip = options.clip;
|
|
171
|
+
// Build args
|
|
172
|
+
const args: string[] = ["screenshot"];
|
|
244
173
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
174
|
+
if (options?.format === "jpeg") {
|
|
175
|
+
args.push("--screenshot-format", "jpeg");
|
|
176
|
+
}
|
|
177
|
+
if (options?.quality) {
|
|
178
|
+
args.push("--screenshot-quality", String(options.quality));
|
|
179
|
+
}
|
|
248
180
|
|
|
249
|
-
|
|
181
|
+
// If clip/selector is provided, agent-browser screenshot accepts a positional selector
|
|
182
|
+
// For element screenshots, we can pass a selector as first positional arg
|
|
183
|
+
// But we don't have selector in options here — the old CDPClient didn't use it either
|
|
184
|
+
// screenshotElement helper handles element-specific screenshots
|
|
250
185
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
const box = await this.evaluate<{ x: number; y: number; width: number; height: number } | null>(`
|
|
254
|
-
(() => {
|
|
255
|
-
const el = document.querySelector(${JSON.stringify(selector)});
|
|
256
|
-
if (!el) return null;
|
|
257
|
-
el.scrollIntoView({ behavior: "instant", block: "center" });
|
|
258
|
-
const r = el.getBoundingClientRect();
|
|
259
|
-
return { x: Math.round(r.left + r.width / 2), y: Math.round(r.top + r.height / 2), width: r.width, height: r.height };
|
|
260
|
-
})()
|
|
261
|
-
`);
|
|
262
|
-
if (!box) throw new Error(`Selector no encontrado: ${selector}`);
|
|
186
|
+
const res = await this.run(args);
|
|
187
|
+
if (!res.success) throw new Error(res.error || "screenshot failed");
|
|
263
188
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
189
|
+
const path = res.data?.path as string;
|
|
190
|
+
if (!path) throw new Error("screenshot did not return a path");
|
|
191
|
+
|
|
192
|
+
const data = readFileSync(path);
|
|
193
|
+
const base64 = Buffer.from(data).toString("base64");
|
|
268
194
|
|
|
269
|
-
//
|
|
270
|
-
|
|
271
|
-
|
|
195
|
+
// Cleanup temp file
|
|
196
|
+
try { rmSync(path); } catch { /* ignore */ }
|
|
197
|
+
|
|
198
|
+
return base64;
|
|
272
199
|
}
|
|
273
200
|
|
|
274
|
-
|
|
201
|
+
async click(selector: string, _options?: Record<string, unknown>): Promise<void> {
|
|
202
|
+
const res = await this.run(["click", selector]);
|
|
203
|
+
if (!res.success) throw new Error(res.error || `click failed: ${selector}`);
|
|
204
|
+
}
|
|
275
205
|
|
|
276
206
|
async type(text: string): Promise<void> {
|
|
277
|
-
//
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
if (this._focusedSelector) {
|
|
281
|
-
const sel = this._focusedSelector;
|
|
282
|
-
await this.evaluate(`
|
|
283
|
-
(() => {
|
|
284
|
-
const el = document.querySelector(${JSON.stringify(sel)});
|
|
285
|
-
if (!el) return;
|
|
286
|
-
const s = el.selectionStart ?? el.value?.length ?? 0;
|
|
287
|
-
const e = el.selectionEnd ?? el.value?.length ?? 0;
|
|
288
|
-
const before = (el.value ?? "").substring(0, s);
|
|
289
|
-
const after = (el.value ?? "").substring(e);
|
|
290
|
-
el.value = before + ${JSON.stringify(text)} + after;
|
|
291
|
-
el.selectionStart = el.selectionEnd = before.length + ${JSON.stringify(text)}.length;
|
|
292
|
-
el.dispatchEvent(new Event('input', { bubbles: true }));
|
|
293
|
-
el.dispatchEvent(new Event('change', { bubbles: true }));
|
|
294
|
-
})()
|
|
295
|
-
`);
|
|
296
|
-
} else {
|
|
297
|
-
// Fallback: char events al elemento activo del browser
|
|
298
|
-
for (const char of text) {
|
|
299
|
-
await this.cdp("Input.dispatchKeyEvent", { type: "char", text: char });
|
|
300
|
-
}
|
|
301
|
-
}
|
|
207
|
+
// Fallback: keyboard inserttext (requires focused element)
|
|
208
|
+
const res = await this.run(["keyboard", "inserttext", text]);
|
|
209
|
+
if (!res.success) throw new Error(res.error || "type failed");
|
|
302
210
|
}
|
|
303
211
|
|
|
304
|
-
|
|
212
|
+
async typeIn(selector: string, text: string): Promise<void> {
|
|
213
|
+
const res = await this.run(["type", selector, text]);
|
|
214
|
+
if (!res.success) throw new Error(res.error || `type failed: ${selector}`);
|
|
215
|
+
}
|
|
305
216
|
|
|
306
|
-
async
|
|
307
|
-
const
|
|
308
|
-
|
|
309
|
-
if (m === "Control" || m === "Meta") return acc | 2;
|
|
310
|
-
if (m === "Shift") return acc | 8;
|
|
311
|
-
return acc;
|
|
312
|
-
}, 0);
|
|
313
|
-
|
|
314
|
-
await this.cdp("Input.dispatchKeyEvent", { type: "keyDown", key, modifiers: modifierBits });
|
|
315
|
-
// El evento 'char' es necesario para que el navegador procese teclas como Enter
|
|
316
|
-
// y dispare comportamientos del DOM (submit de formularios, saltos de línea, etc.)
|
|
317
|
-
await this.cdp("Input.dispatchKeyEvent", {
|
|
318
|
-
type: "char",
|
|
319
|
-
key: key === "Return" || key === "Enter" ? "\r" : key.length === 1 ? key : "",
|
|
320
|
-
modifiers: modifierBits,
|
|
321
|
-
});
|
|
322
|
-
await this.cdp("Input.dispatchKeyEvent", { type: "keyUp", key, modifiers: modifierBits });
|
|
217
|
+
async fill(selector: string, text: string): Promise<void> {
|
|
218
|
+
const res = await this.run(["fill", selector, text]);
|
|
219
|
+
if (!res.success) throw new Error(res.error || `fill failed: ${selector}`);
|
|
323
220
|
}
|
|
324
221
|
|
|
325
|
-
|
|
222
|
+
async press(key: string, options?: { modifiers?: string[] }): Promise<void> {
|
|
223
|
+
const modifiers = options?.modifiers ?? [];
|
|
224
|
+
const combo = modifiers.length > 0
|
|
225
|
+
? `${modifiers.join("+")}+${key}`
|
|
226
|
+
: key;
|
|
227
|
+
const res = await this.run(["press", combo]);
|
|
228
|
+
if (!res.success) throw new Error(res.error || `press failed: ${combo}`);
|
|
229
|
+
}
|
|
326
230
|
|
|
327
231
|
async scroll(dx: number, dy: number): Promise<void> {
|
|
328
|
-
|
|
232
|
+
const dir = dy > 0 ? "down" : dy < 0 ? "up" : dx > 0 ? "right" : "left";
|
|
233
|
+
const px = Math.abs(dy || dx);
|
|
234
|
+
const res = await this.run(["scroll", dir, String(px)]);
|
|
235
|
+
if (!res.success) throw new Error(res.error || "scroll failed");
|
|
329
236
|
}
|
|
330
237
|
|
|
331
|
-
async scrollTo(selector: string,
|
|
332
|
-
|
|
333
|
-
await this.
|
|
238
|
+
async scrollTo(selector: string, _options?: { behavior?: "smooth" | "instant" }): Promise<void> {
|
|
239
|
+
// agent-browser has scrollintoview (behavior not supported via CLI)
|
|
240
|
+
const res = await this.run(["scrollintoview", selector]);
|
|
241
|
+
if (!res.success) throw new Error(res.error || `scrollTo failed: ${selector}`);
|
|
334
242
|
}
|
|
335
243
|
|
|
336
|
-
// ── navigation helpers ──────────────────────────────────────────────────────
|
|
337
|
-
|
|
338
244
|
async back(): Promise<void> {
|
|
339
|
-
await this.
|
|
245
|
+
const res = await this.run(["back"]);
|
|
246
|
+
if (!res.success) throw new Error(res.error || "back failed");
|
|
340
247
|
await new Promise<void>(r => setTimeout(r, 800));
|
|
341
248
|
}
|
|
342
249
|
|
|
343
250
|
async forward(): Promise<void> {
|
|
344
|
-
await this.
|
|
251
|
+
const res = await this.run(["forward"]);
|
|
252
|
+
if (!res.success) throw new Error(res.error || "forward failed");
|
|
345
253
|
await new Promise<void>(r => setTimeout(r, 800));
|
|
346
254
|
}
|
|
347
255
|
|
|
348
256
|
async reload(): Promise<void> {
|
|
349
|
-
await this.
|
|
257
|
+
const res = await this.run(["reload"]);
|
|
258
|
+
if (!res.success) throw new Error(res.error || "reload failed");
|
|
350
259
|
await new Promise<void>(r => setTimeout(r, 1000));
|
|
351
260
|
}
|
|
352
261
|
|
|
353
262
|
async resize(width: number, height: number): Promise<void> {
|
|
354
|
-
await this.
|
|
355
|
-
|
|
356
|
-
|
|
263
|
+
const res = await this.run(["set", "viewport", String(width), String(height)]);
|
|
264
|
+
if (!res.success) throw new Error(res.error || "resize failed");
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/** Capture accessibility tree snapshot (compact, AI-optimized). ~200-600 chars vs ~3000+ innerText. */
|
|
268
|
+
async snapshot(options?: { compact?: boolean; depth?: number; interactiveOnly?: boolean }): Promise<string> {
|
|
269
|
+
const args = ["snapshot"];
|
|
270
|
+
if (options?.compact !== false) args.push("-c");
|
|
271
|
+
if (options?.depth) args.push("-d", String(options.depth));
|
|
272
|
+
if (options?.interactiveOnly) args.push("-i");
|
|
273
|
+
|
|
274
|
+
const res = await this.run(args);
|
|
275
|
+
if (!res.success) throw new Error(res.error || "snapshot failed");
|
|
276
|
+
return res.data?.snapshot as string || "";
|
|
357
277
|
}
|
|
358
278
|
|
|
359
|
-
|
|
279
|
+
async cdp<T = unknown>(method: string, params?: Record<string, unknown>): Promise<T> {
|
|
280
|
+
const script = `
|
|
281
|
+
(() => {
|
|
282
|
+
// agent-browser does not expose raw CDP directly via CLI for all methods.
|
|
283
|
+
// For common methods we can emulate; for others we return a notice.
|
|
284
|
+
const method = ${JSON.stringify(method)};
|
|
285
|
+
const params = ${JSON.stringify(params ?? {})};
|
|
286
|
+
return { method, params, note: "CDP passthrough not fully supported by agent-browser CLI" };
|
|
287
|
+
})()
|
|
288
|
+
`;
|
|
289
|
+
const res = await this.run(["eval", script]);
|
|
290
|
+
if (!res.success) throw new Error(res.error || `cdp failed: ${method}`);
|
|
291
|
+
return res.data?.result as T;
|
|
292
|
+
}
|
|
360
293
|
|
|
361
294
|
close(): void {
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
295
|
+
// Close the session
|
|
296
|
+
this.run(["close"]).catch(() => { /* ignore */ });
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// ─── Backwards compatibility exports ──────────────────────────────────────────
|
|
301
|
+
|
|
302
|
+
/** @deprecated Use AgentBrowserView instead */
|
|
303
|
+
export class CDPClient extends AgentBrowserView {
|
|
304
|
+
private _launched = false;
|
|
305
|
+
|
|
306
|
+
async launch(_spec?: unknown, _options?: unknown): Promise<void> {
|
|
307
|
+
if (this._launched) return;
|
|
308
|
+
// Verify agent-browser is working by opening about:blank
|
|
309
|
+
const res = await this.run(["open", "about:blank"]);
|
|
310
|
+
if (!res.success) throw new Error(res.error || "Failed to launch agent-browser");
|
|
311
|
+
this._launched = true;
|
|
368
312
|
}
|
|
369
313
|
|
|
370
314
|
static closeAll(): void {
|
|
371
|
-
|
|
372
|
-
allInstances.clear();
|
|
315
|
+
// agent-browser sessions are managed by the daemon; no explicit cleanup needed
|
|
373
316
|
}
|
|
374
317
|
}
|
|
375
318
|
|
|
319
|
+
/** @deprecated No longer used — agent-browser handles browser detection internally */
|
|
320
|
+
export function detectBrowser(_options?: unknown): undefined {
|
|
321
|
+
return undefined;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/** @deprecated No longer used */
|
|
325
|
+
export type LaunchSpec = { kind: "remote"; cdpUrl: string };
|
|
326
|
+
|
|
376
327
|
// ─── BrowserService (singleton) ───────────────────────────────────────────────
|
|
377
328
|
|
|
378
|
-
export type BrowserView =
|
|
329
|
+
export type BrowserView = AgentBrowserView;
|
|
379
330
|
|
|
380
|
-
let _client:
|
|
381
|
-
let _spec: LaunchSpec | undefined = undefined;
|
|
331
|
+
let _client: AgentBrowserView | null = null;
|
|
382
332
|
let _available = false;
|
|
383
333
|
let _launching = false;
|
|
384
334
|
|
|
385
335
|
export class BrowserService {
|
|
386
336
|
private static instance: BrowserService | null = null;
|
|
337
|
+
private readonly config: Config;
|
|
387
338
|
|
|
388
|
-
private constructor(
|
|
339
|
+
private constructor(config: Config) {
|
|
340
|
+
this.config = config;
|
|
341
|
+
}
|
|
389
342
|
|
|
390
343
|
static getInstance(config: Config): BrowserService {
|
|
391
344
|
if (!BrowserService.instance) {
|
|
@@ -395,40 +348,52 @@ export class BrowserService {
|
|
|
395
348
|
}
|
|
396
349
|
|
|
397
350
|
/**
|
|
398
|
-
* Probe
|
|
399
|
-
* Does NOT launch the browser — that happens lazily on first tool use.
|
|
351
|
+
* Probe / lazy install agent-browser.
|
|
400
352
|
*/
|
|
401
353
|
async start(): Promise<boolean> {
|
|
402
|
-
|
|
403
|
-
if (
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
354
|
+
const b = this.config.tools?.browser;
|
|
355
|
+
if (b?.enabled === false) {
|
|
356
|
+
_available = false;
|
|
357
|
+
return false;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
const installed = await isAgentBrowserInstalled();
|
|
361
|
+
|
|
362
|
+
if (!installed) {
|
|
363
|
+
try {
|
|
364
|
+
await installAgentBrowser();
|
|
365
|
+
} catch (err) {
|
|
366
|
+
log.warn(`No se pudo instalar agent-browser: ${(err as Error).message}`);
|
|
367
|
+
log.warn(" Instalar manualmente: bun add -g agent-browser");
|
|
368
|
+
_available = false;
|
|
369
|
+
return false;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
try {
|
|
374
|
+
await ensureChromeInstalled();
|
|
375
|
+
} catch (err) {
|
|
376
|
+
log.warn(`Chrome no pudo prepararse: ${(err as Error).message}`);
|
|
408
377
|
_available = false;
|
|
409
378
|
return false;
|
|
410
379
|
}
|
|
380
|
+
|
|
411
381
|
_available = true;
|
|
412
|
-
log.info(
|
|
382
|
+
log.info("✅ agent-browser listo — se abrirá al primer uso");
|
|
413
383
|
return true;
|
|
414
384
|
}
|
|
415
385
|
|
|
416
|
-
/**
|
|
417
|
-
* Lazy launch: called by getView() on first tool use.
|
|
418
|
-
*/
|
|
419
386
|
private async _ensureLaunched(): Promise<boolean> {
|
|
420
387
|
if (_client) return true;
|
|
421
|
-
if (!_spec) return false;
|
|
422
388
|
if (_launching) {
|
|
423
|
-
// Wait up to 10s for concurrent launch to finish
|
|
424
389
|
const deadline = Date.now() + 10000;
|
|
425
390
|
while (_launching && Date.now() < deadline) await new Promise(r => setTimeout(r, 100));
|
|
426
391
|
return !!_client;
|
|
427
392
|
}
|
|
428
393
|
_launching = true;
|
|
429
394
|
try {
|
|
430
|
-
|
|
431
|
-
|
|
395
|
+
const sessionName = this.config.tools?.browser?.sessionName ?? DEFAULT_SESSION_NAME;
|
|
396
|
+
_client = new AgentBrowserView(sessionName);
|
|
432
397
|
log.info("✅ Browser abierto — el usuario verá las acciones del agente");
|
|
433
398
|
return true;
|
|
434
399
|
} catch (err) {
|
|
@@ -441,25 +406,17 @@ export class BrowserService {
|
|
|
441
406
|
}
|
|
442
407
|
}
|
|
443
408
|
|
|
444
|
-
async getView(): Promise<
|
|
409
|
+
async getView(): Promise<AgentBrowserView | null> {
|
|
445
410
|
if (!_available) return null;
|
|
446
|
-
|
|
447
|
-
// Health-check: if Chrome was closed by the user or crashed, relaunch on next call
|
|
448
|
-
if (_client && !_client.isConnected) {
|
|
449
|
-
log.warn("Browser connection lost — relaunching on next tool call");
|
|
450
|
-
_client = null;
|
|
451
|
-
}
|
|
452
|
-
|
|
453
411
|
await this._ensureLaunched();
|
|
454
412
|
return _client;
|
|
455
413
|
}
|
|
456
414
|
|
|
457
|
-
|
|
458
|
-
getViewSync(): CDPClient | null {
|
|
415
|
+
getViewSync(): AgentBrowserView | null {
|
|
459
416
|
return _client;
|
|
460
417
|
}
|
|
461
418
|
|
|
462
|
-
async getPage(): Promise<
|
|
419
|
+
async getPage(): Promise<AgentBrowserView | null> {
|
|
463
420
|
return this.getView();
|
|
464
421
|
}
|
|
465
422
|
|
|
@@ -505,7 +462,7 @@ export function getBrowserService(): BrowserService | null {
|
|
|
505
462
|
// ─── Helpers (misma API que antes) ───────────────────────────────────────────
|
|
506
463
|
|
|
507
464
|
export async function waitForSelector(
|
|
508
|
-
view:
|
|
465
|
+
view: AgentBrowserView,
|
|
509
466
|
selector: string,
|
|
510
467
|
timeout = 30000
|
|
511
468
|
): Promise<void> {
|
|
@@ -519,7 +476,7 @@ export async function waitForSelector(
|
|
|
519
476
|
}
|
|
520
477
|
|
|
521
478
|
export async function waitForCondition(
|
|
522
|
-
view:
|
|
479
|
+
view: AgentBrowserView,
|
|
523
480
|
expression: string,
|
|
524
481
|
timeout = 30000
|
|
525
482
|
): Promise<void> {
|
|
@@ -533,22 +490,19 @@ export async function waitForCondition(
|
|
|
533
490
|
}
|
|
534
491
|
|
|
535
492
|
export async function screenshotElement(
|
|
536
|
-
view:
|
|
493
|
+
view: AgentBrowserView,
|
|
537
494
|
selector: string
|
|
538
495
|
): Promise<string> {
|
|
539
|
-
const
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
return
|
|
551
|
-
format: "png",
|
|
552
|
-
clip: { x: box.x, y: box.y, width: box.width, height: box.height, scale: 1 },
|
|
553
|
-
});
|
|
496
|
+
const res = await (view as any).run(["screenshot", selector]);
|
|
497
|
+
if (!res.success) throw new Error(res.error || `screenshot failed: ${selector}`);
|
|
498
|
+
|
|
499
|
+
const path = res.data?.path as string;
|
|
500
|
+
if (!path) throw new Error("screenshot did not return a path");
|
|
501
|
+
|
|
502
|
+
const data = readFileSync(path);
|
|
503
|
+
const base64 = Buffer.from(data).toString("base64");
|
|
504
|
+
|
|
505
|
+
try { rmSync(path); } catch { /* ignore */ }
|
|
506
|
+
|
|
507
|
+
return base64;
|
|
554
508
|
}
|