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,242 @@
1
+ import { test, describe, before, after, afterEach } from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import fs from 'node:fs';
4
+ import path from 'node:path';
5
+ import os from 'node:os';
6
+ import { DEFAULT_PRESETS, DEFAULTS, loadConfig, saveConfig, } from '../server/config.js';
7
+ // ---------------------------------------------------------------------------
8
+ // Helpers
9
+ // ---------------------------------------------------------------------------
10
+ let tmpDir;
11
+ function makeConfigPath() {
12
+ return path.join(tmpDir, 'config.json');
13
+ }
14
+ /**
15
+ * Applies the same validation logic used in POST /presets (server/index.ts).
16
+ * Returns an error string or null if valid.
17
+ */
18
+ function validatePreset(body) {
19
+ const { name, sort } = body;
20
+ if (!name || typeof name !== 'string' || !name.trim()) {
21
+ return 'name is required';
22
+ }
23
+ if (sort && typeof sort === 'object') {
24
+ const dir = sort.direction;
25
+ if (dir !== 'asc' && dir !== 'desc') {
26
+ return 'sort.direction must be "asc" or "desc"';
27
+ }
28
+ }
29
+ return null;
30
+ }
31
+ /**
32
+ * Applies the same deletion guard used in DELETE /presets/:name (server/index.ts).
33
+ * Returns an error string or null if deletion is allowed.
34
+ */
35
+ function validateDeletion(presets, name) {
36
+ const target = presets.find((p) => p.name === name);
37
+ if (!target) {
38
+ return 'Preset not found';
39
+ }
40
+ if (target.builtIn) {
41
+ return 'Cannot delete a built-in preset';
42
+ }
43
+ return null;
44
+ }
45
+ // ---------------------------------------------------------------------------
46
+ // Setup / teardown
47
+ // ---------------------------------------------------------------------------
48
+ before(() => {
49
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'preset-test-'));
50
+ });
51
+ afterEach(() => {
52
+ for (const entry of fs.readdirSync(tmpDir, { withFileTypes: true })) {
53
+ const fullPath = path.join(tmpDir, entry.name);
54
+ if (entry.isDirectory()) {
55
+ fs.rmSync(fullPath, { recursive: true });
56
+ }
57
+ else {
58
+ fs.unlinkSync(fullPath);
59
+ }
60
+ }
61
+ });
62
+ after(() => {
63
+ fs.rmdirSync(tmpDir);
64
+ });
65
+ // ---------------------------------------------------------------------------
66
+ // Tests — DEFAULT_PRESETS
67
+ // ---------------------------------------------------------------------------
68
+ describe('DEFAULT_PRESETS', () => {
69
+ test('contains at least two built-in presets', () => {
70
+ assert.ok(DEFAULT_PRESETS.length >= 2, 'should have at least 2 default presets');
71
+ });
72
+ test('all default presets are marked builtIn', () => {
73
+ for (const preset of DEFAULT_PRESETS) {
74
+ assert.equal(preset.builtIn, true, `preset "${preset.name}" should be builtIn`);
75
+ }
76
+ });
77
+ test('all default presets have valid sort direction', () => {
78
+ for (const preset of DEFAULT_PRESETS) {
79
+ assert.ok(preset.sort.direction === 'asc' || preset.sort.direction === 'desc', `preset "${preset.name}" has invalid sort.direction: ${preset.sort.direction}`);
80
+ }
81
+ });
82
+ });
83
+ // ---------------------------------------------------------------------------
84
+ // Tests — loadConfig sets default presets when absent
85
+ // ---------------------------------------------------------------------------
86
+ describe('loadConfig preset initialisation', () => {
87
+ test('injects DEFAULT_PRESETS when filterPresets is absent from config file', () => {
88
+ const configPath = makeConfigPath();
89
+ fs.writeFileSync(configPath, JSON.stringify({ port: 3456 }), 'utf8');
90
+ const config = loadConfig(configPath);
91
+ assert.deepEqual(config.filterPresets, DEFAULT_PRESETS);
92
+ });
93
+ test('preserves user-supplied filterPresets from config file', () => {
94
+ const configPath = makeConfigPath();
95
+ const userPresets = [
96
+ {
97
+ name: 'My Preset',
98
+ filters: {},
99
+ sort: { column: 'age', direction: 'asc' },
100
+ },
101
+ ];
102
+ fs.writeFileSync(configPath, JSON.stringify({ port: 3456, filterPresets: userPresets }), 'utf8');
103
+ const config = loadConfig(configPath);
104
+ assert.deepEqual(config.filterPresets, userPresets);
105
+ });
106
+ });
107
+ // ---------------------------------------------------------------------------
108
+ // Tests — POST /presets validation logic
109
+ // ---------------------------------------------------------------------------
110
+ describe('preset creation validation', () => {
111
+ test('rejects missing name', () => {
112
+ assert.equal(validatePreset({ sort: { direction: 'asc' } }), 'name is required');
113
+ });
114
+ test('rejects empty string name', () => {
115
+ assert.equal(validatePreset({ name: '' }), 'name is required');
116
+ });
117
+ test('rejects whitespace-only name', () => {
118
+ assert.equal(validatePreset({ name: ' ' }), 'name is required');
119
+ });
120
+ test('rejects invalid sort.direction', () => {
121
+ assert.equal(validatePreset({ name: 'Valid Name', sort: { direction: 'invalid' } }), 'sort.direction must be "asc" or "desc"');
122
+ });
123
+ test('accepts valid name with asc direction', () => {
124
+ assert.equal(validatePreset({ name: 'My Preset', sort: { direction: 'asc' } }), null);
125
+ });
126
+ test('accepts valid name with desc direction', () => {
127
+ assert.equal(validatePreset({ name: 'My Preset', sort: { direction: 'desc' } }), null);
128
+ });
129
+ test('accepts valid name with no sort (sort is optional)', () => {
130
+ assert.equal(validatePreset({ name: 'My Preset' }), null);
131
+ });
132
+ });
133
+ // ---------------------------------------------------------------------------
134
+ // Tests — preset storage (push + saveConfig round-trip)
135
+ // ---------------------------------------------------------------------------
136
+ describe('preset storage', () => {
137
+ test('new preset is persisted via saveConfig and reloaded by loadConfig', () => {
138
+ const configPath = makeConfigPath();
139
+ saveConfig(configPath, {
140
+ ...DEFAULTS,
141
+ filterPresets: [...DEFAULT_PRESETS],
142
+ });
143
+ const config = loadConfig(configPath);
144
+ const newPreset = {
145
+ name: 'Team Review',
146
+ filters: { role: ['reviewer'] },
147
+ sort: { column: 'age', direction: 'desc' },
148
+ };
149
+ config.filterPresets.push(newPreset);
150
+ saveConfig(configPath, config);
151
+ const reloaded = loadConfig(configPath);
152
+ const found = reloaded.filterPresets?.find((p) => p.name === 'Team Review');
153
+ assert.ok(found, 'saved preset should be present after reload');
154
+ assert.deepEqual(found, newPreset);
155
+ });
156
+ test('duplicate name can be pushed (endpoint does not deduplicate — caller responsibility)', () => {
157
+ // The server endpoint does not currently check for duplicate names,
158
+ // so both entries will be stored. This test documents that behaviour.
159
+ const configPath = makeConfigPath();
160
+ saveConfig(configPath, {
161
+ ...DEFAULTS,
162
+ filterPresets: [...DEFAULT_PRESETS],
163
+ });
164
+ const config = loadConfig(configPath);
165
+ const preset = {
166
+ name: 'Dupe',
167
+ filters: {},
168
+ sort: { column: 'role', direction: 'asc' },
169
+ };
170
+ config.filterPresets.push(preset);
171
+ config.filterPresets.push(preset);
172
+ saveConfig(configPath, config);
173
+ const reloaded = loadConfig(configPath);
174
+ const matches = reloaded.filterPresets?.filter((p) => p.name === 'Dupe') ?? [];
175
+ assert.equal(matches.length, 2, 'both duplicate entries should be stored');
176
+ });
177
+ });
178
+ // ---------------------------------------------------------------------------
179
+ // Tests — DELETE /presets validation logic
180
+ // ---------------------------------------------------------------------------
181
+ describe('preset deletion validation', () => {
182
+ test('rejects deletion of a built-in preset', () => {
183
+ const presets = [
184
+ {
185
+ name: 'All PRs',
186
+ builtIn: true,
187
+ filters: {},
188
+ sort: { column: 'age', direction: 'desc' },
189
+ },
190
+ ];
191
+ assert.equal(validateDeletion(presets, 'All PRs'), 'Cannot delete a built-in preset');
192
+ });
193
+ test('rejects deletion of a preset that does not exist', () => {
194
+ const presets = [
195
+ {
196
+ name: 'All PRs',
197
+ builtIn: true,
198
+ filters: {},
199
+ sort: { column: 'age', direction: 'desc' },
200
+ },
201
+ ];
202
+ assert.equal(validateDeletion(presets, 'Nonexistent'), 'Preset not found');
203
+ });
204
+ test('allows deletion of a user-created preset', () => {
205
+ const presets = [
206
+ {
207
+ name: 'All PRs',
208
+ builtIn: true,
209
+ filters: {},
210
+ sort: { column: 'age', direction: 'desc' },
211
+ },
212
+ {
213
+ name: 'My Custom',
214
+ filters: {},
215
+ sort: { column: 'role', direction: 'asc' },
216
+ },
217
+ ];
218
+ assert.equal(validateDeletion(presets, 'My Custom'), null);
219
+ });
220
+ test('deletion removes exactly the named preset and leaves others intact', () => {
221
+ const configPath = makeConfigPath();
222
+ const initialPresets = [
223
+ ...DEFAULT_PRESETS,
224
+ {
225
+ name: 'To Delete',
226
+ filters: {},
227
+ sort: { column: 'role', direction: 'asc' },
228
+ },
229
+ ];
230
+ saveConfig(configPath, { ...DEFAULTS, filterPresets: initialPresets });
231
+ const config = loadConfig(configPath);
232
+ config.filterPresets = config.filterPresets.filter((p) => p.name !== 'To Delete');
233
+ saveConfig(configPath, config);
234
+ const reloaded = loadConfig(configPath);
235
+ const remaining = reloaded.filterPresets ?? [];
236
+ assert.equal(remaining.find((p) => p.name === 'To Delete'), undefined, 'deleted preset should not exist');
237
+ // Built-in presets survive
238
+ for (const dp of DEFAULT_PRESETS) {
239
+ assert.ok(remaining.find((p) => p.name === dp.name), `built-in preset "${dp.name}" should still be present`);
240
+ }
241
+ });
242
+ });
@@ -0,0 +1,149 @@
1
+ import { afterEach, beforeEach, describe, it } from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import fs from 'node:fs';
4
+ import os from 'node:os';
5
+ import path from 'node:path';
6
+ import * as sessions from '../server/sessions.js';
7
+ const createdIds = [];
8
+ function delay(ms) {
9
+ return new Promise((resolve) => setTimeout(resolve, ms));
10
+ }
11
+ async function waitForScrollbackContains(sessionId, needle, timeoutMs = 4000) {
12
+ const deadline = Date.now() + timeoutMs;
13
+ while (Date.now() < deadline) {
14
+ const session = sessions.get(sessionId);
15
+ if (session) {
16
+ const output = session.scrollback.join('');
17
+ if (output.includes(needle))
18
+ return output;
19
+ }
20
+ await delay(50);
21
+ }
22
+ throw new Error(`Timed out waiting for scrollback to contain: ${needle}`);
23
+ }
24
+ describe('PTY multi-agent hook/plugin wiring', () => {
25
+ const originalHome = process.env.HOME;
26
+ let testHome;
27
+ beforeEach(() => {
28
+ testHome = fs.mkdtempSync(path.join(os.tmpdir(), 'crc-multi-agent-home-'));
29
+ process.env.HOME = testHome;
30
+ });
31
+ afterEach(() => {
32
+ for (const id of createdIds) {
33
+ try {
34
+ if (sessions.get(id))
35
+ sessions.kill(id);
36
+ }
37
+ catch { }
38
+ }
39
+ createdIds.length = 0;
40
+ process.env.HOME = originalHome;
41
+ fs.rmSync(testHome, { recursive: true, force: true });
42
+ });
43
+ it('installs OpenCode relay plugin and injects relay env vars', async () => {
44
+ const port = 4567;
45
+ const hookToken = 'opencode-hook-token';
46
+ const result = sessions.create({
47
+ repoName: 'test-repo',
48
+ repoPath: '/tmp',
49
+ worktreePath: null,
50
+ cwd: '/tmp',
51
+ agent: 'opencode',
52
+ command: process.execPath,
53
+ args: [
54
+ '-e',
55
+ [
56
+ "const fs=require('node:fs');",
57
+ "const os=require('node:os');",
58
+ "const path=require('node:path');",
59
+ "console.log('CRC_RELAY_URL='+(process.env.CRC_RELAY_URL||''));",
60
+ "console.log('CRC_SESSION_ID='+(process.env.CRC_SESSION_ID||''));",
61
+ "console.log('CRC_RELAY_TOKEN='+(process.env.CRC_RELAY_TOKEN||''));",
62
+ "const pluginPath=path.join(os.homedir(),'.config','opencode','plugins','crc-relay.ts');",
63
+ "console.log('PLUGIN_EXISTS='+fs.existsSync(pluginPath));",
64
+ 'setTimeout(()=>{},10000);',
65
+ ].join(' '),
66
+ ],
67
+ port,
68
+ hookToken,
69
+ });
70
+ createdIds.push(result.id);
71
+ const output = await waitForScrollbackContains(result.id, 'PLUGIN_EXISTS=true');
72
+ assert.match(output, new RegExp(`CRC_RELAY_URL=http://127\\.0\\.0\\.1:${port}`));
73
+ assert.match(output, new RegExp(`CRC_SESSION_ID=${result.id}`));
74
+ assert.match(output, new RegExp(`CRC_RELAY_TOKEN=${hookToken}`));
75
+ });
76
+ it('writes Codex hooks adapter and injects CODEX_CONFIG_DIR', async () => {
77
+ const result = sessions.create({
78
+ repoName: 'test-repo',
79
+ repoPath: '/tmp',
80
+ worktreePath: null,
81
+ cwd: '/tmp',
82
+ agent: 'codex',
83
+ command: process.execPath,
84
+ args: [
85
+ '-e',
86
+ [
87
+ "const fs=require('node:fs');",
88
+ "const path=require('node:path');",
89
+ "const dir=process.env.CODEX_CONFIG_DIR||'';",
90
+ "console.log('CODEX_CONFIG_DIR='+dir);",
91
+ "console.log('HAS_HOOKS_JSON='+(dir && fs.existsSync(path.join(dir,'hooks.json'))));",
92
+ "console.log('HAS_RELAY_SH='+(dir && fs.existsSync(path.join(dir,'relay.sh'))));",
93
+ 'setTimeout(()=>{},10000);',
94
+ ].join(' '),
95
+ ],
96
+ port: 7777,
97
+ hookToken: 'codex-token',
98
+ configDir: '/tmp',
99
+ });
100
+ createdIds.push(result.id);
101
+ const output = await waitForScrollbackContains(result.id, 'HAS_RELAY_SH=true');
102
+ assert.match(output, /CODEX_CONFIG_DIR=\/.*codex-hooks-/);
103
+ assert.match(output, /HAS_HOOKS_JSON=true/);
104
+ });
105
+ it('resolves framework from config.frameworks overrides', () => {
106
+ const result = sessions.create({
107
+ repoName: 'test-repo',
108
+ repoPath: '/tmp',
109
+ worktreePath: null,
110
+ cwd: '/tmp',
111
+ agent: 'claude',
112
+ command: '/bin/cat',
113
+ args: [],
114
+ frameworks: {
115
+ claude: {
116
+ eventSource: 'parser',
117
+ },
118
+ },
119
+ });
120
+ createdIds.push(result.id);
121
+ const session = sessions.get(result.id);
122
+ assert.ok(session);
123
+ assert.equal(session.dataQuality, 'parser');
124
+ });
125
+ it('injects framework.yoloEnv for opencode in yolo mode', async () => {
126
+ const result = sessions.create({
127
+ repoName: 'test-repo',
128
+ repoPath: '/tmp',
129
+ worktreePath: null,
130
+ cwd: '/tmp',
131
+ agent: 'opencode',
132
+ yolo: true,
133
+ command: process.execPath,
134
+ args: [
135
+ '-e',
136
+ [
137
+ "const cfg=process.env.OPENCODE_CONFIG_CONTENT||'';",
138
+ "console.log('OPENCODE_CONFIG_CONTENT='+cfg);",
139
+ 'setTimeout(()=>{},10000);',
140
+ ].join(' '),
141
+ ],
142
+ port: 3456,
143
+ hookToken: 'tok-opencode',
144
+ });
145
+ createdIds.push(result.id);
146
+ const output = await waitForScrollbackContains(result.id, 'OPENCODE_CONFIG_CONTENT=');
147
+ assert.match(output, /OPENCODE_CONFIG_CONTENT=.*"permission"/);
148
+ });
149
+ });
@@ -0,0 +1,146 @@
1
+ import { describe, it, afterEach } from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import * as sessions from '../server/sessions.js';
4
+ import { buildStatusLineRelayScript } from '../server/pty-handler.js';
5
+ describe('status-line relay script', () => {
6
+ it('writes telemetry via a temp file while streaming stdin once', () => {
7
+ const script = buildStatusLineRelayScript('session-123', '/tmp/claude-remote-config', '/usr/local/bin/status-line');
8
+ assert.match(script, /mktemp/);
9
+ assert.match(script, /tee "\$tmp_file"/);
10
+ assert.match(script, /mv "\$tmp_file"/);
11
+ assert.doesNotMatch(script, /input=\$\(cat\)/);
12
+ });
13
+ });
14
+ describe('framework-driven PTY handler', () => {
15
+ const createdIds = [];
16
+ afterEach(() => {
17
+ for (const id of createdIds) {
18
+ try {
19
+ const session = sessions.get(id);
20
+ if (session)
21
+ sessions.kill(id);
22
+ }
23
+ catch {
24
+ /* already cleaned up */
25
+ }
26
+ }
27
+ createdIds.length = 0;
28
+ });
29
+ it('command is resolved from framework.command when no override specified', () => {
30
+ // When agent=codex and no custom command, the session agent field should be codex
31
+ // (the actual pty command is internal — we verify it started by the session being active)
32
+ const result = sessions.create({
33
+ repoName: 'test-repo',
34
+ repoPath: '/tmp',
35
+ worktreePath: null,
36
+ cwd: '/tmp',
37
+ agent: 'codex',
38
+ command: '/bin/echo', // use custom command to avoid spawning real codex binary
39
+ args: ['hello'],
40
+ });
41
+ createdIds.push(result.id);
42
+ assert.equal(result.agent, 'codex');
43
+ const session = sessions.get(result.id);
44
+ assert.ok(session);
45
+ // customCommand should be set since we passed command
46
+ assert.equal(session.customCommand, '/bin/echo');
47
+ });
48
+ it('dataQuality is derived from actual hooksActive state (hooks when injection succeeds)', () => {
49
+ const result = sessions.create({
50
+ repoName: 'test-repo',
51
+ repoPath: '/tmp',
52
+ worktreePath: null,
53
+ cwd: '/tmp',
54
+ agent: 'claude',
55
+ command: '/bin/cat',
56
+ args: [],
57
+ });
58
+ createdIds.push(result.id);
59
+ const session = sessions.get(result.id);
60
+ assert.ok(session);
61
+ // Without a port, hook injection doesn't succeed → hooksActive stays false → dataQuality falls back to 'parser'
62
+ assert.equal(session.dataQuality, 'parser');
63
+ });
64
+ it('dataQuality falls back to parser when plugin injection fails', () => {
65
+ const result = sessions.create({
66
+ repoName: 'test-repo',
67
+ repoPath: '/tmp',
68
+ worktreePath: null,
69
+ cwd: '/tmp',
70
+ agent: 'opencode',
71
+ command: '/bin/cat',
72
+ args: [],
73
+ });
74
+ createdIds.push(result.id);
75
+ const session = sessions.get(result.id);
76
+ assert.ok(session);
77
+ // Without a port, opencode plugin injection doesn't succeed → hooksActive stays false → dataQuality is 'parser'
78
+ assert.equal(session.dataQuality, 'parser');
79
+ });
80
+ it('sessionArgs is populated on session matching claudeArgs', () => {
81
+ const result = sessions.create({
82
+ repoName: 'test-repo',
83
+ repoPath: '/tmp',
84
+ worktreePath: null,
85
+ cwd: '/tmp',
86
+ agent: 'claude',
87
+ command: '/bin/cat',
88
+ args: [],
89
+ claudeArgs: ['--model', 'opus'],
90
+ });
91
+ createdIds.push(result.id);
92
+ const session = sessions.get(result.id);
93
+ assert.ok(session);
94
+ assert.deepEqual(session.sessionArgs, ['--model', 'opus']);
95
+ // claudeArgs backward compat still set
96
+ assert.deepEqual(session.claudeArgs, ['--model', 'opus']);
97
+ });
98
+ it('sessionArgs defaults to empty array when claudeArgs not provided', () => {
99
+ const result = sessions.create({
100
+ repoName: 'test-repo',
101
+ repoPath: '/tmp',
102
+ worktreePath: null,
103
+ cwd: '/tmp',
104
+ agent: 'claude',
105
+ command: '/bin/cat',
106
+ args: [],
107
+ });
108
+ createdIds.push(result.id);
109
+ const session = sessions.get(result.id);
110
+ assert.ok(session);
111
+ assert.deepEqual(session.sessionArgs, []);
112
+ });
113
+ it('forceOutputParser sets dataQuality to parser regardless of framework', () => {
114
+ const result = sessions.create({
115
+ repoName: 'test-repo',
116
+ repoPath: '/tmp',
117
+ worktreePath: null,
118
+ cwd: '/tmp',
119
+ agent: 'claude',
120
+ command: '/bin/cat',
121
+ args: [],
122
+ forceOutputParser: true,
123
+ });
124
+ createdIds.push(result.id);
125
+ const session = sessions.get(result.id);
126
+ assert.ok(session);
127
+ assert.equal(session.dataQuality, 'parser');
128
+ });
129
+ it('parser is selected by framework.parserType (opencode uses opencode parser)', () => {
130
+ const result = sessions.create({
131
+ repoName: 'test-repo',
132
+ repoPath: '/tmp',
133
+ worktreePath: null,
134
+ cwd: '/tmp',
135
+ agent: 'opencode',
136
+ command: '/bin/cat',
137
+ args: [],
138
+ });
139
+ createdIds.push(result.id);
140
+ const session = sessions.get(result.id);
141
+ assert.ok(session);
142
+ // The outputParser should be an instance of the opencode parser
143
+ // We verify indirectly by checking the session started correctly
144
+ assert.ok(session.outputParser, 'outputParser should be set');
145
+ });
146
+ });
@@ -0,0 +1,78 @@
1
+ import { describe, it } from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ describe('PullRequest types', () => {
4
+ it('constructs a valid author PR', () => {
5
+ const pr = {
6
+ number: 42,
7
+ title: 'Fix bug',
8
+ url: 'https://github.com/owner/repo/pull/42',
9
+ headRefName: 'fix/bug',
10
+ baseRefName: 'main',
11
+ state: 'OPEN',
12
+ author: 'testuser',
13
+ role: 'author',
14
+ updatedAt: '2026-02-24T00:00:00Z',
15
+ additions: 10,
16
+ deletions: 5,
17
+ reviewDecision: 'APPROVED',
18
+ mergeable: 'MERGEABLE',
19
+ isDraft: false,
20
+ ciStatus: null,
21
+ };
22
+ assert.equal(pr.role, 'author');
23
+ assert.equal(pr.state, 'OPEN');
24
+ });
25
+ it('constructs a valid reviewer PR', () => {
26
+ const pr = {
27
+ number: 43,
28
+ title: 'Add feature',
29
+ url: 'https://github.com/owner/repo/pull/43',
30
+ headRefName: 'feat/new',
31
+ baseRefName: 'main',
32
+ state: 'OPEN',
33
+ author: 'otheruser',
34
+ role: 'reviewer',
35
+ updatedAt: '2026-02-24T00:00:00Z',
36
+ additions: 50,
37
+ deletions: 20,
38
+ reviewDecision: null,
39
+ mergeable: null,
40
+ isDraft: false,
41
+ ciStatus: null,
42
+ };
43
+ assert.equal(pr.role, 'reviewer');
44
+ });
45
+ it('constructs a valid response with error', () => {
46
+ const response = {
47
+ prs: [],
48
+ error: 'gh_not_authenticated',
49
+ };
50
+ assert.equal(response.prs.length, 0);
51
+ assert.equal(response.error, 'gh_not_authenticated');
52
+ });
53
+ it('constructs a valid response without error', () => {
54
+ const response = {
55
+ prs: [
56
+ {
57
+ number: 1,
58
+ title: 'Test',
59
+ url: 'https://github.com/o/r/pull/1',
60
+ headRefName: 'test',
61
+ baseRefName: 'main',
62
+ state: 'OPEN',
63
+ author: 'user',
64
+ role: 'author',
65
+ updatedAt: '2026-02-24T00:00:00Z',
66
+ additions: 0,
67
+ deletions: 0,
68
+ reviewDecision: null,
69
+ mergeable: null,
70
+ isDraft: false,
71
+ ciStatus: null,
72
+ },
73
+ ],
74
+ };
75
+ assert.equal(response.prs.length, 1);
76
+ assert.equal(response.error, undefined);
77
+ });
78
+ });