@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
|
@@ -0,0 +1,478 @@
|
|
|
1
|
+
// Activity feed store — session-based polling and WebSocket streaming.
|
|
2
|
+
// Polls for active sessions, connects to each session's WebSocket, buffers events,
|
|
3
|
+
// and triggers AI summarization.
|
|
4
|
+
|
|
5
|
+
import type {
|
|
6
|
+
ActivityEvent,
|
|
7
|
+
ActivitySessionMessage,
|
|
8
|
+
ActivitySessionRecord,
|
|
9
|
+
ActivitySummary,
|
|
10
|
+
} from '$lib/types';
|
|
11
|
+
|
|
12
|
+
import { getApiKey } from '$lib/modules/client/common';
|
|
13
|
+
import { SvelteMap } from 'svelte/reactivity';
|
|
14
|
+
|
|
15
|
+
import { summarizeEvents } from './summarizer';
|
|
16
|
+
|
|
17
|
+
// -- Constants ------------------------------------------------------------
|
|
18
|
+
|
|
19
|
+
const MAX_EVENTS = 200;
|
|
20
|
+
const MAX_SUMMARIES = 50;
|
|
21
|
+
const SUMMARY_BATCH_SIZE = 5;
|
|
22
|
+
const SUMMARY_INTERVAL_MS = 15_000;
|
|
23
|
+
const POLL_INTERVAL_MS = 15_000;
|
|
24
|
+
const ACTIVE_THRESHOLD_MINUTES = 5;
|
|
25
|
+
|
|
26
|
+
// -- State (Svelte 5 runes) -----------------------------------------------
|
|
27
|
+
|
|
28
|
+
let events = $state<ActivityEvent[]>([]);
|
|
29
|
+
let summaries = $state<ActivitySummary[]>([]);
|
|
30
|
+
let aiAvailable = $state(true);
|
|
31
|
+
let connected = $state(false);
|
|
32
|
+
let summarizing = $state(false);
|
|
33
|
+
|
|
34
|
+
let pollTimer: null | ReturnType<typeof setInterval> = null;
|
|
35
|
+
let summaryTimer: null | ReturnType<typeof setInterval> = null;
|
|
36
|
+
let counter = 0;
|
|
37
|
+
|
|
38
|
+
// Per-session WebSocket connections
|
|
39
|
+
const sessionSockets = new SvelteMap<string, WebSocket>();
|
|
40
|
+
|
|
41
|
+
// Stored API key for polling
|
|
42
|
+
let storedApiKey = '';
|
|
43
|
+
let storedTicketFetcher: (() => Promise<string>) | null = null;
|
|
44
|
+
|
|
45
|
+
// -- Public API -----------------------------------------------------------
|
|
46
|
+
|
|
47
|
+
export async function connect(ticketFetcher: () => Promise<string>): Promise<void> {
|
|
48
|
+
if (pollTimer) {
|
|
49
|
+
// Already connected
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
storedApiKey = getApiKey();
|
|
54
|
+
if (!storedApiKey) {
|
|
55
|
+
console.warn('[ActivityStore] No API key available');
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
storedTicketFetcher = ticketFetcher;
|
|
60
|
+
|
|
61
|
+
// Initial session discovery
|
|
62
|
+
await discoverAndConnectSessions();
|
|
63
|
+
|
|
64
|
+
// Start polling for new sessions every 15s
|
|
65
|
+
pollTimer = setInterval(() => {
|
|
66
|
+
void discoverAndConnectSessions();
|
|
67
|
+
}, POLL_INTERVAL_MS);
|
|
68
|
+
|
|
69
|
+
// Start periodic summarization
|
|
70
|
+
summaryTimer = setInterval(() => {
|
|
71
|
+
void tryGenerateSummary();
|
|
72
|
+
}, SUMMARY_INTERVAL_MS);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function disconnect(): void {
|
|
76
|
+
if (pollTimer) {
|
|
77
|
+
clearInterval(pollTimer);
|
|
78
|
+
pollTimer = null;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (summaryTimer) {
|
|
82
|
+
clearInterval(summaryTimer);
|
|
83
|
+
summaryTimer = null;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Close all session sockets
|
|
87
|
+
for (const [sessionId, sock] of sessionSockets) {
|
|
88
|
+
sock.onclose = null;
|
|
89
|
+
sock.close();
|
|
90
|
+
console.log(`[ActivityStore] Closed session socket for ${sessionId}`);
|
|
91
|
+
}
|
|
92
|
+
sessionSockets.clear();
|
|
93
|
+
|
|
94
|
+
// Reset in-memory feed so reconnect starts clean (no duplicate history replay
|
|
95
|
+
// or stale summary/banner state carried forward).
|
|
96
|
+
events = [];
|
|
97
|
+
summaries = [];
|
|
98
|
+
counter = 0;
|
|
99
|
+
aiAvailable = true;
|
|
100
|
+
summarizing = false;
|
|
101
|
+
connected = false;
|
|
102
|
+
storedApiKey = '';
|
|
103
|
+
storedTicketFetcher = null;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export function getEvents(): ActivityEvent[] {
|
|
107
|
+
return events;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export function getSummaries(): ActivitySummary[] {
|
|
111
|
+
return summaries;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export function isAiAvailable(): boolean {
|
|
115
|
+
return aiAvailable;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export function isConnected(): boolean {
|
|
119
|
+
return connected;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export function isSummarizing(): boolean {
|
|
123
|
+
return summarizing;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// -- Internal: Session Discovery ------------------------------------------
|
|
127
|
+
|
|
128
|
+
/** Add a typed event to the activity buffer. */
|
|
129
|
+
function addActivityEvent(
|
|
130
|
+
session: ActivitySessionRecord,
|
|
131
|
+
eventType: string,
|
|
132
|
+
eventData: Record<string, unknown>,
|
|
133
|
+
timestamp?: string
|
|
134
|
+
): void {
|
|
135
|
+
const event: ActivityEvent = {
|
|
136
|
+
data: eventData,
|
|
137
|
+
id: `evt-${++counter}`,
|
|
138
|
+
projectName: session.projectName,
|
|
139
|
+
sessionId: session.id,
|
|
140
|
+
summarized: false,
|
|
141
|
+
timestamp: timestamp || new Date().toISOString(),
|
|
142
|
+
type: eventType,
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
events = [...events, event].slice(-MAX_EVENTS);
|
|
146
|
+
|
|
147
|
+
// Trigger summary when batch size reached
|
|
148
|
+
const pending = events.filter((e) => !e.summarized);
|
|
149
|
+
if (pending.length >= SUMMARY_BATCH_SIZE) {
|
|
150
|
+
void tryGenerateSummary();
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
async function connectSessionSocket(session: ActivitySessionRecord): Promise<void> {
|
|
155
|
+
if (!storedTicketFetcher) {
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
try {
|
|
160
|
+
const ticket = await storedTicketFetcher();
|
|
161
|
+
const wsBase = window.location.origin.replace(/^http/, 'ws');
|
|
162
|
+
const wsUrl = `${wsBase}/ws/session/${session.id}?ticket=${ticket}`;
|
|
163
|
+
|
|
164
|
+
console.log(`[ActivityStore] Connecting to session ${session.id}`);
|
|
165
|
+
const sock = new WebSocket(wsUrl);
|
|
166
|
+
sessionSockets.set(session.id, sock);
|
|
167
|
+
|
|
168
|
+
sock.onopen = (): void => {
|
|
169
|
+
console.log(`[ActivityStore] Session socket opened: ${session.id}`);
|
|
170
|
+
connected = true;
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
sock.onmessage = (msg: MessageEvent): void => {
|
|
174
|
+
try {
|
|
175
|
+
const raw: unknown = JSON.parse(msg.data as string);
|
|
176
|
+
if (!raw || typeof raw !== 'object') {
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const data = raw as Record<string, unknown>;
|
|
181
|
+
handleActivitySessionMessage(session, data);
|
|
182
|
+
|
|
183
|
+
// Trigger immediate summary after receiving history batch
|
|
184
|
+
if (data.type === 'history') {
|
|
185
|
+
void tryGenerateSummary();
|
|
186
|
+
}
|
|
187
|
+
} catch {
|
|
188
|
+
// ignore malformed
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
sock.onclose = (): void => {
|
|
193
|
+
console.log(`[ActivityStore] Session socket closed: ${session.id}`);
|
|
194
|
+
sessionSockets.delete(session.id);
|
|
195
|
+
connected = sessionSockets.size > 0;
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
sock.onerror = (err: Event): void => {
|
|
199
|
+
console.warn(`[ActivityStore] Session socket error for ${session.id}:`, err);
|
|
200
|
+
sessionSockets.delete(session.id);
|
|
201
|
+
connected = sessionSockets.size > 0;
|
|
202
|
+
};
|
|
203
|
+
} catch (err) {
|
|
204
|
+
console.warn(`[ActivityStore] Failed to connect session ${session.id}:`, err);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
async function discoverAndConnectSessions(): Promise<void> {
|
|
209
|
+
if (!storedApiKey) {
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
try {
|
|
214
|
+
const res = await fetch('/api/sessions?limit=50', {
|
|
215
|
+
headers: { Authorization: `Bearer ${storedApiKey}` },
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
if (!res.ok) {
|
|
219
|
+
console.warn('[ActivityStore] Failed to fetch sessions:', res.status);
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const body = (await res.json()) as {
|
|
224
|
+
projects?: {
|
|
225
|
+
fullPath: string;
|
|
226
|
+
id: string;
|
|
227
|
+
lastModified: string;
|
|
228
|
+
name: string;
|
|
229
|
+
sessions: {
|
|
230
|
+
created: string;
|
|
231
|
+
id: string;
|
|
232
|
+
messageCount: number;
|
|
233
|
+
modified: string;
|
|
234
|
+
title: string;
|
|
235
|
+
}[];
|
|
236
|
+
}[];
|
|
237
|
+
};
|
|
238
|
+
const projects = body.projects ?? [];
|
|
239
|
+
|
|
240
|
+
// Flatten projects into session records, filter to sessions modified in last 5 minutes
|
|
241
|
+
const cutoff = Date.now() - ACTIVE_THRESHOLD_MINUTES * 60 * 1000;
|
|
242
|
+
const activeSessions: ActivitySessionRecord[] = [];
|
|
243
|
+
for (const project of projects) {
|
|
244
|
+
for (const session of project.sessions) {
|
|
245
|
+
const modified = Date.parse(session.modified);
|
|
246
|
+
if (modified > cutoff) {
|
|
247
|
+
activeSessions.push({
|
|
248
|
+
createdAt: session.created,
|
|
249
|
+
id: session.id,
|
|
250
|
+
modifiedAt: session.modified,
|
|
251
|
+
projectName: project.name,
|
|
252
|
+
projectPath: project.fullPath,
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
const activeIds = new Set(activeSessions.map((s) => s.id));
|
|
259
|
+
|
|
260
|
+
// Close sockets for sessions that are no longer active
|
|
261
|
+
for (const [sessionId, sock] of sessionSockets) {
|
|
262
|
+
if (!activeIds.has(sessionId)) {
|
|
263
|
+
sock.onclose = null;
|
|
264
|
+
sock.close();
|
|
265
|
+
sessionSockets.delete(sessionId);
|
|
266
|
+
console.log(`[ActivityStore] Session ${sessionId} went stale, closed socket`);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// Open sockets for new active sessions
|
|
271
|
+
for (const session of activeSessions) {
|
|
272
|
+
if (!sessionSockets.has(session.id)) {
|
|
273
|
+
void connectSessionSocket(session);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
connected = sessionSockets.size > 0;
|
|
278
|
+
} catch (err) {
|
|
279
|
+
console.warn('[ActivityStore] Error discovering sessions:', err);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
function handleActivitySessionMessage(
|
|
284
|
+
session: ActivitySessionRecord,
|
|
285
|
+
data: Record<string, unknown>
|
|
286
|
+
): void {
|
|
287
|
+
const type = data.type as string | undefined;
|
|
288
|
+
|
|
289
|
+
if (type === 'history' && Array.isArray(data.messages)) {
|
|
290
|
+
// Initial batch — process last 20 for context
|
|
291
|
+
const messages = data.messages as ActivitySessionMessage[];
|
|
292
|
+
const recentMessages = messages.slice(-20);
|
|
293
|
+
for (const msg of recentMessages) {
|
|
294
|
+
processMessage(session, msg);
|
|
295
|
+
}
|
|
296
|
+
} else if (type === 'message') {
|
|
297
|
+
// Conversation message (role + content)
|
|
298
|
+
processMessage(session, {
|
|
299
|
+
content: (data.content as ActivitySessionMessage['content']) || '',
|
|
300
|
+
role: (data.role as string) || 'assistant',
|
|
301
|
+
timestamp: data.timestamp as string,
|
|
302
|
+
});
|
|
303
|
+
} else if (type === 'tool-use') {
|
|
304
|
+
// Tool execution started
|
|
305
|
+
const name = (data.name as string) || 'unknown';
|
|
306
|
+
const input =
|
|
307
|
+
typeof data.input === 'object' && data.input !== null
|
|
308
|
+
? (data.input as Record<string, unknown>)
|
|
309
|
+
: {};
|
|
310
|
+
const filePath = (input.file_path as string) || (input.path as string) || '';
|
|
311
|
+
const command = (input.command as string) || '';
|
|
312
|
+
const pattern = (input.pattern as string) || '';
|
|
313
|
+
addActivityEvent(session, 'tool_use', {
|
|
314
|
+
command: command.slice(0, 120),
|
|
315
|
+
filePath: filePath.slice(0, 200),
|
|
316
|
+
name,
|
|
317
|
+
pattern: pattern.slice(0, 100),
|
|
318
|
+
tool: name,
|
|
319
|
+
});
|
|
320
|
+
} else if (type === 'tool-result') {
|
|
321
|
+
// Tool result received
|
|
322
|
+
const isError = data.isError === true;
|
|
323
|
+
const output = ((data.output as string) || '').slice(0, 200);
|
|
324
|
+
// Fall back to the last tool_use event's name when data.name is missing
|
|
325
|
+
let toolName = (data.name as string) || '';
|
|
326
|
+
if (!toolName) {
|
|
327
|
+
for (let i = events.length - 1; i >= 0; i--) {
|
|
328
|
+
if (events[i].type === 'tool_use' && events[i].data.name) {
|
|
329
|
+
toolName = events[i].data.name as string;
|
|
330
|
+
break;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
addActivityEvent(session, 'tool_result', {
|
|
335
|
+
is_error: isError,
|
|
336
|
+
output,
|
|
337
|
+
status: isError ? 'error' : 'done',
|
|
338
|
+
tool: toolName,
|
|
339
|
+
});
|
|
340
|
+
if (isError) {
|
|
341
|
+
void tryGenerateSummary();
|
|
342
|
+
}
|
|
343
|
+
} else if (type === 'thinking') {
|
|
344
|
+
// Skip thinking blocks — too verbose for activity feed
|
|
345
|
+
} else if (type === 'error') {
|
|
346
|
+
addActivityEvent(session, 'error', {
|
|
347
|
+
message: (data.message as string) || 'Unknown error',
|
|
348
|
+
});
|
|
349
|
+
void tryGenerateSummary();
|
|
350
|
+
} else if (type === 'session-end') {
|
|
351
|
+
addActivityEvent(session, 'session_end', {});
|
|
352
|
+
void tryGenerateSummary();
|
|
353
|
+
}
|
|
354
|
+
// Ignore 'welcome' and other unknown types
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/** Process a conversation message from the history batch. */
|
|
358
|
+
function processMessage(session: ActivitySessionRecord, msg: ActivitySessionMessage): void {
|
|
359
|
+
const timestamp = msg.timestamp || new Date().toISOString();
|
|
360
|
+
|
|
361
|
+
// Extract content text
|
|
362
|
+
let contentText = '';
|
|
363
|
+
if (typeof msg.content === 'string') {
|
|
364
|
+
contentText = msg.content;
|
|
365
|
+
} else if (Array.isArray(msg.content)) {
|
|
366
|
+
const textParts = msg.content
|
|
367
|
+
.filter((p) => p.type === 'text')
|
|
368
|
+
.map((p) => p.text || p.content || '');
|
|
369
|
+
contentText = textParts.join('');
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
if (msg.role === 'user') {
|
|
373
|
+
addActivityEvent(
|
|
374
|
+
session,
|
|
375
|
+
'user_message',
|
|
376
|
+
{
|
|
377
|
+
text: contentText.slice(0, 150),
|
|
378
|
+
},
|
|
379
|
+
timestamp
|
|
380
|
+
);
|
|
381
|
+
} else if (msg.role === 'assistant' && Array.isArray(msg.content)) {
|
|
382
|
+
const toolUses = msg.content.filter((p) => p.type === 'tool_use');
|
|
383
|
+
for (const tu of toolUses) {
|
|
384
|
+
const raw = tu as unknown as Record<string, unknown>;
|
|
385
|
+
const input =
|
|
386
|
+
typeof raw.input === 'object' && raw.input !== null
|
|
387
|
+
? (raw.input as Record<string, unknown>)
|
|
388
|
+
: {};
|
|
389
|
+
addActivityEvent(
|
|
390
|
+
session,
|
|
391
|
+
'tool_use',
|
|
392
|
+
{
|
|
393
|
+
command: ((input.command as string) || '').slice(0, 120),
|
|
394
|
+
filePath: ((input.file_path as string) || (input.path as string) || '').slice(0, 200),
|
|
395
|
+
name: tu.name || 'unknown',
|
|
396
|
+
tool: tu.name || 'unknown',
|
|
397
|
+
},
|
|
398
|
+
timestamp
|
|
399
|
+
);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
// Skip assistant text, system messages, and other roles for activity feed
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// -- Internal: Summarization ----------------------------------------------
|
|
406
|
+
|
|
407
|
+
async function tryGenerateSummary(): Promise<void> {
|
|
408
|
+
if (summarizing) {
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
const unsummarized = events.filter((e) => !e.summarized);
|
|
413
|
+
if (unsummarized.length === 0) {
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
// Group by project
|
|
418
|
+
const byProject = new SvelteMap<string, ActivityEvent[]>();
|
|
419
|
+
for (const event of unsummarized) {
|
|
420
|
+
const project = event.projectName || 'Unknown';
|
|
421
|
+
if (!byProject.has(project)) {
|
|
422
|
+
byProject.set(project, []);
|
|
423
|
+
}
|
|
424
|
+
const bucket = byProject.get(project);
|
|
425
|
+
if (bucket) {
|
|
426
|
+
bucket.push(event);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// Only summarize projects with enough events
|
|
431
|
+
const ready = Array.from(byProject.entries()).filter(
|
|
432
|
+
([, projectEvents]) => projectEvents.length >= SUMMARY_BATCH_SIZE
|
|
433
|
+
);
|
|
434
|
+
|
|
435
|
+
if (ready.length === 0) {
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
summarizing = true;
|
|
440
|
+
try {
|
|
441
|
+
// Run up to 3 project summaries in parallel
|
|
442
|
+
const results = await Promise.allSettled(
|
|
443
|
+
ready.slice(0, 3).map(async ([projectName, projectEvents]) => {
|
|
444
|
+
const result = await summarizeEvents(projectEvents, projectName);
|
|
445
|
+
return { projectEvents, projectName, result };
|
|
446
|
+
})
|
|
447
|
+
);
|
|
448
|
+
|
|
449
|
+
for (const r of results) {
|
|
450
|
+
if (r.status !== 'fulfilled' || !r.value.result.text) {
|
|
451
|
+
continue;
|
|
452
|
+
}
|
|
453
|
+
const { projectEvents, projectName, result } = r.value;
|
|
454
|
+
|
|
455
|
+
// Detect AI unavailability from error flag
|
|
456
|
+
if (result.error?.includes('No AI provider')) {
|
|
457
|
+
aiAvailable = false;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
const summary: ActivitySummary = {
|
|
461
|
+
eventIds: projectEvents.map((e) => e.id),
|
|
462
|
+
id: `sum-${++counter}`,
|
|
463
|
+
projectName,
|
|
464
|
+
text: result.text,
|
|
465
|
+
timestamp: new Date().toISOString(),
|
|
466
|
+
};
|
|
467
|
+
|
|
468
|
+
summaries = [...summaries, summary].slice(-MAX_SUMMARIES);
|
|
469
|
+
|
|
470
|
+
const ids = new Set(projectEvents.map((e) => e.id));
|
|
471
|
+
events = events.map((e) => (ids.has(e.id) ? { ...e, summarized: true } : e));
|
|
472
|
+
}
|
|
473
|
+
} catch (err) {
|
|
474
|
+
console.warn('[ActivityStore] Summarization failed:', err);
|
|
475
|
+
} finally {
|
|
476
|
+
summarizing = false;
|
|
477
|
+
}
|
|
478
|
+
}
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
// NeuroLink-powered event summarizer.
|
|
2
|
+
// Loads the NeuroLink SDK from CDN on first use, then generates
|
|
3
|
+
// rolling summaries of coding activity events.
|
|
4
|
+
|
|
5
|
+
import type { ActivityEvent, ActivitySummaryResult } from '$lib/types';
|
|
6
|
+
|
|
7
|
+
import { installFetchProxy } from '$lib/modules/client/neurolink/fetch-proxy';
|
|
8
|
+
import { detectActiveProvider } from '$lib/modules/client/neurolink/provider-config';
|
|
9
|
+
|
|
10
|
+
const NEUROLINK_CDN_URL = 'https://unpkg.com/@juspay/neurolink/dist/browser/neurolink.min.js';
|
|
11
|
+
const LOAD_FAILURE_COOLDOWN_MS = 60_000;
|
|
12
|
+
const MAX_LOAD_ATTEMPTS = 3;
|
|
13
|
+
|
|
14
|
+
// Lazy-loaded SDK instance
|
|
15
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
16
|
+
let sdk: null | { generate: (opts: Record<string, unknown>) => Promise<any> } = null;
|
|
17
|
+
let sdkPromise: null | Promise<typeof sdk> = null;
|
|
18
|
+
let loadAttempts = 0;
|
|
19
|
+
let nextRetryAt = 0;
|
|
20
|
+
|
|
21
|
+
/** Summarize a batch of events using NeuroLink. */
|
|
22
|
+
export async function summarizeEvents(
|
|
23
|
+
events: ActivityEvent[],
|
|
24
|
+
projectName?: string
|
|
25
|
+
): Promise<ActivitySummaryResult> {
|
|
26
|
+
if (events.length === 0) {
|
|
27
|
+
return { error: null, text: '' };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
let nlSdk: typeof sdk;
|
|
31
|
+
try {
|
|
32
|
+
nlSdk = await getSDK();
|
|
33
|
+
} catch {
|
|
34
|
+
const mapped: string[] = events.map((e) => str(e.data.tool) || e.type);
|
|
35
|
+
const tools = Array.from(new Set(mapped));
|
|
36
|
+
return {
|
|
37
|
+
error: 'AI SDK failed to load',
|
|
38
|
+
text: `${events.length} events: ${tools.join(', ')}`,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Fallback: no SDK available — return raw count
|
|
43
|
+
if (!nlSdk) {
|
|
44
|
+
const mapped: string[] = events.map((e) => str(e.data.tool) || e.type);
|
|
45
|
+
const tools = Array.from(new Set(mapped));
|
|
46
|
+
return {
|
|
47
|
+
error: 'No AI provider configured',
|
|
48
|
+
text: `${events.length} events: ${tools.join(', ')}`,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const formatted = formatEvents(events);
|
|
53
|
+
const projectCtx = projectName ? `Project: ${projectName}\n` : '';
|
|
54
|
+
const prompt = `You are a coding activity summarizer. Given these events from active Claude Code sessions, write a 1-2 sentence summary (max 150 chars) of what's happening. Be specific — mention tool names, file names, and what's being accomplished.
|
|
55
|
+
|
|
56
|
+
${projectCtx}Events:
|
|
57
|
+
${formatted}
|
|
58
|
+
|
|
59
|
+
Summary:`;
|
|
60
|
+
|
|
61
|
+
try {
|
|
62
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
63
|
+
const result: Record<string, unknown> = await nlSdk.generate({
|
|
64
|
+
input: { text: prompt },
|
|
65
|
+
maxTokens: 300,
|
|
66
|
+
...(activeModel ? { model: activeModel } : {}),
|
|
67
|
+
...(activeProvider ? { provider: activeProvider } : {}),
|
|
68
|
+
});
|
|
69
|
+
const text = str(result.content ?? result.text);
|
|
70
|
+
return { error: null, text: text.trim() || `${events.length} events processed` };
|
|
71
|
+
} catch (err) {
|
|
72
|
+
return {
|
|
73
|
+
error: err instanceof Error ? err.message : String(err),
|
|
74
|
+
text: `${events.length} events (summary unavailable)`,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function formatEventLine(e: ActivityEvent): string {
|
|
80
|
+
const d = e.data;
|
|
81
|
+
const project = e.projectName ? `[${e.projectName}] ` : '';
|
|
82
|
+
const tool = str(d.tool) || str(d.name) || '';
|
|
83
|
+
const filePath = str(d.filePath);
|
|
84
|
+
const command = str(d.command);
|
|
85
|
+
const pattern = str(d.pattern);
|
|
86
|
+
|
|
87
|
+
switch (e.type) {
|
|
88
|
+
case 'agent-idle':
|
|
89
|
+
case 'agent-question':
|
|
90
|
+
return `${project}${e.type}: ${str(d.message).slice(0, 80)}`;
|
|
91
|
+
case 'error':
|
|
92
|
+
return `${project}Error: ${str(d.error || d.message).slice(0, 100)}`;
|
|
93
|
+
case 'session_end':
|
|
94
|
+
return `${project}Session ended`;
|
|
95
|
+
case 'tool-completed':
|
|
96
|
+
return `${project}[${tool}] completed`;
|
|
97
|
+
case 'tool-failed':
|
|
98
|
+
return `${project}[${tool}] FAILED: ${str(d.error).slice(0, 80)}`;
|
|
99
|
+
case 'tool-started':
|
|
100
|
+
return `${project}[${tool}] started${filePath ? `: ${filePath}` : ''}`;
|
|
101
|
+
case 'tool_result': {
|
|
102
|
+
const status = d.is_error ? 'FAILED' : 'done';
|
|
103
|
+
return `${project}${tool ? `[${tool}] → ` : ''}${status}`;
|
|
104
|
+
}
|
|
105
|
+
case 'tool_use': {
|
|
106
|
+
const target = filePath || command || pattern || '';
|
|
107
|
+
return tool ? `${project}[${tool}] ${target}`.trim() : `${project}[tool] ${target}`.trim();
|
|
108
|
+
}
|
|
109
|
+
case 'user_message':
|
|
110
|
+
return `${project}User: ${str(d.text).slice(0, 80)}`;
|
|
111
|
+
default:
|
|
112
|
+
return `${project}${e.type}`;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** Format events into a prompt string. */
|
|
117
|
+
function formatEvents(events: ActivityEvent[]): string {
|
|
118
|
+
return events.map(formatEventLine).join('\n');
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Track detected provider for generate() calls
|
|
122
|
+
let activeModel = '';
|
|
123
|
+
let activeProvider = '';
|
|
124
|
+
|
|
125
|
+
/** Load the NeuroLink SDK from CDN (once), using auto-detected provider. */
|
|
126
|
+
async function getSDK(): Promise<typeof sdk> {
|
|
127
|
+
if (sdk) {
|
|
128
|
+
return sdk;
|
|
129
|
+
}
|
|
130
|
+
// De-duplicate concurrent calls — share the in-flight load promise
|
|
131
|
+
if (sdkPromise) {
|
|
132
|
+
return sdkPromise;
|
|
133
|
+
}
|
|
134
|
+
// Cool down after repeated failures so a transient CDN blip doesn't
|
|
135
|
+
// permanently disable summaries. Once the cooldown expires, reset the
|
|
136
|
+
// attempt counter and try again.
|
|
137
|
+
if (loadAttempts >= MAX_LOAD_ATTEMPTS) {
|
|
138
|
+
if (Date.now() < nextRetryAt) {
|
|
139
|
+
return null;
|
|
140
|
+
}
|
|
141
|
+
loadAttempts = 0;
|
|
142
|
+
}
|
|
143
|
+
loadAttempts++;
|
|
144
|
+
|
|
145
|
+
sdkPromise = (async (): Promise<typeof sdk> => {
|
|
146
|
+
try {
|
|
147
|
+
// Install fetch proxy for CORS providers (anthropic, openai, mistral)
|
|
148
|
+
installFetchProxy();
|
|
149
|
+
|
|
150
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
151
|
+
const mod = await import(/* @vite-ignore */ NEUROLINK_CDN_URL);
|
|
152
|
+
|
|
153
|
+
// Detect which provider has credentials available
|
|
154
|
+
const serverFlags = (window as unknown as Record<string, Record<string, boolean>>)
|
|
155
|
+
.__aiProviders;
|
|
156
|
+
const preferredProvider = (
|
|
157
|
+
window as unknown as Record<string, Record<string, Record<string, string>>>
|
|
158
|
+
).process?.env?.NEUROLINK_PROVIDER;
|
|
159
|
+
|
|
160
|
+
const active = detectActiveProvider(serverFlags, preferredProvider);
|
|
161
|
+
if (!active) {
|
|
162
|
+
console.warn('[ActivityFeed] No AI provider configured');
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Proxy providers: inject dummy env vars so SDK validation passes.
|
|
167
|
+
// The fetch proxy strips these and injects real keys server-side.
|
|
168
|
+
const proc = (window as unknown as { process?: { env?: Record<string, string> } }).process;
|
|
169
|
+
if (proc?.env && serverFlags) {
|
|
170
|
+
if (serverFlags['google-ai'] && !proc.env.GOOGLE_AI_API_KEY) {
|
|
171
|
+
proc.env.GOOGLE_AI_API_KEY = 'proxy-via-server';
|
|
172
|
+
}
|
|
173
|
+
if (serverFlags.anthropic && !proc.env.ANTHROPIC_API_KEY) {
|
|
174
|
+
proc.env.ANTHROPIC_API_KEY = 'proxy-via-server';
|
|
175
|
+
}
|
|
176
|
+
if (serverFlags.openai && !proc.env.OPENAI_API_KEY) {
|
|
177
|
+
proc.env.OPENAI_API_KEY = 'proxy-via-server';
|
|
178
|
+
}
|
|
179
|
+
if (serverFlags.mistral && !proc.env.MISTRAL_API_KEY) {
|
|
180
|
+
proc.env.MISTRAL_API_KEY = 'proxy-via-server';
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call
|
|
185
|
+
sdk = new mod.NeuroLink({ provider: active.provider }) as typeof sdk;
|
|
186
|
+
activeModel = active.model;
|
|
187
|
+
activeProvider = active.provider;
|
|
188
|
+
loadAttempts = 0; // reset on success
|
|
189
|
+
console.log(`[ActivityFeed] NeuroLink SDK loaded: ${active.provider}/${active.model}`);
|
|
190
|
+
return sdk;
|
|
191
|
+
} catch (err) {
|
|
192
|
+
console.warn(
|
|
193
|
+
'[ActivityFeed] NeuroLink SDK load failed (attempt',
|
|
194
|
+
loadAttempts,
|
|
195
|
+
'):',
|
|
196
|
+
err instanceof Error ? err.message : String(err)
|
|
197
|
+
);
|
|
198
|
+
// On the final attempt, start the cooldown window before allowing
|
|
199
|
+
// fresh retries. Earlier attempts can retry immediately on next call.
|
|
200
|
+
if (loadAttempts >= MAX_LOAD_ATTEMPTS) {
|
|
201
|
+
nextRetryAt = Date.now() + LOAD_FAILURE_COOLDOWN_MS;
|
|
202
|
+
}
|
|
203
|
+
return null;
|
|
204
|
+
} finally {
|
|
205
|
+
sdkPromise = null;
|
|
206
|
+
}
|
|
207
|
+
})();
|
|
208
|
+
|
|
209
|
+
return sdkPromise;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/** Coerce an unknown value to a plain string (never '[object Object]'). */
|
|
213
|
+
function str(v: unknown): string {
|
|
214
|
+
if (v === null || v === undefined) {
|
|
215
|
+
return '';
|
|
216
|
+
}
|
|
217
|
+
if (typeof v === 'string') {
|
|
218
|
+
return v;
|
|
219
|
+
}
|
|
220
|
+
if (typeof v === 'number' || typeof v === 'boolean') {
|
|
221
|
+
return String(v);
|
|
222
|
+
}
|
|
223
|
+
return JSON.stringify(v);
|
|
224
|
+
}
|