@boxes-dev/dvb 1.0.60 → 1.0.61

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/dvbd.cjs CHANGED
@@ -88607,8 +88607,8 @@ var init_otel = __esm({
88607
88607
  return trimmed && trimmed.length > 0 ? trimmed : void 0;
88608
88608
  };
88609
88609
  readBuildMetadata = () => {
88610
- const rawPackageVersion = "1.0.60";
88611
- const rawGitSha = "5d6099c6753665655e7ad622e1aa388d91ea0bf7";
88610
+ const rawPackageVersion = "1.0.61";
88611
+ const rawGitSha = "ef3417e1e95b9ccc6114ef7c44fa645a1704b688";
88612
88612
  const packageVersion = typeof rawPackageVersion === "string" ? rawPackageVersion : void 0;
88613
88613
  const gitSha = typeof rawGitSha === "string" ? rawGitSha : void 0;
88614
88614
  return { packageVersion, gitSha };
@@ -120525,9 +120525,9 @@ var init_sentry = __esm({
120525
120525
  sentryEnabled = false;
120526
120526
  uncaughtExceptionMonitorInstalled = false;
120527
120527
  readBuildMetadata2 = () => {
120528
- const rawPackageVersion = "1.0.60";
120529
- const rawGitSha = "5d6099c6753665655e7ad622e1aa388d91ea0bf7";
120530
- const rawSentryRelease = "boxes-dev-dvb@1.0.60+5d6099c6753665655e7ad622e1aa388d91ea0bf7";
120528
+ const rawPackageVersion = "1.0.61";
120529
+ const rawGitSha = "ef3417e1e95b9ccc6114ef7c44fa645a1704b688";
120530
+ const rawSentryRelease = "boxes-dev-dvb@1.0.61+ef3417e1e95b9ccc6114ef7c44fa645a1704b688";
120531
120531
  const packageVersion = typeof rawPackageVersion === "string" ? rawPackageVersion : void 0;
120532
120532
  const gitSha = typeof rawGitSha === "string" ? rawGitSha : void 0;
120533
120533
  const sentryRelease = typeof rawSentryRelease === "string" ? rawSentryRelease : void 0;
@@ -28,6 +28,8 @@ export declare const resolveSessionSpecifierFromExplicitTarget: ({ sessionName,
28
28
  availableSessions: SessionSpecifierRecord[];
29
29
  }) => SessionSpecifierResolution;
30
30
  export declare const isAttachReplacedCloseCode: (closeCode?: number) => closeCode is 1012;
31
+ type ConnectPhase = "connecting" | "reconnecting" | "attached";
32
+ export declare const shouldCancelConnectOnSigint: (phase: ConnectPhase) => phase is "connecting" | "reconnecting";
31
33
  export declare const runConnect: (args: string[], overrides?: ConnectOverrides) => Promise<void>;
32
34
  export {};
33
35
  //# sourceMappingURL=connect.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"connect.d.ts","sourceRoot":"","sources":["../../../src/devbox/commands/connect.ts"],"names":[],"mappings":"AAuDA,KAAK,gBAAgB,GAAG;IACtB,YAAY,CAAC,EAAE,MAAM,GAAG,UAAU,CAAC;IACnC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B,CAAC;AAEF,KAAK,0BAA0B,GAAG;IAChC,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,iBAAiB,EAAE,MAAM,GAAG,SAAS,CAAC;CACvC,CAAC;AAEF,KAAK,sBAAsB,GAAG;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,CAAC,EAAE,OAAO,CAAC;CACf,CAAC;AAEF,eAAO,MAAM,uBAAuB,GAAI,mGAMrC;IACD,yBAAyB,EAAE,OAAO,CAAC;IACnC,iBAAiB,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,uBAAuB,EAAE,OAAO,CAAC;CAClC,KAAG,MAAM,GAAG,SAKZ,CAAC;AAEF,eAAO,MAAM,6BAA6B,GAAI,oEAI3C;IACD,yBAAyB,EAAE,OAAO,CAAC;IACnC,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,uBAAuB,EAAE,OAAO,CAAC;CAClC,YAC4E,CAAC;AAE9E,eAAO,MAAM,yCAAyC,GAAI,qCAGvD;IACD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iBAAiB,EAAE,sBAAsB,EAAE,CAAC;CAC7C,KAAG,0BAYH,CAAC;AAEF,eAAO,MAAM,yBAAyB,GAAI,YAAY,MAAM,sBACxC,CAAC;AAukCrB,eAAO,MAAM,UAAU,GACrB,MAAM,MAAM,EAAE,EACd,YAAY,gBAAgB,kBAy4B7B,CAAC"}
1
+ {"version":3,"file":"connect.d.ts","sourceRoot":"","sources":["../../../src/devbox/commands/connect.ts"],"names":[],"mappings":"AAuDA,KAAK,gBAAgB,GAAG;IACtB,YAAY,CAAC,EAAE,MAAM,GAAG,UAAU,CAAC;IACnC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B,CAAC;AAEF,KAAK,0BAA0B,GAAG;IAChC,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,iBAAiB,EAAE,MAAM,GAAG,SAAS,CAAC;CACvC,CAAC;AAEF,KAAK,sBAAsB,GAAG;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,CAAC,EAAE,OAAO,CAAC;CACf,CAAC;AAEF,eAAO,MAAM,uBAAuB,GAAI,mGAMrC;IACD,yBAAyB,EAAE,OAAO,CAAC;IACnC,iBAAiB,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,uBAAuB,EAAE,OAAO,CAAC;CAClC,KAAG,MAAM,GAAG,SAKZ,CAAC;AAEF,eAAO,MAAM,6BAA6B,GAAI,oEAI3C;IACD,yBAAyB,EAAE,OAAO,CAAC;IACnC,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,uBAAuB,EAAE,OAAO,CAAC;CAClC,YAC4E,CAAC;AAE9E,eAAO,MAAM,yCAAyC,GAAI,qCAGvD;IACD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iBAAiB,EAAE,sBAAsB,EAAE,CAAC;CAC7C,KAAG,0BAYH,CAAC;AAEF,eAAO,MAAM,yBAAyB,GAAI,YAAY,MAAM,sBACxC,CAAC;AAErB,KAAK,YAAY,GAAG,YAAY,GAAG,cAAc,GAAG,UAAU,CAAC;AAE/D,eAAO,MAAM,2BAA2B,GAAI,OAAO,YAAY,2CACzC,CAAC;AAimCvB,eAAO,MAAM,UAAU,GACrB,MAAM,MAAM,EAAE,EACd,YAAY,gBAAgB,kBAy8B7B,CAAC"}
@@ -40,6 +40,7 @@ export const resolveSessionSpecifierFromExplicitTarget = ({ sessionName, availab
40
40
  return { sessionName: undefined, sessionIdOverride: match.id };
41
41
  };
42
42
  export const isAttachReplacedCloseCode = (closeCode) => closeCode === 1012;
43
+ export const shouldCancelConnectOnSigint = (phase) => phase !== "attached";
43
44
  const DEFAULT_TTY_COLS = 80;
44
45
  const DEFAULT_TTY_ROWS = 24;
45
46
  const DEFAULT_MODAL_APP_NAME = "sandbox-modal-smoke";
@@ -559,6 +560,7 @@ const promptRenameSession = async (current) => {
559
560
  const HEARTBEAT_INTERVAL_MS = 4000;
560
561
  const HEARTBEAT_TIMEOUT_MS = 8000;
561
562
  const INPUT_PROBE_TIMEOUT_MS = 1000;
563
+ const CONNECT_OPEN_TIMEOUT_MS = 20_000;
562
564
  const WS_OPEN_STATE = 1;
563
565
  const streamExecSession = async (ws, options) => new Promise((resolve, reject) => {
564
566
  let exitCode = null;
@@ -571,6 +573,7 @@ const streamExecSession = async (ws, options) => new Promise((resolve, reject) =
571
573
  let lastSeenAt = Date.now();
572
574
  let heartbeatTimer = null;
573
575
  let inputProbeTimer = null;
576
+ let openTimer = null;
574
577
  let inputProbeStamp = 0;
575
578
  let disconnectNoted = false;
576
579
  let errorMessage;
@@ -823,6 +826,12 @@ const streamExecSession = async (ws, options) => new Promise((resolve, reject) =
823
826
  inputProbeTimer = null;
824
827
  }
825
828
  };
829
+ const stopOpenTimer = () => {
830
+ if (openTimer) {
831
+ clearTimeout(openTimer);
832
+ openTimer = null;
833
+ }
834
+ };
826
835
  const onPong = () => {
827
836
  markAlive();
828
837
  latency?.notePong();
@@ -927,6 +936,7 @@ const streamExecSession = async (ws, options) => new Promise((resolve, reject) =
927
936
  ws.removeEventListener("error", onError);
928
937
  ws.removeEventListener("open", onOpen);
929
938
  stopInputProbe();
939
+ stopOpenTimer();
930
940
  stopHeartbeat();
931
941
  stopInput();
932
942
  };
@@ -980,6 +990,7 @@ const streamExecSession = async (ws, options) => new Promise((resolve, reject) =
980
990
  resolve(result);
981
991
  };
982
992
  const onOpen = () => {
993
+ stopOpenTimer();
983
994
  opened = true;
984
995
  markAlive();
985
996
  latency?.noteConnectOpen();
@@ -994,6 +1005,19 @@ const streamExecSession = async (ws, options) => new Promise((resolve, reject) =
994
1005
  ws.addEventListener("error", onError);
995
1006
  ws.addEventListener("open", onOpen);
996
1007
  sendResize();
1008
+ const openTimeoutMs = options.openTimeoutMs ?? CONNECT_OPEN_TIMEOUT_MS;
1009
+ if (openTimeoutMs > 0) {
1010
+ openTimer = setTimeout(() => {
1011
+ if (resolved || opened)
1012
+ return;
1013
+ noteDisconnect();
1014
+ forceClose();
1015
+ onError({
1016
+ data: new Error(`Timed out waiting for session connection after ${openTimeoutMs}ms.`),
1017
+ });
1018
+ }, openTimeoutMs);
1019
+ openTimer.unref();
1020
+ }
997
1021
  });
998
1022
  export const runConnect = async (args, overrides) => {
999
1023
  const parsed = parseConnectArgs(args);
@@ -1532,6 +1556,11 @@ export const runConnect = async (args, overrides) => {
1532
1556
  let retryDelayMs = 250;
1533
1557
  let printedSessionInfo = false;
1534
1558
  let sawOutput = false;
1559
+ let connectPhase = "connecting";
1560
+ let activeSocket = null;
1561
+ let localCancelRequested = false;
1562
+ let attemptedAttach = false;
1563
+ let sigintListenerInstalled = false;
1535
1564
  const printSessionInfo = (message) => {
1536
1565
  if (process.stderr.isTTY && process.stdout.isTTY) {
1537
1566
  process.stderr.write(`\u001b7\r\n${message}\r\n\u001b8`);
@@ -1559,217 +1588,278 @@ export const runConnect = async (args, overrides) => {
1559
1588
  status.stop();
1560
1589
  }
1561
1590
  };
1562
- while (true) {
1591
+ const onSigint = () => {
1592
+ localCancelRequested = true;
1593
+ if (!activeSocket)
1594
+ return;
1563
1595
  try {
1564
- await refreshSessionMapping();
1596
+ activeSocket.terminate?.();
1565
1597
  }
1566
- catch (error) {
1567
- logger.warn("session_refresh_failed", { error: String(error) });
1598
+ catch {
1599
+ // ignore terminate failures
1568
1600
  }
1569
- if (explicitName &&
1570
- sessionName &&
1571
- !sessionId &&
1572
- !confirmedCreateExplicit) {
1573
- pauseStatus();
1574
- const confirmed = await confirmNewSession(sessionName);
1575
- if (!confirmed) {
1576
- throw new Error(`Session "${sessionName}" not created.`);
1577
- }
1578
- confirmedCreateExplicit = true;
1601
+ try {
1602
+ activeSocket.close();
1579
1603
  }
1580
- let createdNew = false;
1581
- let mappingWrite = null;
1582
- latency?.noteConnectStart();
1583
- const canAttach = shouldAttachToExistingSession({
1584
- supportsAttachExecSession,
1585
- sessionId,
1586
- requireFreshSessionOpen,
1587
- });
1588
- stage(canAttach ? "Attaching to session" : "Opening session");
1589
- let ws;
1590
- if (canAttach) {
1591
- if (!sessionId) {
1592
- throw new Error("Missing session id for attach.");
1593
- }
1594
- ws = client.attachExecSession(spriteAlias, sessionId);
1604
+ catch {
1605
+ // ignore close failures
1595
1606
  }
1596
- else {
1597
- createdNew = true;
1598
- ws = client.openExecSession(spriteAlias, {
1599
- cmd: execCommand,
1600
- tty: isTty,
1601
- stdin: openStdin,
1602
- ...(ttySize ? { cols: ttySize.cols, rows: ttySize.rows } : {}),
1603
- ...(modalSessionLogPath ? { logPath: modalSessionLogPath } : {}),
1604
- });
1607
+ };
1608
+ const setConnectPhase = (phase) => {
1609
+ connectPhase = phase;
1610
+ const shouldInstallListener = shouldReconnect && shouldCancelConnectOnSigint(connectPhase);
1611
+ if (shouldInstallListener && !sigintListenerInstalled) {
1612
+ process.on("SIGINT", onSigint);
1613
+ sigintListenerInstalled = true;
1614
+ return;
1605
1615
  }
1606
- const shouldSendInitialInput = createdNew && Boolean(pendingInitialInput);
1607
- const initialInputToSend = shouldSendInitialInput
1608
- ? pendingInitialInput
1609
- : undefined;
1610
- const result = await streamExecSession(ws, {
1611
- tty: isTty,
1612
- stdin: localStdin,
1613
- supportsResizeControlMessages,
1614
- ...(latency ? { latency } : {}),
1615
- onOutput: noteOutput,
1616
- onOutputChunk: (chunk) => {
1617
- sessionLogSink?.write(chunk);
1618
- },
1619
- onInputChunk: (chunk) => {
1620
- sessionLogSink?.write(chunk);
1621
- },
1622
- onOpen: () => {
1623
- if (shouldSendInitialInput) {
1624
- pendingInitialInput = undefined;
1625
- }
1626
- if (sessionId) {
1627
- reportSessionInfo(sessionId);
1616
+ if (!shouldInstallListener && sigintListenerInstalled) {
1617
+ process.removeListener("SIGINT", onSigint);
1618
+ sigintListenerInstalled = false;
1619
+ }
1620
+ };
1621
+ setConnectPhase("connecting");
1622
+ try {
1623
+ while (true) {
1624
+ if (localCancelRequested) {
1625
+ process.exitCode = 130;
1626
+ break;
1627
+ }
1628
+ setConnectPhase(attemptedAttach ? "reconnecting" : "connecting");
1629
+ attemptedAttach = true;
1630
+ try {
1631
+ await refreshSessionMapping();
1632
+ }
1633
+ catch (error) {
1634
+ logger.warn("session_refresh_failed", { error: String(error) });
1635
+ }
1636
+ if (explicitName &&
1637
+ sessionName &&
1638
+ !sessionId &&
1639
+ !confirmedCreateExplicit) {
1640
+ pauseStatus();
1641
+ const confirmed = await confirmNewSession(sessionName);
1642
+ if (!confirmed) {
1643
+ throw new Error(`Session "${sessionName}" not created.`);
1628
1644
  }
1629
- },
1630
- ...(initialInputToSend ? { initialInput: initialInputToSend } : {}),
1631
- onSessionInfo: (id) => {
1632
- if (supportsAttachExecSession) {
1633
- if (createdNew) {
1634
- requireFreshSessionOpen = false;
1635
- }
1636
- sessionId = sessionId ?? id;
1645
+ confirmedCreateExplicit = true;
1646
+ }
1647
+ let createdNew = false;
1648
+ let mappingWrite = null;
1649
+ latency?.noteConnectStart();
1650
+ const canAttach = shouldAttachToExistingSession({
1651
+ supportsAttachExecSession,
1652
+ sessionId,
1653
+ requireFreshSessionOpen,
1654
+ });
1655
+ stage(canAttach ? "Attaching to session" : "Opening session");
1656
+ let ws;
1657
+ if (canAttach) {
1658
+ if (!sessionId) {
1659
+ throw new Error("Missing session id for attach.");
1637
1660
  }
1638
- if (sessionName && createdNew) {
1639
- if (autoNamed) {
1640
- mappingWrite = finalizeAutoSession(id);
1641
- if (mappingWrite) {
1642
- mappingWrite
1643
- .then(() => {
1644
- reportSessionInfo(id);
1645
- })
1646
- .catch((error) => {
1647
- logger.warn("session_auto_rename_failed", {
1648
- error: String(error),
1649
- });
1661
+ ws = client.attachExecSession(spriteAlias, sessionId);
1662
+ }
1663
+ else {
1664
+ createdNew = true;
1665
+ ws = client.openExecSession(spriteAlias, {
1666
+ cmd: execCommand,
1667
+ tty: isTty,
1668
+ stdin: openStdin,
1669
+ ...(ttySize ? { cols: ttySize.cols, rows: ttySize.rows } : {}),
1670
+ ...(modalSessionLogPath ? { logPath: modalSessionLogPath } : {}),
1671
+ });
1672
+ }
1673
+ activeSocket = ws;
1674
+ const shouldSendInitialInput = createdNew && Boolean(pendingInitialInput);
1675
+ const initialInputToSend = shouldSendInitialInput
1676
+ ? pendingInitialInput
1677
+ : undefined;
1678
+ let result;
1679
+ try {
1680
+ result = await streamExecSession(ws, {
1681
+ tty: isTty,
1682
+ stdin: localStdin,
1683
+ supportsResizeControlMessages,
1684
+ ...(latency ? { latency } : {}),
1685
+ openTimeoutMs: CONNECT_OPEN_TIMEOUT_MS,
1686
+ onOutput: noteOutput,
1687
+ onOutputChunk: (chunk) => {
1688
+ sessionLogSink?.write(chunk);
1689
+ },
1690
+ onInputChunk: (chunk) => {
1691
+ sessionLogSink?.write(chunk);
1692
+ },
1693
+ onOpen: () => {
1694
+ setConnectPhase("attached");
1695
+ if (shouldSendInitialInput) {
1696
+ pendingInitialInput = undefined;
1697
+ }
1698
+ if (sessionId) {
1699
+ reportSessionInfo(sessionId);
1700
+ }
1701
+ },
1702
+ ...(initialInputToSend
1703
+ ? { initialInput: initialInputToSend }
1704
+ : {}),
1705
+ onSessionInfo: (id) => {
1706
+ if (supportsAttachExecSession) {
1707
+ if (createdNew) {
1708
+ requireFreshSessionOpen = false;
1709
+ }
1710
+ sessionId = sessionId ?? id;
1711
+ }
1712
+ if (sessionName && createdNew) {
1713
+ if (autoNamed) {
1714
+ mappingWrite = finalizeAutoSession(id);
1715
+ if (mappingWrite) {
1716
+ mappingWrite
1717
+ .then(() => {
1718
+ reportSessionInfo(id);
1719
+ })
1720
+ .catch((error) => {
1721
+ logger.warn("session_auto_rename_failed", {
1722
+ error: String(error),
1723
+ });
1724
+ reportSessionInfo(id);
1725
+ });
1726
+ }
1727
+ else {
1728
+ reportSessionInfo(id);
1729
+ }
1730
+ }
1731
+ else {
1732
+ sessions[sessionName] = id;
1733
+ mappingWrite = writeSpriteSessions(client, spriteAlias, sessions);
1650
1734
  reportSessionInfo(id);
1651
- });
1735
+ }
1652
1736
  }
1653
1737
  else {
1654
1738
  reportSessionInfo(id);
1655
1739
  }
1656
- }
1657
- else {
1658
- sessions[sessionName] = id;
1659
- mappingWrite = writeSpriteSessions(client, spriteAlias, sessions);
1660
- reportSessionInfo(id);
1661
- }
1662
- }
1663
- else {
1664
- reportSessionInfo(id);
1665
- }
1666
- if (isDetached) {
1667
- ws.close();
1668
- }
1669
- },
1670
- onPortEvent: notifyPortEvent,
1671
- ...(shouldReconnect
1672
- ? {
1673
- onDisconnectNotice: () => {
1674
- resetTerminalInputModes();
1675
- if (!reconnectNoticeShown) {
1676
- console.error("Connection lost. Reconnecting... (Ctrl+C to cancel)");
1677
- reconnectNoticeShown = true;
1740
+ if (isDetached) {
1741
+ ws.close();
1678
1742
  }
1679
1743
  },
1744
+ onPortEvent: notifyPortEvent,
1745
+ ...(shouldReconnect
1746
+ ? {
1747
+ onDisconnectNotice: () => {
1748
+ setConnectPhase("reconnecting");
1749
+ resetTerminalInputModes();
1750
+ if (!reconnectNoticeShown) {
1751
+ console.error("Connection lost. Reconnecting... (Ctrl+C to cancel)");
1752
+ reconnectNoticeShown = true;
1753
+ }
1754
+ },
1755
+ }
1756
+ : {}),
1757
+ });
1758
+ }
1759
+ finally {
1760
+ activeSocket = null;
1761
+ }
1762
+ if (mappingWrite) {
1763
+ try {
1764
+ await mappingWrite;
1765
+ }
1766
+ catch (error) {
1767
+ logger.warn("session_write_failed", { error: String(error) });
1680
1768
  }
1681
- : {}),
1682
- });
1683
- if (mappingWrite) {
1684
- try {
1685
- await mappingWrite;
1686
1769
  }
1687
- catch (error) {
1688
- logger.warn("session_write_failed", { error: String(error) });
1770
+ if (localCancelRequested) {
1771
+ process.exitCode = 130;
1772
+ break;
1689
1773
  }
1690
- }
1691
- if (result.kind === "exit") {
1692
- process.exitCode = result.code;
1693
- break;
1694
- }
1695
- if (result.kind === "detach") {
1696
- if (autoNamed && sessionName && sessionId) {
1697
- const nextName = await promptRenameSession(sessionName);
1698
- if (nextName && nextName !== sessionName) {
1699
- try {
1700
- await renameSessionMapping(sessionName, nextName, sessionId);
1701
- }
1702
- catch (error) {
1703
- console.error(`Failed to rename session: ${error instanceof Error ? error.message : String(error)}`);
1774
+ if (result.kind === "exit") {
1775
+ process.exitCode = result.code;
1776
+ break;
1777
+ }
1778
+ if (result.kind === "detach") {
1779
+ if (autoNamed && sessionName && sessionId) {
1780
+ const nextName = await promptRenameSession(sessionName);
1781
+ if (nextName && nextName !== sessionName) {
1782
+ try {
1783
+ await renameSessionMapping(sessionName, nextName, sessionId);
1784
+ }
1785
+ catch (error) {
1786
+ console.error(`Failed to rename session: ${error instanceof Error ? error.message : String(error)}`);
1787
+ }
1704
1788
  }
1705
1789
  }
1790
+ process.exitCode = 0;
1791
+ break;
1706
1792
  }
1707
- process.exitCode = 0;
1708
- break;
1709
- }
1710
- if (isDetached) {
1711
- process.exitCode = 0;
1712
- break;
1713
- }
1714
- if (isAttachReplacedCloseCode(result.closeCode)) {
1715
- resetTerminalInputModes();
1716
- console.error("Session attached from another client. Closing this window.");
1717
- process.exitCode = 0;
1718
- break;
1719
- }
1720
- if (!shouldReconnect) {
1721
- if (result.errorMessage) {
1722
- throw new Error(`Connection lost: ${result.errorMessage}`);
1793
+ if (isDetached) {
1794
+ process.exitCode = 0;
1795
+ break;
1723
1796
  }
1724
- throw new Error("Connection lost.");
1725
- }
1726
- resetTerminalInputModes();
1727
- if (!reconnectNoticeShown) {
1728
- console.error("Connection lost. Reconnecting... (Ctrl+C to cancel)");
1729
- reconnectNoticeShown = true;
1730
- }
1731
- if (sessionId && supportsAttachExecSession) {
1732
- if (computeProvider === "modal" &&
1733
- isSessionNotFoundError(result.errorMessage)) {
1734
- if (sessionName) {
1735
- delete sessions[sessionName];
1736
- await writeSpriteSessions(client, spriteAlias, sessions);
1797
+ if (isAttachReplacedCloseCode(result.closeCode)) {
1798
+ resetTerminalInputModes();
1799
+ console.error("Session attached from another client. Closing this window.");
1800
+ process.exitCode = 0;
1801
+ break;
1802
+ }
1803
+ if (!shouldReconnect) {
1804
+ if (result.errorMessage) {
1805
+ throw new Error(`Connection lost: ${result.errorMessage}`);
1737
1806
  }
1738
- sessionId = undefined;
1807
+ throw new Error("Connection lost.");
1739
1808
  }
1740
- else if (computeProvider !== "modal" &&
1741
- result.opened === false &&
1742
- supportsListExecSessions) {
1743
- try {
1744
- const available = await client.listExecSessions(spriteAlias);
1745
- const match = available.find((session) => session.id === sessionId);
1746
- if (!match || match.tty === false) {
1747
- if (sessionName) {
1748
- delete sessions[sessionName];
1749
- await writeSpriteSessions(client, spriteAlias, sessions);
1750
- }
1751
- sessionId = undefined;
1809
+ setConnectPhase("reconnecting");
1810
+ resetTerminalInputModes();
1811
+ if (!reconnectNoticeShown) {
1812
+ console.error("Connection lost. Reconnecting... (Ctrl+C to cancel)");
1813
+ reconnectNoticeShown = true;
1814
+ }
1815
+ if (sessionId && supportsAttachExecSession) {
1816
+ if (computeProvider === "modal" &&
1817
+ isSessionNotFoundError(result.errorMessage)) {
1818
+ if (sessionName) {
1819
+ delete sessions[sessionName];
1820
+ await writeSpriteSessions(client, spriteAlias, sessions);
1752
1821
  }
1822
+ sessionId = undefined;
1753
1823
  }
1754
- catch (error) {
1755
- logger.warn("session_lookup_failed", { error: String(error) });
1824
+ else if (computeProvider !== "modal" &&
1825
+ result.opened === false &&
1826
+ supportsListExecSessions) {
1827
+ try {
1828
+ const available = await client.listExecSessions(spriteAlias);
1829
+ const match = available.find((session) => session.id === sessionId);
1830
+ if (!match || match.tty === false) {
1831
+ if (sessionName) {
1832
+ delete sessions[sessionName];
1833
+ await writeSpriteSessions(client, spriteAlias, sessions);
1834
+ }
1835
+ sessionId = undefined;
1836
+ }
1837
+ }
1838
+ catch (error) {
1839
+ logger.warn("session_lookup_failed", { error: String(error) });
1840
+ }
1756
1841
  }
1757
1842
  }
1843
+ const networkWait = await waitForNetworkOnline({
1844
+ targetHost: networkTargetHost,
1845
+ onStatus: stage,
1846
+ onNotice: (message) => console.error(message),
1847
+ });
1848
+ if (networkWait === "aborted" || localCancelRequested) {
1849
+ process.exitCode = 130;
1850
+ break;
1851
+ }
1852
+ if (networkWait === "waited") {
1853
+ retryDelayMs = 250;
1854
+ }
1855
+ await new Promise((resolve) => setTimeout(resolve, retryDelayMs));
1856
+ retryDelayMs = Math.min(5000, Math.round(retryDelayMs * 1.6));
1758
1857
  }
1759
- const networkWait = await waitForNetworkOnline({
1760
- targetHost: networkTargetHost,
1761
- onStatus: stage,
1762
- onNotice: (message) => console.error(message),
1763
- });
1764
- if (networkWait === "aborted") {
1765
- process.exitCode = 130;
1766
- break;
1767
- }
1768
- if (networkWait === "waited") {
1769
- retryDelayMs = 250;
1858
+ }
1859
+ finally {
1860
+ if (sigintListenerInstalled) {
1861
+ process.removeListener("SIGINT", onSigint);
1770
1862
  }
1771
- await new Promise((resolve) => setTimeout(resolve, retryDelayMs));
1772
- retryDelayMs = Math.min(5000, Math.round(retryDelayMs * 1.6));
1773
1863
  }
1774
1864
  }
1775
1865
  finally {