@dyyz1993/codenomad 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +173 -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 +140 -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 +576 -0
- package/dist/bin.js +24 -0
- package/dist/cli-upgrade.js +53 -0
- package/dist/cli-upgrade.test.js +31 -0
- package/dist/clients/connection-manager.js +93 -0
- package/dist/config/location.js +57 -0
- package/dist/config/schema.js +71 -0
- package/dist/events/bus.js +45 -0
- package/dist/filesystem/__tests__/browser.test.js +65 -0
- package/dist/filesystem/__tests__/search-cache.test.js +40 -0
- package/dist/filesystem/browser.js +306 -0
- package/dist/filesystem/search-cache.js +43 -0
- package/dist/filesystem/search.js +135 -0
- package/dist/index.js +477 -0
- package/dist/launcher.js +149 -0
- package/dist/loader.js +21 -0
- package/dist/logger.js +109 -0
- package/dist/opencode-config/README.md +32 -0
- package/dist/opencode-config/opencode.jsonc +3 -0
- package/dist/opencode-config/package-lock.json +380 -0
- package/dist/opencode-config/package.json +9 -0
- package/dist/opencode-config/plugin/codenomad.ts +62 -0
- package/dist/opencode-config/plugin/lib/background-process.ts +265 -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.js +26 -0
- package/dist/plugins/channel.js +40 -0
- package/dist/plugins/handlers.js +17 -0
- package/dist/plugins/voice-mode.js +78 -0
- package/dist/releases/dev-release-monitor.js +75 -0
- package/dist/releases/release-monitor.js +107 -0
- package/dist/server/__tests__/network-addresses.test.js +68 -0
- package/dist/server/__tests__/remote-proxy.test.js +204 -0
- package/dist/server/http-server.js +926 -0
- package/dist/server/network-addresses.js +114 -0
- package/dist/server/remote-proxy.js +466 -0
- package/dist/server/routes/auth-pages/login.html +135 -0
- package/dist/server/routes/auth-pages/token.html +93 -0
- package/dist/server/routes/auth.js +148 -0
- package/dist/server/routes/background-processes.js +78 -0
- package/dist/server/routes/events.js +66 -0
- package/dist/server/routes/filesystem.js +43 -0
- package/dist/server/routes/meta.js +44 -0
- package/dist/server/routes/plugin.js +70 -0
- package/dist/server/routes/remote-proxy.js +42 -0
- package/dist/server/routes/remote-servers.js +142 -0
- package/dist/server/routes/settings.js +69 -0
- package/dist/server/routes/sidecars.js +46 -0
- package/dist/server/routes/speech.js +63 -0
- package/dist/server/routes/storage.js +52 -0
- package/dist/server/routes/workspaces.js +221 -0
- package/dist/server/routes/worktrees.js +156 -0
- package/dist/server/tls.js +224 -0
- package/dist/settings/binaries.js +37 -0
- package/dist/settings/merge-patch.js +33 -0
- package/dist/settings/migrate.js +238 -0
- package/dist/settings/public-config.js +33 -0
- package/dist/settings/service.js +101 -0
- package/dist/settings/yaml-doc-store.js +96 -0
- package/dist/sidecars/manager.js +193 -0
- package/dist/speech/providers/openai-compatible.js +189 -0
- package/dist/speech/service.js +58 -0
- package/dist/storage/instance-store.js +56 -0
- package/dist/ui/__tests__/remote-ui.test.js +67 -0
- package/dist/ui/remote-ui.js +462 -0
- package/dist/workspaces/__tests__/git-worktrees.test.js +43 -0
- package/dist/workspaces/__tests__/spawn.test.js +140 -0
- package/dist/workspaces/git-mutations.js +98 -0
- package/dist/workspaces/git-status.js +323 -0
- package/dist/workspaces/git-worktrees.js +216 -0
- package/dist/workspaces/instance-events.js +180 -0
- package/dist/workspaces/manager.js +368 -0
- package/dist/workspaces/opencode-auth.js +34 -0
- package/dist/workspaces/opencode-auth.test.js +34 -0
- package/dist/workspaces/runtime.js +366 -0
- package/dist/workspaces/spawn.js +219 -0
- package/dist/workspaces/worktree-directory.js +74 -0
- package/dist/workspaces/worktree-map.js +116 -0
- package/package.json +50 -0
- package/public/apple-touch-icon-180x180.png +0 -0
- package/public/assets/ChangesTab-BRNmSjeC.js +2 -0
- package/public/assets/CodeNomad-Icon-bmTWNPXy.png +0 -0
- package/public/assets/DiffToolbar-DPPgfx42.js +1 -0
- package/public/assets/FilesTab-sEeGPOoz.js +2 -0
- package/public/assets/GitChangesTab-tJ-TCq0c.js +2 -0
- package/public/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
- package/public/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
- package/public/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
- package/public/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
- package/public/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
- package/public/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
- package/public/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
- package/public/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
- package/public/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
- package/public/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
- package/public/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
- package/public/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
- package/public/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
- package/public/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
- package/public/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
- package/public/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
- package/public/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
- package/public/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
- package/public/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
- package/public/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
- package/public/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
- package/public/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
- package/public/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
- package/public/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
- package/public/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
- package/public/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
- package/public/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
- package/public/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
- package/public/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
- package/public/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
- package/public/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
- package/public/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
- package/public/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
- package/public/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
- package/public/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
- package/public/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
- package/public/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
- package/public/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
- package/public/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
- package/public/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
- package/public/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
- package/public/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
- package/public/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
- package/public/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
- package/public/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
- package/public/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
- package/public/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
- package/public/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
- package/public/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
- package/public/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
- package/public/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
- package/public/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
- package/public/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
- package/public/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
- package/public/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
- package/public/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
- package/public/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
- package/public/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
- package/public/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
- package/public/assets/SplitFilePanel-CHqNLZvU.js +1 -0
- package/public/assets/StatusTab-DMd0ByCA.js +1 -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/align-justify-BWapkGTe.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/bundle-full-CXlKQh5B.js +13 -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/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/diff-viewer-BFtW5J8P.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/fast-diff-vendor-DgdwVvTQ.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-diff-vendor-CSgooKT_.js +52 -0
- package/public/assets/git-diff-vendor-HAZkIolJ.css +19 -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/highlight-vendor-8FKMu9os.js +10 -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-Bdy3MTIn.js +1 -0
- package/public/assets/index-BvRe9GiS.js +1 -0
- package/public/assets/index-Ctq8RqRf.js +1 -0
- package/public/assets/index-D-NvysdX.js +1 -0
- package/public/assets/index-D6RMBXUk.js +2 -0
- package/public/assets/index-DXI9DoVB.css +1 -0
- package/public/assets/index-DYWkPhKo.js +1 -0
- package/public/assets/index-D_FDiI6a.js +1 -0
- package/public/assets/index-Dty5bSHf.js +1 -0
- package/public/assets/index-H16e4Rqc.js +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-W2Y_wR0P.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-Bala0YJ4.js +57 -0
- package/public/assets/make-CHLpvVh8.js +1 -0
- package/public/assets/markdown-Cvjx9yec.js +1 -0
- package/public/assets/markdown-Db8cvZ4Z.js +315 -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/monaco-viewer-DWQqXKm3.js +26 -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/tool-call-D0BYXlFe.js +59 -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/unified-picker-QgONWj_1.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/wrap-text-CEH9wOr_.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 +42 -0
- package/public/loading.html +33 -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
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
import { spawn, spawnSync } from "child_process";
|
|
2
|
+
import { existsSync, statSync } from "fs";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import { buildSpawnSpec, buildWslSignalSpec } from "./spawn";
|
|
5
|
+
const SENSITIVE_ENV_KEY = /(PASSWORD|TOKEN|SECRET)/i;
|
|
6
|
+
const WSL_PID_MARKER = "__CODENOMAD_WSL_PID__:";
|
|
7
|
+
function redactEnvironment(env) {
|
|
8
|
+
const redacted = {};
|
|
9
|
+
for (const [key, value] of Object.entries(env)) {
|
|
10
|
+
if (value === undefined) {
|
|
11
|
+
redacted[key] = value;
|
|
12
|
+
continue;
|
|
13
|
+
}
|
|
14
|
+
redacted[key] = SENSITIVE_ENV_KEY.test(key) ? "[REDACTED]" : value;
|
|
15
|
+
}
|
|
16
|
+
return redacted;
|
|
17
|
+
}
|
|
18
|
+
export class WorkspaceRuntime {
|
|
19
|
+
constructor(eventBus, logger) {
|
|
20
|
+
this.eventBus = eventBus;
|
|
21
|
+
this.logger = logger;
|
|
22
|
+
this.processes = new Map();
|
|
23
|
+
}
|
|
24
|
+
async launch(options) {
|
|
25
|
+
this.validateFolder(options.folder);
|
|
26
|
+
const logLevel = typeof options.logLevel === "string" ? options.logLevel.toUpperCase() : "DEBUG";
|
|
27
|
+
const args = ["serve", "--port", "0", "--print-logs", "--log-level", logLevel];
|
|
28
|
+
const env = { ...process.env, ...(options.environment ?? {}) };
|
|
29
|
+
let exitResolve = null;
|
|
30
|
+
const exitPromise = new Promise((resolveExit) => {
|
|
31
|
+
exitResolve = resolveExit;
|
|
32
|
+
});
|
|
33
|
+
// Store recent output for debugging - keep last 50 lines from each stream
|
|
34
|
+
const MAX_OUTPUT_LINES = 50;
|
|
35
|
+
const recentStdout = [];
|
|
36
|
+
const recentStderr = [];
|
|
37
|
+
const getLastOutput = () => {
|
|
38
|
+
const combined = [];
|
|
39
|
+
if (recentStderr.length > 0) {
|
|
40
|
+
combined.push("Error Stream");
|
|
41
|
+
combined.push(...recentStderr.slice(-10));
|
|
42
|
+
}
|
|
43
|
+
if (recentStdout.length > 0) {
|
|
44
|
+
combined.push("Output Stream");
|
|
45
|
+
combined.push(...recentStdout.slice(-10));
|
|
46
|
+
}
|
|
47
|
+
return combined.join("\n");
|
|
48
|
+
};
|
|
49
|
+
return new Promise((resolve, reject) => {
|
|
50
|
+
const propagatedEnvKeys = Object.keys(options.environment ?? {});
|
|
51
|
+
const spec = buildSpawnSpec(options.binaryPath, args, {
|
|
52
|
+
cwd: options.folder,
|
|
53
|
+
env,
|
|
54
|
+
propagateEnvKeys: propagatedEnvKeys,
|
|
55
|
+
wslPidMarker: WSL_PID_MARKER,
|
|
56
|
+
});
|
|
57
|
+
const commandLine = [spec.command, ...spec.args].join(" ");
|
|
58
|
+
this.logger.info({
|
|
59
|
+
workspaceId: options.workspaceId,
|
|
60
|
+
folder: options.folder,
|
|
61
|
+
binary: options.binaryPath,
|
|
62
|
+
spawnCommand: spec.command,
|
|
63
|
+
commandLine,
|
|
64
|
+
}, "Launching OpenCode process");
|
|
65
|
+
this.logger.debug({
|
|
66
|
+
workspaceId: options.workspaceId,
|
|
67
|
+
spawnArgs: spec.args,
|
|
68
|
+
}, "OpenCode spawn args");
|
|
69
|
+
this.logger.trace({
|
|
70
|
+
workspaceId: options.workspaceId,
|
|
71
|
+
env: redactEnvironment(env),
|
|
72
|
+
}, "OpenCode spawn environment");
|
|
73
|
+
const detached = process.platform !== "win32";
|
|
74
|
+
const child = spawn(spec.command, spec.args, {
|
|
75
|
+
cwd: spec.cwd,
|
|
76
|
+
env: spec.env,
|
|
77
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
78
|
+
detached,
|
|
79
|
+
...spec.options,
|
|
80
|
+
});
|
|
81
|
+
const managed = {
|
|
82
|
+
child,
|
|
83
|
+
requestedStop: false,
|
|
84
|
+
...(spec.wsl ? { wsl: { distro: spec.wsl.distro, linuxPid: null } } : {}),
|
|
85
|
+
};
|
|
86
|
+
this.processes.set(options.workspaceId, managed);
|
|
87
|
+
let stdoutBuffer = "";
|
|
88
|
+
let stderrBuffer = "";
|
|
89
|
+
let portFound = false;
|
|
90
|
+
let warningTimer = null;
|
|
91
|
+
const startWarningTimer = () => {
|
|
92
|
+
warningTimer = setInterval(() => {
|
|
93
|
+
this.logger.warn({ workspaceId: options.workspaceId }, "Workspace runtime has not reported a port yet");
|
|
94
|
+
}, 10000);
|
|
95
|
+
};
|
|
96
|
+
const stopWarningTimer = () => {
|
|
97
|
+
if (warningTimer) {
|
|
98
|
+
clearInterval(warningTimer);
|
|
99
|
+
warningTimer = null;
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
startWarningTimer();
|
|
103
|
+
const cleanupStreams = () => {
|
|
104
|
+
stopWarningTimer();
|
|
105
|
+
child.stdout?.removeAllListeners();
|
|
106
|
+
child.stderr?.removeAllListeners();
|
|
107
|
+
};
|
|
108
|
+
const handleExit = (code, signal) => {
|
|
109
|
+
this.logger.info({ workspaceId: options.workspaceId, code, signal }, "OpenCode process exited");
|
|
110
|
+
this.processes.delete(options.workspaceId);
|
|
111
|
+
cleanupStreams();
|
|
112
|
+
child.removeListener("error", handleError);
|
|
113
|
+
child.removeListener("exit", handleExit);
|
|
114
|
+
const exitInfo = {
|
|
115
|
+
workspaceId: options.workspaceId,
|
|
116
|
+
code,
|
|
117
|
+
signal,
|
|
118
|
+
requested: managed.requestedStop,
|
|
119
|
+
};
|
|
120
|
+
if (exitResolve) {
|
|
121
|
+
exitResolve(exitInfo);
|
|
122
|
+
exitResolve = null;
|
|
123
|
+
}
|
|
124
|
+
if (!portFound) {
|
|
125
|
+
const recentOutput = getLastOutput().trim();
|
|
126
|
+
const reason = recentOutput || stderrBuffer || `Process exited with code ${code}`;
|
|
127
|
+
reject(new Error(reason));
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
options.onExit?.(exitInfo);
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
const handleError = (error) => {
|
|
134
|
+
cleanupStreams();
|
|
135
|
+
child.removeListener("exit", handleExit);
|
|
136
|
+
this.processes.delete(options.workspaceId);
|
|
137
|
+
this.logger.error({ workspaceId: options.workspaceId, err: error }, "Workspace runtime error");
|
|
138
|
+
if (exitResolve) {
|
|
139
|
+
exitResolve({ workspaceId: options.workspaceId, code: null, signal: null, requested: managed.requestedStop });
|
|
140
|
+
exitResolve = null;
|
|
141
|
+
}
|
|
142
|
+
reject(error);
|
|
143
|
+
};
|
|
144
|
+
child.on("error", handleError);
|
|
145
|
+
child.on("exit", handleExit);
|
|
146
|
+
child.stdout?.on("data", (data) => {
|
|
147
|
+
const text = data.toString();
|
|
148
|
+
stdoutBuffer += text;
|
|
149
|
+
const lines = stdoutBuffer.split("\n");
|
|
150
|
+
stdoutBuffer = lines.pop() ?? "";
|
|
151
|
+
for (const line of lines) {
|
|
152
|
+
const trimmed = line.trim();
|
|
153
|
+
if (!trimmed)
|
|
154
|
+
continue;
|
|
155
|
+
if (managed.wsl && trimmed.startsWith(WSL_PID_MARKER)) {
|
|
156
|
+
const linuxPid = Number.parseInt(trimmed.slice(WSL_PID_MARKER.length), 10);
|
|
157
|
+
if (Number.isFinite(linuxPid) && linuxPid > 0) {
|
|
158
|
+
managed.wsl.linuxPid = linuxPid;
|
|
159
|
+
this.logger.debug({ workspaceId: options.workspaceId, linuxPid }, "Captured WSL OpenCode PID");
|
|
160
|
+
}
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
recentStdout.push(trimmed);
|
|
164
|
+
if (recentStdout.length > MAX_OUTPUT_LINES) {
|
|
165
|
+
recentStdout.shift();
|
|
166
|
+
}
|
|
167
|
+
this.emitLog(options.workspaceId, "info", line);
|
|
168
|
+
if (!portFound) {
|
|
169
|
+
const portMatch = line.match(/opencode server listening on http:\/\/.+:(\d+)/i);
|
|
170
|
+
if (portMatch) {
|
|
171
|
+
portFound = true;
|
|
172
|
+
stopWarningTimer();
|
|
173
|
+
child.removeListener("error", handleError);
|
|
174
|
+
const port = parseInt(portMatch[1], 10);
|
|
175
|
+
this.logger.info({ workspaceId: options.workspaceId, port }, "Workspace runtime allocated port");
|
|
176
|
+
resolve({ pid: child.pid, port, exitPromise, getLastOutput });
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
child.stderr?.on("data", (data) => {
|
|
182
|
+
const text = data.toString();
|
|
183
|
+
stderrBuffer += text;
|
|
184
|
+
const lines = stderrBuffer.split("\n");
|
|
185
|
+
stderrBuffer = lines.pop() ?? "";
|
|
186
|
+
for (const line of lines) {
|
|
187
|
+
const trimmed = line.trim();
|
|
188
|
+
if (!trimmed)
|
|
189
|
+
continue;
|
|
190
|
+
recentStderr.push(trimmed);
|
|
191
|
+
if (recentStderr.length > MAX_OUTPUT_LINES) {
|
|
192
|
+
recentStderr.shift();
|
|
193
|
+
}
|
|
194
|
+
this.emitLog(options.workspaceId, "error", line);
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
async stop(workspaceId) {
|
|
200
|
+
const managed = this.processes.get(workspaceId);
|
|
201
|
+
if (!managed)
|
|
202
|
+
return;
|
|
203
|
+
managed.requestedStop = true;
|
|
204
|
+
const child = managed.child;
|
|
205
|
+
this.logger.info({ workspaceId }, "Stopping OpenCode process");
|
|
206
|
+
const pid = child.pid;
|
|
207
|
+
if (!pid) {
|
|
208
|
+
this.logger.warn({ workspaceId }, "Workspace process missing PID; cannot stop");
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
const isAlreadyExited = () => child.exitCode !== null || child.signalCode !== null;
|
|
212
|
+
const tryKillPosixGroup = (signal) => {
|
|
213
|
+
try {
|
|
214
|
+
// Negative PID targets the process group (POSIX).
|
|
215
|
+
process.kill(-pid, signal);
|
|
216
|
+
return true;
|
|
217
|
+
}
|
|
218
|
+
catch (error) {
|
|
219
|
+
const err = error;
|
|
220
|
+
if (err?.code === "ESRCH") {
|
|
221
|
+
return true;
|
|
222
|
+
}
|
|
223
|
+
this.logger.debug({ workspaceId, pid, err }, "Failed to signal POSIX process group");
|
|
224
|
+
return false;
|
|
225
|
+
}
|
|
226
|
+
};
|
|
227
|
+
const tryKillSinglePid = (signal) => {
|
|
228
|
+
try {
|
|
229
|
+
process.kill(pid, signal);
|
|
230
|
+
return true;
|
|
231
|
+
}
|
|
232
|
+
catch (error) {
|
|
233
|
+
const err = error;
|
|
234
|
+
if (err?.code === "ESRCH") {
|
|
235
|
+
return true;
|
|
236
|
+
}
|
|
237
|
+
this.logger.debug({ workspaceId, pid, err }, "Failed to signal workspace PID");
|
|
238
|
+
return false;
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
const tryTaskkill = (force) => {
|
|
242
|
+
const args = ["/PID", String(pid), "/T"];
|
|
243
|
+
if (force) {
|
|
244
|
+
args.push("/F");
|
|
245
|
+
}
|
|
246
|
+
try {
|
|
247
|
+
const result = spawnSync("taskkill", args, { encoding: "utf8" });
|
|
248
|
+
const exitCode = result.status;
|
|
249
|
+
if (exitCode === 0) {
|
|
250
|
+
return true;
|
|
251
|
+
}
|
|
252
|
+
// If the PID is already gone, treat it as success.
|
|
253
|
+
const stderr = (result.stderr ?? "").toString().toLowerCase();
|
|
254
|
+
const stdout = (result.stdout ?? "").toString().toLowerCase();
|
|
255
|
+
const combined = `${stdout}\n${stderr}`;
|
|
256
|
+
if (combined.includes("not found") || combined.includes("no running instance") || combined.includes("process") && combined.includes("not")) {
|
|
257
|
+
return true;
|
|
258
|
+
}
|
|
259
|
+
this.logger.debug({ workspaceId, pid, exitCode, stderr: result.stderr, stdout: result.stdout }, "taskkill failed");
|
|
260
|
+
return false;
|
|
261
|
+
}
|
|
262
|
+
catch (error) {
|
|
263
|
+
this.logger.debug({ workspaceId, pid, err: error }, "taskkill failed to execute");
|
|
264
|
+
return false;
|
|
265
|
+
}
|
|
266
|
+
};
|
|
267
|
+
const trySignalWslProcess = (signal) => {
|
|
268
|
+
if (process.platform !== "win32" || !managed.wsl?.linuxPid) {
|
|
269
|
+
return false;
|
|
270
|
+
}
|
|
271
|
+
try {
|
|
272
|
+
const spec = buildWslSignalSpec(managed.wsl.distro, managed.wsl.linuxPid, signal);
|
|
273
|
+
const result = spawnSync(spec.command, spec.args, { encoding: "utf8" });
|
|
274
|
+
const exitCode = result.status;
|
|
275
|
+
if (exitCode === 0) {
|
|
276
|
+
return true;
|
|
277
|
+
}
|
|
278
|
+
const stderr = (result.stderr ?? "").toString().toLowerCase();
|
|
279
|
+
const stdout = (result.stdout ?? "").toString().toLowerCase();
|
|
280
|
+
const combined = `${stdout}\n${stderr}`;
|
|
281
|
+
if (combined.includes("no such process") || combined.includes("not found")) {
|
|
282
|
+
return true;
|
|
283
|
+
}
|
|
284
|
+
this.logger.debug({ workspaceId, pid, linuxPid: managed.wsl.linuxPid, distro: managed.wsl.distro, exitCode, stderr: result.stderr, stdout: result.stdout }, "WSL kill failed");
|
|
285
|
+
return false;
|
|
286
|
+
}
|
|
287
|
+
catch (error) {
|
|
288
|
+
this.logger.debug({ workspaceId, pid, linuxPid: managed.wsl.linuxPid, distro: managed.wsl.distro, err: error }, "WSL kill failed to execute");
|
|
289
|
+
return false;
|
|
290
|
+
}
|
|
291
|
+
};
|
|
292
|
+
const sendStopSignal = (signal) => {
|
|
293
|
+
if (process.platform === "win32") {
|
|
294
|
+
// WSL-backed launches need a Linux signal first because the tracked Windows PID belongs to wsl.exe.
|
|
295
|
+
if (!trySignalWslProcess(signal)) {
|
|
296
|
+
// Fallback to the Windows process tree rooted at pid. Use /F only for escalation.
|
|
297
|
+
tryTaskkill(signal === "SIGKILL");
|
|
298
|
+
}
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
// Prefer process-group signaling so wrapper launchers (bun/node) don't orphan the real server.
|
|
302
|
+
const groupOk = tryKillPosixGroup(signal);
|
|
303
|
+
if (!groupOk) {
|
|
304
|
+
// Fallback to direct PID kill.
|
|
305
|
+
tryKillSinglePid(signal);
|
|
306
|
+
}
|
|
307
|
+
};
|
|
308
|
+
await new Promise((resolve, reject) => {
|
|
309
|
+
let escalationTimer = null;
|
|
310
|
+
const cleanup = () => {
|
|
311
|
+
child.removeListener("exit", onExit);
|
|
312
|
+
child.removeListener("error", onError);
|
|
313
|
+
if (escalationTimer) {
|
|
314
|
+
clearTimeout(escalationTimer);
|
|
315
|
+
escalationTimer = null;
|
|
316
|
+
}
|
|
317
|
+
};
|
|
318
|
+
const onExit = () => {
|
|
319
|
+
cleanup();
|
|
320
|
+
resolve();
|
|
321
|
+
};
|
|
322
|
+
const onError = (error) => {
|
|
323
|
+
cleanup();
|
|
324
|
+
reject(error);
|
|
325
|
+
};
|
|
326
|
+
if (isAlreadyExited()) {
|
|
327
|
+
this.logger.debug({ workspaceId, exitCode: child.exitCode, signal: child.signalCode }, "Process already exited");
|
|
328
|
+
cleanup();
|
|
329
|
+
resolve();
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
child.once("exit", onExit);
|
|
333
|
+
child.once("error", onError);
|
|
334
|
+
this.logger.debug({ workspaceId, pid, detached: process.platform !== "win32" }, "Sending SIGTERM to workspace process (tree/group)");
|
|
335
|
+
sendStopSignal("SIGTERM");
|
|
336
|
+
escalationTimer = setTimeout(() => {
|
|
337
|
+
escalationTimer = null;
|
|
338
|
+
if (isAlreadyExited()) {
|
|
339
|
+
this.logger.debug({ workspaceId, pid }, "Workspace exited before SIGKILL escalation");
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
this.logger.warn({ workspaceId, pid }, "Process did not stop after SIGTERM, escalating");
|
|
343
|
+
sendStopSignal("SIGKILL");
|
|
344
|
+
}, 2000);
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
emitLog(workspaceId, level, message) {
|
|
348
|
+
const entry = {
|
|
349
|
+
workspaceId,
|
|
350
|
+
timestamp: new Date().toISOString(),
|
|
351
|
+
level,
|
|
352
|
+
message: message.trim(),
|
|
353
|
+
};
|
|
354
|
+
this.eventBus.publish({ type: "workspace.log", entry });
|
|
355
|
+
}
|
|
356
|
+
validateFolder(folder) {
|
|
357
|
+
const resolved = path.resolve(folder);
|
|
358
|
+
if (!existsSync(resolved)) {
|
|
359
|
+
throw new Error(`Folder does not exist: ${resolved}`);
|
|
360
|
+
}
|
|
361
|
+
const stats = statSync(resolved);
|
|
362
|
+
if (!stats.isDirectory()) {
|
|
363
|
+
throw new Error(`Path is not a directory: ${resolved}`);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import { spawnSync } from "child_process";
|
|
2
|
+
import path from "path";
|
|
3
|
+
export const WINDOWS_CMD_EXTENSIONS = new Set([".cmd", ".bat"]);
|
|
4
|
+
export const WINDOWS_POWERSHELL_EXTENSIONS = new Set([".ps1"]);
|
|
5
|
+
const VERSION_REGEX = /([0-9]+\.[0-9]+\.[0-9A-Za-z.-]+)/;
|
|
6
|
+
const WSL_UNC_PATH_REGEX = /^\\\\wsl(?:\.localhost|\$)\\([^\\/]+)(?:[\\/](.*))?$/i;
|
|
7
|
+
const WSL_PATH_ENV_KEYS = new Set(["OPENCODE_CONFIG_DIR", "NODE_EXTRA_CA_CERTS"]);
|
|
8
|
+
export function parseWslUncPath(input) {
|
|
9
|
+
const normalized = input.trim().replace(/\//g, "\\");
|
|
10
|
+
const match = normalized.match(WSL_UNC_PATH_REGEX);
|
|
11
|
+
if (!match) {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
const distro = match[1] ?? "";
|
|
15
|
+
const remainder = match[2] ?? "";
|
|
16
|
+
const segments = remainder.split(/\\+/).filter((segment) => segment.length > 0);
|
|
17
|
+
return {
|
|
18
|
+
distro,
|
|
19
|
+
linuxPath: segments.length > 0 ? `/${segments.join("/")}` : "/",
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
export function resolveWslWorkingDirectory(folder, distro) {
|
|
23
|
+
const wslFolder = parseWslUncPath(folder);
|
|
24
|
+
if (wslFolder) {
|
|
25
|
+
return wslFolder.distro.toLowerCase() === distro.toLowerCase() ? { kind: "linux", path: wslFolder.linuxPath } : null;
|
|
26
|
+
}
|
|
27
|
+
const windowsFolder = normalizeWindowsPath(folder);
|
|
28
|
+
return windowsFolder ? { kind: "windows", path: windowsFolder } : null;
|
|
29
|
+
}
|
|
30
|
+
export function buildWindowsSpawnSpec(binaryPath, args, options = {}) {
|
|
31
|
+
const wslPath = parseWslUncPath(binaryPath);
|
|
32
|
+
if (wslPath) {
|
|
33
|
+
return buildWslSpawnSpec(wslPath, args, options);
|
|
34
|
+
}
|
|
35
|
+
const extension = path.extname(binaryPath).toLowerCase();
|
|
36
|
+
if (WINDOWS_CMD_EXTENSIONS.has(extension)) {
|
|
37
|
+
const comspec = process.env.ComSpec || "cmd.exe";
|
|
38
|
+
// cmd.exe requires the full command as a single string.
|
|
39
|
+
// Using the ""<script> <args>"" pattern ensures paths with spaces are handled.
|
|
40
|
+
const commandLine = `""${binaryPath}" ${args.join(" ")}"`;
|
|
41
|
+
return {
|
|
42
|
+
command: comspec,
|
|
43
|
+
args: ["/d", "/s", "/c", commandLine],
|
|
44
|
+
options: { windowsVerbatimArguments: true },
|
|
45
|
+
cwd: options.cwd,
|
|
46
|
+
env: options.env,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
if (WINDOWS_POWERSHELL_EXTENSIONS.has(extension)) {
|
|
50
|
+
// powershell.exe ships with Windows. (pwsh may not.)
|
|
51
|
+
return {
|
|
52
|
+
command: "powershell.exe",
|
|
53
|
+
args: ["-NoProfile", "-ExecutionPolicy", "Bypass", "-File", binaryPath, ...args],
|
|
54
|
+
options: {},
|
|
55
|
+
cwd: options.cwd,
|
|
56
|
+
env: options.env,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
return {
|
|
60
|
+
command: binaryPath,
|
|
61
|
+
args,
|
|
62
|
+
options: {},
|
|
63
|
+
cwd: options.cwd,
|
|
64
|
+
env: options.env,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
export function buildSpawnSpec(binaryPath, args, options = {}) {
|
|
68
|
+
if (process.platform !== "win32") {
|
|
69
|
+
return {
|
|
70
|
+
command: binaryPath,
|
|
71
|
+
args,
|
|
72
|
+
options: {},
|
|
73
|
+
cwd: options.cwd,
|
|
74
|
+
env: options.env,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
return buildWindowsSpawnSpec(binaryPath, args, options);
|
|
78
|
+
}
|
|
79
|
+
export function buildWslSignalSpec(distro, linuxPid, signal) {
|
|
80
|
+
return {
|
|
81
|
+
command: "wsl.exe",
|
|
82
|
+
args: ["--distribution", distro, "--exec", "kill", signal === "SIGKILL" ? "-KILL" : "-TERM", String(linuxPid)],
|
|
83
|
+
options: {},
|
|
84
|
+
wsl: { distro },
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
export function probeBinaryVersion(binaryPath) {
|
|
88
|
+
if (!binaryPath) {
|
|
89
|
+
return { valid: false, error: "Missing binary path" };
|
|
90
|
+
}
|
|
91
|
+
try {
|
|
92
|
+
const spec = buildSpawnSpec(binaryPath, ["--version"]);
|
|
93
|
+
const result = spawnSync(spec.command, spec.args, {
|
|
94
|
+
encoding: "utf8",
|
|
95
|
+
cwd: spec.cwd,
|
|
96
|
+
env: spec.env,
|
|
97
|
+
windowsVerbatimArguments: Boolean(spec.options.windowsVerbatimArguments),
|
|
98
|
+
});
|
|
99
|
+
if (result.error) {
|
|
100
|
+
return { valid: false, error: result.error.message };
|
|
101
|
+
}
|
|
102
|
+
if (result.status !== 0) {
|
|
103
|
+
const stderr = result.stderr?.trim();
|
|
104
|
+
const stdout = result.stdout?.trim();
|
|
105
|
+
const combined = stderr || stdout;
|
|
106
|
+
const error = combined ? `Exited with code ${result.status}: ${combined}` : `Exited with code ${result.status}`;
|
|
107
|
+
return { valid: false, error };
|
|
108
|
+
}
|
|
109
|
+
const stdoutLines = String(result.stdout ?? "")
|
|
110
|
+
.split(/\r?\n/)
|
|
111
|
+
.map((line) => line.trim())
|
|
112
|
+
.filter((line) => line.length > 0);
|
|
113
|
+
const stderrLines = String(result.stderr ?? "")
|
|
114
|
+
.split(/\r?\n/)
|
|
115
|
+
.map((line) => line.trim())
|
|
116
|
+
.filter((line) => line.length > 0);
|
|
117
|
+
// Prefer stdout; fall back to stderr (some tools report version there).
|
|
118
|
+
const reported = stdoutLines[0] ?? stderrLines[0];
|
|
119
|
+
if (!reported) {
|
|
120
|
+
return { valid: true };
|
|
121
|
+
}
|
|
122
|
+
const versionMatch = reported.match(VERSION_REGEX);
|
|
123
|
+
const version = versionMatch?.[1];
|
|
124
|
+
return { valid: true, version, reported };
|
|
125
|
+
}
|
|
126
|
+
catch (error) {
|
|
127
|
+
return { valid: false, error: error instanceof Error ? error.message : String(error) };
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
function buildWslSpawnSpec(wslPath, args, options) {
|
|
131
|
+
const workingDirectory = options.cwd ? resolveWslWorkingDirectory(options.cwd, wslPath.distro) : undefined;
|
|
132
|
+
if (options.cwd && !workingDirectory) {
|
|
133
|
+
throw new Error(`Unable to translate workspace folder for WSL binary in distro "${wslPath.distro}": ${options.cwd}`);
|
|
134
|
+
}
|
|
135
|
+
const wslArgs = ["--distribution", wslPath.distro];
|
|
136
|
+
const shouldWrapWithShell = Boolean(options.wslPidMarker) || workingDirectory?.kind === "windows";
|
|
137
|
+
if (!shouldWrapWithShell && workingDirectory?.kind === "linux") {
|
|
138
|
+
wslArgs.push("--cd", workingDirectory.path);
|
|
139
|
+
}
|
|
140
|
+
if (shouldWrapWithShell) {
|
|
141
|
+
const launchScript = buildWslLaunchScript(workingDirectory ?? undefined, options.wslPidMarker);
|
|
142
|
+
wslArgs.push("--exec", "sh", "-lc", launchScript, "codenomad-wsl-launch");
|
|
143
|
+
if (workingDirectory) {
|
|
144
|
+
wslArgs.push(workingDirectory.path);
|
|
145
|
+
}
|
|
146
|
+
wslArgs.push(wslPath.linuxPath, ...args);
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
wslArgs.push("--exec", wslPath.linuxPath, ...args);
|
|
150
|
+
}
|
|
151
|
+
return {
|
|
152
|
+
command: "wsl.exe",
|
|
153
|
+
args: wslArgs,
|
|
154
|
+
options: {},
|
|
155
|
+
env: buildWslEnvironment(options.env, options.propagateEnvKeys),
|
|
156
|
+
wsl: { distro: wslPath.distro, pidMarker: options.wslPidMarker },
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
function buildWslLaunchScript(workingDirectory, pidMarker) {
|
|
160
|
+
const steps = [];
|
|
161
|
+
if (pidMarker) {
|
|
162
|
+
steps.push(`printf '%s%s\\n' '${pidMarker}' "$$"`);
|
|
163
|
+
}
|
|
164
|
+
if (workingDirectory?.kind === "linux") {
|
|
165
|
+
steps.push('cd "$1"');
|
|
166
|
+
steps.push("shift");
|
|
167
|
+
}
|
|
168
|
+
else if (workingDirectory?.kind === "windows") {
|
|
169
|
+
steps.push('cd "$(wslpath -au "$1")"');
|
|
170
|
+
steps.push("shift");
|
|
171
|
+
}
|
|
172
|
+
steps.push('exec "$@"');
|
|
173
|
+
return steps.join(" && ");
|
|
174
|
+
}
|
|
175
|
+
function normalizeWindowsPath(input) {
|
|
176
|
+
const normalized = path.win32.normalize(input.trim().replace(/\//g, "\\"));
|
|
177
|
+
if (!normalized) {
|
|
178
|
+
return null;
|
|
179
|
+
}
|
|
180
|
+
if (/^[A-Za-z]:/.test(normalized) || normalized.startsWith("\\\\")) {
|
|
181
|
+
return normalized;
|
|
182
|
+
}
|
|
183
|
+
return null;
|
|
184
|
+
}
|
|
185
|
+
function buildWslEnvironment(env, propagateEnvKeys) {
|
|
186
|
+
if (!env) {
|
|
187
|
+
return env;
|
|
188
|
+
}
|
|
189
|
+
const keysToPropagate = Array.from(new Set([
|
|
190
|
+
...(propagateEnvKeys ?? []).filter((key) => env[key] !== undefined),
|
|
191
|
+
...Array.from(WSL_PATH_ENV_KEYS).filter((key) => env[key] !== undefined),
|
|
192
|
+
]));
|
|
193
|
+
if (keysToPropagate.length === 0) {
|
|
194
|
+
return env;
|
|
195
|
+
}
|
|
196
|
+
const next = { ...env };
|
|
197
|
+
const entries = (next.WSLENV ?? "").split(":").filter((entry) => entry.length > 0);
|
|
198
|
+
const byName = new Map(entries.map((entry) => [entry.split("/")[0] ?? entry, entry]));
|
|
199
|
+
for (const key of keysToPropagate) {
|
|
200
|
+
const existingEntry = byName.get(key);
|
|
201
|
+
if (existingEntry) {
|
|
202
|
+
byName.set(key, ensureWslenvEntry(existingEntry, WSL_PATH_ENV_KEYS.has(key)));
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
byName.set(key, WSL_PATH_ENV_KEYS.has(key) ? `${key}/p` : key);
|
|
206
|
+
}
|
|
207
|
+
next.WSLENV = Array.from(byName.values()).join(":");
|
|
208
|
+
return next;
|
|
209
|
+
}
|
|
210
|
+
function ensureWslenvEntry(entry, requiresPathTranslation) {
|
|
211
|
+
if (!requiresPathTranslation) {
|
|
212
|
+
return entry;
|
|
213
|
+
}
|
|
214
|
+
const [name, rawFlags = ""] = entry.split("/");
|
|
215
|
+
if (rawFlags.includes("p")) {
|
|
216
|
+
return entry;
|
|
217
|
+
}
|
|
218
|
+
return rawFlags.length > 0 ? `${name}/${rawFlags}p` : `${name}/p`;
|
|
219
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { realpath } from "fs/promises";
|
|
2
|
+
import { listWorktrees, resolveRepoRoot } from "./git-worktrees";
|
|
3
|
+
const WORKTREE_CACHE_TTL_MS = 2000;
|
|
4
|
+
const worktreeCache = new Map();
|
|
5
|
+
async function normalizeDirectoryPath(directory) {
|
|
6
|
+
const trimmed = (directory ?? "").trim();
|
|
7
|
+
if (!trimmed)
|
|
8
|
+
return "";
|
|
9
|
+
try {
|
|
10
|
+
return await realpath(trimmed);
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
return trimmed;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
async function getCachedWorktrees(params) {
|
|
17
|
+
const cached = worktreeCache.get(params.workspaceId);
|
|
18
|
+
const now = Date.now();
|
|
19
|
+
if (cached && cached.expiresAt > now) {
|
|
20
|
+
return cached;
|
|
21
|
+
}
|
|
22
|
+
const { repoRoot } = await resolveRepoRoot(params.workspacePath, params.logger);
|
|
23
|
+
const worktrees = await listWorktrees({ repoRoot, workspaceFolder: params.workspacePath, logger: params.logger });
|
|
24
|
+
const entry = {
|
|
25
|
+
expiresAt: now + WORKTREE_CACHE_TTL_MS,
|
|
26
|
+
repoRoot,
|
|
27
|
+
worktrees: await Promise.all(worktrees.map(async (wt) => ({
|
|
28
|
+
slug: wt.slug,
|
|
29
|
+
directory: wt.directory,
|
|
30
|
+
normalizedDirectory: await normalizeDirectoryPath(wt.directory),
|
|
31
|
+
}))),
|
|
32
|
+
};
|
|
33
|
+
worktreeCache.set(params.workspaceId, entry);
|
|
34
|
+
return entry;
|
|
35
|
+
}
|
|
36
|
+
export async function resolveWorktreeDirectory(params) {
|
|
37
|
+
const cached = await getCachedWorktrees({
|
|
38
|
+
workspaceId: params.workspaceId,
|
|
39
|
+
workspacePath: params.workspacePath,
|
|
40
|
+
logger: params.logger,
|
|
41
|
+
});
|
|
42
|
+
const match = cached.worktrees.find((wt) => wt.slug === params.worktreeSlug);
|
|
43
|
+
if (match) {
|
|
44
|
+
return match.directory;
|
|
45
|
+
}
|
|
46
|
+
worktreeCache.delete(params.workspaceId);
|
|
47
|
+
const refreshed = await getCachedWorktrees({
|
|
48
|
+
workspaceId: params.workspaceId,
|
|
49
|
+
workspacePath: params.workspacePath,
|
|
50
|
+
logger: params.logger,
|
|
51
|
+
});
|
|
52
|
+
return refreshed.worktrees.find((wt) => wt.slug === params.worktreeSlug)?.directory ?? null;
|
|
53
|
+
}
|
|
54
|
+
export async function resolveWorktreeSlugForDirectory(params) {
|
|
55
|
+
const target = await normalizeDirectoryPath(params.directory ?? "");
|
|
56
|
+
if (!target)
|
|
57
|
+
return null;
|
|
58
|
+
const cached = await getCachedWorktrees({
|
|
59
|
+
workspaceId: params.workspaceId,
|
|
60
|
+
workspacePath: params.workspacePath,
|
|
61
|
+
logger: params.logger,
|
|
62
|
+
});
|
|
63
|
+
const match = cached.worktrees.find((wt) => wt.normalizedDirectory === target);
|
|
64
|
+
if (match) {
|
|
65
|
+
return match.slug;
|
|
66
|
+
}
|
|
67
|
+
worktreeCache.delete(params.workspaceId);
|
|
68
|
+
const refreshed = await getCachedWorktrees({
|
|
69
|
+
workspaceId: params.workspaceId,
|
|
70
|
+
workspacePath: params.workspacePath,
|
|
71
|
+
logger: params.logger,
|
|
72
|
+
});
|
|
73
|
+
return refreshed.worktrees.find((wt) => wt.normalizedDirectory === target)?.slug ?? null;
|
|
74
|
+
}
|