@juspay/shooter 1.4.0 → 1.6.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 +15 -1
- package/README.md +132 -36
- package/bin/shooter.cjs +98 -29
- package/build/client/_app/immutable/assets/2.Dk9NfqnS.css +1 -0
- package/build/client/_app/immutable/assets/2.Dk9NfqnS.css.br +0 -0
- package/build/client/_app/immutable/assets/2.Dk9NfqnS.css.gz +0 -0
- package/build/client/_app/immutable/assets/3.DHxQoulp.css +1 -0
- package/build/client/_app/immutable/assets/3.DHxQoulp.css.br +0 -0
- package/build/client/_app/immutable/assets/3.DHxQoulp.css.gz +0 -0
- package/build/client/_app/immutable/assets/{3.DGDHCVnW.css → 4.D5l1JxgO.css} +1 -1
- package/build/client/_app/immutable/assets/4.D5l1JxgO.css.br +0 -0
- package/build/client/_app/immutable/assets/4.D5l1JxgO.css.gz +0 -0
- package/build/client/_app/immutable/assets/5.C5qz-NeI.css +1 -0
- package/build/client/_app/immutable/assets/5.C5qz-NeI.css.br +0 -0
- package/build/client/_app/immutable/assets/5.C5qz-NeI.css.gz +0 -0
- package/build/client/_app/immutable/chunks/1mEchsPO.js +1 -0
- package/build/client/_app/immutable/chunks/1mEchsPO.js.br +0 -0
- package/build/client/_app/immutable/chunks/1mEchsPO.js.gz +0 -0
- package/build/client/_app/immutable/chunks/B7X-vhXI.js +1 -0
- package/build/client/_app/immutable/chunks/B7X-vhXI.js.br +0 -0
- package/build/client/_app/immutable/chunks/B7X-vhXI.js.gz +0 -0
- package/build/client/_app/immutable/chunks/BRkqKgVG.js +1 -0
- package/build/client/_app/immutable/chunks/BRkqKgVG.js.br +0 -0
- package/build/client/_app/immutable/chunks/BRkqKgVG.js.gz +0 -0
- package/build/client/_app/immutable/chunks/BfJ-f-Tu.js +1 -0
- package/build/client/_app/immutable/chunks/BfJ-f-Tu.js.br +2 -0
- package/build/client/_app/immutable/chunks/BfJ-f-Tu.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CGMJxf7r.js +1 -0
- package/build/client/_app/immutable/chunks/CGMJxf7r.js.br +0 -0
- package/build/client/_app/immutable/chunks/CGMJxf7r.js.gz +0 -0
- package/build/client/_app/immutable/chunks/{CZHsSL_X.js → CJFjKwJ7.js} +1 -1
- package/build/client/_app/immutable/chunks/CJFjKwJ7.js.br +0 -0
- package/build/client/_app/immutable/chunks/CJFjKwJ7.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CNH2HlKj.js +20 -0
- package/build/client/_app/immutable/chunks/CNH2HlKj.js.br +0 -0
- package/build/client/_app/immutable/chunks/CNH2HlKj.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CR6bkGJW.js +6 -0
- package/build/client/_app/immutable/chunks/CR6bkGJW.js.br +0 -0
- package/build/client/_app/immutable/chunks/CR6bkGJW.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CVtJ6yRM.js +1 -0
- package/build/client/_app/immutable/chunks/CVtJ6yRM.js.br +0 -0
- package/build/client/_app/immutable/chunks/CVtJ6yRM.js.gz +0 -0
- package/build/client/_app/immutable/chunks/{CSoRdFvv.js → CaiJSUi3.js} +1 -1
- package/build/client/_app/immutable/chunks/CaiJSUi3.js.br +0 -0
- package/build/client/_app/immutable/chunks/CaiJSUi3.js.gz +0 -0
- package/build/client/_app/immutable/chunks/{DjsDGxCa.js → CmczWE_d.js} +4 -4
- package/build/client/_app/immutable/chunks/CmczWE_d.js.br +0 -0
- package/build/client/_app/immutable/chunks/CmczWE_d.js.gz +0 -0
- package/build/client/_app/immutable/chunks/{CDVSripB.js → CqfYvnci.js} +1 -1
- package/build/client/_app/immutable/chunks/CqfYvnci.js.br +0 -0
- package/build/client/_app/immutable/chunks/CqfYvnci.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CsgHjHGZ.js +1 -0
- package/build/client/_app/immutable/chunks/CsgHjHGZ.js.br +0 -0
- package/build/client/_app/immutable/chunks/CsgHjHGZ.js.gz +0 -0
- package/build/client/_app/immutable/chunks/{UJOiqIYE.js → CtrCjGZT.js} +1 -1
- package/build/client/_app/immutable/chunks/CtrCjGZT.js.br +0 -0
- package/build/client/_app/immutable/chunks/CtrCjGZT.js.gz +0 -0
- package/build/client/_app/immutable/chunks/DDiOVAd8.js +61 -0
- package/build/client/_app/immutable/chunks/DDiOVAd8.js.br +0 -0
- package/build/client/_app/immutable/chunks/DDiOVAd8.js.gz +0 -0
- package/build/client/_app/immutable/chunks/DVl0sebP.js +2 -0
- package/build/client/_app/immutable/chunks/DVl0sebP.js.br +0 -0
- package/build/client/_app/immutable/chunks/DVl0sebP.js.gz +0 -0
- package/build/client/_app/immutable/chunks/EhLZwqfu.js +3 -0
- package/build/client/_app/immutable/chunks/EhLZwqfu.js.br +0 -0
- package/build/client/_app/immutable/chunks/EhLZwqfu.js.gz +0 -0
- package/build/client/_app/immutable/chunks/gQJcRhou.js +1 -0
- package/build/client/_app/immutable/chunks/gQJcRhou.js.br +0 -0
- package/build/client/_app/immutable/chunks/gQJcRhou.js.gz +0 -0
- package/build/client/_app/immutable/chunks/pRcLbE0d.js +1 -0
- package/build/client/_app/immutable/chunks/pRcLbE0d.js.br +0 -0
- package/build/client/_app/immutable/chunks/pRcLbE0d.js.gz +0 -0
- package/build/client/_app/immutable/chunks/{CF4lQ45j.js → y_g-KC7l.js} +1 -1
- package/build/client/_app/immutable/chunks/y_g-KC7l.js.br +0 -0
- package/build/client/_app/immutable/chunks/y_g-KC7l.js.gz +0 -0
- package/build/client/_app/immutable/entry/app.zJvbFXsj.js +2 -0
- package/build/client/_app/immutable/entry/app.zJvbFXsj.js.br +0 -0
- package/build/client/_app/immutable/entry/app.zJvbFXsj.js.gz +0 -0
- package/build/client/_app/immutable/entry/start.B2Jf5iFd.js +1 -0
- package/build/client/_app/immutable/entry/start.B2Jf5iFd.js.br +2 -0
- package/build/client/_app/immutable/entry/start.B2Jf5iFd.js.gz +0 -0
- package/build/client/_app/immutable/nodes/0.B_E4j3MX.js +1 -0
- package/build/client/_app/immutable/nodes/0.B_E4j3MX.js.br +0 -0
- package/build/client/_app/immutable/nodes/0.B_E4j3MX.js.gz +0 -0
- package/build/client/_app/immutable/nodes/1.B0oFqb8X.js +1 -0
- package/build/client/_app/immutable/nodes/1.B0oFqb8X.js.br +0 -0
- package/build/client/_app/immutable/nodes/1.B0oFqb8X.js.gz +0 -0
- package/build/client/_app/immutable/nodes/2.Bqul0XyM.js +13 -0
- package/build/client/_app/immutable/nodes/2.Bqul0XyM.js.br +0 -0
- package/build/client/_app/immutable/nodes/2.Bqul0XyM.js.gz +0 -0
- package/build/client/_app/immutable/nodes/3.CTqUQKSN.js +9 -0
- package/build/client/_app/immutable/nodes/3.CTqUQKSN.js.br +0 -0
- package/build/client/_app/immutable/nodes/3.CTqUQKSN.js.gz +0 -0
- package/build/client/_app/immutable/nodes/4.B_pbOZoD.js +4 -0
- package/build/client/_app/immutable/nodes/4.B_pbOZoD.js.br +0 -0
- package/build/client/_app/immutable/nodes/4.B_pbOZoD.js.gz +0 -0
- package/build/client/_app/immutable/nodes/5.CdLPNo5-.js +1 -0
- package/build/client/_app/immutable/nodes/5.CdLPNo5-.js.br +0 -0
- package/build/client/_app/immutable/nodes/5.CdLPNo5-.js.gz +0 -0
- package/build/client/_app/immutable/nodes/6.BvjUfHnH.js +1 -0
- package/build/client/_app/immutable/nodes/6.BvjUfHnH.js.br +0 -0
- package/build/client/_app/immutable/nodes/6.BvjUfHnH.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{5.g3R-QfIW.js → 7.5K7Od8ba.js} +3 -3
- package/build/client/_app/immutable/nodes/7.5K7Od8ba.js.br +0 -0
- package/build/client/_app/immutable/nodes/7.5K7Od8ba.js.gz +0 -0
- package/build/client/_app/immutable/nodes/8.Bs1DrW0_.js +2 -0
- package/build/client/_app/immutable/nodes/8.Bs1DrW0_.js.br +0 -0
- package/build/client/_app/immutable/nodes/8.Bs1DrW0_.js.gz +0 -0
- package/build/client/_app/immutable/nodes/9.1fMlGdqv.js +2 -0
- package/build/client/_app/immutable/nodes/9.1fMlGdqv.js.br +0 -0
- package/build/client/_app/immutable/nodes/9.1fMlGdqv.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/server/chunks/0-e1fgD9Mi.js +23 -0
- package/build/server/chunks/0-e1fgD9Mi.js.map +1 -0
- package/build/server/chunks/1-D2_XVthm.js +9 -0
- package/build/server/chunks/{1-BV7u1xGo.js.map → 1-D2_XVthm.js.map} +1 -1
- package/build/server/chunks/2-DADX86JZ.js +21 -0
- package/build/server/chunks/2-DADX86JZ.js.map +1 -0
- package/build/server/chunks/3-CHacdiCg.js +21 -0
- package/build/server/chunks/3-CHacdiCg.js.map +1 -0
- package/build/server/chunks/4-0UE_6Ep-.js +23 -0
- package/build/server/chunks/4-0UE_6Ep-.js.map +1 -0
- package/build/server/chunks/5-BBIP1PzX.js +24 -0
- package/build/server/chunks/5-BBIP1PzX.js.map +1 -0
- package/build/server/chunks/6-CWLNQu6F.js +9 -0
- package/build/server/chunks/6-CWLNQu6F.js.map +1 -0
- package/build/server/chunks/7-DmQ3B8uy.js +9 -0
- package/build/server/chunks/7-DmQ3B8uy.js.map +1 -0
- package/build/server/chunks/8-CnFVjQtZ.js +9 -0
- package/build/server/chunks/8-CnFVjQtZ.js.map +1 -0
- package/build/server/chunks/9-Dw7-P6aF.js +9 -0
- package/build/server/chunks/9-Dw7-P6aF.js.map +1 -0
- package/build/server/chunks/{Button-Cs1aE6ka.js → Button-WKgiLWZI.js} +4 -9
- package/build/server/chunks/Button-WKgiLWZI.js.map +1 -0
- package/build/server/chunks/{EmptyState-DDFH1K8g.js → EmptyState-BUBqASsp.js} +3 -3
- package/build/server/chunks/{EmptyState-DDFH1K8g.js.map → EmptyState-BUBqASsp.js.map} +1 -1
- package/build/server/chunks/{Icon-CEUrotA6.js → Icon-BNBAg85a.js} +3 -3
- package/build/server/chunks/Icon-BNBAg85a.js.map +1 -0
- package/build/server/chunks/{Shimmer-DB8W1zt6.js → Shimmer-C4uBVwxz.js} +2 -2
- package/build/server/chunks/{Shimmer-DB8W1zt6.js.map → Shimmer-C4uBVwxz.js.map} +1 -1
- package/build/server/chunks/{_error.svelte-uCOJNxvr.js → _error.svelte-DkIwmECt.js} +5 -5
- package/build/server/chunks/{_error.svelte-uCOJNxvr.js.map → _error.svelte-DkIwmECt.js.map} +1 -1
- package/build/server/chunks/{_layout.svelte-CtWmEJwe.js → _layout.svelte-DllETxmJ.js} +13 -7
- package/build/server/chunks/_layout.svelte-DllETxmJ.js.map +1 -0
- package/build/server/chunks/_page.svelte-BZSdLKE_.js +118 -0
- package/build/server/chunks/_page.svelte-BZSdLKE_.js.map +1 -0
- package/build/server/chunks/{_page.svelte-CxWcQ0Am.js → _page.svelte-Cmuco1mC.js} +84 -199
- package/build/server/chunks/_page.svelte-Cmuco1mC.js.map +1 -0
- package/build/server/chunks/{_page.svelte-BgevQjq1.js → _page.svelte-Co5sF7W-.js} +12 -11
- package/build/server/chunks/{_page.svelte-BgevQjq1.js.map → _page.svelte-Co5sF7W-.js.map} +1 -1
- package/build/server/chunks/{_page.svelte-DO4oa_LY.js → _page.svelte-CpL3R-VI.js} +8 -8
- package/build/server/chunks/{_page.svelte-DO4oa_LY.js.map → _page.svelte-CpL3R-VI.js.map} +1 -1
- package/build/server/chunks/_page.svelte-DDSzYLUs.js +137 -0
- package/build/server/chunks/_page.svelte-DDSzYLUs.js.map +1 -0
- package/build/server/chunks/_page.svelte-JIkgFUFf.js +26 -0
- package/build/server/chunks/_page.svelte-JIkgFUFf.js.map +1 -0
- package/build/server/chunks/{_page.svelte-CVq6tRb3.js → _page.svelte-Y9-O5a5w.js} +10 -9
- package/build/server/chunks/_page.svelte-Y9-O5a5w.js.map +1 -0
- package/build/server/chunks/{_page.svelte-BcZaKdX9.js → _page.svelte-fcX09N4d.js} +9 -9
- package/build/server/chunks/{_page.svelte-BcZaKdX9.js.map → _page.svelte-fcX09N4d.js.map} +1 -1
- package/build/server/chunks/{_server.ts-DYpJImqd.js → _server.ts-0Xr2fWaq.js} +9 -5
- package/build/server/chunks/_server.ts-0Xr2fWaq.js.map +1 -0
- package/build/server/chunks/{_server.ts-CTpcLUH8.js → _server.ts-2ixC-X3K.js} +20 -5
- package/build/server/chunks/_server.ts-2ixC-X3K.js.map +1 -0
- package/build/server/chunks/{_server.ts-CAxsWKvS.js → _server.ts-40c_epk8.js} +20 -4
- package/build/server/chunks/_server.ts-40c_epk8.js.map +1 -0
- package/build/server/chunks/{_server.ts-WhTJBEJy.js → _server.ts-A9_tRR-K.js} +5 -4
- package/build/server/chunks/{_server.ts-WhTJBEJy.js.map → _server.ts-A9_tRR-K.js.map} +1 -1
- package/build/server/chunks/_server.ts-BRAzC6W1.js +98 -0
- package/build/server/chunks/_server.ts-BRAzC6W1.js.map +1 -0
- package/build/server/chunks/{_server.ts-DB_Kg97c.js → _server.ts-BScvgttw.js} +24 -4
- package/build/server/chunks/_server.ts-BScvgttw.js.map +1 -0
- package/build/server/chunks/{_server.ts-Ch-6iOHp.js → _server.ts-B__YN2kX.js} +121 -87
- package/build/server/chunks/_server.ts-B__YN2kX.js.map +1 -0
- package/build/server/chunks/{_server.ts-tSpgyl1D.js → _server.ts-Bjbr7glm.js} +4 -3
- package/build/server/chunks/_server.ts-Bjbr7glm.js.map +1 -0
- package/build/server/chunks/{_server.ts-COu0vNpd.js → _server.ts-BrqaMMAa.js} +7 -6
- package/build/server/chunks/_server.ts-BrqaMMAa.js.map +1 -0
- package/build/server/chunks/{_server.ts-vekTmWAx.js → _server.ts-BuYyCrnF.js} +6 -4
- package/build/server/chunks/_server.ts-BuYyCrnF.js.map +1 -0
- package/build/server/chunks/{_server.ts-Deok2y88.js → _server.ts-ByIrRtCx.js} +132 -76
- package/build/server/chunks/_server.ts-ByIrRtCx.js.map +1 -0
- package/build/server/chunks/{_server.ts-DYvb9ijZ.js → _server.ts-ByPExYfO.js} +4 -3
- package/build/server/chunks/{_server.ts-DYvb9ijZ.js.map → _server.ts-ByPExYfO.js.map} +1 -1
- package/build/server/chunks/_server.ts-CjpQ10xh.js +123 -0
- package/build/server/chunks/_server.ts-CjpQ10xh.js.map +1 -0
- package/build/server/chunks/_server.ts-CyjDrcZN.js +21 -0
- package/build/server/chunks/_server.ts-CyjDrcZN.js.map +1 -0
- package/build/server/chunks/{_server.ts-DV8zTCF9.js → _server.ts-DOGUMzPx.js} +4 -3
- package/build/server/chunks/{_server.ts-DV8zTCF9.js.map → _server.ts-DOGUMzPx.js.map} +1 -1
- package/build/server/chunks/_server.ts-DZvfyuNj.js +15 -0
- package/build/server/chunks/_server.ts-DZvfyuNj.js.map +1 -0
- package/build/server/chunks/{_server.ts-XzT2UHM1.js → _server.ts-DkPPTUPo.js} +4 -3
- package/build/server/chunks/{_server.ts-XzT2UHM1.js.map → _server.ts-DkPPTUPo.js.map} +1 -1
- package/build/server/chunks/{auth-DeCdZ83n.js → auth-DuunT7Cg.js} +2 -2
- package/build/server/chunks/{auth-DeCdZ83n.js.map → auth-DuunT7Cg.js.map} +1 -1
- package/build/server/chunks/{client-BdGHe_hY.js → client-DRtPDkMh.js} +4 -4
- package/build/server/chunks/{client-BdGHe_hY.js.map → client-DRtPDkMh.js.map} +1 -1
- package/build/server/chunks/client2-bqqmu0b7.js +7 -0
- package/build/server/chunks/{client2-CCBGA-2V.js.map → client2-bqqmu0b7.js.map} +1 -1
- package/build/server/chunks/close-BGlLztTb.js +192 -0
- package/build/server/chunks/close-BGlLztTb.js.map +1 -0
- package/build/server/chunks/events-handler-Dm1mNPQP.js +20 -0
- package/build/server/chunks/events-handler-Dm1mNPQP.js.map +1 -0
- package/build/server/chunks/html-FW6Ia4bL.js +8 -0
- package/build/server/chunks/html-FW6Ia4bL.js.map +1 -0
- package/build/server/chunks/{shared-server-sSGG17Df.js → index-CoD1IJuy.js} +2 -11
- package/build/server/chunks/index-CoD1IJuy.js.map +1 -0
- package/build/server/chunks/{index-DwaY1cAm.js → index-DP9bWJrR.js} +2 -2
- package/build/server/chunks/{index-DwaY1cAm.js.map → index-DP9bWJrR.js.map} +1 -1
- package/build/server/chunks/{index-server-CrDaL06Y.js → index-server-BUmV4MIG.js} +2 -2
- package/build/server/chunks/index-server-BUmV4MIG.js.map +1 -0
- package/build/server/chunks/index-server2-BJrT0wnA.js +5 -0
- package/build/server/chunks/index-server2-BJrT0wnA.js.map +1 -0
- package/build/server/chunks/{index2-CgclKpUj.js → index2-D5Y19GKR.js} +2 -2
- package/build/server/chunks/index2-D5Y19GKR.js.map +1 -0
- package/build/server/chunks/{library-apns-BqJbvSKh.js → library-apns-Cf-E-DhM.js} +5 -2
- package/build/server/chunks/library-apns-Cf-E-DhM.js.map +1 -0
- package/build/server/chunks/providers-DtstoHQ0.js +17 -0
- package/build/server/chunks/providers-DtstoHQ0.js.map +1 -0
- package/build/server/chunks/{pty-manager-BQVB7IVj.js → pty-manager-TyMUpDA9.js} +41 -9
- package/build/server/chunks/pty-manager-TyMUpDA9.js.map +1 -0
- package/build/server/chunks/{root-DDSnEAZv.js → root-CATOR_0t.js} +2 -2
- package/build/server/chunks/root-CATOR_0t.js.map +1 -0
- package/build/server/chunks/shared-server-DaWdgxVh.js +11 -0
- package/build/server/chunks/shared-server-DaWdgxVh.js.map +1 -0
- package/build/server/chunks/{state.svelte-hBbXlUak.js → state.svelte-CftllyvC.js} +3 -3
- package/build/server/chunks/state.svelte-CftllyvC.js.map +1 -0
- package/build/server/chunks/{stores-DHNzYNpX.js → stores-BjL57aOK.js} +4 -4
- package/build/server/chunks/{stores-DHNzYNpX.js.map → stores-BjL57aOK.js.map} +1 -1
- package/build/server/index.js +173 -6
- package/build/server/index.js.map +1 -1
- package/build/server/manifest.js +53 -30
- package/build/server/manifest.js.map +1 -1
- package/package.json +28 -7
- package/scripts/dev.mjs +361 -0
- package/scripts/install.sh +133 -73
- package/scripts/{fix-generated-types.sh → postgen-types.sh} +2 -2
- package/scripts/setup.cjs +440 -244
- package/scripts/vercel-env-commands.sh +3 -3
- package/server.ts +3 -3
- package/src/app.html +163 -0
- package/src/lib/modules/client/activity/ActivityFeed.svelte +279 -0
- package/src/lib/modules/client/activity/index.ts +8 -0
- package/src/lib/modules/client/activity/store.svelte.ts +478 -0
- package/src/lib/modules/client/activity/summarizer.ts +224 -0
- package/src/lib/modules/client/common/Card.svelte +2 -9
- package/src/lib/modules/client/common/EmptyState.svelte +2 -20
- package/src/lib/modules/client/common/Icon.svelte +3 -7
- package/src/lib/modules/client/common/StatusBadge.svelte +2 -4
- package/src/lib/modules/client/common/config-guard.ts +22 -2
- package/src/lib/modules/client/common/index.ts +1 -1
- package/src/lib/modules/client/common/time.ts +27 -9
- package/src/lib/modules/client/dashboard/DashboardCard.svelte +374 -0
- package/src/lib/modules/client/dashboard/DashboardView.svelte +66 -0
- package/src/lib/modules/client/dashboard/index.ts +12 -0
- package/src/lib/modules/client/dashboard/store.svelte.ts +663 -0
- package/src/lib/modules/client/dashboard/summarizer.ts +205 -0
- package/src/lib/modules/client/neurolink/fetch-proxy.ts +70 -0
- package/src/lib/modules/client/neurolink/provider-config.ts +111 -0
- package/src/lib/modules/client/terminal/ChatView.svelte +46 -43
- package/src/lib/modules/client/terminal/CommandPalette.svelte +3 -12
- package/src/lib/modules/client/terminal/ConnectionStatus.svelte +3 -6
- package/src/lib/modules/client/terminal/LaunchSheet.svelte +10 -21
- package/src/lib/modules/client/terminal/QuickKeys.svelte +4 -11
- package/src/lib/modules/client/terminal/ShortcutsHelp.svelte +3 -6
- package/src/lib/modules/client/terminal/keyboard-shortcuts.ts +5 -7
- package/src/lib/modules/client/terminal/xterm-wrapper.ts +27 -47
- package/src/lib/modules/server/apn/library-apns.ts +6 -3
- package/src/lib/modules/server/apn/notification-history.ts +2 -2
- package/src/lib/modules/server/apn/notification-sessions.ts +1 -3
- package/src/lib/modules/server/apn/pending-requests.ts +1 -1
- package/src/lib/modules/server/apn/types.ts +2 -52
- package/src/lib/modules/server/cli/index.ts +1 -30
- package/src/lib/modules/server/cli/runner.ts +7 -15
- package/src/lib/modules/server/fcm/fcm-service.ts +2 -2
- package/src/lib/modules/server/sessions/jsonl-parser.ts +97 -42
- package/src/lib/modules/server/sessions/jsonl-reader.ts +69 -60
- package/src/lib/modules/server/sessions/opencode-reader.ts +1 -1
- package/src/lib/modules/server/sessions/process-detector.ts +72 -31
- package/src/lib/modules/server/sessions/types.ts +2 -42
- package/src/lib/modules/server/terminal/holder-client.ts +11 -35
- package/src/lib/modules/server/terminal/opencode-watcher.ts +16 -24
- package/src/lib/modules/server/terminal/pty-manager.ts +40 -45
- package/src/lib/modules/server/terminal/session-watcher.ts +15 -17
- package/src/lib/modules/server/terminal/terminal-store.ts +1 -1
- package/src/lib/modules/server/ws/events-handler.ts +1 -16
- package/src/lib/modules/server/ws/keepalive.ts +1 -5
- package/src/lib/modules/server/ws/server.ts +1 -1
- package/src/lib/modules/server/ws/session-handler.ts +20 -86
- package/src/lib/modules/server/ws/terminal-handler.ts +28 -51
- package/src/lib/modules/server/ws/ticket-store.ts +1 -1
- package/src/lib/modules/shared/providers.ts +21 -0
- package/src/lib/types/activity.ts +18 -0
- package/src/lib/types/apn.ts +43 -0
- package/src/lib/types/cli.ts +39 -0
- package/src/lib/types/common.ts +39 -0
- package/src/lib/types/dashboard.ts +4 -0
- package/src/lib/types/generated/Client.ts +1656 -0
- package/src/{generated/types → lib/types/generated}/WsProtocol.ts +344 -2
- package/src/lib/types/index.ts +28 -0
- package/src/lib/types/neurolink.ts +4 -0
- package/src/lib/types/server.ts +93 -0
- package/src/lib/types/sessions.ts +59 -0
- package/src/lib/types/terminal-client.ts +132 -0
- package/src/lib/types/ws.ts +161 -0
- package/src/routes/+error.svelte +7 -2
- package/src/routes/+layout.server.ts +9 -0
- package/src/routes/+layout.svelte +36 -7
- package/src/routes/+page.server.ts +7 -0
- package/src/routes/+page.svelte +85 -35
- package/src/routes/activity/+page.server.ts +7 -0
- package/src/routes/activity/+page.svelte +58 -0
- package/src/routes/api/health/+server.ts +32 -19
- package/src/routes/api/neurolink-proxy/+server.ts +136 -0
- package/src/routes/api/notify/+server.ts +159 -84
- package/src/routes/api/sessions/+server.ts +1 -1
- package/src/routes/api/sessions/connect/+server.ts +10 -6
- package/src/routes/api/terminals/+server.ts +20 -1
- package/src/routes/api/terminals/[id]/+server.ts +16 -2
- package/src/routes/api/webhook/+server.ts +5 -33
- package/src/routes/api/ws-ticket/+server.ts +4 -4
- package/src/routes/config/+page.server.ts +9 -0
- package/src/routes/config/+page.svelte +118 -25
- package/src/routes/neurolink/+page.server.ts +10 -0
- package/src/routes/neurolink/+page.svelte +331 -0
- package/src/routes/project/+page.svelte +17 -12
- package/src/routes/session/[id]/+page.svelte +146 -62
- package/src/routes/terminals/+page.svelte +2 -2
- package/src/routes/terminals/[id]/+page.svelte +99 -88
- package/svelte.config.js +1 -3
- package/tsconfig.json +1 -0
- package/build/client/_app/immutable/assets/2.CAShZ7lQ.css +0 -1
- package/build/client/_app/immutable/assets/2.CAShZ7lQ.css.br +0 -1
- package/build/client/_app/immutable/assets/2.CAShZ7lQ.css.gz +0 -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/chunks/B5NAKyil.js +0 -20
- 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 +0 -1
- 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 +0 -6
- 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/BN1NjBrw.js +0 -1
- package/build/client/_app/immutable/chunks/BN1NjBrw.js.br +0 -0
- package/build/client/_app/immutable/chunks/BN1NjBrw.js.gz +0 -0
- package/build/client/_app/immutable/chunks/BOYo8yTr.js +0 -1
- 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/Bu1aqm5j.js +0 -1
- 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/CDVSripB.js.br +0 -0
- package/build/client/_app/immutable/chunks/CDVSripB.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CF4lQ45j.js.br +0 -0
- package/build/client/_app/immutable/chunks/CF4lQ45j.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CQjSATpv.js +0 -61
- 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.br +0 -0
- package/build/client/_app/immutable/chunks/CSoRdFvv.js.gz +0 -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/DSU1n5N_.js +0 -1
- 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 +0 -1
- 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.br +0 -0
- package/build/client/_app/immutable/chunks/DjsDGxCa.js.gz +0 -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 +0 -2
- 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.DwWiuoEC.js +0 -2
- package/build/client/_app/immutable/entry/app.DwWiuoEC.js.br +0 -0
- package/build/client/_app/immutable/entry/app.DwWiuoEC.js.gz +0 -0
- package/build/client/_app/immutable/entry/start.DG8BMhrh.js +0 -1
- package/build/client/_app/immutable/entry/start.DG8BMhrh.js.br +0 -0
- package/build/client/_app/immutable/entry/start.DG8BMhrh.js.gz +0 -0
- package/build/client/_app/immutable/nodes/0.ejabgzDQ.js +0 -1
- package/build/client/_app/immutable/nodes/0.ejabgzDQ.js.br +0 -0
- package/build/client/_app/immutable/nodes/0.ejabgzDQ.js.gz +0 -0
- package/build/client/_app/immutable/nodes/1.BFK7Ubrr.js +0 -1
- package/build/client/_app/immutable/nodes/1.BFK7Ubrr.js.br +0 -0
- package/build/client/_app/immutable/nodes/1.BFK7Ubrr.js.gz +0 -0
- package/build/client/_app/immutable/nodes/2.DV3saFiY.js +0 -1
- package/build/client/_app/immutable/nodes/2.DV3saFiY.js.br +0 -0
- package/build/client/_app/immutable/nodes/2.DV3saFiY.js.gz +0 -0
- package/build/client/_app/immutable/nodes/3.3yohCM25.js +0 -3
- 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.D6NIf10D.js +0 -1
- package/build/client/_app/immutable/nodes/4.D6NIf10D.js.br +0 -0
- package/build/client/_app/immutable/nodes/4.D6NIf10D.js.gz +0 -0
- package/build/client/_app/immutable/nodes/5.g3R-QfIW.js.br +0 -0
- package/build/client/_app/immutable/nodes/5.g3R-QfIW.js.gz +0 -0
- package/build/client/_app/immutable/nodes/6.DSpd_nYK.js +0 -2
- package/build/client/_app/immutable/nodes/6.DSpd_nYK.js.br +0 -0
- package/build/client/_app/immutable/nodes/6.DSpd_nYK.js.gz +0 -0
- package/build/client/_app/immutable/nodes/7.F9WBFTz2.js +0 -2
- package/build/client/_app/immutable/nodes/7.F9WBFTz2.js.br +0 -0
- package/build/client/_app/immutable/nodes/7.F9WBFTz2.js.gz +0 -0
- package/build/server/chunks/0-ePgrkfG9.js +0 -9
- package/build/server/chunks/0-ePgrkfG9.js.map +0 -1
- package/build/server/chunks/1-BV7u1xGo.js +0 -9
- package/build/server/chunks/2-3p1kyvjQ.js +0 -9
- package/build/server/chunks/2-3p1kyvjQ.js.map +0 -1
- package/build/server/chunks/3-Ck7ewhOX.js +0 -9
- package/build/server/chunks/3-Ck7ewhOX.js.map +0 -1
- package/build/server/chunks/4-ChFYfo_S.js +0 -9
- package/build/server/chunks/4-ChFYfo_S.js.map +0 -1
- package/build/server/chunks/5-q-tQLBBu.js +0 -9
- package/build/server/chunks/5-q-tQLBBu.js.map +0 -1
- package/build/server/chunks/6-BIaAbm8b.js +0 -9
- package/build/server/chunks/6-BIaAbm8b.js.map +0 -1
- package/build/server/chunks/7--TmbCgrH.js +0 -9
- package/build/server/chunks/7--TmbCgrH.js.map +0 -1
- package/build/server/chunks/Button-Cs1aE6ka.js.map +0 -1
- package/build/server/chunks/Icon-CEUrotA6.js.map +0 -1
- package/build/server/chunks/_layout.svelte-CtWmEJwe.js.map +0 -1
- package/build/server/chunks/_page.svelte-BdYynOck.js +0 -85
- package/build/server/chunks/_page.svelte-BdYynOck.js.map +0 -1
- package/build/server/chunks/_page.svelte-CVq6tRb3.js.map +0 -1
- package/build/server/chunks/_page.svelte-CxWcQ0Am.js.map +0 -1
- package/build/server/chunks/_server.ts-BStnNIcq.js +0 -34
- package/build/server/chunks/_server.ts-BStnNIcq.js.map +0 -1
- package/build/server/chunks/_server.ts-CAxsWKvS.js.map +0 -1
- package/build/server/chunks/_server.ts-COu0vNpd.js.map +0 -1
- package/build/server/chunks/_server.ts-CTpcLUH8.js.map +0 -1
- package/build/server/chunks/_server.ts-Cf84YIaW.js +0 -25
- package/build/server/chunks/_server.ts-Cf84YIaW.js.map +0 -1
- package/build/server/chunks/_server.ts-Ch-6iOHp.js.map +0 -1
- package/build/server/chunks/_server.ts-CtH0dhUp.js +0 -71
- package/build/server/chunks/_server.ts-CtH0dhUp.js.map +0 -1
- package/build/server/chunks/_server.ts-DB_Kg97c.js.map +0 -1
- package/build/server/chunks/_server.ts-DYpJImqd.js.map +0 -1
- package/build/server/chunks/_server.ts-Deok2y88.js.map +0 -1
- package/build/server/chunks/_server.ts-tSpgyl1D.js.map +0 -1
- package/build/server/chunks/_server.ts-vekTmWAx.js.map +0 -1
- package/build/server/chunks/client2-CCBGA-2V.js +0 -7
- package/build/server/chunks/index-server-CrDaL06Y.js.map +0 -1
- package/build/server/chunks/index2-CgclKpUj.js.map +0 -1
- package/build/server/chunks/library-apns-BqJbvSKh.js.map +0 -1
- package/build/server/chunks/pty-manager-BQVB7IVj.js.map +0 -1
- package/build/server/chunks/root-DDSnEAZv.js.map +0 -1
- package/build/server/chunks/shared-server-sSGG17Df.js.map +0 -1
- package/build/server/chunks/state.svelte-hBbXlUak.js.map +0 -1
- package/src/generated/types/Client.ts +0 -589
- package/src/lib/types/config.ts +0 -1
- /package/build/client/_app/immutable/assets/{4.BFUut--w.css → 6.BFUut--w.css} +0 -0
- /package/build/client/_app/immutable/assets/{4.BFUut--w.css.br → 6.BFUut--w.css.br} +0 -0
- /package/build/client/_app/immutable/assets/{4.BFUut--w.css.gz → 6.BFUut--w.css.gz} +0 -0
- /package/build/client/_app/immutable/assets/{5.BTOx7yt7.css → 7.BTOx7yt7.css} +0 -0
- /package/build/client/_app/immutable/assets/{5.BTOx7yt7.css.br → 7.BTOx7yt7.css.br} +0 -0
- /package/build/client/_app/immutable/assets/{5.BTOx7yt7.css.gz → 7.BTOx7yt7.css.gz} +0 -0
- /package/build/client/_app/immutable/assets/{6.eZGZN-BF.css → 8.eZGZN-BF.css} +0 -0
- /package/build/client/_app/immutable/assets/{6.eZGZN-BF.css.br → 8.eZGZN-BF.css.br} +0 -0
- /package/build/client/_app/immutable/assets/{6.eZGZN-BF.css.gz → 8.eZGZN-BF.css.gz} +0 -0
- /package/build/client/_app/immutable/assets/{7.DwS5ZHBh.css → 9.DwS5ZHBh.css} +0 -0
- /package/build/client/_app/immutable/assets/{7.DwS5ZHBh.css.br → 9.DwS5ZHBh.css.br} +0 -0
- /package/build/client/_app/immutable/assets/{7.DwS5ZHBh.css.gz → 9.DwS5ZHBh.css.gz} +0 -0
- /package/src/{generated/types → lib/types/generated}/API.ts +0 -0
- /package/src/{generated/types → lib/types/generated}/APN.ts +0 -0
- /package/src/{generated/types → lib/types/generated}/CLI.ts +0 -0
- /package/src/{generated/types → lib/types/generated}/Config.ts +0 -0
- /package/src/{generated/types → lib/types/generated}/Holder.ts +0 -0
- /package/src/{generated/types → lib/types/generated}/JWT.ts +0 -0
- /package/src/{generated/types → lib/types/generated}/Notification.ts +0 -0
- /package/src/{generated/types → lib/types/generated}/OpenCode.ts +0 -0
- /package/src/{generated/types → lib/types/generated}/Sessions.ts +0 -0
- /package/src/{generated/types → lib/types/generated}/Terminal.ts +0 -0
- /package/src/{generated/types → lib/types/generated}/index.ts +0 -0
package/scripts/setup.cjs
CHANGED
|
@@ -9,7 +9,6 @@ const { execSync, spawn } = require('child_process');
|
|
|
9
9
|
const crypto = require('crypto');
|
|
10
10
|
const fs = require('fs');
|
|
11
11
|
const path = require('path');
|
|
12
|
-
const http = require('http');
|
|
13
12
|
|
|
14
13
|
// ── ANSI helpers ─────────────────────────────────────────────────────
|
|
15
14
|
const C = {
|
|
@@ -52,10 +51,28 @@ function escapeForDoubleQuotedShell(s) {
|
|
|
52
51
|
return s.replace(/[\\"$`]/g, '\\$&');
|
|
53
52
|
}
|
|
54
53
|
|
|
55
|
-
// ──
|
|
56
|
-
// Produces a
|
|
57
|
-
|
|
58
|
-
|
|
54
|
+
// ── API key generator ────────────────────────────────────────────────
|
|
55
|
+
// Produces a readable key: <machinename><4 random hex chars>
|
|
56
|
+
// e.g. "sachinsharma567f"
|
|
57
|
+
// Easy to recognise, type, and remember — still unique per install.
|
|
58
|
+
function generateApiKey() {
|
|
59
|
+
const os = require('os');
|
|
60
|
+
// Take only the letters-only leading portion of the hostname — strips
|
|
61
|
+
// device serial suffixes like HCW39CV9MH which always contain digits.
|
|
62
|
+
const name =
|
|
63
|
+
os
|
|
64
|
+
.hostname()
|
|
65
|
+
.toLowerCase()
|
|
66
|
+
.replace(/\.local$/, '') // strip macOS .local suffix
|
|
67
|
+
.replace(/[^a-z0-9]/g, ' ') // turn separators into spaces
|
|
68
|
+
.trim()
|
|
69
|
+
.split(' ')
|
|
70
|
+
.filter((p) => /^[a-z]+$/.test(p)) // keep only pure-letter segments
|
|
71
|
+
.join('')
|
|
72
|
+
.slice(0, 20) || 'shooter';
|
|
73
|
+
|
|
74
|
+
const suffix = crypto.randomBytes(16).toString('hex'); // 128 bits of entropy
|
|
75
|
+
return `${name}-${suffix}`;
|
|
59
76
|
}
|
|
60
77
|
|
|
61
78
|
// ── Globals ──────────────────────────────────────────────────────────
|
|
@@ -63,9 +80,64 @@ const ROOT = process.env.SHOOTER_PKG_ROOT || path.resolve(__dirname, '..');
|
|
|
63
80
|
const SHOOTER_HOME = process.env.SHOOTER_HOME || path.join(require('os').homedir(), '.shooter');
|
|
64
81
|
const DOT_ENV_PATH = path.join(SHOOTER_HOME, '.env');
|
|
65
82
|
const AUTO_MODE = process.argv.includes('--auto');
|
|
83
|
+
const PUSH_MODE = process.argv.includes('--push');
|
|
66
84
|
|
|
67
85
|
let rl; // readline interface — created in main()
|
|
68
86
|
|
|
87
|
+
// ── AI Provider Registry ─────────────────────────────────────────────
|
|
88
|
+
const PROVIDER_REGISTRY = [
|
|
89
|
+
{
|
|
90
|
+
envKeys: ['GOOGLE_AI_API_KEY'],
|
|
91
|
+
hint: 'Get free at https://aistudio.google.com/apikey',
|
|
92
|
+
id: 'google-ai',
|
|
93
|
+
label: 'Google AI (Gemini)',
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
envKeys: ['ANTHROPIC_API_KEY'],
|
|
97
|
+
hint: 'Get at https://console.anthropic.com',
|
|
98
|
+
id: 'anthropic',
|
|
99
|
+
label: 'Anthropic (Claude)',
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
envKeys: ['OPENAI_API_KEY'],
|
|
103
|
+
hint: 'Get at https://platform.openai.com/api-keys',
|
|
104
|
+
id: 'openai',
|
|
105
|
+
label: 'OpenAI (GPT)',
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
envKeys: ['MISTRAL_API_KEY'],
|
|
109
|
+
hint: 'Get at https://console.mistral.ai',
|
|
110
|
+
id: 'mistral',
|
|
111
|
+
label: 'Mistral',
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
extraKeys: ['LITELLM_MODEL'],
|
|
115
|
+
envKeys: ['LITELLM_API_KEY', 'LITELLM_BASE_URL'],
|
|
116
|
+
hint: 'Local OpenAI-compatible proxy',
|
|
117
|
+
id: 'litellm',
|
|
118
|
+
label: 'LiteLLM (local)',
|
|
119
|
+
},
|
|
120
|
+
];
|
|
121
|
+
|
|
122
|
+
const AI_ENV_KEYS = [
|
|
123
|
+
'ANTHROPIC_API_KEY',
|
|
124
|
+
'GOOGLE_AI_API_KEY',
|
|
125
|
+
'LITELLM_API_KEY',
|
|
126
|
+
'LITELLM_BASE_URL',
|
|
127
|
+
'LITELLM_MODEL',
|
|
128
|
+
'MISTRAL_API_KEY',
|
|
129
|
+
'NEUROLINK_PROVIDER',
|
|
130
|
+
'OPENAI_API_KEY',
|
|
131
|
+
];
|
|
132
|
+
|
|
133
|
+
// ── Auto-incrementing step counter ──────────────────────────────────
|
|
134
|
+
let _stepNum = 0;
|
|
135
|
+
let _totalSteps = 5;
|
|
136
|
+
function step(label) {
|
|
137
|
+
_stepNum++;
|
|
138
|
+
console.log(`\n${C.blue}[${_stepNum}/${_totalSteps}] ${label}${C.reset}\n`);
|
|
139
|
+
}
|
|
140
|
+
|
|
69
141
|
// ── Readline helpers ─────────────────────────────────────────────────
|
|
70
142
|
|
|
71
143
|
function ask(question) {
|
|
@@ -97,18 +169,6 @@ async function askRequired(question, validator) {
|
|
|
97
169
|
}
|
|
98
170
|
}
|
|
99
171
|
|
|
100
|
-
async function askMultiline(prompt) {
|
|
101
|
-
console.log(cyan(prompt));
|
|
102
|
-
console.log(dim(' Paste the content, then press Enter on an empty line to finish:'));
|
|
103
|
-
const lines = [];
|
|
104
|
-
while (true) {
|
|
105
|
-
const line = await ask('');
|
|
106
|
-
if (line === '') break;
|
|
107
|
-
lines.push(line);
|
|
108
|
-
}
|
|
109
|
-
return lines.join('\n');
|
|
110
|
-
}
|
|
111
|
-
|
|
112
172
|
// ── Banner ───────────────────────────────────────────────────────────
|
|
113
173
|
|
|
114
174
|
function printBanner() {
|
|
@@ -120,7 +180,7 @@ function printBanner() {
|
|
|
120
180
|
console.log(`${C.cyan}${C.bold} |____/|_| |_|\\___/ \\___/ \\__\\___|_| ${C.reset}`);
|
|
121
181
|
console.log('');
|
|
122
182
|
console.log(bold(' Interactive Setup Wizard'));
|
|
123
|
-
console.log(dim('
|
|
183
|
+
console.log(dim(' Remote terminals, session viewer & push notifications'));
|
|
124
184
|
console.log('');
|
|
125
185
|
console.log(dim(' ─────────────────────────────────────────'));
|
|
126
186
|
console.log('');
|
|
@@ -129,7 +189,7 @@ function printBanner() {
|
|
|
129
189
|
// ── Prerequisite checks ─────────────────────────────────────────────
|
|
130
190
|
|
|
131
191
|
function checkPrerequisites() {
|
|
132
|
-
|
|
192
|
+
step('Checking prerequisites...');
|
|
133
193
|
|
|
134
194
|
// Node.js version
|
|
135
195
|
const nodeVersion = process.versions.node;
|
|
@@ -203,77 +263,147 @@ function validateEmail(email) {
|
|
|
203
263
|
return null;
|
|
204
264
|
}
|
|
205
265
|
|
|
206
|
-
// ── Collect
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
apnsKey: '',
|
|
214
|
-
apnsKeyId: '',
|
|
215
|
-
apnsTeamId: '',
|
|
216
|
-
apnsBundleId: '',
|
|
217
|
-
apnsProduction: false,
|
|
218
|
-
deviceToken: '',
|
|
219
|
-
// Android FCM
|
|
220
|
-
wantAndroid: false,
|
|
221
|
-
fcmProjectId: '',
|
|
222
|
-
fcmClientEmail: '',
|
|
223
|
-
fcmPrivateKey: '',
|
|
224
|
-
androidDeviceToken: '',
|
|
225
|
-
};
|
|
266
|
+
// ── Collect AI Provider Config ───────────────────────────────────────
|
|
267
|
+
async function collectAIConfig(config) {
|
|
268
|
+
step('AI-Powered Features (Optional)');
|
|
269
|
+
console.log('');
|
|
270
|
+
console.log(' Shooter uses NeuroLink to generate AI summaries of your coding sessions.');
|
|
271
|
+
console.log(' You can configure one or more AI providers.');
|
|
272
|
+
console.log('');
|
|
226
273
|
|
|
227
|
-
// ── Auto mode: reuse existing key or generate new, skip push config ──
|
|
228
274
|
if (AUTO_MODE) {
|
|
229
|
-
console.log(
|
|
275
|
+
console.log(dim(' Skipping AI provider configuration in --auto mode.'));
|
|
276
|
+
console.log('');
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
230
279
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
280
|
+
const wantAI = await confirm('Configure AI providers?');
|
|
281
|
+
if (!wantAI) {
|
|
282
|
+
console.log(' Skipped — summaries will use basic text fallback.');
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
console.log('');
|
|
287
|
+
console.log(' Available providers:');
|
|
288
|
+
PROVIDER_REGISTRY.forEach((p, i) => {
|
|
289
|
+
console.log(` ${i + 1}. ${p.label}`);
|
|
290
|
+
});
|
|
291
|
+
console.log(` ${PROVIDER_REGISTRY.length + 1}. Skip`);
|
|
292
|
+
console.log('');
|
|
293
|
+
|
|
294
|
+
let configureMore = true;
|
|
295
|
+
while (configureMore) {
|
|
296
|
+
const choice = await askRequired(
|
|
297
|
+
`Choose provider (1-${PROVIDER_REGISTRY.length + 1})`,
|
|
298
|
+
(val) => {
|
|
299
|
+
const n = parseInt(val, 10);
|
|
300
|
+
return n >= 1 && n <= PROVIDER_REGISTRY.length + 1
|
|
301
|
+
? null
|
|
302
|
+
: `Enter a number 1-${PROVIDER_REGISTRY.length + 1}`;
|
|
242
303
|
}
|
|
304
|
+
);
|
|
305
|
+
|
|
306
|
+
const idx = parseInt(choice, 10) - 1;
|
|
307
|
+
if (idx >= PROVIDER_REGISTRY.length) {
|
|
308
|
+
break;
|
|
243
309
|
}
|
|
244
310
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
311
|
+
const provider = PROVIDER_REGISTRY[idx];
|
|
312
|
+
console.log(` ${provider.hint}`);
|
|
313
|
+
|
|
314
|
+
for (const envKey of provider.envKeys) {
|
|
315
|
+
const value = await askRequired(`${envKey}`, (val) =>
|
|
316
|
+
val.length > 10 ? null : 'Key seems too short'
|
|
317
|
+
);
|
|
318
|
+
config[envKey] = value;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// Extra optional keys (e.g., LITELLM_MODEL)
|
|
322
|
+
if (provider.extraKeys) {
|
|
323
|
+
for (const envKey of provider.extraKeys) {
|
|
324
|
+
const value = await ask(`${envKey} (Enter to skip)`);
|
|
325
|
+
if (value) {
|
|
326
|
+
config[envKey] = value;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// Set as preferred provider if first one configured
|
|
332
|
+
if (!config.NEUROLINK_PROVIDER) {
|
|
333
|
+
config.NEUROLINK_PROVIDER = provider.id;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
console.log(` ✓ ${provider.label} configured`);
|
|
249
337
|
console.log('');
|
|
250
|
-
return config;
|
|
251
|
-
}
|
|
252
338
|
|
|
253
|
-
|
|
254
|
-
console.log(bold('2. Server authentication\n'));
|
|
255
|
-
const apiKeyAnswer = await ask(` API_KEY ${dim('(press Enter to auto-generate)')}: `);
|
|
256
|
-
if (apiKeyAnswer) {
|
|
257
|
-
config.apiKey = apiKeyAnswer;
|
|
258
|
-
} else {
|
|
259
|
-
config.apiKey = generateSecureKey();
|
|
260
|
-
const maskedKey = mask(config.apiKey);
|
|
261
|
-
console.log(green(` Generated API key: ${maskedKey}`));
|
|
262
|
-
console.log(dim(' (Saved to ~/.shooter/.env)'));
|
|
339
|
+
configureMore = await confirm('Configure another provider?');
|
|
263
340
|
}
|
|
264
|
-
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// ── Collect configuration ────────────────────────────────────────────
|
|
265
344
|
|
|
345
|
+
// ── Collect push notification config (separate flow) ────────────────
|
|
346
|
+
|
|
347
|
+
async function collectPushConfig(config) {
|
|
266
348
|
// ── iOS Push Notifications ───────────────────────────────────────
|
|
267
|
-
console.log(bold('
|
|
268
|
-
config.wantIos = await confirm('
|
|
349
|
+
console.log(bold(' iOS Push Notifications\n'));
|
|
350
|
+
config.wantIos = await confirm(' Configure iOS push notifications?');
|
|
269
351
|
|
|
270
352
|
if (config.wantIos) {
|
|
271
353
|
console.log('');
|
|
272
354
|
|
|
273
|
-
// APNs key (.p8)
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
355
|
+
// APNs key (.p8) — accept file path or pasted content, with retry on invalid input
|
|
356
|
+
async function askForP8Key() {
|
|
357
|
+
const apnsKeyInput = await askRequired(` APNS_KEY (.p8 file path or paste key): `, (val) => {
|
|
358
|
+
if (!val) return 'APNs key is required.';
|
|
359
|
+
return null;
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
let key;
|
|
363
|
+
|
|
364
|
+
// Check if input is a file path
|
|
365
|
+
if (fs.existsSync(apnsKeyInput)) {
|
|
366
|
+
try {
|
|
367
|
+
key = fs.readFileSync(apnsKeyInput, 'utf-8').trim();
|
|
368
|
+
console.log(green(` Read key from ${apnsKeyInput}`));
|
|
369
|
+
} catch (err) {
|
|
370
|
+
console.log(red(` Could not read file: ${err.message}`));
|
|
371
|
+
process.exit(1);
|
|
372
|
+
}
|
|
373
|
+
} else if (apnsKeyInput.includes('BEGIN PRIVATE KEY')) {
|
|
374
|
+
// User pasted inline content
|
|
375
|
+
key = apnsKeyInput;
|
|
376
|
+
} else {
|
|
377
|
+
// Might be a partial path or multiline paste — try multiline
|
|
378
|
+
console.log(yellow(' Input does not look like a file path or key.'));
|
|
379
|
+
console.log(
|
|
380
|
+
dim(' Paste the full .p8 key contents (press Enter on empty line to finish):')
|
|
381
|
+
);
|
|
382
|
+
const lines = [apnsKeyInput];
|
|
383
|
+
while (true) {
|
|
384
|
+
const line = await ask('');
|
|
385
|
+
if (line === '') break;
|
|
386
|
+
lines.push(line);
|
|
387
|
+
}
|
|
388
|
+
key = lines.join('\n');
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
if (!key.includes('BEGIN PRIVATE KEY')) {
|
|
392
|
+
console.log(yellow(' Warning: File does not appear to be a valid .p8 private key.'));
|
|
393
|
+
const retry = await ask(cyan(' Re-enter path? [Y/n]: '));
|
|
394
|
+
if (retry.toLowerCase() !== 'n') {
|
|
395
|
+
return askForP8Key(); // Recursive retry
|
|
396
|
+
}
|
|
397
|
+
console.log(yellow(' Skipping APNs configuration.'));
|
|
398
|
+
return null;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
return key;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
config.apnsKey = await askForP8Key();
|
|
405
|
+
if (!config.apnsKey) {
|
|
406
|
+
config.wantIos = false;
|
|
277
407
|
}
|
|
278
408
|
console.log('');
|
|
279
409
|
|
|
@@ -302,15 +432,37 @@ async function collectConfig() {
|
|
|
302
432
|
console.log('');
|
|
303
433
|
|
|
304
434
|
// ── Android Push Notifications ───────────────────────────────────
|
|
305
|
-
console.log(bold('
|
|
306
|
-
config.wantAndroid = await confirm('
|
|
435
|
+
console.log(bold(' Android Push Notifications\n'));
|
|
436
|
+
config.wantAndroid = await confirm(' Configure Android push notifications?');
|
|
307
437
|
|
|
308
438
|
if (config.wantAndroid) {
|
|
309
439
|
console.log('');
|
|
310
440
|
config.fcmProjectId = await askRequired(' FCM_PROJECT_ID: ');
|
|
311
441
|
config.fcmClientEmail = await askRequired(' FCM_CLIENT_EMAIL: ', validateEmail);
|
|
312
442
|
|
|
313
|
-
|
|
443
|
+
const fcmKeyInput = await askRequired(' FCM_PRIVATE_KEY (file path or paste key): ');
|
|
444
|
+
|
|
445
|
+
if (fs.existsSync(fcmKeyInput)) {
|
|
446
|
+
try {
|
|
447
|
+
config.fcmPrivateKey = fs.readFileSync(fcmKeyInput, 'utf-8').trim();
|
|
448
|
+
console.log(green(` Read key from ${fcmKeyInput}`));
|
|
449
|
+
} catch (err) {
|
|
450
|
+
console.log(red(` Could not read file: ${err.message}`));
|
|
451
|
+
process.exit(1);
|
|
452
|
+
}
|
|
453
|
+
} else if (fcmKeyInput.includes('BEGIN')) {
|
|
454
|
+
config.fcmPrivateKey = fcmKeyInput;
|
|
455
|
+
} else {
|
|
456
|
+
console.log(dim(' Paste the full private key (press Enter on empty line to finish):'));
|
|
457
|
+
const lines = [fcmKeyInput];
|
|
458
|
+
while (true) {
|
|
459
|
+
const line = await ask('');
|
|
460
|
+
if (line === '') break;
|
|
461
|
+
lines.push(line);
|
|
462
|
+
}
|
|
463
|
+
config.fcmPrivateKey = lines.join('\n');
|
|
464
|
+
}
|
|
465
|
+
|
|
314
466
|
if (!config.fcmPrivateKey.includes('BEGIN')) {
|
|
315
467
|
console.log(
|
|
316
468
|
yellow(' Warning: Key does not look like a PEM private key. Continuing anyway.')
|
|
@@ -326,6 +478,160 @@ async function collectConfig() {
|
|
|
326
478
|
}
|
|
327
479
|
}
|
|
328
480
|
console.log('');
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
// ── Load existing AI config from .env (preserve during re-run) ──────────
|
|
484
|
+
|
|
485
|
+
function loadExistingAIConfig(config) {
|
|
486
|
+
if (!fs.existsSync(DOT_ENV_PATH)) return;
|
|
487
|
+
const existing = fs.readFileSync(DOT_ENV_PATH, 'utf-8');
|
|
488
|
+
|
|
489
|
+
const get = (key) => {
|
|
490
|
+
const m = existing.match(new RegExp(`^${key}=["']?(.+?)["']?$`, 'm'));
|
|
491
|
+
return m ? m[1] : '';
|
|
492
|
+
};
|
|
493
|
+
|
|
494
|
+
for (const key of AI_ENV_KEYS) {
|
|
495
|
+
const value = get(key);
|
|
496
|
+
if (value) {
|
|
497
|
+
config[key] = value;
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
// ── Load existing push config from .env (preserve during non-push setup) ──
|
|
503
|
+
|
|
504
|
+
function loadExistingPushConfig(config) {
|
|
505
|
+
if (!fs.existsSync(DOT_ENV_PATH)) return;
|
|
506
|
+
const existing = fs.readFileSync(DOT_ENV_PATH, 'utf-8');
|
|
507
|
+
|
|
508
|
+
const get = (key) => {
|
|
509
|
+
const m = existing.match(new RegExp(`^${key}=["']?(.+?)["']?$`, 'm'));
|
|
510
|
+
return m ? m[1] : '';
|
|
511
|
+
};
|
|
512
|
+
|
|
513
|
+
// Preserve iOS config if it was previously set
|
|
514
|
+
const apnsKeyId = get('APNS_KEY_ID');
|
|
515
|
+
if (apnsKeyId) {
|
|
516
|
+
config.wantIos = true;
|
|
517
|
+
// Read the raw APNS_KEY (may have escaped newlines)
|
|
518
|
+
const rawApnsKey = get('APNS_KEY');
|
|
519
|
+
config.apnsKey = rawApnsKey ? rawApnsKey.replace(/\\n/g, '\n') : '';
|
|
520
|
+
config.apnsKeyId = apnsKeyId;
|
|
521
|
+
config.apnsTeamId = get('APNS_TEAM_ID');
|
|
522
|
+
config.apnsBundleId = get('APNS_BUNDLE_ID');
|
|
523
|
+
config.apnsProduction = get('APNS_PRODUCTION') === 'true';
|
|
524
|
+
config.deviceToken = get('DEVICE_TOKEN');
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
// Preserve Android config if it was previously set
|
|
528
|
+
const fcmProjectId = get('FCM_PROJECT_ID');
|
|
529
|
+
if (fcmProjectId) {
|
|
530
|
+
config.wantAndroid = true;
|
|
531
|
+
config.fcmProjectId = fcmProjectId;
|
|
532
|
+
config.fcmClientEmail = get('FCM_CLIENT_EMAIL');
|
|
533
|
+
const rawFcmKey = get('FCM_PRIVATE_KEY');
|
|
534
|
+
config.fcmPrivateKey = rawFcmKey ? rawFcmKey.replace(/\\n/g, '\n') : '';
|
|
535
|
+
config.androidDeviceToken = get('ANDROID_DEVICE_TOKEN');
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
async function collectConfig() {
|
|
540
|
+
const config = {
|
|
541
|
+
apiKey: '',
|
|
542
|
+
// iOS APNs
|
|
543
|
+
wantIos: false,
|
|
544
|
+
apnsKey: '',
|
|
545
|
+
apnsKeyId: '',
|
|
546
|
+
apnsTeamId: '',
|
|
547
|
+
apnsBundleId: '',
|
|
548
|
+
apnsProduction: false,
|
|
549
|
+
deviceToken: '',
|
|
550
|
+
// Android FCM
|
|
551
|
+
wantAndroid: false,
|
|
552
|
+
fcmProjectId: '',
|
|
553
|
+
fcmClientEmail: '',
|
|
554
|
+
fcmPrivateKey: '',
|
|
555
|
+
androidDeviceToken: '',
|
|
556
|
+
};
|
|
557
|
+
|
|
558
|
+
// ── Auto mode: reuse existing key or generate new, skip push config ──
|
|
559
|
+
if (AUTO_MODE) {
|
|
560
|
+
step('Auto-configuring...');
|
|
561
|
+
|
|
562
|
+
// Preserve existing API_KEY if .env already exists
|
|
563
|
+
if (fs.existsSync(DOT_ENV_PATH)) {
|
|
564
|
+
const existing = fs.readFileSync(DOT_ENV_PATH, 'utf-8');
|
|
565
|
+
const match = existing.match(/^API_KEY=["']?(.+?)["']?$/m);
|
|
566
|
+
if (match && match[1]) {
|
|
567
|
+
config.apiKey = match[1];
|
|
568
|
+
const maskedKey = mask(config.apiKey);
|
|
569
|
+
console.log(green(` Existing API key preserved: ${maskedKey}`));
|
|
570
|
+
loadExistingPushConfig(config);
|
|
571
|
+
loadExistingAIConfig(config); // Load existing AI config in auto mode
|
|
572
|
+
if (config.wantIos || config.wantAndroid) {
|
|
573
|
+
console.log(green(' Existing push config preserved.'));
|
|
574
|
+
} else {
|
|
575
|
+
console.log(
|
|
576
|
+
dim(' Push notifications not configured (add later with "shooter setup --push")')
|
|
577
|
+
);
|
|
578
|
+
}
|
|
579
|
+
if (config.NEUROLINK_PROVIDER) {
|
|
580
|
+
console.log(green(' Existing AI config preserved.'));
|
|
581
|
+
} else {
|
|
582
|
+
console.log(dim(' AI providers not configured (add later with "shooter setup")'));
|
|
583
|
+
}
|
|
584
|
+
console.log('');
|
|
585
|
+
return config;
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
config.apiKey = generateApiKey(); // Changed to generateApiKey()
|
|
590
|
+
const maskedKey = mask(config.apiKey);
|
|
591
|
+
console.log(green(` API key generated: ${maskedKey}`));
|
|
592
|
+
console.log(dim(' Push notifications not configured (add later with "shooter setup --push")'));
|
|
593
|
+
console.log(dim(' AI providers not configured (add later with "shooter setup")'));
|
|
594
|
+
console.log('');
|
|
595
|
+
return config;
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
// ── API Key ──────────────────────────────────────────────────────
|
|
599
|
+
step('Server authentication');
|
|
600
|
+
const apiKeyAnswer = await ask(` API_KEY ${dim('(press Enter to auto-generate)')}: `);
|
|
601
|
+
if (apiKeyAnswer) {
|
|
602
|
+
config.apiKey = apiKeyAnswer;
|
|
603
|
+
} else {
|
|
604
|
+
config.apiKey = generateApiKey(); // Changed to generateApiKey()
|
|
605
|
+
const maskedKey = mask(config.apiKey);
|
|
606
|
+
console.log(green(` Generated API key: ${maskedKey}`));
|
|
607
|
+
console.log(dim(' (Saved to ~/.shooter/.env)'));
|
|
608
|
+
}
|
|
609
|
+
console.log('');
|
|
610
|
+
|
|
611
|
+
// ── Push notifications: only if --push flag or user opts in ──────
|
|
612
|
+
if (PUSH_MODE) {
|
|
613
|
+
// Direct push config mode — user explicitly asked for it
|
|
614
|
+
step('Push notification setup');
|
|
615
|
+
await collectPushConfig(config);
|
|
616
|
+
} else {
|
|
617
|
+
// Default: skip push, show how to add later
|
|
618
|
+
// Preserve any existing push config from a previous setup
|
|
619
|
+
loadExistingPushConfig(config);
|
|
620
|
+
|
|
621
|
+
if (config.wantIos || config.wantAndroid) {
|
|
622
|
+
console.log(dim(' Existing push notification config preserved.'));
|
|
623
|
+
console.log(dim(' To reconfigure: shooter setup --push'));
|
|
624
|
+
} else {
|
|
625
|
+
console.log(dim(' Push notifications are optional and not required for the server.'));
|
|
626
|
+
console.log(dim(' Terminals, sessions, and the web UI work without push config.'));
|
|
627
|
+
console.log(dim(` Add push later: ${cyan('shooter setup --push')}`));
|
|
628
|
+
}
|
|
629
|
+
console.log('');
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
// ── AI Providers ───────────────────────────────────────────────────
|
|
633
|
+
loadExistingAIConfig(config);
|
|
634
|
+
await collectAIConfig(config);
|
|
329
635
|
|
|
330
636
|
return config;
|
|
331
637
|
}
|
|
@@ -338,7 +644,7 @@ function buildEnvContent(config) {
|
|
|
338
644
|
`# ${new Date().toISOString()}`,
|
|
339
645
|
'',
|
|
340
646
|
'# Authentication',
|
|
341
|
-
`API_KEY
|
|
647
|
+
`API_KEY=${config.apiKey}`,
|
|
342
648
|
'',
|
|
343
649
|
];
|
|
344
650
|
|
|
@@ -398,6 +704,17 @@ function buildEnvContent(config) {
|
|
|
398
704
|
}
|
|
399
705
|
lines.push('');
|
|
400
706
|
|
|
707
|
+
// AI / NeuroLink — preserve existing AI keys across re-runs
|
|
708
|
+
lines.push('# AI / NeuroLink');
|
|
709
|
+
for (const key of AI_ENV_KEYS) {
|
|
710
|
+
if (config[key]) {
|
|
711
|
+
lines.push(`${key}=${config[key]}`);
|
|
712
|
+
} else {
|
|
713
|
+
lines.push(`# ${key}=`);
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
lines.push('');
|
|
717
|
+
|
|
401
718
|
// Server
|
|
402
719
|
lines.push('# Server');
|
|
403
720
|
lines.push('# PORT=54007');
|
|
@@ -407,7 +724,7 @@ function buildEnvContent(config) {
|
|
|
407
724
|
}
|
|
408
725
|
|
|
409
726
|
async function writeEnv(config) {
|
|
410
|
-
|
|
727
|
+
step('Writing .env file');
|
|
411
728
|
|
|
412
729
|
if (fs.existsSync(DOT_ENV_PATH) && !AUTO_MODE) {
|
|
413
730
|
const overwrite = await confirm(' .env already exists. Overwrite it?');
|
|
@@ -427,69 +744,17 @@ async function writeEnv(config) {
|
|
|
427
744
|
}
|
|
428
745
|
|
|
429
746
|
fs.writeFileSync(DOT_ENV_PATH, content, { mode: 0o600 });
|
|
747
|
+
// Enforce secure permissions even if file already existed
|
|
748
|
+
fs.chmodSync(DOT_ENV_PATH, 0o600);
|
|
430
749
|
console.log(green(' .env written successfully.'));
|
|
431
750
|
console.log('');
|
|
432
751
|
return true;
|
|
433
752
|
}
|
|
434
753
|
|
|
435
|
-
// ── Shell profile export ─────────────────────────────────────────────
|
|
436
|
-
|
|
437
|
-
function detectShellProfile() {
|
|
438
|
-
const home = require('os').homedir();
|
|
439
|
-
const shell = process.env.SHELL || '';
|
|
440
|
-
|
|
441
|
-
if (shell.endsWith('/zsh')) {
|
|
442
|
-
return path.join(home, '.zshrc');
|
|
443
|
-
}
|
|
444
|
-
if (shell.endsWith('/bash')) {
|
|
445
|
-
// macOS uses .bash_profile for login shells; Linux uses .bashrc
|
|
446
|
-
const bashProfile = path.join(home, '.bash_profile');
|
|
447
|
-
if (fs.existsSync(bashProfile)) return bashProfile;
|
|
448
|
-
return path.join(home, '.bashrc');
|
|
449
|
-
}
|
|
450
|
-
// Fallback for other shells
|
|
451
|
-
return path.join(home, '.profile');
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
async function offerShellExport(apiKey) {
|
|
455
|
-
console.log(bold('6. Shell environment\n'));
|
|
456
|
-
|
|
457
|
-
const profilePath = detectShellProfile();
|
|
458
|
-
const profileName = path.basename(profilePath);
|
|
459
|
-
|
|
460
|
-
// Check if export already exists
|
|
461
|
-
if (fs.existsSync(profilePath)) {
|
|
462
|
-
const contents = fs.readFileSync(profilePath, 'utf-8');
|
|
463
|
-
if (contents.includes('export API_KEY=')) {
|
|
464
|
-
console.log(green(` export API_KEY is already in ~/${profileName}`));
|
|
465
|
-
console.log('');
|
|
466
|
-
return;
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
console.log(dim(' The Claude Code hooks need API_KEY in your shell environment.'));
|
|
471
|
-
const shouldAdd = AUTO_MODE || await confirm(` Add 'export API_KEY=...' to ~/${profileName}?`);
|
|
472
|
-
|
|
473
|
-
if (shouldAdd) {
|
|
474
|
-
const exportLine = `\nexport API_KEY="${escapeForDoubleQuotedShell(apiKey)}"\n`;
|
|
475
|
-
fs.appendFileSync(profilePath, exportLine, 'utf-8');
|
|
476
|
-
console.log(green(` Added to ~/${profileName}`));
|
|
477
|
-
console.log(
|
|
478
|
-
yellow(` Run ${cyan(`source ~/${profileName}`)} or open a new terminal for hooks to work.`)
|
|
479
|
-
);
|
|
480
|
-
} else {
|
|
481
|
-
console.log(
|
|
482
|
-
yellow(' Skipped. You will need to set API_KEY manually for hooks to authenticate.')
|
|
483
|
-
);
|
|
484
|
-
console.log(dim(` Add this to your shell profile: export API_KEY="<your key from .env>"`));
|
|
485
|
-
}
|
|
486
|
-
console.log('');
|
|
487
|
-
}
|
|
488
|
-
|
|
489
754
|
// ── Build ────────────────────────────────────────────────────────────
|
|
490
755
|
|
|
491
756
|
function runBuild() {
|
|
492
|
-
|
|
757
|
+
step('Building the project...');
|
|
493
758
|
try {
|
|
494
759
|
execSync('pnpm build', { cwd: ROOT, stdio: 'inherit' });
|
|
495
760
|
console.log('');
|
|
@@ -505,107 +770,6 @@ function runBuild() {
|
|
|
505
770
|
}
|
|
506
771
|
}
|
|
507
772
|
|
|
508
|
-
// ── Health check ─────────────────────────────────────────────────────
|
|
509
|
-
|
|
510
|
-
function testHealth() {
|
|
511
|
-
return new Promise((resolve) => {
|
|
512
|
-
console.log(bold('9. Testing server health...\n'));
|
|
513
|
-
|
|
514
|
-
const port = process.env.PORT || 54007;
|
|
515
|
-
let serverProcess;
|
|
516
|
-
let resolved = false;
|
|
517
|
-
|
|
518
|
-
function finish(ok, msg) {
|
|
519
|
-
if (resolved) return;
|
|
520
|
-
resolved = true;
|
|
521
|
-
if (serverProcess && !serverProcess.killed) {
|
|
522
|
-
serverProcess.kill('SIGTERM');
|
|
523
|
-
}
|
|
524
|
-
if (ok) {
|
|
525
|
-
console.log(green(` ${msg}`));
|
|
526
|
-
} else {
|
|
527
|
-
console.log(yellow(` ${msg}`));
|
|
528
|
-
}
|
|
529
|
-
console.log('');
|
|
530
|
-
resolve(ok);
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
// Start the server
|
|
534
|
-
serverProcess = spawn('node', ['--import', 'tsx', 'server.ts'], {
|
|
535
|
-
cwd: ROOT,
|
|
536
|
-
stdio: 'pipe',
|
|
537
|
-
env: { ...process.env, PORT: port.toString(), SHOOTER_HOME, SHOOTER_PKG_ROOT: ROOT },
|
|
538
|
-
});
|
|
539
|
-
|
|
540
|
-
serverProcess.on('error', (err) => {
|
|
541
|
-
finish(false, `Could not start server: ${err.message}`);
|
|
542
|
-
});
|
|
543
|
-
|
|
544
|
-
serverProcess.on('exit', (code) => {
|
|
545
|
-
if (!resolved) {
|
|
546
|
-
finish(false, `Server exited unexpectedly (code ${code}).`);
|
|
547
|
-
}
|
|
548
|
-
});
|
|
549
|
-
|
|
550
|
-
// Capture stderr/stdout for "listening" signal; try health after delay
|
|
551
|
-
let output = '';
|
|
552
|
-
const collectOutput = (data) => {
|
|
553
|
-
output += data.toString();
|
|
554
|
-
};
|
|
555
|
-
serverProcess.stdout.on('data', collectOutput);
|
|
556
|
-
serverProcess.stderr.on('data', collectOutput);
|
|
557
|
-
|
|
558
|
-
// Give the server up to 15 seconds to start, polling every second
|
|
559
|
-
let attempts = 0;
|
|
560
|
-
const maxAttempts = 15;
|
|
561
|
-
|
|
562
|
-
const poll = setInterval(() => {
|
|
563
|
-
attempts++;
|
|
564
|
-
if (resolved) {
|
|
565
|
-
clearInterval(poll);
|
|
566
|
-
return;
|
|
567
|
-
}
|
|
568
|
-
if (attempts > maxAttempts) {
|
|
569
|
-
clearInterval(poll);
|
|
570
|
-
finish(
|
|
571
|
-
false,
|
|
572
|
-
'Server did not respond within 15 seconds. You can test manually with: curl http://localhost:' +
|
|
573
|
-
port +
|
|
574
|
-
'/api/health'
|
|
575
|
-
);
|
|
576
|
-
return;
|
|
577
|
-
}
|
|
578
|
-
|
|
579
|
-
const req = http.get(`http://localhost:${port}/api/health`, (res) => {
|
|
580
|
-
let body = '';
|
|
581
|
-
res.on('data', (chunk) => {
|
|
582
|
-
body += chunk;
|
|
583
|
-
});
|
|
584
|
-
res.on('end', () => {
|
|
585
|
-
clearInterval(poll);
|
|
586
|
-
try {
|
|
587
|
-
const data = JSON.parse(body);
|
|
588
|
-
if (data.status === 'healthy') {
|
|
589
|
-
finish(true, `Health check passed: status=${data.status}`);
|
|
590
|
-
} else {
|
|
591
|
-
finish(
|
|
592
|
-
true,
|
|
593
|
-
`Server running (status=${data.status}). Some optional features may need configuration.`
|
|
594
|
-
);
|
|
595
|
-
}
|
|
596
|
-
} catch {
|
|
597
|
-
finish(true, 'Server responded but health response was not JSON.');
|
|
598
|
-
}
|
|
599
|
-
});
|
|
600
|
-
});
|
|
601
|
-
req.on('error', () => {
|
|
602
|
-
// Server not ready yet — will retry
|
|
603
|
-
});
|
|
604
|
-
req.setTimeout(2000, () => req.destroy());
|
|
605
|
-
}, 1000);
|
|
606
|
-
});
|
|
607
|
-
}
|
|
608
|
-
|
|
609
773
|
// ── Main ─────────────────────────────────────────────────────────────
|
|
610
774
|
|
|
611
775
|
async function main() {
|
|
@@ -625,17 +789,30 @@ async function main() {
|
|
|
625
789
|
rl.close();
|
|
626
790
|
});
|
|
627
791
|
|
|
792
|
+
// Reset step counter; push mode adds one extra step, AI step adds another
|
|
793
|
+
_stepNum = 0;
|
|
794
|
+
_totalSteps = AUTO_MODE ? 5 : PUSH_MODE ? 7 : 6;
|
|
795
|
+
|
|
628
796
|
printBanner();
|
|
797
|
+
|
|
798
|
+
if (PUSH_MODE) {
|
|
799
|
+
console.log(bold(' Adding push notification support...\n'));
|
|
800
|
+
}
|
|
801
|
+
|
|
629
802
|
checkPrerequisites();
|
|
630
803
|
|
|
631
804
|
const config = await collectConfig();
|
|
632
805
|
await writeEnv(config);
|
|
633
|
-
|
|
806
|
+
|
|
807
|
+
// API_KEY is stored in ~/.shooter/.env — hooks read it automatically
|
|
808
|
+
console.log(dim(' API_KEY is stored in ~/.shooter/.env'));
|
|
809
|
+
console.log(dim(' Claude Code hooks read it automatically from there.'));
|
|
810
|
+
console.log('');
|
|
634
811
|
|
|
635
812
|
const buildOk = runBuild();
|
|
636
813
|
|
|
637
814
|
// ── Remote access info ───────────────────────────────────────────────
|
|
638
|
-
|
|
815
|
+
step('Remote access (optional)');
|
|
639
816
|
|
|
640
817
|
let cloudflaredAvailable = false;
|
|
641
818
|
try {
|
|
@@ -648,14 +825,19 @@ async function main() {
|
|
|
648
825
|
if (!cloudflaredAvailable) {
|
|
649
826
|
console.log(yellow(' cloudflared not found.'));
|
|
650
827
|
console.log(dim(' Install it to get a public URL for your phone:'));
|
|
651
|
-
console.log(
|
|
828
|
+
console.log(
|
|
829
|
+
dim(
|
|
830
|
+
' https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/downloads/'
|
|
831
|
+
)
|
|
832
|
+
);
|
|
652
833
|
console.log('');
|
|
653
834
|
console.log(dim(' Once installed, run:'));
|
|
654
835
|
console.log(cyan(' cloudflared tunnel --url http://localhost:54007'));
|
|
655
836
|
console.log('');
|
|
656
837
|
} else {
|
|
657
838
|
const port = process.env.PORT || 54007;
|
|
658
|
-
const startTunnel =
|
|
839
|
+
const startTunnel =
|
|
840
|
+
AUTO_MODE || (await confirm(' Start a Cloudflare Tunnel now to get your public URL?'));
|
|
659
841
|
if (startTunnel) {
|
|
660
842
|
console.log(dim('\n Starting tunnel... (this may take a few seconds)\n'));
|
|
661
843
|
await new Promise((resolve) => {
|
|
@@ -672,7 +854,11 @@ async function main() {
|
|
|
672
854
|
console.log(green(' Tunnel URL (use this on your phone):'));
|
|
673
855
|
console.log(`\n ${C.bold}${C.cyan}${match[0]}${C.reset}\n`);
|
|
674
856
|
console.log(dim(' Keep this terminal session open to maintain the tunnel.'));
|
|
675
|
-
console.log(
|
|
857
|
+
console.log(
|
|
858
|
+
dim(
|
|
859
|
+
' For a persistent tunnel, see: https://developers.cloudflare.com/cloudflare-one/'
|
|
860
|
+
)
|
|
861
|
+
);
|
|
676
862
|
console.log('');
|
|
677
863
|
resolve();
|
|
678
864
|
}
|
|
@@ -710,7 +896,8 @@ async function main() {
|
|
|
710
896
|
rl.close();
|
|
711
897
|
|
|
712
898
|
if (buildOk) {
|
|
713
|
-
|
|
899
|
+
console.log(green(' Ready to start! Run: shooter start'));
|
|
900
|
+
console.log('');
|
|
714
901
|
}
|
|
715
902
|
|
|
716
903
|
// ── Done ───────────────────────────────────────────────────────────
|
|
@@ -726,8 +913,17 @@ async function main() {
|
|
|
726
913
|
console.log(bold(' Your API key (enter this in the app on your phone):'));
|
|
727
914
|
console.log(`\n ${C.bold}${C.cyan}${mask(config.apiKey)}${C.reset}\n`);
|
|
728
915
|
if (!config.wantIos && !config.wantAndroid) {
|
|
729
|
-
console.log(
|
|
730
|
-
console.log(dim('
|
|
916
|
+
console.log(bold(' Optional add-ons:'));
|
|
917
|
+
console.log(` ${dim('Push notifications:')} ${cyan('shooter setup --push')}`);
|
|
918
|
+
console.log(
|
|
919
|
+
` ${dim('Cloudflare Tunnel:')} ${cyan('shooter start')} ${dim('(auto-starts tunnel)')}`
|
|
920
|
+
);
|
|
921
|
+
console.log('');
|
|
922
|
+
} else {
|
|
923
|
+
const platforms = [];
|
|
924
|
+
if (config.wantIos) platforms.push('iOS');
|
|
925
|
+
if (config.wantAndroid) platforms.push('Android');
|
|
926
|
+
console.log(green(` Push notifications: ${platforms.join(' + ')} configured`));
|
|
731
927
|
console.log('');
|
|
732
928
|
}
|
|
733
929
|
|