@cuna_labs/cli 0.1.0 → 0.1.1
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/cuna.js +15 -2
- package/dist/bin/cuna.js.map +1 -1
- package/dist/cli/command-help.js +2 -2
- package/dist/cli/command-help.js.map +1 -1
- package/dist/cli/first-line.d.ts +80 -0
- package/dist/cli/first-line.d.ts.map +1 -0
- package/dist/cli/first-line.js +119 -0
- package/dist/cli/first-line.js.map +1 -0
- package/dist/cli/help.js +1 -1
- package/dist/cli/process-entrypoint.d.ts +3 -0
- package/dist/cli/process-entrypoint.d.ts.map +1 -1
- package/dist/cli/process-entrypoint.js +3 -1
- package/dist/cli/process-entrypoint.js.map +1 -1
- package/dist/cli/progress-line.d.ts +78 -0
- package/dist/cli/progress-line.d.ts.map +1 -0
- package/dist/cli/progress-line.js +94 -0
- package/dist/cli/progress-line.js.map +1 -0
- package/dist/cli/recorded-launch-prompt.d.ts +83 -0
- package/dist/cli/recorded-launch-prompt.d.ts.map +1 -0
- package/dist/cli/recorded-launch-prompt.js +80 -0
- package/dist/cli/recorded-launch-prompt.js.map +1 -0
- package/dist/cli/run.d.ts +19 -0
- package/dist/cli/run.d.ts.map +1 -1
- package/dist/cli/run.js +233 -75
- package/dist/cli/run.js.map +1 -1
- package/dist/commands/commands.js +6 -4
- package/dist/commands/commands.js.map +1 -1
- package/dist/journey/account-identity.d.ts +55 -0
- package/dist/journey/account-identity.d.ts.map +1 -0
- package/dist/journey/account-identity.js +69 -0
- package/dist/journey/account-identity.js.map +1 -0
- package/dist/journey/api-effects.d.ts +9 -0
- package/dist/journey/api-effects.d.ts.map +1 -1
- package/dist/journey/api-effects.js +172 -19
- package/dist/journey/api-effects.js.map +1 -1
- package/dist/journey/index.d.ts +3 -1
- package/dist/journey/index.d.ts.map +1 -1
- package/dist/journey/index.js +2 -0
- package/dist/journey/index.js.map +1 -1
- package/dist/journey/orchestrator.d.ts +24 -0
- package/dist/journey/orchestrator.d.ts.map +1 -1
- package/dist/journey/orchestrator.js +21 -3
- package/dist/journey/orchestrator.js.map +1 -1
- package/dist/journey/provider-launch-intent.d.ts +11 -0
- package/dist/journey/provider-launch-intent.d.ts.map +1 -1
- package/dist/journey/provider-launch-intent.js +50 -15
- package/dist/journey/provider-launch-intent.js.map +1 -1
- package/dist/journey/remote-workspace.d.ts +11 -0
- package/dist/journey/remote-workspace.d.ts.map +1 -1
- package/dist/journey/remote-workspace.js +94 -14
- package/dist/journey/remote-workspace.js.map +1 -1
- package/dist/journey/root-entry.d.ts +15 -0
- package/dist/journey/root-entry.d.ts.map +1 -1
- package/dist/journey/root-entry.js +18 -6
- package/dist/journey/root-entry.js.map +1 -1
- package/dist/journey/selection.d.ts +9 -0
- package/dist/journey/selection.d.ts.map +1 -1
- package/dist/journey/selection.js +14 -2
- package/dist/journey/selection.js.map +1 -1
- package/dist/journey/session-disposition.d.ts +45 -0
- package/dist/journey/session-disposition.d.ts.map +1 -0
- package/dist/journey/session-disposition.js +7 -0
- package/dist/journey/session-disposition.js.map +1 -0
- package/dist/journey/wait-policy.d.ts +218 -0
- package/dist/journey/wait-policy.d.ts.map +1 -0
- package/dist/journey/wait-policy.js +233 -0
- package/dist/journey/wait-policy.js.map +1 -0
- package/dist/journey/workspace-effects.d.ts +7 -1
- package/dist/journey/workspace-effects.d.ts.map +1 -1
- package/dist/journey/workspace-effects.js +131 -14
- package/dist/journey/workspace-effects.js.map +1 -1
- package/dist/machines/explorer.js +2 -2
- package/dist/machines/explorer.js.map +1 -1
- package/dist/machines/machine-first.d.ts.map +1 -1
- package/dist/machines/machine-first.js +2 -0
- package/dist/machines/machine-first.js.map +1 -1
- package/dist/machines/session-actionability.js +10 -0
- package/dist/machines/session-actionability.js.map +1 -1
- package/dist/machines/session-visibility.d.ts +2 -0
- package/dist/machines/session-visibility.d.ts.map +1 -1
- package/dist/machines/session-visibility.js +5 -0
- package/dist/machines/session-visibility.js.map +1 -1
- package/dist/platform/process-lock.d.ts +21 -0
- package/dist/platform/process-lock.d.ts.map +1 -0
- package/dist/platform/process-lock.js +114 -0
- package/dist/platform/process-lock.js.map +1 -0
- package/dist/runtime/boundary.d.ts +2 -0
- package/dist/runtime/boundary.d.ts.map +1 -1
- package/dist/runtime/boundary.js +155 -8
- package/dist/runtime/boundary.js.map +1 -1
- package/dist/runtime/node-foreground-session.d.ts +10 -0
- package/dist/runtime/node-foreground-session.d.ts.map +1 -1
- package/dist/runtime/node-foreground-session.js +112 -4
- package/dist/runtime/node-foreground-session.js.map +1 -1
- package/dist/runtime/terminal-client-identity.d.ts +66 -0
- package/dist/runtime/terminal-client-identity.d.ts.map +1 -0
- package/dist/runtime/terminal-client-identity.js +184 -0
- package/dist/runtime/terminal-client-identity.js.map +1 -0
- package/dist/runtime/terminal-transport.js +2 -2
- package/dist/runtime/terminal-transport.js.map +1 -1
- package/dist/sync/continuous-sync-supervisor.d.ts +64 -1
- package/dist/sync/continuous-sync-supervisor.d.ts.map +1 -1
- package/dist/sync/continuous-sync-supervisor.js +247 -28
- package/dist/sync/continuous-sync-supervisor.js.map +1 -1
- package/dist/sync/journal.js +9 -39
- package/dist/sync/journal.js.map +1 -1
- package/dist/sync/workspace-sync-product-service.d.ts +83 -1
- package/dist/sync/workspace-sync-product-service.d.ts.map +1 -1
- package/dist/sync/workspace-sync-product-service.js +324 -12
- package/dist/sync/workspace-sync-product-service.js.map +1 -1
- package/dist/terminal/foreground.d.ts.map +1 -1
- package/dist/terminal/foreground.js +82 -15
- package/dist/terminal/foreground.js.map +1 -1
- package/dist/terminal/workbench.d.ts +7 -0
- package/dist/terminal/workbench.d.ts.map +1 -1
- package/dist/terminal/workbench.js +24 -6
- package/dist/terminal/workbench.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
|
@@ -4,7 +4,7 @@ import { LOCAL_ACTION_PROTOCOL_VERSION, LocalActionBroker, digestLocalActionArgu
|
|
|
4
4
|
import { RuntimeBoundaryError, runtimeFailure, terminalHistoryGap } from "../runtime/errors.js";
|
|
5
5
|
import { assertCanonicalUuid } from "../core/validation.js";
|
|
6
6
|
import { buildAppbarModel } from "./appbar.js";
|
|
7
|
-
import { renderWorkbenchFrame } from "./workbench.js";
|
|
7
|
+
import { renderWorkbenchFrame, workbenchUpdate } from "./workbench.js";
|
|
8
8
|
import { ViewportRegistry } from "./viewport.js";
|
|
9
9
|
import { XtermViewportAdapter } from "./xterm-vte.js";
|
|
10
10
|
const ESCAPE_PREFIX = 0x1d;
|
|
@@ -39,6 +39,10 @@ const ATTACHING_FRAME_MS = 90;
|
|
|
39
39
|
// never more than one second under an injected/test cadence.
|
|
40
40
|
const MAX_DISCONNECT_FRAME_MS = 250;
|
|
41
41
|
const INPUT_WITHHELD_NOTICE = "Reconnecting · input was not sent. Retry after terminal attached.";
|
|
42
|
+
// Sent keys went unacknowledged past the runtime's input deadline (R12). Said
|
|
43
|
+
// the moment the runtime gives up on the connection, and it already carries the
|
|
44
|
+
// "not resent" half, so it is never prefixed with HISTORICAL_INPUT_NOTICE.
|
|
45
|
+
const INPUT_STALLED_NOTICE = "Connection stalled · reconnecting — input not resent";
|
|
42
46
|
const FLOW_CONTROL_NOTICE = "Terminal output kept active · Ctrl+] s sends Ctrl+S remotely.";
|
|
43
47
|
const RECONNECT_FAILED_NOTICE = "Reconnect failed · Ctrl+] r retries · Ctrl+C disconnects.";
|
|
44
48
|
// Automatic recovery back-off: 100 ms doubling, capped at 5 s per wait, ten
|
|
@@ -69,6 +73,7 @@ export class ForegroundTerminalCoordinator {
|
|
|
69
73
|
#renderTail = Promise.resolve();
|
|
70
74
|
/** The last complete frame the host accepted; undefined whenever the host screen is not known to show it. */
|
|
71
75
|
#lastHostFrame;
|
|
76
|
+
#lastWorkbenchFrame;
|
|
72
77
|
#inputTail = Promise.resolve();
|
|
73
78
|
#copyDetectors = new Map();
|
|
74
79
|
#copyLinks = new Map();
|
|
@@ -574,17 +579,33 @@ export class ForegroundTerminalCoordinator {
|
|
|
574
579
|
const current = this.#tabs.get(event.tabId);
|
|
575
580
|
if (event.signal.aborted || current !== tab || !sameSnapshotBinding(tab.snapshot, event.binding))
|
|
576
581
|
return;
|
|
577
|
-
|
|
582
|
+
// Parsing preserves every ordered byte; painting may skip intermediate
|
|
583
|
+
// screens. A slow host must not hold up ingestion of newer remote output.
|
|
584
|
+
this.#queueStateRender();
|
|
578
585
|
}
|
|
579
586
|
#terminalState(snapshot) {
|
|
580
587
|
const tab = this.#tabs.get(snapshot.tabId);
|
|
581
588
|
if (tab !== undefined) {
|
|
582
589
|
this.#forgetSeatNoticeOnSeatChange(tab.snapshot, snapshot);
|
|
590
|
+
// The PTY keeps whatever geometry the previous writer set, and an
|
|
591
|
+
// observer never resizes it. Taking the seat is therefore the first
|
|
592
|
+
// moment this client may state its own size -- without this, the remote
|
|
593
|
+
// keeps painting at the old writer's height and the bottom of a larger
|
|
594
|
+
// local terminal stays empty until some later host resize happens to
|
|
595
|
+
// occur. Reconcile once, on the observer -> writer edge only.
|
|
596
|
+
const becameWriter = tab.snapshot.accessMode === "observer" &&
|
|
597
|
+
snapshot.accessMode === "writer" && snapshot.state === "active";
|
|
583
598
|
tab.snapshot = snapshot;
|
|
599
|
+
if (becameWriter)
|
|
600
|
+
this.#reconcileSeatGeometry(snapshot);
|
|
584
601
|
if (snapshot.state === "active" &&
|
|
585
|
-
(this.#browserNotice === INPUT_WITHHELD_NOTICE || this.#browserNotice ===
|
|
602
|
+
(this.#browserNotice === INPUT_WITHHELD_NOTICE || this.#browserNotice === INPUT_STALLED_NOTICE ||
|
|
603
|
+
isReconnectFailedNotice(this.#browserNotice))) {
|
|
586
604
|
this.#browserNotice = undefined;
|
|
587
605
|
}
|
|
606
|
+
if (snapshot.state === "interrupted" && snapshot.reason === "input_ack_timeout") {
|
|
607
|
+
this.#browserNotice = INPUT_STALLED_NOTICE;
|
|
608
|
+
}
|
|
588
609
|
if (this.#localDetachTabIds.has(snapshot.tabId) &&
|
|
589
610
|
(snapshot.state === "failed" || snapshot.state === "interrupted" || snapshot.state === "closed" || snapshot.state === "detached")) {
|
|
590
611
|
// A local detach may close the wire before the runtime publishes its
|
|
@@ -626,7 +647,8 @@ export class ForegroundTerminalCoordinator {
|
|
|
626
647
|
void this.stop().catch(() => { this.#state = "failed"; });
|
|
627
648
|
return;
|
|
628
649
|
}
|
|
629
|
-
if (snapshot.state === "interrupted" && !this.#reconnectTasks.has(snapshot.tabId)
|
|
650
|
+
if (snapshot.state === "interrupted" && !this.#reconnectTasks.has(snapshot.tabId) &&
|
|
651
|
+
!this.#recoverableReconnectFailures.has(snapshot.tabId)) {
|
|
630
652
|
this.#startRecovery(snapshot.tabId);
|
|
631
653
|
}
|
|
632
654
|
this.#queueStateRender();
|
|
@@ -645,7 +667,8 @@ export class ForegroundTerminalCoordinator {
|
|
|
645
667
|
const snapshot = await this.#requireRuntime().reconnect({ tabId, signal: this.#lifetimeAbort.signal });
|
|
646
668
|
await this.#reconcileGeometry(snapshot, false);
|
|
647
669
|
this.#recoverableReconnectFailures.delete(tabId);
|
|
648
|
-
if (this.#browserNotice === INPUT_WITHHELD_NOTICE || this.#browserNotice ===
|
|
670
|
+
if (this.#browserNotice === INPUT_WITHHELD_NOTICE || this.#browserNotice === INPUT_STALLED_NOTICE ||
|
|
671
|
+
isReconnectFailedNotice(this.#browserNotice)) {
|
|
649
672
|
this.#browserNotice = undefined;
|
|
650
673
|
}
|
|
651
674
|
return;
|
|
@@ -660,7 +683,7 @@ export class ForegroundTerminalCoordinator {
|
|
|
660
683
|
}
|
|
661
684
|
if (this.#state === "active") {
|
|
662
685
|
this.#recoverableReconnectFailures.set(tabId, lastFailure ?? runtimeFailure("terminal_disconnected", "Automatic terminal reconnection was exhausted.", { retryable: true }));
|
|
663
|
-
this.#browserNotice =
|
|
686
|
+
this.#browserNotice = reconnectFailedNotice(this.#recoverableReconnectFailures.get(tabId));
|
|
664
687
|
await this.#render();
|
|
665
688
|
}
|
|
666
689
|
}
|
|
@@ -699,7 +722,7 @@ export class ForegroundTerminalCoordinator {
|
|
|
699
722
|
this.#pendingBrowserAction = undefined;
|
|
700
723
|
this.#pendingBrowserActionTabId = undefined;
|
|
701
724
|
}
|
|
702
|
-
this.#browserNotice =
|
|
725
|
+
this.#browserNotice = this.#unavailableInputNotice();
|
|
703
726
|
void this.#render().catch(() => undefined);
|
|
704
727
|
return;
|
|
705
728
|
}
|
|
@@ -745,7 +768,7 @@ export class ForegroundTerminalCoordinator {
|
|
|
745
768
|
this.#pendingBrowserAction = undefined;
|
|
746
769
|
this.#pendingBrowserActionTabId = undefined;
|
|
747
770
|
}
|
|
748
|
-
this.#browserNotice =
|
|
771
|
+
this.#browserNotice = this.#unavailableInputNotice();
|
|
749
772
|
void this.#render().catch(() => undefined);
|
|
750
773
|
return;
|
|
751
774
|
}
|
|
@@ -1014,15 +1037,15 @@ export class ForegroundTerminalCoordinator {
|
|
|
1014
1037
|
if (target === undefined || target.tabId !== this.#activeTabId || tab === undefined || link === undefined ||
|
|
1015
1038
|
link.agentSessionId !== tab.snapshot.agentSessionId || link.processEpoch !== tab.snapshot.processEpoch ||
|
|
1016
1039
|
link.fencingGeneration !== tab.snapshot.fencingGeneration) {
|
|
1017
|
-
this.#browserNotice = "No
|
|
1040
|
+
this.#browserNotice = "No sign-in link available. Request a new link in the agent.";
|
|
1018
1041
|
}
|
|
1019
1042
|
else {
|
|
1020
1043
|
try {
|
|
1021
1044
|
await (this.#options.copyText ?? copyLocalText)(link.url);
|
|
1022
|
-
this.#browserNotice = "
|
|
1045
|
+
this.#browserNotice = "Full link copied. Paste it into your browser.";
|
|
1023
1046
|
}
|
|
1024
1047
|
catch {
|
|
1025
|
-
this.#browserNotice = "
|
|
1048
|
+
this.#browserNotice = "Could not copy the link. Try again.";
|
|
1026
1049
|
}
|
|
1027
1050
|
}
|
|
1028
1051
|
await this.#render();
|
|
@@ -1504,6 +1527,10 @@ export class ForegroundTerminalCoordinator {
|
|
|
1504
1527
|
}
|
|
1505
1528
|
catch { /* restore still owns terminal cleanup */ }
|
|
1506
1529
|
}
|
|
1530
|
+
#unavailableInputNotice() {
|
|
1531
|
+
const failure = this.#activeTabId === undefined ? undefined : this.#recoverableReconnectFailures.get(this.#activeTabId);
|
|
1532
|
+
return failure === undefined ? INPUT_WITHHELD_NOTICE : reconnectFailedNotice(failure);
|
|
1533
|
+
}
|
|
1507
1534
|
#captureInputTarget() {
|
|
1508
1535
|
const tabId = this.#activeTabId;
|
|
1509
1536
|
if (tabId === undefined)
|
|
@@ -1558,6 +1585,21 @@ export class ForegroundTerminalCoordinator {
|
|
|
1558
1585
|
}
|
|
1559
1586
|
await this.#render();
|
|
1560
1587
|
}
|
|
1588
|
+
/**
|
|
1589
|
+
* Push this host's geometry to the PTY after the writer seat moves here.
|
|
1590
|
+
* `#terminalState` is synchronous, so the reconciliation runs as its own
|
|
1591
|
+
* guarded task; a refusal is reported like any other terminal failure and
|
|
1592
|
+
* never silently leaves the viewport claiming a size the PTY does not have.
|
|
1593
|
+
*/
|
|
1594
|
+
#reconcileSeatGeometry(snapshot) {
|
|
1595
|
+
void this.#reconcileGeometry(snapshot, false).then(() => this.#queueStateRender(), (error) => {
|
|
1596
|
+
if (this.#state !== "active")
|
|
1597
|
+
return;
|
|
1598
|
+
if (error instanceof RuntimeBoundaryError && (error.code === "terminal_observer" || error.code === "terminal_disconnected"))
|
|
1599
|
+
return;
|
|
1600
|
+
this.#recordFailure(error);
|
|
1601
|
+
});
|
|
1602
|
+
}
|
|
1561
1603
|
async #reconcileGeometry(snapshot, repaintAfterReplay) {
|
|
1562
1604
|
const tab = this.#tabs.get(snapshot.tabId);
|
|
1563
1605
|
if (tab === undefined ||
|
|
@@ -1640,7 +1682,9 @@ export class ForegroundTerminalCoordinator {
|
|
|
1640
1682
|
id: tab.intent.tabId,
|
|
1641
1683
|
label: tab.intent.label,
|
|
1642
1684
|
agent: tab.intent.agent,
|
|
1643
|
-
|
|
1685
|
+
// Hidden tabs contribute labels only; projecting their cells would
|
|
1686
|
+
// recapture a full terminal on every visible output frame.
|
|
1687
|
+
viewport: tab.intent.tabId === activeTabId && tab.snapshot.accessMode === "observer"
|
|
1644
1688
|
? tab.viewport.snapshotForHost(dimensions.columns, remoteRows(dimensions.rows))
|
|
1645
1689
|
: tab.viewport.snapshot(),
|
|
1646
1690
|
}));
|
|
@@ -1649,7 +1693,7 @@ export class ForegroundTerminalCoordinator {
|
|
|
1649
1693
|
rows: dimensions.rows,
|
|
1650
1694
|
activeTabId,
|
|
1651
1695
|
tabs,
|
|
1652
|
-
...(this.#copyLinks.has(activeTabId) ? { action: "
|
|
1696
|
+
...(this.#copyLinks.has(activeTabId) ? { action: "Copy link · Ctrl+] y" } : {}),
|
|
1653
1697
|
appbar: this.#options.appbar?.() ?? runtimeAppbar(this.#clock(), this.#tabs.get(activeTabId)?.snapshot, this.#tabs.get(activeTabId)?.intent),
|
|
1654
1698
|
color: this.#options.color ?? true,
|
|
1655
1699
|
...(this.#disconnectNotice !== undefined
|
|
@@ -1658,7 +1702,7 @@ export class ForegroundTerminalCoordinator {
|
|
|
1658
1702
|
? { notice: "Restoring terminal\u2026" }
|
|
1659
1703
|
: this.#browserNotice !== undefined
|
|
1660
1704
|
? { notice: this.#tabs.get(activeTabId)?.snapshot.historicalInputUncertainty === true &&
|
|
1661
|
-
(this.#browserNotice === INPUT_WITHHELD_NOTICE || this.#browserNotice
|
|
1705
|
+
(this.#browserNotice === INPUT_WITHHELD_NOTICE || isReconnectFailedNotice(this.#browserNotice))
|
|
1662
1706
|
? `${HISTORICAL_INPUT_NOTICE} · ${this.#browserNotice}` : this.#browserNotice }
|
|
1663
1707
|
: this.#pendingBrowserAction !== undefined && this.#pendingBrowserActionTabId === activeTabId
|
|
1664
1708
|
? {
|
|
@@ -1687,9 +1731,11 @@ export class ForegroundTerminalCoordinator {
|
|
|
1687
1731
|
// anything else that touches the host clears this memory first.
|
|
1688
1732
|
if (this.#lastHostFrame !== undefined && sameBytes(this.#lastHostFrame, frame.bytes))
|
|
1689
1733
|
return;
|
|
1734
|
+
const update = workbenchUpdate(this.#lastHostFrame === undefined ? undefined : this.#lastWorkbenchFrame, frame);
|
|
1690
1735
|
this.#lastHostFrame = undefined;
|
|
1691
|
-
await this.#options.host.write(
|
|
1736
|
+
await this.#options.host.write(update);
|
|
1692
1737
|
this.#lastHostFrame = frame.bytes;
|
|
1738
|
+
this.#lastWorkbenchFrame = frame;
|
|
1693
1739
|
});
|
|
1694
1740
|
this.#renderTail = operation.then(() => undefined, () => undefined);
|
|
1695
1741
|
await operation;
|
|
@@ -1962,6 +2008,27 @@ export function admitForegroundDimensions(input) {
|
|
|
1962
2008
|
}
|
|
1963
2009
|
return Object.freeze({ columns: input.columns, rows: input.rows });
|
|
1964
2010
|
}
|
|
2011
|
+
/**
|
|
2012
|
+
* Automatic recovery gave up. Name the typed reason it gave up on: the person
|
|
2013
|
+
* is looking at a frozen frame and "Reconnect failed" alone tells them nothing
|
|
2014
|
+
* about whether retrying can help. Only this client's own closed error codes
|
|
2015
|
+
* are rendered -- never remote text.
|
|
2016
|
+
*/
|
|
2017
|
+
function reconnectFailedNotice(failure) {
|
|
2018
|
+
if (!(failure instanceof RuntimeBoundaryError))
|
|
2019
|
+
return RECONNECT_FAILED_NOTICE;
|
|
2020
|
+
// The capability name is this client's own closed enum, not remote text, and
|
|
2021
|
+
// it is the one word that says WHICH contract the replacement grant failed to
|
|
2022
|
+
// prove. Without it "capability unknown" cannot be acted on by anyone.
|
|
2023
|
+
const capability = failure.safeDetails?.capability;
|
|
2024
|
+
const subject = typeof capability === "string" && /^[a-z_]{1,32}$/u.test(capability)
|
|
2025
|
+
? `${failure.code.replaceAll("_", " ")} (${capability.replaceAll("_", " ")})`
|
|
2026
|
+
: failure.code.replaceAll("_", " ");
|
|
2027
|
+
return `Reconnect failed: ${subject} · Ctrl+] r retries · Ctrl+C disconnects.`;
|
|
2028
|
+
}
|
|
2029
|
+
function isReconnectFailedNotice(value) {
|
|
2030
|
+
return value !== undefined && value.startsWith("Reconnect failed");
|
|
2031
|
+
}
|
|
1965
2032
|
function sameBytes(left, right) {
|
|
1966
2033
|
if (left.byteLength !== right.byteLength)
|
|
1967
2034
|
return false;
|