auvezy-terminal-remote 0.7.6 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +1962 -296
- package/frontend-dist/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
- package/frontend-dist/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
- package/frontend-dist/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
- package/frontend-dist/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
- package/frontend-dist/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
- package/frontend-dist/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
- package/frontend-dist/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
- package/frontend-dist/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
- package/frontend-dist/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
- package/frontend-dist/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
- package/frontend-dist/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
- package/frontend-dist/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
- package/frontend-dist/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
- package/frontend-dist/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
- package/frontend-dist/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
- package/frontend-dist/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
- package/frontend-dist/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
- package/frontend-dist/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
- package/frontend-dist/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
- package/frontend-dist/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
- package/frontend-dist/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
- package/frontend-dist/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
- package/frontend-dist/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
- package/frontend-dist/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
- package/frontend-dist/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
- package/frontend-dist/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
- package/frontend-dist/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
- package/frontend-dist/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
- package/frontend-dist/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
- package/frontend-dist/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
- package/frontend-dist/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
- package/frontend-dist/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
- package/frontend-dist/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
- package/frontend-dist/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
- package/frontend-dist/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
- package/frontend-dist/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
- package/frontend-dist/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
- package/frontend-dist/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
- package/frontend-dist/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
- package/frontend-dist/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
- package/frontend-dist/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
- package/frontend-dist/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
- package/frontend-dist/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
- package/frontend-dist/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
- package/frontend-dist/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
- package/frontend-dist/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
- package/frontend-dist/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
- package/frontend-dist/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
- package/frontend-dist/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
- package/frontend-dist/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
- package/frontend-dist/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
- package/frontend-dist/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
- package/frontend-dist/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
- package/frontend-dist/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
- package/frontend-dist/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
- package/frontend-dist/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
- package/frontend-dist/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
- package/frontend-dist/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
- package/frontend-dist/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
- package/frontend-dist/assets/MarkdownPreview-BhHx4UZB.js +294 -0
- package/frontend-dist/assets/MarkdownPreview-UDCyb9Vn.css +1 -0
- package/frontend-dist/assets/abap-CLvhMVsD.js +1 -0
- package/frontend-dist/assets/actionscript-3--17pq3dv.js +1 -0
- package/frontend-dist/assets/ada-C5qYipkI.js +1 -0
- package/frontend-dist/assets/andromeeda-vGVdxbeo.js +1 -0
- package/frontend-dist/assets/angular-html-DgjvnXWT.js +1 -0
- package/frontend-dist/assets/angular-ts-BsJfg-C9.js +1 -0
- package/frontend-dist/assets/apache-U0d_L8uA.js +1 -0
- package/frontend-dist/assets/apex-VAyPSnFM.js +1 -0
- package/frontend-dist/assets/apl-owRgUhC0.js +1 -0
- package/frontend-dist/assets/applescript-CCn79oCD.js +1 -0
- package/frontend-dist/assets/ara-4CJ0cIlV.js +1 -0
- package/frontend-dist/assets/asciidoc-DE70LPWp.js +1 -0
- package/frontend-dist/assets/asm-Cmm7eHzH.js +1 -0
- package/frontend-dist/assets/astro-ByvNTrWi.js +1 -0
- package/frontend-dist/assets/aurora-x-CDeNXAV0.js +1 -0
- package/frontend-dist/assets/awk-BWXHIvNe.js +1 -0
- package/frontend-dist/assets/ayu-dark-DluEY0Gj.js +1 -0
- package/frontend-dist/assets/ayu-light-C3h-C4tm.js +1 -0
- package/frontend-dist/assets/ayu-mirage-Bqwy1Gya.js +1 -0
- package/frontend-dist/assets/ballerina-B7ZEbQpA.js +1 -0
- package/frontend-dist/assets/bat-Bo4NYOV-.js +1 -0
- package/frontend-dist/assets/beancount-D-usSTwE.js +1 -0
- package/frontend-dist/assets/berry-DKpUyyne.js +1 -0
- package/frontend-dist/assets/bibtex-Ci_nEsc7.js +1 -0
- package/frontend-dist/assets/bicep-CUHmPFLl.js +1 -0
- package/frontend-dist/assets/bird2-C6vDhewU.js +1 -0
- package/frontend-dist/assets/blade-DBB6j2sS.js +1 -0
- package/frontend-dist/assets/bsl-BkkzgIyY.js +1 -0
- package/frontend-dist/assets/c-Ba6e6JWz.js +1 -0
- package/frontend-dist/assets/c3-BFHwR3_K.js +1 -0
- package/frontend-dist/assets/cadence-CQ2zXKGN.js +1 -0
- package/frontend-dist/assets/cairo-DLTphjLi.js +1 -0
- package/frontend-dist/assets/catppuccin-frappe-3VR1Za6u.js +1 -0
- package/frontend-dist/assets/catppuccin-latte-DwIHMF0Q.js +1 -0
- package/frontend-dist/assets/catppuccin-macchiato-DYnBP6_5.js +1 -0
- package/frontend-dist/assets/catppuccin-mocha-DYhrFGRu.js +1 -0
- package/frontend-dist/assets/clarity-SemFz856.js +1 -0
- package/frontend-dist/assets/clojure-DqKBuwfJ.js +1 -0
- package/frontend-dist/assets/cmake-Bj61d0ZC.js +1 -0
- package/frontend-dist/assets/cobol-DyYl4Dsc.js +1 -0
- package/frontend-dist/assets/codeowners-C8r90Shi.js +1 -0
- package/frontend-dist/assets/codeql-oeQT6MSM.js +1 -0
- package/frontend-dist/assets/coffee-GDwlIkMr.js +1 -0
- package/frontend-dist/assets/common-lisp-Cv5bFMCO.js +1 -0
- package/frontend-dist/assets/coq-BrsZFFmf.js +1 -0
- package/frontend-dist/assets/cpp-DdEoNrsu.js +1 -0
- package/frontend-dist/assets/crystal-CsRfpFgZ.js +1 -0
- package/frontend-dist/assets/csharp-oqKa8noW.js +1 -0
- package/frontend-dist/assets/css-EcyqDbrn.js +1 -0
- package/frontend-dist/assets/csv-Dx-8-gkx.js +1 -0
- package/frontend-dist/assets/cue-CE9AQfxI.js +1 -0
- package/frontend-dist/assets/cypher-ClKdZ_lG.js +1 -0
- package/frontend-dist/assets/d-qD-0Kul2.js +1 -0
- package/frontend-dist/assets/dark-plus-Cs2F2srj.js +1 -0
- package/frontend-dist/assets/dart-CnvKMtbv.js +1 -0
- package/frontend-dist/assets/dax-BkyTk9wS.js +1 -0
- package/frontend-dist/assets/desktop-Dlh5hvp9.js +1 -0
- package/frontend-dist/assets/diff-woXpYk--.js +1 -0
- package/frontend-dist/assets/dist-BFglDfWz.js +153 -0
- package/frontend-dist/assets/docker-IyjqRm3v.js +1 -0
- package/frontend-dist/assets/dotenv-_5a1GRtc.js +1 -0
- package/frontend-dist/assets/dracula-BHWKrbxM.js +1 -0
- package/frontend-dist/assets/dracula-soft-5eyTD99u.js +1 -0
- package/frontend-dist/assets/dream-maker-DW3nJb8Q.js +1 -0
- package/frontend-dist/assets/edge-CXwacAJy.js +1 -0
- package/frontend-dist/assets/elixir-BPtzsf8i.js +1 -0
- package/frontend-dist/assets/elm-yyrGQdOF.js +1 -0
- package/frontend-dist/assets/emacs-lisp-B4R74twV.js +1 -0
- package/frontend-dist/assets/embed-ClpqVPsk.js +2 -0
- package/frontend-dist/assets/embed-JyQKJCHx.css +1 -0
- package/frontend-dist/assets/erb-CPVygpcZ.js +1 -0
- package/frontend-dist/assets/erlang-Cphh6RMH.js +1 -0
- package/frontend-dist/assets/eruda-tTLAveCP.js +366 -0
- package/frontend-dist/assets/everforest-dark-sB-x3p7T.js +1 -0
- package/frontend-dist/assets/everforest-light-Df2xbC6M.js +1 -0
- package/frontend-dist/assets/fennel-DQxkIbk2.js +1 -0
- package/frontend-dist/assets/fish-BJitypiv.js +1 -0
- package/frontend-dist/assets/fluent-C03EYrpw.js +1 -0
- package/frontend-dist/assets/fortran-fixed-form-DEKoE2YW.js +1 -0
- package/frontend-dist/assets/fortran-free-form-CYNrtFtB.js +1 -0
- package/frontend-dist/assets/fsharp-D13ZGOAj.js +1 -0
- package/frontend-dist/assets/gdresource-C0sCabJj.js +1 -0
- package/frontend-dist/assets/gdscript-Cp2uCuqX.js +1 -0
- package/frontend-dist/assets/gdshader-CBce3t8t.js +1 -0
- package/frontend-dist/assets/genie-CV2tkWYe.js +1 -0
- package/frontend-dist/assets/gherkin-DExj1W_8.js +1 -0
- package/frontend-dist/assets/git-commit-BSykSTBG.js +1 -0
- package/frontend-dist/assets/git-rebase-BxSEtpsP.js +1 -0
- package/frontend-dist/assets/github-dark-C-LZuMrd.js +1 -0
- package/frontend-dist/assets/github-dark-default-DXG-b-1a.js +1 -0
- package/frontend-dist/assets/github-dark-dimmed-Bx1FflLF.js +1 -0
- package/frontend-dist/assets/github-dark-high-contrast-B_tTalzw.js +1 -0
- package/frontend-dist/assets/github-light-EUqPIrTm.js +1 -0
- package/frontend-dist/assets/github-light-default-BXViO-2h.js +1 -0
- package/frontend-dist/assets/github-light-high-contrast-B68TUdTA.js +1 -0
- package/frontend-dist/assets/gleam-CSRkHgEL.js +1 -0
- package/frontend-dist/assets/glimmer-js-CEhle51e.js +1 -0
- package/frontend-dist/assets/glimmer-ts-DyxDXCrd.js +1 -0
- package/frontend-dist/assets/glsl-CZ8TD0B7.js +1 -0
- package/frontend-dist/assets/gn-ilITqXS6.js +1 -0
- package/frontend-dist/assets/gnuplot-7GGW24-e.js +1 -0
- package/frontend-dist/assets/go-rLFTqkRN.js +1 -0
- package/frontend-dist/assets/graphql-BUs3a1dy.js +1 -0
- package/frontend-dist/assets/groovy-CacY0gHj.js +1 -0
- package/frontend-dist/assets/gruvbox-dark-hard-C820rvS2.js +1 -0
- package/frontend-dist/assets/gruvbox-dark-medium-BPjhmG05.js +1 -0
- package/frontend-dist/assets/gruvbox-dark-soft-MrdJrrXF.js +1 -0
- package/frontend-dist/assets/gruvbox-light-hard-BC_s9l72.js +1 -0
- package/frontend-dist/assets/gruvbox-light-medium-BAWPOn9u.js +1 -0
- package/frontend-dist/assets/gruvbox-light-soft-BSMLrYjP.js +1 -0
- package/frontend-dist/assets/hack-DKo9X4Fh.js +1 -0
- package/frontend-dist/assets/haml-B8slzfN1.js +1 -0
- package/frontend-dist/assets/handlebars-YbFocell.js +1 -0
- package/frontend-dist/assets/haskell-D8IpX4py.js +1 -0
- package/frontend-dist/assets/haxe-OTjmBuCE.js +1 -0
- package/frontend-dist/assets/hcl-Dh228itO.js +1 -0
- package/frontend-dist/assets/hjson-CxZEssPk.js +1 -0
- package/frontend-dist/assets/hlsl-Cvrh5tZx.js +1 -0
- package/frontend-dist/assets/horizon-CE9ld1lL.js +1 -0
- package/frontend-dist/assets/horizon-bright-DSNQnXHK.js +1 -0
- package/frontend-dist/assets/houston-CsvMBhTu.js +1 -0
- package/frontend-dist/assets/html-D2GTk50q.js +1 -0
- package/frontend-dist/assets/html-derivative-D_7h6AG3.js +1 -0
- package/frontend-dist/assets/html-void-elements-DdRmDp70.js +1 -0
- package/frontend-dist/assets/http-D3rMdaas.js +1 -0
- package/frontend-dist/assets/hurl-Am2W_EDh.js +1 -0
- package/frontend-dist/assets/hxml-B0Qn7Nwc.js +1 -0
- package/frontend-dist/assets/hy-CZbG8q4J.js +1 -0
- package/frontend-dist/assets/imba-DsUTQ-LC.js +1 -0
- package/frontend-dist/assets/index-BVlpHV7C.css +1 -0
- package/frontend-dist/assets/index-DTdbmAVn.js +147 -0
- package/frontend-dist/assets/ini-B5eOa1yu.js +1 -0
- package/frontend-dist/assets/java-tbpyqHjD.js +1 -0
- package/frontend-dist/assets/javascript-KWFIoO93.js +1 -0
- package/frontend-dist/assets/jinja-DDB6tGEL.js +1 -0
- package/frontend-dist/assets/jison-BGkRNAgP.js +1 -0
- package/frontend-dist/assets/json-xmxrlNIL.js +1 -0
- package/frontend-dist/assets/json5-BR5RXkoi.js +1 -0
- package/frontend-dist/assets/jsonc-CYpm1nAK.js +1 -0
- package/frontend-dist/assets/jsonl-CmCQp5Yx.js +1 -0
- package/frontend-dist/assets/jsonnet-CJTPZ8u_.js +1 -0
- package/frontend-dist/assets/jssm-DXw9l8Rf.js +1 -0
- package/frontend-dist/assets/jsx-COMlcASR.js +1 -0
- package/frontend-dist/assets/julia-BhR0UkEv.js +1 -0
- package/frontend-dist/assets/just-DXihdLqs.js +1 -0
- package/frontend-dist/assets/kanagawa-dragon-CXtmUGW6.js +1 -0
- package/frontend-dist/assets/kanagawa-lotus-BN08jTvb.js +1 -0
- package/frontend-dist/assets/kanagawa-wave-CTweb8Dz.js +1 -0
- package/frontend-dist/assets/kdl-CsD5j6eV.js +1 -0
- package/frontend-dist/assets/kotlin-DhhofPvG.js +1 -0
- package/frontend-dist/assets/kusto-C7mF5XQf.js +1 -0
- package/frontend-dist/assets/laserwave-C_8bwKvT.js +1 -0
- package/frontend-dist/assets/latex-Bpu5-lW-.js +1 -0
- package/frontend-dist/assets/lean-CewbzKMR.js +1 -0
- package/frontend-dist/assets/less-DVTAwKKz.js +1 -0
- package/frontend-dist/assets/light-plus-DVQuIRkW.js +1 -0
- package/frontend-dist/assets/liquid-BJGa57Gs.js +1 -0
- package/frontend-dist/assets/llvm-Cm23YOpf.js +1 -0
- package/frontend-dist/assets/log-BNLmms1o.js +1 -0
- package/frontend-dist/assets/logo-Cluzi2Zq.js +1 -0
- package/frontend-dist/assets/lua-Bd-3NA7e.js +1 -0
- package/frontend-dist/assets/luau-FMPmPwt6.js +1 -0
- package/frontend-dist/assets/make-Dixweg8N.js +1 -0
- package/frontend-dist/assets/markdown-BYOwaDjH.js +1 -0
- package/frontend-dist/assets/marko-CZd96liA.js +1 -0
- package/frontend-dist/assets/material-theme-Bm3Qr25_.js +1 -0
- package/frontend-dist/assets/material-theme-darker-2IIEA8gg.js +1 -0
- package/frontend-dist/assets/material-theme-lighter-uhdI0v04.js +1 -0
- package/frontend-dist/assets/material-theme-ocean-CHQ94UKr.js +1 -0
- package/frontend-dist/assets/material-theme-palenight-B5W6OYN7.js +1 -0
- package/frontend-dist/assets/matlab-D7qyCx1q.js +1 -0
- package/frontend-dist/assets/mdc-C-cIFcg2.js +1 -0
- package/frontend-dist/assets/mdx-DQZ5AkYe.js +1 -0
- package/frontend-dist/assets/mermaid-Bk4SNUv9.js +1 -0
- package/frontend-dist/assets/min-dark-BSWPekZh.js +1 -0
- package/frontend-dist/assets/min-light-DDpmG2fV.js +1 -0
- package/frontend-dist/assets/mipsasm-BMqwQI7S.js +1 -0
- package/frontend-dist/assets/mojo-BgCJLMeH.js +1 -0
- package/frontend-dist/assets/monokai-CdkpiU2Y.js +1 -0
- package/frontend-dist/assets/moonbit-CaWjb8XO.js +1 -0
- package/frontend-dist/assets/move-B1IS1UjX.js +1 -0
- package/frontend-dist/assets/narrat-_X_XdTYD.js +1 -0
- package/frontend-dist/assets/nextflow-BJtWHP5T.js +1 -0
- package/frontend-dist/assets/nextflow-groovy-DJMQeKeT.js +1 -0
- package/frontend-dist/assets/nginx-BkfQ7PRQ.js +1 -0
- package/frontend-dist/assets/night-owl-DhmEMT88.js +1 -0
- package/frontend-dist/assets/night-owl-light-eJ-hLW7d.js +1 -0
- package/frontend-dist/assets/nim-BlcSihrU.js +1 -0
- package/frontend-dist/assets/nix-IvuFDN5E.js +1 -0
- package/frontend-dist/assets/nord-Cb4Vim4T.js +1 -0
- package/frontend-dist/assets/nushell-DcLAeLz5.js +1 -0
- package/frontend-dist/assets/objective-c-D1A_Heim.js +1 -0
- package/frontend-dist/assets/objective-cpp-BsSzOQcm.js +1 -0
- package/frontend-dist/assets/obsidian-Bopioi3a.css +1 -0
- package/frontend-dist/assets/obsidian-D8GOht-S.js +36 -0
- package/frontend-dist/assets/ocaml-O90oeIOV.js +1 -0
- package/frontend-dist/assets/odin-B1RWQWA5.js +1 -0
- package/frontend-dist/assets/one-dark-pro-CLwyXe_n.js +1 -0
- package/frontend-dist/assets/one-light-D7Lr4KcI.js +1 -0
- package/frontend-dist/assets/openscad-BUDT5pXO.js +1 -0
- package/frontend-dist/assets/pascal-4ZHwLPI5.js +1 -0
- package/frontend-dist/assets/perl-BK1yALmX.js +1 -0
- package/frontend-dist/assets/php-IgnnyOWG.js +1 -0
- package/frontend-dist/assets/pkl-ot-7Btpt.js +1 -0
- package/frontend-dist/assets/plastic-DQwYfKfQ.js +1 -0
- package/frontend-dist/assets/plsql-DGHpHOYJ.js +1 -0
- package/frontend-dist/assets/po-BiJDBrnU.js +1 -0
- package/frontend-dist/assets/poimandres-DRFjx7u4.js +1 -0
- package/frontend-dist/assets/polar-C7UOKdEL.js +1 -0
- package/frontend-dist/assets/postcss-BXeXVLqQ.js +1 -0
- package/frontend-dist/assets/powerquery-DNMTfnFr.js +1 -0
- package/frontend-dist/assets/powershell-DshXNtvi.js +1 -0
- package/frontend-dist/assets/prisma-BsRQq5mF.js +1 -0
- package/frontend-dist/assets/prolog-iXnhIJG7.js +1 -0
- package/frontend-dist/assets/proto-DB4EqR-F.js +1 -0
- package/frontend-dist/assets/pug-CDPGgwiQ.js +1 -0
- package/frontend-dist/assets/puppet-CDv2pdJW.js +1 -0
- package/frontend-dist/assets/purescript-9MfHhQsQ.js +1 -0
- package/frontend-dist/assets/python-gzcpVVnB.js +1 -0
- package/frontend-dist/assets/qml-BJ6xksc_.js +1 -0
- package/frontend-dist/assets/qmldir-DCQb3MpD.js +1 -0
- package/frontend-dist/assets/qss-Fe1Jh2GI.js +1 -0
- package/frontend-dist/assets/r-CtvlrTg3.js +1 -0
- package/frontend-dist/assets/racket-DcIDlBhZ.js +1 -0
- package/frontend-dist/assets/raku-B3gFvitq.js +1 -0
- package/frontend-dist/assets/razor-Dk6lMfGZ.js +1 -0
- package/frontend-dist/assets/red-CJ3rzSJv.js +1 -0
- package/frontend-dist/assets/reg-CRGYupPL.js +1 -0
- package/frontend-dist/assets/regexp-CCpLx9Kn.js +1 -0
- package/frontend-dist/assets/rel-BtDbiS_P.js +1 -0
- package/frontend-dist/assets/riscv-Ckw8ddFX.js +1 -0
- package/frontend-dist/assets/ron-VUp2lXgN.js +1 -0
- package/frontend-dist/assets/rose-pine-BthvhNj6.js +1 -0
- package/frontend-dist/assets/rose-pine-dawn-Dg85fqjY.js +1 -0
- package/frontend-dist/assets/rose-pine-moon-hon4tzzS.js +1 -0
- package/frontend-dist/assets/rosmsg-CAekHB0j.js +1 -0
- package/frontend-dist/assets/rst-DcQF2cud.js +1 -0
- package/frontend-dist/assets/ruby-B_xe_SR9.js +1 -0
- package/frontend-dist/assets/rust-Cfkwpbl8.js +1 -0
- package/frontend-dist/assets/sas-q7tt0uOK.js +1 -0
- package/frontend-dist/assets/sass-DXrisJhu.js +1 -0
- package/frontend-dist/assets/scala-DKOlJaKm.js +1 -0
- package/frontend-dist/assets/scheme-DQCgrYNe.js +1 -0
- package/frontend-dist/assets/scss-tURtktQv.js +1 -0
- package/frontend-dist/assets/sdbl-bTVj8UrX.js +1 -0
- package/frontend-dist/assets/shaderlab-TOUzSsQk.js +1 -0
- package/frontend-dist/assets/shellscript-qT25lAcj.js +1 -0
- package/frontend-dist/assets/shellsession-CMm3mW_a.js +1 -0
- package/frontend-dist/assets/slack-dark-DnToyrRv.js +1 -0
- package/frontend-dist/assets/slack-ochin-B2OO5cIa.js +1 -0
- package/frontend-dist/assets/smalltalk-B16xEiuN.js +1 -0
- package/frontend-dist/assets/snazzy-light-4G7pJPwS.js +1 -0
- package/frontend-dist/assets/solarized-dark-DV17i1UV.js +1 -0
- package/frontend-dist/assets/solarized-light-DSh2HLQt.js +1 -0
- package/frontend-dist/assets/solidity-CKzVLygQ.js +1 -0
- package/frontend-dist/assets/soy-Bu8dHwN_.js +1 -0
- package/frontend-dist/assets/sparql-D_iOobhT.js +1 -0
- package/frontend-dist/assets/splunk-BC2Px7Mm.js +1 -0
- package/frontend-dist/assets/sql-CD4zZmh-.js +1 -0
- package/frontend-dist/assets/ssh-config-BgfXC-Er.js +1 -0
- package/frontend-dist/assets/stata-BSr4Y6xK.js +1 -0
- package/frontend-dist/assets/stylus-B6D30XZt.js +1 -0
- package/frontend-dist/assets/surrealql-BdjWJBSn.js +1 -0
- package/frontend-dist/assets/svelte-BXGL560Z.js +1 -0
- package/frontend-dist/assets/swift-DonLKvLd.js +1 -0
- package/frontend-dist/assets/synthwave-84-nFMaYfgc.js +1 -0
- package/frontend-dist/assets/system-verilog-DJ5XKQeo.js +1 -0
- package/frontend-dist/assets/systemd-BxMlprV5.js +1 -0
- package/frontend-dist/assets/talonscript-CohzipZa.js +1 -0
- package/frontend-dist/assets/tasl-DMoTqEGO.js +1 -0
- package/frontend-dist/assets/tcl-CZd0xW_V.js +1 -0
- package/frontend-dist/assets/templ-B39rZ3Mc.js +1 -0
- package/frontend-dist/assets/terraform-DswuEJGm.js +1 -0
- package/frontend-dist/assets/tex-MbZL_Em8.js +1 -0
- package/frontend-dist/assets/tokyo-night-oM2G3aXe.js +1 -0
- package/frontend-dist/assets/toml-CcmNWLt0.js +1 -0
- package/frontend-dist/assets/ts-tags-BmAVQYXr.js +1 -0
- package/frontend-dist/assets/tsv-sltzmVWM.js +1 -0
- package/frontend-dist/assets/tsx-ZMGqoGTx.js +1 -0
- package/frontend-dist/assets/turtle-ByJddavk.js +1 -0
- package/frontend-dist/assets/twig-DreoDQSs.js +1 -0
- package/frontend-dist/assets/typescript-BpBfIzrI.js +1 -0
- package/frontend-dist/assets/typespec-BRdr0IET.js +1 -0
- package/frontend-dist/assets/typst-DI99ib-x.js +1 -0
- package/frontend-dist/assets/v-DETTlOr0.js +1 -0
- package/frontend-dist/assets/vala-zf12oZj6.js +1 -0
- package/frontend-dist/assets/vb-Djn5o6TS.js +1 -0
- package/frontend-dist/assets/verilog-CiiDBU1e.js +1 -0
- package/frontend-dist/assets/vesper-DdrHHSXu.js +1 -0
- package/frontend-dist/assets/vhdl-BroJfC0k.js +1 -0
- package/frontend-dist/assets/viml-DvXPmvsu.js +1 -0
- package/frontend-dist/assets/vitesse-black-fwtXNY1n.js +1 -0
- package/frontend-dist/assets/vitesse-dark-BZCL-v6S.js +1 -0
- package/frontend-dist/assets/vitesse-light-VbXTXTou.js +1 -0
- package/frontend-dist/assets/vue-DynNEKHy.js +1 -0
- package/frontend-dist/assets/vue-html-Cku1G804.js +1 -0
- package/frontend-dist/assets/vue-vine-wge2CQTq.js +1 -0
- package/frontend-dist/assets/vyper-CgoNMtux.js +1 -0
- package/frontend-dist/assets/wasm-BnjxR4X6.js +1 -0
- package/frontend-dist/assets/wasm-ByWQv1Qj.js +1 -0
- package/frontend-dist/assets/wenyan-C8pVoKbM.js +1 -0
- package/frontend-dist/assets/wgsl-BsKzXJz4.js +1 -0
- package/frontend-dist/assets/wikitext-ClFFjSW2.js +1 -0
- package/frontend-dist/assets/wit-DdvCle-K.js +1 -0
- package/frontend-dist/assets/wolfram-DLL8P-h_.js +1 -0
- package/frontend-dist/assets/xml-Ff0rnvcF.js +1 -0
- package/frontend-dist/assets/xsl-dRWQEowe.js +1 -0
- package/frontend-dist/assets/yaml-BxrA6rU3.js +1 -0
- package/frontend-dist/assets/zenscript-BnlCZFoB.js +1 -0
- package/frontend-dist/assets/zig-CMLA9XwU.js +1 -0
- package/frontend-dist/index.html +2 -2
- package/frontend-dist/manifest.webmanifest +1 -1
- package/frontend-dist/sw.js +1 -2
- package/package.json +1 -1
- package/frontend-dist/assets/eruda-Chf2H6-5.js +0 -366
- package/frontend-dist/assets/index-B7bzacGC.css +0 -32
- package/frontend-dist/assets/index-DkZHNqNf.js +0 -359
package/dist/cli.js
CHANGED
|
@@ -75,30 +75,43 @@ function ensureDefaultUserConfig(input) {
|
|
|
75
75
|
const workdirAllow = normalizeStringArray(src.workdirAllow);
|
|
76
76
|
const workdirDeny = src.workdirDeny === void 0 || !Array.isArray(src.workdirDeny) ? [...DEFAULT_WORKDIR_DENY] : normalizeStringArray(src.workdirDeny) ?? [];
|
|
77
77
|
const rawDisplay = src.display;
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
let fontSizeMin = typeof fsMinRaw === "number" && Number.isFinite(fsMinRaw) && fsMinRaw >= FONT_SIZE_FLOOR && fsMinRaw <= FONT_SIZE_CEIL ? Math.trunc(fsMinRaw) : DEFAULT_DISPLAY.fontSizeMin;
|
|
81
|
-
let fontSizeMax = typeof fsMaxRaw === "number" && Number.isFinite(fsMaxRaw) && fsMaxRaw >= FONT_SIZE_FLOOR && fsMaxRaw <= FONT_SIZE_CEIL ? Math.trunc(fsMaxRaw) : DEFAULT_DISPLAY.fontSizeMax;
|
|
78
|
+
let fontSizeMin = normalizeBoundedNumber(rawDisplay?.fontSizeMin, FONT_SIZE_FLOOR, FONT_SIZE_CEIL, DEFAULT_DISPLAY.fontSizeMin, { truncate: true });
|
|
79
|
+
let fontSizeMax = normalizeBoundedNumber(rawDisplay?.fontSizeMax, FONT_SIZE_FLOOR, FONT_SIZE_CEIL, DEFAULT_DISPLAY.fontSizeMax, { truncate: true });
|
|
82
80
|
if (fontSizeMin > fontSizeMax) {
|
|
83
81
|
[fontSizeMin, fontSizeMax] = [fontSizeMax, fontSizeMin];
|
|
84
82
|
}
|
|
85
83
|
const legacyTargetCols = rawDisplay?.targetCols;
|
|
86
|
-
const
|
|
87
|
-
const
|
|
88
|
-
const lsRaw = rawDisplay?.letterSpacing;
|
|
89
|
-
const letterSpacing = typeof lsRaw === "number" && Number.isFinite(lsRaw) && lsRaw >= -4 && lsRaw <= 8 ? lsRaw : DEFAULT_DISPLAY.letterSpacing;
|
|
84
|
+
const maxCols = normalizeBoundedNumber(rawDisplay?.maxCols ?? legacyTargetCols, 0, 500, DEFAULT_DISPLAY.maxCols, { truncate: true });
|
|
85
|
+
const letterSpacing = normalizeBoundedNumber(rawDisplay?.letterSpacing, -4, 8, DEFAULT_DISPLAY.letterSpacing);
|
|
90
86
|
const themeRaw = rawDisplay?.theme;
|
|
91
87
|
const theme = themeRaw === "dark" || themeRaw === "light" || themeRaw === "dark-ansi" || themeRaw === "light-ansi" || themeRaw === "dark-daltonized" || themeRaw === "light-daltonized" || themeRaw === "auto" ? themeRaw : DEFAULT_DISPLAY.theme;
|
|
88
|
+
const markdownPreview = typeof rawDisplay?.markdownPreview === "boolean" ? rawDisplay.markdownPreview : DEFAULT_DISPLAY.markdownPreview;
|
|
92
89
|
const display = {
|
|
93
90
|
fontSizeMin,
|
|
94
91
|
fontSizeMax,
|
|
95
92
|
maxCols,
|
|
96
93
|
letterSpacing,
|
|
97
|
-
theme
|
|
94
|
+
theme,
|
|
95
|
+
markdownPreview
|
|
98
96
|
};
|
|
99
97
|
const rawIntegrations = src.integrations;
|
|
100
98
|
const ccUserEvents = rawIntegrations?.perModule?.["claude-code"]?.events ?? {};
|
|
101
99
|
const ccDefaults = DEFAULT_INTEGRATIONS.perModule["claude-code"].events;
|
|
100
|
+
const userRenderingMd = rawIntegrations?.rendering?.markdown?.enabled;
|
|
101
|
+
const renderingMdEnabled = typeof userRenderingMd === "boolean" ? userRenderingMd : typeof rawDisplay?.markdownPreview === "boolean" ? rawDisplay.markdownPreview : DEFAULT_INTEGRATIONS.rendering.markdown.enabled;
|
|
102
|
+
const userObsidian = rawIntegrations?.rendering?.obsidian;
|
|
103
|
+
const obsDefaults = DEFAULT_INTEGRATIONS.rendering.obsidian;
|
|
104
|
+
const rendering = {
|
|
105
|
+
markdown: { enabled: renderingMdEnabled },
|
|
106
|
+
obsidian: {
|
|
107
|
+
enabled: typeof userObsidian?.enabled === "boolean" ? userObsidian.enabled : obsDefaults.enabled,
|
|
108
|
+
frontmatter: typeof userObsidian?.frontmatter === "boolean" ? userObsidian.frontmatter : obsDefaults.frontmatter,
|
|
109
|
+
wikilink: typeof userObsidian?.wikilink === "boolean" ? userObsidian.wikilink : obsDefaults.wikilink,
|
|
110
|
+
embed: typeof userObsidian?.embed === "boolean" ? userObsidian.embed : obsDefaults.embed,
|
|
111
|
+
callout: typeof userObsidian?.callout === "boolean" ? userObsidian.callout : obsDefaults.callout,
|
|
112
|
+
inlineSyntax: typeof userObsidian?.inlineSyntax === "boolean" ? userObsidian.inlineSyntax : obsDefaults.inlineSyntax
|
|
113
|
+
}
|
|
114
|
+
};
|
|
102
115
|
const integrations = {
|
|
103
116
|
enabled: typeof rawIntegrations?.enabled === "boolean" ? rawIntegrations.enabled : DEFAULT_INTEGRATIONS.enabled,
|
|
104
117
|
forceModule: rawIntegrations?.forceModule === "auto" || rawIntegrations?.forceModule === "claude-code" || rawIntegrations?.forceModule === "none" ? rawIntegrations.forceModule : DEFAULT_INTEGRATIONS.forceModule,
|
|
@@ -112,7 +125,8 @@ function ensureDefaultUserConfig(input) {
|
|
|
112
125
|
userPrompts: typeof ccUserEvents.userPrompts === "boolean" ? ccUserEvents.userPrompts : ccDefaults.userPrompts
|
|
113
126
|
}
|
|
114
127
|
}
|
|
115
|
-
}
|
|
128
|
+
},
|
|
129
|
+
rendering
|
|
116
130
|
};
|
|
117
131
|
return {
|
|
118
132
|
...src,
|
|
@@ -139,6 +153,13 @@ function normalizeStringArray(input) {
|
|
|
139
153
|
}
|
|
140
154
|
return out;
|
|
141
155
|
}
|
|
156
|
+
function normalizeBoundedNumber(input, min, max, fallback, opts = {}) {
|
|
157
|
+
if (typeof input !== "number" || !Number.isFinite(input))
|
|
158
|
+
return fallback;
|
|
159
|
+
if (input < min || input > max)
|
|
160
|
+
return fallback;
|
|
161
|
+
return opts.truncate ? Math.trunc(input) : input;
|
|
162
|
+
}
|
|
142
163
|
var SHORTCUT_GROUPS, DEFAULT_SHORTCUTS, COMMAND_GROUPS, DEFAULT_COMMANDS, DEFAULT_INTEGRATIONS, DEFAULT_INPUT, DEFAULT_DISPLAY, FONT_SIZE_FLOOR, FONT_SIZE_CEIL, DEFAULT_WORKDIR_DENY;
|
|
143
164
|
var init_defaults = __esm({
|
|
144
165
|
"shared/dist/defaults.js"() {
|
|
@@ -342,6 +363,17 @@ var init_defaults = __esm({
|
|
|
342
363
|
userPrompts: false
|
|
343
364
|
}
|
|
344
365
|
}
|
|
366
|
+
},
|
|
367
|
+
rendering: {
|
|
368
|
+
markdown: { enabled: true },
|
|
369
|
+
obsidian: {
|
|
370
|
+
enabled: true,
|
|
371
|
+
frontmatter: true,
|
|
372
|
+
wikilink: true,
|
|
373
|
+
embed: true,
|
|
374
|
+
callout: true,
|
|
375
|
+
inlineSyntax: true
|
|
376
|
+
}
|
|
345
377
|
}
|
|
346
378
|
};
|
|
347
379
|
DEFAULT_INPUT = {
|
|
@@ -357,8 +389,9 @@ var init_defaults = __esm({
|
|
|
357
389
|
maxCols: 0,
|
|
358
390
|
// 0 = 关闭自适应
|
|
359
391
|
letterSpacing: 0,
|
|
360
|
-
theme: "auto"
|
|
392
|
+
theme: "auto",
|
|
361
393
|
// 跟随系统亮暗模式:dark → Campbell, light → Solarized Light
|
|
394
|
+
markdownPreview: true
|
|
362
395
|
};
|
|
363
396
|
FONT_SIZE_FLOOR = 6;
|
|
364
397
|
FONT_SIZE_CEIL = 32;
|
|
@@ -419,6 +452,14 @@ var init_errors = __esm({
|
|
|
419
452
|
ErrorCode2["PUSH_SEND_FAILED"] = "PUSH_SEND_FAILED";
|
|
420
453
|
ErrorCode2["HOOK_INVALID_PAYLOAD"] = "HOOK_INVALID_PAYLOAD";
|
|
421
454
|
ErrorCode2["HOOK_NON_LOCALHOST"] = "HOOK_NON_LOCALHOST";
|
|
455
|
+
ErrorCode2["BAD_REQUEST"] = "BAD_REQUEST";
|
|
456
|
+
ErrorCode2["PATH_NOT_FOUND"] = "PATH_NOT_FOUND";
|
|
457
|
+
ErrorCode2["PATH_FORBIDDEN"] = "PATH_FORBIDDEN";
|
|
458
|
+
ErrorCode2["FILE_TOO_LARGE"] = "FILE_TOO_LARGE";
|
|
459
|
+
ErrorCode2["FILE_BINARY"] = "FILE_BINARY";
|
|
460
|
+
ErrorCode2["FILE_TYPE_FORBID"] = "FILE_TYPE_FORBID";
|
|
461
|
+
ErrorCode2["SEARCH_INVALID_Q"] = "SEARCH_INVALID_Q";
|
|
462
|
+
ErrorCode2["SEARCH_TIMEOUT"] = "SEARCH_TIMEOUT";
|
|
422
463
|
ErrorCode2["INTERNAL_ERROR"] = "INTERNAL_ERROR";
|
|
423
464
|
ErrorCode2["NOT_IMPLEMENTED"] = "NOT_IMPLEMENTED";
|
|
424
465
|
})(ErrorCode || (ErrorCode = {}));
|
|
@@ -439,8 +480,8 @@ function injectManifestToken(html, token) {
|
|
|
439
480
|
if (!hm)
|
|
440
481
|
return html;
|
|
441
482
|
const oldHref = hm[2] ?? hm[3] ?? "";
|
|
442
|
-
const
|
|
443
|
-
const newHref = `${oldHref}${
|
|
483
|
+
const sep3 = oldHref.includes("?") ? "&" : "?";
|
|
484
|
+
const newHref = `${oldHref}${sep3}token=${encodeURIComponent(token)}`;
|
|
444
485
|
const newTag = tag.replace(hrefRe, `href="${newHref}"`);
|
|
445
486
|
return html.replace(tag, newTag);
|
|
446
487
|
}
|
|
@@ -450,6 +491,26 @@ var init_html_injection = __esm({
|
|
|
450
491
|
}
|
|
451
492
|
});
|
|
452
493
|
|
|
494
|
+
// shared/dist/files.js
|
|
495
|
+
function isSearchMode(v) {
|
|
496
|
+
return typeof v === "string" && SEARCH_MODES.includes(v);
|
|
497
|
+
}
|
|
498
|
+
var SEARCH_MODES, FILE_READ_MAX_BYTES, FILE_RAW_MAX_BYTES, SEARCH_MAX_Q_LENGTH, SEARCH_MAX_NAME_RESULTS, SEARCH_MAX_CONTENT_RESULTS, SEARCH_FILE_TIMEOUT_MS, SEARCH_TOTAL_TIMEOUT_MS, SEARCH_CONCURRENCY;
|
|
499
|
+
var init_files = __esm({
|
|
500
|
+
"shared/dist/files.js"() {
|
|
501
|
+
"use strict";
|
|
502
|
+
SEARCH_MODES = ["name", "content", "both"];
|
|
503
|
+
FILE_READ_MAX_BYTES = 2 * 1024 * 1024;
|
|
504
|
+
FILE_RAW_MAX_BYTES = 8 * 1024 * 1024;
|
|
505
|
+
SEARCH_MAX_Q_LENGTH = 200;
|
|
506
|
+
SEARCH_MAX_NAME_RESULTS = 100;
|
|
507
|
+
SEARCH_MAX_CONTENT_RESULTS = 200;
|
|
508
|
+
SEARCH_FILE_TIMEOUT_MS = 100;
|
|
509
|
+
SEARCH_TOTAL_TIMEOUT_MS = 5e3;
|
|
510
|
+
SEARCH_CONCURRENCY = 8;
|
|
511
|
+
}
|
|
512
|
+
});
|
|
513
|
+
|
|
453
514
|
// shared/dist/index.js
|
|
454
515
|
var init_dist = __esm({
|
|
455
516
|
"shared/dist/index.js"() {
|
|
@@ -462,11 +523,12 @@ var init_dist = __esm({
|
|
|
462
523
|
init_action_tree_defaults();
|
|
463
524
|
init_errors();
|
|
464
525
|
init_html_injection();
|
|
526
|
+
init_files();
|
|
465
527
|
}
|
|
466
528
|
});
|
|
467
529
|
|
|
468
530
|
// backend/dist/errors.js
|
|
469
|
-
var AppError, AuthError, PtyError, ConfigError, InstanceError, LockError, HookError, PushError;
|
|
531
|
+
var AppError, AuthError, PtyError, ConfigError, InstanceError, LockError, HookError, PushError, FileError;
|
|
470
532
|
var init_errors2 = __esm({
|
|
471
533
|
"backend/dist/errors.js"() {
|
|
472
534
|
"use strict";
|
|
@@ -531,6 +593,11 @@ Caused by: ${cause.stack}`;
|
|
|
531
593
|
super(code, message, httpStatus, cause);
|
|
532
594
|
}
|
|
533
595
|
};
|
|
596
|
+
FileError = class extends AppError {
|
|
597
|
+
constructor(code, message, httpStatus = 400, cause) {
|
|
598
|
+
super(code, message, httpStatus, cause);
|
|
599
|
+
}
|
|
600
|
+
};
|
|
534
601
|
}
|
|
535
602
|
});
|
|
536
603
|
|
|
@@ -1352,7 +1419,7 @@ function getEntryUrl(req, subPath = "") {
|
|
|
1352
1419
|
function isFromBroker(req) {
|
|
1353
1420
|
return getInstanceFromHeaders(req.headers) !== null;
|
|
1354
1421
|
}
|
|
1355
|
-
var HEADER_FORWARDED_INSTANCE, HEADER_FORWARDED_PATH, HEADER_FORWARDED_HOST, HEADER_FORWARDED_PROTO, HEADER_FORWARDED_FOR;
|
|
1422
|
+
var HEADER_FORWARDED_INSTANCE, HEADER_FORWARDED_PATH, HEADER_FORWARDED_HOST, HEADER_FORWARDED_PROTO, HEADER_FORWARDED_FOR, HEADER_ATR_ERROR;
|
|
1356
1423
|
var init_forwarded_headers = __esm({
|
|
1357
1424
|
"backend/dist/broker/forwarded-headers.js"() {
|
|
1358
1425
|
"use strict";
|
|
@@ -1361,6 +1428,7 @@ var init_forwarded_headers = __esm({
|
|
|
1361
1428
|
HEADER_FORWARDED_HOST = "x-forwarded-host";
|
|
1362
1429
|
HEADER_FORWARDED_PROTO = "x-forwarded-proto";
|
|
1363
1430
|
HEADER_FORWARDED_FOR = "x-forwarded-for";
|
|
1431
|
+
HEADER_ATR_ERROR = "X-ATR-Error";
|
|
1364
1432
|
}
|
|
1365
1433
|
});
|
|
1366
1434
|
|
|
@@ -1556,79 +1624,1807 @@ function collectEndpoints(port, displayIp) {
|
|
|
1556
1624
|
continue;
|
|
1557
1625
|
ipv6KeptForIface = true;
|
|
1558
1626
|
}
|
|
1559
|
-
const kind = classify(ip, info.family);
|
|
1560
|
-
out.push({ host: ip, port, kind, interface: ifname });
|
|
1627
|
+
const kind = classify(ip, info.family);
|
|
1628
|
+
out.push({ host: ip, port, kind, interface: ifname });
|
|
1629
|
+
}
|
|
1630
|
+
}
|
|
1631
|
+
if (!out.some((e) => e.kind === "loopback")) {
|
|
1632
|
+
out.push({ host: "127.0.0.1", port, kind: "loopback" });
|
|
1633
|
+
}
|
|
1634
|
+
const order = {
|
|
1635
|
+
lan: 0,
|
|
1636
|
+
tailscale: 1,
|
|
1637
|
+
other: 2,
|
|
1638
|
+
ipv6: 3,
|
|
1639
|
+
loopback: 4
|
|
1640
|
+
};
|
|
1641
|
+
out.sort((a, b) => {
|
|
1642
|
+
if (a.kind !== b.kind)
|
|
1643
|
+
return order[a.kind] - order[b.kind];
|
|
1644
|
+
if (a.host === displayIp)
|
|
1645
|
+
return -1;
|
|
1646
|
+
if (b.host === displayIp)
|
|
1647
|
+
return 1;
|
|
1648
|
+
return 0;
|
|
1649
|
+
});
|
|
1650
|
+
let defaultIdx = out.findIndex((e) => e.host === displayIp);
|
|
1651
|
+
if (defaultIdx === -1) {
|
|
1652
|
+
defaultIdx = out.findIndex((e) => e.kind !== "loopback");
|
|
1653
|
+
}
|
|
1654
|
+
if (defaultIdx === -1 && out.length > 0)
|
|
1655
|
+
defaultIdx = 0;
|
|
1656
|
+
if (defaultIdx >= 0)
|
|
1657
|
+
out[defaultIdx].isDefault = true;
|
|
1658
|
+
return out;
|
|
1659
|
+
}
|
|
1660
|
+
function classify(ip, family) {
|
|
1661
|
+
if (isLoopbackIp(ip))
|
|
1662
|
+
return "loopback";
|
|
1663
|
+
if (family === "IPv6")
|
|
1664
|
+
return "ipv6";
|
|
1665
|
+
if (isTailscaleIp(ip))
|
|
1666
|
+
return "tailscale";
|
|
1667
|
+
if (isPrivateIp(ip))
|
|
1668
|
+
return "lan";
|
|
1669
|
+
if (isLinkLocal(ip))
|
|
1670
|
+
return "other";
|
|
1671
|
+
return "other";
|
|
1672
|
+
}
|
|
1673
|
+
var init_share_routes = __esm({
|
|
1674
|
+
"backend/dist/api/share-routes.js"() {
|
|
1675
|
+
"use strict";
|
|
1676
|
+
init_network();
|
|
1677
|
+
}
|
|
1678
|
+
});
|
|
1679
|
+
|
|
1680
|
+
// backend/dist/api/workdir-policy-routes.js
|
|
1681
|
+
import { Router as Router8 } from "express";
|
|
1682
|
+
function createWorkdirPolicyRoutes(authModule, snapshot) {
|
|
1683
|
+
const router = Router8();
|
|
1684
|
+
router.get("/workdir-policy", authModule.requireAuth, (_req, res) => {
|
|
1685
|
+
const s = snapshot();
|
|
1686
|
+
res.json({ ok: true, allow: s.allow });
|
|
1687
|
+
});
|
|
1688
|
+
return router;
|
|
1689
|
+
}
|
|
1690
|
+
var init_workdir_policy_routes = __esm({
|
|
1691
|
+
"backend/dist/api/workdir-policy-routes.js"() {
|
|
1692
|
+
"use strict";
|
|
1693
|
+
}
|
|
1694
|
+
});
|
|
1695
|
+
|
|
1696
|
+
// backend/dist/auth/rate-limiter.js
|
|
1697
|
+
var RateLimiter;
|
|
1698
|
+
var init_rate_limiter = __esm({
|
|
1699
|
+
"backend/dist/auth/rate-limiter.js"() {
|
|
1700
|
+
"use strict";
|
|
1701
|
+
init_logger();
|
|
1702
|
+
RateLimiter = class {
|
|
1703
|
+
entries = /* @__PURE__ */ new Map();
|
|
1704
|
+
maxAttempts;
|
|
1705
|
+
windowMs;
|
|
1706
|
+
cleanupTimer = null;
|
|
1707
|
+
/**
|
|
1708
|
+
* @param maxAttempts 窗口内最大允许次数
|
|
1709
|
+
* @param windowMs 窗口长度(毫秒),默认 60s
|
|
1710
|
+
*/
|
|
1711
|
+
constructor(maxAttempts, windowMs = 6e4) {
|
|
1712
|
+
if (!Number.isInteger(maxAttempts) || maxAttempts <= 0) {
|
|
1713
|
+
throw new Error("RateLimiter: maxAttempts \u5FC5\u987B\u662F\u6B63\u6574\u6570");
|
|
1714
|
+
}
|
|
1715
|
+
this.maxAttempts = maxAttempts;
|
|
1716
|
+
this.windowMs = windowMs;
|
|
1717
|
+
this.cleanupTimer = setInterval(() => this.cleanup(), windowMs * 2);
|
|
1718
|
+
if (typeof this.cleanupTimer.unref === "function") {
|
|
1719
|
+
this.cleanupTimer.unref();
|
|
1720
|
+
}
|
|
1721
|
+
}
|
|
1722
|
+
/**
|
|
1723
|
+
* 尝试一次请求,自动累加计数
|
|
1724
|
+
*
|
|
1725
|
+
* @returns true 通过 / false 已超限被拒
|
|
1726
|
+
*/
|
|
1727
|
+
attempt(ip) {
|
|
1728
|
+
const now = Date.now();
|
|
1729
|
+
const entry = this.entries.get(ip);
|
|
1730
|
+
if (!entry || now >= entry.resetAt) {
|
|
1731
|
+
this.entries.set(ip, { count: 1, resetAt: now + this.windowMs });
|
|
1732
|
+
return true;
|
|
1733
|
+
}
|
|
1734
|
+
entry.count++;
|
|
1735
|
+
if (entry.count > this.maxAttempts) {
|
|
1736
|
+
logger.warn({ ip, count: entry.count, max: this.maxAttempts }, "\u8BA4\u8BC1\u901F\u7387\u8D85\u9650");
|
|
1737
|
+
return false;
|
|
1738
|
+
}
|
|
1739
|
+
return true;
|
|
1740
|
+
}
|
|
1741
|
+
/** 当前窗口内剩余次数 */
|
|
1742
|
+
remaining(ip) {
|
|
1743
|
+
const now = Date.now();
|
|
1744
|
+
const entry = this.entries.get(ip);
|
|
1745
|
+
if (!entry || now >= entry.resetAt)
|
|
1746
|
+
return this.maxAttempts;
|
|
1747
|
+
return Math.max(0, this.maxAttempts - entry.count);
|
|
1748
|
+
}
|
|
1749
|
+
/**
|
|
1750
|
+
* 重置某 IP 的计数(认证成功后清零)
|
|
1751
|
+
*
|
|
1752
|
+
* 让合法用户不会因为之前误输导致后续尝试被限流
|
|
1753
|
+
*/
|
|
1754
|
+
reset(ip) {
|
|
1755
|
+
this.entries.delete(ip);
|
|
1756
|
+
}
|
|
1757
|
+
/** 清理过期 entry(避免内存膨胀) */
|
|
1758
|
+
cleanup() {
|
|
1759
|
+
const now = Date.now();
|
|
1760
|
+
for (const [ip, entry] of this.entries) {
|
|
1761
|
+
if (now >= entry.resetAt) {
|
|
1762
|
+
this.entries.delete(ip);
|
|
1763
|
+
}
|
|
1764
|
+
}
|
|
1765
|
+
}
|
|
1766
|
+
/** 销毁定时器 */
|
|
1767
|
+
destroy() {
|
|
1768
|
+
if (this.cleanupTimer) {
|
|
1769
|
+
clearInterval(this.cleanupTimer);
|
|
1770
|
+
this.cleanupTimer = null;
|
|
1771
|
+
}
|
|
1772
|
+
}
|
|
1773
|
+
};
|
|
1774
|
+
}
|
|
1775
|
+
});
|
|
1776
|
+
|
|
1777
|
+
// backend/dist/constants.js
|
|
1778
|
+
var WS_FLUSH_INTERVAL_MS, WS_MAX_CHUNK_BYTES, WS_HIGH_WATERMARK_BYTES, FILE_LOCK_RETRIES, FILE_LOCK_RETRY_INTERVAL_MS, FILE_LOCK_STALE_MS, FILE_RATE_LIMIT_PER_MIN, SEARCH_RATE_LIMIT_PER_MIN, PTY_DEFAULT_COLS, PTY_DEFAULT_ROWS, PTY_TERM_NAME, DOUBLE_PULSE_DELAY_MS, SHUTDOWN_WS_FLUSH_DELAY_MS, SHUTDOWN_FORCE_EXIT_MS, DOUBLE_CTRL_C_WINDOW_MS, PORT_FINDER_MAX_ATTEMPTS, STOP_INSTANCE_GRACE_MS, STOP_INSTANCE_POLL_INTERVAL_MS, ATTACH_RECONNECT_DELAYS_MS;
|
|
1779
|
+
var init_constants2 = __esm({
|
|
1780
|
+
"backend/dist/constants.js"() {
|
|
1781
|
+
"use strict";
|
|
1782
|
+
WS_FLUSH_INTERVAL_MS = 16;
|
|
1783
|
+
WS_MAX_CHUNK_BYTES = 32 * 1024;
|
|
1784
|
+
WS_HIGH_WATERMARK_BYTES = 256 * 1024;
|
|
1785
|
+
FILE_LOCK_RETRIES = 50;
|
|
1786
|
+
FILE_LOCK_RETRY_INTERVAL_MS = 50;
|
|
1787
|
+
FILE_LOCK_STALE_MS = 1e4;
|
|
1788
|
+
FILE_RATE_LIMIT_PER_MIN = 600;
|
|
1789
|
+
SEARCH_RATE_LIMIT_PER_MIN = 60;
|
|
1790
|
+
PTY_DEFAULT_COLS = 80;
|
|
1791
|
+
PTY_DEFAULT_ROWS = 24;
|
|
1792
|
+
PTY_TERM_NAME = "xterm-256color";
|
|
1793
|
+
DOUBLE_PULSE_DELAY_MS = 50;
|
|
1794
|
+
SHUTDOWN_WS_FLUSH_DELAY_MS = 500;
|
|
1795
|
+
SHUTDOWN_FORCE_EXIT_MS = 2e3;
|
|
1796
|
+
DOUBLE_CTRL_C_WINDOW_MS = 500;
|
|
1797
|
+
PORT_FINDER_MAX_ATTEMPTS = 100;
|
|
1798
|
+
STOP_INSTANCE_GRACE_MS = 3e3;
|
|
1799
|
+
STOP_INSTANCE_POLL_INTERVAL_MS = 100;
|
|
1800
|
+
ATTACH_RECONNECT_DELAYS_MS = [1e3, 2e3, 4e3, 8e3, 16e3, 3e4];
|
|
1801
|
+
}
|
|
1802
|
+
});
|
|
1803
|
+
|
|
1804
|
+
// backend/dist/utils/workdir-policy.js
|
|
1805
|
+
import picomatch from "picomatch";
|
|
1806
|
+
function checkWorkdir(cwd, allow, deny) {
|
|
1807
|
+
const norm = normalizePath(cwd);
|
|
1808
|
+
const opts = { dot: true };
|
|
1809
|
+
if (deny && deny.length > 0) {
|
|
1810
|
+
for (const pattern of deny) {
|
|
1811
|
+
if (picomatch(pattern, opts)(norm)) {
|
|
1812
|
+
return {
|
|
1813
|
+
reason: `cwd "${cwd}" \u547D\u4E2D\u9ED1\u540D\u5355 pattern\uFF1A${pattern}`,
|
|
1814
|
+
matchedPattern: pattern
|
|
1815
|
+
};
|
|
1816
|
+
}
|
|
1817
|
+
}
|
|
1818
|
+
}
|
|
1819
|
+
if (allow && allow.length > 0) {
|
|
1820
|
+
let hit = false;
|
|
1821
|
+
for (const pattern of allow) {
|
|
1822
|
+
if (picomatch(pattern, opts)(norm)) {
|
|
1823
|
+
hit = true;
|
|
1824
|
+
break;
|
|
1825
|
+
}
|
|
1826
|
+
}
|
|
1827
|
+
if (!hit) {
|
|
1828
|
+
return {
|
|
1829
|
+
reason: `cwd "${cwd}" \u672A\u547D\u4E2D\u4EFB\u4F55\u767D\u540D\u5355 pattern\uFF1A[${allow.join(", ")}]`,
|
|
1830
|
+
matchedPattern: ""
|
|
1831
|
+
};
|
|
1832
|
+
}
|
|
1833
|
+
}
|
|
1834
|
+
return null;
|
|
1835
|
+
}
|
|
1836
|
+
function normalizePath(p) {
|
|
1837
|
+
return p.replace(/\\/g, "/");
|
|
1838
|
+
}
|
|
1839
|
+
var init_workdir_policy = __esm({
|
|
1840
|
+
"backend/dist/utils/workdir-policy.js"() {
|
|
1841
|
+
"use strict";
|
|
1842
|
+
}
|
|
1843
|
+
});
|
|
1844
|
+
|
|
1845
|
+
// backend/dist/files/path-resolver.js
|
|
1846
|
+
import { realpathSync } from "node:fs";
|
|
1847
|
+
import { isAbsolute, relative as relativePath, resolve as resolvePath } from "node:path";
|
|
1848
|
+
function resolveSafePath(cwd, input, policy) {
|
|
1849
|
+
const raw = input && input.length > 0 ? input : ".";
|
|
1850
|
+
const abs = isAbsolute(raw) ? raw : resolvePath(cwd, raw);
|
|
1851
|
+
let real;
|
|
1852
|
+
try {
|
|
1853
|
+
real = realpathSync(abs);
|
|
1854
|
+
} catch (err) {
|
|
1855
|
+
throw new FileError(ErrorCode.PATH_NOT_FOUND, `path not found: ${abs}`, 404, err);
|
|
1856
|
+
}
|
|
1857
|
+
const cwdReal = realpathSync(cwd);
|
|
1858
|
+
if (!isWithin(cwdReal, real)) {
|
|
1859
|
+
throw new FileError(ErrorCode.PATH_FORBIDDEN, `path outside instance cwd: ${real}`, 403);
|
|
1860
|
+
}
|
|
1861
|
+
const verdict = checkWorkdir(real, policy.allow, policy.deny);
|
|
1862
|
+
if (verdict !== null) {
|
|
1863
|
+
throw new FileError(ErrorCode.PATH_FORBIDDEN, `path forbidden: ${verdict.reason}`, 403);
|
|
1864
|
+
}
|
|
1865
|
+
return real;
|
|
1866
|
+
}
|
|
1867
|
+
function isWithin(parent, child) {
|
|
1868
|
+
if (parent === child)
|
|
1869
|
+
return true;
|
|
1870
|
+
const rel = relativePath(parent, child);
|
|
1871
|
+
if (rel === "" || rel === ".")
|
|
1872
|
+
return true;
|
|
1873
|
+
if (isAbsolute(rel))
|
|
1874
|
+
return false;
|
|
1875
|
+
if (rel.startsWith(".."))
|
|
1876
|
+
return false;
|
|
1877
|
+
return true;
|
|
1878
|
+
}
|
|
1879
|
+
var init_path_resolver = __esm({
|
|
1880
|
+
"backend/dist/files/path-resolver.js"() {
|
|
1881
|
+
"use strict";
|
|
1882
|
+
init_dist();
|
|
1883
|
+
init_errors2();
|
|
1884
|
+
init_workdir_policy();
|
|
1885
|
+
}
|
|
1886
|
+
});
|
|
1887
|
+
|
|
1888
|
+
// backend/dist/files/mime-detect.js
|
|
1889
|
+
import { basename as basename2, extname } from "node:path";
|
|
1890
|
+
function detectMime(filename) {
|
|
1891
|
+
const base = basename2(filename);
|
|
1892
|
+
const special = lookupSpecial(base);
|
|
1893
|
+
if (special) {
|
|
1894
|
+
return { mime: special.mime, previewable: special.previewable, lang: special.lang };
|
|
1895
|
+
}
|
|
1896
|
+
const ext = extname(base).toLowerCase();
|
|
1897
|
+
const lang = LANG_MAP[ext] ?? "txt";
|
|
1898
|
+
if (ext in IMAGE_EXT_TO_MIME) {
|
|
1899
|
+
return { mime: IMAGE_EXT_TO_MIME[ext], previewable: "image", lang };
|
|
1900
|
+
}
|
|
1901
|
+
if (ext in TEXT_EXT_TO_MIME) {
|
|
1902
|
+
return { mime: TEXT_EXT_TO_MIME[ext], previewable: "text", lang };
|
|
1903
|
+
}
|
|
1904
|
+
return { mime: "application/octet-stream", previewable: "none", lang };
|
|
1905
|
+
}
|
|
1906
|
+
function lookupSpecial(base) {
|
|
1907
|
+
if (base in SPECIAL_NAMES)
|
|
1908
|
+
return SPECIAL_NAMES[base];
|
|
1909
|
+
const ext = extname(base).toLowerCase();
|
|
1910
|
+
if (ext === "" || ext === ".md" || ext === ".markdown" || ext === ".txt") {
|
|
1911
|
+
const stem = base.slice(0, base.length - ext.length);
|
|
1912
|
+
const upper = stem.toUpperCase();
|
|
1913
|
+
if (upper === "README" || upper === "CHANGELOG" || upper === "LICENSE" || upper === "COPYING" || upper === "AUTHORS" || upper === "CONTRIBUTORS" || upper === "NOTICE" || upper === "TODO") {
|
|
1914
|
+
const isMd = ext === ".md" || ext === ".markdown";
|
|
1915
|
+
const baseEntry = SPECIAL_NAMES[upper];
|
|
1916
|
+
if (baseEntry) {
|
|
1917
|
+
return isMd ? { ...baseEntry, lang: "markdown", mime: "text/markdown" } : baseEntry;
|
|
1918
|
+
}
|
|
1919
|
+
}
|
|
1920
|
+
}
|
|
1921
|
+
return null;
|
|
1922
|
+
}
|
|
1923
|
+
var SPECIAL_NAMES, IMAGE_EXT_TO_MIME, TEXT_EXT_TO_MIME, LANG_MAP;
|
|
1924
|
+
var init_mime_detect = __esm({
|
|
1925
|
+
"backend/dist/files/mime-detect.js"() {
|
|
1926
|
+
"use strict";
|
|
1927
|
+
SPECIAL_NAMES = {
|
|
1928
|
+
// ─── 构建/容器 ───
|
|
1929
|
+
"Makefile": { mime: "text/x-makefile", lang: "makefile", previewable: "text" },
|
|
1930
|
+
"GNUmakefile": { mime: "text/x-makefile", lang: "makefile", previewable: "text" },
|
|
1931
|
+
"Dockerfile": { mime: "text/x-dockerfile", lang: "docker", previewable: "text" },
|
|
1932
|
+
"Containerfile": { mime: "text/x-dockerfile", lang: "docker", previewable: "text" },
|
|
1933
|
+
"CMakeLists.txt": { mime: "text/plain", lang: "cmake", previewable: "text" },
|
|
1934
|
+
// ─── 包管理 / 工程 ───
|
|
1935
|
+
"package.json": { mime: "application/json", lang: "json", previewable: "text" },
|
|
1936
|
+
"package-lock.json": { mime: "application/json", lang: "json", previewable: "text" },
|
|
1937
|
+
"tsconfig.json": { mime: "application/json", lang: "jsonc", previewable: "text" },
|
|
1938
|
+
"tsconfig.base.json": { mime: "application/json", lang: "jsonc", previewable: "text" },
|
|
1939
|
+
"jsconfig.json": { mime: "application/json", lang: "jsonc", previewable: "text" },
|
|
1940
|
+
"turbo.json": { mime: "application/json", lang: "jsonc", previewable: "text" },
|
|
1941
|
+
"nx.json": { mime: "application/json", lang: "json", previewable: "text" },
|
|
1942
|
+
"lerna.json": { mime: "application/json", lang: "json", previewable: "text" },
|
|
1943
|
+
"biome.json": { mime: "application/json", lang: "jsonc", previewable: "text" },
|
|
1944
|
+
"composer.json": { mime: "application/json", lang: "json", previewable: "text" },
|
|
1945
|
+
"composer.lock": { mime: "application/json", lang: "json", previewable: "text" },
|
|
1946
|
+
"pnpm-workspace.yaml": { mime: "application/yaml", lang: "yaml", previewable: "text" },
|
|
1947
|
+
"pnpm-lock.yaml": { mime: "application/yaml", lang: "yaml", previewable: "text" },
|
|
1948
|
+
"yarn.lock": { mime: "text/plain", lang: "yaml", previewable: "text" },
|
|
1949
|
+
"bun.lockb": { mime: "application/octet-stream", lang: "txt", previewable: "none" },
|
|
1950
|
+
"bun.lock": { mime: "text/plain", lang: "toml", previewable: "text" },
|
|
1951
|
+
// shiki 没有 go-mod grammar,降级 txt(纯文本仍可读,只是无着色)
|
|
1952
|
+
"go.mod": { mime: "text/x-go-mod", lang: "txt", previewable: "text" },
|
|
1953
|
+
"go.sum": { mime: "text/plain", lang: "txt", previewable: "text" },
|
|
1954
|
+
"Cargo.toml": { mime: "application/toml", lang: "toml", previewable: "text" },
|
|
1955
|
+
"Cargo.lock": { mime: "application/toml", lang: "toml", previewable: "text" },
|
|
1956
|
+
"pyproject.toml": { mime: "application/toml", lang: "toml", previewable: "text" },
|
|
1957
|
+
"poetry.lock": { mime: "application/toml", lang: "toml", previewable: "text" },
|
|
1958
|
+
"Pipfile": { mime: "application/toml", lang: "toml", previewable: "text" },
|
|
1959
|
+
"Pipfile.lock": { mime: "application/json", lang: "json", previewable: "text" },
|
|
1960
|
+
"requirements.txt": { mime: "text/plain", lang: "txt", previewable: "text" },
|
|
1961
|
+
"requirements-dev.txt": { mime: "text/plain", lang: "txt", previewable: "text" },
|
|
1962
|
+
"Rakefile": { mime: "text/x-ruby", lang: "ruby", previewable: "text" },
|
|
1963
|
+
"Gemfile": { mime: "text/x-ruby", lang: "ruby", previewable: "text" },
|
|
1964
|
+
"Gemfile.lock": { mime: "text/plain", lang: "txt", previewable: "text" },
|
|
1965
|
+
"Procfile": { mime: "text/plain", lang: "txt", previewable: "text" },
|
|
1966
|
+
"Brewfile": { mime: "text/x-ruby", lang: "ruby", previewable: "text" },
|
|
1967
|
+
"Vagrantfile": { mime: "text/x-ruby", lang: "ruby", previewable: "text" },
|
|
1968
|
+
"Justfile": { mime: "text/x-just", lang: "just", previewable: "text" },
|
|
1969
|
+
"justfile": { mime: "text/x-just", lang: "just", previewable: "text" },
|
|
1970
|
+
"Taskfile.yml": { mime: "application/yaml", lang: "yaml", previewable: "text" },
|
|
1971
|
+
"Taskfile.yaml": { mime: "application/yaml", lang: "yaml", previewable: "text" },
|
|
1972
|
+
// ─── 配置 dotfile ───
|
|
1973
|
+
".gitignore": { mime: "text/x-gitignore", lang: "txt", previewable: "text" },
|
|
1974
|
+
".gitattributes": { mime: "text/plain", lang: "txt", previewable: "text" },
|
|
1975
|
+
".gitmodules": { mime: "text/x-properties", lang: "ini", previewable: "text" },
|
|
1976
|
+
".gitconfig": { mime: "text/x-properties", lang: "ini", previewable: "text" },
|
|
1977
|
+
".dockerignore": { mime: "text/x-gitignore", lang: "txt", previewable: "text" },
|
|
1978
|
+
".npmignore": { mime: "text/x-gitignore", lang: "txt", previewable: "text" },
|
|
1979
|
+
".eslintignore": { mime: "text/x-gitignore", lang: "txt", previewable: "text" },
|
|
1980
|
+
".prettierignore": { mime: "text/x-gitignore", lang: "txt", previewable: "text" },
|
|
1981
|
+
".editorconfig": { mime: "text/x-properties", lang: "ini", previewable: "text" },
|
|
1982
|
+
".env": { mime: "text/plain", lang: "dotenv", previewable: "text" },
|
|
1983
|
+
".env.local": { mime: "text/plain", lang: "dotenv", previewable: "text" },
|
|
1984
|
+
".env.development": { mime: "text/plain", lang: "dotenv", previewable: "text" },
|
|
1985
|
+
".env.production": { mime: "text/plain", lang: "dotenv", previewable: "text" },
|
|
1986
|
+
".env.test": { mime: "text/plain", lang: "dotenv", previewable: "text" },
|
|
1987
|
+
".env.example": { mime: "text/plain", lang: "dotenv", previewable: "text" },
|
|
1988
|
+
".npmrc": { mime: "text/x-properties", lang: "ini", previewable: "text" },
|
|
1989
|
+
".yarnrc": { mime: "text/plain", lang: "yaml", previewable: "text" },
|
|
1990
|
+
".yarnrc.yml": { mime: "application/yaml", lang: "yaml", previewable: "text" },
|
|
1991
|
+
".nvmrc": { mime: "text/plain", lang: "txt", previewable: "text" },
|
|
1992
|
+
".node-version": { mime: "text/plain", lang: "txt", previewable: "text" },
|
|
1993
|
+
".python-version": { mime: "text/plain", lang: "txt", previewable: "text" },
|
|
1994
|
+
".ruby-version": { mime: "text/plain", lang: "txt", previewable: "text" },
|
|
1995
|
+
".tool-versions": { mime: "text/plain", lang: "txt", previewable: "text" },
|
|
1996
|
+
".eslintrc": { mime: "application/json", lang: "json", previewable: "text" },
|
|
1997
|
+
".eslintrc.json": { mime: "application/json", lang: "json", previewable: "text" },
|
|
1998
|
+
".eslintrc.js": { mime: "text/javascript", lang: "js", previewable: "text" },
|
|
1999
|
+
".eslintrc.cjs": { mime: "text/javascript", lang: "js", previewable: "text" },
|
|
2000
|
+
".eslintrc.yml": { mime: "application/yaml", lang: "yaml", previewable: "text" },
|
|
2001
|
+
".eslintrc.yaml": { mime: "application/yaml", lang: "yaml", previewable: "text" },
|
|
2002
|
+
".prettierrc": { mime: "application/json", lang: "json", previewable: "text" },
|
|
2003
|
+
".prettierrc.json": { mime: "application/json", lang: "json", previewable: "text" },
|
|
2004
|
+
".prettierrc.js": { mime: "text/javascript", lang: "js", previewable: "text" },
|
|
2005
|
+
".prettierrc.cjs": { mime: "text/javascript", lang: "js", previewable: "text" },
|
|
2006
|
+
".prettierrc.yml": { mime: "application/yaml", lang: "yaml", previewable: "text" },
|
|
2007
|
+
".prettierrc.yaml": { mime: "application/yaml", lang: "yaml", previewable: "text" },
|
|
2008
|
+
".stylelintrc": { mime: "application/json", lang: "json", previewable: "text" },
|
|
2009
|
+
".stylelintrc.json": { mime: "application/json", lang: "json", previewable: "text" },
|
|
2010
|
+
".babelrc": { mime: "application/json", lang: "json", previewable: "text" },
|
|
2011
|
+
".babelrc.json": { mime: "application/json", lang: "json", previewable: "text" },
|
|
2012
|
+
".swcrc": { mime: "application/json", lang: "json", previewable: "text" },
|
|
2013
|
+
".huskyrc": { mime: "application/json", lang: "json", previewable: "text" },
|
|
2014
|
+
".atrrc": { mime: "application/json", lang: "json", previewable: "text" },
|
|
2015
|
+
".bashrc": { mime: "text/x-shellscript", lang: "bash", previewable: "text" },
|
|
2016
|
+
".zshrc": { mime: "text/x-shellscript", lang: "bash", previewable: "text" },
|
|
2017
|
+
".profile": { mime: "text/x-shellscript", lang: "bash", previewable: "text" },
|
|
2018
|
+
".bash_profile": { mime: "text/x-shellscript", lang: "bash", previewable: "text" },
|
|
2019
|
+
".zprofile": { mime: "text/x-shellscript", lang: "bash", previewable: "text" },
|
|
2020
|
+
".zshenv": { mime: "text/x-shellscript", lang: "bash", previewable: "text" },
|
|
2021
|
+
// ─── 文档约定(无扩展或带各种扩展,通常 markdown) ───
|
|
2022
|
+
"README": { mime: "text/plain", lang: "markdown", previewable: "text" },
|
|
2023
|
+
"CHANGELOG": { mime: "text/markdown", lang: "markdown", previewable: "text" },
|
|
2024
|
+
"LICENSE": { mime: "text/plain", lang: "txt", previewable: "text" },
|
|
2025
|
+
"COPYING": { mime: "text/plain", lang: "txt", previewable: "text" },
|
|
2026
|
+
"AUTHORS": { mime: "text/plain", lang: "txt", previewable: "text" },
|
|
2027
|
+
"CONTRIBUTORS": { mime: "text/plain", lang: "txt", previewable: "text" },
|
|
2028
|
+
"NOTICE": { mime: "text/plain", lang: "txt", previewable: "text" },
|
|
2029
|
+
"TODO": { mime: "text/plain", lang: "txt", previewable: "text" }
|
|
2030
|
+
};
|
|
2031
|
+
IMAGE_EXT_TO_MIME = {
|
|
2032
|
+
".png": "image/png",
|
|
2033
|
+
".jpg": "image/jpeg",
|
|
2034
|
+
".jpeg": "image/jpeg",
|
|
2035
|
+
".jpe": "image/jpeg",
|
|
2036
|
+
".jfif": "image/jpeg",
|
|
2037
|
+
".gif": "image/gif",
|
|
2038
|
+
".webp": "image/webp",
|
|
2039
|
+
".svg": "image/svg+xml",
|
|
2040
|
+
".bmp": "image/bmp",
|
|
2041
|
+
".ico": "image/x-icon",
|
|
2042
|
+
".avif": "image/avif",
|
|
2043
|
+
".apng": "image/apng",
|
|
2044
|
+
".tiff": "image/tiff",
|
|
2045
|
+
".tif": "image/tiff"
|
|
2046
|
+
};
|
|
2047
|
+
TEXT_EXT_TO_MIME = {
|
|
2048
|
+
// 纯文本 / 文档
|
|
2049
|
+
".txt": "text/plain",
|
|
2050
|
+
".text": "text/plain",
|
|
2051
|
+
".log": "text/plain",
|
|
2052
|
+
".md": "text/markdown",
|
|
2053
|
+
".markdown": "text/markdown",
|
|
2054
|
+
".mkd": "text/markdown",
|
|
2055
|
+
".mdx": "text/markdown",
|
|
2056
|
+
".mdc": "text/markdown",
|
|
2057
|
+
".rst": "text/x-rst",
|
|
2058
|
+
".adoc": "text/asciidoc",
|
|
2059
|
+
".asciidoc": "text/asciidoc",
|
|
2060
|
+
".tex": "text/x-tex",
|
|
2061
|
+
".bib": "text/x-bibtex",
|
|
2062
|
+
// 数据 / 配置
|
|
2063
|
+
".json": "application/json",
|
|
2064
|
+
".jsonc": "application/json",
|
|
2065
|
+
".json5": "application/json",
|
|
2066
|
+
".jsonl": "application/x-jsonlines",
|
|
2067
|
+
".ndjson": "application/x-ndjson",
|
|
2068
|
+
".yml": "application/yaml",
|
|
2069
|
+
".yaml": "application/yaml",
|
|
2070
|
+
".toml": "application/toml",
|
|
2071
|
+
".ini": "text/x-properties",
|
|
2072
|
+
".cfg": "text/x-properties",
|
|
2073
|
+
".conf": "text/plain",
|
|
2074
|
+
".properties": "text/x-properties",
|
|
2075
|
+
".env": "text/plain",
|
|
2076
|
+
".example": "text/plain",
|
|
2077
|
+
".gitignore": "text/x-gitignore",
|
|
2078
|
+
".dockerignore": "text/x-gitignore",
|
|
2079
|
+
".csv": "text/csv",
|
|
2080
|
+
".tsv": "text/tab-separated-values",
|
|
2081
|
+
".lock": "text/plain",
|
|
2082
|
+
".xml": "application/xml",
|
|
2083
|
+
".xsl": "application/xml",
|
|
2084
|
+
".xsd": "application/xml",
|
|
2085
|
+
".plist": "application/xml",
|
|
2086
|
+
".po": "text/x-gettext",
|
|
2087
|
+
".pot": "text/x-gettext",
|
|
2088
|
+
".proto": "text/x-protobuf",
|
|
2089
|
+
".graphql": "application/graphql",
|
|
2090
|
+
".gql": "application/graphql",
|
|
2091
|
+
// Web
|
|
2092
|
+
".html": "text/html",
|
|
2093
|
+
".htm": "text/html",
|
|
2094
|
+
".xhtml": "application/xhtml+xml",
|
|
2095
|
+
".css": "text/css",
|
|
2096
|
+
".scss": "text/x-scss",
|
|
2097
|
+
".sass": "text/x-sass",
|
|
2098
|
+
".less": "text/x-less",
|
|
2099
|
+
".styl": "text/x-stylus",
|
|
2100
|
+
".vue": "text/x-vue",
|
|
2101
|
+
".svelte": "text/x-svelte",
|
|
2102
|
+
".astro": "text/x-astro",
|
|
2103
|
+
".hbs": "text/x-handlebars",
|
|
2104
|
+
".handlebars": "text/x-handlebars",
|
|
2105
|
+
".pug": "text/x-pug",
|
|
2106
|
+
".jade": "text/x-pug",
|
|
2107
|
+
".ejs": "text/x-ejs",
|
|
2108
|
+
".erb": "text/x-erb",
|
|
2109
|
+
".liquid": "text/x-liquid",
|
|
2110
|
+
".twig": "text/x-twig",
|
|
2111
|
+
".njk": "text/x-jinja",
|
|
2112
|
+
".jinja": "text/x-jinja",
|
|
2113
|
+
".jinja2": "text/x-jinja",
|
|
2114
|
+
".j2": "text/x-jinja",
|
|
2115
|
+
// JS/TS 系
|
|
2116
|
+
".ts": "text/typescript",
|
|
2117
|
+
".tsx": "text/tsx",
|
|
2118
|
+
".mts": "text/typescript",
|
|
2119
|
+
".cts": "text/typescript",
|
|
2120
|
+
".js": "text/javascript",
|
|
2121
|
+
".jsx": "text/jsx",
|
|
2122
|
+
".mjs": "text/javascript",
|
|
2123
|
+
".cjs": "text/javascript",
|
|
2124
|
+
".d.ts": "text/typescript",
|
|
2125
|
+
// 系统/脚本
|
|
2126
|
+
".sh": "text/x-shellscript",
|
|
2127
|
+
".bash": "text/x-shellscript",
|
|
2128
|
+
".zsh": "text/x-shellscript",
|
|
2129
|
+
".fish": "text/x-shellscript",
|
|
2130
|
+
".ksh": "text/x-shellscript",
|
|
2131
|
+
".ps1": "text/x-powershell",
|
|
2132
|
+
".psm1": "text/x-powershell",
|
|
2133
|
+
".bat": "text/x-batchfile",
|
|
2134
|
+
".cmd": "text/x-batchfile",
|
|
2135
|
+
".awk": "text/x-awk",
|
|
2136
|
+
".sed": "text/x-sed",
|
|
2137
|
+
".nginx": "text/x-nginx-conf",
|
|
2138
|
+
".nginxconf": "text/x-nginx-conf",
|
|
2139
|
+
".service": "text/x-systemd",
|
|
2140
|
+
".timer": "text/x-systemd",
|
|
2141
|
+
// Python / Ruby / Go / Rust / 其它语言
|
|
2142
|
+
".py": "text/x-python",
|
|
2143
|
+
".pyi": "text/x-python",
|
|
2144
|
+
".pyw": "text/x-python",
|
|
2145
|
+
".rb": "text/x-ruby",
|
|
2146
|
+
".rake": "text/x-ruby",
|
|
2147
|
+
".go": "text/x-go",
|
|
2148
|
+
".rs": "text/x-rust",
|
|
2149
|
+
".php": "text/x-php",
|
|
2150
|
+
".php3": "text/x-php",
|
|
2151
|
+
".phtml": "text/x-php",
|
|
2152
|
+
".java": "text/x-java",
|
|
2153
|
+
".kt": "text/x-kotlin",
|
|
2154
|
+
".kts": "text/x-kotlin",
|
|
2155
|
+
".swift": "text/x-swift",
|
|
2156
|
+
".c": "text/x-c",
|
|
2157
|
+
".h": "text/x-c",
|
|
2158
|
+
".cc": "text/x-c++",
|
|
2159
|
+
".cpp": "text/x-c++",
|
|
2160
|
+
".cxx": "text/x-c++",
|
|
2161
|
+
".hh": "text/x-c++",
|
|
2162
|
+
".hpp": "text/x-c++",
|
|
2163
|
+
".hxx": "text/x-c++",
|
|
2164
|
+
".cs": "text/x-csharp",
|
|
2165
|
+
".fs": "text/x-fsharp",
|
|
2166
|
+
".fsx": "text/x-fsharp",
|
|
2167
|
+
".m": "text/x-objective-c",
|
|
2168
|
+
".mm": "text/x-objective-c++",
|
|
2169
|
+
".scala": "text/x-scala",
|
|
2170
|
+
".sc": "text/x-scala",
|
|
2171
|
+
".ex": "text/x-elixir",
|
|
2172
|
+
".exs": "text/x-elixir",
|
|
2173
|
+
".erl": "text/x-erlang",
|
|
2174
|
+
".hrl": "text/x-erlang",
|
|
2175
|
+
".hs": "text/x-haskell",
|
|
2176
|
+
".lhs": "text/x-haskell",
|
|
2177
|
+
".lua": "text/x-lua",
|
|
2178
|
+
".r": "text/x-r",
|
|
2179
|
+
".R": "text/x-r",
|
|
2180
|
+
".dart": "text/x-dart",
|
|
2181
|
+
".pl": "text/x-perl",
|
|
2182
|
+
".pm": "text/x-perl",
|
|
2183
|
+
".groovy": "text/x-groovy",
|
|
2184
|
+
".gradle": "text/x-groovy",
|
|
2185
|
+
".clj": "text/x-clojure",
|
|
2186
|
+
".cljs": "text/x-clojure",
|
|
2187
|
+
".cljc": "text/x-clojure",
|
|
2188
|
+
".lisp": "text/x-common-lisp",
|
|
2189
|
+
".lsp": "text/x-common-lisp",
|
|
2190
|
+
".el": "text/x-emacs-lisp",
|
|
2191
|
+
".scheme": "text/x-scheme",
|
|
2192
|
+
".scm": "text/x-scheme",
|
|
2193
|
+
".ml": "text/x-ocaml",
|
|
2194
|
+
".mli": "text/x-ocaml",
|
|
2195
|
+
".jl": "text/x-julia",
|
|
2196
|
+
".zig": "text/x-zig",
|
|
2197
|
+
".nim": "text/x-nim",
|
|
2198
|
+
".cr": "text/x-crystal",
|
|
2199
|
+
".v": "text/x-v",
|
|
2200
|
+
".vala": "text/x-vala",
|
|
2201
|
+
".vb": "text/x-vb",
|
|
2202
|
+
// 数据库 / 数据
|
|
2203
|
+
".sql": "application/sql",
|
|
2204
|
+
".psql": "application/sql",
|
|
2205
|
+
".mysql": "application/sql",
|
|
2206
|
+
".prisma": "text/x-prisma",
|
|
2207
|
+
// 配置 / IaC
|
|
2208
|
+
".nix": "text/x-nix",
|
|
2209
|
+
".tf": "text/x-terraform",
|
|
2210
|
+
".tfvars": "text/x-terraform",
|
|
2211
|
+
".hcl": "text/x-hcl",
|
|
2212
|
+
".dockerfile": "text/x-dockerfile",
|
|
2213
|
+
".cmake": "text/x-cmake",
|
|
2214
|
+
".make": "text/x-makefile",
|
|
2215
|
+
".mk": "text/x-makefile",
|
|
2216
|
+
".makefile": "text/x-makefile",
|
|
2217
|
+
".gn": "text/x-gn",
|
|
2218
|
+
".bzl": "text/x-bzl",
|
|
2219
|
+
".bazel": "text/x-bzl",
|
|
2220
|
+
// 其它
|
|
2221
|
+
".diff": "text/x-diff",
|
|
2222
|
+
".patch": "text/x-diff",
|
|
2223
|
+
".regex": "text/x-regex",
|
|
2224
|
+
".http": "message/http",
|
|
2225
|
+
".cmake.in": "text/x-cmake",
|
|
2226
|
+
".gitcommit": "text/x-git-commit"
|
|
2227
|
+
};
|
|
2228
|
+
LANG_MAP = {
|
|
2229
|
+
// 纯文本 / 文档
|
|
2230
|
+
".md": "markdown",
|
|
2231
|
+
".markdown": "markdown",
|
|
2232
|
+
".mkd": "markdown",
|
|
2233
|
+
".mdx": "mdx",
|
|
2234
|
+
".mdc": "mdc",
|
|
2235
|
+
".rst": "rst",
|
|
2236
|
+
".adoc": "asciidoc",
|
|
2237
|
+
".asciidoc": "asciidoc",
|
|
2238
|
+
".tex": "latex",
|
|
2239
|
+
".bib": "bibtex",
|
|
2240
|
+
".log": "log",
|
|
2241
|
+
// 数据 / 配置
|
|
2242
|
+
".json": "json",
|
|
2243
|
+
".jsonc": "jsonc",
|
|
2244
|
+
".json5": "json5",
|
|
2245
|
+
".jsonl": "jsonl",
|
|
2246
|
+
".ndjson": "jsonl",
|
|
2247
|
+
".yml": "yaml",
|
|
2248
|
+
".yaml": "yaml",
|
|
2249
|
+
".toml": "toml",
|
|
2250
|
+
".ini": "ini",
|
|
2251
|
+
".cfg": "ini",
|
|
2252
|
+
".properties": "ini",
|
|
2253
|
+
".conf": "ini",
|
|
2254
|
+
".env": "dotenv",
|
|
2255
|
+
".example": "dotenv",
|
|
2256
|
+
".xml": "xml",
|
|
2257
|
+
".xsl": "xml",
|
|
2258
|
+
".xsd": "xml",
|
|
2259
|
+
".plist": "xml",
|
|
2260
|
+
".proto": "proto",
|
|
2261
|
+
".graphql": "graphql",
|
|
2262
|
+
".gql": "graphql",
|
|
2263
|
+
".po": "po",
|
|
2264
|
+
".pot": "po",
|
|
2265
|
+
// Web
|
|
2266
|
+
".html": "html",
|
|
2267
|
+
".htm": "html",
|
|
2268
|
+
".xhtml": "html",
|
|
2269
|
+
".css": "css",
|
|
2270
|
+
".scss": "scss",
|
|
2271
|
+
".sass": "sass",
|
|
2272
|
+
".less": "less",
|
|
2273
|
+
".styl": "stylus",
|
|
2274
|
+
".vue": "vue",
|
|
2275
|
+
".svelte": "svelte",
|
|
2276
|
+
".astro": "astro",
|
|
2277
|
+
".hbs": "handlebars",
|
|
2278
|
+
".handlebars": "handlebars",
|
|
2279
|
+
".pug": "pug",
|
|
2280
|
+
".jade": "pug",
|
|
2281
|
+
".erb": "erb",
|
|
2282
|
+
".liquid": "liquid",
|
|
2283
|
+
".twig": "twig",
|
|
2284
|
+
".jinja": "jinja",
|
|
2285
|
+
".jinja2": "jinja",
|
|
2286
|
+
".j2": "jinja",
|
|
2287
|
+
".njk": "jinja",
|
|
2288
|
+
// JS / TS 系
|
|
2289
|
+
".ts": "ts",
|
|
2290
|
+
".tsx": "tsx",
|
|
2291
|
+
".mts": "ts",
|
|
2292
|
+
".cts": "ts",
|
|
2293
|
+
".js": "js",
|
|
2294
|
+
".jsx": "jsx",
|
|
2295
|
+
".mjs": "js",
|
|
2296
|
+
".cjs": "js",
|
|
2297
|
+
".d.ts": "ts",
|
|
2298
|
+
// 系统 / 脚本
|
|
2299
|
+
".sh": "shell",
|
|
2300
|
+
".bash": "bash",
|
|
2301
|
+
".zsh": "bash",
|
|
2302
|
+
".fish": "fish",
|
|
2303
|
+
".ksh": "shell",
|
|
2304
|
+
".ps1": "powershell",
|
|
2305
|
+
".psm1": "powershell",
|
|
2306
|
+
".bat": "bat",
|
|
2307
|
+
".cmd": "bat",
|
|
2308
|
+
".awk": "awk",
|
|
2309
|
+
".nginx": "nginx",
|
|
2310
|
+
".nginxconf": "nginx",
|
|
2311
|
+
".service": "systemd",
|
|
2312
|
+
".timer": "systemd",
|
|
2313
|
+
".dockerfile": "docker",
|
|
2314
|
+
// 语言
|
|
2315
|
+
".py": "python",
|
|
2316
|
+
".pyi": "python",
|
|
2317
|
+
".pyw": "python",
|
|
2318
|
+
".rb": "ruby",
|
|
2319
|
+
".rake": "ruby",
|
|
2320
|
+
".go": "go",
|
|
2321
|
+
".rs": "rust",
|
|
2322
|
+
".php": "php",
|
|
2323
|
+
".java": "java",
|
|
2324
|
+
".kt": "kotlin",
|
|
2325
|
+
".kts": "kotlin",
|
|
2326
|
+
".swift": "swift",
|
|
2327
|
+
".c": "c",
|
|
2328
|
+
".h": "c",
|
|
2329
|
+
".cc": "cpp",
|
|
2330
|
+
".cpp": "cpp",
|
|
2331
|
+
".cxx": "cpp",
|
|
2332
|
+
".hh": "cpp",
|
|
2333
|
+
".hpp": "cpp",
|
|
2334
|
+
".hxx": "cpp",
|
|
2335
|
+
".cs": "csharp",
|
|
2336
|
+
".fs": "fsharp",
|
|
2337
|
+
".fsx": "fsharp",
|
|
2338
|
+
".m": "objective-c",
|
|
2339
|
+
".mm": "objective-cpp",
|
|
2340
|
+
".scala": "scala",
|
|
2341
|
+
".sc": "scala",
|
|
2342
|
+
".ex": "elixir",
|
|
2343
|
+
".exs": "elixir",
|
|
2344
|
+
".erl": "erlang",
|
|
2345
|
+
".hrl": "erlang",
|
|
2346
|
+
".hs": "haskell",
|
|
2347
|
+
".lhs": "haskell",
|
|
2348
|
+
".lua": "lua",
|
|
2349
|
+
".r": "r",
|
|
2350
|
+
".R": "r",
|
|
2351
|
+
".dart": "dart",
|
|
2352
|
+
".pl": "perl",
|
|
2353
|
+
".pm": "perl",
|
|
2354
|
+
".groovy": "groovy",
|
|
2355
|
+
".gradle": "groovy",
|
|
2356
|
+
".clj": "clojure",
|
|
2357
|
+
".cljs": "clojure",
|
|
2358
|
+
".cljc": "clojure",
|
|
2359
|
+
".lisp": "common-lisp",
|
|
2360
|
+
".lsp": "common-lisp",
|
|
2361
|
+
".el": "emacs-lisp",
|
|
2362
|
+
".scm": "scheme",
|
|
2363
|
+
".ml": "ocaml",
|
|
2364
|
+
".mli": "ocaml",
|
|
2365
|
+
".jl": "julia",
|
|
2366
|
+
".zig": "zig",
|
|
2367
|
+
".nim": "nim",
|
|
2368
|
+
".cr": "crystal",
|
|
2369
|
+
".v": "v",
|
|
2370
|
+
".vb": "vb",
|
|
2371
|
+
// 数据库
|
|
2372
|
+
".sql": "sql",
|
|
2373
|
+
".psql": "sql",
|
|
2374
|
+
".mysql": "sql",
|
|
2375
|
+
".prisma": "prisma",
|
|
2376
|
+
// IaC
|
|
2377
|
+
".nix": "nix",
|
|
2378
|
+
".tf": "terraform",
|
|
2379
|
+
".tfvars": "terraform",
|
|
2380
|
+
".hcl": "hcl",
|
|
2381
|
+
".cmake": "cmake",
|
|
2382
|
+
".make": "makefile",
|
|
2383
|
+
".mk": "makefile",
|
|
2384
|
+
".makefile": "makefile",
|
|
2385
|
+
// 其它
|
|
2386
|
+
".diff": "diff",
|
|
2387
|
+
".patch": "diff",
|
|
2388
|
+
".svg": "xml",
|
|
2389
|
+
".http": "http"
|
|
2390
|
+
};
|
|
2391
|
+
}
|
|
2392
|
+
});
|
|
2393
|
+
|
|
2394
|
+
// backend/dist/files/file-kind.js
|
|
2395
|
+
function getFileKind(stat3) {
|
|
2396
|
+
if (stat3.isSymbolicLink())
|
|
2397
|
+
return "symlink";
|
|
2398
|
+
if (stat3.isDirectory())
|
|
2399
|
+
return "dir";
|
|
2400
|
+
if (stat3.isFile())
|
|
2401
|
+
return "file";
|
|
2402
|
+
return "other";
|
|
2403
|
+
}
|
|
2404
|
+
var init_file_kind = __esm({
|
|
2405
|
+
"backend/dist/files/file-kind.js"() {
|
|
2406
|
+
"use strict";
|
|
2407
|
+
}
|
|
2408
|
+
});
|
|
2409
|
+
|
|
2410
|
+
// backend/dist/files/list-dir.js
|
|
2411
|
+
import { readdir, lstat } from "node:fs/promises";
|
|
2412
|
+
import { join } from "node:path";
|
|
2413
|
+
async function listDir(dirPath) {
|
|
2414
|
+
const dirents = await readdir(dirPath, { withFileTypes: true });
|
|
2415
|
+
const results = await Promise.all(dirents.map((dirent) => lstat(join(dirPath, dirent.name)).then((stat3) => {
|
|
2416
|
+
const kind = getFileKind(stat3);
|
|
2417
|
+
const entry = {
|
|
2418
|
+
name: dirent.name,
|
|
2419
|
+
kind,
|
|
2420
|
+
size: kind === "dir" ? 0 : stat3.size,
|
|
2421
|
+
mtimeMs: stat3.mtimeMs,
|
|
2422
|
+
hidden: dirent.name.startsWith(".")
|
|
2423
|
+
};
|
|
2424
|
+
if (kind === "file") {
|
|
2425
|
+
const { mime, previewable } = detectMime(dirent.name);
|
|
2426
|
+
entry.mime = mime;
|
|
2427
|
+
entry.previewable = previewable;
|
|
2428
|
+
}
|
|
2429
|
+
return entry;
|
|
2430
|
+
}, () => void 0)));
|
|
2431
|
+
return results.filter((e) => e !== void 0);
|
|
2432
|
+
}
|
|
2433
|
+
var init_list_dir = __esm({
|
|
2434
|
+
"backend/dist/files/list-dir.js"() {
|
|
2435
|
+
"use strict";
|
|
2436
|
+
init_mime_detect();
|
|
2437
|
+
init_file_kind();
|
|
2438
|
+
}
|
|
2439
|
+
});
|
|
2440
|
+
|
|
2441
|
+
// backend/dist/files/read-file.js
|
|
2442
|
+
import { open, stat } from "node:fs/promises";
|
|
2443
|
+
function probeHasAnsi(buf) {
|
|
2444
|
+
for (let i = 0; i < buf.length - 1; i++) {
|
|
2445
|
+
if (buf[i] === 27 && buf[i + 1] === 91)
|
|
2446
|
+
return true;
|
|
2447
|
+
}
|
|
2448
|
+
return false;
|
|
2449
|
+
}
|
|
2450
|
+
async function readTextFile(absPath) {
|
|
2451
|
+
const st = await stat(absPath);
|
|
2452
|
+
const size = st.size;
|
|
2453
|
+
const fh = await open(absPath, "r");
|
|
2454
|
+
try {
|
|
2455
|
+
const probeLen = Math.min(NUL_PROBE_BYTES, size);
|
|
2456
|
+
let hasAnsi = false;
|
|
2457
|
+
if (probeLen > 0) {
|
|
2458
|
+
const probe = Buffer.alloc(probeLen);
|
|
2459
|
+
await fh.read(probe, 0, probeLen, 0);
|
|
2460
|
+
if (probe.includes(0)) {
|
|
2461
|
+
throw new FileError(ErrorCode.FILE_BINARY, "file contains NUL bytes (binary)", 409);
|
|
2462
|
+
}
|
|
2463
|
+
hasAnsi = probeHasAnsi(probe);
|
|
2464
|
+
}
|
|
2465
|
+
const readLen = Math.min(size, FILE_READ_MAX_BYTES);
|
|
2466
|
+
const buf = Buffer.alloc(readLen);
|
|
2467
|
+
if (readLen > 0) {
|
|
2468
|
+
await fh.read(buf, 0, readLen, 0);
|
|
2469
|
+
}
|
|
2470
|
+
const content = buf.toString("utf-8");
|
|
2471
|
+
const truncated = size > FILE_READ_MAX_BYTES;
|
|
2472
|
+
if (content.length > 0) {
|
|
2473
|
+
let replacements = 0;
|
|
2474
|
+
for (const ch of content) {
|
|
2475
|
+
if (ch === REPLACEMENT_CHAR)
|
|
2476
|
+
replacements++;
|
|
2477
|
+
}
|
|
2478
|
+
const density = replacements / content.length;
|
|
2479
|
+
if (density > REPLACEMENT_DENSITY_LIMIT) {
|
|
2480
|
+
throw new FileError(ErrorCode.FILE_BINARY, `non-UTF8 content (replacement density ${(density * 100).toFixed(1)}%)`, 409);
|
|
2481
|
+
}
|
|
2482
|
+
}
|
|
2483
|
+
return { content, truncated, size, hasAnsi };
|
|
2484
|
+
} finally {
|
|
2485
|
+
await fh.close();
|
|
2486
|
+
}
|
|
2487
|
+
}
|
|
2488
|
+
var NUL_PROBE_BYTES, REPLACEMENT_CHAR, REPLACEMENT_DENSITY_LIMIT;
|
|
2489
|
+
var init_read_file = __esm({
|
|
2490
|
+
"backend/dist/files/read-file.js"() {
|
|
2491
|
+
"use strict";
|
|
2492
|
+
init_dist();
|
|
2493
|
+
init_errors2();
|
|
2494
|
+
NUL_PROBE_BYTES = 4 * 1024;
|
|
2495
|
+
REPLACEMENT_CHAR = "\uFFFD";
|
|
2496
|
+
REPLACEMENT_DENSITY_LIMIT = 0.05;
|
|
2497
|
+
}
|
|
2498
|
+
});
|
|
2499
|
+
|
|
2500
|
+
// backend/dist/files/search-engine.js
|
|
2501
|
+
import { opendir, open as open2, stat as stat2 } from "node:fs/promises";
|
|
2502
|
+
import { createInterface } from "node:readline";
|
|
2503
|
+
import { join as join2 } from "node:path";
|
|
2504
|
+
async function runSearch(opts) {
|
|
2505
|
+
const start = Date.now();
|
|
2506
|
+
let scanned = 0;
|
|
2507
|
+
let nameHits = 0;
|
|
2508
|
+
let contentHits = 0;
|
|
2509
|
+
let truncated = false;
|
|
2510
|
+
const matchers = compileMatchers(opts);
|
|
2511
|
+
const { nameMatch, contentMatch } = matchers;
|
|
2512
|
+
const queue = [];
|
|
2513
|
+
let walkDone = false;
|
|
2514
|
+
let waitResolve = null;
|
|
2515
|
+
let waitPromise = new Promise((r) => {
|
|
2516
|
+
waitResolve = r;
|
|
2517
|
+
});
|
|
2518
|
+
const wakeWorkers = () => {
|
|
2519
|
+
if (waitResolve) {
|
|
2520
|
+
const r = waitResolve;
|
|
2521
|
+
waitResolve = null;
|
|
2522
|
+
r();
|
|
2523
|
+
waitPromise = new Promise((rr) => {
|
|
2524
|
+
waitResolve = rr;
|
|
2525
|
+
});
|
|
2526
|
+
}
|
|
2527
|
+
};
|
|
2528
|
+
const openDirs = /* @__PURE__ */ new Set();
|
|
2529
|
+
const abortAllDirs = () => {
|
|
2530
|
+
for (const d of openDirs) {
|
|
2531
|
+
d.close().catch(() => {
|
|
2532
|
+
});
|
|
2533
|
+
}
|
|
2534
|
+
openDirs.clear();
|
|
2535
|
+
};
|
|
2536
|
+
const onCancel = () => {
|
|
2537
|
+
wakeWorkers();
|
|
2538
|
+
abortAllDirs();
|
|
2539
|
+
};
|
|
2540
|
+
opts.cancelSignal?.addEventListener("abort", onCancel, { once: true });
|
|
2541
|
+
const totalTimeoutHandle = setTimeout(onCancel, SEARCH_TOTAL_TIMEOUT_MS);
|
|
2542
|
+
totalTimeoutHandle.unref();
|
|
2543
|
+
async function walk2(dir, depth) {
|
|
2544
|
+
if (depth > MAX_DEPTH)
|
|
2545
|
+
return;
|
|
2546
|
+
if (scanned >= MAX_ENTRIES_SCANNED) {
|
|
2547
|
+
truncated = true;
|
|
2548
|
+
return;
|
|
2549
|
+
}
|
|
2550
|
+
if (Date.now() - start > SEARCH_TOTAL_TIMEOUT_MS) {
|
|
2551
|
+
truncated = true;
|
|
2552
|
+
return;
|
|
2553
|
+
}
|
|
2554
|
+
if (opts.cancelSignal?.aborted)
|
|
2555
|
+
return;
|
|
2556
|
+
let dirh;
|
|
2557
|
+
try {
|
|
2558
|
+
dirh = await opendir(dir);
|
|
2559
|
+
} catch {
|
|
2560
|
+
return;
|
|
2561
|
+
}
|
|
2562
|
+
openDirs.add(dirh);
|
|
2563
|
+
if (opts.cancelSignal?.aborted) {
|
|
2564
|
+
openDirs.delete(dirh);
|
|
2565
|
+
await dirh.close().catch(() => {
|
|
2566
|
+
});
|
|
2567
|
+
return;
|
|
2568
|
+
}
|
|
2569
|
+
try {
|
|
2570
|
+
for await (const ent of dirh) {
|
|
2571
|
+
scanned++;
|
|
2572
|
+
if (scanned >= MAX_ENTRIES_SCANNED) {
|
|
2573
|
+
truncated = true;
|
|
2574
|
+
break;
|
|
2575
|
+
}
|
|
2576
|
+
if (Date.now() - start > SEARCH_TOTAL_TIMEOUT_MS) {
|
|
2577
|
+
truncated = true;
|
|
2578
|
+
break;
|
|
2579
|
+
}
|
|
2580
|
+
if (opts.cancelSignal?.aborted)
|
|
2581
|
+
break;
|
|
2582
|
+
const full = join2(dir, ent.name);
|
|
2583
|
+
if (ent.isDirectory()) {
|
|
2584
|
+
if (IGNORE_DIRS.has(ent.name))
|
|
2585
|
+
continue;
|
|
2586
|
+
if (checkWorkdir(full, opts.policy.allow, opts.policy.deny) !== null)
|
|
2587
|
+
continue;
|
|
2588
|
+
await walk2(full, depth + 1);
|
|
2589
|
+
continue;
|
|
2590
|
+
}
|
|
2591
|
+
if (!ent.isFile())
|
|
2592
|
+
continue;
|
|
2593
|
+
if (opts.mode !== "content" && nameMatch(ent.name) && nameHits < SEARCH_MAX_NAME_RESULTS) {
|
|
2594
|
+
try {
|
|
2595
|
+
const st = await stat2(full);
|
|
2596
|
+
opts.emit({ kind: "name", path: full, size: st.size, mtimeMs: st.mtimeMs });
|
|
2597
|
+
nameHits++;
|
|
2598
|
+
} catch {
|
|
2599
|
+
}
|
|
2600
|
+
}
|
|
2601
|
+
if (opts.mode !== "name") {
|
|
2602
|
+
queue.push(full);
|
|
2603
|
+
wakeWorkers();
|
|
2604
|
+
}
|
|
2605
|
+
}
|
|
2606
|
+
} catch {
|
|
2607
|
+
} finally {
|
|
2608
|
+
openDirs.delete(dirh);
|
|
2609
|
+
await dirh.close().catch(() => {
|
|
2610
|
+
});
|
|
2611
|
+
}
|
|
2612
|
+
}
|
|
2613
|
+
const workers = [];
|
|
2614
|
+
if (opts.mode !== "name") {
|
|
2615
|
+
for (let i = 0; i < SEARCH_CONCURRENCY; i++) {
|
|
2616
|
+
workers.push((async () => {
|
|
2617
|
+
while (true) {
|
|
2618
|
+
if (contentHits >= SEARCH_MAX_CONTENT_RESULTS) {
|
|
2619
|
+
truncated = true;
|
|
2620
|
+
return;
|
|
2621
|
+
}
|
|
2622
|
+
if (Date.now() - start > SEARCH_TOTAL_TIMEOUT_MS) {
|
|
2623
|
+
truncated = true;
|
|
2624
|
+
return;
|
|
2625
|
+
}
|
|
2626
|
+
if (opts.cancelSignal?.aborted)
|
|
2627
|
+
return;
|
|
2628
|
+
const f = queue.shift();
|
|
2629
|
+
if (!f) {
|
|
2630
|
+
if (walkDone)
|
|
2631
|
+
return;
|
|
2632
|
+
await waitPromise;
|
|
2633
|
+
continue;
|
|
2634
|
+
}
|
|
2635
|
+
await scanFile(f);
|
|
2636
|
+
}
|
|
2637
|
+
})());
|
|
2638
|
+
}
|
|
2639
|
+
}
|
|
2640
|
+
try {
|
|
2641
|
+
await walk2(opts.scope, 0);
|
|
2642
|
+
} finally {
|
|
2643
|
+
walkDone = true;
|
|
2644
|
+
wakeWorkers();
|
|
2645
|
+
}
|
|
2646
|
+
await Promise.all(workers);
|
|
2647
|
+
async function scanFile(path) {
|
|
2648
|
+
const fileStart = Date.now();
|
|
2649
|
+
let st;
|
|
2650
|
+
try {
|
|
2651
|
+
st = await stat2(path);
|
|
2652
|
+
} catch {
|
|
2653
|
+
return;
|
|
2654
|
+
}
|
|
2655
|
+
if (st.size > FILE_MAX_SCAN_BYTES)
|
|
2656
|
+
return;
|
|
2657
|
+
let fh;
|
|
2658
|
+
try {
|
|
2659
|
+
fh = await open2(path, "r");
|
|
2660
|
+
} catch {
|
|
2661
|
+
return;
|
|
2662
|
+
}
|
|
2663
|
+
let isBinary = false;
|
|
2664
|
+
try {
|
|
2665
|
+
const probeLen = Math.min(NUL_PROBE_BYTES2, st.size);
|
|
2666
|
+
if (probeLen > 0) {
|
|
2667
|
+
const buf = Buffer.alloc(probeLen);
|
|
2668
|
+
await fh.read(buf, 0, probeLen, 0);
|
|
2669
|
+
if (buf.includes(0))
|
|
2670
|
+
isBinary = true;
|
|
2671
|
+
}
|
|
2672
|
+
} catch {
|
|
2673
|
+
isBinary = true;
|
|
2674
|
+
}
|
|
2675
|
+
if (isBinary) {
|
|
2676
|
+
await fh.close();
|
|
2677
|
+
return;
|
|
2678
|
+
}
|
|
2679
|
+
await new Promise((resolveP) => {
|
|
2680
|
+
const stream = fh.createReadStream({ encoding: "utf-8", autoClose: true, start: 0 });
|
|
2681
|
+
const rl = createInterface({ input: stream, crlfDelay: Infinity });
|
|
2682
|
+
let lineNo = 0;
|
|
2683
|
+
let stopped = false;
|
|
2684
|
+
const stop = () => {
|
|
2685
|
+
if (stopped)
|
|
2686
|
+
return;
|
|
2687
|
+
stopped = true;
|
|
2688
|
+
rl.close();
|
|
2689
|
+
stream.destroy();
|
|
2690
|
+
resolveP();
|
|
2691
|
+
};
|
|
2692
|
+
rl.on("line", (line) => {
|
|
2693
|
+
lineNo++;
|
|
2694
|
+
if (Date.now() - fileStart > SEARCH_FILE_TIMEOUT_MS) {
|
|
2695
|
+
stop();
|
|
2696
|
+
return;
|
|
2697
|
+
}
|
|
2698
|
+
if (contentHits >= SEARCH_MAX_CONTENT_RESULTS) {
|
|
2699
|
+
truncated = true;
|
|
2700
|
+
stop();
|
|
2701
|
+
return;
|
|
2702
|
+
}
|
|
2703
|
+
if (opts.cancelSignal?.aborted) {
|
|
2704
|
+
stop();
|
|
2705
|
+
return;
|
|
2706
|
+
}
|
|
2707
|
+
const m = contentMatch(line);
|
|
2708
|
+
if (m) {
|
|
2709
|
+
const preview = line.length > PREVIEW_MAX_CHARS ? line.slice(0, PREVIEW_MAX_CHARS) : line;
|
|
2710
|
+
opts.emit({
|
|
2711
|
+
kind: "content",
|
|
2712
|
+
path,
|
|
2713
|
+
line: lineNo,
|
|
2714
|
+
preview,
|
|
2715
|
+
matchStart: Math.min(m.start, preview.length),
|
|
2716
|
+
matchEnd: Math.min(m.end, preview.length)
|
|
2717
|
+
});
|
|
2718
|
+
contentHits++;
|
|
2719
|
+
}
|
|
2720
|
+
});
|
|
2721
|
+
rl.on("close", () => resolveP());
|
|
2722
|
+
rl.on("error", () => stop());
|
|
2723
|
+
stream.on("error", () => stop());
|
|
2724
|
+
});
|
|
2725
|
+
}
|
|
2726
|
+
clearTimeout(totalTimeoutHandle);
|
|
2727
|
+
return {
|
|
2728
|
+
truncated,
|
|
2729
|
+
scanned,
|
|
2730
|
+
elapsedMs: Date.now() - start
|
|
2731
|
+
};
|
|
2732
|
+
}
|
|
2733
|
+
function compileMatchers(opts) {
|
|
2734
|
+
if (opts.regex) {
|
|
2735
|
+
if (opts.q.includes("\n")) {
|
|
2736
|
+
throw new FileError(ErrorCode.SEARCH_INVALID_Q, "cross-line regex disallowed", 400);
|
|
2737
|
+
}
|
|
2738
|
+
let re;
|
|
2739
|
+
try {
|
|
2740
|
+
re = new RegExp(opts.q, opts.caseSensitive ? "" : "i");
|
|
2741
|
+
} catch (err) {
|
|
2742
|
+
throw new FileError(ErrorCode.SEARCH_INVALID_Q, `invalid regex: ${String(err)}`, 400);
|
|
2743
|
+
}
|
|
2744
|
+
return {
|
|
2745
|
+
nameMatch: (n) => re.test(n),
|
|
2746
|
+
contentMatch: (line) => {
|
|
2747
|
+
const m = re.exec(line);
|
|
2748
|
+
re.lastIndex = 0;
|
|
2749
|
+
return m ? { start: m.index, end: m.index + m[0].length } : null;
|
|
2750
|
+
}
|
|
2751
|
+
};
|
|
2752
|
+
}
|
|
2753
|
+
const needle = opts.caseSensitive ? opts.q : opts.q.toLowerCase();
|
|
2754
|
+
return {
|
|
2755
|
+
nameMatch: (n) => (opts.caseSensitive ? n : n.toLowerCase()).includes(needle),
|
|
2756
|
+
contentMatch: (line) => {
|
|
2757
|
+
const hay = opts.caseSensitive ? line : line.toLowerCase();
|
|
2758
|
+
const idx = hay.indexOf(needle);
|
|
2759
|
+
return idx === -1 ? null : { start: idx, end: idx + needle.length };
|
|
2760
|
+
}
|
|
2761
|
+
};
|
|
2762
|
+
}
|
|
2763
|
+
var IGNORE_DIRS, MAX_DEPTH, MAX_ENTRIES_SCANNED, NUL_PROBE_BYTES2, PREVIEW_MAX_CHARS, FILE_MAX_SCAN_BYTES;
|
|
2764
|
+
var init_search_engine = __esm({
|
|
2765
|
+
"backend/dist/files/search-engine.js"() {
|
|
2766
|
+
"use strict";
|
|
2767
|
+
init_dist();
|
|
2768
|
+
init_errors2();
|
|
2769
|
+
init_workdir_policy();
|
|
2770
|
+
IGNORE_DIRS = /* @__PURE__ */ new Set([
|
|
2771
|
+
"node_modules",
|
|
2772
|
+
".git",
|
|
2773
|
+
".svn",
|
|
2774
|
+
".hg",
|
|
2775
|
+
"dist",
|
|
2776
|
+
"build",
|
|
2777
|
+
".next",
|
|
2778
|
+
".turbo",
|
|
2779
|
+
".cache",
|
|
2780
|
+
"target",
|
|
2781
|
+
".venv",
|
|
2782
|
+
"__pycache__",
|
|
2783
|
+
".DS_Store"
|
|
2784
|
+
]);
|
|
2785
|
+
MAX_DEPTH = 6;
|
|
2786
|
+
MAX_ENTRIES_SCANNED = 5e3;
|
|
2787
|
+
NUL_PROBE_BYTES2 = 4 * 1024;
|
|
2788
|
+
PREVIEW_MAX_CHARS = 200;
|
|
2789
|
+
FILE_MAX_SCAN_BYTES = 2 * 1024 * 1024;
|
|
2790
|
+
}
|
|
2791
|
+
});
|
|
2792
|
+
|
|
2793
|
+
// backend/dist/files/wikilink-resolver.js
|
|
2794
|
+
import { promises as fsp } from "node:fs";
|
|
2795
|
+
import { watch as watch2 } from "node:fs";
|
|
2796
|
+
import { join as join3, relative, dirname as dirname3, basename as basename3, extname as extname2, sep } from "node:path";
|
|
2797
|
+
function makeResult(resolved, fragment) {
|
|
2798
|
+
return fragment ? { resolved, fragment } : { resolved };
|
|
2799
|
+
}
|
|
2800
|
+
function makeBroken(fragment) {
|
|
2801
|
+
return fragment ? { broken: true, fragment } : { broken: true };
|
|
2802
|
+
}
|
|
2803
|
+
function splitFragment(target) {
|
|
2804
|
+
const piped = target.split("|")[0];
|
|
2805
|
+
const hashIdx = piped.indexOf("#");
|
|
2806
|
+
if (hashIdx < 0)
|
|
2807
|
+
return { pathPart: piped.trim() };
|
|
2808
|
+
const pathPart = piped.slice(0, hashIdx).trim();
|
|
2809
|
+
const frag = piped.slice(hashIdx + 1).trim();
|
|
2810
|
+
if (frag.startsWith("^")) {
|
|
2811
|
+
return { pathPart, fragment: { kind: "block", id: frag.slice(1) } };
|
|
2812
|
+
}
|
|
2813
|
+
return { pathPart, fragment: { kind: "heading", id: frag } };
|
|
2814
|
+
}
|
|
2815
|
+
function stripExt(name) {
|
|
2816
|
+
const ext = extname2(name).toLowerCase();
|
|
2817
|
+
if (MD_EXTS.has(ext))
|
|
2818
|
+
return name.slice(0, -ext.length);
|
|
2819
|
+
return name;
|
|
2820
|
+
}
|
|
2821
|
+
function pickShortestPath(from, candidates) {
|
|
2822
|
+
return candidates.map((c2) => ({ c: c2, common: countCommonDirSegments(from, c2) })).sort((a, b) => {
|
|
2823
|
+
if (b.common !== a.common)
|
|
2824
|
+
return b.common - a.common;
|
|
2825
|
+
return a.c < b.c ? -1 : a.c > b.c ? 1 : 0;
|
|
2826
|
+
})[0].c;
|
|
2827
|
+
}
|
|
2828
|
+
function countCommonDirSegments(a, b) {
|
|
2829
|
+
const da = a.split("/").slice(0, -1);
|
|
2830
|
+
const db = b.split("/").slice(0, -1);
|
|
2831
|
+
let i = 0;
|
|
2832
|
+
while (i < da.length && i < db.length && da[i] === db[i])
|
|
2833
|
+
i++;
|
|
2834
|
+
return i;
|
|
2835
|
+
}
|
|
2836
|
+
async function walk(root, cur, onFile) {
|
|
2837
|
+
let ents;
|
|
2838
|
+
try {
|
|
2839
|
+
ents = await fsp.readdir(cur, { withFileTypes: true });
|
|
2840
|
+
} catch (err) {
|
|
2841
|
+
logger.debug({ err, dir: cur }, "WorkspaceIndex.walk: readdir failed (skipped)");
|
|
2842
|
+
return;
|
|
2843
|
+
}
|
|
2844
|
+
const subWalks = [];
|
|
2845
|
+
for (const e of ents) {
|
|
2846
|
+
if (EXCLUDED_DIRS.has(e.name))
|
|
2847
|
+
continue;
|
|
2848
|
+
const full = join3(cur, e.name);
|
|
2849
|
+
if (e.isSymbolicLink()) {
|
|
2850
|
+
try {
|
|
2851
|
+
const real = await fsp.realpath(full);
|
|
2852
|
+
const r = relative(root, real);
|
|
2853
|
+
if (r.startsWith("..") || r === "" || r.startsWith(sep + ".."))
|
|
2854
|
+
continue;
|
|
2855
|
+
} catch {
|
|
2856
|
+
continue;
|
|
2857
|
+
}
|
|
2858
|
+
}
|
|
2859
|
+
if (e.isDirectory() || e.isSymbolicLink() && await isDir(full)) {
|
|
2860
|
+
subWalks.push(walk(root, full, onFile));
|
|
2861
|
+
} else if (e.isFile() || e.isSymbolicLink()) {
|
|
2862
|
+
const rel = relative(root, full).split(sep).join("/");
|
|
2863
|
+
onFile(rel);
|
|
2864
|
+
}
|
|
2865
|
+
}
|
|
2866
|
+
if (subWalks.length > 0)
|
|
2867
|
+
await Promise.all(subWalks);
|
|
2868
|
+
}
|
|
2869
|
+
async function isDir(p) {
|
|
2870
|
+
try {
|
|
2871
|
+
return (await fsp.stat(p)).isDirectory();
|
|
2872
|
+
} catch {
|
|
2873
|
+
return false;
|
|
2874
|
+
}
|
|
2875
|
+
}
|
|
2876
|
+
var MD_EXTS, REBUILD_INTERVAL_MS, STALE_REBUILD_MS, WorkspaceIndex, EXCLUDED_DIRS;
|
|
2877
|
+
var init_wikilink_resolver = __esm({
|
|
2878
|
+
"backend/dist/files/wikilink-resolver.js"() {
|
|
2879
|
+
"use strict";
|
|
2880
|
+
init_logger();
|
|
2881
|
+
MD_EXTS = /* @__PURE__ */ new Set([".md", ".markdown"]);
|
|
2882
|
+
REBUILD_INTERVAL_MS = 5 * 60 * 1e3;
|
|
2883
|
+
STALE_REBUILD_MS = 30 * 1e3;
|
|
2884
|
+
WorkspaceIndex = class {
|
|
2885
|
+
cwd;
|
|
2886
|
+
/** lowercased basename(去扩展名) → 相对 cwd 路径数组(已 sorted) */
|
|
2887
|
+
byBasename = /* @__PURE__ */ new Map();
|
|
2888
|
+
built = false;
|
|
2889
|
+
buildPromise = null;
|
|
2890
|
+
watcher = null;
|
|
2891
|
+
rebuildTimer = null;
|
|
2892
|
+
rebuildScheduled = false;
|
|
2893
|
+
/** 上次 buildOnce() 完成时间;用于 staleness 检查决定是否要重 build */
|
|
2894
|
+
lastBuiltAt = 0;
|
|
2895
|
+
constructor(cwd) {
|
|
2896
|
+
this.cwd = cwd;
|
|
2897
|
+
}
|
|
2898
|
+
/**
|
|
2899
|
+
* 触发索引就绪。
|
|
2900
|
+
*
|
|
2901
|
+
* 关键设计:**不阻塞用户请求等 rebuild**。
|
|
2902
|
+
* - 首次:必须 await(没数据可用)
|
|
2903
|
+
* - 已 built 但 stale:**fire-and-forget 后台 rebuild**,立即用旧索引响应。
|
|
2904
|
+
* 用户拿到老数据 + 几百毫秒内 background 完成 → 下次请求自动新数据。
|
|
2905
|
+
*
|
|
2906
|
+
* Why:WSL DrvFs walk 5+ 秒,prod WSL 加用户大 vault 可能 10+ 秒,
|
|
2907
|
+
* 不能让用户每 30s 卡一次 wikilink 解析。Obsidian 自身也是后台维护索引,
|
|
2908
|
+
* 用户从不感知 vault rescan。
|
|
2909
|
+
*/
|
|
2910
|
+
async ensureBuilt() {
|
|
2911
|
+
if (this.built) {
|
|
2912
|
+
const stale = Date.now() - this.lastBuiltAt > STALE_REBUILD_MS;
|
|
2913
|
+
if (stale && !this.buildPromise) {
|
|
2914
|
+
this.buildPromise = this.buildOnce().then(() => {
|
|
2915
|
+
this.lastBuiltAt = Date.now();
|
|
2916
|
+
}).catch((err) => {
|
|
2917
|
+
logger.warn({ err }, "WorkspaceIndex background rebuild failed");
|
|
2918
|
+
}).finally(() => {
|
|
2919
|
+
this.buildPromise = null;
|
|
2920
|
+
});
|
|
2921
|
+
}
|
|
2922
|
+
return;
|
|
2923
|
+
}
|
|
2924
|
+
if (this.buildPromise) {
|
|
2925
|
+
await this.buildPromise;
|
|
2926
|
+
return;
|
|
2927
|
+
}
|
|
2928
|
+
this.buildPromise = this.buildOnce();
|
|
2929
|
+
try {
|
|
2930
|
+
await this.buildPromise;
|
|
2931
|
+
this.built = true;
|
|
2932
|
+
this.lastBuiltAt = Date.now();
|
|
2933
|
+
this.startWatch();
|
|
2934
|
+
} finally {
|
|
2935
|
+
this.buildPromise = null;
|
|
2936
|
+
}
|
|
2937
|
+
}
|
|
2938
|
+
/**
|
|
2939
|
+
* 后台预热:在用户主动 resolve 之前异步触发首次 build。
|
|
2940
|
+
* 调用方 fire-and-forget。已 built 或正在 build 时 no-op。
|
|
2941
|
+
*
|
|
2942
|
+
* 推荐挂在 /files/list 第一次被调时(用户刚打开文件浏览器,索引提前热好)。
|
|
2943
|
+
*/
|
|
2944
|
+
prefetch() {
|
|
2945
|
+
if (this.built || this.buildPromise)
|
|
2946
|
+
return;
|
|
2947
|
+
void this.ensureBuilt().catch((err) => logger.debug({ err }, "WorkspaceIndex prefetch failed (silent)"));
|
|
2948
|
+
}
|
|
2949
|
+
resolve(from, target) {
|
|
2950
|
+
const { pathPart, fragment } = splitFragment(target);
|
|
2951
|
+
if (pathPart.length === 0) {
|
|
2952
|
+
return fragment ? { broken: true, fragment } : { broken: true };
|
|
2953
|
+
}
|
|
2954
|
+
const fromRel = this.normalizeFrom(from);
|
|
2955
|
+
if (pathPart.includes("/")) {
|
|
2956
|
+
const fromVault = this.findByRelPath(pathPart);
|
|
2957
|
+
if (fromVault)
|
|
2958
|
+
return makeResult(fromVault, fragment);
|
|
2959
|
+
const fromCurrent = this.findByRelPath(join3(dirname3(fromRel), pathPart));
|
|
2960
|
+
if (fromCurrent)
|
|
2961
|
+
return makeResult(fromCurrent, fragment);
|
|
2962
|
+
return makeBroken(fragment);
|
|
2963
|
+
}
|
|
2964
|
+
const key = stripExt(pathPart).toLowerCase();
|
|
2965
|
+
const candidates = this.byBasename.get(key);
|
|
2966
|
+
if (!candidates || candidates.length === 0)
|
|
2967
|
+
return makeBroken(fragment);
|
|
2968
|
+
if (candidates.length === 1)
|
|
2969
|
+
return makeResult(candidates[0], fragment);
|
|
2970
|
+
const best = pickShortestPath(fromRel, candidates);
|
|
2971
|
+
return {
|
|
2972
|
+
resolved: best,
|
|
2973
|
+
candidates: [...candidates],
|
|
2974
|
+
...fragment ? { fragment } : {}
|
|
2975
|
+
};
|
|
2976
|
+
}
|
|
2977
|
+
/**
|
|
2978
|
+
* 把 from 归一为相对 cwd 路径(POSIX `/` 分隔)。
|
|
2979
|
+
*
|
|
2980
|
+
* - 绝对路径(以 cwd 为前缀)→ 剥前缀
|
|
2981
|
+
* - 已是相对路径 → 原样(再做一次 split/join 防 Windows 反斜杠)
|
|
2982
|
+
* - 越过 cwd 的(`..`)→ 不在 vault 内,返回原 from(让 findByRelPath 自然 miss)
|
|
2983
|
+
*/
|
|
2984
|
+
normalizeFrom(from) {
|
|
2985
|
+
const normalized = from.split(sep).join("/");
|
|
2986
|
+
const rootPosix = this.cwd.split(sep).join("/");
|
|
2987
|
+
if (normalized.startsWith(rootPosix + "/")) {
|
|
2988
|
+
return normalized.slice(rootPosix.length + 1);
|
|
2989
|
+
}
|
|
2990
|
+
if (normalized === rootPosix)
|
|
2991
|
+
return "";
|
|
2992
|
+
return normalized;
|
|
2993
|
+
}
|
|
2994
|
+
shutdown() {
|
|
2995
|
+
this.watcher?.close();
|
|
2996
|
+
this.watcher = null;
|
|
2997
|
+
if (this.rebuildTimer)
|
|
2998
|
+
clearInterval(this.rebuildTimer);
|
|
2999
|
+
this.rebuildTimer = null;
|
|
3000
|
+
}
|
|
3001
|
+
/** 同步触发一次 rebuild(单测 + scheduleRebuild 复用) */
|
|
3002
|
+
async rebuild() {
|
|
3003
|
+
await this.buildOnce();
|
|
3004
|
+
this.lastBuiltAt = Date.now();
|
|
3005
|
+
}
|
|
3006
|
+
// ─── internals ──────────────────────────────────────────────
|
|
3007
|
+
async buildOnce() {
|
|
3008
|
+
const fresh = /* @__PURE__ */ new Map();
|
|
3009
|
+
await walk(this.cwd, this.cwd, (rel) => {
|
|
3010
|
+
const ext = extname2(rel).toLowerCase();
|
|
3011
|
+
if (!MD_EXTS.has(ext))
|
|
3012
|
+
return;
|
|
3013
|
+
const key = stripExt(basename3(rel)).toLowerCase();
|
|
3014
|
+
const arr = fresh.get(key) ?? [];
|
|
3015
|
+
arr.push(rel);
|
|
3016
|
+
fresh.set(key, arr);
|
|
3017
|
+
});
|
|
3018
|
+
for (const arr of fresh.values())
|
|
3019
|
+
arr.sort();
|
|
3020
|
+
this.byBasename = fresh;
|
|
3021
|
+
}
|
|
3022
|
+
startWatch() {
|
|
3023
|
+
try {
|
|
3024
|
+
this.watcher = watch2(this.cwd, { recursive: true }, () => {
|
|
3025
|
+
this.scheduleRebuild();
|
|
3026
|
+
});
|
|
3027
|
+
} catch (e) {
|
|
3028
|
+
logger.warn({ err: e, cwd: this.cwd }, "WorkspaceIndex: fs.watch failed, falling back to 5min poll");
|
|
3029
|
+
}
|
|
3030
|
+
this.rebuildTimer = setInterval(() => {
|
|
3031
|
+
this.scheduleRebuild();
|
|
3032
|
+
}, REBUILD_INTERVAL_MS);
|
|
3033
|
+
this.rebuildTimer.unref?.();
|
|
3034
|
+
}
|
|
3035
|
+
scheduleRebuild() {
|
|
3036
|
+
if (this.rebuildScheduled)
|
|
3037
|
+
return;
|
|
3038
|
+
this.rebuildScheduled = true;
|
|
3039
|
+
setTimeout(() => {
|
|
3040
|
+
this.rebuildScheduled = false;
|
|
3041
|
+
void this.buildOnce().catch((err) => logger.warn({ err }, "WorkspaceIndex rebuild failed"));
|
|
3042
|
+
}, 500);
|
|
3043
|
+
}
|
|
3044
|
+
findByRelPath(rel) {
|
|
3045
|
+
const norm = rel.split(sep).join("/");
|
|
3046
|
+
for (const ext of ["", ".md", ".markdown"]) {
|
|
3047
|
+
const candidate = norm + ext;
|
|
3048
|
+
const key = stripExt(basename3(candidate)).toLowerCase();
|
|
3049
|
+
const arr = this.byBasename.get(key);
|
|
3050
|
+
if (arr?.includes(candidate))
|
|
3051
|
+
return candidate;
|
|
3052
|
+
}
|
|
3053
|
+
return null;
|
|
3054
|
+
}
|
|
3055
|
+
};
|
|
3056
|
+
EXCLUDED_DIRS = /* @__PURE__ */ new Set([".git", ".obsidian", ".trash", "node_modules"]);
|
|
3057
|
+
}
|
|
3058
|
+
});
|
|
3059
|
+
|
|
3060
|
+
// backend/dist/api/file-routes.js
|
|
3061
|
+
import { Router as Router9 } from "express";
|
|
3062
|
+
import { createReadStream } from "node:fs";
|
|
3063
|
+
import { lstat as lstat2 } from "node:fs/promises";
|
|
3064
|
+
import { dirname as dirname4 } from "node:path";
|
|
3065
|
+
function createFileRoutes(opts) {
|
|
3066
|
+
const router = Router9();
|
|
3067
|
+
const { authModule, registry, workdirPolicy } = opts;
|
|
3068
|
+
const fileLimiter = new RateLimiter(FILE_RATE_LIMIT_PER_MIN);
|
|
3069
|
+
const searchLimiter = new RateLimiter(SEARCH_RATE_LIMIT_PER_MIN);
|
|
3070
|
+
const wikilinkIndexes = /* @__PURE__ */ new Map();
|
|
3071
|
+
function getWikilinkIndex(instanceId, cwd) {
|
|
3072
|
+
let idx = wikilinkIndexes.get(instanceId);
|
|
3073
|
+
if (!idx) {
|
|
3074
|
+
idx = new WorkspaceIndex(cwd);
|
|
3075
|
+
wikilinkIndexes.set(instanceId, idx);
|
|
3076
|
+
}
|
|
3077
|
+
return idx;
|
|
3078
|
+
}
|
|
3079
|
+
router.get("/files/list", authModule.requireAuth, requireRate(fileLimiter), wrap(async (req, res) => {
|
|
3080
|
+
const tStart = Date.now();
|
|
3081
|
+
const ctx = await resolveContext(req, registry, workdirPolicy);
|
|
3082
|
+
const target = resolveSafePath(ctx.cwd, asString(req.query.path), ctx.policy);
|
|
3083
|
+
const entries = await listDir(target);
|
|
3084
|
+
const parent = computeParent(ctx.cwd, target, ctx.policy);
|
|
3085
|
+
const payload = {
|
|
3086
|
+
ok: true,
|
|
3087
|
+
cwd: ctx.cwd,
|
|
3088
|
+
path: target,
|
|
3089
|
+
parent,
|
|
3090
|
+
entries
|
|
3091
|
+
};
|
|
3092
|
+
logger.info({
|
|
3093
|
+
action: "list",
|
|
3094
|
+
instanceId: ctx.instanceId,
|
|
3095
|
+
path: target,
|
|
3096
|
+
ip: req.ip,
|
|
3097
|
+
elapsedMs: Date.now() - tStart
|
|
3098
|
+
}, "/api/files audit");
|
|
3099
|
+
res.json(payload);
|
|
3100
|
+
getWikilinkIndex(ctx.instanceId, ctx.cwd).prefetch();
|
|
3101
|
+
}));
|
|
3102
|
+
router.get("/files/stat", authModule.requireAuth, requireRate(fileLimiter), wrap(async (req, res) => {
|
|
3103
|
+
const tStart = Date.now();
|
|
3104
|
+
const ctx = await resolveContext(req, registry, workdirPolicy);
|
|
3105
|
+
const target = resolveSafePath(ctx.cwd, asString(req.query.path), ctx.policy);
|
|
3106
|
+
const st = await lstat2(target);
|
|
3107
|
+
const kind = getFileKind(st);
|
|
3108
|
+
const payload = {
|
|
3109
|
+
ok: true,
|
|
3110
|
+
path: target,
|
|
3111
|
+
kind,
|
|
3112
|
+
size: kind === "dir" ? 0 : st.size,
|
|
3113
|
+
mtimeMs: st.mtimeMs
|
|
3114
|
+
};
|
|
3115
|
+
if (kind === "file") {
|
|
3116
|
+
const m = detectMime(target);
|
|
3117
|
+
payload.mime = m.mime;
|
|
3118
|
+
payload.previewable = m.previewable;
|
|
3119
|
+
}
|
|
3120
|
+
logger.info({
|
|
3121
|
+
action: "stat",
|
|
3122
|
+
instanceId: ctx.instanceId,
|
|
3123
|
+
path: target,
|
|
3124
|
+
ip: req.ip,
|
|
3125
|
+
elapsedMs: Date.now() - tStart
|
|
3126
|
+
}, "/api/files audit");
|
|
3127
|
+
res.json(payload);
|
|
3128
|
+
}));
|
|
3129
|
+
router.get("/files/read", authModule.requireAuth, requireRate(fileLimiter), wrap(async (req, res) => {
|
|
3130
|
+
const tStart = Date.now();
|
|
3131
|
+
const ctx = await resolveContext(req, registry, workdirPolicy);
|
|
3132
|
+
const target = resolveSafePath(ctx.cwd, asString(req.query.path), ctx.policy);
|
|
3133
|
+
const st = await lstat2(target);
|
|
3134
|
+
if (!st.isFile()) {
|
|
3135
|
+
throw new FileError(ErrorCode.FILE_TYPE_FORBID, "not a regular file", 409);
|
|
3136
|
+
}
|
|
3137
|
+
const r = await readTextFile(target);
|
|
3138
|
+
const m = detectMime(target);
|
|
3139
|
+
const payload = {
|
|
3140
|
+
ok: true,
|
|
3141
|
+
path: target,
|
|
3142
|
+
mime: m.mime,
|
|
3143
|
+
content: r.content,
|
|
3144
|
+
truncated: r.truncated,
|
|
3145
|
+
size: r.size,
|
|
3146
|
+
lang: pickLang(m.lang, r.hasAnsi)
|
|
3147
|
+
};
|
|
3148
|
+
logger.info({
|
|
3149
|
+
action: "read",
|
|
3150
|
+
instanceId: ctx.instanceId,
|
|
3151
|
+
path: target,
|
|
3152
|
+
ip: req.ip,
|
|
3153
|
+
elapsedMs: Date.now() - tStart,
|
|
3154
|
+
size: r.size,
|
|
3155
|
+
truncated: r.truncated
|
|
3156
|
+
}, "/api/files audit");
|
|
3157
|
+
res.json(payload);
|
|
3158
|
+
}));
|
|
3159
|
+
router.get("/files/raw", authModule.requireAuth, requireRate(fileLimiter), async (req, res) => {
|
|
3160
|
+
const tStart = Date.now();
|
|
3161
|
+
let auditCtx;
|
|
3162
|
+
let auditPath;
|
|
3163
|
+
try {
|
|
3164
|
+
auditCtx = await resolveContext(req, registry, workdirPolicy);
|
|
3165
|
+
auditPath = resolveSafePath(auditCtx.cwd, asString(req.query.path), auditCtx.policy);
|
|
3166
|
+
const st = await lstat2(auditPath);
|
|
3167
|
+
if (!st.isFile())
|
|
3168
|
+
throw new FileError(ErrorCode.FILE_TYPE_FORBID, "", 409);
|
|
3169
|
+
if (st.size > FILE_RAW_MAX_BYTES) {
|
|
3170
|
+
throw new FileError(ErrorCode.FILE_TOO_LARGE, "", 413);
|
|
3171
|
+
}
|
|
3172
|
+
const { mime } = detectMime(auditPath);
|
|
3173
|
+
res.setHeader("Content-Type", mime);
|
|
3174
|
+
res.setHeader("Cache-Control", "private, max-age=0");
|
|
3175
|
+
res.setHeader("Content-Length", String(st.size));
|
|
3176
|
+
logger.info({
|
|
3177
|
+
action: "raw",
|
|
3178
|
+
instanceId: auditCtx.instanceId,
|
|
3179
|
+
path: auditPath,
|
|
3180
|
+
ip: req.ip,
|
|
3181
|
+
elapsedMs: Date.now() - tStart,
|
|
3182
|
+
size: st.size
|
|
3183
|
+
}, "/api/files audit");
|
|
3184
|
+
createReadStream(auditPath).pipe(res);
|
|
3185
|
+
} catch (err) {
|
|
3186
|
+
const code = err instanceof AppError ? err.code : ErrorCode.INTERNAL_ERROR;
|
|
3187
|
+
const status = err instanceof AppError ? err.httpStatus : 500;
|
|
3188
|
+
res.setHeader(HEADER_ATR_ERROR, String(code));
|
|
3189
|
+
logger.info({
|
|
3190
|
+
action: "raw",
|
|
3191
|
+
instanceId: auditCtx?.instanceId,
|
|
3192
|
+
path: auditPath,
|
|
3193
|
+
ip: req.ip,
|
|
3194
|
+
elapsedMs: Date.now() - tStart,
|
|
3195
|
+
code,
|
|
3196
|
+
status
|
|
3197
|
+
}, "/api/files audit (error)");
|
|
3198
|
+
res.status(status).end();
|
|
3199
|
+
}
|
|
3200
|
+
});
|
|
3201
|
+
router.get("/files/search", authModule.requireAuth, requireRate(searchLimiter), async (req, res) => {
|
|
3202
|
+
const tStart = Date.now();
|
|
3203
|
+
const q = asString(req.query.q);
|
|
3204
|
+
if (!q || q.length === 0 || q.length > SEARCH_MAX_Q_LENGTH) {
|
|
3205
|
+
res.status(400).json({
|
|
3206
|
+
error: { code: ErrorCode.BAD_REQUEST, message: "q is required (1..200)" }
|
|
3207
|
+
});
|
|
3208
|
+
return;
|
|
3209
|
+
}
|
|
3210
|
+
const modeRaw = asString(req.query.mode) ?? "name";
|
|
3211
|
+
if (!isSearchMode(modeRaw)) {
|
|
3212
|
+
res.status(400).json({
|
|
3213
|
+
error: { code: ErrorCode.BAD_REQUEST, message: "invalid mode" }
|
|
3214
|
+
});
|
|
3215
|
+
return;
|
|
3216
|
+
}
|
|
3217
|
+
let ctx;
|
|
3218
|
+
try {
|
|
3219
|
+
ctx = await resolveContext(req, registry, workdirPolicy);
|
|
3220
|
+
} catch (err) {
|
|
3221
|
+
if (err instanceof AppError) {
|
|
3222
|
+
res.status(err.httpStatus).json({ error: { code: err.code, message: err.message } });
|
|
3223
|
+
} else {
|
|
3224
|
+
res.status(500).end();
|
|
3225
|
+
}
|
|
3226
|
+
return;
|
|
3227
|
+
}
|
|
3228
|
+
let scopePath;
|
|
3229
|
+
try {
|
|
3230
|
+
scopePath = resolveSafePath(ctx.cwd, asString(req.query.scope), ctx.policy);
|
|
3231
|
+
const st = await lstat2(scopePath);
|
|
3232
|
+
if (!st.isDirectory()) {
|
|
3233
|
+
throw new FileError(ErrorCode.BAD_REQUEST, "scope must be a directory", 400);
|
|
3234
|
+
}
|
|
3235
|
+
} catch (err) {
|
|
3236
|
+
if (err instanceof AppError) {
|
|
3237
|
+
res.status(err.httpStatus).json({ error: { code: err.code, message: err.message } });
|
|
3238
|
+
} else {
|
|
3239
|
+
res.status(500).end();
|
|
3240
|
+
}
|
|
3241
|
+
return;
|
|
3242
|
+
}
|
|
3243
|
+
res.setHeader("Content-Type", "text/event-stream");
|
|
3244
|
+
res.setHeader("Cache-Control", "no-cache, no-transform");
|
|
3245
|
+
res.setHeader("Connection", "keep-alive");
|
|
3246
|
+
res.setHeader("X-Accel-Buffering", "no");
|
|
3247
|
+
res.flushHeaders();
|
|
3248
|
+
const ac = new AbortController();
|
|
3249
|
+
req.on("close", () => ac.abort());
|
|
3250
|
+
try {
|
|
3251
|
+
const summary = await runSearch({
|
|
3252
|
+
scope: scopePath,
|
|
3253
|
+
q,
|
|
3254
|
+
mode: modeRaw,
|
|
3255
|
+
caseSensitive: asString(req.query.caseSensitive) === "1",
|
|
3256
|
+
regex: asString(req.query.regex) === "1",
|
|
3257
|
+
policy: ctx.policy,
|
|
3258
|
+
cancelSignal: ac.signal,
|
|
3259
|
+
emit: (hit) => {
|
|
3260
|
+
res.write(`event: match
|
|
3261
|
+
data: ${JSON.stringify(hit)}
|
|
3262
|
+
|
|
3263
|
+
`);
|
|
3264
|
+
}
|
|
3265
|
+
});
|
|
3266
|
+
res.write(`event: done
|
|
3267
|
+
data: ${JSON.stringify(summary)}
|
|
3268
|
+
|
|
3269
|
+
`);
|
|
3270
|
+
logger.info({
|
|
3271
|
+
action: "search",
|
|
3272
|
+
instanceId: ctx.instanceId,
|
|
3273
|
+
path: scopePath,
|
|
3274
|
+
ip: req.ip,
|
|
3275
|
+
elapsedMs: Date.now() - tStart,
|
|
3276
|
+
scanned: summary.scanned,
|
|
3277
|
+
truncated: summary.truncated
|
|
3278
|
+
}, "/api/files audit");
|
|
3279
|
+
} catch (err) {
|
|
3280
|
+
const code = err instanceof AppError ? err.code : ErrorCode.INTERNAL_ERROR;
|
|
3281
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
3282
|
+
res.write(`event: error
|
|
3283
|
+
data: ${JSON.stringify({ code, message })}
|
|
3284
|
+
|
|
3285
|
+
`);
|
|
3286
|
+
logger.warn({
|
|
3287
|
+
action: "search",
|
|
3288
|
+
instanceId: ctx.instanceId,
|
|
3289
|
+
path: scopePath,
|
|
3290
|
+
ip: req.ip,
|
|
3291
|
+
elapsedMs: Date.now() - tStart,
|
|
3292
|
+
code
|
|
3293
|
+
}, "/api/files audit (error)");
|
|
3294
|
+
} finally {
|
|
3295
|
+
res.end();
|
|
3296
|
+
}
|
|
3297
|
+
});
|
|
3298
|
+
router.post("/files/resolve-links", authModule.requireAuth, requireRate(fileLimiter), wrap(async (req, res) => {
|
|
3299
|
+
const tStart = Date.now();
|
|
3300
|
+
const body = req.body;
|
|
3301
|
+
if (typeof body.instanceId !== "string" || body.instanceId.length === 0 || typeof body.from !== "string" || !Array.isArray(body.targets)) {
|
|
3302
|
+
throw new AppError(ErrorCode.BAD_REQUEST, "body must be { instanceId: string, from: string, targets: string[] }");
|
|
3303
|
+
}
|
|
3304
|
+
if (body.targets.length > 200) {
|
|
3305
|
+
throw new AppError(ErrorCode.BAD_REQUEST, "too many targets (max 200)");
|
|
3306
|
+
}
|
|
3307
|
+
for (const t of body.targets) {
|
|
3308
|
+
if (typeof t !== "string") {
|
|
3309
|
+
throw new AppError(ErrorCode.BAD_REQUEST, "target must be string");
|
|
3310
|
+
}
|
|
3311
|
+
}
|
|
3312
|
+
const targets = body.targets;
|
|
3313
|
+
const reqWithQuery = Object.assign(Object.create(Object.getPrototypeOf(req)), req, {
|
|
3314
|
+
query: { ...req.query, instanceId: body.instanceId }
|
|
3315
|
+
});
|
|
3316
|
+
const ctx = await resolveContext(reqWithQuery, registry, workdirPolicy);
|
|
3317
|
+
resolveSafePath(ctx.cwd, body.from, ctx.policy);
|
|
3318
|
+
const idx = getWikilinkIndex(ctx.instanceId, ctx.cwd);
|
|
3319
|
+
await idx.ensureBuilt();
|
|
3320
|
+
const results = targets.map((target) => ({
|
|
3321
|
+
target,
|
|
3322
|
+
...idx.resolve(body.from, target)
|
|
3323
|
+
}));
|
|
3324
|
+
res.json({ ok: true, results });
|
|
3325
|
+
logger.info({
|
|
3326
|
+
action: "resolve-links",
|
|
3327
|
+
instanceId: ctx.instanceId,
|
|
3328
|
+
from: body.from,
|
|
3329
|
+
targetsCount: targets.length,
|
|
3330
|
+
ip: req.ip,
|
|
3331
|
+
elapsedMs: Date.now() - tStart
|
|
3332
|
+
}, "/api/files audit");
|
|
3333
|
+
}));
|
|
3334
|
+
return router;
|
|
3335
|
+
}
|
|
3336
|
+
function wrap(h) {
|
|
3337
|
+
return async (req, res, _next) => {
|
|
3338
|
+
try {
|
|
3339
|
+
await h(req, res);
|
|
3340
|
+
} catch (err) {
|
|
3341
|
+
if (err instanceof AppError) {
|
|
3342
|
+
res.status(err.httpStatus).json({
|
|
3343
|
+
error: { code: err.code, message: err.message }
|
|
3344
|
+
});
|
|
3345
|
+
return;
|
|
3346
|
+
}
|
|
3347
|
+
logger.error({ err }, "/api/files unexpected error");
|
|
3348
|
+
res.status(500).json({
|
|
3349
|
+
error: { code: ErrorCode.INTERNAL_ERROR, message: "internal error" }
|
|
3350
|
+
});
|
|
3351
|
+
}
|
|
3352
|
+
};
|
|
3353
|
+
}
|
|
3354
|
+
function asString(v) {
|
|
3355
|
+
return typeof v === "string" ? v : void 0;
|
|
3356
|
+
}
|
|
3357
|
+
function requireRate(limiter) {
|
|
3358
|
+
return (req, res, next) => {
|
|
3359
|
+
const ip = req.ip ?? "unknown";
|
|
3360
|
+
if (!limiter.attempt(ip)) {
|
|
3361
|
+
res.status(429).json({
|
|
3362
|
+
error: { code: ErrorCode.AUTH_RATE_LIMITED, message: "rate limited" }
|
|
3363
|
+
});
|
|
3364
|
+
return;
|
|
1561
3365
|
}
|
|
3366
|
+
next();
|
|
3367
|
+
};
|
|
3368
|
+
}
|
|
3369
|
+
async function resolveContext(req, registry, workdirPolicy) {
|
|
3370
|
+
const instanceId = req.query.instanceId;
|
|
3371
|
+
if (typeof instanceId !== "string" || instanceId.length === 0) {
|
|
3372
|
+
throw new FileError(ErrorCode.BAD_REQUEST, "instanceId is required", 400);
|
|
1562
3373
|
}
|
|
1563
|
-
|
|
1564
|
-
|
|
3374
|
+
const all = await registry.list();
|
|
3375
|
+
const inst = all.find((i) => i.instanceId === instanceId);
|
|
3376
|
+
if (!inst) {
|
|
3377
|
+
throw new FileError(ErrorCode.INSTANCE_NOT_FOUND, `instance not found: ${instanceId}`, 404);
|
|
1565
3378
|
}
|
|
1566
|
-
const
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
loopback: 4
|
|
3379
|
+
const snap = workdirPolicy();
|
|
3380
|
+
return {
|
|
3381
|
+
cwd: inst.cwd,
|
|
3382
|
+
policy: { allow: snap.allow, deny: snap.deny },
|
|
3383
|
+
instanceId
|
|
1572
3384
|
};
|
|
1573
|
-
out.sort((a, b) => {
|
|
1574
|
-
if (a.kind !== b.kind)
|
|
1575
|
-
return order[a.kind] - order[b.kind];
|
|
1576
|
-
if (a.host === displayIp)
|
|
1577
|
-
return -1;
|
|
1578
|
-
if (b.host === displayIp)
|
|
1579
|
-
return 1;
|
|
1580
|
-
return 0;
|
|
1581
|
-
});
|
|
1582
|
-
let defaultIdx = out.findIndex((e) => e.host === displayIp);
|
|
1583
|
-
if (defaultIdx === -1) {
|
|
1584
|
-
defaultIdx = out.findIndex((e) => e.kind !== "loopback");
|
|
1585
|
-
}
|
|
1586
|
-
if (defaultIdx === -1 && out.length > 0)
|
|
1587
|
-
defaultIdx = 0;
|
|
1588
|
-
if (defaultIdx >= 0)
|
|
1589
|
-
out[defaultIdx].isDefault = true;
|
|
1590
|
-
return out;
|
|
1591
3385
|
}
|
|
1592
|
-
function
|
|
1593
|
-
if (
|
|
1594
|
-
return "
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
if (isTailscaleIp(ip))
|
|
1598
|
-
return "tailscale";
|
|
1599
|
-
if (isPrivateIp(ip))
|
|
1600
|
-
return "lan";
|
|
1601
|
-
if (isLinkLocal(ip))
|
|
1602
|
-
return "other";
|
|
1603
|
-
return "other";
|
|
3386
|
+
function pickLang(baseLang, hasAnsi) {
|
|
3387
|
+
if (hasAnsi && (baseLang === "txt" || baseLang === "log")) {
|
|
3388
|
+
return "ansi";
|
|
3389
|
+
}
|
|
3390
|
+
return baseLang;
|
|
1604
3391
|
}
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
3392
|
+
function computeParent(cwd, current, policy) {
|
|
3393
|
+
if (current === cwd)
|
|
3394
|
+
return null;
|
|
3395
|
+
const parentPath = dirname4(current);
|
|
3396
|
+
if (parentPath === current)
|
|
3397
|
+
return null;
|
|
3398
|
+
try {
|
|
3399
|
+
resolveSafePath(cwd, parentPath, policy);
|
|
3400
|
+
return parentPath;
|
|
3401
|
+
} catch {
|
|
3402
|
+
return null;
|
|
1609
3403
|
}
|
|
1610
|
-
});
|
|
1611
|
-
|
|
1612
|
-
// backend/dist/api/workdir-policy-routes.js
|
|
1613
|
-
import { Router as Router8 } from "express";
|
|
1614
|
-
function createWorkdirPolicyRoutes(authModule, snapshot) {
|
|
1615
|
-
const router = Router8();
|
|
1616
|
-
router.get("/workdir-policy", authModule.requireAuth, (_req, res) => {
|
|
1617
|
-
const s = snapshot();
|
|
1618
|
-
res.json({ ok: true, allow: s.allow });
|
|
1619
|
-
});
|
|
1620
|
-
return router;
|
|
1621
3404
|
}
|
|
1622
|
-
var
|
|
1623
|
-
"backend/dist/api/
|
|
3405
|
+
var init_file_routes = __esm({
|
|
3406
|
+
"backend/dist/api/file-routes.js"() {
|
|
1624
3407
|
"use strict";
|
|
3408
|
+
init_dist();
|
|
3409
|
+
init_errors2();
|
|
3410
|
+
init_logger();
|
|
3411
|
+
init_rate_limiter();
|
|
3412
|
+
init_forwarded_headers();
|
|
3413
|
+
init_constants2();
|
|
3414
|
+
init_path_resolver();
|
|
3415
|
+
init_list_dir();
|
|
3416
|
+
init_mime_detect();
|
|
3417
|
+
init_read_file();
|
|
3418
|
+
init_search_engine();
|
|
3419
|
+
init_file_kind();
|
|
3420
|
+
init_wikilink_resolver();
|
|
1625
3421
|
}
|
|
1626
3422
|
});
|
|
1627
3423
|
|
|
1628
3424
|
// backend/dist/api/router.js
|
|
1629
|
-
import { Router as
|
|
3425
|
+
import { Router as Router10 } from "express";
|
|
1630
3426
|
function createBrokerApiRouter(opts) {
|
|
1631
|
-
const router =
|
|
3427
|
+
const router = Router10();
|
|
1632
3428
|
router.use(createHealthRoutes());
|
|
1633
3429
|
router.use(createAuthRoutes(opts.authModule));
|
|
1634
3430
|
router.use(createConfigRoutes(opts.authModule, opts.configStore));
|
|
@@ -1644,10 +3440,15 @@ function createBrokerApiRouter(opts) {
|
|
|
1644
3440
|
displayIp: opts.displayIp
|
|
1645
3441
|
}));
|
|
1646
3442
|
router.use(createWorkdirPolicyRoutes(opts.authModule, opts.workdirPolicy));
|
|
3443
|
+
router.use(createFileRoutes({
|
|
3444
|
+
authModule: opts.authModule,
|
|
3445
|
+
registry: opts.registry,
|
|
3446
|
+
workdirPolicy: opts.workdirPolicy
|
|
3447
|
+
}));
|
|
1647
3448
|
return router;
|
|
1648
3449
|
}
|
|
1649
3450
|
function createWorkerApiRouter(opts) {
|
|
1650
|
-
const router =
|
|
3451
|
+
const router = Router10();
|
|
1651
3452
|
router.use(createHealthRoutes());
|
|
1652
3453
|
router.use(createHookRoutes(opts.integrations));
|
|
1653
3454
|
return router;
|
|
@@ -1663,31 +3464,7 @@ var init_router = __esm({
|
|
|
1663
3464
|
init_push_routes();
|
|
1664
3465
|
init_share_routes();
|
|
1665
3466
|
init_workdir_policy_routes();
|
|
1666
|
-
|
|
1667
|
-
});
|
|
1668
|
-
|
|
1669
|
-
// backend/dist/constants.js
|
|
1670
|
-
var WS_FLUSH_INTERVAL_MS, WS_MAX_CHUNK_BYTES, WS_HIGH_WATERMARK_BYTES, FILE_LOCK_RETRIES, FILE_LOCK_RETRY_INTERVAL_MS, FILE_LOCK_STALE_MS, PTY_DEFAULT_COLS, PTY_DEFAULT_ROWS, PTY_TERM_NAME, DOUBLE_PULSE_DELAY_MS, SHUTDOWN_WS_FLUSH_DELAY_MS, SHUTDOWN_FORCE_EXIT_MS, DOUBLE_CTRL_C_WINDOW_MS, PORT_FINDER_MAX_ATTEMPTS, STOP_INSTANCE_GRACE_MS, STOP_INSTANCE_POLL_INTERVAL_MS, ATTACH_RECONNECT_DELAYS_MS;
|
|
1671
|
-
var init_constants2 = __esm({
|
|
1672
|
-
"backend/dist/constants.js"() {
|
|
1673
|
-
"use strict";
|
|
1674
|
-
WS_FLUSH_INTERVAL_MS = 16;
|
|
1675
|
-
WS_MAX_CHUNK_BYTES = 32 * 1024;
|
|
1676
|
-
WS_HIGH_WATERMARK_BYTES = 256 * 1024;
|
|
1677
|
-
FILE_LOCK_RETRIES = 50;
|
|
1678
|
-
FILE_LOCK_RETRY_INTERVAL_MS = 50;
|
|
1679
|
-
FILE_LOCK_STALE_MS = 1e4;
|
|
1680
|
-
PTY_DEFAULT_COLS = 80;
|
|
1681
|
-
PTY_DEFAULT_ROWS = 24;
|
|
1682
|
-
PTY_TERM_NAME = "xterm-256color";
|
|
1683
|
-
DOUBLE_PULSE_DELAY_MS = 50;
|
|
1684
|
-
SHUTDOWN_WS_FLUSH_DELAY_MS = 500;
|
|
1685
|
-
SHUTDOWN_FORCE_EXIT_MS = 2e3;
|
|
1686
|
-
DOUBLE_CTRL_C_WINDOW_MS = 500;
|
|
1687
|
-
PORT_FINDER_MAX_ATTEMPTS = 100;
|
|
1688
|
-
STOP_INSTANCE_GRACE_MS = 3e3;
|
|
1689
|
-
STOP_INSTANCE_POLL_INTERVAL_MS = 100;
|
|
1690
|
-
ATTACH_RECONNECT_DELAYS_MS = [1e3, 2e3, 4e3, 8e3, 16e3, 3e4];
|
|
3467
|
+
init_file_routes();
|
|
1691
3468
|
}
|
|
1692
3469
|
});
|
|
1693
3470
|
|
|
@@ -2931,87 +4708,6 @@ var init_terminal_relay = __esm({
|
|
|
2931
4708
|
}
|
|
2932
4709
|
});
|
|
2933
4710
|
|
|
2934
|
-
// backend/dist/auth/rate-limiter.js
|
|
2935
|
-
var RateLimiter;
|
|
2936
|
-
var init_rate_limiter = __esm({
|
|
2937
|
-
"backend/dist/auth/rate-limiter.js"() {
|
|
2938
|
-
"use strict";
|
|
2939
|
-
init_logger();
|
|
2940
|
-
RateLimiter = class {
|
|
2941
|
-
entries = /* @__PURE__ */ new Map();
|
|
2942
|
-
maxAttempts;
|
|
2943
|
-
windowMs;
|
|
2944
|
-
cleanupTimer = null;
|
|
2945
|
-
/**
|
|
2946
|
-
* @param maxAttempts 窗口内最大允许次数
|
|
2947
|
-
* @param windowMs 窗口长度(毫秒),默认 60s
|
|
2948
|
-
*/
|
|
2949
|
-
constructor(maxAttempts, windowMs = 6e4) {
|
|
2950
|
-
if (!Number.isInteger(maxAttempts) || maxAttempts <= 0) {
|
|
2951
|
-
throw new Error("RateLimiter: maxAttempts \u5FC5\u987B\u662F\u6B63\u6574\u6570");
|
|
2952
|
-
}
|
|
2953
|
-
this.maxAttempts = maxAttempts;
|
|
2954
|
-
this.windowMs = windowMs;
|
|
2955
|
-
this.cleanupTimer = setInterval(() => this.cleanup(), windowMs * 2);
|
|
2956
|
-
if (typeof this.cleanupTimer.unref === "function") {
|
|
2957
|
-
this.cleanupTimer.unref();
|
|
2958
|
-
}
|
|
2959
|
-
}
|
|
2960
|
-
/**
|
|
2961
|
-
* 尝试一次请求,自动累加计数
|
|
2962
|
-
*
|
|
2963
|
-
* @returns true 通过 / false 已超限被拒
|
|
2964
|
-
*/
|
|
2965
|
-
attempt(ip) {
|
|
2966
|
-
const now = Date.now();
|
|
2967
|
-
const entry = this.entries.get(ip);
|
|
2968
|
-
if (!entry || now >= entry.resetAt) {
|
|
2969
|
-
this.entries.set(ip, { count: 1, resetAt: now + this.windowMs });
|
|
2970
|
-
return true;
|
|
2971
|
-
}
|
|
2972
|
-
entry.count++;
|
|
2973
|
-
if (entry.count > this.maxAttempts) {
|
|
2974
|
-
logger.warn({ ip, count: entry.count, max: this.maxAttempts }, "\u8BA4\u8BC1\u901F\u7387\u8D85\u9650");
|
|
2975
|
-
return false;
|
|
2976
|
-
}
|
|
2977
|
-
return true;
|
|
2978
|
-
}
|
|
2979
|
-
/** 当前窗口内剩余次数 */
|
|
2980
|
-
remaining(ip) {
|
|
2981
|
-
const now = Date.now();
|
|
2982
|
-
const entry = this.entries.get(ip);
|
|
2983
|
-
if (!entry || now >= entry.resetAt)
|
|
2984
|
-
return this.maxAttempts;
|
|
2985
|
-
return Math.max(0, this.maxAttempts - entry.count);
|
|
2986
|
-
}
|
|
2987
|
-
/**
|
|
2988
|
-
* 重置某 IP 的计数(认证成功后清零)
|
|
2989
|
-
*
|
|
2990
|
-
* 让合法用户不会因为之前误输导致后续尝试被限流
|
|
2991
|
-
*/
|
|
2992
|
-
reset(ip) {
|
|
2993
|
-
this.entries.delete(ip);
|
|
2994
|
-
}
|
|
2995
|
-
/** 清理过期 entry(避免内存膨胀) */
|
|
2996
|
-
cleanup() {
|
|
2997
|
-
const now = Date.now();
|
|
2998
|
-
for (const [ip, entry] of this.entries) {
|
|
2999
|
-
if (now >= entry.resetAt) {
|
|
3000
|
-
this.entries.delete(ip);
|
|
3001
|
-
}
|
|
3002
|
-
}
|
|
3003
|
-
}
|
|
3004
|
-
/** 销毁定时器 */
|
|
3005
|
-
destroy() {
|
|
3006
|
-
if (this.cleanupTimer) {
|
|
3007
|
-
clearInterval(this.cleanupTimer);
|
|
3008
|
-
this.cleanupTimer = null;
|
|
3009
|
-
}
|
|
3010
|
-
}
|
|
3011
|
-
};
|
|
3012
|
-
}
|
|
3013
|
-
});
|
|
3014
|
-
|
|
3015
4711
|
// backend/dist/auth/auth-middleware.js
|
|
3016
4712
|
import { timingSafeEqual } from "node:crypto";
|
|
3017
4713
|
import * as cookie from "cookie";
|
|
@@ -3282,7 +4978,7 @@ var init_token_generator = __esm({
|
|
|
3282
4978
|
|
|
3283
4979
|
// backend/dist/sessions/sessions-store.js
|
|
3284
4980
|
import { existsSync as existsSync2, mkdirSync as mkdirSync3, readFileSync as readFileSync2 } from "node:fs";
|
|
3285
|
-
import { dirname as
|
|
4981
|
+
import { dirname as dirname5, resolve as resolve3 } from "node:path";
|
|
3286
4982
|
import { homedir } from "node:os";
|
|
3287
4983
|
function defaultSessionsPath() {
|
|
3288
4984
|
return resolve3(homedir(), ATR_DATA_DIR, SESSIONS_FILENAME);
|
|
@@ -3340,7 +5036,7 @@ var init_sessions_store = __esm({
|
|
|
3340
5036
|
sessionTtlMs;
|
|
3341
5037
|
constructor(opts) {
|
|
3342
5038
|
this.path = opts.path ?? defaultSessionsPath();
|
|
3343
|
-
this.lockDir = opts.lockDir ?? resolve3(
|
|
5039
|
+
this.lockDir = opts.lockDir ?? resolve3(dirname5(this.path), SESSIONS_LOCK_DIRNAME);
|
|
3344
5040
|
this.sessionTtlMs = opts.sessionTtlMs;
|
|
3345
5041
|
}
|
|
3346
5042
|
// ──────────────── 公共 API ────────────────
|
|
@@ -3464,7 +5160,7 @@ var init_sessions_store = __esm({
|
|
|
3464
5160
|
}
|
|
3465
5161
|
/** 原子写 + 确保父目录存在 */
|
|
3466
5162
|
persist(data) {
|
|
3467
|
-
const dir =
|
|
5163
|
+
const dir = dirname5(this.path);
|
|
3468
5164
|
if (!existsSync2(dir)) {
|
|
3469
5165
|
try {
|
|
3470
5166
|
mkdirSync3(dir, { recursive: true, mode: 448 });
|
|
@@ -3523,7 +5219,7 @@ var init_ws_authenticate = __esm({
|
|
|
3523
5219
|
|
|
3524
5220
|
// backend/dist/config.js
|
|
3525
5221
|
import { existsSync as existsSync3, readFileSync as readFileSync3, mkdirSync as mkdirSync4, copyFileSync } from "node:fs";
|
|
3526
|
-
import { resolve as resolve4, basename as
|
|
5222
|
+
import { resolve as resolve4, basename as basename4 } from "node:path";
|
|
3527
5223
|
import { homedir as homedir2 } from "node:os";
|
|
3528
5224
|
import { statSync as statSync2 } from "node:fs";
|
|
3529
5225
|
function extractSettingsFromArgs(args) {
|
|
@@ -3656,7 +5352,7 @@ function loadConfig(deps) {
|
|
|
3656
5352
|
const claudeCwd = cli.workdir ?? env["OCR_CWD"] ?? process.cwd();
|
|
3657
5353
|
const explicitArgs = mergeClaudeArgs(cli.claudeArgs, env["OCR_ARGS"]);
|
|
3658
5354
|
const claudeArgs = explicitArgs.length > 0 || explicitCommand !== void 0 ? explicitArgs : defaultShellArgs(claudeCommand);
|
|
3659
|
-
const instanceName = cli.instanceName ?? env["INSTANCE_NAME"] ?? (
|
|
5355
|
+
const instanceName = cli.instanceName ?? env["INSTANCE_NAME"] ?? (basename4(claudeCwd) || "instance");
|
|
3660
5356
|
const maxBufferLines = cli.maxBufferLines ?? toInt(env["MAX_BUFFER_LINES"]) ?? DEFAULT_MAX_BUFFER_LINES;
|
|
3661
5357
|
const sessionTtlMs = cli.sessionTtlMs ?? toInt(env["SESSION_TTL_MS"]) ?? DEFAULT_SESSION_TTL_MS;
|
|
3662
5358
|
const authRateLimit = cli.authRateLimit ?? toInt(env["AUTH_RATE_LIMIT"]) ?? DEFAULT_AUTH_RATE_LIMIT;
|
|
@@ -3889,9 +5585,9 @@ var init_manager = __esm({
|
|
|
3889
5585
|
});
|
|
3890
5586
|
|
|
3891
5587
|
// backend/dist/integrations/claude-code/detect.js
|
|
3892
|
-
import { basename as
|
|
5588
|
+
import { basename as basename5 } from "node:path";
|
|
3893
5589
|
function isClaudeCommand(command) {
|
|
3894
|
-
let name =
|
|
5590
|
+
let name = basename5(command).toLowerCase();
|
|
3895
5591
|
for (const suffix of WIN_SUFFIXES) {
|
|
3896
5592
|
if (name.endsWith(suffix)) {
|
|
3897
5593
|
name = name.slice(0, -suffix.length);
|
|
@@ -4050,7 +5746,7 @@ function extractToolFromMessage(message) {
|
|
|
4050
5746
|
const m = TOOL_NAME_RE.exec(message);
|
|
4051
5747
|
return m?.[1] ?? null;
|
|
4052
5748
|
}
|
|
4053
|
-
function
|
|
5749
|
+
function asString2(v) {
|
|
4054
5750
|
return typeof v === "string" && v.length > 0 ? v : void 0;
|
|
4055
5751
|
}
|
|
4056
5752
|
function asBool(v) {
|
|
@@ -4060,35 +5756,35 @@ function asNumber(v) {
|
|
|
4060
5756
|
return typeof v === "number" && Number.isFinite(v) ? v : void 0;
|
|
4061
5757
|
}
|
|
4062
5758
|
function deriveApprovalId(payload) {
|
|
4063
|
-
const toolUseId =
|
|
5759
|
+
const toolUseId = asString2(payload["tool_use_id"]);
|
|
4064
5760
|
if (toolUseId)
|
|
4065
5761
|
return toolUseId;
|
|
4066
|
-
const tool =
|
|
5762
|
+
const tool = asString2(payload["tool_name"]) ?? extractToolFromMessage(payload["message"]) ?? "unknown";
|
|
4067
5763
|
return `pending:${tool}`;
|
|
4068
5764
|
}
|
|
4069
5765
|
function mapHookPayload(payload) {
|
|
4070
5766
|
if (!payload || typeof payload !== "object")
|
|
4071
5767
|
return [];
|
|
4072
5768
|
const p = payload;
|
|
4073
|
-
const event =
|
|
5769
|
+
const event = asString2(p["hook_event_name"]);
|
|
4074
5770
|
if (!event)
|
|
4075
5771
|
return [];
|
|
4076
5772
|
switch (event) {
|
|
4077
5773
|
case "Notification": {
|
|
4078
5774
|
if (p["notification_type"] !== "permission_prompt")
|
|
4079
5775
|
return [];
|
|
4080
|
-
const tool =
|
|
5776
|
+
const tool = asString2(p["tool_name"]) ?? extractToolFromMessage(p["message"]) ?? "unknown_tool";
|
|
4081
5777
|
return [
|
|
4082
5778
|
{
|
|
4083
5779
|
kind: "approval_pending",
|
|
4084
5780
|
id: deriveApprovalId(p),
|
|
4085
5781
|
tool,
|
|
4086
|
-
...
|
|
5782
|
+
...asString2(p["message"]) ? { detail: asString2(p["message"]) } : {}
|
|
4087
5783
|
}
|
|
4088
5784
|
];
|
|
4089
5785
|
}
|
|
4090
5786
|
case "PermissionRequest": {
|
|
4091
|
-
const tool =
|
|
5787
|
+
const tool = asString2(p["tool_name"]) ?? "unknown_tool";
|
|
4092
5788
|
const summary = summarizeToolCall(tool, p["tool_input"]);
|
|
4093
5789
|
return [
|
|
4094
5790
|
{
|
|
@@ -4100,18 +5796,18 @@ function mapHookPayload(payload) {
|
|
|
4100
5796
|
];
|
|
4101
5797
|
}
|
|
4102
5798
|
case "PreToolUse": {
|
|
4103
|
-
const tool =
|
|
5799
|
+
const tool = asString2(p["tool_name"]) ?? "unknown_tool";
|
|
4104
5800
|
return [
|
|
4105
5801
|
{
|
|
4106
5802
|
kind: "tool_started",
|
|
4107
|
-
toolUseId:
|
|
5803
|
+
toolUseId: asString2(p["tool_use_id"]) ?? deriveApprovalId(p),
|
|
4108
5804
|
tool,
|
|
4109
5805
|
summary: summarizeToolCall(tool, p["tool_input"])
|
|
4110
5806
|
}
|
|
4111
5807
|
];
|
|
4112
5808
|
}
|
|
4113
5809
|
case "PostToolUse": {
|
|
4114
|
-
const id =
|
|
5810
|
+
const id = asString2(p["tool_use_id"]) ?? deriveApprovalId(p);
|
|
4115
5811
|
const events = [
|
|
4116
5812
|
{ kind: "tool_finished", toolUseId: id, ok: true, ...asNumber(p["duration_ms"]) !== void 0 ? { durationMs: asNumber(p["duration_ms"]) } : {} },
|
|
4117
5813
|
{ kind: "approval_resolved", id, outcome: "allow" }
|
|
@@ -4119,7 +5815,7 @@ function mapHookPayload(payload) {
|
|
|
4119
5815
|
return events;
|
|
4120
5816
|
}
|
|
4121
5817
|
case "PostToolUseFailure": {
|
|
4122
|
-
const id =
|
|
5818
|
+
const id = asString2(p["tool_use_id"]) ?? deriveApprovalId(p);
|
|
4123
5819
|
const interrupted = asBool(p["is_interrupt"]) === true;
|
|
4124
5820
|
const events = [
|
|
4125
5821
|
{
|
|
@@ -4127,14 +5823,14 @@ function mapHookPayload(payload) {
|
|
|
4127
5823
|
toolUseId: id,
|
|
4128
5824
|
ok: false,
|
|
4129
5825
|
...asNumber(p["duration_ms"]) !== void 0 ? { durationMs: asNumber(p["duration_ms"]) } : {},
|
|
4130
|
-
...
|
|
5826
|
+
...asString2(p["error"]) ? { error: asString2(p["error"]) } : {}
|
|
4131
5827
|
},
|
|
4132
5828
|
{ kind: "approval_resolved", id, outcome: interrupted ? "deny" : "unknown" }
|
|
4133
5829
|
];
|
|
4134
5830
|
return events;
|
|
4135
5831
|
}
|
|
4136
5832
|
case "UserPromptSubmit": {
|
|
4137
|
-
const text =
|
|
5833
|
+
const text = asString2(p["prompt"]);
|
|
4138
5834
|
if (!text)
|
|
4139
5835
|
return [];
|
|
4140
5836
|
return [{ kind: "user_prompt", text }];
|
|
@@ -4143,7 +5839,7 @@ function mapHookPayload(payload) {
|
|
|
4143
5839
|
return [
|
|
4144
5840
|
{
|
|
4145
5841
|
kind: "turn_ended",
|
|
4146
|
-
...
|
|
5842
|
+
...asString2(p["last_assistant_message"]) ? { lastMessage: asString2(p["last_assistant_message"]) } : {}
|
|
4147
5843
|
}
|
|
4148
5844
|
];
|
|
4149
5845
|
}
|
|
@@ -4151,8 +5847,8 @@ function mapHookPayload(payload) {
|
|
|
4151
5847
|
return [
|
|
4152
5848
|
{
|
|
4153
5849
|
kind: "turn_failed",
|
|
4154
|
-
errorKind:
|
|
4155
|
-
...
|
|
5850
|
+
errorKind: asString2(p["error"]) ?? "unknown",
|
|
5851
|
+
...asString2(p["error_details"]) ? { detail: asString2(p["error_details"]) } : {}
|
|
4156
5852
|
}
|
|
4157
5853
|
];
|
|
4158
5854
|
}
|
|
@@ -4161,7 +5857,7 @@ function mapHookPayload(payload) {
|
|
|
4161
5857
|
{
|
|
4162
5858
|
kind: "session_event",
|
|
4163
5859
|
phase: "start",
|
|
4164
|
-
...
|
|
5860
|
+
...asString2(p["source"]) ? { detail: asString2(p["source"]) } : {}
|
|
4165
5861
|
}
|
|
4166
5862
|
];
|
|
4167
5863
|
}
|
|
@@ -4170,7 +5866,7 @@ function mapHookPayload(payload) {
|
|
|
4170
5866
|
{
|
|
4171
5867
|
kind: "session_event",
|
|
4172
5868
|
phase: "end",
|
|
4173
|
-
...
|
|
5869
|
+
...asString2(p["reason"]) ? { detail: asString2(p["reason"]) } : {}
|
|
4174
5870
|
}
|
|
4175
5871
|
];
|
|
4176
5872
|
}
|
|
@@ -4179,7 +5875,7 @@ function mapHookPayload(payload) {
|
|
|
4179
5875
|
{
|
|
4180
5876
|
kind: "session_event",
|
|
4181
5877
|
phase: "compact_start",
|
|
4182
|
-
...
|
|
5878
|
+
...asString2(p["trigger"]) ? { detail: asString2(p["trigger"]) } : {}
|
|
4183
5879
|
}
|
|
4184
5880
|
];
|
|
4185
5881
|
}
|
|
@@ -4188,13 +5884,13 @@ function mapHookPayload(payload) {
|
|
|
4188
5884
|
{
|
|
4189
5885
|
kind: "session_event",
|
|
4190
5886
|
phase: "compact_end",
|
|
4191
|
-
...
|
|
5887
|
+
...asString2(p["trigger"]) ? { detail: asString2(p["trigger"]) } : {}
|
|
4192
5888
|
}
|
|
4193
5889
|
];
|
|
4194
5890
|
}
|
|
4195
5891
|
case "CwdChanged": {
|
|
4196
|
-
const from =
|
|
4197
|
-
const to =
|
|
5892
|
+
const from = asString2(p["old_cwd"]);
|
|
5893
|
+
const to = asString2(p["new_cwd"]);
|
|
4198
5894
|
if (!from || !to)
|
|
4199
5895
|
return [];
|
|
4200
5896
|
return [{ kind: "cwd_changed", from, to }];
|
|
@@ -4968,13 +6664,13 @@ __export(resolve_executable_exports, {
|
|
|
4968
6664
|
resolveExecutable: () => resolveExecutable
|
|
4969
6665
|
});
|
|
4970
6666
|
import { accessSync, existsSync as existsSync9, readdirSync, statSync as statSync3, constants } from "node:fs";
|
|
4971
|
-
import { resolve as pathResolve, isAbsolute, sep } from "node:path";
|
|
6667
|
+
import { resolve as pathResolve, isAbsolute as isAbsolute2, sep as sep2 } from "node:path";
|
|
4972
6668
|
import { delimiter } from "node:path";
|
|
4973
6669
|
function resolveExecutable(program, env = process.env) {
|
|
4974
6670
|
if (!program || program.length === 0)
|
|
4975
6671
|
return null;
|
|
4976
|
-
if (program.includes("/") || program.includes(
|
|
4977
|
-
const abs =
|
|
6672
|
+
if (program.includes("/") || program.includes(sep2)) {
|
|
6673
|
+
const abs = isAbsolute2(program) ? program : pathResolve(program);
|
|
4978
6674
|
return isExecutable(abs) ? abs : null;
|
|
4979
6675
|
}
|
|
4980
6676
|
const pathEnv = env["PATH"] ?? env["Path"] ?? "";
|
|
@@ -5073,7 +6769,7 @@ function colorsEnabled() {
|
|
|
5073
6769
|
}
|
|
5074
6770
|
return Boolean(process.stdout.isTTY);
|
|
5075
6771
|
}
|
|
5076
|
-
function
|
|
6772
|
+
function wrap2(fn) {
|
|
5077
6773
|
return (s) => colorsEnabled() ? fn(s) : s;
|
|
5078
6774
|
}
|
|
5079
6775
|
var pcForced, forceDisabled, c;
|
|
@@ -5083,14 +6779,14 @@ var init_colors = __esm({
|
|
|
5083
6779
|
pcForced = pc.createColors(true);
|
|
5084
6780
|
forceDisabled = false;
|
|
5085
6781
|
c = {
|
|
5086
|
-
bold:
|
|
5087
|
-
dim:
|
|
5088
|
-
red:
|
|
5089
|
-
green:
|
|
5090
|
-
yellow:
|
|
5091
|
-
blue:
|
|
5092
|
-
cyan:
|
|
5093
|
-
gray:
|
|
6782
|
+
bold: wrap2(pcForced.bold),
|
|
6783
|
+
dim: wrap2(pcForced.dim),
|
|
6784
|
+
red: wrap2(pcForced.red),
|
|
6785
|
+
green: wrap2(pcForced.green),
|
|
6786
|
+
yellow: wrap2(pcForced.yellow),
|
|
6787
|
+
blue: wrap2(pcForced.blue),
|
|
6788
|
+
cyan: wrap2(pcForced.cyan),
|
|
6789
|
+
gray: wrap2(pcForced.gray)
|
|
5094
6790
|
};
|
|
5095
6791
|
}
|
|
5096
6792
|
});
|
|
@@ -5121,7 +6817,7 @@ var init_did_you_mean = __esm({
|
|
|
5121
6817
|
|
|
5122
6818
|
// backend/dist/broker/broker-state.js
|
|
5123
6819
|
import { existsSync as existsSync10, mkdirSync as mkdirSync9, readFileSync as readFileSync7, rmSync as rmSync2 } from "node:fs";
|
|
5124
|
-
import { dirname as
|
|
6820
|
+
import { dirname as dirname6, resolve as resolve9 } from "node:path";
|
|
5125
6821
|
import { homedir as homedir7 } from "node:os";
|
|
5126
6822
|
function defaultBrokerStatePath() {
|
|
5127
6823
|
return resolve9(homedir7(), ATR_DATA_DIR, BROKER_STATE_FILENAME);
|
|
@@ -5162,7 +6858,7 @@ function readBrokerState(path = defaultBrokerStatePath()) {
|
|
|
5162
6858
|
return { version: SCHEMA_VERSION2, pid, port, host, startedAt, brokerVersion };
|
|
5163
6859
|
}
|
|
5164
6860
|
function writeBrokerState(state, path = defaultBrokerStatePath()) {
|
|
5165
|
-
const dir =
|
|
6861
|
+
const dir = dirname6(path);
|
|
5166
6862
|
if (!existsSync10(dir)) {
|
|
5167
6863
|
try {
|
|
5168
6864
|
mkdirSync9(dir, { recursive: true, mode: 448 });
|
|
@@ -5548,6 +7244,10 @@ function createBrokerApp(opts) {
|
|
|
5548
7244
|
if (req.path.startsWith("/api") || req.path.startsWith("/ws")) {
|
|
5549
7245
|
return next();
|
|
5550
7246
|
}
|
|
7247
|
+
if (isStaticAssetPath(req.path)) {
|
|
7248
|
+
res.status(404).type("text/plain").send("not found");
|
|
7249
|
+
return;
|
|
7250
|
+
}
|
|
5551
7251
|
const instanceId = req.__atrInstanceId;
|
|
5552
7252
|
const urlToken = typeof req.query.token === "string" ? req.query.token : null;
|
|
5553
7253
|
if (!instanceId && !urlToken) {
|
|
@@ -5635,6 +7335,11 @@ async function startBrokerServer(opts) {
|
|
|
5635
7335
|
}
|
|
5636
7336
|
};
|
|
5637
7337
|
}
|
|
7338
|
+
function isStaticAssetPath(p) {
|
|
7339
|
+
if (p.startsWith("/assets/"))
|
|
7340
|
+
return true;
|
|
7341
|
+
return STATIC_EXT_RE.test(p);
|
|
7342
|
+
}
|
|
5638
7343
|
function collectLocalHostnames() {
|
|
5639
7344
|
const set = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "::1"]);
|
|
5640
7345
|
const ifaces = networkInterfaces3();
|
|
@@ -5655,7 +7360,7 @@ function collectLocalHostnames() {
|
|
|
5655
7360
|
}
|
|
5656
7361
|
return set;
|
|
5657
7362
|
}
|
|
5658
|
-
var DEFAULT_BROKER_PORT, DEFAULT_BROKER_HOST;
|
|
7363
|
+
var DEFAULT_BROKER_PORT, DEFAULT_BROKER_HOST, STATIC_EXT_RE;
|
|
5659
7364
|
var init_broker_server = __esm({
|
|
5660
7365
|
"backend/dist/broker/broker-server.js"() {
|
|
5661
7366
|
"use strict";
|
|
@@ -5667,13 +7372,14 @@ var init_broker_server = __esm({
|
|
|
5667
7372
|
init_port_finder();
|
|
5668
7373
|
DEFAULT_BROKER_PORT = 3737;
|
|
5669
7374
|
DEFAULT_BROKER_HOST = "0.0.0.0";
|
|
7375
|
+
STATIC_EXT_RE = /\.(js|mjs|cjs|css|map|json|webmanifest|png|jpg|jpeg|gif|svg|webp|ico|avif|woff|woff2|ttf|otf|wasm)$/i;
|
|
5670
7376
|
}
|
|
5671
7377
|
});
|
|
5672
7378
|
|
|
5673
7379
|
// backend/dist/broker/ensure-broker.js
|
|
5674
7380
|
import { spawn as spawn2 } from "node:child_process";
|
|
5675
7381
|
import { existsSync as existsSync12, mkdirSync as mkdirSync10, openSync } from "node:fs";
|
|
5676
|
-
import { dirname as
|
|
7382
|
+
import { dirname as dirname7, resolve as resolve11 } from "node:path";
|
|
5677
7383
|
import { homedir as homedir8 } from "node:os";
|
|
5678
7384
|
function defaultBrokerLockDir() {
|
|
5679
7385
|
return resolve11(homedir8(), ATR_DATA_DIR, ".broker.lock");
|
|
@@ -5756,7 +7462,7 @@ function resolveBrokerEntry(cliJsPath) {
|
|
|
5756
7462
|
return { execPath: process.execPath, args: [cliJsPath] };
|
|
5757
7463
|
}
|
|
5758
7464
|
function ensureParentDir(lockDir) {
|
|
5759
|
-
const parent =
|
|
7465
|
+
const parent = dirname7(lockDir);
|
|
5760
7466
|
if (existsSync12(parent))
|
|
5761
7467
|
return;
|
|
5762
7468
|
try {
|
|
@@ -5996,7 +7702,7 @@ var entry_prompt_exports = {};
|
|
|
5996
7702
|
__export(entry_prompt_exports, {
|
|
5997
7703
|
promptEntrySelection: () => promptEntrySelection
|
|
5998
7704
|
});
|
|
5999
|
-
import { createInterface } from "node:readline";
|
|
7705
|
+
import { createInterface as createInterface2 } from "node:readline";
|
|
6000
7706
|
async function promptEntrySelection(opts) {
|
|
6001
7707
|
const { candidates } = opts;
|
|
6002
7708
|
if (candidates.length === 0) {
|
|
@@ -6026,7 +7732,7 @@ async function promptEntrySelection(opts) {
|
|
|
6026
7732
|
const timeoutMs = opts.timeoutMs ?? 0;
|
|
6027
7733
|
const useTimeout = timeoutMs > 0;
|
|
6028
7734
|
const promptLine = useTimeout ? ` \u9009\u62E9 [1-${candidates.length}\uFF0C\u56DE\u8F66=${defaultIdx + 1}\uFF0C${Math.round(timeoutMs / 1e3)}s \u8D85\u65F6\u9ED8\u8BA4]: ` : ` \u9009\u62E9 [1-${candidates.length}\uFF0C\u56DE\u8F66=${defaultIdx + 1}]: `;
|
|
6029
|
-
const rl =
|
|
7735
|
+
const rl = createInterface2({
|
|
6030
7736
|
input,
|
|
6031
7737
|
output,
|
|
6032
7738
|
terminal: false
|
|
@@ -6091,7 +7797,7 @@ __export(index_exports, {
|
|
|
6091
7797
|
import { createServer as createServer3 } from "node:http";
|
|
6092
7798
|
import { execSync } from "node:child_process";
|
|
6093
7799
|
import { networkInterfaces as networkInterfaces5 } from "node:os";
|
|
6094
|
-
import { resolve as resolve12, dirname as
|
|
7800
|
+
import { resolve as resolve12, dirname as dirname8 } from "node:path";
|
|
6095
7801
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
6096
7802
|
import express2 from "express";
|
|
6097
7803
|
import cors2 from "cors";
|
|
@@ -6157,7 +7863,7 @@ async function startServer(overrides = {}) {
|
|
|
6157
7863
|
}
|
|
6158
7864
|
logger.info({ requested: cfg.claudeCommand, resolved }, "PTY program resolved");
|
|
6159
7865
|
}
|
|
6160
|
-
const __dirnameForBroker =
|
|
7866
|
+
const __dirnameForBroker = dirname8(fileURLToPath2(import.meta.url));
|
|
6161
7867
|
const cliJsPathForBroker = resolve12(__dirnameForBroker, "cli.js");
|
|
6162
7868
|
const { ensureBroker: ensureBroker2 } = await Promise.resolve().then(() => (init_broker(), broker_exports));
|
|
6163
7869
|
let brokerState;
|
|
@@ -6249,7 +7955,7 @@ hint: check whether ~/.atr/broker.json is held by a stale process; set ATR_DEBUG
|
|
|
6249
7955
|
legacyCookieNames: [createSessionCookieName(cfg.port)],
|
|
6250
7956
|
sessions: sessionsStore
|
|
6251
7957
|
});
|
|
6252
|
-
const __dirname2 =
|
|
7958
|
+
const __dirname2 = dirname8(fileURLToPath2(import.meta.url));
|
|
6253
7959
|
const registry = new InstanceRegistryManager();
|
|
6254
7960
|
const localHostnames = collectLocalHostnames2();
|
|
6255
7961
|
logger.info({ hostnames: Array.from(localHostnames) }, "CORS \u767D\u540D\u5355");
|
|
@@ -6357,7 +8063,7 @@ hint: check whether ~/.atr/broker.json is held by a stale process; set ATR_DEBUG
|
|
|
6357
8063
|
const tokenPreview = cfg.token.length >= 16 ? `${cfg.token.slice(0, 8)}...${cfg.token.slice(-8)}` : cfg.token;
|
|
6358
8064
|
const BOX_INNER = 50;
|
|
6359
8065
|
const top = `\u2554${"\u2550".repeat(BOX_INNER)}\u2557`;
|
|
6360
|
-
const
|
|
8066
|
+
const sep3 = `\u2560${"\u2550".repeat(BOX_INNER)}\u2563`;
|
|
6361
8067
|
const bot = `\u255A${"\u2550".repeat(BOX_INNER)}\u255D`;
|
|
6362
8068
|
const row = (text) => {
|
|
6363
8069
|
const trimmed = text.length <= BOX_INNER ? text.padEnd(BOX_INNER) : text.slice(0, BOX_INNER);
|
|
@@ -6373,13 +8079,13 @@ hint: check whether ~/.atr/broker.json is held by a stale process; set ATR_DEBUG
|
|
|
6373
8079
|
process.stderr.write("\n");
|
|
6374
8080
|
process.stderr.write(top + "\n");
|
|
6375
8081
|
process.stderr.write(center("Auvezy Terminal Remote \xB7 started") + "\n");
|
|
6376
|
-
process.stderr.write(
|
|
8082
|
+
process.stderr.write(sep3 + "\n");
|
|
6377
8083
|
process.stderr.write(row(` instance: ${cfg.instanceName}`) + "\n");
|
|
6378
8084
|
process.stderr.write(row(` service: http://${brokerState.host}:${brokerState.port}`) + "\n");
|
|
6379
8085
|
process.stderr.write(row(` token: ${tokenPreview}`) + "\n");
|
|
6380
8086
|
process.stderr.write(row(` source: ${cfg.tokenSource}`) + "\n");
|
|
6381
8087
|
if (cfg.tokenSource === "generated") {
|
|
6382
|
-
process.stderr.write(
|
|
8088
|
+
process.stderr.write(sep3 + "\n");
|
|
6383
8089
|
process.stderr.write(row(" Full token (shown once \u2014 save it now):") + "\n");
|
|
6384
8090
|
process.stderr.write(row(` ${cfg.token.slice(0, 48)}`) + "\n");
|
|
6385
8091
|
process.stderr.write(row(` ${cfg.token.slice(48)}`) + "\n");
|
|
@@ -7305,7 +9011,7 @@ var init_attach = __esm({
|
|
|
7305
9011
|
|
|
7306
9012
|
// backend/dist/broker/broker-log-rotator.js
|
|
7307
9013
|
import { appendFileSync, existsSync as existsSync13, mkdirSync as mkdirSync11, readdirSync as readdirSync2, statSync as statSync4, unlinkSync as unlinkSync2 } from "node:fs";
|
|
7308
|
-
import { resolve as resolve13, basename as
|
|
9014
|
+
import { resolve as resolve13, basename as basename6 } from "node:path";
|
|
7309
9015
|
function createBrokerLogRotator(opts) {
|
|
7310
9016
|
const now = opts.now ?? (() => /* @__PURE__ */ new Date());
|
|
7311
9017
|
if (!existsSync13(opts.dir)) {
|
|
@@ -7390,51 +9096,10 @@ var init_broker_log_rotator = __esm({
|
|
|
7390
9096
|
}
|
|
7391
9097
|
});
|
|
7392
9098
|
|
|
7393
|
-
// backend/dist/utils/workdir-policy.js
|
|
7394
|
-
import picomatch from "picomatch";
|
|
7395
|
-
function checkWorkdir(cwd, allow, deny) {
|
|
7396
|
-
const norm = normalizePath(cwd);
|
|
7397
|
-
const opts = { dot: true };
|
|
7398
|
-
if (deny && deny.length > 0) {
|
|
7399
|
-
for (const pattern of deny) {
|
|
7400
|
-
if (picomatch(pattern, opts)(norm)) {
|
|
7401
|
-
return {
|
|
7402
|
-
reason: `cwd "${cwd}" \u547D\u4E2D\u9ED1\u540D\u5355 pattern\uFF1A${pattern}`,
|
|
7403
|
-
matchedPattern: pattern
|
|
7404
|
-
};
|
|
7405
|
-
}
|
|
7406
|
-
}
|
|
7407
|
-
}
|
|
7408
|
-
if (allow && allow.length > 0) {
|
|
7409
|
-
let hit = false;
|
|
7410
|
-
for (const pattern of allow) {
|
|
7411
|
-
if (picomatch(pattern, opts)(norm)) {
|
|
7412
|
-
hit = true;
|
|
7413
|
-
break;
|
|
7414
|
-
}
|
|
7415
|
-
}
|
|
7416
|
-
if (!hit) {
|
|
7417
|
-
return {
|
|
7418
|
-
reason: `cwd "${cwd}" \u672A\u547D\u4E2D\u4EFB\u4F55\u767D\u540D\u5355 pattern\uFF1A[${allow.join(", ")}]`,
|
|
7419
|
-
matchedPattern: ""
|
|
7420
|
-
};
|
|
7421
|
-
}
|
|
7422
|
-
}
|
|
7423
|
-
return null;
|
|
7424
|
-
}
|
|
7425
|
-
function normalizePath(p) {
|
|
7426
|
-
return p.replace(/\\/g, "/");
|
|
7427
|
-
}
|
|
7428
|
-
var init_workdir_policy = __esm({
|
|
7429
|
-
"backend/dist/utils/workdir-policy.js"() {
|
|
7430
|
-
"use strict";
|
|
7431
|
-
}
|
|
7432
|
-
});
|
|
7433
|
-
|
|
7434
9099
|
// backend/dist/registry/instance-spawner.js
|
|
7435
9100
|
import { spawn as spawn3 } from "node:child_process";
|
|
7436
9101
|
import { existsSync as existsSync14, statSync as statSync5, openSync as openSync2 } from "node:fs";
|
|
7437
|
-
import { resolve as resolve14, isAbsolute as
|
|
9102
|
+
import { resolve as resolve14, isAbsolute as isAbsolute3, dirname as dirname9 } from "node:path";
|
|
7438
9103
|
function waitForEarlyExit(child, timeoutMs) {
|
|
7439
9104
|
return new Promise((res) => {
|
|
7440
9105
|
let settled = false;
|
|
@@ -7460,7 +9125,7 @@ function resolveEntry(cliJsPath) {
|
|
|
7460
9125
|
}
|
|
7461
9126
|
const tsPath = cliJsPath.replace(/\.js$/, ".ts");
|
|
7462
9127
|
if (existsSync14(tsPath)) {
|
|
7463
|
-
const tsxBin = findTsxBin(
|
|
9128
|
+
const tsxBin = findTsxBin(dirname9(tsPath));
|
|
7464
9129
|
if (tsxBin) {
|
|
7465
9130
|
return { execPath: tsxBin, args: [tsPath] };
|
|
7466
9131
|
}
|
|
@@ -7477,14 +9142,14 @@ function findTsxBin(startDir) {
|
|
|
7477
9142
|
const candidate = resolve14(dir, "node_modules", ".bin", "tsx");
|
|
7478
9143
|
if (existsSync14(candidate))
|
|
7479
9144
|
return candidate;
|
|
7480
|
-
const parent =
|
|
9145
|
+
const parent = dirname9(dir);
|
|
7481
9146
|
if (parent === dir)
|
|
7482
9147
|
break;
|
|
7483
9148
|
dir = parent;
|
|
7484
9149
|
}
|
|
7485
9150
|
return null;
|
|
7486
9151
|
}
|
|
7487
|
-
function
|
|
9152
|
+
function basename7(p) {
|
|
7488
9153
|
const parts = p.split(/[\\/]/).filter(Boolean);
|
|
7489
9154
|
return parts[parts.length - 1] ?? "instance";
|
|
7490
9155
|
}
|
|
@@ -7502,7 +9167,7 @@ var init_instance_spawner = __esm({
|
|
|
7502
9167
|
this.opts = opts;
|
|
7503
9168
|
}
|
|
7504
9169
|
async spawn(input) {
|
|
7505
|
-
const cwd =
|
|
9170
|
+
const cwd = isAbsolute3(input.cwd) ? input.cwd : resolve14(input.cwd);
|
|
7506
9171
|
if (!existsSync14(cwd)) {
|
|
7507
9172
|
throw new InstanceError(ErrorCode.CWD_NOT_EXIST, `cwd does not exist: ${cwd}`, 400);
|
|
7508
9173
|
}
|
|
@@ -7514,7 +9179,7 @@ var init_instance_spawner = __esm({
|
|
|
7514
9179
|
logger.warn({ cwd, verdict }, "workdir policy rejected spawn");
|
|
7515
9180
|
throw new InstanceError(ErrorCode.CWD_NOT_EXIST, `cwd not allowed by workdir policy: ${verdict.reason}`, 403);
|
|
7516
9181
|
}
|
|
7517
|
-
const name = input.name && input.name.trim() ? input.name.trim() :
|
|
9182
|
+
const name = input.name && input.name.trim() ? input.name.trim() : basename7(cwd);
|
|
7518
9183
|
const { execPath, args: entryArgs } = resolveEntry(this.opts.cliJsPath);
|
|
7519
9184
|
const logFd = process.env.ATR_DEBUG_SPAWN ? openSync2(`/tmp/atr-spawn-${Date.now()}.log`, "a") : "ignore";
|
|
7520
9185
|
const child = spawn3(execPath, [...entryArgs, "--no-terminal"], {
|
|
@@ -7571,7 +9236,7 @@ __export(service_installer_exports, {
|
|
|
7571
9236
|
uninstall: () => uninstall
|
|
7572
9237
|
});
|
|
7573
9238
|
import { existsSync as existsSync15, mkdirSync as mkdirSync12, readFileSync as readFileSync10, rmSync as rmSync3, writeFileSync as writeFileSync5 } from "node:fs";
|
|
7574
|
-
import { resolve as resolve15, dirname as
|
|
9239
|
+
import { resolve as resolve15, dirname as dirname10 } from "node:path";
|
|
7575
9240
|
import { homedir as homedir9, platform } from "node:os";
|
|
7576
9241
|
function detectPlatform(env = process.env, plat = platform()) {
|
|
7577
9242
|
if (plat === "darwin")
|
|
@@ -7646,8 +9311,8 @@ function install(opts) {
|
|
|
7646
9311
|
if (platformDetected === "macos") {
|
|
7647
9312
|
const path2 = launchdPlistPath(home);
|
|
7648
9313
|
const logPath = brokerLogPath(home);
|
|
7649
|
-
ensureDir(fs,
|
|
7650
|
-
ensureDir(fs,
|
|
9314
|
+
ensureDir(fs, dirname10(path2));
|
|
9315
|
+
ensureDir(fs, dirname10(logPath));
|
|
7651
9316
|
fs.writeFileSync(path2, renderLaunchdPlist({
|
|
7652
9317
|
nodeBin: opts.nodeBin,
|
|
7653
9318
|
cliPath: opts.cliPath,
|
|
@@ -7665,7 +9330,7 @@ function install(opts) {
|
|
|
7665
9330
|
};
|
|
7666
9331
|
}
|
|
7667
9332
|
const path = systemdUnitPath(home);
|
|
7668
|
-
ensureDir(fs,
|
|
9333
|
+
ensureDir(fs, dirname10(path));
|
|
7669
9334
|
fs.writeFileSync(path, renderSystemdUnit({
|
|
7670
9335
|
nodeBin: opts.nodeBin,
|
|
7671
9336
|
cliPath: opts.cliPath,
|
|
@@ -7792,11 +9457,11 @@ __export(cli_exports, {
|
|
|
7792
9457
|
});
|
|
7793
9458
|
import { execSync as execSync2, spawn as spawn4 } from "node:child_process";
|
|
7794
9459
|
import { existsSync as existsSync16, openSync as openSync3, readFileSync as readFileSync11 } from "node:fs";
|
|
7795
|
-
import { resolve as resolve16, dirname as
|
|
9460
|
+
import { resolve as resolve16, dirname as dirname11 } from "node:path";
|
|
7796
9461
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
7797
9462
|
import { homedir as homedir10 } from "node:os";
|
|
7798
9463
|
function getBrokerVersion() {
|
|
7799
|
-
const __dirname2 =
|
|
9464
|
+
const __dirname2 = dirname11(fileURLToPath3(import.meta.url));
|
|
7800
9465
|
const candidates = [
|
|
7801
9466
|
resolve16(__dirname2, "package.json"),
|
|
7802
9467
|
resolve16(__dirname2, "..", "package.json"),
|
|
@@ -7815,7 +9480,7 @@ function getBrokerVersion() {
|
|
|
7815
9480
|
return "0.0.0";
|
|
7816
9481
|
}
|
|
7817
9482
|
function getCliPath() {
|
|
7818
|
-
const __dirname2 =
|
|
9483
|
+
const __dirname2 = dirname11(fileURLToPath3(import.meta.url));
|
|
7819
9484
|
const parentDir = resolve16(__dirname2, "..", "cli.js");
|
|
7820
9485
|
const sameDir = resolve16(__dirname2, "cli.js");
|
|
7821
9486
|
try {
|
|
@@ -7860,7 +9525,7 @@ async function runBrokerStart(cli) {
|
|
|
7860
9525
|
process.stderr.write(`[atr] log file: ${logRotator.currentFilePath()}
|
|
7861
9526
|
`);
|
|
7862
9527
|
const registry = new InstanceRegistryManager();
|
|
7863
|
-
const __dirname2 =
|
|
9528
|
+
const __dirname2 = dirname11(fileURLToPath3(import.meta.url));
|
|
7864
9529
|
const frontendDist = resolve16(__dirname2, "..", "frontend-dist");
|
|
7865
9530
|
let sharedToken;
|
|
7866
9531
|
try {
|
|
@@ -7912,7 +9577,8 @@ async function runBrokerStart(cli) {
|
|
|
7912
9577
|
brokerPort: port,
|
|
7913
9578
|
displayIp,
|
|
7914
9579
|
workdirPolicy: () => ({
|
|
7915
|
-
allow: currentUserConfig.workdirAllow ?? []
|
|
9580
|
+
allow: currentUserConfig.workdirAllow ?? [],
|
|
9581
|
+
deny: currentUserConfig.workdirDeny ?? []
|
|
7916
9582
|
})
|
|
7917
9583
|
};
|
|
7918
9584
|
let handle;
|
|
@@ -8208,12 +9874,12 @@ async function writeTokenSection() {
|
|
|
8208
9874
|
const { resolve: pathResolve2 } = await import("node:path");
|
|
8209
9875
|
const { homedir: homedir11 } = await import("node:os");
|
|
8210
9876
|
const path = pathResolve2(homedir11(), ".atrrc");
|
|
8211
|
-
const
|
|
9877
|
+
const stat3 = statSync6(path);
|
|
8212
9878
|
const cfg = JSON.parse(readFileSync12(path, "utf-8"));
|
|
8213
9879
|
if (typeof cfg.token === "string" && cfg.token.length > 0) {
|
|
8214
9880
|
process.stdout.write(` token: ${cfg.token}
|
|
8215
9881
|
`);
|
|
8216
|
-
process.stdout.write(` file: ${path} (mode ${(
|
|
9882
|
+
process.stdout.write(` file: ${path} (mode ${(stat3.mode & 511).toString(8)})
|
|
8217
9883
|
`);
|
|
8218
9884
|
} else {
|
|
8219
9885
|
process.stdout.write(` token: (none; ${path} has no .token field)
|
|
@@ -8492,9 +10158,9 @@ void (async () => {
|
|
|
8492
10158
|
}
|
|
8493
10159
|
if (cli.version) {
|
|
8494
10160
|
const { readFileSync: readFileSync12 } = await import("node:fs");
|
|
8495
|
-
const { resolve: resolve17, dirname:
|
|
10161
|
+
const { resolve: resolve17, dirname: dirname12 } = await import("node:path");
|
|
8496
10162
|
const { fileURLToPath: fileURLToPath4 } = await import("node:url");
|
|
8497
|
-
const __dirname2 =
|
|
10163
|
+
const __dirname2 = dirname12(fileURLToPath4(import.meta.url));
|
|
8498
10164
|
const pkg = JSON.parse(readFileSync12(resolve17(__dirname2, "..", "package.json"), "utf-8"));
|
|
8499
10165
|
process.stdout.write(`${pkg.version}
|
|
8500
10166
|
`);
|