@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.
- package/.claude/hooks/notifier.cjs +117 -33
- package/.claude/settings.json +14 -14
- package/README.md +116 -84
- package/bin/shooter.cjs +471 -102
- package/build/client/_app/immutable/assets/{0.CM9Hl6d-.css → 0.BhZOCxO4.css} +1 -1
- package/build/client/_app/immutable/assets/0.BhZOCxO4.css.br +0 -0
- package/build/client/_app/immutable/assets/0.BhZOCxO4.css.gz +0 -0
- package/build/client/_app/immutable/assets/1.BYutk3aU.css +1 -0
- package/build/client/_app/immutable/assets/1.BYutk3aU.css.br +0 -0
- package/build/client/_app/immutable/assets/1.BYutk3aU.css.gz +0 -0
- package/build/client/_app/immutable/assets/2.CAShZ7lQ.css.gz +0 -0
- package/build/client/_app/immutable/assets/3.DGDHCVnW.css +1 -0
- package/build/client/_app/immutable/assets/3.DGDHCVnW.css.br +0 -0
- package/build/client/_app/immutable/assets/3.DGDHCVnW.css.gz +0 -0
- package/build/client/_app/immutable/assets/4.BFUut--w.css +1 -0
- package/build/client/_app/immutable/assets/4.BFUut--w.css.br +0 -0
- package/build/client/_app/immutable/assets/4.BFUut--w.css.gz +0 -0
- package/build/client/_app/immutable/assets/5.BTOx7yt7.css +1 -0
- package/build/client/_app/immutable/assets/5.BTOx7yt7.css.br +0 -0
- package/build/client/_app/immutable/assets/5.BTOx7yt7.css.gz +0 -0
- package/build/client/_app/immutable/assets/6.eZGZN-BF.css +1 -0
- package/build/client/_app/immutable/assets/6.eZGZN-BF.css.br +0 -0
- package/build/client/_app/immutable/assets/6.eZGZN-BF.css.gz +0 -0
- package/build/client/_app/immutable/assets/7.DwS5ZHBh.css +1 -0
- package/build/client/_app/immutable/assets/7.DwS5ZHBh.css.br +0 -0
- package/build/client/_app/immutable/assets/7.DwS5ZHBh.css.gz +0 -0
- package/build/client/_app/immutable/assets/ChatView.CwWbzIL-.css +1 -0
- package/build/client/_app/immutable/assets/ChatView.CwWbzIL-.css.br +0 -0
- package/build/client/_app/immutable/assets/ChatView.CwWbzIL-.css.gz +0 -0
- package/build/client/_app/immutable/assets/Phone.FQEfwCX2.css +1 -0
- package/build/client/_app/immutable/assets/Phone.FQEfwCX2.css.br +0 -0
- package/build/client/_app/immutable/assets/Phone.FQEfwCX2.css.gz +0 -0
- package/build/client/_app/immutable/assets/markdown.Dc-OSJWY.css +1 -0
- package/build/client/_app/immutable/assets/markdown.Dc-OSJWY.css.br +0 -0
- package/build/client/_app/immutable/assets/markdown.Dc-OSJWY.css.gz +0 -0
- package/build/client/_app/immutable/assets/xterm.DFuMZ0ql.css.gz +0 -0
- package/build/client/_app/immutable/chunks/B-K5Sh65.js +1 -0
- package/build/client/_app/immutable/chunks/B-K5Sh65.js.br +0 -0
- package/build/client/_app/immutable/chunks/B-K5Sh65.js.gz +0 -0
- package/build/client/_app/immutable/chunks/B5NAKyil.js +20 -0
- package/build/client/_app/immutable/chunks/B5NAKyil.js.br +0 -0
- package/build/client/_app/immutable/chunks/B5NAKyil.js.gz +0 -0
- package/build/client/_app/immutable/chunks/B8XegpSE.js +1 -0
- package/build/client/_app/immutable/chunks/B8XegpSE.js.br +0 -0
- package/build/client/_app/immutable/chunks/B8XegpSE.js.gz +0 -0
- package/build/client/_app/immutable/chunks/B8zoBsv3.js +6 -0
- package/build/client/_app/immutable/chunks/B8zoBsv3.js.br +0 -0
- package/build/client/_app/immutable/chunks/B8zoBsv3.js.gz +0 -0
- package/build/client/_app/immutable/chunks/BLszSzTT.js +1 -0
- package/build/client/_app/immutable/chunks/BLszSzTT.js.br +0 -0
- package/build/client/_app/immutable/chunks/BLszSzTT.js.gz +0 -0
- package/build/client/_app/immutable/chunks/BOYo8yTr.js +1 -0
- package/build/client/_app/immutable/chunks/BOYo8yTr.js.br +0 -0
- package/build/client/_app/immutable/chunks/BOYo8yTr.js.gz +0 -0
- package/build/client/_app/immutable/chunks/BTGVxaYV.js.gz +0 -0
- package/build/client/_app/immutable/chunks/BYqGCrTe.js +1 -0
- package/build/client/_app/immutable/chunks/BYqGCrTe.js.br +0 -0
- package/build/client/_app/immutable/chunks/BYqGCrTe.js.gz +0 -0
- package/build/client/_app/immutable/chunks/BlxrFPDK.js.gz +0 -0
- package/build/client/_app/immutable/chunks/Bu1aqm5j.js +1 -0
- package/build/client/_app/immutable/chunks/Bu1aqm5j.js.br +0 -0
- package/build/client/_app/immutable/chunks/Bu1aqm5j.js.gz +0 -0
- package/build/client/_app/immutable/chunks/C4mLaWWx.js +1 -0
- package/build/client/_app/immutable/chunks/C4mLaWWx.js.br +0 -0
- package/build/client/_app/immutable/chunks/C4mLaWWx.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CQjSATpv.js +61 -0
- package/build/client/_app/immutable/chunks/CQjSATpv.js.br +0 -0
- package/build/client/_app/immutable/chunks/CQjSATpv.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CSoRdFvv.js +1 -0
- package/build/client/_app/immutable/chunks/CSoRdFvv.js.br +0 -0
- package/build/client/_app/immutable/chunks/CSoRdFvv.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CZHsSL_X.js +1 -0
- package/build/client/_app/immutable/chunks/CZHsSL_X.js.br +0 -0
- package/build/client/_app/immutable/chunks/CZHsSL_X.js.gz +0 -0
- package/build/client/_app/immutable/chunks/DLu6yJIZ.js.gz +0 -0
- package/build/client/_app/immutable/chunks/DSU1n5N_.js +1 -0
- package/build/client/_app/immutable/chunks/DSU1n5N_.js.br +0 -0
- package/build/client/_app/immutable/chunks/DSU1n5N_.js.gz +0 -0
- package/build/client/_app/immutable/chunks/DVkn4r72.js +1 -0
- package/build/client/_app/immutable/chunks/DVkn4r72.js.br +0 -0
- package/build/client/_app/immutable/chunks/DVkn4r72.js.gz +0 -0
- package/build/client/_app/immutable/chunks/DjsDGxCa.js +41 -0
- package/build/client/_app/immutable/chunks/DjsDGxCa.js.br +0 -0
- package/build/client/_app/immutable/chunks/DjsDGxCa.js.gz +0 -0
- package/build/client/_app/immutable/chunks/PPVm8Dsz.js.gz +0 -0
- package/build/client/_app/immutable/chunks/UJOiqIYE.js +1 -0
- package/build/client/_app/immutable/chunks/UJOiqIYE.js.br +0 -0
- package/build/client/_app/immutable/chunks/UJOiqIYE.js.gz +0 -0
- package/build/client/_app/immutable/chunks/r0JawsZc.js +2 -0
- package/build/client/_app/immutable/chunks/r0JawsZc.js.br +0 -0
- package/build/client/_app/immutable/chunks/r0JawsZc.js.gz +0 -0
- package/build/client/_app/immutable/entry/app.Z3zMnuSx.js +2 -0
- package/build/client/_app/immutable/entry/app.Z3zMnuSx.js.br +0 -0
- package/build/client/_app/immutable/entry/app.Z3zMnuSx.js.gz +0 -0
- package/build/client/_app/immutable/entry/start.Dd-gIP4y.js +1 -0
- package/build/client/_app/immutable/entry/start.Dd-gIP4y.js.br +2 -0
- package/build/client/_app/immutable/entry/start.Dd-gIP4y.js.gz +0 -0
- package/build/client/_app/immutable/nodes/0.D2YR8tTD.js +1 -0
- package/build/client/_app/immutable/nodes/0.D2YR8tTD.js.br +0 -0
- package/build/client/_app/immutable/nodes/0.D2YR8tTD.js.gz +0 -0
- package/build/client/_app/immutable/nodes/1.B3m6rO4C.js +1 -0
- package/build/client/_app/immutable/nodes/1.B3m6rO4C.js.br +0 -0
- package/build/client/_app/immutable/nodes/1.B3m6rO4C.js.gz +0 -0
- package/build/client/_app/immutable/nodes/2.CyRB2euU.js +1 -0
- package/build/client/_app/immutable/nodes/2.CyRB2euU.js.br +0 -0
- package/build/client/_app/immutable/nodes/2.CyRB2euU.js.gz +0 -0
- package/build/client/_app/immutable/nodes/3.3yohCM25.js +3 -0
- package/build/client/_app/immutable/nodes/3.3yohCM25.js.br +0 -0
- package/build/client/_app/immutable/nodes/3.3yohCM25.js.gz +0 -0
- package/build/client/_app/immutable/nodes/4.DEAcwl7l.js +1 -0
- package/build/client/_app/immutable/nodes/4.DEAcwl7l.js.br +0 -0
- package/build/client/_app/immutable/nodes/4.DEAcwl7l.js.gz +0 -0
- package/build/client/_app/immutable/nodes/5.C6bLGWQR.js +4 -0
- package/build/client/_app/immutable/nodes/5.C6bLGWQR.js.br +0 -0
- package/build/client/_app/immutable/nodes/5.C6bLGWQR.js.gz +0 -0
- package/build/client/_app/immutable/nodes/6.ByTzlA2D.js +2 -0
- package/build/client/_app/immutable/nodes/6.ByTzlA2D.js.br +0 -0
- package/build/client/_app/immutable/nodes/6.ByTzlA2D.js.gz +0 -0
- package/build/client/_app/immutable/nodes/7.BPMfwzd2.js +2 -0
- package/build/client/_app/immutable/nodes/7.BPMfwzd2.js.br +0 -0
- package/build/client/_app/immutable/nodes/7.BPMfwzd2.js.gz +0 -0
- package/build/client/_app/version.json +1 -1
- package/build/client/_app/version.json.br +0 -0
- package/build/client/_app/version.json.gz +0 -0
- package/build/client/favicon.svg.gz +0 -0
- package/build/client/manifest.json +13 -0
- package/build/client/manifest.json.br +0 -0
- package/build/client/manifest.json.gz +0 -0
- package/build/pty-holder.cjs +37 -8
- package/build/server/chunks/0-Vk38tI2J.js +9 -0
- package/build/server/chunks/0-Vk38tI2J.js.map +1 -0
- package/build/server/chunks/1-BvYQX5MR.js +9 -0
- package/build/server/chunks/1-BvYQX5MR.js.map +1 -0
- package/build/server/chunks/2-Cl7R4Qk2.js +9 -0
- package/build/server/chunks/2-Cl7R4Qk2.js.map +1 -0
- package/build/server/chunks/3-Ck7ewhOX.js +9 -0
- package/build/server/chunks/3-Ck7ewhOX.js.map +1 -0
- package/build/server/chunks/4-CnDeRm2Z.js +9 -0
- package/build/server/chunks/4-CnDeRm2Z.js.map +1 -0
- package/build/server/chunks/5-IxitzEvN.js +9 -0
- package/build/server/chunks/5-IxitzEvN.js.map +1 -0
- package/build/server/chunks/6-CyZ3r1iS.js +9 -0
- package/build/server/chunks/6-CyZ3r1iS.js.map +1 -0
- package/build/server/chunks/7-BmI7du46.js +9 -0
- package/build/server/chunks/7-BmI7du46.js.map +1 -0
- package/build/server/chunks/Button-Cs1aE6ka.js +80 -0
- package/build/server/chunks/Button-Cs1aE6ka.js.map +1 -0
- package/build/server/chunks/EmptyState-DDFH1K8g.js +26 -0
- package/build/server/chunks/EmptyState-DDFH1K8g.js.map +1 -0
- package/build/server/chunks/Icon-CEUrotA6.js +36 -0
- package/build/server/chunks/Icon-CEUrotA6.js.map +1 -0
- package/build/server/chunks/Shimmer-DB8W1zt6.js +10 -0
- package/build/server/chunks/Shimmer-DB8W1zt6.js.map +1 -0
- package/build/server/chunks/_error.svelte-uCOJNxvr.js +39 -0
- package/build/server/chunks/_error.svelte-uCOJNxvr.js.map +1 -0
- package/build/server/chunks/_layout.svelte-CtWmEJwe.js +56 -0
- package/build/server/chunks/_layout.svelte-CtWmEJwe.js.map +1 -0
- package/build/server/chunks/_page.svelte-BcZaKdX9.js +45 -0
- package/build/server/chunks/_page.svelte-BcZaKdX9.js.map +1 -0
- package/build/server/chunks/_page.svelte-BdYynOck.js +85 -0
- package/build/server/chunks/_page.svelte-BdYynOck.js.map +1 -0
- package/build/server/chunks/_page.svelte-BgevQjq1.js +101 -0
- package/build/server/chunks/_page.svelte-BgevQjq1.js.map +1 -0
- package/build/server/chunks/_page.svelte-CVq6tRb3.js +550 -0
- package/build/server/chunks/_page.svelte-CVq6tRb3.js.map +1 -0
- package/build/server/chunks/_page.svelte-CxWcQ0Am.js +651 -0
- package/build/server/chunks/_page.svelte-CxWcQ0Am.js.map +1 -0
- package/build/server/chunks/_page.svelte-DO4oa_LY.js +44 -0
- package/build/server/chunks/_page.svelte-DO4oa_LY.js.map +1 -0
- package/build/server/chunks/{_server.ts-CbDRDIoP.js → _server.ts-BStnNIcq.js} +9 -11
- package/build/server/chunks/_server.ts-BStnNIcq.js.map +1 -0
- package/build/server/chunks/{_server.ts-DRVbgm6k.js → _server.ts-CAxsWKvS.js} +22 -25
- package/build/server/chunks/_server.ts-CAxsWKvS.js.map +1 -0
- package/build/server/chunks/{_server.ts-CPa6DgIt.js → _server.ts-COu0vNpd.js} +6 -6
- package/build/server/chunks/_server.ts-COu0vNpd.js.map +1 -0
- package/build/server/chunks/{_server.ts-C29xzfaw.js → _server.ts-CTpcLUH8.js} +10 -10
- package/build/server/chunks/_server.ts-CTpcLUH8.js.map +1 -0
- package/build/server/chunks/{_server.ts-D4MNi4cD.js → _server.ts-Cf84YIaW.js} +3 -3
- package/build/server/chunks/{_server.ts-D4MNi4cD.js.map → _server.ts-Cf84YIaW.js.map} +1 -1
- package/build/server/chunks/{_server.ts-BL2FGb5Z.js → _server.ts-Ch-6iOHp.js} +99 -53
- package/build/server/chunks/_server.ts-Ch-6iOHp.js.map +1 -0
- package/build/server/chunks/_server.ts-CtH0dhUp.js +71 -0
- package/build/server/chunks/_server.ts-CtH0dhUp.js.map +1 -0
- package/build/server/chunks/_server.ts-DB_Kg97c.js +73 -0
- package/build/server/chunks/_server.ts-DB_Kg97c.js.map +1 -0
- package/build/server/chunks/{_server.ts-DfajWaqh.js → _server.ts-DV8zTCF9.js} +7 -9
- package/build/server/chunks/_server.ts-DV8zTCF9.js.map +1 -0
- package/build/server/chunks/_server.ts-DYpJImqd.js +99 -0
- package/build/server/chunks/_server.ts-DYpJImqd.js.map +1 -0
- package/build/server/chunks/{_server.ts-ColfDHW8.js → _server.ts-DYvb9ijZ.js} +21 -10
- package/build/server/chunks/_server.ts-DYvb9ijZ.js.map +1 -0
- package/build/server/chunks/{_server.ts-Cv_OrRuL.js → _server.ts-Deok2y88.js} +209 -34
- package/build/server/chunks/_server.ts-Deok2y88.js.map +1 -0
- package/build/server/chunks/{_server.ts-y9-WYDMa.js → _server.ts-WhTJBEJy.js} +5 -4
- package/build/server/chunks/{_server.ts-y9-WYDMa.js.map → _server.ts-WhTJBEJy.js.map} +1 -1
- package/build/server/chunks/{_server.ts-BjOJsoy4.js → _server.ts-XzT2UHM1.js} +6 -5
- package/build/server/chunks/_server.ts-XzT2UHM1.js.map +1 -0
- package/build/server/chunks/{_server.ts-BgdjBZco.js → _server.ts-tSpgyl1D.js} +7 -5
- package/build/server/chunks/_server.ts-tSpgyl1D.js.map +1 -0
- package/build/server/chunks/{_server.ts-BihKSdj_.js → _server.ts-vekTmWAx.js} +8 -8
- package/build/server/chunks/_server.ts-vekTmWAx.js.map +1 -0
- package/build/server/chunks/{auth-CEgFis71.js → auth-DeCdZ83n.js} +2 -2
- package/build/server/chunks/{auth-CEgFis71.js.map → auth-DeCdZ83n.js.map} +1 -1
- package/build/server/chunks/client-BdGHe_hY.js +25 -0
- package/build/server/chunks/client-BdGHe_hY.js.map +1 -0
- package/build/server/chunks/client2-CCBGA-2V.js +7 -0
- package/build/server/chunks/client2-CCBGA-2V.js.map +1 -0
- package/build/server/chunks/error-DDXB3duW.js +12 -0
- package/build/server/chunks/error-DDXB3duW.js.map +1 -0
- package/build/server/chunks/{exports-CJ0Q5XmL.js → index-DwaY1cAm.js} +1111 -1634
- package/build/server/chunks/index-DwaY1cAm.js.map +1 -0
- package/build/server/chunks/index-server-CrDaL06Y.js +9 -0
- package/build/server/chunks/index-server-CrDaL06Y.js.map +1 -0
- package/build/server/chunks/index2-CgclKpUj.js +58 -0
- package/build/server/chunks/index2-CgclKpUj.js.map +1 -0
- package/build/server/chunks/{library-apns-BHxLmuIx.js → library-apns-BqJbvSKh.js} +4 -4
- package/build/server/chunks/library-apns-BqJbvSKh.js.map +1 -0
- package/build/server/chunks/markdown-W_mTBct0.js +8 -0
- package/build/server/chunks/markdown-W_mTBct0.js.map +1 -0
- package/build/server/chunks/opencode-db-path-DcfhJtJy.js +15 -0
- package/build/server/chunks/opencode-db-path-DcfhJtJy.js.map +1 -0
- package/build/server/chunks/{pty-manager-C0FhBiVq.js → pty-manager-BQVB7IVj.js} +155 -326
- package/build/server/chunks/pty-manager-BQVB7IVj.js.map +1 -0
- package/build/server/chunks/root-DDSnEAZv.js +1171 -0
- package/build/server/chunks/root-DDSnEAZv.js.map +1 -0
- package/build/server/chunks/{shared-server-BDY8jh20.js → shared-server-sSGG17Df.js} +2 -3
- package/build/server/chunks/{shared-server-BDY8jh20.js.map → shared-server-sSGG17Df.js.map} +1 -1
- package/build/server/chunks/state.svelte-hBbXlUak.js +11 -0
- package/build/server/chunks/state.svelte-hBbXlUak.js.map +1 -0
- package/build/server/chunks/stores-DHNzYNpX.js +28 -0
- package/build/server/chunks/stores-DHNzYNpX.js.map +1 -0
- package/build/server/index.js +1085 -2242
- package/build/server/index.js.map +1 -1
- package/build/server/manifest.js +39 -25
- package/build/server/manifest.js.map +1 -1
- package/package.json +32 -9
- package/scripts/fix-generated-types.sh +37 -0
- package/scripts/homebrew/shooter.rb +51 -0
- package/scripts/install.sh +348 -186
- package/scripts/setup.cjs +215 -45
- package/server.ts +114 -71
- package/src/app.css +12 -3
- package/src/app.d.ts +13 -20
- package/src/app.html +3 -2
- package/src/generated/types/API.ts +280 -0
- package/src/generated/types/APN.ts +186 -203
- package/src/generated/types/CLI.ts +18 -25
- package/src/generated/types/Client.ts +589 -0
- package/src/generated/types/Config.ts +53 -0
- package/src/generated/types/Holder.ts +638 -0
- package/src/generated/types/JWT.ts +39 -50
- package/src/generated/types/Notification.ts +426 -0
- package/src/generated/types/OpenCode.ts +356 -0
- package/src/generated/types/Sessions.ts +570 -0
- package/src/generated/types/Terminal.ts +2184 -2071
- package/src/generated/types/WsProtocol.ts +2004 -0
- package/src/generated/types/index.ts +9 -3
- package/src/lib/env.ts +29 -0
- package/src/lib/modules/client/common/cache.ts +10 -2
- package/src/lib/modules/client/common/config-guard.ts +37 -5
- package/src/lib/modules/client/common/error.ts +10 -0
- package/src/lib/modules/client/common/index.ts +6 -5
- package/src/lib/modules/client/common/markdown.ts +22 -1
- package/src/lib/modules/client/common/native-bridge.ts +28 -20
- package/src/lib/modules/client/common/time.ts +13 -11
- package/src/lib/modules/client/terminal/ChatView.svelte +354 -74
- package/src/lib/modules/client/terminal/CommandPalette.svelte +3 -2
- package/src/lib/modules/client/terminal/ConnectionStatus.svelte +7 -1
- package/src/lib/modules/client/terminal/LaunchSheet.svelte +147 -84
- package/src/lib/modules/client/terminal/QuickKeys.svelte +3 -1
- package/src/lib/modules/client/terminal/ShortcutsHelp.svelte +2 -5
- package/src/lib/modules/client/terminal/keyboard-shortcuts.ts +27 -24
- package/src/lib/modules/client/terminal/xterm-wrapper.ts +74 -45
- package/src/lib/modules/server/apn/library-apns.ts +3 -2
- package/src/lib/modules/server/apn/notification-history.ts +2 -13
- package/src/lib/modules/server/apn/notification-sessions.ts +3 -13
- package/src/lib/modules/server/apn/pending-requests.ts +3 -8
- package/src/lib/modules/server/apn/types.ts +5 -4
- package/src/lib/modules/server/cli/index.ts +3 -2
- package/src/lib/modules/server/fcm/fcm-service.ts +8 -6
- package/src/lib/modules/server/sessions/jsonl-parser.ts +3 -3
- package/src/lib/modules/server/sessions/jsonl-reader.ts +86 -26
- package/src/lib/modules/server/sessions/opencode-db-path.ts +26 -0
- package/src/lib/modules/server/sessions/opencode-reader.ts +13 -15
- package/src/lib/modules/server/sessions/process-detector.ts +103 -0
- package/src/lib/modules/server/sessions/types.ts +11 -22
- package/src/lib/modules/server/terminal/holder-client.ts +272 -248
- package/src/lib/modules/server/terminal/opencode-watcher.ts +547 -556
- package/src/lib/modules/server/terminal/pty-holder.cjs +37 -8
- package/src/lib/modules/server/terminal/pty-manager.ts +157 -115
- package/src/lib/modules/server/terminal/session-watcher.ts +6 -4
- package/src/lib/modules/server/terminal/terminal-store.ts +131 -128
- package/src/lib/modules/server/utils/error.ts +9 -0
- package/src/lib/modules/server/ws/events-handler.ts +12 -6
- package/src/lib/modules/server/ws/keepalive.ts +86 -69
- package/src/lib/modules/server/ws/server.ts +43 -37
- package/src/lib/modules/server/ws/session-handler.ts +332 -147
- package/src/lib/modules/server/ws/terminal-handler.ts +29 -17
- package/src/lib/modules/server/ws/ticket-store.ts +29 -26
- package/src/lib/theme.css +30 -0
- package/src/lib/types/config.ts +1 -6
- package/src/routes/+error.svelte +94 -0
- package/src/routes/+layout.svelte +66 -31
- package/src/routes/+page.svelte +25 -22
- package/src/routes/api/debug/+server.ts +3 -1
- package/src/routes/api/device-token/+server.ts +60 -60
- package/src/routes/api/health/+server.ts +81 -73
- package/src/routes/api/notify/+server.ts +115 -68
- package/src/routes/api/qr-config/+server.ts +30 -32
- package/src/routes/api/response/+server.ts +9 -4
- package/src/routes/api/sessions/+server.ts +15 -5
- package/src/routes/api/sessions/connect/+server.ts +125 -0
- package/src/routes/api/sessions/detect/+server.ts +27 -0
- package/src/routes/api/terminals/+server.ts +26 -24
- package/src/routes/api/terminals/[id]/+server.ts +13 -7
- package/src/routes/api/terminals/[id]/paste-image/+server.ts +54 -52
- package/src/routes/api/terminals/[id]/resize/+server.ts +6 -3
- package/src/routes/api/webhook/+server.ts +8 -10
- package/src/routes/api/ws-status/+server.ts +7 -5
- package/src/routes/api/ws-ticket/+server.ts +42 -41
- package/src/routes/config/+page.svelte +149 -75
- package/src/routes/project/+page.svelte +165 -35
- package/src/routes/session/[id]/+page.svelte +479 -283
- package/src/routes/terminals/+page.svelte +58 -45
- package/src/routes/terminals/[id]/+page.svelte +223 -91
- package/build/client/_app/immutable/assets/0.CM9Hl6d-.css.br +0 -0
- package/build/client/_app/immutable/assets/0.CM9Hl6d-.css.gz +0 -0
- package/build/client/_app/immutable/assets/3.C0uFg0IS.css +0 -1
- package/build/client/_app/immutable/assets/3.C0uFg0IS.css.br +0 -0
- package/build/client/_app/immutable/assets/3.C0uFg0IS.css.gz +0 -0
- package/build/client/_app/immutable/assets/4.cJuCkJKZ.css +0 -1
- package/build/client/_app/immutable/assets/4.cJuCkJKZ.css.br +0 -0
- package/build/client/_app/immutable/assets/4.cJuCkJKZ.css.gz +0 -0
- package/build/client/_app/immutable/assets/5.DRjApZQW.css +0 -1
- package/build/client/_app/immutable/assets/5.DRjApZQW.css.br +0 -0
- package/build/client/_app/immutable/assets/5.DRjApZQW.css.gz +0 -0
- package/build/client/_app/immutable/assets/6.AraZrY8I.css +0 -1
- package/build/client/_app/immutable/assets/6.AraZrY8I.css.br +0 -0
- package/build/client/_app/immutable/assets/6.AraZrY8I.css.gz +0 -0
- package/build/client/_app/immutable/assets/7.BCJ1IuMx.css +0 -1
- package/build/client/_app/immutable/assets/7.BCJ1IuMx.css.br +0 -0
- package/build/client/_app/immutable/assets/7.BCJ1IuMx.css.gz +0 -0
- package/build/client/_app/immutable/assets/ChatView.CsdBAOKx.css +0 -1
- package/build/client/_app/immutable/assets/ChatView.CsdBAOKx.css.br +0 -0
- package/build/client/_app/immutable/assets/ChatView.CsdBAOKx.css.gz +0 -0
- package/build/client/_app/immutable/assets/markdown.B0b5w2tq.css +0 -1
- package/build/client/_app/immutable/assets/markdown.B0b5w2tq.css.br +0 -0
- package/build/client/_app/immutable/assets/markdown.B0b5w2tq.css.gz +0 -0
- package/build/client/_app/immutable/chunks/BNJphC1q.js +0 -56
- package/build/client/_app/immutable/chunks/BNJphC1q.js.br +0 -0
- package/build/client/_app/immutable/chunks/BNJphC1q.js.gz +0 -0
- package/build/client/_app/immutable/chunks/Bvk7mfPM.js +0 -1
- package/build/client/_app/immutable/chunks/Bvk7mfPM.js.br +0 -0
- package/build/client/_app/immutable/chunks/Bvk7mfPM.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CAokzuPQ.js +0 -1
- package/build/client/_app/immutable/chunks/CAokzuPQ.js.br +0 -0
- package/build/client/_app/immutable/chunks/CAokzuPQ.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CGLrx-H5.js +0 -1
- package/build/client/_app/immutable/chunks/CGLrx-H5.js.br +0 -0
- package/build/client/_app/immutable/chunks/CGLrx-H5.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CgCpWzEA.js +0 -1
- package/build/client/_app/immutable/chunks/CgCpWzEA.js.br +0 -0
- package/build/client/_app/immutable/chunks/CgCpWzEA.js.gz +0 -0
- package/build/client/_app/immutable/chunks/Cjwk_cGO.js +0 -6
- package/build/client/_app/immutable/chunks/Cjwk_cGO.js.br +0 -0
- package/build/client/_app/immutable/chunks/Cjwk_cGO.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CtQ8EED1.js +0 -11
- package/build/client/_app/immutable/chunks/CtQ8EED1.js.br +0 -0
- package/build/client/_app/immutable/chunks/CtQ8EED1.js.gz +0 -0
- package/build/client/_app/immutable/chunks/DERQCisl.js +0 -1
- package/build/client/_app/immutable/chunks/DERQCisl.js.br +0 -0
- package/build/client/_app/immutable/chunks/DERQCisl.js.gz +0 -0
- package/build/client/_app/immutable/chunks/DKrg8TQs.js +0 -1
- package/build/client/_app/immutable/chunks/DKrg8TQs.js.br +0 -0
- package/build/client/_app/immutable/chunks/DKrg8TQs.js.gz +0 -0
- package/build/client/_app/immutable/chunks/Dkkpz_4D.js +0 -126
- package/build/client/_app/immutable/chunks/Dkkpz_4D.js.br +0 -0
- package/build/client/_app/immutable/chunks/Dkkpz_4D.js.gz +0 -0
- package/build/client/_app/immutable/chunks/DoczjQhA.js +0 -1
- package/build/client/_app/immutable/chunks/DoczjQhA.js.br +0 -0
- package/build/client/_app/immutable/chunks/DoczjQhA.js.gz +0 -0
- package/build/client/_app/immutable/chunks/RpcNruLP.js +0 -2
- package/build/client/_app/immutable/chunks/RpcNruLP.js.br +0 -0
- package/build/client/_app/immutable/chunks/RpcNruLP.js.gz +0 -0
- package/build/client/_app/immutable/chunks/a-St0Zwo.js +0 -1
- package/build/client/_app/immutable/chunks/a-St0Zwo.js.br +0 -0
- package/build/client/_app/immutable/chunks/a-St0Zwo.js.gz +0 -0
- package/build/client/_app/immutable/chunks/bo70OQUZ.js +0 -1
- package/build/client/_app/immutable/chunks/bo70OQUZ.js.br +0 -0
- package/build/client/_app/immutable/chunks/bo70OQUZ.js.gz +0 -0
- package/build/client/_app/immutable/entry/app.QvGgdvTI.js +0 -2
- package/build/client/_app/immutable/entry/app.QvGgdvTI.js.br +0 -0
- package/build/client/_app/immutable/entry/app.QvGgdvTI.js.gz +0 -0
- package/build/client/_app/immutable/entry/start.BntDNRMC.js +0 -1
- package/build/client/_app/immutable/entry/start.BntDNRMC.js.br +0 -0
- package/build/client/_app/immutable/entry/start.BntDNRMC.js.gz +0 -0
- package/build/client/_app/immutable/nodes/0.CzkdvJ7j.js +0 -1
- package/build/client/_app/immutable/nodes/0.CzkdvJ7j.js.br +0 -0
- package/build/client/_app/immutable/nodes/0.CzkdvJ7j.js.gz +0 -0
- package/build/client/_app/immutable/nodes/1.MG1QhfrI.js +0 -1
- package/build/client/_app/immutable/nodes/1.MG1QhfrI.js.br +0 -0
- package/build/client/_app/immutable/nodes/1.MG1QhfrI.js.gz +0 -0
- package/build/client/_app/immutable/nodes/2.B4MlOSh6.js +0 -1
- package/build/client/_app/immutable/nodes/2.B4MlOSh6.js.br +0 -0
- package/build/client/_app/immutable/nodes/2.B4MlOSh6.js.gz +0 -0
- package/build/client/_app/immutable/nodes/3.DIwYkjDn.js +0 -3
- package/build/client/_app/immutable/nodes/3.DIwYkjDn.js.br +0 -0
- package/build/client/_app/immutable/nodes/3.DIwYkjDn.js.gz +0 -0
- package/build/client/_app/immutable/nodes/4.D-cIe70D.js +0 -1
- package/build/client/_app/immutable/nodes/4.D-cIe70D.js.br +0 -0
- package/build/client/_app/immutable/nodes/4.D-cIe70D.js.gz +0 -0
- package/build/client/_app/immutable/nodes/5.D7zPRe3L.js +0 -1
- package/build/client/_app/immutable/nodes/5.D7zPRe3L.js.br +0 -0
- package/build/client/_app/immutable/nodes/5.D7zPRe3L.js.gz +0 -0
- package/build/client/_app/immutable/nodes/6.BB7QE48r.js +0 -2
- package/build/client/_app/immutable/nodes/6.BB7QE48r.js.br +0 -0
- package/build/client/_app/immutable/nodes/6.BB7QE48r.js.gz +0 -0
- package/build/client/_app/immutable/nodes/7.D8mqsrZG.js +0 -2
- package/build/client/_app/immutable/nodes/7.D8mqsrZG.js.br +0 -0
- package/build/client/_app/immutable/nodes/7.D8mqsrZG.js.gz +0 -0
- package/build/client/manifest.webmanifest +0 -1
- package/build/client/registerSW.js +0 -1
- package/build/client/registerSW.js.br +0 -0
- package/build/client/registerSW.js.gz +0 -0
- package/build/client/sw.js +0 -222
- package/build/client/sw.js.br +0 -0
- package/build/client/sw.js.gz +0 -0
- package/build/client/workbox-5119daf5.js +0 -3395
- package/build/client/workbox-5119daf5.js.br +0 -0
- package/build/client/workbox-5119daf5.js.gz +0 -0
- package/build/server/chunks/0-q2IUp76Y.js +0 -9
- package/build/server/chunks/0-q2IUp76Y.js.map +0 -1
- package/build/server/chunks/1-CU50G5wZ.js +0 -9
- package/build/server/chunks/1-CU50G5wZ.js.map +0 -1
- package/build/server/chunks/2-D01t9s8T.js +0 -9
- package/build/server/chunks/2-D01t9s8T.js.map +0 -1
- package/build/server/chunks/3-5PUQ04wC.js +0 -9
- package/build/server/chunks/3-5PUQ04wC.js.map +0 -1
- package/build/server/chunks/4-e7gywnSG.js +0 -9
- package/build/server/chunks/4-e7gywnSG.js.map +0 -1
- package/build/server/chunks/5-CA1SA6KZ.js +0 -9
- package/build/server/chunks/5-CA1SA6KZ.js.map +0 -1
- package/build/server/chunks/6-71H221sV.js +0 -9
- package/build/server/chunks/6-71H221sV.js.map +0 -1
- package/build/server/chunks/7-Bo-vmdyz.js +0 -9
- package/build/server/chunks/7-Bo-vmdyz.js.map +0 -1
- package/build/server/chunks/_layout.svelte-SFHOxs74.js +0 -132
- package/build/server/chunks/_layout.svelte-SFHOxs74.js.map +0 -1
- package/build/server/chunks/_page.svelte-B4w-2wD-.js +0 -120
- package/build/server/chunks/_page.svelte-B4w-2wD-.js.map +0 -1
- package/build/server/chunks/_page.svelte-B_qAXjkh.js +0 -213
- package/build/server/chunks/_page.svelte-B_qAXjkh.js.map +0 -1
- package/build/server/chunks/_page.svelte-CsF1_TRG.js +0 -50
- package/build/server/chunks/_page.svelte-CsF1_TRG.js.map +0 -1
- package/build/server/chunks/_page.svelte-DJC6U-P0.js +0 -68
- package/build/server/chunks/_page.svelte-DJC6U-P0.js.map +0 -1
- package/build/server/chunks/_page.svelte-DQ6HBtsz.js +0 -407
- package/build/server/chunks/_page.svelte-DQ6HBtsz.js.map +0 -1
- package/build/server/chunks/_page.svelte-LbhhjP21.js +0 -148
- package/build/server/chunks/_page.svelte-LbhhjP21.js.map +0 -1
- package/build/server/chunks/_server.ts-BL2FGb5Z.js.map +0 -1
- package/build/server/chunks/_server.ts-BgdjBZco.js.map +0 -1
- package/build/server/chunks/_server.ts-BihKSdj_.js.map +0 -1
- package/build/server/chunks/_server.ts-BjOJsoy4.js.map +0 -1
- package/build/server/chunks/_server.ts-C29xzfaw.js.map +0 -1
- package/build/server/chunks/_server.ts-CPa6DgIt.js.map +0 -1
- package/build/server/chunks/_server.ts-CbDRDIoP.js.map +0 -1
- package/build/server/chunks/_server.ts-Cl1OEWL4.js +0 -54
- package/build/server/chunks/_server.ts-Cl1OEWL4.js.map +0 -1
- package/build/server/chunks/_server.ts-ColfDHW8.js.map +0 -1
- package/build/server/chunks/_server.ts-Cv_OrRuL.js.map +0 -1
- package/build/server/chunks/_server.ts-DRVbgm6k.js.map +0 -1
- package/build/server/chunks/_server.ts-DfajWaqh.js.map +0 -1
- package/build/server/chunks/client-CxCatAKr.js +0 -255
- package/build/server/chunks/client-CxCatAKr.js.map +0 -1
- package/build/server/chunks/error.svelte-BqdwMWdK.js +0 -26
- package/build/server/chunks/error.svelte-BqdwMWdK.js.map +0 -1
- package/build/server/chunks/exports-CJ0Q5XmL.js.map +0 -1
- package/build/server/chunks/index2-DAxIoAO-.js +0 -36
- package/build/server/chunks/index2-DAxIoAO-.js.map +0 -1
- package/build/server/chunks/jsonl-parser-dmZU_Hyu.js +0 -137
- package/build/server/chunks/jsonl-parser-dmZU_Hyu.js.map +0 -1
- package/build/server/chunks/library-apns-BHxLmuIx.js.map +0 -1
- package/build/server/chunks/markdown-Bxrl3cCF.js +0 -1241
- package/build/server/chunks/markdown-Bxrl3cCF.js.map +0 -1
- package/build/server/chunks/pty-manager-C0FhBiVq.js.map +0 -1
- package/build/server/chunks/stores-D0HorpgL.js +0 -36
- 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 [
|
|
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 -=
|
|
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 {
|
|
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 (
|
|
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 =
|
|
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
|
|
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:
|
|
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(() => {
|
|
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) {
|
|
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') {
|
|
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) {
|
|
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) {
|
|
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) {
|
|
374
|
-
|
|
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) {
|
|
458
|
-
|
|
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') {
|
|
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) {
|
|
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) {
|
|
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(
|
|
615
|
-
|
|
616
|
-
|
|
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
|
|
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
|
|
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
|
|
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) {
|
|
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) {
|
|
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 || '',
|
|
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)) {
|
|
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
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
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
|
|
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
|
|
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(
|
|
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
|
-
|
|
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) {
|
|
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
|
-
|
|
319
|
-
|
|
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;
|