@juspay/shooter 1.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/client/_app/immutable/assets/{0.DC5pAwP3.css → 0.BhZOCxO4.css} +1 -1
- package/build/client/_app/immutable/assets/0.BhZOCxO4.css.br +0 -0
- package/build/client/_app/immutable/assets/0.BhZOCxO4.css.gz +0 -0
- package/build/client/_app/immutable/assets/1.BYutk3aU.css +1 -0
- package/build/client/_app/immutable/assets/1.BYutk3aU.css.br +0 -0
- package/build/client/_app/immutable/assets/1.BYutk3aU.css.gz +0 -0
- package/build/client/_app/immutable/assets/{3.BoXp0JoS.css → 3.DGDHCVnW.css} +1 -1
- package/build/client/_app/immutable/assets/3.DGDHCVnW.css.br +0 -0
- package/build/client/_app/immutable/assets/3.DGDHCVnW.css.gz +0 -0
- package/build/client/_app/immutable/assets/4.BFUut--w.css +1 -0
- package/build/client/_app/immutable/assets/4.BFUut--w.css.br +0 -0
- package/build/client/_app/immutable/assets/4.BFUut--w.css.gz +0 -0
- package/build/client/_app/immutable/assets/5.BTOx7yt7.css +1 -0
- package/build/client/_app/immutable/assets/5.BTOx7yt7.css.br +0 -0
- package/build/client/_app/immutable/assets/5.BTOx7yt7.css.gz +0 -0
- package/build/client/_app/immutable/assets/{7.PyEFVv_s.css → 7.DwS5ZHBh.css} +1 -1
- package/build/client/_app/immutable/assets/7.DwS5ZHBh.css.br +0 -0
- package/build/client/_app/immutable/assets/7.DwS5ZHBh.css.gz +0 -0
- package/build/client/_app/immutable/assets/ChatView.CwWbzIL-.css +1 -0
- package/build/client/_app/immutable/assets/ChatView.CwWbzIL-.css.br +0 -0
- package/build/client/_app/immutable/assets/ChatView.CwWbzIL-.css.gz +0 -0
- package/build/client/_app/immutable/chunks/B-K5Sh65.js +1 -0
- package/build/client/_app/immutable/chunks/B-K5Sh65.js.br +0 -0
- package/build/client/_app/immutable/chunks/B-K5Sh65.js.gz +0 -0
- package/build/client/_app/immutable/chunks/B5NAKyil.js +20 -0
- package/build/client/_app/immutable/chunks/B5NAKyil.js.br +0 -0
- package/build/client/_app/immutable/chunks/B5NAKyil.js.gz +0 -0
- package/build/client/_app/immutable/chunks/B8XegpSE.js +1 -0
- package/build/client/_app/immutable/chunks/B8XegpSE.js.br +0 -0
- package/build/client/_app/immutable/chunks/B8XegpSE.js.gz +0 -0
- package/build/client/_app/immutable/chunks/{50RPd5u3.js → B8zoBsv3.js} +1 -1
- package/build/client/_app/immutable/chunks/B8zoBsv3.js.br +0 -0
- package/build/client/_app/immutable/chunks/B8zoBsv3.js.gz +0 -0
- package/build/client/_app/immutable/chunks/BLszSzTT.js +1 -0
- package/build/client/_app/immutable/chunks/BLszSzTT.js.br +0 -0
- package/build/client/_app/immutable/chunks/BLszSzTT.js.gz +0 -0
- package/build/client/_app/immutable/chunks/BOYo8yTr.js +1 -0
- package/build/client/_app/immutable/chunks/BOYo8yTr.js.br +0 -0
- package/build/client/_app/immutable/chunks/BOYo8yTr.js.gz +0 -0
- package/build/client/_app/immutable/chunks/{CdL99jkG.js → BYqGCrTe.js} +1 -1
- package/build/client/_app/immutable/chunks/BYqGCrTe.js.br +0 -0
- package/build/client/_app/immutable/chunks/BYqGCrTe.js.gz +0 -0
- package/build/client/_app/immutable/chunks/Bu1aqm5j.js +1 -0
- package/build/client/_app/immutable/chunks/Bu1aqm5j.js.br +0 -0
- package/build/client/_app/immutable/chunks/Bu1aqm5j.js.gz +0 -0
- package/build/client/_app/immutable/chunks/C4mLaWWx.js +1 -0
- package/build/client/_app/immutable/chunks/C4mLaWWx.js.br +0 -0
- package/build/client/_app/immutable/chunks/C4mLaWWx.js.gz +0 -0
- package/build/client/_app/immutable/chunks/{ClK4wZbC.js → CQjSATpv.js} +16 -16
- 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/{CojFyTPp.js → CSoRdFvv.js} +1 -1
- 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/{tjbnEgXP.js → CZHsSL_X.js} +1 -1
- 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/DVkn4r72.js +1 -0
- package/build/client/_app/immutable/chunks/DVkn4r72.js.br +0 -0
- package/build/client/_app/immutable/chunks/DVkn4r72.js.gz +0 -0
- package/build/client/_app/immutable/chunks/DjsDGxCa.js +41 -0
- package/build/client/_app/immutable/chunks/DjsDGxCa.js.br +0 -0
- package/build/client/_app/immutable/chunks/DjsDGxCa.js.gz +0 -0
- package/build/client/_app/immutable/chunks/UJOiqIYE.js +1 -0
- package/build/client/_app/immutable/chunks/UJOiqIYE.js.br +0 -0
- package/build/client/_app/immutable/chunks/UJOiqIYE.js.gz +0 -0
- package/build/client/_app/immutable/chunks/r0JawsZc.js +2 -0
- package/build/client/_app/immutable/chunks/r0JawsZc.js.br +0 -0
- package/build/client/_app/immutable/chunks/r0JawsZc.js.gz +0 -0
- package/build/client/_app/immutable/entry/app.Z3zMnuSx.js +2 -0
- package/build/client/_app/immutable/entry/app.Z3zMnuSx.js.br +0 -0
- package/build/client/_app/immutable/entry/app.Z3zMnuSx.js.gz +0 -0
- package/build/client/_app/immutable/entry/start.Dd-gIP4y.js +1 -0
- package/build/client/_app/immutable/entry/start.Dd-gIP4y.js.br +2 -0
- package/build/client/_app/immutable/entry/start.Dd-gIP4y.js.gz +0 -0
- package/build/client/_app/immutable/nodes/0.D2YR8tTD.js +1 -0
- package/build/client/_app/immutable/nodes/0.D2YR8tTD.js.br +0 -0
- package/build/client/_app/immutable/nodes/0.D2YR8tTD.js.gz +0 -0
- package/build/client/_app/immutable/nodes/1.B3m6rO4C.js +1 -0
- package/build/client/_app/immutable/nodes/1.B3m6rO4C.js.br +0 -0
- package/build/client/_app/immutable/nodes/1.B3m6rO4C.js.gz +0 -0
- package/build/client/_app/immutable/nodes/2.CyRB2euU.js +1 -0
- package/build/client/_app/immutable/nodes/2.CyRB2euU.js.br +0 -0
- package/build/client/_app/immutable/nodes/2.CyRB2euU.js.gz +0 -0
- package/build/client/_app/immutable/nodes/3.3yohCM25.js +3 -0
- package/build/client/_app/immutable/nodes/3.3yohCM25.js.br +0 -0
- package/build/client/_app/immutable/nodes/3.3yohCM25.js.gz +0 -0
- package/build/client/_app/immutable/nodes/4.DEAcwl7l.js +1 -0
- package/build/client/_app/immutable/nodes/4.DEAcwl7l.js.br +0 -0
- package/build/client/_app/immutable/nodes/4.DEAcwl7l.js.gz +0 -0
- package/build/client/_app/immutable/nodes/5.C6bLGWQR.js +4 -0
- package/build/client/_app/immutable/nodes/5.C6bLGWQR.js.br +0 -0
- package/build/client/_app/immutable/nodes/5.C6bLGWQR.js.gz +0 -0
- package/build/client/_app/immutable/nodes/6.ByTzlA2D.js +2 -0
- package/build/client/_app/immutable/nodes/6.ByTzlA2D.js.br +0 -0
- package/build/client/_app/immutable/nodes/6.ByTzlA2D.js.gz +0 -0
- package/build/client/_app/immutable/nodes/7.BPMfwzd2.js +2 -0
- package/build/client/_app/immutable/nodes/7.BPMfwzd2.js.br +0 -0
- package/build/client/_app/immutable/nodes/7.BPMfwzd2.js.gz +0 -0
- package/build/client/_app/version.json +1 -1
- package/build/client/_app/version.json.br +0 -0
- package/build/client/_app/version.json.gz +0 -0
- package/build/client/manifest.json +13 -0
- package/build/client/manifest.json.br +0 -0
- package/build/client/manifest.json.gz +0 -0
- package/build/server/chunks/0-Vk38tI2J.js +9 -0
- package/build/server/chunks/0-Vk38tI2J.js.map +1 -0
- package/build/server/chunks/1-BvYQX5MR.js +9 -0
- package/build/server/chunks/1-BvYQX5MR.js.map +1 -0
- package/build/server/chunks/2-Cl7R4Qk2.js +9 -0
- package/build/server/chunks/2-Cl7R4Qk2.js.map +1 -0
- package/build/server/chunks/3-Ck7ewhOX.js +9 -0
- package/build/server/chunks/3-Ck7ewhOX.js.map +1 -0
- package/build/server/chunks/4-CnDeRm2Z.js +9 -0
- package/build/server/chunks/4-CnDeRm2Z.js.map +1 -0
- package/build/server/chunks/5-IxitzEvN.js +9 -0
- package/build/server/chunks/{5-D0wB7nfE.js.map → 5-IxitzEvN.js.map} +1 -1
- package/build/server/chunks/6-CyZ3r1iS.js +9 -0
- package/build/server/chunks/6-CyZ3r1iS.js.map +1 -0
- package/build/server/chunks/7-BmI7du46.js +9 -0
- package/build/server/chunks/7-BmI7du46.js.map +1 -0
- package/build/server/chunks/{Button-C7D5W6wV.js → Button-Cs1aE6ka.js} +2 -2
- package/build/server/chunks/{Button-C7D5W6wV.js.map → Button-Cs1aE6ka.js.map} +1 -1
- package/build/server/chunks/{EmptyState-Ci4pSpmY.js → EmptyState-DDFH1K8g.js} +10 -4
- package/build/server/chunks/EmptyState-DDFH1K8g.js.map +1 -0
- package/build/server/chunks/{Icon-DyrkHVnV.js → Icon-CEUrotA6.js} +3 -3
- package/build/server/chunks/{Icon-DyrkHVnV.js.map → Icon-CEUrotA6.js.map} +1 -1
- package/build/server/chunks/{Shimmer-BITK6wrm.js → Shimmer-DB8W1zt6.js} +2 -2
- package/build/server/chunks/{Shimmer-BITK6wrm.js.map → Shimmer-DB8W1zt6.js.map} +1 -1
- package/build/server/chunks/_error.svelte-uCOJNxvr.js +39 -0
- package/build/server/chunks/_error.svelte-uCOJNxvr.js.map +1 -0
- package/build/server/chunks/{_layout.svelte-CO4f8UD7.js → _layout.svelte-CtWmEJwe.js} +5 -25
- package/build/server/chunks/_layout.svelte-CtWmEJwe.js.map +1 -0
- package/build/server/chunks/{_page.svelte-C6bns9aQ.js → _page.svelte-BcZaKdX9.js} +13 -9
- package/build/server/chunks/_page.svelte-BcZaKdX9.js.map +1 -0
- package/build/server/chunks/{_page.svelte-3Cc3NMAP.js → _page.svelte-BdYynOck.js} +18 -14
- package/build/server/chunks/_page.svelte-BdYynOck.js.map +1 -0
- package/build/server/chunks/{_page.svelte-CFCONiDK.js → _page.svelte-BgevQjq1.js} +16 -12
- package/build/server/chunks/_page.svelte-BgevQjq1.js.map +1 -0
- package/build/server/chunks/{_page.svelte-DskND_G9.js → _page.svelte-CVq6tRb3.js} +8 -8
- package/build/server/chunks/_page.svelte-CVq6tRb3.js.map +1 -0
- package/build/server/chunks/{_page.svelte-rTrWmhOp.js → _page.svelte-CxWcQ0Am.js} +8 -8
- package/build/server/chunks/_page.svelte-CxWcQ0Am.js.map +1 -0
- package/build/server/chunks/{_page.svelte-C0p3HsIW.js → _page.svelte-DO4oa_LY.js} +11 -13
- package/build/server/chunks/_page.svelte-DO4oa_LY.js.map +1 -0
- package/build/server/chunks/{_server.ts-CS5H5klP.js → _server.ts-CAxsWKvS.js} +14 -10
- package/build/server/chunks/_server.ts-CAxsWKvS.js.map +1 -0
- package/build/server/chunks/{_server.ts-C8slOHB0.js → _server.ts-CTpcLUH8.js} +3 -2
- package/build/server/chunks/_server.ts-CTpcLUH8.js.map +1 -0
- package/build/server/chunks/_server.ts-CtH0dhUp.js +71 -0
- package/build/server/chunks/_server.ts-CtH0dhUp.js.map +1 -0
- package/build/server/chunks/{_server.ts-DhTrdlWH.js → _server.ts-DB_Kg97c.js} +14 -3
- package/build/server/chunks/_server.ts-DB_Kg97c.js.map +1 -0
- package/build/server/chunks/_server.ts-DYpJImqd.js +99 -0
- package/build/server/chunks/_server.ts-DYpJImqd.js.map +1 -0
- package/build/server/chunks/{_server.ts-DpEVfp8W.js → _server.ts-Deok2y88.js} +58 -40
- package/build/server/chunks/_server.ts-Deok2y88.js.map +1 -0
- package/build/server/chunks/{_server.ts-uHUi-4cd.js → _server.ts-vekTmWAx.js} +3 -2
- package/build/server/chunks/_server.ts-vekTmWAx.js.map +1 -0
- package/build/server/chunks/{client2-BjxIYuno.js → client-BdGHe_hY.js} +4 -4
- package/build/server/chunks/client-BdGHe_hY.js.map +1 -0
- package/build/server/chunks/client2-CCBGA-2V.js +7 -0
- package/build/server/chunks/client2-CCBGA-2V.js.map +1 -0
- package/build/server/chunks/{index-Cnl871UW.js → index-DwaY1cAm.js} +2 -2
- package/build/server/chunks/{index-Cnl871UW.js.map → index-DwaY1cAm.js.map} +1 -1
- package/build/server/chunks/{index-server-CUC9Jt7r.js → index-server-CrDaL06Y.js} +2 -2
- package/build/server/chunks/{index-server-CUC9Jt7r.js.map → index-server-CrDaL06Y.js.map} +1 -1
- package/build/server/chunks/{index2-HA3VTH7y.js → index2-CgclKpUj.js} +2 -2
- package/build/server/chunks/{index2-HA3VTH7y.js.map → index2-CgclKpUj.js.map} +1 -1
- package/build/server/chunks/opencode-db-path-DcfhJtJy.js +15 -0
- package/build/server/chunks/opencode-db-path-DcfhJtJy.js.map +1 -0
- package/build/server/chunks/{pty-manager-DR0Wt2Ac.js → pty-manager-BQVB7IVj.js} +17 -15
- package/build/server/chunks/pty-manager-BQVB7IVj.js.map +1 -0
- package/build/server/chunks/{root-DhswcH6o.js → root-DDSnEAZv.js} +2 -2
- package/build/server/chunks/{root-DhswcH6o.js.map → root-DDSnEAZv.js.map} +1 -1
- package/build/server/chunks/{state.svelte-2Lellg7t.js → state.svelte-hBbXlUak.js} +3 -3
- package/build/server/chunks/{state.svelte-2Lellg7t.js.map → state.svelte-hBbXlUak.js.map} +1 -1
- package/build/server/chunks/stores-DHNzYNpX.js +28 -0
- package/build/server/chunks/stores-DHNzYNpX.js.map +1 -0
- package/build/server/index.js +5 -5
- package/build/server/index.js.map +1 -1
- package/build/server/manifest.js +30 -16
- package/build/server/manifest.js.map +1 -1
- package/package.json +1 -1
- package/scripts/setup.cjs +87 -13
- package/server.ts +3 -0
- package/src/app.html +3 -0
- package/src/lib/modules/client/common/cache.ts +10 -2
- package/src/lib/modules/client/common/index.ts +1 -1
- package/src/lib/modules/client/common/markdown.ts +22 -1
- package/src/lib/modules/client/common/time.ts +13 -11
- package/src/lib/modules/client/terminal/ChatView.svelte +324 -67
- package/src/lib/modules/client/terminal/ConnectionStatus.svelte +1 -0
- package/src/lib/modules/client/terminal/xterm-wrapper.ts +4 -1
- package/src/lib/modules/server/sessions/jsonl-reader.ts +43 -28
- package/src/lib/modules/server/sessions/opencode-db-path.ts +26 -0
- package/src/lib/modules/server/sessions/opencode-reader.ts +13 -15
- package/src/lib/modules/server/sessions/process-detector.ts +103 -0
- package/src/lib/modules/server/terminal/opencode-watcher.ts +3 -14
- package/src/lib/modules/server/terminal/pty-manager.ts +20 -8
- package/src/lib/modules/server/ws/session-handler.ts +282 -104
- package/src/lib/theme.css +30 -0
- package/src/routes/+error.svelte +94 -0
- package/src/routes/+page.svelte +18 -8
- package/src/routes/api/health/+server.ts +14 -2
- package/src/routes/api/sessions/+server.ts +12 -4
- package/src/routes/api/sessions/connect/+server.ts +125 -0
- package/src/routes/api/sessions/detect/+server.ts +27 -0
- package/src/routes/api/terminals/+server.ts +12 -9
- package/src/routes/config/+page.svelte +22 -1
- package/src/routes/project/+page.svelte +159 -8
- package/src/routes/session/[id]/+page.svelte +477 -299
- package/src/routes/terminals/+page.svelte +14 -3
- package/src/routes/terminals/[id]/+page.svelte +25 -0
- package/build/client/_app/immutable/assets/0.DC5pAwP3.css.br +0 -0
- package/build/client/_app/immutable/assets/0.DC5pAwP3.css.gz +0 -0
- package/build/client/_app/immutable/assets/3.BoXp0JoS.css.br +0 -0
- package/build/client/_app/immutable/assets/3.BoXp0JoS.css.gz +0 -0
- package/build/client/_app/immutable/assets/4.cJuCkJKZ.css +0 -1
- package/build/client/_app/immutable/assets/4.cJuCkJKZ.css.br +0 -0
- package/build/client/_app/immutable/assets/4.cJuCkJKZ.css.gz +0 -0
- package/build/client/_app/immutable/assets/5.DRjApZQW.css +0 -1
- package/build/client/_app/immutable/assets/5.DRjApZQW.css.br +0 -0
- package/build/client/_app/immutable/assets/5.DRjApZQW.css.gz +0 -0
- package/build/client/_app/immutable/assets/7.PyEFVv_s.css.br +0 -0
- package/build/client/_app/immutable/assets/7.PyEFVv_s.css.gz +0 -0
- package/build/client/_app/immutable/assets/ChatView.CsdBAOKx.css +0 -1
- package/build/client/_app/immutable/assets/ChatView.CsdBAOKx.css.br +0 -0
- package/build/client/_app/immutable/assets/ChatView.CsdBAOKx.css.gz +0 -0
- package/build/client/_app/immutable/chunks/50RPd5u3.js.br +0 -0
- package/build/client/_app/immutable/chunks/50RPd5u3.js.gz +0 -0
- package/build/client/_app/immutable/chunks/BDC7XD4o.js +0 -1
- package/build/client/_app/immutable/chunks/BDC7XD4o.js.br +0 -0
- package/build/client/_app/immutable/chunks/BDC7XD4o.js.gz +0 -0
- package/build/client/_app/immutable/chunks/BeONA6_G.js +0 -2
- package/build/client/_app/immutable/chunks/BeONA6_G.js.br +0 -0
- package/build/client/_app/immutable/chunks/BeONA6_G.js.gz +0 -0
- package/build/client/_app/immutable/chunks/BygiiMA0.js +0 -1
- package/build/client/_app/immutable/chunks/BygiiMA0.js.br +0 -0
- package/build/client/_app/immutable/chunks/BygiiMA0.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CGLrx-H5.js +0 -1
- package/build/client/_app/immutable/chunks/CGLrx-H5.js.br +0 -0
- package/build/client/_app/immutable/chunks/CGLrx-H5.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CHvUpVYv.js +0 -1
- package/build/client/_app/immutable/chunks/CHvUpVYv.js.br +0 -0
- package/build/client/_app/immutable/chunks/CHvUpVYv.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CbqC9BW7.js +0 -1
- package/build/client/_app/immutable/chunks/CbqC9BW7.js.br +0 -2
- package/build/client/_app/immutable/chunks/CbqC9BW7.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CdL99jkG.js.br +0 -0
- package/build/client/_app/immutable/chunks/CdL99jkG.js.gz +0 -0
- package/build/client/_app/immutable/chunks/ClK4wZbC.js.br +0 -0
- package/build/client/_app/immutable/chunks/ClK4wZbC.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CojFyTPp.js.br +0 -0
- package/build/client/_app/immutable/chunks/CojFyTPp.js.gz +0 -0
- package/build/client/_app/immutable/chunks/DREFAyhX.js +0 -60
- package/build/client/_app/immutable/chunks/DREFAyhX.js.br +0 -0
- package/build/client/_app/immutable/chunks/DREFAyhX.js.gz +0 -0
- package/build/client/_app/immutable/chunks/K_aHH2KN.js +0 -1
- package/build/client/_app/immutable/chunks/K_aHH2KN.js.br +0 -0
- package/build/client/_app/immutable/chunks/K_aHH2KN.js.gz +0 -0
- package/build/client/_app/immutable/chunks/Ona8oofC.js +0 -1
- package/build/client/_app/immutable/chunks/Ona8oofC.js.br +0 -0
- package/build/client/_app/immutable/chunks/Ona8oofC.js.gz +0 -0
- package/build/client/_app/immutable/chunks/tjbnEgXP.js.br +0 -0
- package/build/client/_app/immutable/chunks/tjbnEgXP.js.gz +0 -0
- package/build/client/_app/immutable/entry/app.anqwe7ZL.js +0 -2
- package/build/client/_app/immutable/entry/app.anqwe7ZL.js.br +0 -0
- package/build/client/_app/immutable/entry/app.anqwe7ZL.js.gz +0 -0
- package/build/client/_app/immutable/entry/start.BV2VRv6h.js +0 -1
- package/build/client/_app/immutable/entry/start.BV2VRv6h.js.br +0 -2
- package/build/client/_app/immutable/entry/start.BV2VRv6h.js.gz +0 -0
- package/build/client/_app/immutable/nodes/0.D0i5MqcI.js +0 -1
- package/build/client/_app/immutable/nodes/0.D0i5MqcI.js.br +0 -0
- package/build/client/_app/immutable/nodes/0.D0i5MqcI.js.gz +0 -0
- package/build/client/_app/immutable/nodes/1.DN5n5c6F.js +0 -1
- package/build/client/_app/immutable/nodes/1.DN5n5c6F.js.br +0 -0
- package/build/client/_app/immutable/nodes/1.DN5n5c6F.js.gz +0 -0
- package/build/client/_app/immutable/nodes/2.BTjkNKZJ.js +0 -1
- package/build/client/_app/immutable/nodes/2.BTjkNKZJ.js.br +0 -0
- package/build/client/_app/immutable/nodes/2.BTjkNKZJ.js.gz +0 -0
- package/build/client/_app/immutable/nodes/3.CEkigHDv.js +0 -3
- package/build/client/_app/immutable/nodes/3.CEkigHDv.js.br +0 -0
- package/build/client/_app/immutable/nodes/3.CEkigHDv.js.gz +0 -0
- package/build/client/_app/immutable/nodes/4.B-8Zsc9k.js +0 -1
- package/build/client/_app/immutable/nodes/4.B-8Zsc9k.js.br +0 -0
- package/build/client/_app/immutable/nodes/4.B-8Zsc9k.js.gz +0 -0
- package/build/client/_app/immutable/nodes/5.shitbtHc.js +0 -1
- package/build/client/_app/immutable/nodes/5.shitbtHc.js.br +0 -0
- package/build/client/_app/immutable/nodes/5.shitbtHc.js.gz +0 -0
- package/build/client/_app/immutable/nodes/6.CB8Q6eH8.js +0 -2
- package/build/client/_app/immutable/nodes/6.CB8Q6eH8.js.br +0 -0
- package/build/client/_app/immutable/nodes/6.CB8Q6eH8.js.gz +0 -0
- package/build/client/_app/immutable/nodes/7.D0CrR6pl.js +0 -2
- package/build/client/_app/immutable/nodes/7.D0CrR6pl.js.br +0 -0
- package/build/client/_app/immutable/nodes/7.D0CrR6pl.js.gz +0 -0
- package/build/server/chunks/0-Cs1dzfRz.js +0 -9
- package/build/server/chunks/0-Cs1dzfRz.js.map +0 -1
- package/build/server/chunks/1-BCSX7oED.js +0 -9
- package/build/server/chunks/1-BCSX7oED.js.map +0 -1
- package/build/server/chunks/2-6gkeO8b4.js +0 -9
- package/build/server/chunks/2-6gkeO8b4.js.map +0 -1
- package/build/server/chunks/3-DH9J9Vsc.js +0 -9
- package/build/server/chunks/3-DH9J9Vsc.js.map +0 -1
- package/build/server/chunks/4-Rzy5TYjl.js +0 -9
- package/build/server/chunks/4-Rzy5TYjl.js.map +0 -1
- package/build/server/chunks/5-D0wB7nfE.js +0 -9
- package/build/server/chunks/6-DR2ABDPq.js +0 -9
- package/build/server/chunks/6-DR2ABDPq.js.map +0 -1
- package/build/server/chunks/7-D-oQJsia.js +0 -9
- package/build/server/chunks/7-D-oQJsia.js.map +0 -1
- package/build/server/chunks/EmptyState-Ci4pSpmY.js.map +0 -1
- package/build/server/chunks/_layout.svelte-CO4f8UD7.js.map +0 -1
- package/build/server/chunks/_page.svelte-3Cc3NMAP.js.map +0 -1
- package/build/server/chunks/_page.svelte-C0p3HsIW.js.map +0 -1
- package/build/server/chunks/_page.svelte-C6bns9aQ.js.map +0 -1
- package/build/server/chunks/_page.svelte-CFCONiDK.js.map +0 -1
- package/build/server/chunks/_page.svelte-DskND_G9.js.map +0 -1
- package/build/server/chunks/_page.svelte-rTrWmhOp.js.map +0 -1
- package/build/server/chunks/_server.ts-C8slOHB0.js.map +0 -1
- package/build/server/chunks/_server.ts-CS5H5klP.js.map +0 -1
- package/build/server/chunks/_server.ts-DhTrdlWH.js.map +0 -1
- package/build/server/chunks/_server.ts-DpEVfp8W.js.map +0 -1
- package/build/server/chunks/_server.ts-uHUi-4cd.js.map +0 -1
- package/build/server/chunks/client-BYT9c0ig.js +0 -7
- package/build/server/chunks/client-BYT9c0ig.js.map +0 -1
- package/build/server/chunks/client2-BjxIYuno.js.map +0 -1
- package/build/server/chunks/error.svelte-BG_yE-Wt.js +0 -27
- package/build/server/chunks/error.svelte-BG_yE-Wt.js.map +0 -1
- package/build/server/chunks/pty-manager-DR0Wt2Ac.js.map +0 -1
|
@@ -9,300 +9,400 @@
|
|
|
9
9
|
|
|
10
10
|
import { browser } from '$app/environment';
|
|
11
11
|
import { page } from '$app/state';
|
|
12
|
-
import {
|
|
12
|
+
import { getCached, setCache } from '$lib/modules/client/common';
|
|
13
13
|
import ChatView from '$lib/modules/client/terminal/ChatView.svelte';
|
|
14
|
-
import {
|
|
14
|
+
import { Shimmer } from '@juspay/svelte-ui-components';
|
|
15
15
|
import { onMount } from 'svelte';
|
|
16
16
|
|
|
17
|
+
// --- State ---
|
|
18
|
+
|
|
17
19
|
let session = $state<null | SessionInfo>(null);
|
|
18
20
|
let messages = $state<ConversationMessage[]>([]);
|
|
19
21
|
let loading = $state(true);
|
|
20
22
|
let error = $state<null | string>(null);
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
let
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
let
|
|
27
|
-
let
|
|
23
|
+
let hasMoreMessages = $state(false);
|
|
24
|
+
let loadingMore = $state(false);
|
|
25
|
+
let currentLimit = $state(200);
|
|
26
|
+
|
|
27
|
+
// Terminal + WS connection state
|
|
28
|
+
let connectedTerminalId = $state<null | string>(null);
|
|
29
|
+
let chatConnectionState = $state<'connected' | 'connecting' | 'disconnected' | 'idle' | 'reconnecting'>('idle');
|
|
30
|
+
// sendStatus:
|
|
31
|
+
// 'idle' = no terminal yet; input ready for first send
|
|
32
|
+
// 'connecting' = calling /api/sessions/connect
|
|
33
|
+
// 'resuming' = waiting for session history (claude loading)
|
|
34
|
+
// 'ready' = history received; input goes straight to PTY
|
|
35
|
+
let sendStatus = $state<'connecting' | 'idle' | 'ready' | 'resuming'>('idle');
|
|
36
|
+
let pendingMessage: null | string = null;
|
|
37
|
+
|
|
38
|
+
// Non-reactive WS refs
|
|
39
|
+
let terminalWs: null | WebSocket = null;
|
|
40
|
+
let sessionWs: null | WebSocket = null;
|
|
41
|
+
let sessionReconnectTimer: null | ReturnType<typeof setTimeout> = null;
|
|
28
42
|
let disposed = false;
|
|
29
43
|
|
|
30
44
|
const sessionId = $derived(page.params.id);
|
|
31
45
|
const projectId = $derived(page.url.searchParams.get('project') || '');
|
|
32
46
|
|
|
33
|
-
//
|
|
34
|
-
|
|
35
|
-
const modifiedTime = new Date(sess.modified).getTime();
|
|
36
|
-
const fiveMinutesAgo = Date.now() - 5 * 60 * 1000;
|
|
37
|
-
return modifiedTime > fiveMinutesAgo;
|
|
38
|
-
}
|
|
47
|
+
// Disable send button while a message is in-flight
|
|
48
|
+
const sendDisabled = $derived(sendStatus === 'connecting' || sendStatus === 'resuming');
|
|
39
49
|
|
|
40
|
-
//
|
|
41
|
-
|
|
42
|
-
function
|
|
43
|
-
|
|
44
|
-
|
|
50
|
+
// --- Helpers ---
|
|
51
|
+
|
|
52
|
+
function getConfig(): null | ShooterConfig {
|
|
53
|
+
try {
|
|
54
|
+
const saved = localStorage.getItem('shooter_config');
|
|
55
|
+
if (!saved) { return null; }
|
|
56
|
+
return JSON.parse(saved) as ShooterConfig;
|
|
57
|
+
} catch {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
45
60
|
}
|
|
46
61
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
content?: MessagePart[];
|
|
51
|
-
id?: string;
|
|
52
|
-
input?: Record<string, unknown>;
|
|
53
|
-
isError?: boolean;
|
|
54
|
-
message?: string;
|
|
55
|
-
name?: string;
|
|
56
|
-
output?: string;
|
|
57
|
-
role?: string;
|
|
58
|
-
status?: string;
|
|
59
|
-
text?: string;
|
|
60
|
-
timestamp?: string;
|
|
61
|
-
type: string;
|
|
62
|
-
};
|
|
62
|
+
async function getWsTicket(): Promise<null | string> {
|
|
63
|
+
const config = getConfig();
|
|
64
|
+
if (!config) { return null; }
|
|
63
65
|
try {
|
|
64
|
-
|
|
66
|
+
const res = await fetch('/api/ws-ticket', {
|
|
67
|
+
headers: { Authorization: `Bearer ${config.apiKey}` },
|
|
68
|
+
method: 'POST',
|
|
69
|
+
});
|
|
70
|
+
if (!res.ok) { return null; }
|
|
71
|
+
const data: { ticket: string } = await res.json();
|
|
72
|
+
return data.ticket;
|
|
65
73
|
} catch {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// --- Session WS (receive chat messages) ---
|
|
79
|
+
|
|
80
|
+
async function connectSessionWs(sessionWsPath: string, termId: string): Promise<void> {
|
|
81
|
+
if (disposed) { return; }
|
|
82
|
+
|
|
83
|
+
// Avoid stacking connections
|
|
84
|
+
if (
|
|
85
|
+
sessionWs &&
|
|
86
|
+
(sessionWs.readyState === WebSocket.OPEN || sessionWs.readyState === WebSocket.CONNECTING)
|
|
87
|
+
) {
|
|
66
88
|
return;
|
|
67
89
|
}
|
|
68
90
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
91
|
+
const ticket = await getWsTicket();
|
|
92
|
+
if (!ticket || disposed) { return; }
|
|
93
|
+
|
|
94
|
+
chatConnectionState = 'connecting';
|
|
95
|
+
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
|
|
96
|
+
const socket = new WebSocket(
|
|
97
|
+
`${protocol}//${window.location.host}${sessionWsPath}?ticket=${ticket}`
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
socket.onopen = () => {
|
|
101
|
+
if (disposed) { socket.close(); return; }
|
|
102
|
+
chatConnectionState = 'connected';
|
|
103
|
+
socket.send(JSON.stringify({ sessionId: termId, type: 'subscribe' }));
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
socket.onmessage = (event) => {
|
|
107
|
+
if (disposed) { return; }
|
|
108
|
+
try { handleSessionMessage(JSON.parse(event.data as string)); } catch { /* ignore */ }
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
socket.onclose = () => {
|
|
112
|
+
if (disposed) { return; }
|
|
113
|
+
chatConnectionState = 'disconnected';
|
|
114
|
+
sessionWs = null;
|
|
115
|
+
if (connectedTerminalId) {
|
|
116
|
+
if (sessionReconnectTimer) { clearTimeout(sessionReconnectTimer); }
|
|
117
|
+
sessionReconnectTimer = setTimeout(() => {
|
|
118
|
+
sessionReconnectTimer = null;
|
|
119
|
+
if (!disposed && connectedTerminalId) {
|
|
120
|
+
void connectSessionWs(`/ws/session/${connectedTerminalId}`, connectedTerminalId);
|
|
121
|
+
}
|
|
122
|
+
}, 2000);
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
socket.onerror = () => {
|
|
127
|
+
if (!disposed) { chatConnectionState = 'disconnected'; }
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
sessionWs = socket;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// --- Terminal WS (send PTY input only) ---
|
|
134
|
+
|
|
135
|
+
async function connectTerminalWs(wsPath: string): Promise<void> {
|
|
136
|
+
if (disposed) { return; }
|
|
137
|
+
|
|
138
|
+
const ticket = await getWsTicket();
|
|
139
|
+
if (!ticket || disposed) { return; }
|
|
140
|
+
|
|
141
|
+
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
|
|
142
|
+
const ws = new WebSocket(`${protocol}//${window.location.host}${wsPath}?ticket=${ticket}`);
|
|
143
|
+
|
|
144
|
+
ws.onopen = () => {
|
|
145
|
+
if (disposed) { ws.close(); return; }
|
|
146
|
+
terminalWs = ws;
|
|
147
|
+
if (sendStatus === 'ready' && pendingMessage) {
|
|
148
|
+
ws.send(JSON.stringify({ data: `${pendingMessage}\n`, type: 'input' }));
|
|
149
|
+
pendingMessage = null;
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
ws.onclose = () => {
|
|
154
|
+
if (terminalWs === ws) { terminalWs = null; }
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// --- Message handler (mirrors terminal page) ---
|
|
159
|
+
|
|
160
|
+
function handleSessionMessage(msg: Record<string, unknown>): void {
|
|
161
|
+
if (msg.type === 'history') {
|
|
162
|
+
const historyRaw = (msg.messages || []) as {
|
|
163
|
+
content: MessagePart[];
|
|
164
|
+
id: string;
|
|
165
|
+
role: string;
|
|
166
|
+
timestamp: string;
|
|
167
|
+
}[];
|
|
168
|
+
messages = historyRaw.map((m) => ({
|
|
169
|
+
id: m.id,
|
|
170
|
+
parts: m.content,
|
|
171
|
+
role: m.role as ConversationMessage['role'],
|
|
172
|
+
timestamp: m.timestamp,
|
|
173
|
+
}));
|
|
174
|
+
// WS history is always complete
|
|
175
|
+
hasMoreMessages = false;
|
|
176
|
+
|
|
177
|
+
if (sendStatus === 'resuming') {
|
|
178
|
+
sendStatus = 'ready';
|
|
179
|
+
if (pendingMessage && terminalWs?.readyState === WebSocket.OPEN) {
|
|
180
|
+
terminalWs.send(JSON.stringify({ data: `${pendingMessage}\n`, type: 'input' }));
|
|
181
|
+
pendingMessage = null;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
} else if (msg.type === 'message') {
|
|
185
|
+
messages = [
|
|
186
|
+
...messages,
|
|
187
|
+
{
|
|
188
|
+
id: `msg-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
|
|
189
|
+
parts: (msg.content as MessagePart[]) || [],
|
|
190
|
+
role: (msg.role as ConversationMessage['role']) || 'assistant',
|
|
191
|
+
timestamp: (msg.timestamp as string) || new Date().toISOString(),
|
|
192
|
+
},
|
|
193
|
+
];
|
|
194
|
+
} else if (msg.type === 'tool-use') {
|
|
195
|
+
const toolUsePart: ToolUsePart = {
|
|
196
|
+
id: (msg.id as string) || `tool-${Date.now()}`,
|
|
197
|
+
input: (msg.input as Record<string, unknown>) || {},
|
|
198
|
+
toolName: msg.name as string,
|
|
84
199
|
type: 'tool_use',
|
|
85
200
|
};
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
201
|
+
const lastToolMsg = messages.length > 0 ? messages[messages.length - 1] : null;
|
|
202
|
+
if (lastToolMsg?.role === 'assistant') {
|
|
203
|
+
messages = [
|
|
204
|
+
...messages.slice(0, -1),
|
|
205
|
+
{ ...lastToolMsg, parts: [...lastToolMsg.parts, toolUsePart] },
|
|
206
|
+
];
|
|
207
|
+
} else {
|
|
208
|
+
messages = [
|
|
209
|
+
...messages,
|
|
92
210
|
{
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
211
|
+
id: `tool-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
|
|
212
|
+
parts: [toolUsePart],
|
|
213
|
+
role: 'assistant',
|
|
214
|
+
timestamp: new Date().toISOString(),
|
|
97
215
|
},
|
|
98
|
-
]
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
content: data.text || '',
|
|
107
|
-
type: 'thinking',
|
|
108
|
-
};
|
|
109
|
-
appendToLastAssistant(thinkPart);
|
|
110
|
-
} else if (data.type === 'error') {
|
|
111
|
-
// Server-sent error -- display in the UI
|
|
112
|
-
const errorMsg: ConversationMessage = {
|
|
113
|
-
id: nextWsMessageId(),
|
|
114
|
-
parts: [{ content: data.text || data.message || 'Unknown error', type: 'text' }],
|
|
115
|
-
role: 'system',
|
|
116
|
-
timestamp: new Date().toISOString(),
|
|
216
|
+
];
|
|
217
|
+
}
|
|
218
|
+
} else if (msg.type === 'tool-result') {
|
|
219
|
+
const toolResultPart: MessagePart = {
|
|
220
|
+
isError: (msg.isError as boolean) || false,
|
|
221
|
+
output: (msg.output as string) || '',
|
|
222
|
+
toolUseId: msg.id as string,
|
|
223
|
+
type: 'tool_result',
|
|
117
224
|
};
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
225
|
+
const lastResultMsg = messages.length > 0 ? messages[messages.length - 1] : null;
|
|
226
|
+
if (lastResultMsg?.role === 'system') {
|
|
227
|
+
messages = [
|
|
228
|
+
...messages.slice(0, -1),
|
|
229
|
+
{ ...lastResultMsg, parts: [...lastResultMsg.parts, toolResultPart] },
|
|
230
|
+
];
|
|
231
|
+
} else {
|
|
232
|
+
messages = [
|
|
233
|
+
...messages,
|
|
234
|
+
{
|
|
235
|
+
id: `result-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
|
|
236
|
+
parts: [toolResultPart],
|
|
237
|
+
role: 'system',
|
|
238
|
+
timestamp: new Date().toISOString(),
|
|
239
|
+
},
|
|
240
|
+
];
|
|
241
|
+
}
|
|
242
|
+
} else if (msg.type === 'thinking') {
|
|
243
|
+
const thinkPart: MessagePart = { content: (msg.text as string) || '', type: 'thinking' };
|
|
244
|
+
const lastMsg = messages.length > 0 ? messages[messages.length - 1] : null;
|
|
245
|
+
if (lastMsg?.role === 'assistant') {
|
|
246
|
+
messages = [
|
|
247
|
+
...messages.slice(0, -1),
|
|
248
|
+
{ ...lastMsg, parts: [...lastMsg.parts, thinkPart] },
|
|
249
|
+
];
|
|
250
|
+
} else {
|
|
251
|
+
messages = [
|
|
252
|
+
...messages,
|
|
253
|
+
{
|
|
254
|
+
id: `think-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
|
|
255
|
+
parts: [thinkPart],
|
|
256
|
+
role: 'assistant',
|
|
257
|
+
timestamp: new Date().toISOString(),
|
|
258
|
+
},
|
|
259
|
+
];
|
|
129
260
|
}
|
|
261
|
+
} else if (msg.type === 'session-end') {
|
|
262
|
+
sendStatus = 'idle';
|
|
130
263
|
}
|
|
131
264
|
}
|
|
132
265
|
|
|
133
|
-
//
|
|
134
|
-
function appendToLastAssistant(part: MessagePart): void {
|
|
135
|
-
const lastMsg = messages.length > 0 ? messages[messages.length - 1] : null;
|
|
136
|
-
if (lastMsg?.role === 'assistant') {
|
|
137
|
-
// Mutate the parts array and reassign to trigger reactivity
|
|
138
|
-
const updatedParts = [...lastMsg.parts, part];
|
|
139
|
-
const updatedMsg = { ...lastMsg, parts: updatedParts };
|
|
140
|
-
messages = [...messages.slice(0, -1), updatedMsg];
|
|
141
|
-
} else {
|
|
142
|
-
// Create a new assistant message to hold this part
|
|
143
|
-
const newMsg: ConversationMessage = {
|
|
144
|
-
id: nextWsMessageId(),
|
|
145
|
-
parts: [part],
|
|
146
|
-
role: 'assistant',
|
|
147
|
-
timestamp: new Date().toISOString(),
|
|
148
|
-
};
|
|
149
|
-
messages = [...messages, newMsg];
|
|
150
|
-
}
|
|
151
|
-
}
|
|
266
|
+
// --- Connect + send flow ---
|
|
152
267
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
if (!browser || !isSessionActive || disposed) {
|
|
156
|
-
return;
|
|
157
|
-
}
|
|
268
|
+
async function connectAndSend(): Promise<void> {
|
|
269
|
+
if (!session || disposed) { sendStatus = 'idle'; return; }
|
|
158
270
|
|
|
159
|
-
const
|
|
160
|
-
if (!
|
|
161
|
-
return;
|
|
162
|
-
}
|
|
163
|
-
let config: ShooterConfig;
|
|
164
|
-
try {
|
|
165
|
-
config = JSON.parse(saved) as ShooterConfig;
|
|
166
|
-
} catch {
|
|
167
|
-
return;
|
|
168
|
-
}
|
|
271
|
+
const config = getConfig();
|
|
272
|
+
if (!config) { sendStatus = 'idle'; return; }
|
|
169
273
|
|
|
170
|
-
|
|
274
|
+
sendStatus = 'connecting';
|
|
171
275
|
|
|
172
276
|
try {
|
|
173
|
-
|
|
174
|
-
const
|
|
175
|
-
|
|
277
|
+
const command = session.source === 'opencode' ? 'opencode' : 'claude';
|
|
278
|
+
const res = await fetch('/api/sessions/connect', {
|
|
279
|
+
body: JSON.stringify({ command, cwd: session.projectPath, sessionId }),
|
|
280
|
+
headers: {
|
|
281
|
+
Authorization: `Bearer ${config.apiKey}`,
|
|
282
|
+
'Content-Type': 'application/json',
|
|
283
|
+
},
|
|
176
284
|
method: 'POST',
|
|
177
285
|
});
|
|
178
|
-
if (!ticketRes.ok || disposed) {
|
|
179
|
-
connectionState = 'disconnected';
|
|
180
|
-
// Schedule retry with same backoff logic as onclose
|
|
181
|
-
if (isSessionActive && !disposed && wsReconnectAttempts < 5) {
|
|
182
|
-
wsReconnectAttempts++;
|
|
183
|
-
wsReconnectTimer = window.setTimeout(() => {
|
|
184
|
-
void connectWebSocket();
|
|
185
|
-
}, 2000);
|
|
186
|
-
}
|
|
187
|
-
return;
|
|
188
|
-
}
|
|
189
|
-
const { ticket } = await ticketRes.json();
|
|
190
|
-
|
|
191
|
-
if (disposed) {
|
|
192
|
-
connectionState = 'disconnected';
|
|
193
|
-
return;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
// Build WebSocket URL
|
|
197
|
-
const wsProtocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
|
|
198
|
-
const wsUrl = `${wsProtocol}//${window.location.host}/ws/session/${sessionId}?ticket=${ticket}`;
|
|
199
|
-
|
|
200
|
-
const socket = new WebSocket(wsUrl);
|
|
201
286
|
|
|
202
|
-
|
|
203
|
-
if (disposed) {
|
|
204
|
-
socket.close();
|
|
205
|
-
return;
|
|
206
|
-
}
|
|
207
|
-
connectionState = 'connected';
|
|
208
|
-
ws = socket;
|
|
209
|
-
wsReconnectAttempts = 0; // Reset on successful connection
|
|
210
|
-
};
|
|
287
|
+
if (!res.ok || disposed) { sendStatus = 'idle'; return; }
|
|
211
288
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
connectionState = 'disconnected';
|
|
216
|
-
ws = null;
|
|
217
|
-
// Reconnect with backoff (max 5 retries)
|
|
218
|
-
if (isSessionActive && !disposed && wsReconnectAttempts < 5) {
|
|
219
|
-
wsReconnectAttempts++;
|
|
220
|
-
wsReconnectTimer = window.setTimeout(() => {
|
|
221
|
-
void connectWebSocket();
|
|
222
|
-
}, 2000);
|
|
223
|
-
}
|
|
224
|
-
};
|
|
289
|
+
const result: { sessionWs: string; terminalId: string; ws: string } = await res.json();
|
|
290
|
+
connectedTerminalId = result.terminalId;
|
|
291
|
+
sendStatus = 'resuming';
|
|
225
292
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
ws = null;
|
|
229
|
-
};
|
|
293
|
+
void connectSessionWs(result.sessionWs, result.terminalId);
|
|
294
|
+
void connectTerminalWs(result.ws);
|
|
230
295
|
} catch {
|
|
231
|
-
|
|
296
|
+
sendStatus = 'idle';
|
|
232
297
|
}
|
|
233
298
|
}
|
|
234
299
|
|
|
235
|
-
|
|
236
|
-
return new Date(ts).toLocaleDateString([], {
|
|
237
|
-
day: 'numeric',
|
|
238
|
-
month: 'short',
|
|
239
|
-
year: 'numeric',
|
|
240
|
-
});
|
|
241
|
-
}
|
|
300
|
+
// --- Called by ChatView on submit ---
|
|
242
301
|
|
|
243
|
-
function
|
|
244
|
-
|
|
245
|
-
return parts.slice(-2).join('/');
|
|
246
|
-
}
|
|
302
|
+
function sendMessage(text: string): void {
|
|
303
|
+
if (!text.trim()) { return; }
|
|
247
304
|
|
|
248
|
-
|
|
249
|
-
|
|
305
|
+
if (sendStatus === 'ready' && terminalWs?.readyState === WebSocket.OPEN) {
|
|
306
|
+
terminalWs.send(JSON.stringify({ data: `${text}\n`, type: 'input' }));
|
|
250
307
|
return;
|
|
251
308
|
}
|
|
252
309
|
|
|
253
|
-
//
|
|
254
|
-
if (
|
|
255
|
-
|
|
310
|
+
// Already connecting — just update queued message
|
|
311
|
+
if (sendStatus === 'connecting' || sendStatus === 'resuming') {
|
|
312
|
+
pendingMessage = text;
|
|
313
|
+
return;
|
|
256
314
|
}
|
|
257
315
|
|
|
316
|
+
// Not connected yet — queue and connect
|
|
317
|
+
pendingMessage = text;
|
|
318
|
+
void connectAndSend();
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// --- REST fetch (initial messages) ---
|
|
322
|
+
|
|
323
|
+
async function fetchSession(): Promise<void> {
|
|
324
|
+
if (!browser) { return; }
|
|
325
|
+
if (messages.length === 0) { loading = true; }
|
|
326
|
+
|
|
258
327
|
try {
|
|
259
|
-
const
|
|
260
|
-
if (!
|
|
328
|
+
const config = getConfig();
|
|
329
|
+
if (!config) {
|
|
261
330
|
error = 'No configuration found. Please configure settings first.';
|
|
262
331
|
loading = false;
|
|
263
332
|
return;
|
|
264
333
|
}
|
|
265
|
-
let config: ShooterConfig;
|
|
266
|
-
try {
|
|
267
|
-
config = JSON.parse(saved) as ShooterConfig;
|
|
268
|
-
} catch {
|
|
269
|
-
error = 'Invalid saved configuration. Please reconfigure in settings.';
|
|
270
|
-
loading = false;
|
|
271
|
-
return;
|
|
272
|
-
}
|
|
273
334
|
|
|
274
335
|
const sid = sessionId || '';
|
|
275
336
|
const pid = projectId || '';
|
|
276
337
|
const queryStr = pid
|
|
277
|
-
? `id=${encodeURIComponent(sid)}&project=${encodeURIComponent(pid)}`
|
|
278
|
-
: `id=${encodeURIComponent(sid)}`;
|
|
279
|
-
|
|
338
|
+
? `id=${encodeURIComponent(sid)}&project=${encodeURIComponent(pid)}&limit=${currentLimit}`
|
|
339
|
+
: `id=${encodeURIComponent(sid)}&limit=${currentLimit}`;
|
|
340
|
+
|
|
341
|
+
let res = await fetch(`/api/sessions?${queryStr}`, {
|
|
280
342
|
headers: { Authorization: `Bearer ${config.apiKey}` },
|
|
281
343
|
});
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
344
|
+
|
|
345
|
+
// Fallback: search across all projects if not found
|
|
346
|
+
if (res.status === 404 && !pid) {
|
|
347
|
+
const projectsRes = await fetch('/api/sessions', {
|
|
348
|
+
headers: { Authorization: `Bearer ${config.apiKey}` },
|
|
349
|
+
});
|
|
350
|
+
if (projectsRes.ok) {
|
|
351
|
+
const projectsData: { projects?: { id: string; sessions?: { id: string }[] }[] } =
|
|
352
|
+
await projectsRes.json();
|
|
353
|
+
for (const project of projectsData.projects || []) {
|
|
354
|
+
if ((project.sessions || []).find((s) => s.id === sid)) {
|
|
355
|
+
res = await fetch(
|
|
356
|
+
`/api/sessions?id=${encodeURIComponent(sid)}&project=${encodeURIComponent(project.id)}`,
|
|
357
|
+
{ headers: { Authorization: `Bearer ${config.apiKey}` } }
|
|
358
|
+
);
|
|
359
|
+
break;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
}
|
|
286
363
|
}
|
|
364
|
+
|
|
365
|
+
if (!res.ok) { error = 'Session not found'; loading = false; return; }
|
|
366
|
+
|
|
287
367
|
const data: SessionViewResponse = await res.json();
|
|
288
368
|
session = data.session as SessionInfo;
|
|
289
|
-
const rawMessages
|
|
290
|
-
messages =
|
|
369
|
+
const rawMessages = Array.isArray(data.messages) ? (data.messages as unknown as ConversationMessage[]) : [];
|
|
370
|
+
messages = rawMessages;
|
|
371
|
+
hasMoreMessages = rawMessages.length >= currentLimit;
|
|
291
372
|
setCache(`shooter_session_${sid}`, { messages: rawMessages, session: data.session });
|
|
292
|
-
|
|
293
|
-
// Check if session is active and connect WebSocket if so
|
|
294
|
-
if (data.session) {
|
|
295
|
-
isSessionActive = checkSessionActive(data.session as SessionInfo);
|
|
296
|
-
if (isSessionActive) {
|
|
297
|
-
void connectWebSocket();
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
373
|
} catch {
|
|
301
374
|
error = 'Failed to load session';
|
|
302
375
|
}
|
|
303
376
|
loading = false;
|
|
304
377
|
}
|
|
305
378
|
|
|
379
|
+
async function loadEarlierMessages(): Promise<void> {
|
|
380
|
+
if (!browser || loadingMore) { return; }
|
|
381
|
+
const config = getConfig();
|
|
382
|
+
if (!config) { return; }
|
|
383
|
+
|
|
384
|
+
loadingMore = true;
|
|
385
|
+
try {
|
|
386
|
+
const newLimit = currentLimit + 200;
|
|
387
|
+
const sid = sessionId || '';
|
|
388
|
+
const pid = projectId || '';
|
|
389
|
+
const queryParts = [`id=${encodeURIComponent(sid)}`, `limit=${newLimit}`];
|
|
390
|
+
if (pid) { queryParts.push(`project=${encodeURIComponent(pid)}`); }
|
|
391
|
+
const res = await fetch(`/api/sessions?${queryParts.join('&')}`, {
|
|
392
|
+
headers: { Authorization: `Bearer ${config.apiKey}` },
|
|
393
|
+
});
|
|
394
|
+
if (!res.ok) { return; }
|
|
395
|
+
const data: SessionViewResponse = await res.json();
|
|
396
|
+
const rawMessages = Array.isArray(data.messages) ? (data.messages as unknown as ConversationMessage[]) : [];
|
|
397
|
+
messages = rawMessages;
|
|
398
|
+
currentLimit = newLimit;
|
|
399
|
+
hasMoreMessages = rawMessages.length >= newLimit;
|
|
400
|
+
setCache(`shooter_session_${sid}`, { messages: rawMessages, session: data.session });
|
|
401
|
+
} catch { /* best effort */ } finally { loadingMore = false; }
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// --- Lifecycle ---
|
|
405
|
+
|
|
306
406
|
onMount(() => {
|
|
307
407
|
// Show cached data immediately
|
|
308
408
|
const cached = getCached(`shooter_session_${page.params.id}`) as null | {
|
|
@@ -311,23 +411,49 @@
|
|
|
311
411
|
};
|
|
312
412
|
if (cached) {
|
|
313
413
|
session = cached.session;
|
|
314
|
-
messages = Array.isArray(cached.messages) ?
|
|
414
|
+
messages = Array.isArray(cached.messages) ? cached.messages : [];
|
|
315
415
|
loading = false;
|
|
316
416
|
}
|
|
317
417
|
|
|
318
|
-
void fetchSession()
|
|
418
|
+
void fetchSession().then(async () => {
|
|
419
|
+
if (!session || disposed) { return; }
|
|
420
|
+
|
|
421
|
+
// Check if there's already a running terminal for this session
|
|
422
|
+
const config = getConfig();
|
|
423
|
+
if (!config) { return; }
|
|
424
|
+
|
|
425
|
+
try {
|
|
426
|
+
const command = session.source === 'opencode' ? 'opencode' : 'claude';
|
|
427
|
+
const res = await fetch('/api/sessions/connect', {
|
|
428
|
+
body: JSON.stringify({
|
|
429
|
+
command,
|
|
430
|
+
cwd: session.projectPath,
|
|
431
|
+
noCreate: true,
|
|
432
|
+
sessionId,
|
|
433
|
+
}),
|
|
434
|
+
headers: {
|
|
435
|
+
Authorization: `Bearer ${config.apiKey}`,
|
|
436
|
+
'Content-Type': 'application/json',
|
|
437
|
+
},
|
|
438
|
+
method: 'POST',
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
if (res.ok && !disposed) {
|
|
442
|
+
const result: { sessionWs: string; terminalId: string; ws: string } = await res.json();
|
|
443
|
+
connectedTerminalId = result.terminalId;
|
|
444
|
+
sendStatus = 'resuming';
|
|
445
|
+
void connectSessionWs(result.sessionWs, result.terminalId);
|
|
446
|
+
void connectTerminalWs(result.ws);
|
|
447
|
+
}
|
|
448
|
+
// 404 = no existing terminal, that's fine
|
|
449
|
+
} catch { /* ignore */ }
|
|
450
|
+
});
|
|
319
451
|
|
|
320
|
-
// Cleanup WebSocket and reconnect timer on component destroy
|
|
321
452
|
return () => {
|
|
322
453
|
disposed = true;
|
|
323
|
-
if (
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
}
|
|
327
|
-
if (ws) {
|
|
328
|
-
ws.close();
|
|
329
|
-
ws = null;
|
|
330
|
-
}
|
|
454
|
+
if (sessionReconnectTimer) { clearTimeout(sessionReconnectTimer); }
|
|
455
|
+
sessionWs?.close();
|
|
456
|
+
terminalWs?.close();
|
|
331
457
|
};
|
|
332
458
|
});
|
|
333
459
|
</script>
|
|
@@ -346,107 +472,159 @@
|
|
|
346
472
|
<Shimmer classes="shimmer-bubble shimmer-bubble-assistant" />
|
|
347
473
|
<Shimmer classes="shimmer-bubble shimmer-bubble-user-short" />
|
|
348
474
|
<Shimmer classes="shimmer-bubble shimmer-bubble-assistant-wide" />
|
|
349
|
-
<Shimmer classes="shimmer-bubble shimmer-bubble-assistant-short" />
|
|
350
475
|
</div>
|
|
351
476
|
</div>
|
|
352
477
|
{:else if error}
|
|
353
478
|
<div class="session-back-row">
|
|
354
|
-
<a href=
|
|
355
|
-
<span class="back-arrow">←</span>
|
|
356
|
-
Back
|
|
357
|
-
</a>
|
|
479
|
+
<a href={projectId ? `/project?id=${projectId}` : '/'} class="back-link">← Back</a>
|
|
358
480
|
</div>
|
|
359
|
-
<
|
|
481
|
+
<p class="error-text">{error}</p>
|
|
360
482
|
{:else if session}
|
|
361
|
-
<!--
|
|
362
|
-
<div class="
|
|
363
|
-
<div class="
|
|
364
|
-
<a href=
|
|
365
|
-
{#if
|
|
366
|
-
<
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
<span class="connection-status-dot"></span>
|
|
374
|
-
</span>
|
|
375
|
-
{:else if connectionState === 'disconnected'}
|
|
376
|
-
<span class="connection-status disconnected">
|
|
377
|
-
<span class="connection-status-dot"></span>
|
|
378
|
-
</span>
|
|
379
|
-
{/if}
|
|
380
|
-
</div>
|
|
381
|
-
{/if}
|
|
382
|
-
</div>
|
|
383
|
-
<div class="chat-session-title-row">
|
|
384
|
-
<h1 class="chat-session-title">{session.title}</h1>
|
|
385
|
-
{#if isSessionActive}
|
|
386
|
-
<Pill text="LIVE" classes="pill-live" />
|
|
483
|
+
<!-- Compact header -->
|
|
484
|
+
<div class="session-header">
|
|
485
|
+
<div class="session-header-row">
|
|
486
|
+
<a href={projectId ? `/project?id=${projectId}` : '/'} class="back-link">← Back</a>
|
|
487
|
+
{#if sendStatus === 'connecting'}
|
|
488
|
+
<span class="resume-status">Connecting...</span>
|
|
489
|
+
{:else if sendStatus === 'resuming'}
|
|
490
|
+
<span class="resume-status">Resuming session...</span>
|
|
491
|
+
{:else if chatConnectionState === 'connected'}
|
|
492
|
+
<span class="connection-dot connected"></span>
|
|
493
|
+
{:else if chatConnectionState === 'reconnecting'}
|
|
494
|
+
<span class="connection-dot reconnecting"></span>
|
|
387
495
|
{/if}
|
|
388
496
|
</div>
|
|
389
|
-
<
|
|
390
|
-
<span class="chat-session-meta-item">📁 {shortPath(session.projectPath)}</span>
|
|
391
|
-
<span class="chat-session-meta-item">🌿 {session.gitBranch}</span>
|
|
392
|
-
<span class="chat-session-meta-item">💬 {session.messageCount} messages</span>
|
|
393
|
-
<span class="chat-session-meta-item">📅 {formatDate(session.created)}</span>
|
|
394
|
-
</div>
|
|
395
|
-
{#if connectionState === 'disconnected' && isSessionActive}
|
|
396
|
-
<Banner text="Live updates paused" classes="banner-warning" />
|
|
397
|
-
{/if}
|
|
497
|
+
<h1 class="session-title">{session.title}</h1>
|
|
398
498
|
</div>
|
|
399
499
|
|
|
400
|
-
<!-- Chat
|
|
500
|
+
<!-- Chat -->
|
|
401
501
|
<div class="session-chat-container">
|
|
402
|
-
|
|
502
|
+
{#if hasMoreMessages}
|
|
503
|
+
<div class="load-earlier-row">
|
|
504
|
+
<button
|
|
505
|
+
class="load-earlier-btn"
|
|
506
|
+
onclick={loadEarlierMessages}
|
|
507
|
+
disabled={loadingMore}
|
|
508
|
+
>
|
|
509
|
+
{loadingMore ? 'Loading...' : 'Load earlier messages'}
|
|
510
|
+
</button>
|
|
511
|
+
</div>
|
|
512
|
+
{/if}
|
|
513
|
+
<ChatView
|
|
514
|
+
messages={[...messages].reverse()}
|
|
515
|
+
newestFirst={true}
|
|
516
|
+
connectionState={chatConnectionState}
|
|
517
|
+
showInput={true}
|
|
518
|
+
{sendDisabled}
|
|
519
|
+
onSendInput={sendMessage}
|
|
520
|
+
sessionEnded={false}
|
|
521
|
+
/>
|
|
403
522
|
</div>
|
|
404
523
|
{/if}
|
|
405
524
|
</main>
|
|
406
525
|
|
|
407
526
|
<style>
|
|
408
|
-
/* Make the main element a flex column so the chat fills remaining space */
|
|
409
527
|
.session-page-main {
|
|
410
528
|
display: flex;
|
|
411
529
|
flex-direction: column;
|
|
412
530
|
overflow: hidden;
|
|
413
531
|
}
|
|
414
532
|
|
|
415
|
-
/* Chat container fills remaining space */
|
|
416
|
-
.session-chat-container {
|
|
417
|
-
flex: 1;
|
|
418
|
-
min-height: 0;
|
|
419
|
-
display: flex;
|
|
420
|
-
flex-direction: column;
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
/* Back link (used in error state) */
|
|
424
533
|
.session-back-row {
|
|
425
534
|
margin-bottom: var(--space-5);
|
|
426
535
|
}
|
|
427
536
|
|
|
428
|
-
|
|
429
|
-
|
|
537
|
+
.error-text {
|
|
538
|
+
color: var(--text-secondary);
|
|
539
|
+
padding: var(--space-4);
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
/* Compact header */
|
|
543
|
+
.session-header {
|
|
544
|
+
padding: var(--space-3) var(--space-4);
|
|
545
|
+
border-bottom: 1px solid var(--border);
|
|
546
|
+
flex-shrink: 0;
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
.session-header-row {
|
|
430
550
|
display: flex;
|
|
431
551
|
align-items: center;
|
|
432
552
|
justify-content: space-between;
|
|
553
|
+
margin-bottom: var(--space-1);
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
.session-title {
|
|
557
|
+
font-size: var(--text-base);
|
|
558
|
+
font-weight: 600;
|
|
559
|
+
color: var(--text-primary);
|
|
560
|
+
margin: 0;
|
|
561
|
+
white-space: nowrap;
|
|
562
|
+
overflow: hidden;
|
|
563
|
+
text-overflow: ellipsis;
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
.resume-status {
|
|
567
|
+
font-size: 0.75rem;
|
|
568
|
+
color: var(--text-tertiary, #888);
|
|
569
|
+
animation: pulse 1.5s ease-in-out infinite;
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
@keyframes pulse {
|
|
573
|
+
0%, 100% { opacity: 1; }
|
|
574
|
+
50% { opacity: 0.4; }
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
.connection-dot {
|
|
578
|
+
display: inline-block;
|
|
579
|
+
width: 7px;
|
|
580
|
+
height: 7px;
|
|
581
|
+
border-radius: 50%;
|
|
582
|
+
flex-shrink: 0;
|
|
433
583
|
}
|
|
434
584
|
|
|
435
|
-
|
|
436
|
-
|
|
585
|
+
.connection-dot.connected {
|
|
586
|
+
background: #4ade80;
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
.connection-dot.reconnecting {
|
|
590
|
+
background: #f59e0b;
|
|
591
|
+
animation: pulse 1s ease-in-out infinite;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
/* Chat container fills remaining space */
|
|
595
|
+
.session-chat-container {
|
|
596
|
+
flex: 1;
|
|
597
|
+
min-height: 0;
|
|
437
598
|
display: flex;
|
|
438
|
-
|
|
439
|
-
gap: 0.5rem;
|
|
599
|
+
flex-direction: column;
|
|
440
600
|
}
|
|
441
601
|
|
|
442
|
-
/*
|
|
443
|
-
.
|
|
602
|
+
/* Load earlier row */
|
|
603
|
+
.load-earlier-row {
|
|
444
604
|
display: flex;
|
|
445
|
-
|
|
605
|
+
justify-content: center;
|
|
606
|
+
padding: var(--space-2) var(--space-3);
|
|
607
|
+
flex-shrink: 0;
|
|
608
|
+
border-bottom: 1px solid var(--border);
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
.load-earlier-btn {
|
|
612
|
+
font-size: 0.8rem;
|
|
613
|
+
color: var(--text-secondary);
|
|
614
|
+
background: none;
|
|
615
|
+
border: 1px solid var(--border);
|
|
616
|
+
border-radius: var(--radius-sm, 4px);
|
|
617
|
+
padding: 4px 12px;
|
|
618
|
+
cursor: pointer;
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
.load-earlier-btn:disabled {
|
|
622
|
+
opacity: 0.5;
|
|
623
|
+
cursor: not-allowed;
|
|
446
624
|
}
|
|
447
625
|
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
626
|
+
.load-earlier-btn:hover:not(:disabled) {
|
|
627
|
+
color: var(--text-primary);
|
|
628
|
+
border-color: var(--text-tertiary, #888);
|
|
451
629
|
}
|
|
452
630
|
</style>
|