@boxes-dev/dvb 1.0.94 → 1.0.96
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/dist/bin/dvb.cjs +929 -886
- package/dist/bin/dvb.cjs.map +1 -1
- package/dist/bin/dvbd.cjs +5 -5
- package/dist/devbox/commands/connect.d.ts +2 -10
- package/dist/devbox/commands/connect.d.ts.map +1 -1
- package/dist/devbox/commands/connect.js +8 -905
- package/dist/devbox/commands/connect.js.map +1 -1
- package/dist/devbox/daemonClient.d.ts.map +1 -1
- package/dist/devbox/daemonClient.js +51 -24
- package/dist/devbox/daemonClient.js.map +1 -1
- package/dist/devbox/terminal/sessionProtocol.d.ts +57 -0
- package/dist/devbox/terminal/sessionProtocol.d.ts.map +1 -0
- package/dist/devbox/terminal/sessionProtocol.js +908 -0
- package/dist/devbox/terminal/sessionProtocol.js.map +1 -0
- package/package.json +1 -1
|
@@ -10,7 +10,7 @@ import { DAEMON_TIMEOUT_MS, ensureDaemonRunning, requestJson, requireDaemonFeatu
|
|
|
10
10
|
import { ensureSpritesToken } from "../auth.js";
|
|
11
11
|
import { logger } from "../logger.js";
|
|
12
12
|
import { createLatencyTracker } from "../latency.js";
|
|
13
|
-
import {
|
|
13
|
+
import { createInitialAttachClearFilter, createModalTtyOutputFilter, createScrollbackEraseFilter, streamExecSession, stripModalDisconnectArtifacts, } from "../terminal/sessionProtocol.js";
|
|
14
14
|
import { createStatusLine } from "../ui/statusLine.js";
|
|
15
15
|
import { recordCompletionSessionsFromMap } from "../completions/index.js";
|
|
16
16
|
import { listDevboxes, selectDevbox } from "./boxSelect.js";
|
|
@@ -558,911 +558,8 @@ const promptRenameSession = async (current) => {
|
|
|
558
558
|
const trimmed = answer.trim();
|
|
559
559
|
return trimmed.length ? trimmed : null;
|
|
560
560
|
};
|
|
561
|
-
const HEARTBEAT_INTERVAL_MS = 4000;
|
|
562
|
-
const INPUT_PROBE_TIMEOUT_MS = 1000;
|
|
563
|
-
const STREAM_STALL_PROBE_MISS_THRESHOLD = 2;
|
|
564
561
|
const CONNECT_OPEN_TIMEOUT_MS = 20_000;
|
|
565
|
-
|
|
566
|
-
export const createScrollbackEraseFilter = () => {
|
|
567
|
-
// Strip only scrollback-erase control sequences:
|
|
568
|
-
// ESC [ 3 J
|
|
569
|
-
// CSI 3 J (C1 single-byte CSI = 0x9b)
|
|
570
|
-
let state = 0;
|
|
571
|
-
const flushPending = (output) => {
|
|
572
|
-
if (state === 1) {
|
|
573
|
-
output.push(0x1b);
|
|
574
|
-
}
|
|
575
|
-
else if (state === 2) {
|
|
576
|
-
output.push(0x1b, 0x5b);
|
|
577
|
-
}
|
|
578
|
-
else if (state === 3) {
|
|
579
|
-
output.push(0x1b, 0x5b, 0x33);
|
|
580
|
-
}
|
|
581
|
-
else if (state === 10) {
|
|
582
|
-
output.push(0x9b);
|
|
583
|
-
}
|
|
584
|
-
else if (state === 11) {
|
|
585
|
-
output.push(0x9b, 0x33);
|
|
586
|
-
}
|
|
587
|
-
state = 0;
|
|
588
|
-
};
|
|
589
|
-
const pushByte = (output, byte) => {
|
|
590
|
-
if (state === 0) {
|
|
591
|
-
if (byte === 0x1b) {
|
|
592
|
-
state = 1;
|
|
593
|
-
return;
|
|
594
|
-
}
|
|
595
|
-
if (byte === 0x9b) {
|
|
596
|
-
state = 10;
|
|
597
|
-
return;
|
|
598
|
-
}
|
|
599
|
-
output.push(byte);
|
|
600
|
-
return;
|
|
601
|
-
}
|
|
602
|
-
if (state === 1) {
|
|
603
|
-
if (byte === 0x5b) {
|
|
604
|
-
state = 2;
|
|
605
|
-
return;
|
|
606
|
-
}
|
|
607
|
-
output.push(0x1b);
|
|
608
|
-
state = 0;
|
|
609
|
-
pushByte(output, byte);
|
|
610
|
-
return;
|
|
611
|
-
}
|
|
612
|
-
if (state === 2) {
|
|
613
|
-
if (byte === 0x33) {
|
|
614
|
-
state = 3;
|
|
615
|
-
return;
|
|
616
|
-
}
|
|
617
|
-
output.push(0x1b, 0x5b);
|
|
618
|
-
state = 0;
|
|
619
|
-
pushByte(output, byte);
|
|
620
|
-
return;
|
|
621
|
-
}
|
|
622
|
-
if (state === 3) {
|
|
623
|
-
if (byte === 0x4a) {
|
|
624
|
-
state = 0;
|
|
625
|
-
return;
|
|
626
|
-
}
|
|
627
|
-
output.push(0x1b, 0x5b, 0x33);
|
|
628
|
-
state = 0;
|
|
629
|
-
pushByte(output, byte);
|
|
630
|
-
return;
|
|
631
|
-
}
|
|
632
|
-
if (state === 10) {
|
|
633
|
-
if (byte === 0x33) {
|
|
634
|
-
state = 11;
|
|
635
|
-
return;
|
|
636
|
-
}
|
|
637
|
-
output.push(0x9b);
|
|
638
|
-
state = 0;
|
|
639
|
-
pushByte(output, byte);
|
|
640
|
-
return;
|
|
641
|
-
}
|
|
642
|
-
if (state === 11) {
|
|
643
|
-
if (byte === 0x4a) {
|
|
644
|
-
state = 0;
|
|
645
|
-
return;
|
|
646
|
-
}
|
|
647
|
-
output.push(0x9b, 0x33);
|
|
648
|
-
state = 0;
|
|
649
|
-
pushByte(output, byte);
|
|
650
|
-
}
|
|
651
|
-
};
|
|
652
|
-
return {
|
|
653
|
-
reset() {
|
|
654
|
-
state = 0;
|
|
655
|
-
},
|
|
656
|
-
transform(chunk) {
|
|
657
|
-
const bytes = Buffer.from(chunk ?? Buffer.alloc(0));
|
|
658
|
-
if (bytes.length === 0) {
|
|
659
|
-
return bytes;
|
|
660
|
-
}
|
|
661
|
-
const output = [];
|
|
662
|
-
for (const byte of bytes) {
|
|
663
|
-
pushByte(output, byte);
|
|
664
|
-
}
|
|
665
|
-
return output.length === 0 ? Buffer.alloc(0) : Buffer.from(output);
|
|
666
|
-
},
|
|
667
|
-
flush() {
|
|
668
|
-
const output = [];
|
|
669
|
-
flushPending(output);
|
|
670
|
-
return output.length === 0 ? Buffer.alloc(0) : Buffer.from(output);
|
|
671
|
-
},
|
|
672
|
-
};
|
|
673
|
-
};
|
|
674
|
-
export const createInitialAttachClearFilter = () => {
|
|
675
|
-
const patterns = [
|
|
676
|
-
Buffer.from("\u001b[H\u001b[J", "latin1"),
|
|
677
|
-
Buffer.from("\u001b[H\u001b[2J", "latin1"),
|
|
678
|
-
Buffer.from("\u001b[2J\u001b[H", "latin1"),
|
|
679
|
-
Buffer.from("\u001b[H", "latin1"),
|
|
680
|
-
Buffer.from("\u001b[2J", "latin1"),
|
|
681
|
-
Buffer.from("\u001b[J", "latin1"),
|
|
682
|
-
Buffer.from("\u001b[?1049h", "latin1"),
|
|
683
|
-
Buffer.from("\u001b[?1048h", "latin1"),
|
|
684
|
-
Buffer.from("\u001b[?1047h", "latin1"),
|
|
685
|
-
Buffer.from("\u001b[?47h", "latin1"),
|
|
686
|
-
Buffer.from("\u001b[?1h", "latin1"),
|
|
687
|
-
Buffer.from("\u001b=", "latin1"),
|
|
688
|
-
Buffer.from("\x9bH", "latin1"),
|
|
689
|
-
Buffer.from("\x9b2J", "latin1"),
|
|
690
|
-
Buffer.from("\x9bJ", "latin1"),
|
|
691
|
-
Buffer.from("\x9b?1049h", "latin1"),
|
|
692
|
-
Buffer.from("\x9b?1048h", "latin1"),
|
|
693
|
-
Buffer.from("\x9b?1047h", "latin1"),
|
|
694
|
-
Buffer.from("\x9b?47h", "latin1"),
|
|
695
|
-
Buffer.from("\x9b?1h", "latin1"),
|
|
696
|
-
];
|
|
697
|
-
const maxPatternLength = patterns.reduce((max, pattern) => Math.max(max, pattern.length), 0);
|
|
698
|
-
const STARTUP_FILTER_CHUNKS = 6;
|
|
699
|
-
let enabled = true;
|
|
700
|
-
let pending = Buffer.alloc(0);
|
|
701
|
-
let chunksRemaining = STARTUP_FILTER_CHUNKS;
|
|
702
|
-
const startsWithPatternPrefix = (bytes) => patterns.some((pattern) => {
|
|
703
|
-
if (bytes.length > pattern.length) {
|
|
704
|
-
return false;
|
|
705
|
-
}
|
|
706
|
-
return pattern.subarray(0, bytes.length).equals(bytes);
|
|
707
|
-
});
|
|
708
|
-
const stripLeadingPatterns = (bytes) => {
|
|
709
|
-
let remaining = bytes;
|
|
710
|
-
let removed = true;
|
|
711
|
-
while (removed && remaining.length > 0) {
|
|
712
|
-
removed = false;
|
|
713
|
-
for (const pattern of patterns) {
|
|
714
|
-
if (remaining.length >= pattern.length) {
|
|
715
|
-
const candidate = remaining.subarray(0, pattern.length);
|
|
716
|
-
if (candidate.equals(pattern)) {
|
|
717
|
-
remaining = remaining.subarray(pattern.length);
|
|
718
|
-
removed = true;
|
|
719
|
-
break;
|
|
720
|
-
}
|
|
721
|
-
}
|
|
722
|
-
}
|
|
723
|
-
}
|
|
724
|
-
return remaining;
|
|
725
|
-
};
|
|
726
|
-
const stripInlineArtifacts = (bytes) => {
|
|
727
|
-
if (bytes.length === 0)
|
|
728
|
-
return bytes;
|
|
729
|
-
let text = bytes.toString("latin1");
|
|
730
|
-
// Keep prompt redraw semantics without full-screen jumps by converting
|
|
731
|
-
// cursor-home moves into CR, then dropping destructive clear sequences.
|
|
732
|
-
text = text.replace(/\x1b\[[0-9;]*H/g, "\r");
|
|
733
|
-
text = text.replace(/\x9b[0-9;]*H/g, "\r");
|
|
734
|
-
text = text.replace(/\x1b\]([^\x07\x1b]*)(\x07|\x1b\\)/g, (match, payload) => payload.startsWith(BROWSER_OPEN_OSC_PREFIX) ? match : "");
|
|
735
|
-
const artifactPatterns = [
|
|
736
|
-
/\x01/g,
|
|
737
|
-
/\x1b\[\?1049h/g,
|
|
738
|
-
/\x1b\[\?1048h/g,
|
|
739
|
-
/\x1b\[\?1047h/g,
|
|
740
|
-
/\x1b\[\?47h/g,
|
|
741
|
-
/\x1b\[\?1h/g,
|
|
742
|
-
/\x1b\[\?2004h/g,
|
|
743
|
-
/\x1b=/g,
|
|
744
|
-
/\x9b\?1049h/g,
|
|
745
|
-
/\x9b\?1048h/g,
|
|
746
|
-
/\x9b\?1047h/g,
|
|
747
|
-
/\x9b\?47h/g,
|
|
748
|
-
/\x9b\?1h/g,
|
|
749
|
-
/\x9b\?2004h/g,
|
|
750
|
-
/\x1b\[[0-9;?]*J/g,
|
|
751
|
-
/\x9b[0-9;?]*J/g,
|
|
752
|
-
];
|
|
753
|
-
for (const pattern of artifactPatterns) {
|
|
754
|
-
text = text.replace(pattern, "");
|
|
755
|
-
}
|
|
756
|
-
text = text.replace(/\r{2,}/g, "\r");
|
|
757
|
-
if (/^\r+$/.test(text)) {
|
|
758
|
-
return Buffer.alloc(0);
|
|
759
|
-
}
|
|
760
|
-
return Buffer.from(text, "latin1");
|
|
761
|
-
};
|
|
762
|
-
return {
|
|
763
|
-
reset() {
|
|
764
|
-
enabled = true;
|
|
765
|
-
pending = Buffer.alloc(0);
|
|
766
|
-
chunksRemaining = STARTUP_FILTER_CHUNKS;
|
|
767
|
-
},
|
|
768
|
-
transform(chunk) {
|
|
769
|
-
const bytes = Buffer.from(chunk ?? Buffer.alloc(0));
|
|
770
|
-
if (bytes.length === 0) {
|
|
771
|
-
return bytes;
|
|
772
|
-
}
|
|
773
|
-
if (!enabled) {
|
|
774
|
-
return bytes;
|
|
775
|
-
}
|
|
776
|
-
let work = pending.length > 0
|
|
777
|
-
? Buffer.concat([pending, bytes])
|
|
778
|
-
: Buffer.from(bytes);
|
|
779
|
-
pending = Buffer.alloc(0);
|
|
780
|
-
work = stripLeadingPatterns(work);
|
|
781
|
-
work = stripInlineArtifacts(work);
|
|
782
|
-
chunksRemaining = Math.max(0, chunksRemaining - 1);
|
|
783
|
-
if (work.length === 0) {
|
|
784
|
-
if (chunksRemaining === 0) {
|
|
785
|
-
enabled = false;
|
|
786
|
-
}
|
|
787
|
-
return Buffer.alloc(0);
|
|
788
|
-
}
|
|
789
|
-
const holdBytes = Math.min(work.length, Math.max(1, maxPatternLength - 1));
|
|
790
|
-
if (holdBytes > 0) {
|
|
791
|
-
const prefix = work.subarray(0, holdBytes);
|
|
792
|
-
if (prefix.length === work.length && startsWithPatternPrefix(prefix)) {
|
|
793
|
-
pending = Buffer.from(prefix);
|
|
794
|
-
return Buffer.alloc(0);
|
|
795
|
-
}
|
|
796
|
-
}
|
|
797
|
-
if (chunksRemaining === 0) {
|
|
798
|
-
enabled = false;
|
|
799
|
-
}
|
|
800
|
-
return work;
|
|
801
|
-
},
|
|
802
|
-
flush() {
|
|
803
|
-
const out = pending;
|
|
804
|
-
pending = Buffer.alloc(0);
|
|
805
|
-
enabled = false;
|
|
806
|
-
chunksRemaining = 0;
|
|
807
|
-
return out;
|
|
808
|
-
},
|
|
809
|
-
};
|
|
810
|
-
};
|
|
811
|
-
export const createModalTtyOutputFilter = () => {
|
|
812
|
-
const initialAttachClearFilter = createInitialAttachClearFilter();
|
|
813
|
-
const scrollbackEraseFilter = createScrollbackEraseFilter();
|
|
814
|
-
return {
|
|
815
|
-
reset() {
|
|
816
|
-
initialAttachClearFilter.reset?.();
|
|
817
|
-
scrollbackEraseFilter.reset?.();
|
|
818
|
-
},
|
|
819
|
-
transform(chunk) {
|
|
820
|
-
const initialFiltered = initialAttachClearFilter.transform(chunk);
|
|
821
|
-
if (initialFiltered.length === 0) {
|
|
822
|
-
return Buffer.alloc(0);
|
|
823
|
-
}
|
|
824
|
-
return scrollbackEraseFilter.transform(initialFiltered);
|
|
825
|
-
},
|
|
826
|
-
flush() {
|
|
827
|
-
const pendingInitial = initialAttachClearFilter.flush();
|
|
828
|
-
const pendingFiltered = pendingInitial.length > 0
|
|
829
|
-
? scrollbackEraseFilter.transform(pendingInitial)
|
|
830
|
-
: Buffer.alloc(0);
|
|
831
|
-
const pendingScrollback = scrollbackEraseFilter.flush();
|
|
832
|
-
if (pendingFiltered.length === 0) {
|
|
833
|
-
return pendingScrollback;
|
|
834
|
-
}
|
|
835
|
-
if (pendingScrollback.length === 0) {
|
|
836
|
-
return pendingFiltered;
|
|
837
|
-
}
|
|
838
|
-
return Buffer.concat([pendingFiltered, pendingScrollback]);
|
|
839
|
-
},
|
|
840
|
-
};
|
|
841
|
-
};
|
|
842
|
-
const MODAL_DISCONNECT_FILTER_CHUNKS = 8;
|
|
843
|
-
export const stripModalDisconnectArtifacts = (chunk) => {
|
|
844
|
-
if (chunk.length === 0)
|
|
845
|
-
return chunk;
|
|
846
|
-
let text = chunk.toString("latin1");
|
|
847
|
-
const artifactPatterns = [
|
|
848
|
-
/\x01/g,
|
|
849
|
-
/\x03\x00/g,
|
|
850
|
-
/\x1b\[\?1000l/g,
|
|
851
|
-
/\x1b\[\?1002l/g,
|
|
852
|
-
/\x1b\[\?1003l/g,
|
|
853
|
-
/\x1b\[\?1004l/g,
|
|
854
|
-
/\x1b\[\?1005l/g,
|
|
855
|
-
/\x1b\[\?1006l/g,
|
|
856
|
-
/\x1b\[\?2004l/g,
|
|
857
|
-
/\x1b\[\?1049l/g,
|
|
858
|
-
/\x1b\[\?1048l/g,
|
|
859
|
-
/\x1b\[\?1047l/g,
|
|
860
|
-
/\x1b\[\?47l/g,
|
|
861
|
-
/\x1b\[\?25h/g,
|
|
862
|
-
/\x1b\[<u/g,
|
|
863
|
-
/\x1b\[>4;0m/g,
|
|
864
|
-
/\x1b\[\?1l/g,
|
|
865
|
-
/\x1b>/g,
|
|
866
|
-
];
|
|
867
|
-
for (const pattern of artifactPatterns) {
|
|
868
|
-
text = text.replace(pattern, "");
|
|
869
|
-
}
|
|
870
|
-
return Buffer.from(text, "latin1");
|
|
871
|
-
};
|
|
872
|
-
const streamExecSession = async (ws, options) => new Promise((resolve, reject) => {
|
|
873
|
-
let exitCode = null;
|
|
874
|
-
let resolved = false;
|
|
875
|
-
let opened = false;
|
|
876
|
-
let closeCode;
|
|
877
|
-
let pendingResize = null;
|
|
878
|
-
let lastResize = null;
|
|
879
|
-
const pendingInput = [];
|
|
880
|
-
let lastSeenAt = Date.now();
|
|
881
|
-
let lastStreamActivityAt = Date.now();
|
|
882
|
-
let heartbeatTimer = null;
|
|
883
|
-
let inputProbeTimer = null;
|
|
884
|
-
let openTimer = null;
|
|
885
|
-
let inputProbeStamp = 0;
|
|
886
|
-
let inputProbeStreamStamp = 0;
|
|
887
|
-
let streamProbeMisses = 0;
|
|
888
|
-
let disconnectNoted = false;
|
|
889
|
-
let errorMessage;
|
|
890
|
-
const stdin = process.stdin;
|
|
891
|
-
const stdout = process.stdout;
|
|
892
|
-
const stderr = process.stderr;
|
|
893
|
-
const latency = options.latency;
|
|
894
|
-
const oscMonitor = createOscMonitor();
|
|
895
|
-
const ttyOutputFilter = options.ttyOutputFilter;
|
|
896
|
-
let disconnectFilterChunksRemaining = 0;
|
|
897
|
-
const armDisconnectFilter = () => {
|
|
898
|
-
if (!options.stripDisconnectArtifacts)
|
|
899
|
-
return;
|
|
900
|
-
disconnectFilterChunksRemaining = Math.max(disconnectFilterChunksRemaining, MODAL_DISCONNECT_FILTER_CHUNKS);
|
|
901
|
-
};
|
|
902
|
-
const applyDisconnectFilter = (chunk) => {
|
|
903
|
-
if (!options.stripDisconnectArtifacts)
|
|
904
|
-
return chunk;
|
|
905
|
-
if (disconnectFilterChunksRemaining <= 0)
|
|
906
|
-
return chunk;
|
|
907
|
-
disconnectFilterChunksRemaining -= 1;
|
|
908
|
-
return stripModalDisconnectArtifacts(chunk);
|
|
909
|
-
};
|
|
910
|
-
const initialInput = typeof options.initialInput === "string"
|
|
911
|
-
? Buffer.from(options.initialInput, "utf8")
|
|
912
|
-
: options.initialInput;
|
|
913
|
-
const supportsResizeControlMessages = options.supportsResizeControlMessages ?? true;
|
|
914
|
-
if (options.stdin && initialInput && initialInput.length > 0) {
|
|
915
|
-
pendingInput.push(initialInput);
|
|
916
|
-
}
|
|
917
|
-
const markAlive = () => {
|
|
918
|
-
lastSeenAt = Date.now();
|
|
919
|
-
};
|
|
920
|
-
const markStreamAlive = () => {
|
|
921
|
-
const now = Date.now();
|
|
922
|
-
lastSeenAt = now;
|
|
923
|
-
lastStreamActivityAt = now;
|
|
924
|
-
streamProbeMisses = 0;
|
|
925
|
-
};
|
|
926
|
-
const noteDisconnect = () => {
|
|
927
|
-
options.onDisconnectNotice?.();
|
|
928
|
-
};
|
|
929
|
-
const recordDisconnect = (kind) => {
|
|
930
|
-
if (disconnectNoted)
|
|
931
|
-
return;
|
|
932
|
-
disconnectNoted = true;
|
|
933
|
-
const details = { kind, opened };
|
|
934
|
-
if (typeof closeCode === "number") {
|
|
935
|
-
details.closeCode = closeCode;
|
|
936
|
-
}
|
|
937
|
-
latency?.noteDisconnect(details);
|
|
938
|
-
};
|
|
939
|
-
const finish = (code) => {
|
|
940
|
-
if (resolved)
|
|
941
|
-
return;
|
|
942
|
-
resolved = true;
|
|
943
|
-
cleanup();
|
|
944
|
-
resolve({ kind: "exit", code });
|
|
945
|
-
};
|
|
946
|
-
const finishDetach = () => {
|
|
947
|
-
if (resolved)
|
|
948
|
-
return;
|
|
949
|
-
resolved = true;
|
|
950
|
-
cleanup();
|
|
951
|
-
try {
|
|
952
|
-
ws.close();
|
|
953
|
-
}
|
|
954
|
-
catch {
|
|
955
|
-
// ignore close failures
|
|
956
|
-
}
|
|
957
|
-
resolve({ kind: "detach" });
|
|
958
|
-
};
|
|
959
|
-
const writeTtyOutput = (chunk) => {
|
|
960
|
-
if (chunk.length === 0)
|
|
961
|
-
return;
|
|
962
|
-
const { output, browserUrls } = oscMonitor.write(chunk);
|
|
963
|
-
if (output.length > 0) {
|
|
964
|
-
stdout.write(output);
|
|
965
|
-
}
|
|
966
|
-
for (const url of browserUrls) {
|
|
967
|
-
if (!openBrowser(url)) {
|
|
968
|
-
stderr.write(`Failed to open browser. Visit: ${url}\r\n`);
|
|
969
|
-
}
|
|
970
|
-
}
|
|
971
|
-
};
|
|
972
|
-
const handleBinary = (buffer) => {
|
|
973
|
-
if (buffer.length === 0)
|
|
974
|
-
return;
|
|
975
|
-
if (options.tty) {
|
|
976
|
-
const rawBuffer = Buffer.from(buffer);
|
|
977
|
-
options.onOutputChunk?.(rawBuffer);
|
|
978
|
-
options.onOutput?.();
|
|
979
|
-
const filteredBuffer = ttyOutputFilter
|
|
980
|
-
? ttyOutputFilter.transform(rawBuffer)
|
|
981
|
-
: rawBuffer;
|
|
982
|
-
writeTtyOutput(applyDisconnectFilter(filteredBuffer));
|
|
983
|
-
return;
|
|
984
|
-
}
|
|
985
|
-
const streamId = buffer[0];
|
|
986
|
-
const payload = buffer.subarray(1);
|
|
987
|
-
if (streamId === 1) {
|
|
988
|
-
if (payload.length > 0) {
|
|
989
|
-
options.onOutputChunk?.(payload);
|
|
990
|
-
}
|
|
991
|
-
if (payload.length > 0)
|
|
992
|
-
options.onOutput?.();
|
|
993
|
-
stdout.write(Buffer.from(payload));
|
|
994
|
-
}
|
|
995
|
-
if (streamId === 2) {
|
|
996
|
-
if (payload.length > 0) {
|
|
997
|
-
options.onOutputChunk?.(payload);
|
|
998
|
-
}
|
|
999
|
-
if (payload.length > 0)
|
|
1000
|
-
options.onOutput?.();
|
|
1001
|
-
stderr.write(Buffer.from(payload));
|
|
1002
|
-
}
|
|
1003
|
-
if (streamId === 3 && payload.length > 0) {
|
|
1004
|
-
armDisconnectFilter();
|
|
1005
|
-
exitCode = payload[0] ?? 0;
|
|
1006
|
-
ws.close();
|
|
1007
|
-
}
|
|
1008
|
-
};
|
|
1009
|
-
const handleText = (data) => {
|
|
1010
|
-
try {
|
|
1011
|
-
const payload = JSON.parse(data);
|
|
1012
|
-
if (payload.type === "session_info") {
|
|
1013
|
-
const rawId = payload.session_id ?? payload.id ?? payload.sessionId;
|
|
1014
|
-
if (rawId !== undefined) {
|
|
1015
|
-
options.onSessionInfo?.(String(rawId));
|
|
1016
|
-
}
|
|
1017
|
-
return;
|
|
1018
|
-
}
|
|
1019
|
-
if (payload.type === "exit" && typeof payload.exit_code === "number") {
|
|
1020
|
-
exitCode = payload.exit_code;
|
|
1021
|
-
ws.close();
|
|
1022
|
-
return;
|
|
1023
|
-
}
|
|
1024
|
-
if (payload.type === "port_opened" || payload.type === "port_closed") {
|
|
1025
|
-
const port = Number(payload.port);
|
|
1026
|
-
if (Number.isFinite(port)) {
|
|
1027
|
-
const event = { type: payload.type, port };
|
|
1028
|
-
if (typeof payload.pid === "number")
|
|
1029
|
-
event.pid = payload.pid;
|
|
1030
|
-
if (typeof payload.address === "string") {
|
|
1031
|
-
event.address = payload.address;
|
|
1032
|
-
}
|
|
1033
|
-
options.onPortEvent?.(event);
|
|
1034
|
-
}
|
|
1035
|
-
return;
|
|
1036
|
-
}
|
|
1037
|
-
if (payload.type === "tty_replay_start") {
|
|
1038
|
-
options.onReplayStart?.();
|
|
1039
|
-
return;
|
|
1040
|
-
}
|
|
1041
|
-
if (payload.type === "tty_replay_done") {
|
|
1042
|
-
if (options.tty && ttyOutputFilter) {
|
|
1043
|
-
writeTtyOutput(applyDisconnectFilter(ttyOutputFilter.flush()));
|
|
1044
|
-
}
|
|
1045
|
-
options.onReplayDone?.();
|
|
1046
|
-
return;
|
|
1047
|
-
}
|
|
1048
|
-
}
|
|
1049
|
-
catch {
|
|
1050
|
-
// fall through
|
|
1051
|
-
}
|
|
1052
|
-
if (data.length > 0) {
|
|
1053
|
-
options.onOutputChunk?.(Buffer.from(data, "utf8"));
|
|
1054
|
-
}
|
|
1055
|
-
options.onOutput?.();
|
|
1056
|
-
if (options.tty) {
|
|
1057
|
-
if (data.includes("logout")) {
|
|
1058
|
-
armDisconnectFilter();
|
|
1059
|
-
}
|
|
1060
|
-
const dataBuffer = Buffer.from(data, "utf8");
|
|
1061
|
-
const filtered = ttyOutputFilter
|
|
1062
|
-
? ttyOutputFilter.transform(dataBuffer)
|
|
1063
|
-
: dataBuffer;
|
|
1064
|
-
writeTtyOutput(applyDisconnectFilter(filtered));
|
|
1065
|
-
}
|
|
1066
|
-
else {
|
|
1067
|
-
stdout.write(data);
|
|
1068
|
-
}
|
|
1069
|
-
};
|
|
1070
|
-
const onMessage = (event) => {
|
|
1071
|
-
markStreamAlive();
|
|
1072
|
-
const data = event.data;
|
|
1073
|
-
if (typeof data === "string") {
|
|
1074
|
-
handleText(data);
|
|
1075
|
-
return;
|
|
1076
|
-
}
|
|
1077
|
-
if (data instanceof ArrayBuffer) {
|
|
1078
|
-
handleBinary(new Uint8Array(data));
|
|
1079
|
-
return;
|
|
1080
|
-
}
|
|
1081
|
-
if (data instanceof Uint8Array) {
|
|
1082
|
-
handleBinary(data);
|
|
1083
|
-
return;
|
|
1084
|
-
}
|
|
1085
|
-
if (typeof Blob !== "undefined" && data instanceof Blob) {
|
|
1086
|
-
data
|
|
1087
|
-
.arrayBuffer()
|
|
1088
|
-
.then((buffer) => handleBinary(new Uint8Array(buffer)))
|
|
1089
|
-
.catch(() => {
|
|
1090
|
-
// ignore blob decode failure
|
|
1091
|
-
});
|
|
1092
|
-
}
|
|
1093
|
-
};
|
|
1094
|
-
const sendResize = () => {
|
|
1095
|
-
if (!options.tty)
|
|
1096
|
-
return;
|
|
1097
|
-
if (!supportsResizeControlMessages)
|
|
1098
|
-
return;
|
|
1099
|
-
const { cols, rows } = resolveTtySize();
|
|
1100
|
-
if (lastResize && lastResize.cols === cols && lastResize.rows === rows) {
|
|
1101
|
-
return;
|
|
1102
|
-
}
|
|
1103
|
-
if (!opened) {
|
|
1104
|
-
pendingResize = { cols, rows };
|
|
1105
|
-
return;
|
|
1106
|
-
}
|
|
1107
|
-
ws.send(JSON.stringify({ type: "resize", cols, rows }));
|
|
1108
|
-
lastResize = { cols, rows };
|
|
1109
|
-
};
|
|
1110
|
-
const sendInput = (chunk) => {
|
|
1111
|
-
if (chunk.length > 0) {
|
|
1112
|
-
options.onInputChunk?.(chunk);
|
|
1113
|
-
}
|
|
1114
|
-
if (!latency) {
|
|
1115
|
-
ws.send(chunk);
|
|
1116
|
-
return;
|
|
1117
|
-
}
|
|
1118
|
-
const bufferedAmount = typeof ws.bufferedAmount === "number" ? ws.bufferedAmount : undefined;
|
|
1119
|
-
const onSent = latency.noteSend(chunk.byteLength, bufferedAmount);
|
|
1120
|
-
try {
|
|
1121
|
-
ws.send(chunk, onSent);
|
|
1122
|
-
}
|
|
1123
|
-
catch (error) {
|
|
1124
|
-
onSent(error instanceof Error ? error : new Error(String(error)));
|
|
1125
|
-
throw error;
|
|
1126
|
-
}
|
|
1127
|
-
};
|
|
1128
|
-
const summarizeInputChunk = (chunk) => ({
|
|
1129
|
-
length: chunk.length,
|
|
1130
|
-
hasCtrlC: chunk.includes(0x03),
|
|
1131
|
-
hasCtrlD: chunk.includes(0x04),
|
|
1132
|
-
hasCr: chunk.includes(0x0d),
|
|
1133
|
-
hasLf: chunk.includes(0x0a),
|
|
1134
|
-
});
|
|
1135
|
-
const onStdin = (chunk) => {
|
|
1136
|
-
if (options.tty && isRawCtrlCChunk(chunk)) {
|
|
1137
|
-
const handled = options.onRawCtrlC?.() ?? false;
|
|
1138
|
-
if (handled) {
|
|
1139
|
-
return;
|
|
1140
|
-
}
|
|
1141
|
-
}
|
|
1142
|
-
if (!opened) {
|
|
1143
|
-
pendingInput.push(chunk);
|
|
1144
|
-
logger.debug("connect_stdin_buffered_preopen", {
|
|
1145
|
-
...summarizeInputChunk(chunk),
|
|
1146
|
-
pendingInputCount: pendingInput.length,
|
|
1147
|
-
});
|
|
1148
|
-
return;
|
|
1149
|
-
}
|
|
1150
|
-
if (options.tty && chunk.length === 1 && chunk[0] === 0x1c) {
|
|
1151
|
-
stderr.write("\r\nDetached.\r\n");
|
|
1152
|
-
logger.info("connect_stdin_detach_key", {
|
|
1153
|
-
opened,
|
|
1154
|
-
readyState: ws.readyState,
|
|
1155
|
-
});
|
|
1156
|
-
finishDetach();
|
|
1157
|
-
return;
|
|
1158
|
-
}
|
|
1159
|
-
if (options.tty && chunk.length === 1 && chunk[0] === 0x04) {
|
|
1160
|
-
armDisconnectFilter();
|
|
1161
|
-
}
|
|
1162
|
-
if (typeof ws.readyState === "number" &&
|
|
1163
|
-
ws.readyState !== WS_OPEN_STATE) {
|
|
1164
|
-
logger.warn("connect_stdin_socket_not_open", {
|
|
1165
|
-
readyState: ws.readyState,
|
|
1166
|
-
opened,
|
|
1167
|
-
...summarizeInputChunk(chunk),
|
|
1168
|
-
});
|
|
1169
|
-
noteDisconnect();
|
|
1170
|
-
forceClose();
|
|
1171
|
-
onError();
|
|
1172
|
-
return;
|
|
1173
|
-
}
|
|
1174
|
-
const ageMs = Date.now() - lastSeenAt;
|
|
1175
|
-
const hasRecoveryKey = chunk.includes(0x03) || // Ctrl+C
|
|
1176
|
-
chunk.includes(0x04) || // Ctrl+D
|
|
1177
|
-
chunk.includes(0x0d) || // Enter (CR)
|
|
1178
|
-
chunk.includes(0x0a); // Enter (LF)
|
|
1179
|
-
if (ageMs > HEARTBEAT_INTERVAL_MS ||
|
|
1180
|
-
(streamProbeMisses > 0 && hasRecoveryKey)) {
|
|
1181
|
-
startInputProbe();
|
|
1182
|
-
}
|
|
1183
|
-
if (hasRecoveryKey) {
|
|
1184
|
-
logger.info("connect_stdin_recovery_key", {
|
|
1185
|
-
...summarizeInputChunk(chunk),
|
|
1186
|
-
ageMs,
|
|
1187
|
-
streamProbeMisses,
|
|
1188
|
-
readyState: ws.readyState,
|
|
1189
|
-
});
|
|
1190
|
-
}
|
|
1191
|
-
try {
|
|
1192
|
-
sendInput(chunk);
|
|
1193
|
-
}
|
|
1194
|
-
catch {
|
|
1195
|
-
logger.warn("connect_stdin_send_failed", {
|
|
1196
|
-
...summarizeInputChunk(chunk),
|
|
1197
|
-
ageMs,
|
|
1198
|
-
readyState: ws.readyState,
|
|
1199
|
-
});
|
|
1200
|
-
noteDisconnect();
|
|
1201
|
-
forceClose();
|
|
1202
|
-
onError();
|
|
1203
|
-
}
|
|
1204
|
-
};
|
|
1205
|
-
const startInputProbe = () => {
|
|
1206
|
-
if (inputProbeTimer || typeof ws.ping !== "function")
|
|
1207
|
-
return;
|
|
1208
|
-
inputProbeStamp = lastSeenAt;
|
|
1209
|
-
inputProbeStreamStamp = lastStreamActivityAt;
|
|
1210
|
-
try {
|
|
1211
|
-
ws.ping?.();
|
|
1212
|
-
latency?.notePingSent("input_probe");
|
|
1213
|
-
}
|
|
1214
|
-
catch {
|
|
1215
|
-
// ignore ping failures
|
|
1216
|
-
}
|
|
1217
|
-
inputProbeTimer = setTimeout(() => {
|
|
1218
|
-
inputProbeTimer = null;
|
|
1219
|
-
if (resolved)
|
|
1220
|
-
return;
|
|
1221
|
-
if (lastSeenAt === inputProbeStamp) {
|
|
1222
|
-
streamProbeMisses = 0;
|
|
1223
|
-
latency?.noteInputProbeTimeout();
|
|
1224
|
-
noteDisconnect();
|
|
1225
|
-
forceClose();
|
|
1226
|
-
onError();
|
|
1227
|
-
return;
|
|
1228
|
-
}
|
|
1229
|
-
if (lastStreamActivityAt === inputProbeStreamStamp) {
|
|
1230
|
-
streamProbeMisses += 1;
|
|
1231
|
-
if (streamProbeMisses >= STREAM_STALL_PROBE_MISS_THRESHOLD) {
|
|
1232
|
-
latency?.noteInputProbeTimeout();
|
|
1233
|
-
noteDisconnect();
|
|
1234
|
-
forceClose();
|
|
1235
|
-
onError();
|
|
1236
|
-
}
|
|
1237
|
-
return;
|
|
1238
|
-
}
|
|
1239
|
-
}, INPUT_PROBE_TIMEOUT_MS);
|
|
1240
|
-
};
|
|
1241
|
-
const stopInputProbe = () => {
|
|
1242
|
-
if (inputProbeTimer) {
|
|
1243
|
-
clearTimeout(inputProbeTimer);
|
|
1244
|
-
inputProbeTimer = null;
|
|
1245
|
-
}
|
|
1246
|
-
};
|
|
1247
|
-
const stopOpenTimer = () => {
|
|
1248
|
-
if (openTimer) {
|
|
1249
|
-
clearTimeout(openTimer);
|
|
1250
|
-
openTimer = null;
|
|
1251
|
-
}
|
|
1252
|
-
};
|
|
1253
|
-
const onPong = () => {
|
|
1254
|
-
markAlive();
|
|
1255
|
-
latency?.notePong();
|
|
1256
|
-
};
|
|
1257
|
-
const forceClose = () => {
|
|
1258
|
-
if (typeof ws.terminate === "function") {
|
|
1259
|
-
ws.terminate();
|
|
1260
|
-
return;
|
|
1261
|
-
}
|
|
1262
|
-
ws.close();
|
|
1263
|
-
};
|
|
1264
|
-
const startHeartbeat = () => {
|
|
1265
|
-
if (typeof ws.ping !== "function")
|
|
1266
|
-
return;
|
|
1267
|
-
markAlive();
|
|
1268
|
-
if (typeof ws.on === "function") {
|
|
1269
|
-
ws.on("pong", onPong);
|
|
1270
|
-
}
|
|
1271
|
-
heartbeatTimer = setInterval(() => {
|
|
1272
|
-
if (resolved)
|
|
1273
|
-
return;
|
|
1274
|
-
try {
|
|
1275
|
-
ws.ping?.();
|
|
1276
|
-
latency?.notePingSent("heartbeat");
|
|
1277
|
-
}
|
|
1278
|
-
catch {
|
|
1279
|
-
// ignore ping failures
|
|
1280
|
-
}
|
|
1281
|
-
}, HEARTBEAT_INTERVAL_MS);
|
|
1282
|
-
};
|
|
1283
|
-
const stopHeartbeat = () => {
|
|
1284
|
-
if (heartbeatTimer) {
|
|
1285
|
-
clearInterval(heartbeatTimer);
|
|
1286
|
-
heartbeatTimer = null;
|
|
1287
|
-
}
|
|
1288
|
-
if (typeof ws.off === "function") {
|
|
1289
|
-
ws.off("pong", onPong);
|
|
1290
|
-
}
|
|
1291
|
-
else if (typeof ws.removeListener === "function") {
|
|
1292
|
-
ws.removeListener("pong", onPong);
|
|
1293
|
-
}
|
|
1294
|
-
};
|
|
1295
|
-
const startInput = () => {
|
|
1296
|
-
if (!options.stdin)
|
|
1297
|
-
return;
|
|
1298
|
-
let rawModeEnabled = false;
|
|
1299
|
-
if (options.tty && stdin.isTTY) {
|
|
1300
|
-
try {
|
|
1301
|
-
stdin.setRawMode(true);
|
|
1302
|
-
rawModeEnabled = true;
|
|
1303
|
-
}
|
|
1304
|
-
catch {
|
|
1305
|
-
logger.warn("connect_stdin_rawmode_enable_failed");
|
|
1306
|
-
// ignore raw mode failure
|
|
1307
|
-
}
|
|
1308
|
-
}
|
|
1309
|
-
logger.info("connect_stdin_start", {
|
|
1310
|
-
tty: options.tty,
|
|
1311
|
-
stdinIsTty: stdin.isTTY,
|
|
1312
|
-
rawModeEnabled,
|
|
1313
|
-
});
|
|
1314
|
-
stdin.resume();
|
|
1315
|
-
stdin.on("data", onStdin);
|
|
1316
|
-
process.on("SIGWINCH", sendResize);
|
|
1317
|
-
};
|
|
1318
|
-
const stopInput = () => {
|
|
1319
|
-
if (!options.stdin)
|
|
1320
|
-
return;
|
|
1321
|
-
stdin.removeListener("data", onStdin);
|
|
1322
|
-
process.removeListener("SIGWINCH", sendResize);
|
|
1323
|
-
stdin.pause();
|
|
1324
|
-
let rawModeRestored = false;
|
|
1325
|
-
if (options.tty && stdin.isTTY) {
|
|
1326
|
-
try {
|
|
1327
|
-
stdin.setRawMode(false);
|
|
1328
|
-
rawModeRestored = true;
|
|
1329
|
-
}
|
|
1330
|
-
catch {
|
|
1331
|
-
logger.warn("connect_stdin_rawmode_restore_failed");
|
|
1332
|
-
// ignore restore errors
|
|
1333
|
-
}
|
|
1334
|
-
}
|
|
1335
|
-
logger.info("connect_stdin_stop", {
|
|
1336
|
-
tty: options.tty,
|
|
1337
|
-
stdinIsTty: stdin.isTTY,
|
|
1338
|
-
rawModeRestored,
|
|
1339
|
-
});
|
|
1340
|
-
};
|
|
1341
|
-
const flushPending = () => {
|
|
1342
|
-
if (!opened)
|
|
1343
|
-
return;
|
|
1344
|
-
if (pendingResize) {
|
|
1345
|
-
ws.send(JSON.stringify({
|
|
1346
|
-
type: "resize",
|
|
1347
|
-
cols: pendingResize.cols,
|
|
1348
|
-
rows: pendingResize.rows,
|
|
1349
|
-
}));
|
|
1350
|
-
lastResize = pendingResize;
|
|
1351
|
-
pendingResize = null;
|
|
1352
|
-
}
|
|
1353
|
-
if (pendingInput.length > 0) {
|
|
1354
|
-
for (const chunk of pendingInput) {
|
|
1355
|
-
sendInput(chunk);
|
|
1356
|
-
}
|
|
1357
|
-
pendingInput.length = 0;
|
|
1358
|
-
}
|
|
1359
|
-
};
|
|
1360
|
-
const cleanup = () => {
|
|
1361
|
-
if (options.tty && ttyOutputFilter) {
|
|
1362
|
-
writeTtyOutput(ttyOutputFilter.flush());
|
|
1363
|
-
}
|
|
1364
|
-
ws.removeEventListener("message", onMessage);
|
|
1365
|
-
ws.removeEventListener("close", onClose);
|
|
1366
|
-
ws.removeEventListener("error", onError);
|
|
1367
|
-
ws.removeEventListener("open", onOpen);
|
|
1368
|
-
stopInputProbe();
|
|
1369
|
-
stopOpenTimer();
|
|
1370
|
-
stopHeartbeat();
|
|
1371
|
-
stopInput();
|
|
1372
|
-
};
|
|
1373
|
-
const onClose = (event) => {
|
|
1374
|
-
closeCode = typeof event?.code === "number" ? event.code : closeCode;
|
|
1375
|
-
logger.info("connect_ws_close", {
|
|
1376
|
-
opened,
|
|
1377
|
-
closeCode,
|
|
1378
|
-
exitCode,
|
|
1379
|
-
});
|
|
1380
|
-
recordDisconnect("close");
|
|
1381
|
-
if (exitCode !== null) {
|
|
1382
|
-
finish(exitCode);
|
|
1383
|
-
return;
|
|
1384
|
-
}
|
|
1385
|
-
if (closeCode === 1000) {
|
|
1386
|
-
finish(0);
|
|
1387
|
-
return;
|
|
1388
|
-
}
|
|
1389
|
-
const result = {
|
|
1390
|
-
kind: "disconnect",
|
|
1391
|
-
opened,
|
|
1392
|
-
};
|
|
1393
|
-
if (closeCode !== undefined)
|
|
1394
|
-
result.closeCode = closeCode;
|
|
1395
|
-
cleanup();
|
|
1396
|
-
resolve(result);
|
|
1397
|
-
};
|
|
1398
|
-
const onError = (event) => {
|
|
1399
|
-
if (resolved)
|
|
1400
|
-
return;
|
|
1401
|
-
const raw = event?.data;
|
|
1402
|
-
if (raw instanceof Error && raw.message.trim()) {
|
|
1403
|
-
errorMessage = raw.message.trim();
|
|
1404
|
-
}
|
|
1405
|
-
else if (typeof raw === "string" && raw.trim()) {
|
|
1406
|
-
errorMessage = raw.trim();
|
|
1407
|
-
}
|
|
1408
|
-
else if (raw &&
|
|
1409
|
-
typeof raw === "object" &&
|
|
1410
|
-
"message" in raw &&
|
|
1411
|
-
typeof raw.message === "string" &&
|
|
1412
|
-
raw.message.trim()) {
|
|
1413
|
-
errorMessage = raw.message.trim();
|
|
1414
|
-
}
|
|
1415
|
-
recordDisconnect("error");
|
|
1416
|
-
const result = {
|
|
1417
|
-
kind: "disconnect",
|
|
1418
|
-
opened,
|
|
1419
|
-
};
|
|
1420
|
-
if (closeCode !== undefined)
|
|
1421
|
-
result.closeCode = closeCode;
|
|
1422
|
-
if (errorMessage)
|
|
1423
|
-
result.errorMessage = errorMessage;
|
|
1424
|
-
logger.warn("connect_ws_error", {
|
|
1425
|
-
opened,
|
|
1426
|
-
closeCode,
|
|
1427
|
-
errorMessage,
|
|
1428
|
-
});
|
|
1429
|
-
cleanup();
|
|
1430
|
-
resolve(result);
|
|
1431
|
-
};
|
|
1432
|
-
const onOpen = () => {
|
|
1433
|
-
stopOpenTimer();
|
|
1434
|
-
opened = true;
|
|
1435
|
-
markAlive();
|
|
1436
|
-
latency?.noteConnectOpen();
|
|
1437
|
-
logger.info("connect_ws_open", {
|
|
1438
|
-
tty: options.tty,
|
|
1439
|
-
stdin: options.stdin,
|
|
1440
|
-
});
|
|
1441
|
-
flushPending();
|
|
1442
|
-
startInput();
|
|
1443
|
-
startHeartbeat();
|
|
1444
|
-
options.onOpen?.();
|
|
1445
|
-
};
|
|
1446
|
-
ws.binaryType = "arraybuffer";
|
|
1447
|
-
ws.addEventListener("message", onMessage);
|
|
1448
|
-
ws.addEventListener("close", onClose);
|
|
1449
|
-
ws.addEventListener("error", onError);
|
|
1450
|
-
ws.addEventListener("open", onOpen);
|
|
1451
|
-
sendResize();
|
|
1452
|
-
const openTimeoutMs = options.openTimeoutMs ?? CONNECT_OPEN_TIMEOUT_MS;
|
|
1453
|
-
if (openTimeoutMs > 0) {
|
|
1454
|
-
openTimer = setTimeout(() => {
|
|
1455
|
-
if (resolved || opened)
|
|
1456
|
-
return;
|
|
1457
|
-
noteDisconnect();
|
|
1458
|
-
forceClose();
|
|
1459
|
-
onError({
|
|
1460
|
-
data: new Error(`Timed out waiting for session connection after ${openTimeoutMs}ms.`),
|
|
1461
|
-
});
|
|
1462
|
-
}, openTimeoutMs);
|
|
1463
|
-
openTimer.unref();
|
|
1464
|
-
}
|
|
1465
|
-
});
|
|
562
|
+
export { createInitialAttachClearFilter, createModalTtyOutputFilter, createScrollbackEraseFilter, stripModalDisconnectArtifacts, };
|
|
1466
563
|
export const runConnect = async (args, overrides) => {
|
|
1467
564
|
const parsed = parseConnectArgs(args);
|
|
1468
565
|
const latency = createLatencyTracker();
|
|
@@ -1978,6 +1075,9 @@ export const runConnect = async (args, overrides) => {
|
|
|
1978
1075
|
tty: isTty,
|
|
1979
1076
|
stdin: false,
|
|
1980
1077
|
supportsResizeControlMessages,
|
|
1078
|
+
resolveTtySize,
|
|
1079
|
+
openBrowser,
|
|
1080
|
+
isRawCtrlCChunk,
|
|
1981
1081
|
...(computeProvider === "modal" && isTty
|
|
1982
1082
|
? (() => {
|
|
1983
1083
|
const ttyOutputFilter = createModalTtyOutputFilter();
|
|
@@ -2169,6 +1269,9 @@ export const runConnect = async (args, overrides) => {
|
|
|
2169
1269
|
tty: isTty,
|
|
2170
1270
|
stdin: localStdin,
|
|
2171
1271
|
supportsResizeControlMessages,
|
|
1272
|
+
resolveTtySize,
|
|
1273
|
+
openBrowser,
|
|
1274
|
+
isRawCtrlCChunk,
|
|
2172
1275
|
...(computeProvider === "modal" && isTty
|
|
2173
1276
|
? (() => {
|
|
2174
1277
|
const ttyOutputFilter = createModalTtyOutputFilter();
|