openspecui 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.mjs +17 -16
- package/dist/index.mjs +1 -1
- package/dist/{open-CmiqsyCQ.mjs → open-Cw_l4kh9.mjs} +11 -11
- package/dist/{src-Bmm_kaB8.mjs → src-BS3LYQ6T.mjs} +1012 -109
- package/package.json +6 -5
- package/web/assets/index-DU-ty3pA.css +1 -0
- package/web/assets/index-noSlrtA-.js +337 -0
- package/web/index.html +4 -4
- package/web/logo.svg +8 -0
- package/web/openspec_pixel_dark.svg +89 -0
- package/web/openspec_pixel_light.svg +89 -0
- package/web/assets/index-BhzIZ_eQ.js +0 -150
- package/web/assets/index-D6x5Q1lW.css +0 -1
- package/web/dist/assets/index-BNmb5C1F.css +0 -1
- package/web/dist/assets/index-BhzIZ_eQ.js +0 -150
- package/web/dist/assets/index-BxBbLX3V.js +0 -170
- package/web/dist/assets/index-C4c5ARU5.css +0 -1
- package/web/dist/assets/index-CqKOZnDE.css +0 -1
- package/web/dist/assets/index-D6x5Q1lW.css +0 -1
- package/web/dist/assets/index-Om_P2_SF.js +0 -155
- package/web/dist/assets/index-l4gcUD8y.js +0 -382
- package/web/dist/index.html +0 -14
- /package/web/{dist/assets → assets}/abap-BdImnpbu.js +0 -0
- /package/web/{dist/assets → assets}/actionscript-3-CfeIJUat.js +0 -0
- /package/web/{dist/assets → assets}/ada-bCR0ucgS.js +0 -0
- /package/web/{dist/assets → assets}/andromeeda-C-Jbm3Hp.js +0 -0
- /package/web/{dist/assets → assets}/angular-html-CU67Zn6k.js +0 -0
- /package/web/{dist/assets → assets}/angular-ts-BwZT4LLn.js +0 -0
- /package/web/{dist/assets → assets}/apache-Pmp26Uib.js +0 -0
- /package/web/{dist/assets → assets}/apex-DDbsPZ6N.js +0 -0
- /package/web/{dist/assets → assets}/apl-dKokRX4l.js +0 -0
- /package/web/{dist/assets → assets}/applescript-Co6uUVPk.js +0 -0
- /package/web/{dist/assets → assets}/ara-BRHolxvo.js +0 -0
- /package/web/{dist/assets → assets}/asciidoc-Dv7Oe6Be.js +0 -0
- /package/web/{dist/assets → assets}/asm-D_Q5rh1f.js +0 -0
- /package/web/{dist/assets → assets}/astro-CbQHKStN.js +0 -0
- /package/web/{dist/assets → assets}/aurora-x-D-2ljcwZ.js +0 -0
- /package/web/{dist/assets → assets}/awk-DMzUqQB5.js +0 -0
- /package/web/{dist/assets → assets}/ayu-dark-Cv9koXgw.js +0 -0
- /package/web/{dist/assets → assets}/ballerina-BFfxhgS-.js +0 -0
- /package/web/{dist/assets → assets}/bat-BkioyH1T.js +0 -0
- /package/web/{dist/assets → assets}/beancount-k_qm7-4y.js +0 -0
- /package/web/{dist/assets → assets}/berry-uYugtg8r.js +0 -0
- /package/web/{dist/assets → assets}/bibtex-CHM0blh-.js +0 -0
- /package/web/{dist/assets → assets}/bicep-Bmn6On1c.js +0 -0
- /package/web/{dist/assets → assets}/blade-DVc8C-J4.js +0 -0
- /package/web/{dist/assets → assets}/bsl-BO_Y6i37.js +0 -0
- /package/web/{dist/assets → assets}/c-BIGW1oBm.js +0 -0
- /package/web/{dist/assets → assets}/cadence-Bv_4Rxtq.js +0 -0
- /package/web/{dist/assets → assets}/cairo-KRGpt6FW.js +0 -0
- /package/web/{dist/assets → assets}/catppuccin-frappe-DFWUc33u.js +0 -0
- /package/web/{dist/assets → assets}/catppuccin-latte-C9dUb6Cb.js +0 -0
- /package/web/{dist/assets → assets}/catppuccin-macchiato-DQyhUUbL.js +0 -0
- /package/web/{dist/assets → assets}/catppuccin-mocha-D87Tk5Gz.js +0 -0
- /package/web/{dist/assets → assets}/clarity-D53aC0YG.js +0 -0
- /package/web/{dist/assets → assets}/clojure-P80f7IUj.js +0 -0
- /package/web/{dist/assets → assets}/cmake-D1j8_8rp.js +0 -0
- /package/web/{dist/assets → assets}/cobol-nwyudZeR.js +0 -0
- /package/web/{dist/assets → assets}/codeowners-Bp6g37R7.js +0 -0
- /package/web/{dist/assets → assets}/codeql-DsOJ9woJ.js +0 -0
- /package/web/{dist/assets → assets}/coffee-Ch7k5sss.js +0 -0
- /package/web/{dist/assets → assets}/common-lisp-Cg-RD9OK.js +0 -0
- /package/web/{dist/assets → assets}/coq-DkFqJrB1.js +0 -0
- /package/web/{dist/assets → assets}/cpp-CofmeUqb.js +0 -0
- /package/web/{dist/assets → assets}/crystal-tKQVLTB8.js +0 -0
- /package/web/{dist/assets → assets}/csharp-K5feNrxe.js +0 -0
- /package/web/{dist/assets → assets}/css-DPfMkruS.js +0 -0
- /package/web/{dist/assets → assets}/csv-fuZLfV_i.js +0 -0
- /package/web/{dist/assets → assets}/cue-D82EKSYY.js +0 -0
- /package/web/{dist/assets → assets}/cypher-COkxafJQ.js +0 -0
- /package/web/{dist/assets → assets}/d-85-TOEBH.js +0 -0
- /package/web/{dist/assets → assets}/dark-plus-C3mMm8J8.js +0 -0
- /package/web/{dist/assets → assets}/dart-CF10PKvl.js +0 -0
- /package/web/{dist/assets → assets}/dax-CEL-wOlO.js +0 -0
- /package/web/{dist/assets → assets}/desktop-BmXAJ9_W.js +0 -0
- /package/web/{dist/assets → assets}/diff-D97Zzqfu.js +0 -0
- /package/web/{dist/assets → assets}/docker-BcOcwvcX.js +0 -0
- /package/web/{dist/assets → assets}/dotenv-Da5cRb03.js +0 -0
- /package/web/{dist/assets → assets}/dracula-BzJJZx-M.js +0 -0
- /package/web/{dist/assets → assets}/dracula-soft-BXkSAIEj.js +0 -0
- /package/web/{dist/assets → assets}/dream-maker-BtqSS_iP.js +0 -0
- /package/web/{dist/assets → assets}/edge-BkV0erSs.js +0 -0
- /package/web/{dist/assets → assets}/elixir-CDX3lj18.js +0 -0
- /package/web/{dist/assets → assets}/elm-DbKCFpqz.js +0 -0
- /package/web/{dist/assets → assets}/emacs-lisp-C9XAeP06.js +0 -0
- /package/web/{dist/assets → assets}/erb-BOJIQeun.js +0 -0
- /package/web/{dist/assets → assets}/erlang-DsQrWhSR.js +0 -0
- /package/web/{dist/assets → assets}/everforest-dark-BgDCqdQA.js +0 -0
- /package/web/{dist/assets → assets}/everforest-light-C8M2exoo.js +0 -0
- /package/web/{dist/assets → assets}/fennel-BYunw83y.js +0 -0
- /package/web/{dist/assets → assets}/fish-BvzEVeQv.js +0 -0
- /package/web/{dist/assets → assets}/fluent-C4IJs8-o.js +0 -0
- /package/web/{dist/assets → assets}/fortran-fixed-form-BZjJHVRy.js +0 -0
- /package/web/{dist/assets → assets}/fortran-free-form-D22FLkUw.js +0 -0
- /package/web/{dist/assets → assets}/fsharp-CXgrBDvD.js +0 -0
- /package/web/{dist/assets → assets}/gdresource-B7Tvp0Sc.js +0 -0
- /package/web/{dist/assets → assets}/gdscript-DTMYz4Jt.js +0 -0
- /package/web/{dist/assets → assets}/gdshader-DkwncUOv.js +0 -0
- /package/web/{dist/assets → assets}/genie-D0YGMca9.js +0 -0
- /package/web/{dist/assets → assets}/gherkin-DyxjwDmM.js +0 -0
- /package/web/{dist/assets → assets}/git-commit-F4YmCXRG.js +0 -0
- /package/web/{dist/assets → assets}/git-rebase-r7XF79zn.js +0 -0
- /package/web/{dist/assets → assets}/github-dark-DHJKELXO.js +0 -0
- /package/web/{dist/assets → assets}/github-dark-default-Cuk6v7N8.js +0 -0
- /package/web/{dist/assets → assets}/github-dark-dimmed-DH5Ifo-i.js +0 -0
- /package/web/{dist/assets → assets}/github-dark-high-contrast-E3gJ1_iC.js +0 -0
- /package/web/{dist/assets → assets}/github-light-DAi9KRSo.js +0 -0
- /package/web/{dist/assets → assets}/github-light-default-D7oLnXFd.js +0 -0
- /package/web/{dist/assets → assets}/github-light-high-contrast-BfjtVDDH.js +0 -0
- /package/web/{dist/assets → assets}/gleam-BspZqrRM.js +0 -0
- /package/web/{dist/assets → assets}/glimmer-js-Rg0-pVw9.js +0 -0
- /package/web/{dist/assets → assets}/glimmer-ts-U6CK756n.js +0 -0
- /package/web/{dist/assets → assets}/glsl-DplSGwfg.js +0 -0
- /package/web/{dist/assets → assets}/gnuplot-DdkO51Og.js +0 -0
- /package/web/{dist/assets → assets}/go-Dn2_MT6a.js +0 -0
- /package/web/{dist/assets → assets}/graphql-ChdNCCLP.js +0 -0
- /package/web/{dist/assets → assets}/groovy-gcz8RCvz.js +0 -0
- /package/web/{dist/assets → assets}/gruvbox-dark-hard-CFHQjOhq.js +0 -0
- /package/web/{dist/assets → assets}/gruvbox-dark-medium-GsRaNv29.js +0 -0
- /package/web/{dist/assets → assets}/gruvbox-dark-soft-CVdnzihN.js +0 -0
- /package/web/{dist/assets → assets}/gruvbox-light-hard-CH1njM8p.js +0 -0
- /package/web/{dist/assets → assets}/gruvbox-light-medium-DRw_LuNl.js +0 -0
- /package/web/{dist/assets → assets}/gruvbox-light-soft-hJgmCMqR.js +0 -0
- /package/web/{dist/assets → assets}/hack-CaT9iCJl.js +0 -0
- /package/web/{dist/assets → assets}/haml-B8DHNrY2.js +0 -0
- /package/web/{dist/assets → assets}/handlebars-BL8al0AC.js +0 -0
- /package/web/{dist/assets → assets}/haskell-Df6bDoY_.js +0 -0
- /package/web/{dist/assets → assets}/haxe-CzTSHFRz.js +0 -0
- /package/web/{dist/assets → assets}/hcl-BWvSN4gD.js +0 -0
- /package/web/{dist/assets → assets}/hjson-D5-asLiD.js +0 -0
- /package/web/{dist/assets → assets}/hlsl-D3lLCCz7.js +0 -0
- /package/web/{dist/assets → assets}/houston-DnULxvSX.js +0 -0
- /package/web/{dist/assets → assets}/html-GMplVEZG.js +0 -0
- /package/web/{dist/assets → assets}/html-derivative-BFtXZ54Q.js +0 -0
- /package/web/{dist/assets → assets}/http-jrhK8wxY.js +0 -0
- /package/web/{dist/assets → assets}/hurl-irOxFIW8.js +0 -0
- /package/web/{dist/assets → assets}/hxml-Bvhsp5Yf.js +0 -0
- /package/web/{dist/assets → assets}/hy-DFXneXwc.js +0 -0
- /package/web/{dist/assets → assets}/imba-DGztddWO.js +0 -0
- /package/web/{dist/assets → assets}/ini-BEwlwnbL.js +0 -0
- /package/web/{dist/assets → assets}/java-CylS5w8V.js +0 -0
- /package/web/{dist/assets → assets}/javascript-wDzz0qaB.js +0 -0
- /package/web/{dist/assets → assets}/jinja-4LBKfQ-Z.js +0 -0
- /package/web/{dist/assets → assets}/jison-wvAkD_A8.js +0 -0
- /package/web/{dist/assets → assets}/json-Cp-IABpG.js +0 -0
- /package/web/{dist/assets → assets}/json5-C9tS-k6U.js +0 -0
- /package/web/{dist/assets → assets}/jsonc-Des-eS-w.js +0 -0
- /package/web/{dist/assets → assets}/jsonl-DcaNXYhu.js +0 -0
- /package/web/{dist/assets → assets}/jsonnet-DFQXde-d.js +0 -0
- /package/web/{dist/assets → assets}/jssm-C2t-YnRu.js +0 -0
- /package/web/{dist/assets → assets}/jsx-g9-lgVsj.js +0 -0
- /package/web/{dist/assets → assets}/julia-C8NyazO9.js +0 -0
- /package/web/{dist/assets → assets}/kanagawa-dragon-CkXjmgJE.js +0 -0
- /package/web/{dist/assets → assets}/kanagawa-lotus-CfQXZHmo.js +0 -0
- /package/web/{dist/assets → assets}/kanagawa-wave-DWedfzmr.js +0 -0
- /package/web/{dist/assets → assets}/kdl-DV7GczEv.js +0 -0
- /package/web/{dist/assets → assets}/kotlin-BdnUsdx6.js +0 -0
- /package/web/{dist/assets → assets}/kusto-BvAqAH-y.js +0 -0
- /package/web/{dist/assets → assets}/laserwave-DUszq2jm.js +0 -0
- /package/web/{dist/assets → assets}/latex-BdAV_C_H.js +0 -0
- /package/web/{dist/assets → assets}/lean-Bc6EcWN3.js +0 -0
- /package/web/{dist/assets → assets}/less-B1dDrJ26.js +0 -0
- /package/web/{dist/assets → assets}/light-plus-B7mTdjB0.js +0 -0
- /package/web/{dist/assets → assets}/liquid-DYVedYrR.js +0 -0
- /package/web/{dist/assets → assets}/llvm-BtvRca6l.js +0 -0
- /package/web/{dist/assets → assets}/log-2UxHyX5q.js +0 -0
- /package/web/{dist/assets → assets}/logo-BtOb2qkB.js +0 -0
- /package/web/{dist/assets → assets}/lua-BbnMAYS6.js +0 -0
- /package/web/{dist/assets → assets}/luau-CXu1NL6O.js +0 -0
- /package/web/{dist/assets → assets}/make-CHLpvVh8.js +0 -0
- /package/web/{dist/assets → assets}/markdown-Cvjx9yec.js +0 -0
- /package/web/{dist/assets → assets}/marko-CPi9NSCl.js +0 -0
- /package/web/{dist/assets → assets}/material-theme-D5KoaKCx.js +0 -0
- /package/web/{dist/assets → assets}/material-theme-darker-BfHTSMKl.js +0 -0
- /package/web/{dist/assets → assets}/material-theme-lighter-B0m2ddpp.js +0 -0
- /package/web/{dist/assets → assets}/material-theme-ocean-CyktbL80.js +0 -0
- /package/web/{dist/assets → assets}/material-theme-palenight-Csfq5Kiy.js +0 -0
- /package/web/{dist/assets → assets}/matlab-D7o27uSR.js +0 -0
- /package/web/{dist/assets → assets}/mdc-DUICxH0z.js +0 -0
- /package/web/{dist/assets → assets}/mdx-Cmh6b_Ma.js +0 -0
- /package/web/{dist/assets → assets}/mermaid-DKYwYmdq.js +0 -0
- /package/web/{dist/assets → assets}/min-dark-CafNBF8u.js +0 -0
- /package/web/{dist/assets → assets}/min-light-CTRr51gU.js +0 -0
- /package/web/{dist/assets → assets}/mipsasm-CKIfxQSi.js +0 -0
- /package/web/{dist/assets → assets}/mojo-1DNp92w6.js +0 -0
- /package/web/{dist/assets → assets}/monokai-D4h5O-jR.js +0 -0
- /package/web/{dist/assets → assets}/move-Bu9oaDYs.js +0 -0
- /package/web/{dist/assets → assets}/narrat-DRg8JJMk.js +0 -0
- /package/web/{dist/assets → assets}/nextflow-BrzmwbiE.js +0 -0
- /package/web/{dist/assets → assets}/nginx-DknmC5AR.js +0 -0
- /package/web/{dist/assets → assets}/night-owl-C39BiMTA.js +0 -0
- /package/web/{dist/assets → assets}/nim-CVrawwO9.js +0 -0
- /package/web/{dist/assets → assets}/nix-c8nO5XWb.js +0 -0
- /package/web/{dist/assets → assets}/nord-Ddv68eIx.js +0 -0
- /package/web/{dist/assets → assets}/nushell-C-sUppwS.js +0 -0
- /package/web/{dist/assets → assets}/objective-c-DXmwc3jG.js +0 -0
- /package/web/{dist/assets → assets}/objective-cpp-CLxacb5B.js +0 -0
- /package/web/{dist/assets → assets}/ocaml-C0hk2d4L.js +0 -0
- /package/web/{dist/assets → assets}/one-dark-pro-DVMEJ2y_.js +0 -0
- /package/web/{dist/assets → assets}/one-light-PoHY5YXO.js +0 -0
- /package/web/{dist/assets → assets}/openscad-C4EeE6gA.js +0 -0
- /package/web/{dist/assets → assets}/pascal-D93ZcfNL.js +0 -0
- /package/web/{dist/assets → assets}/perl-C0TMdlhV.js +0 -0
- /package/web/{dist/assets → assets}/php-CDn_0X-4.js +0 -0
- /package/web/{dist/assets → assets}/pkl-u5AG7uiY.js +0 -0
- /package/web/{dist/assets → assets}/plastic-3e1v2bzS.js +0 -0
- /package/web/{dist/assets → assets}/plsql-ChMvpjG-.js +0 -0
- /package/web/{dist/assets → assets}/po-BTJTHyun.js +0 -0
- /package/web/{dist/assets → assets}/poimandres-CS3Unz2-.js +0 -0
- /package/web/{dist/assets → assets}/polar-C0HS_06l.js +0 -0
- /package/web/{dist/assets → assets}/postcss-CXtECtnM.js +0 -0
- /package/web/{dist/assets → assets}/powerquery-CEu0bR-o.js +0 -0
- /package/web/{dist/assets → assets}/powershell-Dpen1YoG.js +0 -0
- /package/web/{dist/assets → assets}/prisma-Dd19v3D-.js +0 -0
- /package/web/{dist/assets → assets}/prolog-CbFg5uaA.js +0 -0
- /package/web/{dist/assets → assets}/proto-DyJlTyXw.js +0 -0
- /package/web/{dist/assets → assets}/pug-CGlum2m_.js +0 -0
- /package/web/{dist/assets → assets}/puppet-BMWR74SV.js +0 -0
- /package/web/{dist/assets → assets}/purescript-CklMAg4u.js +0 -0
- /package/web/{dist/assets → assets}/python-B6aJPvgy.js +0 -0
- /package/web/{dist/assets → assets}/qml-3beO22l8.js +0 -0
- /package/web/{dist/assets → assets}/qmldir-C8lEn-DE.js +0 -0
- /package/web/{dist/assets → assets}/qss-IeuSbFQv.js +0 -0
- /package/web/{dist/assets → assets}/r-DiinP2Uv.js +0 -0
- /package/web/{dist/assets → assets}/racket-BqYA7rlc.js +0 -0
- /package/web/{dist/assets → assets}/raku-DXvB9xmW.js +0 -0
- /package/web/{dist/assets → assets}/razor-CE9lU5zL.js +0 -0
- /package/web/{dist/assets → assets}/red-bN70gL4F.js +0 -0
- /package/web/{dist/assets → assets}/reg-C-SQnVFl.js +0 -0
- /package/web/{dist/assets → assets}/regexp-CDVJQ6XC.js +0 -0
- /package/web/{dist/assets → assets}/rel-C3B-1QV4.js +0 -0
- /package/web/{dist/assets → assets}/riscv-BM1_JUlF.js +0 -0
- /package/web/{dist/assets → assets}/rose-pine-dawn-DHQR4-dF.js +0 -0
- /package/web/{dist/assets → assets}/rose-pine-moon-D4_iv3hh.js +0 -0
- /package/web/{dist/assets → assets}/rose-pine-qdsjHGoJ.js +0 -0
- /package/web/{dist/assets → assets}/rosmsg-BJDFO7_C.js +0 -0
- /package/web/{dist/assets → assets}/rst-B0xPkSld.js +0 -0
- /package/web/{dist/assets → assets}/ruby-BvKwtOVI.js +0 -0
- /package/web/{dist/assets → assets}/rust-B1yitclQ.js +0 -0
- /package/web/{dist/assets → assets}/sas-cz2c8ADy.js +0 -0
- /package/web/{dist/assets → assets}/sass-Cj5Yp3dK.js +0 -0
- /package/web/{dist/assets → assets}/scala-C151Ov-r.js +0 -0
- /package/web/{dist/assets → assets}/scheme-C98Dy4si.js +0 -0
- /package/web/{dist/assets → assets}/scss-OYdSNvt2.js +0 -0
- /package/web/{dist/assets → assets}/sdbl-DVxCFoDh.js +0 -0
- /package/web/{dist/assets → assets}/shaderlab-Dg9Lc6iA.js +0 -0
- /package/web/{dist/assets → assets}/shellscript-Yzrsuije.js +0 -0
- /package/web/{dist/assets → assets}/shellsession-BADoaaVG.js +0 -0
- /package/web/{dist/assets → assets}/slack-dark-BthQWCQV.js +0 -0
- /package/web/{dist/assets → assets}/slack-ochin-DqwNpetd.js +0 -0
- /package/web/{dist/assets → assets}/smalltalk-BERRCDM3.js +0 -0
- /package/web/{dist/assets → assets}/snazzy-light-Bw305WKR.js +0 -0
- /package/web/{dist/assets → assets}/solarized-dark-DXbdFlpD.js +0 -0
- /package/web/{dist/assets → assets}/solarized-light-L9t79GZl.js +0 -0
- /package/web/{dist/assets → assets}/solidity-rGO070M0.js +0 -0
- /package/web/{dist/assets → assets}/soy-Brmx7dQM.js +0 -0
- /package/web/{dist/assets → assets}/sparql-rVzFXLq3.js +0 -0
- /package/web/{dist/assets → assets}/splunk-BtCnVYZw.js +0 -0
- /package/web/{dist/assets → assets}/sql-BLtJtn59.js +0 -0
- /package/web/{dist/assets → assets}/ssh-config-_ykCGR6B.js +0 -0
- /package/web/{dist/assets → assets}/stata-BH5u7GGu.js +0 -0
- /package/web/{dist/assets → assets}/stylus-BEDo0Tqx.js +0 -0
- /package/web/{dist/assets → assets}/svelte-3Dk4HxPD.js +0 -0
- /package/web/{dist/assets → assets}/swift-Dg5xB15N.js +0 -0
- /package/web/{dist/assets → assets}/synthwave-84-CbfX1IO0.js +0 -0
- /package/web/{dist/assets → assets}/system-verilog-CnnmHF94.js +0 -0
- /package/web/{dist/assets → assets}/systemd-4A_iFExJ.js +0 -0
- /package/web/{dist/assets → assets}/talonscript-CkByrt1z.js +0 -0
- /package/web/{dist/assets → assets}/tasl-QIJgUcNo.js +0 -0
- /package/web/{dist/assets → assets}/tcl-dwOrl1Do.js +0 -0
- /package/web/{dist/assets → assets}/templ-W15q3VgB.js +0 -0
- /package/web/{dist/assets → assets}/terraform-BETggiCN.js +0 -0
- /package/web/{dist/assets → assets}/tex-CxkMU7Pf.js +0 -0
- /package/web/{dist/assets → assets}/tokyo-night-hegEt444.js +0 -0
- /package/web/{dist/assets → assets}/toml-vGWfd6FD.js +0 -0
- /package/web/{dist/assets → assets}/ts-tags-zn1MmPIZ.js +0 -0
- /package/web/{dist/assets → assets}/tsv-B_m7g4N7.js +0 -0
- /package/web/{dist/assets → assets}/tsx-COt5Ahok.js +0 -0
- /package/web/{dist/assets → assets}/turtle-BsS91CYL.js +0 -0
- /package/web/{dist/assets → assets}/twig-CO9l9SDP.js +0 -0
- /package/web/{dist/assets → assets}/typescript-BPQ3VLAy.js +0 -0
- /package/web/{dist/assets → assets}/typespec-BGHnOYBU.js +0 -0
- /package/web/{dist/assets → assets}/typst-DHCkPAjA.js +0 -0
- /package/web/{dist/assets → assets}/v-BcVCzyr7.js +0 -0
- /package/web/{dist/assets → assets}/vala-CsfeWuGM.js +0 -0
- /package/web/{dist/assets → assets}/vb-D17OF-Vu.js +0 -0
- /package/web/{dist/assets → assets}/verilog-BQ8w6xss.js +0 -0
- /package/web/{dist/assets → assets}/vesper-DU1UobuO.js +0 -0
- /package/web/{dist/assets → assets}/vhdl-CeAyd5Ju.js +0 -0
- /package/web/{dist/assets → assets}/viml-CJc9bBzg.js +0 -0
- /package/web/{dist/assets → assets}/vitesse-black-Bkuqu6BP.js +0 -0
- /package/web/{dist/assets → assets}/vitesse-dark-D0r3Knsf.js +0 -0
- /package/web/{dist/assets → assets}/vitesse-light-CVO1_9PV.js +0 -0
- /package/web/{dist/assets → assets}/vue-DnHKYNfI.js +0 -0
- /package/web/{dist/assets → assets}/vue-html-CChd_i61.js +0 -0
- /package/web/{dist/assets → assets}/vue-vine-8moa0y9V.js +0 -0
- /package/web/{dist/assets → assets}/vyper-CDx5xZoG.js +0 -0
- /package/web/{dist/assets → assets}/wasm-CG6Dc4jp.js +0 -0
- /package/web/{dist/assets → assets}/wasm-MzD3tlZU.js +0 -0
- /package/web/{dist/assets → assets}/wenyan-BV7otONQ.js +0 -0
- /package/web/{dist/assets → assets}/wgsl-Dx-B1_4e.js +0 -0
- /package/web/{dist/assets → assets}/wikitext-BhOHFoWU.js +0 -0
- /package/web/{dist/assets → assets}/wit-5i3qLPDT.js +0 -0
- /package/web/{dist/assets → assets}/wolfram-lXgVvXCa.js +0 -0
- /package/web/{dist/assets → assets}/xml-sdJ4AIDG.js +0 -0
- /package/web/{dist/assets → assets}/xsl-CtQFsRM5.js +0 -0
- /package/web/{dist/assets → assets}/yaml-Buea-lGh.js +0 -0
- /package/web/{dist/assets → assets}/zenscript-DVFEvuxE.js +0 -0
- /package/web/{dist/assets → assets}/zig-VOosw3JB.js +0 -0
|
@@ -4,12 +4,15 @@ import { Http2ServerRequest } from "http2";
|
|
|
4
4
|
import { Readable } from "stream";
|
|
5
5
|
import crypto from "crypto";
|
|
6
6
|
import { EventEmitter } from "events";
|
|
7
|
-
import { mkdir, readFile,
|
|
7
|
+
import { mkdir, readFile, rename, writeFile } from "fs/promises";
|
|
8
8
|
import { join } from "path";
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
9
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
10
|
+
import { readFile as readFile$1, readdir, stat } from "node:fs/promises";
|
|
11
|
+
import { dirname as dirname$1, join as join$1, resolve as resolve$1 } from "node:path";
|
|
12
|
+
import { existsSync, readFileSync, statSync, watch } from "node:fs";
|
|
13
|
+
import { watch as watch$1 } from "fs";
|
|
14
|
+
import { spawn } from "child_process";
|
|
11
15
|
import { createServer as createServer$1 } from "node:net";
|
|
12
|
-
import { dirname as dirname$1, join as join$1 } from "node:path";
|
|
13
16
|
import { fileURLToPath } from "node:url";
|
|
14
17
|
|
|
15
18
|
//#region rolldown:runtime
|
|
@@ -376,7 +379,7 @@ var responseViaResponseObject = async (res, outgoing, options = {}) => {
|
|
|
376
379
|
});
|
|
377
380
|
if (!chunk) {
|
|
378
381
|
if (i === 1) {
|
|
379
|
-
await new Promise((resolve$
|
|
382
|
+
await new Promise((resolve$2) => setTimeout(resolve$2));
|
|
380
383
|
maxReadCount = 3;
|
|
381
384
|
continue;
|
|
382
385
|
}
|
|
@@ -6653,8 +6656,8 @@ var Unpromise = class Unpromise$1 {
|
|
|
6653
6656
|
status: "fulfilled",
|
|
6654
6657
|
value
|
|
6655
6658
|
};
|
|
6656
|
-
subscribers === null || subscribers === void 0 || subscribers.forEach(({ resolve: resolve$
|
|
6657
|
-
resolve$
|
|
6659
|
+
subscribers === null || subscribers === void 0 || subscribers.forEach(({ resolve: resolve$2 }) => {
|
|
6660
|
+
resolve$2(value);
|
|
6658
6661
|
});
|
|
6659
6662
|
});
|
|
6660
6663
|
if ("catch" in thenReturn) thenReturn.catch((reason) => {
|
|
@@ -6801,14 +6804,14 @@ function resolveSelfTuple(promise) {
|
|
|
6801
6804
|
/** VENDORED (Future) PROMISE UTILITIES */
|
|
6802
6805
|
/** Reference implementation of https://github.com/tc39/proposal-promise-with-resolvers */
|
|
6803
6806
|
function withResolvers() {
|
|
6804
|
-
let resolve$
|
|
6807
|
+
let resolve$2;
|
|
6805
6808
|
let reject;
|
|
6806
6809
|
return {
|
|
6807
6810
|
promise: new Promise((_resolve, _reject) => {
|
|
6808
|
-
resolve$
|
|
6811
|
+
resolve$2 = _resolve;
|
|
6809
6812
|
reject = _reject;
|
|
6810
6813
|
}),
|
|
6811
|
-
resolve: resolve$
|
|
6814
|
+
resolve: resolve$2,
|
|
6812
6815
|
reject
|
|
6813
6816
|
};
|
|
6814
6817
|
}
|
|
@@ -6864,8 +6867,8 @@ function timerResource(ms) {
|
|
|
6864
6867
|
let timer = null;
|
|
6865
6868
|
return makeResource({ start() {
|
|
6866
6869
|
if (timer) throw new Error("Timer already started");
|
|
6867
|
-
return new Promise((resolve$
|
|
6868
|
-
timer = setTimeout(() => resolve$
|
|
6870
|
+
return new Promise((resolve$2) => {
|
|
6871
|
+
timer = setTimeout(() => resolve$2(disposablePromiseTimerResult), ms);
|
|
6869
6872
|
});
|
|
6870
6873
|
} }, () => {
|
|
6871
6874
|
if (timer) clearTimeout(timer);
|
|
@@ -7082,14 +7085,14 @@ function _takeWithGrace() {
|
|
|
7082
7085
|
return _takeWithGrace.apply(this, arguments);
|
|
7083
7086
|
}
|
|
7084
7087
|
function createDeferred() {
|
|
7085
|
-
let resolve$
|
|
7088
|
+
let resolve$2;
|
|
7086
7089
|
let reject;
|
|
7087
7090
|
return {
|
|
7088
7091
|
promise: new Promise((res, rej) => {
|
|
7089
|
-
resolve$
|
|
7092
|
+
resolve$2 = res;
|
|
7090
7093
|
reject = rej;
|
|
7091
7094
|
}),
|
|
7092
|
-
resolve: resolve$
|
|
7095
|
+
resolve: resolve$2,
|
|
7093
7096
|
reject
|
|
7094
7097
|
};
|
|
7095
7098
|
}
|
|
@@ -8703,8 +8706,8 @@ function getWSConnectionHandler(opts) {
|
|
|
8703
8706
|
try {
|
|
8704
8707
|
var _usingCtx = (0, import_usingCtx.default)();
|
|
8705
8708
|
const iterator = _usingCtx.a(iteratorResource(iterable));
|
|
8706
|
-
const abortPromise = new Promise((resolve$
|
|
8707
|
-
abortController$1.signal.onabort = () => resolve$
|
|
8709
|
+
const abortPromise = new Promise((resolve$2) => {
|
|
8710
|
+
abortController$1.signal.onabort = () => resolve$2("abort");
|
|
8708
8711
|
});
|
|
8709
8712
|
let next;
|
|
8710
8713
|
let result$1;
|
|
@@ -12716,6 +12719,390 @@ var Validator = class {
|
|
|
12716
12719
|
}
|
|
12717
12720
|
};
|
|
12718
12721
|
|
|
12722
|
+
//#endregion
|
|
12723
|
+
//#region ../core/src/reactive-fs/reactive-state.ts
|
|
12724
|
+
/**
|
|
12725
|
+
* 全局的 AsyncLocalStorage,用于在异步调用链中传递 ReactiveContext
|
|
12726
|
+
* 这是实现依赖收集的核心机制
|
|
12727
|
+
*/
|
|
12728
|
+
const contextStorage = new AsyncLocalStorage();
|
|
12729
|
+
/**
|
|
12730
|
+
* 响应式状态类,类似 Signal.State
|
|
12731
|
+
*
|
|
12732
|
+
* 核心机制:
|
|
12733
|
+
* - get() 时自动注册到当前 ReactiveContext 的依赖列表
|
|
12734
|
+
* - set() 时如果值变化,通知所有依赖的 Context
|
|
12735
|
+
*/
|
|
12736
|
+
var ReactiveState = class {
|
|
12737
|
+
currentValue;
|
|
12738
|
+
equals;
|
|
12739
|
+
/** 所有依赖此状态的 Context */
|
|
12740
|
+
subscribers = /* @__PURE__ */ new Set();
|
|
12741
|
+
constructor(initialValue, options) {
|
|
12742
|
+
this.currentValue = initialValue;
|
|
12743
|
+
this.equals = options?.equals ?? ((a, b) => a === b);
|
|
12744
|
+
}
|
|
12745
|
+
/**
|
|
12746
|
+
* 获取当前值
|
|
12747
|
+
* 如果在 ReactiveContext 中调用,会自动注册依赖
|
|
12748
|
+
*/
|
|
12749
|
+
get() {
|
|
12750
|
+
const context = contextStorage.getStore();
|
|
12751
|
+
if (context) {
|
|
12752
|
+
context.track(this);
|
|
12753
|
+
this.subscribers.add(context);
|
|
12754
|
+
}
|
|
12755
|
+
return this.currentValue;
|
|
12756
|
+
}
|
|
12757
|
+
/**
|
|
12758
|
+
* 设置新值
|
|
12759
|
+
* 如果值变化,通知所有订阅者
|
|
12760
|
+
* @returns 是否发生了变化
|
|
12761
|
+
*/
|
|
12762
|
+
set(newValue) {
|
|
12763
|
+
if (this.equals(this.currentValue, newValue)) return false;
|
|
12764
|
+
this.currentValue = newValue;
|
|
12765
|
+
for (const context of this.subscribers) context.notifyChange();
|
|
12766
|
+
return true;
|
|
12767
|
+
}
|
|
12768
|
+
/**
|
|
12769
|
+
* 取消订阅
|
|
12770
|
+
* 当 Context 销毁时调用
|
|
12771
|
+
*/
|
|
12772
|
+
unsubscribe(context) {
|
|
12773
|
+
this.subscribers.delete(context);
|
|
12774
|
+
}
|
|
12775
|
+
/**
|
|
12776
|
+
* 获取当前订阅者数量(用于调试)
|
|
12777
|
+
*/
|
|
12778
|
+
get subscriberCount() {
|
|
12779
|
+
return this.subscribers.size;
|
|
12780
|
+
}
|
|
12781
|
+
};
|
|
12782
|
+
|
|
12783
|
+
//#endregion
|
|
12784
|
+
//#region ../core/src/reactive-fs/reactive-context.ts
|
|
12785
|
+
function createPromiseWithResolvers() {
|
|
12786
|
+
let resolve$2;
|
|
12787
|
+
let reject;
|
|
12788
|
+
return {
|
|
12789
|
+
promise: new Promise((res, rej) => {
|
|
12790
|
+
resolve$2 = res;
|
|
12791
|
+
reject = rej;
|
|
12792
|
+
}),
|
|
12793
|
+
resolve: resolve$2,
|
|
12794
|
+
reject
|
|
12795
|
+
};
|
|
12796
|
+
}
|
|
12797
|
+
/**
|
|
12798
|
+
* 响应式上下文,管理依赖收集和变更通知
|
|
12799
|
+
*
|
|
12800
|
+
* 核心机制:
|
|
12801
|
+
* - 在 stream() 中执行任务时,通过 AsyncLocalStorage 传递 this
|
|
12802
|
+
* - 任务中的所有 ReactiveState.get() 调用都会自动注册依赖
|
|
12803
|
+
* - 当任何依赖变更时,重新执行任务并 yield 新结果
|
|
12804
|
+
*/
|
|
12805
|
+
var ReactiveContext = class {
|
|
12806
|
+
/** 当前追踪的依赖 */
|
|
12807
|
+
dependencies = /* @__PURE__ */ new Set();
|
|
12808
|
+
/** 等待变更的 Promise */
|
|
12809
|
+
changePromise;
|
|
12810
|
+
/** 是否已销毁 */
|
|
12811
|
+
destroyed = false;
|
|
12812
|
+
/**
|
|
12813
|
+
* 追踪依赖
|
|
12814
|
+
* 由 ReactiveState.get() 调用
|
|
12815
|
+
*/
|
|
12816
|
+
track(state) {
|
|
12817
|
+
if (!this.destroyed) this.dependencies.add(state);
|
|
12818
|
+
}
|
|
12819
|
+
/**
|
|
12820
|
+
* 通知变更
|
|
12821
|
+
* 由 ReactiveState.set() 调用
|
|
12822
|
+
*/
|
|
12823
|
+
notifyChange() {
|
|
12824
|
+
if (!this.destroyed && this.changePromise) this.changePromise.resolve();
|
|
12825
|
+
}
|
|
12826
|
+
/**
|
|
12827
|
+
* 运行响应式任务流
|
|
12828
|
+
* 每次依赖变更时重新执行任务并 yield 结果
|
|
12829
|
+
*
|
|
12830
|
+
* @param task 要执行的异步任务
|
|
12831
|
+
* @param signal 用于取消的 AbortSignal
|
|
12832
|
+
*/
|
|
12833
|
+
async *stream(task, signal) {
|
|
12834
|
+
try {
|
|
12835
|
+
while (!signal?.aborted && !this.destroyed) {
|
|
12836
|
+
this.clearDependencies();
|
|
12837
|
+
this.changePromise = createPromiseWithResolvers();
|
|
12838
|
+
yield await contextStorage.run(this, task);
|
|
12839
|
+
if (this.dependencies.size === 0) break;
|
|
12840
|
+
await Promise.race([this.changePromise.promise, signal ? this.waitForAbort(signal) : new Promise(() => {})]);
|
|
12841
|
+
if (signal?.aborted) break;
|
|
12842
|
+
}
|
|
12843
|
+
} finally {
|
|
12844
|
+
this.destroy();
|
|
12845
|
+
}
|
|
12846
|
+
}
|
|
12847
|
+
/**
|
|
12848
|
+
* 执行一次任务(非响应式)
|
|
12849
|
+
* 用于初始数据获取
|
|
12850
|
+
*/
|
|
12851
|
+
async runOnce(task) {
|
|
12852
|
+
return contextStorage.run(this, task);
|
|
12853
|
+
}
|
|
12854
|
+
/**
|
|
12855
|
+
* 清理依赖
|
|
12856
|
+
*/
|
|
12857
|
+
clearDependencies() {
|
|
12858
|
+
for (const state of this.dependencies) state.unsubscribe(this);
|
|
12859
|
+
this.dependencies.clear();
|
|
12860
|
+
}
|
|
12861
|
+
/**
|
|
12862
|
+
* 销毁上下文
|
|
12863
|
+
* @param reason 可选的销毁原因,如果提供则 reject changePromise
|
|
12864
|
+
*/
|
|
12865
|
+
destroy(reason) {
|
|
12866
|
+
this.destroyed = true;
|
|
12867
|
+
this.clearDependencies();
|
|
12868
|
+
if (reason && this.changePromise) this.changePromise.reject(reason);
|
|
12869
|
+
this.changePromise = void 0;
|
|
12870
|
+
}
|
|
12871
|
+
/**
|
|
12872
|
+
* 等待 AbortSignal
|
|
12873
|
+
*/
|
|
12874
|
+
waitForAbort(signal) {
|
|
12875
|
+
return new Promise((_, reject) => {
|
|
12876
|
+
if (signal.aborted) {
|
|
12877
|
+
reject(new DOMException("Aborted", "AbortError"));
|
|
12878
|
+
return;
|
|
12879
|
+
}
|
|
12880
|
+
signal.addEventListener("abort", () => {
|
|
12881
|
+
reject(new DOMException("Aborted", "AbortError"));
|
|
12882
|
+
});
|
|
12883
|
+
});
|
|
12884
|
+
}
|
|
12885
|
+
};
|
|
12886
|
+
|
|
12887
|
+
//#endregion
|
|
12888
|
+
//#region ../core/src/reactive-fs/watcher-pool.ts
|
|
12889
|
+
/** 全局监听器池,共享同一路径的监听器 */
|
|
12890
|
+
const watcherPool = /* @__PURE__ */ new Map();
|
|
12891
|
+
/** 防抖定时器 */
|
|
12892
|
+
const debounceTimers = /* @__PURE__ */ new Map();
|
|
12893
|
+
/** 默认防抖时间 (ms) */
|
|
12894
|
+
const DEBOUNCE_MS = 100;
|
|
12895
|
+
/**
|
|
12896
|
+
* 获取或创建文件/目录监听器
|
|
12897
|
+
*
|
|
12898
|
+
* 特性:
|
|
12899
|
+
* - 同一路径共享监听器
|
|
12900
|
+
* - 引用计数管理生命周期
|
|
12901
|
+
* - 内置防抖机制
|
|
12902
|
+
*
|
|
12903
|
+
* @param path 要监听的路径
|
|
12904
|
+
* @param onChange 变更回调
|
|
12905
|
+
* @param options 监听选项
|
|
12906
|
+
* @returns 释放函数,调用后取消订阅
|
|
12907
|
+
*/
|
|
12908
|
+
function acquireWatcher(path$1, onChange, options = {}) {
|
|
12909
|
+
const normalizedPath = resolve$1(path$1);
|
|
12910
|
+
const debounceMs = options.debounceMs ?? DEBOUNCE_MS;
|
|
12911
|
+
let entry = watcherPool.get(normalizedPath);
|
|
12912
|
+
if (!entry) {
|
|
12913
|
+
const watcher = watch(normalizedPath, {
|
|
12914
|
+
recursive: options.recursive ?? false,
|
|
12915
|
+
persistent: false
|
|
12916
|
+
}, () => {
|
|
12917
|
+
const existingTimer = debounceTimers.get(normalizedPath);
|
|
12918
|
+
if (existingTimer) clearTimeout(existingTimer);
|
|
12919
|
+
const timer = setTimeout(() => {
|
|
12920
|
+
debounceTimers.delete(normalizedPath);
|
|
12921
|
+
const currentEntry = watcherPool.get(normalizedPath);
|
|
12922
|
+
if (currentEntry) for (const cb of currentEntry.callbacks) try {
|
|
12923
|
+
cb();
|
|
12924
|
+
} catch (err) {
|
|
12925
|
+
console.error(`Watcher callback error for ${normalizedPath}:`, err);
|
|
12926
|
+
}
|
|
12927
|
+
}, debounceMs);
|
|
12928
|
+
debounceTimers.set(normalizedPath, timer);
|
|
12929
|
+
});
|
|
12930
|
+
watcher.on("error", (err) => {
|
|
12931
|
+
console.error(`Watcher error for ${normalizedPath}:`, err);
|
|
12932
|
+
});
|
|
12933
|
+
entry = {
|
|
12934
|
+
watcher,
|
|
12935
|
+
refCount: 0,
|
|
12936
|
+
callbacks: /* @__PURE__ */ new Set()
|
|
12937
|
+
};
|
|
12938
|
+
watcherPool.set(normalizedPath, entry);
|
|
12939
|
+
}
|
|
12940
|
+
entry.refCount++;
|
|
12941
|
+
entry.callbacks.add(onChange);
|
|
12942
|
+
return () => {
|
|
12943
|
+
const currentEntry = watcherPool.get(normalizedPath);
|
|
12944
|
+
if (!currentEntry) return;
|
|
12945
|
+
currentEntry.callbacks.delete(onChange);
|
|
12946
|
+
currentEntry.refCount--;
|
|
12947
|
+
if (currentEntry.refCount === 0) {
|
|
12948
|
+
currentEntry.watcher.close();
|
|
12949
|
+
watcherPool.delete(normalizedPath);
|
|
12950
|
+
const timer = debounceTimers.get(normalizedPath);
|
|
12951
|
+
if (timer) {
|
|
12952
|
+
clearTimeout(timer);
|
|
12953
|
+
debounceTimers.delete(normalizedPath);
|
|
12954
|
+
}
|
|
12955
|
+
}
|
|
12956
|
+
};
|
|
12957
|
+
}
|
|
12958
|
+
|
|
12959
|
+
//#endregion
|
|
12960
|
+
//#region ../core/src/reactive-fs/reactive-fs.ts
|
|
12961
|
+
/** 状态缓存:路径 -> ReactiveState */
|
|
12962
|
+
const stateCache = /* @__PURE__ */ new Map();
|
|
12963
|
+
/** 监听器释放函数缓存 */
|
|
12964
|
+
const releaseCache = /* @__PURE__ */ new Map();
|
|
12965
|
+
/**
|
|
12966
|
+
* 响应式读取文件内容
|
|
12967
|
+
*
|
|
12968
|
+
* 特性:
|
|
12969
|
+
* - 自动注册文件监听
|
|
12970
|
+
* - 文件变更时自动更新状态
|
|
12971
|
+
* - 在 ReactiveContext 中调用时自动追踪依赖
|
|
12972
|
+
*
|
|
12973
|
+
* @param filepath 文件路径
|
|
12974
|
+
* @returns 文件内容,文件不存在时返回 null
|
|
12975
|
+
*/
|
|
12976
|
+
async function reactiveReadFile(filepath) {
|
|
12977
|
+
const normalizedPath = resolve$1(filepath);
|
|
12978
|
+
const key = `file:${normalizedPath}`;
|
|
12979
|
+
const getValue = async () => {
|
|
12980
|
+
try {
|
|
12981
|
+
return await readFile$1(normalizedPath, "utf-8");
|
|
12982
|
+
} catch {
|
|
12983
|
+
return null;
|
|
12984
|
+
}
|
|
12985
|
+
};
|
|
12986
|
+
let state = stateCache.get(key);
|
|
12987
|
+
if (!state) {
|
|
12988
|
+
state = new ReactiveState(await getValue());
|
|
12989
|
+
stateCache.set(key, state);
|
|
12990
|
+
const release = acquireWatcher(dirname$1(normalizedPath), async () => {
|
|
12991
|
+
const newValue = await getValue();
|
|
12992
|
+
state.set(newValue);
|
|
12993
|
+
});
|
|
12994
|
+
releaseCache.set(key, release);
|
|
12995
|
+
}
|
|
12996
|
+
return state.get();
|
|
12997
|
+
}
|
|
12998
|
+
/**
|
|
12999
|
+
* 响应式读取目录内容
|
|
13000
|
+
*
|
|
13001
|
+
* 特性:
|
|
13002
|
+
* - 自动注册目录监听
|
|
13003
|
+
* - 目录变更时自动更新状态
|
|
13004
|
+
* - 在 ReactiveContext 中调用时自动追踪依赖
|
|
13005
|
+
*
|
|
13006
|
+
* @param dirpath 目录路径
|
|
13007
|
+
* @param options 选项
|
|
13008
|
+
* @returns 目录项名称数组
|
|
13009
|
+
*/
|
|
13010
|
+
async function reactiveReadDir(dirpath, options = {}) {
|
|
13011
|
+
const normalizedPath = resolve$1(dirpath);
|
|
13012
|
+
const key = `dir:${normalizedPath}:${JSON.stringify(options)}`;
|
|
13013
|
+
const getValue = async () => {
|
|
13014
|
+
try {
|
|
13015
|
+
return (await readdir(normalizedPath, { withFileTypes: true })).filter((entry) => {
|
|
13016
|
+
if (!options.includeHidden && entry.name.startsWith(".")) return false;
|
|
13017
|
+
if (options.exclude?.includes(entry.name)) return false;
|
|
13018
|
+
if (options.directoriesOnly && !entry.isDirectory()) return false;
|
|
13019
|
+
if (options.filesOnly && !entry.isFile()) return false;
|
|
13020
|
+
return true;
|
|
13021
|
+
}).map((entry) => entry.name);
|
|
13022
|
+
} catch {
|
|
13023
|
+
return [];
|
|
13024
|
+
}
|
|
13025
|
+
};
|
|
13026
|
+
let state = stateCache.get(key);
|
|
13027
|
+
if (!state) {
|
|
13028
|
+
state = new ReactiveState(await getValue(), { equals: (a, b) => a.length === b.length && a.every((v, i) => v === b[i]) });
|
|
13029
|
+
stateCache.set(key, state);
|
|
13030
|
+
const release = acquireWatcher(normalizedPath, async () => {
|
|
13031
|
+
const newValue = await getValue();
|
|
13032
|
+
state.set(newValue);
|
|
13033
|
+
}, { recursive: true });
|
|
13034
|
+
releaseCache.set(key, release);
|
|
13035
|
+
}
|
|
13036
|
+
return state.get();
|
|
13037
|
+
}
|
|
13038
|
+
/**
|
|
13039
|
+
* 响应式检查路径是否存在
|
|
13040
|
+
*
|
|
13041
|
+
* @param path 路径
|
|
13042
|
+
* @returns 是否存在
|
|
13043
|
+
*/
|
|
13044
|
+
async function reactiveExists(path$1) {
|
|
13045
|
+
const normalizedPath = resolve$1(path$1);
|
|
13046
|
+
const key = `exists:${normalizedPath}`;
|
|
13047
|
+
const getValue = async () => {
|
|
13048
|
+
try {
|
|
13049
|
+
await stat(normalizedPath);
|
|
13050
|
+
return true;
|
|
13051
|
+
} catch {
|
|
13052
|
+
return false;
|
|
13053
|
+
}
|
|
13054
|
+
};
|
|
13055
|
+
let state = stateCache.get(key);
|
|
13056
|
+
if (!state) {
|
|
13057
|
+
state = new ReactiveState(await getValue());
|
|
13058
|
+
stateCache.set(key, state);
|
|
13059
|
+
const release = acquireWatcher(dirname$1(normalizedPath), async () => {
|
|
13060
|
+
const newValue = await getValue();
|
|
13061
|
+
state.set(newValue);
|
|
13062
|
+
});
|
|
13063
|
+
releaseCache.set(key, release);
|
|
13064
|
+
}
|
|
13065
|
+
return state.get();
|
|
13066
|
+
}
|
|
13067
|
+
/**
|
|
13068
|
+
* 响应式获取文件/目录的 stat 信息
|
|
13069
|
+
*
|
|
13070
|
+
* @param path 路径
|
|
13071
|
+
* @returns stat 信息,不存在时返回 null
|
|
13072
|
+
*/
|
|
13073
|
+
async function reactiveStat(path$1) {
|
|
13074
|
+
const normalizedPath = resolve$1(path$1);
|
|
13075
|
+
const key = `stat:${normalizedPath}`;
|
|
13076
|
+
const getValue = async () => {
|
|
13077
|
+
try {
|
|
13078
|
+
const s = await stat(normalizedPath);
|
|
13079
|
+
return {
|
|
13080
|
+
isDirectory: s.isDirectory(),
|
|
13081
|
+
isFile: s.isFile(),
|
|
13082
|
+
mtime: s.mtime.getTime(),
|
|
13083
|
+
birthtime: s.birthtime.getTime()
|
|
13084
|
+
};
|
|
13085
|
+
} catch {
|
|
13086
|
+
return null;
|
|
13087
|
+
}
|
|
13088
|
+
};
|
|
13089
|
+
let state = stateCache.get(key);
|
|
13090
|
+
if (!state) {
|
|
13091
|
+
state = new ReactiveState(await getValue(), { equals: (a, b) => {
|
|
13092
|
+
if (a === null && b === null) return true;
|
|
13093
|
+
if (a === null || b === null) return false;
|
|
13094
|
+
return a.isDirectory === b.isDirectory && a.isFile === b.isFile && a.mtime === b.mtime && a.birthtime === b.birthtime;
|
|
13095
|
+
} });
|
|
13096
|
+
stateCache.set(key, state);
|
|
13097
|
+
const release = acquireWatcher(dirname$1(normalizedPath), async () => {
|
|
13098
|
+
const newValue = await getValue();
|
|
13099
|
+
state.set(newValue);
|
|
13100
|
+
});
|
|
13101
|
+
releaseCache.set(key, release);
|
|
13102
|
+
}
|
|
13103
|
+
return state.get();
|
|
13104
|
+
}
|
|
13105
|
+
|
|
12719
13106
|
//#endregion
|
|
12720
13107
|
//#region ../core/src/adapter.ts
|
|
12721
13108
|
/**
|
|
@@ -12741,93 +13128,101 @@ var OpenSpecAdapter = class {
|
|
|
12741
13128
|
return join(this.changesDir, "archive");
|
|
12742
13129
|
}
|
|
12743
13130
|
async isInitialized() {
|
|
12744
|
-
|
|
12745
|
-
|
|
12746
|
-
|
|
12747
|
-
|
|
12748
|
-
|
|
13131
|
+
return (await reactiveStat(this.openspecDir))?.isDirectory ?? false;
|
|
13132
|
+
}
|
|
13133
|
+
/** File time info derived from filesystem (reactive) */
|
|
13134
|
+
async getFileTimeInfo(filePath) {
|
|
13135
|
+
const statInfo = await reactiveStat(filePath);
|
|
13136
|
+
if (!statInfo) return null;
|
|
13137
|
+
return {
|
|
13138
|
+
createdAt: statInfo.birthtime,
|
|
13139
|
+
updatedAt: statInfo.mtime
|
|
13140
|
+
};
|
|
12749
13141
|
}
|
|
12750
13142
|
async listSpecs() {
|
|
12751
|
-
|
|
12752
|
-
return (await readdir(this.specsDir, { withFileTypes: true })).filter((e) => e.isDirectory() && !e.name.startsWith(".")).map((e) => e.name);
|
|
12753
|
-
} catch {
|
|
12754
|
-
return [];
|
|
12755
|
-
}
|
|
13143
|
+
return reactiveReadDir(this.specsDir, { directoriesOnly: true });
|
|
12756
13144
|
}
|
|
12757
13145
|
/**
|
|
12758
|
-
* List specs with metadata (id and
|
|
13146
|
+
* List specs with metadata (id, name, and time info)
|
|
13147
|
+
* Only returns specs that have valid spec.md
|
|
13148
|
+
* Sorted by updatedAt descending (most recent first)
|
|
12759
13149
|
*/
|
|
12760
13150
|
async listSpecsWithMeta() {
|
|
12761
13151
|
const ids = await this.listSpecs();
|
|
12762
|
-
return await Promise.all(ids.map(async (id) => {
|
|
13152
|
+
return (await Promise.all(ids.map(async (id) => {
|
|
13153
|
+
const spec = await this.readSpec(id);
|
|
13154
|
+
if (!spec) return null;
|
|
13155
|
+
const specPath = join(this.specsDir, id, "spec.md");
|
|
13156
|
+
const timeInfo = await this.getFileTimeInfo(specPath);
|
|
12763
13157
|
return {
|
|
12764
13158
|
id,
|
|
12765
|
-
name:
|
|
13159
|
+
name: spec.name,
|
|
13160
|
+
createdAt: timeInfo?.createdAt ?? 0,
|
|
13161
|
+
updatedAt: timeInfo?.updatedAt ?? 0
|
|
12766
13162
|
};
|
|
12767
|
-
}));
|
|
13163
|
+
}))).filter((r) => r !== null).sort((a, b) => b.updatedAt - a.updatedAt);
|
|
12768
13164
|
}
|
|
12769
13165
|
async listChanges() {
|
|
12770
|
-
|
|
12771
|
-
|
|
12772
|
-
|
|
12773
|
-
|
|
12774
|
-
}
|
|
13166
|
+
return reactiveReadDir(this.changesDir, {
|
|
13167
|
+
directoriesOnly: true,
|
|
13168
|
+
exclude: ["archive"]
|
|
13169
|
+
});
|
|
12775
13170
|
}
|
|
12776
13171
|
/**
|
|
12777
|
-
* List changes with metadata (id, name, and
|
|
13172
|
+
* List changes with metadata (id, name, progress, and time info)
|
|
13173
|
+
* Only returns changes that have valid proposal.md
|
|
13174
|
+
* Sorted by updatedAt descending (most recent first)
|
|
12778
13175
|
*/
|
|
12779
13176
|
async listChangesWithMeta() {
|
|
12780
13177
|
const ids = await this.listChanges();
|
|
12781
|
-
return await Promise.all(ids.map(async (id) => {
|
|
13178
|
+
return (await Promise.all(ids.map(async (id) => {
|
|
12782
13179
|
const change = await this.readChange(id);
|
|
13180
|
+
if (!change) return null;
|
|
13181
|
+
const proposalPath = join(this.changesDir, id, "proposal.md");
|
|
13182
|
+
const timeInfo = await this.getFileTimeInfo(proposalPath);
|
|
12783
13183
|
return {
|
|
12784
13184
|
id,
|
|
12785
|
-
name: change
|
|
12786
|
-
progress: change
|
|
12787
|
-
|
|
12788
|
-
|
|
12789
|
-
}
|
|
13185
|
+
name: change.name,
|
|
13186
|
+
progress: change.progress,
|
|
13187
|
+
createdAt: timeInfo?.createdAt ?? 0,
|
|
13188
|
+
updatedAt: timeInfo?.updatedAt ?? 0
|
|
12790
13189
|
};
|
|
12791
|
-
}));
|
|
13190
|
+
}))).filter((r) => r !== null).sort((a, b) => b.updatedAt - a.updatedAt);
|
|
12792
13191
|
}
|
|
12793
13192
|
async listArchivedChanges() {
|
|
12794
|
-
|
|
12795
|
-
return (await readdir(this.archiveDir, { withFileTypes: true })).filter((e) => e.isDirectory() && !e.name.startsWith(".")).map((e) => e.name);
|
|
12796
|
-
} catch {
|
|
12797
|
-
return [];
|
|
12798
|
-
}
|
|
13193
|
+
return reactiveReadDir(this.archiveDir, { directoriesOnly: true });
|
|
12799
13194
|
}
|
|
12800
13195
|
/**
|
|
12801
|
-
* List archived changes with metadata
|
|
13196
|
+
* List archived changes with metadata and time info
|
|
13197
|
+
* Only returns archives that have valid proposal.md
|
|
13198
|
+
* Sorted by updatedAt descending (most recent first)
|
|
12802
13199
|
*/
|
|
12803
13200
|
async listArchivedChangesWithMeta() {
|
|
12804
13201
|
const ids = await this.listArchivedChanges();
|
|
12805
|
-
return await Promise.all(ids.map(async (id) => {
|
|
13202
|
+
return (await Promise.all(ids.map(async (id) => {
|
|
13203
|
+
const change = await this.readArchivedChange(id);
|
|
13204
|
+
if (!change) return null;
|
|
13205
|
+
const proposalPath = join(this.archiveDir, id, "proposal.md");
|
|
13206
|
+
const timeInfo = await this.getFileTimeInfo(proposalPath);
|
|
12806
13207
|
return {
|
|
12807
13208
|
id,
|
|
12808
|
-
name:
|
|
13209
|
+
name: change.name,
|
|
13210
|
+
createdAt: timeInfo?.createdAt ?? 0,
|
|
13211
|
+
updatedAt: timeInfo?.updatedAt ?? 0
|
|
12809
13212
|
};
|
|
12810
|
-
}));
|
|
13213
|
+
}))).filter((r) => r !== null).sort((a, b) => b.updatedAt - a.updatedAt);
|
|
12811
13214
|
}
|
|
12812
13215
|
/**
|
|
12813
|
-
* Read project.md content
|
|
13216
|
+
* Read project.md content (reactive)
|
|
12814
13217
|
*/
|
|
12815
13218
|
async readProjectMd() {
|
|
12816
|
-
|
|
12817
|
-
return await readFile(join(this.openspecDir, "project.md"), "utf-8");
|
|
12818
|
-
} catch {
|
|
12819
|
-
return null;
|
|
12820
|
-
}
|
|
13219
|
+
return reactiveReadFile(join(this.openspecDir, "project.md"));
|
|
12821
13220
|
}
|
|
12822
13221
|
/**
|
|
12823
|
-
* Read AGENTS.md content
|
|
13222
|
+
* Read AGENTS.md content (reactive)
|
|
12824
13223
|
*/
|
|
12825
13224
|
async readAgentsMd() {
|
|
12826
|
-
|
|
12827
|
-
return await readFile(join(this.openspecDir, "AGENTS.md"), "utf-8");
|
|
12828
|
-
} catch {
|
|
12829
|
-
return null;
|
|
12830
|
-
}
|
|
13225
|
+
return reactiveReadFile(join(this.openspecDir, "AGENTS.md"));
|
|
12831
13226
|
}
|
|
12832
13227
|
/**
|
|
12833
13228
|
* Write project.md content
|
|
@@ -12851,11 +13246,7 @@ var OpenSpecAdapter = class {
|
|
|
12851
13246
|
}
|
|
12852
13247
|
}
|
|
12853
13248
|
async readSpecRaw(specId) {
|
|
12854
|
-
|
|
12855
|
-
return await readFile(join(this.specsDir, specId, "spec.md"), "utf-8");
|
|
12856
|
-
} catch {
|
|
12857
|
-
return null;
|
|
12858
|
-
}
|
|
13249
|
+
return reactiveReadFile(join(this.specsDir, specId, "spec.md"));
|
|
12859
13250
|
}
|
|
12860
13251
|
async readChange(changeId) {
|
|
12861
13252
|
try {
|
|
@@ -12867,17 +13258,14 @@ var OpenSpecAdapter = class {
|
|
|
12867
13258
|
}
|
|
12868
13259
|
}
|
|
12869
13260
|
async readChangeRaw(changeId) {
|
|
12870
|
-
|
|
12871
|
-
|
|
12872
|
-
|
|
12873
|
-
|
|
12874
|
-
|
|
12875
|
-
|
|
12876
|
-
|
|
12877
|
-
|
|
12878
|
-
} catch {
|
|
12879
|
-
return null;
|
|
12880
|
-
}
|
|
13261
|
+
const proposalPath = join(this.changesDir, changeId, "proposal.md");
|
|
13262
|
+
const tasksPath = join(this.changesDir, changeId, "tasks.md");
|
|
13263
|
+
const [proposal, tasks] = await Promise.all([reactiveReadFile(proposalPath), reactiveReadFile(tasksPath)]);
|
|
13264
|
+
if (!proposal) return null;
|
|
13265
|
+
return {
|
|
13266
|
+
proposal,
|
|
13267
|
+
tasks: tasks ?? ""
|
|
13268
|
+
};
|
|
12881
13269
|
}
|
|
12882
13270
|
/**
|
|
12883
13271
|
* Read an archived change
|
|
@@ -12892,20 +13280,17 @@ var OpenSpecAdapter = class {
|
|
|
12892
13280
|
}
|
|
12893
13281
|
}
|
|
12894
13282
|
/**
|
|
12895
|
-
* Read raw archived change files
|
|
13283
|
+
* Read raw archived change files (reactive)
|
|
12896
13284
|
*/
|
|
12897
13285
|
async readArchivedChangeRaw(changeId) {
|
|
12898
|
-
|
|
12899
|
-
|
|
12900
|
-
|
|
12901
|
-
|
|
12902
|
-
|
|
12903
|
-
|
|
12904
|
-
|
|
12905
|
-
|
|
12906
|
-
} catch {
|
|
12907
|
-
return null;
|
|
12908
|
-
}
|
|
13286
|
+
const proposalPath = join(this.archiveDir, changeId, "proposal.md");
|
|
13287
|
+
const tasksPath = join(this.archiveDir, changeId, "tasks.md");
|
|
13288
|
+
const [proposal, tasks] = await Promise.all([reactiveReadFile(proposalPath), reactiveReadFile(tasksPath)]);
|
|
13289
|
+
if (!proposal) return null;
|
|
13290
|
+
return {
|
|
13291
|
+
proposal,
|
|
13292
|
+
tasks: tasks ?? ""
|
|
13293
|
+
};
|
|
12909
13294
|
}
|
|
12910
13295
|
async writeSpec(specId, content) {
|
|
12911
13296
|
const specDir = join(this.specsDir, specId);
|
|
@@ -13222,7 +13607,7 @@ var OpenSpecWatcher = class extends EventEmitter {
|
|
|
13222
13607
|
*/
|
|
13223
13608
|
watchDir(dir, callback) {
|
|
13224
13609
|
try {
|
|
13225
|
-
const watcher = watch(dir, { recursive: true }, (eventType, filename) => {
|
|
13610
|
+
const watcher = watch$1(dir, { recursive: true }, (eventType, filename) => {
|
|
13226
13611
|
if (filename) callback(filename, eventType);
|
|
13227
13612
|
});
|
|
13228
13613
|
watcher.on("error", (error) => {
|
|
@@ -13247,6 +13632,397 @@ var OpenSpecWatcher = class extends EventEmitter {
|
|
|
13247
13632
|
}
|
|
13248
13633
|
};
|
|
13249
13634
|
|
|
13635
|
+
//#endregion
|
|
13636
|
+
//#region ../core/src/config.ts
|
|
13637
|
+
/**
|
|
13638
|
+
* OpenSpecUI 配置 Schema
|
|
13639
|
+
*
|
|
13640
|
+
* 存储在 openspec/.openspecui.json 中,利用文件监听实现响应式更新
|
|
13641
|
+
*/
|
|
13642
|
+
const OpenSpecUIConfigSchema = objectType({
|
|
13643
|
+
cli: objectType({ command: stringType().default("npx @fission-ai/openspec") }).default({}),
|
|
13644
|
+
ui: objectType({ theme: enumType([
|
|
13645
|
+
"light",
|
|
13646
|
+
"dark",
|
|
13647
|
+
"system"
|
|
13648
|
+
]).default("system") }).default({})
|
|
13649
|
+
});
|
|
13650
|
+
/** 默认配置 */
|
|
13651
|
+
const DEFAULT_CONFIG = {
|
|
13652
|
+
cli: { command: "npx @fission-ai/openspec" },
|
|
13653
|
+
ui: { theme: "system" }
|
|
13654
|
+
};
|
|
13655
|
+
/**
|
|
13656
|
+
* 配置管理器
|
|
13657
|
+
*
|
|
13658
|
+
* 负责读写 openspec/.openspecui.json 配置文件。
|
|
13659
|
+
* 读取操作使用 reactiveReadFile,支持响应式更新。
|
|
13660
|
+
*/
|
|
13661
|
+
var ConfigManager = class {
|
|
13662
|
+
configPath;
|
|
13663
|
+
constructor(projectDir) {
|
|
13664
|
+
this.configPath = join(projectDir, "openspec", ".openspecui.json");
|
|
13665
|
+
}
|
|
13666
|
+
/**
|
|
13667
|
+
* 读取配置(响应式)
|
|
13668
|
+
*
|
|
13669
|
+
* 如果配置文件不存在,返回默认配置。
|
|
13670
|
+
* 如果配置文件格式错误,返回默认配置并打印警告。
|
|
13671
|
+
*/
|
|
13672
|
+
async readConfig() {
|
|
13673
|
+
const content = await reactiveReadFile(this.configPath);
|
|
13674
|
+
if (!content) return DEFAULT_CONFIG;
|
|
13675
|
+
try {
|
|
13676
|
+
const parsed = JSON.parse(content);
|
|
13677
|
+
const result = OpenSpecUIConfigSchema.safeParse(parsed);
|
|
13678
|
+
if (result.success) return result.data;
|
|
13679
|
+
console.warn("Invalid config format, using defaults:", result.error.message);
|
|
13680
|
+
return DEFAULT_CONFIG;
|
|
13681
|
+
} catch (err) {
|
|
13682
|
+
console.warn("Failed to parse config, using defaults:", err);
|
|
13683
|
+
return DEFAULT_CONFIG;
|
|
13684
|
+
}
|
|
13685
|
+
}
|
|
13686
|
+
/**
|
|
13687
|
+
* 写入配置
|
|
13688
|
+
*
|
|
13689
|
+
* 会触发文件监听,自动更新订阅者。
|
|
13690
|
+
*/
|
|
13691
|
+
async writeConfig(config) {
|
|
13692
|
+
const current = await this.readConfig();
|
|
13693
|
+
const merged = {
|
|
13694
|
+
...current,
|
|
13695
|
+
...config,
|
|
13696
|
+
cli: {
|
|
13697
|
+
...current.cli,
|
|
13698
|
+
...config.cli
|
|
13699
|
+
},
|
|
13700
|
+
ui: {
|
|
13701
|
+
...current.ui,
|
|
13702
|
+
...config.ui
|
|
13703
|
+
}
|
|
13704
|
+
};
|
|
13705
|
+
await writeFile(this.configPath, JSON.stringify(merged, null, 2), "utf-8");
|
|
13706
|
+
}
|
|
13707
|
+
/**
|
|
13708
|
+
* 获取 CLI 命令
|
|
13709
|
+
*/
|
|
13710
|
+
async getCliCommand() {
|
|
13711
|
+
return (await this.readConfig()).cli.command;
|
|
13712
|
+
}
|
|
13713
|
+
/**
|
|
13714
|
+
* 设置 CLI 命令
|
|
13715
|
+
*/
|
|
13716
|
+
async setCliCommand(command) {
|
|
13717
|
+
await this.writeConfig({ cli: { command } });
|
|
13718
|
+
}
|
|
13719
|
+
};
|
|
13720
|
+
|
|
13721
|
+
//#endregion
|
|
13722
|
+
//#region ../core/src/cli-executor.ts
|
|
13723
|
+
/**
|
|
13724
|
+
* CLI 执行器
|
|
13725
|
+
*
|
|
13726
|
+
* 负责调用外部 openspec CLI 命令。
|
|
13727
|
+
* 命令前缀从 ConfigManager 获取,支持:
|
|
13728
|
+
* - npx @fission-ai/openspec (默认)
|
|
13729
|
+
* - bunx openspec
|
|
13730
|
+
* - openspec (本地安装)
|
|
13731
|
+
* - 自定义命令 (如 xspec)
|
|
13732
|
+
*/
|
|
13733
|
+
var CliExecutor = class {
|
|
13734
|
+
constructor(configManager, projectDir) {
|
|
13735
|
+
this.configManager = configManager;
|
|
13736
|
+
this.projectDir = projectDir;
|
|
13737
|
+
}
|
|
13738
|
+
/**
|
|
13739
|
+
* 创建干净的环境变量,移除 pnpm 特有的配置
|
|
13740
|
+
* 避免 pnpm 环境变量污染 npx/npm 执行
|
|
13741
|
+
*/
|
|
13742
|
+
getCleanEnv() {
|
|
13743
|
+
const env = { ...process.env };
|
|
13744
|
+
for (const key of Object.keys(env)) if (key.startsWith("npm_config_") || key.startsWith("npm_package_") || key === "npm_execpath" || key === "npm_lifecycle_event" || key === "npm_lifecycle_script") delete env[key];
|
|
13745
|
+
return env;
|
|
13746
|
+
}
|
|
13747
|
+
/**
|
|
13748
|
+
* 执行 CLI 命令
|
|
13749
|
+
*
|
|
13750
|
+
* @param args CLI 参数,如 ['init'] 或 ['archive', 'change-id']
|
|
13751
|
+
* @returns 执行结果
|
|
13752
|
+
*/
|
|
13753
|
+
async execute(args) {
|
|
13754
|
+
const parts = (await this.configManager.getCliCommand()).split(/\s+/);
|
|
13755
|
+
const cmd = parts[0];
|
|
13756
|
+
const cmdArgs = [...parts.slice(1), ...args];
|
|
13757
|
+
return new Promise((resolve$2) => {
|
|
13758
|
+
const child = spawn(cmd, cmdArgs, {
|
|
13759
|
+
cwd: this.projectDir,
|
|
13760
|
+
shell: true,
|
|
13761
|
+
env: this.getCleanEnv()
|
|
13762
|
+
});
|
|
13763
|
+
let stdout = "";
|
|
13764
|
+
let stderr = "";
|
|
13765
|
+
child.stdout?.on("data", (data) => {
|
|
13766
|
+
stdout += data.toString();
|
|
13767
|
+
});
|
|
13768
|
+
child.stderr?.on("data", (data) => {
|
|
13769
|
+
stderr += data.toString();
|
|
13770
|
+
});
|
|
13771
|
+
child.on("close", (exitCode) => {
|
|
13772
|
+
resolve$2({
|
|
13773
|
+
success: exitCode === 0,
|
|
13774
|
+
stdout,
|
|
13775
|
+
stderr,
|
|
13776
|
+
exitCode
|
|
13777
|
+
});
|
|
13778
|
+
});
|
|
13779
|
+
child.on("error", (err) => {
|
|
13780
|
+
resolve$2({
|
|
13781
|
+
success: false,
|
|
13782
|
+
stdout,
|
|
13783
|
+
stderr: stderr + "\n" + err.message,
|
|
13784
|
+
exitCode: null
|
|
13785
|
+
});
|
|
13786
|
+
});
|
|
13787
|
+
});
|
|
13788
|
+
}
|
|
13789
|
+
/**
|
|
13790
|
+
* 执行 openspec init(非交互式)
|
|
13791
|
+
*
|
|
13792
|
+
* @param tools 工具列表,如 ['claude', 'cursor'] 或 'all' 或 'none'
|
|
13793
|
+
*/
|
|
13794
|
+
async init(tools = "all") {
|
|
13795
|
+
const toolsArg = Array.isArray(tools) ? tools.join(",") : tools;
|
|
13796
|
+
return this.execute(["init", `--tools=${toolsArg}`]);
|
|
13797
|
+
}
|
|
13798
|
+
/**
|
|
13799
|
+
* 执行 openspec archive <changeId>(非交互式)
|
|
13800
|
+
*
|
|
13801
|
+
* @param changeId 要归档的 change ID
|
|
13802
|
+
* @param options 选项
|
|
13803
|
+
*/
|
|
13804
|
+
async archive(changeId, options = {}) {
|
|
13805
|
+
const args = [
|
|
13806
|
+
"archive",
|
|
13807
|
+
"-y",
|
|
13808
|
+
changeId
|
|
13809
|
+
];
|
|
13810
|
+
if (options.skipSpecs) args.push("--skip-specs");
|
|
13811
|
+
if (options.noValidate) args.push("--no-validate");
|
|
13812
|
+
return this.execute(args);
|
|
13813
|
+
}
|
|
13814
|
+
/**
|
|
13815
|
+
* 执行 openspec validate [type] [id]
|
|
13816
|
+
*/
|
|
13817
|
+
async validate(type, id) {
|
|
13818
|
+
const args = ["validate"];
|
|
13819
|
+
if (type) args.push(type);
|
|
13820
|
+
if (id) args.push(id);
|
|
13821
|
+
return this.execute(args);
|
|
13822
|
+
}
|
|
13823
|
+
/**
|
|
13824
|
+
* 检查 CLI 是否可用
|
|
13825
|
+
*/
|
|
13826
|
+
async checkAvailability() {
|
|
13827
|
+
try {
|
|
13828
|
+
const result = await this.execute(["--version"]);
|
|
13829
|
+
if (result.success) return {
|
|
13830
|
+
available: true,
|
|
13831
|
+
version: result.stdout.trim()
|
|
13832
|
+
};
|
|
13833
|
+
return {
|
|
13834
|
+
available: false,
|
|
13835
|
+
error: result.stderr || "Unknown error"
|
|
13836
|
+
};
|
|
13837
|
+
} catch (err) {
|
|
13838
|
+
return {
|
|
13839
|
+
available: false,
|
|
13840
|
+
error: err instanceof Error ? err.message : "Unknown error"
|
|
13841
|
+
};
|
|
13842
|
+
}
|
|
13843
|
+
}
|
|
13844
|
+
};
|
|
13845
|
+
|
|
13846
|
+
//#endregion
|
|
13847
|
+
//#region ../core/src/tool-config.ts
|
|
13848
|
+
/**
|
|
13849
|
+
* 工具配置检测模块
|
|
13850
|
+
*
|
|
13851
|
+
* 基于 @fission-ai/openspec 的 configurators 实现
|
|
13852
|
+
* 用于检测项目中已配置的 AI 工具
|
|
13853
|
+
*
|
|
13854
|
+
* 重要:使用响应式文件系统 (reactiveExists) 实现,
|
|
13855
|
+
* 当配置文件变化时会自动触发更新。
|
|
13856
|
+
*
|
|
13857
|
+
* @see references/openspec/src/core/configurators/slash/
|
|
13858
|
+
*/
|
|
13859
|
+
/**
|
|
13860
|
+
* 所有支持的工具配置
|
|
13861
|
+
*
|
|
13862
|
+
* 检测路径使用 proposal 命令文件,因为这是 openspec init 创建的第一个文件
|
|
13863
|
+
* 如果该文件存在,说明工具已配置
|
|
13864
|
+
*/
|
|
13865
|
+
const TOOL_CONFIGS = [
|
|
13866
|
+
{
|
|
13867
|
+
toolId: "claude",
|
|
13868
|
+
detectionPath: ".claude/commands/openspec/proposal.md"
|
|
13869
|
+
},
|
|
13870
|
+
{
|
|
13871
|
+
toolId: "cursor",
|
|
13872
|
+
detectionPath: ".cursor/commands/openspec-proposal.md"
|
|
13873
|
+
},
|
|
13874
|
+
{
|
|
13875
|
+
toolId: "windsurf",
|
|
13876
|
+
detectionPath: ".windsurf/workflows/openspec-proposal.md"
|
|
13877
|
+
},
|
|
13878
|
+
{
|
|
13879
|
+
toolId: "cline",
|
|
13880
|
+
detectionPath: ".clinerules/workflows/openspec-proposal.md"
|
|
13881
|
+
},
|
|
13882
|
+
{
|
|
13883
|
+
toolId: "github-copilot",
|
|
13884
|
+
detectionPath: ".github/prompts/openspec-proposal.prompt.md"
|
|
13885
|
+
},
|
|
13886
|
+
{
|
|
13887
|
+
toolId: "amazon-q",
|
|
13888
|
+
detectionPath: ".amazonq/prompts/openspec-proposal.md"
|
|
13889
|
+
},
|
|
13890
|
+
{
|
|
13891
|
+
toolId: "codex",
|
|
13892
|
+
detectionPath: ".codex/prompts/openspec-proposal.md"
|
|
13893
|
+
},
|
|
13894
|
+
{
|
|
13895
|
+
toolId: "gemini",
|
|
13896
|
+
detectionPath: ".gemini/commands/openspec/proposal.toml"
|
|
13897
|
+
},
|
|
13898
|
+
{
|
|
13899
|
+
toolId: "auggie",
|
|
13900
|
+
detectionPath: ".augment/commands/openspec-proposal.md"
|
|
13901
|
+
},
|
|
13902
|
+
{
|
|
13903
|
+
toolId: "codebuddy",
|
|
13904
|
+
detectionPath: ".codebuddy/commands/openspec/proposal.md"
|
|
13905
|
+
},
|
|
13906
|
+
{
|
|
13907
|
+
toolId: "qoder",
|
|
13908
|
+
detectionPath: ".qoder/commands/openspec/proposal.md"
|
|
13909
|
+
},
|
|
13910
|
+
{
|
|
13911
|
+
toolId: "roocode",
|
|
13912
|
+
detectionPath: ".roo/commands/openspec-proposal.md"
|
|
13913
|
+
},
|
|
13914
|
+
{
|
|
13915
|
+
toolId: "kilocode",
|
|
13916
|
+
detectionPath: ".kilocode/workflows/openspec-proposal.md"
|
|
13917
|
+
},
|
|
13918
|
+
{
|
|
13919
|
+
toolId: "opencode",
|
|
13920
|
+
detectionPath: ".opencode/command/openspec-proposal.md"
|
|
13921
|
+
},
|
|
13922
|
+
{
|
|
13923
|
+
toolId: "factory",
|
|
13924
|
+
detectionPath: ".factory/commands/openspec-proposal.md"
|
|
13925
|
+
},
|
|
13926
|
+
{
|
|
13927
|
+
toolId: "crush",
|
|
13928
|
+
detectionPath: ".crush/commands/openspec/proposal.md"
|
|
13929
|
+
},
|
|
13930
|
+
{
|
|
13931
|
+
toolId: "costrict",
|
|
13932
|
+
detectionPath: ".cospec/openspec/commands/openspec-proposal.md"
|
|
13933
|
+
},
|
|
13934
|
+
{
|
|
13935
|
+
toolId: "qwen",
|
|
13936
|
+
detectionPath: ".qwen/commands/openspec-proposal.toml"
|
|
13937
|
+
},
|
|
13938
|
+
{
|
|
13939
|
+
toolId: "iflow",
|
|
13940
|
+
detectionPath: ".iflow/commands/openspec-proposal.md"
|
|
13941
|
+
},
|
|
13942
|
+
{
|
|
13943
|
+
toolId: "antigravity",
|
|
13944
|
+
detectionPath: ".agent/workflows/openspec-proposal.md"
|
|
13945
|
+
}
|
|
13946
|
+
];
|
|
13947
|
+
/**
|
|
13948
|
+
* 获取所有可用的工具 ID 列表
|
|
13949
|
+
*/
|
|
13950
|
+
function getAvailableToolIds() {
|
|
13951
|
+
return TOOL_CONFIGS.map((config) => config.toolId);
|
|
13952
|
+
}
|
|
13953
|
+
/**
|
|
13954
|
+
* 检测项目中已配置的工具(响应式)
|
|
13955
|
+
*
|
|
13956
|
+
* 使用 reactiveExists 检测文件,当文件变化时会自动触发更新。
|
|
13957
|
+
* 必须在 ReactiveContext 中调用才能获得响应式能力。
|
|
13958
|
+
*
|
|
13959
|
+
* @param projectDir 项目根目录
|
|
13960
|
+
* @returns 已配置的工具 ID 列表
|
|
13961
|
+
*/
|
|
13962
|
+
async function getConfiguredTools(projectDir) {
|
|
13963
|
+
const configured = [];
|
|
13964
|
+
for (const config of TOOL_CONFIGS) if (await reactiveExists(join(projectDir, config.detectionPath))) configured.push(config.toolId);
|
|
13965
|
+
return configured;
|
|
13966
|
+
}
|
|
13967
|
+
|
|
13968
|
+
//#endregion
|
|
13969
|
+
//#region ../server/src/reactive-subscription.ts
|
|
13970
|
+
/**
|
|
13971
|
+
* 创建响应式订阅
|
|
13972
|
+
*
|
|
13973
|
+
* 自动追踪 task 中的文件依赖,当依赖变更时自动重新执行并推送新数据。
|
|
13974
|
+
*
|
|
13975
|
+
* @param task 要执行的异步任务,内部的文件读取会被自动追踪
|
|
13976
|
+
* @returns tRPC observable
|
|
13977
|
+
*
|
|
13978
|
+
* @example
|
|
13979
|
+
* ```typescript
|
|
13980
|
+
* // 在 router 中使用
|
|
13981
|
+
* subscribe: publicProcedure.subscription(({ ctx }) => {
|
|
13982
|
+
* return createReactiveSubscription(() => ctx.adapter.listSpecsWithMeta())
|
|
13983
|
+
* })
|
|
13984
|
+
* ```
|
|
13985
|
+
*/
|
|
13986
|
+
function createReactiveSubscription(task) {
|
|
13987
|
+
return observable((emit) => {
|
|
13988
|
+
const context = new ReactiveContext();
|
|
13989
|
+
const controller = new AbortController();
|
|
13990
|
+
(async () => {
|
|
13991
|
+
try {
|
|
13992
|
+
for await (const data of context.stream(task, controller.signal)) emit.next(data);
|
|
13993
|
+
} catch (err) {
|
|
13994
|
+
if (!controller.signal.aborted) emit.error(err);
|
|
13995
|
+
}
|
|
13996
|
+
})();
|
|
13997
|
+
return () => {
|
|
13998
|
+
controller.abort();
|
|
13999
|
+
};
|
|
14000
|
+
});
|
|
14001
|
+
}
|
|
14002
|
+
/**
|
|
14003
|
+
* 创建带输入参数的响应式订阅
|
|
14004
|
+
*
|
|
14005
|
+
* @param task 接收输入参数的异步任务
|
|
14006
|
+
* @returns 返回一个函数,接收输入参数并返回 tRPC observable
|
|
14007
|
+
*
|
|
14008
|
+
* @example
|
|
14009
|
+
* ```typescript
|
|
14010
|
+
* // 在 router 中使用
|
|
14011
|
+
* subscribeOne: publicProcedure
|
|
14012
|
+
* .input(z.object({ id: z.string() }))
|
|
14013
|
+
* .subscription(({ ctx, input }) => {
|
|
14014
|
+
* return createReactiveSubscriptionWithInput(
|
|
14015
|
+
* (id: string) => ctx.adapter.readSpec(id)
|
|
14016
|
+
* )(input.id)
|
|
14017
|
+
* })
|
|
14018
|
+
* ```
|
|
14019
|
+
*/
|
|
14020
|
+
function createReactiveSubscriptionWithInput(task) {
|
|
14021
|
+
return (input) => {
|
|
14022
|
+
return createReactiveSubscription(() => task(input));
|
|
14023
|
+
};
|
|
14024
|
+
}
|
|
14025
|
+
|
|
13250
14026
|
//#endregion
|
|
13251
14027
|
//#region ../server/src/router.ts
|
|
13252
14028
|
const t = initTRPC.context().create();
|
|
@@ -13261,6 +14037,12 @@ const dashboardRouter = router({
|
|
|
13261
14037
|
}),
|
|
13262
14038
|
isInitialized: publicProcedure.query(async ({ ctx }) => {
|
|
13263
14039
|
return ctx.adapter.isInitialized();
|
|
14040
|
+
}),
|
|
14041
|
+
subscribe: publicProcedure.subscription(({ ctx }) => {
|
|
14042
|
+
return createReactiveSubscription(() => ctx.adapter.getDashboardData());
|
|
14043
|
+
}),
|
|
14044
|
+
subscribeInitialized: publicProcedure.subscription(({ ctx }) => {
|
|
14045
|
+
return createReactiveSubscription(() => ctx.adapter.isInitialized());
|
|
13264
14046
|
})
|
|
13265
14047
|
});
|
|
13266
14048
|
/**
|
|
@@ -13288,6 +14070,15 @@ const specRouter = router({
|
|
|
13288
14070
|
}),
|
|
13289
14071
|
validate: publicProcedure.input(objectType({ id: stringType() })).query(async ({ ctx, input }) => {
|
|
13290
14072
|
return ctx.adapter.validateSpec(input.id);
|
|
14073
|
+
}),
|
|
14074
|
+
subscribe: publicProcedure.subscription(({ ctx }) => {
|
|
14075
|
+
return createReactiveSubscription(() => ctx.adapter.listSpecsWithMeta());
|
|
14076
|
+
}),
|
|
14077
|
+
subscribeOne: publicProcedure.input(objectType({ id: stringType() })).subscription(({ ctx, input }) => {
|
|
14078
|
+
return createReactiveSubscriptionWithInput((id) => ctx.adapter.readSpec(id))(input.id);
|
|
14079
|
+
}),
|
|
14080
|
+
subscribeRaw: publicProcedure.input(objectType({ id: stringType() })).subscription(({ ctx, input }) => {
|
|
14081
|
+
return createReactiveSubscriptionWithInput((id) => ctx.adapter.readSpecRaw(id))(input.id);
|
|
13291
14082
|
})
|
|
13292
14083
|
});
|
|
13293
14084
|
/**
|
|
@@ -13330,6 +14121,15 @@ const changeRouter = router({
|
|
|
13330
14121
|
})).mutation(async ({ ctx, input }) => {
|
|
13331
14122
|
if (!await ctx.adapter.toggleTask(input.changeId, input.taskIndex, input.completed)) throw new Error(`Failed to toggle task ${input.taskIndex} in change ${input.changeId}`);
|
|
13332
14123
|
return { success: true };
|
|
14124
|
+
}),
|
|
14125
|
+
subscribe: publicProcedure.subscription(({ ctx }) => {
|
|
14126
|
+
return createReactiveSubscription(() => ctx.adapter.listChangesWithMeta());
|
|
14127
|
+
}),
|
|
14128
|
+
subscribeOne: publicProcedure.input(objectType({ id: stringType() })).subscription(({ ctx, input }) => {
|
|
14129
|
+
return createReactiveSubscriptionWithInput((id) => ctx.adapter.readChange(id))(input.id);
|
|
14130
|
+
}),
|
|
14131
|
+
subscribeRaw: publicProcedure.input(objectType({ id: stringType() })).subscription(({ ctx, input }) => {
|
|
14132
|
+
return createReactiveSubscriptionWithInput((id) => ctx.adapter.readChangeRaw(id))(input.id);
|
|
13333
14133
|
})
|
|
13334
14134
|
});
|
|
13335
14135
|
/**
|
|
@@ -13427,6 +14227,12 @@ const projectRouter = router({
|
|
|
13427
14227
|
saveAgentsMd: publicProcedure.input(objectType({ content: stringType() })).mutation(async ({ ctx, input }) => {
|
|
13428
14228
|
await ctx.adapter.writeAgentsMd(input.content);
|
|
13429
14229
|
return { success: true };
|
|
14230
|
+
}),
|
|
14231
|
+
subscribeProjectMd: publicProcedure.subscription(({ ctx }) => {
|
|
14232
|
+
return createReactiveSubscription(() => ctx.adapter.readProjectMd());
|
|
14233
|
+
}),
|
|
14234
|
+
subscribeAgentsMd: publicProcedure.subscription(({ ctx }) => {
|
|
14235
|
+
return createReactiveSubscription(() => ctx.adapter.readAgentsMd());
|
|
13430
14236
|
})
|
|
13431
14237
|
});
|
|
13432
14238
|
/**
|
|
@@ -13444,6 +14250,12 @@ const archiveRouter = router({
|
|
|
13444
14250
|
}),
|
|
13445
14251
|
getRaw: publicProcedure.input(objectType({ id: stringType() })).query(async ({ ctx, input }) => {
|
|
13446
14252
|
return ctx.adapter.readArchivedChangeRaw(input.id);
|
|
14253
|
+
}),
|
|
14254
|
+
subscribe: publicProcedure.subscription(({ ctx }) => {
|
|
14255
|
+
return createReactiveSubscription(() => ctx.adapter.listArchivedChangesWithMeta());
|
|
14256
|
+
}),
|
|
14257
|
+
subscribeOne: publicProcedure.input(objectType({ id: stringType() })).subscription(({ ctx, input }) => {
|
|
14258
|
+
return createReactiveSubscriptionWithInput((id) => ctx.adapter.readArchivedChange(id))(input.id);
|
|
13447
14259
|
})
|
|
13448
14260
|
});
|
|
13449
14261
|
objectType({
|
|
@@ -13517,6 +14329,75 @@ const realtimeRouter = router({
|
|
|
13517
14329
|
})
|
|
13518
14330
|
});
|
|
13519
14331
|
/**
|
|
14332
|
+
* Config router - configuration management
|
|
14333
|
+
*/
|
|
14334
|
+
const configRouter = router({
|
|
14335
|
+
get: publicProcedure.query(async ({ ctx }) => {
|
|
14336
|
+
return ctx.configManager.readConfig();
|
|
14337
|
+
}),
|
|
14338
|
+
update: publicProcedure.input(objectType({
|
|
14339
|
+
cli: objectType({ command: stringType() }).optional(),
|
|
14340
|
+
ui: objectType({ theme: enumType([
|
|
14341
|
+
"light",
|
|
14342
|
+
"dark",
|
|
14343
|
+
"system"
|
|
14344
|
+
]) }).optional()
|
|
14345
|
+
})).mutation(async ({ ctx, input }) => {
|
|
14346
|
+
await ctx.configManager.writeConfig(input);
|
|
14347
|
+
return { success: true };
|
|
14348
|
+
}),
|
|
14349
|
+
setCliCommand: publicProcedure.input(objectType({ command: stringType() })).mutation(async ({ ctx, input }) => {
|
|
14350
|
+
await ctx.configManager.setCliCommand(input.command);
|
|
14351
|
+
return { success: true };
|
|
14352
|
+
}),
|
|
14353
|
+
subscribe: publicProcedure.subscription(({ ctx }) => {
|
|
14354
|
+
return createReactiveSubscription(() => ctx.configManager.readConfig());
|
|
14355
|
+
})
|
|
14356
|
+
});
|
|
14357
|
+
/**
|
|
14358
|
+
* CLI router - execute external openspec CLI commands
|
|
14359
|
+
*/
|
|
14360
|
+
const cliRouter = router({
|
|
14361
|
+
checkAvailability: publicProcedure.query(async ({ ctx }) => {
|
|
14362
|
+
return ctx.cliExecutor.checkAvailability();
|
|
14363
|
+
}),
|
|
14364
|
+
getAvailableTools: publicProcedure.query(() => {
|
|
14365
|
+
return getAvailableToolIds();
|
|
14366
|
+
}),
|
|
14367
|
+
getConfiguredTools: publicProcedure.query(async ({ ctx }) => {
|
|
14368
|
+
return getConfiguredTools(ctx.projectDir);
|
|
14369
|
+
}),
|
|
14370
|
+
subscribeConfiguredTools: publicProcedure.subscription(({ ctx }) => {
|
|
14371
|
+
return createReactiveSubscription(() => getConfiguredTools(ctx.projectDir));
|
|
14372
|
+
}),
|
|
14373
|
+
init: publicProcedure.input(objectType({ tools: unionType([
|
|
14374
|
+
arrayType(stringType()),
|
|
14375
|
+
literalType("all"),
|
|
14376
|
+
literalType("none")
|
|
14377
|
+
]).optional() }).optional()).mutation(async ({ ctx, input }) => {
|
|
14378
|
+
return ctx.cliExecutor.init(input?.tools ?? "all");
|
|
14379
|
+
}),
|
|
14380
|
+
archive: publicProcedure.input(objectType({
|
|
14381
|
+
changeId: stringType(),
|
|
14382
|
+
skipSpecs: booleanType().optional(),
|
|
14383
|
+
noValidate: booleanType().optional()
|
|
14384
|
+
})).mutation(async ({ ctx, input }) => {
|
|
14385
|
+
return ctx.cliExecutor.archive(input.changeId, {
|
|
14386
|
+
skipSpecs: input.skipSpecs,
|
|
14387
|
+
noValidate: input.noValidate
|
|
14388
|
+
});
|
|
14389
|
+
}),
|
|
14390
|
+
validate: publicProcedure.input(objectType({
|
|
14391
|
+
type: enumType(["spec", "change"]).optional(),
|
|
14392
|
+
id: stringType().optional()
|
|
14393
|
+
})).mutation(async ({ ctx, input }) => {
|
|
14394
|
+
return ctx.cliExecutor.validate(input.type, input.id);
|
|
14395
|
+
}),
|
|
14396
|
+
execute: publicProcedure.input(objectType({ args: arrayType(stringType()) })).mutation(async ({ ctx, input }) => {
|
|
14397
|
+
return ctx.cliExecutor.execute(input.args);
|
|
14398
|
+
})
|
|
14399
|
+
});
|
|
14400
|
+
/**
|
|
13520
14401
|
* Main app router
|
|
13521
14402
|
*/
|
|
13522
14403
|
const appRouter = router({
|
|
@@ -13527,7 +14408,9 @@ const appRouter = router({
|
|
|
13527
14408
|
project: projectRouter,
|
|
13528
14409
|
ai: aiRouter,
|
|
13529
14410
|
init: initRouter,
|
|
13530
|
-
realtime: realtimeRouter
|
|
14411
|
+
realtime: realtimeRouter,
|
|
14412
|
+
config: configRouter,
|
|
14413
|
+
cli: cliRouter
|
|
13531
14414
|
});
|
|
13532
14415
|
|
|
13533
14416
|
//#endregion
|
|
@@ -13550,6 +14433,8 @@ const appRouter = router({
|
|
|
13550
14433
|
function createServer$2(config) {
|
|
13551
14434
|
const adapter = new OpenSpecAdapter(config.projectDir);
|
|
13552
14435
|
const providerManager = new ProviderManager(config.providers);
|
|
14436
|
+
const configManager = new ConfigManager(config.projectDir);
|
|
14437
|
+
const cliExecutor = new CliExecutor(configManager, config.projectDir);
|
|
13553
14438
|
const watcher = config.enableWatcher !== false ? new OpenSpecWatcher(config.projectDir) : void 0;
|
|
13554
14439
|
const app = new Hono();
|
|
13555
14440
|
const corsOrigins = config.corsOrigins ?? ["http://localhost:5173", "http://localhost:3000"];
|
|
@@ -13572,19 +14457,27 @@ function createServer$2(config) {
|
|
|
13572
14457
|
createContext: () => ({
|
|
13573
14458
|
adapter,
|
|
13574
14459
|
providerManager,
|
|
13575
|
-
|
|
14460
|
+
configManager,
|
|
14461
|
+
cliExecutor,
|
|
14462
|
+
watcher,
|
|
14463
|
+
projectDir: config.projectDir
|
|
13576
14464
|
})
|
|
13577
14465
|
});
|
|
13578
14466
|
});
|
|
13579
14467
|
const createContext = () => ({
|
|
13580
14468
|
adapter,
|
|
13581
14469
|
providerManager,
|
|
13582
|
-
|
|
14470
|
+
configManager,
|
|
14471
|
+
cliExecutor,
|
|
14472
|
+
watcher,
|
|
14473
|
+
projectDir: config.projectDir
|
|
13583
14474
|
});
|
|
13584
14475
|
return {
|
|
13585
14476
|
app,
|
|
13586
14477
|
adapter,
|
|
13587
14478
|
providerManager,
|
|
14479
|
+
configManager,
|
|
14480
|
+
cliExecutor,
|
|
13588
14481
|
watcher,
|
|
13589
14482
|
createContext,
|
|
13590
14483
|
port: config.port ?? 3100
|
|
@@ -13622,18 +14515,28 @@ function createWebSocketServer(server, httpServer) {
|
|
|
13622
14515
|
//#region src/index.ts
|
|
13623
14516
|
const __dirname = dirname$1(fileURLToPath(import.meta.url));
|
|
13624
14517
|
/**
|
|
13625
|
-
* Check if a port is available by trying to listen on it
|
|
14518
|
+
* Check if a port is available by trying to listen on it.
|
|
14519
|
+
* Tests both 127.0.0.1 and 0.0.0.0 to ensure the port is truly available.
|
|
13626
14520
|
*/
|
|
13627
14521
|
function isPortAvailable(port) {
|
|
13628
|
-
return new Promise((resolve$
|
|
13629
|
-
const
|
|
13630
|
-
|
|
13631
|
-
resolve$
|
|
13632
|
-
});
|
|
13633
|
-
|
|
13634
|
-
|
|
14522
|
+
return new Promise((resolve$2) => {
|
|
14523
|
+
const server1 = createServer$1();
|
|
14524
|
+
server1.once("error", () => {
|
|
14525
|
+
resolve$2(false);
|
|
14526
|
+
});
|
|
14527
|
+
server1.once("listening", () => {
|
|
14528
|
+
server1.close(() => {
|
|
14529
|
+
const server2 = createServer$1();
|
|
14530
|
+
server2.once("error", () => {
|
|
14531
|
+
resolve$2(false);
|
|
14532
|
+
});
|
|
14533
|
+
server2.once("listening", () => {
|
|
14534
|
+
server2.close(() => resolve$2(true));
|
|
14535
|
+
});
|
|
14536
|
+
server2.listen(port, "0.0.0.0");
|
|
14537
|
+
});
|
|
13635
14538
|
});
|
|
13636
|
-
|
|
14539
|
+
server1.listen(port, "127.0.0.1");
|
|
13637
14540
|
});
|
|
13638
14541
|
}
|
|
13639
14542
|
/**
|
|
@@ -13702,8 +14605,8 @@ async function startServer(options = {}) {
|
|
|
13702
14605
|
const path$1 = c.req.path === "/" ? "/index.html" : c.req.path;
|
|
13703
14606
|
if (path$1.startsWith("/trpc")) return next();
|
|
13704
14607
|
const filePath = join$1(webDir, path$1);
|
|
13705
|
-
if (existsSync(filePath) && statSync
|
|
13706
|
-
const content = readFileSync
|
|
14608
|
+
if (existsSync(filePath) && statSync(filePath).isFile()) {
|
|
14609
|
+
const content = readFileSync(filePath);
|
|
13707
14610
|
const contentType = {
|
|
13708
14611
|
html: "text/html",
|
|
13709
14612
|
js: "application/javascript",
|
|
@@ -13724,7 +14627,7 @@ async function startServer(options = {}) {
|
|
|
13724
14627
|
if (!path$1.includes(".")) {
|
|
13725
14628
|
const indexPath = join$1(webDir, "index.html");
|
|
13726
14629
|
if (existsSync(indexPath)) {
|
|
13727
|
-
const content = readFileSync
|
|
14630
|
+
const content = readFileSync(indexPath, "utf-8");
|
|
13728
14631
|
return c.html(content);
|
|
13729
14632
|
}
|
|
13730
14633
|
}
|