@buildautomaton/cli 0.1.23 → 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.23".length > 0 ? "0.1.23" : "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
  }
@@ -37451,6 +37767,7 @@ function handleBridgePrompt(msg, deps) {
37451
37767
  const sessionParentPath = typeof msg.sessionParentPath === "string" && msg.sessionParentPath.trim() ? msg.sessionParentPath.trim() : null;
37452
37768
  const agentType = typeof msg.agentType === "string" && msg.agentType.trim() ? msg.agentType.trim() : void 0;
37453
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;
37454
37771
  acpManager.logPromptReceivedFromBridge({ agentType, mode });
37455
37772
  async function preambleAndPrompt(resolvedCwd) {
37456
37773
  const effectiveCwd = resolveSessionParentPathForAgentProcess(resolvedCwd);
@@ -37486,6 +37803,7 @@ function handleBridgePrompt(msg, deps) {
37486
37803
  runId,
37487
37804
  mode,
37488
37805
  agentType,
37806
+ agentConfig,
37489
37807
  sessionParentPath: effectiveCwd,
37490
37808
  sendResult: sendResult2,
37491
37809
  sendSessionUpdate,
@@ -37502,27 +37820,27 @@ function handleBridgePrompt(msg, deps) {
37502
37820
  });
37503
37821
  }
37504
37822
 
37505
- // src/bridge/routing/handlers/prompt.ts
37823
+ // src/routing/handlers/prompt.ts
37506
37824
  var handlePromptMessage = (msg, deps) => {
37507
37825
  handleBridgePrompt(msg, deps);
37508
37826
  };
37509
37827
 
37510
- // src/bridge/routing/handlers/prompt-queue-state.ts
37828
+ // src/routing/handlers/prompt-queue-state.ts
37511
37829
  var handlePromptQueueStateMessage = (msg, deps) => {
37512
37830
  void applyPromptQueueStateFromServer(msg, deps).catch((err) => {
37513
37831
  deps.log(`[Queue] applyPromptQueueStateFromServer failed: ${err instanceof Error ? err.message : String(err)}`);
37514
37832
  });
37515
37833
  };
37516
37834
 
37517
- // src/agents/acp/from-bridge/handle-bridge-cursor-request-response.ts
37518
- function handleBridgeCursorRequestResponse(msg, { acpManager }) {
37835
+ // src/agents/acp/from-bridge/handle-bridge-session-request-response.ts
37836
+ function handleBridgeSessionRequestResponse(msg, { acpManager }) {
37519
37837
  if (typeof msg.requestId !== "string") return;
37520
37838
  acpManager.resolveRequest(msg.requestId, msg.result ?? {});
37521
37839
  }
37522
37840
 
37523
- // src/bridge/routing/handlers/cursor-request-response.ts
37524
- var handleCursorRequestResponseMessage = (msg, deps) => {
37525
- handleBridgeCursorRequestResponse(msg, deps);
37841
+ // src/routing/handlers/session-request-response.ts
37842
+ var handleSessionRequestResponseMessage = (msg, deps) => {
37843
+ handleBridgeSessionRequestResponse(msg, deps);
37526
37844
  };
37527
37845
 
37528
37846
  // src/skills/preview.ts
@@ -37709,7 +38027,7 @@ function handleSkillCall(msg, socket, log2) {
37709
38027
  });
37710
38028
  }
37711
38029
 
37712
- // src/bridge/routing/handlers/skill-call.ts
38030
+ // src/routing/handlers/skill-call.ts
37713
38031
  var handleSkillCallMessage = (msg, { getWs, log: log2 }) => {
37714
38032
  const skillId = typeof msg.skillId === "string" ? msg.skillId : "";
37715
38033
  const operationId = typeof msg.operationId === "string" ? msg.operationId : "";
@@ -38051,7 +38369,7 @@ function handleFileBrowserRequest(msg, socket, e2ee) {
38051
38369
  })();
38052
38370
  }
38053
38371
 
38054
- // src/bridge/routing/handlers/file-browser-messages.ts
38372
+ // src/routing/handlers/file-browser-messages.ts
38055
38373
  function handleFileBrowserRequestMessage(msg, { getWs, e2ee }) {
38056
38374
  if (typeof msg.id !== "string" || typeof msg.path !== "string") return;
38057
38375
  const socket = getWs();
@@ -38069,7 +38387,7 @@ function handleFileBrowserSearchMessage(msg, { getWs, e2ee }) {
38069
38387
  handleFileBrowserSearch(msg, socket, e2ee);
38070
38388
  }
38071
38389
 
38072
- // src/bridge/routing/handlers/skill-layout-request.ts
38390
+ // src/routing/handlers/skill-layout-request.ts
38073
38391
  function handleSkillLayoutRequest(msg, deps) {
38074
38392
  const socket = deps.getWs();
38075
38393
  const id = typeof msg.id === "string" ? msg.id : "";
@@ -38115,7 +38433,7 @@ function installRemoteSkills(cwd, targetDir, items) {
38115
38433
  }
38116
38434
  }
38117
38435
 
38118
- // src/bridge/routing/handlers/install-skills.ts
38436
+ // src/routing/handlers/install-skills.ts
38119
38437
  var handleInstallSkillsMessage = (msg, deps) => {
38120
38438
  const socket = deps.getWs();
38121
38439
  const id = typeof msg.id === "string" ? msg.id : "";
@@ -38136,12 +38454,12 @@ var handleInstallSkillsMessage = (msg, deps) => {
38136
38454
  }
38137
38455
  };
38138
38456
 
38139
- // src/bridge/routing/handlers/refresh-local-skills.ts
38457
+ // src/routing/handlers/refresh-local-skills.ts
38140
38458
  var handleRefreshLocalSkills = (_msg, deps) => {
38141
38459
  deps.sendLocalSkillsReport?.();
38142
38460
  };
38143
38461
 
38144
- // src/bridge/routing/handlers/session-git-request.ts
38462
+ // src/routing/handlers/session-git-request.ts
38145
38463
  function sendResult(ws, id, payload, e2ee, encryptedFields = []) {
38146
38464
  if (!ws) return;
38147
38465
  const message = { type: "session_git_result", id, ...payload };
@@ -38227,7 +38545,7 @@ var handleSessionGitRequestMessage = (msg, deps) => {
38227
38545
  })();
38228
38546
  };
38229
38547
 
38230
- // src/bridge/routing/handlers/rename-session-branch.ts
38548
+ // src/routing/handlers/rename-session-branch.ts
38231
38549
  var handleRenameSessionBranchMessage = (msg, deps) => {
38232
38550
  const sessionId = typeof msg.sessionId === "string" ? msg.sessionId : "";
38233
38551
  const newBranch = typeof msg.newBranch === "string" ? msg.newBranch : "";
@@ -38235,21 +38553,21 @@ var handleRenameSessionBranchMessage = (msg, deps) => {
38235
38553
  void deps.sessionWorktreeManager.renameSessionBranch(sessionId, newBranch);
38236
38554
  };
38237
38555
 
38238
- // src/bridge/routing/handlers/session-archived.ts
38556
+ // src/routing/handlers/session-archived.ts
38239
38557
  var handleSessionArchivedMessage = (msg, deps) => {
38240
38558
  const sessionId = typeof msg.sessionId === "string" ? msg.sessionId : "";
38241
38559
  if (!sessionId) return;
38242
38560
  void deps.sessionWorktreeManager.removeSessionWorktrees(sessionId);
38243
38561
  };
38244
38562
 
38245
- // src/bridge/routing/handlers/session-discarded.ts
38563
+ // src/routing/handlers/session-discarded.ts
38246
38564
  var handleSessionDiscardedMessage = (msg, deps) => {
38247
38565
  const sessionId = typeof msg.sessionId === "string" ? msg.sessionId : "";
38248
38566
  if (!sessionId) return;
38249
38567
  void deps.sessionWorktreeManager.removeSessionWorktrees(sessionId);
38250
38568
  };
38251
38569
 
38252
- // src/bridge/routing/handlers/revert-turn-snapshot.ts
38570
+ // src/routing/handlers/revert-turn-snapshot.ts
38253
38571
  import * as fs33 from "node:fs";
38254
38572
  var handleRevertTurnSnapshotMessage = (msg, deps) => {
38255
38573
  const id = typeof msg.id === "string" ? msg.id : "";
@@ -38281,7 +38599,7 @@ var handleRevertTurnSnapshotMessage = (msg, deps) => {
38281
38599
  })();
38282
38600
  };
38283
38601
 
38284
- // src/bridge/routing/handlers/dev-server-control.ts
38602
+ // src/routing/handlers/dev-server-control.ts
38285
38603
  var handleDevServerControl = (msg, deps) => {
38286
38604
  let wire;
38287
38605
  try {
@@ -38296,13 +38614,13 @@ var handleDevServerControl = (msg, deps) => {
38296
38614
  deps.devServerManager?.handleControl(serverId, action);
38297
38615
  };
38298
38616
 
38299
- // src/bridge/routing/handlers/dev-servers-config.ts
38617
+ // src/routing/handlers/dev-servers-config.ts
38300
38618
  var handleDevServersConfig = (msg, deps) => {
38301
38619
  const devServers = msg.devServers;
38302
38620
  deps.devServerManager?.applyConfig(devServers ?? []);
38303
38621
  };
38304
38622
 
38305
- // src/bridge/routing/dispatch-bridge-message.ts
38623
+ // src/routing/dispatch-bridge-message.ts
38306
38624
  function dispatchBridgeMessage(msg, deps) {
38307
38625
  switch (msg.type) {
38308
38626
  case "auth_token":
@@ -38342,7 +38660,7 @@ function dispatchBridgeMessage(msg, deps) {
38342
38660
  handleRevertTurnSnapshotMessage(msg, deps);
38343
38661
  break;
38344
38662
  case "cursor_request_response":
38345
- handleCursorRequestResponseMessage(msg, deps);
38663
+ handleSessionRequestResponseMessage(msg, deps);
38346
38664
  break;
38347
38665
  case "skill_call":
38348
38666
  handleSkillCallMessage(msg, deps);
@@ -38365,7 +38683,7 @@ function dispatchBridgeMessage(msg, deps) {
38365
38683
  }
38366
38684
  }
38367
38685
 
38368
- // src/bridge/routing/handle-bridge-message.ts
38686
+ // src/routing/handle-bridge-message.ts
38369
38687
  function handleBridgeMessage(data, deps) {
38370
38688
  if (!deps.getWs()) return;
38371
38689
  const msg = parseApiToBridgeMessage(data, deps.log);
@@ -38402,7 +38720,7 @@ async function refreshBridgeTokens(params) {
38402
38720
  }
38403
38721
  }
38404
38722
 
38405
- // src/bridge/connection/main-bridge-ws-lifecycle.ts
38723
+ // src/connection/main-bridge-ws-lifecycle.ts
38406
38724
  function createMainBridgeWebSocketLifecycle(params) {
38407
38725
  const {
38408
38726
  state,
@@ -38448,13 +38766,26 @@ function createMainBridgeWebSocketLifecycle(params) {
38448
38766
  }
38449
38767
  }
38450
38768
  function handleClose(code, reason) {
38451
- const was = state.currentWs;
38452
- state.currentWs = null;
38453
- if (was) was.removeAllListeners();
38454
- const willReconnect = !state.closedByUser;
38455
- beginMainBridgeDeferredDisconnect(state, code, reason, logFn, willReconnect);
38456
- if (willReconnect) {
38457
- 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
+ }
38458
38789
  }
38459
38790
  }
38460
38791
  function connect() {
@@ -38464,7 +38795,10 @@ function createMainBridgeWebSocketLifecycle(params) {
38464
38795
  state.reconnectTimeout = null;
38465
38796
  }
38466
38797
  if (state.reconnectAttempt === 0) {
38467
- logFn("Connecting to bridge service\u2026");
38798
+ try {
38799
+ logFn("Connecting to bridge service\u2026");
38800
+ } catch {
38801
+ }
38468
38802
  }
38469
38803
  const prev = state.currentWs;
38470
38804
  if (prev) {
@@ -38480,53 +38814,84 @@ function createMainBridgeWebSocketLifecycle(params) {
38480
38814
  }
38481
38815
  state.currentWs = null;
38482
38816
  }
38483
- const url2 = buildBridgeUrl(apiUrl, workspaceId, tokens.accessToken);
38484
- state.currentWs = createWsBridge({
38485
- url: url2,
38486
- clientPingIntervalMs: CLI_WEBSOCKET_CLIENT_PING_MS,
38487
- onAuthInvalid: () => {
38488
- if (authRefreshInFlight) return;
38489
- void (async () => {
38490
- authRefreshInFlight = true;
38491
- try {
38492
- if (tokens.refreshToken) {
38493
- const next = await refreshBridgeTokens({
38494
- apiUrl,
38495
- workspaceId,
38496
- refreshToken: tokens.refreshToken
38497
- });
38498
- if (next?.token && next.refreshToken) {
38499
- tokens.accessToken = next.token;
38500
- tokens.refreshToken = next.refreshToken;
38501
- persistTokens?.({ token: tokens.accessToken, refreshToken: tokens.refreshToken });
38502
- logFn("[Bridge service] Access token refreshed; reconnecting\u2026");
38503
- state.reconnectAttempt = 0;
38504
- state.logBridgeOpenAsReconnect = true;
38505
- authRefreshInFlight = false;
38506
- connect();
38507
- 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
+ }
38508
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();
38509
38861
  }
38510
- authRefreshInFlight = false;
38511
- state.reconnectAttempt = 0;
38512
- 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);
38513
38872
  } catch {
38514
- authRefreshInFlight = false;
38515
- state.reconnectAttempt = 0;
38516
- onAuthInvalid();
38517
38873
  }
38518
- })();
38519
- },
38520
- onOpen: handleOpen,
38521
- onClose: handleClose,
38522
- onError: (err) => logCliWebSocketError(logFn, "[Bridge service]", err),
38523
- onMessage: (data) => handleBridgeMessage(data, messageDeps)
38524
- });
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
+ }
38525
38890
  }
38526
38891
  return { connect };
38527
38892
  }
38528
38893
 
38529
- // src/bridge/connection/create-bridge-connection.ts
38894
+ // src/connection/create-bridge-connection.ts
38530
38895
  async function createBridgeConnection(options) {
38531
38896
  const { apiUrl, workspaceId, justAuthenticated, onAuthInvalid, persistTokens } = options;
38532
38897
  const firehoseServerUrl = options.firehoseServerUrl ?? options.proxyServerUrl;
@@ -38538,16 +38903,20 @@ async function createBridgeConnection(options) {
38538
38903
  const state = {
38539
38904
  closedByUser: false,
38540
38905
  reconnectAttempt: 0,
38906
+ lastReconnectCloseMeta: null,
38541
38907
  logBridgeOpenAsReconnect: false,
38542
38908
  reconnectTimeout: null,
38543
38909
  currentWs: null,
38544
38910
  mainQuiet: createEmptyReconnectQuietSlot(),
38911
+ mainOutage: createEmptyReconnectOutageTracker(),
38545
38912
  firehoseHandle: null,
38546
38913
  lastFirehoseParams: null,
38547
38914
  firehoseReconnectTimeout: null,
38548
38915
  firehoseReconnectAttempt: 0,
38549
38916
  firehoseGeneration: 0,
38550
- firehoseQuiet: createEmptyReconnectQuietSlot()
38917
+ firehoseQuiet: createEmptyReconnectQuietSlot(),
38918
+ firehoseOutage: createEmptyReconnectOutageTracker(),
38919
+ lastFirehoseReconnectCloseMeta: null
38551
38920
  };
38552
38921
  const worktreesRootPath = options.worktreesRootPath ?? defaultWorktreesRootPath();
38553
38922
  const sessionWorktreeManager = new SessionWorktreeManager({
@@ -38809,6 +39178,9 @@ function colorize(color, message) {
38809
39178
  return `${color}${message}${RESET}`;
38810
39179
  }
38811
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");
38812
39184
  const positionalUrl = program2.args?.[0];
38813
39185
  const urlFromPositional = typeof positionalUrl === "string" && /^https?:\/\//i.test(positionalUrl) ? positionalUrl : void 0;
38814
39186
  const apiUrlFromCli = opts.apiUrl ?? urlFromPositional;
@@ -38900,7 +39272,7 @@ async function main() {
38900
39272
  "--e2ee-certificates-dir <path>",
38901
39273
  "Directory to load or generate E2EE keys for sessions, files, and logs",
38902
39274
  process.env.BUILDAUTOMATON_E2EE_CERTIFICATES_DIR
38903
- ).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));
38904
39276
  await program2.parseAsync(process.argv);
38905
39277
  }
38906
39278
  main().catch((err) => {