@dyyz1993/codenomad 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +173 -0
- package/dist/api-types.js +1 -0
- package/dist/auth/auth-store.js +134 -0
- package/dist/auth/http-auth.js +37 -0
- package/dist/auth/manager.js +140 -0
- package/dist/auth/password-hash.js +32 -0
- package/dist/auth/session-manager.js +17 -0
- package/dist/auth/token-manager.js +27 -0
- package/dist/background-processes/manager.js +576 -0
- package/dist/bin.js +24 -0
- package/dist/cli-upgrade.js +53 -0
- package/dist/cli-upgrade.test.js +31 -0
- package/dist/clients/connection-manager.js +93 -0
- package/dist/config/location.js +57 -0
- package/dist/config/schema.js +71 -0
- package/dist/events/bus.js +45 -0
- package/dist/filesystem/__tests__/browser.test.js +65 -0
- package/dist/filesystem/__tests__/search-cache.test.js +40 -0
- package/dist/filesystem/browser.js +306 -0
- package/dist/filesystem/search-cache.js +43 -0
- package/dist/filesystem/search.js +135 -0
- package/dist/index.js +477 -0
- package/dist/launcher.js +149 -0
- package/dist/loader.js +21 -0
- package/dist/logger.js +109 -0
- package/dist/opencode-config/README.md +32 -0
- package/dist/opencode-config/opencode.jsonc +3 -0
- package/dist/opencode-config/package-lock.json +380 -0
- package/dist/opencode-config/package.json +9 -0
- package/dist/opencode-config/plugin/codenomad.ts +62 -0
- package/dist/opencode-config/plugin/lib/background-process.ts +265 -0
- package/dist/opencode-config/plugin/lib/client.ts +133 -0
- package/dist/opencode-config/plugin/lib/request.ts +214 -0
- package/dist/opencode-config.js +26 -0
- package/dist/plugins/channel.js +40 -0
- package/dist/plugins/handlers.js +17 -0
- package/dist/plugins/voice-mode.js +78 -0
- package/dist/releases/dev-release-monitor.js +75 -0
- package/dist/releases/release-monitor.js +107 -0
- package/dist/server/__tests__/network-addresses.test.js +68 -0
- package/dist/server/__tests__/remote-proxy.test.js +204 -0
- package/dist/server/http-server.js +926 -0
- package/dist/server/network-addresses.js +114 -0
- package/dist/server/remote-proxy.js +466 -0
- package/dist/server/routes/auth-pages/login.html +135 -0
- package/dist/server/routes/auth-pages/token.html +93 -0
- package/dist/server/routes/auth.js +148 -0
- package/dist/server/routes/background-processes.js +78 -0
- package/dist/server/routes/events.js +66 -0
- package/dist/server/routes/filesystem.js +43 -0
- package/dist/server/routes/meta.js +44 -0
- package/dist/server/routes/plugin.js +70 -0
- package/dist/server/routes/remote-proxy.js +42 -0
- package/dist/server/routes/remote-servers.js +142 -0
- package/dist/server/routes/settings.js +69 -0
- package/dist/server/routes/sidecars.js +46 -0
- package/dist/server/routes/speech.js +63 -0
- package/dist/server/routes/storage.js +52 -0
- package/dist/server/routes/workspaces.js +221 -0
- package/dist/server/routes/worktrees.js +156 -0
- package/dist/server/tls.js +224 -0
- package/dist/settings/binaries.js +37 -0
- package/dist/settings/merge-patch.js +33 -0
- package/dist/settings/migrate.js +238 -0
- package/dist/settings/public-config.js +33 -0
- package/dist/settings/service.js +101 -0
- package/dist/settings/yaml-doc-store.js +96 -0
- package/dist/sidecars/manager.js +193 -0
- package/dist/speech/providers/openai-compatible.js +189 -0
- package/dist/speech/service.js +58 -0
- package/dist/storage/instance-store.js +56 -0
- package/dist/ui/__tests__/remote-ui.test.js +67 -0
- package/dist/ui/remote-ui.js +462 -0
- package/dist/workspaces/__tests__/git-worktrees.test.js +43 -0
- package/dist/workspaces/__tests__/spawn.test.js +140 -0
- package/dist/workspaces/git-mutations.js +98 -0
- package/dist/workspaces/git-status.js +323 -0
- package/dist/workspaces/git-worktrees.js +216 -0
- package/dist/workspaces/instance-events.js +180 -0
- package/dist/workspaces/manager.js +368 -0
- package/dist/workspaces/opencode-auth.js +34 -0
- package/dist/workspaces/opencode-auth.test.js +34 -0
- package/dist/workspaces/runtime.js +366 -0
- package/dist/workspaces/spawn.js +219 -0
- package/dist/workspaces/worktree-directory.js +74 -0
- package/dist/workspaces/worktree-map.js +116 -0
- package/package.json +50 -0
- package/public/apple-touch-icon-180x180.png +0 -0
- package/public/assets/ChangesTab-BRNmSjeC.js +2 -0
- package/public/assets/CodeNomad-Icon-bmTWNPXy.png +0 -0
- package/public/assets/DiffToolbar-DPPgfx42.js +1 -0
- package/public/assets/FilesTab-sEeGPOoz.js +2 -0
- package/public/assets/GitChangesTab-tJ-TCq0c.js +2 -0
- package/public/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
- package/public/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
- package/public/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
- package/public/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
- package/public/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
- package/public/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
- package/public/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
- package/public/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
- package/public/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
- package/public/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
- package/public/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
- package/public/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
- package/public/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
- package/public/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
- package/public/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
- package/public/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
- package/public/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
- package/public/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
- package/public/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
- package/public/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
- package/public/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
- package/public/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
- package/public/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
- package/public/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
- package/public/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
- package/public/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
- package/public/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
- package/public/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
- package/public/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
- package/public/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
- package/public/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
- package/public/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
- package/public/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
- package/public/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
- package/public/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
- package/public/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
- package/public/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
- package/public/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
- package/public/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
- package/public/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
- package/public/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
- package/public/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
- package/public/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
- package/public/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
- package/public/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
- package/public/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
- package/public/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
- package/public/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
- package/public/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
- package/public/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
- package/public/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
- package/public/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
- package/public/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
- package/public/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
- package/public/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
- package/public/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
- package/public/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
- package/public/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
- package/public/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
- package/public/assets/SplitFilePanel-CHqNLZvU.js +1 -0
- package/public/assets/StatusTab-DMd0ByCA.js +1 -0
- package/public/assets/abap-BdImnpbu.js +1 -0
- package/public/assets/actionscript-3-CfeIJUat.js +1 -0
- package/public/assets/ada-bCR0ucgS.js +1 -0
- package/public/assets/align-justify-BWapkGTe.js +1 -0
- package/public/assets/andromeeda-C-Jbm3Hp.js +1 -0
- package/public/assets/angular-html-CU67Zn6k.js +1 -0
- package/public/assets/angular-ts-BwZT4LLn.js +1 -0
- package/public/assets/apache-Pmp26Uib.js +1 -0
- package/public/assets/apex-DhZLUxFE.js +1 -0
- package/public/assets/apl-dKokRX4l.js +1 -0
- package/public/assets/applescript-Co6uUVPk.js +1 -0
- package/public/assets/ara-BRHolxvo.js +1 -0
- package/public/assets/asciidoc-Dv7Oe6Be.js +1 -0
- package/public/assets/asm-D_Q5rh1f.js +1 -0
- package/public/assets/astro-CbQHKStN.js +1 -0
- package/public/assets/aurora-x-D-2ljcwZ.js +1 -0
- package/public/assets/awk-DMzUqQB5.js +1 -0
- package/public/assets/ayu-dark-Cv9koXgw.js +1 -0
- package/public/assets/ballerina-BFfxhgS-.js +1 -0
- package/public/assets/bat-BkioyH1T.js +1 -0
- package/public/assets/beancount-k_qm7-4y.js +1 -0
- package/public/assets/berry-D08WgyRC.js +1 -0
- package/public/assets/bibtex-CHM0blh-.js +1 -0
- package/public/assets/bicep-Bmn6On1c.js +1 -0
- package/public/assets/blade-DVc8C-J4.js +1 -0
- package/public/assets/bsl-BO_Y6i37.js +1 -0
- package/public/assets/bundle-full-CXlKQh5B.js +13 -0
- package/public/assets/c-BIGW1oBm.js +1 -0
- package/public/assets/cadence-Bv_4Rxtq.js +1 -0
- package/public/assets/cairo-KRGpt6FW.js +1 -0
- package/public/assets/catppuccin-frappe-DFWUc33u.js +1 -0
- package/public/assets/catppuccin-latte-C9dUb6Cb.js +1 -0
- package/public/assets/catppuccin-macchiato-DQyhUUbL.js +1 -0
- package/public/assets/catppuccin-mocha-D87Tk5Gz.js +1 -0
- package/public/assets/clarity-D53aC0YG.js +1 -0
- package/public/assets/clojure-P80f7IUj.js +1 -0
- package/public/assets/cmake-D1j8_8rp.js +1 -0
- package/public/assets/cobol-nwyudZeR.js +1 -0
- package/public/assets/codeowners-Bp6g37R7.js +1 -0
- package/public/assets/codeql-DsOJ9woJ.js +1 -0
- package/public/assets/coffee-Ch7k5sss.js +1 -0
- package/public/assets/common-lisp-Cg-RD9OK.js +1 -0
- package/public/assets/coq-DkFqJrB1.js +1 -0
- package/public/assets/cpp-CofmeUqb.js +1 -0
- package/public/assets/crystal-tKQVLTB8.js +1 -0
- package/public/assets/csharp-CX12Zw3r.js +1 -0
- package/public/assets/css-DPfMkruS.js +1 -0
- package/public/assets/csv-fuZLfV_i.js +1 -0
- package/public/assets/cue-D82EKSYY.js +1 -0
- package/public/assets/cypher-COkxafJQ.js +1 -0
- package/public/assets/d-85-TOEBH.js +1 -0
- package/public/assets/dark-plus-eOWES_5F.js +1 -0
- package/public/assets/dart-CF10PKvl.js +1 -0
- package/public/assets/dax-CEL-wOlO.js +1 -0
- package/public/assets/desktop-BmXAJ9_W.js +1 -0
- package/public/assets/diff-D97Zzqfu.js +1 -0
- package/public/assets/diff-viewer-BFtW5J8P.js +1 -0
- package/public/assets/docker-BcOcwvcX.js +1 -0
- package/public/assets/dotenv-Da5cRb03.js +1 -0
- package/public/assets/dracula-BzJJZx-M.js +1 -0
- package/public/assets/dracula-soft-BXkSAIEj.js +1 -0
- package/public/assets/dream-maker-BtqSS_iP.js +1 -0
- package/public/assets/edge-BkV0erSs.js +1 -0
- package/public/assets/elixir-CDX3lj18.js +1 -0
- package/public/assets/elm-DbKCFpqz.js +1 -0
- package/public/assets/emacs-lisp-C9XAeP06.js +1 -0
- package/public/assets/erb-BOJIQeun.js +1 -0
- package/public/assets/erlang-DsQrWhSR.js +1 -0
- package/public/assets/everforest-dark-BgDCqdQA.js +1 -0
- package/public/assets/everforest-light-C8M2exoo.js +1 -0
- package/public/assets/fast-diff-vendor-DgdwVvTQ.js +1 -0
- package/public/assets/fennel-BYunw83y.js +1 -0
- package/public/assets/fish-BvzEVeQv.js +1 -0
- package/public/assets/fluent-C4IJs8-o.js +1 -0
- package/public/assets/fortran-fixed-form-BZjJHVRy.js +1 -0
- package/public/assets/fortran-free-form-D22FLkUw.js +1 -0
- package/public/assets/fsharp-CXgrBDvD.js +1 -0
- package/public/assets/gdresource-B7Tvp0Sc.js +1 -0
- package/public/assets/gdscript-DTMYz4Jt.js +1 -0
- package/public/assets/gdshader-DkwncUOv.js +1 -0
- package/public/assets/genie-D0YGMca9.js +1 -0
- package/public/assets/gherkin-DyxjwDmM.js +1 -0
- package/public/assets/git-commit-F4YmCXRG.js +1 -0
- package/public/assets/git-diff-vendor-CSgooKT_.js +52 -0
- package/public/assets/git-diff-vendor-HAZkIolJ.css +19 -0
- package/public/assets/git-rebase-r7XF79zn.js +1 -0
- package/public/assets/github-dark-DHJKELXO.js +1 -0
- package/public/assets/github-dark-default-Cuk6v7N8.js +1 -0
- package/public/assets/github-dark-dimmed-DH5Ifo-i.js +1 -0
- package/public/assets/github-dark-high-contrast-E3gJ1_iC.js +1 -0
- package/public/assets/github-light-DAi9KRSo.js +1 -0
- package/public/assets/github-light-default-D7oLnXFd.js +1 -0
- package/public/assets/github-light-high-contrast-BfjtVDDH.js +1 -0
- package/public/assets/gleam-BspZqrRM.js +1 -0
- package/public/assets/glimmer-js-Rg0-pVw9.js +1 -0
- package/public/assets/glimmer-ts-U6CK756n.js +1 -0
- package/public/assets/glsl-DplSGwfg.js +1 -0
- package/public/assets/gnuplot-DdkO51Og.js +1 -0
- package/public/assets/go-Dn2_MT6a.js +1 -0
- package/public/assets/graphql-ChdNCCLP.js +1 -0
- package/public/assets/groovy-gcz8RCvz.js +1 -0
- package/public/assets/gruvbox-dark-hard-CFHQjOhq.js +1 -0
- package/public/assets/gruvbox-dark-medium-GsRaNv29.js +1 -0
- package/public/assets/gruvbox-dark-soft-CVdnzihN.js +1 -0
- package/public/assets/gruvbox-light-hard-CH1njM8p.js +1 -0
- package/public/assets/gruvbox-light-medium-DRw_LuNl.js +1 -0
- package/public/assets/gruvbox-light-soft-hJgmCMqR.js +1 -0
- package/public/assets/hack-CaT9iCJl.js +1 -0
- package/public/assets/haml-B8DHNrY2.js +1 -0
- package/public/assets/handlebars-BL8al0AC.js +1 -0
- package/public/assets/haskell-Df6bDoY_.js +1 -0
- package/public/assets/haxe-CzTSHFRz.js +1 -0
- package/public/assets/hcl-BWvSN4gD.js +1 -0
- package/public/assets/highlight-vendor-8FKMu9os.js +10 -0
- package/public/assets/hjson-D5-asLiD.js +1 -0
- package/public/assets/hlsl-D3lLCCz7.js +1 -0
- package/public/assets/houston-DnULxvSX.js +1 -0
- package/public/assets/html-GMplVEZG.js +1 -0
- package/public/assets/html-derivative-BFtXZ54Q.js +1 -0
- package/public/assets/http-jrhK8wxY.js +1 -0
- package/public/assets/hurl-irOxFIW8.js +1 -0
- package/public/assets/hxml-Bvhsp5Yf.js +1 -0
- package/public/assets/hy-DFXneXwc.js +1 -0
- package/public/assets/imba-DGztddWO.js +1 -0
- package/public/assets/index-Bdy3MTIn.js +1 -0
- package/public/assets/index-BvRe9GiS.js +1 -0
- package/public/assets/index-Ctq8RqRf.js +1 -0
- package/public/assets/index-D-NvysdX.js +1 -0
- package/public/assets/index-D6RMBXUk.js +2 -0
- package/public/assets/index-DXI9DoVB.css +1 -0
- package/public/assets/index-DYWkPhKo.js +1 -0
- package/public/assets/index-D_FDiI6a.js +1 -0
- package/public/assets/index-Dty5bSHf.js +1 -0
- package/public/assets/index-H16e4Rqc.js +1 -0
- package/public/assets/ini-BEwlwnbL.js +1 -0
- package/public/assets/java-CylS5w8V.js +1 -0
- package/public/assets/javascript-wDzz0qaB.js +1 -0
- package/public/assets/jinja-4LBKfQ-Z.js +1 -0
- package/public/assets/jison-wvAkD_A8.js +1 -0
- package/public/assets/json-Cp-IABpG.js +1 -0
- package/public/assets/json5-C9tS-k6U.js +1 -0
- package/public/assets/jsonc-Des-eS-w.js +1 -0
- package/public/assets/jsonl-DcaNXYhu.js +1 -0
- package/public/assets/jsonnet-DFQXde-d.js +1 -0
- package/public/assets/jssm-C2t-YnRu.js +1 -0
- package/public/assets/jsx-g9-lgVsj.js +1 -0
- package/public/assets/julia-C8NyazO9.js +1 -0
- package/public/assets/kanagawa-dragon-CkXjmgJE.js +1 -0
- package/public/assets/kanagawa-lotus-CfQXZHmo.js +1 -0
- package/public/assets/kanagawa-wave-DWedfzmr.js +1 -0
- package/public/assets/kdl-DV7GczEv.js +1 -0
- package/public/assets/kotlin-BdnUsdx6.js +1 -0
- package/public/assets/kusto-BvAqAH-y.js +1 -0
- package/public/assets/laserwave-DUszq2jm.js +1 -0
- package/public/assets/latex-BUKiar2Z.js +1 -0
- package/public/assets/lean-DP1Csr6i.js +1 -0
- package/public/assets/less-B1dDrJ26.js +1 -0
- package/public/assets/light-plus-B7mTdjB0.js +1 -0
- package/public/assets/liquid-DYVedYrR.js +1 -0
- package/public/assets/llvm-BtvRca6l.js +1 -0
- package/public/assets/loading-CmEVQgyj.css +1 -0
- package/public/assets/loading-W2Y_wR0P.js +1 -0
- package/public/assets/log-2UxHyX5q.js +1 -0
- package/public/assets/logo-BtOb2qkB.js +1 -0
- package/public/assets/lua-BbnMAYS6.js +1 -0
- package/public/assets/luau-CXu1NL6O.js +1 -0
- package/public/assets/main-Bala0YJ4.js +57 -0
- package/public/assets/make-CHLpvVh8.js +1 -0
- package/public/assets/markdown-Cvjx9yec.js +1 -0
- package/public/assets/markdown-Db8cvZ4Z.js +315 -0
- package/public/assets/marko-CPi9NSCl.js +1 -0
- package/public/assets/material-theme-D5KoaKCx.js +1 -0
- package/public/assets/material-theme-darker-BfHTSMKl.js +1 -0
- package/public/assets/material-theme-lighter-B0m2ddpp.js +1 -0
- package/public/assets/material-theme-ocean-CyktbL80.js +1 -0
- package/public/assets/material-theme-palenight-Csfq5Kiy.js +1 -0
- package/public/assets/matlab-D7o27uSR.js +1 -0
- package/public/assets/mdc-DUICxH0z.js +1 -0
- package/public/assets/mdx-Cmh6b_Ma.js +1 -0
- package/public/assets/mermaid-DKYwYmdq.js +1 -0
- package/public/assets/min-dark-CafNBF8u.js +1 -0
- package/public/assets/min-light-CTRr51gU.js +1 -0
- package/public/assets/mipsasm-CKIfxQSi.js +1 -0
- package/public/assets/mojo-1DNp92w6.js +1 -0
- package/public/assets/monaco-viewer-DWQqXKm3.js +26 -0
- package/public/assets/monokai-D4h5O-jR.js +1 -0
- package/public/assets/move-Bu9oaDYs.js +1 -0
- package/public/assets/narrat-DRg8JJMk.js +1 -0
- package/public/assets/nextflow-CUEJCptM.js +1 -0
- package/public/assets/nginx-DknmC5AR.js +1 -0
- package/public/assets/night-owl-C39BiMTA.js +1 -0
- package/public/assets/nim-CVrawwO9.js +1 -0
- package/public/assets/nix-BbRYJGeE.js +1 -0
- package/public/assets/nord-Ddv68eIx.js +1 -0
- package/public/assets/nushell-C-sUppwS.js +1 -0
- package/public/assets/objective-c-DXmwc3jG.js +1 -0
- package/public/assets/objective-cpp-CLxacb5B.js +1 -0
- package/public/assets/ocaml-C0hk2d4L.js +1 -0
- package/public/assets/one-dark-pro-DVMEJ2y_.js +1 -0
- package/public/assets/one-light-PoHY5YXO.js +1 -0
- package/public/assets/pascal-D93ZcfNL.js +1 -0
- package/public/assets/perl-C0TMdlhV.js +1 -0
- package/public/assets/php-CDn_0X-4.js +1 -0
- package/public/assets/pkl-u5AG7uiY.js +1 -0
- package/public/assets/plastic-3e1v2bzS.js +1 -0
- package/public/assets/plsql-ChMvpjG-.js +1 -0
- package/public/assets/po-BTJTHyun.js +1 -0
- package/public/assets/poimandres-CS3Unz2-.js +1 -0
- package/public/assets/polar-C0HS_06l.js +1 -0
- package/public/assets/postcss-CXtECtnM.js +1 -0
- package/public/assets/powerquery-CEu0bR-o.js +1 -0
- package/public/assets/powershell-Dpen1YoG.js +1 -0
- package/public/assets/prisma-Dd19v3D-.js +1 -0
- package/public/assets/prolog-CbFg5uaA.js +1 -0
- package/public/assets/proto-DyJlTyXw.js +1 -0
- package/public/assets/pug-CGlum2m_.js +1 -0
- package/public/assets/puppet-BMWR74SV.js +1 -0
- package/public/assets/purescript-CklMAg4u.js +1 -0
- package/public/assets/python-B6aJPvgy.js +1 -0
- package/public/assets/qml-3beO22l8.js +1 -0
- package/public/assets/qmldir-C8lEn-DE.js +1 -0
- package/public/assets/qss-IeuSbFQv.js +1 -0
- package/public/assets/r-DiinP2Uv.js +1 -0
- package/public/assets/racket-BqYA7rlc.js +1 -0
- package/public/assets/raku-DXvB9xmW.js +1 -0
- package/public/assets/razor-WgofotgN.js +1 -0
- package/public/assets/red-bN70gL4F.js +1 -0
- package/public/assets/reg-C-SQnVFl.js +1 -0
- package/public/assets/regexp-CDVJQ6XC.js +1 -0
- package/public/assets/rel-C3B-1QV4.js +1 -0
- package/public/assets/riscv-BM1_JUlF.js +1 -0
- package/public/assets/rose-pine-BHrmToEH.js +1 -0
- package/public/assets/rose-pine-dawn-CnK8MTSM.js +1 -0
- package/public/assets/rose-pine-moon-NleAzG8P.js +1 -0
- package/public/assets/rosmsg-BJDFO7_C.js +1 -0
- package/public/assets/rst-B0xPkSld.js +1 -0
- package/public/assets/ruby-BvKwtOVI.js +1 -0
- package/public/assets/rust-B1yitclQ.js +1 -0
- package/public/assets/sas-cz2c8ADy.js +1 -0
- package/public/assets/sass-Cj5Yp3dK.js +1 -0
- package/public/assets/scala-C151Ov-r.js +1 -0
- package/public/assets/scheme-C98Dy4si.js +1 -0
- package/public/assets/scss-OYdSNvt2.js +1 -0
- package/public/assets/sdbl-DVxCFoDh.js +1 -0
- package/public/assets/shaderlab-Dg9Lc6iA.js +1 -0
- package/public/assets/shellscript-Yzrsuije.js +1 -0
- package/public/assets/shellsession-BADoaaVG.js +1 -0
- package/public/assets/slack-dark-BthQWCQV.js +1 -0
- package/public/assets/slack-ochin-DqwNpetd.js +1 -0
- package/public/assets/smalltalk-BERRCDM3.js +1 -0
- package/public/assets/snazzy-light-Bw305WKR.js +1 -0
- package/public/assets/solarized-dark-DXbdFlpD.js +1 -0
- package/public/assets/solarized-light-L9t79GZl.js +1 -0
- package/public/assets/solidity-BbcW6ACK.js +1 -0
- package/public/assets/soy-Brmx7dQM.js +1 -0
- package/public/assets/sparql-rVzFXLq3.js +1 -0
- package/public/assets/splunk-BtCnVYZw.js +1 -0
- package/public/assets/sql-BLtJtn59.js +1 -0
- package/public/assets/ssh-config-_ykCGR6B.js +1 -0
- package/public/assets/stata-BH5u7GGu.js +1 -0
- package/public/assets/stylus-BEDo0Tqx.js +1 -0
- package/public/assets/svelte-3Dk4HxPD.js +1 -0
- package/public/assets/swift-Dg5xB15N.js +1 -0
- package/public/assets/synthwave-84-CbfX1IO0.js +1 -0
- package/public/assets/system-verilog-CnnmHF94.js +1 -0
- package/public/assets/systemd-4A_iFExJ.js +1 -0
- package/public/assets/talonscript-CkByrt1z.js +1 -0
- package/public/assets/tasl-QIJgUcNo.js +1 -0
- package/public/assets/tcl-dwOrl1Do.js +1 -0
- package/public/assets/templ-W15q3VgB.js +1 -0
- package/public/assets/terraform-BETggiCN.js +1 -0
- package/public/assets/tex-Cppo0RY3.js +1 -0
- package/public/assets/tokyo-night-hegEt444.js +1 -0
- package/public/assets/toml-vGWfd6FD.js +1 -0
- package/public/assets/tool-call-D0BYXlFe.js +59 -0
- package/public/assets/ts-tags-zn1MmPIZ.js +1 -0
- package/public/assets/tsv-B_m7g4N7.js +1 -0
- package/public/assets/tsx-COt5Ahok.js +1 -0
- package/public/assets/turtle-BsS91CYL.js +1 -0
- package/public/assets/twig-CO9l9SDP.js +1 -0
- package/public/assets/typescript-BPQ3VLAy.js +1 -0
- package/public/assets/typespec-Df68jz8_.js +1 -0
- package/public/assets/typst-DHCkPAjA.js +1 -0
- package/public/assets/unified-picker-QgONWj_1.js +1 -0
- package/public/assets/v-BcVCzyr7.js +1 -0
- package/public/assets/vala-CsfeWuGM.js +1 -0
- package/public/assets/vb-D17OF-Vu.js +1 -0
- package/public/assets/verilog-BQ8w6xss.js +1 -0
- package/public/assets/vesper-DU1UobuO.js +1 -0
- package/public/assets/vhdl-CeAyd5Ju.js +1 -0
- package/public/assets/viml-CJc9bBzg.js +1 -0
- package/public/assets/vitesse-black-Bkuqu6BP.js +1 -0
- package/public/assets/vitesse-dark-D0r3Knsf.js +1 -0
- package/public/assets/vitesse-light-CVO1_9PV.js +1 -0
- package/public/assets/vue-CCoi5OLL.js +1 -0
- package/public/assets/vue-html-DAAvJJDi.js +1 -0
- package/public/assets/vue-vine-_Ih-lPRR.js +1 -0
- package/public/assets/vyper-CDx5xZoG.js +1 -0
- package/public/assets/wasm-CG6Dc4jp.js +1 -0
- package/public/assets/wasm-MzD3tlZU.js +1 -0
- package/public/assets/wenyan-BV7otONQ.js +1 -0
- package/public/assets/wgsl-Dx-B1_4e.js +1 -0
- package/public/assets/wikitext-BhOHFoWU.js +1 -0
- package/public/assets/wit-5i3qLPDT.js +1 -0
- package/public/assets/wolfram-lXgVvXCa.js +1 -0
- package/public/assets/wrap-text-CEH9wOr_.js +1 -0
- package/public/assets/xml-sdJ4AIDG.js +1 -0
- package/public/assets/xsl-CtQFsRM5.js +1 -0
- package/public/assets/yaml-Buea-lGh.js +1 -0
- package/public/assets/zenscript-DVFEvuxE.js +1 -0
- package/public/assets/zig-VOosw3JB.js +1 -0
- package/public/favicon.ico +0 -0
- package/public/index.html +42 -0
- package/public/loading.html +33 -0
- package/public/logo.png +0 -0
- package/public/manifest.webmanifest +1 -0
- package/public/maskable-icon-512x512.png +0 -0
- package/public/monaco/vs/base/browser/ui/codicons/codicon/codicon.ttf +0 -0
- package/public/monaco/vs/base/worker/workerMain.js +31 -0
- package/public/monaco/vs/basic-languages/cpp/cpp.js +10 -0
- package/public/monaco/vs/basic-languages/kotlin/kotlin.js +10 -0
- package/public/monaco/vs/basic-languages/markdown/markdown.js +10 -0
- package/public/monaco/vs/basic-languages/python/python.js +10 -0
- package/public/monaco/vs/editor/editor.main.css +8 -0
- package/public/monaco/vs/editor/editor.main.js +798 -0
- package/public/monaco/vs/language/css/cssMode.js +13 -0
- package/public/monaco/vs/language/css/cssWorker.js +77 -0
- package/public/monaco/vs/language/html/htmlMode.js +13 -0
- package/public/monaco/vs/language/html/htmlWorker.js +454 -0
- package/public/monaco/vs/language/json/jsonMode.js +19 -0
- package/public/monaco/vs/language/json/jsonWorker.js +42 -0
- package/public/monaco/vs/language/typescript/tsMode.js +20 -0
- package/public/monaco/vs/language/typescript/tsWorker.js +51328 -0
- package/public/monaco/vs/loader.js +11 -0
- package/public/monaco.worker.js +7 -0
- package/public/pwa-192x192.png +0 -0
- package/public/pwa-512x512.png +0 -0
- package/public/pwa-64x64.png +0 -0
- package/public/registerSW.js +1 -0
- package/public/sw.js +1 -0
- package/public/ui-version.json +3 -0
- package/public/workbox-60d14903.js +1 -0
|
@@ -0,0 +1,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 CodeNomad 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 CodeNomad 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: "CodeNomad 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
|
+
}
|
|
@@ -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
|
+
}
|