@juspay/shooter 1.21.0 → 1.23.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 +94 -1
- package/build/client/_app/immutable/assets/2.JWRrnR-w.css +1 -0
- package/build/client/_app/immutable/assets/2.JWRrnR-w.css.br +0 -0
- package/build/client/_app/immutable/assets/2.JWRrnR-w.css.gz +0 -0
- package/build/client/_app/immutable/chunks/{DOEXXmsh.js → Bj5wFimK.js} +2 -2
- package/build/client/_app/immutable/chunks/Bj5wFimK.js.br +0 -0
- package/build/client/_app/immutable/chunks/Bj5wFimK.js.gz +0 -0
- package/build/client/_app/immutable/chunks/{EqMAkEha.js → BjYr_-Ss.js} +1 -1
- package/build/client/_app/immutable/chunks/BjYr_-Ss.js.br +0 -0
- package/build/client/_app/immutable/chunks/BjYr_-Ss.js.gz +0 -0
- package/build/client/_app/immutable/chunks/C4Hns_Wl.js +1 -0
- package/build/client/_app/immutable/chunks/C4Hns_Wl.js.br +0 -0
- package/build/client/_app/immutable/chunks/C4Hns_Wl.js.gz +0 -0
- package/build/client/_app/immutable/chunks/DULfdsh6.js +6 -0
- package/build/client/_app/immutable/chunks/DULfdsh6.js.br +0 -0
- package/build/client/_app/immutable/chunks/DULfdsh6.js.gz +0 -0
- package/build/client/_app/immutable/chunks/{BmfLecb1.js → fcNfTA-E.js} +1 -1
- package/build/client/_app/immutable/chunks/fcNfTA-E.js.br +0 -0
- package/build/client/_app/immutable/chunks/fcNfTA-E.js.gz +0 -0
- package/build/client/_app/immutable/entry/{app.CeSxgGat.js → app.Bvoqymnp.js} +2 -2
- package/build/client/_app/immutable/entry/app.Bvoqymnp.js.br +0 -0
- package/build/client/_app/immutable/entry/app.Bvoqymnp.js.gz +0 -0
- package/build/client/_app/immutable/entry/start.BqXCPPZJ.js +1 -0
- package/build/client/_app/immutable/entry/start.BqXCPPZJ.js.br +2 -0
- package/build/client/_app/immutable/entry/start.BqXCPPZJ.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{0.oaPwxh1O.js → 0.Bv_TwEnq.js} +1 -1
- package/build/client/_app/immutable/nodes/0.Bv_TwEnq.js.br +0 -0
- package/build/client/_app/immutable/nodes/0.Bv_TwEnq.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{1.DMPyoM-M.js → 1.7lffTIeb.js} +1 -1
- package/build/client/_app/immutable/nodes/1.7lffTIeb.js.br +0 -0
- package/build/client/_app/immutable/nodes/1.7lffTIeb.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{10.Cbm7nQKK.js → 10.ChiIrIDl.js} +1 -1
- package/build/client/_app/immutable/nodes/10.ChiIrIDl.js.br +0 -0
- package/build/client/_app/immutable/nodes/10.ChiIrIDl.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{11.CKmZjP_a.js → 11.DO3vyXEv.js} +2 -2
- package/build/client/_app/immutable/nodes/11.DO3vyXEv.js.br +0 -0
- package/build/client/_app/immutable/nodes/{11.CKmZjP_a.js.gz → 11.DO3vyXEv.js.gz} +0 -0
- package/build/client/_app/immutable/nodes/2.iMIqsE7n.js +23 -0
- package/build/client/_app/immutable/nodes/2.iMIqsE7n.js.br +0 -0
- package/build/client/_app/immutable/nodes/2.iMIqsE7n.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{3.BgLpGnzb.js → 3.CArnSHOO.js} +1 -1
- package/build/client/_app/immutable/nodes/3.CArnSHOO.js.br +0 -0
- package/build/client/_app/immutable/nodes/3.CArnSHOO.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{5.Avc1-gVb.js → 5.DziEu9rx.js} +1 -1
- package/build/client/_app/immutable/nodes/5.DziEu9rx.js.br +0 -0
- package/build/client/_app/immutable/nodes/5.DziEu9rx.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{6.Dw2wEssJ.js → 6.B8l1RwkB.js} +1 -1
- package/build/client/_app/immutable/nodes/6.B8l1RwkB.js.br +0 -0
- package/build/client/_app/immutable/nodes/6.B8l1RwkB.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{7.DwKZjoBg.js → 7.BPyfhDis.js} +1 -1
- package/build/client/_app/immutable/nodes/7.BPyfhDis.js.br +0 -0
- package/build/client/_app/immutable/nodes/7.BPyfhDis.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{8.ZUAI6g5E.js → 8.D_vszZ9E.js} +1 -1
- package/build/client/_app/immutable/nodes/8.D_vszZ9E.js.br +0 -0
- package/build/client/_app/immutable/nodes/8.D_vszZ9E.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{9.I_KGXPwB.js → 9.Drah-do-.js} +1 -1
- package/build/client/_app/immutable/nodes/9.Drah-do-.js.br +0 -0
- package/build/client/_app/immutable/nodes/9.Drah-do-.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/pty-holder.cjs +6 -0
- package/build/server/chunks/{0-vrTNAfZB.js → 0-DAB_6Vm1.js} +2 -2
- package/build/server/chunks/{0-vrTNAfZB.js.map → 0-DAB_6Vm1.js.map} +1 -1
- package/build/server/chunks/{1-nbr-bOoF.js → 1-D-qMYaCx.js} +2 -2
- package/build/server/chunks/{1-nbr-bOoF.js.map → 1-D-qMYaCx.js.map} +1 -1
- package/build/server/chunks/{10-ChyvvJ6w.js → 10-CeFFGo-X.js} +2 -2
- package/build/server/chunks/{10-ChyvvJ6w.js.map → 10-CeFFGo-X.js.map} +1 -1
- package/build/server/chunks/{11-6ZAjL3uU.js → 11-DRMu_ATU.js} +2 -2
- package/build/server/chunks/{11-6ZAjL3uU.js.map → 11-DRMu_ATU.js.map} +1 -1
- package/build/server/chunks/{2-DWFRVDWJ.js → 2-B7OLBMNH.js} +4 -4
- package/build/server/chunks/{2-DWFRVDWJ.js.map → 2-B7OLBMNH.js.map} +1 -1
- package/build/server/chunks/{3-CKANM_WM.js → 3-B38ZarLw.js} +2 -2
- package/build/server/chunks/{3-CKANM_WM.js.map → 3-B38ZarLw.js.map} +1 -1
- package/build/server/chunks/{5-BxVjs2qi.js → 5-D-Uv1voC.js} +2 -2
- package/build/server/chunks/{5-BxVjs2qi.js.map → 5-D-Uv1voC.js.map} +1 -1
- package/build/server/chunks/{6-Cbf1AAMQ.js → 6-DP46cUej.js} +2 -2
- package/build/server/chunks/{6-Cbf1AAMQ.js.map → 6-DP46cUej.js.map} +1 -1
- package/build/server/chunks/{7-CMK2quEf.js → 7-B29_3ar6.js} +2 -2
- package/build/server/chunks/{7-CMK2quEf.js.map → 7-B29_3ar6.js.map} +1 -1
- package/build/server/chunks/{8-DhdfkfDM.js → 8-DCnSDVrX.js} +2 -2
- package/build/server/chunks/{8-DhdfkfDM.js.map → 8-DCnSDVrX.js.map} +1 -1
- package/build/server/chunks/{9-CPpxtRM5.js → 9-BwqDc8wC.js} +2 -2
- package/build/server/chunks/{9-CPpxtRM5.js.map → 9-BwqDc8wC.js.map} +1 -1
- package/build/server/chunks/_page.svelte-8OFzwdNA.js +758 -0
- package/build/server/chunks/_page.svelte-8OFzwdNA.js.map +1 -0
- package/build/server/chunks/{_server.ts-BWVlO8iV.js → _server.ts-05JJOdcX.js} +15 -12
- package/build/server/chunks/_server.ts-05JJOdcX.js.map +1 -0
- package/build/server/chunks/{_server.ts-BevnuePu.js → _server.ts-BCljU9Sg.js} +7 -3
- package/build/server/chunks/_server.ts-BCljU9Sg.js.map +1 -0
- package/build/server/chunks/{_server.ts-D-vgx5UZ.js → _server.ts-BTmknWpO.js} +2 -2
- package/build/server/chunks/{_server.ts-D-vgx5UZ.js.map → _server.ts-BTmknWpO.js.map} +1 -1
- package/build/server/chunks/{_server.ts-tChyh9FX.js → _server.ts-BXhmLZwN.js} +4 -2
- package/build/server/chunks/{_server.ts-tChyh9FX.js.map → _server.ts-BXhmLZwN.js.map} +1 -1
- package/build/server/chunks/{_server.ts-CvJKTS3Z.js → _server.ts-BbRSpB74.js} +4 -2
- package/build/server/chunks/{_server.ts-CvJKTS3Z.js.map → _server.ts-BbRSpB74.js.map} +1 -1
- package/build/server/chunks/{_server.ts-CC2K8-L2.js → _server.ts-Blx6TuRU.js} +4 -2
- package/build/server/chunks/_server.ts-Blx6TuRU.js.map +1 -0
- package/build/server/chunks/_server.ts-C6NRpe7e.js +33 -0
- package/build/server/chunks/_server.ts-C6NRpe7e.js.map +1 -0
- package/build/server/chunks/_server.ts-CGqCOCdK.js +53 -0
- package/build/server/chunks/_server.ts-CGqCOCdK.js.map +1 -0
- package/build/server/chunks/{_server.ts-X1R7L_QI.js → _server.ts-CYWXjihn.js} +4 -2
- package/build/server/chunks/{_server.ts-X1R7L_QI.js.map → _server.ts-CYWXjihn.js.map} +1 -1
- package/build/server/chunks/{_server.ts-CD7JP3fz.js → _server.ts-D0___krA.js} +4 -2
- package/build/server/chunks/_server.ts-D0___krA.js.map +1 -0
- package/build/server/chunks/{_server.ts-VzDcFFgy.js → _server.ts-DPHRUFYS.js} +4 -2
- package/build/server/chunks/_server.ts-DPHRUFYS.js.map +1 -0
- package/build/server/chunks/{_server.ts-D0zRDSx0.js → _server.ts-D_WRex0k.js} +4 -2
- package/build/server/chunks/_server.ts-D_WRex0k.js.map +1 -0
- package/build/server/chunks/{_server.ts-CA5KUENM.js → _server.ts-Da1kSClZ.js} +4 -2
- package/build/server/chunks/_server.ts-Da1kSClZ.js.map +1 -0
- package/build/server/chunks/{_server.ts-Dp-hXW_I.js → _server.ts-l3cd4Cto.js} +4 -2
- package/build/server/chunks/_server.ts-l3cd4Cto.js.map +1 -0
- package/build/server/chunks/{library-apns-Dl3iRE2h.js → library-apns-D8RPINlv.js} +62 -7
- package/build/server/chunks/library-apns-D8RPINlv.js.map +1 -0
- package/build/server/chunks/{pending-requests-C9p57WoU.js → pending-requests-8rWjrF6d.js} +3 -2
- package/build/server/chunks/pending-requests-8rWjrF6d.js.map +1 -0
- package/build/server/chunks/presence-store-Bx_g0-Gd.js +23 -0
- package/build/server/chunks/presence-store-Bx_g0-Gd.js.map +1 -0
- package/build/server/chunks/{pty-manager-ZqXqa-6A.js → pty-manager-DDjG7DlH.js} +297 -31
- package/build/server/chunks/pty-manager-DDjG7DlH.js.map +1 -0
- package/build/server/chunks/shooter-home-4f_HkdGI.js +10 -0
- package/build/server/chunks/shooter-home-4f_HkdGI.js.map +1 -0
- package/build/server/index.js +1 -1
- package/build/server/index.js.map +1 -1
- package/build/server/manifest.js +38 -24
- package/build/server/manifest.js.map +1 -1
- package/package.json +4 -2
- package/server.ts +2 -2
- package/src/lib/modules/client/common/index.ts +1 -0
- package/src/lib/modules/client/common/presence.ts +47 -0
- package/src/lib/modules/client/dashboard/AutopilotPanel.svelte +188 -4
- package/src/lib/modules/client/dashboard/autopilot-driver.svelte.ts +681 -0
- package/src/lib/modules/client/dashboard/decide-injection.ts +127 -0
- package/src/lib/modules/client/dashboard/store.svelte.ts +65 -24
- package/src/lib/modules/client/neurolink/fetch-proxy.ts +38 -1
- package/src/lib/modules/client/terminal/xterm-wrapper.ts +52 -12
- package/src/lib/modules/server/apn/apns-payload.ts +50 -0
- package/src/lib/modules/server/apn/library-apns.ts +50 -8
- package/src/lib/modules/server/apn/pending-requests.ts +3 -1
- package/src/lib/modules/server/sessions/autopilot-context.ts +57 -0
- package/src/lib/modules/server/sessions/autopilot-engine.ts +148 -43
- package/src/lib/modules/server/sessions/litellm-client.ts +90 -34
- package/src/lib/modules/server/sessions/next-step-consensus.ts +27 -2
- package/src/lib/modules/server/sessions/summary-store.ts +3 -1
- package/src/lib/modules/server/terminal/agent-launch.ts +26 -0
- package/src/lib/modules/server/terminal/pty-holder.cjs +6 -0
- package/src/lib/modules/server/terminal/pty-manager.ts +292 -38
- package/src/lib/modules/server/terminal/session-watcher.ts +12 -2
- package/src/lib/modules/server/terminal/terminal-emulator.ts +102 -0
- package/src/lib/modules/server/terminal/terminal-store.ts +3 -1
- package/src/lib/modules/server/utils/shooter-home.ts +16 -0
- package/src/lib/modules/server/ws/presence-store.ts +50 -0
- package/src/lib/modules/server/ws/server.ts +18 -2
- package/src/lib/modules/server/ws/terminal-handler.ts +11 -6
- package/src/lib/types/autopilot.ts +65 -0
- package/src/lib/types/generated/WsProtocol.ts +10 -1
- package/src/lib/types/server.ts +27 -1
- package/src/lib/types/terminal-client.ts +3 -0
- package/src/lib/types/ws.ts +3 -2
- package/src/routes/api/autopilot/goal/+server.ts +72 -0
- package/src/routes/api/neurolink-proxy/+server.ts +8 -2
- package/src/routes/api/notify/+server.ts +22 -15
- package/src/routes/api/presence/+server.ts +39 -0
- package/src/routes/api/ws-status/+server.ts +8 -5
- package/build/client/_app/immutable/assets/2.BHi6pjT2.css +0 -1
- package/build/client/_app/immutable/assets/2.BHi6pjT2.css.br +0 -0
- package/build/client/_app/immutable/assets/2.BHi6pjT2.css.gz +0 -0
- package/build/client/_app/immutable/chunks/BmfLecb1.js.br +0 -0
- package/build/client/_app/immutable/chunks/BmfLecb1.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CRkG7oE4.js +0 -1
- package/build/client/_app/immutable/chunks/CRkG7oE4.js.br +0 -0
- package/build/client/_app/immutable/chunks/CRkG7oE4.js.gz +0 -0
- package/build/client/_app/immutable/chunks/DOEXXmsh.js.br +0 -0
- package/build/client/_app/immutable/chunks/DOEXXmsh.js.gz +0 -0
- package/build/client/_app/immutable/chunks/EqMAkEha.js.br +0 -0
- package/build/client/_app/immutable/chunks/EqMAkEha.js.gz +0 -0
- package/build/client/_app/immutable/chunks/J5-Cr5oR.js +0 -6
- package/build/client/_app/immutable/chunks/J5-Cr5oR.js.br +0 -0
- package/build/client/_app/immutable/chunks/J5-Cr5oR.js.gz +0 -0
- package/build/client/_app/immutable/entry/app.CeSxgGat.js.br +0 -0
- package/build/client/_app/immutable/entry/app.CeSxgGat.js.gz +0 -0
- package/build/client/_app/immutable/entry/start.DrnJFwxA.js +0 -1
- package/build/client/_app/immutable/entry/start.DrnJFwxA.js.br +0 -2
- package/build/client/_app/immutable/entry/start.DrnJFwxA.js.gz +0 -0
- package/build/client/_app/immutable/nodes/0.oaPwxh1O.js.br +0 -0
- package/build/client/_app/immutable/nodes/0.oaPwxh1O.js.gz +0 -0
- package/build/client/_app/immutable/nodes/1.DMPyoM-M.js.br +0 -0
- package/build/client/_app/immutable/nodes/1.DMPyoM-M.js.gz +0 -0
- package/build/client/_app/immutable/nodes/10.Cbm7nQKK.js.br +0 -0
- package/build/client/_app/immutable/nodes/10.Cbm7nQKK.js.gz +0 -0
- package/build/client/_app/immutable/nodes/11.CKmZjP_a.js.br +0 -0
- package/build/client/_app/immutable/nodes/2.zlrdNFtH.js +0 -13
- package/build/client/_app/immutable/nodes/2.zlrdNFtH.js.br +0 -0
- package/build/client/_app/immutable/nodes/2.zlrdNFtH.js.gz +0 -0
- package/build/client/_app/immutable/nodes/3.BgLpGnzb.js.br +0 -0
- package/build/client/_app/immutable/nodes/3.BgLpGnzb.js.gz +0 -0
- package/build/client/_app/immutable/nodes/5.Avc1-gVb.js.br +0 -0
- package/build/client/_app/immutable/nodes/5.Avc1-gVb.js.gz +0 -0
- package/build/client/_app/immutable/nodes/6.Dw2wEssJ.js.br +0 -0
- package/build/client/_app/immutable/nodes/6.Dw2wEssJ.js.gz +0 -0
- package/build/client/_app/immutable/nodes/7.DwKZjoBg.js.br +0 -0
- package/build/client/_app/immutable/nodes/7.DwKZjoBg.js.gz +0 -0
- package/build/client/_app/immutable/nodes/8.ZUAI6g5E.js.br +0 -0
- package/build/client/_app/immutable/nodes/8.ZUAI6g5E.js.gz +0 -0
- package/build/client/_app/immutable/nodes/9.I_KGXPwB.js.br +0 -0
- package/build/client/_app/immutable/nodes/9.I_KGXPwB.js.gz +0 -0
- package/build/server/chunks/_page.svelte-tBuIq8Pg.js +0 -159
- package/build/server/chunks/_page.svelte-tBuIq8Pg.js.map +0 -1
- package/build/server/chunks/_server.ts-BWVlO8iV.js.map +0 -1
- package/build/server/chunks/_server.ts-BevnuePu.js.map +0 -1
- package/build/server/chunks/_server.ts-CA5KUENM.js.map +0 -1
- package/build/server/chunks/_server.ts-CC2K8-L2.js.map +0 -1
- package/build/server/chunks/_server.ts-CD7JP3fz.js.map +0 -1
- package/build/server/chunks/_server.ts-D0zRDSx0.js.map +0 -1
- package/build/server/chunks/_server.ts-Dp-hXW_I.js.map +0 -1
- package/build/server/chunks/_server.ts-VzDcFFgy.js.map +0 -1
- package/build/server/chunks/library-apns-Dl3iRE2h.js.map +0 -1
- package/build/server/chunks/pending-requests-C9p57WoU.js.map +0 -1
- package/build/server/chunks/pty-manager-ZqXqa-6A.js.map +0 -1
|
@@ -10,6 +10,8 @@ import { b as broadcastEvent } from './guest-registry-Dxvd7p-g.js';
|
|
|
10
10
|
import './super-session-handler-DPyxFgmz.js';
|
|
11
11
|
import * as net from 'net';
|
|
12
12
|
import Database from 'better-sqlite3';
|
|
13
|
+
import { createRequire } from 'node:module';
|
|
14
|
+
import { s as shooterDataDir } from './shooter-home-4f_HkdGI.js';
|
|
13
15
|
|
|
14
16
|
function readOnlySourceForCommand(command) {
|
|
15
17
|
switch (command) {
|
|
@@ -69,6 +71,21 @@ function discoverReadOnlyProviderSessionFile(source, cwd, launchTimeMs, nowMs) {
|
|
|
69
71
|
}
|
|
70
72
|
return resolveReadOnlyProviderFile(source, match.id);
|
|
71
73
|
}
|
|
74
|
+
const CLAUDE_COMMANDS = /* @__PURE__ */ new Set(["claude"]);
|
|
75
|
+
function withAgentPermissionMode(command, args, mode) {
|
|
76
|
+
const trimmed = (mode ?? "").trim();
|
|
77
|
+
if (trimmed.length === 0) {
|
|
78
|
+
return args;
|
|
79
|
+
}
|
|
80
|
+
const base = command.split("/").pop() ?? command;
|
|
81
|
+
if (!CLAUDE_COMMANDS.has(base)) {
|
|
82
|
+
return args;
|
|
83
|
+
}
|
|
84
|
+
if (args.includes("--permission-mode")) {
|
|
85
|
+
return args;
|
|
86
|
+
}
|
|
87
|
+
return [...args, "--permission-mode", trimmed];
|
|
88
|
+
}
|
|
72
89
|
class HolderClient {
|
|
73
90
|
connected = false;
|
|
74
91
|
pid = 0;
|
|
@@ -705,7 +722,63 @@ function toMillis(timestamp) {
|
|
|
705
722
|
const OW_GLOBAL_KEY = "__shooter_opencode_watcher";
|
|
706
723
|
const openCodeWatcher = globalThis[OW_GLOBAL_KEY] || new OpenCodeWatcher();
|
|
707
724
|
globalThis[OW_GLOBAL_KEY] = openCodeWatcher;
|
|
708
|
-
const
|
|
725
|
+
const require$1 = createRequire(import.meta.url);
|
|
726
|
+
const { Terminal } = require$1("@xterm/headless");
|
|
727
|
+
const { SerializeAddon } = require$1("@xterm/addon-serialize");
|
|
728
|
+
const SNAPSHOT_SCROLLBACK_LINES = 1e3;
|
|
729
|
+
const HIDE_CURSOR = "\x1B[?25l";
|
|
730
|
+
const SHOW_CURSOR = "\x1B[?25h";
|
|
731
|
+
class TerminalEmulator {
|
|
732
|
+
cursorHidden = false;
|
|
733
|
+
serializer;
|
|
734
|
+
term;
|
|
735
|
+
constructor(cols, rows) {
|
|
736
|
+
this.term = new Terminal({
|
|
737
|
+
allowProposedApi: true,
|
|
738
|
+
cols: cols > 0 ? cols : 80,
|
|
739
|
+
rows: rows > 0 ? rows : 24,
|
|
740
|
+
scrollback: SNAPSHOT_SCROLLBACK_LINES
|
|
741
|
+
});
|
|
742
|
+
this.serializer = new SerializeAddon();
|
|
743
|
+
this.term.loadAddon(this.serializer);
|
|
744
|
+
}
|
|
745
|
+
dispose() {
|
|
746
|
+
try {
|
|
747
|
+
this.serializer.dispose();
|
|
748
|
+
this.term.dispose();
|
|
749
|
+
} catch {
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
resize(cols, rows) {
|
|
753
|
+
if (cols > 0 && rows > 0) {
|
|
754
|
+
this.term.resize(cols, rows);
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
/**
|
|
758
|
+
* Capture the current screen as a VT-escape string. Serialization runs inside
|
|
759
|
+
* a write() callback so all previously-written bytes are parsed first.
|
|
760
|
+
*/
|
|
761
|
+
snapshot() {
|
|
762
|
+
return new Promise((resolve) => {
|
|
763
|
+
this.term.write("", () => {
|
|
764
|
+
let data = this.serializer.serialize({ scrollback: SNAPSHOT_SCROLLBACK_LINES });
|
|
765
|
+
if (this.cursorHidden) {
|
|
766
|
+
data += HIDE_CURSOR;
|
|
767
|
+
}
|
|
768
|
+
resolve({ cols: this.term.cols, data, rows: this.term.rows });
|
|
769
|
+
});
|
|
770
|
+
});
|
|
771
|
+
}
|
|
772
|
+
write(data) {
|
|
773
|
+
const hideIdx = data.lastIndexOf(HIDE_CURSOR);
|
|
774
|
+
const showIdx = data.lastIndexOf(SHOW_CURSOR);
|
|
775
|
+
if (hideIdx !== -1 || showIdx !== -1) {
|
|
776
|
+
this.cursorHidden = hideIdx > showIdx;
|
|
777
|
+
}
|
|
778
|
+
this.term.write(data);
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
const DB_DIR = shooterDataDir();
|
|
709
782
|
const DB_PATH = path.join(DB_DIR, "shooter.db");
|
|
710
783
|
const COLUMNS = [
|
|
711
784
|
"id",
|
|
@@ -849,6 +922,11 @@ globalThis[TS_GLOBAL_KEY] = terminalStore;
|
|
|
849
922
|
const MAX_SCROLLBACK_BYTES = 512 * 1024;
|
|
850
923
|
const MAX_OUTPUT_BUFFER_BYTES = 1024 * 1024;
|
|
851
924
|
const SCROLLBACK_CHUNK_SIZE = 50 * 1024;
|
|
925
|
+
const SEQ_RING_MAX_ENTRIES = 2e3;
|
|
926
|
+
const SNAPSHOT_ENABLED = process.env.SHOOTER_SNAPSHOT_FALLBACK !== "raw";
|
|
927
|
+
const RESNAPSHOT_LOW_WATER_BYTES = MAX_OUTPUT_BUFFER_BYTES / 4;
|
|
928
|
+
const RESNAPSHOT_POLL_MS = 100;
|
|
929
|
+
const RESNAPSHOT_MAX_WAIT_MS = 1e4;
|
|
852
930
|
const CLEANUP_INTERVAL_MS = 5 * 60 * 1e3;
|
|
853
931
|
const EXITED_TTL_MS = 60 * 60 * 1e3;
|
|
854
932
|
const MAX_EXITED_TERMINALS = 10;
|
|
@@ -859,6 +937,10 @@ const __filename$1 = fileURLToPath(import.meta.url);
|
|
|
859
937
|
const __dirname$1 = path__default.dirname(__filename$1);
|
|
860
938
|
class PtyManager {
|
|
861
939
|
cleanupTimer = null;
|
|
940
|
+
// Clients currently converging via a resnapshot (Phase 2). While pending, a
|
|
941
|
+
// client receives no normal output frames — the forthcoming snapshot brings
|
|
942
|
+
// it to the current screen. WeakSet so disconnected sockets drop out on GC.
|
|
943
|
+
resnapshotPending = /* @__PURE__ */ new WeakSet();
|
|
862
944
|
terminals = /* @__PURE__ */ new Map();
|
|
863
945
|
constructor() {
|
|
864
946
|
this.cleanupTimer = setInterval(() => {
|
|
@@ -869,19 +951,42 @@ class PtyManager {
|
|
|
869
951
|
// create — now async: forks a holder process, connects via HolderClient,
|
|
870
952
|
// persists to SQLite
|
|
871
953
|
// -----------------------------------------------------------------------
|
|
872
|
-
attach(id, ws) {
|
|
954
|
+
attach(id, ws, opts) {
|
|
873
955
|
const terminal = this.terminals.get(id);
|
|
874
956
|
if (!terminal) {
|
|
875
957
|
return false;
|
|
876
958
|
}
|
|
877
|
-
terminal.clients.add(ws);
|
|
878
959
|
terminal.outputBuffers.set(ws, { data: [], size: 0 });
|
|
960
|
+
const wantsSnapshot = opts?.snapshot === true;
|
|
961
|
+
const lastSeq = opts?.lastSeq ?? 0;
|
|
962
|
+
if (wantsSnapshot && lastSeq > 0) {
|
|
963
|
+
const gap = this.getSeqRingFrom(id, lastSeq);
|
|
964
|
+
if (gap !== null) {
|
|
965
|
+
for (const entry of gap) {
|
|
966
|
+
this.safeSend(ws, JSON.stringify({ data: entry.data, seq: entry.seq, type: "output" }));
|
|
967
|
+
}
|
|
968
|
+
terminal.clients.add(ws);
|
|
969
|
+
return true;
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
if (wantsSnapshot && terminal.emulator) {
|
|
973
|
+
void this.snapshotAndSend(terminal, ws).then((ok) => {
|
|
974
|
+
if (ws.readyState !== 1) {
|
|
975
|
+
return;
|
|
976
|
+
}
|
|
977
|
+
if (!ok) {
|
|
978
|
+
terminal.clients.add(ws);
|
|
979
|
+
void this.sendScrollback(terminal, ws);
|
|
980
|
+
return;
|
|
981
|
+
}
|
|
982
|
+
terminal.clients.add(ws);
|
|
983
|
+
});
|
|
984
|
+
return true;
|
|
985
|
+
}
|
|
986
|
+
terminal.clients.add(ws);
|
|
879
987
|
void this.sendScrollback(terminal, ws);
|
|
880
988
|
return true;
|
|
881
989
|
}
|
|
882
|
-
// -----------------------------------------------------------------------
|
|
883
|
-
// reconnectAll — recover persisted terminals on server startup
|
|
884
|
-
// -----------------------------------------------------------------------
|
|
885
990
|
cleanup() {
|
|
886
991
|
const now = Date.now();
|
|
887
992
|
const exited = [];
|
|
@@ -912,13 +1017,18 @@ class PtyManager {
|
|
|
912
1017
|
}
|
|
913
1018
|
}
|
|
914
1019
|
// -----------------------------------------------------------------------
|
|
915
|
-
//
|
|
1020
|
+
// reconnectAll — recover persisted terminals on server startup
|
|
916
1021
|
// -----------------------------------------------------------------------
|
|
917
1022
|
async create(command, args, cwd, cols, rows) {
|
|
918
1023
|
const id = randomBytes(4).toString("hex");
|
|
919
1024
|
const socketPath = `/tmp/shooter-term-${id}.sock`;
|
|
920
1025
|
const holderScript = resolveHolderPath();
|
|
921
|
-
const
|
|
1026
|
+
const launchArgs = withAgentPermissionMode(
|
|
1027
|
+
command,
|
|
1028
|
+
args,
|
|
1029
|
+
process.env.SHOOTER_AGENT_PERMISSION_MODE
|
|
1030
|
+
);
|
|
1031
|
+
const holderArgs = [id, socketPath, cwd, String(cols), String(rows), command, ...launchArgs];
|
|
922
1032
|
const holder = fork(holderScript, holderArgs, {
|
|
923
1033
|
detached: true,
|
|
924
1034
|
stdio: ["ignore", "ignore", "ignore", "ipc"]
|
|
@@ -953,13 +1063,14 @@ class PtyManager {
|
|
|
953
1063
|
const connectResult = await client.connect(socketPath);
|
|
954
1064
|
const now = /* @__PURE__ */ new Date();
|
|
955
1065
|
const terminal = {
|
|
956
|
-
args,
|
|
1066
|
+
args: launchArgs,
|
|
957
1067
|
clients: /* @__PURE__ */ new Set(),
|
|
958
1068
|
cols,
|
|
959
1069
|
command,
|
|
960
1070
|
createdAt: now,
|
|
961
1071
|
currentCwd: null,
|
|
962
1072
|
cwd,
|
|
1073
|
+
emulator: SNAPSHOT_ENABLED ? new TerminalEmulator(cols, rows) : null,
|
|
963
1074
|
exitCode: connectResult.exitCode,
|
|
964
1075
|
exitedAt: null,
|
|
965
1076
|
holderPid,
|
|
@@ -973,6 +1084,8 @@ class PtyManager {
|
|
|
973
1084
|
pty: client,
|
|
974
1085
|
rows,
|
|
975
1086
|
scrollback: connectResult.scrollback,
|
|
1087
|
+
seqCounter: 0,
|
|
1088
|
+
seqRing: [],
|
|
976
1089
|
sessionFile: null,
|
|
977
1090
|
socketPath,
|
|
978
1091
|
status: connectResult.exited ? "exited" : "running",
|
|
@@ -980,7 +1093,7 @@ class PtyManager {
|
|
|
980
1093
|
};
|
|
981
1094
|
this.wireHolderCallbacks(client, terminal);
|
|
982
1095
|
terminalStore.insert({
|
|
983
|
-
args: JSON.stringify(
|
|
1096
|
+
args: JSON.stringify(launchArgs),
|
|
984
1097
|
cols,
|
|
985
1098
|
command,
|
|
986
1099
|
createdAt: now.toISOString(),
|
|
@@ -1006,7 +1119,7 @@ class PtyManager {
|
|
|
1006
1119
|
return terminal;
|
|
1007
1120
|
}
|
|
1008
1121
|
// -----------------------------------------------------------------------
|
|
1009
|
-
//
|
|
1122
|
+
// disconnectAll — graceful shutdown: disconnect clients, keep holders alive
|
|
1010
1123
|
// -----------------------------------------------------------------------
|
|
1011
1124
|
destroy() {
|
|
1012
1125
|
if (this.cleanupTimer) {
|
|
@@ -1041,8 +1154,7 @@ class PtyManager {
|
|
|
1041
1154
|
}
|
|
1042
1155
|
}
|
|
1043
1156
|
// -----------------------------------------------------------------------
|
|
1044
|
-
//
|
|
1045
|
-
// createdAt descending
|
|
1157
|
+
// get
|
|
1046
1158
|
// -----------------------------------------------------------------------
|
|
1047
1159
|
detach(id, ws) {
|
|
1048
1160
|
const terminal = this.terminals.get(id);
|
|
@@ -1054,7 +1166,8 @@ class PtyManager {
|
|
|
1054
1166
|
return true;
|
|
1055
1167
|
}
|
|
1056
1168
|
// -----------------------------------------------------------------------
|
|
1057
|
-
//
|
|
1169
|
+
// list — running first, then recently exited, each group sorted by
|
|
1170
|
+
// createdAt descending
|
|
1058
1171
|
// -----------------------------------------------------------------------
|
|
1059
1172
|
disconnectAll() {
|
|
1060
1173
|
if (this.cleanupTimer) {
|
|
@@ -1079,13 +1192,13 @@ class PtyManager {
|
|
|
1079
1192
|
this.terminals.clear();
|
|
1080
1193
|
}
|
|
1081
1194
|
// -----------------------------------------------------------------------
|
|
1082
|
-
//
|
|
1195
|
+
// kill — route through holder: SIGTERM, then SIGKILL after 5 s
|
|
1083
1196
|
// -----------------------------------------------------------------------
|
|
1084
1197
|
get(id) {
|
|
1085
1198
|
return this.terminals.get(id) ?? null;
|
|
1086
1199
|
}
|
|
1087
1200
|
// -----------------------------------------------------------------------
|
|
1088
|
-
//
|
|
1201
|
+
// remove — remove an exited terminal from the map
|
|
1089
1202
|
// -----------------------------------------------------------------------
|
|
1090
1203
|
getScrollback(id) {
|
|
1091
1204
|
const terminal = this.terminals.get(id);
|
|
@@ -1095,8 +1208,40 @@ class PtyManager {
|
|
|
1095
1208
|
return terminal.scrollback;
|
|
1096
1209
|
}
|
|
1097
1210
|
// -----------------------------------------------------------------------
|
|
1098
|
-
//
|
|
1211
|
+
// resize
|
|
1099
1212
|
// -----------------------------------------------------------------------
|
|
1213
|
+
/** Current highest assigned seq for a terminal, or null if unknown. */
|
|
1214
|
+
getSeqCounter(id) {
|
|
1215
|
+
return this.terminals.get(id)?.seqCounter ?? null;
|
|
1216
|
+
}
|
|
1217
|
+
/**
|
|
1218
|
+
* Return the ring entries with seq > afterSeq, in order. Returns an empty
|
|
1219
|
+
* array when the caller is already current, or null when the gap is
|
|
1220
|
+
* unresolvable from the ring (caller must take a full snapshot). Unresolvable
|
|
1221
|
+
* means any of:
|
|
1222
|
+
* - afterSeq > seqCounter: the caller claims a seq we never produced — this
|
|
1223
|
+
* happens when the seq counter reset across a server restart (the client
|
|
1224
|
+
* is from a previous terminal lifetime), so its content is unrelated.
|
|
1225
|
+
* - ring empty but afterSeq > 0: nothing buffered to bridge the gap.
|
|
1226
|
+
* - afterSeq predates the oldest retained entry: the gap aged out.
|
|
1227
|
+
*/
|
|
1228
|
+
getSeqRingFrom(id, afterSeq) {
|
|
1229
|
+
const terminal = this.terminals.get(id);
|
|
1230
|
+
if (!terminal) {
|
|
1231
|
+
return null;
|
|
1232
|
+
}
|
|
1233
|
+
if (afterSeq > terminal.seqCounter) {
|
|
1234
|
+
return null;
|
|
1235
|
+
}
|
|
1236
|
+
const ring = terminal.seqRing;
|
|
1237
|
+
if (ring.length === 0) {
|
|
1238
|
+
return afterSeq <= 0 ? [] : null;
|
|
1239
|
+
}
|
|
1240
|
+
if (afterSeq < ring[0].seq - 1) {
|
|
1241
|
+
return null;
|
|
1242
|
+
}
|
|
1243
|
+
return ring.filter((e) => e.seq > afterSeq);
|
|
1244
|
+
}
|
|
1100
1245
|
kill(id) {
|
|
1101
1246
|
const terminal = this.terminals.get(id);
|
|
1102
1247
|
if (!terminal) {
|
|
@@ -1129,7 +1274,7 @@ class PtyManager {
|
|
|
1129
1274
|
return true;
|
|
1130
1275
|
}
|
|
1131
1276
|
// -----------------------------------------------------------------------
|
|
1132
|
-
//
|
|
1277
|
+
// attach — register a WebSocket client and replay scrollback
|
|
1133
1278
|
// -----------------------------------------------------------------------
|
|
1134
1279
|
list() {
|
|
1135
1280
|
const all = Array.from(this.terminals.values());
|
|
@@ -1142,7 +1287,7 @@ class PtyManager {
|
|
|
1142
1287
|
return [...running, ...exited];
|
|
1143
1288
|
}
|
|
1144
1289
|
// -----------------------------------------------------------------------
|
|
1145
|
-
//
|
|
1290
|
+
// detach — remove a WebSocket client
|
|
1146
1291
|
// -----------------------------------------------------------------------
|
|
1147
1292
|
async reconnectAll() {
|
|
1148
1293
|
const running = terminalStore.listRunning();
|
|
@@ -1162,8 +1307,7 @@ class PtyManager {
|
|
|
1162
1307
|
}
|
|
1163
1308
|
}
|
|
1164
1309
|
// -----------------------------------------------------------------------
|
|
1165
|
-
//
|
|
1166
|
-
// also clean up old SQLite records
|
|
1310
|
+
// getScrollback — return raw scrollback data for replay
|
|
1167
1311
|
// -----------------------------------------------------------------------
|
|
1168
1312
|
remove(id) {
|
|
1169
1313
|
const terminal = this.terminals.get(id);
|
|
@@ -1177,7 +1321,8 @@ class PtyManager {
|
|
|
1177
1321
|
return true;
|
|
1178
1322
|
}
|
|
1179
1323
|
// -----------------------------------------------------------------------
|
|
1180
|
-
//
|
|
1324
|
+
// cleanup — evict exited terminals older than 1 hour, cap at 10 exited;
|
|
1325
|
+
// also clean up old SQLite records
|
|
1181
1326
|
// -----------------------------------------------------------------------
|
|
1182
1327
|
resize(id, cols, rows) {
|
|
1183
1328
|
const terminal = this.terminals.get(id);
|
|
@@ -1188,6 +1333,7 @@ class PtyManager {
|
|
|
1188
1333
|
terminal.pty.resize(cols, rows);
|
|
1189
1334
|
terminal.cols = cols;
|
|
1190
1335
|
terminal.rows = rows;
|
|
1336
|
+
terminal.emulator?.resize(cols, rows);
|
|
1191
1337
|
const msg = JSON.stringify({ cols, rows, type: "resize" });
|
|
1192
1338
|
for (const ws of terminal.clients) {
|
|
1193
1339
|
this.safeSend(ws, msg);
|
|
@@ -1198,6 +1344,39 @@ class PtyManager {
|
|
|
1198
1344
|
}
|
|
1199
1345
|
}
|
|
1200
1346
|
// -----------------------------------------------------------------------
|
|
1347
|
+
// destroy — emergency forced kill (kills holder processes too)
|
|
1348
|
+
// -----------------------------------------------------------------------
|
|
1349
|
+
/**
|
|
1350
|
+
* Compute the current-screen snapshot from the emulator and send it as a
|
|
1351
|
+
* single {type:'snapshot'} frame stamped with the current seq. Returns false
|
|
1352
|
+
* if there is no emulator, the socket closed, or serialization failed.
|
|
1353
|
+
* Reused by Phase 2 to resnapshot a client after a backpressure gap.
|
|
1354
|
+
*/
|
|
1355
|
+
async snapshotAndSend(terminal, ws) {
|
|
1356
|
+
if (!terminal.emulator) {
|
|
1357
|
+
return false;
|
|
1358
|
+
}
|
|
1359
|
+
try {
|
|
1360
|
+
const snap = await terminal.emulator.snapshot();
|
|
1361
|
+
if (ws.readyState !== 1) {
|
|
1362
|
+
return false;
|
|
1363
|
+
}
|
|
1364
|
+
this.safeSend(
|
|
1365
|
+
ws,
|
|
1366
|
+
JSON.stringify({
|
|
1367
|
+
cols: snap.cols,
|
|
1368
|
+
data: snap.data,
|
|
1369
|
+
rows: snap.rows,
|
|
1370
|
+
seq: terminal.seqCounter,
|
|
1371
|
+
type: "snapshot"
|
|
1372
|
+
})
|
|
1373
|
+
);
|
|
1374
|
+
return true;
|
|
1375
|
+
} catch {
|
|
1376
|
+
return false;
|
|
1377
|
+
}
|
|
1378
|
+
}
|
|
1379
|
+
// -----------------------------------------------------------------------
|
|
1201
1380
|
// Private: reconnectOne — reconnect to a single persisted terminal
|
|
1202
1381
|
// -----------------------------------------------------------------------
|
|
1203
1382
|
appendScrollback(terminal, data) {
|
|
@@ -1212,21 +1391,103 @@ class PtyManager {
|
|
|
1212
1391
|
}
|
|
1213
1392
|
}
|
|
1214
1393
|
}
|
|
1394
|
+
/**
|
|
1395
|
+
* Assign the next sequence number to an output chunk and append it to the
|
|
1396
|
+
* bounded replay ring. Returns the new seq. Phase 2 uses the ring to replay
|
|
1397
|
+
* the gap to a reconnecting client without a full snapshot.
|
|
1398
|
+
*/
|
|
1399
|
+
appendSeqRing(terminal, data) {
|
|
1400
|
+
const seq = terminal.seqCounter + 1;
|
|
1401
|
+
terminal.seqRing.push({ data, seq });
|
|
1402
|
+
if (terminal.seqRing.length > SEQ_RING_MAX_ENTRIES) {
|
|
1403
|
+
terminal.seqRing.shift();
|
|
1404
|
+
}
|
|
1405
|
+
terminal.seqCounter = seq;
|
|
1406
|
+
return seq;
|
|
1407
|
+
}
|
|
1408
|
+
/**
|
|
1409
|
+
* Mark a client for convergence (Phase 2). Its queued output is discarded and
|
|
1410
|
+
* further live frames are withheld until its socket drains below the low-water
|
|
1411
|
+
* mark (or a hard timeout elapses), at which point a fresh snapshot resets it
|
|
1412
|
+
* to the current screen. This replaces silent byte-dropping so a slow or
|
|
1413
|
+
* throttled client can never diverge permanently (G1).
|
|
1414
|
+
*/
|
|
1415
|
+
beginResnapshot(terminal, ws) {
|
|
1416
|
+
if (this.resnapshotPending.has(ws)) {
|
|
1417
|
+
return;
|
|
1418
|
+
}
|
|
1419
|
+
this.resnapshotPending.add(ws);
|
|
1420
|
+
const buffer = terminal.outputBuffers.get(ws);
|
|
1421
|
+
if (buffer) {
|
|
1422
|
+
buffer.data.length = 0;
|
|
1423
|
+
buffer.size = 0;
|
|
1424
|
+
}
|
|
1425
|
+
this.safeSend(ws, JSON.stringify({ bytes: 0, type: "output-dropped" }));
|
|
1426
|
+
const startedAt = Date.now();
|
|
1427
|
+
const poll = () => {
|
|
1428
|
+
if (!this.resnapshotPending.has(ws)) {
|
|
1429
|
+
return;
|
|
1430
|
+
}
|
|
1431
|
+
if (ws.readyState !== 1 || !terminal.emulator || !terminal.clients.has(ws)) {
|
|
1432
|
+
this.resnapshotPending.delete(ws);
|
|
1433
|
+
return;
|
|
1434
|
+
}
|
|
1435
|
+
const drained = ws.bufferedAmount <= RESNAPSHOT_LOW_WATER_BYTES;
|
|
1436
|
+
const timedOut = Date.now() - startedAt > RESNAPSHOT_MAX_WAIT_MS;
|
|
1437
|
+
if (drained || timedOut) {
|
|
1438
|
+
void this.snapshotAndSend(terminal, ws).finally(() => {
|
|
1439
|
+
this.resnapshotPending.delete(ws);
|
|
1440
|
+
});
|
|
1441
|
+
return;
|
|
1442
|
+
}
|
|
1443
|
+
setTimeout(poll, RESNAPSHOT_POLL_MS);
|
|
1444
|
+
};
|
|
1445
|
+
setTimeout(poll, RESNAPSHOT_POLL_MS);
|
|
1446
|
+
}
|
|
1215
1447
|
// -----------------------------------------------------------------------
|
|
1216
1448
|
// Private: handleReconnectFailure — handle failed reconnection
|
|
1217
1449
|
// -----------------------------------------------------------------------
|
|
1218
1450
|
broadcastOutput(terminal, data) {
|
|
1219
|
-
const
|
|
1451
|
+
const seq = this.appendSeqRing(terminal, data);
|
|
1452
|
+
const msg = JSON.stringify({ data, seq, type: "output" });
|
|
1453
|
+
if (!terminal.emulator) {
|
|
1454
|
+
this.broadcastOutputLegacy(terminal, msg);
|
|
1455
|
+
return;
|
|
1456
|
+
}
|
|
1457
|
+
const msgSize = Buffer.byteLength(msg, "utf8");
|
|
1458
|
+
for (const ws of terminal.clients) {
|
|
1459
|
+
if (this.resnapshotPending.has(ws)) {
|
|
1460
|
+
continue;
|
|
1461
|
+
}
|
|
1462
|
+
const buffer = terminal.outputBuffers.get(ws);
|
|
1463
|
+
if (!buffer) {
|
|
1464
|
+
continue;
|
|
1465
|
+
}
|
|
1466
|
+
if (ws.bufferedAmount > MAX_OUTPUT_BUFFER_BYTES || buffer.size + msgSize > MAX_OUTPUT_BUFFER_BYTES) {
|
|
1467
|
+
this.beginResnapshot(terminal, ws);
|
|
1468
|
+
continue;
|
|
1469
|
+
}
|
|
1470
|
+
buffer.data.push(msg);
|
|
1471
|
+
buffer.size += msgSize;
|
|
1472
|
+
this.flushOutputBuffer(ws, buffer);
|
|
1473
|
+
}
|
|
1474
|
+
}
|
|
1475
|
+
/**
|
|
1476
|
+
* Legacy broadcast path used only when the emulator is disabled
|
|
1477
|
+
* (SHOOTER_SNAPSHOT_FALLBACK=raw). Drops the oldest buffered output to make
|
|
1478
|
+
* room and notifies the client; there is no snapshot to converge it to.
|
|
1479
|
+
*/
|
|
1480
|
+
broadcastOutputLegacy(terminal, msg) {
|
|
1481
|
+
const msgSize = Buffer.byteLength(msg, "utf8");
|
|
1220
1482
|
for (const ws of terminal.clients) {
|
|
1221
1483
|
if (ws.bufferedAmount > MAX_OUTPUT_BUFFER_BYTES) {
|
|
1222
|
-
this.safeSend(ws, JSON.stringify({ bytes:
|
|
1484
|
+
this.safeSend(ws, JSON.stringify({ bytes: msgSize, type: "output-dropped" }));
|
|
1223
1485
|
continue;
|
|
1224
1486
|
}
|
|
1225
1487
|
const buffer = terminal.outputBuffers.get(ws);
|
|
1226
1488
|
if (!buffer) {
|
|
1227
1489
|
continue;
|
|
1228
1490
|
}
|
|
1229
|
-
const msgSize = Buffer.byteLength(msg, "utf8");
|
|
1230
1491
|
if (buffer.size + msgSize > MAX_OUTPUT_BUFFER_BYTES) {
|
|
1231
1492
|
let droppedBytes = 0;
|
|
1232
1493
|
while (buffer.size + msgSize > MAX_OUTPUT_BUFFER_BYTES && buffer.data.length > 0) {
|
|
@@ -1238,11 +1499,7 @@ class PtyManager {
|
|
|
1238
1499
|
}
|
|
1239
1500
|
}
|
|
1240
1501
|
if (droppedBytes > 0) {
|
|
1241
|
-
|
|
1242
|
-
bytes: droppedBytes,
|
|
1243
|
-
type: "output-dropped"
|
|
1244
|
-
});
|
|
1245
|
-
this.safeSend(ws, dropMsg);
|
|
1502
|
+
this.safeSend(ws, JSON.stringify({ bytes: droppedBytes, type: "output-dropped" }));
|
|
1246
1503
|
}
|
|
1247
1504
|
}
|
|
1248
1505
|
buffer.data.push(msg);
|
|
@@ -1275,6 +1532,8 @@ class PtyManager {
|
|
|
1275
1532
|
openCodeWatcher.stop(terminal.openCodeSessionId, terminal.openCodeNoopCb);
|
|
1276
1533
|
terminal.openCodeNoopCb = null;
|
|
1277
1534
|
}
|
|
1535
|
+
terminal.emulator?.dispose();
|
|
1536
|
+
terminal.emulator = null;
|
|
1278
1537
|
terminal.pty.disconnect();
|
|
1279
1538
|
for (const ws of terminal.clients) {
|
|
1280
1539
|
try {
|
|
@@ -1366,6 +1625,7 @@ class PtyManager {
|
|
|
1366
1625
|
createdAt: new Date(record.createdAt),
|
|
1367
1626
|
currentCwd: null,
|
|
1368
1627
|
cwd: record.cwd,
|
|
1628
|
+
emulator: SNAPSHOT_ENABLED ? new TerminalEmulator(record.cols, record.rows) : null,
|
|
1369
1629
|
exitCode: connectResult.exitCode,
|
|
1370
1630
|
exitedAt: record.exitedAt ? new Date(record.exitedAt) : null,
|
|
1371
1631
|
holderPid: record.holderPid ?? 0,
|
|
@@ -1379,11 +1639,16 @@ class PtyManager {
|
|
|
1379
1639
|
pty: client,
|
|
1380
1640
|
rows: record.rows,
|
|
1381
1641
|
scrollback: connectResult.scrollback,
|
|
1642
|
+
seqCounter: 0,
|
|
1643
|
+
seqRing: [],
|
|
1382
1644
|
sessionFile: record.sessionFile ?? null,
|
|
1383
1645
|
socketPath: record.socketPath,
|
|
1384
1646
|
status: connectResult.exited ? "exited" : "running",
|
|
1385
1647
|
watcherOffset: 0
|
|
1386
1648
|
};
|
|
1649
|
+
if (terminal.emulator && connectResult.scrollback.length > 0) {
|
|
1650
|
+
terminal.emulator.write(connectResult.scrollback);
|
|
1651
|
+
}
|
|
1387
1652
|
if (connectResult.exited) {
|
|
1388
1653
|
terminal.exitedAt = terminal.exitedAt ?? /* @__PURE__ */ new Date();
|
|
1389
1654
|
terminalStore.markExited(record.id, connectResult.exitCode);
|
|
@@ -1646,6 +1911,7 @@ class PtyManager {
|
|
|
1646
1911
|
}
|
|
1647
1912
|
});
|
|
1648
1913
|
client.onOutput((data) => {
|
|
1914
|
+
terminal.emulator?.write(data);
|
|
1649
1915
|
this.appendScrollback(terminal, data);
|
|
1650
1916
|
this.broadcastOutput(terminal, data);
|
|
1651
1917
|
});
|
|
@@ -1690,4 +1956,4 @@ const ptyManager = globalThis[PTY_GLOBAL_KEY] || new PtyManager();
|
|
|
1690
1956
|
globalThis[PTY_GLOBAL_KEY] = ptyManager;
|
|
1691
1957
|
|
|
1692
1958
|
export { ptyManager as p };
|
|
1693
|
-
//# sourceMappingURL=pty-manager-
|
|
1959
|
+
//# sourceMappingURL=pty-manager-DDjG7DlH.js.map
|