@vividcodeai/embeddedcowork 0.0.22 → 0.0.24
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/releases/rollback.js +54 -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-Uu8GvqIU.js +2 -0
- package/public/assets/DiffToolbar-BgdJy7ap.js +1 -0
- package/public/assets/EmbeddedCowork-Icon-DSw5nKk7.png +0 -0
- package/public/assets/FilesTab-BlKXggYW.js +2 -0
- package/public/assets/GitChangesTab-DRAyGtTx.js +2 -0
- package/public/assets/SplitFilePanel-DR88B4Vy.js +1 -0
- package/public/assets/StatusTab-BS7K80gj.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-BusrfhOv.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-BDYT5S-Q.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-DaSE45SW.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-BKMyzTSR.js +1 -0
- package/public/assets/index-BWhMWtCP.js +2 -0
- package/public/assets/index-Bsb7z85O.js +1 -0
- package/public/assets/index-By8QJmOa.js +1 -0
- package/public/assets/index-CLSJ4cO9.js +1 -0
- package/public/assets/index-DBPzV8JQ.js +1 -0
- package/public/assets/index-DhOdialG.js +1 -0
- package/public/assets/index-DlobuxMv.js +1 -0
- package/public/assets/index-QdhU2jY3.css +1 -0
- package/public/assets/index-WYKVZxn4.js +1 -0
- package/public/assets/index-gBWCTkMr.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-C4yc63Qi.js +2 -0
- package/public/assets/loading-CugGjKDZ.css +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-y4WbiJBN.js +53 -0
- package/public/assets/make-CHLpvVh8.js +1 -0
- package/public/assets/markdown-Cvjx9yec.js +1 -0
- package/public/assets/markdown-DmuP22dE.js +58 -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-OXO07Mos.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-_6sSKM36.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-ChcZ5m6V.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-BkweRxZy.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-DDrpiK4b.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,238 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
|
|
4
|
+
import { isPlainObject } from "./merge-patch";
|
|
5
|
+
function ensureTrailingNewline(content) {
|
|
6
|
+
if (!content)
|
|
7
|
+
return "\n";
|
|
8
|
+
return content.endsWith("\n") ? content : `${content}\n`;
|
|
9
|
+
}
|
|
10
|
+
function safeReadYaml(filePath, logger) {
|
|
11
|
+
try {
|
|
12
|
+
const content = fs.readFileSync(filePath, "utf-8");
|
|
13
|
+
return parseYaml(content);
|
|
14
|
+
}
|
|
15
|
+
catch (error) {
|
|
16
|
+
logger.warn({ err: error, filePath }, "Failed to read YAML file during migration");
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function safeReadJson(filePath, logger) {
|
|
21
|
+
try {
|
|
22
|
+
const content = fs.readFileSync(filePath, "utf-8");
|
|
23
|
+
return JSON.parse(content);
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
logger.warn({ err: error, filePath }, "Failed to read JSON file during migration");
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function writeYaml(filePath, doc, logger) {
|
|
31
|
+
try {
|
|
32
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
33
|
+
const yaml = stringifyYaml(doc);
|
|
34
|
+
fs.writeFileSync(filePath, ensureTrailingNewline(yaml), "utf-8");
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
logger.warn({ err: error, filePath }, "Failed to write YAML file during migration");
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
function pickBackupPath(filePath) {
|
|
41
|
+
const preferred = `${filePath}.bak`;
|
|
42
|
+
if (!fs.existsSync(preferred)) {
|
|
43
|
+
return preferred;
|
|
44
|
+
}
|
|
45
|
+
return `${filePath}.bak.${Date.now()}`;
|
|
46
|
+
}
|
|
47
|
+
function normalizeDoc(value) {
|
|
48
|
+
return isPlainObject(value) ? value : {};
|
|
49
|
+
}
|
|
50
|
+
function looksLikeNewOwnerDoc(value) {
|
|
51
|
+
const doc = normalizeDoc(value);
|
|
52
|
+
// Heuristic: owner-bucket docs have at least one of these roots.
|
|
53
|
+
return Boolean(doc.ui || doc.server || doc.app || doc.legacy);
|
|
54
|
+
}
|
|
55
|
+
function looksLikeLegacyConfig(value) {
|
|
56
|
+
const doc = normalizeDoc(value);
|
|
57
|
+
return Boolean(doc.preferences || doc.opencodeBinaries || doc.theme || doc.recentFolders);
|
|
58
|
+
}
|
|
59
|
+
function looksLikeLegacyState(value) {
|
|
60
|
+
const doc = normalizeDoc(value);
|
|
61
|
+
return Boolean(doc.recentFolders);
|
|
62
|
+
}
|
|
63
|
+
function omitKeys(source, keys) {
|
|
64
|
+
const out = {};
|
|
65
|
+
for (const [k, v] of Object.entries(source)) {
|
|
66
|
+
if (keys.has(k))
|
|
67
|
+
continue;
|
|
68
|
+
out[k] = v;
|
|
69
|
+
}
|
|
70
|
+
return out;
|
|
71
|
+
}
|
|
72
|
+
function mapLegacyToOwnerDocs(legacyConfig, legacyState) {
|
|
73
|
+
const cfg = normalizeDoc(legacyConfig);
|
|
74
|
+
const st = normalizeDoc(legacyState);
|
|
75
|
+
const outConfig = {};
|
|
76
|
+
const outState = {};
|
|
77
|
+
const uiConfig = {};
|
|
78
|
+
const uiSettings = {};
|
|
79
|
+
const serverConfig = {};
|
|
80
|
+
const uiState = {};
|
|
81
|
+
// theme -> config.ui.theme
|
|
82
|
+
if (typeof cfg.theme === "string") {
|
|
83
|
+
uiConfig.theme = cfg.theme;
|
|
84
|
+
}
|
|
85
|
+
const preferences = normalizeDoc(cfg.preferences);
|
|
86
|
+
if (Object.keys(preferences).length > 0) {
|
|
87
|
+
// Server-owned stable keys
|
|
88
|
+
const envVars = preferences.environmentVariables;
|
|
89
|
+
if (isPlainObject(envVars)) {
|
|
90
|
+
serverConfig.environmentVariables = envVars;
|
|
91
|
+
}
|
|
92
|
+
const listeningMode = preferences.listeningMode;
|
|
93
|
+
if (typeof listeningMode === "string") {
|
|
94
|
+
serverConfig.listeningMode = listeningMode;
|
|
95
|
+
}
|
|
96
|
+
const logLevel = preferences.logLevel;
|
|
97
|
+
if (typeof logLevel === "string") {
|
|
98
|
+
serverConfig.logLevel = logLevel;
|
|
99
|
+
}
|
|
100
|
+
const lastUsedBinary = preferences.lastUsedBinary;
|
|
101
|
+
if (typeof lastUsedBinary === "string") {
|
|
102
|
+
serverConfig.opencodeBinary = lastUsedBinary;
|
|
103
|
+
}
|
|
104
|
+
// UI-owned state keys (drop preferences)
|
|
105
|
+
const modelRecents = preferences.modelRecents;
|
|
106
|
+
const modelFavorites = preferences.modelFavorites;
|
|
107
|
+
const modelThinkingSelections = preferences.modelThinkingSelections;
|
|
108
|
+
const models = {};
|
|
109
|
+
if (Array.isArray(modelRecents)) {
|
|
110
|
+
models.recents = modelRecents;
|
|
111
|
+
}
|
|
112
|
+
if (Array.isArray(modelFavorites)) {
|
|
113
|
+
models.favorites = modelFavorites;
|
|
114
|
+
}
|
|
115
|
+
if (isPlainObject(modelThinkingSelections)) {
|
|
116
|
+
models.thinkingSelections = modelThinkingSelections;
|
|
117
|
+
}
|
|
118
|
+
if (Object.keys(models).length > 0) {
|
|
119
|
+
uiState.models = models;
|
|
120
|
+
}
|
|
121
|
+
// Remaining preferences are treated as stable UI settings.
|
|
122
|
+
const moved = new Set([
|
|
123
|
+
"environmentVariables",
|
|
124
|
+
"listeningMode",
|
|
125
|
+
"logLevel",
|
|
126
|
+
"lastUsedBinary",
|
|
127
|
+
"modelRecents",
|
|
128
|
+
"modelFavorites",
|
|
129
|
+
"modelThinkingSelections",
|
|
130
|
+
]);
|
|
131
|
+
Object.assign(uiSettings, omitKeys(preferences, moved));
|
|
132
|
+
}
|
|
133
|
+
// recentFolders lives in legacy state (yaml) or legacy config.json
|
|
134
|
+
const recentFolders = (st.recentFolders ?? cfg.recentFolders);
|
|
135
|
+
if (Array.isArray(recentFolders)) {
|
|
136
|
+
uiState.recentFolders = recentFolders;
|
|
137
|
+
}
|
|
138
|
+
// opencodeBinaries -> state.ui.opencodeBinaries
|
|
139
|
+
if (Array.isArray(cfg.opencodeBinaries)) {
|
|
140
|
+
uiState.opencodeBinaries = cfg.opencodeBinaries;
|
|
141
|
+
}
|
|
142
|
+
if (Object.keys(uiSettings).length > 0) {
|
|
143
|
+
uiConfig.settings = uiSettings;
|
|
144
|
+
}
|
|
145
|
+
if (Object.keys(uiConfig).length > 0) {
|
|
146
|
+
outConfig.ui = uiConfig;
|
|
147
|
+
}
|
|
148
|
+
if (Object.keys(serverConfig).length > 0) {
|
|
149
|
+
outConfig.server = serverConfig;
|
|
150
|
+
}
|
|
151
|
+
if (Object.keys(uiState).length > 0) {
|
|
152
|
+
outState.ui = uiState;
|
|
153
|
+
}
|
|
154
|
+
// Unknown top-level keys -> legacy.unknown
|
|
155
|
+
const knownConfigKeys = new Set(["preferences", "opencodeBinaries", "theme", "recentFolders"]);
|
|
156
|
+
const unknownConfig = omitKeys(cfg, knownConfigKeys);
|
|
157
|
+
if (Object.keys(unknownConfig).length > 0) {
|
|
158
|
+
outConfig.legacy = { unknown: unknownConfig };
|
|
159
|
+
}
|
|
160
|
+
const knownStateKeys = new Set(["recentFolders"]);
|
|
161
|
+
const unknownState = omitKeys(st, knownStateKeys);
|
|
162
|
+
if (Object.keys(unknownState).length > 0) {
|
|
163
|
+
outState.legacy = { unknown: unknownState };
|
|
164
|
+
}
|
|
165
|
+
return { config: outConfig, state: outState };
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Migrate older config/state layouts into owner-bucket YAML docs.
|
|
169
|
+
*
|
|
170
|
+
* Legacy inputs supported:
|
|
171
|
+
* - config.yaml with { preferences, opencodeBinaries, theme }
|
|
172
|
+
* - state.yaml with { recentFolders }
|
|
173
|
+
* - legacy config.json with full ConfigFile schema
|
|
174
|
+
*/
|
|
175
|
+
export function migrateSettingsLayout(location, logger) {
|
|
176
|
+
const configYamlPath = location.configYamlPath;
|
|
177
|
+
const stateYamlPath = location.stateYamlPath;
|
|
178
|
+
const legacyJsonPath = location.legacyJsonPath;
|
|
179
|
+
const configExists = fs.existsSync(configYamlPath);
|
|
180
|
+
const stateExists = fs.existsSync(stateYamlPath);
|
|
181
|
+
const configDoc = configExists ? safeReadYaml(configYamlPath, logger) : null;
|
|
182
|
+
const stateDoc = stateExists ? safeReadYaml(stateYamlPath, logger) : null;
|
|
183
|
+
const configIsNew = configExists && looksLikeNewOwnerDoc(configDoc) && !looksLikeLegacyConfig(configDoc);
|
|
184
|
+
const stateIsNew = stateExists && looksLikeNewOwnerDoc(stateDoc) && !looksLikeLegacyState(stateDoc);
|
|
185
|
+
if (configIsNew && stateIsNew) {
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
const legacyJsonExists = fs.existsSync(legacyJsonPath);
|
|
189
|
+
const hasLegacyYaml = (configExists && looksLikeLegacyConfig(configDoc)) || (stateExists && looksLikeLegacyState(stateDoc));
|
|
190
|
+
const shouldMigrateFromJson = !configExists && legacyJsonExists;
|
|
191
|
+
if (!hasLegacyYaml && !shouldMigrateFromJson) {
|
|
192
|
+
// Either fresh install or partially written docs; let stores create on first write.
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
const sourceConfig = shouldMigrateFromJson ? safeReadJson(legacyJsonPath, logger) : configDoc;
|
|
196
|
+
const sourceState = shouldMigrateFromJson ? sourceConfig : stateDoc;
|
|
197
|
+
const { config, state } = mapLegacyToOwnerDocs(sourceConfig, sourceState);
|
|
198
|
+
try {
|
|
199
|
+
fs.mkdirSync(location.baseDir, { recursive: true });
|
|
200
|
+
}
|
|
201
|
+
catch (error) {
|
|
202
|
+
logger.warn({ err: error, baseDir: location.baseDir }, "Failed to create base directory during migration");
|
|
203
|
+
}
|
|
204
|
+
// Backup legacy files before rewriting.
|
|
205
|
+
if (configExists) {
|
|
206
|
+
try {
|
|
207
|
+
const bak = pickBackupPath(configYamlPath);
|
|
208
|
+
fs.renameSync(configYamlPath, bak);
|
|
209
|
+
logger.info({ configYamlPath, bak }, "Backed up legacy config.yaml");
|
|
210
|
+
}
|
|
211
|
+
catch (error) {
|
|
212
|
+
logger.warn({ err: error, configYamlPath }, "Failed to backup legacy config.yaml");
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
if (stateExists) {
|
|
216
|
+
try {
|
|
217
|
+
const bak = pickBackupPath(stateYamlPath);
|
|
218
|
+
fs.renameSync(stateYamlPath, bak);
|
|
219
|
+
logger.info({ stateYamlPath, bak }, "Backed up legacy state.yaml");
|
|
220
|
+
}
|
|
221
|
+
catch (error) {
|
|
222
|
+
logger.warn({ err: error, stateYamlPath }, "Failed to backup legacy state.yaml");
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
if (shouldMigrateFromJson) {
|
|
226
|
+
try {
|
|
227
|
+
const bak = pickBackupPath(legacyJsonPath);
|
|
228
|
+
fs.renameSync(legacyJsonPath, bak);
|
|
229
|
+
logger.info({ legacyJsonPath, bak }, "Moved legacy config.json to backup");
|
|
230
|
+
}
|
|
231
|
+
catch (error) {
|
|
232
|
+
logger.warn({ err: error, legacyJsonPath }, "Failed to move legacy config.json to backup");
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
writeYaml(configYamlPath, config, logger);
|
|
236
|
+
writeYaml(stateYamlPath, state, logger);
|
|
237
|
+
logger.info({ configYamlPath, stateYamlPath }, "Migrated settings docs to owner-bucket layout");
|
|
238
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
function isPlainObject(value) {
|
|
2
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3
|
+
}
|
|
4
|
+
function sanitizeServerOwner(value) {
|
|
5
|
+
const next = { ...value };
|
|
6
|
+
const speech = isPlainObject(next.speech) ? { ...next.speech } : null;
|
|
7
|
+
if (!speech) {
|
|
8
|
+
return next;
|
|
9
|
+
}
|
|
10
|
+
const rawApiKey = typeof speech.apiKey === "string" ? speech.apiKey.trim() : "";
|
|
11
|
+
if (rawApiKey) {
|
|
12
|
+
delete speech.apiKey;
|
|
13
|
+
speech.hasApiKey = true;
|
|
14
|
+
}
|
|
15
|
+
else if (!("hasApiKey" in speech)) {
|
|
16
|
+
speech.hasApiKey = false;
|
|
17
|
+
}
|
|
18
|
+
next.speech = speech;
|
|
19
|
+
return next;
|
|
20
|
+
}
|
|
21
|
+
export function sanitizeConfigOwner(owner, value) {
|
|
22
|
+
if (owner !== "server") {
|
|
23
|
+
return value;
|
|
24
|
+
}
|
|
25
|
+
return sanitizeServerOwner(value);
|
|
26
|
+
}
|
|
27
|
+
export function sanitizeConfigDoc(value) {
|
|
28
|
+
const next = { ...value };
|
|
29
|
+
if (isPlainObject(next.server)) {
|
|
30
|
+
next.server = sanitizeServerOwner(next.server);
|
|
31
|
+
}
|
|
32
|
+
return next;
|
|
33
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { YamlDocStore } from "./yaml-doc-store";
|
|
3
|
+
import { migrateSettingsLayout } from "./migrate";
|
|
4
|
+
import { sanitizeConfigOwner } from "./public-config";
|
|
5
|
+
const CanonicalLogLevelSchema = z.preprocess((value) => (typeof value === "string" ? value.trim().toUpperCase() : value), z.enum(["DEBUG", "INFO", "WARN", "ERROR"]));
|
|
6
|
+
function isPlainObject(value) {
|
|
7
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
8
|
+
}
|
|
9
|
+
function isDeepEqual(a, b) {
|
|
10
|
+
if (a === b)
|
|
11
|
+
return true;
|
|
12
|
+
try {
|
|
13
|
+
return JSON.stringify(a) === JSON.stringify(b);
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function normalizeServerConfigOwner(value) {
|
|
20
|
+
if (!isPlainObject(value)) {
|
|
21
|
+
return {};
|
|
22
|
+
}
|
|
23
|
+
const next = { ...value };
|
|
24
|
+
const parsedLogLevel = CanonicalLogLevelSchema.safeParse(next.logLevel);
|
|
25
|
+
if (parsedLogLevel.success) {
|
|
26
|
+
next.logLevel = parsedLogLevel.data;
|
|
27
|
+
}
|
|
28
|
+
else if (next.logLevel !== undefined) {
|
|
29
|
+
next.logLevel = "DEBUG";
|
|
30
|
+
}
|
|
31
|
+
return next;
|
|
32
|
+
}
|
|
33
|
+
function normalizeConfigDoc(doc) {
|
|
34
|
+
if (!isPlainObject(doc)) {
|
|
35
|
+
return {};
|
|
36
|
+
}
|
|
37
|
+
if (!isPlainObject(doc.server)) {
|
|
38
|
+
return doc;
|
|
39
|
+
}
|
|
40
|
+
return {
|
|
41
|
+
...doc,
|
|
42
|
+
server: normalizeServerConfigOwner(doc.server),
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
export class SettingsService {
|
|
46
|
+
constructor(location, eventBus, logger) {
|
|
47
|
+
this.location = location;
|
|
48
|
+
this.eventBus = eventBus;
|
|
49
|
+
this.logger = logger;
|
|
50
|
+
migrateSettingsLayout(location, logger);
|
|
51
|
+
this.configStore = new YamlDocStore(location.configYamlPath, logger.child({ component: "settings-config" }));
|
|
52
|
+
this.stateStore = new YamlDocStore(location.stateYamlPath, logger.child({ component: "settings-state" }));
|
|
53
|
+
}
|
|
54
|
+
getDoc(kind) {
|
|
55
|
+
if (kind !== "config") {
|
|
56
|
+
return this.stateStore.get();
|
|
57
|
+
}
|
|
58
|
+
const current = this.configStore.get();
|
|
59
|
+
const normalized = normalizeConfigDoc(current);
|
|
60
|
+
if (!isDeepEqual(current, normalized)) {
|
|
61
|
+
this.configStore.replace(normalized);
|
|
62
|
+
}
|
|
63
|
+
return normalized;
|
|
64
|
+
}
|
|
65
|
+
mergePatchDoc(kind, patch) {
|
|
66
|
+
const updated = kind === "config"
|
|
67
|
+
? this.configStore.replace(normalizeConfigDoc(this.configStore.mergePatch(patch)))
|
|
68
|
+
: this.stateStore.mergePatch(patch);
|
|
69
|
+
this.publish(kind, "*");
|
|
70
|
+
return updated;
|
|
71
|
+
}
|
|
72
|
+
getOwner(kind, owner) {
|
|
73
|
+
if (kind !== "config") {
|
|
74
|
+
return this.stateStore.getOwner(owner);
|
|
75
|
+
}
|
|
76
|
+
return owner === "server"
|
|
77
|
+
? normalizeServerConfigOwner(this.getDoc("config").server)
|
|
78
|
+
: this.getDoc("config")[owner];
|
|
79
|
+
}
|
|
80
|
+
mergePatchOwner(kind, owner, patch) {
|
|
81
|
+
const updated = kind === "config"
|
|
82
|
+
? owner === "server"
|
|
83
|
+
? this.configStore.replaceOwner(owner, normalizeServerConfigOwner(this.configStore.mergePatchOwner(owner, patch)))
|
|
84
|
+
: this.configStore.mergePatchOwner(owner, patch)
|
|
85
|
+
: this.stateStore.mergePatchOwner(owner, patch);
|
|
86
|
+
this.publish(kind, owner, updated);
|
|
87
|
+
return updated;
|
|
88
|
+
}
|
|
89
|
+
publish(kind, owner, value) {
|
|
90
|
+
if (!this.eventBus)
|
|
91
|
+
return;
|
|
92
|
+
const type = kind === "config" ? "storage.configChanged" : "storage.stateChanged";
|
|
93
|
+
const nextValue = value ?? this.getOwner(kind, owner);
|
|
94
|
+
const payload = {
|
|
95
|
+
type,
|
|
96
|
+
owner,
|
|
97
|
+
value: kind === "config" ? sanitizeConfigOwner(owner, nextValue) : nextValue,
|
|
98
|
+
};
|
|
99
|
+
this.eventBus.publish(payload);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
|
|
4
|
+
import { applyMergePatch, isPlainObject } from "./merge-patch";
|
|
5
|
+
function ensureTrailingNewline(content) {
|
|
6
|
+
if (!content)
|
|
7
|
+
return "\n";
|
|
8
|
+
return content.endsWith("\n") ? content : `${content}\n`;
|
|
9
|
+
}
|
|
10
|
+
function normalizeDoc(input) {
|
|
11
|
+
if (!isPlainObject(input)) {
|
|
12
|
+
return {};
|
|
13
|
+
}
|
|
14
|
+
return input;
|
|
15
|
+
}
|
|
16
|
+
export class YamlDocStore {
|
|
17
|
+
constructor(filePath, logger) {
|
|
18
|
+
this.filePath = filePath;
|
|
19
|
+
this.logger = logger;
|
|
20
|
+
this.cache = {};
|
|
21
|
+
this.loaded = false;
|
|
22
|
+
}
|
|
23
|
+
load() {
|
|
24
|
+
if (this.loaded) {
|
|
25
|
+
return this.cache;
|
|
26
|
+
}
|
|
27
|
+
try {
|
|
28
|
+
if (!fs.existsSync(this.filePath)) {
|
|
29
|
+
this.cache = {};
|
|
30
|
+
this.loaded = true;
|
|
31
|
+
return this.cache;
|
|
32
|
+
}
|
|
33
|
+
const content = fs.readFileSync(this.filePath, "utf-8");
|
|
34
|
+
const parsed = parseYaml(content);
|
|
35
|
+
this.cache = normalizeDoc(parsed);
|
|
36
|
+
this.loaded = true;
|
|
37
|
+
return this.cache;
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
this.logger.warn({ err: error, filePath: this.filePath }, "Failed to read YAML doc; using empty object");
|
|
41
|
+
this.cache = {};
|
|
42
|
+
this.loaded = true;
|
|
43
|
+
return this.cache;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
get() {
|
|
47
|
+
return this.load();
|
|
48
|
+
}
|
|
49
|
+
replace(next) {
|
|
50
|
+
const normalized = normalizeDoc(next);
|
|
51
|
+
this.cache = normalized;
|
|
52
|
+
this.loaded = true;
|
|
53
|
+
this.persist();
|
|
54
|
+
return this.cache;
|
|
55
|
+
}
|
|
56
|
+
mergePatch(patch) {
|
|
57
|
+
if (!isPlainObject(patch)) {
|
|
58
|
+
throw new Error("Patch must be a JSON object");
|
|
59
|
+
}
|
|
60
|
+
const current = this.get();
|
|
61
|
+
const next = applyMergePatch(current, patch);
|
|
62
|
+
return this.replace(next);
|
|
63
|
+
}
|
|
64
|
+
getOwner(owner) {
|
|
65
|
+
const doc = this.get();
|
|
66
|
+
const value = doc?.[owner];
|
|
67
|
+
return normalizeDoc(value);
|
|
68
|
+
}
|
|
69
|
+
replaceOwner(owner, value) {
|
|
70
|
+
const doc = this.get();
|
|
71
|
+
const nextDoc = { ...doc, [owner]: normalizeDoc(value) };
|
|
72
|
+
this.replace(nextDoc);
|
|
73
|
+
return nextDoc[owner];
|
|
74
|
+
}
|
|
75
|
+
mergePatchOwner(owner, patch) {
|
|
76
|
+
if (!isPlainObject(patch)) {
|
|
77
|
+
throw new Error("Patch must be a JSON object");
|
|
78
|
+
}
|
|
79
|
+
const doc = this.get();
|
|
80
|
+
const currentOwner = normalizeDoc(doc?.[owner]);
|
|
81
|
+
const nextOwner = normalizeDoc(applyMergePatch(currentOwner, patch));
|
|
82
|
+
const nextDoc = { ...doc, [owner]: nextOwner };
|
|
83
|
+
this.replace(nextDoc);
|
|
84
|
+
return nextOwner;
|
|
85
|
+
}
|
|
86
|
+
persist() {
|
|
87
|
+
try {
|
|
88
|
+
fs.mkdirSync(path.dirname(this.filePath), { recursive: true });
|
|
89
|
+
const yaml = stringifyYaml(this.cache);
|
|
90
|
+
fs.writeFileSync(this.filePath, ensureTrailingNewline(yaml), "utf-8");
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
this.logger.warn({ err: error, filePath: this.filePath }, "Failed to persist YAML doc");
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import { connect } from "net";
|
|
2
|
+
export class SideCarManager {
|
|
3
|
+
constructor(options) {
|
|
4
|
+
this.options = options;
|
|
5
|
+
this.configs = new Map();
|
|
6
|
+
this.runtime = new Map();
|
|
7
|
+
for (const record of this.loadConfiguredSideCars()) {
|
|
8
|
+
this.configs.set(record.id, record);
|
|
9
|
+
this.runtime.set(record.id, { status: "stopped" });
|
|
10
|
+
}
|
|
11
|
+
queueMicrotask(() => {
|
|
12
|
+
for (const record of this.configs.values()) {
|
|
13
|
+
void this.refreshPortSideCar(record.id).catch((error) => {
|
|
14
|
+
this.options.logger.warn({ sidecarId: record.id, err: error }, "Failed to probe sidecar port");
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
async list() {
|
|
20
|
+
await this.refreshPortStatuses();
|
|
21
|
+
return Array.from(this.configs.values()).map((record) => this.toSideCar(record));
|
|
22
|
+
}
|
|
23
|
+
async get(id) {
|
|
24
|
+
if (!this.configs.has(id))
|
|
25
|
+
return undefined;
|
|
26
|
+
await this.refreshPortSideCar(id);
|
|
27
|
+
return this.toSideCar(this.requireConfig(id));
|
|
28
|
+
}
|
|
29
|
+
async create(input) {
|
|
30
|
+
const normalizedName = input.name.trim();
|
|
31
|
+
const id = this.buildSideCarId(normalizedName);
|
|
32
|
+
if (this.configs.has(id)) {
|
|
33
|
+
throw new Error(`SideCar '${id}' already exists`);
|
|
34
|
+
}
|
|
35
|
+
const now = new Date().toISOString();
|
|
36
|
+
const record = {
|
|
37
|
+
id,
|
|
38
|
+
kind: input.kind,
|
|
39
|
+
name: normalizedName,
|
|
40
|
+
port: input.port,
|
|
41
|
+
insecure: input.insecure,
|
|
42
|
+
prefixMode: input.prefixMode,
|
|
43
|
+
createdAt: now,
|
|
44
|
+
updatedAt: now,
|
|
45
|
+
};
|
|
46
|
+
this.configs.set(record.id, record);
|
|
47
|
+
this.runtime.set(record.id, { status: "stopped" });
|
|
48
|
+
this.persistConfigs();
|
|
49
|
+
await this.refreshPortSideCar(record.id);
|
|
50
|
+
return this.toSideCar(record);
|
|
51
|
+
}
|
|
52
|
+
async update(id, input) {
|
|
53
|
+
const record = this.requireConfig(id);
|
|
54
|
+
record.name = typeof input.name === "string" ? input.name.trim() : record.name;
|
|
55
|
+
record.port = typeof input.port === "number" ? input.port : record.port;
|
|
56
|
+
record.insecure = typeof input.insecure === "boolean" ? input.insecure : record.insecure;
|
|
57
|
+
record.prefixMode = typeof input.prefixMode === "string" ? input.prefixMode : record.prefixMode;
|
|
58
|
+
record.updatedAt = new Date().toISOString();
|
|
59
|
+
this.persistConfigs();
|
|
60
|
+
await this.refreshPortSideCar(id);
|
|
61
|
+
return this.toSideCar(record);
|
|
62
|
+
}
|
|
63
|
+
async delete(id) {
|
|
64
|
+
const record = this.configs.get(id);
|
|
65
|
+
if (!record)
|
|
66
|
+
return false;
|
|
67
|
+
this.configs.delete(id);
|
|
68
|
+
this.runtime.delete(id);
|
|
69
|
+
this.persistConfigs();
|
|
70
|
+
this.options.eventBus.publish({ type: "sidecar.removed", sidecarId: id });
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
async shutdown() {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
buildTargetOrigin(sidecar) {
|
|
77
|
+
const protocol = sidecar.insecure ? "http" : "https";
|
|
78
|
+
return `${protocol}://127.0.0.1:${sidecar.port}`;
|
|
79
|
+
}
|
|
80
|
+
buildProxyBasePath(id) {
|
|
81
|
+
return `/sidecars/${encodeURIComponent(id)}`;
|
|
82
|
+
}
|
|
83
|
+
buildTargetPath(id, incomingPath, search = "") {
|
|
84
|
+
const record = this.requireConfig(id);
|
|
85
|
+
const publicBase = this.buildProxyBasePath(id);
|
|
86
|
+
const normalizedPath = incomingPath || publicBase;
|
|
87
|
+
if (record.prefixMode === "preserve") {
|
|
88
|
+
return `${normalizedPath}${search}`;
|
|
89
|
+
}
|
|
90
|
+
let stripped = normalizedPath.startsWith(publicBase) ? normalizedPath.slice(publicBase.length) : normalizedPath;
|
|
91
|
+
if (!stripped || stripped === "/") {
|
|
92
|
+
stripped = "/";
|
|
93
|
+
}
|
|
94
|
+
else if (!stripped.startsWith("/")) {
|
|
95
|
+
stripped = `/${stripped}`;
|
|
96
|
+
}
|
|
97
|
+
return `${stripped}${search}`;
|
|
98
|
+
}
|
|
99
|
+
async refreshPortStatuses() {
|
|
100
|
+
await Promise.all(Array.from(this.configs.values()).map((record) => this.refreshPortSideCar(record.id)));
|
|
101
|
+
}
|
|
102
|
+
async refreshPortSideCar(id) {
|
|
103
|
+
const record = this.configs.get(id);
|
|
104
|
+
if (!record)
|
|
105
|
+
return;
|
|
106
|
+
const isAvailable = await this.isPortAvailable(record.port);
|
|
107
|
+
const current = this.runtime.get(id);
|
|
108
|
+
const nextStatus = isAvailable ? "running" : "stopped";
|
|
109
|
+
if (current?.status === nextStatus) {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
this.runtime.set(id, { status: nextStatus });
|
|
113
|
+
record.updatedAt = new Date().toISOString();
|
|
114
|
+
this.publish(id);
|
|
115
|
+
}
|
|
116
|
+
publish(id) {
|
|
117
|
+
const record = this.configs.get(id);
|
|
118
|
+
if (!record)
|
|
119
|
+
return;
|
|
120
|
+
this.options.eventBus.publish({ type: "sidecar.updated", sidecar: this.toSideCar(record) });
|
|
121
|
+
}
|
|
122
|
+
toSideCar(record) {
|
|
123
|
+
const runtime = this.runtime.get(record.id);
|
|
124
|
+
return {
|
|
125
|
+
id: record.id,
|
|
126
|
+
kind: record.kind,
|
|
127
|
+
name: record.name,
|
|
128
|
+
port: record.port,
|
|
129
|
+
insecure: record.insecure,
|
|
130
|
+
prefixMode: record.prefixMode,
|
|
131
|
+
status: runtime?.status ?? "stopped",
|
|
132
|
+
createdAt: record.createdAt,
|
|
133
|
+
updatedAt: record.updatedAt,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
requireConfig(id) {
|
|
137
|
+
const record = this.configs.get(id);
|
|
138
|
+
if (!record) {
|
|
139
|
+
throw new Error("SideCar not found");
|
|
140
|
+
}
|
|
141
|
+
return record;
|
|
142
|
+
}
|
|
143
|
+
persistConfigs() {
|
|
144
|
+
const sidecars = Array.from(this.configs.values()).map((record) => ({ ...record }));
|
|
145
|
+
this.options.settings.mergePatchOwner("config", "server", { sidecars });
|
|
146
|
+
}
|
|
147
|
+
loadConfiguredSideCars() {
|
|
148
|
+
const serverConfig = this.options.settings.getOwner("config", "server");
|
|
149
|
+
const list = Array.isArray(serverConfig?.sidecars) ? serverConfig.sidecars : [];
|
|
150
|
+
const records = [];
|
|
151
|
+
for (const item of list) {
|
|
152
|
+
if (!item || typeof item !== "object")
|
|
153
|
+
continue;
|
|
154
|
+
const record = item;
|
|
155
|
+
const kind = record.kind === "port" ? "port" : null;
|
|
156
|
+
const id = typeof record.id === "string" && record.id.trim() ? record.id.trim() : null;
|
|
157
|
+
const name = typeof record.name === "string" && record.name.trim() ? record.name.trim() : null;
|
|
158
|
+
const port = typeof record.port === "number" && Number.isInteger(record.port) ? record.port : null;
|
|
159
|
+
if (!kind || !id || !name || !port)
|
|
160
|
+
continue;
|
|
161
|
+
const insecure = record.insecure === true;
|
|
162
|
+
const prefixMode = record.prefixMode === "preserve" ? "preserve" : "strip";
|
|
163
|
+
const createdAt = typeof record.createdAt === "string" && record.createdAt ? record.createdAt : new Date().toISOString();
|
|
164
|
+
const updatedAt = typeof record.updatedAt === "string" && record.updatedAt ? record.updatedAt : createdAt;
|
|
165
|
+
records.push({ id, kind, name, port, insecure, prefixMode, createdAt, updatedAt });
|
|
166
|
+
}
|
|
167
|
+
return records;
|
|
168
|
+
}
|
|
169
|
+
isPortAvailable(port) {
|
|
170
|
+
return new Promise((resolve) => {
|
|
171
|
+
const socket = connect({ port, host: "127.0.0.1" }, () => {
|
|
172
|
+
socket.end();
|
|
173
|
+
resolve(true);
|
|
174
|
+
});
|
|
175
|
+
socket.once("error", () => {
|
|
176
|
+
socket.destroy();
|
|
177
|
+
resolve(false);
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
buildSideCarId(name) {
|
|
182
|
+
const normalized = name
|
|
183
|
+
.trim()
|
|
184
|
+
.toLowerCase()
|
|
185
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
186
|
+
.replace(/-{2,}/g, "-")
|
|
187
|
+
.replace(/^-|-$/g, "");
|
|
188
|
+
if (!normalized) {
|
|
189
|
+
throw new Error("SideCar name must include letters or numbers");
|
|
190
|
+
}
|
|
191
|
+
return normalized;
|
|
192
|
+
}
|
|
193
|
+
}
|