@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
@@ -33,8 +33,8 @@ const RUNTIME = IS_OPENCODE ? 'opencode' : 'claude-code';
33
33
 
34
34
  // Environment configuration
35
35
  const USE_LOCAL = process.env.SHOOTER_USE_LOCAL === 'true';
36
- const LOCAL_PORT = process.env.SHOOTER_LOCAL_PORT || '3000';
37
- const REMOTE_BASE_URL = process.env.SHOOTER_API_URL || '';
36
+ const LOCAL_PORT = process.env.SHOOTER_LOCAL_PORT || '54007';
37
+ const REMOTE_BASE_URL = process.env.SHOOTER_API_URL?.trim() || '';
38
38
  const LOCAL_BASE_URL = `http://localhost:${LOCAL_PORT}`;
39
39
  const BASE_URL = USE_LOCAL ? LOCAL_BASE_URL : REMOTE_BASE_URL;
40
40
  const API_URL = `${BASE_URL}/api/notify`;
@@ -80,28 +80,35 @@ async function hasWebSocketClients() {
80
80
  const url = `${BASE_URL}/api/ws-status`;
81
81
  const protocol = url.startsWith('https') ? https : http;
82
82
  return new Promise((resolve) => {
83
- const req = protocol.request(url, {
84
- method: 'GET',
85
- headers: { Authorization: `Bearer ${AUTH_KEY}` },
86
- timeout: 3000,
87
- }, (res) => {
88
- let data = '';
89
- res.on('data', (chunk) => (data += chunk));
90
- res.on('end', () => {
91
- if (res.statusCode === 200) {
92
- try {
93
- const parsed = JSON.parse(data);
94
- resolve(parsed.connectedClients > 0);
95
- } catch (e) {
83
+ const req = protocol.request(
84
+ url,
85
+ {
86
+ method: 'GET',
87
+ headers: { Authorization: `Bearer ${AUTH_KEY}` },
88
+ timeout: 3000,
89
+ },
90
+ (res) => {
91
+ let data = '';
92
+ res.on('data', (chunk) => (data += chunk));
93
+ res.on('end', () => {
94
+ if (res.statusCode === 200) {
95
+ try {
96
+ const parsed = JSON.parse(data);
97
+ resolve(parsed.connectedClients > 0);
98
+ } catch (e) {
99
+ resolve(false);
100
+ }
101
+ } else {
96
102
  resolve(false);
97
103
  }
98
- } else {
99
- resolve(false);
100
- }
101
- });
102
- });
104
+ });
105
+ }
106
+ );
103
107
  req.on('error', () => resolve(false));
104
- req.on('timeout', () => { req.destroy(); resolve(false); });
108
+ req.on('timeout', () => {
109
+ req.destroy();
110
+ resolve(false);
111
+ });
105
112
  req.end();
106
113
  });
107
114
  } catch (e) {
@@ -625,26 +632,20 @@ async function handlePermission(event) {
625
632
  console.error(`=== REGISTERING REQUEST & POLLING VIA WEBSOCKET CHANNEL ===\n`);
626
633
  }
627
634
 
628
- // POST to /api/notify with waitForResponse so the server creates a pending
629
- // request entry. Without this, GET /api/response returns 404 every time.
635
+ // Register the pending request but skip the actual push notification
636
+ // the events channel will broadcast the permission to connected clients.
630
637
  result = await sendNotificationAndPoll(
631
638
  title,
632
639
  body,
633
640
  'permission',
634
641
  event.source,
635
642
  requestId,
636
- d
643
+ d,
644
+ { skipPush: true }
637
645
  );
638
646
  } else {
639
647
  // No WebSocket clients — send push notification and poll
640
- result = await sendNotificationAndPoll(
641
- title,
642
- body,
643
- 'permission',
644
- event.source,
645
- requestId,
646
- d
647
- );
648
+ result = await sendNotificationAndPoll(title, body, 'permission', event.source, requestId, d);
648
649
  }
649
650
 
650
651
  if (result && result.decision) {
@@ -1022,7 +1023,15 @@ function createCompletionMessage(state, projectName) {
1022
1023
  *
1023
1024
  * Returns { decision: 'allow' | 'deny' } or null on timeout.
1024
1025
  */
1025
- function sendNotificationAndPoll(title, body, category, source, requestId, eventData) {
1026
+ function sendNotificationAndPoll(
1027
+ title,
1028
+ body,
1029
+ category,
1030
+ source,
1031
+ requestId,
1032
+ eventData,
1033
+ { skipPush = false } = {}
1034
+ ) {
1026
1035
  return new Promise((resolve) => {
1027
1036
  const timestamp = new Date().toISOString();
1028
1037
 
@@ -1030,6 +1039,81 @@ function sendNotificationAndPoll(title, body, category, source, requestId, event
1030
1039
  const envPrefix = USE_LOCAL ? '[LOCAL]' : '';
1031
1040
  const finalTitle = `${runtimePrefix}${envPrefix ? ' ' + envPrefix : ''} ${title}`;
1032
1041
 
1042
+ // When skipPush is true (WebSocket clients connected), skip the actual
1043
+ // POST to /api/notify but still register the pending request and poll.
1044
+ if (skipPush) {
1045
+ debugLog(
1046
+ `Skipping push notification (WebSocket active), going straight to polling (requestId: ${requestId})`
1047
+ );
1048
+ // Register the pending request on the server so polling finds it.
1049
+ // We still need to POST with waitForResponse so the server creates
1050
+ // the pending-request entry, but we mark it as ws-only.
1051
+ const registerPayload = JSON.stringify({
1052
+ title: finalTitle,
1053
+ message: body,
1054
+ waitForResponse: true,
1055
+ skipPush: true,
1056
+ ...(DEVICE_TOKEN && { deviceToken: DEVICE_TOKEN }),
1057
+ data: {
1058
+ category,
1059
+ project: getProjectName(),
1060
+ timestamp,
1061
+ requestId,
1062
+ clientTimestamp: timestamp,
1063
+ source: 'shooter-completion-detector',
1064
+ environment: USE_LOCAL ? 'local' : 'remote',
1065
+ runtime: source,
1066
+ toolName: eventData.tool || '',
1067
+ toolInput: eventData.toolInput || {},
1068
+ sessionId: eventData.sessionId || '',
1069
+ },
1070
+ });
1071
+
1072
+ const registerOptions = {
1073
+ method: 'POST',
1074
+ headers: {
1075
+ 'Content-Type': 'application/json',
1076
+ Authorization: `Bearer ${AUTH_KEY}`,
1077
+ 'Content-Length': Buffer.byteLength(registerPayload),
1078
+ 'User-Agent': `Shooter-Notifier/3.0 ${source}`,
1079
+ },
1080
+ };
1081
+
1082
+ const protocol = API_URL.startsWith('https') ? https : http;
1083
+ const req = protocol.request(API_URL, registerOptions, (res) => {
1084
+ let responseData = '';
1085
+ res.on('data', (chunk) => (responseData += chunk));
1086
+ res.on('end', () => {
1087
+ debugLog(`Pending request registered (skipPush): status=${res.statusCode}`);
1088
+ if (res.statusCode !== 200) {
1089
+ debugLog(
1090
+ `Registration failed (skipPush): ${res.statusCode} - falling through to local dialog`
1091
+ );
1092
+ resolve(null);
1093
+ return;
1094
+ }
1095
+ // Start polling only after successful registration — the WebSocket
1096
+ // events channel will deliver the permission to connected clients.
1097
+ startPolling(requestId, resolve);
1098
+ });
1099
+ });
1100
+
1101
+ req.on('error', (error) => {
1102
+ debugLog(
1103
+ `Register request error (skipPush): ${error.message} - falling through to local dialog`
1104
+ );
1105
+ resolve(null);
1106
+ });
1107
+
1108
+ req.setTimeout(10000, () => {
1109
+ req.destroy(new Error('Request timeout'));
1110
+ });
1111
+
1112
+ req.write(registerPayload);
1113
+ req.end();
1114
+ return;
1115
+ }
1116
+
1033
1117
  debugLog(`Sending bidirectional notification: "${finalTitle}" (requestId: ${requestId})`);
1034
1118
 
1035
1119
  const payload = JSON.stringify({
@@ -9,7 +9,7 @@
9
9
  "hooks": [
10
10
  {
11
11
  "type": "command",
12
- "command": "SHOOTER_USE_LOCAL=true SHOOTER_LOCAL_PORT=3000 API_KEY=$API_KEY node .claude/hooks/notifier.cjs PreToolUse"
12
+ "command": "SHOOTER_USE_LOCAL=true SHOOTER_LOCAL_PORT=${SHOOTER_LOCAL_PORT:-54007} API_KEY=$API_KEY node .claude/hooks/notifier.cjs PreToolUse"
13
13
  }
14
14
  ]
15
15
  }
@@ -20,7 +20,7 @@
20
20
  "hooks": [
21
21
  {
22
22
  "type": "command",
23
- "command": "SHOOTER_USE_LOCAL=true SHOOTER_LOCAL_PORT=3000 API_KEY=$API_KEY node .claude/hooks/notifier.cjs PostToolUse"
23
+ "command": "SHOOTER_USE_LOCAL=true SHOOTER_LOCAL_PORT=${SHOOTER_LOCAL_PORT:-54007} API_KEY=$API_KEY node .claude/hooks/notifier.cjs PostToolUse"
24
24
  }
25
25
  ]
26
26
  }
@@ -31,7 +31,7 @@
31
31
  "hooks": [
32
32
  {
33
33
  "type": "command",
34
- "command": "SHOOTER_USE_LOCAL=true SHOOTER_LOCAL_PORT=3000 API_KEY=$API_KEY node .claude/hooks/notifier.cjs PostToolUseFailure"
34
+ "command": "SHOOTER_USE_LOCAL=true SHOOTER_LOCAL_PORT=${SHOOTER_LOCAL_PORT:-54007} API_KEY=$API_KEY node .claude/hooks/notifier.cjs PostToolUseFailure"
35
35
  }
36
36
  ]
37
37
  }
@@ -42,7 +42,7 @@
42
42
  "hooks": [
43
43
  {
44
44
  "type": "command",
45
- "command": "SHOOTER_USE_LOCAL=true SHOOTER_LOCAL_PORT=3000 API_KEY=$API_KEY node .claude/hooks/notifier.cjs Stop"
45
+ "command": "SHOOTER_USE_LOCAL=true SHOOTER_LOCAL_PORT=${SHOOTER_LOCAL_PORT:-54007} API_KEY=$API_KEY node .claude/hooks/notifier.cjs Stop"
46
46
  }
47
47
  ]
48
48
  }
@@ -53,7 +53,7 @@
53
53
  "hooks": [
54
54
  {
55
55
  "type": "command",
56
- "command": "SHOOTER_USE_LOCAL=true SHOOTER_LOCAL_PORT=3000 API_KEY=$API_KEY node .claude/hooks/notifier.cjs SessionStart"
56
+ "command": "SHOOTER_USE_LOCAL=true SHOOTER_LOCAL_PORT=${SHOOTER_LOCAL_PORT:-54007} API_KEY=$API_KEY node .claude/hooks/notifier.cjs SessionStart"
57
57
  }
58
58
  ]
59
59
  }
@@ -64,7 +64,7 @@
64
64
  "hooks": [
65
65
  {
66
66
  "type": "command",
67
- "command": "SHOOTER_USE_LOCAL=true SHOOTER_LOCAL_PORT=3000 API_KEY=$API_KEY node .claude/hooks/notifier.cjs SessionEnd"
67
+ "command": "SHOOTER_USE_LOCAL=true SHOOTER_LOCAL_PORT=${SHOOTER_LOCAL_PORT:-54007} API_KEY=$API_KEY node .claude/hooks/notifier.cjs SessionEnd"
68
68
  }
69
69
  ]
70
70
  }
@@ -75,7 +75,7 @@
75
75
  "hooks": [
76
76
  {
77
77
  "type": "command",
78
- "command": "SHOOTER_USE_LOCAL=true SHOOTER_LOCAL_PORT=3000 API_KEY=$API_KEY node .claude/hooks/notifier.cjs Notification"
78
+ "command": "SHOOTER_USE_LOCAL=true SHOOTER_LOCAL_PORT=${SHOOTER_LOCAL_PORT:-54007} API_KEY=$API_KEY node .claude/hooks/notifier.cjs Notification"
79
79
  }
80
80
  ]
81
81
  }
@@ -86,7 +86,7 @@
86
86
  "hooks": [
87
87
  {
88
88
  "type": "command",
89
- "command": "SHOOTER_USE_LOCAL=true SHOOTER_LOCAL_PORT=3000 API_KEY=$API_KEY node .claude/hooks/notifier.cjs PermissionRequest",
89
+ "command": "SHOOTER_USE_LOCAL=true SHOOTER_LOCAL_PORT=${SHOOTER_LOCAL_PORT:-54007} API_KEY=$API_KEY node .claude/hooks/notifier.cjs PermissionRequest",
90
90
  "timeout": 180
91
91
  }
92
92
  ]
@@ -98,7 +98,7 @@
98
98
  "hooks": [
99
99
  {
100
100
  "type": "command",
101
- "command": "SHOOTER_USE_LOCAL=true SHOOTER_LOCAL_PORT=3000 API_KEY=$API_KEY node .claude/hooks/notifier.cjs SubagentStart"
101
+ "command": "SHOOTER_USE_LOCAL=true SHOOTER_LOCAL_PORT=${SHOOTER_LOCAL_PORT:-54007} API_KEY=$API_KEY node .claude/hooks/notifier.cjs SubagentStart"
102
102
  }
103
103
  ]
104
104
  }
@@ -109,7 +109,7 @@
109
109
  "hooks": [
110
110
  {
111
111
  "type": "command",
112
- "command": "SHOOTER_USE_LOCAL=true SHOOTER_LOCAL_PORT=3000 API_KEY=$API_KEY node .claude/hooks/notifier.cjs SubagentStop"
112
+ "command": "SHOOTER_USE_LOCAL=true SHOOTER_LOCAL_PORT=${SHOOTER_LOCAL_PORT:-54007} API_KEY=$API_KEY node .claude/hooks/notifier.cjs SubagentStop"
113
113
  }
114
114
  ]
115
115
  }
@@ -120,7 +120,7 @@
120
120
  "hooks": [
121
121
  {
122
122
  "type": "command",
123
- "command": "SHOOTER_USE_LOCAL=true SHOOTER_LOCAL_PORT=3000 API_KEY=$API_KEY node .claude/hooks/notifier.cjs UserPromptSubmit"
123
+ "command": "SHOOTER_USE_LOCAL=true SHOOTER_LOCAL_PORT=${SHOOTER_LOCAL_PORT:-54007} API_KEY=$API_KEY node .claude/hooks/notifier.cjs UserPromptSubmit"
124
124
  }
125
125
  ]
126
126
  }
@@ -131,7 +131,7 @@
131
131
  "hooks": [
132
132
  {
133
133
  "type": "command",
134
- "command": "SHOOTER_USE_LOCAL=true SHOOTER_LOCAL_PORT=3000 API_KEY=$API_KEY node .claude/hooks/notifier.cjs TeammateIdle"
134
+ "command": "SHOOTER_USE_LOCAL=true SHOOTER_LOCAL_PORT=${SHOOTER_LOCAL_PORT:-54007} API_KEY=$API_KEY node .claude/hooks/notifier.cjs TeammateIdle"
135
135
  }
136
136
  ]
137
137
  }
@@ -142,7 +142,7 @@
142
142
  "hooks": [
143
143
  {
144
144
  "type": "command",
145
- "command": "SHOOTER_USE_LOCAL=true SHOOTER_LOCAL_PORT=3000 API_KEY=$API_KEY node .claude/hooks/notifier.cjs TaskCompleted"
145
+ "command": "SHOOTER_USE_LOCAL=true SHOOTER_LOCAL_PORT=${SHOOTER_LOCAL_PORT:-54007} API_KEY=$API_KEY node .claude/hooks/notifier.cjs TaskCompleted"
146
146
  }
147
147
  ]
148
148
  }
@@ -153,7 +153,7 @@
153
153
  "hooks": [
154
154
  {
155
155
  "type": "command",
156
- "command": "SHOOTER_USE_LOCAL=true SHOOTER_LOCAL_PORT=3000 API_KEY=$API_KEY node .claude/hooks/notifier.cjs PreCompact"
156
+ "command": "SHOOTER_USE_LOCAL=true SHOOTER_LOCAL_PORT=${SHOOTER_LOCAL_PORT:-54007} API_KEY=$API_KEY node .claude/hooks/notifier.cjs PreCompact"
157
157
  }
158
158
  ]
159
159
  }
package/README.md CHANGED
@@ -33,27 +33,37 @@ Shooter turns your phone into a remote control for AI coding sessions running on
33
33
 
34
34
  ## Quick Start
35
35
 
36
+ **One-command install** (recommended):
37
+
38
+ ```bash
39
+ curl -fsSL https://raw.githubusercontent.com/juspay/shooter/release/scripts/install.sh | sh
40
+ ```
41
+
42
+ This clones to `~/.shooter/repo`, auto-generates an API key, installs dependencies, builds, offers to install cloudflared for remote access, enables autostart on login, and starts the server.
43
+
44
+ **Or clone and set up manually:**
45
+
36
46
  ```bash
37
47
  git clone https://github.com/juspay/shooter.git
38
48
  cd shooter
39
49
  pnpm install
40
50
  pnpm setup # interactive wizard: generates .env, builds, runs health check
41
- pnpm start # start the server on http://localhost:3000
51
+ pnpm start # start the server on http://localhost:54007
42
52
  ```
43
53
 
44
- Open [http://localhost:3000](http://localhost:3000) in your browser. Visit `/config` to enter your API key for the web UI.
54
+ Open [http://localhost:54007](http://localhost:54007) in your browser. Visit `/config` to enter your API key for the web UI.
45
55
 
46
56
  ---
47
57
 
48
58
  ## All Setup Methods
49
59
 
50
- | Method | Command | Notes |
51
- |--------|---------|-------|
52
- | Interactive wizard | `pnpm setup` | Recommended. Walks through env config, builds, and verifies. |
53
- | CLI (npx) | `npx @juspay/shooter setup` | No clone needed -- runs the setup wizard directly from npm |
54
- | One-command install | `curl -fsSL https://raw.githubusercontent.com/juspay/shooter/release/scripts/install.sh \| sh` | Clones to `~/.shooter`, installs deps, runs wizard |
55
- | Docker | `docker compose up -d` | See [Docker](#docker) |
56
- | Manual | See [Manual Setup](#manual-setup) | For advanced users |
60
+ | Method | Command | Notes |
61
+ | ------------------- | ---------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
62
+ | One-command install | `curl -fsSL https://raw.githubusercontent.com/juspay/shooter/release/scripts/install.sh \| sh` | Recommended. Clones to `~/.shooter/repo`, auto-generates API key, builds, installs cloudflared, starts server |
63
+ | Interactive wizard | `pnpm setup` | Walks through env config, builds, and verifies. Pass `--auto` for non-interactive mode. |
64
+ | CLI (npx) | `npx @juspay/shooter setup` | No clone needed -- runs the setup wizard directly from npm |
65
+ | Docker | `docker compose up -d` | See [Docker](#docker) |
66
+ | Manual | See [Manual Setup](#manual-setup) | For advanced users |
57
67
 
58
68
  ### Manual Setup
59
69
 
@@ -82,7 +92,7 @@ source ~/.zshrc
82
92
  +----------------------------------------------------------+
83
93
  | Dev Machine |
84
94
  | |
85
- | SvelteKit Server (adapter-node, port 3000) |
95
+ | SvelteKit Server (adapter-node, port 54007) |
86
96
  | +-- REST API (/api/terminals, /api/notify, ...) |
87
97
  | +-- WebSocket Server (ws, noServer mode) |
88
98
  | +-- PTY Manager (node-pty + holder processes) |
@@ -111,11 +121,11 @@ source ~/.zshrc
111
121
 
112
122
  **Three WebSocket channels:**
113
123
 
114
- | Channel | Path | Purpose |
115
- |---------|------|---------|
116
- | Terminal I/O | `/ws/terminal/:id` | Raw PTY byte stream (xterm.js) |
117
- | Session stream | `/ws/session/:id` | Structured AI conversation updates |
118
- | Global events | `/ws/events` | Server broadcasts (new sessions, exits, permissions) |
124
+ | Channel | Path | Purpose |
125
+ | -------------- | ------------------ | ---------------------------------------------------- |
126
+ | Terminal I/O | `/ws/terminal/:id` | Raw PTY byte stream (xterm.js) |
127
+ | Session stream | `/ws/session/:id` | Structured AI conversation updates |
128
+ | Global events | `/ws/events` | Server broadcasts (new sessions, exits, permissions) |
119
129
 
120
130
  ---
121
131
 
@@ -123,21 +133,21 @@ source ~/.zshrc
123
133
 
124
134
  Copy `.env.example` to `.env` and fill in your values. The `pnpm setup` wizard handles this interactively.
125
135
 
126
- | Variable | Required | Default | Description |
127
- |----------|----------|---------|-------------|
128
- | `API_KEY` | **Yes** | -- | Bearer token for authenticating all API and hook requests |
129
- | `PORT` | No | `3000` | HTTP server port |
130
- | `DEVICE_PLATFORM` | No | `ios` | Push notification target: `ios` or `android` |
131
- | `APNS_KEY` | No | -- | APNs private key (`.p8` file contents, newlines escaped as `\n`) |
132
- | `APNS_KEY_ID` | No | -- | 10-character APNs key identifier from Apple Developer portal |
133
- | `APNS_TEAM_ID` | No | -- | 10-character Apple Team ID |
134
- | `APNS_BUNDLE_ID` | No | -- | iOS app bundle identifier (must match Xcode project) |
135
- | `APNS_PRODUCTION` | No | `false` | Set `true` for TestFlight / App Store builds |
136
- | `DEVICE_TOKEN` | No | -- | Target iOS device token (64-character hex) |
137
- | `FCM_PROJECT_ID` | No | -- | Firebase project ID |
138
- | `FCM_CLIENT_EMAIL` | No | -- | Firebase service account email |
139
- | `FCM_PRIVATE_KEY` | No | -- | Firebase service account private key (PEM format) |
140
- | `ANDROID_DEVICE_TOKEN` | No | -- | Target Android FCM device token |
136
+ | Variable | Required | Default | Description |
137
+ | ---------------------- | -------- | ------- | ---------------------------------------------------------------- |
138
+ | `API_KEY` | **Yes** | -- | Bearer token for authenticating all API and hook requests |
139
+ | `PORT` | No | `54007` | HTTP server port |
140
+ | `DEVICE_PLATFORM` | No | `ios` | Push notification target: `ios` or `android` |
141
+ | `APNS_KEY` | No | -- | APNs private key (`.p8` file contents, newlines escaped as `\n`) |
142
+ | `APNS_KEY_ID` | No | -- | 10-character APNs key identifier from Apple Developer portal |
143
+ | `APNS_TEAM_ID` | No | -- | 10-character Apple Team ID |
144
+ | `APNS_BUNDLE_ID` | No | -- | iOS app bundle identifier (must match Xcode project) |
145
+ | `APNS_PRODUCTION` | No | `false` | Set `true` for TestFlight / App Store builds |
146
+ | `DEVICE_TOKEN` | No | -- | Target iOS device token (64-character hex) |
147
+ | `FCM_PROJECT_ID` | No | -- | Firebase project ID |
148
+ | `FCM_CLIENT_EMAIL` | No | -- | Firebase service account email |
149
+ | `FCM_PRIVATE_KEY` | No | -- | Firebase service account private key (PEM format) |
150
+ | `ANDROID_DEVICE_TOKEN` | No | -- | Target Android FCM device token |
141
151
 
142
152
  ---
143
153
 
@@ -225,22 +235,22 @@ Shooter integrates with Claude Code through lifecycle hooks defined in `.claude/
225
235
 
226
236
  ### Captured Events
227
237
 
228
- | Hook | Description |
229
- |------|-------------|
230
- | `PreToolUse` | Before a tool executes (file edit, bash command, etc.) |
231
- | `PostToolUse` | After a tool completes successfully |
232
- | `PostToolUseFailure` | After a tool fails |
233
- | `PermissionRequest` | Claude Code asks for permission -- **blocks until you respond** |
234
- | `SessionStart` | A new coding session begins |
235
- | `SessionEnd` | A coding session ends |
236
- | `Stop` | Claude Code stops execution |
237
- | `Notification` | General notification from Claude Code |
238
- | `SubagentStart` | A subagent is spawned |
239
- | `SubagentStop` | A subagent completes |
240
- | `UserPromptSubmit` | User submits a prompt |
241
- | `TeammateIdle` | A teammate agent becomes idle |
242
- | `TaskCompleted` | A task finishes |
243
- | `PreCompact` | Before context compaction |
238
+ | Hook | Description |
239
+ | -------------------- | --------------------------------------------------------------- |
240
+ | `PreToolUse` | Before a tool executes (file edit, bash command, etc.) |
241
+ | `PostToolUse` | After a tool completes successfully |
242
+ | `PostToolUseFailure` | After a tool fails |
243
+ | `PermissionRequest` | Claude Code asks for permission -- **blocks until you respond** |
244
+ | `SessionStart` | A new coding session begins |
245
+ | `SessionEnd` | A coding session ends |
246
+ | `Stop` | Claude Code stops execution |
247
+ | `Notification` | General notification from Claude Code |
248
+ | `SubagentStart` | A subagent is spawned |
249
+ | `SubagentStop` | A subagent completes |
250
+ | `UserPromptSubmit` | User submits a prompt |
251
+ | `TeammateIdle` | A teammate agent becomes idle |
252
+ | `TaskCompleted` | A task finishes |
253
+ | `PreCompact` | Before context compaction |
244
254
 
245
255
  ### Permission Flow
246
256
 
@@ -254,13 +264,13 @@ The `PermissionRequest` hook has a 180-second timeout in `.claude/settings.json`
254
264
 
255
265
  ### Hook Environment Variables
256
266
 
257
- | Variable | Default | Description |
258
- |----------|---------|-------------|
259
- | `SHOOTER_USE_LOCAL` | -- | Set `true` to connect to local server instead of remote URL |
260
- | `SHOOTER_LOCAL_PORT` | `3000` | Local server port when using `SHOOTER_USE_LOCAL` |
261
- | `SHOOTER_API_URL` | -- | Remote server URL (when not using local) |
262
- | `SHOOTER_PERMISSION_TIMEOUT` | `120` | Seconds to wait for a permission response |
263
- | `API_KEY` | -- | Bearer token (must match the server's `API_KEY`) |
267
+ | Variable | Default | Description |
268
+ | ---------------------------- | ------- | ----------------------------------------------------------- |
269
+ | `SHOOTER_USE_LOCAL` | -- | Set `true` to connect to local server instead of remote URL |
270
+ | `SHOOTER_LOCAL_PORT` | `54007` | Local server port when using `SHOOTER_USE_LOCAL` |
271
+ | `SHOOTER_API_URL` | -- | Remote server URL (when not using local) |
272
+ | `SHOOTER_PERMISSION_TIMEOUT` | `120` | Seconds to wait for a permission response |
273
+ | `API_KEY` | -- | Bearer token (must match the server's `API_KEY`) |
264
274
 
265
275
  ---
266
276
 
@@ -282,7 +292,7 @@ docker build -t shooter .
282
292
  docker run -d \
283
293
  --name shooter \
284
294
  --env-file .env \
285
- -p 3000:3000 \
295
+ -p 54007:54007 \
286
296
  -v shooter-data:/root/.shooter \
287
297
  --restart unless-stopped \
288
298
  shooter
@@ -290,6 +300,8 @@ docker run -d \
290
300
 
291
301
  The multi-stage Dockerfile uses `node:20-slim` and includes build tools for `node-pty` and `better-sqlite3` native addons. SQLite data is persisted in the `shooter-data` volume. The `.env` file is injected at runtime and never baked into the image.
292
302
 
303
+ A separate `Dockerfile.test` is provided for verifying the fresh-user install experience in an isolated container.
304
+
293
305
  ### docker-compose.yml
294
306
 
295
307
  ```yaml
@@ -297,7 +309,7 @@ services:
297
309
  shooter:
298
310
  build: .
299
311
  ports:
300
- - "3000:3000"
312
+ - '54007:54007'
301
313
  env_file:
302
314
  - .env
303
315
  volumes:
@@ -314,25 +326,25 @@ volumes:
314
326
 
315
327
  All endpoints require the `Authorization: Bearer <API_KEY>` header.
316
328
 
317
- | Method | Path | Description |
318
- |--------|------|-------------|
319
- | `GET` | `/api/health` | Health check with server status |
320
- | `GET` | `/api/terminals` | List all active and recently exited terminals |
321
- | `POST` | `/api/terminals` | Create a new terminal session |
322
- | `GET` | `/api/terminals/:id` | Get details for a specific terminal |
323
- | `DELETE` | `/api/terminals/:id` | Kill and remove a terminal session |
324
- | `POST` | `/api/terminals/:id/resize` | Resize a terminal (cols, rows) |
325
- | `POST` | `/api/ws-ticket` | Generate a short-lived WebSocket auth ticket |
326
- | `GET` | `/api/ws-status` | Get connected WebSocket client count |
327
- | `POST` | `/api/notify` | Send a push notification via APNs or FCM |
328
- | `GET` | `/api/notify` | Check notification status and history |
329
- | `POST` | `/api/response` | Submit a permission allow/deny decision |
330
- | `GET` | `/api/response` | Poll for a pending permission decision |
331
- | `GET` | `/api/sessions` | List sessions across all projects |
332
- | `POST` | `/api/webhook` | Receive external webhook events |
333
- | `GET` | `/api/qr-config` | Generate QR code for mobile app pairing |
334
- | `POST` | `/api/device-token` | Register a device token (iOS or Android) |
335
- | `GET` | `/api/debug` | Debug information (APNs config, device token status) |
329
+ | Method | Path | Description |
330
+ | -------- | --------------------------- | ---------------------------------------------------- |
331
+ | `GET` | `/api/health` | Health check with server status |
332
+ | `GET` | `/api/terminals` | List all active and recently exited terminals |
333
+ | `POST` | `/api/terminals` | Create a new terminal session |
334
+ | `GET` | `/api/terminals/:id` | Get details for a specific terminal |
335
+ | `DELETE` | `/api/terminals/:id` | Kill and remove a terminal session |
336
+ | `POST` | `/api/terminals/:id/resize` | Resize a terminal (cols, rows) |
337
+ | `POST` | `/api/ws-ticket` | Generate a short-lived WebSocket auth ticket |
338
+ | `GET` | `/api/ws-status` | Get connected WebSocket client count |
339
+ | `POST` | `/api/notify` | Send a push notification via APNs or FCM |
340
+ | `GET` | `/api/notify` | Check notification status and history |
341
+ | `POST` | `/api/response` | Submit a permission allow/deny decision |
342
+ | `GET` | `/api/response` | Poll for a pending permission decision |
343
+ | `GET` | `/api/sessions` | List sessions across all projects |
344
+ | `POST` | `/api/webhook` | Receive external webhook events |
345
+ | `GET` | `/api/qr-config` | Generate QR code for mobile app pairing |
346
+ | `POST` | `/api/device-token` | Register a device token (iOS or Android) |
347
+ | `GET` | `/api/debug` | Debug information (APNs config, device token status) |
336
348
 
337
349
  ### WebSocket Authentication
338
350
 
@@ -341,7 +353,7 @@ WebSocket connections use ticket-based auth. First call `POST /api/ws-ticket` wi
341
353
  ### Example: Create Terminal
342
354
 
343
355
  ```bash
344
- curl -X POST http://localhost:3000/api/terminals \
356
+ curl -X POST http://localhost:54007/api/terminals \
345
357
  -H "Authorization: Bearer $API_KEY" \
346
358
  -H "Content-Type: application/json" \
347
359
  -d '{"command": "claude", "cwd": "/Users/me/project", "cols": 80, "rows": 24}'
@@ -380,6 +392,24 @@ pnpm format:check # Check formatting without writing
380
392
 
381
393
  **Note:** `pnpm dev` runs the Vite dev server, which does not include the WebSocket server or PTY manager. For full functionality (terminal sessions, live streaming), use `pnpm build && pnpm start`.
382
394
 
395
+ ### CLI Commands
396
+
397
+ The `shooter` command (via `bin/shooter.cjs` or the global `shooter` symlink) supports:
398
+
399
+ | Command | Description |
400
+ | -------------------- | ------------------------------------------------------------------ |
401
+ | `shooter start` | Start the server (default if no command given) |
402
+ | `shooter stop` | Stop the running server gracefully (SIGTERM, then SIGKILL after 5s)|
403
+ | `shooter status` | Show PID, URL, autostart state, log path |
404
+ | `shooter autostart on` | Enable autostart on login (LaunchAgent on macOS, systemd on Linux) |
405
+ | `shooter autostart off` | Disable autostart and remove the service definition |
406
+ | `shooter logs` | Tail server logs (log file on macOS, journalctl on Linux) |
407
+ | `shooter setup` | Run the interactive setup wizard; pass `--auto` for non-interactive|
408
+ | `shooter version` | Print version number |
409
+ | `shooter help` | Show all available commands |
410
+
411
+ Process state is tracked via a PID file at `~/.shooter/shooter.pid`. Logs are written to `~/.shooter/logs/shooter.log` when running via autostart.
412
+
383
413
  ### Type System
384
414
 
385
415
  Types are auto-generated from YAML specifications in `specs/types/` using [type-crafter](https://github.com/nicktaf/type-crafter). Never edit files in `src/generated/types/` directly -- edit the YAML specs and run `pnpm run gen:types`.
@@ -390,18 +420,19 @@ Types are auto-generated from YAML specifications in `specs/types/` using [type-
390
420
 
391
421
  ```
392
422
  shooter/
393
- server.ts # HTTP + WebSocket server entry point
423
+ server.ts # HTTP + WebSocket server entry point (build guard check)
394
424
  package.json # Dependencies and scripts (pnpm only)
395
425
  Dockerfile # Multi-stage Docker build
426
+ Dockerfile.test # Test image for fresh-user install verification
396
427
  docker-compose.yml # Docker Compose config
397
428
  .env.example # Environment variable template
398
429
  svelte.config.js # SvelteKit config (adapter-node)
399
430
  vite.config.ts # Vite config (node-pty external)
400
431
  bin/
401
- shooter.cjs # CLI entry point (shooter start|setup|help)
432
+ shooter.cjs # CLI entry point (start|stop|status|autostart|logs|setup|help)
402
433
  scripts/
403
- setup.cjs # Interactive setup wizard
404
- install.sh # One-command curl installer
434
+ setup.cjs # Interactive setup wizard (--auto for non-interactive)
435
+ install.sh # One-command installer (full auto setup + cloudflared)
405
436
  .claude/
406
437
  hooks/notifier.cjs # Unified hook notifier (Node.js)
407
438
  settings.json # Hook configuration (13 event types)
@@ -458,8 +489,9 @@ shooter/
458
489
 
459
490
  - Verify Node.js 20+ is installed: `node --version`
460
491
  - Ensure pnpm is used (npm and yarn are blocked): `pnpm --version`
461
- - Check that `pnpm build` completed without errors before running `pnpm start`
462
- - Confirm `.env` exists and `API_KEY` is set
492
+ - Check that `pnpm build` completed without errors before running `pnpm start` -- `server.ts` has a build guard that exits with a clear error if `build/handler.js` is missing
493
+ - Confirm `.env` exists and `API_KEY` is set (the server also checks `~/.shooter/.env` as a fallback)
494
+ - On Linux, ensure build tools are installed: `python3`, `make`, `g++` (needed for native modules)
463
495
 
464
496
  ### WebSocket connections fail
465
497
 
@@ -479,7 +511,7 @@ shooter/
479
511
 
480
512
  - `API_KEY` must be exported in your shell environment, not just in `.env`: `export API_KEY="..."`
481
513
  - Verify the hooks are configured in `.claude/settings.json`
482
- - Test connectivity: `curl -H "Authorization: Bearer $API_KEY" http://localhost:3000/api/health`
514
+ - Test connectivity: `curl -H "Authorization: Bearer $API_KEY" http://localhost:54007/api/health`
483
515
 
484
516
  ### Terminal sessions lost after restart
485
517
 
@@ -494,8 +526,8 @@ shooter/
494
526
 
495
527
  ### Port already in use
496
528
 
497
- - Default port is 3000. Set `PORT=<number>` in `.env` to use a different port.
498
- - Check what is using the port: `lsof -i :3000`
529
+ - Default port is 54007. Set `PORT=<number>` in `.env` to use a different port.
530
+ - Check what is using the port: `lsof -i :54007`
499
531
 
500
532
  ---
501
533