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,294 @@
1
+ import { test, before, after } from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import express from 'express';
4
+ import { createIntegrationJiraRouter } from '../server/integration-jira.js';
5
+ // ─── Constants ────────────────────────────────────────────────────────────────
6
+ const FAKE_BASE_URL = 'https://fake-jira.atlassian.net';
7
+ const FAKE_TOKEN = 'test-api-token';
8
+ const FAKE_EMAIL = 'test@example.com';
9
+ // ─── Helpers ──────────────────────────────────────────────────────────────────
10
+ /**
11
+ * Builds a minimal Jira REST API issue shape for use in mock responses.
12
+ */
13
+ function makeJiraApiIssue(overrides) {
14
+ const { key = 'TEST-1', summary = 'Test issue', status = 'In Progress', priority = 'Medium', storyPoints = null, sprint = null, assignee = 'Jane Doe', updated = '2026-03-21T00:00:00.000+0000', } = overrides;
15
+ return {
16
+ key,
17
+ self: `${FAKE_BASE_URL}/rest/api/3/issue/${key}`,
18
+ fields: {
19
+ summary,
20
+ status: { name: status },
21
+ priority: priority !== null ? { name: priority } : null,
22
+ customfield_10016: storyPoints,
23
+ customfield_10020: sprint !== null ? [{ name: sprint }] : null,
24
+ assignee: assignee !== null ? { displayName: assignee } : null,
25
+ updated,
26
+ },
27
+ };
28
+ }
29
+ /**
30
+ * Builds a mock Response with a given status and JSON body.
31
+ */
32
+ function makeJsonResponse(body, status = 200) {
33
+ return new Response(JSON.stringify(body), {
34
+ status,
35
+ headers: { 'Content-Type': 'application/json' },
36
+ });
37
+ }
38
+ // ─── Server lifecycle ─────────────────────────────────────────────────────────
39
+ let server;
40
+ let baseUrl;
41
+ let originalFetch;
42
+ /**
43
+ * Starts a fresh Express server with a new Jira router instance (and therefore
44
+ * a fresh in-memory cache).
45
+ */
46
+ function startServer() {
47
+ return new Promise((resolve) => {
48
+ const app = express();
49
+ app.use(express.json());
50
+ app.use('/integration-jira', createIntegrationJiraRouter({ configPath: '' }));
51
+ server = app.listen(0, '127.0.0.1', () => {
52
+ const addr = server.address();
53
+ if (typeof addr === 'object' && addr) {
54
+ baseUrl = `http://127.0.0.1:${addr.port}`;
55
+ }
56
+ resolve();
57
+ });
58
+ });
59
+ }
60
+ function stopServer() {
61
+ return new Promise((resolve) => {
62
+ if (server)
63
+ server.close(() => resolve());
64
+ else
65
+ resolve();
66
+ });
67
+ }
68
+ // ─── Env var helpers ──────────────────────────────────────────────────────────
69
+ function setEnvVars() {
70
+ process.env.JIRA_API_TOKEN = FAKE_TOKEN;
71
+ process.env.JIRA_EMAIL = FAKE_EMAIL;
72
+ process.env.JIRA_BASE_URL = FAKE_BASE_URL;
73
+ }
74
+ function clearEnvVars() {
75
+ delete process.env.JIRA_API_TOKEN;
76
+ delete process.env.JIRA_EMAIL;
77
+ delete process.env.JIRA_BASE_URL;
78
+ }
79
+ // ─── Suite setup / teardown ───────────────────────────────────────────────────
80
+ before(() => {
81
+ originalFetch = globalThis.fetch;
82
+ });
83
+ after(async () => {
84
+ globalThis.fetch = originalFetch;
85
+ clearEnvVars();
86
+ await stopServer();
87
+ });
88
+ // ─── Tests ────────────────────────────────────────────────────────────────────
89
+ test('GET /configured returns true when all env vars are set', async () => {
90
+ setEnvVars();
91
+ await stopServer();
92
+ await startServer();
93
+ const res = await fetch(`${baseUrl}/integration-jira/configured`);
94
+ assert.equal(res.status, 200);
95
+ const body = (await res.json());
96
+ assert.equal(body.configured, true);
97
+ });
98
+ test('GET /configured returns false when any env var is missing', async () => {
99
+ clearEnvVars();
100
+ // Set only two of the three required vars
101
+ process.env.JIRA_API_TOKEN = FAKE_TOKEN;
102
+ process.env.JIRA_EMAIL = FAKE_EMAIL;
103
+ // JIRA_BASE_URL intentionally omitted
104
+ await stopServer();
105
+ await startServer();
106
+ const res = await fetch(`${baseUrl}/integration-jira/configured`);
107
+ assert.equal(res.status, 200);
108
+ const body = (await res.json());
109
+ assert.equal(body.configured, false);
110
+ // Clean up partial env
111
+ delete process.env.JIRA_API_TOKEN;
112
+ delete process.env.JIRA_EMAIL;
113
+ });
114
+ test('GET /issues returns mapped JiraIssue[] from mocked Jira search response', async () => {
115
+ setEnvVars();
116
+ await stopServer();
117
+ await startServer();
118
+ const mockSearchPayload = {
119
+ issues: [
120
+ makeJiraApiIssue({
121
+ key: 'PROJ-42',
122
+ summary: 'Fix the login bug',
123
+ status: 'In Progress',
124
+ priority: 'High',
125
+ storyPoints: 3,
126
+ sprint: 'Sprint 5',
127
+ assignee: 'Alice',
128
+ updated: '2026-03-21T12:00:00.000+0000',
129
+ }),
130
+ makeJiraApiIssue({
131
+ key: 'PROJ-10',
132
+ summary: 'Update docs',
133
+ status: 'To Do',
134
+ priority: null,
135
+ storyPoints: null,
136
+ sprint: null,
137
+ assignee: null,
138
+ updated: '2026-03-20T08:00:00.000+0000',
139
+ }),
140
+ ],
141
+ };
142
+ globalThis.fetch = (async (input) => {
143
+ const url = typeof input === 'string' ? input : input.toString();
144
+ if (url.includes('/rest/api/3/search')) {
145
+ return makeJsonResponse(mockSearchPayload);
146
+ }
147
+ return makeJsonResponse({ error: 'unexpected url' }, 500);
148
+ });
149
+ const res = await fetch(`${baseUrl}/integration-jira/issues`);
150
+ assert.equal(res.status, 200);
151
+ const data = (await res.json());
152
+ assert.equal(data.error, undefined, `Unexpected error: ${data.error}`);
153
+ assert.equal(data.issues.length, 2);
154
+ const issue42 = data.issues.find((i) => i.key === 'PROJ-42');
155
+ assert.ok(issue42, 'Should contain PROJ-42');
156
+ assert.equal(issue42.title, 'Fix the login bug');
157
+ assert.equal(issue42.status, 'In Progress');
158
+ assert.equal(issue42.priority, 'High');
159
+ assert.equal(issue42.storyPoints, 3);
160
+ assert.equal(issue42.sprint, 'Sprint 5');
161
+ assert.equal(issue42.assignee, 'Alice');
162
+ assert.equal(issue42.url, `${FAKE_BASE_URL}/browse/PROJ-42`);
163
+ assert.equal(issue42.projectKey, 'PROJ');
164
+ const issue10 = data.issues.find((i) => i.key === 'PROJ-10');
165
+ assert.ok(issue10, 'Should contain PROJ-10');
166
+ assert.equal(issue10.priority, null);
167
+ assert.equal(issue10.storyPoints, null);
168
+ assert.equal(issue10.sprint, null);
169
+ assert.equal(issue10.assignee, null);
170
+ // Verify sorted descending by updatedAt — PROJ-42 (newer) should come first
171
+ assert.equal(data.issues[0]?.key, 'PROJ-42');
172
+ assert.equal(data.issues[1]?.key, 'PROJ-10');
173
+ });
174
+ test('GET /issues caches results within TTL — fetch called only once for two requests', async () => {
175
+ setEnvVars();
176
+ await stopServer();
177
+ await startServer();
178
+ let fetchCallCount = 0;
179
+ const mockPayload = {
180
+ issues: [makeJiraApiIssue({ key: 'CACHE-1', summary: 'Cached issue' })],
181
+ };
182
+ globalThis.fetch = (async (input) => {
183
+ const url = typeof input === 'string' ? input : input.toString();
184
+ if (url.includes('/rest/api/3/search')) {
185
+ fetchCallCount++;
186
+ return makeJsonResponse(mockPayload);
187
+ }
188
+ return makeJsonResponse({}, 500);
189
+ });
190
+ // First request — populates cache
191
+ const first = (await (await fetch(`${baseUrl}/integration-jira/issues`)).json());
192
+ assert.equal(first.error, undefined);
193
+ assert.equal(first.issues.length, 1);
194
+ assert.equal(fetchCallCount, 1, 'fetch should be called once on first request');
195
+ // Second request — should be served from cache, no additional fetch
196
+ const second = (await (await fetch(`${baseUrl}/integration-jira/issues`)).json());
197
+ assert.equal(second.error, undefined);
198
+ assert.equal(second.issues.length, 1);
199
+ assert.equal(fetchCallCount, 1, 'fetch should not be called again within TTL (cache hit)');
200
+ });
201
+ test('GET /issues returns jira_not_configured when env vars missing', async () => {
202
+ clearEnvVars();
203
+ await stopServer();
204
+ await startServer();
205
+ // fetch should never be called — use a mock that throws to verify early return
206
+ globalThis.fetch = (async () => {
207
+ throw new Error('fetch should not be called when not configured');
208
+ });
209
+ const res = await fetch(`${baseUrl}/integration-jira/issues`);
210
+ assert.equal(res.status, 200);
211
+ const data = (await res.json());
212
+ assert.equal(data.error, 'jira_not_configured');
213
+ assert.equal(data.issues.length, 0);
214
+ });
215
+ test('GET /statuses?projectKey=TEST returns deduplicated statuses from Jira project API', async () => {
216
+ setEnvVars();
217
+ await stopServer();
218
+ await startServer();
219
+ // Jira returns one entry per issue type; statuses may overlap — deduplication by id is expected
220
+ const mockProjectStatuses = [
221
+ {
222
+ statuses: [
223
+ { id: '1', name: 'To Do' },
224
+ { id: '2', name: 'In Progress' },
225
+ ],
226
+ },
227
+ {
228
+ statuses: [
229
+ { id: '2', name: 'In Progress' }, // duplicate — should be filtered
230
+ { id: '3', name: 'Done' },
231
+ ],
232
+ },
233
+ ];
234
+ globalThis.fetch = (async (input) => {
235
+ const url = typeof input === 'string' ? input : input.toString();
236
+ if (url.includes('/rest/api/3/project/TEST/statuses')) {
237
+ return makeJsonResponse(mockProjectStatuses);
238
+ }
239
+ return makeJsonResponse({}, 500);
240
+ });
241
+ const res = await fetch(`${baseUrl}/integration-jira/statuses?projectKey=TEST`);
242
+ assert.equal(res.status, 200);
243
+ const data = (await res.json());
244
+ assert.equal(data.error, undefined, `Unexpected error: ${data.error}`);
245
+ assert.equal(data.statuses.length, 3, 'Should deduplicate statuses by id');
246
+ const ids = data.statuses.map((s) => s.id);
247
+ assert.deepEqual(ids, ['1', '2', '3']);
248
+ const names = data.statuses.map((s) => s.name);
249
+ assert.deepEqual(names, ['To Do', 'In Progress', 'Done']);
250
+ });
251
+ test('GET /issues returns jira_auth_failed on 401 from Jira API', async () => {
252
+ setEnvVars();
253
+ await stopServer();
254
+ await startServer();
255
+ globalThis.fetch = (async (input) => {
256
+ const url = typeof input === 'string' ? input : input.toString();
257
+ if (url.includes('/rest/api/3/search')) {
258
+ return makeJsonResponse({ message: 'Unauthorized' }, 401);
259
+ }
260
+ return makeJsonResponse({}, 500);
261
+ });
262
+ const res = await fetch(`${baseUrl}/integration-jira/issues`);
263
+ assert.equal(res.status, 200);
264
+ const data = (await res.json());
265
+ assert.equal(data.error, 'jira_auth_failed');
266
+ assert.equal(data.issues.length, 0);
267
+ });
268
+ test('GET /statuses returns jira_auth_failed on 401 from Jira project API', async () => {
269
+ setEnvVars();
270
+ await stopServer();
271
+ await startServer();
272
+ globalThis.fetch = (async (input) => {
273
+ const url = typeof input === 'string' ? input : input.toString();
274
+ if (url.includes('/rest/api/3/project')) {
275
+ return makeJsonResponse({ message: 'Unauthorized' }, 401);
276
+ }
277
+ return makeJsonResponse({}, 500);
278
+ });
279
+ const res = await fetch(`${baseUrl}/integration-jira/statuses?projectKey=TEST`);
280
+ assert.equal(res.status, 200);
281
+ const data = (await res.json());
282
+ assert.equal(data.error, 'jira_auth_failed');
283
+ assert.equal(data.statuses.length, 0);
284
+ });
285
+ test('GET /statuses returns 400 when projectKey query param is missing', async () => {
286
+ setEnvVars();
287
+ await stopServer();
288
+ await startServer();
289
+ globalThis.fetch = originalFetch;
290
+ const res = await fetch(`${baseUrl}/integration-jira/statuses`);
291
+ assert.equal(res.status, 400);
292
+ const data = (await res.json());
293
+ assert.equal(data.error, 'missing_project_key');
294
+ });
@@ -0,0 +1,293 @@
1
+ import { test, beforeEach, afterEach } from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import express from 'express';
4
+ import { createIntegrationLinearRouter } from '../server/integration-linear.js';
5
+ // ─── State ───────────────────────────────────────────────────────────────────
6
+ let server;
7
+ let baseUrl;
8
+ // Saved before any mock replaces globalThis.fetch so test HTTP calls
9
+ // to the local Express server always reach it even when fetch is mocked.
10
+ let httpFetch;
11
+ let originalApiKey;
12
+ // ─── Helpers ─────────────────────────────────────────────────────────────────
13
+ /** Builds a single Linear issue node as returned by the GraphQL API. */
14
+ function makeIssueNode(overrides = {}) {
15
+ return {
16
+ id: overrides.id ?? 'issue-1',
17
+ identifier: overrides.identifier ?? 'ENG-1',
18
+ title: overrides.title ?? 'Test Issue',
19
+ url: overrides.url ?? 'https://linear.app/team/issue/ENG-1',
20
+ state: overrides.stateName != null ? { name: overrides.stateName } : { name: 'In Progress' },
21
+ priority: overrides.priority ?? 2,
22
+ priorityLabel: overrides.priorityLabel ?? 'Medium',
23
+ cycle: (overrides.cycle !== undefined)
24
+ ? (overrides.cycle !== null ? { name: overrides.cycle } : null)
25
+ : null,
26
+ estimate: overrides.estimate ?? null,
27
+ assignee: overrides.assigneeName != null ? { name: overrides.assigneeName } : null,
28
+ updatedAt: overrides.updatedAt ?? '2026-03-21T00:00:00Z',
29
+ team: overrides.teamId != null ? { id: overrides.teamId } : { id: 'team-abc' },
30
+ };
31
+ }
32
+ /** Builds a GraphQL issues response envelope. */
33
+ function makeIssuesGqlResponse(nodes) {
34
+ return {
35
+ data: {
36
+ viewer: {
37
+ assignedIssues: {
38
+ nodes,
39
+ },
40
+ },
41
+ },
42
+ };
43
+ }
44
+ /** Builds a GraphQL workflow states response envelope. */
45
+ function makeStatesGqlResponse(nodes) {
46
+ return {
47
+ data: {
48
+ workflowStates: {
49
+ nodes,
50
+ },
51
+ },
52
+ };
53
+ }
54
+ /**
55
+ * Returns a function suitable for replacing globalThis.fetch.
56
+ * The returned mock always resolves with a minimal Response-shaped object.
57
+ */
58
+ function makeMockFetch(gqlBody, opts = {}) {
59
+ const status = opts.status ?? 200;
60
+ const ok = opts.ok ?? (status >= 200 && status < 300);
61
+ return (async () => ({
62
+ ok,
63
+ status,
64
+ json: async () => gqlBody,
65
+ }));
66
+ }
67
+ function startServer() {
68
+ return new Promise((resolve) => {
69
+ const app = express();
70
+ app.use(express.json());
71
+ // configPath is unused by the Linear router at runtime; pass a dummy.
72
+ app.use('/integration-linear', createIntegrationLinearRouter({ configPath: '/dev/null' }));
73
+ server = app.listen(0, '127.0.0.1', () => {
74
+ const addr = server.address();
75
+ if (typeof addr === 'object' && addr) {
76
+ baseUrl = `http://127.0.0.1:${addr.port}`;
77
+ }
78
+ resolve();
79
+ });
80
+ });
81
+ }
82
+ function stopServer() {
83
+ return new Promise((resolve) => {
84
+ if (server)
85
+ server.close(() => resolve());
86
+ else
87
+ resolve();
88
+ });
89
+ }
90
+ // ─── Lifecycle ────────────────────────────────────────────────────────────────
91
+ // Each test gets a fresh server instance so the module-level issuesCache starts
92
+ // at null. This is the only reliable way to clear cache state without exposing
93
+ // internals, since issuesCache lives in the router closure.
94
+ beforeEach(async () => {
95
+ await startServer();
96
+ // Capture real fetch AFTER server starts (so baseUrl is set) but BEFORE any
97
+ // test mock replaces it. Used for all test-to-server HTTP calls.
98
+ httpFetch = globalThis.fetch;
99
+ originalApiKey = process.env['LINEAR_API_KEY'];
100
+ });
101
+ afterEach(async () => {
102
+ await stopServer();
103
+ globalThis.fetch = httpFetch;
104
+ if (originalApiKey === undefined) {
105
+ delete process.env['LINEAR_API_KEY'];
106
+ }
107
+ else {
108
+ process.env['LINEAR_API_KEY'] = originalApiKey;
109
+ }
110
+ });
111
+ // ─── GET /configured ─────────────────────────────────────────────────────────
112
+ test('GET /configured — returns { configured: true } when LINEAR_API_KEY is set', async () => {
113
+ process.env['LINEAR_API_KEY'] = 'lin_test_key';
114
+ const res = await httpFetch(`${baseUrl}/integration-linear/configured`);
115
+ assert.equal(res.ok, true);
116
+ const body = await res.json();
117
+ assert.deepEqual(body, { configured: true });
118
+ });
119
+ test('GET /configured — returns { configured: false } when LINEAR_API_KEY is not set', async () => {
120
+ delete process.env['LINEAR_API_KEY'];
121
+ const res = await httpFetch(`${baseUrl}/integration-linear/configured`);
122
+ assert.equal(res.ok, true);
123
+ const body = await res.json();
124
+ assert.deepEqual(body, { configured: false });
125
+ });
126
+ // ─── GET /issues ──────────────────────────────────────────────────────────────
127
+ test('GET /issues — returns linear_not_configured error when API key is missing', async () => {
128
+ delete process.env['LINEAR_API_KEY'];
129
+ const res = await httpFetch(`${baseUrl}/integration-linear/issues`);
130
+ assert.equal(res.ok, true);
131
+ const body = await res.json();
132
+ assert.equal(body.error, 'linear_not_configured');
133
+ assert.deepEqual(body.issues, []);
134
+ });
135
+ test('GET /issues — returns mapped LinearIssue[] from mocked GraphQL response', async () => {
136
+ process.env['LINEAR_API_KEY'] = 'lin_test_key';
137
+ const nodes = [
138
+ makeIssueNode({
139
+ id: 'issue-abc',
140
+ identifier: 'ENG-42',
141
+ title: 'Build something',
142
+ url: 'https://linear.app/team/issue/ENG-42',
143
+ stateName: 'In Progress',
144
+ priority: 1,
145
+ priorityLabel: 'Urgent',
146
+ cycle: 'Sprint 5',
147
+ estimate: 3,
148
+ assigneeName: 'Alice',
149
+ updatedAt: '2026-03-21T12:00:00Z',
150
+ teamId: 'team-xyz',
151
+ }),
152
+ ];
153
+ globalThis.fetch = makeMockFetch(makeIssuesGqlResponse(nodes));
154
+ const res = await httpFetch(`${baseUrl}/integration-linear/issues`);
155
+ assert.equal(res.ok, true);
156
+ const body = await res.json();
157
+ assert.equal(body.error, undefined, `Unexpected error: ${body.error}`);
158
+ assert.equal(body.issues.length, 1);
159
+ const issue = body.issues[0];
160
+ assert.equal(issue.id, 'issue-abc');
161
+ assert.equal(issue.identifier, 'ENG-42');
162
+ assert.equal(issue.title, 'Build something');
163
+ assert.equal(issue.url, 'https://linear.app/team/issue/ENG-42');
164
+ assert.equal(issue.state, 'In Progress');
165
+ assert.equal(issue.priority, 1);
166
+ assert.equal(issue.priorityLabel, 'Urgent');
167
+ assert.equal(issue.cycle, 'Sprint 5');
168
+ assert.equal(issue.estimate, 3);
169
+ assert.equal(issue.assignee, 'Alice');
170
+ assert.equal(issue.updatedAt, '2026-03-21T12:00:00Z');
171
+ assert.equal(issue.teamId, 'team-xyz');
172
+ });
173
+ test('GET /issues — caches results within TTL (fetch called only once for two requests)', async () => {
174
+ process.env['LINEAR_API_KEY'] = 'lin_test_key';
175
+ let fetchCallCount = 0;
176
+ const nodes = [makeIssueNode({ id: 'cached-issue', identifier: 'ENG-99' })];
177
+ const mockBody = makeIssuesGqlResponse(nodes);
178
+ globalThis.fetch = (async () => {
179
+ fetchCallCount++;
180
+ return { ok: true, status: 200, json: async () => mockBody };
181
+ });
182
+ // First request — populates cache
183
+ const first = await httpFetch(`${baseUrl}/integration-linear/issues`);
184
+ const firstBody = await first.json();
185
+ assert.equal(firstBody.error, undefined, `Unexpected error on first request: ${firstBody.error}`);
186
+ assert.equal(firstBody.issues.length, 1);
187
+ assert.equal(fetchCallCount, 1, 'fetch should be called once on the first request');
188
+ // Second request — should be served from cache, no additional fetch calls
189
+ const second = await httpFetch(`${baseUrl}/integration-linear/issues`);
190
+ const secondBody = await second.json();
191
+ assert.equal(secondBody.error, undefined, `Unexpected error on second request: ${secondBody.error}`);
192
+ assert.equal(secondBody.issues.length, 1);
193
+ assert.equal(fetchCallCount, 1, 'fetch should not be called again within TTL (cache hit)');
194
+ });
195
+ // ─── GET /states ──────────────────────────────────────────────────────────────
196
+ test('GET /states — returns workflow states from mocked GraphQL response', async () => {
197
+ process.env['LINEAR_API_KEY'] = 'lin_test_key';
198
+ const stateNodes = [
199
+ { id: 'state-1', name: 'Backlog' },
200
+ { id: 'state-2', name: 'In Progress' },
201
+ { id: 'state-3', name: 'Done' },
202
+ ];
203
+ globalThis.fetch = makeMockFetch(makeStatesGqlResponse(stateNodes));
204
+ const res = await httpFetch(`${baseUrl}/integration-linear/states?teamId=team-abc`);
205
+ assert.equal(res.ok, true);
206
+ const body = await res.json();
207
+ assert.equal(body.error, undefined, `Unexpected error: ${body.error}`);
208
+ assert.equal(body.states.length, 3);
209
+ assert.deepEqual(body.states, stateNodes);
210
+ });
211
+ test('GET /states — returns linear_not_configured error when API key is missing', async () => {
212
+ delete process.env['LINEAR_API_KEY'];
213
+ const res = await httpFetch(`${baseUrl}/integration-linear/states?teamId=team-abc`);
214
+ assert.equal(res.ok, true);
215
+ const body = await res.json();
216
+ assert.equal(body.error, 'linear_not_configured');
217
+ assert.deepEqual(body.states, []);
218
+ });
219
+ test('GET /states — returns 400 missing_team_id when teamId query param is absent', async () => {
220
+ process.env['LINEAR_API_KEY'] = 'lin_test_key';
221
+ const res = await httpFetch(`${baseUrl}/integration-linear/states`);
222
+ assert.equal(res.status, 400);
223
+ const body = await res.json();
224
+ assert.equal(body.error, 'missing_team_id');
225
+ });
226
+ // ─── Error handling ───────────────────────────────────────────────────────────
227
+ test('auth failure (HTTP 401) returns linear_auth_failed for /issues', async () => {
228
+ process.env['LINEAR_API_KEY'] = 'lin_bad_key';
229
+ globalThis.fetch = makeMockFetch({}, { status: 401, ok: false });
230
+ const res = await httpFetch(`${baseUrl}/integration-linear/issues`);
231
+ assert.equal(res.ok, true);
232
+ const body = await res.json();
233
+ assert.equal(body.error, 'linear_auth_failed');
234
+ assert.deepEqual(body.issues, []);
235
+ });
236
+ test('auth failure (HTTP 403) returns linear_auth_failed for /issues', async () => {
237
+ process.env['LINEAR_API_KEY'] = 'lin_bad_key';
238
+ globalThis.fetch = makeMockFetch({}, { status: 403, ok: false });
239
+ const res = await httpFetch(`${baseUrl}/integration-linear/issues`);
240
+ assert.equal(res.ok, true);
241
+ const body = await res.json();
242
+ assert.equal(body.error, 'linear_auth_failed');
243
+ assert.deepEqual(body.issues, []);
244
+ });
245
+ test('non-ok response (HTTP 500) returns linear_fetch_failed for /issues', async () => {
246
+ process.env['LINEAR_API_KEY'] = 'lin_test_key';
247
+ globalThis.fetch = makeMockFetch({}, { status: 500, ok: false });
248
+ const res = await httpFetch(`${baseUrl}/integration-linear/issues`);
249
+ assert.equal(res.ok, true);
250
+ const body = await res.json();
251
+ assert.equal(body.error, 'linear_fetch_failed');
252
+ assert.deepEqual(body.issues, []);
253
+ });
254
+ test('GraphQL-level authentication error returns linear_auth_failed', async () => {
255
+ process.env['LINEAR_API_KEY'] = 'lin_test_key';
256
+ const gqlAuthError = {
257
+ errors: [{ extensions: { type: 'authentication' } }],
258
+ data: null,
259
+ };
260
+ globalThis.fetch = makeMockFetch(gqlAuthError);
261
+ const res = await httpFetch(`${baseUrl}/integration-linear/issues`);
262
+ assert.equal(res.ok, true);
263
+ const body = await res.json();
264
+ assert.equal(body.error, 'linear_auth_failed');
265
+ assert.deepEqual(body.issues, []);
266
+ });
267
+ test('network error (fetch throws) returns linear_fetch_failed for /issues', async () => {
268
+ process.env['LINEAR_API_KEY'] = 'lin_test_key';
269
+ globalThis.fetch = (async () => { throw new Error('Network failure'); });
270
+ const res = await httpFetch(`${baseUrl}/integration-linear/issues`);
271
+ assert.equal(res.ok, true);
272
+ const body = await res.json();
273
+ assert.equal(body.error, 'linear_fetch_failed');
274
+ assert.deepEqual(body.issues, []);
275
+ });
276
+ test('auth failure (HTTP 401) returns linear_auth_failed for /states', async () => {
277
+ process.env['LINEAR_API_KEY'] = 'lin_bad_key';
278
+ globalThis.fetch = makeMockFetch({}, { status: 401, ok: false });
279
+ const res = await httpFetch(`${baseUrl}/integration-linear/states?teamId=team-abc`);
280
+ assert.equal(res.ok, true);
281
+ const body = await res.json();
282
+ assert.equal(body.error, 'linear_auth_failed');
283
+ assert.deepEqual(body.states, []);
284
+ });
285
+ test('non-ok response (HTTP 500) returns linear_fetch_failed for /states', async () => {
286
+ process.env['LINEAR_API_KEY'] = 'lin_test_key';
287
+ globalThis.fetch = makeMockFetch({}, { status: 500, ok: false });
288
+ const res = await httpFetch(`${baseUrl}/integration-linear/states?teamId=team-abc`);
289
+ assert.equal(res.ok, true);
290
+ const body = await res.json();
291
+ assert.equal(body.error, 'linear_fetch_failed');
292
+ assert.deepEqual(body.states, []);
293
+ });