relay-ide 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (506) hide show
  1. package/README.md +259 -0
  2. package/dist/bin/claude-remote-cli.js +390 -0
  3. package/dist/bin/relay-ide.js +390 -0
  4. package/dist/frontend/assets/abap-BdImnpbu.js +1 -0
  5. package/dist/frontend/assets/actionscript-3-CoDkCxhg.js +1 -0
  6. package/dist/frontend/assets/ada-bCR0ucgS.js +1 -0
  7. package/dist/frontend/assets/andromeeda-C4gqWexZ.js +1 -0
  8. package/dist/frontend/assets/angular-html-DA-rfuFy.js +1 -0
  9. package/dist/frontend/assets/angular-ts-BrjP3tb8.js +1 -0
  10. package/dist/frontend/assets/apache-Pmp26Uib.js +1 -0
  11. package/dist/frontend/assets/apex-D8_7TLub.js +1 -0
  12. package/dist/frontend/assets/apl-CORt7UWP.js +1 -0
  13. package/dist/frontend/assets/applescript-Co6uUVPk.js +1 -0
  14. package/dist/frontend/assets/ara-BRHolxvo.js +1 -0
  15. package/dist/frontend/assets/asciidoc-Ve4PFQV2.js +1 -0
  16. package/dist/frontend/assets/asm-D_Q5rh1f.js +1 -0
  17. package/dist/frontend/assets/astro-HNnZUWAn.js +1 -0
  18. package/dist/frontend/assets/aurora-x-D-2ljcwZ.js +1 -0
  19. package/dist/frontend/assets/awk-DMzUqQB5.js +1 -0
  20. package/dist/frontend/assets/ayu-dark-DYE7WIF3.js +1 -0
  21. package/dist/frontend/assets/ayu-light-BA47KaF1.js +1 -0
  22. package/dist/frontend/assets/ayu-mirage-32ctXXKs.js +1 -0
  23. package/dist/frontend/assets/ballerina-BFfxhgS-.js +1 -0
  24. package/dist/frontend/assets/bat-BkioyH1T.js +1 -0
  25. package/dist/frontend/assets/beancount-k_qm7-4y.js +1 -0
  26. package/dist/frontend/assets/berry-uYugtg8r.js +1 -0
  27. package/dist/frontend/assets/bibtex-CHM0blh-.js +1 -0
  28. package/dist/frontend/assets/bicep-Bmn6On1c.js +1 -0
  29. package/dist/frontend/assets/bird2-BIv1doCn.js +1 -0
  30. package/dist/frontend/assets/blade-BjGOyj-B.js +1 -0
  31. package/dist/frontend/assets/bsl-BO_Y6i37.js +1 -0
  32. package/dist/frontend/assets/c-BIGW1oBm.js +1 -0
  33. package/dist/frontend/assets/c3-eo99z4R2.js +1 -0
  34. package/dist/frontend/assets/cadence-Bv_4Rxtq.js +1 -0
  35. package/dist/frontend/assets/cairo-KRGpt6FW.js +1 -0
  36. package/dist/frontend/assets/catppuccin-frappe-DFWUc33u.js +1 -0
  37. package/dist/frontend/assets/catppuccin-latte-C9dUb6Cb.js +1 -0
  38. package/dist/frontend/assets/catppuccin-macchiato-DQyhUUbL.js +1 -0
  39. package/dist/frontend/assets/catppuccin-mocha-D87Tk5Gz.js +1 -0
  40. package/dist/frontend/assets/clarity-D53aC0YG.js +1 -0
  41. package/dist/frontend/assets/clojure-P80f7IUj.js +1 -0
  42. package/dist/frontend/assets/cmake-D1j8_8rp.js +1 -0
  43. package/dist/frontend/assets/cobol-nBiQ_Alo.js +1 -0
  44. package/dist/frontend/assets/codeowners-Bp6g37R7.js +1 -0
  45. package/dist/frontend/assets/codeql-DsOJ9woJ.js +1 -0
  46. package/dist/frontend/assets/coffee-Ch7k5sss.js +1 -0
  47. package/dist/frontend/assets/common-lisp-Cg-RD9OK.js +1 -0
  48. package/dist/frontend/assets/coq-DkFqJrB1.js +1 -0
  49. package/dist/frontend/assets/cpp-CofmeUqb.js +1 -0
  50. package/dist/frontend/assets/crystal-DNxU26gB.js +1 -0
  51. package/dist/frontend/assets/csharp-COcwbKMJ.js +1 -0
  52. package/dist/frontend/assets/css-CLj8gQPS.js +1 -0
  53. package/dist/frontend/assets/csv-fuZLfV_i.js +1 -0
  54. package/dist/frontend/assets/cue-D82EKSYY.js +1 -0
  55. package/dist/frontend/assets/cypher-COkxafJQ.js +1 -0
  56. package/dist/frontend/assets/d-85-TOEBH.js +1 -0
  57. package/dist/frontend/assets/dark-plus-C3mMm8J8.js +1 -0
  58. package/dist/frontend/assets/dart-bE4Kk8sk.js +1 -0
  59. package/dist/frontend/assets/dax-CEL-wOlO.js +1 -0
  60. package/dist/frontend/assets/desktop-BmXAJ9_W.js +1 -0
  61. package/dist/frontend/assets/diff-D97Zzqfu.js +1 -0
  62. package/dist/frontend/assets/docker-BcOcwvcX.js +1 -0
  63. package/dist/frontend/assets/dotenv-Da5cRb03.js +1 -0
  64. package/dist/frontend/assets/dracula-BzJJZx-M.js +1 -0
  65. package/dist/frontend/assets/dracula-soft-BXkSAIEj.js +1 -0
  66. package/dist/frontend/assets/dream-maker-BtqSS_iP.js +1 -0
  67. package/dist/frontend/assets/edge-FbVlp4U3.js +1 -0
  68. package/dist/frontend/assets/elixir-CkH2-t6x.js +1 -0
  69. package/dist/frontend/assets/elm-DbKCFpqz.js +1 -0
  70. package/dist/frontend/assets/emacs-lisp-CXvaQtF9.js +1 -0
  71. package/dist/frontend/assets/erb-BYCe7drp.js +1 -0
  72. package/dist/frontend/assets/erlang-DsQrWhSR.js +1 -0
  73. package/dist/frontend/assets/everforest-dark-BgDCqdQA.js +1 -0
  74. package/dist/frontend/assets/everforest-light-C8M2exoo.js +1 -0
  75. package/dist/frontend/assets/fennel-BYunw83y.js +1 -0
  76. package/dist/frontend/assets/fish-BvzEVeQv.js +1 -0
  77. package/dist/frontend/assets/fluent-C4IJs8-o.js +1 -0
  78. package/dist/frontend/assets/fortran-fixed-form-CkoXwp7k.js +1 -0
  79. package/dist/frontend/assets/fortran-free-form-BxgE0vQu.js +1 -0
  80. package/dist/frontend/assets/fsharp-CXgrBDvD.js +1 -0
  81. package/dist/frontend/assets/gdresource-BOOCDP_w.js +1 -0
  82. package/dist/frontend/assets/gdscript-C5YyOfLZ.js +1 -0
  83. package/dist/frontend/assets/gdshader-DkwncUOv.js +1 -0
  84. package/dist/frontend/assets/genie-D0YGMca9.js +1 -0
  85. package/dist/frontend/assets/gherkin-DyxjwDmM.js +1 -0
  86. package/dist/frontend/assets/git-commit-F4YmCXRG.js +1 -0
  87. package/dist/frontend/assets/git-rebase-r7XF79zn.js +1 -0
  88. package/dist/frontend/assets/github-dark-DHJKELXO.js +1 -0
  89. package/dist/frontend/assets/github-dark-default-Cuk6v7N8.js +1 -0
  90. package/dist/frontend/assets/github-dark-dimmed-DH5Ifo-i.js +1 -0
  91. package/dist/frontend/assets/github-dark-high-contrast-E3gJ1_iC.js +1 -0
  92. package/dist/frontend/assets/github-light-DAi9KRSo.js +1 -0
  93. package/dist/frontend/assets/github-light-default-D7oLnXFd.js +1 -0
  94. package/dist/frontend/assets/github-light-high-contrast-BfjtVDDH.js +1 -0
  95. package/dist/frontend/assets/gleam-BspZqrRM.js +1 -0
  96. package/dist/frontend/assets/glimmer-js-ByusRIyA.js +1 -0
  97. package/dist/frontend/assets/glimmer-ts-BfAWNZQY.js +1 -0
  98. package/dist/frontend/assets/glsl-DplSGwfg.js +1 -0
  99. package/dist/frontend/assets/gn-n2N0HUVH.js +1 -0
  100. package/dist/frontend/assets/gnuplot-DdkO51Og.js +1 -0
  101. package/dist/frontend/assets/go-C27-OAKa.js +1 -0
  102. package/dist/frontend/assets/graphql-ChdNCCLP.js +1 -0
  103. package/dist/frontend/assets/groovy-gcz8RCvz.js +1 -0
  104. package/dist/frontend/assets/gruvbox-dark-hard-CFHQjOhq.js +1 -0
  105. package/dist/frontend/assets/gruvbox-dark-medium-GsRaNv29.js +1 -0
  106. package/dist/frontend/assets/gruvbox-dark-soft-CVdnzihN.js +1 -0
  107. package/dist/frontend/assets/gruvbox-light-hard-CH1njM8p.js +1 -0
  108. package/dist/frontend/assets/gruvbox-light-medium-DRw_LuNl.js +1 -0
  109. package/dist/frontend/assets/gruvbox-light-soft-hJgmCMqR.js +1 -0
  110. package/dist/frontend/assets/hack-i7_Ulhet.js +1 -0
  111. package/dist/frontend/assets/haml-D5jkg6IW.js +1 -0
  112. package/dist/frontend/assets/handlebars-BpdQsYii.js +1 -0
  113. package/dist/frontend/assets/haskell-Df6bDoY_.js +1 -0
  114. package/dist/frontend/assets/haxe-CzTSHFRz.js +1 -0
  115. package/dist/frontend/assets/hcl-BWvSN4gD.js +1 -0
  116. package/dist/frontend/assets/hjson-D5-asLiD.js +1 -0
  117. package/dist/frontend/assets/hlsl-D3lLCCz7.js +1 -0
  118. package/dist/frontend/assets/horizon-BUw7H-hv.js +1 -0
  119. package/dist/frontend/assets/horizon-bright-CUuTKBJd.js +1 -0
  120. package/dist/frontend/assets/houston-DnULxvSX.js +1 -0
  121. package/dist/frontend/assets/html-derivative-DlHx6ybY.js +1 -0
  122. package/dist/frontend/assets/html-pp8916En.js +1 -0
  123. package/dist/frontend/assets/http-jrhK8wxY.js +1 -0
  124. package/dist/frontend/assets/hurl-irOxFIW8.js +1 -0
  125. package/dist/frontend/assets/hxml-Bvhsp5Yf.js +1 -0
  126. package/dist/frontend/assets/hy-DFXneXwc.js +1 -0
  127. package/dist/frontend/assets/imba-DGztddWO.js +1 -0
  128. package/dist/frontend/assets/ini-BEwlwnbL.js +1 -0
  129. package/dist/frontend/assets/java-CylS5w8V.js +1 -0
  130. package/dist/frontend/assets/javascript-wDzz0qaB.js +1 -0
  131. package/dist/frontend/assets/jinja-f2NsQr07.js +1 -0
  132. package/dist/frontend/assets/jison-wvAkD_A8.js +1 -0
  133. package/dist/frontend/assets/json-Cp-IABpG.js +1 -0
  134. package/dist/frontend/assets/json5-C9tS-k6U.js +1 -0
  135. package/dist/frontend/assets/jsonc-Des-eS-w.js +1 -0
  136. package/dist/frontend/assets/jsonl-DcaNXYhu.js +1 -0
  137. package/dist/frontend/assets/jsonnet-DFQXde-d.js +1 -0
  138. package/dist/frontend/assets/jssm-C2t-YnRu.js +1 -0
  139. package/dist/frontend/assets/jsx-g9-lgVsj.js +1 -0
  140. package/dist/frontend/assets/julia-CxzCAyBv.js +1 -0
  141. package/dist/frontend/assets/just-VxiPbLrw.js +1 -0
  142. package/dist/frontend/assets/kanagawa-dragon-CkXjmgJE.js +1 -0
  143. package/dist/frontend/assets/kanagawa-lotus-CfQXZHmo.js +1 -0
  144. package/dist/frontend/assets/kanagawa-wave-DWedfzmr.js +1 -0
  145. package/dist/frontend/assets/kdl-DV7GczEv.js +1 -0
  146. package/dist/frontend/assets/kotlin-BdnUsdx6.js +1 -0
  147. package/dist/frontend/assets/kusto-wEQ09or8.js +1 -0
  148. package/dist/frontend/assets/laserwave-DUszq2jm.js +1 -0
  149. package/dist/frontend/assets/latex-CWtU0Tv5.js +1 -0
  150. package/dist/frontend/assets/lean-BZvkOJ9d.js +1 -0
  151. package/dist/frontend/assets/less-B1dDrJ26.js +1 -0
  152. package/dist/frontend/assets/light-plus-B7mTdjB0.js +1 -0
  153. package/dist/frontend/assets/liquid-C0sCDyMI.js +1 -0
  154. package/dist/frontend/assets/llvm-DjAJT7YJ.js +1 -0
  155. package/dist/frontend/assets/log-2UxHyX5q.js +1 -0
  156. package/dist/frontend/assets/logo-BtOb2qkB.js +1 -0
  157. package/dist/frontend/assets/lua-BaeVxFsk.js +1 -0
  158. package/dist/frontend/assets/luau-C-HG3fhB.js +1 -0
  159. package/dist/frontend/assets/main-CL5_Wlhv.css +32 -0
  160. package/dist/frontend/assets/main-Czet4Z1x.js +371 -0
  161. package/dist/frontend/assets/make-CHLpvVh8.js +1 -0
  162. package/dist/frontend/assets/markdown-Cvjx9yec.js +1 -0
  163. package/dist/frontend/assets/marko-DjSrsDqO.js +1 -0
  164. package/dist/frontend/assets/material-theme-D5KoaKCx.js +1 -0
  165. package/dist/frontend/assets/material-theme-darker-BfHTSMKl.js +1 -0
  166. package/dist/frontend/assets/material-theme-lighter-B0m2ddpp.js +1 -0
  167. package/dist/frontend/assets/material-theme-ocean-CyktbL80.js +1 -0
  168. package/dist/frontend/assets/material-theme-palenight-Csfq5Kiy.js +1 -0
  169. package/dist/frontend/assets/matlab-D7o27uSR.js +1 -0
  170. package/dist/frontend/assets/mdc-DTYItulj.js +1 -0
  171. package/dist/frontend/assets/mdx-Cmh6b_Ma.js +1 -0
  172. package/dist/frontend/assets/mermaid-mWjccvbQ.js +1 -0
  173. package/dist/frontend/assets/min-dark-CafNBF8u.js +1 -0
  174. package/dist/frontend/assets/min-light-CTRr51gU.js +1 -0
  175. package/dist/frontend/assets/mipsasm-CKIfxQSi.js +1 -0
  176. package/dist/frontend/assets/mojo-rZm6bMo-.js +1 -0
  177. package/dist/frontend/assets/monokai-D4h5O-jR.js +1 -0
  178. package/dist/frontend/assets/moonbit-_H4v1dQx.js +1 -0
  179. package/dist/frontend/assets/move-IF9eRakj.js +1 -0
  180. package/dist/frontend/assets/narrat-DRg8JJMk.js +1 -0
  181. package/dist/frontend/assets/nextflow-C-mBbutL.js +1 -0
  182. package/dist/frontend/assets/nextflow-groovy-vE_lwT2v.js +1 -0
  183. package/dist/frontend/assets/nginx-BpAMiNFr.js +1 -0
  184. package/dist/frontend/assets/night-owl-C39BiMTA.js +1 -0
  185. package/dist/frontend/assets/night-owl-light-CMTm3GFP.js +1 -0
  186. package/dist/frontend/assets/nim-BIad80T-.js +1 -0
  187. package/dist/frontend/assets/nix-CwoSXNpI.js +1 -0
  188. package/dist/frontend/assets/nord-Ddv68eIx.js +1 -0
  189. package/dist/frontend/assets/nushell-Cz2AlsmD.js +1 -0
  190. package/dist/frontend/assets/objective-c-DXmwc3jG.js +1 -0
  191. package/dist/frontend/assets/objective-cpp-CLxacb5B.js +1 -0
  192. package/dist/frontend/assets/ocaml-C0hk2d4L.js +1 -0
  193. package/dist/frontend/assets/odin-BBf5iR-q.js +1 -0
  194. package/dist/frontend/assets/one-dark-pro-DVMEJ2y_.js +1 -0
  195. package/dist/frontend/assets/one-light-C3Wv6jpd.js +1 -0
  196. package/dist/frontend/assets/openscad-C4EeE6gA.js +1 -0
  197. package/dist/frontend/assets/pascal-D93ZcfNL.js +1 -0
  198. package/dist/frontend/assets/perl-NvoQZIq0.js +1 -0
  199. package/dist/frontend/assets/php-R6g_5hLQ.js +1 -0
  200. package/dist/frontend/assets/pkl-u5AG7uiY.js +1 -0
  201. package/dist/frontend/assets/plastic-3e1v2bzS.js +1 -0
  202. package/dist/frontend/assets/plsql-ChMvpjG-.js +1 -0
  203. package/dist/frontend/assets/po-BTJTHyun.js +1 -0
  204. package/dist/frontend/assets/poimandres-CS3Unz2-.js +1 -0
  205. package/dist/frontend/assets/polar-C0HS_06l.js +1 -0
  206. package/dist/frontend/assets/postcss-CXtECtnM.js +1 -0
  207. package/dist/frontend/assets/powerquery-CEu0bR-o.js +1 -0
  208. package/dist/frontend/assets/powershell-Dpen1YoG.js +1 -0
  209. package/dist/frontend/assets/prisma-Dd19v3D-.js +1 -0
  210. package/dist/frontend/assets/prolog-CbFg5uaA.js +1 -0
  211. package/dist/frontend/assets/proto-C7zT0LnQ.js +1 -0
  212. package/dist/frontend/assets/pug-DKIMFp6K.js +1 -0
  213. package/dist/frontend/assets/puppet-BMWR74SV.js +1 -0
  214. package/dist/frontend/assets/purescript-CklMAg4u.js +1 -0
  215. package/dist/frontend/assets/python-B6aJPvgy.js +1 -0
  216. package/dist/frontend/assets/qml-3beO22l8.js +1 -0
  217. package/dist/frontend/assets/qmldir-C8lEn-DE.js +1 -0
  218. package/dist/frontend/assets/qss-IeuSbFQv.js +1 -0
  219. package/dist/frontend/assets/r-Dspwwk_N.js +1 -0
  220. package/dist/frontend/assets/racket-BqYA7rlc.js +1 -0
  221. package/dist/frontend/assets/raku-DXvB9xmW.js +1 -0
  222. package/dist/frontend/assets/razor-BDqjjVU7.js +1 -0
  223. package/dist/frontend/assets/red-bN70gL4F.js +1 -0
  224. package/dist/frontend/assets/reg-C-SQnVFl.js +1 -0
  225. package/dist/frontend/assets/regexp-CDVJQ6XC.js +1 -0
  226. package/dist/frontend/assets/rel-C3B-1QV4.js +1 -0
  227. package/dist/frontend/assets/riscv-BM1_JUlF.js +1 -0
  228. package/dist/frontend/assets/ron-D8l8udqQ.js +1 -0
  229. package/dist/frontend/assets/rose-pine-dawn-DHQR4-dF.js +1 -0
  230. package/dist/frontend/assets/rose-pine-moon-D4_iv3hh.js +1 -0
  231. package/dist/frontend/assets/rose-pine-qdsjHGoJ.js +1 -0
  232. package/dist/frontend/assets/rosmsg-BJDFO7_C.js +1 -0
  233. package/dist/frontend/assets/rst-CRjBmOyv.js +1 -0
  234. package/dist/frontend/assets/ruby-Wjq7vjNf.js +1 -0
  235. package/dist/frontend/assets/rust-B1yitclQ.js +1 -0
  236. package/dist/frontend/assets/sas-cz2c8ADy.js +1 -0
  237. package/dist/frontend/assets/sass-Cj5Yp3dK.js +1 -0
  238. package/dist/frontend/assets/scala-C151Ov-r.js +1 -0
  239. package/dist/frontend/assets/scheme-C98Dy4si.js +1 -0
  240. package/dist/frontend/assets/scss-D5BDwBP9.js +1 -0
  241. package/dist/frontend/assets/sdbl-DVxCFoDh.js +1 -0
  242. package/dist/frontend/assets/shaderlab-Dg9Lc6iA.js +1 -0
  243. package/dist/frontend/assets/shellscript-Yzrsuije.js +1 -0
  244. package/dist/frontend/assets/shellsession-BADoaaVG.js +1 -0
  245. package/dist/frontend/assets/slack-dark-BthQWCQV.js +1 -0
  246. package/dist/frontend/assets/slack-ochin-DqwNpetd.js +1 -0
  247. package/dist/frontend/assets/smalltalk-BERRCDM3.js +1 -0
  248. package/dist/frontend/assets/snazzy-light-Bw305WKR.js +1 -0
  249. package/dist/frontend/assets/solarized-dark-DXbdFlpD.js +1 -0
  250. package/dist/frontend/assets/solarized-light-L9t79GZl.js +1 -0
  251. package/dist/frontend/assets/solidity-rGO070M0.js +1 -0
  252. package/dist/frontend/assets/soy-8wufbnw4.js +1 -0
  253. package/dist/frontend/assets/sparql-rVzFXLq3.js +1 -0
  254. package/dist/frontend/assets/splunk-BtCnVYZw.js +1 -0
  255. package/dist/frontend/assets/sql-BLtJtn59.js +1 -0
  256. package/dist/frontend/assets/ssh-config-_ykCGR6B.js +1 -0
  257. package/dist/frontend/assets/stata-BH5u7GGu.js +1 -0
  258. package/dist/frontend/assets/stylus-BEDo0Tqx.js +1 -0
  259. package/dist/frontend/assets/surrealql-Bq5Q-fJD.js +1 -0
  260. package/dist/frontend/assets/svelte-Cy7k_4gC.js +1 -0
  261. package/dist/frontend/assets/swift-D82vCrfD.js +1 -0
  262. package/dist/frontend/assets/synthwave-84-CbfX1IO0.js +1 -0
  263. package/dist/frontend/assets/system-verilog-CnnmHF94.js +1 -0
  264. package/dist/frontend/assets/systemd-4A_iFExJ.js +1 -0
  265. package/dist/frontend/assets/talonscript-CkByrt1z.js +1 -0
  266. package/dist/frontend/assets/tasl-QIJgUcNo.js +1 -0
  267. package/dist/frontend/assets/tcl-dwOrl1Do.js +1 -0
  268. package/dist/frontend/assets/templ-DhtptRzy.js +1 -0
  269. package/dist/frontend/assets/terraform-BETggiCN.js +1 -0
  270. package/dist/frontend/assets/tex-idrVyKtj.js +1 -0
  271. package/dist/frontend/assets/tokyo-night-hegEt444.js +1 -0
  272. package/dist/frontend/assets/toml-vGWfd6FD.js +1 -0
  273. package/dist/frontend/assets/ts-tags-DQrlYJgV.js +1 -0
  274. package/dist/frontend/assets/tsv-B_m7g4N7.js +1 -0
  275. package/dist/frontend/assets/tsx-COt5Ahok.js +1 -0
  276. package/dist/frontend/assets/turtle-BsS91CYL.js +1 -0
  277. package/dist/frontend/assets/twig-xg9kU7Mw.js +1 -0
  278. package/dist/frontend/assets/typescript-BPQ3VLAy.js +1 -0
  279. package/dist/frontend/assets/typespec-CAFt9gP4.js +1 -0
  280. package/dist/frontend/assets/typst-DHCkPAjA.js +1 -0
  281. package/dist/frontend/assets/v-BcVCzyr7.js +1 -0
  282. package/dist/frontend/assets/vala-CsfeWuGM.js +1 -0
  283. package/dist/frontend/assets/vb-D17OF-Vu.js +1 -0
  284. package/dist/frontend/assets/verilog-BQ8w6xss.js +1 -0
  285. package/dist/frontend/assets/vesper-DU1UobuO.js +1 -0
  286. package/dist/frontend/assets/vhdl-CeAyd5Ju.js +1 -0
  287. package/dist/frontend/assets/viml-CJc9bBzg.js +1 -0
  288. package/dist/frontend/assets/vitesse-black-Bkuqu6BP.js +1 -0
  289. package/dist/frontend/assets/vitesse-dark-D0r3Knsf.js +1 -0
  290. package/dist/frontend/assets/vitesse-light-CVO1_9PV.js +1 -0
  291. package/dist/frontend/assets/vue-D2xRrEX4.js +1 -0
  292. package/dist/frontend/assets/vue-html-AaS7Mt5G.js +1 -0
  293. package/dist/frontend/assets/vue-vine-BoDAl6tE.js +1 -0
  294. package/dist/frontend/assets/vyper-CDx5xZoG.js +1 -0
  295. package/dist/frontend/assets/wasm-CG6Dc4jp.js +1 -0
  296. package/dist/frontend/assets/wasm-MzD3tlZU.js +1 -0
  297. package/dist/frontend/assets/wenyan-BV7otONQ.js +1 -0
  298. package/dist/frontend/assets/wgsl-Dx-B1_4e.js +1 -0
  299. package/dist/frontend/assets/wikitext-BhOHFoWU.js +1 -0
  300. package/dist/frontend/assets/wit-5i3qLPDT.js +1 -0
  301. package/dist/frontend/assets/wolfram-lXgVvXCa.js +1 -0
  302. package/dist/frontend/assets/xml-sdJ4AIDG.js +1 -0
  303. package/dist/frontend/assets/xsl-CtQFsRM5.js +1 -0
  304. package/dist/frontend/assets/yaml-Buea-lGh.js +1 -0
  305. package/dist/frontend/assets/zenscript-DVFEvuxE.js +1 -0
  306. package/dist/frontend/assets/zig-VOosw3JB.js +1 -0
  307. package/dist/frontend/icon-192.png +0 -0
  308. package/dist/frontend/icon-512.png +0 -0
  309. package/dist/frontend/icon.svg +8 -0
  310. package/dist/frontend/index.html +30 -0
  311. package/dist/frontend/manifest.json +25 -0
  312. package/dist/frontend/sw.js +66 -0
  313. package/dist/server/agent-events.js +39 -0
  314. package/dist/server/analytics.js +885 -0
  315. package/dist/server/auth.js +65 -0
  316. package/dist/server/belayer/executor.js +200 -0
  317. package/dist/server/belayer/intake.js +27 -0
  318. package/dist/server/belayer/pipeline.js +97 -0
  319. package/dist/server/belayer/pr-lifecycle.js +69 -0
  320. package/dist/server/belayer/prompts.js +154 -0
  321. package/dist/server/belayer/types.js +23 -0
  322. package/dist/server/branch-linker.js +137 -0
  323. package/dist/server/browser-content.js +145 -0
  324. package/dist/server/clipboard.js +63 -0
  325. package/dist/server/codex-hooks-adapter.js +93 -0
  326. package/dist/server/config.js +325 -0
  327. package/dist/server/gh-routes.js +163 -0
  328. package/dist/server/gh.js +276 -0
  329. package/dist/server/git-routes.js +154 -0
  330. package/dist/server/git.js +694 -0
  331. package/dist/server/github-app.js +218 -0
  332. package/dist/server/github-graphql.js +178 -0
  333. package/dist/server/hooks.js +373 -0
  334. package/dist/server/index.js +1549 -0
  335. package/dist/server/integration-github.js +137 -0
  336. package/dist/server/integration-jira.js +210 -0
  337. package/dist/server/integration-linear.js +176 -0
  338. package/dist/server/logger.js +18 -0
  339. package/dist/server/mobile-input-pipeline.js +129 -0
  340. package/dist/server/opencode-relay.js +53 -0
  341. package/dist/server/org-dashboard.js +241 -0
  342. package/dist/server/output-parsers/claude-parser.js +56 -0
  343. package/dist/server/output-parsers/codex-parser.js +13 -0
  344. package/dist/server/output-parsers/index.js +14 -0
  345. package/dist/server/output-parsers/null-parser.js +12 -0
  346. package/dist/server/output-parsers/opencode-parser.js +77 -0
  347. package/dist/server/pty-handler.js +586 -0
  348. package/dist/server/push.js +84 -0
  349. package/dist/server/review-poller.js +237 -0
  350. package/dist/server/sdk-handler.js +539 -0
  351. package/dist/server/service.js +189 -0
  352. package/dist/server/sessions.js +638 -0
  353. package/dist/server/telemetry.js +236 -0
  354. package/dist/server/ticket-transitions.js +166 -0
  355. package/dist/server/types.js +146 -0
  356. package/dist/server/utils.js +23 -0
  357. package/dist/server/watcher.js +661 -0
  358. package/dist/server/webhook-manager.js +547 -0
  359. package/dist/server/webhooks.js +73 -0
  360. package/dist/server/workspace-groups.js +363 -0
  361. package/dist/server/workspaces.js +1207 -0
  362. package/dist/server/ws.js +192 -0
  363. package/dist/test/EmptyState.spec.js +51 -0
  364. package/dist/test/action-coverage.test.js +139 -0
  365. package/dist/test/actions/registry.test.js +59 -0
  366. package/dist/test/actions/shortcuts.test.js +79 -0
  367. package/dist/test/agent-events.test.js +151 -0
  368. package/dist/test/analytics.test.js +158 -0
  369. package/dist/test/attention.test.js +91 -0
  370. package/dist/test/auth.test.js +105 -0
  371. package/dist/test/backend-state.test.js +47 -0
  372. package/dist/test/belayer-executor.test.js +33 -0
  373. package/dist/test/belayer-intake.test.js +44 -0
  374. package/dist/test/belayer-pipeline.test.js +113 -0
  375. package/dist/test/belayer-pr-lifecycle.test.js +26 -0
  376. package/dist/test/belayer-prompts.test.js +60 -0
  377. package/dist/test/belayer-types.test.js +69 -0
  378. package/dist/test/bin/claude-remote-cli.js +214 -0
  379. package/dist/test/boot-state.test.js +133 -0
  380. package/dist/test/branch-lifecycle.test.js +75 -0
  381. package/dist/test/branch-linker.test.js +236 -0
  382. package/dist/test/branch-rename.test.js +45 -0
  383. package/dist/test/branch-watcher.test.js +115 -0
  384. package/dist/test/browser-cli.test.js +91 -0
  385. package/dist/test/browser-content.test.js +93 -0
  386. package/dist/test/browser-tabs-ui.test.js +39 -0
  387. package/dist/test/changed-files-api.test.js +140 -0
  388. package/dist/test/clipboard.test.js +12 -0
  389. package/dist/test/codex-hooks-adapter.test.js +237 -0
  390. package/dist/test/components/EmptyState.spec.js +51 -0
  391. package/dist/test/components/ErrorToast.spec.js +65 -0
  392. package/dist/test/components/TuiCheckbox.spec.js +120 -0
  393. package/dist/test/components/TuiInput.spec.js +186 -0
  394. package/dist/test/components/leaf-component-migration.spec.js +104 -0
  395. package/dist/test/config-freshness.test.js +63 -0
  396. package/dist/test/config.test.js +813 -0
  397. package/dist/test/diff-summary.test.js +98 -0
  398. package/dist/test/display-state.test.js +179 -0
  399. package/dist/test/event-message-types.test.js +32 -0
  400. package/dist/test/file-tree-utils.test.js +167 -0
  401. package/dist/test/framework-types.test.js +183 -0
  402. package/dist/test/frameworks-api.test.js +93 -0
  403. package/dist/test/frontend/src/lib/pr-state.js +114 -0
  404. package/dist/test/fs-browse.test.js +246 -0
  405. package/dist/test/fuzzy-scorer.test.js +145 -0
  406. package/dist/test/gh-routes.test.js +156 -0
  407. package/dist/test/git-changed-files.test.js +152 -0
  408. package/dist/test/git-routes.test.js +146 -0
  409. package/dist/test/git-utils.test.js +68 -0
  410. package/dist/test/git-watcher.test.js +110 -0
  411. package/dist/test/git.test.js +140 -0
  412. package/dist/test/github-app.test.js +455 -0
  413. package/dist/test/github-graphql.test.js +301 -0
  414. package/dist/test/greetings.test.js +83 -0
  415. package/dist/test/hooks-agent-event.test.js +412 -0
  416. package/dist/test/hooks.test.js +149 -0
  417. package/dist/test/integration-github.test.js +220 -0
  418. package/dist/test/integration-jira.test.js +238 -0
  419. package/dist/test/integration-linear.test.js +293 -0
  420. package/dist/test/mobile-input.test.js +235 -0
  421. package/dist/test/opencode-relay.test.js +107 -0
  422. package/dist/test/org-dashboard.test.js +349 -0
  423. package/dist/test/output-parser.test.js +217 -0
  424. package/dist/test/paths.test.js +32 -0
  425. package/dist/test/pr-state.test.js +407 -0
  426. package/dist/test/pr-status.test.js +82 -0
  427. package/dist/test/presets.test.js +242 -0
  428. package/dist/test/pty-handler-multi-agent.test.js +149 -0
  429. package/dist/test/pty-handler.test.js +146 -0
  430. package/dist/test/pull-requests.test.js +78 -0
  431. package/dist/test/review-poller.test.js +349 -0
  432. package/dist/test/server/analytics.js +121 -0
  433. package/dist/test/server/auth.js +63 -0
  434. package/dist/test/server/branch-linker.js +124 -0
  435. package/dist/test/server/clipboard.js +56 -0
  436. package/dist/test/server/config.js +137 -0
  437. package/dist/test/server/git.js +308 -0
  438. package/dist/test/server/hooks.js +196 -0
  439. package/dist/test/server/index.js +1124 -0
  440. package/dist/test/server/integration-github.js +117 -0
  441. package/dist/test/server/integration-jira.js +164 -0
  442. package/dist/test/server/integration-linear.js +176 -0
  443. package/dist/test/server/mobile-input-pipeline.js +123 -0
  444. package/dist/test/server/org-dashboard.js +184 -0
  445. package/dist/test/server/output-parsers/claude-parser.js +54 -0
  446. package/dist/test/server/output-parsers/codex-parser.js +13 -0
  447. package/dist/test/server/output-parsers/index.js +7 -0
  448. package/dist/test/server/pty-handler.js +310 -0
  449. package/dist/test/server/push.js +80 -0
  450. package/dist/test/server/review-poller.js +218 -0
  451. package/dist/test/server/service.js +169 -0
  452. package/dist/test/server/sessions.js +434 -0
  453. package/dist/test/server/ticket-transitions.js +216 -0
  454. package/dist/test/server/types.js +20 -0
  455. package/dist/test/server/utils.js +22 -0
  456. package/dist/test/server/watcher.js +139 -0
  457. package/dist/test/server/workspaces.js +657 -0
  458. package/dist/test/server/ws.js +152 -0
  459. package/dist/test/server-startup.test.js +62 -0
  460. package/dist/test/service.test.js +43 -0
  461. package/dist/test/session-analytics-api.test.js +123 -0
  462. package/dist/test/session-analytics.test.js +425 -0
  463. package/dist/test/session-intent.test.js +249 -0
  464. package/dist/test/sessions.test.js +1152 -0
  465. package/dist/test/sidebar-items.test.js +164 -0
  466. package/dist/test/stores/boot-state-store.test.js +165 -0
  467. package/dist/test/stores/sessions-logic.test.js +191 -0
  468. package/dist/test/stores/toasts-store.test.js +66 -0
  469. package/dist/test/stores/ui-store.test.js +203 -0
  470. package/dist/test/stores/unread-store.test.js +97 -0
  471. package/dist/test/telemetry-api.test.js +54 -0
  472. package/dist/test/telemetry-sync.test.js +68 -0
  473. package/dist/test/telemetry.test.js +295 -0
  474. package/dist/test/terminal-zoom.test.js +102 -0
  475. package/dist/test/test/analytics.test.js +152 -0
  476. package/dist/test/test/auth.test.js +95 -0
  477. package/dist/test/test/branch-linker.test.js +231 -0
  478. package/dist/test/test/branch-rename.test.js +45 -0
  479. package/dist/test/test/clipboard.test.js +12 -0
  480. package/dist/test/test/config.test.js +281 -0
  481. package/dist/test/test/fs-browse.test.js +202 -0
  482. package/dist/test/test/git.test.js +67 -0
  483. package/dist/test/test/hooks.test.js +139 -0
  484. package/dist/test/test/integration-github.test.js +203 -0
  485. package/dist/test/test/integration-jira.test.js +294 -0
  486. package/dist/test/test/integration-linear.test.js +293 -0
  487. package/dist/test/test/mobile-input.test.js +193 -0
  488. package/dist/test/test/org-dashboard.test.js +240 -0
  489. package/dist/test/test/output-parser.test.js +95 -0
  490. package/dist/test/test/paths.test.js +32 -0
  491. package/dist/test/test/pr-state.test.js +220 -0
  492. package/dist/test/test/pull-requests.test.js +67 -0
  493. package/dist/test/test/review-poller.test.js +235 -0
  494. package/dist/test/test/service.test.js +43 -0
  495. package/dist/test/test/sessions.test.js +750 -0
  496. package/dist/test/test/ticket-transitions.test.js +130 -0
  497. package/dist/test/test/version.test.js +34 -0
  498. package/dist/test/test/worktrees.test.js +256 -0
  499. package/dist/test/ticket-transitions.test.js +312 -0
  500. package/dist/test/unread.test.js +23 -0
  501. package/dist/test/version.test.js +34 -0
  502. package/dist/test/webhook-manager.test.js +484 -0
  503. package/dist/test/webhooks.test.js +208 -0
  504. package/dist/test/workspace-groups.test.js +377 -0
  505. package/dist/test/worktrees.test.js +531 -0
  506. package/package.json +88 -0
@@ -0,0 +1,586 @@
1
+ import pty from 'node-pty';
2
+ import crypto from 'node:crypto';
3
+ import fs from 'node:fs';
4
+ import os from 'node:os';
5
+ import path from 'node:path';
6
+ import { AGENT_COMMANDS, AGENT_CONTINUE_ARGS, resolveFramework, } from './types.js';
7
+ import { readMeta, writeMeta } from './config.js';
8
+ import { cleanEnv } from './utils.js';
9
+ import { outputParsers } from './output-parsers/index.js';
10
+ import { installOpencodeRelayPlugin } from './opencode-relay.js';
11
+ import { writeCodexHooksAdapter } from './codex-hooks-adapter.js';
12
+ import { createLogger } from './logger.js';
13
+ const IDLE_TIMEOUT_MS = 5000;
14
+ const MAX_SCROLLBACK = 256 * 1024; // 256KB max
15
+ const logger = createLogger('pty');
16
+ export function getTmuxPrefix() {
17
+ return process.env.NO_PIN === '1' ? 'crcd-' : 'crc-';
18
+ }
19
+ export function generateTmuxSessionName(displayName, id) {
20
+ const sanitized = displayName
21
+ .replace(/[^a-zA-Z0-9-]/g, '-')
22
+ .replace(/-+/g, '-')
23
+ .slice(0, 30);
24
+ return `${getTmuxPrefix()}${sanitized}-${id.slice(0, 8)}`;
25
+ }
26
+ export function resolveTmuxSpawn(command, args, tmuxSessionName) {
27
+ return {
28
+ command: 'tmux',
29
+ args: [
30
+ '-u',
31
+ 'new-session',
32
+ '-s',
33
+ tmuxSessionName,
34
+ '--',
35
+ command,
36
+ ...args,
37
+ ';',
38
+ 'set',
39
+ 'set-clipboard',
40
+ 'on',
41
+ ';',
42
+ 'set',
43
+ 'allow-passthrough',
44
+ 'on',
45
+ ';',
46
+ 'set',
47
+ 'mode-keys',
48
+ 'vi',
49
+ ],
50
+ };
51
+ }
52
+ function shellQuote(value) {
53
+ return "'" + value.replace(/'/g, "'\"'\"'") + "'";
54
+ }
55
+ function readGlobalStatusLineCommand() {
56
+ try {
57
+ const settingsPath = path.join(os.homedir(), '.claude', 'settings.json');
58
+ const raw = fs.readFileSync(settingsPath, 'utf-8');
59
+ const parsed = JSON.parse(raw);
60
+ return typeof parsed.statusLine?.command === 'string'
61
+ ? parsed.statusLine.command
62
+ : '';
63
+ }
64
+ catch {
65
+ return '';
66
+ }
67
+ }
68
+ export function buildStatusLineRelayScript(sessionId, configDir, globalCmd) {
69
+ const telemetryDir = path.join(configDir, 'telemetry');
70
+ const telemetryPath = path.join(telemetryDir, `${sessionId}.json`);
71
+ const tempPattern = `${telemetryPath}.tmp.XXXXXX`;
72
+ return `#!/usr/bin/env bash
73
+ set -u
74
+ mkdir -p ${shellQuote(telemetryDir)}
75
+ tmp_file=$(mktemp ${shellQuote(tempPattern)})
76
+ cleanup() {
77
+ rm -f "$tmp_file"
78
+ }
79
+ trap cleanup EXIT
80
+ GLOBAL_CMD=${shellQuote(globalCmd)}
81
+ if [ -n "$GLOBAL_CMD" ] && [ -x "$GLOBAL_CMD" ]; then
82
+ tee "$tmp_file" | "$GLOBAL_CMD"
83
+ pipeline_statuses=("\${PIPESTATUS[@]}")
84
+ else
85
+ tee "$tmp_file" | node -e 'let raw="";process.stdin.setEncoding("utf8");process.stdin.on("data", (chunk) => raw += chunk);process.stdin.on("end", () => { try { const data = JSON.parse(raw); const model = data?.model?.display_name ?? "Claude"; const remaining = data?.context_window?.remaining_percentage ?? "?"; process.stdout.write(model + " | " + remaining + "% ctx\n"); } catch { process.stdout.write("Claude | ?% ctx\n"); } });'
86
+ pipeline_statuses=("\${PIPESTATUS[@]}")
87
+ fi
88
+
89
+ pipeline_status=0
90
+ for status in "\${pipeline_statuses[@]}"; do
91
+ if [ "$status" -ne 0 ]; then
92
+ pipeline_status=$status
93
+ fi
94
+ done
95
+
96
+ if [ "\${pipeline_statuses[0]}" -eq 0 ]; then
97
+ mv "$tmp_file" ${shellQuote(telemetryPath)}
98
+ trap - EXIT
99
+ fi
100
+
101
+ exit "$pipeline_status"
102
+ `;
103
+ }
104
+ function writeStatusLineScript(sessionId, dir, configDir) {
105
+ const scriptPath = path.join(dir, 'relay-statusline.sh');
106
+ const script = buildStatusLineRelayScript(sessionId, configDir, readGlobalStatusLineCommand());
107
+ fs.writeFileSync(scriptPath, script, 'utf-8');
108
+ fs.chmodSync(scriptPath, 0o755);
109
+ return scriptPath;
110
+ }
111
+ /**
112
+ * Upgrade an existing hooks-settings.json to include statusLine if missing.
113
+ * Called on session restore to ensure sessions created before telemetry support
114
+ * get the relay script written to disk. The running Claude process will pick this
115
+ * up on its next statusLine poll cycle (Claude re-reads the settings file).
116
+ */
117
+ export function upgradeHooksSettings(sessionId, configDir) {
118
+ const dir = path.join(os.tmpdir(), 'relay-ide', sessionId);
119
+ const filePath = path.join(dir, 'hooks-settings.json');
120
+ try {
121
+ const raw = fs.readFileSync(filePath, 'utf-8');
122
+ const settings = JSON.parse(raw);
123
+ if (settings.statusLine)
124
+ return false; // already has statusLine
125
+ }
126
+ catch {
127
+ return false; // file doesn't exist or is malformed
128
+ }
129
+ // Write the relay script and patch the settings file
130
+ try {
131
+ const statusLinePath = writeStatusLineScript(sessionId, dir, configDir);
132
+ const raw = fs.readFileSync(filePath, 'utf-8');
133
+ const settings = JSON.parse(raw);
134
+ settings.statusLine = { type: 'command', command: statusLinePath };
135
+ fs.writeFileSync(filePath, JSON.stringify(settings, null, 2), 'utf-8');
136
+ return true;
137
+ }
138
+ catch (err) {
139
+ logger.warn(`Failed to upgrade hooks settings for session ${sessionId}:`, err);
140
+ return false;
141
+ }
142
+ }
143
+ function writeHooksSettingsFile(sessionId, port, token, configDir) {
144
+ const dir = path.join(os.tmpdir(), 'relay-ide', sessionId);
145
+ fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
146
+ const filePath = path.join(dir, 'hooks-settings.json');
147
+ const statusLinePath = writeStatusLineScript(sessionId, dir, configDir);
148
+ const base = `http://127.0.0.1:${port}`;
149
+ const q = `sessionId=${sessionId}&token=${token}`;
150
+ const settings = {
151
+ hooks: {
152
+ Stop: [
153
+ {
154
+ hooks: [{ type: 'http', url: `${base}/hooks/stop?${q}`, timeout: 5 }],
155
+ },
156
+ ],
157
+ Notification: [
158
+ {
159
+ matcher: 'permission_prompt',
160
+ hooks: [
161
+ {
162
+ type: 'http',
163
+ url: `${base}/hooks/notification?${q}&type=permission_prompt`,
164
+ timeout: 5,
165
+ },
166
+ ],
167
+ },
168
+ {
169
+ matcher: 'idle_prompt',
170
+ hooks: [
171
+ {
172
+ type: 'http',
173
+ url: `${base}/hooks/notification?${q}&type=idle_prompt`,
174
+ timeout: 5,
175
+ },
176
+ ],
177
+ },
178
+ ],
179
+ UserPromptSubmit: [
180
+ {
181
+ hooks: [
182
+ {
183
+ type: 'http',
184
+ url: `${base}/hooks/prompt-submit?${q}`,
185
+ timeout: 5,
186
+ },
187
+ ],
188
+ },
189
+ ],
190
+ SessionEnd: [
191
+ {
192
+ hooks: [
193
+ { type: 'http', url: `${base}/hooks/session-end?${q}`, timeout: 5 },
194
+ ],
195
+ },
196
+ ],
197
+ PreToolUse: [
198
+ {
199
+ hooks: [
200
+ { type: 'http', url: `${base}/hooks/tool-use?${q}`, timeout: 5 },
201
+ ],
202
+ },
203
+ ],
204
+ PostToolUse: [
205
+ {
206
+ hooks: [
207
+ { type: 'http', url: `${base}/hooks/tool-result?${q}`, timeout: 5 },
208
+ ],
209
+ },
210
+ ],
211
+ },
212
+ statusLine: {
213
+ type: 'command',
214
+ command: statusLinePath,
215
+ },
216
+ };
217
+ fs.writeFileSync(filePath, JSON.stringify(settings, null, 2), 'utf-8');
218
+ fs.chmodSync(filePath, 0o600);
219
+ return filePath;
220
+ }
221
+ export function createPtySession(params, sessionsMap, stateChangeCallbacks = [], sessionEndCallbacks = [], fireBackendStateIfChanged) {
222
+ const { id, type, agent = 'claude', repoName, repoPath, worktreePath = null, cwd, branchName, displayName, command, args: rawArgs = [], cols = 80, rows = 24, configPath, configDir, useTmux: paramUseTmux, tmuxSessionName: paramTmuxSessionName, initialScrollback, restored: paramRestored, port, forceOutputParser, yolo: paramYolo, claudeArgs: paramClaudeArgs, hookToken: paramHookToken, hooksActive: paramHooksActive, frameworks, } = params;
223
+ let args = rawArgs;
224
+ const createdAt = new Date().toISOString();
225
+ // Resolve the agent framework (builtin or future config-driven custom)
226
+ // Falls back to a minimal stub using deprecated aliases when agent is not a known framework.
227
+ let framework;
228
+ try {
229
+ framework = resolveFramework(frameworks ? { frameworks } : {}, agent);
230
+ }
231
+ catch {
232
+ // Unknown agent — synthesize a minimal framework from deprecated aliases for backward compat
233
+ framework = {
234
+ id: agent,
235
+ displayName: agent,
236
+ command: AGENT_COMMANDS[agent] ?? agent,
237
+ continueArgs: AGENT_CONTINUE_ARGS[agent] ?? [],
238
+ yoloArgs: [],
239
+ parserType: 'none',
240
+ eventSource: 'parser',
241
+ capabilities: {
242
+ supportsHooks: false,
243
+ supportsContinue: false,
244
+ supportsYolo: false,
245
+ supportsTelemetry: false,
246
+ },
247
+ };
248
+ }
249
+ const resolvedCommand = command || framework.commandOverride || framework.command;
250
+ const env = cleanEnv();
251
+ // Inject Claude --settings hooks file for frameworks that use HTTP hook callbacks.
252
+ // This is the Claude-specific hook mechanism; codex uses hooks.json (Task 7) and
253
+ // opencode uses a TS relay plugin (Task 6) — each with its own injection path.
254
+ // For restored sessions whose tmux died, reuse the preserved token but re-create the settings
255
+ // file on disk — the new Claude process needs --settings even though the token is the same.
256
+ // For surviving tmux sessions (command='tmux'), shouldInjectHooks is false — the old Claude
257
+ // instance already has its settings file, and we just need the token for server-side validation.
258
+ let hookToken = paramHookToken ?? '';
259
+ let hooksActive = paramHooksActive ?? false;
260
+ let settingsPath = '';
261
+ const effectiveEventSource = forceOutputParser
262
+ ? 'parser'
263
+ : framework.eventSource;
264
+ if (paramYolo && framework.yoloEnv) {
265
+ Object.assign(env, framework.yoloEnv);
266
+ }
267
+ if (effectiveEventSource === 'plugin' && port !== undefined) {
268
+ if (!hookToken) {
269
+ hookToken = crypto.randomBytes(32).toString('hex');
270
+ }
271
+ try {
272
+ installOpencodeRelayPlugin();
273
+ env.CRC_RELAY_URL = `http://127.0.0.1:${port}`;
274
+ env.CRC_SESSION_ID = id;
275
+ env.CRC_RELAY_TOKEN = hookToken;
276
+ hooksActive = true;
277
+ }
278
+ catch (err) {
279
+ logger.warn(`Failed to install opencode relay plugin for session ${id}:`, err);
280
+ }
281
+ }
282
+ if (framework.id === 'codex' && port !== undefined) {
283
+ if (!hookToken) {
284
+ hookToken = crypto.randomBytes(32).toString('hex');
285
+ }
286
+ try {
287
+ const codexConfigDir = writeCodexHooksAdapter(id, port, hookToken, configDir ?? process.cwd());
288
+ env.CODEX_CONFIG_DIR = codexConfigDir;
289
+ hooksActive = true;
290
+ }
291
+ catch (err) {
292
+ logger.warn(`Failed to write codex hooks adapter for session ${id}:`, err);
293
+ }
294
+ }
295
+ // Only inject --settings for claude; codex and opencode have their own hook injection paths
296
+ const shouldInjectHooks = framework.id === 'claude' &&
297
+ framework.capabilities.supportsHooks &&
298
+ effectiveEventSource === 'hooks' &&
299
+ !command &&
300
+ port !== undefined;
301
+ if (shouldInjectHooks) {
302
+ if (!hookToken) {
303
+ hookToken = crypto.randomBytes(32).toString('hex');
304
+ }
305
+ try {
306
+ settingsPath = writeHooksSettingsFile(id, port, hookToken, configDir ?? process.cwd());
307
+ args = ['--settings', settingsPath, ...args];
308
+ hooksActive = true;
309
+ }
310
+ catch (err) {
311
+ logger.warn(`Failed to generate hooks settings for session ${id}:`, err);
312
+ hooksActive = false;
313
+ hookToken = '';
314
+ }
315
+ }
316
+ const useTmux = !command && !!paramUseTmux;
317
+ let spawnCommand = resolvedCommand;
318
+ let spawnArgs = args;
319
+ const tmuxSessionName = paramTmuxSessionName ||
320
+ (useTmux
321
+ ? generateTmuxSessionName(params.tmuxDisplayName ||
322
+ displayName ||
323
+ repoName ||
324
+ path.basename(cwd) ||
325
+ 'session', id)
326
+ : '');
327
+ if (useTmux) {
328
+ const tmux = resolveTmuxSpawn(resolvedCommand, args, tmuxSessionName);
329
+ spawnCommand = tmux.command;
330
+ spawnArgs = tmux.args;
331
+ }
332
+ const ptyProcess = pty.spawn(spawnCommand, spawnArgs, {
333
+ name: 'xterm-256color',
334
+ cols,
335
+ rows,
336
+ cwd,
337
+ env,
338
+ });
339
+ // Scrollback buffer: stores all PTY output so we can replay on WebSocket (re)connect
340
+ const scrollback = initialScrollback ? [...initialScrollback] : [];
341
+ let scrollbackBytes = initialScrollback
342
+ ? initialScrollback.reduce((sum, s) => sum + s.length, 0)
343
+ : 0;
344
+ // Instantiate vendor-specific output parser dispatched by framework.parserType
345
+ // Falls back to 'none' parser for unknown parserType values
346
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
347
+ const parserFactory = outputParsers[framework.parserType] ?? outputParsers['none'];
348
+ const parser = parserFactory();
349
+ const session = {
350
+ id,
351
+ type: type || 'agent',
352
+ agent,
353
+ mode: 'pty',
354
+ repoPath: repoPath || '',
355
+ worktreePath: worktreePath ?? null,
356
+ repoName: repoName || '',
357
+ branchName: branchName || '',
358
+ displayName: displayName || repoName || path.basename(cwd) || '',
359
+ pty: ptyProcess,
360
+ createdAt,
361
+ lastActivity: createdAt,
362
+ scrollback,
363
+ idle: false,
364
+ cwd,
365
+ customCommand: command || null,
366
+ useTmux,
367
+ tmuxSessionName,
368
+ onPtyReplacedCallbacks: [],
369
+ status: 'active',
370
+ restored: paramRestored || false,
371
+ needsBranchRename: false,
372
+ agentState: 'initializing',
373
+ outputParser: parser,
374
+ hookToken,
375
+ hooksActive,
376
+ cleanedUp: false,
377
+ yolo: paramYolo ?? false,
378
+ sessionArgs: paramClaudeArgs ?? [],
379
+ claudeArgs: paramClaudeArgs ?? [], // keep for backward compat
380
+ continuePolicy: params.continuePolicy ?? 'never',
381
+ // Derive dataQuality from what actually got enabled, not just the configured framework eventSource.
382
+ // If hook/plugin injection failed, hooksActive is false → downgrade to 'parser' for accuracy.
383
+ dataQuality: hooksActive ? effectiveEventSource : 'parser',
384
+ _lastHookTime: undefined,
385
+ };
386
+ sessionsMap.set(id, session);
387
+ // Load existing metadata to preserve a previously-set displayName
388
+ if (configPath && worktreePath) {
389
+ const existing = readMeta(configPath, worktreePath);
390
+ if (existing && existing.displayName) {
391
+ session.displayName = existing.displayName;
392
+ }
393
+ writeMeta(configPath, {
394
+ worktreePath,
395
+ displayName: session.displayName,
396
+ lastActivity: createdAt,
397
+ });
398
+ }
399
+ let metaFlushTimer = null;
400
+ let idleTimer = null;
401
+ function resetIdleTimer() {
402
+ if (session.idle) {
403
+ session.idle = false;
404
+ fireBackendStateIfChanged?.(session);
405
+ }
406
+ if (idleTimer)
407
+ clearTimeout(idleTimer);
408
+ idleTimer = setTimeout(() => {
409
+ if (!session.idle) {
410
+ session.idle = true;
411
+ fireBackendStateIfChanged?.(session);
412
+ }
413
+ }, IDLE_TIMEOUT_MS);
414
+ }
415
+ const continueArgs = framework.continueArgs;
416
+ function attachHandlers(proc, canRetry) {
417
+ const spawnTime = Date.now();
418
+ // Clear restored flag after 3s of running — means the PTY is healthy
419
+ const restoredClearTimer = session.restored
420
+ ? setTimeout(() => {
421
+ session.restored = false;
422
+ }, 3000)
423
+ : null;
424
+ proc.onData((data) => {
425
+ session.lastActivity = new Date().toISOString();
426
+ resetIdleTimer();
427
+ scrollback.push(data);
428
+ scrollbackBytes += data.length;
429
+ // Trim oldest entries if over limit
430
+ while (scrollbackBytes > MAX_SCROLLBACK && scrollback.length > 1) {
431
+ scrollbackBytes -= scrollback.shift().length;
432
+ }
433
+ if (configPath && worktreePath && !metaFlushTimer) {
434
+ metaFlushTimer = setTimeout(() => {
435
+ metaFlushTimer = null;
436
+ writeMeta(configPath, {
437
+ worktreePath,
438
+ displayName: session.displayName,
439
+ lastActivity: session.lastActivity,
440
+ });
441
+ }, 5000);
442
+ }
443
+ // Vendor-specific output parsing for semantic state detection
444
+ const parseResult = session.outputParser.onData(data, scrollback.slice(-20));
445
+ if (parseResult && parseResult.state !== session.agentState) {
446
+ if (session.hooksActive) {
447
+ // Hooks are authoritative — check 30s reconciliation timeout
448
+ const lastHook = session._lastHookTime;
449
+ const sessionAge = Date.now() - new Date(session.createdAt).getTime();
450
+ if (lastHook && Date.now() - lastHook > 30000) {
451
+ // No hook for 30s and parser disagrees — parser overrides
452
+ session.agentState = parseResult.state;
453
+ for (const cb of stateChangeCallbacks)
454
+ cb(session.id, parseResult.state);
455
+ fireBackendStateIfChanged?.(session);
456
+ }
457
+ else if (!lastHook && sessionAge > 30000) {
458
+ // Hooks active but never fired in 30s — allow parser to override to prevent permanent suppression
459
+ session.agentState = parseResult.state;
460
+ for (const cb of stateChangeCallbacks)
461
+ cb(session.id, parseResult.state);
462
+ fireBackendStateIfChanged?.(session);
463
+ }
464
+ // else: suppress parser — hooks are still fresh
465
+ }
466
+ else {
467
+ // No hooks — parser is primary (current behavior)
468
+ session.agentState = parseResult.state;
469
+ for (const cb of stateChangeCallbacks)
470
+ cb(session.id, parseResult.state);
471
+ fireBackendStateIfChanged?.(session);
472
+ }
473
+ }
474
+ });
475
+ proc.onExit(() => {
476
+ if (canRetry && Date.now() - spawnTime < 3000) {
477
+ let retryArgs = rawArgs.filter((a) => !continueArgs.includes(a));
478
+ // Re-inject hooks settings if active (settingsPath captured from outer scope)
479
+ if (session.hooksActive && settingsPath) {
480
+ retryArgs = ['--settings', settingsPath, ...retryArgs];
481
+ }
482
+ const retryNotice = '\r\n[relay-ide] --continue not available; starting new session...\r\n';
483
+ scrollback.length = 0;
484
+ scrollbackBytes = 0;
485
+ scrollback.push(retryNotice);
486
+ scrollbackBytes = retryNotice.length;
487
+ let retryCommand = resolvedCommand;
488
+ let retrySpawnArgs = retryArgs;
489
+ if (useTmux && tmuxSessionName) {
490
+ const retryTmuxName = tmuxSessionName + '-retry';
491
+ session.tmuxSessionName = retryTmuxName;
492
+ const tmux = resolveTmuxSpawn(resolvedCommand, retryArgs, retryTmuxName);
493
+ retryCommand = tmux.command;
494
+ retrySpawnArgs = tmux.args;
495
+ }
496
+ let retryPty;
497
+ try {
498
+ retryPty = pty.spawn(retryCommand, retrySpawnArgs, {
499
+ name: 'xterm-256color',
500
+ cols,
501
+ rows,
502
+ cwd,
503
+ env,
504
+ });
505
+ }
506
+ catch {
507
+ // Retry spawn failed — fall through to normal exit cleanup
508
+ if (restoredClearTimer)
509
+ clearTimeout(restoredClearTimer);
510
+ if (idleTimer)
511
+ clearTimeout(idleTimer);
512
+ if (metaFlushTimer)
513
+ clearTimeout(metaFlushTimer);
514
+ sessionsMap.delete(id);
515
+ return;
516
+ }
517
+ session.pty = retryPty;
518
+ for (const cb of session.onPtyReplacedCallbacks)
519
+ cb(retryPty);
520
+ attachHandlers(retryPty, false);
521
+ return;
522
+ }
523
+ if (session.cleanedUp)
524
+ return; // Dedup: SessionEnd hook already cleaned up
525
+ session.cleanedUp = true;
526
+ if (restoredClearTimer)
527
+ clearTimeout(restoredClearTimer);
528
+ // If PTY exited and this is a restored session, mark disconnected rather than delete
529
+ if (session.restored) {
530
+ session.status = 'disconnected';
531
+ session.restored = false; // clear so user-initiated kills can delete normally
532
+ if (idleTimer)
533
+ clearTimeout(idleTimer);
534
+ if (metaFlushTimer)
535
+ clearTimeout(metaFlushTimer);
536
+ return;
537
+ }
538
+ if (idleTimer)
539
+ clearTimeout(idleTimer);
540
+ if (metaFlushTimer)
541
+ clearTimeout(metaFlushTimer);
542
+ if (configPath && worktreePath) {
543
+ writeMeta(configPath, {
544
+ worktreePath,
545
+ displayName: session.displayName,
546
+ lastActivity: session.lastActivity,
547
+ });
548
+ }
549
+ for (const cb of sessionEndCallbacks) {
550
+ try {
551
+ cb(id, cwd, session.branchName);
552
+ }
553
+ catch (err) {
554
+ logger.error('sessionEnd callback error:', err);
555
+ }
556
+ }
557
+ sessionsMap.delete(id);
558
+ const tmpDir = path.join(os.tmpdir(), 'relay-ide', id);
559
+ fs.rm(tmpDir, { recursive: true, force: true }, () => { });
560
+ });
561
+ }
562
+ attachHandlers(ptyProcess, continueArgs.some((a) => args.includes(a)));
563
+ const result = {
564
+ id,
565
+ type: session.type,
566
+ agent: session.agent,
567
+ mode: 'pty',
568
+ repoPath: session.repoPath,
569
+ worktreePath: session.worktreePath,
570
+ repoName: session.repoName,
571
+ branchName: session.branchName,
572
+ displayName: session.displayName,
573
+ pid: ptyProcess.pid,
574
+ createdAt,
575
+ lastActivity: createdAt,
576
+ idle: false,
577
+ cwd,
578
+ customCommand: command || null,
579
+ useTmux,
580
+ tmuxSessionName,
581
+ status: 'active',
582
+ needsBranchRename: false,
583
+ agentState: 'initializing',
584
+ };
585
+ return { session, result };
586
+ }
@@ -0,0 +1,84 @@
1
+ import webpush from 'web-push';
2
+ let vapidPublicKey = null;
3
+ const subscriptions = new Map();
4
+ const MAX_PAYLOAD_SIZE = 4 * 1024; // 4KB
5
+ export function ensureVapidKeys(config, configPath, save) {
6
+ if (config.vapidPublicKey && config.vapidPrivateKey) {
7
+ vapidPublicKey = config.vapidPublicKey;
8
+ webpush.setVapidDetails('mailto:noreply@relay-ide.local', config.vapidPublicKey, config.vapidPrivateKey);
9
+ return;
10
+ }
11
+ try {
12
+ const keys = webpush.generateVAPIDKeys();
13
+ config.vapidPublicKey = keys.publicKey;
14
+ config.vapidPrivateKey = keys.privateKey;
15
+ save(configPath, config);
16
+ vapidPublicKey = keys.publicKey;
17
+ webpush.setVapidDetails('mailto:noreply@relay-ide.local', keys.publicKey, keys.privateKey);
18
+ }
19
+ catch {
20
+ // VAPID key generation failed — push will be unavailable
21
+ vapidPublicKey = null;
22
+ }
23
+ }
24
+ export function getVapidPublicKey() {
25
+ return vapidPublicKey;
26
+ }
27
+ export function subscribe(subscription, sessionIds) {
28
+ // Replace the full session list for this endpoint — the client sends
29
+ // the complete set of sessions it wants notifications for.
30
+ subscriptions.set(subscription.endpoint, {
31
+ subscription,
32
+ sessionIds: new Set(sessionIds),
33
+ });
34
+ }
35
+ export function unsubscribe(endpoint) {
36
+ subscriptions.delete(endpoint);
37
+ }
38
+ export function removeSession(sessionId) {
39
+ for (const entry of subscriptions.values()) {
40
+ entry.sessionIds.delete(sessionId);
41
+ }
42
+ }
43
+ function truncatePayload(payload) {
44
+ if (payload.length <= MAX_PAYLOAD_SIZE)
45
+ return payload;
46
+ // Try to parse, truncate text fields, and re-serialize
47
+ try {
48
+ const obj = JSON.parse(payload);
49
+ if (typeof obj.enrichedMessage === 'string' &&
50
+ obj.enrichedMessage.length > 100) {
51
+ obj.enrichedMessage =
52
+ obj.enrichedMessage.slice(0, 100) + '...';
53
+ }
54
+ const truncated = JSON.stringify(obj);
55
+ if (truncated.length <= MAX_PAYLOAD_SIZE)
56
+ return truncated;
57
+ }
58
+ catch {
59
+ // fall through
60
+ }
61
+ return payload.slice(0, MAX_PAYLOAD_SIZE);
62
+ }
63
+ export function notifySessionAttention(sessionId, session) {
64
+ if (!vapidPublicKey)
65
+ return;
66
+ const payloadObj = {
67
+ type: 'session-attention',
68
+ sessionId,
69
+ displayName: session.displayName,
70
+ sessionType: session.type,
71
+ };
72
+ const payload = truncatePayload(JSON.stringify(payloadObj));
73
+ for (const [endpoint, entry] of subscriptions) {
74
+ if (!entry.sessionIds.has(sessionId))
75
+ continue;
76
+ webpush
77
+ .sendNotification(entry.subscription, payload)
78
+ .catch((err) => {
79
+ if (err.statusCode === 410 || err.statusCode === 404) {
80
+ subscriptions.delete(endpoint);
81
+ }
82
+ });
83
+ }
84
+ }