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,117 @@
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.workspaces ?? [];
28
+ if (workspacePaths.length === 0) {
29
+ const response = { issues: [], error: 'no_workspaces' };
30
+ res.json(response);
31
+ return;
32
+ }
33
+ const now = Date.now();
34
+ // Fetch issues per repo using Promise.allSettled (partial failures are non-fatal)
35
+ const results = await Promise.allSettled(workspacePaths.map(async (wsPath) => {
36
+ // Return cached result if still fresh
37
+ const cached = repoCache.get(wsPath);
38
+ if (cached && now - cached.fetchedAt < CACHE_TTL_MS) {
39
+ return cached.issues;
40
+ }
41
+ let stdout;
42
+ try {
43
+ ({ stdout } = await exec('gh', [
44
+ 'issue', 'list',
45
+ '--assignee', '@me',
46
+ '--state', 'open',
47
+ '--json', 'number,title,url,state,labels,assignees,createdAt,updatedAt',
48
+ '--limit', '50',
49
+ ], { cwd: wsPath, timeout: GH_TIMEOUT_MS }));
50
+ }
51
+ catch (err) {
52
+ const errCode = err.code;
53
+ if (errCode === 'ENOENT') {
54
+ throw Object.assign(new Error('gh_not_in_path'), { code: 'GH_NOT_IN_PATH' });
55
+ }
56
+ // Check for auth failure via stderr
57
+ const stderr = err.stderr ?? '';
58
+ if (stderr.includes('not logged') || stderr.includes('auth') || stderr.includes('authentication')) {
59
+ throw Object.assign(new Error('gh_not_authenticated'), { code: 'GH_NOT_AUTHENTICATED' });
60
+ }
61
+ // Not a github repo or other non-fatal error
62
+ return [];
63
+ }
64
+ let items;
65
+ try {
66
+ items = JSON.parse(stdout);
67
+ }
68
+ catch {
69
+ return [];
70
+ }
71
+ const repoName = path.basename(wsPath);
72
+ const issues = items.map((item) => ({
73
+ number: item.number,
74
+ title: item.title,
75
+ url: item.url,
76
+ state: item.state === 'OPEN' ? 'OPEN' : 'CLOSED',
77
+ labels: item.labels,
78
+ assignees: item.assignees,
79
+ createdAt: item.createdAt,
80
+ updatedAt: item.updatedAt,
81
+ repoName,
82
+ repoPath: wsPath,
83
+ }));
84
+ // Update per-repo cache
85
+ repoCache.set(wsPath, { issues, fetchedAt: now });
86
+ return issues;
87
+ }));
88
+ // Check if gh is not in path or not authenticated (any settled rejection with known codes)
89
+ for (const result of results) {
90
+ if (result.status === 'rejected') {
91
+ const err = result.reason;
92
+ if (err.code === 'GH_NOT_IN_PATH') {
93
+ const response = { issues: [], error: 'gh_not_in_path' };
94
+ res.json(response);
95
+ return;
96
+ }
97
+ if (err.code === 'GH_NOT_AUTHENTICATED') {
98
+ const response = { issues: [], error: 'gh_not_authenticated' };
99
+ res.json(response);
100
+ return;
101
+ }
102
+ }
103
+ }
104
+ // Merge all fulfilled results
105
+ const allIssues = [];
106
+ for (const result of results) {
107
+ if (result.status === 'fulfilled') {
108
+ allIssues.push(...result.value);
109
+ }
110
+ }
111
+ // Sort by updatedAt descending
112
+ allIssues.sort((a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime());
113
+ const response = { issues: allIssues };
114
+ res.json(response);
115
+ });
116
+ return router;
117
+ }
@@ -0,0 +1,164 @@
1
+ import { Router } from 'express';
2
+ const CACHE_TTL_MS = 60_000;
3
+ const JIRA_ISSUES_CACHE_KEY = 'jira_issues';
4
+ /**
5
+ * Creates and returns an Express Router that handles all /integration-jira routes.
6
+ *
7
+ * Caller is responsible for mounting and applying auth middleware:
8
+ * app.use('/integration-jira', requireAuth, createIntegrationJiraRouter({ configPath }));
9
+ */
10
+ export function createIntegrationJiraRouter(_deps) {
11
+ const router = Router();
12
+ // Single 60s in-memory cache (Jira is cross-workspace, not per-repo)
13
+ const issuesCache = new Map();
14
+ function getEnvVars() {
15
+ const token = process.env.JIRA_API_TOKEN;
16
+ const email = process.env.JIRA_EMAIL;
17
+ const baseUrl = process.env.JIRA_BASE_URL;
18
+ if (!token || !email || !baseUrl)
19
+ return null;
20
+ return { token, email, baseUrl };
21
+ }
22
+ function buildAuthHeader(email, token) {
23
+ return `Basic ${Buffer.from(`${email}:${token}`).toString('base64')}`;
24
+ }
25
+ // GET /integrations/jira/configured — returns whether env vars are set
26
+ router.get('/configured', (_req, res) => {
27
+ const env = getEnvVars();
28
+ res.json({ configured: env !== null });
29
+ });
30
+ // GET /integrations/jira/issues — search issues assigned to currentUser
31
+ router.get('/issues', async (_req, res) => {
32
+ const env = getEnvVars();
33
+ if (!env) {
34
+ const response = { issues: [], error: 'jira_not_configured' };
35
+ res.json(response);
36
+ return;
37
+ }
38
+ const now = Date.now();
39
+ // Return cached result if still fresh
40
+ const cached = issuesCache.get(JIRA_ISSUES_CACHE_KEY);
41
+ if (cached && now - cached.fetchedAt < CACHE_TTL_MS) {
42
+ const response = { issues: cached.issues };
43
+ res.json(response);
44
+ return;
45
+ }
46
+ const jql = 'assignee=currentUser() AND status NOT IN (Done, Closed) ORDER BY updated DESC';
47
+ const fields = 'summary,status,priority,customfield_10016,customfield_10020,assignee,updated';
48
+ const url = `${env.baseUrl}/rest/api/3/search?jql=${encodeURIComponent(jql)}&fields=${encodeURIComponent(fields)}&maxResults=50`;
49
+ let data;
50
+ try {
51
+ const fetchResult = await Promise.allSettled([
52
+ fetch(url, {
53
+ headers: {
54
+ Authorization: buildAuthHeader(env.email, env.token),
55
+ Accept: 'application/json',
56
+ },
57
+ }),
58
+ ]);
59
+ const settled = fetchResult[0];
60
+ if (settled.status === 'rejected') {
61
+ const response = { issues: [], error: 'jira_fetch_failed' };
62
+ res.json(response);
63
+ return;
64
+ }
65
+ const httpRes = settled.value;
66
+ if (httpRes.status === 401 || httpRes.status === 403) {
67
+ const response = { issues: [], error: 'jira_auth_failed' };
68
+ res.json(response);
69
+ return;
70
+ }
71
+ if (!httpRes.ok) {
72
+ const response = { issues: [], error: 'jira_fetch_failed' };
73
+ res.json(response);
74
+ return;
75
+ }
76
+ data = (await httpRes.json());
77
+ }
78
+ catch {
79
+ const response = { issues: [], error: 'jira_fetch_failed' };
80
+ res.json(response);
81
+ return;
82
+ }
83
+ const issues = data.issues.map((item) => {
84
+ const projectKey = item.key.split('-')[0] ?? item.key;
85
+ const sprint = item.fields.customfield_10020;
86
+ const latestSprint = sprint && sprint.length > 0 ? sprint[sprint.length - 1]?.name ?? null : null;
87
+ return {
88
+ key: item.key,
89
+ title: item.fields.summary,
90
+ url: `${env.baseUrl}/browse/${item.key}`,
91
+ status: item.fields.status.name,
92
+ priority: item.fields.priority?.name ?? null,
93
+ sprint: latestSprint,
94
+ storyPoints: item.fields.customfield_10016,
95
+ assignee: item.fields.assignee?.displayName ?? null,
96
+ updatedAt: item.fields.updated,
97
+ projectKey,
98
+ };
99
+ });
100
+ // Sort by updatedAt descending
101
+ issues.sort((a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime());
102
+ // Update cache
103
+ issuesCache.set(JIRA_ISSUES_CACHE_KEY, { issues, fetchedAt: now });
104
+ const response = { issues };
105
+ res.json(response);
106
+ });
107
+ // GET /integrations/jira/statuses?projectKey=X — fetch project statuses
108
+ router.get('/statuses', async (req, res) => {
109
+ const env = getEnvVars();
110
+ if (!env) {
111
+ res.json({ statuses: [], error: 'jira_not_configured' });
112
+ return;
113
+ }
114
+ const projectKey = req.query['projectKey'];
115
+ if (!projectKey || typeof projectKey !== 'string') {
116
+ res.status(400).json({ statuses: [], error: 'missing_project_key' });
117
+ return;
118
+ }
119
+ const url = `${env.baseUrl}/rest/api/3/project/${encodeURIComponent(projectKey)}/statuses`;
120
+ let rawData;
121
+ try {
122
+ const fetchResults = await Promise.allSettled([
123
+ fetch(url, {
124
+ headers: {
125
+ Authorization: buildAuthHeader(env.email, env.token),
126
+ Accept: 'application/json',
127
+ },
128
+ }),
129
+ ]);
130
+ const settled = fetchResults[0];
131
+ if (settled.status === 'rejected') {
132
+ res.json({ statuses: [], error: 'jira_fetch_failed' });
133
+ return;
134
+ }
135
+ const httpRes = settled.value;
136
+ if (httpRes.status === 401 || httpRes.status === 403) {
137
+ res.json({ statuses: [], error: 'jira_auth_failed' });
138
+ return;
139
+ }
140
+ if (!httpRes.ok) {
141
+ res.json({ statuses: [], error: 'jira_fetch_failed' });
142
+ return;
143
+ }
144
+ rawData = (await httpRes.json());
145
+ }
146
+ catch {
147
+ res.json({ statuses: [], error: 'jira_fetch_failed' });
148
+ return;
149
+ }
150
+ // Flatten statuses across all issue types and deduplicate by id
151
+ const seen = new Set();
152
+ const statuses = [];
153
+ for (const issueType of rawData) {
154
+ for (const s of issueType.statuses) {
155
+ if (!seen.has(s.id)) {
156
+ seen.add(s.id);
157
+ statuses.push({ id: s.id, name: s.name });
158
+ }
159
+ }
160
+ }
161
+ res.json({ statuses });
162
+ });
163
+ return router;
164
+ }
@@ -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,123 @@
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 ? intent.valueBefore.slice(lastSpaceIdx + 1) : intent.valueBefore;
43
+ // Buffer ends with a space — nothing to autocorrect
44
+ if (lastWord.length === 0) {
45
+ return { payload: '', newInputValue: intent.valueBefore, debug: 'CURSOR0: empty lastWord, skip' };
46
+ }
47
+ const prefix = lastSpaceIdx >= 0 ? intent.valueBefore.slice(0, lastSpaceIdx + 1) : '';
48
+ // Gboard sometimes sends the full replacement word (data[0] === firstChar,
49
+ // e.g. "teh" → data="the") and sometimes only the suffix after the first
50
+ // char (data[0] !== firstChar, e.g. "tsestin" → data="esting ").
51
+ // Evidence from device diagnostics confirms both patterns occur.
52
+ const firstChar = lastWord.charAt(0);
53
+ const isSuffix = data.charAt(0) !== firstChar;
54
+ const replacement = isSuffix ? firstChar + data : data;
55
+ const payload = makeBackspaces(codepointCount(lastWord)) + replacement;
56
+ const debug = `CURSOR0: lastWord="${lastWord}" firstChar="${firstChar}" data[0]="${data.charAt(0)}" mode=${isSuffix ? 'suffix' : 'fullword'} replacement="${replacement}" del=${codepointCount(lastWord)}`;
57
+ return { payload, newInputValue: prefix + replacement, debug };
58
+ }
59
+ return { payload: data };
60
+ }
61
+ // No data and no range — fall back to diff
62
+ return handleFallbackDiff(intent, currentValue);
63
+ }
64
+ function handleDelete(intent, currentValue) {
65
+ const { rangeStart, rangeEnd, valueBefore } = intent;
66
+ if (rangeStart !== null && rangeEnd !== null) {
67
+ const deleted = valueBefore.slice(rangeStart, rangeEnd);
68
+ const charsToDelete = codepointCount(deleted);
69
+ return { payload: makeBackspaces(charsToDelete) };
70
+ }
71
+ // No range info — diff to figure out how many chars were deleted
72
+ const deleted = valueBefore.length - currentValue.length;
73
+ const charsToDelete = Math.max(1, deleted);
74
+ return { payload: makeBackspaces(charsToDelete) };
75
+ }
76
+ function handleReplacement(intent, currentValue) {
77
+ const { rangeStart, rangeEnd, data, valueBefore } = intent;
78
+ if (rangeStart !== null && rangeEnd !== null) {
79
+ const replaced = valueBefore.slice(rangeStart, rangeEnd);
80
+ const charsToDelete = codepointCount(replaced);
81
+ const payload = makeBackspaces(charsToDelete) + (data ?? '');
82
+ return { payload };
83
+ }
84
+ return handleFallbackDiff(intent, currentValue);
85
+ }
86
+ function handlePaste(intent, currentValue) {
87
+ const commonLen = commonPrefixLength(intent.valueBefore, currentValue);
88
+ const pasted = currentValue.slice(commonLen);
89
+ return { payload: pasted };
90
+ }
91
+ function handleFallbackDiff(intent, currentValue) {
92
+ const valueBefore = intent.valueBefore || '';
93
+ if (currentValue === valueBefore) {
94
+ return { payload: '' };
95
+ }
96
+ const commonLen = commonPrefixLength(valueBefore, currentValue);
97
+ const deletedSlice = valueBefore.slice(commonLen);
98
+ const charsToDelete = codepointCount(deletedSlice);
99
+ const newChars = currentValue.slice(commonLen);
100
+ const payload = makeBackspaces(charsToDelete) + newChars;
101
+ return { payload };
102
+ }
103
+ export function processIntent(intent, currentValue) {
104
+ switch (intent.type) {
105
+ case 'insertText':
106
+ return handleInsert(intent, currentValue);
107
+ case 'deleteContentBackward':
108
+ case 'deleteContentForward':
109
+ case 'deleteWordBackward':
110
+ case 'deleteWordForward':
111
+ case 'deleteSoftLineBackward':
112
+ case 'deleteSoftLineForward':
113
+ case 'deleteBySoftwareKeyboard':
114
+ return handleDelete(intent, currentValue);
115
+ case 'insertReplacementText':
116
+ return handleReplacement(intent, currentValue);
117
+ case 'insertFromPaste':
118
+ case 'insertFromDrop':
119
+ return handlePaste(intent, currentValue);
120
+ default:
121
+ return handleFallbackDiff(intent, currentValue);
122
+ }
123
+ }