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,137 @@
1
+ import path from 'node:path';
2
+ import { execFile } from 'node:child_process';
3
+ import { promisify } from 'node:util';
4
+ import { Router } from 'express';
5
+ import { loadConfig } from './config.js';
6
+ const execFileAsync = promisify(execFile);
7
+ const GH_TIMEOUT_MS = 10_000;
8
+ const CACHE_TTL_MS = 60_000;
9
+ /**
10
+ * Creates and returns an Express Router that handles all /integration-github routes.
11
+ *
12
+ * Caller is responsible for mounting and applying auth middleware:
13
+ * app.use('/integration-github', requireAuth, createIntegrationGitHubRouter({ configPath }));
14
+ */
15
+ export function createIntegrationGitHubRouter(deps) {
16
+ const { configPath } = deps;
17
+ const exec = deps.execAsync ?? execFileAsync;
18
+ const router = Router();
19
+ // Per-repo 60s in-memory cache
20
+ const repoCache = new Map();
21
+ function getConfig() {
22
+ return loadConfig(configPath);
23
+ }
24
+ // GET /integrations/github/issues — list open issues assigned to @me across all workspaces
25
+ router.get('/issues', async (_req, res) => {
26
+ const config = getConfig();
27
+ const workspacePaths = config.repos ?? [];
28
+ if (workspacePaths.length === 0) {
29
+ const response = {
30
+ issues: [],
31
+ error: 'no_workspaces',
32
+ };
33
+ res.json(response);
34
+ return;
35
+ }
36
+ const now = Date.now();
37
+ // Fetch issues per repo using Promise.allSettled (partial failures are non-fatal)
38
+ const results = await Promise.allSettled(workspacePaths.map(async (wsPath) => {
39
+ // Return cached result if still fresh
40
+ const cached = repoCache.get(wsPath);
41
+ if (cached && now - cached.fetchedAt < CACHE_TTL_MS) {
42
+ return cached.issues;
43
+ }
44
+ let stdout;
45
+ try {
46
+ ({ stdout } = await exec('gh', [
47
+ 'issue',
48
+ 'list',
49
+ '--assignee',
50
+ '@me',
51
+ '--state',
52
+ 'open',
53
+ '--json',
54
+ 'number,title,url,state,labels,assignees,createdAt,updatedAt',
55
+ '--limit',
56
+ '50',
57
+ ], { cwd: wsPath, timeout: GH_TIMEOUT_MS }));
58
+ }
59
+ catch (err) {
60
+ const errCode = err.code;
61
+ if (errCode === 'ENOENT') {
62
+ throw Object.assign(new Error('gh_not_in_path'), {
63
+ code: 'GH_NOT_IN_PATH',
64
+ });
65
+ }
66
+ // Check for auth failure via stderr
67
+ const stderr = err.stderr ?? '';
68
+ if (stderr.includes('not logged') ||
69
+ stderr.includes('auth') ||
70
+ stderr.includes('authentication')) {
71
+ throw Object.assign(new Error('gh_not_authenticated'), {
72
+ code: 'GH_NOT_AUTHENTICATED',
73
+ });
74
+ }
75
+ // Not a github repo or other non-fatal error
76
+ return [];
77
+ }
78
+ let items;
79
+ try {
80
+ items = JSON.parse(stdout);
81
+ }
82
+ catch {
83
+ return [];
84
+ }
85
+ const repoName = path.basename(wsPath);
86
+ const issues = items.map((item) => ({
87
+ number: item.number,
88
+ title: item.title,
89
+ url: item.url,
90
+ state: item.state === 'OPEN' ? 'OPEN' : 'CLOSED',
91
+ labels: item.labels,
92
+ assignees: item.assignees,
93
+ createdAt: item.createdAt,
94
+ updatedAt: item.updatedAt,
95
+ repoName,
96
+ repoPath: wsPath,
97
+ }));
98
+ // Update per-repo cache
99
+ repoCache.set(wsPath, { issues, fetchedAt: now });
100
+ return issues;
101
+ }));
102
+ // Check if gh is not in path or not authenticated (any settled rejection with known codes)
103
+ for (const result of results) {
104
+ if (result.status === 'rejected') {
105
+ const err = result.reason;
106
+ if (err.code === 'GH_NOT_IN_PATH') {
107
+ const response = {
108
+ issues: [],
109
+ error: 'gh_not_in_path',
110
+ };
111
+ res.json(response);
112
+ return;
113
+ }
114
+ if (err.code === 'GH_NOT_AUTHENTICATED') {
115
+ const response = {
116
+ issues: [],
117
+ error: 'gh_not_authenticated',
118
+ };
119
+ res.json(response);
120
+ return;
121
+ }
122
+ }
123
+ }
124
+ // Merge all fulfilled results
125
+ const allIssues = [];
126
+ for (const result of results) {
127
+ if (result.status === 'fulfilled') {
128
+ allIssues.push(...result.value);
129
+ }
130
+ }
131
+ // Sort by updatedAt descending
132
+ allIssues.sort((a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime());
133
+ const response = { issues: allIssues };
134
+ res.json(response);
135
+ });
136
+ return router;
137
+ }
@@ -0,0 +1,210 @@
1
+ import { execFile } from 'node:child_process';
2
+ import { promisify } from 'node:util';
3
+ import { Router } from 'express';
4
+ const execFileAsync = promisify(execFile);
5
+ const JIRA_TIMEOUT_MS = 10_000;
6
+ const CACHE_TTL_MS = 60_000;
7
+ const JIRA_ISSUES_CACHE_KEY = 'jira_issues';
8
+ /**
9
+ * Creates and returns an Express Router that handles all /integration-jira routes.
10
+ *
11
+ * Caller is responsible for mounting and applying auth middleware:
12
+ * app.use('/integration-jira', requireAuth, createIntegrationJiraRouter({ configPath }));
13
+ */
14
+ export function createIntegrationJiraRouter(deps) {
15
+ const exec = deps.execAsync ?? execFileAsync;
16
+ const router = Router();
17
+ // Single 60s in-memory cache (Jira is cross-workspace, not per-repo)
18
+ const issuesCache = new Map();
19
+ // Cached site URL — resolved once per server lifetime
20
+ let cachedSiteUrl = null;
21
+ async function getSiteUrl() {
22
+ if (cachedSiteUrl !== null)
23
+ return cachedSiteUrl;
24
+ const { stdout } = await exec('acli', ['jira', 'auth', 'status'], {
25
+ timeout: JIRA_TIMEOUT_MS,
26
+ });
27
+ const match = /Site:\s*([\w-]+\.atlassian\.net)/.exec(stdout);
28
+ if (!match || !match[1]) {
29
+ throw new Error('Could not parse site URL from acli jira auth status output');
30
+ }
31
+ cachedSiteUrl = match[1];
32
+ return cachedSiteUrl;
33
+ }
34
+ // GET /integrations/jira/issues — search issues assigned to currentUser
35
+ router.get('/issues', async (_req, res) => {
36
+ const now = Date.now();
37
+ // Return cached result if still fresh
38
+ const cached = issuesCache.get(JIRA_ISSUES_CACHE_KEY);
39
+ if (cached && now - cached.fetchedAt < CACHE_TTL_MS) {
40
+ const response = { issues: cached.issues };
41
+ res.json(response);
42
+ return;
43
+ }
44
+ let siteUrl;
45
+ try {
46
+ siteUrl = await getSiteUrl();
47
+ }
48
+ catch (err) {
49
+ const errCode = err.code;
50
+ if (errCode === 'ENOENT') {
51
+ const response = {
52
+ issues: [],
53
+ error: 'acli_not_in_path',
54
+ };
55
+ res.json(response);
56
+ return;
57
+ }
58
+ const stderr = err.stderr ?? '';
59
+ if (stderr.includes('not logged') ||
60
+ stderr.includes('auth') ||
61
+ stderr.includes('unauthorized')) {
62
+ const response = {
63
+ issues: [],
64
+ error: 'acli_not_authenticated',
65
+ };
66
+ res.json(response);
67
+ return;
68
+ }
69
+ const response = {
70
+ issues: [],
71
+ error: 'jira_fetch_failed',
72
+ };
73
+ res.json(response);
74
+ return;
75
+ }
76
+ let stdout;
77
+ try {
78
+ ({ stdout } = await exec('acli', [
79
+ 'jira',
80
+ 'workitem',
81
+ 'search',
82
+ '--jql',
83
+ 'assignee=currentUser() AND status NOT IN (Done, Closed) ORDER BY updated DESC',
84
+ '--json',
85
+ '--limit',
86
+ '50',
87
+ ], { timeout: JIRA_TIMEOUT_MS }));
88
+ }
89
+ catch (err) {
90
+ const errCode = err.code;
91
+ if (errCode === 'ENOENT') {
92
+ const response = {
93
+ issues: [],
94
+ error: 'acli_not_in_path',
95
+ };
96
+ res.json(response);
97
+ return;
98
+ }
99
+ const stderr = err.stderr ?? '';
100
+ if (stderr.includes('not logged') ||
101
+ stderr.includes('auth') ||
102
+ stderr.includes('unauthorized')) {
103
+ const response = {
104
+ issues: [],
105
+ error: 'acli_not_authenticated',
106
+ };
107
+ res.json(response);
108
+ return;
109
+ }
110
+ const response = {
111
+ issues: [],
112
+ error: 'jira_fetch_failed',
113
+ };
114
+ res.json(response);
115
+ return;
116
+ }
117
+ let items;
118
+ try {
119
+ items = JSON.parse(stdout);
120
+ }
121
+ catch {
122
+ const response = {
123
+ issues: [],
124
+ error: 'jira_fetch_failed',
125
+ };
126
+ res.json(response);
127
+ return;
128
+ }
129
+ const issues = items.map((item) => ({
130
+ key: item.key,
131
+ title: item.fields.summary,
132
+ url: `https://${siteUrl}/browse/${item.key}`,
133
+ status: item.fields.status.name,
134
+ priority: item.fields.priority?.name ?? null,
135
+ assignee: item.fields.assignee?.displayName ?? null,
136
+ projectKey: item.key.split('-')[0] ?? item.key,
137
+ updatedAt: '',
138
+ sprint: null,
139
+ storyPoints: null,
140
+ }));
141
+ // Update cache
142
+ issuesCache.set(JIRA_ISSUES_CACHE_KEY, { issues, fetchedAt: now });
143
+ const response = { issues };
144
+ res.json(response);
145
+ });
146
+ // GET /integrations/jira/statuses?projectKey=X — fetch unique statuses for a project
147
+ router.get('/statuses', async (req, res) => {
148
+ const projectKey = req.query['projectKey'];
149
+ if (!projectKey || typeof projectKey !== 'string') {
150
+ res.status(400).json({ statuses: [], error: 'missing_project_key' });
151
+ return;
152
+ }
153
+ // Sanitize: only allow [A-Z0-9]+ to prevent command injection
154
+ if (!/^[A-Z0-9]+$/.test(projectKey)) {
155
+ res.status(400).json({ statuses: [], error: 'invalid_project_key' });
156
+ return;
157
+ }
158
+ let stdout;
159
+ try {
160
+ ({ stdout } = await exec('acli', [
161
+ 'jira',
162
+ 'workitem',
163
+ 'search',
164
+ '--jql',
165
+ `project = ${projectKey}`,
166
+ '--fields',
167
+ 'status',
168
+ '--json',
169
+ '--limit',
170
+ '50',
171
+ ], { timeout: JIRA_TIMEOUT_MS }));
172
+ }
173
+ catch (err) {
174
+ const errCode = err.code;
175
+ if (errCode === 'ENOENT') {
176
+ res.json({ statuses: [], error: 'acli_not_in_path' });
177
+ return;
178
+ }
179
+ const stderr = err.stderr ?? '';
180
+ if (stderr.includes('not logged') ||
181
+ stderr.includes('auth') ||
182
+ stderr.includes('unauthorized')) {
183
+ res.json({ statuses: [], error: 'acli_not_authenticated' });
184
+ return;
185
+ }
186
+ res.json({ statuses: [], error: 'jira_fetch_failed' });
187
+ return;
188
+ }
189
+ let items;
190
+ try {
191
+ items = JSON.parse(stdout);
192
+ }
193
+ catch {
194
+ res.json({ statuses: [], error: 'jira_fetch_failed' });
195
+ return;
196
+ }
197
+ // Deduplicate statuses by id
198
+ const seen = new Set();
199
+ const statuses = [];
200
+ for (const item of items) {
201
+ const { id, name } = item.fields.status;
202
+ if (!seen.has(id)) {
203
+ seen.add(id);
204
+ statuses.push({ id, name });
205
+ }
206
+ }
207
+ res.json({ statuses });
208
+ });
209
+ return router;
210
+ }
@@ -0,0 +1,176 @@
1
+ import { Router } from 'express';
2
+ const LINEAR_GRAPHQL_URL = 'https://api.linear.app/graphql';
3
+ const CACHE_TTL_MS = 60_000;
4
+ /**
5
+ * Creates and returns an Express Router that handles all /integration-linear routes.
6
+ *
7
+ * Caller is responsible for mounting and applying auth middleware:
8
+ * app.use('/integration-linear', requireAuth, createIntegrationLinearRouter({ configPath }));
9
+ */
10
+ export function createIntegrationLinearRouter(_deps) {
11
+ const router = Router();
12
+ // Single 60s in-memory cache for assigned issues
13
+ let issuesCache = null;
14
+ // GET /integrations/linear/configured — check whether the API key is set
15
+ router.get('/configured', (_req, res) => {
16
+ const apiKey = process.env.LINEAR_API_KEY;
17
+ res.json({ configured: Boolean(apiKey) });
18
+ });
19
+ // GET /integrations/linear/issues — fetch assigned issues (non-completed, non-canceled)
20
+ router.get('/issues', async (_req, res) => {
21
+ const apiKey = process.env.LINEAR_API_KEY;
22
+ if (!apiKey) {
23
+ const response = { issues: [], error: 'linear_not_configured' };
24
+ res.json(response);
25
+ return;
26
+ }
27
+ // Return cached result if still fresh
28
+ const now = Date.now();
29
+ if (issuesCache && now - issuesCache.fetchedAt < CACHE_TTL_MS) {
30
+ const response = { issues: issuesCache.issues };
31
+ res.json(response);
32
+ return;
33
+ }
34
+ const query = `
35
+ query {
36
+ viewer {
37
+ assignedIssues(
38
+ filter: { state: { type: { nin: ["completed", "canceled"] } } }
39
+ first: 50
40
+ orderBy: updatedAt
41
+ ) {
42
+ nodes {
43
+ id
44
+ identifier
45
+ title
46
+ url
47
+ state { name }
48
+ priority
49
+ priorityLabel
50
+ cycle { name }
51
+ estimate
52
+ assignee { name }
53
+ updatedAt
54
+ team { id }
55
+ }
56
+ }
57
+ }
58
+ }
59
+ `;
60
+ let data;
61
+ try {
62
+ const fetchRes = await fetch(LINEAR_GRAPHQL_URL, {
63
+ method: 'POST',
64
+ headers: {
65
+ 'Authorization': `Bearer ${apiKey}`,
66
+ 'Content-Type': 'application/json',
67
+ },
68
+ body: JSON.stringify({ query }),
69
+ });
70
+ if (fetchRes.status === 401 || fetchRes.status === 403) {
71
+ const response = { issues: [], error: 'linear_auth_failed' };
72
+ res.json(response);
73
+ return;
74
+ }
75
+ if (!fetchRes.ok) {
76
+ const response = { issues: [], error: 'linear_fetch_failed' };
77
+ res.json(response);
78
+ return;
79
+ }
80
+ data = await fetchRes.json();
81
+ }
82
+ catch {
83
+ const response = { issues: [], error: 'linear_fetch_failed' };
84
+ res.json(response);
85
+ return;
86
+ }
87
+ // Check for GraphQL-level auth errors
88
+ const gqlData = data;
89
+ if (gqlData.errors && gqlData.errors.length > 0) {
90
+ const errType = gqlData.errors[0]?.extensions?.type;
91
+ if (errType === 'authentication' || errType === 'authorization') {
92
+ const response = { issues: [], error: 'linear_auth_failed' };
93
+ res.json(response);
94
+ return;
95
+ }
96
+ const response = { issues: [], error: 'linear_fetch_failed' };
97
+ res.json(response);
98
+ return;
99
+ }
100
+ const nodes = gqlData.data?.viewer?.assignedIssues?.nodes ?? [];
101
+ const issues = nodes.map((node) => ({
102
+ id: node.id,
103
+ identifier: node.identifier,
104
+ title: node.title,
105
+ url: node.url,
106
+ state: node.state?.name ?? '',
107
+ priority: node.priority,
108
+ priorityLabel: node.priorityLabel,
109
+ cycle: node.cycle?.name ?? null,
110
+ estimate: node.estimate ?? null,
111
+ assignee: node.assignee?.name ?? null,
112
+ updatedAt: node.updatedAt,
113
+ teamId: node.team?.id ?? '',
114
+ }));
115
+ // Update cache
116
+ issuesCache = { issues, fetchedAt: now };
117
+ const response = { issues };
118
+ res.json(response);
119
+ });
120
+ // GET /integrations/linear/states?teamId=X — fetch workflow states for a team
121
+ router.get('/states', async (req, res) => {
122
+ const apiKey = process.env.LINEAR_API_KEY;
123
+ if (!apiKey) {
124
+ res.json({ states: [], error: 'linear_not_configured' });
125
+ return;
126
+ }
127
+ const teamId = req.query['teamId'];
128
+ if (typeof teamId !== 'string' || !teamId) {
129
+ res.status(400).json({ states: [], error: 'missing_team_id' });
130
+ return;
131
+ }
132
+ const query = `
133
+ query($teamId: String!) {
134
+ workflowStates(filter: { team: { id: { eq: $teamId } } }) {
135
+ nodes { id name }
136
+ }
137
+ }
138
+ `;
139
+ let data;
140
+ try {
141
+ const fetchRes = await fetch(LINEAR_GRAPHQL_URL, {
142
+ method: 'POST',
143
+ headers: {
144
+ 'Authorization': `Bearer ${apiKey}`,
145
+ 'Content-Type': 'application/json',
146
+ },
147
+ body: JSON.stringify({ query, variables: { teamId } }),
148
+ });
149
+ if (fetchRes.status === 401 || fetchRes.status === 403) {
150
+ res.json({ states: [], error: 'linear_auth_failed' });
151
+ return;
152
+ }
153
+ if (!fetchRes.ok) {
154
+ res.json({ states: [], error: 'linear_fetch_failed' });
155
+ return;
156
+ }
157
+ data = await fetchRes.json();
158
+ }
159
+ catch {
160
+ res.json({ states: [], error: 'linear_fetch_failed' });
161
+ return;
162
+ }
163
+ const gqlData = data;
164
+ if (gqlData.errors && gqlData.errors.length > 0) {
165
+ res.json({ states: [], error: 'linear_fetch_failed' });
166
+ return;
167
+ }
168
+ const nodes = gqlData.data?.workflowStates?.nodes ?? [];
169
+ const states = nodes.map((node) => ({
170
+ id: node.id,
171
+ name: node.name,
172
+ }));
173
+ res.json({ states });
174
+ });
175
+ return router;
176
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Create a namespaced logger that prefixes all messages.
3
+ *
4
+ * @param namespace - Namespace used in the log prefix.
5
+ * @returns A logger that delegates to `console.*`.
6
+ */
7
+ export function createLogger(namespace) {
8
+ const prefix = `[${namespace}]`;
9
+ const log = (level, message, ...args) => {
10
+ console[level](`${prefix} ${message}`, ...args);
11
+ };
12
+ return {
13
+ debug: (message, ...args) => log('debug', message, ...args),
14
+ info: (message, ...args) => log('info', message, ...args),
15
+ warn: (message, ...args) => log('warn', message, ...args),
16
+ error: (message, ...args) => log('error', message, ...args),
17
+ };
18
+ }
@@ -0,0 +1,129 @@
1
+ export function codepointCount(str) {
2
+ let count = 0;
3
+ for (let i = 0; i < str.length; i++) {
4
+ count++;
5
+ if (str.charCodeAt(i) >= 0xd800 && str.charCodeAt(i) <= 0xdbff)
6
+ i++;
7
+ }
8
+ return count;
9
+ }
10
+ export function commonPrefixLength(a, b) {
11
+ let len = 0;
12
+ while (len < a.length && len < b.length && a[len] === b[len])
13
+ len++;
14
+ return len;
15
+ }
16
+ const DEL = '\x7f';
17
+ function makeBackspaces(count) {
18
+ let s = '';
19
+ for (let i = 0; i < count; i++)
20
+ s += DEL;
21
+ return s;
22
+ }
23
+ function handleInsert(intent, currentValue) {
24
+ const { rangeStart, rangeEnd, data } = intent;
25
+ if (rangeStart !== null && rangeEnd !== null && rangeStart !== rangeEnd) {
26
+ // Non-collapsed range = autocorrect replacement
27
+ const replaced = intent.valueBefore.slice(rangeStart, rangeEnd);
28
+ const charsToDelete = codepointCount(replaced);
29
+ const payload = makeBackspaces(charsToDelete) + (data ?? '');
30
+ return { payload };
31
+ }
32
+ if (data) {
33
+ // Gboard cursor-0 bug: keyboard loses cursor position and prepends the
34
+ // replacement word at position 0 instead of replacing the last word in place.
35
+ // Detect by: multi-char data, cursor was at 0, and new value = data + old value.
36
+ const isCursor0Prepend = data.length > 1 &&
37
+ intent.cursorBefore === 0 &&
38
+ intent.valueBefore.length > 0 &&
39
+ currentValue === data + intent.valueBefore;
40
+ if (isCursor0Prepend) {
41
+ const lastSpaceIdx = intent.valueBefore.lastIndexOf(' ');
42
+ const lastWord = lastSpaceIdx >= 0
43
+ ? intent.valueBefore.slice(lastSpaceIdx + 1)
44
+ : intent.valueBefore;
45
+ // Buffer ends with a space — nothing to autocorrect
46
+ if (lastWord.length === 0) {
47
+ return {
48
+ payload: '',
49
+ newInputValue: intent.valueBefore,
50
+ debug: 'CURSOR0: empty lastWord, skip',
51
+ };
52
+ }
53
+ const prefix = lastSpaceIdx >= 0 ? intent.valueBefore.slice(0, lastSpaceIdx + 1) : '';
54
+ // Gboard sometimes sends the full replacement word (data[0] === firstChar,
55
+ // e.g. "teh" → data="the") and sometimes only the suffix after the first
56
+ // char (data[0] !== firstChar, e.g. "tsestin" → data="esting ").
57
+ // Evidence from device diagnostics confirms both patterns occur.
58
+ const firstChar = lastWord.charAt(0);
59
+ const isSuffix = data.charAt(0) !== firstChar;
60
+ const replacement = isSuffix ? firstChar + data : data;
61
+ const payload = makeBackspaces(codepointCount(lastWord)) + replacement;
62
+ const debug = `CURSOR0: lastWord="${lastWord}" firstChar="${firstChar}" data[0]="${data.charAt(0)}" mode=${isSuffix ? 'suffix' : 'fullword'} replacement="${replacement}" del=${codepointCount(lastWord)}`;
63
+ return { payload, newInputValue: prefix + replacement, debug };
64
+ }
65
+ return { payload: data };
66
+ }
67
+ // No data and no range — fall back to diff
68
+ return handleFallbackDiff(intent, currentValue);
69
+ }
70
+ function handleDelete(intent, currentValue) {
71
+ const { rangeStart, rangeEnd, valueBefore } = intent;
72
+ if (rangeStart !== null && rangeEnd !== null) {
73
+ const deleted = valueBefore.slice(rangeStart, rangeEnd);
74
+ const charsToDelete = codepointCount(deleted);
75
+ return { payload: makeBackspaces(charsToDelete) };
76
+ }
77
+ // No range info — diff to figure out how many chars were deleted
78
+ const deleted = valueBefore.length - currentValue.length;
79
+ const charsToDelete = Math.max(1, deleted);
80
+ return { payload: makeBackspaces(charsToDelete) };
81
+ }
82
+ function handleReplacement(intent, currentValue) {
83
+ const { rangeStart, rangeEnd, data, valueBefore } = intent;
84
+ if (rangeStart !== null && rangeEnd !== null) {
85
+ const replaced = valueBefore.slice(rangeStart, rangeEnd);
86
+ const charsToDelete = codepointCount(replaced);
87
+ const payload = makeBackspaces(charsToDelete) + (data ?? '');
88
+ return { payload };
89
+ }
90
+ return handleFallbackDiff(intent, currentValue);
91
+ }
92
+ function handlePaste(intent, currentValue) {
93
+ const commonLen = commonPrefixLength(intent.valueBefore, currentValue);
94
+ const pasted = currentValue.slice(commonLen);
95
+ return { payload: pasted };
96
+ }
97
+ function handleFallbackDiff(intent, currentValue) {
98
+ const valueBefore = intent.valueBefore || '';
99
+ if (currentValue === valueBefore) {
100
+ return { payload: '' };
101
+ }
102
+ const commonLen = commonPrefixLength(valueBefore, currentValue);
103
+ const deletedSlice = valueBefore.slice(commonLen);
104
+ const charsToDelete = codepointCount(deletedSlice);
105
+ const newChars = currentValue.slice(commonLen);
106
+ const payload = makeBackspaces(charsToDelete) + newChars;
107
+ return { payload };
108
+ }
109
+ export function processIntent(intent, currentValue) {
110
+ switch (intent.type) {
111
+ case 'insertText':
112
+ return handleInsert(intent, currentValue);
113
+ case 'deleteContentBackward':
114
+ case 'deleteContentForward':
115
+ case 'deleteWordBackward':
116
+ case 'deleteWordForward':
117
+ case 'deleteSoftLineBackward':
118
+ case 'deleteSoftLineForward':
119
+ case 'deleteBySoftwareKeyboard':
120
+ return handleDelete(intent, currentValue);
121
+ case 'insertReplacementText':
122
+ return handleReplacement(intent, currentValue);
123
+ case 'insertFromPaste':
124
+ case 'insertFromDrop':
125
+ return handlePaste(intent, currentValue);
126
+ default:
127
+ return handleFallbackDiff(intent, currentValue);
128
+ }
129
+ }