@buildautomaton/cli 0.1.22 → 0.1.24

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
@@ -25064,7 +25064,7 @@ var {
25064
25064
  } = import_index.default;
25065
25065
 
25066
25066
  // src/cli-version.ts
25067
- var CLI_VERSION = "0.1.22".length > 0 ? "0.1.22" : "0.0.0-dev";
25067
+ var CLI_VERSION = "0.1.24".length > 0 ? "0.1.24" : "0.0.0-dev";
25068
25068
 
25069
25069
  // src/cli/defaults.ts
25070
25070
  var DEFAULT_API_URL = process.env.BUILDAUTOMATON_API_URL ?? "https://api.buildautomaton.com";
@@ -25074,6 +25074,18 @@ var DEFAULT_FIREHOSE_URL = "https://buildautomaton-firehose.fly.dev";
25074
25074
  import * as fs34 from "node:fs";
25075
25075
  import * as path35 from "node:path";
25076
25076
 
25077
+ // src/cli-log-level.ts
25078
+ var verbosity = "info";
25079
+ function setCliLogVerbosity(level) {
25080
+ verbosity = level;
25081
+ }
25082
+ function getCliLogVerbosity() {
25083
+ return verbosity;
25084
+ }
25085
+ function isCliTrace() {
25086
+ return verbosity === "trace";
25087
+ }
25088
+
25077
25089
  // src/config.ts
25078
25090
  import fs from "node:fs";
25079
25091
  import path from "node:path";
@@ -25442,15 +25454,21 @@ function getBridgeRoot() {
25442
25454
  }
25443
25455
 
25444
25456
  // src/log.ts
25445
- function log(line) {
25457
+ function timestampPrefix() {
25446
25458
  const time3 = (/* @__PURE__ */ new Date()).toISOString().slice(11, 19);
25447
- console.log(`[${time3}] ${line}`);
25459
+ return `[${time3}]`;
25460
+ }
25461
+ function log(line) {
25462
+ console.log(`${timestampPrefix()} ${line}`);
25448
25463
  }
25449
25464
  function logImmediate(line) {
25450
- const time3 = (/* @__PURE__ */ new Date()).toISOString().slice(11, 19);
25451
- process.stdout.write(`[${time3}] ${line}
25465
+ process.stdout.write(`${timestampPrefix()} ${line}
25452
25466
  `);
25453
25467
  }
25468
+ function logTrace(line) {
25469
+ if (getCliLogVerbosity() !== "trace") return;
25470
+ console.log(`${timestampPrefix()} [trace] ${line}`);
25471
+ }
25454
25472
 
25455
25473
  // src/process-bridge-resilience.ts
25456
25474
  var installed = false;
@@ -25485,7 +25503,7 @@ function applyCliOutboundNetworkPreferences() {
25485
25503
  }
25486
25504
  }
25487
25505
 
25488
- // src/bridge/connection/cli-ws-client.ts
25506
+ // src/connection/cli-ws-client.ts
25489
25507
  import https from "node:https";
25490
25508
  var CLI_WEBSOCKET_CLIENT_PING_MS = 25e3;
25491
25509
  function attachWebSocketClientPing(ws, intervalMs) {
@@ -25545,7 +25563,7 @@ function safeSendWebSocketBinary(ws, data) {
25545
25563
  }
25546
25564
  }
25547
25565
 
25548
- // src/bridge/connection/create-ws-bridge.ts
25566
+ // src/connection/create-ws-bridge.ts
25549
25567
  var BRIDGE_AUTH_ERROR_HEADER = "x-bridge-auth-error";
25550
25568
  var BRIDGE_AUTH_ERROR_TOKEN_INVALID = "token_invalid";
25551
25569
  function createWsBridge(options) {
@@ -26156,29 +26174,22 @@ async function openBrowser(connectionId, initialWorkspaceId, preferredBridgeName
26156
26174
  }
26157
26175
  }
26158
26176
 
26159
- // src/bridge/connection/reconnect/constants.ts
26160
- var RECONNECT_FIRST_MS = 100;
26161
- var RECONNECT_MAX_MS = 3e4;
26177
+ // src/connection/reconnect/constants.ts
26162
26178
  var RECONNECT_QUIET_MS = 2e3;
26163
- function reconnectDelayMs(attemptBeforeIncrement) {
26164
- return Math.min(RECONNECT_FIRST_MS * 2 ** attemptBeforeIncrement, RECONNECT_MAX_MS);
26179
+ var PENDING_AUTH_RECONNECT_FIRST_MS = 100;
26180
+ var PENDING_AUTH_RECONNECT_MAX_MS = 3e4;
26181
+ function pendingAuthReconnectDelayMs(attemptBeforeIncrement) {
26182
+ return Math.min(PENDING_AUTH_RECONNECT_FIRST_MS * 2 ** attemptBeforeIncrement, PENDING_AUTH_RECONNECT_MAX_MS);
26165
26183
  }
26166
26184
 
26167
- // src/bridge/connection/reconnect/format-reconnect-delay-for-log.ts
26185
+ // src/connection/reconnect/format-reconnect-delay-for-log.ts
26168
26186
  function formatReconnectDelayForLog(delayMs) {
26169
26187
  if (delayMs < 1e3) return `${delayMs}ms`;
26170
26188
  const s = delayMs / 1e3;
26171
26189
  return Number.isInteger(s) ? `${s}s` : `${s.toFixed(1)}s`;
26172
26190
  }
26173
26191
 
26174
- // src/bridge/connection/reconnect/log-next-reconnect-attempt.ts
26175
- function logNextReconnectAttempt(log2, serviceLabel, quiet, delayMs, attempt) {
26176
- if (!quiet.verboseLogs) return;
26177
- const delayLabel = formatReconnectDelayForLog(delayMs);
26178
- log2(`${serviceLabel} Next connection attempt in ${delayLabel} (attempt ${attempt}).`);
26179
- }
26180
-
26181
- // src/bridge/connection/ws-close-diagnostics.ts
26192
+ // src/connection/ws-close-diagnostics.ts
26182
26193
  function describeWebSocketCloseCode(code) {
26183
26194
  const known = {
26184
26195
  1e3: "normal closure",
@@ -26209,7 +26220,7 @@ function formatWebSocketClose(label, code, reason, extra) {
26209
26220
  return `${label} Disconnected: code=${code} (${describeWebSocketCloseCode(code)})${reasonPart}${extraPart}`;
26210
26221
  }
26211
26222
 
26212
- // src/bridge/connection/reconnect/reconnect-quiet-slot.ts
26223
+ // src/connection/reconnect/reconnect-quiet-slot.ts
26213
26224
  function createEmptyReconnectQuietSlot() {
26214
26225
  return { timer: null, verboseLogs: false, pendingCloseLog: null };
26215
26226
  }
@@ -26219,6 +26230,11 @@ function clearReconnectQuietTimer(quiet) {
26219
26230
  quiet.timer = null;
26220
26231
  }
26221
26232
  }
26233
+ function abandonReconnectQuietWindow(quiet) {
26234
+ clearReconnectQuietTimer(quiet);
26235
+ quiet.pendingCloseLog = null;
26236
+ quiet.verboseLogs = false;
26237
+ }
26222
26238
  function clearReconnectQuietOnSuccessfulConnection(quiet, log2, reconnectedMessage) {
26223
26239
  clearReconnectQuietTimer(quiet);
26224
26240
  quiet.pendingCloseLog = null;
@@ -26239,12 +26255,16 @@ function beginDeferredDisconnectForReconnect(options) {
26239
26255
  shutdownDetail,
26240
26256
  reconnectingDetail,
26241
26257
  quietMs = RECONNECT_QUIET_MS,
26242
- shouldAbortQuietWindow
26258
+ shouldAbortQuietWindow,
26259
+ silentWhileReconnect = false
26243
26260
  } = options;
26244
26261
  if (!willReconnect) {
26245
26262
  log2(formatWebSocketClose(serviceLabel, code, reason, shutdownDetail));
26246
26263
  return;
26247
26264
  }
26265
+ if (silentWhileReconnect) {
26266
+ return;
26267
+ }
26248
26268
  quiet.pendingCloseLog = { code, reason };
26249
26269
  if (quiet.timer == null) {
26250
26270
  quiet.timer = setTimeout(() => {
@@ -26261,18 +26281,178 @@ function beginDeferredDisconnectForReconnect(options) {
26261
26281
  }
26262
26282
  }
26263
26283
 
26264
- // src/bridge/connection/reconnect/bridge-main-reconnect.ts
26265
- function beginMainBridgeDeferredDisconnect(state, code, reason, log2, willReconnect) {
26284
+ // src/connection/reconnect/reconnect-outage-plan.ts
26285
+ function createEmptyReconnectOutageTracker() {
26286
+ return {
26287
+ startedAt: null,
26288
+ totalFailures: 0,
26289
+ lastLoggedTierIndex: -1,
26290
+ originalCloseCode: null,
26291
+ originalCloseReason: null
26292
+ };
26293
+ }
26294
+ function resetReconnectOutageTracker(tracker) {
26295
+ tracker.startedAt = null;
26296
+ tracker.totalFailures = 0;
26297
+ tracker.lastLoggedTierIndex = -1;
26298
+ tracker.originalCloseCode = null;
26299
+ tracker.originalCloseReason = null;
26300
+ }
26301
+ function reconnectTierIndexForOutageElapsedMs(elapsedMs) {
26302
+ if (elapsedMs < 6e4) return 0;
26303
+ if (elapsedMs < 36e4) return 1;
26304
+ if (elapsedMs < 12e5) return 2;
26305
+ return 3;
26306
+ }
26307
+ function reconnectDelayMsForOutageElapsedMs(elapsedMs) {
26308
+ if (elapsedMs < 6e4) return 3e3;
26309
+ if (elapsedMs < 36e4) return 1e4;
26310
+ if (elapsedMs < 12e5) return 2e4;
26311
+ return 6e4;
26312
+ }
26313
+ function disconnectDetail(code, reason) {
26314
+ const r = reason.trim();
26315
+ const reasonPart = r ? ` reason="${r}"` : "";
26316
+ return `code=${code} (${describeWebSocketCloseCode(code)})${reasonPart}`;
26317
+ }
26318
+ function formatTierPromotionLogLine(parts) {
26319
+ const detail = disconnectDetail(parts.code, parts.reason);
26320
+ return `${parts.serviceLabel} ${parts.disconnectLeadIn}: ${detail}. Trying to re-establish a connection in the background (retry attempts so far: ${parts.totalFailures}).`;
26321
+ }
26322
+ function planReconnectAfterFailure(tracker, nowMs, serviceLabel, disconnectLeadIn, closeCode, closeReason) {
26323
+ if (tracker.startedAt == null) {
26324
+ tracker.startedAt = nowMs;
26325
+ }
26326
+ if (closeCode != null && tracker.originalCloseCode == null) {
26327
+ tracker.originalCloseCode = closeCode;
26328
+ tracker.originalCloseReason = closeReason ?? "";
26329
+ }
26330
+ tracker.totalFailures += 1;
26331
+ const elapsed = nowMs - tracker.startedAt;
26332
+ const tier = reconnectTierIndexForOutageElapsedMs(elapsed);
26333
+ const delayMs = reconnectDelayMsForOutageElapsedMs(elapsed);
26334
+ let logLine = null;
26335
+ if (tier > tracker.lastLoggedTierIndex) {
26336
+ tracker.lastLoggedTierIndex = tier;
26337
+ const code = tracker.originalCloseCode ?? closeCode ?? 0;
26338
+ const reason = tracker.originalCloseReason ?? closeReason ?? "";
26339
+ logLine = formatTierPromotionLogLine({
26340
+ serviceLabel,
26341
+ disconnectLeadIn,
26342
+ code,
26343
+ reason,
26344
+ totalFailures: tracker.totalFailures
26345
+ });
26346
+ }
26347
+ return { delayMs, logLine };
26348
+ }
26349
+
26350
+ // src/connection/reconnect/tiered-channel-reconnect.ts
26351
+ var BRIDGE_SERVICE_LABEL = "[Bridge service]";
26352
+ var BRIDGE_DISCONNECT_LEAD_IN = "Bridge connection was disconnected";
26353
+ var PREVIEW_TUNNEL_SERVICE_LABEL = "[Proxy and log service]";
26354
+ var PREVIEW_TUNNEL_DISCONNECT_LEAD_IN = "Preview tunnel connection was disconnected";
26355
+ var SHUTDOWN_DETAIL = "Not reconnecting (shutting down).";
26356
+ var RECONNECTING_DETAIL = "Reconnecting\u2026";
26357
+ function applyTieredReconnectPlanAndLog(tracker, log2, serviceLabel, disconnectLeadIn, closeCode, closeReason) {
26358
+ try {
26359
+ const { delayMs, logLine } = planReconnectAfterFailure(
26360
+ tracker,
26361
+ Date.now(),
26362
+ serviceLabel,
26363
+ disconnectLeadIn,
26364
+ closeCode,
26365
+ closeReason
26366
+ );
26367
+ if (logLine) {
26368
+ try {
26369
+ log2(logLine);
26370
+ } catch {
26371
+ }
26372
+ }
26373
+ return delayMs;
26374
+ } catch {
26375
+ return 3e3;
26376
+ }
26377
+ }
26378
+ function armReconnectDelayTimer(options) {
26379
+ const {
26380
+ delayMs,
26381
+ bumpAttempt,
26382
+ clearTimer,
26383
+ setTimer,
26384
+ shouldAbortBeforeRun,
26385
+ run,
26386
+ reschedule
26387
+ } = options;
26388
+ clearTimer();
26389
+ bumpAttempt();
26390
+ setTimer(
26391
+ setTimeout(() => {
26392
+ setTimer(null);
26393
+ if (shouldAbortBeforeRun()) return;
26394
+ try {
26395
+ run();
26396
+ } catch {
26397
+ try {
26398
+ if (!shouldAbortBeforeRun()) {
26399
+ reschedule();
26400
+ }
26401
+ } catch {
26402
+ }
26403
+ }
26404
+ }, delayMs)
26405
+ );
26406
+ }
26407
+ function beginTieredSilentReconnectDisconnect(options) {
26408
+ const {
26409
+ isClosedByUser,
26410
+ quiet,
26411
+ code,
26412
+ reason,
26413
+ willReconnect,
26414
+ log: log2,
26415
+ serviceLabel,
26416
+ shouldAbortQuietWindow
26417
+ } = options;
26266
26418
  beginDeferredDisconnectForReconnect({
26419
+ isClosedByUser,
26420
+ quiet,
26421
+ code,
26422
+ reason,
26423
+ willReconnect,
26424
+ log: log2,
26425
+ serviceLabel,
26426
+ shutdownDetail: SHUTDOWN_DETAIL,
26427
+ reconnectingDetail: RECONNECTING_DETAIL,
26428
+ shouldAbortQuietWindow,
26429
+ silentWhileReconnect: willReconnect
26430
+ });
26431
+ }
26432
+ function clearTieredReconnectChannelOnOpen(options) {
26433
+ const { quiet, outage, clearLastCloseMeta, log: log2, restoredMessage } = options;
26434
+ const hadOutage = outage.startedAt != null || outage.totalFailures > 0;
26435
+ abandonReconnectQuietWindow(quiet);
26436
+ resetReconnectOutageTracker(outage);
26437
+ clearLastCloseMeta();
26438
+ if (hadOutage) {
26439
+ try {
26440
+ log2(restoredMessage);
26441
+ } catch {
26442
+ }
26443
+ }
26444
+ }
26445
+
26446
+ // src/connection/reconnect/bridge-main-reconnect.ts
26447
+ function beginMainBridgeDeferredDisconnect(state, code, reason, log2, willReconnect) {
26448
+ beginTieredSilentReconnectDisconnect({
26267
26449
  isClosedByUser: () => state.closedByUser,
26268
26450
  quiet: state.mainQuiet,
26269
26451
  code,
26270
26452
  reason,
26271
26453
  willReconnect,
26272
26454
  log: log2,
26273
- serviceLabel: "[Bridge service]",
26274
- shutdownDetail: "Not reconnecting (shutting down).",
26275
- reconnectingDetail: "Reconnecting\u2026",
26455
+ serviceLabel: BRIDGE_SERVICE_LABEL,
26276
26456
  shouldAbortQuietWindow: () => {
26277
26457
  const w = state.currentWs;
26278
26458
  return w != null && w.readyState === wrapper_default.OPEN;
@@ -26280,32 +26460,59 @@ function beginMainBridgeDeferredDisconnect(state, code, reason, log2, willReconn
26280
26460
  });
26281
26461
  }
26282
26462
  function clearMainBridgeReconnectQuietOnOpen(state, log2) {
26283
- clearReconnectQuietOnSuccessfulConnection(state.mainQuiet, log2, "Bridge connection restored.");
26463
+ clearTieredReconnectChannelOnOpen({
26464
+ quiet: state.mainQuiet,
26465
+ outage: state.mainOutage,
26466
+ clearLastCloseMeta: () => {
26467
+ state.lastReconnectCloseMeta = null;
26468
+ },
26469
+ log: log2,
26470
+ restoredMessage: "Bridge connection restored."
26471
+ });
26284
26472
  }
26285
- function scheduleMainBridgeReconnect(state, connect, log2) {
26473
+ function scheduleMainBridgeReconnect(state, connect, log2, closeMeta) {
26286
26474
  if (state.closedByUser || state.currentWs != null) return;
26287
- const delay2 = reconnectDelayMs(state.reconnectAttempt);
26288
- state.reconnectAttempt += 1;
26289
- logNextReconnectAttempt(log2, "[Bridge service]", state.mainQuiet, delay2, state.reconnectAttempt);
26290
- state.reconnectTimeout = setTimeout(() => {
26291
- state.reconnectTimeout = null;
26292
- connect();
26293
- }, delay2);
26475
+ const meta = closeMeta ?? state.lastReconnectCloseMeta ?? void 0;
26476
+ const delay2 = applyTieredReconnectPlanAndLog(
26477
+ state.mainOutage,
26478
+ log2,
26479
+ BRIDGE_SERVICE_LABEL,
26480
+ BRIDGE_DISCONNECT_LEAD_IN,
26481
+ meta?.code,
26482
+ meta?.reason
26483
+ );
26484
+ armReconnectDelayTimer({
26485
+ delayMs: delay2,
26486
+ bumpAttempt: () => {
26487
+ state.reconnectAttempt += 1;
26488
+ },
26489
+ clearTimer: () => {
26490
+ if (state.reconnectTimeout != null) {
26491
+ clearTimeout(state.reconnectTimeout);
26492
+ state.reconnectTimeout = null;
26493
+ }
26494
+ },
26495
+ setTimer: (id) => {
26496
+ state.reconnectTimeout = id;
26497
+ },
26498
+ shouldAbortBeforeRun: () => state.closedByUser || state.currentWs != null,
26499
+ run: connect,
26500
+ reschedule: () => {
26501
+ scheduleMainBridgeReconnect(state, connect, log2);
26502
+ }
26503
+ });
26294
26504
  }
26295
26505
 
26296
- // src/bridge/connection/reconnect/firehose-reconnect.ts
26297
- var PROXY_AND_LOG_SERVICE_LABEL = "[Proxy and log service]";
26506
+ // src/connection/reconnect/firehose-reconnect.ts
26298
26507
  function beginFirehoseDeferredDisconnect(ctx, code, reason, log2) {
26299
- beginDeferredDisconnectForReconnect({
26508
+ beginTieredSilentReconnectDisconnect({
26300
26509
  isClosedByUser: () => ctx.closedByUser,
26301
26510
  quiet: ctx.firehoseQuiet,
26302
26511
  code,
26303
26512
  reason,
26304
26513
  willReconnect: true,
26305
26514
  log: log2,
26306
- serviceLabel: PROXY_AND_LOG_SERVICE_LABEL,
26307
- shutdownDetail: "Not reconnecting (shutting down).",
26308
- reconnectingDetail: "Reconnecting\u2026",
26515
+ serviceLabel: PREVIEW_TUNNEL_SERVICE_LABEL,
26309
26516
  shouldAbortQuietWindow: () => {
26310
26517
  const w = ctx.currentWs;
26311
26518
  if (!w || w.readyState !== wrapper_default.OPEN) return true;
@@ -26314,11 +26521,15 @@ function beginFirehoseDeferredDisconnect(ctx, code, reason, log2) {
26314
26521
  });
26315
26522
  }
26316
26523
  function clearFirehoseReconnectQuietOnOpen(ctx, log2) {
26317
- clearReconnectQuietOnSuccessfulConnection(
26318
- ctx.firehoseQuiet,
26319
- log2,
26320
- "Preview tunnel restored (local HTTP proxy and dev logs)."
26321
- );
26524
+ clearTieredReconnectChannelOnOpen({
26525
+ quiet: ctx.firehoseQuiet,
26526
+ outage: ctx.firehoseOutage,
26527
+ clearLastCloseMeta: () => {
26528
+ ctx.lastFirehoseReconnectCloseMeta = null;
26529
+ },
26530
+ log: log2,
26531
+ restoredMessage: "Preview tunnel restored (local HTTP proxy and dev logs)."
26532
+ });
26322
26533
  }
26323
26534
 
26324
26535
  // src/auth/run-pending-auth.ts
@@ -26414,7 +26625,7 @@ function runPendingAuth(options) {
26414
26625
  }
26415
26626
  if (resolved) return;
26416
26627
  beginDeferredPendingCloseLog(code, reason);
26417
- const delay2 = reconnectDelayMs(reconnectAttempt);
26628
+ const delay2 = pendingAuthReconnectDelayMs(reconnectAttempt);
26418
26629
  reconnectAttempt += 1;
26419
26630
  if (signInQuiet.verboseLogs) {
26420
26631
  const delayLabel = formatReconnectDelayForLog(delay2);
@@ -26459,7 +26670,7 @@ function runPendingAuth(options) {
26459
26670
  };
26460
26671
  }
26461
26672
 
26462
- // src/bridge/connection/close-bridge-connection.ts
26673
+ // src/connection/close-bridge-connection.ts
26463
26674
  async function closeBridgeConnection(state, acpManager, devServerManager, log2) {
26464
26675
  const say = log2 ?? logImmediate;
26465
26676
  say("Cleaning up connections\u2026");
@@ -26974,6 +27185,30 @@ var GitRepoMetaSchema = external_exports.object({
26974
27185
  updatedAt: external_exports.string()
26975
27186
  });
26976
27187
 
27188
+ // ../types/src/claude-code-permission-mode.ts
27189
+ var CLAUDE_CODE_PERMISSION_MODES = [
27190
+ "default",
27191
+ "acceptEdits",
27192
+ "plan",
27193
+ "auto",
27194
+ "dontAsk",
27195
+ "bypassPermissions"
27196
+ ];
27197
+ var MODE_SET = new Set(CLAUDE_CODE_PERMISSION_MODES);
27198
+ function isClaudeCodePermissionMode(value) {
27199
+ return MODE_SET.has(value);
27200
+ }
27201
+
27202
+ // ../types/src/agent-config.ts
27203
+ var AGENT_CONFIG_CLAUDE_PERMISSION_MODE_KEY = "claude_permission_mode";
27204
+ function getClaudePermissionModeFromAgentConfig(config2) {
27205
+ if (!config2) return null;
27206
+ const raw = config2[AGENT_CONFIG_CLAUDE_PERMISSION_MODE_KEY];
27207
+ if (typeof raw !== "string") return null;
27208
+ const t = raw.trim();
27209
+ return isClaudeCodePermissionMode(t) ? t : null;
27210
+ }
27211
+
26977
27212
  // src/git/session-git-queue.ts
26978
27213
  import { execFile as execFile7 } from "node:child_process";
26979
27214
  import { readFile as readFile2, stat as stat2 } from "node:fs/promises";
@@ -31549,10 +31784,28 @@ function gitInstanceFactory(baseDir, options) {
31549
31784
  init_git_response_error();
31550
31785
  var simpleGit = gitInstanceFactory;
31551
31786
 
31787
+ // src/git/cli-simple-git.ts
31788
+ function cliSimpleGit(baseDir) {
31789
+ const git = simpleGit({ baseDir });
31790
+ git.outputHandler((command, stdout, stderr) => {
31791
+ const trace = isCliTrace();
31792
+ const onChunk = (label) => (chunk) => {
31793
+ const text = typeof chunk === "string" ? chunk : chunk.toString("utf8");
31794
+ const line = text.replace(/\s+$/, "");
31795
+ if (trace && line) {
31796
+ logTrace(`[git ${command}] ${label}: ${line}`);
31797
+ }
31798
+ };
31799
+ stdout?.on("data", onChunk("stdout"));
31800
+ stderr?.on("data", onChunk("stderr"));
31801
+ });
31802
+ return git;
31803
+ }
31804
+
31552
31805
  // src/git/remote-origin-url.ts
31553
31806
  async function getRemoteOriginUrl(gitDir) {
31554
31807
  try {
31555
- const git = simpleGit(gitDir);
31808
+ const git = cliSimpleGit(gitDir);
31556
31809
  const remotes = await git.getRemotes(true);
31557
31810
  const list = Array.isArray(remotes) ? remotes : [];
31558
31811
  const origin = list.find((r) => r.name === "origin");
@@ -31566,7 +31819,7 @@ async function getRemoteOriginUrl(gitDir) {
31566
31819
  // src/git/is-git-repo.ts
31567
31820
  async function isGitRepoDirectory(dirPath) {
31568
31821
  try {
31569
- return await simpleGit(dirPath).checkIsRepo();
31822
+ return await cliSimpleGit(dirPath).checkIsRepo();
31570
31823
  } catch {
31571
31824
  return false;
31572
31825
  }
@@ -32259,7 +32512,8 @@ async function createSdkStdioAcpClient(options) {
32259
32512
  onRequest,
32260
32513
  onFileChange,
32261
32514
  killSubprocessAfterCancelMs,
32262
- onAgentSubprocessExit
32515
+ onAgentSubprocessExit,
32516
+ agentConfig
32263
32517
  } = options;
32264
32518
  const isWindows = process.platform === "win32";
32265
32519
  const child = spawn2(command[0], command.slice(1), {
@@ -32374,6 +32628,20 @@ async function createSdkStdioAcpClient(options) {
32374
32628
  });
32375
32629
  const newSessionRes = await connection.newSession({ cwd, mcpServers: [] });
32376
32630
  const sessionId = newSessionRes.sessionId;
32631
+ if (backendAgentType === "claude-code") {
32632
+ const cfg = agentConfig != null && typeof agentConfig === "object" && !Array.isArray(agentConfig) ? agentConfig : null;
32633
+ const desiredMode = getClaudePermissionModeFromAgentConfig(cfg);
32634
+ const modes = newSessionRes.modes;
32635
+ if (desiredMode != null && modes?.availableModes?.length) {
32636
+ const allowed = modes.availableModes.some((m) => m.id === desiredMode);
32637
+ if (allowed && desiredMode !== modes.currentModeId) {
32638
+ try {
32639
+ await connection.setSessionMode({ sessionId, modeId: desiredMode });
32640
+ } catch {
32641
+ }
32642
+ }
32643
+ }
32644
+ }
32377
32645
  settleResolve({
32378
32646
  sessionId,
32379
32647
  async sendPrompt(prompt, _options) {
@@ -32485,10 +32753,7 @@ async function detectLocalAgentPresence() {
32485
32753
  return false;
32486
32754
  }
32487
32755
  }
32488
- function buildClaudeCodeAcpSpawnCommand(base, sessionMode) {
32489
- if (!sessionMode) return [...base];
32490
- const m = sessionMode.trim();
32491
- if (m === "plan") return [...base, "--permission-mode", "plan"];
32756
+ function buildClaudeCodeAcpSpawnCommand(base, _sessionMode) {
32492
32757
  return [...base];
32493
32758
  }
32494
32759
  async function createClaudeCodeAcpClient(options) {
@@ -32951,7 +33216,7 @@ function resolveAgentCommand(preferredAgentType) {
32951
33216
  command,
32952
33217
  label: preferredAgentType,
32953
33218
  createClient: createCursorAcpClient,
32954
- spawnCommandForSession: (sessionMode) => buildCursorAcpSpawnCommand(command, sessionMode)
33219
+ spawnCommandForSession: (sessionMode, _agentConfig) => buildCursorAcpSpawnCommand(command, sessionMode)
32955
33220
  };
32956
33221
  }
32957
33222
  if (useCodexAcp(preferredAgentType, command)) {
@@ -32959,7 +33224,7 @@ function resolveAgentCommand(preferredAgentType) {
32959
33224
  command,
32960
33225
  label: preferredAgentType,
32961
33226
  createClient: createCodexAcpClient,
32962
- spawnCommandForSession: (sessionMode) => buildCodexAcpSpawnCommand(command, sessionMode)
33227
+ spawnCommandForSession: (sessionMode, _agentConfig) => buildCodexAcpSpawnCommand(command, sessionMode)
32963
33228
  };
32964
33229
  }
32965
33230
  if (useKiroAcp(preferredAgentType, command)) {
@@ -32967,7 +33232,7 @@ function resolveAgentCommand(preferredAgentType) {
32967
33232
  command,
32968
33233
  label: preferredAgentType,
32969
33234
  createClient: createKiroAcpClient,
32970
- spawnCommandForSession: (sessionMode) => buildKiroAcpSpawnCommand(command, sessionMode)
33235
+ spawnCommandForSession: (sessionMode, _agentConfig) => buildKiroAcpSpawnCommand(command, sessionMode)
32971
33236
  };
32972
33237
  }
32973
33238
  return {
@@ -33633,7 +33898,7 @@ function buildAcpSessionBridgeHooks(opts) {
33633
33898
 
33634
33899
  // src/agents/acp/ensure-acp-client.ts
33635
33900
  async function ensureAcpClient(options) {
33636
- const { state, preferredAgentType, mode, sessionParentPath, routing, sendSessionUpdate, sendRequest, log: log2 } = options;
33901
+ const { state, preferredAgentType, mode, agentConfig, sessionParentPath, routing, sendSessionUpdate, sendRequest, log: log2 } = options;
33637
33902
  const targetSessionParentPath = resolveSessionParentPathForAgentProcess(sessionParentPath);
33638
33903
  if (state.acpStartPromise && !state.acpHandle) {
33639
33904
  await state.acpStartPromise;
@@ -33655,8 +33920,9 @@ async function ensureAcpClient(options) {
33655
33920
  state.lastAcpStartError = "No agent type: ensure the app sends agentType on prompts or agent_config for this bridge.";
33656
33921
  return null;
33657
33922
  }
33658
- const fullCmd = resolved.spawnCommandForSession(mode);
33659
- const agentKey = `${resolved.label}::${fullCmd.join("\0")}`;
33923
+ const fullCmd = resolved.spawnCommandForSession(mode, agentConfig ?? null);
33924
+ const cacheConfig = agentConfig != null && typeof agentConfig === "object" && !Array.isArray(agentConfig) && Object.keys(agentConfig).length > 0 ? `\0${JSON.stringify(agentConfig, Object.keys(agentConfig).sort())}` : "";
33925
+ const agentKey = `${resolved.label}::${fullCmd.join("\0")}${cacheConfig}`;
33660
33926
  if (state.acpHandle && state.acpAgentKey !== agentKey) {
33661
33927
  try {
33662
33928
  state.acpHandle.disconnect();
@@ -33693,6 +33959,7 @@ async function ensureAcpClient(options) {
33693
33959
  state.acpStartPromise = resolved.createClient({
33694
33960
  command: resolved.command,
33695
33961
  sessionMode: mode,
33962
+ agentConfig: agentConfig ?? null,
33696
33963
  backendAgentType: preferredAgentType,
33697
33964
  onAgentSubprocessExit: () => {
33698
33965
  state.acpHandle = null;
@@ -33754,6 +34021,7 @@ async function createAcpManager(options) {
33754
34021
  runId,
33755
34022
  mode,
33756
34023
  agentType,
34024
+ agentConfig,
33757
34025
  sessionParentPath,
33758
34026
  sendResult: sendResult2,
33759
34027
  sendSessionUpdate,
@@ -33773,6 +34041,7 @@ async function createAcpManager(options) {
33773
34041
  state,
33774
34042
  preferredAgentType: preferredForPrompt,
33775
34043
  mode,
34044
+ agentConfig: agentConfig ?? null,
33776
34045
  sessionParentPath,
33777
34046
  routing: promptRouting,
33778
34047
  sendSessionUpdate,
@@ -33886,7 +34155,7 @@ import * as path14 from "node:path";
33886
34155
 
33887
34156
  // src/git/worktree-add.ts
33888
34157
  async function gitWorktreeAddBranch(mainRepoPath, worktreePath, branch) {
33889
- const mainGit = simpleGit(mainRepoPath);
34158
+ const mainGit = cliSimpleGit(mainRepoPath);
33890
34159
  await mainGit.raw(["worktree", "add", "-b", branch, worktreePath, "HEAD"]);
33891
34160
  }
33892
34161
 
@@ -33991,7 +34260,7 @@ async function prepareNewSessionWorktrees(options) {
33991
34260
 
33992
34261
  // src/git/rename-branch.ts
33993
34262
  async function gitRenameCurrentBranch(repoDir, newName) {
33994
- const g = simpleGit(repoDir);
34263
+ const g = cliSimpleGit(repoDir);
33995
34264
  await g.raw(["branch", "-m", newName]);
33996
34265
  }
33997
34266
 
@@ -34034,7 +34303,7 @@ function resolveMainRepoFromWorktreeGitFile(wt) {
34034
34303
  async function gitWorktreeRemoveForce(worktreePath) {
34035
34304
  const mainRepo = resolveMainRepoFromWorktreeGitFile(worktreePath);
34036
34305
  if (mainRepo) {
34037
- await simpleGit(mainRepo).raw(["worktree", "remove", "--force", worktreePath]);
34306
+ await cliSimpleGit(mainRepo).raw(["worktree", "remove", "--force", worktreePath]);
34038
34307
  } else {
34039
34308
  fs14.rmSync(worktreePath, { recursive: true, force: true });
34040
34309
  }
@@ -34164,7 +34433,7 @@ async function gitLogNotReachableFromBase(g, baseSha, headSha) {
34164
34433
  }
34165
34434
  }
34166
34435
  async function commitsAheadOfRemoteTracking(repoDir) {
34167
- const g = simpleGit(repoDir);
34436
+ const g = cliSimpleGit(repoDir);
34168
34437
  const headSha = await revParseSafe(g, "HEAD");
34169
34438
  if (!headSha) return 0;
34170
34439
  const baseSha = await resolveBaseShaForUnpushedCommits(g);
@@ -34178,14 +34447,14 @@ async function commitsAheadOfRemoteTracking(repoDir) {
34178
34447
  }
34179
34448
  }
34180
34449
  async function getRepoWorkingTreeStatus(repoDir) {
34181
- const g = simpleGit(repoDir);
34450
+ const g = cliSimpleGit(repoDir);
34182
34451
  const st = await g.status();
34183
34452
  const hasUncommittedChanges = (st.files?.length ?? 0) > 0;
34184
34453
  const ahead = await commitsAheadOfRemoteTracking(repoDir);
34185
34454
  return { hasUncommittedChanges, hasUnpushedCommits: ahead > 0 };
34186
34455
  }
34187
34456
  async function listUnpushedCommits(repoDir) {
34188
- const g = simpleGit(repoDir);
34457
+ const g = cliSimpleGit(repoDir);
34189
34458
  const headSha = await revParseSafe(g, "HEAD");
34190
34459
  if (!headSha) return [];
34191
34460
  const baseSha = await resolveBaseShaForUnpushedCommits(g);
@@ -34204,7 +34473,7 @@ async function aggregateSessionPathsWorkingTreeStatus(paths) {
34204
34473
  }
34205
34474
  async function pushAheadOfUpstreamForPaths(paths) {
34206
34475
  for (const p of paths) {
34207
- const g = simpleGit(p);
34476
+ const g = cliSimpleGit(p);
34208
34477
  const ahead = await commitsAheadOfRemoteTracking(p);
34209
34478
  if (ahead <= 0) continue;
34210
34479
  await g.push();
@@ -34346,7 +34615,7 @@ async function parentForCommitDiff(g, sha) {
34346
34615
  }
34347
34616
  }
34348
34617
  async function listChangedFilesForCommit(repoGitCwd, repoRelPath, commitSha) {
34349
- const g = simpleGit(repoGitCwd);
34618
+ const g = cliSimpleGit(repoGitCwd);
34350
34619
  const parent = await parentForCommitDiff(g, commitSha);
34351
34620
  const range = `${parent}..${commitSha}`;
34352
34621
  const [nameStatusRaw, numstatRaw] = await Promise.all([
@@ -34421,7 +34690,7 @@ var MAX_HYDRATE_LINES_PER_FILE = 8e4;
34421
34690
  async function readGitBlobLines(repoCwd, pathInRepo) {
34422
34691
  try {
34423
34692
  const rel = pathInRepo.replace(/\\/g, "/");
34424
- const raw = await simpleGit(repoCwd).show([`HEAD:${rel}`]);
34693
+ const raw = await cliSimpleGit(repoCwd).show([`HEAD:${rel}`]);
34425
34694
  return String(raw).split(/\r?\n/);
34426
34695
  } catch {
34427
34696
  return null;
@@ -34531,7 +34800,7 @@ async function hydrateUnifiedPatchWithFileContext(patch, filePath, repoGitCwd, p
34531
34800
 
34532
34801
  // src/git/working-directory/changes/unified-diff-for-file.ts
34533
34802
  async function unifiedDiffForFile(repoCwd, pathInRepo, change) {
34534
- const g = simpleGit(repoCwd);
34803
+ const g = cliSimpleGit(repoCwd);
34535
34804
  try {
34536
34805
  let raw;
34537
34806
  if (change === "added") {
@@ -34550,7 +34819,7 @@ async function unifiedDiffForFile(repoCwd, pathInRepo, change) {
34550
34819
 
34551
34820
  // src/git/working-directory/changes/list-changed-files-for-repo.ts
34552
34821
  async function listChangedFilesForRepo(repoGitCwd, repoRelPath) {
34553
- const g = simpleGit(repoGitCwd);
34822
+ const g = cliSimpleGit(repoGitCwd);
34554
34823
  const [nameStatusRaw, numstatRaw, untrackedRaw] = await Promise.all([
34555
34824
  g.raw(["diff", "--name-status", "HEAD"]).catch(() => ""),
34556
34825
  g.raw(["diff", "HEAD", "--numstat"]).catch(() => ""),
@@ -34635,7 +34904,7 @@ async function getWorkingTreeChangeRepoDetails(options) {
34635
34904
  for (const target of options.commitTargetPaths) {
34636
34905
  const t = path17.resolve(target);
34637
34906
  if (!await isGitRepoDirectory(t)) continue;
34638
- const g = simpleGit(t);
34907
+ const g = cliSimpleGit(t);
34639
34908
  let branch = "HEAD";
34640
34909
  try {
34641
34910
  branch = (await g.raw(["rev-parse", "--abbrev-ref", "HEAD"])).trim() || "HEAD";
@@ -34686,7 +34955,7 @@ async function getWorkingTreeChangeRepoDetails(options) {
34686
34955
 
34687
34956
  // src/git/commit-and-push.ts
34688
34957
  async function gitCommitAllIfDirty(repoDir, message, options) {
34689
- const g = simpleGit(repoDir);
34958
+ const g = cliSimpleGit(repoDir);
34690
34959
  const st = await g.status();
34691
34960
  if (!st.files?.length) return;
34692
34961
  const branch = options.branch.trim();
@@ -35473,7 +35742,7 @@ function startFileIndexWatcher(cwd = getBridgeRoot()) {
35473
35742
  };
35474
35743
  }
35475
35744
 
35476
- // src/bridge/connection/create-bridge-connection.ts
35745
+ // src/connection/create-bridge-connection.ts
35477
35746
  import * as path34 from "node:path";
35478
35747
 
35479
35748
  // src/dev-servers/manager/dev-server-manager.ts
@@ -36609,7 +36878,17 @@ function tryConsumeBinaryProxyBody(raw, deps) {
36609
36878
 
36610
36879
  // src/firehose/connect-firehose.ts
36611
36880
  function connectFirehose(options) {
36612
- const { firehoseServerUrl, workspaceId, bridgeName, proxyPorts, log: log2, devServerManager, onOpen, onClose } = options;
36881
+ const {
36882
+ firehoseServerUrl,
36883
+ workspaceId,
36884
+ bridgeName,
36885
+ proxyPorts,
36886
+ log: log2,
36887
+ devServerManager,
36888
+ onOpen,
36889
+ onClose,
36890
+ suppressWebSocketErrors
36891
+ } = options;
36613
36892
  const wsUrl = buildFirehoseCliWsUrl(firehoseServerUrl);
36614
36893
  applyCliOutboundNetworkPreferences();
36615
36894
  const ws = new wrapper_default(wsUrl, buildCliWebSocketClientOptions(wsUrl));
@@ -36654,7 +36933,9 @@ function connectFirehose(options) {
36654
36933
  });
36655
36934
  ws.on("error", (err) => {
36656
36935
  disposeClientPing();
36657
- logCliWebSocketError(log2, "[Proxy and log service]", err);
36936
+ if (!suppressWebSocketErrors?.()) {
36937
+ logCliWebSocketError(log2, "[Proxy and log service]", err);
36938
+ }
36658
36939
  if (ws.readyState === wrapper_default.CONNECTING || ws.readyState === wrapper_default.OPEN) {
36659
36940
  safeCloseWebSocket(ws);
36660
36941
  }
@@ -36669,7 +36950,7 @@ function connectFirehose(options) {
36669
36950
  };
36670
36951
  }
36671
36952
 
36672
- // src/bridge/connection/attach-firehose-after-identified.ts
36953
+ // src/connection/attach-firehose-after-identified.ts
36673
36954
  function attachFirehoseAfterIdentified(ctx, params) {
36674
36955
  const { state, devServerManager, logFn } = ctx;
36675
36956
  function clearFirehoseReconnectTimer() {
@@ -36686,10 +36967,44 @@ function attachFirehoseAfterIdentified(ctx, params) {
36686
36967
  firehoseQuiet: state.firehoseQuiet
36687
36968
  };
36688
36969
  }
36970
+ function scheduleFirehoseRetryAfterDrop(closeMeta) {
36971
+ if (state.closedByUser) return;
36972
+ const meta = closeMeta ?? state.lastFirehoseReconnectCloseMeta ?? void 0;
36973
+ const delay2 = applyTieredReconnectPlanAndLog(
36974
+ state.firehoseOutage,
36975
+ logFn,
36976
+ PREVIEW_TUNNEL_SERVICE_LABEL,
36977
+ PREVIEW_TUNNEL_DISCONNECT_LEAD_IN,
36978
+ meta?.code,
36979
+ meta?.reason
36980
+ );
36981
+ armReconnectDelayTimer({
36982
+ delayMs: delay2,
36983
+ bumpAttempt: () => {
36984
+ state.firehoseReconnectAttempt += 1;
36985
+ },
36986
+ clearTimer: clearFirehoseReconnectTimer,
36987
+ setTimer: (id) => {
36988
+ state.firehoseReconnectTimeout = id;
36989
+ },
36990
+ shouldAbortBeforeRun: () => state.closedByUser,
36991
+ run: () => {
36992
+ const p = state.lastFirehoseParams;
36993
+ if (!p) return;
36994
+ attachFirehoseAfterIdentified(ctx, p);
36995
+ },
36996
+ reschedule: () => {
36997
+ scheduleFirehoseRetryAfterDrop();
36998
+ }
36999
+ });
37000
+ }
36689
37001
  state.lastFirehoseParams = params;
36690
37002
  clearFirehoseReconnectTimer();
36691
37003
  if (state.firehoseReconnectAttempt === 0) {
36692
- logFn("Connecting to preview tunnel (local HTTP proxy and dev logs)\u2026");
37004
+ try {
37005
+ logFn("Connecting to preview tunnel (local HTTP proxy and dev logs)\u2026");
37006
+ } catch {
37007
+ }
36693
37008
  }
36694
37009
  state.firehoseGeneration += 1;
36695
37010
  const myGen = state.firehoseGeneration;
@@ -36704,47 +37019,47 @@ function attachFirehoseAfterIdentified(ctx, params) {
36704
37019
  proxyPorts: params.proxyPorts,
36705
37020
  log: logFn,
36706
37021
  devServerManager,
37022
+ suppressWebSocketErrors: () => !state.closedByUser && state.firehoseOutage.startedAt != null,
36707
37023
  onOpen: () => {
36708
37024
  if (myGen !== state.firehoseGeneration) return;
36709
- clearFirehoseReconnectQuietOnOpen({ firehoseQuiet: state.firehoseQuiet }, logFn);
37025
+ try {
37026
+ clearFirehoseReconnectQuietOnOpen(
37027
+ {
37028
+ firehoseQuiet: state.firehoseQuiet,
37029
+ firehoseOutage: state.firehoseOutage,
37030
+ lastFirehoseReconnectCloseMeta: state.lastFirehoseReconnectCloseMeta
37031
+ },
37032
+ logFn
37033
+ );
37034
+ } catch {
37035
+ }
36710
37036
  const logOpenAsFirehoseReconnect = state.firehoseReconnectAttempt > 0;
36711
37037
  state.firehoseReconnectAttempt = 0;
36712
37038
  if (!logOpenAsFirehoseReconnect) {
36713
- logFn("Connected to preview tunnel (local HTTP proxy and dev logs).");
37039
+ try {
37040
+ logFn("Connected to preview tunnel (local HTTP proxy and dev logs).");
37041
+ } catch {
37042
+ }
36714
37043
  }
36715
37044
  },
36716
37045
  onClose: (code, reason) => {
36717
37046
  if (myGen !== state.firehoseGeneration) return;
36718
37047
  state.firehoseHandle = null;
36719
37048
  if (state.closedByUser) return;
36720
- beginFirehoseDeferredDisconnect(firehoseCtx(), code, reason, logFn);
36721
- clearFirehoseReconnectTimer();
36722
- const delay2 = reconnectDelayMs(state.firehoseReconnectAttempt);
36723
- state.firehoseReconnectAttempt += 1;
36724
- logNextReconnectAttempt(
36725
- logFn,
36726
- PROXY_AND_LOG_SERVICE_LABEL,
36727
- state.firehoseQuiet,
36728
- delay2,
36729
- state.firehoseReconnectAttempt
36730
- );
36731
- state.firehoseReconnectTimeout = setTimeout(() => {
36732
- state.firehoseReconnectTimeout = null;
36733
- if (state.closedByUser) return;
36734
- const p = state.lastFirehoseParams;
36735
- if (!p) {
36736
- if (state.firehoseQuiet.verboseLogs) {
36737
- logFn(`${PROXY_AND_LOG_SERVICE_LABEL} Reconnect skipped: no stored connection parameters.`);
36738
- }
36739
- return;
36740
- }
36741
- attachFirehoseAfterIdentified(ctx, p);
36742
- }, delay2);
37049
+ state.lastFirehoseReconnectCloseMeta = { code, reason };
37050
+ try {
37051
+ beginFirehoseDeferredDisconnect(firehoseCtx(), code, reason, logFn);
37052
+ } catch {
37053
+ }
37054
+ try {
37055
+ scheduleFirehoseRetryAfterDrop({ code, reason });
37056
+ } catch {
37057
+ }
36743
37058
  }
36744
37059
  });
36745
37060
  }
36746
37061
 
36747
- // src/bridge/connection/create-bridge-identified-handler.ts
37062
+ // src/connection/create-bridge-identified-handler.ts
36748
37063
  function createOnBridgeIdentified(opts) {
36749
37064
  const { devServerManager, firehoseServerUrl, workspaceId, state, logFn } = opts;
36750
37065
  const firehoseCtx = { state, devServerManager, logFn };
@@ -36855,7 +37170,7 @@ async function detectLocalAgentTypes() {
36855
37170
  }
36856
37171
  }
36857
37172
 
36858
- // src/bridge/connection/create-bridge-local-reports.ts
37173
+ // src/connection/create-bridge-local-reports.ts
36859
37174
  function createSendLocalSkillsReport(getWs, logFn) {
36860
37175
  return () => {
36861
37176
  setImmediate(() => {
@@ -36888,14 +37203,14 @@ function createReportAutoDetectedAgents(getWs, logFn) {
36888
37203
  };
36889
37204
  }
36890
37205
 
36891
- // src/bridge/connection/build-bridge-url.ts
37206
+ // src/connection/build-bridge-url.ts
36892
37207
  function buildBridgeUrl(apiUrl, workspaceId, authToken) {
36893
37208
  const base = apiUrl.startsWith("https") ? apiUrl.replace(/^https/, "wss") : apiUrl.replace(/^http/, "ws");
36894
37209
  const params = new URLSearchParams({ workspaceId, token: authToken });
36895
37210
  return `${base}/ws/bridge?${params.toString()}`;
36896
37211
  }
36897
37212
 
36898
- // src/bridge/connection/report-git-repos.ts
37213
+ // src/connection/report-git-repos.ts
36899
37214
  function reportGitRepos(getWs, log2) {
36900
37215
  setImmediate(() => {
36901
37216
  discoverGitRepos().then((repos) => {
@@ -36951,14 +37266,14 @@ function parseApiToBridgeMessage(data, log2) {
36951
37266
  return data;
36952
37267
  }
36953
37268
 
36954
- // src/bridge/routing/handlers/auth-token.ts
37269
+ // src/routing/handlers/auth-token.ts
36955
37270
  var handleAuthToken = (msg, { log: log2 }) => {
36956
37271
  if (typeof msg.token !== "string") return;
36957
37272
  log2("Received auth token. Save it for future runs:");
36958
37273
  log2(` export BUILDAUTOMATON_AUTH_TOKEN="${msg.token}"`);
36959
37274
  };
36960
37275
 
36961
- // src/bridge/routing/handlers/bridge-identified.ts
37276
+ // src/routing/handlers/bridge-identified.ts
36962
37277
  var handleBridgeIdentified = (msg, deps) => {
36963
37278
  if (typeof msg.bridgeName !== "string") return;
36964
37279
  deps.onBridgeIdentified({
@@ -36994,7 +37309,7 @@ function handleBridgeAgentConfig(msg, { acpManager }) {
36994
37309
  acpManager.setPreferredAgentType(msg.agents[0].type);
36995
37310
  }
36996
37311
 
36997
- // src/bridge/routing/handlers/agent-config.ts
37312
+ // src/routing/handlers/agent-config.ts
36998
37313
  var handleAgentConfigMessage = (msg, deps) => {
36999
37314
  handleBridgeAgentConfig(msg, deps);
37000
37315
  };
@@ -37153,7 +37468,8 @@ function dispatchLocalPrompt(next, deps) {
37153
37468
  ...typeof pl.followUpCatalogPromptId === "string" ? { followUpCatalogPromptId: pl.followUpCatalogPromptId } : {},
37154
37469
  ...Array.isArray(pl.sessionChangeSummaryFilePaths) ? { sessionChangeSummaryFilePaths: pl.sessionChangeSummaryFilePaths } : {},
37155
37470
  ...Array.isArray(pl.sessionChangeSummaryFileSnapshots) ? { sessionChangeSummaryFileSnapshots: pl.sessionChangeSummaryFileSnapshots } : {},
37156
- ...typeof pl.agentType === "string" && pl.agentType.trim() ? { agentType: pl.agentType.trim() } : {}
37471
+ ...typeof pl.agentType === "string" && pl.agentType.trim() ? { agentType: pl.agentType.trim() } : {},
37472
+ ...pl.agentConfig != null && typeof pl.agentConfig === "object" && !Array.isArray(pl.agentConfig) && Object.keys(pl.agentConfig).length > 0 ? { agentConfig: pl.agentConfig } : {}
37157
37473
  };
37158
37474
  handleBridgePrompt(msg, deps);
37159
37475
  }
@@ -37180,7 +37496,24 @@ async function applyPromptQueueStateFromServer(msg, deps) {
37180
37496
  if (!file2) continue;
37181
37497
  const cancelRow = file2.turns.find((t) => t.serverState === "cancel_requested" && t.lastClientState === "running");
37182
37498
  if (cancelRow) {
37183
- await deps.acpManager.cancelRun(cancelRow.turnId);
37499
+ const localCancelHandled = await deps.acpManager.cancelRun(cancelRow.turnId);
37500
+ if (!localCancelHandled) {
37501
+ deps.log(
37502
+ `[Queue] server cancel_requested for ${cancelRow.turnId.slice(0, 8)}\u2026 but no local agent run is active (e.g. after CLI restart); marking cancelled and notifying bridge.`
37503
+ );
37504
+ finalizePromptTurnOnBridge(deps.getWs, cancelRow.turnId, false, { terminalClientState: "cancelled" });
37505
+ const ws = deps.getWs();
37506
+ if (ws && cancelRow.sessionId) {
37507
+ sendWsMessage(ws, {
37508
+ type: "prompt_result",
37509
+ sessionId: cancelRow.sessionId,
37510
+ runId: cancelRow.turnId,
37511
+ success: false,
37512
+ error: "Stopped by user",
37513
+ stopReason: "cancelled"
37514
+ });
37515
+ }
37516
+ }
37184
37517
  }
37185
37518
  }
37186
37519
  const report = {};
@@ -37434,6 +37767,7 @@ function handleBridgePrompt(msg, deps) {
37434
37767
  const sessionParentPath = typeof msg.sessionParentPath === "string" && msg.sessionParentPath.trim() ? msg.sessionParentPath.trim() : null;
37435
37768
  const agentType = typeof msg.agentType === "string" && msg.agentType.trim() ? msg.agentType.trim() : void 0;
37436
37769
  const mode = typeof msg.mode === "string" && msg.mode.trim() ? msg.mode.trim() : void 0;
37770
+ const agentConfig = msg.agentConfig != null && typeof msg.agentConfig === "object" && !Array.isArray(msg.agentConfig) ? msg.agentConfig : void 0;
37437
37771
  acpManager.logPromptReceivedFromBridge({ agentType, mode });
37438
37772
  async function preambleAndPrompt(resolvedCwd) {
37439
37773
  const effectiveCwd = resolveSessionParentPathForAgentProcess(resolvedCwd);
@@ -37469,6 +37803,7 @@ function handleBridgePrompt(msg, deps) {
37469
37803
  runId,
37470
37804
  mode,
37471
37805
  agentType,
37806
+ agentConfig,
37472
37807
  sessionParentPath: effectiveCwd,
37473
37808
  sendResult: sendResult2,
37474
37809
  sendSessionUpdate,
@@ -37485,27 +37820,27 @@ function handleBridgePrompt(msg, deps) {
37485
37820
  });
37486
37821
  }
37487
37822
 
37488
- // src/bridge/routing/handlers/prompt.ts
37823
+ // src/routing/handlers/prompt.ts
37489
37824
  var handlePromptMessage = (msg, deps) => {
37490
37825
  handleBridgePrompt(msg, deps);
37491
37826
  };
37492
37827
 
37493
- // src/bridge/routing/handlers/prompt-queue-state.ts
37828
+ // src/routing/handlers/prompt-queue-state.ts
37494
37829
  var handlePromptQueueStateMessage = (msg, deps) => {
37495
37830
  void applyPromptQueueStateFromServer(msg, deps).catch((err) => {
37496
37831
  deps.log(`[Queue] applyPromptQueueStateFromServer failed: ${err instanceof Error ? err.message : String(err)}`);
37497
37832
  });
37498
37833
  };
37499
37834
 
37500
- // src/agents/acp/from-bridge/handle-bridge-cursor-request-response.ts
37501
- function handleBridgeCursorRequestResponse(msg, { acpManager }) {
37835
+ // src/agents/acp/from-bridge/handle-bridge-session-request-response.ts
37836
+ function handleBridgeSessionRequestResponse(msg, { acpManager }) {
37502
37837
  if (typeof msg.requestId !== "string") return;
37503
37838
  acpManager.resolveRequest(msg.requestId, msg.result ?? {});
37504
37839
  }
37505
37840
 
37506
- // src/bridge/routing/handlers/cursor-request-response.ts
37507
- var handleCursorRequestResponseMessage = (msg, deps) => {
37508
- handleBridgeCursorRequestResponse(msg, deps);
37841
+ // src/routing/handlers/session-request-response.ts
37842
+ var handleSessionRequestResponseMessage = (msg, deps) => {
37843
+ handleBridgeSessionRequestResponse(msg, deps);
37509
37844
  };
37510
37845
 
37511
37846
  // src/skills/preview.ts
@@ -37692,7 +38027,7 @@ function handleSkillCall(msg, socket, log2) {
37692
38027
  });
37693
38028
  }
37694
38029
 
37695
- // src/bridge/routing/handlers/skill-call.ts
38030
+ // src/routing/handlers/skill-call.ts
37696
38031
  var handleSkillCallMessage = (msg, { getWs, log: log2 }) => {
37697
38032
  const skillId = typeof msg.skillId === "string" ? msg.skillId : "";
37698
38033
  const operationId = typeof msg.operationId === "string" ? msg.operationId : "";
@@ -38034,7 +38369,7 @@ function handleFileBrowserRequest(msg, socket, e2ee) {
38034
38369
  })();
38035
38370
  }
38036
38371
 
38037
- // src/bridge/routing/handlers/file-browser-messages.ts
38372
+ // src/routing/handlers/file-browser-messages.ts
38038
38373
  function handleFileBrowserRequestMessage(msg, { getWs, e2ee }) {
38039
38374
  if (typeof msg.id !== "string" || typeof msg.path !== "string") return;
38040
38375
  const socket = getWs();
@@ -38052,7 +38387,7 @@ function handleFileBrowserSearchMessage(msg, { getWs, e2ee }) {
38052
38387
  handleFileBrowserSearch(msg, socket, e2ee);
38053
38388
  }
38054
38389
 
38055
- // src/bridge/routing/handlers/skill-layout-request.ts
38390
+ // src/routing/handlers/skill-layout-request.ts
38056
38391
  function handleSkillLayoutRequest(msg, deps) {
38057
38392
  const socket = deps.getWs();
38058
38393
  const id = typeof msg.id === "string" ? msg.id : "";
@@ -38098,7 +38433,7 @@ function installRemoteSkills(cwd, targetDir, items) {
38098
38433
  }
38099
38434
  }
38100
38435
 
38101
- // src/bridge/routing/handlers/install-skills.ts
38436
+ // src/routing/handlers/install-skills.ts
38102
38437
  var handleInstallSkillsMessage = (msg, deps) => {
38103
38438
  const socket = deps.getWs();
38104
38439
  const id = typeof msg.id === "string" ? msg.id : "";
@@ -38119,12 +38454,12 @@ var handleInstallSkillsMessage = (msg, deps) => {
38119
38454
  }
38120
38455
  };
38121
38456
 
38122
- // src/bridge/routing/handlers/refresh-local-skills.ts
38457
+ // src/routing/handlers/refresh-local-skills.ts
38123
38458
  var handleRefreshLocalSkills = (_msg, deps) => {
38124
38459
  deps.sendLocalSkillsReport?.();
38125
38460
  };
38126
38461
 
38127
- // src/bridge/routing/handlers/session-git-request.ts
38462
+ // src/routing/handlers/session-git-request.ts
38128
38463
  function sendResult(ws, id, payload, e2ee, encryptedFields = []) {
38129
38464
  if (!ws) return;
38130
38465
  const message = { type: "session_git_result", id, ...payload };
@@ -38210,7 +38545,7 @@ var handleSessionGitRequestMessage = (msg, deps) => {
38210
38545
  })();
38211
38546
  };
38212
38547
 
38213
- // src/bridge/routing/handlers/rename-session-branch.ts
38548
+ // src/routing/handlers/rename-session-branch.ts
38214
38549
  var handleRenameSessionBranchMessage = (msg, deps) => {
38215
38550
  const sessionId = typeof msg.sessionId === "string" ? msg.sessionId : "";
38216
38551
  const newBranch = typeof msg.newBranch === "string" ? msg.newBranch : "";
@@ -38218,21 +38553,21 @@ var handleRenameSessionBranchMessage = (msg, deps) => {
38218
38553
  void deps.sessionWorktreeManager.renameSessionBranch(sessionId, newBranch);
38219
38554
  };
38220
38555
 
38221
- // src/bridge/routing/handlers/session-archived.ts
38556
+ // src/routing/handlers/session-archived.ts
38222
38557
  var handleSessionArchivedMessage = (msg, deps) => {
38223
38558
  const sessionId = typeof msg.sessionId === "string" ? msg.sessionId : "";
38224
38559
  if (!sessionId) return;
38225
38560
  void deps.sessionWorktreeManager.removeSessionWorktrees(sessionId);
38226
38561
  };
38227
38562
 
38228
- // src/bridge/routing/handlers/session-discarded.ts
38563
+ // src/routing/handlers/session-discarded.ts
38229
38564
  var handleSessionDiscardedMessage = (msg, deps) => {
38230
38565
  const sessionId = typeof msg.sessionId === "string" ? msg.sessionId : "";
38231
38566
  if (!sessionId) return;
38232
38567
  void deps.sessionWorktreeManager.removeSessionWorktrees(sessionId);
38233
38568
  };
38234
38569
 
38235
- // src/bridge/routing/handlers/revert-turn-snapshot.ts
38570
+ // src/routing/handlers/revert-turn-snapshot.ts
38236
38571
  import * as fs33 from "node:fs";
38237
38572
  var handleRevertTurnSnapshotMessage = (msg, deps) => {
38238
38573
  const id = typeof msg.id === "string" ? msg.id : "";
@@ -38264,7 +38599,7 @@ var handleRevertTurnSnapshotMessage = (msg, deps) => {
38264
38599
  })();
38265
38600
  };
38266
38601
 
38267
- // src/bridge/routing/handlers/dev-server-control.ts
38602
+ // src/routing/handlers/dev-server-control.ts
38268
38603
  var handleDevServerControl = (msg, deps) => {
38269
38604
  let wire;
38270
38605
  try {
@@ -38279,13 +38614,13 @@ var handleDevServerControl = (msg, deps) => {
38279
38614
  deps.devServerManager?.handleControl(serverId, action);
38280
38615
  };
38281
38616
 
38282
- // src/bridge/routing/handlers/dev-servers-config.ts
38617
+ // src/routing/handlers/dev-servers-config.ts
38283
38618
  var handleDevServersConfig = (msg, deps) => {
38284
38619
  const devServers = msg.devServers;
38285
38620
  deps.devServerManager?.applyConfig(devServers ?? []);
38286
38621
  };
38287
38622
 
38288
- // src/bridge/routing/dispatch-bridge-message.ts
38623
+ // src/routing/dispatch-bridge-message.ts
38289
38624
  function dispatchBridgeMessage(msg, deps) {
38290
38625
  switch (msg.type) {
38291
38626
  case "auth_token":
@@ -38325,7 +38660,7 @@ function dispatchBridgeMessage(msg, deps) {
38325
38660
  handleRevertTurnSnapshotMessage(msg, deps);
38326
38661
  break;
38327
38662
  case "cursor_request_response":
38328
- handleCursorRequestResponseMessage(msg, deps);
38663
+ handleSessionRequestResponseMessage(msg, deps);
38329
38664
  break;
38330
38665
  case "skill_call":
38331
38666
  handleSkillCallMessage(msg, deps);
@@ -38348,7 +38683,7 @@ function dispatchBridgeMessage(msg, deps) {
38348
38683
  }
38349
38684
  }
38350
38685
 
38351
- // src/bridge/routing/handle-bridge-message.ts
38686
+ // src/routing/handle-bridge-message.ts
38352
38687
  function handleBridgeMessage(data, deps) {
38353
38688
  if (!deps.getWs()) return;
38354
38689
  const msg = parseApiToBridgeMessage(data, deps.log);
@@ -38385,7 +38720,7 @@ async function refreshBridgeTokens(params) {
38385
38720
  }
38386
38721
  }
38387
38722
 
38388
- // src/bridge/connection/main-bridge-ws-lifecycle.ts
38723
+ // src/connection/main-bridge-ws-lifecycle.ts
38389
38724
  function createMainBridgeWebSocketLifecycle(params) {
38390
38725
  const {
38391
38726
  state,
@@ -38431,13 +38766,26 @@ function createMainBridgeWebSocketLifecycle(params) {
38431
38766
  }
38432
38767
  }
38433
38768
  function handleClose(code, reason) {
38434
- const was = state.currentWs;
38435
- state.currentWs = null;
38436
- if (was) was.removeAllListeners();
38437
- const willReconnect = !state.closedByUser;
38438
- beginMainBridgeDeferredDisconnect(state, code, reason, logFn, willReconnect);
38439
- if (willReconnect) {
38440
- scheduleMainBridgeReconnect(state, connect, logFn);
38769
+ try {
38770
+ const was = state.currentWs;
38771
+ state.currentWs = null;
38772
+ if (was) was.removeAllListeners();
38773
+ const willReconnect = !state.closedByUser;
38774
+ beginMainBridgeDeferredDisconnect(state, code, reason, logFn, willReconnect);
38775
+ if (willReconnect) {
38776
+ state.lastReconnectCloseMeta = { code, reason };
38777
+ try {
38778
+ scheduleMainBridgeReconnect(state, connect, logFn, { code, reason });
38779
+ } catch {
38780
+ }
38781
+ }
38782
+ } catch {
38783
+ if (!state.closedByUser) {
38784
+ try {
38785
+ scheduleMainBridgeReconnect(state, connect, logFn);
38786
+ } catch {
38787
+ }
38788
+ }
38441
38789
  }
38442
38790
  }
38443
38791
  function connect() {
@@ -38447,7 +38795,10 @@ function createMainBridgeWebSocketLifecycle(params) {
38447
38795
  state.reconnectTimeout = null;
38448
38796
  }
38449
38797
  if (state.reconnectAttempt === 0) {
38450
- logFn("Connecting to bridge service\u2026");
38798
+ try {
38799
+ logFn("Connecting to bridge service\u2026");
38800
+ } catch {
38801
+ }
38451
38802
  }
38452
38803
  const prev = state.currentWs;
38453
38804
  if (prev) {
@@ -38463,53 +38814,84 @@ function createMainBridgeWebSocketLifecycle(params) {
38463
38814
  }
38464
38815
  state.currentWs = null;
38465
38816
  }
38466
- const url2 = buildBridgeUrl(apiUrl, workspaceId, tokens.accessToken);
38467
- state.currentWs = createWsBridge({
38468
- url: url2,
38469
- clientPingIntervalMs: CLI_WEBSOCKET_CLIENT_PING_MS,
38470
- onAuthInvalid: () => {
38471
- if (authRefreshInFlight) return;
38472
- void (async () => {
38473
- authRefreshInFlight = true;
38474
- try {
38475
- if (tokens.refreshToken) {
38476
- const next = await refreshBridgeTokens({
38477
- apiUrl,
38478
- workspaceId,
38479
- refreshToken: tokens.refreshToken
38480
- });
38481
- if (next?.token && next.refreshToken) {
38482
- tokens.accessToken = next.token;
38483
- tokens.refreshToken = next.refreshToken;
38484
- persistTokens?.({ token: tokens.accessToken, refreshToken: tokens.refreshToken });
38485
- logFn("[Bridge service] Access token refreshed; reconnecting\u2026");
38486
- state.reconnectAttempt = 0;
38487
- state.logBridgeOpenAsReconnect = true;
38488
- authRefreshInFlight = false;
38489
- connect();
38490
- return;
38817
+ try {
38818
+ const url2 = buildBridgeUrl(apiUrl, workspaceId, tokens.accessToken);
38819
+ state.currentWs = createWsBridge({
38820
+ url: url2,
38821
+ clientPingIntervalMs: CLI_WEBSOCKET_CLIENT_PING_MS,
38822
+ onAuthInvalid: () => {
38823
+ if (authRefreshInFlight) return;
38824
+ void (async () => {
38825
+ authRefreshInFlight = true;
38826
+ try {
38827
+ if (tokens.refreshToken) {
38828
+ const next = await refreshBridgeTokens({
38829
+ apiUrl,
38830
+ workspaceId,
38831
+ refreshToken: tokens.refreshToken
38832
+ });
38833
+ if (next?.token && next.refreshToken) {
38834
+ tokens.accessToken = next.token;
38835
+ tokens.refreshToken = next.refreshToken;
38836
+ persistTokens?.({ token: tokens.accessToken, refreshToken: tokens.refreshToken });
38837
+ try {
38838
+ logFn("[Bridge service] Access token refreshed; reconnecting\u2026");
38839
+ } catch {
38840
+ }
38841
+ state.reconnectAttempt = 0;
38842
+ resetReconnectOutageTracker(state.mainOutage);
38843
+ state.lastReconnectCloseMeta = null;
38844
+ state.logBridgeOpenAsReconnect = true;
38845
+ authRefreshInFlight = false;
38846
+ connect();
38847
+ return;
38848
+ }
38491
38849
  }
38850
+ authRefreshInFlight = false;
38851
+ state.reconnectAttempt = 0;
38852
+ resetReconnectOutageTracker(state.mainOutage);
38853
+ state.lastReconnectCloseMeta = null;
38854
+ onAuthInvalid();
38855
+ } catch {
38856
+ authRefreshInFlight = false;
38857
+ state.reconnectAttempt = 0;
38858
+ resetReconnectOutageTracker(state.mainOutage);
38859
+ state.lastReconnectCloseMeta = null;
38860
+ onAuthInvalid();
38492
38861
  }
38493
- authRefreshInFlight = false;
38494
- state.reconnectAttempt = 0;
38495
- onAuthInvalid();
38862
+ })();
38863
+ },
38864
+ onOpen: handleOpen,
38865
+ onClose: handleClose,
38866
+ onError: (err) => {
38867
+ if (state.mainOutage.startedAt != null) {
38868
+ return;
38869
+ }
38870
+ try {
38871
+ logCliWebSocketError(logFn, "[Bridge service]", err);
38496
38872
  } catch {
38497
- authRefreshInFlight = false;
38498
- state.reconnectAttempt = 0;
38499
- onAuthInvalid();
38500
38873
  }
38501
- })();
38502
- },
38503
- onOpen: handleOpen,
38504
- onClose: handleClose,
38505
- onError: (err) => logCliWebSocketError(logFn, "[Bridge service]", err),
38506
- onMessage: (data) => handleBridgeMessage(data, messageDeps)
38507
- });
38874
+ },
38875
+ onMessage: (data) => {
38876
+ try {
38877
+ handleBridgeMessage(data, messageDeps);
38878
+ } catch {
38879
+ }
38880
+ }
38881
+ });
38882
+ } catch {
38883
+ if (!state.closedByUser && state.currentWs == null) {
38884
+ try {
38885
+ scheduleMainBridgeReconnect(state, connect, logFn);
38886
+ } catch {
38887
+ }
38888
+ }
38889
+ }
38508
38890
  }
38509
38891
  return { connect };
38510
38892
  }
38511
38893
 
38512
- // src/bridge/connection/create-bridge-connection.ts
38894
+ // src/connection/create-bridge-connection.ts
38513
38895
  async function createBridgeConnection(options) {
38514
38896
  const { apiUrl, workspaceId, justAuthenticated, onAuthInvalid, persistTokens } = options;
38515
38897
  const firehoseServerUrl = options.firehoseServerUrl ?? options.proxyServerUrl;
@@ -38521,16 +38903,20 @@ async function createBridgeConnection(options) {
38521
38903
  const state = {
38522
38904
  closedByUser: false,
38523
38905
  reconnectAttempt: 0,
38906
+ lastReconnectCloseMeta: null,
38524
38907
  logBridgeOpenAsReconnect: false,
38525
38908
  reconnectTimeout: null,
38526
38909
  currentWs: null,
38527
38910
  mainQuiet: createEmptyReconnectQuietSlot(),
38911
+ mainOutage: createEmptyReconnectOutageTracker(),
38528
38912
  firehoseHandle: null,
38529
38913
  lastFirehoseParams: null,
38530
38914
  firehoseReconnectTimeout: null,
38531
38915
  firehoseReconnectAttempt: 0,
38532
38916
  firehoseGeneration: 0,
38533
- firehoseQuiet: createEmptyReconnectQuietSlot()
38917
+ firehoseQuiet: createEmptyReconnectQuietSlot(),
38918
+ firehoseOutage: createEmptyReconnectOutageTracker(),
38919
+ lastFirehoseReconnectCloseMeta: null
38534
38920
  };
38535
38921
  const worktreesRootPath = options.worktreesRootPath ?? defaultWorktreesRootPath();
38536
38922
  const sessionWorktreeManager = new SessionWorktreeManager({
@@ -38792,6 +39178,9 @@ function colorize(color, message) {
38792
39178
  return `${color}${message}${RESET}`;
38793
39179
  }
38794
39180
  async function runCliAction(program2, opts) {
39181
+ const trace = opts.trace === true;
39182
+ const debug2 = opts.debug === true || trace;
39183
+ setCliLogVerbosity(trace ? "trace" : debug2 ? "debug" : "info");
38795
39184
  const positionalUrl = program2.args?.[0];
38796
39185
  const urlFromPositional = typeof positionalUrl === "string" && /^https?:\/\//i.test(positionalUrl) ? positionalUrl : void 0;
38797
39186
  const apiUrlFromCli = opts.apiUrl ?? urlFromPositional;
@@ -38883,7 +39272,7 @@ async function main() {
38883
39272
  "--e2ee-certificates-dir <path>",
38884
39273
  "Directory to load or generate E2EE keys for sessions, files, and logs",
38885
39274
  process.env.BUILDAUTOMATON_E2EE_CERTIFICATES_DIR
38886
- ).option("--no-config", "Ignore saved config at ~/.buildautomaton/config.json").action(async (opts) => runCliAction(program2, opts));
39275
+ ).option("--no-config", "Ignore saved config at ~/.buildautomaton/config.json").option("--debug", "Print debug-level messages from the CLI").option("--trace", "Print trace-level messages (e.g. git subprocess output); implies --debug").action(async (opts) => runCliAction(program2, opts));
38887
39276
  await program2.parseAsync(process.argv);
38888
39277
  }
38889
39278
  main().catch((err) => {