@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
@@ -2,23 +2,24 @@
2
2
  // Relays terminal output to connected clients and routes client input
3
3
  // (keystrokes, resize, signals) back to the PTY.
4
4
 
5
+ import type { TerminalSignal } from '$generated/types';
5
6
  import type { WebSocket } from 'ws';
6
7
 
7
8
  // ── Types ────────────────────────────────────────────────────────────
8
9
 
9
10
  /** Inbound messages from the client. */
10
11
  type ClientMessage =
11
- | { cols: number; rows: number; type: 'resize'; }
12
- | { data: string; type: 'input'; }
13
- | { signal: 'SIGINT' | 'SIGTERM' | 'SIGTSTP'; type: 'signal'; };
12
+ | { cols: number; rows: number; type: 'resize' }
13
+ | { data: string; type: 'input' }
14
+ | { signal: TerminalSignal; type: 'signal' };
14
15
 
15
16
  /** Outbound messages to the client. */
16
17
  type ServerMessage =
17
- | { bytes: number; type: 'output-dropped'; }
18
- | { chunk: number; data: string; total: number; type: 'scrollback'; }
19
- | { code: null | number; signal: null | string; type: 'exit'; }
20
- | { data: string; type: 'output'; }
21
- | { message: string; type: 'error'; };
18
+ | { bytes: number; type: 'output-dropped' }
19
+ | { chunk: number; data: string; total: number; type: 'scrollback' }
20
+ | { code: null | number; signal: null | string; type: 'exit' }
21
+ | { data: string; type: 'output' }
22
+ | { message: string; type: 'error' };
22
23
 
23
24
  // ── Constants ────────────────────────────────────────────────────────
24
25
 
@@ -29,8 +30,8 @@ const SIGNAL_MAP: Record<string, NodeJS.Signals> = {
29
30
  SIGTSTP: 'SIGTSTP',
30
31
  };
31
32
 
32
- // ── PTY Manager interface ────────────────────────────────────────────
33
- // Minimal duck-typed interface matching the real pty-manager singleton.
33
+ // ── PTY Manager type ─────────────────────────────────────────────────
34
+ // Minimal duck-typed shape matching the real pty-manager singleton.
34
35
 
35
36
  interface ManagedTerminal {
36
37
  clients: Set<WebSocket>;
@@ -159,18 +160,29 @@ function parseClientMessage(raw: string): ClientMessage | null {
159
160
 
160
161
  switch (msg.type) {
161
162
  case 'input':
162
- if (typeof msg.data !== 'string') {return null;}
163
+ if (typeof msg.data !== 'string') {
164
+ return null;
165
+ }
163
166
  return { data: msg.data, type: 'input' };
164
167
  case 'resize':
165
- if (typeof msg.cols !== 'number' || typeof msg.rows !== 'number') {return null;}
166
- if (msg.cols < 1 || msg.rows < 1 || msg.cols > 500 || msg.rows > 200) {return null;}
168
+ if (
169
+ typeof msg.cols !== 'number' ||
170
+ typeof msg.rows !== 'number' ||
171
+ !Number.isFinite(msg.cols) ||
172
+ !Number.isFinite(msg.rows)
173
+ ) {
174
+ return null;
175
+ }
176
+ if (msg.cols < 1 || msg.rows < 1 || msg.cols > 500 || msg.rows > 200) {
177
+ return null;
178
+ }
167
179
  return { cols: Math.floor(msg.cols), rows: Math.floor(msg.rows), type: 'resize' };
168
180
  case 'signal':
169
- if (typeof msg.signal !== 'string' || !(msg.signal in SIGNAL_MAP)) {return null;}
181
+ if (typeof msg.signal !== 'string' || !Object.hasOwn(SIGNAL_MAP, msg.signal)) {
182
+ return null;
183
+ }
170
184
  return {
171
- signal: msg.signal as ClientMessage & { type: 'signal' } extends { signal: infer S }
172
- ? S
173
- : never,
185
+ signal: msg.signal as TerminalSignal,
174
186
  type: 'signal',
175
187
  };
176
188
  default:
@@ -1,24 +1,21 @@
1
1
  // WebSocket ticket store.
2
2
  // Generates short-lived, single-use tickets for authenticating WebSocket upgrades.
3
- // Tickets expire after 30 seconds and are cleaned up every 60 seconds.
3
+ // Tickets expire after 30 seconds and are cleaned up every 30 seconds.
4
4
  //
5
5
  // This avoids putting the long-lived API_KEY in WebSocket URL query parameters,
6
6
  // which would appear in proxy logs, Cloudflare access logs, and browser history.
7
7
 
8
- import { randomBytes } from 'crypto';
8
+ import type { Ticket } from '$generated/types';
9
9
 
10
- interface Ticket {
11
- createdAt: number;
12
- used: boolean;
13
- }
10
+ import { randomBytes } from 'crypto';
14
11
 
15
12
  // Use globalThis to ensure a single shared Map across module instances.
16
13
  // server.ts (via tsx) and SvelteKit's build handler load this module separately,
17
14
  // creating two different module scopes. globalThis bridges them.
18
15
  const GLOBAL_KEY = '__shooter_ws_tickets';
19
16
  const tickets: Map<string, Ticket> =
20
- (globalThis as Record<string, unknown>)[GLOBAL_KEY] as Map<string, Ticket> ||
21
- new Map<string, Ticket>();
17
+ ((globalThis as Record<string, unknown>)[GLOBAL_KEY] as Map<string, Ticket>) ||
18
+ new Map<string, Ticket>();
22
19
  (globalThis as Record<string, unknown>)[GLOBAL_KEY] = tickets;
23
20
 
24
21
  /**
@@ -26,9 +23,9 @@ const tickets: Map<string, Ticket> =
26
23
  * The ticket is valid for 30 seconds and can only be consumed once.
27
24
  */
28
25
  export function generateTicket(): string {
29
- const ticket = randomBytes(32).toString('hex');
30
- tickets.set(ticket, { createdAt: Date.now(), used: false });
31
- return ticket;
26
+ const ticket = randomBytes(32).toString('hex');
27
+ tickets.set(ticket, { createdAt: Date.now(), used: false });
28
+ return ticket;
32
29
  }
33
30
 
34
31
  /**
@@ -37,22 +34,28 @@ export function generateTicket(): string {
37
34
  * A valid ticket is marked as used (single-use) and cannot be reused.
38
35
  */
39
36
  export function validateTicket(ticket: null | string): boolean {
40
- if (!ticket) {return false;}
41
- const entry = tickets.get(ticket);
42
- if (!entry || entry.used) {return false;}
43
- if (Date.now() - entry.createdAt > 30_000) {
44
- tickets.delete(ticket);
45
- return false;
46
- }
47
- entry.used = true;
48
- return true;
37
+ if (!ticket) {
38
+ return false;
39
+ }
40
+ const entry = tickets.get(ticket);
41
+ if (!entry || entry.used) {
42
+ return false;
43
+ }
44
+ if (Date.now() - entry.createdAt > 30_000) {
45
+ tickets.delete(ticket);
46
+ return false;
47
+ }
48
+ entry.used = true;
49
+ return true;
49
50
  }
50
51
 
51
- // Cleanup expired tickets every 60 seconds.
52
+ // Cleanup expired tickets every 30 seconds (matches ticket lifetime).
52
53
  // .unref() ensures this timer does not prevent Node.js from exiting.
53
54
  setInterval(() => {
54
- const now = Date.now();
55
- for (const [key, val] of tickets) {
56
- if (now - val.createdAt > 60_000) {tickets.delete(key);}
57
- }
58
- }, 60_000).unref();
55
+ const now = Date.now();
56
+ for (const [key, val] of tickets) {
57
+ if (now - val.createdAt > 30_000) {
58
+ tickets.delete(key);
59
+ }
60
+ }
61
+ }, 30_000).unref();
package/src/lib/theme.css CHANGED
@@ -276,12 +276,37 @@
276
276
  --button-hover-text-color: var(--ds-background-100);
277
277
  }
278
278
 
279
+ .btn-connect {
280
+ --button-color: #0d9488;
281
+ --button-text-color: #fff;
282
+ --button-hover-color: #14b8a6;
283
+ --button-hover-text-color: #fff;
284
+ --button-border: 1px solid #0d9488;
285
+ --button-hover-border: 1px solid #14b8a6;
286
+ }
287
+
288
+ .btn-resume {
289
+ --button-color: transparent;
290
+ --button-text-color: var(--ds-gray-900);
291
+ --button-border: 1px solid var(--ds-gray-400);
292
+ --button-hover-color: var(--ds-gray-100);
293
+ --button-hover-text-color: var(--ds-gray-1000);
294
+ --button-hover-border: 1px solid var(--ds-gray-600);
295
+ }
296
+
279
297
  .btn-sm {
280
298
  --button-height: 32px;
281
299
  --button-padding: 0 10px;
282
300
  --button-font-size: var(--text-base);
283
301
  }
284
302
 
303
+ .btn-xs {
304
+ --button-height: 28px;
305
+ --button-padding: 0 8px;
306
+ --button-font-size: var(--text-xs);
307
+ --button-border-radius: var(--radius-sm);
308
+ }
309
+
285
310
  .btn-lg {
286
311
  --button-height: 48px;
287
312
  --button-padding: 0 20px;
@@ -526,4 +551,9 @@
526
551
  .shimmer-card {
527
552
  --shimmer-height: 90px;
528
553
  }
554
+
555
+ .btn-xs {
556
+ min-height: 36px;
557
+ padding: 6px 12px;
558
+ }
529
559
  }
@@ -1,6 +1 @@
1
- export interface ShooterConfig {
2
- apiKey: string;
3
- deviceToken: string;
4
- lastUpdated?: number;
5
- serverUrl?: string;
6
- }
1
+ export type { ShooterConfig } from '$generated/types';
@@ -0,0 +1,94 @@
1
+ <script lang="ts">
2
+ import { goto } from '$app/navigation';
3
+ import { page } from '$app/stores';
4
+ </script>
5
+
6
+ <svelte:head>
7
+ <title>Error - Shooter</title>
8
+ </svelte:head>
9
+
10
+ <main class="error-page">
11
+ <div class="error-content">
12
+ <div class="error-code">{$page.status}</div>
13
+ <h1 class="error-title">
14
+ {#if $page.status === 404}
15
+ Page not found
16
+ {:else}
17
+ Something went wrong
18
+ {/if}
19
+ </h1>
20
+ <p class="error-message">
21
+ {#if $page.error?.message}
22
+ {$page.error.message}
23
+ {:else if $page.status === 404}
24
+ The page you're looking for doesn't exist or has been moved.
25
+ {:else}
26
+ An unexpected error occurred. Try refreshing or going back.
27
+ {/if}
28
+ </p>
29
+ <div class="error-actions">
30
+ <button class="error-btn" onclick={() => goto('/')}>Go Home</button>
31
+ <button class="error-btn error-btn-secondary" onclick={() => { history.back(); }}>Go Back</button>
32
+ </div>
33
+ </div>
34
+ </main>
35
+
36
+ <style>
37
+ .error-page {
38
+ display: flex;
39
+ align-items: center;
40
+ justify-content: center;
41
+ min-height: 60vh;
42
+ padding: var(--space-6);
43
+ }
44
+
45
+ .error-content {
46
+ text-align: center;
47
+ max-width: 400px;
48
+ }
49
+
50
+ .error-code {
51
+ font-size: 4rem;
52
+ font-weight: 700;
53
+ color: var(--color-text-tertiary, #555);
54
+ line-height: 1;
55
+ margin-bottom: var(--space-2);
56
+ }
57
+
58
+ .error-title {
59
+ font-size: 1.25rem;
60
+ font-weight: 600;
61
+ margin-bottom: var(--space-3);
62
+ }
63
+
64
+ .error-message {
65
+ color: var(--color-text-secondary, #888);
66
+ font-size: 0.875rem;
67
+ line-height: 1.5;
68
+ margin-bottom: var(--space-5);
69
+ }
70
+
71
+ .error-actions {
72
+ display: flex;
73
+ gap: var(--space-3);
74
+ justify-content: center;
75
+ }
76
+
77
+ .error-btn {
78
+ padding: 10px 20px;
79
+ border-radius: 8px;
80
+ font-size: 0.875rem;
81
+ font-weight: 500;
82
+ cursor: pointer;
83
+ border: none;
84
+ background: var(--color-accent, #0d9488);
85
+ color: white;
86
+ min-height: 44px;
87
+ }
88
+
89
+ .error-btn-secondary {
90
+ background: transparent;
91
+ border: 1px solid var(--border, #333);
92
+ color: var(--color-text-secondary, #888);
93
+ }
94
+ </style>
@@ -3,7 +3,6 @@
3
3
  import '$lib/theme.css';
4
4
  import { goto } from '$app/navigation';
5
5
  import { page } from '$app/stores';
6
- import { Button } from '@juspay/svelte-ui-components';
7
6
  import { StatusBadge } from '$lib/modules/client/common';
8
7
  import { onMount, type Snippet } from 'svelte';
9
8
 
@@ -50,18 +49,27 @@
50
49
 
51
50
  <div class="nav-right">
52
51
  <StatusBadge status={systemStatus} />
53
- <Button
54
- classes="btn-gear {$page.url.pathname === '/config' ? 'btn-gear-active' : ''}"
52
+ <button
53
+ class="btn-gear {$page.url.pathname === '/config' ? 'btn-gear-active' : ''}"
55
54
  onclick={() => goto('/config')}
56
55
  aria-label="Settings"
57
56
  >
58
- {#snippet children()}
59
- <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
60
- <circle cx="12" cy="12" r="3"></circle>
61
- <path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"></path>
62
- </svg>
63
- {/snippet}
64
- </Button>
57
+ <svg
58
+ width="18"
59
+ height="18"
60
+ viewBox="0 0 24 24"
61
+ fill="none"
62
+ stroke="currentColor"
63
+ stroke-width="2"
64
+ stroke-linecap="round"
65
+ stroke-linejoin="round"
66
+ >
67
+ <circle cx="12" cy="12" r="3"></circle>
68
+ <path
69
+ d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"
70
+ ></path>
71
+ </svg>
72
+ </button>
65
73
  </div>
66
74
  </div>
67
75
  </header>
@@ -77,10 +85,20 @@
77
85
  <a
78
86
  href="/"
79
87
  class="tab-item"
80
- class:active={$page.url.pathname === '/' || $page.url.pathname.startsWith('/project') || $page.url.pathname.startsWith('/session')}
88
+ class:active={$page.url.pathname === '/' ||
89
+ $page.url.pathname.startsWith('/project') ||
90
+ $page.url.pathname.startsWith('/session')}
81
91
  >
82
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
83
- <path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"></path>
92
+ <svg
93
+ viewBox="0 0 24 24"
94
+ fill="none"
95
+ stroke="currentColor"
96
+ stroke-width="1.8"
97
+ stroke-linecap="round"
98
+ stroke-linejoin="round"
99
+ >
100
+ <path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"
101
+ ></path>
84
102
  </svg>
85
103
  <span>Projects</span>
86
104
  </a>
@@ -89,7 +107,14 @@
89
107
  class="tab-item"
90
108
  class:active={$page.url.pathname.startsWith('/terminals')}
91
109
  >
92
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
110
+ <svg
111
+ viewBox="0 0 24 24"
112
+ fill="none"
113
+ stroke="currentColor"
114
+ stroke-width="1.8"
115
+ stroke-linecap="round"
116
+ stroke-linejoin="round"
117
+ >
93
118
  <polyline points="4 17 10 11 4 5"></polyline>
94
119
  <line x1="12" y1="19" x2="20" y2="19"></line>
95
120
  </svg>
@@ -106,27 +131,37 @@
106
131
  gap: var(--space-2);
107
132
  }
108
133
 
109
- :global(.btn-gear) {
110
- --button-color: transparent;
111
- --button-text-color: var(--text-muted);
112
- --button-hover-color: var(--component-bg);
113
- --button-hover-text-color: var(--text-primary);
114
- --button-border: none;
115
- --button-hover-border: none;
116
- --button-padding: 0;
117
- --button-height: 36px;
118
- --button-width: 36px;
119
- --button-border-radius: var(--radius-md);
134
+ .btn-gear {
135
+ background: transparent;
136
+ color: var(--text-muted);
137
+ border: none;
138
+ padding: 0;
139
+ height: 36px;
140
+ width: 36px;
141
+ border-radius: var(--radius-md);
142
+ cursor: pointer;
143
+ display: flex;
144
+ align-items: center;
145
+ justify-content: center;
146
+ }
147
+ .btn-gear:hover {
148
+ background: var(--component-bg);
149
+ color: var(--text-primary);
150
+ }
151
+ .btn-gear:focus-visible {
152
+ outline: 2px solid var(--ds-green-700);
153
+ outline-offset: 2px;
120
154
  }
121
- :global(.btn-gear-active) {
122
- --button-color: var(--component-bg);
123
- --button-text-color: var(--text-primary);
155
+ .btn-gear-active {
156
+ background: var(--component-bg);
157
+ color: var(--text-primary);
124
158
  }
125
159
 
126
160
  .content-area {
127
161
  flex: 1;
128
162
  overflow-y: auto;
129
163
  -webkit-overflow-scrolling: touch;
164
+ padding-bottom: env(safe-area-inset-bottom, 0px);
130
165
  }
131
166
 
132
167
  .bottom-tabs {
@@ -178,9 +213,9 @@
178
213
 
179
214
  /* Mobile: page headers stack vertically, buttons wrap */
180
215
  @media (max-width: 480px) {
181
- :global(.btn-gear) {
182
- --button-height: 44px;
183
- --button-width: 44px;
216
+ .btn-gear {
217
+ height: 44px;
218
+ width: 44px;
184
219
  }
185
220
  .bottom-tabs {
186
221
  height: 60px;
@@ -1,9 +1,9 @@
1
1
  <script lang="ts">
2
- import type { ShooterConfig } from '$lib/types/config';
2
+ import type { ProjectGroup, ShooterConfig } from '$generated/types';
3
3
 
4
4
  import { goto } from '$app/navigation';
5
- import { Button, Pill, Shimmer } from '@juspay/svelte-ui-components';
6
5
  import {
6
+ clearCache,
7
7
  EmptyState,
8
8
  formatRelativeTime,
9
9
  getCached,
@@ -11,17 +11,10 @@
11
11
  isShooterConfig,
12
12
  setCache,
13
13
  } from '$lib/modules/client/common';
14
+ import { Banner, Button, Pill, Shimmer } from '@juspay/svelte-ui-components';
14
15
  import { onDestroy, onMount } from 'svelte';
15
16
 
16
- interface ProjectGroup {
17
- fullPath: string;
18
- id: string;
19
- lastModified: string;
20
- name: string;
21
- sessionCount: number;
22
- }
23
-
24
- const POLL_INTERVAL_MS = 30_000; // 30s - avoid heavy reflows
17
+ const POLL_INTERVAL_MS = 10_000;
25
18
  const PAGE_SIZE = 20;
26
19
 
27
20
  let projects = $state<ProjectGroup[]>([]);
@@ -29,6 +22,7 @@
29
22
  let fetching = false; // non-reactive guard to prevent overlapping fetches
30
23
  let config = $state<null | ShooterConfig>(null);
31
24
  let pollTimer: null | ReturnType<typeof setInterval> = null;
25
+ let fetchError = $state<null | string>(null);
32
26
  let hasMore = $state(false);
33
27
  let currentOffset = $state(0);
34
28
 
@@ -46,7 +40,7 @@
46
40
  void fetchSessions();
47
41
 
48
42
  pollTimer = setInterval(() => {
49
- if (config?.apiKey) {
43
+ if (config?.apiKey && currentOffset <= PAGE_SIZE) {
50
44
  void fetchSessions();
51
45
  }
52
46
  }, POLL_INTERVAL_MS);
@@ -76,7 +70,7 @@
76
70
  }
77
71
  }
78
72
 
79
- async function fetchSessions(append = false): Promise<void> {
73
+ async function fetchSessions(append = false, bustCache = false): Promise<void> {
80
74
  if (!config?.apiKey || fetching) {
81
75
  return;
82
76
  }
@@ -90,16 +84,19 @@
90
84
  const offset = append ? currentOffset : 0;
91
85
 
92
86
  try {
93
- const response = await fetch(`/api/sessions?limit=${PAGE_SIZE}&offset=${offset}`, {
87
+ const base = bustCache ? '/api/sessions?refresh=true&' : '/api/sessions?';
88
+ const response = await fetch(`${base}limit=${PAGE_SIZE}&offset=${offset}`, {
94
89
  headers: {
95
90
  Authorization: `Bearer ${config.apiKey}`,
96
91
  },
97
92
  });
98
93
 
99
94
  if (!response.ok) {
95
+ fetchError = `Failed to load projects (HTTP ${response.status})`;
100
96
  return;
101
97
  }
102
98
 
99
+ fetchError = null;
103
100
  const result: { projects: ProjectGroup[]; total?: number } = await response.json();
104
101
  if (append) {
105
102
  projects = [...projects, ...result.projects];
@@ -110,6 +107,7 @@
110
107
  hasMore = result.total !== undefined ? projects.length < result.total : false;
111
108
  setCache('shooter_projects', projects);
112
109
  } catch (error) {
110
+ fetchError = 'Failed to load projects';
113
111
  console.error('Failed to fetch sessions:', error);
114
112
  } finally {
115
113
  loading = false;
@@ -123,8 +121,8 @@
123
121
 
124
122
  async function forceRefresh(): Promise<void> {
125
123
  loading = true;
126
- sessionStorage.removeItem('shooter_projects');
127
- await fetchSessions();
124
+ clearCache('shooter_projects');
125
+ await fetchSessions(false, true);
128
126
  }
129
127
 
130
128
  function navigateToConfig(): void {
@@ -150,15 +148,17 @@
150
148
  </div>
151
149
  <div class="page-actions">
152
150
  <Button classes="btn-secondary" onclick={forceRefresh} disabled={loading}>
153
- {#snippet children()}
154
- <Icon name="refresh" size={14} />
155
- Refresh
156
- {/snippet}
151
+ <Icon name="refresh" size={14} />
152
+ Refresh
157
153
  </Button>
158
154
  </div>
159
155
  </div>
160
156
  </div>
161
157
 
158
+ {#if fetchError}
159
+ <Banner text={fetchError} classes="banner-error" />
160
+ {/if}
161
+
162
162
  {#if loading && projects.length === 0}
163
163
  <div class="loading-container">
164
164
  {#each Array(5) as _, i (i)}
@@ -188,10 +188,13 @@
188
188
  <h3 class="session-card-title">{project.name}</h3>
189
189
  <div class="session-card-subtitle">{project.fullPath}</div>
190
190
  </div>
191
- <Pill text={formatRelativeTime(project.lastModified)} classes="pill-session-time" />
191
+ <Pill text="Last updated {formatRelativeTime(project.lastModified)}" classes="pill-session-time" />
192
192
  </div>
193
193
  <div class="session-stats">
194
- <span><strong>{project.sessionCount}</strong> {project.sessionCount === 1 ? 'session' : 'sessions'}</span>
194
+ <span
195
+ ><strong>{project.sessionCount}</strong>
196
+ {project.sessionCount === 1 ? 'session' : 'sessions'}</span
197
+ >
195
198
  </div>
196
199
  </a>
197
200
  {/each}
@@ -7,7 +7,9 @@ import type { RequestHandler } from './$types';
7
7
 
8
8
  export const GET: RequestHandler = ({ request }) => {
9
9
  const authError = validateAuth(request);
10
- if (authError) {return authError;}
10
+ if (authError) {
11
+ return authError;
12
+ }
11
13
 
12
14
  const apnsClient = new LibraryAPNsService();
13
15
  const deviceToken = env.DEVICE_TOKEN?.trim();