@buildautomaton/cli 0.1.32 → 0.1.33

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -23962,7 +23962,7 @@ function installBridgeProcessResilience() {
23962
23962
  }
23963
23963
 
23964
23964
  // src/cli-version.ts
23965
- var CLI_VERSION = "0.1.32".length > 0 ? "0.1.32" : "0.0.0-dev";
23965
+ var CLI_VERSION = "0.1.33".length > 0 ? "0.1.33" : "0.0.0-dev";
23966
23966
 
23967
23967
  // src/connection/heartbeat/constants.ts
23968
23968
  var BRIDGE_APP_HEARTBEAT_INTERVAL_MS = 1e4;
@@ -25022,6 +25022,30 @@ function runPendingAuth(options) {
25022
25022
  };
25023
25023
  }
25024
25024
 
25025
+ // src/dev-servers/manager/dev-server-constants.ts
25026
+ var BRIDGE_CLOSE_DEV_SERVER_GRACE_MS = 0;
25027
+ var BRIDGE_SHUTDOWN_GRACE_MS = 8e3;
25028
+
25029
+ // src/runtime/cli-process-interrupt.ts
25030
+ var cliImmediateShutdownRequested = false;
25031
+ function requestCliImmediateShutdown() {
25032
+ cliImmediateShutdownRequested = true;
25033
+ }
25034
+ function isCliImmediateShutdownRequested() {
25035
+ return cliImmediateShutdownRequested;
25036
+ }
25037
+ async function delayMsUnlessShutdownRequested(ms) {
25038
+ if (ms <= 0) return true;
25039
+ const end = Date.now() + ms;
25040
+ while (Date.now() < end) {
25041
+ if (isCliImmediateShutdownRequested()) return false;
25042
+ const chunk = Math.min(50, end - Date.now());
25043
+ if (chunk <= 0) break;
25044
+ await new Promise((r) => setTimeout(r, chunk));
25045
+ }
25046
+ return !isCliImmediateShutdownRequested();
25047
+ }
25048
+
25025
25049
  // src/sqlite/cli-database.ts
25026
25050
  import sqliteWasm from "node-sqlite3-wasm";
25027
25051
 
@@ -25325,6 +25349,12 @@ var { Database: SqliteDatabase } = sqliteWasm;
25325
25349
  var CLI_SQLITE_SYNC_RETRY_MAX = 40;
25326
25350
  var CLI_SQLITE_ASYNC_RETRY_MAX = 60;
25327
25351
  var CLI_SQLITE_ASYNC_BASE_DELAY_MS = 20;
25352
+ var CliSqliteInterrupted = class extends Error {
25353
+ name = "CliSqliteInterrupted";
25354
+ constructor() {
25355
+ super("CLI SQLite interrupted (shutdown)");
25356
+ }
25357
+ };
25328
25358
  function applyCliSqliteConcurrencyPragmas(db) {
25329
25359
  try {
25330
25360
  db.exec("PRAGMA journal_mode = WAL");
@@ -25335,7 +25365,7 @@ function applyCliSqliteConcurrencyPragmas(db) {
25335
25365
  } catch {
25336
25366
  }
25337
25367
  try {
25338
- db.run("PRAGMA busy_timeout = 8000");
25368
+ db.run("PRAGMA busy_timeout = 500");
25339
25369
  } catch {
25340
25370
  }
25341
25371
  }
@@ -25356,26 +25386,42 @@ function safeCloseCliSqliteDatabase(db) {
25356
25386
  function closeAllCliSqliteConnections() {
25357
25387
  }
25358
25388
  function isCliSqliteLockError(e) {
25389
+ if (e instanceof CliSqliteInterrupted) return false;
25359
25390
  const msg = e instanceof Error ? e.message : String(e);
25360
25391
  const lower = msg.toLowerCase();
25361
25392
  return lower.includes("database is locked") || lower.includes("sqlite_busy") || lower.includes("sqlite3_busy") || lower.includes("database") && lower.includes("locked");
25362
25393
  }
25363
25394
  function syncSleepMs(ms) {
25364
25395
  if (ms <= 0) return;
25365
- try {
25366
- const sab = new SharedArrayBuffer(4);
25367
- const ia = new Int32Array(sab);
25368
- Atomics.wait(ia, 0, 0, Math.min(ms, 1e4));
25369
- } catch {
25370
- const end = Date.now() + ms;
25371
- while (Date.now() < end) {
25396
+ const deadline = Date.now() + ms;
25397
+ while (Date.now() < deadline) {
25398
+ if (isCliImmediateShutdownRequested()) throw new CliSqliteInterrupted();
25399
+ const chunk = Math.min(80, deadline - Date.now());
25400
+ if (chunk <= 0) break;
25401
+ try {
25402
+ const sab = new SharedArrayBuffer(4);
25403
+ const ia = new Int32Array(sab);
25404
+ Atomics.wait(ia, 0, 0, chunk);
25405
+ } catch {
25406
+ const end = Date.now() + chunk;
25407
+ while (Date.now() < end) {
25408
+ if (isCliImmediateShutdownRequested()) throw new CliSqliteInterrupted();
25409
+ }
25372
25410
  }
25373
25411
  }
25374
25412
  }
25375
- function asyncDelayMs(ms) {
25376
- return new Promise((resolve18) => setTimeout(resolve18, ms));
25413
+ async function asyncDelayMsInterruptible(ms) {
25414
+ const end = Date.now() + ms;
25415
+ while (Date.now() < end) {
25416
+ if (isCliImmediateShutdownRequested()) throw new CliSqliteInterrupted();
25417
+ const chunk = Math.min(50, end - Date.now());
25418
+ if (chunk <= 0) break;
25419
+ await new Promise((r) => setTimeout(r, chunk));
25420
+ await yieldToEventLoop();
25421
+ }
25377
25422
  }
25378
25423
  function openCliSqliteConnection(options) {
25424
+ if (isCliImmediateShutdownRequested()) throw new CliSqliteInterrupted();
25379
25425
  const sqlitePath = getCliSqlitePath();
25380
25426
  ensureCliSqliteParentDir(sqlitePath);
25381
25427
  const db = new SqliteDatabase(sqlitePath);
@@ -25392,6 +25438,7 @@ function openCliSqliteConnection(options) {
25392
25438
  }
25393
25439
  function withCliSqliteSync(fn, options) {
25394
25440
  for (let attempt = 1; attempt <= CLI_SQLITE_SYNC_RETRY_MAX; attempt++) {
25441
+ if (isCliImmediateShutdownRequested()) throw new CliSqliteInterrupted();
25395
25442
  let db;
25396
25443
  try {
25397
25444
  db = openCliSqliteConnection(options);
@@ -25402,6 +25449,7 @@ function withCliSqliteSync(fn, options) {
25402
25449
  }
25403
25450
  } catch (e) {
25404
25451
  safeCloseCliSqliteDatabase(db);
25452
+ if (e instanceof CliSqliteInterrupted) throw e;
25405
25453
  if (!isCliSqliteLockError(e) || attempt === CLI_SQLITE_SYNC_RETRY_MAX) throw e;
25406
25454
  syncSleepMs(Math.min(500, 12 * attempt));
25407
25455
  }
@@ -25411,6 +25459,7 @@ function withCliSqliteSync(fn, options) {
25411
25459
  async function withCliSqlite(fn, options) {
25412
25460
  let lastError;
25413
25461
  for (let attempt = 1; attempt <= CLI_SQLITE_ASYNC_RETRY_MAX; attempt++) {
25462
+ if (isCliImmediateShutdownRequested()) throw new CliSqliteInterrupted();
25414
25463
  let db;
25415
25464
  try {
25416
25465
  db = openCliSqliteConnection(options);
@@ -25422,9 +25471,10 @@ async function withCliSqlite(fn, options) {
25422
25471
  } catch (e) {
25423
25472
  lastError = e;
25424
25473
  safeCloseCliSqliteDatabase(db);
25474
+ if (e instanceof CliSqliteInterrupted) throw e;
25425
25475
  if (!isCliSqliteLockError(e) || attempt === CLI_SQLITE_ASYNC_RETRY_MAX) throw e;
25426
25476
  const delayMs = Math.min(600, CLI_SQLITE_ASYNC_BASE_DELAY_MS * attempt);
25427
- await asyncDelayMs(delayMs);
25477
+ await asyncDelayMsInterruptible(delayMs);
25428
25478
  await yieldToEventLoop();
25429
25479
  }
25430
25480
  }
@@ -25437,9 +25487,9 @@ async function ensureCliSqliteInitialized(options) {
25437
25487
 
25438
25488
  // src/connection/close-bridge-connection.ts
25439
25489
  async function closeBridgeConnection(state, acpManager, devServerManager, log2) {
25490
+ requestCliImmediateShutdown();
25440
25491
  const say = log2 ?? logImmediate;
25441
25492
  say("Cleaning up connections\u2026");
25442
- await new Promise((resolve18) => setImmediate(resolve18));
25443
25493
  state.closedByUser = true;
25444
25494
  clearReconnectQuietTimer(state.mainQuiet);
25445
25495
  clearReconnectQuietTimer(state.firehoseQuiet);
@@ -25476,7 +25526,7 @@ async function closeBridgeConnection(state, acpManager, devServerManager, log2)
25476
25526
  }
25477
25527
  if (devServerManager) {
25478
25528
  say("Stopping local dev server processes\u2026");
25479
- await devServerManager.shutdownAllGraceful();
25529
+ await devServerManager.shutdownAllGraceful({ graceMs: BRIDGE_CLOSE_DEV_SERVER_GRACE_MS });
25480
25530
  }
25481
25531
  try {
25482
25532
  closeAllCliSqliteConnections();
@@ -30722,34 +30772,50 @@ __export(claude_code_acp_client_exports, {
30722
30772
  createClaudeCodeAcpClient: () => createClaudeCodeAcpClient,
30723
30773
  detectLocalAgentPresence: () => detectLocalAgentPresence
30724
30774
  });
30725
- import { execFile as execFile9 } from "node:child_process";
30726
- import { promisify as promisify9 } from "node:util";
30727
30775
 
30728
30776
  // src/agents/acp/clients/detect-command-on-path.ts
30729
30777
  import { execFile as execFile8 } from "node:child_process";
30730
30778
  import { promisify as promisify8 } from "node:util";
30731
30779
  var execFileAsync7 = promisify8(execFile8);
30732
- async function isCommandOnPath(command, timeoutMs = 4e3) {
30780
+ var COMMAND_ON_PATH_PROBE_TIMEOUT_MS = 750;
30781
+ async function execFileShutdownAware(file2, args, timeoutMs) {
30782
+ if (isCliImmediateShutdownRequested()) throw new Error("shutdown");
30783
+ const ac = new AbortController();
30784
+ const shutdownPoll = setInterval(() => {
30785
+ if (isCliImmediateShutdownRequested()) ac.abort();
30786
+ }, 50);
30787
+ shutdownPoll.unref?.();
30733
30788
  try {
30734
- await execFileAsync7("which", [command], { timeout: timeoutMs });
30789
+ await execFileAsync7(file2, args, { timeout: timeoutMs, signal: ac.signal });
30790
+ } finally {
30791
+ clearInterval(shutdownPoll);
30792
+ }
30793
+ }
30794
+ async function isCommandOnPath(command, timeoutMs = COMMAND_ON_PATH_PROBE_TIMEOUT_MS) {
30795
+ if (isCliImmediateShutdownRequested()) return false;
30796
+ try {
30797
+ await execFileShutdownAware("which", [command], timeoutMs);
30735
30798
  return true;
30736
30799
  } catch {
30737
30800
  return false;
30738
30801
  }
30739
30802
  }
30740
-
30741
- // src/agents/acp/clients/claude-code-acp-client.ts
30742
- var execFileAsync8 = promisify9(execFile9);
30743
- var BACKEND_LOCAL_AGENT_TYPE = "claude-code";
30744
- async function detectLocalAgentPresence() {
30745
- if (await isCommandOnPath("claude")) return true;
30803
+ async function execProbeShutdownAware(file2, args, timeoutMs) {
30804
+ if (isCliImmediateShutdownRequested()) return false;
30746
30805
  try {
30747
- await execFileAsync8("npx", ["--yes", "@anthropic-ai/claude-code", "--version"], { timeout: 25e3 });
30806
+ await execFileShutdownAware(file2, args, timeoutMs);
30748
30807
  return true;
30749
30808
  } catch {
30750
30809
  return false;
30751
30810
  }
30752
30811
  }
30812
+
30813
+ // src/agents/acp/clients/claude-code-acp-client.ts
30814
+ var BACKEND_LOCAL_AGENT_TYPE = "claude-code";
30815
+ async function detectLocalAgentPresence() {
30816
+ if (await isCommandOnPath("claude")) return true;
30817
+ return execProbeShutdownAware("npx", ["--yes", "@anthropic-ai/claude-code", "--version"], 3e3);
30818
+ }
30753
30819
  function buildClaudeCodeAcpSpawnCommand(base, _sessionMode) {
30754
30820
  return [...base];
30755
30821
  }
@@ -33577,30 +33643,39 @@ import path27 from "node:path";
33577
33643
  function shouldSkipWorkspaceWalkEntry(name) {
33578
33644
  return name.startsWith(".");
33579
33645
  }
33580
- function walkWorkspaceTreeSync(dir, baseDir, onFile) {
33646
+ async function walkWorkspaceTreeAsync(dir, baseDir, onFile, state) {
33581
33647
  let names;
33582
33648
  try {
33583
- names = fs24.readdirSync(dir);
33649
+ names = await fs24.promises.readdir(dir);
33584
33650
  } catch {
33585
33651
  return;
33586
33652
  }
33587
33653
  for (const name of names) {
33588
33654
  if (shouldSkipWorkspaceWalkEntry(name)) continue;
33655
+ if (state.n > 0 && state.n % INDEX_WORK_YIELD_EVERY === 0) {
33656
+ if (isCliImmediateShutdownRequested()) throw new CliSqliteInterrupted();
33657
+ await yieldToEventLoop();
33658
+ if (isCliImmediateShutdownRequested()) throw new CliSqliteInterrupted();
33659
+ }
33660
+ state.n++;
33589
33661
  const full = path27.join(dir, name);
33590
33662
  let stat2;
33591
33663
  try {
33592
- stat2 = fs24.statSync(full);
33664
+ stat2 = await fs24.promises.stat(full);
33593
33665
  } catch {
33594
33666
  continue;
33595
33667
  }
33596
33668
  const relative5 = path27.relative(baseDir, full).replace(/\\/g, "/");
33597
33669
  if (stat2.isDirectory()) {
33598
- walkWorkspaceTreeSync(full, baseDir, onFile);
33670
+ await walkWorkspaceTreeAsync(full, baseDir, onFile, state);
33599
33671
  } else if (stat2.isFile()) {
33600
33672
  onFile(relative5);
33601
33673
  }
33602
33674
  }
33603
33675
  }
33676
+ function createWalkYieldState() {
33677
+ return { n: 0 };
33678
+ }
33604
33679
 
33605
33680
  // src/files/index/file-index-sqlite-lock.ts
33606
33681
  import fs25 from "node:fs";
@@ -33633,29 +33708,28 @@ function withFileIndexSqliteLock(fn) {
33633
33708
  }
33634
33709
 
33635
33710
  // src/files/index/build-file-index.ts
33636
- var FILE_INDEX_INSERT_BUFFER = 2048;
33637
- function persistFileIndexForResolvedCwd(resolved) {
33711
+ var FILE_INDEX_INTERRUPT_CHECK_EVERY = 256;
33712
+ function assertNotShutdown() {
33713
+ if (isCliImmediateShutdownRequested()) throw new CliSqliteInterrupted();
33714
+ }
33715
+ function persistFileIndexPaths(resolved, paths) {
33638
33716
  return withCliSqliteSync((db) => {
33639
33717
  const h = getCwdHashForFileIndex(resolved);
33640
- const buf = [];
33641
33718
  let pathCount = 0;
33642
33719
  db.run("BEGIN IMMEDIATE");
33643
33720
  try {
33644
33721
  db.run("DELETE FROM file_index_path WHERE cwd_hash = ?", [h]);
33645
33722
  const ins = db.prepare("INSERT INTO file_index_path (cwd_hash, path) VALUES (?, ?)");
33646
33723
  try {
33647
- const flushBuf = () => {
33648
- for (const rel of buf) {
33649
- ins.run([h, rel]);
33724
+ let batch = 0;
33725
+ for (const rel of paths) {
33726
+ if (++batch >= FILE_INDEX_INTERRUPT_CHECK_EVERY) {
33727
+ batch = 0;
33728
+ assertNotShutdown();
33650
33729
  }
33651
- pathCount += buf.length;
33652
- buf.length = 0;
33653
- };
33654
- walkWorkspaceTreeSync(resolved, resolved, (rel) => {
33655
- buf.push(rel);
33656
- if (buf.length >= FILE_INDEX_INSERT_BUFFER) flushBuf();
33657
- });
33658
- flushBuf();
33730
+ ins.run([h, rel]);
33731
+ pathCount += 1;
33732
+ }
33659
33733
  } finally {
33660
33734
  ins.finalize();
33661
33735
  }
@@ -33670,12 +33744,24 @@ function persistFileIndexForResolvedCwd(resolved) {
33670
33744
  return pathCount;
33671
33745
  });
33672
33746
  }
33747
+ async function collectWorkspacePathsAsync(resolved) {
33748
+ const paths = [];
33749
+ const state = createWalkYieldState();
33750
+ await walkWorkspaceTreeAsync(resolved, resolved, (rel) => {
33751
+ assertNotShutdown();
33752
+ paths.push(rel);
33753
+ }, state);
33754
+ return paths;
33755
+ }
33673
33756
  async function buildFileIndexAsync(cwd) {
33674
33757
  return withFileIndexSqliteLock(async () => {
33675
33758
  const resolved = path28.resolve(cwd);
33676
33759
  await yieldToEventLoop();
33677
- const pathCount = persistFileIndexForResolvedCwd(resolved);
33760
+ assertNotShutdown();
33761
+ const paths = await collectWorkspacePathsAsync(resolved);
33678
33762
  await yieldToEventLoop();
33763
+ assertNotShutdown();
33764
+ const pathCount = persistFileIndexPaths(resolved, paths);
33679
33765
  return { pathCount };
33680
33766
  });
33681
33767
  }
@@ -33779,11 +33865,13 @@ function createFsWatcher(resolved, schedule) {
33779
33865
  function startFileIndexWatcher(cwd = getBridgeRoot()) {
33780
33866
  const resolved = path30.resolve(cwd);
33781
33867
  void buildFileIndexAsync(resolved).catch((e) => {
33868
+ if (e instanceof CliSqliteInterrupted) return;
33782
33869
  console.error("[file-index] Initial index build failed:", e);
33783
33870
  });
33784
33871
  let timer = null;
33785
33872
  const runRebuild = () => {
33786
33873
  void buildFileIndexAsync(resolved).catch((e) => {
33874
+ if (e instanceof CliSqliteInterrupted) return;
33787
33875
  console.error("[file-index] Watch rebuild failed:", e);
33788
33876
  });
33789
33877
  };
@@ -34317,9 +34405,6 @@ var StreamTail = class {
34317
34405
  }
34318
34406
  };
34319
34407
 
34320
- // src/dev-servers/manager/dev-server-constants.ts
34321
- var BRIDGE_SHUTDOWN_GRACE_MS = 8e3;
34322
-
34323
34408
  // src/dev-servers/manager/dev-server-firehose-messages.ts
34324
34409
  function buildFirehoseSnapshotMessage(params) {
34325
34410
  const payload = {
@@ -34605,22 +34690,23 @@ var DevServerManager = class {
34605
34690
  }
34606
34691
  this.start(serverId);
34607
34692
  }
34608
- async shutdownAllGraceful() {
34693
+ async shutdownAllGraceful(opts) {
34694
+ const graceMs = opts?.graceMs ?? BRIDGE_SHUTDOWN_GRACE_MS;
34609
34695
  const pairs = [...this.processes.entries()];
34610
34696
  if (pairs.length === 0) return;
34611
34697
  this.log(
34612
34698
  `[dev-server] Stopping ${pairs.length} local dev server process${pairs.length === 1 ? "" : "es"}\u2026`
34613
34699
  );
34614
- await Promise.all(pairs.map(([serverId, proc]) => this.gracefulTerminateOrUnknown(serverId, proc)));
34700
+ await Promise.all(pairs.map(([serverId, proc]) => this.gracefulTerminateOrUnknown(serverId, proc, graceMs)));
34615
34701
  }
34616
- async gracefulTerminateOrUnknown(serverId, proc) {
34702
+ async gracefulTerminateOrUnknown(serverId, proc, graceMs) {
34617
34703
  const shortId = `${serverId.slice(0, 8)}\u2026`;
34618
- await sigtermAndWaitForExit(proc, BRIDGE_SHUTDOWN_GRACE_MS, this.log, shortId);
34704
+ await sigtermAndWaitForExit(proc, graceMs, this.log, shortId);
34619
34705
  if (!this.processes.has(serverId) || this.processes.get(serverId) !== proc) {
34620
34706
  return;
34621
34707
  }
34622
34708
  this.bumpGeneration(serverId);
34623
- forceKillChild(proc, this.log, shortId, BRIDGE_SHUTDOWN_GRACE_MS);
34709
+ forceKillChild(proc, this.log, shortId, graceMs);
34624
34710
  this.processes.delete(serverId);
34625
34711
  this.clearPoll(serverId);
34626
34712
  this.pipedCaptureByServerId.delete(serverId);
@@ -35047,10 +35133,13 @@ var LOCAL_AGENT_ACP_MODULES = [
35047
35133
  ];
35048
35134
  async function detectLocalAgentTypes() {
35049
35135
  try {
35136
+ if (isCliImmediateShutdownRequested()) return [];
35050
35137
  const out = [];
35051
35138
  for (let i = 0; i < LOCAL_AGENT_ACP_MODULES.length; i++) {
35139
+ if (isCliImmediateShutdownRequested()) return out;
35052
35140
  if (i > 0) {
35053
35141
  await yieldToEventLoop();
35142
+ if (isCliImmediateShutdownRequested()) return out;
35054
35143
  }
35055
35144
  const mod = LOCAL_AGENT_ACP_MODULES[i];
35056
35145
  try {
@@ -35083,6 +35172,7 @@ function createSendLocalSkillsReport(getWs, logFn) {
35083
35172
  }
35084
35173
  function createReportAutoDetectedAgents(getWs, logFn) {
35085
35174
  return async () => {
35175
+ if (isCliImmediateShutdownRequested()) return;
35086
35176
  try {
35087
35177
  const types = await detectLocalAgentTypes();
35088
35178
  const socket = getWs();
@@ -35182,6 +35272,7 @@ var handleBridgeIdentified = (msg, deps) => {
35182
35272
  });
35183
35273
  setImmediate(() => {
35184
35274
  void (async () => {
35275
+ if (isCliImmediateShutdownRequested()) return;
35185
35276
  try {
35186
35277
  await deps.reportAutoDetectedAgents?.();
35187
35278
  } catch (e) {
@@ -35189,6 +35280,7 @@ var handleBridgeIdentified = (msg, deps) => {
35189
35280
  `[Bridge service] Auto-detect agents failed: ${e instanceof Error ? e.message : String(e)}`
35190
35281
  );
35191
35282
  }
35283
+ if (isCliImmediateShutdownRequested()) return;
35192
35284
  try {
35193
35285
  await deps.warmupAgentCapabilitiesOnConnect?.();
35194
35286
  } catch (e) {
@@ -35199,6 +35291,7 @@ var handleBridgeIdentified = (msg, deps) => {
35199
35291
  })();
35200
35292
  });
35201
35293
  setImmediate(() => {
35294
+ if (isCliImmediateShutdownRequested()) return;
35202
35295
  try {
35203
35296
  deps.sendLocalSkillsReport?.();
35204
35297
  } catch (e) {
@@ -35500,12 +35593,12 @@ function createBridgePromptSenders(deps, getWs) {
35500
35593
  }
35501
35594
 
35502
35595
  // src/agents/acp/from-bridge/bridge-prompt-preamble.ts
35503
- import { execFile as execFile10 } from "node:child_process";
35504
- import { promisify as promisify10 } from "node:util";
35505
- var execFileAsync9 = promisify10(execFile10);
35596
+ import { execFile as execFile9 } from "node:child_process";
35597
+ import { promisify as promisify9 } from "node:util";
35598
+ var execFileAsync8 = promisify9(execFile9);
35506
35599
  async function readGitBranch(cwd) {
35507
35600
  try {
35508
- const { stdout } = await execFileAsync9("git", ["branch", "--show-current"], { cwd, maxBuffer: 64 * 1024 });
35601
+ const { stdout } = await execFileAsync8("git", ["branch", "--show-current"], { cwd, maxBuffer: 64 * 1024 });
35509
35602
  const b = stdout.trim();
35510
35603
  return b || null;
35511
35604
  } catch {
@@ -36876,6 +36969,7 @@ import * as path39 from "node:path";
36876
36969
  import * as path38 from "node:path";
36877
36970
  async function probeOneAgentTypeForCapabilities(params) {
36878
36971
  const { agentType, cwd, workspaceId, log: log2, reportAgentCapabilities, bridgeReport = true } = params;
36972
+ if (isCliImmediateShutdownRequested()) return false;
36879
36973
  const resolved = resolveAgentCommand(agentType);
36880
36974
  if (!resolved) return false;
36881
36975
  let sqliteChanged = false;
@@ -36909,6 +37003,7 @@ async function probeOneAgentTypeForCapabilities(params) {
36909
37003
  }, 28e3);
36910
37004
  killTimer.unref?.();
36911
37005
  try {
37006
+ if (isCliImmediateShutdownRequested()) return false;
36912
37007
  handle = await resolved.createClient({
36913
37008
  command: resolved.command,
36914
37009
  cwd: path38.resolve(cwd),
@@ -36928,7 +37023,7 @@ async function probeOneAgentTypeForCapabilities(params) {
36928
37023
  onSessionUpdate: () => {
36929
37024
  }
36930
37025
  });
36931
- await new Promise((r) => setTimeout(r, 1200));
37026
+ if (!await delayMsUnlessShutdownRequested(1200)) return false;
36932
37027
  } catch (e) {
36933
37028
  log2(
36934
37029
  `[Bridge service] Agent capability probe (${agentType}): ${e instanceof Error ? e.message : String(e)}`
@@ -36956,6 +37051,7 @@ async function probeAgentCapabilitiesForDetectedTypes(params) {
36956
37051
  } = params;
36957
37052
  let changedCount = 0;
36958
37053
  for (let i = 0; i < agentTypes.length; i++) {
37054
+ if (isCliImmediateShutdownRequested()) return changedCount;
36959
37055
  if (i > 0) await yieldToEventLoop();
36960
37056
  const agentType = agentTypes[i];
36961
37057
  if (!agentType.trim()) continue;
@@ -36983,6 +37079,7 @@ async function probeAgentCapabilitiesForDetectedTypes(params) {
36983
37079
  // src/agents/capabilities/warmup-agent-capabilities-on-connect.ts
36984
37080
  async function warmupAgentCapabilitiesOnConnect(params) {
36985
37081
  const { workspaceId, log: log2, getWs } = params;
37082
+ if (isCliImmediateShutdownRequested()) return;
36986
37083
  const cwd = path39.resolve(getBridgeRoot());
36987
37084
  async function sendBatchFromCache() {
36988
37085
  const socket = getWs();
@@ -36995,18 +37092,21 @@ async function warmupAgentCapabilitiesOnConnect(params) {
36995
37092
  items: rows.map((r) => ({ agentType: r.agentType, configOptions: r.configOptions }))
36996
37093
  });
36997
37094
  } catch (e) {
37095
+ if (e instanceof CliSqliteInterrupted) return;
36998
37096
  log2(
36999
37097
  `[Bridge service] Agent capability batch to bridge failed: ${e instanceof Error ? e.message : String(e)}`
37000
37098
  );
37001
37099
  }
37002
37100
  }
37003
37101
  await sendBatchFromCache();
37102
+ if (isCliImmediateShutdownRequested()) return;
37004
37103
  let types = [];
37005
37104
  try {
37006
37105
  types = [...await detectLocalAgentTypes()];
37007
37106
  } catch (e) {
37008
37107
  log2(`[Bridge service] detectLocalAgentTypes failed: ${e instanceof Error ? e.message : String(e)}`);
37009
37108
  }
37109
+ if (isCliImmediateShutdownRequested()) return;
37010
37110
  try {
37011
37111
  const n = await probeAgentCapabilitiesForDetectedTypes({
37012
37112
  agentTypes: types,
@@ -37018,11 +37118,13 @@ async function warmupAgentCapabilitiesOnConnect(params) {
37018
37118
  });
37019
37119
  if (n > 0) await sendBatchFromCache();
37020
37120
  } catch (e) {
37121
+ if (e instanceof CliSqliteInterrupted) return;
37021
37122
  log2(`[Bridge service] Agent capability probe (missing cache) failed: ${e instanceof Error ? e.message : String(e)}`);
37022
37123
  }
37023
37124
  void (async () => {
37024
37125
  try {
37025
37126
  await yieldToEventLoop();
37127
+ if (isCliImmediateShutdownRequested()) return;
37026
37128
  const n = await probeAgentCapabilitiesForDetectedTypes({
37027
37129
  agentTypes: types,
37028
37130
  cwd,
@@ -37033,6 +37135,7 @@ async function warmupAgentCapabilitiesOnConnect(params) {
37033
37135
  });
37034
37136
  if (n > 0) await sendBatchFromCache();
37035
37137
  } catch (e) {
37138
+ if (e instanceof CliSqliteInterrupted) return;
37036
37139
  log2(`[Bridge service] Agent capability lazy refresh failed: ${e instanceof Error ? e.message : String(e)}`);
37037
37140
  }
37038
37141
  })();
@@ -37080,7 +37183,8 @@ async function createBridgeConnection(options) {
37080
37183
  configOptions: info.configOptions
37081
37184
  })
37082
37185
  );
37083
- } catch {
37186
+ } catch (e) {
37187
+ if (e instanceof CliSqliteInterrupted) return;
37084
37188
  }
37085
37189
  if (!changed) return;
37086
37190
  const socket = getWs();
@@ -37163,6 +37267,7 @@ async function createBridgeConnection(options) {
37163
37267
  const stopFileIndexWatcher = startFileIndexWatcher(getBridgeRoot());
37164
37268
  return {
37165
37269
  close: async () => {
37270
+ requestCliImmediateShutdown();
37166
37271
  stopFileIndexWatcher();
37167
37272
  bridgeHeartbeat.stop();
37168
37273
  await closeBridgeConnection(state, acpManager, devServerManager, logFn);
@@ -37236,47 +37341,60 @@ async function runConnectedBridge(options, restartWithoutAuth) {
37236
37341
  } = options;
37237
37342
  const firehoseServerUrl = options.firehoseServerUrl ?? options.proxyServerUrl;
37238
37343
  let cleanupKeyCommand;
37239
- const handle = await createBridgeConnection({
37240
- apiUrl,
37241
- workspaceId,
37242
- authToken,
37243
- refreshToken,
37244
- firehoseServerUrl,
37245
- justAuthenticated,
37246
- worktreesRootPath,
37247
- e2eCertificate,
37248
- log,
37249
- persistTokens: (t) => {
37250
- writeConfigForApi(apiUrl, {
37251
- workspaceId,
37252
- token: t.token,
37253
- refreshToken: t.refreshToken
37254
- });
37255
- },
37256
- onAuthInvalid: () => {
37257
- cleanupKeyCommand?.();
37258
- log("[Bridge service] Access token invalid or revoked; re-authenticating\u2026");
37259
- clearConfigForApi(apiUrl);
37260
- void handle.close().then(() => {
37261
- void restartWithoutAuth({ apiUrl, firehoseServerUrl, worktreesRootPath, e2eCertificate });
37262
- });
37263
- }
37264
- });
37344
+ let bridgeClose = null;
37265
37345
  const onSignal = (kind) => {
37346
+ requestCliImmediateShutdown();
37266
37347
  cleanupKeyCommand?.();
37267
37348
  logImmediate(
37268
37349
  kind === "interrupt" ? "Keyboard interrupt (Ctrl+C) \u2014 stopping\u2026" : "Stop requested \u2014 shutting down\u2026"
37269
37350
  );
37270
- setImmediate(() => {
37271
- void handle.close().then(() => {
37272
- process.exit(0);
37273
- });
37274
- });
37351
+ if (bridgeClose) {
37352
+ void bridgeClose().then(() => process.exit(0));
37353
+ return;
37354
+ }
37355
+ process.exit(0);
37275
37356
  };
37276
37357
  const onSigInt = () => onSignal("interrupt");
37277
37358
  const onSigTerm = () => onSignal("stop");
37278
37359
  process.on("SIGINT", onSigInt);
37279
37360
  process.on("SIGTERM", onSigTerm);
37361
+ let handle;
37362
+ try {
37363
+ handle = await createBridgeConnection({
37364
+ apiUrl,
37365
+ workspaceId,
37366
+ authToken,
37367
+ refreshToken,
37368
+ firehoseServerUrl,
37369
+ justAuthenticated,
37370
+ worktreesRootPath,
37371
+ e2eCertificate,
37372
+ log,
37373
+ persistTokens: (t) => {
37374
+ writeConfigForApi(apiUrl, {
37375
+ workspaceId,
37376
+ token: t.token,
37377
+ refreshToken: t.refreshToken
37378
+ });
37379
+ },
37380
+ onAuthInvalid: () => {
37381
+ cleanupKeyCommand?.();
37382
+ log("[Bridge service] Access token invalid or revoked; re-authenticating\u2026");
37383
+ clearConfigForApi(apiUrl);
37384
+ void handle.close().then(() => {
37385
+ void restartWithoutAuth({ apiUrl, firehoseServerUrl, worktreesRootPath, e2eCertificate });
37386
+ });
37387
+ }
37388
+ });
37389
+ } catch (e) {
37390
+ process.off("SIGINT", onSigInt);
37391
+ process.off("SIGTERM", onSigTerm);
37392
+ if (e instanceof CliSqliteInterrupted) {
37393
+ process.exit(0);
37394
+ }
37395
+ throw e;
37396
+ }
37397
+ bridgeClose = () => handle.close();
37280
37398
  if (e2eCertificate) {
37281
37399
  let openingCertificate = false;
37282
37400
  cleanupKeyCommand = installE2eCertificateKeyCommand({
@@ -37321,6 +37439,7 @@ async function runBridge(options) {
37321
37439
  }
37322
37440
  });
37323
37441
  const onSignal = (kind) => {
37442
+ requestCliImmediateShutdown();
37324
37443
  logImmediate(
37325
37444
  kind === "interrupt" ? "Keyboard interrupt (Ctrl+C) \u2014 stopping\u2026" : "Stop requested \u2014 shutting down\u2026"
37326
37445
  );