@pi-chamber/web 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/README.md +43 -0
- package/bin/cli-entry.js +55 -0
- package/bin/cli-output.js +140 -0
- package/bin/cli-output.test.js +36 -0
- package/bin/cli.js +383 -0
- package/bin/cli.test.js +1065 -0
- package/bin/lib/DOCUMENTATION.md +31 -0
- package/bin/lib/cli-api-target.js +74 -0
- package/bin/lib/cli-args.js +846 -0
- package/bin/lib/cli-errors.js +18 -0
- package/bin/lib/cli-http.js +252 -0
- package/bin/lib/cli-lifecycle.js +421 -0
- package/bin/lib/cli-log-files.js +93 -0
- package/bin/lib/cli-network.js +186 -0
- package/bin/lib/cli-paths.js +128 -0
- package/bin/lib/cli-ports.js +51 -0
- package/bin/lib/cli-process.js +342 -0
- package/bin/lib/cli-startup.js +371 -0
- package/bin/lib/cli-tunnel-capabilities.js +9 -0
- package/bin/lib/cli-tunnel-profiles.js +366 -0
- package/bin/lib/cli-tunnel-utils.js +300 -0
- package/bin/lib/commands-connect-url.js +329 -0
- package/bin/lib/commands-lifecycle.js +396 -0
- package/bin/lib/commands-logs.js +110 -0
- package/bin/lib/commands-serve.js +419 -0
- package/bin/lib/commands-startup.js +64 -0
- package/bin/lib/commands-status.js +114 -0
- package/bin/lib/commands-tunnel.js +1761 -0
- package/bin/lib/commands-update.js +184 -0
- package/bin/lib/commands-update.test.js +98 -0
- package/dist/apple-touch-icon-120x120.png +0 -0
- package/dist/apple-touch-icon-152x152.png +0 -0
- package/dist/apple-touch-icon-167x167.png +0 -0
- package/dist/apple-touch-icon-180x180.png +0 -0
- package/dist/apple-touch-icon.png +0 -0
- package/dist/apple-touch-icon.svg +13 -0
- package/dist/assets/CommitSection-DGGArAb2.js +1 -0
- package/dist/assets/DiagramEditor-ig5nYV6t.js +1 -0
- package/dist/assets/DiagramView-8Qr6pwwI.js +1 -0
- package/dist/assets/DiffView-BHS4m_K7.js +18 -0
- package/dist/assets/FilesView-DmZcdwl-.js +14 -0
- package/dist/assets/GitView-gfHxmc66.js +6 -0
- package/dist/assets/JsonTreeView-DbFqka3P.js +1 -0
- package/dist/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
- package/dist/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
- package/dist/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
- package/dist/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
- package/dist/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
- package/dist/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
- package/dist/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
- package/dist/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
- package/dist/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
- package/dist/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
- package/dist/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
- package/dist/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
- package/dist/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
- package/dist/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
- package/dist/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
- package/dist/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
- package/dist/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
- package/dist/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
- package/dist/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
- package/dist/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
- package/dist/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
- package/dist/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
- package/dist/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
- package/dist/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
- package/dist/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
- package/dist/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
- package/dist/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
- package/dist/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
- package/dist/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
- package/dist/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
- package/dist/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
- package/dist/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
- package/dist/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
- package/dist/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
- package/dist/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
- package/dist/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
- package/dist/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
- package/dist/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
- package/dist/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
- package/dist/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
- package/dist/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
- package/dist/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
- package/dist/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
- package/dist/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
- package/dist/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
- package/dist/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
- package/dist/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
- package/dist/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
- package/dist/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
- package/dist/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
- package/dist/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
- package/dist/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
- package/dist/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
- package/dist/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
- package/dist/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
- package/dist/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
- package/dist/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
- package/dist/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
- package/dist/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
- package/dist/assets/MarkdownRendererImpl-B07lHhBl.js +12 -0
- package/dist/assets/PatchDiff-DD9pAs47.js +1 -0
- package/dist/assets/PierreDiffViewer-Bo0_BCT7.js +136 -0
- package/dist/assets/SessionSwitcherDropdown-t7WZX24f.js +1 -0
- package/dist/assets/SettingsView-CtsS4oDI.js +3 -0
- package/dist/assets/SettingsWindow-DIRGn3eQ.js +1 -0
- package/dist/assets/TerminalView-BV3KOtdA.css +1 -0
- package/dist/assets/TerminalView-DUwO1CCY.js +46 -0
- package/dist/assets/ToolOutputDialog-iZG2ey4T.js +15 -0
- package/dist/assets/ToolPartDiffPreview-FTexlZSz.js +8 -0
- package/dist/assets/VirtualizedFileDiff-DD5IznIe.js +10 -0
- package/dist/assets/Virtualizer-BxW2CJGB.js +1 -0
- package/dist/assets/abap-BdImnpbu.js +1 -0
- package/dist/assets/abap-Dg923wIy.js +1 -0
- package/dist/assets/actionscript-3-8WTK4YD2.js +1 -0
- package/dist/assets/actionscript-3-CoDkCxhg.js +1 -0
- package/dist/assets/ada-CTi7YQtL.js +1 -0
- package/dist/assets/ada-bCR0ucgS.js +1 -0
- package/dist/assets/andromeeda-C4gqWexZ.js +1 -0
- package/dist/assets/andromeeda-CT0vplkN.js +1 -0
- package/dist/assets/angular-html-CU67Zn6k.js +1 -0
- package/dist/assets/angular-html-D2R1pe45.js +1 -0
- package/dist/assets/angular-ts-BDmYQnhm.js +1 -0
- package/dist/assets/angular-ts-BwZT4LLn.js +1 -0
- package/dist/assets/apache-PcO-DBxQ.js +1 -0
- package/dist/assets/apache-Pmp26Uib.js +1 -0
- package/dist/assets/apex-CnCu5_gb.js +1 -0
- package/dist/assets/apex-D8_7TLub.js +1 -0
- package/dist/assets/apl-B4CMkyY2.js +1 -0
- package/dist/assets/apl-DkfyvC6M.js +1 -0
- package/dist/assets/apl-dKokRX4l.js +1 -0
- package/dist/assets/appThemeRegistry-BK0dHKa6.js +1 -0
- package/dist/assets/applescript-Co6uUVPk.js +1 -0
- package/dist/assets/applescript-Ds_W2jPS.js +1 -0
- package/dist/assets/ara-BRHolxvo.js +1 -0
- package/dist/assets/ara-C1--7J61.js +1 -0
- package/dist/assets/asciiarmor-Df11BRmG.js +1 -0
- package/dist/assets/asciidoc-Ve4PFQV2.js +1 -0
- package/dist/assets/asciidoc-nTfEFGxb.js +1 -0
- package/dist/assets/asm-CTFRnVOM.js +1 -0
- package/dist/assets/asm-D_Q5rh1f.js +1 -0
- package/dist/assets/asn1-EdZsLKOL.js +1 -0
- package/dist/assets/asterisk-B-8jnY81.js +1 -0
- package/dist/assets/astro-CbQHKStN.js +1 -0
- package/dist/assets/astro-DtPAdMss.js +1 -0
- package/dist/assets/aurora-x-C0VKFdlO.js +1 -0
- package/dist/assets/aurora-x-D-2ljcwZ.js +1 -0
- package/dist/assets/awk-CrWgQKhV.js +1 -0
- package/dist/assets/awk-DMzUqQB5.js +1 -0
- package/dist/assets/ayu-dark-DYE7WIF3.js +1 -0
- package/dist/assets/ayu-dark-wZ6ORhBL.js +1 -0
- package/dist/assets/ayu-light-BA47KaF1.js +1 -0
- package/dist/assets/ayu-light-xXLJ_Ch1.js +1 -0
- package/dist/assets/ayu-mirage-32ctXXKs.js +1 -0
- package/dist/assets/ayu-mirage-CQckA_sP.js +1 -0
- package/dist/assets/ballerina-BFfxhgS-.js +1 -0
- package/dist/assets/ballerina-a0yOSyc5.js +1 -0
- package/dist/assets/bat-B0cHNp6s.js +1 -0
- package/dist/assets/bat-BkioyH1T.js +1 -0
- package/dist/assets/beancount-dPJi_gci.js +1 -0
- package/dist/assets/beancount-k_qm7-4y.js +1 -0
- package/dist/assets/berry-CVKoklHx.js +1 -0
- package/dist/assets/berry-uYugtg8r.js +1 -0
- package/dist/assets/bibtex-CHM0blh-.js +1 -0
- package/dist/assets/bibtex-DRcCXNgM.js +1 -0
- package/dist/assets/bicep-Bmn6On1c.js +1 -0
- package/dist/assets/bicep-jWz-hQ8I.js +1 -0
- package/dist/assets/bird2-DPOp833l.js +1 -0
- package/dist/assets/bird2-DaGXIRGu.js +1 -0
- package/dist/assets/blade-D4QpJJKB.js +1 -0
- package/dist/assets/blade-QlWdzBxO.js +1 -0
- package/dist/assets/brainfuck-C4LP7Hcl.js +1 -0
- package/dist/assets/bsl-BO_Y6i37.js +1 -0
- package/dist/assets/bsl-jNujDQK2.js +1 -0
- package/dist/assets/c-BG-tZi7o.js +1 -0
- package/dist/assets/c-BIGW1oBm.js +1 -0
- package/dist/assets/c3-Fcb8_HKm.js +1 -0
- package/dist/assets/c3-eo99z4R2.js +1 -0
- package/dist/assets/cadence-BuaeHeSu.js +1 -0
- package/dist/assets/cadence-Bv_4Rxtq.js +1 -0
- package/dist/assets/cairo-BYiDgOF2.js +1 -0
- package/dist/assets/cairo-KRGpt6FW.js +1 -0
- package/dist/assets/catppuccin-frappe-CfssSZMA.js +1 -0
- package/dist/assets/catppuccin-frappe-DFWUc33u.js +1 -0
- package/dist/assets/catppuccin-latte-C9dUb6Cb.js +1 -0
- package/dist/assets/catppuccin-latte-DoBaWTGn.js +1 -0
- package/dist/assets/catppuccin-macchiato-BkAuvdZC.js +1 -0
- package/dist/assets/catppuccin-macchiato-DQyhUUbL.js +1 -0
- package/dist/assets/catppuccin-mocha-D87Tk5Gz.js +1 -0
- package/dist/assets/catppuccin-mocha-D9fcC0iT.js +1 -0
- package/dist/assets/clarity-CC_0IFDj.js +1 -0
- package/dist/assets/clarity-D53aC0YG.js +1 -0
- package/dist/assets/clike-B9uivgTg.js +1 -0
- package/dist/assets/clojure-BMjYHr_A.js +1 -0
- package/dist/assets/clojure-KnfDkB_V.js +1 -0
- package/dist/assets/clojure-P80f7IUj.js +1 -0
- package/dist/assets/cmake-BQqOBYOt.js +1 -0
- package/dist/assets/cmake-D1j8_8rp.js +1 -0
- package/dist/assets/cmake-bh2MLnkv.js +1 -0
- package/dist/assets/cobol-CWcv1MsR.js +1 -0
- package/dist/assets/cobol-dIXRVV9Y.js +1 -0
- package/dist/assets/cobol-nwyudZeR.js +1 -0
- package/dist/assets/codeowners-Bp6g37R7.js +1 -0
- package/dist/assets/codeowners-CtwIM3TN.js +1 -0
- package/dist/assets/codeql-D164juXQ.js +1 -0
- package/dist/assets/codeql-DsOJ9woJ.js +1 -0
- package/dist/assets/coffee-Bk-z6oI_.js +1 -0
- package/dist/assets/coffee-Ch7k5sss.js +1 -0
- package/dist/assets/coffeescript-S37ZYGWr.js +1 -0
- package/dist/assets/collapsible-ODWWxk4f.js +1 -0
- package/dist/assets/common-lisp-Cg-RD9OK.js +1 -0
- package/dist/assets/common-lisp-DXc9XcIM.js +1 -0
- package/dist/assets/commonlisp-DBKNyK5s.js +1 -0
- package/dist/assets/computeVirtualFileMetrics-DdXkLFAD.js +3 -0
- package/dist/assets/contextFileOpenGuard-uC4OerzL.js +1 -0
- package/dist/assets/coq-BBL5-mG4.js +1 -0
- package/dist/assets/coq-DkFqJrB1.js +1 -0
- package/dist/assets/cpp-C0lzDePx.js +1 -0
- package/dist/assets/cpp-CofmeUqb.js +1 -0
- package/dist/assets/crystal-Bhade0k2.js +1 -0
- package/dist/assets/crystal-SjHAIU92.js +1 -0
- package/dist/assets/crystal-tKQVLTB8.js +1 -0
- package/dist/assets/csharp-B7nLXyi8.js +1 -0
- package/dist/assets/csharp-COcwbKMJ.js +1 -0
- package/dist/assets/css-BnMrqG3P.js +1 -0
- package/dist/assets/css-DPfMkruS.js +1 -0
- package/dist/assets/css-PZBBrkI0.js +1 -0
- package/dist/assets/csv-D5aBhKLr.js +1 -0
- package/dist/assets/csv-fuZLfV_i.js +1 -0
- package/dist/assets/cue-D82EKSYY.js +1 -0
- package/dist/assets/cue-WCin6O4e.js +1 -0
- package/dist/assets/cypher-COkxafJQ.js +1 -0
- package/dist/assets/cypher-C_CwsFkJ.js +1 -0
- package/dist/assets/cypher-DYQk2G82.js +1 -0
- package/dist/assets/d-85-TOEBH.js +1 -0
- package/dist/assets/d-DVc-yF-l.js +1 -0
- package/dist/assets/d-pRatUO7H.js +1 -0
- package/dist/assets/dark-plus-C3mMm8J8.js +1 -0
- package/dist/assets/dark-plus-DLU5INA3.js +1 -0
- package/dist/assets/dart-CF10PKvl.js +1 -0
- package/dist/assets/dart-YLd5yyRs.js +1 -0
- package/dist/assets/dax-CEL-wOlO.js +1 -0
- package/dist/assets/dax-oldcCaJg.js +1 -0
- package/dist/assets/desktop-BmXAJ9_W.js +1 -0
- package/dist/assets/desktop-DZfyBMgF.js +1 -0
- package/dist/assets/diff-D97Zzqfu.js +1 -0
- package/dist/assets/diff-DbItnlRl.js +1 -0
- package/dist/assets/diff-jW5kmiru.js +1 -0
- package/dist/assets/docker-BcOcwvcX.js +1 -0
- package/dist/assets/docker-DG9neUKd.js +1 -0
- package/dist/assets/dockerfile-BKs6k2Af.js +1 -0
- package/dist/assets/document-attachments-BslE4Ns7.js +30 -0
- package/dist/assets/dotenv-Bea-FkXq.js +1 -0
- package/dist/assets/dotenv-Da5cRb03.js +1 -0
- package/dist/assets/dracula-BzJJZx-M.js +1 -0
- package/dist/assets/dracula-XcYHBZy6.js +1 -0
- package/dist/assets/dracula-soft-BPeX9gxW.js +1 -0
- package/dist/assets/dracula-soft-BXkSAIEj.js +1 -0
- package/dist/assets/dream-maker-BtqSS_iP.js +1 -0
- package/dist/assets/dream-maker-DAHQwpwp.js +1 -0
- package/dist/assets/dtd-DF_7sFjM.js +1 -0
- package/dist/assets/dylan-DwRh75JA.js +1 -0
- package/dist/assets/ebnf-CDyGwa7X.js +1 -0
- package/dist/assets/ecl-Cabwm37j.js +1 -0
- package/dist/assets/edge-BkV0erSs.js +1 -0
- package/dist/assets/edge-zt3dU95k.js +1 -0
- package/dist/assets/eiffel-CnydiIhH.js +1 -0
- package/dist/assets/elixir-CDX3lj18.js +1 -0
- package/dist/assets/elixir-JolcG37e.js +1 -0
- package/dist/assets/elm-BjIDgKzz.js +1 -0
- package/dist/assets/elm-DbKCFpqz.js +1 -0
- package/dist/assets/elm-vLlmbW-K.js +1 -0
- package/dist/assets/emacs-lisp-C9XAeP06.js +1 -0
- package/dist/assets/emacs-lisp-DMBh55R4.js +1 -0
- package/dist/assets/erb-B12qg9BL.js +1 -0
- package/dist/assets/erb-rsjdL4fB.js +1 -0
- package/dist/assets/erlang-BNw1qcRV.js +1 -0
- package/dist/assets/erlang-DNeY46SW.js +1 -0
- package/dist/assets/erlang-DsQrWhSR.js +1 -0
- package/dist/assets/everforest-dark-BgDCqdQA.js +1 -0
- package/dist/assets/everforest-dark-WOub3wle.js +1 -0
- package/dist/assets/everforest-light-C8M2exoo.js +1 -0
- package/dist/assets/everforest-light-DCUg8VH8.js +1 -0
- package/dist/assets/factor-kuTfRLto.js +1 -0
- package/dist/assets/fcl-Kvtd6kyn.js +1 -0
- package/dist/assets/fennel-BYunw83y.js +1 -0
- package/dist/assets/fennel-DBMsxtrh.js +1 -0
- package/dist/assets/fish-BvzEVeQv.js +1 -0
- package/dist/assets/fish-BxskpYPN.js +1 -0
- package/dist/assets/fluent-Bwo2XpEt.js +1 -0
- package/dist/assets/fluent-C4IJs8-o.js +1 -0
- package/dist/assets/forth-Ffai-XNe.js +1 -0
- package/dist/assets/fortran-DYz_wnZ1.js +1 -0
- package/dist/assets/fortran-fixed-form-CkoXwp7k.js +1 -0
- package/dist/assets/fortran-fixed-form-DFFnVrrY.js +1 -0
- package/dist/assets/fortran-free-form-BxgE0vQu.js +1 -0
- package/dist/assets/fortran-free-form-DB5N_DgY.js +1 -0
- package/dist/assets/fsharp-CXgrBDvD.js +1 -0
- package/dist/assets/fsharp-DRAF1jsN.js +1 -0
- package/dist/assets/gas-Bneqetm1.js +1 -0
- package/dist/assets/gdresource-BD-1uzY7.js +1 -0
- package/dist/assets/gdresource-BOOCDP_w.js +1 -0
- package/dist/assets/gdscript-C3mT4Uym.js +1 -0
- package/dist/assets/gdscript-C5YyOfLZ.js +1 -0
- package/dist/assets/gdshader-DkwncUOv.js +1 -0
- package/dist/assets/gdshader-G7X6ysY3.js +1 -0
- package/dist/assets/genie-D0YGMca9.js +1 -0
- package/dist/assets/genie-DSsjfZMX.js +1 -0
- package/dist/assets/gherkin-Cl2Fw-_0.js +1 -0
- package/dist/assets/gherkin-DyxjwDmM.js +1 -0
- package/dist/assets/gherkin-heZmZLOM.js +1 -0
- package/dist/assets/git-commit-CJPsQObV.js +1 -0
- package/dist/assets/git-commit-F4YmCXRG.js +1 -0
- package/dist/assets/git-rebase-DeZqv4fp.js +1 -0
- package/dist/assets/git-rebase-r7XF79zn.js +1 -0
- package/dist/assets/github-dark-BQV5ugrc.js +1 -0
- package/dist/assets/github-dark-DHJKELXO.js +1 -0
- package/dist/assets/github-dark-default-Cuk6v7N8.js +1 -0
- package/dist/assets/github-dark-default-DLWDM7FH.js +1 -0
- package/dist/assets/github-dark-dimmed-D1FmiR_p.js +1 -0
- package/dist/assets/github-dark-dimmed-DH5Ifo-i.js +1 -0
- package/dist/assets/github-dark-high-contrast-BilLoi0o.js +1 -0
- package/dist/assets/github-dark-high-contrast-E3gJ1_iC.js +1 -0
- package/dist/assets/github-light-DAi9KRSo.js +1 -0
- package/dist/assets/github-light-Rcb2q3L5.js +1 -0
- package/dist/assets/github-light-default-CQ75esMw.js +1 -0
- package/dist/assets/github-light-default-D7oLnXFd.js +1 -0
- package/dist/assets/github-light-high-contrast-BfjtVDDH.js +1 -0
- package/dist/assets/github-light-high-contrast-CRF1wyLy.js +1 -0
- package/dist/assets/gleam-BeBqoPwE.js +1 -0
- package/dist/assets/gleam-BspZqrRM.js +1 -0
- package/dist/assets/glimmer-js-BuyKUjfV.js +1 -0
- package/dist/assets/glimmer-js-Rg0-pVw9.js +1 -0
- package/dist/assets/glimmer-ts-D9iniPFN.js +1 -0
- package/dist/assets/glimmer-ts-U6CK756n.js +1 -0
- package/dist/assets/glsl-BOG9l2nV.js +1 -0
- package/dist/assets/glsl-DplSGwfg.js +1 -0
- package/dist/assets/gn-Dxr0sSUf.js +1 -0
- package/dist/assets/gn-n2N0HUVH.js +1 -0
- package/dist/assets/gnuplot-CtW3R_Hz.js +1 -0
- package/dist/assets/gnuplot-DdkO51Og.js +1 -0
- package/dist/assets/go-BBr2UQCn.js +1 -0
- package/dist/assets/go-CxLEBnE3.js +1 -0
- package/dist/assets/graphql-B6XHM4uj.js +1 -0
- package/dist/assets/graphql-ChdNCCLP.js +1 -0
- package/dist/assets/groovy-D9Dt4D0W.js +1 -0
- package/dist/assets/groovy-DFxE31Qs.js +1 -0
- package/dist/assets/groovy-gcz8RCvz.js +1 -0
- package/dist/assets/gruvbox-dark-hard-C37G1gbc.js +1 -0
- package/dist/assets/gruvbox-dark-hard-CFHQjOhq.js +1 -0
- package/dist/assets/gruvbox-dark-medium-C3G9m3TV.js +1 -0
- package/dist/assets/gruvbox-dark-medium-GsRaNv29.js +1 -0
- package/dist/assets/gruvbox-dark-soft-CVdnzihN.js +1 -0
- package/dist/assets/gruvbox-dark-soft-eZmcmZ9I.js +1 -0
- package/dist/assets/gruvbox-light-hard-BYj-vz_3.js +1 -0
- package/dist/assets/gruvbox-light-hard-CH1njM8p.js +1 -0
- package/dist/assets/gruvbox-light-medium-BdQlDiiF.js +1 -0
- package/dist/assets/gruvbox-light-medium-DRw_LuNl.js +1 -0
- package/dist/assets/gruvbox-light-soft-BGNwjiRh.js +1 -0
- package/dist/assets/gruvbox-light-soft-hJgmCMqR.js +1 -0
- package/dist/assets/hack-CaT9iCJl.js +1 -0
- package/dist/assets/hack-Cxa-FaZ4.js +1 -0
- package/dist/assets/haml-2e0EI6Is.js +1 -0
- package/dist/assets/haml-B8DHNrY2.js +1 -0
- package/dist/assets/handlebars-BL8al0AC.js +1 -0
- package/dist/assets/handlebars-CojHdNmS.js +1 -0
- package/dist/assets/haskell-BfdhR_WN.js +1 -0
- package/dist/assets/haskell-Cw1EW3IL.js +1 -0
- package/dist/assets/haskell-Df6bDoY_.js +1 -0
- package/dist/assets/haxe-CzTSHFRz.js +1 -0
- package/dist/assets/haxe-DVIybFAS.js +1 -0
- package/dist/assets/haxe-H-WmDvRZ.js +1 -0
- package/dist/assets/hcl-B6OePqme.js +1 -0
- package/dist/assets/hcl-BWvSN4gD.js +1 -0
- package/dist/assets/hjson-D2jn7IRw.js +1 -0
- package/dist/assets/hjson-D5-asLiD.js +1 -0
- package/dist/assets/hlsl-D3lLCCz7.js +1 -0
- package/dist/assets/hlsl-MKeth5V9.js +1 -0
- package/dist/assets/horizon-BUw7H-hv.js +1 -0
- package/dist/assets/horizon-bright-Cn-bp-IR.js +1 -0
- package/dist/assets/horizon-bright-CoQRYDvy.js +1 -0
- package/dist/assets/horizon-cslQss18.js +1 -0
- package/dist/assets/houston-DnULxvSX.js +1 -0
- package/dist/assets/houston-RHIKkNfR.js +1 -0
- package/dist/assets/html-DOf0XnR2.js +1 -0
- package/dist/assets/html-GMplVEZG.js +1 -0
- package/dist/assets/html-derivative-BFtXZ54Q.js +1 -0
- package/dist/assets/html-derivative-DfCZ6Hio.js +1 -0
- package/dist/assets/http-CG3jvXcS.js +1 -0
- package/dist/assets/http-DBlCnlav.js +1 -0
- package/dist/assets/http-jrhK8wxY.js +1 -0
- package/dist/assets/hurl-CxQrh4RI.js +1 -0
- package/dist/assets/hurl-irOxFIW8.js +1 -0
- package/dist/assets/hxml-Bvhsp5Yf.js +1 -0
- package/dist/assets/hxml-CkS3Eie7.js +1 -0
- package/dist/assets/hy-DFXneXwc.js +1 -0
- package/dist/assets/hy-DI6mCUnw.js +1 -0
- package/dist/assets/idl-BEugSyMb.js +1 -0
- package/dist/assets/imba-D-Pk5S0X.js +1 -0
- package/dist/assets/imba-DGztddWO.js +1 -0
- package/dist/assets/index-CRB7i1S2.js +8 -0
- package/dist/assets/index-C_Mo87uS.css +1 -0
- package/dist/assets/index-MSKFS67y.js +1 -0
- package/dist/assets/ini-BEwlwnbL.js +1 -0
- package/dist/assets/ini-Dix3pTmg.js +1 -0
- package/dist/assets/isDiffPlainText-CeCQwHA2.js +1 -0
- package/dist/assets/isFilePlainText-BcfhE9FH.js +1 -0
- package/dist/assets/java-BTDmaRNO.js +1 -0
- package/dist/assets/java-CylS5w8V.js +1 -0
- package/dist/assets/javascript-BO_2ioGC.js +1 -0
- package/dist/assets/javascript-WyGyM1wV.js +1 -0
- package/dist/assets/javascript-wDzz0qaB.js +1 -0
- package/dist/assets/jinja-4LBKfQ-Z.js +1 -0
- package/dist/assets/jinja-D0mZ3q2r.js +1 -0
- package/dist/assets/jison-B9XHrcYJ.js +1 -0
- package/dist/assets/jison-wvAkD_A8.js +1 -0
- package/dist/assets/json-B8JryHcM.js +1 -0
- package/dist/assets/json-Cp-IABpG.js +1 -0
- package/dist/assets/json5-C9tS-k6U.js +1 -0
- package/dist/assets/json5-Cdatmj9n.js +1 -0
- package/dist/assets/jsonc-CvUCa1o5.js +1 -0
- package/dist/assets/jsonc-Des-eS-w.js +1 -0
- package/dist/assets/jsonl-9I_GBd5L.js +1 -0
- package/dist/assets/jsonl-DcaNXYhu.js +1 -0
- package/dist/assets/jsonnet-DFQXde-d.js +1 -0
- package/dist/assets/jsonnet-XoenQ2oc.js +1 -0
- package/dist/assets/jssm-C2t-YnRu.js +1 -0
- package/dist/assets/jssm-CWE2S0Z5.js +1 -0
- package/dist/assets/jsx-8cLv2Six.js +1 -0
- package/dist/assets/jsx-g9-lgVsj.js +1 -0
- package/dist/assets/julia-C1oINnwa.js +1 -0
- package/dist/assets/julia-CxzCAyBv.js +1 -0
- package/dist/assets/julia-DuME0IfC.js +1 -0
- package/dist/assets/just-Bo3fV9RY.js +1 -0
- package/dist/assets/just-Cw27pwNe.js +1 -0
- package/dist/assets/kanagawa-dragon-BOvIHJVT.js +1 -0
- package/dist/assets/kanagawa-dragon-CkXjmgJE.js +1 -0
- package/dist/assets/kanagawa-lotus-Bwcd7Bvx.js +1 -0
- package/dist/assets/kanagawa-lotus-CfQXZHmo.js +1 -0
- package/dist/assets/kanagawa-wave-0SAlpf0s.js +1 -0
- package/dist/assets/kanagawa-wave-DWedfzmr.js +1 -0
- package/dist/assets/kdl-CMbyIXjQ.js +1 -0
- package/dist/assets/kdl-DV7GczEv.js +1 -0
- package/dist/assets/kimi-for-coding-eVEHug_h.svg +3 -0
- package/dist/assets/kotlin-BQy537ds.js +1 -0
- package/dist/assets/kotlin-BdnUsdx6.js +1 -0
- package/dist/assets/kusto-B_y0uMaZ.js +1 -0
- package/dist/assets/kusto-DZf3V79B.js +1 -0
- package/dist/assets/laserwave-DUszq2jm.js +1 -0
- package/dist/assets/laserwave-si7wuSrt.js +1 -0
- package/dist/assets/latex-CWtU0Tv5.js +1 -0
- package/dist/assets/latex-CZAhoKip.js +1 -0
- package/dist/assets/lean-BZvkOJ9d.js +1 -0
- package/dist/assets/lean-C4RHkN41.js +1 -0
- package/dist/assets/less-B1dDrJ26.js +1 -0
- package/dist/assets/less-XNfgnZC7.js +1 -0
- package/dist/assets/light-plus-B7mTdjB0.js +1 -0
- package/dist/assets/light-plus-g_Fi9vkG.js +1 -0
- package/dist/assets/liquid-DFlqVvG0.js +1 -0
- package/dist/assets/liquid-DYVedYrR.js +1 -0
- package/dist/assets/livescript-BwQOo05w.js +1 -0
- package/dist/assets/llmapi-detailed-Pup_107f.svg +28 -0
- package/dist/assets/llvm-BJRUtCev.js +1 -0
- package/dist/assets/llvm-DjAJT7YJ.js +1 -0
- package/dist/assets/log-2UxHyX5q.js +1 -0
- package/dist/assets/log-CcaF_xNv.js +1 -0
- package/dist/assets/logo-BtOb2qkB.js +1 -0
- package/dist/assets/logo-D9WQLoKQ.js +1 -0
- package/dist/assets/lua-8Av-h-ns.js +1 -0
- package/dist/assets/lua-BaeVxFsk.js +1 -0
- package/dist/assets/lua-BgMRiT3U.js +1 -0
- package/dist/assets/luau-BTSFCcFb.js +1 -0
- package/dist/assets/luau-C-HG3fhB.js +1 -0
- package/dist/assets/main-CPygRDd9.js +134 -0
- package/dist/assets/main-DZg4qzOe.js +2 -0
- package/dist/assets/make-CHLpvVh8.js +1 -0
- package/dist/assets/make-DXVx14cr.js +1 -0
- package/dist/assets/markdown-Cvjx9yec.js +1 -0
- package/dist/assets/markdown-DX-TNHnc.js +1 -0
- package/dist/assets/markdown-shiki.worker-DnvH1S1x.js +12 -0
- package/dist/assets/markdownCore-BULtt6Xa.js +2 -0
- package/dist/assets/marko-CnJfTvn9.js +1 -0
- package/dist/assets/marko-DiIoNBFw.js +1 -0
- package/dist/assets/material-theme-CIJ3cGaW.js +1 -0
- package/dist/assets/material-theme-D5KoaKCx.js +1 -0
- package/dist/assets/material-theme-darker-BWg3m1GB.js +1 -0
- package/dist/assets/material-theme-darker-BfHTSMKl.js +1 -0
- package/dist/assets/material-theme-lighter-B0m2ddpp.js +1 -0
- package/dist/assets/material-theme-lighter-DR8HWMN7.js +1 -0
- package/dist/assets/material-theme-ocean-CZf9pk28.js +1 -0
- package/dist/assets/material-theme-ocean-CyktbL80.js +1 -0
- package/dist/assets/material-theme-palenight-BF01YeZy.js +1 -0
- package/dist/assets/material-theme-palenight-Csfq5Kiy.js +1 -0
- package/dist/assets/mathematica-DTrFuWx2.js +1 -0
- package/dist/assets/matlab-D7o27uSR.js +1 -0
- package/dist/assets/matlab-DzreyMvm.js +1 -0
- package/dist/assets/mbox-CNhZ1qSd.js +1 -0
- package/dist/assets/mdc-BMNejdWA.js +1 -0
- package/dist/assets/mdc-D_DpLax5.js +1 -0
- package/dist/assets/mdx-C2fqO2XY.js +1 -0
- package/dist/assets/mdx-Cmh6b_Ma.js +1 -0
- package/dist/assets/mermaid-Cwy4Up_p.js +1 -0
- package/dist/assets/mermaid-mWjccvbQ.js +1 -0
- package/dist/assets/min-dark-CafNBF8u.js +1 -0
- package/dist/assets/min-dark-DhfaepVE.js +1 -0
- package/dist/assets/min-light-6xx0uMgm.js +1 -0
- package/dist/assets/min-light-CTRr51gU.js +1 -0
- package/dist/assets/miniChat-BFotDgpO.js +2 -0
- package/dist/assets/mipsasm-CKIfxQSi.js +1 -0
- package/dist/assets/mipsasm-yE_Aqo9y.js +1 -0
- package/dist/assets/mirc-CjQqDB4T.js +1 -0
- package/dist/assets/mllike-BO34x3tv.js +1 -0
- package/dist/assets/mobile-3LLzhN5Q.js +2 -0
- package/dist/assets/modelica-Dc1JOy9r.js +1 -0
- package/dist/assets/mojo-M6pJPbMk.js +1 -0
- package/dist/assets/mojo-rZm6bMo-.js +1 -0
- package/dist/assets/monokai-BodnsaDQ.js +1 -0
- package/dist/assets/monokai-D4h5O-jR.js +1 -0
- package/dist/assets/moonbit-CR8uFLGd.js +1 -0
- package/dist/assets/moonbit-_H4v1dQx.js +1 -0
- package/dist/assets/move-CyYMWVy5.js +1 -0
- package/dist/assets/move-IF9eRakj.js +1 -0
- package/dist/assets/mscgen-BA5vi2Kp.js +1 -0
- package/dist/assets/mumps-BT43cFF4.js +1 -0
- package/dist/assets/narrat-DRg8JJMk.js +1 -0
- package/dist/assets/narrat-DXUZ_ZkW.js +1 -0
- package/dist/assets/nextflow-BPolriEg.js +1 -0
- package/dist/assets/nextflow-Zz6hmt5N.js +1 -0
- package/dist/assets/nextflow-groovy-BeH2EWoN.js +1 -0
- package/dist/assets/nextflow-groovy-pQRwk_Jg.js +1 -0
- package/dist/assets/nginx-BpAMiNFr.js +1 -0
- package/dist/assets/nginx-DSXkacNc.js +1 -0
- package/dist/assets/nginx-DdIZxoE0.js +1 -0
- package/dist/assets/night-owl-C39BiMTA.js +1 -0
- package/dist/assets/night-owl-EdP2Hztp.js +1 -0
- package/dist/assets/night-owl-light-Bk-lYVyk.js +1 -0
- package/dist/assets/night-owl-light-CMTm3GFP.js +1 -0
- package/dist/assets/nim-CVrawwO9.js +1 -0
- package/dist/assets/nim-e2z2kCcq.js +1 -0
- package/dist/assets/nix-Ckjrpg0r.js +1 -0
- package/dist/assets/nix-CwoSXNpI.js +1 -0
- package/dist/assets/nord-BAu0wM6c.js +1 -0
- package/dist/assets/nord-Ddv68eIx.js +1 -0
- package/dist/assets/nsis-LdVXkNf5.js +1 -0
- package/dist/assets/ntriples-BfvgReVJ.js +1 -0
- package/dist/assets/nushell-Cz2AlsmD.js +1 -0
- package/dist/assets/nushell-DtHLcZf1.js +1 -0
- package/dist/assets/objective-c-BoaBTdtz.js +1 -0
- package/dist/assets/objective-c-DXmwc3jG.js +1 -0
- package/dist/assets/objective-cpp-CLxacb5B.js +1 -0
- package/dist/assets/objective-cpp-DstR8hNy.js +1 -0
- package/dist/assets/ocaml-C0hk2d4L.js +1 -0
- package/dist/assets/ocaml-DTbn2Hv7.js +1 -0
- package/dist/assets/octave-CWZ4xMHY.js +1 -0
- package/dist/assets/odin-BBf5iR-q.js +1 -0
- package/dist/assets/odin-Bxc4S6Xn.js +1 -0
- package/dist/assets/one-dark-pro-DVMEJ2y_.js +1 -0
- package/dist/assets/one-dark-pro-utPxbBbJ.js +1 -0
- package/dist/assets/one-light-BuCQ22aO.js +1 -0
- package/dist/assets/one-light-C3Wv6jpd.js +1 -0
- package/dist/assets/openscad-C4EeE6gA.js +1 -0
- package/dist/assets/openscad-CXHpNBsb.js +1 -0
- package/dist/assets/oz-BzwKVEFT.js +1 -0
- package/dist/assets/pascal--L3eBynH.js +1 -0
- package/dist/assets/pascal-D93ZcfNL.js +1 -0
- package/dist/assets/pascal-Sr-XLSE6.js +1 -0
- package/dist/assets/perl-C0TMdlhV.js +1 -0
- package/dist/assets/perl-C95KruZl.js +1 -0
- package/dist/assets/perl-CdXCOZ3F.js +1 -0
- package/dist/assets/permissionStore-BRcQ9Jgt.js +1 -0
- package/dist/assets/php-B4Kqya17.js +1 -0
- package/dist/assets/php-Dhbhpdrm.js +1 -0
- package/dist/assets/pig-CevX1Tat.js +1 -0
- package/dist/assets/pkl-CRusufhw.js +1 -0
- package/dist/assets/pkl-u5AG7uiY.js +1 -0
- package/dist/assets/plastic-3e1v2bzS.js +1 -0
- package/dist/assets/plastic-CXnNNlFZ.js +1 -0
- package/dist/assets/plsql-ChMvpjG-.js +1 -0
- package/dist/assets/plsql-Dbm3tV2N.js +1 -0
- package/dist/assets/po-BTJTHyun.js +1 -0
- package/dist/assets/po-LG0lFwH4.js +1 -0
- package/dist/assets/poimandres-BrOVMvuE.js +1 -0
- package/dist/assets/poimandres-CS3Unz2-.js +1 -0
- package/dist/assets/polar-C0HS_06l.js +1 -0
- package/dist/assets/polar-DjOs3GWb.js +1 -0
- package/dist/assets/postcss-CXtECtnM.js +1 -0
- package/dist/assets/postcss-GRRkWn2F.js +1 -0
- package/dist/assets/powerquery-CEu0bR-o.js +1 -0
- package/dist/assets/powerquery-DHf-dtTS.js +1 -0
- package/dist/assets/powershell-CFHJl5sT.js +1 -0
- package/dist/assets/powershell-DXrKVo3G.js +1 -0
- package/dist/assets/powershell-Dpen1YoG.js +1 -0
- package/dist/assets/prisma-Dd19v3D-.js +1 -0
- package/dist/assets/prisma-Dgf8IN_C.js +1 -0
- package/dist/assets/prolog-9JcfiY6I.js +1 -0
- package/dist/assets/prolog-CbFg5uaA.js +1 -0
- package/dist/assets/properties-C78fOPTZ.js +1 -0
- package/dist/assets/proto-C7zT0LnQ.js +1 -0
- package/dist/assets/proto-dqx_Sj2i.js +1 -0
- package/dist/assets/protobuf-ChK-085T.js +1 -0
- package/dist/assets/pug-CGlum2m_.js +1 -0
- package/dist/assets/pug-CIGw5rlb.js +1 -0
- package/dist/assets/pug-CoOVtr4u.js +1 -0
- package/dist/assets/puppet-BMWR74SV.js +1 -0
- package/dist/assets/puppet-DMA9R1ak.js +1 -0
- package/dist/assets/puppet-Di57VbSS.js +1 -0
- package/dist/assets/purescript-BgpM7rDH.js +1 -0
- package/dist/assets/purescript-CklMAg4u.js +1 -0
- package/dist/assets/python-B6Ihcp-E.js +1 -0
- package/dist/assets/python-B6aJPvgy.js +1 -0
- package/dist/assets/python-Cso5vRIg.js +1 -0
- package/dist/assets/q-pXgVlZs6.js +1 -0
- package/dist/assets/qml-3beO22l8.js +1 -0
- package/dist/assets/qml-DmGQGbca.js +1 -0
- package/dist/assets/qmldir-C8lEn-DE.js +1 -0
- package/dist/assets/qmldir-DXd9s5XC.js +1 -0
- package/dist/assets/qss-B7NnhfIi.js +1 -0
- package/dist/assets/qss-IeuSbFQv.js +1 -0
- package/dist/assets/r-1ts4xG1r.js +1 -0
- package/dist/assets/r-B6wPVr8A.js +1 -0
- package/dist/assets/r-Dspwwk_N.js +1 -0
- package/dist/assets/racket-BqYA7rlc.js +1 -0
- package/dist/assets/racket-CeDx7KO2.js +1 -0
- package/dist/assets/raku-BMJwmrCS.js +1 -0
- package/dist/assets/raku-DXvB9xmW.js +1 -0
- package/dist/assets/razor-BaMsy-R7.js +1 -0
- package/dist/assets/razor-Uh8Bk_45.js +1 -0
- package/dist/assets/red-DR6ZwibK.js +1 -0
- package/dist/assets/red-bN70gL4F.js +1 -0
- package/dist/assets/reg-C-SQnVFl.js +1 -0
- package/dist/assets/reg-Cvyu2Gnx.js +1 -0
- package/dist/assets/regexp-CDVJQ6XC.js +1 -0
- package/dist/assets/regexp-DK-WlHNu.js +1 -0
- package/dist/assets/registerCustomTheme-BBXUCRnG.js +1 -0
- package/dist/assets/rel-BlqNHz8k.js +1 -0
- package/dist/assets/rel-C3B-1QV4.js +1 -0
- package/dist/assets/renderElectronMiniChatApp-DGmmT9Wf.js +1 -0
- package/dist/assets/renderMobileApp-Dd8P9hvM.js +23 -0
- package/dist/assets/riscv-BM1_JUlF.js +1 -0
- package/dist/assets/riscv-C5dpdlr7.js +1 -0
- package/dist/assets/ron-D8l8udqQ.js +1 -0
- package/dist/assets/ron-j5NfrB3z.js +1 -0
- package/dist/assets/rose-pine-CN2F2lJl.js +1 -0
- package/dist/assets/rose-pine-dawn-0S8NEhsA.js +1 -0
- package/dist/assets/rose-pine-dawn-DHQR4-dF.js +1 -0
- package/dist/assets/rose-pine-moon-D4_iv3hh.js +1 -0
- package/dist/assets/rose-pine-moon-DzBDT4rc.js +1 -0
- package/dist/assets/rose-pine-qdsjHGoJ.js +1 -0
- package/dist/assets/rosmsg-BJDFO7_C.js +1 -0
- package/dist/assets/rosmsg-ZkSIzPhc.js +1 -0
- package/dist/assets/rpm-CTu-6PCP.js +1 -0
- package/dist/assets/rst-BrH8l1NY.js +1 -0
- package/dist/assets/rst-C371KPjr.js +1 -0
- package/dist/assets/ruby-B2Rjki9n.js +1 -0
- package/dist/assets/ruby-DusoydGj.js +1 -0
- package/dist/assets/ruby-Dw2BHqvy.js +1 -0
- package/dist/assets/runtimeSurface-Cnqjhb6Y.js +1 -0
- package/dist/assets/rust-B1yitclQ.js +1 -0
- package/dist/assets/rust-CxuBE0J8.js +1 -0
- package/dist/assets/sas-B4kiWyti.js +1 -0
- package/dist/assets/sas-DtSHuIwI.js +1 -0
- package/dist/assets/sas-cz2c8ADy.js +1 -0
- package/dist/assets/sass-Cj5Yp3dK.js +1 -0
- package/dist/assets/sass-bfEVHPJ8.js +1 -0
- package/dist/assets/scala-C151Ov-r.js +1 -0
- package/dist/assets/scala-Dtw19ca_.js +1 -0
- package/dist/assets/scheme-C41bIUwD.js +1 -0
- package/dist/assets/scheme-C98Dy4si.js +1 -0
- package/dist/assets/scheme-OJppFQF3.js +1 -0
- package/dist/assets/scss-DxBkwadj.js +1 -0
- package/dist/assets/scss-OYdSNvt2.js +1 -0
- package/dist/assets/sdbl-CvZs3G9c.js +1 -0
- package/dist/assets/sdbl-DVxCFoDh.js +1 -0
- package/dist/assets/shaderlab-CEJedz68.js +1 -0
- package/dist/assets/shaderlab-Dg9Lc6iA.js +1 -0
- package/dist/assets/shellscript-DgJyDXW6.js +1 -0
- package/dist/assets/shellscript-Yzrsuije.js +1 -0
- package/dist/assets/shellsession-BADoaaVG.js +1 -0
- package/dist/assets/shellsession-DSlzxnCM.js +1 -0
- package/dist/assets/sieve-C3Gn_uJK.js +1 -0
- package/dist/assets/simple-mode-GW_nhZxv.js +1 -0
- package/dist/assets/slack-dark-BthQWCQV.js +1 -0
- package/dist/assets/slack-dark-CWRQmTmo.js +1 -0
- package/dist/assets/slack-ochin-DO_ok7XK.js +1 -0
- package/dist/assets/slack-ochin-DqwNpetd.js +1 -0
- package/dist/assets/smalltalk-B9IHbYt7.js +1 -0
- package/dist/assets/smalltalk-BERRCDM3.js +1 -0
- package/dist/assets/smalltalk-CnHTOXQT.js +1 -0
- package/dist/assets/snazzy-light-B5llO_gK.js +1 -0
- package/dist/assets/snazzy-light-Bw305WKR.js +1 -0
- package/dist/assets/solarized-dark-DXbdFlpD.js +1 -0
- package/dist/assets/solarized-dark-G9yBjKj_.js +1 -0
- package/dist/assets/solarized-light-DeSzljQz.js +1 -0
- package/dist/assets/solarized-light-L9t79GZl.js +1 -0
- package/dist/assets/solidity-B3CjIuAd.js +1 -0
- package/dist/assets/solidity-rGO070M0.js +1 -0
- package/dist/assets/solr-DehyRSwq.js +1 -0
- package/dist/assets/soy-BSb8SaP0.js +1 -0
- package/dist/assets/soy-Brmx7dQM.js +1 -0
- package/dist/assets/sparql-BJh4JEw-.js +1 -0
- package/dist/assets/sparql-DkYu6x3z.js +1 -0
- package/dist/assets/sparql-rVzFXLq3.js +1 -0
- package/dist/assets/splunk-BtCnVYZw.js +1 -0
- package/dist/assets/splunk-h-bNAnbk.js +1 -0
- package/dist/assets/spreadsheet-BCZA_wO0.js +1 -0
- package/dist/assets/sql-BLtJtn59.js +1 -0
- package/dist/assets/sql-BbONfI8K.js +1 -0
- package/dist/assets/sql-D0XecflT.js +1 -0
- package/dist/assets/ssh-config-CywOjoXH.js +1 -0
- package/dist/assets/ssh-config-_ykCGR6B.js +1 -0
- package/dist/assets/stata-BH5u7GGu.js +1 -0
- package/dist/assets/stata-BKjsT8qe.js +1 -0
- package/dist/assets/stex-C3f8Ysf7.js +1 -0
- package/dist/assets/stylus-B533Al4x.js +1 -0
- package/dist/assets/stylus-BEDo0Tqx.js +1 -0
- package/dist/assets/stylus-CIg617SB.js +1 -0
- package/dist/assets/surrealql-2KNEnAt7.js +1 -0
- package/dist/assets/surrealql-Bq5Q-fJD.js +1 -0
- package/dist/assets/svelte-C_ipcX3V.js +1 -0
- package/dist/assets/svelte-xaxjcSSP.js +1 -0
- package/dist/assets/swift-BzpIVaGY.js +1 -0
- package/dist/assets/swift-D82vCrfD.js +1 -0
- package/dist/assets/swift-tEesUtni.js +1 -0
- package/dist/assets/synthwave-84-CbfX1IO0.js +1 -0
- package/dist/assets/synthwave-84-DRVv-_m_.js +1 -0
- package/dist/assets/system-verilog-CnnmHF94.js +1 -0
- package/dist/assets/system-verilog-Qdm_1g2q.js +1 -0
- package/dist/assets/systemd-4A_iFExJ.js +1 -0
- package/dist/assets/systemd-DKMPPZjB.js +1 -0
- package/dist/assets/talonscript-CkByrt1z.js +1 -0
- package/dist/assets/talonscript-eRuyeYHl.js +1 -0
- package/dist/assets/tasl-B9CVvjfG.js +1 -0
- package/dist/assets/tasl-QIJgUcNo.js +1 -0
- package/dist/assets/tcl-Bq9dbVAF.js +1 -0
- package/dist/assets/tcl-DVfN8rqt.js +1 -0
- package/dist/assets/tcl-dwOrl1Do.js +1 -0
- package/dist/assets/templ-BVTOctZ7.js +1 -0
- package/dist/assets/templ-P3uqSqPl.js +1 -0
- package/dist/assets/terraform-BETggiCN.js +1 -0
- package/dist/assets/terraform-CAp711iT.js +1 -0
- package/dist/assets/tex-BHxF5DMe.js +1 -0
- package/dist/assets/tex-idrVyKtj.js +1 -0
- package/dist/assets/textile-CnDTJFAw.js +1 -0
- package/dist/assets/themeResolver-BmJyTmjJ.js +1 -0
- package/dist/assets/tiddlywiki-DO-Gjzrf.js +1 -0
- package/dist/assets/tiki-DGYXhP31.js +1 -0
- package/dist/assets/tokyo-night-BvwC8l2v.js +1 -0
- package/dist/assets/tokyo-night-hegEt444.js +1 -0
- package/dist/assets/toml-Bm5Em-hy.js +1 -0
- package/dist/assets/toml-Ms459DBw.js +1 -0
- package/dist/assets/toml-vGWfd6FD.js +1 -0
- package/dist/assets/troff-wAsdV37c.js +1 -0
- package/dist/assets/ts-tags-C-gylFal.js +1 -0
- package/dist/assets/ts-tags-zn1MmPIZ.js +1 -0
- package/dist/assets/tsv-BFtm7NXx.js +1 -0
- package/dist/assets/tsv-B_m7g4N7.js +1 -0
- package/dist/assets/tsx-COt5Ahok.js +1 -0
- package/dist/assets/tsx-D6116Ycy.js +1 -0
- package/dist/assets/ttcn-CfJYG6tj.js +1 -0
- package/dist/assets/ttcn-cfg-B9xdYoR4.js +1 -0
- package/dist/assets/turtle-B1tBg_DP.js +1 -0
- package/dist/assets/turtle-BsS91CYL.js +1 -0
- package/dist/assets/turtle-DpRhbQZY.js +1 -0
- package/dist/assets/twig-DNn4PbVi.js +1 -0
- package/dist/assets/twig-DuP5mDZ4.js +1 -0
- package/dist/assets/typescript-BPQ3VLAy.js +1 -0
- package/dist/assets/typescript-BqvgUolh.js +1 -0
- package/dist/assets/typespec-BGHnOYBU.js +1 -0
- package/dist/assets/typespec-DhK7u29_.js +1 -0
- package/dist/assets/typst-DHCkPAjA.js +1 -0
- package/dist/assets/typst-Dfd71gQj.js +1 -0
- package/dist/assets/useAppFontEffects-DXASDavv.js +7043 -0
- package/dist/assets/useInlineCommentController-KJFe98HG.js +2 -0
- package/dist/assets/useOpenInAppsStore-DHAeT9zx.js +1 -0
- package/dist/assets/useWindowTitle-BcwmWC0M.js +10 -0
- package/dist/assets/v-BH76v7br.js +1 -0
- package/dist/assets/v-BcVCzyr7.js +1 -0
- package/dist/assets/vala-CsfeWuGM.js +1 -0
- package/dist/assets/vala-z59IxkFd.js +1 -0
- package/dist/assets/vb-4AxSLKIU.js +1 -0
- package/dist/assets/vb-CmGdzxic.js +1 -0
- package/dist/assets/vb-D17OF-Vu.js +1 -0
- package/dist/assets/vbscript-BuJXcnF6.js +1 -0
- package/dist/assets/velocity-D8B20fx6.js +1 -0
- package/dist/assets/vendor-aparajita-capacitor-secure-storage-5IShChL0.js +1 -0
- package/dist/assets/vendor-aria-hidden-DvXkyWUv.js +1 -0
- package/dist/assets/vendor-base-ui-DwfTg5wD.js +1 -0
- package/dist/assets/vendor-beautiful-mermaid-r4GK_usS.js +119 -0
- package/dist/assets/vendor-capacitor-app-BrDJj9I_.js +1 -0
- package/dist/assets/vendor-capacitor-core-B4EWRrHz.js +1 -0
- package/dist/assets/vendor-capacitor-keyboard-CYccoJjr.js +1 -0
- package/dist/assets/vendor-capacitor-push-notifications-KNe1zSBF.js +1 -0
- package/dist/assets/vendor-capacitor-status-bar-B-N0EOVk.js +1 -0
- package/dist/assets/vendor-ccount-c2V3InAJ.js +1 -0
- package/dist/assets/vendor-character-entities-html4-D7taRCQ7.js +1 -0
- package/dist/assets/vendor-character-entities-legacy-BpoLgcEt.js +1 -0
- package/dist/assets/vendor-class-variance-authority-5VPnzWs2.js +1 -0
- package/dist/assets/vendor-clsx-B-dksMZM.js +1 -0
- package/dist/assets/vendor-cmdk-BCEUzcHE.js +1 -0
- package/dist/assets/vendor-codemirror-autocomplete-3xMyV7a2.js +1 -0
- package/dist/assets/vendor-codemirror-commands-Bzf12ODH.js +1 -0
- package/dist/assets/vendor-codemirror-lang-angular-0K6s6cvb.js +1 -0
- package/dist/assets/vendor-codemirror-lang-cpp-B0aL03z-.js +1 -0
- package/dist/assets/vendor-codemirror-lang-css-C4jEScp7.js +1 -0
- package/dist/assets/vendor-codemirror-lang-go-CgrfVBPr.js +7 -0
- package/dist/assets/vendor-codemirror-lang-html-C1Svvdey.js +1 -0
- package/dist/assets/vendor-codemirror-lang-java-DNPz7_Bx.js +1 -0
- package/dist/assets/vendor-codemirror-lang-javascript-CrbeaEAU.js +13 -0
- package/dist/assets/vendor-codemirror-lang-jinja-DL8BR_RG.js +1 -0
- package/dist/assets/vendor-codemirror-lang-json-BTvAjwkJ.js +1 -0
- package/dist/assets/vendor-codemirror-lang-less-CPABt6NZ.js +1 -0
- package/dist/assets/vendor-codemirror-lang-liquid-BLOLOBMg.js +1 -0
- package/dist/assets/vendor-codemirror-lang-markdown-BloD9GN-.js +1 -0
- package/dist/assets/vendor-codemirror-lang-php-FIZMTuWM.js +1 -0
- package/dist/assets/vendor-codemirror-lang-python-CR2g6ukK.js +3 -0
- package/dist/assets/vendor-codemirror-lang-rust-lsPqn4k4.js +1 -0
- package/dist/assets/vendor-codemirror-lang-sass-CtECMwzH.js +1 -0
- package/dist/assets/vendor-codemirror-lang-sql-BtmZrrZH.js +2 -0
- package/dist/assets/vendor-codemirror-lang-vue-Br6DrntY.js +1 -0
- package/dist/assets/vendor-codemirror-lang-wast-DT7IlCnb.js +1 -0
- package/dist/assets/vendor-codemirror-lang-xml-wNr-LZOf.js +1 -0
- package/dist/assets/vendor-codemirror-lang-yaml-BTKFwfc2.js +1 -0
- package/dist/assets/vendor-codemirror-language-BuDklWi8.js +3 -0
- package/dist/assets/vendor-codemirror-language-data-CFVH0pe8.js +2 -0
- package/dist/assets/vendor-codemirror-search-BzyfPoRb.js +2 -0
- package/dist/assets/vendor-codemirror-state-Wdz6Qsow.js +5 -0
- package/dist/assets/vendor-codemirror-view-pXtfpqbT.js +4 -0
- package/dist/assets/vendor-comma-separated-tokens-D8TqSuIK.js +1 -0
- package/dist/assets/vendor-crelt-C8TCjufn.js +1 -0
- package/dist/assets/vendor-detect-node-es-l0sNRNKZ.js +1 -0
- package/dist/assets/vendor-diff-C3JhlQdm.js +13 -0
- package/dist/assets/vendor-dijkstrajs-COg3n3zL.js +1 -0
- package/dist/assets/vendor-dnd-kit-accessibility-Cv52zwWT.js +1 -0
- package/dist/assets/vendor-dnd-kit-core-GipZW2tY.js +5 -0
- package/dist/assets/vendor-dnd-kit-sortable-Ct2QxIHb.js +1 -0
- package/dist/assets/vendor-dnd-kit-utilities-CrOcp0Bp.js +1 -0
- package/dist/assets/vendor-dompurify-r8glNMJW.js +2 -0
- package/dist/assets/vendor-elkjs-CRCLNXjf.js +24 -0
- package/dist/assets/vendor-entities-C3BA0AJP.js +1 -0
- package/dist/assets/vendor-fflate-C7GOKjkv.js +1 -0
- package/dist/assets/vendor-floating-ui-core-CXmKXOXc.js +1 -0
- package/dist/assets/vendor-floating-ui-dom-Bkw82kL9.js +1 -0
- package/dist/assets/vendor-floating-ui-react-dom-sVFnuruh.js +1 -0
- package/dist/assets/vendor-floating-ui-utils-BzXdqYpP.js +1 -0
- package/dist/assets/vendor-framer-motion-DJASDzNt.js +1 -0
- package/dist/assets/vendor-fuse.js-Dw8P0qyA.js +1 -0
- package/dist/assets/vendor-get-nonce-C-Z93AgS.js +1 -0
- package/dist/assets/vendor-ghostty-web-CrxinGGH.js +13 -0
- package/dist/assets/vendor-hast-util-to-html-BKBhbTDb.js +9 -0
- package/dist/assets/vendor-hast-util-whitespace-D4Wp6AEg.js +1 -0
- package/dist/assets/vendor-heic2any-L8hyfc24.js +66 -0
- package/dist/assets/vendor-html-to-image-BeoRn2gJ.js +2 -0
- package/dist/assets/vendor-html-void-elements-CkdplsNl.js +1 -0
- package/dist/assets/vendor-katex-CHD9BdDO.js +257 -0
- package/dist/assets/vendor-katex-Cp579UoJ.css +1 -0
- package/dist/assets/vendor-lezer-common-B3-cUCR_.js +1 -0
- package/dist/assets/vendor-lezer-cpp-BleAU9o-.js +1 -0
- package/dist/assets/vendor-lezer-css-C5xB6jxd.js +1 -0
- package/dist/assets/vendor-lezer-go-CXK1UKk8.js +1 -0
- package/dist/assets/vendor-lezer-highlight-CDMHUZ74.js +1 -0
- package/dist/assets/vendor-lezer-html-C0I1p52t.js +1 -0
- package/dist/assets/vendor-lezer-java-D_zzj-NV.js +1 -0
- package/dist/assets/vendor-lezer-javascript-D7Tj24ot.js +1 -0
- package/dist/assets/vendor-lezer-json-Iaex0UN-.js +1 -0
- package/dist/assets/vendor-lezer-lr-bhwg1HMg.js +1 -0
- package/dist/assets/vendor-lezer-markdown-C6g-d6P3.js +6 -0
- package/dist/assets/vendor-lezer-php-_DHrbsZG.js +1 -0
- package/dist/assets/vendor-lezer-python-CvuFxeZl.js +1 -0
- package/dist/assets/vendor-lezer-rust-Lqgd5SAo.js +1 -0
- package/dist/assets/vendor-lezer-sass-Dnm2lY4t.js +1 -0
- package/dist/assets/vendor-lezer-xml-hUDuAyS5.js +1 -0
- package/dist/assets/vendor-lezer-yaml-BNIl6S_O.js +1 -0
- package/dist/assets/vendor-lru_map-B-cPXHXW.js +1 -0
- package/dist/assets/vendor-marijn-find-cluster-break-DXwl3gUT.js +1 -0
- package/dist/assets/vendor-marked-i_gIkIP3.js +59 -0
- package/dist/assets/vendor-morphdom-B7dS_VPB.js +1 -0
- package/dist/assets/vendor-motion-dom-DJLgZh4Q.js +1 -0
- package/dist/assets/vendor-motion-l0sNRNKZ.js +1 -0
- package/dist/assets/vendor-motion-utils-BGh8QBgx.js +1 -0
- package/dist/assets/vendor-oniguruma-parser-C0D4_9rT.js +33 -0
- package/dist/assets/vendor-oniguruma-to-es-DLBnFDCE.js +94 -0
- package/dist/assets/vendor-pierre-theme-Bp7SdUL6.js +1 -0
- package/dist/assets/vendor-pierre-theming-C6Z4M5_u.js +1 -0
- package/dist/assets/vendor-property-information-rJCbAzvX.js +1 -0
- package/dist/assets/vendor-qrcode-COsmt6jn.js +8 -0
- package/dist/assets/vendor-radix-ui-primitive-Dc_FVRD7.js +1 -0
- package/dist/assets/vendor-radix-ui-react-compose-refs-D1GnwnP9.js +1 -0
- package/dist/assets/vendor-radix-ui-react-context-CM4yFQ2I.js +1 -0
- package/dist/assets/vendor-radix-ui-react-dialog-jX3Dld8c.js +5 -0
- package/dist/assets/vendor-radix-ui-react-dismissable-layer-M7LsH3yL.js +1 -0
- package/dist/assets/vendor-radix-ui-react-focus-guards-D5MZ0nQs.js +1 -0
- package/dist/assets/vendor-radix-ui-react-focus-scope-C2K7lNPv.js +1 -0
- package/dist/assets/vendor-radix-ui-react-id-BcxRjnbX.js +1 -0
- package/dist/assets/vendor-radix-ui-react-portal-xlJWBfDg.js +1 -0
- package/dist/assets/vendor-radix-ui-react-presence-Btn-Dp7L.js +1 -0
- package/dist/assets/vendor-radix-ui-react-primitive-CfpW6_IT.js +1 -0
- package/dist/assets/vendor-radix-ui-react-slot-BxUc3Pvz.js +1 -0
- package/dist/assets/vendor-radix-ui-react-use-callback-ref-UKEWHtoP.js +1 -0
- package/dist/assets/vendor-radix-ui-react-use-controllable-state-C_ecoh35.js +1 -0
- package/dist/assets/vendor-radix-ui-react-use-effect-event-DkJl_Rn_.js +1 -0
- package/dist/assets/vendor-radix-ui-react-use-escape-keydown-Co13lHGj.js +1 -0
- package/dist/assets/vendor-radix-ui-react-use-layout-effect-0fyGwS2_.js +1 -0
- package/dist/assets/vendor-react-DYyjh7Q0.js +9 -0
- package/dist/assets/vendor-react-drawio-Boqanm0j.js +1 -0
- package/dist/assets/vendor-react-remove-scroll-CfeMe0DL.js +4 -0
- package/dist/assets/vendor-react-remove-scroll-bar-C9YRs2nU.js +38 -0
- package/dist/assets/vendor-react-style-singleton-B1H4bWSG.js +1 -0
- package/dist/assets/vendor-regex-BBWcyTkg.js +16 -0
- package/dist/assets/vendor-regex-recursion-JU4mF-dD.js +1 -0
- package/dist/assets/vendor-regex-utilities-2BQyy0xI.js +1 -0
- package/dist/assets/vendor-remend-BWYYytN3.js +22 -0
- package/dist/assets/vendor-remixicon-react-CzPq62YO.js +1 -0
- package/dist/assets/vendor-replit-codemirror-vim-BUqe2Slv.js +39 -0
- package/dist/assets/vendor-reselect-l0sNRNKZ.js +1 -0
- package/dist/assets/vendor-scheduler-7OC5HNn7.js +1 -0
- package/dist/assets/vendor-shiki-Bbf2GEcE.js +2 -0
- package/dist/assets/vendor-shikijs-core-CGM_jqPK.js +2 -0
- package/dist/assets/vendor-shikijs-engine-javascript-Cme4830A.js +1 -0
- package/dist/assets/vendor-shikijs-engine-oniguruma-DPWuIF1G.js +1 -0
- package/dist/assets/vendor-shikijs-transformers-CZWsLLKm.js +1 -0
- package/dist/assets/vendor-shikijs-types-CPSbSeK6.js +1 -0
- package/dist/assets/vendor-shikijs-vscode-textmate-BgLrS_2D.js +3 -0
- package/dist/assets/vendor-simplewebauthn-browser-VDA66rT_.js +2 -0
- package/dist/assets/vendor-sonner-dWA7qvjr.js +1 -0
- package/dist/assets/vendor-space-separated-tokens-DD3iYX1K.js +1 -0
- package/dist/assets/vendor-stringify-entities-CsH1VEn9.js +1 -0
- package/dist/assets/vendor-style-mod-NZ5MEPvj.js +3 -0
- package/dist/assets/vendor-tailwind-merge-CPcsbTWB.js +1 -0
- package/dist/assets/vendor-tanstack-react-virtual-IrbCBGJ4.js +1 -0
- package/dist/assets/vendor-tanstack-virtual-core-Dubg10g5.js +1 -0
- package/dist/assets/vendor-tslib-De9GV7Vy.js +1 -0
- package/dist/assets/vendor-use-callback-ref-CJB0R85I.js +1 -0
- package/dist/assets/vendor-use-sidecar-C-AuTczP.js +1 -0
- package/dist/assets/vendor-use-sync-external-store-B7OScDIw.js +1 -0
- package/dist/assets/vendor-vite-runtime-DD1OZmLC.js +1 -0
- package/dist/assets/vendor-w3c-keyname-Vcq4gwWv.js +1 -0
- package/dist/assets/vendor-workbox-window-BIl4cyR9.js +2 -0
- package/dist/assets/vendor-zumer-snapdom-Btyu_Xi2.js +8 -0
- package/dist/assets/vendor-zustand-CnsBKjP5.js +6 -0
- package/dist/assets/vendor-zwitch-C2o2j-tx.js +1 -0
- package/dist/assets/verilog-BQ8w6xss.js +1 -0
- package/dist/assets/verilog-C6RDOZhf.js +1 -0
- package/dist/assets/verilog-LfQUkHDc.js +1 -0
- package/dist/assets/vesper--t9w6MWz.js +1 -0
- package/dist/assets/vesper-DU1UobuO.js +1 -0
- package/dist/assets/vhdl-CeAyd5Ju.js +1 -0
- package/dist/assets/vhdl-DJCsFYgn.js +1 -0
- package/dist/assets/vhdl-lSbBsy5d.js +1 -0
- package/dist/assets/viml-CJc9bBzg.js +1 -0
- package/dist/assets/viml-pbKHW3Ia.js +1 -0
- package/dist/assets/vitesse-black-B_BYPdvp.js +1 -0
- package/dist/assets/vitesse-black-Bkuqu6BP.js +1 -0
- package/dist/assets/vitesse-dark-D0r3Knsf.js +1 -0
- package/dist/assets/vitesse-dark-DOSOf2Sz.js +1 -0
- package/dist/assets/vitesse-light-CVO1_9PV.js +1 -0
- package/dist/assets/vitesse-light-gUX51GpB.js +1 -0
- package/dist/assets/vue-D2k0BAl5.js +1 -0
- package/dist/assets/vue-DN_0RTcg.js +1 -0
- package/dist/assets/vue-html-AaS7Mt5G.js +1 -0
- package/dist/assets/vue-html-zEfNEgpj.js +1 -0
- package/dist/assets/vue-vine-CQOfvN7w.js +1 -0
- package/dist/assets/vue-vine-D9eCaTqi.js +1 -0
- package/dist/assets/vyper-CDx5xZoG.js +1 -0
- package/dist/assets/vyper-DpazWNpx.js +1 -0
- package/dist/assets/wasm-CFi-k9--.js +1 -0
- package/dist/assets/wasm-CG6Dc4jp.js +1 -0
- package/dist/assets/wasm-MzD3tlZU.js +1 -0
- package/dist/assets/web-components-CNPQSzpa.js +1390 -0
- package/dist/assets/webidl-ZXfAyPTL.js +1 -0
- package/dist/assets/wenyan-BV7otONQ.js +1 -0
- package/dist/assets/wenyan-CYFFG9DK.js +1 -0
- package/dist/assets/wgsl-CxjTZs-b.js +1 -0
- package/dist/assets/wgsl-Dx-B1_4e.js +1 -0
- package/dist/assets/wikitext-BhOHFoWU.js +1 -0
- package/dist/assets/wikitext-Bx2qct8O.js +1 -0
- package/dist/assets/wit-5i3qLPDT.js +1 -0
- package/dist/assets/wit-DDW3hD69.js +1 -0
- package/dist/assets/wolfram-lXgVvXCa.js +1 -0
- package/dist/assets/wolfram-pu21K997.js +1 -0
- package/dist/assets/worker-BBk7yuNN.js +153 -0
- package/dist/assets/workerFactory-tw7cIdmV.js +1 -0
- package/dist/assets/xml-BkAJuBCc.js +1 -0
- package/dist/assets/xml-sdJ4AIDG.js +1 -0
- package/dist/assets/xquery-DzFWVndE.js +1 -0
- package/dist/assets/xsl-C6UUWSil.js +1 -0
- package/dist/assets/xsl-CtQFsRM5.js +1 -0
- package/dist/assets/yacas-BJ4BC0dw.js +1 -0
- package/dist/assets/yaml-Bopit2L3.js +1 -0
- package/dist/assets/yaml-Buea-lGh.js +1 -0
- package/dist/assets/z80-Hz9HOZM7.js +1 -0
- package/dist/assets/zenscript-BIAKq-aR.js +1 -0
- package/dist/assets/zenscript-DVFEvuxE.js +1 -0
- package/dist/assets/zig-VOosw3JB.js +1 -0
- package/dist/assets/zig-bSFrHQ7n.js +1 -0
- package/dist/favicon-16.png +0 -0
- package/dist/favicon-32.png +0 -0
- package/dist/favicon.png +0 -0
- package/dist/favicon.svg +13 -0
- package/dist/index.html +658 -0
- package/dist/logo-dark-192x192.png +0 -0
- package/dist/logo-dark-512x512.svg +13 -0
- package/dist/logo-light-192x192.png +0 -0
- package/dist/logo-light-512x512.svg +13 -0
- package/dist/mini-chat.html +121 -0
- package/dist/mobile.html +79 -0
- package/dist/pwa-192.png +0 -0
- package/dist/pwa-512.png +0 -0
- package/dist/pwa-maskable-192.png +0 -0
- package/dist/pwa-maskable-512.png +0 -0
- package/dist/site.webmanifest +21 -0
- package/dist/sw.js +1 -0
- package/package.json +115 -0
- package/public/apple-touch-icon-120x120.png +0 -0
- package/public/apple-touch-icon-152x152.png +0 -0
- package/public/apple-touch-icon-167x167.png +0 -0
- package/public/apple-touch-icon-180x180.png +0 -0
- package/public/apple-touch-icon.png +0 -0
- package/public/apple-touch-icon.svg +13 -0
- package/public/favicon-16.png +0 -0
- package/public/favicon-32.png +0 -0
- package/public/favicon.png +0 -0
- package/public/favicon.svg +13 -0
- package/public/logo-dark-192x192.png +0 -0
- package/public/logo-dark-512x512.svg +13 -0
- package/public/logo-light-192x192.png +0 -0
- package/public/logo-light-512x512.svg +13 -0
- package/public/pwa-192.png +0 -0
- package/public/pwa-512.png +0 -0
- package/public/pwa-maskable-192.png +0 -0
- package/public/pwa-maskable-512.png +0 -0
- package/public/site.webmanifest +21 -0
- package/server/index.d.ts +35 -0
- package/server/index.js +206 -0
- package/server/lib/client-auth/pairing.js +308 -0
- package/server/lib/client-auth/pairing.test.js +142 -0
- package/server/lib/client-auth/remote-clients.js +280 -0
- package/server/lib/client-auth/remote-clients.test.js +158 -0
- package/server/lib/cloudflare-tunnel.js +606 -0
- package/server/lib/fs/DOCUMENTATION.md +39 -0
- package/server/lib/fs/pick-directory.js +236 -0
- package/server/lib/fs/pick-directory.test.js +134 -0
- package/server/lib/fs/routes.js +1543 -0
- package/server/lib/fs/routes.test.js +920 -0
- package/server/lib/fs/search.js +238 -0
- package/server/lib/git/DOCUMENTATION.md +180 -0
- package/server/lib/git/credentials.js +41 -0
- package/server/lib/git/identity-storage.js +113 -0
- package/server/lib/git/index.js +6 -0
- package/server/lib/git/issue-2746-longpaths.test.js +206 -0
- package/server/lib/git/routes.js +1089 -0
- package/server/lib/git/routes.test.js +210 -0
- package/server/lib/git/service.js +4927 -0
- package/server/lib/git/service.test.js +1216 -0
- package/server/lib/inherited-env.js +74 -0
- package/server/lib/inherited-env.test.js +65 -0
- package/server/lib/ngrok-tunnel.js +343 -0
- package/server/lib/ngrok-tunnel.test.js +50 -0
- package/server/lib/package-manager.js +850 -0
- package/server/lib/package-manager.test.js +370 -0
- package/server/lib/path-realpath-cache.js +85 -0
- package/server/lib/path-realpath-cache.test.js +75 -0
- package/server/lib/pi/archive-store.js +52 -0
- package/server/lib/pi/archive-store.test.js +32 -0
- package/server/lib/pi/attachment-store.js +98 -0
- package/server/lib/pi/attachment-store.test.js +40 -0
- package/server/lib/pi/custom-themes.js +33 -0
- package/server/lib/pi/custom-themes.test.js +21 -0
- package/server/lib/pi/legacy-ui-model-defaults.js +57 -0
- package/server/lib/pi/legacy-ui-model-defaults.test.js +65 -0
- package/server/lib/pi/model-config-store.js +173 -0
- package/server/lib/pi/model-config-store.test.js +42 -0
- package/server/lib/pi/routes.js +956 -0
- package/server/lib/pi/routes.test.js +733 -0
- package/server/lib/pi/session-daemon/DOCUMENTATION.md +41 -0
- package/server/lib/pi/session-daemon/daemon-process.js +113 -0
- package/server/lib/pi/session-daemon/ipc-client.js +144 -0
- package/server/lib/pi/session-daemon/runtime-registry.js +149 -0
- package/server/lib/pi/session-daemon/runtime-registry.test.js +109 -0
- package/server/lib/pi/session-daemon/session-daemon.js +1903 -0
- package/server/lib/pi/session-daemon/session-daemon.test.js +1306 -0
- package/server/lib/pi/session-daemon/session-jsonl.js +94 -0
- package/server/lib/pi/session-daemon/session-jsonl.test.js +73 -0
- package/server/lib/pi/session-daemon/supervisor.js +458 -0
- package/server/lib/pi/session-daemon/supervisor.test.js +212 -0
- package/server/lib/pi/session-folders-store.js +74 -0
- package/server/lib/pi/session-folders-store.test.js +45 -0
- package/server/lib/pi/session-lifecycle.integration.test.js +197 -0
- package/server/lib/pi/settings-store.js +133 -0
- package/server/lib/pi/settings-store.test.js +113 -0
- package/server/lib/pi/thinking-levels.js +39 -0
- package/server/lib/pi/thinking-levels.test.js +34 -0
- package/server/lib/pi/ui-settings-store.js +68 -0
- package/server/lib/pi/ui-settings-store.test.js +40 -0
- package/server/lib/pichamber-data-dir.js +78 -0
- package/server/lib/pichamber-data-dir.test.js +105 -0
- package/server/lib/pichamber-data-root-authority.test.js +81 -0
- package/server/lib/preview/proxy-runtime.js +1604 -0
- package/server/lib/preview/proxy-runtime.test.js +528 -0
- package/server/lib/pwa-keys-contract.test.ts +62 -0
- package/server/lib/relay/DOCUMENTATION.md +106 -0
- package/server/lib/relay/cross-compat.test.js +135 -0
- package/server/lib/relay/e2ee.js +341 -0
- package/server/lib/relay/e2ee.test.js +124 -0
- package/server/lib/relay/host-client.js +389 -0
- package/server/lib/relay/host-client.test.js +292 -0
- package/server/lib/relay/identity.js +93 -0
- package/server/lib/relay/identity.test.js +78 -0
- package/server/lib/relay/service.js +9 -0
- package/server/lib/relay/signing-key.js +73 -0
- package/server/lib/relay/tunnel-codec.js +373 -0
- package/server/lib/relay/tunnel-codec.test.js +58 -0
- package/server/lib/relay/tunnel-host.js +465 -0
- package/server/lib/security/bind-host.js +40 -0
- package/server/lib/security/bind-host.test.js +36 -0
- package/server/lib/security/packaged-client-origins.js +11 -0
- package/server/lib/security/request-security.js +122 -0
- package/server/lib/security/request-security.test.js +57 -0
- package/server/lib/server/cli-entry-runtime.js +44 -0
- package/server/lib/server/cli-options.js +132 -0
- package/server/lib/server/core-routes.js +1041 -0
- package/server/lib/server/cors.js +51 -0
- package/server/lib/server/cors.test.js +37 -0
- package/server/lib/server/lan-addresses.js +41 -0
- package/server/lib/server/lan-addresses.test.js +42 -0
- package/server/lib/server/path-utils.js +27 -0
- package/server/lib/server/tunnel-auth.js +586 -0
- package/server/lib/terminal/DOCUMENTATION.md +48 -0
- package/server/lib/terminal/history.js +83 -0
- package/server/lib/terminal/history.test.js +19 -0
- package/server/lib/terminal/runtime.js +391 -0
- package/server/lib/terminal/runtime.test.js +566 -0
- package/server/lib/terminal/shells.js +100 -0
- package/server/lib/terminal/shells.test.js +73 -0
- package/server/lib/terminal/terminal-ws-protocol.js +68 -0
- package/server/lib/terminal/terminal-ws-protocol.test.js +145 -0
- package/server/lib/terminal/theme-response.js +98 -0
- package/server/lib/terminal/theme-response.test.js +70 -0
- package/server/lib/tunnels/DOCUMENTATION.md +33 -0
- package/server/lib/tunnels/executable-search.js +140 -0
- package/server/lib/tunnels/executable-search.test.js +96 -0
- package/server/lib/tunnels/install-help.js +56 -0
- package/server/lib/tunnels/install-help.test.js +31 -0
- package/server/lib/tunnels/providers/cloudflare.js +54 -0
- package/server/lib/tunnels/providers/ngrok.js +32 -0
- package/server/lib/tunnels/types.js +16 -0
- package/server/lib/ui-auth/DOCUMENTATION.md +44 -0
- package/server/lib/ui-auth/ui-auth.js +972 -0
- package/server/lib/ui-auth/ui-auth.test.js +250 -0
- package/server/lib/ui-auth/ui-passkeys.js +543 -0
- package/server/lib/workspace/host.js +91 -0
|
@@ -0,0 +1,1543 @@
|
|
|
1
|
+
import { createRealpathCache } from '../path-realpath-cache.js';
|
|
2
|
+
import nodeFsPromises from 'node:fs/promises';
|
|
3
|
+
import nodePath from 'node:path';
|
|
4
|
+
import { resolvePiChamberDataDir } from '../pichamber-data-dir.js';
|
|
5
|
+
import { createFsSearchRuntime } from './search.js';
|
|
6
|
+
import { pickHostDirectory } from './pick-directory.js';
|
|
7
|
+
|
|
8
|
+
const EXEC_JOB_TTL_MS = 30 * 60 * 1000;
|
|
9
|
+
const OUTSIDE_FILE_GRANT_TTL_MS = 10 * 60 * 1000;
|
|
10
|
+
|
|
11
|
+
const outsideFileGrants = new Map();
|
|
12
|
+
|
|
13
|
+
const pruneOutsideFileGrants = () => {
|
|
14
|
+
const now = Date.now();
|
|
15
|
+
for (const [token, grant] of outsideFileGrants.entries()) {
|
|
16
|
+
if (!grant || grant.expiresAt <= now) {
|
|
17
|
+
outsideFileGrants.delete(token);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const isOsPermissionError = (error) => (
|
|
23
|
+
error
|
|
24
|
+
&& typeof error === 'object'
|
|
25
|
+
&& (error.code === 'EACCES' || error.code === 'EPERM')
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
const sendOsPermissionDenied = (res, message) => (
|
|
29
|
+
res.status(403).json({ error: message, reason: 'os-permission' })
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
export const mintOutsideFileGrant = async (targetPath, {
|
|
33
|
+
scopes = ['stat', 'read', 'raw'],
|
|
34
|
+
fsPromises = nodeFsPromises,
|
|
35
|
+
path = nodePath,
|
|
36
|
+
crypto = globalThis.crypto,
|
|
37
|
+
} = {}) => {
|
|
38
|
+
const raw = typeof targetPath === 'string' ? targetPath.trim() : '';
|
|
39
|
+
if (!raw) {
|
|
40
|
+
throw new Error('Path is required');
|
|
41
|
+
}
|
|
42
|
+
const canonicalPath = await fsPromises.realpath(raw);
|
|
43
|
+
const stats = await fsPromises.stat(canonicalPath);
|
|
44
|
+
if (!stats.isFile()) {
|
|
45
|
+
throw new Error('Outside file grants require a file path');
|
|
46
|
+
}
|
|
47
|
+
pruneOutsideFileGrants();
|
|
48
|
+
const token = typeof crypto?.randomUUID === 'function'
|
|
49
|
+
? crypto.randomUUID()
|
|
50
|
+
: `${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
51
|
+
const normalizedScopes = new Set(
|
|
52
|
+
(Array.isArray(scopes) ? scopes : [])
|
|
53
|
+
.filter((scope) => typeof scope === 'string' && scope.trim())
|
|
54
|
+
.map((scope) => scope.trim())
|
|
55
|
+
);
|
|
56
|
+
if (normalizedScopes.size === 0) {
|
|
57
|
+
normalizedScopes.add('read');
|
|
58
|
+
}
|
|
59
|
+
const grant = {
|
|
60
|
+
canonicalPath,
|
|
61
|
+
base: path.dirname(canonicalPath),
|
|
62
|
+
scopes: normalizedScopes,
|
|
63
|
+
expiresAt: Date.now() + OUTSIDE_FILE_GRANT_TTL_MS,
|
|
64
|
+
};
|
|
65
|
+
outsideFileGrants.set(token, grant);
|
|
66
|
+
return {
|
|
67
|
+
path: canonicalPath,
|
|
68
|
+
outsideFileGrant: token,
|
|
69
|
+
expiresAt: grant.expiresAt,
|
|
70
|
+
};
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const resolveOutsideFileGrant = async ({ token, targetPath, scope, fsPromises }) => {
|
|
74
|
+
pruneOutsideFileGrants();
|
|
75
|
+
if (typeof token !== 'string' || !token.trim()) {
|
|
76
|
+
return { ok: false, error: 'Outside workspace file access requires a grant' };
|
|
77
|
+
}
|
|
78
|
+
const grant = outsideFileGrants.get(token.trim());
|
|
79
|
+
if (!grant) {
|
|
80
|
+
return { ok: false, error: 'Outside workspace file grant is invalid or expired' };
|
|
81
|
+
}
|
|
82
|
+
if (!grant.scopes.has(scope)) {
|
|
83
|
+
return { ok: false, error: 'Outside workspace file grant does not allow this operation' };
|
|
84
|
+
}
|
|
85
|
+
const canonicalPath = await fsPromises.realpath(targetPath);
|
|
86
|
+
if (canonicalPath !== grant.canonicalPath) {
|
|
87
|
+
return { ok: false, error: 'Outside workspace file grant does not match requested path' };
|
|
88
|
+
}
|
|
89
|
+
return { ok: true, base: grant.base, resolved: canonicalPath, granted: true };
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const createCommandTimeoutMs = () => {
|
|
93
|
+
const raw = Number(process.env.PICHAMBER_FS_EXEC_TIMEOUT_MS);
|
|
94
|
+
if (Number.isFinite(raw) && raw > 0) return raw;
|
|
95
|
+
return 5 * 60 * 1000;
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
// How long a cached git-read result stays fresh. The location of a repo's git
|
|
99
|
+
// directory is effectively static while the app runs, so a short TTL safely
|
|
100
|
+
// absorbs the burst of identical lookups a fresh client (e.g. right after a
|
|
101
|
+
// page reload) fires for every project. Set to 0 to disable caching.
|
|
102
|
+
const createGitReadCacheTtlMs = () => {
|
|
103
|
+
const raw = Number(process.env.PICHAMBER_GIT_READ_CACHE_TTL_MS);
|
|
104
|
+
if (Number.isFinite(raw) && raw >= 0) return raw;
|
|
105
|
+
return 30 * 1000;
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
const createGitCheckIgnoreTimeoutMs = () => {
|
|
109
|
+
const raw = Number(process.env.PICHAMBER_GIT_CHECK_IGNORE_TIMEOUT_MS);
|
|
110
|
+
if (Number.isFinite(raw) && raw >= 0) return raw;
|
|
111
|
+
return 2500;
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
const FILE_MIME_MAP = Object.freeze({
|
|
115
|
+
'.html': 'text/html',
|
|
116
|
+
'.htm': 'text/html',
|
|
117
|
+
'.css': 'text/css',
|
|
118
|
+
'.js': 'application/javascript',
|
|
119
|
+
'.mjs': 'application/javascript',
|
|
120
|
+
'.json': 'application/json',
|
|
121
|
+
'.wasm': 'application/wasm',
|
|
122
|
+
'.xml': 'application/xml',
|
|
123
|
+
'.txt': 'text/plain',
|
|
124
|
+
'.md': 'text/markdown',
|
|
125
|
+
'.pdf': 'application/pdf',
|
|
126
|
+
'.csv': 'text/csv',
|
|
127
|
+
'.woff2': 'font/woff2',
|
|
128
|
+
'.woff': 'font/woff',
|
|
129
|
+
'.ttf': 'font/ttf',
|
|
130
|
+
'.eot': 'application/vnd.ms-fontobject',
|
|
131
|
+
'.mp3': 'audio/mpeg',
|
|
132
|
+
'.mp4': 'video/mp4',
|
|
133
|
+
'.png': 'image/png',
|
|
134
|
+
'.jpg': 'image/jpeg',
|
|
135
|
+
'.jpeg': 'image/jpeg',
|
|
136
|
+
'.gif': 'image/gif',
|
|
137
|
+
'.svg': 'image/svg+xml',
|
|
138
|
+
'.webp': 'image/webp',
|
|
139
|
+
'.ico': 'image/x-icon',
|
|
140
|
+
'.bmp': 'image/bmp',
|
|
141
|
+
'.avif': 'image/avif',
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
const MAX_SERVE_BYTES = 100 * 1024 * 1024;
|
|
145
|
+
|
|
146
|
+
// Only deterministic, side-effect-free git plumbing path queries are cacheable.
|
|
147
|
+
// Anything outside this allowlist (including any non-git command) runs normally
|
|
148
|
+
// — we never cache arbitrary exec.
|
|
149
|
+
const normalizeCommand = (command) =>
|
|
150
|
+
typeof command === 'string' ? command.trim().replace(/\s+/g, ' ') : '';
|
|
151
|
+
|
|
152
|
+
const isCacheableGitReadCommand = (command) => {
|
|
153
|
+
const normalized = normalizeCommand(command);
|
|
154
|
+
return /^git rev-parse(?: --(?:absolute-git-dir|git-common-dir|show-toplevel)){1,3}$/.test(normalized);
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
// Dual-constraint bound per the project's caching policy (count + bytes). Git
|
|
158
|
+
// rev-parse outputs are tiny, so these ceilings are generous and only guard
|
|
159
|
+
// against pathological growth on long-lived, many-directory deployments.
|
|
160
|
+
const GIT_READ_CACHE_MAX_ENTRIES = 500;
|
|
161
|
+
const GIT_READ_CACHE_MAX_BYTES = 1024 * 1024;
|
|
162
|
+
|
|
163
|
+
const gitReadEntryBytes = (key, result) =>
|
|
164
|
+
key.length + (result?.stdout?.length || 0) + (result?.stderr?.length || 0);
|
|
165
|
+
|
|
166
|
+
const isPathWithinRoot = (resolvedPath, rootPath, path, os) => {
|
|
167
|
+
const resolvedRoot = path.resolve(rootPath || os.homedir());
|
|
168
|
+
const relative = path.relative(resolvedRoot, resolvedPath);
|
|
169
|
+
if (relative.startsWith('..') || path.isAbsolute(relative)) {
|
|
170
|
+
return false;
|
|
171
|
+
}
|
|
172
|
+
return true;
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
const resolveWorkspacePath = ({ targetPath, baseDirectory, path, os, normalizeDirectoryPath, pichamberUserConfigRoot }) => {
|
|
176
|
+
const normalized = normalizeDirectoryPath(targetPath);
|
|
177
|
+
if (!normalized || typeof normalized !== 'string') {
|
|
178
|
+
return { ok: false, error: 'Path is required' };
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const resolved = path.resolve(normalized);
|
|
182
|
+
const resolvedBase = path.resolve(baseDirectory || os.homedir());
|
|
183
|
+
|
|
184
|
+
if (isPathWithinRoot(resolved, resolvedBase, path, os)) {
|
|
185
|
+
return { ok: true, base: resolvedBase, resolved };
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (isPathWithinRoot(resolved, pichamberUserConfigRoot, path, os)) {
|
|
189
|
+
return { ok: true, base: path.resolve(pichamberUserConfigRoot), resolved };
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return { ok: false, error: 'Path is outside of active workspace' };
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
const resolveWorkspacePathFromWorktrees = async ({ targetPath, baseDirectory, path, os, normalizeDirectoryPath }) => {
|
|
196
|
+
const normalized = normalizeDirectoryPath(targetPath);
|
|
197
|
+
if (!normalized || typeof normalized !== 'string') {
|
|
198
|
+
return { ok: false, error: 'Path is required' };
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const resolved = path.resolve(normalized);
|
|
202
|
+
const resolvedBase = path.resolve(baseDirectory || os.homedir());
|
|
203
|
+
|
|
204
|
+
try {
|
|
205
|
+
const { getWorktrees } = await import('../git/index.js');
|
|
206
|
+
const worktrees = await getWorktrees(resolvedBase);
|
|
207
|
+
|
|
208
|
+
for (const worktree of worktrees) {
|
|
209
|
+
const candidatePath = typeof worktree?.path === 'string'
|
|
210
|
+
? worktree.path
|
|
211
|
+
: (typeof worktree?.worktree === 'string' ? worktree.worktree : '');
|
|
212
|
+
const candidate = normalizeDirectoryPath(candidatePath);
|
|
213
|
+
if (!candidate) {
|
|
214
|
+
continue;
|
|
215
|
+
}
|
|
216
|
+
const candidateResolved = path.resolve(candidate);
|
|
217
|
+
if (isPathWithinRoot(resolved, candidateResolved, path, os)) {
|
|
218
|
+
return { ok: true, base: candidateResolved, resolved };
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
} catch (error) {
|
|
222
|
+
console.warn('Failed to resolve worktree roots:', error);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return { ok: false, error: 'Path is outside of active workspace' };
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
const resolveWorkspacePathFromContext = async ({ req, targetPath, resolveProjectDirectory, path, os, normalizeDirectoryPath, pichamberUserConfigRoot }) => {
|
|
229
|
+
const resolvedProject = await resolveProjectDirectory(req);
|
|
230
|
+
if (!resolvedProject.directory) {
|
|
231
|
+
return { ok: false, error: resolvedProject.error || 'Active workspace is required' };
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const resolved = resolveWorkspacePath({
|
|
235
|
+
targetPath,
|
|
236
|
+
baseDirectory: resolvedProject.directory,
|
|
237
|
+
path,
|
|
238
|
+
os,
|
|
239
|
+
normalizeDirectoryPath,
|
|
240
|
+
pichamberUserConfigRoot,
|
|
241
|
+
});
|
|
242
|
+
if (resolved.ok || resolved.error !== 'Path is outside of active workspace') {
|
|
243
|
+
return resolved;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
return resolveWorkspacePathFromWorktrees({
|
|
247
|
+
targetPath,
|
|
248
|
+
baseDirectory: resolvedProject.directory,
|
|
249
|
+
path,
|
|
250
|
+
os,
|
|
251
|
+
normalizeDirectoryPath,
|
|
252
|
+
});
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
const deriveCloneDirectoryName = (remoteUrl) => {
|
|
256
|
+
const remote = typeof remoteUrl === 'string' ? remoteUrl.trim() : '';
|
|
257
|
+
if (!remote) return '';
|
|
258
|
+
const withoutQuery = remote.split(/[?#]/, 1)[0] || remote;
|
|
259
|
+
const match = withoutQuery.match(/([^/:]+?)(?:\.git)?\/?$/);
|
|
260
|
+
return match?.[1]?.trim() || '';
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
const resolveCloneGitIdentity = async (gitIdentityId) => {
|
|
264
|
+
const id = typeof gitIdentityId === 'string' ? gitIdentityId.trim() : '';
|
|
265
|
+
if (!id) return null;
|
|
266
|
+
const { getProfile, getGlobalIdentity } = await import('../git/index.js');
|
|
267
|
+
if (id === 'global') {
|
|
268
|
+
const globalIdentity = await getGlobalIdentity();
|
|
269
|
+
if (!globalIdentity?.userName || !globalIdentity?.userEmail) return null;
|
|
270
|
+
return {
|
|
271
|
+
id: 'global',
|
|
272
|
+
name: 'Global Identity',
|
|
273
|
+
userName: globalIdentity.userName,
|
|
274
|
+
userEmail: globalIdentity.userEmail,
|
|
275
|
+
sshKey: globalIdentity.sshCommand ? globalIdentity.sshCommand.replace('ssh -i ', '') : null,
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
return getProfile(id) || null;
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
const escapeCloneSshKeyPath = (sshKeyPath) => {
|
|
282
|
+
const raw = String(sshKeyPath || '').trim();
|
|
283
|
+
if (!raw) return '';
|
|
284
|
+
const normalized = process.platform === 'win32' ? raw.replace(/\\/g, '/') : raw;
|
|
285
|
+
const dangerousChars = /[`$!"';&|<>(){}[\]*?#~]/;
|
|
286
|
+
if (dangerousChars.test(normalized)) {
|
|
287
|
+
throw new Error(`SSH key path contains invalid characters: ${raw}`);
|
|
288
|
+
}
|
|
289
|
+
if (process.platform === 'win32') {
|
|
290
|
+
const driveMatch = normalized.match(/^([A-Za-z]):\//);
|
|
291
|
+
const unixPath = driveMatch ? `/${driveMatch[1].toLowerCase()}${normalized.slice(2)}` : normalized;
|
|
292
|
+
return `'${unixPath}'`;
|
|
293
|
+
}
|
|
294
|
+
return `'${normalized.replace(/'/g, "'\\''")}'`;
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
const resolveReadPathFromContext = async ({ req, targetPath, scope, resolveProjectDirectory, path, os, fsPromises, normalizeDirectoryPath, pichamberUserConfigRoot }) => {
|
|
298
|
+
if (req.query?.allowOutsideWorkspace === 'true') {
|
|
299
|
+
const normalized = normalizeDirectoryPath(targetPath);
|
|
300
|
+
if (!normalized || typeof normalized !== 'string') {
|
|
301
|
+
return { ok: false, error: 'Path is required' };
|
|
302
|
+
}
|
|
303
|
+
const resolved = path.resolve(normalized);
|
|
304
|
+
return resolveOutsideFileGrant({
|
|
305
|
+
token: req.query?.outsideFileGrant,
|
|
306
|
+
targetPath: resolved,
|
|
307
|
+
scope,
|
|
308
|
+
fsPromises,
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
return resolveWorkspacePathFromContext({
|
|
313
|
+
req,
|
|
314
|
+
targetPath,
|
|
315
|
+
resolveProjectDirectory,
|
|
316
|
+
path,
|
|
317
|
+
os,
|
|
318
|
+
normalizeDirectoryPath,
|
|
319
|
+
pichamberUserConfigRoot,
|
|
320
|
+
});
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
const runCommandInDirectory = ({ shell, shellFlag, command, resolvedCwd, spawn, buildAugmentedPath, commandTimeoutMs }) => {
|
|
324
|
+
return new Promise((resolve) => {
|
|
325
|
+
let stdout = '';
|
|
326
|
+
let stderr = '';
|
|
327
|
+
let timedOut = false;
|
|
328
|
+
|
|
329
|
+
const envPath = buildAugmentedPath();
|
|
330
|
+
const execEnv = { ...process.env, PATH: envPath };
|
|
331
|
+
|
|
332
|
+
const child = spawn(shell, [shellFlag, command], {
|
|
333
|
+
cwd: resolvedCwd,
|
|
334
|
+
env: execEnv,
|
|
335
|
+
windowsHide: true,
|
|
336
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
const timeout = setTimeout(() => {
|
|
340
|
+
timedOut = true;
|
|
341
|
+
try {
|
|
342
|
+
child.kill('SIGKILL');
|
|
343
|
+
} catch {
|
|
344
|
+
}
|
|
345
|
+
}, commandTimeoutMs);
|
|
346
|
+
|
|
347
|
+
child.stdout?.on('data', (chunk) => {
|
|
348
|
+
stdout += chunk.toString();
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
child.stderr?.on('data', (chunk) => {
|
|
352
|
+
stderr += chunk.toString();
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
child.on('error', (error) => {
|
|
356
|
+
clearTimeout(timeout);
|
|
357
|
+
resolve({
|
|
358
|
+
command,
|
|
359
|
+
success: false,
|
|
360
|
+
exitCode: undefined,
|
|
361
|
+
stdout: stdout.trim(),
|
|
362
|
+
stderr: stderr.trim(),
|
|
363
|
+
error: (error && error.message) || 'Command execution failed',
|
|
364
|
+
});
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
child.on('close', (code, signal) => {
|
|
368
|
+
clearTimeout(timeout);
|
|
369
|
+
const exitCode = typeof code === 'number' ? code : undefined;
|
|
370
|
+
const base = {
|
|
371
|
+
command,
|
|
372
|
+
success: exitCode === 0 && !timedOut,
|
|
373
|
+
exitCode,
|
|
374
|
+
stdout: stdout.trim(),
|
|
375
|
+
stderr: stderr.trim(),
|
|
376
|
+
};
|
|
377
|
+
|
|
378
|
+
if (timedOut) {
|
|
379
|
+
resolve({
|
|
380
|
+
...base,
|
|
381
|
+
success: false,
|
|
382
|
+
error: `Command timed out after ${commandTimeoutMs}ms` + (signal ? ` (${signal})` : ''),
|
|
383
|
+
});
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
resolve(base);
|
|
388
|
+
});
|
|
389
|
+
});
|
|
390
|
+
};
|
|
391
|
+
|
|
392
|
+
export const registerFsRoutes = (app, dependencies) => {
|
|
393
|
+
const {
|
|
394
|
+
os,
|
|
395
|
+
path,
|
|
396
|
+
fsPromises,
|
|
397
|
+
spawn,
|
|
398
|
+
platform = process.platform,
|
|
399
|
+
crypto,
|
|
400
|
+
normalizeDirectoryPath,
|
|
401
|
+
resolveProjectDirectory,
|
|
402
|
+
buildAugmentedPath,
|
|
403
|
+
resolveGitBinaryForSpawn,
|
|
404
|
+
pichamberUserConfigRoot,
|
|
405
|
+
pickDirectory = pickHostDirectory,
|
|
406
|
+
} = dependencies;
|
|
407
|
+
const realpathCache = createRealpathCache({
|
|
408
|
+
realpath: fsPromises.realpath.bind(fsPromises),
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
const spawnDetached = (command, args) => new Promise((resolve, reject) => {
|
|
412
|
+
let child;
|
|
413
|
+
try {
|
|
414
|
+
child = spawn(command, args, { windowsHide: true, stdio: 'ignore', detached: true });
|
|
415
|
+
} catch (error) {
|
|
416
|
+
reject(new Error('Failed to launch file browser', { cause: error }));
|
|
417
|
+
return;
|
|
418
|
+
}
|
|
419
|
+
const onError = (error) => {
|
|
420
|
+
child.removeListener('spawn', onSpawn);
|
|
421
|
+
reject(new Error('Failed to launch file browser', { cause: error }));
|
|
422
|
+
};
|
|
423
|
+
const onSpawn = () => {
|
|
424
|
+
child.removeListener('error', onError);
|
|
425
|
+
child.unref();
|
|
426
|
+
resolve();
|
|
427
|
+
};
|
|
428
|
+
child.once('error', onError);
|
|
429
|
+
child.once('spawn', onSpawn);
|
|
430
|
+
});
|
|
431
|
+
|
|
432
|
+
const execJobs = new Map();
|
|
433
|
+
const commandTimeoutMs = createCommandTimeoutMs();
|
|
434
|
+
const gitReadCacheTtlMs = createGitReadCacheTtlMs();
|
|
435
|
+
const gitCheckIgnoreTimeoutMs = createGitCheckIgnoreTimeoutMs();
|
|
436
|
+
const gitReadCache = new Map();
|
|
437
|
+
const inFlightGitReadCache = new Map();
|
|
438
|
+
|
|
439
|
+
const pruneExecJobs = () => {
|
|
440
|
+
const now = Date.now();
|
|
441
|
+
for (const [jobId, job] of execJobs.entries()) {
|
|
442
|
+
if (!job || typeof job !== 'object') {
|
|
443
|
+
execJobs.delete(jobId);
|
|
444
|
+
continue;
|
|
445
|
+
}
|
|
446
|
+
const updatedAt = typeof job.updatedAt === 'number' ? job.updatedAt : 0;
|
|
447
|
+
if (updatedAt && now - updatedAt > EXEC_JOB_TTL_MS) {
|
|
448
|
+
execJobs.delete(jobId);
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
};
|
|
452
|
+
|
|
453
|
+
const pruneGitReadCache = () => {
|
|
454
|
+
if (gitReadCacheTtlMs <= 0) {
|
|
455
|
+
return;
|
|
456
|
+
}
|
|
457
|
+
const now = Date.now();
|
|
458
|
+
for (const [key, entry] of gitReadCache.entries()) {
|
|
459
|
+
if (!entry || now - entry.at > gitReadCacheTtlMs) {
|
|
460
|
+
gitReadCache.delete(key);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
};
|
|
464
|
+
|
|
465
|
+
// Insert with LRU (oldest-first) eviction enforcing both count and byte caps.
|
|
466
|
+
// Map iteration order is insertion order, so deleting+re-setting a key moves
|
|
467
|
+
// it to the most-recently-used position.
|
|
468
|
+
const setGitReadCacheEntry = (key, result) => {
|
|
469
|
+
gitReadCache.delete(key);
|
|
470
|
+
gitReadCache.set(key, { result, at: Date.now() });
|
|
471
|
+
|
|
472
|
+
let totalBytes = 0;
|
|
473
|
+
for (const [k, entry] of gitReadCache) {
|
|
474
|
+
totalBytes += gitReadEntryBytes(k, entry.result);
|
|
475
|
+
}
|
|
476
|
+
while (
|
|
477
|
+
gitReadCache.size > GIT_READ_CACHE_MAX_ENTRIES ||
|
|
478
|
+
(totalBytes > GIT_READ_CACHE_MAX_BYTES && gitReadCache.size > 1)
|
|
479
|
+
) {
|
|
480
|
+
const oldest = gitReadCache.entries().next().value;
|
|
481
|
+
if (!oldest) {
|
|
482
|
+
break;
|
|
483
|
+
}
|
|
484
|
+
totalBytes -= gitReadEntryBytes(oldest[0], oldest[1].result);
|
|
485
|
+
gitReadCache.delete(oldest[0]);
|
|
486
|
+
}
|
|
487
|
+
};
|
|
488
|
+
|
|
489
|
+
// Runs a command, transparently serving/storing cacheable git-read results.
|
|
490
|
+
// Non-cacheable commands always execute and are never stored.
|
|
491
|
+
const runCommandWithGitReadCache = async ({ shell, shellFlag, command, resolvedCwd }) => {
|
|
492
|
+
const cacheable = gitReadCacheTtlMs > 0 && isCacheableGitReadCommand(command);
|
|
493
|
+
const cacheKey = cacheable ? `${resolvedCwd}${normalizeCommand(command)}` : null;
|
|
494
|
+
|
|
495
|
+
if (cacheKey) {
|
|
496
|
+
const cached = gitReadCache.get(cacheKey);
|
|
497
|
+
if (cached && Date.now() - cached.at < gitReadCacheTtlMs) {
|
|
498
|
+
// Refresh recency for LRU without altering the entry's age/TTL.
|
|
499
|
+
gitReadCache.delete(cacheKey);
|
|
500
|
+
gitReadCache.set(cacheKey, cached);
|
|
501
|
+
return { ...cached.result, command };
|
|
502
|
+
}
|
|
503
|
+
if (cached) {
|
|
504
|
+
gitReadCache.delete(cacheKey);
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
const inFlight = inFlightGitReadCache.get(cacheKey);
|
|
508
|
+
if (inFlight) {
|
|
509
|
+
const result = await inFlight;
|
|
510
|
+
return { ...result, command };
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
const runPromise = runCommandInDirectory({
|
|
515
|
+
shell,
|
|
516
|
+
shellFlag,
|
|
517
|
+
command,
|
|
518
|
+
resolvedCwd,
|
|
519
|
+
spawn,
|
|
520
|
+
buildAugmentedPath,
|
|
521
|
+
commandTimeoutMs,
|
|
522
|
+
}).then((result) => {
|
|
523
|
+
// Only cache successful results — failures may be transient.
|
|
524
|
+
if (cacheKey && result && result.success) {
|
|
525
|
+
setGitReadCacheEntry(cacheKey, result);
|
|
526
|
+
}
|
|
527
|
+
return result;
|
|
528
|
+
}).finally(() => {
|
|
529
|
+
if (cacheKey && inFlightGitReadCache.get(cacheKey) === runPromise) {
|
|
530
|
+
inFlightGitReadCache.delete(cacheKey);
|
|
531
|
+
}
|
|
532
|
+
});
|
|
533
|
+
|
|
534
|
+
if (cacheKey) {
|
|
535
|
+
inFlightGitReadCache.set(cacheKey, runPromise);
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
return runPromise;
|
|
539
|
+
};
|
|
540
|
+
|
|
541
|
+
const runExecJob = async (job) => {
|
|
542
|
+
job.status = 'running';
|
|
543
|
+
job.updatedAt = Date.now();
|
|
544
|
+
|
|
545
|
+
const results = [];
|
|
546
|
+
for (const command of job.commands) {
|
|
547
|
+
if (typeof command !== 'string' || !command.trim()) {
|
|
548
|
+
results.push({ command, success: false, error: 'Invalid command' });
|
|
549
|
+
continue;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
try {
|
|
553
|
+
const result = await runCommandWithGitReadCache({
|
|
554
|
+
shell: job.shell,
|
|
555
|
+
shellFlag: job.shellFlag,
|
|
556
|
+
command,
|
|
557
|
+
resolvedCwd: job.resolvedCwd,
|
|
558
|
+
});
|
|
559
|
+
results.push(result);
|
|
560
|
+
} catch (error) {
|
|
561
|
+
results.push({
|
|
562
|
+
command,
|
|
563
|
+
success: false,
|
|
564
|
+
error: (error && error.message) || 'Command execution failed',
|
|
565
|
+
});
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
job.results = results;
|
|
569
|
+
job.updatedAt = Date.now();
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
job.results = results;
|
|
573
|
+
job.success = results.every((r) => r.success);
|
|
574
|
+
job.status = 'done';
|
|
575
|
+
job.finishedAt = Date.now();
|
|
576
|
+
job.updatedAt = Date.now();
|
|
577
|
+
};
|
|
578
|
+
|
|
579
|
+
app.get('/api/fs/home', (_req, res) => {
|
|
580
|
+
try {
|
|
581
|
+
const home = os.homedir();
|
|
582
|
+
if (!home || typeof home !== 'string' || home.length === 0) {
|
|
583
|
+
return res.status(500).json({ error: 'Failed to resolve home directory' });
|
|
584
|
+
}
|
|
585
|
+
const pichamberDataDir = typeof dependencies?.resolvePiChamberDataDir === 'function'
|
|
586
|
+
? dependencies.resolvePiChamberDataDir()
|
|
587
|
+
: resolvePiChamberDataDir();
|
|
588
|
+
return res.json({ home, pichamberDataDir });
|
|
589
|
+
} catch (error) {
|
|
590
|
+
console.error('Failed to resolve home directory:', error);
|
|
591
|
+
return res.status(500).json({ error: (error && error.message) || 'Failed to resolve home directory' });
|
|
592
|
+
}
|
|
593
|
+
});
|
|
594
|
+
|
|
595
|
+
app.post('/api/fs/mkdir', async (req, res) => {
|
|
596
|
+
try {
|
|
597
|
+
const { path: dirPath, allowOutsideWorkspace } = req.body ?? {};
|
|
598
|
+
if (typeof dirPath !== 'string' || !dirPath.trim()) {
|
|
599
|
+
return res.status(400).json({ error: 'Path is required' });
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
let resolvedPath = '';
|
|
603
|
+
if (allowOutsideWorkspace) {
|
|
604
|
+
console.warn('Rejected outside-workspace mkdir without trusted directory grant');
|
|
605
|
+
return res.status(403).json({ error: 'Outside workspace directory creation requires a grant' });
|
|
606
|
+
} else {
|
|
607
|
+
const resolved = await resolveWorkspacePathFromContext({
|
|
608
|
+
req,
|
|
609
|
+
targetPath: dirPath,
|
|
610
|
+
resolveProjectDirectory,
|
|
611
|
+
path,
|
|
612
|
+
os,
|
|
613
|
+
normalizeDirectoryPath,
|
|
614
|
+
pichamberUserConfigRoot,
|
|
615
|
+
});
|
|
616
|
+
if (!resolved.ok) {
|
|
617
|
+
return res.status(400).json({ error: resolved.error });
|
|
618
|
+
}
|
|
619
|
+
resolvedPath = resolved.resolved;
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
await fsPromises.mkdir(resolvedPath, { recursive: true });
|
|
623
|
+
return res.json({ success: true, path: resolvedPath });
|
|
624
|
+
} catch (error) {
|
|
625
|
+
if (isOsPermissionError(error)) {
|
|
626
|
+
return sendOsPermissionDenied(res, 'Access denied');
|
|
627
|
+
}
|
|
628
|
+
console.error('Failed to create directory:', error);
|
|
629
|
+
return res.status(500).json({ error: error.message || 'Failed to create directory' });
|
|
630
|
+
}
|
|
631
|
+
});
|
|
632
|
+
|
|
633
|
+
app.post('/api/fs/clone', async (req, res) => {
|
|
634
|
+
try {
|
|
635
|
+
const { remoteUrl, destinationPath, gitIdentityId } = req.body ?? {};
|
|
636
|
+
const remote = typeof remoteUrl === 'string' ? remoteUrl.trim() : '';
|
|
637
|
+
const destination = typeof destinationPath === 'string' ? destinationPath.trim() : '';
|
|
638
|
+
if (!remote) {
|
|
639
|
+
return res.status(400).json({ error: 'Repository URL is required' });
|
|
640
|
+
}
|
|
641
|
+
if (!destination) {
|
|
642
|
+
return res.status(400).json({ error: 'Destination path is required' });
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
let resolvedDestination = path.resolve(normalizeDirectoryPath(destination));
|
|
646
|
+
let parentPath = path.dirname(resolvedDestination);
|
|
647
|
+
let directoryName = path.basename(resolvedDestination);
|
|
648
|
+
|
|
649
|
+
const cloneIntoDestinationDirectory = destination.endsWith('/') || destination.endsWith('\\');
|
|
650
|
+
if (cloneIntoDestinationDirectory) {
|
|
651
|
+
const inferredName = deriveCloneDirectoryName(remote);
|
|
652
|
+
if (!inferredName) {
|
|
653
|
+
return res.status(400).json({ error: 'Could not infer repository directory name from URL' });
|
|
654
|
+
}
|
|
655
|
+
parentPath = resolvedDestination;
|
|
656
|
+
directoryName = inferredName;
|
|
657
|
+
resolvedDestination = path.join(parentPath, directoryName);
|
|
658
|
+
} else {
|
|
659
|
+
try {
|
|
660
|
+
const stat = await fsPromises.stat(resolvedDestination);
|
|
661
|
+
if (stat.isDirectory()) {
|
|
662
|
+
const inferredName = deriveCloneDirectoryName(remote);
|
|
663
|
+
if (!inferredName) {
|
|
664
|
+
return res.status(400).json({ error: 'Could not infer repository directory name from URL' });
|
|
665
|
+
}
|
|
666
|
+
parentPath = resolvedDestination;
|
|
667
|
+
directoryName = inferredName;
|
|
668
|
+
resolvedDestination = path.join(parentPath, directoryName);
|
|
669
|
+
}
|
|
670
|
+
} catch (error) {
|
|
671
|
+
if (!error || error.code !== 'ENOENT') {
|
|
672
|
+
throw error;
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
if (!directoryName || directoryName === '.' || directoryName === '..') {
|
|
677
|
+
return res.status(400).json({ error: 'Destination path must include a directory name' });
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
const identity = await resolveCloneGitIdentity(gitIdentityId);
|
|
681
|
+
const gitArgs = ['clone', '--', remote, directoryName];
|
|
682
|
+
const sshKeyPath = typeof identity?.sshKey === 'string' ? identity.sshKey.trim() : '';
|
|
683
|
+
if (sshKeyPath) {
|
|
684
|
+
gitArgs.unshift(`core.sshCommand=ssh -i ${escapeCloneSshKeyPath(sshKeyPath)} -o IdentitiesOnly=yes -o BatchMode=yes -o StrictHostKeyChecking=accept-new`);
|
|
685
|
+
gitArgs.unshift('-c');
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
await fsPromises.mkdir(parentPath, { recursive: true });
|
|
689
|
+
try {
|
|
690
|
+
await fsPromises.access(resolvedDestination);
|
|
691
|
+
return res.status(409).json({ error: 'Destination path already exists' });
|
|
692
|
+
} catch (error) {
|
|
693
|
+
if (!error || error.code !== 'ENOENT') {
|
|
694
|
+
throw error;
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
const output = await new Promise((resolve, reject) => {
|
|
699
|
+
const child = spawn(resolveGitBinaryForSpawn(), gitArgs, {
|
|
700
|
+
cwd: parentPath,
|
|
701
|
+
windowsHide: true,
|
|
702
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
703
|
+
env: {
|
|
704
|
+
...process.env,
|
|
705
|
+
PATH: buildAugmentedPath ? buildAugmentedPath(process.env.PATH || '') : process.env.PATH,
|
|
706
|
+
GIT_TERMINAL_PROMPT: '0',
|
|
707
|
+
},
|
|
708
|
+
});
|
|
709
|
+
|
|
710
|
+
let stdout = '';
|
|
711
|
+
let stderr = '';
|
|
712
|
+
child.stdout.on('data', (data) => { stdout += data.toString(); });
|
|
713
|
+
child.stderr.on('data', (data) => { stderr += data.toString(); });
|
|
714
|
+
child.on('error', reject);
|
|
715
|
+
child.on('close', (code) => {
|
|
716
|
+
const combined = `${stdout}\n${stderr}`.trim();
|
|
717
|
+
if (code === 0) {
|
|
718
|
+
resolve(combined);
|
|
719
|
+
return;
|
|
720
|
+
}
|
|
721
|
+
const message = combined || `git clone failed with exit code ${code}`;
|
|
722
|
+
reject(new Error(message));
|
|
723
|
+
});
|
|
724
|
+
});
|
|
725
|
+
|
|
726
|
+
if (identity?.userName && identity?.userEmail) {
|
|
727
|
+
try {
|
|
728
|
+
const { setLocalIdentity } = await import('../git/index.js');
|
|
729
|
+
await setLocalIdentity(resolvedDestination, identity);
|
|
730
|
+
} catch (error) {
|
|
731
|
+
console.warn('Failed to apply git identity after clone:', error);
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
return res.json({ success: true, path: resolvedDestination, output });
|
|
736
|
+
} catch (error) {
|
|
737
|
+
console.error('Failed to clone repository:', error);
|
|
738
|
+
return res.status(500).json({ error: error.message || 'Failed to clone repository' });
|
|
739
|
+
}
|
|
740
|
+
});
|
|
741
|
+
|
|
742
|
+
app.get('/api/fs/stat', async (req, res) => {
|
|
743
|
+
const filePath = typeof req.query.path === 'string' ? req.query.path.trim() : '';
|
|
744
|
+
const optional = req.query.optional === 'true';
|
|
745
|
+
if (!filePath) {
|
|
746
|
+
return res.status(400).json({ error: 'Path is required' });
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
try {
|
|
750
|
+
const resolved = await resolveReadPathFromContext({
|
|
751
|
+
req,
|
|
752
|
+
targetPath: filePath,
|
|
753
|
+
scope: 'stat',
|
|
754
|
+
resolveProjectDirectory,
|
|
755
|
+
path,
|
|
756
|
+
os,
|
|
757
|
+
fsPromises,
|
|
758
|
+
normalizeDirectoryPath,
|
|
759
|
+
pichamberUserConfigRoot,
|
|
760
|
+
});
|
|
761
|
+
if (!resolved.ok) {
|
|
762
|
+
if (req.query?.allowOutsideWorkspace === 'true') {
|
|
763
|
+
console.warn(`Rejected outside-workspace stat: ${resolved.error}`);
|
|
764
|
+
}
|
|
765
|
+
return res.status(400).json({ error: resolved.error });
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
const [canonicalPath, canonicalBase] = await Promise.all([
|
|
769
|
+
fsPromises.realpath(resolved.resolved),
|
|
770
|
+
fsPromises.realpath(resolved.base).catch(() => path.resolve(resolved.base)),
|
|
771
|
+
]);
|
|
772
|
+
|
|
773
|
+
if (!isPathWithinRoot(canonicalPath, canonicalBase, path, os)) {
|
|
774
|
+
return res.status(403).json({ error: 'Access to file denied' });
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
const stats = await fsPromises.stat(canonicalPath);
|
|
778
|
+
if (!stats.isFile()) {
|
|
779
|
+
return res.status(400).json({ error: 'Specified path is not a file' });
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
return res.json({ path: canonicalPath, isFile: true, size: stats.size, mtimeMs: stats.mtimeMs });
|
|
783
|
+
} catch (error) {
|
|
784
|
+
const err = error;
|
|
785
|
+
if (err && typeof err === 'object' && err.code === 'ENOENT') {
|
|
786
|
+
if (optional) {
|
|
787
|
+
return res.json({ path: filePath, exists: false });
|
|
788
|
+
}
|
|
789
|
+
return res.status(404).json({ error: 'File not found' });
|
|
790
|
+
}
|
|
791
|
+
if (isOsPermissionError(err)) {
|
|
792
|
+
return sendOsPermissionDenied(res, 'Access to file denied');
|
|
793
|
+
}
|
|
794
|
+
console.error('Failed to stat file:', error);
|
|
795
|
+
return res.status(500).json({ error: (error && error.message) || 'Failed to stat file' });
|
|
796
|
+
}
|
|
797
|
+
});
|
|
798
|
+
|
|
799
|
+
app.get('/api/fs/read', async (req, res) => {
|
|
800
|
+
const filePath = typeof req.query.path === 'string' ? req.query.path.trim() : '';
|
|
801
|
+
const optional = req.query.optional === 'true';
|
|
802
|
+
if (!filePath) {
|
|
803
|
+
return res.status(400).json({ error: 'Path is required' });
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
try {
|
|
807
|
+
const resolved = await resolveReadPathFromContext({
|
|
808
|
+
req,
|
|
809
|
+
targetPath: filePath,
|
|
810
|
+
scope: 'read',
|
|
811
|
+
resolveProjectDirectory,
|
|
812
|
+
path,
|
|
813
|
+
os,
|
|
814
|
+
fsPromises,
|
|
815
|
+
normalizeDirectoryPath,
|
|
816
|
+
pichamberUserConfigRoot,
|
|
817
|
+
});
|
|
818
|
+
if (!resolved.ok) {
|
|
819
|
+
if (req.query?.allowOutsideWorkspace === 'true') {
|
|
820
|
+
console.warn(`Rejected outside-workspace read: ${resolved.error}`);
|
|
821
|
+
}
|
|
822
|
+
return res.status(400).json({ error: resolved.error });
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
const [canonicalPath, canonicalBase] = await Promise.all([
|
|
826
|
+
fsPromises.realpath(resolved.resolved),
|
|
827
|
+
fsPromises.realpath(resolved.base).catch(() => path.resolve(resolved.base)),
|
|
828
|
+
]);
|
|
829
|
+
|
|
830
|
+
if (!isPathWithinRoot(canonicalPath, canonicalBase, path, os)) {
|
|
831
|
+
return res.status(403).json({ error: 'Access to file denied' });
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
const stats = await fsPromises.stat(canonicalPath);
|
|
835
|
+
if (!stats.isFile()) {
|
|
836
|
+
return res.status(400).json({ error: 'Specified path is not a file' });
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
let content = await fsPromises.readFile(canonicalPath, 'utf8');
|
|
840
|
+
// Retry empty reads — concurrent writer may have truncated the file
|
|
841
|
+
// between our stat and read (O_TRUNC window). If the file existed with
|
|
842
|
+
// content at stat time but we read nothing, the writer hasn't finished
|
|
843
|
+
// writing yet.
|
|
844
|
+
if (content.length === 0 && stats.size > 0) {
|
|
845
|
+
for (let attempt = 0; attempt < 3; attempt++) {
|
|
846
|
+
await new Promise((r) => setTimeout(r, 50 * (attempt + 1)));
|
|
847
|
+
content = await fsPromises.readFile(canonicalPath, 'utf8');
|
|
848
|
+
if (content.length > 0) break;
|
|
849
|
+
}
|
|
850
|
+
if (content.length === 0) {
|
|
851
|
+
console.warn(`Read retry exhausted for ${canonicalPath}: stat reported ${stats.size} bytes but content is empty`);
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
return res.type('text/plain').send(content);
|
|
855
|
+
} catch (error) {
|
|
856
|
+
const err = error;
|
|
857
|
+
if (err && typeof err === 'object' && err.code === 'ENOENT') {
|
|
858
|
+
if (optional) {
|
|
859
|
+
return res.type('text/plain').send('');
|
|
860
|
+
}
|
|
861
|
+
return res.status(404).json({ error: 'File not found' });
|
|
862
|
+
}
|
|
863
|
+
if (isOsPermissionError(err)) {
|
|
864
|
+
return sendOsPermissionDenied(res, 'Access to file denied');
|
|
865
|
+
}
|
|
866
|
+
console.error('Failed to read file:', error);
|
|
867
|
+
return res.status(500).json({ error: (error && error.message) || 'Failed to read file' });
|
|
868
|
+
}
|
|
869
|
+
});
|
|
870
|
+
|
|
871
|
+
app.get('/api/fs/raw', async (req, res) => {
|
|
872
|
+
const filePath = typeof req.query.path === 'string' ? req.query.path.trim() : '';
|
|
873
|
+
if (!filePath) {
|
|
874
|
+
return res.status(400).json({ error: 'Path is required' });
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
try {
|
|
878
|
+
const resolved = await resolveReadPathFromContext({
|
|
879
|
+
req,
|
|
880
|
+
targetPath: filePath,
|
|
881
|
+
scope: 'raw',
|
|
882
|
+
resolveProjectDirectory,
|
|
883
|
+
path,
|
|
884
|
+
os,
|
|
885
|
+
fsPromises,
|
|
886
|
+
normalizeDirectoryPath,
|
|
887
|
+
pichamberUserConfigRoot,
|
|
888
|
+
});
|
|
889
|
+
if (!resolved.ok) {
|
|
890
|
+
if (req.query?.allowOutsideWorkspace === 'true') {
|
|
891
|
+
console.warn(`Rejected outside-workspace raw read: ${resolved.error}`);
|
|
892
|
+
}
|
|
893
|
+
return res.status(400).json({ error: resolved.error });
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
const [canonicalPath, canonicalBase] = await Promise.all([
|
|
897
|
+
fsPromises.realpath(resolved.resolved),
|
|
898
|
+
fsPromises.realpath(resolved.base).catch(() => path.resolve(resolved.base)),
|
|
899
|
+
]);
|
|
900
|
+
|
|
901
|
+
if (!isPathWithinRoot(canonicalPath, canonicalBase, path, os)) {
|
|
902
|
+
return res.status(403).json({ error: 'Access to file denied' });
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
const stats = await fsPromises.stat(canonicalPath);
|
|
906
|
+
if (!stats.isFile()) {
|
|
907
|
+
return res.status(400).json({ error: 'Specified path is not a file' });
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
const ext = path.extname(canonicalPath).toLowerCase();
|
|
911
|
+
const mimeMap = {
|
|
912
|
+
'.png': 'image/png',
|
|
913
|
+
'.jpg': 'image/jpeg',
|
|
914
|
+
'.jpeg': 'image/jpeg',
|
|
915
|
+
'.gif': 'image/gif',
|
|
916
|
+
'.svg': 'image/svg+xml',
|
|
917
|
+
'.webp': 'image/webp',
|
|
918
|
+
'.ico': 'image/x-icon',
|
|
919
|
+
'.bmp': 'image/bmp',
|
|
920
|
+
'.avif': 'image/avif',
|
|
921
|
+
'.pdf': 'application/pdf',
|
|
922
|
+
};
|
|
923
|
+
const mimeType = mimeMap[ext] || 'application/octet-stream';
|
|
924
|
+
|
|
925
|
+
const download = req.query.download === 'true';
|
|
926
|
+
if (download) {
|
|
927
|
+
const fileName = path.basename(canonicalPath);
|
|
928
|
+
// RFC 5987: use filename*= for non-ASCII filenames, with ASCII-only
|
|
929
|
+
// filename= as fallback for older clients.
|
|
930
|
+
const asciiOnly = fileName.replace(/[^\u0000-\u007F]/g, '');
|
|
931
|
+
const fallback = asciiOnly || 'file';
|
|
932
|
+
// Percent-encode the raw UTF-8 bytes for filename*=
|
|
933
|
+
const encoded = encodeURIComponent(fileName);
|
|
934
|
+
res.setHeader('Content-Disposition', `attachment; filename="${fallback}"; filename*=UTF-8''${encoded}`);
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
const content = await fsPromises.readFile(canonicalPath);
|
|
938
|
+
res.setHeader('Cache-Control', 'no-store');
|
|
939
|
+
if (resolved.granted) {
|
|
940
|
+
res.setHeader('Referrer-Policy', 'no-referrer');
|
|
941
|
+
}
|
|
942
|
+
return res.type(mimeType).send(content);
|
|
943
|
+
} catch (error) {
|
|
944
|
+
const err = error;
|
|
945
|
+
if (err && typeof err === 'object' && err.code === 'ENOENT') {
|
|
946
|
+
return res.status(404).json({ error: 'File not found' });
|
|
947
|
+
}
|
|
948
|
+
if (isOsPermissionError(err)) {
|
|
949
|
+
return sendOsPermissionDenied(res, 'Access to file denied');
|
|
950
|
+
}
|
|
951
|
+
console.error('Failed to read raw file:', error);
|
|
952
|
+
return res.status(500).json({ error: (error && error.message) || 'Failed to read file' });
|
|
953
|
+
}
|
|
954
|
+
});
|
|
955
|
+
|
|
956
|
+
app.get(/^\/api\/fs\/serve\/(.+)$/, async (req, res) => {
|
|
957
|
+
const rawPath = req.params[0] || '';
|
|
958
|
+
if (!rawPath) {
|
|
959
|
+
return res.status(400).json({ error: 'Path is required' });
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
try {
|
|
963
|
+
if (req.query?.allowOutsideWorkspace === 'true') {
|
|
964
|
+
return res.status(403).json({ error: 'allowOutsideWorkspace is not permitted for this endpoint' });
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
const filePath = path.resolve('/', rawPath);
|
|
968
|
+
const resolved = await resolveReadPathFromContext({
|
|
969
|
+
req,
|
|
970
|
+
targetPath: filePath,
|
|
971
|
+
resolveProjectDirectory,
|
|
972
|
+
path,
|
|
973
|
+
os,
|
|
974
|
+
normalizeDirectoryPath,
|
|
975
|
+
pichamberUserConfigRoot,
|
|
976
|
+
});
|
|
977
|
+
if (!resolved.ok) {
|
|
978
|
+
return res.status(400).json({ error: resolved.error });
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
const [canonicalPath, canonicalBase] = await Promise.all([
|
|
982
|
+
fsPromises.realpath(resolved.resolved),
|
|
983
|
+
fsPromises.realpath(resolved.base).catch(() => path.resolve(resolved.base)),
|
|
984
|
+
]);
|
|
985
|
+
|
|
986
|
+
if (!isPathWithinRoot(canonicalPath, canonicalBase, path, os)) {
|
|
987
|
+
return res.status(403).json({ error: 'Access to file denied' });
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
const stats = await fsPromises.stat(canonicalPath);
|
|
991
|
+
if (!stats.isFile()) {
|
|
992
|
+
return res.status(400).json({ error: 'Specified path is not a file' });
|
|
993
|
+
}
|
|
994
|
+
if (stats.size > MAX_SERVE_BYTES) {
|
|
995
|
+
return res.status(413).json({ error: 'File too large to serve' });
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
const ext = path.extname(canonicalPath).toLowerCase();
|
|
999
|
+
const mimeType = FILE_MIME_MAP[ext] || 'application/octet-stream';
|
|
1000
|
+
const content = await fsPromises.readFile(canonicalPath);
|
|
1001
|
+
res.setHeader('Cache-Control', 'no-store');
|
|
1002
|
+
res.setHeader('X-Content-Type-Options', 'nosniff');
|
|
1003
|
+
return res.type(mimeType).send(content);
|
|
1004
|
+
} catch (error) {
|
|
1005
|
+
const err = error;
|
|
1006
|
+
if (err && typeof err === 'object' && err.code === 'ENOENT') {
|
|
1007
|
+
return res.status(404).json({ error: 'File not found' });
|
|
1008
|
+
}
|
|
1009
|
+
if (isOsPermissionError(err)) {
|
|
1010
|
+
return sendOsPermissionDenied(res, 'Access to file denied');
|
|
1011
|
+
}
|
|
1012
|
+
console.error('Failed to serve file:', error);
|
|
1013
|
+
return res.status(500).json({ error: (error && error.message) || 'Failed to serve file' });
|
|
1014
|
+
}
|
|
1015
|
+
});
|
|
1016
|
+
|
|
1017
|
+
app.post('/api/fs/write', async (req, res) => {
|
|
1018
|
+
const { path: filePath, content } = req.body || {};
|
|
1019
|
+
if (!filePath || typeof filePath !== 'string') {
|
|
1020
|
+
return res.status(400).json({ error: 'Path is required' });
|
|
1021
|
+
}
|
|
1022
|
+
if (typeof content !== 'string') {
|
|
1023
|
+
return res.status(400).json({ error: 'Content is required' });
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
try {
|
|
1027
|
+
const resolved = await resolveWorkspacePathFromContext({
|
|
1028
|
+
req,
|
|
1029
|
+
targetPath: filePath,
|
|
1030
|
+
resolveProjectDirectory,
|
|
1031
|
+
path,
|
|
1032
|
+
os,
|
|
1033
|
+
normalizeDirectoryPath,
|
|
1034
|
+
pichamberUserConfigRoot,
|
|
1035
|
+
});
|
|
1036
|
+
if (!resolved.ok) {
|
|
1037
|
+
return res.status(400).json({ error: resolved.error });
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
const writePath = await fsPromises.realpath(resolved.resolved).catch((error) => {
|
|
1041
|
+
if (error && typeof error === 'object' && error.code === 'ENOENT') {
|
|
1042
|
+
return resolved.resolved;
|
|
1043
|
+
}
|
|
1044
|
+
throw error;
|
|
1045
|
+
});
|
|
1046
|
+
const canonicalBase = await fsPromises.realpath(resolved.base).catch(() => path.resolve(resolved.base));
|
|
1047
|
+
if (!isPathWithinRoot(writePath, canonicalBase, path, os)) {
|
|
1048
|
+
return res.status(403).json({ error: 'Access denied' });
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
const existing = await fsPromises.readFile(writePath, 'utf8').catch(() => null);
|
|
1052
|
+
if (existing === content) {
|
|
1053
|
+
return res.json({ success: true, path: resolved.resolved });
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
await fsPromises.mkdir(path.dirname(writePath), { recursive: true });
|
|
1057
|
+
|
|
1058
|
+
// Atomic write: write to temp then rename to avoid concurrent readers
|
|
1059
|
+
// seeing an empty file during the O_TRUNC window of direct writeFile.
|
|
1060
|
+
const tmp = `${writePath}.tmp-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
1061
|
+
try {
|
|
1062
|
+
await fsPromises.writeFile(tmp, content, 'utf8');
|
|
1063
|
+
await fsPromises.rename(tmp, writePath);
|
|
1064
|
+
} catch (error) {
|
|
1065
|
+
await fsPromises.unlink(tmp).catch(() => {});
|
|
1066
|
+
throw error;
|
|
1067
|
+
}
|
|
1068
|
+
return res.json({ success: true, path: resolved.resolved });
|
|
1069
|
+
} catch (error) {
|
|
1070
|
+
const err = error;
|
|
1071
|
+
if (isOsPermissionError(err)) {
|
|
1072
|
+
return sendOsPermissionDenied(res, 'Access denied');
|
|
1073
|
+
}
|
|
1074
|
+
console.error('Failed to write file:', error);
|
|
1075
|
+
return res.status(500).json({ error: (error && error.message) || 'Failed to write file' });
|
|
1076
|
+
}
|
|
1077
|
+
});
|
|
1078
|
+
|
|
1079
|
+
app.post('/api/fs/delete', async (req, res) => {
|
|
1080
|
+
const { path: targetPath } = req.body || {};
|
|
1081
|
+
if (!targetPath || typeof targetPath !== 'string') {
|
|
1082
|
+
return res.status(400).json({ error: 'Path is required' });
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
try {
|
|
1086
|
+
const resolved = await resolveWorkspacePathFromContext({
|
|
1087
|
+
req,
|
|
1088
|
+
targetPath,
|
|
1089
|
+
resolveProjectDirectory,
|
|
1090
|
+
path,
|
|
1091
|
+
os,
|
|
1092
|
+
normalizeDirectoryPath,
|
|
1093
|
+
pichamberUserConfigRoot,
|
|
1094
|
+
});
|
|
1095
|
+
if (!resolved.ok) {
|
|
1096
|
+
return res.status(400).json({ error: resolved.error });
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
await fsPromises.rm(resolved.resolved, { recursive: true, force: true });
|
|
1100
|
+
return res.json({ success: true, path: resolved.resolved });
|
|
1101
|
+
} catch (error) {
|
|
1102
|
+
const err = error;
|
|
1103
|
+
if (err && typeof err === 'object' && err.code === 'ENOENT') {
|
|
1104
|
+
return res.status(404).json({ error: 'File or directory not found' });
|
|
1105
|
+
}
|
|
1106
|
+
if (isOsPermissionError(err)) {
|
|
1107
|
+
return sendOsPermissionDenied(res, 'Access denied');
|
|
1108
|
+
}
|
|
1109
|
+
console.error('Failed to delete path:', error);
|
|
1110
|
+
return res.status(500).json({ error: (error && error.message) || 'Failed to delete path' });
|
|
1111
|
+
}
|
|
1112
|
+
});
|
|
1113
|
+
|
|
1114
|
+
app.post('/api/fs/rename', async (req, res) => {
|
|
1115
|
+
const { oldPath, newPath } = req.body || {};
|
|
1116
|
+
if (!oldPath || typeof oldPath !== 'string') {
|
|
1117
|
+
return res.status(400).json({ error: 'oldPath is required' });
|
|
1118
|
+
}
|
|
1119
|
+
if (!newPath || typeof newPath !== 'string') {
|
|
1120
|
+
return res.status(400).json({ error: 'newPath is required' });
|
|
1121
|
+
}
|
|
1122
|
+
|
|
1123
|
+
try {
|
|
1124
|
+
const resolvedOld = await resolveWorkspacePathFromContext({
|
|
1125
|
+
req,
|
|
1126
|
+
targetPath: oldPath,
|
|
1127
|
+
resolveProjectDirectory,
|
|
1128
|
+
path,
|
|
1129
|
+
os,
|
|
1130
|
+
normalizeDirectoryPath,
|
|
1131
|
+
pichamberUserConfigRoot,
|
|
1132
|
+
});
|
|
1133
|
+
if (!resolvedOld.ok) {
|
|
1134
|
+
return res.status(400).json({ error: resolvedOld.error });
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
const resolvedNew = await resolveWorkspacePathFromContext({
|
|
1138
|
+
req,
|
|
1139
|
+
targetPath: newPath,
|
|
1140
|
+
resolveProjectDirectory,
|
|
1141
|
+
path,
|
|
1142
|
+
os,
|
|
1143
|
+
normalizeDirectoryPath,
|
|
1144
|
+
pichamberUserConfigRoot,
|
|
1145
|
+
});
|
|
1146
|
+
if (!resolvedNew.ok) {
|
|
1147
|
+
return res.status(400).json({ error: resolvedNew.error });
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
if (resolvedOld.base !== resolvedNew.base) {
|
|
1151
|
+
return res.status(400).json({ error: 'Source and destination must share the same workspace root' });
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1154
|
+
await fsPromises.rename(resolvedOld.resolved, resolvedNew.resolved);
|
|
1155
|
+
return res.json({ success: true, path: resolvedNew.resolved });
|
|
1156
|
+
} catch (error) {
|
|
1157
|
+
const err = error;
|
|
1158
|
+
if (err && typeof err === 'object' && err.code === 'ENOENT') {
|
|
1159
|
+
return res.status(404).json({ error: 'Source path not found' });
|
|
1160
|
+
}
|
|
1161
|
+
if (isOsPermissionError(err)) {
|
|
1162
|
+
return sendOsPermissionDenied(res, 'Access denied');
|
|
1163
|
+
}
|
|
1164
|
+
console.error('Failed to rename path:', error);
|
|
1165
|
+
return res.status(500).json({ error: (error && error.message) || 'Failed to rename path' });
|
|
1166
|
+
}
|
|
1167
|
+
});
|
|
1168
|
+
|
|
1169
|
+
app.post('/api/fs/reveal', async (req, res) => {
|
|
1170
|
+
const { path: targetPath } = req.body || {};
|
|
1171
|
+
if (!targetPath || typeof targetPath !== 'string') {
|
|
1172
|
+
return res.status(400).json({ error: 'Path is required' });
|
|
1173
|
+
}
|
|
1174
|
+
|
|
1175
|
+
try {
|
|
1176
|
+
const resolved = path.resolve(targetPath.trim());
|
|
1177
|
+
await fsPromises.access(resolved);
|
|
1178
|
+
|
|
1179
|
+
if (platform === 'darwin') {
|
|
1180
|
+
const stat = await fsPromises.stat(resolved);
|
|
1181
|
+
if (stat.isDirectory()) {
|
|
1182
|
+
await spawnDetached('open', [resolved]);
|
|
1183
|
+
} else {
|
|
1184
|
+
await spawnDetached('open', ['-R', resolved]);
|
|
1185
|
+
}
|
|
1186
|
+
} else if (platform === 'win32') {
|
|
1187
|
+
const stat = await fsPromises.stat(resolved);
|
|
1188
|
+
const escapedPath = resolved.replace(/'/g, "''");
|
|
1189
|
+
const explorerArg = stat.isDirectory() ? escapedPath : `/select,${escapedPath}`;
|
|
1190
|
+
const command = `Start-Process -FilePath explorer.exe -ArgumentList '${explorerArg}'`;
|
|
1191
|
+
await new Promise((resolve, reject) => {
|
|
1192
|
+
const child = spawn('powershell.exe', ['-NoProfile', '-NonInteractive', '-Command', command], {
|
|
1193
|
+
windowsHide: true,
|
|
1194
|
+
stdio: 'ignore',
|
|
1195
|
+
});
|
|
1196
|
+
child.once('error', reject);
|
|
1197
|
+
child.once('exit', (code) => {
|
|
1198
|
+
if (code === 0) {
|
|
1199
|
+
resolve();
|
|
1200
|
+
return;
|
|
1201
|
+
}
|
|
1202
|
+
reject(new Error(`Explorer launch failed with code ${code ?? 'unknown'}`));
|
|
1203
|
+
});
|
|
1204
|
+
});
|
|
1205
|
+
} else {
|
|
1206
|
+
const stat = await fsPromises.stat(resolved);
|
|
1207
|
+
const dir = stat.isDirectory() ? resolved : path.dirname(resolved);
|
|
1208
|
+
await spawnDetached('xdg-open', [dir]);
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1211
|
+
return res.json({ success: true, path: resolved });
|
|
1212
|
+
} catch (error) {
|
|
1213
|
+
const err = error;
|
|
1214
|
+
if (err && typeof err === 'object' && err.code === 'ENOENT') {
|
|
1215
|
+
return res.status(404).json({ error: 'Path not found' });
|
|
1216
|
+
}
|
|
1217
|
+
if (isOsPermissionError(err)) {
|
|
1218
|
+
return sendOsPermissionDenied(res, 'Access to path denied');
|
|
1219
|
+
}
|
|
1220
|
+
console.error('Failed to reveal path:', error);
|
|
1221
|
+
return res.status(500).json({ error: (error && error.message) || 'Failed to reveal path' });
|
|
1222
|
+
}
|
|
1223
|
+
});
|
|
1224
|
+
|
|
1225
|
+
app.post('/api/fs/pick-directory', async (req, res) => {
|
|
1226
|
+
const requestedPath = typeof req.body?.path === 'string' ? req.body.path.trim() : '';
|
|
1227
|
+
try {
|
|
1228
|
+
const picked = await pickDirectory({
|
|
1229
|
+
platform,
|
|
1230
|
+
spawn,
|
|
1231
|
+
defaultPath: requestedPath,
|
|
1232
|
+
});
|
|
1233
|
+
if (picked?.status === 'cancelled') {
|
|
1234
|
+
return res.json({ cancelled: true });
|
|
1235
|
+
}
|
|
1236
|
+
if (picked?.status === 'unavailable') {
|
|
1237
|
+
return res.status(501).json({ error: 'Folder picker is not available on this host.' });
|
|
1238
|
+
}
|
|
1239
|
+
if (picked?.status !== 'ok' || typeof picked.path !== 'string' || !picked.path.trim()) {
|
|
1240
|
+
return res.status(500).json({ error: picked?.error || 'Failed to pick directory' });
|
|
1241
|
+
}
|
|
1242
|
+
const resolved = path.resolve(picked.path.trim());
|
|
1243
|
+
const stats = await fsPromises.stat(resolved);
|
|
1244
|
+
if (!stats.isDirectory()) {
|
|
1245
|
+
return res.status(400).json({ error: 'Selected path is not a directory' });
|
|
1246
|
+
}
|
|
1247
|
+
return res.json({ path: resolved });
|
|
1248
|
+
} catch (error) {
|
|
1249
|
+
const err = error;
|
|
1250
|
+
if (err && typeof err === 'object' && err.code === 'ENOENT') {
|
|
1251
|
+
return res.status(404).json({ error: 'Path not found' });
|
|
1252
|
+
}
|
|
1253
|
+
if (isOsPermissionError(err)) {
|
|
1254
|
+
return sendOsPermissionDenied(res, 'Access to path denied');
|
|
1255
|
+
}
|
|
1256
|
+
console.error('Failed to pick directory:', error);
|
|
1257
|
+
return res.status(500).json({ error: (error && error.message) || 'Failed to pick directory' });
|
|
1258
|
+
}
|
|
1259
|
+
});
|
|
1260
|
+
|
|
1261
|
+
app.post('/api/fs/exec', async (req, res) => {
|
|
1262
|
+
const { commands, cwd, background } = req.body || {};
|
|
1263
|
+
if (!Array.isArray(commands) || commands.length === 0) {
|
|
1264
|
+
return res.status(400).json({ error: 'Commands array is required' });
|
|
1265
|
+
}
|
|
1266
|
+
if (!cwd || typeof cwd !== 'string') {
|
|
1267
|
+
return res.status(400).json({ error: 'Working directory (cwd) is required' });
|
|
1268
|
+
}
|
|
1269
|
+
|
|
1270
|
+
pruneExecJobs();
|
|
1271
|
+
pruneGitReadCache();
|
|
1272
|
+
|
|
1273
|
+
try {
|
|
1274
|
+
if (background === true) {
|
|
1275
|
+
console.warn('Rejected background /api/fs/exec request');
|
|
1276
|
+
return res.status(400).json({ error: 'Background command execution is not allowed' });
|
|
1277
|
+
}
|
|
1278
|
+
const resolvedCwdCandidate = path.resolve(normalizeDirectoryPath(cwd));
|
|
1279
|
+
const resolvedForWorkspace = await resolveWorkspacePathFromContext({
|
|
1280
|
+
req,
|
|
1281
|
+
targetPath: resolvedCwdCandidate,
|
|
1282
|
+
resolveProjectDirectory,
|
|
1283
|
+
path,
|
|
1284
|
+
os,
|
|
1285
|
+
normalizeDirectoryPath,
|
|
1286
|
+
pichamberUserConfigRoot,
|
|
1287
|
+
});
|
|
1288
|
+
if (!resolvedForWorkspace.ok) {
|
|
1289
|
+
console.warn(`Rejected /api/fs/exec outside workspace: ${resolvedForWorkspace.error}`);
|
|
1290
|
+
return res.status(403).json({ error: resolvedForWorkspace.error });
|
|
1291
|
+
}
|
|
1292
|
+
const resolvedCwd = resolvedForWorkspace.resolved;
|
|
1293
|
+
const stats = await fsPromises.stat(resolvedCwd);
|
|
1294
|
+
if (!stats.isDirectory()) {
|
|
1295
|
+
return res.status(400).json({ error: 'Specified cwd is not a directory' });
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1298
|
+
const shell = process.env.SHELL || (process.platform === 'win32' ? 'cmd.exe' : '/bin/sh');
|
|
1299
|
+
const shellFlag = process.platform === 'win32' ? '/c' : '-c';
|
|
1300
|
+
|
|
1301
|
+
const jobId = crypto.randomUUID();
|
|
1302
|
+
const job = {
|
|
1303
|
+
jobId,
|
|
1304
|
+
status: 'queued',
|
|
1305
|
+
success: null,
|
|
1306
|
+
commands,
|
|
1307
|
+
resolvedCwd,
|
|
1308
|
+
shell,
|
|
1309
|
+
shellFlag,
|
|
1310
|
+
results: [],
|
|
1311
|
+
startedAt: Date.now(),
|
|
1312
|
+
finishedAt: null,
|
|
1313
|
+
updatedAt: Date.now(),
|
|
1314
|
+
};
|
|
1315
|
+
|
|
1316
|
+
execJobs.set(jobId, job);
|
|
1317
|
+
|
|
1318
|
+
const isBackground = false;
|
|
1319
|
+
if (isBackground) {
|
|
1320
|
+
void runExecJob(job).catch((error) => {
|
|
1321
|
+
job.status = 'done';
|
|
1322
|
+
job.success = false;
|
|
1323
|
+
job.results = Array.isArray(job.results) ? job.results : [];
|
|
1324
|
+
job.results.push({
|
|
1325
|
+
command: '',
|
|
1326
|
+
success: false,
|
|
1327
|
+
error: (error && error.message) || 'Command execution failed',
|
|
1328
|
+
});
|
|
1329
|
+
job.finishedAt = Date.now();
|
|
1330
|
+
job.updatedAt = Date.now();
|
|
1331
|
+
});
|
|
1332
|
+
|
|
1333
|
+
return res.status(202).json({
|
|
1334
|
+
jobId,
|
|
1335
|
+
status: 'running',
|
|
1336
|
+
});
|
|
1337
|
+
}
|
|
1338
|
+
|
|
1339
|
+
await runExecJob(job);
|
|
1340
|
+
return res.json({
|
|
1341
|
+
jobId,
|
|
1342
|
+
status: job.status,
|
|
1343
|
+
success: job.success === true,
|
|
1344
|
+
results: job.results,
|
|
1345
|
+
});
|
|
1346
|
+
} catch (error) {
|
|
1347
|
+
console.error('Failed to execute commands:', error);
|
|
1348
|
+
return res.status(500).json({ error: (error && error.message) || 'Failed to execute commands' });
|
|
1349
|
+
}
|
|
1350
|
+
});
|
|
1351
|
+
|
|
1352
|
+
app.get('/api/fs/exec/:jobId', (req, res) => {
|
|
1353
|
+
const jobId = typeof req.params?.jobId === 'string' ? req.params.jobId : '';
|
|
1354
|
+
if (!jobId) {
|
|
1355
|
+
return res.status(400).json({ error: 'Job id is required' });
|
|
1356
|
+
}
|
|
1357
|
+
|
|
1358
|
+
pruneExecJobs();
|
|
1359
|
+
|
|
1360
|
+
const job = execJobs.get(jobId);
|
|
1361
|
+
if (!job) {
|
|
1362
|
+
return res.status(404).json({ error: 'Job not found' });
|
|
1363
|
+
}
|
|
1364
|
+
|
|
1365
|
+
job.updatedAt = Date.now();
|
|
1366
|
+
return res.json({
|
|
1367
|
+
jobId: job.jobId,
|
|
1368
|
+
status: job.status,
|
|
1369
|
+
success: job.success === true,
|
|
1370
|
+
results: Array.isArray(job.results) ? job.results : [],
|
|
1371
|
+
});
|
|
1372
|
+
});
|
|
1373
|
+
|
|
1374
|
+
app.get('/api/fs/list', async (req, res) => {
|
|
1375
|
+
const rawPath = typeof req.query.path === 'string' && req.query.path.trim().length > 0
|
|
1376
|
+
? req.query.path.trim()
|
|
1377
|
+
: os.homedir();
|
|
1378
|
+
const respectGitignore = req.query.respectGitignore === 'true';
|
|
1379
|
+
// Logical (requested) path stays in the caller's path space. Realpath is
|
|
1380
|
+
// only used to read directory contents — returning real paths for entries
|
|
1381
|
+
// breaks file-tree expansion when listing through a symlink, because the
|
|
1382
|
+
// UI rejects expanded paths that fall outside the workspace root.
|
|
1383
|
+
let requestedPath = '';
|
|
1384
|
+
let resolvedPath = '';
|
|
1385
|
+
|
|
1386
|
+
try {
|
|
1387
|
+
requestedPath = path.resolve(normalizeDirectoryPath(rawPath));
|
|
1388
|
+
resolvedPath = await realpathCache.resolve(requestedPath);
|
|
1389
|
+
|
|
1390
|
+
const stats = await fsPromises.stat(resolvedPath);
|
|
1391
|
+
if (!stats.isDirectory()) {
|
|
1392
|
+
return res.status(400).json({ error: 'Specified path is not a directory', reason: 'not-directory' });
|
|
1393
|
+
}
|
|
1394
|
+
|
|
1395
|
+
const dirents = await fsPromises.readdir(resolvedPath, { withFileTypes: true });
|
|
1396
|
+
let ignoredPaths = new Set();
|
|
1397
|
+
if (respectGitignore) {
|
|
1398
|
+
try {
|
|
1399
|
+
const pathsToCheck = dirents.map((d) => d.name);
|
|
1400
|
+
if (pathsToCheck.length > 0) {
|
|
1401
|
+
try {
|
|
1402
|
+
const result = await new Promise((resolve) => {
|
|
1403
|
+
const child = spawn(resolveGitBinaryForSpawn(), ['check-ignore', '--', ...pathsToCheck], {
|
|
1404
|
+
cwd: resolvedPath,
|
|
1405
|
+
windowsHide: true,
|
|
1406
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
1407
|
+
});
|
|
1408
|
+
|
|
1409
|
+
let stdout = '';
|
|
1410
|
+
let settled = false;
|
|
1411
|
+
let timeout = null;
|
|
1412
|
+
const finish = (value) => {
|
|
1413
|
+
if (settled) return;
|
|
1414
|
+
settled = true;
|
|
1415
|
+
if (timeout) clearTimeout(timeout);
|
|
1416
|
+
resolve(value);
|
|
1417
|
+
};
|
|
1418
|
+
|
|
1419
|
+
if (gitCheckIgnoreTimeoutMs > 0) {
|
|
1420
|
+
timeout = setTimeout(() => {
|
|
1421
|
+
try {
|
|
1422
|
+
child.kill('SIGKILL');
|
|
1423
|
+
} catch {
|
|
1424
|
+
}
|
|
1425
|
+
finish('');
|
|
1426
|
+
}, gitCheckIgnoreTimeoutMs);
|
|
1427
|
+
}
|
|
1428
|
+
|
|
1429
|
+
child.stdout.on('data', (data) => { stdout += data.toString(); });
|
|
1430
|
+
child.on('close', () => finish(stdout));
|
|
1431
|
+
child.on('error', () => finish(''));
|
|
1432
|
+
});
|
|
1433
|
+
|
|
1434
|
+
result.split('\n').filter(Boolean).forEach((name) => {
|
|
1435
|
+
const fullPath = path.join(resolvedPath, name.trim());
|
|
1436
|
+
ignoredPaths.add(fullPath);
|
|
1437
|
+
});
|
|
1438
|
+
} catch {
|
|
1439
|
+
}
|
|
1440
|
+
}
|
|
1441
|
+
} catch {
|
|
1442
|
+
}
|
|
1443
|
+
}
|
|
1444
|
+
|
|
1445
|
+
const entries = await Promise.all(
|
|
1446
|
+
dirents.map(async (dirent) => {
|
|
1447
|
+
const physicalEntryPath = path.join(resolvedPath, dirent.name);
|
|
1448
|
+
if (respectGitignore && ignoredPaths.has(physicalEntryPath)) {
|
|
1449
|
+
return null;
|
|
1450
|
+
}
|
|
1451
|
+
|
|
1452
|
+
let isDirectory = dirent.isDirectory();
|
|
1453
|
+
const isSymbolicLink = dirent.isSymbolicLink();
|
|
1454
|
+
|
|
1455
|
+
if (!isDirectory && isSymbolicLink) {
|
|
1456
|
+
try {
|
|
1457
|
+
const linkStats = await fsPromises.stat(physicalEntryPath);
|
|
1458
|
+
isDirectory = linkStats.isDirectory();
|
|
1459
|
+
} catch {
|
|
1460
|
+
isDirectory = false;
|
|
1461
|
+
}
|
|
1462
|
+
}
|
|
1463
|
+
|
|
1464
|
+
return {
|
|
1465
|
+
name: dirent.name,
|
|
1466
|
+
path: path.join(requestedPath, dirent.name),
|
|
1467
|
+
isDirectory,
|
|
1468
|
+
isFile: dirent.isFile(),
|
|
1469
|
+
isSymbolicLink,
|
|
1470
|
+
};
|
|
1471
|
+
})
|
|
1472
|
+
);
|
|
1473
|
+
|
|
1474
|
+
return res.json({
|
|
1475
|
+
path: requestedPath,
|
|
1476
|
+
entries: entries.filter(Boolean),
|
|
1477
|
+
});
|
|
1478
|
+
} catch (error) {
|
|
1479
|
+
const err = error;
|
|
1480
|
+
const code = err && typeof err === 'object' && 'code' in err ? err.code : undefined;
|
|
1481
|
+
if (code !== 'ENOENT') {
|
|
1482
|
+
console.error('Failed to list directory:', error);
|
|
1483
|
+
}
|
|
1484
|
+
if (code === 'ENOENT') {
|
|
1485
|
+
return res.status(404).json({ error: 'Directory not found', reason: 'not-found' });
|
|
1486
|
+
}
|
|
1487
|
+
if (isOsPermissionError(err)) {
|
|
1488
|
+
return sendOsPermissionDenied(res, 'Access to directory denied');
|
|
1489
|
+
}
|
|
1490
|
+
return res.status(500).json({ error: (error && error.message) || 'Failed to list directory' });
|
|
1491
|
+
}
|
|
1492
|
+
});
|
|
1493
|
+
|
|
1494
|
+
app.get('/api/fs/find', async (req, res) => {
|
|
1495
|
+
const rawPath = typeof req.query.directory === 'string' ? req.query.directory : typeof req.query.path === 'string' ? req.query.path : '';
|
|
1496
|
+
if (!rawPath.trim()) return res.status(400).json({ error: 'Directory path is required' });
|
|
1497
|
+
|
|
1498
|
+
try {
|
|
1499
|
+
const { canonicalPath } = await resolveWorkspacePath(rawPath, { scope: 'read' });
|
|
1500
|
+
const query = typeof req.query.query === 'string' ? req.query.query : '';
|
|
1501
|
+
const limit = Number.isSafeInteger(Number(req.query.limit)) ? Math.min(Number(req.query.limit), 500) : 60;
|
|
1502
|
+
const includeHidden = req.query.includeHidden === 'true';
|
|
1503
|
+
const respectGitignore = req.query.respectGitignore !== 'false';
|
|
1504
|
+
const type = req.query.type === 'directory' ? 'directory' : req.query.type === 'file' ? 'file' : undefined;
|
|
1505
|
+
|
|
1506
|
+
const fsSearchRuntime = createFsSearchRuntime({ fsPromises, path, spawn, resolveGitBinaryForSpawn });
|
|
1507
|
+
const rawHits = await fsSearchRuntime.searchFilesystemFiles(canonicalPath, {
|
|
1508
|
+
query,
|
|
1509
|
+
limit,
|
|
1510
|
+
includeHidden,
|
|
1511
|
+
respectGitignore,
|
|
1512
|
+
});
|
|
1513
|
+
|
|
1514
|
+
const files = rawHits
|
|
1515
|
+
.filter((hit) => {
|
|
1516
|
+
if (type === 'file' && hit.isDir) return false;
|
|
1517
|
+
if (type === 'directory' && !hit.isDir) return false;
|
|
1518
|
+
return true;
|
|
1519
|
+
})
|
|
1520
|
+
.map((hit) => {
|
|
1521
|
+
const hitPath = hit.path.replace(/\\/g, '/');
|
|
1522
|
+
const rootNorm = canonicalPath.replace(/\\/g, '/');
|
|
1523
|
+
const relativePath = hitPath.startsWith(rootNorm)
|
|
1524
|
+
? hitPath.slice(rootNorm.length).replace(/^\//, '')
|
|
1525
|
+
: path.basename(hitPath);
|
|
1526
|
+
const name = path.basename(hitPath);
|
|
1527
|
+
return {
|
|
1528
|
+
name,
|
|
1529
|
+
path: hitPath,
|
|
1530
|
+
relativePath,
|
|
1531
|
+
...(name.includes('.') ? { extension: name.split('.').pop().toLowerCase() } : {}),
|
|
1532
|
+
};
|
|
1533
|
+
});
|
|
1534
|
+
|
|
1535
|
+
return res.json({ files });
|
|
1536
|
+
} catch (error) {
|
|
1537
|
+
if (error && error.code === 'WORKSPACE_ACCESS_DENIED') {
|
|
1538
|
+
return sendWorkspaceAccessDenied(res, 'Access to directory denied');
|
|
1539
|
+
}
|
|
1540
|
+
return res.status(500).json({ error: (error && error.message) || 'Failed to search files' });
|
|
1541
|
+
}
|
|
1542
|
+
});
|
|
1543
|
+
};
|