@vividcodeai/embeddedcowork 0.0.22 → 0.0.23
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/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/clients/connection-manager.js +93 -0
- package/dist/config/location.js +57 -0
- package/dist/config/schema.js +72 -0
- package/dist/events/bus.js +47 -0
- package/dist/filesystem/__tests__/search-cache.test.js +40 -0
- package/dist/filesystem/browser.js +292 -0
- package/dist/filesystem/search-cache.js +43 -0
- package/dist/filesystem/search.js +135 -0
- package/dist/index.js +496 -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 +35 -0
- package/dist/opencode-config/package.json +9 -0
- package/dist/opencode-config/plugin/embeddedcowork.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 +15 -0
- package/dist/opencode-downloader.js +295 -0
- package/dist/opencode-paths.js +167 -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/runtime-paths.js +67 -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 +998 -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 +149 -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/opencode-status.js +10 -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__/spawn.test.js +139 -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 +432 -0
- package/dist/workspaces/opencode-auth.js +16 -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 +3 -12
- package/public/apple-touch-icon-180x180.png +0 -0
- package/public/assets/ChangesTab-C4_zdV74.js +2 -0
- package/public/assets/DiffToolbar-BsUPigM5.js +1 -0
- package/public/assets/EmbeddedCowork-Icon-DSw5nKk7.png +0 -0
- package/public/assets/FilesTab-Drvo30nM.js +2 -0
- package/public/assets/GitChangesTab-BhV6qTfP.js +2 -0
- package/public/assets/SplitFilePanel-BBrs329I.js +1 -0
- package/public/assets/StatusTab-DfYWlTSX.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/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-CAZqmV2E.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/core-DhEqZVGG.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--UIZ5GEn.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/event-DjZVAIBO.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--0EL0_Tu.js +2 -0
- package/public/assets/index-BKMyzTSR.js +1 -0
- package/public/assets/index-BLouhjer.js +1 -0
- package/public/assets/index-CLSJ4cO9.js +1 -0
- package/public/assets/index-CgjnEdBS.js +1 -0
- package/public/assets/index-ChCPoe9m.js +1 -0
- package/public/assets/index-Co-dJ-Xs.js +1 -0
- package/public/assets/index-Ctcq8Rhl.css +1 -0
- package/public/assets/index-Da0V-sLI.js +1 -0
- package/public/assets/index-hOT6sqTO.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-CugGjKDZ.css +1 -0
- package/public/assets/loading-CvW03p4Z.js +2 -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-ByuKWHRz.js +53 -0
- package/public/assets/make-CHLpvVh8.js +1 -0
- package/public/assets/markdown-BBFC6uy4.js +58 -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/monaco-viewer-DnczYBfh.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/todo-BLpUqy61.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-B4Xz6FbC.js +60 -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-BrzM_sH6.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-B1i7zgLk.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 +44 -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
- package/bin/cli.js +0 -56
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { getWorktreeGitDiff, getWorktreeGitStatus } from "../../workspaces/git-status";
|
|
3
|
+
import { commitWorktreeChanges, isGitMutationError, stageWorktreePaths, unstageWorktreePaths } from "../../workspaces/git-mutations";
|
|
4
|
+
import { isGitAvailable, resolveRepoRoot } from "../../workspaces/git-worktrees";
|
|
5
|
+
import { resolveWorktreeDirectory } from "../../workspaces/worktree-directory";
|
|
6
|
+
const WorkspaceCreateSchema = z.object({
|
|
7
|
+
path: z.string(),
|
|
8
|
+
name: z.string().optional(),
|
|
9
|
+
});
|
|
10
|
+
const WorkspaceFilesQuerySchema = z.object({
|
|
11
|
+
path: z.string().optional(),
|
|
12
|
+
});
|
|
13
|
+
const WorkspaceFileContentQuerySchema = z.object({
|
|
14
|
+
path: z.string(),
|
|
15
|
+
});
|
|
16
|
+
const WorkspaceFileContentBodySchema = z.object({
|
|
17
|
+
contents: z.string(),
|
|
18
|
+
});
|
|
19
|
+
const WorktreeGitDiffQuerySchema = z.object({
|
|
20
|
+
path: z.string().trim().min(1, "Path is required"),
|
|
21
|
+
originalPath: z.string().trim().optional(),
|
|
22
|
+
scope: z.enum(["staged", "unstaged"]),
|
|
23
|
+
});
|
|
24
|
+
const WorktreeGitPathsBodySchema = z.object({
|
|
25
|
+
paths: z.array(z.string().trim().min(1, "Path is required")).min(1, "At least one path is required"),
|
|
26
|
+
});
|
|
27
|
+
const WorktreeGitCommitBodySchema = z.object({
|
|
28
|
+
message: z.string().trim().min(1, "Commit message is required"),
|
|
29
|
+
});
|
|
30
|
+
const WorkspaceFileSearchQuerySchema = z.object({
|
|
31
|
+
q: z.string().trim().min(1, "Query is required"),
|
|
32
|
+
limit: z.coerce.number().int().positive().max(200).optional(),
|
|
33
|
+
type: z.enum(["all", "file", "directory"]).optional(),
|
|
34
|
+
refresh: z
|
|
35
|
+
.string()
|
|
36
|
+
.optional()
|
|
37
|
+
.transform((value) => (value === undefined ? undefined : value === "true")),
|
|
38
|
+
});
|
|
39
|
+
export function registerWorkspaceRoutes(app, deps) {
|
|
40
|
+
app.get("/api/workspaces", async () => {
|
|
41
|
+
return deps.workspaceManager.list();
|
|
42
|
+
});
|
|
43
|
+
app.post("/api/workspaces", async (request, reply) => {
|
|
44
|
+
try {
|
|
45
|
+
const body = WorkspaceCreateSchema.parse(request.body ?? {});
|
|
46
|
+
const workspace = await deps.workspaceManager.create(body.path, body.name);
|
|
47
|
+
reply.code(201);
|
|
48
|
+
return workspace;
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
request.log.error({ err: error }, "Failed to create workspace");
|
|
52
|
+
const message = error instanceof Error ? error.message : "Failed to create workspace";
|
|
53
|
+
reply.code(400).type("text/plain").send(message);
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
app.get("/api/workspaces/:id", async (request, reply) => {
|
|
57
|
+
const workspace = deps.workspaceManager.get(request.params.id);
|
|
58
|
+
if (!workspace) {
|
|
59
|
+
reply.code(404);
|
|
60
|
+
return { error: "Workspace not found" };
|
|
61
|
+
}
|
|
62
|
+
return workspace;
|
|
63
|
+
});
|
|
64
|
+
app.delete("/api/workspaces/:id", async (request, reply) => {
|
|
65
|
+
await deps.workspaceManager.delete(request.params.id);
|
|
66
|
+
reply.code(204);
|
|
67
|
+
});
|
|
68
|
+
app.get("/api/workspaces/:id/files", async (request, reply) => {
|
|
69
|
+
try {
|
|
70
|
+
const query = WorkspaceFilesQuerySchema.parse(request.query ?? {});
|
|
71
|
+
return deps.workspaceManager.listFiles(request.params.id, query.path ?? ".");
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
return handleWorkspaceError(error, reply);
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
app.get("/api/workspaces/:id/files/search", async (request, reply) => {
|
|
78
|
+
try {
|
|
79
|
+
const query = WorkspaceFileSearchQuerySchema.parse(request.query ?? {});
|
|
80
|
+
return deps.workspaceManager.searchFiles(request.params.id, query.q, {
|
|
81
|
+
limit: query.limit,
|
|
82
|
+
type: query.type,
|
|
83
|
+
refresh: query.refresh,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
catch (error) {
|
|
87
|
+
return handleWorkspaceError(error, reply);
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
app.get("/api/workspaces/:id/files/content", async (request, reply) => {
|
|
91
|
+
try {
|
|
92
|
+
const query = WorkspaceFileContentQuerySchema.parse(request.query ?? {});
|
|
93
|
+
return deps.workspaceManager.readFile(request.params.id, query.path);
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
return handleWorkspaceError(error, reply);
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
app.put("/api/workspaces/:id/files/content", async (request, reply) => {
|
|
100
|
+
try {
|
|
101
|
+
const query = WorkspaceFileContentQuerySchema.parse(request.query ?? {});
|
|
102
|
+
const body = WorkspaceFileContentBodySchema.parse(request.body ?? {});
|
|
103
|
+
deps.workspaceManager.writeFile(request.params.id, query.path, body.contents);
|
|
104
|
+
reply.code(204);
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
return handleWorkspaceError(error, reply);
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
app.get("/api/workspaces/:id/worktrees/:slug/git-status", async (request, reply) => {
|
|
111
|
+
try {
|
|
112
|
+
const directory = await resolveGitWorktreeDirectory(deps.workspaceManager, request.params.id, request.params.slug, request.log, reply);
|
|
113
|
+
if (!directory)
|
|
114
|
+
return;
|
|
115
|
+
return await getWorktreeGitStatus({ workspaceFolder: directory, logger: request.log });
|
|
116
|
+
}
|
|
117
|
+
catch (error) {
|
|
118
|
+
return handleWorkspaceError(error, reply);
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
app.get("/api/workspaces/:id/worktrees/:slug/git-diff", async (request, reply) => {
|
|
122
|
+
try {
|
|
123
|
+
const query = WorktreeGitDiffQuerySchema.parse(request.query ?? {});
|
|
124
|
+
const directory = await resolveGitWorktreeDirectory(deps.workspaceManager, request.params.id, request.params.slug, request.log, reply);
|
|
125
|
+
if (!directory)
|
|
126
|
+
return;
|
|
127
|
+
return await getWorktreeGitDiff({
|
|
128
|
+
workspaceFolder: directory,
|
|
129
|
+
path: query.path,
|
|
130
|
+
originalPath: query.originalPath,
|
|
131
|
+
scope: query.scope,
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
catch (error) {
|
|
135
|
+
return handleWorkspaceError(error, reply);
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
app.post("/api/workspaces/:id/worktrees/:slug/git-stage", async (request, reply) => {
|
|
139
|
+
try {
|
|
140
|
+
const body = WorktreeGitPathsBodySchema.parse(request.body ?? {});
|
|
141
|
+
const directory = await resolveGitWorktreeDirectory(deps.workspaceManager, request.params.id, request.params.slug, request.log, reply);
|
|
142
|
+
if (!directory)
|
|
143
|
+
return;
|
|
144
|
+
await stageWorktreePaths({ workspaceFolder: directory, paths: body.paths });
|
|
145
|
+
return { ok: true };
|
|
146
|
+
}
|
|
147
|
+
catch (error) {
|
|
148
|
+
return handleWorkspaceError(error, reply);
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
app.post("/api/workspaces/:id/worktrees/:slug/git-unstage", async (request, reply) => {
|
|
152
|
+
try {
|
|
153
|
+
const body = WorktreeGitPathsBodySchema.parse(request.body ?? {});
|
|
154
|
+
const directory = await resolveGitWorktreeDirectory(deps.workspaceManager, request.params.id, request.params.slug, request.log, reply);
|
|
155
|
+
if (!directory)
|
|
156
|
+
return;
|
|
157
|
+
await unstageWorktreePaths({ workspaceFolder: directory, paths: body.paths });
|
|
158
|
+
return { ok: true };
|
|
159
|
+
}
|
|
160
|
+
catch (error) {
|
|
161
|
+
return handleWorkspaceError(error, reply);
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
app.post("/api/workspaces/:id/worktrees/:slug/git-commit", async (request, reply) => {
|
|
165
|
+
try {
|
|
166
|
+
const body = WorktreeGitCommitBodySchema.parse(request.body ?? {});
|
|
167
|
+
const directory = await resolveGitWorktreeDirectory(deps.workspaceManager, request.params.id, request.params.slug, request.log, reply);
|
|
168
|
+
if (!directory)
|
|
169
|
+
return;
|
|
170
|
+
const result = await commitWorktreeChanges({ workspaceFolder: directory, message: body.message });
|
|
171
|
+
return { ok: true, ...result };
|
|
172
|
+
}
|
|
173
|
+
catch (error) {
|
|
174
|
+
return handleWorkspaceError(error, reply);
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
async function resolveGitWorktreeDirectory(workspaceManager, workspaceId, worktreeSlug, logger, reply) {
|
|
179
|
+
const workspace = workspaceManager.get(workspaceId);
|
|
180
|
+
if (!workspace) {
|
|
181
|
+
reply.code(404);
|
|
182
|
+
reply.send({ error: "Workspace not found" });
|
|
183
|
+
return null;
|
|
184
|
+
}
|
|
185
|
+
const gitAvailable = await isGitAvailable(workspace.path);
|
|
186
|
+
if (!gitAvailable) {
|
|
187
|
+
reply.code(503);
|
|
188
|
+
reply.send({ error: "Git is not installed or not available in PATH" });
|
|
189
|
+
return null;
|
|
190
|
+
}
|
|
191
|
+
const { isGitRepo } = await resolveRepoRoot(workspace.path, logger);
|
|
192
|
+
if (!isGitRepo) {
|
|
193
|
+
reply.code(400);
|
|
194
|
+
reply.send({ error: "Workspace is not a Git repository" });
|
|
195
|
+
return null;
|
|
196
|
+
}
|
|
197
|
+
const directory = await resolveWorktreeDirectory({
|
|
198
|
+
workspaceId: workspace.id,
|
|
199
|
+
workspacePath: workspace.path,
|
|
200
|
+
worktreeSlug,
|
|
201
|
+
logger,
|
|
202
|
+
});
|
|
203
|
+
if (!directory) {
|
|
204
|
+
reply.code(404);
|
|
205
|
+
reply.send({ error: "Worktree not found" });
|
|
206
|
+
return null;
|
|
207
|
+
}
|
|
208
|
+
return directory;
|
|
209
|
+
}
|
|
210
|
+
function handleWorkspaceError(error, reply) {
|
|
211
|
+
if (isGitMutationError(error)) {
|
|
212
|
+
reply.code(error.statusCode);
|
|
213
|
+
return { error: error.message };
|
|
214
|
+
}
|
|
215
|
+
if (error instanceof Error && error.message === "Workspace not found") {
|
|
216
|
+
reply.code(404);
|
|
217
|
+
return { error: "Workspace not found" };
|
|
218
|
+
}
|
|
219
|
+
reply.code(400);
|
|
220
|
+
return { error: error instanceof Error ? error.message : "Unable to fulfill request" };
|
|
221
|
+
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { resolveRepoRoot, listWorktrees, isValidWorktreeSlug, createManagedWorktree, removeWorktree, } from "../../workspaces/git-worktrees";
|
|
3
|
+
import { ensureCodenomadGitExclude, readWorktreeMap, writeWorktreeMap } from "../../workspaces/worktree-map";
|
|
4
|
+
const WorktreeMapSchema = z.object({
|
|
5
|
+
version: z.literal(1),
|
|
6
|
+
defaultWorktreeSlug: z.string().min(1).default("root"),
|
|
7
|
+
parentSessionWorktreeSlug: z.record(z.string(), z.string()).default({}),
|
|
8
|
+
});
|
|
9
|
+
const WorktreeCreateSchema = z.object({
|
|
10
|
+
slug: z.string().trim().min(1),
|
|
11
|
+
branch: z.string().trim().min(1).optional(),
|
|
12
|
+
});
|
|
13
|
+
export function registerWorktreeRoutes(app, deps) {
|
|
14
|
+
app.get("/api/workspaces/:id/worktrees", async (request, reply) => {
|
|
15
|
+
const workspace = deps.workspaceManager.get(request.params.id);
|
|
16
|
+
if (!workspace) {
|
|
17
|
+
reply.code(404);
|
|
18
|
+
return { error: "Workspace not found" };
|
|
19
|
+
}
|
|
20
|
+
const { repoRoot, isGitRepo } = await resolveRepoRoot(workspace.path, request.log);
|
|
21
|
+
const worktrees = await listWorktrees({ repoRoot, workspaceFolder: workspace.path, logger: request.log });
|
|
22
|
+
const response = { worktrees, isGitRepo };
|
|
23
|
+
return response;
|
|
24
|
+
});
|
|
25
|
+
app.post("/api/workspaces/:id/worktrees", async (request, reply) => {
|
|
26
|
+
const workspace = deps.workspaceManager.get(request.params.id);
|
|
27
|
+
if (!workspace) {
|
|
28
|
+
reply.code(404);
|
|
29
|
+
return { error: "Workspace not found" };
|
|
30
|
+
}
|
|
31
|
+
try {
|
|
32
|
+
const body = WorktreeCreateSchema.parse(request.body ?? {});
|
|
33
|
+
const slug = body.slug;
|
|
34
|
+
if (!isValidWorktreeSlug(slug) || slug === "root") {
|
|
35
|
+
reply.code(400);
|
|
36
|
+
return { error: "Invalid worktree slug" };
|
|
37
|
+
}
|
|
38
|
+
if (body.branch) {
|
|
39
|
+
if (!isValidWorktreeSlug(body.branch) || body.branch === "root") {
|
|
40
|
+
reply.code(400);
|
|
41
|
+
return { error: "Invalid worktree branch" };
|
|
42
|
+
}
|
|
43
|
+
if (body.branch !== slug) {
|
|
44
|
+
reply.code(400);
|
|
45
|
+
return { error: "Branch must match slug" };
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
const { repoRoot, isGitRepo } = await resolveRepoRoot(workspace.path, request.log);
|
|
49
|
+
if (!isGitRepo) {
|
|
50
|
+
reply.code(400);
|
|
51
|
+
return { error: "Workspace is not a Git repository" };
|
|
52
|
+
}
|
|
53
|
+
await ensureCodenomadGitExclude(workspace.path, request.log).catch(() => undefined);
|
|
54
|
+
const created = await createManagedWorktree({
|
|
55
|
+
repoRoot,
|
|
56
|
+
workspaceFolder: workspace.path,
|
|
57
|
+
slug,
|
|
58
|
+
logger: request.log,
|
|
59
|
+
});
|
|
60
|
+
reply.code(201);
|
|
61
|
+
return created;
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
return handleError(error, reply);
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
app.delete("/api/workspaces/:id/worktrees/:slug", async (request, reply) => {
|
|
68
|
+
const workspace = deps.workspaceManager.get(request.params.id);
|
|
69
|
+
if (!workspace) {
|
|
70
|
+
reply.code(404);
|
|
71
|
+
return { error: "Workspace not found" };
|
|
72
|
+
}
|
|
73
|
+
const slug = (request.params.slug ?? "").trim();
|
|
74
|
+
if (!isValidWorktreeSlug(slug) || slug === "root") {
|
|
75
|
+
reply.code(400);
|
|
76
|
+
return { error: "Invalid worktree slug" };
|
|
77
|
+
}
|
|
78
|
+
const { repoRoot, isGitRepo } = await resolveRepoRoot(workspace.path, request.log);
|
|
79
|
+
if (!isGitRepo) {
|
|
80
|
+
reply.code(400);
|
|
81
|
+
return { error: "Workspace is not a Git repository" };
|
|
82
|
+
}
|
|
83
|
+
const force = (request.query?.force ?? "").toString().toLowerCase() === "true";
|
|
84
|
+
try {
|
|
85
|
+
const worktrees = await listWorktrees({ repoRoot, workspaceFolder: workspace.path, logger: request.log });
|
|
86
|
+
const match = worktrees.find((wt) => wt.slug === slug);
|
|
87
|
+
if (!match || match.kind === "root") {
|
|
88
|
+
reply.code(404);
|
|
89
|
+
return { error: "Worktree not found" };
|
|
90
|
+
}
|
|
91
|
+
await removeWorktree({ workspaceFolder: workspace.path, directory: match.directory, force, logger: request.log });
|
|
92
|
+
// Best-effort: prune any mappings that point at the deleted worktree.
|
|
93
|
+
const current = await readWorktreeMap(workspace.path, request.log);
|
|
94
|
+
let changed = false;
|
|
95
|
+
const nextMapping = { ...(current.parentSessionWorktreeSlug ?? {}) };
|
|
96
|
+
for (const [sessionId, mapped] of Object.entries(nextMapping)) {
|
|
97
|
+
if (mapped === slug) {
|
|
98
|
+
delete nextMapping[sessionId];
|
|
99
|
+
changed = true;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
const nextDefault = current.defaultWorktreeSlug === slug ? "root" : current.defaultWorktreeSlug;
|
|
103
|
+
if (nextDefault !== current.defaultWorktreeSlug) {
|
|
104
|
+
changed = true;
|
|
105
|
+
}
|
|
106
|
+
if (changed) {
|
|
107
|
+
await writeWorktreeMap(workspace.path, {
|
|
108
|
+
version: 1,
|
|
109
|
+
defaultWorktreeSlug: nextDefault,
|
|
110
|
+
parentSessionWorktreeSlug: nextMapping,
|
|
111
|
+
}, request.log);
|
|
112
|
+
}
|
|
113
|
+
reply.code(204);
|
|
114
|
+
}
|
|
115
|
+
catch (error) {
|
|
116
|
+
return handleError(error, reply);
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
app.get("/api/workspaces/:id/worktrees/map", async (request, reply) => {
|
|
120
|
+
const workspace = deps.workspaceManager.get(request.params.id);
|
|
121
|
+
if (!workspace) {
|
|
122
|
+
reply.code(404);
|
|
123
|
+
return { error: "Workspace not found" };
|
|
124
|
+
}
|
|
125
|
+
return await readWorktreeMap(workspace.path, request.log);
|
|
126
|
+
});
|
|
127
|
+
app.put("/api/workspaces/:id/worktrees/map", async (request, reply) => {
|
|
128
|
+
const workspace = deps.workspaceManager.get(request.params.id);
|
|
129
|
+
if (!workspace) {
|
|
130
|
+
reply.code(404);
|
|
131
|
+
return { error: "Workspace not found" };
|
|
132
|
+
}
|
|
133
|
+
try {
|
|
134
|
+
const parsed = WorktreeMapSchema.parse(request.body ?? {});
|
|
135
|
+
if (!isValidWorktreeSlug(parsed.defaultWorktreeSlug)) {
|
|
136
|
+
reply.code(400);
|
|
137
|
+
return { error: "Invalid defaultWorktreeSlug" };
|
|
138
|
+
}
|
|
139
|
+
for (const slug of Object.values(parsed.parentSessionWorktreeSlug ?? {})) {
|
|
140
|
+
if (!isValidWorktreeSlug(slug)) {
|
|
141
|
+
reply.code(400);
|
|
142
|
+
return { error: "Invalid worktree slug in mapping" };
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
await writeWorktreeMap(workspace.path, parsed, request.log);
|
|
146
|
+
reply.code(204);
|
|
147
|
+
}
|
|
148
|
+
catch (error) {
|
|
149
|
+
return handleError(error, reply);
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
function handleError(error, reply) {
|
|
154
|
+
reply.code(400);
|
|
155
|
+
return { error: error instanceof Error ? error.message : "Unable to fulfill request" };
|
|
156
|
+
}
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import crypto from "crypto";
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import { createRequire } from "module";
|
|
5
|
+
const require = createRequire(import.meta.url);
|
|
6
|
+
function loadForge() {
|
|
7
|
+
// node-forge is CJS in many installs; require keeps this compatible with our ESM output.
|
|
8
|
+
return require("node-forge");
|
|
9
|
+
}
|
|
10
|
+
const LEAF_VALIDITY_DAYS = 30;
|
|
11
|
+
const ROTATE_IF_EXPIRES_WITHIN_DAYS = 3;
|
|
12
|
+
const CA_VALIDITY_DAYS = 365;
|
|
13
|
+
export function resolveHttpsOptions(args) {
|
|
14
|
+
if (!args.enabled) {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
const hasProvided = Boolean(args.tlsKeyPath && args.tlsCertPath);
|
|
18
|
+
if (hasProvided) {
|
|
19
|
+
const key = fs.readFileSync(args.tlsKeyPath, "utf-8");
|
|
20
|
+
const cert = fs.readFileSync(args.tlsCertPath, "utf-8");
|
|
21
|
+
const ca = args.tlsCaPath ? fs.readFileSync(args.tlsCaPath, "utf-8") : undefined;
|
|
22
|
+
return {
|
|
23
|
+
httpsOptions: { key, cert, ca },
|
|
24
|
+
caCertPath: args.tlsCaPath,
|
|
25
|
+
mode: "provided",
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
return ensureGeneratedTls(args);
|
|
29
|
+
}
|
|
30
|
+
function ensureGeneratedTls(args) {
|
|
31
|
+
const tlsDir = path.join(args.configDir, "tls");
|
|
32
|
+
const caKeyPath = path.join(tlsDir, "ca-key.pem");
|
|
33
|
+
const caCertPath = path.join(tlsDir, "ca-cert.pem");
|
|
34
|
+
const keyPath = path.join(tlsDir, "server-key.pem");
|
|
35
|
+
const certPath = path.join(tlsDir, "server-cert.pem");
|
|
36
|
+
fs.mkdirSync(tlsDir, { recursive: true });
|
|
37
|
+
const shouldRotateLeaf = () => {
|
|
38
|
+
try {
|
|
39
|
+
if (!fs.existsSync(certPath))
|
|
40
|
+
return true;
|
|
41
|
+
const pem = fs.readFileSync(certPath, "utf-8");
|
|
42
|
+
const x509 = new crypto.X509Certificate(pem);
|
|
43
|
+
const validToMs = Date.parse(x509.validTo);
|
|
44
|
+
if (!Number.isFinite(validToMs))
|
|
45
|
+
return true;
|
|
46
|
+
const rotateAt = validToMs - ROTATE_IF_EXPIRES_WITHIN_DAYS * 24 * 60 * 60 * 1000;
|
|
47
|
+
return Date.now() >= rotateAt;
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
const shouldRotateCa = () => {
|
|
54
|
+
try {
|
|
55
|
+
if (!fs.existsSync(caCertPath))
|
|
56
|
+
return true;
|
|
57
|
+
const pem = fs.readFileSync(caCertPath, "utf-8");
|
|
58
|
+
const x509 = new crypto.X509Certificate(pem);
|
|
59
|
+
const validToMs = Date.parse(x509.validTo);
|
|
60
|
+
if (!Number.isFinite(validToMs))
|
|
61
|
+
return true;
|
|
62
|
+
// CA rotates only when expired.
|
|
63
|
+
return Date.now() >= validToMs;
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
if (shouldRotateCa() || !fs.existsSync(caKeyPath)) {
|
|
70
|
+
const { caKeyPem, caCertPem } = generateCaCertificate();
|
|
71
|
+
writePemFile(caKeyPath, caKeyPem, 0o600);
|
|
72
|
+
writePemFile(caCertPath, caCertPem, 0o644);
|
|
73
|
+
args.logger.info({ caCertPath }, "Generated self-signed EmbeddedCowork CA certificate");
|
|
74
|
+
}
|
|
75
|
+
if (shouldRotateLeaf() || !fs.existsSync(keyPath)) {
|
|
76
|
+
const caKeyPem = fs.readFileSync(caKeyPath, "utf-8");
|
|
77
|
+
const caCertPem = fs.readFileSync(caCertPath, "utf-8");
|
|
78
|
+
const { keyPem, certPem } = generateServerCertificate({
|
|
79
|
+
host: args.host,
|
|
80
|
+
tlsSANs: args.tlsSANs,
|
|
81
|
+
caKeyPem,
|
|
82
|
+
caCertPem,
|
|
83
|
+
});
|
|
84
|
+
writePemFile(keyPath, keyPem, 0o600);
|
|
85
|
+
writePemFile(certPath, certPem, 0o644);
|
|
86
|
+
args.logger.info({ certPath }, "Generated EmbeddedCowork HTTPS certificate");
|
|
87
|
+
}
|
|
88
|
+
const key = fs.readFileSync(keyPath, "utf-8");
|
|
89
|
+
const cert = fs.readFileSync(certPath, "utf-8");
|
|
90
|
+
const ca = fs.readFileSync(caCertPath, "utf-8");
|
|
91
|
+
// Present the CA as part of the chain.
|
|
92
|
+
const chainedCert = `${cert.trim()}\n${ca.trim()}\n`;
|
|
93
|
+
return {
|
|
94
|
+
httpsOptions: {
|
|
95
|
+
key,
|
|
96
|
+
cert: chainedCert,
|
|
97
|
+
},
|
|
98
|
+
caCertPath,
|
|
99
|
+
mode: "generated",
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
function writePemFile(filePath, content, mode) {
|
|
103
|
+
fs.writeFileSync(filePath, content, { encoding: "utf-8", mode });
|
|
104
|
+
try {
|
|
105
|
+
fs.chmodSync(filePath, mode);
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
// best effort on platforms that ignore chmod
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
function generateCaCertificate() {
|
|
112
|
+
const forge = loadForge();
|
|
113
|
+
const keys = forge.pki.rsa.generateKeyPair(2048);
|
|
114
|
+
const cert = forge.pki.createCertificate();
|
|
115
|
+
cert.publicKey = keys.publicKey;
|
|
116
|
+
cert.serialNumber = crypto.randomBytes(16).toString("hex");
|
|
117
|
+
const now = new Date();
|
|
118
|
+
const notBefore = new Date(now.getTime() - 60000);
|
|
119
|
+
const notAfter = new Date(now.getTime() + CA_VALIDITY_DAYS * 24 * 60 * 60 * 1000);
|
|
120
|
+
cert.validity.notBefore = notBefore;
|
|
121
|
+
cert.validity.notAfter = notAfter;
|
|
122
|
+
const attrs = [{ name: "commonName", value: "EmbeddedCowork Local CA" }];
|
|
123
|
+
cert.setSubject(attrs);
|
|
124
|
+
cert.setIssuer(attrs);
|
|
125
|
+
cert.setExtensions([
|
|
126
|
+
{ name: "basicConstraints", cA: true },
|
|
127
|
+
{ name: "keyUsage", keyCertSign: true, cRLSign: true, digitalSignature: true },
|
|
128
|
+
{ name: "subjectKeyIdentifier" },
|
|
129
|
+
]);
|
|
130
|
+
cert.sign(keys.privateKey, forge.md.sha256.create());
|
|
131
|
+
return {
|
|
132
|
+
caKeyPem: forge.pki.privateKeyToPem(keys.privateKey),
|
|
133
|
+
caCertPem: forge.pki.certificateToPem(cert),
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
function generateServerCertificate(args) {
|
|
137
|
+
const forge = loadForge();
|
|
138
|
+
const caKey = forge.pki.privateKeyFromPem(args.caKeyPem);
|
|
139
|
+
const caCert = forge.pki.certificateFromPem(args.caCertPem);
|
|
140
|
+
const keys = forge.pki.rsa.generateKeyPair(2048);
|
|
141
|
+
const cert = forge.pki.createCertificate();
|
|
142
|
+
cert.publicKey = keys.publicKey;
|
|
143
|
+
cert.serialNumber = crypto.randomBytes(16).toString("hex");
|
|
144
|
+
const now = new Date();
|
|
145
|
+
const notBefore = new Date(now.getTime() - 60000);
|
|
146
|
+
const notAfter = new Date(now.getTime() + LEAF_VALIDITY_DAYS * 24 * 60 * 60 * 1000);
|
|
147
|
+
cert.validity.notBefore = notBefore;
|
|
148
|
+
cert.validity.notAfter = notAfter;
|
|
149
|
+
const commonName = pickCommonName(args.host);
|
|
150
|
+
cert.setSubject([{ name: "commonName", value: commonName }]);
|
|
151
|
+
cert.setIssuer(caCert.subject.attributes);
|
|
152
|
+
const san = buildSubjectAltNames(args.host, args.tlsSANs);
|
|
153
|
+
cert.setExtensions([
|
|
154
|
+
{ name: "basicConstraints", cA: false },
|
|
155
|
+
{ name: "keyUsage", digitalSignature: true, keyEncipherment: true },
|
|
156
|
+
{ name: "extKeyUsage", serverAuth: true },
|
|
157
|
+
{ name: "subjectAltName", altNames: san },
|
|
158
|
+
{ name: "subjectKeyIdentifier" },
|
|
159
|
+
]);
|
|
160
|
+
cert.sign(caKey, forge.md.sha256.create());
|
|
161
|
+
return {
|
|
162
|
+
keyPem: forge.pki.privateKeyToPem(keys.privateKey),
|
|
163
|
+
certPem: forge.pki.certificateToPem(cert),
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
function pickCommonName(host) {
|
|
167
|
+
if (!host || host === "0.0.0.0") {
|
|
168
|
+
return "localhost";
|
|
169
|
+
}
|
|
170
|
+
if (host === "127.0.0.1") {
|
|
171
|
+
return "localhost";
|
|
172
|
+
}
|
|
173
|
+
return host;
|
|
174
|
+
}
|
|
175
|
+
function buildSubjectAltNames(host, tlsSANs) {
|
|
176
|
+
const dns = new Set();
|
|
177
|
+
const ips = new Set();
|
|
178
|
+
dns.add("localhost");
|
|
179
|
+
ips.add("127.0.0.1");
|
|
180
|
+
if (host && host !== "0.0.0.0") {
|
|
181
|
+
if (isIPv4(host)) {
|
|
182
|
+
ips.add(host);
|
|
183
|
+
}
|
|
184
|
+
else {
|
|
185
|
+
dns.add(host);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
for (const token of splitList(tlsSANs)) {
|
|
189
|
+
if (isIPv4(token)) {
|
|
190
|
+
ips.add(token);
|
|
191
|
+
}
|
|
192
|
+
else if (token) {
|
|
193
|
+
dns.add(token);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
const altNames = [];
|
|
197
|
+
// 2 = DNS, 7 = IP
|
|
198
|
+
for (const name of Array.from(dns)) {
|
|
199
|
+
altNames.push({ type: 2, value: name });
|
|
200
|
+
}
|
|
201
|
+
for (const ip of Array.from(ips)) {
|
|
202
|
+
altNames.push({ type: 7, ip });
|
|
203
|
+
}
|
|
204
|
+
return altNames;
|
|
205
|
+
}
|
|
206
|
+
function splitList(input) {
|
|
207
|
+
if (!input)
|
|
208
|
+
return [];
|
|
209
|
+
return input
|
|
210
|
+
.split(",")
|
|
211
|
+
.map((part) => part.trim())
|
|
212
|
+
.filter(Boolean);
|
|
213
|
+
}
|
|
214
|
+
function isIPv4(value) {
|
|
215
|
+
const parts = value.split(".");
|
|
216
|
+
if (parts.length !== 4)
|
|
217
|
+
return false;
|
|
218
|
+
return parts.every((part) => {
|
|
219
|
+
if (!/^[0-9]+$/.test(part))
|
|
220
|
+
return false;
|
|
221
|
+
const num = Number(part);
|
|
222
|
+
return Number.isInteger(num) && num >= 0 && num <= 255;
|
|
223
|
+
});
|
|
224
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
function prettyLabel(p) {
|
|
2
|
+
const parts = p.split(/[\\/]/);
|
|
3
|
+
const last = parts[parts.length - 1] || p;
|
|
4
|
+
return last || p;
|
|
5
|
+
}
|
|
6
|
+
function readUiBinaries(settings) {
|
|
7
|
+
const ui = settings.getOwner("state", "ui");
|
|
8
|
+
const list = ui?.opencodeBinaries;
|
|
9
|
+
if (!Array.isArray(list))
|
|
10
|
+
return [];
|
|
11
|
+
return list.filter((item) => item && typeof item === "object" && typeof item.path === "string");
|
|
12
|
+
}
|
|
13
|
+
function readDefaultBinaryPath(settings) {
|
|
14
|
+
const server = settings.getOwner("config", "server");
|
|
15
|
+
const value = server?.opencodeBinary;
|
|
16
|
+
return typeof value === "string" && value.trim().length > 0 ? value.trim() : undefined;
|
|
17
|
+
}
|
|
18
|
+
export class BinaryResolver {
|
|
19
|
+
constructor(settings) {
|
|
20
|
+
this.settings = settings;
|
|
21
|
+
}
|
|
22
|
+
list() {
|
|
23
|
+
return readUiBinaries(this.settings);
|
|
24
|
+
}
|
|
25
|
+
resolveDefault() {
|
|
26
|
+
const binaries = this.list();
|
|
27
|
+
const configuredDefault = readDefaultBinaryPath(this.settings);
|
|
28
|
+
const fallback = binaries[0]?.path;
|
|
29
|
+
const path = configuredDefault ?? fallback ?? "opencode";
|
|
30
|
+
const entry = binaries.find((b) => b.path === path);
|
|
31
|
+
return {
|
|
32
|
+
path,
|
|
33
|
+
label: entry?.label ?? prettyLabel(path),
|
|
34
|
+
version: entry?.version,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export function isPlainObject(value) {
|
|
2
|
+
if (!value || typeof value !== "object")
|
|
3
|
+
return false;
|
|
4
|
+
if (Array.isArray(value))
|
|
5
|
+
return false;
|
|
6
|
+
const proto = Object.getPrototypeOf(value);
|
|
7
|
+
return proto === Object.prototype || proto === null;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* RFC 7396-ish merge patch with explicit null deletes.
|
|
11
|
+
* - Objects merge recursively
|
|
12
|
+
* - Arrays/scalars replace
|
|
13
|
+
* - null deletes keys
|
|
14
|
+
*/
|
|
15
|
+
export function applyMergePatch(current, patch) {
|
|
16
|
+
if (!isPlainObject(patch)) {
|
|
17
|
+
return patch;
|
|
18
|
+
}
|
|
19
|
+
const base = isPlainObject(current) ? { ...current } : {};
|
|
20
|
+
for (const [key, value] of Object.entries(patch)) {
|
|
21
|
+
if (value === null) {
|
|
22
|
+
delete base[key];
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
const existing = base[key];
|
|
26
|
+
if (isPlainObject(value) && isPlainObject(existing)) {
|
|
27
|
+
base[key] = applyMergePatch(existing, value);
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
base[key] = value;
|
|
31
|
+
}
|
|
32
|
+
return base;
|
|
33
|
+
}
|