hammoc 1.0.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 (838) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +272 -0
  3. package/bin/hammoc.js +59 -0
  4. package/package.json +86 -0
  5. package/packages/client/dist/assets/abap-DsBKuouk.js +1 -0
  6. package/packages/client/dist/assets/actionscript-3-D_z4Izcz.js +1 -0
  7. package/packages/client/dist/assets/ada-727ZlQH0.js +1 -0
  8. package/packages/client/dist/assets/andromeeda-C3khCPGq.js +1 -0
  9. package/packages/client/dist/assets/angular-html-LfdN0zeE.js +1 -0
  10. package/packages/client/dist/assets/angular-ts-CKsD7JZE.js +1 -0
  11. package/packages/client/dist/assets/apache-Dn00JSTd.js +1 -0
  12. package/packages/client/dist/assets/apex-COJ4H7py.js +1 -0
  13. package/packages/client/dist/assets/apl-BBq3IX1j.js +1 -0
  14. package/packages/client/dist/assets/applescript-Bu5BbsvL.js +1 -0
  15. package/packages/client/dist/assets/ara-7O62HKoU.js +1 -0
  16. package/packages/client/dist/assets/asciidoc-BPT9niGB.js +1 -0
  17. package/packages/client/dist/assets/asm-Dhn9LcZ4.js +1 -0
  18. package/packages/client/dist/assets/astro-CqkE3fuf.js +1 -0
  19. package/packages/client/dist/assets/aurora-x-D-2ljcwZ.js +1 -0
  20. package/packages/client/dist/assets/awk-eg146-Ew.js +1 -0
  21. package/packages/client/dist/assets/ayu-dark-Cv9koXgw.js +1 -0
  22. package/packages/client/dist/assets/ballerina-Du268qiB.js +1 -0
  23. package/packages/client/dist/assets/bat-fje9CFhw.js +1 -0
  24. package/packages/client/dist/assets/beancount-BwXTMy5W.js +1 -0
  25. package/packages/client/dist/assets/berry-3xVqZejG.js +1 -0
  26. package/packages/client/dist/assets/bibtex-xW4inM5L.js +1 -0
  27. package/packages/client/dist/assets/bicep-DHo0CJ0O.js +1 -0
  28. package/packages/client/dist/assets/blade-a8OxSdnT.js +1 -0
  29. package/packages/client/dist/assets/bsl-Dgyn0ogV.js +1 -0
  30. package/packages/client/dist/assets/c-C3t2pwGQ.js +1 -0
  31. package/packages/client/dist/assets/cadence-DNquZEk8.js +1 -0
  32. package/packages/client/dist/assets/cairo--RitsXJZ.js +1 -0
  33. package/packages/client/dist/assets/catppuccin-frappe-CD_QflpE.js +1 -0
  34. package/packages/client/dist/assets/catppuccin-latte-DRW-0cLl.js +1 -0
  35. package/packages/client/dist/assets/catppuccin-macchiato-C-_shW-Y.js +1 -0
  36. package/packages/client/dist/assets/catppuccin-mocha-LGGdnPYs.js +1 -0
  37. package/packages/client/dist/assets/clarity-BHOwM8T6.js +1 -0
  38. package/packages/client/dist/assets/clojure-DxSadP1t.js +1 -0
  39. package/packages/client/dist/assets/cmake-DbXoA79R.js +1 -0
  40. package/packages/client/dist/assets/cobol-PTqiYgYu.js +1 -0
  41. package/packages/client/dist/assets/codeowners-Bp6g37R7.js +1 -0
  42. package/packages/client/dist/assets/codeql-sacFqUAJ.js +1 -0
  43. package/packages/client/dist/assets/coffee-dyiR41kL.js +1 -0
  44. package/packages/client/dist/assets/common-lisp-C7gG9l05.js +1 -0
  45. package/packages/client/dist/assets/coq-Dsg_Bt_b.js +1 -0
  46. package/packages/client/dist/assets/cpp-BksuvNSY.js +1 -0
  47. package/packages/client/dist/assets/crystal-DtDmRg-F.js +1 -0
  48. package/packages/client/dist/assets/csharp-D9R-vmeu.js +1 -0
  49. package/packages/client/dist/assets/css-BPhBrDlE.js +1 -0
  50. package/packages/client/dist/assets/csv-B0qRVHPH.js +1 -0
  51. package/packages/client/dist/assets/cue-DtFQj3wx.js +1 -0
  52. package/packages/client/dist/assets/cypher-m2LEI-9-.js +1 -0
  53. package/packages/client/dist/assets/d-BoXegm-a.js +1 -0
  54. package/packages/client/dist/assets/dark-plus-C3mMm8J8.js +1 -0
  55. package/packages/client/dist/assets/dart-B9wLZaAG.js +1 -0
  56. package/packages/client/dist/assets/dax-ClGRhx96.js +1 -0
  57. package/packages/client/dist/assets/desktop-DEIpsLCJ.js +1 -0
  58. package/packages/client/dist/assets/diff-BgYniUM_.js +1 -0
  59. package/packages/client/dist/assets/docker-COcR7UxN.js +1 -0
  60. package/packages/client/dist/assets/dotenv-BjQB5zDj.js +1 -0
  61. package/packages/client/dist/assets/dracula-BzJJZx-M.js +1 -0
  62. package/packages/client/dist/assets/dracula-soft-BXkSAIEj.js +1 -0
  63. package/packages/client/dist/assets/dream-maker-C-nORZOA.js +1 -0
  64. package/packages/client/dist/assets/edge-D5gP-w-T.js +1 -0
  65. package/packages/client/dist/assets/elixir-CLiX3zqd.js +1 -0
  66. package/packages/client/dist/assets/elm-CmHSxxaM.js +1 -0
  67. package/packages/client/dist/assets/emacs-lisp-BX77sIaO.js +1 -0
  68. package/packages/client/dist/assets/erb-BYTLMnw6.js +1 -0
  69. package/packages/client/dist/assets/erlang-B-DoSBHF.js +1 -0
  70. package/packages/client/dist/assets/everforest-dark-BgDCqdQA.js +1 -0
  71. package/packages/client/dist/assets/everforest-light-C8M2exoo.js +1 -0
  72. package/packages/client/dist/assets/fennel-bCA53EVm.js +1 -0
  73. package/packages/client/dist/assets/fish-w-ucz2PV.js +1 -0
  74. package/packages/client/dist/assets/fluent-Dayu4EKP.js +1 -0
  75. package/packages/client/dist/assets/fortran-fixed-form-TqA4NnZg.js +1 -0
  76. package/packages/client/dist/assets/fortran-free-form-DKXYxT9g.js +1 -0
  77. package/packages/client/dist/assets/fsharp-XplgxFYe.js +1 -0
  78. package/packages/client/dist/assets/gdresource-BHYsBjWJ.js +1 -0
  79. package/packages/client/dist/assets/gdscript-DfxzS6Rs.js +1 -0
  80. package/packages/client/dist/assets/gdshader-SKMF96pI.js +1 -0
  81. package/packages/client/dist/assets/genie-ajMbGru0.js +1 -0
  82. package/packages/client/dist/assets/gherkin--30QC5Em.js +1 -0
  83. package/packages/client/dist/assets/git-commit-i4q6IMui.js +1 -0
  84. package/packages/client/dist/assets/git-rebase-B-v9cOL2.js +1 -0
  85. package/packages/client/dist/assets/github-dark-DHJKELXO.js +1 -0
  86. package/packages/client/dist/assets/github-dark-default-Cuk6v7N8.js +1 -0
  87. package/packages/client/dist/assets/github-dark-dimmed-DH5Ifo-i.js +1 -0
  88. package/packages/client/dist/assets/github-dark-high-contrast-E3gJ1_iC.js +1 -0
  89. package/packages/client/dist/assets/github-light-DAi9KRSo.js +1 -0
  90. package/packages/client/dist/assets/github-light-default-D7oLnXFd.js +1 -0
  91. package/packages/client/dist/assets/github-light-high-contrast-BfjtVDDH.js +1 -0
  92. package/packages/client/dist/assets/gleam-B430Bg39.js +1 -0
  93. package/packages/client/dist/assets/glimmer-js-D-cwc0-E.js +1 -0
  94. package/packages/client/dist/assets/glimmer-ts-pgjy16dm.js +1 -0
  95. package/packages/client/dist/assets/glsl-DBO2IWDn.js +1 -0
  96. package/packages/client/dist/assets/gnuplot-CM8KxXT1.js +1 -0
  97. package/packages/client/dist/assets/go-B1SYOhNW.js +1 -0
  98. package/packages/client/dist/assets/graphql-cDcHW_If.js +1 -0
  99. package/packages/client/dist/assets/groovy-DkBy-JyN.js +1 -0
  100. package/packages/client/dist/assets/hack-D1yCygmZ.js +1 -0
  101. package/packages/client/dist/assets/haml-B2EZWmdv.js +1 -0
  102. package/packages/client/dist/assets/handlebars-BQGss363.js +1 -0
  103. package/packages/client/dist/assets/haskell-BILxekzW.js +1 -0
  104. package/packages/client/dist/assets/haxe-C5wWYbrZ.js +1 -0
  105. package/packages/client/dist/assets/hcl-HzYwdGDm.js +1 -0
  106. package/packages/client/dist/assets/hjson-T-Tgc4AT.js +1 -0
  107. package/packages/client/dist/assets/hlsl-ifBTmRxC.js +1 -0
  108. package/packages/client/dist/assets/houston-DnULxvSX.js +1 -0
  109. package/packages/client/dist/assets/html-C2L_23MC.js +1 -0
  110. package/packages/client/dist/assets/html-derivative-CSfWNPLT.js +1 -0
  111. package/packages/client/dist/assets/http-FRrOvY1W.js +1 -0
  112. package/packages/client/dist/assets/hxml-TIA70rKU.js +1 -0
  113. package/packages/client/dist/assets/hy-BMj5Y0dO.js +1 -0
  114. package/packages/client/dist/assets/imba-bv_oIlVt.js +1 -0
  115. package/packages/client/dist/assets/index-Bzql1gnR.js +2 -0
  116. package/packages/client/dist/assets/index-DjQDxRju.css +32 -0
  117. package/packages/client/dist/assets/index-q3VZc6RP.js +1297 -0
  118. package/packages/client/dist/assets/ini-BjABl1g7.js +1 -0
  119. package/packages/client/dist/assets/java-xI-RfyKK.js +1 -0
  120. package/packages/client/dist/assets/javascript-ySlJ1b_l.js +1 -0
  121. package/packages/client/dist/assets/jinja-DGy0s7-h.js +1 -0
  122. package/packages/client/dist/assets/jison-BqZprYcd.js +1 -0
  123. package/packages/client/dist/assets/json-BQoSv7ci.js +1 -0
  124. package/packages/client/dist/assets/json5-w8dY5SsB.js +1 -0
  125. package/packages/client/dist/assets/jsonc-TU54ms6u.js +1 -0
  126. package/packages/client/dist/assets/jsonl-DREVFZK8.js +1 -0
  127. package/packages/client/dist/assets/jsonnet-BfivnA6A.js +1 -0
  128. package/packages/client/dist/assets/jssm-P4WzXJd0.js +1 -0
  129. package/packages/client/dist/assets/jsx-BAng5TT0.js +1 -0
  130. package/packages/client/dist/assets/julia-BBuGR-5E.js +1 -0
  131. package/packages/client/dist/assets/kanagawa-dragon-CkXjmgJE.js +1 -0
  132. package/packages/client/dist/assets/kanagawa-lotus-CfQXZHmo.js +1 -0
  133. package/packages/client/dist/assets/kanagawa-wave-DWedfzmr.js +1 -0
  134. package/packages/client/dist/assets/kotlin-B5lbUyaz.js +1 -0
  135. package/packages/client/dist/assets/kusto-mebxcVVE.js +1 -0
  136. package/packages/client/dist/assets/laserwave-DUszq2jm.js +1 -0
  137. package/packages/client/dist/assets/latex-C-cWTeAZ.js +1 -0
  138. package/packages/client/dist/assets/lean-XBlWyCtg.js +1 -0
  139. package/packages/client/dist/assets/less-BfCpw3nA.js +1 -0
  140. package/packages/client/dist/assets/light-plus-B7mTdjB0.js +1 -0
  141. package/packages/client/dist/assets/liquid-D3W5UaiH.js +1 -0
  142. package/packages/client/dist/assets/log-Cc5clBb7.js +1 -0
  143. package/packages/client/dist/assets/logo-IuBKFhSY.js +1 -0
  144. package/packages/client/dist/assets/lua-CvWAzNxB.js +1 -0
  145. package/packages/client/dist/assets/luau-Du5NY7AG.js +1 -0
  146. package/packages/client/dist/assets/make-Bvotw-X0.js +1 -0
  147. package/packages/client/dist/assets/markdown-UIAJJxZW.js +1 -0
  148. package/packages/client/dist/assets/marko-z0MBrx5-.js +1 -0
  149. package/packages/client/dist/assets/material-theme-D5KoaKCx.js +1 -0
  150. package/packages/client/dist/assets/material-theme-darker-BfHTSMKl.js +1 -0
  151. package/packages/client/dist/assets/material-theme-lighter-B0m2ddpp.js +1 -0
  152. package/packages/client/dist/assets/material-theme-ocean-CyktbL80.js +1 -0
  153. package/packages/client/dist/assets/material-theme-palenight-Csfq5Kiy.js +1 -0
  154. package/packages/client/dist/assets/matlab-D9-PGadD.js +1 -0
  155. package/packages/client/dist/assets/mdc-DB_EDNY_.js +1 -0
  156. package/packages/client/dist/assets/mdx-sdHcTMYB.js +1 -0
  157. package/packages/client/dist/assets/mermaid-Ci6OQyBP.js +1 -0
  158. package/packages/client/dist/assets/min-dark-CafNBF8u.js +1 -0
  159. package/packages/client/dist/assets/min-light-CTRr51gU.js +1 -0
  160. package/packages/client/dist/assets/mipsasm-BC5c_5Pe.js +1 -0
  161. package/packages/client/dist/assets/mojo-Tz6hzZYG.js +1 -0
  162. package/packages/client/dist/assets/monokai-D4h5O-jR.js +1 -0
  163. package/packages/client/dist/assets/move-DB_GagMm.js +1 -0
  164. package/packages/client/dist/assets/narrat-DLbgOhZU.js +1 -0
  165. package/packages/client/dist/assets/nextflow-B0XVJmRM.js +1 -0
  166. package/packages/client/dist/assets/nginx-D_VnBJ67.js +1 -0
  167. package/packages/client/dist/assets/night-owl-C39BiMTA.js +1 -0
  168. package/packages/client/dist/assets/nim-ZlGxZxc3.js +1 -0
  169. package/packages/client/dist/assets/nix-shcSOmrb.js +1 -0
  170. package/packages/client/dist/assets/nord-Ddv68eIx.js +1 -0
  171. package/packages/client/dist/assets/nushell-D4Tzg5kh.js +1 -0
  172. package/packages/client/dist/assets/objective-c-Deuh7S70.js +1 -0
  173. package/packages/client/dist/assets/objective-cpp-BUEGK8hf.js +1 -0
  174. package/packages/client/dist/assets/ocaml-BNioltXt.js +1 -0
  175. package/packages/client/dist/assets/one-dark-pro-GBQ2dnAY.js +1 -0
  176. package/packages/client/dist/assets/one-light-PoHY5YXO.js +1 -0
  177. package/packages/client/dist/assets/pascal-JqZropPD.js +1 -0
  178. package/packages/client/dist/assets/perl-CHQXSrWU.js +1 -0
  179. package/packages/client/dist/assets/php-B5ebYQev.js +1 -0
  180. package/packages/client/dist/assets/plastic-3e1v2bzS.js +1 -0
  181. package/packages/client/dist/assets/plsql-LKU2TuZ1.js +1 -0
  182. package/packages/client/dist/assets/po-BFLt1xDp.js +1 -0
  183. package/packages/client/dist/assets/poimandres-CS3Unz2-.js +1 -0
  184. package/packages/client/dist/assets/polar-DKykz6zU.js +1 -0
  185. package/packages/client/dist/assets/postcss-B3ZDOciz.js +1 -0
  186. package/packages/client/dist/assets/powerquery-CSHBycmS.js +1 -0
  187. package/packages/client/dist/assets/powershell-BIEUsx6d.js +1 -0
  188. package/packages/client/dist/assets/prisma-B48N-Iqd.js +1 -0
  189. package/packages/client/dist/assets/prolog-BY-TUvya.js +1 -0
  190. package/packages/client/dist/assets/proto-zocC4JxJ.js +1 -0
  191. package/packages/client/dist/assets/pug-CM9l7STV.js +1 -0
  192. package/packages/client/dist/assets/puppet-Cza_XSSt.js +1 -0
  193. package/packages/client/dist/assets/purescript-Bg-kzb6g.js +1 -0
  194. package/packages/client/dist/assets/python-DhUJRlN_.js +1 -0
  195. package/packages/client/dist/assets/qml-D8XfuvdV.js +1 -0
  196. package/packages/client/dist/assets/qmldir-C8lEn-DE.js +1 -0
  197. package/packages/client/dist/assets/qss-DhMKtDLN.js +1 -0
  198. package/packages/client/dist/assets/r-CwjWoCRV.js +1 -0
  199. package/packages/client/dist/assets/racket-CzouJOBO.js +1 -0
  200. package/packages/client/dist/assets/raku-B1bQXN8T.js +1 -0
  201. package/packages/client/dist/assets/razor-CNLDkMZG.js +1 -0
  202. package/packages/client/dist/assets/red-bN70gL4F.js +1 -0
  203. package/packages/client/dist/assets/reg-5LuOXUq_.js +1 -0
  204. package/packages/client/dist/assets/regexp-DWJ3fJO_.js +1 -0
  205. package/packages/client/dist/assets/rel-DJlmqQ1C.js +1 -0
  206. package/packages/client/dist/assets/riscv-QhoSD0DR.js +1 -0
  207. package/packages/client/dist/assets/rose-pine-CmCqftbK.js +1 -0
  208. package/packages/client/dist/assets/rose-pine-dawn-Ds-gbosJ.js +1 -0
  209. package/packages/client/dist/assets/rose-pine-moon-CjDtw9vr.js +1 -0
  210. package/packages/client/dist/assets/rst-4NLicBqY.js +1 -0
  211. package/packages/client/dist/assets/ruby-DeZ3UC14.js +1 -0
  212. package/packages/client/dist/assets/rust-Be6lgOlo.js +1 -0
  213. package/packages/client/dist/assets/sas-BmTFh92c.js +1 -0
  214. package/packages/client/dist/assets/sass-BJ4Li9vH.js +1 -0
  215. package/packages/client/dist/assets/scala-DQVVAn-B.js +1 -0
  216. package/packages/client/dist/assets/scheme-BJGe-b2p.js +1 -0
  217. package/packages/client/dist/assets/scss-C31hgJw-.js +1 -0
  218. package/packages/client/dist/assets/sdbl-BLhTXw86.js +1 -0
  219. package/packages/client/dist/assets/shaderlab-B7qAK45m.js +1 -0
  220. package/packages/client/dist/assets/shellscript-atvbtKCR.js +1 -0
  221. package/packages/client/dist/assets/shellsession-C_rIy8kc.js +1 -0
  222. package/packages/client/dist/assets/slack-dark-BthQWCQV.js +1 -0
  223. package/packages/client/dist/assets/slack-ochin-DqwNpetd.js +1 -0
  224. package/packages/client/dist/assets/smalltalk-DkLiglaE.js +1 -0
  225. package/packages/client/dist/assets/snazzy-light-Bw305WKR.js +1 -0
  226. package/packages/client/dist/assets/solarized-dark-DXbdFlpD.js +1 -0
  227. package/packages/client/dist/assets/solarized-light-L9t79GZl.js +1 -0
  228. package/packages/client/dist/assets/solidity-C1w2a3ep.js +1 -0
  229. package/packages/client/dist/assets/soy-C-lX7w71.js +1 -0
  230. package/packages/client/dist/assets/sparql-bYkjHRlG.js +1 -0
  231. package/packages/client/dist/assets/splunk-Cf8iN4DR.js +1 -0
  232. package/packages/client/dist/assets/sql-COK4E0Yg.js +1 -0
  233. package/packages/client/dist/assets/ssh-config-BknIz3MU.js +1 -0
  234. package/packages/client/dist/assets/stata-DorPZHa4.js +1 -0
  235. package/packages/client/dist/assets/stylus-BeQkCIfX.js +1 -0
  236. package/packages/client/dist/assets/svelte-MSaWC3Je.js +1 -0
  237. package/packages/client/dist/assets/swift-BSxZ-RaX.js +1 -0
  238. package/packages/client/dist/assets/synthwave-84-CbfX1IO0.js +1 -0
  239. package/packages/client/dist/assets/system-verilog-C7L56vO4.js +1 -0
  240. package/packages/client/dist/assets/systemd-CUnW07Te.js +1 -0
  241. package/packages/client/dist/assets/talonscript-C1XDQQGZ.js +1 -0
  242. package/packages/client/dist/assets/tasl-CQjiPCtT.js +1 -0
  243. package/packages/client/dist/assets/tcl-DQ1-QYvQ.js +1 -0
  244. package/packages/client/dist/assets/templ-dwX3ZSMB.js +1 -0
  245. package/packages/client/dist/assets/terraform-BbSNqyBO.js +1 -0
  246. package/packages/client/dist/assets/tex-rYs2v40G.js +1 -0
  247. package/packages/client/dist/assets/tokyo-night-DBQeEorK.js +1 -0
  248. package/packages/client/dist/assets/toml-CB2ApiWb.js +1 -0
  249. package/packages/client/dist/assets/ts-tags-CipyTH0X.js +1 -0
  250. package/packages/client/dist/assets/tsv-B_m7g4N7.js +1 -0
  251. package/packages/client/dist/assets/tsx-B6W0miNI.js +1 -0
  252. package/packages/client/dist/assets/turtle-BMR_PYu6.js +1 -0
  253. package/packages/client/dist/assets/twig-NC5TFiHP.js +1 -0
  254. package/packages/client/dist/assets/typescript-Dj6nwHGl.js +1 -0
  255. package/packages/client/dist/assets/typespec-BpWG_bgh.js +1 -0
  256. package/packages/client/dist/assets/typst-BVUVsWT6.js +1 -0
  257. package/packages/client/dist/assets/v-CAQ2eGtk.js +1 -0
  258. package/packages/client/dist/assets/vala-BFOHcciG.js +1 -0
  259. package/packages/client/dist/assets/vb-CdO5JTpU.js +1 -0
  260. package/packages/client/dist/assets/verilog-CJaU5se_.js +1 -0
  261. package/packages/client/dist/assets/vesper-BEBZ7ncR.js +1 -0
  262. package/packages/client/dist/assets/vhdl-DYoNaHQp.js +1 -0
  263. package/packages/client/dist/assets/viml-m4uW47V2.js +1 -0
  264. package/packages/client/dist/assets/vitesse-black-Bkuqu6BP.js +1 -0
  265. package/packages/client/dist/assets/vitesse-dark-D0r3Knsf.js +1 -0
  266. package/packages/client/dist/assets/vitesse-light-CVO1_9PV.js +1 -0
  267. package/packages/client/dist/assets/vue-BuYVFjOK.js +1 -0
  268. package/packages/client/dist/assets/vue-html-xdeiXROB.js +1 -0
  269. package/packages/client/dist/assets/vyper-nyqBNV6O.js +1 -0
  270. package/packages/client/dist/assets/wasm-C6j12Q_x.js +1 -0
  271. package/packages/client/dist/assets/wasm-CG6Dc4jp.js +1 -0
  272. package/packages/client/dist/assets/wenyan-7A4Fjokl.js +1 -0
  273. package/packages/client/dist/assets/wgsl-CB0Krxn9.js +1 -0
  274. package/packages/client/dist/assets/wikitext-DCE3LsBG.js +1 -0
  275. package/packages/client/dist/assets/wolfram-C3FkfJm5.js +1 -0
  276. package/packages/client/dist/assets/xml-e3z08dGr.js +1 -0
  277. package/packages/client/dist/assets/xsl-Dd0NUgwM.js +1 -0
  278. package/packages/client/dist/assets/yaml-CVw76BM1.js +1 -0
  279. package/packages/client/dist/assets/zenscript-HnGAYVZD.js +1 -0
  280. package/packages/client/dist/assets/zig-BVz_zdnA.js +1 -0
  281. package/packages/client/dist/index.html +13 -0
  282. package/packages/server/dist/__tests__/i18n.test.d.ts +5 -0
  283. package/packages/server/dist/__tests__/i18n.test.d.ts.map +1 -0
  284. package/packages/server/dist/__tests__/i18n.test.js +31 -0
  285. package/packages/server/dist/__tests__/i18n.test.js.map +1 -0
  286. package/packages/server/dist/app.d.ts +15 -0
  287. package/packages/server/dist/app.d.ts.map +1 -0
  288. package/packages/server/dist/app.js +120 -0
  289. package/packages/server/dist/app.js.map +1 -0
  290. package/packages/server/dist/cli/passwordSetup.d.ts +5 -0
  291. package/packages/server/dist/cli/passwordSetup.d.ts.map +1 -0
  292. package/packages/server/dist/cli/passwordSetup.js +74 -0
  293. package/packages/server/dist/cli/passwordSetup.js.map +1 -0
  294. package/packages/server/dist/config/index.d.ts +81 -0
  295. package/packages/server/dist/config/index.d.ts.map +1 -0
  296. package/packages/server/dist/config/index.js +107 -0
  297. package/packages/server/dist/config/index.js.map +1 -0
  298. package/packages/server/dist/controllers/__tests__/authController.test.d.ts +6 -0
  299. package/packages/server/dist/controllers/__tests__/authController.test.d.ts.map +1 -0
  300. package/packages/server/dist/controllers/__tests__/authController.test.js +198 -0
  301. package/packages/server/dist/controllers/__tests__/authController.test.js.map +1 -0
  302. package/packages/server/dist/controllers/__tests__/bmadStatusController.test.d.ts +6 -0
  303. package/packages/server/dist/controllers/__tests__/bmadStatusController.test.d.ts.map +1 -0
  304. package/packages/server/dist/controllers/__tests__/bmadStatusController.test.js +121 -0
  305. package/packages/server/dist/controllers/__tests__/bmadStatusController.test.js.map +1 -0
  306. package/packages/server/dist/controllers/__tests__/commandController.test.d.ts +6 -0
  307. package/packages/server/dist/controllers/__tests__/commandController.test.d.ts.map +1 -0
  308. package/packages/server/dist/controllers/__tests__/commandController.test.js +73 -0
  309. package/packages/server/dist/controllers/__tests__/commandController.test.js.map +1 -0
  310. package/packages/server/dist/controllers/__tests__/dashboardController.test.d.ts +6 -0
  311. package/packages/server/dist/controllers/__tests__/dashboardController.test.d.ts.map +1 -0
  312. package/packages/server/dist/controllers/__tests__/dashboardController.test.js +63 -0
  313. package/packages/server/dist/controllers/__tests__/dashboardController.test.js.map +1 -0
  314. package/packages/server/dist/controllers/__tests__/gitController.test.d.ts +6 -0
  315. package/packages/server/dist/controllers/__tests__/gitController.test.d.ts.map +1 -0
  316. package/packages/server/dist/controllers/__tests__/gitController.test.js +585 -0
  317. package/packages/server/dist/controllers/__tests__/gitController.test.js.map +1 -0
  318. package/packages/server/dist/controllers/__tests__/projectController.test.d.ts +6 -0
  319. package/packages/server/dist/controllers/__tests__/projectController.test.d.ts.map +1 -0
  320. package/packages/server/dist/controllers/__tests__/projectController.test.js +175 -0
  321. package/packages/server/dist/controllers/__tests__/projectController.test.js.map +1 -0
  322. package/packages/server/dist/controllers/__tests__/queueController.test.d.ts +6 -0
  323. package/packages/server/dist/controllers/__tests__/queueController.test.d.ts.map +1 -0
  324. package/packages/server/dist/controllers/__tests__/queueController.test.js +115 -0
  325. package/packages/server/dist/controllers/__tests__/queueController.test.js.map +1 -0
  326. package/packages/server/dist/controllers/__tests__/queueTemplateController.test.d.ts +6 -0
  327. package/packages/server/dist/controllers/__tests__/queueTemplateController.test.d.ts.map +1 -0
  328. package/packages/server/dist/controllers/__tests__/queueTemplateController.test.js +167 -0
  329. package/packages/server/dist/controllers/__tests__/queueTemplateController.test.js.map +1 -0
  330. package/packages/server/dist/controllers/__tests__/sessionController.test.d.ts +6 -0
  331. package/packages/server/dist/controllers/__tests__/sessionController.test.d.ts.map +1 -0
  332. package/packages/server/dist/controllers/__tests__/sessionController.test.js +113 -0
  333. package/packages/server/dist/controllers/__tests__/sessionController.test.js.map +1 -0
  334. package/packages/server/dist/controllers/authController.d.ts +30 -0
  335. package/packages/server/dist/controllers/authController.d.ts.map +1 -0
  336. package/packages/server/dist/controllers/authController.js +209 -0
  337. package/packages/server/dist/controllers/authController.js.map +1 -0
  338. package/packages/server/dist/controllers/bmadStatusController.d.ts +5 -0
  339. package/packages/server/dist/controllers/bmadStatusController.d.ts.map +1 -0
  340. package/packages/server/dist/controllers/bmadStatusController.js +53 -0
  341. package/packages/server/dist/controllers/bmadStatusController.js.map +1 -0
  342. package/packages/server/dist/controllers/boardController.d.ts +16 -0
  343. package/packages/server/dist/controllers/boardController.d.ts.map +1 -0
  344. package/packages/server/dist/controllers/boardController.js +295 -0
  345. package/packages/server/dist/controllers/boardController.js.map +1 -0
  346. package/packages/server/dist/controllers/cliController.d.ts +7 -0
  347. package/packages/server/dist/controllers/cliController.d.ts.map +1 -0
  348. package/packages/server/dist/controllers/cliController.js +27 -0
  349. package/packages/server/dist/controllers/cliController.js.map +1 -0
  350. package/packages/server/dist/controllers/commandController.d.ts +14 -0
  351. package/packages/server/dist/controllers/commandController.d.ts.map +1 -0
  352. package/packages/server/dist/controllers/commandController.js +31 -0
  353. package/packages/server/dist/controllers/commandController.js.map +1 -0
  354. package/packages/server/dist/controllers/dashboardController.d.ts +10 -0
  355. package/packages/server/dist/controllers/dashboardController.d.ts.map +1 -0
  356. package/packages/server/dist/controllers/dashboardController.js +23 -0
  357. package/packages/server/dist/controllers/dashboardController.js.map +1 -0
  358. package/packages/server/dist/controllers/fileSystemController.d.ts +54 -0
  359. package/packages/server/dist/controllers/fileSystemController.d.ts.map +1 -0
  360. package/packages/server/dist/controllers/fileSystemController.js +435 -0
  361. package/packages/server/dist/controllers/fileSystemController.js.map +1 -0
  362. package/packages/server/dist/controllers/gitController.d.ts +57 -0
  363. package/packages/server/dist/controllers/gitController.d.ts.map +1 -0
  364. package/packages/server/dist/controllers/gitController.js +494 -0
  365. package/packages/server/dist/controllers/gitController.js.map +1 -0
  366. package/packages/server/dist/controllers/projectController.d.ts +57 -0
  367. package/packages/server/dist/controllers/projectController.d.ts.map +1 -0
  368. package/packages/server/dist/controllers/projectController.js +358 -0
  369. package/packages/server/dist/controllers/projectController.js.map +1 -0
  370. package/packages/server/dist/controllers/queueController.d.ts +15 -0
  371. package/packages/server/dist/controllers/queueController.d.ts.map +1 -0
  372. package/packages/server/dist/controllers/queueController.js +131 -0
  373. package/packages/server/dist/controllers/queueController.js.map +1 -0
  374. package/packages/server/dist/controllers/queueTemplateController.d.ts +15 -0
  375. package/packages/server/dist/controllers/queueTemplateController.d.ts.map +1 -0
  376. package/packages/server/dist/controllers/queueTemplateController.js +187 -0
  377. package/packages/server/dist/controllers/queueTemplateController.js.map +1 -0
  378. package/packages/server/dist/controllers/serverController.d.ts +14 -0
  379. package/packages/server/dist/controllers/serverController.d.ts.map +1 -0
  380. package/packages/server/dist/controllers/serverController.js +164 -0
  381. package/packages/server/dist/controllers/serverController.js.map +1 -0
  382. package/packages/server/dist/controllers/sessionController.d.ts +39 -0
  383. package/packages/server/dist/controllers/sessionController.d.ts.map +1 -0
  384. package/packages/server/dist/controllers/sessionController.js +230 -0
  385. package/packages/server/dist/controllers/sessionController.js.map +1 -0
  386. package/packages/server/dist/dev.d.ts +2 -0
  387. package/packages/server/dist/dev.d.ts.map +1 -0
  388. package/packages/server/dist/dev.js +8 -0
  389. package/packages/server/dist/dev.js.map +1 -0
  390. package/packages/server/dist/handlers/__tests__/websocket.auth.test.d.ts +7 -0
  391. package/packages/server/dist/handlers/__tests__/websocket.auth.test.d.ts.map +1 -0
  392. package/packages/server/dist/handlers/__tests__/websocket.auth.test.js +125 -0
  393. package/packages/server/dist/handlers/__tests__/websocket.auth.test.js.map +1 -0
  394. package/packages/server/dist/handlers/__tests__/websocket.test.d.ts +2 -0
  395. package/packages/server/dist/handlers/__tests__/websocket.test.d.ts.map +1 -0
  396. package/packages/server/dist/handlers/__tests__/websocket.test.js +1316 -0
  397. package/packages/server/dist/handlers/__tests__/websocket.test.js.map +1 -0
  398. package/packages/server/dist/handlers/streamCallbacks.d.ts +52 -0
  399. package/packages/server/dist/handlers/streamCallbacks.d.ts.map +1 -0
  400. package/packages/server/dist/handlers/streamCallbacks.js +107 -0
  401. package/packages/server/dist/handlers/streamCallbacks.js.map +1 -0
  402. package/packages/server/dist/handlers/websocket.d.ts +82 -0
  403. package/packages/server/dist/handlers/websocket.d.ts.map +1 -0
  404. package/packages/server/dist/handlers/websocket.js +1041 -0
  405. package/packages/server/dist/handlers/websocket.js.map +1 -0
  406. package/packages/server/dist/i18n.d.ts +7 -0
  407. package/packages/server/dist/i18n.d.ts.map +1 -0
  408. package/packages/server/dist/i18n.js +32 -0
  409. package/packages/server/dist/i18n.js.map +1 -0
  410. package/packages/server/dist/index.d.ts +2 -0
  411. package/packages/server/dist/index.d.ts.map +1 -0
  412. package/packages/server/dist/index.js +114 -0
  413. package/packages/server/dist/index.js.map +1 -0
  414. package/packages/server/dist/locales/en/server.json +303 -0
  415. package/packages/server/dist/locales/es/server.json +303 -0
  416. package/packages/server/dist/locales/ja/server.json +303 -0
  417. package/packages/server/dist/locales/ko/server.json +303 -0
  418. package/packages/server/dist/locales/pt/server.json +303 -0
  419. package/packages/server/dist/locales/zh-CN/server.json +303 -0
  420. package/packages/server/dist/middleware/__tests__/auth.test.d.ts +6 -0
  421. package/packages/server/dist/middleware/__tests__/auth.test.d.ts.map +1 -0
  422. package/packages/server/dist/middleware/__tests__/auth.test.js +146 -0
  423. package/packages/server/dist/middleware/__tests__/auth.test.js.map +1 -0
  424. package/packages/server/dist/middleware/__tests__/i18n.test.d.ts +5 -0
  425. package/packages/server/dist/middleware/__tests__/i18n.test.d.ts.map +1 -0
  426. package/packages/server/dist/middleware/__tests__/i18n.test.js +96 -0
  427. package/packages/server/dist/middleware/__tests__/i18n.test.js.map +1 -0
  428. package/packages/server/dist/middleware/__tests__/pathGuard.test.d.ts +6 -0
  429. package/packages/server/dist/middleware/__tests__/pathGuard.test.d.ts.map +1 -0
  430. package/packages/server/dist/middleware/__tests__/pathGuard.test.js +73 -0
  431. package/packages/server/dist/middleware/__tests__/pathGuard.test.js.map +1 -0
  432. package/packages/server/dist/middleware/__tests__/session.test.d.ts +6 -0
  433. package/packages/server/dist/middleware/__tests__/session.test.d.ts.map +1 -0
  434. package/packages/server/dist/middleware/__tests__/session.test.js +33 -0
  435. package/packages/server/dist/middleware/__tests__/session.test.js.map +1 -0
  436. package/packages/server/dist/middleware/auth.d.ts +18 -0
  437. package/packages/server/dist/middleware/auth.d.ts.map +1 -0
  438. package/packages/server/dist/middleware/auth.js +52 -0
  439. package/packages/server/dist/middleware/auth.js.map +1 -0
  440. package/packages/server/dist/middleware/i18n.d.ts +17 -0
  441. package/packages/server/dist/middleware/i18n.d.ts.map +1 -0
  442. package/packages/server/dist/middleware/i18n.js +42 -0
  443. package/packages/server/dist/middleware/i18n.js.map +1 -0
  444. package/packages/server/dist/middleware/pathGuard.d.ts +15 -0
  445. package/packages/server/dist/middleware/pathGuard.d.ts.map +1 -0
  446. package/packages/server/dist/middleware/pathGuard.js +41 -0
  447. package/packages/server/dist/middleware/pathGuard.js.map +1 -0
  448. package/packages/server/dist/middleware/session.d.ts +18 -0
  449. package/packages/server/dist/middleware/session.d.ts.map +1 -0
  450. package/packages/server/dist/middleware/session.js +35 -0
  451. package/packages/server/dist/middleware/session.js.map +1 -0
  452. package/packages/server/dist/routes/__tests__/auth.integration.test.d.ts +6 -0
  453. package/packages/server/dist/routes/__tests__/auth.integration.test.d.ts.map +1 -0
  454. package/packages/server/dist/routes/__tests__/auth.integration.test.js +111 -0
  455. package/packages/server/dist/routes/__tests__/auth.integration.test.js.map +1 -0
  456. package/packages/server/dist/routes/__tests__/auth.test.d.ts +6 -0
  457. package/packages/server/dist/routes/__tests__/auth.test.d.ts.map +1 -0
  458. package/packages/server/dist/routes/__tests__/auth.test.js +155 -0
  459. package/packages/server/dist/routes/__tests__/auth.test.js.map +1 -0
  460. package/packages/server/dist/routes/__tests__/board.test.d.ts +2 -0
  461. package/packages/server/dist/routes/__tests__/board.test.d.ts.map +1 -0
  462. package/packages/server/dist/routes/__tests__/board.test.js +201 -0
  463. package/packages/server/dist/routes/__tests__/board.test.js.map +1 -0
  464. package/packages/server/dist/routes/__tests__/cli.test.d.ts +2 -0
  465. package/packages/server/dist/routes/__tests__/cli.test.d.ts.map +1 -0
  466. package/packages/server/dist/routes/__tests__/cli.test.js +121 -0
  467. package/packages/server/dist/routes/__tests__/cli.test.js.map +1 -0
  468. package/packages/server/dist/routes/__tests__/commands.test.d.ts +6 -0
  469. package/packages/server/dist/routes/__tests__/commands.test.d.ts.map +1 -0
  470. package/packages/server/dist/routes/__tests__/commands.test.js +74 -0
  471. package/packages/server/dist/routes/__tests__/commands.test.js.map +1 -0
  472. package/packages/server/dist/routes/__tests__/projects.integration.test.d.ts +9 -0
  473. package/packages/server/dist/routes/__tests__/projects.integration.test.d.ts.map +1 -0
  474. package/packages/server/dist/routes/__tests__/projects.integration.test.js +517 -0
  475. package/packages/server/dist/routes/__tests__/projects.integration.test.js.map +1 -0
  476. package/packages/server/dist/routes/__tests__/projects.test.d.ts +7 -0
  477. package/packages/server/dist/routes/__tests__/projects.test.d.ts.map +1 -0
  478. package/packages/server/dist/routes/__tests__/projects.test.js +361 -0
  479. package/packages/server/dist/routes/__tests__/projects.test.js.map +1 -0
  480. package/packages/server/dist/routes/__tests__/sessions.integration.test.d.ts +8 -0
  481. package/packages/server/dist/routes/__tests__/sessions.integration.test.d.ts.map +1 -0
  482. package/packages/server/dist/routes/__tests__/sessions.integration.test.js +287 -0
  483. package/packages/server/dist/routes/__tests__/sessions.integration.test.js.map +1 -0
  484. package/packages/server/dist/routes/__tests__/sessions.test.d.ts +6 -0
  485. package/packages/server/dist/routes/__tests__/sessions.test.d.ts.map +1 -0
  486. package/packages/server/dist/routes/__tests__/sessions.test.js +261 -0
  487. package/packages/server/dist/routes/__tests__/sessions.test.js.map +1 -0
  488. package/packages/server/dist/routes/auth.d.ts +8 -0
  489. package/packages/server/dist/routes/auth.d.ts.map +1 -0
  490. package/packages/server/dist/routes/auth.js +18 -0
  491. package/packages/server/dist/routes/auth.js.map +1 -0
  492. package/packages/server/dist/routes/bmadStatus.d.ts +3 -0
  493. package/packages/server/dist/routes/bmadStatus.d.ts.map +1 -0
  494. package/packages/server/dist/routes/bmadStatus.js +7 -0
  495. package/packages/server/dist/routes/bmadStatus.js.map +1 -0
  496. package/packages/server/dist/routes/board.d.ts +3 -0
  497. package/packages/server/dist/routes/board.d.ts.map +1 -0
  498. package/packages/server/dist/routes/board.js +16 -0
  499. package/packages/server/dist/routes/board.js.map +1 -0
  500. package/packages/server/dist/routes/cli.d.ts +3 -0
  501. package/packages/server/dist/routes/cli.d.ts.map +1 -0
  502. package/packages/server/dist/routes/cli.js +10 -0
  503. package/packages/server/dist/routes/cli.js.map +1 -0
  504. package/packages/server/dist/routes/commands.d.ts +8 -0
  505. package/packages/server/dist/routes/commands.d.ts.map +1 -0
  506. package/packages/server/dist/routes/commands.js +12 -0
  507. package/packages/server/dist/routes/commands.js.map +1 -0
  508. package/packages/server/dist/routes/dashboard.d.ts +3 -0
  509. package/packages/server/dist/routes/dashboard.d.ts.map +1 -0
  510. package/packages/server/dist/routes/dashboard.js +7 -0
  511. package/packages/server/dist/routes/dashboard.js.map +1 -0
  512. package/packages/server/dist/routes/debug.d.ts +6 -0
  513. package/packages/server/dist/routes/debug.d.ts.map +1 -0
  514. package/packages/server/dist/routes/debug.js +59 -0
  515. package/packages/server/dist/routes/debug.js.map +1 -0
  516. package/packages/server/dist/routes/fileSystem.d.ts +8 -0
  517. package/packages/server/dist/routes/fileSystem.d.ts.map +1 -0
  518. package/packages/server/dist/routes/fileSystem.js +24 -0
  519. package/packages/server/dist/routes/fileSystem.js.map +1 -0
  520. package/packages/server/dist/routes/git.d.ts +8 -0
  521. package/packages/server/dist/routes/git.d.ts.map +1 -0
  522. package/packages/server/dist/routes/git.js +24 -0
  523. package/packages/server/dist/routes/git.js.map +1 -0
  524. package/packages/server/dist/routes/preferences.d.ts +7 -0
  525. package/packages/server/dist/routes/preferences.d.ts.map +1 -0
  526. package/packages/server/dist/routes/preferences.js +112 -0
  527. package/packages/server/dist/routes/preferences.js.map +1 -0
  528. package/packages/server/dist/routes/projects.d.ts +9 -0
  529. package/packages/server/dist/routes/projects.d.ts.map +1 -0
  530. package/packages/server/dist/routes/projects.js +84 -0
  531. package/packages/server/dist/routes/projects.js.map +1 -0
  532. package/packages/server/dist/routes/queue.d.ts +7 -0
  533. package/packages/server/dist/routes/queue.d.ts.map +1 -0
  534. package/packages/server/dist/routes/queue.js +22 -0
  535. package/packages/server/dist/routes/queue.js.map +1 -0
  536. package/packages/server/dist/routes/server.d.ts +3 -0
  537. package/packages/server/dist/routes/server.d.ts.map +1 -0
  538. package/packages/server/dist/routes/server.js +10 -0
  539. package/packages/server/dist/routes/server.js.map +1 -0
  540. package/packages/server/dist/routes/sessions.d.ts +8 -0
  541. package/packages/server/dist/routes/sessions.d.ts.map +1 -0
  542. package/packages/server/dist/routes/sessions.js +47 -0
  543. package/packages/server/dist/routes/sessions.js.map +1 -0
  544. package/packages/server/dist/services/__tests__/authConfigService.test.d.ts +2 -0
  545. package/packages/server/dist/services/__tests__/authConfigService.test.d.ts.map +1 -0
  546. package/packages/server/dist/services/__tests__/authConfigService.test.js +274 -0
  547. package/packages/server/dist/services/__tests__/authConfigService.test.js.map +1 -0
  548. package/packages/server/dist/services/__tests__/bmadStatusService.test.d.ts +6 -0
  549. package/packages/server/dist/services/__tests__/bmadStatusService.test.d.ts.map +1 -0
  550. package/packages/server/dist/services/__tests__/bmadStatusService.test.js +456 -0
  551. package/packages/server/dist/services/__tests__/bmadStatusService.test.js.map +1 -0
  552. package/packages/server/dist/services/__tests__/chatService.integration.test.d.ts +32 -0
  553. package/packages/server/dist/services/__tests__/chatService.integration.test.d.ts.map +1 -0
  554. package/packages/server/dist/services/__tests__/chatService.integration.test.js +97 -0
  555. package/packages/server/dist/services/__tests__/chatService.integration.test.js.map +1 -0
  556. package/packages/server/dist/services/__tests__/chatService.test.d.ts +2 -0
  557. package/packages/server/dist/services/__tests__/chatService.test.d.ts.map +1 -0
  558. package/packages/server/dist/services/__tests__/chatService.test.js +517 -0
  559. package/packages/server/dist/services/__tests__/chatService.test.js.map +1 -0
  560. package/packages/server/dist/services/__tests__/cliService.test.d.ts +2 -0
  561. package/packages/server/dist/services/__tests__/cliService.test.d.ts.map +1 -0
  562. package/packages/server/dist/services/__tests__/cliService.test.js +172 -0
  563. package/packages/server/dist/services/__tests__/cliService.test.js.map +1 -0
  564. package/packages/server/dist/services/__tests__/commandService.test.d.ts +6 -0
  565. package/packages/server/dist/services/__tests__/commandService.test.d.ts.map +1 -0
  566. package/packages/server/dist/services/__tests__/commandService.test.js +525 -0
  567. package/packages/server/dist/services/__tests__/commandService.test.js.map +1 -0
  568. package/packages/server/dist/services/__tests__/dashboardService.test.d.ts +6 -0
  569. package/packages/server/dist/services/__tests__/dashboardService.test.d.ts.map +1 -0
  570. package/packages/server/dist/services/__tests__/dashboardService.test.js +248 -0
  571. package/packages/server/dist/services/__tests__/dashboardService.test.js.map +1 -0
  572. package/packages/server/dist/services/__tests__/fileSystemService.test.d.ts +7 -0
  573. package/packages/server/dist/services/__tests__/fileSystemService.test.d.ts.map +1 -0
  574. package/packages/server/dist/services/__tests__/fileSystemService.test.js +348 -0
  575. package/packages/server/dist/services/__tests__/fileSystemService.test.js.map +1 -0
  576. package/packages/server/dist/services/__tests__/gitService.test.d.ts +6 -0
  577. package/packages/server/dist/services/__tests__/gitService.test.d.ts.map +1 -0
  578. package/packages/server/dist/services/__tests__/gitService.test.js +340 -0
  579. package/packages/server/dist/services/__tests__/gitService.test.js.map +1 -0
  580. package/packages/server/dist/services/__tests__/historyParser.test.d.ts +2 -0
  581. package/packages/server/dist/services/__tests__/historyParser.test.d.ts.map +1 -0
  582. package/packages/server/dist/services/__tests__/historyParser.test.js +471 -0
  583. package/packages/server/dist/services/__tests__/historyParser.test.js.map +1 -0
  584. package/packages/server/dist/services/__tests__/issueService.test.d.ts +2 -0
  585. package/packages/server/dist/services/__tests__/issueService.test.d.ts.map +1 -0
  586. package/packages/server/dist/services/__tests__/issueService.test.js +382 -0
  587. package/packages/server/dist/services/__tests__/issueService.test.js.map +1 -0
  588. package/packages/server/dist/services/__tests__/notificationService.test.d.ts +6 -0
  589. package/packages/server/dist/services/__tests__/notificationService.test.d.ts.map +1 -0
  590. package/packages/server/dist/services/__tests__/notificationService.test.js +131 -0
  591. package/packages/server/dist/services/__tests__/notificationService.test.js.map +1 -0
  592. package/packages/server/dist/services/__tests__/preferencesService.telegram.test.d.ts +6 -0
  593. package/packages/server/dist/services/__tests__/preferencesService.telegram.test.d.ts.map +1 -0
  594. package/packages/server/dist/services/__tests__/preferencesService.telegram.test.js +101 -0
  595. package/packages/server/dist/services/__tests__/preferencesService.telegram.test.js.map +1 -0
  596. package/packages/server/dist/services/__tests__/preferencesService.test.d.ts +6 -0
  597. package/packages/server/dist/services/__tests__/preferencesService.test.d.ts.map +1 -0
  598. package/packages/server/dist/services/__tests__/preferencesService.test.js +68 -0
  599. package/packages/server/dist/services/__tests__/preferencesService.test.js.map +1 -0
  600. package/packages/server/dist/services/__tests__/projectService.settings.test.d.ts +6 -0
  601. package/packages/server/dist/services/__tests__/projectService.settings.test.d.ts.map +1 -0
  602. package/packages/server/dist/services/__tests__/projectService.settings.test.js +101 -0
  603. package/packages/server/dist/services/__tests__/projectService.settings.test.js.map +1 -0
  604. package/packages/server/dist/services/__tests__/projectService.test.d.ts +7 -0
  605. package/packages/server/dist/services/__tests__/projectService.test.d.ts.map +1 -0
  606. package/packages/server/dist/services/__tests__/projectService.test.js +618 -0
  607. package/packages/server/dist/services/__tests__/projectService.test.js.map +1 -0
  608. package/packages/server/dist/services/__tests__/ptyService.test.d.ts +6 -0
  609. package/packages/server/dist/services/__tests__/ptyService.test.d.ts.map +1 -0
  610. package/packages/server/dist/services/__tests__/ptyService.test.js +289 -0
  611. package/packages/server/dist/services/__tests__/ptyService.test.js.map +1 -0
  612. package/packages/server/dist/services/__tests__/queueService.test.d.ts +6 -0
  613. package/packages/server/dist/services/__tests__/queueService.test.d.ts.map +1 -0
  614. package/packages/server/dist/services/__tests__/queueService.test.js +477 -0
  615. package/packages/server/dist/services/__tests__/queueService.test.js.map +1 -0
  616. package/packages/server/dist/services/__tests__/queueTemplateService.test.d.ts +6 -0
  617. package/packages/server/dist/services/__tests__/queueTemplateService.test.d.ts.map +1 -0
  618. package/packages/server/dist/services/__tests__/queueTemplateService.test.js +129 -0
  619. package/packages/server/dist/services/__tests__/queueTemplateService.test.js.map +1 -0
  620. package/packages/server/dist/services/__tests__/rateLimiter.test.d.ts +6 -0
  621. package/packages/server/dist/services/__tests__/rateLimiter.test.d.ts.map +1 -0
  622. package/packages/server/dist/services/__tests__/rateLimiter.test.js +123 -0
  623. package/packages/server/dist/services/__tests__/rateLimiter.test.js.map +1 -0
  624. package/packages/server/dist/services/__tests__/sessionService.test.d.ts +2 -0
  625. package/packages/server/dist/services/__tests__/sessionService.test.d.ts.map +1 -0
  626. package/packages/server/dist/services/__tests__/sessionService.test.js +349 -0
  627. package/packages/server/dist/services/__tests__/sessionService.test.js.map +1 -0
  628. package/packages/server/dist/services/__tests__/streamHandler.test.d.ts +2 -0
  629. package/packages/server/dist/services/__tests__/streamHandler.test.d.ts.map +1 -0
  630. package/packages/server/dist/services/__tests__/streamHandler.test.js +654 -0
  631. package/packages/server/dist/services/__tests__/streamHandler.test.js.map +1 -0
  632. package/packages/server/dist/services/authConfigService.d.ts +56 -0
  633. package/packages/server/dist/services/authConfigService.d.ts.map +1 -0
  634. package/packages/server/dist/services/authConfigService.js +186 -0
  635. package/packages/server/dist/services/authConfigService.js.map +1 -0
  636. package/packages/server/dist/services/bmadStatusService.d.ts +56 -0
  637. package/packages/server/dist/services/bmadStatusService.d.ts.map +1 -0
  638. package/packages/server/dist/services/bmadStatusService.js +438 -0
  639. package/packages/server/dist/services/bmadStatusService.js.map +1 -0
  640. package/packages/server/dist/services/chatService.d.ts +78 -0
  641. package/packages/server/dist/services/chatService.d.ts.map +1 -0
  642. package/packages/server/dist/services/chatService.js +335 -0
  643. package/packages/server/dist/services/chatService.js.map +1 -0
  644. package/packages/server/dist/services/cliService.d.ts +29 -0
  645. package/packages/server/dist/services/cliService.d.ts.map +1 -0
  646. package/packages/server/dist/services/cliService.js +101 -0
  647. package/packages/server/dist/services/cliService.js.map +1 -0
  648. package/packages/server/dist/services/commandService.d.ts +84 -0
  649. package/packages/server/dist/services/commandService.d.ts.map +1 -0
  650. package/packages/server/dist/services/commandService.js +322 -0
  651. package/packages/server/dist/services/commandService.js.map +1 -0
  652. package/packages/server/dist/services/dashboardService.d.ts +23 -0
  653. package/packages/server/dist/services/dashboardService.d.ts.map +1 -0
  654. package/packages/server/dist/services/dashboardService.js +81 -0
  655. package/packages/server/dist/services/dashboardService.js.map +1 -0
  656. package/packages/server/dist/services/fileSystemService.d.ts +105 -0
  657. package/packages/server/dist/services/fileSystemService.d.ts.map +1 -0
  658. package/packages/server/dist/services/fileSystemService.js +498 -0
  659. package/packages/server/dist/services/fileSystemService.js.map +1 -0
  660. package/packages/server/dist/services/gitService.d.ts +25 -0
  661. package/packages/server/dist/services/gitService.d.ts.map +1 -0
  662. package/packages/server/dist/services/gitService.js +230 -0
  663. package/packages/server/dist/services/gitService.js.map +1 -0
  664. package/packages/server/dist/services/historyParser.d.ts +45 -0
  665. package/packages/server/dist/services/historyParser.d.ts.map +1 -0
  666. package/packages/server/dist/services/historyParser.js +315 -0
  667. package/packages/server/dist/services/historyParser.js.map +1 -0
  668. package/packages/server/dist/services/issueService.d.ts +79 -0
  669. package/packages/server/dist/services/issueService.d.ts.map +1 -0
  670. package/packages/server/dist/services/issueService.js +708 -0
  671. package/packages/server/dist/services/issueService.js.map +1 -0
  672. package/packages/server/dist/services/notificationService.d.ts +78 -0
  673. package/packages/server/dist/services/notificationService.d.ts.map +1 -0
  674. package/packages/server/dist/services/notificationService.js +246 -0
  675. package/packages/server/dist/services/notificationService.js.map +1 -0
  676. package/packages/server/dist/services/preferencesService.d.ts +35 -0
  677. package/packages/server/dist/services/preferencesService.d.ts.map +1 -0
  678. package/packages/server/dist/services/preferencesService.js +121 -0
  679. package/packages/server/dist/services/preferencesService.js.map +1 -0
  680. package/packages/server/dist/services/projectService.d.ts +210 -0
  681. package/packages/server/dist/services/projectService.d.ts.map +1 -0
  682. package/packages/server/dist/services/projectService.js +892 -0
  683. package/packages/server/dist/services/projectService.js.map +1 -0
  684. package/packages/server/dist/services/ptyService.d.ts +83 -0
  685. package/packages/server/dist/services/ptyService.d.ts.map +1 -0
  686. package/packages/server/dist/services/ptyService.js +214 -0
  687. package/packages/server/dist/services/ptyService.js.map +1 -0
  688. package/packages/server/dist/services/queueService.d.ts +93 -0
  689. package/packages/server/dist/services/queueService.d.ts.map +1 -0
  690. package/packages/server/dist/services/queueService.js +598 -0
  691. package/packages/server/dist/services/queueService.js.map +1 -0
  692. package/packages/server/dist/services/queueTemplateService.d.ts +12 -0
  693. package/packages/server/dist/services/queueTemplateService.d.ts.map +1 -0
  694. package/packages/server/dist/services/queueTemplateService.js +73 -0
  695. package/packages/server/dist/services/queueTemplateService.js.map +1 -0
  696. package/packages/server/dist/services/rateLimitProbeService.d.ts +56 -0
  697. package/packages/server/dist/services/rateLimitProbeService.d.ts.map +1 -0
  698. package/packages/server/dist/services/rateLimitProbeService.js +227 -0
  699. package/packages/server/dist/services/rateLimitProbeService.js.map +1 -0
  700. package/packages/server/dist/services/rateLimiter.d.ts +32 -0
  701. package/packages/server/dist/services/rateLimiter.d.ts.map +1 -0
  702. package/packages/server/dist/services/rateLimiter.js +60 -0
  703. package/packages/server/dist/services/rateLimiter.js.map +1 -0
  704. package/packages/server/dist/services/sessionService.d.ts +152 -0
  705. package/packages/server/dist/services/sessionService.d.ts.map +1 -0
  706. package/packages/server/dist/services/sessionService.js +672 -0
  707. package/packages/server/dist/services/sessionService.js.map +1 -0
  708. package/packages/server/dist/services/streamHandler.d.ts +145 -0
  709. package/packages/server/dist/services/streamHandler.d.ts.map +1 -0
  710. package/packages/server/dist/services/streamHandler.js +753 -0
  711. package/packages/server/dist/services/streamHandler.js.map +1 -0
  712. package/packages/server/dist/utils/__tests__/networkUtils.test.d.ts +6 -0
  713. package/packages/server/dist/utils/__tests__/networkUtils.test.d.ts.map +1 -0
  714. package/packages/server/dist/utils/__tests__/networkUtils.test.js +106 -0
  715. package/packages/server/dist/utils/__tests__/networkUtils.test.js.map +1 -0
  716. package/packages/server/dist/utils/__tests__/pathUtils.test.d.ts +6 -0
  717. package/packages/server/dist/utils/__tests__/pathUtils.test.d.ts.map +1 -0
  718. package/packages/server/dist/utils/__tests__/pathUtils.test.js +111 -0
  719. package/packages/server/dist/utils/__tests__/pathUtils.test.js.map +1 -0
  720. package/packages/server/dist/utils/errors.d.ts +79 -0
  721. package/packages/server/dist/utils/errors.d.ts.map +1 -0
  722. package/packages/server/dist/utils/errors.js +167 -0
  723. package/packages/server/dist/utils/errors.js.map +1 -0
  724. package/packages/server/dist/utils/logger.d.ts +28 -0
  725. package/packages/server/dist/utils/logger.d.ts.map +1 -0
  726. package/packages/server/dist/utils/logger.js +87 -0
  727. package/packages/server/dist/utils/logger.js.map +1 -0
  728. package/packages/server/dist/utils/networkUtils.d.ts +26 -0
  729. package/packages/server/dist/utils/networkUtils.d.ts.map +1 -0
  730. package/packages/server/dist/utils/networkUtils.js +70 -0
  731. package/packages/server/dist/utils/networkUtils.js.map +1 -0
  732. package/packages/server/dist/utils/pathUtils.d.ts +30 -0
  733. package/packages/server/dist/utils/pathUtils.d.ts.map +1 -0
  734. package/packages/server/dist/utils/pathUtils.js +81 -0
  735. package/packages/server/dist/utils/pathUtils.js.map +1 -0
  736. package/packages/server/package.json +48 -0
  737. package/packages/shared/dist/constants/errorCodes.d.ts +31 -0
  738. package/packages/shared/dist/constants/errorCodes.d.ts.map +1 -0
  739. package/packages/shared/dist/constants/errorCodes.js +33 -0
  740. package/packages/shared/dist/constants/errorCodes.js.map +1 -0
  741. package/packages/shared/dist/index.d.ts +26 -0
  742. package/packages/shared/dist/index.d.ts.map +1 -0
  743. package/packages/shared/dist/index.js +48 -0
  744. package/packages/shared/dist/index.js.map +1 -0
  745. package/packages/shared/dist/types/auth.d.ts +159 -0
  746. package/packages/shared/dist/types/auth.d.ts.map +1 -0
  747. package/packages/shared/dist/types/auth.js +74 -0
  748. package/packages/shared/dist/types/auth.js.map +1 -0
  749. package/packages/shared/dist/types/bmadStatus.d.ts +97 -0
  750. package/packages/shared/dist/types/bmadStatus.d.ts.map +1 -0
  751. package/packages/shared/dist/types/bmadStatus.js +18 -0
  752. package/packages/shared/dist/types/bmadStatus.js.map +1 -0
  753. package/packages/shared/dist/types/board.d.ts +68 -0
  754. package/packages/shared/dist/types/board.d.ts.map +1 -0
  755. package/packages/shared/dist/types/board.js +108 -0
  756. package/packages/shared/dist/types/board.js.map +1 -0
  757. package/packages/shared/dist/types/cli.d.ts +34 -0
  758. package/packages/shared/dist/types/cli.d.ts.map +1 -0
  759. package/packages/shared/dist/types/cli.js +10 -0
  760. package/packages/shared/dist/types/cli.js.map +1 -0
  761. package/packages/shared/dist/types/command.d.ts +39 -0
  762. package/packages/shared/dist/types/command.d.ts.map +1 -0
  763. package/packages/shared/dist/types/command.js +6 -0
  764. package/packages/shared/dist/types/command.js.map +1 -0
  765. package/packages/shared/dist/types/dashboard.d.ts +15 -0
  766. package/packages/shared/dist/types/dashboard.d.ts.map +1 -0
  767. package/packages/shared/dist/types/dashboard.js +3 -0
  768. package/packages/shared/dist/types/dashboard.js.map +1 -0
  769. package/packages/shared/dist/types/fileSystem.d.ts +175 -0
  770. package/packages/shared/dist/types/fileSystem.d.ts.map +1 -0
  771. package/packages/shared/dist/types/fileSystem.js +62 -0
  772. package/packages/shared/dist/types/fileSystem.js.map +1 -0
  773. package/packages/shared/dist/types/git.d.ts +81 -0
  774. package/packages/shared/dist/types/git.d.ts.map +1 -0
  775. package/packages/shared/dist/types/git.js +34 -0
  776. package/packages/shared/dist/types/git.js.map +1 -0
  777. package/packages/shared/dist/types/history.d.ts +103 -0
  778. package/packages/shared/dist/types/history.d.ts.map +1 -0
  779. package/packages/shared/dist/types/history.js +6 -0
  780. package/packages/shared/dist/types/history.js.map +1 -0
  781. package/packages/shared/dist/types/logger.d.ts +18 -0
  782. package/packages/shared/dist/types/logger.d.ts.map +1 -0
  783. package/packages/shared/dist/types/logger.js +29 -0
  784. package/packages/shared/dist/types/logger.js.map +1 -0
  785. package/packages/shared/dist/types/message.d.ts +34 -0
  786. package/packages/shared/dist/types/message.d.ts.map +1 -0
  787. package/packages/shared/dist/types/message.js +14 -0
  788. package/packages/shared/dist/types/message.js.map +1 -0
  789. package/packages/shared/dist/types/preferences.d.ts +95 -0
  790. package/packages/shared/dist/types/preferences.d.ts.map +1 -0
  791. package/packages/shared/dist/types/preferences.js +15 -0
  792. package/packages/shared/dist/types/preferences.js.map +1 -0
  793. package/packages/shared/dist/types/project.d.ts +205 -0
  794. package/packages/shared/dist/types/project.d.ts.map +1 -0
  795. package/packages/shared/dist/types/project.js +64 -0
  796. package/packages/shared/dist/types/project.js.map +1 -0
  797. package/packages/shared/dist/types/queue.d.ts +94 -0
  798. package/packages/shared/dist/types/queue.d.ts.map +1 -0
  799. package/packages/shared/dist/types/queue.js +2 -0
  800. package/packages/shared/dist/types/queue.js.map +1 -0
  801. package/packages/shared/dist/types/sdk.d.ts +156 -0
  802. package/packages/shared/dist/types/sdk.d.ts.map +1 -0
  803. package/packages/shared/dist/types/sdk.js +13 -0
  804. package/packages/shared/dist/types/sdk.js.map +1 -0
  805. package/packages/shared/dist/types/session.d.ts +149 -0
  806. package/packages/shared/dist/types/session.d.ts.map +1 -0
  807. package/packages/shared/dist/types/session.js +51 -0
  808. package/packages/shared/dist/types/session.js.map +1 -0
  809. package/packages/shared/dist/types/streaming.d.ts +269 -0
  810. package/packages/shared/dist/types/streaming.d.ts.map +1 -0
  811. package/packages/shared/dist/types/streaming.js +43 -0
  812. package/packages/shared/dist/types/streaming.js.map +1 -0
  813. package/packages/shared/dist/types/terminal.d.ts +93 -0
  814. package/packages/shared/dist/types/terminal.d.ts.map +1 -0
  815. package/packages/shared/dist/types/terminal.js +38 -0
  816. package/packages/shared/dist/types/terminal.js.map +1 -0
  817. package/packages/shared/dist/types/websocket.d.ts +196 -0
  818. package/packages/shared/dist/types/websocket.d.ts.map +1 -0
  819. package/packages/shared/dist/types/websocket.js +7 -0
  820. package/packages/shared/dist/types/websocket.js.map +1 -0
  821. package/packages/shared/dist/utils/__tests__/queueParser.test.d.ts +2 -0
  822. package/packages/shared/dist/utils/__tests__/queueParser.test.d.ts.map +1 -0
  823. package/packages/shared/dist/utils/__tests__/queueParser.test.js +246 -0
  824. package/packages/shared/dist/utils/__tests__/queueParser.test.js.map +1 -0
  825. package/packages/shared/dist/utils/__tests__/queueTemplateUtils.test.d.ts +6 -0
  826. package/packages/shared/dist/utils/__tests__/queueTemplateUtils.test.d.ts.map +1 -0
  827. package/packages/shared/dist/utils/__tests__/queueTemplateUtils.test.js +138 -0
  828. package/packages/shared/dist/utils/__tests__/queueTemplateUtils.test.js.map +1 -0
  829. package/packages/shared/dist/utils/queueParser.d.ts +3 -0
  830. package/packages/shared/dist/utils/queueParser.d.ts.map +1 -0
  831. package/packages/shared/dist/utils/queueParser.js +165 -0
  832. package/packages/shared/dist/utils/queueParser.js.map +1 -0
  833. package/packages/shared/dist/utils/queueTemplateUtils.d.ts +17 -0
  834. package/packages/shared/dist/utils/queueTemplateUtils.d.ts.map +1 -0
  835. package/packages/shared/dist/utils/queueTemplateUtils.js +65 -0
  836. package/packages/shared/dist/utils/queueTemplateUtils.js.map +1 -0
  837. package/packages/shared/package.json +23 -0
  838. package/scripts/postinstall.cjs +23 -0
@@ -0,0 +1,1041 @@
1
+ /**
2
+ * WebSocket Handler for Socket.io
3
+ * Story 1.4: WebSocket Server Setup
4
+ * Story 1.5: Chat event handler with streaming
5
+ * Story 4.6: Timeout handling and error management
6
+ */
7
+ import { Server as SocketIOServer } from 'socket.io';
8
+ import { existsSync } from 'fs';
9
+ import { ERROR_CODES, IMAGE_CONSTRAINTS, parseQueueScript, TERMINAL_ERRORS } from '@hammoc/shared';
10
+ import i18next from '../i18n.js';
11
+ import { SUPPORTED_LANGUAGES } from '@hammoc/shared';
12
+ import { isLocalIP, extractClientIP } from '../utils/networkUtils.js';
13
+ import { ChatService } from '../services/chatService.js';
14
+ import { SessionService } from '../services/sessionService.js';
15
+ import { parseSDKError, AbortedError } from '../utils/errors.js';
16
+ import { createSessionMiddleware } from '../middleware/session.js';
17
+ import { config } from '../config/index.js';
18
+ import { notificationService, formatAskQuestionPrompt } from '../services/notificationService.js';
19
+ import { preferencesService } from '../services/preferencesService.js';
20
+ import { getOrCreateQueueService, getQueueInstances } from '../controllers/queueController.js';
21
+ import { createLogger } from '../utils/logger.js';
22
+ import { buildStreamCallbacks } from './streamCallbacks.js';
23
+ import { rateLimitProbeService } from '../services/rateLimitProbeService.js';
24
+ import { ptyService } from '../services/ptyService.js';
25
+ import { projectService } from '../services/projectService.js';
26
+ import { dashboardService } from '../services/dashboardService.js';
27
+ const log = createLogger('websocket');
28
+ // Alias for concise usage in guards
29
+ const queueInstances = getQueueInstances;
30
+ // Socket-to-terminal mapping: socket.id → Set of terminalIds (Story 17.1)
31
+ const socketTerminals = new Map();
32
+ // Story 20.1: Session-to-project mapping for dashboard triggers
33
+ const sessionProjectMap = new Map();
34
+ // Story 20.1: Per-project debounced dashboard status change broadcaster
35
+ const dashboardDebounceTimers = new Map();
36
+ function triggerDashboardStatusChange(projectSlug) {
37
+ const existing = dashboardDebounceTimers.get(projectSlug);
38
+ if (existing)
39
+ clearTimeout(existing);
40
+ dashboardDebounceTimers.set(projectSlug, setTimeout(async () => {
41
+ dashboardDebounceTimers.delete(projectSlug);
42
+ try {
43
+ const status = await dashboardService.getProjectStatus(projectSlug);
44
+ io.to('dashboard').emit('dashboard:status-change', { projectSlug, status });
45
+ }
46
+ catch (err) {
47
+ log.error(`Failed to broadcast dashboard status for ${projectSlug}:`, err);
48
+ }
49
+ }, 300));
50
+ }
51
+ /**
52
+ * Check terminal access for a socket connection.
53
+ * Fail-closed: denies access on any error (e.g., preferences file read failure).
54
+ * Story 17.5: Terminal Security
55
+ */
56
+ async function checkTerminalAccess(socket, lang) {
57
+ const t = i18next.getFixedT(lang);
58
+ try {
59
+ const terminalEnabled = await preferencesService.getTerminalEnabled();
60
+ if (!terminalEnabled) {
61
+ return {
62
+ allowed: false,
63
+ error: {
64
+ code: TERMINAL_ERRORS.TERMINAL_DISABLED.code,
65
+ message: t('ws.error.terminalDisabled'),
66
+ },
67
+ };
68
+ }
69
+ }
70
+ catch (err) {
71
+ // Fail-closed: deny access if preferences read fails
72
+ log.error('Failed to check terminal enabled state, denying access:', err);
73
+ return {
74
+ allowed: false,
75
+ error: {
76
+ code: TERMINAL_ERRORS.TERMINAL_DISABLED.code,
77
+ message: t('ws.error.terminalDisabled'),
78
+ },
79
+ };
80
+ }
81
+ const clientIP = extractClientIP(socket);
82
+ if (!isLocalIP(clientIP)) {
83
+ return {
84
+ allowed: false,
85
+ error: {
86
+ code: TERMINAL_ERRORS.TERMINAL_ACCESS_DENIED.code,
87
+ message: t('ws.error.terminalAccessDenied'),
88
+ },
89
+ };
90
+ }
91
+ return { allowed: true };
92
+ }
93
+ let io;
94
+ let connectedClients = 0;
95
+ // Primary maps: sessionId → ActiveStream, socketId → sessionId
96
+ const activeStreams = new Map();
97
+ const socketToSession = new Map();
98
+ let permissionRequestCounter = 0;
99
+ /** Create a buffered emit function that buffers and broadcasts to all connected sockets */
100
+ function createStreamEmit(stream) {
101
+ return (event, data) => {
102
+ stream.buffer.push({ event, data });
103
+ for (const sock of stream.sockets) {
104
+ if (sock.connected) {
105
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
106
+ sock.emit(event, data);
107
+ }
108
+ }
109
+ };
110
+ }
111
+ /** Get session IDs of all currently running active streams */
112
+ export function getActiveStreamSessionIds() {
113
+ return [...activeStreams.entries()]
114
+ .filter(([, stream]) => stream.status === 'running')
115
+ .map(([key]) => key);
116
+ }
117
+ /** Get active (running) session counts grouped by project slug (in-memory, no I/O) */
118
+ export function getActiveSessionCountsByProject() {
119
+ const counts = new Map();
120
+ for (const [sessionId, stream] of activeStreams.entries()) {
121
+ if (stream.status !== 'running')
122
+ continue;
123
+ const slug = sessionProjectMap.get(sessionId);
124
+ if (slug) {
125
+ counts.set(slug, (counts.get(slug) ?? 0) + 1);
126
+ }
127
+ }
128
+ return counts;
129
+ }
130
+ /** Check if a specific session has a running active stream */
131
+ export function isSessionStreaming(sessionId) {
132
+ const stream = activeStreams.get(sessionId);
133
+ return !!stream && stream.status === 'running';
134
+ }
135
+ /** Clean up a stream from all maps */
136
+ function cleanupStream(streamKey) {
137
+ activeStreams.delete(streamKey);
138
+ for (const [sockId, sessId] of socketToSession.entries()) {
139
+ if (sessId === streamKey)
140
+ socketToSession.delete(sockId);
141
+ }
142
+ }
143
+ /**
144
+ * Create a headless ActiveStream (no attached socket) for queue execution.
145
+ * Returns a buffered emit function and a broadcast function for project room delivery.
146
+ * The stream is registered in activeStreams so session:join/reconnect works.
147
+ */
148
+ export function createHeadlessStream(sessionId, abortController, projectSlug) {
149
+ // Collect sockets from session room (same as chat:send handler)
150
+ const sockets = new Set();
151
+ if (io) {
152
+ const roomSockets = io.sockets.adapter.rooms.get(`session:${sessionId}`);
153
+ if (roomSockets) {
154
+ for (const socketId of roomSockets) {
155
+ const sock = io.sockets.sockets.get(socketId);
156
+ if (sock)
157
+ sockets.add(sock);
158
+ }
159
+ }
160
+ }
161
+ const stream = {
162
+ sessionId,
163
+ sockets,
164
+ abortController,
165
+ buffer: [],
166
+ pendingPermissions: new Map(),
167
+ status: 'running',
168
+ startedAt: Date.now(),
169
+ };
170
+ activeStreams.set(sessionId, stream);
171
+ if (projectSlug) {
172
+ sessionProjectMap.set(sessionId, projectSlug);
173
+ }
174
+ for (const sock of sockets) {
175
+ socketToSession.set(sock.id, sessionId);
176
+ }
177
+ const emit = createStreamEmit(stream);
178
+ return { stream, emit };
179
+ }
180
+ /**
181
+ * Re-key a stream when SDK assigns a different sessionId than the initial key.
182
+ * Updates activeStreams map and socketToSession references.
183
+ */
184
+ export function rekeyStream(stream, newSessionId) {
185
+ if (stream.sessionId === newSessionId)
186
+ return;
187
+ const oldSessionId = stream.sessionId;
188
+ const projectSlug = sessionProjectMap.get(oldSessionId);
189
+ activeStreams.delete(oldSessionId);
190
+ stream.sessionId = newSessionId;
191
+ activeStreams.set(newSessionId, stream);
192
+ if (projectSlug) {
193
+ sessionProjectMap.delete(oldSessionId);
194
+ sessionProjectMap.set(newSessionId, projectSlug);
195
+ }
196
+ for (const sock of stream.sockets) {
197
+ socketToSession.set(sock.id, newSessionId);
198
+ sock.leave(`session:${oldSessionId}`);
199
+ sock.join(`session:${newSessionId}`);
200
+ }
201
+ }
202
+ /**
203
+ * Mark a stream as completed and broadcast stream-change.
204
+ * Cleans up from activeStreams map.
205
+ */
206
+ export function finalizeStream(sessionId) {
207
+ const stream = activeStreams.get(sessionId);
208
+ if (stream) {
209
+ stream.status = 'completed';
210
+ cleanupStream(sessionId);
211
+ }
212
+ io.emit('session:stream-change', { sessionId, active: false });
213
+ const slug = sessionProjectMap.get(sessionId);
214
+ if (slug) {
215
+ sessionProjectMap.delete(sessionId);
216
+ triggerDashboardStatusChange(slug);
217
+ }
218
+ }
219
+ /**
220
+ * Broadcast session:stream-change to all connected clients.
221
+ * Used by queue service to signal stream start/end.
222
+ * Story 20.1: Also triggers dashboard status change when projectSlug is known.
223
+ */
224
+ export function broadcastStreamChange(sessionId, active) {
225
+ io.emit('session:stream-change', { sessionId, active });
226
+ const slug = sessionProjectMap.get(sessionId);
227
+ if (slug) {
228
+ triggerDashboardStatusChange(slug);
229
+ if (!active)
230
+ sessionProjectMap.delete(sessionId);
231
+ }
232
+ }
233
+ /**
234
+ * Match Accept-Language header against SUPPORTED_LANGUAGES.
235
+ * Returns the first matching language code or null.
236
+ */
237
+ function matchAcceptLanguage(header) {
238
+ if (!header)
239
+ return null;
240
+ const parts = header.split(',').map(part => {
241
+ const [lang, qStr] = part.trim().split(';q=');
242
+ return { lang: lang.trim().split('-')[0].toLowerCase(), q: qStr ? parseFloat(qStr) : 1.0 };
243
+ }).sort((a, b) => b.q - a.q);
244
+ for (const { lang } of parts) {
245
+ if (SUPPORTED_LANGUAGES.includes(lang)) {
246
+ return lang;
247
+ }
248
+ }
249
+ return null;
250
+ }
251
+ /**
252
+ * Initialize Socket.io server with the HTTP server
253
+ * @param httpServer - HTTP server instance from Express
254
+ * @returns Socket.io server instance
255
+ * [Source: Story 2.5 - Task 4]
256
+ */
257
+ export async function initializeWebSocket(httpServer) {
258
+ io = new SocketIOServer(httpServer, {
259
+ cors: config.websocket.cors,
260
+ maxHttpBufferSize: 100 * 1024 * 1024, // 100MB for base64 image payloads
261
+ });
262
+ // Session middleware for WebSocket (Story 2.5 - Task 4)
263
+ const sessionMiddleware = await createSessionMiddleware();
264
+ // Parse session from cookie for Socket.io connections
265
+ io.use((socket, next) => {
266
+ // Express middleware adapter for Socket.io
267
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
268
+ sessionMiddleware(socket.request, {}, next);
269
+ });
270
+ // Authentication middleware (Story 2.5 - Task 4)
271
+ io.use((socket, next) => {
272
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
273
+ const session = socket.request.session;
274
+ if (session?.authenticated) {
275
+ return next();
276
+ }
277
+ return next(new Error('Unauthorized'));
278
+ });
279
+ io.on('connection', (socket) => {
280
+ connectedClients++;
281
+ log.info(`Client connected. Total: ${connectedClients}`);
282
+ // Set default language synchronously; resolved asynchronously below.
283
+ // Handlers read socket.data.language at invocation time (not capture time).
284
+ socket.data.language = 'en';
285
+ // Resolve language preference and send terminal:access asynchronously.
286
+ // Event listeners are registered synchronously below to prevent race conditions
287
+ // where early client emits could be dropped during async preference loading.
288
+ (async () => {
289
+ try {
290
+ const prefs = await preferencesService.readPreferences();
291
+ const prefLang = prefs.language && SUPPORTED_LANGUAGES.includes(prefs.language)
292
+ ? prefs.language : null;
293
+ const headerLang = matchAcceptLanguage(socket.request?.headers?.['accept-language']);
294
+ socket.data.language = prefLang || headerLang || 'en';
295
+ }
296
+ catch {
297
+ // Keep default 'en'
298
+ }
299
+ // Story 17.5: Send terminal access info on connection (after language resolved)
300
+ const lang = socket.data.language || 'en';
301
+ const t = i18next.getFixedT(lang);
302
+ try {
303
+ const clientIP = extractClientIP(socket);
304
+ const isLocal = isLocalIP(clientIP);
305
+ const terminalEnabled = await preferencesService.getTerminalEnabled();
306
+ socket.emit('terminal:access', {
307
+ allowed: terminalEnabled && isLocal,
308
+ enabled: terminalEnabled,
309
+ reason: !terminalEnabled
310
+ ? t('ws.error.terminalDisabled')
311
+ : !isLocal
312
+ ? t('ws.error.terminalAccessDenied')
313
+ : undefined,
314
+ });
315
+ }
316
+ catch (err) {
317
+ log.error('Failed to send terminal:access event:', err);
318
+ // Fail-closed: report as disabled
319
+ socket.emit('terminal:access', {
320
+ allowed: false,
321
+ enabled: false,
322
+ reason: t('ws.error.terminalDisabled'),
323
+ });
324
+ }
325
+ })();
326
+ // Start rate limit polling on first client connection
327
+ if (connectedClients === 1) {
328
+ rateLimitProbeService.startPolling((data) => { io.emit('rateLimit:update', data); }, (data) => { io.emit('apiHealth:update', data); });
329
+ }
330
+ // Send cached rate limit data immediately to newly connected client
331
+ const cachedRateLimit = rateLimitProbeService.getCachedResult();
332
+ if (cachedRateLimit) {
333
+ socket.emit('rateLimit:update', cachedRateLimit);
334
+ }
335
+ // Send cached API health status to newly connected client
336
+ const cachedHealth = rateLimitProbeService.getApiHealth();
337
+ if (cachedHealth) {
338
+ socket.emit('apiHealth:update', cachedHealth);
339
+ }
340
+ // Handle chat:send event — background streaming with reconnect support
341
+ socket.on('chat:send', async (data) => {
342
+ const lang = socket.data.language || 'en';
343
+ const t = i18next.getFixedT(lang);
344
+ // Reject if queue has locked this session (server-side enforcement)
345
+ if (data.sessionId) {
346
+ for (const [, qs] of queueInstances()) {
347
+ if (qs.lockedSessionId === data.sessionId) {
348
+ socket.emit('error', {
349
+ code: ERROR_CODES.CHAT_ERROR,
350
+ message: t('ws.error.queueSessionLocked'),
351
+ });
352
+ return;
353
+ }
354
+ }
355
+ }
356
+ const abortController = new AbortController();
357
+ const streamKey = data.sessionId || `pending-${socket.id}`;
358
+ // Abort existing active stream for same session — notify all watchers
359
+ const existingStream = activeStreams.get(streamKey);
360
+ if (existingStream && existingStream.status === 'running') {
361
+ for (const sock of existingStream.sockets) {
362
+ if (sock.id !== socket.id) {
363
+ sock.emit('stream:detached', { sessionId: streamKey, reason: 'another-client' });
364
+ socketToSession.delete(sock.id);
365
+ }
366
+ }
367
+ existingStream.abortController.abort('another-client');
368
+ }
369
+ // Collect all sockets viewing this session (from persistent session room)
370
+ const initialSockets = new Set([socket]);
371
+ const roomSockets = io.sockets.adapter.rooms.get(`session:${streamKey}`);
372
+ if (roomSockets) {
373
+ for (const socketId of roomSockets) {
374
+ const roomSocket = io.sockets.sockets.get(socketId);
375
+ if (roomSocket && roomSocket.id !== socket.id) {
376
+ initialSockets.add(roomSocket);
377
+ }
378
+ }
379
+ }
380
+ const stream = {
381
+ sessionId: streamKey,
382
+ sockets: initialSockets,
383
+ abortController,
384
+ buffer: [],
385
+ pendingPermissions: new Map(),
386
+ status: 'running',
387
+ startedAt: Date.now(),
388
+ };
389
+ activeStreams.set(streamKey, stream);
390
+ for (const sock of initialSockets) {
391
+ socketToSession.set(sock.id, streamKey);
392
+ }
393
+ // Story 20.1: Populate session→project mapping for dashboard triggers
394
+ // Use stream.sessionId (mutable) instead of captured streamKey to handle
395
+ // race condition where rekeyStream() may have already changed the session ID
396
+ projectService.findProjectByPath(data.workingDirectory).then((project) => {
397
+ if (project && stream.status === 'running') {
398
+ sessionProjectMap.set(stream.sessionId, project.projectSlug);
399
+ triggerDashboardStatusChange(project.projectSlug);
400
+ }
401
+ }).catch(() => { });
402
+ try {
403
+ await handleChatSend(stream, data, abortController, lang);
404
+ }
405
+ finally {
406
+ stream.status = 'completed';
407
+ const endedSessionId = stream.sessionId;
408
+ // Only cleanup if this stream is still the active one for this session.
409
+ // A replacement stream (from another chat:send) may have already taken over
410
+ // the same key — deleting it would be a race condition.
411
+ if (activeStreams.get(endedSessionId) === stream) {
412
+ cleanupStream(endedSessionId);
413
+ io.emit('session:stream-change', { sessionId: endedSessionId, active: false });
414
+ // Story 20.1: Trigger dashboard status change on stream end
415
+ const endProjectSlug = sessionProjectMap.get(endedSessionId);
416
+ if (endProjectSlug) {
417
+ triggerDashboardStatusChange(endProjectSlug);
418
+ sessionProjectMap.delete(endedSessionId);
419
+ }
420
+ }
421
+ }
422
+ });
423
+ // Handle permission:respond event — route to ActiveStream (covers both normal chat and queue)
424
+ socket.on('permission:respond', (data) => {
425
+ const sessionId = socketToSession.get(socket.id);
426
+ if (!sessionId)
427
+ return;
428
+ const stream = activeStreams.get(sessionId);
429
+ if (stream?.pendingPermissions.has(data.requestId)) {
430
+ stream.pendingPermissions.get(data.requestId).resolve({ approved: data.approved, response: data.response });
431
+ stream.pendingPermissions.delete(data.requestId);
432
+ // Broadcast the actual resolution to all OTHER viewers so their
433
+ // tool/interactive cards can show the correct approve/deny state
434
+ for (const sock of stream.sockets) {
435
+ if (sock.id !== socket.id) {
436
+ sock.emit('permission:resolved', {
437
+ requestId: data.requestId,
438
+ approved: data.approved,
439
+ interactionType: data.interactionType,
440
+ response: data.response,
441
+ });
442
+ }
443
+ }
444
+ }
445
+ else {
446
+ // Permission already resolved by another viewer — notify sender
447
+ socket.emit('permission:already-resolved', { requestId: data.requestId });
448
+ }
449
+ });
450
+ // Handle chat:abort event — find stream and abort, notify all viewers
451
+ socket.on('chat:abort', () => {
452
+ const sessionId = socketToSession.get(socket.id);
453
+ if (!sessionId)
454
+ return;
455
+ const stream = activeStreams.get(sessionId);
456
+ if (stream && stream.status === 'running') {
457
+ // Emit abort notification to all connected viewers BEFORE triggering abort.
458
+ // This ensures passive viewers (who didn't initiate the abort) also
459
+ // receive a completion signal, preventing them from being stuck in isStreaming=true.
460
+ for (const sock of stream.sockets) {
461
+ sock.emit('stream:detached', { sessionId, reason: 'user-abort' });
462
+ }
463
+ stream.abortController.abort('user-abort');
464
+ }
465
+ });
466
+ // Handle permission:mode-change — update SDK permission mode during active stream
467
+ socket.on('permission:mode-change', async (data) => {
468
+ const sessionId = socketToSession.get(socket.id);
469
+ if (!sessionId)
470
+ return;
471
+ const stream = activeStreams.get(sessionId);
472
+ if (!stream?.chatService || stream.status !== 'running')
473
+ return;
474
+ try {
475
+ await stream.chatService.setPermissionMode(data.mode);
476
+ log.debug(`Permission mode changed to "${data.mode}" for session ${sessionId}`);
477
+ // Broadcast to other viewers so their UI stays in sync
478
+ for (const sock of stream.sockets) {
479
+ if (sock.id !== socket.id) {
480
+ sock.emit('permission:mode-change', { mode: data.mode });
481
+ }
482
+ }
483
+ }
484
+ catch (err) {
485
+ log.error('Failed to change permission mode:', err);
486
+ }
487
+ });
488
+ // Handle session:join event — attach socket to active running stream (broadcast)
489
+ // Also joins a persistent Socket.io room so future streams auto-include this socket.
490
+ socket.on('session:join', (sessionId) => {
491
+ // Detach this socket from any previously-attached stream to prevent
492
+ // events from the old stream leaking to a different session's listeners
493
+ const prevSessionId = socketToSession.get(socket.id);
494
+ if (prevSessionId && prevSessionId !== sessionId) {
495
+ const prevStream = activeStreams.get(prevSessionId);
496
+ if (prevStream) {
497
+ prevStream.sockets.delete(socket);
498
+ }
499
+ socketToSession.delete(socket.id);
500
+ socket.leave(`session:${prevSessionId}`);
501
+ }
502
+ // Join persistent session room (survives beyond ActiveStream lifecycle)
503
+ socket.join(`session:${sessionId}`);
504
+ const stream = activeStreams.get(sessionId);
505
+ if (!stream || stream.status !== 'running') {
506
+ socket.emit('stream:status', { active: false, sessionId });
507
+ return;
508
+ }
509
+ // Add socket to broadcast set (multiple browsers can watch simultaneously)
510
+ stream.sockets.add(socket);
511
+ socketToSession.set(socket.id, sessionId);
512
+ // Notify this client that stream is active, then replay entire buffer
513
+ socket.emit('stream:status', { active: true, sessionId });
514
+ for (const entry of stream.buffer) {
515
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
516
+ socket.emit(entry.event, entry.data);
517
+ }
518
+ });
519
+ // Handle session:leave event — detach socket from current stream and session room
520
+ // (client navigating away from a session while streaming continues in background)
521
+ socket.on('session:leave', (sessionId) => {
522
+ const prevSessionId = socketToSession.get(socket.id);
523
+ if (prevSessionId) {
524
+ const prevStream = activeStreams.get(prevSessionId);
525
+ if (prevStream) {
526
+ prevStream.sockets.delete(socket);
527
+ }
528
+ socketToSession.delete(socket.id);
529
+ }
530
+ // Use prevSessionId as fallback — client may send empty string when
531
+ // the sessionId is not available at unmount time (e.g., ChatPage cleanup)
532
+ const roomSessionId = sessionId || prevSessionId;
533
+ if (roomSessionId) {
534
+ socket.leave(`session:${roomSessionId}`);
535
+ }
536
+ });
537
+ // Story 20.1: Dashboard subscribe/unsubscribe
538
+ socket.on('dashboard:subscribe', () => {
539
+ socket.join('dashboard');
540
+ });
541
+ socket.on('dashboard:unsubscribe', () => {
542
+ socket.leave('dashboard');
543
+ });
544
+ // Handle project:join/leave — room for queue event delivery (Story 15.2)
545
+ socket.on('project:join', (projectSlug) => {
546
+ socket.join(`project:${projectSlug}`);
547
+ });
548
+ socket.on('project:leave', (projectSlug) => {
549
+ socket.leave(`project:${projectSlug}`);
550
+ });
551
+ // Handle queue events via WebSocket (Story 15.2)
552
+ socket.on('queue:start', async (data) => {
553
+ const { items, sessionId, projectSlug, permissionMode } = data;
554
+ socket.join(`project:${projectSlug}`);
555
+ const queueService = getOrCreateQueueService(projectSlug);
556
+ if (queueService.isRunning) {
557
+ const t = i18next.getFixedT(socket.data.language || 'en');
558
+ socket.emit('error', { code: 'QUEUE_ALREADY_RUNNING', message: t('ws.error.queueAlreadyRunning') });
559
+ return;
560
+ }
561
+ queueService.start(items, projectSlug, sessionId, permissionMode).catch((err) => {
562
+ log.error('Queue execution error:', err);
563
+ });
564
+ triggerDashboardStatusChange(projectSlug);
565
+ });
566
+ socket.on('queue:pause', (data) => {
567
+ const qs = getOrCreateQueueService(data.projectSlug);
568
+ if (qs.isRunning)
569
+ qs.pause();
570
+ triggerDashboardStatusChange(data.projectSlug);
571
+ });
572
+ socket.on('queue:resume', (data) => {
573
+ const qs = getOrCreateQueueService(data.projectSlug);
574
+ if (qs.isRunning)
575
+ qs.resume().catch(() => { });
576
+ triggerDashboardStatusChange(data.projectSlug);
577
+ });
578
+ socket.on('queue:abort', (data) => {
579
+ const qs = getOrCreateQueueService(data.projectSlug);
580
+ if (qs.isRunning)
581
+ qs.abort();
582
+ triggerDashboardStatusChange(data.projectSlug);
583
+ });
584
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
585
+ socket.on('queue:dismiss', (data) => {
586
+ const qs = getOrCreateQueueService(data.projectSlug);
587
+ qs.dismiss();
588
+ });
589
+ socket.on('queue:removeItem', (data) => {
590
+ const qs = getOrCreateQueueService(data.projectSlug);
591
+ qs.removeItem(data.itemIndex);
592
+ });
593
+ socket.on('queue:addItem', (data) => {
594
+ const qs = getOrCreateQueueService(data.projectSlug);
595
+ const result = parseQueueScript(data.rawLine);
596
+ if (result.items.length > 0) {
597
+ qs.addItem(result.items[0]);
598
+ }
599
+ });
600
+ socket.on('queue:reorderItems', (data) => {
601
+ const qs = getOrCreateQueueService(data.projectSlug);
602
+ qs.reorderItems(data.newOrder);
603
+ });
604
+ // --- Story 17.1: Terminal PTY events ---
605
+ socketTerminals.set(socket.id, new Set());
606
+ socket.on('terminal:create', async (data) => {
607
+ const lang = socket.data.language || 'en';
608
+ const t = i18next.getFixedT(lang);
609
+ // Story 17.5: Security guard
610
+ const access = await checkTerminalAccess(socket, lang);
611
+ if (!access.allowed) {
612
+ log.warn(`Terminal access denied for ${extractClientIP(socket)} on terminal:create`);
613
+ socket.emit('terminal:error', access.error);
614
+ return;
615
+ }
616
+ try {
617
+ // Reattach to existing session
618
+ if (data.terminalId) {
619
+ const tid = data.terminalId;
620
+ const existing = ptyService.getSession(tid);
621
+ if (!existing) {
622
+ socket.emit('terminal:error', {
623
+ terminalId: tid,
624
+ code: TERMINAL_ERRORS.TERMINAL_NOT_FOUND.code,
625
+ message: t('ws.error.terminalNotFound'),
626
+ });
627
+ return;
628
+ }
629
+ ptyService.cancelCleanup(tid);
630
+ ptyService.onData(tid, (output) => {
631
+ socket.emit('terminal:data', { terminalId: tid, data: output });
632
+ });
633
+ ptyService.onExit(tid, (exitCode) => {
634
+ socket.emit('terminal:exit', { terminalId: tid, exitCode });
635
+ socketTerminals.get(socket.id)?.delete(tid);
636
+ });
637
+ socketTerminals.get(socket.id)?.add(tid);
638
+ socket.emit('terminal:created', { terminalId: tid, shell: existing.shell });
639
+ return;
640
+ }
641
+ // Create new session
642
+ const projectPath = await projectService.resolveProjectPath(data.projectSlug);
643
+ if (!projectPath) {
644
+ socket.emit('terminal:error', {
645
+ code: TERMINAL_ERRORS.PTY_SPAWN_ERROR.code,
646
+ message: t('ws.error.terminalProjectNotFound'),
647
+ });
648
+ return;
649
+ }
650
+ const { terminalId, shell } = ptyService.createSession(projectPath, data.projectSlug);
651
+ ptyService.onData(terminalId, (output) => {
652
+ socket.emit('terminal:data', { terminalId, data: output });
653
+ });
654
+ ptyService.onExit(terminalId, (exitCode) => {
655
+ socket.emit('terminal:exit', { terminalId, exitCode });
656
+ socketTerminals.get(socket.id)?.delete(terminalId);
657
+ // Story 20.1: Trigger dashboard on natural PTY exit
658
+ triggerDashboardStatusChange(data.projectSlug);
659
+ });
660
+ socketTerminals.get(socket.id)?.add(terminalId);
661
+ socket.emit('terminal:created', { terminalId, shell });
662
+ // Story 20.1: Trigger dashboard on terminal create
663
+ triggerDashboardStatusChange(data.projectSlug);
664
+ }
665
+ catch (err) {
666
+ const code = err.code || TERMINAL_ERRORS.PTY_SPAWN_ERROR.code;
667
+ const message = err.message || t('ws.error.ptySpawnError');
668
+ socket.emit('terminal:error', { terminalId: data.terminalId, code, message });
669
+ }
670
+ });
671
+ socket.on('terminal:input', async (data) => {
672
+ // Story 17.5: Security guard
673
+ const inputAccess = await checkTerminalAccess(socket, socket.data.language || 'en');
674
+ if (!inputAccess.allowed) {
675
+ log.warn(`Terminal access denied for ${extractClientIP(socket)} on terminal:input`);
676
+ socket.emit('terminal:error', { ...inputAccess.error, terminalId: data.terminalId });
677
+ return;
678
+ }
679
+ try {
680
+ ptyService.writeInput(data.terminalId, data.data);
681
+ }
682
+ catch (err) {
683
+ const code = err.code || TERMINAL_ERRORS.TERMINAL_NOT_FOUND.code;
684
+ socket.emit('terminal:error', { terminalId: data.terminalId, code, message: err.message });
685
+ }
686
+ });
687
+ socket.on('terminal:resize', async (data) => {
688
+ // Story 17.5: Security guard
689
+ const resizeAccess = await checkTerminalAccess(socket, socket.data.language || 'en');
690
+ if (!resizeAccess.allowed) {
691
+ log.warn(`Terminal access denied for ${extractClientIP(socket)} on terminal:resize`);
692
+ socket.emit('terminal:error', { ...resizeAccess.error, terminalId: data.terminalId });
693
+ return;
694
+ }
695
+ try {
696
+ ptyService.resize(data.terminalId, data.cols, data.rows);
697
+ }
698
+ catch (err) {
699
+ const code = err.code || TERMINAL_ERRORS.INVALID_DIMENSIONS.code;
700
+ socket.emit('terminal:error', { terminalId: data.terminalId, code, message: err.message });
701
+ }
702
+ });
703
+ socket.on('terminal:list', async (data) => {
704
+ const lang = socket.data.language || 'en';
705
+ const access = await checkTerminalAccess(socket, lang);
706
+ if (!access.allowed) {
707
+ socket.emit('terminal:list', { projectSlug: data.projectSlug, terminals: [] });
708
+ return;
709
+ }
710
+ try {
711
+ const sessions = ptyService.getSessionsByProject(data.projectSlug);
712
+ const socketTerminalSet = socketTerminals.get(socket.id);
713
+ const terminals = sessions.map((s) => {
714
+ ptyService.cancelCleanup(s.terminalId);
715
+ // Only re-register callbacks if not already attached to this socket
716
+ if (!socketTerminalSet?.has(s.terminalId)) {
717
+ ptyService.onData(s.terminalId, (output) => {
718
+ socket.emit('terminal:data', { terminalId: s.terminalId, data: output });
719
+ });
720
+ ptyService.onExit(s.terminalId, (exitCode) => {
721
+ socket.emit('terminal:exit', { terminalId: s.terminalId, exitCode });
722
+ socketTerminals.get(socket.id)?.delete(s.terminalId);
723
+ triggerDashboardStatusChange(data.projectSlug);
724
+ });
725
+ socketTerminalSet?.add(s.terminalId);
726
+ }
727
+ return { terminalId: s.terminalId, shell: s.shell };
728
+ });
729
+ socket.emit('terminal:list', { projectSlug: data.projectSlug, terminals });
730
+ }
731
+ catch (err) {
732
+ log.error('terminal:list error:', err);
733
+ socket.emit('terminal:list', { projectSlug: data.projectSlug, terminals: [] });
734
+ }
735
+ });
736
+ socket.on('terminal:close', async (data) => {
737
+ // Story 17.5: Security guard
738
+ const closeAccess = await checkTerminalAccess(socket, socket.data.language || 'en');
739
+ if (!closeAccess.allowed) {
740
+ log.warn(`Terminal access denied for ${extractClientIP(socket)} on terminal:close`);
741
+ socket.emit('terminal:error', { ...closeAccess.error, terminalId: data.terminalId });
742
+ return;
743
+ }
744
+ try {
745
+ // Story 20.1: Extract projectSlug BEFORE closeSession removes the session
746
+ const closingSession = ptyService.getSession(data.terminalId);
747
+ const closeProjectSlug = closingSession?.projectSlug;
748
+ ptyService.closeSession(data.terminalId);
749
+ socketTerminals.get(socket.id)?.delete(data.terminalId);
750
+ if (closeProjectSlug) {
751
+ triggerDashboardStatusChange(closeProjectSlug);
752
+ }
753
+ }
754
+ catch {
755
+ // Ignore close errors — session may already be gone
756
+ }
757
+ });
758
+ // Disconnect: detach socket from stream, DON'T abort or deny permissions
759
+ socket.on('disconnect', () => {
760
+ const sessionId = socketToSession.get(socket.id);
761
+ if (sessionId) {
762
+ const stream = activeStreams.get(sessionId);
763
+ if (stream) {
764
+ stream.sockets.delete(socket);
765
+ }
766
+ socketToSession.delete(socket.id);
767
+ }
768
+ // PTY sessions are NOT cleaned up on socket disconnect.
769
+ // They persist until explicitly closed by the user, the PTY process exits,
770
+ // or the server shuts down. This prevents losing long-running terminal
771
+ // work during browser refreshes or temporary network interruptions.
772
+ const terminalIds = socketTerminals.get(socket.id);
773
+ if (terminalIds) {
774
+ socketTerminals.delete(socket.id);
775
+ }
776
+ connectedClients--;
777
+ log.info(`Client disconnected. Total: ${connectedClients}`);
778
+ // Stop polling when no clients connected
779
+ if (connectedClients === 0) {
780
+ rateLimitProbeService.stopPolling();
781
+ }
782
+ });
783
+ });
784
+ return io;
785
+ }
786
+ /**
787
+ * Get the Socket.io server instance
788
+ * @throws Error if Socket.io is not initialized
789
+ * @returns Socket.io server instance
790
+ */
791
+ export function getIO() {
792
+ if (!io) {
793
+ throw new Error('Socket.io not initialized');
794
+ }
795
+ return io;
796
+ }
797
+ /**
798
+ * Get the current number of connected clients
799
+ * @returns Number of connected clients
800
+ */
801
+ export function getConnectedClientsCount() {
802
+ return connectedClients;
803
+ }
804
+ /**
805
+ * Validate image attachments
806
+ * Story 5.5: Image Attachment
807
+ */
808
+ function validateImages(images, lang) {
809
+ const t = i18next.getFixedT(lang);
810
+ if (images.length > IMAGE_CONSTRAINTS.MAX_COUNT) {
811
+ return { valid: false, error: t('ws.error.maxImages', { value: IMAGE_CONSTRAINTS.MAX_COUNT }) };
812
+ }
813
+ for (const img of images) {
814
+ if (!IMAGE_CONSTRAINTS.ACCEPTED_TYPES.includes(img.mimeType)) {
815
+ return { valid: false, error: t('ws.error.unsupportedImageFormat', { value: img.mimeType }) };
816
+ }
817
+ // base64 size approximation: base64 length * 0.75 ≈ original bytes
818
+ const sizeBytes = Math.ceil(img.data.length * 0.75);
819
+ if (sizeBytes > IMAGE_CONSTRAINTS.MAX_SIZE_BYTES) {
820
+ return { valid: false, error: t('ws.error.imageSizeExceeded', { value: img.name }) };
821
+ }
822
+ }
823
+ return { valid: true };
824
+ }
825
+ /**
826
+ * Check if error message indicates a session not found error
827
+ */
828
+ function isSessionNotFoundError(error) {
829
+ const message = error.message.toLowerCase();
830
+ return (message.includes('session not found') ||
831
+ message.includes('session does not exist') ||
832
+ message.includes('invalid session') ||
833
+ message.includes('no such session'));
834
+ }
835
+ /**
836
+ * Handle chat:send event from client
837
+ * Processes user message through ChatService and streams response back
838
+ * All emit calls are buffered via createStreamEmit for reconnect support.
839
+ */
840
+ async function handleChatSend(stream, data, abortController, lang) {
841
+ const emit = createStreamEmit(stream);
842
+ const t = i18next.getFixedT(lang);
843
+ const { content, workingDirectory, sessionId, resume, permissionMode, model, images } = data;
844
+ // Validate images if present (Story 5.5)
845
+ if (images && images.length > 0) {
846
+ const validation = validateImages(images, lang);
847
+ if (!validation.valid) {
848
+ emit('error', {
849
+ code: ERROR_CODES.VALIDATION_ERROR,
850
+ message: validation.error,
851
+ });
852
+ return;
853
+ }
854
+ }
855
+ // Validate workingDirectory exists
856
+ if (!workingDirectory || !existsSync(workingDirectory)) {
857
+ emit('error', {
858
+ code: ERROR_CODES.INVALID_WORKING_DIR,
859
+ message: t('ws.error.projectPathNotFound'),
860
+ });
861
+ return;
862
+ }
863
+ // Buffer the user's message so reconnecting clients can display it
864
+ // (SDK may not have written the JSONL file yet at reconnect time)
865
+ emit('user:message', { content, sessionId: sessionId || '' });
866
+ const isResuming = resume && sessionId;
867
+ const sessionService = new SessionService();
868
+ let timeoutId = null;
869
+ try {
870
+ const chatService = new ChatService({ workingDirectory, permissionMode });
871
+ stream.chatService = chatService;
872
+ // Load preferences early for advanced settings + timeout
873
+ const effectivePrefs = await preferencesService.getEffectivePreferences();
874
+ const chatOptions = {
875
+ ...(isResuming ? { resume: sessionId } : { sessionId }),
876
+ abortController,
877
+ model,
878
+ images,
879
+ // Advanced settings from preferences
880
+ customSystemPrompt: effectivePrefs.customSystemPrompt,
881
+ maxThinkingTokens: effectivePrefs.maxThinkingTokens,
882
+ maxTurns: effectivePrefs.maxTurns,
883
+ maxBudgetUsd: effectivePrefs.maxBudgetUsd,
884
+ };
885
+ // Create canUseTool callback for permission & AskUserQuestion handling
886
+ // Promise stays pending if socket disconnected — SDK naturally waits
887
+ const canUseTool = async (toolName, input, options) => {
888
+ // Auto-approve ExitPlanMode when the user originally chose Bypass mode.
889
+ // The SDK internally switches to 'plan' after EnterPlanMode, which causes
890
+ // ExitPlanMode to request approval even though the user intended full bypass.
891
+ if (toolName === 'ExitPlanMode' && permissionMode === 'bypassPermissions') {
892
+ log.debug('Auto-approving ExitPlanMode: original permissionMode is bypassPermissions');
893
+ return { behavior: 'allow', updatedInput: input };
894
+ }
895
+ const isAskUserQuestion = toolName === 'AskUserQuestion';
896
+ const requestId = options.toolUseID || `perm-${++permissionRequestCounter}`;
897
+ log.debug(`canUseTool called: tool=${toolName}, toolUseID=${options.toolUseID}, requestId=${requestId}`);
898
+ // Emit permission:request (buffered + forwarded to connected socket)
899
+ emit('permission:request', {
900
+ id: requestId,
901
+ sessionId: sessionIdRef.current || '',
902
+ toolCall: { id: requestId, name: toolName, input },
903
+ requiresApproval: true,
904
+ });
905
+ // Notify via Telegram if no socket connected (or alwaysNotify enabled)
906
+ if (notificationService.shouldNotify(stream.sockets.size)) {
907
+ const prompt = isAskUserQuestion
908
+ ? formatAskQuestionPrompt(input)
909
+ : `${toolName}`;
910
+ notificationService.notifyInputRequired(stream.sessionId, toolName, prompt);
911
+ }
912
+ // Wait for user response — Promise stays pending if no socket connected
913
+ const userResponse = await new Promise((resolve) => {
914
+ stream.pendingPermissions.set(requestId, {
915
+ resolve,
916
+ interactionType: isAskUserQuestion ? 'question' : 'permission',
917
+ });
918
+ });
919
+ if (isAskUserQuestion) {
920
+ const questions = input.questions;
921
+ let answers;
922
+ if (typeof userResponse.response === 'object' && !Array.isArray(userResponse.response) && userResponse.response !== null) {
923
+ answers = userResponse.response;
924
+ }
925
+ else {
926
+ const answer = typeof userResponse.response === 'string'
927
+ ? userResponse.response
928
+ : Array.isArray(userResponse.response) ? userResponse.response.join(', ') : '';
929
+ answers = { [questions[0].question]: answer };
930
+ }
931
+ return {
932
+ behavior: 'allow',
933
+ updatedInput: {
934
+ questions,
935
+ answers,
936
+ },
937
+ };
938
+ }
939
+ if (userResponse.approved) {
940
+ return { behavior: 'allow', updatedInput: input };
941
+ }
942
+ else {
943
+ return { behavior: 'deny', message: 'User denied permission', interrupt: true };
944
+ }
945
+ };
946
+ // Activity-based timeout: resets on every SDK callback event
947
+ // Prevents cancellation while SDK is actively working (e.g., large Write input streaming)
948
+ // Timeout value from preferences (with env var override), clamped to 30s–30min range
949
+ const rawTimeoutMs = effectivePrefs.chatTimeoutMs ?? config.chat.timeoutMs;
950
+ const timeoutMs = (rawTimeoutMs >= 30000 && rawTimeoutMs <= 1800000) ? rawTimeoutMs : 300000;
951
+ let lastResetSource = 'initial';
952
+ const resetTimeout = (source) => {
953
+ if (source)
954
+ lastResetSource = source;
955
+ if (timeoutId)
956
+ clearTimeout(timeoutId);
957
+ timeoutId = setTimeout(() => {
958
+ log.warn(`TIMEOUT FIRED after ${timeoutMs}ms inactivity (last reset by: ${lastResetSource})`);
959
+ abortController.abort('timeout');
960
+ }, timeoutMs);
961
+ };
962
+ resetTimeout('initial');
963
+ // Build shared callbacks (common logic for browser & queue paths)
964
+ const { callbacks, sessionIdRef } = buildStreamCallbacks({
965
+ emit,
966
+ stream,
967
+ isResuming: !!isResuming,
968
+ initialSessionId: sessionId,
969
+ rekeyStream: (sid) => rekeyStream(stream, sid),
970
+ broadcastStreamChange,
971
+ notificationService,
972
+ }, {
973
+ onCallbackActivity: (source) => resetTimeout(source),
974
+ onSessionIdResolved: (sid) => {
975
+ sessionService.saveSessionId(workingDirectory, sid).catch(() => { });
976
+ },
977
+ });
978
+ // Browser-only callbacks
979
+ callbacks.onActivity = (messageType) => {
980
+ resetTimeout(`onActivity:${messageType}`);
981
+ };
982
+ callbacks.onError = (error) => {
983
+ // Ignore abort errors from replaced streams (another-client or user-abort)
984
+ if (abortController.signal.aborted) {
985
+ const reason = abortController.signal.reason;
986
+ if (reason === 'another-client' || reason === 'user-abort') {
987
+ return;
988
+ }
989
+ }
990
+ const sdkError = parseSDKError(error, lang);
991
+ if (isResuming && isSessionNotFoundError(error)) {
992
+ emit('error', {
993
+ code: ERROR_CODES.SESSION_NOT_FOUND,
994
+ message: t('ws.error.sessionNotFound'),
995
+ });
996
+ return;
997
+ }
998
+ emit('error', {
999
+ code: ERROR_CODES.CHAT_ERROR,
1000
+ message: sdkError.message,
1001
+ });
1002
+ // Notify via Telegram if no socket connected (or alwaysNotify enabled)
1003
+ if (notificationService.shouldNotify(stream.sockets.size)) {
1004
+ notificationService.notifyError(stream.sessionId, sdkError.message);
1005
+ }
1006
+ };
1007
+ await chatService.sendMessageWithCallbacks(content, callbacks, chatOptions, canUseTool, (messageType) => {
1008
+ resetTimeout(`raw:${messageType}`);
1009
+ });
1010
+ }
1011
+ catch (error) {
1012
+ const sdkError = parseSDKError(error, lang);
1013
+ if (sdkError instanceof AbortedError || abortController.signal.aborted) {
1014
+ if (abortController.signal.reason === 'user-abort' || abortController.signal.reason === 'another-client') {
1015
+ return;
1016
+ }
1017
+ emit('error', {
1018
+ code: ERROR_CODES.TIMEOUT_ERROR,
1019
+ message: t('ws.error.timeout'),
1020
+ });
1021
+ return;
1022
+ }
1023
+ if (isResuming && error instanceof Error && isSessionNotFoundError(error)) {
1024
+ emit('error', {
1025
+ code: ERROR_CODES.SESSION_NOT_FOUND,
1026
+ message: t('ws.error.sessionNotFound'),
1027
+ });
1028
+ return;
1029
+ }
1030
+ emit('error', {
1031
+ code: ERROR_CODES.CHAT_ERROR,
1032
+ message: sdkError.message,
1033
+ });
1034
+ }
1035
+ finally {
1036
+ if (timeoutId) {
1037
+ clearTimeout(timeoutId);
1038
+ }
1039
+ }
1040
+ }
1041
+ //# sourceMappingURL=websocket.js.map