@sunshinelife83/hearth 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +323 -0
- package/bin/hearth-agentd.js +4 -0
- package/bin/hearth.js +4 -0
- package/bin/run-entrypoint.js +20 -0
- package/dist/agent-tools.js +738 -0
- package/dist/app-version.js +16 -0
- package/dist/apply-patch.js +402 -0
- package/dist/artifact-error.js +8 -0
- package/dist/artifact-tools.js +377 -0
- package/dist/cli-workspace.js +36 -0
- package/dist/cli.js +1086 -0
- package/dist/config-migration.js +82 -0
- package/dist/config-schema.js +136 -0
- package/dist/config.js +115 -0
- package/dist/context/context-tools.js +150 -0
- package/dist/context/repo-map.js +102 -0
- package/dist/dashboard/app.js +226 -0
- package/dist/dashboard/index.html +45 -0
- package/dist/dashboard/landing.html +57 -0
- package/dist/dashboard/styles.css +23 -0
- package/dist/dashboard.js +271 -0
- package/dist/db/client.js +29 -0
- package/dist/db/migrations.js +262 -0
- package/dist/db/schema.js +85 -0
- package/dist/device-tokens.js +61 -0
- package/dist/git-worktrees.js +134 -0
- package/dist/git.js +41 -0
- package/dist/incoming-artifacts.js +378 -0
- package/dist/local-agent-acp.js +768 -0
- package/dist/local-agent-adapters.js +20 -0
- package/dist/local-agent-availability.js +92 -0
- package/dist/local-agent-catalog.js +51 -0
- package/dist/local-agent-claude.js +334 -0
- package/dist/local-agent-client.js +519 -0
- package/dist/local-agent-codex.js +532 -0
- package/dist/local-agent-config.js +34 -0
- package/dist/local-agent-daemon-lifecycle.js +195 -0
- package/dist/local-agent-daemon-main.js +86 -0
- package/dist/local-agent-daemon-protocol.js +302 -0
- package/dist/local-agent-daemon.js +407 -0
- package/dist/local-agent-errors.js +378 -0
- package/dist/local-agent-grok.js +202 -0
- package/dist/local-agent-manager.js +715 -0
- package/dist/local-agent-opencode.js +463 -0
- package/dist/local-agent-path.js +24 -0
- package/dist/local-agent-pi-sandbox.js +472 -0
- package/dist/local-agent-pi.js +298 -0
- package/dist/local-agent-presentation.js +104 -0
- package/dist/local-agent-profiles.js +127 -0
- package/dist/local-agent-runtime-pool.js +481 -0
- package/dist/local-agent-runtime.js +1 -0
- package/dist/local-agent-store.js +193 -0
- package/dist/local-agent-targets.js +97 -0
- package/dist/logger.js +97 -0
- package/dist/machine-id.js +35 -0
- package/dist/mcp-modern-server.js +65 -0
- package/dist/oauth-provider.js +331 -0
- package/dist/oauth-store.js +144 -0
- package/dist/onboarding.js +35 -0
- package/dist/orchestration/brief.js +70 -0
- package/dist/orchestration/lanes.js +145 -0
- package/dist/orchestration/result.js +46 -0
- package/dist/orchestration/scope.js +61 -0
- package/dist/pi-tools.js +58 -0
- package/dist/policy/command-policy.js +104 -0
- package/dist/policy/env-allowlist.js +96 -0
- package/dist/policy/sandbox.js +68 -0
- package/dist/policy/workspace-profiles.js +47 -0
- package/dist/process-journal.js +116 -0
- package/dist/process-platform.js +49 -0
- package/dist/process-sessions.js +388 -0
- package/dist/rate-limit.js +77 -0
- package/dist/request-meta.js +9 -0
- package/dist/review-checkpoints.js +349 -0
- package/dist/roots.js +37 -0
- package/dist/server-shutdown.js +12 -0
- package/dist/server.js +980 -0
- package/dist/skills.js +84 -0
- package/dist/snapshot-manager.js +123 -0
- package/dist/stdio-server.js +80 -0
- package/dist/task-store.js +203 -0
- package/dist/task-tools.js +455 -0
- package/dist/tool-surfaces/claude.js +220 -0
- package/dist/tool-surfaces/codex.js +255 -0
- package/dist/tool-surfaces/index.js +15 -0
- package/dist/tool-surfaces/shared.js +228 -0
- package/dist/tool-surfaces/types.js +27 -0
- package/dist/ui/.vite/manifest.json +2544 -0
- package/dist/ui/assets/abap-CLvhMVsD.js +1 -0
- package/dist/ui/assets/actionscript-3--17pq3dv.js +1 -0
- package/dist/ui/assets/ada-C5qYipkI.js +1 -0
- package/dist/ui/assets/andromeeda-vGVdxbeo.js +1 -0
- package/dist/ui/assets/angular-html-DrbO3_Xe.js +1 -0
- package/dist/ui/assets/angular-ts-CmTVzHJx.js +1 -0
- package/dist/ui/assets/apache-U0d_L8uA.js +1 -0
- package/dist/ui/assets/apex-CGTLDQj6.js +1 -0
- package/dist/ui/assets/apl-BOpCpqXf.js +1 -0
- package/dist/ui/assets/applescript-CCn79oCD.js +1 -0
- package/dist/ui/assets/ara-4CJ0cIlV.js +1 -0
- package/dist/ui/assets/asciidoc-DE70LPWp.js +1 -0
- package/dist/ui/assets/asm-Cmm7eHzH.js +1 -0
- package/dist/ui/assets/astro-CxPeBwei.js +1 -0
- package/dist/ui/assets/aurora-x-CDeNXAV0.js +1 -0
- package/dist/ui/assets/awk-BWXHIvNe.js +1 -0
- package/dist/ui/assets/ayu-dark-DluEY0Gj.js +1 -0
- package/dist/ui/assets/ayu-light-C3h-C4tm.js +1 -0
- package/dist/ui/assets/ayu-mirage-Bqwy1Gya.js +1 -0
- package/dist/ui/assets/ballerina-B7ZEbQpA.js +1 -0
- package/dist/ui/assets/bat-Bo4NYOV-.js +1 -0
- package/dist/ui/assets/beancount-D-usSTwE.js +1 -0
- package/dist/ui/assets/berry-DKpUyyne.js +1 -0
- package/dist/ui/assets/bibtex-Ci_nEsc7.js +1 -0
- package/dist/ui/assets/bicep-CUHmPFLl.js +1 -0
- package/dist/ui/assets/bird2-C2hNVINV.js +1 -0
- package/dist/ui/assets/blade-DtsAAG4P.js +1 -0
- package/dist/ui/assets/bsl-BkkzgIyY.js +1 -0
- package/dist/ui/assets/c-D9hLjNUM.js +1 -0
- package/dist/ui/assets/c3-CnJL0r0V.js +1 -0
- package/dist/ui/assets/cadence-CQ2zXKGN.js +1 -0
- package/dist/ui/assets/cairo-DLTphjLi.js +1 -0
- package/dist/ui/assets/catppuccin-frappe-3VR1Za6u.js +1 -0
- package/dist/ui/assets/catppuccin-latte-DwIHMF0Q.js +1 -0
- package/dist/ui/assets/catppuccin-macchiato-DYnBP6_5.js +1 -0
- package/dist/ui/assets/catppuccin-mocha-DYhrFGRu.js +1 -0
- package/dist/ui/assets/clarity-SemFz856.js +1 -0
- package/dist/ui/assets/clojure-DqKBuwfJ.js +1 -0
- package/dist/ui/assets/cmake-Bj61d0ZC.js +1 -0
- package/dist/ui/assets/cobol-C1SWlvZm.js +1 -0
- package/dist/ui/assets/codeowners-C8r90Shi.js +1 -0
- package/dist/ui/assets/codeql-oeQT6MSM.js +1 -0
- package/dist/ui/assets/coffee-B71R3v2U.js +1 -0
- package/dist/ui/assets/common-lisp-Cv5bFMCO.js +1 -0
- package/dist/ui/assets/coq-BrsZFFmf.js +1 -0
- package/dist/ui/assets/cpp--5a4c3v4.js +1 -0
- package/dist/ui/assets/crystal-AtqWYtE0.js +1 -0
- package/dist/ui/assets/csharp-Ct8U2NOr.js +1 -0
- package/dist/ui/assets/css-CbqKSHI-.js +1 -0
- package/dist/ui/assets/csv-Dx-8-gkx.js +1 -0
- package/dist/ui/assets/cue-CE9AQfxI.js +1 -0
- package/dist/ui/assets/cypher-ClKdZ_lG.js +1 -0
- package/dist/ui/assets/d-qD-0Kul2.js +1 -0
- package/dist/ui/assets/dark-plus-Cs2F2srj.js +1 -0
- package/dist/ui/assets/dart-DkHntEIa.js +1 -0
- package/dist/ui/assets/dax-BkyTk9wS.js +1 -0
- package/dist/ui/assets/desktop-Dlh5hvp9.js +1 -0
- package/dist/ui/assets/diff-woXpYk--.js +1 -0
- package/dist/ui/assets/docker-IyjqRm3v.js +1 -0
- package/dist/ui/assets/dotenv-_5a1GRtc.js +1 -0
- package/dist/ui/assets/dracula-BHWKrbxM.js +1 -0
- package/dist/ui/assets/dracula-soft-5eyTD99u.js +1 -0
- package/dist/ui/assets/dream-maker-DW3nJb8Q.js +1 -0
- package/dist/ui/assets/edge-BPwY_RrJ.js +1 -0
- package/dist/ui/assets/elixir-DVH2yLJf.js +1 -0
- package/dist/ui/assets/elm-BrleA6gS.js +1 -0
- package/dist/ui/assets/emacs-lisp-C9PiwqqW.js +1 -0
- package/dist/ui/assets/erb-2SHiYe30.js +1 -0
- package/dist/ui/assets/erlang-Cphh6RMH.js +1 -0
- package/dist/ui/assets/everforest-dark-sB-x3p7T.js +1 -0
- package/dist/ui/assets/everforest-light-Df2xbC6M.js +1 -0
- package/dist/ui/assets/fennel-DQxkIbk2.js +1 -0
- package/dist/ui/assets/fish-BJitypiv.js +1 -0
- package/dist/ui/assets/fluent-C03EYrpw.js +1 -0
- package/dist/ui/assets/fortran-fixed-form-DEKoE2YW.js +1 -0
- package/dist/ui/assets/fortran-free-form-CYNrtFtB.js +1 -0
- package/dist/ui/assets/fsharp-D13ZGOAj.js +1 -0
- package/dist/ui/assets/gdresource-C0sCabJj.js +1 -0
- package/dist/ui/assets/gdscript-Cp2uCuqX.js +1 -0
- package/dist/ui/assets/gdshader-CBce3t8t.js +1 -0
- package/dist/ui/assets/genie-CV2tkWYe.js +1 -0
- package/dist/ui/assets/gherkin-DExj1W_8.js +1 -0
- package/dist/ui/assets/git-commit-BSykSTBG.js +1 -0
- package/dist/ui/assets/git-rebase-Bc7atMaV.js +1 -0
- package/dist/ui/assets/github-dark-C-LZuMrd.js +1 -0
- package/dist/ui/assets/github-dark-default-DXG-b-1a.js +1 -0
- package/dist/ui/assets/github-dark-dimmed-Bx1FflLF.js +1 -0
- package/dist/ui/assets/github-dark-high-contrast-B_tTalzw.js +1 -0
- package/dist/ui/assets/github-light-EUqPIrTm.js +1 -0
- package/dist/ui/assets/github-light-default-BXViO-2h.js +1 -0
- package/dist/ui/assets/github-light-high-contrast-B68TUdTA.js +1 -0
- package/dist/ui/assets/gleam-CSRkHgEL.js +1 -0
- package/dist/ui/assets/glimmer-js-DXeTDPtW.js +1 -0
- package/dist/ui/assets/glimmer-ts-CEOoXHU4.js +1 -0
- package/dist/ui/assets/glsl-5-43-Gsg.js +1 -0
- package/dist/ui/assets/gn-ilITqXS6.js +1 -0
- package/dist/ui/assets/gnuplot-7GGW24-e.js +1 -0
- package/dist/ui/assets/go-BJwz_mda.js +1 -0
- package/dist/ui/assets/graphql-Bk7Ah1KA.js +1 -0
- package/dist/ui/assets/groovy-CacY0gHj.js +1 -0
- package/dist/ui/assets/gruvbox-dark-hard-C820rvS2.js +1 -0
- package/dist/ui/assets/gruvbox-dark-medium-BPjhmG05.js +1 -0
- package/dist/ui/assets/gruvbox-dark-soft-MrdJrrXF.js +1 -0
- package/dist/ui/assets/gruvbox-light-hard-BC_s9l72.js +1 -0
- package/dist/ui/assets/gruvbox-light-medium-BAWPOn9u.js +1 -0
- package/dist/ui/assets/gruvbox-light-soft-BSMLrYjP.js +1 -0
- package/dist/ui/assets/hack-DUHz4rCJ.js +1 -0
- package/dist/ui/assets/haml-BPCuuTPR.js +1 -0
- package/dist/ui/assets/handlebars-CHxmUsnU.js +1 -0
- package/dist/ui/assets/haskell-D8IpX4py.js +1 -0
- package/dist/ui/assets/haxe-OTjmBuCE.js +1 -0
- package/dist/ui/assets/hcl-Dh228itO.js +1 -0
- package/dist/ui/assets/hjson-CxZEssPk.js +1 -0
- package/dist/ui/assets/hlsl-Cvrh5tZx.js +1 -0
- package/dist/ui/assets/horizon-CE9ld1lL.js +1 -0
- package/dist/ui/assets/horizon-bright-Br1oVSNq.js +1 -0
- package/dist/ui/assets/houston-CsvMBhTu.js +1 -0
- package/dist/ui/assets/html-BBJqizz0.js +1 -0
- package/dist/ui/assets/html-derivative-ShTPCcKN.js +1 -0
- package/dist/ui/assets/http-VFTyziwA.js +1 -0
- package/dist/ui/assets/hurl-B8yxTa4r.js +1 -0
- package/dist/ui/assets/hxml-B0Qn7Nwc.js +1 -0
- package/dist/ui/assets/hy-CZbG8q4J.js +1 -0
- package/dist/ui/assets/imba-DsUTQ-LC.js +1 -0
- package/dist/ui/assets/ini-B5eOa1yu.js +1 -0
- package/dist/ui/assets/java-BZ5DAgJZ.js +1 -0
- package/dist/ui/assets/javascript-C_zuIEaC.js +1 -0
- package/dist/ui/assets/jinja-DWd6xfNr.js +1 -0
- package/dist/ui/assets/jison-BbY63d1V.js +1 -0
- package/dist/ui/assets/json-C4DnajZs.js +1 -0
- package/dist/ui/assets/json5-BR5RXkoi.js +1 -0
- package/dist/ui/assets/jsonc-CYpm1nAK.js +1 -0
- package/dist/ui/assets/jsonl-CmCQp5Yx.js +1 -0
- package/dist/ui/assets/jsonnet-CJTPZ8u_.js +1 -0
- package/dist/ui/assets/jssm-DXw9l8Rf.js +1 -0
- package/dist/ui/assets/jsx-C4AACieZ.js +1 -0
- package/dist/ui/assets/julia-sRu7EFMW.js +1 -0
- package/dist/ui/assets/just-DpZ9Sx1v.js +1 -0
- package/dist/ui/assets/kanagawa-dragon-CXtmUGW6.js +1 -0
- package/dist/ui/assets/kanagawa-lotus-BN08jTvb.js +1 -0
- package/dist/ui/assets/kanagawa-wave-CTweb8Dz.js +1 -0
- package/dist/ui/assets/kdl-CsD5j6eV.js +1 -0
- package/dist/ui/assets/kotlin-DhhofPvG.js +1 -0
- package/dist/ui/assets/kusto-BUv0MjJC.js +1 -0
- package/dist/ui/assets/laserwave-C_8bwKvT.js +1 -0
- package/dist/ui/assets/latex-B6a_uum7.js +1 -0
- package/dist/ui/assets/lean-CewbzKMR.js +1 -0
- package/dist/ui/assets/less-DVTAwKKz.js +1 -0
- package/dist/ui/assets/light-plus-DVQuIRkW.js +1 -0
- package/dist/ui/assets/liquid-B2sVpjjr.js +1 -0
- package/dist/ui/assets/llvm-Cm23YOpf.js +1 -0
- package/dist/ui/assets/log-BNLmms1o.js +1 -0
- package/dist/ui/assets/logo-Cluzi2Zq.js +1 -0
- package/dist/ui/assets/lua-DxJJll61.js +1 -0
- package/dist/ui/assets/luau-CNKltnaQ.js +1 -0
- package/dist/ui/assets/make-Dixweg8N.js +1 -0
- package/dist/ui/assets/markdown-BYOwaDjH.js +1 -0
- package/dist/ui/assets/marko-Dx8mw-Wx.js +1 -0
- package/dist/ui/assets/material-theme-Bm3Qr25_.js +1 -0
- package/dist/ui/assets/material-theme-darker-2IIEA8gg.js +1 -0
- package/dist/ui/assets/material-theme-lighter-uhdI0v04.js +1 -0
- package/dist/ui/assets/material-theme-ocean-CHQ94UKr.js +1 -0
- package/dist/ui/assets/material-theme-palenight-B5W6OYN7.js +1 -0
- package/dist/ui/assets/matlab-D7qyCx1q.js +1 -0
- package/dist/ui/assets/mdc-vNKeXEwR.js +1 -0
- package/dist/ui/assets/mdx-DQZ5AkYe.js +1 -0
- package/dist/ui/assets/mermaid-Bk4SNUv9.js +1 -0
- package/dist/ui/assets/min-dark-BSWPekZh.js +1 -0
- package/dist/ui/assets/min-light-DDpmG2fV.js +1 -0
- package/dist/ui/assets/mipsasm-BMqwQI7S.js +1 -0
- package/dist/ui/assets/mojo-BgCJLMeH.js +1 -0
- package/dist/ui/assets/monokai-CdkpiU2Y.js +1 -0
- package/dist/ui/assets/moonbit-CaWjb8XO.js +1 -0
- package/dist/ui/assets/move-B1IS1UjX.js +1 -0
- package/dist/ui/assets/narrat-_X_XdTYD.js +1 -0
- package/dist/ui/assets/nextflow-Bbiyy34d.js +1 -0
- package/dist/ui/assets/nextflow-groovy-Dc_ddanL.js +1 -0
- package/dist/ui/assets/nginx-DScQAGLT.js +1 -0
- package/dist/ui/assets/night-owl-DhmEMT88.js +1 -0
- package/dist/ui/assets/night-owl-light-eJ-hLW7d.js +1 -0
- package/dist/ui/assets/nim-lAJch9uT.js +1 -0
- package/dist/ui/assets/nix-IvuFDN5E.js +1 -0
- package/dist/ui/assets/nord-Cb4Vim4T.js +1 -0
- package/dist/ui/assets/nushell-DcLAeLz5.js +1 -0
- package/dist/ui/assets/objective-c-D1A_Heim.js +1 -0
- package/dist/ui/assets/objective-cpp-BsSzOQcm.js +1 -0
- package/dist/ui/assets/ocaml-O90oeIOV.js +1 -0
- package/dist/ui/assets/odin-B1RWQWA5.js +1 -0
- package/dist/ui/assets/one-dark-pro-CLwyXe_n.js +1 -0
- package/dist/ui/assets/one-light-D7Lr4KcI.js +1 -0
- package/dist/ui/assets/openscad-BUDT5pXO.js +1 -0
- package/dist/ui/assets/pascal-4ZHwLPI5.js +1 -0
- package/dist/ui/assets/perl-C_GkVo_E.js +1 -0
- package/dist/ui/assets/php-DziV3K6J.js +1 -0
- package/dist/ui/assets/pierre-dark-CpLgRqie.js +1 -0
- package/dist/ui/assets/pierre-dark-protanopia-deuteranopia-B35FxJx-.js +1 -0
- package/dist/ui/assets/pierre-dark-soft-kZQmAZld.js +1 -0
- package/dist/ui/assets/pierre-dark-tritanopia-CpjhbsIL.js +1 -0
- package/dist/ui/assets/pierre-dark-vibrant-CpQYzh95.js +1 -0
- package/dist/ui/assets/pierre-light-CoaEpmwp.js +1 -0
- package/dist/ui/assets/pierre-light-protanopia-deuteranopia-0fSaH845.js +1 -0
- package/dist/ui/assets/pierre-light-soft-lWLdNTOI.js +1 -0
- package/dist/ui/assets/pierre-light-tritanopia-CEbqgOJL.js +1 -0
- package/dist/ui/assets/pierre-light-vibrant-D80Fkn33.js +1 -0
- package/dist/ui/assets/pkl-ot-7Btpt.js +1 -0
- package/dist/ui/assets/plastic-DQwYfKfQ.js +1 -0
- package/dist/ui/assets/plsql-DGHpHOYJ.js +1 -0
- package/dist/ui/assets/po-BiJDBrnU.js +1 -0
- package/dist/ui/assets/poimandres-DRFjx7u4.js +1 -0
- package/dist/ui/assets/polar-C7UOKdEL.js +1 -0
- package/dist/ui/assets/postcss-BXeXVLqQ.js +1 -0
- package/dist/ui/assets/powerquery-DNMTfnFr.js +1 -0
- package/dist/ui/assets/powershell-DshXNtvi.js +1 -0
- package/dist/ui/assets/prisma-BsRQq5mF.js +1 -0
- package/dist/ui/assets/prolog-iXnhIJG7.js +1 -0
- package/dist/ui/assets/proto-DB4EqR-F.js +1 -0
- package/dist/ui/assets/pug-z75UIWVK.js +1 -0
- package/dist/ui/assets/puppet-CDv2pdJW.js +1 -0
- package/dist/ui/assets/purescript-9MfHhQsQ.js +1 -0
- package/dist/ui/assets/python-gzcpVVnB.js +1 -0
- package/dist/ui/assets/qml-Cmm4coz1.js +1 -0
- package/dist/ui/assets/qmldir-DCQb3MpD.js +1 -0
- package/dist/ui/assets/qss-Fe1Jh2GI.js +1 -0
- package/dist/ui/assets/r-Brd0axBE.js +1 -0
- package/dist/ui/assets/racket-DcIDlBhZ.js +1 -0
- package/dist/ui/assets/raku-B3gFvitq.js +1 -0
- package/dist/ui/assets/razor-DSbwmoiu.js +1 -0
- package/dist/ui/assets/red-CJ3rzSJv.js +1 -0
- package/dist/ui/assets/reg-CRGYupPL.js +1 -0
- package/dist/ui/assets/regexp-Bs3Uk-pY.js +1 -0
- package/dist/ui/assets/rel-BtDbiS_P.js +1 -0
- package/dist/ui/assets/review-payload-CbwCBKB1.js +1580 -0
- package/dist/ui/assets/riscv-Ckw8ddFX.js +1 -0
- package/dist/ui/assets/ron-VUp2lXgN.js +1 -0
- package/dist/ui/assets/rose-pine-BthvhNj6.js +1 -0
- package/dist/ui/assets/rose-pine-dawn-Dg85fqjY.js +1 -0
- package/dist/ui/assets/rose-pine-moon-hon4tzzS.js +1 -0
- package/dist/ui/assets/rosmsg-CAekHB0j.js +1 -0
- package/dist/ui/assets/rst-ChIyKpUC.js +1 -0
- package/dist/ui/assets/ruby-BdAmvOMO.js +1 -0
- package/dist/ui/assets/rust-Cfkwpbl8.js +1 -0
- package/dist/ui/assets/sas-DpXn8hc5.js +1 -0
- package/dist/ui/assets/sass-DXrisJhu.js +1 -0
- package/dist/ui/assets/scala-DKOlJaKm.js +1 -0
- package/dist/ui/assets/scheme-DQCgrYNe.js +1 -0
- package/dist/ui/assets/scss-atX1Uuef.js +1 -0
- package/dist/ui/assets/sdbl-bTVj8UrX.js +1 -0
- package/dist/ui/assets/shaderlab-TOUzSsQk.js +1 -0
- package/dist/ui/assets/shellscript-BqgcAeMK.js +1 -0
- package/dist/ui/assets/shellsession-DfQSMRN5.js +1 -0
- package/dist/ui/assets/slack-dark-DnToyrRv.js +1 -0
- package/dist/ui/assets/slack-ochin-B2OO5cIa.js +1 -0
- package/dist/ui/assets/smalltalk-B16xEiuN.js +1 -0
- package/dist/ui/assets/snazzy-light-4G7pJPwS.js +1 -0
- package/dist/ui/assets/solarized-dark-DV17i1UV.js +1 -0
- package/dist/ui/assets/solarized-light-DSh2HLQt.js +1 -0
- package/dist/ui/assets/solidity-CKzVLygQ.js +1 -0
- package/dist/ui/assets/soy-DzP7zZTB.js +1 -0
- package/dist/ui/assets/sparql-D_iOobhT.js +1 -0
- package/dist/ui/assets/splunk-BC2Px7Mm.js +1 -0
- package/dist/ui/assets/sql-gmaLk66z.js +1 -0
- package/dist/ui/assets/ssh-config-BgfXC-Er.js +1 -0
- package/dist/ui/assets/stata-CJ0YTMcV.js +1 -0
- package/dist/ui/assets/stylus-B6D30XZt.js +1 -0
- package/dist/ui/assets/surrealql-LH7FciQz.js +1 -0
- package/dist/ui/assets/svelte-BME2PU6V.js +1 -0
- package/dist/ui/assets/swift-DonLKvLd.js +1 -0
- package/dist/ui/assets/synthwave-84-nFMaYfgc.js +1 -0
- package/dist/ui/assets/system-verilog-DJ5XKQeo.js +1 -0
- package/dist/ui/assets/systemd-BxMlprV5.js +1 -0
- package/dist/ui/assets/talonscript-CohzipZa.js +1 -0
- package/dist/ui/assets/tasl-DMoTqEGO.js +1 -0
- package/dist/ui/assets/tcl-CZd0xW_V.js +1 -0
- package/dist/ui/assets/templ-DHBNIfYW.js +1 -0
- package/dist/ui/assets/terraform-DswuEJGm.js +1 -0
- package/dist/ui/assets/tex-C8-kPEzO.js +1 -0
- package/dist/ui/assets/tokyo-night-oM2G3aXe.js +1 -0
- package/dist/ui/assets/toml-CcmNWLt0.js +1 -0
- package/dist/ui/assets/ts-tags-CiewV0vR.js +1 -0
- package/dist/ui/assets/tsv-sltzmVWM.js +1 -0
- package/dist/ui/assets/tsx-CUYPqmnq.js +1 -0
- package/dist/ui/assets/turtle-ByJddavk.js +1 -0
- package/dist/ui/assets/twig-BTkvsl0b.js +1 -0
- package/dist/ui/assets/typescript-DDQMaPwj.js +1 -0
- package/dist/ui/assets/typespec-B88KGewJ.js +1 -0
- package/dist/ui/assets/typst-DI99ib-x.js +1 -0
- package/dist/ui/assets/v-DETTlOr0.js +1 -0
- package/dist/ui/assets/vala-zf12oZj6.js +1 -0
- package/dist/ui/assets/vb-Djn5o6TS.js +1 -0
- package/dist/ui/assets/verilog-CiiDBU1e.js +1 -0
- package/dist/ui/assets/vesper-D5bVUKB1.js +1 -0
- package/dist/ui/assets/vhdl-BroJfC0k.js +1 -0
- package/dist/ui/assets/viml-DvXPmvsu.js +1 -0
- package/dist/ui/assets/vitesse-black-fwtXNY1n.js +1 -0
- package/dist/ui/assets/vitesse-dark-BZCL-v6S.js +1 -0
- package/dist/ui/assets/vitesse-light-VbXTXTou.js +1 -0
- package/dist/ui/assets/vue-BH5SEWRH.js +1 -0
- package/dist/ui/assets/vue-html-BT7yeYez.js +1 -0
- package/dist/ui/assets/vue-vine-Cs5IN2rs.js +1 -0
- package/dist/ui/assets/vyper-CgoNMtux.js +1 -0
- package/dist/ui/assets/wasm-BnjxR4X6.js +1 -0
- package/dist/ui/assets/wasm-ByWQv1Qj.js +1 -0
- package/dist/ui/assets/wenyan-C8pVoKbM.js +1 -0
- package/dist/ui/assets/wgsl-BsKzXJz4.js +1 -0
- package/dist/ui/assets/wikitext-ClFFjSW2.js +1 -0
- package/dist/ui/assets/wit-DdvCle-K.js +1 -0
- package/dist/ui/assets/wolfram-DLL8P-h_.js +1 -0
- package/dist/ui/assets/workspace-app-B_BnSEHO.css +1 -0
- package/dist/ui/assets/workspace-app-DSFpnrIC.js +115 -0
- package/dist/ui/assets/xml-D3_1BEcO.js +1 -0
- package/dist/ui/assets/xsl-BBOCDn4Y.js +1 -0
- package/dist/ui/assets/yaml-lThBDT45.js +1 -0
- package/dist/ui/assets/zenscript-BnlCZFoB.js +1 -0
- package/dist/ui/assets/zig-CMLA9XwU.js +1 -0
- package/dist/ui/workspace-app.html +15 -0
- package/dist/user-config.js +228 -0
- package/dist/verification.js +74 -0
- package/dist/workspace-store.js +133 -0
- package/dist/workspaces.js +433 -0
- package/docs/adr/devspace-x-implementation-adrs.md +159 -0
- package/docs/agent-profile-schema.md +203 -0
- package/docs/artifact-exchange.md +44 -0
- package/docs/assets/devspace-logo-light.png +0 -0
- package/docs/assets/devspace-screenshot.png +0 -0
- package/docs/assets/v11-review-ui-after.png +0 -0
- package/docs/assets/v11-review-ui-before.png +0 -0
- package/docs/chatgpt-coding-workflow.md +215 -0
- package/docs/configuration.md +217 -0
- package/docs/gotchas.md +294 -0
- package/docs/local-agent-daemon.md +101 -0
- package/docs/security.md +197 -0
- package/docs/setup.md +174 -0
- package/examples/agents/claude-implementer.md +30 -0
- package/examples/agents/codex-explorer.md +27 -0
- package/examples/agents/codex-qa-tester.md +29 -0
- package/examples/agents/codex-worker.md +25 -0
- package/examples/agents/copilot-reviewer.md +25 -0
- package/examples/agents/cursor-agent-worker.md +26 -0
- package/examples/agents/grok-builder.md +10 -0
- package/examples/agents/opencode-explorer.md +27 -0
- package/examples/agents/pi-reviewer.md +27 -0
- package/package.json +102 -0
- package/schema/v1/hearth.schema.json +529 -0
- package/scripts/copy-dashboard.mjs +9 -0
- package/scripts/fix-node-pty-permissions.mjs +22 -0
- package/scripts/generate-config-schema.ts +9 -0
- package/skills/subagents/SKILL.md +57 -0
|
@@ -0,0 +1,519 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
3
|
+
import { spawn } from "node:child_process";
|
|
4
|
+
import { createConnection } from "node:net";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
import { matchError, Result } from "better-result";
|
|
7
|
+
import { AgentDaemonInvalidResponseError, AgentDaemonProtocolMismatchError, AgentDaemonStartupError, AgentDaemonTimeoutError, AgentDaemonUnavailableError, agentErrorFromPayload, isAgentDaemonError, isProgrammerDefect, } from "./local-agent-errors.js";
|
|
8
|
+
import { decodeAgentRecord, decodeAgentRecordList, decodeDaemonLogs, decodeDaemonStatus, decodeLocalAgentDaemonResponse, encodeLocalAgentDaemonRequest, LocalAgentDaemonProtocolError, } from "./local-agent-daemon-protocol.js";
|
|
9
|
+
import { LOCAL_AGENT_DAEMON_PROTOCOL_VERSION, ensureLocalAgentDaemonSecret, isProcessAlive, localAgentDaemonPaths, readLocalAgentDaemonSecret, } from "./local-agent-daemon-lifecycle.js";
|
|
10
|
+
import { hearthConfigDir } from "./user-config.js";
|
|
11
|
+
const DEFAULT_STARTUP_TIMEOUT_MS = 8_000;
|
|
12
|
+
const DEFAULT_REQUEST_TIMEOUT_MS = 30_000;
|
|
13
|
+
const RETRY_DELAY_MS = 40;
|
|
14
|
+
export class LocalAgentClient {
|
|
15
|
+
stateDir;
|
|
16
|
+
paths;
|
|
17
|
+
endpoint;
|
|
18
|
+
startupTimeoutMs;
|
|
19
|
+
requestTimeoutMs;
|
|
20
|
+
spawnDaemon;
|
|
21
|
+
startupPromise;
|
|
22
|
+
constructor(options) {
|
|
23
|
+
this.stateDir = options.stateDir;
|
|
24
|
+
this.paths = localAgentDaemonPaths(options.stateDir);
|
|
25
|
+
this.endpoint = options.endpoint ?? this.paths.endpoint;
|
|
26
|
+
this.startupTimeoutMs = options.startupTimeoutMs ?? DEFAULT_STARTUP_TIMEOUT_MS;
|
|
27
|
+
this.requestTimeoutMs = options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
|
|
28
|
+
this.spawnDaemon = options.spawnDaemon ?? (() => spawnLocalAgentDaemon(options.configDir ?? hearthConfigDir()));
|
|
29
|
+
}
|
|
30
|
+
async run(input) {
|
|
31
|
+
return this.start(input);
|
|
32
|
+
}
|
|
33
|
+
async start(input) {
|
|
34
|
+
const result = await this.request("agent.start", input);
|
|
35
|
+
return decodeRequestResult(result, "agent.start", decodeAgentRecord);
|
|
36
|
+
}
|
|
37
|
+
async continue(agentId, prompt, overrides = {}, scope, timeoutMs) {
|
|
38
|
+
const result = await this.request("agent.continue", {
|
|
39
|
+
id: agentId,
|
|
40
|
+
prompt,
|
|
41
|
+
scope,
|
|
42
|
+
...(Object.keys(overrides).length > 0 ? { overrides } : {}),
|
|
43
|
+
...(timeoutMs !== undefined ? { timeoutMs } : {}),
|
|
44
|
+
});
|
|
45
|
+
return decodeRequestResult(result, "agent.continue", decodeAgentRecord);
|
|
46
|
+
}
|
|
47
|
+
async get(agentId, scope) {
|
|
48
|
+
const result = await this.request("agent.get", { id: agentId, scope });
|
|
49
|
+
return decodeRequestResult(result, "agent.get", decodeAgentRecord);
|
|
50
|
+
}
|
|
51
|
+
async list(scope) {
|
|
52
|
+
const result = await this.request("agent.list", scope);
|
|
53
|
+
return decodeRequestResult(result, "agent.list", decodeAgentRecordList);
|
|
54
|
+
}
|
|
55
|
+
async pause(agentId, scope, options = {}) {
|
|
56
|
+
const result = await this.request("agent.pause", {
|
|
57
|
+
id: agentId,
|
|
58
|
+
scope,
|
|
59
|
+
...(options.force ? { force: true } : {}),
|
|
60
|
+
});
|
|
61
|
+
return decodeRequestResult(result, "agent.pause", decodeAgentRecord);
|
|
62
|
+
}
|
|
63
|
+
async resume(agentId, scope, options = {}) {
|
|
64
|
+
const result = await this.request("agent.resume", {
|
|
65
|
+
id: agentId,
|
|
66
|
+
scope,
|
|
67
|
+
...(options.prompt ? { prompt: options.prompt } : {}),
|
|
68
|
+
...(options.overrides && Object.keys(options.overrides).length > 0 ? { overrides: options.overrides } : {}),
|
|
69
|
+
...(options.timeoutMs !== undefined ? { timeoutMs: options.timeoutMs } : {}),
|
|
70
|
+
});
|
|
71
|
+
return decodeRequestResult(result, "agent.resume", decodeAgentRecord);
|
|
72
|
+
}
|
|
73
|
+
async stopAgent(agentId, scope, options = {}) {
|
|
74
|
+
const result = await this.request("agent.stop", {
|
|
75
|
+
id: agentId,
|
|
76
|
+
scope,
|
|
77
|
+
...(options.force ? { force: true } : {}),
|
|
78
|
+
});
|
|
79
|
+
return decodeRequestResult(result, "agent.stop", decodeAgentRecord);
|
|
80
|
+
}
|
|
81
|
+
async status() {
|
|
82
|
+
const result = await this.requestExisting("daemon.status", {});
|
|
83
|
+
return decodeRequestResult(result, "daemon.status", decodeDaemonStatus);
|
|
84
|
+
}
|
|
85
|
+
async stop() {
|
|
86
|
+
const result = await this.requestExisting("daemon.stop", {});
|
|
87
|
+
return decodeRequestResult(result, "daemon.stop", decodeDaemonStatus);
|
|
88
|
+
}
|
|
89
|
+
async logs(lines = 200) {
|
|
90
|
+
const result = await this.requestExisting("daemon.logs", { lines });
|
|
91
|
+
return decodeRequestResult(result, "daemon.logs", decodeDaemonLogs);
|
|
92
|
+
}
|
|
93
|
+
async ensureReady() {
|
|
94
|
+
if (this.startupPromise)
|
|
95
|
+
return this.startupPromise;
|
|
96
|
+
this.startupPromise = this.ensureReadyInternal().finally(() => {
|
|
97
|
+
this.startupPromise = undefined;
|
|
98
|
+
});
|
|
99
|
+
return this.startupPromise;
|
|
100
|
+
}
|
|
101
|
+
async ensureReadyInternal() {
|
|
102
|
+
const existing = await this.tryHello();
|
|
103
|
+
if (existing.isErr())
|
|
104
|
+
return existing;
|
|
105
|
+
if (existing.value)
|
|
106
|
+
return Result.ok(existing.value);
|
|
107
|
+
try {
|
|
108
|
+
this.spawnDaemon();
|
|
109
|
+
}
|
|
110
|
+
catch (cause) {
|
|
111
|
+
return Result.err(new AgentDaemonStartupError({
|
|
112
|
+
code: "DAEMON_STARTUP_FAILURE",
|
|
113
|
+
operation: "startup",
|
|
114
|
+
retryable: true,
|
|
115
|
+
cause,
|
|
116
|
+
message: `Unable to start the local agent daemon in ${this.stateDir}.`,
|
|
117
|
+
}));
|
|
118
|
+
}
|
|
119
|
+
const deadline = Date.now() + this.startupTimeoutMs;
|
|
120
|
+
let lastError;
|
|
121
|
+
while (Date.now() < deadline) {
|
|
122
|
+
await delay(RETRY_DELAY_MS);
|
|
123
|
+
const ready = await this.tryHello();
|
|
124
|
+
if (ready.isErr()) {
|
|
125
|
+
lastError = ready.error;
|
|
126
|
+
if (ready.error.code === "DAEMON_PROTOCOL_MISMATCH"
|
|
127
|
+
|| ready.error.code === "DAEMON_INVALID_RESPONSE")
|
|
128
|
+
return ready;
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
if (ready.value)
|
|
132
|
+
return Result.ok(ready.value);
|
|
133
|
+
}
|
|
134
|
+
return Result.err(new AgentDaemonStartupError({
|
|
135
|
+
code: "DAEMON_STARTUP_FAILURE",
|
|
136
|
+
operation: "startup",
|
|
137
|
+
retryable: true,
|
|
138
|
+
cause: lastError,
|
|
139
|
+
message: `Unable to start the local agent daemon in ${this.stateDir}.`,
|
|
140
|
+
}));
|
|
141
|
+
}
|
|
142
|
+
async tryHello() {
|
|
143
|
+
const authToken = this.authTokenResult("hello");
|
|
144
|
+
if (authToken.isErr())
|
|
145
|
+
return authToken;
|
|
146
|
+
const response = await sendRequest(this.endpoint, {
|
|
147
|
+
requestId: randomUUID(),
|
|
148
|
+
protocolVersion: LOCAL_AGENT_DAEMON_PROTOCOL_VERSION,
|
|
149
|
+
authToken: authToken.value,
|
|
150
|
+
method: "hello",
|
|
151
|
+
params: {},
|
|
152
|
+
}, this.requestTimeoutMs);
|
|
153
|
+
if (response.isErr()) {
|
|
154
|
+
if (response.error.code === "DAEMON_UNAVAILABLE"
|
|
155
|
+
|| response.error.code === "DAEMON_TIMEOUT")
|
|
156
|
+
return Result.ok(undefined);
|
|
157
|
+
return response;
|
|
158
|
+
}
|
|
159
|
+
if (!response.value.ok) {
|
|
160
|
+
const error = decodeRemoteError(response.value.error, "hello");
|
|
161
|
+
if (!isAgentDaemonError(error)) {
|
|
162
|
+
return Result.err(new AgentDaemonInvalidResponseError({
|
|
163
|
+
code: "DAEMON_INVALID_RESPONSE",
|
|
164
|
+
operation: "hello",
|
|
165
|
+
retryable: false,
|
|
166
|
+
cause: response.value.error,
|
|
167
|
+
message: "Local agent daemon returned an invalid hello error.",
|
|
168
|
+
}));
|
|
169
|
+
}
|
|
170
|
+
if (error.code === "DAEMON_PROTOCOL_MISMATCH"
|
|
171
|
+
&& response.value.protocolVersion < LOCAL_AGENT_DAEMON_PROTOCOL_VERSION) {
|
|
172
|
+
return this.replaceIdleOlderDaemon(authToken.value, response.value.protocolVersion, error);
|
|
173
|
+
}
|
|
174
|
+
return error.code === "DAEMON_UNAVAILABLE" ? Result.ok(undefined) : Result.err(error);
|
|
175
|
+
}
|
|
176
|
+
const decoded = decodeValue(response.value.result, "hello", decodeDaemonStatus);
|
|
177
|
+
return decoded.map((status) => status.state === "ready" ? status : undefined);
|
|
178
|
+
}
|
|
179
|
+
async replaceIdleOlderDaemon(authToken, protocolVersion, mismatch) {
|
|
180
|
+
const statusResponse = await sendRequest(this.endpoint, {
|
|
181
|
+
requestId: randomUUID(),
|
|
182
|
+
protocolVersion,
|
|
183
|
+
authToken,
|
|
184
|
+
method: "hello",
|
|
185
|
+
params: {},
|
|
186
|
+
}, this.requestTimeoutMs);
|
|
187
|
+
if (statusResponse.isErr() || !statusResponse.value.ok)
|
|
188
|
+
return Result.err(mismatch);
|
|
189
|
+
const status = decodeValue(statusResponse.value.result, "hello", decodeDaemonStatus);
|
|
190
|
+
if (status.isErr())
|
|
191
|
+
return status;
|
|
192
|
+
if (status.value.activeTurns > 0) {
|
|
193
|
+
return Result.err(new AgentDaemonProtocolMismatchError({
|
|
194
|
+
code: "DAEMON_PROTOCOL_MISMATCH",
|
|
195
|
+
operation: "startup",
|
|
196
|
+
retryable: true,
|
|
197
|
+
cause: mismatch,
|
|
198
|
+
message: "An older local agent daemon is still running active turns. Retry after they finish.",
|
|
199
|
+
}));
|
|
200
|
+
}
|
|
201
|
+
const stopResponse = await sendRequest(this.endpoint, {
|
|
202
|
+
requestId: randomUUID(),
|
|
203
|
+
protocolVersion,
|
|
204
|
+
authToken,
|
|
205
|
+
method: "daemon.stop",
|
|
206
|
+
params: {},
|
|
207
|
+
}, this.requestTimeoutMs);
|
|
208
|
+
if (stopResponse.isErr() || !stopResponse.value.ok)
|
|
209
|
+
return Result.err(mismatch);
|
|
210
|
+
const deadline = Date.now() + this.startupTimeoutMs;
|
|
211
|
+
while (Date.now() < deadline) {
|
|
212
|
+
await delay(RETRY_DELAY_MS);
|
|
213
|
+
const probe = await sendRequest(this.endpoint, {
|
|
214
|
+
requestId: randomUUID(),
|
|
215
|
+
protocolVersion,
|
|
216
|
+
authToken,
|
|
217
|
+
method: "hello",
|
|
218
|
+
params: {},
|
|
219
|
+
}, Math.min(this.requestTimeoutMs, 250));
|
|
220
|
+
if (probe.isErr() && probe.error.code === "DAEMON_UNAVAILABLE") {
|
|
221
|
+
if (!existsSync(this.paths.lockPath) || !isProcessAlive(status.value.pid)) {
|
|
222
|
+
return Result.ok(undefined);
|
|
223
|
+
}
|
|
224
|
+
continue;
|
|
225
|
+
}
|
|
226
|
+
if (probe.isOk()
|
|
227
|
+
&& probe.value.protocolVersion >= LOCAL_AGENT_DAEMON_PROTOCOL_VERSION) {
|
|
228
|
+
// Another client completed the replacement while this client was
|
|
229
|
+
// waiting for the old endpoint to disappear.
|
|
230
|
+
return this.tryHello();
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
return Result.err(new AgentDaemonStartupError({
|
|
234
|
+
code: "DAEMON_STARTUP_FAILURE",
|
|
235
|
+
operation: "startup",
|
|
236
|
+
retryable: true,
|
|
237
|
+
cause: mismatch,
|
|
238
|
+
message: "The older local agent daemon did not stop in time for the upgrade.",
|
|
239
|
+
}));
|
|
240
|
+
}
|
|
241
|
+
async request(method, params) {
|
|
242
|
+
const ready = await this.ensureReady();
|
|
243
|
+
if (ready.isErr())
|
|
244
|
+
return ready;
|
|
245
|
+
const authToken = this.authTokenResult(method);
|
|
246
|
+
if (authToken.isErr())
|
|
247
|
+
return authToken;
|
|
248
|
+
const response = await sendRequest(this.endpoint, {
|
|
249
|
+
requestId: randomUUID(),
|
|
250
|
+
protocolVersion: LOCAL_AGENT_DAEMON_PROTOCOL_VERSION,
|
|
251
|
+
authToken: authToken.value,
|
|
252
|
+
method,
|
|
253
|
+
params,
|
|
254
|
+
}, this.requestTimeoutMs);
|
|
255
|
+
if (response.isErr())
|
|
256
|
+
return response;
|
|
257
|
+
if (!response.value.ok) {
|
|
258
|
+
const error = decodeRemoteError(response.value.error, method);
|
|
259
|
+
if (!isRequestError(method, error)) {
|
|
260
|
+
return Result.err(new AgentDaemonInvalidResponseError({
|
|
261
|
+
code: "DAEMON_INVALID_RESPONSE",
|
|
262
|
+
operation: method,
|
|
263
|
+
retryable: false,
|
|
264
|
+
cause: response.value.error,
|
|
265
|
+
message: "Local agent daemon returned an error that is invalid for this request.",
|
|
266
|
+
}));
|
|
267
|
+
}
|
|
268
|
+
return Result.err(error);
|
|
269
|
+
}
|
|
270
|
+
return Result.ok(response.value.result);
|
|
271
|
+
}
|
|
272
|
+
async requestExisting(method, params) {
|
|
273
|
+
const authToken = this.existingAuthTokenResult(method);
|
|
274
|
+
if (authToken.isErr())
|
|
275
|
+
return authToken;
|
|
276
|
+
if (!authToken.value) {
|
|
277
|
+
return Result.err(new AgentDaemonUnavailableError({
|
|
278
|
+
code: "DAEMON_UNAVAILABLE",
|
|
279
|
+
operation: method,
|
|
280
|
+
retryable: true,
|
|
281
|
+
message: "Local agent daemon is not running.",
|
|
282
|
+
}));
|
|
283
|
+
}
|
|
284
|
+
const response = await sendRequest(this.endpoint, {
|
|
285
|
+
requestId: randomUUID(),
|
|
286
|
+
protocolVersion: LOCAL_AGENT_DAEMON_PROTOCOL_VERSION,
|
|
287
|
+
authToken: authToken.value,
|
|
288
|
+
method,
|
|
289
|
+
params,
|
|
290
|
+
}, this.requestTimeoutMs);
|
|
291
|
+
if (response.isErr())
|
|
292
|
+
return response;
|
|
293
|
+
if (!response.value.ok) {
|
|
294
|
+
const error = decodeRemoteError(response.value.error, method);
|
|
295
|
+
if (isAgentDaemonError(error))
|
|
296
|
+
return Result.err(error);
|
|
297
|
+
return Result.err(new AgentDaemonInvalidResponseError({
|
|
298
|
+
code: "DAEMON_INVALID_RESPONSE",
|
|
299
|
+
operation: method,
|
|
300
|
+
retryable: false,
|
|
301
|
+
cause: response.value.error,
|
|
302
|
+
message: "Local agent daemon returned an invalid daemon-control error.",
|
|
303
|
+
}));
|
|
304
|
+
}
|
|
305
|
+
return Result.ok(response.value.result);
|
|
306
|
+
}
|
|
307
|
+
authTokenResult(operation) {
|
|
308
|
+
try {
|
|
309
|
+
return Result.ok(ensureLocalAgentDaemonSecret(this.paths));
|
|
310
|
+
}
|
|
311
|
+
catch (cause) {
|
|
312
|
+
if (isProgrammerDefect(cause))
|
|
313
|
+
throw cause;
|
|
314
|
+
return Result.err(new AgentDaemonUnavailableError({
|
|
315
|
+
code: "DAEMON_UNAVAILABLE",
|
|
316
|
+
operation,
|
|
317
|
+
retryable: false,
|
|
318
|
+
cause,
|
|
319
|
+
message: "Local agent daemon credentials are unavailable.",
|
|
320
|
+
}));
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
existingAuthTokenResult(operation) {
|
|
324
|
+
try {
|
|
325
|
+
return Result.ok(readLocalAgentDaemonSecret(this.paths));
|
|
326
|
+
}
|
|
327
|
+
catch (cause) {
|
|
328
|
+
if (isProgrammerDefect(cause))
|
|
329
|
+
throw cause;
|
|
330
|
+
return Result.err(new AgentDaemonUnavailableError({
|
|
331
|
+
code: "DAEMON_UNAVAILABLE",
|
|
332
|
+
operation,
|
|
333
|
+
retryable: false,
|
|
334
|
+
cause,
|
|
335
|
+
message: "Local agent daemon credentials are unavailable.",
|
|
336
|
+
}));
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
export function createLocalAgentClient(config) {
|
|
341
|
+
return new LocalAgentClient({ configDir: config.configDir, stateDir: config.stateDir });
|
|
342
|
+
}
|
|
343
|
+
export function spawnLocalAgentDaemon(configDir, env = process.env) {
|
|
344
|
+
const entrypoint = resolveDaemonEntrypoint();
|
|
345
|
+
const child = spawn(process.execPath, [...daemonExecArgv(process.execArgv), entrypoint], {
|
|
346
|
+
detached: true,
|
|
347
|
+
stdio: "ignore",
|
|
348
|
+
windowsHide: true,
|
|
349
|
+
env: localAgentDaemonEnvironment(configDir, env),
|
|
350
|
+
});
|
|
351
|
+
// The detached child is unref()d immediately; without an error listener a
|
|
352
|
+
// spawn failure (missing interpreter/entrypoint) escalates to an uncaught
|
|
353
|
+
// exception in this process instead of surfacing as a hello timeout.
|
|
354
|
+
child.on("error", () => undefined);
|
|
355
|
+
child.unref();
|
|
356
|
+
}
|
|
357
|
+
export function localAgentDaemonEnvironment(configDir, env) {
|
|
358
|
+
return { ...env, HEARTH_CONFIG_DIR: configDir };
|
|
359
|
+
}
|
|
360
|
+
export function daemonExecArgv(execArgv) {
|
|
361
|
+
const result = [];
|
|
362
|
+
for (let index = 0; index < execArgv.length; index += 1) {
|
|
363
|
+
const argument = execArgv[index];
|
|
364
|
+
if (/^--inspect(?:-brk|-wait)?(?:=.*)?$/.test(argument))
|
|
365
|
+
continue;
|
|
366
|
+
if (argument === "--inspect-port") {
|
|
367
|
+
index += 1;
|
|
368
|
+
continue;
|
|
369
|
+
}
|
|
370
|
+
if (argument.startsWith("--inspect-port="))
|
|
371
|
+
continue;
|
|
372
|
+
result.push(argument);
|
|
373
|
+
}
|
|
374
|
+
return result;
|
|
375
|
+
}
|
|
376
|
+
export function resolveDaemonEntrypoint() {
|
|
377
|
+
const compiled = fileURLToPath(new URL("./local-agent-daemon-main.js", import.meta.url));
|
|
378
|
+
if (existsSync(compiled))
|
|
379
|
+
return compiled;
|
|
380
|
+
return fileURLToPath(new URL("./local-agent-daemon-main.ts", import.meta.url));
|
|
381
|
+
}
|
|
382
|
+
async function sendRequest(endpoint, request, timeoutMs) {
|
|
383
|
+
return new Promise((resolve) => {
|
|
384
|
+
const socket = createConnection(endpoint);
|
|
385
|
+
let buffer = "";
|
|
386
|
+
let settled = false;
|
|
387
|
+
const timer = setTimeout(() => {
|
|
388
|
+
finish(Result.err(new AgentDaemonTimeoutError({
|
|
389
|
+
code: "DAEMON_TIMEOUT",
|
|
390
|
+
operation: request.method,
|
|
391
|
+
retryable: true,
|
|
392
|
+
message: "Timed out waiting for the local agent daemon.",
|
|
393
|
+
})), true);
|
|
394
|
+
}, timeoutMs);
|
|
395
|
+
const finish = (result, destroy = false) => {
|
|
396
|
+
if (settled)
|
|
397
|
+
return;
|
|
398
|
+
settled = true;
|
|
399
|
+
clearTimeout(timer);
|
|
400
|
+
if (destroy)
|
|
401
|
+
socket.destroy();
|
|
402
|
+
resolve(result);
|
|
403
|
+
};
|
|
404
|
+
socket.setEncoding("utf8");
|
|
405
|
+
socket.on("data", (chunk) => {
|
|
406
|
+
buffer += chunk.toString();
|
|
407
|
+
const newline = buffer.indexOf("\n");
|
|
408
|
+
if (newline === -1)
|
|
409
|
+
return;
|
|
410
|
+
try {
|
|
411
|
+
const response = decodeLocalAgentDaemonResponse(JSON.parse(buffer.slice(0, newline)));
|
|
412
|
+
if (response.requestId !== request.requestId) {
|
|
413
|
+
throw new LocalAgentDaemonProtocolError("INVALID_RESPONSE", "Daemon response request id did not match.");
|
|
414
|
+
}
|
|
415
|
+
finish(Result.ok(response));
|
|
416
|
+
socket.end();
|
|
417
|
+
}
|
|
418
|
+
catch (cause) {
|
|
419
|
+
finish(Result.err(new AgentDaemonInvalidResponseError({
|
|
420
|
+
code: "DAEMON_INVALID_RESPONSE",
|
|
421
|
+
operation: request.method,
|
|
422
|
+
retryable: false,
|
|
423
|
+
cause,
|
|
424
|
+
message: "Local agent daemon returned an invalid response.",
|
|
425
|
+
})), true);
|
|
426
|
+
}
|
|
427
|
+
});
|
|
428
|
+
socket.once("error", (cause) => finish(Result.err(new AgentDaemonUnavailableError({
|
|
429
|
+
code: "DAEMON_UNAVAILABLE",
|
|
430
|
+
operation: request.method,
|
|
431
|
+
retryable: true,
|
|
432
|
+
cause,
|
|
433
|
+
message: "Local agent daemon is unavailable.",
|
|
434
|
+
}))));
|
|
435
|
+
socket.once("close", () => {
|
|
436
|
+
if (!settled) {
|
|
437
|
+
finish(Result.err(new AgentDaemonUnavailableError({
|
|
438
|
+
code: "DAEMON_UNAVAILABLE",
|
|
439
|
+
operation: request.method,
|
|
440
|
+
retryable: true,
|
|
441
|
+
message: "Local agent daemon closed the connection.",
|
|
442
|
+
})));
|
|
443
|
+
}
|
|
444
|
+
});
|
|
445
|
+
socket.once("connect", () => socket.write(encodeLocalAgentDaemonRequest(request)));
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
function decodeRequestResult(result, operation, decode) {
|
|
449
|
+
return result.andThen((value) => decodeValue(value, operation, decode));
|
|
450
|
+
}
|
|
451
|
+
function decodeValue(value, operation, decode) {
|
|
452
|
+
try {
|
|
453
|
+
return Result.ok(decode(value));
|
|
454
|
+
}
|
|
455
|
+
catch (cause) {
|
|
456
|
+
return Result.err(new AgentDaemonInvalidResponseError({
|
|
457
|
+
code: "DAEMON_INVALID_RESPONSE",
|
|
458
|
+
operation,
|
|
459
|
+
retryable: false,
|
|
460
|
+
cause,
|
|
461
|
+
message: "Local agent daemon returned an invalid response.",
|
|
462
|
+
}));
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
function decodeRemoteError(payload, operation) {
|
|
466
|
+
const decoded = agentErrorFromPayload(payload);
|
|
467
|
+
return decoded ?? new AgentDaemonInvalidResponseError({
|
|
468
|
+
code: "DAEMON_INVALID_RESPONSE",
|
|
469
|
+
operation,
|
|
470
|
+
retryable: false,
|
|
471
|
+
cause: payload,
|
|
472
|
+
message: "Local agent daemon returned an unknown error code.",
|
|
473
|
+
});
|
|
474
|
+
}
|
|
475
|
+
function isRequestError(method, error) {
|
|
476
|
+
const category = matchError(error, {
|
|
477
|
+
AgentTargetError: () => "target",
|
|
478
|
+
AgentConflictError: () => "conflict",
|
|
479
|
+
AgentScopeError: () => "scope",
|
|
480
|
+
AgentProviderUnavailableError: () => "provider",
|
|
481
|
+
AgentProviderCancelledError: () => "provider",
|
|
482
|
+
AgentProviderProtocolError: () => "provider",
|
|
483
|
+
AgentProviderExecutionError: () => "provider",
|
|
484
|
+
AgentDaemonUnavailableError: () => "daemon",
|
|
485
|
+
AgentDaemonStartupError: () => "daemon",
|
|
486
|
+
AgentDaemonTimeoutError: () => "daemon",
|
|
487
|
+
AgentDaemonProtocolMismatchError: () => "daemon",
|
|
488
|
+
AgentDaemonUnauthorizedError: () => "daemon",
|
|
489
|
+
AgentDaemonInvalidRequestError: () => "daemon",
|
|
490
|
+
AgentDaemonInvalidResponseError: () => "daemon",
|
|
491
|
+
AgentDaemonInternalError: () => "daemon",
|
|
492
|
+
AgentStoreError: () => "store",
|
|
493
|
+
});
|
|
494
|
+
if (category === "daemon")
|
|
495
|
+
return true;
|
|
496
|
+
switch (method) {
|
|
497
|
+
case "agent.start":
|
|
498
|
+
case "agent.continue":
|
|
499
|
+
case "agent.pause":
|
|
500
|
+
case "agent.resume":
|
|
501
|
+
case "agent.stop":
|
|
502
|
+
return category === "target"
|
|
503
|
+
|| category === "scope"
|
|
504
|
+
|| category === "conflict"
|
|
505
|
+
|| category === "store";
|
|
506
|
+
case "agent.get":
|
|
507
|
+
return category === "target" || category === "scope" || category === "store";
|
|
508
|
+
case "agent.list":
|
|
509
|
+
return category === "scope" || category === "store";
|
|
510
|
+
case "hello":
|
|
511
|
+
case "daemon.status":
|
|
512
|
+
case "daemon.stop":
|
|
513
|
+
case "daemon.logs":
|
|
514
|
+
return false;
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
function delay(ms) {
|
|
518
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
519
|
+
}
|