@buildautomaton/cli 0.1.3 → 0.1.5

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
@@ -2235,13 +2235,13 @@ var require_extension = __commonJS({
2235
2235
  var require_websocket = __commonJS({
2236
2236
  "../../node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/websocket.js"(exports, module) {
2237
2237
  "use strict";
2238
- var EventEmitter3 = __require("events");
2238
+ var EventEmitter2 = __require("events");
2239
2239
  var https3 = __require("https");
2240
2240
  var http = __require("http");
2241
2241
  var net = __require("net");
2242
2242
  var tls = __require("tls");
2243
2243
  var { randomBytes, createHash: createHash2 } = __require("crypto");
2244
- var { Duplex, Readable: Readable3 } = __require("stream");
2244
+ var { Duplex, Readable: Readable2 } = __require("stream");
2245
2245
  var { URL: URL2 } = __require("url");
2246
2246
  var PerMessageDeflate = require_permessage_deflate();
2247
2247
  var Receiver2 = require_receiver();
@@ -2267,7 +2267,7 @@ var require_websocket = __commonJS({
2267
2267
  var protocolVersions = [8, 13];
2268
2268
  var readyStates = ["CONNECTING", "OPEN", "CLOSING", "CLOSED"];
2269
2269
  var subprotocolRegex = /^[!#$%&'*+\-.0-9A-Z^_`|a-z~]+$/;
2270
- var WebSocket2 = class _WebSocket extends EventEmitter3 {
2270
+ var WebSocket2 = class _WebSocket extends EventEmitter2 {
2271
2271
  /**
2272
2272
  * Create a new `WebSocket`.
2273
2273
  *
@@ -3179,7 +3179,7 @@ var require_stream = __commonJS({
3179
3179
  };
3180
3180
  duplex._final = function(callback) {
3181
3181
  if (ws.readyState === ws.CONNECTING) {
3182
- ws.once("open", function open2() {
3182
+ ws.once("open", function open() {
3183
3183
  duplex._final(callback);
3184
3184
  });
3185
3185
  return;
@@ -3200,7 +3200,7 @@ var require_stream = __commonJS({
3200
3200
  };
3201
3201
  duplex._write = function(chunk, encoding, callback) {
3202
3202
  if (ws.readyState === ws.CONNECTING) {
3203
- ws.once("open", function open2() {
3203
+ ws.once("open", function open() {
3204
3204
  duplex._write(chunk, encoding, callback);
3205
3205
  });
3206
3206
  return;
@@ -3264,7 +3264,7 @@ var require_subprotocol = __commonJS({
3264
3264
  var require_websocket_server = __commonJS({
3265
3265
  "../../node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/websocket-server.js"(exports, module) {
3266
3266
  "use strict";
3267
- var EventEmitter3 = __require("events");
3267
+ var EventEmitter2 = __require("events");
3268
3268
  var http = __require("http");
3269
3269
  var { Duplex } = __require("stream");
3270
3270
  var { createHash: createHash2 } = __require("crypto");
@@ -3277,7 +3277,7 @@ var require_websocket_server = __commonJS({
3277
3277
  var RUNNING = 0;
3278
3278
  var CLOSING = 1;
3279
3279
  var CLOSED = 2;
3280
- var WebSocketServer2 = class extends EventEmitter3 {
3280
+ var WebSocketServer2 = class extends EventEmitter2 {
3281
3281
  /**
3282
3282
  * Create a `WebSocketServer` instance.
3283
3283
  *
@@ -20943,8 +20943,8 @@ var init_acp = __esm({
20943
20943
  this.#requestHandler = requestHandler;
20944
20944
  this.#notificationHandler = notificationHandler;
20945
20945
  this.#stream = stream;
20946
- this.#closedPromise = new Promise((resolve16) => {
20947
- this.#abortController.signal.addEventListener("abort", () => resolve16());
20946
+ this.#closedPromise = new Promise((resolve14) => {
20947
+ this.#abortController.signal.addEventListener("abort", () => resolve14());
20948
20948
  });
20949
20949
  this.#receive();
20950
20950
  }
@@ -21093,8 +21093,8 @@ var init_acp = __esm({
21093
21093
  }
21094
21094
  async sendRequest(method, params) {
21095
21095
  const id = this.#nextRequestId++;
21096
- const responsePromise = new Promise((resolve16, reject) => {
21097
- this.#pendingResponses.set(id, { resolve: resolve16, reject });
21096
+ const responsePromise = new Promise((resolve14, reject) => {
21097
+ this.#pendingResponses.set(id, { resolve: resolve14, reject });
21098
21098
  });
21099
21099
  await this.#sendMessage({ jsonrpc: "2.0", id, method, params });
21100
21100
  return responsePromise;
@@ -21966,12 +21966,12 @@ var require_src2 = __commonJS({
21966
21966
  function check2(path24, isFile, isDirectory) {
21967
21967
  log2(`checking %s`, path24);
21968
21968
  try {
21969
- const stat4 = fs_1.statSync(path24);
21970
- if (stat4.isFile() && isFile) {
21969
+ const stat = fs_1.statSync(path24);
21970
+ if (stat.isFile() && isFile) {
21971
21971
  log2(`[OK] path represents a file`);
21972
21972
  return true;
21973
21973
  }
21974
- if (stat4.isDirectory() && isDirectory) {
21974
+ if (stat.isDirectory() && isDirectory) {
21975
21975
  log2(`[OK] path represents a directory`);
21976
21976
  return true;
21977
21977
  }
@@ -22065,12 +22065,19 @@ var wrapper_default = import_websocket.default;
22065
22065
  var BRIDGE_AUTH_ERROR_HEADER = "x-bridge-auth-error";
22066
22066
  var BRIDGE_AUTH_ERROR_TOKEN_INVALID = "token_invalid";
22067
22067
  function createWsBridge(options) {
22068
- const { url: url2, onMessage, onOpen, onClose, onError: onError2, onAuthInvalid } = options;
22068
+ const { url: url2, onMessage, onOpen, onClose, onError: onError2, onAuthInvalid, clientPingIntervalMs } = options;
22069
22069
  const wsOptions = {};
22070
22070
  if (url2.startsWith("wss://")) {
22071
22071
  wsOptions.agent = new https.Agent({ rejectUnauthorized: false });
22072
22072
  }
22073
22073
  const ws = new wrapper_default(url2, wsOptions);
22074
+ let clientPingTimer = null;
22075
+ function clearClientPing() {
22076
+ if (clientPingTimer != null) {
22077
+ clearInterval(clientPingTimer);
22078
+ clientPingTimer = null;
22079
+ }
22080
+ }
22074
22081
  ws.on("unexpected-response", (request, response) => {
22075
22082
  const status = response?.statusCode ?? 0;
22076
22083
  const headers = response?.headers ?? {};
@@ -22080,6 +22087,17 @@ function createWsBridge(options) {
22080
22087
  }
22081
22088
  });
22082
22089
  ws.on("open", () => {
22090
+ clearClientPing();
22091
+ if (clientPingIntervalMs != null && clientPingIntervalMs > 0) {
22092
+ clientPingTimer = setInterval(() => {
22093
+ if (ws.readyState === wrapper_default.OPEN) {
22094
+ try {
22095
+ ws.ping();
22096
+ } catch {
22097
+ }
22098
+ }
22099
+ }, clientPingIntervalMs);
22100
+ }
22083
22101
  onOpen?.();
22084
22102
  });
22085
22103
  ws.on("message", (raw) => {
@@ -22099,9 +22117,11 @@ function createWsBridge(options) {
22099
22117
  }
22100
22118
  });
22101
22119
  ws.on("close", (code, reason) => {
22120
+ clearClientPing();
22102
22121
  onClose?.(code, reason.toString());
22103
22122
  });
22104
22123
  ws.on("error", (err) => {
22124
+ clearClientPing();
22105
22125
  onError2?.(err);
22106
22126
  });
22107
22127
  return ws;
@@ -22115,6 +22135,18 @@ function sendWsMessage(ws, payload) {
22115
22135
  // src/acp/clients/acp-client.ts
22116
22136
  import { spawn } from "node:child_process";
22117
22137
  import { Readable, Writable } from "node:stream";
22138
+
22139
+ // src/files/cwd/bridge-workspace-directory.ts
22140
+ import * as path from "node:path";
22141
+ var bridgeWorkspaceDirectory = null;
22142
+ function getBridgeWorkspaceDirectory() {
22143
+ if (bridgeWorkspaceDirectory == null) {
22144
+ bridgeWorkspaceDirectory = path.resolve(process.cwd());
22145
+ }
22146
+ return bridgeWorkspaceDirectory;
22147
+ }
22148
+
22149
+ // src/acp/clients/acp-client.ts
22118
22150
  function formatSpawnError(err, command) {
22119
22151
  if (err.code === "ENOENT") {
22120
22152
  return `Command "${command}" not found. Install the agent (e.g. Cursor CLI) or add it to PATH.`;
@@ -22131,15 +22163,15 @@ function toErrorMessage(err) {
22131
22163
  }
22132
22164
  async function createAcpClient(options) {
22133
22165
  const { ClientSideConnection: ClientSideConnection2, ndJsonStream: ndJsonStream2 } = await Promise.resolve().then(() => (init_acp(), acp_exports));
22134
- const { command, cwd: cwd3 = process.cwd(), onSessionUpdate } = options;
22135
- const isWindows2 = process.platform === "win32";
22166
+ const { command, cwd = getBridgeWorkspaceDirectory(), onSessionUpdate } = options;
22167
+ const isWindows = process.platform === "win32";
22136
22168
  const child = spawn(command[0], command.slice(1), {
22137
- cwd: cwd3,
22169
+ cwd,
22138
22170
  stdio: ["pipe", "pipe", "inherit"],
22139
22171
  env: process.env,
22140
- shell: isWindows2
22172
+ shell: isWindows
22141
22173
  });
22142
- return new Promise((resolve16, reject) => {
22174
+ return new Promise((resolve14, reject) => {
22143
22175
  child.on("error", (err) => {
22144
22176
  child.kill();
22145
22177
  reject(new Error(formatSpawnError(err, command[0])));
@@ -22166,9 +22198,9 @@ async function createAcpClient(options) {
22166
22198
  capabilities: {},
22167
22199
  clientInfo: { name: "buildautomaton-cli", version: "0.1.0" }
22168
22200
  });
22169
- const newSessionRes = await connection.newSession({ workingDirectory: cwd3 });
22201
+ const newSessionRes = await connection.newSession({ workingDirectory: cwd });
22170
22202
  const sessionId = newSessionRes.sessionId;
22171
- resolve16({
22203
+ resolve14({
22172
22204
  sessionId,
22173
22205
  async sendPrompt(prompt, _options) {
22174
22206
  try {
@@ -22244,7 +22276,7 @@ async function proxyToLocal(request) {
22244
22276
  path: url2.pathname + url2.search,
22245
22277
  headers: request.headers
22246
22278
  };
22247
- return new Promise((resolve16) => {
22279
+ return new Promise((resolve14) => {
22248
22280
  const req = mod.request(opts, (res) => {
22249
22281
  const chunks = [];
22250
22282
  res.on("data", (c) => chunks.push(c));
@@ -22255,7 +22287,7 @@ async function proxyToLocal(request) {
22255
22287
  if (typeof v === "string") headers[k] = v;
22256
22288
  else if (Array.isArray(v) && v[0]) headers[k] = v[0];
22257
22289
  }
22258
- resolve16({
22290
+ resolve14({
22259
22291
  id: request.id,
22260
22292
  statusCode: res.statusCode ?? 0,
22261
22293
  headers,
@@ -22264,7 +22296,7 @@ async function proxyToLocal(request) {
22264
22296
  });
22265
22297
  });
22266
22298
  req.on("error", (err) => {
22267
- resolve16({
22299
+ resolve14({
22268
22300
  id: request.id,
22269
22301
  statusCode: 0,
22270
22302
  headers: {},
@@ -22394,12 +22426,12 @@ var previewSkill = {
22394
22426
  const port = params.port ?? (Number(process.env[PREVIEW_PORT_ENV]) || DEFAULT_PORT);
22395
22427
  previewPort = port;
22396
22428
  previewSecret = randomSecret();
22397
- const isWindows2 = process.platform === "win32";
22429
+ const isWindows = process.platform === "win32";
22398
22430
  const parts = command.split(/\s+/);
22399
22431
  const exe = parts[0];
22400
22432
  const args = parts.slice(1);
22401
- previewProcess = spawn2(isWindows2 && exe === "npm" ? "npm.cmd" : exe, args, {
22402
- cwd: process.cwd(),
22433
+ previewProcess = spawn2(isWindows && exe === "npm" ? "npm.cmd" : exe, args, {
22434
+ cwd: getBridgeWorkspaceDirectory(),
22403
22435
  stdio: ["ignore", "pipe", "pipe"],
22404
22436
  env: {
22405
22437
  ...process.env,
@@ -22508,11 +22540,11 @@ function logImmediate(line) {
22508
22540
 
22509
22541
  // src/config.ts
22510
22542
  import fs from "node:fs";
22511
- import path from "node:path";
22543
+ import path2 from "node:path";
22512
22544
  import os from "node:os";
22513
22545
  function getConfigPath() {
22514
- const dir = path.join(os.homedir(), ".buildautomaton");
22515
- return path.join(dir, "config.json");
22546
+ const dir = path2.join(os.homedir(), ".buildautomaton");
22547
+ return path2.join(dir, "config.json");
22516
22548
  }
22517
22549
  function normalizeApiUrl(url2) {
22518
22550
  return url2.replace(/\/$/, "");
@@ -22528,7 +22560,7 @@ function readRawConfig() {
22528
22560
  }
22529
22561
  function writeConfigForApi(apiUrl, auth) {
22530
22562
  const p = getConfigPath();
22531
- const dir = path.dirname(p);
22563
+ const dir = path2.dirname(p);
22532
22564
  const key = normalizeApiUrl(apiUrl);
22533
22565
  const prev = readRawConfig() ?? {};
22534
22566
  const servers = { ...prev.servers ?? {}, [key]: { ...auth } };
@@ -22537,7 +22569,7 @@ function writeConfigForApi(apiUrl, auth) {
22537
22569
  if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
22538
22570
  fs.writeFileSync(p, JSON.stringify(next, null, 2), "utf8");
22539
22571
  } catch (e) {
22540
- console.error("Could not save auth config:", e);
22572
+ console.error("Could not save authentication config:", e);
22541
22573
  }
22542
22574
  }
22543
22575
  function clearConfigForApi(apiUrl) {
@@ -22599,6 +22631,28 @@ function openBrowser(connectionId, initialWorkspaceId, preferredBridgeName, apiU
22599
22631
  }
22600
22632
  }
22601
22633
 
22634
+ // src/bridge/connection/reconnect/constants.ts
22635
+ var RECONNECT_FIRST_MS = 100;
22636
+ var RECONNECT_MAX_MS = 3e4;
22637
+ var RECONNECT_QUIET_MS = 2e3;
22638
+ function reconnectDelayMs(attemptBeforeIncrement) {
22639
+ return Math.min(RECONNECT_FIRST_MS * 2 ** attemptBeforeIncrement, RECONNECT_MAX_MS);
22640
+ }
22641
+
22642
+ // src/bridge/connection/reconnect/format-reconnect-delay-for-log.ts
22643
+ function formatReconnectDelayForLog(delayMs) {
22644
+ if (delayMs < 1e3) return `${delayMs}ms`;
22645
+ const s = delayMs / 1e3;
22646
+ return Number.isInteger(s) ? `${s}s` : `${s.toFixed(1)}s`;
22647
+ }
22648
+
22649
+ // src/bridge/connection/reconnect/log-next-reconnect-attempt.ts
22650
+ function logNextReconnectAttempt(log2, serviceLabel, quiet, delayMs, attempt) {
22651
+ if (!quiet.verboseLogs) return;
22652
+ const delayLabel = formatReconnectDelayForLog(delayMs);
22653
+ log2(`${serviceLabel} Next connection attempt in ${delayLabel} (attempt ${attempt}).`);
22654
+ }
22655
+
22602
22656
  // src/bridge/connection/ws-close-diagnostics.ts
22603
22657
  function describeWebSocketCloseCode(code) {
22604
22658
  const known = {
@@ -22627,11 +22681,122 @@ function formatWebSocketClose(label, code, reason, extra) {
22627
22681
  const r = reason.trim();
22628
22682
  const reasonPart = r ? ` reason="${r}"` : "";
22629
22683
  const extraPart = extra ? ` ${extra}` : "";
22630
- return `${label} closed: code=${code} (${describeWebSocketCloseCode(code)})${reasonPart}${extraPart}`;
22684
+ return `${label} Disconnected: code=${code} (${describeWebSocketCloseCode(code)})${reasonPart}${extraPart}`;
22685
+ }
22686
+
22687
+ // src/bridge/connection/reconnect/reconnect-quiet-slot.ts
22688
+ function createEmptyReconnectQuietSlot() {
22689
+ return { timer: null, verboseLogs: false, pendingCloseLog: null };
22690
+ }
22691
+ function clearReconnectQuietTimer(quiet) {
22692
+ if (quiet.timer != null) {
22693
+ clearTimeout(quiet.timer);
22694
+ quiet.timer = null;
22695
+ }
22696
+ }
22697
+ function clearReconnectQuietOnSuccessfulConnection(quiet, log2, reconnectedMessage) {
22698
+ clearReconnectQuietTimer(quiet);
22699
+ quiet.pendingCloseLog = null;
22700
+ if (quiet.verboseLogs) {
22701
+ log2(reconnectedMessage);
22702
+ quiet.verboseLogs = false;
22703
+ }
22704
+ }
22705
+ function beginDeferredDisconnectForReconnect(options) {
22706
+ const {
22707
+ isClosedByUser,
22708
+ quiet,
22709
+ code,
22710
+ reason,
22711
+ willReconnect,
22712
+ log: log2,
22713
+ serviceLabel,
22714
+ shutdownDetail,
22715
+ reconnectingDetail,
22716
+ quietMs = RECONNECT_QUIET_MS,
22717
+ shouldAbortQuietWindow
22718
+ } = options;
22719
+ if (!willReconnect) {
22720
+ log2(formatWebSocketClose(serviceLabel, code, reason, shutdownDetail));
22721
+ return;
22722
+ }
22723
+ quiet.pendingCloseLog = { code, reason };
22724
+ if (quiet.timer == null) {
22725
+ quiet.timer = setTimeout(() => {
22726
+ quiet.timer = null;
22727
+ if (isClosedByUser()) return;
22728
+ if (shouldAbortQuietWindow()) return;
22729
+ if (quiet.pendingCloseLog) {
22730
+ const { code: c, reason: r } = quiet.pendingCloseLog;
22731
+ quiet.pendingCloseLog = null;
22732
+ log2(formatWebSocketClose(serviceLabel, c, r, reconnectingDetail));
22733
+ }
22734
+ quiet.verboseLogs = true;
22735
+ }, quietMs);
22736
+ }
22737
+ }
22738
+
22739
+ // src/bridge/connection/reconnect/bridge-main-reconnect.ts
22740
+ function beginMainBridgeDeferredDisconnect(state, code, reason, log2, willReconnect) {
22741
+ beginDeferredDisconnectForReconnect({
22742
+ isClosedByUser: () => state.closedByUser,
22743
+ quiet: state.mainQuiet,
22744
+ code,
22745
+ reason,
22746
+ willReconnect,
22747
+ log: log2,
22748
+ serviceLabel: "[Bridge service]",
22749
+ shutdownDetail: "Not reconnecting (shutting down).",
22750
+ reconnectingDetail: "Reconnecting\u2026",
22751
+ shouldAbortQuietWindow: () => {
22752
+ const w = state.currentWs;
22753
+ return w != null && w.readyState === wrapper_default.OPEN;
22754
+ }
22755
+ });
22756
+ }
22757
+ function clearMainBridgeReconnectQuietOnOpen(state, log2) {
22758
+ clearReconnectQuietOnSuccessfulConnection(state.mainQuiet, log2, "[Bridge service] Reconnected.");
22759
+ }
22760
+ function scheduleMainBridgeReconnect(state, connect, log2) {
22761
+ if (state.closedByUser || state.currentWs != null) return;
22762
+ const delay2 = reconnectDelayMs(state.reconnectAttempt);
22763
+ state.reconnectAttempt += 1;
22764
+ logNextReconnectAttempt(log2, "[Bridge service]", state.mainQuiet, delay2, state.reconnectAttempt);
22765
+ state.reconnectTimeout = setTimeout(() => {
22766
+ state.reconnectTimeout = null;
22767
+ connect();
22768
+ }, delay2);
22769
+ }
22770
+
22771
+ // src/bridge/connection/reconnect/firehose-reconnect.ts
22772
+ var PROXY_AND_LOG_SERVICE_LABEL = "[Proxy and log service]";
22773
+ function beginFirehoseDeferredDisconnect(ctx, code, reason, log2) {
22774
+ beginDeferredDisconnectForReconnect({
22775
+ isClosedByUser: () => ctx.closedByUser,
22776
+ quiet: ctx.firehoseQuiet,
22777
+ code,
22778
+ reason,
22779
+ willReconnect: true,
22780
+ log: log2,
22781
+ serviceLabel: PROXY_AND_LOG_SERVICE_LABEL,
22782
+ shutdownDetail: "Not reconnecting (shutting down).",
22783
+ reconnectingDetail: "Reconnecting\u2026",
22784
+ shouldAbortQuietWindow: () => {
22785
+ const w = ctx.currentWs;
22786
+ if (!w || w.readyState !== wrapper_default.OPEN) return true;
22787
+ return ctx.firehoseHandle?.isConnected() ?? false;
22788
+ }
22789
+ });
22790
+ }
22791
+ function clearFirehoseReconnectQuietOnOpen(ctx, log2) {
22792
+ clearReconnectQuietOnSuccessfulConnection(
22793
+ ctx.firehoseQuiet,
22794
+ log2,
22795
+ `${PROXY_AND_LOG_SERVICE_LABEL} Reconnected.`
22796
+ );
22631
22797
  }
22632
22798
 
22633
22799
  // src/auth/run-pending-auth.ts
22634
- var PENDING_RECONNECT_MS = 2e3;
22635
22800
  var PENDING_KEEPALIVE_MS = 25e3;
22636
22801
  var BROWSER_OPEN_FALLBACK_MS = 4e3;
22637
22802
  function buildPendingBridgeUrl(apiUrl, connectionId) {
@@ -22650,10 +22815,35 @@ function runPendingAuth(options) {
22650
22815
  let hasOpenedBrowser = false;
22651
22816
  let resolved = false;
22652
22817
  let resolveAuth;
22653
- const authPromise = new Promise((resolve16) => {
22654
- resolveAuth = resolve16;
22818
+ const authPromise = new Promise((resolve14) => {
22819
+ resolveAuth = resolve14;
22655
22820
  });
22821
+ let reconnectAttempt = 0;
22822
+ const signInQuiet = createEmptyReconnectQuietSlot();
22823
+ function clearQuietOnOpen() {
22824
+ clearReconnectQuietOnSuccessfulConnection(
22825
+ signInQuiet,
22826
+ logFn,
22827
+ "[Bridge service] Sign-in connection restored."
22828
+ );
22829
+ reconnectAttempt = 0;
22830
+ }
22831
+ function beginDeferredPendingCloseLog(code, reason) {
22832
+ beginDeferredDisconnectForReconnect({
22833
+ isClosedByUser: () => resolved,
22834
+ quiet: signInQuiet,
22835
+ code,
22836
+ reason,
22837
+ willReconnect: true,
22838
+ log: logFn,
22839
+ serviceLabel: "[Bridge service]",
22840
+ shutdownDetail: "Not reconnecting (shutting down).",
22841
+ reconnectingDetail: "Waiting for browser sign-in; reconnecting\u2026",
22842
+ shouldAbortQuietWindow: () => ws != null && ws.readyState === wrapper_default.OPEN
22843
+ });
22844
+ }
22656
22845
  function cleanup() {
22846
+ clearReconnectQuietTimer(signInQuiet);
22657
22847
  if (reconnectTimeout) {
22658
22848
  clearTimeout(reconnectTimeout);
22659
22849
  reconnectTimeout = null;
@@ -22677,6 +22867,7 @@ function runPendingAuth(options) {
22677
22867
  ws = createWsBridge({
22678
22868
  url: url2,
22679
22869
  onOpen: () => {
22870
+ clearQuietOnOpen();
22680
22871
  sendWsMessage(ws, { type: "identify", role: "cli" });
22681
22872
  keepaliveInterval = setInterval(() => {
22682
22873
  if (resolved || !ws || ws.readyState !== 1) return;
@@ -22697,15 +22888,21 @@ function runPendingAuth(options) {
22697
22888
  keepaliveInterval = null;
22698
22889
  }
22699
22890
  if (resolved) return;
22700
- logFn(
22701
- formatWebSocketClose("[Bridge service]", code, reason, "pending sign-in; will reconnect in 2s")
22702
- );
22891
+ beginDeferredPendingCloseLog(code, reason);
22892
+ const delay2 = reconnectDelayMs(reconnectAttempt);
22893
+ reconnectAttempt += 1;
22894
+ if (signInQuiet.verboseLogs) {
22895
+ const delayLabel = formatReconnectDelayForLog(delay2);
22896
+ logFn(
22897
+ `[Bridge service] Next sign-in connection attempt in ${delayLabel} (attempt ${reconnectAttempt}).`
22898
+ );
22899
+ }
22703
22900
  reconnectTimeout = setTimeout(() => {
22704
22901
  reconnectTimeout = null;
22705
22902
  connect();
22706
- }, PENDING_RECONNECT_MS);
22903
+ }, delay2);
22707
22904
  },
22708
- onError: (err) => logFn(`[Bridge service] WebSocket error (pending sign-in): ${err.message}`),
22905
+ onError: (err) => logFn(`[Bridge service] WebSocket error while waiting for sign-in: ${err.message}`),
22709
22906
  onMessage: (data) => {
22710
22907
  const msg = data;
22711
22908
  if (msg.type === "auth_token" && typeof msg.token === "string") {
@@ -22746,7 +22943,7 @@ function buildBridgeUrl(apiUrl, workspaceId, authToken) {
22746
22943
 
22747
22944
  // src/git/discover-repos.ts
22748
22945
  import * as fs2 from "node:fs";
22749
- import * as path2 from "node:path";
22946
+ import * as path3 from "node:path";
22750
22947
 
22751
22948
  // ../../node_modules/.pnpm/simple-git@3.32.3/node_modules/simple-git/dist/esm/index.js
22752
22949
  var import_file_exists = __toESM(require_dist(), 1);
@@ -24008,8 +24205,8 @@ var init_git_executor_chain = __esm2({
24008
24205
  get cwd() {
24009
24206
  return this._cwd || this._executor.cwd;
24010
24207
  }
24011
- set cwd(cwd3) {
24012
- this._cwd = cwd3;
24208
+ set cwd(cwd) {
24209
+ this._cwd = cwd;
24013
24210
  }
24014
24211
  get env() {
24015
24212
  return this._executor.env;
@@ -24200,8 +24397,8 @@ var init_git_executor = __esm2({
24200
24397
  "use strict";
24201
24398
  init_git_executor_chain();
24202
24399
  GitExecutor = class {
24203
- constructor(cwd3, _scheduler, _plugins) {
24204
- this.cwd = cwd3;
24400
+ constructor(cwd, _scheduler, _plugins) {
24401
+ this.cwd = cwd;
24205
24402
  this._scheduler = _scheduler;
24206
24403
  this._plugins = _plugins;
24207
24404
  this._chain = new GitExecutorChain(this, this._scheduler, this._plugins);
@@ -27331,9 +27528,9 @@ async function isGitRepoDirectory(dirPath) {
27331
27528
  }
27332
27529
 
27333
27530
  // src/git/discover-repos.ts
27334
- async function discoverGitRepos(cwd3 = process.cwd()) {
27531
+ async function discoverGitRepos(cwd = getBridgeWorkspaceDirectory()) {
27335
27532
  const result = [];
27336
- const cwdResolved = path2.resolve(cwd3);
27533
+ const cwdResolved = path3.resolve(cwd);
27337
27534
  if (await isGitRepoDirectory(cwdResolved)) {
27338
27535
  const remoteUrl = await getRemoteOriginUrl(cwdResolved);
27339
27536
  result.push({ absolutePath: cwdResolved, remoteUrl });
@@ -27346,7 +27543,7 @@ async function discoverGitRepos(cwd3 = process.cwd()) {
27346
27543
  }
27347
27544
  for (const ent of entries) {
27348
27545
  if (!ent.isDirectory()) continue;
27349
- const childPath = path2.join(cwdResolved, ent.name);
27546
+ const childPath = path3.join(cwdResolved, ent.name);
27350
27547
  if (await isGitRepoDirectory(childPath)) {
27351
27548
  const remoteUrl = await getRemoteOriginUrl(childPath);
27352
27549
  result.push({ absolutePath: childPath, remoteUrl });
@@ -27355,11 +27552,11 @@ async function discoverGitRepos(cwd3 = process.cwd()) {
27355
27552
  return result;
27356
27553
  }
27357
27554
  async function discoverGitReposUnderRoot(rootAbs) {
27358
- const root = path2.resolve(rootAbs);
27555
+ const root = path3.resolve(rootAbs);
27359
27556
  const roots = [];
27360
27557
  async function walk(dir) {
27361
27558
  if (await isGitRepoDirectory(dir)) {
27362
- roots.push(path2.resolve(dir));
27559
+ roots.push(path3.resolve(dir));
27363
27560
  return;
27364
27561
  }
27365
27562
  let entries;
@@ -27370,7 +27567,7 @@ async function discoverGitReposUnderRoot(rootAbs) {
27370
27567
  }
27371
27568
  for (const ent of entries) {
27372
27569
  if (!ent.isDirectory() || ent.name === ".git") continue;
27373
- await walk(path2.join(dir, ent.name));
27570
+ await walk(path3.join(dir, ent.name));
27374
27571
  }
27375
27572
  }
27376
27573
  await walk(root);
@@ -27397,56 +27594,43 @@ function reportGitRepos(getWs, log2) {
27397
27594
  }
27398
27595
  }
27399
27596
  }).catch((err) => {
27400
- log2(`[Bridge service] git repo discovery failed: ${err instanceof Error ? err.message : String(err)}`);
27597
+ log2(
27598
+ `[Bridge service] Git repository discovery failed: ${err instanceof Error ? err.message : String(err)}`
27599
+ );
27401
27600
  });
27402
27601
  });
27403
27602
  }
27404
27603
 
27405
- // src/bridge/connection/schedule-reconnect.ts
27406
- var RECONNECT_BASE_MS = 1e3;
27407
- var RECONNECT_MAX_MS = 3e4;
27408
- function scheduleReconnect(state, connect, log2) {
27409
- if (state.closedByUser || state.currentWs != null) return;
27410
- const delay2 = Math.min(
27411
- RECONNECT_BASE_MS * 2 ** state.reconnectAttempt,
27412
- RECONNECT_MAX_MS
27413
- );
27414
- state.reconnectAttempt += 1;
27415
- log2(`[Bridge service] reconnect attempt ${state.reconnectAttempt} in ${delay2 / 1e3}s\u2026`);
27416
- state.reconnectTimeout = setTimeout(() => {
27417
- state.reconnectTimeout = null;
27418
- connect();
27419
- }, delay2);
27420
- }
27421
-
27422
27604
  // src/bridge/connection/close-bridge-connection.ts
27423
27605
  async function closeBridgeConnection(state, acpManager, devServerManager, log2) {
27424
27606
  log2?.("Shutting down\u2026");
27425
- await new Promise((resolve16) => setImmediate(resolve16));
27607
+ await new Promise((resolve14) => setImmediate(resolve14));
27426
27608
  if (devServerManager) {
27427
27609
  log2?.("Requesting dev server processes to stop\u2026");
27428
27610
  await devServerManager.shutdownAllGraceful();
27429
27611
  }
27430
27612
  state.closedByUser = true;
27613
+ clearReconnectQuietTimer(state.mainQuiet);
27614
+ clearReconnectQuietTimer(state.firehoseQuiet);
27431
27615
  if (state.reconnectTimeout != null) {
27432
27616
  log2?.("Cancelling reconnect timer\u2026");
27433
27617
  clearTimeout(state.reconnectTimeout);
27434
27618
  state.reconnectTimeout = null;
27435
27619
  }
27436
27620
  if (state.firehoseReconnectTimeout != null) {
27437
- log2?.("[Proxy and log service] cancelling reconnect timer\u2026");
27621
+ log2?.("[Proxy and log service] Cancelling reconnect timer\u2026");
27438
27622
  clearTimeout(state.firehoseReconnectTimeout);
27439
27623
  state.firehoseReconnectTimeout = null;
27440
27624
  }
27441
27625
  if (state.firehoseHandle) {
27442
- log2?.("[Proxy and log service] closing connection (CLI shutdown)\u2026");
27626
+ log2?.("[Proxy and log service] Closing connection (CLI shutdown)\u2026");
27443
27627
  state.firehoseHandle.close();
27444
27628
  state.firehoseHandle = null;
27445
27629
  }
27446
27630
  log2?.("Disconnecting local agents (ACP)\u2026");
27447
27631
  acpManager.disconnect();
27448
27632
  if (state.currentWs) {
27449
- log2?.("[Bridge service] closing connection (CLI shutdown)\u2026");
27633
+ log2?.("[Bridge service] Closing connection (CLI shutdown)\u2026");
27450
27634
  state.currentWs.removeAllListeners();
27451
27635
  const wsState = state.currentWs.readyState;
27452
27636
  if (wsState === 1 || wsState === 2) {
@@ -27462,11 +27646,20 @@ async function closeBridgeConnection(state, acpManager, devServerManager, log2)
27462
27646
  }
27463
27647
 
27464
27648
  // src/git/session-git-queue.ts
27649
+ import { execFile as execFile2 } from "node:child_process";
27650
+ import { promisify as promisify2 } from "node:util";
27651
+ import * as fs4 from "node:fs";
27652
+ import * as path5 from "node:path";
27653
+
27654
+ // src/git/pre-turn-snapshot.ts
27655
+ import * as fs3 from "node:fs";
27656
+ import * as path4 from "node:path";
27465
27657
  import { execFile } from "node:child_process";
27466
27658
  import { promisify } from "node:util";
27467
- import * as path3 from "node:path";
27468
27659
  var execFileAsync = promisify(execFile);
27469
- var sessionBoundaryBySessionId = /* @__PURE__ */ new Map();
27660
+ function snapshotsDirForCwd(agentCwd) {
27661
+ return path4.join(agentCwd, ".buildautomaton", "snapshots");
27662
+ }
27470
27663
  async function gitStashCreate(repoRoot, log2) {
27471
27664
  try {
27472
27665
  const { stdout } = await execFileAsync("git", ["stash", "create"], {
@@ -27475,51 +27668,149 @@ async function gitStashCreate(repoRoot, log2) {
27475
27668
  });
27476
27669
  return stdout.trim();
27477
27670
  } catch (e) {
27478
- log2(`[session-git-queue] stash create failed in ${repoRoot}: ${e instanceof Error ? e.message : String(e)}`);
27671
+ log2(
27672
+ `[snapshot] Git stash create failed in ${repoRoot}: ${e instanceof Error ? e.message : String(e)}`
27673
+ );
27479
27674
  return "";
27480
27675
  }
27481
27676
  }
27482
- async function captureSessionGitBoundary(options) {
27483
- const { sessionId, repoRoots, log: log2 } = options;
27484
- if (!repoRoots.length) return;
27677
+ async function gitRun(repoRoot, args, log2, label) {
27678
+ try {
27679
+ await execFileAsync("git", args, { cwd: repoRoot, maxBuffer: 10 * 1024 * 1024 });
27680
+ return { ok: true };
27681
+ } catch (e) {
27682
+ const msg = e instanceof Error ? e.message : String(e);
27683
+ log2(`[snapshot] Git ${label} failed in ${repoRoot}: ${msg}`);
27684
+ return { ok: false, error: msg };
27685
+ }
27686
+ }
27687
+ async function resolveSnapshotRepoRoots(options) {
27688
+ const { worktreePaths, fallbackCwd, log: log2 } = options;
27689
+ if (worktreePaths?.length) {
27690
+ const uniq = [...new Set(worktreePaths.map((p) => path4.resolve(p)))];
27691
+ return uniq;
27692
+ }
27693
+ try {
27694
+ const repos = await discoverGitReposUnderRoot(fallbackCwd);
27695
+ return repos.map((r) => r.absolutePath);
27696
+ } catch (e) {
27697
+ log2(`[snapshot] Discover repositories failed: ${e instanceof Error ? e.message : String(e)}`);
27698
+ return [];
27699
+ }
27700
+ }
27701
+ async function capturePreTurnSnapshot(options) {
27702
+ const { runId, repoRoots, agentCwd, log: log2 } = options;
27703
+ if (!runId || !repoRoots.length) {
27704
+ return { ok: false, error: "No git repos to snapshot" };
27705
+ }
27485
27706
  const repos = [];
27486
27707
  for (const root of repoRoots) {
27487
27708
  const stashSha = await gitStashCreate(root, log2);
27488
27709
  repos.push({ path: root, stashSha });
27489
27710
  }
27490
- sessionBoundaryBySessionId.set(sessionId, repos);
27491
- log2(`[session-git-queue] boundary stash ${sessionId.slice(0, 8)}\u2026 (${repos.length} repo(s))`);
27711
+ const dir = snapshotsDirForCwd(agentCwd);
27712
+ try {
27713
+ fs3.mkdirSync(dir, { recursive: true });
27714
+ } catch (e) {
27715
+ return { ok: false, error: e instanceof Error ? e.message : String(e) };
27716
+ }
27717
+ const payload = {
27718
+ runId,
27719
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
27720
+ repos
27721
+ };
27722
+ const filePath = path4.join(dir, `${runId}.json`);
27723
+ try {
27724
+ fs3.writeFileSync(filePath, JSON.stringify(payload, null, 2), "utf8");
27725
+ } catch (e) {
27726
+ return { ok: false, error: e instanceof Error ? e.message : String(e) };
27727
+ }
27728
+ const repoList = repos.map((r) => r.path).join(", ");
27729
+ log2(
27730
+ `[snapshot] Saved pre-turn snapshot ${runId.slice(0, 8)}\u2026 (${repos.length} repo(s)): ${repoList}`
27731
+ );
27732
+ return { ok: true, filePath, repos };
27733
+ }
27734
+ async function applyPreTurnSnapshot(filePath, log2) {
27735
+ let data;
27736
+ try {
27737
+ const raw = fs3.readFileSync(filePath, "utf8");
27738
+ data = JSON.parse(raw);
27739
+ } catch (e) {
27740
+ return { ok: false, error: e instanceof Error ? e.message : String(e) };
27741
+ }
27742
+ if (!Array.isArray(data.repos)) {
27743
+ return { ok: false, error: "Invalid snapshot file" };
27744
+ }
27745
+ for (const r of data.repos) {
27746
+ if (!r.path) continue;
27747
+ const reset = await gitRun(r.path, ["reset", "--hard", "HEAD"], log2, "reset --hard");
27748
+ if (!reset.ok) return reset;
27749
+ const clean = await gitRun(r.path, ["clean", "-fd"], log2, "clean -fd");
27750
+ if (!clean.ok) return clean;
27751
+ if (r.stashSha) {
27752
+ const ap = await gitRun(r.path, ["stash", "apply", r.stashSha], log2, "stash apply");
27753
+ if (!ap.ok) return ap;
27754
+ }
27755
+ }
27756
+ log2(`[snapshot] Restored pre-turn state for ${data.runId.slice(0, 8)}\u2026`);
27757
+ return { ok: true };
27758
+ }
27759
+ function snapshotFilePath(agentCwd, runId) {
27760
+ return path4.join(snapshotsDirForCwd(agentCwd), `${runId}.json`);
27761
+ }
27762
+
27763
+ // src/git/session-git-queue.ts
27764
+ var execFileAsync2 = promisify2(execFile2);
27765
+ var MAX_FULL_FILE_TEXT_BYTES = 512 * 1024;
27766
+ function readWorkspaceFileAsUtf8(absPath) {
27767
+ try {
27768
+ const st = fs4.statSync(absPath);
27769
+ if (!st.isFile() || st.size > MAX_FULL_FILE_TEXT_BYTES) return void 0;
27770
+ return fs4.readFileSync(absPath, "utf8");
27771
+ } catch {
27772
+ return void 0;
27773
+ }
27492
27774
  }
27493
- async function collectSessionDiffAndNotify(options) {
27494
- const { sessionId, runId, sendSessionUpdate, log: log2 } = options;
27495
- const repos = sessionBoundaryBySessionId.get(sessionId);
27496
- if (!repos?.length) {
27497
- log2(`[session-git-queue] no boundary for ${sessionId.slice(0, 8)}\u2026; skip aggregate diff`);
27775
+ async function collectTurnGitDiffFromPreTurnSnapshot(options) {
27776
+ const { sessionId, runId, agentCwd, sendSessionUpdate, log: log2 } = options;
27777
+ const filePath = snapshotFilePath(agentCwd, runId);
27778
+ let data;
27779
+ try {
27780
+ const raw = fs4.readFileSync(filePath, "utf8");
27781
+ data = JSON.parse(raw);
27782
+ } catch (e) {
27783
+ log2(
27784
+ `[session-git-queue] No pre-turn snapshot for run ${runId.slice(0, 8)}\u2026: ${e instanceof Error ? e.message : String(e)}`
27785
+ );
27498
27786
  return;
27499
27787
  }
27500
- sessionBoundaryBySessionId.delete(sessionId);
27501
- const multiRepo = repos.length > 1;
27502
- for (const repo of repos) {
27788
+ if (!Array.isArray(data.repos) || !data.repos.length) {
27789
+ log2(`[session-git-queue] Empty repos in snapshot ${runId.slice(0, 8)}\u2026; skipping aggregate diff.`);
27790
+ return;
27791
+ }
27792
+ const multiRepo = data.repos.length > 1;
27793
+ for (const repo of data.repos) {
27503
27794
  if (!repo.stashSha) continue;
27504
27795
  let namesRaw;
27505
27796
  try {
27506
- const { stdout } = await execFileAsync("git", ["diff", "--name-only", repo.stashSha], {
27797
+ const { stdout } = await execFileAsync2("git", ["diff", "--name-only", repo.stashSha], {
27507
27798
  cwd: repo.path,
27508
27799
  maxBuffer: 10 * 1024 * 1024
27509
27800
  });
27510
27801
  namesRaw = stdout;
27511
27802
  } catch (e) {
27512
27803
  log2(
27513
- `[session-git-queue] git diff --name-only failed in ${repo.path}: ${e instanceof Error ? e.message : String(e)}`
27804
+ `[session-git-queue] Git diff --name-only failed in ${repo.path}: ${e instanceof Error ? e.message : String(e)}`
27514
27805
  );
27515
27806
  continue;
27516
27807
  }
27517
27808
  const lines = namesRaw.split("\n").map((l) => l.trim()).filter(Boolean);
27518
- const slug = path3.basename(repo.path).replace(/[^\w.-]+/g, "_") || "repo";
27809
+ const slug = path5.basename(repo.path).replace(/[^\w.-]+/g, "_") || "repo";
27519
27810
  for (const rel of lines) {
27520
27811
  if (rel.includes("..")) continue;
27521
27812
  try {
27522
- const { stdout: patchContent } = await execFileAsync(
27813
+ const { stdout: patchContent } = await execFileAsync2(
27523
27814
  "git",
27524
27815
  ["diff", "--no-color", repo.stashSha, "--", rel],
27525
27816
  {
@@ -27529,15 +27820,20 @@ async function collectSessionDiffAndNotify(options) {
27529
27820
  );
27530
27821
  if (!patchContent.trim()) continue;
27531
27822
  const displayPath = multiRepo ? `${slug}/${rel}` : rel;
27823
+ const absFile = path5.join(repo.path, rel);
27824
+ const newText = readWorkspaceFileAsUtf8(absFile);
27532
27825
  sendSessionUpdate({
27533
27826
  type: "session_file_change",
27534
27827
  sessionId,
27535
27828
  runId,
27536
27829
  path: displayPath,
27537
- patchContent
27830
+ patchContent,
27831
+ ...newText !== void 0 ? { newText } : {}
27538
27832
  });
27539
27833
  } catch (e) {
27540
- log2(`[session-git-queue] git diff failed for ${rel}: ${e instanceof Error ? e.message : String(e)}`);
27834
+ log2(
27835
+ `[session-git-queue] Git diff failed for ${rel}: ${e instanceof Error ? e.message : String(e)}`
27836
+ );
27541
27837
  }
27542
27838
  }
27543
27839
  }
@@ -27551,15 +27847,21 @@ async function sendPromptToAgent(options) {
27551
27847
  promptId,
27552
27848
  sessionId,
27553
27849
  runId,
27850
+ agentCwd,
27554
27851
  sendResult,
27555
27852
  sendSessionUpdate,
27556
- collectSessionDiffAfterTurn,
27557
27853
  log: log2
27558
27854
  } = options;
27559
27855
  try {
27560
27856
  const result = await handle.sendPrompt(promptText, {});
27561
- if (collectSessionDiffAfterTurn && sessionId && runId && sendSessionUpdate && result.success) {
27562
- await collectSessionDiffAndNotify({ sessionId, runId, sendSessionUpdate, log: log2 });
27857
+ if (sessionId && runId && sendSessionUpdate && agentCwd && result.success) {
27858
+ await collectTurnGitDiffFromPreTurnSnapshot({
27859
+ sessionId,
27860
+ runId,
27861
+ agentCwd,
27862
+ sendSessionUpdate,
27863
+ log: log2
27864
+ });
27563
27865
  }
27564
27866
  sendResult({
27565
27867
  type: "prompt_result",
@@ -27569,12 +27871,12 @@ async function sendPromptToAgent(options) {
27569
27871
  ...result
27570
27872
  });
27571
27873
  if (!result.success) {
27572
- log2(`[agent] ${result.error ?? "error"}`);
27874
+ log2(`[Agent] ${result.error ?? "Error"}`);
27573
27875
  }
27574
27876
  } catch (err) {
27575
27877
  const errMsg = err instanceof Error ? err.message : String(err);
27576
- log2(`[agent] send failed: ${errMsg}`);
27577
- if (err instanceof Error && err.stack) log2(`[agent] ${err.stack}`);
27878
+ log2(`[Agent] Send failed: ${errMsg}`);
27879
+ if (err instanceof Error && err.stack) log2(`[Agent] ${err.stack}`);
27578
27880
  sendResult({
27579
27881
  type: "prompt_result",
27580
27882
  id: promptId,
@@ -27587,8 +27889,8 @@ async function sendPromptToAgent(options) {
27587
27889
  }
27588
27890
 
27589
27891
  // src/acp/ensure-acp-client.ts
27590
- import * as fs3 from "node:fs";
27591
- import * as path7 from "node:path";
27892
+ import * as fs5 from "node:fs";
27893
+ import * as path9 from "node:path";
27592
27894
 
27593
27895
  // src/error-message.ts
27594
27896
  function errorMessage(err) {
@@ -27612,27 +27914,27 @@ async function createCodexAcpClient(options) {
27612
27914
  }
27613
27915
 
27614
27916
  // src/acp/clients/cursor-acp-client.ts
27615
- import { readFileSync, writeFileSync, mkdirSync } from "node:fs";
27917
+ import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "node:fs";
27616
27918
  import { dirname } from "node:path";
27617
27919
  import { spawn as spawn4 } from "node:child_process";
27618
27920
  import * as readline from "node:readline";
27619
27921
 
27620
27922
  // src/acp/safe-fs-path.ts
27621
- import * as path4 from "node:path";
27622
- function resolveSafePathUnderCwd(cwd3, filePath) {
27923
+ import * as path6 from "node:path";
27924
+ function resolveSafePathUnderCwd(cwd, filePath) {
27623
27925
  const trimmed2 = filePath.trim();
27624
27926
  if (!trimmed2) return null;
27625
- const normalizedCwd = path4.resolve(cwd3);
27626
- const resolved = path4.isAbsolute(trimmed2) ? path4.normalize(trimmed2) : path4.resolve(normalizedCwd, trimmed2);
27627
- const rel = path4.relative(normalizedCwd, resolved);
27628
- if (rel.startsWith("..") || path4.isAbsolute(rel)) return null;
27927
+ const normalizedCwd = path6.resolve(cwd);
27928
+ const resolved = path6.isAbsolute(trimmed2) ? path6.normalize(trimmed2) : path6.resolve(normalizedCwd, trimmed2);
27929
+ const rel = path6.relative(normalizedCwd, resolved);
27930
+ if (rel.startsWith("..") || path6.isAbsolute(rel)) return null;
27629
27931
  return resolved;
27630
27932
  }
27631
- function toDisplayPathRelativeToCwd(cwd3, absolutePath) {
27632
- const normalizedCwd = path4.resolve(cwd3);
27633
- const rel = path4.relative(normalizedCwd, path4.resolve(absolutePath));
27634
- if (!rel || rel === "") return path4.basename(absolutePath);
27635
- return rel.split(path4.sep).join("/");
27933
+ function toDisplayPathRelativeToCwd(cwd, absolutePath) {
27934
+ const normalizedCwd = path6.resolve(cwd);
27935
+ const rel = path6.relative(normalizedCwd, path6.resolve(absolutePath));
27936
+ if (!rel || rel === "") return path6.basename(absolutePath);
27937
+ return rel.split(path6.sep).join("/");
27636
27938
  }
27637
27939
 
27638
27940
  // src/files/diff/unified-diff.ts
@@ -27681,6 +27983,19 @@ function editSnippetToUnifiedDiff(filePath, oldText, newText) {
27681
27983
  return out.join("\n");
27682
27984
  }
27683
27985
 
27986
+ // src/acp/format-session-update-kind-for-log.ts
27987
+ var SESSION_UPDATE_KIND_LABELS = {
27988
+ tool_call: "Tool call",
27989
+ tool_call_update: "Tool call status",
27990
+ agent_message_chunk: "Agent message chunk",
27991
+ update: "Session update"
27992
+ };
27993
+ function formatSessionUpdateKindForLog(kind) {
27994
+ const known = SESSION_UPDATE_KIND_LABELS[kind];
27995
+ if (known) return known;
27996
+ return kind.split("_").filter(Boolean).map((w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase()).join(" ");
27997
+ }
27998
+
27684
27999
  // src/acp/clients/cursor-acp-client.ts
27685
28000
  var FS_READ_METHODS = /* @__PURE__ */ new Set(["fs/read_text_file", "fs/readTextFile"]);
27686
28001
  var FS_WRITE_METHODS = /* @__PURE__ */ new Set(["fs/write_text_file", "fs/writeTextFile"]);
@@ -27706,16 +28021,16 @@ function sliceLinesByRange(content, line, limit) {
27706
28021
  return lines.slice(start, end).join("\n");
27707
28022
  }
27708
28023
  async function createCursorAcpClient(options) {
27709
- const { command, cwd: cwd3 = process.cwd(), onSessionUpdate, onRequest, onFileChange } = options;
28024
+ const { command, cwd = getBridgeWorkspaceDirectory(), onSessionUpdate, onRequest, onFileChange } = options;
27710
28025
  const dbgFs = process.env.BUILDAMATON_DEBUG_ACP_FS === "1";
27711
- const isWindows2 = process.platform === "win32";
28026
+ const isWindows = process.platform === "win32";
27712
28027
  const child = spawn4(command[0], command.slice(1), {
27713
- cwd: cwd3,
28028
+ cwd,
27714
28029
  stdio: ["pipe", "pipe", "inherit"],
27715
28030
  env: process.env,
27716
- shell: isWindows2
28031
+ shell: isWindows
27717
28032
  });
27718
- return new Promise((resolve16, reject) => {
28033
+ return new Promise((resolve14, reject) => {
27719
28034
  child.on("error", (err) => {
27720
28035
  child.kill();
27721
28036
  reject(new Error(formatSpawnError2(err, command[0])));
@@ -27768,7 +28083,10 @@ async function createCursorAcpClient(options) {
27768
28083
  const toolCall = update.toolCall ?? update.tool_call;
27769
28084
  const toolName = typeof toolCall?.name === "string" ? toolCall.name : "";
27770
28085
  if (dbgFs && (sessionUpdate === "tool_call" || sessionUpdate === "tool_call_update")) {
27771
- console.error(`[acp] recv session/update kind=${sessionUpdate} tool=${toolName || "(none)"}`);
28086
+ const kindLabel = formatSessionUpdateKindForLog(sessionUpdate ?? "update");
28087
+ console.error(
28088
+ `[acp] Received session update (${kindLabel}) tool=${toolName || "(none)"}`
28089
+ );
27772
28090
  }
27773
28091
  const isTextChunk = sessionUpdate === "agent_message_chunk" && update.content?.text;
27774
28092
  if (isTextChunk && update.content?.text) {
@@ -27788,14 +28106,14 @@ async function createCursorAcpClient(options) {
27788
28106
  if (dbgFs) {
27789
28107
  console.error(`[acp-fs] ${method} path=${filePath.slice(0, 200)}${filePath.length > 200 ? "\u2026" : ""}`);
27790
28108
  }
27791
- const abs = resolveSafePathUnderCwd(cwd3, filePath);
28109
+ const abs = resolveSafePathUnderCwd(cwd, filePath);
27792
28110
  if (!abs) {
27793
28111
  if (dbgFs) console.error(`[acp-fs] ${method} rejected path (outside cwd or empty)`);
27794
28112
  respondJsonRpcError(id, -32602, "Invalid or disallowed path");
27795
28113
  return;
27796
28114
  }
27797
28115
  try {
27798
- let content = readFileSync(abs, "utf8");
28116
+ let content = readFileSync3(abs, "utf8");
27799
28117
  const line2 = typeof params.line === "number" ? params.line : void 0;
27800
28118
  const limit = typeof params.limit === "number" ? params.limit : void 0;
27801
28119
  content = sliceLinesByRange(content, line2, limit);
@@ -27819,7 +28137,7 @@ async function createCursorAcpClient(options) {
27819
28137
  `[acp-fs] ${method} path=${filePath.slice(0, 200)}${filePath.length > 200 ? "\u2026" : ""} newBytes=${newText.length}`
27820
28138
  );
27821
28139
  }
27822
- const abs = resolveSafePathUnderCwd(cwd3, filePath);
28140
+ const abs = resolveSafePathUnderCwd(cwd, filePath);
27823
28141
  if (!abs) {
27824
28142
  if (dbgFs) console.error(`[acp-fs] ${method} rejected path (outside cwd or empty): ${filePath.slice(0, 120)}`);
27825
28143
  respondJsonRpcError(id, -32602, "Invalid or disallowed path");
@@ -27827,7 +28145,7 @@ async function createCursorAcpClient(options) {
27827
28145
  }
27828
28146
  let oldText = "";
27829
28147
  try {
27830
- oldText = readFileSync(abs, "utf8");
28148
+ oldText = readFileSync3(abs, "utf8");
27831
28149
  } catch (e) {
27832
28150
  if (e.code !== "ENOENT") {
27833
28151
  respondJsonRpcError(id, -32e3, e instanceof Error ? e.message : String(e));
@@ -27835,13 +28153,13 @@ async function createCursorAcpClient(options) {
27835
28153
  }
27836
28154
  }
27837
28155
  try {
27838
- mkdirSync(dirname(abs), { recursive: true });
27839
- writeFileSync(abs, newText, "utf8");
28156
+ mkdirSync2(dirname(abs), { recursive: true });
28157
+ writeFileSync2(abs, newText, "utf8");
27840
28158
  } catch (e) {
27841
28159
  respondJsonRpcError(id, -32e3, e instanceof Error ? e.message : String(e));
27842
28160
  return;
27843
28161
  }
27844
- const displayPath = toDisplayPathRelativeToCwd(cwd3, abs);
28162
+ const displayPath = toDisplayPathRelativeToCwd(cwd, abs);
27845
28163
  const patchContent = editSnippetToUnifiedDiff(displayPath, oldText, newText);
27846
28164
  onFileChange?.({ path: displayPath, oldText, newText, patchContent });
27847
28165
  respond(id, null);
@@ -27883,10 +28201,10 @@ async function createCursorAcpClient(options) {
27883
28201
  clientInfo: { name: "buildautomaton-cli", version: "0.1.0" }
27884
28202
  });
27885
28203
  await send("authenticate", { methodId: "cursor_login" });
27886
- const newResult = await send("session/new", { cwd: cwd3, mcpServers: [] });
28204
+ const newResult = await send("session/new", { cwd, mcpServers: [] });
27887
28205
  const sessionId = newResult?.sessionId ?? "";
27888
28206
  if (!sessionId) throw new Error("Cursor ACP session/new did not return sessionId");
27889
- resolve16({
28207
+ resolve14({
27890
28208
  sessionId,
27891
28209
  async sendPrompt(prompt, _options) {
27892
28210
  promptOutputBuffer = "";
@@ -27955,20 +28273,20 @@ function resolveAgentCommand(preferredAgentType) {
27955
28273
 
27956
28274
  // src/acp/session-file-change-path-kind.ts
27957
28275
  import { execFileSync as execFileSync3 } from "node:child_process";
27958
- import { existsSync, statSync } from "node:fs";
28276
+ import { existsSync, statSync as statSync2 } from "node:fs";
27959
28277
 
27960
28278
  // src/git/get-git-repo-root-sync.ts
27961
28279
  import { execFileSync } from "node:child_process";
27962
- import * as path5 from "node:path";
28280
+ import * as path7 from "node:path";
27963
28281
  function getGitRepoRootSync(startDir) {
27964
28282
  try {
27965
28283
  const out = execFileSync("git", ["rev-parse", "--show-toplevel"], {
27966
- cwd: path5.resolve(startDir),
28284
+ cwd: path7.resolve(startDir),
27967
28285
  encoding: "utf8",
27968
28286
  stdio: ["ignore", "pipe", "ignore"],
27969
28287
  maxBuffer: 1024 * 1024
27970
28288
  }).trim();
27971
- return out ? path5.resolve(out) : null;
28289
+ return out ? path7.resolve(out) : null;
27972
28290
  } catch {
27973
28291
  return null;
27974
28292
  }
@@ -27976,65 +28294,65 @@ function getGitRepoRootSync(startDir) {
27976
28294
 
27977
28295
  // src/acp/workspace-files.ts
27978
28296
  import { execFileSync as execFileSync2 } from "node:child_process";
27979
- import { readFileSync as readFileSync2 } from "node:fs";
27980
- import * as path6 from "node:path";
27981
- function resolveWorkspaceFilePath(cwd3, rawPath) {
28297
+ import { readFileSync as readFileSync4 } from "node:fs";
28298
+ import * as path8 from "node:path";
28299
+ function resolveWorkspaceFilePath(cwd, rawPath) {
27982
28300
  const trimmed2 = rawPath.trim();
27983
28301
  if (!trimmed2) return null;
27984
- const normalizedCwd = path6.resolve(cwd3);
27985
- let abs = resolveSafePathUnderCwd(cwd3, trimmed2);
28302
+ const normalizedCwd = path8.resolve(cwd);
28303
+ let abs = resolveSafePathUnderCwd(cwd, trimmed2);
27986
28304
  if (!abs) {
27987
- const candidate = path6.isAbsolute(trimmed2) ? path6.normalize(trimmed2) : path6.normalize(path6.resolve(normalizedCwd, trimmed2));
27988
- const gitRoot2 = getGitRepoRootSync(cwd3);
28305
+ const candidate = path8.isAbsolute(trimmed2) ? path8.normalize(trimmed2) : path8.normalize(path8.resolve(normalizedCwd, trimmed2));
28306
+ const gitRoot2 = getGitRepoRootSync(cwd);
27989
28307
  if (!gitRoot2) return null;
27990
- const rel = path6.relative(gitRoot2, candidate);
27991
- if (rel.startsWith("..") || path6.isAbsolute(rel)) return null;
28308
+ const rel = path8.relative(gitRoot2, candidate);
28309
+ if (rel.startsWith("..") || path8.isAbsolute(rel)) return null;
27992
28310
  abs = candidate;
27993
28311
  }
27994
- const gitRoot = getGitRepoRootSync(cwd3);
28312
+ const gitRoot = getGitRepoRootSync(cwd);
27995
28313
  if (gitRoot) {
27996
- const relFromRoot = path6.relative(gitRoot, abs);
27997
- if (!relFromRoot.startsWith("..") && !path6.isAbsolute(relFromRoot)) {
27998
- return { abs, display: relFromRoot.split(path6.sep).join("/") };
28314
+ const relFromRoot = path8.relative(gitRoot, abs);
28315
+ if (!relFromRoot.startsWith("..") && !path8.isAbsolute(relFromRoot)) {
28316
+ return { abs, display: relFromRoot.split(path8.sep).join("/") };
27999
28317
  }
28000
28318
  }
28001
- return { abs, display: toDisplayPathRelativeToCwd(cwd3, abs) };
28319
+ return { abs, display: toDisplayPathRelativeToCwd(cwd, abs) };
28002
28320
  }
28003
- function readUtf8WorkspaceFile(cwd3, displayPath) {
28321
+ function readUtf8WorkspaceFile(cwd, displayPath) {
28004
28322
  if (!displayPath || displayPath.includes("..")) return "";
28005
- const gitRoot = getGitRepoRootSync(cwd3);
28323
+ const gitRoot = getGitRepoRootSync(cwd);
28006
28324
  if (gitRoot) {
28007
- const abs2 = path6.resolve(gitRoot, displayPath);
28008
- const rel = path6.relative(gitRoot, abs2);
28009
- if (!rel.startsWith("..") && !path6.isAbsolute(rel)) {
28325
+ const abs2 = path8.resolve(gitRoot, displayPath);
28326
+ const rel = path8.relative(gitRoot, abs2);
28327
+ if (!rel.startsWith("..") && !path8.isAbsolute(rel)) {
28010
28328
  try {
28011
- return readFileSync2(abs2, "utf8");
28329
+ return readFileSync4(abs2, "utf8");
28012
28330
  } catch {
28013
28331
  }
28014
28332
  }
28015
28333
  }
28016
- const abs = resolveSafePathUnderCwd(cwd3, displayPath);
28334
+ const abs = resolveSafePathUnderCwd(cwd, displayPath);
28017
28335
  if (!abs) return "";
28018
28336
  try {
28019
- return readFileSync2(abs, "utf8");
28337
+ return readFileSync4(abs, "utf8");
28020
28338
  } catch {
28021
28339
  return "";
28022
28340
  }
28023
28341
  }
28024
- function tryWorkspaceDisplayToAbs(cwd3, displayPath) {
28342
+ function tryWorkspaceDisplayToAbs(cwd, displayPath) {
28025
28343
  if (!displayPath || displayPath.includes("..")) return null;
28026
- const gitRoot = getGitRepoRootSync(cwd3);
28344
+ const gitRoot = getGitRepoRootSync(cwd);
28027
28345
  if (gitRoot) {
28028
- const abs = path6.resolve(gitRoot, displayPath);
28029
- const rel = path6.relative(gitRoot, abs);
28030
- if (!rel.startsWith("..") && !path6.isAbsolute(rel)) return abs;
28346
+ const abs = path8.resolve(gitRoot, displayPath);
28347
+ const rel = path8.relative(gitRoot, abs);
28348
+ if (!rel.startsWith("..") && !path8.isAbsolute(rel)) return abs;
28031
28349
  }
28032
- return resolveSafePathUnderCwd(cwd3, displayPath);
28350
+ return resolveSafePathUnderCwd(cwd, displayPath);
28033
28351
  }
28034
- function readGitHeadBlob(cwd3, displayPath) {
28352
+ function readGitHeadBlob(cwd, displayPath) {
28035
28353
  if (!displayPath || displayPath.includes("..")) return "";
28036
- const gitRoot = getGitRepoRootSync(cwd3);
28037
- const execCwd = gitRoot ?? cwd3;
28354
+ const gitRoot = getGitRepoRootSync(cwd);
28355
+ const execCwd = gitRoot ?? cwd;
28038
28356
  try {
28039
28357
  return execFileSync2("git", ["show", `HEAD:${displayPath}`], {
28040
28358
  cwd: execCwd,
@@ -28047,9 +28365,9 @@ function readGitHeadBlob(cwd3, displayPath) {
28047
28365
  }
28048
28366
 
28049
28367
  // src/acp/session-file-change-path-kind.ts
28050
- function gitHeadPathObjectType(cwd3, displayPath) {
28368
+ function gitHeadPathObjectType(cwd, displayPath) {
28051
28369
  if (!displayPath || displayPath.includes("..")) return null;
28052
- const gitRoot = getGitRepoRootSync(cwd3);
28370
+ const gitRoot = getGitRepoRootSync(cwd);
28053
28371
  if (!gitRoot) return null;
28054
28372
  try {
28055
28373
  return execFileSync3("git", ["cat-file", "-t", `HEAD:${displayPath}`], {
@@ -28060,11 +28378,11 @@ function gitHeadPathObjectType(cwd3, displayPath) {
28060
28378
  return null;
28061
28379
  }
28062
28380
  }
28063
- function getSessionFileChangeDirectoryFlags(cwd3, displayPath) {
28064
- const abs = tryWorkspaceDisplayToAbs(cwd3, displayPath);
28381
+ function getSessionFileChangeDirectoryFlags(cwd, displayPath) {
28382
+ const abs = tryWorkspaceDisplayToAbs(cwd, displayPath);
28065
28383
  if (abs && existsSync(abs)) {
28066
28384
  try {
28067
- if (statSync(abs).isDirectory()) {
28385
+ if (statSync2(abs).isDirectory()) {
28068
28386
  return { isDirectory: true, directoryRemoved: false };
28069
28387
  }
28070
28388
  return { isDirectory: false, directoryRemoved: false };
@@ -28072,7 +28390,7 @@ function getSessionFileChangeDirectoryFlags(cwd3, displayPath) {
28072
28390
  return { isDirectory: false, directoryRemoved: false };
28073
28391
  }
28074
28392
  }
28075
- if (gitHeadPathObjectType(cwd3, displayPath) === "tree") {
28393
+ if (gitHeadPathObjectType(cwd, displayPath) === "tree") {
28076
28394
  return { isDirectory: true, directoryRemoved: true };
28077
28395
  }
28078
28396
  return { isDirectory: false, directoryRemoved: false };
@@ -28086,11 +28404,13 @@ function createBridgeOnFileChange(opts) {
28086
28404
  const sessionId = routing.sessionId;
28087
28405
  const send = getSendSessionUpdate();
28088
28406
  if (!send || !runId || !sessionId) {
28089
- log2(`[Bridge service] file change not forwarded path=${evt.path} (session/run not wired)`);
28407
+ log2(
28408
+ `[Bridge service] File change not forwarded (${evt.path}): session or run not wired to the bridge.`
28409
+ );
28090
28410
  return;
28091
28411
  }
28092
- const cwd3 = process.cwd();
28093
- const dirFlags = getSessionFileChangeDirectoryFlags(cwd3, evt.path);
28412
+ const cwd = getBridgeWorkspaceDirectory();
28413
+ const dirFlags = getSessionFileChangeDirectoryFlags(cwd, evt.path);
28094
28414
  try {
28095
28415
  send({
28096
28416
  type: "session_file_change",
@@ -28104,7 +28424,7 @@ function createBridgeOnFileChange(opts) {
28104
28424
  directoryRemoved: dirFlags.directoryRemoved
28105
28425
  });
28106
28426
  } catch (err) {
28107
- log2(`[Bridge service] session_file_change failed ${evt.path}: ${errorMessage(err)}`);
28427
+ log2(`[Bridge service] Session file change failed for ${evt.path}: ${errorMessage(err)}`);
28108
28428
  }
28109
28429
  };
28110
28430
  }
@@ -28131,7 +28451,9 @@ function createBridgeOnRequest(opts) {
28131
28451
  }
28132
28452
  });
28133
28453
  } catch (err) {
28134
- log2(`[Bridge service] ACP request forward failed (${request.method}): ${errorMessage(err)}`);
28454
+ log2(
28455
+ `[Bridge service] Agent protocol request forward failed (${request.method}): ${errorMessage(err)}`
28456
+ );
28135
28457
  }
28136
28458
  };
28137
28459
  }
@@ -28163,12 +28485,12 @@ function extractDiffPath(o) {
28163
28485
  }
28164
28486
 
28165
28487
  // src/acp/hooks/extract-acp-file-diffs-from-update/push-diff.ts
28166
- function pushDiffIfComplete(o, cwd3, out) {
28488
+ function pushDiffIfComplete(o, cwd, out) {
28167
28489
  const t = o.type;
28168
28490
  if (typeof t !== "string" || t.toLowerCase() !== "diff") return;
28169
28491
  const rawPath = extractDiffPath(o);
28170
28492
  if (!rawPath) return;
28171
- const resolved = resolveWorkspaceFilePath(cwd3, rawPath);
28493
+ const resolved = resolveWorkspaceFilePath(cwd, rawPath);
28172
28494
  if (!resolved) return;
28173
28495
  const oldText = readOptionalTextField(o.oldText ?? o.old_text ?? o.before ?? o.oldContent);
28174
28496
  const newText = readOptionalTextField(o.newText ?? o.new_text ?? o.after ?? o.newContent);
@@ -28187,17 +28509,17 @@ var NEST_KEYS = [
28187
28509
  "data",
28188
28510
  "arguments"
28189
28511
  ];
28190
- function walkValue(value, cwd3, depth, out) {
28512
+ function walkValue(value, cwd, depth, out) {
28191
28513
  if (depth > 12 || value == null) return;
28192
28514
  if (Array.isArray(value)) {
28193
- for (const x of value) walkValue(x, cwd3, depth + 1, out);
28515
+ for (const x of value) walkValue(x, cwd, depth + 1, out);
28194
28516
  return;
28195
28517
  }
28196
28518
  if (typeof value !== "object") return;
28197
28519
  const o = value;
28198
- pushDiffIfComplete(o, cwd3, out);
28520
+ pushDiffIfComplete(o, cwd, out);
28199
28521
  if (o.type === "content" && o.content != null && typeof o.content === "object") {
28200
- walkValue(o.content, cwd3, depth + 1, out);
28522
+ walkValue(o.content, cwd, depth + 1, out);
28201
28523
  }
28202
28524
  for (const k of NEST_KEYS) {
28203
28525
  if (o.type === "content" && k === "content") continue;
@@ -28205,23 +28527,23 @@ function walkValue(value, cwd3, depth, out) {
28205
28527
  if (v == null) continue;
28206
28528
  if (k === "arguments" && typeof v === "string") {
28207
28529
  try {
28208
- walkValue(JSON.parse(v), cwd3, depth + 1, out);
28530
+ walkValue(JSON.parse(v), cwd, depth + 1, out);
28209
28531
  } catch {
28210
28532
  }
28211
28533
  continue;
28212
28534
  }
28213
- walkValue(v, cwd3, depth + 1, out);
28535
+ walkValue(v, cwd, depth + 1, out);
28214
28536
  }
28215
28537
  }
28216
28538
 
28217
28539
  // src/acp/hooks/extract-acp-file-diffs-from-update/extract.ts
28218
- function extractAcpFileDiffsFromUpdate(update, cwd3) {
28540
+ function extractAcpFileDiffsFromUpdate(update, cwd) {
28219
28541
  if (!update || typeof update !== "object") return [];
28220
28542
  const u = update;
28221
28543
  const out = [];
28222
28544
  const content = u.content;
28223
28545
  if (Array.isArray(content)) {
28224
- for (const x of content) walkValue(x, cwd3, 0, out);
28546
+ for (const x of content) walkValue(x, cwd, 0, out);
28225
28547
  }
28226
28548
  const byPath = /* @__PURE__ */ new Map();
28227
28549
  for (const d of out) byPath.set(d.path, d);
@@ -28229,18 +28551,18 @@ function extractAcpFileDiffsFromUpdate(update, cwd3) {
28229
28551
  }
28230
28552
 
28231
28553
  // src/acp/hooks/extract-tool-target-paths.ts
28232
- function addPath(cwd3, raw, out) {
28554
+ function addPath(cwd, raw, out) {
28233
28555
  if (typeof raw !== "string") return;
28234
28556
  const trimmed2 = raw.trim();
28235
28557
  if (!trimmed2) return;
28236
- const resolved = resolveWorkspaceFilePath(cwd3, trimmed2);
28558
+ const resolved = resolveWorkspaceFilePath(cwd, trimmed2);
28237
28559
  if (!resolved) return;
28238
28560
  out.add(resolved.display);
28239
28561
  }
28240
- function walkLocations(cwd3, loc, out) {
28562
+ function walkLocations(cwd, loc, out) {
28241
28563
  if (!Array.isArray(loc)) return;
28242
28564
  for (const item of loc) {
28243
- if (item && typeof item === "object") addPath(cwd3, item.path, out);
28565
+ if (item && typeof item === "object") addPath(cwd, item.path, out);
28244
28566
  }
28245
28567
  }
28246
28568
  var PATH_KEYS = [
@@ -28253,56 +28575,56 @@ var PATH_KEYS = [
28253
28575
  "file_path",
28254
28576
  "target_file"
28255
28577
  ];
28256
- function collectFromObject(cwd3, obj, out, depth) {
28578
+ function collectFromObject(cwd, obj, out, depth) {
28257
28579
  if (depth > 10) return;
28258
28580
  for (const k of PATH_KEYS) {
28259
- if (k in obj) addPath(cwd3, obj[k], out);
28581
+ if (k in obj) addPath(cwd, obj[k], out);
28260
28582
  }
28261
28583
  for (const v of Object.values(obj)) {
28262
- if (v != null && typeof v === "object") collectUnknown(cwd3, v, out, depth + 1);
28584
+ if (v != null && typeof v === "object") collectUnknown(cwd, v, out, depth + 1);
28263
28585
  }
28264
28586
  }
28265
- function collectUnknown(cwd3, v, out, depth) {
28587
+ function collectUnknown(cwd, v, out, depth) {
28266
28588
  if (depth > 10 || v == null) return;
28267
28589
  if (Array.isArray(v)) {
28268
- for (const x of v) collectUnknown(cwd3, x, out, depth + 1);
28590
+ for (const x of v) collectUnknown(cwd, x, out, depth + 1);
28269
28591
  return;
28270
28592
  }
28271
- if (typeof v === "object") collectFromObject(cwd3, v, out, depth);
28593
+ if (typeof v === "object") collectFromObject(cwd, v, out, depth);
28272
28594
  }
28273
- function walkContentArray(cwd3, content, out) {
28595
+ function walkContentArray(cwd, content, out) {
28274
28596
  if (!Array.isArray(content)) return;
28275
28597
  for (const item of content) {
28276
28598
  if (!item || typeof item !== "object") continue;
28277
28599
  const o = item;
28278
28600
  if (typeof o.type === "string" && o.type.toLowerCase() === "diff") {
28279
- for (const k of PATH_KEYS) if (k in o) addPath(cwd3, o[k], out);
28601
+ for (const k of PATH_KEYS) if (k in o) addPath(cwd, o[k], out);
28280
28602
  }
28281
28603
  if (o.type === "content" && o.content != null && typeof o.content === "object") {
28282
28604
  const inner = o.content;
28283
28605
  if (typeof inner.type === "string" && inner.type.toLowerCase() === "diff") {
28284
- for (const k of PATH_KEYS) if (k in inner) addPath(cwd3, inner[k], out);
28606
+ for (const k of PATH_KEYS) if (k in inner) addPath(cwd, inner[k], out);
28285
28607
  }
28286
28608
  }
28287
28609
  }
28288
28610
  }
28289
- function extractToolTargetDisplayPaths(update, cwd3) {
28611
+ function extractToolTargetDisplayPaths(update, cwd) {
28290
28612
  const out = /* @__PURE__ */ new Set();
28291
28613
  if (!update || typeof update !== "object") return [];
28292
28614
  const u = update;
28293
- walkLocations(cwd3, u.locations, out);
28294
- walkLocations(cwd3, u.fileLocations, out);
28295
- walkLocations(cwd3, u.file_locations, out);
28615
+ walkLocations(cwd, u.locations, out);
28616
+ walkLocations(cwd, u.fileLocations, out);
28617
+ walkLocations(cwd, u.file_locations, out);
28296
28618
  const tc = u.toolCall ?? u.tool_call;
28297
28619
  if (tc && typeof tc.arguments === "string") {
28298
28620
  try {
28299
28621
  const parsed = JSON.parse(tc.arguments);
28300
- collectUnknown(cwd3, parsed, out, 0);
28622
+ collectUnknown(cwd, parsed, out, 0);
28301
28623
  } catch {
28302
28624
  }
28303
28625
  }
28304
- walkContentArray(cwd3, u.content, out);
28305
- collectFromObject(cwd3, u, out, 0);
28626
+ walkContentArray(cwd, u.content, out);
28627
+ collectFromObject(cwd, u, out, 0);
28306
28628
  return [...out];
28307
28629
  }
28308
28630
 
@@ -28368,7 +28690,7 @@ var PathSnapshotTracker = class {
28368
28690
  this.beforeByToolKey.delete(toolKey);
28369
28691
  this.accumulatedPathsByToolKey.delete(toolKey);
28370
28692
  }
28371
- captureBeforeFromDisk(toolKey, paths, cwd3) {
28693
+ captureBeforeFromDisk(toolKey, paths, cwd) {
28372
28694
  if (paths.length === 0) return;
28373
28695
  let m = this.beforeByToolKey.get(toolKey);
28374
28696
  if (!m) {
@@ -28377,10 +28699,10 @@ var PathSnapshotTracker = class {
28377
28699
  }
28378
28700
  for (const p of paths) {
28379
28701
  if (m.has(p)) continue;
28380
- m.set(p, readUtf8WorkspaceFile(cwd3, p));
28702
+ m.set(p, readUtf8WorkspaceFile(cwd, p));
28381
28703
  }
28382
28704
  }
28383
- ensureBeforeFromHeadForMissing(toolKey, paths, cwd3) {
28705
+ ensureBeforeFromHeadForMissing(toolKey, paths, cwd) {
28384
28706
  let m = this.beforeByToolKey.get(toolKey);
28385
28707
  if (!m) {
28386
28708
  m = /* @__PURE__ */ new Map();
@@ -28388,10 +28710,10 @@ var PathSnapshotTracker = class {
28388
28710
  }
28389
28711
  for (const p of paths) {
28390
28712
  if (m.has(p)) continue;
28391
- m.set(p, readGitHeadBlob(cwd3, p));
28713
+ m.set(p, readGitHeadBlob(cwd, p));
28392
28714
  }
28393
28715
  }
28394
- flushPathSnapshots(toolKey, cwd3, sentPaths, send, runId, sessionId, log2) {
28716
+ flushPathSnapshots(toolKey, cwd, sentPaths, send, runId, sessionId, log2) {
28395
28717
  const t = this.debouncers.get(toolKey);
28396
28718
  if (t) clearTimeout(t);
28397
28719
  this.debouncers.delete(toolKey);
@@ -28400,10 +28722,10 @@ var PathSnapshotTracker = class {
28400
28722
  this.beforeByToolKey.delete(toolKey);
28401
28723
  if (!send || !runId || !sessionId) return;
28402
28724
  for (const [displayPath, oldText] of beforeMap) {
28403
- const newText = readUtf8WorkspaceFile(cwd3, displayPath);
28725
+ const newText = readUtf8WorkspaceFile(cwd, displayPath);
28404
28726
  if (oldText === newText) continue;
28405
28727
  const patchContent = editSnippetToUnifiedDiff(displayPath, oldText, newText);
28406
- const dirFlags = getSessionFileChangeDirectoryFlags(cwd3, displayPath);
28728
+ const dirFlags = getSessionFileChangeDirectoryFlags(cwd, displayPath);
28407
28729
  try {
28408
28730
  send({
28409
28731
  type: "session_file_change",
@@ -28418,35 +28740,35 @@ var PathSnapshotTracker = class {
28418
28740
  });
28419
28741
  sentPaths.add(displayPath);
28420
28742
  } catch (err) {
28421
- log2(`[Bridge service] session_file_change failed ${displayPath}: ${errorMessage(err)}`);
28743
+ log2(`[Bridge service] Session file change failed for ${displayPath}: ${errorMessage(err)}`);
28422
28744
  }
28423
28745
  }
28424
28746
  }
28425
- scheduleDebouncedFlush(toolKey, cwd3, sentPaths, send, runId, sessionId, log2) {
28747
+ scheduleDebouncedFlush(toolKey, cwd, sentPaths, send, runId, sessionId, log2) {
28426
28748
  const prev = this.debouncers.get(toolKey);
28427
28749
  if (prev) clearTimeout(prev);
28428
28750
  this.debouncers.set(
28429
28751
  toolKey,
28430
28752
  setTimeout(() => {
28431
28753
  this.debouncers.delete(toolKey);
28432
- this.flushPathSnapshots(toolKey, cwd3, sentPaths, send, runId, sessionId, log2);
28754
+ this.flushPathSnapshots(toolKey, cwd, sentPaths, send, runId, sessionId, log2);
28433
28755
  }, PATH_SNAPSHOT_DEBOUNCE_MS)
28434
28756
  );
28435
28757
  }
28436
- handleToolCallLifecycle(updateKind, toolKey, toolPaths, status, cwd3, sentPaths, send, runId, sessionId, log2) {
28758
+ handleToolCallLifecycle(updateKind, toolKey, toolPaths, status, cwd, sentPaths, send, runId, sessionId, log2) {
28437
28759
  if (updateKind === "tool_call") {
28438
28760
  this.resetToolSnapshots(toolKey);
28439
28761
  }
28440
28762
  if (updateKind === "tool_call") {
28441
- this.captureBeforeFromDisk(toolKey, toolPaths, cwd3);
28763
+ this.captureBeforeFromDisk(toolKey, toolPaths, cwd);
28442
28764
  } else if (updateKind === "tool_call_update") {
28443
28765
  if (isCompletedToolStatus(status)) {
28444
- this.ensureBeforeFromHeadForMissing(toolKey, toolPaths, cwd3);
28445
- this.flushPathSnapshots(toolKey, cwd3, sentPaths, send, runId, sessionId, log2);
28766
+ this.ensureBeforeFromHeadForMissing(toolKey, toolPaths, cwd);
28767
+ this.flushPathSnapshots(toolKey, cwd, sentPaths, send, runId, sessionId, log2);
28446
28768
  } else {
28447
- this.captureBeforeFromDisk(toolKey, toolPaths, cwd3);
28769
+ this.captureBeforeFromDisk(toolKey, toolPaths, cwd);
28448
28770
  if (this.beforeByToolKey.has(toolKey)) {
28449
- this.scheduleDebouncedFlush(toolKey, cwd3, sentPaths, send, runId, sessionId, log2);
28771
+ this.scheduleDebouncedFlush(toolKey, cwd, sentPaths, send, runId, sessionId, log2);
28450
28772
  }
28451
28773
  }
28452
28774
  }
@@ -28454,11 +28776,11 @@ var PathSnapshotTracker = class {
28454
28776
  };
28455
28777
 
28456
28778
  // src/acp/hooks/bridge-on-session-update/send-structured-file-changes.ts
28457
- function sendExtractedDiffsAsSessionFileChanges(diffs, send, cwd3, sessionId, runId, sentPaths, log2) {
28779
+ function sendExtractedDiffsAsSessionFileChanges(diffs, send, cwd, sessionId, runId, sentPaths, log2) {
28458
28780
  for (const d of diffs) {
28459
28781
  try {
28460
28782
  const patchContent = editSnippetToUnifiedDiff(d.path, d.oldText, d.newText);
28461
- const dirFlags = getSessionFileChangeDirectoryFlags(cwd3, d.path);
28783
+ const dirFlags = getSessionFileChangeDirectoryFlags(cwd, d.path);
28462
28784
  send({
28463
28785
  type: "session_file_change",
28464
28786
  sessionId,
@@ -28472,19 +28794,19 @@ function sendExtractedDiffsAsSessionFileChanges(diffs, send, cwd3, sessionId, ru
28472
28794
  });
28473
28795
  sentPaths.add(d.path);
28474
28796
  } catch (err) {
28475
- log2(`[Bridge service] session_file_change failed ${d.path}: ${errorMessage(err)}`);
28797
+ log2(`[Bridge service] Session file change failed for ${d.path}: ${errorMessage(err)}`);
28476
28798
  }
28477
28799
  }
28478
28800
  }
28479
- function sendGitHeadVsWorkspaceForToolPaths(mergedPaths, sentPaths, send, cwd3, sessionId, runId, log2) {
28801
+ function sendGitHeadVsWorkspaceForToolPaths(mergedPaths, sentPaths, send, cwd, sessionId, runId, log2) {
28480
28802
  for (const displayPath of mergedPaths) {
28481
28803
  if (sentPaths.has(displayPath)) continue;
28482
- const oldText = readGitHeadBlob(cwd3, displayPath);
28483
- const newText = readUtf8WorkspaceFile(cwd3, displayPath);
28804
+ const oldText = readGitHeadBlob(cwd, displayPath);
28805
+ const newText = readUtf8WorkspaceFile(cwd, displayPath);
28484
28806
  if (oldText === newText) continue;
28485
28807
  try {
28486
28808
  const patchContent = editSnippetToUnifiedDiff(displayPath, oldText, newText);
28487
- const dirFlags = getSessionFileChangeDirectoryFlags(cwd3, displayPath);
28809
+ const dirFlags = getSessionFileChangeDirectoryFlags(cwd, displayPath);
28488
28810
  send({
28489
28811
  type: "session_file_change",
28490
28812
  sessionId,
@@ -28498,7 +28820,7 @@ function sendGitHeadVsWorkspaceForToolPaths(mergedPaths, sentPaths, send, cwd3,
28498
28820
  });
28499
28821
  sentPaths.add(displayPath);
28500
28822
  } catch (err) {
28501
- log2(`[Bridge service] session_file_change failed ${displayPath}: ${errorMessage(err)}`);
28823
+ log2(`[Bridge service] Session file change failed for ${displayPath}: ${errorMessage(err)}`);
28502
28824
  }
28503
28825
  }
28504
28826
  }
@@ -28511,7 +28833,7 @@ function createBridgeOnSessionUpdate(opts) {
28511
28833
  const runId = routing.runId;
28512
28834
  const sessionId = routing.sessionId;
28513
28835
  pathTracker.onRunIdChanged(runId);
28514
- const cwd3 = process.cwd();
28836
+ const cwd = getBridgeWorkspaceDirectory();
28515
28837
  const send = getSendSessionUpdate();
28516
28838
  const sentFileChangePaths = /* @__PURE__ */ new Set();
28517
28839
  const p = params;
@@ -28519,7 +28841,7 @@ function createBridgeOnSessionUpdate(opts) {
28519
28841
  const isCompletedToolCallUpdate = updateKind === "tool_call_update" && isCompletedToolStatus(p.status);
28520
28842
  const toolName = p.toolCall?.name ?? p.tool_call?.name ?? "";
28521
28843
  const isToolUpdate = updateKind === "tool_call" || updateKind === "tool_call_update" || typeof toolName === "string" && toolName.length > 0;
28522
- const toolPaths = isToolUpdate ? extractToolTargetDisplayPaths(params, cwd3) : [];
28844
+ const toolPaths = isToolUpdate ? extractToolTargetDisplayPaths(params, cwd) : [];
28523
28845
  const toolKey = isToolUpdate ? pathTracker.resolveToolKey(params, updateKind) : "";
28524
28846
  if (updateKind === "tool_call") {
28525
28847
  pathTracker.resetToolSnapshots(toolKey);
@@ -28534,7 +28856,7 @@ function createBridgeOnSessionUpdate(opts) {
28534
28856
  toolKey,
28535
28857
  toolPaths,
28536
28858
  p.status,
28537
- cwd3,
28859
+ cwd,
28538
28860
  sentFileChangePaths,
28539
28861
  deliver,
28540
28862
  runId ?? "",
@@ -28542,18 +28864,18 @@ function createBridgeOnSessionUpdate(opts) {
28542
28864
  log2
28543
28865
  );
28544
28866
  }
28545
- const diffs = extractAcpFileDiffsFromUpdate(params, cwd3);
28867
+ const diffs = extractAcpFileDiffsFromUpdate(params, cwd);
28546
28868
  if (diffs.length > 0 && send && runId && sessionId) {
28547
- sendExtractedDiffsAsSessionFileChanges(diffs, send, cwd3, sessionId, runId, sentFileChangePaths, log2);
28869
+ sendExtractedDiffsAsSessionFileChanges(diffs, send, cwd, sessionId, runId, sentFileChangePaths, log2);
28548
28870
  } else if (diffs.length > 0) {
28549
28871
  log2(
28550
- `[Bridge service] ACP file diff(s) not forwarded (${diffs.length}): session/run not wired to bridge`
28872
+ `[Bridge service] Agent file diff(s) not forwarded (${diffs.length}): session or run not wired to the bridge.`
28551
28873
  );
28552
28874
  }
28553
28875
  if (isCompletedToolCallUpdate && send && runId && sessionId) {
28554
28876
  const acc = pathTracker.accumulatedPathsByToolKey.get(toolKey);
28555
28877
  const merged = [.../* @__PURE__ */ new Set([...acc ? [...acc] : [], ...toolPaths])];
28556
- sendGitHeadVsWorkspaceForToolPaths(merged, sentFileChangePaths, send, cwd3, sessionId, runId, log2);
28878
+ sendGitHeadVsWorkspaceForToolPaths(merged, sentFileChangePaths, send, cwd, sessionId, runId, log2);
28557
28879
  pathTracker.accumulatedPathsByToolKey.delete(toolKey);
28558
28880
  }
28559
28881
  if (runId && send) {
@@ -28566,7 +28888,9 @@ function createBridgeOnSessionUpdate(opts) {
28566
28888
  payload: params
28567
28889
  });
28568
28890
  } catch (err) {
28569
- log2(`[Bridge service] session_update send failed (${updateKind}): ${errorMessage(err)}`);
28891
+ log2(
28892
+ `[Bridge service] Session update send failed (${formatSessionUpdateKindForLog(updateKind)}): ${errorMessage(err)}`
28893
+ );
28570
28894
  }
28571
28895
  }
28572
28896
  };
@@ -28583,11 +28907,11 @@ function buildAcpSessionBridgeHooks(opts) {
28583
28907
 
28584
28908
  // src/acp/ensure-acp-client.ts
28585
28909
  async function ensureAcpClient(options) {
28586
- const { state, preferredAgentType, mode, cwd: cwd3, routing, sendSessionUpdate, sendRequest, log: log2 } = options;
28587
- const targetCwd = path7.resolve(
28588
- cwd3 != null && String(cwd3).trim() !== "" ? String(cwd3).trim() : process.cwd()
28910
+ const { state, preferredAgentType, mode, cwd, routing, sendSessionUpdate, sendRequest, log: log2 } = options;
28911
+ const targetCwd = path9.resolve(
28912
+ cwd != null && String(cwd).trim() !== "" ? String(cwd).trim() : getBridgeWorkspaceDirectory()
28589
28913
  );
28590
- if (state.acpHandle && state.lastAcpCwd != null && path7.resolve(state.lastAcpCwd) !== path7.resolve(targetCwd)) {
28914
+ if (state.acpHandle && state.lastAcpCwd != null && path9.resolve(state.lastAcpCwd) !== path9.resolve(targetCwd)) {
28591
28915
  try {
28592
28916
  state.acpHandle.disconnect();
28593
28917
  } catch {
@@ -28599,7 +28923,7 @@ async function ensureAcpClient(options) {
28599
28923
  const resolved = resolveAgentCommand(preferredAgentType);
28600
28924
  if (!resolved) {
28601
28925
  log2(
28602
- `[agent] No local agent type (${preferredAgentType === null ? "none" : `"${preferredAgentType}"`}). Send agentType on prompts or agent_config after identify.`
28926
+ `[Agent] No local agent type (${preferredAgentType === null ? "none" : `"${preferredAgentType}"`}). Send agent type on prompts or agent configuration after identify.`
28603
28927
  );
28604
28928
  state.lastAcpStartError = "No agent type: ensure the app sends agentType on prompts or agent_config for this bridge.";
28605
28929
  return null;
@@ -28618,15 +28942,15 @@ async function ensureAcpClient(options) {
28618
28942
  if (!state.acpStartPromise) {
28619
28943
  let statOk = false;
28620
28944
  try {
28621
- const st = fs3.statSync(targetCwd);
28945
+ const st = fs5.statSync(targetCwd);
28622
28946
  statOk = st.isDirectory();
28623
28947
  if (!statOk) {
28624
28948
  state.lastAcpStartError = `Agent cwd is not a directory: ${targetCwd}`;
28625
- log2(`[agent] ${state.lastAcpStartError}`);
28949
+ log2(`[Agent] ${state.lastAcpStartError}`);
28626
28950
  }
28627
28951
  } catch {
28628
28952
  state.lastAcpStartError = `Agent cwd missing or inaccessible: ${targetCwd}`;
28629
- log2(`[agent] ${state.lastAcpStartError}`);
28953
+ log2(`[Agent] ${state.lastAcpStartError}`);
28630
28954
  }
28631
28955
  if (!statOk) {
28632
28956
  return null;
@@ -28651,7 +28975,7 @@ async function ensureAcpClient(options) {
28651
28975
  return h;
28652
28976
  }).catch((err) => {
28653
28977
  state.lastAcpStartError = errorMessage(err);
28654
- log2(`[agent] failed to start: ${state.lastAcpStartError}`);
28978
+ log2(`[Agent] Failed to start: ${state.lastAcpStartError}`);
28655
28979
  state.acpStartPromise = null;
28656
28980
  state.acpAgentKey = null;
28657
28981
  return null;
@@ -28687,10 +29011,9 @@ async function createAcpManager(options) {
28687
29011
  runId,
28688
29012
  mode,
28689
29013
  agentType,
28690
- cwd: cwd3,
29014
+ cwd,
28691
29015
  sendResult,
28692
- sendSessionUpdate,
28693
- collectSessionDiffAfterTurn
29016
+ sendSessionUpdate
28694
29017
  } = opts;
28695
29018
  const preferredForPrompt = agentType ?? backendFallbackAgentType ?? null;
28696
29019
  pendingCancelRunId = void 0;
@@ -28702,7 +29025,7 @@ async function createAcpManager(options) {
28702
29025
  state,
28703
29026
  preferredAgentType: preferredForPrompt,
28704
29027
  mode,
28705
- cwd: cwd3,
29028
+ cwd,
28706
29029
  routing: promptRouting,
28707
29030
  sendSessionUpdate,
28708
29031
  sendRequest: sendSessionUpdate,
@@ -28743,9 +29066,9 @@ async function createAcpManager(options) {
28743
29066
  promptId,
28744
29067
  sessionId,
28745
29068
  runId,
29069
+ agentCwd: cwd,
28746
29070
  sendResult,
28747
29071
  sendSessionUpdate,
28748
- collectSessionDiffAfterTurn,
28749
29072
  log: log2
28750
29073
  });
28751
29074
  }
@@ -28764,7 +29087,7 @@ async function createAcpManager(options) {
28764
29087
  await handle.cancel();
28765
29088
  return true;
28766
29089
  } catch (err) {
28767
- log2(`[agent] cancel failed: ${err instanceof Error ? err.message : String(err)}`);
29090
+ log2(`[Agent] Cancel failed: ${err instanceof Error ? err.message : String(err)}`);
28768
29091
  return false;
28769
29092
  }
28770
29093
  }
@@ -28801,7 +29124,9 @@ var handleBridgeIdentified = (msg, deps) => {
28801
29124
  try {
28802
29125
  await deps.reportAutoDetectedAgents?.();
28803
29126
  } catch (e) {
28804
- deps.log(`[Bridge service] auto-detect agents failed: ${e instanceof Error ? e.message : String(e)}`);
29127
+ deps.log(
29128
+ `[Bridge service] Auto-detect agents failed: ${e instanceof Error ? e.message : String(e)}`
29129
+ );
28805
29130
  }
28806
29131
  })();
28807
29132
  });
@@ -28809,7 +29134,9 @@ var handleBridgeIdentified = (msg, deps) => {
28809
29134
  try {
28810
29135
  deps.sendLocalSkillsReport?.();
28811
29136
  } catch (e) {
28812
- deps.log(`[Bridge service] local skills report failed: ${e instanceof Error ? e.message : String(e)}`);
29137
+ deps.log(
29138
+ `[Bridge service] Local skills report failed: ${e instanceof Error ? e.message : String(e)}`
29139
+ );
28813
29140
  }
28814
29141
  });
28815
29142
  };
@@ -28826,12 +29153,12 @@ var handleAgentConfigMessage = (msg, deps) => {
28826
29153
  };
28827
29154
 
28828
29155
  // src/acp/from-bridge/handle-bridge-prompt.ts
28829
- import * as path10 from "node:path";
29156
+ import * as path11 from "node:path";
28830
29157
  import { execFile as execFile3 } from "node:child_process";
28831
29158
  import { promisify as promisify3 } from "node:util";
28832
29159
 
28833
29160
  // src/git/bridge-queue-key.ts
28834
- import * as path8 from "node:path";
29161
+ import * as path10 from "node:path";
28835
29162
  import { createHash } from "node:crypto";
28836
29163
  function normalizeCanonicalGitUrl(url2) {
28837
29164
  let s = url2.trim();
@@ -28859,13 +29186,13 @@ function canonicalUrlToRepoIdSync(url2) {
28859
29186
  return createHash("sha256").update(normalized).digest("hex").slice(0, 32);
28860
29187
  }
28861
29188
  function fallbackRepoIdFromPath(absPath) {
28862
- return createHash("sha256").update(path8.resolve(absPath)).digest("hex").slice(0, 32);
29189
+ return createHash("sha256").update(path10.resolve(absPath)).digest("hex").slice(0, 32);
28863
29190
  }
28864
29191
  async function resolveBridgeQueueBindFields(options) {
28865
29192
  const { effectiveCwd, worktreePaths, primaryRepoRoots, log: log2 } = options;
28866
- const cwdAbs = worktreePaths.length > 0 ? path8.resolve(worktreePaths[0]) : path8.resolve(effectiveCwd);
29193
+ const cwdAbs = worktreePaths.length > 0 ? path10.resolve(worktreePaths[0]) : path10.resolve(effectiveCwd);
28867
29194
  if (!primaryRepoRoots.length) {
28868
- log2("[Bridge service] prompt queue bind skipped: no git repo roots under cwd");
29195
+ log2("[Bridge service] Prompt queue bind skipped: no Git repository roots under the working directory.");
28869
29196
  return null;
28870
29197
  }
28871
29198
  let primaryRoot = primaryRepoRoots[0];
@@ -28885,138 +29212,32 @@ async function resolveBridgeQueueBindFields(options) {
28885
29212
  return { canonicalQueueKey, repoId, cwdAbs };
28886
29213
  }
28887
29214
 
28888
- // src/git/pre-turn-snapshot.ts
28889
- import * as fs4 from "node:fs";
28890
- import * as path9 from "node:path";
28891
- import { execFile as execFile2 } from "node:child_process";
28892
- import { promisify as promisify2 } from "node:util";
28893
- var execFileAsync2 = promisify2(execFile2);
28894
- function snapshotsDirForCwd(agentCwd) {
28895
- return path9.join(agentCwd, ".buildautomaton", "snapshots");
28896
- }
28897
- async function gitStashCreate2(repoRoot, log2) {
28898
- try {
28899
- const { stdout } = await execFileAsync2("git", ["stash", "create"], {
28900
- cwd: repoRoot,
28901
- maxBuffer: 10 * 1024 * 1024
28902
- });
28903
- return stdout.trim();
28904
- } catch (e) {
28905
- log2(`[snapshot] git stash create failed in ${repoRoot}: ${e instanceof Error ? e.message : String(e)}`);
28906
- return "";
28907
- }
28908
- }
28909
- async function gitRun(repoRoot, args, log2, label) {
28910
- try {
28911
- await execFileAsync2("git", args, { cwd: repoRoot, maxBuffer: 10 * 1024 * 1024 });
28912
- return { ok: true };
28913
- } catch (e) {
28914
- const msg = e instanceof Error ? e.message : String(e);
28915
- log2(`[snapshot] git ${label} failed in ${repoRoot}: ${msg}`);
28916
- return { ok: false, error: msg };
28917
- }
28918
- }
28919
- async function resolveSnapshotRepoRoots(options) {
28920
- const { worktreePaths, fallbackCwd, log: log2 } = options;
28921
- if (worktreePaths?.length) {
28922
- const uniq = [...new Set(worktreePaths.map((p) => path9.resolve(p)))];
28923
- return uniq;
28924
- }
29215
+ // src/acp/from-bridge/handle-bridge-prompt.ts
29216
+ var execFileAsync3 = promisify3(execFile3);
29217
+ async function readGitBranch(cwd) {
28925
29218
  try {
28926
- const repos = await discoverGitReposUnderRoot(fallbackCwd);
28927
- return repos.map((r) => r.absolutePath);
28928
- } catch (e) {
28929
- log2(`[snapshot] discover repos failed: ${e instanceof Error ? e.message : String(e)}`);
28930
- return [];
29219
+ const { stdout } = await execFileAsync3("git", ["branch", "--show-current"], { cwd, maxBuffer: 64 * 1024 });
29220
+ const b = stdout.trim();
29221
+ return b || null;
29222
+ } catch {
29223
+ return null;
28931
29224
  }
28932
29225
  }
28933
- async function capturePreTurnSnapshot(options) {
28934
- const { runId, repoRoots, agentCwd, log: log2 } = options;
28935
- if (!runId || !repoRoots.length) {
28936
- return { ok: false, error: "No git repos to snapshot" };
28937
- }
28938
- const repos = [];
28939
- for (const root of repoRoots) {
28940
- const stashSha = await gitStashCreate2(root, log2);
28941
- repos.push({ path: root, stashSha });
28942
- }
28943
- const dir = snapshotsDirForCwd(agentCwd);
28944
- try {
28945
- fs4.mkdirSync(dir, { recursive: true });
28946
- } catch (e) {
28947
- return { ok: false, error: e instanceof Error ? e.message : String(e) };
28948
- }
28949
- const payload = {
28950
- runId,
28951
- createdAt: (/* @__PURE__ */ new Date()).toISOString(),
28952
- repos
28953
- };
28954
- const filePath = path9.join(dir, `${runId}.json`);
28955
- try {
28956
- fs4.writeFileSync(filePath, JSON.stringify(payload, null, 2), "utf8");
28957
- } catch (e) {
28958
- return { ok: false, error: e instanceof Error ? e.message : String(e) };
28959
- }
28960
- log2(`[snapshot] Saved pre-turn snapshot ${runId.slice(0, 8)}\u2026 (${repos.length} repo(s)) \u2192 ${filePath}`);
28961
- return { ok: true, filePath, repos };
28962
- }
28963
- async function applyPreTurnSnapshot(filePath, log2) {
28964
- let data;
28965
- try {
28966
- const raw = fs4.readFileSync(filePath, "utf8");
28967
- data = JSON.parse(raw);
28968
- } catch (e) {
28969
- return { ok: false, error: e instanceof Error ? e.message : String(e) };
28970
- }
28971
- if (!Array.isArray(data.repos)) {
28972
- return { ok: false, error: "Invalid snapshot file" };
28973
- }
28974
- for (const r of data.repos) {
28975
- if (!r.path) continue;
28976
- const reset = await gitRun(r.path, ["reset", "--hard", "HEAD"], log2, "reset --hard");
28977
- if (!reset.ok) return reset;
28978
- const clean = await gitRun(r.path, ["clean", "-fd"], log2, "clean -fd");
28979
- if (!clean.ok) return clean;
28980
- if (r.stashSha) {
28981
- const ap = await gitRun(r.path, ["stash", "apply", r.stashSha], log2, "stash apply");
28982
- if (!ap.ok) return ap;
28983
- }
28984
- }
28985
- log2(`[snapshot] Restored pre-turn state for ${data.runId.slice(0, 8)}\u2026`);
28986
- return { ok: true };
28987
- }
28988
- function snapshotFilePath(agentCwd, runId) {
28989
- return path9.join(snapshotsDirForCwd(agentCwd), `${runId}.json`);
28990
- }
28991
-
28992
- // src/acp/from-bridge/handle-bridge-prompt.ts
28993
- var execFileAsync3 = promisify3(execFile3);
28994
- async function readGitBranch(cwd3) {
28995
- try {
28996
- const { stdout } = await execFileAsync3("git", ["branch", "--show-current"], { cwd: cwd3, maxBuffer: 64 * 1024 });
28997
- const b = stdout.trim();
28998
- return b || null;
28999
- } catch {
29000
- return null;
29001
- }
29002
- }
29003
- function handleBridgePrompt(msg, deps) {
29004
- const { getWs, log: log2, acpManager, sessionWorktreeManager } = deps;
29005
- const rawPrompt = msg.prompt;
29006
- const promptText = typeof rawPrompt === "string" ? rawPrompt : rawPrompt != null ? String(rawPrompt) : "";
29007
- if (!promptText.trim()) {
29008
- log2(
29009
- `[Bridge service] prompt ignored: empty or missing prompt field (sessionId=${typeof msg.sessionId === "string" ? msg.sessionId.slice(0, 8) : "\u2014"}\u2026 runId=${typeof msg.runId === "string" ? msg.runId.slice(0, 8) : "\u2014"}\u2026)`
29010
- );
29011
- return;
29226
+ function handleBridgePrompt(msg, deps) {
29227
+ const { getWs, log: log2, acpManager, sessionWorktreeManager } = deps;
29228
+ const rawPrompt = msg.prompt;
29229
+ const promptText = typeof rawPrompt === "string" ? rawPrompt : rawPrompt != null ? String(rawPrompt) : "";
29230
+ if (!promptText.trim()) {
29231
+ log2(
29232
+ `[Bridge service] Prompt ignored: empty or missing prompt text (session ${typeof msg.sessionId === "string" ? msg.sessionId.slice(0, 8) : "\u2014"}\u2026, run ${typeof msg.runId === "string" ? msg.runId.slice(0, 8) : "\u2014"}\u2026).`
29233
+ );
29234
+ return;
29012
29235
  }
29013
29236
  const sessionId = msg.sessionId;
29014
29237
  const isNewSession = msg.isNewSession === true;
29015
29238
  const sessionWorktreesEnabled = msg.sessionWorktreesEnabled === true;
29016
29239
  const agentType = typeof msg.agentType === "string" && msg.agentType.trim() ? msg.agentType.trim() : void 0;
29017
29240
  const runId = typeof msg.runId === "string" ? msg.runId : void 0;
29018
- const sessionGitQueueStart = msg.sessionGitQueueStart === true;
29019
- const collectSessionDiffAfterTurn = msg.collectSessionDiffAfterTurn === true;
29020
29241
  const sendResult = (result) => {
29021
29242
  const s = getWs();
29022
29243
  if (s) sendWsMessage(s, result);
@@ -29024,7 +29245,7 @@ function handleBridgePrompt(msg, deps) {
29024
29245
  const sendSessionUpdate = (payload) => {
29025
29246
  const s = getWs();
29026
29247
  if (!s) {
29027
- log2("[Bridge service] session_update not sent: not connected");
29248
+ log2("[Bridge service] Session update not sent: not connected to the bridge.");
29028
29249
  return;
29029
29250
  }
29030
29251
  const p = payload;
@@ -29032,7 +29253,7 @@ function handleBridgePrompt(msg, deps) {
29032
29253
  };
29033
29254
  async function preambleAndPrompt(resolvedCwd) {
29034
29255
  const s = getWs();
29035
- const effectiveCwd = path10.resolve(resolvedCwd ?? process.cwd());
29256
+ const effectiveCwd = path11.resolve(resolvedCwd ?? getBridgeWorkspaceDirectory());
29036
29257
  const worktreePaths = sessionWorktreeManager.getWorktreePathsForSession(sessionId) ?? [];
29037
29258
  const repoRoots = await resolveSnapshotRepoRoots({
29038
29259
  worktreePaths,
@@ -29065,9 +29286,6 @@ function handleBridgePrompt(msg, deps) {
29065
29286
  agentUsesWorktree: sessionWorktreeManager.usesWorktreeSession(sessionId)
29066
29287
  });
29067
29288
  }
29068
- if (sessionGitQueueStart && sessionId && repoRoots.length > 0) {
29069
- await captureSessionGitBoundary({ sessionId, repoRoots, log: log2 });
29070
- }
29071
29289
  if (s && sessionId && runId) {
29072
29290
  const cap = repoRoots.length > 0 ? await capturePreTurnSnapshot({ runId, repoRoots, agentCwd: effectiveCwd, log: log2 }) : { ok: false, error: "No git repos" };
29073
29291
  sendWsMessage(s, {
@@ -29086,12 +29304,11 @@ function handleBridgePrompt(msg, deps) {
29086
29304
  agentType,
29087
29305
  cwd: effectiveCwd,
29088
29306
  sendResult,
29089
- sendSessionUpdate,
29090
- collectSessionDiffAfterTurn
29307
+ sendSessionUpdate
29091
29308
  });
29092
29309
  }
29093
- void sessionWorktreeManager.resolveCwdForPrompt(sessionId, { isNewSession, sessionWorktreesEnabled }).then((cwd3) => preambleAndPrompt(cwd3)).catch((err) => {
29094
- log2(`[agent] worktree resolve failed: ${err instanceof Error ? err.message : String(err)}`);
29310
+ void sessionWorktreeManager.resolveCwdForPrompt(sessionId, { isNewSession, sessionWorktreesEnabled }).then((cwd) => preambleAndPrompt(cwd)).catch((err) => {
29311
+ log2(`[Agent] Worktree resolve failed: ${err instanceof Error ? err.message : String(err)}`);
29095
29312
  void preambleAndPrompt(void 0);
29096
29313
  });
29097
29314
  }
@@ -29108,7 +29325,7 @@ function handleBridgeCancelRun(msg, { log: log2, acpManager }) {
29108
29325
  void acpManager.cancelRun(runId).then((sent) => {
29109
29326
  if (!sent) {
29110
29327
  log2(
29111
- `[agent] cancel ignored for run ${runId.slice(0, 8)}\u2026 (no active run or cancel not available)`
29328
+ `[Agent] Cancel ignored for run ${runId.slice(0, 8)}\u2026 (no active run or cancel not available).`
29112
29329
  );
29113
29330
  }
29114
29331
  });
@@ -29136,7 +29353,7 @@ function handleSkillCall(msg, socket, log2) {
29136
29353
  sendWsMessage(socket, { type: "skill_result", id: msg.id, result });
29137
29354
  }).catch((err) => {
29138
29355
  sendWsMessage(socket, { type: "skill_result", id: msg.id, error: String(err) });
29139
- log2(`[Bridge service] skill_call failed (${msg.skillId}/${msg.operationId}): ${err}`);
29356
+ log2(`[Bridge service] Skill invocation failed (${msg.skillId}/${msg.operationId}): ${err}`);
29140
29357
  });
29141
29358
  }
29142
29359
 
@@ -29153,36 +29370,35 @@ var handleSkillCallMessage = (msg, { getWs, log: log2 }) => {
29153
29370
  };
29154
29371
 
29155
29372
  // src/files/list-dir.ts
29156
- import fs5 from "node:fs";
29157
- import path12 from "node:path";
29373
+ import fs6 from "node:fs";
29374
+ import path13 from "node:path";
29158
29375
 
29159
29376
  // src/files/ensure-under-cwd.ts
29160
- import path11 from "node:path";
29161
- function ensureUnderCwd(relativePath, cwd3 = process.cwd()) {
29162
- const normalized = path11.normalize(relativePath).replace(/^(\.\/)+/, "");
29163
- const resolved = path11.resolve(cwd3, normalized);
29164
- if (!resolved.startsWith(cwd3 + path11.sep) && resolved !== cwd3) {
29377
+ import path12 from "node:path";
29378
+ function ensureUnderCwd(relativePath, cwd = getBridgeWorkspaceDirectory()) {
29379
+ const normalized = path12.normalize(relativePath).replace(/^(\.\/)+/, "");
29380
+ const resolved = path12.resolve(cwd, normalized);
29381
+ if (!resolved.startsWith(cwd + path12.sep) && resolved !== cwd) {
29165
29382
  return null;
29166
29383
  }
29167
29384
  return resolved;
29168
29385
  }
29169
29386
 
29170
29387
  // src/files/list-dir.ts
29171
- var cwd = process.cwd();
29172
29388
  function listDir(relativePath) {
29173
- const resolved = ensureUnderCwd(relativePath || ".", cwd);
29389
+ const resolved = ensureUnderCwd(relativePath || ".", getBridgeWorkspaceDirectory());
29174
29390
  if (!resolved) {
29175
29391
  return { error: "Path is outside working directory" };
29176
29392
  }
29177
29393
  try {
29178
- const names = fs5.readdirSync(resolved, { withFileTypes: true });
29394
+ const names = fs6.readdirSync(resolved, { withFileTypes: true });
29179
29395
  const entries = names.filter((d) => !d.name.startsWith(".")).map((d) => {
29180
- const entryPath = path12.join(relativePath || ".", d.name).replace(/\\/g, "/");
29181
- const fullPath = path12.join(resolved, d.name);
29396
+ const entryPath = path13.join(relativePath || ".", d.name).replace(/\\/g, "/");
29397
+ const fullPath = path13.join(resolved, d.name);
29182
29398
  let isDir = d.isDirectory();
29183
29399
  if (d.isSymbolicLink()) {
29184
29400
  try {
29185
- const targetStat = fs5.statSync(fullPath);
29401
+ const targetStat = fs6.statSync(fullPath);
29186
29402
  isDir = targetStat.isDirectory();
29187
29403
  } catch {
29188
29404
  isDir = false;
@@ -29206,26 +29422,25 @@ function listDir(relativePath) {
29206
29422
  }
29207
29423
 
29208
29424
  // src/files/read-file.ts
29209
- import fs6 from "node:fs";
29425
+ import fs7 from "node:fs";
29210
29426
  import { StringDecoder } from "node:string_decoder";
29211
- var cwd2 = process.cwd();
29212
29427
  function resolveFilePath(relativePath) {
29213
- const resolved = ensureUnderCwd(relativePath, cwd2);
29428
+ const resolved = ensureUnderCwd(relativePath, getBridgeWorkspaceDirectory());
29214
29429
  if (!resolved) return { error: "Path is outside working directory" };
29215
29430
  let real;
29216
29431
  try {
29217
- real = fs6.realpathSync(resolved);
29432
+ real = fs7.realpathSync(resolved);
29218
29433
  } catch {
29219
29434
  real = resolved;
29220
29435
  }
29221
- const stat4 = fs6.statSync(real);
29222
- if (!stat4.isFile()) return { error: "Not a file" };
29436
+ const stat = fs7.statSync(real);
29437
+ if (!stat.isFile()) return { error: "Not a file" };
29223
29438
  return real;
29224
29439
  }
29225
29440
  var LINE_CHUNK_SIZE = 64 * 1024;
29226
29441
  function readFileRange(filePath, startLine, endLine, lineOffsetIn, lineChunkSize = LINE_CHUNK_SIZE) {
29227
- const fileSize = fs6.statSync(filePath).size;
29228
- const fd = fs6.openSync(filePath, "r");
29442
+ const fileSize = fs7.statSync(filePath).size;
29443
+ const fd = fs7.openSync(filePath, "r");
29229
29444
  const bufSize = 64 * 1024;
29230
29445
  const buf = Buffer.alloc(bufSize);
29231
29446
  const decoder = new StringDecoder("utf8");
@@ -29238,7 +29453,7 @@ function readFileRange(filePath, startLine, endLine, lineOffsetIn, lineChunkSize
29238
29453
  let line0Accum = "";
29239
29454
  try {
29240
29455
  let bytesRead;
29241
- while (!done && (bytesRead = fs6.readSync(fd, buf, 0, bufSize, null)) > 0) {
29456
+ while (!done && (bytesRead = fs7.readSync(fd, buf, 0, bufSize, null)) > 0) {
29242
29457
  const text = partial2 + decoder.write(buf.subarray(0, bytesRead));
29243
29458
  partial2 = "";
29244
29459
  let lineStart = 0;
@@ -29373,7 +29588,7 @@ function readFileRange(filePath, startLine, endLine, lineOffsetIn, lineChunkSize
29373
29588
  }
29374
29589
  return { content: resultLines.join("\n"), size: fileSize };
29375
29590
  } finally {
29376
- fs6.closeSync(fd);
29591
+ fs7.closeSync(fd);
29377
29592
  }
29378
29593
  }
29379
29594
  function readFile(relativePath, startLine, endLine, lineOffset, lineChunkSize = LINE_CHUNK_SIZE) {
@@ -29384,26 +29599,26 @@ function readFile(relativePath, startLine, endLine, lineOffset, lineChunkSize =
29384
29599
  if (hasRange) {
29385
29600
  return readFileRange(result, startLine, endLine, lineOffset, lineChunkSize);
29386
29601
  }
29387
- const stat4 = fs6.statSync(result);
29388
- const raw = fs6.readFileSync(result, "utf8");
29602
+ const stat = fs7.statSync(result);
29603
+ const raw = fs7.readFileSync(result, "utf8");
29389
29604
  const lines = raw.split(/\r?\n/);
29390
- return { content: raw, totalLines: lines.length, size: stat4.size };
29605
+ return { content: raw, totalLines: lines.length, size: stat.size };
29391
29606
  } catch (err) {
29392
29607
  return { error: err instanceof Error ? err.message : String(err) };
29393
29608
  }
29394
29609
  }
29395
29610
 
29396
29611
  // src/files/file-index.ts
29397
- import fs7 from "node:fs";
29398
- import path13 from "node:path";
29612
+ import fs8 from "node:fs";
29613
+ import path14 from "node:path";
29399
29614
  import os2 from "node:os";
29400
29615
  import crypto2 from "node:crypto";
29401
- var INDEX_DIR = path13.join(os2.homedir(), ".buildautomaton");
29616
+ var INDEX_DIR = path14.join(os2.homedir(), ".buildautomaton");
29402
29617
  var HASH_LEN = 16;
29403
29618
  var INDEX_VERSION = 2;
29404
- function getIndexPath(cwd3) {
29405
- const hash = crypto2.createHash("sha256").update(cwd3).digest("hex").slice(0, HASH_LEN);
29406
- return path13.join(INDEX_DIR, `.file-index-${hash}.json`);
29619
+ function getIndexPath(cwd) {
29620
+ const hash = crypto2.createHash("sha256").update(cwd).digest("hex").slice(0, HASH_LEN);
29621
+ return path14.join(INDEX_DIR, `.file-index-${hash}.json`);
29407
29622
  }
29408
29623
  function getTrigrams(s) {
29409
29624
  const lower = s.toLowerCase();
@@ -29441,29 +29656,29 @@ function binarySearch(arr, x) {
29441
29656
  function walkDir(dir, baseDir, out) {
29442
29657
  let names;
29443
29658
  try {
29444
- names = fs7.readdirSync(dir);
29659
+ names = fs8.readdirSync(dir);
29445
29660
  } catch {
29446
29661
  return;
29447
29662
  }
29448
29663
  for (const name of names) {
29449
29664
  if (name.startsWith(".")) continue;
29450
- const full = path13.join(dir, name);
29451
- let stat4;
29665
+ const full = path14.join(dir, name);
29666
+ let stat;
29452
29667
  try {
29453
- stat4 = fs7.statSync(full);
29668
+ stat = fs8.statSync(full);
29454
29669
  } catch {
29455
29670
  continue;
29456
29671
  }
29457
- const relative6 = path13.relative(baseDir, full).replace(/\\/g, "/");
29458
- if (stat4.isDirectory()) {
29672
+ const relative4 = path14.relative(baseDir, full).replace(/\\/g, "/");
29673
+ if (stat.isDirectory()) {
29459
29674
  walkDir(full, baseDir, out);
29460
- } else if (stat4.isFile()) {
29461
- out.push(relative6);
29675
+ } else if (stat.isFile()) {
29676
+ out.push(relative4);
29462
29677
  }
29463
29678
  }
29464
29679
  }
29465
- function buildFileIndex(cwd3) {
29466
- const resolved = path13.resolve(cwd3);
29680
+ function buildFileIndex(cwd) {
29681
+ const resolved = path14.resolve(cwd);
29467
29682
  const paths = [];
29468
29683
  walkDir(resolved, resolved, paths);
29469
29684
  paths.sort((a, b) => a.localeCompare(b, void 0, { sensitivity: "base" }));
@@ -29481,18 +29696,18 @@ function buildFileIndex(cwd3) {
29481
29696
  const data = { version: INDEX_VERSION, paths, trigramIndex };
29482
29697
  const indexPath = getIndexPath(resolved);
29483
29698
  try {
29484
- if (!fs7.existsSync(INDEX_DIR)) fs7.mkdirSync(INDEX_DIR, { recursive: true });
29485
- fs7.writeFileSync(indexPath, JSON.stringify(data), "utf8");
29699
+ if (!fs8.existsSync(INDEX_DIR)) fs8.mkdirSync(INDEX_DIR, { recursive: true });
29700
+ fs8.writeFileSync(indexPath, JSON.stringify(data), "utf8");
29486
29701
  } catch (e) {
29487
29702
  console.error("[file-index] Failed to write index:", e);
29488
29703
  }
29489
29704
  return data;
29490
29705
  }
29491
- function loadFileIndex(cwd3) {
29492
- const resolved = path13.resolve(cwd3);
29706
+ function loadFileIndex(cwd) {
29707
+ const resolved = path14.resolve(cwd);
29493
29708
  const indexPath = getIndexPath(resolved);
29494
29709
  try {
29495
- const raw = fs7.readFileSync(indexPath, "utf8");
29710
+ const raw = fs8.readFileSync(indexPath, "utf8");
29496
29711
  const parsed = JSON.parse(raw);
29497
29712
  if (parsed !== null && typeof parsed === "object" && Array.isArray(parsed.paths)) {
29498
29713
  const obj = parsed;
@@ -29509,8 +29724,8 @@ function loadFileIndex(cwd3) {
29509
29724
  return null;
29510
29725
  }
29511
29726
  }
29512
- function ensureFileIndex(cwd3) {
29513
- const resolved = path13.resolve(cwd3);
29727
+ function ensureFileIndex(cwd) {
29728
+ const resolved = path14.resolve(cwd);
29514
29729
  const cached2 = loadFileIndex(resolved);
29515
29730
  if (cached2 !== null) return { data: cached2, fromCache: true };
29516
29731
  const data = buildFileIndex(resolved);
@@ -29548,8 +29763,8 @@ function searchFileIndex(index, query, limit = 100) {
29548
29763
  var SEARCH_LIMIT = 100;
29549
29764
  function handleFileBrowserSearch(msg, socket) {
29550
29765
  const q = typeof msg.q === "string" ? msg.q : "";
29551
- const cwd3 = process.cwd();
29552
- const index = loadFileIndex(cwd3);
29766
+ const cwd = getBridgeWorkspaceDirectory();
29767
+ const index = loadFileIndex(cwd);
29553
29768
  if (index === null) {
29554
29769
  sendWsMessage(socket, {
29555
29770
  type: "file_browser_search_response",
@@ -29570,7 +29785,7 @@ function handleFileBrowserSearch(msg, socket) {
29570
29785
  function triggerFileIndexBuild() {
29571
29786
  setImmediate(() => {
29572
29787
  try {
29573
- ensureFileIndex(process.cwd());
29788
+ ensureFileIndex(getBridgeWorkspaceDirectory());
29574
29789
  } catch (e) {
29575
29790
  console.error("[file-index] Background build failed:", e);
29576
29791
  }
@@ -29631,30 +29846,30 @@ function handleFileBrowserSearchMessage(msg, { getWs }) {
29631
29846
  }
29632
29847
 
29633
29848
  // src/skills/discover-local-agent-skills.ts
29634
- import fs8 from "node:fs";
29635
- import path14 from "node:path";
29849
+ import fs9 from "node:fs";
29850
+ import path15 from "node:path";
29636
29851
  var SKILL_DISCOVERY_ROOTS = [".agents/skills", ".claude/skills", ".cursor/skills", "skills"];
29637
- function discoverLocalSkills(cwd3) {
29852
+ function discoverLocalSkills(cwd) {
29638
29853
  const out = [];
29639
29854
  const seenKeys = /* @__PURE__ */ new Set();
29640
29855
  for (const rel of SKILL_DISCOVERY_ROOTS) {
29641
- const base = path14.join(cwd3, rel);
29642
- if (!fs8.existsSync(base) || !fs8.statSync(base).isDirectory()) continue;
29856
+ const base = path15.join(cwd, rel);
29857
+ if (!fs9.existsSync(base) || !fs9.statSync(base).isDirectory()) continue;
29643
29858
  let entries = [];
29644
29859
  try {
29645
- entries = fs8.readdirSync(base);
29860
+ entries = fs9.readdirSync(base);
29646
29861
  } catch {
29647
29862
  continue;
29648
29863
  }
29649
29864
  for (const name of entries) {
29650
- const dir = path14.join(base, name);
29865
+ const dir = path15.join(base, name);
29651
29866
  try {
29652
- if (!fs8.statSync(dir).isDirectory()) continue;
29867
+ if (!fs9.statSync(dir).isDirectory()) continue;
29653
29868
  } catch {
29654
29869
  continue;
29655
29870
  }
29656
- const skillMd = path14.join(dir, "SKILL.md");
29657
- if (!fs8.existsSync(skillMd)) continue;
29871
+ const skillMd = path15.join(dir, "SKILL.md");
29872
+ if (!fs9.existsSync(skillMd)) continue;
29658
29873
  const key = `${rel}/${name}`;
29659
29874
  if (seenKeys.has(key)) continue;
29660
29875
  seenKeys.add(key);
@@ -29663,26 +29878,26 @@ function discoverLocalSkills(cwd3) {
29663
29878
  }
29664
29879
  return out;
29665
29880
  }
29666
- function discoverSkillLayoutRoots(cwd3) {
29881
+ function discoverSkillLayoutRoots(cwd) {
29667
29882
  const roots = [];
29668
29883
  for (const rel of SKILL_DISCOVERY_ROOTS) {
29669
- const base = path14.join(cwd3, rel);
29670
- if (!fs8.existsSync(base) || !fs8.statSync(base).isDirectory()) continue;
29884
+ const base = path15.join(cwd, rel);
29885
+ if (!fs9.existsSync(base) || !fs9.statSync(base).isDirectory()) continue;
29671
29886
  let entries = [];
29672
29887
  try {
29673
- entries = fs8.readdirSync(base);
29888
+ entries = fs9.readdirSync(base);
29674
29889
  } catch {
29675
29890
  continue;
29676
29891
  }
29677
29892
  const skills2 = [];
29678
29893
  for (const name of entries) {
29679
- const dir = path14.join(base, name);
29894
+ const dir = path15.join(base, name);
29680
29895
  try {
29681
- if (!fs8.statSync(dir).isDirectory()) continue;
29896
+ if (!fs9.statSync(dir).isDirectory()) continue;
29682
29897
  } catch {
29683
29898
  continue;
29684
29899
  }
29685
- if (!fs8.existsSync(path14.join(dir, "SKILL.md"))) continue;
29900
+ if (!fs9.existsSync(path15.join(dir, "SKILL.md"))) continue;
29686
29901
  const relPath = `${rel}/${name}`.replace(/\\/g, "/");
29687
29902
  skills2.push({ name, relPath });
29688
29903
  }
@@ -29697,14 +29912,14 @@ function discoverSkillLayoutRoots(cwd3) {
29697
29912
  function handleSkillLayoutRequest(msg, deps) {
29698
29913
  const socket = deps.getWs();
29699
29914
  const id = typeof msg.id === "string" ? msg.id : "";
29700
- const roots = discoverSkillLayoutRoots(process.cwd());
29915
+ const roots = discoverSkillLayoutRoots(getBridgeWorkspaceDirectory());
29701
29916
  socket?.send(JSON.stringify({ type: "skill_layout_response", id, roots }));
29702
29917
  }
29703
29918
 
29704
29919
  // src/skills/install-remote-skills.ts
29705
- import fs9 from "node:fs";
29706
- import path15 from "node:path";
29707
- function installRemoteSkills(cwd3, targetDir, items) {
29920
+ import fs10 from "node:fs";
29921
+ import path16 from "node:path";
29922
+ function installRemoteSkills(cwd, targetDir, items) {
29708
29923
  const installed = [];
29709
29924
  if (!Array.isArray(items)) {
29710
29925
  return { success: false, error: "Invalid items" };
@@ -29714,15 +29929,15 @@ function installRemoteSkills(cwd3, targetDir, items) {
29714
29929
  if (typeof item.sourceId !== "string" || typeof item.skillName !== "string" || typeof item.versionHash !== "string" || !Array.isArray(item.files)) {
29715
29930
  continue;
29716
29931
  }
29717
- const skillDir = path15.join(cwd3, targetDir, item.skillName);
29932
+ const skillDir = path16.join(cwd, targetDir, item.skillName);
29718
29933
  for (const f of item.files) {
29719
29934
  if (typeof f.path !== "string" || !f.text && !f.base64) continue;
29720
- const dest = path15.join(skillDir, f.path);
29721
- fs9.mkdirSync(path15.dirname(dest), { recursive: true });
29935
+ const dest = path16.join(skillDir, f.path);
29936
+ fs10.mkdirSync(path16.dirname(dest), { recursive: true });
29722
29937
  if (f.text !== void 0) {
29723
- fs9.writeFileSync(dest, f.text, "utf8");
29938
+ fs10.writeFileSync(dest, f.text, "utf8");
29724
29939
  } else if (f.base64) {
29725
- fs9.writeFileSync(dest, Buffer.from(f.base64, "base64"));
29940
+ fs10.writeFileSync(dest, Buffer.from(f.base64, "base64"));
29726
29941
  }
29727
29942
  }
29728
29943
  installed.push({
@@ -29743,11 +29958,11 @@ var handleInstallSkillsMessage = (msg, deps) => {
29743
29958
  const id = typeof msg.id === "string" ? msg.id : "";
29744
29959
  const targetDir = typeof msg.targetDir === "string" && msg.targetDir.trim() ? msg.targetDir.trim() : ".agents/skills";
29745
29960
  const rawItems = msg.items;
29746
- const cwd3 = process.cwd();
29747
- const result = installRemoteSkills(cwd3, targetDir, rawItems);
29961
+ const cwd = getBridgeWorkspaceDirectory();
29962
+ const result = installRemoteSkills(cwd, targetDir, rawItems);
29748
29963
  if (!result.success) {
29749
29964
  const err = result.error ?? "Invalid items";
29750
- deps.log(`[Bridge service] install_skills failed: ${err}`);
29965
+ deps.log(`[Bridge service] Install skills failed: ${err}`);
29751
29966
  socket?.send(JSON.stringify({ type: "install_skills_result", id, success: false, error: err }));
29752
29967
  return;
29753
29968
  }
@@ -29816,8 +30031,7 @@ var handleSessionDiscardedMessage = (msg, deps) => {
29816
30031
  };
29817
30032
 
29818
30033
  // src/bridge/routing/handlers/revert-turn-snapshot.ts
29819
- import * as fs10 from "node:fs";
29820
- import * as path16 from "node:path";
30034
+ import * as fs11 from "node:fs";
29821
30035
  var handleRevertTurnSnapshotMessage = (msg, deps) => {
29822
30036
  const id = typeof msg.id === "string" ? msg.id : "";
29823
30037
  const sessionId = typeof msg.sessionId === "string" ? msg.sessionId : "";
@@ -29827,9 +30041,9 @@ var handleRevertTurnSnapshotMessage = (msg, deps) => {
29827
30041
  void (async () => {
29828
30042
  const s = getWs();
29829
30043
  if (!s) return;
29830
- const agentBase = sessionWorktreeManager.getAgentCwdForSession(sessionId) ?? path16.resolve(process.cwd());
30044
+ const agentBase = sessionWorktreeManager.getAgentCwdForSession(sessionId) ?? getBridgeWorkspaceDirectory();
29831
30045
  const file2 = snapshotFilePath(agentBase, turnId);
29832
- if (!fs10.existsSync(file2)) {
30046
+ if (!fs11.existsSync(file2)) {
29833
30047
  sendWsMessage(s, {
29834
30048
  type: "revert_turn_snapshot_result",
29835
30049
  id,
@@ -29956,7 +30170,7 @@ import * as path20 from "node:path";
29956
30170
  import os4 from "node:os";
29957
30171
 
29958
30172
  // src/worktrees/prepare-new-session-worktrees.ts
29959
- import * as fs12 from "node:fs";
30173
+ import * as fs13 from "node:fs";
29960
30174
  import * as path18 from "node:path";
29961
30175
 
29962
30176
  // src/git/worktree-add.ts
@@ -29966,7 +30180,7 @@ async function gitWorktreeAddBranch(mainRepoPath, worktreePath, branch) {
29966
30180
  }
29967
30181
 
29968
30182
  // src/worktrees/worktree-layout-file.ts
29969
- import * as fs11 from "node:fs";
30183
+ import * as fs12 from "node:fs";
29970
30184
  import * as path17 from "node:path";
29971
30185
  import os3 from "node:os";
29972
30186
  var LAYOUT_FILENAME = "worktree-launcher-layout.json";
@@ -29983,8 +30197,8 @@ function normalizeLoadedLayout(raw) {
29983
30197
  function loadWorktreeLayout() {
29984
30198
  try {
29985
30199
  const p = defaultWorktreeLayoutPath();
29986
- if (!fs11.existsSync(p)) return { launcherCwds: [] };
29987
- const raw = JSON.parse(fs11.readFileSync(p, "utf8"));
30200
+ if (!fs12.existsSync(p)) return { launcherCwds: [] };
30201
+ const raw = JSON.parse(fs12.readFileSync(p, "utf8"));
29988
30202
  return normalizeLoadedLayout(raw);
29989
30203
  } catch {
29990
30204
  return { launcherCwds: [] };
@@ -29993,8 +30207,8 @@ function loadWorktreeLayout() {
29993
30207
  function saveWorktreeLayout(layout) {
29994
30208
  try {
29995
30209
  const dir = path17.dirname(defaultWorktreeLayoutPath());
29996
- fs11.mkdirSync(dir, { recursive: true });
29997
- fs11.writeFileSync(defaultWorktreeLayoutPath(), JSON.stringify(layout, null, 2), "utf8");
30210
+ fs12.mkdirSync(dir, { recursive: true });
30211
+ fs12.writeFileSync(defaultWorktreeLayoutPath(), JSON.stringify(layout, null, 2), "utf8");
29998
30212
  } catch {
29999
30213
  }
30000
30214
  }
@@ -30026,25 +30240,25 @@ async function prepareNewSessionWorktrees(options) {
30026
30240
  const agentMirrorRoot = path18.join(rootAbs, cwdKey);
30027
30241
  const repos = await discoverGitReposUnderRoot(launcherResolved);
30028
30242
  if (repos.length === 0) {
30029
- log2("[worktrees] No git repos under launcher cwd; skipping worktree creation");
30243
+ log2("[worktrees] No Git repositories under launcher working directory; skipping worktree creation.");
30030
30244
  return null;
30031
30245
  }
30032
30246
  const branch = `session-${sessionId}`;
30033
30247
  const worktreePaths = [];
30034
- fs12.mkdirSync(agentMirrorRoot, { recursive: true });
30248
+ fs13.mkdirSync(agentMirrorRoot, { recursive: true });
30035
30249
  for (const repo of repos) {
30036
30250
  let rel = path18.relative(launcherResolved, repo.absolutePath);
30037
30251
  if (rel.startsWith("..") || path18.isAbsolute(rel)) continue;
30038
30252
  const relNorm = rel === "" ? "." : rel;
30039
30253
  const wtPath = path18.join(agentMirrorRoot, relNorm, sessionId);
30040
- fs12.mkdirSync(path18.dirname(wtPath), { recursive: true });
30254
+ fs13.mkdirSync(path18.dirname(wtPath), { recursive: true });
30041
30255
  try {
30042
30256
  await gitWorktreeAddBranch(repo.absolutePath, wtPath, branch);
30043
- log2(`[worktrees] Added worktree ${wtPath} (branch ${branch})`);
30257
+ log2(`[worktrees] Added worktree ${wtPath} (branch ${branch}).`);
30044
30258
  worktreePaths.push(wtPath);
30045
30259
  } catch (e) {
30046
30260
  log2(
30047
- `[worktrees] worktree add failed for ${repo.absolutePath}: ${e instanceof Error ? e.message : String(e)}`
30261
+ `[worktrees] Worktree add failed for ${repo.absolutePath}: ${e instanceof Error ? e.message : String(e)}`
30048
30262
  );
30049
30263
  }
30050
30264
  }
@@ -30066,24 +30280,26 @@ async function renameSessionWorktreeBranches(paths, newBranch, log2) {
30066
30280
  await gitRenameCurrentBranch(wt, safe);
30067
30281
  log2(`[worktrees] Renamed branch in ${wt} \u2192 ${safe}`);
30068
30282
  } catch (e) {
30069
- log2(`[worktrees] branch rename failed in ${wt}: ${e instanceof Error ? e.message : String(e)}`);
30283
+ log2(
30284
+ `[worktrees] Branch rename failed in ${wt}: ${e instanceof Error ? e.message : String(e)}`
30285
+ );
30070
30286
  }
30071
30287
  }
30072
30288
  }
30073
30289
 
30074
30290
  // src/worktrees/remove-session-worktrees.ts
30075
- import * as fs15 from "node:fs";
30291
+ import * as fs16 from "node:fs";
30076
30292
 
30077
30293
  // src/git/worktree-remove.ts
30078
- import * as fs14 from "node:fs";
30294
+ import * as fs15 from "node:fs";
30079
30295
 
30080
30296
  // src/git/resolve-main-repo-from-git-file.ts
30081
- import * as fs13 from "node:fs";
30297
+ import * as fs14 from "node:fs";
30082
30298
  import * as path19 from "node:path";
30083
30299
  function resolveMainRepoFromWorktreeGitFile(wt) {
30084
30300
  const gitDirFile = path19.join(wt, ".git");
30085
- if (!fs13.existsSync(gitDirFile) || !fs13.statSync(gitDirFile).isFile()) return "";
30086
- const first2 = fs13.readFileSync(gitDirFile, "utf8").trim();
30301
+ if (!fs14.existsSync(gitDirFile) || !fs14.statSync(gitDirFile).isFile()) return "";
30302
+ const first2 = fs14.readFileSync(gitDirFile, "utf8").trim();
30087
30303
  const m = first2.match(/^gitdir:\s*(.+)$/im);
30088
30304
  if (!m) return "";
30089
30305
  const gitWorktreePath = path19.resolve(wt, m[1].trim());
@@ -30097,7 +30313,7 @@ async function gitWorktreeRemoveForce(worktreePath) {
30097
30313
  if (mainRepo) {
30098
30314
  await simpleGit(mainRepo).raw(["worktree", "remove", "--force", worktreePath]);
30099
30315
  } else {
30100
- fs14.rmSync(worktreePath, { recursive: true, force: true });
30316
+ fs15.rmSync(worktreePath, { recursive: true, force: true });
30101
30317
  }
30102
30318
  }
30103
30319
 
@@ -30108,9 +30324,9 @@ async function removeSessionWorktrees(paths, log2) {
30108
30324
  await gitWorktreeRemoveForce(wt);
30109
30325
  log2(`[worktrees] Removed worktree ${wt}`);
30110
30326
  } catch (e) {
30111
- log2(`[worktrees] remove failed for ${wt}: ${e instanceof Error ? e.message : String(e)}`);
30327
+ log2(`[worktrees] Remove failed for ${wt}: ${e instanceof Error ? e.message : String(e)}`);
30112
30328
  try {
30113
- fs15.rmSync(wt, { recursive: true, force: true });
30329
+ fs16.rmSync(wt, { recursive: true, force: true });
30114
30330
  } catch {
30115
30331
  }
30116
30332
  }
@@ -30174,7 +30390,7 @@ var SessionWorktreeManager = class {
30174
30390
  return this.bridgeWantsWorktrees;
30175
30391
  }
30176
30392
  /**
30177
- * Returns cwd for the agent (mirror of launcher tree), or undefined to use process.cwd().
30393
+ * Returns cwd for the agent (mirror of launcher tree), or undefined to use the bridge workspace directory.
30178
30394
  */
30179
30395
  async resolveCwdForPrompt(sessionId, opts) {
30180
30396
  if (!sessionId || !this.effective() || !opts.sessionWorktreesEnabled) {
@@ -30187,7 +30403,7 @@ var SessionWorktreeManager = class {
30187
30403
  }
30188
30404
  const prep = await prepareNewSessionWorktrees({
30189
30405
  rootAbs: this.rootAbs,
30190
- launcherCwd: process.cwd(),
30406
+ launcherCwd: getBridgeWorkspaceDirectory(),
30191
30407
  sessionId,
30192
30408
  layout: this.layout,
30193
30409
  log: this.log
@@ -30227,7 +30443,7 @@ var SessionWorktreeManager = class {
30227
30443
  }
30228
30444
  async commitSession(params) {
30229
30445
  const paths = this.sessionPaths.get(params.sessionId);
30230
- const targets = paths?.length ? paths : [process.cwd()];
30446
+ const targets = paths?.length ? paths : [getBridgeWorkspaceDirectory()];
30231
30447
  return commitSessionWorktrees({
30232
30448
  paths: targets,
30233
30449
  branch: params.branch,
@@ -30268,1700 +30484,8 @@ async function refreshBridgeTokens(params) {
30268
30484
  }
30269
30485
 
30270
30486
  // src/files/watch-file-index.ts
30487
+ import { watch } from "node:fs";
30271
30488
  import path21 from "node:path";
30272
-
30273
- // ../../node_modules/.pnpm/chokidar@4.0.3/node_modules/chokidar/esm/index.js
30274
- import { stat as statcb } from "fs";
30275
- import { stat as stat3, readdir as readdir2 } from "fs/promises";
30276
- import { EventEmitter as EventEmitter2 } from "events";
30277
- import * as sysPath2 from "path";
30278
-
30279
- // ../../node_modules/.pnpm/readdirp@4.1.2/node_modules/readdirp/esm/index.js
30280
- import { stat, lstat, readdir, realpath } from "node:fs/promises";
30281
- import { Readable as Readable2 } from "node:stream";
30282
- import { resolve as presolve, relative as prelative, join as pjoin, sep as psep } from "node:path";
30283
- var EntryTypes = {
30284
- FILE_TYPE: "files",
30285
- DIR_TYPE: "directories",
30286
- FILE_DIR_TYPE: "files_directories",
30287
- EVERYTHING_TYPE: "all"
30288
- };
30289
- var defaultOptions2 = {
30290
- root: ".",
30291
- fileFilter: (_entryInfo) => true,
30292
- directoryFilter: (_entryInfo) => true,
30293
- type: EntryTypes.FILE_TYPE,
30294
- lstat: false,
30295
- depth: 2147483648,
30296
- alwaysStat: false,
30297
- highWaterMark: 4096
30298
- };
30299
- Object.freeze(defaultOptions2);
30300
- var RECURSIVE_ERROR_CODE = "READDIRP_RECURSIVE_ERROR";
30301
- var NORMAL_FLOW_ERRORS = /* @__PURE__ */ new Set(["ENOENT", "EPERM", "EACCES", "ELOOP", RECURSIVE_ERROR_CODE]);
30302
- var ALL_TYPES = [
30303
- EntryTypes.DIR_TYPE,
30304
- EntryTypes.EVERYTHING_TYPE,
30305
- EntryTypes.FILE_DIR_TYPE,
30306
- EntryTypes.FILE_TYPE
30307
- ];
30308
- var DIR_TYPES = /* @__PURE__ */ new Set([
30309
- EntryTypes.DIR_TYPE,
30310
- EntryTypes.EVERYTHING_TYPE,
30311
- EntryTypes.FILE_DIR_TYPE
30312
- ]);
30313
- var FILE_TYPES = /* @__PURE__ */ new Set([
30314
- EntryTypes.EVERYTHING_TYPE,
30315
- EntryTypes.FILE_DIR_TYPE,
30316
- EntryTypes.FILE_TYPE
30317
- ]);
30318
- var isNormalFlowError = (error40) => NORMAL_FLOW_ERRORS.has(error40.code);
30319
- var wantBigintFsStats = process.platform === "win32";
30320
- var emptyFn = (_entryInfo) => true;
30321
- var normalizeFilter = (filter) => {
30322
- if (filter === void 0)
30323
- return emptyFn;
30324
- if (typeof filter === "function")
30325
- return filter;
30326
- if (typeof filter === "string") {
30327
- const fl = filter.trim();
30328
- return (entry) => entry.basename === fl;
30329
- }
30330
- if (Array.isArray(filter)) {
30331
- const trItems = filter.map((item) => item.trim());
30332
- return (entry) => trItems.some((f) => entry.basename === f);
30333
- }
30334
- return emptyFn;
30335
- };
30336
- var ReaddirpStream = class extends Readable2 {
30337
- constructor(options = {}) {
30338
- super({
30339
- objectMode: true,
30340
- autoDestroy: true,
30341
- highWaterMark: options.highWaterMark
30342
- });
30343
- const opts = { ...defaultOptions2, ...options };
30344
- const { root, type } = opts;
30345
- this._fileFilter = normalizeFilter(opts.fileFilter);
30346
- this._directoryFilter = normalizeFilter(opts.directoryFilter);
30347
- const statMethod = opts.lstat ? lstat : stat;
30348
- if (wantBigintFsStats) {
30349
- this._stat = (path24) => statMethod(path24, { bigint: true });
30350
- } else {
30351
- this._stat = statMethod;
30352
- }
30353
- this._maxDepth = opts.depth ?? defaultOptions2.depth;
30354
- this._wantsDir = type ? DIR_TYPES.has(type) : false;
30355
- this._wantsFile = type ? FILE_TYPES.has(type) : false;
30356
- this._wantsEverything = type === EntryTypes.EVERYTHING_TYPE;
30357
- this._root = presolve(root);
30358
- this._isDirent = !opts.alwaysStat;
30359
- this._statsProp = this._isDirent ? "dirent" : "stats";
30360
- this._rdOptions = { encoding: "utf8", withFileTypes: this._isDirent };
30361
- this.parents = [this._exploreDir(root, 1)];
30362
- this.reading = false;
30363
- this.parent = void 0;
30364
- }
30365
- async _read(batch) {
30366
- if (this.reading)
30367
- return;
30368
- this.reading = true;
30369
- try {
30370
- while (!this.destroyed && batch > 0) {
30371
- const par = this.parent;
30372
- const fil = par && par.files;
30373
- if (fil && fil.length > 0) {
30374
- const { path: path24, depth } = par;
30375
- const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path24));
30376
- const awaited = await Promise.all(slice);
30377
- for (const entry of awaited) {
30378
- if (!entry)
30379
- continue;
30380
- if (this.destroyed)
30381
- return;
30382
- const entryType = await this._getEntryType(entry);
30383
- if (entryType === "directory" && this._directoryFilter(entry)) {
30384
- if (depth <= this._maxDepth) {
30385
- this.parents.push(this._exploreDir(entry.fullPath, depth + 1));
30386
- }
30387
- if (this._wantsDir) {
30388
- this.push(entry);
30389
- batch--;
30390
- }
30391
- } else if ((entryType === "file" || this._includeAsFile(entry)) && this._fileFilter(entry)) {
30392
- if (this._wantsFile) {
30393
- this.push(entry);
30394
- batch--;
30395
- }
30396
- }
30397
- }
30398
- } else {
30399
- const parent = this.parents.pop();
30400
- if (!parent) {
30401
- this.push(null);
30402
- break;
30403
- }
30404
- this.parent = await parent;
30405
- if (this.destroyed)
30406
- return;
30407
- }
30408
- }
30409
- } catch (error40) {
30410
- this.destroy(error40);
30411
- } finally {
30412
- this.reading = false;
30413
- }
30414
- }
30415
- async _exploreDir(path24, depth) {
30416
- let files;
30417
- try {
30418
- files = await readdir(path24, this._rdOptions);
30419
- } catch (error40) {
30420
- this._onError(error40);
30421
- }
30422
- return { files, depth, path: path24 };
30423
- }
30424
- async _formatEntry(dirent, path24) {
30425
- let entry;
30426
- const basename6 = this._isDirent ? dirent.name : dirent;
30427
- try {
30428
- const fullPath = presolve(pjoin(path24, basename6));
30429
- entry = { path: prelative(this._root, fullPath), fullPath, basename: basename6 };
30430
- entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
30431
- } catch (err) {
30432
- this._onError(err);
30433
- return;
30434
- }
30435
- return entry;
30436
- }
30437
- _onError(err) {
30438
- if (isNormalFlowError(err) && !this.destroyed) {
30439
- this.emit("warn", err);
30440
- } else {
30441
- this.destroy(err);
30442
- }
30443
- }
30444
- async _getEntryType(entry) {
30445
- if (!entry && this._statsProp in entry) {
30446
- return "";
30447
- }
30448
- const stats = entry[this._statsProp];
30449
- if (stats.isFile())
30450
- return "file";
30451
- if (stats.isDirectory())
30452
- return "directory";
30453
- if (stats && stats.isSymbolicLink()) {
30454
- const full = entry.fullPath;
30455
- try {
30456
- const entryRealPath = await realpath(full);
30457
- const entryRealPathStats = await lstat(entryRealPath);
30458
- if (entryRealPathStats.isFile()) {
30459
- return "file";
30460
- }
30461
- if (entryRealPathStats.isDirectory()) {
30462
- const len = entryRealPath.length;
30463
- if (full.startsWith(entryRealPath) && full.substr(len, 1) === psep) {
30464
- const recursiveError = new Error(`Circular symlink detected: "${full}" points to "${entryRealPath}"`);
30465
- recursiveError.code = RECURSIVE_ERROR_CODE;
30466
- return this._onError(recursiveError);
30467
- }
30468
- return "directory";
30469
- }
30470
- } catch (error40) {
30471
- this._onError(error40);
30472
- return "";
30473
- }
30474
- }
30475
- }
30476
- _includeAsFile(entry) {
30477
- const stats = entry && entry[this._statsProp];
30478
- return stats && this._wantsEverything && !stats.isDirectory();
30479
- }
30480
- };
30481
- function readdirp(root, options = {}) {
30482
- let type = options.entryType || options.type;
30483
- if (type === "both")
30484
- type = EntryTypes.FILE_DIR_TYPE;
30485
- if (type)
30486
- options.type = type;
30487
- if (!root) {
30488
- throw new Error("readdirp: root argument is required. Usage: readdirp(root, options)");
30489
- } else if (typeof root !== "string") {
30490
- throw new TypeError("readdirp: root argument must be a string. Usage: readdirp(root, options)");
30491
- } else if (type && !ALL_TYPES.includes(type)) {
30492
- throw new Error(`readdirp: Invalid type passed. Use one of ${ALL_TYPES.join(", ")}`);
30493
- }
30494
- options.root = root;
30495
- return new ReaddirpStream(options);
30496
- }
30497
-
30498
- // ../../node_modules/.pnpm/chokidar@4.0.3/node_modules/chokidar/esm/handler.js
30499
- import { watchFile, unwatchFile, watch as fs_watch } from "fs";
30500
- import { open, stat as stat2, lstat as lstat2, realpath as fsrealpath } from "fs/promises";
30501
- import * as sysPath from "path";
30502
- import { type as osType } from "os";
30503
- var STR_DATA = "data";
30504
- var STR_END = "end";
30505
- var STR_CLOSE = "close";
30506
- var EMPTY_FN = () => {
30507
- };
30508
- var pl = process.platform;
30509
- var isWindows = pl === "win32";
30510
- var isMacos = pl === "darwin";
30511
- var isLinux = pl === "linux";
30512
- var isFreeBSD = pl === "freebsd";
30513
- var isIBMi = osType() === "OS400";
30514
- var EVENTS = {
30515
- ALL: "all",
30516
- READY: "ready",
30517
- ADD: "add",
30518
- CHANGE: "change",
30519
- ADD_DIR: "addDir",
30520
- UNLINK: "unlink",
30521
- UNLINK_DIR: "unlinkDir",
30522
- RAW: "raw",
30523
- ERROR: "error"
30524
- };
30525
- var EV = EVENTS;
30526
- var THROTTLE_MODE_WATCH = "watch";
30527
- var statMethods = { lstat: lstat2, stat: stat2 };
30528
- var KEY_LISTENERS = "listeners";
30529
- var KEY_ERR = "errHandlers";
30530
- var KEY_RAW = "rawEmitters";
30531
- var HANDLER_KEYS = [KEY_LISTENERS, KEY_ERR, KEY_RAW];
30532
- var binaryExtensions = /* @__PURE__ */ new Set([
30533
- "3dm",
30534
- "3ds",
30535
- "3g2",
30536
- "3gp",
30537
- "7z",
30538
- "a",
30539
- "aac",
30540
- "adp",
30541
- "afdesign",
30542
- "afphoto",
30543
- "afpub",
30544
- "ai",
30545
- "aif",
30546
- "aiff",
30547
- "alz",
30548
- "ape",
30549
- "apk",
30550
- "appimage",
30551
- "ar",
30552
- "arj",
30553
- "asf",
30554
- "au",
30555
- "avi",
30556
- "bak",
30557
- "baml",
30558
- "bh",
30559
- "bin",
30560
- "bk",
30561
- "bmp",
30562
- "btif",
30563
- "bz2",
30564
- "bzip2",
30565
- "cab",
30566
- "caf",
30567
- "cgm",
30568
- "class",
30569
- "cmx",
30570
- "cpio",
30571
- "cr2",
30572
- "cur",
30573
- "dat",
30574
- "dcm",
30575
- "deb",
30576
- "dex",
30577
- "djvu",
30578
- "dll",
30579
- "dmg",
30580
- "dng",
30581
- "doc",
30582
- "docm",
30583
- "docx",
30584
- "dot",
30585
- "dotm",
30586
- "dra",
30587
- "DS_Store",
30588
- "dsk",
30589
- "dts",
30590
- "dtshd",
30591
- "dvb",
30592
- "dwg",
30593
- "dxf",
30594
- "ecelp4800",
30595
- "ecelp7470",
30596
- "ecelp9600",
30597
- "egg",
30598
- "eol",
30599
- "eot",
30600
- "epub",
30601
- "exe",
30602
- "f4v",
30603
- "fbs",
30604
- "fh",
30605
- "fla",
30606
- "flac",
30607
- "flatpak",
30608
- "fli",
30609
- "flv",
30610
- "fpx",
30611
- "fst",
30612
- "fvt",
30613
- "g3",
30614
- "gh",
30615
- "gif",
30616
- "graffle",
30617
- "gz",
30618
- "gzip",
30619
- "h261",
30620
- "h263",
30621
- "h264",
30622
- "icns",
30623
- "ico",
30624
- "ief",
30625
- "img",
30626
- "ipa",
30627
- "iso",
30628
- "jar",
30629
- "jpeg",
30630
- "jpg",
30631
- "jpgv",
30632
- "jpm",
30633
- "jxr",
30634
- "key",
30635
- "ktx",
30636
- "lha",
30637
- "lib",
30638
- "lvp",
30639
- "lz",
30640
- "lzh",
30641
- "lzma",
30642
- "lzo",
30643
- "m3u",
30644
- "m4a",
30645
- "m4v",
30646
- "mar",
30647
- "mdi",
30648
- "mht",
30649
- "mid",
30650
- "midi",
30651
- "mj2",
30652
- "mka",
30653
- "mkv",
30654
- "mmr",
30655
- "mng",
30656
- "mobi",
30657
- "mov",
30658
- "movie",
30659
- "mp3",
30660
- "mp4",
30661
- "mp4a",
30662
- "mpeg",
30663
- "mpg",
30664
- "mpga",
30665
- "mxu",
30666
- "nef",
30667
- "npx",
30668
- "numbers",
30669
- "nupkg",
30670
- "o",
30671
- "odp",
30672
- "ods",
30673
- "odt",
30674
- "oga",
30675
- "ogg",
30676
- "ogv",
30677
- "otf",
30678
- "ott",
30679
- "pages",
30680
- "pbm",
30681
- "pcx",
30682
- "pdb",
30683
- "pdf",
30684
- "pea",
30685
- "pgm",
30686
- "pic",
30687
- "png",
30688
- "pnm",
30689
- "pot",
30690
- "potm",
30691
- "potx",
30692
- "ppa",
30693
- "ppam",
30694
- "ppm",
30695
- "pps",
30696
- "ppsm",
30697
- "ppsx",
30698
- "ppt",
30699
- "pptm",
30700
- "pptx",
30701
- "psd",
30702
- "pya",
30703
- "pyc",
30704
- "pyo",
30705
- "pyv",
30706
- "qt",
30707
- "rar",
30708
- "ras",
30709
- "raw",
30710
- "resources",
30711
- "rgb",
30712
- "rip",
30713
- "rlc",
30714
- "rmf",
30715
- "rmvb",
30716
- "rpm",
30717
- "rtf",
30718
- "rz",
30719
- "s3m",
30720
- "s7z",
30721
- "scpt",
30722
- "sgi",
30723
- "shar",
30724
- "snap",
30725
- "sil",
30726
- "sketch",
30727
- "slk",
30728
- "smv",
30729
- "snk",
30730
- "so",
30731
- "stl",
30732
- "suo",
30733
- "sub",
30734
- "swf",
30735
- "tar",
30736
- "tbz",
30737
- "tbz2",
30738
- "tga",
30739
- "tgz",
30740
- "thmx",
30741
- "tif",
30742
- "tiff",
30743
- "tlz",
30744
- "ttc",
30745
- "ttf",
30746
- "txz",
30747
- "udf",
30748
- "uvh",
30749
- "uvi",
30750
- "uvm",
30751
- "uvp",
30752
- "uvs",
30753
- "uvu",
30754
- "viv",
30755
- "vob",
30756
- "war",
30757
- "wav",
30758
- "wax",
30759
- "wbmp",
30760
- "wdp",
30761
- "weba",
30762
- "webm",
30763
- "webp",
30764
- "whl",
30765
- "wim",
30766
- "wm",
30767
- "wma",
30768
- "wmv",
30769
- "wmx",
30770
- "woff",
30771
- "woff2",
30772
- "wrm",
30773
- "wvx",
30774
- "xbm",
30775
- "xif",
30776
- "xla",
30777
- "xlam",
30778
- "xls",
30779
- "xlsb",
30780
- "xlsm",
30781
- "xlsx",
30782
- "xlt",
30783
- "xltm",
30784
- "xltx",
30785
- "xm",
30786
- "xmind",
30787
- "xpi",
30788
- "xpm",
30789
- "xwd",
30790
- "xz",
30791
- "z",
30792
- "zip",
30793
- "zipx"
30794
- ]);
30795
- var isBinaryPath = (filePath) => binaryExtensions.has(sysPath.extname(filePath).slice(1).toLowerCase());
30796
- var foreach = (val, fn) => {
30797
- if (val instanceof Set) {
30798
- val.forEach(fn);
30799
- } else {
30800
- fn(val);
30801
- }
30802
- };
30803
- var addAndConvert = (main, prop, item) => {
30804
- let container = main[prop];
30805
- if (!(container instanceof Set)) {
30806
- main[prop] = container = /* @__PURE__ */ new Set([container]);
30807
- }
30808
- container.add(item);
30809
- };
30810
- var clearItem = (cont) => (key) => {
30811
- const set2 = cont[key];
30812
- if (set2 instanceof Set) {
30813
- set2.clear();
30814
- } else {
30815
- delete cont[key];
30816
- }
30817
- };
30818
- var delFromSet = (main, prop, item) => {
30819
- const container = main[prop];
30820
- if (container instanceof Set) {
30821
- container.delete(item);
30822
- } else if (container === item) {
30823
- delete main[prop];
30824
- }
30825
- };
30826
- var isEmptySet = (val) => val instanceof Set ? val.size === 0 : !val;
30827
- var FsWatchInstances = /* @__PURE__ */ new Map();
30828
- function createFsWatchInstance(path24, options, listener, errHandler, emitRaw) {
30829
- const handleEvent = (rawEvent, evPath) => {
30830
- listener(path24);
30831
- emitRaw(rawEvent, evPath, { watchedPath: path24 });
30832
- if (evPath && path24 !== evPath) {
30833
- fsWatchBroadcast(sysPath.resolve(path24, evPath), KEY_LISTENERS, sysPath.join(path24, evPath));
30834
- }
30835
- };
30836
- try {
30837
- return fs_watch(path24, {
30838
- persistent: options.persistent
30839
- }, handleEvent);
30840
- } catch (error40) {
30841
- errHandler(error40);
30842
- return void 0;
30843
- }
30844
- }
30845
- var fsWatchBroadcast = (fullPath, listenerType, val1, val2, val3) => {
30846
- const cont = FsWatchInstances.get(fullPath);
30847
- if (!cont)
30848
- return;
30849
- foreach(cont[listenerType], (listener) => {
30850
- listener(val1, val2, val3);
30851
- });
30852
- };
30853
- var setFsWatchListener = (path24, fullPath, options, handlers) => {
30854
- const { listener, errHandler, rawEmitter } = handlers;
30855
- let cont = FsWatchInstances.get(fullPath);
30856
- let watcher;
30857
- if (!options.persistent) {
30858
- watcher = createFsWatchInstance(path24, options, listener, errHandler, rawEmitter);
30859
- if (!watcher)
30860
- return;
30861
- return watcher.close.bind(watcher);
30862
- }
30863
- if (cont) {
30864
- addAndConvert(cont, KEY_LISTENERS, listener);
30865
- addAndConvert(cont, KEY_ERR, errHandler);
30866
- addAndConvert(cont, KEY_RAW, rawEmitter);
30867
- } else {
30868
- watcher = createFsWatchInstance(
30869
- path24,
30870
- options,
30871
- fsWatchBroadcast.bind(null, fullPath, KEY_LISTENERS),
30872
- errHandler,
30873
- // no need to use broadcast here
30874
- fsWatchBroadcast.bind(null, fullPath, KEY_RAW)
30875
- );
30876
- if (!watcher)
30877
- return;
30878
- watcher.on(EV.ERROR, async (error40) => {
30879
- const broadcastErr = fsWatchBroadcast.bind(null, fullPath, KEY_ERR);
30880
- if (cont)
30881
- cont.watcherUnusable = true;
30882
- if (isWindows && error40.code === "EPERM") {
30883
- try {
30884
- const fd = await open(path24, "r");
30885
- await fd.close();
30886
- broadcastErr(error40);
30887
- } catch (err) {
30888
- }
30889
- } else {
30890
- broadcastErr(error40);
30891
- }
30892
- });
30893
- cont = {
30894
- listeners: listener,
30895
- errHandlers: errHandler,
30896
- rawEmitters: rawEmitter,
30897
- watcher
30898
- };
30899
- FsWatchInstances.set(fullPath, cont);
30900
- }
30901
- return () => {
30902
- delFromSet(cont, KEY_LISTENERS, listener);
30903
- delFromSet(cont, KEY_ERR, errHandler);
30904
- delFromSet(cont, KEY_RAW, rawEmitter);
30905
- if (isEmptySet(cont.listeners)) {
30906
- cont.watcher.close();
30907
- FsWatchInstances.delete(fullPath);
30908
- HANDLER_KEYS.forEach(clearItem(cont));
30909
- cont.watcher = void 0;
30910
- Object.freeze(cont);
30911
- }
30912
- };
30913
- };
30914
- var FsWatchFileInstances = /* @__PURE__ */ new Map();
30915
- var setFsWatchFileListener = (path24, fullPath, options, handlers) => {
30916
- const { listener, rawEmitter } = handlers;
30917
- let cont = FsWatchFileInstances.get(fullPath);
30918
- const copts = cont && cont.options;
30919
- if (copts && (copts.persistent < options.persistent || copts.interval > options.interval)) {
30920
- unwatchFile(fullPath);
30921
- cont = void 0;
30922
- }
30923
- if (cont) {
30924
- addAndConvert(cont, KEY_LISTENERS, listener);
30925
- addAndConvert(cont, KEY_RAW, rawEmitter);
30926
- } else {
30927
- cont = {
30928
- listeners: listener,
30929
- rawEmitters: rawEmitter,
30930
- options,
30931
- watcher: watchFile(fullPath, options, (curr, prev) => {
30932
- foreach(cont.rawEmitters, (rawEmitter2) => {
30933
- rawEmitter2(EV.CHANGE, fullPath, { curr, prev });
30934
- });
30935
- const currmtime = curr.mtimeMs;
30936
- if (curr.size !== prev.size || currmtime > prev.mtimeMs || currmtime === 0) {
30937
- foreach(cont.listeners, (listener2) => listener2(path24, curr));
30938
- }
30939
- })
30940
- };
30941
- FsWatchFileInstances.set(fullPath, cont);
30942
- }
30943
- return () => {
30944
- delFromSet(cont, KEY_LISTENERS, listener);
30945
- delFromSet(cont, KEY_RAW, rawEmitter);
30946
- if (isEmptySet(cont.listeners)) {
30947
- FsWatchFileInstances.delete(fullPath);
30948
- unwatchFile(fullPath);
30949
- cont.options = cont.watcher = void 0;
30950
- Object.freeze(cont);
30951
- }
30952
- };
30953
- };
30954
- var NodeFsHandler = class {
30955
- constructor(fsW) {
30956
- this.fsw = fsW;
30957
- this._boundHandleError = (error40) => fsW._handleError(error40);
30958
- }
30959
- /**
30960
- * Watch file for changes with fs_watchFile or fs_watch.
30961
- * @param path to file or dir
30962
- * @param listener on fs change
30963
- * @returns closer for the watcher instance
30964
- */
30965
- _watchWithNodeFs(path24, listener) {
30966
- const opts = this.fsw.options;
30967
- const directory = sysPath.dirname(path24);
30968
- const basename6 = sysPath.basename(path24);
30969
- const parent = this.fsw._getWatchedDir(directory);
30970
- parent.add(basename6);
30971
- const absolutePath = sysPath.resolve(path24);
30972
- const options = {
30973
- persistent: opts.persistent
30974
- };
30975
- if (!listener)
30976
- listener = EMPTY_FN;
30977
- let closer;
30978
- if (opts.usePolling) {
30979
- const enableBin = opts.interval !== opts.binaryInterval;
30980
- options.interval = enableBin && isBinaryPath(basename6) ? opts.binaryInterval : opts.interval;
30981
- closer = setFsWatchFileListener(path24, absolutePath, options, {
30982
- listener,
30983
- rawEmitter: this.fsw._emitRaw
30984
- });
30985
- } else {
30986
- closer = setFsWatchListener(path24, absolutePath, options, {
30987
- listener,
30988
- errHandler: this._boundHandleError,
30989
- rawEmitter: this.fsw._emitRaw
30990
- });
30991
- }
30992
- return closer;
30993
- }
30994
- /**
30995
- * Watch a file and emit add event if warranted.
30996
- * @returns closer for the watcher instance
30997
- */
30998
- _handleFile(file2, stats, initialAdd) {
30999
- if (this.fsw.closed) {
31000
- return;
31001
- }
31002
- const dirname7 = sysPath.dirname(file2);
31003
- const basename6 = sysPath.basename(file2);
31004
- const parent = this.fsw._getWatchedDir(dirname7);
31005
- let prevStats = stats;
31006
- if (parent.has(basename6))
31007
- return;
31008
- const listener = async (path24, newStats) => {
31009
- if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file2, 5))
31010
- return;
31011
- if (!newStats || newStats.mtimeMs === 0) {
31012
- try {
31013
- const newStats2 = await stat2(file2);
31014
- if (this.fsw.closed)
31015
- return;
31016
- const at = newStats2.atimeMs;
31017
- const mt = newStats2.mtimeMs;
31018
- if (!at || at <= mt || mt !== prevStats.mtimeMs) {
31019
- this.fsw._emit(EV.CHANGE, file2, newStats2);
31020
- }
31021
- if ((isMacos || isLinux || isFreeBSD) && prevStats.ino !== newStats2.ino) {
31022
- this.fsw._closeFile(path24);
31023
- prevStats = newStats2;
31024
- const closer2 = this._watchWithNodeFs(file2, listener);
31025
- if (closer2)
31026
- this.fsw._addPathCloser(path24, closer2);
31027
- } else {
31028
- prevStats = newStats2;
31029
- }
31030
- } catch (error40) {
31031
- this.fsw._remove(dirname7, basename6);
31032
- }
31033
- } else if (parent.has(basename6)) {
31034
- const at = newStats.atimeMs;
31035
- const mt = newStats.mtimeMs;
31036
- if (!at || at <= mt || mt !== prevStats.mtimeMs) {
31037
- this.fsw._emit(EV.CHANGE, file2, newStats);
31038
- }
31039
- prevStats = newStats;
31040
- }
31041
- };
31042
- const closer = this._watchWithNodeFs(file2, listener);
31043
- if (!(initialAdd && this.fsw.options.ignoreInitial) && this.fsw._isntIgnored(file2)) {
31044
- if (!this.fsw._throttle(EV.ADD, file2, 0))
31045
- return;
31046
- this.fsw._emit(EV.ADD, file2, stats);
31047
- }
31048
- return closer;
31049
- }
31050
- /**
31051
- * Handle symlinks encountered while reading a dir.
31052
- * @param entry returned by readdirp
31053
- * @param directory path of dir being read
31054
- * @param path of this item
31055
- * @param item basename of this item
31056
- * @returns true if no more processing is needed for this entry.
31057
- */
31058
- async _handleSymlink(entry, directory, path24, item) {
31059
- if (this.fsw.closed) {
31060
- return;
31061
- }
31062
- const full = entry.fullPath;
31063
- const dir = this.fsw._getWatchedDir(directory);
31064
- if (!this.fsw.options.followSymlinks) {
31065
- this.fsw._incrReadyCount();
31066
- let linkPath;
31067
- try {
31068
- linkPath = await fsrealpath(path24);
31069
- } catch (e) {
31070
- this.fsw._emitReady();
31071
- return true;
31072
- }
31073
- if (this.fsw.closed)
31074
- return;
31075
- if (dir.has(item)) {
31076
- if (this.fsw._symlinkPaths.get(full) !== linkPath) {
31077
- this.fsw._symlinkPaths.set(full, linkPath);
31078
- this.fsw._emit(EV.CHANGE, path24, entry.stats);
31079
- }
31080
- } else {
31081
- dir.add(item);
31082
- this.fsw._symlinkPaths.set(full, linkPath);
31083
- this.fsw._emit(EV.ADD, path24, entry.stats);
31084
- }
31085
- this.fsw._emitReady();
31086
- return true;
31087
- }
31088
- if (this.fsw._symlinkPaths.has(full)) {
31089
- return true;
31090
- }
31091
- this.fsw._symlinkPaths.set(full, true);
31092
- }
31093
- _handleRead(directory, initialAdd, wh, target, dir, depth, throttler) {
31094
- directory = sysPath.join(directory, "");
31095
- throttler = this.fsw._throttle("readdir", directory, 1e3);
31096
- if (!throttler)
31097
- return;
31098
- const previous = this.fsw._getWatchedDir(wh.path);
31099
- const current = /* @__PURE__ */ new Set();
31100
- let stream = this.fsw._readdirp(directory, {
31101
- fileFilter: (entry) => wh.filterPath(entry),
31102
- directoryFilter: (entry) => wh.filterDir(entry)
31103
- });
31104
- if (!stream)
31105
- return;
31106
- stream.on(STR_DATA, async (entry) => {
31107
- if (this.fsw.closed) {
31108
- stream = void 0;
31109
- return;
31110
- }
31111
- const item = entry.path;
31112
- let path24 = sysPath.join(directory, item);
31113
- current.add(item);
31114
- if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path24, item)) {
31115
- return;
31116
- }
31117
- if (this.fsw.closed) {
31118
- stream = void 0;
31119
- return;
31120
- }
31121
- if (item === target || !target && !previous.has(item)) {
31122
- this.fsw._incrReadyCount();
31123
- path24 = sysPath.join(dir, sysPath.relative(dir, path24));
31124
- this._addToNodeFs(path24, initialAdd, wh, depth + 1);
31125
- }
31126
- }).on(EV.ERROR, this._boundHandleError);
31127
- return new Promise((resolve16, reject) => {
31128
- if (!stream)
31129
- return reject();
31130
- stream.once(STR_END, () => {
31131
- if (this.fsw.closed) {
31132
- stream = void 0;
31133
- return;
31134
- }
31135
- const wasThrottled = throttler ? throttler.clear() : false;
31136
- resolve16(void 0);
31137
- previous.getChildren().filter((item) => {
31138
- return item !== directory && !current.has(item);
31139
- }).forEach((item) => {
31140
- this.fsw._remove(directory, item);
31141
- });
31142
- stream = void 0;
31143
- if (wasThrottled)
31144
- this._handleRead(directory, false, wh, target, dir, depth, throttler);
31145
- });
31146
- });
31147
- }
31148
- /**
31149
- * Read directory to add / remove files from `@watched` list and re-read it on change.
31150
- * @param dir fs path
31151
- * @param stats
31152
- * @param initialAdd
31153
- * @param depth relative to user-supplied path
31154
- * @param target child path targeted for watch
31155
- * @param wh Common watch helpers for this path
31156
- * @param realpath
31157
- * @returns closer for the watcher instance.
31158
- */
31159
- async _handleDir(dir, stats, initialAdd, depth, target, wh, realpath2) {
31160
- const parentDir = this.fsw._getWatchedDir(sysPath.dirname(dir));
31161
- const tracked = parentDir.has(sysPath.basename(dir));
31162
- if (!(initialAdd && this.fsw.options.ignoreInitial) && !target && !tracked) {
31163
- this.fsw._emit(EV.ADD_DIR, dir, stats);
31164
- }
31165
- parentDir.add(sysPath.basename(dir));
31166
- this.fsw._getWatchedDir(dir);
31167
- let throttler;
31168
- let closer;
31169
- const oDepth = this.fsw.options.depth;
31170
- if ((oDepth == null || depth <= oDepth) && !this.fsw._symlinkPaths.has(realpath2)) {
31171
- if (!target) {
31172
- await this._handleRead(dir, initialAdd, wh, target, dir, depth, throttler);
31173
- if (this.fsw.closed)
31174
- return;
31175
- }
31176
- closer = this._watchWithNodeFs(dir, (dirPath, stats2) => {
31177
- if (stats2 && stats2.mtimeMs === 0)
31178
- return;
31179
- this._handleRead(dirPath, false, wh, target, dir, depth, throttler);
31180
- });
31181
- }
31182
- return closer;
31183
- }
31184
- /**
31185
- * Handle added file, directory, or glob pattern.
31186
- * Delegates call to _handleFile / _handleDir after checks.
31187
- * @param path to file or ir
31188
- * @param initialAdd was the file added at watch instantiation?
31189
- * @param priorWh depth relative to user-supplied path
31190
- * @param depth Child path actually targeted for watch
31191
- * @param target Child path actually targeted for watch
31192
- */
31193
- async _addToNodeFs(path24, initialAdd, priorWh, depth, target) {
31194
- const ready = this.fsw._emitReady;
31195
- if (this.fsw._isIgnored(path24) || this.fsw.closed) {
31196
- ready();
31197
- return false;
31198
- }
31199
- const wh = this.fsw._getWatchHelpers(path24);
31200
- if (priorWh) {
31201
- wh.filterPath = (entry) => priorWh.filterPath(entry);
31202
- wh.filterDir = (entry) => priorWh.filterDir(entry);
31203
- }
31204
- try {
31205
- const stats = await statMethods[wh.statMethod](wh.watchPath);
31206
- if (this.fsw.closed)
31207
- return;
31208
- if (this.fsw._isIgnored(wh.watchPath, stats)) {
31209
- ready();
31210
- return false;
31211
- }
31212
- const follow = this.fsw.options.followSymlinks;
31213
- let closer;
31214
- if (stats.isDirectory()) {
31215
- const absPath = sysPath.resolve(path24);
31216
- const targetPath = follow ? await fsrealpath(path24) : path24;
31217
- if (this.fsw.closed)
31218
- return;
31219
- closer = await this._handleDir(wh.watchPath, stats, initialAdd, depth, target, wh, targetPath);
31220
- if (this.fsw.closed)
31221
- return;
31222
- if (absPath !== targetPath && targetPath !== void 0) {
31223
- this.fsw._symlinkPaths.set(absPath, targetPath);
31224
- }
31225
- } else if (stats.isSymbolicLink()) {
31226
- const targetPath = follow ? await fsrealpath(path24) : path24;
31227
- if (this.fsw.closed)
31228
- return;
31229
- const parent = sysPath.dirname(wh.watchPath);
31230
- this.fsw._getWatchedDir(parent).add(wh.watchPath);
31231
- this.fsw._emit(EV.ADD, wh.watchPath, stats);
31232
- closer = await this._handleDir(parent, stats, initialAdd, depth, path24, wh, targetPath);
31233
- if (this.fsw.closed)
31234
- return;
31235
- if (targetPath !== void 0) {
31236
- this.fsw._symlinkPaths.set(sysPath.resolve(path24), targetPath);
31237
- }
31238
- } else {
31239
- closer = this._handleFile(wh.watchPath, stats, initialAdd);
31240
- }
31241
- ready();
31242
- if (closer)
31243
- this.fsw._addPathCloser(path24, closer);
31244
- return false;
31245
- } catch (error40) {
31246
- if (this.fsw._handleError(error40)) {
31247
- ready();
31248
- return path24;
31249
- }
31250
- }
31251
- }
31252
- };
31253
-
31254
- // ../../node_modules/.pnpm/chokidar@4.0.3/node_modules/chokidar/esm/index.js
31255
- var SLASH = "/";
31256
- var SLASH_SLASH = "//";
31257
- var ONE_DOT = ".";
31258
- var TWO_DOTS = "..";
31259
- var STRING_TYPE = "string";
31260
- var BACK_SLASH_RE = /\\/g;
31261
- var DOUBLE_SLASH_RE = /\/\//;
31262
- var DOT_RE = /\..*\.(sw[px])$|~$|\.subl.*\.tmp/;
31263
- var REPLACER_RE = /^\.[/\\]/;
31264
- function arrify(item) {
31265
- return Array.isArray(item) ? item : [item];
31266
- }
31267
- var isMatcherObject = (matcher) => typeof matcher === "object" && matcher !== null && !(matcher instanceof RegExp);
31268
- function createPattern(matcher) {
31269
- if (typeof matcher === "function")
31270
- return matcher;
31271
- if (typeof matcher === "string")
31272
- return (string4) => matcher === string4;
31273
- if (matcher instanceof RegExp)
31274
- return (string4) => matcher.test(string4);
31275
- if (typeof matcher === "object" && matcher !== null) {
31276
- return (string4) => {
31277
- if (matcher.path === string4)
31278
- return true;
31279
- if (matcher.recursive) {
31280
- const relative6 = sysPath2.relative(matcher.path, string4);
31281
- if (!relative6) {
31282
- return false;
31283
- }
31284
- return !relative6.startsWith("..") && !sysPath2.isAbsolute(relative6);
31285
- }
31286
- return false;
31287
- };
31288
- }
31289
- return () => false;
31290
- }
31291
- function normalizePath(path24) {
31292
- if (typeof path24 !== "string")
31293
- throw new Error("string expected");
31294
- path24 = sysPath2.normalize(path24);
31295
- path24 = path24.replace(/\\/g, "/");
31296
- let prepend = false;
31297
- if (path24.startsWith("//"))
31298
- prepend = true;
31299
- const DOUBLE_SLASH_RE2 = /\/\//;
31300
- while (path24.match(DOUBLE_SLASH_RE2))
31301
- path24 = path24.replace(DOUBLE_SLASH_RE2, "/");
31302
- if (prepend)
31303
- path24 = "/" + path24;
31304
- return path24;
31305
- }
31306
- function matchPatterns(patterns, testString, stats) {
31307
- const path24 = normalizePath(testString);
31308
- for (let index = 0; index < patterns.length; index++) {
31309
- const pattern = patterns[index];
31310
- if (pattern(path24, stats)) {
31311
- return true;
31312
- }
31313
- }
31314
- return false;
31315
- }
31316
- function anymatch(matchers, testString) {
31317
- if (matchers == null) {
31318
- throw new TypeError("anymatch: specify first argument");
31319
- }
31320
- const matchersArray = arrify(matchers);
31321
- const patterns = matchersArray.map((matcher) => createPattern(matcher));
31322
- if (testString == null) {
31323
- return (testString2, stats) => {
31324
- return matchPatterns(patterns, testString2, stats);
31325
- };
31326
- }
31327
- return matchPatterns(patterns, testString);
31328
- }
31329
- var unifyPaths = (paths_) => {
31330
- const paths = arrify(paths_).flat();
31331
- if (!paths.every((p) => typeof p === STRING_TYPE)) {
31332
- throw new TypeError(`Non-string provided as watch path: ${paths}`);
31333
- }
31334
- return paths.map(normalizePathToUnix);
31335
- };
31336
- var toUnix = (string4) => {
31337
- let str = string4.replace(BACK_SLASH_RE, SLASH);
31338
- let prepend = false;
31339
- if (str.startsWith(SLASH_SLASH)) {
31340
- prepend = true;
31341
- }
31342
- while (str.match(DOUBLE_SLASH_RE)) {
31343
- str = str.replace(DOUBLE_SLASH_RE, SLASH);
31344
- }
31345
- if (prepend) {
31346
- str = SLASH + str;
31347
- }
31348
- return str;
31349
- };
31350
- var normalizePathToUnix = (path24) => toUnix(sysPath2.normalize(toUnix(path24)));
31351
- var normalizeIgnored = (cwd3 = "") => (path24) => {
31352
- if (typeof path24 === "string") {
31353
- return normalizePathToUnix(sysPath2.isAbsolute(path24) ? path24 : sysPath2.join(cwd3, path24));
31354
- } else {
31355
- return path24;
31356
- }
31357
- };
31358
- var getAbsolutePath = (path24, cwd3) => {
31359
- if (sysPath2.isAbsolute(path24)) {
31360
- return path24;
31361
- }
31362
- return sysPath2.join(cwd3, path24);
31363
- };
31364
- var EMPTY_SET = Object.freeze(/* @__PURE__ */ new Set());
31365
- var DirEntry = class {
31366
- constructor(dir, removeWatcher) {
31367
- this.path = dir;
31368
- this._removeWatcher = removeWatcher;
31369
- this.items = /* @__PURE__ */ new Set();
31370
- }
31371
- add(item) {
31372
- const { items } = this;
31373
- if (!items)
31374
- return;
31375
- if (item !== ONE_DOT && item !== TWO_DOTS)
31376
- items.add(item);
31377
- }
31378
- async remove(item) {
31379
- const { items } = this;
31380
- if (!items)
31381
- return;
31382
- items.delete(item);
31383
- if (items.size > 0)
31384
- return;
31385
- const dir = this.path;
31386
- try {
31387
- await readdir2(dir);
31388
- } catch (err) {
31389
- if (this._removeWatcher) {
31390
- this._removeWatcher(sysPath2.dirname(dir), sysPath2.basename(dir));
31391
- }
31392
- }
31393
- }
31394
- has(item) {
31395
- const { items } = this;
31396
- if (!items)
31397
- return;
31398
- return items.has(item);
31399
- }
31400
- getChildren() {
31401
- const { items } = this;
31402
- if (!items)
31403
- return [];
31404
- return [...items.values()];
31405
- }
31406
- dispose() {
31407
- this.items.clear();
31408
- this.path = "";
31409
- this._removeWatcher = EMPTY_FN;
31410
- this.items = EMPTY_SET;
31411
- Object.freeze(this);
31412
- }
31413
- };
31414
- var STAT_METHOD_F = "stat";
31415
- var STAT_METHOD_L = "lstat";
31416
- var WatchHelper = class {
31417
- constructor(path24, follow, fsw) {
31418
- this.fsw = fsw;
31419
- const watchPath = path24;
31420
- this.path = path24 = path24.replace(REPLACER_RE, "");
31421
- this.watchPath = watchPath;
31422
- this.fullWatchPath = sysPath2.resolve(watchPath);
31423
- this.dirParts = [];
31424
- this.dirParts.forEach((parts) => {
31425
- if (parts.length > 1)
31426
- parts.pop();
31427
- });
31428
- this.followSymlinks = follow;
31429
- this.statMethod = follow ? STAT_METHOD_F : STAT_METHOD_L;
31430
- }
31431
- entryPath(entry) {
31432
- return sysPath2.join(this.watchPath, sysPath2.relative(this.watchPath, entry.fullPath));
31433
- }
31434
- filterPath(entry) {
31435
- const { stats } = entry;
31436
- if (stats && stats.isSymbolicLink())
31437
- return this.filterDir(entry);
31438
- const resolvedPath = this.entryPath(entry);
31439
- return this.fsw._isntIgnored(resolvedPath, stats) && this.fsw._hasReadPermissions(stats);
31440
- }
31441
- filterDir(entry) {
31442
- return this.fsw._isntIgnored(this.entryPath(entry), entry.stats);
31443
- }
31444
- };
31445
- var FSWatcher = class extends EventEmitter2 {
31446
- // Not indenting methods for history sake; for now.
31447
- constructor(_opts = {}) {
31448
- super();
31449
- this.closed = false;
31450
- this._closers = /* @__PURE__ */ new Map();
31451
- this._ignoredPaths = /* @__PURE__ */ new Set();
31452
- this._throttled = /* @__PURE__ */ new Map();
31453
- this._streams = /* @__PURE__ */ new Set();
31454
- this._symlinkPaths = /* @__PURE__ */ new Map();
31455
- this._watched = /* @__PURE__ */ new Map();
31456
- this._pendingWrites = /* @__PURE__ */ new Map();
31457
- this._pendingUnlinks = /* @__PURE__ */ new Map();
31458
- this._readyCount = 0;
31459
- this._readyEmitted = false;
31460
- const awf = _opts.awaitWriteFinish;
31461
- const DEF_AWF = { stabilityThreshold: 2e3, pollInterval: 100 };
31462
- const opts = {
31463
- // Defaults
31464
- persistent: true,
31465
- ignoreInitial: false,
31466
- ignorePermissionErrors: false,
31467
- interval: 100,
31468
- binaryInterval: 300,
31469
- followSymlinks: true,
31470
- usePolling: false,
31471
- // useAsync: false,
31472
- atomic: true,
31473
- // NOTE: overwritten later (depends on usePolling)
31474
- ..._opts,
31475
- // Change format
31476
- ignored: _opts.ignored ? arrify(_opts.ignored) : arrify([]),
31477
- awaitWriteFinish: awf === true ? DEF_AWF : typeof awf === "object" ? { ...DEF_AWF, ...awf } : false
31478
- };
31479
- if (isIBMi)
31480
- opts.usePolling = true;
31481
- if (opts.atomic === void 0)
31482
- opts.atomic = !opts.usePolling;
31483
- const envPoll = process.env.CHOKIDAR_USEPOLLING;
31484
- if (envPoll !== void 0) {
31485
- const envLower = envPoll.toLowerCase();
31486
- if (envLower === "false" || envLower === "0")
31487
- opts.usePolling = false;
31488
- else if (envLower === "true" || envLower === "1")
31489
- opts.usePolling = true;
31490
- else
31491
- opts.usePolling = !!envLower;
31492
- }
31493
- const envInterval = process.env.CHOKIDAR_INTERVAL;
31494
- if (envInterval)
31495
- opts.interval = Number.parseInt(envInterval, 10);
31496
- let readyCalls = 0;
31497
- this._emitReady = () => {
31498
- readyCalls++;
31499
- if (readyCalls >= this._readyCount) {
31500
- this._emitReady = EMPTY_FN;
31501
- this._readyEmitted = true;
31502
- process.nextTick(() => this.emit(EVENTS.READY));
31503
- }
31504
- };
31505
- this._emitRaw = (...args) => this.emit(EVENTS.RAW, ...args);
31506
- this._boundRemove = this._remove.bind(this);
31507
- this.options = opts;
31508
- this._nodeFsHandler = new NodeFsHandler(this);
31509
- Object.freeze(opts);
31510
- }
31511
- _addIgnoredPath(matcher) {
31512
- if (isMatcherObject(matcher)) {
31513
- for (const ignored of this._ignoredPaths) {
31514
- if (isMatcherObject(ignored) && ignored.path === matcher.path && ignored.recursive === matcher.recursive) {
31515
- return;
31516
- }
31517
- }
31518
- }
31519
- this._ignoredPaths.add(matcher);
31520
- }
31521
- _removeIgnoredPath(matcher) {
31522
- this._ignoredPaths.delete(matcher);
31523
- if (typeof matcher === "string") {
31524
- for (const ignored of this._ignoredPaths) {
31525
- if (isMatcherObject(ignored) && ignored.path === matcher) {
31526
- this._ignoredPaths.delete(ignored);
31527
- }
31528
- }
31529
- }
31530
- }
31531
- // Public methods
31532
- /**
31533
- * Adds paths to be watched on an existing FSWatcher instance.
31534
- * @param paths_ file or file list. Other arguments are unused
31535
- */
31536
- add(paths_, _origAdd, _internal) {
31537
- const { cwd: cwd3 } = this.options;
31538
- this.closed = false;
31539
- this._closePromise = void 0;
31540
- let paths = unifyPaths(paths_);
31541
- if (cwd3) {
31542
- paths = paths.map((path24) => {
31543
- const absPath = getAbsolutePath(path24, cwd3);
31544
- return absPath;
31545
- });
31546
- }
31547
- paths.forEach((path24) => {
31548
- this._removeIgnoredPath(path24);
31549
- });
31550
- this._userIgnored = void 0;
31551
- if (!this._readyCount)
31552
- this._readyCount = 0;
31553
- this._readyCount += paths.length;
31554
- Promise.all(paths.map(async (path24) => {
31555
- const res = await this._nodeFsHandler._addToNodeFs(path24, !_internal, void 0, 0, _origAdd);
31556
- if (res)
31557
- this._emitReady();
31558
- return res;
31559
- })).then((results) => {
31560
- if (this.closed)
31561
- return;
31562
- results.forEach((item) => {
31563
- if (item)
31564
- this.add(sysPath2.dirname(item), sysPath2.basename(_origAdd || item));
31565
- });
31566
- });
31567
- return this;
31568
- }
31569
- /**
31570
- * Close watchers or start ignoring events from specified paths.
31571
- */
31572
- unwatch(paths_) {
31573
- if (this.closed)
31574
- return this;
31575
- const paths = unifyPaths(paths_);
31576
- const { cwd: cwd3 } = this.options;
31577
- paths.forEach((path24) => {
31578
- if (!sysPath2.isAbsolute(path24) && !this._closers.has(path24)) {
31579
- if (cwd3)
31580
- path24 = sysPath2.join(cwd3, path24);
31581
- path24 = sysPath2.resolve(path24);
31582
- }
31583
- this._closePath(path24);
31584
- this._addIgnoredPath(path24);
31585
- if (this._watched.has(path24)) {
31586
- this._addIgnoredPath({
31587
- path: path24,
31588
- recursive: true
31589
- });
31590
- }
31591
- this._userIgnored = void 0;
31592
- });
31593
- return this;
31594
- }
31595
- /**
31596
- * Close watchers and remove all listeners from watched paths.
31597
- */
31598
- close() {
31599
- if (this._closePromise) {
31600
- return this._closePromise;
31601
- }
31602
- this.closed = true;
31603
- this.removeAllListeners();
31604
- const closers = [];
31605
- this._closers.forEach((closerList) => closerList.forEach((closer) => {
31606
- const promise2 = closer();
31607
- if (promise2 instanceof Promise)
31608
- closers.push(promise2);
31609
- }));
31610
- this._streams.forEach((stream) => stream.destroy());
31611
- this._userIgnored = void 0;
31612
- this._readyCount = 0;
31613
- this._readyEmitted = false;
31614
- this._watched.forEach((dirent) => dirent.dispose());
31615
- this._closers.clear();
31616
- this._watched.clear();
31617
- this._streams.clear();
31618
- this._symlinkPaths.clear();
31619
- this._throttled.clear();
31620
- this._closePromise = closers.length ? Promise.all(closers).then(() => void 0) : Promise.resolve();
31621
- return this._closePromise;
31622
- }
31623
- /**
31624
- * Expose list of watched paths
31625
- * @returns for chaining
31626
- */
31627
- getWatched() {
31628
- const watchList = {};
31629
- this._watched.forEach((entry, dir) => {
31630
- const key = this.options.cwd ? sysPath2.relative(this.options.cwd, dir) : dir;
31631
- const index = key || ONE_DOT;
31632
- watchList[index] = entry.getChildren().sort();
31633
- });
31634
- return watchList;
31635
- }
31636
- emitWithAll(event, args) {
31637
- this.emit(event, ...args);
31638
- if (event !== EVENTS.ERROR)
31639
- this.emit(EVENTS.ALL, event, ...args);
31640
- }
31641
- // Common helpers
31642
- // --------------
31643
- /**
31644
- * Normalize and emit events.
31645
- * Calling _emit DOES NOT MEAN emit() would be called!
31646
- * @param event Type of event
31647
- * @param path File or directory path
31648
- * @param stats arguments to be passed with event
31649
- * @returns the error if defined, otherwise the value of the FSWatcher instance's `closed` flag
31650
- */
31651
- async _emit(event, path24, stats) {
31652
- if (this.closed)
31653
- return;
31654
- const opts = this.options;
31655
- if (isWindows)
31656
- path24 = sysPath2.normalize(path24);
31657
- if (opts.cwd)
31658
- path24 = sysPath2.relative(opts.cwd, path24);
31659
- const args = [path24];
31660
- if (stats != null)
31661
- args.push(stats);
31662
- const awf = opts.awaitWriteFinish;
31663
- let pw;
31664
- if (awf && (pw = this._pendingWrites.get(path24))) {
31665
- pw.lastChange = /* @__PURE__ */ new Date();
31666
- return this;
31667
- }
31668
- if (opts.atomic) {
31669
- if (event === EVENTS.UNLINK) {
31670
- this._pendingUnlinks.set(path24, [event, ...args]);
31671
- setTimeout(() => {
31672
- this._pendingUnlinks.forEach((entry, path25) => {
31673
- this.emit(...entry);
31674
- this.emit(EVENTS.ALL, ...entry);
31675
- this._pendingUnlinks.delete(path25);
31676
- });
31677
- }, typeof opts.atomic === "number" ? opts.atomic : 100);
31678
- return this;
31679
- }
31680
- if (event === EVENTS.ADD && this._pendingUnlinks.has(path24)) {
31681
- event = EVENTS.CHANGE;
31682
- this._pendingUnlinks.delete(path24);
31683
- }
31684
- }
31685
- if (awf && (event === EVENTS.ADD || event === EVENTS.CHANGE) && this._readyEmitted) {
31686
- const awfEmit = (err, stats2) => {
31687
- if (err) {
31688
- event = EVENTS.ERROR;
31689
- args[0] = err;
31690
- this.emitWithAll(event, args);
31691
- } else if (stats2) {
31692
- if (args.length > 1) {
31693
- args[1] = stats2;
31694
- } else {
31695
- args.push(stats2);
31696
- }
31697
- this.emitWithAll(event, args);
31698
- }
31699
- };
31700
- this._awaitWriteFinish(path24, awf.stabilityThreshold, event, awfEmit);
31701
- return this;
31702
- }
31703
- if (event === EVENTS.CHANGE) {
31704
- const isThrottled = !this._throttle(EVENTS.CHANGE, path24, 50);
31705
- if (isThrottled)
31706
- return this;
31707
- }
31708
- if (opts.alwaysStat && stats === void 0 && (event === EVENTS.ADD || event === EVENTS.ADD_DIR || event === EVENTS.CHANGE)) {
31709
- const fullPath = opts.cwd ? sysPath2.join(opts.cwd, path24) : path24;
31710
- let stats2;
31711
- try {
31712
- stats2 = await stat3(fullPath);
31713
- } catch (err) {
31714
- }
31715
- if (!stats2 || this.closed)
31716
- return;
31717
- args.push(stats2);
31718
- }
31719
- this.emitWithAll(event, args);
31720
- return this;
31721
- }
31722
- /**
31723
- * Common handler for errors
31724
- * @returns The error if defined, otherwise the value of the FSWatcher instance's `closed` flag
31725
- */
31726
- _handleError(error40) {
31727
- const code = error40 && error40.code;
31728
- if (error40 && code !== "ENOENT" && code !== "ENOTDIR" && (!this.options.ignorePermissionErrors || code !== "EPERM" && code !== "EACCES")) {
31729
- this.emit(EVENTS.ERROR, error40);
31730
- }
31731
- return error40 || this.closed;
31732
- }
31733
- /**
31734
- * Helper utility for throttling
31735
- * @param actionType type being throttled
31736
- * @param path being acted upon
31737
- * @param timeout duration of time to suppress duplicate actions
31738
- * @returns tracking object or false if action should be suppressed
31739
- */
31740
- _throttle(actionType, path24, timeout) {
31741
- if (!this._throttled.has(actionType)) {
31742
- this._throttled.set(actionType, /* @__PURE__ */ new Map());
31743
- }
31744
- const action = this._throttled.get(actionType);
31745
- if (!action)
31746
- throw new Error("invalid throttle");
31747
- const actionPath = action.get(path24);
31748
- if (actionPath) {
31749
- actionPath.count++;
31750
- return false;
31751
- }
31752
- let timeoutObject;
31753
- const clear = () => {
31754
- const item = action.get(path24);
31755
- const count = item ? item.count : 0;
31756
- action.delete(path24);
31757
- clearTimeout(timeoutObject);
31758
- if (item)
31759
- clearTimeout(item.timeoutObject);
31760
- return count;
31761
- };
31762
- timeoutObject = setTimeout(clear, timeout);
31763
- const thr = { timeoutObject, clear, count: 0 };
31764
- action.set(path24, thr);
31765
- return thr;
31766
- }
31767
- _incrReadyCount() {
31768
- return this._readyCount++;
31769
- }
31770
- /**
31771
- * Awaits write operation to finish.
31772
- * Polls a newly created file for size variations. When files size does not change for 'threshold' milliseconds calls callback.
31773
- * @param path being acted upon
31774
- * @param threshold Time in milliseconds a file size must be fixed before acknowledging write OP is finished
31775
- * @param event
31776
- * @param awfEmit Callback to be called when ready for event to be emitted.
31777
- */
31778
- _awaitWriteFinish(path24, threshold, event, awfEmit) {
31779
- const awf = this.options.awaitWriteFinish;
31780
- if (typeof awf !== "object")
31781
- return;
31782
- const pollInterval = awf.pollInterval;
31783
- let timeoutHandler;
31784
- let fullPath = path24;
31785
- if (this.options.cwd && !sysPath2.isAbsolute(path24)) {
31786
- fullPath = sysPath2.join(this.options.cwd, path24);
31787
- }
31788
- const now = /* @__PURE__ */ new Date();
31789
- const writes = this._pendingWrites;
31790
- function awaitWriteFinishFn(prevStat) {
31791
- statcb(fullPath, (err, curStat) => {
31792
- if (err || !writes.has(path24)) {
31793
- if (err && err.code !== "ENOENT")
31794
- awfEmit(err);
31795
- return;
31796
- }
31797
- const now2 = Number(/* @__PURE__ */ new Date());
31798
- if (prevStat && curStat.size !== prevStat.size) {
31799
- writes.get(path24).lastChange = now2;
31800
- }
31801
- const pw = writes.get(path24);
31802
- const df = now2 - pw.lastChange;
31803
- if (df >= threshold) {
31804
- writes.delete(path24);
31805
- awfEmit(void 0, curStat);
31806
- } else {
31807
- timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval, curStat);
31808
- }
31809
- });
31810
- }
31811
- if (!writes.has(path24)) {
31812
- writes.set(path24, {
31813
- lastChange: now,
31814
- cancelWait: () => {
31815
- writes.delete(path24);
31816
- clearTimeout(timeoutHandler);
31817
- return event;
31818
- }
31819
- });
31820
- timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval);
31821
- }
31822
- }
31823
- /**
31824
- * Determines whether user has asked to ignore this path.
31825
- */
31826
- _isIgnored(path24, stats) {
31827
- if (this.options.atomic && DOT_RE.test(path24))
31828
- return true;
31829
- if (!this._userIgnored) {
31830
- const { cwd: cwd3 } = this.options;
31831
- const ign = this.options.ignored;
31832
- const ignored = (ign || []).map(normalizeIgnored(cwd3));
31833
- const ignoredPaths = [...this._ignoredPaths];
31834
- const list = [...ignoredPaths.map(normalizeIgnored(cwd3)), ...ignored];
31835
- this._userIgnored = anymatch(list, void 0);
31836
- }
31837
- return this._userIgnored(path24, stats);
31838
- }
31839
- _isntIgnored(path24, stat4) {
31840
- return !this._isIgnored(path24, stat4);
31841
- }
31842
- /**
31843
- * Provides a set of common helpers and properties relating to symlink handling.
31844
- * @param path file or directory pattern being watched
31845
- */
31846
- _getWatchHelpers(path24) {
31847
- return new WatchHelper(path24, this.options.followSymlinks, this);
31848
- }
31849
- // Directory helpers
31850
- // -----------------
31851
- /**
31852
- * Provides directory tracking objects
31853
- * @param directory path of the directory
31854
- */
31855
- _getWatchedDir(directory) {
31856
- const dir = sysPath2.resolve(directory);
31857
- if (!this._watched.has(dir))
31858
- this._watched.set(dir, new DirEntry(dir, this._boundRemove));
31859
- return this._watched.get(dir);
31860
- }
31861
- // File helpers
31862
- // ------------
31863
- /**
31864
- * Check for read permissions: https://stackoverflow.com/a/11781404/1358405
31865
- */
31866
- _hasReadPermissions(stats) {
31867
- if (this.options.ignorePermissionErrors)
31868
- return true;
31869
- return Boolean(Number(stats.mode) & 256);
31870
- }
31871
- /**
31872
- * Handles emitting unlink events for
31873
- * files and directories, and via recursion, for
31874
- * files and directories within directories that are unlinked
31875
- * @param directory within which the following item is located
31876
- * @param item base path of item/directory
31877
- */
31878
- _remove(directory, item, isDirectory) {
31879
- const path24 = sysPath2.join(directory, item);
31880
- const fullPath = sysPath2.resolve(path24);
31881
- isDirectory = isDirectory != null ? isDirectory : this._watched.has(path24) || this._watched.has(fullPath);
31882
- if (!this._throttle("remove", path24, 100))
31883
- return;
31884
- if (!isDirectory && this._watched.size === 1) {
31885
- this.add(directory, item, true);
31886
- }
31887
- const wp = this._getWatchedDir(path24);
31888
- const nestedDirectoryChildren = wp.getChildren();
31889
- nestedDirectoryChildren.forEach((nested) => this._remove(path24, nested));
31890
- const parent = this._getWatchedDir(directory);
31891
- const wasTracked = parent.has(item);
31892
- parent.remove(item);
31893
- if (this._symlinkPaths.has(fullPath)) {
31894
- this._symlinkPaths.delete(fullPath);
31895
- }
31896
- let relPath = path24;
31897
- if (this.options.cwd)
31898
- relPath = sysPath2.relative(this.options.cwd, path24);
31899
- if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) {
31900
- const event = this._pendingWrites.get(relPath).cancelWait();
31901
- if (event === EVENTS.ADD)
31902
- return;
31903
- }
31904
- this._watched.delete(path24);
31905
- this._watched.delete(fullPath);
31906
- const eventName = isDirectory ? EVENTS.UNLINK_DIR : EVENTS.UNLINK;
31907
- if (wasTracked && !this._isIgnored(path24))
31908
- this._emit(eventName, path24);
31909
- this._closePath(path24);
31910
- }
31911
- /**
31912
- * Closes all watchers for a path
31913
- */
31914
- _closePath(path24) {
31915
- this._closeFile(path24);
31916
- const dir = sysPath2.dirname(path24);
31917
- this._getWatchedDir(dir).remove(sysPath2.basename(path24));
31918
- }
31919
- /**
31920
- * Closes only file-specific watchers
31921
- */
31922
- _closeFile(path24) {
31923
- const closers = this._closers.get(path24);
31924
- if (!closers)
31925
- return;
31926
- closers.forEach((closer) => closer());
31927
- this._closers.delete(path24);
31928
- }
31929
- _addPathCloser(path24, closer) {
31930
- if (!closer)
31931
- return;
31932
- let list = this._closers.get(path24);
31933
- if (!list) {
31934
- list = [];
31935
- this._closers.set(path24, list);
31936
- }
31937
- list.push(closer);
31938
- }
31939
- _readdirp(root, opts) {
31940
- if (this.closed)
31941
- return;
31942
- const options = { type: EVENTS.ALL, alwaysStat: true, lstat: true, ...opts, depth: 0 };
31943
- let stream = readdirp(root, options);
31944
- this._streams.add(stream);
31945
- stream.once(STR_CLOSE, () => {
31946
- stream = void 0;
31947
- });
31948
- stream.once(STR_END, () => {
31949
- if (stream) {
31950
- this._streams.delete(stream);
31951
- stream = void 0;
31952
- }
31953
- });
31954
- return stream;
31955
- }
31956
- };
31957
- function watch(paths, options = {}) {
31958
- const watcher = new FSWatcher(options);
31959
- watcher.add(paths);
31960
- return watcher;
31961
- }
31962
- var esm_default = { watch, FSWatcher };
31963
-
31964
- // src/files/watch-file-index.ts
31965
30489
  var DEBOUNCE_MS = 900;
31966
30490
  function shouldIgnoreRelative(rel) {
31967
30491
  const n = rel.replace(/\\/g, "/");
@@ -31970,8 +30494,43 @@ function shouldIgnoreRelative(rel) {
31970
30494
  if (n.includes("/.buildautomaton/") || n.startsWith(".buildautomaton/")) return true;
31971
30495
  return false;
31972
30496
  }
31973
- function startFileIndexWatcher(cwd3 = process.cwd()) {
31974
- const resolved = path21.resolve(cwd3);
30497
+ function attachWatchErrorLog(w) {
30498
+ w.on("error", (err) => {
30499
+ console.error("[file-index] File watcher error:", err);
30500
+ });
30501
+ }
30502
+ function createFsWatcher(resolved, schedule) {
30503
+ const onEvent = (_event, filename) => {
30504
+ if (filename != null) {
30505
+ const rel = typeof filename === "string" ? filename.replace(/\\/g, "/") : filename.toString("utf8").replace(/\\/g, "/");
30506
+ if (shouldIgnoreRelative(rel)) return;
30507
+ }
30508
+ schedule();
30509
+ };
30510
+ try {
30511
+ const w = watch(resolved, { recursive: true }, onEvent);
30512
+ attachWatchErrorLog(w);
30513
+ return w;
30514
+ } catch (e) {
30515
+ const code = typeof e === "object" && e !== null && "code" in e ? e.code : void 0;
30516
+ if (code === "ERR_FEATURE_UNAVAILABLE_ON_PLATFORM") {
30517
+ console.warn(
30518
+ "[file-index] Recursive file watching is unavailable on this platform; using non-recursive watch (Node 20+ on Linux enables recursive). Nested file changes may be missed until you upgrade."
30519
+ );
30520
+ const w = watch(resolved, { recursive: false }, onEvent);
30521
+ attachWatchErrorLog(w);
30522
+ return w;
30523
+ }
30524
+ throw e;
30525
+ }
30526
+ }
30527
+ function startFileIndexWatcher(cwd = getBridgeWorkspaceDirectory()) {
30528
+ const resolved = path21.resolve(cwd);
30529
+ try {
30530
+ buildFileIndex(resolved);
30531
+ } catch (e) {
30532
+ console.error("[file-index] Initial index build failed:", e);
30533
+ }
31975
30534
  let timer = null;
31976
30535
  const runRebuild = () => {
31977
30536
  try {
@@ -31987,27 +30546,13 @@ function startFileIndexWatcher(cwd3 = process.cwd()) {
31987
30546
  runRebuild();
31988
30547
  }, DEBOUNCE_MS);
31989
30548
  };
31990
- const watcher = esm_default.watch(".", {
31991
- cwd: resolved,
31992
- ignoreInitial: true,
31993
- persistent: true,
31994
- ignored: (p) => {
31995
- const rel = path21.isAbsolute(p) ? path21.relative(resolved, p).replace(/\\/g, "/") : p.replace(/\\/g, "/");
31996
- return shouldIgnoreRelative(rel || ".");
31997
- },
31998
- awaitWriteFinish: { stabilityThreshold: 250, pollInterval: 100 }
31999
- });
32000
- watcher.on("add", schedule);
32001
- watcher.on("change", schedule);
32002
- watcher.on("unlink", schedule);
32003
- watcher.on("addDir", schedule);
32004
- watcher.on("unlinkDir", schedule);
30549
+ const watcher = createFsWatcher(resolved, schedule);
32005
30550
  return () => {
32006
30551
  if (timer) {
32007
30552
  clearTimeout(timer);
32008
30553
  timer = null;
32009
30554
  }
32010
- void watcher.close();
30555
+ watcher.close();
32011
30556
  };
32012
30557
  }
32013
30558
 
@@ -32031,18 +30576,20 @@ function sendDevServerStatus(getWs, serverId, status, options) {
32031
30576
 
32032
30577
  // src/dev-servers/process/terminate-child-process.ts
32033
30578
  async function sigtermAndWaitForExit(proc, graceMs, log2, shortId) {
32034
- const exited = new Promise((resolve16) => {
32035
- proc.once("exit", () => resolve16());
30579
+ const exited = new Promise((resolve14) => {
30580
+ proc.once("exit", () => resolve14());
32036
30581
  });
32037
- log2(`[dev-server] Sending SIGTERM to ${shortId} (pid=${proc.pid ?? "?"})\u2026`);
30582
+ log2(`[dev-server] Sending SIGTERM to ${shortId} (pid=${proc.pid ?? "?"}).`);
32038
30583
  try {
32039
30584
  proc.kill("SIGTERM");
32040
30585
  } catch {
32041
30586
  }
32042
- await Promise.race([exited, new Promise((resolve16) => setTimeout(resolve16, graceMs))]);
30587
+ await Promise.race([exited, new Promise((resolve14) => setTimeout(resolve14, graceMs))]);
32043
30588
  }
32044
30589
  function forceKillChild(proc, log2, shortId, graceMs) {
32045
- log2(`[dev-server] ${shortId} did not exit within ${graceMs}ms; sending SIGKILL (pid=${proc.pid ?? "?"})\u2026`);
30590
+ log2(
30591
+ `[dev-server] ${shortId} did not exit within ${graceMs}ms; sending SIGKILL (pid=${proc.pid ?? "?"}).`
30592
+ );
32046
30593
  proc.removeAllListeners();
32047
30594
  try {
32048
30595
  proc.kill("SIGKILL");
@@ -32051,7 +30598,7 @@ function forceKillChild(proc, log2, shortId, graceMs) {
32051
30598
  }
32052
30599
 
32053
30600
  // src/dev-servers/process/wire-dev-server-child-process.ts
32054
- import fs16 from "node:fs";
30601
+ import fs17 from "node:fs";
32055
30602
 
32056
30603
  // src/dev-servers/manager/forward-pipe.ts
32057
30604
  function forwardChildPipe(childReadable, terminal, onData) {
@@ -32087,7 +30634,7 @@ function wireDevServerChildProcess(d) {
32087
30634
  d.setPollInterval(void 0);
32088
30635
  return;
32089
30636
  }
32090
- fs16.readFile(d.mergedLogPath, (err, buf) => {
30637
+ fs17.readFile(d.mergedLogPath, (err, buf) => {
32091
30638
  if (err || (d.getSpawnGeneration() ?? 0) !== d.scheduledGen) return;
32092
30639
  if (buf.length <= d.mergedReadPos.value) return;
32093
30640
  const chunk = Buffer.from(buf.subarray(d.mergedReadPos.value));
@@ -32112,7 +30659,7 @@ function wireDevServerChildProcess(d) {
32112
30659
  d.rmMergedCleanupDir(cleanupDir);
32113
30660
  }
32114
30661
  if (signal) {
32115
- d.log(`[dev-server] ${title} stopped (signal ${String(signal)})`);
30662
+ d.log(`[dev-server] ${title} stopped (signal: ${String(signal)}).`);
32116
30663
  } else if (code !== null && code !== 0) {
32117
30664
  const errTail = d.stderrTail.getTail().slice(-3).join("\n");
32118
30665
  d.log(`[dev-server] ${title} exited with code ${code}${errTail ? `
@@ -32125,7 +30672,7 @@ ${errTail}` : ""}`);
32125
30672
  d.sendStatus(code === 0 || code == null ? "stopped" : "error", detail, tails);
32126
30673
  };
32127
30674
  if (mergedPath) {
32128
- fs16.readFile(mergedPath, (err, buf) => {
30675
+ fs17.readFile(mergedPath, (err, buf) => {
32129
30676
  if (!err && buf.length > d.mergedReadPos.value) {
32130
30677
  const chunk = Buffer.from(buf.subarray(d.mergedReadPos.value));
32131
30678
  if (chunk.length > 0) {
@@ -32227,13 +30774,13 @@ function parseDevServerDefs(servers) {
32227
30774
  }
32228
30775
 
32229
30776
  // src/dev-servers/manager/shell-spawn/utils.ts
32230
- import fs17 from "node:fs";
30777
+ import fs18 from "node:fs";
32231
30778
  function isSpawnEbadf(e) {
32232
30779
  return typeof e === "object" && e !== null && "code" in e && e.code === "EBADF";
32233
30780
  }
32234
30781
  function rmDirQuiet(dir) {
32235
30782
  try {
32236
- fs17.rmSync(dir, { recursive: true, force: true });
30783
+ fs18.rmSync(dir, { recursive: true, force: true });
32237
30784
  } catch {
32238
30785
  }
32239
30786
  }
@@ -32241,7 +30788,7 @@ var cachedDevNullReadFd;
32241
30788
  function devNullReadFd() {
32242
30789
  if (cachedDevNullReadFd === void 0) {
32243
30790
  const devPath = process.platform === "win32" ? "nul" : "/dev/null";
32244
- cachedDevNullReadFd = fs17.openSync(devPath, "r");
30791
+ cachedDevNullReadFd = fs18.openSync(devPath, "r");
32245
30792
  }
32246
30793
  return cachedDevNullReadFd;
32247
30794
  }
@@ -32251,7 +30798,7 @@ function pipedStdoutStderrFor(attemptStdio) {
32251
30798
 
32252
30799
  // src/dev-servers/manager/shell-spawn/try-spawn-piped-via-sh.ts
32253
30800
  import { spawn as spawn5 } from "node:child_process";
32254
- function trySpawnPipedViaSh(command, env, cwd3, signal) {
30801
+ function trySpawnPipedViaSh(command, env, cwd, signal) {
32255
30802
  const attempts = [
32256
30803
  { stdio: [devNullReadFd(), "pipe", "pipe"], endStdin: false },
32257
30804
  { stdio: ["ignore", "pipe", "pipe"], endStdin: true },
@@ -32262,7 +30809,7 @@ function trySpawnPipedViaSh(command, env, cwd3, signal) {
32262
30809
  const attempt = attempts[i];
32263
30810
  const opts = {
32264
30811
  env,
32265
- cwd: cwd3,
30812
+ cwd,
32266
30813
  stdio: attempt.stdio,
32267
30814
  ...signal ? { signal } : {}
32268
30815
  };
@@ -32294,11 +30841,11 @@ function trySpawnPipedViaSh(command, env, cwd3, signal) {
32294
30841
 
32295
30842
  // src/dev-servers/manager/shell-spawn/try-spawn-shell-true-piped.ts
32296
30843
  import { spawn as spawn6 } from "node:child_process";
32297
- function trySpawnShellTruePiped(command, env, cwd3, devNullFd, signal) {
30844
+ function trySpawnShellTruePiped(command, env, cwd, devNullFd, signal) {
32298
30845
  try {
32299
30846
  const opts = {
32300
30847
  env,
32301
- cwd: cwd3,
30848
+ cwd,
32302
30849
  stdio: [devNullFd, "pipe", "pipe"],
32303
30850
  shell: true,
32304
30851
  ...signal ? { signal } : {}
@@ -32315,15 +30862,15 @@ function trySpawnShellTruePiped(command, env, cwd3, devNullFd, signal) {
32315
30862
 
32316
30863
  // src/dev-servers/manager/shell-spawn/try-spawn-merged-log-file.ts
32317
30864
  import { spawn as spawn7 } from "node:child_process";
32318
- import fs18 from "node:fs";
30865
+ import fs19 from "node:fs";
32319
30866
  import { tmpdir } from "node:os";
32320
30867
  import path22 from "node:path";
32321
- function trySpawnMergedLogFile(command, env, cwd3, signal) {
32322
- const tmpRoot = fs18.mkdtempSync(path22.join(tmpdir(), "ba-devsrv-log-"));
30868
+ function trySpawnMergedLogFile(command, env, cwd, signal) {
30869
+ const tmpRoot = fs19.mkdtempSync(path22.join(tmpdir(), "ba-devsrv-log-"));
32323
30870
  const logPath = path22.join(tmpRoot, "combined.log");
32324
30871
  let logFd;
32325
30872
  try {
32326
- logFd = fs18.openSync(logPath, "a");
30873
+ logFd = fs19.openSync(logPath, "a");
32327
30874
  } catch {
32328
30875
  rmDirQuiet(tmpRoot);
32329
30876
  return null;
@@ -32334,15 +30881,15 @@ function trySpawnMergedLogFile(command, env, cwd3, signal) {
32334
30881
  if (process.platform === "win32") {
32335
30882
  proc = spawn7(process.env.ComSpec || "cmd.exe", ["/d", "/s", "/c", command], {
32336
30883
  env,
32337
- cwd: cwd3,
30884
+ cwd,
32338
30885
  stdio,
32339
30886
  windowsHide: true,
32340
30887
  ...signal ? { signal } : {}
32341
30888
  });
32342
30889
  } else {
32343
- proc = spawn7("/bin/sh", ["-c", command], { env, cwd: cwd3, stdio, ...signal ? { signal } : {} });
30890
+ proc = spawn7("/bin/sh", ["-c", command], { env, cwd, stdio, ...signal ? { signal } : {} });
32344
30891
  }
32345
- fs18.closeSync(logFd);
30892
+ fs19.closeSync(logFd);
32346
30893
  return {
32347
30894
  proc,
32348
30895
  pipedStdoutStderr: true,
@@ -32351,7 +30898,7 @@ function trySpawnMergedLogFile(command, env, cwd3, signal) {
32351
30898
  };
32352
30899
  } catch (e) {
32353
30900
  try {
32354
- fs18.closeSync(logFd);
30901
+ fs19.closeSync(logFd);
32355
30902
  } catch {
32356
30903
  }
32357
30904
  rmDirQuiet(tmpRoot);
@@ -32362,25 +30909,25 @@ function trySpawnMergedLogFile(command, env, cwd3, signal) {
32362
30909
 
32363
30910
  // src/dev-servers/manager/shell-spawn/try-spawn-shell-script-log-redirect.ts
32364
30911
  import { spawn as spawn8 } from "node:child_process";
32365
- import fs19 from "node:fs";
30912
+ import fs20 from "node:fs";
32366
30913
  import { tmpdir as tmpdir2 } from "node:os";
32367
30914
  import path23 from "node:path";
32368
30915
  function shSingleQuote(s) {
32369
30916
  return `'${s.replace(/'/g, `'\\''`)}'`;
32370
30917
  }
32371
- function trySpawnShellScriptLogRedirectUnix(command, env, cwd3, signal) {
32372
- const tmpRoot = fs19.mkdtempSync(path23.join(tmpdir2(), "ba-devsrv-sh-"));
30918
+ function trySpawnShellScriptLogRedirectUnix(command, env, cwd, signal) {
30919
+ const tmpRoot = fs20.mkdtempSync(path23.join(tmpdir2(), "ba-devsrv-sh-"));
32373
30920
  const logPath = path23.join(tmpRoot, "combined.log");
32374
30921
  const innerPath = path23.join(tmpRoot, "_cmd.sh");
32375
30922
  const runnerPath = path23.join(tmpRoot, "_run.sh");
32376
30923
  try {
32377
- fs19.writeFileSync(innerPath, `#!/bin/sh
30924
+ fs20.writeFileSync(innerPath, `#!/bin/sh
32378
30925
  ${command}
32379
30926
  `);
32380
- fs19.writeFileSync(
30927
+ fs20.writeFileSync(
32381
30928
  runnerPath,
32382
30929
  `#!/bin/sh
32383
- cd ${shSingleQuote(cwd3)}
30930
+ cd ${shSingleQuote(cwd)}
32384
30931
  /bin/sh ${shSingleQuote(innerPath)} >>${shSingleQuote(logPath)} 2>&1
32385
30932
  `
32386
30933
  );
@@ -32402,17 +30949,17 @@ cd ${shSingleQuote(cwd3)}
32402
30949
  throw e;
32403
30950
  }
32404
30951
  }
32405
- function trySpawnShellScriptLogRedirectWin(command, env, cwd3, signal) {
32406
- const tmpRoot = fs19.mkdtempSync(path23.join(tmpdir2(), "ba-devsrv-sh-"));
30952
+ function trySpawnShellScriptLogRedirectWin(command, env, cwd, signal) {
30953
+ const tmpRoot = fs20.mkdtempSync(path23.join(tmpdir2(), "ba-devsrv-sh-"));
32407
30954
  const logPath = path23.join(tmpRoot, "combined.log");
32408
30955
  const runnerPath = path23.join(tmpRoot, "_run.bat");
32409
30956
  const q = (p) => `"${p.replace(/"/g, '""')}"`;
32410
30957
  const com = process.env.ComSpec || "cmd.exe";
32411
30958
  try {
32412
- fs19.writeFileSync(
30959
+ fs20.writeFileSync(
32413
30960
  runnerPath,
32414
30961
  `@ECHO OFF\r
32415
- CD /D ${q(cwd3)}\r
30962
+ CD /D ${q(cwd)}\r
32416
30963
  ${command} >> ${q(logPath)} 2>&1\r
32417
30964
  `
32418
30965
  );
@@ -32438,10 +30985,10 @@ ${command} >> ${q(logPath)} 2>&1\r
32438
30985
 
32439
30986
  // src/dev-servers/manager/shell-spawn/try-spawn-inherit.ts
32440
30987
  import { spawn as spawn9 } from "node:child_process";
32441
- function trySpawnInheritStdio(command, env, cwd3, signal) {
30988
+ function trySpawnInheritStdio(command, env, cwd, signal) {
32442
30989
  const opts = {
32443
30990
  env,
32444
- cwd: cwd3,
30991
+ cwd,
32445
30992
  stdio: "inherit",
32446
30993
  ...signal ? { signal } : {}
32447
30994
  };
@@ -32457,27 +31004,27 @@ function trySpawnInheritStdio(command, env, cwd3, signal) {
32457
31004
  }
32458
31005
 
32459
31006
  // src/dev-servers/manager/shell-spawn/shell-spawn.ts
32460
- function shellSpawn(command, env, cwd3, options) {
31007
+ function shellSpawn(command, env, cwd, options) {
32461
31008
  const signal = options?.signal;
32462
- const piped = trySpawnPipedViaSh(command, env, cwd3, signal);
31009
+ const piped = trySpawnPipedViaSh(command, env, cwd, signal);
32463
31010
  if (piped.ok) {
32464
31011
  return piped.result;
32465
31012
  }
32466
31013
  let lastErr = piped.lastErr;
32467
- const shellTrueProc = trySpawnShellTruePiped(command, env, cwd3, devNullReadFd(), signal);
31014
+ const shellTrueProc = trySpawnShellTruePiped(command, env, cwd, devNullReadFd(), signal);
32468
31015
  if (shellTrueProc) {
32469
31016
  return { proc: shellTrueProc, pipedStdoutStderr: true };
32470
31017
  }
32471
- const fileCapture = trySpawnMergedLogFile(command, env, cwd3, signal);
31018
+ const fileCapture = trySpawnMergedLogFile(command, env, cwd, signal);
32472
31019
  if (fileCapture) {
32473
31020
  return fileCapture;
32474
31021
  }
32475
- const scriptCapture = process.platform === "win32" ? trySpawnShellScriptLogRedirectWin(command, env, cwd3, signal) : trySpawnShellScriptLogRedirectUnix(command, env, cwd3, signal);
31022
+ const scriptCapture = process.platform === "win32" ? trySpawnShellScriptLogRedirectWin(command, env, cwd, signal) : trySpawnShellScriptLogRedirectUnix(command, env, cwd, signal);
32476
31023
  if (scriptCapture) {
32477
31024
  return scriptCapture;
32478
31025
  }
32479
31026
  try {
32480
- return trySpawnInheritStdio(command, env, cwd3, signal);
31027
+ return trySpawnInheritStdio(command, env, cwd, signal);
32481
31028
  } catch (e) {
32482
31029
  throw lastErr instanceof Error ? lastErr : e instanceof Error ? e : new Error(String(e));
32483
31030
  }
@@ -32535,9 +31082,11 @@ var DevServerManager = class {
32535
31082
  abortControllersByServerId = /* @__PURE__ */ new Map();
32536
31083
  getWs;
32537
31084
  log;
31085
+ getBridgeCwd;
32538
31086
  constructor(options) {
32539
31087
  this.getWs = options.getWs;
32540
31088
  this.log = options.log;
31089
+ this.getBridgeCwd = options.getBridgeCwd ?? (() => process.cwd());
32541
31090
  }
32542
31091
  attachFirehose(send) {
32543
31092
  this.firehoseSend = send;
@@ -32661,7 +31210,7 @@ var DevServerManager = class {
32661
31210
  this.sendStatus(serverId, "starting", void 0, emptyTails());
32662
31211
  const ac = new AbortController();
32663
31212
  this.abortControllersByServerId.set(serverId, ac);
32664
- const cwd3 = process.cwd();
31213
+ const cwd = this.getBridgeCwd();
32665
31214
  const childEnv = envForSpawn(process.env, def.env, def.ports);
32666
31215
  const cmd = substituteCommand(def.command.trim(), childEnv);
32667
31216
  const title = def.name.trim() || serverId.slice(0, 8);
@@ -32676,7 +31225,7 @@ var DevServerManager = class {
32676
31225
  let mergedLogPath;
32677
31226
  let mergedCleanupDir;
32678
31227
  try {
32679
- const spawned = shellSpawn(cmd, childEnv, cwd3, {
31228
+ const spawned = shellSpawn(cmd, childEnv, cwd, {
32680
31229
  signal: ac.signal
32681
31230
  });
32682
31231
  proc = spawned.proc;
@@ -32685,7 +31234,7 @@ var DevServerManager = class {
32685
31234
  mergedCleanupDir = spawned.mergedLogCleanupDir;
32686
31235
  } catch (e) {
32687
31236
  const msg = e instanceof Error ? e.message : String(e);
32688
- this.log(`[dev-server] ${title} failed to start: ${msg}`);
31237
+ this.log(`[dev-server] Failed to start ${title}: ${msg}`);
32689
31238
  this.abortControllersByServerId.delete(serverId);
32690
31239
  this.sendStatus(serverId, "error", msg, emptyTails());
32691
31240
  return;
@@ -32810,7 +31359,7 @@ function startStreamingProxy(ws, log2, pr) {
32810
31359
  },
32811
31360
  onEnd: () => sendWsMessage(ws, { type: "proxy_result_end", id: pr.id }),
32812
31361
  onError: (error40) => {
32813
- log2(`[Proxy and log service] streaming preview failed: ${error40}`);
31362
+ log2(`[Proxy and log service] Streaming preview failed: ${error40}`);
32814
31363
  sendWsMessage(ws, { type: "proxy_result_error", id: pr.id, error: error40 });
32815
31364
  }
32816
31365
  });
@@ -32883,11 +31432,11 @@ var handleProxyMessage = (msg, deps) => {
32883
31432
  return;
32884
31433
  }
32885
31434
  void proxyToLocal(pr).then((res) => {
32886
- if (res.error) deps.log(`[Proxy and log service] preview proxy failed: ${res.error}`);
31435
+ if (res.error) deps.log(`[Proxy and log service] Preview proxy failed: ${res.error}`);
32887
31436
  sendWsMessage(deps.ws, { type: "proxy_result", ...res, id: pr.id });
32888
31437
  }).catch((err) => {
32889
31438
  deps.log(
32890
- `[Proxy and log service] preview proxy failed: ${err instanceof Error ? err.message : String(err)}`
31439
+ `[Proxy and log service] Preview proxy failed: ${err instanceof Error ? err.message : String(err)}`
32891
31440
  );
32892
31441
  sendWsMessage(deps.ws, { type: "proxy_result", id: pr.id, error: String(err) });
32893
31442
  });
@@ -32924,6 +31473,7 @@ function tryConsumeBinaryProxyBody(raw, deps) {
32924
31473
  }
32925
31474
 
32926
31475
  // src/firehose/connect-firehose.ts
31476
+ var FIREHOSE_CLIENT_PING_MS = 25e3;
32927
31477
  function connectFirehose(options) {
32928
31478
  const { firehoseServerUrl, workspaceId, bridgeName, proxyPorts, log: log2, devServerManager, onOpen, onClose } = options;
32929
31479
  const wsUrl = buildFirehoseCliWsUrl(firehoseServerUrl);
@@ -32932,6 +31482,13 @@ function connectFirehose(options) {
32932
31482
  wsOptions.agent = new https2.Agent({ rejectUnauthorized: false });
32933
31483
  }
32934
31484
  const ws = new wrapper_default(wsUrl, wsOptions);
31485
+ let clientPingTimer = null;
31486
+ function clearClientPing() {
31487
+ if (clientPingTimer != null) {
31488
+ clearInterval(clientPingTimer);
31489
+ clientPingTimer = null;
31490
+ }
31491
+ }
32935
31492
  const firehoseSend = (payload) => {
32936
31493
  sendWsMessage(ws, payload);
32937
31494
  };
@@ -32944,6 +31501,15 @@ function connectFirehose(options) {
32944
31501
  startStreamingProxy: (pr) => startStreamingProxy(ws, log2, pr)
32945
31502
  };
32946
31503
  ws.on("open", () => {
31504
+ clearClientPing();
31505
+ clientPingTimer = setInterval(() => {
31506
+ if (ws.readyState === wrapper_default.OPEN) {
31507
+ try {
31508
+ ws.ping();
31509
+ } catch {
31510
+ }
31511
+ }
31512
+ }, FIREHOSE_CLIENT_PING_MS);
32947
31513
  onOpen?.();
32948
31514
  devServerManager.attachFirehose(firehoseSend);
32949
31515
  sendWsMessage(ws, { type: "identify", workspaceId, bridgeName, proxyPorts });
@@ -32961,35 +31527,30 @@ function connectFirehose(options) {
32961
31527
  });
32962
31528
  });
32963
31529
  ws.on("close", (code, reason) => {
31530
+ clearClientPing();
32964
31531
  devServerManager.detachFirehose();
32965
- log2(
32966
- formatWebSocketClose(
32967
- "[Proxy and log service]",
32968
- code,
32969
- typeof reason === "string" ? reason : reason.toString(),
32970
- "reconnects automatically if the bridge service stays connected"
32971
- )
32972
- );
32973
- onClose?.();
31532
+ const reasonStr = typeof reason === "string" ? reason : reason.toString();
31533
+ onClose?.(code, reasonStr);
32974
31534
  });
32975
31535
  ws.on("error", (err) => {
31536
+ clearClientPing();
32976
31537
  log2(`[Proxy and log service] WebSocket error: ${err.message}`);
32977
31538
  });
32978
31539
  return {
32979
31540
  close() {
31541
+ clearClientPing();
32980
31542
  devServerManager.detachFirehose();
32981
31543
  try {
32982
31544
  ws.removeAllListeners();
32983
31545
  ws.close();
32984
31546
  } catch {
32985
31547
  }
32986
- }
31548
+ },
31549
+ isConnected: () => ws.readyState === wrapper_default.OPEN
32987
31550
  };
32988
31551
  }
32989
31552
 
32990
31553
  // src/bridge/connection/create-bridge-identified-handler.ts
32991
- var FH_RECONNECT_BASE_MS = 2e3;
32992
- var FH_RECONNECT_MAX_MS = 3e4;
32993
31554
  function createOnBridgeIdentified(opts) {
32994
31555
  const { sessionWorktreeManager, devServerManager, firehoseServerUrl, workspaceId, state, logFn } = opts;
32995
31556
  function clearFirehoseReconnectTimer() {
@@ -32998,6 +31559,14 @@ function createOnBridgeIdentified(opts) {
32998
31559
  state.firehoseReconnectTimeout = null;
32999
31560
  }
33000
31561
  }
31562
+ function firehoseCtx() {
31563
+ return {
31564
+ closedByUser: state.closedByUser,
31565
+ currentWs: state.currentWs,
31566
+ firehoseHandle: state.firehoseHandle,
31567
+ firehoseQuiet: state.firehoseQuiet
31568
+ };
31569
+ }
33001
31570
  function attachFirehose(params) {
33002
31571
  state.lastFirehoseParams = params;
33003
31572
  clearFirehoseReconnectTimer();
@@ -33016,37 +31585,52 @@ function createOnBridgeIdentified(opts) {
33016
31585
  devServerManager,
33017
31586
  onOpen: () => {
33018
31587
  if (myGen !== state.firehoseGeneration) return;
31588
+ clearFirehoseReconnectQuietOnOpen({ firehoseQuiet: state.firehoseQuiet }, logFn);
31589
+ const logOpenAsFirehoseReconnect = state.firehoseReconnectAttempt > 0;
33019
31590
  state.firehoseReconnectAttempt = 0;
31591
+ if (logOpenAsFirehoseReconnect) {
31592
+ logFn("[Proxy and log service] reconnected.");
31593
+ }
33020
31594
  },
33021
- onClose: () => {
31595
+ onClose: (code, reason) => {
33022
31596
  if (myGen !== state.firehoseGeneration) return;
33023
31597
  state.firehoseHandle = null;
33024
31598
  if (state.closedByUser) return;
33025
31599
  const main = state.currentWs;
33026
31600
  if (!main || main.readyState !== wrapper_default.OPEN) {
33027
- logFn("[Proxy and log service] not reconnecting: bridge service socket is not open");
31601
+ logFn(
31602
+ `${PROXY_AND_LOG_SERVICE_LABEL} Not reconnecting preview and log stream: main bridge connection is not open.`
31603
+ );
33028
31604
  return;
33029
31605
  }
31606
+ beginFirehoseDeferredDisconnect(firehoseCtx(), code, reason, logFn);
33030
31607
  clearFirehoseReconnectTimer();
33031
- const delay2 = Math.min(
33032
- FH_RECONNECT_BASE_MS * 2 ** state.firehoseReconnectAttempt,
33033
- FH_RECONNECT_MAX_MS
33034
- );
31608
+ const delay2 = reconnectDelayMs(state.firehoseReconnectAttempt);
33035
31609
  state.firehoseReconnectAttempt += 1;
33036
- logFn(
33037
- `[Proxy and log service] reconnect attempt ${state.firehoseReconnectAttempt} in ${delay2 / 1e3}s\u2026`
31610
+ logNextReconnectAttempt(
31611
+ logFn,
31612
+ PROXY_AND_LOG_SERVICE_LABEL,
31613
+ state.firehoseQuiet,
31614
+ delay2,
31615
+ state.firehoseReconnectAttempt
33038
31616
  );
33039
31617
  state.firehoseReconnectTimeout = setTimeout(() => {
33040
31618
  state.firehoseReconnectTimeout = null;
33041
31619
  if (state.closedByUser) return;
33042
31620
  const w = state.currentWs;
33043
31621
  if (!w || w.readyState !== wrapper_default.OPEN) {
33044
- logFn("[Proxy and log service] reconnect skipped: bridge service socket closed");
31622
+ if (state.firehoseQuiet.verboseLogs) {
31623
+ logFn(
31624
+ `${PROXY_AND_LOG_SERVICE_LABEL} Reconnect skipped: main bridge connection closed before preview stream could reconnect.`
31625
+ );
31626
+ }
33045
31627
  return;
33046
31628
  }
33047
31629
  const p = state.lastFirehoseParams;
33048
31630
  if (!p) {
33049
- logFn("[Proxy and log service] reconnect skipped: no stored connection params");
31631
+ if (state.firehoseQuiet.verboseLogs) {
31632
+ logFn(`${PROXY_AND_LOG_SERVICE_LABEL} Reconnect skipped: no stored connection parameters.`);
31633
+ }
33050
31634
  return;
33051
31635
  }
33052
31636
  attachFirehose(p);
@@ -33129,10 +31713,12 @@ function createSendLocalSkillsReport(getWs, logFn) {
33129
31713
  try {
33130
31714
  const socket = getWs();
33131
31715
  if (!socket || socket.readyState !== wrapper_default.OPEN) return;
33132
- const skills2 = discoverLocalSkills(process.cwd());
31716
+ const skills2 = discoverLocalSkills(getBridgeWorkspaceDirectory());
33133
31717
  socket.send(JSON.stringify({ type: "local_skills", skills: skills2 }));
33134
31718
  } catch (e) {
33135
- logFn(`[Bridge service] local_skills report failed: ${e instanceof Error ? e.message : String(e)}`);
31719
+ logFn(
31720
+ `[Bridge service] Local skills report failed: ${e instanceof Error ? e.message : String(e)}`
31721
+ );
33136
31722
  }
33137
31723
  };
33138
31724
  }
@@ -33145,12 +31731,15 @@ function createReportAutoDetectedAgents(getWs, logFn) {
33145
31731
  sendWsMessage(socket, { type: "auto_detected_agents_report", agentTypes: types });
33146
31732
  }
33147
31733
  } catch (e) {
33148
- logFn(`[Bridge service] auto_detected_agents report failed: ${e instanceof Error ? e.message : String(e)}`);
31734
+ logFn(
31735
+ `[Bridge service] Auto-detected agents report failed: ${e instanceof Error ? e.message : String(e)}`
31736
+ );
33149
31737
  }
33150
31738
  };
33151
31739
  }
33152
31740
 
33153
31741
  // src/bridge/connection/create-bridge-connection.ts
31742
+ var BRIDGE_CLIENT_PING_MS = 25e3;
33154
31743
  async function createBridgeConnection(options) {
33155
31744
  const { apiUrl, workspaceId, justAuthenticated, onAuthInvalid, persistTokens } = options;
33156
31745
  const firehoseServerUrl = options.firehoseServerUrl ?? options.proxyServerUrl;
@@ -33161,13 +31750,16 @@ async function createBridgeConnection(options) {
33161
31750
  const state = {
33162
31751
  closedByUser: false,
33163
31752
  reconnectAttempt: 0,
31753
+ logBridgeOpenAsReconnect: false,
33164
31754
  reconnectTimeout: null,
33165
31755
  currentWs: null,
31756
+ mainQuiet: createEmptyReconnectQuietSlot(),
33166
31757
  firehoseHandle: null,
33167
31758
  lastFirehoseParams: null,
33168
31759
  firehoseReconnectTimeout: null,
33169
31760
  firehoseReconnectAttempt: 0,
33170
- firehoseGeneration: 0
31761
+ firehoseGeneration: 0,
31762
+ firehoseQuiet: createEmptyReconnectQuietSlot()
33171
31763
  };
33172
31764
  const worktreesRootAbs = options.worktreesRootAbs ?? defaultWorktreesRootAbs();
33173
31765
  const sessionWorktreeManager = new SessionWorktreeManager({
@@ -33179,7 +31771,7 @@ async function createBridgeConnection(options) {
33179
31771
  function getWs() {
33180
31772
  return state.currentWs;
33181
31773
  }
33182
- const devServerManager = new DevServerManager({ getWs, log: logFn });
31774
+ const devServerManager = new DevServerManager({ getWs, log: logFn, getBridgeCwd: getBridgeWorkspaceDirectory });
33183
31775
  const onBridgeIdentified = createOnBridgeIdentified({
33184
31776
  sessionWorktreeManager,
33185
31777
  devServerManager,
@@ -33191,7 +31783,13 @@ async function createBridgeConnection(options) {
33191
31783
  const sendLocalSkillsReport = createSendLocalSkillsReport(getWs, logFn);
33192
31784
  const reportAutoDetectedAgents = createReportAutoDetectedAgents(getWs, logFn);
33193
31785
  function handleOpen() {
31786
+ const logOpenAsPostRefreshReconnect = state.logBridgeOpenAsReconnect;
31787
+ clearMainBridgeReconnectQuietOnOpen(state, logFn);
33194
31788
  state.reconnectAttempt = 0;
31789
+ state.logBridgeOpenAsReconnect = false;
31790
+ if (logOpenAsPostRefreshReconnect) {
31791
+ logFn("[Bridge service] reconnected.");
31792
+ }
33195
31793
  const socket = getWs();
33196
31794
  if (socket) {
33197
31795
  sendWsMessage(socket, { type: "identify", role: "cli" });
@@ -33208,11 +31806,9 @@ async function createBridgeConnection(options) {
33208
31806
  state.currentWs = null;
33209
31807
  if (was) was.removeAllListeners();
33210
31808
  const willReconnect = !state.closedByUser;
33211
- logFn(
33212
- formatWebSocketClose("[Bridge service]", code, reason, willReconnect ? "will schedule reconnect" : "not reconnecting (CLI shutting down)")
33213
- );
31809
+ beginMainBridgeDeferredDisconnect(state, code, reason, logFn, willReconnect);
33214
31810
  if (willReconnect) {
33215
- scheduleReconnect(state, connect, logFn);
31811
+ scheduleMainBridgeReconnect(state, connect, logFn);
33216
31812
  }
33217
31813
  }
33218
31814
  const messageDeps = {
@@ -33227,6 +31823,10 @@ async function createBridgeConnection(options) {
33227
31823
  };
33228
31824
  function connect() {
33229
31825
  if (state.closedByUser) return;
31826
+ if (state.reconnectTimeout != null) {
31827
+ clearTimeout(state.reconnectTimeout);
31828
+ state.reconnectTimeout = null;
31829
+ }
33230
31830
  const prev = state.currentWs;
33231
31831
  if (prev) {
33232
31832
  prev.removeAllListeners();
@@ -33239,6 +31839,7 @@ async function createBridgeConnection(options) {
33239
31839
  const url2 = buildBridgeUrl(apiUrl, workspaceId, accessToken);
33240
31840
  state.currentWs = createWsBridge({
33241
31841
  url: url2,
31842
+ clientPingIntervalMs: BRIDGE_CLIENT_PING_MS,
33242
31843
  onAuthInvalid: () => {
33243
31844
  if (authRefreshInFlight) return;
33244
31845
  void (async () => {
@@ -33250,8 +31851,9 @@ async function createBridgeConnection(options) {
33250
31851
  accessToken = next.token;
33251
31852
  refreshTok = next.refreshToken;
33252
31853
  persistTokens?.({ token: accessToken, refreshToken: refreshTok });
33253
- logFn("[Bridge service] access token refreshed; reconnecting\u2026");
31854
+ logFn("[Bridge service] Access token refreshed; reconnecting\u2026");
33254
31855
  state.reconnectAttempt = 0;
31856
+ state.logBridgeOpenAsReconnect = true;
33255
31857
  authRefreshInFlight = false;
33256
31858
  connect();
33257
31859
  return;
@@ -33274,7 +31876,7 @@ async function createBridgeConnection(options) {
33274
31876
  });
33275
31877
  }
33276
31878
  connect();
33277
- const stopFileIndexWatcher = startFileIndexWatcher(process.cwd());
31879
+ const stopFileIndexWatcher = startFileIndexWatcher(getBridgeWorkspaceDirectory());
33278
31880
  return {
33279
31881
  close: async () => {
33280
31882
  stopFileIndexWatcher();
@@ -33347,7 +31949,7 @@ async function runBridge(options) {
33347
31949
  });
33348
31950
  },
33349
31951
  onAuthInvalid: () => {
33350
- log("[Bridge service] token invalid or revoked; re-authenticating\u2026");
31952
+ log("[Bridge service] Access token invalid or revoked; re-authenticating\u2026");
33351
31953
  clearConfigForApi(apiUrl);
33352
31954
  void handle.close().then(() => {
33353
31955
  void runBridge({ apiUrl, firehoseServerUrl, worktreesRootAbs });
@@ -33381,9 +31983,4 @@ export {
33381
31983
  runPendingAuth,
33382
31984
  sendWsMessage
33383
31985
  };
33384
- /*! Bundled license information:
33385
-
33386
- chokidar/esm/index.js:
33387
- (*! chokidar - MIT License (c) 2012 Paul Miller (paulmillr.com) *)
33388
- */
33389
31986
  //# sourceMappingURL=index.js.map