@buildautomaton/cli 0.1.79 → 0.1.80

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/cli.js CHANGED
@@ -12522,6 +12522,16 @@ async function awaitSymbolIndexWorkerShutdown() {
12522
12522
  await terminateSymbolIndexWorker2();
12523
12523
  }
12524
12524
  }
12525
+ async function clearCliImmediateShutdown() {
12526
+ if (symbolIndexShutdownPromise != null) {
12527
+ try {
12528
+ await symbolIndexShutdownPromise;
12529
+ } catch {
12530
+ }
12531
+ }
12532
+ cliImmediateShutdownRequested = false;
12533
+ symbolIndexShutdownPromise = null;
12534
+ }
12525
12535
  function resetCliImmediateShutdownForTests() {
12526
12536
  cliImmediateShutdownRequested = false;
12527
12537
  symbolIndexShutdownPromise = null;
@@ -31071,7 +31081,7 @@ var {
31071
31081
  } = import_index.default;
31072
31082
 
31073
31083
  // src/cli-version.ts
31074
- var CLI_VERSION = "0.1.79".length > 0 ? "0.1.79" : "0.0.0-dev";
31084
+ var CLI_VERSION = "0.1.80".length > 0 ? "0.1.80" : "0.0.0-dev";
31075
31085
 
31076
31086
  // src/cli/defaults.ts
31077
31087
  var DEFAULT_API_URL = process.env.BUILDAUTOMATON_API_URL ?? "https://api.buildautomaton.com";
@@ -31533,181 +31543,8 @@ function installBridgeProcessResilience() {
31533
31543
  });
31534
31544
  }
31535
31545
 
31536
- // ../../node_modules/.pnpm/ws@8.19.0/node_modules/ws/wrapper.mjs
31537
- var import_stream = __toESM(require_stream(), 1);
31538
- var import_receiver = __toESM(require_receiver(), 1);
31539
- var import_sender = __toESM(require_sender(), 1);
31540
- var import_websocket = __toESM(require_websocket(), 1);
31541
- var import_websocket_server = __toESM(require_websocket_server(), 1);
31542
- var wrapper_default = import_websocket.default;
31543
-
31544
- // src/connection/cli-ws-client.ts
31545
- import https from "node:https";
31546
- var CLI_WEBSOCKET_CLIENT_PING_MS = 25e3;
31547
- function attachWebSocketClientPing(ws, intervalMs) {
31548
- let timer = null;
31549
- function clear() {
31550
- if (timer != null) {
31551
- clearInterval(timer);
31552
- timer = null;
31553
- }
31554
- }
31555
- clear();
31556
- timer = setInterval(() => {
31557
- if (ws.readyState === wrapper_default.OPEN) {
31558
- try {
31559
- ws.ping();
31560
- } catch {
31561
- }
31562
- }
31563
- }, intervalMs);
31564
- return clear;
31565
- }
31566
- function buildCliWebSocketClientOptions(wsUrl) {
31567
- const wsOptions = { perMessageDeflate: false };
31568
- if (wsUrl.startsWith("wss://")) {
31569
- wsOptions.agent = new https.Agent({ rejectUnauthorized: false });
31570
- }
31571
- return wsOptions;
31572
- }
31573
- function logCliWebSocketError(log2, serviceLabel, err, detail) {
31574
- const mid = detail ? ` ${detail}` : "";
31575
- log2(`${serviceLabel} WebSocket error${mid}: ${err.message}`);
31576
- }
31577
- function safeCloseWebSocket(ws) {
31578
- try {
31579
- if (ws.readyState === wrapper_default.CLOSED) {
31580
- ws.removeAllListeners();
31581
- return;
31582
- }
31583
- ws.once("close", () => {
31584
- ws.removeAllListeners();
31585
- });
31586
- ws.close();
31587
- } catch {
31588
- try {
31589
- ws.removeAllListeners();
31590
- } catch {
31591
- }
31592
- }
31593
- }
31594
- function safeSendWebSocketBinary(ws, data) {
31595
- if (ws.readyState !== wrapper_default.OPEN) return false;
31596
- try {
31597
- ws.send(data, { binary: true });
31598
- return true;
31599
- } catch {
31600
- return false;
31601
- }
31602
- }
31603
-
31604
- // src/connection/parse-compact-heartbeat-ack.ts
31605
- function tryParseCompactHeartbeatAck(raw) {
31606
- let str = null;
31607
- if (typeof raw === "string") {
31608
- str = raw;
31609
- } else if (Buffer.isBuffer(raw)) {
31610
- str = raw.toString("utf8");
31611
- } else if (raw instanceof ArrayBuffer) {
31612
- str = Buffer.from(raw).toString("utf8");
31613
- } else {
31614
- return null;
31615
- }
31616
- if (str.length > 64 || !str.includes('"ha"')) return null;
31617
- try {
31618
- const parsed = JSON.parse(str);
31619
- if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) return null;
31620
- const o = parsed;
31621
- if (o.t !== "ha") return null;
31622
- const s = o.s;
31623
- if (typeof s !== "number" || !Number.isFinite(s)) return null;
31624
- return Math.trunc(s);
31625
- } catch {
31626
- return null;
31627
- }
31628
- }
31629
-
31630
- // src/connection/create-ws-bridge.ts
31631
- var BRIDGE_AUTH_ERROR_HEADER = "x-bridge-auth-error";
31632
- var BRIDGE_AUTH_ERROR_TOKEN_INVALID = "token_invalid";
31633
- function createWsBridge(options) {
31634
- const {
31635
- url: url2,
31636
- onMessage,
31637
- onOpen,
31638
- onClose,
31639
- onError: onError2,
31640
- onAuthInvalid,
31641
- clientPingIntervalMs,
31642
- onCompactHeartbeatAck,
31643
- isActiveSocket
31644
- } = options;
31645
- const ws = new wrapper_default(url2, buildCliWebSocketClientOptions(url2));
31646
- let clearClientPing = null;
31647
- function disposeClientPing() {
31648
- clearClientPing?.();
31649
- clearClientPing = null;
31650
- }
31651
- ws.on("unexpected-response", (request, response) => {
31652
- const status = response?.statusCode ?? 0;
31653
- const headers = response?.headers ?? {};
31654
- const errCode = (headers[BRIDGE_AUTH_ERROR_HEADER] ?? headers["X-Bridge-Auth-Error"] ?? "").toLowerCase();
31655
- if (status === 401 && errCode === BRIDGE_AUTH_ERROR_TOKEN_INVALID) {
31656
- void Promise.resolve(onAuthInvalid?.());
31657
- }
31658
- });
31659
- ws.on("open", () => {
31660
- disposeClientPing();
31661
- if (clientPingIntervalMs != null && clientPingIntervalMs > 0) {
31662
- clearClientPing = attachWebSocketClientPing(ws, clientPingIntervalMs);
31663
- }
31664
- onOpen?.();
31665
- });
31666
- ws.on("message", (raw) => {
31667
- const ackSeq = tryParseCompactHeartbeatAck(raw);
31668
- if (ackSeq != null) {
31669
- onCompactHeartbeatAck?.(ackSeq);
31670
- return;
31671
- }
31672
- setImmediate(() => {
31673
- if (isActiveSocket && !isActiveSocket(ws)) return;
31674
- try {
31675
- let data;
31676
- if (typeof raw === "string") {
31677
- data = JSON.parse(raw);
31678
- } else if (Buffer.isBuffer(raw) || raw instanceof ArrayBuffer) {
31679
- const str = Buffer.isBuffer(raw) ? raw.toString("utf8") : Buffer.from(raw).toString("utf8");
31680
- data = JSON.parse(str);
31681
- } else {
31682
- data = raw;
31683
- }
31684
- onMessage?.(data, ws);
31685
- } catch {
31686
- onMessage?.(raw, ws);
31687
- }
31688
- });
31689
- });
31690
- ws.on("close", (code, reason) => {
31691
- disposeClientPing();
31692
- onClose?.(code, reason.toString());
31693
- });
31694
- ws.on("error", (err) => {
31695
- disposeClientPing();
31696
- onError2?.(err);
31697
- });
31698
- return ws;
31699
- }
31700
- function sendWsMessage(ws, payload) {
31701
- if (ws.readyState !== wrapper_default.OPEN) return false;
31702
- ws.send(JSON.stringify(payload));
31703
- return true;
31704
- }
31705
-
31706
- // src/connection/heartbeat/constants.ts
31707
- var BRIDGE_APP_HEARTBEAT_INTERVAL_MS = 1e4;
31708
- var BRIDGE_HEARTBEAT_SEQ_MAX = 2147483646;
31709
- var BRIDGE_HEARTBEAT_MISSED_ACKS_BEFORE_RECONNECT = 4;
31710
- var BRIDGE_HEARTBEAT_RTT_SAMPLE_MAX = 5;
31546
+ // src/auth/pending/run-pending-auth.ts
31547
+ init_log();
31711
31548
 
31712
31549
  // ../../node_modules/.pnpm/open@10.2.0/node_modules/open/index.js
31713
31550
  import process7 from "node:process";
@@ -32262,8 +32099,196 @@ async function openBrowser(connectionId, initialWorkspaceId, preferredBridgeName
32262
32099
  }
32263
32100
  }
32264
32101
 
32265
- // src/auth/run-pending-auth.ts
32266
- init_log();
32102
+ // src/auth/pending/constants.ts
32103
+ var PENDING_KEEPALIVE_MS = 25e3;
32104
+ var BROWSER_OPEN_FALLBACK_MS = 4e3;
32105
+
32106
+ // ../../node_modules/.pnpm/ws@8.19.0/node_modules/ws/wrapper.mjs
32107
+ var import_stream = __toESM(require_stream(), 1);
32108
+ var import_receiver = __toESM(require_receiver(), 1);
32109
+ var import_sender = __toESM(require_sender(), 1);
32110
+ var import_websocket = __toESM(require_websocket(), 1);
32111
+ var import_websocket_server = __toESM(require_websocket_server(), 1);
32112
+ var wrapper_default = import_websocket.default;
32113
+
32114
+ // src/connection/cli-ws-client.ts
32115
+ import https from "node:https";
32116
+ var CLI_WEBSOCKET_CLIENT_PING_MS = 25e3;
32117
+ function attachWebSocketClientPing(ws, intervalMs) {
32118
+ let timer = null;
32119
+ function clear() {
32120
+ if (timer != null) {
32121
+ clearInterval(timer);
32122
+ timer = null;
32123
+ }
32124
+ }
32125
+ clear();
32126
+ timer = setInterval(() => {
32127
+ if (ws.readyState === wrapper_default.OPEN) {
32128
+ try {
32129
+ ws.ping();
32130
+ } catch {
32131
+ }
32132
+ }
32133
+ }, intervalMs);
32134
+ return clear;
32135
+ }
32136
+ function buildCliWebSocketClientOptions(wsUrl) {
32137
+ const wsOptions = { perMessageDeflate: false };
32138
+ if (wsUrl.startsWith("wss://")) {
32139
+ wsOptions.agent = new https.Agent({ rejectUnauthorized: false });
32140
+ }
32141
+ return wsOptions;
32142
+ }
32143
+ function logCliWebSocketError(log2, serviceLabel, err, detail) {
32144
+ const mid = detail ? ` ${detail}` : "";
32145
+ log2(`${serviceLabel} WebSocket error${mid}: ${err.message}`);
32146
+ }
32147
+ function safeCloseWebSocket(ws) {
32148
+ try {
32149
+ if (ws.readyState === wrapper_default.CLOSED) {
32150
+ ws.removeAllListeners();
32151
+ return;
32152
+ }
32153
+ ws.once("close", () => {
32154
+ ws.removeAllListeners();
32155
+ });
32156
+ ws.close();
32157
+ } catch {
32158
+ try {
32159
+ ws.removeAllListeners();
32160
+ } catch {
32161
+ }
32162
+ }
32163
+ }
32164
+ function safeSendWebSocketBinary(ws, data) {
32165
+ if (ws.readyState !== wrapper_default.OPEN) return false;
32166
+ try {
32167
+ ws.send(data, { binary: true });
32168
+ return true;
32169
+ } catch {
32170
+ return false;
32171
+ }
32172
+ }
32173
+
32174
+ // src/connection/create-ws-bridge-options.ts
32175
+ var BRIDGE_AUTH_ERROR_HEADER = "x-bridge-auth-error";
32176
+ var BRIDGE_AUTH_ERROR_TOKEN_INVALID = "token_invalid";
32177
+
32178
+ // src/connection/dispatch-ws-inbound-message.ts
32179
+ function dispatchWsInboundMessage(raw, ws, onMessage, isActiveSocket) {
32180
+ if (isActiveSocket && !isActiveSocket(ws)) return;
32181
+ try {
32182
+ let data;
32183
+ if (typeof raw === "string") {
32184
+ data = JSON.parse(raw);
32185
+ } else if (Buffer.isBuffer(raw) || raw instanceof ArrayBuffer) {
32186
+ const str = Buffer.isBuffer(raw) ? raw.toString("utf8") : Buffer.from(raw).toString("utf8");
32187
+ data = JSON.parse(str);
32188
+ } else {
32189
+ data = raw;
32190
+ }
32191
+ onMessage?.(data, ws);
32192
+ } catch {
32193
+ onMessage?.(raw, ws);
32194
+ }
32195
+ }
32196
+
32197
+ // src/connection/parse-compact-heartbeat-ack.ts
32198
+ function tryParseCompactHeartbeatAck(raw) {
32199
+ let str = null;
32200
+ if (typeof raw === "string") {
32201
+ str = raw;
32202
+ } else if (Buffer.isBuffer(raw)) {
32203
+ str = raw.toString("utf8");
32204
+ } else if (raw instanceof ArrayBuffer) {
32205
+ str = Buffer.from(raw).toString("utf8");
32206
+ } else {
32207
+ return null;
32208
+ }
32209
+ if (str.length > 64 || !str.includes('"ha"')) return null;
32210
+ try {
32211
+ const parsed = JSON.parse(str);
32212
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) return null;
32213
+ const o = parsed;
32214
+ if (o.t !== "ha") return null;
32215
+ const s = o.s;
32216
+ if (typeof s !== "number" || !Number.isFinite(s)) return null;
32217
+ return Math.trunc(s);
32218
+ } catch {
32219
+ return null;
32220
+ }
32221
+ }
32222
+
32223
+ // src/connection/create-ws-bridge.ts
32224
+ function createWsBridge(options) {
32225
+ const {
32226
+ url: url2,
32227
+ onMessage,
32228
+ onOpen,
32229
+ onClose,
32230
+ onError: onError2,
32231
+ onAuthInvalid,
32232
+ clientPingIntervalMs,
32233
+ onCompactHeartbeatAck,
32234
+ isActiveSocket,
32235
+ deferInboundMessages = true
32236
+ } = options;
32237
+ const ws = new wrapper_default(url2, buildCliWebSocketClientOptions(url2));
32238
+ let clearClientPing = null;
32239
+ const disposeClientPing = () => {
32240
+ clearClientPing?.();
32241
+ clearClientPing = null;
32242
+ };
32243
+ ws.on("unexpected-response", (_request, response) => {
32244
+ const status = response?.statusCode ?? 0;
32245
+ const headers = response?.headers ?? {};
32246
+ const errCode = (headers[BRIDGE_AUTH_ERROR_HEADER] ?? headers["X-Bridge-Auth-Error"] ?? "").toLowerCase();
32247
+ if (status === 401 && errCode === BRIDGE_AUTH_ERROR_TOKEN_INVALID) {
32248
+ void Promise.resolve(onAuthInvalid?.());
32249
+ }
32250
+ });
32251
+ ws.on("open", () => {
32252
+ disposeClientPing();
32253
+ if (clientPingIntervalMs != null && clientPingIntervalMs > 0) {
32254
+ clearClientPing = attachWebSocketClientPing(ws, clientPingIntervalMs);
32255
+ }
32256
+ onOpen?.();
32257
+ });
32258
+ ws.on("message", (raw) => {
32259
+ const ackSeq = tryParseCompactHeartbeatAck(raw);
32260
+ if (ackSeq != null) {
32261
+ onCompactHeartbeatAck?.(ackSeq);
32262
+ return;
32263
+ }
32264
+ if (deferInboundMessages) {
32265
+ setImmediate(() => dispatchWsInboundMessage(raw, ws, onMessage, isActiveSocket));
32266
+ return;
32267
+ }
32268
+ dispatchWsInboundMessage(raw, ws, onMessage, isActiveSocket);
32269
+ });
32270
+ ws.on("close", (code, reason) => {
32271
+ disposeClientPing();
32272
+ onClose?.(code, reason.toString());
32273
+ });
32274
+ ws.on("error", (err) => {
32275
+ disposeClientPing();
32276
+ onError2?.(err);
32277
+ });
32278
+ return ws;
32279
+ }
32280
+ function sendWsMessage(ws, payload) {
32281
+ if (ws.readyState !== wrapper_default.OPEN) return false;
32282
+ ws.send(JSON.stringify(payload));
32283
+ return true;
32284
+ }
32285
+
32286
+ // src/auth/pending/build-pending-bridge-url.ts
32287
+ function buildPendingBridgeUrl(apiUrl, connectionId) {
32288
+ const base = apiUrl.startsWith("https") ? apiUrl.replace(/^https/, "wss") : apiUrl.replace(/^http/, "ws");
32289
+ const params = new URLSearchParams({ connectionId });
32290
+ return `${base}/ws/bridge/pending?${params.toString()}`;
32291
+ }
32267
32292
 
32268
32293
  // src/connection/reconnect/constants.ts
32269
32294
  var RECONNECT_QUIET_MS = 2e3;
@@ -32594,6 +32619,12 @@ function scheduleMainBridgeReconnect(state, connect, log2, closeMeta) {
32594
32619
  });
32595
32620
  }
32596
32621
 
32622
+ // src/connection/heartbeat/constants.ts
32623
+ var BRIDGE_APP_HEARTBEAT_INTERVAL_MS = 1e4;
32624
+ var BRIDGE_HEARTBEAT_SEQ_MAX = 2147483646;
32625
+ var BRIDGE_HEARTBEAT_MISSED_ACKS_BEFORE_RECONNECT = 4;
32626
+ var BRIDGE_HEARTBEAT_RTT_SAMPLE_MAX = 5;
32627
+
32597
32628
  // src/connection/reconnect/duplicate-bridge-connection-detect.ts
32598
32629
  var BRIDGE_SUPERSEDED_CLOSE_REASON_SNIPPET = "superseded";
32599
32630
  var DUPLICATE_BRIDGE_SHORT_SESSION_MS = 25e3;
@@ -32678,145 +32709,177 @@ function clearFirehoseReconnectQuietOnOpen(ctx, log2) {
32678
32709
  });
32679
32710
  }
32680
32711
 
32681
- // src/auth/run-pending-auth.ts
32682
- var PENDING_KEEPALIVE_MS = 25e3;
32683
- var BROWSER_OPEN_FALLBACK_MS = 4e3;
32684
- function buildPendingBridgeUrl(apiUrl, connectionId) {
32685
- const base = apiUrl.startsWith("https") ? apiUrl.replace(/^https/, "wss") : apiUrl.replace(/^http/, "ws");
32686
- const params = new URLSearchParams({ connectionId });
32687
- return `${base}/ws/bridge/pending?${params.toString()}`;
32688
- }
32689
- function runPendingAuth(options) {
32690
- const { apiUrl, initialWorkspaceId, preferredBridgeName, onAuth } = options;
32691
- const logFn = options.log ?? log;
32692
- let ws = null;
32693
- let reconnectTimeout = null;
32694
- let keepaliveInterval = null;
32695
- let browserFallback = null;
32696
- const connectionId = crypto.randomUUID();
32697
- let hasOpenedBrowser = false;
32698
- let resolved = false;
32699
- let resolveAuth;
32700
- const authPromise = new Promise((resolve37) => {
32701
- resolveAuth = resolve37;
32702
- });
32703
- let reconnectAttempt = 0;
32704
- const signInQuiet = createEmptyReconnectQuietSlot();
32705
- function clearQuietOnOpen() {
32706
- clearReconnectQuietOnSuccessfulConnection(
32707
- signInQuiet,
32708
- logFn,
32709
- "[Bridge service] Sign-in connection restored."
32710
- );
32711
- reconnectAttempt = 0;
32712
- }
32713
- function beginDeferredPendingCloseLog(code, reason) {
32712
+ // src/auth/pending/pending-auth-on-close.ts
32713
+ function createPendingAuthOnClose(params) {
32714
+ const { session, log: log2, connect } = params;
32715
+ return (code, reason) => {
32716
+ if (session.keepaliveInterval) {
32717
+ clearInterval(session.keepaliveInterval);
32718
+ session.keepaliveInterval = null;
32719
+ }
32720
+ if (session.resolved) return;
32714
32721
  beginDeferredDisconnectForReconnect({
32715
- isClosedByUser: () => resolved,
32716
- quiet: signInQuiet,
32722
+ isClosedByUser: () => session.resolved,
32723
+ quiet: session.signInQuiet,
32717
32724
  code,
32718
32725
  reason,
32719
32726
  willReconnect: true,
32720
- log: logFn,
32727
+ log: log2,
32721
32728
  serviceLabel: "[Bridge service]",
32722
32729
  shutdownDetail: "Not reconnecting (shutting down).",
32723
32730
  reconnectingDetail: "Waiting for browser sign-in; reconnecting\u2026",
32724
- shouldAbortQuietWindow: () => ws != null && ws.readyState === wrapper_default.OPEN
32731
+ shouldAbortQuietWindow: () => session.ws != null && session.ws.readyState === wrapper_default.OPEN
32725
32732
  });
32726
- }
32727
- function cleanup() {
32728
- clearReconnectQuietTimer(signInQuiet);
32729
- if (reconnectTimeout) {
32730
- clearTimeout(reconnectTimeout);
32731
- reconnectTimeout = null;
32732
- }
32733
- if (browserFallback) {
32734
- clearTimeout(browserFallback);
32735
- browserFallback = null;
32736
- }
32737
- if (keepaliveInterval) {
32738
- clearInterval(keepaliveInterval);
32739
- keepaliveInterval = null;
32740
- }
32741
- if (ws) {
32742
- const w = ws;
32743
- ws = null;
32744
- safeCloseWebSocket(w);
32733
+ const delay4 = pendingAuthReconnectDelayMs(session.reconnectAttempt);
32734
+ session.reconnectAttempt += 1;
32735
+ if (session.signInQuiet.verboseLogs) {
32736
+ const delayLabel = formatReconnectDelayForLog(delay4);
32737
+ log2(
32738
+ `[Bridge service] Next sign-in connection attempt in ${delayLabel} (attempt ${session.reconnectAttempt}).`
32739
+ );
32745
32740
  }
32741
+ session.reconnectTimeout = setTimeout(() => {
32742
+ session.reconnectTimeout = null;
32743
+ connect();
32744
+ }, delay4);
32745
+ };
32746
+ }
32747
+
32748
+ // src/auth/pending/pending-auth-session.ts
32749
+ function createPendingAuthSession() {
32750
+ let resolveAuth;
32751
+ const authPromise = new Promise((resolve37) => {
32752
+ resolveAuth = resolve37;
32753
+ });
32754
+ return {
32755
+ ws: null,
32756
+ reconnectTimeout: null,
32757
+ keepaliveInterval: null,
32758
+ browserFallback: null,
32759
+ connectionId: crypto.randomUUID(),
32760
+ hasOpenedBrowser: false,
32761
+ resolved: false,
32762
+ resolveAuth,
32763
+ reconnectAttempt: 0,
32764
+ signInQuiet: createEmptyReconnectQuietSlot(),
32765
+ authPromise
32766
+ };
32767
+ }
32768
+ function cleanupPendingAuthSession(session) {
32769
+ clearReconnectQuietTimer(session.signInQuiet);
32770
+ if (session.reconnectTimeout) {
32771
+ clearTimeout(session.reconnectTimeout);
32772
+ session.reconnectTimeout = null;
32746
32773
  }
32747
- function connect() {
32748
- const url2 = buildPendingBridgeUrl(apiUrl, connectionId);
32749
- let pendingHbSeq = -1;
32750
- ws = createWsBridge({
32751
- url: url2,
32752
- onOpen: () => {
32753
- clearQuietOnOpen();
32754
- pendingHbSeq = -1;
32755
- sendWsMessage(ws, { type: "identify", role: "cli", cliVersion: CLI_VERSION });
32756
- keepaliveInterval = setInterval(() => {
32757
- if (resolved || !ws || ws.readyState !== 1) return;
32758
- pendingHbSeq = pendingHbSeq >= BRIDGE_HEARTBEAT_SEQ_MAX ? 0 : pendingHbSeq + 1;
32759
- const hb = { t: "h", s: pendingHbSeq };
32760
- sendWsMessage(ws, hb);
32761
- }, PENDING_KEEPALIVE_MS);
32762
- if (browserFallback) {
32763
- clearTimeout(browserFallback);
32764
- browserFallback = null;
32765
- }
32766
- if (!hasOpenedBrowser) {
32767
- hasOpenedBrowser = true;
32768
- void openBrowser(connectionId, initialWorkspaceId, preferredBridgeName, apiUrl, logFn);
32769
- }
32770
- },
32771
- onClose: (code, reason) => {
32772
- if (keepaliveInterval) {
32773
- clearInterval(keepaliveInterval);
32774
- keepaliveInterval = null;
32775
- }
32776
- if (resolved) return;
32777
- beginDeferredPendingCloseLog(code, reason);
32778
- const delay4 = pendingAuthReconnectDelayMs(reconnectAttempt);
32779
- reconnectAttempt += 1;
32780
- if (signInQuiet.verboseLogs) {
32781
- const delayLabel = formatReconnectDelayForLog(delay4);
32782
- logFn(
32783
- `[Bridge service] Next sign-in connection attempt in ${delayLabel} (attempt ${reconnectAttempt}).`
32784
- );
32785
- }
32786
- reconnectTimeout = setTimeout(() => {
32787
- reconnectTimeout = null;
32788
- connect();
32789
- }, delay4);
32790
- },
32791
- onError: (err) => logCliWebSocketError(logFn, "[Bridge service]", err, "while waiting for sign-in"),
32792
- onMessage: (data) => {
32793
- const msg = data;
32794
- if (msg.type === "auth_token" && typeof msg.token === "string") {
32795
- resolved = true;
32796
- const workspaceId = typeof msg.workspaceId === "string" ? msg.workspaceId : "";
32797
- cleanup();
32798
- const refreshToken = typeof msg.refreshToken === "string" ? msg.refreshToken : void 0;
32799
- const result = { workspaceId, token: msg.token, refreshToken };
32800
- resolveAuth(result);
32801
- onAuth(result);
32802
- }
32803
- }
32804
- });
32774
+ if (session.browserFallback) {
32775
+ clearTimeout(session.browserFallback);
32776
+ session.browserFallback = null;
32805
32777
  }
32806
- browserFallback = setTimeout(() => {
32807
- browserFallback = null;
32808
- if (!hasOpenedBrowser) {
32809
- hasOpenedBrowser = true;
32810
- void openBrowser(connectionId, initialWorkspaceId, preferredBridgeName, apiUrl, logFn);
32778
+ if (session.keepaliveInterval) {
32779
+ clearInterval(session.keepaliveInterval);
32780
+ session.keepaliveInterval = null;
32781
+ }
32782
+ if (session.ws) {
32783
+ const w = session.ws;
32784
+ session.ws = null;
32785
+ safeCloseWebSocket(w);
32786
+ }
32787
+ }
32788
+
32789
+ // src/auth/pending/pending-auth-on-message.ts
32790
+ function createPendingAuthOnMessage(params) {
32791
+ const { session, onAuth } = params;
32792
+ return (data) => {
32793
+ const msg = data;
32794
+ if (msg.type !== "auth_token" || typeof msg.token !== "string") return;
32795
+ session.resolved = true;
32796
+ const workspaceId = typeof msg.workspaceId === "string" ? msg.workspaceId : "";
32797
+ const refreshToken = typeof msg.refreshToken === "string" ? msg.refreshToken : void 0;
32798
+ const result = { workspaceId, token: msg.token, refreshToken };
32799
+ session.resolveAuth(result);
32800
+ onAuth(result);
32801
+ cleanupPendingAuthSession(session);
32802
+ };
32803
+ }
32804
+
32805
+ // src/auth/pending/pending-auth-on-open.ts
32806
+ function createPendingAuthOnOpen(params) {
32807
+ const { session, apiUrl, initialWorkspaceId, preferredBridgeName, log: log2 } = params;
32808
+ let pendingHbSeq = -1;
32809
+ return () => {
32810
+ clearReconnectQuietOnSuccessfulConnection(
32811
+ session.signInQuiet,
32812
+ log2,
32813
+ "[Bridge service] Sign-in connection restored."
32814
+ );
32815
+ session.reconnectAttempt = 0;
32816
+ pendingHbSeq = -1;
32817
+ sendWsMessage(session.ws, { type: "identify", role: "cli", cliVersion: CLI_VERSION });
32818
+ session.keepaliveInterval = setInterval(() => {
32819
+ if (session.resolved || !session.ws || session.ws.readyState !== 1) return;
32820
+ pendingHbSeq = pendingHbSeq >= BRIDGE_HEARTBEAT_SEQ_MAX ? 0 : pendingHbSeq + 1;
32821
+ const hb = { t: "h", s: pendingHbSeq };
32822
+ sendWsMessage(session.ws, hb);
32823
+ }, PENDING_KEEPALIVE_MS);
32824
+ if (session.browserFallback) {
32825
+ clearTimeout(session.browserFallback);
32826
+ session.browserFallback = null;
32827
+ }
32828
+ if (!session.hasOpenedBrowser) {
32829
+ session.hasOpenedBrowser = true;
32830
+ void openBrowser(session.connectionId, initialWorkspaceId, preferredBridgeName, apiUrl, log2);
32831
+ }
32832
+ };
32833
+ }
32834
+
32835
+ // src/auth/pending/connect-pending-auth.ts
32836
+ function connectPendingAuth(params) {
32837
+ const { session, apiUrl, initialWorkspaceId, preferredBridgeName, onAuth, log: log2 } = params;
32838
+ const connect = () => {
32839
+ session.ws = createWsBridge({
32840
+ url: buildPendingBridgeUrl(apiUrl, session.connectionId),
32841
+ deferInboundMessages: false,
32842
+ onOpen: createPendingAuthOnOpen({
32843
+ session,
32844
+ apiUrl,
32845
+ initialWorkspaceId,
32846
+ preferredBridgeName,
32847
+ log: log2
32848
+ }),
32849
+ onClose: createPendingAuthOnClose({ session, log: log2, connect }),
32850
+ onError: (err) => logCliWebSocketError(log2, "[Bridge service]", err, "while waiting for sign-in"),
32851
+ onMessage: createPendingAuthOnMessage({ session, onAuth })
32852
+ });
32853
+ };
32854
+ connect();
32855
+ }
32856
+
32857
+ // src/auth/pending/run-pending-auth.ts
32858
+ function runPendingAuth(options) {
32859
+ const { apiUrl, initialWorkspaceId, preferredBridgeName, onAuth } = options;
32860
+ const logFn = options.log ?? log;
32861
+ const session = createPendingAuthSession();
32862
+ session.browserFallback = setTimeout(() => {
32863
+ session.browserFallback = null;
32864
+ if (!session.hasOpenedBrowser) {
32865
+ session.hasOpenedBrowser = true;
32866
+ void openBrowser(session.connectionId, initialWorkspaceId, preferredBridgeName, apiUrl, logFn);
32811
32867
  }
32812
32868
  }, BROWSER_OPEN_FALLBACK_MS);
32813
- connect();
32869
+ connectPendingAuth({
32870
+ session,
32871
+ apiUrl,
32872
+ initialWorkspaceId,
32873
+ preferredBridgeName,
32874
+ onAuth,
32875
+ log: logFn
32876
+ });
32814
32877
  return {
32815
32878
  close: () => {
32816
- if (!resolved) resolveAuth(null);
32817
- cleanup();
32879
+ if (!session.resolved) session.resolveAuth(null);
32880
+ cleanupPendingAuthSession(session);
32818
32881
  },
32819
- authPromise
32882
+ authPromise: session.authPromise
32820
32883
  };
32821
32884
  }
32822
32885
 
@@ -54266,32 +54329,42 @@ async function openE2eCertificateImportUrl({
54266
54329
  }
54267
54330
  }
54268
54331
 
54269
- // src/run-bridge-connected.ts
54332
+ // src/run-bridge-e2ee-key-command.ts
54333
+ function installConnectedBridgeE2eeKeyCommand(params) {
54334
+ let openingCertificate = false;
54335
+ return installE2eCertificateKeyCommand({
54336
+ log: params.log,
54337
+ onInterrupt: params.onInterrupt,
54338
+ onOpenCertificate: () => {
54339
+ if (openingCertificate) return;
54340
+ openingCertificate = true;
54341
+ void openE2eCertificateImportUrl({
54342
+ apiUrl: params.apiUrl,
54343
+ workspaceId: params.workspaceId,
54344
+ certificate: params.e2eCertificate,
54345
+ log: params.log
54346
+ }).finally(() => {
54347
+ openingCertificate = false;
54348
+ });
54349
+ }
54350
+ });
54351
+ }
54352
+
54353
+ // src/run-bridge-connected-signals.ts
54354
+ init_log();
54270
54355
  init_cli_process_interrupt();
54271
- init_cli_database();
54272
- async function runConnectedBridge(options, restartWithoutAuth) {
54273
- const {
54274
- apiUrl,
54275
- workspaceId,
54276
- authToken,
54277
- refreshToken,
54278
- justAuthenticated,
54279
- worktreesRootPath,
54280
- e2eCertificate
54281
- } = options;
54282
- const firehoseServerUrl = options.firehoseServerUrl ?? options.proxyServerUrl;
54283
- let cleanupKeyCommand;
54284
- let bridgeClose = null;
54356
+ function installConnectedBridgeSignals(params) {
54285
54357
  let shutdownStarted = false;
54286
54358
  const onSignal = (kind) => {
54287
54359
  requestCliImmediateShutdown();
54288
54360
  abortActiveGitChildProcesses();
54289
- cleanupKeyCommand?.();
54361
+ params.cleanupKeyCommand();
54290
54362
  if (shutdownStarted) return;
54291
54363
  shutdownStarted = true;
54292
54364
  logImmediate(
54293
54365
  kind === "interrupt" ? "Keyboard interrupt (Ctrl+C) \u2014 stopping\u2026" : "Stop requested \u2014 shutting down\u2026"
54294
54366
  );
54367
+ const bridgeClose = params.getBridgeClose();
54295
54368
  if (bridgeClose) {
54296
54369
  void bridgeClose().finally(() => process.exit(0));
54297
54370
  return;
@@ -54302,9 +54375,56 @@ async function runConnectedBridge(options, restartWithoutAuth) {
54302
54375
  const onSigTerm = () => onSignal("stop");
54303
54376
  process.on("SIGINT", onSigInt);
54304
54377
  process.on("SIGTERM", onSigTerm);
54305
- let handle;
54378
+ return {
54379
+ onSigInt,
54380
+ remove: () => {
54381
+ process.off("SIGINT", onSigInt);
54382
+ process.off("SIGTERM", onSigTerm);
54383
+ }
54384
+ };
54385
+ }
54386
+
54387
+ // src/run-bridge-on-auth-invalid.ts
54388
+ init_cli_process_interrupt();
54389
+ async function closeBridgeForReauth(params) {
54390
+ params.log("[Bridge service] Access token invalid or revoked; re-authenticating\u2026");
54391
+ clearConfigForApi(params.apiUrl);
54392
+ await params.close();
54393
+ params.removeSignalHandlers();
54394
+ await clearCliImmediateShutdown();
54395
+ params.endConnectedSession();
54396
+ }
54397
+
54398
+ // src/run-bridge-connected.ts
54399
+ init_cli_database();
54400
+ async function runConnectedBridge(options, restartWithoutAuth) {
54401
+ const {
54402
+ apiUrl,
54403
+ workspaceId,
54404
+ authToken,
54405
+ refreshToken,
54406
+ justAuthenticated,
54407
+ worktreesRootPath,
54408
+ e2eCertificate
54409
+ } = options;
54410
+ const firehoseServerUrl = options.firehoseServerUrl ?? options.proxyServerUrl;
54411
+ let cleanupKeyCommand;
54412
+ let bridgeClose = null;
54413
+ let endConnectedSession;
54414
+ const sessionDone = new Promise((resolve37) => {
54415
+ endConnectedSession = resolve37;
54416
+ });
54417
+ let resolveHandleReady;
54418
+ const handleReady = new Promise((resolve37) => {
54419
+ resolveHandleReady = resolve37;
54420
+ });
54421
+ const handleRef = { current: null };
54422
+ const signals = installConnectedBridgeSignals({
54423
+ getBridgeClose: () => bridgeClose,
54424
+ cleanupKeyCommand: () => cleanupKeyCommand?.()
54425
+ });
54306
54426
  try {
54307
- handle = await createBridgeConnection({
54427
+ const handle = await createBridgeConnection({
54308
54428
  apiUrl,
54309
54429
  workspaceId,
54310
54430
  authToken,
@@ -54323,41 +54443,40 @@ async function runConnectedBridge(options, restartWithoutAuth) {
54323
54443
  },
54324
54444
  onAuthInvalid: () => {
54325
54445
  cleanupKeyCommand?.();
54326
- log("[Bridge service] Access token invalid or revoked; re-authenticating\u2026");
54327
- clearConfigForApi(apiUrl);
54328
- void handle.close().then(() => {
54329
- void restartWithoutAuth({ apiUrl, firehoseServerUrl, worktreesRootPath, e2eCertificate });
54330
- });
54446
+ void (async () => {
54447
+ await handleReady;
54448
+ await closeBridgeForReauth({
54449
+ apiUrl,
54450
+ log,
54451
+ close: async () => {
54452
+ if (handleRef.current) await handleRef.current.close();
54453
+ },
54454
+ removeSignalHandlers: signals.remove,
54455
+ endConnectedSession
54456
+ });
54457
+ })();
54331
54458
  }
54332
54459
  });
54333
- } catch (e) {
54334
- process.off("SIGINT", onSigInt);
54335
- process.off("SIGTERM", onSigTerm);
54336
- if (e instanceof CliSqliteInterrupted) {
54337
- process.exit(0);
54460
+ handleRef.current = handle;
54461
+ bridgeClose = () => handle.close();
54462
+ if (e2eCertificate) {
54463
+ cleanupKeyCommand = installConnectedBridgeE2eeKeyCommand({
54464
+ apiUrl,
54465
+ workspaceId,
54466
+ e2eCertificate,
54467
+ log,
54468
+ onInterrupt: signals.onSigInt
54469
+ });
54338
54470
  }
54471
+ } catch (e) {
54472
+ signals.remove();
54473
+ if (e instanceof CliSqliteInterrupted) process.exit(0);
54339
54474
  throw e;
54475
+ } finally {
54476
+ resolveHandleReady();
54340
54477
  }
54341
- bridgeClose = () => handle.close();
54342
- if (e2eCertificate) {
54343
- let openingCertificate = false;
54344
- cleanupKeyCommand = installE2eCertificateKeyCommand({
54345
- log,
54346
- onInterrupt: onSigInt,
54347
- onOpenCertificate: () => {
54348
- if (openingCertificate) return;
54349
- openingCertificate = true;
54350
- void openE2eCertificateImportUrl({
54351
- apiUrl,
54352
- workspaceId,
54353
- certificate: e2eCertificate,
54354
- log
54355
- }).finally(() => {
54356
- openingCertificate = false;
54357
- });
54358
- }
54359
- });
54360
- }
54478
+ await sessionDone;
54479
+ await restartWithoutAuth({ apiUrl, firehoseServerUrl, worktreesRootPath, e2eCertificate });
54361
54480
  }
54362
54481
 
54363
54482
  // src/run-bridge.ts
@@ -54401,12 +54520,16 @@ async function runBridge(options) {
54401
54520
  process.off("SIGINT", onSigInt);
54402
54521
  process.off("SIGTERM", onSigTerm);
54403
54522
  handle.close();
54404
- if (!auth) return;
54523
+ if (!auth) {
54524
+ log("Sign-in ended before a token was received.");
54525
+ return;
54526
+ }
54405
54527
  writeConfigForApi(apiUrl, {
54406
54528
  workspaceId: auth.workspaceId,
54407
54529
  token: auth.token,
54408
54530
  refreshToken: auth.refreshToken
54409
54531
  });
54532
+ log("Link established. Connecting with the new token\u2026");
54410
54533
  await runBridge({
54411
54534
  apiUrl,
54412
54535
  workspaceId: auth.workspaceId,