@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
@@ -15,7 +15,7 @@ const pty = require('node-pty');
15
15
  // Parse arguments
16
16
  // ---------------------------------------------------------------------------
17
17
 
18
- const [,, id, socketPath, cwd, colsStr, rowsStr, command, ...args] = process.argv;
18
+ const [, , id, socketPath, cwd, colsStr, rowsStr, command, ...args] = process.argv;
19
19
 
20
20
  if (!id || !socketPath || !cwd || !colsStr || !rowsStr || !command) {
21
21
  process.stderr.write('pty-holder: missing required arguments\n');
@@ -31,6 +31,29 @@ const rows = parseInt(rowsStr, 10);
31
31
 
32
32
  const MAX_SCROLLBACK_LINES = 5000;
33
33
  const MAX_INPUT_BYTES = 65_536; // 64 KB — cap per-write to prevent memory abuse
34
+
35
+ /**
36
+ * Truncate a string to at most maxBytes bytes without splitting a multi-byte
37
+ * UTF-8 character. String.prototype.slice() counts UTF-16 code units, not
38
+ * bytes, so a naive slice can produce more bytes than intended or cut a
39
+ * multi-byte character in half. This helper uses Buffer to measure byte length
40
+ * and binary-searches for the right code-unit boundary.
41
+ */
42
+ function truncateUtf8(str, maxBytes) {
43
+ if (Buffer.byteLength(str, 'utf8') <= maxBytes) return str;
44
+ // Binary search for the largest code-unit prefix that fits
45
+ let lo = 0;
46
+ let hi = str.length;
47
+ while (lo < hi) {
48
+ const mid = (lo + hi + 1) >>> 1;
49
+ if (Buffer.byteLength(str.slice(0, mid), 'utf8') <= maxBytes) {
50
+ lo = mid;
51
+ } else {
52
+ hi = mid - 1;
53
+ }
54
+ }
55
+ return str.slice(0, lo);
56
+ }
34
57
  const GRACE_PERIOD_MS = 60_000; // 60 seconds after PTY exit before self-terminating
35
58
  const ACTIVITY_IDLE_MS = 5_000; // 5 seconds of no output → idle
36
59
 
@@ -49,7 +72,7 @@ function pushScrollback(data) {
49
72
  while (scrollbackLineCount > MAX_SCROLLBACK_LINES && scrollbackChunks.length > 1) {
50
73
  const removed = scrollbackChunks.shift();
51
74
  if (removed) {
52
- scrollbackLineCount -= (removed.split('\n').length - 1);
75
+ scrollbackLineCount -= removed.split('\n').length - 1;
53
76
  }
54
77
  }
55
78
  }
@@ -85,7 +108,11 @@ const ptyEnv = { ...process.env };
85
108
 
86
109
  // Clipboard image paste support: per-terminal clipboard directory
87
110
  const clipboardDir = path.join(os.tmpdir(), `shooter-clipboard-${id}`);
88
- try { fs.mkdirSync(clipboardDir, { recursive: true }); } catch { /* best effort */ }
111
+ try {
112
+ fs.mkdirSync(clipboardDir, { recursive: true });
113
+ } catch {
114
+ /* best effort */
115
+ }
89
116
  ptyEnv.SHOOTER_CLIPBOARD_DIR = clipboardDir;
90
117
 
91
118
  // Prepend clipboard shim scripts to PATH so tools find our xclip/wl-paste
@@ -102,7 +129,10 @@ let spawnCommand;
102
129
  let spawnArgs;
103
130
 
104
131
  if (SHELL_COMMANDS.includes(commandBase)) {
105
- if (commandBase === 'zsh' || (commandBase === 'sh' && (process.env.SHELL || '').includes('zsh'))) {
132
+ if (
133
+ commandBase === 'zsh' ||
134
+ (commandBase === 'sh' && (process.env.SHELL || '').includes('zsh'))
135
+ ) {
106
136
  // zsh: use ZDOTDIR with custom .zshrc
107
137
  const zdotdir = path.join(os.tmpdir(), `shooter-zd-${id}`);
108
138
  try {
@@ -208,7 +238,8 @@ function broadcast(msg) {
208
238
  // ---------------------------------------------------------------------------
209
239
 
210
240
  // Matches: \x1b]7;file://hostname/path\x1b\\ or \x1b]7;file://hostname/path\x07
211
- const OSC7_RE = /\x1b\]7;file:\/\/[^/]*([^\x07\x1b]*)\x07|\x1b\]7;file:\/\/[^/]*([^\x07\x1b]*)\x1b\\/g;
241
+ const OSC7_RE =
242
+ /\x1b\]7;file:\/\/[^/]*([^\x07\x1b]*)\x07|\x1b\]7;file:\/\/[^/]*([^\x07\x1b]*)\x1b\\/g;
212
243
 
213
244
  // Buffer for incomplete OSC 7 sequences split across data chunks
214
245
  let osc7PartialBuf = '';
@@ -391,9 +422,7 @@ function handleMessage(msg) {
391
422
  // static-analysis scanners and prevent memory abuse.
392
423
  if (!exited && typeof msg.data === 'string' && msg.data.length > 0) {
393
424
  try {
394
- const input = msg.data.length > MAX_INPUT_BYTES
395
- ? msg.data.slice(0, MAX_INPUT_BYTES)
396
- : msg.data;
425
+ const input = truncateUtf8(msg.data, MAX_INPUT_BYTES);
397
426
  ptyProcess.write(input); // CodeQL[js/code-injection] — intentional PTY stdin
398
427
  } catch {
399
428
  // PTY may have closed between check and write
@@ -1,3 +1,4 @@
1
+ import type { TerminalRecord } from '$generated/types';
1
2
  import type WebSocket from 'ws';
2
3
 
3
4
  import { type ChildProcess, fork } from 'child_process';
@@ -6,9 +7,10 @@ import { existsSync, readdirSync, readFileSync, statSync, unlinkSync } from 'fs'
6
7
  import path from 'path';
7
8
  import { fileURLToPath } from 'url';
8
9
 
10
+ import type { ConversationMessage } from '../sessions/types';
11
+
9
12
  import { HolderClient } from './holder-client';
10
13
  import { openCodeWatcher } from './opencode-watcher';
11
- import { sessionWatcher } from './session-watcher';
12
14
  import { terminalStore } from './terminal-store';
13
15
 
14
16
  // ---------------------------------------------------------------------------
@@ -28,7 +30,7 @@ interface ManagedTerminal {
28
30
  holderPid: number;
29
31
  id: string;
30
32
  isActive: boolean;
31
- openCodeNoopCb: ((messages: import('../sessions/types').ConversationMessage[]) => void) | null;
33
+ openCodeNoopCb: ((messages: ConversationMessage[]) => void) | null;
32
34
  openCodeSessionId: null | string;
33
35
  outputBuffers: Map<WebSocket, OutputBuffer>;
34
36
  pid: number;
@@ -75,7 +77,9 @@ class PtyManager {
75
77
  private terminals = new Map<string, ManagedTerminal>();
76
78
 
77
79
  constructor() {
78
- this.cleanupTimer = setInterval(() => { this.cleanup(); }, CLEANUP_INTERVAL_MS);
80
+ this.cleanupTimer = setInterval(() => {
81
+ this.cleanup();
82
+ }, CLEANUP_INTERVAL_MS);
79
83
  }
80
84
 
81
85
  // -----------------------------------------------------------------------
@@ -85,13 +89,15 @@ class PtyManager {
85
89
 
86
90
  attach(id: string, ws: WebSocket): boolean {
87
91
  const terminal = this.terminals.get(id);
88
- if (!terminal) {return false;}
92
+ if (!terminal) {
93
+ return false;
94
+ }
89
95
 
90
96
  terminal.clients.add(ws);
91
97
  terminal.outputBuffers.set(ws, { data: [], size: 0 });
92
98
 
93
99
  // Send cached scrollback in chunks
94
- this.sendScrollback(terminal, ws);
100
+ void this.sendScrollback(terminal, ws);
95
101
 
96
102
  return true;
97
103
  }
@@ -102,10 +108,12 @@ class PtyManager {
102
108
 
103
109
  cleanup(): void {
104
110
  const now = Date.now();
105
- const exited: { exitedAt: number; id: string; }[] = [];
111
+ const exited: { exitedAt: number; id: string }[] = [];
106
112
 
107
113
  for (const [id, terminal] of this.terminals) {
108
- if (terminal.status !== 'exited') {continue;}
114
+ if (terminal.status !== 'exited') {
115
+ continue;
116
+ }
109
117
 
110
118
  const exitTime = terminal.exitedAt?.getTime() ?? terminal.createdAt.getTime();
111
119
 
@@ -200,8 +208,8 @@ class PtyManager {
200
208
  cols,
201
209
  command,
202
210
  createdAt: now,
203
- cwd,
204
211
  currentCwd: null,
212
+ cwd,
205
213
  exitCode: connectResult.exitCode,
206
214
  exitedAt: null,
207
215
  holderPid,
@@ -303,7 +311,9 @@ class PtyManager {
303
311
 
304
312
  detach(id: string, ws: WebSocket): boolean {
305
313
  const terminal = this.terminals.get(id);
306
- if (!terminal) {return false;}
314
+ if (!terminal) {
315
+ return false;
316
+ }
307
317
 
308
318
  terminal.clients.delete(ws);
309
319
  terminal.outputBuffers.delete(ws);
@@ -359,7 +369,9 @@ class PtyManager {
359
369
 
360
370
  getScrollback(id: string): null | string {
361
371
  const terminal = this.terminals.get(id);
362
- if (!terminal) {return null;}
372
+ if (!terminal) {
373
+ return null;
374
+ }
363
375
 
364
376
  return terminal.scrollback;
365
377
  }
@@ -370,8 +382,12 @@ class PtyManager {
370
382
 
371
383
  kill(id: string): boolean {
372
384
  const terminal = this.terminals.get(id);
373
- if (!terminal) {return false;}
374
- if (terminal.status === 'exited') {return true;} // already dead
385
+ if (!terminal) {
386
+ return false;
387
+ }
388
+ if (terminal.status === 'exited') {
389
+ return true;
390
+ } // already dead
375
391
 
376
392
  try {
377
393
  // Send SIGTERM through the holder protocol
@@ -454,8 +470,12 @@ class PtyManager {
454
470
 
455
471
  remove(id: string): boolean {
456
472
  const terminal = this.terminals.get(id);
457
- if (!terminal) {return false;}
458
- if (terminal.status === 'running') {return false;} // cannot remove running terminals
473
+ if (!terminal) {
474
+ return false;
475
+ }
476
+ if (terminal.status === 'running') {
477
+ return false;
478
+ } // cannot remove running terminals
459
479
 
460
480
  this.evict(id);
461
481
  return true;
@@ -467,7 +487,9 @@ class PtyManager {
467
487
 
468
488
  resize(id: string, cols: number, rows: number): boolean {
469
489
  const terminal = this.terminals.get(id);
470
- if (!terminal || terminal.status === 'exited') {return false;}
490
+ if (!terminal || terminal.status === 'exited') {
491
+ return false;
492
+ }
471
493
 
472
494
  try {
473
495
  terminal.pty.resize(cols, rows);
@@ -515,7 +537,9 @@ class PtyManager {
515
537
  }
516
538
 
517
539
  const buffer = terminal.outputBuffers.get(ws);
518
- if (!buffer) {continue;}
540
+ if (!buffer) {
541
+ continue;
542
+ }
519
543
 
520
544
  const msgSize = Buffer.byteLength(msg, 'utf8');
521
545
 
@@ -556,7 +580,9 @@ class PtyManager {
556
580
  /** Evict a terminal, freeing all resources. */
557
581
  private evict(id: string): void {
558
582
  const terminal = this.terminals.get(id);
559
- if (!terminal) {return;}
583
+ if (!terminal) {
584
+ return;
585
+ }
560
586
 
561
587
  // Clear session-file poll timer if still running
562
588
  if (terminal.pollTimer) {
@@ -611,11 +637,9 @@ class PtyManager {
611
637
  // with backpressure management
612
638
  // -----------------------------------------------------------------------
613
639
 
614
- private handleReconnectFailure(record: {
615
- holderPid: null | number;
616
- id: string;
617
- socketPath: null | string;
618
- }): void {
640
+ private handleReconnectFailure(
641
+ record: Pick<TerminalRecord, 'holderPid' | 'id' | 'socketPath'>
642
+ ): void {
619
643
  // Check if holder PID is still alive
620
644
  if (record.holderPid) {
621
645
  try {
@@ -631,7 +655,7 @@ class PtyManager {
631
655
 
632
656
  // Check for .exit sidecar
633
657
  if (record.socketPath) {
634
- const exitFilePath = `${record.socketPath }.exit`;
658
+ const exitFilePath = `${record.socketPath}.exit`;
635
659
  if (existsSync(exitFilePath)) {
636
660
  try {
637
661
  const exitData = JSON.parse(readFileSync(exitFilePath, 'utf8')) as {
@@ -655,30 +679,14 @@ class PtyManager {
655
679
  console.log(`[pty-manager] Marked terminal ${record.id} as orphaned`);
656
680
  }
657
681
 
658
- private async reconnectOne(record: {
659
- args: string;
660
- cols: number;
661
- command: string;
662
- createdAt: string;
663
- cwd: string;
664
- exitCode: null | number;
665
- exitedAt: null | string;
666
- holderPid: null | number;
667
- id: string;
668
- opencodeSessionId: null | string;
669
- pid: null | number;
670
- rows: number;
671
- sessionFile: null | string;
672
- socketPath: null | string;
673
- status: string;
674
- }): Promise<void> {
682
+ private async reconnectOne(record: TerminalRecord): Promise<void> {
675
683
  if (!record.socketPath) {
676
684
  throw new Error('No socket path stored');
677
685
  }
678
686
 
679
687
  // Check for .exit sidecar file — the PTY may have exited while
680
688
  // the server was down
681
- const exitFilePath = `${record.socketPath }.exit`;
689
+ const exitFilePath = `${record.socketPath}.exit`;
682
690
  if (existsSync(exitFilePath)) {
683
691
  try {
684
692
  const exitData = JSON.parse(readFileSync(exitFilePath, 'utf8')) as {
@@ -751,7 +759,9 @@ class PtyManager {
751
759
  // which blocked real subscribers due to the single-callback guard.
752
760
  }
753
761
  if (terminal.openCodeSessionId) {
754
- const noopCb: (messages: import('../sessions/types').ConversationMessage[]) => void = () => {};
762
+ const noopCb = (_messages: ConversationMessage[]): void => {
763
+ /* noop */
764
+ };
755
765
  terminal.openCodeNoopCb = noopCb;
756
766
  openCodeWatcher.watch(terminal.openCodeSessionId, noopCb);
757
767
  }
@@ -773,60 +783,13 @@ class PtyManager {
773
783
  );
774
784
  }
775
785
 
776
- /** Wire up all HolderClient callbacks (activity, CWD, output, exit, disconnect). */
777
- private wireHolderCallbacks(client: HolderClient, terminal: ManagedTerminal): void {
778
- client.onActivity((active: boolean) => {
779
- terminal.isActive = active;
780
- const msg = JSON.stringify({ active, type: 'activity' });
781
- for (const ws of terminal.clients) {
782
- this.safeSend(ws, msg);
783
- }
784
- });
785
-
786
- client.onCwd((path: string) => {
787
- terminal.currentCwd = path;
788
- const msg = JSON.stringify({ path, type: 'cwd' });
789
- for (const ws of terminal.clients) {
790
- this.safeSend(ws, msg);
791
- }
792
- });
793
-
794
- client.onOutput((data: string) => {
795
- this.appendScrollback(terminal, data);
796
- this.broadcastOutput(terminal, data);
797
- });
798
-
799
- client.onExit((exitCode: null | number) => {
800
- terminal.status = 'exited';
801
- terminal.exitCode = exitCode;
802
- terminal.exitedAt = new Date();
803
- terminalStore.markExited(terminal.id, exitCode);
804
-
805
- const exitMsg = JSON.stringify({
806
- code: exitCode,
807
- signal: null,
808
- type: 'exit',
809
- });
810
- for (const ws of terminal.clients) {
811
- this.safeSend(ws, exitMsg);
812
- }
813
- });
814
-
815
- client.onDisconnect(() => {
816
- if (terminal.status === 'running') {
817
- console.warn(`[pty-manager] Holder disconnected unexpectedly for terminal ${terminal.id}`);
818
- terminal.status = 'exited';
819
- terminal.exitedAt = new Date();
820
- terminalStore.markOrphaned(terminal.id);
821
- }
822
- });
823
- }
824
-
825
786
  /** Safely send a message to a WebSocket, returning false on failure. */
826
787
  private safeSend(ws: WebSocket, msg: string): boolean {
827
788
  try {
828
789
  // readyState 1 === OPEN
829
- if (ws.readyState !== 1) {return false;}
790
+ if (ws.readyState !== 1) {
791
+ return false;
792
+ }
830
793
  ws.send(msg);
831
794
  return true;
832
795
  } catch {
@@ -837,7 +800,9 @@ class PtyManager {
837
800
  /** Send cached scrollback data to a newly connected client in 50 KB chunks. */
838
801
  private async sendScrollback(terminal: ManagedTerminal, ws: WebSocket): Promise<void> {
839
802
  const fullData = terminal.scrollback;
840
- if (fullData.length === 0) {return;}
803
+ if (fullData.length === 0) {
804
+ return;
805
+ }
841
806
 
842
807
  const totalBytes = Buffer.byteLength(fullData, 'utf8');
843
808
  const totalChunks = Math.ceil(totalBytes / SCROLLBACK_CHUNK_SIZE);
@@ -862,7 +827,7 @@ class PtyManager {
862
827
  while (offset < buf.length) {
863
828
  // Gate scrollback sending on actual socket backpressure
864
829
  if (ws.bufferedAmount > SCROLLBACK_CHUNK_SIZE * 2) {
865
- await new Promise(resolve => setTimeout(resolve, 50));
830
+ await new Promise((resolve) => setTimeout(resolve, 50));
866
831
  }
867
832
 
868
833
  const end = Math.min(offset + SCROLLBACK_CHUNK_SIZE, buf.length);
@@ -886,9 +851,31 @@ class PtyManager {
886
851
  // directory for new JSONL files created after launch.
887
852
  if (command === 'claude') {
888
853
  const projectDir = path.join(
889
- process.env.HOME || '', '.claude', 'projects',
854
+ process.env.HOME || '',
855
+ '.claude',
856
+ 'projects',
890
857
  cwd.replace(/\//g, '-')
891
858
  );
859
+
860
+ // Fast path: `claude --resume <uuid>` resumes an existing session whose
861
+ // JSONL file was created before this terminal launched. The birthtime
862
+ // filter used in the polling loop would never find it, so check directly.
863
+ const resumeIdx = terminal.args.indexOf('--resume');
864
+ if (resumeIdx !== -1 && resumeIdx + 1 < terminal.args.length) {
865
+ const resumeId = terminal.args[resumeIdx + 1];
866
+ // Validate resumeId is a safe identifier (UUID or hex string, no path separators)
867
+ if (!/^[0-9a-f-]+$/i.test(resumeId)) {
868
+ console.warn(`[pty-manager] Invalid resume ID: ${resumeId}`);
869
+ } else {
870
+ const resumeFile = path.join(projectDir, `${resumeId}.jsonl`);
871
+ if (existsSync(resumeFile)) {
872
+ terminal.sessionFile = resumeFile;
873
+ terminalStore.update(id, { sessionFile: resumeFile });
874
+ return; // File found immediately — no polling needed
875
+ }
876
+ }
877
+ }
878
+
892
879
  const launchTime = terminal.createdAt.getTime();
893
880
 
894
881
  terminal.pollTimer = setInterval(() => {
@@ -897,15 +884,12 @@ class PtyManager {
897
884
  clearInterval(terminal.pollTimer);
898
885
  terminal.pollTimer = null;
899
886
  }
900
- if (terminal.sessionFile) {
901
- // No-op: session-handler.ts subscribes when a client connects.
902
- // Previously called sessionWatcher.watch() with an empty callback,
903
- // which blocked real subscribers due to the single-callback guard.
904
- }
905
887
  return;
906
888
  }
907
889
  try {
908
- if (!existsSync(projectDir)) {return;}
890
+ if (!existsSync(projectDir)) {
891
+ return;
892
+ }
909
893
  const files = readdirSync(projectDir)
910
894
  .filter((f) => f.endsWith('.jsonl'))
911
895
  .map((f) => {
@@ -929,9 +913,6 @@ class PtyManager {
929
913
  clearInterval(terminal.pollTimer);
930
914
  terminal.pollTimer = null;
931
915
  }
932
- // No-op: session-handler.ts subscribes when a client connects.
933
- // Previously called sessionWatcher.watch() with an empty callback,
934
- // which blocked real subscribers due to the single-callback guard.
935
916
  // Persist session file to SQLite
936
917
  terminalStore.update(id, { sessionFile: terminal.sessionFile });
937
918
  }
@@ -940,12 +921,15 @@ class PtyManager {
940
921
  }
941
922
  }, 1500);
942
923
 
943
- setTimeout(() => {
944
- if (terminal.pollTimer) {
945
- clearInterval(terminal.pollTimer);
946
- terminal.pollTimer = null;
947
- }
948
- }, 5 * 60 * 1000);
924
+ setTimeout(
925
+ () => {
926
+ if (terminal.pollTimer) {
927
+ clearInterval(terminal.pollTimer);
928
+ terminal.pollTimer = null;
929
+ }
930
+ },
931
+ 5 * 60 * 1000
932
+ );
949
933
  }
950
934
 
951
935
  // For OpenCode: detect the session via SQLite database lookup.
@@ -957,7 +941,9 @@ class PtyManager {
957
941
  if (terminal.status === 'exited' || terminal.openCodeSessionId) {
958
942
  clearInterval(pollInterval);
959
943
  if (terminal.openCodeSessionId) {
960
- const noopCb: (messages: import('../sessions/types').ConversationMessage[]) => void = () => {};
944
+ const noopCb = (_messages: ConversationMessage[]): void => {
945
+ /* noop */
946
+ };
961
947
  terminal.openCodeNoopCb = noopCb;
962
948
  openCodeWatcher.watch(terminal.openCodeSessionId, noopCb);
963
949
  }
@@ -967,7 +953,9 @@ class PtyManager {
967
953
  if (sessionId) {
968
954
  terminal.openCodeSessionId = sessionId;
969
955
  clearInterval(pollInterval);
970
- const noopCb: (messages: import('../sessions/types').ConversationMessage[]) => void = () => {};
956
+ const noopCb = (_messages: ConversationMessage[]): void => {
957
+ /* noop */
958
+ };
971
959
  terminal.openCodeNoopCb = noopCb;
972
960
  openCodeWatcher.watch(sessionId, noopCb);
973
961
  // Persist session ID to SQLite
@@ -976,9 +964,64 @@ class PtyManager {
976
964
  }, 2000);
977
965
 
978
966
  terminal.pollTimer = pollInterval;
979
- setTimeout(() => { clearInterval(pollInterval); terminal.pollTimer = null; }, 5 * 60 * 1000);
967
+ setTimeout(
968
+ () => {
969
+ clearInterval(pollInterval);
970
+ terminal.pollTimer = null;
971
+ },
972
+ 5 * 60 * 1000
973
+ );
980
974
  }
981
975
  }
976
+
977
+ /** Wire up all HolderClient callbacks (activity, CWD, output, exit, disconnect). */
978
+ private wireHolderCallbacks(client: HolderClient, terminal: ManagedTerminal): void {
979
+ client.onActivity((active: boolean) => {
980
+ terminal.isActive = active;
981
+ const msg = JSON.stringify({ active, type: 'activity' });
982
+ for (const ws of terminal.clients) {
983
+ this.safeSend(ws, msg);
984
+ }
985
+ });
986
+
987
+ client.onCwd((path: string) => {
988
+ terminal.currentCwd = path;
989
+ const msg = JSON.stringify({ path, type: 'cwd' });
990
+ for (const ws of terminal.clients) {
991
+ this.safeSend(ws, msg);
992
+ }
993
+ });
994
+
995
+ client.onOutput((data: string) => {
996
+ this.appendScrollback(terminal, data);
997
+ this.broadcastOutput(terminal, data);
998
+ });
999
+
1000
+ client.onExit((exitCode: null | number) => {
1001
+ terminal.status = 'exited';
1002
+ terminal.exitCode = exitCode;
1003
+ terminal.exitedAt = new Date();
1004
+ terminalStore.markExited(terminal.id, exitCode);
1005
+
1006
+ const exitMsg = JSON.stringify({
1007
+ code: exitCode,
1008
+ signal: null,
1009
+ type: 'exit',
1010
+ });
1011
+ for (const ws of terminal.clients) {
1012
+ this.safeSend(ws, exitMsg);
1013
+ }
1014
+ });
1015
+
1016
+ client.onDisconnect(() => {
1017
+ if (terminal.status === 'running') {
1018
+ console.warn(`[pty-manager] Holder disconnected unexpectedly for terminal ${terminal.id}`);
1019
+ terminal.status = 'exited';
1020
+ terminal.exitedAt = new Date();
1021
+ terminalStore.markOrphaned(terminal.id);
1022
+ }
1023
+ });
1024
+ }
982
1025
  }
983
1026
 
984
1027
  // ---------------------------------------------------------------------------
@@ -1007,6 +1050,5 @@ function resolveHolderPath(): string {
1007
1050
  // server.ts (tsx) and SvelteKit's build handler load this module separately.
1008
1051
  const PTY_GLOBAL_KEY = '__shooter_pty_manager';
1009
1052
  export const ptyManager: PtyManager =
1010
- ((globalThis as Record<string, unknown>)[PTY_GLOBAL_KEY] as PtyManager) ||
1011
- new PtyManager();
1053
+ ((globalThis as Record<string, unknown>)[PTY_GLOBAL_KEY] as PtyManager) || new PtyManager();
1012
1054
  (globalThis as Record<string, unknown>)[PTY_GLOBAL_KEY] = ptyManager;
@@ -85,7 +85,9 @@ class SessionWatcher {
85
85
  const entries: Record<string, unknown>[] = [];
86
86
  for (const line of raw.split('\n')) {
87
87
  const trimmed = line.trim();
88
- if (!trimmed) {continue;}
88
+ if (!trimmed) {
89
+ continue;
90
+ }
89
91
  try {
90
92
  entries.push(JSON.parse(trimmed));
91
93
  } catch {
@@ -108,7 +110,7 @@ class SessionWatcher {
108
110
  return;
109
111
  }
110
112
 
111
- watched.watcher.close();
113
+ void watched.watcher.close();
112
114
  this.watchedFiles.delete(filePath);
113
115
  this.assistantTurnsPerFile.delete(filePath);
114
116
  this.messageIndexPerFile.delete(filePath);
@@ -315,6 +317,6 @@ function encodeCwd(cwd: string): string {
315
317
  // Use globalThis to ensure a single shared instance across module loaders.
316
318
  const SW_GLOBAL_KEY = '__shooter_session_watcher';
317
319
  export const sessionWatcher: SessionWatcher =
318
- ((globalThis as Record<string, unknown>)[SW_GLOBAL_KEY] as SessionWatcher) ||
319
- new SessionWatcher();
320
+ ((globalThis as Record<string, unknown>)[SW_GLOBAL_KEY] as SessionWatcher) ||
321
+ new SessionWatcher();
320
322
  (globalThis as Record<string, unknown>)[SW_GLOBAL_KEY] = sessionWatcher;