@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
|
@@ -7,79 +7,79 @@ import { join } from 'path';
|
|
|
7
7
|
|
|
8
8
|
import type { RequestHandler } from './$types';
|
|
9
9
|
|
|
10
|
-
interface DeviceTokens {
|
|
11
|
-
android?: string;
|
|
12
|
-
ios?: string;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
interface DeviceTokenRequest {
|
|
16
|
-
bundleId?: string;
|
|
17
|
-
deviceToken?: string;
|
|
18
|
-
platform: string;
|
|
19
|
-
token?: string;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
10
|
const TOKENS_DIR = join(homedir(), '.shooter');
|
|
23
11
|
const TOKENS_FILE = join(TOKENS_DIR, 'device-tokens.json');
|
|
24
12
|
|
|
25
|
-
function readTokens():
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
13
|
+
function readTokens(): { android?: string; ios?: string } {
|
|
14
|
+
try {
|
|
15
|
+
if (existsSync(TOKENS_FILE)) {
|
|
16
|
+
const parsed: unknown = JSON.parse(readFileSync(TOKENS_FILE, 'utf-8'));
|
|
17
|
+
// Guard against valid-but-wrong JSON (null, array, number, string)
|
|
18
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
19
|
+
return {};
|
|
20
|
+
}
|
|
21
|
+
return parsed as { android?: string; ios?: string };
|
|
22
|
+
}
|
|
23
|
+
} catch {
|
|
24
|
+
// Corrupt file -- start fresh
|
|
25
|
+
}
|
|
26
|
+
return {};
|
|
34
27
|
}
|
|
35
28
|
|
|
36
|
-
function writeTokens(tokens:
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
29
|
+
function writeTokens(tokens: { android?: string; ios?: string }): void {
|
|
30
|
+
if (!existsSync(TOKENS_DIR)) {
|
|
31
|
+
mkdirSync(TOKENS_DIR, { mode: 0o700, recursive: true });
|
|
32
|
+
}
|
|
33
|
+
writeFileSync(TOKENS_FILE, JSON.stringify(tokens, null, 2), { encoding: 'utf-8', mode: 0o600 });
|
|
41
34
|
}
|
|
42
35
|
|
|
43
36
|
export const POST: RequestHandler = async ({ request }) => {
|
|
44
|
-
|
|
45
|
-
|
|
37
|
+
const authError = validateAuth(request);
|
|
38
|
+
if (authError) {
|
|
39
|
+
return authError;
|
|
40
|
+
}
|
|
46
41
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
42
|
+
let body: { bundleId?: string; deviceToken?: string; platform: string; token?: string };
|
|
43
|
+
try {
|
|
44
|
+
const parsed: unknown = await request.json();
|
|
45
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
46
|
+
return json({ error: 'Invalid JSON body: expected an object' }, { status: 400 });
|
|
47
|
+
}
|
|
48
|
+
body = parsed as typeof body;
|
|
49
|
+
} catch {
|
|
50
|
+
return json({ error: 'Invalid JSON body' }, { status: 400 });
|
|
51
|
+
}
|
|
53
52
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
53
|
+
const platform = body.platform;
|
|
54
|
+
if (!platform || (platform !== 'ios' && platform !== 'android')) {
|
|
55
|
+
return json(
|
|
56
|
+
{ error: 'Missing or invalid platform (must be "ios" or "android")' },
|
|
57
|
+
{ status: 400 }
|
|
58
|
+
);
|
|
59
|
+
}
|
|
61
60
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
61
|
+
// iOS sends "deviceToken", Android sends "token"
|
|
62
|
+
const rawToken = body.deviceToken || body.token;
|
|
63
|
+
if (!rawToken || typeof rawToken !== 'string' || rawToken.trim().length === 0) {
|
|
64
|
+
return json({ error: 'Missing device token (deviceToken or token)' }, { status: 400 });
|
|
65
|
+
}
|
|
66
|
+
const token = rawToken.trim();
|
|
67
67
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
68
|
+
// Persist to ~/.shooter/device-tokens.json
|
|
69
|
+
const tokens = readTokens();
|
|
70
|
+
tokens[platform] = token;
|
|
71
|
+
writeTokens(tokens);
|
|
72
72
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
73
|
+
// Update in-memory env so APNs can use it immediately (iOS is the primary APNs target)
|
|
74
|
+
if (platform === 'ios') {
|
|
75
|
+
(env as Record<string, string>).DEVICE_TOKEN = token;
|
|
76
|
+
}
|
|
77
77
|
|
|
78
|
-
|
|
78
|
+
console.log(`[device-token] Registered ${platform} token (length: ${token.length})`);
|
|
79
79
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
80
|
+
return json({
|
|
81
|
+
platform,
|
|
82
|
+
success: true,
|
|
83
|
+
timestamp: new Date().toISOString(),
|
|
84
|
+
});
|
|
85
85
|
};
|
|
@@ -1,40 +1,27 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
FCMConfiguration,
|
|
3
|
+
HealthChecks,
|
|
4
|
+
HealthConfiguration,
|
|
5
|
+
HealthStatus,
|
|
6
|
+
} from '$generated/types';
|
|
7
|
+
|
|
1
8
|
import { env } from '$env/dynamic/private';
|
|
2
9
|
import { validateAuth } from '$lib/modules/server/auth';
|
|
3
10
|
import { json } from '@sveltejs/kit';
|
|
11
|
+
import { readFileSync } from 'fs';
|
|
12
|
+
import { join } from 'path';
|
|
4
13
|
|
|
5
14
|
import type { RequestHandler } from './$types';
|
|
6
15
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
hasAPNsConfig: boolean;
|
|
17
|
-
hasBundleId: boolean;
|
|
18
|
-
hasDeviceToken: boolean;
|
|
19
|
-
hasFCMConfig: boolean;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
interface HealthConfiguration {
|
|
23
|
-
apnsKeyId: null | string;
|
|
24
|
-
bundleId: null | string;
|
|
25
|
-
deviceTokenLength: number;
|
|
26
|
-
fcm: FCMConfiguration;
|
|
27
|
-
production: boolean;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
interface HealthResponse {
|
|
31
|
-
checks: HealthChecks;
|
|
32
|
-
configuration: HealthConfiguration;
|
|
33
|
-
environment: string;
|
|
34
|
-
status: 'degraded' | 'healthy';
|
|
35
|
-
timestamp: string;
|
|
36
|
-
version: string;
|
|
37
|
-
}
|
|
16
|
+
const PKG_VERSION: string = (() => {
|
|
17
|
+
const root = process.env.SHOOTER_PKG_ROOT || process.cwd();
|
|
18
|
+
try {
|
|
19
|
+
const pkg = JSON.parse(readFileSync(join(root, 'package.json'), 'utf-8')) as { version?: string };
|
|
20
|
+
return pkg.version || 'unknown';
|
|
21
|
+
} catch {
|
|
22
|
+
return 'unknown';
|
|
23
|
+
}
|
|
24
|
+
})();
|
|
38
25
|
|
|
39
26
|
export const GET: RequestHandler = ({ request, url }) => {
|
|
40
27
|
// Basic status check is public (used by layout status badge).
|
|
@@ -42,58 +29,79 @@ export const GET: RequestHandler = ({ request, url }) => {
|
|
|
42
29
|
const wantsDetails = url.searchParams.get('details') === 'true';
|
|
43
30
|
if (wantsDetails) {
|
|
44
31
|
const authError = validateAuth(request);
|
|
45
|
-
if (authError)
|
|
32
|
+
if (authError) {
|
|
33
|
+
return authError;
|
|
34
|
+
}
|
|
46
35
|
}
|
|
47
36
|
|
|
48
|
-
const hasProjectId = !!env.FCM_PROJECT_ID;
|
|
49
|
-
const hasClientEmail = !!env.FCM_CLIENT_EMAIL;
|
|
50
|
-
const hasPrivateKey = !!env.FCM_PRIVATE_KEY;
|
|
37
|
+
const hasProjectId = !!env.FCM_PROJECT_ID?.trim();
|
|
38
|
+
const hasClientEmail = !!env.FCM_CLIENT_EMAIL?.trim();
|
|
39
|
+
const hasPrivateKey = !!env.FCM_PRIVATE_KEY?.trim();
|
|
40
|
+
|
|
41
|
+
const checks: HealthChecks = {
|
|
42
|
+
hasApiKey: !!env.API_KEY?.trim(),
|
|
43
|
+
hasAPNsConfig: !!(env.APNS_KEY_ID?.trim() && env.APNS_TEAM_ID?.trim() && env.APNS_KEY?.trim()),
|
|
44
|
+
hasBundleId: !!env.APNS_BUNDLE_ID?.trim(),
|
|
45
|
+
hasDeviceToken: !!env.DEVICE_TOKEN?.trim(),
|
|
46
|
+
hasFCMConfig: hasProjectId && hasClientEmail && hasPrivateKey,
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const fcm: FCMConfiguration = {
|
|
50
|
+
configured: hasProjectId && hasClientEmail && hasPrivateKey,
|
|
51
|
+
hasClientEmail,
|
|
52
|
+
hasPrivateKey,
|
|
53
|
+
hasProjectId,
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const configuration: HealthConfiguration = {
|
|
57
|
+
apnsKeyId: env.APNS_KEY_ID ? `${env.APNS_KEY_ID.substring(0, 4)}...` : '',
|
|
58
|
+
bundleId: env.APNS_BUNDLE_ID || '',
|
|
59
|
+
deviceTokenLength: env.DEVICE_TOKEN ? env.DEVICE_TOKEN.length : 0,
|
|
60
|
+
fcm,
|
|
61
|
+
production: env.APNS_PRODUCTION === 'true',
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
// Collect warnings for optional features that are not configured.
|
|
65
|
+
// These are informational — the server is still fully functional for
|
|
66
|
+
// terminals and sessions without push notification support.
|
|
67
|
+
const warnings: string[] = [];
|
|
51
68
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
configuration:
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
},
|
|
74
|
-
production: env.APNS_PRODUCTION === 'true',
|
|
75
|
-
},
|
|
69
|
+
if (!checks.hasAPNsConfig || !checks.hasBundleId) {
|
|
70
|
+
warnings.push('APNs not configured — iOS push notifications disabled');
|
|
71
|
+
}
|
|
72
|
+
if (!checks.hasDeviceToken) {
|
|
73
|
+
warnings.push('No device token set — push notifications have no target');
|
|
74
|
+
}
|
|
75
|
+
if (!checks.hasFCMConfig) {
|
|
76
|
+
warnings.push('FCM not configured — Android push notifications disabled');
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const health: {
|
|
80
|
+
checks: HealthChecks;
|
|
81
|
+
configuration: HealthConfiguration;
|
|
82
|
+
environment: string;
|
|
83
|
+
status: HealthStatus;
|
|
84
|
+
timestamp: string;
|
|
85
|
+
version: string;
|
|
86
|
+
warnings: string[];
|
|
87
|
+
} = {
|
|
88
|
+
checks,
|
|
89
|
+
configuration,
|
|
76
90
|
environment: env.NODE_ENV || 'development',
|
|
77
91
|
status: 'healthy',
|
|
78
92
|
timestamp: new Date().toISOString(),
|
|
79
|
-
version:
|
|
93
|
+
version: PKG_VERSION,
|
|
94
|
+
warnings,
|
|
80
95
|
};
|
|
81
96
|
|
|
82
|
-
//
|
|
83
|
-
//
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
health.checks.hasAPNsConfig,
|
|
87
|
-
health.checks.hasBundleId,
|
|
88
|
-
];
|
|
89
|
-
|
|
90
|
-
if (criticalChecks.some((check) => !check)) {
|
|
91
|
-
health.status = 'degraded';
|
|
92
|
-
}
|
|
97
|
+
// Only mark as degraded for actual system-level problems
|
|
98
|
+
// (e.g., DB unreachable, critical services down).
|
|
99
|
+
// Missing push-notification config is NOT a degraded state — it just
|
|
100
|
+
// means notifications are disabled, which is fine for terminal/session use.
|
|
93
101
|
|
|
94
|
-
// Public response: status
|
|
102
|
+
// Public response: status + warnings. Authenticated: full details.
|
|
95
103
|
if (!wantsDetails) {
|
|
96
|
-
return json({ status: health.status, timestamp: health.timestamp });
|
|
104
|
+
return json({ status: health.status, timestamp: health.timestamp, version: health.version, warnings: health.warnings });
|
|
97
105
|
}
|
|
98
106
|
|
|
99
107
|
return json(health);
|
|
@@ -1,34 +1,16 @@
|
|
|
1
|
+
import type { NotificationData } from '$generated/types';
|
|
2
|
+
|
|
1
3
|
import { env } from '$env/dynamic/private';
|
|
2
4
|
import { LibraryAPNsService } from '$lib/modules/server/apn/library-apns';
|
|
3
5
|
import { addNotification, getNotifications } from '$lib/modules/server/apn/notification-history';
|
|
4
6
|
import { createPendingRequest } from '$lib/modules/server/apn/pending-requests';
|
|
5
7
|
import { validateAuth } from '$lib/modules/server/auth';
|
|
6
8
|
import { isFCMConfigured, sendFCMNotification } from '$lib/modules/server/fcm/fcm-service.js';
|
|
9
|
+
import { toErrorMessage } from '$lib/modules/server/utils/error';
|
|
7
10
|
import { json } from '@sveltejs/kit';
|
|
8
11
|
|
|
9
12
|
import type { RequestHandler } from './$types';
|
|
10
13
|
|
|
11
|
-
interface FilterResult {
|
|
12
|
-
reason: string;
|
|
13
|
-
send: boolean;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
interface NotificationData {
|
|
17
|
-
[key: string]: unknown;
|
|
18
|
-
category?: string;
|
|
19
|
-
files?: string;
|
|
20
|
-
project?: string;
|
|
21
|
-
source?: string;
|
|
22
|
-
tool?: string;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
interface NotificationRequest {
|
|
26
|
-
data?: NotificationData;
|
|
27
|
-
message: string;
|
|
28
|
-
title: string;
|
|
29
|
-
waitForResponse?: boolean;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
14
|
// Singleton APNs client - reuses HTTP/2 connection across requests
|
|
33
15
|
let apnsSingleton: LibraryAPNsService | null = null;
|
|
34
16
|
function getAPNsClient(): LibraryAPNsService {
|
|
@@ -42,12 +24,11 @@ function getAPNsClient(): LibraryAPNsService {
|
|
|
42
24
|
const notificationCache = new Map<string, number>();
|
|
43
25
|
const DEDUP_WINDOW = 10000; // 10 seconds deduplication window
|
|
44
26
|
|
|
45
|
-
// 🎯 INTELLIGENT NOTIFICATION FILTERING
|
|
46
27
|
function intelligentNotificationFilter(
|
|
47
28
|
title: string,
|
|
48
29
|
message: string,
|
|
49
30
|
data?: NotificationData
|
|
50
|
-
):
|
|
31
|
+
): { reason: string; send: boolean } {
|
|
51
32
|
const source = data?.source || 'unknown';
|
|
52
33
|
|
|
53
34
|
// Check for duplicate notifications first
|
|
@@ -137,7 +118,7 @@ function isDuplicateNotification(title: string, message: string, data?: Notifica
|
|
|
137
118
|
}
|
|
138
119
|
}
|
|
139
120
|
|
|
140
|
-
// Do NOT record here
|
|
121
|
+
// Do NOT record here -- caller must call recordNotification() after
|
|
141
122
|
// successful delivery to avoid cache poisoning on send failure.
|
|
142
123
|
return false;
|
|
143
124
|
}
|
|
@@ -155,14 +136,35 @@ export const POST: RequestHandler = async ({ request }) => {
|
|
|
155
136
|
|
|
156
137
|
// Validate API key using timing-safe comparison
|
|
157
138
|
const authError = validateAuth(request);
|
|
158
|
-
if (authError)
|
|
139
|
+
if (authError) {
|
|
140
|
+
return authError;
|
|
141
|
+
}
|
|
159
142
|
|
|
160
143
|
// Parse request body
|
|
161
|
-
const
|
|
162
|
-
const { data, message, title, waitForResponse } = body;
|
|
144
|
+
const rawBody: unknown = await request.json();
|
|
163
145
|
|
|
164
|
-
if (!
|
|
165
|
-
return json({ error: '
|
|
146
|
+
if (!rawBody || typeof rawBody !== 'object' || Array.isArray(rawBody)) {
|
|
147
|
+
return json({ error: 'Request body must be a JSON object' }, { status: 400 });
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const body = rawBody as Record<string, unknown>;
|
|
151
|
+
const data = body.data as NotificationData | undefined;
|
|
152
|
+
const requestDeviceToken = body.deviceToken as string | undefined;
|
|
153
|
+
const message = body.message as string;
|
|
154
|
+
const title = body.title as string;
|
|
155
|
+
|
|
156
|
+
// Coerce skipPush and waitForResponse to booleans — string "false" would
|
|
157
|
+
// be truthy so we require an actual boolean, defaulting to false otherwise.
|
|
158
|
+
const skipPush = typeof body.skipPush === 'boolean' ? body.skipPush : false;
|
|
159
|
+
const waitForResponse =
|
|
160
|
+
typeof body.waitForResponse === 'boolean' ? body.waitForResponse : false;
|
|
161
|
+
|
|
162
|
+
if (!title || typeof title !== 'string' || !message || typeof message !== 'string') {
|
|
163
|
+
return json({ error: 'Title and message are required and must be strings' }, { status: 400 });
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (requestDeviceToken !== undefined && typeof requestDeviceToken !== 'string') {
|
|
167
|
+
return json({ error: 'deviceToken must be a string' }, { status: 400 });
|
|
166
168
|
}
|
|
167
169
|
|
|
168
170
|
// Smart notification filtering
|
|
@@ -173,17 +175,17 @@ export const POST: RequestHandler = async ({ request }) => {
|
|
|
173
175
|
|
|
174
176
|
if (!shouldSendNotification.send) {
|
|
175
177
|
addNotification({
|
|
176
|
-
category: data?.category,
|
|
177
|
-
data: data as Record<string, unknown
|
|
178
|
+
category: data?.category ?? null,
|
|
179
|
+
data: (data as Record<string, unknown>) ?? null,
|
|
178
180
|
error: shouldSendNotification.reason,
|
|
179
181
|
id: canonicalRequestId,
|
|
180
182
|
message,
|
|
181
|
-
project: data?.project,
|
|
182
|
-
source: data?.source,
|
|
183
|
+
project: data?.project ?? null,
|
|
184
|
+
source: data?.source ?? null,
|
|
183
185
|
status: 'filtered',
|
|
184
186
|
timestamp: new Date().toISOString(),
|
|
185
187
|
title,
|
|
186
|
-
tool: data?.tool,
|
|
188
|
+
tool: data?.tool ?? null,
|
|
187
189
|
});
|
|
188
190
|
|
|
189
191
|
return json({
|
|
@@ -194,6 +196,41 @@ export const POST: RequestHandler = async ({ request }) => {
|
|
|
194
196
|
});
|
|
195
197
|
}
|
|
196
198
|
|
|
199
|
+
// When skipPush is true, the caller wants to register a pending request
|
|
200
|
+
// (for bidirectional permission polling) without actually sending a push
|
|
201
|
+
// notification. This happens when WebSocket clients are connected and the
|
|
202
|
+
// events channel will broadcast the permission-requested event instead.
|
|
203
|
+
if (skipPush) {
|
|
204
|
+
if (waitForResponse) {
|
|
205
|
+
createPendingRequest(canonicalRequestId, {
|
|
206
|
+
sessionId: (data?.sessionId as string) || '',
|
|
207
|
+
toolInput: (data?.toolInput as Record<string, unknown>) || {},
|
|
208
|
+
toolName: (data?.toolName as string) || '',
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
addNotification({
|
|
213
|
+
category: data?.category ?? null,
|
|
214
|
+
data: (data as Record<string, unknown>) ?? null,
|
|
215
|
+
error: null,
|
|
216
|
+
id: canonicalRequestId,
|
|
217
|
+
message,
|
|
218
|
+
project: data?.project ?? null,
|
|
219
|
+
source: data?.source ?? null,
|
|
220
|
+
status: 'skipped',
|
|
221
|
+
timestamp: new Date().toISOString(),
|
|
222
|
+
title,
|
|
223
|
+
tool: data?.tool ?? null,
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
return json({
|
|
227
|
+
message: 'Push skipped (WebSocket clients connected)',
|
|
228
|
+
requestId: canonicalRequestId,
|
|
229
|
+
success: true,
|
|
230
|
+
timestamp: new Date().toISOString(),
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
|
|
197
234
|
// Build notification payload (shared between APNs and FCM)
|
|
198
235
|
const payload = {
|
|
199
236
|
badge: 1,
|
|
@@ -219,19 +256,23 @@ export const POST: RequestHandler = async ({ request }) => {
|
|
|
219
256
|
if (!isFCMConfigured()) {
|
|
220
257
|
return json(
|
|
221
258
|
{
|
|
222
|
-
details:
|
|
259
|
+
details:
|
|
260
|
+
'Missing FCM_PROJECT_ID, FCM_CLIENT_EMAIL, or FCM_PRIVATE_KEY environment variables',
|
|
223
261
|
error: 'FCM not configured',
|
|
224
262
|
},
|
|
225
263
|
{ status: 500 }
|
|
226
264
|
);
|
|
227
265
|
}
|
|
228
266
|
|
|
229
|
-
|
|
267
|
+
// Honor request-scoped deviceToken, falling back to environment variables.
|
|
268
|
+
const androidToken =
|
|
269
|
+
requestDeviceToken?.trim() || (env.ANDROID_DEVICE_TOKEN || env.DEVICE_TOKEN)?.trim();
|
|
230
270
|
|
|
231
271
|
if (!androidToken) {
|
|
232
272
|
return json(
|
|
233
273
|
{
|
|
234
|
-
details:
|
|
274
|
+
details:
|
|
275
|
+
'ANDROID_DEVICE_TOKEN or DEVICE_TOKEN environment variable is missing and no deviceToken in request body',
|
|
235
276
|
error: 'No device token configured',
|
|
236
277
|
},
|
|
237
278
|
{ status: 500 }
|
|
@@ -253,16 +294,17 @@ export const POST: RequestHandler = async ({ request }) => {
|
|
|
253
294
|
}
|
|
254
295
|
|
|
255
296
|
addNotification({
|
|
256
|
-
category: data?.category,
|
|
257
|
-
data: data as Record<string, unknown
|
|
297
|
+
category: data?.category ?? null,
|
|
298
|
+
data: (data as Record<string, unknown>) ?? null,
|
|
299
|
+
error: null,
|
|
258
300
|
id: canonicalRequestId,
|
|
259
301
|
message,
|
|
260
|
-
project: data?.project,
|
|
261
|
-
source: data?.source,
|
|
302
|
+
project: data?.project ?? null,
|
|
303
|
+
source: data?.source ?? null,
|
|
262
304
|
status: 'sent',
|
|
263
305
|
timestamp: new Date().toISOString(),
|
|
264
306
|
title,
|
|
265
|
-
tool: data?.tool,
|
|
307
|
+
tool: data?.tool ?? null,
|
|
266
308
|
});
|
|
267
309
|
|
|
268
310
|
return json({
|
|
@@ -276,17 +318,17 @@ export const POST: RequestHandler = async ({ request }) => {
|
|
|
276
318
|
console.error(`[notify] FCM delivery failed: ${fcmResult.error}`);
|
|
277
319
|
|
|
278
320
|
addNotification({
|
|
279
|
-
category: data?.category,
|
|
280
|
-
data: data as Record<string, unknown
|
|
281
|
-
error: fcmResult.error,
|
|
321
|
+
category: data?.category ?? null,
|
|
322
|
+
data: (data as Record<string, unknown>) ?? null,
|
|
323
|
+
error: fcmResult.error ?? null,
|
|
282
324
|
id: canonicalRequestId,
|
|
283
325
|
message,
|
|
284
|
-
project: data?.project,
|
|
285
|
-
source: data?.source,
|
|
326
|
+
project: data?.project ?? null,
|
|
327
|
+
source: data?.source ?? null,
|
|
286
328
|
status: 'failed',
|
|
287
329
|
timestamp: new Date().toISOString(),
|
|
288
330
|
title,
|
|
289
|
-
tool: data?.tool,
|
|
331
|
+
tool: data?.tool ?? null,
|
|
290
332
|
});
|
|
291
333
|
|
|
292
334
|
return json(
|
|
@@ -311,12 +353,15 @@ export const POST: RequestHandler = async ({ request }) => {
|
|
|
311
353
|
);
|
|
312
354
|
}
|
|
313
355
|
|
|
314
|
-
|
|
356
|
+
// Honor request-scoped deviceToken (e.g., from config page test),
|
|
357
|
+
// falling back to the server-wide environment variable.
|
|
358
|
+
const deviceToken = requestDeviceToken?.trim() || env.DEVICE_TOKEN?.trim();
|
|
315
359
|
|
|
316
360
|
if (!deviceToken) {
|
|
317
361
|
return json(
|
|
318
362
|
{
|
|
319
|
-
details:
|
|
363
|
+
details:
|
|
364
|
+
'DEVICE_TOKEN environment variable is missing and no deviceToken in request body',
|
|
320
365
|
error: 'No device token configured',
|
|
321
366
|
},
|
|
322
367
|
{ status: 500 }
|
|
@@ -340,16 +385,17 @@ export const POST: RequestHandler = async ({ request }) => {
|
|
|
340
385
|
}
|
|
341
386
|
|
|
342
387
|
addNotification({
|
|
343
|
-
category: data?.category,
|
|
344
|
-
data: data as Record<string, unknown
|
|
388
|
+
category: data?.category ?? null,
|
|
389
|
+
data: (data as Record<string, unknown>) ?? null,
|
|
390
|
+
error: null,
|
|
345
391
|
id: canonicalRequestId,
|
|
346
392
|
message,
|
|
347
|
-
project: data?.project,
|
|
348
|
-
source: data?.source,
|
|
393
|
+
project: data?.project ?? null,
|
|
394
|
+
source: data?.source ?? null,
|
|
349
395
|
status: 'sent',
|
|
350
396
|
timestamp: new Date().toISOString(),
|
|
351
397
|
title,
|
|
352
|
-
tool: data?.tool,
|
|
398
|
+
tool: data?.tool ?? null,
|
|
353
399
|
});
|
|
354
400
|
|
|
355
401
|
return json({
|
|
@@ -360,26 +406,26 @@ export const POST: RequestHandler = async ({ request }) => {
|
|
|
360
406
|
timestamp: new Date().toISOString(),
|
|
361
407
|
});
|
|
362
408
|
} catch (notificationError) {
|
|
363
|
-
const
|
|
364
|
-
console.error(`[notify] APNs delivery failed: ${
|
|
409
|
+
const notifErrMsg = toErrorMessage(notificationError);
|
|
410
|
+
console.error(`[notify] APNs delivery failed: ${notifErrMsg}`);
|
|
365
411
|
|
|
366
412
|
addNotification({
|
|
367
|
-
category: data?.category,
|
|
368
|
-
data: data as Record<string, unknown
|
|
369
|
-
error:
|
|
413
|
+
category: data?.category ?? null,
|
|
414
|
+
data: (data as Record<string, unknown>) ?? null,
|
|
415
|
+
error: notifErrMsg,
|
|
370
416
|
id: canonicalRequestId,
|
|
371
417
|
message,
|
|
372
|
-
project: data?.project,
|
|
373
|
-
source: data?.source,
|
|
418
|
+
project: data?.project ?? null,
|
|
419
|
+
source: data?.source ?? null,
|
|
374
420
|
status: 'failed',
|
|
375
421
|
timestamp: new Date().toISOString(),
|
|
376
422
|
title,
|
|
377
|
-
tool: data?.tool,
|
|
423
|
+
tool: data?.tool ?? null,
|
|
378
424
|
});
|
|
379
425
|
|
|
380
426
|
return json(
|
|
381
427
|
{
|
|
382
|
-
details:
|
|
428
|
+
details: notifErrMsg,
|
|
383
429
|
error: 'Failed to send notification',
|
|
384
430
|
requestId: canonicalRequestId,
|
|
385
431
|
timestamp: new Date().toISOString(),
|
|
@@ -389,11 +435,10 @@ export const POST: RequestHandler = async ({ request }) => {
|
|
|
389
435
|
}
|
|
390
436
|
}
|
|
391
437
|
} catch (error) {
|
|
392
|
-
|
|
393
|
-
console.error('Notification error:', err);
|
|
438
|
+
console.error('Notification error:', error);
|
|
394
439
|
return json(
|
|
395
440
|
{
|
|
396
|
-
details:
|
|
441
|
+
details: toErrorMessage(error),
|
|
397
442
|
error: 'Failed to send notification',
|
|
398
443
|
timestamp: new Date().toISOString(),
|
|
399
444
|
},
|
|
@@ -405,7 +450,9 @@ export const POST: RequestHandler = async ({ request }) => {
|
|
|
405
450
|
export const GET: RequestHandler = ({ request, url }) => {
|
|
406
451
|
// Validate API key using timing-safe comparison
|
|
407
452
|
const authError = validateAuth(request);
|
|
408
|
-
if (authError)
|
|
453
|
+
if (authError) {
|
|
454
|
+
return authError;
|
|
455
|
+
}
|
|
409
456
|
|
|
410
457
|
const limit = parseInt(url.searchParams.get('limit') || '50');
|
|
411
458
|
const notifications = getNotifications(limit);
|