@waishnav/devspace 1.0.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 +146 -0
- package/dist/cli.js +331 -0
- package/dist/config.js +188 -0
- package/dist/db/client.js +22 -0
- package/dist/db/schema.js +29 -0
- package/dist/git-worktrees.js +134 -0
- package/dist/git.js +41 -0
- package/dist/logger.js +69 -0
- package/dist/oauth-provider.js +266 -0
- package/dist/pi-tools.js +81 -0
- package/dist/review-checkpoints.js +142 -0
- package/dist/roots.js +34 -0
- package/dist/server.js +1148 -0
- package/dist/skills.js +45 -0
- package/dist/ui/.vite/manifest.json +2520 -0
- package/dist/ui/assets/abap-CLvhMVsD.js +1 -0
- package/dist/ui/assets/actionscript-3--17pq3dv.js +1 -0
- package/dist/ui/assets/ada-C5qYipkI.js +1 -0
- package/dist/ui/assets/andromeeda-vGVdxbeo.js +1 -0
- package/dist/ui/assets/angular-html-Di31D1W0.js +1 -0
- package/dist/ui/assets/angular-ts-BKuSFrBS.js +1 -0
- package/dist/ui/assets/apache-U0d_L8uA.js +1 -0
- package/dist/ui/assets/apex-CGTLDQj6.js +1 -0
- package/dist/ui/assets/apl-DgqYbYoh.js +1 -0
- package/dist/ui/assets/applescript-CCn79oCD.js +1 -0
- package/dist/ui/assets/ara-4CJ0cIlV.js +1 -0
- package/dist/ui/assets/asciidoc-DE70LPWp.js +1 -0
- package/dist/ui/assets/asm-Cmm7eHzH.js +1 -0
- package/dist/ui/assets/astro-B19gi6Os.js +1 -0
- package/dist/ui/assets/aurora-x-CDeNXAV0.js +1 -0
- package/dist/ui/assets/awk-BWXHIvNe.js +1 -0
- package/dist/ui/assets/ayu-dark-DluEY0Gj.js +1 -0
- package/dist/ui/assets/ayu-light-C3h-C4tm.js +1 -0
- package/dist/ui/assets/ayu-mirage-Bqwy1Gya.js +1 -0
- package/dist/ui/assets/ballerina-B7ZEbQpA.js +1 -0
- package/dist/ui/assets/bat-Bo4NYOV-.js +1 -0
- package/dist/ui/assets/beancount-D-usSTwE.js +1 -0
- package/dist/ui/assets/berry-DKpUyyne.js +1 -0
- package/dist/ui/assets/bibtex-Ci_nEsc7.js +1 -0
- package/dist/ui/assets/bicep-CUHmPFLl.js +1 -0
- package/dist/ui/assets/bird2-C2hNVINV.js +1 -0
- package/dist/ui/assets/blade-BKHjA4oS.js +1 -0
- package/dist/ui/assets/bsl-BkkzgIyY.js +1 -0
- package/dist/ui/assets/c-FjyGimBn.js +1 -0
- package/dist/ui/assets/c3-CnJL0r0V.js +1 -0
- package/dist/ui/assets/cadence-CQ2zXKGN.js +1 -0
- package/dist/ui/assets/cairo-DLTphjLi.js +1 -0
- package/dist/ui/assets/catppuccin-frappe-3VR1Za6u.js +1 -0
- package/dist/ui/assets/catppuccin-latte-DwIHMF0Q.js +1 -0
- package/dist/ui/assets/catppuccin-macchiato-DYnBP6_5.js +1 -0
- package/dist/ui/assets/catppuccin-mocha-DYhrFGRu.js +1 -0
- package/dist/ui/assets/clarity-SemFz856.js +1 -0
- package/dist/ui/assets/clojure-DqKBuwfJ.js +1 -0
- package/dist/ui/assets/cmake-Bj61d0ZC.js +1 -0
- package/dist/ui/assets/cobol-DlmtgjjX.js +1 -0
- package/dist/ui/assets/codeowners-C8r90Shi.js +1 -0
- package/dist/ui/assets/codeql-oeQT6MSM.js +1 -0
- package/dist/ui/assets/coffee-BlTKLwcf.js +1 -0
- package/dist/ui/assets/common-lisp-Cv5bFMCO.js +1 -0
- package/dist/ui/assets/coq-BrsZFFmf.js +1 -0
- package/dist/ui/assets/cpp-DtVUwoaa.js +1 -0
- package/dist/ui/assets/crystal-C0TLGTHr.js +1 -0
- package/dist/ui/assets/csharp-Ct8U2NOr.js +1 -0
- package/dist/ui/assets/css-FssmIjiX.js +1 -0
- package/dist/ui/assets/csv-Dx-8-gkx.js +1 -0
- package/dist/ui/assets/cue-CE9AQfxI.js +1 -0
- package/dist/ui/assets/cypher-ClKdZ_lG.js +1 -0
- package/dist/ui/assets/d-qD-0Kul2.js +1 -0
- package/dist/ui/assets/dark-plus-Cs2F2srj.js +1 -0
- package/dist/ui/assets/dart-DkHntEIa.js +1 -0
- package/dist/ui/assets/dax-BkyTk9wS.js +1 -0
- package/dist/ui/assets/desktop-Dlh5hvp9.js +1 -0
- package/dist/ui/assets/diff-woXpYk--.js +1 -0
- package/dist/ui/assets/docker-IyjqRm3v.js +1 -0
- package/dist/ui/assets/dotenv-_5a1GRtc.js +1 -0
- package/dist/ui/assets/dracula-BHWKrbxM.js +1 -0
- package/dist/ui/assets/dracula-soft-5eyTD99u.js +1 -0
- package/dist/ui/assets/dream-maker-DW3nJb8Q.js +1 -0
- package/dist/ui/assets/edge-_m-12TBz.js +1 -0
- package/dist/ui/assets/elixir-RKJED3b7.js +1 -0
- package/dist/ui/assets/elm-CzMSD9Oq.js +1 -0
- package/dist/ui/assets/emacs-lisp-C9PiwqqW.js +1 -0
- package/dist/ui/assets/erb-Cqpp-3vK.js +1 -0
- package/dist/ui/assets/erlang-Cphh6RMH.js +1 -0
- package/dist/ui/assets/everforest-dark-sB-x3p7T.js +1 -0
- package/dist/ui/assets/everforest-light-Df2xbC6M.js +1 -0
- package/dist/ui/assets/fennel-DQxkIbk2.js +1 -0
- package/dist/ui/assets/fish-BJitypiv.js +1 -0
- package/dist/ui/assets/fluent-C03EYrpw.js +1 -0
- package/dist/ui/assets/fortran-fixed-form-DEKoE2YW.js +1 -0
- package/dist/ui/assets/fortran-free-form-CYNrtFtB.js +1 -0
- package/dist/ui/assets/fsharp-D13ZGOAj.js +1 -0
- package/dist/ui/assets/gdresource-C0sCabJj.js +1 -0
- package/dist/ui/assets/gdscript-Cp2uCuqX.js +1 -0
- package/dist/ui/assets/gdshader-CBce3t8t.js +1 -0
- package/dist/ui/assets/genie-CV2tkWYe.js +1 -0
- package/dist/ui/assets/gherkin-DExj1W_8.js +1 -0
- package/dist/ui/assets/git-commit-BSykSTBG.js +1 -0
- package/dist/ui/assets/git-rebase-DLS2r2oj.js +1 -0
- package/dist/ui/assets/github-dark-C-LZuMrd.js +1 -0
- package/dist/ui/assets/github-dark-default-DXG-b-1a.js +1 -0
- package/dist/ui/assets/github-dark-dimmed-Bx1FflLF.js +1 -0
- package/dist/ui/assets/github-dark-high-contrast-B_tTalzw.js +1 -0
- package/dist/ui/assets/github-light-EUqPIrTm.js +1 -0
- package/dist/ui/assets/github-light-default-BXViO-2h.js +1 -0
- package/dist/ui/assets/github-light-high-contrast-B68TUdTA.js +1 -0
- package/dist/ui/assets/gleam-CSRkHgEL.js +1 -0
- package/dist/ui/assets/glimmer-js-B1t-JGK1.js +1 -0
- package/dist/ui/assets/glimmer-ts-CazygCNf.js +1 -0
- package/dist/ui/assets/glsl-gBg2vKZ-.js +1 -0
- package/dist/ui/assets/gn-ilITqXS6.js +1 -0
- package/dist/ui/assets/gnuplot-7GGW24-e.js +1 -0
- package/dist/ui/assets/go-BJwz_mda.js +1 -0
- package/dist/ui/assets/graphql-C8u6MNtl.js +1 -0
- package/dist/ui/assets/groovy-CacY0gHj.js +1 -0
- package/dist/ui/assets/gruvbox-dark-hard-C820rvS2.js +1 -0
- package/dist/ui/assets/gruvbox-dark-medium-BPjhmG05.js +1 -0
- package/dist/ui/assets/gruvbox-dark-soft-MrdJrrXF.js +1 -0
- package/dist/ui/assets/gruvbox-light-hard-BC_s9l72.js +1 -0
- package/dist/ui/assets/gruvbox-light-medium-BAWPOn9u.js +1 -0
- package/dist/ui/assets/gruvbox-light-soft-BSMLrYjP.js +1 -0
- package/dist/ui/assets/hack-CgKuqejW.js +1 -0
- package/dist/ui/assets/haml-I5WU4_Rg.js +1 -0
- package/dist/ui/assets/handlebars-DGf6O3Q6.js +1 -0
- package/dist/ui/assets/haskell-D8IpX4py.js +1 -0
- package/dist/ui/assets/haxe-OTjmBuCE.js +1 -0
- package/dist/ui/assets/hcl-Dh228itO.js +1 -0
- package/dist/ui/assets/heavy-payload-Bp6srDbj.js +4 -0
- package/dist/ui/assets/hjson-CxZEssPk.js +1 -0
- package/dist/ui/assets/hlsl-Cvrh5tZx.js +1 -0
- package/dist/ui/assets/horizon-CE9ld1lL.js +1 -0
- package/dist/ui/assets/horizon-bright-Br1oVSNq.js +1 -0
- package/dist/ui/assets/houston-CsvMBhTu.js +1 -0
- package/dist/ui/assets/html-BrIuweS5.js +1 -0
- package/dist/ui/assets/html-derivative-CflP_1S6.js +1 -0
- package/dist/ui/assets/http-qgGkv9Ds.js +1 -0
- package/dist/ui/assets/hurl-CantyQ5z.js +1 -0
- package/dist/ui/assets/hxml-B0Qn7Nwc.js +1 -0
- package/dist/ui/assets/hy-CZbG8q4J.js +1 -0
- package/dist/ui/assets/imba-DsUTQ-LC.js +1 -0
- package/dist/ui/assets/ini-B5eOa1yu.js +1 -0
- package/dist/ui/assets/java-DqStxnBY.js +1 -0
- package/dist/ui/assets/javascript-CJ1ZUMNM.js +1 -0
- package/dist/ui/assets/jinja-Ta-y-8tW.js +1 -0
- package/dist/ui/assets/jison-DZStAtdf.js +1 -0
- package/dist/ui/assets/json-Bfn_pgvS.js +1 -0
- package/dist/ui/assets/json5-BR5RXkoi.js +1 -0
- package/dist/ui/assets/jsonc-CYpm1nAK.js +1 -0
- package/dist/ui/assets/jsonl-CmCQp5Yx.js +1 -0
- package/dist/ui/assets/jsonnet-CJTPZ8u_.js +1 -0
- package/dist/ui/assets/jssm-DXw9l8Rf.js +1 -0
- package/dist/ui/assets/jsx-CFi5D1JI.js +1 -0
- package/dist/ui/assets/julia-CmuPb_FV.js +1 -0
- package/dist/ui/assets/just-xoDaYyy4.js +1 -0
- package/dist/ui/assets/kanagawa-dragon-CXtmUGW6.js +1 -0
- package/dist/ui/assets/kanagawa-lotus-BN08jTvb.js +1 -0
- package/dist/ui/assets/kanagawa-wave-CTweb8Dz.js +1 -0
- package/dist/ui/assets/kdl-CsD5j6eV.js +1 -0
- package/dist/ui/assets/kotlin-DhhofPvG.js +1 -0
- package/dist/ui/assets/kusto-BUv0MjJC.js +1 -0
- package/dist/ui/assets/laserwave-C_8bwKvT.js +1 -0
- package/dist/ui/assets/latex-C7jbQT4s.js +1 -0
- package/dist/ui/assets/lean-CewbzKMR.js +1 -0
- package/dist/ui/assets/less-DVTAwKKz.js +1 -0
- package/dist/ui/assets/light-plus-DVQuIRkW.js +1 -0
- package/dist/ui/assets/liquid-DyVckOju.js +1 -0
- package/dist/ui/assets/llvm-Cm23YOpf.js +1 -0
- package/dist/ui/assets/log-BNLmms1o.js +1 -0
- package/dist/ui/assets/logo-Cluzi2Zq.js +1 -0
- package/dist/ui/assets/lua-CQGEo-kr.js +1 -0
- package/dist/ui/assets/luau-CNKltnaQ.js +1 -0
- package/dist/ui/assets/make-Dixweg8N.js +1 -0
- package/dist/ui/assets/markdown-BYOwaDjH.js +1 -0
- package/dist/ui/assets/marko-CF3FIlcL.js +1 -0
- package/dist/ui/assets/material-theme-Bm3Qr25_.js +1 -0
- package/dist/ui/assets/material-theme-darker-2IIEA8gg.js +1 -0
- package/dist/ui/assets/material-theme-lighter-uhdI0v04.js +1 -0
- package/dist/ui/assets/material-theme-ocean-CHQ94UKr.js +1 -0
- package/dist/ui/assets/material-theme-palenight-B5W6OYN7.js +1 -0
- package/dist/ui/assets/matlab-D7qyCx1q.js +1 -0
- package/dist/ui/assets/mdc-DJfBh8KU.js +1 -0
- package/dist/ui/assets/mdx-DQZ5AkYe.js +1 -0
- package/dist/ui/assets/mermaid-Bk4SNUv9.js +1 -0
- package/dist/ui/assets/min-dark-BSWPekZh.js +1 -0
- package/dist/ui/assets/min-light-DDpmG2fV.js +1 -0
- package/dist/ui/assets/mipsasm-BMqwQI7S.js +1 -0
- package/dist/ui/assets/mojo-BgCJLMeH.js +1 -0
- package/dist/ui/assets/monokai-CdkpiU2Y.js +1 -0
- package/dist/ui/assets/moonbit-CaWjb8XO.js +1 -0
- package/dist/ui/assets/move-B1IS1UjX.js +1 -0
- package/dist/ui/assets/narrat-_X_XdTYD.js +1 -0
- package/dist/ui/assets/nextflow-Bbiyy34d.js +1 -0
- package/dist/ui/assets/nextflow-groovy-Dc_ddanL.js +1 -0
- package/dist/ui/assets/nginx-CmN5T-d3.js +1 -0
- package/dist/ui/assets/night-owl-DhmEMT88.js +1 -0
- package/dist/ui/assets/night-owl-light-eJ-hLW7d.js +1 -0
- package/dist/ui/assets/nim-a_705mqZ.js +1 -0
- package/dist/ui/assets/nix-IvuFDN5E.js +1 -0
- package/dist/ui/assets/nord-Cb4Vim4T.js +1 -0
- package/dist/ui/assets/nushell-DcLAeLz5.js +1 -0
- package/dist/ui/assets/objective-c-D1A_Heim.js +1 -0
- package/dist/ui/assets/objective-cpp-BsSzOQcm.js +1 -0
- package/dist/ui/assets/ocaml-O90oeIOV.js +1 -0
- package/dist/ui/assets/odin-B1RWQWA5.js +1 -0
- package/dist/ui/assets/one-dark-pro-CLwyXe_n.js +1 -0
- package/dist/ui/assets/one-light-D7Lr4KcI.js +1 -0
- package/dist/ui/assets/openscad-BUDT5pXO.js +1 -0
- package/dist/ui/assets/pascal-4ZHwLPI5.js +1 -0
- package/dist/ui/assets/perl-BFxFi55O.js +1 -0
- package/dist/ui/assets/php-C9Uf0Vo_.js +1 -0
- package/dist/ui/assets/pierre-dark-sU4Zdns8.js +1 -0
- package/dist/ui/assets/pierre-dark-soft-HXgun5vs.js +1 -0
- package/dist/ui/assets/pierre-light-DWBk51d8.js +1 -0
- package/dist/ui/assets/pierre-light-soft-CodEzPxf.js +1 -0
- package/dist/ui/assets/pkl-ot-7Btpt.js +1 -0
- package/dist/ui/assets/plastic-DQwYfKfQ.js +1 -0
- package/dist/ui/assets/plsql-DGHpHOYJ.js +1 -0
- package/dist/ui/assets/po-BiJDBrnU.js +1 -0
- package/dist/ui/assets/poimandres-DRFjx7u4.js +1 -0
- package/dist/ui/assets/polar-C7UOKdEL.js +1 -0
- package/dist/ui/assets/postcss-BXeXVLqQ.js +1 -0
- package/dist/ui/assets/powerquery-DNMTfnFr.js +1 -0
- package/dist/ui/assets/powershell-DshXNtvi.js +1 -0
- package/dist/ui/assets/prisma-BsRQq5mF.js +1 -0
- package/dist/ui/assets/prolog-iXnhIJG7.js +1 -0
- package/dist/ui/assets/proto-DB4EqR-F.js +1 -0
- package/dist/ui/assets/pug-DQIrtlYh.js +1 -0
- package/dist/ui/assets/puppet-CDv2pdJW.js +1 -0
- package/dist/ui/assets/purescript-9MfHhQsQ.js +1 -0
- package/dist/ui/assets/python-gzcpVVnB.js +1 -0
- package/dist/ui/assets/qml-CpI_Asuw.js +1 -0
- package/dist/ui/assets/qmldir-DCQb3MpD.js +1 -0
- package/dist/ui/assets/qss-Fe1Jh2GI.js +1 -0
- package/dist/ui/assets/r-hYZdyIMH.js +1 -0
- package/dist/ui/assets/racket-DcIDlBhZ.js +1 -0
- package/dist/ui/assets/raku-B3gFvitq.js +1 -0
- package/dist/ui/assets/razor-CbXx350T.js +1 -0
- package/dist/ui/assets/red-CJ3rzSJv.js +1 -0
- package/dist/ui/assets/reg-CRGYupPL.js +1 -0
- package/dist/ui/assets/regexp-BdfO5R2u.js +1 -0
- package/dist/ui/assets/rel-BtDbiS_P.js +1 -0
- package/dist/ui/assets/review-payload-CC-3-Lb9.js +1 -0
- package/dist/ui/assets/riscv-Ckw8ddFX.js +1 -0
- package/dist/ui/assets/ron-VUp2lXgN.js +1 -0
- package/dist/ui/assets/rose-pine-BthvhNj6.js +1 -0
- package/dist/ui/assets/rose-pine-dawn-Dg85fqjY.js +1 -0
- package/dist/ui/assets/rose-pine-moon-hon4tzzS.js +1 -0
- package/dist/ui/assets/rosmsg-CAekHB0j.js +1 -0
- package/dist/ui/assets/rst-CP6mAm3Y.js +1 -0
- package/dist/ui/assets/ruby-8tjXzrRr.js +1 -0
- package/dist/ui/assets/rust-Cfkwpbl8.js +1 -0
- package/dist/ui/assets/sas-rqWHY37U.js +1 -0
- package/dist/ui/assets/sass-DXrisJhu.js +1 -0
- package/dist/ui/assets/scala-DKOlJaKm.js +1 -0
- package/dist/ui/assets/scheme-DQCgrYNe.js +1 -0
- package/dist/ui/assets/scss-aGYgGui9.js +1 -0
- package/dist/ui/assets/sdbl-bTVj8UrX.js +1 -0
- package/dist/ui/assets/shaderlab-TOUzSsQk.js +1 -0
- package/dist/ui/assets/shellscript-CYr5JWBI.js +1 -0
- package/dist/ui/assets/shellsession-CO3ljRR7.js +1 -0
- package/dist/ui/assets/slack-dark-DnToyrRv.js +1 -0
- package/dist/ui/assets/slack-ochin-B2OO5cIa.js +1 -0
- package/dist/ui/assets/smalltalk-B16xEiuN.js +1 -0
- package/dist/ui/assets/snazzy-light-4G7pJPwS.js +1 -0
- package/dist/ui/assets/solarized-dark-DV17i1UV.js +1 -0
- package/dist/ui/assets/solarized-light-DSh2HLQt.js +1 -0
- package/dist/ui/assets/solidity-CKzVLygQ.js +1 -0
- package/dist/ui/assets/soy-C4_rLmHV.js +1 -0
- package/dist/ui/assets/sparql-D_iOobhT.js +1 -0
- package/dist/ui/assets/splunk-BC2Px7Mm.js +1 -0
- package/dist/ui/assets/sql-2jcbKHJ9.js +1 -0
- package/dist/ui/assets/ssh-config-BgfXC-Er.js +1 -0
- package/dist/ui/assets/stata-Dbr0lM_v.js +1 -0
- package/dist/ui/assets/stylus-B6D30XZt.js +1 -0
- package/dist/ui/assets/surrealql-Cp0eMxMo.js +1 -0
- package/dist/ui/assets/svelte-BfsT9lcS.js +1 -0
- package/dist/ui/assets/swift-DonLKvLd.js +1 -0
- package/dist/ui/assets/synthwave-84-nFMaYfgc.js +1 -0
- package/dist/ui/assets/system-verilog-DJ5XKQeo.js +1 -0
- package/dist/ui/assets/systemd-BxMlprV5.js +1 -0
- package/dist/ui/assets/talonscript-CohzipZa.js +1 -0
- package/dist/ui/assets/tasl-DMoTqEGO.js +1 -0
- package/dist/ui/assets/tcl-CZd0xW_V.js +1 -0
- package/dist/ui/assets/templ-BBOwNqeW.js +1 -0
- package/dist/ui/assets/terraform-DswuEJGm.js +1 -0
- package/dist/ui/assets/tex-CPmn_RsX.js +1 -0
- package/dist/ui/assets/tokyo-night-oM2G3aXe.js +1 -0
- package/dist/ui/assets/toml-CcmNWLt0.js +1 -0
- package/dist/ui/assets/ts-tags-DMILlUz_.js +1 -0
- package/dist/ui/assets/tsv-sltzmVWM.js +1 -0
- package/dist/ui/assets/tsx-CUP2Lfiz.js +1 -0
- package/dist/ui/assets/turtle-ByJddavk.js +1 -0
- package/dist/ui/assets/twig-DEZ_7SDX.js +1 -0
- package/dist/ui/assets/typescript-DhaMQEhQ.js +1 -0
- package/dist/ui/assets/typespec-B88KGewJ.js +1 -0
- package/dist/ui/assets/typst-DI99ib-x.js +1 -0
- package/dist/ui/assets/useFileDiffInstance-Bo6irrE6.js +244 -0
- package/dist/ui/assets/v-DETTlOr0.js +1 -0
- package/dist/ui/assets/vala-zf12oZj6.js +1 -0
- package/dist/ui/assets/vb-Djn5o6TS.js +1 -0
- package/dist/ui/assets/verilog-CiiDBU1e.js +1 -0
- package/dist/ui/assets/vesper-D5bVUKB1.js +1 -0
- package/dist/ui/assets/vhdl-BroJfC0k.js +1 -0
- package/dist/ui/assets/viml-DvXPmvsu.js +1 -0
- package/dist/ui/assets/vitesse-black-fwtXNY1n.js +1 -0
- package/dist/ui/assets/vitesse-dark-BZCL-v6S.js +1 -0
- package/dist/ui/assets/vitesse-light-VbXTXTou.js +1 -0
- package/dist/ui/assets/vue-CAaS6wtt.js +1 -0
- package/dist/ui/assets/vue-html-D3Etensv.js +1 -0
- package/dist/ui/assets/vue-vine-DNRKj0Lh.js +1 -0
- package/dist/ui/assets/vyper-CgoNMtux.js +1 -0
- package/dist/ui/assets/wasm-BnjxR4X6.js +1 -0
- package/dist/ui/assets/wasm-ByWQv1Qj.js +1 -0
- package/dist/ui/assets/wenyan-C8pVoKbM.js +1 -0
- package/dist/ui/assets/wgsl-BsKzXJz4.js +1 -0
- package/dist/ui/assets/wikitext-ClFFjSW2.js +1 -0
- package/dist/ui/assets/wit-DdvCle-K.js +1 -0
- package/dist/ui/assets/wolfram-DLL8P-h_.js +1 -0
- package/dist/ui/assets/workspace-app-8--oTbCd.css +1 -0
- package/dist/ui/assets/workspace-app-BLeU1qC_.js +118 -0
- package/dist/ui/assets/xml-M-X6I2aP.js +1 -0
- package/dist/ui/assets/xsl-CR0ZlrL7.js +1 -0
- package/dist/ui/assets/yaml-pI4xDDgj.js +1 -0
- package/dist/ui/assets/zenscript-BnlCZFoB.js +1 -0
- package/dist/ui/assets/zig-CMLA9XwU.js +1 -0
- package/dist/ui/workspace-app.html +15 -0
- package/dist/user-config.js +57 -0
- package/dist/workspace-store.js +125 -0
- package/dist/workspaces.js +226 -0
- package/docs/assets/devspace-logo-light.png +0 -0
- package/docs/assets/devspace-screenshot.png +0 -0
- package/docs/chatgpt-coding-workflow.md +143 -0
- package/docs/configuration.md +129 -0
- package/docs/gotchas.md +214 -0
- package/docs/security.md +100 -0
- package/docs/setup.md +132 -0
- package/package.json +66 -0
- package/scripts/build-release.mjs +89 -0
- package/scripts/ensure-native-deps.mjs +29 -0
package/README.md
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<picture>
|
|
3
|
+
<img src="docs/assets/devspace-logo-light.png" alt="DevSpace logo" width="140">
|
|
4
|
+
</picture>
|
|
5
|
+
</p>
|
|
6
|
+
|
|
7
|
+
<h1 align="center">DevSpace</h1>
|
|
8
|
+
|
|
9
|
+
<p align="center">Bring a Codex-style coding workflow to ChatGPT.</p>
|
|
10
|
+
|
|
11
|
+
<p align="center">
|
|
12
|
+
<a href="https://www.npmjs.com/package/@waishnav/devspace"><img alt="npm" src="https://img.shields.io/npm/v/%40waishnav%2Fdevspace?style=flat-square" /></a>
|
|
13
|
+
<a href="https://github.com/Waishnav/devspace/actions/workflows/ci.yml"><img alt="CI" src="https://img.shields.io/github/actions/workflow/status/Waishnav/devspace/ci.yml?style=flat-square&branch=main" /></a>
|
|
14
|
+
<a href="https://github.com/Waishnav/devspace/blob/main/LICENSE"><img alt="License" src="https://img.shields.io/npm/l/%40waishnav%2Fdevspace?style=flat-square" /></a>
|
|
15
|
+
</p>
|
|
16
|
+
|
|
17
|
+
[](docs/assets/devspace-screenshot.png)
|
|
18
|
+
|
|
19
|
+
**Give ChatGPT a secure connection to your own machine and Turn ChatGPT into Codex**
|
|
20
|
+
|
|
21
|
+
DevSpace is a self-hosted MCP server that lets ChatGPT read, edit, search, and run code in your real local projects — your files, your tools, your terminal — without uploading anything to a third party. You run it on your machine, expose it through a tunnel you control, and approve the connection with a password only you have.
|
|
22
|
+
|
|
23
|
+
## Quick Start
|
|
24
|
+
|
|
25
|
+
DevSpace requires Node `>=20.12 <27`. Node 22 LTS is recommended.
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npx @waishnav/devspace init
|
|
29
|
+
npx @waishnav/devspace serve
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
During setup, DevSpace asks for:
|
|
33
|
+
|
|
34
|
+
- the local project folders ChatGPT is allowed to open through DevSpace
|
|
35
|
+
- the local port, usually `7676`
|
|
36
|
+
- your public HTTPS base URL from Cloudflare Tunnel, ngrok, Pinggy, Tailscale Funnel, or
|
|
37
|
+
another reverse proxy
|
|
38
|
+
|
|
39
|
+
Use the public origin without `/mcp` during setup:
|
|
40
|
+
|
|
41
|
+
```text
|
|
42
|
+
https://your-tunnel-host.example.com
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Then configure your MCP client with:
|
|
46
|
+
|
|
47
|
+
```text
|
|
48
|
+
https://your-tunnel-host.example.com/mcp
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
When the client connects, DevSpace opens an Owner password approval page. Enter
|
|
52
|
+
the Owner password printed by `devspace init`. It is also stored in:
|
|
53
|
+
|
|
54
|
+
```text
|
|
55
|
+
~/.devspace/auth.json
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Keep that password private.
|
|
59
|
+
|
|
60
|
+
## What ChatGPT Gets
|
|
61
|
+
|
|
62
|
+
After the MCP client connects, ChatGPT can open a project with
|
|
63
|
+
`open_workspace` and then reuse the returned `workspaceId` for later calls.
|
|
64
|
+
|
|
65
|
+
DevSpace provides tools for:
|
|
66
|
+
|
|
67
|
+
- reading, writing, and editing files inside the opened workspace
|
|
68
|
+
- searching files and listing directories
|
|
69
|
+
- running shell commands for tests, builds, git, and package scripts
|
|
70
|
+
- opening isolated Git worktrees when you want parallel work
|
|
71
|
+
- loading `AGENTS.md` and `CLAUDE.md` instructions
|
|
72
|
+
- exposing local agent skills from your skill folders
|
|
73
|
+
- showing ChatGPT Apps review cards for aggregate diffs
|
|
74
|
+
|
|
75
|
+
The default local endpoint is:
|
|
76
|
+
|
|
77
|
+
```text
|
|
78
|
+
http://127.0.0.1:7676/mcp
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Most users should connect through a public HTTPS tunnel:
|
|
82
|
+
|
|
83
|
+
```text
|
|
84
|
+
https://your-tunnel-host.example.com/mcp
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Mental Model
|
|
88
|
+
|
|
89
|
+
DevSpace is remote access to selected local folders.
|
|
90
|
+
|
|
91
|
+
You decide which roots are allowed. The MCP client still has powerful local
|
|
92
|
+
capabilities inside an opened workspace, including shell execution. Treat a
|
|
93
|
+
connected client like a trusted coding partner with access to your machine.
|
|
94
|
+
|
|
95
|
+
For a normal ChatGPT coding session:
|
|
96
|
+
|
|
97
|
+
1. Start your tunnel.
|
|
98
|
+
2. Run `npx @waishnav/devspace serve`.
|
|
99
|
+
3. Connect the MCP client to your public `/mcp` URL.
|
|
100
|
+
4. Approve the connection with the Owner password.
|
|
101
|
+
5. Ask ChatGPT to open a project inside one of your allowed roots.
|
|
102
|
+
|
|
103
|
+
## Platform Support
|
|
104
|
+
|
|
105
|
+
DevSpace supports Linux, macOS, and Windows environments with a Bash-compatible
|
|
106
|
+
shell.
|
|
107
|
+
|
|
108
|
+
| Platform | Status | Notes |
|
|
109
|
+
| ------------------------------------------------- | ----------------- | ---------------------------------------------- |
|
|
110
|
+
| Linux | Supported | Requires Node, npm, Git, and Bash. |
|
|
111
|
+
| macOS | Supported | Requires Node, npm, Git, and Bash. |
|
|
112
|
+
| Windows with Git Bash, WSL, MSYS2, or Cygwin Bash | Supported | Git Bash is the simplest native Windows setup. |
|
|
113
|
+
| Windows PowerShell or `cmd.exe` only | Not supported yet | Install Git Bash or use WSL. |
|
|
114
|
+
|
|
115
|
+
Run this to inspect your local setup:
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
npx @waishnav/devspace doctor
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## Documentation
|
|
122
|
+
|
|
123
|
+
- [Setup Guide](docs/setup.md)
|
|
124
|
+
- [ChatGPT Coding Workflow](docs/chatgpt-coding-workflow.md)
|
|
125
|
+
- [Configuration Reference](docs/configuration.md)
|
|
126
|
+
- [Security Model](docs/security.md)
|
|
127
|
+
- [Troubleshooting Gotchas](docs/gotchas.md)
|
|
128
|
+
|
|
129
|
+
## Local Development
|
|
130
|
+
|
|
131
|
+
For working on DevSpace itself:
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
npm install --include=dev
|
|
135
|
+
npm run typecheck
|
|
136
|
+
npm test
|
|
137
|
+
npm run build
|
|
138
|
+
npm run start
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
For long-running local server processes, build an immutable release copy:
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
npm run release:build
|
|
145
|
+
npm run release:start
|
|
146
|
+
```
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createRequire } from "node:module";
|
|
3
|
+
import { stdin as input, stdout as output } from "node:process";
|
|
4
|
+
import { resolve } from "node:path";
|
|
5
|
+
import * as prompts from "@clack/prompts";
|
|
6
|
+
import { getShellConfig } from "@earendil-works/pi-coding-agent";
|
|
7
|
+
import { satisfies } from "semver";
|
|
8
|
+
import { loadConfig } from "./config.js";
|
|
9
|
+
import { generateOwnerToken, loadDevspaceFiles, writeDevspaceAuth, writeDevspaceConfig, } from "./user-config.js";
|
|
10
|
+
import { expandHomePath } from "./roots.js";
|
|
11
|
+
const require = createRequire(import.meta.url);
|
|
12
|
+
const SUPPORTED_NODE_RANGE = ">=20.12 <27";
|
|
13
|
+
async function main(argv) {
|
|
14
|
+
assertSupportedNode();
|
|
15
|
+
const [rawCommand, ...args] = argv;
|
|
16
|
+
const command = normalizeCommand(rawCommand);
|
|
17
|
+
switch (command) {
|
|
18
|
+
case "serve":
|
|
19
|
+
await ensureConfigured();
|
|
20
|
+
await serve();
|
|
21
|
+
return;
|
|
22
|
+
case "init":
|
|
23
|
+
await runInit({ force: args.includes("--force") });
|
|
24
|
+
return;
|
|
25
|
+
case "doctor":
|
|
26
|
+
await runDoctor();
|
|
27
|
+
return;
|
|
28
|
+
case "config":
|
|
29
|
+
runConfigCommand(args);
|
|
30
|
+
return;
|
|
31
|
+
case "help":
|
|
32
|
+
printHelp();
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function normalizeCommand(command) {
|
|
37
|
+
if (!command || command === "serve" || command === "start")
|
|
38
|
+
return "serve";
|
|
39
|
+
if (command === "init" || command === "doctor" || command === "config")
|
|
40
|
+
return command;
|
|
41
|
+
if (command === "help" || command === "--help" || command === "-h")
|
|
42
|
+
return "help";
|
|
43
|
+
throw new Error(`Unknown command: ${command}`);
|
|
44
|
+
}
|
|
45
|
+
async function ensureConfigured() {
|
|
46
|
+
const files = loadDevspaceFiles();
|
|
47
|
+
if (files.configExists && files.authExists)
|
|
48
|
+
return;
|
|
49
|
+
if (process.env.DEVSPACE_OAUTH_OWNER_TOKEN)
|
|
50
|
+
return;
|
|
51
|
+
if (!input.isTTY || !output.isTTY) {
|
|
52
|
+
throw new Error([
|
|
53
|
+
"DevSpace is not configured and this terminal is non-interactive.",
|
|
54
|
+
"",
|
|
55
|
+
"Run:",
|
|
56
|
+
" devspace init",
|
|
57
|
+
"",
|
|
58
|
+
"Or provide DEVSPACE_OAUTH_OWNER_TOKEN and DEVSPACE_ALLOWED_ROOTS.",
|
|
59
|
+
].join("\n"));
|
|
60
|
+
}
|
|
61
|
+
await runInit({ force: false });
|
|
62
|
+
}
|
|
63
|
+
async function runInit({ force }) {
|
|
64
|
+
const files = loadDevspaceFiles();
|
|
65
|
+
if (!force && files.configExists && files.authExists) {
|
|
66
|
+
prompts.log.info(`DevSpace is already configured at ${files.dir}`);
|
|
67
|
+
prompts.log.info("Run `devspace init --force` to update it.");
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
try {
|
|
71
|
+
prompts.intro("DevSpace setup");
|
|
72
|
+
const defaultRoots = files.config.allowedRoots?.join(", ") || process.cwd();
|
|
73
|
+
const rootsAnswer = await textPrompt({
|
|
74
|
+
message: `Where are your projects located? Press Enter to use ${defaultRoots}`,
|
|
75
|
+
placeholder: defaultRoots,
|
|
76
|
+
defaultValue: defaultRoots,
|
|
77
|
+
validate: (value) => value?.trim() ? undefined : "Enter at least one project root.",
|
|
78
|
+
});
|
|
79
|
+
const allowedRoots = rootsAnswer
|
|
80
|
+
.split(",")
|
|
81
|
+
.map((root) => resolve(expandHomePath(root.trim())))
|
|
82
|
+
.filter(Boolean);
|
|
83
|
+
const defaultPort = String(files.config.port ?? 7676);
|
|
84
|
+
const portAnswer = await textPrompt({
|
|
85
|
+
message: `Which local port should DevSpace use? Press Enter to use ${defaultPort}`,
|
|
86
|
+
placeholder: defaultPort,
|
|
87
|
+
defaultValue: defaultPort,
|
|
88
|
+
validate: validatePort,
|
|
89
|
+
});
|
|
90
|
+
const port = Number(portAnswer);
|
|
91
|
+
prompts.note([
|
|
92
|
+
"DevSpace needs a public base URL so ChatGPT or Claude can reach this MCP server.",
|
|
93
|
+
"Create a tunnel or reverse proxy with Cloudflare Tunnel, ngrok, Pinggy, Tailscale Funnel, or your own HTTPS proxy.",
|
|
94
|
+
"Paste the public origin here, without /mcp.",
|
|
95
|
+
"",
|
|
96
|
+
"Example: https://your-tunnel-host.example.com",
|
|
97
|
+
].join("\n"), "Public URL required");
|
|
98
|
+
const publicBaseUrl = normalizePublicBaseUrl(await textPrompt({
|
|
99
|
+
message: files.config.publicBaseUrl
|
|
100
|
+
? `What is the public base URL? Press Enter to keep ${files.config.publicBaseUrl}`
|
|
101
|
+
: "What is the public base URL?",
|
|
102
|
+
placeholder: files.config.publicBaseUrl ?? "https://your-tunnel-host.example.com",
|
|
103
|
+
defaultValue: files.config.publicBaseUrl ?? "",
|
|
104
|
+
validate: validateRequiredPublicBaseUrl,
|
|
105
|
+
}));
|
|
106
|
+
const config = {
|
|
107
|
+
host: files.config.host ?? "127.0.0.1",
|
|
108
|
+
port,
|
|
109
|
+
allowedRoots,
|
|
110
|
+
publicBaseUrl,
|
|
111
|
+
};
|
|
112
|
+
const auth = {
|
|
113
|
+
ownerToken: files.auth.ownerToken ?? generateOwnerToken(),
|
|
114
|
+
};
|
|
115
|
+
const configPath = writeDevspaceConfig(config);
|
|
116
|
+
const authPath = writeDevspaceAuth(auth);
|
|
117
|
+
const lines = [
|
|
118
|
+
`Config: ${configPath}`,
|
|
119
|
+
`Auth: ${authPath}`,
|
|
120
|
+
`Local MCP URL: http://${config.host}:${config.port}/mcp`,
|
|
121
|
+
...(publicBaseUrl ? [`Public MCP URL: ${publicBaseUrl}/mcp`] : []),
|
|
122
|
+
];
|
|
123
|
+
prompts.note(lines.join("\n"), "DevSpace configured");
|
|
124
|
+
prompts.note([
|
|
125
|
+
`Owner password: ${auth.ownerToken}`,
|
|
126
|
+
"Use this when ChatGPT or Claude asks you to approve DevSpace access.",
|
|
127
|
+
`Stored at: ${authPath}`,
|
|
128
|
+
].join("\n"), "Owner password");
|
|
129
|
+
prompts.outro("Run `devspace serve` to start the MCP server.");
|
|
130
|
+
}
|
|
131
|
+
catch (error) {
|
|
132
|
+
if (error instanceof SetupCancelledError) {
|
|
133
|
+
prompts.cancel("Setup cancelled");
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
throw error;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
async function serve() {
|
|
140
|
+
const sqliteStatus = checkSqliteNative();
|
|
141
|
+
if (sqliteStatus !== "ok") {
|
|
142
|
+
throw new Error([
|
|
143
|
+
"better-sqlite3 could not load for this Node runtime.",
|
|
144
|
+
sqliteStatus,
|
|
145
|
+
"",
|
|
146
|
+
"Try reinstalling or rebuilding dependencies under the active Node version:",
|
|
147
|
+
" npm rebuild better-sqlite3",
|
|
148
|
+
].join("\n"));
|
|
149
|
+
}
|
|
150
|
+
const { createServer } = await import("./server.js");
|
|
151
|
+
const config = loadConfig();
|
|
152
|
+
const { app } = createServer(config);
|
|
153
|
+
const httpServer = app.listen(config.port, config.host, () => {
|
|
154
|
+
console.log(`devspace listening on http://${config.host}:${config.port}/mcp`);
|
|
155
|
+
console.log(`public base url: ${config.publicBaseUrl}`);
|
|
156
|
+
console.log(`allowed roots: ${config.allowedRoots.join(", ")}`);
|
|
157
|
+
console.log(`allowed hosts: ${config.allowedHosts.join(", ")}`);
|
|
158
|
+
if (config.allowedHosts.includes("*")) {
|
|
159
|
+
console.warn("warning: Host header allowlist is disabled because DEVSPACE_ALLOWED_HOSTS=*");
|
|
160
|
+
}
|
|
161
|
+
console.log("auth: Owner password approval required");
|
|
162
|
+
console.log(`logging: ${config.logging.level} ${config.logging.format}`);
|
|
163
|
+
});
|
|
164
|
+
const shutdown = () => {
|
|
165
|
+
httpServer.close(() => process.exit(0));
|
|
166
|
+
};
|
|
167
|
+
process.once("SIGINT", shutdown);
|
|
168
|
+
process.once("SIGTERM", shutdown);
|
|
169
|
+
}
|
|
170
|
+
async function runDoctor() {
|
|
171
|
+
const files = loadDevspaceFiles();
|
|
172
|
+
console.log(`Config dir: ${files.dir}`);
|
|
173
|
+
console.log(`Config file: ${files.configExists ? files.configPath : "missing"}`);
|
|
174
|
+
console.log(`Auth file: ${files.authExists ? files.authPath : "missing"}`);
|
|
175
|
+
console.log(`Node: ${process.version} (${nodeVersionStatus()})`);
|
|
176
|
+
console.log(`Node ABI: ${process.versions.modules}`);
|
|
177
|
+
console.log(`Platform: ${process.platform} ${process.arch}`);
|
|
178
|
+
console.log(`Git: ${checkGitAvailable()}`);
|
|
179
|
+
console.log(`Bash shell: ${checkBashShell()}`);
|
|
180
|
+
console.log(`SQLite native dependency: ${checkSqliteNative()}`);
|
|
181
|
+
try {
|
|
182
|
+
const config = loadConfig();
|
|
183
|
+
console.log(`Local MCP URL: http://${config.host}:${config.port}/mcp`);
|
|
184
|
+
console.log(`Public MCP URL: ${new URL("/mcp", config.publicBaseUrl).toString()}`);
|
|
185
|
+
console.log(`Allowed roots: ${config.allowedRoots.join(", ")}`);
|
|
186
|
+
console.log(`Allowed hosts: ${config.allowedHosts.join(", ")}`);
|
|
187
|
+
}
|
|
188
|
+
catch (error) {
|
|
189
|
+
console.log(`Config status: ${error instanceof Error ? error.message : String(error)}`);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
function runConfigCommand(args) {
|
|
193
|
+
const [subcommand, key, ...rest] = args;
|
|
194
|
+
const files = loadDevspaceFiles();
|
|
195
|
+
if (!subcommand || subcommand === "get") {
|
|
196
|
+
console.log(JSON.stringify(files.config, null, 2));
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
if (subcommand !== "set") {
|
|
200
|
+
throw new Error(`Unknown config command: ${subcommand}`);
|
|
201
|
+
}
|
|
202
|
+
if (key !== "publicBaseUrl") {
|
|
203
|
+
throw new Error("Only `devspace config set publicBaseUrl <url|null>` is supported right now.");
|
|
204
|
+
}
|
|
205
|
+
const value = rest.join(" ").trim();
|
|
206
|
+
if (!value) {
|
|
207
|
+
throw new Error("Missing publicBaseUrl value.");
|
|
208
|
+
}
|
|
209
|
+
writeDevspaceConfig({
|
|
210
|
+
...files.config,
|
|
211
|
+
publicBaseUrl: normalizeOptionalPublicBaseUrl(value),
|
|
212
|
+
});
|
|
213
|
+
console.log(`Updated ${files.configPath}`);
|
|
214
|
+
}
|
|
215
|
+
function printHelp() {
|
|
216
|
+
console.log([
|
|
217
|
+
"DevSpace",
|
|
218
|
+
"",
|
|
219
|
+
"Usage:",
|
|
220
|
+
" devspace Run first-time setup if needed, then start the server",
|
|
221
|
+
" devspace serve Start the server",
|
|
222
|
+
" devspace init Create or update ~/.devspace/config.json and auth.json",
|
|
223
|
+
" devspace doctor Show config, runtime, and native dependency status",
|
|
224
|
+
" devspace config get Print persisted config",
|
|
225
|
+
" devspace config set publicBaseUrl <url|null>",
|
|
226
|
+
"",
|
|
227
|
+
"For temporary tunnels:",
|
|
228
|
+
" DEVSPACE_PUBLIC_BASE_URL=https://example.trycloudflare.com devspace serve",
|
|
229
|
+
].join("\n"));
|
|
230
|
+
}
|
|
231
|
+
function normalizeOptionalPublicBaseUrl(value) {
|
|
232
|
+
const trimmed = value.trim();
|
|
233
|
+
if (!trimmed || trimmed === "null" || trimmed === "none")
|
|
234
|
+
return null;
|
|
235
|
+
return normalizePublicBaseUrl(trimmed);
|
|
236
|
+
}
|
|
237
|
+
function normalizePublicBaseUrl(value) {
|
|
238
|
+
const trimmed = value.trim();
|
|
239
|
+
const parsed = new URL(trimmed);
|
|
240
|
+
parsed.hash = "";
|
|
241
|
+
parsed.search = "";
|
|
242
|
+
parsed.pathname = parsed.pathname.replace(/\/+$/, "");
|
|
243
|
+
return parsed.toString().replace(/\/$/, "");
|
|
244
|
+
}
|
|
245
|
+
async function textPrompt(options) {
|
|
246
|
+
const result = await prompts.text({
|
|
247
|
+
...options,
|
|
248
|
+
validate: (value) => options.validate?.(value?.trim() ? value : options.defaultValue),
|
|
249
|
+
});
|
|
250
|
+
if (prompts.isCancel(result))
|
|
251
|
+
throw new SetupCancelledError();
|
|
252
|
+
const value = String(result).trim();
|
|
253
|
+
return value || options.defaultValue;
|
|
254
|
+
}
|
|
255
|
+
function validatePort(value) {
|
|
256
|
+
const port = Number(value);
|
|
257
|
+
return Number.isInteger(port) && port >= 1 && port <= 65535
|
|
258
|
+
? undefined
|
|
259
|
+
: "Enter a port between 1 and 65535.";
|
|
260
|
+
}
|
|
261
|
+
function validateRequiredPublicBaseUrl(value) {
|
|
262
|
+
const trimmed = value?.trim() ?? "";
|
|
263
|
+
if (!trimmed)
|
|
264
|
+
return "Enter the public URL from your tunnel or reverse proxy.";
|
|
265
|
+
if (trimmed.endsWith("/mcp"))
|
|
266
|
+
return "Enter the base URL only, without /mcp.";
|
|
267
|
+
return validatePublicBaseUrl(trimmed);
|
|
268
|
+
}
|
|
269
|
+
function validatePublicBaseUrl(value) {
|
|
270
|
+
try {
|
|
271
|
+
const parsed = new URL(value);
|
|
272
|
+
return parsed.protocol === "http:" || parsed.protocol === "https:"
|
|
273
|
+
? undefined
|
|
274
|
+
: "Use an http or https URL.";
|
|
275
|
+
}
|
|
276
|
+
catch {
|
|
277
|
+
return "Enter a valid URL, for example https://your-tunnel-host.example.com.";
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
function assertSupportedNode() {
|
|
281
|
+
if (satisfies(process.versions.node, SUPPORTED_NODE_RANGE))
|
|
282
|
+
return;
|
|
283
|
+
throw new Error([
|
|
284
|
+
`DevSpace requires Node ${SUPPORTED_NODE_RANGE}.`,
|
|
285
|
+
`Current Node: ${process.version}`,
|
|
286
|
+
"",
|
|
287
|
+
"Install Node 22 LTS or use a version manager such as nvm, fnm, or mise.",
|
|
288
|
+
].join("\n"));
|
|
289
|
+
}
|
|
290
|
+
function nodeVersionStatus() {
|
|
291
|
+
return satisfies(process.versions.node, SUPPORTED_NODE_RANGE)
|
|
292
|
+
? `supported ${SUPPORTED_NODE_RANGE}`
|
|
293
|
+
: `unsupported, requires ${SUPPORTED_NODE_RANGE}`;
|
|
294
|
+
}
|
|
295
|
+
class SetupCancelledError extends Error {
|
|
296
|
+
}
|
|
297
|
+
function checkSqliteNative() {
|
|
298
|
+
try {
|
|
299
|
+
const Database = require("better-sqlite3");
|
|
300
|
+
const db = new Database(":memory:");
|
|
301
|
+
db.close();
|
|
302
|
+
return "ok";
|
|
303
|
+
}
|
|
304
|
+
catch (error) {
|
|
305
|
+
return error instanceof Error ? error.message : String(error);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
function checkGitAvailable() {
|
|
309
|
+
try {
|
|
310
|
+
const { execFileSync } = require("node:child_process");
|
|
311
|
+
return execFileSync("git", ["--version"], { encoding: "utf8" }).trim();
|
|
312
|
+
}
|
|
313
|
+
catch (error) {
|
|
314
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
315
|
+
return `unavailable (${message})`;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
function checkBashShell() {
|
|
319
|
+
try {
|
|
320
|
+
const { shell, args } = getShellConfig();
|
|
321
|
+
return `${shell} ${args.join(" ")}`;
|
|
322
|
+
}
|
|
323
|
+
catch (error) {
|
|
324
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
325
|
+
return `unavailable (${message})`;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
main(process.argv.slice(2)).catch((error) => {
|
|
329
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
330
|
+
process.exitCode = 1;
|
|
331
|
+
});
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import { homedir } from "node:os";
|
|
2
|
+
import { join, resolve } from "node:path";
|
|
3
|
+
import { expandHomePath } from "./roots.js";
|
|
4
|
+
import { loadDevspaceFiles } from "./user-config.js";
|
|
5
|
+
const DEFAULT_OAUTH_ACCESS_TOKEN_TTL_SECONDS = 60 * 60;
|
|
6
|
+
const DEFAULT_OAUTH_REFRESH_TOKEN_TTL_SECONDS = 30 * 24 * 60 * 60;
|
|
7
|
+
function parsePort(value) {
|
|
8
|
+
if (value === undefined || value === "")
|
|
9
|
+
return 7676;
|
|
10
|
+
const port = Number(value);
|
|
11
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535) {
|
|
12
|
+
throw new Error(`Invalid PORT: ${value}`);
|
|
13
|
+
}
|
|
14
|
+
return port;
|
|
15
|
+
}
|
|
16
|
+
function parseAllowedRoots(value) {
|
|
17
|
+
if (Array.isArray(value)) {
|
|
18
|
+
const roots = value.map((entry) => entry.trim()).filter(Boolean);
|
|
19
|
+
return (roots.length > 0 ? roots : [process.cwd()]).map((root) => resolve(expandHomePath(root)));
|
|
20
|
+
}
|
|
21
|
+
const rawRoots = value
|
|
22
|
+
?.split(",")
|
|
23
|
+
.map((entry) => entry.trim())
|
|
24
|
+
.filter(Boolean) ?? [];
|
|
25
|
+
const roots = rawRoots.length > 0 ? rawRoots : [process.cwd()];
|
|
26
|
+
return roots.map((root) => resolve(expandHomePath(root)));
|
|
27
|
+
}
|
|
28
|
+
function parseAllowedHosts(value, derivedHosts) {
|
|
29
|
+
if (Array.isArray(value)) {
|
|
30
|
+
return normalizeAllowedHosts(value, derivedHosts);
|
|
31
|
+
}
|
|
32
|
+
const rawHosts = value
|
|
33
|
+
?.split(",")
|
|
34
|
+
.map((entry) => entry.trim())
|
|
35
|
+
.filter(Boolean) ?? [];
|
|
36
|
+
return normalizeAllowedHosts(rawHosts, derivedHosts);
|
|
37
|
+
}
|
|
38
|
+
function normalizeAllowedHosts(rawHosts, derivedHosts) {
|
|
39
|
+
const hosts = rawHosts.length > 0 ? rawHosts : derivedHosts;
|
|
40
|
+
if (hosts.includes("*"))
|
|
41
|
+
return ["*"];
|
|
42
|
+
return Array.from(new Set(hosts.map((host) => host.trim()).filter(Boolean)));
|
|
43
|
+
}
|
|
44
|
+
function parseBoolean(value) {
|
|
45
|
+
return ["1", "true", "yes", "on"].includes(value?.toLowerCase() ?? "");
|
|
46
|
+
}
|
|
47
|
+
function parseMinimalTools(env) {
|
|
48
|
+
return env.DEVSPACE_TOOL_MODE === "minimal" || parseBoolean(env.DEVSPACE_MINIMAL_TOOLS);
|
|
49
|
+
}
|
|
50
|
+
function parseLogLevel(value) {
|
|
51
|
+
if (!value || value === "info")
|
|
52
|
+
return "info";
|
|
53
|
+
if (["silent", "error", "warn", "debug"].includes(value))
|
|
54
|
+
return value;
|
|
55
|
+
throw new Error(`Invalid DEVSPACE_LOG_LEVEL: ${value}`);
|
|
56
|
+
}
|
|
57
|
+
function parseLogFormat(value) {
|
|
58
|
+
if (!value || value === "json")
|
|
59
|
+
return "json";
|
|
60
|
+
if (value === "pretty")
|
|
61
|
+
return "pretty";
|
|
62
|
+
throw new Error(`Invalid DEVSPACE_LOG_FORMAT: ${value}`);
|
|
63
|
+
}
|
|
64
|
+
function parsePathList(value) {
|
|
65
|
+
return (value
|
|
66
|
+
?.split(",")
|
|
67
|
+
.map((entry) => entry.trim())
|
|
68
|
+
.filter(Boolean)
|
|
69
|
+
.map((entry) => resolve(expandHomePath(entry))) ?? []);
|
|
70
|
+
}
|
|
71
|
+
function parseStringList(value, fallback) {
|
|
72
|
+
const entries = value
|
|
73
|
+
?.split(",")
|
|
74
|
+
.map((entry) => entry.trim())
|
|
75
|
+
.filter(Boolean);
|
|
76
|
+
return entries && entries.length > 0 ? entries : fallback;
|
|
77
|
+
}
|
|
78
|
+
function parsePositiveInteger(value, fallback, name) {
|
|
79
|
+
if (!value)
|
|
80
|
+
return fallback;
|
|
81
|
+
const parsed = Number(value);
|
|
82
|
+
if (!Number.isInteger(parsed) || parsed < 1) {
|
|
83
|
+
throw new Error(`Invalid ${name}: ${value}`);
|
|
84
|
+
}
|
|
85
|
+
return parsed;
|
|
86
|
+
}
|
|
87
|
+
function parseToolNaming(value) {
|
|
88
|
+
if (!value || value === "legacy")
|
|
89
|
+
return "legacy";
|
|
90
|
+
if (value === "short")
|
|
91
|
+
return "short";
|
|
92
|
+
throw new Error(`Invalid DEVSPACE_TOOL_NAMING: ${value}`);
|
|
93
|
+
}
|
|
94
|
+
function parseLoggingConfig(env) {
|
|
95
|
+
return {
|
|
96
|
+
level: parseLogLevel(env.DEVSPACE_LOG_LEVEL),
|
|
97
|
+
format: parseLogFormat(env.DEVSPACE_LOG_FORMAT),
|
|
98
|
+
requests: env.DEVSPACE_LOG_REQUESTS === undefined ? true : parseBoolean(env.DEVSPACE_LOG_REQUESTS),
|
|
99
|
+
assets: parseBoolean(env.DEVSPACE_LOG_ASSETS),
|
|
100
|
+
toolCalls: env.DEVSPACE_LOG_TOOL_CALLS === undefined ? true : parseBoolean(env.DEVSPACE_LOG_TOOL_CALLS),
|
|
101
|
+
shellCommands: parseBoolean(env.DEVSPACE_LOG_SHELL_COMMANDS),
|
|
102
|
+
trustProxy: parseBoolean(env.DEVSPACE_TRUST_PROXY),
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
function parseWidgetMode(value) {
|
|
106
|
+
if (!value || value === "changes")
|
|
107
|
+
return "changes";
|
|
108
|
+
if (value === "off" || value === "full")
|
|
109
|
+
return value;
|
|
110
|
+
throw new Error(`Invalid DEVSPACE_WIDGETS: ${value}`);
|
|
111
|
+
}
|
|
112
|
+
function parseRequiredSecret(value, name) {
|
|
113
|
+
const secret = value?.trim();
|
|
114
|
+
if (!secret) {
|
|
115
|
+
throw new Error(`${name} is required for DevSpace OAuth. Run: devspace init`);
|
|
116
|
+
}
|
|
117
|
+
if (secret.length < 16) {
|
|
118
|
+
throw new Error(`${name} must be at least 16 characters long.`);
|
|
119
|
+
}
|
|
120
|
+
return secret;
|
|
121
|
+
}
|
|
122
|
+
function parseOAuthConfig(env, ownerToken) {
|
|
123
|
+
return {
|
|
124
|
+
ownerToken: parseRequiredSecret(env.DEVSPACE_OAUTH_OWNER_TOKEN ?? ownerToken, "DEVSPACE_OAUTH_OWNER_TOKEN"),
|
|
125
|
+
accessTokenTtlSeconds: parsePositiveInteger(env.DEVSPACE_OAUTH_ACCESS_TOKEN_TTL_SECONDS, DEFAULT_OAUTH_ACCESS_TOKEN_TTL_SECONDS, "DEVSPACE_OAUTH_ACCESS_TOKEN_TTL_SECONDS"),
|
|
126
|
+
refreshTokenTtlSeconds: parsePositiveInteger(env.DEVSPACE_OAUTH_REFRESH_TOKEN_TTL_SECONDS, DEFAULT_OAUTH_REFRESH_TOKEN_TTL_SECONDS, "DEVSPACE_OAUTH_REFRESH_TOKEN_TTL_SECONDS"),
|
|
127
|
+
scopes: parseStringList(env.DEVSPACE_OAUTH_SCOPES, ["devspace"]),
|
|
128
|
+
allowedRedirectHosts: parseStringList(env.DEVSPACE_OAUTH_ALLOWED_REDIRECT_HOSTS, [
|
|
129
|
+
"chatgpt.com",
|
|
130
|
+
"localhost",
|
|
131
|
+
"127.0.0.1",
|
|
132
|
+
]),
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
function defaultStateDir() {
|
|
136
|
+
return join(homedir(), ".local", "share", "devspace");
|
|
137
|
+
}
|
|
138
|
+
function defaultWorktreeRoot() {
|
|
139
|
+
return join(homedir(), ".devspace", "worktrees");
|
|
140
|
+
}
|
|
141
|
+
function defaultAgentDir() {
|
|
142
|
+
return join(homedir(), ".codex");
|
|
143
|
+
}
|
|
144
|
+
export function loadConfig(env = process.env) {
|
|
145
|
+
const files = loadDevspaceFiles(env);
|
|
146
|
+
const host = env.HOST ?? files.config.host ?? "127.0.0.1";
|
|
147
|
+
const port = parsePort(env.PORT ?? files.config.port);
|
|
148
|
+
const publicBaseUrl = parsePublicBaseUrl(env.DEVSPACE_PUBLIC_BASE_URL ?? files.config.publicBaseUrl ?? localPublicBaseUrl(host, port));
|
|
149
|
+
const derivedAllowedHosts = [
|
|
150
|
+
"localhost",
|
|
151
|
+
"127.0.0.1",
|
|
152
|
+
"::1",
|
|
153
|
+
host,
|
|
154
|
+
new URL(publicBaseUrl).hostname,
|
|
155
|
+
...(files.config.allowedHosts ?? []),
|
|
156
|
+
];
|
|
157
|
+
return {
|
|
158
|
+
host,
|
|
159
|
+
port,
|
|
160
|
+
oauth: parseOAuthConfig(env, files.auth.ownerToken),
|
|
161
|
+
allowedRoots: parseAllowedRoots(env.DEVSPACE_ALLOWED_ROOTS ?? files.config.allowedRoots),
|
|
162
|
+
allowedHosts: parseAllowedHosts(env.DEVSPACE_ALLOWED_HOSTS, derivedAllowedHosts),
|
|
163
|
+
publicBaseUrl,
|
|
164
|
+
minimalTools: parseMinimalTools(env),
|
|
165
|
+
toolNaming: parseToolNaming(env.DEVSPACE_TOOL_NAMING),
|
|
166
|
+
widgets: parseWidgetMode(env.DEVSPACE_WIDGETS),
|
|
167
|
+
stateDir: resolve(expandHomePath(env.DEVSPACE_STATE_DIR ?? files.config.stateDir ?? defaultStateDir())),
|
|
168
|
+
worktreeRoot: resolve(expandHomePath(env.DEVSPACE_WORKTREE_ROOT ?? files.config.worktreeRoot ?? defaultWorktreeRoot())),
|
|
169
|
+
skillsEnabled: env.DEVSPACE_SKILLS === undefined ? true : parseBoolean(env.DEVSPACE_SKILLS),
|
|
170
|
+
skillPaths: parsePathList(env.DEVSPACE_SKILL_PATHS),
|
|
171
|
+
agentDir: resolve(expandHomePath(env.DEVSPACE_AGENT_DIR ?? files.config.agentDir ?? defaultAgentDir())),
|
|
172
|
+
logging: parseLoggingConfig(env),
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
function parsePublicBaseUrl(value) {
|
|
176
|
+
const parsed = new URL(value);
|
|
177
|
+
parsed.hash = "";
|
|
178
|
+
parsed.search = "";
|
|
179
|
+
parsed.pathname = parsed.pathname.replace(/\/+$/, "");
|
|
180
|
+
return parsed.toString().replace(/\/$/, "");
|
|
181
|
+
}
|
|
182
|
+
function localPublicBaseUrl(host, port) {
|
|
183
|
+
const publicHost = host === "0.0.0.0" || host === "::" ? "127.0.0.1" : host;
|
|
184
|
+
const formattedHost = publicHost.includes(":") && !publicHost.startsWith("[")
|
|
185
|
+
? `[${publicHost}]`
|
|
186
|
+
: publicHost;
|
|
187
|
+
return `http://${formattedHost}:${port}`;
|
|
188
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { mkdirSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import Database from "better-sqlite3";
|
|
4
|
+
import { drizzle } from "drizzle-orm/better-sqlite3";
|
|
5
|
+
import * as schema from "./schema.js";
|
|
6
|
+
export function databasePath(stateDir) {
|
|
7
|
+
return join(stateDir, "devspace.sqlite");
|
|
8
|
+
}
|
|
9
|
+
export function openDatabase(stateDir) {
|
|
10
|
+
mkdirSync(stateDir, { recursive: true });
|
|
11
|
+
const sqlite = new Database(databasePath(stateDir));
|
|
12
|
+
sqlite.pragma("journal_mode = WAL");
|
|
13
|
+
sqlite.pragma("foreign_keys = ON");
|
|
14
|
+
return {
|
|
15
|
+
sqlite,
|
|
16
|
+
db: createDrizzleDatabase(sqlite),
|
|
17
|
+
close: () => sqlite.close(),
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
function createDrizzleDatabase(sqlite) {
|
|
21
|
+
return drizzle(sqlite, { schema });
|
|
22
|
+
}
|