@zoer7788/mcp-nexus-node 0.1.6 → 0.1.8
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 +2 -1
- package/package.json +13 -3
- 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,1204 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import { access, realpath } from "node:fs/promises";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
6
|
+
import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js";
|
|
7
|
+
import { mcpAuthRouter, getOAuthProtectedResourceMetadataUrl } from "@modelcontextprotocol/sdk/server/auth/router.js";
|
|
8
|
+
import { requireBearerAuth } from "@modelcontextprotocol/sdk/server/auth/middleware/bearerAuth.js";
|
|
9
|
+
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
10
|
+
import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
|
|
11
|
+
import { checkResourceAllowed, resourceUrlFromServerUrl } from "@modelcontextprotocol/sdk/shared/auth-utils.js";
|
|
12
|
+
import { registerAppResource, registerAppTool, RESOURCE_MIME_TYPE, } from "@modelcontextprotocol/ext-apps/server";
|
|
13
|
+
import express from "express";
|
|
14
|
+
import * as z from "zod/v4";
|
|
15
|
+
import { loadConfig } from "./config.js";
|
|
16
|
+
import { dashboardConfigPayload, renderDashboardHtml, saveDashboardConfigFromBody, } from "./dashboard.js";
|
|
17
|
+
import { logEvent, requestIp, requestPath, commandPreview, sessionIdPrefix, } from "./logger.js";
|
|
18
|
+
import { editFileTool, findFilesTool, grepFilesTool, listDirectoryTool, readFileTool, runShellTool, writeFileTool, } from "./pi-tools.js";
|
|
19
|
+
import { SingleUserOAuthProvider } from "./oauth-provider.js";
|
|
20
|
+
import { createReviewCheckpointManager } from "./review-checkpoints.js";
|
|
21
|
+
import { formatPathForPrompt } from "./skills.js";
|
|
22
|
+
import { createWorkspaceStore } from "./workspace-store.js";
|
|
23
|
+
import { formatAgentsPath, WorkspaceRegistry } from "./workspaces.js";
|
|
24
|
+
const WORKSPACE_APP_URI = "ui://devspace/workspace-app.html";
|
|
25
|
+
const WORKSPACE_APP_MANIFEST_ENTRY = "workspace-app.html";
|
|
26
|
+
const WRITE_TOOL_ANNOTATIONS = {
|
|
27
|
+
readOnlyHint: false,
|
|
28
|
+
destructiveHint: true,
|
|
29
|
+
idempotentHint: false,
|
|
30
|
+
openWorldHint: false,
|
|
31
|
+
};
|
|
32
|
+
const EDIT_TOOL_ANNOTATIONS = {
|
|
33
|
+
readOnlyHint: false,
|
|
34
|
+
destructiveHint: true,
|
|
35
|
+
idempotentHint: false,
|
|
36
|
+
openWorldHint: false,
|
|
37
|
+
};
|
|
38
|
+
const SHELL_TOOL_ANNOTATIONS = {
|
|
39
|
+
readOnlyHint: false,
|
|
40
|
+
destructiveHint: true,
|
|
41
|
+
idempotentHint: false,
|
|
42
|
+
openWorldHint: true,
|
|
43
|
+
};
|
|
44
|
+
function shouldAttachWidget(mode, kind) {
|
|
45
|
+
switch (mode) {
|
|
46
|
+
case "off":
|
|
47
|
+
return false;
|
|
48
|
+
case "changes":
|
|
49
|
+
return kind === "workspace" || kind === "show_changes";
|
|
50
|
+
case "full":
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function toolWidgetDescriptorMeta(config, kind) {
|
|
55
|
+
if (!shouldAttachWidget(config.widgets, kind))
|
|
56
|
+
return { _meta: {} };
|
|
57
|
+
return {
|
|
58
|
+
_meta: {
|
|
59
|
+
ui: {
|
|
60
|
+
resourceUri: WORKSPACE_APP_URI,
|
|
61
|
+
visibility: ["model"],
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
function toolNamesFor(config) {
|
|
67
|
+
return config.toolNaming === "short"
|
|
68
|
+
? {
|
|
69
|
+
openWorkspace: "open_workspace",
|
|
70
|
+
read: "read",
|
|
71
|
+
write: "write",
|
|
72
|
+
edit: "edit",
|
|
73
|
+
grep: "grep",
|
|
74
|
+
glob: "glob",
|
|
75
|
+
ls: "ls",
|
|
76
|
+
shell: "bash",
|
|
77
|
+
}
|
|
78
|
+
: {
|
|
79
|
+
openWorkspace: "open_workspace",
|
|
80
|
+
read: "read_file",
|
|
81
|
+
write: "write_file",
|
|
82
|
+
edit: "edit_file",
|
|
83
|
+
grep: "grep_files",
|
|
84
|
+
glob: "find_files",
|
|
85
|
+
ls: "list_directory",
|
|
86
|
+
shell: "run_shell",
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
function serverInstructions(config, toolNames) {
|
|
90
|
+
const inspection = config.minimalTools
|
|
91
|
+
? `In minimal tool mode, ${toolNames.grep}, ${toolNames.glob}, and ${toolNames.ls} are disabled; use ${toolNames.shell} with command-line tools such as grep, rg, find, ls, and tree for search and directory inspection. `
|
|
92
|
+
: `Prefer ${toolNames.read}, ${toolNames.grep}, ${toolNames.glob}, and ${toolNames.ls} for file inspection. `;
|
|
93
|
+
const skills = config.skillsEnabled
|
|
94
|
+
? `When ${toolNames.openWorkspace} returns available skills and a task matches a skill, use ${toolNames.read} to read that skill's path before proceeding. Skill paths may be outside the workspace, but ${toolNames.read} only permits advertised SKILL.md files and files under already-loaded skill directories. `
|
|
95
|
+
: "";
|
|
96
|
+
const agentsMd = `Follow instructions returned by ${toolNames.openWorkspace}. Before working under a path listed in availableAgentsFiles, use ${toolNames.read} to inspect that instruction file and follow it. `;
|
|
97
|
+
const showChanges = config.widgets === "changes"
|
|
98
|
+
? " After creating, editing, or overwriting files, call show_changes once after the related file changes are complete so the user can see the aggregate diff."
|
|
99
|
+
: "";
|
|
100
|
+
return `Use DevSpace as a local coding workspace. Call ${toolNames.openWorkspace} once per project folder or worktree to obtain a workspaceId. Reuse that same workspaceId for all later file, search, edit, write, show-changes, and shell tools in that folder; do not call ${toolNames.openWorkspace} again unless switching folders/worktrees, changing checkout/worktree mode, the workspaceId is rejected as unknown, or the user explicitly asks to reopen. ${agentsMd}${skills}${inspection}Prefer ${toolNames.edit} for targeted modifications, ${toolNames.write} only for new files or complete rewrites, and ${toolNames.shell} for tests, builds, git inspection, package scripts, and commands that are better executed by the shell. Do not create or modify files with ${toolNames.shell}; avoid shell redirection, heredocs, tee, sed -i, perl -i, node/python/ruby scripts, or any command whose purpose is to write project files.${showChanges}`;
|
|
101
|
+
}
|
|
102
|
+
function resultOutputSchema(extra = {}) {
|
|
103
|
+
return {
|
|
104
|
+
result: z
|
|
105
|
+
.string()
|
|
106
|
+
.describe("Model-readable result text for follow-up reasoning and plain MCP hosts."),
|
|
107
|
+
...extra,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
const workspaceSkillOutputSchema = z.object({
|
|
111
|
+
name: z.string(),
|
|
112
|
+
description: z.string(),
|
|
113
|
+
path: z.string(),
|
|
114
|
+
});
|
|
115
|
+
const workspaceAgentsFileOutputSchema = z.object({
|
|
116
|
+
path: z.string(),
|
|
117
|
+
content: z.string(),
|
|
118
|
+
});
|
|
119
|
+
const workspaceAvailableAgentsFileOutputSchema = z.object({
|
|
120
|
+
path: z.string(),
|
|
121
|
+
});
|
|
122
|
+
const reviewFileOutputSchema = z.object({
|
|
123
|
+
path: z.string(),
|
|
124
|
+
previousPath: z.string().optional(),
|
|
125
|
+
type: z.enum(["change", "rename-pure", "rename-changed", "new", "deleted"]),
|
|
126
|
+
additions: z.number(),
|
|
127
|
+
removals: z.number(),
|
|
128
|
+
});
|
|
129
|
+
const reviewSummaryOutputSchema = z.object({
|
|
130
|
+
files: z.number(),
|
|
131
|
+
additions: z.number(),
|
|
132
|
+
removals: z.number(),
|
|
133
|
+
});
|
|
134
|
+
function sendJsonRpcError(res, status, code, message) {
|
|
135
|
+
res.status(status).json({
|
|
136
|
+
jsonrpc: "2.0",
|
|
137
|
+
error: { code, message },
|
|
138
|
+
id: null,
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
function requestLogFields(req, config) {
|
|
142
|
+
return {
|
|
143
|
+
ip: requestIp(req, config.logging.trustProxy),
|
|
144
|
+
host: req.header("host"),
|
|
145
|
+
userAgent: req.header("user-agent"),
|
|
146
|
+
origin: req.header("origin"),
|
|
147
|
+
referer: req.header("referer"),
|
|
148
|
+
contentLength: req.header("content-length"),
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
function logToolCall(config, fields) {
|
|
152
|
+
if (!config.logging.toolCalls)
|
|
153
|
+
return;
|
|
154
|
+
const { command, ...safeFields } = fields;
|
|
155
|
+
logEvent(config.logging, fields.success ? "info" : "warn", "tool_call", {
|
|
156
|
+
...safeFields,
|
|
157
|
+
commandPreview: config.logging.shellCommands && command ? commandPreview(command) : undefined,
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
function contentText(content) {
|
|
161
|
+
return content
|
|
162
|
+
.filter((item) => item.type === "text")
|
|
163
|
+
.map((item) => item.text)
|
|
164
|
+
.join("\n");
|
|
165
|
+
}
|
|
166
|
+
function toolErrorPreview(content) {
|
|
167
|
+
const text = contentText(content).replace(/\s+/g, " ").trim();
|
|
168
|
+
if (!text)
|
|
169
|
+
return undefined;
|
|
170
|
+
return text.length > 240 ? `${text.slice(0, 237)}...` : text;
|
|
171
|
+
}
|
|
172
|
+
function logFailedToolResponse(config, fields, content, startedAt) {
|
|
173
|
+
logToolCall(config, {
|
|
174
|
+
...fields,
|
|
175
|
+
success: false,
|
|
176
|
+
durationMs: Math.round(performance.now() - startedAt),
|
|
177
|
+
error: toolErrorPreview(content),
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
function textBlock(text) {
|
|
181
|
+
return { type: "text", text };
|
|
182
|
+
}
|
|
183
|
+
function textSummary(content) {
|
|
184
|
+
const text = contentText(content);
|
|
185
|
+
return {
|
|
186
|
+
lines: text.length === 0 ? 0 : text.split("\n").length,
|
|
187
|
+
characters: text.length,
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
function contentLineCount(content) {
|
|
191
|
+
if (content.length === 0)
|
|
192
|
+
return 0;
|
|
193
|
+
return content.endsWith("\n")
|
|
194
|
+
? content.slice(0, -1).split("\n").length
|
|
195
|
+
: content.split("\n").length;
|
|
196
|
+
}
|
|
197
|
+
function countDiffStats(diff) {
|
|
198
|
+
if (!diff)
|
|
199
|
+
return { additions: 0, removals: 0 };
|
|
200
|
+
let additions = 0;
|
|
201
|
+
let removals = 0;
|
|
202
|
+
for (const line of diff.split("\n")) {
|
|
203
|
+
if (line.startsWith("+") && !line.startsWith("+++"))
|
|
204
|
+
additions++;
|
|
205
|
+
if (line.startsWith("-") && !line.startsWith("---"))
|
|
206
|
+
removals++;
|
|
207
|
+
}
|
|
208
|
+
return { additions, removals };
|
|
209
|
+
}
|
|
210
|
+
function newFilePatch(path, content) {
|
|
211
|
+
const lines = content.length === 0
|
|
212
|
+
? []
|
|
213
|
+
: content.endsWith("\n")
|
|
214
|
+
? content.slice(0, -1).split("\n")
|
|
215
|
+
: content.split("\n");
|
|
216
|
+
const hunkLength = lines.length;
|
|
217
|
+
const hunkRange = hunkLength === 0 ? "+0,0" : `+1,${hunkLength}`;
|
|
218
|
+
const body = lines.map((line) => `+${line}`).join("\n");
|
|
219
|
+
return [
|
|
220
|
+
`diff --git a/${path} b/${path}`,
|
|
221
|
+
"new file mode 100644",
|
|
222
|
+
"index 0000000..0000000",
|
|
223
|
+
"--- /dev/null",
|
|
224
|
+
`+++ b/${path}`,
|
|
225
|
+
`@@ -0,0 ${hunkRange} @@`,
|
|
226
|
+
body,
|
|
227
|
+
]
|
|
228
|
+
.filter((line) => line.length > 0)
|
|
229
|
+
.join("\n");
|
|
230
|
+
}
|
|
231
|
+
function assetBaseUrl(config) {
|
|
232
|
+
return `${config.publicBaseUrl.replace(/\/+$/, "")}/mcp-app-assets`;
|
|
233
|
+
}
|
|
234
|
+
function uiManifestUrl() {
|
|
235
|
+
return new URL("../dist/ui/.vite/manifest.json", import.meta.url);
|
|
236
|
+
}
|
|
237
|
+
function readWorkspaceAppManifest() {
|
|
238
|
+
return JSON.parse(readFileSync(uiManifestUrl(), "utf8"));
|
|
239
|
+
}
|
|
240
|
+
function getWorkspaceAppManifestEntry() {
|
|
241
|
+
const manifest = readWorkspaceAppManifest();
|
|
242
|
+
const entry = manifest[WORKSPACE_APP_MANIFEST_ENTRY];
|
|
243
|
+
if (!entry?.file) {
|
|
244
|
+
throw new Error(`Missing ${WORKSPACE_APP_MANIFEST_ENTRY} in UI manifest.`);
|
|
245
|
+
}
|
|
246
|
+
return entry;
|
|
247
|
+
}
|
|
248
|
+
function assetUrl(baseUrl, assetPath) {
|
|
249
|
+
return `${baseUrl}/${assetPath.replace(/^\/+/, "")}`;
|
|
250
|
+
}
|
|
251
|
+
function workspaceAppHtml(config) {
|
|
252
|
+
const baseUrl = assetBaseUrl(config);
|
|
253
|
+
const entry = getWorkspaceAppManifestEntry();
|
|
254
|
+
const stylesheets = (entry.css ?? [])
|
|
255
|
+
.map((stylesheet) => ` <link rel="stylesheet" crossorigin href="${assetUrl(baseUrl, stylesheet)}" />`)
|
|
256
|
+
.join("\n");
|
|
257
|
+
return `<!doctype html>
|
|
258
|
+
<html lang="en">
|
|
259
|
+
<head>
|
|
260
|
+
<meta charset="UTF-8" />
|
|
261
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
262
|
+
<title>DevSpace Workspace</title>
|
|
263
|
+
<script type="module" crossorigin src="${assetUrl(baseUrl, entry.file)}"></script>
|
|
264
|
+
${stylesheets}
|
|
265
|
+
</head>
|
|
266
|
+
<body>
|
|
267
|
+
<main id="app" class="shell">
|
|
268
|
+
<section class="empty">Waiting for a tool result.</section>
|
|
269
|
+
</main>
|
|
270
|
+
</body>
|
|
271
|
+
</html>`;
|
|
272
|
+
}
|
|
273
|
+
function appCsp(config) {
|
|
274
|
+
const publicBaseUrl = config.publicBaseUrl.replace(/\/+$/, "");
|
|
275
|
+
return {
|
|
276
|
+
resourceDomains: [publicBaseUrl],
|
|
277
|
+
connectDomains: [publicBaseUrl],
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
function uiBuildDirectory() {
|
|
281
|
+
return fileURLToPath(new URL("../dist/ui", import.meta.url));
|
|
282
|
+
}
|
|
283
|
+
function setAssetHeaders(res) {
|
|
284
|
+
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
285
|
+
res.setHeader("Access-Control-Allow-Methods", "GET, HEAD, OPTIONS");
|
|
286
|
+
res.setHeader("Access-Control-Allow-Headers", "Content-Type, Range");
|
|
287
|
+
res.setHeader("Cross-Origin-Resource-Policy", "cross-origin");
|
|
288
|
+
}
|
|
289
|
+
async function assertWorkspaceAppAssets() {
|
|
290
|
+
const entry = getWorkspaceAppManifestEntry();
|
|
291
|
+
const candidates = [entry.file, ...(entry.css ?? [])].map((assetPath) => new URL(`../dist/ui/${assetPath}`, import.meta.url));
|
|
292
|
+
for (const candidate of candidates) {
|
|
293
|
+
await access(candidate);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
function createMcpServer(config, workspaces, reviewCheckpoints) {
|
|
297
|
+
const toolNames = toolNamesFor(config);
|
|
298
|
+
const server = new McpServer({
|
|
299
|
+
name: "devspace",
|
|
300
|
+
title: "DevSpace",
|
|
301
|
+
version: "0.1.0",
|
|
302
|
+
description: "Secure local coding workspace for MCP clients. Provides workspace-scoped file, search, edit, write, and shell tools.",
|
|
303
|
+
}, {
|
|
304
|
+
instructions: serverInstructions(config, toolNames),
|
|
305
|
+
});
|
|
306
|
+
registerAppResource(server, "DevSpace Diff Card", WORKSPACE_APP_URI, {
|
|
307
|
+
description: "Interactive card for viewing DevSpace file diffs.",
|
|
308
|
+
_meta: {
|
|
309
|
+
ui: {
|
|
310
|
+
csp: appCsp(config),
|
|
311
|
+
},
|
|
312
|
+
},
|
|
313
|
+
}, async () => {
|
|
314
|
+
await assertWorkspaceAppAssets();
|
|
315
|
+
return {
|
|
316
|
+
contents: [
|
|
317
|
+
{
|
|
318
|
+
uri: WORKSPACE_APP_URI,
|
|
319
|
+
mimeType: RESOURCE_MIME_TYPE,
|
|
320
|
+
text: workspaceAppHtml(config),
|
|
321
|
+
_meta: {
|
|
322
|
+
ui: {
|
|
323
|
+
csp: appCsp(config),
|
|
324
|
+
},
|
|
325
|
+
},
|
|
326
|
+
},
|
|
327
|
+
],
|
|
328
|
+
};
|
|
329
|
+
});
|
|
330
|
+
registerAppTool(server, "open_workspace", {
|
|
331
|
+
title: "Open workspace",
|
|
332
|
+
description: "Open a local project directory as a coding workspace. Call this once per project folder or worktree before reading, editing, searching, writing, showing changes, or running commands. Reuse the returned workspaceId for later calls in the same folder; do not call open_workspace again unless switching folders/worktrees, changing checkout/worktree mode, the workspaceId is rejected as unknown, or the user explicitly asks to reopen. By default this opens the actual checkout; set mode=\"worktree\" when the user asks for an isolated or parallel coding session. Returns a workspaceId, loaded root project instructions, and nested instruction file paths the model should read before working in those directories.",
|
|
333
|
+
inputSchema: {
|
|
334
|
+
path: z
|
|
335
|
+
.string()
|
|
336
|
+
.describe("Absolute path, or a leading-tilde home path such as ~/project, to a local project directory inside an allowed root."),
|
|
337
|
+
mode: z
|
|
338
|
+
.enum(["checkout", "worktree"])
|
|
339
|
+
.optional()
|
|
340
|
+
.describe("Defaults to checkout. Use checkout to work in the actual directory. Use worktree to create an isolated managed Git worktree for parallel work."),
|
|
341
|
+
baseRef: z
|
|
342
|
+
.string()
|
|
343
|
+
.optional()
|
|
344
|
+
.describe("Git ref to base a worktree on. Only used with mode=\"worktree\". Defaults to HEAD."),
|
|
345
|
+
},
|
|
346
|
+
outputSchema: {
|
|
347
|
+
workspaceId: z.string(),
|
|
348
|
+
root: z.string(),
|
|
349
|
+
mode: z.enum(["checkout", "worktree"]),
|
|
350
|
+
sourceRoot: z.string().optional(),
|
|
351
|
+
worktree: z
|
|
352
|
+
.object({
|
|
353
|
+
path: z.string(),
|
|
354
|
+
baseRef: z.string(),
|
|
355
|
+
baseSha: z.string(),
|
|
356
|
+
dirtySource: z.boolean(),
|
|
357
|
+
detached: z.boolean(),
|
|
358
|
+
managed: z.boolean(),
|
|
359
|
+
})
|
|
360
|
+
.optional(),
|
|
361
|
+
agentsFiles: z.array(workspaceAgentsFileOutputSchema),
|
|
362
|
+
availableAgentsFiles: z.array(workspaceAvailableAgentsFileOutputSchema),
|
|
363
|
+
skills: z.array(workspaceSkillOutputSchema),
|
|
364
|
+
skillDiagnostics: z.array(z.unknown()),
|
|
365
|
+
instruction: z.string(),
|
|
366
|
+
},
|
|
367
|
+
...toolWidgetDescriptorMeta(config, "workspace"),
|
|
368
|
+
annotations: { readOnlyHint: true },
|
|
369
|
+
}, async ({ path, mode, baseRef }) => {
|
|
370
|
+
const startedAt = performance.now();
|
|
371
|
+
const { workspace, agentsFiles, availableAgentsFiles } = await workspaces.openWorkspace({ path, mode, baseRef });
|
|
372
|
+
if (config.widgets === "changes") {
|
|
373
|
+
void reviewCheckpoints.initializeWorkspace({
|
|
374
|
+
workspaceId: workspace.id,
|
|
375
|
+
root: workspace.root,
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
const visibleSkills = workspace.skills
|
|
379
|
+
.filter((skill) => !skill.disableModelInvocation)
|
|
380
|
+
.map((skill) => ({
|
|
381
|
+
name: skill.name,
|
|
382
|
+
description: skill.description,
|
|
383
|
+
path: formatPathForPrompt(skill.filePath),
|
|
384
|
+
}));
|
|
385
|
+
const loadedAgentsFiles = agentsFiles.map((file) => ({
|
|
386
|
+
path: formatAgentsPath(file.path, workspace.root),
|
|
387
|
+
content: file.content,
|
|
388
|
+
}));
|
|
389
|
+
const availableAgentsFileOutputs = availableAgentsFiles.map((file) => ({
|
|
390
|
+
path: formatAgentsPath(file.path, workspace.root),
|
|
391
|
+
}));
|
|
392
|
+
const instruction = config.skillsEnabled
|
|
393
|
+
? "Use this workspaceId in all subsequent tool calls for this project. Do not call open_workspace again for this same folder unless this workspaceId stops working, the user asks to reopen, or you switch to a different folder/worktree. Follow loaded agentsFiles instructions. Before working under a path listed in availableAgentsFiles, read that instruction file. When a task matches an available skill in skills, read its path before proceeding."
|
|
394
|
+
: "Use this workspaceId in all subsequent tool calls for this project. Do not call open_workspace again for this same folder unless this workspaceId stops working, the user asks to reopen, or you switch to a different folder/worktree. Follow loaded agentsFiles instructions. Before working under a path listed in availableAgentsFiles, read that instruction file.";
|
|
395
|
+
const resultContent = [
|
|
396
|
+
{
|
|
397
|
+
type: "text",
|
|
398
|
+
text: [
|
|
399
|
+
`Opened workspace ${workspace.id}`,
|
|
400
|
+
`Root: ${workspace.root}`,
|
|
401
|
+
`Mode: ${workspace.mode}`,
|
|
402
|
+
loadedAgentsFiles.length > 0
|
|
403
|
+
? `Loaded project instructions: ${loadedAgentsFiles.map((file) => file.path).join(", ")}`
|
|
404
|
+
: undefined,
|
|
405
|
+
availableAgentsFileOutputs.length > 0
|
|
406
|
+
? `Available nested instructions: ${availableAgentsFileOutputs.map((file) => file.path).join(", ")}`
|
|
407
|
+
: undefined,
|
|
408
|
+
visibleSkills.length > 0
|
|
409
|
+
? `Available skills: ${visibleSkills.map((skill) => skill.name).join(", ")}`
|
|
410
|
+
: undefined,
|
|
411
|
+
instruction,
|
|
412
|
+
].filter(Boolean).join("\n"),
|
|
413
|
+
},
|
|
414
|
+
];
|
|
415
|
+
logToolCall(config, {
|
|
416
|
+
tool: "open_workspace",
|
|
417
|
+
workspaceId: workspace.id,
|
|
418
|
+
path: workspace.root,
|
|
419
|
+
success: true,
|
|
420
|
+
durationMs: Math.round(performance.now() - startedAt),
|
|
421
|
+
});
|
|
422
|
+
return {
|
|
423
|
+
content: resultContent,
|
|
424
|
+
_meta: {
|
|
425
|
+
tool: "open_workspace",
|
|
426
|
+
card: {
|
|
427
|
+
workspaceId: workspace.id,
|
|
428
|
+
root: workspace.root,
|
|
429
|
+
path: workspace.root,
|
|
430
|
+
summary: {
|
|
431
|
+
agentsFiles: loadedAgentsFiles.length,
|
|
432
|
+
availableAgentsFiles: availableAgentsFileOutputs.length,
|
|
433
|
+
skills: visibleSkills.length,
|
|
434
|
+
skillDiagnostics: workspace.skillDiagnostics.length,
|
|
435
|
+
},
|
|
436
|
+
},
|
|
437
|
+
},
|
|
438
|
+
structuredContent: {
|
|
439
|
+
workspaceId: workspace.id,
|
|
440
|
+
root: workspace.root,
|
|
441
|
+
mode: workspace.mode,
|
|
442
|
+
sourceRoot: workspace.sourceRoot,
|
|
443
|
+
worktree: workspace.worktree,
|
|
444
|
+
agentsFiles: loadedAgentsFiles,
|
|
445
|
+
availableAgentsFiles: availableAgentsFileOutputs,
|
|
446
|
+
skills: visibleSkills,
|
|
447
|
+
skillDiagnostics: workspace.skillDiagnostics,
|
|
448
|
+
instruction,
|
|
449
|
+
},
|
|
450
|
+
};
|
|
451
|
+
});
|
|
452
|
+
registerAppTool(server, toolNames.read, {
|
|
453
|
+
title: "Read file",
|
|
454
|
+
description: [
|
|
455
|
+
"Read a file inside an open workspace. Use this for file inspection instead of shell commands like cat or sed. Call open_workspace first and pass workspaceId.",
|
|
456
|
+
"Use this tool to inspect relevant AGENTS.md or CLAUDE.md files listed by open_workspace before working in nested directories.",
|
|
457
|
+
config.skillsEnabled
|
|
458
|
+
? "If available skills were returned and a task matches one, read that skill's path before proceeding. Skill paths may be outside the workspace; only advertised SKILL.md files and files under already-loaded skill directories are readable."
|
|
459
|
+
: "",
|
|
460
|
+
]
|
|
461
|
+
.filter(Boolean)
|
|
462
|
+
.join(" "),
|
|
463
|
+
inputSchema: {
|
|
464
|
+
workspaceId: z
|
|
465
|
+
.string()
|
|
466
|
+
.describe("Workspace identifier returned by open_workspace."),
|
|
467
|
+
path: z
|
|
468
|
+
.string()
|
|
469
|
+
.describe(config.skillsEnabled
|
|
470
|
+
? "File path to read, relative to the workspace root. May also be an advertised skill path from open_workspace skills."
|
|
471
|
+
: "File path to read, relative to the workspace root."),
|
|
472
|
+
offset: z
|
|
473
|
+
.number()
|
|
474
|
+
.int()
|
|
475
|
+
.positive()
|
|
476
|
+
.optional()
|
|
477
|
+
.describe("1-indexed line number to start reading from."),
|
|
478
|
+
limit: z
|
|
479
|
+
.number()
|
|
480
|
+
.int()
|
|
481
|
+
.positive()
|
|
482
|
+
.optional()
|
|
483
|
+
.describe("Maximum number of lines to read."),
|
|
484
|
+
},
|
|
485
|
+
outputSchema: resultOutputSchema(),
|
|
486
|
+
...toolWidgetDescriptorMeta(config, "read"),
|
|
487
|
+
annotations: { readOnlyHint: true },
|
|
488
|
+
}, async ({ workspaceId, ...input }) => {
|
|
489
|
+
const startedAt = performance.now();
|
|
490
|
+
const workspace = workspaces.getWorkspace(workspaceId);
|
|
491
|
+
const readPath = workspaces.resolveReadPath(workspace, input.path);
|
|
492
|
+
const response = await readFileTool({ ...input, path: readPath.absolutePath }, {
|
|
493
|
+
cwd: workspace.root,
|
|
494
|
+
root: workspace.root,
|
|
495
|
+
readRoots: readPath.readRoots,
|
|
496
|
+
});
|
|
497
|
+
if (response.isError) {
|
|
498
|
+
logFailedToolResponse(config, {
|
|
499
|
+
tool: toolNames.read,
|
|
500
|
+
workspaceId,
|
|
501
|
+
path: input.path,
|
|
502
|
+
}, response.content, startedAt);
|
|
503
|
+
return response;
|
|
504
|
+
}
|
|
505
|
+
workspaces.markReadPathLoaded(workspace, readPath);
|
|
506
|
+
const summary = {
|
|
507
|
+
...textSummary(response.content),
|
|
508
|
+
offset: input.offset ?? 1,
|
|
509
|
+
limited: input.limit !== undefined,
|
|
510
|
+
};
|
|
511
|
+
logToolCall(config, {
|
|
512
|
+
tool: toolNames.read,
|
|
513
|
+
workspaceId,
|
|
514
|
+
path: input.path,
|
|
515
|
+
success: true,
|
|
516
|
+
durationMs: Math.round(performance.now() - startedAt),
|
|
517
|
+
});
|
|
518
|
+
return {
|
|
519
|
+
...response,
|
|
520
|
+
_meta: {
|
|
521
|
+
tool: toolNames.read,
|
|
522
|
+
card: {
|
|
523
|
+
workspaceId,
|
|
524
|
+
path: input.path,
|
|
525
|
+
summary,
|
|
526
|
+
payload: { content: response.content },
|
|
527
|
+
},
|
|
528
|
+
},
|
|
529
|
+
structuredContent: {
|
|
530
|
+
result: contentText(response.content),
|
|
531
|
+
},
|
|
532
|
+
};
|
|
533
|
+
});
|
|
534
|
+
registerAppTool(server, toolNames.write, {
|
|
535
|
+
title: "Write file",
|
|
536
|
+
description: `Create or completely overwrite a file inside an open workspace. Prefer ${toolNames.edit} for targeted changes to existing files. Call open_workspace first and pass workspaceId.`,
|
|
537
|
+
inputSchema: {
|
|
538
|
+
workspaceId: z
|
|
539
|
+
.string()
|
|
540
|
+
.describe("Workspace identifier returned by open_workspace."),
|
|
541
|
+
path: z
|
|
542
|
+
.string()
|
|
543
|
+
.describe("File path to write, relative to the workspace root."),
|
|
544
|
+
content: z.string().describe("Complete new file content."),
|
|
545
|
+
},
|
|
546
|
+
outputSchema: resultOutputSchema(),
|
|
547
|
+
...toolWidgetDescriptorMeta(config, "write"),
|
|
548
|
+
annotations: WRITE_TOOL_ANNOTATIONS,
|
|
549
|
+
}, async ({ workspaceId, ...input }) => {
|
|
550
|
+
const startedAt = performance.now();
|
|
551
|
+
const workspace = workspaces.getWorkspace(workspaceId);
|
|
552
|
+
workspaces.resolvePath(workspace, input.path);
|
|
553
|
+
const response = await writeFileTool(input, {
|
|
554
|
+
cwd: workspace.root,
|
|
555
|
+
root: workspace.root,
|
|
556
|
+
});
|
|
557
|
+
if (response.isError) {
|
|
558
|
+
logFailedToolResponse(config, {
|
|
559
|
+
tool: toolNames.write,
|
|
560
|
+
workspaceId,
|
|
561
|
+
path: input.path,
|
|
562
|
+
}, response.content, startedAt);
|
|
563
|
+
return response;
|
|
564
|
+
}
|
|
565
|
+
const patch = newFilePatch(input.path, input.content);
|
|
566
|
+
const stats = countDiffStats(patch);
|
|
567
|
+
const summary = {
|
|
568
|
+
...stats,
|
|
569
|
+
lines: contentLineCount(input.content),
|
|
570
|
+
characters: input.content.length,
|
|
571
|
+
};
|
|
572
|
+
logToolCall(config, {
|
|
573
|
+
tool: toolNames.write,
|
|
574
|
+
workspaceId,
|
|
575
|
+
path: input.path,
|
|
576
|
+
success: true,
|
|
577
|
+
durationMs: Math.round(performance.now() - startedAt),
|
|
578
|
+
});
|
|
579
|
+
return {
|
|
580
|
+
...response,
|
|
581
|
+
_meta: {
|
|
582
|
+
tool: toolNames.write,
|
|
583
|
+
card: {
|
|
584
|
+
workspaceId,
|
|
585
|
+
path: input.path,
|
|
586
|
+
summary,
|
|
587
|
+
payload: {
|
|
588
|
+
content: response.content,
|
|
589
|
+
patch,
|
|
590
|
+
},
|
|
591
|
+
},
|
|
592
|
+
},
|
|
593
|
+
structuredContent: {
|
|
594
|
+
result: contentText(response.content),
|
|
595
|
+
},
|
|
596
|
+
};
|
|
597
|
+
});
|
|
598
|
+
registerAppTool(server, toolNames.edit, {
|
|
599
|
+
title: "Edit file",
|
|
600
|
+
description: `Edit one file inside an open workspace by replacing exact text blocks. Prefer this over ${toolNames.write} for targeted changes. Each oldText must match a unique, non-overlapping region of the original file; merge nearby changes into one edit and keep oldText as small as possible while still unique. Call open_workspace first and pass workspaceId.`,
|
|
601
|
+
inputSchema: {
|
|
602
|
+
workspaceId: z
|
|
603
|
+
.string()
|
|
604
|
+
.describe("Workspace identifier returned by open_workspace."),
|
|
605
|
+
path: z
|
|
606
|
+
.string()
|
|
607
|
+
.describe("File path to edit, relative to the workspace root."),
|
|
608
|
+
edits: z
|
|
609
|
+
.array(z.object({
|
|
610
|
+
oldText: z
|
|
611
|
+
.string()
|
|
612
|
+
.describe("Exact text to replace. Must match uniquely in the original file."),
|
|
613
|
+
newText: z.string().describe("Replacement text."),
|
|
614
|
+
}))
|
|
615
|
+
.min(1),
|
|
616
|
+
},
|
|
617
|
+
outputSchema: resultOutputSchema({
|
|
618
|
+
status: z.literal("applied"),
|
|
619
|
+
}),
|
|
620
|
+
...toolWidgetDescriptorMeta(config, "edit"),
|
|
621
|
+
annotations: EDIT_TOOL_ANNOTATIONS,
|
|
622
|
+
}, async ({ workspaceId, ...input }) => {
|
|
623
|
+
const startedAt = performance.now();
|
|
624
|
+
const workspace = workspaces.getWorkspace(workspaceId);
|
|
625
|
+
workspaces.resolvePath(workspace, input.path);
|
|
626
|
+
const response = await editFileTool(input, {
|
|
627
|
+
cwd: workspace.root,
|
|
628
|
+
root: workspace.root,
|
|
629
|
+
});
|
|
630
|
+
if (response.isError) {
|
|
631
|
+
logFailedToolResponse(config, {
|
|
632
|
+
tool: toolNames.edit,
|
|
633
|
+
workspaceId,
|
|
634
|
+
path: input.path,
|
|
635
|
+
}, response.content, startedAt);
|
|
636
|
+
return response;
|
|
637
|
+
}
|
|
638
|
+
const stats = countDiffStats(response.details?.patch ?? response.details?.diff);
|
|
639
|
+
const summary = {
|
|
640
|
+
...stats,
|
|
641
|
+
editCount: input.edits.length,
|
|
642
|
+
};
|
|
643
|
+
const editResultText = `Edited ${input.path} (+${stats.additions} -${stats.removals}).`;
|
|
644
|
+
const editContent = [textBlock(editResultText)];
|
|
645
|
+
logToolCall(config, {
|
|
646
|
+
tool: toolNames.edit,
|
|
647
|
+
workspaceId,
|
|
648
|
+
path: input.path,
|
|
649
|
+
success: true,
|
|
650
|
+
durationMs: Math.round(performance.now() - startedAt),
|
|
651
|
+
});
|
|
652
|
+
return {
|
|
653
|
+
content: editContent,
|
|
654
|
+
_meta: {
|
|
655
|
+
tool: toolNames.edit,
|
|
656
|
+
card: {
|
|
657
|
+
workspaceId,
|
|
658
|
+
path: input.path,
|
|
659
|
+
summary,
|
|
660
|
+
payload: {
|
|
661
|
+
diff: response.details?.diff,
|
|
662
|
+
patch: response.details?.patch,
|
|
663
|
+
},
|
|
664
|
+
},
|
|
665
|
+
},
|
|
666
|
+
structuredContent: {
|
|
667
|
+
status: "applied",
|
|
668
|
+
result: contentText(editContent),
|
|
669
|
+
},
|
|
670
|
+
};
|
|
671
|
+
});
|
|
672
|
+
if (config.widgets === "changes") {
|
|
673
|
+
registerAppTool(server, "show_changes", {
|
|
674
|
+
title: "Show changes",
|
|
675
|
+
description: "Show aggregate file changes in an open workspace since the last shown checkpoint or since the workspace was opened. After you create, edit, or overwrite files, call this once when the related file changes are complete so the user can inspect the combined diff.",
|
|
676
|
+
inputSchema: {
|
|
677
|
+
workspaceId: z
|
|
678
|
+
.string()
|
|
679
|
+
.describe("Workspace identifier returned by open_workspace."),
|
|
680
|
+
since: z
|
|
681
|
+
.enum(["last_shown", "workspace_open"])
|
|
682
|
+
.optional()
|
|
683
|
+
.describe("Defaults to last_shown. Use workspace_open to compare against the initial open_workspace checkpoint."),
|
|
684
|
+
markReviewed: z
|
|
685
|
+
.boolean()
|
|
686
|
+
.optional()
|
|
687
|
+
.describe("Defaults to true. When true, advances the last shown checkpoint to the current workspace state."),
|
|
688
|
+
},
|
|
689
|
+
outputSchema: resultOutputSchema(),
|
|
690
|
+
...toolWidgetDescriptorMeta(config, "show_changes"),
|
|
691
|
+
annotations: { readOnlyHint: true },
|
|
692
|
+
}, async ({ workspaceId, since, markReviewed }) => {
|
|
693
|
+
const startedAt = performance.now();
|
|
694
|
+
const workspace = workspaces.getWorkspace(workspaceId);
|
|
695
|
+
const review = await reviewCheckpoints.reviewChanges({
|
|
696
|
+
workspaceId,
|
|
697
|
+
root: workspace.root,
|
|
698
|
+
since: since ?? "last_shown",
|
|
699
|
+
markReviewed: markReviewed ?? true,
|
|
700
|
+
});
|
|
701
|
+
const content = [textBlock(review.result)];
|
|
702
|
+
logToolCall(config, {
|
|
703
|
+
tool: "show_changes",
|
|
704
|
+
workspaceId,
|
|
705
|
+
success: true,
|
|
706
|
+
durationMs: Math.round(performance.now() - startedAt),
|
|
707
|
+
});
|
|
708
|
+
return {
|
|
709
|
+
content,
|
|
710
|
+
_meta: {
|
|
711
|
+
tool: "show_changes",
|
|
712
|
+
card: {
|
|
713
|
+
workspaceId,
|
|
714
|
+
summary: review.summary,
|
|
715
|
+
files: review.files,
|
|
716
|
+
payload: {
|
|
717
|
+
patch: review.patch,
|
|
718
|
+
},
|
|
719
|
+
},
|
|
720
|
+
},
|
|
721
|
+
structuredContent: {
|
|
722
|
+
result: contentText(content),
|
|
723
|
+
},
|
|
724
|
+
};
|
|
725
|
+
});
|
|
726
|
+
}
|
|
727
|
+
if (!config.minimalTools) {
|
|
728
|
+
registerAppTool(server, toolNames.grep, {
|
|
729
|
+
title: config.toolNaming === "short" ? "Grep" : "Grep files",
|
|
730
|
+
description: "Search file contents inside an open workspace. Use this before broad reads when looking for symbols, text, or usage sites. Respects project ignore rules. Call open_workspace first and pass workspaceId.",
|
|
731
|
+
inputSchema: {
|
|
732
|
+
workspaceId: z
|
|
733
|
+
.string()
|
|
734
|
+
.describe("Workspace identifier returned by open_workspace."),
|
|
735
|
+
pattern: z.string().describe("Search pattern."),
|
|
736
|
+
path: z
|
|
737
|
+
.string()
|
|
738
|
+
.optional()
|
|
739
|
+
.describe("Optional path or glob scope relative to the workspace root."),
|
|
740
|
+
include: z.string().optional().describe("Optional include glob."),
|
|
741
|
+
},
|
|
742
|
+
outputSchema: resultOutputSchema(),
|
|
743
|
+
...toolWidgetDescriptorMeta(config, "search"),
|
|
744
|
+
annotations: { readOnlyHint: true },
|
|
745
|
+
}, async ({ workspaceId, ...input }) => {
|
|
746
|
+
const startedAt = performance.now();
|
|
747
|
+
const workspace = workspaces.getWorkspace(workspaceId);
|
|
748
|
+
if (input.path)
|
|
749
|
+
workspaces.resolvePath(workspace, input.path);
|
|
750
|
+
const response = await grepFilesTool(input, {
|
|
751
|
+
cwd: workspace.root,
|
|
752
|
+
root: workspace.root,
|
|
753
|
+
});
|
|
754
|
+
if (response.isError) {
|
|
755
|
+
logFailedToolResponse(config, {
|
|
756
|
+
tool: toolNames.grep,
|
|
757
|
+
workspaceId,
|
|
758
|
+
path: input.path,
|
|
759
|
+
}, response.content, startedAt);
|
|
760
|
+
return response;
|
|
761
|
+
}
|
|
762
|
+
const summary = {
|
|
763
|
+
pattern: input.pattern,
|
|
764
|
+
scope: input.path ?? ".",
|
|
765
|
+
...textSummary(response.content),
|
|
766
|
+
};
|
|
767
|
+
logToolCall(config, {
|
|
768
|
+
tool: toolNames.grep,
|
|
769
|
+
workspaceId,
|
|
770
|
+
path: input.path,
|
|
771
|
+
success: true,
|
|
772
|
+
durationMs: Math.round(performance.now() - startedAt),
|
|
773
|
+
});
|
|
774
|
+
return {
|
|
775
|
+
...response,
|
|
776
|
+
_meta: {
|
|
777
|
+
tool: toolNames.grep,
|
|
778
|
+
card: {
|
|
779
|
+
workspaceId,
|
|
780
|
+
path: input.path,
|
|
781
|
+
summary,
|
|
782
|
+
payload: { content: response.content },
|
|
783
|
+
},
|
|
784
|
+
},
|
|
785
|
+
structuredContent: {
|
|
786
|
+
result: contentText(response.content),
|
|
787
|
+
},
|
|
788
|
+
};
|
|
789
|
+
});
|
|
790
|
+
registerAppTool(server, toolNames.glob, {
|
|
791
|
+
title: config.toolNaming === "short" ? "Glob" : "Find files",
|
|
792
|
+
description: "Find files by glob pattern inside an open workspace. Use this to discover filenames or narrow file sets before reading. Respects project ignore rules. Call open_workspace first and pass workspaceId.",
|
|
793
|
+
inputSchema: {
|
|
794
|
+
workspaceId: z
|
|
795
|
+
.string()
|
|
796
|
+
.describe("Workspace identifier returned by open_workspace."),
|
|
797
|
+
pattern: z.string().describe("File glob pattern."),
|
|
798
|
+
path: z
|
|
799
|
+
.string()
|
|
800
|
+
.optional()
|
|
801
|
+
.describe("Optional path scope relative to the workspace root."),
|
|
802
|
+
},
|
|
803
|
+
outputSchema: resultOutputSchema(),
|
|
804
|
+
...toolWidgetDescriptorMeta(config, "search"),
|
|
805
|
+
annotations: { readOnlyHint: true },
|
|
806
|
+
}, async ({ workspaceId, ...input }) => {
|
|
807
|
+
const startedAt = performance.now();
|
|
808
|
+
const workspace = workspaces.getWorkspace(workspaceId);
|
|
809
|
+
if (input.path)
|
|
810
|
+
workspaces.resolvePath(workspace, input.path);
|
|
811
|
+
const response = await findFilesTool(input, {
|
|
812
|
+
cwd: workspace.root,
|
|
813
|
+
root: workspace.root,
|
|
814
|
+
});
|
|
815
|
+
if (response.isError) {
|
|
816
|
+
logFailedToolResponse(config, {
|
|
817
|
+
tool: toolNames.glob,
|
|
818
|
+
workspaceId,
|
|
819
|
+
path: input.path,
|
|
820
|
+
}, response.content, startedAt);
|
|
821
|
+
return response;
|
|
822
|
+
}
|
|
823
|
+
const summary = {
|
|
824
|
+
pattern: input.pattern,
|
|
825
|
+
scope: input.path ?? ".",
|
|
826
|
+
...textSummary(response.content),
|
|
827
|
+
};
|
|
828
|
+
logToolCall(config, {
|
|
829
|
+
tool: toolNames.glob,
|
|
830
|
+
workspaceId,
|
|
831
|
+
path: input.path,
|
|
832
|
+
success: true,
|
|
833
|
+
durationMs: Math.round(performance.now() - startedAt),
|
|
834
|
+
});
|
|
835
|
+
return {
|
|
836
|
+
...response,
|
|
837
|
+
_meta: {
|
|
838
|
+
tool: toolNames.glob,
|
|
839
|
+
card: {
|
|
840
|
+
workspaceId,
|
|
841
|
+
path: input.path,
|
|
842
|
+
summary,
|
|
843
|
+
payload: { content: response.content },
|
|
844
|
+
},
|
|
845
|
+
},
|
|
846
|
+
structuredContent: {
|
|
847
|
+
result: contentText(response.content),
|
|
848
|
+
},
|
|
849
|
+
};
|
|
850
|
+
});
|
|
851
|
+
registerAppTool(server, toolNames.ls, {
|
|
852
|
+
title: config.toolNaming === "short" ? "Ls" : "List directory",
|
|
853
|
+
description: "List a directory inside an open workspace. Use this for directory inspection before reading files. Call open_workspace first and pass workspaceId.",
|
|
854
|
+
inputSchema: {
|
|
855
|
+
workspaceId: z
|
|
856
|
+
.string()
|
|
857
|
+
.describe("Workspace identifier returned by open_workspace."),
|
|
858
|
+
path: z
|
|
859
|
+
.string()
|
|
860
|
+
.describe("Directory path to list, relative to the workspace root."),
|
|
861
|
+
},
|
|
862
|
+
outputSchema: resultOutputSchema(),
|
|
863
|
+
...toolWidgetDescriptorMeta(config, "directory"),
|
|
864
|
+
annotations: { readOnlyHint: true },
|
|
865
|
+
}, async ({ workspaceId, ...input }) => {
|
|
866
|
+
const startedAt = performance.now();
|
|
867
|
+
const workspace = workspaces.getWorkspace(workspaceId);
|
|
868
|
+
workspaces.resolvePath(workspace, input.path);
|
|
869
|
+
const response = await listDirectoryTool(input, {
|
|
870
|
+
cwd: workspace.root,
|
|
871
|
+
root: workspace.root,
|
|
872
|
+
});
|
|
873
|
+
if (response.isError) {
|
|
874
|
+
logFailedToolResponse(config, {
|
|
875
|
+
tool: toolNames.ls,
|
|
876
|
+
workspaceId,
|
|
877
|
+
path: input.path,
|
|
878
|
+
}, response.content, startedAt);
|
|
879
|
+
return response;
|
|
880
|
+
}
|
|
881
|
+
const summary = textSummary(response.content);
|
|
882
|
+
logToolCall(config, {
|
|
883
|
+
tool: toolNames.ls,
|
|
884
|
+
workspaceId,
|
|
885
|
+
path: input.path,
|
|
886
|
+
success: true,
|
|
887
|
+
durationMs: Math.round(performance.now() - startedAt),
|
|
888
|
+
});
|
|
889
|
+
return {
|
|
890
|
+
...response,
|
|
891
|
+
_meta: {
|
|
892
|
+
tool: toolNames.ls,
|
|
893
|
+
card: {
|
|
894
|
+
workspaceId,
|
|
895
|
+
path: input.path,
|
|
896
|
+
summary,
|
|
897
|
+
payload: { content: response.content },
|
|
898
|
+
},
|
|
899
|
+
},
|
|
900
|
+
structuredContent: {
|
|
901
|
+
result: contentText(response.content),
|
|
902
|
+
},
|
|
903
|
+
};
|
|
904
|
+
});
|
|
905
|
+
}
|
|
906
|
+
registerAppTool(server, toolNames.shell, {
|
|
907
|
+
title: config.toolNaming === "short" ? "Bash" : "Run shell",
|
|
908
|
+
description: config.minimalTools
|
|
909
|
+
? `Run a shell command inside an open workspace. Use only for tests, builds, git inspection, package scripts, search, file discovery, and directory inspection. In minimal tool mode, ${toolNames.grep}, ${toolNames.glob}, and ${toolNames.ls} are disabled; use command-line tools such as grep, rg, find, ls, and tree for those read-only inspection actions. Do not use ${toolNames.shell} to create or modify files. Do not use shell redirection, heredocs, tee, sed -i, perl -i, node/python/ruby scripts, or generated scripts to write project files; use ${toolNames.edit} for targeted changes and ${toolNames.write} for new files or full rewrites. Prefer ${toolNames.read} for direct file reads. Call open_workspace first and pass workspaceId. This is powerful local execution and should only be exposed behind strong authentication.`
|
|
910
|
+
: `Run a shell command inside an open workspace. Use only for tests, builds, git inspection, package scripts, and commands that are better executed by the shell. Do not use ${toolNames.shell} to create or modify files. Do not use shell redirection, heredocs, tee, sed -i, perl -i, node/python/ruby scripts, or generated scripts to write project files; use ${toolNames.edit} for targeted changes and ${toolNames.write} for new files or full rewrites. Prefer ${toolNames.read}, ${toolNames.grep}, ${toolNames.glob}, and ${toolNames.ls} for file inspection. Call open_workspace first and pass workspaceId. This is powerful local execution and should only be exposed behind strong authentication.`,
|
|
911
|
+
inputSchema: {
|
|
912
|
+
workspaceId: z
|
|
913
|
+
.string()
|
|
914
|
+
.describe("Workspace identifier returned by open_workspace."),
|
|
915
|
+
command: z
|
|
916
|
+
.string()
|
|
917
|
+
.describe(`Shell command to run. Must not create or modify project files; use ${toolNames.edit} or ${toolNames.write} for file changes.`),
|
|
918
|
+
workingDirectory: z
|
|
919
|
+
.string()
|
|
920
|
+
.optional()
|
|
921
|
+
.describe("Optional working directory relative to the workspace root. Defaults to the workspace root."),
|
|
922
|
+
timeout: z
|
|
923
|
+
.number()
|
|
924
|
+
.positive()
|
|
925
|
+
.max(300)
|
|
926
|
+
.optional()
|
|
927
|
+
.describe("Timeout in seconds. Defaults to 30, max 300."),
|
|
928
|
+
},
|
|
929
|
+
outputSchema: resultOutputSchema(),
|
|
930
|
+
...toolWidgetDescriptorMeta(config, "shell"),
|
|
931
|
+
annotations: SHELL_TOOL_ANNOTATIONS,
|
|
932
|
+
}, async ({ workspaceId, workingDirectory, ...input }) => {
|
|
933
|
+
const startedAt = performance.now();
|
|
934
|
+
const workspace = workspaces.getWorkspace(workspaceId);
|
|
935
|
+
const cwd = workspaces.resolveWorkingDirectory(workspace, workingDirectory);
|
|
936
|
+
const response = await runShellTool(input, {
|
|
937
|
+
cwd,
|
|
938
|
+
root: workspace.root,
|
|
939
|
+
});
|
|
940
|
+
if (response.isError) {
|
|
941
|
+
logFailedToolResponse(config, {
|
|
942
|
+
tool: toolNames.shell,
|
|
943
|
+
workspaceId,
|
|
944
|
+
workingDirectory: workingDirectory ?? ".",
|
|
945
|
+
command: input.command,
|
|
946
|
+
commandLength: input.command.length,
|
|
947
|
+
}, response.content, startedAt);
|
|
948
|
+
return response;
|
|
949
|
+
}
|
|
950
|
+
const summary = {
|
|
951
|
+
command: input.command,
|
|
952
|
+
workingDirectory: workingDirectory ?? ".",
|
|
953
|
+
...textSummary(response.content),
|
|
954
|
+
};
|
|
955
|
+
logToolCall(config, {
|
|
956
|
+
tool: toolNames.shell,
|
|
957
|
+
workspaceId,
|
|
958
|
+
workingDirectory: workingDirectory ?? ".",
|
|
959
|
+
command: input.command,
|
|
960
|
+
commandLength: input.command.length,
|
|
961
|
+
success: true,
|
|
962
|
+
durationMs: Math.round(performance.now() - startedAt),
|
|
963
|
+
});
|
|
964
|
+
return {
|
|
965
|
+
...response,
|
|
966
|
+
_meta: {
|
|
967
|
+
tool: toolNames.shell,
|
|
968
|
+
card: {
|
|
969
|
+
workspaceId,
|
|
970
|
+
path: workingDirectory,
|
|
971
|
+
summary,
|
|
972
|
+
payload: { content: response.content },
|
|
973
|
+
},
|
|
974
|
+
},
|
|
975
|
+
structuredContent: {
|
|
976
|
+
result: contentText(response.content),
|
|
977
|
+
},
|
|
978
|
+
};
|
|
979
|
+
});
|
|
980
|
+
return server;
|
|
981
|
+
}
|
|
982
|
+
export function createServer(config = loadConfig()) {
|
|
983
|
+
const allowedHosts = config.allowedHosts.includes("*")
|
|
984
|
+
? undefined
|
|
985
|
+
: Array.from(new Set([config.host, ...config.allowedHosts]));
|
|
986
|
+
const app = createMcpExpressApp({
|
|
987
|
+
host: config.host,
|
|
988
|
+
...(allowedHosts ? { allowedHosts } : {}),
|
|
989
|
+
});
|
|
990
|
+
const transports = new Map();
|
|
991
|
+
const mcpUrl = new URL("/mcp", config.publicBaseUrl);
|
|
992
|
+
const resourceServerUrl = resourceUrlFromServerUrl(mcpUrl);
|
|
993
|
+
const oauthProvider = new SingleUserOAuthProvider(config.oauth, mcpUrl, config.stateDir);
|
|
994
|
+
const bearerAuth = requireBearerAuth({
|
|
995
|
+
verifier: oauthProvider,
|
|
996
|
+
requiredScopes: [config.oauth.scopes[0] ?? "devspace"],
|
|
997
|
+
resourceMetadataUrl: getOAuthProtectedResourceMetadataUrl(resourceServerUrl),
|
|
998
|
+
});
|
|
999
|
+
const workspaceStore = createWorkspaceStore(config.stateDir);
|
|
1000
|
+
const workspaces = new WorkspaceRegistry(config, workspaceStore);
|
|
1001
|
+
const reviewCheckpoints = createReviewCheckpointManager();
|
|
1002
|
+
if (config.logging.trustProxy) {
|
|
1003
|
+
app.set("trust proxy", true);
|
|
1004
|
+
}
|
|
1005
|
+
app.use((req, res, next) => {
|
|
1006
|
+
const requestId = randomUUID();
|
|
1007
|
+
const startedAt = performance.now();
|
|
1008
|
+
res.locals.requestId = requestId;
|
|
1009
|
+
res.on("finish", () => {
|
|
1010
|
+
const path = requestPath(req);
|
|
1011
|
+
if (!config.logging.requests)
|
|
1012
|
+
return;
|
|
1013
|
+
if (!config.logging.assets && path.startsWith("/mcp-app-assets"))
|
|
1014
|
+
return;
|
|
1015
|
+
logEvent(config.logging, "info", "http_request", {
|
|
1016
|
+
requestId,
|
|
1017
|
+
method: req.method,
|
|
1018
|
+
path,
|
|
1019
|
+
status: res.statusCode,
|
|
1020
|
+
durationMs: Math.round(performance.now() - startedAt),
|
|
1021
|
+
...requestLogFields(req, config),
|
|
1022
|
+
});
|
|
1023
|
+
});
|
|
1024
|
+
next();
|
|
1025
|
+
});
|
|
1026
|
+
app.use(mcpAuthRouter({
|
|
1027
|
+
provider: oauthProvider,
|
|
1028
|
+
issuerUrl: new URL(config.publicBaseUrl),
|
|
1029
|
+
baseUrl: new URL(config.publicBaseUrl),
|
|
1030
|
+
resourceServerUrl,
|
|
1031
|
+
scopesSupported: config.oauth.scopes,
|
|
1032
|
+
resourceName: "DevSpace",
|
|
1033
|
+
}));
|
|
1034
|
+
app.get("/.well-known/oauth-protected-resource", (_req, res) => {
|
|
1035
|
+
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
1036
|
+
res.json({
|
|
1037
|
+
resource: resourceServerUrl.href,
|
|
1038
|
+
authorization_servers: [config.publicBaseUrl],
|
|
1039
|
+
scopes_supported: config.oauth.scopes,
|
|
1040
|
+
resource_name: "DevSpace",
|
|
1041
|
+
});
|
|
1042
|
+
});
|
|
1043
|
+
app.options("/mcp-app-assets/{*asset}", (_req, res) => {
|
|
1044
|
+
setAssetHeaders(res);
|
|
1045
|
+
res.sendStatus(204);
|
|
1046
|
+
});
|
|
1047
|
+
app.use("/mcp-app-assets", express.static(uiBuildDirectory(), {
|
|
1048
|
+
immutable: true,
|
|
1049
|
+
maxAge: "1y",
|
|
1050
|
+
fallthrough: false,
|
|
1051
|
+
setHeaders: setAssetHeaders,
|
|
1052
|
+
}));
|
|
1053
|
+
app.get("/", (_req, res) => {
|
|
1054
|
+
res.redirect(302, "/dashboard");
|
|
1055
|
+
});
|
|
1056
|
+
app.get("/dashboard", (_req, res) => {
|
|
1057
|
+
res.type("html").send(renderDashboardHtml());
|
|
1058
|
+
});
|
|
1059
|
+
app.use("/dashboard/api", express.json({ limit: "64kb" }));
|
|
1060
|
+
app.get("/dashboard/api/config", (req, res) => {
|
|
1061
|
+
res.json({ ok: true, ...dashboardConfigPayload(config) });
|
|
1062
|
+
});
|
|
1063
|
+
app.put("/dashboard/api/config", (req, res) => {
|
|
1064
|
+
try {
|
|
1065
|
+
const saved = saveDashboardConfigFromBody(req.body);
|
|
1066
|
+
res.json({
|
|
1067
|
+
ok: true,
|
|
1068
|
+
configPath: saved.configPath,
|
|
1069
|
+
persisted: saved.config,
|
|
1070
|
+
restartRequired: true,
|
|
1071
|
+
});
|
|
1072
|
+
}
|
|
1073
|
+
catch (error) {
|
|
1074
|
+
res.status(400).json({
|
|
1075
|
+
ok: false,
|
|
1076
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1077
|
+
});
|
|
1078
|
+
}
|
|
1079
|
+
});
|
|
1080
|
+
app.get("/healthz", (_req, res) => {
|
|
1081
|
+
res.json({ ok: true, name: "devspace", dashboard: "/dashboard" });
|
|
1082
|
+
});
|
|
1083
|
+
app.all("/mcp", async (req, res) => {
|
|
1084
|
+
const requestId = res.locals.requestId;
|
|
1085
|
+
const sessionId = req.header("mcp-session-id");
|
|
1086
|
+
const initializeRequest = req.method === "POST" && isInitializeRequest(req.body);
|
|
1087
|
+
await new Promise((resolve, reject) => {
|
|
1088
|
+
bearerAuth(req, res, (error) => {
|
|
1089
|
+
if (error)
|
|
1090
|
+
reject(error);
|
|
1091
|
+
else
|
|
1092
|
+
resolve();
|
|
1093
|
+
});
|
|
1094
|
+
});
|
|
1095
|
+
if (res.headersSent)
|
|
1096
|
+
return;
|
|
1097
|
+
if (!req.auth?.resource || !checkResourceAllowed({ requestedResource: req.auth.resource, configuredResource: resourceServerUrl })) {
|
|
1098
|
+
logEvent(config.logging, "warn", "auth_denied", {
|
|
1099
|
+
requestId,
|
|
1100
|
+
method: req.method,
|
|
1101
|
+
path: requestPath(req),
|
|
1102
|
+
reason: "invalid_oauth_resource",
|
|
1103
|
+
...requestLogFields(req, config),
|
|
1104
|
+
});
|
|
1105
|
+
sendJsonRpcError(res, 401, -32001, "Unauthorized");
|
|
1106
|
+
return;
|
|
1107
|
+
}
|
|
1108
|
+
logEvent(config.logging, "debug", "mcp_request", {
|
|
1109
|
+
requestId,
|
|
1110
|
+
method: req.method,
|
|
1111
|
+
sessionIdPresent: Boolean(sessionId),
|
|
1112
|
+
sessionIdPrefix: sessionIdPrefix(sessionId),
|
|
1113
|
+
isInitialize: initializeRequest,
|
|
1114
|
+
});
|
|
1115
|
+
try {
|
|
1116
|
+
let transport;
|
|
1117
|
+
if (sessionId) {
|
|
1118
|
+
transport = transports.get(sessionId);
|
|
1119
|
+
if (!transport) {
|
|
1120
|
+
sendJsonRpcError(res, 404, -32000, "Unknown MCP session");
|
|
1121
|
+
return;
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1124
|
+
else if (initializeRequest) {
|
|
1125
|
+
transport = new StreamableHTTPServerTransport({
|
|
1126
|
+
sessionIdGenerator: () => randomUUID(),
|
|
1127
|
+
onsessioninitialized: (newSessionId) => {
|
|
1128
|
+
if (transport)
|
|
1129
|
+
transports.set(newSessionId, transport);
|
|
1130
|
+
logEvent(config.logging, "info", "mcp_session_created", {
|
|
1131
|
+
requestId,
|
|
1132
|
+
sessionIdPrefix: sessionIdPrefix(newSessionId),
|
|
1133
|
+
...requestLogFields(req, config),
|
|
1134
|
+
});
|
|
1135
|
+
},
|
|
1136
|
+
});
|
|
1137
|
+
transport.onclose = () => {
|
|
1138
|
+
const closedSessionId = transport?.sessionId;
|
|
1139
|
+
if (closedSessionId) {
|
|
1140
|
+
transports.delete(closedSessionId);
|
|
1141
|
+
logEvent(config.logging, "info", "mcp_session_closed", {
|
|
1142
|
+
sessionIdPrefix: sessionIdPrefix(closedSessionId),
|
|
1143
|
+
});
|
|
1144
|
+
}
|
|
1145
|
+
};
|
|
1146
|
+
const server = createMcpServer(config, workspaces, reviewCheckpoints);
|
|
1147
|
+
await server.connect(transport);
|
|
1148
|
+
}
|
|
1149
|
+
else {
|
|
1150
|
+
sendJsonRpcError(res, 400, -32000, "No valid MCP session");
|
|
1151
|
+
return;
|
|
1152
|
+
}
|
|
1153
|
+
await transport.handleRequest(req, res, req.body);
|
|
1154
|
+
}
|
|
1155
|
+
catch (error) {
|
|
1156
|
+
logEvent(config.logging, "error", "mcp_request_error", {
|
|
1157
|
+
requestId,
|
|
1158
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1159
|
+
});
|
|
1160
|
+
if (!res.headersSent) {
|
|
1161
|
+
sendJsonRpcError(res, 500, -32603, "Internal server error");
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1164
|
+
});
|
|
1165
|
+
let closed = false;
|
|
1166
|
+
return {
|
|
1167
|
+
app,
|
|
1168
|
+
config,
|
|
1169
|
+
close: () => {
|
|
1170
|
+
if (closed)
|
|
1171
|
+
return;
|
|
1172
|
+
closed = true;
|
|
1173
|
+
oauthProvider.close();
|
|
1174
|
+
workspaceStore.close?.();
|
|
1175
|
+
},
|
|
1176
|
+
};
|
|
1177
|
+
}
|
|
1178
|
+
async function isMainModule() {
|
|
1179
|
+
if (!process.argv[1])
|
|
1180
|
+
return false;
|
|
1181
|
+
const modulePath = await realpath(fileURLToPath(import.meta.url));
|
|
1182
|
+
const entrypointPath = await realpath(process.argv[1]);
|
|
1183
|
+
return modulePath === entrypointPath;
|
|
1184
|
+
}
|
|
1185
|
+
if (await isMainModule()) {
|
|
1186
|
+
const { app, config, close } = createServer();
|
|
1187
|
+
const httpServer = app.listen(config.port, config.host, () => {
|
|
1188
|
+
console.log(`devspace listening on http://${config.host}:${config.port}/mcp`);
|
|
1189
|
+
console.log(`allowed roots: ${config.allowedRoots.join(", ")}`);
|
|
1190
|
+
console.log("auth: oauth owner-token flow required");
|
|
1191
|
+
console.log(`logging: ${config.logging.level} ${config.logging.format}`);
|
|
1192
|
+
console.log(`request logging: ${config.logging.requests ? "enabled" : "disabled"}`);
|
|
1193
|
+
console.log(`asset logging: ${config.logging.assets ? "enabled" : "disabled"}`);
|
|
1194
|
+
console.log(`trust proxy: ${config.logging.trustProxy ? "enabled" : "disabled"}`);
|
|
1195
|
+
});
|
|
1196
|
+
const shutdown = () => {
|
|
1197
|
+
httpServer.close(() => {
|
|
1198
|
+
close();
|
|
1199
|
+
process.exit(0);
|
|
1200
|
+
});
|
|
1201
|
+
};
|
|
1202
|
+
process.once("SIGINT", shutdown);
|
|
1203
|
+
process.once("SIGTERM", shutdown);
|
|
1204
|
+
}
|