remote-claude-code 0.1.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 (338) hide show
  1. package/README.md +193 -0
  2. package/README.zh-CN.md +193 -0
  3. package/cli/commands/logs.ts +68 -0
  4. package/cli/commands/setup.ts +43 -0
  5. package/cli/commands/start.ts +68 -0
  6. package/cli/commands/status.ts +41 -0
  7. package/cli/commands/stop.ts +14 -0
  8. package/cli/constants.ts +1 -0
  9. package/cli/index.ts +18 -0
  10. package/cli/utils/process-manager.ts +77 -0
  11. package/cli/utils/server.ts +45 -0
  12. package/dist/client/assets/abap-BdImnpbu.js +1 -0
  13. package/dist/client/assets/actionscript-3-CoDkCxhg.js +1 -0
  14. package/dist/client/assets/ada-bCR0ucgS.js +1 -0
  15. package/dist/client/assets/andromeeda-C4gqWexZ.js +1 -0
  16. package/dist/client/assets/angular-html-CU67Zn6k.js +1 -0
  17. package/dist/client/assets/angular-ts-BwZT4LLn.js +1 -0
  18. package/dist/client/assets/apache-Pmp26Uib.js +1 -0
  19. package/dist/client/assets/apex-D8_7TLub.js +1 -0
  20. package/dist/client/assets/apl-dKokRX4l.js +1 -0
  21. package/dist/client/assets/applescript-Co6uUVPk.js +1 -0
  22. package/dist/client/assets/ara-BRHolxvo.js +1 -0
  23. package/dist/client/assets/asciidoc-Ve4PFQV2.js +1 -0
  24. package/dist/client/assets/asm-D_Q5rh1f.js +1 -0
  25. package/dist/client/assets/astro-CbQHKStN.js +1 -0
  26. package/dist/client/assets/aurora-x-D-2ljcwZ.js +1 -0
  27. package/dist/client/assets/awk-DMzUqQB5.js +1 -0
  28. package/dist/client/assets/ayu-dark-DYE7WIF3.js +1 -0
  29. package/dist/client/assets/ayu-light-BA47KaF1.js +1 -0
  30. package/dist/client/assets/ayu-mirage-32ctXXKs.js +1 -0
  31. package/dist/client/assets/ballerina-BFfxhgS-.js +1 -0
  32. package/dist/client/assets/bat-BkioyH1T.js +1 -0
  33. package/dist/client/assets/beancount-k_qm7-4y.js +1 -0
  34. package/dist/client/assets/berry-uYugtg8r.js +1 -0
  35. package/dist/client/assets/bibtex-CHM0blh-.js +1 -0
  36. package/dist/client/assets/bicep-Bmn6On1c.js +1 -0
  37. package/dist/client/assets/bird2-DPOp833l.js +1 -0
  38. package/dist/client/assets/blade-D4QpJJKB.js +1 -0
  39. package/dist/client/assets/bsl-BO_Y6i37.js +1 -0
  40. package/dist/client/assets/c-BIGW1oBm.js +1 -0
  41. package/dist/client/assets/c3-eo99z4R2.js +1 -0
  42. package/dist/client/assets/cadence-Bv_4Rxtq.js +1 -0
  43. package/dist/client/assets/cairo-KRGpt6FW.js +1 -0
  44. package/dist/client/assets/catppuccin-frappe-DFWUc33u.js +1 -0
  45. package/dist/client/assets/catppuccin-latte-C9dUb6Cb.js +1 -0
  46. package/dist/client/assets/catppuccin-macchiato-DQyhUUbL.js +1 -0
  47. package/dist/client/assets/catppuccin-mocha-D87Tk5Gz.js +1 -0
  48. package/dist/client/assets/clarity-D53aC0YG.js +1 -0
  49. package/dist/client/assets/clojure-P80f7IUj.js +1 -0
  50. package/dist/client/assets/cmake-D1j8_8rp.js +1 -0
  51. package/dist/client/assets/cobol-nwyudZeR.js +1 -0
  52. package/dist/client/assets/code-highlighter-D_0rJlW2.js +153 -0
  53. package/dist/client/assets/codeowners-Bp6g37R7.js +1 -0
  54. package/dist/client/assets/codeql-DsOJ9woJ.js +1 -0
  55. package/dist/client/assets/coffee-Ch7k5sss.js +1 -0
  56. package/dist/client/assets/common-lisp-Cg-RD9OK.js +1 -0
  57. package/dist/client/assets/coq-DkFqJrB1.js +1 -0
  58. package/dist/client/assets/cpp-CofmeUqb.js +1 -0
  59. package/dist/client/assets/crystal-tKQVLTB8.js +1 -0
  60. package/dist/client/assets/csharp-COcwbKMJ.js +1 -0
  61. package/dist/client/assets/css-DPfMkruS.js +1 -0
  62. package/dist/client/assets/csv-fuZLfV_i.js +1 -0
  63. package/dist/client/assets/cue-D82EKSYY.js +1 -0
  64. package/dist/client/assets/cypher-COkxafJQ.js +1 -0
  65. package/dist/client/assets/d-85-TOEBH.js +1 -0
  66. package/dist/client/assets/dark-plus-C3mMm8J8.js +1 -0
  67. package/dist/client/assets/dart-CF10PKvl.js +1 -0
  68. package/dist/client/assets/dax-CEL-wOlO.js +1 -0
  69. package/dist/client/assets/desktop-BmXAJ9_W.js +1 -0
  70. package/dist/client/assets/diff-D97Zzqfu.js +1 -0
  71. package/dist/client/assets/docker-BcOcwvcX.js +1 -0
  72. package/dist/client/assets/dotenv-Da5cRb03.js +1 -0
  73. package/dist/client/assets/dracula-BzJJZx-M.js +1 -0
  74. package/dist/client/assets/dracula-soft-BXkSAIEj.js +1 -0
  75. package/dist/client/assets/dream-maker-BtqSS_iP.js +1 -0
  76. package/dist/client/assets/edge-BkV0erSs.js +1 -0
  77. package/dist/client/assets/elixir-CDX3lj18.js +1 -0
  78. package/dist/client/assets/elm-DbKCFpqz.js +1 -0
  79. package/dist/client/assets/emacs-lisp-C9XAeP06.js +1 -0
  80. package/dist/client/assets/erb-B12qg9BL.js +1 -0
  81. package/dist/client/assets/erlang-DsQrWhSR.js +1 -0
  82. package/dist/client/assets/everforest-dark-BgDCqdQA.js +1 -0
  83. package/dist/client/assets/everforest-light-C8M2exoo.js +1 -0
  84. package/dist/client/assets/fennel-BYunw83y.js +1 -0
  85. package/dist/client/assets/fish-BvzEVeQv.js +1 -0
  86. package/dist/client/assets/fluent-C4IJs8-o.js +1 -0
  87. package/dist/client/assets/fortran-fixed-form-CkoXwp7k.js +1 -0
  88. package/dist/client/assets/fortran-free-form-BxgE0vQu.js +1 -0
  89. package/dist/client/assets/fsharp-CXgrBDvD.js +1 -0
  90. package/dist/client/assets/gdresource-BOOCDP_w.js +1 -0
  91. package/dist/client/assets/gdscript-C5YyOfLZ.js +1 -0
  92. package/dist/client/assets/gdshader-DkwncUOv.js +1 -0
  93. package/dist/client/assets/genie-D0YGMca9.js +1 -0
  94. package/dist/client/assets/gherkin-DyxjwDmM.js +1 -0
  95. package/dist/client/assets/git-commit-F4YmCXRG.js +1 -0
  96. package/dist/client/assets/git-rebase-r7XF79zn.js +1 -0
  97. package/dist/client/assets/github-dark-DHJKELXO.js +1 -0
  98. package/dist/client/assets/github-dark-default-Cuk6v7N8.js +1 -0
  99. package/dist/client/assets/github-dark-dimmed-DH5Ifo-i.js +1 -0
  100. package/dist/client/assets/github-dark-high-contrast-E3gJ1_iC.js +1 -0
  101. package/dist/client/assets/github-light-DAi9KRSo.js +1 -0
  102. package/dist/client/assets/github-light-default-D7oLnXFd.js +1 -0
  103. package/dist/client/assets/github-light-high-contrast-BfjtVDDH.js +1 -0
  104. package/dist/client/assets/gleam-BspZqrRM.js +1 -0
  105. package/dist/client/assets/glimmer-js-Rg0-pVw9.js +1 -0
  106. package/dist/client/assets/glimmer-ts-U6CK756n.js +1 -0
  107. package/dist/client/assets/glsl-DplSGwfg.js +1 -0
  108. package/dist/client/assets/gn-n2N0HUVH.js +1 -0
  109. package/dist/client/assets/gnuplot-DdkO51Og.js +1 -0
  110. package/dist/client/assets/go-CxLEBnE3.js +1 -0
  111. package/dist/client/assets/graphql-ChdNCCLP.js +1 -0
  112. package/dist/client/assets/groovy-gcz8RCvz.js +1 -0
  113. package/dist/client/assets/gruvbox-dark-hard-CFHQjOhq.js +1 -0
  114. package/dist/client/assets/gruvbox-dark-medium-GsRaNv29.js +1 -0
  115. package/dist/client/assets/gruvbox-dark-soft-CVdnzihN.js +1 -0
  116. package/dist/client/assets/gruvbox-light-hard-CH1njM8p.js +1 -0
  117. package/dist/client/assets/gruvbox-light-medium-DRw_LuNl.js +1 -0
  118. package/dist/client/assets/gruvbox-light-soft-hJgmCMqR.js +1 -0
  119. package/dist/client/assets/hack-CaT9iCJl.js +1 -0
  120. package/dist/client/assets/haml-B8DHNrY2.js +1 -0
  121. package/dist/client/assets/handlebars-BL8al0AC.js +1 -0
  122. package/dist/client/assets/haskell-Df6bDoY_.js +1 -0
  123. package/dist/client/assets/haxe-CzTSHFRz.js +1 -0
  124. package/dist/client/assets/hcl-BWvSN4gD.js +1 -0
  125. package/dist/client/assets/highlighted-body-TPN3WLV5-Bbf4VBqH.js +1 -0
  126. package/dist/client/assets/hjson-D5-asLiD.js +1 -0
  127. package/dist/client/assets/hlsl-D3lLCCz7.js +1 -0
  128. package/dist/client/assets/horizon-BUw7H-hv.js +1 -0
  129. package/dist/client/assets/horizon-bright-Cn-bp-IR.js +1 -0
  130. package/dist/client/assets/houston-DnULxvSX.js +1 -0
  131. package/dist/client/assets/html-GMplVEZG.js +1 -0
  132. package/dist/client/assets/html-derivative-BFtXZ54Q.js +1 -0
  133. package/dist/client/assets/http-jrhK8wxY.js +1 -0
  134. package/dist/client/assets/hurl-irOxFIW8.js +1 -0
  135. package/dist/client/assets/hxml-Bvhsp5Yf.js +1 -0
  136. package/dist/client/assets/hy-DFXneXwc.js +1 -0
  137. package/dist/client/assets/imba-DGztddWO.js +1 -0
  138. package/dist/client/assets/index-D7x3ypuO.css +32 -0
  139. package/dist/client/assets/index-DLfciZbe.js +477 -0
  140. package/dist/client/assets/ini-BEwlwnbL.js +1 -0
  141. package/dist/client/assets/java-CylS5w8V.js +1 -0
  142. package/dist/client/assets/javascript-wDzz0qaB.js +1 -0
  143. package/dist/client/assets/jinja-4LBKfQ-Z.js +1 -0
  144. package/dist/client/assets/jison-wvAkD_A8.js +1 -0
  145. package/dist/client/assets/json-Cp-IABpG.js +1 -0
  146. package/dist/client/assets/json5-C9tS-k6U.js +1 -0
  147. package/dist/client/assets/jsonc-Des-eS-w.js +1 -0
  148. package/dist/client/assets/jsonl-DcaNXYhu.js +1 -0
  149. package/dist/client/assets/jsonnet-DFQXde-d.js +1 -0
  150. package/dist/client/assets/jssm-C2t-YnRu.js +1 -0
  151. package/dist/client/assets/jsx-g9-lgVsj.js +1 -0
  152. package/dist/client/assets/julia-CxzCAyBv.js +1 -0
  153. package/dist/client/assets/just-Cw27pwNe.js +1 -0
  154. package/dist/client/assets/kanagawa-dragon-CkXjmgJE.js +1 -0
  155. package/dist/client/assets/kanagawa-lotus-CfQXZHmo.js +1 -0
  156. package/dist/client/assets/kanagawa-wave-DWedfzmr.js +1 -0
  157. package/dist/client/assets/kdl-DV7GczEv.js +1 -0
  158. package/dist/client/assets/kotlin-BdnUsdx6.js +1 -0
  159. package/dist/client/assets/kusto-DZf3V79B.js +1 -0
  160. package/dist/client/assets/laserwave-DUszq2jm.js +1 -0
  161. package/dist/client/assets/latex-CWtU0Tv5.js +1 -0
  162. package/dist/client/assets/lean-BZvkOJ9d.js +1 -0
  163. package/dist/client/assets/less-B1dDrJ26.js +1 -0
  164. package/dist/client/assets/light-plus-B7mTdjB0.js +1 -0
  165. package/dist/client/assets/liquid-DYVedYrR.js +1 -0
  166. package/dist/client/assets/llvm-DjAJT7YJ.js +1 -0
  167. package/dist/client/assets/log-2UxHyX5q.js +1 -0
  168. package/dist/client/assets/logo-BtOb2qkB.js +1 -0
  169. package/dist/client/assets/lua-BaeVxFsk.js +1 -0
  170. package/dist/client/assets/luau-C-HG3fhB.js +1 -0
  171. package/dist/client/assets/make-CHLpvVh8.js +1 -0
  172. package/dist/client/assets/markdown-Cvjx9yec.js +1 -0
  173. package/dist/client/assets/markdown-ksT5_TvX.js +146 -0
  174. package/dist/client/assets/marko-CnJfTvn9.js +1 -0
  175. package/dist/client/assets/material-theme-D5KoaKCx.js +1 -0
  176. package/dist/client/assets/material-theme-darker-BfHTSMKl.js +1 -0
  177. package/dist/client/assets/material-theme-lighter-B0m2ddpp.js +1 -0
  178. package/dist/client/assets/material-theme-ocean-CyktbL80.js +1 -0
  179. package/dist/client/assets/material-theme-palenight-Csfq5Kiy.js +1 -0
  180. package/dist/client/assets/matlab-D7o27uSR.js +1 -0
  181. package/dist/client/assets/mdc-BMNejdWA.js +1 -0
  182. package/dist/client/assets/mdx-Cmh6b_Ma.js +1 -0
  183. package/dist/client/assets/mermaid-mWjccvbQ.js +1 -0
  184. package/dist/client/assets/min-dark-CafNBF8u.js +1 -0
  185. package/dist/client/assets/min-light-CTRr51gU.js +1 -0
  186. package/dist/client/assets/mipsasm-CKIfxQSi.js +1 -0
  187. package/dist/client/assets/mojo-rZm6bMo-.js +1 -0
  188. package/dist/client/assets/monokai-D4h5O-jR.js +1 -0
  189. package/dist/client/assets/moonbit-_H4v1dQx.js +1 -0
  190. package/dist/client/assets/move-IF9eRakj.js +1 -0
  191. package/dist/client/assets/narrat-DRg8JJMk.js +1 -0
  192. package/dist/client/assets/nextflow-Zz6hmt5N.js +1 -0
  193. package/dist/client/assets/nextflow-groovy-BeH2EWoN.js +1 -0
  194. package/dist/client/assets/nginx-BpAMiNFr.js +1 -0
  195. package/dist/client/assets/night-owl-C39BiMTA.js +1 -0
  196. package/dist/client/assets/night-owl-light-CMTm3GFP.js +1 -0
  197. package/dist/client/assets/nim-CVrawwO9.js +1 -0
  198. package/dist/client/assets/nix-CwoSXNpI.js +1 -0
  199. package/dist/client/assets/nord-Ddv68eIx.js +1 -0
  200. package/dist/client/assets/nushell-Cz2AlsmD.js +1 -0
  201. package/dist/client/assets/objective-c-DXmwc3jG.js +1 -0
  202. package/dist/client/assets/objective-cpp-CLxacb5B.js +1 -0
  203. package/dist/client/assets/ocaml-C0hk2d4L.js +1 -0
  204. package/dist/client/assets/odin-BBf5iR-q.js +1 -0
  205. package/dist/client/assets/one-dark-pro-DVMEJ2y_.js +1 -0
  206. package/dist/client/assets/one-light-C3Wv6jpd.js +1 -0
  207. package/dist/client/assets/openscad-C4EeE6gA.js +1 -0
  208. package/dist/client/assets/pascal-D93ZcfNL.js +1 -0
  209. package/dist/client/assets/perl-C0TMdlhV.js +1 -0
  210. package/dist/client/assets/php-Dhbhpdrm.js +1 -0
  211. package/dist/client/assets/pkl-u5AG7uiY.js +1 -0
  212. package/dist/client/assets/plastic-3e1v2bzS.js +1 -0
  213. package/dist/client/assets/plsql-ChMvpjG-.js +1 -0
  214. package/dist/client/assets/po-BTJTHyun.js +1 -0
  215. package/dist/client/assets/poimandres-CS3Unz2-.js +1 -0
  216. package/dist/client/assets/polar-C0HS_06l.js +1 -0
  217. package/dist/client/assets/postcss-CXtECtnM.js +1 -0
  218. package/dist/client/assets/powerquery-CEu0bR-o.js +1 -0
  219. package/dist/client/assets/powershell-Dpen1YoG.js +1 -0
  220. package/dist/client/assets/prisma-Dd19v3D-.js +1 -0
  221. package/dist/client/assets/prolog-CbFg5uaA.js +1 -0
  222. package/dist/client/assets/proto-C7zT0LnQ.js +1 -0
  223. package/dist/client/assets/pug-CGlum2m_.js +1 -0
  224. package/dist/client/assets/puppet-BMWR74SV.js +1 -0
  225. package/dist/client/assets/purescript-CklMAg4u.js +1 -0
  226. package/dist/client/assets/python-B6aJPvgy.js +1 -0
  227. package/dist/client/assets/qml-3beO22l8.js +1 -0
  228. package/dist/client/assets/qmldir-C8lEn-DE.js +1 -0
  229. package/dist/client/assets/qss-IeuSbFQv.js +1 -0
  230. package/dist/client/assets/r-Dspwwk_N.js +1 -0
  231. package/dist/client/assets/racket-BqYA7rlc.js +1 -0
  232. package/dist/client/assets/raku-DXvB9xmW.js +1 -0
  233. package/dist/client/assets/razor-Uh8Bk_45.js +1 -0
  234. package/dist/client/assets/red-bN70gL4F.js +1 -0
  235. package/dist/client/assets/reg-C-SQnVFl.js +1 -0
  236. package/dist/client/assets/regexp-CDVJQ6XC.js +1 -0
  237. package/dist/client/assets/rel-C3B-1QV4.js +1 -0
  238. package/dist/client/assets/riscv-BM1_JUlF.js +1 -0
  239. package/dist/client/assets/ron-D8l8udqQ.js +1 -0
  240. package/dist/client/assets/rose-pine-dawn-DHQR4-dF.js +1 -0
  241. package/dist/client/assets/rose-pine-moon-D4_iv3hh.js +1 -0
  242. package/dist/client/assets/rose-pine-qdsjHGoJ.js +1 -0
  243. package/dist/client/assets/rosmsg-BJDFO7_C.js +1 -0
  244. package/dist/client/assets/rst-BrH8l1NY.js +1 -0
  245. package/dist/client/assets/ruby-Dw2BHqvy.js +1 -0
  246. package/dist/client/assets/rust-B1yitclQ.js +1 -0
  247. package/dist/client/assets/sas-cz2c8ADy.js +1 -0
  248. package/dist/client/assets/sass-Cj5Yp3dK.js +1 -0
  249. package/dist/client/assets/scala-C151Ov-r.js +1 -0
  250. package/dist/client/assets/scheme-C98Dy4si.js +1 -0
  251. package/dist/client/assets/scss-OYdSNvt2.js +1 -0
  252. package/dist/client/assets/sdbl-DVxCFoDh.js +1 -0
  253. package/dist/client/assets/shaderlab-Dg9Lc6iA.js +1 -0
  254. package/dist/client/assets/shellscript-Yzrsuije.js +1 -0
  255. package/dist/client/assets/shellsession-BADoaaVG.js +1 -0
  256. package/dist/client/assets/slack-dark-BthQWCQV.js +1 -0
  257. package/dist/client/assets/slack-ochin-DqwNpetd.js +1 -0
  258. package/dist/client/assets/smalltalk-BERRCDM3.js +1 -0
  259. package/dist/client/assets/snazzy-light-Bw305WKR.js +1 -0
  260. package/dist/client/assets/solarized-dark-DXbdFlpD.js +1 -0
  261. package/dist/client/assets/solarized-light-L9t79GZl.js +1 -0
  262. package/dist/client/assets/solidity-rGO070M0.js +1 -0
  263. package/dist/client/assets/soy-Brmx7dQM.js +1 -0
  264. package/dist/client/assets/sparql-rVzFXLq3.js +1 -0
  265. package/dist/client/assets/splunk-BtCnVYZw.js +1 -0
  266. package/dist/client/assets/sql-BLtJtn59.js +1 -0
  267. package/dist/client/assets/ssh-config-_ykCGR6B.js +1 -0
  268. package/dist/client/assets/stata-BH5u7GGu.js +1 -0
  269. package/dist/client/assets/stylus-BEDo0Tqx.js +1 -0
  270. package/dist/client/assets/surrealql-Bq5Q-fJD.js +1 -0
  271. package/dist/client/assets/svelte-C_ipcX3V.js +1 -0
  272. package/dist/client/assets/swift-D82vCrfD.js +1 -0
  273. package/dist/client/assets/synthwave-84-CbfX1IO0.js +1 -0
  274. package/dist/client/assets/system-verilog-CnnmHF94.js +1 -0
  275. package/dist/client/assets/systemd-4A_iFExJ.js +1 -0
  276. package/dist/client/assets/talonscript-CkByrt1z.js +1 -0
  277. package/dist/client/assets/tasl-QIJgUcNo.js +1 -0
  278. package/dist/client/assets/tcl-dwOrl1Do.js +1 -0
  279. package/dist/client/assets/templ-P3uqSqPl.js +1 -0
  280. package/dist/client/assets/terraform-BETggiCN.js +1 -0
  281. package/dist/client/assets/tex-idrVyKtj.js +1 -0
  282. package/dist/client/assets/tokyo-night-hegEt444.js +1 -0
  283. package/dist/client/assets/toml-vGWfd6FD.js +1 -0
  284. package/dist/client/assets/ts-tags-zn1MmPIZ.js +1 -0
  285. package/dist/client/assets/tsv-B_m7g4N7.js +1 -0
  286. package/dist/client/assets/tsx-COt5Ahok.js +1 -0
  287. package/dist/client/assets/turtle-BsS91CYL.js +1 -0
  288. package/dist/client/assets/twig-DNn4PbVi.js +1 -0
  289. package/dist/client/assets/typescript-BPQ3VLAy.js +1 -0
  290. package/dist/client/assets/typespec-BGHnOYBU.js +1 -0
  291. package/dist/client/assets/typst-DHCkPAjA.js +1 -0
  292. package/dist/client/assets/v-BcVCzyr7.js +1 -0
  293. package/dist/client/assets/vala-CsfeWuGM.js +1 -0
  294. package/dist/client/assets/vb-D17OF-Vu.js +1 -0
  295. package/dist/client/assets/verilog-BQ8w6xss.js +1 -0
  296. package/dist/client/assets/vesper-DU1UobuO.js +1 -0
  297. package/dist/client/assets/vhdl-CeAyd5Ju.js +1 -0
  298. package/dist/client/assets/viml-CJc9bBzg.js +1 -0
  299. package/dist/client/assets/vitesse-black-Bkuqu6BP.js +1 -0
  300. package/dist/client/assets/vitesse-dark-D0r3Knsf.js +1 -0
  301. package/dist/client/assets/vitesse-light-CVO1_9PV.js +1 -0
  302. package/dist/client/assets/vue-DN_0RTcg.js +1 -0
  303. package/dist/client/assets/vue-html-AaS7Mt5G.js +1 -0
  304. package/dist/client/assets/vue-vine-CQOfvN7w.js +1 -0
  305. package/dist/client/assets/vyper-CDx5xZoG.js +1 -0
  306. package/dist/client/assets/wasm-CG6Dc4jp.js +1 -0
  307. package/dist/client/assets/wasm-MzD3tlZU.js +1 -0
  308. package/dist/client/assets/wenyan-BV7otONQ.js +1 -0
  309. package/dist/client/assets/wgsl-Dx-B1_4e.js +1 -0
  310. package/dist/client/assets/wikitext-BhOHFoWU.js +1 -0
  311. package/dist/client/assets/wit-5i3qLPDT.js +1 -0
  312. package/dist/client/assets/wolfram-lXgVvXCa.js +1 -0
  313. package/dist/client/assets/xml-sdJ4AIDG.js +1 -0
  314. package/dist/client/assets/xsl-CtQFsRM5.js +1 -0
  315. package/dist/client/assets/yaml-Buea-lGh.js +1 -0
  316. package/dist/client/assets/zenscript-DVFEvuxE.js +1 -0
  317. package/dist/client/assets/zig-VOosw3JB.js +1 -0
  318. package/dist/client/images/logo.png +0 -0
  319. package/dist/client/index.html +23 -0
  320. package/dist/client/manifest.webmanifest +1 -0
  321. package/dist/client/registerSW.js +1 -0
  322. package/dist/client/sw.js +1 -0
  323. package/dist/client/workbox-1ef09536.js +1 -0
  324. package/package.json +61 -0
  325. package/public/images/logo.png +0 -0
  326. package/server/auth-cookie.ts +25 -0
  327. package/server/auth.ts +92 -0
  328. package/server/claude.ts +759 -0
  329. package/server/db.ts +134 -0
  330. package/server/index.ts +177 -0
  331. package/server/preview.ts +93 -0
  332. package/server/projects.ts +401 -0
  333. package/server/tailscale.ts +87 -0
  334. package/server/terminal.ts +411 -0
  335. package/server/threads.ts +39 -0
  336. package/server/tunnel.ts +121 -0
  337. package/server/upload.ts +109 -0
  338. package/server/ws.ts +785 -0
@@ -0,0 +1,759 @@
1
+ import { query, type Query } from "@anthropic-ai/claude-agent-sdk";
2
+ import { existsSync } from "node:fs";
3
+ import { readdir, readFile } from "node:fs/promises";
4
+ import { homedir } from "node:os";
5
+ import { extname, join, relative, resolve } from "node:path";
6
+ import { spawn } from "node:child_process";
7
+
8
+ const SESSION_CLEANUP_DELAY_MS = 60_000; // 完成后 60 秒清除
9
+ const SESSION_CLEANUP_INTERVAL_MS = 60_000; // 每 60 秒扫描一次
10
+ const PROBE_CACHE_TTL_MS = 300_000; // probe 缓存 5 分钟
11
+ const SLASH_COMMAND_CACHE_TTL_MS = 30_000; // slash command 缓存 30 秒
12
+ const STDERR_BUFFER_LIMIT = 40;
13
+ const SDK_SETTING_SOURCES = ["user", "project", "local"] as const;
14
+
15
+ interface SlashCommandDefinition {
16
+ name: string;
17
+ description?: string;
18
+ argumentHint?: string;
19
+ filePath?: string;
20
+ body?: string;
21
+ }
22
+
23
+ type LocalSlashCommand = SlashCommandDefinition & {
24
+ body: string;
25
+ filePath: string;
26
+ };
27
+
28
+ function stripAnsi(text: string) {
29
+ return text.replace(/\x1B\[[0-9;]*m/g, "");
30
+ }
31
+
32
+ function summarizeStderr(stderrLines: string[]) {
33
+ const cleaned = stderrLines
34
+ .map((line) => stripAnsi(line).trim())
35
+ .filter(Boolean)
36
+ .filter((line) => !line.startsWith("DEBUG "));
37
+
38
+ if (cleaned.length === 0) return null;
39
+
40
+ // 优先取最后一条 error/fatal/unknown option,其次取最后一条非空日志
41
+ const preferred = [...cleaned]
42
+ .reverse()
43
+ .find((line) => /error|fatal|unknown option|invalid/i.test(line));
44
+
45
+ return preferred || cleaned[cleaned.length - 1];
46
+ }
47
+
48
+ function getExitCodeAdvice(code: number): string {
49
+ switch (code) {
50
+ case 1:
51
+ return "可能原因:CLI 未正确安装、未登录或配置异常。请执行 `claude doctor` 检查,或运行 `claude --version` 确认 CLI 可用。";
52
+ case 2:
53
+ return "命令行参数错误。请检查 Claude Code CLI 版本是否与 SDK 兼容。";
54
+ case 126:
55
+ return "权限不足,无法执行 claude 命令。请检查文件权限。";
56
+ case 127:
57
+ return "未找到 claude 命令。请确认已安装 Claude Code CLI 并在 PATH 中。";
58
+ default:
59
+ return "请执行 `claude doctor` 检查 Claude Code 安装、登录和配置。";
60
+ }
61
+ }
62
+
63
+ function getStderrAdvice(stderr: string): string | null {
64
+ if (/unauthorized|auth|login|token/i.test(stderr)) {
65
+ return "请检查是否已登录 Claude Code:运行 `claude login`";
66
+ }
67
+ if (/ECONNREFUSED|network|timeout|fetch failed/i.test(stderr)) {
68
+ return "网络连接异常,请检查网络连接和代理设置";
69
+ }
70
+ if (/unknown option|invalid.*flag/i.test(stderr)) {
71
+ return "CLI 参数不兼容,请更新 Claude Code CLI 到最新版本:`claude update`";
72
+ }
73
+ return null;
74
+ }
75
+
76
+ function enrichProcessExitError(err: unknown, stderrLines: string[]) {
77
+ const base = err instanceof Error ? err : new Error(String(err));
78
+ const match = base.message.match(/Claude Code process exited with code (\d+)/i);
79
+ if (!match) {
80
+ return base;
81
+ }
82
+
83
+ const exitCode = parseInt(match[1], 10);
84
+ const stderrSummary = summarizeStderr(stderrLines);
85
+
86
+ if (stderrSummary) {
87
+ const advice = getStderrAdvice(stderrSummary);
88
+ const suffix = advice ? `\n建议:${advice}` : "";
89
+ return new Error(`${base.message}: ${stderrSummary}${suffix}`);
90
+ }
91
+
92
+ return new Error(`${base.message}。${getExitCodeAdvice(exitCode)}`);
93
+ }
94
+
95
+ function stripQuotes(value: string) {
96
+ return value.replace(/^["']|["']$/g, "");
97
+ }
98
+
99
+ function parseFrontmatter(content: string) {
100
+ const normalized = content.replace(/\r\n/g, "\n");
101
+ if (!normalized.startsWith("---\n")) {
102
+ return { body: normalized.trim(), metadata: {} as Record<string, string> };
103
+ }
104
+
105
+ const end = normalized.indexOf("\n---\n", 4);
106
+ if (end === -1) {
107
+ return { body: normalized.trim(), metadata: {} as Record<string, string> };
108
+ }
109
+
110
+ const raw = normalized.slice(4, end);
111
+ const metadata: Record<string, string> = {};
112
+
113
+ for (const line of raw.split("\n")) {
114
+ const match = line.match(/^([A-Za-z0-9_-]+):\s*(.*)$/);
115
+ if (!match) continue;
116
+ metadata[match[1].toLowerCase()] = stripQuotes(match[2].trim());
117
+ }
118
+
119
+ return {
120
+ body: normalized.slice(end + 5).trim(),
121
+ metadata,
122
+ };
123
+ }
124
+
125
+ function buildCommandName(relativePath: string) {
126
+ return relativePath
127
+ .replace(/\\/g, "/")
128
+ .replace(/\/SKILL\.md$/i, "")
129
+ .replace(/\.md$/i, "")
130
+ .split("/")
131
+ .filter(Boolean)
132
+ .join(":");
133
+ }
134
+
135
+ async function collectFiles(dir: string, matcher: (filePath: string) => boolean): Promise<string[]> {
136
+ if (!existsSync(dir)) return [];
137
+
138
+ const entries = await readdir(dir, { withFileTypes: true });
139
+ const files: string[] = [];
140
+
141
+ for (const entry of entries) {
142
+ const filePath = join(dir, entry.name);
143
+ if (entry.isDirectory()) {
144
+ files.push(...await collectFiles(filePath, matcher));
145
+ continue;
146
+ }
147
+ if (entry.isFile() && matcher(filePath)) {
148
+ files.push(filePath);
149
+ }
150
+ }
151
+
152
+ return files;
153
+ }
154
+
155
+ async function loadMarkdownCommand(filePath: string, relativePath: string) {
156
+ const content = await readFile(filePath, "utf8");
157
+ const { body, metadata } = parseFrontmatter(content);
158
+ const name = metadata.name?.trim() || buildCommandName(relativePath);
159
+ if (!name) return null;
160
+
161
+ return {
162
+ name,
163
+ description: metadata.description?.trim(),
164
+ argumentHint: metadata["argument-hint"]?.trim(),
165
+ filePath,
166
+ body,
167
+ } satisfies LocalSlashCommand;
168
+ }
169
+
170
+ async function discoverFallbackSlashCommands(cwd: string) {
171
+ const normalizedCwd = resolve(cwd);
172
+ const candidateDirs = [
173
+ join(normalizedCwd, ".claude"),
174
+ join(homedir(), ".claude"),
175
+ ].filter((dir, index, dirs) => existsSync(dir) && dirs.indexOf(dir) === index);
176
+
177
+ const merged = new Map<string, LocalSlashCommand>();
178
+
179
+ for (const claudeDir of candidateDirs) {
180
+ const commandsRoot = join(claudeDir, "commands");
181
+ const skillsRoot = join(claudeDir, "skills");
182
+ const [commandFiles, skillFiles] = await Promise.all([
183
+ collectFiles(commandsRoot, (filePath) => extname(filePath).toLowerCase() === ".md"),
184
+ collectFiles(skillsRoot, (filePath) => filePath.endsWith("/SKILL.md") || filePath.endsWith("\\SKILL.md")),
185
+ ]);
186
+
187
+ const loaded = await Promise.all([
188
+ ...commandFiles.map((filePath) => loadMarkdownCommand(filePath, relative(commandsRoot, filePath))),
189
+ ...skillFiles.map((filePath) => loadMarkdownCommand(filePath, relative(skillsRoot, filePath))),
190
+ ]);
191
+
192
+ for (const command of loaded) {
193
+ if (command && !merged.has(command.name)) {
194
+ merged.set(command.name, command);
195
+ }
196
+ }
197
+ }
198
+
199
+ return [...merged.values()];
200
+ }
201
+
202
+ function parseSlashInvocation(prompt: string) {
203
+ const trimmed = prompt.trim();
204
+ if (!trimmed.startsWith("/")) return null;
205
+
206
+ const withoutSlash = trimmed.slice(1);
207
+ const firstWhitespace = withoutSlash.search(/\s/);
208
+ if (firstWhitespace === -1) {
209
+ return { args: "", name: withoutSlash };
210
+ }
211
+
212
+ return {
213
+ name: withoutSlash.slice(0, firstWhitespace),
214
+ args: withoutSlash.slice(firstWhitespace).trim(),
215
+ };
216
+ }
217
+
218
+ function extractCommandNames(commands: unknown[]) {
219
+ return new Set(
220
+ commands
221
+ .map((command) => {
222
+ if (typeof command === "string") return command;
223
+ if (command && typeof command === "object") {
224
+ const record = command as Record<string, unknown>;
225
+ return String(record.name ?? record.command ?? "");
226
+ }
227
+ return "";
228
+ })
229
+ .filter(Boolean)
230
+ );
231
+ }
232
+
233
+ function applyCommandArguments(body: string, args: string) {
234
+ const positional = args ? args.split(/\s+/) : [];
235
+ let expanded = body.replace(/\$ARGUMENTS\b/g, args);
236
+
237
+ positional.forEach((value, index) => {
238
+ expanded = expanded.replace(new RegExp(`\\$${index + 1}(?!\\d)`, "g"), value);
239
+ });
240
+
241
+ return expanded;
242
+ }
243
+
244
+ /** 跨平台终止子进程(Windows 使用 taskkill,Unix 使用 SIGTERM/SIGKILL) */
245
+ function killChild(child: import("node:child_process").ChildProcess): void {
246
+ if (process.platform === "win32") {
247
+ if (child.pid) {
248
+ try {
249
+ Bun.spawn(["taskkill", "/PID", String(child.pid), "/T", "/F"], {
250
+ stdin: "ignore",
251
+ stdout: "ignore",
252
+ stderr: "ignore",
253
+ });
254
+ } catch {
255
+ child.kill();
256
+ }
257
+ }
258
+ } else {
259
+ child.kill("SIGTERM");
260
+ setTimeout(() => {
261
+ try { child.kill("SIGKILL"); } catch {}
262
+ }, 2_000);
263
+ }
264
+ }
265
+
266
+ function runShellCommand(command: string, cwd: string) {
267
+ return new Promise<string>((resolveCommand) => {
268
+ const child = spawn(command, {
269
+ cwd,
270
+ env: process.env,
271
+ shell: true,
272
+ stdio: ["ignore", "pipe", "pipe"],
273
+ });
274
+
275
+ let stdout = "";
276
+ let stderr = "";
277
+ let settled = false;
278
+
279
+ const finish = (output: string) => {
280
+ if (settled) return;
281
+ settled = true;
282
+ resolveCommand(output.trim());
283
+ };
284
+
285
+ child.stdout.on("data", (chunk) => {
286
+ stdout += chunk.toString();
287
+ });
288
+ child.stderr.on("data", (chunk) => {
289
+ stderr += chunk.toString();
290
+ });
291
+ child.on("error", (error) => {
292
+ finish(`Command failed: ${error.message}`);
293
+ });
294
+ child.on("close", (code) => {
295
+ const combined = [stdout.trim(), stderr.trim()].filter(Boolean).join("\n");
296
+ if (combined) {
297
+ finish(combined);
298
+ } else if (code && code !== 0) {
299
+ finish(`Command exited with status ${code}`);
300
+ } else {
301
+ finish("(no output)");
302
+ }
303
+ });
304
+
305
+ setTimeout(() => {
306
+ if (!settled) {
307
+ killChild(child);
308
+ finish("Command timed out after 10s");
309
+ }
310
+ }, 10_000);
311
+ });
312
+ }
313
+
314
+ async function expandBangCommands(body: string, cwd: string) {
315
+ const matches = [...body.matchAll(/!`([^`]+)`/g)];
316
+ if (matches.length === 0) return body;
317
+
318
+ let expanded = body;
319
+ for (const match of matches) {
320
+ const command = match[1]?.trim();
321
+ if (!command) continue;
322
+
323
+ const output = await runShellCommand(command, cwd);
324
+ const replacement = `\n\`\`\`text\n${output || "(no output)"}\n\`\`\``;
325
+ expanded = expanded.replace(match[0], replacement);
326
+ }
327
+
328
+ return expanded;
329
+ }
330
+
331
+ async function buildLocalCommandPrompt(command: LocalSlashCommand, args: string, cwd: string) {
332
+ const withArgs = applyCommandArguments(command.body, args);
333
+ const withCommandOutput = await expandBangCommands(withArgs, cwd);
334
+
335
+ return [
336
+ `# Local Claude Code Command`,
337
+ `Source: ${command.filePath}`,
338
+ `Invocation: /${command.name}${args ? ` ${args}` : ""}`,
339
+ "",
340
+ withCommandOutput,
341
+ ].join("\n");
342
+ }
343
+
344
+ export interface SessionInfo {
345
+ id: string;
346
+ cwd: string;
347
+ isActive: boolean;
348
+ abortController: AbortController;
349
+ queryHandle: Query | null;
350
+ completedAt: number | null;
351
+ pendingPermissions: Map<string, {
352
+ info: PermissionRequestInfo;
353
+ resolve: (result: { behavior: "allow" } | { behavior: "deny"; message: string }) => void;
354
+ }>;
355
+ }
356
+
357
+ export type MessageCallback = (msg: unknown) => void;
358
+
359
+ export interface PermissionRequestInfo {
360
+ requestId: string;
361
+ toolName: string;
362
+ input: Record<string, unknown>;
363
+ decisionReason?: string;
364
+ description?: string;
365
+ }
366
+
367
+ export type PermissionRequestCallback = (info: PermissionRequestInfo) => void;
368
+
369
+ export interface SessionOptions {
370
+ model?: string;
371
+ effort?: "low" | "medium" | "high" | "max";
372
+ thinking?: { type: "adaptive" } | { type: "enabled"; budgetTokens?: number } | { type: "disabled" };
373
+ permissionMode?: "default" | "acceptEdits" | "plan" | "dontAsk";
374
+ }
375
+
376
+ export class ClaudeSessionManager {
377
+ private sessions = new Map<string, SessionInfo>();
378
+ private cleanupTimer: ReturnType<typeof setInterval>;
379
+ private probeCache = new Map<string, { data: { models: unknown[]; commands: unknown[]; mcpServers: unknown[] }; timestamp: number }>();
380
+ private slashCommandCache = new Map<string, { commands: LocalSlashCommand[]; timestamp: number }>();
381
+ private healthCache: { available: boolean; version?: string; error?: string; timestamp: number } | null = null;
382
+
383
+ constructor() {
384
+ this.cleanupTimer = setInterval(() => {
385
+ this.sweepExpiredSessions();
386
+ }, SESSION_CLEANUP_INTERVAL_MS);
387
+ }
388
+
389
+ private sweepExpiredSessions() {
390
+ const now = Date.now();
391
+ for (const [id, session] of this.sessions) {
392
+ if (
393
+ session.completedAt !== null &&
394
+ now - session.completedAt > SESSION_CLEANUP_DELAY_MS
395
+ ) {
396
+ session.queryHandle = null;
397
+ this.sessions.delete(id);
398
+ console.log(`[Session] Cleaned up expired session ${id}`);
399
+ }
400
+ }
401
+ }
402
+
403
+ /** 检查 Claude CLI 是否可用,结果缓存 5 分钟,失败缓存 60 秒 */
404
+ async checkClaudeHealth(): Promise<{ available: boolean; version?: string; error?: string }> {
405
+ if (this.healthCache && Date.now() - this.healthCache.timestamp < PROBE_CACHE_TTL_MS) {
406
+ return this.healthCache;
407
+ }
408
+
409
+ try {
410
+ const proc = Bun.spawn(["claude", "--version"], {
411
+ stdout: "pipe",
412
+ stderr: "pipe",
413
+ });
414
+ const timer = setTimeout(() => proc.kill(), 5_000);
415
+ const output = await new Response(proc.stdout).text();
416
+ clearTimeout(timer);
417
+ const code = await proc.exited;
418
+
419
+ if (code === 0) {
420
+ const result = { available: true, version: output.trim() };
421
+ this.healthCache = { ...result, timestamp: Date.now() };
422
+ return result;
423
+ }
424
+
425
+ const stderrText = await new Response(proc.stderr).text();
426
+ const result = { available: false, error: stderrText.trim() || `claude --version exited with code ${code}` };
427
+ // 失败时缓存 60 秒,避免每次请求都卡住
428
+ this.healthCache = { ...result, timestamp: Date.now() - PROBE_CACHE_TTL_MS + 60_000 };
429
+ return result;
430
+ } catch (err) {
431
+ const msg = err instanceof Error ? err.message : String(err);
432
+ const result = { available: false, error: msg };
433
+ this.healthCache = { ...result, timestamp: Date.now() - PROBE_CACHE_TTL_MS + 60_000 };
434
+ return result;
435
+ }
436
+ }
437
+
438
+ private markCompleted(sessionId: string, expectedSession?: SessionInfo) {
439
+ const session = this.sessions.get(sessionId);
440
+ if (!session) return;
441
+ if (expectedSession && session !== expectedSession) return;
442
+ this.denyAllPendingPermissions(sessionId);
443
+ session.isActive = false;
444
+ session.queryHandle = null;
445
+ session.completedAt = Date.now();
446
+ // 清理由全局 sweepExpiredSessions 统一处理
447
+ }
448
+
449
+ dispose() {
450
+ clearInterval(this.cleanupTimer);
451
+ for (const [, session] of this.sessions) {
452
+ if (session.isActive) {
453
+ session.abortController.abort();
454
+ }
455
+ session.queryHandle = null;
456
+ }
457
+ this.sessions.clear();
458
+ console.log("[Session] All sessions disposed");
459
+ }
460
+
461
+ abortIfActive(sessionId: string) {
462
+ const session = this.sessions.get(sessionId);
463
+ if (session?.isActive) {
464
+ session.abortController.abort();
465
+ this.markCompleted(sessionId, session);
466
+ console.log(`[Session] Aborted orphan session ${sessionId}`);
467
+ }
468
+ }
469
+
470
+ async startSession(
471
+ prompt: string,
472
+ cwd: string,
473
+ onMessage: MessageCallback,
474
+ onComplete: () => void,
475
+ onError: (err: Error) => void,
476
+ onPermissionRequest: PermissionRequestCallback,
477
+ resumeSessionId?: string,
478
+ options?: SessionOptions
479
+ ): Promise<string> {
480
+ const sessionId = resumeSessionId || crypto.randomUUID();
481
+ const existingSession = this.sessions.get(sessionId);
482
+ if (existingSession?.isActive) {
483
+ throw new Error(`Session ${sessionId} is already active`);
484
+ }
485
+
486
+ // CLI 预检:确认 claude 命令可用
487
+ const health = await this.checkClaudeHealth();
488
+ if (!health.available) {
489
+ throw new Error(
490
+ `Claude Code CLI 不可用:${health.error || "未知错误"}。` +
491
+ `请确认已安装 Claude Code CLI 并且 \`claude\` 命令在 PATH 中可用。`
492
+ );
493
+ }
494
+
495
+ const abortController = new AbortController();
496
+
497
+ const session: SessionInfo = {
498
+ id: sessionId,
499
+ cwd,
500
+ isActive: true,
501
+ abortController,
502
+ queryHandle: null,
503
+ completedAt: null,
504
+ pendingPermissions: new Map(),
505
+ };
506
+ this.sessions.set(sessionId, session);
507
+ const stderrLines: string[] = [];
508
+
509
+ const q = query({
510
+ prompt,
511
+ options: {
512
+ cwd,
513
+ abortController,
514
+ ...(resumeSessionId ? { resume: resumeSessionId } : {}),
515
+ ...(options?.model ? { model: options.model } : {}),
516
+ ...(options?.effort ? { effort: options.effort } : {}),
517
+ ...(options?.thinking ? { thinking: options.thinking } : {}),
518
+ settingSources: [...SDK_SETTING_SOURCES],
519
+ allowedTools: [
520
+ "Read",
521
+ "Edit",
522
+ "Write",
523
+ "Bash",
524
+ "Glob",
525
+ "Grep",
526
+ "WebSearch",
527
+ "WebFetch",
528
+ "TodoWrite",
529
+ "Skill",
530
+ "Agent",
531
+ "NotebookEdit",
532
+ "LSP",
533
+ ],
534
+ permissionMode: options?.permissionMode ?? "acceptEdits",
535
+ maxTurns: 50,
536
+ includePartialMessages: true,
537
+ stderr: (data) => {
538
+ const line = data.trim();
539
+ if (!line) return;
540
+ stderrLines.push(line);
541
+ if (stderrLines.length > STDERR_BUFFER_LIMIT) stderrLines.shift();
542
+ console.warn(`[Claude stderr][${sessionId}] ${line}`);
543
+ },
544
+ canUseTool: async (toolName, input, opts) => {
545
+ return new Promise((resolve) => {
546
+ const requestId = opts.toolUseID;
547
+ const info: PermissionRequestInfo = {
548
+ requestId,
549
+ toolName,
550
+ input,
551
+ decisionReason: opts.decisionReason,
552
+ };
553
+ session.pendingPermissions.set(requestId, { info, resolve });
554
+
555
+ onPermissionRequest(info);
556
+
557
+ opts.signal.addEventListener("abort", () => {
558
+ if (session.pendingPermissions.has(requestId)) {
559
+ session.pendingPermissions.delete(requestId);
560
+ resolve({ behavior: "deny", message: "Request aborted" });
561
+ }
562
+ }, { once: true });
563
+ });
564
+ },
565
+ },
566
+ });
567
+
568
+ session.queryHandle = q;
569
+
570
+ // 异步处理消息流
571
+ (async () => {
572
+ try {
573
+ for await (const message of q) {
574
+ onMessage(message);
575
+ }
576
+ onComplete();
577
+ } catch (err) {
578
+ onError(enrichProcessExitError(err, stderrLines));
579
+ } finally {
580
+ this.markCompleted(sessionId, session);
581
+ }
582
+ })();
583
+
584
+ return sessionId;
585
+ }
586
+
587
+ async getCapabilities(sessionId: string) {
588
+ const session = this.sessions.get(sessionId);
589
+ if (!session?.queryHandle) return null;
590
+
591
+ try {
592
+ const [models, commands, mcpStatus] = await Promise.all([
593
+ session.queryHandle.supportedModels(),
594
+ session.queryHandle.supportedCommands(),
595
+ session.queryHandle.mcpServerStatus(),
596
+ ]);
597
+ return { models, commands, mcpServers: mcpStatus };
598
+ } catch {
599
+ return null;
600
+ }
601
+ }
602
+
603
+ async setModel(sessionId: string, model: string) {
604
+ const session = this.sessions.get(sessionId);
605
+ if (session?.queryHandle) {
606
+ await session.queryHandle.setModel(model);
607
+ }
608
+ }
609
+
610
+ async setPermissionMode(sessionId: string, mode: string) {
611
+ const session = this.sessions.get(sessionId);
612
+ if (session?.queryHandle) {
613
+ await session.queryHandle.setPermissionMode(mode as "default" | "acceptEdits" | "plan" | "dontAsk");
614
+ }
615
+ }
616
+
617
+ async interruptSession(sessionId: string) {
618
+ const session = this.sessions.get(sessionId);
619
+ if (session?.queryHandle) {
620
+ await session.queryHandle.interrupt();
621
+ }
622
+ }
623
+
624
+ abortSession(sessionId: string) {
625
+ const session = this.sessions.get(sessionId);
626
+ if (session) {
627
+ session.abortController.abort();
628
+ this.markCompleted(sessionId, session);
629
+ }
630
+ }
631
+
632
+ resolvePermission(sessionId: string, requestId: string, behavior: "allow" | "deny") {
633
+ const session = this.sessions.get(sessionId);
634
+ const pending = session?.pendingPermissions.get(requestId);
635
+ if (pending) {
636
+ session!.pendingPermissions.delete(requestId);
637
+ if (behavior === "allow") {
638
+ pending.resolve({ behavior: "allow" });
639
+ } else {
640
+ pending.resolve({ behavior: "deny", message: "Denied by user" });
641
+ }
642
+ }
643
+ }
644
+
645
+ denyAllPendingPermissions(sessionId: string) {
646
+ const session = this.sessions.get(sessionId);
647
+ if (!session) return;
648
+ for (const pending of session.pendingPermissions.values()) {
649
+ pending.resolve({ behavior: "deny", message: "Session ended" });
650
+ }
651
+ session.pendingPermissions.clear();
652
+ }
653
+
654
+ getPendingPermissions(sessionId: string): PermissionRequestInfo[] {
655
+ const session = this.sessions.get(sessionId);
656
+ if (!session) return [];
657
+ return [...session.pendingPermissions.values()].map((pending) => pending.info);
658
+ }
659
+
660
+ getActiveSession(): SessionInfo | undefined {
661
+ for (const session of this.sessions.values()) {
662
+ if (session.isActive) return session;
663
+ }
664
+ return undefined;
665
+ }
666
+
667
+ getAnySessionWithHandle(): SessionInfo | undefined {
668
+ for (const session of this.sessions.values()) {
669
+ if (session.isActive && session.queryHandle) return session;
670
+ }
671
+ return undefined;
672
+ }
673
+
674
+ isSessionActive(sessionId: string): boolean {
675
+ return this.sessions.get(sessionId)?.isActive ?? false;
676
+ }
677
+
678
+ /**
679
+ * 创建一个轻量级探测 session 获取 capabilities(模型列表等),然后立即 abort。
680
+ * 用于在没有活跃 session 时获取可用模型信息。
681
+ */
682
+ async probeCapabilities(cwd: string): Promise<{ models: unknown[]; commands: unknown[]; mcpServers: unknown[] } | null> {
683
+ const cacheKey = resolve(cwd);
684
+
685
+ // 使用缓存避免重复创建探测 session
686
+ const cached = this.probeCache.get(cacheKey);
687
+ if (cached && Date.now() - cached.timestamp < PROBE_CACHE_TTL_MS) {
688
+ return cached.data;
689
+ }
690
+
691
+ const abortController = new AbortController();
692
+ let q: Query | null = null;
693
+ const probeStderr: string[] = [];
694
+ try {
695
+ q = query({
696
+ prompt: "hi",
697
+ options: {
698
+ cwd,
699
+ abortController,
700
+ maxTurns: 1,
701
+ permissionMode: "plan",
702
+ allowedTools: [],
703
+ settingSources: [...SDK_SETTING_SOURCES],
704
+ stderr: (data) => {
705
+ const line = data.trim();
706
+ if (!line) return;
707
+ probeStderr.push(line);
708
+ if (probeStderr.length > STDERR_BUFFER_LIMIT) probeStderr.shift();
709
+ },
710
+ },
711
+ });
712
+
713
+ const initResult = await q.initializationResult();
714
+ const data = {
715
+ models: initResult.models,
716
+ commands: initResult.commands,
717
+ mcpServers: [],
718
+ };
719
+ this.probeCache.set(cacheKey, { data, timestamp: Date.now() });
720
+ return data;
721
+ } catch (error) {
722
+ const enriched = enrichProcessExitError(error, probeStderr);
723
+ console.warn(`[Claude probe] ${enriched.message}`);
724
+ return null;
725
+ } finally {
726
+ abortController.abort();
727
+ q = null;
728
+ }
729
+ }
730
+
731
+ async resolvePrompt(prompt: string, cwd: string) {
732
+ const invocation = parseSlashInvocation(prompt);
733
+ if (!invocation) return prompt;
734
+
735
+ const caps = await this.probeCapabilities(cwd);
736
+ if (!caps) return prompt;
737
+
738
+ const knownCommands = extractCommandNames(caps.commands);
739
+ if (knownCommands.has(invocation.name)) {
740
+ return prompt;
741
+ }
742
+
743
+ // 使用缓存的 slash command 发现结果
744
+ const normalizedCwd = resolve(cwd);
745
+ const cached = this.slashCommandCache.get(normalizedCwd);
746
+ let localCommands: LocalSlashCommand[];
747
+ if (cached && Date.now() - cached.timestamp < SLASH_COMMAND_CACHE_TTL_MS) {
748
+ localCommands = cached.commands;
749
+ } else {
750
+ localCommands = await discoverFallbackSlashCommands(cwd);
751
+ this.slashCommandCache.set(normalizedCwd, { commands: localCommands, timestamp: Date.now() });
752
+ }
753
+
754
+ const command = localCommands.find((item) => item.name === invocation.name);
755
+ if (!command) return prompt;
756
+
757
+ return buildLocalCommandPrompt(command, invocation.args, cwd);
758
+ }
759
+ }