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
package/server/ws.ts ADDED
@@ -0,0 +1,785 @@
1
+ import { verify } from "hono/jwt";
2
+ import { ClaudeSessionManager, type SessionOptions } from "./claude";
3
+ import { TerminalManager } from "./terminal";
4
+ import { DataStore } from "./db";
5
+ import type { ServerWebSocket } from "bun";
6
+
7
+ const DISCONNECT_ABORT_DELAY_MS = 30_000; // 断连 30 秒后 abort 进行中的 session
8
+
9
+ // SDK 消息类型(简化定义,运行时用 type 字段判断)
10
+ interface SDKMessage {
11
+ type: string;
12
+ [key: string]: unknown;
13
+ }
14
+
15
+ interface WSState {
16
+ authenticated: boolean;
17
+ clientId: string;
18
+ activeSessionId: string | null;
19
+ }
20
+
21
+ const sessionManager = new ClaudeSessionManager();
22
+ const terminalManager = new TerminalManager();
23
+
24
+ export { sessionManager, terminalManager };
25
+
26
+ function send(ws: ServerWebSocket<WSState> | null | undefined, data: unknown) {
27
+ if (ws?.readyState === 1) {
28
+ ws.send(JSON.stringify(data));
29
+ }
30
+ }
31
+
32
+ // 每个 session 一个可重绑定的流式转发器,允许断线后把输出挂到新连接。
33
+ type SessionBinding = {
34
+ ws: ServerWebSocket<WSState> | null;
35
+ pendingMessages: unknown[];
36
+ text: Map<number, string>; // blockIndex -> accumulated text
37
+ thinking: Map<number, string>; // blockIndex -> accumulated thinking
38
+ toolInput: Map<number, string>; // blockIndex -> accumulated input delta
39
+ timer: ReturnType<typeof setTimeout> | null;
40
+ };
41
+
42
+ function createSessionBinding(ws: ServerWebSocket<WSState> | null): SessionBinding {
43
+ return {
44
+ ws,
45
+ pendingMessages: [],
46
+ text: new Map(),
47
+ thinking: new Map(),
48
+ toolInput: new Map(),
49
+ timer: null,
50
+ };
51
+ }
52
+
53
+ function sendOrQueue(binding: SessionBinding, data: unknown) {
54
+ if (binding.ws?.readyState === 1) {
55
+ binding.ws.send(JSON.stringify(data));
56
+ return;
57
+ }
58
+ binding.pendingMessages.push(data);
59
+ }
60
+
61
+ function flushQueuedMessages(binding: SessionBinding) {
62
+ if (binding.ws?.readyState !== 1 || binding.pendingMessages.length === 0) return;
63
+
64
+ const queued = [...binding.pendingMessages];
65
+ binding.pendingMessages = [];
66
+
67
+ for (const message of queued) {
68
+ binding.ws.send(JSON.stringify(message));
69
+ }
70
+ }
71
+
72
+ function flushDeltaBuffer(binding: SessionBinding) {
73
+ for (const [blockIndex, text] of binding.text) {
74
+ sendOrQueue(binding, { type: "stream_delta", payload: { text, blockIndex } });
75
+ }
76
+ for (const [blockIndex, thinking] of binding.thinking) {
77
+ sendOrQueue(binding, { type: "thinking_delta", payload: { thinking, blockIndex } });
78
+ }
79
+ for (const [blockIndex, delta] of binding.toolInput) {
80
+ sendOrQueue(binding, { type: "tool_input_delta", payload: { delta, blockIndex } });
81
+ }
82
+ binding.text.clear();
83
+ binding.thinking.clear();
84
+ binding.toolInput.clear();
85
+ binding.timer = null;
86
+ }
87
+
88
+ function scheduleDeltaFlush(binding: SessionBinding) {
89
+ if (!binding.timer) {
90
+ binding.timer = setTimeout(() => flushDeltaBuffer(binding), 16);
91
+ }
92
+ }
93
+
94
+ function mapCommands(commands: Array<Record<string, unknown>>) {
95
+ return commands
96
+ .map((command) => ({
97
+ name: String(command.name ?? command.command ?? ""),
98
+ description: typeof command.description === "string" ? command.description : undefined,
99
+ argumentHint: typeof command.argumentHint === "string" ? command.argumentHint : undefined,
100
+ }))
101
+ .filter((command) => command.name);
102
+ }
103
+
104
+ function forwardSDKMessage(binding: SessionBinding, msg: SDKMessage, managedSessionId: string) {
105
+ switch (msg.type) {
106
+ case "stream_event": {
107
+ const event = msg.event as Record<string, unknown>;
108
+ if (!event) break;
109
+
110
+ const eventType = event.type as string;
111
+
112
+ if (eventType === "content_block_delta") {
113
+ const delta = event.delta as Record<string, unknown>;
114
+ if (delta?.type === "text_delta") {
115
+ // 使用 buffer 合并文本 delta
116
+ const blockIndex = event.index as number;
117
+ const existing = binding.text.get(blockIndex) || "";
118
+ binding.text.set(blockIndex, existing + (delta.text as string));
119
+ scheduleDeltaFlush(binding);
120
+ } else if (delta?.type === "thinking_delta") {
121
+ // 使用 buffer 合并 thinking delta
122
+ const blockIndex = event.index as number;
123
+ const existing = binding.thinking.get(blockIndex) || "";
124
+ binding.thinking.set(blockIndex, existing + (delta.thinking as string));
125
+ scheduleDeltaFlush(binding);
126
+ } else if (delta?.type === "input_json_delta") {
127
+ // 使用 buffer 合并 tool input delta
128
+ const blockIndex = event.index as number;
129
+ const existing = binding.toolInput.get(blockIndex) || "";
130
+ binding.toolInput.set(blockIndex, existing + (delta.partial_json as string));
131
+ scheduleDeltaFlush(binding);
132
+ }
133
+ } else if (eventType === "content_block_start") {
134
+ // block start 前先 flush 积压的 delta
135
+ if (binding.timer) {
136
+ clearTimeout(binding.timer);
137
+ flushDeltaBuffer(binding);
138
+ }
139
+ const block = event.content_block as Record<string, unknown>;
140
+ if (block?.type === "tool_use") {
141
+ sendOrQueue(binding, {
142
+ type: "tool_start",
143
+ payload: {
144
+ id: block.id,
145
+ name: block.name,
146
+ blockIndex: event.index,
147
+ },
148
+ });
149
+ } else if (block?.type === "thinking") {
150
+ sendOrQueue(binding, {
151
+ type: "thinking_start",
152
+ payload: { blockIndex: event.index },
153
+ });
154
+ }
155
+ } else if (eventType === "content_block_stop") {
156
+ // block stop 前先 flush 积压的 delta
157
+ if (binding.timer) {
158
+ clearTimeout(binding.timer);
159
+ flushDeltaBuffer(binding);
160
+ }
161
+ sendOrQueue(binding, {
162
+ type: "block_stop",
163
+ payload: { blockIndex: event.index },
164
+ });
165
+ }
166
+ break;
167
+ }
168
+
169
+ case "assistant": {
170
+ // 完整消息到达前 flush 所有积压的 delta
171
+ if (binding.timer) {
172
+ clearTimeout(binding.timer);
173
+ flushDeltaBuffer(binding);
174
+ }
175
+ const message = msg.message as Record<string, unknown>;
176
+ const content = message?.content;
177
+ if (Array.isArray(content)) {
178
+ const blocks = content.map((block: Record<string, unknown>) => {
179
+ if (block.type === "text")
180
+ return { type: "text", text: block.text };
181
+ if (block.type === "tool_use")
182
+ return {
183
+ type: "tool_use",
184
+ id: block.id,
185
+ name: block.name,
186
+ input: block.input,
187
+ };
188
+ if (block.type === "thinking")
189
+ return { type: "thinking", thinking: block.thinking };
190
+ if (block.type === "tool_result")
191
+ return {
192
+ type: "tool_result",
193
+ tool_use_id: block.tool_use_id,
194
+ content: block.content,
195
+ is_error: block.is_error,
196
+ };
197
+ return block;
198
+ });
199
+ sendOrQueue(binding, {
200
+ type: "assistant_message",
201
+ payload: { content: blocks, model: message.model },
202
+ });
203
+ }
204
+ break;
205
+ }
206
+
207
+ case "result": {
208
+ // result 前 flush 所有积压的 delta
209
+ if (binding.timer) {
210
+ clearTimeout(binding.timer);
211
+ flushDeltaBuffer(binding);
212
+ }
213
+ sendOrQueue(binding, {
214
+ type: "result",
215
+ payload: {
216
+ sessionId: managedSessionId || (msg.session_id as string),
217
+ durationMs: msg.duration_ms,
218
+ isError: msg.is_error,
219
+ numTurns: msg.num_turns,
220
+ },
221
+ });
222
+ break;
223
+ }
224
+
225
+ case "system": {
226
+ if (msg.subtype === "init") {
227
+ const mcpServers = Array.isArray(msg.mcp_servers)
228
+ ? (msg.mcp_servers as Array<{ name: string; status: string }>).map((s) => ({
229
+ name: s.name,
230
+ status: s.status,
231
+ }))
232
+ : [];
233
+
234
+ const slashCommands = Array.isArray(msg.slash_commands)
235
+ ? (msg.slash_commands as string[])
236
+ : [];
237
+ const skills = Array.isArray(msg.skills)
238
+ ? (msg.skills as string[])
239
+ : [];
240
+
241
+ sendOrQueue(binding, {
242
+ type: "system_init",
243
+ payload: {
244
+ sessionId: managedSessionId || (msg.session_id as string),
245
+ tools: msg.tools,
246
+ model: msg.model,
247
+ permissionMode: msg.permissionMode,
248
+ mcpServers,
249
+ slashCommands,
250
+ skills,
251
+ },
252
+ });
253
+
254
+ // 异步获取详细能力信息
255
+ const sessionId = managedSessionId || (msg.session_id as string);
256
+ if (sessionId) {
257
+ sendCapabilities(binding.ws, sessionId);
258
+ }
259
+ }
260
+ break;
261
+ }
262
+ }
263
+ }
264
+
265
+ async function sendCapabilities(ws: ServerWebSocket<WSState> | null, sessionId: string) {
266
+ try {
267
+ const caps = await sessionManager.getCapabilities(sessionId);
268
+ if (!caps) return false;
269
+
270
+ send(ws, {
271
+ type: "capabilities",
272
+ payload: {
273
+ models: (caps.models as Array<Record<string, unknown>>).map((m) => ({
274
+ value: m.value,
275
+ displayName: m.displayName,
276
+ description: m.description,
277
+ supportsEffort: m.supportsEffort,
278
+ supportedEffortLevels: m.supportedEffortLevels,
279
+ supportsAdaptiveThinking: m.supportsAdaptiveThinking,
280
+ supportsFastMode: m.supportsFastMode,
281
+ })),
282
+ commands: mapCommands(caps.commands as Array<Record<string, unknown>>),
283
+ mcpServers: (caps.mcpServers as Array<Record<string, unknown>>).map((s) => ({
284
+ name: s.name,
285
+ status: s.status,
286
+ })),
287
+ },
288
+ });
289
+ return true;
290
+ } catch {
291
+ // capabilities 获取失败不影响正常使用
292
+ return false;
293
+ }
294
+ }
295
+
296
+ export function createWSHandlers(jwtSecret: string, db: DataStore) {
297
+ const disconnectTimers = new Map<string, ReturnType<typeof setTimeout>>();
298
+ const sessionBindings = new Map<string, SessionBinding>();
299
+ const terminalSocketBindings = new Map<string, ServerWebSocket<WSState> | null>();
300
+
301
+ function clearDisconnectTimer(sessionId: string) {
302
+ const timer = disconnectTimers.get(sessionId);
303
+ if (!timer) return;
304
+ clearTimeout(timer);
305
+ disconnectTimers.delete(sessionId);
306
+ }
307
+
308
+ function startDisconnectTimer(sessionId: string) {
309
+ if (disconnectTimers.has(sessionId)) return;
310
+
311
+ disconnectTimers.set(
312
+ sessionId,
313
+ setTimeout(() => {
314
+ console.log(`[WS] Client did not reconnect, aborting session ${sessionId}`);
315
+ sessionManager.denyAllPendingPermissions(sessionId);
316
+ sessionManager.abortIfActive(sessionId);
317
+ sessionBindings.delete(sessionId);
318
+ disconnectTimers.delete(sessionId);
319
+ }, DISCONNECT_ABORT_DELAY_MS)
320
+ );
321
+ }
322
+
323
+ function detachSessionFromSocket(ws: ServerWebSocket<WSState>, sessionId: string) {
324
+ const binding = sessionBindings.get(sessionId);
325
+ if (binding?.ws === ws) {
326
+ binding.ws = null;
327
+ startDisconnectTimer(sessionId);
328
+ }
329
+ if (ws.data.activeSessionId === sessionId) {
330
+ ws.data.activeSessionId = null;
331
+ }
332
+ }
333
+
334
+ function replayPendingPermissions(ws: ServerWebSocket<WSState>, sessionId: string) {
335
+ for (const permReq of sessionManager.getPendingPermissions(sessionId)) {
336
+ send(ws, {
337
+ type: "permission_request",
338
+ payload: {
339
+ sessionId,
340
+ ...permReq,
341
+ },
342
+ });
343
+ }
344
+ }
345
+
346
+ function attachSession(ws: ServerWebSocket<WSState>, sessionId: string) {
347
+ if (ws.data.activeSessionId && ws.data.activeSessionId !== sessionId) {
348
+ if (sessionManager.isSessionActive(ws.data.activeSessionId)) {
349
+ detachSessionFromSocket(ws, ws.data.activeSessionId);
350
+ } else {
351
+ const currentBinding = sessionBindings.get(ws.data.activeSessionId);
352
+ if (currentBinding?.ws === ws) {
353
+ currentBinding.ws = null;
354
+ }
355
+ ws.data.activeSessionId = null;
356
+ }
357
+ }
358
+
359
+ const binding = sessionBindings.get(sessionId);
360
+ if (!binding || !sessionManager.isSessionActive(sessionId)) return false;
361
+ if (binding.ws && binding.ws !== ws) {
362
+ binding.ws.data.activeSessionId = null;
363
+ }
364
+ binding.ws = ws;
365
+ ws.data.activeSessionId = sessionId;
366
+ clearDisconnectTimer(sessionId);
367
+ flushQueuedMessages(binding);
368
+ if (binding.timer || binding.text.size > 0 || binding.thinking.size > 0 || binding.toolInput.size > 0) {
369
+ if (binding.timer) {
370
+ clearTimeout(binding.timer);
371
+ }
372
+ flushDeltaBuffer(binding);
373
+ }
374
+ replayPendingPermissions(ws, sessionId);
375
+ void sendCapabilities(ws, sessionId);
376
+ return true;
377
+ }
378
+
379
+ function bindTerminalSocket(terminalId: string, ws: ServerWebSocket<WSState>) {
380
+ terminalSocketBindings.set(terminalId, ws);
381
+ }
382
+
383
+ function unbindTerminalSocketsFor(ws: ServerWebSocket<WSState>) {
384
+ for (const [terminalId, boundWs] of terminalSocketBindings) {
385
+ if (boundWs === ws) {
386
+ terminalSocketBindings.set(terminalId, null);
387
+ }
388
+ }
389
+ }
390
+
391
+ function sendTerminalEvent(terminalId: string, data: unknown) {
392
+ const targetWs = terminalSocketBindings.get(terminalId);
393
+ send(targetWs, data);
394
+ }
395
+
396
+ return {
397
+ open(ws: ServerWebSocket<WSState>) {
398
+ send(ws, {
399
+ type: "status",
400
+ payload: { connected: true, needsAuth: true },
401
+ });
402
+ },
403
+
404
+ async message(ws: ServerWebSocket<WSState>, message: string | Buffer) {
405
+ let data: Record<string, unknown>;
406
+ try {
407
+ data = JSON.parse(
408
+ typeof message === "string" ? message : message.toString()
409
+ );
410
+ } catch {
411
+ send(ws, { type: "error", payload: { message: "Invalid JSON" } });
412
+ return;
413
+ }
414
+
415
+ switch (data.type) {
416
+ case "auth": {
417
+ try {
418
+ await verify(data.token as string, jwtSecret, "HS256");
419
+ ws.data.authenticated = true;
420
+ send(ws, {
421
+ type: "auth_result",
422
+ payload: { success: true },
423
+ });
424
+ } catch {
425
+ send(ws, {
426
+ type: "auth_result",
427
+ payload: { success: false, error: "Invalid token" },
428
+ });
429
+ }
430
+ break;
431
+ }
432
+
433
+ case "chat": {
434
+ if (!ws.data.authenticated) {
435
+ send(ws, {
436
+ type: "error",
437
+ payload: { message: "Not authenticated" },
438
+ });
439
+ return;
440
+ }
441
+
442
+ const payload = data.payload as {
443
+ prompt: string;
444
+ cwd?: string;
445
+ resumeSessionId?: string;
446
+ model?: string;
447
+ effort?: string;
448
+ thinking?: string;
449
+ permissionMode?: SessionOptions["permissionMode"];
450
+ };
451
+
452
+ if (!payload?.prompt) {
453
+ send(ws, {
454
+ type: "error",
455
+ payload: { message: "Missing prompt" },
456
+ });
457
+ return;
458
+ }
459
+
460
+ if (payload.resumeSessionId && sessionManager.isSessionActive(payload.resumeSessionId)) {
461
+ send(ws, {
462
+ type: "error",
463
+ payload: { message: "Session is still processing. Reattach before sending another message." },
464
+ });
465
+ return;
466
+ }
467
+
468
+ try {
469
+ if (
470
+ ws.data.activeSessionId &&
471
+ ws.data.activeSessionId !== payload.resumeSessionId &&
472
+ sessionManager.isSessionActive(ws.data.activeSessionId)
473
+ ) {
474
+ send(ws, {
475
+ type: "error",
476
+ payload: { message: "A session is already processing on this connection. Interrupt or abort it first." },
477
+ });
478
+ return;
479
+ }
480
+
481
+ const cwd = payload.cwd || process.cwd();
482
+ const resolvedPrompt = await sessionManager.resolvePrompt(payload.prompt, cwd);
483
+
484
+ const sessionOpts: SessionOptions = {};
485
+ if (payload.model) sessionOpts.model = payload.model;
486
+ if (payload.effort) sessionOpts.effort = payload.effort as SessionOptions["effort"];
487
+ if (payload.thinking) {
488
+ if (payload.thinking === "disabled") {
489
+ sessionOpts.thinking = { type: "disabled" };
490
+ } else if (payload.thinking === "adaptive") {
491
+ sessionOpts.thinking = { type: "adaptive" };
492
+ } else if (payload.thinking === "enabled") {
493
+ sessionOpts.thinking = { type: "enabled" };
494
+ }
495
+ }
496
+ if (payload.permissionMode) {
497
+ sessionOpts.permissionMode = payload.permissionMode;
498
+ }
499
+
500
+ send(ws, { type: "chat_started", payload: { cwd } });
501
+ if (payload.resumeSessionId) clearDisconnectTimer(payload.resumeSessionId);
502
+
503
+ let sessionId = payload.resumeSessionId || "";
504
+ const binding = createSessionBinding(ws);
505
+ sessionId = await sessionManager.startSession(
506
+ resolvedPrompt,
507
+ cwd,
508
+ (msg) => forwardSDKMessage(binding, msg as SDKMessage, sessionId),
509
+ () => {
510
+ if (binding.ws?.data.activeSessionId === sessionId) {
511
+ binding.ws.data.activeSessionId = null;
512
+ }
513
+ clearDisconnectTimer(sessionId);
514
+ sessionBindings.delete(sessionId);
515
+ send(binding.ws, {
516
+ type: "chat_complete",
517
+ payload: { sessionId },
518
+ });
519
+ void sendCapabilities(binding.ws, sessionId);
520
+ },
521
+ (err) => {
522
+ if (binding.ws?.data.activeSessionId === sessionId) {
523
+ binding.ws.data.activeSessionId = null;
524
+ }
525
+ clearDisconnectTimer(sessionId);
526
+ sessionBindings.delete(sessionId);
527
+ send(binding.ws, {
528
+ type: "error",
529
+ payload: { message: err.message },
530
+ });
531
+ },
532
+ (permReq) => {
533
+ send(binding.ws, {
534
+ type: "permission_request",
535
+ payload: {
536
+ sessionId,
537
+ ...permReq,
538
+ },
539
+ });
540
+ },
541
+ payload.resumeSessionId,
542
+ sessionOpts
543
+ );
544
+
545
+ sessionBindings.set(sessionId, binding);
546
+ ws.data.activeSessionId = sessionId;
547
+ db.createSession(sessionId, cwd);
548
+ db.saveMessage(sessionId, "user", { prompt: payload.prompt });
549
+ } catch (err) {
550
+ send(ws, {
551
+ type: "error",
552
+ payload: {
553
+ message:
554
+ err instanceof Error ? err.message : "Failed to start session",
555
+ },
556
+ });
557
+ }
558
+ break;
559
+ }
560
+
561
+ case "reattach": {
562
+ if (!ws.data.authenticated) return;
563
+ const payload = data.payload as { sessionId?: string };
564
+ if (!payload?.sessionId) {
565
+ send(ws, {
566
+ type: "error",
567
+ payload: { message: "Missing sessionId" },
568
+ });
569
+ return;
570
+ }
571
+
572
+ if (!sessionManager.isSessionActive(payload.sessionId)) {
573
+ send(ws, {
574
+ type: "error",
575
+ payload: { message: "Session is no longer active" },
576
+ });
577
+ return;
578
+ }
579
+
580
+ if (!attachSession(ws, payload.sessionId)) {
581
+ send(ws, {
582
+ type: "error",
583
+ payload: { message: "Failed to reattach session" },
584
+ });
585
+ }
586
+ break;
587
+ }
588
+
589
+ case "set_model": {
590
+ if (!ws.data.authenticated) return;
591
+ const sessionId = ws.data.activeSessionId;
592
+ if (sessionId) {
593
+ try {
594
+ const model = (data.payload as { model: string }).model;
595
+ await sessionManager.setModel(sessionId, model);
596
+ send(ws, { type: "model_changed", payload: { model } });
597
+ } catch (err) {
598
+ send(ws, {
599
+ type: "error",
600
+ payload: { message: err instanceof Error ? err.message : "Failed to set model" },
601
+ });
602
+ }
603
+ }
604
+ break;
605
+ }
606
+
607
+ case "set_permission_mode": {
608
+ if (!ws.data.authenticated) return;
609
+ const sessionId = ws.data.activeSessionId;
610
+ if (sessionId) {
611
+ try {
612
+ const mode = (data.payload as { mode: string }).mode;
613
+ await sessionManager.setPermissionMode(sessionId, mode);
614
+ send(ws, { type: "permission_mode_changed", payload: { mode } });
615
+ } catch (err) {
616
+ send(ws, {
617
+ type: "error",
618
+ payload: { message: err instanceof Error ? err.message : "Failed to set permission mode" },
619
+ });
620
+ }
621
+ }
622
+ break;
623
+ }
624
+
625
+ case "request_capabilities": {
626
+ if (!ws.data.authenticated) return;
627
+ const payload = (data.payload as { cwd?: string; sessionId?: string } | undefined) ?? {};
628
+ const sessionId = payload.sessionId || ws.data.activeSessionId;
629
+ const sent = sessionId ? await sendCapabilities(ws, sessionId) : false;
630
+ if (!sent) {
631
+ // 无活跃 session 时通过探测获取 capabilities
632
+ try {
633
+ const caps = await sessionManager.probeCapabilities(payload.cwd || process.cwd());
634
+ if (caps) {
635
+ send(ws, {
636
+ type: "capabilities",
637
+ payload: {
638
+ models: (caps.models as Array<Record<string, unknown>>).map((m) => ({
639
+ value: m.value,
640
+ displayName: m.displayName,
641
+ description: m.description,
642
+ supportsEffort: m.supportsEffort,
643
+ supportedEffortLevels: m.supportedEffortLevels,
644
+ supportsAdaptiveThinking: m.supportsAdaptiveThinking,
645
+ supportsFastMode: m.supportsFastMode,
646
+ })),
647
+ commands: mapCommands(caps.commands as Array<Record<string, unknown>>),
648
+ mcpServers: [],
649
+ },
650
+ });
651
+ }
652
+ } catch {
653
+ // probe 失败不影响正常使用
654
+ }
655
+ }
656
+ break;
657
+ }
658
+
659
+ case "interrupt": {
660
+ if (!ws.data.authenticated) return;
661
+ const sessionId = ws.data.activeSessionId;
662
+ if (sessionId) {
663
+ await sessionManager.interruptSession(sessionId);
664
+ send(ws, { type: "interrupted", payload: {} });
665
+ }
666
+ break;
667
+ }
668
+
669
+ case "abort": {
670
+ if (!ws.data.authenticated) return;
671
+ const sessionId = ws.data.activeSessionId;
672
+ if (sessionId) {
673
+ sessionManager.abortSession(sessionId);
674
+ const binding = sessionBindings.get(sessionId);
675
+ if (binding?.ws?.data.activeSessionId === sessionId) {
676
+ binding.ws.data.activeSessionId = null;
677
+ }
678
+ ws.data.activeSessionId = null;
679
+ sessionBindings.delete(sessionId);
680
+ clearDisconnectTimer(sessionId);
681
+ send(ws, { type: "aborted", payload: {} });
682
+ }
683
+ break;
684
+ }
685
+
686
+ case "permission_response": {
687
+ if (!ws.data.authenticated) return;
688
+ const permPayload = data.payload as { requestId: string; behavior: "allow" | "deny"; sessionId?: string };
689
+ const sessionId = permPayload?.sessionId || ws.data.activeSessionId;
690
+ if (sessionId && permPayload?.requestId) {
691
+ sessionManager.resolvePermission(sessionId, permPayload.requestId, permPayload.behavior);
692
+ }
693
+ break;
694
+ }
695
+
696
+ case "ping": {
697
+ send(ws, { type: "pong" });
698
+ break;
699
+ }
700
+
701
+ // Terminal 消息处理
702
+ case "terminal_create": {
703
+ if (!ws.data.authenticated) return;
704
+ const tPayload = data.payload as { id?: string; cwd?: string; shell?: string; cols?: number; rows?: number } | undefined;
705
+ const termId = tPayload?.id || crypto.randomUUID();
706
+ bindTerminalSocket(termId, ws);
707
+ try {
708
+ const info = terminalManager.create(termId, {
709
+ cwd: tPayload?.cwd,
710
+ shell: tPayload?.shell,
711
+ cols: tPayload?.cols,
712
+ rows: tPayload?.rows,
713
+ onData: (output) => {
714
+ sendTerminalEvent(termId, { type: "terminal_output", payload: { id: termId, data: output } });
715
+ },
716
+ onExit: (exitCode) => {
717
+ sendTerminalEvent(termId, { type: "terminal_exited", payload: { id: termId, exitCode } });
718
+ terminalSocketBindings.delete(termId);
719
+ },
720
+ onPortDetected: (port, url) => {
721
+ sendTerminalEvent(termId, { type: "port_detected", payload: { terminalId: termId, port, url } });
722
+ },
723
+ });
724
+ send(ws, { type: "terminal_created", payload: { id: info.id, shell: info.shell, cwd: info.cwd } });
725
+ } catch (err) {
726
+ terminalSocketBindings.delete(termId);
727
+ send(ws, { type: "error", payload: { message: err instanceof Error ? err.message : "Failed to create terminal" } });
728
+ }
729
+ break;
730
+ }
731
+
732
+ case "terminal_input": {
733
+ if (!ws.data.authenticated) return;
734
+ const inputPayload = data.payload as { id: string; data: string };
735
+ try {
736
+ terminalManager.write(inputPayload.id, inputPayload.data);
737
+ } catch (err) {
738
+ send(ws, { type: "error", payload: { message: err instanceof Error ? err.message : "Terminal write failed" } });
739
+ }
740
+ break;
741
+ }
742
+
743
+ case "terminal_resize": {
744
+ if (!ws.data.authenticated) return;
745
+ const resizePayload = data.payload as { id: string; cols: number; rows: number };
746
+ try {
747
+ terminalManager.resize(resizePayload.id, resizePayload.cols, resizePayload.rows);
748
+ } catch (err) {
749
+ send(ws, { type: "error", payload: { message: err instanceof Error ? err.message : "Terminal resize failed" } });
750
+ }
751
+ break;
752
+ }
753
+
754
+ case "terminal_destroy": {
755
+ if (!ws.data.authenticated) return;
756
+ const destroyPayload = data.payload as { id: string };
757
+ terminalManager.destroy(destroyPayload.id);
758
+ break;
759
+ }
760
+
761
+ case "terminal_list": {
762
+ if (!ws.data.authenticated) return;
763
+ const terminals = terminalManager.list();
764
+ for (const terminal of terminals) {
765
+ bindTerminalSocket(terminal.id, ws);
766
+ }
767
+ send(ws, { type: "terminal_list", payload: { terminals } });
768
+ break;
769
+ }
770
+ }
771
+ },
772
+
773
+ close(ws: ServerWebSocket<WSState>) {
774
+ console.log(`[WS] Client disconnected: ${ws.data.clientId}`);
775
+
776
+ const activeSessionId = ws.data.activeSessionId;
777
+ if (activeSessionId) {
778
+ detachSessionFromSocket(ws, activeSessionId);
779
+ }
780
+ unbindTerminalSocketsFor(ws);
781
+ },
782
+ };
783
+ }
784
+
785
+ export type { WSState };