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.
Files changed (379) hide show
  1. package/dist/cli.js +1962 -296
  2. package/frontend-dist/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  3. package/frontend-dist/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  4. package/frontend-dist/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  5. package/frontend-dist/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  6. package/frontend-dist/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  7. package/frontend-dist/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  8. package/frontend-dist/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  9. package/frontend-dist/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  10. package/frontend-dist/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  11. package/frontend-dist/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  12. package/frontend-dist/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  13. package/frontend-dist/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  14. package/frontend-dist/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  15. package/frontend-dist/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  16. package/frontend-dist/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  17. package/frontend-dist/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  18. package/frontend-dist/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  19. package/frontend-dist/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  20. package/frontend-dist/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  21. package/frontend-dist/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  22. package/frontend-dist/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  23. package/frontend-dist/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  24. package/frontend-dist/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  25. package/frontend-dist/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  26. package/frontend-dist/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  27. package/frontend-dist/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  28. package/frontend-dist/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  29. package/frontend-dist/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  30. package/frontend-dist/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  31. package/frontend-dist/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  32. package/frontend-dist/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  33. package/frontend-dist/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  34. package/frontend-dist/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  35. package/frontend-dist/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  36. package/frontend-dist/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  37. package/frontend-dist/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  38. package/frontend-dist/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  39. package/frontend-dist/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  40. package/frontend-dist/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  41. package/frontend-dist/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  42. package/frontend-dist/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  43. package/frontend-dist/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  44. package/frontend-dist/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  45. package/frontend-dist/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  46. package/frontend-dist/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  47. package/frontend-dist/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  48. package/frontend-dist/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  49. package/frontend-dist/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  50. package/frontend-dist/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  51. package/frontend-dist/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  52. package/frontend-dist/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  53. package/frontend-dist/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  54. package/frontend-dist/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  55. package/frontend-dist/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  56. package/frontend-dist/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  57. package/frontend-dist/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  58. package/frontend-dist/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  59. package/frontend-dist/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  60. package/frontend-dist/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  61. package/frontend-dist/assets/MarkdownPreview-BhHx4UZB.js +294 -0
  62. package/frontend-dist/assets/MarkdownPreview-UDCyb9Vn.css +1 -0
  63. package/frontend-dist/assets/abap-CLvhMVsD.js +1 -0
  64. package/frontend-dist/assets/actionscript-3--17pq3dv.js +1 -0
  65. package/frontend-dist/assets/ada-C5qYipkI.js +1 -0
  66. package/frontend-dist/assets/andromeeda-vGVdxbeo.js +1 -0
  67. package/frontend-dist/assets/angular-html-DgjvnXWT.js +1 -0
  68. package/frontend-dist/assets/angular-ts-BsJfg-C9.js +1 -0
  69. package/frontend-dist/assets/apache-U0d_L8uA.js +1 -0
  70. package/frontend-dist/assets/apex-VAyPSnFM.js +1 -0
  71. package/frontend-dist/assets/apl-owRgUhC0.js +1 -0
  72. package/frontend-dist/assets/applescript-CCn79oCD.js +1 -0
  73. package/frontend-dist/assets/ara-4CJ0cIlV.js +1 -0
  74. package/frontend-dist/assets/asciidoc-DE70LPWp.js +1 -0
  75. package/frontend-dist/assets/asm-Cmm7eHzH.js +1 -0
  76. package/frontend-dist/assets/astro-ByvNTrWi.js +1 -0
  77. package/frontend-dist/assets/aurora-x-CDeNXAV0.js +1 -0
  78. package/frontend-dist/assets/awk-BWXHIvNe.js +1 -0
  79. package/frontend-dist/assets/ayu-dark-DluEY0Gj.js +1 -0
  80. package/frontend-dist/assets/ayu-light-C3h-C4tm.js +1 -0
  81. package/frontend-dist/assets/ayu-mirage-Bqwy1Gya.js +1 -0
  82. package/frontend-dist/assets/ballerina-B7ZEbQpA.js +1 -0
  83. package/frontend-dist/assets/bat-Bo4NYOV-.js +1 -0
  84. package/frontend-dist/assets/beancount-D-usSTwE.js +1 -0
  85. package/frontend-dist/assets/berry-DKpUyyne.js +1 -0
  86. package/frontend-dist/assets/bibtex-Ci_nEsc7.js +1 -0
  87. package/frontend-dist/assets/bicep-CUHmPFLl.js +1 -0
  88. package/frontend-dist/assets/bird2-C6vDhewU.js +1 -0
  89. package/frontend-dist/assets/blade-DBB6j2sS.js +1 -0
  90. package/frontend-dist/assets/bsl-BkkzgIyY.js +1 -0
  91. package/frontend-dist/assets/c-Ba6e6JWz.js +1 -0
  92. package/frontend-dist/assets/c3-BFHwR3_K.js +1 -0
  93. package/frontend-dist/assets/cadence-CQ2zXKGN.js +1 -0
  94. package/frontend-dist/assets/cairo-DLTphjLi.js +1 -0
  95. package/frontend-dist/assets/catppuccin-frappe-3VR1Za6u.js +1 -0
  96. package/frontend-dist/assets/catppuccin-latte-DwIHMF0Q.js +1 -0
  97. package/frontend-dist/assets/catppuccin-macchiato-DYnBP6_5.js +1 -0
  98. package/frontend-dist/assets/catppuccin-mocha-DYhrFGRu.js +1 -0
  99. package/frontend-dist/assets/clarity-SemFz856.js +1 -0
  100. package/frontend-dist/assets/clojure-DqKBuwfJ.js +1 -0
  101. package/frontend-dist/assets/cmake-Bj61d0ZC.js +1 -0
  102. package/frontend-dist/assets/cobol-DyYl4Dsc.js +1 -0
  103. package/frontend-dist/assets/codeowners-C8r90Shi.js +1 -0
  104. package/frontend-dist/assets/codeql-oeQT6MSM.js +1 -0
  105. package/frontend-dist/assets/coffee-GDwlIkMr.js +1 -0
  106. package/frontend-dist/assets/common-lisp-Cv5bFMCO.js +1 -0
  107. package/frontend-dist/assets/coq-BrsZFFmf.js +1 -0
  108. package/frontend-dist/assets/cpp-DdEoNrsu.js +1 -0
  109. package/frontend-dist/assets/crystal-CsRfpFgZ.js +1 -0
  110. package/frontend-dist/assets/csharp-oqKa8noW.js +1 -0
  111. package/frontend-dist/assets/css-EcyqDbrn.js +1 -0
  112. package/frontend-dist/assets/csv-Dx-8-gkx.js +1 -0
  113. package/frontend-dist/assets/cue-CE9AQfxI.js +1 -0
  114. package/frontend-dist/assets/cypher-ClKdZ_lG.js +1 -0
  115. package/frontend-dist/assets/d-qD-0Kul2.js +1 -0
  116. package/frontend-dist/assets/dark-plus-Cs2F2srj.js +1 -0
  117. package/frontend-dist/assets/dart-CnvKMtbv.js +1 -0
  118. package/frontend-dist/assets/dax-BkyTk9wS.js +1 -0
  119. package/frontend-dist/assets/desktop-Dlh5hvp9.js +1 -0
  120. package/frontend-dist/assets/diff-woXpYk--.js +1 -0
  121. package/frontend-dist/assets/dist-BFglDfWz.js +153 -0
  122. package/frontend-dist/assets/docker-IyjqRm3v.js +1 -0
  123. package/frontend-dist/assets/dotenv-_5a1GRtc.js +1 -0
  124. package/frontend-dist/assets/dracula-BHWKrbxM.js +1 -0
  125. package/frontend-dist/assets/dracula-soft-5eyTD99u.js +1 -0
  126. package/frontend-dist/assets/dream-maker-DW3nJb8Q.js +1 -0
  127. package/frontend-dist/assets/edge-CXwacAJy.js +1 -0
  128. package/frontend-dist/assets/elixir-BPtzsf8i.js +1 -0
  129. package/frontend-dist/assets/elm-yyrGQdOF.js +1 -0
  130. package/frontend-dist/assets/emacs-lisp-B4R74twV.js +1 -0
  131. package/frontend-dist/assets/embed-ClpqVPsk.js +2 -0
  132. package/frontend-dist/assets/embed-JyQKJCHx.css +1 -0
  133. package/frontend-dist/assets/erb-CPVygpcZ.js +1 -0
  134. package/frontend-dist/assets/erlang-Cphh6RMH.js +1 -0
  135. package/frontend-dist/assets/eruda-tTLAveCP.js +366 -0
  136. package/frontend-dist/assets/everforest-dark-sB-x3p7T.js +1 -0
  137. package/frontend-dist/assets/everforest-light-Df2xbC6M.js +1 -0
  138. package/frontend-dist/assets/fennel-DQxkIbk2.js +1 -0
  139. package/frontend-dist/assets/fish-BJitypiv.js +1 -0
  140. package/frontend-dist/assets/fluent-C03EYrpw.js +1 -0
  141. package/frontend-dist/assets/fortran-fixed-form-DEKoE2YW.js +1 -0
  142. package/frontend-dist/assets/fortran-free-form-CYNrtFtB.js +1 -0
  143. package/frontend-dist/assets/fsharp-D13ZGOAj.js +1 -0
  144. package/frontend-dist/assets/gdresource-C0sCabJj.js +1 -0
  145. package/frontend-dist/assets/gdscript-Cp2uCuqX.js +1 -0
  146. package/frontend-dist/assets/gdshader-CBce3t8t.js +1 -0
  147. package/frontend-dist/assets/genie-CV2tkWYe.js +1 -0
  148. package/frontend-dist/assets/gherkin-DExj1W_8.js +1 -0
  149. package/frontend-dist/assets/git-commit-BSykSTBG.js +1 -0
  150. package/frontend-dist/assets/git-rebase-BxSEtpsP.js +1 -0
  151. package/frontend-dist/assets/github-dark-C-LZuMrd.js +1 -0
  152. package/frontend-dist/assets/github-dark-default-DXG-b-1a.js +1 -0
  153. package/frontend-dist/assets/github-dark-dimmed-Bx1FflLF.js +1 -0
  154. package/frontend-dist/assets/github-dark-high-contrast-B_tTalzw.js +1 -0
  155. package/frontend-dist/assets/github-light-EUqPIrTm.js +1 -0
  156. package/frontend-dist/assets/github-light-default-BXViO-2h.js +1 -0
  157. package/frontend-dist/assets/github-light-high-contrast-B68TUdTA.js +1 -0
  158. package/frontend-dist/assets/gleam-CSRkHgEL.js +1 -0
  159. package/frontend-dist/assets/glimmer-js-CEhle51e.js +1 -0
  160. package/frontend-dist/assets/glimmer-ts-DyxDXCrd.js +1 -0
  161. package/frontend-dist/assets/glsl-CZ8TD0B7.js +1 -0
  162. package/frontend-dist/assets/gn-ilITqXS6.js +1 -0
  163. package/frontend-dist/assets/gnuplot-7GGW24-e.js +1 -0
  164. package/frontend-dist/assets/go-rLFTqkRN.js +1 -0
  165. package/frontend-dist/assets/graphql-BUs3a1dy.js +1 -0
  166. package/frontend-dist/assets/groovy-CacY0gHj.js +1 -0
  167. package/frontend-dist/assets/gruvbox-dark-hard-C820rvS2.js +1 -0
  168. package/frontend-dist/assets/gruvbox-dark-medium-BPjhmG05.js +1 -0
  169. package/frontend-dist/assets/gruvbox-dark-soft-MrdJrrXF.js +1 -0
  170. package/frontend-dist/assets/gruvbox-light-hard-BC_s9l72.js +1 -0
  171. package/frontend-dist/assets/gruvbox-light-medium-BAWPOn9u.js +1 -0
  172. package/frontend-dist/assets/gruvbox-light-soft-BSMLrYjP.js +1 -0
  173. package/frontend-dist/assets/hack-DKo9X4Fh.js +1 -0
  174. package/frontend-dist/assets/haml-B8slzfN1.js +1 -0
  175. package/frontend-dist/assets/handlebars-YbFocell.js +1 -0
  176. package/frontend-dist/assets/haskell-D8IpX4py.js +1 -0
  177. package/frontend-dist/assets/haxe-OTjmBuCE.js +1 -0
  178. package/frontend-dist/assets/hcl-Dh228itO.js +1 -0
  179. package/frontend-dist/assets/hjson-CxZEssPk.js +1 -0
  180. package/frontend-dist/assets/hlsl-Cvrh5tZx.js +1 -0
  181. package/frontend-dist/assets/horizon-CE9ld1lL.js +1 -0
  182. package/frontend-dist/assets/horizon-bright-DSNQnXHK.js +1 -0
  183. package/frontend-dist/assets/houston-CsvMBhTu.js +1 -0
  184. package/frontend-dist/assets/html-D2GTk50q.js +1 -0
  185. package/frontend-dist/assets/html-derivative-D_7h6AG3.js +1 -0
  186. package/frontend-dist/assets/html-void-elements-DdRmDp70.js +1 -0
  187. package/frontend-dist/assets/http-D3rMdaas.js +1 -0
  188. package/frontend-dist/assets/hurl-Am2W_EDh.js +1 -0
  189. package/frontend-dist/assets/hxml-B0Qn7Nwc.js +1 -0
  190. package/frontend-dist/assets/hy-CZbG8q4J.js +1 -0
  191. package/frontend-dist/assets/imba-DsUTQ-LC.js +1 -0
  192. package/frontend-dist/assets/index-BVlpHV7C.css +1 -0
  193. package/frontend-dist/assets/index-DTdbmAVn.js +147 -0
  194. package/frontend-dist/assets/ini-B5eOa1yu.js +1 -0
  195. package/frontend-dist/assets/java-tbpyqHjD.js +1 -0
  196. package/frontend-dist/assets/javascript-KWFIoO93.js +1 -0
  197. package/frontend-dist/assets/jinja-DDB6tGEL.js +1 -0
  198. package/frontend-dist/assets/jison-BGkRNAgP.js +1 -0
  199. package/frontend-dist/assets/json-xmxrlNIL.js +1 -0
  200. package/frontend-dist/assets/json5-BR5RXkoi.js +1 -0
  201. package/frontend-dist/assets/jsonc-CYpm1nAK.js +1 -0
  202. package/frontend-dist/assets/jsonl-CmCQp5Yx.js +1 -0
  203. package/frontend-dist/assets/jsonnet-CJTPZ8u_.js +1 -0
  204. package/frontend-dist/assets/jssm-DXw9l8Rf.js +1 -0
  205. package/frontend-dist/assets/jsx-COMlcASR.js +1 -0
  206. package/frontend-dist/assets/julia-BhR0UkEv.js +1 -0
  207. package/frontend-dist/assets/just-DXihdLqs.js +1 -0
  208. package/frontend-dist/assets/kanagawa-dragon-CXtmUGW6.js +1 -0
  209. package/frontend-dist/assets/kanagawa-lotus-BN08jTvb.js +1 -0
  210. package/frontend-dist/assets/kanagawa-wave-CTweb8Dz.js +1 -0
  211. package/frontend-dist/assets/kdl-CsD5j6eV.js +1 -0
  212. package/frontend-dist/assets/kotlin-DhhofPvG.js +1 -0
  213. package/frontend-dist/assets/kusto-C7mF5XQf.js +1 -0
  214. package/frontend-dist/assets/laserwave-C_8bwKvT.js +1 -0
  215. package/frontend-dist/assets/latex-Bpu5-lW-.js +1 -0
  216. package/frontend-dist/assets/lean-CewbzKMR.js +1 -0
  217. package/frontend-dist/assets/less-DVTAwKKz.js +1 -0
  218. package/frontend-dist/assets/light-plus-DVQuIRkW.js +1 -0
  219. package/frontend-dist/assets/liquid-BJGa57Gs.js +1 -0
  220. package/frontend-dist/assets/llvm-Cm23YOpf.js +1 -0
  221. package/frontend-dist/assets/log-BNLmms1o.js +1 -0
  222. package/frontend-dist/assets/logo-Cluzi2Zq.js +1 -0
  223. package/frontend-dist/assets/lua-Bd-3NA7e.js +1 -0
  224. package/frontend-dist/assets/luau-FMPmPwt6.js +1 -0
  225. package/frontend-dist/assets/make-Dixweg8N.js +1 -0
  226. package/frontend-dist/assets/markdown-BYOwaDjH.js +1 -0
  227. package/frontend-dist/assets/marko-CZd96liA.js +1 -0
  228. package/frontend-dist/assets/material-theme-Bm3Qr25_.js +1 -0
  229. package/frontend-dist/assets/material-theme-darker-2IIEA8gg.js +1 -0
  230. package/frontend-dist/assets/material-theme-lighter-uhdI0v04.js +1 -0
  231. package/frontend-dist/assets/material-theme-ocean-CHQ94UKr.js +1 -0
  232. package/frontend-dist/assets/material-theme-palenight-B5W6OYN7.js +1 -0
  233. package/frontend-dist/assets/matlab-D7qyCx1q.js +1 -0
  234. package/frontend-dist/assets/mdc-C-cIFcg2.js +1 -0
  235. package/frontend-dist/assets/mdx-DQZ5AkYe.js +1 -0
  236. package/frontend-dist/assets/mermaid-Bk4SNUv9.js +1 -0
  237. package/frontend-dist/assets/min-dark-BSWPekZh.js +1 -0
  238. package/frontend-dist/assets/min-light-DDpmG2fV.js +1 -0
  239. package/frontend-dist/assets/mipsasm-BMqwQI7S.js +1 -0
  240. package/frontend-dist/assets/mojo-BgCJLMeH.js +1 -0
  241. package/frontend-dist/assets/monokai-CdkpiU2Y.js +1 -0
  242. package/frontend-dist/assets/moonbit-CaWjb8XO.js +1 -0
  243. package/frontend-dist/assets/move-B1IS1UjX.js +1 -0
  244. package/frontend-dist/assets/narrat-_X_XdTYD.js +1 -0
  245. package/frontend-dist/assets/nextflow-BJtWHP5T.js +1 -0
  246. package/frontend-dist/assets/nextflow-groovy-DJMQeKeT.js +1 -0
  247. package/frontend-dist/assets/nginx-BkfQ7PRQ.js +1 -0
  248. package/frontend-dist/assets/night-owl-DhmEMT88.js +1 -0
  249. package/frontend-dist/assets/night-owl-light-eJ-hLW7d.js +1 -0
  250. package/frontend-dist/assets/nim-BlcSihrU.js +1 -0
  251. package/frontend-dist/assets/nix-IvuFDN5E.js +1 -0
  252. package/frontend-dist/assets/nord-Cb4Vim4T.js +1 -0
  253. package/frontend-dist/assets/nushell-DcLAeLz5.js +1 -0
  254. package/frontend-dist/assets/objective-c-D1A_Heim.js +1 -0
  255. package/frontend-dist/assets/objective-cpp-BsSzOQcm.js +1 -0
  256. package/frontend-dist/assets/obsidian-Bopioi3a.css +1 -0
  257. package/frontend-dist/assets/obsidian-D8GOht-S.js +36 -0
  258. package/frontend-dist/assets/ocaml-O90oeIOV.js +1 -0
  259. package/frontend-dist/assets/odin-B1RWQWA5.js +1 -0
  260. package/frontend-dist/assets/one-dark-pro-CLwyXe_n.js +1 -0
  261. package/frontend-dist/assets/one-light-D7Lr4KcI.js +1 -0
  262. package/frontend-dist/assets/openscad-BUDT5pXO.js +1 -0
  263. package/frontend-dist/assets/pascal-4ZHwLPI5.js +1 -0
  264. package/frontend-dist/assets/perl-BK1yALmX.js +1 -0
  265. package/frontend-dist/assets/php-IgnnyOWG.js +1 -0
  266. package/frontend-dist/assets/pkl-ot-7Btpt.js +1 -0
  267. package/frontend-dist/assets/plastic-DQwYfKfQ.js +1 -0
  268. package/frontend-dist/assets/plsql-DGHpHOYJ.js +1 -0
  269. package/frontend-dist/assets/po-BiJDBrnU.js +1 -0
  270. package/frontend-dist/assets/poimandres-DRFjx7u4.js +1 -0
  271. package/frontend-dist/assets/polar-C7UOKdEL.js +1 -0
  272. package/frontend-dist/assets/postcss-BXeXVLqQ.js +1 -0
  273. package/frontend-dist/assets/powerquery-DNMTfnFr.js +1 -0
  274. package/frontend-dist/assets/powershell-DshXNtvi.js +1 -0
  275. package/frontend-dist/assets/prisma-BsRQq5mF.js +1 -0
  276. package/frontend-dist/assets/prolog-iXnhIJG7.js +1 -0
  277. package/frontend-dist/assets/proto-DB4EqR-F.js +1 -0
  278. package/frontend-dist/assets/pug-CDPGgwiQ.js +1 -0
  279. package/frontend-dist/assets/puppet-CDv2pdJW.js +1 -0
  280. package/frontend-dist/assets/purescript-9MfHhQsQ.js +1 -0
  281. package/frontend-dist/assets/python-gzcpVVnB.js +1 -0
  282. package/frontend-dist/assets/qml-BJ6xksc_.js +1 -0
  283. package/frontend-dist/assets/qmldir-DCQb3MpD.js +1 -0
  284. package/frontend-dist/assets/qss-Fe1Jh2GI.js +1 -0
  285. package/frontend-dist/assets/r-CtvlrTg3.js +1 -0
  286. package/frontend-dist/assets/racket-DcIDlBhZ.js +1 -0
  287. package/frontend-dist/assets/raku-B3gFvitq.js +1 -0
  288. package/frontend-dist/assets/razor-Dk6lMfGZ.js +1 -0
  289. package/frontend-dist/assets/red-CJ3rzSJv.js +1 -0
  290. package/frontend-dist/assets/reg-CRGYupPL.js +1 -0
  291. package/frontend-dist/assets/regexp-CCpLx9Kn.js +1 -0
  292. package/frontend-dist/assets/rel-BtDbiS_P.js +1 -0
  293. package/frontend-dist/assets/riscv-Ckw8ddFX.js +1 -0
  294. package/frontend-dist/assets/ron-VUp2lXgN.js +1 -0
  295. package/frontend-dist/assets/rose-pine-BthvhNj6.js +1 -0
  296. package/frontend-dist/assets/rose-pine-dawn-Dg85fqjY.js +1 -0
  297. package/frontend-dist/assets/rose-pine-moon-hon4tzzS.js +1 -0
  298. package/frontend-dist/assets/rosmsg-CAekHB0j.js +1 -0
  299. package/frontend-dist/assets/rst-DcQF2cud.js +1 -0
  300. package/frontend-dist/assets/ruby-B_xe_SR9.js +1 -0
  301. package/frontend-dist/assets/rust-Cfkwpbl8.js +1 -0
  302. package/frontend-dist/assets/sas-q7tt0uOK.js +1 -0
  303. package/frontend-dist/assets/sass-DXrisJhu.js +1 -0
  304. package/frontend-dist/assets/scala-DKOlJaKm.js +1 -0
  305. package/frontend-dist/assets/scheme-DQCgrYNe.js +1 -0
  306. package/frontend-dist/assets/scss-tURtktQv.js +1 -0
  307. package/frontend-dist/assets/sdbl-bTVj8UrX.js +1 -0
  308. package/frontend-dist/assets/shaderlab-TOUzSsQk.js +1 -0
  309. package/frontend-dist/assets/shellscript-qT25lAcj.js +1 -0
  310. package/frontend-dist/assets/shellsession-CMm3mW_a.js +1 -0
  311. package/frontend-dist/assets/slack-dark-DnToyrRv.js +1 -0
  312. package/frontend-dist/assets/slack-ochin-B2OO5cIa.js +1 -0
  313. package/frontend-dist/assets/smalltalk-B16xEiuN.js +1 -0
  314. package/frontend-dist/assets/snazzy-light-4G7pJPwS.js +1 -0
  315. package/frontend-dist/assets/solarized-dark-DV17i1UV.js +1 -0
  316. package/frontend-dist/assets/solarized-light-DSh2HLQt.js +1 -0
  317. package/frontend-dist/assets/solidity-CKzVLygQ.js +1 -0
  318. package/frontend-dist/assets/soy-Bu8dHwN_.js +1 -0
  319. package/frontend-dist/assets/sparql-D_iOobhT.js +1 -0
  320. package/frontend-dist/assets/splunk-BC2Px7Mm.js +1 -0
  321. package/frontend-dist/assets/sql-CD4zZmh-.js +1 -0
  322. package/frontend-dist/assets/ssh-config-BgfXC-Er.js +1 -0
  323. package/frontend-dist/assets/stata-BSr4Y6xK.js +1 -0
  324. package/frontend-dist/assets/stylus-B6D30XZt.js +1 -0
  325. package/frontend-dist/assets/surrealql-BdjWJBSn.js +1 -0
  326. package/frontend-dist/assets/svelte-BXGL560Z.js +1 -0
  327. package/frontend-dist/assets/swift-DonLKvLd.js +1 -0
  328. package/frontend-dist/assets/synthwave-84-nFMaYfgc.js +1 -0
  329. package/frontend-dist/assets/system-verilog-DJ5XKQeo.js +1 -0
  330. package/frontend-dist/assets/systemd-BxMlprV5.js +1 -0
  331. package/frontend-dist/assets/talonscript-CohzipZa.js +1 -0
  332. package/frontend-dist/assets/tasl-DMoTqEGO.js +1 -0
  333. package/frontend-dist/assets/tcl-CZd0xW_V.js +1 -0
  334. package/frontend-dist/assets/templ-B39rZ3Mc.js +1 -0
  335. package/frontend-dist/assets/terraform-DswuEJGm.js +1 -0
  336. package/frontend-dist/assets/tex-MbZL_Em8.js +1 -0
  337. package/frontend-dist/assets/tokyo-night-oM2G3aXe.js +1 -0
  338. package/frontend-dist/assets/toml-CcmNWLt0.js +1 -0
  339. package/frontend-dist/assets/ts-tags-BmAVQYXr.js +1 -0
  340. package/frontend-dist/assets/tsv-sltzmVWM.js +1 -0
  341. package/frontend-dist/assets/tsx-ZMGqoGTx.js +1 -0
  342. package/frontend-dist/assets/turtle-ByJddavk.js +1 -0
  343. package/frontend-dist/assets/twig-DreoDQSs.js +1 -0
  344. package/frontend-dist/assets/typescript-BpBfIzrI.js +1 -0
  345. package/frontend-dist/assets/typespec-BRdr0IET.js +1 -0
  346. package/frontend-dist/assets/typst-DI99ib-x.js +1 -0
  347. package/frontend-dist/assets/v-DETTlOr0.js +1 -0
  348. package/frontend-dist/assets/vala-zf12oZj6.js +1 -0
  349. package/frontend-dist/assets/vb-Djn5o6TS.js +1 -0
  350. package/frontend-dist/assets/verilog-CiiDBU1e.js +1 -0
  351. package/frontend-dist/assets/vesper-DdrHHSXu.js +1 -0
  352. package/frontend-dist/assets/vhdl-BroJfC0k.js +1 -0
  353. package/frontend-dist/assets/viml-DvXPmvsu.js +1 -0
  354. package/frontend-dist/assets/vitesse-black-fwtXNY1n.js +1 -0
  355. package/frontend-dist/assets/vitesse-dark-BZCL-v6S.js +1 -0
  356. package/frontend-dist/assets/vitesse-light-VbXTXTou.js +1 -0
  357. package/frontend-dist/assets/vue-DynNEKHy.js +1 -0
  358. package/frontend-dist/assets/vue-html-Cku1G804.js +1 -0
  359. package/frontend-dist/assets/vue-vine-wge2CQTq.js +1 -0
  360. package/frontend-dist/assets/vyper-CgoNMtux.js +1 -0
  361. package/frontend-dist/assets/wasm-BnjxR4X6.js +1 -0
  362. package/frontend-dist/assets/wasm-ByWQv1Qj.js +1 -0
  363. package/frontend-dist/assets/wenyan-C8pVoKbM.js +1 -0
  364. package/frontend-dist/assets/wgsl-BsKzXJz4.js +1 -0
  365. package/frontend-dist/assets/wikitext-ClFFjSW2.js +1 -0
  366. package/frontend-dist/assets/wit-DdvCle-K.js +1 -0
  367. package/frontend-dist/assets/wolfram-DLL8P-h_.js +1 -0
  368. package/frontend-dist/assets/xml-Ff0rnvcF.js +1 -0
  369. package/frontend-dist/assets/xsl-dRWQEowe.js +1 -0
  370. package/frontend-dist/assets/yaml-BxrA6rU3.js +1 -0
  371. package/frontend-dist/assets/zenscript-BnlCZFoB.js +1 -0
  372. package/frontend-dist/assets/zig-CMLA9XwU.js +1 -0
  373. package/frontend-dist/index.html +2 -2
  374. package/frontend-dist/manifest.webmanifest +1 -1
  375. package/frontend-dist/sw.js +1 -2
  376. package/package.json +1 -1
  377. package/frontend-dist/assets/eruda-Chf2H6-5.js +0 -366
  378. package/frontend-dist/assets/index-B7bzacGC.css +0 -32
  379. 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
- const fsMinRaw = rawDisplay?.fontSizeMin;
79
- const fsMaxRaw = rawDisplay?.fontSizeMax;
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 colsRaw = rawDisplay?.maxCols ?? legacyTargetCols;
87
- const maxCols = typeof colsRaw === "number" && Number.isFinite(colsRaw) && colsRaw >= 0 && colsRaw <= 500 ? Math.trunc(colsRaw) : DEFAULT_DISPLAY.maxCols;
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 sep2 = oldHref.includes("?") ? "&" : "?";
443
- const newHref = `${oldHref}${sep2}token=${encodeURIComponent(token)}`;
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
- if (!out.some((e) => e.kind === "loopback")) {
1564
- out.push({ host: "127.0.0.1", port, kind: "loopback" });
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 order = {
1567
- lan: 0,
1568
- tailscale: 1,
1569
- other: 2,
1570
- ipv6: 3,
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 classify(ip, family) {
1593
- if (isLoopbackIp(ip))
1594
- return "loopback";
1595
- if (family === "IPv6")
1596
- return "ipv6";
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
- var init_share_routes = __esm({
1606
- "backend/dist/api/share-routes.js"() {
1607
- "use strict";
1608
- init_network();
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 init_workdir_policy_routes = __esm({
1623
- "backend/dist/api/workdir-policy-routes.js"() {
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 Router9 } from "express";
3425
+ import { Router as Router10 } from "express";
1630
3426
  function createBrokerApiRouter(opts) {
1631
- const router = Router9();
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 = Router9();
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 dirname3, resolve as resolve3 } from "node:path";
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(dirname3(this.path), SESSIONS_LOCK_DIRNAME);
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 = dirname3(this.path);
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 basename2 } from "node:path";
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"] ?? (basename2(claudeCwd) || "instance");
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 basename3 } from "node:path";
5588
+ import { basename as basename5 } from "node:path";
3893
5589
  function isClaudeCommand(command) {
3894
- let name = basename3(command).toLowerCase();
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 asString(v) {
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 = asString(payload["tool_use_id"]);
5759
+ const toolUseId = asString2(payload["tool_use_id"]);
4064
5760
  if (toolUseId)
4065
5761
  return toolUseId;
4066
- const tool = asString(payload["tool_name"]) ?? extractToolFromMessage(payload["message"]) ?? "unknown";
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 = asString(p["hook_event_name"]);
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 = asString(p["tool_name"]) ?? extractToolFromMessage(p["message"]) ?? "unknown_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
- ...asString(p["message"]) ? { detail: asString(p["message"]) } : {}
5782
+ ...asString2(p["message"]) ? { detail: asString2(p["message"]) } : {}
4087
5783
  }
4088
5784
  ];
4089
5785
  }
4090
5786
  case "PermissionRequest": {
4091
- const tool = asString(p["tool_name"]) ?? "unknown_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 = asString(p["tool_name"]) ?? "unknown_tool";
5799
+ const tool = asString2(p["tool_name"]) ?? "unknown_tool";
4104
5800
  return [
4105
5801
  {
4106
5802
  kind: "tool_started",
4107
- toolUseId: asString(p["tool_use_id"]) ?? deriveApprovalId(p),
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 = asString(p["tool_use_id"]) ?? deriveApprovalId(p);
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 = asString(p["tool_use_id"]) ?? deriveApprovalId(p);
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
- ...asString(p["error"]) ? { error: asString(p["error"]) } : {}
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 = asString(p["prompt"]);
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
- ...asString(p["last_assistant_message"]) ? { lastMessage: asString(p["last_assistant_message"]) } : {}
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: asString(p["error"]) ?? "unknown",
4155
- ...asString(p["error_details"]) ? { detail: asString(p["error_details"]) } : {}
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
- ...asString(p["source"]) ? { detail: asString(p["source"]) } : {}
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
- ...asString(p["reason"]) ? { detail: asString(p["reason"]) } : {}
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
- ...asString(p["trigger"]) ? { detail: asString(p["trigger"]) } : {}
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
- ...asString(p["trigger"]) ? { detail: asString(p["trigger"]) } : {}
5887
+ ...asString2(p["trigger"]) ? { detail: asString2(p["trigger"]) } : {}
4192
5888
  }
4193
5889
  ];
4194
5890
  }
4195
5891
  case "CwdChanged": {
4196
- const from = asString(p["old_cwd"]);
4197
- const to = asString(p["new_cwd"]);
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(sep)) {
4977
- const abs = isAbsolute(program) ? program : pathResolve(program);
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 wrap(fn) {
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: wrap(pcForced.bold),
5087
- dim: wrap(pcForced.dim),
5088
- red: wrap(pcForced.red),
5089
- green: wrap(pcForced.green),
5090
- yellow: wrap(pcForced.yellow),
5091
- blue: wrap(pcForced.blue),
5092
- cyan: wrap(pcForced.cyan),
5093
- gray: wrap(pcForced.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 dirname4, resolve as resolve9 } from "node:path";
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 = dirname4(path);
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 dirname5, resolve as resolve11 } from "node:path";
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 = dirname5(lockDir);
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 = createInterface({
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 dirname6 } from "node:path";
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 = dirname6(fileURLToPath2(import.meta.url));
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 = dirname6(fileURLToPath2(import.meta.url));
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 sep2 = `\u2560${"\u2550".repeat(BOX_INNER)}\u2563`;
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(sep2 + "\n");
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(sep2 + "\n");
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 basename4 } from "node:path";
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 isAbsolute2, dirname as dirname7 } from "node:path";
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(dirname7(tsPath));
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 = dirname7(dir);
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 basename5(p) {
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 = isAbsolute2(input.cwd) ? input.cwd : resolve14(input.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() : basename5(cwd);
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 dirname8 } from "node:path";
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, dirname8(path2));
7650
- ensureDir(fs, dirname8(logPath));
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, dirname8(path));
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 dirname9 } from "node:path";
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 = dirname9(fileURLToPath3(import.meta.url));
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 = dirname9(fileURLToPath3(import.meta.url));
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 = dirname9(fileURLToPath3(import.meta.url));
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 stat = statSync6(path);
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 ${(stat.mode & 511).toString(8)})
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: dirname10 } = await import("node:path");
10161
+ const { resolve: resolve17, dirname: dirname12 } = await import("node:path");
8496
10162
  const { fileURLToPath: fileURLToPath4 } = await import("node:url");
8497
- const __dirname2 = dirname10(fileURLToPath4(import.meta.url));
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
  `);