@buildautomaton/cli 0.1.35 → 0.1.37

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
@@ -974,7 +974,7 @@ var require_command = __commonJS({
974
974
  var EventEmitter2 = __require("node:events").EventEmitter;
975
975
  var childProcess2 = __require("node:child_process");
976
976
  var path43 = __require("node:path");
977
- var fs38 = __require("node:fs");
977
+ var fs41 = __require("node:fs");
978
978
  var process8 = __require("node:process");
979
979
  var { Argument: Argument2, humanReadableArgName } = require_argument();
980
980
  var { CommanderError: CommanderError2 } = require_error();
@@ -1907,10 +1907,10 @@ Expecting one of '${allowedValues.join("', '")}'`);
1907
1907
  const sourceExt = [".js", ".ts", ".tsx", ".mjs", ".cjs"];
1908
1908
  function findFile(baseDir, baseName) {
1909
1909
  const localBin = path43.resolve(baseDir, baseName);
1910
- if (fs38.existsSync(localBin)) return localBin;
1910
+ if (fs41.existsSync(localBin)) return localBin;
1911
1911
  if (sourceExt.includes(path43.extname(baseName))) return void 0;
1912
1912
  const foundExt = sourceExt.find(
1913
- (ext) => fs38.existsSync(`${localBin}${ext}`)
1913
+ (ext) => fs41.existsSync(`${localBin}${ext}`)
1914
1914
  );
1915
1915
  if (foundExt) return `${localBin}${foundExt}`;
1916
1916
  return void 0;
@@ -1922,7 +1922,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
1922
1922
  if (this._scriptPath) {
1923
1923
  let resolvedScriptPath;
1924
1924
  try {
1925
- resolvedScriptPath = fs38.realpathSync(this._scriptPath);
1925
+ resolvedScriptPath = fs41.realpathSync(this._scriptPath);
1926
1926
  } catch (err) {
1927
1927
  resolvedScriptPath = this._scriptPath;
1928
1928
  }
@@ -25064,14 +25064,14 @@ var {
25064
25064
  } = import_index.default;
25065
25065
 
25066
25066
  // src/cli-version.ts
25067
- var CLI_VERSION = "0.1.35".length > 0 ? "0.1.35" : "0.0.0-dev";
25067
+ var CLI_VERSION = "0.1.37".length > 0 ? "0.1.37" : "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";
25071
25071
  var DEFAULT_FIREHOSE_URL = "https://buildautomaton-firehose.fly.dev";
25072
25072
 
25073
25073
  // src/cli/run-cli-action.ts
25074
- import * as fs37 from "node:fs";
25074
+ import * as fs40 from "node:fs";
25075
25075
  import * as path42 from "node:path";
25076
25076
 
25077
25077
  // src/cli-log-level.ts
@@ -26961,6 +26961,7 @@ function recordMigrationAndPruneCheckpointLegacy(db, migration, applied2) {
26961
26961
  var CHECKPOINT_V1 = "001_cli_sqlite_checkpoint_v1";
26962
26962
  var CHECKPOINT_V1_SQL = readCliSqliteMigrationSql("001_cli_sqlite_checkpoint_v1.sql");
26963
26963
  var AGENT_CAPABILITIES_SQL = readCliSqliteMigrationSql("002_agent_capabilities.sql");
26964
+ var FILE_INDEX_PARENT_PATHS_SQL = readCliSqliteMigrationSql("003_file_index_parent_paths.sql");
26964
26965
  function agentCapabilitiesTableState(db) {
26965
26966
  const rows = db.all(
26966
26967
  `SELECT name FROM sqlite_master WHERE type='table' AND name IN ('agent_capabilities', 'agent_capability_cache')`
@@ -26994,6 +26995,18 @@ var CLI_SQLITE_MIGRATIONS = [
26994
26995
  db.exec(AGENT_CAPABILITIES_SQL);
26995
26996
  },
26996
26997
  alreadyApplied: (db) => agentCapabilitiesTableState(db) === "current"
26998
+ },
26999
+ {
27000
+ name: "003_file_index_parent_paths",
27001
+ migrate: (db) => {
27002
+ db.exec(FILE_INDEX_PARENT_PATHS_SQL);
27003
+ },
27004
+ alreadyApplied: (db) => {
27005
+ const row = db.get(
27006
+ `SELECT 1 as ok FROM sqlite_master WHERE type='table' AND name='file_index_parent_path' LIMIT 1`
27007
+ );
27008
+ return row != null;
27009
+ }
26997
27010
  }
26998
27011
  ];
26999
27012
  function migrateCliSqlite(db) {
@@ -27032,6 +27045,10 @@ var CliSqliteInterrupted = class extends Error {
27032
27045
  }
27033
27046
  };
27034
27047
  function applyCliSqliteConcurrencyPragmas(db) {
27048
+ try {
27049
+ db.run("PRAGMA foreign_keys = ON");
27050
+ } catch {
27051
+ }
27035
27052
  try {
27036
27053
  db.exec("PRAGMA journal_mode = WAL");
27037
27054
  } catch {
@@ -27694,6 +27711,16 @@ function isClaudeCodePermissionMode(value) {
27694
27711
  return MODE_SET.has(value);
27695
27712
  }
27696
27713
 
27714
+ // ../types/src/codex-permission-mode.ts
27715
+ function isCodexPermissionMode(value) {
27716
+ return value.trim() !== "";
27717
+ }
27718
+ function normalizeCodexPermissionModeInput(raw) {
27719
+ if (typeof raw !== "string") return null;
27720
+ const t = raw.trim();
27721
+ return isCodexPermissionMode(t) ? t : null;
27722
+ }
27723
+
27697
27724
  // ../types/src/cli-permission-mode.ts
27698
27725
  var CLI_PERMISSION_MODE_DEFAULT = "default";
27699
27726
  var CLI_PERMISSION_MODE_DANGEROUS = "dangerous";
@@ -27765,6 +27792,7 @@ function buildCliAutoApprovedPermissionRpcResult(requestParams) {
27765
27792
  // ../types/src/agent-config.ts
27766
27793
  var AGENT_CONFIG_CLAUDE_PERMISSION_MODE_KEY = "claude_permission_mode";
27767
27794
  var AGENT_CONFIG_CLI_PERMISSION_MODE_KEY = "cli_permission_mode";
27795
+ var AGENT_CONFIG_CODEX_PERMISSION_MODE_KEY = "codex_permission_mode";
27768
27796
  var AGENT_CONFIG_AGENT_MODEL_KEY = "agent_model";
27769
27797
  function getClaudePermissionModeFromAgentConfig(config2) {
27770
27798
  if (!config2) return null;
@@ -27777,6 +27805,10 @@ function getCliPermissionModeFromAgentConfig(config2) {
27777
27805
  if (!config2) return CLI_PERMISSION_MODE_DEFAULT;
27778
27806
  return normalizeCliPermissionModeInput(config2[AGENT_CONFIG_CLI_PERMISSION_MODE_KEY]);
27779
27807
  }
27808
+ function getCodexPermissionModeFromAgentConfig(config2) {
27809
+ if (!config2) return null;
27810
+ return normalizeCodexPermissionModeInput(config2[AGENT_CONFIG_CODEX_PERMISSION_MODE_KEY]);
27811
+ }
27780
27812
  function getAgentModelFromAgentConfig(config2) {
27781
27813
  if (!config2) return null;
27782
27814
  const cur = config2[AGENT_CONFIG_AGENT_MODEL_KEY];
@@ -33377,8 +33409,60 @@ async function applyClaudePermissionFromAcpSession(params) {
33377
33409
  }
33378
33410
  }
33379
33411
 
33380
- // src/agents/acp/apply-acp-model-from-agent-session.ts
33412
+ // src/agents/acp/codex-acp-permission-from-session.ts
33381
33413
  function flattenSelectOptions2(options) {
33414
+ if (options == null || options.length === 0) return [];
33415
+ const first2 = options[0];
33416
+ if (first2 != null && typeof first2 === "object" && "group" in first2 && first2.group != null) {
33417
+ return options.flatMap(
33418
+ (g) => Array.isArray(g.options) ? g.options : []
33419
+ );
33420
+ }
33421
+ return options;
33422
+ }
33423
+ function pickModeConfigOption2(configOptions) {
33424
+ if (configOptions == null || configOptions.length === 0) return null;
33425
+ const byCategory = configOptions.find((o) => o.category === "mode");
33426
+ if (byCategory) return byCategory;
33427
+ return configOptions.find((o) => o.id === "mode") ?? null;
33428
+ }
33429
+ async function applyCodexPermissionFromAcpSession(params) {
33430
+ const { sessionId, agentConfig, configOptions, modes, setSessionConfigOption, setSessionMode, logDebug: logDebug2 } = params;
33431
+ const desiredMode = getCodexPermissionModeFromAgentConfig(agentConfig);
33432
+ if (desiredMode == null) return;
33433
+ const modeOpt = pickModeConfigOption2(configOptions ?? null);
33434
+ if (modeOpt != null) {
33435
+ const flat = flattenSelectOptions2(modeOpt.options);
33436
+ const allowed = flat.some((o) => o.value === desiredMode);
33437
+ if (allowed && modeOpt.currentValue !== desiredMode) {
33438
+ try {
33439
+ logDebug2(
33440
+ `[Agent] Codex: sending ACP session/set_config_option (mode) configId=${JSON.stringify(modeOpt.id)} value=${JSON.stringify(desiredMode)} was=${JSON.stringify(modeOpt.currentValue)} sessionId=${sessionId.slice(0, 8)}\u2026`
33441
+ );
33442
+ await setSessionConfigOption({ sessionId, configId: modeOpt.id, value: desiredMode });
33443
+ } catch (e) {
33444
+ logDebug2(`[Agent] Codex: session/set_config_option failed: ${e instanceof Error ? e.message : String(e)}`);
33445
+ }
33446
+ }
33447
+ return;
33448
+ }
33449
+ if (modes?.availableModes?.length) {
33450
+ const allowed = modes.availableModes.some((m) => m.id === desiredMode);
33451
+ if (allowed && desiredMode !== modes.currentModeId) {
33452
+ try {
33453
+ logDebug2(
33454
+ `[Agent] Codex: sending ACP session/set_mode modeId=${JSON.stringify(desiredMode)} was=${JSON.stringify(modes.currentModeId ?? null)} sessionId=${sessionId.slice(0, 8)}\u2026`
33455
+ );
33456
+ await setSessionMode({ sessionId, modeId: desiredMode });
33457
+ } catch (e) {
33458
+ logDebug2(`[Agent] Codex: session/set_mode failed: ${e instanceof Error ? e.message : String(e)}`);
33459
+ }
33460
+ }
33461
+ }
33462
+ }
33463
+
33464
+ // src/agents/acp/apply-acp-model-from-agent-session.ts
33465
+ function flattenSelectOptions3(options) {
33382
33466
  if (options == null || options.length === 0) return [];
33383
33467
  const first2 = options[0];
33384
33468
  if (first2 != null && typeof first2 === "object" && "group" in first2 && first2.group != null) {
@@ -33403,7 +33487,7 @@ async function applyAcpModelFromAcpSession(params) {
33403
33487
  if (desired == null) return;
33404
33488
  const modelOpt = pickModelConfigOption(configOptions ?? null);
33405
33489
  if (modelOpt == null) return;
33406
- const flat = flattenSelectOptions2(modelOpt.options);
33490
+ const flat = flattenSelectOptions3(modelOpt.options);
33407
33491
  const allowed = flat.some((o) => o.value === desired);
33408
33492
  if (!allowed) return;
33409
33493
  if (modelOpt.currentValue === desired) return;
@@ -33483,12 +33567,41 @@ function parseAcpInitAgentCapabilities(initResult) {
33483
33567
  }
33484
33568
 
33485
33569
  // src/agents/acp/clients/shared/bootstrap-acp-wire-session.ts
33570
+ function configOptionsWithModes(configOptions, modes) {
33571
+ const modeState = modes && typeof modes === "object" ? modes : null;
33572
+ if (!modeState?.availableModes?.length) return configOptions;
33573
+ const hasModeConfig = Array.isArray(configOptions) && configOptions.some((raw) => {
33574
+ if (raw == null || typeof raw !== "object" || Array.isArray(raw)) return false;
33575
+ const o = raw;
33576
+ return o.category === "mode" || o.id === "mode";
33577
+ });
33578
+ if (hasModeConfig) return configOptions;
33579
+ return [
33580
+ ...configOptions ?? [],
33581
+ {
33582
+ id: "mode",
33583
+ name: "Mode",
33584
+ type: "select",
33585
+ category: "mode",
33586
+ currentValue: modeState.currentModeId ?? null,
33587
+ options: modeState.availableModes.map((m) => {
33588
+ const r = m;
33589
+ return {
33590
+ value: m.id,
33591
+ name: m.name ?? m.id,
33592
+ ...typeof r.description === "string" && r.description.trim() !== "" ? { description: r.description.trim() } : {}
33593
+ };
33594
+ })
33595
+ }
33596
+ ];
33597
+ }
33486
33598
  async function bootstrapAcpWireSession(transport, ctx, initializeRequest) {
33487
33599
  const initResult = await transport.initialize(initializeRequest);
33488
33600
  const { canResume, canLoad, promptSupportsImage } = parseAcpInitAgentCapabilities(initResult);
33489
33601
  ctx.agentPromptImageSupported = promptSupportsImage;
33490
33602
  await transport.afterInitialize?.();
33491
33603
  const established = await establishAcpSessionWithTransport(transport, ctx, canResume, canLoad);
33604
+ established.configOptions = configOptionsWithModes(established.configOptions, established.modes);
33492
33605
  const sessionId = established.sessionId;
33493
33606
  ctx.onAcpSessionEstablished?.({
33494
33607
  acpSessionId: sessionId,
@@ -33511,6 +33624,22 @@ async function bootstrapAcpWireSession(transport, ctx, initializeRequest) {
33511
33624
  logDebug: ctx.logDebug
33512
33625
  });
33513
33626
  }
33627
+ if (ctx.backendAgentType === "codex-acp") {
33628
+ const cfg = ctx.agentConfig != null && typeof ctx.agentConfig === "object" && !Array.isArray(ctx.agentConfig) ? ctx.agentConfig : null;
33629
+ const configOptionsTyped = established.configOptions;
33630
+ const modesTyped = established.modes;
33631
+ await applyCodexPermissionFromAcpSession({
33632
+ sessionId,
33633
+ agentConfig: cfg,
33634
+ configOptions: configOptionsForPermission(ctx.getActiveConfigOptions, configOptionsTyped),
33635
+ modes: modesTyped,
33636
+ setSessionConfigOption: transport.setSessionConfigOption ? (p) => transport.setSessionConfigOption(p) : async () => {
33637
+ },
33638
+ setSessionMode: transport.setSessionMode ? (p) => transport.setSessionMode(p) : async () => {
33639
+ },
33640
+ logDebug: ctx.logDebug
33641
+ });
33642
+ }
33514
33643
  const cfgAll = ctx.agentConfig != null && typeof ctx.agentConfig === "object" && !Array.isArray(ctx.agentConfig) ? ctx.agentConfig : null;
33515
33644
  const configOptionsForModel = established.configOptions;
33516
33645
  if (transport.setSessionConfigOption) {
@@ -33883,13 +34012,18 @@ function isCodexAcpCommand(command) {
33883
34012
  const i = command.indexOf("@zed-industries/codex-acp");
33884
34013
  return i >= 0 && (i === 0 || command[i - 1] === "npx" || command[i - 1] === "bunx");
33885
34014
  }
33886
- function buildCodexAcpSpawnCommand(base, _sessionMode) {
34015
+ function buildCodexAcpSpawnCommand(base, _sessionMode, _agentConfig) {
33887
34016
  return [...base];
33888
34017
  }
33889
34018
  async function createCodexAcpClient(options) {
33890
34019
  const base = options.command?.length && options.command.some((a) => a.includes("codex-acp")) ? options.command : [...DEFAULT_CODEX_ACP_COMMAND];
33891
- const command = buildCodexAcpSpawnCommand(base, options.sessionMode);
33892
- return createSdkStdioAcpClient({ ...options, command });
34020
+ const command = buildCodexAcpSpawnCommand(base, options.sessionMode, options.agentConfig);
34021
+ return createSdkStdioAcpClient({
34022
+ ...options,
34023
+ command,
34024
+ /** Codex ACP can ignore `session/cancel`; mirror Claude Code's subprocess fallback. */
34025
+ killSubprocessAfterCancelMs: options.killSubprocessAfterCancelMs ?? 2500
34026
+ });
33893
34027
  }
33894
34028
 
33895
34029
  // src/agents/acp/clients/cursor/cursor-acp-client.ts
@@ -34287,7 +34421,7 @@ function resolveAgentCommand(preferredAgentType) {
34287
34421
  command,
34288
34422
  label: preferredAgentType,
34289
34423
  createClient: createCodexAcpClient,
34290
- spawnCommandForSession: (sessionMode, _agentConfig) => buildCodexAcpSpawnCommand(command, sessionMode)
34424
+ spawnCommandForSession: (sessionMode, agentConfig) => buildCodexAcpSpawnCommand(command, sessionMode, agentConfig)
34291
34425
  };
34292
34426
  }
34293
34427
  if (useKiroAcp(preferredAgentType, command)) {
@@ -36743,8 +36877,18 @@ import path29 from "node:path";
36743
36877
  // src/files/index/walk-workspace-tree.ts
36744
36878
  import fs25 from "node:fs";
36745
36879
  import path28 from "node:path";
36880
+ var DEPENDENCY_INSTALL_DIR_NAMES = /* @__PURE__ */ new Set([
36881
+ "node_modules",
36882
+ "bower_components",
36883
+ "vendor",
36884
+ "Pods",
36885
+ "Carthage",
36886
+ "DerivedData",
36887
+ ".yarn",
36888
+ ".pnpm-store"
36889
+ ]);
36746
36890
  function shouldSkipWorkspaceWalkEntry(name) {
36747
- return name.startsWith(".");
36891
+ return DEPENDENCY_INSTALL_DIR_NAMES.has(name);
36748
36892
  }
36749
36893
  async function walkWorkspaceTreeAsync(dir, baseDir, onFile, state) {
36750
36894
  let names;
@@ -36815,14 +36959,26 @@ var FILE_INDEX_INTERRUPT_CHECK_EVERY = 256;
36815
36959
  function assertNotShutdown() {
36816
36960
  if (isCliImmediateShutdownRequested()) throw new CliSqliteInterrupted();
36817
36961
  }
36962
+ function upsertFileIndexParentPath(db, resolved) {
36963
+ const now = (/* @__PURE__ */ new Date()).toISOString();
36964
+ db.run(
36965
+ `INSERT INTO file_index_parent_path (path, path_hash, updated_at)
36966
+ VALUES (?, ?, ?)
36967
+ ON CONFLICT(path) DO UPDATE SET path_hash = excluded.path_hash, updated_at = excluded.updated_at`,
36968
+ [resolved, getCwdHashForFileIndex(resolved), now]
36969
+ );
36970
+ const row = db.get("SELECT id FROM file_index_parent_path WHERE path = ?", [resolved]);
36971
+ if (row == null) throw new Error(`Failed to upsert file index parent path: ${resolved}`);
36972
+ return Number(row.id);
36973
+ }
36818
36974
  function persistFileIndexPaths(resolved, paths) {
36819
36975
  return withCliSqliteSync((db) => {
36820
- const h = getCwdHashForFileIndex(resolved);
36821
36976
  let pathCount = 0;
36822
36977
  db.run("BEGIN IMMEDIATE");
36823
36978
  try {
36824
- db.run("DELETE FROM file_index_path WHERE cwd_hash = ?", [h]);
36825
- const ins = db.prepare("INSERT INTO file_index_path (cwd_hash, path) VALUES (?, ?)");
36979
+ const parentId = upsertFileIndexParentPath(db, resolved);
36980
+ db.run("DELETE FROM file_index_child_path WHERE parent_id = ?", [parentId]);
36981
+ const ins = db.prepare("INSERT INTO file_index_child_path (parent_id, path) VALUES (?, ?)");
36826
36982
  try {
36827
36983
  let batch = 0;
36828
36984
  for (const rel of paths) {
@@ -36830,7 +36986,7 @@ function persistFileIndexPaths(resolved, paths) {
36830
36986
  batch = 0;
36831
36987
  assertNotShutdown();
36832
36988
  }
36833
- ins.run([h, rel]);
36989
+ ins.run([parentId, rel]);
36834
36990
  pathCount += 1;
36835
36991
  }
36836
36992
  } finally {
@@ -36872,36 +37028,38 @@ async function buildFileIndexAsync(cwd) {
36872
37028
  // src/files/index/ensure-file-index.ts
36873
37029
  import path30 from "node:path";
36874
37030
 
36875
- // src/files/index/file-index-dependency-path.ts
36876
- function sqliteExprBridgeFileIndexDependencyRank() {
36877
- return `CASE WHEN lower(path) = 'node_modules' OR lower(path) LIKE 'node_modules/%' OR lower(path) LIKE '%/node_modules/%' OR lower(path) = 'bower_components' OR lower(path) LIKE 'bower_components/%' OR lower(path) LIKE '%/bower_components/%' THEN 1 ELSE 0 END`;
36878
- }
36879
-
36880
37031
  // src/files/index/search-file-index.ts
36881
37032
  function escapeLikePattern(fragment) {
36882
37033
  return fragment.replace(/\\/g, "\\\\").replace(/%/g, "\\%").replace(/_/g, "\\_");
36883
37034
  }
36884
37035
  function bridgeFileIndexIsPopulatedWithDb(resolvedCwd, db) {
36885
- const h = getCwdHashForFileIndex(resolvedCwd);
36886
- const row = db.get("SELECT 1 as ok FROM file_index_path WHERE cwd_hash = ? LIMIT 1", [h]);
37036
+ const row = db.get("SELECT 1 as ok FROM file_index_parent_path WHERE path = ? LIMIT 1", [resolvedCwd]);
36887
37037
  return row != null;
36888
37038
  }
36889
37039
  function bridgeFileIndexPathCountWithDb(resolvedCwd, db) {
36890
- const h = getCwdHashForFileIndex(resolvedCwd);
36891
- const row = db.get("SELECT COUNT(*) as c FROM file_index_path WHERE cwd_hash = ?", [h]);
37040
+ const row = db.get(
37041
+ `SELECT COUNT(*) as c
37042
+ FROM file_index_child_path child
37043
+ JOIN file_index_parent_path parent ON parent.id = child.parent_id
37044
+ WHERE parent.path = ?`,
37045
+ [resolvedCwd]
37046
+ );
36892
37047
  const c = row?.c ?? 0;
36893
37048
  return Number(c);
36894
37049
  }
36895
37050
  function searchBridgeFilePathsWithDb(resolvedCwd, query, limit, db) {
36896
37051
  const q = query.trim().toLowerCase();
36897
37052
  if (!q) return [];
36898
- const h = getCwdHashForFileIndex(resolvedCwd);
36899
37053
  const pattern = `%${escapeLikePattern(q)}%`;
36900
37054
  const lim = Math.max(0, Math.min(1e4, Math.floor(limit)));
36901
- const depRank = sqliteExprBridgeFileIndexDependencyRank();
36902
37055
  const rows = db.all(
36903
- `SELECT path FROM file_index_path WHERE cwd_hash = ? AND lower(path) LIKE ? ESCAPE '\\' ORDER BY ${depRank}, path LIMIT ?`,
36904
- [h, pattern, lim]
37056
+ `SELECT child.path
37057
+ FROM file_index_child_path child
37058
+ JOIN file_index_parent_path parent ON parent.id = child.parent_id
37059
+ WHERE parent.path = ? AND lower(child.path) LIKE ? ESCAPE '\\'
37060
+ ORDER BY child.path
37061
+ LIMIT ?`,
37062
+ [resolvedCwd, pattern, lim]
36905
37063
  );
36906
37064
  return rows.map((r) => String(r.path));
36907
37065
  }
@@ -36931,9 +37089,7 @@ async function ensureFileIndexAsync(cwd) {
36931
37089
  var DEBOUNCE_MS = 900;
36932
37090
  function shouldIgnoreRelative(rel) {
36933
37091
  const n = rel.replace(/\\/g, "/");
36934
- if (n.includes("/.git/") || n === ".git" || n.startsWith(".git/")) return true;
36935
- if (n.includes("/.buildautomaton/") || n.startsWith(".buildautomaton/")) return true;
36936
- return false;
37092
+ return n.split("/").some((segment) => shouldSkipWorkspaceWalkEntry(segment));
36937
37093
  }
36938
37094
  function attachWatchErrorLog(w) {
36939
37095
  w.on("error", (err) => {
@@ -39318,9 +39474,8 @@ var handleSkillCallMessage = (msg, { getWs, log: log2 }) => {
39318
39474
  );
39319
39475
  };
39320
39476
 
39321
- // src/files/list-dir.ts
39322
- import fs33 from "node:fs";
39323
- import path36 from "node:path";
39477
+ // src/files/list-dir/index.ts
39478
+ import fs34 from "node:fs";
39324
39479
 
39325
39480
  // src/files/ensure-under-cwd.ts
39326
39481
  import path35 from "node:path";
@@ -39333,71 +39488,93 @@ function ensureUnderCwd(relativePath, cwd = getBridgeRoot()) {
39333
39488
  return resolved;
39334
39489
  }
39335
39490
 
39336
- // src/files/list-dir.ts
39491
+ // src/files/list-dir/types.ts
39337
39492
  var LIST_DIR_YIELD_EVERY = 256;
39338
- async function listDirAsync(relativePath) {
39339
- const resolved = ensureUnderCwd(relativePath || ".", getBridgeRoot());
39493
+
39494
+ // src/files/list-dir/map-dir-entry.ts
39495
+ import path36 from "node:path";
39496
+ import fs33 from "node:fs";
39497
+ async function mapDirEntry(d, relativePath, resolved) {
39498
+ const entryPath = path36.join(relativePath || ".", d.name).replace(/\\/g, "/");
39499
+ const fullPath = path36.join(resolved, d.name);
39500
+ let isDir = d.isDirectory();
39501
+ if (d.isSymbolicLink()) {
39502
+ try {
39503
+ const targetStat = await fs33.promises.stat(fullPath);
39504
+ isDir = targetStat.isDirectory();
39505
+ } catch {
39506
+ isDir = false;
39507
+ }
39508
+ }
39509
+ return {
39510
+ name: d.name,
39511
+ path: entryPath,
39512
+ isDir,
39513
+ isSymlink: d.isSymbolicLink()
39514
+ };
39515
+ }
39516
+
39517
+ // src/files/list-dir/sort-entries.ts
39518
+ function sortListEntries(entries) {
39519
+ return entries.sort((a, b) => {
39520
+ if (a.isDir !== b.isDir) return a.isDir ? -1 : 1;
39521
+ return a.name.localeCompare(b.name, void 0, { sensitivity: "base" });
39522
+ });
39523
+ }
39524
+
39525
+ // src/files/list-dir/index.ts
39526
+ async function listDirAsync(relativePath, sessionParentPath = getBridgeRoot()) {
39527
+ await yieldToEventLoop();
39528
+ const resolved = ensureUnderCwd(relativePath || ".", sessionParentPath);
39340
39529
  if (!resolved) {
39341
39530
  return { error: "Path is outside working directory" };
39342
39531
  }
39343
39532
  try {
39344
- const names = await fs33.promises.readdir(resolved, { withFileTypes: true });
39345
- const visible = names.filter((d) => !d.name.startsWith("."));
39533
+ const names = await fs34.promises.readdir(resolved, { withFileTypes: true });
39346
39534
  const entries = [];
39347
- for (let i = 0; i < visible.length; i++) {
39535
+ for (let i = 0; i < names.length; i++) {
39348
39536
  if (i > 0 && i % LIST_DIR_YIELD_EVERY === 0) {
39349
39537
  await yieldToEventLoop();
39350
39538
  }
39351
- const d = visible[i];
39352
- const entryPath = path36.join(relativePath || ".", d.name).replace(/\\/g, "/");
39353
- const fullPath = path36.join(resolved, d.name);
39354
- let isDir = d.isDirectory();
39355
- if (d.isSymbolicLink()) {
39356
- try {
39357
- const targetStat = await fs33.promises.stat(fullPath);
39358
- isDir = targetStat.isDirectory();
39359
- } catch {
39360
- isDir = false;
39361
- }
39362
- }
39363
- entries.push({
39364
- name: d.name,
39365
- path: entryPath,
39366
- isDir,
39367
- isSymlink: d.isSymbolicLink()
39368
- });
39539
+ entries.push(await mapDirEntry(names[i], relativePath, resolved));
39369
39540
  }
39370
- entries.sort((a, b) => {
39371
- if (a.isDir !== b.isDir) return a.isDir ? -1 : 1;
39372
- return a.name.localeCompare(b.name, void 0, { sensitivity: "base" });
39373
- });
39374
- return { entries };
39541
+ return { entries: sortListEntries(entries) };
39375
39542
  } catch (err) {
39376
39543
  const message = err instanceof Error ? err.message : String(err);
39377
39544
  return { error: message };
39378
39545
  }
39379
39546
  }
39380
39547
 
39381
- // src/files/read-file.ts
39382
- import fs34 from "node:fs";
39383
- import { StringDecoder } from "node:string_decoder";
39384
- function resolveFilePath(relativePath) {
39385
- const resolved = ensureUnderCwd(relativePath, getBridgeRoot());
39548
+ // src/files/read-file/types.ts
39549
+ var LINE_CHUNK_SIZE = 64 * 1024;
39550
+ var READ_RANGE_YIELD_EVERY_BYTES = 256 * 1024;
39551
+
39552
+ // src/files/read-file/resolve-file-path.ts
39553
+ import fs35 from "node:fs";
39554
+ async function resolveFilePathAsync(relativePath, sessionParentPath = getBridgeRoot()) {
39555
+ const resolved = ensureUnderCwd(relativePath, sessionParentPath);
39386
39556
  if (!resolved) return { error: "Path is outside working directory" };
39387
39557
  let real;
39388
39558
  try {
39389
- real = fs34.realpathSync(resolved);
39559
+ real = await fs35.promises.realpath(resolved);
39390
39560
  } catch {
39391
39561
  real = resolved;
39392
39562
  }
39393
- const stat3 = fs34.statSync(real);
39394
- if (!stat3.isFile()) return { error: "Not a file" };
39395
- return real;
39563
+ try {
39564
+ const stat3 = await fs35.promises.stat(real);
39565
+ if (!stat3.isFile()) return { error: "Not a file" };
39566
+ return real;
39567
+ } catch (err) {
39568
+ return { error: err instanceof Error ? err.message : String(err) };
39569
+ }
39396
39570
  }
39397
- var LINE_CHUNK_SIZE = 64 * 1024;
39398
- function readFileRange(filePath, startLine, endLine, lineOffsetIn, lineChunkSize = LINE_CHUNK_SIZE) {
39399
- const fileSize = fs34.statSync(filePath).size;
39400
- const fd = fs34.openSync(filePath, "r");
39571
+
39572
+ // src/files/read-file/read-file-range-async.ts
39573
+ import fs36 from "node:fs";
39574
+ import { StringDecoder } from "node:string_decoder";
39575
+ async function readFileRangeAsync(filePath, startLine, endLine, lineOffsetIn, lineChunkSize = LINE_CHUNK_SIZE) {
39576
+ const fileSize = (await fs36.promises.stat(filePath)).size;
39577
+ const fd = await fs36.promises.open(filePath, "r");
39401
39578
  const bufSize = 64 * 1024;
39402
39579
  const buf = Buffer.alloc(bufSize);
39403
39580
  const decoder = new StringDecoder("utf8");
@@ -39408,9 +39585,18 @@ function readFileRange(filePath, startLine, endLine, lineOffsetIn, lineChunkSize
39408
39585
  let skipLine0Chars = typeof lineOffsetIn === "number" ? lineOffsetIn : 0;
39409
39586
  let line0CharsReturned = 0;
39410
39587
  let line0Accum = "";
39588
+ let bytesSinceYield = 0;
39411
39589
  try {
39412
- let bytesRead;
39413
- while (!done && (bytesRead = fs34.readSync(fd, buf, 0, bufSize, null)) > 0) {
39590
+ let position = 0;
39591
+ while (!done) {
39592
+ const { bytesRead } = await fd.read(buf, 0, bufSize, position);
39593
+ if (bytesRead === 0) break;
39594
+ position += bytesRead;
39595
+ bytesSinceYield += bytesRead;
39596
+ if (bytesSinceYield >= READ_RANGE_YIELD_EVERY_BYTES) {
39597
+ await yieldToEventLoop();
39598
+ bytesSinceYield = 0;
39599
+ }
39414
39600
  const text = partial2 + decoder.write(buf.subarray(0, bytesRead));
39415
39601
  partial2 = "";
39416
39602
  let lineStart = 0;
@@ -39545,39 +39731,132 @@ function readFileRange(filePath, startLine, endLine, lineOffsetIn, lineChunkSize
39545
39731
  }
39546
39732
  return { content: resultLines.join("\n"), size: fileSize };
39547
39733
  } finally {
39548
- fs34.closeSync(fd);
39734
+ await fd.close();
39549
39735
  }
39550
39736
  }
39551
- function readFile3(relativePath, startLine, endLine, lineOffset, lineChunkSize = LINE_CHUNK_SIZE) {
39737
+
39738
+ // src/files/read-file/read-file-buffer-full-async.ts
39739
+ import fs37 from "node:fs";
39740
+ var READ_CHUNK_BYTES = 256 * 1024;
39741
+ async function readFileBufferFullAsync(filePath) {
39742
+ const stat3 = await fs37.promises.stat(filePath);
39743
+ const fd = await fs37.promises.open(filePath, "r");
39744
+ const chunks = [];
39745
+ let position = 0;
39746
+ let bytesSinceYield = 0;
39552
39747
  try {
39553
- const result = resolveFilePath(relativePath);
39748
+ while (position < stat3.size) {
39749
+ const buf = Buffer.alloc(Math.min(READ_CHUNK_BYTES, stat3.size - position));
39750
+ const { bytesRead } = await fd.read(buf, 0, buf.length, position);
39751
+ if (bytesRead === 0) break;
39752
+ chunks.push(buf.subarray(0, bytesRead));
39753
+ position += bytesRead;
39754
+ bytesSinceYield += bytesRead;
39755
+ if (bytesSinceYield >= READ_RANGE_YIELD_EVERY_BYTES) {
39756
+ await yieldToEventLoop();
39757
+ bytesSinceYield = 0;
39758
+ }
39759
+ }
39760
+ } finally {
39761
+ await fd.close();
39762
+ }
39763
+ return { buffer: Buffer.concat(chunks), size: stat3.size };
39764
+ }
39765
+
39766
+ // src/files/read-file/read-file-full-async.ts
39767
+ async function readFileFullAsync(filePath) {
39768
+ const { buffer, size } = await readFileBufferFullAsync(filePath);
39769
+ const raw = buffer.toString("utf8");
39770
+ const lines = raw.split(/\r?\n/);
39771
+ return { content: raw, totalLines: lines.length, size };
39772
+ }
39773
+
39774
+ // src/files/read-file/guess-mime-type.ts
39775
+ var MIME_BY_EXT = {
39776
+ png: "image/png",
39777
+ jpg: "image/jpeg",
39778
+ jpeg: "image/jpeg",
39779
+ gif: "image/gif",
39780
+ bmp: "image/bmp",
39781
+ ico: "image/x-icon",
39782
+ webp: "image/webp",
39783
+ avif: "image/avif",
39784
+ svg: "image/svg+xml",
39785
+ pdf: "application/pdf",
39786
+ json: "application/json",
39787
+ html: "text/html",
39788
+ htm: "text/html",
39789
+ css: "text/css",
39790
+ js: "text/javascript",
39791
+ mjs: "text/javascript",
39792
+ ts: "text/typescript",
39793
+ txt: "text/plain",
39794
+ md: "text/markdown",
39795
+ xml: "application/xml",
39796
+ zip: "application/zip",
39797
+ gz: "application/gzip",
39798
+ wasm: "application/wasm"
39799
+ };
39800
+ function guessMimeType(filePath) {
39801
+ const ext = filePath.split(".").pop()?.toLowerCase() ?? "";
39802
+ return MIME_BY_EXT[ext] ?? "application/octet-stream";
39803
+ }
39804
+
39805
+ // src/files/read-file/read-file-binary-full-async.ts
39806
+ async function readFileBinaryFullAsync(filePath) {
39807
+ const { buffer, size } = await readFileBufferFullAsync(filePath);
39808
+ return {
39809
+ content: buffer.toString("base64"),
39810
+ size,
39811
+ mimeType: guessMimeType(filePath)
39812
+ };
39813
+ }
39814
+
39815
+ // src/files/read-file/index.ts
39816
+ async function readFileAsync(relativePath, startLine, endLine, lineOffset, lineChunkSize = LINE_CHUNK_SIZE, sessionParentPath = getBridgeRoot(), encoding = "utf8") {
39817
+ await yieldToEventLoop();
39818
+ try {
39819
+ const result = await resolveFilePathAsync(relativePath, sessionParentPath);
39554
39820
  if (typeof result === "object") return result;
39821
+ const resolvedPath = result;
39555
39822
  const hasRange = typeof startLine === "number" && typeof endLine === "number";
39823
+ if (encoding === "base64") {
39824
+ if (hasRange) return { error: "base64 encoding requires a full file read (no line range)" };
39825
+ const read2 = await readFileBinaryFullAsync(resolvedPath);
39826
+ return { ...read2, resolvedPath };
39827
+ }
39556
39828
  if (hasRange) {
39557
- return readFileRange(result, startLine, endLine, lineOffset, lineChunkSize);
39829
+ return readFileRangeAsync(resolvedPath, startLine, endLine, lineOffset, lineChunkSize);
39558
39830
  }
39559
- const stat3 = fs34.statSync(result);
39560
- const raw = fs34.readFileSync(result, "utf8");
39561
- const lines = raw.split(/\r?\n/);
39562
- return { content: raw, totalLines: lines.length, size: stat3.size };
39831
+ const read = await readFileFullAsync(resolvedPath);
39832
+ return { ...read, resolvedPath };
39563
39833
  } catch (err) {
39564
39834
  return { error: err instanceof Error ? err.message : String(err) };
39565
39835
  }
39566
39836
  }
39567
- async function readFileAsync(relativePath, startLine, endLine, lineOffset, lineChunkSize = LINE_CHUNK_SIZE) {
39568
- await yieldToEventLoop();
39569
- return readFile3(relativePath, startLine, endLine, lineOffset, lineChunkSize);
39837
+
39838
+ // src/files/resolve-file-browser-session-parent.ts
39839
+ function resolveFileBrowserSessionParent(sessionWorktreeManager, sessionId) {
39840
+ const sid = sessionId?.trim();
39841
+ if (sid) {
39842
+ sessionWorktreeManager.ensureRepoCheckoutPathsForSession(sid);
39843
+ const worktreeRoot = sessionWorktreeManager.getSessionWorktreeRootForSession(sid);
39844
+ if (worktreeRoot) return worktreeRoot;
39845
+ }
39846
+ return getBridgeRoot();
39570
39847
  }
39571
39848
 
39572
39849
  // src/files/handle-file-browser-search.ts
39573
39850
  import path37 from "node:path";
39574
39851
  var SEARCH_LIMIT = 100;
39575
- function handleFileBrowserSearch(msg, socket, e2ee) {
39852
+ function handleFileBrowserSearch(msg, socket, e2ee, sessionWorktreeManager) {
39576
39853
  void (async () => {
39577
39854
  await yieldToEventLoop();
39578
39855
  const q = typeof msg.q === "string" ? msg.q : "";
39579
- const cwd = path37.resolve(getBridgeRoot());
39580
- if (!await bridgeFileIndexIsPopulated(cwd)) {
39856
+ const sessionParentPath = path37.resolve(
39857
+ sessionWorktreeManager != null ? resolveFileBrowserSessionParent(sessionWorktreeManager, msg.sessionId) : getBridgeRoot()
39858
+ );
39859
+ if (!await bridgeFileIndexIsPopulated(sessionParentPath)) {
39581
39860
  const payload2 = {
39582
39861
  type: "file_browser_search_response",
39583
39862
  id: msg.id,
@@ -39587,7 +39866,7 @@ function handleFileBrowserSearch(msg, socket, e2ee) {
39587
39866
  sendWsMessage(socket, e2ee ? e2ee.encryptFields(payload2, ["paths"]) : payload2);
39588
39867
  return;
39589
39868
  }
39590
- const results = await searchBridgeFilePathsAsync(cwd, q, SEARCH_LIMIT);
39869
+ const results = await searchBridgeFilePathsAsync(sessionParentPath, q, SEARCH_LIMIT);
39591
39870
  const payload = {
39592
39871
  type: "file_browser_search_response",
39593
39872
  id: msg.id,
@@ -39597,9 +39876,9 @@ function handleFileBrowserSearch(msg, socket, e2ee) {
39597
39876
  sendWsMessage(socket, e2ee ? e2ee.encryptFields(payload, ["paths"]) : payload);
39598
39877
  })();
39599
39878
  }
39600
- function triggerFileIndexBuild() {
39879
+ function triggerFileIndexBuild(sessionParentPath = getBridgeRoot()) {
39601
39880
  setImmediate(() => {
39602
- void ensureFileIndexAsync(getBridgeRoot()).catch((e) => {
39881
+ void ensureFileIndexAsync(sessionParentPath).catch((e) => {
39603
39882
  console.error("[file-index] Background build failed:", e);
39604
39883
  });
39605
39884
  });
@@ -39609,18 +39888,19 @@ function triggerFileIndexBuild() {
39609
39888
  function sendFileBrowserMessage(socket, e2ee, payload) {
39610
39889
  sendWsMessage(socket, e2ee ? e2ee.encryptFields(payload, ["entries", "content", "totalLines", "size", "lineOffset"]) : payload);
39611
39890
  }
39612
- function handleFileBrowserRequest(msg, socket, e2ee) {
39891
+ function handleFileBrowserRequest(msg, socket, e2ee, sessionWorktreeManager) {
39613
39892
  void (async () => {
39614
39893
  const reqPath = msg.path.replace(/^\/+/, "") || ".";
39615
39894
  const op = msg.op === "read" ? "read" : "list";
39895
+ const sessionParentPath = sessionWorktreeManager != null ? resolveFileBrowserSessionParent(sessionWorktreeManager, msg.sessionId) : void 0;
39616
39896
  if (op === "list") {
39617
- const result = await listDirAsync(reqPath);
39897
+ const result = await listDirAsync(reqPath, sessionParentPath);
39618
39898
  if ("error" in result) {
39619
39899
  sendWsMessage(socket, { type: "file_browser_response", id: msg.id, error: result.error });
39620
39900
  } else {
39621
39901
  sendFileBrowserMessage(socket, e2ee, { type: "file_browser_response", id: msg.id, entries: result.entries });
39622
39902
  if (reqPath === "." || reqPath === "") {
39623
- triggerFileIndexBuild();
39903
+ triggerFileIndexBuild(sessionParentPath);
39624
39904
  }
39625
39905
  }
39626
39906
  } else {
@@ -39628,7 +39908,16 @@ function handleFileBrowserRequest(msg, socket, e2ee) {
39628
39908
  const endLine = typeof msg.endLine === "number" ? msg.endLine : void 0;
39629
39909
  const lineOffset = typeof msg.lineOffset === "number" ? msg.lineOffset : void 0;
39630
39910
  const lineChunkSize = typeof msg.lineChunkSize === "number" ? msg.lineChunkSize : void 0;
39631
- const result = await readFileAsync(reqPath, startLine, endLine, lineOffset, lineChunkSize);
39911
+ const encoding = msg.encoding === "base64" ? "base64" : "utf8";
39912
+ const result = await readFileAsync(
39913
+ reqPath,
39914
+ startLine,
39915
+ endLine,
39916
+ lineOffset,
39917
+ lineChunkSize,
39918
+ sessionParentPath,
39919
+ encoding
39920
+ );
39632
39921
  if ("error" in result) {
39633
39922
  sendWsMessage(socket, { type: "file_browser_response", id: msg.id, error: result.error });
39634
39923
  } else {
@@ -39640,6 +39929,8 @@ function handleFileBrowserRequest(msg, socket, e2ee) {
39640
39929
  size: result.size
39641
39930
  };
39642
39931
  if (result.lineOffset != null) payload.lineOffset = result.lineOffset;
39932
+ if (result.mimeType != null) payload.mimeType = result.mimeType;
39933
+ if (result.resolvedPath != null) payload.resolvedPath = result.resolvedPath;
39643
39934
  sendFileBrowserMessage(socket, e2ee, payload);
39644
39935
  }
39645
39936
  }
@@ -39647,21 +39938,27 @@ function handleFileBrowserRequest(msg, socket, e2ee) {
39647
39938
  }
39648
39939
 
39649
39940
  // src/routing/handlers/file-browser-messages.ts
39650
- function handleFileBrowserRequestMessage(msg, { getWs, e2ee }) {
39941
+ function handleFileBrowserRequestMessage(msg, { getWs, e2ee, sessionWorktreeManager }) {
39651
39942
  if (typeof msg.id !== "string" || typeof msg.path !== "string") return;
39652
39943
  const socket = getWs();
39653
39944
  if (!socket) return;
39654
39945
  handleFileBrowserRequest(
39655
39946
  msg,
39656
39947
  socket,
39657
- e2ee
39948
+ e2ee,
39949
+ sessionWorktreeManager
39658
39950
  );
39659
39951
  }
39660
- function handleFileBrowserSearchMessage(msg, { getWs, e2ee }) {
39952
+ function handleFileBrowserSearchMessage(msg, { getWs, e2ee, sessionWorktreeManager }) {
39661
39953
  if (typeof msg.id !== "string") return;
39662
39954
  const socket = getWs();
39663
39955
  if (!socket) return;
39664
- handleFileBrowserSearch(msg, socket, e2ee);
39956
+ handleFileBrowserSearch(
39957
+ msg,
39958
+ socket,
39959
+ e2ee,
39960
+ sessionWorktreeManager
39961
+ );
39665
39962
  }
39666
39963
 
39667
39964
  // src/routing/handlers/skill-layout-request.ts
@@ -39675,7 +39972,7 @@ function handleSkillLayoutRequest(msg, deps) {
39675
39972
  }
39676
39973
 
39677
39974
  // src/skills/install-remote-skills.ts
39678
- import fs35 from "node:fs";
39975
+ import fs38 from "node:fs";
39679
39976
  import path38 from "node:path";
39680
39977
  function installRemoteSkills(cwd, targetDir, items) {
39681
39978
  const installed2 = [];
@@ -39691,11 +39988,11 @@ function installRemoteSkills(cwd, targetDir, items) {
39691
39988
  for (const f of item.files) {
39692
39989
  if (typeof f.path !== "string" || !f.text && !f.base64) continue;
39693
39990
  const dest = path38.join(skillDir, f.path);
39694
- fs35.mkdirSync(path38.dirname(dest), { recursive: true });
39991
+ fs38.mkdirSync(path38.dirname(dest), { recursive: true });
39695
39992
  if (f.text !== void 0) {
39696
- fs35.writeFileSync(dest, f.text, "utf8");
39993
+ fs38.writeFileSync(dest, f.text, "utf8");
39697
39994
  } else if (f.base64) {
39698
- fs35.writeFileSync(dest, Buffer.from(f.base64, "base64"));
39995
+ fs38.writeFileSync(dest, Buffer.from(f.base64, "base64"));
39699
39996
  }
39700
39997
  }
39701
39998
  installed2.push({
@@ -39853,7 +40150,7 @@ var handleSessionDiscardedMessage = (msg, deps) => {
39853
40150
  };
39854
40151
 
39855
40152
  // src/routing/handlers/revert-turn-snapshot.ts
39856
- import * as fs36 from "node:fs";
40153
+ import * as fs39 from "node:fs";
39857
40154
  var handleRevertTurnSnapshotMessage = (msg, deps) => {
39858
40155
  const id = typeof msg.id === "string" ? msg.id : "";
39859
40156
  const sessionId = typeof msg.sessionId === "string" ? msg.sessionId : "";
@@ -39866,7 +40163,7 @@ var handleRevertTurnSnapshotMessage = (msg, deps) => {
39866
40163
  const agentBase = sessionWorktreeManager.getSessionWorktreeRootForSession(sessionId) ?? getBridgeRoot();
39867
40164
  const file2 = snapshotFilePath(agentBase, turnId);
39868
40165
  try {
39869
- await fs36.promises.access(file2, fs36.constants.F_OK);
40166
+ await fs39.promises.access(file2, fs39.constants.F_OK);
39870
40167
  } catch {
39871
40168
  sendWsMessage(s, {
39872
40169
  type: "revert_turn_snapshot_result",
@@ -40882,7 +41179,7 @@ async function runCliAction(program2, opts) {
40882
41179
  if (bridgeRootOpt) {
40883
41180
  const resolvedBridgeRoot = path42.resolve(process.cwd(), bridgeRootOpt);
40884
41181
  try {
40885
- const st = fs37.statSync(resolvedBridgeRoot);
41182
+ const st = fs40.statSync(resolvedBridgeRoot);
40886
41183
  if (!st.isDirectory()) {
40887
41184
  console.error(`Bridge root is not a directory: ${resolvedBridgeRoot}`);
40888
41185
  process.exit(1);