@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,20 +1,22 @@
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
 
4
5
  import type { RequestHandler } from './$types';
5
6
 
6
- type WebhookBody = Record<string, unknown>;
7
-
7
+ // TODO: Add HMAC signature validation for webhook payloads.
8
+ // This is currently a stub endpoint — no signature verification is performed.
8
9
  export const POST: RequestHandler = async ({ request }) => {
9
10
  try {
10
11
  const authError = validateAuth(request);
11
- if (authError) return authError;
12
+ if (authError) {
13
+ return authError;
14
+ }
12
15
 
13
- const body = (await request.json()) as WebhookBody;
16
+ const body = (await request.json()) as Record<string, unknown>;
14
17
 
15
18
  console.log('Webhook received:', {
16
19
  body,
17
- headers: Object.fromEntries(request.headers.entries()),
18
20
  timestamp: new Date().toISOString(),
19
21
  });
20
22
 
@@ -22,19 +24,15 @@ export const POST: RequestHandler = async ({ request }) => {
22
24
  // For now, just log and acknowledge
23
25
 
24
26
  return json({
25
- data: body,
26
27
  message: 'Webhook received successfully',
27
28
  success: true,
28
29
  timestamp: new Date().toISOString(),
29
30
  });
30
31
  } catch (error) {
31
- const err = error as Error;
32
- console.error('Webhook error:', err);
32
+ console.error('[webhook] Failed to process webhook:', toErrorMessage(error));
33
33
  return json(
34
34
  {
35
- details: err.message,
36
35
  error: 'Failed to process webhook',
37
- timestamp: new Date().toISOString(),
38
36
  },
39
37
  { status: 500 }
40
38
  );
@@ -14,10 +14,12 @@ import type { RequestHandler } from './$types';
14
14
  // ── Endpoint ────────────────────────────────────────────────────────
15
15
 
16
16
  export const GET: RequestHandler = ({ request }) => {
17
- const authError = validateAuth(request);
18
- if (authError) {return authError;}
17
+ const authError = validateAuth(request);
18
+ if (authError) {
19
+ return authError;
20
+ }
19
21
 
20
- return json({
21
- connectedClients: getConnectedClientCount(),
22
- });
22
+ return json({
23
+ connectedClients: getConnectedClientCount(),
24
+ });
23
25
  };
@@ -7,7 +7,6 @@
7
7
  //
8
8
  // Rate limited to 10 requests per minute per API key.
9
9
 
10
- import { env } from '$env/dynamic/private';
11
10
  import { validateAuth } from '$lib/modules/server/auth';
12
11
  import { generateTicket } from '$lib/modules/server/ws/ticket-store';
13
12
  import { json } from '@sveltejs/kit';
@@ -27,60 +26,62 @@ const rateLimitMap = new Map<string, number[]>();
27
26
  * Returns true if the request is within the rate limit, false if exceeded.
28
27
  */
29
28
  function checkRateLimit(apiKey: string): boolean {
30
- const now = Date.now();
31
- const cutoff = now - RATE_LIMIT_WINDOW_MS;
29
+ const now = Date.now();
30
+ const cutoff = now - RATE_LIMIT_WINDOW_MS;
32
31
 
33
- let timestamps = rateLimitMap.get(apiKey);
34
- if (!timestamps) {
35
- timestamps = [];
36
- rateLimitMap.set(apiKey, timestamps);
37
- }
32
+ let timestamps = rateLimitMap.get(apiKey);
33
+ if (!timestamps) {
34
+ timestamps = [];
35
+ rateLimitMap.set(apiKey, timestamps);
36
+ }
38
37
 
39
- // Prune timestamps outside the window
40
- const recent = timestamps.filter((t) => t > cutoff);
41
- rateLimitMap.set(apiKey, recent);
38
+ // Prune timestamps outside the window
39
+ const recent = timestamps.filter((t) => t > cutoff);
40
+ rateLimitMap.set(apiKey, recent);
42
41
 
43
- if (recent.length >= RATE_LIMIT_MAX) {
44
- return false;
45
- }
42
+ if (recent.length >= RATE_LIMIT_MAX) {
43
+ return false;
44
+ }
46
45
 
47
- recent.push(now);
48
- return true;
46
+ recent.push(now);
47
+ return true;
49
48
  }
50
49
 
51
50
  // Cleanup stale rate limit entries every 5 minutes
52
51
  setInterval(() => {
53
- const cutoff = Date.now() - RATE_LIMIT_WINDOW_MS;
54
- for (const [key, timestamps] of rateLimitMap) {
55
- const recent = timestamps.filter((t) => t > cutoff);
56
- if (recent.length === 0) {
57
- rateLimitMap.delete(key);
58
- } else {
59
- rateLimitMap.set(key, recent);
60
- }
61
- }
52
+ const cutoff = Date.now() - RATE_LIMIT_WINDOW_MS;
53
+ for (const [key, timestamps] of rateLimitMap) {
54
+ const recent = timestamps.filter((t) => t > cutoff);
55
+ if (recent.length === 0) {
56
+ rateLimitMap.delete(key);
57
+ } else {
58
+ rateLimitMap.set(key, recent);
59
+ }
60
+ }
62
61
  }, 300_000).unref();
63
62
 
64
63
  // ── Endpoint ────────────────────────────────────────────────────────
65
64
 
66
- export const POST: RequestHandler = async ({ request }) => {
67
- const authError = validateAuth(request);
68
- if (authError) {return authError;}
65
+ export const POST: RequestHandler = ({ request }) => {
66
+ const authError = validateAuth(request);
67
+ if (authError) {
68
+ return authError;
69
+ }
69
70
 
70
- // Extract the API key for rate limiting
71
- const apiKey = request.headers.get('authorization')!.substring(7);
71
+ // Extract the API key for rate limiting
72
+ const apiKey = request.headers.get('authorization')!.substring(7);
72
73
 
73
- if (!checkRateLimit(apiKey)) {
74
- return json(
75
- { error: 'Rate limit exceeded. Maximum 10 ticket requests per minute.' },
76
- { status: 429 }
77
- );
78
- }
74
+ if (!checkRateLimit(apiKey)) {
75
+ return json(
76
+ { error: 'Rate limit exceeded. Maximum 10 ticket requests per minute.' },
77
+ { status: 429 }
78
+ );
79
+ }
79
80
 
80
- const ticket = generateTicket();
81
+ const ticket = generateTicket();
81
82
 
82
- return json({
83
- expiresIn: 30,
84
- ticket,
85
- });
83
+ return json({
84
+ expiresIn: 30,
85
+ ticket,
86
+ });
86
87
  };
@@ -1,10 +1,17 @@
1
1
  <script lang="ts">
2
+ import type { NativeBridgeConfig } from '$generated/types';
2
3
  import type { ShooterConfig } from '$lib/types/config';
3
4
 
4
5
  import { browser } from '$app/environment';
5
- import { goto } from '$app/navigation';
6
+ import {
7
+ Card,
8
+ hasScanner,
9
+ Icon,
10
+ isShooterConfig,
11
+ scanQR,
12
+ toErrorMessage,
13
+ } from '$lib/modules/client/common';
6
14
  import { Banner, Button, Input, Stepper } from '@juspay/svelte-ui-components';
7
- import { Card, hasScanner, Icon, isShooterConfig, scanQR } from '$lib/modules/client/common';
8
15
  import { onMount } from 'svelte';
9
16
 
10
17
  let serverUrl = $state('');
@@ -19,7 +26,8 @@
19
26
  let qrLoading = $state(false);
20
27
  let qrError = $state('');
21
28
  let canScan = $state(false);
22
- let bridgeCheckDone = $state(false);
29
+ let _bridgeCheckDone = $state(false);
30
+ let bridgeHydrated = false;
23
31
  let scanLoading = $state(false);
24
32
 
25
33
  async function fetchQrCode(): Promise<void> {
@@ -30,6 +38,8 @@
30
38
 
31
39
  qrLoading = true;
32
40
  qrError = '';
41
+ qrDataUrl = '';
42
+ qrServerUrl = '';
33
43
 
34
44
  try {
35
45
  const response = await fetch('/api/qr-config', {
@@ -46,8 +56,7 @@
46
56
  qrDataUrl = data.dataUrl;
47
57
  qrServerUrl = data.serverUrl;
48
58
  } catch (error) {
49
- const err = error as Error;
50
- qrError = `Network error: ${err.message}`;
59
+ qrError = `Network error: ${toErrorMessage(error)}`;
51
60
  } finally {
52
61
  qrLoading = false;
53
62
  }
@@ -71,16 +80,20 @@
71
80
  apiKey = config.apiKey;
72
81
  }
73
82
  await saveConfiguration();
74
- result = 'Configuration updated from QR code';
75
- statusType = 'success';
83
+ // Only show QR success if saveConfiguration() didn't set an error/warning
84
+ const savedStatus: string = statusType;
85
+ if (savedStatus !== 'error' && savedStatus !== 'warning') {
86
+ result = 'Configuration updated from QR code';
87
+ statusType = 'success';
88
+ }
76
89
  } catch {
77
90
  result = 'Invalid QR code data';
78
91
  statusType = 'error';
79
92
  }
80
93
  } catch (error) {
81
- const err = error as Error;
82
- if (err.message !== 'cancelled') {
83
- result = `Scanner error: ${err.message}`;
94
+ const msg = toErrorMessage(error);
95
+ if (msg !== 'cancelled') {
96
+ result = `Scanner error: ${msg}`;
84
97
  statusType = 'error';
85
98
  }
86
99
  } finally {
@@ -88,19 +101,16 @@
88
101
  }
89
102
  }
90
103
 
91
- function getNativeBridge(): {
92
- getConfig(): string;
93
- saveConfig(json: string): void;
94
- getFcmToken(): string;
95
- getPlatform(): string;
96
- } | null {
97
- if (typeof window !== 'undefined' && 'ShooterBridge' in window) {
98
- return (window as Record<string, unknown>).ShooterBridge as {
99
- getConfig(): string;
100
- saveConfig(json: string): void;
101
- getFcmToken(): string;
102
- getPlatform(): string;
103
- };
104
+ function getNativeBridge(): null | typeof window.ShooterBridge {
105
+ if (typeof window === 'undefined') {
106
+ return null;
107
+ }
108
+ // iOS injects window.ShooterBridge, Android injects window.ShooterNativeBridge
109
+ if (window.ShooterBridge) {
110
+ return window.ShooterBridge;
111
+ }
112
+ if (window.ShooterNativeBridge) {
113
+ return window.ShooterNativeBridge;
104
114
  }
105
115
  return null;
106
116
  }
@@ -109,10 +119,45 @@
109
119
  return window.location.origin;
110
120
  }
111
121
 
122
+ /** Attempt to read config from the native bridge and hydrate state.
123
+ * Only performs the full hydration (overwriting form fields) once.
124
+ * Subsequent calls only refresh the `canScan` flag so re-polling
125
+ * for a late-injected bridge does not clobber user edits. */
126
+ function hydrateBridge(): void {
127
+ const bridge = getNativeBridge();
128
+ if (bridge && !bridgeHydrated) {
129
+ isNativeApp = true;
130
+ try {
131
+ const nativeConfig = JSON.parse(
132
+ bridge.getConfig?.() ?? '{}'
133
+ ) as Partial<NativeBridgeConfig>;
134
+ // If native has config, use it (native is source of truth for credentials).
135
+ // Native serverUrl always overrides — it is set before the browser
136
+ // fallback (window.location.origin) so the native value wins.
137
+ if (nativeConfig.serverUrl) {
138
+ serverUrl = nativeConfig.serverUrl;
139
+ }
140
+ if (nativeConfig.apiKey) {
141
+ apiKey = nativeConfig.apiKey;
142
+ }
143
+ // Auto-populate device token with FCM token
144
+ const fcmToken = bridge.getFcmToken?.();
145
+ if (fcmToken) {
146
+ deviceToken = fcmToken;
147
+ }
148
+ // Mark hydrated only after native reads succeeded
149
+ bridgeHydrated = true;
150
+ } catch {
151
+ // Bridge communication failed — leave bridgeHydrated false so
152
+ // a subsequent poll can retry once the bridge is ready.
153
+ }
154
+ }
155
+ canScan = hasScanner();
156
+ }
157
+
112
158
  onMount(() => {
113
159
  if (browser) {
114
160
  try {
115
- serverUrl = getDefaultServerUrl();
116
161
  const saved = localStorage.getItem('shooter_config');
117
162
  if (saved) {
118
163
  const parsed: unknown = JSON.parse(saved);
@@ -134,42 +179,28 @@
134
179
  // No saved configuration — expected on first visit
135
180
  }
136
181
 
137
- const bridge = getNativeBridge();
138
- if (bridge) {
139
- isNativeApp = true;
140
- try {
141
- const nativeConfig = JSON.parse(bridge.getConfig());
142
- // If native has config, use it (native is source of truth for credentials)
143
- if (nativeConfig.serverUrl && !serverUrl) {
144
- serverUrl = nativeConfig.serverUrl;
145
- }
146
- if (nativeConfig.apiKey) {
147
- apiKey = nativeConfig.apiKey;
148
- }
149
- // Auto-populate device token with FCM token
150
- const fcmToken = bridge.getFcmToken();
151
- if (fcmToken) {
152
- deviceToken = fcmToken;
153
- }
154
- } catch {
155
- // Bridge communication failed
156
- }
182
+ // Hydrate from native bridge BEFORE applying the browser default.
183
+ // This lets native-provided serverUrl take precedence over
184
+ // window.location.origin.
185
+ hydrateBridge();
186
+ if (!serverUrl) {
187
+ serverUrl = getDefaultServerUrl();
157
188
  }
158
-
159
- canScan = hasScanner();
160
- bridgeCheckDone = true;
189
+ _bridgeCheckDone = true;
161
190
 
162
191
  // The native bridge may be injected after SvelteKit hydration.
163
192
  // Re-check periodically for a short window to catch late injection.
164
193
  if (!canScan) {
165
194
  const recheckInterval = setInterval(() => {
166
- canScan = hasScanner();
195
+ hydrateBridge();
167
196
  if (canScan) {
168
197
  clearInterval(recheckInterval);
169
198
  }
170
199
  }, 200);
171
200
  // Stop checking after 3 seconds
172
- setTimeout(() => clearInterval(recheckInterval), 3000);
201
+ setTimeout(() => {
202
+ clearInterval(recheckInterval);
203
+ }, 3000);
173
204
  }
174
205
  }
175
206
  });
@@ -193,10 +224,10 @@
193
224
 
194
225
  const bridge = getNativeBridge();
195
226
  if (bridge) {
196
- bridge.saveConfig(
227
+ bridge.saveConfig?.(
197
228
  JSON.stringify({
198
- serverUrl: trimmedUrl || getDefaultServerUrl(),
199
229
  apiKey: apiKey.trim(),
230
+ serverUrl: trimmedUrl || getDefaultServerUrl(),
200
231
  })
201
232
  );
202
233
  }
@@ -211,8 +242,7 @@
211
242
  statusType = 'warning';
212
243
  }
213
244
  } catch (error) {
214
- const err = error as Error;
215
- result = `Configuration failed: ${err.message}`;
245
+ result = `Configuration failed: ${toErrorMessage(error)}`;
216
246
  statusType = 'error';
217
247
  }
218
248
 
@@ -265,8 +295,7 @@
265
295
  statusType = 'error';
266
296
  }
267
297
  } catch (error) {
268
- const err = error as Error;
269
- result = `Network error: ${err.message}`;
298
+ result = `Network error: ${toErrorMessage(error)}`;
270
299
  statusType = 'error';
271
300
  }
272
301
 
@@ -284,7 +313,7 @@
284
313
 
285
314
  const bridge = getNativeBridge();
286
315
  if (bridge) {
287
- bridge.saveConfig(JSON.stringify({ serverUrl: '', apiKey: '' }));
316
+ bridge.saveConfig?.(JSON.stringify({ apiKey: '', serverUrl: '' }));
288
317
  }
289
318
  }
290
319
  }
@@ -297,6 +326,9 @@
297
326
 
298
327
  <main class="main">
299
328
  <div class="settings-container">
329
+ <div style="margin-bottom: var(--space-4);">
330
+ <a href="/" class="back-link">← Back to Projects</a>
331
+ </div>
300
332
  <div class="page-header">
301
333
  <h1 class="page-title">Settings</h1>
302
334
  <p class="page-description">Configure your API credentials and notification preferences</p>
@@ -304,7 +336,10 @@
304
336
 
305
337
  <div class="settings-grid">
306
338
  <section class="settings-section">
307
- <Card title="Server Configuration" description="Configure server connection and credentials">
339
+ <Card
340
+ title="Server Configuration"
341
+ description="Configure server connection and credentials"
342
+ >
308
343
  <Input
309
344
  name="serverUrl"
310
345
  label="Server URL"
@@ -322,6 +357,9 @@
322
357
  placeholder="Enter your API key"
323
358
  infoMessage="Required for sending notifications"
324
359
  />
360
+ <p class="input-help">
361
+ Find this in your <code>~/.shooter/.env</code> file. Run <code>shooter setup</code> to generate one.
362
+ </p>
325
363
 
326
364
  <Input
327
365
  name="deviceToken"
@@ -329,13 +367,21 @@
329
367
  bind:value={deviceToken}
330
368
  dataType="text"
331
369
  placeholder={isNativeApp ? 'Waiting for token...' : '64-character hex string'}
332
- infoMessage={isNativeApp && deviceToken ? 'Auto-detected from app' : 'Device token from app registration'}
370
+ infoMessage={isNativeApp && deviceToken
371
+ ? 'Auto-detected from app'
372
+ : 'Device token from app registration'}
333
373
  classes="input-mono"
334
374
  />
375
+ <p class="input-help">Optional — only needed for iOS/Android push notifications.</p>
335
376
  </Card>
336
377
 
337
378
  {#if result}
338
- {@const bannerIcon = statusType === 'success' ? 'check-circle' : statusType === 'error' ? 'x-circle' : 'alert-triangle'}
379
+ {@const bannerIcon =
380
+ statusType === 'success'
381
+ ? 'check-circle'
382
+ : statusType === 'error'
383
+ ? 'x-circle'
384
+ : 'alert-triangle'}
339
385
  <Banner text={result} classes="banner-{statusType || 'info'}">
340
386
  {#snippet icon()}
341
387
  <Icon name={bannerIcon} size={16} />
@@ -349,12 +395,10 @@
349
395
  onclick={testConfiguration}
350
396
  disabled={loading || !apiKey.trim()}
351
397
  >
352
- {#snippet children()}
353
- {#if !loading}
354
- <Icon name="play" size={14} />
355
- {/if}
356
- Test Connection
357
- {/snippet}
398
+ {#if !loading}
399
+ <Icon name="play" size={14} />
400
+ {/if}
401
+ Test Connection
358
402
  </Button>
359
403
  <Button
360
404
  classes="btn-primary"
@@ -379,14 +423,20 @@
379
423
  />
380
424
  </Card>
381
425
 
382
- <Card title="Mobile App Setup" description={canScan ? 'Scan a QR code to connect' : 'Scan to connect your mobile app'}>
426
+ <Card
427
+ title="Mobile App Setup"
428
+ description={canScan ? 'Scan a QR code to connect' : 'Scan to connect your mobile app'}
429
+ >
383
430
  <div class="qr-section">
384
431
  {#if canScan}
385
432
  <p class="qr-description">
386
- Scan the QR code shown on your server's settings page to auto-configure
387
- the connection.
433
+ Scan the QR code shown on your server's settings page to auto-configure the
434
+ connection.
388
435
  </p>
389
- <Button classes="btn-secondary btn-sm" onclick={handleScanQR} disabled={scanLoading}
436
+ <Button
437
+ classes="btn-secondary btn-sm"
438
+ onclick={handleScanQR}
439
+ disabled={scanLoading}
390
440
  text={scanLoading ? 'Scanning...' : 'Scan QR Code'}
391
441
  />
392
442
  {:else}
@@ -410,11 +460,14 @@
410
460
  <p class="qr-error">{qrError}</p>
411
461
  {:else}
412
462
  <p class="qr-description">
413
- Generate a QR code containing your server URL and API key. Your mobile app can scan
414
- it to auto-configure the connection.
463
+ Generate a QR code containing your server URL and API key. Your mobile app can
464
+ scan it to auto-configure the connection.
415
465
  </p>
416
466
  {/if}
417
- <Button classes="btn-secondary btn-sm" onclick={fetchQrCode} disabled={qrLoading || !apiKey.trim()}
467
+ <Button
468
+ classes="btn-secondary btn-sm"
469
+ onclick={fetchQrCode}
470
+ disabled={qrLoading || !apiKey.trim()}
418
471
  text={qrDataUrl ? 'Regenerate QR Code' : 'Generate QR Code'}
419
472
  />
420
473
  {/if}
@@ -423,7 +476,9 @@
423
476
 
424
477
  <Card title="Danger Zone">
425
478
  <p class="danger-description">Clear all saved configuration data from this device.</p>
426
- <Button classes="btn-danger btn-sm" onclick={clearConfiguration}
479
+ <Button
480
+ classes="btn-danger btn-sm"
481
+ onclick={clearConfiguration}
427
482
  text="Clear Configuration"
428
483
  />
429
484
  </Card>
@@ -436,7 +491,7 @@
436
491
  .settings-container {
437
492
  max-width: 900px;
438
493
  margin: 0 auto;
439
- padding-bottom: 80px;
494
+ padding-bottom: var(--space-6);
440
495
  }
441
496
 
442
497
  .page-header {
@@ -491,11 +546,12 @@
491
546
  --step-text-margin: 0 0 0 var(--space-3);
492
547
  --separator-display: none;
493
548
  --step-text-active-color: var(--text-primary);
494
- --step-text-completed-color: var(--ds-green-700);
549
+ --step-text-completed-color: var(--ds-green-900);
495
550
  --step-index-container-active-background-color: var(--component-bg-active);
496
551
  --step-index-container-completed-background-color: var(--ds-green-700);
497
552
  --step-index-container-background-color: var(--component-bg-active);
498
553
  --step-index-color: var(--text-secondary);
554
+ align-items: flex-start;
499
555
  gap: var(--space-3);
500
556
  }
501
557
 
@@ -505,6 +561,10 @@
505
561
  gap: var(--space-3);
506
562
  }
507
563
 
564
+ .qr-section :global(.button-container) {
565
+ align-self: center;
566
+ }
567
+
508
568
  .qr-container {
509
569
  display: flex;
510
570
  justify-content: center;
@@ -583,6 +643,20 @@
583
643
  border-left: 3px solid rgba(217, 48, 54, 0.5);
584
644
  }
585
645
 
646
+ .input-help {
647
+ font-size: var(--text-xs);
648
+ color: var(--color-text-tertiary, #888);
649
+ margin-top: var(--space-1);
650
+ margin-bottom: 0;
651
+ }
652
+ .input-help code {
653
+ background: var(--ds-gray-200, #1a1a1a);
654
+ padding: 1px 4px;
655
+ border-radius: var(--radius-sm);
656
+ font-family: var(--font-mono);
657
+ font-size: var(--text-xs);
658
+ }
659
+
586
660
  @media (max-width: 768px) {
587
661
  .settings-grid {
588
662
  grid-template-columns: 1fr;