@buildautomaton/cli 0.1.20 → 0.1.21
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 +678 -412
- package/dist/cli.js.map +4 -4
- package/dist/index.js +650 -388
- package/dist/index.js.map +4 -4
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -25064,7 +25064,7 @@ var {
|
|
|
25064
25064
|
} = import_index.default;
|
|
25065
25065
|
|
|
25066
25066
|
// src/cli-version.ts
|
|
25067
|
-
var CLI_VERSION = "0.1.
|
|
25067
|
+
var CLI_VERSION = "0.1.21".length > 0 ? "0.1.21" : "0.0.0-dev";
|
|
25068
25068
|
|
|
25069
25069
|
// src/cli/defaults.ts
|
|
25070
25070
|
var DEFAULT_API_URL = process.env.BUILDAUTOMATON_API_URL ?? "https://api.buildautomaton.com";
|
|
@@ -25428,17 +25428,17 @@ async function loadOrCreateE2eCertificates(directory) {
|
|
|
25428
25428
|
};
|
|
25429
25429
|
}
|
|
25430
25430
|
|
|
25431
|
-
// src/files/cwd/bridge-
|
|
25431
|
+
// src/files/cwd/bridge-root.ts
|
|
25432
25432
|
import * as path3 from "node:path";
|
|
25433
|
-
var
|
|
25434
|
-
function
|
|
25435
|
-
|
|
25433
|
+
var bridgeRootPath = null;
|
|
25434
|
+
function initBridgeRoot() {
|
|
25435
|
+
bridgeRootPath = path3.resolve(process.cwd());
|
|
25436
25436
|
}
|
|
25437
|
-
function
|
|
25438
|
-
if (
|
|
25439
|
-
|
|
25437
|
+
function getBridgeRoot() {
|
|
25438
|
+
if (bridgeRootPath == null) {
|
|
25439
|
+
bridgeRootPath = path3.resolve(process.cwd());
|
|
25440
25440
|
}
|
|
25441
|
-
return
|
|
25441
|
+
return bridgeRootPath;
|
|
25442
25442
|
}
|
|
25443
25443
|
|
|
25444
25444
|
// src/log.ts
|
|
@@ -26505,6 +26505,21 @@ async function closeBridgeConnection(state, acpManager, devServerManager, log2)
|
|
|
26505
26505
|
say("Shutdown complete.");
|
|
26506
26506
|
}
|
|
26507
26507
|
|
|
26508
|
+
// src/paths/session-layout-paths.ts
|
|
26509
|
+
import * as path5 from "node:path";
|
|
26510
|
+
function resolveIsolatedSessionParentPathFromCheckouts(worktreePaths) {
|
|
26511
|
+
const resolved = worktreePaths.map((p) => path5.resolve(p)).filter(Boolean);
|
|
26512
|
+
if (resolved.length === 0) return null;
|
|
26513
|
+
resolved.sort();
|
|
26514
|
+
return resolved[0];
|
|
26515
|
+
}
|
|
26516
|
+
function resolveSessionParentPathForAgentProcess(resolvedSessionParentPath) {
|
|
26517
|
+
if (resolvedSessionParentPath != null && String(resolvedSessionParentPath).trim() !== "") {
|
|
26518
|
+
return path5.resolve(String(resolvedSessionParentPath).trim());
|
|
26519
|
+
}
|
|
26520
|
+
return getBridgeRoot();
|
|
26521
|
+
}
|
|
26522
|
+
|
|
26508
26523
|
// src/lib/local-agent-auth.ts
|
|
26509
26524
|
var LOCAL_AGENT_AUTH_ERROR_HINTS = {
|
|
26510
26525
|
"kiro-acp": [/not logged in/i, /kiro-cli\s+login/i, /log in with kiro-cli/i],
|
|
@@ -26536,17 +26551,17 @@ function localAgentErrorSuggestsAuth(agentType, errorText) {
|
|
|
26536
26551
|
import { execFile as execFile7 } from "node:child_process";
|
|
26537
26552
|
import { readFile as readFile2, stat as stat2 } from "node:fs/promises";
|
|
26538
26553
|
import { promisify as promisify7 } from "node:util";
|
|
26539
|
-
import * as
|
|
26554
|
+
import * as path8 from "node:path";
|
|
26540
26555
|
|
|
26541
26556
|
// src/git/pre-turn-snapshot.ts
|
|
26542
26557
|
import * as fs9 from "node:fs";
|
|
26543
|
-
import * as
|
|
26558
|
+
import * as path7 from "node:path";
|
|
26544
26559
|
import { execFile as execFile6 } from "node:child_process";
|
|
26545
26560
|
import { promisify as promisify6 } from "node:util";
|
|
26546
26561
|
|
|
26547
26562
|
// src/git/discover-repos.ts
|
|
26548
26563
|
import * as fs8 from "node:fs";
|
|
26549
|
-
import * as
|
|
26564
|
+
import * as path6 from "node:path";
|
|
26550
26565
|
|
|
26551
26566
|
// ../../node_modules/.pnpm/simple-git@3.32.3/node_modules/simple-git/dist/esm/index.js
|
|
26552
26567
|
var import_file_exists = __toESM(require_dist(), 1);
|
|
@@ -31131,9 +31146,9 @@ async function isGitRepoDirectory(dirPath) {
|
|
|
31131
31146
|
}
|
|
31132
31147
|
|
|
31133
31148
|
// src/git/discover-repos.ts
|
|
31134
|
-
async function discoverGitRepos(cwd =
|
|
31149
|
+
async function discoverGitRepos(cwd = getBridgeRoot()) {
|
|
31135
31150
|
const result = [];
|
|
31136
|
-
const cwdResolved =
|
|
31151
|
+
const cwdResolved = path6.resolve(cwd);
|
|
31137
31152
|
if (await isGitRepoDirectory(cwdResolved)) {
|
|
31138
31153
|
const remoteUrl = await getRemoteOriginUrl(cwdResolved);
|
|
31139
31154
|
result.push({ absolutePath: cwdResolved, remoteUrl });
|
|
@@ -31146,7 +31161,7 @@ async function discoverGitRepos(cwd = getBridgeWorkspaceDirectory()) {
|
|
|
31146
31161
|
}
|
|
31147
31162
|
for (const ent of entries) {
|
|
31148
31163
|
if (!ent.isDirectory()) continue;
|
|
31149
|
-
const childPath =
|
|
31164
|
+
const childPath = path6.join(cwdResolved, ent.name);
|
|
31150
31165
|
if (await isGitRepoDirectory(childPath)) {
|
|
31151
31166
|
const remoteUrl = await getRemoteOriginUrl(childPath);
|
|
31152
31167
|
result.push({ absolutePath: childPath, remoteUrl });
|
|
@@ -31154,12 +31169,12 @@ async function discoverGitRepos(cwd = getBridgeWorkspaceDirectory()) {
|
|
|
31154
31169
|
}
|
|
31155
31170
|
return result;
|
|
31156
31171
|
}
|
|
31157
|
-
async function discoverGitReposUnderRoot(
|
|
31158
|
-
const root =
|
|
31172
|
+
async function discoverGitReposUnderRoot(rootPath) {
|
|
31173
|
+
const root = path6.resolve(rootPath);
|
|
31159
31174
|
const roots = [];
|
|
31160
31175
|
async function walk(dir) {
|
|
31161
31176
|
if (await isGitRepoDirectory(dir)) {
|
|
31162
|
-
roots.push(
|
|
31177
|
+
roots.push(path6.resolve(dir));
|
|
31163
31178
|
return;
|
|
31164
31179
|
}
|
|
31165
31180
|
let entries;
|
|
@@ -31170,7 +31185,7 @@ async function discoverGitReposUnderRoot(rootAbs) {
|
|
|
31170
31185
|
}
|
|
31171
31186
|
for (const ent of entries) {
|
|
31172
31187
|
if (!ent.isDirectory() || ent.name === ".git") continue;
|
|
31173
|
-
await walk(
|
|
31188
|
+
await walk(path6.join(dir, ent.name));
|
|
31174
31189
|
}
|
|
31175
31190
|
}
|
|
31176
31191
|
await walk(root);
|
|
@@ -31186,7 +31201,7 @@ async function discoverGitReposUnderRoot(rootAbs) {
|
|
|
31186
31201
|
// src/git/pre-turn-snapshot.ts
|
|
31187
31202
|
var execFileAsync5 = promisify6(execFile6);
|
|
31188
31203
|
function snapshotsDirForCwd(agentCwd) {
|
|
31189
|
-
return
|
|
31204
|
+
return path7.join(agentCwd, ".buildautomaton", "snapshots");
|
|
31190
31205
|
}
|
|
31191
31206
|
async function gitStashCreate(repoRoot, log2) {
|
|
31192
31207
|
try {
|
|
@@ -31213,14 +31228,20 @@ async function gitRun(repoRoot, args, log2, label) {
|
|
|
31213
31228
|
}
|
|
31214
31229
|
}
|
|
31215
31230
|
async function resolveSnapshotRepoRoots(options) {
|
|
31216
|
-
const { worktreePaths, fallbackCwd, log: log2 } = options;
|
|
31231
|
+
const { worktreePaths, fallbackCwd, sessionId, log: log2 } = options;
|
|
31217
31232
|
if (worktreePaths?.length) {
|
|
31218
|
-
const uniq = [...new Set(worktreePaths.map((p) =>
|
|
31233
|
+
const uniq = [...new Set(worktreePaths.map((p) => path7.resolve(p)))];
|
|
31219
31234
|
return uniq;
|
|
31220
31235
|
}
|
|
31221
31236
|
try {
|
|
31222
31237
|
const repos = await discoverGitReposUnderRoot(fallbackCwd);
|
|
31223
|
-
|
|
31238
|
+
const mapped = repos.map((r) => r.absolutePath);
|
|
31239
|
+
const sid = sessionId?.trim();
|
|
31240
|
+
if (sid) {
|
|
31241
|
+
const filtered = mapped.filter((root) => path7.basename(root) === sid);
|
|
31242
|
+
if (filtered.length > 0) return filtered;
|
|
31243
|
+
}
|
|
31244
|
+
return mapped;
|
|
31224
31245
|
} catch (e) {
|
|
31225
31246
|
log2(`[snapshot] Discover repositories failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
31226
31247
|
return [];
|
|
@@ -31247,7 +31268,7 @@ async function capturePreTurnSnapshot(options) {
|
|
|
31247
31268
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
31248
31269
|
repos
|
|
31249
31270
|
};
|
|
31250
|
-
const filePath =
|
|
31271
|
+
const filePath = path7.join(dir, `${runId}.json`);
|
|
31251
31272
|
try {
|
|
31252
31273
|
fs9.writeFileSync(filePath, JSON.stringify(payload, null, 2), "utf8");
|
|
31253
31274
|
} catch (e) {
|
|
@@ -31285,7 +31306,7 @@ async function applyPreTurnSnapshot(filePath, log2) {
|
|
|
31285
31306
|
return { ok: true };
|
|
31286
31307
|
}
|
|
31287
31308
|
function snapshotFilePath(agentCwd, runId) {
|
|
31288
|
-
return
|
|
31309
|
+
return path7.join(snapshotsDirForCwd(agentCwd), `${runId}.json`);
|
|
31289
31310
|
}
|
|
31290
31311
|
|
|
31291
31312
|
// src/git/session-git-queue.ts
|
|
@@ -31334,7 +31355,7 @@ async function collectTurnGitDiffFromPreTurnSnapshot(options) {
|
|
|
31334
31355
|
continue;
|
|
31335
31356
|
}
|
|
31336
31357
|
const lines = namesRaw.split("\n").map((l) => l.trim()).filter(Boolean);
|
|
31337
|
-
const slug =
|
|
31358
|
+
const slug = path8.basename(repo.path).replace(/[^\w.-]+/g, "_") || "repo";
|
|
31338
31359
|
for (const rel of lines) {
|
|
31339
31360
|
if (rel.includes("..")) continue;
|
|
31340
31361
|
try {
|
|
@@ -31348,8 +31369,8 @@ async function collectTurnGitDiffFromPreTurnSnapshot(options) {
|
|
|
31348
31369
|
);
|
|
31349
31370
|
if (!patchContent.trim()) continue;
|
|
31350
31371
|
const displayPath = multiRepo ? `${slug}/${rel}` : rel;
|
|
31351
|
-
const
|
|
31352
|
-
const newText = await readWorkspaceFileAsUtf8(
|
|
31372
|
+
const workspaceFilePath = path8.join(repo.path, rel);
|
|
31373
|
+
const newText = await readWorkspaceFileAsUtf8(workspaceFilePath);
|
|
31353
31374
|
sendSessionUpdate({
|
|
31354
31375
|
type: "session_file_change",
|
|
31355
31376
|
sessionId,
|
|
@@ -31964,7 +31985,7 @@ async function sendPromptToAgent(options) {
|
|
|
31964
31985
|
|
|
31965
31986
|
// src/agents/acp/ensure-acp-client.ts
|
|
31966
31987
|
import * as fs10 from "node:fs";
|
|
31967
|
-
import * as
|
|
31988
|
+
import * as path12 from "node:path";
|
|
31968
31989
|
|
|
31969
31990
|
// src/error-message.ts
|
|
31970
31991
|
function errorMessage(err) {
|
|
@@ -32003,7 +32024,7 @@ async function isCommandOnPath(command, timeoutMs = 4e3) {
|
|
|
32003
32024
|
// src/agents/acp/clients/sdk-stdio-acp-client.ts
|
|
32004
32025
|
import { spawn as spawn2 } from "node:child_process";
|
|
32005
32026
|
import { mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
32006
|
-
import { dirname } from "node:path";
|
|
32027
|
+
import { dirname as dirname2 } from "node:path";
|
|
32007
32028
|
import { Readable, Writable } from "node:stream";
|
|
32008
32029
|
|
|
32009
32030
|
// src/files/diff/unified-diff.ts
|
|
@@ -32053,21 +32074,21 @@ function editSnippetToUnifiedDiff(filePath, oldText, newText) {
|
|
|
32053
32074
|
}
|
|
32054
32075
|
|
|
32055
32076
|
// src/agents/acp/safe-fs-path.ts
|
|
32056
|
-
import * as
|
|
32077
|
+
import * as path9 from "node:path";
|
|
32057
32078
|
function resolveSafePathUnderCwd(cwd, filePath) {
|
|
32058
32079
|
const trimmed2 = filePath.trim();
|
|
32059
32080
|
if (!trimmed2) return null;
|
|
32060
|
-
const normalizedCwd =
|
|
32061
|
-
const resolved =
|
|
32062
|
-
const rel =
|
|
32063
|
-
if (rel.startsWith("..") ||
|
|
32081
|
+
const normalizedCwd = path9.resolve(cwd);
|
|
32082
|
+
const resolved = path9.isAbsolute(trimmed2) ? path9.normalize(trimmed2) : path9.resolve(normalizedCwd, trimmed2);
|
|
32083
|
+
const rel = path9.relative(normalizedCwd, resolved);
|
|
32084
|
+
if (rel.startsWith("..") || path9.isAbsolute(rel)) return null;
|
|
32064
32085
|
return resolved;
|
|
32065
32086
|
}
|
|
32066
32087
|
function toDisplayPathRelativeToCwd(cwd, absolutePath) {
|
|
32067
|
-
const normalizedCwd =
|
|
32068
|
-
const rel =
|
|
32069
|
-
if (!rel || rel === "") return
|
|
32070
|
-
return rel.split(
|
|
32088
|
+
const normalizedCwd = path9.resolve(cwd);
|
|
32089
|
+
const rel = path9.relative(normalizedCwd, path9.resolve(absolutePath));
|
|
32090
|
+
if (!rel || rel === "") return path9.basename(absolutePath);
|
|
32091
|
+
return rel.split(path9.sep).join("/");
|
|
32071
32092
|
}
|
|
32072
32093
|
|
|
32073
32094
|
// src/agents/acp/clients/agent-stderr-capture.ts
|
|
@@ -32147,6 +32168,52 @@ function createSdkStdioExtNotificationHandler(options) {
|
|
|
32147
32168
|
}
|
|
32148
32169
|
}
|
|
32149
32170
|
|
|
32171
|
+
// src/agents/acp/enrich-acp-permission-rpc-result.ts
|
|
32172
|
+
var META_KEY = "permissionOptionKind";
|
|
32173
|
+
function optionRecordId(rec) {
|
|
32174
|
+
const raw = rec.optionId ?? rec.id;
|
|
32175
|
+
if (typeof raw === "string" && raw.trim() !== "") return raw.trim();
|
|
32176
|
+
if (typeof raw === "number" && Number.isFinite(raw)) return String(raw);
|
|
32177
|
+
return "";
|
|
32178
|
+
}
|
|
32179
|
+
function enrichAcpPermissionRpcResultFromRequestParams(result, params) {
|
|
32180
|
+
if (params == null || result == null || typeof result !== "object" || Array.isArray(result)) {
|
|
32181
|
+
return result;
|
|
32182
|
+
}
|
|
32183
|
+
const root = result;
|
|
32184
|
+
const outcome = root.outcome;
|
|
32185
|
+
if (outcome == null || typeof outcome !== "object" || Array.isArray(outcome)) return result;
|
|
32186
|
+
const o = outcome;
|
|
32187
|
+
if (o.outcome !== "selected" || typeof o.optionId !== "string" || o.optionId.trim() === "") {
|
|
32188
|
+
return result;
|
|
32189
|
+
}
|
|
32190
|
+
const selectedId = o.optionId.trim();
|
|
32191
|
+
const prevMeta = o._meta != null && typeof o._meta === "object" && !Array.isArray(o._meta) ? o._meta : {};
|
|
32192
|
+
if (typeof prevMeta[META_KEY] === "string" && prevMeta[META_KEY].trim() !== "") {
|
|
32193
|
+
return result;
|
|
32194
|
+
}
|
|
32195
|
+
const rawOpts = Array.isArray(params.options) ? params.options : [];
|
|
32196
|
+
let matchedKind;
|
|
32197
|
+
for (const item of rawOpts) {
|
|
32198
|
+
if (item == null || typeof item !== "object" || Array.isArray(item)) continue;
|
|
32199
|
+
const rec = item;
|
|
32200
|
+
const id = optionRecordId(rec);
|
|
32201
|
+
if (!id || id !== selectedId) continue;
|
|
32202
|
+
if (typeof rec.kind === "string" && rec.kind.trim() !== "") {
|
|
32203
|
+
matchedKind = rec.kind.trim();
|
|
32204
|
+
break;
|
|
32205
|
+
}
|
|
32206
|
+
}
|
|
32207
|
+
if (!matchedKind) return result;
|
|
32208
|
+
return {
|
|
32209
|
+
...root,
|
|
32210
|
+
outcome: {
|
|
32211
|
+
...o,
|
|
32212
|
+
_meta: { ...prevMeta, [META_KEY]: matchedKind }
|
|
32213
|
+
}
|
|
32214
|
+
};
|
|
32215
|
+
}
|
|
32216
|
+
|
|
32150
32217
|
// src/agents/acp/clients/sdk-stdio-acp-client.ts
|
|
32151
32218
|
function formatSpawnError(err, command) {
|
|
32152
32219
|
if (err.code === "ENOENT") {
|
|
@@ -32168,7 +32235,7 @@ async function createSdkStdioAcpClient(options) {
|
|
|
32168
32235
|
const { ClientSideConnection: ClientSideConnection2, ndJsonStream: ndJsonStream2, PROTOCOL_VERSION: PROTOCOL_VERSION2 } = await Promise.resolve().then(() => (init_acp(), acp_exports));
|
|
32169
32236
|
const {
|
|
32170
32237
|
command,
|
|
32171
|
-
cwd =
|
|
32238
|
+
cwd = getBridgeRoot(),
|
|
32172
32239
|
backendAgentType,
|
|
32173
32240
|
onSessionUpdate,
|
|
32174
32241
|
onRequest,
|
|
@@ -32224,7 +32291,7 @@ async function createSdkStdioAcpClient(options) {
|
|
|
32224
32291
|
onSessionUpdate
|
|
32225
32292
|
});
|
|
32226
32293
|
let permissionSeq = 0;
|
|
32227
|
-
const
|
|
32294
|
+
const pendingPermissionReplies = /* @__PURE__ */ new Map();
|
|
32228
32295
|
const client = (_agent) => ({
|
|
32229
32296
|
async requestPermission(params) {
|
|
32230
32297
|
const requestId = `perm-${++permissionSeq}`;
|
|
@@ -32238,14 +32305,14 @@ async function createSdkStdioAcpClient(options) {
|
|
|
32238
32305
|
} catch {
|
|
32239
32306
|
}
|
|
32240
32307
|
return await new Promise((resolve19) => {
|
|
32241
|
-
|
|
32308
|
+
pendingPermissionReplies.set(requestId, { resolve: resolve19, params: paramsRecord });
|
|
32242
32309
|
});
|
|
32243
32310
|
},
|
|
32244
32311
|
async readTextFile(params) {
|
|
32245
|
-
const
|
|
32246
|
-
if (!
|
|
32312
|
+
const resolvedPath = resolveSafePathUnderCwd(cwd, params.path);
|
|
32313
|
+
if (!resolvedPath) throw new Error("Invalid or disallowed path");
|
|
32247
32314
|
try {
|
|
32248
|
-
let content = readFileSync2(
|
|
32315
|
+
let content = readFileSync2(resolvedPath, "utf8");
|
|
32249
32316
|
content = sliceFileContentRange(content, params.line, params.limit);
|
|
32250
32317
|
return { content };
|
|
32251
32318
|
} catch (e) {
|
|
@@ -32254,17 +32321,17 @@ async function createSdkStdioAcpClient(options) {
|
|
|
32254
32321
|
}
|
|
32255
32322
|
},
|
|
32256
32323
|
async writeTextFile(params) {
|
|
32257
|
-
const
|
|
32258
|
-
if (!
|
|
32324
|
+
const resolvedPath = resolveSafePathUnderCwd(cwd, params.path);
|
|
32325
|
+
if (!resolvedPath) throw new Error("Invalid or disallowed path");
|
|
32259
32326
|
let oldText = "";
|
|
32260
32327
|
try {
|
|
32261
|
-
oldText = readFileSync2(
|
|
32328
|
+
oldText = readFileSync2(resolvedPath, "utf8");
|
|
32262
32329
|
} catch (e) {
|
|
32263
32330
|
if (e.code !== "ENOENT") throw e;
|
|
32264
32331
|
}
|
|
32265
|
-
mkdirSync2(
|
|
32266
|
-
writeFileSync2(
|
|
32267
|
-
const displayPath = toDisplayPathRelativeToCwd(cwd,
|
|
32332
|
+
mkdirSync2(dirname2(resolvedPath), { recursive: true });
|
|
32333
|
+
writeFileSync2(resolvedPath, params.content, "utf8");
|
|
32334
|
+
const displayPath = toDisplayPathRelativeToCwd(cwd, resolvedPath);
|
|
32268
32335
|
const patchContent = editSnippetToUnifiedDiff(displayPath, oldText, params.content);
|
|
32269
32336
|
onFileChange?.({ path: displayPath, oldText, newText: params.content, patchContent });
|
|
32270
32337
|
return {};
|
|
@@ -32346,9 +32413,9 @@ async function createSdkStdioAcpClient(options) {
|
|
|
32346
32413
|
}
|
|
32347
32414
|
},
|
|
32348
32415
|
async cancel() {
|
|
32349
|
-
for (const [id,
|
|
32350
|
-
|
|
32351
|
-
|
|
32416
|
+
for (const [id, entry] of [...pendingPermissionReplies.entries()]) {
|
|
32417
|
+
pendingPermissionReplies.delete(id);
|
|
32418
|
+
entry.resolve({ outcome: { outcome: "cancelled" } });
|
|
32352
32419
|
}
|
|
32353
32420
|
try {
|
|
32354
32421
|
await connection.cancel({ sessionId });
|
|
@@ -32364,10 +32431,11 @@ async function createSdkStdioAcpClient(options) {
|
|
|
32364
32431
|
}
|
|
32365
32432
|
},
|
|
32366
32433
|
resolveRequest(requestId, result) {
|
|
32367
|
-
const
|
|
32368
|
-
if (!
|
|
32369
|
-
|
|
32370
|
-
|
|
32434
|
+
const entry = pendingPermissionReplies.get(requestId);
|
|
32435
|
+
if (!entry) return;
|
|
32436
|
+
pendingPermissionReplies.delete(requestId);
|
|
32437
|
+
const enriched = enrichAcpPermissionRpcResultFromRequestParams(result, entry.params);
|
|
32438
|
+
entry.resolve(enriched);
|
|
32371
32439
|
},
|
|
32372
32440
|
disconnect() {
|
|
32373
32441
|
child.kill();
|
|
@@ -32452,7 +32520,7 @@ __export(cursor_acp_client_exports, {
|
|
|
32452
32520
|
detectLocalAgentPresence: () => detectLocalAgentPresence3
|
|
32453
32521
|
});
|
|
32454
32522
|
import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3 } from "node:fs";
|
|
32455
|
-
import { dirname as
|
|
32523
|
+
import { dirname as dirname3 } from "node:path";
|
|
32456
32524
|
import { spawn as spawn3 } from "node:child_process";
|
|
32457
32525
|
import * as readline from "node:readline";
|
|
32458
32526
|
|
|
@@ -32502,7 +32570,7 @@ function buildCursorAcpSpawnCommand(base, sessionMode) {
|
|
|
32502
32570
|
async function createCursorAcpClient(options) {
|
|
32503
32571
|
const command = buildCursorAcpSpawnCommand(options.command, options.sessionMode);
|
|
32504
32572
|
const {
|
|
32505
|
-
cwd =
|
|
32573
|
+
cwd = getBridgeRoot(),
|
|
32506
32574
|
backendAgentType,
|
|
32507
32575
|
onSessionUpdate,
|
|
32508
32576
|
onRequest,
|
|
@@ -32544,7 +32612,9 @@ async function createCursorAcpClient(options) {
|
|
|
32544
32612
|
child.stdin.write(JSON.stringify({ jsonrpc: "2.0", id, error: { code, message } }) + "\n");
|
|
32545
32613
|
}
|
|
32546
32614
|
function resolveRequest(requestId, result) {
|
|
32547
|
-
|
|
32615
|
+
const pending2 = pendingRequests.get(requestId);
|
|
32616
|
+
const payload = pending2?.method === "session/request_permission" ? enrichAcpPermissionRpcResultFromRequestParams(result, pending2.params) : result;
|
|
32617
|
+
respond(requestId, payload);
|
|
32548
32618
|
pendingRequests.delete(requestId);
|
|
32549
32619
|
}
|
|
32550
32620
|
let promptOutputBuffer = "";
|
|
@@ -32600,14 +32670,14 @@ async function createCursorAcpClient(options) {
|
|
|
32600
32670
|
if (dbgFs) {
|
|
32601
32671
|
console.error(`[acp-fs] ${method} path=${filePath.slice(0, 200)}${filePath.length > 200 ? "\u2026" : ""}`);
|
|
32602
32672
|
}
|
|
32603
|
-
const
|
|
32604
|
-
if (!
|
|
32673
|
+
const resolvedPath = resolveSafePathUnderCwd(cwd, filePath);
|
|
32674
|
+
if (!resolvedPath) {
|
|
32605
32675
|
if (dbgFs) console.error(`[acp-fs] ${method} rejected path (outside cwd or empty)`);
|
|
32606
32676
|
respondJsonRpcError(id, -32602, "Invalid or disallowed path");
|
|
32607
32677
|
return;
|
|
32608
32678
|
}
|
|
32609
32679
|
try {
|
|
32610
|
-
let content = readFileSync3(
|
|
32680
|
+
let content = readFileSync3(resolvedPath, "utf8");
|
|
32611
32681
|
const line2 = typeof params.line === "number" ? params.line : void 0;
|
|
32612
32682
|
const limit = typeof params.limit === "number" ? params.limit : void 0;
|
|
32613
32683
|
content = sliceLinesByRange(content, line2, limit);
|
|
@@ -32631,15 +32701,15 @@ async function createCursorAcpClient(options) {
|
|
|
32631
32701
|
`[acp-fs] ${method} path=${filePath.slice(0, 200)}${filePath.length > 200 ? "\u2026" : ""} newBytes=${newText.length}`
|
|
32632
32702
|
);
|
|
32633
32703
|
}
|
|
32634
|
-
const
|
|
32635
|
-
if (!
|
|
32704
|
+
const resolvedPath = resolveSafePathUnderCwd(cwd, filePath);
|
|
32705
|
+
if (!resolvedPath) {
|
|
32636
32706
|
if (dbgFs) console.error(`[acp-fs] ${method} rejected path (outside cwd or empty): ${filePath.slice(0, 120)}`);
|
|
32637
32707
|
respondJsonRpcError(id, -32602, "Invalid or disallowed path");
|
|
32638
32708
|
return;
|
|
32639
32709
|
}
|
|
32640
32710
|
let oldText = "";
|
|
32641
32711
|
try {
|
|
32642
|
-
oldText = readFileSync3(
|
|
32712
|
+
oldText = readFileSync3(resolvedPath, "utf8");
|
|
32643
32713
|
} catch (e) {
|
|
32644
32714
|
if (e.code !== "ENOENT") {
|
|
32645
32715
|
respondJsonRpcError(id, -32e3, e instanceof Error ? e.message : String(e));
|
|
@@ -32647,13 +32717,13 @@ async function createCursorAcpClient(options) {
|
|
|
32647
32717
|
}
|
|
32648
32718
|
}
|
|
32649
32719
|
try {
|
|
32650
|
-
mkdirSync3(
|
|
32651
|
-
writeFileSync3(
|
|
32720
|
+
mkdirSync3(dirname3(resolvedPath), { recursive: true });
|
|
32721
|
+
writeFileSync3(resolvedPath, newText, "utf8");
|
|
32652
32722
|
} catch (e) {
|
|
32653
32723
|
respondJsonRpcError(id, -32e3, e instanceof Error ? e.message : String(e));
|
|
32654
32724
|
return;
|
|
32655
32725
|
}
|
|
32656
|
-
const displayPath = toDisplayPathRelativeToCwd(cwd,
|
|
32726
|
+
const displayPath = toDisplayPathRelativeToCwd(cwd, resolvedPath);
|
|
32657
32727
|
const patchContent = editSnippetToUnifiedDiff(displayPath, oldText, newText);
|
|
32658
32728
|
onFileChange?.({ path: displayPath, oldText, newText, patchContent });
|
|
32659
32729
|
respond(id, null);
|
|
@@ -32769,8 +32839,7 @@ async function createCursorAcpClient(options) {
|
|
|
32769
32839
|
resolveRequest(requestId, result) {
|
|
32770
32840
|
const numericId = Number(requestId);
|
|
32771
32841
|
if (!Number.isFinite(numericId)) return;
|
|
32772
|
-
|
|
32773
|
-
if (!pendingRequest) return;
|
|
32842
|
+
if (!pendingRequests.get(numericId)) return;
|
|
32774
32843
|
resolveRequest(numericId, result);
|
|
32775
32844
|
},
|
|
32776
32845
|
disconnect() {
|
|
@@ -32896,16 +32965,16 @@ import { existsSync, statSync } from "node:fs";
|
|
|
32896
32965
|
|
|
32897
32966
|
// src/git/get-git-repo-root-sync.ts
|
|
32898
32967
|
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
32899
|
-
import * as
|
|
32968
|
+
import * as path10 from "node:path";
|
|
32900
32969
|
function getGitRepoRootSync(startDir) {
|
|
32901
32970
|
try {
|
|
32902
32971
|
const out = execFileSync2("git", ["rev-parse", "--show-toplevel"], {
|
|
32903
|
-
cwd:
|
|
32972
|
+
cwd: path10.resolve(startDir),
|
|
32904
32973
|
encoding: "utf8",
|
|
32905
32974
|
stdio: ["ignore", "pipe", "ignore"],
|
|
32906
32975
|
maxBuffer: 1024 * 1024
|
|
32907
32976
|
}).trim();
|
|
32908
|
-
return out ?
|
|
32977
|
+
return out ? path10.resolve(out) : null;
|
|
32909
32978
|
} catch {
|
|
32910
32979
|
return null;
|
|
32911
32980
|
}
|
|
@@ -32914,64 +32983,64 @@ function getGitRepoRootSync(startDir) {
|
|
|
32914
32983
|
// src/agents/acp/workspace-files.ts
|
|
32915
32984
|
import { execFileSync as execFileSync3 } from "node:child_process";
|
|
32916
32985
|
import { readFileSync as readFileSync4 } from "node:fs";
|
|
32917
|
-
import * as
|
|
32918
|
-
function resolveWorkspaceFilePath(
|
|
32986
|
+
import * as path11 from "node:path";
|
|
32987
|
+
function resolveWorkspaceFilePath(sessionParentPath, rawPath) {
|
|
32919
32988
|
const trimmed2 = rawPath.trim();
|
|
32920
32989
|
if (!trimmed2) return null;
|
|
32921
|
-
const
|
|
32922
|
-
let
|
|
32923
|
-
if (!
|
|
32924
|
-
const candidate =
|
|
32925
|
-
const gitRoot2 = getGitRepoRootSync(
|
|
32990
|
+
const normalizedSessionParent = path11.resolve(sessionParentPath);
|
|
32991
|
+
let resolvedPath = resolveSafePathUnderCwd(sessionParentPath, trimmed2);
|
|
32992
|
+
if (!resolvedPath) {
|
|
32993
|
+
const candidate = path11.isAbsolute(trimmed2) ? path11.normalize(trimmed2) : path11.normalize(path11.resolve(normalizedSessionParent, trimmed2));
|
|
32994
|
+
const gitRoot2 = getGitRepoRootSync(sessionParentPath);
|
|
32926
32995
|
if (!gitRoot2) return null;
|
|
32927
|
-
const rel =
|
|
32928
|
-
if (rel.startsWith("..") ||
|
|
32929
|
-
|
|
32996
|
+
const rel = path11.relative(gitRoot2, candidate);
|
|
32997
|
+
if (rel.startsWith("..") || path11.isAbsolute(rel)) return null;
|
|
32998
|
+
resolvedPath = candidate;
|
|
32930
32999
|
}
|
|
32931
|
-
const gitRoot = getGitRepoRootSync(
|
|
33000
|
+
const gitRoot = getGitRepoRootSync(sessionParentPath);
|
|
32932
33001
|
if (gitRoot) {
|
|
32933
|
-
const relFromRoot =
|
|
32934
|
-
if (!relFromRoot.startsWith("..") && !
|
|
32935
|
-
return {
|
|
33002
|
+
const relFromRoot = path11.relative(gitRoot, resolvedPath);
|
|
33003
|
+
if (!relFromRoot.startsWith("..") && !path11.isAbsolute(relFromRoot)) {
|
|
33004
|
+
return { resolvedPath, display: relFromRoot.split(path11.sep).join("/") };
|
|
32936
33005
|
}
|
|
32937
33006
|
}
|
|
32938
|
-
return {
|
|
33007
|
+
return { resolvedPath, display: toDisplayPathRelativeToCwd(sessionParentPath, resolvedPath) };
|
|
32939
33008
|
}
|
|
32940
|
-
function readUtf8WorkspaceFile(
|
|
33009
|
+
function readUtf8WorkspaceFile(sessionParentPath, displayPath) {
|
|
32941
33010
|
if (!displayPath || displayPath.includes("..")) return "";
|
|
32942
|
-
const gitRoot = getGitRepoRootSync(
|
|
33011
|
+
const gitRoot = getGitRepoRootSync(sessionParentPath);
|
|
32943
33012
|
if (gitRoot) {
|
|
32944
|
-
const
|
|
32945
|
-
const rel =
|
|
32946
|
-
if (!rel.startsWith("..") && !
|
|
33013
|
+
const resolvedPath2 = path11.resolve(gitRoot, displayPath);
|
|
33014
|
+
const rel = path11.relative(gitRoot, resolvedPath2);
|
|
33015
|
+
if (!rel.startsWith("..") && !path11.isAbsolute(rel)) {
|
|
32947
33016
|
try {
|
|
32948
|
-
return readFileSync4(
|
|
33017
|
+
return readFileSync4(resolvedPath2, "utf8");
|
|
32949
33018
|
} catch {
|
|
32950
33019
|
}
|
|
32951
33020
|
}
|
|
32952
33021
|
}
|
|
32953
|
-
const
|
|
32954
|
-
if (!
|
|
33022
|
+
const resolvedPath = resolveSafePathUnderCwd(sessionParentPath, displayPath);
|
|
33023
|
+
if (!resolvedPath) return "";
|
|
32955
33024
|
try {
|
|
32956
|
-
return readFileSync4(
|
|
33025
|
+
return readFileSync4(resolvedPath, "utf8");
|
|
32957
33026
|
} catch {
|
|
32958
33027
|
return "";
|
|
32959
33028
|
}
|
|
32960
33029
|
}
|
|
32961
|
-
function
|
|
33030
|
+
function tryWorkspaceDisplayToPath(sessionParentPath, displayPath) {
|
|
32962
33031
|
if (!displayPath || displayPath.includes("..")) return null;
|
|
32963
|
-
const gitRoot = getGitRepoRootSync(
|
|
33032
|
+
const gitRoot = getGitRepoRootSync(sessionParentPath);
|
|
32964
33033
|
if (gitRoot) {
|
|
32965
|
-
const
|
|
32966
|
-
const rel =
|
|
32967
|
-
if (!rel.startsWith("..") && !
|
|
33034
|
+
const resolvedPath = path11.resolve(gitRoot, displayPath);
|
|
33035
|
+
const rel = path11.relative(gitRoot, resolvedPath);
|
|
33036
|
+
if (!rel.startsWith("..") && !path11.isAbsolute(rel)) return resolvedPath;
|
|
32968
33037
|
}
|
|
32969
|
-
return resolveSafePathUnderCwd(
|
|
33038
|
+
return resolveSafePathUnderCwd(sessionParentPath, displayPath);
|
|
32970
33039
|
}
|
|
32971
|
-
function readGitHeadBlob(
|
|
33040
|
+
function readGitHeadBlob(sessionParentPath, displayPath) {
|
|
32972
33041
|
if (!displayPath || displayPath.includes("..")) return "";
|
|
32973
|
-
const gitRoot = getGitRepoRootSync(
|
|
32974
|
-
const execCwd = gitRoot ??
|
|
33042
|
+
const gitRoot = getGitRepoRootSync(sessionParentPath);
|
|
33043
|
+
const execCwd = gitRoot ?? sessionParentPath;
|
|
32975
33044
|
try {
|
|
32976
33045
|
return execFileSync3("git", ["show", `HEAD:${displayPath}`], {
|
|
32977
33046
|
cwd: execCwd,
|
|
@@ -32984,9 +33053,9 @@ function readGitHeadBlob(cwd, displayPath) {
|
|
|
32984
33053
|
}
|
|
32985
33054
|
|
|
32986
33055
|
// src/agents/acp/session-file-change-path-kind.ts
|
|
32987
|
-
function gitHeadPathObjectType(
|
|
33056
|
+
function gitHeadPathObjectType(sessionParentPath, displayPath) {
|
|
32988
33057
|
if (!displayPath || displayPath.includes("..")) return null;
|
|
32989
|
-
const gitRoot = getGitRepoRootSync(
|
|
33058
|
+
const gitRoot = getGitRepoRootSync(sessionParentPath);
|
|
32990
33059
|
if (!gitRoot) return null;
|
|
32991
33060
|
try {
|
|
32992
33061
|
return execFileSync4("git", ["cat-file", "-t", `HEAD:${displayPath}`], {
|
|
@@ -32997,11 +33066,11 @@ function gitHeadPathObjectType(cwd, displayPath) {
|
|
|
32997
33066
|
return null;
|
|
32998
33067
|
}
|
|
32999
33068
|
}
|
|
33000
|
-
function getSessionFileChangeDirectoryFlags(
|
|
33001
|
-
const
|
|
33002
|
-
if (
|
|
33069
|
+
function getSessionFileChangeDirectoryFlags(sessionParentPath, displayPath) {
|
|
33070
|
+
const resolvedPath = tryWorkspaceDisplayToPath(sessionParentPath, displayPath);
|
|
33071
|
+
if (resolvedPath && existsSync(resolvedPath)) {
|
|
33003
33072
|
try {
|
|
33004
|
-
if (statSync(
|
|
33073
|
+
if (statSync(resolvedPath).isDirectory()) {
|
|
33005
33074
|
return { isDirectory: true, directoryRemoved: false };
|
|
33006
33075
|
}
|
|
33007
33076
|
return { isDirectory: false, directoryRemoved: false };
|
|
@@ -33009,7 +33078,7 @@ function getSessionFileChangeDirectoryFlags(cwd, displayPath) {
|
|
|
33009
33078
|
return { isDirectory: false, directoryRemoved: false };
|
|
33010
33079
|
}
|
|
33011
33080
|
}
|
|
33012
|
-
if (gitHeadPathObjectType(
|
|
33081
|
+
if (gitHeadPathObjectType(sessionParentPath, displayPath) === "tree") {
|
|
33013
33082
|
return { isDirectory: true, directoryRemoved: true };
|
|
33014
33083
|
}
|
|
33015
33084
|
return { isDirectory: false, directoryRemoved: false };
|
|
@@ -33017,7 +33086,7 @@ function getSessionFileChangeDirectoryFlags(cwd, displayPath) {
|
|
|
33017
33086
|
|
|
33018
33087
|
// src/agents/acp/hooks/bridge-on-file-change.ts
|
|
33019
33088
|
function createBridgeOnFileChange(opts) {
|
|
33020
|
-
const { routing, getSendSessionUpdate, log: log2 } = opts;
|
|
33089
|
+
const { routing, getSendSessionUpdate, log: log2, sessionParentPath } = opts;
|
|
33021
33090
|
return (evt) => {
|
|
33022
33091
|
const runId = routing.runId;
|
|
33023
33092
|
const sessionId = routing.sessionId;
|
|
@@ -33028,8 +33097,7 @@ function createBridgeOnFileChange(opts) {
|
|
|
33028
33097
|
);
|
|
33029
33098
|
return;
|
|
33030
33099
|
}
|
|
33031
|
-
const
|
|
33032
|
-
const dirFlags = getSessionFileChangeDirectoryFlags(cwd, evt.path);
|
|
33100
|
+
const dirFlags = getSessionFileChangeDirectoryFlags(sessionParentPath, evt.path);
|
|
33033
33101
|
try {
|
|
33034
33102
|
send({
|
|
33035
33103
|
type: "session_file_change",
|
|
@@ -33106,12 +33174,12 @@ function extractDiffPath(o) {
|
|
|
33106
33174
|
}
|
|
33107
33175
|
|
|
33108
33176
|
// src/agents/acp/hooks/extract-acp-file-diffs-from-update/push-diff.ts
|
|
33109
|
-
function pushDiffIfComplete(o,
|
|
33177
|
+
function pushDiffIfComplete(o, sessionParentPath, out) {
|
|
33110
33178
|
const t = o.type;
|
|
33111
33179
|
if (typeof t !== "string" || t.toLowerCase() !== "diff") return;
|
|
33112
33180
|
const rawPath = extractDiffPath(o);
|
|
33113
33181
|
if (!rawPath) return;
|
|
33114
|
-
const resolved = resolveWorkspaceFilePath(
|
|
33182
|
+
const resolved = resolveWorkspaceFilePath(sessionParentPath, rawPath);
|
|
33115
33183
|
if (!resolved) return;
|
|
33116
33184
|
const oldText = readOptionalTextField(o.oldText ?? o.old_text ?? o.before ?? o.oldContent);
|
|
33117
33185
|
const newText = readOptionalTextField(o.newText ?? o.new_text ?? o.after ?? o.newContent);
|
|
@@ -33130,17 +33198,17 @@ var NEST_KEYS = [
|
|
|
33130
33198
|
"data",
|
|
33131
33199
|
"arguments"
|
|
33132
33200
|
];
|
|
33133
|
-
function walkValue(value,
|
|
33201
|
+
function walkValue(value, sessionParentPath, depth, out) {
|
|
33134
33202
|
if (depth > 12 || value == null) return;
|
|
33135
33203
|
if (Array.isArray(value)) {
|
|
33136
|
-
for (const x of value) walkValue(x,
|
|
33204
|
+
for (const x of value) walkValue(x, sessionParentPath, depth + 1, out);
|
|
33137
33205
|
return;
|
|
33138
33206
|
}
|
|
33139
33207
|
if (typeof value !== "object") return;
|
|
33140
33208
|
const o = value;
|
|
33141
|
-
pushDiffIfComplete(o,
|
|
33209
|
+
pushDiffIfComplete(o, sessionParentPath, out);
|
|
33142
33210
|
if (o.type === "content" && o.content != null && typeof o.content === "object") {
|
|
33143
|
-
walkValue(o.content,
|
|
33211
|
+
walkValue(o.content, sessionParentPath, depth + 1, out);
|
|
33144
33212
|
}
|
|
33145
33213
|
for (const k of NEST_KEYS) {
|
|
33146
33214
|
if (o.type === "content" && k === "content") continue;
|
|
@@ -33148,23 +33216,23 @@ function walkValue(value, cwd, depth, out) {
|
|
|
33148
33216
|
if (v == null) continue;
|
|
33149
33217
|
if (k === "arguments" && typeof v === "string") {
|
|
33150
33218
|
try {
|
|
33151
|
-
walkValue(JSON.parse(v),
|
|
33219
|
+
walkValue(JSON.parse(v), sessionParentPath, depth + 1, out);
|
|
33152
33220
|
} catch {
|
|
33153
33221
|
}
|
|
33154
33222
|
continue;
|
|
33155
33223
|
}
|
|
33156
|
-
walkValue(v,
|
|
33224
|
+
walkValue(v, sessionParentPath, depth + 1, out);
|
|
33157
33225
|
}
|
|
33158
33226
|
}
|
|
33159
33227
|
|
|
33160
33228
|
// src/agents/acp/hooks/extract-acp-file-diffs-from-update/extract.ts
|
|
33161
|
-
function extractAcpFileDiffsFromUpdate(update,
|
|
33229
|
+
function extractAcpFileDiffsFromUpdate(update, sessionParentPath) {
|
|
33162
33230
|
if (!update || typeof update !== "object") return [];
|
|
33163
33231
|
const u = update;
|
|
33164
33232
|
const out = [];
|
|
33165
33233
|
const content = u.content;
|
|
33166
33234
|
if (Array.isArray(content)) {
|
|
33167
|
-
for (const x of content) walkValue(x,
|
|
33235
|
+
for (const x of content) walkValue(x, sessionParentPath, 0, out);
|
|
33168
33236
|
}
|
|
33169
33237
|
const byPath = /* @__PURE__ */ new Map();
|
|
33170
33238
|
for (const d of out) byPath.set(d.path, d);
|
|
@@ -33172,18 +33240,18 @@ function extractAcpFileDiffsFromUpdate(update, cwd) {
|
|
|
33172
33240
|
}
|
|
33173
33241
|
|
|
33174
33242
|
// src/agents/acp/hooks/extract-tool-target-paths.ts
|
|
33175
|
-
function addPath(
|
|
33243
|
+
function addPath(sessionParentPath, raw, out) {
|
|
33176
33244
|
if (typeof raw !== "string") return;
|
|
33177
33245
|
const trimmed2 = raw.trim();
|
|
33178
33246
|
if (!trimmed2) return;
|
|
33179
|
-
const resolved = resolveWorkspaceFilePath(
|
|
33247
|
+
const resolved = resolveWorkspaceFilePath(sessionParentPath, trimmed2);
|
|
33180
33248
|
if (!resolved) return;
|
|
33181
33249
|
out.add(resolved.display);
|
|
33182
33250
|
}
|
|
33183
|
-
function walkLocations(
|
|
33251
|
+
function walkLocations(sessionParentPath, loc, out) {
|
|
33184
33252
|
if (!Array.isArray(loc)) return;
|
|
33185
33253
|
for (const item of loc) {
|
|
33186
|
-
if (item && typeof item === "object") addPath(
|
|
33254
|
+
if (item && typeof item === "object") addPath(sessionParentPath, item.path, out);
|
|
33187
33255
|
}
|
|
33188
33256
|
}
|
|
33189
33257
|
var PATH_KEYS = [
|
|
@@ -33196,56 +33264,56 @@ var PATH_KEYS = [
|
|
|
33196
33264
|
"file_path",
|
|
33197
33265
|
"target_file"
|
|
33198
33266
|
];
|
|
33199
|
-
function collectFromObject(
|
|
33267
|
+
function collectFromObject(sessionParentPath, obj, out, depth) {
|
|
33200
33268
|
if (depth > 10) return;
|
|
33201
33269
|
for (const k of PATH_KEYS) {
|
|
33202
|
-
if (k in obj) addPath(
|
|
33270
|
+
if (k in obj) addPath(sessionParentPath, obj[k], out);
|
|
33203
33271
|
}
|
|
33204
33272
|
for (const v of Object.values(obj)) {
|
|
33205
|
-
if (v != null && typeof v === "object") collectUnknown(
|
|
33273
|
+
if (v != null && typeof v === "object") collectUnknown(sessionParentPath, v, out, depth + 1);
|
|
33206
33274
|
}
|
|
33207
33275
|
}
|
|
33208
|
-
function collectUnknown(
|
|
33276
|
+
function collectUnknown(sessionParentPath, v, out, depth) {
|
|
33209
33277
|
if (depth > 10 || v == null) return;
|
|
33210
33278
|
if (Array.isArray(v)) {
|
|
33211
|
-
for (const x of v) collectUnknown(
|
|
33279
|
+
for (const x of v) collectUnknown(sessionParentPath, x, out, depth + 1);
|
|
33212
33280
|
return;
|
|
33213
33281
|
}
|
|
33214
|
-
if (typeof v === "object") collectFromObject(
|
|
33282
|
+
if (typeof v === "object") collectFromObject(sessionParentPath, v, out, depth);
|
|
33215
33283
|
}
|
|
33216
|
-
function walkContentArray(
|
|
33284
|
+
function walkContentArray(sessionParentPath, content, out) {
|
|
33217
33285
|
if (!Array.isArray(content)) return;
|
|
33218
33286
|
for (const item of content) {
|
|
33219
33287
|
if (!item || typeof item !== "object") continue;
|
|
33220
33288
|
const o = item;
|
|
33221
33289
|
if (typeof o.type === "string" && o.type.toLowerCase() === "diff") {
|
|
33222
|
-
for (const k of PATH_KEYS) if (k in o) addPath(
|
|
33290
|
+
for (const k of PATH_KEYS) if (k in o) addPath(sessionParentPath, o[k], out);
|
|
33223
33291
|
}
|
|
33224
33292
|
if (o.type === "content" && o.content != null && typeof o.content === "object") {
|
|
33225
33293
|
const inner = o.content;
|
|
33226
33294
|
if (typeof inner.type === "string" && inner.type.toLowerCase() === "diff") {
|
|
33227
|
-
for (const k of PATH_KEYS) if (k in inner) addPath(
|
|
33295
|
+
for (const k of PATH_KEYS) if (k in inner) addPath(sessionParentPath, inner[k], out);
|
|
33228
33296
|
}
|
|
33229
33297
|
}
|
|
33230
33298
|
}
|
|
33231
33299
|
}
|
|
33232
|
-
function extractToolTargetDisplayPaths(update,
|
|
33300
|
+
function extractToolTargetDisplayPaths(update, sessionParentPath) {
|
|
33233
33301
|
const out = /* @__PURE__ */ new Set();
|
|
33234
33302
|
if (!update || typeof update !== "object") return [];
|
|
33235
33303
|
const u = update;
|
|
33236
|
-
walkLocations(
|
|
33237
|
-
walkLocations(
|
|
33238
|
-
walkLocations(
|
|
33304
|
+
walkLocations(sessionParentPath, u.locations, out);
|
|
33305
|
+
walkLocations(sessionParentPath, u.fileLocations, out);
|
|
33306
|
+
walkLocations(sessionParentPath, u.file_locations, out);
|
|
33239
33307
|
const tc = u.toolCall ?? u.tool_call;
|
|
33240
33308
|
if (tc && typeof tc.arguments === "string") {
|
|
33241
33309
|
try {
|
|
33242
33310
|
const parsed = JSON.parse(tc.arguments);
|
|
33243
|
-
collectUnknown(
|
|
33311
|
+
collectUnknown(sessionParentPath, parsed, out, 0);
|
|
33244
33312
|
} catch {
|
|
33245
33313
|
}
|
|
33246
33314
|
}
|
|
33247
|
-
walkContentArray(
|
|
33248
|
-
collectFromObject(
|
|
33315
|
+
walkContentArray(sessionParentPath, u.content, out);
|
|
33316
|
+
collectFromObject(sessionParentPath, u, out, 0);
|
|
33249
33317
|
return [...out];
|
|
33250
33318
|
}
|
|
33251
33319
|
|
|
@@ -33311,7 +33379,7 @@ var PathSnapshotTracker = class {
|
|
|
33311
33379
|
this.beforeByToolKey.delete(toolKey);
|
|
33312
33380
|
this.accumulatedPathsByToolKey.delete(toolKey);
|
|
33313
33381
|
}
|
|
33314
|
-
captureBeforeFromDisk(toolKey, paths,
|
|
33382
|
+
captureBeforeFromDisk(toolKey, paths, sessionParentPath) {
|
|
33315
33383
|
if (paths.length === 0) return;
|
|
33316
33384
|
let m = this.beforeByToolKey.get(toolKey);
|
|
33317
33385
|
if (!m) {
|
|
@@ -33320,10 +33388,10 @@ var PathSnapshotTracker = class {
|
|
|
33320
33388
|
}
|
|
33321
33389
|
for (const p of paths) {
|
|
33322
33390
|
if (m.has(p)) continue;
|
|
33323
|
-
m.set(p, readUtf8WorkspaceFile(
|
|
33391
|
+
m.set(p, readUtf8WorkspaceFile(sessionParentPath, p));
|
|
33324
33392
|
}
|
|
33325
33393
|
}
|
|
33326
|
-
ensureBeforeFromHeadForMissing(toolKey, paths,
|
|
33394
|
+
ensureBeforeFromHeadForMissing(toolKey, paths, sessionParentPath) {
|
|
33327
33395
|
let m = this.beforeByToolKey.get(toolKey);
|
|
33328
33396
|
if (!m) {
|
|
33329
33397
|
m = /* @__PURE__ */ new Map();
|
|
@@ -33331,10 +33399,10 @@ var PathSnapshotTracker = class {
|
|
|
33331
33399
|
}
|
|
33332
33400
|
for (const p of paths) {
|
|
33333
33401
|
if (m.has(p)) continue;
|
|
33334
|
-
m.set(p, readGitHeadBlob(
|
|
33402
|
+
m.set(p, readGitHeadBlob(sessionParentPath, p));
|
|
33335
33403
|
}
|
|
33336
33404
|
}
|
|
33337
|
-
flushPathSnapshots(toolKey,
|
|
33405
|
+
flushPathSnapshots(toolKey, sessionParentPath, sentPaths, send, runId, sessionId, log2) {
|
|
33338
33406
|
const t = this.debouncers.get(toolKey);
|
|
33339
33407
|
if (t) clearTimeout(t);
|
|
33340
33408
|
this.debouncers.delete(toolKey);
|
|
@@ -33343,10 +33411,10 @@ var PathSnapshotTracker = class {
|
|
|
33343
33411
|
this.beforeByToolKey.delete(toolKey);
|
|
33344
33412
|
if (!send || !runId || !sessionId) return;
|
|
33345
33413
|
for (const [displayPath, oldText] of beforeMap) {
|
|
33346
|
-
const newText = readUtf8WorkspaceFile(
|
|
33414
|
+
const newText = readUtf8WorkspaceFile(sessionParentPath, displayPath);
|
|
33347
33415
|
if (oldText === newText) continue;
|
|
33348
33416
|
const patchContent = editSnippetToUnifiedDiff(displayPath, oldText, newText);
|
|
33349
|
-
const dirFlags = getSessionFileChangeDirectoryFlags(
|
|
33417
|
+
const dirFlags = getSessionFileChangeDirectoryFlags(sessionParentPath, displayPath);
|
|
33350
33418
|
try {
|
|
33351
33419
|
send({
|
|
33352
33420
|
type: "session_file_change",
|
|
@@ -33365,31 +33433,31 @@ var PathSnapshotTracker = class {
|
|
|
33365
33433
|
}
|
|
33366
33434
|
}
|
|
33367
33435
|
}
|
|
33368
|
-
scheduleDebouncedFlush(toolKey,
|
|
33436
|
+
scheduleDebouncedFlush(toolKey, sessionParentPath, sentPaths, send, runId, sessionId, log2) {
|
|
33369
33437
|
const prev = this.debouncers.get(toolKey);
|
|
33370
33438
|
if (prev) clearTimeout(prev);
|
|
33371
33439
|
this.debouncers.set(
|
|
33372
33440
|
toolKey,
|
|
33373
33441
|
setTimeout(() => {
|
|
33374
33442
|
this.debouncers.delete(toolKey);
|
|
33375
|
-
this.flushPathSnapshots(toolKey,
|
|
33443
|
+
this.flushPathSnapshots(toolKey, sessionParentPath, sentPaths, send, runId, sessionId, log2);
|
|
33376
33444
|
}, PATH_SNAPSHOT_DEBOUNCE_MS)
|
|
33377
33445
|
);
|
|
33378
33446
|
}
|
|
33379
|
-
handleToolCallLifecycle(updateKind, toolKey, toolPaths, status,
|
|
33447
|
+
handleToolCallLifecycle(updateKind, toolKey, toolPaths, status, sessionParentPath, sentPaths, send, runId, sessionId, log2) {
|
|
33380
33448
|
if (updateKind === "tool_call") {
|
|
33381
33449
|
this.resetToolSnapshots(toolKey);
|
|
33382
33450
|
}
|
|
33383
33451
|
if (updateKind === "tool_call") {
|
|
33384
|
-
this.captureBeforeFromDisk(toolKey, toolPaths,
|
|
33452
|
+
this.captureBeforeFromDisk(toolKey, toolPaths, sessionParentPath);
|
|
33385
33453
|
} else if (updateKind === "tool_call_update") {
|
|
33386
33454
|
if (isCompletedToolStatus(status)) {
|
|
33387
|
-
this.ensureBeforeFromHeadForMissing(toolKey, toolPaths,
|
|
33388
|
-
this.flushPathSnapshots(toolKey,
|
|
33455
|
+
this.ensureBeforeFromHeadForMissing(toolKey, toolPaths, sessionParentPath);
|
|
33456
|
+
this.flushPathSnapshots(toolKey, sessionParentPath, sentPaths, send, runId, sessionId, log2);
|
|
33389
33457
|
} else {
|
|
33390
|
-
this.captureBeforeFromDisk(toolKey, toolPaths,
|
|
33458
|
+
this.captureBeforeFromDisk(toolKey, toolPaths, sessionParentPath);
|
|
33391
33459
|
if (this.beforeByToolKey.has(toolKey)) {
|
|
33392
|
-
this.scheduleDebouncedFlush(toolKey,
|
|
33460
|
+
this.scheduleDebouncedFlush(toolKey, sessionParentPath, sentPaths, send, runId, sessionId, log2);
|
|
33393
33461
|
}
|
|
33394
33462
|
}
|
|
33395
33463
|
}
|
|
@@ -33397,11 +33465,11 @@ var PathSnapshotTracker = class {
|
|
|
33397
33465
|
};
|
|
33398
33466
|
|
|
33399
33467
|
// src/agents/acp/hooks/bridge-on-session-update/send-structured-file-changes.ts
|
|
33400
|
-
function sendExtractedDiffsAsSessionFileChanges(diffs, send,
|
|
33468
|
+
function sendExtractedDiffsAsSessionFileChanges(diffs, send, sessionParentPath, sessionId, runId, sentPaths, log2) {
|
|
33401
33469
|
for (const d of diffs) {
|
|
33402
33470
|
try {
|
|
33403
33471
|
const patchContent = editSnippetToUnifiedDiff(d.path, d.oldText, d.newText);
|
|
33404
|
-
const dirFlags = getSessionFileChangeDirectoryFlags(
|
|
33472
|
+
const dirFlags = getSessionFileChangeDirectoryFlags(sessionParentPath, d.path);
|
|
33405
33473
|
send({
|
|
33406
33474
|
type: "session_file_change",
|
|
33407
33475
|
sessionId,
|
|
@@ -33419,15 +33487,15 @@ function sendExtractedDiffsAsSessionFileChanges(diffs, send, cwd, sessionId, run
|
|
|
33419
33487
|
}
|
|
33420
33488
|
}
|
|
33421
33489
|
}
|
|
33422
|
-
function sendGitHeadVsWorkspaceForToolPaths(mergedPaths, sentPaths, send,
|
|
33490
|
+
function sendGitHeadVsWorkspaceForToolPaths(mergedPaths, sentPaths, send, sessionParentPath, sessionId, runId, log2) {
|
|
33423
33491
|
for (const displayPath of mergedPaths) {
|
|
33424
33492
|
if (sentPaths.has(displayPath)) continue;
|
|
33425
|
-
const oldText = readGitHeadBlob(
|
|
33426
|
-
const newText = readUtf8WorkspaceFile(
|
|
33493
|
+
const oldText = readGitHeadBlob(sessionParentPath, displayPath);
|
|
33494
|
+
const newText = readUtf8WorkspaceFile(sessionParentPath, displayPath);
|
|
33427
33495
|
if (oldText === newText) continue;
|
|
33428
33496
|
try {
|
|
33429
33497
|
const patchContent = editSnippetToUnifiedDiff(displayPath, oldText, newText);
|
|
33430
|
-
const dirFlags = getSessionFileChangeDirectoryFlags(
|
|
33498
|
+
const dirFlags = getSessionFileChangeDirectoryFlags(sessionParentPath, displayPath);
|
|
33431
33499
|
send({
|
|
33432
33500
|
type: "session_file_change",
|
|
33433
33501
|
sessionId,
|
|
@@ -33448,13 +33516,12 @@ function sendGitHeadVsWorkspaceForToolPaths(mergedPaths, sentPaths, send, cwd, s
|
|
|
33448
33516
|
|
|
33449
33517
|
// src/agents/acp/hooks/bridge-on-session-update/create-bridge-on-session-update.ts
|
|
33450
33518
|
function createBridgeOnSessionUpdate(opts) {
|
|
33451
|
-
const { routing, getSendSessionUpdate, log: log2 } = opts;
|
|
33519
|
+
const { routing, getSendSessionUpdate, log: log2, sessionParentPath } = opts;
|
|
33452
33520
|
const pathTracker = new PathSnapshotTracker();
|
|
33453
33521
|
return (params) => {
|
|
33454
33522
|
const runId = routing.runId;
|
|
33455
33523
|
const sessionId = routing.sessionId;
|
|
33456
33524
|
pathTracker.onRunIdChanged(runId);
|
|
33457
|
-
const cwd = getBridgeWorkspaceDirectory();
|
|
33458
33525
|
const send = getSendSessionUpdate();
|
|
33459
33526
|
const sentFileChangePaths = /* @__PURE__ */ new Set();
|
|
33460
33527
|
const p = params;
|
|
@@ -33462,7 +33529,7 @@ function createBridgeOnSessionUpdate(opts) {
|
|
|
33462
33529
|
const isCompletedToolCallUpdate = updateKind === "tool_call_update" && isCompletedToolStatus(p.status);
|
|
33463
33530
|
const toolName = p.toolCall?.name ?? p.tool_call?.name ?? "";
|
|
33464
33531
|
const isToolUpdate = updateKind === "tool_call" || updateKind === "tool_call_update" || typeof toolName === "string" && toolName.length > 0;
|
|
33465
|
-
const toolPaths = isToolUpdate ? extractToolTargetDisplayPaths(params,
|
|
33532
|
+
const toolPaths = isToolUpdate ? extractToolTargetDisplayPaths(params, sessionParentPath) : [];
|
|
33466
33533
|
const toolKey = isToolUpdate ? pathTracker.resolveToolKey(params, updateKind) : "";
|
|
33467
33534
|
if (updateKind === "tool_call") {
|
|
33468
33535
|
pathTracker.resetToolSnapshots(toolKey);
|
|
@@ -33477,7 +33544,7 @@ function createBridgeOnSessionUpdate(opts) {
|
|
|
33477
33544
|
toolKey,
|
|
33478
33545
|
toolPaths,
|
|
33479
33546
|
p.status,
|
|
33480
|
-
|
|
33547
|
+
sessionParentPath,
|
|
33481
33548
|
sentFileChangePaths,
|
|
33482
33549
|
deliver,
|
|
33483
33550
|
runId ?? "",
|
|
@@ -33485,9 +33552,17 @@ function createBridgeOnSessionUpdate(opts) {
|
|
|
33485
33552
|
log2
|
|
33486
33553
|
);
|
|
33487
33554
|
}
|
|
33488
|
-
const diffs = extractAcpFileDiffsFromUpdate(params,
|
|
33555
|
+
const diffs = extractAcpFileDiffsFromUpdate(params, sessionParentPath);
|
|
33489
33556
|
if (diffs.length > 0 && send && runId && sessionId) {
|
|
33490
|
-
sendExtractedDiffsAsSessionFileChanges(
|
|
33557
|
+
sendExtractedDiffsAsSessionFileChanges(
|
|
33558
|
+
diffs,
|
|
33559
|
+
send,
|
|
33560
|
+
sessionParentPath,
|
|
33561
|
+
sessionId,
|
|
33562
|
+
runId,
|
|
33563
|
+
sentFileChangePaths,
|
|
33564
|
+
log2
|
|
33565
|
+
);
|
|
33491
33566
|
} else if (diffs.length > 0) {
|
|
33492
33567
|
log2(
|
|
33493
33568
|
`[Bridge service] Agent file diff(s) not forwarded (${diffs.length}): session or run not wired to the bridge.`
|
|
@@ -33496,7 +33571,15 @@ function createBridgeOnSessionUpdate(opts) {
|
|
|
33496
33571
|
if (isCompletedToolCallUpdate && send && runId && sessionId) {
|
|
33497
33572
|
const acc = pathTracker.accumulatedPathsByToolKey.get(toolKey);
|
|
33498
33573
|
const merged = [.../* @__PURE__ */ new Set([...acc ? [...acc] : [], ...toolPaths])];
|
|
33499
|
-
sendGitHeadVsWorkspaceForToolPaths(
|
|
33574
|
+
sendGitHeadVsWorkspaceForToolPaths(
|
|
33575
|
+
merged,
|
|
33576
|
+
sentFileChangePaths,
|
|
33577
|
+
send,
|
|
33578
|
+
sessionParentPath,
|
|
33579
|
+
sessionId,
|
|
33580
|
+
runId,
|
|
33581
|
+
log2
|
|
33582
|
+
);
|
|
33500
33583
|
pathTracker.accumulatedPathsByToolKey.delete(toolKey);
|
|
33501
33584
|
}
|
|
33502
33585
|
if (runId && send) {
|
|
@@ -33531,11 +33614,12 @@ function buildAcpSessionBridgeHooks(opts) {
|
|
|
33531
33614
|
|
|
33532
33615
|
// src/agents/acp/ensure-acp-client.ts
|
|
33533
33616
|
async function ensureAcpClient(options) {
|
|
33534
|
-
const { state, preferredAgentType, mode,
|
|
33535
|
-
const
|
|
33536
|
-
|
|
33537
|
-
|
|
33538
|
-
|
|
33617
|
+
const { state, preferredAgentType, mode, sessionParentPath, routing, sendSessionUpdate, sendRequest, log: log2 } = options;
|
|
33618
|
+
const targetSessionParentPath = resolveSessionParentPathForAgentProcess(sessionParentPath);
|
|
33619
|
+
if (state.acpStartPromise && !state.acpHandle) {
|
|
33620
|
+
await state.acpStartPromise;
|
|
33621
|
+
}
|
|
33622
|
+
if (state.acpHandle && state.lastAcpCwd != null && path12.resolve(state.lastAcpCwd) !== path12.resolve(targetSessionParentPath)) {
|
|
33539
33623
|
try {
|
|
33540
33624
|
state.acpHandle.disconnect();
|
|
33541
33625
|
} catch {
|
|
@@ -33567,14 +33651,14 @@ async function ensureAcpClient(options) {
|
|
|
33567
33651
|
if (!state.acpStartPromise) {
|
|
33568
33652
|
let statOk = false;
|
|
33569
33653
|
try {
|
|
33570
|
-
const st = fs10.statSync(
|
|
33654
|
+
const st = fs10.statSync(targetSessionParentPath);
|
|
33571
33655
|
statOk = st.isDirectory();
|
|
33572
33656
|
if (!statOk) {
|
|
33573
|
-
state.lastAcpStartError = `Agent cwd is not a directory: ${
|
|
33657
|
+
state.lastAcpStartError = `Agent cwd is not a directory: ${targetSessionParentPath}`;
|
|
33574
33658
|
log2(`[Agent] ${state.lastAcpStartError}`);
|
|
33575
33659
|
}
|
|
33576
33660
|
} catch {
|
|
33577
|
-
state.lastAcpStartError = `Agent cwd missing or inaccessible: ${
|
|
33661
|
+
state.lastAcpStartError = `Agent cwd missing or inaccessible: ${targetSessionParentPath}`;
|
|
33578
33662
|
log2(`[Agent] ${state.lastAcpStartError}`);
|
|
33579
33663
|
}
|
|
33580
33664
|
if (!statOk) {
|
|
@@ -33582,6 +33666,7 @@ async function ensureAcpClient(options) {
|
|
|
33582
33666
|
}
|
|
33583
33667
|
const hooks = buildAcpSessionBridgeHooks({
|
|
33584
33668
|
routing,
|
|
33669
|
+
sessionParentPath: targetSessionParentPath,
|
|
33585
33670
|
getSendSessionUpdate: () => sendSessionUpdate,
|
|
33586
33671
|
getSendRequest: () => sendRequest,
|
|
33587
33672
|
log: log2
|
|
@@ -33589,7 +33674,6 @@ async function ensureAcpClient(options) {
|
|
|
33589
33674
|
state.acpStartPromise = resolved.createClient({
|
|
33590
33675
|
command: resolved.command,
|
|
33591
33676
|
sessionMode: mode,
|
|
33592
|
-
cwd: targetCwd,
|
|
33593
33677
|
backendAgentType: preferredAgentType,
|
|
33594
33678
|
onAgentSubprocessExit: () => {
|
|
33595
33679
|
state.acpHandle = null;
|
|
@@ -33597,11 +33681,12 @@ async function ensureAcpClient(options) {
|
|
|
33597
33681
|
state.acpAgentKey = null;
|
|
33598
33682
|
state.lastAcpStartError = "Agent subprocess exited";
|
|
33599
33683
|
},
|
|
33600
|
-
...hooks
|
|
33684
|
+
...hooks,
|
|
33685
|
+
cwd: targetSessionParentPath
|
|
33601
33686
|
}).then((h) => {
|
|
33602
33687
|
state.lastAcpStartError = null;
|
|
33603
33688
|
state.acpHandle = h;
|
|
33604
|
-
state.lastAcpCwd =
|
|
33689
|
+
state.lastAcpCwd = targetSessionParentPath;
|
|
33605
33690
|
state.acpAgentKey = agentKey;
|
|
33606
33691
|
return h;
|
|
33607
33692
|
}).catch((err) => {
|
|
@@ -33650,7 +33735,7 @@ async function createAcpManager(options) {
|
|
|
33650
33735
|
runId,
|
|
33651
33736
|
mode,
|
|
33652
33737
|
agentType,
|
|
33653
|
-
|
|
33738
|
+
sessionParentPath,
|
|
33654
33739
|
sendResult: sendResult2,
|
|
33655
33740
|
sendSessionUpdate,
|
|
33656
33741
|
followUpCatalogPromptId,
|
|
@@ -33669,7 +33754,7 @@ async function createAcpManager(options) {
|
|
|
33669
33754
|
state,
|
|
33670
33755
|
preferredAgentType: preferredForPrompt,
|
|
33671
33756
|
mode,
|
|
33672
|
-
|
|
33757
|
+
sessionParentPath,
|
|
33673
33758
|
routing: promptRouting,
|
|
33674
33759
|
sendSessionUpdate,
|
|
33675
33760
|
sendRequest: sendSessionUpdate,
|
|
@@ -33718,7 +33803,7 @@ async function createAcpManager(options) {
|
|
|
33718
33803
|
sessionId,
|
|
33719
33804
|
runId,
|
|
33720
33805
|
agentType: preferredForPrompt,
|
|
33721
|
-
agentCwd:
|
|
33806
|
+
agentCwd: resolveSessionParentPathForAgentProcess(sessionParentPath),
|
|
33722
33807
|
sendResult: sendResult2,
|
|
33723
33808
|
sendSessionUpdate,
|
|
33724
33809
|
log: log2,
|
|
@@ -33773,12 +33858,12 @@ async function createAcpManager(options) {
|
|
|
33773
33858
|
}
|
|
33774
33859
|
|
|
33775
33860
|
// src/worktrees/session-worktree-manager.ts
|
|
33776
|
-
import * as
|
|
33861
|
+
import * as path19 from "node:path";
|
|
33777
33862
|
import os4 from "node:os";
|
|
33778
33863
|
|
|
33779
33864
|
// src/worktrees/prepare-new-session-worktrees.ts
|
|
33780
33865
|
import * as fs12 from "node:fs";
|
|
33781
|
-
import * as
|
|
33866
|
+
import * as path14 from "node:path";
|
|
33782
33867
|
|
|
33783
33868
|
// src/git/worktree-add.ts
|
|
33784
33869
|
async function gitWorktreeAddBranch(mainRepoPath, worktreePath, branch) {
|
|
@@ -33788,11 +33873,11 @@ async function gitWorktreeAddBranch(mainRepoPath, worktreePath, branch) {
|
|
|
33788
33873
|
|
|
33789
33874
|
// src/worktrees/worktree-layout-file.ts
|
|
33790
33875
|
import * as fs11 from "node:fs";
|
|
33791
|
-
import * as
|
|
33876
|
+
import * as path13 from "node:path";
|
|
33792
33877
|
import os3 from "node:os";
|
|
33793
33878
|
var LAYOUT_FILENAME = "worktree-launcher-layout.json";
|
|
33794
33879
|
function defaultWorktreeLayoutPath() {
|
|
33795
|
-
return
|
|
33880
|
+
return path13.join(os3.homedir(), ".buildautomaton", LAYOUT_FILENAME);
|
|
33796
33881
|
}
|
|
33797
33882
|
function normalizeLoadedLayout(raw) {
|
|
33798
33883
|
if (raw && typeof raw === "object" && "launcherCwds" in raw) {
|
|
@@ -33813,24 +33898,24 @@ function loadWorktreeLayout() {
|
|
|
33813
33898
|
}
|
|
33814
33899
|
function saveWorktreeLayout(layout) {
|
|
33815
33900
|
try {
|
|
33816
|
-
const dir =
|
|
33901
|
+
const dir = path13.dirname(defaultWorktreeLayoutPath());
|
|
33817
33902
|
fs11.mkdirSync(dir, { recursive: true });
|
|
33818
33903
|
fs11.writeFileSync(defaultWorktreeLayoutPath(), JSON.stringify(layout, null, 2), "utf8");
|
|
33819
33904
|
} catch {
|
|
33820
33905
|
}
|
|
33821
33906
|
}
|
|
33822
|
-
function baseNameSafe(
|
|
33823
|
-
return
|
|
33907
|
+
function baseNameSafe(pathString) {
|
|
33908
|
+
return path13.basename(pathString).replace(/[^a-zA-Z0-9._-]+/g, "-") || "cwd";
|
|
33824
33909
|
}
|
|
33825
|
-
function getLauncherDirNameIfPresent(layout,
|
|
33826
|
-
const norm =
|
|
33827
|
-
const existing = layout.launcherCwds.find((e) =>
|
|
33910
|
+
function getLauncherDirNameIfPresent(layout, bridgeRootPath2) {
|
|
33911
|
+
const norm = path13.resolve(bridgeRootPath2);
|
|
33912
|
+
const existing = layout.launcherCwds.find((e) => path13.resolve(e.absolutePath) === norm);
|
|
33828
33913
|
return existing?.dirName;
|
|
33829
33914
|
}
|
|
33830
|
-
function allocateDirNameForLauncherCwd(layout,
|
|
33831
|
-
const existing = getLauncherDirNameIfPresent(layout,
|
|
33915
|
+
function allocateDirNameForLauncherCwd(layout, bridgeRootPath2) {
|
|
33916
|
+
const existing = getLauncherDirNameIfPresent(layout, bridgeRootPath2);
|
|
33832
33917
|
if (existing) return existing;
|
|
33833
|
-
const norm =
|
|
33918
|
+
const norm = path13.resolve(bridgeRootPath2);
|
|
33834
33919
|
const base = baseNameSafe(norm);
|
|
33835
33920
|
const used = new Set(layout.launcherCwds.map((e) => e.dirName));
|
|
33836
33921
|
let name = base;
|
|
@@ -33846,24 +33931,27 @@ function allocateDirNameForLauncherCwd(layout, launcherCwdAbs) {
|
|
|
33846
33931
|
|
|
33847
33932
|
// src/worktrees/prepare-new-session-worktrees.ts
|
|
33848
33933
|
async function prepareNewSessionWorktrees(options) {
|
|
33849
|
-
const {
|
|
33850
|
-
const
|
|
33851
|
-
const cwdKey = allocateDirNameForLauncherCwd(layout,
|
|
33852
|
-
const
|
|
33853
|
-
const
|
|
33934
|
+
const { worktreesRootPath, bridgeRoot, sessionId, layout, log: log2 } = options;
|
|
33935
|
+
const bridgeResolved = path14.resolve(bridgeRoot);
|
|
33936
|
+
const cwdKey = allocateDirNameForLauncherCwd(layout, bridgeResolved);
|
|
33937
|
+
const bridgeKeyDir = path14.join(worktreesRootPath, cwdKey);
|
|
33938
|
+
const sessionDir = path14.join(bridgeKeyDir, sessionId);
|
|
33939
|
+
const repos = await discoverGitReposUnderRoot(bridgeResolved);
|
|
33854
33940
|
if (repos.length === 0) {
|
|
33855
|
-
log2("[worktrees] No Git repositories under
|
|
33941
|
+
log2("[worktrees] No Git repositories under bridge root; skipping worktree creation.");
|
|
33856
33942
|
return null;
|
|
33857
33943
|
}
|
|
33858
33944
|
const branch = `session-${sessionId}`;
|
|
33859
33945
|
const worktreePaths = [];
|
|
33860
|
-
fs12.mkdirSync(
|
|
33946
|
+
fs12.mkdirSync(sessionDir, { recursive: true });
|
|
33861
33947
|
for (const repo of repos) {
|
|
33862
|
-
let rel =
|
|
33863
|
-
if (rel.startsWith("..") ||
|
|
33948
|
+
let rel = path14.relative(bridgeResolved, repo.absolutePath);
|
|
33949
|
+
if (rel.startsWith("..") || path14.isAbsolute(rel)) continue;
|
|
33864
33950
|
const relNorm = rel === "" ? "." : rel;
|
|
33865
|
-
const wtPath =
|
|
33866
|
-
|
|
33951
|
+
const wtPath = relNorm === "." ? sessionDir : path14.join(sessionDir, relNorm);
|
|
33952
|
+
if (relNorm !== ".") {
|
|
33953
|
+
fs12.mkdirSync(path14.dirname(wtPath), { recursive: true });
|
|
33954
|
+
}
|
|
33867
33955
|
try {
|
|
33868
33956
|
await gitWorktreeAddBranch(repo.absolutePath, wtPath, branch);
|
|
33869
33957
|
log2(`[worktrees] Added worktree ${wtPath} (branch ${branch}).`);
|
|
@@ -33875,7 +33963,11 @@ async function prepareNewSessionWorktrees(options) {
|
|
|
33875
33963
|
}
|
|
33876
33964
|
}
|
|
33877
33965
|
if (worktreePaths.length === 0) return null;
|
|
33878
|
-
return {
|
|
33966
|
+
return {
|
|
33967
|
+
worktreePaths,
|
|
33968
|
+
sessionParentPath: sessionDir,
|
|
33969
|
+
workingTreeRelRoot: sessionDir
|
|
33970
|
+
};
|
|
33879
33971
|
}
|
|
33880
33972
|
|
|
33881
33973
|
// src/git/rename-branch.ts
|
|
@@ -33907,16 +33999,16 @@ import * as fs14 from "node:fs";
|
|
|
33907
33999
|
|
|
33908
34000
|
// src/git/resolve-main-repo-from-git-file.ts
|
|
33909
34001
|
import * as fs13 from "node:fs";
|
|
33910
|
-
import * as
|
|
34002
|
+
import * as path15 from "node:path";
|
|
33911
34003
|
function resolveMainRepoFromWorktreeGitFile(wt) {
|
|
33912
|
-
const gitDirFile =
|
|
34004
|
+
const gitDirFile = path15.join(wt, ".git");
|
|
33913
34005
|
if (!fs13.existsSync(gitDirFile) || !fs13.statSync(gitDirFile).isFile()) return "";
|
|
33914
34006
|
const first2 = fs13.readFileSync(gitDirFile, "utf8").trim();
|
|
33915
34007
|
const m = first2.match(/^gitdir:\s*(.+)$/im);
|
|
33916
34008
|
if (!m) return "";
|
|
33917
|
-
const gitWorktreePath =
|
|
33918
|
-
const gitDir =
|
|
33919
|
-
return
|
|
34009
|
+
const gitWorktreePath = path15.resolve(wt, m[1].trim());
|
|
34010
|
+
const gitDir = path15.dirname(path15.dirname(gitWorktreePath));
|
|
34011
|
+
return path15.dirname(gitDir);
|
|
33920
34012
|
}
|
|
33921
34013
|
|
|
33922
34014
|
// src/git/worktree-remove.ts
|
|
@@ -34158,7 +34250,7 @@ function formatRemoteDisplayLabel(remoteUrl) {
|
|
|
34158
34250
|
}
|
|
34159
34251
|
|
|
34160
34252
|
// src/git/working-directory/changes/get-working-tree-change-repo-details.ts
|
|
34161
|
-
import * as
|
|
34253
|
+
import * as path17 from "node:path";
|
|
34162
34254
|
|
|
34163
34255
|
// src/git/working-directory/changes/parse-git-status.ts
|
|
34164
34256
|
function parseNameStatusLines(lines) {
|
|
@@ -34279,16 +34371,16 @@ async function listChangedFilesForCommit(repoGitCwd, repoRelPath, commitSha) {
|
|
|
34279
34371
|
|
|
34280
34372
|
// src/git/working-directory/changes/list-changed-files-for-repo.ts
|
|
34281
34373
|
import * as fs17 from "node:fs";
|
|
34282
|
-
import * as
|
|
34374
|
+
import * as path16 from "node:path";
|
|
34283
34375
|
|
|
34284
34376
|
// src/git/working-directory/changes/count-lines.ts
|
|
34285
34377
|
import { createReadStream } from "node:fs";
|
|
34286
34378
|
import * as readline2 from "node:readline";
|
|
34287
|
-
async function countTextFileLines(
|
|
34379
|
+
async function countTextFileLines(filePath) {
|
|
34288
34380
|
let bytes = 0;
|
|
34289
34381
|
const maxBytes = 512e3;
|
|
34290
34382
|
let lines = 0;
|
|
34291
|
-
const stream = createReadStream(
|
|
34383
|
+
const stream = createReadStream(filePath, { encoding: "utf8" });
|
|
34292
34384
|
const rl = readline2.createInterface({ input: stream, crlfDelay: Infinity });
|
|
34293
34385
|
for await (const _line of rl) {
|
|
34294
34386
|
lines += 1;
|
|
@@ -34316,15 +34408,15 @@ async function readGitBlobLines(repoCwd, pathInRepo) {
|
|
|
34316
34408
|
return null;
|
|
34317
34409
|
}
|
|
34318
34410
|
}
|
|
34319
|
-
async function readWorktreeFileLines(
|
|
34411
|
+
async function readWorktreeFileLines(filePath) {
|
|
34320
34412
|
try {
|
|
34321
|
-
const raw = await fs16.promises.readFile(
|
|
34413
|
+
const raw = await fs16.promises.readFile(filePath, "utf8");
|
|
34322
34414
|
return raw.split(/\r?\n/);
|
|
34323
34415
|
} catch {
|
|
34324
34416
|
return null;
|
|
34325
34417
|
}
|
|
34326
34418
|
}
|
|
34327
|
-
async function hydrateUnifiedPatchWithFileContext(patch,
|
|
34419
|
+
async function hydrateUnifiedPatchWithFileContext(patch, filePath, repoGitCwd, pathInRepo, change) {
|
|
34328
34420
|
if (!patch.trim() || patch.includes("Binary files")) return patch;
|
|
34329
34421
|
const all = patch.split("\n");
|
|
34330
34422
|
const out = [];
|
|
@@ -34341,7 +34433,7 @@ async function hydrateUnifiedPatchWithFileContext(patch, absFile, repoGitCwd, pa
|
|
|
34341
34433
|
};
|
|
34342
34434
|
const diskLines = async () => {
|
|
34343
34435
|
if (diskCache !== void 0) return diskCache;
|
|
34344
|
-
diskCache = await readWorktreeFileLines(
|
|
34436
|
+
diskCache = await readWorktreeFileLines(filePath);
|
|
34345
34437
|
return diskCache;
|
|
34346
34438
|
};
|
|
34347
34439
|
while (i < all.length) {
|
|
@@ -34453,7 +34545,7 @@ async function listChangedFilesForRepo(repoGitCwd, repoRelPath) {
|
|
|
34453
34545
|
const rows = [];
|
|
34454
34546
|
for (const pathInRepo of paths) {
|
|
34455
34547
|
const relLauncher = posixJoinDirFile(repoRelPath, pathInRepo.replace(/\\/g, "/"));
|
|
34456
|
-
const
|
|
34548
|
+
const repoFilePath = path16.join(repoGitCwd, pathInRepo);
|
|
34457
34549
|
const nums = numByPath.get(pathInRepo);
|
|
34458
34550
|
let additions = nums?.additions ?? 0;
|
|
34459
34551
|
let deletions = nums?.deletions ?? 0;
|
|
@@ -34466,8 +34558,8 @@ async function listChangedFilesForRepo(repoGitCwd, repoRelPath) {
|
|
|
34466
34558
|
deletions = fromGit.deletions;
|
|
34467
34559
|
} else {
|
|
34468
34560
|
try {
|
|
34469
|
-
const st = await fs17.promises.stat(
|
|
34470
|
-
if (st.isFile()) additions = await countTextFileLines(
|
|
34561
|
+
const st = await fs17.promises.stat(repoFilePath);
|
|
34562
|
+
if (st.isFile()) additions = await countTextFileLines(repoFilePath);
|
|
34471
34563
|
else additions = 0;
|
|
34472
34564
|
} catch {
|
|
34473
34565
|
additions = 0;
|
|
@@ -34492,10 +34584,10 @@ async function listChangedFilesForRepo(repoGitCwd, repoRelPath) {
|
|
|
34492
34584
|
} else {
|
|
34493
34585
|
pathInRepo = row.pathRelLauncher;
|
|
34494
34586
|
}
|
|
34495
|
-
const
|
|
34587
|
+
const filePath = path16.join(repoGitCwd, pathInRepo);
|
|
34496
34588
|
let patch = await unifiedDiffForFile(repoGitCwd, pathInRepo, row.change);
|
|
34497
34589
|
if (patch) {
|
|
34498
|
-
patch = await hydrateUnifiedPatchWithFileContext(patch,
|
|
34590
|
+
patch = await hydrateUnifiedPatchWithFileContext(patch, filePath, repoGitCwd, pathInRepo, row.change);
|
|
34499
34591
|
}
|
|
34500
34592
|
row.patchContent = patch;
|
|
34501
34593
|
}
|
|
@@ -34508,8 +34600,9 @@ function normRepoRel(p) {
|
|
|
34508
34600
|
return x === "" ? "." : x;
|
|
34509
34601
|
}
|
|
34510
34602
|
async function getWorkingTreeChangeRepoDetails(options) {
|
|
34511
|
-
const
|
|
34512
|
-
const
|
|
34603
|
+
const bridgeRoot = path17.resolve(getBridgeRoot());
|
|
34604
|
+
const sessionWtRoot = options.sessionWorktreeRootPath ? path17.resolve(options.sessionWorktreeRootPath) : null;
|
|
34605
|
+
const legacyNested = options.legacyRepoNestedSessionLayout === true;
|
|
34513
34606
|
const out = [];
|
|
34514
34607
|
const filter = options.repoFilterRelPath != null ? normRepoRel(options.repoFilterRelPath) : null;
|
|
34515
34608
|
const basisInput = options.basis ?? { kind: "working" };
|
|
@@ -34520,8 +34613,8 @@ async function getWorkingTreeChangeRepoDetails(options) {
|
|
|
34520
34613
|
throw new Error("commit sha is required for commit changes");
|
|
34521
34614
|
}
|
|
34522
34615
|
const basis = filter == null && basisInput.kind === "commit" ? { kind: "working" } : basisInput;
|
|
34523
|
-
for (const target of options.
|
|
34524
|
-
const t =
|
|
34616
|
+
for (const target of options.commitTargetPaths) {
|
|
34617
|
+
const t = path17.resolve(target);
|
|
34525
34618
|
if (!await isGitRepoDirectory(t)) continue;
|
|
34526
34619
|
const g = simpleGit(t);
|
|
34527
34620
|
let branch = "HEAD";
|
|
@@ -34533,8 +34626,9 @@ async function getWorkingTreeChangeRepoDetails(options) {
|
|
|
34533
34626
|
const remoteUrl = await getRemoteOriginUrl(t);
|
|
34534
34627
|
const remoteDisplay = formatRemoteDisplayLabel(remoteUrl);
|
|
34535
34628
|
let repoRelPath;
|
|
34536
|
-
if (
|
|
34537
|
-
const
|
|
34629
|
+
if (sessionWtRoot) {
|
|
34630
|
+
const anchor = legacyNested ? path17.dirname(t) : t;
|
|
34631
|
+
const relNorm = path17.relative(sessionWtRoot, anchor);
|
|
34538
34632
|
repoRelPath = relNorm === "" ? "." : relNorm.replace(/\\/g, "/");
|
|
34539
34633
|
} else {
|
|
34540
34634
|
let top = t;
|
|
@@ -34543,8 +34637,8 @@ async function getWorkingTreeChangeRepoDetails(options) {
|
|
|
34543
34637
|
} catch {
|
|
34544
34638
|
top = t;
|
|
34545
34639
|
}
|
|
34546
|
-
const rel =
|
|
34547
|
-
repoRelPath = rel.startsWith("..") ?
|
|
34640
|
+
const rel = path17.relative(bridgeRoot, path17.resolve(top)).replace(/\\/g, "/") || ".";
|
|
34641
|
+
repoRelPath = rel.startsWith("..") ? path17.basename(path17.resolve(top)) : rel;
|
|
34548
34642
|
}
|
|
34549
34643
|
const norm = normRepoRel(repoRelPath === "" ? "." : repoRelPath);
|
|
34550
34644
|
if (filter && norm !== filter) continue;
|
|
@@ -34610,14 +34704,37 @@ async function commitSessionWorktrees(options) {
|
|
|
34610
34704
|
|
|
34611
34705
|
// src/worktrees/discover-session-worktree-on-disk.ts
|
|
34612
34706
|
import * as fs18 from "node:fs";
|
|
34613
|
-
import * as
|
|
34614
|
-
function isGitDir(
|
|
34707
|
+
import * as path18 from "node:path";
|
|
34708
|
+
function isGitDir(dirPath) {
|
|
34615
34709
|
try {
|
|
34616
|
-
return fs18.existsSync(
|
|
34710
|
+
return fs18.existsSync(path18.join(dirPath, ".git"));
|
|
34617
34711
|
} catch {
|
|
34618
34712
|
return false;
|
|
34619
34713
|
}
|
|
34620
34714
|
}
|
|
34715
|
+
function collectGitRepoRootsUnderDirectory(rootPath) {
|
|
34716
|
+
const out = [];
|
|
34717
|
+
const walk = (dir) => {
|
|
34718
|
+
if (isGitDir(dir)) {
|
|
34719
|
+
out.push(path18.resolve(dir));
|
|
34720
|
+
return;
|
|
34721
|
+
}
|
|
34722
|
+
let entries;
|
|
34723
|
+
try {
|
|
34724
|
+
entries = fs18.readdirSync(dir, { withFileTypes: true });
|
|
34725
|
+
} catch {
|
|
34726
|
+
return;
|
|
34727
|
+
}
|
|
34728
|
+
for (const e of entries) {
|
|
34729
|
+
if (e.name.startsWith(".")) continue;
|
|
34730
|
+
const full = path18.join(dir, e.name);
|
|
34731
|
+
if (!e.isDirectory()) continue;
|
|
34732
|
+
walk(full);
|
|
34733
|
+
}
|
|
34734
|
+
};
|
|
34735
|
+
walk(path18.resolve(rootPath));
|
|
34736
|
+
return [...new Set(out)];
|
|
34737
|
+
}
|
|
34621
34738
|
function collectWorktreeRootsNamed(root, sessionId, maxDepth) {
|
|
34622
34739
|
const out = [];
|
|
34623
34740
|
const walk = (dir, depth) => {
|
|
@@ -34630,10 +34747,10 @@ function collectWorktreeRootsNamed(root, sessionId, maxDepth) {
|
|
|
34630
34747
|
}
|
|
34631
34748
|
for (const e of entries) {
|
|
34632
34749
|
if (e.name.startsWith(".")) continue;
|
|
34633
|
-
const full =
|
|
34750
|
+
const full = path18.join(dir, e.name);
|
|
34634
34751
|
if (!e.isDirectory()) continue;
|
|
34635
34752
|
if (e.name === sessionId) {
|
|
34636
|
-
if (isGitDir(full)) out.push(
|
|
34753
|
+
if (isGitDir(full)) out.push(path18.resolve(full));
|
|
34637
34754
|
} else {
|
|
34638
34755
|
walk(full, depth + 1);
|
|
34639
34756
|
}
|
|
@@ -34642,16 +34759,55 @@ function collectWorktreeRootsNamed(root, sessionId, maxDepth) {
|
|
|
34642
34759
|
walk(root, 0);
|
|
34643
34760
|
return out;
|
|
34644
34761
|
}
|
|
34762
|
+
function tryBindingFromSessionDirectory(sessionDir) {
|
|
34763
|
+
let st;
|
|
34764
|
+
try {
|
|
34765
|
+
st = fs18.statSync(sessionDir);
|
|
34766
|
+
} catch {
|
|
34767
|
+
return null;
|
|
34768
|
+
}
|
|
34769
|
+
if (!st.isDirectory()) return null;
|
|
34770
|
+
const worktreePaths = collectGitRepoRootsUnderDirectory(sessionDir);
|
|
34771
|
+
if (worktreePaths.length === 0) return null;
|
|
34772
|
+
const abs = path18.resolve(sessionDir);
|
|
34773
|
+
return {
|
|
34774
|
+
sessionParentPath: abs,
|
|
34775
|
+
workingTreeRelRoot: abs,
|
|
34776
|
+
repoCheckoutPaths: worktreePaths
|
|
34777
|
+
};
|
|
34778
|
+
}
|
|
34779
|
+
function discoverLegacyBindingAscendingFromCheckout(sessionId, checkoutPath) {
|
|
34780
|
+
const sid = sessionId.trim();
|
|
34781
|
+
if (!sid) return null;
|
|
34782
|
+
const hintR = path18.resolve(checkoutPath);
|
|
34783
|
+
let best = null;
|
|
34784
|
+
let cur = path18.dirname(hintR);
|
|
34785
|
+
for (let i = 0; i < 40; i++) {
|
|
34786
|
+
const paths = collectWorktreeRootsNamed(cur, sid, 24);
|
|
34787
|
+
if (paths.some((p) => path18.resolve(p) === hintR)) {
|
|
34788
|
+
const isolated = resolveIsolatedSessionParentPathFromCheckouts(paths) ?? path18.resolve(paths[0]);
|
|
34789
|
+
best = {
|
|
34790
|
+
sessionParentPath: path18.resolve(isolated),
|
|
34791
|
+
workingTreeRelRoot: path18.resolve(cur),
|
|
34792
|
+
repoCheckoutPaths: paths.map((p) => path18.resolve(p))
|
|
34793
|
+
};
|
|
34794
|
+
}
|
|
34795
|
+
const next = path18.dirname(cur);
|
|
34796
|
+
if (next === cur) break;
|
|
34797
|
+
cur = next;
|
|
34798
|
+
}
|
|
34799
|
+
return best;
|
|
34800
|
+
}
|
|
34645
34801
|
function discoverSessionWorktreeOnDisk(options) {
|
|
34646
|
-
const { sessionId,
|
|
34647
|
-
if (!sessionId.trim() || !fs18.existsSync(
|
|
34648
|
-
const preferredKey = getLauncherDirNameIfPresent(layout,
|
|
34802
|
+
const { sessionId, worktreesRootPath, layout, bridgeRoot } = options;
|
|
34803
|
+
if (!sessionId.trim() || !fs18.existsSync(worktreesRootPath)) return null;
|
|
34804
|
+
const preferredKey = getLauncherDirNameIfPresent(layout, bridgeRoot);
|
|
34649
34805
|
const keys = [];
|
|
34650
34806
|
if (preferredKey) keys.push(preferredKey);
|
|
34651
34807
|
try {
|
|
34652
|
-
for (const name of fs18.readdirSync(
|
|
34808
|
+
for (const name of fs18.readdirSync(worktreesRootPath)) {
|
|
34653
34809
|
if (name.startsWith(".")) continue;
|
|
34654
|
-
const p =
|
|
34810
|
+
const p = path18.join(worktreesRootPath, name);
|
|
34655
34811
|
if (!fs18.statSync(p).isDirectory()) continue;
|
|
34656
34812
|
if (name !== preferredKey) keys.push(name);
|
|
34657
34813
|
}
|
|
@@ -34659,26 +34815,58 @@ function discoverSessionWorktreeOnDisk(options) {
|
|
|
34659
34815
|
return null;
|
|
34660
34816
|
}
|
|
34661
34817
|
for (const key of keys) {
|
|
34662
|
-
const
|
|
34663
|
-
if (!fs18.existsSync(
|
|
34664
|
-
const
|
|
34665
|
-
|
|
34666
|
-
|
|
34818
|
+
const layoutRoot = path18.join(worktreesRootPath, key);
|
|
34819
|
+
if (!fs18.existsSync(layoutRoot) || !fs18.statSync(layoutRoot).isDirectory()) continue;
|
|
34820
|
+
const sessionDir = path18.join(layoutRoot, sessionId);
|
|
34821
|
+
const nested = tryBindingFromSessionDirectory(sessionDir);
|
|
34822
|
+
if (nested) return nested;
|
|
34823
|
+
const legacyPaths = collectWorktreeRootsNamed(layoutRoot, sessionId, 24);
|
|
34824
|
+
if (legacyPaths.length > 0) {
|
|
34825
|
+
const isolated = resolveIsolatedSessionParentPathFromCheckouts(legacyPaths) ?? path18.resolve(legacyPaths[0]);
|
|
34826
|
+
return {
|
|
34827
|
+
sessionParentPath: path18.resolve(isolated),
|
|
34828
|
+
workingTreeRelRoot: path18.resolve(layoutRoot),
|
|
34829
|
+
repoCheckoutPaths: legacyPaths.map((p) => path18.resolve(p))
|
|
34830
|
+
};
|
|
34667
34831
|
}
|
|
34668
34832
|
}
|
|
34669
34833
|
return null;
|
|
34670
34834
|
}
|
|
34671
|
-
function
|
|
34672
|
-
const
|
|
34673
|
-
if (!
|
|
34835
|
+
function discoverSessionWorktreesUnderSessionWorktreeRoot(sessionWorktreeRootPathOrHint, sessionId) {
|
|
34836
|
+
const sid = sessionId.trim();
|
|
34837
|
+
if (!sid) return null;
|
|
34838
|
+
const hint = path18.resolve(sessionWorktreeRootPathOrHint);
|
|
34839
|
+
const underHint = tryBindingFromSessionDirectory(path18.join(hint, sid));
|
|
34840
|
+
if (underHint) return underHint;
|
|
34841
|
+
const direct = tryBindingFromSessionDirectory(hint);
|
|
34842
|
+
if (direct) {
|
|
34843
|
+
if (path18.basename(hint) === sid && isGitDir(hint)) {
|
|
34844
|
+
const legacyFromCheckout = discoverLegacyBindingAscendingFromCheckout(sid, hint);
|
|
34845
|
+
if (legacyFromCheckout && legacyFromCheckout.repoCheckoutPaths.length > direct.repoCheckoutPaths.length) {
|
|
34846
|
+
return legacyFromCheckout;
|
|
34847
|
+
}
|
|
34848
|
+
}
|
|
34849
|
+
return direct;
|
|
34850
|
+
}
|
|
34851
|
+
if (path18.basename(hint) === sid && isGitDir(hint)) {
|
|
34852
|
+
const legacyFromCheckout = discoverLegacyBindingAscendingFromCheckout(sid, hint);
|
|
34853
|
+
if (legacyFromCheckout) return legacyFromCheckout;
|
|
34854
|
+
}
|
|
34855
|
+
let st;
|
|
34674
34856
|
try {
|
|
34675
|
-
|
|
34857
|
+
st = fs18.statSync(hint);
|
|
34676
34858
|
} catch {
|
|
34677
34859
|
return null;
|
|
34678
34860
|
}
|
|
34679
|
-
|
|
34680
|
-
|
|
34681
|
-
|
|
34861
|
+
if (!st.isDirectory()) return null;
|
|
34862
|
+
const legacyPaths = collectWorktreeRootsNamed(hint, sid, 24);
|
|
34863
|
+
if (legacyPaths.length === 0) return null;
|
|
34864
|
+
const isolated = resolveIsolatedSessionParentPathFromCheckouts(legacyPaths) ?? path18.resolve(legacyPaths[0]);
|
|
34865
|
+
return {
|
|
34866
|
+
sessionParentPath: path18.resolve(isolated),
|
|
34867
|
+
workingTreeRelRoot: hint,
|
|
34868
|
+
repoCheckoutPaths: legacyPaths.map((p) => path18.resolve(p))
|
|
34869
|
+
};
|
|
34682
34870
|
}
|
|
34683
34871
|
|
|
34684
34872
|
// src/worktrees/session-worktree-manager.ts
|
|
@@ -34688,42 +34876,86 @@ function parseSessionParent(v) {
|
|
|
34688
34876
|
return null;
|
|
34689
34877
|
}
|
|
34690
34878
|
var SessionWorktreeManager = class {
|
|
34691
|
-
|
|
34879
|
+
worktreesRootPath;
|
|
34692
34880
|
log;
|
|
34693
|
-
|
|
34694
|
-
|
|
34881
|
+
sessionRepoCheckoutPaths = /* @__PURE__ */ new Map();
|
|
34882
|
+
sessionParentPathBySession = /* @__PURE__ */ new Map();
|
|
34883
|
+
sessionWorkingTreeRelRootBySession = /* @__PURE__ */ new Map();
|
|
34695
34884
|
layout;
|
|
34696
34885
|
constructor(options) {
|
|
34697
|
-
this.
|
|
34886
|
+
this.worktreesRootPath = options.worktreesRootPath;
|
|
34698
34887
|
this.log = options.log;
|
|
34699
34888
|
this.layout = loadWorktreeLayout();
|
|
34700
34889
|
}
|
|
34701
|
-
|
|
34702
|
-
|
|
34703
|
-
this.
|
|
34890
|
+
rememberSessionWorktrees(sessionId, binding) {
|
|
34891
|
+
const paths = binding.repoCheckoutPaths.map((p) => path19.resolve(p));
|
|
34892
|
+
this.sessionRepoCheckoutPaths.set(sessionId, paths);
|
|
34893
|
+
this.sessionParentPathBySession.set(sessionId, path19.resolve(binding.sessionParentPath));
|
|
34894
|
+
this.sessionWorkingTreeRelRootBySession.set(sessionId, path19.resolve(binding.workingTreeRelRoot));
|
|
34895
|
+
}
|
|
34896
|
+
sessionParentPathAfterRemember(sessionId) {
|
|
34897
|
+
return this.sessionParentPathBySession.get(sessionId);
|
|
34704
34898
|
}
|
|
34705
34899
|
tryDiscoverFromDisk(sessionId) {
|
|
34706
34900
|
return discoverSessionWorktreeOnDisk({
|
|
34707
34901
|
sessionId,
|
|
34708
|
-
|
|
34902
|
+
worktreesRootPath: this.worktreesRootPath,
|
|
34709
34903
|
layout: this.layout,
|
|
34710
|
-
|
|
34904
|
+
bridgeRoot: getBridgeRoot()
|
|
34711
34905
|
});
|
|
34712
34906
|
}
|
|
34907
|
+
isLegacyNestedLayout(sessionId) {
|
|
34908
|
+
const parent = this.sessionParentPathBySession.get(sessionId);
|
|
34909
|
+
const relRoot = this.sessionWorkingTreeRelRootBySession.get(sessionId);
|
|
34910
|
+
if (!parent || !relRoot) return false;
|
|
34911
|
+
return path19.resolve(parent) !== path19.resolve(relRoot);
|
|
34912
|
+
}
|
|
34913
|
+
/**
|
|
34914
|
+
* Session parent path for `worktrees_root`: the per-session directory (new layout) or primary checkout (legacy).
|
|
34915
|
+
*/
|
|
34916
|
+
getIsolatedSessionParentPathForSession(sessionId) {
|
|
34917
|
+
if (!sessionId) return null;
|
|
34918
|
+
const sid = sessionId.trim();
|
|
34919
|
+
const cached2 = this.sessionParentPathBySession.get(sid);
|
|
34920
|
+
if (cached2) return path19.resolve(cached2);
|
|
34921
|
+
const paths = this.ensureRepoCheckoutPathsForSession(sid) ?? this.getRepoCheckoutPathsForSession(sid);
|
|
34922
|
+
if (!paths?.length) return null;
|
|
34923
|
+
return resolveIsolatedSessionParentPathFromCheckouts(paths);
|
|
34924
|
+
}
|
|
34713
34925
|
/**
|
|
34714
|
-
*
|
|
34926
|
+
* Resolved **session parent path** for the agent: session directory in worktrees mode,
|
|
34927
|
+
* or `undefined` meaning use {@link getBridgeRoot}.
|
|
34715
34928
|
*/
|
|
34716
|
-
async
|
|
34929
|
+
async resolveSessionParentPathForPrompt(sessionId, opts) {
|
|
34717
34930
|
if (!sessionId) return void 0;
|
|
34931
|
+
const sid = sessionId.trim();
|
|
34718
34932
|
const parentPathRaw = opts.sessionParentPath?.trim();
|
|
34719
34933
|
if (parentPathRaw) {
|
|
34720
|
-
const
|
|
34721
|
-
const sid = sessionId?.trim();
|
|
34934
|
+
const resolved = path19.resolve(parentPathRaw);
|
|
34722
34935
|
if (sid && parseSessionParent(opts.sessionParent) === "worktrees_root") {
|
|
34723
|
-
const
|
|
34724
|
-
if (
|
|
34936
|
+
const diskFirst = this.tryDiscoverFromDisk(sid);
|
|
34937
|
+
if (diskFirst) {
|
|
34938
|
+
this.rememberSessionWorktrees(sid, diskFirst);
|
|
34939
|
+
return this.sessionParentPathAfterRemember(sid);
|
|
34940
|
+
}
|
|
34941
|
+
const fromRoot = discoverSessionWorktreesUnderSessionWorktreeRoot(resolved, sid);
|
|
34942
|
+
if (fromRoot) {
|
|
34943
|
+
this.rememberSessionWorktrees(sid, fromRoot);
|
|
34944
|
+
return this.sessionParentPathAfterRemember(sid);
|
|
34945
|
+
}
|
|
34946
|
+
let cur = resolved;
|
|
34947
|
+
for (let i = 0; i < 16; i++) {
|
|
34948
|
+
const tryRoot = discoverSessionWorktreesUnderSessionWorktreeRoot(cur, sid);
|
|
34949
|
+
if (tryRoot) {
|
|
34950
|
+
this.rememberSessionWorktrees(sid, tryRoot);
|
|
34951
|
+
return this.sessionParentPathAfterRemember(sid);
|
|
34952
|
+
}
|
|
34953
|
+
const next = path19.dirname(cur);
|
|
34954
|
+
if (next === cur) break;
|
|
34955
|
+
cur = next;
|
|
34956
|
+
}
|
|
34725
34957
|
}
|
|
34726
|
-
return
|
|
34958
|
+
return resolved;
|
|
34727
34959
|
}
|
|
34728
34960
|
const parentKind = parseSessionParent(opts.sessionParent);
|
|
34729
34961
|
if (parentKind === "bridge_root") {
|
|
@@ -34731,78 +34963,102 @@ var SessionWorktreeManager = class {
|
|
|
34731
34963
|
}
|
|
34732
34964
|
if (parentKind === "worktrees_root") {
|
|
34733
34965
|
if (!opts.isNewSession) {
|
|
34734
|
-
const cached2 = this.
|
|
34735
|
-
if (cached2) return
|
|
34736
|
-
const disc = this.tryDiscoverFromDisk(
|
|
34966
|
+
const cached2 = this.sessionParentPathAfterRemember(sid);
|
|
34967
|
+
if (cached2) return cached2;
|
|
34968
|
+
const disc = this.tryDiscoverFromDisk(sid);
|
|
34737
34969
|
if (disc) {
|
|
34738
|
-
this.
|
|
34739
|
-
return
|
|
34970
|
+
this.rememberSessionWorktrees(sid, disc);
|
|
34971
|
+
return this.sessionParentPathAfterRemember(sid);
|
|
34740
34972
|
}
|
|
34741
34973
|
return void 0;
|
|
34742
34974
|
}
|
|
34743
34975
|
const prep2 = await prepareNewSessionWorktrees({
|
|
34744
|
-
|
|
34745
|
-
|
|
34746
|
-
sessionId,
|
|
34976
|
+
worktreesRootPath: this.worktreesRootPath,
|
|
34977
|
+
bridgeRoot: getBridgeRoot(),
|
|
34978
|
+
sessionId: sid,
|
|
34747
34979
|
layout: this.layout,
|
|
34748
34980
|
log: this.log
|
|
34749
34981
|
});
|
|
34750
34982
|
if (!prep2) return void 0;
|
|
34751
|
-
this.
|
|
34752
|
-
|
|
34983
|
+
this.rememberSessionWorktrees(sid, {
|
|
34984
|
+
sessionParentPath: prep2.sessionParentPath,
|
|
34985
|
+
workingTreeRelRoot: prep2.workingTreeRelRoot,
|
|
34986
|
+
repoCheckoutPaths: prep2.worktreePaths
|
|
34987
|
+
});
|
|
34988
|
+
return this.sessionParentPathAfterRemember(sid);
|
|
34753
34989
|
}
|
|
34754
34990
|
if (!opts.isNewSession) {
|
|
34755
|
-
const cached2 = this.
|
|
34756
|
-
if (cached2) return
|
|
34757
|
-
const disc = this.tryDiscoverFromDisk(
|
|
34991
|
+
const cached2 = this.sessionParentPathAfterRemember(sid);
|
|
34992
|
+
if (cached2) return cached2;
|
|
34993
|
+
const disc = this.tryDiscoverFromDisk(sid);
|
|
34758
34994
|
if (disc) {
|
|
34759
|
-
this.
|
|
34760
|
-
return
|
|
34995
|
+
this.rememberSessionWorktrees(sid, disc);
|
|
34996
|
+
return this.sessionParentPathAfterRemember(sid);
|
|
34761
34997
|
}
|
|
34762
34998
|
return void 0;
|
|
34763
34999
|
}
|
|
34764
35000
|
const prep = await prepareNewSessionWorktrees({
|
|
34765
|
-
|
|
34766
|
-
|
|
34767
|
-
sessionId,
|
|
35001
|
+
worktreesRootPath: this.worktreesRootPath,
|
|
35002
|
+
bridgeRoot: getBridgeRoot(),
|
|
35003
|
+
sessionId: sid,
|
|
34768
35004
|
layout: this.layout,
|
|
34769
35005
|
log: this.log
|
|
34770
35006
|
});
|
|
34771
35007
|
if (!prep) return void 0;
|
|
34772
|
-
this.
|
|
34773
|
-
|
|
35008
|
+
this.rememberSessionWorktrees(sid, {
|
|
35009
|
+
sessionParentPath: prep.sessionParentPath,
|
|
35010
|
+
workingTreeRelRoot: prep.workingTreeRelRoot,
|
|
35011
|
+
repoCheckoutPaths: prep.worktreePaths
|
|
35012
|
+
});
|
|
35013
|
+
return this.sessionParentPathAfterRemember(sid);
|
|
34774
35014
|
}
|
|
34775
35015
|
async renameSessionBranch(sessionId, newBranch) {
|
|
34776
|
-
const paths = this.
|
|
35016
|
+
const paths = this.sessionRepoCheckoutPaths.get(sessionId);
|
|
34777
35017
|
if (!paths?.length) return;
|
|
34778
35018
|
await renameSessionWorktreeBranches(paths, newBranch, this.log);
|
|
34779
35019
|
}
|
|
34780
|
-
/** True when this session
|
|
35020
|
+
/** True when this session uses an isolated worktree layout (not the bridge root). */
|
|
34781
35021
|
usesWorktreeSession(sessionId) {
|
|
34782
35022
|
if (!sessionId) return false;
|
|
34783
|
-
return this.
|
|
35023
|
+
return this.sessionParentPathBySession.has(sessionId);
|
|
34784
35024
|
}
|
|
34785
|
-
|
|
35025
|
+
/** Per-repo git checkout directories for this session (for snapshots, commits, change lists). */
|
|
35026
|
+
getRepoCheckoutPathsForSession(sessionId) {
|
|
34786
35027
|
if (!sessionId) return void 0;
|
|
34787
|
-
const paths = this.
|
|
35028
|
+
const paths = this.sessionRepoCheckoutPaths.get(sessionId);
|
|
34788
35029
|
return paths?.length ? [...paths] : void 0;
|
|
34789
35030
|
}
|
|
34790
|
-
/**
|
|
34791
|
-
|
|
34792
|
-
|
|
34793
|
-
|
|
34794
|
-
|
|
35031
|
+
/**
|
|
35032
|
+
* Same paths as {@link getRepoCheckoutPathsForSession}, but loads from disk into memory when the CLI
|
|
35033
|
+
* restarted or maps were not yet populated (avoids discovering every repo under the worktrees root).
|
|
35034
|
+
*/
|
|
35035
|
+
ensureRepoCheckoutPathsForSession(sessionId) {
|
|
35036
|
+
if (!sessionId?.trim()) return void 0;
|
|
35037
|
+
const sid = sessionId.trim();
|
|
35038
|
+
const cached2 = this.sessionRepoCheckoutPaths.get(sid);
|
|
35039
|
+
if (cached2?.length) return [...cached2];
|
|
35040
|
+
const disc = this.tryDiscoverFromDisk(sid);
|
|
35041
|
+
if (disc?.repoCheckoutPaths.length) {
|
|
35042
|
+
this.rememberSessionWorktrees(sid, disc);
|
|
35043
|
+
return [...disc.repoCheckoutPaths];
|
|
35044
|
+
}
|
|
35045
|
+
return void 0;
|
|
35046
|
+
}
|
|
35047
|
+
/** Session parent directory when in worktrees mode; null otherwise (same as {@link getIsolatedSessionParentPathForSession} path). */
|
|
35048
|
+
getSessionWorktreeRootForSession(sessionId) {
|
|
35049
|
+
return this.getIsolatedSessionParentPathForSession(sessionId);
|
|
34795
35050
|
}
|
|
34796
35051
|
async removeSessionWorktrees(sessionId) {
|
|
34797
|
-
const paths = this.
|
|
34798
|
-
this.
|
|
34799
|
-
this.
|
|
35052
|
+
const paths = this.sessionRepoCheckoutPaths.get(sessionId);
|
|
35053
|
+
this.sessionRepoCheckoutPaths.delete(sessionId);
|
|
35054
|
+
this.sessionParentPathBySession.delete(sessionId);
|
|
35055
|
+
this.sessionWorkingTreeRelRootBySession.delete(sessionId);
|
|
34800
35056
|
if (!paths?.length) return;
|
|
34801
35057
|
await removeSessionWorktrees(paths, this.log);
|
|
34802
35058
|
}
|
|
34803
35059
|
async commitSession(params) {
|
|
34804
|
-
const paths = this.
|
|
34805
|
-
const targets = paths?.length ? paths : [
|
|
35060
|
+
const paths = this.sessionRepoCheckoutPaths.get(params.sessionId);
|
|
35061
|
+
const targets = paths?.length ? paths : [getBridgeRoot()];
|
|
34806
35062
|
return commitSessionWorktrees({
|
|
34807
35063
|
paths: targets,
|
|
34808
35064
|
branch: params.branch,
|
|
@@ -34811,14 +35067,14 @@ var SessionWorktreeManager = class {
|
|
|
34811
35067
|
});
|
|
34812
35068
|
}
|
|
34813
35069
|
resolveCommitTargets(sessionId) {
|
|
34814
|
-
const paths = this.
|
|
35070
|
+
const paths = this.sessionRepoCheckoutPaths.get(sessionId);
|
|
34815
35071
|
if (paths?.length) return paths;
|
|
34816
35072
|
const disc = this.tryDiscoverFromDisk(sessionId);
|
|
34817
|
-
if (disc?.
|
|
34818
|
-
this.
|
|
34819
|
-
return disc.
|
|
35073
|
+
if (disc?.repoCheckoutPaths.length) {
|
|
35074
|
+
this.rememberSessionWorktrees(sessionId, disc);
|
|
35075
|
+
return disc.repoCheckoutPaths;
|
|
34820
35076
|
}
|
|
34821
|
-
return [
|
|
35077
|
+
return [getBridgeRoot()];
|
|
34822
35078
|
}
|
|
34823
35079
|
async getSessionWorkingTreeStatus(sessionId) {
|
|
34824
35080
|
return aggregateSessionPathsWorkingTreeStatus(this.resolveCommitTargets(sessionId));
|
|
@@ -34826,10 +35082,12 @@ var SessionWorktreeManager = class {
|
|
|
34826
35082
|
/** Per-repo changed files vs HEAD (or a single commit vs parent) for the same git roots used for commit/push. */
|
|
34827
35083
|
async getSessionWorkingTreeChangeDetails(sessionId, opts) {
|
|
34828
35084
|
const targets = this.resolveCommitTargets(sessionId);
|
|
34829
|
-
const
|
|
35085
|
+
const sessionWorkingTreeRelRoot = this.sessionWorkingTreeRelRootBySession.get(sessionId) ?? null;
|
|
35086
|
+
const legacyNested = this.isLegacyNestedLayout(sessionId);
|
|
34830
35087
|
return getWorkingTreeChangeRepoDetails({
|
|
34831
|
-
|
|
34832
|
-
|
|
35088
|
+
commitTargetPaths: targets,
|
|
35089
|
+
sessionWorktreeRootPath: sessionWorkingTreeRelRoot,
|
|
35090
|
+
legacyRepoNestedSessionLayout: legacyNested,
|
|
34833
35091
|
repoFilterRelPath: opts?.repoRelPath?.trim() ? opts.repoRelPath.trim() : null,
|
|
34834
35092
|
basis: opts?.basis
|
|
34835
35093
|
});
|
|
@@ -34844,16 +35102,16 @@ var SessionWorktreeManager = class {
|
|
|
34844
35102
|
}
|
|
34845
35103
|
}
|
|
34846
35104
|
};
|
|
34847
|
-
function
|
|
34848
|
-
return
|
|
35105
|
+
function defaultWorktreesRootPath() {
|
|
35106
|
+
return path19.join(os4.homedir(), ".buildautomaton", "worktrees");
|
|
34849
35107
|
}
|
|
34850
35108
|
|
|
34851
35109
|
// src/files/watch-file-index.ts
|
|
34852
35110
|
import { watch } from "node:fs";
|
|
34853
|
-
import
|
|
35111
|
+
import path26 from "node:path";
|
|
34854
35112
|
|
|
34855
35113
|
// src/files/index/build-file-index.ts
|
|
34856
|
-
import
|
|
35114
|
+
import path23 from "node:path";
|
|
34857
35115
|
|
|
34858
35116
|
// src/runtime/yield-to-event-loop.ts
|
|
34859
35117
|
function yieldToEventLoop() {
|
|
@@ -34862,13 +35120,13 @@ function yieldToEventLoop() {
|
|
|
34862
35120
|
|
|
34863
35121
|
// src/files/index/walk-workspace-tree.ts
|
|
34864
35122
|
import fs19 from "node:fs";
|
|
34865
|
-
import
|
|
35123
|
+
import path21 from "node:path";
|
|
34866
35124
|
|
|
34867
35125
|
// src/files/index/constants.ts
|
|
34868
|
-
import
|
|
35126
|
+
import path20 from "node:path";
|
|
34869
35127
|
import os5 from "node:os";
|
|
34870
35128
|
var INDEX_WORK_YIELD_EVERY = 256;
|
|
34871
|
-
var INDEX_DIR =
|
|
35129
|
+
var INDEX_DIR = path20.join(os5.homedir(), ".buildautomaton");
|
|
34872
35130
|
var INDEX_HASH_LEN = 16;
|
|
34873
35131
|
var INDEX_VERSION = 2;
|
|
34874
35132
|
var INDEX_LOG_PREFIX = "[file-index]";
|
|
@@ -34883,14 +35141,14 @@ function walkWorkspaceTreeSync(dir, baseDir, out) {
|
|
|
34883
35141
|
}
|
|
34884
35142
|
for (const name of names) {
|
|
34885
35143
|
if (name.startsWith(".")) continue;
|
|
34886
|
-
const full =
|
|
35144
|
+
const full = path21.join(dir, name);
|
|
34887
35145
|
let stat3;
|
|
34888
35146
|
try {
|
|
34889
35147
|
stat3 = fs19.statSync(full);
|
|
34890
35148
|
} catch {
|
|
34891
35149
|
continue;
|
|
34892
35150
|
}
|
|
34893
|
-
const relative5 =
|
|
35151
|
+
const relative5 = path21.relative(baseDir, full).replace(/\\/g, "/");
|
|
34894
35152
|
if (stat3.isDirectory()) {
|
|
34895
35153
|
walkWorkspaceTreeSync(full, baseDir, out);
|
|
34896
35154
|
} else if (stat3.isFile()) {
|
|
@@ -34911,14 +35169,14 @@ async function walkWorkspaceTreeAsync(dir, baseDir, out, state) {
|
|
|
34911
35169
|
await yieldToEventLoop();
|
|
34912
35170
|
}
|
|
34913
35171
|
state.n++;
|
|
34914
|
-
const full =
|
|
35172
|
+
const full = path21.join(dir, name);
|
|
34915
35173
|
let stat3;
|
|
34916
35174
|
try {
|
|
34917
35175
|
stat3 = await fs19.promises.stat(full);
|
|
34918
35176
|
} catch {
|
|
34919
35177
|
continue;
|
|
34920
35178
|
}
|
|
34921
|
-
const relative5 =
|
|
35179
|
+
const relative5 = path21.relative(baseDir, full).replace(/\\/g, "/");
|
|
34922
35180
|
if (stat3.isDirectory()) {
|
|
34923
35181
|
await walkWorkspaceTreeAsync(full, baseDir, out, state);
|
|
34924
35182
|
} else if (stat3.isFile()) {
|
|
@@ -35002,11 +35260,11 @@ async function buildTrigramMapForPathsAsync(paths) {
|
|
|
35002
35260
|
import fs20 from "node:fs";
|
|
35003
35261
|
|
|
35004
35262
|
// src/files/index/paths.ts
|
|
35005
|
-
import
|
|
35263
|
+
import path22 from "node:path";
|
|
35006
35264
|
import crypto2 from "node:crypto";
|
|
35007
35265
|
function getIndexPathForCwd(resolvedCwd) {
|
|
35008
35266
|
const hash = crypto2.createHash("sha256").update(resolvedCwd).digest("hex").slice(0, INDEX_HASH_LEN);
|
|
35009
|
-
return
|
|
35267
|
+
return path22.join(INDEX_DIR, `.file-index-${hash}.json`);
|
|
35010
35268
|
}
|
|
35011
35269
|
|
|
35012
35270
|
// src/files/index/write-index-file.ts
|
|
@@ -35037,7 +35295,7 @@ function sortPaths(paths) {
|
|
|
35037
35295
|
paths.sort((a, b) => a.localeCompare(b, void 0, { sensitivity: "base" }));
|
|
35038
35296
|
}
|
|
35039
35297
|
function buildFileIndex(cwd) {
|
|
35040
|
-
const resolved =
|
|
35298
|
+
const resolved = path23.resolve(cwd);
|
|
35041
35299
|
const paths = [];
|
|
35042
35300
|
walkWorkspaceTreeSync(resolved, resolved, paths);
|
|
35043
35301
|
sortPaths(paths);
|
|
@@ -35047,7 +35305,7 @@ function buildFileIndex(cwd) {
|
|
|
35047
35305
|
return data;
|
|
35048
35306
|
}
|
|
35049
35307
|
async function buildFileIndexAsync(cwd) {
|
|
35050
|
-
const resolved =
|
|
35308
|
+
const resolved = path23.resolve(cwd);
|
|
35051
35309
|
const paths = [];
|
|
35052
35310
|
await walkWorkspaceTreeAsync(resolved, resolved, paths, createWalkYieldState());
|
|
35053
35311
|
await yieldToEventLoop();
|
|
@@ -35060,9 +35318,9 @@ async function buildFileIndexAsync(cwd) {
|
|
|
35060
35318
|
|
|
35061
35319
|
// src/files/index/load-file-index.ts
|
|
35062
35320
|
import fs21 from "node:fs";
|
|
35063
|
-
import
|
|
35321
|
+
import path24 from "node:path";
|
|
35064
35322
|
function loadFileIndex(cwd) {
|
|
35065
|
-
const resolved =
|
|
35323
|
+
const resolved = path24.resolve(cwd);
|
|
35066
35324
|
const indexPath = getIndexPathForCwd(resolved);
|
|
35067
35325
|
try {
|
|
35068
35326
|
const raw = fs21.readFileSync(indexPath, "utf8");
|
|
@@ -35084,9 +35342,9 @@ function loadFileIndex(cwd) {
|
|
|
35084
35342
|
}
|
|
35085
35343
|
|
|
35086
35344
|
// src/files/index/ensure-file-index.ts
|
|
35087
|
-
import
|
|
35345
|
+
import path25 from "node:path";
|
|
35088
35346
|
async function ensureFileIndexAsync(cwd) {
|
|
35089
|
-
const resolved =
|
|
35347
|
+
const resolved = path25.resolve(cwd);
|
|
35090
35348
|
const cached2 = loadFileIndex(resolved);
|
|
35091
35349
|
if (cached2 !== null) return { data: cached2, fromCache: true };
|
|
35092
35350
|
const data = await buildFileIndexAsync(resolved);
|
|
@@ -35168,8 +35426,8 @@ function createFsWatcher(resolved, schedule) {
|
|
|
35168
35426
|
throw e;
|
|
35169
35427
|
}
|
|
35170
35428
|
}
|
|
35171
|
-
function startFileIndexWatcher(cwd =
|
|
35172
|
-
const resolved =
|
|
35429
|
+
function startFileIndexWatcher(cwd = getBridgeRoot()) {
|
|
35430
|
+
const resolved = path26.resolve(cwd);
|
|
35173
35431
|
void buildFileIndexAsync(resolved).catch((e) => {
|
|
35174
35432
|
console.error("[file-index] Initial index build failed:", e);
|
|
35175
35433
|
});
|
|
@@ -35507,10 +35765,10 @@ function trySpawnShellTruePiped(command, env, cwd, devNullFd, signal) {
|
|
|
35507
35765
|
import { spawn as spawn6 } from "node:child_process";
|
|
35508
35766
|
import fs24 from "node:fs";
|
|
35509
35767
|
import { tmpdir } from "node:os";
|
|
35510
|
-
import
|
|
35768
|
+
import path27 from "node:path";
|
|
35511
35769
|
function trySpawnMergedLogFile(command, env, cwd, signal) {
|
|
35512
|
-
const tmpRoot = fs24.mkdtempSync(
|
|
35513
|
-
const logPath =
|
|
35770
|
+
const tmpRoot = fs24.mkdtempSync(path27.join(tmpdir(), "ba-devsrv-log-"));
|
|
35771
|
+
const logPath = path27.join(tmpRoot, "combined.log");
|
|
35514
35772
|
let logFd;
|
|
35515
35773
|
try {
|
|
35516
35774
|
logFd = fs24.openSync(logPath, "a");
|
|
@@ -35554,15 +35812,15 @@ function trySpawnMergedLogFile(command, env, cwd, signal) {
|
|
|
35554
35812
|
import { spawn as spawn7 } from "node:child_process";
|
|
35555
35813
|
import fs25 from "node:fs";
|
|
35556
35814
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
35557
|
-
import
|
|
35815
|
+
import path28 from "node:path";
|
|
35558
35816
|
function shSingleQuote(s) {
|
|
35559
35817
|
return `'${s.replace(/'/g, `'\\''`)}'`;
|
|
35560
35818
|
}
|
|
35561
35819
|
function trySpawnShellScriptLogRedirectUnix(command, env, cwd, signal) {
|
|
35562
|
-
const tmpRoot = fs25.mkdtempSync(
|
|
35563
|
-
const logPath =
|
|
35564
|
-
const innerPath =
|
|
35565
|
-
const runnerPath =
|
|
35820
|
+
const tmpRoot = fs25.mkdtempSync(path28.join(tmpdir2(), "ba-devsrv-sh-"));
|
|
35821
|
+
const logPath = path28.join(tmpRoot, "combined.log");
|
|
35822
|
+
const innerPath = path28.join(tmpRoot, "_cmd.sh");
|
|
35823
|
+
const runnerPath = path28.join(tmpRoot, "_run.sh");
|
|
35566
35824
|
try {
|
|
35567
35825
|
fs25.writeFileSync(innerPath, `#!/bin/sh
|
|
35568
35826
|
${command}
|
|
@@ -35593,9 +35851,9 @@ cd ${shSingleQuote(cwd)}
|
|
|
35593
35851
|
}
|
|
35594
35852
|
}
|
|
35595
35853
|
function trySpawnShellScriptLogRedirectWin(command, env, cwd, signal) {
|
|
35596
|
-
const tmpRoot = fs25.mkdtempSync(
|
|
35597
|
-
const logPath =
|
|
35598
|
-
const runnerPath =
|
|
35854
|
+
const tmpRoot = fs25.mkdtempSync(path28.join(tmpdir2(), "ba-devsrv-sh-"));
|
|
35855
|
+
const logPath = path28.join(tmpRoot, "combined.log");
|
|
35856
|
+
const runnerPath = path28.join(tmpRoot, "_run.bat");
|
|
35599
35857
|
const q = (p) => `"${p.replace(/"/g, '""')}"`;
|
|
35600
35858
|
const com = process.env.ComSpec || "cmd.exe";
|
|
35601
35859
|
try {
|
|
@@ -35805,13 +36063,13 @@ var DevServerManager = class {
|
|
|
35805
36063
|
abortControllersByServerId = /* @__PURE__ */ new Map();
|
|
35806
36064
|
getWs;
|
|
35807
36065
|
log;
|
|
35808
|
-
|
|
36066
|
+
getBridgeRoot;
|
|
35809
36067
|
e2ee;
|
|
35810
36068
|
firehoseSink;
|
|
35811
36069
|
constructor(options) {
|
|
35812
36070
|
this.getWs = options.getWs;
|
|
35813
36071
|
this.log = options.log;
|
|
35814
|
-
this.
|
|
36072
|
+
this.getBridgeRoot = options.getBridgeRoot ?? (() => process.cwd());
|
|
35815
36073
|
this.e2ee = options.e2ee;
|
|
35816
36074
|
this.firehoseSink = new DevServerFirehoseSink({
|
|
35817
36075
|
getTails: (serverId) => this.snapshotTails(serverId),
|
|
@@ -35900,7 +36158,7 @@ var DevServerManager = class {
|
|
|
35900
36158
|
this.sendStatus(serverId, "starting", void 0, emptyTails());
|
|
35901
36159
|
const ac = new AbortController();
|
|
35902
36160
|
this.abortControllersByServerId.set(serverId, ac);
|
|
35903
|
-
const cwd = this.
|
|
36161
|
+
const cwd = this.getBridgeRoot();
|
|
35904
36162
|
const childEnv = envForSpawn(process.env, def.env, def.ports);
|
|
35905
36163
|
const cmd = substituteCommand(def.command.trim(), childEnv);
|
|
35906
36164
|
const title = def.name.trim() || serverId.slice(0, 8);
|
|
@@ -36491,13 +36749,13 @@ function createOnBridgeIdentified(opts) {
|
|
|
36491
36749
|
|
|
36492
36750
|
// src/skills/discover-local-agent-skills.ts
|
|
36493
36751
|
import fs26 from "node:fs";
|
|
36494
|
-
import
|
|
36752
|
+
import path29 from "node:path";
|
|
36495
36753
|
var SKILL_DISCOVERY_ROOTS = [".agents/skills", ".claude/skills", ".cursor/skills", "skills"];
|
|
36496
36754
|
function discoverLocalSkills(cwd) {
|
|
36497
36755
|
const out = [];
|
|
36498
36756
|
const seenKeys = /* @__PURE__ */ new Set();
|
|
36499
36757
|
for (const rel of SKILL_DISCOVERY_ROOTS) {
|
|
36500
|
-
const base =
|
|
36758
|
+
const base = path29.join(cwd, rel);
|
|
36501
36759
|
if (!fs26.existsSync(base) || !fs26.statSync(base).isDirectory()) continue;
|
|
36502
36760
|
let entries = [];
|
|
36503
36761
|
try {
|
|
@@ -36506,13 +36764,13 @@ function discoverLocalSkills(cwd) {
|
|
|
36506
36764
|
continue;
|
|
36507
36765
|
}
|
|
36508
36766
|
for (const name of entries) {
|
|
36509
|
-
const dir =
|
|
36767
|
+
const dir = path29.join(base, name);
|
|
36510
36768
|
try {
|
|
36511
36769
|
if (!fs26.statSync(dir).isDirectory()) continue;
|
|
36512
36770
|
} catch {
|
|
36513
36771
|
continue;
|
|
36514
36772
|
}
|
|
36515
|
-
const skillMd =
|
|
36773
|
+
const skillMd = path29.join(dir, "SKILL.md");
|
|
36516
36774
|
if (!fs26.existsSync(skillMd)) continue;
|
|
36517
36775
|
const key = `${rel}/${name}`;
|
|
36518
36776
|
if (seenKeys.has(key)) continue;
|
|
@@ -36525,7 +36783,7 @@ function discoverLocalSkills(cwd) {
|
|
|
36525
36783
|
function discoverSkillLayoutRoots(cwd) {
|
|
36526
36784
|
const roots = [];
|
|
36527
36785
|
for (const rel of SKILL_DISCOVERY_ROOTS) {
|
|
36528
|
-
const base =
|
|
36786
|
+
const base = path29.join(cwd, rel);
|
|
36529
36787
|
if (!fs26.existsSync(base) || !fs26.statSync(base).isDirectory()) continue;
|
|
36530
36788
|
let entries = [];
|
|
36531
36789
|
try {
|
|
@@ -36535,13 +36793,13 @@ function discoverSkillLayoutRoots(cwd) {
|
|
|
36535
36793
|
}
|
|
36536
36794
|
const skills2 = [];
|
|
36537
36795
|
for (const name of entries) {
|
|
36538
|
-
const dir =
|
|
36796
|
+
const dir = path29.join(base, name);
|
|
36539
36797
|
try {
|
|
36540
36798
|
if (!fs26.statSync(dir).isDirectory()) continue;
|
|
36541
36799
|
} catch {
|
|
36542
36800
|
continue;
|
|
36543
36801
|
}
|
|
36544
|
-
if (!fs26.existsSync(
|
|
36802
|
+
if (!fs26.existsSync(path29.join(dir, "SKILL.md"))) continue;
|
|
36545
36803
|
const relPath = `${rel}/${name}`.replace(/\\/g, "/");
|
|
36546
36804
|
skills2.push({ name, relPath });
|
|
36547
36805
|
}
|
|
@@ -36585,7 +36843,7 @@ function createSendLocalSkillsReport(getWs, logFn) {
|
|
|
36585
36843
|
try {
|
|
36586
36844
|
const socket = getWs();
|
|
36587
36845
|
if (!socket || socket.readyState !== wrapper_default.OPEN) return;
|
|
36588
|
-
const skills2 = discoverLocalSkills(
|
|
36846
|
+
const skills2 = discoverLocalSkills(getBridgeRoot());
|
|
36589
36847
|
sendWsMessage(socket, { type: "local_skills", skills: skills2 });
|
|
36590
36848
|
} catch (e) {
|
|
36591
36849
|
logFn(
|
|
@@ -36723,9 +36981,6 @@ var handleAgentConfigMessage = (msg, deps) => {
|
|
|
36723
36981
|
handleBridgeAgentConfig(msg, deps);
|
|
36724
36982
|
};
|
|
36725
36983
|
|
|
36726
|
-
// src/agents/acp/from-bridge/handle-bridge-prompt.ts
|
|
36727
|
-
import * as path30 from "node:path";
|
|
36728
|
-
|
|
36729
36984
|
// src/prompt-turn-queue/client-report.ts
|
|
36730
36985
|
function sendPromptQueueClientReport(ws, queues) {
|
|
36731
36986
|
if (!ws) return false;
|
|
@@ -36739,7 +36994,7 @@ import fs28 from "node:fs";
|
|
|
36739
36994
|
// src/prompt-turn-queue/paths.ts
|
|
36740
36995
|
import crypto3 from "node:crypto";
|
|
36741
36996
|
import fs27 from "node:fs";
|
|
36742
|
-
import
|
|
36997
|
+
import path30 from "node:path";
|
|
36743
36998
|
import os6 from "node:os";
|
|
36744
36999
|
var QUEUE_KEY_HEX_64 = /^[a-f0-9]{64}$/i;
|
|
36745
37000
|
function queueStateFileSlug(queueKey) {
|
|
@@ -36748,15 +37003,15 @@ function queueStateFileSlug(queueKey) {
|
|
|
36748
37003
|
}
|
|
36749
37004
|
function getPromptQueuesDirectory() {
|
|
36750
37005
|
const override = process.env.BUILDAMATON_PROMPT_QUEUES_DIR?.trim();
|
|
36751
|
-
if (override) return
|
|
36752
|
-
return
|
|
37006
|
+
if (override) return path30.resolve(override);
|
|
37007
|
+
return path30.join(os6.homedir(), ".buildautomaton", "queues");
|
|
36753
37008
|
}
|
|
36754
37009
|
function ensurePromptQueuesDirectory() {
|
|
36755
37010
|
const dir = getPromptQueuesDirectory();
|
|
36756
37011
|
if (!fs27.existsSync(dir)) fs27.mkdirSync(dir, { recursive: true });
|
|
36757
37012
|
}
|
|
36758
37013
|
function queueStateFilePath(queueKey) {
|
|
36759
|
-
return
|
|
37014
|
+
return path30.join(getPromptQueuesDirectory(), `${queueStateFileSlug(queueKey)}.json`);
|
|
36760
37015
|
}
|
|
36761
37016
|
|
|
36762
37017
|
// src/prompt-turn-queue/disk-store.ts
|
|
@@ -36828,6 +37083,10 @@ function hasRunningTurn(turns) {
|
|
|
36828
37083
|
}
|
|
36829
37084
|
function dispatchLocalPrompt(next, deps) {
|
|
36830
37085
|
const pl = next.payload;
|
|
37086
|
+
const rawParent = pl["sessionParent"];
|
|
37087
|
+
const sessionParent = rawParent === "bridge_root" || rawParent === "worktrees_root" ? rawParent : void 0;
|
|
37088
|
+
const rawParentPath = pl["sessionParentPath"];
|
|
37089
|
+
const sessionParentPath = typeof rawParentPath === "string" && rawParentPath.trim() !== "" ? rawParentPath.trim() : void 0;
|
|
36831
37090
|
const msg = {
|
|
36832
37091
|
type: "prompt",
|
|
36833
37092
|
sessionId: next.sessionId,
|
|
@@ -36835,6 +37094,8 @@ function dispatchLocalPrompt(next, deps) {
|
|
|
36835
37094
|
prompt: pl.prompt,
|
|
36836
37095
|
mode: typeof pl.mode === "string" ? pl.mode : "agent",
|
|
36837
37096
|
isNewSession: pl.isNewSession === true,
|
|
37097
|
+
...sessionParent ? { sessionParent } : {},
|
|
37098
|
+
...sessionParentPath ? { sessionParentPath } : {},
|
|
36838
37099
|
...typeof pl.followUpCatalogPromptId === "string" ? { followUpCatalogPromptId: pl.followUpCatalogPromptId } : {},
|
|
36839
37100
|
...Array.isArray(pl.sessionChangeSummaryFilePaths) ? { sessionChangeSummaryFilePaths: pl.sessionChangeSummaryFilePaths } : {},
|
|
36840
37101
|
...Array.isArray(pl.sessionChangeSummaryFileSnapshots) ? { sessionChangeSummaryFileSnapshots: pl.sessionChangeSummaryFileSnapshots } : {},
|
|
@@ -36946,23 +37207,24 @@ async function readGitBranch(cwd) {
|
|
|
36946
37207
|
async function runBridgePromptPreamble(params) {
|
|
36947
37208
|
const { getWs, log: log2, sessionWorktreeManager, sessionId, runId, effectiveCwd } = params;
|
|
36948
37209
|
const s = getWs();
|
|
36949
|
-
const
|
|
37210
|
+
const repoCheckoutPaths = sessionWorktreeManager.ensureRepoCheckoutPathsForSession(sessionId);
|
|
36950
37211
|
const repoRoots = await resolveSnapshotRepoRoots({
|
|
36951
|
-
worktreePaths,
|
|
37212
|
+
worktreePaths: repoCheckoutPaths,
|
|
36952
37213
|
fallbackCwd: effectiveCwd,
|
|
37214
|
+
sessionId: sessionId?.trim() || void 0,
|
|
36953
37215
|
log: log2
|
|
36954
37216
|
});
|
|
36955
37217
|
if (s && sessionId) {
|
|
36956
37218
|
const cliGitBranch = await readGitBranch(effectiveCwd);
|
|
36957
37219
|
const usesWt = sessionWorktreeManager.usesWorktreeSession(sessionId);
|
|
36958
|
-
const
|
|
37220
|
+
const isolatedSessionParentPath = sessionWorktreeManager.getIsolatedSessionParentPathForSession(sessionId);
|
|
36959
37221
|
sendWsMessage(s, {
|
|
36960
37222
|
type: "session_git_context_report",
|
|
36961
37223
|
sessionId,
|
|
36962
37224
|
cliGitBranch,
|
|
36963
37225
|
agentUsesWorktree: usesWt,
|
|
36964
37226
|
sessionParent: usesWt ? "worktrees_root" : "bridge_root",
|
|
36965
|
-
sessionParentPath: usesWt ?
|
|
37227
|
+
sessionParentPath: usesWt ? isolatedSessionParentPath ?? effectiveCwd : getBridgeRoot()
|
|
36966
37228
|
});
|
|
36967
37229
|
}
|
|
36968
37230
|
if (s && sessionId && runId) {
|
|
@@ -37089,7 +37351,7 @@ function handleBridgePrompt(msg, deps) {
|
|
|
37089
37351
|
const mode = typeof msg.mode === "string" && msg.mode.trim() ? msg.mode.trim() : void 0;
|
|
37090
37352
|
acpManager.logPromptReceivedFromBridge({ agentType, mode });
|
|
37091
37353
|
async function preambleAndPrompt(resolvedCwd) {
|
|
37092
|
-
const effectiveCwd =
|
|
37354
|
+
const effectiveCwd = resolveSessionParentPathForAgentProcess(resolvedCwd);
|
|
37093
37355
|
await runBridgePromptPreamble({
|
|
37094
37356
|
getWs,
|
|
37095
37357
|
log: log2,
|
|
@@ -37122,7 +37384,7 @@ function handleBridgePrompt(msg, deps) {
|
|
|
37122
37384
|
runId,
|
|
37123
37385
|
mode,
|
|
37124
37386
|
agentType,
|
|
37125
|
-
|
|
37387
|
+
sessionParentPath: effectiveCwd,
|
|
37126
37388
|
sendResult: sendResult2,
|
|
37127
37389
|
sendSessionUpdate,
|
|
37128
37390
|
followUpCatalogPromptId,
|
|
@@ -37132,8 +37394,8 @@ function handleBridgePrompt(msg, deps) {
|
|
|
37132
37394
|
e2ee: deps.e2ee
|
|
37133
37395
|
});
|
|
37134
37396
|
}
|
|
37135
|
-
void sessionWorktreeManager.
|
|
37136
|
-
log2(`[Agent]
|
|
37397
|
+
void sessionWorktreeManager.resolveSessionParentPathForPrompt(sessionId, { isNewSession, sessionParent, sessionParentPath }).then((cwd) => preambleAndPrompt(cwd)).catch((err) => {
|
|
37398
|
+
log2(`[Agent] Session parent path resolve failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
37137
37399
|
void preambleAndPrompt(void 0);
|
|
37138
37400
|
});
|
|
37139
37401
|
}
|
|
@@ -37267,7 +37529,7 @@ var previewSkill = {
|
|
|
37267
37529
|
const exe = parts[0];
|
|
37268
37530
|
const args = parts.slice(1);
|
|
37269
37531
|
previewProcess = spawn9(isWindows && exe === "npm" ? "npm.cmd" : exe, args, {
|
|
37270
|
-
cwd:
|
|
37532
|
+
cwd: getBridgeRoot(),
|
|
37271
37533
|
stdio: ["ignore", "pipe", "pipe"],
|
|
37272
37534
|
env: {
|
|
37273
37535
|
...process.env,
|
|
@@ -37390,7 +37652,7 @@ import path32 from "node:path";
|
|
|
37390
37652
|
|
|
37391
37653
|
// src/files/ensure-under-cwd.ts
|
|
37392
37654
|
import path31 from "node:path";
|
|
37393
|
-
function ensureUnderCwd(relativePath, cwd =
|
|
37655
|
+
function ensureUnderCwd(relativePath, cwd = getBridgeRoot()) {
|
|
37394
37656
|
const normalized = path31.normalize(relativePath).replace(/^(\.\/)+/, "");
|
|
37395
37657
|
const resolved = path31.resolve(cwd, normalized);
|
|
37396
37658
|
if (!resolved.startsWith(cwd + path31.sep) && resolved !== cwd) {
|
|
@@ -37402,7 +37664,7 @@ function ensureUnderCwd(relativePath, cwd = getBridgeWorkspaceDirectory()) {
|
|
|
37402
37664
|
// src/files/list-dir.ts
|
|
37403
37665
|
var LIST_DIR_YIELD_EVERY = 256;
|
|
37404
37666
|
async function listDirAsync(relativePath) {
|
|
37405
|
-
const resolved = ensureUnderCwd(relativePath || ".",
|
|
37667
|
+
const resolved = ensureUnderCwd(relativePath || ".", getBridgeRoot());
|
|
37406
37668
|
if (!resolved) {
|
|
37407
37669
|
return { error: "Path is outside working directory" };
|
|
37408
37670
|
}
|
|
@@ -37448,7 +37710,7 @@ async function listDirAsync(relativePath) {
|
|
|
37448
37710
|
import fs30 from "node:fs";
|
|
37449
37711
|
import { StringDecoder } from "node:string_decoder";
|
|
37450
37712
|
function resolveFilePath(relativePath) {
|
|
37451
|
-
const resolved = ensureUnderCwd(relativePath,
|
|
37713
|
+
const resolved = ensureUnderCwd(relativePath, getBridgeRoot());
|
|
37452
37714
|
if (!resolved) return { error: "Path is outside working directory" };
|
|
37453
37715
|
let real;
|
|
37454
37716
|
try {
|
|
@@ -37641,7 +37903,7 @@ function handleFileBrowserSearch(msg, socket, e2ee) {
|
|
|
37641
37903
|
void (async () => {
|
|
37642
37904
|
await yieldToEventLoop();
|
|
37643
37905
|
const q = typeof msg.q === "string" ? msg.q : "";
|
|
37644
|
-
const cwd =
|
|
37906
|
+
const cwd = getBridgeRoot();
|
|
37645
37907
|
const index = loadFileIndex(cwd);
|
|
37646
37908
|
if (index === null) {
|
|
37647
37909
|
const payload2 = {
|
|
@@ -37665,7 +37927,7 @@ function handleFileBrowserSearch(msg, socket, e2ee) {
|
|
|
37665
37927
|
}
|
|
37666
37928
|
function triggerFileIndexBuild() {
|
|
37667
37929
|
setImmediate(() => {
|
|
37668
|
-
void ensureFileIndexAsync(
|
|
37930
|
+
void ensureFileIndexAsync(getBridgeRoot()).catch((e) => {
|
|
37669
37931
|
console.error("[file-index] Background build failed:", e);
|
|
37670
37932
|
});
|
|
37671
37933
|
});
|
|
@@ -37734,7 +37996,7 @@ function handleFileBrowserSearchMessage(msg, { getWs, e2ee }) {
|
|
|
37734
37996
|
function handleSkillLayoutRequest(msg, deps) {
|
|
37735
37997
|
const socket = deps.getWs();
|
|
37736
37998
|
const id = typeof msg.id === "string" ? msg.id : "";
|
|
37737
|
-
const roots = discoverSkillLayoutRoots(
|
|
37999
|
+
const roots = discoverSkillLayoutRoots(getBridgeRoot());
|
|
37738
38000
|
if (socket) {
|
|
37739
38001
|
sendWsMessage(socket, { type: "skill_layout_response", id, roots });
|
|
37740
38002
|
}
|
|
@@ -37782,7 +38044,7 @@ var handleInstallSkillsMessage = (msg, deps) => {
|
|
|
37782
38044
|
const id = typeof msg.id === "string" ? msg.id : "";
|
|
37783
38045
|
const targetDir = typeof msg.targetDir === "string" && msg.targetDir.trim() ? msg.targetDir.trim() : ".agents/skills";
|
|
37784
38046
|
const rawItems = msg.items;
|
|
37785
|
-
const cwd =
|
|
38047
|
+
const cwd = getBridgeRoot();
|
|
37786
38048
|
const result = installRemoteSkills(cwd, targetDir, rawItems);
|
|
37787
38049
|
if (!result.success) {
|
|
37788
38050
|
const err = result.error ?? "Invalid items";
|
|
@@ -37921,7 +38183,7 @@ var handleRevertTurnSnapshotMessage = (msg, deps) => {
|
|
|
37921
38183
|
void (async () => {
|
|
37922
38184
|
const s = getWs();
|
|
37923
38185
|
if (!s) return;
|
|
37924
|
-
const agentBase = sessionWorktreeManager.
|
|
38186
|
+
const agentBase = sessionWorktreeManager.getSessionWorktreeRootForSession(sessionId) ?? getBridgeRoot();
|
|
37925
38187
|
const file2 = snapshotFilePath(agentBase, turnId);
|
|
37926
38188
|
if (!fs32.existsSync(file2)) {
|
|
37927
38189
|
sendWsMessage(s, {
|
|
@@ -38213,9 +38475,9 @@ async function createBridgeConnection(options) {
|
|
|
38213
38475
|
firehoseGeneration: 0,
|
|
38214
38476
|
firehoseQuiet: createEmptyReconnectQuietSlot()
|
|
38215
38477
|
};
|
|
38216
|
-
const
|
|
38478
|
+
const worktreesRootPath = options.worktreesRootPath ?? defaultWorktreesRootPath();
|
|
38217
38479
|
const sessionWorktreeManager = new SessionWorktreeManager({
|
|
38218
|
-
|
|
38480
|
+
worktreesRootPath,
|
|
38219
38481
|
log: logFn
|
|
38220
38482
|
});
|
|
38221
38483
|
const acpManager = await createAcpManager({ log: logFn });
|
|
@@ -38224,7 +38486,7 @@ async function createBridgeConnection(options) {
|
|
|
38224
38486
|
return state.currentWs;
|
|
38225
38487
|
}
|
|
38226
38488
|
const e2ee = options.e2eCertificate ? createCliE2eeRuntime(options.e2eCertificate) : void 0;
|
|
38227
|
-
const devServerManager = new DevServerManager({ getWs, log: logFn,
|
|
38489
|
+
const devServerManager = new DevServerManager({ getWs, log: logFn, getBridgeRoot, e2ee });
|
|
38228
38490
|
const onBridgeIdentified = createOnBridgeIdentified({
|
|
38229
38491
|
devServerManager,
|
|
38230
38492
|
firehoseServerUrl,
|
|
@@ -38248,8 +38510,8 @@ async function createBridgeConnection(options) {
|
|
|
38248
38510
|
getCloudAccessToken: () => tokens.accessToken
|
|
38249
38511
|
};
|
|
38250
38512
|
const identifyReportedPaths = {
|
|
38251
|
-
bridgeRootPath: path34.resolve(
|
|
38252
|
-
worktreesRootPath: path34.resolve(
|
|
38513
|
+
bridgeRootPath: path34.resolve(getBridgeRoot()),
|
|
38514
|
+
worktreesRootPath: path34.resolve(worktreesRootPath)
|
|
38253
38515
|
};
|
|
38254
38516
|
const { connect } = createMainBridgeWebSocketLifecycle({
|
|
38255
38517
|
state,
|
|
@@ -38266,7 +38528,7 @@ async function createBridgeConnection(options) {
|
|
|
38266
38528
|
identifyReportedPaths
|
|
38267
38529
|
});
|
|
38268
38530
|
connect();
|
|
38269
|
-
const stopFileIndexWatcher = startFileIndexWatcher(
|
|
38531
|
+
const stopFileIndexWatcher = startFileIndexWatcher(getBridgeRoot());
|
|
38270
38532
|
return {
|
|
38271
38533
|
close: async () => {
|
|
38272
38534
|
stopFileIndexWatcher();
|
|
@@ -38336,7 +38598,7 @@ async function runConnectedBridge(options, restartWithoutAuth) {
|
|
|
38336
38598
|
authToken,
|
|
38337
38599
|
refreshToken,
|
|
38338
38600
|
justAuthenticated,
|
|
38339
|
-
|
|
38601
|
+
worktreesRootPath,
|
|
38340
38602
|
e2eCertificate
|
|
38341
38603
|
} = options;
|
|
38342
38604
|
const firehoseServerUrl = options.firehoseServerUrl ?? options.proxyServerUrl;
|
|
@@ -38348,7 +38610,7 @@ async function runConnectedBridge(options, restartWithoutAuth) {
|
|
|
38348
38610
|
refreshToken,
|
|
38349
38611
|
firehoseServerUrl,
|
|
38350
38612
|
justAuthenticated,
|
|
38351
|
-
|
|
38613
|
+
worktreesRootPath,
|
|
38352
38614
|
e2eCertificate,
|
|
38353
38615
|
log,
|
|
38354
38616
|
persistTokens: (t) => {
|
|
@@ -38363,7 +38625,7 @@ async function runConnectedBridge(options, restartWithoutAuth) {
|
|
|
38363
38625
|
log("[Bridge service] Access token invalid or revoked; re-authenticating\u2026");
|
|
38364
38626
|
clearConfigForApi(apiUrl);
|
|
38365
38627
|
void handle.close().then(() => {
|
|
38366
|
-
void restartWithoutAuth({ apiUrl, firehoseServerUrl,
|
|
38628
|
+
void restartWithoutAuth({ apiUrl, firehoseServerUrl, worktreesRootPath, e2eCertificate });
|
|
38367
38629
|
});
|
|
38368
38630
|
}
|
|
38369
38631
|
});
|
|
@@ -38411,7 +38673,7 @@ async function runBridge(options) {
|
|
|
38411
38673
|
workspaceId,
|
|
38412
38674
|
authToken,
|
|
38413
38675
|
bridgeName,
|
|
38414
|
-
|
|
38676
|
+
worktreesRootPath,
|
|
38415
38677
|
e2eCertificate
|
|
38416
38678
|
} = options;
|
|
38417
38679
|
const firehoseServerUrl = options.firehoseServerUrl ?? options.proxyServerUrl;
|
|
@@ -38456,7 +38718,7 @@ async function runBridge(options) {
|
|
|
38456
38718
|
firehoseServerUrl,
|
|
38457
38719
|
bridgeName,
|
|
38458
38720
|
justAuthenticated: true,
|
|
38459
|
-
|
|
38721
|
+
worktreesRootPath,
|
|
38460
38722
|
e2eCertificate
|
|
38461
38723
|
});
|
|
38462
38724
|
return;
|
|
@@ -38480,30 +38742,31 @@ async function runCliAction(program2, opts) {
|
|
|
38480
38742
|
let workspaceId = opts.workspaceId ?? "";
|
|
38481
38743
|
let authToken = opts.token;
|
|
38482
38744
|
const firehoseServerUrl = opts.firehoseUrl ?? opts.proxyUrl ?? process.env.BUILDAUTOMATON_FIREHOSE_URL ?? process.env.BUILDAUTOMATON_PROXY_URL ?? DEFAULT_FIREHOSE_URL;
|
|
38483
|
-
|
|
38484
|
-
|
|
38745
|
+
const bridgeRootOpt = (opts.bridgeRoot && typeof opts.bridgeRoot === "string" && opts.bridgeRoot.trim() ? opts.bridgeRoot.trim() : null) ?? (opts.cwd && typeof opts.cwd === "string" && opts.cwd.trim() ? opts.cwd.trim() : null);
|
|
38746
|
+
if (bridgeRootOpt) {
|
|
38747
|
+
const resolvedBridgeRoot = path35.resolve(process.cwd(), bridgeRootOpt);
|
|
38485
38748
|
try {
|
|
38486
|
-
const st = fs33.statSync(
|
|
38749
|
+
const st = fs33.statSync(resolvedBridgeRoot);
|
|
38487
38750
|
if (!st.isDirectory()) {
|
|
38488
|
-
console.error(
|
|
38751
|
+
console.error(`Bridge root is not a directory: ${resolvedBridgeRoot}`);
|
|
38489
38752
|
process.exit(1);
|
|
38490
38753
|
}
|
|
38491
38754
|
} catch {
|
|
38492
|
-
console.error(
|
|
38755
|
+
console.error(`Bridge root path does not exist or is not accessible: ${resolvedBridgeRoot}`);
|
|
38493
38756
|
process.exit(1);
|
|
38494
38757
|
}
|
|
38495
|
-
process.chdir(
|
|
38758
|
+
process.chdir(resolvedBridgeRoot);
|
|
38496
38759
|
}
|
|
38497
|
-
|
|
38760
|
+
initBridgeRoot();
|
|
38498
38761
|
console.log(
|
|
38499
38762
|
colorize(
|
|
38500
38763
|
GOLDEN_YELLOW,
|
|
38501
|
-
`[
|
|
38764
|
+
`[Bridge] Using ${getBridgeRoot()} as the bridge root (repos, file index, agents, and bridge_root sessions).`
|
|
38502
38765
|
)
|
|
38503
38766
|
);
|
|
38504
|
-
let
|
|
38767
|
+
let worktreesRootPath;
|
|
38505
38768
|
if (opts.worktreesRoot && opts.worktreesRoot.trim()) {
|
|
38506
|
-
|
|
38769
|
+
worktreesRootPath = path35.resolve(opts.worktreesRoot.trim());
|
|
38507
38770
|
}
|
|
38508
38771
|
const e2eCertificates = opts.e2eeCertificatesDir?.trim() ? await loadOrCreateE2eCertificates(opts.e2eeCertificatesDir.trim()) : void 0;
|
|
38509
38772
|
if (e2eCertificates) {
|
|
@@ -38538,7 +38801,7 @@ async function runCliAction(program2, opts) {
|
|
|
38538
38801
|
refreshToken,
|
|
38539
38802
|
firehoseServerUrl,
|
|
38540
38803
|
bridgeName: opts.name?.trim() || void 0,
|
|
38541
|
-
|
|
38804
|
+
worktreesRootPath,
|
|
38542
38805
|
e2eCertificate: e2eCertificates?.activeCertificate
|
|
38543
38806
|
});
|
|
38544
38807
|
}
|
|
@@ -38551,8 +38814,11 @@ async function main() {
|
|
|
38551
38814
|
"Firehose server URL (default: Fly app; or BUILDAUTOMATON_FIREHOSE_URL / legacy BUILDAUTOMATON_PROXY_URL)",
|
|
38552
38815
|
process.env.BUILDAUTOMATON_FIREHOSE_URL ?? process.env.BUILDAUTOMATON_PROXY_URL ?? DEFAULT_FIREHOSE_URL
|
|
38553
38816
|
).option("--proxy-url <url>", "Deprecated alias for --firehose-url", void 0).option(
|
|
38817
|
+
"--bridge-root <path>",
|
|
38818
|
+
"Bridge root directory (absolute or relative to the current directory): git repos, skills, file index, and agent cwd for bridge_root sessions"
|
|
38819
|
+
).option(
|
|
38554
38820
|
"--cwd <path>",
|
|
38555
|
-
"
|
|
38821
|
+
"Deprecated: same as --bridge-root"
|
|
38556
38822
|
).option("-n, --name <name>", "Bridge name when creating via browser (alphanumeric and underscores only)").option(
|
|
38557
38823
|
"--worktrees-root <path>",
|
|
38558
38824
|
"Root directory for per-session git worktrees (default: ~/.buildautomaton/worktrees)."
|