@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,160 @@
|
|
|
1
|
+
import { tool } from "@opencode-ai/plugin/tool"
|
|
2
|
+
|
|
3
|
+
type BackgroundProcess = {
|
|
4
|
+
id: string
|
|
5
|
+
title: string
|
|
6
|
+
command: string
|
|
7
|
+
status: "running" | "stopped" | "error"
|
|
8
|
+
startedAt: string
|
|
9
|
+
stoppedAt?: string
|
|
10
|
+
exitCode?: number
|
|
11
|
+
outputSizeBytes?: number
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
type CodeNomadConfig = {
|
|
15
|
+
instanceId: string
|
|
16
|
+
baseUrl: string
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function createBackgroundProcessTools(config: CodeNomadConfig) {
|
|
20
|
+
const request = async <T>(path: string, init?: RequestInit): Promise<T> => {
|
|
21
|
+
|
|
22
|
+
const base = config.baseUrl.replace(/\/+$/, "")
|
|
23
|
+
const url = `${base}/workspaces/${config.instanceId}/plugin/background-processes${path}`
|
|
24
|
+
const headers = normalizeHeaders(init?.headers)
|
|
25
|
+
if (init?.body !== undefined) {
|
|
26
|
+
headers["Content-Type"] = "application/json"
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const response = await fetch(url, {
|
|
30
|
+
...init,
|
|
31
|
+
headers,
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
if (!response.ok) {
|
|
35
|
+
const message = await response.text()
|
|
36
|
+
throw new Error(message || `Request failed with ${response.status}`)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (response.status === 204) {
|
|
40
|
+
return undefined as T
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return (await response.json()) as T
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return {
|
|
47
|
+
run_background_process: tool({
|
|
48
|
+
description:
|
|
49
|
+
"Run a long-lived background process (dev servers, DBs, watchers) so it keeps running while you do other tasks. Use it for running processes that timeout otherwise or produce a lot of output.",
|
|
50
|
+
args: {
|
|
51
|
+
title: tool.schema.string().describe("Short label for the process (e.g. Dev server, DB server)"),
|
|
52
|
+
command: tool.schema.string().describe("Shell command to run in the workspace"),
|
|
53
|
+
},
|
|
54
|
+
async execute(args) {
|
|
55
|
+
const process = await request<BackgroundProcess>("", {
|
|
56
|
+
method: "POST",
|
|
57
|
+
body: JSON.stringify({ title: args.title, command: args.command }),
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
return `Started background process ${process.id} (${process.title})\nStatus: ${process.status}\nCommand: ${process.command}`
|
|
61
|
+
},
|
|
62
|
+
}),
|
|
63
|
+
list_background_processes: tool({
|
|
64
|
+
description: "List background processes running for this workspace.",
|
|
65
|
+
args: {},
|
|
66
|
+
async execute() {
|
|
67
|
+
const response = await request<{ processes: BackgroundProcess[] }>("")
|
|
68
|
+
if (response.processes.length === 0) {
|
|
69
|
+
return "No background processes running."
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return response.processes
|
|
73
|
+
.map((process) => {
|
|
74
|
+
const status = process.status === "running" ? "running" : process.status
|
|
75
|
+
const exit = process.exitCode !== undefined ? ` (exit ${process.exitCode})` : ""
|
|
76
|
+
const size =
|
|
77
|
+
typeof process.outputSizeBytes === "number" ? ` | ${Math.round(process.outputSizeBytes / 1024)}KB` : ""
|
|
78
|
+
return `- ${process.id} | ${process.title} | ${status}${exit}${size}\n ${process.command}`
|
|
79
|
+
})
|
|
80
|
+
.join("\n")
|
|
81
|
+
},
|
|
82
|
+
}),
|
|
83
|
+
read_background_process_output: tool({
|
|
84
|
+
description: "Read output from a background process. Use full, grep, head, or tail.",
|
|
85
|
+
args: {
|
|
86
|
+
id: tool.schema.string().describe("Background process ID"),
|
|
87
|
+
method: tool.schema
|
|
88
|
+
.enum(["full", "grep", "head", "tail"])
|
|
89
|
+
.default("full")
|
|
90
|
+
.describe("Method to read output"),
|
|
91
|
+
pattern: tool.schema.string().optional().describe("Pattern for grep method"),
|
|
92
|
+
lines: tool.schema.number().optional().describe("Number of lines for head/tail methods"),
|
|
93
|
+
},
|
|
94
|
+
async execute(args) {
|
|
95
|
+
if (args.method === "grep" && !args.pattern) {
|
|
96
|
+
return "Pattern is required for grep method."
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const params = new URLSearchParams({ method: args.method })
|
|
100
|
+
if (args.pattern) {
|
|
101
|
+
params.set("pattern", args.pattern)
|
|
102
|
+
}
|
|
103
|
+
if (args.lines) {
|
|
104
|
+
params.set("lines", String(args.lines))
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const response = await request<{ id: string; content: string; truncated: boolean; sizeBytes: number }>(
|
|
108
|
+
`/${args.id}/output?${params.toString()}`,
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
const header = response.truncated
|
|
112
|
+
? `Output (truncated, ${Math.round(response.sizeBytes / 1024)}KB):`
|
|
113
|
+
: `Output (${Math.round(response.sizeBytes / 1024)}KB):`
|
|
114
|
+
|
|
115
|
+
return `${header}\n\n${response.content}`
|
|
116
|
+
},
|
|
117
|
+
}),
|
|
118
|
+
stop_background_process: tool({
|
|
119
|
+
description: "Stop a background process (SIGTERM) but keep its output and entry.",
|
|
120
|
+
args: {
|
|
121
|
+
id: tool.schema.string().describe("Background process ID"),
|
|
122
|
+
},
|
|
123
|
+
async execute(args) {
|
|
124
|
+
const process = await request<BackgroundProcess>(`/${args.id}/stop`, { method: "POST" })
|
|
125
|
+
return `Stopped background process ${process.id} (${process.title}). Status: ${process.status}`
|
|
126
|
+
},
|
|
127
|
+
}),
|
|
128
|
+
terminate_background_process: tool({
|
|
129
|
+
description: "Terminate a background process and delete its output + entry.",
|
|
130
|
+
args: {
|
|
131
|
+
id: tool.schema.string().describe("Background process ID"),
|
|
132
|
+
},
|
|
133
|
+
async execute(args) {
|
|
134
|
+
await request<void>(`/${args.id}/terminate`, { method: "POST" })
|
|
135
|
+
return `Terminated background process ${args.id} and removed its output.`
|
|
136
|
+
},
|
|
137
|
+
}),
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function normalizeHeaders(headers: HeadersInit | undefined): Record<string, string> {
|
|
142
|
+
const output: Record<string, string> = {}
|
|
143
|
+
if (!headers) return output
|
|
144
|
+
|
|
145
|
+
if (headers instanceof Headers) {
|
|
146
|
+
headers.forEach((value, key) => {
|
|
147
|
+
output[key] = value
|
|
148
|
+
})
|
|
149
|
+
return output
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (Array.isArray(headers)) {
|
|
153
|
+
for (const [key, value] of headers) {
|
|
154
|
+
output[key] = value
|
|
155
|
+
}
|
|
156
|
+
return output
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return { ...headers }
|
|
160
|
+
}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
export type PluginEvent = {
|
|
2
|
+
type: string
|
|
3
|
+
properties?: Record<string, unknown>
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export type CodeNomadConfig = {
|
|
7
|
+
instanceId: string
|
|
8
|
+
baseUrl: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function getCodeNomadConfig(): CodeNomadConfig {
|
|
12
|
+
return {
|
|
13
|
+
instanceId: requireEnv("CODENOMAD_INSTANCE_ID"),
|
|
14
|
+
baseUrl: requireEnv("CODENOMAD_BASE_URL"),
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function createCodeNomadClient(config: CodeNomadConfig) {
|
|
19
|
+
return {
|
|
20
|
+
postEvent: (event: PluginEvent) => postPluginEvent(config.baseUrl, config.instanceId, event),
|
|
21
|
+
startEvents: (onEvent: (event: PluginEvent) => void) => startPluginEvents(config.baseUrl, config.instanceId, onEvent),
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function requireEnv(key: string): string {
|
|
26
|
+
const value = process.env[key]
|
|
27
|
+
if (!value || !value.trim()) {
|
|
28
|
+
throw new Error(`[CodeNomadPlugin] Missing required env var ${key}`)
|
|
29
|
+
}
|
|
30
|
+
return value
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function delay(ms: number) {
|
|
34
|
+
return new Promise<void>((resolve) => setTimeout(resolve, ms))
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async function postPluginEvent(baseUrl: string, instanceId: string, event: PluginEvent) {
|
|
38
|
+
const url = `${baseUrl.replace(/\/+$/, "")}/workspaces/${instanceId}/plugin/event`
|
|
39
|
+
const response = await fetch(url, {
|
|
40
|
+
method: "POST",
|
|
41
|
+
headers: {
|
|
42
|
+
"Content-Type": "application/json",
|
|
43
|
+
},
|
|
44
|
+
body: JSON.stringify(event),
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
if (!response.ok) {
|
|
48
|
+
throw new Error(`[CodeNomadPlugin] POST ${url} failed (${response.status})`)
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async function startPluginEvents(baseUrl: string, instanceId: string, onEvent: (event: PluginEvent) => void) {
|
|
53
|
+
const url = `${baseUrl.replace(/\/+$/, "")}/workspaces/${instanceId}/plugin/events`
|
|
54
|
+
|
|
55
|
+
// Fail plugin startup if we cannot establish the initial connection.
|
|
56
|
+
const initialBody = await connectWithRetries(url, 3)
|
|
57
|
+
|
|
58
|
+
// After startup, keep reconnecting; throw after 3 consecutive failures.
|
|
59
|
+
void consumeWithReconnect(url, onEvent, initialBody)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async function connectWithRetries(url: string, maxAttempts: number) {
|
|
63
|
+
let lastError: unknown
|
|
64
|
+
|
|
65
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
|
|
66
|
+
try {
|
|
67
|
+
const response = await fetch(url, { headers: { Accept: "text/event-stream" } })
|
|
68
|
+
if (!response.ok || !response.body) {
|
|
69
|
+
throw new Error(`[CodeNomadPlugin] SSE unavailable (${response.status})`)
|
|
70
|
+
}
|
|
71
|
+
return response.body
|
|
72
|
+
} catch (error) {
|
|
73
|
+
lastError = error
|
|
74
|
+
await delay(500 * attempt)
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const reason = lastError instanceof Error ? lastError.message : String(lastError)
|
|
79
|
+
throw new Error(`[CodeNomadPlugin] Failed to connect to CodeNomad after ${maxAttempts} retries: ${reason}`)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async function consumeWithReconnect(
|
|
83
|
+
url: string,
|
|
84
|
+
onEvent: (event: PluginEvent) => void,
|
|
85
|
+
initialBody: ReadableStream<Uint8Array>,
|
|
86
|
+
) {
|
|
87
|
+
let consecutiveFailures = 0
|
|
88
|
+
let body: ReadableStream<Uint8Array> | null = initialBody
|
|
89
|
+
|
|
90
|
+
while (true) {
|
|
91
|
+
try {
|
|
92
|
+
if (!body) {
|
|
93
|
+
body = await connectWithRetries(url, 3)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
await consumeSseBody(body, onEvent)
|
|
97
|
+
body = null
|
|
98
|
+
consecutiveFailures = 0
|
|
99
|
+
} catch (error) {
|
|
100
|
+
body = null
|
|
101
|
+
consecutiveFailures += 1
|
|
102
|
+
if (consecutiveFailures >= 3) {
|
|
103
|
+
const reason = error instanceof Error ? error.message : String(error)
|
|
104
|
+
throw new Error(`[CodeNomadPlugin] Plugin event stream failed after 3 retries: ${reason}`)
|
|
105
|
+
}
|
|
106
|
+
await delay(500 * consecutiveFailures)
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
async function consumeSseBody(body: ReadableStream<Uint8Array>, onEvent: (event: PluginEvent) => void) {
|
|
112
|
+
const reader = body.getReader()
|
|
113
|
+
const decoder = new TextDecoder()
|
|
114
|
+
let buffer = ""
|
|
115
|
+
|
|
116
|
+
while (true) {
|
|
117
|
+
const { done, value } = await reader.read()
|
|
118
|
+
if (done || !value) {
|
|
119
|
+
break
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
buffer += decoder.decode(value, { stream: true })
|
|
123
|
+
|
|
124
|
+
let separatorIndex = buffer.indexOf("\n\n")
|
|
125
|
+
while (separatorIndex >= 0) {
|
|
126
|
+
const chunk = buffer.slice(0, separatorIndex)
|
|
127
|
+
buffer = buffer.slice(separatorIndex + 2)
|
|
128
|
+
separatorIndex = buffer.indexOf("\n\n")
|
|
129
|
+
|
|
130
|
+
const event = parseSseChunk(chunk)
|
|
131
|
+
if (event) {
|
|
132
|
+
onEvent(event)
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
throw new Error("SSE stream ended")
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function parseSseChunk(chunk: string): PluginEvent | null {
|
|
141
|
+
const lines = chunk.split(/\r?\n/)
|
|
142
|
+
const dataLines: string[] = []
|
|
143
|
+
|
|
144
|
+
for (const line of lines) {
|
|
145
|
+
if (line.startsWith(":")) continue
|
|
146
|
+
if (line.startsWith("data:")) {
|
|
147
|
+
dataLines.push(line.slice(5).trimStart())
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (dataLines.length === 0) return null
|
|
152
|
+
|
|
153
|
+
const payload = dataLines.join("\n").trim()
|
|
154
|
+
if (!payload) return null
|
|
155
|
+
|
|
156
|
+
try {
|
|
157
|
+
const parsed = JSON.parse(payload)
|
|
158
|
+
if (!parsed || typeof parsed !== "object" || typeof (parsed as any).type !== "string") {
|
|
159
|
+
return null
|
|
160
|
+
}
|
|
161
|
+
return parsed as PluginEvent
|
|
162
|
+
} catch {
|
|
163
|
+
return null
|
|
164
|
+
}
|
|
165
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { existsSync } from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { fileURLToPath } from "url";
|
|
4
|
+
import { createLogger } from "./logger";
|
|
5
|
+
const log = createLogger({ component: "opencode-config" });
|
|
6
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
7
|
+
const __dirname = path.dirname(__filename);
|
|
8
|
+
const devTemplateDir = path.resolve(__dirname, "../../opencode-config");
|
|
9
|
+
const resourcesPath = process.resourcesPath;
|
|
10
|
+
const prodTemplateDirs = [
|
|
11
|
+
resourcesPath ? path.resolve(resourcesPath, "opencode-config") : undefined,
|
|
12
|
+
path.resolve(__dirname, "opencode-config"),
|
|
13
|
+
].filter((dir) => Boolean(dir));
|
|
14
|
+
const isDevBuild = Boolean(process.env.CODENOMAD_DEV ?? process.env.CLI_UI_DEV_SERVER) || existsSync(devTemplateDir);
|
|
15
|
+
const templateDir = isDevBuild
|
|
16
|
+
? devTemplateDir
|
|
17
|
+
: prodTemplateDirs.find((dir) => existsSync(dir)) ?? prodTemplateDirs[0];
|
|
18
|
+
export function getOpencodeConfigDir() {
|
|
19
|
+
if (!existsSync(templateDir)) {
|
|
20
|
+
throw new Error(`CodeNomad Opencode config template missing at ${templateDir}`);
|
|
21
|
+
}
|
|
22
|
+
if (isDevBuild) {
|
|
23
|
+
log.debug({ templateDir }, "Using Opencode config template directly (dev mode)");
|
|
24
|
+
}
|
|
25
|
+
return templateDir;
|
|
26
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export class PluginChannelManager {
|
|
2
|
+
constructor(logger) {
|
|
3
|
+
this.logger = logger;
|
|
4
|
+
this.clients = new Set();
|
|
5
|
+
}
|
|
6
|
+
register(workspaceId, reply) {
|
|
7
|
+
const connection = { workspaceId, reply };
|
|
8
|
+
this.clients.add(connection);
|
|
9
|
+
this.logger.debug({ workspaceId }, "Plugin SSE client connected");
|
|
10
|
+
let closed = false;
|
|
11
|
+
const close = () => {
|
|
12
|
+
if (closed)
|
|
13
|
+
return;
|
|
14
|
+
closed = true;
|
|
15
|
+
this.clients.delete(connection);
|
|
16
|
+
this.logger.debug({ workspaceId }, "Plugin SSE client disconnected");
|
|
17
|
+
};
|
|
18
|
+
return { close };
|
|
19
|
+
}
|
|
20
|
+
send(workspaceId, event) {
|
|
21
|
+
for (const client of this.clients) {
|
|
22
|
+
if (client.workspaceId !== workspaceId)
|
|
23
|
+
continue;
|
|
24
|
+
this.write(client.reply, event);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
broadcast(event) {
|
|
28
|
+
for (const client of this.clients) {
|
|
29
|
+
this.write(client.reply, event);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
write(reply, event) {
|
|
33
|
+
try {
|
|
34
|
+
reply.raw.write(`data: ${JSON.stringify(event)}\n\n`);
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
this.logger.warn({ err: error }, "Failed to write plugin SSE event");
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export function handlePluginEvent(workspaceId, event, deps) {
|
|
2
|
+
switch (event.type) {
|
|
3
|
+
case "codenomad.pong":
|
|
4
|
+
deps.logger.debug({ workspaceId, properties: event.properties }, "Plugin pong received");
|
|
5
|
+
return;
|
|
6
|
+
default:
|
|
7
|
+
deps.logger.debug({ workspaceId, eventType: event.type }, "Unhandled plugin event");
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
export function buildPingEvent() {
|
|
11
|
+
return {
|
|
12
|
+
type: "codenomad.ping",
|
|
13
|
+
properties: {
|
|
14
|
+
ts: Date.now(),
|
|
15
|
+
},
|
|
16
|
+
};
|
|
17
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { fetch } from "undici";
|
|
2
|
+
import { compareVersionStrings, stripTagPrefix } from "./release-monitor";
|
|
3
|
+
const DEFAULT_POLL_INTERVAL_MS = 15 * 60 * 1000;
|
|
4
|
+
export function startDevReleaseMonitor(options) {
|
|
5
|
+
let stopped = false;
|
|
6
|
+
let timer = null;
|
|
7
|
+
const pollIntervalMs = Number.isFinite(options.pollIntervalMs) && (options.pollIntervalMs ?? 0) > 0
|
|
8
|
+
? options.pollIntervalMs
|
|
9
|
+
: DEFAULT_POLL_INTERVAL_MS;
|
|
10
|
+
const refresh = async () => {
|
|
11
|
+
if (stopped)
|
|
12
|
+
return;
|
|
13
|
+
try {
|
|
14
|
+
const release = await fetchLatestPrerelease({
|
|
15
|
+
repo: options.repo,
|
|
16
|
+
currentVersion: options.currentVersion,
|
|
17
|
+
});
|
|
18
|
+
options.onUpdate(release);
|
|
19
|
+
}
|
|
20
|
+
catch (error) {
|
|
21
|
+
options.logger.debug({ err: error }, "Failed to refresh dev prerelease information");
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
void refresh();
|
|
25
|
+
timer = setInterval(() => void refresh(), pollIntervalMs);
|
|
26
|
+
return {
|
|
27
|
+
stop() {
|
|
28
|
+
stopped = true;
|
|
29
|
+
if (timer) {
|
|
30
|
+
clearInterval(timer);
|
|
31
|
+
timer = null;
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
async function fetchLatestPrerelease(args) {
|
|
37
|
+
const normalizedRepo = args.repo.trim();
|
|
38
|
+
if (!/^[^/\s]+\/[^/\s]+$/.test(normalizedRepo)) {
|
|
39
|
+
throw new Error(`Invalid GitHub repo: ${args.repo}`);
|
|
40
|
+
}
|
|
41
|
+
const apiUrl = `https://api.github.com/repos/${normalizedRepo}/releases?per_page=20`;
|
|
42
|
+
const response = await fetch(apiUrl, {
|
|
43
|
+
headers: {
|
|
44
|
+
Accept: "application/vnd.github+json",
|
|
45
|
+
"User-Agent": "CodeNomad-CLI",
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
if (!response.ok) {
|
|
49
|
+
throw new Error(`GitHub releases API responded with ${response.status}`);
|
|
50
|
+
}
|
|
51
|
+
const list = (await response.json());
|
|
52
|
+
const latest = list.find((r) => r && r.prerelease === true && r.draft !== true);
|
|
53
|
+
if (!latest) {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
const tag = latest.tag_name || latest.name;
|
|
57
|
+
if (!tag) {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
const normalizedVersion = stripTagPrefix(tag);
|
|
61
|
+
if (!normalizedVersion) {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
if (compareVersionStrings(normalizedVersion, args.currentVersion) <= 0) {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
68
|
+
version: normalizedVersion,
|
|
69
|
+
tag,
|
|
70
|
+
url: latest.html_url ?? `https://github.com/${normalizedRepo}/releases/tag/${encodeURIComponent(tag)}`,
|
|
71
|
+
channel: "dev",
|
|
72
|
+
publishedAt: latest.published_at ?? latest.created_at,
|
|
73
|
+
notes: latest.body,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { fetch } from "undici";
|
|
2
|
+
const RELEASES_API_URL = "https://api.github.com/repos/NeuralNomadsAI/CodeNomad/releases/latest";
|
|
3
|
+
export function startReleaseMonitor(options) {
|
|
4
|
+
let stopped = false;
|
|
5
|
+
const refreshRelease = async () => {
|
|
6
|
+
if (stopped)
|
|
7
|
+
return;
|
|
8
|
+
try {
|
|
9
|
+
const release = await fetchLatestRelease(options);
|
|
10
|
+
options.onUpdate(release);
|
|
11
|
+
}
|
|
12
|
+
catch (error) {
|
|
13
|
+
options.logger.warn({ err: error }, "Failed to refresh release information");
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
void refreshRelease();
|
|
17
|
+
return {
|
|
18
|
+
stop() {
|
|
19
|
+
stopped = true;
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
export function compareVersionStrings(a, b) {
|
|
24
|
+
const left = parseVersion(a);
|
|
25
|
+
const right = parseVersion(b);
|
|
26
|
+
return compareVersions(left, right);
|
|
27
|
+
}
|
|
28
|
+
async function fetchLatestRelease(options) {
|
|
29
|
+
const response = await fetch(RELEASES_API_URL, {
|
|
30
|
+
headers: {
|
|
31
|
+
Accept: "application/vnd.github+json",
|
|
32
|
+
"User-Agent": "CodeNomad-CLI",
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
if (!response.ok) {
|
|
36
|
+
throw new Error(`Release API responded with ${response.status}`);
|
|
37
|
+
}
|
|
38
|
+
const json = (await response.json());
|
|
39
|
+
const tagFromServer = json.tag_name || json.name;
|
|
40
|
+
if (!tagFromServer) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
const normalizedVersion = stripTagPrefix(tagFromServer);
|
|
44
|
+
if (!normalizedVersion) {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
const current = parseVersion(options.currentVersion);
|
|
48
|
+
const remote = parseVersion(normalizedVersion);
|
|
49
|
+
if (compareVersions(remote, current) <= 0) {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
return {
|
|
53
|
+
version: normalizedVersion,
|
|
54
|
+
tag: tagFromServer,
|
|
55
|
+
url: json.html_url ?? `https://github.com/NeuralNomadsAI/CodeNomad/releases/tag/${encodeURIComponent(tagFromServer)}`,
|
|
56
|
+
channel: json.prerelease || normalizedVersion.includes("-") ? "dev" : "stable",
|
|
57
|
+
publishedAt: json.published_at ?? json.created_at,
|
|
58
|
+
notes: json.body,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
export function stripTagPrefix(tag) {
|
|
62
|
+
if (!tag)
|
|
63
|
+
return null;
|
|
64
|
+
const trimmed = tag.trim();
|
|
65
|
+
if (!trimmed)
|
|
66
|
+
return null;
|
|
67
|
+
return trimmed.replace(/^v/i, "");
|
|
68
|
+
}
|
|
69
|
+
function parseVersion(value) {
|
|
70
|
+
const normalized = stripTagPrefix(value) ?? "0.0.0";
|
|
71
|
+
const dashIndex = normalized.indexOf("-");
|
|
72
|
+
const core = dashIndex >= 0 ? normalized.slice(0, dashIndex) : normalized;
|
|
73
|
+
const prerelease = dashIndex >= 0 ? normalized.slice(dashIndex + 1) : null;
|
|
74
|
+
const [major = 0, minor = 0, patch = 0] = core.split(".").map((segment) => {
|
|
75
|
+
const parsed = Number.parseInt(segment, 10);
|
|
76
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
77
|
+
});
|
|
78
|
+
return {
|
|
79
|
+
major,
|
|
80
|
+
minor,
|
|
81
|
+
patch,
|
|
82
|
+
prerelease,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
function compareVersions(a, b) {
|
|
86
|
+
if (a.major !== b.major) {
|
|
87
|
+
return a.major > b.major ? 1 : -1;
|
|
88
|
+
}
|
|
89
|
+
if (a.minor !== b.minor) {
|
|
90
|
+
return a.minor > b.minor ? 1 : -1;
|
|
91
|
+
}
|
|
92
|
+
if (a.patch !== b.patch) {
|
|
93
|
+
return a.patch > b.patch ? 1 : -1;
|
|
94
|
+
}
|
|
95
|
+
const aPre = a.prerelease && a.prerelease.length > 0 ? a.prerelease : null;
|
|
96
|
+
const bPre = b.prerelease && b.prerelease.length > 0 ? b.prerelease : null;
|
|
97
|
+
if (aPre === bPre) {
|
|
98
|
+
return 0;
|
|
99
|
+
}
|
|
100
|
+
if (!aPre) {
|
|
101
|
+
return 1;
|
|
102
|
+
}
|
|
103
|
+
if (!bPre) {
|
|
104
|
+
return -1;
|
|
105
|
+
}
|
|
106
|
+
return aPre.localeCompare(bPre);
|
|
107
|
+
}
|