@zoer7788/mcp-nexus-node 0.1.5 → 0.1.7
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/cli.js +1 -0
- package/package.json +4 -5
- package/vendor/devspace/dist/cli.js +374 -0
- package/vendor/devspace/dist/config.js +253 -0
- package/vendor/devspace/dist/dashboard.js +545 -0
- package/vendor/devspace/dist/db/client.js +29 -0
- package/vendor/devspace/dist/db/migrations.js +123 -0
- package/vendor/devspace/dist/db/schema.js +52 -0
- package/vendor/devspace/dist/git-worktrees.js +134 -0
- package/vendor/devspace/dist/git.js +41 -0
- package/vendor/devspace/dist/logger.js +69 -0
- package/vendor/devspace/dist/oauth-provider.js +237 -0
- package/vendor/devspace/dist/oauth-store.js +138 -0
- package/vendor/devspace/dist/pi-tools.js +81 -0
- package/vendor/devspace/dist/review-checkpoints.js +142 -0
- package/vendor/devspace/dist/roots.js +34 -0
- package/vendor/devspace/dist/server.js +1204 -0
- package/vendor/devspace/dist/skills.js +45 -0
- package/vendor/devspace/dist/ui/.vite/manifest.json +2520 -0
- package/vendor/devspace/dist/ui/assets/abap-CLvhMVsD.js +1 -0
- package/vendor/devspace/dist/ui/assets/actionscript-3--17pq3dv.js +1 -0
- package/vendor/devspace/dist/ui/assets/ada-C5qYipkI.js +1 -0
- package/vendor/devspace/dist/ui/assets/andromeeda-vGVdxbeo.js +1 -0
- package/vendor/devspace/dist/ui/assets/angular-html-Dj4LysSE.js +1 -0
- package/vendor/devspace/dist/ui/assets/angular-ts-CxuC_FFh.js +1 -0
- package/vendor/devspace/dist/ui/assets/apache-U0d_L8uA.js +1 -0
- package/vendor/devspace/dist/ui/assets/apex-CGTLDQj6.js +1 -0
- package/vendor/devspace/dist/ui/assets/apl-RwuvNFFo.js +1 -0
- package/vendor/devspace/dist/ui/assets/applescript-CCn79oCD.js +1 -0
- package/vendor/devspace/dist/ui/assets/ara-4CJ0cIlV.js +1 -0
- package/vendor/devspace/dist/ui/assets/asciidoc-DE70LPWp.js +1 -0
- package/vendor/devspace/dist/ui/assets/asm-Cmm7eHzH.js +1 -0
- package/vendor/devspace/dist/ui/assets/astro-ncoaUbsO.js +1 -0
- package/vendor/devspace/dist/ui/assets/aurora-x-CDeNXAV0.js +1 -0
- package/vendor/devspace/dist/ui/assets/awk-BWXHIvNe.js +1 -0
- package/vendor/devspace/dist/ui/assets/ayu-dark-DluEY0Gj.js +1 -0
- package/vendor/devspace/dist/ui/assets/ayu-light-C3h-C4tm.js +1 -0
- package/vendor/devspace/dist/ui/assets/ayu-mirage-Bqwy1Gya.js +1 -0
- package/vendor/devspace/dist/ui/assets/ballerina-B7ZEbQpA.js +1 -0
- package/vendor/devspace/dist/ui/assets/bat-Bo4NYOV-.js +1 -0
- package/vendor/devspace/dist/ui/assets/beancount-D-usSTwE.js +1 -0
- package/vendor/devspace/dist/ui/assets/berry-DKpUyyne.js +1 -0
- package/vendor/devspace/dist/ui/assets/bibtex-Ci_nEsc7.js +1 -0
- package/vendor/devspace/dist/ui/assets/bicep-CUHmPFLl.js +1 -0
- package/vendor/devspace/dist/ui/assets/bird2-C2hNVINV.js +1 -0
- package/vendor/devspace/dist/ui/assets/blade-Dw8-Lzux.js +1 -0
- package/vendor/devspace/dist/ui/assets/bsl-BkkzgIyY.js +1 -0
- package/vendor/devspace/dist/ui/assets/c-Dpbbo_f2.js +1 -0
- package/vendor/devspace/dist/ui/assets/c3-CnJL0r0V.js +1 -0
- package/vendor/devspace/dist/ui/assets/cadence-CQ2zXKGN.js +1 -0
- package/vendor/devspace/dist/ui/assets/cairo-DLTphjLi.js +1 -0
- package/vendor/devspace/dist/ui/assets/catppuccin-frappe-3VR1Za6u.js +1 -0
- package/vendor/devspace/dist/ui/assets/catppuccin-latte-DwIHMF0Q.js +1 -0
- package/vendor/devspace/dist/ui/assets/catppuccin-macchiato-DYnBP6_5.js +1 -0
- package/vendor/devspace/dist/ui/assets/catppuccin-mocha-DYhrFGRu.js +1 -0
- package/vendor/devspace/dist/ui/assets/clarity-SemFz856.js +1 -0
- package/vendor/devspace/dist/ui/assets/clojure-DqKBuwfJ.js +1 -0
- package/vendor/devspace/dist/ui/assets/cmake-Bj61d0ZC.js +1 -0
- package/vendor/devspace/dist/ui/assets/cobol-4ddX3THy.js +1 -0
- package/vendor/devspace/dist/ui/assets/codeowners-C8r90Shi.js +1 -0
- package/vendor/devspace/dist/ui/assets/codeql-oeQT6MSM.js +1 -0
- package/vendor/devspace/dist/ui/assets/coffee-Bn-a5KqK.js +1 -0
- package/vendor/devspace/dist/ui/assets/common-lisp-Cv5bFMCO.js +1 -0
- package/vendor/devspace/dist/ui/assets/coq-BrsZFFmf.js +1 -0
- package/vendor/devspace/dist/ui/assets/cpp-DoRrlM_T.js +1 -0
- package/vendor/devspace/dist/ui/assets/crystal-DiZfCxn-.js +1 -0
- package/vendor/devspace/dist/ui/assets/csharp-Ct8U2NOr.js +1 -0
- package/vendor/devspace/dist/ui/assets/css-DiQoSjDH.js +1 -0
- package/vendor/devspace/dist/ui/assets/csv-Dx-8-gkx.js +1 -0
- package/vendor/devspace/dist/ui/assets/cue-CE9AQfxI.js +1 -0
- package/vendor/devspace/dist/ui/assets/cypher-ClKdZ_lG.js +1 -0
- package/vendor/devspace/dist/ui/assets/d-qD-0Kul2.js +1 -0
- package/vendor/devspace/dist/ui/assets/dark-plus-Cs2F2srj.js +1 -0
- package/vendor/devspace/dist/ui/assets/dart-DkHntEIa.js +1 -0
- package/vendor/devspace/dist/ui/assets/dax-BkyTk9wS.js +1 -0
- package/vendor/devspace/dist/ui/assets/desktop-Dlh5hvp9.js +1 -0
- package/vendor/devspace/dist/ui/assets/diff-woXpYk--.js +1 -0
- package/vendor/devspace/dist/ui/assets/docker-IyjqRm3v.js +1 -0
- package/vendor/devspace/dist/ui/assets/dotenv-_5a1GRtc.js +1 -0
- package/vendor/devspace/dist/ui/assets/dracula-BHWKrbxM.js +1 -0
- package/vendor/devspace/dist/ui/assets/dracula-soft-5eyTD99u.js +1 -0
- package/vendor/devspace/dist/ui/assets/dream-maker-DW3nJb8Q.js +1 -0
- package/vendor/devspace/dist/ui/assets/edge-wEhCUoEt.js +1 -0
- package/vendor/devspace/dist/ui/assets/elixir-C3obhm0H.js +1 -0
- package/vendor/devspace/dist/ui/assets/elm-DyTBfh1L.js +1 -0
- package/vendor/devspace/dist/ui/assets/emacs-lisp-C9PiwqqW.js +1 -0
- package/vendor/devspace/dist/ui/assets/erb-f4TnXRuY.js +1 -0
- package/vendor/devspace/dist/ui/assets/erlang-Cphh6RMH.js +1 -0
- package/vendor/devspace/dist/ui/assets/everforest-dark-sB-x3p7T.js +1 -0
- package/vendor/devspace/dist/ui/assets/everforest-light-Df2xbC6M.js +1 -0
- package/vendor/devspace/dist/ui/assets/fennel-DQxkIbk2.js +1 -0
- package/vendor/devspace/dist/ui/assets/fish-BJitypiv.js +1 -0
- package/vendor/devspace/dist/ui/assets/fluent-C03EYrpw.js +1 -0
- package/vendor/devspace/dist/ui/assets/fortran-fixed-form-DEKoE2YW.js +1 -0
- package/vendor/devspace/dist/ui/assets/fortran-free-form-CYNrtFtB.js +1 -0
- package/vendor/devspace/dist/ui/assets/fsharp-D13ZGOAj.js +1 -0
- package/vendor/devspace/dist/ui/assets/gdresource-C0sCabJj.js +1 -0
- package/vendor/devspace/dist/ui/assets/gdscript-Cp2uCuqX.js +1 -0
- package/vendor/devspace/dist/ui/assets/gdshader-CBce3t8t.js +1 -0
- package/vendor/devspace/dist/ui/assets/genie-CV2tkWYe.js +1 -0
- package/vendor/devspace/dist/ui/assets/gherkin-DExj1W_8.js +1 -0
- package/vendor/devspace/dist/ui/assets/git-commit-BSykSTBG.js +1 -0
- package/vendor/devspace/dist/ui/assets/git-rebase-vhZbWNfp.js +1 -0
- package/vendor/devspace/dist/ui/assets/github-dark-C-LZuMrd.js +1 -0
- package/vendor/devspace/dist/ui/assets/github-dark-default-DXG-b-1a.js +1 -0
- package/vendor/devspace/dist/ui/assets/github-dark-dimmed-Bx1FflLF.js +1 -0
- package/vendor/devspace/dist/ui/assets/github-dark-high-contrast-B_tTalzw.js +1 -0
- package/vendor/devspace/dist/ui/assets/github-light-EUqPIrTm.js +1 -0
- package/vendor/devspace/dist/ui/assets/github-light-default-BXViO-2h.js +1 -0
- package/vendor/devspace/dist/ui/assets/github-light-high-contrast-B68TUdTA.js +1 -0
- package/vendor/devspace/dist/ui/assets/gleam-CSRkHgEL.js +1 -0
- package/vendor/devspace/dist/ui/assets/glimmer-js-BG7puL6Y.js +1 -0
- package/vendor/devspace/dist/ui/assets/glimmer-ts-CNCfLLL1.js +1 -0
- package/vendor/devspace/dist/ui/assets/glsl-DhT76AL5.js +1 -0
- package/vendor/devspace/dist/ui/assets/gn-ilITqXS6.js +1 -0
- package/vendor/devspace/dist/ui/assets/gnuplot-7GGW24-e.js +1 -0
- package/vendor/devspace/dist/ui/assets/go-BJwz_mda.js +1 -0
- package/vendor/devspace/dist/ui/assets/graphql-CJX08w8y.js +1 -0
- package/vendor/devspace/dist/ui/assets/groovy-CacY0gHj.js +1 -0
- package/vendor/devspace/dist/ui/assets/gruvbox-dark-hard-C820rvS2.js +1 -0
- package/vendor/devspace/dist/ui/assets/gruvbox-dark-medium-BPjhmG05.js +1 -0
- package/vendor/devspace/dist/ui/assets/gruvbox-dark-soft-MrdJrrXF.js +1 -0
- package/vendor/devspace/dist/ui/assets/gruvbox-light-hard-BC_s9l72.js +1 -0
- package/vendor/devspace/dist/ui/assets/gruvbox-light-medium-BAWPOn9u.js +1 -0
- package/vendor/devspace/dist/ui/assets/gruvbox-light-soft-BSMLrYjP.js +1 -0
- package/vendor/devspace/dist/ui/assets/hack-DY4NGCCL.js +1 -0
- package/vendor/devspace/dist/ui/assets/haml-DwSkmVcG.js +1 -0
- package/vendor/devspace/dist/ui/assets/handlebars-CQ_1dEmi.js +1 -0
- package/vendor/devspace/dist/ui/assets/haskell-D8IpX4py.js +1 -0
- package/vendor/devspace/dist/ui/assets/haxe-OTjmBuCE.js +1 -0
- package/vendor/devspace/dist/ui/assets/hcl-Dh228itO.js +1 -0
- package/vendor/devspace/dist/ui/assets/heavy-payload-2rGBiitA.js +4 -0
- package/vendor/devspace/dist/ui/assets/hjson-CxZEssPk.js +1 -0
- package/vendor/devspace/dist/ui/assets/hlsl-Cvrh5tZx.js +1 -0
- package/vendor/devspace/dist/ui/assets/horizon-CE9ld1lL.js +1 -0
- package/vendor/devspace/dist/ui/assets/horizon-bright-Br1oVSNq.js +1 -0
- package/vendor/devspace/dist/ui/assets/houston-CsvMBhTu.js +1 -0
- package/vendor/devspace/dist/ui/assets/html-B_e3Njif.js +1 -0
- package/vendor/devspace/dist/ui/assets/html-derivative-_nHZ_Bk-.js +1 -0
- package/vendor/devspace/dist/ui/assets/http-CfcvPIru.js +1 -0
- package/vendor/devspace/dist/ui/assets/hurl-9vWDQIMO.js +1 -0
- package/vendor/devspace/dist/ui/assets/hxml-B0Qn7Nwc.js +1 -0
- package/vendor/devspace/dist/ui/assets/hy-CZbG8q4J.js +1 -0
- package/vendor/devspace/dist/ui/assets/imba-DsUTQ-LC.js +1 -0
- package/vendor/devspace/dist/ui/assets/ini-B5eOa1yu.js +1 -0
- package/vendor/devspace/dist/ui/assets/java-C4S5r6L5.js +1 -0
- package/vendor/devspace/dist/ui/assets/javascript-BRwqbMMC.js +1 -0
- package/vendor/devspace/dist/ui/assets/jinja-DHxzMMc5.js +1 -0
- package/vendor/devspace/dist/ui/assets/jison-B1DqeWXb.js +1 -0
- package/vendor/devspace/dist/ui/assets/json-DcFTkEdd.js +1 -0
- package/vendor/devspace/dist/ui/assets/json5-BR5RXkoi.js +1 -0
- package/vendor/devspace/dist/ui/assets/jsonc-CYpm1nAK.js +1 -0
- package/vendor/devspace/dist/ui/assets/jsonl-CmCQp5Yx.js +1 -0
- package/vendor/devspace/dist/ui/assets/jsonnet-CJTPZ8u_.js +1 -0
- package/vendor/devspace/dist/ui/assets/jssm-DXw9l8Rf.js +1 -0
- package/vendor/devspace/dist/ui/assets/jsx-DgfmYa9f.js +1 -0
- package/vendor/devspace/dist/ui/assets/julia-3M8SJ8iF.js +1 -0
- package/vendor/devspace/dist/ui/assets/just-UyH7_DNz.js +1 -0
- package/vendor/devspace/dist/ui/assets/kanagawa-dragon-CXtmUGW6.js +1 -0
- package/vendor/devspace/dist/ui/assets/kanagawa-lotus-BN08jTvb.js +1 -0
- package/vendor/devspace/dist/ui/assets/kanagawa-wave-CTweb8Dz.js +1 -0
- package/vendor/devspace/dist/ui/assets/kdl-CsD5j6eV.js +1 -0
- package/vendor/devspace/dist/ui/assets/kotlin-DhhofPvG.js +1 -0
- package/vendor/devspace/dist/ui/assets/kusto-BUv0MjJC.js +1 -0
- package/vendor/devspace/dist/ui/assets/laserwave-C_8bwKvT.js +1 -0
- package/vendor/devspace/dist/ui/assets/latex-wUbha6EX.js +1 -0
- package/vendor/devspace/dist/ui/assets/lean-CewbzKMR.js +1 -0
- package/vendor/devspace/dist/ui/assets/less-DVTAwKKz.js +1 -0
- package/vendor/devspace/dist/ui/assets/light-plus-DVQuIRkW.js +1 -0
- package/vendor/devspace/dist/ui/assets/liquid-DqNTB9kN.js +1 -0
- package/vendor/devspace/dist/ui/assets/llvm-Cm23YOpf.js +1 -0
- package/vendor/devspace/dist/ui/assets/log-BNLmms1o.js +1 -0
- package/vendor/devspace/dist/ui/assets/logo-Cluzi2Zq.js +1 -0
- package/vendor/devspace/dist/ui/assets/lua-DrEQ1QTW.js +1 -0
- package/vendor/devspace/dist/ui/assets/luau-CNKltnaQ.js +1 -0
- package/vendor/devspace/dist/ui/assets/make-Dixweg8N.js +1 -0
- package/vendor/devspace/dist/ui/assets/markdown-BYOwaDjH.js +1 -0
- package/vendor/devspace/dist/ui/assets/marko-B3GJw9Md.js +1 -0
- package/vendor/devspace/dist/ui/assets/material-theme-Bm3Qr25_.js +1 -0
- package/vendor/devspace/dist/ui/assets/material-theme-darker-2IIEA8gg.js +1 -0
- package/vendor/devspace/dist/ui/assets/material-theme-lighter-uhdI0v04.js +1 -0
- package/vendor/devspace/dist/ui/assets/material-theme-ocean-CHQ94UKr.js +1 -0
- package/vendor/devspace/dist/ui/assets/material-theme-palenight-B5W6OYN7.js +1 -0
- package/vendor/devspace/dist/ui/assets/matlab-D7qyCx1q.js +1 -0
- package/vendor/devspace/dist/ui/assets/mdc-BnThk82m.js +1 -0
- package/vendor/devspace/dist/ui/assets/mdx-DQZ5AkYe.js +1 -0
- package/vendor/devspace/dist/ui/assets/mermaid-Bk4SNUv9.js +1 -0
- package/vendor/devspace/dist/ui/assets/min-dark-BSWPekZh.js +1 -0
- package/vendor/devspace/dist/ui/assets/min-light-DDpmG2fV.js +1 -0
- package/vendor/devspace/dist/ui/assets/mipsasm-BMqwQI7S.js +1 -0
- package/vendor/devspace/dist/ui/assets/mojo-BgCJLMeH.js +1 -0
- package/vendor/devspace/dist/ui/assets/monokai-CdkpiU2Y.js +1 -0
- package/vendor/devspace/dist/ui/assets/moonbit-CaWjb8XO.js +1 -0
- package/vendor/devspace/dist/ui/assets/move-B1IS1UjX.js +1 -0
- package/vendor/devspace/dist/ui/assets/narrat-_X_XdTYD.js +1 -0
- package/vendor/devspace/dist/ui/assets/nextflow-Bbiyy34d.js +1 -0
- package/vendor/devspace/dist/ui/assets/nextflow-groovy-Dc_ddanL.js +1 -0
- package/vendor/devspace/dist/ui/assets/nginx-B5IeSsOX.js +1 -0
- package/vendor/devspace/dist/ui/assets/night-owl-DhmEMT88.js +1 -0
- package/vendor/devspace/dist/ui/assets/night-owl-light-eJ-hLW7d.js +1 -0
- package/vendor/devspace/dist/ui/assets/nim-BJraatnS.js +1 -0
- package/vendor/devspace/dist/ui/assets/nix-IvuFDN5E.js +1 -0
- package/vendor/devspace/dist/ui/assets/nord-Cb4Vim4T.js +1 -0
- package/vendor/devspace/dist/ui/assets/nushell-DcLAeLz5.js +1 -0
- package/vendor/devspace/dist/ui/assets/objective-c-D1A_Heim.js +1 -0
- package/vendor/devspace/dist/ui/assets/objective-cpp-BsSzOQcm.js +1 -0
- package/vendor/devspace/dist/ui/assets/ocaml-O90oeIOV.js +1 -0
- package/vendor/devspace/dist/ui/assets/odin-B1RWQWA5.js +1 -0
- package/vendor/devspace/dist/ui/assets/one-dark-pro-CLwyXe_n.js +1 -0
- package/vendor/devspace/dist/ui/assets/one-light-D7Lr4KcI.js +1 -0
- package/vendor/devspace/dist/ui/assets/openscad-BUDT5pXO.js +1 -0
- package/vendor/devspace/dist/ui/assets/pascal-4ZHwLPI5.js +1 -0
- package/vendor/devspace/dist/ui/assets/perl-BAhrR4gt.js +1 -0
- package/vendor/devspace/dist/ui/assets/php-BKRCk_wX.js +1 -0
- package/vendor/devspace/dist/ui/assets/pierre-dark-sU4Zdns8.js +1 -0
- package/vendor/devspace/dist/ui/assets/pierre-dark-soft-HXgun5vs.js +1 -0
- package/vendor/devspace/dist/ui/assets/pierre-light-DWBk51d8.js +1 -0
- package/vendor/devspace/dist/ui/assets/pierre-light-soft-CodEzPxf.js +1 -0
- package/vendor/devspace/dist/ui/assets/pkl-ot-7Btpt.js +1 -0
- package/vendor/devspace/dist/ui/assets/plastic-DQwYfKfQ.js +1 -0
- package/vendor/devspace/dist/ui/assets/plsql-DGHpHOYJ.js +1 -0
- package/vendor/devspace/dist/ui/assets/po-BiJDBrnU.js +1 -0
- package/vendor/devspace/dist/ui/assets/poimandres-DRFjx7u4.js +1 -0
- package/vendor/devspace/dist/ui/assets/polar-C7UOKdEL.js +1 -0
- package/vendor/devspace/dist/ui/assets/postcss-BXeXVLqQ.js +1 -0
- package/vendor/devspace/dist/ui/assets/powerquery-DNMTfnFr.js +1 -0
- package/vendor/devspace/dist/ui/assets/powershell-DshXNtvi.js +1 -0
- package/vendor/devspace/dist/ui/assets/prisma-BsRQq5mF.js +1 -0
- package/vendor/devspace/dist/ui/assets/prolog-iXnhIJG7.js +1 -0
- package/vendor/devspace/dist/ui/assets/proto-DB4EqR-F.js +1 -0
- package/vendor/devspace/dist/ui/assets/pug-B8gmSV7z.js +1 -0
- package/vendor/devspace/dist/ui/assets/puppet-CDv2pdJW.js +1 -0
- package/vendor/devspace/dist/ui/assets/purescript-9MfHhQsQ.js +1 -0
- package/vendor/devspace/dist/ui/assets/python-gzcpVVnB.js +1 -0
- package/vendor/devspace/dist/ui/assets/qml-DKrxIjHR.js +1 -0
- package/vendor/devspace/dist/ui/assets/qmldir-DCQb3MpD.js +1 -0
- package/vendor/devspace/dist/ui/assets/qss-Fe1Jh2GI.js +1 -0
- package/vendor/devspace/dist/ui/assets/r-YASBZvTH.js +1 -0
- package/vendor/devspace/dist/ui/assets/racket-DcIDlBhZ.js +1 -0
- package/vendor/devspace/dist/ui/assets/raku-B3gFvitq.js +1 -0
- package/vendor/devspace/dist/ui/assets/razor-P-XFZqh6.js +1 -0
- package/vendor/devspace/dist/ui/assets/red-CJ3rzSJv.js +1 -0
- package/vendor/devspace/dist/ui/assets/reg-CRGYupPL.js +1 -0
- package/vendor/devspace/dist/ui/assets/regexp-CCpiH6nO.js +1 -0
- package/vendor/devspace/dist/ui/assets/rel-BtDbiS_P.js +1 -0
- package/vendor/devspace/dist/ui/assets/review-payload-BLYwg6t8.js +1 -0
- package/vendor/devspace/dist/ui/assets/riscv-Ckw8ddFX.js +1 -0
- package/vendor/devspace/dist/ui/assets/ron-VUp2lXgN.js +1 -0
- package/vendor/devspace/dist/ui/assets/rose-pine-BthvhNj6.js +1 -0
- package/vendor/devspace/dist/ui/assets/rose-pine-dawn-Dg85fqjY.js +1 -0
- package/vendor/devspace/dist/ui/assets/rose-pine-moon-hon4tzzS.js +1 -0
- package/vendor/devspace/dist/ui/assets/rosmsg-CAekHB0j.js +1 -0
- package/vendor/devspace/dist/ui/assets/rst-CFAzckbn.js +1 -0
- package/vendor/devspace/dist/ui/assets/ruby-Dyp_Rx-S.js +1 -0
- package/vendor/devspace/dist/ui/assets/rust-Cfkwpbl8.js +1 -0
- package/vendor/devspace/dist/ui/assets/sas-YkWZIXxT.js +1 -0
- package/vendor/devspace/dist/ui/assets/sass-DXrisJhu.js +1 -0
- package/vendor/devspace/dist/ui/assets/scala-DKOlJaKm.js +1 -0
- package/vendor/devspace/dist/ui/assets/scheme-DQCgrYNe.js +1 -0
- package/vendor/devspace/dist/ui/assets/scss-Cf89idGl.js +1 -0
- package/vendor/devspace/dist/ui/assets/sdbl-bTVj8UrX.js +1 -0
- package/vendor/devspace/dist/ui/assets/shaderlab-TOUzSsQk.js +1 -0
- package/vendor/devspace/dist/ui/assets/shellscript-BFMb52q8.js +1 -0
- package/vendor/devspace/dist/ui/assets/shellsession-DuthW-5v.js +1 -0
- package/vendor/devspace/dist/ui/assets/slack-dark-DnToyrRv.js +1 -0
- package/vendor/devspace/dist/ui/assets/slack-ochin-B2OO5cIa.js +1 -0
- package/vendor/devspace/dist/ui/assets/smalltalk-B16xEiuN.js +1 -0
- package/vendor/devspace/dist/ui/assets/snazzy-light-4G7pJPwS.js +1 -0
- package/vendor/devspace/dist/ui/assets/solarized-dark-DV17i1UV.js +1 -0
- package/vendor/devspace/dist/ui/assets/solarized-light-DSh2HLQt.js +1 -0
- package/vendor/devspace/dist/ui/assets/solidity-CKzVLygQ.js +1 -0
- package/vendor/devspace/dist/ui/assets/soy-BohVVlJU.js +1 -0
- package/vendor/devspace/dist/ui/assets/sparql-D_iOobhT.js +1 -0
- package/vendor/devspace/dist/ui/assets/splunk-BC2Px7Mm.js +1 -0
- package/vendor/devspace/dist/ui/assets/sql-CnK9R2Gt.js +1 -0
- package/vendor/devspace/dist/ui/assets/ssh-config-BgfXC-Er.js +1 -0
- package/vendor/devspace/dist/ui/assets/stata-D-sV4Vbr.js +1 -0
- package/vendor/devspace/dist/ui/assets/stylus-B6D30XZt.js +1 -0
- package/vendor/devspace/dist/ui/assets/surrealql-ChAy0gHv.js +1 -0
- package/vendor/devspace/dist/ui/assets/svelte-3HBVqG-H.js +1 -0
- package/vendor/devspace/dist/ui/assets/swift-DonLKvLd.js +1 -0
- package/vendor/devspace/dist/ui/assets/synthwave-84-nFMaYfgc.js +1 -0
- package/vendor/devspace/dist/ui/assets/system-verilog-DJ5XKQeo.js +1 -0
- package/vendor/devspace/dist/ui/assets/systemd-BxMlprV5.js +1 -0
- package/vendor/devspace/dist/ui/assets/talonscript-CohzipZa.js +1 -0
- package/vendor/devspace/dist/ui/assets/tasl-DMoTqEGO.js +1 -0
- package/vendor/devspace/dist/ui/assets/tcl-CZd0xW_V.js +1 -0
- package/vendor/devspace/dist/ui/assets/templ-BixlV4Cp.js +1 -0
- package/vendor/devspace/dist/ui/assets/terraform-DswuEJGm.js +1 -0
- package/vendor/devspace/dist/ui/assets/tex-DWoJKHvD.js +1 -0
- package/vendor/devspace/dist/ui/assets/tokyo-night-oM2G3aXe.js +1 -0
- package/vendor/devspace/dist/ui/assets/toml-CcmNWLt0.js +1 -0
- package/vendor/devspace/dist/ui/assets/ts-tags-DLA8eH13.js +1 -0
- package/vendor/devspace/dist/ui/assets/tsv-sltzmVWM.js +1 -0
- package/vendor/devspace/dist/ui/assets/tsx-BYWa5JUz.js +1 -0
- package/vendor/devspace/dist/ui/assets/turtle-ByJddavk.js +1 -0
- package/vendor/devspace/dist/ui/assets/twig-CnY65vAo.js +1 -0
- package/vendor/devspace/dist/ui/assets/typescript-ByInUsro.js +1 -0
- package/vendor/devspace/dist/ui/assets/typespec-B88KGewJ.js +1 -0
- package/vendor/devspace/dist/ui/assets/typst-DI99ib-x.js +1 -0
- package/vendor/devspace/dist/ui/assets/useFileDiffInstance-Dg0pWcJV.js +244 -0
- package/vendor/devspace/dist/ui/assets/v-DETTlOr0.js +1 -0
- package/vendor/devspace/dist/ui/assets/vala-zf12oZj6.js +1 -0
- package/vendor/devspace/dist/ui/assets/vb-Djn5o6TS.js +1 -0
- package/vendor/devspace/dist/ui/assets/verilog-CiiDBU1e.js +1 -0
- package/vendor/devspace/dist/ui/assets/vesper-D5bVUKB1.js +1 -0
- package/vendor/devspace/dist/ui/assets/vhdl-BroJfC0k.js +1 -0
- package/vendor/devspace/dist/ui/assets/viml-DvXPmvsu.js +1 -0
- package/vendor/devspace/dist/ui/assets/vitesse-black-fwtXNY1n.js +1 -0
- package/vendor/devspace/dist/ui/assets/vitesse-dark-BZCL-v6S.js +1 -0
- package/vendor/devspace/dist/ui/assets/vitesse-light-VbXTXTou.js +1 -0
- package/vendor/devspace/dist/ui/assets/vue-Qngt2rkz.js +1 -0
- package/vendor/devspace/dist/ui/assets/vue-html-DDue9upr.js +1 -0
- package/vendor/devspace/dist/ui/assets/vue-vine-DBcAKsA6.js +1 -0
- package/vendor/devspace/dist/ui/assets/vyper-CgoNMtux.js +1 -0
- package/vendor/devspace/dist/ui/assets/wasm-BnjxR4X6.js +1 -0
- package/vendor/devspace/dist/ui/assets/wasm-ByWQv1Qj.js +1 -0
- package/vendor/devspace/dist/ui/assets/wenyan-C8pVoKbM.js +1 -0
- package/vendor/devspace/dist/ui/assets/wgsl-BsKzXJz4.js +1 -0
- package/vendor/devspace/dist/ui/assets/wikitext-ClFFjSW2.js +1 -0
- package/vendor/devspace/dist/ui/assets/wit-DdvCle-K.js +1 -0
- package/vendor/devspace/dist/ui/assets/wolfram-DLL8P-h_.js +1 -0
- package/vendor/devspace/dist/ui/assets/workspace-app-DGD9R89D.css +1 -0
- package/vendor/devspace/dist/ui/assets/workspace-app-DuzpU4Hf.js +118 -0
- package/vendor/devspace/dist/ui/assets/xml-CRLR5jiL.js +1 -0
- package/vendor/devspace/dist/ui/assets/xsl-CmcwyXbm.js +1 -0
- package/vendor/devspace/dist/ui/assets/yaml-Dpe6Eg-k.js +1 -0
- package/vendor/devspace/dist/ui/assets/zenscript-BnlCZFoB.js +1 -0
- package/vendor/devspace/dist/ui/assets/zig-CMLA9XwU.js +1 -0
- package/vendor/devspace/dist/ui/workspace-app.html +15 -0
- package/vendor/devspace/dist/user-config.js +57 -0
- package/vendor/devspace/dist/workspace-store.js +75 -0
- package/vendor/devspace/dist/workspaces.js +226 -0
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
const migrations = [
|
|
2
|
+
{
|
|
3
|
+
version: 1,
|
|
4
|
+
name: "workspace-state",
|
|
5
|
+
up: migrateWorkspaceState,
|
|
6
|
+
},
|
|
7
|
+
{
|
|
8
|
+
version: 2,
|
|
9
|
+
name: "oauth-state",
|
|
10
|
+
up: migrateOAuthState,
|
|
11
|
+
},
|
|
12
|
+
];
|
|
13
|
+
export function migrateDatabase(sqlite) {
|
|
14
|
+
const migrate = sqlite.transaction(() => {
|
|
15
|
+
sqlite.exec(`
|
|
16
|
+
create table if not exists devspace_schema_migrations (
|
|
17
|
+
version integer primary key,
|
|
18
|
+
name text not null,
|
|
19
|
+
applied_at text not null
|
|
20
|
+
);
|
|
21
|
+
`);
|
|
22
|
+
const applied = new Set(sqlite.prepare("select version from devspace_schema_migrations").all().map((row) => row.version));
|
|
23
|
+
const recordMigration = sqlite.prepare("insert into devspace_schema_migrations (version, name, applied_at) values (?, ?, ?)");
|
|
24
|
+
for (const migration of migrations) {
|
|
25
|
+
if (applied.has(migration.version))
|
|
26
|
+
continue;
|
|
27
|
+
migration.up(sqlite);
|
|
28
|
+
recordMigration.run(migration.version, migration.name, new Date().toISOString());
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
migrate.immediate();
|
|
32
|
+
}
|
|
33
|
+
function migrateWorkspaceState(sqlite) {
|
|
34
|
+
sqlite.exec(`
|
|
35
|
+
create table if not exists workspace_sessions (
|
|
36
|
+
id text primary key,
|
|
37
|
+
root text not null,
|
|
38
|
+
status text not null default 'active',
|
|
39
|
+
mode text not null default 'checkout',
|
|
40
|
+
source_root text,
|
|
41
|
+
base_ref text,
|
|
42
|
+
base_sha text,
|
|
43
|
+
managed text not null default 'false',
|
|
44
|
+
created_at text not null,
|
|
45
|
+
last_used_at text not null
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
create index if not exists workspace_sessions_root_idx
|
|
49
|
+
on workspace_sessions(root, last_used_at desc);
|
|
50
|
+
|
|
51
|
+
create index if not exists workspace_sessions_status_idx
|
|
52
|
+
on workspace_sessions(status, last_used_at desc);
|
|
53
|
+
|
|
54
|
+
create table if not exists loaded_agent_files (
|
|
55
|
+
workspace_session_id text not null,
|
|
56
|
+
path text not null,
|
|
57
|
+
content_hash text not null,
|
|
58
|
+
content text not null,
|
|
59
|
+
loaded_at text not null,
|
|
60
|
+
last_seen_at text not null,
|
|
61
|
+
primary key (workspace_session_id, path),
|
|
62
|
+
foreign key (workspace_session_id)
|
|
63
|
+
references workspace_sessions(id)
|
|
64
|
+
on delete cascade
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
create index if not exists loaded_agent_files_path_idx
|
|
68
|
+
on loaded_agent_files(path);
|
|
69
|
+
`);
|
|
70
|
+
addColumnIfMissing(sqlite, "workspace_sessions", "mode", "text not null default 'checkout'");
|
|
71
|
+
addColumnIfMissing(sqlite, "workspace_sessions", "source_root", "text");
|
|
72
|
+
addColumnIfMissing(sqlite, "workspace_sessions", "base_ref", "text");
|
|
73
|
+
addColumnIfMissing(sqlite, "workspace_sessions", "base_sha", "text");
|
|
74
|
+
addColumnIfMissing(sqlite, "workspace_sessions", "managed", "text not null default 'false'");
|
|
75
|
+
}
|
|
76
|
+
function migrateOAuthState(sqlite) {
|
|
77
|
+
sqlite.exec(`
|
|
78
|
+
create table if not exists oauth_clients (
|
|
79
|
+
client_id text primary key,
|
|
80
|
+
client_json text not null,
|
|
81
|
+
issued_at integer not null
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
create index if not exists oauth_clients_issued_at_idx
|
|
85
|
+
on oauth_clients(issued_at desc);
|
|
86
|
+
|
|
87
|
+
create table if not exists oauth_access_tokens (
|
|
88
|
+
token_hash text primary key,
|
|
89
|
+
client_id text not null,
|
|
90
|
+
scopes_json text not null,
|
|
91
|
+
expires_at integer not null,
|
|
92
|
+
resource text,
|
|
93
|
+
foreign key (client_id) references oauth_clients(client_id) on delete cascade
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
create index if not exists oauth_access_tokens_client_id_idx
|
|
97
|
+
on oauth_access_tokens(client_id);
|
|
98
|
+
|
|
99
|
+
create index if not exists oauth_access_tokens_expires_at_idx
|
|
100
|
+
on oauth_access_tokens(expires_at);
|
|
101
|
+
|
|
102
|
+
create table if not exists oauth_refresh_tokens (
|
|
103
|
+
token_hash text primary key,
|
|
104
|
+
client_id text not null,
|
|
105
|
+
scopes_json text not null,
|
|
106
|
+
expires_at integer not null,
|
|
107
|
+
resource text,
|
|
108
|
+
foreign key (client_id) references oauth_clients(client_id) on delete cascade
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
create index if not exists oauth_refresh_tokens_client_id_idx
|
|
112
|
+
on oauth_refresh_tokens(client_id);
|
|
113
|
+
|
|
114
|
+
create index if not exists oauth_refresh_tokens_expires_at_idx
|
|
115
|
+
on oauth_refresh_tokens(expires_at);
|
|
116
|
+
`);
|
|
117
|
+
}
|
|
118
|
+
function addColumnIfMissing(sqlite, table, column, definition) {
|
|
119
|
+
const columns = sqlite.prepare(`pragma table_info(${table})`).all();
|
|
120
|
+
if (columns.some((existingColumn) => existingColumn.name === column))
|
|
121
|
+
return;
|
|
122
|
+
sqlite.exec(`alter table ${table} add column ${column} ${definition}`);
|
|
123
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { index, integer, primaryKey, sqliteTable, text } from "drizzle-orm/sqlite-core";
|
|
2
|
+
export const workspaceSessions = sqliteTable("workspace_sessions", {
|
|
3
|
+
id: text("id").primaryKey(),
|
|
4
|
+
root: text("root").notNull(),
|
|
5
|
+
status: text("status").notNull().default("active"),
|
|
6
|
+
mode: text("mode").notNull().default("checkout"),
|
|
7
|
+
sourceRoot: text("source_root"),
|
|
8
|
+
baseRef: text("base_ref"),
|
|
9
|
+
baseSha: text("base_sha"),
|
|
10
|
+
managed: text("managed").notNull().default("false"),
|
|
11
|
+
createdAt: text("created_at").notNull(),
|
|
12
|
+
lastUsedAt: text("last_used_at").notNull(),
|
|
13
|
+
}, (table) => [
|
|
14
|
+
index("workspace_sessions_root_idx").on(table.root, table.lastUsedAt),
|
|
15
|
+
index("workspace_sessions_status_idx").on(table.status, table.lastUsedAt),
|
|
16
|
+
]);
|
|
17
|
+
export const loadedAgentFiles = sqliteTable("loaded_agent_files", {
|
|
18
|
+
workspaceSessionId: text("workspace_session_id")
|
|
19
|
+
.notNull()
|
|
20
|
+
.references(() => workspaceSessions.id, { onDelete: "cascade" }),
|
|
21
|
+
path: text("path").notNull(),
|
|
22
|
+
contentHash: text("content_hash").notNull(),
|
|
23
|
+
content: text("content").notNull(),
|
|
24
|
+
loadedAt: text("loaded_at").notNull(),
|
|
25
|
+
lastSeenAt: text("last_seen_at").notNull(),
|
|
26
|
+
}, (table) => [
|
|
27
|
+
primaryKey({ columns: [table.workspaceSessionId, table.path] }),
|
|
28
|
+
index("loaded_agent_files_path_idx").on(table.path),
|
|
29
|
+
]);
|
|
30
|
+
export const oauthClients = sqliteTable("oauth_clients", {
|
|
31
|
+
clientId: text("client_id").primaryKey(),
|
|
32
|
+
clientJson: text("client_json").notNull(),
|
|
33
|
+
issuedAt: integer("issued_at").notNull(),
|
|
34
|
+
});
|
|
35
|
+
export const oauthAccessTokens = sqliteTable("oauth_access_tokens", {
|
|
36
|
+
tokenHash: text("token_hash").primaryKey(),
|
|
37
|
+
clientId: text("client_id")
|
|
38
|
+
.notNull()
|
|
39
|
+
.references(() => oauthClients.clientId, { onDelete: "cascade" }),
|
|
40
|
+
scopesJson: text("scopes_json").notNull(),
|
|
41
|
+
expiresAt: integer("expires_at").notNull(),
|
|
42
|
+
resource: text("resource"),
|
|
43
|
+
});
|
|
44
|
+
export const oauthRefreshTokens = sqliteTable("oauth_refresh_tokens", {
|
|
45
|
+
tokenHash: text("token_hash").primaryKey(),
|
|
46
|
+
clientId: text("client_id")
|
|
47
|
+
.notNull()
|
|
48
|
+
.references(() => oauthClients.clientId, { onDelete: "cascade" }),
|
|
49
|
+
scopesJson: text("scopes_json").notNull(),
|
|
50
|
+
expiresAt: integer("expires_at").notNull(),
|
|
51
|
+
resource: text("resource"),
|
|
52
|
+
});
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { randomBytes } from "node:crypto";
|
|
2
|
+
import { execFile } from "node:child_process";
|
|
3
|
+
import { promisify } from "node:util";
|
|
4
|
+
import { mkdir, realpath, rm, stat } from "node:fs/promises";
|
|
5
|
+
import { basename, join, relative, resolve } from "node:path";
|
|
6
|
+
import { assertAllowedPath, isPathInsideRoot } from "./roots.js";
|
|
7
|
+
const execFileAsync = promisify(execFile);
|
|
8
|
+
export class GitWorktreeError extends Error {
|
|
9
|
+
code;
|
|
10
|
+
constructor(code, message) {
|
|
11
|
+
super(message);
|
|
12
|
+
this.code = code;
|
|
13
|
+
this.name = "GitWorktreeError";
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
export async function createManagedWorktree(input) {
|
|
17
|
+
const sourcePath = assertAllowedPath(input.sourcePath, input.config.allowedRoots);
|
|
18
|
+
try {
|
|
19
|
+
const sourceStats = await stat(sourcePath);
|
|
20
|
+
if (!sourceStats.isDirectory()) {
|
|
21
|
+
throw new GitWorktreeError("GIT_REPOSITORY_NOT_FOUND", `Cannot open workspace in worktree mode because the source path is not a directory: ${input.sourcePath}`);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
if (error instanceof GitWorktreeError)
|
|
26
|
+
throw error;
|
|
27
|
+
throw new GitWorktreeError("GIT_REPOSITORY_NOT_FOUND", `Cannot open workspace in worktree mode because the source path does not exist: ${input.sourcePath}`);
|
|
28
|
+
}
|
|
29
|
+
const sourceRoot = await resolveGitRoot(sourcePath, input.config.allowedRoots);
|
|
30
|
+
const baseRef = input.baseRef ?? "HEAD";
|
|
31
|
+
const baseSha = await resolveBaseCommit(sourceRoot, baseRef);
|
|
32
|
+
const dirtySource = (await git(["status", "--porcelain=v1"], sourceRoot)).trim().length > 0;
|
|
33
|
+
const worktreePath = managedWorktreePath({
|
|
34
|
+
worktreeRoot: input.config.worktreeRoot,
|
|
35
|
+
repoRoot: sourceRoot,
|
|
36
|
+
});
|
|
37
|
+
await mkdir(input.config.worktreeRoot, { recursive: true });
|
|
38
|
+
assertAllowedPath(worktreePath, [input.config.worktreeRoot]);
|
|
39
|
+
try {
|
|
40
|
+
await git(["worktree", "add", "--detach", worktreePath, baseSha], sourceRoot);
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
await rm(worktreePath, { recursive: true, force: true });
|
|
44
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
45
|
+
throw new GitWorktreeError("GIT_WORKTREE_CREATE_FAILED", `Git failed to create the managed worktree. ${message}`);
|
|
46
|
+
}
|
|
47
|
+
return {
|
|
48
|
+
sourceRoot,
|
|
49
|
+
path: worktreePath,
|
|
50
|
+
baseRef,
|
|
51
|
+
baseSha,
|
|
52
|
+
dirtySource,
|
|
53
|
+
detached: true,
|
|
54
|
+
managed: true,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
async function resolveGitRoot(path, allowedRoots) {
|
|
58
|
+
try {
|
|
59
|
+
const output = await git(["rev-parse", "--show-toplevel"], path);
|
|
60
|
+
return await assertGitRootAllowed(output.trim(), allowedRoots);
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
if (isGitUnavailable(error)) {
|
|
64
|
+
throw new GitWorktreeError("GIT_NOT_AVAILABLE", "Cannot open workspace in worktree mode because Git is not available on this machine.");
|
|
65
|
+
}
|
|
66
|
+
throw new GitWorktreeError("GIT_REPOSITORY_NOT_FOUND", `Cannot open workspace in worktree mode because this path is not inside a Git repository: ${path}. Use mode=\"checkout\" to work directly in this directory, or initialize Git and create an initial commit first.`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
async function assertGitRootAllowed(gitRoot, allowedRoots) {
|
|
70
|
+
try {
|
|
71
|
+
return assertAllowedPath(gitRoot, allowedRoots);
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
const canonicalGitRoot = await realpath(gitRoot);
|
|
75
|
+
for (const allowedRoot of allowedRoots) {
|
|
76
|
+
const canonicalAllowedRoot = await realpath(allowedRoot).catch(() => undefined);
|
|
77
|
+
if (!canonicalAllowedRoot || !isPathInsideRoot(canonicalGitRoot, canonicalAllowedRoot)) {
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
const logicalGitRoot = resolve(allowedRoot, relative(canonicalAllowedRoot, canonicalGitRoot));
|
|
81
|
+
return assertAllowedPath(logicalGitRoot, allowedRoots);
|
|
82
|
+
}
|
|
83
|
+
return assertAllowedPath(canonicalGitRoot, allowedRoots);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
async function resolveBaseCommit(sourceRoot, baseRef) {
|
|
87
|
+
try {
|
|
88
|
+
return (await git(["rev-parse", "--verify", `${baseRef}^{commit}`], sourceRoot)).trim();
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
if (baseRef === "HEAD") {
|
|
92
|
+
throw new GitWorktreeError("GIT_REPOSITORY_HAS_NO_COMMITS", "Cannot open workspace in worktree mode because the repository has no commits yet. Create an initial commit first, or use mode=\"checkout\".");
|
|
93
|
+
}
|
|
94
|
+
throw new GitWorktreeError("GIT_INVALID_BASE_REF", `Cannot open workspace in worktree mode because baseRef ${JSON.stringify(baseRef)} does not resolve to a commit.`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
function managedWorktreePath(input) {
|
|
98
|
+
const repoName = sanitizePathSegment(basename(input.repoRoot)) || "repo";
|
|
99
|
+
const worktreeId = randomBytes(4).toString("hex");
|
|
100
|
+
return join(input.worktreeRoot, `${repoName}-${worktreeId}`);
|
|
101
|
+
}
|
|
102
|
+
function sanitizePathSegment(value) {
|
|
103
|
+
return value
|
|
104
|
+
.replace(/[^a-zA-Z0-9._-]+/g, "-")
|
|
105
|
+
.replace(/^-+|-+$/g, "")
|
|
106
|
+
.slice(0, 80);
|
|
107
|
+
}
|
|
108
|
+
async function git(args, cwd) {
|
|
109
|
+
try {
|
|
110
|
+
const { stdout } = await execFileAsync("git", args, {
|
|
111
|
+
cwd,
|
|
112
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
113
|
+
});
|
|
114
|
+
return stdout;
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
if (isGitUnavailable(error))
|
|
118
|
+
throw error;
|
|
119
|
+
const stderr = typeof error === "object" && error && "stderr" in error
|
|
120
|
+
? String(error.stderr ?? "").trim()
|
|
121
|
+
: "";
|
|
122
|
+
const stdout = typeof error === "object" && error && "stdout" in error
|
|
123
|
+
? String(error.stdout ?? "").trim()
|
|
124
|
+
: "";
|
|
125
|
+
const details = stderr || stdout || (error instanceof Error ? error.message : String(error));
|
|
126
|
+
throw new Error(details);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
function isGitUnavailable(error) {
|
|
130
|
+
return Boolean(typeof error === "object" &&
|
|
131
|
+
error &&
|
|
132
|
+
"code" in error &&
|
|
133
|
+
error.code === "ENOENT");
|
|
134
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { execFile } from "node:child_process";
|
|
3
|
+
import { promisify } from "node:util";
|
|
4
|
+
const execFileAsync = promisify(execFile);
|
|
5
|
+
export async function git(cwd, args, options = {}) {
|
|
6
|
+
const { stdout, stderr } = await execFileAsync("git", args, {
|
|
7
|
+
cwd,
|
|
8
|
+
env: options.env ? { ...process.env, ...options.env } : process.env,
|
|
9
|
+
maxBuffer: options.maxBuffer ?? 10 * 1024 * 1024,
|
|
10
|
+
});
|
|
11
|
+
return { stdout, stderr };
|
|
12
|
+
}
|
|
13
|
+
export async function getGitEligibility(cwd) {
|
|
14
|
+
try {
|
|
15
|
+
await git(cwd, ["rev-parse", "--is-inside-work-tree"]);
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return {
|
|
19
|
+
ok: false,
|
|
20
|
+
reason: "not_git",
|
|
21
|
+
message: "workspace is not inside a git repository",
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
const gitRoot = (await git(cwd, ["rev-parse", "--show-toplevel"])).stdout.trim();
|
|
25
|
+
try {
|
|
26
|
+
await git(gitRoot, ["rev-parse", "--verify", "--quiet", "HEAD^{commit}"]);
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return {
|
|
30
|
+
ok: false,
|
|
31
|
+
gitRoot,
|
|
32
|
+
reason: "no_head",
|
|
33
|
+
message: "repository has no HEAD commit",
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
return { ok: true, gitRoot };
|
|
37
|
+
}
|
|
38
|
+
export function safeWorkspaceRefSegment(workspaceId) {
|
|
39
|
+
const safe = workspaceId.replace(/[^A-Za-z0-9._-]/g, "-");
|
|
40
|
+
return safe.length > 0 ? safe : createHash("sha256").update(workspaceId).digest("hex").slice(0, 16);
|
|
41
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
const LEVEL_WEIGHT = {
|
|
2
|
+
silent: 0,
|
|
3
|
+
error: 1,
|
|
4
|
+
warn: 2,
|
|
5
|
+
info: 3,
|
|
6
|
+
debug: 4,
|
|
7
|
+
};
|
|
8
|
+
export function shouldLog(config, level) {
|
|
9
|
+
return LEVEL_WEIGHT[config.level] >= LEVEL_WEIGHT[level];
|
|
10
|
+
}
|
|
11
|
+
export function logEvent(config, level, event, fields = {}) {
|
|
12
|
+
if (!shouldLog(config, level))
|
|
13
|
+
return;
|
|
14
|
+
const entry = {
|
|
15
|
+
ts: new Date().toISOString(),
|
|
16
|
+
level,
|
|
17
|
+
event,
|
|
18
|
+
...fields,
|
|
19
|
+
};
|
|
20
|
+
const line = config.format === "pretty" ? formatPretty(entry) : JSON.stringify(entry);
|
|
21
|
+
if (level === "error") {
|
|
22
|
+
console.error(line);
|
|
23
|
+
}
|
|
24
|
+
else if (level === "warn") {
|
|
25
|
+
console.warn(line);
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
console.log(line);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
export function requestIp(req, trustProxy) {
|
|
32
|
+
if (trustProxy) {
|
|
33
|
+
const cfConnectingIp = firstHeaderValue(req.header("cf-connecting-ip"));
|
|
34
|
+
if (cfConnectingIp)
|
|
35
|
+
return cfConnectingIp;
|
|
36
|
+
const forwardedFor = firstHeaderValue(req.header("x-forwarded-for"));
|
|
37
|
+
if (forwardedFor)
|
|
38
|
+
return forwardedFor;
|
|
39
|
+
}
|
|
40
|
+
return req.ip ?? req.socket.remoteAddress;
|
|
41
|
+
}
|
|
42
|
+
export function requestPath(req) {
|
|
43
|
+
return req.path || req.url.split("?")[0] || req.url;
|
|
44
|
+
}
|
|
45
|
+
export function sessionIdPrefix(sessionId) {
|
|
46
|
+
return sessionId ? sessionId.slice(0, 8) : undefined;
|
|
47
|
+
}
|
|
48
|
+
export function commandPreview(command) {
|
|
49
|
+
const normalized = command.replace(/\s+/g, " ").trim();
|
|
50
|
+
return normalized.length > 120 ? `${normalized.slice(0, 117)}...` : normalized;
|
|
51
|
+
}
|
|
52
|
+
function firstHeaderValue(value) {
|
|
53
|
+
return value?.split(",")[0]?.trim() || undefined;
|
|
54
|
+
}
|
|
55
|
+
function formatPretty(entry) {
|
|
56
|
+
const ts = String(entry.ts);
|
|
57
|
+
const level = String(entry.level).toUpperCase();
|
|
58
|
+
const event = String(entry.event);
|
|
59
|
+
const rest = Object.entries(entry)
|
|
60
|
+
.filter(([key, value]) => !["ts", "level", "event"].includes(key) && value !== undefined)
|
|
61
|
+
.map(([key, value]) => `${key}=${formatPrettyValue(value)}`)
|
|
62
|
+
.join(" ");
|
|
63
|
+
return rest ? `${ts} ${level} ${event} ${rest}` : `${ts} ${level} ${event}`;
|
|
64
|
+
}
|
|
65
|
+
function formatPrettyValue(value) {
|
|
66
|
+
if (typeof value === "string")
|
|
67
|
+
return JSON.stringify(value);
|
|
68
|
+
return JSON.stringify(value);
|
|
69
|
+
}
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import { timingSafeEqual, randomBytes, randomUUID, createHash } from "node:crypto";
|
|
2
|
+
import { AccessDeniedError, InvalidGrantError, InvalidRequestError, InvalidTokenError } from "@modelcontextprotocol/sdk/server/auth/errors.js";
|
|
3
|
+
import { checkResourceAllowed, resourceUrlFromServerUrl } from "@modelcontextprotocol/sdk/shared/auth-utils.js";
|
|
4
|
+
import { SqliteOAuthClientsStore, SqliteOAuthStore } from "./oauth-store.js";
|
|
5
|
+
const CODE_TTL_MS = 5 * 60 * 1000;
|
|
6
|
+
function randomToken() {
|
|
7
|
+
return randomBytes(32).toString("base64url");
|
|
8
|
+
}
|
|
9
|
+
function safeEquals(a, b) {
|
|
10
|
+
const left = Buffer.from(a);
|
|
11
|
+
const right = Buffer.from(b);
|
|
12
|
+
if (left.byteLength !== right.byteLength)
|
|
13
|
+
return false;
|
|
14
|
+
return timingSafeEqual(left, right);
|
|
15
|
+
}
|
|
16
|
+
function htmlEscape(value) {
|
|
17
|
+
return value
|
|
18
|
+
.replaceAll("&", "&")
|
|
19
|
+
.replaceAll("<", "<")
|
|
20
|
+
.replaceAll(">", ">")
|
|
21
|
+
.replaceAll('"', """)
|
|
22
|
+
.replaceAll("'", "'");
|
|
23
|
+
}
|
|
24
|
+
function formHtml(params) {
|
|
25
|
+
const scopeText = params.scopes.length > 0 ? params.scopes.join(" ") : "devspace";
|
|
26
|
+
const resourceText = params.resource?.href ?? "DevSpace MCP endpoint";
|
|
27
|
+
const error = params.error
|
|
28
|
+
? `<p class="error">${htmlEscape(params.error)}</p>`
|
|
29
|
+
: "";
|
|
30
|
+
const hiddenFields = Object.entries(params.fields)
|
|
31
|
+
.filter((entry) => entry[1] !== undefined)
|
|
32
|
+
.map(([name, value]) => ` <input type="hidden" name="${htmlEscape(name)}" value="${htmlEscape(value)}" />`)
|
|
33
|
+
.join("\n");
|
|
34
|
+
return `<!doctype html>
|
|
35
|
+
<html lang="en">
|
|
36
|
+
<head>
|
|
37
|
+
<meta charset="utf-8" />
|
|
38
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
39
|
+
<title>Connect DevSpace</title>
|
|
40
|
+
<style>
|
|
41
|
+
body { font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; margin: 0; background: #0f172a; color: #e2e8f0; }
|
|
42
|
+
main { max-width: 440px; margin: 12vh auto; padding: 32px; background: #111827; border: 1px solid #334155; border-radius: 18px; box-shadow: 0 24px 80px rgba(0,0,0,.35); }
|
|
43
|
+
h1 { margin: 0 0 12px; font-size: 28px; }
|
|
44
|
+
p { line-height: 1.5; color: #cbd5e1; }
|
|
45
|
+
dl { padding: 16px; background: #020617; border-radius: 12px; }
|
|
46
|
+
dt { color: #94a3b8; font-size: 12px; text-transform: uppercase; letter-spacing: .06em; }
|
|
47
|
+
dd { margin: 4px 0 12px; word-break: break-word; }
|
|
48
|
+
label { display: block; margin: 18px 0 8px; font-weight: 600; }
|
|
49
|
+
input { box-sizing: border-box; width: 100%; padding: 12px 14px; border-radius: 10px; border: 1px solid #475569; background: #020617; color: #e2e8f0; font-size: 16px; }
|
|
50
|
+
button { margin-top: 18px; width: 100%; border: 0; border-radius: 10px; padding: 12px 14px; font-weight: 700; color: #020617; background: #38bdf8; cursor: pointer; }
|
|
51
|
+
.error { color: #fecaca; background: #7f1d1d; border-radius: 10px; padding: 10px 12px; }
|
|
52
|
+
.warning { color: #fde68a; }
|
|
53
|
+
</style>
|
|
54
|
+
</head>
|
|
55
|
+
<body>
|
|
56
|
+
<main>
|
|
57
|
+
<h1>Connect DevSpace</h1>
|
|
58
|
+
<p class="warning">Only approve this if you are intentionally connecting your own ChatGPT or MCP client to this local machine.</p>
|
|
59
|
+
${error}
|
|
60
|
+
<dl>
|
|
61
|
+
<dt>Client</dt><dd>${htmlEscape(params.clientName)}</dd>
|
|
62
|
+
<dt>Scope</dt><dd>${htmlEscape(scopeText)}</dd>
|
|
63
|
+
<dt>Resource</dt><dd>${htmlEscape(resourceText)}</dd>
|
|
64
|
+
</dl>
|
|
65
|
+
<form method="post">
|
|
66
|
+
${hiddenFields}
|
|
67
|
+
<label for="owner_token">Owner password</label>
|
|
68
|
+
<input id="owner_token" name="owner_token" type="password" autocomplete="current-password" autofocus required />
|
|
69
|
+
<button type="submit">Authorize DevSpace</button>
|
|
70
|
+
</form>
|
|
71
|
+
</main>
|
|
72
|
+
</body>
|
|
73
|
+
</html>`;
|
|
74
|
+
}
|
|
75
|
+
function requestedScopesAllowed(requested, supported) {
|
|
76
|
+
return requested.every((scope) => supported.includes(scope));
|
|
77
|
+
}
|
|
78
|
+
export class SingleUserOAuthProvider {
|
|
79
|
+
config;
|
|
80
|
+
clientsStore;
|
|
81
|
+
codes = new Map();
|
|
82
|
+
oauthStore;
|
|
83
|
+
resourceServerUrl;
|
|
84
|
+
constructor(config, resourceServerUrl, stateDir) {
|
|
85
|
+
this.config = config;
|
|
86
|
+
this.resourceServerUrl = resourceUrlFromServerUrl(resourceServerUrl);
|
|
87
|
+
this.oauthStore = new SqliteOAuthStore(stateDir);
|
|
88
|
+
this.clientsStore = new SqliteOAuthClientsStore(this.oauthStore, config.allowedRedirectHosts);
|
|
89
|
+
}
|
|
90
|
+
async authorize(client, params, res) {
|
|
91
|
+
if (!params.resource || !checkResourceAllowed({ requestedResource: params.resource, configuredResource: this.resourceServerUrl })) {
|
|
92
|
+
throw new InvalidRequestError("Invalid or missing OAuth resource");
|
|
93
|
+
}
|
|
94
|
+
if (!requestedScopesAllowed(params.scopes ?? [], this.config.scopes)) {
|
|
95
|
+
throw new InvalidRequestError("Requested scope is not supported");
|
|
96
|
+
}
|
|
97
|
+
if (res.req.method !== "POST") {
|
|
98
|
+
res.status(200).setHeader("Content-Type", "text/html; charset=utf-8");
|
|
99
|
+
res.send(formHtml({
|
|
100
|
+
clientName: client.client_name ?? client.client_id,
|
|
101
|
+
scopes: params.scopes ?? this.config.scopes,
|
|
102
|
+
resource: params.resource,
|
|
103
|
+
fields: authorizationFormFields(client, params),
|
|
104
|
+
}));
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
const providedToken = String(res.req.body?.owner_token ?? "");
|
|
108
|
+
if (!safeEquals(providedToken, this.config.ownerToken)) {
|
|
109
|
+
res.status(401).setHeader("Content-Type", "text/html; charset=utf-8");
|
|
110
|
+
res.send(formHtml({
|
|
111
|
+
error: "The Owner password was not accepted.",
|
|
112
|
+
clientName: client.client_name ?? client.client_id,
|
|
113
|
+
scopes: params.scopes ?? this.config.scopes,
|
|
114
|
+
resource: params.resource,
|
|
115
|
+
fields: authorizationFormFields(client, params),
|
|
116
|
+
}));
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
const code = `code-${randomUUID()}`;
|
|
120
|
+
this.codes.set(code, {
|
|
121
|
+
clientId: client.client_id,
|
|
122
|
+
params,
|
|
123
|
+
expiresAtMs: Date.now() + CODE_TTL_MS,
|
|
124
|
+
});
|
|
125
|
+
const redirectUrl = new URL(params.redirectUri);
|
|
126
|
+
redirectUrl.searchParams.set("code", code);
|
|
127
|
+
if (params.state !== undefined)
|
|
128
|
+
redirectUrl.searchParams.set("state", params.state);
|
|
129
|
+
res.redirect(302, redirectUrl.href);
|
|
130
|
+
}
|
|
131
|
+
async challengeForAuthorizationCode(client, authorizationCode) {
|
|
132
|
+
const record = this.validCodeRecord(client, authorizationCode);
|
|
133
|
+
return record.params.codeChallenge;
|
|
134
|
+
}
|
|
135
|
+
async exchangeAuthorizationCode(client, authorizationCode, _codeVerifier, redirectUri, resource) {
|
|
136
|
+
const record = this.validCodeRecord(client, authorizationCode);
|
|
137
|
+
if (redirectUri && redirectUri !== record.params.redirectUri) {
|
|
138
|
+
throw new InvalidGrantError("redirect_uri does not match the authorization request");
|
|
139
|
+
}
|
|
140
|
+
if (resource && !checkResourceAllowed({ requestedResource: resource, configuredResource: this.resourceServerUrl })) {
|
|
141
|
+
throw new InvalidGrantError("Invalid resource");
|
|
142
|
+
}
|
|
143
|
+
this.codes.delete(authorizationCode);
|
|
144
|
+
return this.issueTokens(client.client_id, record.params.scopes ?? this.config.scopes, record.params.resource);
|
|
145
|
+
}
|
|
146
|
+
async exchangeRefreshToken(client, refreshToken, scopes, resource) {
|
|
147
|
+
const refreshTokenHash = hashToken(refreshToken);
|
|
148
|
+
const record = this.oauthStore.getRefreshToken(refreshTokenHash);
|
|
149
|
+
if (!record || record.clientId !== client.client_id || record.expiresAt < Math.floor(Date.now() / 1000)) {
|
|
150
|
+
throw new InvalidGrantError("Invalid refresh token");
|
|
151
|
+
}
|
|
152
|
+
if (resource && !checkResourceAllowed({ requestedResource: resource, configuredResource: this.resourceServerUrl })) {
|
|
153
|
+
throw new InvalidGrantError("Invalid resource");
|
|
154
|
+
}
|
|
155
|
+
const requestedScopes = scopes ?? record.scopes;
|
|
156
|
+
if (!requestedScopes.every((scope) => record.scopes.includes(scope))) {
|
|
157
|
+
throw new AccessDeniedError("Refresh token cannot grant requested scopes");
|
|
158
|
+
}
|
|
159
|
+
return this.issueTokens(client.client_id, requestedScopes, resource ?? (record.resource ? new URL(record.resource) : undefined), refreshTokenHash);
|
|
160
|
+
}
|
|
161
|
+
async verifyAccessToken(token) {
|
|
162
|
+
const record = this.oauthStore.getAccessToken(hashToken(token));
|
|
163
|
+
if (!record || record.expiresAt < Math.floor(Date.now() / 1000)) {
|
|
164
|
+
throw new InvalidTokenError("Invalid or expired access token");
|
|
165
|
+
}
|
|
166
|
+
return {
|
|
167
|
+
token,
|
|
168
|
+
clientId: record.clientId,
|
|
169
|
+
scopes: record.scopes,
|
|
170
|
+
expiresAt: record.expiresAt,
|
|
171
|
+
resource: record.resource ? new URL(record.resource) : undefined,
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
async revokeToken(_client, request) {
|
|
175
|
+
const hashed = hashToken(request.token);
|
|
176
|
+
this.oauthStore.deleteAccessToken(hashed);
|
|
177
|
+
this.oauthStore.deleteRefreshToken(hashed);
|
|
178
|
+
}
|
|
179
|
+
close() {
|
|
180
|
+
this.oauthStore.close();
|
|
181
|
+
}
|
|
182
|
+
validCodeRecord(client, authorizationCode) {
|
|
183
|
+
const record = this.codes.get(authorizationCode);
|
|
184
|
+
if (!record || record.clientId !== client.client_id || record.expiresAtMs < Date.now()) {
|
|
185
|
+
throw new InvalidGrantError("Invalid authorization code");
|
|
186
|
+
}
|
|
187
|
+
return record;
|
|
188
|
+
}
|
|
189
|
+
issueTokens(clientId, scopes, resource, consumedRefreshTokenHash) {
|
|
190
|
+
const now = Math.floor(Date.now() / 1000);
|
|
191
|
+
const accessToken = randomToken();
|
|
192
|
+
const refreshToken = randomToken();
|
|
193
|
+
const accessExpiresAt = now + this.config.accessTokenTtlSeconds;
|
|
194
|
+
const refreshExpiresAt = now + this.config.refreshTokenTtlSeconds;
|
|
195
|
+
const saved = this.oauthStore.saveTokenPair({
|
|
196
|
+
accessTokenHash: hashToken(accessToken),
|
|
197
|
+
accessToken: {
|
|
198
|
+
clientId,
|
|
199
|
+
scopes,
|
|
200
|
+
expiresAt: accessExpiresAt,
|
|
201
|
+
resource: resource?.href,
|
|
202
|
+
},
|
|
203
|
+
refreshTokenHash: hashToken(refreshToken),
|
|
204
|
+
refreshToken: {
|
|
205
|
+
clientId,
|
|
206
|
+
scopes,
|
|
207
|
+
expiresAt: refreshExpiresAt,
|
|
208
|
+
resource: resource?.href,
|
|
209
|
+
},
|
|
210
|
+
}, consumedRefreshTokenHash);
|
|
211
|
+
if (!saved) {
|
|
212
|
+
throw new InvalidGrantError("Invalid refresh token");
|
|
213
|
+
}
|
|
214
|
+
return {
|
|
215
|
+
access_token: accessToken,
|
|
216
|
+
token_type: "bearer",
|
|
217
|
+
expires_in: this.config.accessTokenTtlSeconds,
|
|
218
|
+
refresh_token: refreshToken,
|
|
219
|
+
scope: scopes.join(" "),
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
function authorizationFormFields(client, params) {
|
|
224
|
+
return {
|
|
225
|
+
response_type: "code",
|
|
226
|
+
client_id: client.client_id,
|
|
227
|
+
redirect_uri: params.redirectUri,
|
|
228
|
+
code_challenge: params.codeChallenge,
|
|
229
|
+
code_challenge_method: "S256",
|
|
230
|
+
scope: params.scopes?.join(" "),
|
|
231
|
+
state: params.state,
|
|
232
|
+
resource: params.resource?.href,
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
function hashToken(token) {
|
|
236
|
+
return createHash("sha256").update(token).digest("base64url");
|
|
237
|
+
}
|