@buildautomaton/cli 0.1.27 → 0.1.28

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/index.js CHANGED
@@ -23896,7 +23896,13 @@ function installBridgeProcessResilience() {
23896
23896
  }
23897
23897
 
23898
23898
  // src/cli-version.ts
23899
- var CLI_VERSION = "0.1.27".length > 0 ? "0.1.27" : "0.0.0-dev";
23899
+ var CLI_VERSION = "0.1.28".length > 0 ? "0.1.28" : "0.0.0-dev";
23900
+
23901
+ // src/connection/heartbeat/constants.ts
23902
+ var BRIDGE_APP_HEARTBEAT_INTERVAL_MS = 1e4;
23903
+ var BRIDGE_HEARTBEAT_SEQ_MAX = 2147483646;
23904
+ var BRIDGE_HEARTBEAT_MISSED_ACKS_BEFORE_RECONNECT = 4;
23905
+ var BRIDGE_HEARTBEAT_RTT_SAMPLE_MAX = 5;
23900
23906
 
23901
23907
  // ../../node_modules/.pnpm/open@10.2.0/node_modules/open/index.js
23902
23908
  import process7 from "node:process";
@@ -24876,14 +24882,18 @@ function runPendingAuth(options) {
24876
24882
  }
24877
24883
  function connect() {
24878
24884
  const url2 = buildPendingBridgeUrl(apiUrl, connectionId);
24885
+ let pendingHbSeq = -1;
24879
24886
  ws = createWsBridge({
24880
24887
  url: url2,
24881
24888
  onOpen: () => {
24882
24889
  clearQuietOnOpen();
24890
+ pendingHbSeq = -1;
24883
24891
  sendWsMessage(ws, { type: "identify", role: "cli", cliVersion: CLI_VERSION });
24884
24892
  keepaliveInterval = setInterval(() => {
24885
24893
  if (resolved || !ws || ws.readyState !== 1) return;
24886
- sendWsMessage(ws, { type: "ping", timestamp: Date.now() });
24894
+ pendingHbSeq = pendingHbSeq >= BRIDGE_HEARTBEAT_SEQ_MAX ? 0 : pendingHbSeq + 1;
24895
+ const hb = { t: "h", s: pendingHbSeq };
24896
+ sendWsMessage(ws, hb);
24887
24897
  }, PENDING_KEEPALIVE_MS);
24888
24898
  if (browserFallback) {
24889
24899
  clearTimeout(browserFallback);
@@ -34654,6 +34664,7 @@ function reportGitRepos(getWs, log2) {
34654
34664
  var API_TO_BRIDGE_MESSAGE_TYPES = [
34655
34665
  "auth_token",
34656
34666
  "bridge_identified",
34667
+ "ha",
34657
34668
  "dev_servers_config",
34658
34669
  "server_control",
34659
34670
  "agent_config",
@@ -34682,6 +34693,10 @@ function parseApiToBridgeMessage(data, log2) {
34682
34693
  }
34683
34694
  return null;
34684
34695
  }
34696
+ if (t === "ha") {
34697
+ const s = data.s;
34698
+ if (typeof s !== "number" || !Number.isFinite(s)) return null;
34699
+ }
34685
34700
  return data;
34686
34701
  }
34687
34702
 
@@ -34722,6 +34737,13 @@ var handleBridgeIdentified = (msg, deps) => {
34722
34737
  });
34723
34738
  };
34724
34739
 
34740
+ // src/connection/heartbeat/ack.ts
34741
+ var handleBridgeHeartbeatAck = (msg, deps) => {
34742
+ const raw = msg.s;
34743
+ if (typeof raw !== "number" || !Number.isFinite(raw)) return;
34744
+ deps.onBridgeHeartbeatAck?.(Math.trunc(raw));
34745
+ };
34746
+
34725
34747
  // src/agents/acp/from-bridge/handle-bridge-agent-config.ts
34726
34748
  function handleBridgeAgentConfig(msg, { acpManager }) {
34727
34749
  if (!Array.isArray(msg.agents) || msg.agents.length === 0) return;
@@ -35891,6 +35913,9 @@ function dispatchBridgeMessage(msg, deps) {
35891
35913
  case "bridge_identified":
35892
35914
  handleBridgeIdentified(msg, deps);
35893
35915
  break;
35916
+ case "ha":
35917
+ handleBridgeHeartbeatAck(msg, deps);
35918
+ break;
35894
35919
  case "dev_servers_config":
35895
35920
  handleDevServersConfig(msg, deps);
35896
35921
  break;
@@ -35946,9 +35971,17 @@ function dispatchBridgeMessage(msg, deps) {
35946
35971
  }
35947
35972
 
35948
35973
  // src/routing/handle-bridge-message.ts
35974
+ function normalizeInboundBridgeWebSocketJson(data) {
35975
+ if (data === null || typeof data !== "object" || Array.isArray(data)) return data;
35976
+ const o = data;
35977
+ if (o.t === "ha" && typeof o.s === "number" && Number.isFinite(o.s)) {
35978
+ return { type: "ha", s: Math.trunc(o.s) };
35979
+ }
35980
+ return data;
35981
+ }
35949
35982
  function handleBridgeMessage(data, deps) {
35950
35983
  if (!deps.getWs()) return;
35951
- const msg = parseApiToBridgeMessage(data, deps.log);
35984
+ const msg = parseApiToBridgeMessage(normalizeInboundBridgeWebSocketJson(data), deps.log);
35952
35985
  if (!msg) return;
35953
35986
  setImmediate(() => {
35954
35987
  dispatchBridgeMessage(msg, deps);
@@ -35996,7 +36029,8 @@ function createMainBridgeWebSocketLifecycle(params) {
35996
36029
  persistTokens,
35997
36030
  onAuthInvalid,
35998
36031
  e2ee,
35999
- identifyReportedPaths
36032
+ identifyReportedPaths,
36033
+ bridgeHeartbeat
36000
36034
  } = params;
36001
36035
  let authRefreshInFlight = false;
36002
36036
  function handleOpen() {
@@ -36028,6 +36062,7 @@ function createMainBridgeWebSocketLifecycle(params) {
36028
36062
  }
36029
36063
  }
36030
36064
  function handleClose(code, reason) {
36065
+ bridgeHeartbeat?.stop();
36031
36066
  try {
36032
36067
  const was = state.currentWs;
36033
36068
  state.currentWs = null;
@@ -36062,6 +36097,7 @@ function createMainBridgeWebSocketLifecycle(params) {
36062
36097
  } catch {
36063
36098
  }
36064
36099
  }
36100
+ bridgeHeartbeat?.stop();
36065
36101
  const prev = state.currentWs;
36066
36102
  if (prev) {
36067
36103
  prev.removeAllListeners();
@@ -36232,6 +36268,92 @@ function createCliE2eeRuntime(key) {
36232
36268
  };
36233
36269
  }
36234
36270
 
36271
+ // src/connection/heartbeat/controller.ts
36272
+ function meanRttMs(samples) {
36273
+ if (samples.length === 0) return void 0;
36274
+ let sum = 0;
36275
+ for (const x of samples) sum += x;
36276
+ return sum / samples.length;
36277
+ }
36278
+ function createBridgeHeartbeatController(params) {
36279
+ const { getWs, log: log2 } = params;
36280
+ let interval = null;
36281
+ let seqCursor = -1;
36282
+ let awaitingSeq = null;
36283
+ let sentAtMs = 0;
36284
+ let missed = 0;
36285
+ const rttSamples = [];
36286
+ function clearTimer() {
36287
+ if (interval != null) {
36288
+ clearInterval(interval);
36289
+ interval = null;
36290
+ }
36291
+ }
36292
+ function nextSeq() {
36293
+ seqCursor = seqCursor >= BRIDGE_HEARTBEAT_SEQ_MAX ? 0 : seqCursor + 1;
36294
+ return seqCursor;
36295
+ }
36296
+ function tick() {
36297
+ const ws = getWs();
36298
+ if (!ws || ws.readyState !== wrapper_default.OPEN) return;
36299
+ if (awaitingSeq !== null) {
36300
+ missed++;
36301
+ if (missed >= BRIDGE_HEARTBEAT_MISSED_ACKS_BEFORE_RECONNECT) {
36302
+ try {
36303
+ log2("[Bridge service] Heartbeat missed repeatedly; reconnecting\u2026");
36304
+ } catch {
36305
+ }
36306
+ clearTimer();
36307
+ awaitingSeq = null;
36308
+ missed = 0;
36309
+ rttSamples.length = 0;
36310
+ safeCloseWebSocket(ws);
36311
+ return;
36312
+ }
36313
+ }
36314
+ const seq = nextSeq();
36315
+ const mean = meanRttMs(rttSamples);
36316
+ const payload = mean !== void 0 && Number.isFinite(mean) ? { t: "h", s: seq, m: Math.round(mean) } : { t: "h", s: seq };
36317
+ sendWsMessage(ws, payload);
36318
+ awaitingSeq = seq;
36319
+ sentAtMs = Date.now();
36320
+ }
36321
+ return {
36322
+ start() {
36323
+ clearTimer();
36324
+ awaitingSeq = null;
36325
+ missed = 0;
36326
+ seqCursor = -1;
36327
+ rttSamples.length = 0;
36328
+ interval = setInterval(tick, BRIDGE_APP_HEARTBEAT_INTERVAL_MS);
36329
+ },
36330
+ stop() {
36331
+ clearTimer();
36332
+ awaitingSeq = null;
36333
+ missed = 0;
36334
+ rttSamples.length = 0;
36335
+ },
36336
+ onAck(seq) {
36337
+ if (awaitingSeq === null) return;
36338
+ if (!Number.isFinite(seq)) return;
36339
+ const ack = Math.trunc(seq);
36340
+ const pending = awaitingSeq;
36341
+ if (ack < pending) return;
36342
+ if (ack === pending) {
36343
+ const rtt = Date.now() - sentAtMs;
36344
+ if (Number.isFinite(rtt) && rtt >= 0 && rtt < 6e5) {
36345
+ rttSamples.push(rtt);
36346
+ if (rttSamples.length > BRIDGE_HEARTBEAT_RTT_SAMPLE_MAX) {
36347
+ rttSamples.splice(0, rttSamples.length - BRIDGE_HEARTBEAT_RTT_SAMPLE_MAX);
36348
+ }
36349
+ }
36350
+ }
36351
+ awaitingSeq = null;
36352
+ missed = 0;
36353
+ }
36354
+ };
36355
+ }
36356
+
36235
36357
  // src/connection/create-bridge-connection.ts
36236
36358
  async function createBridgeConnection(options) {
36237
36359
  const { apiUrl, workspaceId, justAuthenticated, onAuthInvalid, persistTokens } = options;
@@ -36271,13 +36393,18 @@ async function createBridgeConnection(options) {
36271
36393
  }
36272
36394
  const e2ee = options.e2eCertificate ? createCliE2eeRuntime(options.e2eCertificate) : void 0;
36273
36395
  const devServerManager = new DevServerManager({ getWs, log: logFn, getBridgeRoot, e2ee });
36274
- const onBridgeIdentified = createOnBridgeIdentified({
36396
+ const bridgeHeartbeat = createBridgeHeartbeatController({ getWs, log: logFn });
36397
+ const baseOnBridgeIdentified = createOnBridgeIdentified({
36275
36398
  devServerManager,
36276
36399
  firehoseServerUrl,
36277
36400
  workspaceId,
36278
36401
  state,
36279
36402
  logFn
36280
36403
  });
36404
+ const onBridgeIdentified = (msg) => {
36405
+ baseOnBridgeIdentified(msg);
36406
+ bridgeHeartbeat.start();
36407
+ };
36281
36408
  const sendLocalSkillsReport = createSendLocalSkillsReport(getWs, logFn);
36282
36409
  const reportAutoDetectedAgents = createReportAutoDetectedAgents(getWs, logFn);
36283
36410
  const messageDeps = {
@@ -36286,6 +36413,9 @@ async function createBridgeConnection(options) {
36286
36413
  acpManager,
36287
36414
  sessionWorktreeManager,
36288
36415
  onBridgeIdentified,
36416
+ onBridgeHeartbeatAck: (seq) => {
36417
+ bridgeHeartbeat.onAck(seq);
36418
+ },
36289
36419
  sendLocalSkillsReport,
36290
36420
  reportAutoDetectedAgents,
36291
36421
  devServerManager,
@@ -36309,13 +36439,15 @@ async function createBridgeConnection(options) {
36309
36439
  persistTokens,
36310
36440
  onAuthInvalid,
36311
36441
  e2ee,
36312
- identifyReportedPaths
36442
+ identifyReportedPaths,
36443
+ bridgeHeartbeat
36313
36444
  });
36314
36445
  connect();
36315
36446
  const stopFileIndexWatcher = startFileIndexWatcher(getBridgeRoot());
36316
36447
  return {
36317
36448
  close: async () => {
36318
36449
  stopFileIndexWatcher();
36450
+ bridgeHeartbeat.stop();
36319
36451
  await closeBridgeConnection(state, acpManager, devServerManager, logFn);
36320
36452
  }
36321
36453
  };