@neuralnomads/codenomad-dev 0.10.3-dev-20260213-ba418a85
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 +126 -0
- package/dist/api-types.js +1 -0
- package/dist/auth/auth-store.js +134 -0
- package/dist/auth/http-auth.js +37 -0
- package/dist/auth/manager.js +128 -0
- package/dist/auth/password-hash.js +32 -0
- package/dist/auth/session-manager.js +17 -0
- package/dist/auth/token-manager.js +27 -0
- package/dist/background-processes/manager.js +437 -0
- package/dist/bin.js +24 -0
- package/dist/config/binaries.js +148 -0
- package/dist/config/location.js +57 -0
- package/dist/config/schema.js +70 -0
- package/dist/config/store.js +200 -0
- package/dist/events/bus.js +41 -0
- package/dist/filesystem/__tests__/search-cache.test.js +40 -0
- package/dist/filesystem/browser.js +285 -0
- package/dist/filesystem/search-cache.js +43 -0
- package/dist/filesystem/search.js +135 -0
- package/dist/index.js +410 -0
- package/dist/integrations/github/bot-signature.js +11 -0
- package/dist/integrations/github/git-ops.js +133 -0
- package/dist/integrations/github/github-types.js +1 -0
- package/dist/integrations/github/job-runner.js +608 -0
- package/dist/integrations/github/octokit.js +58 -0
- package/dist/integrations/github/sanitize-webhook.js +42 -0
- package/dist/integrations/github/webhook-verify.js +21 -0
- package/dist/integrations/github/workspace-context.js +10 -0
- package/dist/integrations/github/worktree-context.js +15 -0
- package/dist/launcher.js +149 -0
- package/dist/loader.js +21 -0
- package/dist/logger.js +109 -0
- package/dist/opencode/request-context.js +39 -0
- package/dist/opencode/worktree-directory.js +42 -0
- package/dist/opencode-config/README.md +32 -0
- package/dist/opencode-config/opencode.jsonc +3 -0
- package/dist/opencode-config/package.json +9 -0
- package/dist/opencode-config/plugin/codenomad.ts +32 -0
- package/dist/opencode-config/plugin/lib/background-process.ts +253 -0
- package/dist/opencode-config/plugin/lib/client.ts +133 -0
- package/dist/opencode-config/plugin/lib/request.ts +214 -0
- package/dist/opencode-config-template/README.md +32 -0
- package/dist/opencode-config-template/opencode.jsonc +3 -0
- package/dist/opencode-config-template/plugin/codenomad.ts +40 -0
- package/dist/opencode-config-template/plugin/lib/background-process.ts +160 -0
- package/dist/opencode-config-template/plugin/lib/client.ts +165 -0
- package/dist/opencode-config.js +26 -0
- package/dist/plugins/channel.js +40 -0
- package/dist/plugins/handlers.js +17 -0
- package/dist/releases/dev-release-monitor.js +75 -0
- package/dist/releases/release-monitor.js +107 -0
- package/dist/server/http-server.js +547 -0
- package/dist/server/network-addresses.js +72 -0
- package/dist/server/routes/auth-pages/login.html +134 -0
- package/dist/server/routes/auth-pages/token.html +93 -0
- package/dist/server/routes/auth.js +134 -0
- package/dist/server/routes/background-processes.js +60 -0
- package/dist/server/routes/config.js +59 -0
- package/dist/server/routes/events.js +43 -0
- package/dist/server/routes/filesystem.js +43 -0
- package/dist/server/routes/github-plugin.js +215 -0
- package/dist/server/routes/github-webhook.js +32 -0
- package/dist/server/routes/meta.js +47 -0
- package/dist/server/routes/plugin.js +52 -0
- package/dist/server/routes/storage.js +52 -0
- package/dist/server/routes/workspaces.js +89 -0
- package/dist/server/routes/worktrees.js +156 -0
- package/dist/server/tls.js +224 -0
- package/dist/storage/instance-store.js +56 -0
- package/dist/ui/__tests__/remote-ui.test.js +46 -0
- package/dist/ui/remote-ui.js +462 -0
- package/dist/workspaces/git-worktrees.js +199 -0
- package/dist/workspaces/instance-events.js +180 -0
- package/dist/workspaces/manager.js +375 -0
- package/dist/workspaces/opencode-auth.js +16 -0
- package/dist/workspaces/runtime.js +346 -0
- package/dist/workspaces/worktree-map.js +116 -0
- package/package.json +49 -0
- package/public/apple-touch-icon-180x180.png +0 -0
- package/public/assets/CodeNomad-Icon-bmTWNPXy.png +0 -0
- package/public/assets/abap-BdImnpbu.js +1 -0
- package/public/assets/actionscript-3-CfeIJUat.js +1 -0
- package/public/assets/ada-bCR0ucgS.js +1 -0
- package/public/assets/andromeeda-C-Jbm3Hp.js +1 -0
- package/public/assets/angular-html-CU67Zn6k.js +1 -0
- package/public/assets/angular-ts-BwZT4LLn.js +1 -0
- package/public/assets/apache-Pmp26Uib.js +1 -0
- package/public/assets/apex-DhZLUxFE.js +1 -0
- package/public/assets/apl-dKokRX4l.js +1 -0
- package/public/assets/applescript-Co6uUVPk.js +1 -0
- package/public/assets/ara-BRHolxvo.js +1 -0
- package/public/assets/asciidoc-Dv7Oe6Be.js +1 -0
- package/public/assets/asm-D_Q5rh1f.js +1 -0
- package/public/assets/astro-CbQHKStN.js +1 -0
- package/public/assets/aurora-x-D-2ljcwZ.js +1 -0
- package/public/assets/awk-DMzUqQB5.js +1 -0
- package/public/assets/ayu-dark-Cv9koXgw.js +1 -0
- package/public/assets/ballerina-BFfxhgS-.js +1 -0
- package/public/assets/bat-BkioyH1T.js +1 -0
- package/public/assets/beancount-k_qm7-4y.js +1 -0
- package/public/assets/berry-D08WgyRC.js +1 -0
- package/public/assets/bibtex-CHM0blh-.js +1 -0
- package/public/assets/bicep-Bmn6On1c.js +1 -0
- package/public/assets/blade-DVc8C-J4.js +1 -0
- package/public/assets/bsl-BO_Y6i37.js +1 -0
- package/public/assets/c-BIGW1oBm.js +1 -0
- package/public/assets/cadence-Bv_4Rxtq.js +1 -0
- package/public/assets/cairo-KRGpt6FW.js +1 -0
- package/public/assets/catppuccin-frappe-DFWUc33u.js +1 -0
- package/public/assets/catppuccin-latte-C9dUb6Cb.js +1 -0
- package/public/assets/catppuccin-macchiato-DQyhUUbL.js +1 -0
- package/public/assets/catppuccin-mocha-D87Tk5Gz.js +1 -0
- package/public/assets/clarity-D53aC0YG.js +1 -0
- package/public/assets/clojure-P80f7IUj.js +1 -0
- package/public/assets/cmake-D1j8_8rp.js +1 -0
- package/public/assets/cobol-nwyudZeR.js +1 -0
- package/public/assets/codeowners-Bp6g37R7.js +1 -0
- package/public/assets/codeql-DsOJ9woJ.js +1 -0
- package/public/assets/coffee-Ch7k5sss.js +1 -0
- package/public/assets/common-lisp-Cg-RD9OK.js +1 -0
- package/public/assets/coq-DkFqJrB1.js +1 -0
- package/public/assets/core-BSTVzpXI.js +1 -0
- package/public/assets/cpp-CofmeUqb.js +1 -0
- package/public/assets/crystal-tKQVLTB8.js +1 -0
- package/public/assets/csharp-CX12Zw3r.js +1 -0
- package/public/assets/css-DPfMkruS.js +1 -0
- package/public/assets/csv-fuZLfV_i.js +1 -0
- package/public/assets/cue-D82EKSYY.js +1 -0
- package/public/assets/cypher-COkxafJQ.js +1 -0
- package/public/assets/d-85-TOEBH.js +1 -0
- package/public/assets/dark-plus-eOWES_5F.js +1 -0
- package/public/assets/dart-CF10PKvl.js +1 -0
- package/public/assets/dax-CEL-wOlO.js +1 -0
- package/public/assets/desktop-BmXAJ9_W.js +1 -0
- package/public/assets/diff-D97Zzqfu.js +1 -0
- package/public/assets/docker-BcOcwvcX.js +1 -0
- package/public/assets/dotenv-Da5cRb03.js +1 -0
- package/public/assets/dracula-BzJJZx-M.js +1 -0
- package/public/assets/dracula-soft-BXkSAIEj.js +1 -0
- package/public/assets/dream-maker-BtqSS_iP.js +1 -0
- package/public/assets/edge-BkV0erSs.js +1 -0
- package/public/assets/elixir-CDX3lj18.js +1 -0
- package/public/assets/elm-DbKCFpqz.js +1 -0
- package/public/assets/emacs-lisp-C9XAeP06.js +1 -0
- package/public/assets/erb-BOJIQeun.js +1 -0
- package/public/assets/erlang-DsQrWhSR.js +1 -0
- package/public/assets/everforest-dark-BgDCqdQA.js +1 -0
- package/public/assets/everforest-light-C8M2exoo.js +1 -0
- package/public/assets/fennel-BYunw83y.js +1 -0
- package/public/assets/fish-BvzEVeQv.js +1 -0
- package/public/assets/fluent-C4IJs8-o.js +1 -0
- package/public/assets/fortran-fixed-form-BZjJHVRy.js +1 -0
- package/public/assets/fortran-free-form-D22FLkUw.js +1 -0
- package/public/assets/fsharp-CXgrBDvD.js +1 -0
- package/public/assets/gdresource-B7Tvp0Sc.js +1 -0
- package/public/assets/gdscript-DTMYz4Jt.js +1 -0
- package/public/assets/gdshader-DkwncUOv.js +1 -0
- package/public/assets/genie-D0YGMca9.js +1 -0
- package/public/assets/gherkin-DyxjwDmM.js +1 -0
- package/public/assets/git-commit-F4YmCXRG.js +1 -0
- package/public/assets/git-rebase-r7XF79zn.js +1 -0
- package/public/assets/github-dark-DHJKELXO.js +1 -0
- package/public/assets/github-dark-default-Cuk6v7N8.js +1 -0
- package/public/assets/github-dark-dimmed-DH5Ifo-i.js +1 -0
- package/public/assets/github-dark-high-contrast-E3gJ1_iC.js +1 -0
- package/public/assets/github-light-DAi9KRSo.js +1 -0
- package/public/assets/github-light-default-D7oLnXFd.js +1 -0
- package/public/assets/github-light-high-contrast-BfjtVDDH.js +1 -0
- package/public/assets/gleam-BspZqrRM.js +1 -0
- package/public/assets/glimmer-js-Rg0-pVw9.js +1 -0
- package/public/assets/glimmer-ts-U6CK756n.js +1 -0
- package/public/assets/glsl-DplSGwfg.js +1 -0
- package/public/assets/gnuplot-DdkO51Og.js +1 -0
- package/public/assets/go-Dn2_MT6a.js +1 -0
- package/public/assets/graphql-ChdNCCLP.js +1 -0
- package/public/assets/groovy-gcz8RCvz.js +1 -0
- package/public/assets/gruvbox-dark-hard-CFHQjOhq.js +1 -0
- package/public/assets/gruvbox-dark-medium-GsRaNv29.js +1 -0
- package/public/assets/gruvbox-dark-soft-CVdnzihN.js +1 -0
- package/public/assets/gruvbox-light-hard-CH1njM8p.js +1 -0
- package/public/assets/gruvbox-light-medium-DRw_LuNl.js +1 -0
- package/public/assets/gruvbox-light-soft-hJgmCMqR.js +1 -0
- package/public/assets/hack-CaT9iCJl.js +1 -0
- package/public/assets/haml-B8DHNrY2.js +1 -0
- package/public/assets/handlebars-BL8al0AC.js +1 -0
- package/public/assets/haskell-Df6bDoY_.js +1 -0
- package/public/assets/haxe-CzTSHFRz.js +1 -0
- package/public/assets/hcl-BWvSN4gD.js +1 -0
- package/public/assets/hjson-D5-asLiD.js +1 -0
- package/public/assets/hlsl-D3lLCCz7.js +1 -0
- package/public/assets/houston-DnULxvSX.js +1 -0
- package/public/assets/html-GMplVEZG.js +1 -0
- package/public/assets/html-derivative-BFtXZ54Q.js +1 -0
- package/public/assets/http-jrhK8wxY.js +1 -0
- package/public/assets/hurl-irOxFIW8.js +1 -0
- package/public/assets/hxml-Bvhsp5Yf.js +1 -0
- package/public/assets/hy-DFXneXwc.js +1 -0
- package/public/assets/imba-DGztddWO.js +1 -0
- package/public/assets/index-D4PT0yE4.js +1 -0
- package/public/assets/index-DN20ggb1.js +1 -0
- package/public/assets/index-DdQ7zIzB.js +1 -0
- package/public/assets/index-Dl-rJJuP.js +1 -0
- package/public/assets/index-Dlo2gDiy.css +1 -0
- package/public/assets/ini-BEwlwnbL.js +1 -0
- package/public/assets/java-CylS5w8V.js +1 -0
- package/public/assets/javascript-wDzz0qaB.js +1 -0
- package/public/assets/jinja-4LBKfQ-Z.js +1 -0
- package/public/assets/jison-wvAkD_A8.js +1 -0
- package/public/assets/json-Cp-IABpG.js +1 -0
- package/public/assets/json5-C9tS-k6U.js +1 -0
- package/public/assets/jsonc-Des-eS-w.js +1 -0
- package/public/assets/jsonl-DcaNXYhu.js +1 -0
- package/public/assets/jsonnet-DFQXde-d.js +1 -0
- package/public/assets/jssm-C2t-YnRu.js +1 -0
- package/public/assets/jsx-g9-lgVsj.js +1 -0
- package/public/assets/julia-C8NyazO9.js +1 -0
- package/public/assets/kanagawa-dragon-CkXjmgJE.js +1 -0
- package/public/assets/kanagawa-lotus-CfQXZHmo.js +1 -0
- package/public/assets/kanagawa-wave-DWedfzmr.js +1 -0
- package/public/assets/kdl-DV7GczEv.js +1 -0
- package/public/assets/kotlin-BdnUsdx6.js +1 -0
- package/public/assets/kusto-BvAqAH-y.js +1 -0
- package/public/assets/laserwave-DUszq2jm.js +1 -0
- package/public/assets/latex-BUKiar2Z.js +1 -0
- package/public/assets/lean-DP1Csr6i.js +1 -0
- package/public/assets/less-B1dDrJ26.js +1 -0
- package/public/assets/light-plus-B7mTdjB0.js +1 -0
- package/public/assets/liquid-DYVedYrR.js +1 -0
- package/public/assets/llvm-BtvRca6l.js +1 -0
- package/public/assets/loading-CmEVQgyj.css +1 -0
- package/public/assets/loading-DgqIiz-T.js +1 -0
- package/public/assets/log-2UxHyX5q.js +1 -0
- package/public/assets/logo-BtOb2qkB.js +1 -0
- package/public/assets/lua-BbnMAYS6.js +1 -0
- package/public/assets/luau-CXu1NL6O.js +1 -0
- package/public/assets/main-CSlDZj4f.js +188 -0
- package/public/assets/main-HAZkIolJ.css +19 -0
- package/public/assets/make-CHLpvVh8.js +1 -0
- package/public/assets/markdown-Cvjx9yec.js +1 -0
- package/public/assets/marko-CPi9NSCl.js +1 -0
- package/public/assets/material-theme-D5KoaKCx.js +1 -0
- package/public/assets/material-theme-darker-BfHTSMKl.js +1 -0
- package/public/assets/material-theme-lighter-B0m2ddpp.js +1 -0
- package/public/assets/material-theme-ocean-CyktbL80.js +1 -0
- package/public/assets/material-theme-palenight-Csfq5Kiy.js +1 -0
- package/public/assets/matlab-D7o27uSR.js +1 -0
- package/public/assets/mdc-DUICxH0z.js +1 -0
- package/public/assets/mdx-Cmh6b_Ma.js +1 -0
- package/public/assets/mermaid-DKYwYmdq.js +1 -0
- package/public/assets/min-dark-CafNBF8u.js +1 -0
- package/public/assets/min-light-CTRr51gU.js +1 -0
- package/public/assets/mipsasm-CKIfxQSi.js +1 -0
- package/public/assets/mojo-1DNp92w6.js +1 -0
- package/public/assets/monokai-D4h5O-jR.js +1 -0
- package/public/assets/move-Bu9oaDYs.js +1 -0
- package/public/assets/narrat-DRg8JJMk.js +1 -0
- package/public/assets/nextflow-CUEJCptM.js +1 -0
- package/public/assets/nginx-DknmC5AR.js +1 -0
- package/public/assets/night-owl-C39BiMTA.js +1 -0
- package/public/assets/nim-CVrawwO9.js +1 -0
- package/public/assets/nix-BbRYJGeE.js +1 -0
- package/public/assets/nord-Ddv68eIx.js +1 -0
- package/public/assets/nushell-C-sUppwS.js +1 -0
- package/public/assets/objective-c-DXmwc3jG.js +1 -0
- package/public/assets/objective-cpp-CLxacb5B.js +1 -0
- package/public/assets/ocaml-C0hk2d4L.js +1 -0
- package/public/assets/one-dark-pro-DVMEJ2y_.js +1 -0
- package/public/assets/one-light-PoHY5YXO.js +1 -0
- package/public/assets/pascal-D93ZcfNL.js +1 -0
- package/public/assets/perl-C0TMdlhV.js +1 -0
- package/public/assets/php-CDn_0X-4.js +1 -0
- package/public/assets/pkl-u5AG7uiY.js +1 -0
- package/public/assets/plastic-3e1v2bzS.js +1 -0
- package/public/assets/plsql-ChMvpjG-.js +1 -0
- package/public/assets/po-BTJTHyun.js +1 -0
- package/public/assets/poimandres-CS3Unz2-.js +1 -0
- package/public/assets/polar-C0HS_06l.js +1 -0
- package/public/assets/postcss-CXtECtnM.js +1 -0
- package/public/assets/powerquery-CEu0bR-o.js +1 -0
- package/public/assets/powershell-Dpen1YoG.js +1 -0
- package/public/assets/prisma-Dd19v3D-.js +1 -0
- package/public/assets/prolog-CbFg5uaA.js +1 -0
- package/public/assets/proto-DyJlTyXw.js +1 -0
- package/public/assets/pug-CGlum2m_.js +1 -0
- package/public/assets/puppet-BMWR74SV.js +1 -0
- package/public/assets/purescript-CklMAg4u.js +1 -0
- package/public/assets/python-B6aJPvgy.js +1 -0
- package/public/assets/qml-3beO22l8.js +1 -0
- package/public/assets/qmldir-C8lEn-DE.js +1 -0
- package/public/assets/qss-IeuSbFQv.js +1 -0
- package/public/assets/r-DiinP2Uv.js +1 -0
- package/public/assets/racket-BqYA7rlc.js +1 -0
- package/public/assets/raku-DXvB9xmW.js +1 -0
- package/public/assets/razor-WgofotgN.js +1 -0
- package/public/assets/red-bN70gL4F.js +1 -0
- package/public/assets/reg-C-SQnVFl.js +1 -0
- package/public/assets/regexp-CDVJQ6XC.js +1 -0
- package/public/assets/rel-C3B-1QV4.js +1 -0
- package/public/assets/riscv-BM1_JUlF.js +1 -0
- package/public/assets/rose-pine-BHrmToEH.js +1 -0
- package/public/assets/rose-pine-dawn-CnK8MTSM.js +1 -0
- package/public/assets/rose-pine-moon-NleAzG8P.js +1 -0
- package/public/assets/rosmsg-BJDFO7_C.js +1 -0
- package/public/assets/rst-B0xPkSld.js +1 -0
- package/public/assets/ruby-BvKwtOVI.js +1 -0
- package/public/assets/rust-B1yitclQ.js +1 -0
- package/public/assets/sas-cz2c8ADy.js +1 -0
- package/public/assets/sass-Cj5Yp3dK.js +1 -0
- package/public/assets/scala-C151Ov-r.js +1 -0
- package/public/assets/scheme-C98Dy4si.js +1 -0
- package/public/assets/scss-OYdSNvt2.js +1 -0
- package/public/assets/sdbl-DVxCFoDh.js +1 -0
- package/public/assets/shaderlab-Dg9Lc6iA.js +1 -0
- package/public/assets/shellscript-Yzrsuije.js +1 -0
- package/public/assets/shellsession-BADoaaVG.js +1 -0
- package/public/assets/slack-dark-BthQWCQV.js +1 -0
- package/public/assets/slack-ochin-DqwNpetd.js +1 -0
- package/public/assets/smalltalk-BERRCDM3.js +1 -0
- package/public/assets/snazzy-light-Bw305WKR.js +1 -0
- package/public/assets/solarized-dark-DXbdFlpD.js +1 -0
- package/public/assets/solarized-light-L9t79GZl.js +1 -0
- package/public/assets/solidity-BbcW6ACK.js +1 -0
- package/public/assets/soy-Brmx7dQM.js +1 -0
- package/public/assets/sparql-rVzFXLq3.js +1 -0
- package/public/assets/splunk-BtCnVYZw.js +1 -0
- package/public/assets/sql-BLtJtn59.js +1 -0
- package/public/assets/ssh-config-_ykCGR6B.js +1 -0
- package/public/assets/stata-BH5u7GGu.js +1 -0
- package/public/assets/stylus-BEDo0Tqx.js +1 -0
- package/public/assets/svelte-3Dk4HxPD.js +1 -0
- package/public/assets/swift-Dg5xB15N.js +1 -0
- package/public/assets/synthwave-84-CbfX1IO0.js +1 -0
- package/public/assets/system-verilog-CnnmHF94.js +1 -0
- package/public/assets/systemd-4A_iFExJ.js +1 -0
- package/public/assets/talonscript-CkByrt1z.js +1 -0
- package/public/assets/tasl-QIJgUcNo.js +1 -0
- package/public/assets/tcl-dwOrl1Do.js +1 -0
- package/public/assets/templ-W15q3VgB.js +1 -0
- package/public/assets/terraform-BETggiCN.js +1 -0
- package/public/assets/tex-Cppo0RY3.js +1 -0
- package/public/assets/tokyo-night-hegEt444.js +1 -0
- package/public/assets/toml-vGWfd6FD.js +1 -0
- package/public/assets/ts-tags-zn1MmPIZ.js +1 -0
- package/public/assets/tsv-B_m7g4N7.js +1 -0
- package/public/assets/tsx-COt5Ahok.js +1 -0
- package/public/assets/turtle-BsS91CYL.js +1 -0
- package/public/assets/twig-CO9l9SDP.js +1 -0
- package/public/assets/typescript-BPQ3VLAy.js +1 -0
- package/public/assets/typespec-Df68jz8_.js +1 -0
- package/public/assets/typst-DHCkPAjA.js +1 -0
- package/public/assets/v-BcVCzyr7.js +1 -0
- package/public/assets/vala-CsfeWuGM.js +1 -0
- package/public/assets/vb-D17OF-Vu.js +1 -0
- package/public/assets/verilog-BQ8w6xss.js +1 -0
- package/public/assets/vesper-DU1UobuO.js +1 -0
- package/public/assets/vhdl-CeAyd5Ju.js +1 -0
- package/public/assets/viml-CJc9bBzg.js +1 -0
- package/public/assets/vitesse-black-Bkuqu6BP.js +1 -0
- package/public/assets/vitesse-dark-D0r3Knsf.js +1 -0
- package/public/assets/vitesse-light-CVO1_9PV.js +1 -0
- package/public/assets/vue-CCoi5OLL.js +1 -0
- package/public/assets/vue-html-DAAvJJDi.js +1 -0
- package/public/assets/vue-vine-_Ih-lPRR.js +1 -0
- package/public/assets/vyper-CDx5xZoG.js +1 -0
- package/public/assets/wasm-CG6Dc4jp.js +1 -0
- package/public/assets/wasm-MzD3tlZU.js +1 -0
- package/public/assets/wenyan-BV7otONQ.js +1 -0
- package/public/assets/wgsl-Dx-B1_4e.js +1 -0
- package/public/assets/wikitext-BhOHFoWU.js +1 -0
- package/public/assets/wit-5i3qLPDT.js +1 -0
- package/public/assets/wolfram-lXgVvXCa.js +1 -0
- package/public/assets/xml-sdJ4AIDG.js +1 -0
- package/public/assets/xsl-CtQFsRM5.js +1 -0
- package/public/assets/yaml-Buea-lGh.js +1 -0
- package/public/assets/zenscript-DVFEvuxE.js +1 -0
- package/public/assets/zig-VOosw3JB.js +1 -0
- package/public/favicon.ico +0 -0
- package/public/index.html +38 -0
- package/public/loading.html +28 -0
- package/public/logo.png +0 -0
- package/public/manifest.webmanifest +1 -0
- package/public/maskable-icon-512x512.png +0 -0
- package/public/monaco/vs/base/browser/ui/codicons/codicon/codicon.ttf +0 -0
- package/public/monaco/vs/base/worker/workerMain.js +31 -0
- package/public/monaco/vs/basic-languages/cpp/cpp.js +10 -0
- package/public/monaco/vs/basic-languages/kotlin/kotlin.js +10 -0
- package/public/monaco/vs/basic-languages/markdown/markdown.js +10 -0
- package/public/monaco/vs/basic-languages/python/python.js +10 -0
- package/public/monaco/vs/editor/editor.main.css +8 -0
- package/public/monaco/vs/editor/editor.main.js +798 -0
- package/public/monaco/vs/language/css/cssMode.js +13 -0
- package/public/monaco/vs/language/css/cssWorker.js +77 -0
- package/public/monaco/vs/language/html/htmlMode.js +13 -0
- package/public/monaco/vs/language/html/htmlWorker.js +454 -0
- package/public/monaco/vs/language/json/jsonMode.js +19 -0
- package/public/monaco/vs/language/json/jsonWorker.js +42 -0
- package/public/monaco/vs/language/typescript/tsMode.js +20 -0
- package/public/monaco/vs/language/typescript/tsWorker.js +51328 -0
- package/public/monaco/vs/loader.js +11 -0
- package/public/monaco.worker.js +7 -0
- package/public/pwa-192x192.png +0 -0
- package/public/pwa-512x512.png +0 -0
- package/public/pwa-64x64.png +0 -0
- package/public/registerSW.js +1 -0
- package/public/sw.js +1 -0
- package/public/ui-version.json +3 -0
- package/public/workbox-60d14903.js +1 -0
- package/scripts/copy-auth-pages.mjs +22 -0
- package/scripts/copy-opencode-config.mjs +61 -0
- package/scripts/copy-ui-dist.mjs +21 -0
- package/src/api-types.ts +326 -0
- package/src/auth/auth-store.ts +175 -0
- package/src/auth/http-auth.ts +38 -0
- package/src/auth/manager.ts +163 -0
- package/src/auth/password-hash.ts +49 -0
- package/src/auth/session-manager.ts +23 -0
- package/src/auth/token-manager.ts +32 -0
- package/src/background-processes/manager.ts +519 -0
- package/src/bin.ts +29 -0
- package/src/config/binaries.ts +192 -0
- package/src/config/location.ts +78 -0
- package/src/config/schema.ts +104 -0
- package/src/config/store.ts +244 -0
- package/src/events/bus.ts +45 -0
- package/src/filesystem/__tests__/search-cache.test.ts +61 -0
- package/src/filesystem/browser.ts +353 -0
- package/src/filesystem/search-cache.ts +66 -0
- package/src/filesystem/search.ts +184 -0
- package/src/index.ts +540 -0
- package/src/launcher.ts +177 -0
- package/src/loader.ts +21 -0
- package/src/logger.ts +133 -0
- package/src/opencode-config.ts +31 -0
- package/src/plugins/channel.ts +55 -0
- package/src/plugins/handlers.ts +36 -0
- package/src/releases/dev-release-monitor.ts +118 -0
- package/src/releases/release-monitor.ts +149 -0
- package/src/server/http-server.ts +693 -0
- package/src/server/network-addresses.ts +75 -0
- package/src/server/routes/auth-pages/login.html +134 -0
- package/src/server/routes/auth-pages/token.html +93 -0
- package/src/server/routes/auth.ts +164 -0
- package/src/server/routes/background-processes.ts +85 -0
- package/src/server/routes/config.ts +76 -0
- package/src/server/routes/events.ts +61 -0
- package/src/server/routes/filesystem.ts +54 -0
- package/src/server/routes/meta.ts +58 -0
- package/src/server/routes/plugin.ts +75 -0
- package/src/server/routes/storage.ts +66 -0
- package/src/server/routes/workspaces.ts +113 -0
- package/src/server/routes/worktrees.ts +195 -0
- package/src/server/tls.ts +283 -0
- package/src/storage/instance-store.ts +64 -0
- package/src/ui/__tests__/remote-ui.test.ts +58 -0
- package/src/ui/remote-ui.ts +571 -0
- package/src/workspaces/git-worktrees.ts +241 -0
- package/src/workspaces/instance-events.ts +226 -0
- package/src/workspaces/manager.ts +493 -0
- package/src/workspaces/opencode-auth.ts +22 -0
- package/src/workspaces/runtime.ts +428 -0
- package/src/workspaces/worktree-map.ts +129 -0
- package/tsconfig.json +17 -0
|
@@ -0,0 +1,462 @@
|
|
|
1
|
+
import { createHash } from "crypto";
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import { promises as fsp } from "fs";
|
|
4
|
+
import os from "os";
|
|
5
|
+
import path from "path";
|
|
6
|
+
import { Readable } from "stream";
|
|
7
|
+
import { fetch } from "undici";
|
|
8
|
+
import yauzl from "yauzl";
|
|
9
|
+
const DEFAULT_MANIFEST_URL = "https://ui.codenomad.neuralnomads.ai/version.json";
|
|
10
|
+
const MANIFEST_TIMEOUT_MS = 5000;
|
|
11
|
+
const ZIP_TIMEOUT_MS = 30000;
|
|
12
|
+
export async function resolveUi(options) {
|
|
13
|
+
const manifestUrl = options.manifestUrl ?? DEFAULT_MANIFEST_URL;
|
|
14
|
+
if (options.uiDevServerUrl) {
|
|
15
|
+
return {
|
|
16
|
+
uiDevServerUrl: options.uiDevServerUrl,
|
|
17
|
+
source: "dev-proxy",
|
|
18
|
+
supported: true,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
if (options.overrideUiDir) {
|
|
22
|
+
const resolved = await resolveStaticUiDir(options.overrideUiDir);
|
|
23
|
+
return {
|
|
24
|
+
uiStaticDir: resolved ?? options.overrideUiDir,
|
|
25
|
+
source: "override",
|
|
26
|
+
uiVersion: await readUiVersion(resolved ?? options.overrideUiDir),
|
|
27
|
+
supported: true,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
const uiRoot = resolveUiCacheRoot(options.configDir);
|
|
31
|
+
const currentDir = path.join(uiRoot, "current");
|
|
32
|
+
const previousDir = path.join(uiRoot, "previous");
|
|
33
|
+
if (!options.autoUpdate) {
|
|
34
|
+
return await resolveFromCacheOrBundled({
|
|
35
|
+
logger: options.logger,
|
|
36
|
+
bundledUiDir: options.bundledUiDir,
|
|
37
|
+
currentDir,
|
|
38
|
+
previousDir,
|
|
39
|
+
supported: true,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
let manifest = null;
|
|
43
|
+
try {
|
|
44
|
+
manifest = await fetchManifest(manifestUrl, options.logger);
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
options.logger.debug({ err: error }, "Remote UI manifest unavailable; using cached/bundled UI");
|
|
48
|
+
}
|
|
49
|
+
if (!manifest) {
|
|
50
|
+
return await resolveFromCacheOrBundled({
|
|
51
|
+
logger: options.logger,
|
|
52
|
+
bundledUiDir: options.bundledUiDir,
|
|
53
|
+
currentDir,
|
|
54
|
+
previousDir,
|
|
55
|
+
supported: true,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
const supported = compareSemverCore(options.serverVersion, manifest.minServerVersion) >= 0;
|
|
59
|
+
if (!supported) {
|
|
60
|
+
const message = "Upgrade App to use latest features";
|
|
61
|
+
return await resolveFromCacheOrBundled({
|
|
62
|
+
logger: options.logger,
|
|
63
|
+
bundledUiDir: options.bundledUiDir,
|
|
64
|
+
currentDir,
|
|
65
|
+
previousDir,
|
|
66
|
+
supported: false,
|
|
67
|
+
message,
|
|
68
|
+
latestServerVersion: manifest.latestServerVersion,
|
|
69
|
+
latestServerUrl: manifest.latestServerUrl,
|
|
70
|
+
minServerVersion: manifest.minServerVersion,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
const bestLocal = await pickBestLocalUi({
|
|
74
|
+
logger: options.logger,
|
|
75
|
+
bundledUiDir: options.bundledUiDir,
|
|
76
|
+
currentDir,
|
|
77
|
+
previousDir,
|
|
78
|
+
});
|
|
79
|
+
const remoteIsNewer = !bestLocal ||
|
|
80
|
+
compareSemverMaybe(manifest.latestUIVersion, bestLocal.uiVersion) > 0;
|
|
81
|
+
if (!remoteIsNewer) {
|
|
82
|
+
return await resolveFromCacheOrBundled({
|
|
83
|
+
logger: options.logger,
|
|
84
|
+
bundledUiDir: options.bundledUiDir,
|
|
85
|
+
currentDir,
|
|
86
|
+
previousDir,
|
|
87
|
+
supported: true,
|
|
88
|
+
latestServerVersion: manifest.latestServerVersion,
|
|
89
|
+
latestServerUrl: manifest.latestServerUrl,
|
|
90
|
+
minServerVersion: manifest.minServerVersion,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
try {
|
|
94
|
+
await installRemoteUi({
|
|
95
|
+
manifest,
|
|
96
|
+
uiRoot,
|
|
97
|
+
currentDir,
|
|
98
|
+
previousDir,
|
|
99
|
+
logger: options.logger,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
options.logger.warn({ err: error }, "Failed to install remote UI; falling back");
|
|
104
|
+
return await resolveFromCacheOrBundled({
|
|
105
|
+
logger: options.logger,
|
|
106
|
+
bundledUiDir: options.bundledUiDir,
|
|
107
|
+
currentDir,
|
|
108
|
+
previousDir,
|
|
109
|
+
supported: true,
|
|
110
|
+
latestServerVersion: manifest.latestServerVersion,
|
|
111
|
+
latestServerUrl: manifest.latestServerUrl,
|
|
112
|
+
minServerVersion: manifest.minServerVersion,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
const installed = await resolveStaticUiDir(currentDir);
|
|
116
|
+
if (installed) {
|
|
117
|
+
return {
|
|
118
|
+
uiStaticDir: installed,
|
|
119
|
+
source: "downloaded",
|
|
120
|
+
uiVersion: await readUiVersion(installed),
|
|
121
|
+
supported: true,
|
|
122
|
+
latestServerVersion: manifest.latestServerVersion,
|
|
123
|
+
latestServerUrl: manifest.latestServerUrl,
|
|
124
|
+
minServerVersion: manifest.minServerVersion,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
return await resolveFromCacheOrBundled({
|
|
128
|
+
logger: options.logger,
|
|
129
|
+
bundledUiDir: options.bundledUiDir,
|
|
130
|
+
currentDir,
|
|
131
|
+
previousDir,
|
|
132
|
+
supported: true,
|
|
133
|
+
latestServerVersion: manifest.latestServerVersion,
|
|
134
|
+
latestServerUrl: manifest.latestServerUrl,
|
|
135
|
+
minServerVersion: manifest.minServerVersion,
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
function resolveUiCacheRoot(configDir) {
|
|
139
|
+
if (configDir) {
|
|
140
|
+
return path.join(configDir, "ui");
|
|
141
|
+
}
|
|
142
|
+
return path.join(os.homedir(), ".config", "codenomad", "ui");
|
|
143
|
+
}
|
|
144
|
+
async function resolveFromCacheOrBundled(args) {
|
|
145
|
+
const bestLocal = await pickBestLocalUi({
|
|
146
|
+
logger: args.logger,
|
|
147
|
+
bundledUiDir: args.bundledUiDir,
|
|
148
|
+
currentDir: args.currentDir,
|
|
149
|
+
previousDir: args.previousDir,
|
|
150
|
+
});
|
|
151
|
+
if (bestLocal) {
|
|
152
|
+
return {
|
|
153
|
+
uiStaticDir: bestLocal.uiStaticDir,
|
|
154
|
+
source: bestLocal.source,
|
|
155
|
+
uiVersion: bestLocal.uiVersion,
|
|
156
|
+
supported: args.supported,
|
|
157
|
+
message: args.message,
|
|
158
|
+
latestServerVersion: args.latestServerVersion,
|
|
159
|
+
latestServerUrl: args.latestServerUrl,
|
|
160
|
+
minServerVersion: args.minServerVersion,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
args.logger.warn({ bundledUiDir: args.bundledUiDir }, "No UI assets found");
|
|
164
|
+
return {
|
|
165
|
+
uiStaticDir: args.bundledUiDir,
|
|
166
|
+
source: "missing",
|
|
167
|
+
supported: args.supported,
|
|
168
|
+
message: args.message,
|
|
169
|
+
latestServerVersion: args.latestServerVersion,
|
|
170
|
+
latestServerUrl: args.latestServerUrl,
|
|
171
|
+
minServerVersion: args.minServerVersion,
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
async function pickBestLocalUi(args) {
|
|
175
|
+
const candidates = [];
|
|
176
|
+
const currentResolved = await resolveStaticUiDir(args.currentDir);
|
|
177
|
+
if (currentResolved) {
|
|
178
|
+
candidates.push({
|
|
179
|
+
uiStaticDir: currentResolved,
|
|
180
|
+
source: "downloaded",
|
|
181
|
+
uiVersion: await readUiVersion(currentResolved),
|
|
182
|
+
priority: 2,
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
const bundledResolved = await resolveStaticUiDir(args.bundledUiDir);
|
|
186
|
+
if (bundledResolved) {
|
|
187
|
+
candidates.push({
|
|
188
|
+
uiStaticDir: bundledResolved,
|
|
189
|
+
source: "bundled",
|
|
190
|
+
uiVersion: await readUiVersion(bundledResolved),
|
|
191
|
+
priority: 1,
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
const previousResolved = await resolveStaticUiDir(args.previousDir);
|
|
195
|
+
if (previousResolved) {
|
|
196
|
+
candidates.push({
|
|
197
|
+
uiStaticDir: previousResolved,
|
|
198
|
+
source: "previous",
|
|
199
|
+
uiVersion: await readUiVersion(previousResolved),
|
|
200
|
+
priority: 0,
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
if (candidates.length === 0) {
|
|
204
|
+
return null;
|
|
205
|
+
}
|
|
206
|
+
candidates.sort((a, b) => {
|
|
207
|
+
const versionCmp = compareSemverMaybe(a.uiVersion, b.uiVersion);
|
|
208
|
+
if (versionCmp !== 0)
|
|
209
|
+
return -versionCmp;
|
|
210
|
+
return b.priority - a.priority;
|
|
211
|
+
});
|
|
212
|
+
const best = candidates[0];
|
|
213
|
+
if (!best)
|
|
214
|
+
return null;
|
|
215
|
+
return { uiStaticDir: best.uiStaticDir, source: best.source, uiVersion: best.uiVersion };
|
|
216
|
+
}
|
|
217
|
+
function compareSemverMaybe(a, b) {
|
|
218
|
+
if (!a && !b)
|
|
219
|
+
return 0;
|
|
220
|
+
if (!a)
|
|
221
|
+
return -1;
|
|
222
|
+
if (!b)
|
|
223
|
+
return 1;
|
|
224
|
+
return compareSemverCore(a, b);
|
|
225
|
+
}
|
|
226
|
+
async function resolveStaticUiDir(uiDir) {
|
|
227
|
+
try {
|
|
228
|
+
const indexPath = path.join(uiDir, "index.html");
|
|
229
|
+
await fsp.access(indexPath, fs.constants.R_OK);
|
|
230
|
+
return uiDir;
|
|
231
|
+
}
|
|
232
|
+
catch {
|
|
233
|
+
return null;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
async function readUiVersion(uiDir) {
|
|
237
|
+
try {
|
|
238
|
+
const content = await fsp.readFile(path.join(uiDir, "ui-version.json"), "utf-8");
|
|
239
|
+
const parsed = JSON.parse(content);
|
|
240
|
+
return parsed.uiVersion ?? parsed.version;
|
|
241
|
+
}
|
|
242
|
+
catch {
|
|
243
|
+
return undefined;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
async function fetchManifest(url, logger) {
|
|
247
|
+
const controller = new AbortController();
|
|
248
|
+
const timeout = setTimeout(() => controller.abort(), MANIFEST_TIMEOUT_MS);
|
|
249
|
+
try {
|
|
250
|
+
const response = await fetch(url, {
|
|
251
|
+
signal: controller.signal,
|
|
252
|
+
headers: {
|
|
253
|
+
Accept: "application/json",
|
|
254
|
+
"User-Agent": "CodeNomad-CLI",
|
|
255
|
+
},
|
|
256
|
+
});
|
|
257
|
+
if (!response.ok) {
|
|
258
|
+
throw new Error(`Manifest responded with ${response.status}`);
|
|
259
|
+
}
|
|
260
|
+
const json = (await response.json());
|
|
261
|
+
validateManifest(json);
|
|
262
|
+
return json;
|
|
263
|
+
}
|
|
264
|
+
catch (error) {
|
|
265
|
+
logger.debug({ err: error, url }, "Failed to fetch remote UI manifest");
|
|
266
|
+
throw error;
|
|
267
|
+
}
|
|
268
|
+
finally {
|
|
269
|
+
clearTimeout(timeout);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
function validateManifest(manifest) {
|
|
273
|
+
const required = ["minServerVersion", "latestUIVersion", "uiPackageURL", "sha256"];
|
|
274
|
+
for (const key of required) {
|
|
275
|
+
const value = manifest[key];
|
|
276
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
277
|
+
throw new Error(`Manifest missing ${key}`);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
if (!/^https:\/\//i.test(manifest.uiPackageURL)) {
|
|
281
|
+
throw new Error("uiPackageURL must be https");
|
|
282
|
+
}
|
|
283
|
+
if (!/^[a-f0-9]{64}$/i.test(manifest.sha256.trim())) {
|
|
284
|
+
throw new Error("sha256 must be 64 hex chars");
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
async function installRemoteUi(args) {
|
|
288
|
+
await fsp.mkdir(args.uiRoot, { recursive: true });
|
|
289
|
+
const tmpDir = path.join(args.uiRoot, `tmp-${Date.now()}`);
|
|
290
|
+
const zipPath = path.join(args.uiRoot, `ui-${args.manifest.latestUIVersion}.zip`);
|
|
291
|
+
try {
|
|
292
|
+
await downloadFile(args.manifest.uiPackageURL, zipPath, args.logger);
|
|
293
|
+
const digest = await sha256File(zipPath);
|
|
294
|
+
if (digest.toLowerCase() !== args.manifest.sha256.toLowerCase()) {
|
|
295
|
+
throw new Error(`sha256 mismatch for UI zip (expected ${args.manifest.sha256}, got ${digest})`);
|
|
296
|
+
}
|
|
297
|
+
await extractZip(zipPath, tmpDir);
|
|
298
|
+
const indexPath = path.join(tmpDir, "index.html");
|
|
299
|
+
if (!fs.existsSync(indexPath)) {
|
|
300
|
+
throw new Error("Extracted UI missing index.html");
|
|
301
|
+
}
|
|
302
|
+
await rotateDirs({ currentDir: args.currentDir, previousDir: args.previousDir, logger: args.logger });
|
|
303
|
+
fs.rmSync(args.currentDir, { recursive: true, force: true });
|
|
304
|
+
fs.renameSync(tmpDir, args.currentDir);
|
|
305
|
+
}
|
|
306
|
+
finally {
|
|
307
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
308
|
+
fs.rmSync(zipPath, { force: true });
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
async function rotateDirs(args) {
|
|
312
|
+
try {
|
|
313
|
+
if (fs.existsSync(args.previousDir)) {
|
|
314
|
+
fs.rmSync(args.previousDir, { recursive: true, force: true });
|
|
315
|
+
}
|
|
316
|
+
if (fs.existsSync(args.currentDir)) {
|
|
317
|
+
fs.renameSync(args.currentDir, args.previousDir);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
catch (error) {
|
|
321
|
+
args.logger.warn({ err: error }, "Failed to rotate UI cache directories");
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
async function downloadFile(url, targetPath, logger) {
|
|
325
|
+
const controller = new AbortController();
|
|
326
|
+
const timeout = setTimeout(() => controller.abort(), ZIP_TIMEOUT_MS);
|
|
327
|
+
try {
|
|
328
|
+
const response = await fetch(url, {
|
|
329
|
+
signal: controller.signal,
|
|
330
|
+
headers: {
|
|
331
|
+
Accept: "application/octet-stream",
|
|
332
|
+
"User-Agent": "CodeNomad-CLI",
|
|
333
|
+
},
|
|
334
|
+
});
|
|
335
|
+
if (!response.ok || !response.body) {
|
|
336
|
+
throw new Error(`UI zip download failed with ${response.status}`);
|
|
337
|
+
}
|
|
338
|
+
await fsp.mkdir(path.dirname(targetPath), { recursive: true });
|
|
339
|
+
const fileStream = fs.createWriteStream(targetPath);
|
|
340
|
+
const body = response.body;
|
|
341
|
+
if (!body) {
|
|
342
|
+
throw new Error("UI zip response missing body");
|
|
343
|
+
}
|
|
344
|
+
const nodeStream = Readable.fromWeb(body);
|
|
345
|
+
await new Promise((resolve, reject) => {
|
|
346
|
+
nodeStream.pipe(fileStream);
|
|
347
|
+
nodeStream.on("error", reject);
|
|
348
|
+
fileStream.on("error", reject);
|
|
349
|
+
fileStream.on("finish", () => resolve());
|
|
350
|
+
});
|
|
351
|
+
logger.debug({ url, targetPath }, "Downloaded remote UI bundle");
|
|
352
|
+
}
|
|
353
|
+
finally {
|
|
354
|
+
clearTimeout(timeout);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
async function sha256File(filePath) {
|
|
358
|
+
const hash = createHash("sha256");
|
|
359
|
+
const stream = fs.createReadStream(filePath);
|
|
360
|
+
await new Promise((resolve, reject) => {
|
|
361
|
+
stream.on("data", (chunk) => hash.update(chunk));
|
|
362
|
+
stream.on("error", reject);
|
|
363
|
+
stream.on("end", () => resolve());
|
|
364
|
+
});
|
|
365
|
+
return hash.digest("hex");
|
|
366
|
+
}
|
|
367
|
+
async function extractZip(zipPath, targetDir) {
|
|
368
|
+
await fsp.mkdir(targetDir, { recursive: true });
|
|
369
|
+
await new Promise((resolve, reject) => {
|
|
370
|
+
yauzl.open(zipPath, { lazyEntries: true }, (openErr, zipfile) => {
|
|
371
|
+
if (openErr || !zipfile) {
|
|
372
|
+
reject(openErr ?? new Error("Unable to open zip"));
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
375
|
+
const root = path.resolve(targetDir);
|
|
376
|
+
const closeWithError = (error) => {
|
|
377
|
+
try {
|
|
378
|
+
zipfile.close();
|
|
379
|
+
}
|
|
380
|
+
catch {
|
|
381
|
+
// ignore
|
|
382
|
+
}
|
|
383
|
+
reject(error);
|
|
384
|
+
};
|
|
385
|
+
zipfile.readEntry();
|
|
386
|
+
zipfile.on("entry", (entry) => {
|
|
387
|
+
// Normalize and guard against zip-slip.
|
|
388
|
+
const entryPath = entry.fileName.replace(/\\/g, "/");
|
|
389
|
+
const segments = entryPath.split("/").filter(Boolean);
|
|
390
|
+
if (segments.some((segment) => segment === "..") || path.isAbsolute(entryPath)) {
|
|
391
|
+
closeWithError(new Error(`Invalid zip entry path: ${entry.fileName}`));
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
394
|
+
const destination = path.resolve(targetDir, entryPath);
|
|
395
|
+
if (!destination.startsWith(root + path.sep) && destination !== root) {
|
|
396
|
+
closeWithError(new Error(`Zip entry escapes target dir: ${entry.fileName}`));
|
|
397
|
+
return;
|
|
398
|
+
}
|
|
399
|
+
const isDirectory = entry.fileName.endsWith("/");
|
|
400
|
+
if (isDirectory) {
|
|
401
|
+
fsp
|
|
402
|
+
.mkdir(destination, { recursive: true })
|
|
403
|
+
.then(() => zipfile.readEntry())
|
|
404
|
+
.catch((error) => closeWithError(error));
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
407
|
+
fsp
|
|
408
|
+
.mkdir(path.dirname(destination), { recursive: true })
|
|
409
|
+
.then(() => {
|
|
410
|
+
zipfile.openReadStream(entry, (streamErr, readStream) => {
|
|
411
|
+
if (streamErr || !readStream) {
|
|
412
|
+
closeWithError(streamErr ?? new Error("Unable to read zip entry"));
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
const writeStream = fs.createWriteStream(destination);
|
|
416
|
+
const cleanup = (error) => {
|
|
417
|
+
readStream.destroy();
|
|
418
|
+
writeStream.destroy();
|
|
419
|
+
if (error) {
|
|
420
|
+
closeWithError(error);
|
|
421
|
+
}
|
|
422
|
+
};
|
|
423
|
+
readStream.on("error", cleanup);
|
|
424
|
+
writeStream.on("error", cleanup);
|
|
425
|
+
writeStream.on("finish", () => zipfile.readEntry());
|
|
426
|
+
readStream.pipe(writeStream);
|
|
427
|
+
});
|
|
428
|
+
})
|
|
429
|
+
.catch((error) => closeWithError(error));
|
|
430
|
+
});
|
|
431
|
+
zipfile.on("end", () => {
|
|
432
|
+
zipfile.close();
|
|
433
|
+
resolve();
|
|
434
|
+
});
|
|
435
|
+
zipfile.on("error", (error) => closeWithError(error));
|
|
436
|
+
});
|
|
437
|
+
});
|
|
438
|
+
}
|
|
439
|
+
function compareSemverCore(a, b) {
|
|
440
|
+
const pa = parseSemverCore(a);
|
|
441
|
+
const pb = parseSemverCore(b);
|
|
442
|
+
if (pa.major !== pb.major)
|
|
443
|
+
return pa.major > pb.major ? 1 : -1;
|
|
444
|
+
if (pa.minor !== pb.minor)
|
|
445
|
+
return pa.minor > pb.minor ? 1 : -1;
|
|
446
|
+
if (pa.patch !== pb.patch)
|
|
447
|
+
return pa.patch > pb.patch ? 1 : -1;
|
|
448
|
+
return 0;
|
|
449
|
+
}
|
|
450
|
+
function parseSemverCore(value) {
|
|
451
|
+
const core = value.trim().replace(/^v/i, "").split("-", 1)[0] ?? "0.0.0";
|
|
452
|
+
const parts = core.split(".");
|
|
453
|
+
const parsePart = (input) => {
|
|
454
|
+
const n = Number.parseInt((input ?? "0").replace(/[^0-9]/g, ""), 10);
|
|
455
|
+
return Number.isFinite(n) ? n : 0;
|
|
456
|
+
};
|
|
457
|
+
return {
|
|
458
|
+
major: parsePart(parts[0]),
|
|
459
|
+
minor: parsePart(parts[1]),
|
|
460
|
+
patch: parsePart(parts[2]),
|
|
461
|
+
};
|
|
462
|
+
}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import path from "path";
|
|
2
|
+
import { spawn } from "child_process";
|
|
3
|
+
import { promises as fsp } from "fs";
|
|
4
|
+
function runGit(args, cwd) {
|
|
5
|
+
return new Promise((resolve) => {
|
|
6
|
+
const child = spawn("git", args, { cwd, stdio: ["ignore", "pipe", "pipe"] });
|
|
7
|
+
let stdout = "";
|
|
8
|
+
let stderr = "";
|
|
9
|
+
child.stdout?.on("data", (chunk) => {
|
|
10
|
+
stdout += chunk.toString();
|
|
11
|
+
});
|
|
12
|
+
child.stderr?.on("data", (chunk) => {
|
|
13
|
+
stderr += chunk.toString();
|
|
14
|
+
});
|
|
15
|
+
child.once("error", (error) => {
|
|
16
|
+
resolve({ ok: false, error, stdout, stderr });
|
|
17
|
+
});
|
|
18
|
+
child.once("close", (code) => {
|
|
19
|
+
if (code === 0) {
|
|
20
|
+
resolve({ ok: true, stdout });
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
const error = new Error(stderr.trim() || `git ${args.join(" ")} failed with code ${code}`);
|
|
24
|
+
resolve({ ok: false, error, stdout, stderr });
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
export async function resolveRepoRoot(folder, logger) {
|
|
30
|
+
const result = await runGit(["rev-parse", "--show-toplevel"], folder);
|
|
31
|
+
if (!result.ok) {
|
|
32
|
+
logger?.debug?.({ folder, err: result.error }, "Folder is not a Git repository; using workspace folder as root");
|
|
33
|
+
return { repoRoot: folder, isGitRepo: false };
|
|
34
|
+
}
|
|
35
|
+
const repoRoot = result.stdout.trim();
|
|
36
|
+
if (!repoRoot) {
|
|
37
|
+
return { repoRoot: folder, isGitRepo: false };
|
|
38
|
+
}
|
|
39
|
+
return { repoRoot, isGitRepo: true };
|
|
40
|
+
}
|
|
41
|
+
function parseWorktreePorcelain(output) {
|
|
42
|
+
const records = [];
|
|
43
|
+
const lines = output.split(/\r?\n/);
|
|
44
|
+
let current = {};
|
|
45
|
+
const flush = () => {
|
|
46
|
+
if (current.worktree) {
|
|
47
|
+
records.push({ worktree: current.worktree, branch: current.branch });
|
|
48
|
+
}
|
|
49
|
+
current = {};
|
|
50
|
+
};
|
|
51
|
+
for (const line of lines) {
|
|
52
|
+
const trimmed = line.trim();
|
|
53
|
+
if (!trimmed) {
|
|
54
|
+
flush();
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
const [key, ...rest] = trimmed.split(" ");
|
|
58
|
+
const value = rest.join(" ").trim();
|
|
59
|
+
if (key === "worktree") {
|
|
60
|
+
current.worktree = value;
|
|
61
|
+
}
|
|
62
|
+
else if (key === "branch") {
|
|
63
|
+
// branch is like refs/heads/foo
|
|
64
|
+
current.branch = value.replace(/^refs\/heads\//, "");
|
|
65
|
+
}
|
|
66
|
+
else if (key === "HEAD") {
|
|
67
|
+
current.head = value;
|
|
68
|
+
}
|
|
69
|
+
else if (key === "detached") {
|
|
70
|
+
current.detached = true;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
flush();
|
|
74
|
+
return records;
|
|
75
|
+
}
|
|
76
|
+
export async function listWorktrees(params) {
|
|
77
|
+
const { repoRoot, workspaceFolder, logger } = params;
|
|
78
|
+
const rootDescriptor = { slug: "root", directory: repoRoot, kind: "root" };
|
|
79
|
+
const result = await runGit(["worktree", "list", "--porcelain"], workspaceFolder);
|
|
80
|
+
if (!result.ok) {
|
|
81
|
+
logger?.debug?.({ repoRoot, err: result.error }, "Failed to list git worktrees; returning root only");
|
|
82
|
+
return [rootDescriptor];
|
|
83
|
+
}
|
|
84
|
+
const records = parseWorktreePorcelain(result.stdout);
|
|
85
|
+
const worktrees = [rootDescriptor];
|
|
86
|
+
const seen = new Set(["root"]);
|
|
87
|
+
const normalizeSlug = (record) => {
|
|
88
|
+
const branch = (record.branch ?? "").trim();
|
|
89
|
+
if (branch) {
|
|
90
|
+
return branch;
|
|
91
|
+
}
|
|
92
|
+
const head = (record.head ?? "").trim();
|
|
93
|
+
if (head && /^[0-9a-f]{7,40}$/i.test(head)) {
|
|
94
|
+
return `detached-${head.slice(0, 7)}`;
|
|
95
|
+
}
|
|
96
|
+
// Fallback: stable-ish identifier derived from directory basename.
|
|
97
|
+
const base = path.basename(record.worktree || "");
|
|
98
|
+
return base ? `worktree-${base}` : "worktree";
|
|
99
|
+
};
|
|
100
|
+
for (const record of records) {
|
|
101
|
+
const abs = record.worktree;
|
|
102
|
+
if (!abs || typeof abs !== "string")
|
|
103
|
+
continue;
|
|
104
|
+
// Skip the root record (we always expose it as slug="root").
|
|
105
|
+
if (path.resolve(abs) === path.resolve(repoRoot)) {
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
const slug = normalizeSlug(record);
|
|
109
|
+
if (!slug || slug === "root") {
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
if (seen.has(slug)) {
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
seen.add(slug);
|
|
116
|
+
worktrees.push({ slug, directory: abs, kind: "worktree", branch: record.branch });
|
|
117
|
+
}
|
|
118
|
+
return worktrees;
|
|
119
|
+
}
|
|
120
|
+
export function isValidWorktreeSlug(slug) {
|
|
121
|
+
if (!slug)
|
|
122
|
+
return false;
|
|
123
|
+
const trimmed = slug.trim();
|
|
124
|
+
if (!trimmed)
|
|
125
|
+
return false;
|
|
126
|
+
if (trimmed.length > 200)
|
|
127
|
+
return false;
|
|
128
|
+
// Disallow control characters; allow branch-like slugs including '/'.
|
|
129
|
+
if (/[\x00-\x1F\x7F]/.test(trimmed))
|
|
130
|
+
return false;
|
|
131
|
+
return true;
|
|
132
|
+
}
|
|
133
|
+
export async function createManagedWorktree(params) {
|
|
134
|
+
const { repoRoot, workspaceFolder, logger } = params;
|
|
135
|
+
const branch = params.slug.trim();
|
|
136
|
+
if (!branch || branch === "root" || !isValidWorktreeSlug(branch)) {
|
|
137
|
+
throw new Error("Invalid worktree slug");
|
|
138
|
+
}
|
|
139
|
+
const sanitizeDirName = (input) => {
|
|
140
|
+
const normalized = input
|
|
141
|
+
.trim()
|
|
142
|
+
.replace(/[\\/]+/g, "-")
|
|
143
|
+
.replace(/\s+/g, "-")
|
|
144
|
+
.replace(/[^a-zA-Z0-9_.-]+/g, "-")
|
|
145
|
+
.replace(/-{2,}/g, "-")
|
|
146
|
+
.replace(/^-+|-+$/g, "");
|
|
147
|
+
return normalized || "worktree";
|
|
148
|
+
};
|
|
149
|
+
const worktreesDir = path.join(repoRoot, ".codenomad", "worktrees");
|
|
150
|
+
const targetDir = path.join(worktreesDir, sanitizeDirName(branch));
|
|
151
|
+
await fsp.mkdir(worktreesDir, { recursive: true });
|
|
152
|
+
try {
|
|
153
|
+
const stat = await fsp.stat(targetDir);
|
|
154
|
+
if (stat.isDirectory()) {
|
|
155
|
+
throw new Error("Worktree directory already exists");
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
catch (error) {
|
|
159
|
+
const code = error.code;
|
|
160
|
+
if (code !== "ENOENT") {
|
|
161
|
+
throw error;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
logger?.debug?.({ slug: branch, branch, targetDir }, "Creating managed git worktree");
|
|
165
|
+
// Prefer creating a new branch from HEAD.
|
|
166
|
+
const first = await runGit(["worktree", "add", "-b", branch, targetDir, "HEAD"], workspaceFolder);
|
|
167
|
+
if (first.ok) {
|
|
168
|
+
return { slug: branch, directory: targetDir, branch };
|
|
169
|
+
}
|
|
170
|
+
const message = first.stderr?.toLowerCase() ?? first.error.message.toLowerCase();
|
|
171
|
+
if (message.includes("already exists")) {
|
|
172
|
+
// If the branch already exists, add worktree for that branch.
|
|
173
|
+
const second = await runGit(["worktree", "add", targetDir, branch], workspaceFolder);
|
|
174
|
+
if (second.ok) {
|
|
175
|
+
return { slug: branch, directory: targetDir, branch };
|
|
176
|
+
}
|
|
177
|
+
throw second.error;
|
|
178
|
+
}
|
|
179
|
+
throw first.error;
|
|
180
|
+
}
|
|
181
|
+
export async function removeWorktree(params) {
|
|
182
|
+
const { workspaceFolder, logger } = params;
|
|
183
|
+
const directory = (params.directory ?? "").trim();
|
|
184
|
+
if (!directory) {
|
|
185
|
+
throw new Error("Invalid worktree directory");
|
|
186
|
+
}
|
|
187
|
+
logger?.debug?.({ directory, force: Boolean(params.force) }, "Removing git worktree");
|
|
188
|
+
const args = ["worktree", "remove"];
|
|
189
|
+
if (params.force) {
|
|
190
|
+
args.push("--force");
|
|
191
|
+
}
|
|
192
|
+
args.push(directory);
|
|
193
|
+
const result = await runGit(args, workspaceFolder);
|
|
194
|
+
if (!result.ok) {
|
|
195
|
+
throw result.error;
|
|
196
|
+
}
|
|
197
|
+
// Best-effort cleanup of stale metadata.
|
|
198
|
+
await runGit(["worktree", "prune"], workspaceFolder).catch(() => undefined);
|
|
199
|
+
}
|