@buildautomaton/cli 0.1.4 → 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/cli.js +815 -542
- package/dist/cli.js.map +4 -4
- package/dist/index.js +810 -537
- package/dist/index.js.map +4 -4
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -974,7 +974,7 @@ var require_command = __commonJS({
|
|
|
974
974
|
var EventEmitter2 = __require("node:events").EventEmitter;
|
|
975
975
|
var childProcess = __require("node:child_process");
|
|
976
976
|
var path25 = __require("node:path");
|
|
977
|
-
var
|
|
977
|
+
var fs22 = __require("node:fs");
|
|
978
978
|
var process2 = __require("node:process");
|
|
979
979
|
var { Argument: Argument2, humanReadableArgName } = require_argument();
|
|
980
980
|
var { CommanderError: CommanderError2 } = require_error();
|
|
@@ -1907,10 +1907,10 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1907
1907
|
const sourceExt = [".js", ".ts", ".tsx", ".mjs", ".cjs"];
|
|
1908
1908
|
function findFile(baseDir, baseName) {
|
|
1909
1909
|
const localBin = path25.resolve(baseDir, baseName);
|
|
1910
|
-
if (
|
|
1910
|
+
if (fs22.existsSync(localBin)) return localBin;
|
|
1911
1911
|
if (sourceExt.includes(path25.extname(baseName))) return void 0;
|
|
1912
1912
|
const foundExt = sourceExt.find(
|
|
1913
|
-
(ext) =>
|
|
1913
|
+
(ext) => fs22.existsSync(`${localBin}${ext}`)
|
|
1914
1914
|
);
|
|
1915
1915
|
if (foundExt) return `${localBin}${foundExt}`;
|
|
1916
1916
|
return void 0;
|
|
@@ -1922,7 +1922,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1922
1922
|
if (this._scriptPath) {
|
|
1923
1923
|
let resolvedScriptPath;
|
|
1924
1924
|
try {
|
|
1925
|
-
resolvedScriptPath =
|
|
1925
|
+
resolvedScriptPath = fs22.realpathSync(this._scriptPath);
|
|
1926
1926
|
} catch (err) {
|
|
1927
1927
|
resolvedScriptPath = this._scriptPath;
|
|
1928
1928
|
}
|
|
@@ -25047,7 +25047,7 @@ var init_acp = __esm({
|
|
|
25047
25047
|
});
|
|
25048
25048
|
|
|
25049
25049
|
// src/cli.ts
|
|
25050
|
-
import * as
|
|
25050
|
+
import * as fs21 from "node:fs";
|
|
25051
25051
|
import * as path24 from "node:path";
|
|
25052
25052
|
|
|
25053
25053
|
// ../../node_modules/.pnpm/commander@12.1.0/node_modules/commander/esm.mjs
|
|
@@ -25118,7 +25118,7 @@ function writeConfigForApi(apiUrl, auth) {
|
|
|
25118
25118
|
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
25119
25119
|
fs.writeFileSync(p, JSON.stringify(next, null, 2), "utf8");
|
|
25120
25120
|
} catch (e) {
|
|
25121
|
-
console.error("Could not save
|
|
25121
|
+
console.error("Could not save authentication config:", e);
|
|
25122
25122
|
}
|
|
25123
25123
|
}
|
|
25124
25124
|
function clearConfigForApi(apiUrl) {
|
|
@@ -25145,6 +25145,19 @@ function clearConfigForApi(apiUrl) {
|
|
|
25145
25145
|
}
|
|
25146
25146
|
}
|
|
25147
25147
|
|
|
25148
|
+
// src/files/cwd/bridge-workspace-directory.ts
|
|
25149
|
+
import * as path2 from "node:path";
|
|
25150
|
+
var bridgeWorkspaceDirectory = null;
|
|
25151
|
+
function initBridgeWorkspaceDirectory() {
|
|
25152
|
+
bridgeWorkspaceDirectory = path2.resolve(process.cwd());
|
|
25153
|
+
}
|
|
25154
|
+
function getBridgeWorkspaceDirectory() {
|
|
25155
|
+
if (bridgeWorkspaceDirectory == null) {
|
|
25156
|
+
bridgeWorkspaceDirectory = path2.resolve(process.cwd());
|
|
25157
|
+
}
|
|
25158
|
+
return bridgeWorkspaceDirectory;
|
|
25159
|
+
}
|
|
25160
|
+
|
|
25148
25161
|
// src/log.ts
|
|
25149
25162
|
function log(line) {
|
|
25150
25163
|
const time3 = (/* @__PURE__ */ new Date()).toISOString().slice(11, 19);
|
|
@@ -25156,9 +25169,6 @@ function logImmediate(line) {
|
|
|
25156
25169
|
`);
|
|
25157
25170
|
}
|
|
25158
25171
|
|
|
25159
|
-
// src/bridge/connection/create-ws-bridge.ts
|
|
25160
|
-
import https from "node:https";
|
|
25161
|
-
|
|
25162
25172
|
// ../../node_modules/.pnpm/ws@8.19.0/node_modules/ws/wrapper.mjs
|
|
25163
25173
|
var import_stream = __toESM(require_stream(), 1);
|
|
25164
25174
|
var import_receiver = __toESM(require_receiver(), 1);
|
|
@@ -25168,15 +25178,23 @@ var import_websocket_server = __toESM(require_websocket_server(), 1);
|
|
|
25168
25178
|
var wrapper_default = import_websocket.default;
|
|
25169
25179
|
|
|
25170
25180
|
// src/bridge/connection/create-ws-bridge.ts
|
|
25181
|
+
import https from "node:https";
|
|
25171
25182
|
var BRIDGE_AUTH_ERROR_HEADER = "x-bridge-auth-error";
|
|
25172
25183
|
var BRIDGE_AUTH_ERROR_TOKEN_INVALID = "token_invalid";
|
|
25173
25184
|
function createWsBridge(options) {
|
|
25174
|
-
const { url: url2, onMessage, onOpen, onClose, onError: onError2, onAuthInvalid } = options;
|
|
25185
|
+
const { url: url2, onMessage, onOpen, onClose, onError: onError2, onAuthInvalid, clientPingIntervalMs } = options;
|
|
25175
25186
|
const wsOptions = {};
|
|
25176
25187
|
if (url2.startsWith("wss://")) {
|
|
25177
25188
|
wsOptions.agent = new https.Agent({ rejectUnauthorized: false });
|
|
25178
25189
|
}
|
|
25179
25190
|
const ws = new wrapper_default(url2, wsOptions);
|
|
25191
|
+
let clientPingTimer = null;
|
|
25192
|
+
function clearClientPing() {
|
|
25193
|
+
if (clientPingTimer != null) {
|
|
25194
|
+
clearInterval(clientPingTimer);
|
|
25195
|
+
clientPingTimer = null;
|
|
25196
|
+
}
|
|
25197
|
+
}
|
|
25180
25198
|
ws.on("unexpected-response", (request, response) => {
|
|
25181
25199
|
const status = response?.statusCode ?? 0;
|
|
25182
25200
|
const headers = response?.headers ?? {};
|
|
@@ -25186,6 +25204,17 @@ function createWsBridge(options) {
|
|
|
25186
25204
|
}
|
|
25187
25205
|
});
|
|
25188
25206
|
ws.on("open", () => {
|
|
25207
|
+
clearClientPing();
|
|
25208
|
+
if (clientPingIntervalMs != null && clientPingIntervalMs > 0) {
|
|
25209
|
+
clientPingTimer = setInterval(() => {
|
|
25210
|
+
if (ws.readyState === wrapper_default.OPEN) {
|
|
25211
|
+
try {
|
|
25212
|
+
ws.ping();
|
|
25213
|
+
} catch {
|
|
25214
|
+
}
|
|
25215
|
+
}
|
|
25216
|
+
}, clientPingIntervalMs);
|
|
25217
|
+
}
|
|
25189
25218
|
onOpen?.();
|
|
25190
25219
|
});
|
|
25191
25220
|
ws.on("message", (raw) => {
|
|
@@ -25205,9 +25234,11 @@ function createWsBridge(options) {
|
|
|
25205
25234
|
}
|
|
25206
25235
|
});
|
|
25207
25236
|
ws.on("close", (code, reason) => {
|
|
25237
|
+
clearClientPing();
|
|
25208
25238
|
onClose?.(code, reason.toString());
|
|
25209
25239
|
});
|
|
25210
25240
|
ws.on("error", (err) => {
|
|
25241
|
+
clearClientPing();
|
|
25211
25242
|
onError2?.(err);
|
|
25212
25243
|
});
|
|
25213
25244
|
return ws;
|
|
@@ -25253,6 +25284,28 @@ function openBrowser(connectionId, initialWorkspaceId, preferredBridgeName, apiU
|
|
|
25253
25284
|
}
|
|
25254
25285
|
}
|
|
25255
25286
|
|
|
25287
|
+
// src/bridge/connection/reconnect/constants.ts
|
|
25288
|
+
var RECONNECT_FIRST_MS = 100;
|
|
25289
|
+
var RECONNECT_MAX_MS = 3e4;
|
|
25290
|
+
var RECONNECT_QUIET_MS = 2e3;
|
|
25291
|
+
function reconnectDelayMs(attemptBeforeIncrement) {
|
|
25292
|
+
return Math.min(RECONNECT_FIRST_MS * 2 ** attemptBeforeIncrement, RECONNECT_MAX_MS);
|
|
25293
|
+
}
|
|
25294
|
+
|
|
25295
|
+
// src/bridge/connection/reconnect/format-reconnect-delay-for-log.ts
|
|
25296
|
+
function formatReconnectDelayForLog(delayMs) {
|
|
25297
|
+
if (delayMs < 1e3) return `${delayMs}ms`;
|
|
25298
|
+
const s = delayMs / 1e3;
|
|
25299
|
+
return Number.isInteger(s) ? `${s}s` : `${s.toFixed(1)}s`;
|
|
25300
|
+
}
|
|
25301
|
+
|
|
25302
|
+
// src/bridge/connection/reconnect/log-next-reconnect-attempt.ts
|
|
25303
|
+
function logNextReconnectAttempt(log2, serviceLabel, quiet, delayMs, attempt) {
|
|
25304
|
+
if (!quiet.verboseLogs) return;
|
|
25305
|
+
const delayLabel = formatReconnectDelayForLog(delayMs);
|
|
25306
|
+
log2(`${serviceLabel} Next connection attempt in ${delayLabel} (attempt ${attempt}).`);
|
|
25307
|
+
}
|
|
25308
|
+
|
|
25256
25309
|
// src/bridge/connection/ws-close-diagnostics.ts
|
|
25257
25310
|
function describeWebSocketCloseCode(code) {
|
|
25258
25311
|
const known = {
|
|
@@ -25281,11 +25334,122 @@ function formatWebSocketClose(label, code, reason, extra) {
|
|
|
25281
25334
|
const r = reason.trim();
|
|
25282
25335
|
const reasonPart = r ? ` reason="${r}"` : "";
|
|
25283
25336
|
const extraPart = extra ? ` ${extra}` : "";
|
|
25284
|
-
return `${label}
|
|
25337
|
+
return `${label} Disconnected: code=${code} (${describeWebSocketCloseCode(code)})${reasonPart}${extraPart}`;
|
|
25338
|
+
}
|
|
25339
|
+
|
|
25340
|
+
// src/bridge/connection/reconnect/reconnect-quiet-slot.ts
|
|
25341
|
+
function createEmptyReconnectQuietSlot() {
|
|
25342
|
+
return { timer: null, verboseLogs: false, pendingCloseLog: null };
|
|
25343
|
+
}
|
|
25344
|
+
function clearReconnectQuietTimer(quiet) {
|
|
25345
|
+
if (quiet.timer != null) {
|
|
25346
|
+
clearTimeout(quiet.timer);
|
|
25347
|
+
quiet.timer = null;
|
|
25348
|
+
}
|
|
25349
|
+
}
|
|
25350
|
+
function clearReconnectQuietOnSuccessfulConnection(quiet, log2, reconnectedMessage) {
|
|
25351
|
+
clearReconnectQuietTimer(quiet);
|
|
25352
|
+
quiet.pendingCloseLog = null;
|
|
25353
|
+
if (quiet.verboseLogs) {
|
|
25354
|
+
log2(reconnectedMessage);
|
|
25355
|
+
quiet.verboseLogs = false;
|
|
25356
|
+
}
|
|
25357
|
+
}
|
|
25358
|
+
function beginDeferredDisconnectForReconnect(options) {
|
|
25359
|
+
const {
|
|
25360
|
+
isClosedByUser,
|
|
25361
|
+
quiet,
|
|
25362
|
+
code,
|
|
25363
|
+
reason,
|
|
25364
|
+
willReconnect,
|
|
25365
|
+
log: log2,
|
|
25366
|
+
serviceLabel,
|
|
25367
|
+
shutdownDetail,
|
|
25368
|
+
reconnectingDetail,
|
|
25369
|
+
quietMs = RECONNECT_QUIET_MS,
|
|
25370
|
+
shouldAbortQuietWindow
|
|
25371
|
+
} = options;
|
|
25372
|
+
if (!willReconnect) {
|
|
25373
|
+
log2(formatWebSocketClose(serviceLabel, code, reason, shutdownDetail));
|
|
25374
|
+
return;
|
|
25375
|
+
}
|
|
25376
|
+
quiet.pendingCloseLog = { code, reason };
|
|
25377
|
+
if (quiet.timer == null) {
|
|
25378
|
+
quiet.timer = setTimeout(() => {
|
|
25379
|
+
quiet.timer = null;
|
|
25380
|
+
if (isClosedByUser()) return;
|
|
25381
|
+
if (shouldAbortQuietWindow()) return;
|
|
25382
|
+
if (quiet.pendingCloseLog) {
|
|
25383
|
+
const { code: c, reason: r } = quiet.pendingCloseLog;
|
|
25384
|
+
quiet.pendingCloseLog = null;
|
|
25385
|
+
log2(formatWebSocketClose(serviceLabel, c, r, reconnectingDetail));
|
|
25386
|
+
}
|
|
25387
|
+
quiet.verboseLogs = true;
|
|
25388
|
+
}, quietMs);
|
|
25389
|
+
}
|
|
25390
|
+
}
|
|
25391
|
+
|
|
25392
|
+
// src/bridge/connection/reconnect/bridge-main-reconnect.ts
|
|
25393
|
+
function beginMainBridgeDeferredDisconnect(state, code, reason, log2, willReconnect) {
|
|
25394
|
+
beginDeferredDisconnectForReconnect({
|
|
25395
|
+
isClosedByUser: () => state.closedByUser,
|
|
25396
|
+
quiet: state.mainQuiet,
|
|
25397
|
+
code,
|
|
25398
|
+
reason,
|
|
25399
|
+
willReconnect,
|
|
25400
|
+
log: log2,
|
|
25401
|
+
serviceLabel: "[Bridge service]",
|
|
25402
|
+
shutdownDetail: "Not reconnecting (shutting down).",
|
|
25403
|
+
reconnectingDetail: "Reconnecting\u2026",
|
|
25404
|
+
shouldAbortQuietWindow: () => {
|
|
25405
|
+
const w = state.currentWs;
|
|
25406
|
+
return w != null && w.readyState === wrapper_default.OPEN;
|
|
25407
|
+
}
|
|
25408
|
+
});
|
|
25409
|
+
}
|
|
25410
|
+
function clearMainBridgeReconnectQuietOnOpen(state, log2) {
|
|
25411
|
+
clearReconnectQuietOnSuccessfulConnection(state.mainQuiet, log2, "[Bridge service] Reconnected.");
|
|
25412
|
+
}
|
|
25413
|
+
function scheduleMainBridgeReconnect(state, connect, log2) {
|
|
25414
|
+
if (state.closedByUser || state.currentWs != null) return;
|
|
25415
|
+
const delay2 = reconnectDelayMs(state.reconnectAttempt);
|
|
25416
|
+
state.reconnectAttempt += 1;
|
|
25417
|
+
logNextReconnectAttempt(log2, "[Bridge service]", state.mainQuiet, delay2, state.reconnectAttempt);
|
|
25418
|
+
state.reconnectTimeout = setTimeout(() => {
|
|
25419
|
+
state.reconnectTimeout = null;
|
|
25420
|
+
connect();
|
|
25421
|
+
}, delay2);
|
|
25422
|
+
}
|
|
25423
|
+
|
|
25424
|
+
// src/bridge/connection/reconnect/firehose-reconnect.ts
|
|
25425
|
+
var PROXY_AND_LOG_SERVICE_LABEL = "[Proxy and log service]";
|
|
25426
|
+
function beginFirehoseDeferredDisconnect(ctx, code, reason, log2) {
|
|
25427
|
+
beginDeferredDisconnectForReconnect({
|
|
25428
|
+
isClosedByUser: () => ctx.closedByUser,
|
|
25429
|
+
quiet: ctx.firehoseQuiet,
|
|
25430
|
+
code,
|
|
25431
|
+
reason,
|
|
25432
|
+
willReconnect: true,
|
|
25433
|
+
log: log2,
|
|
25434
|
+
serviceLabel: PROXY_AND_LOG_SERVICE_LABEL,
|
|
25435
|
+
shutdownDetail: "Not reconnecting (shutting down).",
|
|
25436
|
+
reconnectingDetail: "Reconnecting\u2026",
|
|
25437
|
+
shouldAbortQuietWindow: () => {
|
|
25438
|
+
const w = ctx.currentWs;
|
|
25439
|
+
if (!w || w.readyState !== wrapper_default.OPEN) return true;
|
|
25440
|
+
return ctx.firehoseHandle?.isConnected() ?? false;
|
|
25441
|
+
}
|
|
25442
|
+
});
|
|
25443
|
+
}
|
|
25444
|
+
function clearFirehoseReconnectQuietOnOpen(ctx, log2) {
|
|
25445
|
+
clearReconnectQuietOnSuccessfulConnection(
|
|
25446
|
+
ctx.firehoseQuiet,
|
|
25447
|
+
log2,
|
|
25448
|
+
`${PROXY_AND_LOG_SERVICE_LABEL} Reconnected.`
|
|
25449
|
+
);
|
|
25285
25450
|
}
|
|
25286
25451
|
|
|
25287
25452
|
// src/auth/run-pending-auth.ts
|
|
25288
|
-
var PENDING_RECONNECT_MS = 2e3;
|
|
25289
25453
|
var PENDING_KEEPALIVE_MS = 25e3;
|
|
25290
25454
|
var BROWSER_OPEN_FALLBACK_MS = 4e3;
|
|
25291
25455
|
function buildPendingBridgeUrl(apiUrl, connectionId) {
|
|
@@ -25307,7 +25471,32 @@ function runPendingAuth(options) {
|
|
|
25307
25471
|
const authPromise = new Promise((resolve15) => {
|
|
25308
25472
|
resolveAuth = resolve15;
|
|
25309
25473
|
});
|
|
25474
|
+
let reconnectAttempt = 0;
|
|
25475
|
+
const signInQuiet = createEmptyReconnectQuietSlot();
|
|
25476
|
+
function clearQuietOnOpen() {
|
|
25477
|
+
clearReconnectQuietOnSuccessfulConnection(
|
|
25478
|
+
signInQuiet,
|
|
25479
|
+
logFn,
|
|
25480
|
+
"[Bridge service] Sign-in connection restored."
|
|
25481
|
+
);
|
|
25482
|
+
reconnectAttempt = 0;
|
|
25483
|
+
}
|
|
25484
|
+
function beginDeferredPendingCloseLog(code, reason) {
|
|
25485
|
+
beginDeferredDisconnectForReconnect({
|
|
25486
|
+
isClosedByUser: () => resolved,
|
|
25487
|
+
quiet: signInQuiet,
|
|
25488
|
+
code,
|
|
25489
|
+
reason,
|
|
25490
|
+
willReconnect: true,
|
|
25491
|
+
log: logFn,
|
|
25492
|
+
serviceLabel: "[Bridge service]",
|
|
25493
|
+
shutdownDetail: "Not reconnecting (shutting down).",
|
|
25494
|
+
reconnectingDetail: "Waiting for browser sign-in; reconnecting\u2026",
|
|
25495
|
+
shouldAbortQuietWindow: () => ws != null && ws.readyState === wrapper_default.OPEN
|
|
25496
|
+
});
|
|
25497
|
+
}
|
|
25310
25498
|
function cleanup() {
|
|
25499
|
+
clearReconnectQuietTimer(signInQuiet);
|
|
25311
25500
|
if (reconnectTimeout) {
|
|
25312
25501
|
clearTimeout(reconnectTimeout);
|
|
25313
25502
|
reconnectTimeout = null;
|
|
@@ -25331,6 +25520,7 @@ function runPendingAuth(options) {
|
|
|
25331
25520
|
ws = createWsBridge({
|
|
25332
25521
|
url: url2,
|
|
25333
25522
|
onOpen: () => {
|
|
25523
|
+
clearQuietOnOpen();
|
|
25334
25524
|
sendWsMessage(ws, { type: "identify", role: "cli" });
|
|
25335
25525
|
keepaliveInterval = setInterval(() => {
|
|
25336
25526
|
if (resolved || !ws || ws.readyState !== 1) return;
|
|
@@ -25351,15 +25541,21 @@ function runPendingAuth(options) {
|
|
|
25351
25541
|
keepaliveInterval = null;
|
|
25352
25542
|
}
|
|
25353
25543
|
if (resolved) return;
|
|
25354
|
-
|
|
25355
|
-
|
|
25356
|
-
|
|
25544
|
+
beginDeferredPendingCloseLog(code, reason);
|
|
25545
|
+
const delay2 = reconnectDelayMs(reconnectAttempt);
|
|
25546
|
+
reconnectAttempt += 1;
|
|
25547
|
+
if (signInQuiet.verboseLogs) {
|
|
25548
|
+
const delayLabel = formatReconnectDelayForLog(delay2);
|
|
25549
|
+
logFn(
|
|
25550
|
+
`[Bridge service] Next sign-in connection attempt in ${delayLabel} (attempt ${reconnectAttempt}).`
|
|
25551
|
+
);
|
|
25552
|
+
}
|
|
25357
25553
|
reconnectTimeout = setTimeout(() => {
|
|
25358
25554
|
reconnectTimeout = null;
|
|
25359
25555
|
connect();
|
|
25360
|
-
},
|
|
25556
|
+
}, delay2);
|
|
25361
25557
|
},
|
|
25362
|
-
onError: (err) => logFn(`[Bridge service] WebSocket error
|
|
25558
|
+
onError: (err) => logFn(`[Bridge service] WebSocket error while waiting for sign-in: ${err.message}`),
|
|
25363
25559
|
onMessage: (data) => {
|
|
25364
25560
|
const msg = data;
|
|
25365
25561
|
if (msg.type === "auth_token" && typeof msg.token === "string") {
|
|
@@ -25400,7 +25596,7 @@ function buildBridgeUrl(apiUrl, workspaceId, authToken) {
|
|
|
25400
25596
|
|
|
25401
25597
|
// src/git/discover-repos.ts
|
|
25402
25598
|
import * as fs2 from "node:fs";
|
|
25403
|
-
import * as
|
|
25599
|
+
import * as path3 from "node:path";
|
|
25404
25600
|
|
|
25405
25601
|
// ../../node_modules/.pnpm/simple-git@3.32.3/node_modules/simple-git/dist/esm/index.js
|
|
25406
25602
|
var import_file_exists = __toESM(require_dist(), 1);
|
|
@@ -26662,8 +26858,8 @@ var init_git_executor_chain = __esm2({
|
|
|
26662
26858
|
get cwd() {
|
|
26663
26859
|
return this._cwd || this._executor.cwd;
|
|
26664
26860
|
}
|
|
26665
|
-
set cwd(
|
|
26666
|
-
this._cwd =
|
|
26861
|
+
set cwd(cwd) {
|
|
26862
|
+
this._cwd = cwd;
|
|
26667
26863
|
}
|
|
26668
26864
|
get env() {
|
|
26669
26865
|
return this._executor.env;
|
|
@@ -26854,8 +27050,8 @@ var init_git_executor = __esm2({
|
|
|
26854
27050
|
"use strict";
|
|
26855
27051
|
init_git_executor_chain();
|
|
26856
27052
|
GitExecutor = class {
|
|
26857
|
-
constructor(
|
|
26858
|
-
this.cwd =
|
|
27053
|
+
constructor(cwd, _scheduler, _plugins) {
|
|
27054
|
+
this.cwd = cwd;
|
|
26859
27055
|
this._scheduler = _scheduler;
|
|
26860
27056
|
this._plugins = _plugins;
|
|
26861
27057
|
this._chain = new GitExecutorChain(this, this._scheduler, this._plugins);
|
|
@@ -29985,9 +30181,9 @@ async function isGitRepoDirectory(dirPath) {
|
|
|
29985
30181
|
}
|
|
29986
30182
|
|
|
29987
30183
|
// src/git/discover-repos.ts
|
|
29988
|
-
async function discoverGitRepos(
|
|
30184
|
+
async function discoverGitRepos(cwd = getBridgeWorkspaceDirectory()) {
|
|
29989
30185
|
const result = [];
|
|
29990
|
-
const cwdResolved =
|
|
30186
|
+
const cwdResolved = path3.resolve(cwd);
|
|
29991
30187
|
if (await isGitRepoDirectory(cwdResolved)) {
|
|
29992
30188
|
const remoteUrl = await getRemoteOriginUrl(cwdResolved);
|
|
29993
30189
|
result.push({ absolutePath: cwdResolved, remoteUrl });
|
|
@@ -30000,7 +30196,7 @@ async function discoverGitRepos(cwd3 = process.cwd()) {
|
|
|
30000
30196
|
}
|
|
30001
30197
|
for (const ent of entries) {
|
|
30002
30198
|
if (!ent.isDirectory()) continue;
|
|
30003
|
-
const childPath =
|
|
30199
|
+
const childPath = path3.join(cwdResolved, ent.name);
|
|
30004
30200
|
if (await isGitRepoDirectory(childPath)) {
|
|
30005
30201
|
const remoteUrl = await getRemoteOriginUrl(childPath);
|
|
30006
30202
|
result.push({ absolutePath: childPath, remoteUrl });
|
|
@@ -30009,11 +30205,11 @@ async function discoverGitRepos(cwd3 = process.cwd()) {
|
|
|
30009
30205
|
return result;
|
|
30010
30206
|
}
|
|
30011
30207
|
async function discoverGitReposUnderRoot(rootAbs) {
|
|
30012
|
-
const root =
|
|
30208
|
+
const root = path3.resolve(rootAbs);
|
|
30013
30209
|
const roots = [];
|
|
30014
30210
|
async function walk(dir) {
|
|
30015
30211
|
if (await isGitRepoDirectory(dir)) {
|
|
30016
|
-
roots.push(
|
|
30212
|
+
roots.push(path3.resolve(dir));
|
|
30017
30213
|
return;
|
|
30018
30214
|
}
|
|
30019
30215
|
let entries;
|
|
@@ -30024,7 +30220,7 @@ async function discoverGitReposUnderRoot(rootAbs) {
|
|
|
30024
30220
|
}
|
|
30025
30221
|
for (const ent of entries) {
|
|
30026
30222
|
if (!ent.isDirectory() || ent.name === ".git") continue;
|
|
30027
|
-
await walk(
|
|
30223
|
+
await walk(path3.join(dir, ent.name));
|
|
30028
30224
|
}
|
|
30029
30225
|
}
|
|
30030
30226
|
await walk(root);
|
|
@@ -30051,28 +30247,13 @@ function reportGitRepos(getWs, log2) {
|
|
|
30051
30247
|
}
|
|
30052
30248
|
}
|
|
30053
30249
|
}).catch((err) => {
|
|
30054
|
-
log2(
|
|
30250
|
+
log2(
|
|
30251
|
+
`[Bridge service] Git repository discovery failed: ${err instanceof Error ? err.message : String(err)}`
|
|
30252
|
+
);
|
|
30055
30253
|
});
|
|
30056
30254
|
});
|
|
30057
30255
|
}
|
|
30058
30256
|
|
|
30059
|
-
// src/bridge/connection/schedule-reconnect.ts
|
|
30060
|
-
var RECONNECT_BASE_MS = 1e3;
|
|
30061
|
-
var RECONNECT_MAX_MS = 3e4;
|
|
30062
|
-
function scheduleReconnect(state, connect, log2) {
|
|
30063
|
-
if (state.closedByUser || state.currentWs != null) return;
|
|
30064
|
-
const delay2 = Math.min(
|
|
30065
|
-
RECONNECT_BASE_MS * 2 ** state.reconnectAttempt,
|
|
30066
|
-
RECONNECT_MAX_MS
|
|
30067
|
-
);
|
|
30068
|
-
state.reconnectAttempt += 1;
|
|
30069
|
-
log2(`[Bridge service] reconnect attempt ${state.reconnectAttempt} in ${delay2 / 1e3}s\u2026`);
|
|
30070
|
-
state.reconnectTimeout = setTimeout(() => {
|
|
30071
|
-
state.reconnectTimeout = null;
|
|
30072
|
-
connect();
|
|
30073
|
-
}, delay2);
|
|
30074
|
-
}
|
|
30075
|
-
|
|
30076
30257
|
// src/bridge/connection/close-bridge-connection.ts
|
|
30077
30258
|
async function closeBridgeConnection(state, acpManager, devServerManager, log2) {
|
|
30078
30259
|
log2?.("Shutting down\u2026");
|
|
@@ -30082,25 +30263,27 @@ async function closeBridgeConnection(state, acpManager, devServerManager, log2)
|
|
|
30082
30263
|
await devServerManager.shutdownAllGraceful();
|
|
30083
30264
|
}
|
|
30084
30265
|
state.closedByUser = true;
|
|
30266
|
+
clearReconnectQuietTimer(state.mainQuiet);
|
|
30267
|
+
clearReconnectQuietTimer(state.firehoseQuiet);
|
|
30085
30268
|
if (state.reconnectTimeout != null) {
|
|
30086
30269
|
log2?.("Cancelling reconnect timer\u2026");
|
|
30087
30270
|
clearTimeout(state.reconnectTimeout);
|
|
30088
30271
|
state.reconnectTimeout = null;
|
|
30089
30272
|
}
|
|
30090
30273
|
if (state.firehoseReconnectTimeout != null) {
|
|
30091
|
-
log2?.("[Proxy and log service]
|
|
30274
|
+
log2?.("[Proxy and log service] Cancelling reconnect timer\u2026");
|
|
30092
30275
|
clearTimeout(state.firehoseReconnectTimeout);
|
|
30093
30276
|
state.firehoseReconnectTimeout = null;
|
|
30094
30277
|
}
|
|
30095
30278
|
if (state.firehoseHandle) {
|
|
30096
|
-
log2?.("[Proxy and log service]
|
|
30279
|
+
log2?.("[Proxy and log service] Closing connection (CLI shutdown)\u2026");
|
|
30097
30280
|
state.firehoseHandle.close();
|
|
30098
30281
|
state.firehoseHandle = null;
|
|
30099
30282
|
}
|
|
30100
30283
|
log2?.("Disconnecting local agents (ACP)\u2026");
|
|
30101
30284
|
acpManager.disconnect();
|
|
30102
30285
|
if (state.currentWs) {
|
|
30103
|
-
log2?.("[Bridge service]
|
|
30286
|
+
log2?.("[Bridge service] Closing connection (CLI shutdown)\u2026");
|
|
30104
30287
|
state.currentWs.removeAllListeners();
|
|
30105
30288
|
const wsState = state.currentWs.readyState;
|
|
30106
30289
|
if (wsState === 1 || wsState === 2) {
|
|
@@ -30116,11 +30299,20 @@ async function closeBridgeConnection(state, acpManager, devServerManager, log2)
|
|
|
30116
30299
|
}
|
|
30117
30300
|
|
|
30118
30301
|
// src/git/session-git-queue.ts
|
|
30302
|
+
import { execFile as execFile2 } from "node:child_process";
|
|
30303
|
+
import { promisify as promisify2 } from "node:util";
|
|
30304
|
+
import * as fs4 from "node:fs";
|
|
30305
|
+
import * as path5 from "node:path";
|
|
30306
|
+
|
|
30307
|
+
// src/git/pre-turn-snapshot.ts
|
|
30308
|
+
import * as fs3 from "node:fs";
|
|
30309
|
+
import * as path4 from "node:path";
|
|
30119
30310
|
import { execFile } from "node:child_process";
|
|
30120
30311
|
import { promisify } from "node:util";
|
|
30121
|
-
import * as path3 from "node:path";
|
|
30122
30312
|
var execFileAsync = promisify(execFile);
|
|
30123
|
-
|
|
30313
|
+
function snapshotsDirForCwd(agentCwd) {
|
|
30314
|
+
return path4.join(agentCwd, ".buildautomaton", "snapshots");
|
|
30315
|
+
}
|
|
30124
30316
|
async function gitStashCreate(repoRoot, log2) {
|
|
30125
30317
|
try {
|
|
30126
30318
|
const { stdout } = await execFileAsync("git", ["stash", "create"], {
|
|
@@ -30129,51 +30321,149 @@ async function gitStashCreate(repoRoot, log2) {
|
|
|
30129
30321
|
});
|
|
30130
30322
|
return stdout.trim();
|
|
30131
30323
|
} catch (e) {
|
|
30132
|
-
log2(
|
|
30324
|
+
log2(
|
|
30325
|
+
`[snapshot] Git stash create failed in ${repoRoot}: ${e instanceof Error ? e.message : String(e)}`
|
|
30326
|
+
);
|
|
30133
30327
|
return "";
|
|
30134
30328
|
}
|
|
30135
30329
|
}
|
|
30136
|
-
async function
|
|
30137
|
-
|
|
30138
|
-
|
|
30330
|
+
async function gitRun(repoRoot, args, log2, label) {
|
|
30331
|
+
try {
|
|
30332
|
+
await execFileAsync("git", args, { cwd: repoRoot, maxBuffer: 10 * 1024 * 1024 });
|
|
30333
|
+
return { ok: true };
|
|
30334
|
+
} catch (e) {
|
|
30335
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
30336
|
+
log2(`[snapshot] Git ${label} failed in ${repoRoot}: ${msg}`);
|
|
30337
|
+
return { ok: false, error: msg };
|
|
30338
|
+
}
|
|
30339
|
+
}
|
|
30340
|
+
async function resolveSnapshotRepoRoots(options) {
|
|
30341
|
+
const { worktreePaths, fallbackCwd, log: log2 } = options;
|
|
30342
|
+
if (worktreePaths?.length) {
|
|
30343
|
+
const uniq = [...new Set(worktreePaths.map((p) => path4.resolve(p)))];
|
|
30344
|
+
return uniq;
|
|
30345
|
+
}
|
|
30346
|
+
try {
|
|
30347
|
+
const repos = await discoverGitReposUnderRoot(fallbackCwd);
|
|
30348
|
+
return repos.map((r) => r.absolutePath);
|
|
30349
|
+
} catch (e) {
|
|
30350
|
+
log2(`[snapshot] Discover repositories failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
30351
|
+
return [];
|
|
30352
|
+
}
|
|
30353
|
+
}
|
|
30354
|
+
async function capturePreTurnSnapshot(options) {
|
|
30355
|
+
const { runId, repoRoots, agentCwd, log: log2 } = options;
|
|
30356
|
+
if (!runId || !repoRoots.length) {
|
|
30357
|
+
return { ok: false, error: "No git repos to snapshot" };
|
|
30358
|
+
}
|
|
30139
30359
|
const repos = [];
|
|
30140
30360
|
for (const root of repoRoots) {
|
|
30141
30361
|
const stashSha = await gitStashCreate(root, log2);
|
|
30142
30362
|
repos.push({ path: root, stashSha });
|
|
30143
30363
|
}
|
|
30144
|
-
|
|
30145
|
-
|
|
30364
|
+
const dir = snapshotsDirForCwd(agentCwd);
|
|
30365
|
+
try {
|
|
30366
|
+
fs3.mkdirSync(dir, { recursive: true });
|
|
30367
|
+
} catch (e) {
|
|
30368
|
+
return { ok: false, error: e instanceof Error ? e.message : String(e) };
|
|
30369
|
+
}
|
|
30370
|
+
const payload = {
|
|
30371
|
+
runId,
|
|
30372
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
30373
|
+
repos
|
|
30374
|
+
};
|
|
30375
|
+
const filePath = path4.join(dir, `${runId}.json`);
|
|
30376
|
+
try {
|
|
30377
|
+
fs3.writeFileSync(filePath, JSON.stringify(payload, null, 2), "utf8");
|
|
30378
|
+
} catch (e) {
|
|
30379
|
+
return { ok: false, error: e instanceof Error ? e.message : String(e) };
|
|
30380
|
+
}
|
|
30381
|
+
const repoList = repos.map((r) => r.path).join(", ");
|
|
30382
|
+
log2(
|
|
30383
|
+
`[snapshot] Saved pre-turn snapshot ${runId.slice(0, 8)}\u2026 (${repos.length} repo(s)): ${repoList}`
|
|
30384
|
+
);
|
|
30385
|
+
return { ok: true, filePath, repos };
|
|
30386
|
+
}
|
|
30387
|
+
async function applyPreTurnSnapshot(filePath, log2) {
|
|
30388
|
+
let data;
|
|
30389
|
+
try {
|
|
30390
|
+
const raw = fs3.readFileSync(filePath, "utf8");
|
|
30391
|
+
data = JSON.parse(raw);
|
|
30392
|
+
} catch (e) {
|
|
30393
|
+
return { ok: false, error: e instanceof Error ? e.message : String(e) };
|
|
30394
|
+
}
|
|
30395
|
+
if (!Array.isArray(data.repos)) {
|
|
30396
|
+
return { ok: false, error: "Invalid snapshot file" };
|
|
30397
|
+
}
|
|
30398
|
+
for (const r of data.repos) {
|
|
30399
|
+
if (!r.path) continue;
|
|
30400
|
+
const reset = await gitRun(r.path, ["reset", "--hard", "HEAD"], log2, "reset --hard");
|
|
30401
|
+
if (!reset.ok) return reset;
|
|
30402
|
+
const clean = await gitRun(r.path, ["clean", "-fd"], log2, "clean -fd");
|
|
30403
|
+
if (!clean.ok) return clean;
|
|
30404
|
+
if (r.stashSha) {
|
|
30405
|
+
const ap = await gitRun(r.path, ["stash", "apply", r.stashSha], log2, "stash apply");
|
|
30406
|
+
if (!ap.ok) return ap;
|
|
30407
|
+
}
|
|
30408
|
+
}
|
|
30409
|
+
log2(`[snapshot] Restored pre-turn state for ${data.runId.slice(0, 8)}\u2026`);
|
|
30410
|
+
return { ok: true };
|
|
30146
30411
|
}
|
|
30147
|
-
|
|
30148
|
-
|
|
30149
|
-
|
|
30150
|
-
|
|
30151
|
-
|
|
30412
|
+
function snapshotFilePath(agentCwd, runId) {
|
|
30413
|
+
return path4.join(snapshotsDirForCwd(agentCwd), `${runId}.json`);
|
|
30414
|
+
}
|
|
30415
|
+
|
|
30416
|
+
// src/git/session-git-queue.ts
|
|
30417
|
+
var execFileAsync2 = promisify2(execFile2);
|
|
30418
|
+
var MAX_FULL_FILE_TEXT_BYTES = 512 * 1024;
|
|
30419
|
+
function readWorkspaceFileAsUtf8(absPath) {
|
|
30420
|
+
try {
|
|
30421
|
+
const st = fs4.statSync(absPath);
|
|
30422
|
+
if (!st.isFile() || st.size > MAX_FULL_FILE_TEXT_BYTES) return void 0;
|
|
30423
|
+
return fs4.readFileSync(absPath, "utf8");
|
|
30424
|
+
} catch {
|
|
30425
|
+
return void 0;
|
|
30426
|
+
}
|
|
30427
|
+
}
|
|
30428
|
+
async function collectTurnGitDiffFromPreTurnSnapshot(options) {
|
|
30429
|
+
const { sessionId, runId, agentCwd, sendSessionUpdate, log: log2 } = options;
|
|
30430
|
+
const filePath = snapshotFilePath(agentCwd, runId);
|
|
30431
|
+
let data;
|
|
30432
|
+
try {
|
|
30433
|
+
const raw = fs4.readFileSync(filePath, "utf8");
|
|
30434
|
+
data = JSON.parse(raw);
|
|
30435
|
+
} catch (e) {
|
|
30436
|
+
log2(
|
|
30437
|
+
`[session-git-queue] No pre-turn snapshot for run ${runId.slice(0, 8)}\u2026: ${e instanceof Error ? e.message : String(e)}`
|
|
30438
|
+
);
|
|
30152
30439
|
return;
|
|
30153
30440
|
}
|
|
30154
|
-
|
|
30155
|
-
|
|
30156
|
-
|
|
30441
|
+
if (!Array.isArray(data.repos) || !data.repos.length) {
|
|
30442
|
+
log2(`[session-git-queue] Empty repos in snapshot ${runId.slice(0, 8)}\u2026; skipping aggregate diff.`);
|
|
30443
|
+
return;
|
|
30444
|
+
}
|
|
30445
|
+
const multiRepo = data.repos.length > 1;
|
|
30446
|
+
for (const repo of data.repos) {
|
|
30157
30447
|
if (!repo.stashSha) continue;
|
|
30158
30448
|
let namesRaw;
|
|
30159
30449
|
try {
|
|
30160
|
-
const { stdout } = await
|
|
30450
|
+
const { stdout } = await execFileAsync2("git", ["diff", "--name-only", repo.stashSha], {
|
|
30161
30451
|
cwd: repo.path,
|
|
30162
30452
|
maxBuffer: 10 * 1024 * 1024
|
|
30163
30453
|
});
|
|
30164
30454
|
namesRaw = stdout;
|
|
30165
30455
|
} catch (e) {
|
|
30166
30456
|
log2(
|
|
30167
|
-
`[session-git-queue]
|
|
30457
|
+
`[session-git-queue] Git diff --name-only failed in ${repo.path}: ${e instanceof Error ? e.message : String(e)}`
|
|
30168
30458
|
);
|
|
30169
30459
|
continue;
|
|
30170
30460
|
}
|
|
30171
30461
|
const lines = namesRaw.split("\n").map((l) => l.trim()).filter(Boolean);
|
|
30172
|
-
const slug =
|
|
30462
|
+
const slug = path5.basename(repo.path).replace(/[^\w.-]+/g, "_") || "repo";
|
|
30173
30463
|
for (const rel of lines) {
|
|
30174
30464
|
if (rel.includes("..")) continue;
|
|
30175
30465
|
try {
|
|
30176
|
-
const { stdout: patchContent } = await
|
|
30466
|
+
const { stdout: patchContent } = await execFileAsync2(
|
|
30177
30467
|
"git",
|
|
30178
30468
|
["diff", "--no-color", repo.stashSha, "--", rel],
|
|
30179
30469
|
{
|
|
@@ -30183,15 +30473,20 @@ async function collectSessionDiffAndNotify(options) {
|
|
|
30183
30473
|
);
|
|
30184
30474
|
if (!patchContent.trim()) continue;
|
|
30185
30475
|
const displayPath = multiRepo ? `${slug}/${rel}` : rel;
|
|
30476
|
+
const absFile = path5.join(repo.path, rel);
|
|
30477
|
+
const newText = readWorkspaceFileAsUtf8(absFile);
|
|
30186
30478
|
sendSessionUpdate({
|
|
30187
30479
|
type: "session_file_change",
|
|
30188
30480
|
sessionId,
|
|
30189
30481
|
runId,
|
|
30190
30482
|
path: displayPath,
|
|
30191
|
-
patchContent
|
|
30483
|
+
patchContent,
|
|
30484
|
+
...newText !== void 0 ? { newText } : {}
|
|
30192
30485
|
});
|
|
30193
30486
|
} catch (e) {
|
|
30194
|
-
log2(
|
|
30487
|
+
log2(
|
|
30488
|
+
`[session-git-queue] Git diff failed for ${rel}: ${e instanceof Error ? e.message : String(e)}`
|
|
30489
|
+
);
|
|
30195
30490
|
}
|
|
30196
30491
|
}
|
|
30197
30492
|
}
|
|
@@ -30205,15 +30500,21 @@ async function sendPromptToAgent(options) {
|
|
|
30205
30500
|
promptId,
|
|
30206
30501
|
sessionId,
|
|
30207
30502
|
runId,
|
|
30503
|
+
agentCwd,
|
|
30208
30504
|
sendResult,
|
|
30209
30505
|
sendSessionUpdate,
|
|
30210
|
-
collectSessionDiffAfterTurn,
|
|
30211
30506
|
log: log2
|
|
30212
30507
|
} = options;
|
|
30213
30508
|
try {
|
|
30214
30509
|
const result = await handle.sendPrompt(promptText, {});
|
|
30215
|
-
if (
|
|
30216
|
-
await
|
|
30510
|
+
if (sessionId && runId && sendSessionUpdate && agentCwd && result.success) {
|
|
30511
|
+
await collectTurnGitDiffFromPreTurnSnapshot({
|
|
30512
|
+
sessionId,
|
|
30513
|
+
runId,
|
|
30514
|
+
agentCwd,
|
|
30515
|
+
sendSessionUpdate,
|
|
30516
|
+
log: log2
|
|
30517
|
+
});
|
|
30217
30518
|
}
|
|
30218
30519
|
sendResult({
|
|
30219
30520
|
type: "prompt_result",
|
|
@@ -30223,12 +30524,12 @@ async function sendPromptToAgent(options) {
|
|
|
30223
30524
|
...result
|
|
30224
30525
|
});
|
|
30225
30526
|
if (!result.success) {
|
|
30226
|
-
log2(`[
|
|
30527
|
+
log2(`[Agent] ${result.error ?? "Error"}`);
|
|
30227
30528
|
}
|
|
30228
30529
|
} catch (err) {
|
|
30229
30530
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
30230
|
-
log2(`[
|
|
30231
|
-
if (err instanceof Error && err.stack) log2(`[
|
|
30531
|
+
log2(`[Agent] Send failed: ${errMsg}`);
|
|
30532
|
+
if (err instanceof Error && err.stack) log2(`[Agent] ${err.stack}`);
|
|
30232
30533
|
sendResult({
|
|
30233
30534
|
type: "prompt_result",
|
|
30234
30535
|
id: promptId,
|
|
@@ -30241,8 +30542,8 @@ async function sendPromptToAgent(options) {
|
|
|
30241
30542
|
}
|
|
30242
30543
|
|
|
30243
30544
|
// src/acp/ensure-acp-client.ts
|
|
30244
|
-
import * as
|
|
30245
|
-
import * as
|
|
30545
|
+
import * as fs5 from "node:fs";
|
|
30546
|
+
import * as path9 from "node:path";
|
|
30246
30547
|
|
|
30247
30548
|
// src/error-message.ts
|
|
30248
30549
|
function errorMessage(err) {
|
|
@@ -30273,10 +30574,10 @@ function toErrorMessage(err) {
|
|
|
30273
30574
|
}
|
|
30274
30575
|
async function createAcpClient(options) {
|
|
30275
30576
|
const { ClientSideConnection: ClientSideConnection2, ndJsonStream: ndJsonStream2 } = await Promise.resolve().then(() => (init_acp(), acp_exports));
|
|
30276
|
-
const { command, cwd
|
|
30577
|
+
const { command, cwd = getBridgeWorkspaceDirectory(), onSessionUpdate } = options;
|
|
30277
30578
|
const isWindows = process.platform === "win32";
|
|
30278
30579
|
const child = spawn2(command[0], command.slice(1), {
|
|
30279
|
-
cwd
|
|
30580
|
+
cwd,
|
|
30280
30581
|
stdio: ["pipe", "pipe", "inherit"],
|
|
30281
30582
|
env: process.env,
|
|
30282
30583
|
shell: isWindows
|
|
@@ -30308,7 +30609,7 @@ async function createAcpClient(options) {
|
|
|
30308
30609
|
capabilities: {},
|
|
30309
30610
|
clientInfo: { name: "buildautomaton-cli", version: "0.1.0" }
|
|
30310
30611
|
});
|
|
30311
|
-
const newSessionRes = await connection.newSession({ workingDirectory:
|
|
30612
|
+
const newSessionRes = await connection.newSession({ workingDirectory: cwd });
|
|
30312
30613
|
const sessionId = newSessionRes.sessionId;
|
|
30313
30614
|
resolve15({
|
|
30314
30615
|
sessionId,
|
|
@@ -30365,27 +30666,27 @@ async function createCodexAcpClient(options) {
|
|
|
30365
30666
|
}
|
|
30366
30667
|
|
|
30367
30668
|
// src/acp/clients/cursor-acp-client.ts
|
|
30368
|
-
import { readFileSync, writeFileSync, mkdirSync } from "node:fs";
|
|
30669
|
+
import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "node:fs";
|
|
30369
30670
|
import { dirname } from "node:path";
|
|
30370
30671
|
import { spawn as spawn3 } from "node:child_process";
|
|
30371
30672
|
import * as readline from "node:readline";
|
|
30372
30673
|
|
|
30373
30674
|
// src/acp/safe-fs-path.ts
|
|
30374
|
-
import * as
|
|
30375
|
-
function resolveSafePathUnderCwd(
|
|
30675
|
+
import * as path6 from "node:path";
|
|
30676
|
+
function resolveSafePathUnderCwd(cwd, filePath) {
|
|
30376
30677
|
const trimmed2 = filePath.trim();
|
|
30377
30678
|
if (!trimmed2) return null;
|
|
30378
|
-
const normalizedCwd =
|
|
30379
|
-
const resolved =
|
|
30380
|
-
const rel =
|
|
30381
|
-
if (rel.startsWith("..") ||
|
|
30679
|
+
const normalizedCwd = path6.resolve(cwd);
|
|
30680
|
+
const resolved = path6.isAbsolute(trimmed2) ? path6.normalize(trimmed2) : path6.resolve(normalizedCwd, trimmed2);
|
|
30681
|
+
const rel = path6.relative(normalizedCwd, resolved);
|
|
30682
|
+
if (rel.startsWith("..") || path6.isAbsolute(rel)) return null;
|
|
30382
30683
|
return resolved;
|
|
30383
30684
|
}
|
|
30384
|
-
function toDisplayPathRelativeToCwd(
|
|
30385
|
-
const normalizedCwd =
|
|
30386
|
-
const rel =
|
|
30387
|
-
if (!rel || rel === "") return
|
|
30388
|
-
return rel.split(
|
|
30685
|
+
function toDisplayPathRelativeToCwd(cwd, absolutePath) {
|
|
30686
|
+
const normalizedCwd = path6.resolve(cwd);
|
|
30687
|
+
const rel = path6.relative(normalizedCwd, path6.resolve(absolutePath));
|
|
30688
|
+
if (!rel || rel === "") return path6.basename(absolutePath);
|
|
30689
|
+
return rel.split(path6.sep).join("/");
|
|
30389
30690
|
}
|
|
30390
30691
|
|
|
30391
30692
|
// src/files/diff/unified-diff.ts
|
|
@@ -30434,6 +30735,19 @@ function editSnippetToUnifiedDiff(filePath, oldText, newText) {
|
|
|
30434
30735
|
return out.join("\n");
|
|
30435
30736
|
}
|
|
30436
30737
|
|
|
30738
|
+
// src/acp/format-session-update-kind-for-log.ts
|
|
30739
|
+
var SESSION_UPDATE_KIND_LABELS = {
|
|
30740
|
+
tool_call: "Tool call",
|
|
30741
|
+
tool_call_update: "Tool call status",
|
|
30742
|
+
agent_message_chunk: "Agent message chunk",
|
|
30743
|
+
update: "Session update"
|
|
30744
|
+
};
|
|
30745
|
+
function formatSessionUpdateKindForLog(kind) {
|
|
30746
|
+
const known = SESSION_UPDATE_KIND_LABELS[kind];
|
|
30747
|
+
if (known) return known;
|
|
30748
|
+
return kind.split("_").filter(Boolean).map((w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase()).join(" ");
|
|
30749
|
+
}
|
|
30750
|
+
|
|
30437
30751
|
// src/acp/clients/cursor-acp-client.ts
|
|
30438
30752
|
var FS_READ_METHODS = /* @__PURE__ */ new Set(["fs/read_text_file", "fs/readTextFile"]);
|
|
30439
30753
|
var FS_WRITE_METHODS = /* @__PURE__ */ new Set(["fs/write_text_file", "fs/writeTextFile"]);
|
|
@@ -30459,11 +30773,11 @@ function sliceLinesByRange(content, line, limit) {
|
|
|
30459
30773
|
return lines.slice(start, end).join("\n");
|
|
30460
30774
|
}
|
|
30461
30775
|
async function createCursorAcpClient(options) {
|
|
30462
|
-
const { command, cwd
|
|
30776
|
+
const { command, cwd = getBridgeWorkspaceDirectory(), onSessionUpdate, onRequest, onFileChange } = options;
|
|
30463
30777
|
const dbgFs = process.env.BUILDAMATON_DEBUG_ACP_FS === "1";
|
|
30464
30778
|
const isWindows = process.platform === "win32";
|
|
30465
30779
|
const child = spawn3(command[0], command.slice(1), {
|
|
30466
|
-
cwd
|
|
30780
|
+
cwd,
|
|
30467
30781
|
stdio: ["pipe", "pipe", "inherit"],
|
|
30468
30782
|
env: process.env,
|
|
30469
30783
|
shell: isWindows
|
|
@@ -30521,7 +30835,10 @@ async function createCursorAcpClient(options) {
|
|
|
30521
30835
|
const toolCall = update.toolCall ?? update.tool_call;
|
|
30522
30836
|
const toolName = typeof toolCall?.name === "string" ? toolCall.name : "";
|
|
30523
30837
|
if (dbgFs && (sessionUpdate === "tool_call" || sessionUpdate === "tool_call_update")) {
|
|
30524
|
-
|
|
30838
|
+
const kindLabel = formatSessionUpdateKindForLog(sessionUpdate ?? "update");
|
|
30839
|
+
console.error(
|
|
30840
|
+
`[acp] Received session update (${kindLabel}) tool=${toolName || "(none)"}`
|
|
30841
|
+
);
|
|
30525
30842
|
}
|
|
30526
30843
|
const isTextChunk = sessionUpdate === "agent_message_chunk" && update.content?.text;
|
|
30527
30844
|
if (isTextChunk && update.content?.text) {
|
|
@@ -30541,14 +30858,14 @@ async function createCursorAcpClient(options) {
|
|
|
30541
30858
|
if (dbgFs) {
|
|
30542
30859
|
console.error(`[acp-fs] ${method} path=${filePath.slice(0, 200)}${filePath.length > 200 ? "\u2026" : ""}`);
|
|
30543
30860
|
}
|
|
30544
|
-
const abs = resolveSafePathUnderCwd(
|
|
30861
|
+
const abs = resolveSafePathUnderCwd(cwd, filePath);
|
|
30545
30862
|
if (!abs) {
|
|
30546
30863
|
if (dbgFs) console.error(`[acp-fs] ${method} rejected path (outside cwd or empty)`);
|
|
30547
30864
|
respondJsonRpcError(id, -32602, "Invalid or disallowed path");
|
|
30548
30865
|
return;
|
|
30549
30866
|
}
|
|
30550
30867
|
try {
|
|
30551
|
-
let content =
|
|
30868
|
+
let content = readFileSync3(abs, "utf8");
|
|
30552
30869
|
const line2 = typeof params.line === "number" ? params.line : void 0;
|
|
30553
30870
|
const limit = typeof params.limit === "number" ? params.limit : void 0;
|
|
30554
30871
|
content = sliceLinesByRange(content, line2, limit);
|
|
@@ -30572,7 +30889,7 @@ async function createCursorAcpClient(options) {
|
|
|
30572
30889
|
`[acp-fs] ${method} path=${filePath.slice(0, 200)}${filePath.length > 200 ? "\u2026" : ""} newBytes=${newText.length}`
|
|
30573
30890
|
);
|
|
30574
30891
|
}
|
|
30575
|
-
const abs = resolveSafePathUnderCwd(
|
|
30892
|
+
const abs = resolveSafePathUnderCwd(cwd, filePath);
|
|
30576
30893
|
if (!abs) {
|
|
30577
30894
|
if (dbgFs) console.error(`[acp-fs] ${method} rejected path (outside cwd or empty): ${filePath.slice(0, 120)}`);
|
|
30578
30895
|
respondJsonRpcError(id, -32602, "Invalid or disallowed path");
|
|
@@ -30580,7 +30897,7 @@ async function createCursorAcpClient(options) {
|
|
|
30580
30897
|
}
|
|
30581
30898
|
let oldText = "";
|
|
30582
30899
|
try {
|
|
30583
|
-
oldText =
|
|
30900
|
+
oldText = readFileSync3(abs, "utf8");
|
|
30584
30901
|
} catch (e) {
|
|
30585
30902
|
if (e.code !== "ENOENT") {
|
|
30586
30903
|
respondJsonRpcError(id, -32e3, e instanceof Error ? e.message : String(e));
|
|
@@ -30588,13 +30905,13 @@ async function createCursorAcpClient(options) {
|
|
|
30588
30905
|
}
|
|
30589
30906
|
}
|
|
30590
30907
|
try {
|
|
30591
|
-
|
|
30592
|
-
|
|
30908
|
+
mkdirSync2(dirname(abs), { recursive: true });
|
|
30909
|
+
writeFileSync2(abs, newText, "utf8");
|
|
30593
30910
|
} catch (e) {
|
|
30594
30911
|
respondJsonRpcError(id, -32e3, e instanceof Error ? e.message : String(e));
|
|
30595
30912
|
return;
|
|
30596
30913
|
}
|
|
30597
|
-
const displayPath = toDisplayPathRelativeToCwd(
|
|
30914
|
+
const displayPath = toDisplayPathRelativeToCwd(cwd, abs);
|
|
30598
30915
|
const patchContent = editSnippetToUnifiedDiff(displayPath, oldText, newText);
|
|
30599
30916
|
onFileChange?.({ path: displayPath, oldText, newText, patchContent });
|
|
30600
30917
|
respond(id, null);
|
|
@@ -30636,7 +30953,7 @@ async function createCursorAcpClient(options) {
|
|
|
30636
30953
|
clientInfo: { name: "buildautomaton-cli", version: "0.1.0" }
|
|
30637
30954
|
});
|
|
30638
30955
|
await send("authenticate", { methodId: "cursor_login" });
|
|
30639
|
-
const newResult = await send("session/new", { cwd
|
|
30956
|
+
const newResult = await send("session/new", { cwd, mcpServers: [] });
|
|
30640
30957
|
const sessionId = newResult?.sessionId ?? "";
|
|
30641
30958
|
if (!sessionId) throw new Error("Cursor ACP session/new did not return sessionId");
|
|
30642
30959
|
resolve15({
|
|
@@ -30708,20 +31025,20 @@ function resolveAgentCommand(preferredAgentType) {
|
|
|
30708
31025
|
|
|
30709
31026
|
// src/acp/session-file-change-path-kind.ts
|
|
30710
31027
|
import { execFileSync as execFileSync3 } from "node:child_process";
|
|
30711
|
-
import { existsSync, statSync } from "node:fs";
|
|
31028
|
+
import { existsSync, statSync as statSync2 } from "node:fs";
|
|
30712
31029
|
|
|
30713
31030
|
// src/git/get-git-repo-root-sync.ts
|
|
30714
31031
|
import { execFileSync } from "node:child_process";
|
|
30715
|
-
import * as
|
|
31032
|
+
import * as path7 from "node:path";
|
|
30716
31033
|
function getGitRepoRootSync(startDir) {
|
|
30717
31034
|
try {
|
|
30718
31035
|
const out = execFileSync("git", ["rev-parse", "--show-toplevel"], {
|
|
30719
|
-
cwd:
|
|
31036
|
+
cwd: path7.resolve(startDir),
|
|
30720
31037
|
encoding: "utf8",
|
|
30721
31038
|
stdio: ["ignore", "pipe", "ignore"],
|
|
30722
31039
|
maxBuffer: 1024 * 1024
|
|
30723
31040
|
}).trim();
|
|
30724
|
-
return out ?
|
|
31041
|
+
return out ? path7.resolve(out) : null;
|
|
30725
31042
|
} catch {
|
|
30726
31043
|
return null;
|
|
30727
31044
|
}
|
|
@@ -30729,65 +31046,65 @@ function getGitRepoRootSync(startDir) {
|
|
|
30729
31046
|
|
|
30730
31047
|
// src/acp/workspace-files.ts
|
|
30731
31048
|
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
30732
|
-
import { readFileSync as
|
|
30733
|
-
import * as
|
|
30734
|
-
function resolveWorkspaceFilePath(
|
|
31049
|
+
import { readFileSync as readFileSync4 } from "node:fs";
|
|
31050
|
+
import * as path8 from "node:path";
|
|
31051
|
+
function resolveWorkspaceFilePath(cwd, rawPath) {
|
|
30735
31052
|
const trimmed2 = rawPath.trim();
|
|
30736
31053
|
if (!trimmed2) return null;
|
|
30737
|
-
const normalizedCwd =
|
|
30738
|
-
let abs = resolveSafePathUnderCwd(
|
|
31054
|
+
const normalizedCwd = path8.resolve(cwd);
|
|
31055
|
+
let abs = resolveSafePathUnderCwd(cwd, trimmed2);
|
|
30739
31056
|
if (!abs) {
|
|
30740
|
-
const candidate =
|
|
30741
|
-
const gitRoot2 = getGitRepoRootSync(
|
|
31057
|
+
const candidate = path8.isAbsolute(trimmed2) ? path8.normalize(trimmed2) : path8.normalize(path8.resolve(normalizedCwd, trimmed2));
|
|
31058
|
+
const gitRoot2 = getGitRepoRootSync(cwd);
|
|
30742
31059
|
if (!gitRoot2) return null;
|
|
30743
|
-
const rel =
|
|
30744
|
-
if (rel.startsWith("..") ||
|
|
31060
|
+
const rel = path8.relative(gitRoot2, candidate);
|
|
31061
|
+
if (rel.startsWith("..") || path8.isAbsolute(rel)) return null;
|
|
30745
31062
|
abs = candidate;
|
|
30746
31063
|
}
|
|
30747
|
-
const gitRoot = getGitRepoRootSync(
|
|
31064
|
+
const gitRoot = getGitRepoRootSync(cwd);
|
|
30748
31065
|
if (gitRoot) {
|
|
30749
|
-
const relFromRoot =
|
|
30750
|
-
if (!relFromRoot.startsWith("..") && !
|
|
30751
|
-
return { abs, display: relFromRoot.split(
|
|
31066
|
+
const relFromRoot = path8.relative(gitRoot, abs);
|
|
31067
|
+
if (!relFromRoot.startsWith("..") && !path8.isAbsolute(relFromRoot)) {
|
|
31068
|
+
return { abs, display: relFromRoot.split(path8.sep).join("/") };
|
|
30752
31069
|
}
|
|
30753
31070
|
}
|
|
30754
|
-
return { abs, display: toDisplayPathRelativeToCwd(
|
|
31071
|
+
return { abs, display: toDisplayPathRelativeToCwd(cwd, abs) };
|
|
30755
31072
|
}
|
|
30756
|
-
function readUtf8WorkspaceFile(
|
|
31073
|
+
function readUtf8WorkspaceFile(cwd, displayPath) {
|
|
30757
31074
|
if (!displayPath || displayPath.includes("..")) return "";
|
|
30758
|
-
const gitRoot = getGitRepoRootSync(
|
|
31075
|
+
const gitRoot = getGitRepoRootSync(cwd);
|
|
30759
31076
|
if (gitRoot) {
|
|
30760
|
-
const abs2 =
|
|
30761
|
-
const rel =
|
|
30762
|
-
if (!rel.startsWith("..") && !
|
|
31077
|
+
const abs2 = path8.resolve(gitRoot, displayPath);
|
|
31078
|
+
const rel = path8.relative(gitRoot, abs2);
|
|
31079
|
+
if (!rel.startsWith("..") && !path8.isAbsolute(rel)) {
|
|
30763
31080
|
try {
|
|
30764
|
-
return
|
|
31081
|
+
return readFileSync4(abs2, "utf8");
|
|
30765
31082
|
} catch {
|
|
30766
31083
|
}
|
|
30767
31084
|
}
|
|
30768
31085
|
}
|
|
30769
|
-
const abs = resolveSafePathUnderCwd(
|
|
31086
|
+
const abs = resolveSafePathUnderCwd(cwd, displayPath);
|
|
30770
31087
|
if (!abs) return "";
|
|
30771
31088
|
try {
|
|
30772
|
-
return
|
|
31089
|
+
return readFileSync4(abs, "utf8");
|
|
30773
31090
|
} catch {
|
|
30774
31091
|
return "";
|
|
30775
31092
|
}
|
|
30776
31093
|
}
|
|
30777
|
-
function tryWorkspaceDisplayToAbs(
|
|
31094
|
+
function tryWorkspaceDisplayToAbs(cwd, displayPath) {
|
|
30778
31095
|
if (!displayPath || displayPath.includes("..")) return null;
|
|
30779
|
-
const gitRoot = getGitRepoRootSync(
|
|
31096
|
+
const gitRoot = getGitRepoRootSync(cwd);
|
|
30780
31097
|
if (gitRoot) {
|
|
30781
|
-
const abs =
|
|
30782
|
-
const rel =
|
|
30783
|
-
if (!rel.startsWith("..") && !
|
|
31098
|
+
const abs = path8.resolve(gitRoot, displayPath);
|
|
31099
|
+
const rel = path8.relative(gitRoot, abs);
|
|
31100
|
+
if (!rel.startsWith("..") && !path8.isAbsolute(rel)) return abs;
|
|
30784
31101
|
}
|
|
30785
|
-
return resolveSafePathUnderCwd(
|
|
31102
|
+
return resolveSafePathUnderCwd(cwd, displayPath);
|
|
30786
31103
|
}
|
|
30787
|
-
function readGitHeadBlob(
|
|
31104
|
+
function readGitHeadBlob(cwd, displayPath) {
|
|
30788
31105
|
if (!displayPath || displayPath.includes("..")) return "";
|
|
30789
|
-
const gitRoot = getGitRepoRootSync(
|
|
30790
|
-
const execCwd = gitRoot ??
|
|
31106
|
+
const gitRoot = getGitRepoRootSync(cwd);
|
|
31107
|
+
const execCwd = gitRoot ?? cwd;
|
|
30791
31108
|
try {
|
|
30792
31109
|
return execFileSync2("git", ["show", `HEAD:${displayPath}`], {
|
|
30793
31110
|
cwd: execCwd,
|
|
@@ -30800,9 +31117,9 @@ function readGitHeadBlob(cwd3, displayPath) {
|
|
|
30800
31117
|
}
|
|
30801
31118
|
|
|
30802
31119
|
// src/acp/session-file-change-path-kind.ts
|
|
30803
|
-
function gitHeadPathObjectType(
|
|
31120
|
+
function gitHeadPathObjectType(cwd, displayPath) {
|
|
30804
31121
|
if (!displayPath || displayPath.includes("..")) return null;
|
|
30805
|
-
const gitRoot = getGitRepoRootSync(
|
|
31122
|
+
const gitRoot = getGitRepoRootSync(cwd);
|
|
30806
31123
|
if (!gitRoot) return null;
|
|
30807
31124
|
try {
|
|
30808
31125
|
return execFileSync3("git", ["cat-file", "-t", `HEAD:${displayPath}`], {
|
|
@@ -30813,11 +31130,11 @@ function gitHeadPathObjectType(cwd3, displayPath) {
|
|
|
30813
31130
|
return null;
|
|
30814
31131
|
}
|
|
30815
31132
|
}
|
|
30816
|
-
function getSessionFileChangeDirectoryFlags(
|
|
30817
|
-
const abs = tryWorkspaceDisplayToAbs(
|
|
31133
|
+
function getSessionFileChangeDirectoryFlags(cwd, displayPath) {
|
|
31134
|
+
const abs = tryWorkspaceDisplayToAbs(cwd, displayPath);
|
|
30818
31135
|
if (abs && existsSync(abs)) {
|
|
30819
31136
|
try {
|
|
30820
|
-
if (
|
|
31137
|
+
if (statSync2(abs).isDirectory()) {
|
|
30821
31138
|
return { isDirectory: true, directoryRemoved: false };
|
|
30822
31139
|
}
|
|
30823
31140
|
return { isDirectory: false, directoryRemoved: false };
|
|
@@ -30825,7 +31142,7 @@ function getSessionFileChangeDirectoryFlags(cwd3, displayPath) {
|
|
|
30825
31142
|
return { isDirectory: false, directoryRemoved: false };
|
|
30826
31143
|
}
|
|
30827
31144
|
}
|
|
30828
|
-
if (gitHeadPathObjectType(
|
|
31145
|
+
if (gitHeadPathObjectType(cwd, displayPath) === "tree") {
|
|
30829
31146
|
return { isDirectory: true, directoryRemoved: true };
|
|
30830
31147
|
}
|
|
30831
31148
|
return { isDirectory: false, directoryRemoved: false };
|
|
@@ -30839,11 +31156,13 @@ function createBridgeOnFileChange(opts) {
|
|
|
30839
31156
|
const sessionId = routing.sessionId;
|
|
30840
31157
|
const send = getSendSessionUpdate();
|
|
30841
31158
|
if (!send || !runId || !sessionId) {
|
|
30842
|
-
log2(
|
|
31159
|
+
log2(
|
|
31160
|
+
`[Bridge service] File change not forwarded (${evt.path}): session or run not wired to the bridge.`
|
|
31161
|
+
);
|
|
30843
31162
|
return;
|
|
30844
31163
|
}
|
|
30845
|
-
const
|
|
30846
|
-
const dirFlags = getSessionFileChangeDirectoryFlags(
|
|
31164
|
+
const cwd = getBridgeWorkspaceDirectory();
|
|
31165
|
+
const dirFlags = getSessionFileChangeDirectoryFlags(cwd, evt.path);
|
|
30847
31166
|
try {
|
|
30848
31167
|
send({
|
|
30849
31168
|
type: "session_file_change",
|
|
@@ -30857,7 +31176,7 @@ function createBridgeOnFileChange(opts) {
|
|
|
30857
31176
|
directoryRemoved: dirFlags.directoryRemoved
|
|
30858
31177
|
});
|
|
30859
31178
|
} catch (err) {
|
|
30860
|
-
log2(`[Bridge service]
|
|
31179
|
+
log2(`[Bridge service] Session file change failed for ${evt.path}: ${errorMessage(err)}`);
|
|
30861
31180
|
}
|
|
30862
31181
|
};
|
|
30863
31182
|
}
|
|
@@ -30884,7 +31203,9 @@ function createBridgeOnRequest(opts) {
|
|
|
30884
31203
|
}
|
|
30885
31204
|
});
|
|
30886
31205
|
} catch (err) {
|
|
30887
|
-
log2(
|
|
31206
|
+
log2(
|
|
31207
|
+
`[Bridge service] Agent protocol request forward failed (${request.method}): ${errorMessage(err)}`
|
|
31208
|
+
);
|
|
30888
31209
|
}
|
|
30889
31210
|
};
|
|
30890
31211
|
}
|
|
@@ -30916,12 +31237,12 @@ function extractDiffPath(o) {
|
|
|
30916
31237
|
}
|
|
30917
31238
|
|
|
30918
31239
|
// src/acp/hooks/extract-acp-file-diffs-from-update/push-diff.ts
|
|
30919
|
-
function pushDiffIfComplete(o,
|
|
31240
|
+
function pushDiffIfComplete(o, cwd, out) {
|
|
30920
31241
|
const t = o.type;
|
|
30921
31242
|
if (typeof t !== "string" || t.toLowerCase() !== "diff") return;
|
|
30922
31243
|
const rawPath = extractDiffPath(o);
|
|
30923
31244
|
if (!rawPath) return;
|
|
30924
|
-
const resolved = resolveWorkspaceFilePath(
|
|
31245
|
+
const resolved = resolveWorkspaceFilePath(cwd, rawPath);
|
|
30925
31246
|
if (!resolved) return;
|
|
30926
31247
|
const oldText = readOptionalTextField(o.oldText ?? o.old_text ?? o.before ?? o.oldContent);
|
|
30927
31248
|
const newText = readOptionalTextField(o.newText ?? o.new_text ?? o.after ?? o.newContent);
|
|
@@ -30940,17 +31261,17 @@ var NEST_KEYS = [
|
|
|
30940
31261
|
"data",
|
|
30941
31262
|
"arguments"
|
|
30942
31263
|
];
|
|
30943
|
-
function walkValue(value,
|
|
31264
|
+
function walkValue(value, cwd, depth, out) {
|
|
30944
31265
|
if (depth > 12 || value == null) return;
|
|
30945
31266
|
if (Array.isArray(value)) {
|
|
30946
|
-
for (const x of value) walkValue(x,
|
|
31267
|
+
for (const x of value) walkValue(x, cwd, depth + 1, out);
|
|
30947
31268
|
return;
|
|
30948
31269
|
}
|
|
30949
31270
|
if (typeof value !== "object") return;
|
|
30950
31271
|
const o = value;
|
|
30951
|
-
pushDiffIfComplete(o,
|
|
31272
|
+
pushDiffIfComplete(o, cwd, out);
|
|
30952
31273
|
if (o.type === "content" && o.content != null && typeof o.content === "object") {
|
|
30953
|
-
walkValue(o.content,
|
|
31274
|
+
walkValue(o.content, cwd, depth + 1, out);
|
|
30954
31275
|
}
|
|
30955
31276
|
for (const k of NEST_KEYS) {
|
|
30956
31277
|
if (o.type === "content" && k === "content") continue;
|
|
@@ -30958,23 +31279,23 @@ function walkValue(value, cwd3, depth, out) {
|
|
|
30958
31279
|
if (v == null) continue;
|
|
30959
31280
|
if (k === "arguments" && typeof v === "string") {
|
|
30960
31281
|
try {
|
|
30961
|
-
walkValue(JSON.parse(v),
|
|
31282
|
+
walkValue(JSON.parse(v), cwd, depth + 1, out);
|
|
30962
31283
|
} catch {
|
|
30963
31284
|
}
|
|
30964
31285
|
continue;
|
|
30965
31286
|
}
|
|
30966
|
-
walkValue(v,
|
|
31287
|
+
walkValue(v, cwd, depth + 1, out);
|
|
30967
31288
|
}
|
|
30968
31289
|
}
|
|
30969
31290
|
|
|
30970
31291
|
// src/acp/hooks/extract-acp-file-diffs-from-update/extract.ts
|
|
30971
|
-
function extractAcpFileDiffsFromUpdate(update,
|
|
31292
|
+
function extractAcpFileDiffsFromUpdate(update, cwd) {
|
|
30972
31293
|
if (!update || typeof update !== "object") return [];
|
|
30973
31294
|
const u = update;
|
|
30974
31295
|
const out = [];
|
|
30975
31296
|
const content = u.content;
|
|
30976
31297
|
if (Array.isArray(content)) {
|
|
30977
|
-
for (const x of content) walkValue(x,
|
|
31298
|
+
for (const x of content) walkValue(x, cwd, 0, out);
|
|
30978
31299
|
}
|
|
30979
31300
|
const byPath = /* @__PURE__ */ new Map();
|
|
30980
31301
|
for (const d of out) byPath.set(d.path, d);
|
|
@@ -30982,18 +31303,18 @@ function extractAcpFileDiffsFromUpdate(update, cwd3) {
|
|
|
30982
31303
|
}
|
|
30983
31304
|
|
|
30984
31305
|
// src/acp/hooks/extract-tool-target-paths.ts
|
|
30985
|
-
function addPath(
|
|
31306
|
+
function addPath(cwd, raw, out) {
|
|
30986
31307
|
if (typeof raw !== "string") return;
|
|
30987
31308
|
const trimmed2 = raw.trim();
|
|
30988
31309
|
if (!trimmed2) return;
|
|
30989
|
-
const resolved = resolveWorkspaceFilePath(
|
|
31310
|
+
const resolved = resolveWorkspaceFilePath(cwd, trimmed2);
|
|
30990
31311
|
if (!resolved) return;
|
|
30991
31312
|
out.add(resolved.display);
|
|
30992
31313
|
}
|
|
30993
|
-
function walkLocations(
|
|
31314
|
+
function walkLocations(cwd, loc, out) {
|
|
30994
31315
|
if (!Array.isArray(loc)) return;
|
|
30995
31316
|
for (const item of loc) {
|
|
30996
|
-
if (item && typeof item === "object") addPath(
|
|
31317
|
+
if (item && typeof item === "object") addPath(cwd, item.path, out);
|
|
30997
31318
|
}
|
|
30998
31319
|
}
|
|
30999
31320
|
var PATH_KEYS = [
|
|
@@ -31006,56 +31327,56 @@ var PATH_KEYS = [
|
|
|
31006
31327
|
"file_path",
|
|
31007
31328
|
"target_file"
|
|
31008
31329
|
];
|
|
31009
|
-
function collectFromObject(
|
|
31330
|
+
function collectFromObject(cwd, obj, out, depth) {
|
|
31010
31331
|
if (depth > 10) return;
|
|
31011
31332
|
for (const k of PATH_KEYS) {
|
|
31012
|
-
if (k in obj) addPath(
|
|
31333
|
+
if (k in obj) addPath(cwd, obj[k], out);
|
|
31013
31334
|
}
|
|
31014
31335
|
for (const v of Object.values(obj)) {
|
|
31015
|
-
if (v != null && typeof v === "object") collectUnknown(
|
|
31336
|
+
if (v != null && typeof v === "object") collectUnknown(cwd, v, out, depth + 1);
|
|
31016
31337
|
}
|
|
31017
31338
|
}
|
|
31018
|
-
function collectUnknown(
|
|
31339
|
+
function collectUnknown(cwd, v, out, depth) {
|
|
31019
31340
|
if (depth > 10 || v == null) return;
|
|
31020
31341
|
if (Array.isArray(v)) {
|
|
31021
|
-
for (const x of v) collectUnknown(
|
|
31342
|
+
for (const x of v) collectUnknown(cwd, x, out, depth + 1);
|
|
31022
31343
|
return;
|
|
31023
31344
|
}
|
|
31024
|
-
if (typeof v === "object") collectFromObject(
|
|
31345
|
+
if (typeof v === "object") collectFromObject(cwd, v, out, depth);
|
|
31025
31346
|
}
|
|
31026
|
-
function walkContentArray(
|
|
31347
|
+
function walkContentArray(cwd, content, out) {
|
|
31027
31348
|
if (!Array.isArray(content)) return;
|
|
31028
31349
|
for (const item of content) {
|
|
31029
31350
|
if (!item || typeof item !== "object") continue;
|
|
31030
31351
|
const o = item;
|
|
31031
31352
|
if (typeof o.type === "string" && o.type.toLowerCase() === "diff") {
|
|
31032
|
-
for (const k of PATH_KEYS) if (k in o) addPath(
|
|
31353
|
+
for (const k of PATH_KEYS) if (k in o) addPath(cwd, o[k], out);
|
|
31033
31354
|
}
|
|
31034
31355
|
if (o.type === "content" && o.content != null && typeof o.content === "object") {
|
|
31035
31356
|
const inner = o.content;
|
|
31036
31357
|
if (typeof inner.type === "string" && inner.type.toLowerCase() === "diff") {
|
|
31037
|
-
for (const k of PATH_KEYS) if (k in inner) addPath(
|
|
31358
|
+
for (const k of PATH_KEYS) if (k in inner) addPath(cwd, inner[k], out);
|
|
31038
31359
|
}
|
|
31039
31360
|
}
|
|
31040
31361
|
}
|
|
31041
31362
|
}
|
|
31042
|
-
function extractToolTargetDisplayPaths(update,
|
|
31363
|
+
function extractToolTargetDisplayPaths(update, cwd) {
|
|
31043
31364
|
const out = /* @__PURE__ */ new Set();
|
|
31044
31365
|
if (!update || typeof update !== "object") return [];
|
|
31045
31366
|
const u = update;
|
|
31046
|
-
walkLocations(
|
|
31047
|
-
walkLocations(
|
|
31048
|
-
walkLocations(
|
|
31367
|
+
walkLocations(cwd, u.locations, out);
|
|
31368
|
+
walkLocations(cwd, u.fileLocations, out);
|
|
31369
|
+
walkLocations(cwd, u.file_locations, out);
|
|
31049
31370
|
const tc = u.toolCall ?? u.tool_call;
|
|
31050
31371
|
if (tc && typeof tc.arguments === "string") {
|
|
31051
31372
|
try {
|
|
31052
31373
|
const parsed = JSON.parse(tc.arguments);
|
|
31053
|
-
collectUnknown(
|
|
31374
|
+
collectUnknown(cwd, parsed, out, 0);
|
|
31054
31375
|
} catch {
|
|
31055
31376
|
}
|
|
31056
31377
|
}
|
|
31057
|
-
walkContentArray(
|
|
31058
|
-
collectFromObject(
|
|
31378
|
+
walkContentArray(cwd, u.content, out);
|
|
31379
|
+
collectFromObject(cwd, u, out, 0);
|
|
31059
31380
|
return [...out];
|
|
31060
31381
|
}
|
|
31061
31382
|
|
|
@@ -31121,7 +31442,7 @@ var PathSnapshotTracker = class {
|
|
|
31121
31442
|
this.beforeByToolKey.delete(toolKey);
|
|
31122
31443
|
this.accumulatedPathsByToolKey.delete(toolKey);
|
|
31123
31444
|
}
|
|
31124
|
-
captureBeforeFromDisk(toolKey, paths,
|
|
31445
|
+
captureBeforeFromDisk(toolKey, paths, cwd) {
|
|
31125
31446
|
if (paths.length === 0) return;
|
|
31126
31447
|
let m = this.beforeByToolKey.get(toolKey);
|
|
31127
31448
|
if (!m) {
|
|
@@ -31130,10 +31451,10 @@ var PathSnapshotTracker = class {
|
|
|
31130
31451
|
}
|
|
31131
31452
|
for (const p of paths) {
|
|
31132
31453
|
if (m.has(p)) continue;
|
|
31133
|
-
m.set(p, readUtf8WorkspaceFile(
|
|
31454
|
+
m.set(p, readUtf8WorkspaceFile(cwd, p));
|
|
31134
31455
|
}
|
|
31135
31456
|
}
|
|
31136
|
-
ensureBeforeFromHeadForMissing(toolKey, paths,
|
|
31457
|
+
ensureBeforeFromHeadForMissing(toolKey, paths, cwd) {
|
|
31137
31458
|
let m = this.beforeByToolKey.get(toolKey);
|
|
31138
31459
|
if (!m) {
|
|
31139
31460
|
m = /* @__PURE__ */ new Map();
|
|
@@ -31141,10 +31462,10 @@ var PathSnapshotTracker = class {
|
|
|
31141
31462
|
}
|
|
31142
31463
|
for (const p of paths) {
|
|
31143
31464
|
if (m.has(p)) continue;
|
|
31144
|
-
m.set(p, readGitHeadBlob(
|
|
31465
|
+
m.set(p, readGitHeadBlob(cwd, p));
|
|
31145
31466
|
}
|
|
31146
31467
|
}
|
|
31147
|
-
flushPathSnapshots(toolKey,
|
|
31468
|
+
flushPathSnapshots(toolKey, cwd, sentPaths, send, runId, sessionId, log2) {
|
|
31148
31469
|
const t = this.debouncers.get(toolKey);
|
|
31149
31470
|
if (t) clearTimeout(t);
|
|
31150
31471
|
this.debouncers.delete(toolKey);
|
|
@@ -31153,10 +31474,10 @@ var PathSnapshotTracker = class {
|
|
|
31153
31474
|
this.beforeByToolKey.delete(toolKey);
|
|
31154
31475
|
if (!send || !runId || !sessionId) return;
|
|
31155
31476
|
for (const [displayPath, oldText] of beforeMap) {
|
|
31156
|
-
const newText = readUtf8WorkspaceFile(
|
|
31477
|
+
const newText = readUtf8WorkspaceFile(cwd, displayPath);
|
|
31157
31478
|
if (oldText === newText) continue;
|
|
31158
31479
|
const patchContent = editSnippetToUnifiedDiff(displayPath, oldText, newText);
|
|
31159
|
-
const dirFlags = getSessionFileChangeDirectoryFlags(
|
|
31480
|
+
const dirFlags = getSessionFileChangeDirectoryFlags(cwd, displayPath);
|
|
31160
31481
|
try {
|
|
31161
31482
|
send({
|
|
31162
31483
|
type: "session_file_change",
|
|
@@ -31171,35 +31492,35 @@ var PathSnapshotTracker = class {
|
|
|
31171
31492
|
});
|
|
31172
31493
|
sentPaths.add(displayPath);
|
|
31173
31494
|
} catch (err) {
|
|
31174
|
-
log2(`[Bridge service]
|
|
31495
|
+
log2(`[Bridge service] Session file change failed for ${displayPath}: ${errorMessage(err)}`);
|
|
31175
31496
|
}
|
|
31176
31497
|
}
|
|
31177
31498
|
}
|
|
31178
|
-
scheduleDebouncedFlush(toolKey,
|
|
31499
|
+
scheduleDebouncedFlush(toolKey, cwd, sentPaths, send, runId, sessionId, log2) {
|
|
31179
31500
|
const prev = this.debouncers.get(toolKey);
|
|
31180
31501
|
if (prev) clearTimeout(prev);
|
|
31181
31502
|
this.debouncers.set(
|
|
31182
31503
|
toolKey,
|
|
31183
31504
|
setTimeout(() => {
|
|
31184
31505
|
this.debouncers.delete(toolKey);
|
|
31185
|
-
this.flushPathSnapshots(toolKey,
|
|
31506
|
+
this.flushPathSnapshots(toolKey, cwd, sentPaths, send, runId, sessionId, log2);
|
|
31186
31507
|
}, PATH_SNAPSHOT_DEBOUNCE_MS)
|
|
31187
31508
|
);
|
|
31188
31509
|
}
|
|
31189
|
-
handleToolCallLifecycle(updateKind, toolKey, toolPaths, status,
|
|
31510
|
+
handleToolCallLifecycle(updateKind, toolKey, toolPaths, status, cwd, sentPaths, send, runId, sessionId, log2) {
|
|
31190
31511
|
if (updateKind === "tool_call") {
|
|
31191
31512
|
this.resetToolSnapshots(toolKey);
|
|
31192
31513
|
}
|
|
31193
31514
|
if (updateKind === "tool_call") {
|
|
31194
|
-
this.captureBeforeFromDisk(toolKey, toolPaths,
|
|
31515
|
+
this.captureBeforeFromDisk(toolKey, toolPaths, cwd);
|
|
31195
31516
|
} else if (updateKind === "tool_call_update") {
|
|
31196
31517
|
if (isCompletedToolStatus(status)) {
|
|
31197
|
-
this.ensureBeforeFromHeadForMissing(toolKey, toolPaths,
|
|
31198
|
-
this.flushPathSnapshots(toolKey,
|
|
31518
|
+
this.ensureBeforeFromHeadForMissing(toolKey, toolPaths, cwd);
|
|
31519
|
+
this.flushPathSnapshots(toolKey, cwd, sentPaths, send, runId, sessionId, log2);
|
|
31199
31520
|
} else {
|
|
31200
|
-
this.captureBeforeFromDisk(toolKey, toolPaths,
|
|
31521
|
+
this.captureBeforeFromDisk(toolKey, toolPaths, cwd);
|
|
31201
31522
|
if (this.beforeByToolKey.has(toolKey)) {
|
|
31202
|
-
this.scheduleDebouncedFlush(toolKey,
|
|
31523
|
+
this.scheduleDebouncedFlush(toolKey, cwd, sentPaths, send, runId, sessionId, log2);
|
|
31203
31524
|
}
|
|
31204
31525
|
}
|
|
31205
31526
|
}
|
|
@@ -31207,11 +31528,11 @@ var PathSnapshotTracker = class {
|
|
|
31207
31528
|
};
|
|
31208
31529
|
|
|
31209
31530
|
// src/acp/hooks/bridge-on-session-update/send-structured-file-changes.ts
|
|
31210
|
-
function sendExtractedDiffsAsSessionFileChanges(diffs, send,
|
|
31531
|
+
function sendExtractedDiffsAsSessionFileChanges(diffs, send, cwd, sessionId, runId, sentPaths, log2) {
|
|
31211
31532
|
for (const d of diffs) {
|
|
31212
31533
|
try {
|
|
31213
31534
|
const patchContent = editSnippetToUnifiedDiff(d.path, d.oldText, d.newText);
|
|
31214
|
-
const dirFlags = getSessionFileChangeDirectoryFlags(
|
|
31535
|
+
const dirFlags = getSessionFileChangeDirectoryFlags(cwd, d.path);
|
|
31215
31536
|
send({
|
|
31216
31537
|
type: "session_file_change",
|
|
31217
31538
|
sessionId,
|
|
@@ -31225,19 +31546,19 @@ function sendExtractedDiffsAsSessionFileChanges(diffs, send, cwd3, sessionId, ru
|
|
|
31225
31546
|
});
|
|
31226
31547
|
sentPaths.add(d.path);
|
|
31227
31548
|
} catch (err) {
|
|
31228
|
-
log2(`[Bridge service]
|
|
31549
|
+
log2(`[Bridge service] Session file change failed for ${d.path}: ${errorMessage(err)}`);
|
|
31229
31550
|
}
|
|
31230
31551
|
}
|
|
31231
31552
|
}
|
|
31232
|
-
function sendGitHeadVsWorkspaceForToolPaths(mergedPaths, sentPaths, send,
|
|
31553
|
+
function sendGitHeadVsWorkspaceForToolPaths(mergedPaths, sentPaths, send, cwd, sessionId, runId, log2) {
|
|
31233
31554
|
for (const displayPath of mergedPaths) {
|
|
31234
31555
|
if (sentPaths.has(displayPath)) continue;
|
|
31235
|
-
const oldText = readGitHeadBlob(
|
|
31236
|
-
const newText = readUtf8WorkspaceFile(
|
|
31556
|
+
const oldText = readGitHeadBlob(cwd, displayPath);
|
|
31557
|
+
const newText = readUtf8WorkspaceFile(cwd, displayPath);
|
|
31237
31558
|
if (oldText === newText) continue;
|
|
31238
31559
|
try {
|
|
31239
31560
|
const patchContent = editSnippetToUnifiedDiff(displayPath, oldText, newText);
|
|
31240
|
-
const dirFlags = getSessionFileChangeDirectoryFlags(
|
|
31561
|
+
const dirFlags = getSessionFileChangeDirectoryFlags(cwd, displayPath);
|
|
31241
31562
|
send({
|
|
31242
31563
|
type: "session_file_change",
|
|
31243
31564
|
sessionId,
|
|
@@ -31251,7 +31572,7 @@ function sendGitHeadVsWorkspaceForToolPaths(mergedPaths, sentPaths, send, cwd3,
|
|
|
31251
31572
|
});
|
|
31252
31573
|
sentPaths.add(displayPath);
|
|
31253
31574
|
} catch (err) {
|
|
31254
|
-
log2(`[Bridge service]
|
|
31575
|
+
log2(`[Bridge service] Session file change failed for ${displayPath}: ${errorMessage(err)}`);
|
|
31255
31576
|
}
|
|
31256
31577
|
}
|
|
31257
31578
|
}
|
|
@@ -31264,7 +31585,7 @@ function createBridgeOnSessionUpdate(opts) {
|
|
|
31264
31585
|
const runId = routing.runId;
|
|
31265
31586
|
const sessionId = routing.sessionId;
|
|
31266
31587
|
pathTracker.onRunIdChanged(runId);
|
|
31267
|
-
const
|
|
31588
|
+
const cwd = getBridgeWorkspaceDirectory();
|
|
31268
31589
|
const send = getSendSessionUpdate();
|
|
31269
31590
|
const sentFileChangePaths = /* @__PURE__ */ new Set();
|
|
31270
31591
|
const p = params;
|
|
@@ -31272,7 +31593,7 @@ function createBridgeOnSessionUpdate(opts) {
|
|
|
31272
31593
|
const isCompletedToolCallUpdate = updateKind === "tool_call_update" && isCompletedToolStatus(p.status);
|
|
31273
31594
|
const toolName = p.toolCall?.name ?? p.tool_call?.name ?? "";
|
|
31274
31595
|
const isToolUpdate = updateKind === "tool_call" || updateKind === "tool_call_update" || typeof toolName === "string" && toolName.length > 0;
|
|
31275
|
-
const toolPaths = isToolUpdate ? extractToolTargetDisplayPaths(params,
|
|
31596
|
+
const toolPaths = isToolUpdate ? extractToolTargetDisplayPaths(params, cwd) : [];
|
|
31276
31597
|
const toolKey = isToolUpdate ? pathTracker.resolveToolKey(params, updateKind) : "";
|
|
31277
31598
|
if (updateKind === "tool_call") {
|
|
31278
31599
|
pathTracker.resetToolSnapshots(toolKey);
|
|
@@ -31287,7 +31608,7 @@ function createBridgeOnSessionUpdate(opts) {
|
|
|
31287
31608
|
toolKey,
|
|
31288
31609
|
toolPaths,
|
|
31289
31610
|
p.status,
|
|
31290
|
-
|
|
31611
|
+
cwd,
|
|
31291
31612
|
sentFileChangePaths,
|
|
31292
31613
|
deliver,
|
|
31293
31614
|
runId ?? "",
|
|
@@ -31295,18 +31616,18 @@ function createBridgeOnSessionUpdate(opts) {
|
|
|
31295
31616
|
log2
|
|
31296
31617
|
);
|
|
31297
31618
|
}
|
|
31298
|
-
const diffs = extractAcpFileDiffsFromUpdate(params,
|
|
31619
|
+
const diffs = extractAcpFileDiffsFromUpdate(params, cwd);
|
|
31299
31620
|
if (diffs.length > 0 && send && runId && sessionId) {
|
|
31300
|
-
sendExtractedDiffsAsSessionFileChanges(diffs, send,
|
|
31621
|
+
sendExtractedDiffsAsSessionFileChanges(diffs, send, cwd, sessionId, runId, sentFileChangePaths, log2);
|
|
31301
31622
|
} else if (diffs.length > 0) {
|
|
31302
31623
|
log2(
|
|
31303
|
-
`[Bridge service]
|
|
31624
|
+
`[Bridge service] Agent file diff(s) not forwarded (${diffs.length}): session or run not wired to the bridge.`
|
|
31304
31625
|
);
|
|
31305
31626
|
}
|
|
31306
31627
|
if (isCompletedToolCallUpdate && send && runId && sessionId) {
|
|
31307
31628
|
const acc = pathTracker.accumulatedPathsByToolKey.get(toolKey);
|
|
31308
31629
|
const merged = [.../* @__PURE__ */ new Set([...acc ? [...acc] : [], ...toolPaths])];
|
|
31309
|
-
sendGitHeadVsWorkspaceForToolPaths(merged, sentFileChangePaths, send,
|
|
31630
|
+
sendGitHeadVsWorkspaceForToolPaths(merged, sentFileChangePaths, send, cwd, sessionId, runId, log2);
|
|
31310
31631
|
pathTracker.accumulatedPathsByToolKey.delete(toolKey);
|
|
31311
31632
|
}
|
|
31312
31633
|
if (runId && send) {
|
|
@@ -31319,7 +31640,9 @@ function createBridgeOnSessionUpdate(opts) {
|
|
|
31319
31640
|
payload: params
|
|
31320
31641
|
});
|
|
31321
31642
|
} catch (err) {
|
|
31322
|
-
log2(
|
|
31643
|
+
log2(
|
|
31644
|
+
`[Bridge service] Session update send failed (${formatSessionUpdateKindForLog(updateKind)}): ${errorMessage(err)}`
|
|
31645
|
+
);
|
|
31323
31646
|
}
|
|
31324
31647
|
}
|
|
31325
31648
|
};
|
|
@@ -31336,11 +31659,11 @@ function buildAcpSessionBridgeHooks(opts) {
|
|
|
31336
31659
|
|
|
31337
31660
|
// src/acp/ensure-acp-client.ts
|
|
31338
31661
|
async function ensureAcpClient(options) {
|
|
31339
|
-
const { state, preferredAgentType, mode, cwd
|
|
31340
|
-
const targetCwd =
|
|
31341
|
-
|
|
31662
|
+
const { state, preferredAgentType, mode, cwd, routing, sendSessionUpdate, sendRequest, log: log2 } = options;
|
|
31663
|
+
const targetCwd = path9.resolve(
|
|
31664
|
+
cwd != null && String(cwd).trim() !== "" ? String(cwd).trim() : getBridgeWorkspaceDirectory()
|
|
31342
31665
|
);
|
|
31343
|
-
if (state.acpHandle && state.lastAcpCwd != null &&
|
|
31666
|
+
if (state.acpHandle && state.lastAcpCwd != null && path9.resolve(state.lastAcpCwd) !== path9.resolve(targetCwd)) {
|
|
31344
31667
|
try {
|
|
31345
31668
|
state.acpHandle.disconnect();
|
|
31346
31669
|
} catch {
|
|
@@ -31352,7 +31675,7 @@ async function ensureAcpClient(options) {
|
|
|
31352
31675
|
const resolved = resolveAgentCommand(preferredAgentType);
|
|
31353
31676
|
if (!resolved) {
|
|
31354
31677
|
log2(
|
|
31355
|
-
`[
|
|
31678
|
+
`[Agent] No local agent type (${preferredAgentType === null ? "none" : `"${preferredAgentType}"`}). Send agent type on prompts or agent configuration after identify.`
|
|
31356
31679
|
);
|
|
31357
31680
|
state.lastAcpStartError = "No agent type: ensure the app sends agentType on prompts or agent_config for this bridge.";
|
|
31358
31681
|
return null;
|
|
@@ -31371,15 +31694,15 @@ async function ensureAcpClient(options) {
|
|
|
31371
31694
|
if (!state.acpStartPromise) {
|
|
31372
31695
|
let statOk = false;
|
|
31373
31696
|
try {
|
|
31374
|
-
const st =
|
|
31697
|
+
const st = fs5.statSync(targetCwd);
|
|
31375
31698
|
statOk = st.isDirectory();
|
|
31376
31699
|
if (!statOk) {
|
|
31377
31700
|
state.lastAcpStartError = `Agent cwd is not a directory: ${targetCwd}`;
|
|
31378
|
-
log2(`[
|
|
31701
|
+
log2(`[Agent] ${state.lastAcpStartError}`);
|
|
31379
31702
|
}
|
|
31380
31703
|
} catch {
|
|
31381
31704
|
state.lastAcpStartError = `Agent cwd missing or inaccessible: ${targetCwd}`;
|
|
31382
|
-
log2(`[
|
|
31705
|
+
log2(`[Agent] ${state.lastAcpStartError}`);
|
|
31383
31706
|
}
|
|
31384
31707
|
if (!statOk) {
|
|
31385
31708
|
return null;
|
|
@@ -31404,7 +31727,7 @@ async function ensureAcpClient(options) {
|
|
|
31404
31727
|
return h;
|
|
31405
31728
|
}).catch((err) => {
|
|
31406
31729
|
state.lastAcpStartError = errorMessage(err);
|
|
31407
|
-
log2(`[
|
|
31730
|
+
log2(`[Agent] Failed to start: ${state.lastAcpStartError}`);
|
|
31408
31731
|
state.acpStartPromise = null;
|
|
31409
31732
|
state.acpAgentKey = null;
|
|
31410
31733
|
return null;
|
|
@@ -31440,10 +31763,9 @@ async function createAcpManager(options) {
|
|
|
31440
31763
|
runId,
|
|
31441
31764
|
mode,
|
|
31442
31765
|
agentType,
|
|
31443
|
-
cwd
|
|
31766
|
+
cwd,
|
|
31444
31767
|
sendResult,
|
|
31445
|
-
sendSessionUpdate
|
|
31446
|
-
collectSessionDiffAfterTurn
|
|
31768
|
+
sendSessionUpdate
|
|
31447
31769
|
} = opts;
|
|
31448
31770
|
const preferredForPrompt = agentType ?? backendFallbackAgentType ?? null;
|
|
31449
31771
|
pendingCancelRunId = void 0;
|
|
@@ -31455,7 +31777,7 @@ async function createAcpManager(options) {
|
|
|
31455
31777
|
state,
|
|
31456
31778
|
preferredAgentType: preferredForPrompt,
|
|
31457
31779
|
mode,
|
|
31458
|
-
cwd
|
|
31780
|
+
cwd,
|
|
31459
31781
|
routing: promptRouting,
|
|
31460
31782
|
sendSessionUpdate,
|
|
31461
31783
|
sendRequest: sendSessionUpdate,
|
|
@@ -31496,9 +31818,9 @@ async function createAcpManager(options) {
|
|
|
31496
31818
|
promptId,
|
|
31497
31819
|
sessionId,
|
|
31498
31820
|
runId,
|
|
31821
|
+
agentCwd: cwd,
|
|
31499
31822
|
sendResult,
|
|
31500
31823
|
sendSessionUpdate,
|
|
31501
|
-
collectSessionDiffAfterTurn,
|
|
31502
31824
|
log: log2
|
|
31503
31825
|
});
|
|
31504
31826
|
}
|
|
@@ -31517,7 +31839,7 @@ async function createAcpManager(options) {
|
|
|
31517
31839
|
await handle.cancel();
|
|
31518
31840
|
return true;
|
|
31519
31841
|
} catch (err) {
|
|
31520
|
-
log2(`[
|
|
31842
|
+
log2(`[Agent] Cancel failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
31521
31843
|
return false;
|
|
31522
31844
|
}
|
|
31523
31845
|
}
|
|
@@ -31554,7 +31876,9 @@ var handleBridgeIdentified = (msg, deps) => {
|
|
|
31554
31876
|
try {
|
|
31555
31877
|
await deps.reportAutoDetectedAgents?.();
|
|
31556
31878
|
} catch (e) {
|
|
31557
|
-
deps.log(
|
|
31879
|
+
deps.log(
|
|
31880
|
+
`[Bridge service] Auto-detect agents failed: ${e instanceof Error ? e.message : String(e)}`
|
|
31881
|
+
);
|
|
31558
31882
|
}
|
|
31559
31883
|
})();
|
|
31560
31884
|
});
|
|
@@ -31562,7 +31886,9 @@ var handleBridgeIdentified = (msg, deps) => {
|
|
|
31562
31886
|
try {
|
|
31563
31887
|
deps.sendLocalSkillsReport?.();
|
|
31564
31888
|
} catch (e) {
|
|
31565
|
-
deps.log(
|
|
31889
|
+
deps.log(
|
|
31890
|
+
`[Bridge service] Local skills report failed: ${e instanceof Error ? e.message : String(e)}`
|
|
31891
|
+
);
|
|
31566
31892
|
}
|
|
31567
31893
|
});
|
|
31568
31894
|
};
|
|
@@ -31579,12 +31905,12 @@ var handleAgentConfigMessage = (msg, deps) => {
|
|
|
31579
31905
|
};
|
|
31580
31906
|
|
|
31581
31907
|
// src/acp/from-bridge/handle-bridge-prompt.ts
|
|
31582
|
-
import * as
|
|
31908
|
+
import * as path11 from "node:path";
|
|
31583
31909
|
import { execFile as execFile3 } from "node:child_process";
|
|
31584
31910
|
import { promisify as promisify3 } from "node:util";
|
|
31585
31911
|
|
|
31586
31912
|
// src/git/bridge-queue-key.ts
|
|
31587
|
-
import * as
|
|
31913
|
+
import * as path10 from "node:path";
|
|
31588
31914
|
import { createHash } from "node:crypto";
|
|
31589
31915
|
function normalizeCanonicalGitUrl(url2) {
|
|
31590
31916
|
let s = url2.trim();
|
|
@@ -31612,13 +31938,13 @@ function canonicalUrlToRepoIdSync(url2) {
|
|
|
31612
31938
|
return createHash("sha256").update(normalized).digest("hex").slice(0, 32);
|
|
31613
31939
|
}
|
|
31614
31940
|
function fallbackRepoIdFromPath(absPath) {
|
|
31615
|
-
return createHash("sha256").update(
|
|
31941
|
+
return createHash("sha256").update(path10.resolve(absPath)).digest("hex").slice(0, 32);
|
|
31616
31942
|
}
|
|
31617
31943
|
async function resolveBridgeQueueBindFields(options) {
|
|
31618
31944
|
const { effectiveCwd, worktreePaths, primaryRepoRoots, log: log2 } = options;
|
|
31619
|
-
const cwdAbs = worktreePaths.length > 0 ?
|
|
31945
|
+
const cwdAbs = worktreePaths.length > 0 ? path10.resolve(worktreePaths[0]) : path10.resolve(effectiveCwd);
|
|
31620
31946
|
if (!primaryRepoRoots.length) {
|
|
31621
|
-
log2("[Bridge service]
|
|
31947
|
+
log2("[Bridge service] Prompt queue bind skipped: no Git repository roots under the working directory.");
|
|
31622
31948
|
return null;
|
|
31623
31949
|
}
|
|
31624
31950
|
let primaryRoot = primaryRepoRoots[0];
|
|
@@ -31638,115 +31964,11 @@ async function resolveBridgeQueueBindFields(options) {
|
|
|
31638
31964
|
return { canonicalQueueKey, repoId, cwdAbs };
|
|
31639
31965
|
}
|
|
31640
31966
|
|
|
31641
|
-
// src/git/pre-turn-snapshot.ts
|
|
31642
|
-
import * as fs4 from "node:fs";
|
|
31643
|
-
import * as path9 from "node:path";
|
|
31644
|
-
import { execFile as execFile2 } from "node:child_process";
|
|
31645
|
-
import { promisify as promisify2 } from "node:util";
|
|
31646
|
-
var execFileAsync2 = promisify2(execFile2);
|
|
31647
|
-
function snapshotsDirForCwd(agentCwd) {
|
|
31648
|
-
return path9.join(agentCwd, ".buildautomaton", "snapshots");
|
|
31649
|
-
}
|
|
31650
|
-
async function gitStashCreate2(repoRoot, log2) {
|
|
31651
|
-
try {
|
|
31652
|
-
const { stdout } = await execFileAsync2("git", ["stash", "create"], {
|
|
31653
|
-
cwd: repoRoot,
|
|
31654
|
-
maxBuffer: 10 * 1024 * 1024
|
|
31655
|
-
});
|
|
31656
|
-
return stdout.trim();
|
|
31657
|
-
} catch (e) {
|
|
31658
|
-
log2(`[snapshot] git stash create failed in ${repoRoot}: ${e instanceof Error ? e.message : String(e)}`);
|
|
31659
|
-
return "";
|
|
31660
|
-
}
|
|
31661
|
-
}
|
|
31662
|
-
async function gitRun(repoRoot, args, log2, label) {
|
|
31663
|
-
try {
|
|
31664
|
-
await execFileAsync2("git", args, { cwd: repoRoot, maxBuffer: 10 * 1024 * 1024 });
|
|
31665
|
-
return { ok: true };
|
|
31666
|
-
} catch (e) {
|
|
31667
|
-
const msg = e instanceof Error ? e.message : String(e);
|
|
31668
|
-
log2(`[snapshot] git ${label} failed in ${repoRoot}: ${msg}`);
|
|
31669
|
-
return { ok: false, error: msg };
|
|
31670
|
-
}
|
|
31671
|
-
}
|
|
31672
|
-
async function resolveSnapshotRepoRoots(options) {
|
|
31673
|
-
const { worktreePaths, fallbackCwd, log: log2 } = options;
|
|
31674
|
-
if (worktreePaths?.length) {
|
|
31675
|
-
const uniq = [...new Set(worktreePaths.map((p) => path9.resolve(p)))];
|
|
31676
|
-
return uniq;
|
|
31677
|
-
}
|
|
31678
|
-
try {
|
|
31679
|
-
const repos = await discoverGitReposUnderRoot(fallbackCwd);
|
|
31680
|
-
return repos.map((r) => r.absolutePath);
|
|
31681
|
-
} catch (e) {
|
|
31682
|
-
log2(`[snapshot] discover repos failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
31683
|
-
return [];
|
|
31684
|
-
}
|
|
31685
|
-
}
|
|
31686
|
-
async function capturePreTurnSnapshot(options) {
|
|
31687
|
-
const { runId, repoRoots, agentCwd, log: log2 } = options;
|
|
31688
|
-
if (!runId || !repoRoots.length) {
|
|
31689
|
-
return { ok: false, error: "No git repos to snapshot" };
|
|
31690
|
-
}
|
|
31691
|
-
const repos = [];
|
|
31692
|
-
for (const root of repoRoots) {
|
|
31693
|
-
const stashSha = await gitStashCreate2(root, log2);
|
|
31694
|
-
repos.push({ path: root, stashSha });
|
|
31695
|
-
}
|
|
31696
|
-
const dir = snapshotsDirForCwd(agentCwd);
|
|
31697
|
-
try {
|
|
31698
|
-
fs4.mkdirSync(dir, { recursive: true });
|
|
31699
|
-
} catch (e) {
|
|
31700
|
-
return { ok: false, error: e instanceof Error ? e.message : String(e) };
|
|
31701
|
-
}
|
|
31702
|
-
const payload = {
|
|
31703
|
-
runId,
|
|
31704
|
-
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
31705
|
-
repos
|
|
31706
|
-
};
|
|
31707
|
-
const filePath = path9.join(dir, `${runId}.json`);
|
|
31708
|
-
try {
|
|
31709
|
-
fs4.writeFileSync(filePath, JSON.stringify(payload, null, 2), "utf8");
|
|
31710
|
-
} catch (e) {
|
|
31711
|
-
return { ok: false, error: e instanceof Error ? e.message : String(e) };
|
|
31712
|
-
}
|
|
31713
|
-
log2(`[snapshot] Saved pre-turn snapshot ${runId.slice(0, 8)}\u2026 (${repos.length} repo(s)) \u2192 ${filePath}`);
|
|
31714
|
-
return { ok: true, filePath, repos };
|
|
31715
|
-
}
|
|
31716
|
-
async function applyPreTurnSnapshot(filePath, log2) {
|
|
31717
|
-
let data;
|
|
31718
|
-
try {
|
|
31719
|
-
const raw = fs4.readFileSync(filePath, "utf8");
|
|
31720
|
-
data = JSON.parse(raw);
|
|
31721
|
-
} catch (e) {
|
|
31722
|
-
return { ok: false, error: e instanceof Error ? e.message : String(e) };
|
|
31723
|
-
}
|
|
31724
|
-
if (!Array.isArray(data.repos)) {
|
|
31725
|
-
return { ok: false, error: "Invalid snapshot file" };
|
|
31726
|
-
}
|
|
31727
|
-
for (const r of data.repos) {
|
|
31728
|
-
if (!r.path) continue;
|
|
31729
|
-
const reset = await gitRun(r.path, ["reset", "--hard", "HEAD"], log2, "reset --hard");
|
|
31730
|
-
if (!reset.ok) return reset;
|
|
31731
|
-
const clean = await gitRun(r.path, ["clean", "-fd"], log2, "clean -fd");
|
|
31732
|
-
if (!clean.ok) return clean;
|
|
31733
|
-
if (r.stashSha) {
|
|
31734
|
-
const ap = await gitRun(r.path, ["stash", "apply", r.stashSha], log2, "stash apply");
|
|
31735
|
-
if (!ap.ok) return ap;
|
|
31736
|
-
}
|
|
31737
|
-
}
|
|
31738
|
-
log2(`[snapshot] Restored pre-turn state for ${data.runId.slice(0, 8)}\u2026`);
|
|
31739
|
-
return { ok: true };
|
|
31740
|
-
}
|
|
31741
|
-
function snapshotFilePath(agentCwd, runId) {
|
|
31742
|
-
return path9.join(snapshotsDirForCwd(agentCwd), `${runId}.json`);
|
|
31743
|
-
}
|
|
31744
|
-
|
|
31745
31967
|
// src/acp/from-bridge/handle-bridge-prompt.ts
|
|
31746
31968
|
var execFileAsync3 = promisify3(execFile3);
|
|
31747
|
-
async function readGitBranch(
|
|
31969
|
+
async function readGitBranch(cwd) {
|
|
31748
31970
|
try {
|
|
31749
|
-
const { stdout } = await execFileAsync3("git", ["branch", "--show-current"], { cwd
|
|
31971
|
+
const { stdout } = await execFileAsync3("git", ["branch", "--show-current"], { cwd, maxBuffer: 64 * 1024 });
|
|
31750
31972
|
const b = stdout.trim();
|
|
31751
31973
|
return b || null;
|
|
31752
31974
|
} catch {
|
|
@@ -31759,7 +31981,7 @@ function handleBridgePrompt(msg, deps) {
|
|
|
31759
31981
|
const promptText = typeof rawPrompt === "string" ? rawPrompt : rawPrompt != null ? String(rawPrompt) : "";
|
|
31760
31982
|
if (!promptText.trim()) {
|
|
31761
31983
|
log2(
|
|
31762
|
-
`[Bridge service]
|
|
31984
|
+
`[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).`
|
|
31763
31985
|
);
|
|
31764
31986
|
return;
|
|
31765
31987
|
}
|
|
@@ -31768,8 +31990,6 @@ function handleBridgePrompt(msg, deps) {
|
|
|
31768
31990
|
const sessionWorktreesEnabled = msg.sessionWorktreesEnabled === true;
|
|
31769
31991
|
const agentType = typeof msg.agentType === "string" && msg.agentType.trim() ? msg.agentType.trim() : void 0;
|
|
31770
31992
|
const runId = typeof msg.runId === "string" ? msg.runId : void 0;
|
|
31771
|
-
const sessionGitQueueStart = msg.sessionGitQueueStart === true;
|
|
31772
|
-
const collectSessionDiffAfterTurn = msg.collectSessionDiffAfterTurn === true;
|
|
31773
31993
|
const sendResult = (result) => {
|
|
31774
31994
|
const s = getWs();
|
|
31775
31995
|
if (s) sendWsMessage(s, result);
|
|
@@ -31777,7 +31997,7 @@ function handleBridgePrompt(msg, deps) {
|
|
|
31777
31997
|
const sendSessionUpdate = (payload) => {
|
|
31778
31998
|
const s = getWs();
|
|
31779
31999
|
if (!s) {
|
|
31780
|
-
log2("[Bridge service]
|
|
32000
|
+
log2("[Bridge service] Session update not sent: not connected to the bridge.");
|
|
31781
32001
|
return;
|
|
31782
32002
|
}
|
|
31783
32003
|
const p = payload;
|
|
@@ -31785,7 +32005,7 @@ function handleBridgePrompt(msg, deps) {
|
|
|
31785
32005
|
};
|
|
31786
32006
|
async function preambleAndPrompt(resolvedCwd) {
|
|
31787
32007
|
const s = getWs();
|
|
31788
|
-
const effectiveCwd =
|
|
32008
|
+
const effectiveCwd = path11.resolve(resolvedCwd ?? getBridgeWorkspaceDirectory());
|
|
31789
32009
|
const worktreePaths = sessionWorktreeManager.getWorktreePathsForSession(sessionId) ?? [];
|
|
31790
32010
|
const repoRoots = await resolveSnapshotRepoRoots({
|
|
31791
32011
|
worktreePaths,
|
|
@@ -31818,9 +32038,6 @@ function handleBridgePrompt(msg, deps) {
|
|
|
31818
32038
|
agentUsesWorktree: sessionWorktreeManager.usesWorktreeSession(sessionId)
|
|
31819
32039
|
});
|
|
31820
32040
|
}
|
|
31821
|
-
if (sessionGitQueueStart && sessionId && repoRoots.length > 0) {
|
|
31822
|
-
await captureSessionGitBoundary({ sessionId, repoRoots, log: log2 });
|
|
31823
|
-
}
|
|
31824
32041
|
if (s && sessionId && runId) {
|
|
31825
32042
|
const cap = repoRoots.length > 0 ? await capturePreTurnSnapshot({ runId, repoRoots, agentCwd: effectiveCwd, log: log2 }) : { ok: false, error: "No git repos" };
|
|
31826
32043
|
sendWsMessage(s, {
|
|
@@ -31839,12 +32056,11 @@ function handleBridgePrompt(msg, deps) {
|
|
|
31839
32056
|
agentType,
|
|
31840
32057
|
cwd: effectiveCwd,
|
|
31841
32058
|
sendResult,
|
|
31842
|
-
sendSessionUpdate
|
|
31843
|
-
collectSessionDiffAfterTurn
|
|
32059
|
+
sendSessionUpdate
|
|
31844
32060
|
});
|
|
31845
32061
|
}
|
|
31846
|
-
void sessionWorktreeManager.resolveCwdForPrompt(sessionId, { isNewSession, sessionWorktreesEnabled }).then((
|
|
31847
|
-
log2(`[
|
|
32062
|
+
void sessionWorktreeManager.resolveCwdForPrompt(sessionId, { isNewSession, sessionWorktreesEnabled }).then((cwd) => preambleAndPrompt(cwd)).catch((err) => {
|
|
32063
|
+
log2(`[Agent] Worktree resolve failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
31848
32064
|
void preambleAndPrompt(void 0);
|
|
31849
32065
|
});
|
|
31850
32066
|
}
|
|
@@ -31861,7 +32077,7 @@ function handleBridgeCancelRun(msg, { log: log2, acpManager }) {
|
|
|
31861
32077
|
void acpManager.cancelRun(runId).then((sent) => {
|
|
31862
32078
|
if (!sent) {
|
|
31863
32079
|
log2(
|
|
31864
|
-
`[
|
|
32080
|
+
`[Agent] Cancel ignored for run ${runId.slice(0, 8)}\u2026 (no active run or cancel not available).`
|
|
31865
32081
|
);
|
|
31866
32082
|
}
|
|
31867
32083
|
});
|
|
@@ -31964,7 +32180,7 @@ var previewSkill = {
|
|
|
31964
32180
|
const exe = parts[0];
|
|
31965
32181
|
const args = parts.slice(1);
|
|
31966
32182
|
previewProcess = spawn4(isWindows && exe === "npm" ? "npm.cmd" : exe, args, {
|
|
31967
|
-
cwd:
|
|
32183
|
+
cwd: getBridgeWorkspaceDirectory(),
|
|
31968
32184
|
stdio: ["ignore", "pipe", "pipe"],
|
|
31969
32185
|
env: {
|
|
31970
32186
|
...process.env,
|
|
@@ -32063,7 +32279,7 @@ function handleSkillCall(msg, socket, log2) {
|
|
|
32063
32279
|
sendWsMessage(socket, { type: "skill_result", id: msg.id, result });
|
|
32064
32280
|
}).catch((err) => {
|
|
32065
32281
|
sendWsMessage(socket, { type: "skill_result", id: msg.id, error: String(err) });
|
|
32066
|
-
log2(`[Bridge service]
|
|
32282
|
+
log2(`[Bridge service] Skill invocation failed (${msg.skillId}/${msg.operationId}): ${err}`);
|
|
32067
32283
|
});
|
|
32068
32284
|
}
|
|
32069
32285
|
|
|
@@ -32080,36 +32296,35 @@ var handleSkillCallMessage = (msg, { getWs, log: log2 }) => {
|
|
|
32080
32296
|
};
|
|
32081
32297
|
|
|
32082
32298
|
// src/files/list-dir.ts
|
|
32083
|
-
import
|
|
32084
|
-
import
|
|
32299
|
+
import fs6 from "node:fs";
|
|
32300
|
+
import path13 from "node:path";
|
|
32085
32301
|
|
|
32086
32302
|
// src/files/ensure-under-cwd.ts
|
|
32087
|
-
import
|
|
32088
|
-
function ensureUnderCwd(relativePath,
|
|
32089
|
-
const normalized =
|
|
32090
|
-
const resolved =
|
|
32091
|
-
if (!resolved.startsWith(
|
|
32303
|
+
import path12 from "node:path";
|
|
32304
|
+
function ensureUnderCwd(relativePath, cwd = getBridgeWorkspaceDirectory()) {
|
|
32305
|
+
const normalized = path12.normalize(relativePath).replace(/^(\.\/)+/, "");
|
|
32306
|
+
const resolved = path12.resolve(cwd, normalized);
|
|
32307
|
+
if (!resolved.startsWith(cwd + path12.sep) && resolved !== cwd) {
|
|
32092
32308
|
return null;
|
|
32093
32309
|
}
|
|
32094
32310
|
return resolved;
|
|
32095
32311
|
}
|
|
32096
32312
|
|
|
32097
32313
|
// src/files/list-dir.ts
|
|
32098
|
-
var cwd = process.cwd();
|
|
32099
32314
|
function listDir(relativePath) {
|
|
32100
|
-
const resolved = ensureUnderCwd(relativePath || ".",
|
|
32315
|
+
const resolved = ensureUnderCwd(relativePath || ".", getBridgeWorkspaceDirectory());
|
|
32101
32316
|
if (!resolved) {
|
|
32102
32317
|
return { error: "Path is outside working directory" };
|
|
32103
32318
|
}
|
|
32104
32319
|
try {
|
|
32105
|
-
const names =
|
|
32320
|
+
const names = fs6.readdirSync(resolved, { withFileTypes: true });
|
|
32106
32321
|
const entries = names.filter((d) => !d.name.startsWith(".")).map((d) => {
|
|
32107
|
-
const entryPath =
|
|
32108
|
-
const fullPath =
|
|
32322
|
+
const entryPath = path13.join(relativePath || ".", d.name).replace(/\\/g, "/");
|
|
32323
|
+
const fullPath = path13.join(resolved, d.name);
|
|
32109
32324
|
let isDir = d.isDirectory();
|
|
32110
32325
|
if (d.isSymbolicLink()) {
|
|
32111
32326
|
try {
|
|
32112
|
-
const targetStat =
|
|
32327
|
+
const targetStat = fs6.statSync(fullPath);
|
|
32113
32328
|
isDir = targetStat.isDirectory();
|
|
32114
32329
|
} catch {
|
|
32115
32330
|
isDir = false;
|
|
@@ -32133,26 +32348,25 @@ function listDir(relativePath) {
|
|
|
32133
32348
|
}
|
|
32134
32349
|
|
|
32135
32350
|
// src/files/read-file.ts
|
|
32136
|
-
import
|
|
32351
|
+
import fs7 from "node:fs";
|
|
32137
32352
|
import { StringDecoder } from "node:string_decoder";
|
|
32138
|
-
var cwd2 = process.cwd();
|
|
32139
32353
|
function resolveFilePath(relativePath) {
|
|
32140
|
-
const resolved = ensureUnderCwd(relativePath,
|
|
32354
|
+
const resolved = ensureUnderCwd(relativePath, getBridgeWorkspaceDirectory());
|
|
32141
32355
|
if (!resolved) return { error: "Path is outside working directory" };
|
|
32142
32356
|
let real;
|
|
32143
32357
|
try {
|
|
32144
|
-
real =
|
|
32358
|
+
real = fs7.realpathSync(resolved);
|
|
32145
32359
|
} catch {
|
|
32146
32360
|
real = resolved;
|
|
32147
32361
|
}
|
|
32148
|
-
const stat =
|
|
32362
|
+
const stat = fs7.statSync(real);
|
|
32149
32363
|
if (!stat.isFile()) return { error: "Not a file" };
|
|
32150
32364
|
return real;
|
|
32151
32365
|
}
|
|
32152
32366
|
var LINE_CHUNK_SIZE = 64 * 1024;
|
|
32153
32367
|
function readFileRange(filePath, startLine, endLine, lineOffsetIn, lineChunkSize = LINE_CHUNK_SIZE) {
|
|
32154
|
-
const fileSize =
|
|
32155
|
-
const fd =
|
|
32368
|
+
const fileSize = fs7.statSync(filePath).size;
|
|
32369
|
+
const fd = fs7.openSync(filePath, "r");
|
|
32156
32370
|
const bufSize = 64 * 1024;
|
|
32157
32371
|
const buf = Buffer.alloc(bufSize);
|
|
32158
32372
|
const decoder = new StringDecoder("utf8");
|
|
@@ -32165,7 +32379,7 @@ function readFileRange(filePath, startLine, endLine, lineOffsetIn, lineChunkSize
|
|
|
32165
32379
|
let line0Accum = "";
|
|
32166
32380
|
try {
|
|
32167
32381
|
let bytesRead;
|
|
32168
|
-
while (!done && (bytesRead =
|
|
32382
|
+
while (!done && (bytesRead = fs7.readSync(fd, buf, 0, bufSize, null)) > 0) {
|
|
32169
32383
|
const text = partial2 + decoder.write(buf.subarray(0, bytesRead));
|
|
32170
32384
|
partial2 = "";
|
|
32171
32385
|
let lineStart = 0;
|
|
@@ -32300,7 +32514,7 @@ function readFileRange(filePath, startLine, endLine, lineOffsetIn, lineChunkSize
|
|
|
32300
32514
|
}
|
|
32301
32515
|
return { content: resultLines.join("\n"), size: fileSize };
|
|
32302
32516
|
} finally {
|
|
32303
|
-
|
|
32517
|
+
fs7.closeSync(fd);
|
|
32304
32518
|
}
|
|
32305
32519
|
}
|
|
32306
32520
|
function readFile(relativePath, startLine, endLine, lineOffset, lineChunkSize = LINE_CHUNK_SIZE) {
|
|
@@ -32311,8 +32525,8 @@ function readFile(relativePath, startLine, endLine, lineOffset, lineChunkSize =
|
|
|
32311
32525
|
if (hasRange) {
|
|
32312
32526
|
return readFileRange(result, startLine, endLine, lineOffset, lineChunkSize);
|
|
32313
32527
|
}
|
|
32314
|
-
const stat =
|
|
32315
|
-
const raw =
|
|
32528
|
+
const stat = fs7.statSync(result);
|
|
32529
|
+
const raw = fs7.readFileSync(result, "utf8");
|
|
32316
32530
|
const lines = raw.split(/\r?\n/);
|
|
32317
32531
|
return { content: raw, totalLines: lines.length, size: stat.size };
|
|
32318
32532
|
} catch (err) {
|
|
@@ -32321,16 +32535,16 @@ function readFile(relativePath, startLine, endLine, lineOffset, lineChunkSize =
|
|
|
32321
32535
|
}
|
|
32322
32536
|
|
|
32323
32537
|
// src/files/file-index.ts
|
|
32324
|
-
import
|
|
32325
|
-
import
|
|
32538
|
+
import fs8 from "node:fs";
|
|
32539
|
+
import path14 from "node:path";
|
|
32326
32540
|
import os2 from "node:os";
|
|
32327
32541
|
import crypto2 from "node:crypto";
|
|
32328
|
-
var INDEX_DIR =
|
|
32542
|
+
var INDEX_DIR = path14.join(os2.homedir(), ".buildautomaton");
|
|
32329
32543
|
var HASH_LEN = 16;
|
|
32330
32544
|
var INDEX_VERSION = 2;
|
|
32331
|
-
function getIndexPath(
|
|
32332
|
-
const hash = crypto2.createHash("sha256").update(
|
|
32333
|
-
return
|
|
32545
|
+
function getIndexPath(cwd) {
|
|
32546
|
+
const hash = crypto2.createHash("sha256").update(cwd).digest("hex").slice(0, HASH_LEN);
|
|
32547
|
+
return path14.join(INDEX_DIR, `.file-index-${hash}.json`);
|
|
32334
32548
|
}
|
|
32335
32549
|
function getTrigrams(s) {
|
|
32336
32550
|
const lower = s.toLowerCase();
|
|
@@ -32368,20 +32582,20 @@ function binarySearch(arr, x) {
|
|
|
32368
32582
|
function walkDir(dir, baseDir, out) {
|
|
32369
32583
|
let names;
|
|
32370
32584
|
try {
|
|
32371
|
-
names =
|
|
32585
|
+
names = fs8.readdirSync(dir);
|
|
32372
32586
|
} catch {
|
|
32373
32587
|
return;
|
|
32374
32588
|
}
|
|
32375
32589
|
for (const name of names) {
|
|
32376
32590
|
if (name.startsWith(".")) continue;
|
|
32377
|
-
const full =
|
|
32591
|
+
const full = path14.join(dir, name);
|
|
32378
32592
|
let stat;
|
|
32379
32593
|
try {
|
|
32380
|
-
stat =
|
|
32594
|
+
stat = fs8.statSync(full);
|
|
32381
32595
|
} catch {
|
|
32382
32596
|
continue;
|
|
32383
32597
|
}
|
|
32384
|
-
const relative4 =
|
|
32598
|
+
const relative4 = path14.relative(baseDir, full).replace(/\\/g, "/");
|
|
32385
32599
|
if (stat.isDirectory()) {
|
|
32386
32600
|
walkDir(full, baseDir, out);
|
|
32387
32601
|
} else if (stat.isFile()) {
|
|
@@ -32389,8 +32603,8 @@ function walkDir(dir, baseDir, out) {
|
|
|
32389
32603
|
}
|
|
32390
32604
|
}
|
|
32391
32605
|
}
|
|
32392
|
-
function buildFileIndex(
|
|
32393
|
-
const resolved =
|
|
32606
|
+
function buildFileIndex(cwd) {
|
|
32607
|
+
const resolved = path14.resolve(cwd);
|
|
32394
32608
|
const paths = [];
|
|
32395
32609
|
walkDir(resolved, resolved, paths);
|
|
32396
32610
|
paths.sort((a, b) => a.localeCompare(b, void 0, { sensitivity: "base" }));
|
|
@@ -32408,18 +32622,18 @@ function buildFileIndex(cwd3) {
|
|
|
32408
32622
|
const data = { version: INDEX_VERSION, paths, trigramIndex };
|
|
32409
32623
|
const indexPath = getIndexPath(resolved);
|
|
32410
32624
|
try {
|
|
32411
|
-
if (!
|
|
32412
|
-
|
|
32625
|
+
if (!fs8.existsSync(INDEX_DIR)) fs8.mkdirSync(INDEX_DIR, { recursive: true });
|
|
32626
|
+
fs8.writeFileSync(indexPath, JSON.stringify(data), "utf8");
|
|
32413
32627
|
} catch (e) {
|
|
32414
32628
|
console.error("[file-index] Failed to write index:", e);
|
|
32415
32629
|
}
|
|
32416
32630
|
return data;
|
|
32417
32631
|
}
|
|
32418
|
-
function loadFileIndex(
|
|
32419
|
-
const resolved =
|
|
32632
|
+
function loadFileIndex(cwd) {
|
|
32633
|
+
const resolved = path14.resolve(cwd);
|
|
32420
32634
|
const indexPath = getIndexPath(resolved);
|
|
32421
32635
|
try {
|
|
32422
|
-
const raw =
|
|
32636
|
+
const raw = fs8.readFileSync(indexPath, "utf8");
|
|
32423
32637
|
const parsed = JSON.parse(raw);
|
|
32424
32638
|
if (parsed !== null && typeof parsed === "object" && Array.isArray(parsed.paths)) {
|
|
32425
32639
|
const obj = parsed;
|
|
@@ -32436,8 +32650,8 @@ function loadFileIndex(cwd3) {
|
|
|
32436
32650
|
return null;
|
|
32437
32651
|
}
|
|
32438
32652
|
}
|
|
32439
|
-
function ensureFileIndex(
|
|
32440
|
-
const resolved =
|
|
32653
|
+
function ensureFileIndex(cwd) {
|
|
32654
|
+
const resolved = path14.resolve(cwd);
|
|
32441
32655
|
const cached2 = loadFileIndex(resolved);
|
|
32442
32656
|
if (cached2 !== null) return { data: cached2, fromCache: true };
|
|
32443
32657
|
const data = buildFileIndex(resolved);
|
|
@@ -32475,8 +32689,8 @@ function searchFileIndex(index, query, limit = 100) {
|
|
|
32475
32689
|
var SEARCH_LIMIT = 100;
|
|
32476
32690
|
function handleFileBrowserSearch(msg, socket) {
|
|
32477
32691
|
const q = typeof msg.q === "string" ? msg.q : "";
|
|
32478
|
-
const
|
|
32479
|
-
const index = loadFileIndex(
|
|
32692
|
+
const cwd = getBridgeWorkspaceDirectory();
|
|
32693
|
+
const index = loadFileIndex(cwd);
|
|
32480
32694
|
if (index === null) {
|
|
32481
32695
|
sendWsMessage(socket, {
|
|
32482
32696
|
type: "file_browser_search_response",
|
|
@@ -32497,7 +32711,7 @@ function handleFileBrowserSearch(msg, socket) {
|
|
|
32497
32711
|
function triggerFileIndexBuild() {
|
|
32498
32712
|
setImmediate(() => {
|
|
32499
32713
|
try {
|
|
32500
|
-
ensureFileIndex(
|
|
32714
|
+
ensureFileIndex(getBridgeWorkspaceDirectory());
|
|
32501
32715
|
} catch (e) {
|
|
32502
32716
|
console.error("[file-index] Background build failed:", e);
|
|
32503
32717
|
}
|
|
@@ -32558,30 +32772,30 @@ function handleFileBrowserSearchMessage(msg, { getWs }) {
|
|
|
32558
32772
|
}
|
|
32559
32773
|
|
|
32560
32774
|
// src/skills/discover-local-agent-skills.ts
|
|
32561
|
-
import
|
|
32562
|
-
import
|
|
32775
|
+
import fs9 from "node:fs";
|
|
32776
|
+
import path15 from "node:path";
|
|
32563
32777
|
var SKILL_DISCOVERY_ROOTS = [".agents/skills", ".claude/skills", ".cursor/skills", "skills"];
|
|
32564
|
-
function discoverLocalSkills(
|
|
32778
|
+
function discoverLocalSkills(cwd) {
|
|
32565
32779
|
const out = [];
|
|
32566
32780
|
const seenKeys = /* @__PURE__ */ new Set();
|
|
32567
32781
|
for (const rel of SKILL_DISCOVERY_ROOTS) {
|
|
32568
|
-
const base =
|
|
32569
|
-
if (!
|
|
32782
|
+
const base = path15.join(cwd, rel);
|
|
32783
|
+
if (!fs9.existsSync(base) || !fs9.statSync(base).isDirectory()) continue;
|
|
32570
32784
|
let entries = [];
|
|
32571
32785
|
try {
|
|
32572
|
-
entries =
|
|
32786
|
+
entries = fs9.readdirSync(base);
|
|
32573
32787
|
} catch {
|
|
32574
32788
|
continue;
|
|
32575
32789
|
}
|
|
32576
32790
|
for (const name of entries) {
|
|
32577
|
-
const dir =
|
|
32791
|
+
const dir = path15.join(base, name);
|
|
32578
32792
|
try {
|
|
32579
|
-
if (!
|
|
32793
|
+
if (!fs9.statSync(dir).isDirectory()) continue;
|
|
32580
32794
|
} catch {
|
|
32581
32795
|
continue;
|
|
32582
32796
|
}
|
|
32583
|
-
const skillMd =
|
|
32584
|
-
if (!
|
|
32797
|
+
const skillMd = path15.join(dir, "SKILL.md");
|
|
32798
|
+
if (!fs9.existsSync(skillMd)) continue;
|
|
32585
32799
|
const key = `${rel}/${name}`;
|
|
32586
32800
|
if (seenKeys.has(key)) continue;
|
|
32587
32801
|
seenKeys.add(key);
|
|
@@ -32590,26 +32804,26 @@ function discoverLocalSkills(cwd3) {
|
|
|
32590
32804
|
}
|
|
32591
32805
|
return out;
|
|
32592
32806
|
}
|
|
32593
|
-
function discoverSkillLayoutRoots(
|
|
32807
|
+
function discoverSkillLayoutRoots(cwd) {
|
|
32594
32808
|
const roots = [];
|
|
32595
32809
|
for (const rel of SKILL_DISCOVERY_ROOTS) {
|
|
32596
|
-
const base =
|
|
32597
|
-
if (!
|
|
32810
|
+
const base = path15.join(cwd, rel);
|
|
32811
|
+
if (!fs9.existsSync(base) || !fs9.statSync(base).isDirectory()) continue;
|
|
32598
32812
|
let entries = [];
|
|
32599
32813
|
try {
|
|
32600
|
-
entries =
|
|
32814
|
+
entries = fs9.readdirSync(base);
|
|
32601
32815
|
} catch {
|
|
32602
32816
|
continue;
|
|
32603
32817
|
}
|
|
32604
32818
|
const skills2 = [];
|
|
32605
32819
|
for (const name of entries) {
|
|
32606
|
-
const dir =
|
|
32820
|
+
const dir = path15.join(base, name);
|
|
32607
32821
|
try {
|
|
32608
|
-
if (!
|
|
32822
|
+
if (!fs9.statSync(dir).isDirectory()) continue;
|
|
32609
32823
|
} catch {
|
|
32610
32824
|
continue;
|
|
32611
32825
|
}
|
|
32612
|
-
if (!
|
|
32826
|
+
if (!fs9.existsSync(path15.join(dir, "SKILL.md"))) continue;
|
|
32613
32827
|
const relPath = `${rel}/${name}`.replace(/\\/g, "/");
|
|
32614
32828
|
skills2.push({ name, relPath });
|
|
32615
32829
|
}
|
|
@@ -32624,14 +32838,14 @@ function discoverSkillLayoutRoots(cwd3) {
|
|
|
32624
32838
|
function handleSkillLayoutRequest(msg, deps) {
|
|
32625
32839
|
const socket = deps.getWs();
|
|
32626
32840
|
const id = typeof msg.id === "string" ? msg.id : "";
|
|
32627
|
-
const roots = discoverSkillLayoutRoots(
|
|
32841
|
+
const roots = discoverSkillLayoutRoots(getBridgeWorkspaceDirectory());
|
|
32628
32842
|
socket?.send(JSON.stringify({ type: "skill_layout_response", id, roots }));
|
|
32629
32843
|
}
|
|
32630
32844
|
|
|
32631
32845
|
// src/skills/install-remote-skills.ts
|
|
32632
|
-
import
|
|
32633
|
-
import
|
|
32634
|
-
function installRemoteSkills(
|
|
32846
|
+
import fs10 from "node:fs";
|
|
32847
|
+
import path16 from "node:path";
|
|
32848
|
+
function installRemoteSkills(cwd, targetDir, items) {
|
|
32635
32849
|
const installed = [];
|
|
32636
32850
|
if (!Array.isArray(items)) {
|
|
32637
32851
|
return { success: false, error: "Invalid items" };
|
|
@@ -32641,15 +32855,15 @@ function installRemoteSkills(cwd3, targetDir, items) {
|
|
|
32641
32855
|
if (typeof item.sourceId !== "string" || typeof item.skillName !== "string" || typeof item.versionHash !== "string" || !Array.isArray(item.files)) {
|
|
32642
32856
|
continue;
|
|
32643
32857
|
}
|
|
32644
|
-
const skillDir =
|
|
32858
|
+
const skillDir = path16.join(cwd, targetDir, item.skillName);
|
|
32645
32859
|
for (const f of item.files) {
|
|
32646
32860
|
if (typeof f.path !== "string" || !f.text && !f.base64) continue;
|
|
32647
|
-
const dest =
|
|
32648
|
-
|
|
32861
|
+
const dest = path16.join(skillDir, f.path);
|
|
32862
|
+
fs10.mkdirSync(path16.dirname(dest), { recursive: true });
|
|
32649
32863
|
if (f.text !== void 0) {
|
|
32650
|
-
|
|
32864
|
+
fs10.writeFileSync(dest, f.text, "utf8");
|
|
32651
32865
|
} else if (f.base64) {
|
|
32652
|
-
|
|
32866
|
+
fs10.writeFileSync(dest, Buffer.from(f.base64, "base64"));
|
|
32653
32867
|
}
|
|
32654
32868
|
}
|
|
32655
32869
|
installed.push({
|
|
@@ -32670,11 +32884,11 @@ var handleInstallSkillsMessage = (msg, deps) => {
|
|
|
32670
32884
|
const id = typeof msg.id === "string" ? msg.id : "";
|
|
32671
32885
|
const targetDir = typeof msg.targetDir === "string" && msg.targetDir.trim() ? msg.targetDir.trim() : ".agents/skills";
|
|
32672
32886
|
const rawItems = msg.items;
|
|
32673
|
-
const
|
|
32674
|
-
const result = installRemoteSkills(
|
|
32887
|
+
const cwd = getBridgeWorkspaceDirectory();
|
|
32888
|
+
const result = installRemoteSkills(cwd, targetDir, rawItems);
|
|
32675
32889
|
if (!result.success) {
|
|
32676
32890
|
const err = result.error ?? "Invalid items";
|
|
32677
|
-
deps.log(`[Bridge service]
|
|
32891
|
+
deps.log(`[Bridge service] Install skills failed: ${err}`);
|
|
32678
32892
|
socket?.send(JSON.stringify({ type: "install_skills_result", id, success: false, error: err }));
|
|
32679
32893
|
return;
|
|
32680
32894
|
}
|
|
@@ -32743,8 +32957,7 @@ var handleSessionDiscardedMessage = (msg, deps) => {
|
|
|
32743
32957
|
};
|
|
32744
32958
|
|
|
32745
32959
|
// src/bridge/routing/handlers/revert-turn-snapshot.ts
|
|
32746
|
-
import * as
|
|
32747
|
-
import * as path16 from "node:path";
|
|
32960
|
+
import * as fs11 from "node:fs";
|
|
32748
32961
|
var handleRevertTurnSnapshotMessage = (msg, deps) => {
|
|
32749
32962
|
const id = typeof msg.id === "string" ? msg.id : "";
|
|
32750
32963
|
const sessionId = typeof msg.sessionId === "string" ? msg.sessionId : "";
|
|
@@ -32754,9 +32967,9 @@ var handleRevertTurnSnapshotMessage = (msg, deps) => {
|
|
|
32754
32967
|
void (async () => {
|
|
32755
32968
|
const s = getWs();
|
|
32756
32969
|
if (!s) return;
|
|
32757
|
-
const agentBase = sessionWorktreeManager.getAgentCwdForSession(sessionId) ??
|
|
32970
|
+
const agentBase = sessionWorktreeManager.getAgentCwdForSession(sessionId) ?? getBridgeWorkspaceDirectory();
|
|
32758
32971
|
const file2 = snapshotFilePath(agentBase, turnId);
|
|
32759
|
-
if (!
|
|
32972
|
+
if (!fs11.existsSync(file2)) {
|
|
32760
32973
|
sendWsMessage(s, {
|
|
32761
32974
|
type: "revert_turn_snapshot_result",
|
|
32762
32975
|
id,
|
|
@@ -32883,7 +33096,7 @@ import * as path20 from "node:path";
|
|
|
32883
33096
|
import os4 from "node:os";
|
|
32884
33097
|
|
|
32885
33098
|
// src/worktrees/prepare-new-session-worktrees.ts
|
|
32886
|
-
import * as
|
|
33099
|
+
import * as fs13 from "node:fs";
|
|
32887
33100
|
import * as path18 from "node:path";
|
|
32888
33101
|
|
|
32889
33102
|
// src/git/worktree-add.ts
|
|
@@ -32893,7 +33106,7 @@ async function gitWorktreeAddBranch(mainRepoPath, worktreePath, branch) {
|
|
|
32893
33106
|
}
|
|
32894
33107
|
|
|
32895
33108
|
// src/worktrees/worktree-layout-file.ts
|
|
32896
|
-
import * as
|
|
33109
|
+
import * as fs12 from "node:fs";
|
|
32897
33110
|
import * as path17 from "node:path";
|
|
32898
33111
|
import os3 from "node:os";
|
|
32899
33112
|
var LAYOUT_FILENAME = "worktree-launcher-layout.json";
|
|
@@ -32910,8 +33123,8 @@ function normalizeLoadedLayout(raw) {
|
|
|
32910
33123
|
function loadWorktreeLayout() {
|
|
32911
33124
|
try {
|
|
32912
33125
|
const p = defaultWorktreeLayoutPath();
|
|
32913
|
-
if (!
|
|
32914
|
-
const raw = JSON.parse(
|
|
33126
|
+
if (!fs12.existsSync(p)) return { launcherCwds: [] };
|
|
33127
|
+
const raw = JSON.parse(fs12.readFileSync(p, "utf8"));
|
|
32915
33128
|
return normalizeLoadedLayout(raw);
|
|
32916
33129
|
} catch {
|
|
32917
33130
|
return { launcherCwds: [] };
|
|
@@ -32920,8 +33133,8 @@ function loadWorktreeLayout() {
|
|
|
32920
33133
|
function saveWorktreeLayout(layout) {
|
|
32921
33134
|
try {
|
|
32922
33135
|
const dir = path17.dirname(defaultWorktreeLayoutPath());
|
|
32923
|
-
|
|
32924
|
-
|
|
33136
|
+
fs12.mkdirSync(dir, { recursive: true });
|
|
33137
|
+
fs12.writeFileSync(defaultWorktreeLayoutPath(), JSON.stringify(layout, null, 2), "utf8");
|
|
32925
33138
|
} catch {
|
|
32926
33139
|
}
|
|
32927
33140
|
}
|
|
@@ -32953,25 +33166,25 @@ async function prepareNewSessionWorktrees(options) {
|
|
|
32953
33166
|
const agentMirrorRoot = path18.join(rootAbs, cwdKey);
|
|
32954
33167
|
const repos = await discoverGitReposUnderRoot(launcherResolved);
|
|
32955
33168
|
if (repos.length === 0) {
|
|
32956
|
-
log2("[worktrees] No
|
|
33169
|
+
log2("[worktrees] No Git repositories under launcher working directory; skipping worktree creation.");
|
|
32957
33170
|
return null;
|
|
32958
33171
|
}
|
|
32959
33172
|
const branch = `session-${sessionId}`;
|
|
32960
33173
|
const worktreePaths = [];
|
|
32961
|
-
|
|
33174
|
+
fs13.mkdirSync(agentMirrorRoot, { recursive: true });
|
|
32962
33175
|
for (const repo of repos) {
|
|
32963
33176
|
let rel = path18.relative(launcherResolved, repo.absolutePath);
|
|
32964
33177
|
if (rel.startsWith("..") || path18.isAbsolute(rel)) continue;
|
|
32965
33178
|
const relNorm = rel === "" ? "." : rel;
|
|
32966
33179
|
const wtPath = path18.join(agentMirrorRoot, relNorm, sessionId);
|
|
32967
|
-
|
|
33180
|
+
fs13.mkdirSync(path18.dirname(wtPath), { recursive: true });
|
|
32968
33181
|
try {
|
|
32969
33182
|
await gitWorktreeAddBranch(repo.absolutePath, wtPath, branch);
|
|
32970
|
-
log2(`[worktrees] Added worktree ${wtPath} (branch ${branch})
|
|
33183
|
+
log2(`[worktrees] Added worktree ${wtPath} (branch ${branch}).`);
|
|
32971
33184
|
worktreePaths.push(wtPath);
|
|
32972
33185
|
} catch (e) {
|
|
32973
33186
|
log2(
|
|
32974
|
-
`[worktrees]
|
|
33187
|
+
`[worktrees] Worktree add failed for ${repo.absolutePath}: ${e instanceof Error ? e.message : String(e)}`
|
|
32975
33188
|
);
|
|
32976
33189
|
}
|
|
32977
33190
|
}
|
|
@@ -32993,24 +33206,26 @@ async function renameSessionWorktreeBranches(paths, newBranch, log2) {
|
|
|
32993
33206
|
await gitRenameCurrentBranch(wt, safe);
|
|
32994
33207
|
log2(`[worktrees] Renamed branch in ${wt} \u2192 ${safe}`);
|
|
32995
33208
|
} catch (e) {
|
|
32996
|
-
log2(
|
|
33209
|
+
log2(
|
|
33210
|
+
`[worktrees] Branch rename failed in ${wt}: ${e instanceof Error ? e.message : String(e)}`
|
|
33211
|
+
);
|
|
32997
33212
|
}
|
|
32998
33213
|
}
|
|
32999
33214
|
}
|
|
33000
33215
|
|
|
33001
33216
|
// src/worktrees/remove-session-worktrees.ts
|
|
33002
|
-
import * as
|
|
33217
|
+
import * as fs16 from "node:fs";
|
|
33003
33218
|
|
|
33004
33219
|
// src/git/worktree-remove.ts
|
|
33005
|
-
import * as
|
|
33220
|
+
import * as fs15 from "node:fs";
|
|
33006
33221
|
|
|
33007
33222
|
// src/git/resolve-main-repo-from-git-file.ts
|
|
33008
|
-
import * as
|
|
33223
|
+
import * as fs14 from "node:fs";
|
|
33009
33224
|
import * as path19 from "node:path";
|
|
33010
33225
|
function resolveMainRepoFromWorktreeGitFile(wt) {
|
|
33011
33226
|
const gitDirFile = path19.join(wt, ".git");
|
|
33012
|
-
if (!
|
|
33013
|
-
const first2 =
|
|
33227
|
+
if (!fs14.existsSync(gitDirFile) || !fs14.statSync(gitDirFile).isFile()) return "";
|
|
33228
|
+
const first2 = fs14.readFileSync(gitDirFile, "utf8").trim();
|
|
33014
33229
|
const m = first2.match(/^gitdir:\s*(.+)$/im);
|
|
33015
33230
|
if (!m) return "";
|
|
33016
33231
|
const gitWorktreePath = path19.resolve(wt, m[1].trim());
|
|
@@ -33024,7 +33239,7 @@ async function gitWorktreeRemoveForce(worktreePath) {
|
|
|
33024
33239
|
if (mainRepo) {
|
|
33025
33240
|
await simpleGit(mainRepo).raw(["worktree", "remove", "--force", worktreePath]);
|
|
33026
33241
|
} else {
|
|
33027
|
-
|
|
33242
|
+
fs15.rmSync(worktreePath, { recursive: true, force: true });
|
|
33028
33243
|
}
|
|
33029
33244
|
}
|
|
33030
33245
|
|
|
@@ -33035,9 +33250,9 @@ async function removeSessionWorktrees(paths, log2) {
|
|
|
33035
33250
|
await gitWorktreeRemoveForce(wt);
|
|
33036
33251
|
log2(`[worktrees] Removed worktree ${wt}`);
|
|
33037
33252
|
} catch (e) {
|
|
33038
|
-
log2(`[worktrees]
|
|
33253
|
+
log2(`[worktrees] Remove failed for ${wt}: ${e instanceof Error ? e.message : String(e)}`);
|
|
33039
33254
|
try {
|
|
33040
|
-
|
|
33255
|
+
fs16.rmSync(wt, { recursive: true, force: true });
|
|
33041
33256
|
} catch {
|
|
33042
33257
|
}
|
|
33043
33258
|
}
|
|
@@ -33101,7 +33316,7 @@ var SessionWorktreeManager = class {
|
|
|
33101
33316
|
return this.bridgeWantsWorktrees;
|
|
33102
33317
|
}
|
|
33103
33318
|
/**
|
|
33104
|
-
* Returns cwd for the agent (mirror of launcher tree), or undefined to use
|
|
33319
|
+
* Returns cwd for the agent (mirror of launcher tree), or undefined to use the bridge workspace directory.
|
|
33105
33320
|
*/
|
|
33106
33321
|
async resolveCwdForPrompt(sessionId, opts) {
|
|
33107
33322
|
if (!sessionId || !this.effective() || !opts.sessionWorktreesEnabled) {
|
|
@@ -33114,7 +33329,7 @@ var SessionWorktreeManager = class {
|
|
|
33114
33329
|
}
|
|
33115
33330
|
const prep = await prepareNewSessionWorktrees({
|
|
33116
33331
|
rootAbs: this.rootAbs,
|
|
33117
|
-
launcherCwd:
|
|
33332
|
+
launcherCwd: getBridgeWorkspaceDirectory(),
|
|
33118
33333
|
sessionId,
|
|
33119
33334
|
layout: this.layout,
|
|
33120
33335
|
log: this.log
|
|
@@ -33154,7 +33369,7 @@ var SessionWorktreeManager = class {
|
|
|
33154
33369
|
}
|
|
33155
33370
|
async commitSession(params) {
|
|
33156
33371
|
const paths = this.sessionPaths.get(params.sessionId);
|
|
33157
|
-
const targets = paths?.length ? paths : [
|
|
33372
|
+
const targets = paths?.length ? paths : [getBridgeWorkspaceDirectory()];
|
|
33158
33373
|
return commitSessionWorktrees({
|
|
33159
33374
|
paths: targets,
|
|
33160
33375
|
branch: params.branch,
|
|
@@ -33207,7 +33422,7 @@ function shouldIgnoreRelative(rel) {
|
|
|
33207
33422
|
}
|
|
33208
33423
|
function attachWatchErrorLog(w) {
|
|
33209
33424
|
w.on("error", (err) => {
|
|
33210
|
-
console.error("[file-index]
|
|
33425
|
+
console.error("[file-index] File watcher error:", err);
|
|
33211
33426
|
});
|
|
33212
33427
|
}
|
|
33213
33428
|
function createFsWatcher(resolved, schedule) {
|
|
@@ -33226,7 +33441,7 @@ function createFsWatcher(resolved, schedule) {
|
|
|
33226
33441
|
const code = typeof e === "object" && e !== null && "code" in e ? e.code : void 0;
|
|
33227
33442
|
if (code === "ERR_FEATURE_UNAVAILABLE_ON_PLATFORM") {
|
|
33228
33443
|
console.warn(
|
|
33229
|
-
"[file-index]
|
|
33444
|
+
"[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."
|
|
33230
33445
|
);
|
|
33231
33446
|
const w = watch(resolved, { recursive: false }, onEvent);
|
|
33232
33447
|
attachWatchErrorLog(w);
|
|
@@ -33235,8 +33450,8 @@ function createFsWatcher(resolved, schedule) {
|
|
|
33235
33450
|
throw e;
|
|
33236
33451
|
}
|
|
33237
33452
|
}
|
|
33238
|
-
function startFileIndexWatcher(
|
|
33239
|
-
const resolved = path21.resolve(
|
|
33453
|
+
function startFileIndexWatcher(cwd = getBridgeWorkspaceDirectory()) {
|
|
33454
|
+
const resolved = path21.resolve(cwd);
|
|
33240
33455
|
try {
|
|
33241
33456
|
buildFileIndex(resolved);
|
|
33242
33457
|
} catch (e) {
|
|
@@ -33290,7 +33505,7 @@ async function sigtermAndWaitForExit(proc, graceMs, log2, shortId) {
|
|
|
33290
33505
|
const exited = new Promise((resolve15) => {
|
|
33291
33506
|
proc.once("exit", () => resolve15());
|
|
33292
33507
|
});
|
|
33293
|
-
log2(`[dev-server] Sending SIGTERM to ${shortId} (pid=${proc.pid ?? "?"})
|
|
33508
|
+
log2(`[dev-server] Sending SIGTERM to ${shortId} (pid=${proc.pid ?? "?"}).`);
|
|
33294
33509
|
try {
|
|
33295
33510
|
proc.kill("SIGTERM");
|
|
33296
33511
|
} catch {
|
|
@@ -33298,7 +33513,9 @@ async function sigtermAndWaitForExit(proc, graceMs, log2, shortId) {
|
|
|
33298
33513
|
await Promise.race([exited, new Promise((resolve15) => setTimeout(resolve15, graceMs))]);
|
|
33299
33514
|
}
|
|
33300
33515
|
function forceKillChild(proc, log2, shortId, graceMs) {
|
|
33301
|
-
log2(
|
|
33516
|
+
log2(
|
|
33517
|
+
`[dev-server] ${shortId} did not exit within ${graceMs}ms; sending SIGKILL (pid=${proc.pid ?? "?"}).`
|
|
33518
|
+
);
|
|
33302
33519
|
proc.removeAllListeners();
|
|
33303
33520
|
try {
|
|
33304
33521
|
proc.kill("SIGKILL");
|
|
@@ -33307,7 +33524,7 @@ function forceKillChild(proc, log2, shortId, graceMs) {
|
|
|
33307
33524
|
}
|
|
33308
33525
|
|
|
33309
33526
|
// src/dev-servers/process/wire-dev-server-child-process.ts
|
|
33310
|
-
import
|
|
33527
|
+
import fs17 from "node:fs";
|
|
33311
33528
|
|
|
33312
33529
|
// src/dev-servers/manager/forward-pipe.ts
|
|
33313
33530
|
function forwardChildPipe(childReadable, terminal, onData) {
|
|
@@ -33343,7 +33560,7 @@ function wireDevServerChildProcess(d) {
|
|
|
33343
33560
|
d.setPollInterval(void 0);
|
|
33344
33561
|
return;
|
|
33345
33562
|
}
|
|
33346
|
-
|
|
33563
|
+
fs17.readFile(d.mergedLogPath, (err, buf) => {
|
|
33347
33564
|
if (err || (d.getSpawnGeneration() ?? 0) !== d.scheduledGen) return;
|
|
33348
33565
|
if (buf.length <= d.mergedReadPos.value) return;
|
|
33349
33566
|
const chunk = Buffer.from(buf.subarray(d.mergedReadPos.value));
|
|
@@ -33368,7 +33585,7 @@ function wireDevServerChildProcess(d) {
|
|
|
33368
33585
|
d.rmMergedCleanupDir(cleanupDir);
|
|
33369
33586
|
}
|
|
33370
33587
|
if (signal) {
|
|
33371
|
-
d.log(`[dev-server] ${title} stopped (signal ${String(signal)})
|
|
33588
|
+
d.log(`[dev-server] ${title} stopped (signal: ${String(signal)}).`);
|
|
33372
33589
|
} else if (code !== null && code !== 0) {
|
|
33373
33590
|
const errTail = d.stderrTail.getTail().slice(-3).join("\n");
|
|
33374
33591
|
d.log(`[dev-server] ${title} exited with code ${code}${errTail ? `
|
|
@@ -33381,7 +33598,7 @@ ${errTail}` : ""}`);
|
|
|
33381
33598
|
d.sendStatus(code === 0 || code == null ? "stopped" : "error", detail, tails);
|
|
33382
33599
|
};
|
|
33383
33600
|
if (mergedPath) {
|
|
33384
|
-
|
|
33601
|
+
fs17.readFile(mergedPath, (err, buf) => {
|
|
33385
33602
|
if (!err && buf.length > d.mergedReadPos.value) {
|
|
33386
33603
|
const chunk = Buffer.from(buf.subarray(d.mergedReadPos.value));
|
|
33387
33604
|
if (chunk.length > 0) {
|
|
@@ -33483,13 +33700,13 @@ function parseDevServerDefs(servers) {
|
|
|
33483
33700
|
}
|
|
33484
33701
|
|
|
33485
33702
|
// src/dev-servers/manager/shell-spawn/utils.ts
|
|
33486
|
-
import
|
|
33703
|
+
import fs18 from "node:fs";
|
|
33487
33704
|
function isSpawnEbadf(e) {
|
|
33488
33705
|
return typeof e === "object" && e !== null && "code" in e && e.code === "EBADF";
|
|
33489
33706
|
}
|
|
33490
33707
|
function rmDirQuiet(dir) {
|
|
33491
33708
|
try {
|
|
33492
|
-
|
|
33709
|
+
fs18.rmSync(dir, { recursive: true, force: true });
|
|
33493
33710
|
} catch {
|
|
33494
33711
|
}
|
|
33495
33712
|
}
|
|
@@ -33497,7 +33714,7 @@ var cachedDevNullReadFd;
|
|
|
33497
33714
|
function devNullReadFd() {
|
|
33498
33715
|
if (cachedDevNullReadFd === void 0) {
|
|
33499
33716
|
const devPath = process.platform === "win32" ? "nul" : "/dev/null";
|
|
33500
|
-
cachedDevNullReadFd =
|
|
33717
|
+
cachedDevNullReadFd = fs18.openSync(devPath, "r");
|
|
33501
33718
|
}
|
|
33502
33719
|
return cachedDevNullReadFd;
|
|
33503
33720
|
}
|
|
@@ -33507,7 +33724,7 @@ function pipedStdoutStderrFor(attemptStdio) {
|
|
|
33507
33724
|
|
|
33508
33725
|
// src/dev-servers/manager/shell-spawn/try-spawn-piped-via-sh.ts
|
|
33509
33726
|
import { spawn as spawn5 } from "node:child_process";
|
|
33510
|
-
function trySpawnPipedViaSh(command, env,
|
|
33727
|
+
function trySpawnPipedViaSh(command, env, cwd, signal) {
|
|
33511
33728
|
const attempts = [
|
|
33512
33729
|
{ stdio: [devNullReadFd(), "pipe", "pipe"], endStdin: false },
|
|
33513
33730
|
{ stdio: ["ignore", "pipe", "pipe"], endStdin: true },
|
|
@@ -33518,7 +33735,7 @@ function trySpawnPipedViaSh(command, env, cwd3, signal) {
|
|
|
33518
33735
|
const attempt = attempts[i];
|
|
33519
33736
|
const opts = {
|
|
33520
33737
|
env,
|
|
33521
|
-
cwd
|
|
33738
|
+
cwd,
|
|
33522
33739
|
stdio: attempt.stdio,
|
|
33523
33740
|
...signal ? { signal } : {}
|
|
33524
33741
|
};
|
|
@@ -33550,11 +33767,11 @@ function trySpawnPipedViaSh(command, env, cwd3, signal) {
|
|
|
33550
33767
|
|
|
33551
33768
|
// src/dev-servers/manager/shell-spawn/try-spawn-shell-true-piped.ts
|
|
33552
33769
|
import { spawn as spawn6 } from "node:child_process";
|
|
33553
|
-
function trySpawnShellTruePiped(command, env,
|
|
33770
|
+
function trySpawnShellTruePiped(command, env, cwd, devNullFd, signal) {
|
|
33554
33771
|
try {
|
|
33555
33772
|
const opts = {
|
|
33556
33773
|
env,
|
|
33557
|
-
cwd
|
|
33774
|
+
cwd,
|
|
33558
33775
|
stdio: [devNullFd, "pipe", "pipe"],
|
|
33559
33776
|
shell: true,
|
|
33560
33777
|
...signal ? { signal } : {}
|
|
@@ -33571,15 +33788,15 @@ function trySpawnShellTruePiped(command, env, cwd3, devNullFd, signal) {
|
|
|
33571
33788
|
|
|
33572
33789
|
// src/dev-servers/manager/shell-spawn/try-spawn-merged-log-file.ts
|
|
33573
33790
|
import { spawn as spawn7 } from "node:child_process";
|
|
33574
|
-
import
|
|
33791
|
+
import fs19 from "node:fs";
|
|
33575
33792
|
import { tmpdir } from "node:os";
|
|
33576
33793
|
import path22 from "node:path";
|
|
33577
|
-
function trySpawnMergedLogFile(command, env,
|
|
33578
|
-
const tmpRoot =
|
|
33794
|
+
function trySpawnMergedLogFile(command, env, cwd, signal) {
|
|
33795
|
+
const tmpRoot = fs19.mkdtempSync(path22.join(tmpdir(), "ba-devsrv-log-"));
|
|
33579
33796
|
const logPath = path22.join(tmpRoot, "combined.log");
|
|
33580
33797
|
let logFd;
|
|
33581
33798
|
try {
|
|
33582
|
-
logFd =
|
|
33799
|
+
logFd = fs19.openSync(logPath, "a");
|
|
33583
33800
|
} catch {
|
|
33584
33801
|
rmDirQuiet(tmpRoot);
|
|
33585
33802
|
return null;
|
|
@@ -33590,15 +33807,15 @@ function trySpawnMergedLogFile(command, env, cwd3, signal) {
|
|
|
33590
33807
|
if (process.platform === "win32") {
|
|
33591
33808
|
proc = spawn7(process.env.ComSpec || "cmd.exe", ["/d", "/s", "/c", command], {
|
|
33592
33809
|
env,
|
|
33593
|
-
cwd
|
|
33810
|
+
cwd,
|
|
33594
33811
|
stdio,
|
|
33595
33812
|
windowsHide: true,
|
|
33596
33813
|
...signal ? { signal } : {}
|
|
33597
33814
|
});
|
|
33598
33815
|
} else {
|
|
33599
|
-
proc = spawn7("/bin/sh", ["-c", command], { env, cwd
|
|
33816
|
+
proc = spawn7("/bin/sh", ["-c", command], { env, cwd, stdio, ...signal ? { signal } : {} });
|
|
33600
33817
|
}
|
|
33601
|
-
|
|
33818
|
+
fs19.closeSync(logFd);
|
|
33602
33819
|
return {
|
|
33603
33820
|
proc,
|
|
33604
33821
|
pipedStdoutStderr: true,
|
|
@@ -33607,7 +33824,7 @@ function trySpawnMergedLogFile(command, env, cwd3, signal) {
|
|
|
33607
33824
|
};
|
|
33608
33825
|
} catch (e) {
|
|
33609
33826
|
try {
|
|
33610
|
-
|
|
33827
|
+
fs19.closeSync(logFd);
|
|
33611
33828
|
} catch {
|
|
33612
33829
|
}
|
|
33613
33830
|
rmDirQuiet(tmpRoot);
|
|
@@ -33618,25 +33835,25 @@ function trySpawnMergedLogFile(command, env, cwd3, signal) {
|
|
|
33618
33835
|
|
|
33619
33836
|
// src/dev-servers/manager/shell-spawn/try-spawn-shell-script-log-redirect.ts
|
|
33620
33837
|
import { spawn as spawn8 } from "node:child_process";
|
|
33621
|
-
import
|
|
33838
|
+
import fs20 from "node:fs";
|
|
33622
33839
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
33623
33840
|
import path23 from "node:path";
|
|
33624
33841
|
function shSingleQuote(s) {
|
|
33625
33842
|
return `'${s.replace(/'/g, `'\\''`)}'`;
|
|
33626
33843
|
}
|
|
33627
|
-
function trySpawnShellScriptLogRedirectUnix(command, env,
|
|
33628
|
-
const tmpRoot =
|
|
33844
|
+
function trySpawnShellScriptLogRedirectUnix(command, env, cwd, signal) {
|
|
33845
|
+
const tmpRoot = fs20.mkdtempSync(path23.join(tmpdir2(), "ba-devsrv-sh-"));
|
|
33629
33846
|
const logPath = path23.join(tmpRoot, "combined.log");
|
|
33630
33847
|
const innerPath = path23.join(tmpRoot, "_cmd.sh");
|
|
33631
33848
|
const runnerPath = path23.join(tmpRoot, "_run.sh");
|
|
33632
33849
|
try {
|
|
33633
|
-
|
|
33850
|
+
fs20.writeFileSync(innerPath, `#!/bin/sh
|
|
33634
33851
|
${command}
|
|
33635
33852
|
`);
|
|
33636
|
-
|
|
33853
|
+
fs20.writeFileSync(
|
|
33637
33854
|
runnerPath,
|
|
33638
33855
|
`#!/bin/sh
|
|
33639
|
-
cd ${shSingleQuote(
|
|
33856
|
+
cd ${shSingleQuote(cwd)}
|
|
33640
33857
|
/bin/sh ${shSingleQuote(innerPath)} >>${shSingleQuote(logPath)} 2>&1
|
|
33641
33858
|
`
|
|
33642
33859
|
);
|
|
@@ -33658,17 +33875,17 @@ cd ${shSingleQuote(cwd3)}
|
|
|
33658
33875
|
throw e;
|
|
33659
33876
|
}
|
|
33660
33877
|
}
|
|
33661
|
-
function trySpawnShellScriptLogRedirectWin(command, env,
|
|
33662
|
-
const tmpRoot =
|
|
33878
|
+
function trySpawnShellScriptLogRedirectWin(command, env, cwd, signal) {
|
|
33879
|
+
const tmpRoot = fs20.mkdtempSync(path23.join(tmpdir2(), "ba-devsrv-sh-"));
|
|
33663
33880
|
const logPath = path23.join(tmpRoot, "combined.log");
|
|
33664
33881
|
const runnerPath = path23.join(tmpRoot, "_run.bat");
|
|
33665
33882
|
const q = (p) => `"${p.replace(/"/g, '""')}"`;
|
|
33666
33883
|
const com = process.env.ComSpec || "cmd.exe";
|
|
33667
33884
|
try {
|
|
33668
|
-
|
|
33885
|
+
fs20.writeFileSync(
|
|
33669
33886
|
runnerPath,
|
|
33670
33887
|
`@ECHO OFF\r
|
|
33671
|
-
CD /D ${q(
|
|
33888
|
+
CD /D ${q(cwd)}\r
|
|
33672
33889
|
${command} >> ${q(logPath)} 2>&1\r
|
|
33673
33890
|
`
|
|
33674
33891
|
);
|
|
@@ -33694,10 +33911,10 @@ ${command} >> ${q(logPath)} 2>&1\r
|
|
|
33694
33911
|
|
|
33695
33912
|
// src/dev-servers/manager/shell-spawn/try-spawn-inherit.ts
|
|
33696
33913
|
import { spawn as spawn9 } from "node:child_process";
|
|
33697
|
-
function trySpawnInheritStdio(command, env,
|
|
33914
|
+
function trySpawnInheritStdio(command, env, cwd, signal) {
|
|
33698
33915
|
const opts = {
|
|
33699
33916
|
env,
|
|
33700
|
-
cwd
|
|
33917
|
+
cwd,
|
|
33701
33918
|
stdio: "inherit",
|
|
33702
33919
|
...signal ? { signal } : {}
|
|
33703
33920
|
};
|
|
@@ -33713,27 +33930,27 @@ function trySpawnInheritStdio(command, env, cwd3, signal) {
|
|
|
33713
33930
|
}
|
|
33714
33931
|
|
|
33715
33932
|
// src/dev-servers/manager/shell-spawn/shell-spawn.ts
|
|
33716
|
-
function shellSpawn(command, env,
|
|
33933
|
+
function shellSpawn(command, env, cwd, options) {
|
|
33717
33934
|
const signal = options?.signal;
|
|
33718
|
-
const piped = trySpawnPipedViaSh(command, env,
|
|
33935
|
+
const piped = trySpawnPipedViaSh(command, env, cwd, signal);
|
|
33719
33936
|
if (piped.ok) {
|
|
33720
33937
|
return piped.result;
|
|
33721
33938
|
}
|
|
33722
33939
|
let lastErr = piped.lastErr;
|
|
33723
|
-
const shellTrueProc = trySpawnShellTruePiped(command, env,
|
|
33940
|
+
const shellTrueProc = trySpawnShellTruePiped(command, env, cwd, devNullReadFd(), signal);
|
|
33724
33941
|
if (shellTrueProc) {
|
|
33725
33942
|
return { proc: shellTrueProc, pipedStdoutStderr: true };
|
|
33726
33943
|
}
|
|
33727
|
-
const fileCapture = trySpawnMergedLogFile(command, env,
|
|
33944
|
+
const fileCapture = trySpawnMergedLogFile(command, env, cwd, signal);
|
|
33728
33945
|
if (fileCapture) {
|
|
33729
33946
|
return fileCapture;
|
|
33730
33947
|
}
|
|
33731
|
-
const scriptCapture = process.platform === "win32" ? trySpawnShellScriptLogRedirectWin(command, env,
|
|
33948
|
+
const scriptCapture = process.platform === "win32" ? trySpawnShellScriptLogRedirectWin(command, env, cwd, signal) : trySpawnShellScriptLogRedirectUnix(command, env, cwd, signal);
|
|
33732
33949
|
if (scriptCapture) {
|
|
33733
33950
|
return scriptCapture;
|
|
33734
33951
|
}
|
|
33735
33952
|
try {
|
|
33736
|
-
return trySpawnInheritStdio(command, env,
|
|
33953
|
+
return trySpawnInheritStdio(command, env, cwd, signal);
|
|
33737
33954
|
} catch (e) {
|
|
33738
33955
|
throw lastErr instanceof Error ? lastErr : e instanceof Error ? e : new Error(String(e));
|
|
33739
33956
|
}
|
|
@@ -33791,9 +34008,11 @@ var DevServerManager = class {
|
|
|
33791
34008
|
abortControllersByServerId = /* @__PURE__ */ new Map();
|
|
33792
34009
|
getWs;
|
|
33793
34010
|
log;
|
|
34011
|
+
getBridgeCwd;
|
|
33794
34012
|
constructor(options) {
|
|
33795
34013
|
this.getWs = options.getWs;
|
|
33796
34014
|
this.log = options.log;
|
|
34015
|
+
this.getBridgeCwd = options.getBridgeCwd ?? (() => process.cwd());
|
|
33797
34016
|
}
|
|
33798
34017
|
attachFirehose(send) {
|
|
33799
34018
|
this.firehoseSend = send;
|
|
@@ -33917,7 +34136,7 @@ var DevServerManager = class {
|
|
|
33917
34136
|
this.sendStatus(serverId, "starting", void 0, emptyTails());
|
|
33918
34137
|
const ac = new AbortController();
|
|
33919
34138
|
this.abortControllersByServerId.set(serverId, ac);
|
|
33920
|
-
const
|
|
34139
|
+
const cwd = this.getBridgeCwd();
|
|
33921
34140
|
const childEnv = envForSpawn(process.env, def.env, def.ports);
|
|
33922
34141
|
const cmd = substituteCommand(def.command.trim(), childEnv);
|
|
33923
34142
|
const title = def.name.trim() || serverId.slice(0, 8);
|
|
@@ -33932,7 +34151,7 @@ var DevServerManager = class {
|
|
|
33932
34151
|
let mergedLogPath;
|
|
33933
34152
|
let mergedCleanupDir;
|
|
33934
34153
|
try {
|
|
33935
|
-
const spawned = shellSpawn(cmd, childEnv,
|
|
34154
|
+
const spawned = shellSpawn(cmd, childEnv, cwd, {
|
|
33936
34155
|
signal: ac.signal
|
|
33937
34156
|
});
|
|
33938
34157
|
proc = spawned.proc;
|
|
@@ -33941,7 +34160,7 @@ var DevServerManager = class {
|
|
|
33941
34160
|
mergedCleanupDir = spawned.mergedLogCleanupDir;
|
|
33942
34161
|
} catch (e) {
|
|
33943
34162
|
const msg = e instanceof Error ? e.message : String(e);
|
|
33944
|
-
this.log(`[dev-server] ${title}
|
|
34163
|
+
this.log(`[dev-server] Failed to start ${title}: ${msg}`);
|
|
33945
34164
|
this.abortControllersByServerId.delete(serverId);
|
|
33946
34165
|
this.sendStatus(serverId, "error", msg, emptyTails());
|
|
33947
34166
|
return;
|
|
@@ -34173,7 +34392,7 @@ function startStreamingProxy(ws, log2, pr) {
|
|
|
34173
34392
|
},
|
|
34174
34393
|
onEnd: () => sendWsMessage(ws, { type: "proxy_result_end", id: pr.id }),
|
|
34175
34394
|
onError: (error40) => {
|
|
34176
|
-
log2(`[Proxy and log service]
|
|
34395
|
+
log2(`[Proxy and log service] Streaming preview failed: ${error40}`);
|
|
34177
34396
|
sendWsMessage(ws, { type: "proxy_result_error", id: pr.id, error: error40 });
|
|
34178
34397
|
}
|
|
34179
34398
|
});
|
|
@@ -34246,11 +34465,11 @@ var handleProxyMessage = (msg, deps) => {
|
|
|
34246
34465
|
return;
|
|
34247
34466
|
}
|
|
34248
34467
|
void proxyToLocal(pr).then((res) => {
|
|
34249
|
-
if (res.error) deps.log(`[Proxy and log service]
|
|
34468
|
+
if (res.error) deps.log(`[Proxy and log service] Preview proxy failed: ${res.error}`);
|
|
34250
34469
|
sendWsMessage(deps.ws, { type: "proxy_result", ...res, id: pr.id });
|
|
34251
34470
|
}).catch((err) => {
|
|
34252
34471
|
deps.log(
|
|
34253
|
-
`[Proxy and log service]
|
|
34472
|
+
`[Proxy and log service] Preview proxy failed: ${err instanceof Error ? err.message : String(err)}`
|
|
34254
34473
|
);
|
|
34255
34474
|
sendWsMessage(deps.ws, { type: "proxy_result", id: pr.id, error: String(err) });
|
|
34256
34475
|
});
|
|
@@ -34287,6 +34506,7 @@ function tryConsumeBinaryProxyBody(raw, deps) {
|
|
|
34287
34506
|
}
|
|
34288
34507
|
|
|
34289
34508
|
// src/firehose/connect-firehose.ts
|
|
34509
|
+
var FIREHOSE_CLIENT_PING_MS = 25e3;
|
|
34290
34510
|
function connectFirehose(options) {
|
|
34291
34511
|
const { firehoseServerUrl, workspaceId, bridgeName, proxyPorts, log: log2, devServerManager, onOpen, onClose } = options;
|
|
34292
34512
|
const wsUrl = buildFirehoseCliWsUrl(firehoseServerUrl);
|
|
@@ -34295,6 +34515,13 @@ function connectFirehose(options) {
|
|
|
34295
34515
|
wsOptions.agent = new https2.Agent({ rejectUnauthorized: false });
|
|
34296
34516
|
}
|
|
34297
34517
|
const ws = new wrapper_default(wsUrl, wsOptions);
|
|
34518
|
+
let clientPingTimer = null;
|
|
34519
|
+
function clearClientPing() {
|
|
34520
|
+
if (clientPingTimer != null) {
|
|
34521
|
+
clearInterval(clientPingTimer);
|
|
34522
|
+
clientPingTimer = null;
|
|
34523
|
+
}
|
|
34524
|
+
}
|
|
34298
34525
|
const firehoseSend = (payload) => {
|
|
34299
34526
|
sendWsMessage(ws, payload);
|
|
34300
34527
|
};
|
|
@@ -34307,6 +34534,15 @@ function connectFirehose(options) {
|
|
|
34307
34534
|
startStreamingProxy: (pr) => startStreamingProxy(ws, log2, pr)
|
|
34308
34535
|
};
|
|
34309
34536
|
ws.on("open", () => {
|
|
34537
|
+
clearClientPing();
|
|
34538
|
+
clientPingTimer = setInterval(() => {
|
|
34539
|
+
if (ws.readyState === wrapper_default.OPEN) {
|
|
34540
|
+
try {
|
|
34541
|
+
ws.ping();
|
|
34542
|
+
} catch {
|
|
34543
|
+
}
|
|
34544
|
+
}
|
|
34545
|
+
}, FIREHOSE_CLIENT_PING_MS);
|
|
34310
34546
|
onOpen?.();
|
|
34311
34547
|
devServerManager.attachFirehose(firehoseSend);
|
|
34312
34548
|
sendWsMessage(ws, { type: "identify", workspaceId, bridgeName, proxyPorts });
|
|
@@ -34324,35 +34560,30 @@ function connectFirehose(options) {
|
|
|
34324
34560
|
});
|
|
34325
34561
|
});
|
|
34326
34562
|
ws.on("close", (code, reason) => {
|
|
34563
|
+
clearClientPing();
|
|
34327
34564
|
devServerManager.detachFirehose();
|
|
34328
|
-
|
|
34329
|
-
|
|
34330
|
-
"[Proxy and log service]",
|
|
34331
|
-
code,
|
|
34332
|
-
typeof reason === "string" ? reason : reason.toString(),
|
|
34333
|
-
"reconnects automatically if the bridge service stays connected"
|
|
34334
|
-
)
|
|
34335
|
-
);
|
|
34336
|
-
onClose?.();
|
|
34565
|
+
const reasonStr = typeof reason === "string" ? reason : reason.toString();
|
|
34566
|
+
onClose?.(code, reasonStr);
|
|
34337
34567
|
});
|
|
34338
34568
|
ws.on("error", (err) => {
|
|
34569
|
+
clearClientPing();
|
|
34339
34570
|
log2(`[Proxy and log service] WebSocket error: ${err.message}`);
|
|
34340
34571
|
});
|
|
34341
34572
|
return {
|
|
34342
34573
|
close() {
|
|
34574
|
+
clearClientPing();
|
|
34343
34575
|
devServerManager.detachFirehose();
|
|
34344
34576
|
try {
|
|
34345
34577
|
ws.removeAllListeners();
|
|
34346
34578
|
ws.close();
|
|
34347
34579
|
} catch {
|
|
34348
34580
|
}
|
|
34349
|
-
}
|
|
34581
|
+
},
|
|
34582
|
+
isConnected: () => ws.readyState === wrapper_default.OPEN
|
|
34350
34583
|
};
|
|
34351
34584
|
}
|
|
34352
34585
|
|
|
34353
34586
|
// src/bridge/connection/create-bridge-identified-handler.ts
|
|
34354
|
-
var FH_RECONNECT_BASE_MS = 2e3;
|
|
34355
|
-
var FH_RECONNECT_MAX_MS = 3e4;
|
|
34356
34587
|
function createOnBridgeIdentified(opts) {
|
|
34357
34588
|
const { sessionWorktreeManager, devServerManager, firehoseServerUrl, workspaceId, state, logFn } = opts;
|
|
34358
34589
|
function clearFirehoseReconnectTimer() {
|
|
@@ -34361,6 +34592,14 @@ function createOnBridgeIdentified(opts) {
|
|
|
34361
34592
|
state.firehoseReconnectTimeout = null;
|
|
34362
34593
|
}
|
|
34363
34594
|
}
|
|
34595
|
+
function firehoseCtx() {
|
|
34596
|
+
return {
|
|
34597
|
+
closedByUser: state.closedByUser,
|
|
34598
|
+
currentWs: state.currentWs,
|
|
34599
|
+
firehoseHandle: state.firehoseHandle,
|
|
34600
|
+
firehoseQuiet: state.firehoseQuiet
|
|
34601
|
+
};
|
|
34602
|
+
}
|
|
34364
34603
|
function attachFirehose(params) {
|
|
34365
34604
|
state.lastFirehoseParams = params;
|
|
34366
34605
|
clearFirehoseReconnectTimer();
|
|
@@ -34379,37 +34618,52 @@ function createOnBridgeIdentified(opts) {
|
|
|
34379
34618
|
devServerManager,
|
|
34380
34619
|
onOpen: () => {
|
|
34381
34620
|
if (myGen !== state.firehoseGeneration) return;
|
|
34621
|
+
clearFirehoseReconnectQuietOnOpen({ firehoseQuiet: state.firehoseQuiet }, logFn);
|
|
34622
|
+
const logOpenAsFirehoseReconnect = state.firehoseReconnectAttempt > 0;
|
|
34382
34623
|
state.firehoseReconnectAttempt = 0;
|
|
34624
|
+
if (logOpenAsFirehoseReconnect) {
|
|
34625
|
+
logFn("[Proxy and log service] reconnected.");
|
|
34626
|
+
}
|
|
34383
34627
|
},
|
|
34384
|
-
onClose: () => {
|
|
34628
|
+
onClose: (code, reason) => {
|
|
34385
34629
|
if (myGen !== state.firehoseGeneration) return;
|
|
34386
34630
|
state.firehoseHandle = null;
|
|
34387
34631
|
if (state.closedByUser) return;
|
|
34388
34632
|
const main2 = state.currentWs;
|
|
34389
34633
|
if (!main2 || main2.readyState !== wrapper_default.OPEN) {
|
|
34390
|
-
logFn(
|
|
34634
|
+
logFn(
|
|
34635
|
+
`${PROXY_AND_LOG_SERVICE_LABEL} Not reconnecting preview and log stream: main bridge connection is not open.`
|
|
34636
|
+
);
|
|
34391
34637
|
return;
|
|
34392
34638
|
}
|
|
34639
|
+
beginFirehoseDeferredDisconnect(firehoseCtx(), code, reason, logFn);
|
|
34393
34640
|
clearFirehoseReconnectTimer();
|
|
34394
|
-
const delay2 =
|
|
34395
|
-
FH_RECONNECT_BASE_MS * 2 ** state.firehoseReconnectAttempt,
|
|
34396
|
-
FH_RECONNECT_MAX_MS
|
|
34397
|
-
);
|
|
34641
|
+
const delay2 = reconnectDelayMs(state.firehoseReconnectAttempt);
|
|
34398
34642
|
state.firehoseReconnectAttempt += 1;
|
|
34399
|
-
|
|
34400
|
-
|
|
34643
|
+
logNextReconnectAttempt(
|
|
34644
|
+
logFn,
|
|
34645
|
+
PROXY_AND_LOG_SERVICE_LABEL,
|
|
34646
|
+
state.firehoseQuiet,
|
|
34647
|
+
delay2,
|
|
34648
|
+
state.firehoseReconnectAttempt
|
|
34401
34649
|
);
|
|
34402
34650
|
state.firehoseReconnectTimeout = setTimeout(() => {
|
|
34403
34651
|
state.firehoseReconnectTimeout = null;
|
|
34404
34652
|
if (state.closedByUser) return;
|
|
34405
34653
|
const w = state.currentWs;
|
|
34406
34654
|
if (!w || w.readyState !== wrapper_default.OPEN) {
|
|
34407
|
-
|
|
34655
|
+
if (state.firehoseQuiet.verboseLogs) {
|
|
34656
|
+
logFn(
|
|
34657
|
+
`${PROXY_AND_LOG_SERVICE_LABEL} Reconnect skipped: main bridge connection closed before preview stream could reconnect.`
|
|
34658
|
+
);
|
|
34659
|
+
}
|
|
34408
34660
|
return;
|
|
34409
34661
|
}
|
|
34410
34662
|
const p = state.lastFirehoseParams;
|
|
34411
34663
|
if (!p) {
|
|
34412
|
-
|
|
34664
|
+
if (state.firehoseQuiet.verboseLogs) {
|
|
34665
|
+
logFn(`${PROXY_AND_LOG_SERVICE_LABEL} Reconnect skipped: no stored connection parameters.`);
|
|
34666
|
+
}
|
|
34413
34667
|
return;
|
|
34414
34668
|
}
|
|
34415
34669
|
attachFirehose(p);
|
|
@@ -34492,10 +34746,12 @@ function createSendLocalSkillsReport(getWs, logFn) {
|
|
|
34492
34746
|
try {
|
|
34493
34747
|
const socket = getWs();
|
|
34494
34748
|
if (!socket || socket.readyState !== wrapper_default.OPEN) return;
|
|
34495
|
-
const skills2 = discoverLocalSkills(
|
|
34749
|
+
const skills2 = discoverLocalSkills(getBridgeWorkspaceDirectory());
|
|
34496
34750
|
socket.send(JSON.stringify({ type: "local_skills", skills: skills2 }));
|
|
34497
34751
|
} catch (e) {
|
|
34498
|
-
logFn(
|
|
34752
|
+
logFn(
|
|
34753
|
+
`[Bridge service] Local skills report failed: ${e instanceof Error ? e.message : String(e)}`
|
|
34754
|
+
);
|
|
34499
34755
|
}
|
|
34500
34756
|
};
|
|
34501
34757
|
}
|
|
@@ -34508,12 +34764,15 @@ function createReportAutoDetectedAgents(getWs, logFn) {
|
|
|
34508
34764
|
sendWsMessage(socket, { type: "auto_detected_agents_report", agentTypes: types });
|
|
34509
34765
|
}
|
|
34510
34766
|
} catch (e) {
|
|
34511
|
-
logFn(
|
|
34767
|
+
logFn(
|
|
34768
|
+
`[Bridge service] Auto-detected agents report failed: ${e instanceof Error ? e.message : String(e)}`
|
|
34769
|
+
);
|
|
34512
34770
|
}
|
|
34513
34771
|
};
|
|
34514
34772
|
}
|
|
34515
34773
|
|
|
34516
34774
|
// src/bridge/connection/create-bridge-connection.ts
|
|
34775
|
+
var BRIDGE_CLIENT_PING_MS = 25e3;
|
|
34517
34776
|
async function createBridgeConnection(options) {
|
|
34518
34777
|
const { apiUrl, workspaceId, justAuthenticated, onAuthInvalid, persistTokens } = options;
|
|
34519
34778
|
const firehoseServerUrl = options.firehoseServerUrl ?? options.proxyServerUrl;
|
|
@@ -34524,13 +34783,16 @@ async function createBridgeConnection(options) {
|
|
|
34524
34783
|
const state = {
|
|
34525
34784
|
closedByUser: false,
|
|
34526
34785
|
reconnectAttempt: 0,
|
|
34786
|
+
logBridgeOpenAsReconnect: false,
|
|
34527
34787
|
reconnectTimeout: null,
|
|
34528
34788
|
currentWs: null,
|
|
34789
|
+
mainQuiet: createEmptyReconnectQuietSlot(),
|
|
34529
34790
|
firehoseHandle: null,
|
|
34530
34791
|
lastFirehoseParams: null,
|
|
34531
34792
|
firehoseReconnectTimeout: null,
|
|
34532
34793
|
firehoseReconnectAttempt: 0,
|
|
34533
|
-
firehoseGeneration: 0
|
|
34794
|
+
firehoseGeneration: 0,
|
|
34795
|
+
firehoseQuiet: createEmptyReconnectQuietSlot()
|
|
34534
34796
|
};
|
|
34535
34797
|
const worktreesRootAbs = options.worktreesRootAbs ?? defaultWorktreesRootAbs();
|
|
34536
34798
|
const sessionWorktreeManager = new SessionWorktreeManager({
|
|
@@ -34542,7 +34804,7 @@ async function createBridgeConnection(options) {
|
|
|
34542
34804
|
function getWs() {
|
|
34543
34805
|
return state.currentWs;
|
|
34544
34806
|
}
|
|
34545
|
-
const devServerManager = new DevServerManager({ getWs, log: logFn });
|
|
34807
|
+
const devServerManager = new DevServerManager({ getWs, log: logFn, getBridgeCwd: getBridgeWorkspaceDirectory });
|
|
34546
34808
|
const onBridgeIdentified = createOnBridgeIdentified({
|
|
34547
34809
|
sessionWorktreeManager,
|
|
34548
34810
|
devServerManager,
|
|
@@ -34554,7 +34816,13 @@ async function createBridgeConnection(options) {
|
|
|
34554
34816
|
const sendLocalSkillsReport = createSendLocalSkillsReport(getWs, logFn);
|
|
34555
34817
|
const reportAutoDetectedAgents = createReportAutoDetectedAgents(getWs, logFn);
|
|
34556
34818
|
function handleOpen() {
|
|
34819
|
+
const logOpenAsPostRefreshReconnect = state.logBridgeOpenAsReconnect;
|
|
34820
|
+
clearMainBridgeReconnectQuietOnOpen(state, logFn);
|
|
34557
34821
|
state.reconnectAttempt = 0;
|
|
34822
|
+
state.logBridgeOpenAsReconnect = false;
|
|
34823
|
+
if (logOpenAsPostRefreshReconnect) {
|
|
34824
|
+
logFn("[Bridge service] reconnected.");
|
|
34825
|
+
}
|
|
34558
34826
|
const socket = getWs();
|
|
34559
34827
|
if (socket) {
|
|
34560
34828
|
sendWsMessage(socket, { type: "identify", role: "cli" });
|
|
@@ -34571,11 +34839,9 @@ async function createBridgeConnection(options) {
|
|
|
34571
34839
|
state.currentWs = null;
|
|
34572
34840
|
if (was) was.removeAllListeners();
|
|
34573
34841
|
const willReconnect = !state.closedByUser;
|
|
34574
|
-
logFn
|
|
34575
|
-
formatWebSocketClose("[Bridge service]", code, reason, willReconnect ? "will schedule reconnect" : "not reconnecting (CLI shutting down)")
|
|
34576
|
-
);
|
|
34842
|
+
beginMainBridgeDeferredDisconnect(state, code, reason, logFn, willReconnect);
|
|
34577
34843
|
if (willReconnect) {
|
|
34578
|
-
|
|
34844
|
+
scheduleMainBridgeReconnect(state, connect, logFn);
|
|
34579
34845
|
}
|
|
34580
34846
|
}
|
|
34581
34847
|
const messageDeps = {
|
|
@@ -34590,6 +34856,10 @@ async function createBridgeConnection(options) {
|
|
|
34590
34856
|
};
|
|
34591
34857
|
function connect() {
|
|
34592
34858
|
if (state.closedByUser) return;
|
|
34859
|
+
if (state.reconnectTimeout != null) {
|
|
34860
|
+
clearTimeout(state.reconnectTimeout);
|
|
34861
|
+
state.reconnectTimeout = null;
|
|
34862
|
+
}
|
|
34593
34863
|
const prev = state.currentWs;
|
|
34594
34864
|
if (prev) {
|
|
34595
34865
|
prev.removeAllListeners();
|
|
@@ -34602,6 +34872,7 @@ async function createBridgeConnection(options) {
|
|
|
34602
34872
|
const url2 = buildBridgeUrl(apiUrl, workspaceId, accessToken);
|
|
34603
34873
|
state.currentWs = createWsBridge({
|
|
34604
34874
|
url: url2,
|
|
34875
|
+
clientPingIntervalMs: BRIDGE_CLIENT_PING_MS,
|
|
34605
34876
|
onAuthInvalid: () => {
|
|
34606
34877
|
if (authRefreshInFlight) return;
|
|
34607
34878
|
void (async () => {
|
|
@@ -34613,8 +34884,9 @@ async function createBridgeConnection(options) {
|
|
|
34613
34884
|
accessToken = next.token;
|
|
34614
34885
|
refreshTok = next.refreshToken;
|
|
34615
34886
|
persistTokens?.({ token: accessToken, refreshToken: refreshTok });
|
|
34616
|
-
logFn("[Bridge service]
|
|
34887
|
+
logFn("[Bridge service] Access token refreshed; reconnecting\u2026");
|
|
34617
34888
|
state.reconnectAttempt = 0;
|
|
34889
|
+
state.logBridgeOpenAsReconnect = true;
|
|
34618
34890
|
authRefreshInFlight = false;
|
|
34619
34891
|
connect();
|
|
34620
34892
|
return;
|
|
@@ -34637,7 +34909,7 @@ async function createBridgeConnection(options) {
|
|
|
34637
34909
|
});
|
|
34638
34910
|
}
|
|
34639
34911
|
connect();
|
|
34640
|
-
const stopFileIndexWatcher = startFileIndexWatcher(
|
|
34912
|
+
const stopFileIndexWatcher = startFileIndexWatcher(getBridgeWorkspaceDirectory());
|
|
34641
34913
|
return {
|
|
34642
34914
|
close: async () => {
|
|
34643
34915
|
stopFileIndexWatcher();
|
|
@@ -34710,7 +34982,7 @@ async function runBridge(options) {
|
|
|
34710
34982
|
});
|
|
34711
34983
|
},
|
|
34712
34984
|
onAuthInvalid: () => {
|
|
34713
|
-
log("[Bridge service] token invalid or revoked; re-authenticating\u2026");
|
|
34985
|
+
log("[Bridge service] Access token invalid or revoked; re-authenticating\u2026");
|
|
34714
34986
|
clearConfigForApi(apiUrl);
|
|
34715
34987
|
void handle.close().then(() => {
|
|
34716
34988
|
void runBridge({ apiUrl, firehoseServerUrl, worktreesRootAbs });
|
|
@@ -34757,7 +35029,7 @@ async function main() {
|
|
|
34757
35029
|
if (opts.cwd && typeof opts.cwd === "string" && opts.cwd.trim()) {
|
|
34758
35030
|
const resolvedCwd = path24.resolve(process.cwd(), opts.cwd.trim());
|
|
34759
35031
|
try {
|
|
34760
|
-
const st =
|
|
35032
|
+
const st = fs21.statSync(resolvedCwd);
|
|
34761
35033
|
if (!st.isDirectory()) {
|
|
34762
35034
|
console.error(`--cwd is not a directory: ${resolvedCwd}`);
|
|
34763
35035
|
process.exit(1);
|
|
@@ -34768,6 +35040,7 @@ async function main() {
|
|
|
34768
35040
|
}
|
|
34769
35041
|
process.chdir(resolvedCwd);
|
|
34770
35042
|
}
|
|
35043
|
+
initBridgeWorkspaceDirectory();
|
|
34771
35044
|
let worktreesRootAbs;
|
|
34772
35045
|
if (opts.worktreesRoot && opts.worktreesRoot.trim()) {
|
|
34773
35046
|
worktreesRootAbs = path24.resolve(opts.worktreesRoot.trim());
|