@juspay/shooter 1.0.0 → 1.2.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 (488) hide show
  1. package/.claude/hooks/notifier.cjs +117 -33
  2. package/.claude/settings.json +14 -14
  3. package/README.md +116 -84
  4. package/bin/shooter.cjs +471 -102
  5. package/build/client/_app/immutable/assets/{0.CM9Hl6d-.css → 0.BhZOCxO4.css} +1 -1
  6. package/build/client/_app/immutable/assets/0.BhZOCxO4.css.br +0 -0
  7. package/build/client/_app/immutable/assets/0.BhZOCxO4.css.gz +0 -0
  8. package/build/client/_app/immutable/assets/1.BYutk3aU.css +1 -0
  9. package/build/client/_app/immutable/assets/1.BYutk3aU.css.br +0 -0
  10. package/build/client/_app/immutable/assets/1.BYutk3aU.css.gz +0 -0
  11. package/build/client/_app/immutable/assets/2.CAShZ7lQ.css.gz +0 -0
  12. package/build/client/_app/immutable/assets/3.DGDHCVnW.css +1 -0
  13. package/build/client/_app/immutable/assets/3.DGDHCVnW.css.br +0 -0
  14. package/build/client/_app/immutable/assets/3.DGDHCVnW.css.gz +0 -0
  15. package/build/client/_app/immutable/assets/4.BFUut--w.css +1 -0
  16. package/build/client/_app/immutable/assets/4.BFUut--w.css.br +0 -0
  17. package/build/client/_app/immutable/assets/4.BFUut--w.css.gz +0 -0
  18. package/build/client/_app/immutable/assets/5.BTOx7yt7.css +1 -0
  19. package/build/client/_app/immutable/assets/5.BTOx7yt7.css.br +0 -0
  20. package/build/client/_app/immutable/assets/5.BTOx7yt7.css.gz +0 -0
  21. package/build/client/_app/immutable/assets/6.eZGZN-BF.css +1 -0
  22. package/build/client/_app/immutable/assets/6.eZGZN-BF.css.br +0 -0
  23. package/build/client/_app/immutable/assets/6.eZGZN-BF.css.gz +0 -0
  24. package/build/client/_app/immutable/assets/7.DwS5ZHBh.css +1 -0
  25. package/build/client/_app/immutable/assets/7.DwS5ZHBh.css.br +0 -0
  26. package/build/client/_app/immutable/assets/7.DwS5ZHBh.css.gz +0 -0
  27. package/build/client/_app/immutable/assets/ChatView.CwWbzIL-.css +1 -0
  28. package/build/client/_app/immutable/assets/ChatView.CwWbzIL-.css.br +0 -0
  29. package/build/client/_app/immutable/assets/ChatView.CwWbzIL-.css.gz +0 -0
  30. package/build/client/_app/immutable/assets/Phone.FQEfwCX2.css +1 -0
  31. package/build/client/_app/immutable/assets/Phone.FQEfwCX2.css.br +0 -0
  32. package/build/client/_app/immutable/assets/Phone.FQEfwCX2.css.gz +0 -0
  33. package/build/client/_app/immutable/assets/markdown.Dc-OSJWY.css +1 -0
  34. package/build/client/_app/immutable/assets/markdown.Dc-OSJWY.css.br +0 -0
  35. package/build/client/_app/immutable/assets/markdown.Dc-OSJWY.css.gz +0 -0
  36. package/build/client/_app/immutable/assets/xterm.DFuMZ0ql.css.gz +0 -0
  37. package/build/client/_app/immutable/chunks/B-K5Sh65.js +1 -0
  38. package/build/client/_app/immutable/chunks/B-K5Sh65.js.br +0 -0
  39. package/build/client/_app/immutable/chunks/B-K5Sh65.js.gz +0 -0
  40. package/build/client/_app/immutable/chunks/B5NAKyil.js +20 -0
  41. package/build/client/_app/immutable/chunks/B5NAKyil.js.br +0 -0
  42. package/build/client/_app/immutable/chunks/B5NAKyil.js.gz +0 -0
  43. package/build/client/_app/immutable/chunks/B8XegpSE.js +1 -0
  44. package/build/client/_app/immutable/chunks/B8XegpSE.js.br +0 -0
  45. package/build/client/_app/immutable/chunks/B8XegpSE.js.gz +0 -0
  46. package/build/client/_app/immutable/chunks/B8zoBsv3.js +6 -0
  47. package/build/client/_app/immutable/chunks/B8zoBsv3.js.br +0 -0
  48. package/build/client/_app/immutable/chunks/B8zoBsv3.js.gz +0 -0
  49. package/build/client/_app/immutable/chunks/BLszSzTT.js +1 -0
  50. package/build/client/_app/immutable/chunks/BLszSzTT.js.br +0 -0
  51. package/build/client/_app/immutable/chunks/BLszSzTT.js.gz +0 -0
  52. package/build/client/_app/immutable/chunks/BOYo8yTr.js +1 -0
  53. package/build/client/_app/immutable/chunks/BOYo8yTr.js.br +0 -0
  54. package/build/client/_app/immutable/chunks/BOYo8yTr.js.gz +0 -0
  55. package/build/client/_app/immutable/chunks/BTGVxaYV.js.gz +0 -0
  56. package/build/client/_app/immutable/chunks/BYqGCrTe.js +1 -0
  57. package/build/client/_app/immutable/chunks/BYqGCrTe.js.br +0 -0
  58. package/build/client/_app/immutable/chunks/BYqGCrTe.js.gz +0 -0
  59. package/build/client/_app/immutable/chunks/BlxrFPDK.js.gz +0 -0
  60. package/build/client/_app/immutable/chunks/Bu1aqm5j.js +1 -0
  61. package/build/client/_app/immutable/chunks/Bu1aqm5j.js.br +0 -0
  62. package/build/client/_app/immutable/chunks/Bu1aqm5j.js.gz +0 -0
  63. package/build/client/_app/immutable/chunks/C4mLaWWx.js +1 -0
  64. package/build/client/_app/immutable/chunks/C4mLaWWx.js.br +0 -0
  65. package/build/client/_app/immutable/chunks/C4mLaWWx.js.gz +0 -0
  66. package/build/client/_app/immutable/chunks/CQjSATpv.js +61 -0
  67. package/build/client/_app/immutable/chunks/CQjSATpv.js.br +0 -0
  68. package/build/client/_app/immutable/chunks/CQjSATpv.js.gz +0 -0
  69. package/build/client/_app/immutable/chunks/CSoRdFvv.js +1 -0
  70. package/build/client/_app/immutable/chunks/CSoRdFvv.js.br +0 -0
  71. package/build/client/_app/immutable/chunks/CSoRdFvv.js.gz +0 -0
  72. package/build/client/_app/immutable/chunks/CZHsSL_X.js +1 -0
  73. package/build/client/_app/immutable/chunks/CZHsSL_X.js.br +0 -0
  74. package/build/client/_app/immutable/chunks/CZHsSL_X.js.gz +0 -0
  75. package/build/client/_app/immutable/chunks/DLu6yJIZ.js.gz +0 -0
  76. package/build/client/_app/immutable/chunks/DSU1n5N_.js +1 -0
  77. package/build/client/_app/immutable/chunks/DSU1n5N_.js.br +0 -0
  78. package/build/client/_app/immutable/chunks/DSU1n5N_.js.gz +0 -0
  79. package/build/client/_app/immutable/chunks/DVkn4r72.js +1 -0
  80. package/build/client/_app/immutable/chunks/DVkn4r72.js.br +0 -0
  81. package/build/client/_app/immutable/chunks/DVkn4r72.js.gz +0 -0
  82. package/build/client/_app/immutable/chunks/DjsDGxCa.js +41 -0
  83. package/build/client/_app/immutable/chunks/DjsDGxCa.js.br +0 -0
  84. package/build/client/_app/immutable/chunks/DjsDGxCa.js.gz +0 -0
  85. package/build/client/_app/immutable/chunks/PPVm8Dsz.js.gz +0 -0
  86. package/build/client/_app/immutable/chunks/UJOiqIYE.js +1 -0
  87. package/build/client/_app/immutable/chunks/UJOiqIYE.js.br +0 -0
  88. package/build/client/_app/immutable/chunks/UJOiqIYE.js.gz +0 -0
  89. package/build/client/_app/immutable/chunks/r0JawsZc.js +2 -0
  90. package/build/client/_app/immutable/chunks/r0JawsZc.js.br +0 -0
  91. package/build/client/_app/immutable/chunks/r0JawsZc.js.gz +0 -0
  92. package/build/client/_app/immutable/entry/app.Z3zMnuSx.js +2 -0
  93. package/build/client/_app/immutable/entry/app.Z3zMnuSx.js.br +0 -0
  94. package/build/client/_app/immutable/entry/app.Z3zMnuSx.js.gz +0 -0
  95. package/build/client/_app/immutable/entry/start.Dd-gIP4y.js +1 -0
  96. package/build/client/_app/immutable/entry/start.Dd-gIP4y.js.br +2 -0
  97. package/build/client/_app/immutable/entry/start.Dd-gIP4y.js.gz +0 -0
  98. package/build/client/_app/immutable/nodes/0.D2YR8tTD.js +1 -0
  99. package/build/client/_app/immutable/nodes/0.D2YR8tTD.js.br +0 -0
  100. package/build/client/_app/immutable/nodes/0.D2YR8tTD.js.gz +0 -0
  101. package/build/client/_app/immutable/nodes/1.B3m6rO4C.js +1 -0
  102. package/build/client/_app/immutable/nodes/1.B3m6rO4C.js.br +0 -0
  103. package/build/client/_app/immutable/nodes/1.B3m6rO4C.js.gz +0 -0
  104. package/build/client/_app/immutable/nodes/2.CyRB2euU.js +1 -0
  105. package/build/client/_app/immutable/nodes/2.CyRB2euU.js.br +0 -0
  106. package/build/client/_app/immutable/nodes/2.CyRB2euU.js.gz +0 -0
  107. package/build/client/_app/immutable/nodes/3.3yohCM25.js +3 -0
  108. package/build/client/_app/immutable/nodes/3.3yohCM25.js.br +0 -0
  109. package/build/client/_app/immutable/nodes/3.3yohCM25.js.gz +0 -0
  110. package/build/client/_app/immutable/nodes/4.DEAcwl7l.js +1 -0
  111. package/build/client/_app/immutable/nodes/4.DEAcwl7l.js.br +0 -0
  112. package/build/client/_app/immutable/nodes/4.DEAcwl7l.js.gz +0 -0
  113. package/build/client/_app/immutable/nodes/5.C6bLGWQR.js +4 -0
  114. package/build/client/_app/immutable/nodes/5.C6bLGWQR.js.br +0 -0
  115. package/build/client/_app/immutable/nodes/5.C6bLGWQR.js.gz +0 -0
  116. package/build/client/_app/immutable/nodes/6.ByTzlA2D.js +2 -0
  117. package/build/client/_app/immutable/nodes/6.ByTzlA2D.js.br +0 -0
  118. package/build/client/_app/immutable/nodes/6.ByTzlA2D.js.gz +0 -0
  119. package/build/client/_app/immutable/nodes/7.BPMfwzd2.js +2 -0
  120. package/build/client/_app/immutable/nodes/7.BPMfwzd2.js.br +0 -0
  121. package/build/client/_app/immutable/nodes/7.BPMfwzd2.js.gz +0 -0
  122. package/build/client/_app/version.json +1 -1
  123. package/build/client/_app/version.json.br +0 -0
  124. package/build/client/_app/version.json.gz +0 -0
  125. package/build/client/favicon.svg.gz +0 -0
  126. package/build/client/manifest.json +13 -0
  127. package/build/client/manifest.json.br +0 -0
  128. package/build/client/manifest.json.gz +0 -0
  129. package/build/pty-holder.cjs +37 -8
  130. package/build/server/chunks/0-Vk38tI2J.js +9 -0
  131. package/build/server/chunks/0-Vk38tI2J.js.map +1 -0
  132. package/build/server/chunks/1-BvYQX5MR.js +9 -0
  133. package/build/server/chunks/1-BvYQX5MR.js.map +1 -0
  134. package/build/server/chunks/2-Cl7R4Qk2.js +9 -0
  135. package/build/server/chunks/2-Cl7R4Qk2.js.map +1 -0
  136. package/build/server/chunks/3-Ck7ewhOX.js +9 -0
  137. package/build/server/chunks/3-Ck7ewhOX.js.map +1 -0
  138. package/build/server/chunks/4-CnDeRm2Z.js +9 -0
  139. package/build/server/chunks/4-CnDeRm2Z.js.map +1 -0
  140. package/build/server/chunks/5-IxitzEvN.js +9 -0
  141. package/build/server/chunks/5-IxitzEvN.js.map +1 -0
  142. package/build/server/chunks/6-CyZ3r1iS.js +9 -0
  143. package/build/server/chunks/6-CyZ3r1iS.js.map +1 -0
  144. package/build/server/chunks/7-BmI7du46.js +9 -0
  145. package/build/server/chunks/7-BmI7du46.js.map +1 -0
  146. package/build/server/chunks/Button-Cs1aE6ka.js +80 -0
  147. package/build/server/chunks/Button-Cs1aE6ka.js.map +1 -0
  148. package/build/server/chunks/EmptyState-DDFH1K8g.js +26 -0
  149. package/build/server/chunks/EmptyState-DDFH1K8g.js.map +1 -0
  150. package/build/server/chunks/Icon-CEUrotA6.js +36 -0
  151. package/build/server/chunks/Icon-CEUrotA6.js.map +1 -0
  152. package/build/server/chunks/Shimmer-DB8W1zt6.js +10 -0
  153. package/build/server/chunks/Shimmer-DB8W1zt6.js.map +1 -0
  154. package/build/server/chunks/_error.svelte-uCOJNxvr.js +39 -0
  155. package/build/server/chunks/_error.svelte-uCOJNxvr.js.map +1 -0
  156. package/build/server/chunks/_layout.svelte-CtWmEJwe.js +56 -0
  157. package/build/server/chunks/_layout.svelte-CtWmEJwe.js.map +1 -0
  158. package/build/server/chunks/_page.svelte-BcZaKdX9.js +45 -0
  159. package/build/server/chunks/_page.svelte-BcZaKdX9.js.map +1 -0
  160. package/build/server/chunks/_page.svelte-BdYynOck.js +85 -0
  161. package/build/server/chunks/_page.svelte-BdYynOck.js.map +1 -0
  162. package/build/server/chunks/_page.svelte-BgevQjq1.js +101 -0
  163. package/build/server/chunks/_page.svelte-BgevQjq1.js.map +1 -0
  164. package/build/server/chunks/_page.svelte-CVq6tRb3.js +550 -0
  165. package/build/server/chunks/_page.svelte-CVq6tRb3.js.map +1 -0
  166. package/build/server/chunks/_page.svelte-CxWcQ0Am.js +651 -0
  167. package/build/server/chunks/_page.svelte-CxWcQ0Am.js.map +1 -0
  168. package/build/server/chunks/_page.svelte-DO4oa_LY.js +44 -0
  169. package/build/server/chunks/_page.svelte-DO4oa_LY.js.map +1 -0
  170. package/build/server/chunks/{_server.ts-CbDRDIoP.js → _server.ts-BStnNIcq.js} +9 -11
  171. package/build/server/chunks/_server.ts-BStnNIcq.js.map +1 -0
  172. package/build/server/chunks/{_server.ts-DRVbgm6k.js → _server.ts-CAxsWKvS.js} +22 -25
  173. package/build/server/chunks/_server.ts-CAxsWKvS.js.map +1 -0
  174. package/build/server/chunks/{_server.ts-CPa6DgIt.js → _server.ts-COu0vNpd.js} +6 -6
  175. package/build/server/chunks/_server.ts-COu0vNpd.js.map +1 -0
  176. package/build/server/chunks/{_server.ts-C29xzfaw.js → _server.ts-CTpcLUH8.js} +10 -10
  177. package/build/server/chunks/_server.ts-CTpcLUH8.js.map +1 -0
  178. package/build/server/chunks/{_server.ts-D4MNi4cD.js → _server.ts-Cf84YIaW.js} +3 -3
  179. package/build/server/chunks/{_server.ts-D4MNi4cD.js.map → _server.ts-Cf84YIaW.js.map} +1 -1
  180. package/build/server/chunks/{_server.ts-BL2FGb5Z.js → _server.ts-Ch-6iOHp.js} +99 -53
  181. package/build/server/chunks/_server.ts-Ch-6iOHp.js.map +1 -0
  182. package/build/server/chunks/_server.ts-CtH0dhUp.js +71 -0
  183. package/build/server/chunks/_server.ts-CtH0dhUp.js.map +1 -0
  184. package/build/server/chunks/_server.ts-DB_Kg97c.js +73 -0
  185. package/build/server/chunks/_server.ts-DB_Kg97c.js.map +1 -0
  186. package/build/server/chunks/{_server.ts-DfajWaqh.js → _server.ts-DV8zTCF9.js} +7 -9
  187. package/build/server/chunks/_server.ts-DV8zTCF9.js.map +1 -0
  188. package/build/server/chunks/_server.ts-DYpJImqd.js +99 -0
  189. package/build/server/chunks/_server.ts-DYpJImqd.js.map +1 -0
  190. package/build/server/chunks/{_server.ts-ColfDHW8.js → _server.ts-DYvb9ijZ.js} +21 -10
  191. package/build/server/chunks/_server.ts-DYvb9ijZ.js.map +1 -0
  192. package/build/server/chunks/{_server.ts-Cv_OrRuL.js → _server.ts-Deok2y88.js} +209 -34
  193. package/build/server/chunks/_server.ts-Deok2y88.js.map +1 -0
  194. package/build/server/chunks/{_server.ts-y9-WYDMa.js → _server.ts-WhTJBEJy.js} +5 -4
  195. package/build/server/chunks/{_server.ts-y9-WYDMa.js.map → _server.ts-WhTJBEJy.js.map} +1 -1
  196. package/build/server/chunks/{_server.ts-BjOJsoy4.js → _server.ts-XzT2UHM1.js} +6 -5
  197. package/build/server/chunks/_server.ts-XzT2UHM1.js.map +1 -0
  198. package/build/server/chunks/{_server.ts-BgdjBZco.js → _server.ts-tSpgyl1D.js} +7 -5
  199. package/build/server/chunks/_server.ts-tSpgyl1D.js.map +1 -0
  200. package/build/server/chunks/{_server.ts-BihKSdj_.js → _server.ts-vekTmWAx.js} +8 -8
  201. package/build/server/chunks/_server.ts-vekTmWAx.js.map +1 -0
  202. package/build/server/chunks/{auth-CEgFis71.js → auth-DeCdZ83n.js} +2 -2
  203. package/build/server/chunks/{auth-CEgFis71.js.map → auth-DeCdZ83n.js.map} +1 -1
  204. package/build/server/chunks/client-BdGHe_hY.js +25 -0
  205. package/build/server/chunks/client-BdGHe_hY.js.map +1 -0
  206. package/build/server/chunks/client2-CCBGA-2V.js +7 -0
  207. package/build/server/chunks/client2-CCBGA-2V.js.map +1 -0
  208. package/build/server/chunks/error-DDXB3duW.js +12 -0
  209. package/build/server/chunks/error-DDXB3duW.js.map +1 -0
  210. package/build/server/chunks/{exports-CJ0Q5XmL.js → index-DwaY1cAm.js} +1111 -1634
  211. package/build/server/chunks/index-DwaY1cAm.js.map +1 -0
  212. package/build/server/chunks/index-server-CrDaL06Y.js +9 -0
  213. package/build/server/chunks/index-server-CrDaL06Y.js.map +1 -0
  214. package/build/server/chunks/index2-CgclKpUj.js +58 -0
  215. package/build/server/chunks/index2-CgclKpUj.js.map +1 -0
  216. package/build/server/chunks/{library-apns-BHxLmuIx.js → library-apns-BqJbvSKh.js} +4 -4
  217. package/build/server/chunks/library-apns-BqJbvSKh.js.map +1 -0
  218. package/build/server/chunks/markdown-W_mTBct0.js +8 -0
  219. package/build/server/chunks/markdown-W_mTBct0.js.map +1 -0
  220. package/build/server/chunks/opencode-db-path-DcfhJtJy.js +15 -0
  221. package/build/server/chunks/opencode-db-path-DcfhJtJy.js.map +1 -0
  222. package/build/server/chunks/{pty-manager-C0FhBiVq.js → pty-manager-BQVB7IVj.js} +155 -326
  223. package/build/server/chunks/pty-manager-BQVB7IVj.js.map +1 -0
  224. package/build/server/chunks/root-DDSnEAZv.js +1171 -0
  225. package/build/server/chunks/root-DDSnEAZv.js.map +1 -0
  226. package/build/server/chunks/{shared-server-BDY8jh20.js → shared-server-sSGG17Df.js} +2 -3
  227. package/build/server/chunks/{shared-server-BDY8jh20.js.map → shared-server-sSGG17Df.js.map} +1 -1
  228. package/build/server/chunks/state.svelte-hBbXlUak.js +11 -0
  229. package/build/server/chunks/state.svelte-hBbXlUak.js.map +1 -0
  230. package/build/server/chunks/stores-DHNzYNpX.js +28 -0
  231. package/build/server/chunks/stores-DHNzYNpX.js.map +1 -0
  232. package/build/server/index.js +1085 -2242
  233. package/build/server/index.js.map +1 -1
  234. package/build/server/manifest.js +39 -25
  235. package/build/server/manifest.js.map +1 -1
  236. package/package.json +32 -9
  237. package/scripts/fix-generated-types.sh +37 -0
  238. package/scripts/homebrew/shooter.rb +51 -0
  239. package/scripts/install.sh +348 -186
  240. package/scripts/setup.cjs +215 -45
  241. package/server.ts +114 -71
  242. package/src/app.css +12 -3
  243. package/src/app.d.ts +13 -20
  244. package/src/app.html +3 -2
  245. package/src/generated/types/API.ts +280 -0
  246. package/src/generated/types/APN.ts +186 -203
  247. package/src/generated/types/CLI.ts +18 -25
  248. package/src/generated/types/Client.ts +589 -0
  249. package/src/generated/types/Config.ts +53 -0
  250. package/src/generated/types/Holder.ts +638 -0
  251. package/src/generated/types/JWT.ts +39 -50
  252. package/src/generated/types/Notification.ts +426 -0
  253. package/src/generated/types/OpenCode.ts +356 -0
  254. package/src/generated/types/Sessions.ts +570 -0
  255. package/src/generated/types/Terminal.ts +2184 -2071
  256. package/src/generated/types/WsProtocol.ts +2004 -0
  257. package/src/generated/types/index.ts +9 -3
  258. package/src/lib/env.ts +29 -0
  259. package/src/lib/modules/client/common/cache.ts +10 -2
  260. package/src/lib/modules/client/common/config-guard.ts +37 -5
  261. package/src/lib/modules/client/common/error.ts +10 -0
  262. package/src/lib/modules/client/common/index.ts +6 -5
  263. package/src/lib/modules/client/common/markdown.ts +22 -1
  264. package/src/lib/modules/client/common/native-bridge.ts +28 -20
  265. package/src/lib/modules/client/common/time.ts +13 -11
  266. package/src/lib/modules/client/terminal/ChatView.svelte +354 -74
  267. package/src/lib/modules/client/terminal/CommandPalette.svelte +3 -2
  268. package/src/lib/modules/client/terminal/ConnectionStatus.svelte +7 -1
  269. package/src/lib/modules/client/terminal/LaunchSheet.svelte +147 -84
  270. package/src/lib/modules/client/terminal/QuickKeys.svelte +3 -1
  271. package/src/lib/modules/client/terminal/ShortcutsHelp.svelte +2 -5
  272. package/src/lib/modules/client/terminal/keyboard-shortcuts.ts +27 -24
  273. package/src/lib/modules/client/terminal/xterm-wrapper.ts +74 -45
  274. package/src/lib/modules/server/apn/library-apns.ts +3 -2
  275. package/src/lib/modules/server/apn/notification-history.ts +2 -13
  276. package/src/lib/modules/server/apn/notification-sessions.ts +3 -13
  277. package/src/lib/modules/server/apn/pending-requests.ts +3 -8
  278. package/src/lib/modules/server/apn/types.ts +5 -4
  279. package/src/lib/modules/server/cli/index.ts +3 -2
  280. package/src/lib/modules/server/fcm/fcm-service.ts +8 -6
  281. package/src/lib/modules/server/sessions/jsonl-parser.ts +3 -3
  282. package/src/lib/modules/server/sessions/jsonl-reader.ts +86 -26
  283. package/src/lib/modules/server/sessions/opencode-db-path.ts +26 -0
  284. package/src/lib/modules/server/sessions/opencode-reader.ts +13 -15
  285. package/src/lib/modules/server/sessions/process-detector.ts +103 -0
  286. package/src/lib/modules/server/sessions/types.ts +11 -22
  287. package/src/lib/modules/server/terminal/holder-client.ts +272 -248
  288. package/src/lib/modules/server/terminal/opencode-watcher.ts +547 -556
  289. package/src/lib/modules/server/terminal/pty-holder.cjs +37 -8
  290. package/src/lib/modules/server/terminal/pty-manager.ts +157 -115
  291. package/src/lib/modules/server/terminal/session-watcher.ts +6 -4
  292. package/src/lib/modules/server/terminal/terminal-store.ts +131 -128
  293. package/src/lib/modules/server/utils/error.ts +9 -0
  294. package/src/lib/modules/server/ws/events-handler.ts +12 -6
  295. package/src/lib/modules/server/ws/keepalive.ts +86 -69
  296. package/src/lib/modules/server/ws/server.ts +43 -37
  297. package/src/lib/modules/server/ws/session-handler.ts +332 -147
  298. package/src/lib/modules/server/ws/terminal-handler.ts +29 -17
  299. package/src/lib/modules/server/ws/ticket-store.ts +29 -26
  300. package/src/lib/theme.css +30 -0
  301. package/src/lib/types/config.ts +1 -6
  302. package/src/routes/+error.svelte +94 -0
  303. package/src/routes/+layout.svelte +66 -31
  304. package/src/routes/+page.svelte +25 -22
  305. package/src/routes/api/debug/+server.ts +3 -1
  306. package/src/routes/api/device-token/+server.ts +60 -60
  307. package/src/routes/api/health/+server.ts +81 -73
  308. package/src/routes/api/notify/+server.ts +115 -68
  309. package/src/routes/api/qr-config/+server.ts +30 -32
  310. package/src/routes/api/response/+server.ts +9 -4
  311. package/src/routes/api/sessions/+server.ts +15 -5
  312. package/src/routes/api/sessions/connect/+server.ts +125 -0
  313. package/src/routes/api/sessions/detect/+server.ts +27 -0
  314. package/src/routes/api/terminals/+server.ts +26 -24
  315. package/src/routes/api/terminals/[id]/+server.ts +13 -7
  316. package/src/routes/api/terminals/[id]/paste-image/+server.ts +54 -52
  317. package/src/routes/api/terminals/[id]/resize/+server.ts +6 -3
  318. package/src/routes/api/webhook/+server.ts +8 -10
  319. package/src/routes/api/ws-status/+server.ts +7 -5
  320. package/src/routes/api/ws-ticket/+server.ts +42 -41
  321. package/src/routes/config/+page.svelte +149 -75
  322. package/src/routes/project/+page.svelte +165 -35
  323. package/src/routes/session/[id]/+page.svelte +479 -283
  324. package/src/routes/terminals/+page.svelte +58 -45
  325. package/src/routes/terminals/[id]/+page.svelte +223 -91
  326. package/build/client/_app/immutable/assets/0.CM9Hl6d-.css.br +0 -0
  327. package/build/client/_app/immutable/assets/0.CM9Hl6d-.css.gz +0 -0
  328. package/build/client/_app/immutable/assets/3.C0uFg0IS.css +0 -1
  329. package/build/client/_app/immutable/assets/3.C0uFg0IS.css.br +0 -0
  330. package/build/client/_app/immutable/assets/3.C0uFg0IS.css.gz +0 -0
  331. package/build/client/_app/immutable/assets/4.cJuCkJKZ.css +0 -1
  332. package/build/client/_app/immutable/assets/4.cJuCkJKZ.css.br +0 -0
  333. package/build/client/_app/immutable/assets/4.cJuCkJKZ.css.gz +0 -0
  334. package/build/client/_app/immutable/assets/5.DRjApZQW.css +0 -1
  335. package/build/client/_app/immutable/assets/5.DRjApZQW.css.br +0 -0
  336. package/build/client/_app/immutable/assets/5.DRjApZQW.css.gz +0 -0
  337. package/build/client/_app/immutable/assets/6.AraZrY8I.css +0 -1
  338. package/build/client/_app/immutable/assets/6.AraZrY8I.css.br +0 -0
  339. package/build/client/_app/immutable/assets/6.AraZrY8I.css.gz +0 -0
  340. package/build/client/_app/immutable/assets/7.BCJ1IuMx.css +0 -1
  341. package/build/client/_app/immutable/assets/7.BCJ1IuMx.css.br +0 -0
  342. package/build/client/_app/immutable/assets/7.BCJ1IuMx.css.gz +0 -0
  343. package/build/client/_app/immutable/assets/ChatView.CsdBAOKx.css +0 -1
  344. package/build/client/_app/immutable/assets/ChatView.CsdBAOKx.css.br +0 -0
  345. package/build/client/_app/immutable/assets/ChatView.CsdBAOKx.css.gz +0 -0
  346. package/build/client/_app/immutable/assets/markdown.B0b5w2tq.css +0 -1
  347. package/build/client/_app/immutable/assets/markdown.B0b5w2tq.css.br +0 -0
  348. package/build/client/_app/immutable/assets/markdown.B0b5w2tq.css.gz +0 -0
  349. package/build/client/_app/immutable/chunks/BNJphC1q.js +0 -56
  350. package/build/client/_app/immutable/chunks/BNJphC1q.js.br +0 -0
  351. package/build/client/_app/immutable/chunks/BNJphC1q.js.gz +0 -0
  352. package/build/client/_app/immutable/chunks/Bvk7mfPM.js +0 -1
  353. package/build/client/_app/immutable/chunks/Bvk7mfPM.js.br +0 -0
  354. package/build/client/_app/immutable/chunks/Bvk7mfPM.js.gz +0 -0
  355. package/build/client/_app/immutable/chunks/CAokzuPQ.js +0 -1
  356. package/build/client/_app/immutable/chunks/CAokzuPQ.js.br +0 -0
  357. package/build/client/_app/immutable/chunks/CAokzuPQ.js.gz +0 -0
  358. package/build/client/_app/immutable/chunks/CGLrx-H5.js +0 -1
  359. package/build/client/_app/immutable/chunks/CGLrx-H5.js.br +0 -0
  360. package/build/client/_app/immutable/chunks/CGLrx-H5.js.gz +0 -0
  361. package/build/client/_app/immutable/chunks/CgCpWzEA.js +0 -1
  362. package/build/client/_app/immutable/chunks/CgCpWzEA.js.br +0 -0
  363. package/build/client/_app/immutable/chunks/CgCpWzEA.js.gz +0 -0
  364. package/build/client/_app/immutable/chunks/Cjwk_cGO.js +0 -6
  365. package/build/client/_app/immutable/chunks/Cjwk_cGO.js.br +0 -0
  366. package/build/client/_app/immutable/chunks/Cjwk_cGO.js.gz +0 -0
  367. package/build/client/_app/immutable/chunks/CtQ8EED1.js +0 -11
  368. package/build/client/_app/immutable/chunks/CtQ8EED1.js.br +0 -0
  369. package/build/client/_app/immutable/chunks/CtQ8EED1.js.gz +0 -0
  370. package/build/client/_app/immutable/chunks/DERQCisl.js +0 -1
  371. package/build/client/_app/immutable/chunks/DERQCisl.js.br +0 -0
  372. package/build/client/_app/immutable/chunks/DERQCisl.js.gz +0 -0
  373. package/build/client/_app/immutable/chunks/DKrg8TQs.js +0 -1
  374. package/build/client/_app/immutable/chunks/DKrg8TQs.js.br +0 -0
  375. package/build/client/_app/immutable/chunks/DKrg8TQs.js.gz +0 -0
  376. package/build/client/_app/immutable/chunks/Dkkpz_4D.js +0 -126
  377. package/build/client/_app/immutable/chunks/Dkkpz_4D.js.br +0 -0
  378. package/build/client/_app/immutable/chunks/Dkkpz_4D.js.gz +0 -0
  379. package/build/client/_app/immutable/chunks/DoczjQhA.js +0 -1
  380. package/build/client/_app/immutable/chunks/DoczjQhA.js.br +0 -0
  381. package/build/client/_app/immutable/chunks/DoczjQhA.js.gz +0 -0
  382. package/build/client/_app/immutable/chunks/RpcNruLP.js +0 -2
  383. package/build/client/_app/immutable/chunks/RpcNruLP.js.br +0 -0
  384. package/build/client/_app/immutable/chunks/RpcNruLP.js.gz +0 -0
  385. package/build/client/_app/immutable/chunks/a-St0Zwo.js +0 -1
  386. package/build/client/_app/immutable/chunks/a-St0Zwo.js.br +0 -0
  387. package/build/client/_app/immutable/chunks/a-St0Zwo.js.gz +0 -0
  388. package/build/client/_app/immutable/chunks/bo70OQUZ.js +0 -1
  389. package/build/client/_app/immutable/chunks/bo70OQUZ.js.br +0 -0
  390. package/build/client/_app/immutable/chunks/bo70OQUZ.js.gz +0 -0
  391. package/build/client/_app/immutable/entry/app.QvGgdvTI.js +0 -2
  392. package/build/client/_app/immutable/entry/app.QvGgdvTI.js.br +0 -0
  393. package/build/client/_app/immutable/entry/app.QvGgdvTI.js.gz +0 -0
  394. package/build/client/_app/immutable/entry/start.BntDNRMC.js +0 -1
  395. package/build/client/_app/immutable/entry/start.BntDNRMC.js.br +0 -0
  396. package/build/client/_app/immutable/entry/start.BntDNRMC.js.gz +0 -0
  397. package/build/client/_app/immutable/nodes/0.CzkdvJ7j.js +0 -1
  398. package/build/client/_app/immutable/nodes/0.CzkdvJ7j.js.br +0 -0
  399. package/build/client/_app/immutable/nodes/0.CzkdvJ7j.js.gz +0 -0
  400. package/build/client/_app/immutable/nodes/1.MG1QhfrI.js +0 -1
  401. package/build/client/_app/immutable/nodes/1.MG1QhfrI.js.br +0 -0
  402. package/build/client/_app/immutable/nodes/1.MG1QhfrI.js.gz +0 -0
  403. package/build/client/_app/immutable/nodes/2.B4MlOSh6.js +0 -1
  404. package/build/client/_app/immutable/nodes/2.B4MlOSh6.js.br +0 -0
  405. package/build/client/_app/immutable/nodes/2.B4MlOSh6.js.gz +0 -0
  406. package/build/client/_app/immutable/nodes/3.DIwYkjDn.js +0 -3
  407. package/build/client/_app/immutable/nodes/3.DIwYkjDn.js.br +0 -0
  408. package/build/client/_app/immutable/nodes/3.DIwYkjDn.js.gz +0 -0
  409. package/build/client/_app/immutable/nodes/4.D-cIe70D.js +0 -1
  410. package/build/client/_app/immutable/nodes/4.D-cIe70D.js.br +0 -0
  411. package/build/client/_app/immutable/nodes/4.D-cIe70D.js.gz +0 -0
  412. package/build/client/_app/immutable/nodes/5.D7zPRe3L.js +0 -1
  413. package/build/client/_app/immutable/nodes/5.D7zPRe3L.js.br +0 -0
  414. package/build/client/_app/immutable/nodes/5.D7zPRe3L.js.gz +0 -0
  415. package/build/client/_app/immutable/nodes/6.BB7QE48r.js +0 -2
  416. package/build/client/_app/immutable/nodes/6.BB7QE48r.js.br +0 -0
  417. package/build/client/_app/immutable/nodes/6.BB7QE48r.js.gz +0 -0
  418. package/build/client/_app/immutable/nodes/7.D8mqsrZG.js +0 -2
  419. package/build/client/_app/immutable/nodes/7.D8mqsrZG.js.br +0 -0
  420. package/build/client/_app/immutable/nodes/7.D8mqsrZG.js.gz +0 -0
  421. package/build/client/manifest.webmanifest +0 -1
  422. package/build/client/registerSW.js +0 -1
  423. package/build/client/registerSW.js.br +0 -0
  424. package/build/client/registerSW.js.gz +0 -0
  425. package/build/client/sw.js +0 -222
  426. package/build/client/sw.js.br +0 -0
  427. package/build/client/sw.js.gz +0 -0
  428. package/build/client/workbox-5119daf5.js +0 -3395
  429. package/build/client/workbox-5119daf5.js.br +0 -0
  430. package/build/client/workbox-5119daf5.js.gz +0 -0
  431. package/build/server/chunks/0-q2IUp76Y.js +0 -9
  432. package/build/server/chunks/0-q2IUp76Y.js.map +0 -1
  433. package/build/server/chunks/1-CU50G5wZ.js +0 -9
  434. package/build/server/chunks/1-CU50G5wZ.js.map +0 -1
  435. package/build/server/chunks/2-D01t9s8T.js +0 -9
  436. package/build/server/chunks/2-D01t9s8T.js.map +0 -1
  437. package/build/server/chunks/3-5PUQ04wC.js +0 -9
  438. package/build/server/chunks/3-5PUQ04wC.js.map +0 -1
  439. package/build/server/chunks/4-e7gywnSG.js +0 -9
  440. package/build/server/chunks/4-e7gywnSG.js.map +0 -1
  441. package/build/server/chunks/5-CA1SA6KZ.js +0 -9
  442. package/build/server/chunks/5-CA1SA6KZ.js.map +0 -1
  443. package/build/server/chunks/6-71H221sV.js +0 -9
  444. package/build/server/chunks/6-71H221sV.js.map +0 -1
  445. package/build/server/chunks/7-Bo-vmdyz.js +0 -9
  446. package/build/server/chunks/7-Bo-vmdyz.js.map +0 -1
  447. package/build/server/chunks/_layout.svelte-SFHOxs74.js +0 -132
  448. package/build/server/chunks/_layout.svelte-SFHOxs74.js.map +0 -1
  449. package/build/server/chunks/_page.svelte-B4w-2wD-.js +0 -120
  450. package/build/server/chunks/_page.svelte-B4w-2wD-.js.map +0 -1
  451. package/build/server/chunks/_page.svelte-B_qAXjkh.js +0 -213
  452. package/build/server/chunks/_page.svelte-B_qAXjkh.js.map +0 -1
  453. package/build/server/chunks/_page.svelte-CsF1_TRG.js +0 -50
  454. package/build/server/chunks/_page.svelte-CsF1_TRG.js.map +0 -1
  455. package/build/server/chunks/_page.svelte-DJC6U-P0.js +0 -68
  456. package/build/server/chunks/_page.svelte-DJC6U-P0.js.map +0 -1
  457. package/build/server/chunks/_page.svelte-DQ6HBtsz.js +0 -407
  458. package/build/server/chunks/_page.svelte-DQ6HBtsz.js.map +0 -1
  459. package/build/server/chunks/_page.svelte-LbhhjP21.js +0 -148
  460. package/build/server/chunks/_page.svelte-LbhhjP21.js.map +0 -1
  461. package/build/server/chunks/_server.ts-BL2FGb5Z.js.map +0 -1
  462. package/build/server/chunks/_server.ts-BgdjBZco.js.map +0 -1
  463. package/build/server/chunks/_server.ts-BihKSdj_.js.map +0 -1
  464. package/build/server/chunks/_server.ts-BjOJsoy4.js.map +0 -1
  465. package/build/server/chunks/_server.ts-C29xzfaw.js.map +0 -1
  466. package/build/server/chunks/_server.ts-CPa6DgIt.js.map +0 -1
  467. package/build/server/chunks/_server.ts-CbDRDIoP.js.map +0 -1
  468. package/build/server/chunks/_server.ts-Cl1OEWL4.js +0 -54
  469. package/build/server/chunks/_server.ts-Cl1OEWL4.js.map +0 -1
  470. package/build/server/chunks/_server.ts-ColfDHW8.js.map +0 -1
  471. package/build/server/chunks/_server.ts-Cv_OrRuL.js.map +0 -1
  472. package/build/server/chunks/_server.ts-DRVbgm6k.js.map +0 -1
  473. package/build/server/chunks/_server.ts-DfajWaqh.js.map +0 -1
  474. package/build/server/chunks/client-CxCatAKr.js +0 -255
  475. package/build/server/chunks/client-CxCatAKr.js.map +0 -1
  476. package/build/server/chunks/error.svelte-BqdwMWdK.js +0 -26
  477. package/build/server/chunks/error.svelte-BqdwMWdK.js.map +0 -1
  478. package/build/server/chunks/exports-CJ0Q5XmL.js.map +0 -1
  479. package/build/server/chunks/index2-DAxIoAO-.js +0 -36
  480. package/build/server/chunks/index2-DAxIoAO-.js.map +0 -1
  481. package/build/server/chunks/jsonl-parser-dmZU_Hyu.js +0 -137
  482. package/build/server/chunks/jsonl-parser-dmZU_Hyu.js.map +0 -1
  483. package/build/server/chunks/library-apns-BHxLmuIx.js.map +0 -1
  484. package/build/server/chunks/markdown-Bxrl3cCF.js +0 -1241
  485. package/build/server/chunks/markdown-Bxrl3cCF.js.map +0 -1
  486. package/build/server/chunks/pty-manager-C0FhBiVq.js.map +0 -1
  487. package/build/server/chunks/stores-D0HorpgL.js +0 -36
  488. package/build/server/chunks/stores-D0HorpgL.js.map +0 -1
@@ -1,45 +1,43 @@
1
1
  import { env } from '$env/dynamic/private';
2
2
  import { validateAuth } from '$lib/modules/server/auth';
3
+ import { toErrorMessage } from '$lib/modules/server/utils/error';
3
4
  import { json } from '@sveltejs/kit';
4
5
  import QRCode from 'qrcode';
5
6
 
6
7
  import type { RequestHandler } from './$types';
7
8
 
8
- export const GET: RequestHandler = async ({ request }) => {
9
- const authError = validateAuth(request);
10
- if (authError) {
11
- return authError;
12
- }
9
+ export const GET: RequestHandler = async ({ request, url }) => {
10
+ const authError = validateAuth(request);
11
+ if (authError) {
12
+ return authError;
13
+ }
13
14
 
14
- const apiKey = env.API_KEY?.trim();
15
- if (!apiKey) {
16
- return json({ error: 'API_KEY not configured on server' }, { status: 500 });
17
- }
15
+ const apiKey = env.API_KEY?.trim();
16
+ if (!apiKey) {
17
+ return json({ error: 'API_KEY not configured on server' }, { status: 500 });
18
+ }
18
19
 
19
- // Derive the server URL from the incoming request's Host header so the QR
20
- // code always points to the right address (works behind tunnels, proxies, etc.)
21
- const proto = request.headers.get('x-forwarded-proto') || 'http';
22
- const host = request.headers.get('x-forwarded-host') || request.headers.get('host') || 'localhost:3000';
23
- const serverUrl = `${proto}://${host}`;
20
+ // Use a configured server URL from env when available (trusted), otherwise
21
+ // fall back to url.origin (derived from the incoming request URL by SvelteKit).
22
+ const serverUrl = env.ORIGIN?.trim() || url.origin;
24
23
 
25
- const configPayload = JSON.stringify({ apiKey, serverUrl });
24
+ const configPayload = JSON.stringify({ apiKey, serverUrl });
26
25
 
27
- try {
28
- const dataUrl = await QRCode.toDataURL(configPayload, {
29
- color: {
30
- dark: '#ededed',
31
- light: '#0a0a0a',
32
- },
33
- errorCorrectionLevel: 'M',
34
- margin: 2,
35
- type: 'image/png',
36
- width: 280,
37
- });
26
+ try {
27
+ const dataUrl = await QRCode.toDataURL(configPayload, {
28
+ color: {
29
+ dark: '#ededed',
30
+ light: '#0a0a0a',
31
+ },
32
+ errorCorrectionLevel: 'M',
33
+ margin: 2,
34
+ type: 'image/png',
35
+ width: 280,
36
+ });
38
37
 
39
- return json({ dataUrl, serverUrl });
40
- } catch (error) {
41
- const err = error as Error;
42
- console.error('[qr-config] QR generation failed:', err.message);
43
- return json({ error: 'Failed to generate QR code' }, { status: 500 });
44
- }
38
+ return json({ dataUrl, serverUrl });
39
+ } catch (error) {
40
+ console.error('[qr-config] QR generation failed:', toErrorMessage(error));
41
+ return json({ error: 'Failed to generate QR code' }, { status: 500 });
42
+ }
45
43
  };
@@ -1,5 +1,6 @@
1
1
  import { cleanup, getDecision, setDecision } from '$lib/modules/server/apn/pending-requests';
2
2
  import { validateAuth } from '$lib/modules/server/auth';
3
+ import { toErrorMessage } from '$lib/modules/server/utils/error';
3
4
  import { json } from '@sveltejs/kit';
4
5
 
5
6
  import type { RequestHandler } from './$types';
@@ -9,7 +10,9 @@ export const POST: RequestHandler = async ({ request }) => {
9
10
  cleanup();
10
11
 
11
12
  const authError = validateAuth(request);
12
- if (authError) {return authError;}
13
+ if (authError) {
14
+ return authError;
15
+ }
13
16
 
14
17
  let body: { decision?: string; requestId?: string };
15
18
  try {
@@ -42,8 +45,8 @@ export const POST: RequestHandler = async ({ request }) => {
42
45
  timestamp: new Date().toISOString(),
43
46
  });
44
47
  } catch (error) {
45
- const err = error as Error;
46
- return json({ details: err.message, error: 'Failed to process response' }, { status: 500 });
48
+ console.error('[response] Failed to process response:', toErrorMessage(error));
49
+ return json({ error: 'Failed to process response' }, { status: 500 });
47
50
  }
48
51
  };
49
52
 
@@ -52,7 +55,9 @@ export const GET: RequestHandler = ({ request, url }) => {
52
55
  cleanup();
53
56
 
54
57
  const authErr = validateAuth(request);
55
- if (authErr) {return authErr;}
58
+ if (authErr) {
59
+ return authErr;
60
+ }
56
61
 
57
62
  const requestId = url.searchParams.get('requestId');
58
63
  if (!requestId) {
@@ -13,10 +13,10 @@ import { json } from '@sveltejs/kit';
13
13
 
14
14
  import type { RequestHandler } from './$types';
15
15
 
16
- // Server-side cache for merged projects (30s TTL)
16
+ // Server-side cache for merged projects (5s TTL)
17
17
  let cachedProjects: null | ProjectGroup[] = null;
18
18
  let cacheTimestamp = 0;
19
- const CACHE_TTL_MS = 30_000;
19
+ const CACHE_TTL_MS = 5_000;
20
20
 
21
21
  function getMergedProjects(): ProjectGroup[] {
22
22
  const now = Date.now();
@@ -57,14 +57,24 @@ function getMergedProjects(): ProjectGroup[] {
57
57
 
58
58
  export const GET: RequestHandler = ({ request, url }) => {
59
59
  const authError = validateAuth(request);
60
- if (authError) {return authError;}
60
+ if (authError) {
61
+ return authError;
62
+ }
63
+
64
+ // Force cache invalidation when ?refresh=true is passed
65
+ if (url.searchParams.get('refresh') === 'true') {
66
+ cachedProjects = null;
67
+ cacheTimestamp = 0;
68
+ }
61
69
 
62
70
  const sessionId = url.searchParams.get('id');
63
71
 
64
72
  if (sessionId) {
65
73
  const projectId = url.searchParams.get('project') || '';
66
- const offset = parseInt(url.searchParams.get('offset') || '0');
67
- const limit = parseInt(url.searchParams.get('limit') || '100');
74
+ const rawOffset = parseInt(url.searchParams.get('offset') || '0');
75
+ const rawLimit = parseInt(url.searchParams.get('limit') || '200');
76
+ const offset = Number.isFinite(rawOffset) && rawOffset >= 0 ? rawOffset : 0;
77
+ const limit = Number.isFinite(rawLimit) && rawLimit >= 1 ? Math.min(rawLimit, 500) : 200;
68
78
 
69
79
  // Resolve short project ID to full path for the readers
70
80
  const allProjects = getMergedProjects();
@@ -0,0 +1,125 @@
1
+ import { validateAuth } from '$lib/modules/server/auth';
2
+ import { ptyManager } from '$lib/modules/server/terminal/pty-manager';
3
+ import { toErrorMessage } from '$lib/modules/server/utils/error';
4
+ import { json } from '@sveltejs/kit';
5
+ import { realpathSync, statSync } from 'fs';
6
+ import { isAbsolute, relative } from 'path';
7
+
8
+ import type { RequestHandler } from './$types';
9
+
10
+ // POST /api/sessions/connect — Resume a Claude Code / OpenCode session in a new terminal
11
+ export const POST: RequestHandler = async ({ request }) => {
12
+ const authError = validateAuth(request);
13
+ if (authError) {
14
+ return authError;
15
+ }
16
+
17
+ let body: { command?: string; cwd?: string; noCreate?: boolean; sessionId?: string };
18
+ try {
19
+ body = (await request.json()) as {
20
+ command?: string;
21
+ cwd?: string;
22
+ noCreate?: boolean;
23
+ sessionId?: string;
24
+ };
25
+ } catch {
26
+ return json({ error: 'Invalid JSON in request body' }, { status: 400 });
27
+ }
28
+
29
+ const { command, cwd, noCreate, sessionId } = body;
30
+
31
+ // --- Validate required fields ---
32
+
33
+ if (!sessionId || typeof sessionId !== 'string') {
34
+ return json({ error: 'sessionId is required (string)' }, { status: 400 });
35
+ }
36
+
37
+ if (!cwd || typeof cwd !== 'string') {
38
+ return json({ error: 'cwd is required (string)' }, { status: 400 });
39
+ }
40
+
41
+ if (!command || (command !== 'claude' && command !== 'opencode')) {
42
+ return json({ error: 'command must be "claude" or "opencode"' }, { status: 400 });
43
+ }
44
+
45
+ // --- Validate cwd (same checks as POST /api/terminals) ---
46
+
47
+ let realCwd: string;
48
+ try {
49
+ realCwd = realpathSync(cwd);
50
+ if (!statSync(realCwd).isDirectory()) {
51
+ return json({ error: 'Invalid working directory' }, { status: 400 });
52
+ }
53
+ } catch {
54
+ return json({ error: 'Invalid working directory' }, { status: 400 });
55
+ }
56
+ const home = process.env.HOME || '';
57
+ if (home) {
58
+ const rel = relative(home, realCwd);
59
+ if (rel.startsWith('..') || isAbsolute(rel)) {
60
+ return json({ error: 'Working directory must be under home directory' }, { status: 400 });
61
+ }
62
+ }
63
+
64
+ // --- Reuse existing terminal if one is already running for this session ---
65
+
66
+ const existing = ptyManager
67
+ .list()
68
+ .find((t) => t.status === 'running' && (
69
+ t.sessionFile?.endsWith(`/${sessionId}.jsonl`) || t.openCodeSessionId === sessionId
70
+ ));
71
+
72
+ if (existing) {
73
+ console.log(
74
+ `[sessions/connect] Reusing terminal ${existing.id} for ${command} session ${sessionId}`
75
+ );
76
+ return json({
77
+ command: existing.command,
78
+ createdAt: existing.createdAt.toISOString(),
79
+ cwd: existing.cwd,
80
+ id: existing.id,
81
+ pid: existing.pid,
82
+ reused: true,
83
+ sessionId,
84
+ sessionWs: `/ws/session/${existing.id}`,
85
+ terminalId: existing.id,
86
+ ws: `/ws/terminal/${existing.id}`,
87
+ });
88
+ }
89
+
90
+ // No existing terminal — return 404 if caller requested no-create
91
+ if (noCreate) {
92
+ return json({ error: 'No existing terminal for this session' }, { status: 404 });
93
+ }
94
+
95
+ // --- Build args based on command ---
96
+
97
+ const args: string[] =
98
+ command === 'claude' ? ['--resume', sessionId] : ['--session', sessionId];
99
+
100
+ try {
101
+ const terminal = await ptyManager.create(command, args, realCwd, 120, 40);
102
+
103
+ console.log(
104
+ `[sessions/connect] Created terminal ${terminal.id} for ${command} session ${sessionId} (pid=${terminal.pid})`
105
+ );
106
+
107
+ return json(
108
+ {
109
+ command: terminal.command,
110
+ createdAt: terminal.createdAt instanceof Date ? terminal.createdAt.toISOString() : terminal.createdAt,
111
+ cwd: terminal.cwd,
112
+ id: terminal.id,
113
+ pid: terminal.pid,
114
+ sessionId,
115
+ sessionWs: `/ws/session/${terminal.id}`,
116
+ terminalId: terminal.id,
117
+ ws: `/ws/terminal/${terminal.id}`,
118
+ },
119
+ { status: 201 }
120
+ );
121
+ } catch (error) {
122
+ console.error('[sessions/connect] Failed to create terminal:', toErrorMessage(error));
123
+ return json({ error: 'Failed to create terminal for session' }, { status: 500 });
124
+ }
125
+ };
@@ -0,0 +1,27 @@
1
+ import { validateAuth } from '$lib/modules/server/auth';
2
+ import { detectRunningAISessions } from '$lib/modules/server/sessions/process-detector';
3
+ import { toErrorMessage } from '$lib/modules/server/utils/error';
4
+ import { json } from '@sveltejs/kit';
5
+
6
+ import type { RequestHandler } from './$types';
7
+
8
+ // GET /api/sessions/detect — Detect running Claude Code / OpenCode processes
9
+ export const GET: RequestHandler = ({ request }) => {
10
+ const authError = validateAuth(request);
11
+ if (authError) {
12
+ return authError;
13
+ }
14
+
15
+ try {
16
+ const processes = detectRunningAISessions();
17
+
18
+ return json({
19
+ count: processes.length,
20
+ processes,
21
+ timestamp: new Date().toISOString(),
22
+ });
23
+ } catch (error) {
24
+ console.error('[sessions/detect] Failed to detect running sessions:', toErrorMessage(error));
25
+ return json({ error: 'Failed to detect running sessions' }, { status: 500 });
26
+ }
27
+ };
@@ -1,8 +1,9 @@
1
1
  import { validateAuth } from '$lib/modules/server/auth';
2
- import { ptyManager } from '$lib/modules/server/terminal/pty-manager.js';
2
+ import { ptyManager } from '$lib/modules/server/terminal/pty-manager';
3
+ import { toErrorMessage } from '$lib/modules/server/utils/error';
3
4
  import { json } from '@sveltejs/kit';
4
5
  import { realpathSync, statSync } from 'fs';
5
- import { basename } from 'path';
6
+ import { basename, isAbsolute, relative } from 'path';
6
7
 
7
8
  import type { RequestHandler } from './$types';
8
9
 
@@ -11,10 +12,12 @@ const ALLOWED_COMMANDS = ['zsh', 'bash', 'sh', 'fish', 'claude', 'opencode'];
11
12
  // GET /api/terminals — List all terminals (active + recently exited)
12
13
  export const GET: RequestHandler = ({ request }) => {
13
14
  const authError = validateAuth(request);
14
- if (authError) {return authError;}
15
+ if (authError) {
16
+ return authError;
17
+ }
15
18
 
16
19
  try {
17
- const terminals = ptyManager.list().map(t => ({
20
+ const terminals = ptyManager.list().map((t) => ({
18
21
  args: t.args,
19
22
  clientCount: t.clients.size,
20
23
  command: t.command,
@@ -35,15 +38,17 @@ export const GET: RequestHandler = ({ request }) => {
35
38
  timestamp: new Date().toISOString(),
36
39
  });
37
40
  } catch (error) {
38
- const err = error as Error;
39
- return json({ details: err.message, error: 'Failed to list terminals' }, { status: 500 });
41
+ console.error('[terminals] Failed to list terminals:', toErrorMessage(error));
42
+ return json({ error: 'Failed to list terminals' }, { status: 500 });
40
43
  }
41
44
  };
42
45
 
43
46
  // POST /api/terminals — Create a new terminal session
44
47
  export const POST: RequestHandler = async ({ request }) => {
45
48
  const authError = validateAuth(request);
46
- if (authError) {return authError;}
49
+ if (authError) {
50
+ return authError;
51
+ }
47
52
 
48
53
  let body: { args?: string[]; cols?: number; command?: string; cwd?: string; rows?: number };
49
54
  try {
@@ -70,7 +75,7 @@ export const POST: RequestHandler = async ({ request }) => {
70
75
  if (!ALLOWED_COMMANDS.includes(commandBasename)) {
71
76
  return json(
72
77
  { error: `Command not allowed. Allowed: ${ALLOWED_COMMANDS.join(', ')}` },
73
- { status: 400 },
78
+ { status: 400 }
74
79
  );
75
80
  }
76
81
 
@@ -82,15 +87,18 @@ export const POST: RequestHandler = async ({ request }) => {
82
87
  let realCwd: string;
83
88
  try {
84
89
  realCwd = realpathSync(cwd);
90
+ if (!statSync(realCwd).isDirectory()) {
91
+ return json({ error: 'cwd must be a directory' }, { status: 400 });
92
+ }
85
93
  } catch {
86
- return json({ error: 'Invalid working directory' }, { status: 400 });
87
- }
88
- if (!statSync(realCwd).isDirectory()) {
89
- return json({ error: 'Invalid working directory' }, { status: 400 });
94
+ return json({ error: 'cwd must be a directory' }, { status: 400 });
90
95
  }
91
96
  const home = process.env.HOME || '';
92
- if (home && !realCwd.startsWith(home)) {
93
- return json({ error: 'Working directory must be under home directory' }, { status: 400 });
97
+ if (home) {
98
+ const rel = relative(home, realCwd);
99
+ if (rel.startsWith('..') || isAbsolute(rel)) {
100
+ return json({ error: 'Working directory must be under home directory' }, { status: 400 });
101
+ }
94
102
  }
95
103
 
96
104
  if (args !== undefined && !Array.isArray(args)) {
@@ -110,13 +118,7 @@ export const POST: RequestHandler = async ({ request }) => {
110
118
  return json({ error: 'rows must be a positive number' }, { status: 400 });
111
119
  }
112
120
 
113
- const terminal = await ptyManager.create(
114
- command,
115
- args ?? [],
116
- realCwd,
117
- cols ?? 80,
118
- rows ?? 24,
119
- );
121
+ const terminal = await ptyManager.create(command, args ?? [], realCwd, cols ?? 80, rows ?? 24);
120
122
 
121
123
  console.log(
122
124
  `[terminals] Created terminal ${terminal.id} (pid=${terminal.pid}, command=${command})`
@@ -125,7 +127,7 @@ export const POST: RequestHandler = async ({ request }) => {
125
127
  return json(
126
128
  {
127
129
  command: terminal.command,
128
- createdAt: terminal.createdAt,
130
+ createdAt: terminal.createdAt instanceof Date ? terminal.createdAt.toISOString() : terminal.createdAt,
129
131
  cwd: terminal.cwd,
130
132
  id: terminal.id,
131
133
  pid: terminal.pid,
@@ -135,7 +137,7 @@ export const POST: RequestHandler = async ({ request }) => {
135
137
  { status: 201 }
136
138
  );
137
139
  } catch (error) {
138
- const err = error as Error;
139
- return json({ details: err.message, error: 'Failed to create terminal' }, { status: 500 });
140
+ console.error('[terminals] Failed to create terminal:', toErrorMessage(error));
141
+ return json({ error: 'Failed to create terminal' }, { status: 500 });
140
142
  }
141
143
  };
@@ -1,5 +1,6 @@
1
1
  import { validateAuth } from '$lib/modules/server/auth';
2
2
  import { ptyManager } from '$lib/modules/server/terminal/pty-manager.js';
3
+ import { toErrorMessage } from '$lib/modules/server/utils/error';
3
4
  import { json } from '@sveltejs/kit';
4
5
 
5
6
  import type { RequestHandler } from './$types';
@@ -7,7 +8,9 @@ import type { RequestHandler } from './$types';
7
8
  // GET /api/terminals/:id — Get terminal details by ID
8
9
  export const GET: RequestHandler = ({ params, request }) => {
9
10
  const authError = validateAuth(request);
10
- if (authError) {return authError;}
11
+ if (authError) {
12
+ return authError;
13
+ }
11
14
 
12
15
  try {
13
16
  const terminal = ptyManager.get(params.id);
@@ -25,7 +28,8 @@ export const GET: RequestHandler = ({ params, request }) => {
25
28
  exitCode: terminal.exitCode,
26
29
  exitedAt: terminal.exitedAt?.toISOString() ?? null,
27
30
  id: terminal.id,
28
- lastOutput: terminal.scrollback.length > 0 ? terminal.scrollback[terminal.scrollback.length - 1] : null,
31
+ lastOutput:
32
+ terminal.scrollback.length > 0 ? terminal.scrollback[terminal.scrollback.length - 1] : null,
29
33
  pid: terminal.pid,
30
34
  sessionWs: `/ws/session/${terminal.id}`,
31
35
  status: terminal.status,
@@ -33,15 +37,17 @@ export const GET: RequestHandler = ({ params, request }) => {
33
37
  ws: `/ws/terminal/${terminal.id}`,
34
38
  });
35
39
  } catch (error) {
36
- const err = error as Error;
37
- return json({ details: err.message, error: 'Failed to get terminal' }, { status: 500 });
40
+ console.error('[terminals] Failed to get terminal:', toErrorMessage(error));
41
+ return json({ error: 'Failed to get terminal' }, { status: 500 });
38
42
  }
39
43
  };
40
44
 
41
45
  // DELETE /api/terminals/:id — Kill terminal by ID (SIGTERM)
42
46
  export const DELETE: RequestHandler = ({ params, request }) => {
43
47
  const authError = validateAuth(request);
44
- if (authError) {return authError;}
48
+ if (authError) {
49
+ return authError;
50
+ }
45
51
 
46
52
  try {
47
53
  const terminal = ptyManager.get(params.id);
@@ -69,7 +75,7 @@ export const DELETE: RequestHandler = ({ params, request }) => {
69
75
  timestamp: new Date().toISOString(),
70
76
  });
71
77
  } catch (error) {
72
- const err = error as Error;
73
- return json({ details: err.message, error: 'Failed to kill terminal' }, { status: 500 });
78
+ console.error('[terminals] Failed to kill terminal:', toErrorMessage(error));
79
+ return json({ error: 'Failed to kill terminal' }, { status: 500 });
74
80
  }
75
81
  };
@@ -1,4 +1,5 @@
1
1
  import { validateAuth } from '$lib/modules/server/auth';
2
+ import { toErrorMessage } from '$lib/modules/server/utils/error';
2
3
  import { json } from '@sveltejs/kit';
3
4
  import { mkdirSync, writeFileSync } from 'fs';
4
5
 
@@ -6,56 +7,57 @@ import type { RequestHandler } from './$types';
6
7
 
7
8
  // POST /api/terminals/[id]/paste-image — Write clipboard image for a terminal
8
9
  export const POST: RequestHandler = async ({ params, request }) => {
9
- const authError = validateAuth(request);
10
- if (authError) {
11
- return authError;
12
- }
13
-
14
- const terminalId = params.id;
15
-
16
- // Validate terminalId to prevent path traversal
17
- if (!/^[A-Za-z0-9_-]+$/.test(terminalId)) {
18
- return json({ error: 'Invalid terminal ID' }, { status: 400 });
19
- }
20
-
21
- let body: { image: string };
22
- try {
23
- body = (await request.json()) as { image: string };
24
- } catch {
25
- return json({ error: 'Invalid JSON' }, { status: 400 });
26
- }
27
-
28
- if (!body.image || typeof body.image !== 'string') {
29
- return json({ error: 'image (base64) is required' }, { status: 400 });
30
- }
31
-
32
- // Decode base64 image
33
- let imageBuffer: Buffer;
34
- try {
35
- // Strip data URI prefix if present
36
- const base64Data = body.image.replace(/^data:image\/\w+;base64,/, '');
37
-
38
- // Validate base64 encoding via round-trip check
39
- // Buffer.from silently ignores invalid chars, so verify re-encoding matches
40
- const decoded = Buffer.from(base64Data, 'base64');
41
- if (decoded.length === 0 || decoded.toString('base64') !== base64Data) {
42
- return json({ error: 'Invalid base64 image data' }, { status: 400 });
43
- }
44
- imageBuffer = decoded;
45
- } catch {
46
- return json({ error: 'Invalid base64 image data' }, { status: 400 });
47
- }
48
-
49
- // Write to per-terminal clipboard directory
50
- const clipboardDir = `/tmp/shooter-clipboard-${terminalId}`;
51
- const imagePath = `${clipboardDir}/image.png`;
52
-
53
- try {
54
- mkdirSync(clipboardDir, { recursive: true });
55
- writeFileSync(imagePath, imageBuffer);
56
- } catch (err) {
57
- return json({ details: (err as Error).message, error: 'Failed to write image' }, { status: 500 });
58
- }
59
-
60
- return json({ path: imagePath, size: imageBuffer.length });
10
+ const authError = validateAuth(request);
11
+ if (authError) {
12
+ return authError;
13
+ }
14
+
15
+ const terminalId = params.id;
16
+
17
+ // Validate terminalId to prevent path traversal
18
+ if (!/^[A-Za-z0-9_-]+$/.test(terminalId)) {
19
+ return json({ error: 'Invalid terminal ID' }, { status: 400 });
20
+ }
21
+
22
+ let body: { image: string };
23
+ try {
24
+ body = (await request.json()) as { image: string };
25
+ } catch {
26
+ return json({ error: 'Invalid JSON' }, { status: 400 });
27
+ }
28
+
29
+ if (!body.image || typeof body.image !== 'string') {
30
+ return json({ error: 'image (base64) is required' }, { status: 400 });
31
+ }
32
+
33
+ // Decode base64 image
34
+ let imageBuffer: Buffer;
35
+ try {
36
+ // Strip data URI prefix if present
37
+ const base64Data = body.image.replace(/^data:image\/\w+;base64,/, '');
38
+
39
+ // Validate base64 encoding via round-trip check
40
+ // Buffer.from silently ignores invalid chars, so verify re-encoding matches
41
+ const decoded = Buffer.from(base64Data, 'base64');
42
+ if (decoded.length === 0 || decoded.toString('base64') !== base64Data) {
43
+ return json({ error: 'Invalid base64 image data' }, { status: 400 });
44
+ }
45
+ imageBuffer = decoded;
46
+ } catch {
47
+ return json({ error: 'Invalid base64 image data' }, { status: 400 });
48
+ }
49
+
50
+ // Write to per-terminal clipboard directory
51
+ const clipboardDir = `/tmp/shooter-clipboard-${terminalId}`;
52
+ const imagePath = `${clipboardDir}/image.png`;
53
+
54
+ try {
55
+ mkdirSync(clipboardDir, { recursive: true });
56
+ writeFileSync(imagePath, imageBuffer);
57
+ } catch (err) {
58
+ console.error('[paste-image] Failed to write image:', toErrorMessage(err));
59
+ return json({ error: 'Failed to write image' }, { status: 500 });
60
+ }
61
+
62
+ return json({ size: imageBuffer.length, success: true });
61
63
  };
@@ -1,5 +1,6 @@
1
1
  import { validateAuth } from '$lib/modules/server/auth';
2
2
  import { ptyManager } from '$lib/modules/server/terminal/pty-manager.js';
3
+ import { toErrorMessage } from '$lib/modules/server/utils/error';
3
4
  import { json } from '@sveltejs/kit';
4
5
 
5
6
  import type { RequestHandler } from './$types';
@@ -7,7 +8,9 @@ import type { RequestHandler } from './$types';
7
8
  // POST /api/terminals/:id/resize — Resize terminal
8
9
  export const POST: RequestHandler = async ({ params, request }) => {
9
10
  const authError = validateAuth(request);
10
- if (authError) {return authError;}
11
+ if (authError) {
12
+ return authError;
13
+ }
11
14
 
12
15
  let body: { cols?: number; rows?: number };
13
16
  try {
@@ -54,7 +57,7 @@ export const POST: RequestHandler = async ({ params, request }) => {
54
57
  timestamp: new Date().toISOString(),
55
58
  });
56
59
  } catch (error) {
57
- const err = error as Error;
58
- return json({ details: err.message, error: 'Failed to resize terminal' }, { status: 500 });
60
+ console.error('[terminals] Failed to resize terminal:', toErrorMessage(error));
61
+ return json({ error: 'Failed to resize terminal' }, { status: 500 });
59
62
  }
60
63
  };