@buildautomaton/cli 0.1.19 → 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 +945 -543
- package/dist/cli.js.map +4 -4
- package/dist/index.js +912 -514
- package/dist/index.js.map +4 -4
- package/package.json +5 -4
package/dist/cli.js
CHANGED
|
@@ -974,7 +974,7 @@ var require_command = __commonJS({
|
|
|
974
974
|
var EventEmitter2 = __require("node:events").EventEmitter;
|
|
975
975
|
var childProcess2 = __require("node:child_process");
|
|
976
976
|
var path36 = __require("node:path");
|
|
977
|
-
var
|
|
977
|
+
var fs34 = __require("node:fs");
|
|
978
978
|
var process8 = __require("node:process");
|
|
979
979
|
var { Argument: Argument2, humanReadableArgName } = require_argument();
|
|
980
980
|
var { CommanderError: CommanderError2 } = require_error();
|
|
@@ -1907,10 +1907,10 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1907
1907
|
const sourceExt = [".js", ".ts", ".tsx", ".mjs", ".cjs"];
|
|
1908
1908
|
function findFile(baseDir, baseName) {
|
|
1909
1909
|
const localBin = path36.resolve(baseDir, baseName);
|
|
1910
|
-
if (
|
|
1910
|
+
if (fs34.existsSync(localBin)) return localBin;
|
|
1911
1911
|
if (sourceExt.includes(path36.extname(baseName))) return void 0;
|
|
1912
1912
|
const foundExt = sourceExt.find(
|
|
1913
|
-
(ext) =>
|
|
1913
|
+
(ext) => fs34.existsSync(`${localBin}${ext}`)
|
|
1914
1914
|
);
|
|
1915
1915
|
if (foundExt) return `${localBin}${foundExt}`;
|
|
1916
1916
|
return void 0;
|
|
@@ -1922,7 +1922,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1922
1922
|
if (this._scriptPath) {
|
|
1923
1923
|
let resolvedScriptPath;
|
|
1924
1924
|
try {
|
|
1925
|
-
resolvedScriptPath =
|
|
1925
|
+
resolvedScriptPath = fs34.realpathSync(this._scriptPath);
|
|
1926
1926
|
} catch (err) {
|
|
1927
1927
|
resolvedScriptPath = this._scriptPath;
|
|
1928
1928
|
}
|
|
@@ -5236,7 +5236,7 @@ var require_websocket = __commonJS({
|
|
|
5236
5236
|
var http = __require("http");
|
|
5237
5237
|
var net = __require("net");
|
|
5238
5238
|
var tls = __require("tls");
|
|
5239
|
-
var { randomBytes: randomBytes3, createHash:
|
|
5239
|
+
var { randomBytes: randomBytes3, createHash: createHash2 } = __require("crypto");
|
|
5240
5240
|
var { Duplex, Readable: Readable2 } = __require("stream");
|
|
5241
5241
|
var { URL: URL2 } = __require("url");
|
|
5242
5242
|
var PerMessageDeflate = require_permessage_deflate();
|
|
@@ -5896,7 +5896,7 @@ var require_websocket = __commonJS({
|
|
|
5896
5896
|
abortHandshake(websocket, socket, "Invalid Upgrade header");
|
|
5897
5897
|
return;
|
|
5898
5898
|
}
|
|
5899
|
-
const digest =
|
|
5899
|
+
const digest = createHash2("sha1").update(key + GUID).digest("base64");
|
|
5900
5900
|
if (res.headers["sec-websocket-accept"] !== digest) {
|
|
5901
5901
|
abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
|
|
5902
5902
|
return;
|
|
@@ -6263,7 +6263,7 @@ var require_websocket_server = __commonJS({
|
|
|
6263
6263
|
var EventEmitter2 = __require("events");
|
|
6264
6264
|
var http = __require("http");
|
|
6265
6265
|
var { Duplex } = __require("stream");
|
|
6266
|
-
var { createHash:
|
|
6266
|
+
var { createHash: createHash2 } = __require("crypto");
|
|
6267
6267
|
var extension = require_extension();
|
|
6268
6268
|
var PerMessageDeflate = require_permessage_deflate();
|
|
6269
6269
|
var subprotocol = require_subprotocol();
|
|
@@ -6564,7 +6564,7 @@ var require_websocket_server = __commonJS({
|
|
|
6564
6564
|
);
|
|
6565
6565
|
}
|
|
6566
6566
|
if (this._state > RUNNING) return abortHandshake(socket, 503);
|
|
6567
|
-
const digest =
|
|
6567
|
+
const digest = createHash2("sha1").update(key + GUID).digest("base64");
|
|
6568
6568
|
const headers = [
|
|
6569
6569
|
"HTTP/1.1 101 Switching Protocols",
|
|
6570
6570
|
"Upgrade: websocket",
|
|
@@ -7129,7 +7129,7 @@ var require_has_flag = __commonJS({
|
|
|
7129
7129
|
var require_supports_color = __commonJS({
|
|
7130
7130
|
"../../node_modules/.pnpm/supports-color@7.2.0/node_modules/supports-color/index.js"(exports, module) {
|
|
7131
7131
|
"use strict";
|
|
7132
|
-
var
|
|
7132
|
+
var os7 = __require("os");
|
|
7133
7133
|
var tty = __require("tty");
|
|
7134
7134
|
var hasFlag = require_has_flag();
|
|
7135
7135
|
var { env } = process;
|
|
@@ -7177,7 +7177,7 @@ var require_supports_color = __commonJS({
|
|
|
7177
7177
|
return min;
|
|
7178
7178
|
}
|
|
7179
7179
|
if (process.platform === "win32") {
|
|
7180
|
-
const osRelease =
|
|
7180
|
+
const osRelease = os7.release().split(".");
|
|
7181
7181
|
if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) {
|
|
7182
7182
|
return Number(osRelease[2]) >= 14931 ? 3 : 2;
|
|
7183
7183
|
}
|
|
@@ -24800,8 +24800,8 @@ var init_acp = __esm({
|
|
|
24800
24800
|
this.#requestHandler = requestHandler;
|
|
24801
24801
|
this.#notificationHandler = notificationHandler;
|
|
24802
24802
|
this.#stream = stream;
|
|
24803
|
-
this.#closedPromise = new Promise((
|
|
24804
|
-
this.#abortController.signal.addEventListener("abort", () =>
|
|
24803
|
+
this.#closedPromise = new Promise((resolve18) => {
|
|
24804
|
+
this.#abortController.signal.addEventListener("abort", () => resolve18());
|
|
24805
24805
|
});
|
|
24806
24806
|
this.#receive();
|
|
24807
24807
|
}
|
|
@@ -24950,8 +24950,8 @@ var init_acp = __esm({
|
|
|
24950
24950
|
}
|
|
24951
24951
|
async sendRequest(method, params) {
|
|
24952
24952
|
const id = this.#nextRequestId++;
|
|
24953
|
-
const responsePromise = new Promise((
|
|
24954
|
-
this.#pendingResponses.set(id, { resolve:
|
|
24953
|
+
const responsePromise = new Promise((resolve18, reject) => {
|
|
24954
|
+
this.#pendingResponses.set(id, { resolve: resolve18, reject });
|
|
24955
24955
|
});
|
|
24956
24956
|
await this.#sendMessage({ jsonrpc: "2.0", id, method, params });
|
|
24957
24957
|
return responsePromise;
|
|
@@ -25064,14 +25064,14 @@ 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";
|
|
25071
25071
|
var DEFAULT_FIREHOSE_URL = "https://buildautomaton-firehose.fly.dev";
|
|
25072
25072
|
|
|
25073
25073
|
// src/cli/run-cli-action.ts
|
|
25074
|
-
import * as
|
|
25074
|
+
import * as fs33 from "node:fs";
|
|
25075
25075
|
import * as path35 from "node:path";
|
|
25076
25076
|
|
|
25077
25077
|
// src/config.ts
|
|
@@ -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
|
|
@@ -26037,14 +26037,14 @@ var baseOpen = async (options) => {
|
|
|
26037
26037
|
}
|
|
26038
26038
|
const subprocess = childProcess.spawn(command, cliArguments, childProcessOptions);
|
|
26039
26039
|
if (options.wait) {
|
|
26040
|
-
return new Promise((
|
|
26040
|
+
return new Promise((resolve18, reject) => {
|
|
26041
26041
|
subprocess.once("error", reject);
|
|
26042
26042
|
subprocess.once("close", (exitCode) => {
|
|
26043
26043
|
if (!options.allowNonzeroExitCode && exitCode > 0) {
|
|
26044
26044
|
reject(new Error(`Exited with code ${exitCode}`));
|
|
26045
26045
|
return;
|
|
26046
26046
|
}
|
|
26047
|
-
|
|
26047
|
+
resolve18(subprocess);
|
|
26048
26048
|
});
|
|
26049
26049
|
});
|
|
26050
26050
|
}
|
|
@@ -26340,8 +26340,8 @@ function runPendingAuth(options) {
|
|
|
26340
26340
|
let hasOpenedBrowser = false;
|
|
26341
26341
|
let resolved = false;
|
|
26342
26342
|
let resolveAuth;
|
|
26343
|
-
const authPromise = new Promise((
|
|
26344
|
-
resolveAuth =
|
|
26343
|
+
const authPromise = new Promise((resolve18) => {
|
|
26344
|
+
resolveAuth = resolve18;
|
|
26345
26345
|
});
|
|
26346
26346
|
let reconnectAttempt = 0;
|
|
26347
26347
|
const signInQuiet = createEmptyReconnectQuietSlot();
|
|
@@ -26463,7 +26463,7 @@ function runPendingAuth(options) {
|
|
|
26463
26463
|
async function closeBridgeConnection(state, acpManager, devServerManager, log2) {
|
|
26464
26464
|
const say = log2 ?? logImmediate;
|
|
26465
26465
|
say("Cleaning up connections\u2026");
|
|
26466
|
-
await new Promise((
|
|
26466
|
+
await new Promise((resolve18) => setImmediate(resolve18));
|
|
26467
26467
|
state.closedByUser = true;
|
|
26468
26468
|
clearReconnectQuietTimer(state.mainQuiet);
|
|
26469
26469
|
clearReconnectQuietTimer(state.firehoseQuiet);
|
|
@@ -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,
|
|
@@ -31367,18 +31388,7 @@ async function collectTurnGitDiffFromPreTurnSnapshot(options) {
|
|
|
31367
31388
|
}
|
|
31368
31389
|
}
|
|
31369
31390
|
|
|
31370
|
-
// ../types/
|
|
31371
|
-
init_zod();
|
|
31372
|
-
init_zod();
|
|
31373
|
-
init_zod();
|
|
31374
|
-
init_zod();
|
|
31375
|
-
init_zod();
|
|
31376
|
-
init_zod();
|
|
31377
|
-
init_zod();
|
|
31378
|
-
init_zod();
|
|
31379
|
-
init_zod();
|
|
31380
|
-
init_zod();
|
|
31381
|
-
init_zod();
|
|
31391
|
+
// ../types/src/work-items.ts
|
|
31382
31392
|
init_zod();
|
|
31383
31393
|
var WorkItemStatusSchema = external_exports.enum(["backlog", "in-progress", "completed"]);
|
|
31384
31394
|
var WorkItemProgressSchema = external_exports.object({
|
|
@@ -31419,6 +31429,9 @@ var WorkItemSchema = external_exports.object({
|
|
|
31419
31429
|
dependencies: external_exports.array(WorkItemDependencySchema).default([]),
|
|
31420
31430
|
assignedToUserId: external_exports.string().optional()
|
|
31421
31431
|
});
|
|
31432
|
+
|
|
31433
|
+
// ../types/src/user-profiles.ts
|
|
31434
|
+
init_zod();
|
|
31422
31435
|
var UserWorkspaceProfileSchema = external_exports.object({
|
|
31423
31436
|
id: external_exports.string(),
|
|
31424
31437
|
workspaceId: external_exports.string(),
|
|
@@ -31428,6 +31441,9 @@ var UserWorkspaceProfileSchema = external_exports.object({
|
|
|
31428
31441
|
preferences: external_exports.record(external_exports.unknown()).optional(),
|
|
31429
31442
|
learnings: external_exports.array(external_exports.string())
|
|
31430
31443
|
});
|
|
31444
|
+
|
|
31445
|
+
// ../types/src/runtime.ts
|
|
31446
|
+
init_zod();
|
|
31431
31447
|
var WorkspaceOwnerInfoSchema = external_exports.object({
|
|
31432
31448
|
ownerId: external_exports.string(),
|
|
31433
31449
|
ownerName: external_exports.string().optional(),
|
|
@@ -31441,6 +31457,9 @@ var WorkspaceRuntimeEntrySchema = external_exports.object({
|
|
|
31441
31457
|
owner: WorkspaceOwnerInfoSchema.optional(),
|
|
31442
31458
|
isOwner: external_exports.boolean().optional()
|
|
31443
31459
|
});
|
|
31460
|
+
|
|
31461
|
+
// ../types/src/agent.ts
|
|
31462
|
+
init_zod();
|
|
31444
31463
|
var ProjectContextSchema = external_exports.object({
|
|
31445
31464
|
projectId: external_exports.string(),
|
|
31446
31465
|
context: external_exports.record(external_exports.unknown()).default({}),
|
|
@@ -31465,6 +31484,9 @@ var WebSocketMessageSchema = external_exports.object({
|
|
|
31465
31484
|
data: external_exports.any().optional(),
|
|
31466
31485
|
error: external_exports.string().optional()
|
|
31467
31486
|
});
|
|
31487
|
+
|
|
31488
|
+
// ../types/src/checkpoints.ts
|
|
31489
|
+
init_zod();
|
|
31468
31490
|
var CheckpointKindSchema = external_exports.enum(["daily", "weekly", "overall"]);
|
|
31469
31491
|
var CheckpointSummarySchema = external_exports.object({
|
|
31470
31492
|
id: external_exports.string(),
|
|
@@ -31475,6 +31497,9 @@ var CheckpointSummarySchema = external_exports.object({
|
|
|
31475
31497
|
createdAt: external_exports.string(),
|
|
31476
31498
|
updatedAt: external_exports.string()
|
|
31477
31499
|
});
|
|
31500
|
+
|
|
31501
|
+
// ../types/src/threads.ts
|
|
31502
|
+
init_zod();
|
|
31478
31503
|
var ThreadMetaSchema = external_exports.object({
|
|
31479
31504
|
threadId: external_exports.string(),
|
|
31480
31505
|
workspaceId: external_exports.string(),
|
|
@@ -31502,6 +31527,9 @@ var ThreadMessageSchema = external_exports.object({
|
|
|
31502
31527
|
var ThreadCheckpointSummarySchema = CheckpointSummarySchema.extend({
|
|
31503
31528
|
threadId: external_exports.string()
|
|
31504
31529
|
});
|
|
31530
|
+
|
|
31531
|
+
// ../types/src/content-items.ts
|
|
31532
|
+
init_zod();
|
|
31505
31533
|
var ContentSourceSchema = external_exports.enum(["notion", "doc", "slack_thread", "other"]);
|
|
31506
31534
|
var ContentItemMetaSchema = external_exports.object({
|
|
31507
31535
|
contentId: external_exports.string(),
|
|
@@ -31523,6 +31551,9 @@ var ContentStorageRefSchema = external_exports.object({
|
|
|
31523
31551
|
var ContentCheckpointSummarySchema = CheckpointSummarySchema.extend({
|
|
31524
31552
|
contentId: external_exports.string()
|
|
31525
31553
|
});
|
|
31554
|
+
|
|
31555
|
+
// ../types/src/stories.ts
|
|
31556
|
+
init_zod();
|
|
31526
31557
|
var StoryMetaSchema = external_exports.object({
|
|
31527
31558
|
storyId: external_exports.string(),
|
|
31528
31559
|
workspaceId: external_exports.string(),
|
|
@@ -31545,6 +31576,9 @@ var StoryContentItemRefSchema = external_exports.object({
|
|
|
31545
31576
|
var StoryCheckpointSummarySchema = CheckpointSummarySchema.extend({
|
|
31546
31577
|
storyId: external_exports.string()
|
|
31547
31578
|
});
|
|
31579
|
+
|
|
31580
|
+
// ../types/src/sessions.ts
|
|
31581
|
+
init_zod();
|
|
31548
31582
|
var BUILTIN_SESSION_CHANGE_SUMMARY_FOLLOW_UP_CATALOG_PROMPT_ID = "__builtin_change_summary__";
|
|
31549
31583
|
var SessionMetaSchema = external_exports.object({
|
|
31550
31584
|
sessionId: external_exports.string(),
|
|
@@ -31587,6 +31621,8 @@ var SessionThreadRefSchema = external_exports.object({
|
|
|
31587
31621
|
threadId: external_exports.string(),
|
|
31588
31622
|
addedAt: external_exports.string()
|
|
31589
31623
|
});
|
|
31624
|
+
|
|
31625
|
+
// ../types/src/change-summary-path.ts
|
|
31590
31626
|
function normalizeRepoRelativePath(p) {
|
|
31591
31627
|
let t = p.trim().replace(/\\/g, "/");
|
|
31592
31628
|
while (t.startsWith("./")) t = t.slice(2);
|
|
@@ -31603,6 +31639,8 @@ function resolveChangeSummaryPathAgainstAllowed(rawPath, allowed) {
|
|
|
31603
31639
|
}
|
|
31604
31640
|
return null;
|
|
31605
31641
|
}
|
|
31642
|
+
|
|
31643
|
+
// ../types/src/parse-change-summary-json.ts
|
|
31606
31644
|
function clampSummaryToAtMostTwoLines(summary) {
|
|
31607
31645
|
const lines = summary.split(/\r?\n/).map((l) => l.trim()).filter((l) => l.length > 0);
|
|
31608
31646
|
return lines.slice(0, 2).join("\n");
|
|
@@ -31645,6 +31683,8 @@ function parseChangeSummaryJson(raw, allowedPaths, options) {
|
|
|
31645
31683
|
}
|
|
31646
31684
|
return rows;
|
|
31647
31685
|
}
|
|
31686
|
+
|
|
31687
|
+
// ../types/src/build-change-summary-prompt.ts
|
|
31648
31688
|
var PATCH_PREVIEW_MAX = 12e3;
|
|
31649
31689
|
function clip(s, max) {
|
|
31650
31690
|
if (s.length <= max) return s;
|
|
@@ -31702,6 +31742,8 @@ function buildSessionChangeSummaryPrompt(files) {
|
|
|
31702
31742
|
}
|
|
31703
31743
|
return lines.join("\n");
|
|
31704
31744
|
}
|
|
31745
|
+
|
|
31746
|
+
// ../types/src/dedupe-session-file-changes-by-path.ts
|
|
31705
31747
|
function defaultRichness(c) {
|
|
31706
31748
|
const patch = typeof c.patchContent === "string" ? c.patchContent.length : 0;
|
|
31707
31749
|
const nt = typeof c.newText === "string" ? c.newText.length : 0;
|
|
@@ -31719,6 +31761,9 @@ function dedupeSessionFileChangesByPath(items, richness = (item) => defaultRichn
|
|
|
31719
31761
|
}
|
|
31720
31762
|
return Array.from(byPath.entries()).sort(([a], [b]) => a.localeCompare(b)).map(([, v]) => v);
|
|
31721
31763
|
}
|
|
31764
|
+
|
|
31765
|
+
// ../types/src/artifacts.ts
|
|
31766
|
+
init_zod();
|
|
31722
31767
|
var ArtifactMetaSchema = external_exports.object({
|
|
31723
31768
|
artifactId: external_exports.string(),
|
|
31724
31769
|
workspaceId: external_exports.string(),
|
|
@@ -31732,6 +31777,9 @@ var ArtifactMetaSchema = external_exports.object({
|
|
|
31732
31777
|
createdAt: external_exports.string(),
|
|
31733
31778
|
updatedAt: external_exports.string()
|
|
31734
31779
|
});
|
|
31780
|
+
|
|
31781
|
+
// ../types/src/templates.ts
|
|
31782
|
+
init_zod();
|
|
31735
31783
|
var TemplateMetaSchema = external_exports.object({
|
|
31736
31784
|
templateId: external_exports.string(),
|
|
31737
31785
|
workspaceId: external_exports.string(),
|
|
@@ -31741,6 +31789,9 @@ var TemplateMetaSchema = external_exports.object({
|
|
|
31741
31789
|
createdAt: external_exports.string(),
|
|
31742
31790
|
updatedAt: external_exports.string()
|
|
31743
31791
|
});
|
|
31792
|
+
|
|
31793
|
+
// ../types/src/git-repos.ts
|
|
31794
|
+
init_zod();
|
|
31744
31795
|
var GitRepoMetaSchema = external_exports.object({
|
|
31745
31796
|
/** Stable id for the repo (e.g. hash of normalized canonical URL). Used for DO idFromName. */
|
|
31746
31797
|
repoId: external_exports.string(),
|
|
@@ -31934,7 +31985,7 @@ async function sendPromptToAgent(options) {
|
|
|
31934
31985
|
|
|
31935
31986
|
// src/agents/acp/ensure-acp-client.ts
|
|
31936
31987
|
import * as fs10 from "node:fs";
|
|
31937
|
-
import * as
|
|
31988
|
+
import * as path12 from "node:path";
|
|
31938
31989
|
|
|
31939
31990
|
// src/error-message.ts
|
|
31940
31991
|
function errorMessage(err) {
|
|
@@ -31973,7 +32024,7 @@ async function isCommandOnPath(command, timeoutMs = 4e3) {
|
|
|
31973
32024
|
// src/agents/acp/clients/sdk-stdio-acp-client.ts
|
|
31974
32025
|
import { spawn as spawn2 } from "node:child_process";
|
|
31975
32026
|
import { mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
31976
|
-
import { dirname } from "node:path";
|
|
32027
|
+
import { dirname as dirname2 } from "node:path";
|
|
31977
32028
|
import { Readable, Writable } from "node:stream";
|
|
31978
32029
|
|
|
31979
32030
|
// src/files/diff/unified-diff.ts
|
|
@@ -32023,21 +32074,21 @@ function editSnippetToUnifiedDiff(filePath, oldText, newText) {
|
|
|
32023
32074
|
}
|
|
32024
32075
|
|
|
32025
32076
|
// src/agents/acp/safe-fs-path.ts
|
|
32026
|
-
import * as
|
|
32077
|
+
import * as path9 from "node:path";
|
|
32027
32078
|
function resolveSafePathUnderCwd(cwd, filePath) {
|
|
32028
32079
|
const trimmed2 = filePath.trim();
|
|
32029
32080
|
if (!trimmed2) return null;
|
|
32030
|
-
const normalizedCwd =
|
|
32031
|
-
const resolved =
|
|
32032
|
-
const rel =
|
|
32033
|
-
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;
|
|
32034
32085
|
return resolved;
|
|
32035
32086
|
}
|
|
32036
32087
|
function toDisplayPathRelativeToCwd(cwd, absolutePath) {
|
|
32037
|
-
const normalizedCwd =
|
|
32038
|
-
const rel =
|
|
32039
|
-
if (!rel || rel === "") return
|
|
32040
|
-
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("/");
|
|
32041
32092
|
}
|
|
32042
32093
|
|
|
32043
32094
|
// src/agents/acp/clients/agent-stderr-capture.ts
|
|
@@ -32117,6 +32168,52 @@ function createSdkStdioExtNotificationHandler(options) {
|
|
|
32117
32168
|
}
|
|
32118
32169
|
}
|
|
32119
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
|
+
|
|
32120
32217
|
// src/agents/acp/clients/sdk-stdio-acp-client.ts
|
|
32121
32218
|
function formatSpawnError(err, command) {
|
|
32122
32219
|
if (err.code === "ENOENT") {
|
|
@@ -32138,7 +32235,7 @@ async function createSdkStdioAcpClient(options) {
|
|
|
32138
32235
|
const { ClientSideConnection: ClientSideConnection2, ndJsonStream: ndJsonStream2, PROTOCOL_VERSION: PROTOCOL_VERSION2 } = await Promise.resolve().then(() => (init_acp(), acp_exports));
|
|
32139
32236
|
const {
|
|
32140
32237
|
command,
|
|
32141
|
-
cwd =
|
|
32238
|
+
cwd = getBridgeRoot(),
|
|
32142
32239
|
backendAgentType,
|
|
32143
32240
|
onSessionUpdate,
|
|
32144
32241
|
onRequest,
|
|
@@ -32157,7 +32254,7 @@ async function createSdkStdioAcpClient(options) {
|
|
|
32157
32254
|
child.once("close", (code, signal) => {
|
|
32158
32255
|
onAgentSubprocessExit?.({ code, signal });
|
|
32159
32256
|
});
|
|
32160
|
-
return new Promise((
|
|
32257
|
+
return new Promise((resolve18, reject) => {
|
|
32161
32258
|
let initSettled = false;
|
|
32162
32259
|
const settleReject = (err) => {
|
|
32163
32260
|
if (initSettled) return;
|
|
@@ -32171,7 +32268,7 @@ async function createSdkStdioAcpClient(options) {
|
|
|
32171
32268
|
const settleResolve = (handle) => {
|
|
32172
32269
|
if (initSettled) return;
|
|
32173
32270
|
initSettled = true;
|
|
32174
|
-
|
|
32271
|
+
resolve18(handle);
|
|
32175
32272
|
};
|
|
32176
32273
|
child.on("error", (err) => {
|
|
32177
32274
|
settleReject(new Error(formatSpawnError(err, command[0])));
|
|
@@ -32194,7 +32291,7 @@ async function createSdkStdioAcpClient(options) {
|
|
|
32194
32291
|
onSessionUpdate
|
|
32195
32292
|
});
|
|
32196
32293
|
let permissionSeq = 0;
|
|
32197
|
-
const
|
|
32294
|
+
const pendingPermissionReplies = /* @__PURE__ */ new Map();
|
|
32198
32295
|
const client = (_agent) => ({
|
|
32199
32296
|
async requestPermission(params) {
|
|
32200
32297
|
const requestId = `perm-${++permissionSeq}`;
|
|
@@ -32207,15 +32304,15 @@ async function createSdkStdioAcpClient(options) {
|
|
|
32207
32304
|
});
|
|
32208
32305
|
} catch {
|
|
32209
32306
|
}
|
|
32210
|
-
return await new Promise((
|
|
32211
|
-
|
|
32307
|
+
return await new Promise((resolve19) => {
|
|
32308
|
+
pendingPermissionReplies.set(requestId, { resolve: resolve19, params: paramsRecord });
|
|
32212
32309
|
});
|
|
32213
32310
|
},
|
|
32214
32311
|
async readTextFile(params) {
|
|
32215
|
-
const
|
|
32216
|
-
if (!
|
|
32312
|
+
const resolvedPath = resolveSafePathUnderCwd(cwd, params.path);
|
|
32313
|
+
if (!resolvedPath) throw new Error("Invalid or disallowed path");
|
|
32217
32314
|
try {
|
|
32218
|
-
let content = readFileSync2(
|
|
32315
|
+
let content = readFileSync2(resolvedPath, "utf8");
|
|
32219
32316
|
content = sliceFileContentRange(content, params.line, params.limit);
|
|
32220
32317
|
return { content };
|
|
32221
32318
|
} catch (e) {
|
|
@@ -32224,17 +32321,17 @@ async function createSdkStdioAcpClient(options) {
|
|
|
32224
32321
|
}
|
|
32225
32322
|
},
|
|
32226
32323
|
async writeTextFile(params) {
|
|
32227
|
-
const
|
|
32228
|
-
if (!
|
|
32324
|
+
const resolvedPath = resolveSafePathUnderCwd(cwd, params.path);
|
|
32325
|
+
if (!resolvedPath) throw new Error("Invalid or disallowed path");
|
|
32229
32326
|
let oldText = "";
|
|
32230
32327
|
try {
|
|
32231
|
-
oldText = readFileSync2(
|
|
32328
|
+
oldText = readFileSync2(resolvedPath, "utf8");
|
|
32232
32329
|
} catch (e) {
|
|
32233
32330
|
if (e.code !== "ENOENT") throw e;
|
|
32234
32331
|
}
|
|
32235
|
-
mkdirSync2(
|
|
32236
|
-
writeFileSync2(
|
|
32237
|
-
const displayPath = toDisplayPathRelativeToCwd(cwd,
|
|
32332
|
+
mkdirSync2(dirname2(resolvedPath), { recursive: true });
|
|
32333
|
+
writeFileSync2(resolvedPath, params.content, "utf8");
|
|
32334
|
+
const displayPath = toDisplayPathRelativeToCwd(cwd, resolvedPath);
|
|
32238
32335
|
const patchContent = editSnippetToUnifiedDiff(displayPath, oldText, params.content);
|
|
32239
32336
|
onFileChange?.({ path: displayPath, oldText, newText: params.content, patchContent });
|
|
32240
32337
|
return {};
|
|
@@ -32316,9 +32413,9 @@ async function createSdkStdioAcpClient(options) {
|
|
|
32316
32413
|
}
|
|
32317
32414
|
},
|
|
32318
32415
|
async cancel() {
|
|
32319
|
-
for (const [id,
|
|
32320
|
-
|
|
32321
|
-
|
|
32416
|
+
for (const [id, entry] of [...pendingPermissionReplies.entries()]) {
|
|
32417
|
+
pendingPermissionReplies.delete(id);
|
|
32418
|
+
entry.resolve({ outcome: { outcome: "cancelled" } });
|
|
32322
32419
|
}
|
|
32323
32420
|
try {
|
|
32324
32421
|
await connection.cancel({ sessionId });
|
|
@@ -32334,10 +32431,11 @@ async function createSdkStdioAcpClient(options) {
|
|
|
32334
32431
|
}
|
|
32335
32432
|
},
|
|
32336
32433
|
resolveRequest(requestId, result) {
|
|
32337
|
-
const
|
|
32338
|
-
if (!
|
|
32339
|
-
|
|
32340
|
-
|
|
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);
|
|
32341
32439
|
},
|
|
32342
32440
|
disconnect() {
|
|
32343
32441
|
child.kill();
|
|
@@ -32422,7 +32520,7 @@ __export(cursor_acp_client_exports, {
|
|
|
32422
32520
|
detectLocalAgentPresence: () => detectLocalAgentPresence3
|
|
32423
32521
|
});
|
|
32424
32522
|
import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3 } from "node:fs";
|
|
32425
|
-
import { dirname as
|
|
32523
|
+
import { dirname as dirname3 } from "node:path";
|
|
32426
32524
|
import { spawn as spawn3 } from "node:child_process";
|
|
32427
32525
|
import * as readline from "node:readline";
|
|
32428
32526
|
|
|
@@ -32472,7 +32570,7 @@ function buildCursorAcpSpawnCommand(base, sessionMode) {
|
|
|
32472
32570
|
async function createCursorAcpClient(options) {
|
|
32473
32571
|
const command = buildCursorAcpSpawnCommand(options.command, options.sessionMode);
|
|
32474
32572
|
const {
|
|
32475
|
-
cwd =
|
|
32573
|
+
cwd = getBridgeRoot(),
|
|
32476
32574
|
backendAgentType,
|
|
32477
32575
|
onSessionUpdate,
|
|
32478
32576
|
onRequest,
|
|
@@ -32488,7 +32586,7 @@ async function createCursorAcpClient(options) {
|
|
|
32488
32586
|
});
|
|
32489
32587
|
const stderrCapture = createStderrCapture(child);
|
|
32490
32588
|
child.stderr?.on("data", (chunk) => stderrCapture.append(chunk));
|
|
32491
|
-
return new Promise((
|
|
32589
|
+
return new Promise((resolve18, reject) => {
|
|
32492
32590
|
child.on("error", (err) => {
|
|
32493
32591
|
child.kill();
|
|
32494
32592
|
reject(new Error(formatSpawnError2(err, command[0])));
|
|
@@ -32514,7 +32612,9 @@ async function createCursorAcpClient(options) {
|
|
|
32514
32612
|
child.stdin.write(JSON.stringify({ jsonrpc: "2.0", id, error: { code, message } }) + "\n");
|
|
32515
32613
|
}
|
|
32516
32614
|
function resolveRequest(requestId, result) {
|
|
32517
|
-
|
|
32615
|
+
const pending2 = pendingRequests.get(requestId);
|
|
32616
|
+
const payload = pending2?.method === "session/request_permission" ? enrichAcpPermissionRpcResultFromRequestParams(result, pending2.params) : result;
|
|
32617
|
+
respond(requestId, payload);
|
|
32518
32618
|
pendingRequests.delete(requestId);
|
|
32519
32619
|
}
|
|
32520
32620
|
let promptOutputBuffer = "";
|
|
@@ -32570,14 +32670,14 @@ async function createCursorAcpClient(options) {
|
|
|
32570
32670
|
if (dbgFs) {
|
|
32571
32671
|
console.error(`[acp-fs] ${method} path=${filePath.slice(0, 200)}${filePath.length > 200 ? "\u2026" : ""}`);
|
|
32572
32672
|
}
|
|
32573
|
-
const
|
|
32574
|
-
if (!
|
|
32673
|
+
const resolvedPath = resolveSafePathUnderCwd(cwd, filePath);
|
|
32674
|
+
if (!resolvedPath) {
|
|
32575
32675
|
if (dbgFs) console.error(`[acp-fs] ${method} rejected path (outside cwd or empty)`);
|
|
32576
32676
|
respondJsonRpcError(id, -32602, "Invalid or disallowed path");
|
|
32577
32677
|
return;
|
|
32578
32678
|
}
|
|
32579
32679
|
try {
|
|
32580
|
-
let content = readFileSync3(
|
|
32680
|
+
let content = readFileSync3(resolvedPath, "utf8");
|
|
32581
32681
|
const line2 = typeof params.line === "number" ? params.line : void 0;
|
|
32582
32682
|
const limit = typeof params.limit === "number" ? params.limit : void 0;
|
|
32583
32683
|
content = sliceLinesByRange(content, line2, limit);
|
|
@@ -32601,15 +32701,15 @@ async function createCursorAcpClient(options) {
|
|
|
32601
32701
|
`[acp-fs] ${method} path=${filePath.slice(0, 200)}${filePath.length > 200 ? "\u2026" : ""} newBytes=${newText.length}`
|
|
32602
32702
|
);
|
|
32603
32703
|
}
|
|
32604
|
-
const
|
|
32605
|
-
if (!
|
|
32704
|
+
const resolvedPath = resolveSafePathUnderCwd(cwd, filePath);
|
|
32705
|
+
if (!resolvedPath) {
|
|
32606
32706
|
if (dbgFs) console.error(`[acp-fs] ${method} rejected path (outside cwd or empty): ${filePath.slice(0, 120)}`);
|
|
32607
32707
|
respondJsonRpcError(id, -32602, "Invalid or disallowed path");
|
|
32608
32708
|
return;
|
|
32609
32709
|
}
|
|
32610
32710
|
let oldText = "";
|
|
32611
32711
|
try {
|
|
32612
|
-
oldText = readFileSync3(
|
|
32712
|
+
oldText = readFileSync3(resolvedPath, "utf8");
|
|
32613
32713
|
} catch (e) {
|
|
32614
32714
|
if (e.code !== "ENOENT") {
|
|
32615
32715
|
respondJsonRpcError(id, -32e3, e instanceof Error ? e.message : String(e));
|
|
@@ -32617,13 +32717,13 @@ async function createCursorAcpClient(options) {
|
|
|
32617
32717
|
}
|
|
32618
32718
|
}
|
|
32619
32719
|
try {
|
|
32620
|
-
mkdirSync3(
|
|
32621
|
-
writeFileSync3(
|
|
32720
|
+
mkdirSync3(dirname3(resolvedPath), { recursive: true });
|
|
32721
|
+
writeFileSync3(resolvedPath, newText, "utf8");
|
|
32622
32722
|
} catch (e) {
|
|
32623
32723
|
respondJsonRpcError(id, -32e3, e instanceof Error ? e.message : String(e));
|
|
32624
32724
|
return;
|
|
32625
32725
|
}
|
|
32626
|
-
const displayPath = toDisplayPathRelativeToCwd(cwd,
|
|
32726
|
+
const displayPath = toDisplayPathRelativeToCwd(cwd, resolvedPath);
|
|
32627
32727
|
const patchContent = editSnippetToUnifiedDiff(displayPath, oldText, newText);
|
|
32628
32728
|
onFileChange?.({ path: displayPath, oldText, newText, patchContent });
|
|
32629
32729
|
respond(id, null);
|
|
@@ -32675,7 +32775,7 @@ async function createCursorAcpClient(options) {
|
|
|
32675
32775
|
const newResult = await send("session/new", { cwd, mcpServers: [] });
|
|
32676
32776
|
const sessionId = newResult?.sessionId ?? "";
|
|
32677
32777
|
if (!sessionId) throw new Error("Cursor ACP session/new did not return sessionId");
|
|
32678
|
-
|
|
32778
|
+
resolve18({
|
|
32679
32779
|
sessionId,
|
|
32680
32780
|
async sendPrompt(prompt, _options) {
|
|
32681
32781
|
promptOutputBuffer = "";
|
|
@@ -32739,8 +32839,7 @@ async function createCursorAcpClient(options) {
|
|
|
32739
32839
|
resolveRequest(requestId, result) {
|
|
32740
32840
|
const numericId = Number(requestId);
|
|
32741
32841
|
if (!Number.isFinite(numericId)) return;
|
|
32742
|
-
|
|
32743
|
-
if (!pendingRequest) return;
|
|
32842
|
+
if (!pendingRequests.get(numericId)) return;
|
|
32744
32843
|
resolveRequest(numericId, result);
|
|
32745
32844
|
},
|
|
32746
32845
|
disconnect() {
|
|
@@ -32866,16 +32965,16 @@ import { existsSync, statSync } from "node:fs";
|
|
|
32866
32965
|
|
|
32867
32966
|
// src/git/get-git-repo-root-sync.ts
|
|
32868
32967
|
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
32869
|
-
import * as
|
|
32968
|
+
import * as path10 from "node:path";
|
|
32870
32969
|
function getGitRepoRootSync(startDir) {
|
|
32871
32970
|
try {
|
|
32872
32971
|
const out = execFileSync2("git", ["rev-parse", "--show-toplevel"], {
|
|
32873
|
-
cwd:
|
|
32972
|
+
cwd: path10.resolve(startDir),
|
|
32874
32973
|
encoding: "utf8",
|
|
32875
32974
|
stdio: ["ignore", "pipe", "ignore"],
|
|
32876
32975
|
maxBuffer: 1024 * 1024
|
|
32877
32976
|
}).trim();
|
|
32878
|
-
return out ?
|
|
32977
|
+
return out ? path10.resolve(out) : null;
|
|
32879
32978
|
} catch {
|
|
32880
32979
|
return null;
|
|
32881
32980
|
}
|
|
@@ -32884,64 +32983,64 @@ function getGitRepoRootSync(startDir) {
|
|
|
32884
32983
|
// src/agents/acp/workspace-files.ts
|
|
32885
32984
|
import { execFileSync as execFileSync3 } from "node:child_process";
|
|
32886
32985
|
import { readFileSync as readFileSync4 } from "node:fs";
|
|
32887
|
-
import * as
|
|
32888
|
-
function resolveWorkspaceFilePath(
|
|
32986
|
+
import * as path11 from "node:path";
|
|
32987
|
+
function resolveWorkspaceFilePath(sessionParentPath, rawPath) {
|
|
32889
32988
|
const trimmed2 = rawPath.trim();
|
|
32890
32989
|
if (!trimmed2) return null;
|
|
32891
|
-
const
|
|
32892
|
-
let
|
|
32893
|
-
if (!
|
|
32894
|
-
const candidate =
|
|
32895
|
-
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);
|
|
32896
32995
|
if (!gitRoot2) return null;
|
|
32897
|
-
const rel =
|
|
32898
|
-
if (rel.startsWith("..") ||
|
|
32899
|
-
|
|
32996
|
+
const rel = path11.relative(gitRoot2, candidate);
|
|
32997
|
+
if (rel.startsWith("..") || path11.isAbsolute(rel)) return null;
|
|
32998
|
+
resolvedPath = candidate;
|
|
32900
32999
|
}
|
|
32901
|
-
const gitRoot = getGitRepoRootSync(
|
|
33000
|
+
const gitRoot = getGitRepoRootSync(sessionParentPath);
|
|
32902
33001
|
if (gitRoot) {
|
|
32903
|
-
const relFromRoot =
|
|
32904
|
-
if (!relFromRoot.startsWith("..") && !
|
|
32905
|
-
return {
|
|
33002
|
+
const relFromRoot = path11.relative(gitRoot, resolvedPath);
|
|
33003
|
+
if (!relFromRoot.startsWith("..") && !path11.isAbsolute(relFromRoot)) {
|
|
33004
|
+
return { resolvedPath, display: relFromRoot.split(path11.sep).join("/") };
|
|
32906
33005
|
}
|
|
32907
33006
|
}
|
|
32908
|
-
return {
|
|
33007
|
+
return { resolvedPath, display: toDisplayPathRelativeToCwd(sessionParentPath, resolvedPath) };
|
|
32909
33008
|
}
|
|
32910
|
-
function readUtf8WorkspaceFile(
|
|
33009
|
+
function readUtf8WorkspaceFile(sessionParentPath, displayPath) {
|
|
32911
33010
|
if (!displayPath || displayPath.includes("..")) return "";
|
|
32912
|
-
const gitRoot = getGitRepoRootSync(
|
|
33011
|
+
const gitRoot = getGitRepoRootSync(sessionParentPath);
|
|
32913
33012
|
if (gitRoot) {
|
|
32914
|
-
const
|
|
32915
|
-
const rel =
|
|
32916
|
-
if (!rel.startsWith("..") && !
|
|
33013
|
+
const resolvedPath2 = path11.resolve(gitRoot, displayPath);
|
|
33014
|
+
const rel = path11.relative(gitRoot, resolvedPath2);
|
|
33015
|
+
if (!rel.startsWith("..") && !path11.isAbsolute(rel)) {
|
|
32917
33016
|
try {
|
|
32918
|
-
return readFileSync4(
|
|
33017
|
+
return readFileSync4(resolvedPath2, "utf8");
|
|
32919
33018
|
} catch {
|
|
32920
33019
|
}
|
|
32921
33020
|
}
|
|
32922
33021
|
}
|
|
32923
|
-
const
|
|
32924
|
-
if (!
|
|
33022
|
+
const resolvedPath = resolveSafePathUnderCwd(sessionParentPath, displayPath);
|
|
33023
|
+
if (!resolvedPath) return "";
|
|
32925
33024
|
try {
|
|
32926
|
-
return readFileSync4(
|
|
33025
|
+
return readFileSync4(resolvedPath, "utf8");
|
|
32927
33026
|
} catch {
|
|
32928
33027
|
return "";
|
|
32929
33028
|
}
|
|
32930
33029
|
}
|
|
32931
|
-
function
|
|
33030
|
+
function tryWorkspaceDisplayToPath(sessionParentPath, displayPath) {
|
|
32932
33031
|
if (!displayPath || displayPath.includes("..")) return null;
|
|
32933
|
-
const gitRoot = getGitRepoRootSync(
|
|
33032
|
+
const gitRoot = getGitRepoRootSync(sessionParentPath);
|
|
32934
33033
|
if (gitRoot) {
|
|
32935
|
-
const
|
|
32936
|
-
const rel =
|
|
32937
|
-
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;
|
|
32938
33037
|
}
|
|
32939
|
-
return resolveSafePathUnderCwd(
|
|
33038
|
+
return resolveSafePathUnderCwd(sessionParentPath, displayPath);
|
|
32940
33039
|
}
|
|
32941
|
-
function readGitHeadBlob(
|
|
33040
|
+
function readGitHeadBlob(sessionParentPath, displayPath) {
|
|
32942
33041
|
if (!displayPath || displayPath.includes("..")) return "";
|
|
32943
|
-
const gitRoot = getGitRepoRootSync(
|
|
32944
|
-
const execCwd = gitRoot ??
|
|
33042
|
+
const gitRoot = getGitRepoRootSync(sessionParentPath);
|
|
33043
|
+
const execCwd = gitRoot ?? sessionParentPath;
|
|
32945
33044
|
try {
|
|
32946
33045
|
return execFileSync3("git", ["show", `HEAD:${displayPath}`], {
|
|
32947
33046
|
cwd: execCwd,
|
|
@@ -32954,9 +33053,9 @@ function readGitHeadBlob(cwd, displayPath) {
|
|
|
32954
33053
|
}
|
|
32955
33054
|
|
|
32956
33055
|
// src/agents/acp/session-file-change-path-kind.ts
|
|
32957
|
-
function gitHeadPathObjectType(
|
|
33056
|
+
function gitHeadPathObjectType(sessionParentPath, displayPath) {
|
|
32958
33057
|
if (!displayPath || displayPath.includes("..")) return null;
|
|
32959
|
-
const gitRoot = getGitRepoRootSync(
|
|
33058
|
+
const gitRoot = getGitRepoRootSync(sessionParentPath);
|
|
32960
33059
|
if (!gitRoot) return null;
|
|
32961
33060
|
try {
|
|
32962
33061
|
return execFileSync4("git", ["cat-file", "-t", `HEAD:${displayPath}`], {
|
|
@@ -32967,11 +33066,11 @@ function gitHeadPathObjectType(cwd, displayPath) {
|
|
|
32967
33066
|
return null;
|
|
32968
33067
|
}
|
|
32969
33068
|
}
|
|
32970
|
-
function getSessionFileChangeDirectoryFlags(
|
|
32971
|
-
const
|
|
32972
|
-
if (
|
|
33069
|
+
function getSessionFileChangeDirectoryFlags(sessionParentPath, displayPath) {
|
|
33070
|
+
const resolvedPath = tryWorkspaceDisplayToPath(sessionParentPath, displayPath);
|
|
33071
|
+
if (resolvedPath && existsSync(resolvedPath)) {
|
|
32973
33072
|
try {
|
|
32974
|
-
if (statSync(
|
|
33073
|
+
if (statSync(resolvedPath).isDirectory()) {
|
|
32975
33074
|
return { isDirectory: true, directoryRemoved: false };
|
|
32976
33075
|
}
|
|
32977
33076
|
return { isDirectory: false, directoryRemoved: false };
|
|
@@ -32979,7 +33078,7 @@ function getSessionFileChangeDirectoryFlags(cwd, displayPath) {
|
|
|
32979
33078
|
return { isDirectory: false, directoryRemoved: false };
|
|
32980
33079
|
}
|
|
32981
33080
|
}
|
|
32982
|
-
if (gitHeadPathObjectType(
|
|
33081
|
+
if (gitHeadPathObjectType(sessionParentPath, displayPath) === "tree") {
|
|
32983
33082
|
return { isDirectory: true, directoryRemoved: true };
|
|
32984
33083
|
}
|
|
32985
33084
|
return { isDirectory: false, directoryRemoved: false };
|
|
@@ -32987,7 +33086,7 @@ function getSessionFileChangeDirectoryFlags(cwd, displayPath) {
|
|
|
32987
33086
|
|
|
32988
33087
|
// src/agents/acp/hooks/bridge-on-file-change.ts
|
|
32989
33088
|
function createBridgeOnFileChange(opts) {
|
|
32990
|
-
const { routing, getSendSessionUpdate, log: log2 } = opts;
|
|
33089
|
+
const { routing, getSendSessionUpdate, log: log2, sessionParentPath } = opts;
|
|
32991
33090
|
return (evt) => {
|
|
32992
33091
|
const runId = routing.runId;
|
|
32993
33092
|
const sessionId = routing.sessionId;
|
|
@@ -32998,8 +33097,7 @@ function createBridgeOnFileChange(opts) {
|
|
|
32998
33097
|
);
|
|
32999
33098
|
return;
|
|
33000
33099
|
}
|
|
33001
|
-
const
|
|
33002
|
-
const dirFlags = getSessionFileChangeDirectoryFlags(cwd, evt.path);
|
|
33100
|
+
const dirFlags = getSessionFileChangeDirectoryFlags(sessionParentPath, evt.path);
|
|
33003
33101
|
try {
|
|
33004
33102
|
send({
|
|
33005
33103
|
type: "session_file_change",
|
|
@@ -33076,12 +33174,12 @@ function extractDiffPath(o) {
|
|
|
33076
33174
|
}
|
|
33077
33175
|
|
|
33078
33176
|
// src/agents/acp/hooks/extract-acp-file-diffs-from-update/push-diff.ts
|
|
33079
|
-
function pushDiffIfComplete(o,
|
|
33177
|
+
function pushDiffIfComplete(o, sessionParentPath, out) {
|
|
33080
33178
|
const t = o.type;
|
|
33081
33179
|
if (typeof t !== "string" || t.toLowerCase() !== "diff") return;
|
|
33082
33180
|
const rawPath = extractDiffPath(o);
|
|
33083
33181
|
if (!rawPath) return;
|
|
33084
|
-
const resolved = resolveWorkspaceFilePath(
|
|
33182
|
+
const resolved = resolveWorkspaceFilePath(sessionParentPath, rawPath);
|
|
33085
33183
|
if (!resolved) return;
|
|
33086
33184
|
const oldText = readOptionalTextField(o.oldText ?? o.old_text ?? o.before ?? o.oldContent);
|
|
33087
33185
|
const newText = readOptionalTextField(o.newText ?? o.new_text ?? o.after ?? o.newContent);
|
|
@@ -33100,17 +33198,17 @@ var NEST_KEYS = [
|
|
|
33100
33198
|
"data",
|
|
33101
33199
|
"arguments"
|
|
33102
33200
|
];
|
|
33103
|
-
function walkValue(value,
|
|
33201
|
+
function walkValue(value, sessionParentPath, depth, out) {
|
|
33104
33202
|
if (depth > 12 || value == null) return;
|
|
33105
33203
|
if (Array.isArray(value)) {
|
|
33106
|
-
for (const x of value) walkValue(x,
|
|
33204
|
+
for (const x of value) walkValue(x, sessionParentPath, depth + 1, out);
|
|
33107
33205
|
return;
|
|
33108
33206
|
}
|
|
33109
33207
|
if (typeof value !== "object") return;
|
|
33110
33208
|
const o = value;
|
|
33111
|
-
pushDiffIfComplete(o,
|
|
33209
|
+
pushDiffIfComplete(o, sessionParentPath, out);
|
|
33112
33210
|
if (o.type === "content" && o.content != null && typeof o.content === "object") {
|
|
33113
|
-
walkValue(o.content,
|
|
33211
|
+
walkValue(o.content, sessionParentPath, depth + 1, out);
|
|
33114
33212
|
}
|
|
33115
33213
|
for (const k of NEST_KEYS) {
|
|
33116
33214
|
if (o.type === "content" && k === "content") continue;
|
|
@@ -33118,23 +33216,23 @@ function walkValue(value, cwd, depth, out) {
|
|
|
33118
33216
|
if (v == null) continue;
|
|
33119
33217
|
if (k === "arguments" && typeof v === "string") {
|
|
33120
33218
|
try {
|
|
33121
|
-
walkValue(JSON.parse(v),
|
|
33219
|
+
walkValue(JSON.parse(v), sessionParentPath, depth + 1, out);
|
|
33122
33220
|
} catch {
|
|
33123
33221
|
}
|
|
33124
33222
|
continue;
|
|
33125
33223
|
}
|
|
33126
|
-
walkValue(v,
|
|
33224
|
+
walkValue(v, sessionParentPath, depth + 1, out);
|
|
33127
33225
|
}
|
|
33128
33226
|
}
|
|
33129
33227
|
|
|
33130
33228
|
// src/agents/acp/hooks/extract-acp-file-diffs-from-update/extract.ts
|
|
33131
|
-
function extractAcpFileDiffsFromUpdate(update,
|
|
33229
|
+
function extractAcpFileDiffsFromUpdate(update, sessionParentPath) {
|
|
33132
33230
|
if (!update || typeof update !== "object") return [];
|
|
33133
33231
|
const u = update;
|
|
33134
33232
|
const out = [];
|
|
33135
33233
|
const content = u.content;
|
|
33136
33234
|
if (Array.isArray(content)) {
|
|
33137
|
-
for (const x of content) walkValue(x,
|
|
33235
|
+
for (const x of content) walkValue(x, sessionParentPath, 0, out);
|
|
33138
33236
|
}
|
|
33139
33237
|
const byPath = /* @__PURE__ */ new Map();
|
|
33140
33238
|
for (const d of out) byPath.set(d.path, d);
|
|
@@ -33142,18 +33240,18 @@ function extractAcpFileDiffsFromUpdate(update, cwd) {
|
|
|
33142
33240
|
}
|
|
33143
33241
|
|
|
33144
33242
|
// src/agents/acp/hooks/extract-tool-target-paths.ts
|
|
33145
|
-
function addPath(
|
|
33243
|
+
function addPath(sessionParentPath, raw, out) {
|
|
33146
33244
|
if (typeof raw !== "string") return;
|
|
33147
33245
|
const trimmed2 = raw.trim();
|
|
33148
33246
|
if (!trimmed2) return;
|
|
33149
|
-
const resolved = resolveWorkspaceFilePath(
|
|
33247
|
+
const resolved = resolveWorkspaceFilePath(sessionParentPath, trimmed2);
|
|
33150
33248
|
if (!resolved) return;
|
|
33151
33249
|
out.add(resolved.display);
|
|
33152
33250
|
}
|
|
33153
|
-
function walkLocations(
|
|
33251
|
+
function walkLocations(sessionParentPath, loc, out) {
|
|
33154
33252
|
if (!Array.isArray(loc)) return;
|
|
33155
33253
|
for (const item of loc) {
|
|
33156
|
-
if (item && typeof item === "object") addPath(
|
|
33254
|
+
if (item && typeof item === "object") addPath(sessionParentPath, item.path, out);
|
|
33157
33255
|
}
|
|
33158
33256
|
}
|
|
33159
33257
|
var PATH_KEYS = [
|
|
@@ -33166,56 +33264,56 @@ var PATH_KEYS = [
|
|
|
33166
33264
|
"file_path",
|
|
33167
33265
|
"target_file"
|
|
33168
33266
|
];
|
|
33169
|
-
function collectFromObject(
|
|
33267
|
+
function collectFromObject(sessionParentPath, obj, out, depth) {
|
|
33170
33268
|
if (depth > 10) return;
|
|
33171
33269
|
for (const k of PATH_KEYS) {
|
|
33172
|
-
if (k in obj) addPath(
|
|
33270
|
+
if (k in obj) addPath(sessionParentPath, obj[k], out);
|
|
33173
33271
|
}
|
|
33174
33272
|
for (const v of Object.values(obj)) {
|
|
33175
|
-
if (v != null && typeof v === "object") collectUnknown(
|
|
33273
|
+
if (v != null && typeof v === "object") collectUnknown(sessionParentPath, v, out, depth + 1);
|
|
33176
33274
|
}
|
|
33177
33275
|
}
|
|
33178
|
-
function collectUnknown(
|
|
33276
|
+
function collectUnknown(sessionParentPath, v, out, depth) {
|
|
33179
33277
|
if (depth > 10 || v == null) return;
|
|
33180
33278
|
if (Array.isArray(v)) {
|
|
33181
|
-
for (const x of v) collectUnknown(
|
|
33279
|
+
for (const x of v) collectUnknown(sessionParentPath, x, out, depth + 1);
|
|
33182
33280
|
return;
|
|
33183
33281
|
}
|
|
33184
|
-
if (typeof v === "object") collectFromObject(
|
|
33282
|
+
if (typeof v === "object") collectFromObject(sessionParentPath, v, out, depth);
|
|
33185
33283
|
}
|
|
33186
|
-
function walkContentArray(
|
|
33284
|
+
function walkContentArray(sessionParentPath, content, out) {
|
|
33187
33285
|
if (!Array.isArray(content)) return;
|
|
33188
33286
|
for (const item of content) {
|
|
33189
33287
|
if (!item || typeof item !== "object") continue;
|
|
33190
33288
|
const o = item;
|
|
33191
33289
|
if (typeof o.type === "string" && o.type.toLowerCase() === "diff") {
|
|
33192
|
-
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);
|
|
33193
33291
|
}
|
|
33194
33292
|
if (o.type === "content" && o.content != null && typeof o.content === "object") {
|
|
33195
33293
|
const inner = o.content;
|
|
33196
33294
|
if (typeof inner.type === "string" && inner.type.toLowerCase() === "diff") {
|
|
33197
|
-
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);
|
|
33198
33296
|
}
|
|
33199
33297
|
}
|
|
33200
33298
|
}
|
|
33201
33299
|
}
|
|
33202
|
-
function extractToolTargetDisplayPaths(update,
|
|
33300
|
+
function extractToolTargetDisplayPaths(update, sessionParentPath) {
|
|
33203
33301
|
const out = /* @__PURE__ */ new Set();
|
|
33204
33302
|
if (!update || typeof update !== "object") return [];
|
|
33205
33303
|
const u = update;
|
|
33206
|
-
walkLocations(
|
|
33207
|
-
walkLocations(
|
|
33208
|
-
walkLocations(
|
|
33304
|
+
walkLocations(sessionParentPath, u.locations, out);
|
|
33305
|
+
walkLocations(sessionParentPath, u.fileLocations, out);
|
|
33306
|
+
walkLocations(sessionParentPath, u.file_locations, out);
|
|
33209
33307
|
const tc = u.toolCall ?? u.tool_call;
|
|
33210
33308
|
if (tc && typeof tc.arguments === "string") {
|
|
33211
33309
|
try {
|
|
33212
33310
|
const parsed = JSON.parse(tc.arguments);
|
|
33213
|
-
collectUnknown(
|
|
33311
|
+
collectUnknown(sessionParentPath, parsed, out, 0);
|
|
33214
33312
|
} catch {
|
|
33215
33313
|
}
|
|
33216
33314
|
}
|
|
33217
|
-
walkContentArray(
|
|
33218
|
-
collectFromObject(
|
|
33315
|
+
walkContentArray(sessionParentPath, u.content, out);
|
|
33316
|
+
collectFromObject(sessionParentPath, u, out, 0);
|
|
33219
33317
|
return [...out];
|
|
33220
33318
|
}
|
|
33221
33319
|
|
|
@@ -33281,7 +33379,7 @@ var PathSnapshotTracker = class {
|
|
|
33281
33379
|
this.beforeByToolKey.delete(toolKey);
|
|
33282
33380
|
this.accumulatedPathsByToolKey.delete(toolKey);
|
|
33283
33381
|
}
|
|
33284
|
-
captureBeforeFromDisk(toolKey, paths,
|
|
33382
|
+
captureBeforeFromDisk(toolKey, paths, sessionParentPath) {
|
|
33285
33383
|
if (paths.length === 0) return;
|
|
33286
33384
|
let m = this.beforeByToolKey.get(toolKey);
|
|
33287
33385
|
if (!m) {
|
|
@@ -33290,10 +33388,10 @@ var PathSnapshotTracker = class {
|
|
|
33290
33388
|
}
|
|
33291
33389
|
for (const p of paths) {
|
|
33292
33390
|
if (m.has(p)) continue;
|
|
33293
|
-
m.set(p, readUtf8WorkspaceFile(
|
|
33391
|
+
m.set(p, readUtf8WorkspaceFile(sessionParentPath, p));
|
|
33294
33392
|
}
|
|
33295
33393
|
}
|
|
33296
|
-
ensureBeforeFromHeadForMissing(toolKey, paths,
|
|
33394
|
+
ensureBeforeFromHeadForMissing(toolKey, paths, sessionParentPath) {
|
|
33297
33395
|
let m = this.beforeByToolKey.get(toolKey);
|
|
33298
33396
|
if (!m) {
|
|
33299
33397
|
m = /* @__PURE__ */ new Map();
|
|
@@ -33301,10 +33399,10 @@ var PathSnapshotTracker = class {
|
|
|
33301
33399
|
}
|
|
33302
33400
|
for (const p of paths) {
|
|
33303
33401
|
if (m.has(p)) continue;
|
|
33304
|
-
m.set(p, readGitHeadBlob(
|
|
33402
|
+
m.set(p, readGitHeadBlob(sessionParentPath, p));
|
|
33305
33403
|
}
|
|
33306
33404
|
}
|
|
33307
|
-
flushPathSnapshots(toolKey,
|
|
33405
|
+
flushPathSnapshots(toolKey, sessionParentPath, sentPaths, send, runId, sessionId, log2) {
|
|
33308
33406
|
const t = this.debouncers.get(toolKey);
|
|
33309
33407
|
if (t) clearTimeout(t);
|
|
33310
33408
|
this.debouncers.delete(toolKey);
|
|
@@ -33313,10 +33411,10 @@ var PathSnapshotTracker = class {
|
|
|
33313
33411
|
this.beforeByToolKey.delete(toolKey);
|
|
33314
33412
|
if (!send || !runId || !sessionId) return;
|
|
33315
33413
|
for (const [displayPath, oldText] of beforeMap) {
|
|
33316
|
-
const newText = readUtf8WorkspaceFile(
|
|
33414
|
+
const newText = readUtf8WorkspaceFile(sessionParentPath, displayPath);
|
|
33317
33415
|
if (oldText === newText) continue;
|
|
33318
33416
|
const patchContent = editSnippetToUnifiedDiff(displayPath, oldText, newText);
|
|
33319
|
-
const dirFlags = getSessionFileChangeDirectoryFlags(
|
|
33417
|
+
const dirFlags = getSessionFileChangeDirectoryFlags(sessionParentPath, displayPath);
|
|
33320
33418
|
try {
|
|
33321
33419
|
send({
|
|
33322
33420
|
type: "session_file_change",
|
|
@@ -33335,31 +33433,31 @@ var PathSnapshotTracker = class {
|
|
|
33335
33433
|
}
|
|
33336
33434
|
}
|
|
33337
33435
|
}
|
|
33338
|
-
scheduleDebouncedFlush(toolKey,
|
|
33436
|
+
scheduleDebouncedFlush(toolKey, sessionParentPath, sentPaths, send, runId, sessionId, log2) {
|
|
33339
33437
|
const prev = this.debouncers.get(toolKey);
|
|
33340
33438
|
if (prev) clearTimeout(prev);
|
|
33341
33439
|
this.debouncers.set(
|
|
33342
33440
|
toolKey,
|
|
33343
33441
|
setTimeout(() => {
|
|
33344
33442
|
this.debouncers.delete(toolKey);
|
|
33345
|
-
this.flushPathSnapshots(toolKey,
|
|
33443
|
+
this.flushPathSnapshots(toolKey, sessionParentPath, sentPaths, send, runId, sessionId, log2);
|
|
33346
33444
|
}, PATH_SNAPSHOT_DEBOUNCE_MS)
|
|
33347
33445
|
);
|
|
33348
33446
|
}
|
|
33349
|
-
handleToolCallLifecycle(updateKind, toolKey, toolPaths, status,
|
|
33447
|
+
handleToolCallLifecycle(updateKind, toolKey, toolPaths, status, sessionParentPath, sentPaths, send, runId, sessionId, log2) {
|
|
33350
33448
|
if (updateKind === "tool_call") {
|
|
33351
33449
|
this.resetToolSnapshots(toolKey);
|
|
33352
33450
|
}
|
|
33353
33451
|
if (updateKind === "tool_call") {
|
|
33354
|
-
this.captureBeforeFromDisk(toolKey, toolPaths,
|
|
33452
|
+
this.captureBeforeFromDisk(toolKey, toolPaths, sessionParentPath);
|
|
33355
33453
|
} else if (updateKind === "tool_call_update") {
|
|
33356
33454
|
if (isCompletedToolStatus(status)) {
|
|
33357
|
-
this.ensureBeforeFromHeadForMissing(toolKey, toolPaths,
|
|
33358
|
-
this.flushPathSnapshots(toolKey,
|
|
33455
|
+
this.ensureBeforeFromHeadForMissing(toolKey, toolPaths, sessionParentPath);
|
|
33456
|
+
this.flushPathSnapshots(toolKey, sessionParentPath, sentPaths, send, runId, sessionId, log2);
|
|
33359
33457
|
} else {
|
|
33360
|
-
this.captureBeforeFromDisk(toolKey, toolPaths,
|
|
33458
|
+
this.captureBeforeFromDisk(toolKey, toolPaths, sessionParentPath);
|
|
33361
33459
|
if (this.beforeByToolKey.has(toolKey)) {
|
|
33362
|
-
this.scheduleDebouncedFlush(toolKey,
|
|
33460
|
+
this.scheduleDebouncedFlush(toolKey, sessionParentPath, sentPaths, send, runId, sessionId, log2);
|
|
33363
33461
|
}
|
|
33364
33462
|
}
|
|
33365
33463
|
}
|
|
@@ -33367,11 +33465,11 @@ var PathSnapshotTracker = class {
|
|
|
33367
33465
|
};
|
|
33368
33466
|
|
|
33369
33467
|
// src/agents/acp/hooks/bridge-on-session-update/send-structured-file-changes.ts
|
|
33370
|
-
function sendExtractedDiffsAsSessionFileChanges(diffs, send,
|
|
33468
|
+
function sendExtractedDiffsAsSessionFileChanges(diffs, send, sessionParentPath, sessionId, runId, sentPaths, log2) {
|
|
33371
33469
|
for (const d of diffs) {
|
|
33372
33470
|
try {
|
|
33373
33471
|
const patchContent = editSnippetToUnifiedDiff(d.path, d.oldText, d.newText);
|
|
33374
|
-
const dirFlags = getSessionFileChangeDirectoryFlags(
|
|
33472
|
+
const dirFlags = getSessionFileChangeDirectoryFlags(sessionParentPath, d.path);
|
|
33375
33473
|
send({
|
|
33376
33474
|
type: "session_file_change",
|
|
33377
33475
|
sessionId,
|
|
@@ -33389,15 +33487,15 @@ function sendExtractedDiffsAsSessionFileChanges(diffs, send, cwd, sessionId, run
|
|
|
33389
33487
|
}
|
|
33390
33488
|
}
|
|
33391
33489
|
}
|
|
33392
|
-
function sendGitHeadVsWorkspaceForToolPaths(mergedPaths, sentPaths, send,
|
|
33490
|
+
function sendGitHeadVsWorkspaceForToolPaths(mergedPaths, sentPaths, send, sessionParentPath, sessionId, runId, log2) {
|
|
33393
33491
|
for (const displayPath of mergedPaths) {
|
|
33394
33492
|
if (sentPaths.has(displayPath)) continue;
|
|
33395
|
-
const oldText = readGitHeadBlob(
|
|
33396
|
-
const newText = readUtf8WorkspaceFile(
|
|
33493
|
+
const oldText = readGitHeadBlob(sessionParentPath, displayPath);
|
|
33494
|
+
const newText = readUtf8WorkspaceFile(sessionParentPath, displayPath);
|
|
33397
33495
|
if (oldText === newText) continue;
|
|
33398
33496
|
try {
|
|
33399
33497
|
const patchContent = editSnippetToUnifiedDiff(displayPath, oldText, newText);
|
|
33400
|
-
const dirFlags = getSessionFileChangeDirectoryFlags(
|
|
33498
|
+
const dirFlags = getSessionFileChangeDirectoryFlags(sessionParentPath, displayPath);
|
|
33401
33499
|
send({
|
|
33402
33500
|
type: "session_file_change",
|
|
33403
33501
|
sessionId,
|
|
@@ -33418,13 +33516,12 @@ function sendGitHeadVsWorkspaceForToolPaths(mergedPaths, sentPaths, send, cwd, s
|
|
|
33418
33516
|
|
|
33419
33517
|
// src/agents/acp/hooks/bridge-on-session-update/create-bridge-on-session-update.ts
|
|
33420
33518
|
function createBridgeOnSessionUpdate(opts) {
|
|
33421
|
-
const { routing, getSendSessionUpdate, log: log2 } = opts;
|
|
33519
|
+
const { routing, getSendSessionUpdate, log: log2, sessionParentPath } = opts;
|
|
33422
33520
|
const pathTracker = new PathSnapshotTracker();
|
|
33423
33521
|
return (params) => {
|
|
33424
33522
|
const runId = routing.runId;
|
|
33425
33523
|
const sessionId = routing.sessionId;
|
|
33426
33524
|
pathTracker.onRunIdChanged(runId);
|
|
33427
|
-
const cwd = getBridgeWorkspaceDirectory();
|
|
33428
33525
|
const send = getSendSessionUpdate();
|
|
33429
33526
|
const sentFileChangePaths = /* @__PURE__ */ new Set();
|
|
33430
33527
|
const p = params;
|
|
@@ -33432,7 +33529,7 @@ function createBridgeOnSessionUpdate(opts) {
|
|
|
33432
33529
|
const isCompletedToolCallUpdate = updateKind === "tool_call_update" && isCompletedToolStatus(p.status);
|
|
33433
33530
|
const toolName = p.toolCall?.name ?? p.tool_call?.name ?? "";
|
|
33434
33531
|
const isToolUpdate = updateKind === "tool_call" || updateKind === "tool_call_update" || typeof toolName === "string" && toolName.length > 0;
|
|
33435
|
-
const toolPaths = isToolUpdate ? extractToolTargetDisplayPaths(params,
|
|
33532
|
+
const toolPaths = isToolUpdate ? extractToolTargetDisplayPaths(params, sessionParentPath) : [];
|
|
33436
33533
|
const toolKey = isToolUpdate ? pathTracker.resolveToolKey(params, updateKind) : "";
|
|
33437
33534
|
if (updateKind === "tool_call") {
|
|
33438
33535
|
pathTracker.resetToolSnapshots(toolKey);
|
|
@@ -33447,7 +33544,7 @@ function createBridgeOnSessionUpdate(opts) {
|
|
|
33447
33544
|
toolKey,
|
|
33448
33545
|
toolPaths,
|
|
33449
33546
|
p.status,
|
|
33450
|
-
|
|
33547
|
+
sessionParentPath,
|
|
33451
33548
|
sentFileChangePaths,
|
|
33452
33549
|
deliver,
|
|
33453
33550
|
runId ?? "",
|
|
@@ -33455,9 +33552,17 @@ function createBridgeOnSessionUpdate(opts) {
|
|
|
33455
33552
|
log2
|
|
33456
33553
|
);
|
|
33457
33554
|
}
|
|
33458
|
-
const diffs = extractAcpFileDiffsFromUpdate(params,
|
|
33555
|
+
const diffs = extractAcpFileDiffsFromUpdate(params, sessionParentPath);
|
|
33459
33556
|
if (diffs.length > 0 && send && runId && sessionId) {
|
|
33460
|
-
sendExtractedDiffsAsSessionFileChanges(
|
|
33557
|
+
sendExtractedDiffsAsSessionFileChanges(
|
|
33558
|
+
diffs,
|
|
33559
|
+
send,
|
|
33560
|
+
sessionParentPath,
|
|
33561
|
+
sessionId,
|
|
33562
|
+
runId,
|
|
33563
|
+
sentFileChangePaths,
|
|
33564
|
+
log2
|
|
33565
|
+
);
|
|
33461
33566
|
} else if (diffs.length > 0) {
|
|
33462
33567
|
log2(
|
|
33463
33568
|
`[Bridge service] Agent file diff(s) not forwarded (${diffs.length}): session or run not wired to the bridge.`
|
|
@@ -33466,7 +33571,15 @@ function createBridgeOnSessionUpdate(opts) {
|
|
|
33466
33571
|
if (isCompletedToolCallUpdate && send && runId && sessionId) {
|
|
33467
33572
|
const acc = pathTracker.accumulatedPathsByToolKey.get(toolKey);
|
|
33468
33573
|
const merged = [.../* @__PURE__ */ new Set([...acc ? [...acc] : [], ...toolPaths])];
|
|
33469
|
-
sendGitHeadVsWorkspaceForToolPaths(
|
|
33574
|
+
sendGitHeadVsWorkspaceForToolPaths(
|
|
33575
|
+
merged,
|
|
33576
|
+
sentFileChangePaths,
|
|
33577
|
+
send,
|
|
33578
|
+
sessionParentPath,
|
|
33579
|
+
sessionId,
|
|
33580
|
+
runId,
|
|
33581
|
+
log2
|
|
33582
|
+
);
|
|
33470
33583
|
pathTracker.accumulatedPathsByToolKey.delete(toolKey);
|
|
33471
33584
|
}
|
|
33472
33585
|
if (runId && send) {
|
|
@@ -33501,11 +33614,12 @@ function buildAcpSessionBridgeHooks(opts) {
|
|
|
33501
33614
|
|
|
33502
33615
|
// src/agents/acp/ensure-acp-client.ts
|
|
33503
33616
|
async function ensureAcpClient(options) {
|
|
33504
|
-
const { state, preferredAgentType, mode,
|
|
33505
|
-
const
|
|
33506
|
-
|
|
33507
|
-
|
|
33508
|
-
|
|
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)) {
|
|
33509
33623
|
try {
|
|
33510
33624
|
state.acpHandle.disconnect();
|
|
33511
33625
|
} catch {
|
|
@@ -33537,14 +33651,14 @@ async function ensureAcpClient(options) {
|
|
|
33537
33651
|
if (!state.acpStartPromise) {
|
|
33538
33652
|
let statOk = false;
|
|
33539
33653
|
try {
|
|
33540
|
-
const st = fs10.statSync(
|
|
33654
|
+
const st = fs10.statSync(targetSessionParentPath);
|
|
33541
33655
|
statOk = st.isDirectory();
|
|
33542
33656
|
if (!statOk) {
|
|
33543
|
-
state.lastAcpStartError = `Agent cwd is not a directory: ${
|
|
33657
|
+
state.lastAcpStartError = `Agent cwd is not a directory: ${targetSessionParentPath}`;
|
|
33544
33658
|
log2(`[Agent] ${state.lastAcpStartError}`);
|
|
33545
33659
|
}
|
|
33546
33660
|
} catch {
|
|
33547
|
-
state.lastAcpStartError = `Agent cwd missing or inaccessible: ${
|
|
33661
|
+
state.lastAcpStartError = `Agent cwd missing or inaccessible: ${targetSessionParentPath}`;
|
|
33548
33662
|
log2(`[Agent] ${state.lastAcpStartError}`);
|
|
33549
33663
|
}
|
|
33550
33664
|
if (!statOk) {
|
|
@@ -33552,6 +33666,7 @@ async function ensureAcpClient(options) {
|
|
|
33552
33666
|
}
|
|
33553
33667
|
const hooks = buildAcpSessionBridgeHooks({
|
|
33554
33668
|
routing,
|
|
33669
|
+
sessionParentPath: targetSessionParentPath,
|
|
33555
33670
|
getSendSessionUpdate: () => sendSessionUpdate,
|
|
33556
33671
|
getSendRequest: () => sendRequest,
|
|
33557
33672
|
log: log2
|
|
@@ -33559,7 +33674,6 @@ async function ensureAcpClient(options) {
|
|
|
33559
33674
|
state.acpStartPromise = resolved.createClient({
|
|
33560
33675
|
command: resolved.command,
|
|
33561
33676
|
sessionMode: mode,
|
|
33562
|
-
cwd: targetCwd,
|
|
33563
33677
|
backendAgentType: preferredAgentType,
|
|
33564
33678
|
onAgentSubprocessExit: () => {
|
|
33565
33679
|
state.acpHandle = null;
|
|
@@ -33567,11 +33681,12 @@ async function ensureAcpClient(options) {
|
|
|
33567
33681
|
state.acpAgentKey = null;
|
|
33568
33682
|
state.lastAcpStartError = "Agent subprocess exited";
|
|
33569
33683
|
},
|
|
33570
|
-
...hooks
|
|
33684
|
+
...hooks,
|
|
33685
|
+
cwd: targetSessionParentPath
|
|
33571
33686
|
}).then((h) => {
|
|
33572
33687
|
state.lastAcpStartError = null;
|
|
33573
33688
|
state.acpHandle = h;
|
|
33574
|
-
state.lastAcpCwd =
|
|
33689
|
+
state.lastAcpCwd = targetSessionParentPath;
|
|
33575
33690
|
state.acpAgentKey = agentKey;
|
|
33576
33691
|
return h;
|
|
33577
33692
|
}).catch((err) => {
|
|
@@ -33620,7 +33735,7 @@ async function createAcpManager(options) {
|
|
|
33620
33735
|
runId,
|
|
33621
33736
|
mode,
|
|
33622
33737
|
agentType,
|
|
33623
|
-
|
|
33738
|
+
sessionParentPath,
|
|
33624
33739
|
sendResult: sendResult2,
|
|
33625
33740
|
sendSessionUpdate,
|
|
33626
33741
|
followUpCatalogPromptId,
|
|
@@ -33639,7 +33754,7 @@ async function createAcpManager(options) {
|
|
|
33639
33754
|
state,
|
|
33640
33755
|
preferredAgentType: preferredForPrompt,
|
|
33641
33756
|
mode,
|
|
33642
|
-
|
|
33757
|
+
sessionParentPath,
|
|
33643
33758
|
routing: promptRouting,
|
|
33644
33759
|
sendSessionUpdate,
|
|
33645
33760
|
sendRequest: sendSessionUpdate,
|
|
@@ -33688,7 +33803,7 @@ async function createAcpManager(options) {
|
|
|
33688
33803
|
sessionId,
|
|
33689
33804
|
runId,
|
|
33690
33805
|
agentType: preferredForPrompt,
|
|
33691
|
-
agentCwd:
|
|
33806
|
+
agentCwd: resolveSessionParentPathForAgentProcess(sessionParentPath),
|
|
33692
33807
|
sendResult: sendResult2,
|
|
33693
33808
|
sendSessionUpdate,
|
|
33694
33809
|
log: log2,
|
|
@@ -33743,12 +33858,12 @@ async function createAcpManager(options) {
|
|
|
33743
33858
|
}
|
|
33744
33859
|
|
|
33745
33860
|
// src/worktrees/session-worktree-manager.ts
|
|
33746
|
-
import * as
|
|
33861
|
+
import * as path19 from "node:path";
|
|
33747
33862
|
import os4 from "node:os";
|
|
33748
33863
|
|
|
33749
33864
|
// src/worktrees/prepare-new-session-worktrees.ts
|
|
33750
33865
|
import * as fs12 from "node:fs";
|
|
33751
|
-
import * as
|
|
33866
|
+
import * as path14 from "node:path";
|
|
33752
33867
|
|
|
33753
33868
|
// src/git/worktree-add.ts
|
|
33754
33869
|
async function gitWorktreeAddBranch(mainRepoPath, worktreePath, branch) {
|
|
@@ -33758,11 +33873,11 @@ async function gitWorktreeAddBranch(mainRepoPath, worktreePath, branch) {
|
|
|
33758
33873
|
|
|
33759
33874
|
// src/worktrees/worktree-layout-file.ts
|
|
33760
33875
|
import * as fs11 from "node:fs";
|
|
33761
|
-
import * as
|
|
33876
|
+
import * as path13 from "node:path";
|
|
33762
33877
|
import os3 from "node:os";
|
|
33763
33878
|
var LAYOUT_FILENAME = "worktree-launcher-layout.json";
|
|
33764
33879
|
function defaultWorktreeLayoutPath() {
|
|
33765
|
-
return
|
|
33880
|
+
return path13.join(os3.homedir(), ".buildautomaton", LAYOUT_FILENAME);
|
|
33766
33881
|
}
|
|
33767
33882
|
function normalizeLoadedLayout(raw) {
|
|
33768
33883
|
if (raw && typeof raw === "object" && "launcherCwds" in raw) {
|
|
@@ -33783,24 +33898,24 @@ function loadWorktreeLayout() {
|
|
|
33783
33898
|
}
|
|
33784
33899
|
function saveWorktreeLayout(layout) {
|
|
33785
33900
|
try {
|
|
33786
|
-
const dir =
|
|
33901
|
+
const dir = path13.dirname(defaultWorktreeLayoutPath());
|
|
33787
33902
|
fs11.mkdirSync(dir, { recursive: true });
|
|
33788
33903
|
fs11.writeFileSync(defaultWorktreeLayoutPath(), JSON.stringify(layout, null, 2), "utf8");
|
|
33789
33904
|
} catch {
|
|
33790
33905
|
}
|
|
33791
33906
|
}
|
|
33792
|
-
function baseNameSafe(
|
|
33793
|
-
return
|
|
33907
|
+
function baseNameSafe(pathString) {
|
|
33908
|
+
return path13.basename(pathString).replace(/[^a-zA-Z0-9._-]+/g, "-") || "cwd";
|
|
33794
33909
|
}
|
|
33795
|
-
function getLauncherDirNameIfPresent(layout,
|
|
33796
|
-
const norm =
|
|
33797
|
-
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);
|
|
33798
33913
|
return existing?.dirName;
|
|
33799
33914
|
}
|
|
33800
|
-
function allocateDirNameForLauncherCwd(layout,
|
|
33801
|
-
const existing = getLauncherDirNameIfPresent(layout,
|
|
33915
|
+
function allocateDirNameForLauncherCwd(layout, bridgeRootPath2) {
|
|
33916
|
+
const existing = getLauncherDirNameIfPresent(layout, bridgeRootPath2);
|
|
33802
33917
|
if (existing) return existing;
|
|
33803
|
-
const norm =
|
|
33918
|
+
const norm = path13.resolve(bridgeRootPath2);
|
|
33804
33919
|
const base = baseNameSafe(norm);
|
|
33805
33920
|
const used = new Set(layout.launcherCwds.map((e) => e.dirName));
|
|
33806
33921
|
let name = base;
|
|
@@ -33816,24 +33931,27 @@ function allocateDirNameForLauncherCwd(layout, launcherCwdAbs) {
|
|
|
33816
33931
|
|
|
33817
33932
|
// src/worktrees/prepare-new-session-worktrees.ts
|
|
33818
33933
|
async function prepareNewSessionWorktrees(options) {
|
|
33819
|
-
const {
|
|
33820
|
-
const
|
|
33821
|
-
const cwdKey = allocateDirNameForLauncherCwd(layout,
|
|
33822
|
-
const
|
|
33823
|
-
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);
|
|
33824
33940
|
if (repos.length === 0) {
|
|
33825
|
-
log2("[worktrees] No Git repositories under
|
|
33941
|
+
log2("[worktrees] No Git repositories under bridge root; skipping worktree creation.");
|
|
33826
33942
|
return null;
|
|
33827
33943
|
}
|
|
33828
33944
|
const branch = `session-${sessionId}`;
|
|
33829
33945
|
const worktreePaths = [];
|
|
33830
|
-
fs12.mkdirSync(
|
|
33946
|
+
fs12.mkdirSync(sessionDir, { recursive: true });
|
|
33831
33947
|
for (const repo of repos) {
|
|
33832
|
-
let rel =
|
|
33833
|
-
if (rel.startsWith("..") ||
|
|
33948
|
+
let rel = path14.relative(bridgeResolved, repo.absolutePath);
|
|
33949
|
+
if (rel.startsWith("..") || path14.isAbsolute(rel)) continue;
|
|
33834
33950
|
const relNorm = rel === "" ? "." : rel;
|
|
33835
|
-
const wtPath =
|
|
33836
|
-
|
|
33951
|
+
const wtPath = relNorm === "." ? sessionDir : path14.join(sessionDir, relNorm);
|
|
33952
|
+
if (relNorm !== ".") {
|
|
33953
|
+
fs12.mkdirSync(path14.dirname(wtPath), { recursive: true });
|
|
33954
|
+
}
|
|
33837
33955
|
try {
|
|
33838
33956
|
await gitWorktreeAddBranch(repo.absolutePath, wtPath, branch);
|
|
33839
33957
|
log2(`[worktrees] Added worktree ${wtPath} (branch ${branch}).`);
|
|
@@ -33845,7 +33963,11 @@ async function prepareNewSessionWorktrees(options) {
|
|
|
33845
33963
|
}
|
|
33846
33964
|
}
|
|
33847
33965
|
if (worktreePaths.length === 0) return null;
|
|
33848
|
-
return {
|
|
33966
|
+
return {
|
|
33967
|
+
worktreePaths,
|
|
33968
|
+
sessionParentPath: sessionDir,
|
|
33969
|
+
workingTreeRelRoot: sessionDir
|
|
33970
|
+
};
|
|
33849
33971
|
}
|
|
33850
33972
|
|
|
33851
33973
|
// src/git/rename-branch.ts
|
|
@@ -33877,16 +33999,16 @@ import * as fs14 from "node:fs";
|
|
|
33877
33999
|
|
|
33878
34000
|
// src/git/resolve-main-repo-from-git-file.ts
|
|
33879
34001
|
import * as fs13 from "node:fs";
|
|
33880
|
-
import * as
|
|
34002
|
+
import * as path15 from "node:path";
|
|
33881
34003
|
function resolveMainRepoFromWorktreeGitFile(wt) {
|
|
33882
|
-
const gitDirFile =
|
|
34004
|
+
const gitDirFile = path15.join(wt, ".git");
|
|
33883
34005
|
if (!fs13.existsSync(gitDirFile) || !fs13.statSync(gitDirFile).isFile()) return "";
|
|
33884
34006
|
const first2 = fs13.readFileSync(gitDirFile, "utf8").trim();
|
|
33885
34007
|
const m = first2.match(/^gitdir:\s*(.+)$/im);
|
|
33886
34008
|
if (!m) return "";
|
|
33887
|
-
const gitWorktreePath =
|
|
33888
|
-
const gitDir =
|
|
33889
|
-
return
|
|
34009
|
+
const gitWorktreePath = path15.resolve(wt, m[1].trim());
|
|
34010
|
+
const gitDir = path15.dirname(path15.dirname(gitWorktreePath));
|
|
34011
|
+
return path15.dirname(gitDir);
|
|
33890
34012
|
}
|
|
33891
34013
|
|
|
33892
34014
|
// src/git/worktree-remove.ts
|
|
@@ -34128,7 +34250,7 @@ function formatRemoteDisplayLabel(remoteUrl) {
|
|
|
34128
34250
|
}
|
|
34129
34251
|
|
|
34130
34252
|
// src/git/working-directory/changes/get-working-tree-change-repo-details.ts
|
|
34131
|
-
import * as
|
|
34253
|
+
import * as path17 from "node:path";
|
|
34132
34254
|
|
|
34133
34255
|
// src/git/working-directory/changes/parse-git-status.ts
|
|
34134
34256
|
function parseNameStatusLines(lines) {
|
|
@@ -34249,16 +34371,16 @@ async function listChangedFilesForCommit(repoGitCwd, repoRelPath, commitSha) {
|
|
|
34249
34371
|
|
|
34250
34372
|
// src/git/working-directory/changes/list-changed-files-for-repo.ts
|
|
34251
34373
|
import * as fs17 from "node:fs";
|
|
34252
|
-
import * as
|
|
34374
|
+
import * as path16 from "node:path";
|
|
34253
34375
|
|
|
34254
34376
|
// src/git/working-directory/changes/count-lines.ts
|
|
34255
34377
|
import { createReadStream } from "node:fs";
|
|
34256
34378
|
import * as readline2 from "node:readline";
|
|
34257
|
-
async function countTextFileLines(
|
|
34379
|
+
async function countTextFileLines(filePath) {
|
|
34258
34380
|
let bytes = 0;
|
|
34259
34381
|
const maxBytes = 512e3;
|
|
34260
34382
|
let lines = 0;
|
|
34261
|
-
const stream = createReadStream(
|
|
34383
|
+
const stream = createReadStream(filePath, { encoding: "utf8" });
|
|
34262
34384
|
const rl = readline2.createInterface({ input: stream, crlfDelay: Infinity });
|
|
34263
34385
|
for await (const _line of rl) {
|
|
34264
34386
|
lines += 1;
|
|
@@ -34286,15 +34408,15 @@ async function readGitBlobLines(repoCwd, pathInRepo) {
|
|
|
34286
34408
|
return null;
|
|
34287
34409
|
}
|
|
34288
34410
|
}
|
|
34289
|
-
async function readWorktreeFileLines(
|
|
34411
|
+
async function readWorktreeFileLines(filePath) {
|
|
34290
34412
|
try {
|
|
34291
|
-
const raw = await fs16.promises.readFile(
|
|
34413
|
+
const raw = await fs16.promises.readFile(filePath, "utf8");
|
|
34292
34414
|
return raw.split(/\r?\n/);
|
|
34293
34415
|
} catch {
|
|
34294
34416
|
return null;
|
|
34295
34417
|
}
|
|
34296
34418
|
}
|
|
34297
|
-
async function hydrateUnifiedPatchWithFileContext(patch,
|
|
34419
|
+
async function hydrateUnifiedPatchWithFileContext(patch, filePath, repoGitCwd, pathInRepo, change) {
|
|
34298
34420
|
if (!patch.trim() || patch.includes("Binary files")) return patch;
|
|
34299
34421
|
const all = patch.split("\n");
|
|
34300
34422
|
const out = [];
|
|
@@ -34311,7 +34433,7 @@ async function hydrateUnifiedPatchWithFileContext(patch, absFile, repoGitCwd, pa
|
|
|
34311
34433
|
};
|
|
34312
34434
|
const diskLines = async () => {
|
|
34313
34435
|
if (diskCache !== void 0) return diskCache;
|
|
34314
|
-
diskCache = await readWorktreeFileLines(
|
|
34436
|
+
diskCache = await readWorktreeFileLines(filePath);
|
|
34315
34437
|
return diskCache;
|
|
34316
34438
|
};
|
|
34317
34439
|
while (i < all.length) {
|
|
@@ -34423,7 +34545,7 @@ async function listChangedFilesForRepo(repoGitCwd, repoRelPath) {
|
|
|
34423
34545
|
const rows = [];
|
|
34424
34546
|
for (const pathInRepo of paths) {
|
|
34425
34547
|
const relLauncher = posixJoinDirFile(repoRelPath, pathInRepo.replace(/\\/g, "/"));
|
|
34426
|
-
const
|
|
34548
|
+
const repoFilePath = path16.join(repoGitCwd, pathInRepo);
|
|
34427
34549
|
const nums = numByPath.get(pathInRepo);
|
|
34428
34550
|
let additions = nums?.additions ?? 0;
|
|
34429
34551
|
let deletions = nums?.deletions ?? 0;
|
|
@@ -34436,8 +34558,8 @@ async function listChangedFilesForRepo(repoGitCwd, repoRelPath) {
|
|
|
34436
34558
|
deletions = fromGit.deletions;
|
|
34437
34559
|
} else {
|
|
34438
34560
|
try {
|
|
34439
|
-
const st = await fs17.promises.stat(
|
|
34440
|
-
if (st.isFile()) additions = await countTextFileLines(
|
|
34561
|
+
const st = await fs17.promises.stat(repoFilePath);
|
|
34562
|
+
if (st.isFile()) additions = await countTextFileLines(repoFilePath);
|
|
34441
34563
|
else additions = 0;
|
|
34442
34564
|
} catch {
|
|
34443
34565
|
additions = 0;
|
|
@@ -34462,10 +34584,10 @@ async function listChangedFilesForRepo(repoGitCwd, repoRelPath) {
|
|
|
34462
34584
|
} else {
|
|
34463
34585
|
pathInRepo = row.pathRelLauncher;
|
|
34464
34586
|
}
|
|
34465
|
-
const
|
|
34587
|
+
const filePath = path16.join(repoGitCwd, pathInRepo);
|
|
34466
34588
|
let patch = await unifiedDiffForFile(repoGitCwd, pathInRepo, row.change);
|
|
34467
34589
|
if (patch) {
|
|
34468
|
-
patch = await hydrateUnifiedPatchWithFileContext(patch,
|
|
34590
|
+
patch = await hydrateUnifiedPatchWithFileContext(patch, filePath, repoGitCwd, pathInRepo, row.change);
|
|
34469
34591
|
}
|
|
34470
34592
|
row.patchContent = patch;
|
|
34471
34593
|
}
|
|
@@ -34478,8 +34600,9 @@ function normRepoRel(p) {
|
|
|
34478
34600
|
return x === "" ? "." : x;
|
|
34479
34601
|
}
|
|
34480
34602
|
async function getWorkingTreeChangeRepoDetails(options) {
|
|
34481
|
-
const
|
|
34482
|
-
const
|
|
34603
|
+
const bridgeRoot = path17.resolve(getBridgeRoot());
|
|
34604
|
+
const sessionWtRoot = options.sessionWorktreeRootPath ? path17.resolve(options.sessionWorktreeRootPath) : null;
|
|
34605
|
+
const legacyNested = options.legacyRepoNestedSessionLayout === true;
|
|
34483
34606
|
const out = [];
|
|
34484
34607
|
const filter = options.repoFilterRelPath != null ? normRepoRel(options.repoFilterRelPath) : null;
|
|
34485
34608
|
const basisInput = options.basis ?? { kind: "working" };
|
|
@@ -34490,8 +34613,8 @@ async function getWorkingTreeChangeRepoDetails(options) {
|
|
|
34490
34613
|
throw new Error("commit sha is required for commit changes");
|
|
34491
34614
|
}
|
|
34492
34615
|
const basis = filter == null && basisInput.kind === "commit" ? { kind: "working" } : basisInput;
|
|
34493
|
-
for (const target of options.
|
|
34494
|
-
const t =
|
|
34616
|
+
for (const target of options.commitTargetPaths) {
|
|
34617
|
+
const t = path17.resolve(target);
|
|
34495
34618
|
if (!await isGitRepoDirectory(t)) continue;
|
|
34496
34619
|
const g = simpleGit(t);
|
|
34497
34620
|
let branch = "HEAD";
|
|
@@ -34503,8 +34626,9 @@ async function getWorkingTreeChangeRepoDetails(options) {
|
|
|
34503
34626
|
const remoteUrl = await getRemoteOriginUrl(t);
|
|
34504
34627
|
const remoteDisplay = formatRemoteDisplayLabel(remoteUrl);
|
|
34505
34628
|
let repoRelPath;
|
|
34506
|
-
if (
|
|
34507
|
-
const
|
|
34629
|
+
if (sessionWtRoot) {
|
|
34630
|
+
const anchor = legacyNested ? path17.dirname(t) : t;
|
|
34631
|
+
const relNorm = path17.relative(sessionWtRoot, anchor);
|
|
34508
34632
|
repoRelPath = relNorm === "" ? "." : relNorm.replace(/\\/g, "/");
|
|
34509
34633
|
} else {
|
|
34510
34634
|
let top = t;
|
|
@@ -34513,8 +34637,8 @@ async function getWorkingTreeChangeRepoDetails(options) {
|
|
|
34513
34637
|
} catch {
|
|
34514
34638
|
top = t;
|
|
34515
34639
|
}
|
|
34516
|
-
const rel =
|
|
34517
|
-
repoRelPath = rel.startsWith("..") ?
|
|
34640
|
+
const rel = path17.relative(bridgeRoot, path17.resolve(top)).replace(/\\/g, "/") || ".";
|
|
34641
|
+
repoRelPath = rel.startsWith("..") ? path17.basename(path17.resolve(top)) : rel;
|
|
34518
34642
|
}
|
|
34519
34643
|
const norm = normRepoRel(repoRelPath === "" ? "." : repoRelPath);
|
|
34520
34644
|
if (filter && norm !== filter) continue;
|
|
@@ -34580,14 +34704,37 @@ async function commitSessionWorktrees(options) {
|
|
|
34580
34704
|
|
|
34581
34705
|
// src/worktrees/discover-session-worktree-on-disk.ts
|
|
34582
34706
|
import * as fs18 from "node:fs";
|
|
34583
|
-
import * as
|
|
34584
|
-
function isGitDir(
|
|
34707
|
+
import * as path18 from "node:path";
|
|
34708
|
+
function isGitDir(dirPath) {
|
|
34585
34709
|
try {
|
|
34586
|
-
return fs18.existsSync(
|
|
34710
|
+
return fs18.existsSync(path18.join(dirPath, ".git"));
|
|
34587
34711
|
} catch {
|
|
34588
34712
|
return false;
|
|
34589
34713
|
}
|
|
34590
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
|
+
}
|
|
34591
34738
|
function collectWorktreeRootsNamed(root, sessionId, maxDepth) {
|
|
34592
34739
|
const out = [];
|
|
34593
34740
|
const walk = (dir, depth) => {
|
|
@@ -34600,10 +34747,10 @@ function collectWorktreeRootsNamed(root, sessionId, maxDepth) {
|
|
|
34600
34747
|
}
|
|
34601
34748
|
for (const e of entries) {
|
|
34602
34749
|
if (e.name.startsWith(".")) continue;
|
|
34603
|
-
const full =
|
|
34750
|
+
const full = path18.join(dir, e.name);
|
|
34604
34751
|
if (!e.isDirectory()) continue;
|
|
34605
34752
|
if (e.name === sessionId) {
|
|
34606
|
-
if (isGitDir(full)) out.push(
|
|
34753
|
+
if (isGitDir(full)) out.push(path18.resolve(full));
|
|
34607
34754
|
} else {
|
|
34608
34755
|
walk(full, depth + 1);
|
|
34609
34756
|
}
|
|
@@ -34612,16 +34759,55 @@ function collectWorktreeRootsNamed(root, sessionId, maxDepth) {
|
|
|
34612
34759
|
walk(root, 0);
|
|
34613
34760
|
return out;
|
|
34614
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
|
+
}
|
|
34615
34801
|
function discoverSessionWorktreeOnDisk(options) {
|
|
34616
|
-
const { sessionId,
|
|
34617
|
-
if (!sessionId.trim() || !fs18.existsSync(
|
|
34618
|
-
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);
|
|
34619
34805
|
const keys = [];
|
|
34620
34806
|
if (preferredKey) keys.push(preferredKey);
|
|
34621
34807
|
try {
|
|
34622
|
-
for (const name of fs18.readdirSync(
|
|
34808
|
+
for (const name of fs18.readdirSync(worktreesRootPath)) {
|
|
34623
34809
|
if (name.startsWith(".")) continue;
|
|
34624
|
-
const p =
|
|
34810
|
+
const p = path18.join(worktreesRootPath, name);
|
|
34625
34811
|
if (!fs18.statSync(p).isDirectory()) continue;
|
|
34626
34812
|
if (name !== preferredKey) keys.push(name);
|
|
34627
34813
|
}
|
|
@@ -34629,26 +34815,58 @@ function discoverSessionWorktreeOnDisk(options) {
|
|
|
34629
34815
|
return null;
|
|
34630
34816
|
}
|
|
34631
34817
|
for (const key of keys) {
|
|
34632
|
-
const
|
|
34633
|
-
if (!fs18.existsSync(
|
|
34634
|
-
const
|
|
34635
|
-
|
|
34636
|
-
|
|
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
|
+
};
|
|
34637
34831
|
}
|
|
34638
34832
|
}
|
|
34639
34833
|
return null;
|
|
34640
34834
|
}
|
|
34641
|
-
function
|
|
34642
|
-
const
|
|
34643
|
-
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;
|
|
34644
34856
|
try {
|
|
34645
|
-
|
|
34857
|
+
st = fs18.statSync(hint);
|
|
34646
34858
|
} catch {
|
|
34647
34859
|
return null;
|
|
34648
34860
|
}
|
|
34649
|
-
|
|
34650
|
-
|
|
34651
|
-
|
|
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
|
+
};
|
|
34652
34870
|
}
|
|
34653
34871
|
|
|
34654
34872
|
// src/worktrees/session-worktree-manager.ts
|
|
@@ -34658,42 +34876,86 @@ function parseSessionParent(v) {
|
|
|
34658
34876
|
return null;
|
|
34659
34877
|
}
|
|
34660
34878
|
var SessionWorktreeManager = class {
|
|
34661
|
-
|
|
34879
|
+
worktreesRootPath;
|
|
34662
34880
|
log;
|
|
34663
|
-
|
|
34664
|
-
|
|
34881
|
+
sessionRepoCheckoutPaths = /* @__PURE__ */ new Map();
|
|
34882
|
+
sessionParentPathBySession = /* @__PURE__ */ new Map();
|
|
34883
|
+
sessionWorkingTreeRelRootBySession = /* @__PURE__ */ new Map();
|
|
34665
34884
|
layout;
|
|
34666
34885
|
constructor(options) {
|
|
34667
|
-
this.
|
|
34886
|
+
this.worktreesRootPath = options.worktreesRootPath;
|
|
34668
34887
|
this.log = options.log;
|
|
34669
34888
|
this.layout = loadWorktreeLayout();
|
|
34670
34889
|
}
|
|
34671
|
-
|
|
34672
|
-
|
|
34673
|
-
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);
|
|
34674
34898
|
}
|
|
34675
34899
|
tryDiscoverFromDisk(sessionId) {
|
|
34676
34900
|
return discoverSessionWorktreeOnDisk({
|
|
34677
34901
|
sessionId,
|
|
34678
|
-
|
|
34902
|
+
worktreesRootPath: this.worktreesRootPath,
|
|
34679
34903
|
layout: this.layout,
|
|
34680
|
-
|
|
34904
|
+
bridgeRoot: getBridgeRoot()
|
|
34681
34905
|
});
|
|
34682
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
|
+
}
|
|
34683
34913
|
/**
|
|
34684
|
-
*
|
|
34914
|
+
* Session parent path for `worktrees_root`: the per-session directory (new layout) or primary checkout (legacy).
|
|
34685
34915
|
*/
|
|
34686
|
-
|
|
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
|
+
}
|
|
34925
|
+
/**
|
|
34926
|
+
* Resolved **session parent path** for the agent: session directory in worktrees mode,
|
|
34927
|
+
* or `undefined` meaning use {@link getBridgeRoot}.
|
|
34928
|
+
*/
|
|
34929
|
+
async resolveSessionParentPathForPrompt(sessionId, opts) {
|
|
34687
34930
|
if (!sessionId) return void 0;
|
|
34931
|
+
const sid = sessionId.trim();
|
|
34688
34932
|
const parentPathRaw = opts.sessionParentPath?.trim();
|
|
34689
34933
|
if (parentPathRaw) {
|
|
34690
|
-
const
|
|
34691
|
-
const sid = sessionId?.trim();
|
|
34934
|
+
const resolved = path19.resolve(parentPathRaw);
|
|
34692
34935
|
if (sid && parseSessionParent(opts.sessionParent) === "worktrees_root") {
|
|
34693
|
-
const
|
|
34694
|
-
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
|
+
}
|
|
34695
34957
|
}
|
|
34696
|
-
return
|
|
34958
|
+
return resolved;
|
|
34697
34959
|
}
|
|
34698
34960
|
const parentKind = parseSessionParent(opts.sessionParent);
|
|
34699
34961
|
if (parentKind === "bridge_root") {
|
|
@@ -34701,78 +34963,102 @@ var SessionWorktreeManager = class {
|
|
|
34701
34963
|
}
|
|
34702
34964
|
if (parentKind === "worktrees_root") {
|
|
34703
34965
|
if (!opts.isNewSession) {
|
|
34704
|
-
const cached2 = this.
|
|
34705
|
-
if (cached2) return
|
|
34706
|
-
const disc = this.tryDiscoverFromDisk(
|
|
34966
|
+
const cached2 = this.sessionParentPathAfterRemember(sid);
|
|
34967
|
+
if (cached2) return cached2;
|
|
34968
|
+
const disc = this.tryDiscoverFromDisk(sid);
|
|
34707
34969
|
if (disc) {
|
|
34708
|
-
this.
|
|
34709
|
-
return
|
|
34970
|
+
this.rememberSessionWorktrees(sid, disc);
|
|
34971
|
+
return this.sessionParentPathAfterRemember(sid);
|
|
34710
34972
|
}
|
|
34711
34973
|
return void 0;
|
|
34712
34974
|
}
|
|
34713
34975
|
const prep2 = await prepareNewSessionWorktrees({
|
|
34714
|
-
|
|
34715
|
-
|
|
34716
|
-
sessionId,
|
|
34976
|
+
worktreesRootPath: this.worktreesRootPath,
|
|
34977
|
+
bridgeRoot: getBridgeRoot(),
|
|
34978
|
+
sessionId: sid,
|
|
34717
34979
|
layout: this.layout,
|
|
34718
34980
|
log: this.log
|
|
34719
34981
|
});
|
|
34720
34982
|
if (!prep2) return void 0;
|
|
34721
|
-
this.
|
|
34722
|
-
|
|
34983
|
+
this.rememberSessionWorktrees(sid, {
|
|
34984
|
+
sessionParentPath: prep2.sessionParentPath,
|
|
34985
|
+
workingTreeRelRoot: prep2.workingTreeRelRoot,
|
|
34986
|
+
repoCheckoutPaths: prep2.worktreePaths
|
|
34987
|
+
});
|
|
34988
|
+
return this.sessionParentPathAfterRemember(sid);
|
|
34723
34989
|
}
|
|
34724
34990
|
if (!opts.isNewSession) {
|
|
34725
|
-
const cached2 = this.
|
|
34726
|
-
if (cached2) return
|
|
34727
|
-
const disc = this.tryDiscoverFromDisk(
|
|
34991
|
+
const cached2 = this.sessionParentPathAfterRemember(sid);
|
|
34992
|
+
if (cached2) return cached2;
|
|
34993
|
+
const disc = this.tryDiscoverFromDisk(sid);
|
|
34728
34994
|
if (disc) {
|
|
34729
|
-
this.
|
|
34730
|
-
return
|
|
34995
|
+
this.rememberSessionWorktrees(sid, disc);
|
|
34996
|
+
return this.sessionParentPathAfterRemember(sid);
|
|
34731
34997
|
}
|
|
34732
34998
|
return void 0;
|
|
34733
34999
|
}
|
|
34734
35000
|
const prep = await prepareNewSessionWorktrees({
|
|
34735
|
-
|
|
34736
|
-
|
|
34737
|
-
sessionId,
|
|
35001
|
+
worktreesRootPath: this.worktreesRootPath,
|
|
35002
|
+
bridgeRoot: getBridgeRoot(),
|
|
35003
|
+
sessionId: sid,
|
|
34738
35004
|
layout: this.layout,
|
|
34739
35005
|
log: this.log
|
|
34740
35006
|
});
|
|
34741
35007
|
if (!prep) return void 0;
|
|
34742
|
-
this.
|
|
34743
|
-
|
|
35008
|
+
this.rememberSessionWorktrees(sid, {
|
|
35009
|
+
sessionParentPath: prep.sessionParentPath,
|
|
35010
|
+
workingTreeRelRoot: prep.workingTreeRelRoot,
|
|
35011
|
+
repoCheckoutPaths: prep.worktreePaths
|
|
35012
|
+
});
|
|
35013
|
+
return this.sessionParentPathAfterRemember(sid);
|
|
34744
35014
|
}
|
|
34745
35015
|
async renameSessionBranch(sessionId, newBranch) {
|
|
34746
|
-
const paths = this.
|
|
35016
|
+
const paths = this.sessionRepoCheckoutPaths.get(sessionId);
|
|
34747
35017
|
if (!paths?.length) return;
|
|
34748
35018
|
await renameSessionWorktreeBranches(paths, newBranch, this.log);
|
|
34749
35019
|
}
|
|
34750
|
-
/** True when this session
|
|
35020
|
+
/** True when this session uses an isolated worktree layout (not the bridge root). */
|
|
34751
35021
|
usesWorktreeSession(sessionId) {
|
|
34752
35022
|
if (!sessionId) return false;
|
|
34753
|
-
return this.
|
|
35023
|
+
return this.sessionParentPathBySession.has(sessionId);
|
|
34754
35024
|
}
|
|
34755
|
-
|
|
35025
|
+
/** Per-repo git checkout directories for this session (for snapshots, commits, change lists). */
|
|
35026
|
+
getRepoCheckoutPathsForSession(sessionId) {
|
|
34756
35027
|
if (!sessionId) return void 0;
|
|
34757
|
-
const paths = this.
|
|
35028
|
+
const paths = this.sessionRepoCheckoutPaths.get(sessionId);
|
|
34758
35029
|
return paths?.length ? [...paths] : void 0;
|
|
34759
35030
|
}
|
|
34760
|
-
/**
|
|
34761
|
-
|
|
34762
|
-
|
|
34763
|
-
|
|
34764
|
-
|
|
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);
|
|
34765
35050
|
}
|
|
34766
35051
|
async removeSessionWorktrees(sessionId) {
|
|
34767
|
-
const paths = this.
|
|
34768
|
-
this.
|
|
34769
|
-
this.
|
|
35052
|
+
const paths = this.sessionRepoCheckoutPaths.get(sessionId);
|
|
35053
|
+
this.sessionRepoCheckoutPaths.delete(sessionId);
|
|
35054
|
+
this.sessionParentPathBySession.delete(sessionId);
|
|
35055
|
+
this.sessionWorkingTreeRelRootBySession.delete(sessionId);
|
|
34770
35056
|
if (!paths?.length) return;
|
|
34771
35057
|
await removeSessionWorktrees(paths, this.log);
|
|
34772
35058
|
}
|
|
34773
35059
|
async commitSession(params) {
|
|
34774
|
-
const paths = this.
|
|
34775
|
-
const targets = paths?.length ? paths : [
|
|
35060
|
+
const paths = this.sessionRepoCheckoutPaths.get(params.sessionId);
|
|
35061
|
+
const targets = paths?.length ? paths : [getBridgeRoot()];
|
|
34776
35062
|
return commitSessionWorktrees({
|
|
34777
35063
|
paths: targets,
|
|
34778
35064
|
branch: params.branch,
|
|
@@ -34781,14 +35067,14 @@ var SessionWorktreeManager = class {
|
|
|
34781
35067
|
});
|
|
34782
35068
|
}
|
|
34783
35069
|
resolveCommitTargets(sessionId) {
|
|
34784
|
-
const paths = this.
|
|
35070
|
+
const paths = this.sessionRepoCheckoutPaths.get(sessionId);
|
|
34785
35071
|
if (paths?.length) return paths;
|
|
34786
35072
|
const disc = this.tryDiscoverFromDisk(sessionId);
|
|
34787
|
-
if (disc?.
|
|
34788
|
-
this.
|
|
34789
|
-
return disc.
|
|
35073
|
+
if (disc?.repoCheckoutPaths.length) {
|
|
35074
|
+
this.rememberSessionWorktrees(sessionId, disc);
|
|
35075
|
+
return disc.repoCheckoutPaths;
|
|
34790
35076
|
}
|
|
34791
|
-
return [
|
|
35077
|
+
return [getBridgeRoot()];
|
|
34792
35078
|
}
|
|
34793
35079
|
async getSessionWorkingTreeStatus(sessionId) {
|
|
34794
35080
|
return aggregateSessionPathsWorkingTreeStatus(this.resolveCommitTargets(sessionId));
|
|
@@ -34796,10 +35082,12 @@ var SessionWorktreeManager = class {
|
|
|
34796
35082
|
/** Per-repo changed files vs HEAD (or a single commit vs parent) for the same git roots used for commit/push. */
|
|
34797
35083
|
async getSessionWorkingTreeChangeDetails(sessionId, opts) {
|
|
34798
35084
|
const targets = this.resolveCommitTargets(sessionId);
|
|
34799
|
-
const
|
|
35085
|
+
const sessionWorkingTreeRelRoot = this.sessionWorkingTreeRelRootBySession.get(sessionId) ?? null;
|
|
35086
|
+
const legacyNested = this.isLegacyNestedLayout(sessionId);
|
|
34800
35087
|
return getWorkingTreeChangeRepoDetails({
|
|
34801
|
-
|
|
34802
|
-
|
|
35088
|
+
commitTargetPaths: targets,
|
|
35089
|
+
sessionWorktreeRootPath: sessionWorkingTreeRelRoot,
|
|
35090
|
+
legacyRepoNestedSessionLayout: legacyNested,
|
|
34803
35091
|
repoFilterRelPath: opts?.repoRelPath?.trim() ? opts.repoRelPath.trim() : null,
|
|
34804
35092
|
basis: opts?.basis
|
|
34805
35093
|
});
|
|
@@ -34814,31 +35102,31 @@ var SessionWorktreeManager = class {
|
|
|
34814
35102
|
}
|
|
34815
35103
|
}
|
|
34816
35104
|
};
|
|
34817
|
-
function
|
|
34818
|
-
return
|
|
35105
|
+
function defaultWorktreesRootPath() {
|
|
35106
|
+
return path19.join(os4.homedir(), ".buildautomaton", "worktrees");
|
|
34819
35107
|
}
|
|
34820
35108
|
|
|
34821
35109
|
// src/files/watch-file-index.ts
|
|
34822
35110
|
import { watch } from "node:fs";
|
|
34823
|
-
import
|
|
35111
|
+
import path26 from "node:path";
|
|
34824
35112
|
|
|
34825
35113
|
// src/files/index/build-file-index.ts
|
|
34826
|
-
import
|
|
35114
|
+
import path23 from "node:path";
|
|
34827
35115
|
|
|
34828
35116
|
// src/runtime/yield-to-event-loop.ts
|
|
34829
35117
|
function yieldToEventLoop() {
|
|
34830
|
-
return new Promise((
|
|
35118
|
+
return new Promise((resolve18) => setImmediate(resolve18));
|
|
34831
35119
|
}
|
|
34832
35120
|
|
|
34833
35121
|
// src/files/index/walk-workspace-tree.ts
|
|
34834
35122
|
import fs19 from "node:fs";
|
|
34835
|
-
import
|
|
35123
|
+
import path21 from "node:path";
|
|
34836
35124
|
|
|
34837
35125
|
// src/files/index/constants.ts
|
|
34838
|
-
import
|
|
35126
|
+
import path20 from "node:path";
|
|
34839
35127
|
import os5 from "node:os";
|
|
34840
35128
|
var INDEX_WORK_YIELD_EVERY = 256;
|
|
34841
|
-
var INDEX_DIR =
|
|
35129
|
+
var INDEX_DIR = path20.join(os5.homedir(), ".buildautomaton");
|
|
34842
35130
|
var INDEX_HASH_LEN = 16;
|
|
34843
35131
|
var INDEX_VERSION = 2;
|
|
34844
35132
|
var INDEX_LOG_PREFIX = "[file-index]";
|
|
@@ -34853,14 +35141,14 @@ function walkWorkspaceTreeSync(dir, baseDir, out) {
|
|
|
34853
35141
|
}
|
|
34854
35142
|
for (const name of names) {
|
|
34855
35143
|
if (name.startsWith(".")) continue;
|
|
34856
|
-
const full =
|
|
35144
|
+
const full = path21.join(dir, name);
|
|
34857
35145
|
let stat3;
|
|
34858
35146
|
try {
|
|
34859
35147
|
stat3 = fs19.statSync(full);
|
|
34860
35148
|
} catch {
|
|
34861
35149
|
continue;
|
|
34862
35150
|
}
|
|
34863
|
-
const relative5 =
|
|
35151
|
+
const relative5 = path21.relative(baseDir, full).replace(/\\/g, "/");
|
|
34864
35152
|
if (stat3.isDirectory()) {
|
|
34865
35153
|
walkWorkspaceTreeSync(full, baseDir, out);
|
|
34866
35154
|
} else if (stat3.isFile()) {
|
|
@@ -34881,14 +35169,14 @@ async function walkWorkspaceTreeAsync(dir, baseDir, out, state) {
|
|
|
34881
35169
|
await yieldToEventLoop();
|
|
34882
35170
|
}
|
|
34883
35171
|
state.n++;
|
|
34884
|
-
const full =
|
|
35172
|
+
const full = path21.join(dir, name);
|
|
34885
35173
|
let stat3;
|
|
34886
35174
|
try {
|
|
34887
35175
|
stat3 = await fs19.promises.stat(full);
|
|
34888
35176
|
} catch {
|
|
34889
35177
|
continue;
|
|
34890
35178
|
}
|
|
34891
|
-
const relative5 =
|
|
35179
|
+
const relative5 = path21.relative(baseDir, full).replace(/\\/g, "/");
|
|
34892
35180
|
if (stat3.isDirectory()) {
|
|
34893
35181
|
await walkWorkspaceTreeAsync(full, baseDir, out, state);
|
|
34894
35182
|
} else if (stat3.isFile()) {
|
|
@@ -34972,11 +35260,11 @@ async function buildTrigramMapForPathsAsync(paths) {
|
|
|
34972
35260
|
import fs20 from "node:fs";
|
|
34973
35261
|
|
|
34974
35262
|
// src/files/index/paths.ts
|
|
34975
|
-
import
|
|
35263
|
+
import path22 from "node:path";
|
|
34976
35264
|
import crypto2 from "node:crypto";
|
|
34977
35265
|
function getIndexPathForCwd(resolvedCwd) {
|
|
34978
35266
|
const hash = crypto2.createHash("sha256").update(resolvedCwd).digest("hex").slice(0, INDEX_HASH_LEN);
|
|
34979
|
-
return
|
|
35267
|
+
return path22.join(INDEX_DIR, `.file-index-${hash}.json`);
|
|
34980
35268
|
}
|
|
34981
35269
|
|
|
34982
35270
|
// src/files/index/write-index-file.ts
|
|
@@ -35007,7 +35295,7 @@ function sortPaths(paths) {
|
|
|
35007
35295
|
paths.sort((a, b) => a.localeCompare(b, void 0, { sensitivity: "base" }));
|
|
35008
35296
|
}
|
|
35009
35297
|
function buildFileIndex(cwd) {
|
|
35010
|
-
const resolved =
|
|
35298
|
+
const resolved = path23.resolve(cwd);
|
|
35011
35299
|
const paths = [];
|
|
35012
35300
|
walkWorkspaceTreeSync(resolved, resolved, paths);
|
|
35013
35301
|
sortPaths(paths);
|
|
@@ -35017,7 +35305,7 @@ function buildFileIndex(cwd) {
|
|
|
35017
35305
|
return data;
|
|
35018
35306
|
}
|
|
35019
35307
|
async function buildFileIndexAsync(cwd) {
|
|
35020
|
-
const resolved =
|
|
35308
|
+
const resolved = path23.resolve(cwd);
|
|
35021
35309
|
const paths = [];
|
|
35022
35310
|
await walkWorkspaceTreeAsync(resolved, resolved, paths, createWalkYieldState());
|
|
35023
35311
|
await yieldToEventLoop();
|
|
@@ -35030,9 +35318,9 @@ async function buildFileIndexAsync(cwd) {
|
|
|
35030
35318
|
|
|
35031
35319
|
// src/files/index/load-file-index.ts
|
|
35032
35320
|
import fs21 from "node:fs";
|
|
35033
|
-
import
|
|
35321
|
+
import path24 from "node:path";
|
|
35034
35322
|
function loadFileIndex(cwd) {
|
|
35035
|
-
const resolved =
|
|
35323
|
+
const resolved = path24.resolve(cwd);
|
|
35036
35324
|
const indexPath = getIndexPathForCwd(resolved);
|
|
35037
35325
|
try {
|
|
35038
35326
|
const raw = fs21.readFileSync(indexPath, "utf8");
|
|
@@ -35054,9 +35342,9 @@ function loadFileIndex(cwd) {
|
|
|
35054
35342
|
}
|
|
35055
35343
|
|
|
35056
35344
|
// src/files/index/ensure-file-index.ts
|
|
35057
|
-
import
|
|
35345
|
+
import path25 from "node:path";
|
|
35058
35346
|
async function ensureFileIndexAsync(cwd) {
|
|
35059
|
-
const resolved =
|
|
35347
|
+
const resolved = path25.resolve(cwd);
|
|
35060
35348
|
const cached2 = loadFileIndex(resolved);
|
|
35061
35349
|
if (cached2 !== null) return { data: cached2, fromCache: true };
|
|
35062
35350
|
const data = await buildFileIndexAsync(resolved);
|
|
@@ -35138,8 +35426,8 @@ function createFsWatcher(resolved, schedule) {
|
|
|
35138
35426
|
throw e;
|
|
35139
35427
|
}
|
|
35140
35428
|
}
|
|
35141
|
-
function startFileIndexWatcher(cwd =
|
|
35142
|
-
const resolved =
|
|
35429
|
+
function startFileIndexWatcher(cwd = getBridgeRoot()) {
|
|
35430
|
+
const resolved = path26.resolve(cwd);
|
|
35143
35431
|
void buildFileIndexAsync(resolved).catch((e) => {
|
|
35144
35432
|
console.error("[file-index] Initial index build failed:", e);
|
|
35145
35433
|
});
|
|
@@ -35189,15 +35477,15 @@ function sendDevServerStatus(getWs, serverId, status, options) {
|
|
|
35189
35477
|
|
|
35190
35478
|
// src/dev-servers/process/terminate-child-process.ts
|
|
35191
35479
|
async function sigtermAndWaitForExit(proc, graceMs, log2, shortId) {
|
|
35192
|
-
const exited = new Promise((
|
|
35193
|
-
proc.once("exit", () =>
|
|
35480
|
+
const exited = new Promise((resolve18) => {
|
|
35481
|
+
proc.once("exit", () => resolve18());
|
|
35194
35482
|
});
|
|
35195
35483
|
log2(`[dev-server] Sending SIGTERM to ${shortId} (pid=${proc.pid ?? "?"}).`);
|
|
35196
35484
|
try {
|
|
35197
35485
|
proc.kill("SIGTERM");
|
|
35198
35486
|
} catch {
|
|
35199
35487
|
}
|
|
35200
|
-
await Promise.race([exited, new Promise((
|
|
35488
|
+
await Promise.race([exited, new Promise((resolve18) => setTimeout(resolve18, graceMs))]);
|
|
35201
35489
|
}
|
|
35202
35490
|
function forceKillChild(proc, log2, shortId, graceMs) {
|
|
35203
35491
|
log2(
|
|
@@ -35477,10 +35765,10 @@ function trySpawnShellTruePiped(command, env, cwd, devNullFd, signal) {
|
|
|
35477
35765
|
import { spawn as spawn6 } from "node:child_process";
|
|
35478
35766
|
import fs24 from "node:fs";
|
|
35479
35767
|
import { tmpdir } from "node:os";
|
|
35480
|
-
import
|
|
35768
|
+
import path27 from "node:path";
|
|
35481
35769
|
function trySpawnMergedLogFile(command, env, cwd, signal) {
|
|
35482
|
-
const tmpRoot = fs24.mkdtempSync(
|
|
35483
|
-
const logPath =
|
|
35770
|
+
const tmpRoot = fs24.mkdtempSync(path27.join(tmpdir(), "ba-devsrv-log-"));
|
|
35771
|
+
const logPath = path27.join(tmpRoot, "combined.log");
|
|
35484
35772
|
let logFd;
|
|
35485
35773
|
try {
|
|
35486
35774
|
logFd = fs24.openSync(logPath, "a");
|
|
@@ -35524,15 +35812,15 @@ function trySpawnMergedLogFile(command, env, cwd, signal) {
|
|
|
35524
35812
|
import { spawn as spawn7 } from "node:child_process";
|
|
35525
35813
|
import fs25 from "node:fs";
|
|
35526
35814
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
35527
|
-
import
|
|
35815
|
+
import path28 from "node:path";
|
|
35528
35816
|
function shSingleQuote(s) {
|
|
35529
35817
|
return `'${s.replace(/'/g, `'\\''`)}'`;
|
|
35530
35818
|
}
|
|
35531
35819
|
function trySpawnShellScriptLogRedirectUnix(command, env, cwd, signal) {
|
|
35532
|
-
const tmpRoot = fs25.mkdtempSync(
|
|
35533
|
-
const logPath =
|
|
35534
|
-
const innerPath =
|
|
35535
|
-
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");
|
|
35536
35824
|
try {
|
|
35537
35825
|
fs25.writeFileSync(innerPath, `#!/bin/sh
|
|
35538
35826
|
${command}
|
|
@@ -35563,9 +35851,9 @@ cd ${shSingleQuote(cwd)}
|
|
|
35563
35851
|
}
|
|
35564
35852
|
}
|
|
35565
35853
|
function trySpawnShellScriptLogRedirectWin(command, env, cwd, signal) {
|
|
35566
|
-
const tmpRoot = fs25.mkdtempSync(
|
|
35567
|
-
const logPath =
|
|
35568
|
-
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");
|
|
35569
35857
|
const q = (p) => `"${p.replace(/"/g, '""')}"`;
|
|
35570
35858
|
const com = process.env.ComSpec || "cmd.exe";
|
|
35571
35859
|
try {
|
|
@@ -35775,13 +36063,13 @@ var DevServerManager = class {
|
|
|
35775
36063
|
abortControllersByServerId = /* @__PURE__ */ new Map();
|
|
35776
36064
|
getWs;
|
|
35777
36065
|
log;
|
|
35778
|
-
|
|
36066
|
+
getBridgeRoot;
|
|
35779
36067
|
e2ee;
|
|
35780
36068
|
firehoseSink;
|
|
35781
36069
|
constructor(options) {
|
|
35782
36070
|
this.getWs = options.getWs;
|
|
35783
36071
|
this.log = options.log;
|
|
35784
|
-
this.
|
|
36072
|
+
this.getBridgeRoot = options.getBridgeRoot ?? (() => process.cwd());
|
|
35785
36073
|
this.e2ee = options.e2ee;
|
|
35786
36074
|
this.firehoseSink = new DevServerFirehoseSink({
|
|
35787
36075
|
getTails: (serverId) => this.snapshotTails(serverId),
|
|
@@ -35870,7 +36158,7 @@ var DevServerManager = class {
|
|
|
35870
36158
|
this.sendStatus(serverId, "starting", void 0, emptyTails());
|
|
35871
36159
|
const ac = new AbortController();
|
|
35872
36160
|
this.abortControllersByServerId.set(serverId, ac);
|
|
35873
|
-
const cwd = this.
|
|
36161
|
+
const cwd = this.getBridgeRoot();
|
|
35874
36162
|
const childEnv = envForSpawn(process.env, def.env, def.ports);
|
|
35875
36163
|
const cmd = substituteCommand(def.command.trim(), childEnv);
|
|
35876
36164
|
const title = def.name.trim() || serverId.slice(0, 8);
|
|
@@ -36080,7 +36368,7 @@ async function proxyToLocal(request) {
|
|
|
36080
36368
|
};
|
|
36081
36369
|
const maxAttempts = isIdempotentProxyMethod(request.method) ? LOCAL_PREVIEW_FETCH_RETRY_DELAYS_MS.length + 1 : 1;
|
|
36082
36370
|
for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
|
|
36083
|
-
const once = await new Promise((
|
|
36371
|
+
const once = await new Promise((resolve18) => {
|
|
36084
36372
|
const req = mod.request(opts, (res) => {
|
|
36085
36373
|
const chunks = [];
|
|
36086
36374
|
res.on("data", (c) => chunks.push(c));
|
|
@@ -36091,7 +36379,7 @@ async function proxyToLocal(request) {
|
|
|
36091
36379
|
if (typeof v === "string") headers[k] = v;
|
|
36092
36380
|
else if (Array.isArray(v) && v[0]) headers[k] = v[0];
|
|
36093
36381
|
}
|
|
36094
|
-
|
|
36382
|
+
resolve18({
|
|
36095
36383
|
id: request.id,
|
|
36096
36384
|
statusCode: res.statusCode ?? 0,
|
|
36097
36385
|
headers,
|
|
@@ -36100,7 +36388,7 @@ async function proxyToLocal(request) {
|
|
|
36100
36388
|
});
|
|
36101
36389
|
});
|
|
36102
36390
|
req.on("error", (err) => {
|
|
36103
|
-
|
|
36391
|
+
resolve18({
|
|
36104
36392
|
id: request.id,
|
|
36105
36393
|
statusCode: 0,
|
|
36106
36394
|
headers: {},
|
|
@@ -36461,13 +36749,13 @@ function createOnBridgeIdentified(opts) {
|
|
|
36461
36749
|
|
|
36462
36750
|
// src/skills/discover-local-agent-skills.ts
|
|
36463
36751
|
import fs26 from "node:fs";
|
|
36464
|
-
import
|
|
36752
|
+
import path29 from "node:path";
|
|
36465
36753
|
var SKILL_DISCOVERY_ROOTS = [".agents/skills", ".claude/skills", ".cursor/skills", "skills"];
|
|
36466
36754
|
function discoverLocalSkills(cwd) {
|
|
36467
36755
|
const out = [];
|
|
36468
36756
|
const seenKeys = /* @__PURE__ */ new Set();
|
|
36469
36757
|
for (const rel of SKILL_DISCOVERY_ROOTS) {
|
|
36470
|
-
const base =
|
|
36758
|
+
const base = path29.join(cwd, rel);
|
|
36471
36759
|
if (!fs26.existsSync(base) || !fs26.statSync(base).isDirectory()) continue;
|
|
36472
36760
|
let entries = [];
|
|
36473
36761
|
try {
|
|
@@ -36476,13 +36764,13 @@ function discoverLocalSkills(cwd) {
|
|
|
36476
36764
|
continue;
|
|
36477
36765
|
}
|
|
36478
36766
|
for (const name of entries) {
|
|
36479
|
-
const dir =
|
|
36767
|
+
const dir = path29.join(base, name);
|
|
36480
36768
|
try {
|
|
36481
36769
|
if (!fs26.statSync(dir).isDirectory()) continue;
|
|
36482
36770
|
} catch {
|
|
36483
36771
|
continue;
|
|
36484
36772
|
}
|
|
36485
|
-
const skillMd =
|
|
36773
|
+
const skillMd = path29.join(dir, "SKILL.md");
|
|
36486
36774
|
if (!fs26.existsSync(skillMd)) continue;
|
|
36487
36775
|
const key = `${rel}/${name}`;
|
|
36488
36776
|
if (seenKeys.has(key)) continue;
|
|
@@ -36495,7 +36783,7 @@ function discoverLocalSkills(cwd) {
|
|
|
36495
36783
|
function discoverSkillLayoutRoots(cwd) {
|
|
36496
36784
|
const roots = [];
|
|
36497
36785
|
for (const rel of SKILL_DISCOVERY_ROOTS) {
|
|
36498
|
-
const base =
|
|
36786
|
+
const base = path29.join(cwd, rel);
|
|
36499
36787
|
if (!fs26.existsSync(base) || !fs26.statSync(base).isDirectory()) continue;
|
|
36500
36788
|
let entries = [];
|
|
36501
36789
|
try {
|
|
@@ -36505,13 +36793,13 @@ function discoverSkillLayoutRoots(cwd) {
|
|
|
36505
36793
|
}
|
|
36506
36794
|
const skills2 = [];
|
|
36507
36795
|
for (const name of entries) {
|
|
36508
|
-
const dir =
|
|
36796
|
+
const dir = path29.join(base, name);
|
|
36509
36797
|
try {
|
|
36510
36798
|
if (!fs26.statSync(dir).isDirectory()) continue;
|
|
36511
36799
|
} catch {
|
|
36512
36800
|
continue;
|
|
36513
36801
|
}
|
|
36514
|
-
if (!fs26.existsSync(
|
|
36802
|
+
if (!fs26.existsSync(path29.join(dir, "SKILL.md"))) continue;
|
|
36515
36803
|
const relPath = `${rel}/${name}`.replace(/\\/g, "/");
|
|
36516
36804
|
skills2.push({ name, relPath });
|
|
36517
36805
|
}
|
|
@@ -36555,7 +36843,7 @@ function createSendLocalSkillsReport(getWs, logFn) {
|
|
|
36555
36843
|
try {
|
|
36556
36844
|
const socket = getWs();
|
|
36557
36845
|
if (!socket || socket.readyState !== wrapper_default.OPEN) return;
|
|
36558
|
-
const skills2 = discoverLocalSkills(
|
|
36846
|
+
const skills2 = discoverLocalSkills(getBridgeRoot());
|
|
36559
36847
|
sendWsMessage(socket, { type: "local_skills", skills: skills2 });
|
|
36560
36848
|
} catch (e) {
|
|
36561
36849
|
logFn(
|
|
@@ -36616,6 +36904,7 @@ var API_TO_BRIDGE_MESSAGE_TYPES = [
|
|
|
36616
36904
|
"dev_servers_config",
|
|
36617
36905
|
"server_control",
|
|
36618
36906
|
"agent_config",
|
|
36907
|
+
"prompt_queue_state",
|
|
36619
36908
|
"prompt",
|
|
36620
36909
|
"session_git_request",
|
|
36621
36910
|
"rename_session_branch",
|
|
@@ -36692,8 +36981,178 @@ var handleAgentConfigMessage = (msg, deps) => {
|
|
|
36692
36981
|
handleBridgeAgentConfig(msg, deps);
|
|
36693
36982
|
};
|
|
36694
36983
|
|
|
36695
|
-
// src/
|
|
36696
|
-
|
|
36984
|
+
// src/prompt-turn-queue/client-report.ts
|
|
36985
|
+
function sendPromptQueueClientReport(ws, queues) {
|
|
36986
|
+
if (!ws) return false;
|
|
36987
|
+
sendWsMessage(ws, { type: "prompt_queue_client_report", queues });
|
|
36988
|
+
return true;
|
|
36989
|
+
}
|
|
36990
|
+
|
|
36991
|
+
// src/prompt-turn-queue/disk-store.ts
|
|
36992
|
+
import fs28 from "node:fs";
|
|
36993
|
+
|
|
36994
|
+
// src/prompt-turn-queue/paths.ts
|
|
36995
|
+
import crypto3 from "node:crypto";
|
|
36996
|
+
import fs27 from "node:fs";
|
|
36997
|
+
import path30 from "node:path";
|
|
36998
|
+
import os6 from "node:os";
|
|
36999
|
+
var QUEUE_KEY_HEX_64 = /^[a-f0-9]{64}$/i;
|
|
37000
|
+
function queueStateFileSlug(queueKey) {
|
|
37001
|
+
if (QUEUE_KEY_HEX_64.test(queueKey)) return queueKey.toLowerCase();
|
|
37002
|
+
return crypto3.createHash("sha256").update(queueKey, "utf8").digest("hex");
|
|
37003
|
+
}
|
|
37004
|
+
function getPromptQueuesDirectory() {
|
|
37005
|
+
const override = process.env.BUILDAMATON_PROMPT_QUEUES_DIR?.trim();
|
|
37006
|
+
if (override) return path30.resolve(override);
|
|
37007
|
+
return path30.join(os6.homedir(), ".buildautomaton", "queues");
|
|
37008
|
+
}
|
|
37009
|
+
function ensurePromptQueuesDirectory() {
|
|
37010
|
+
const dir = getPromptQueuesDirectory();
|
|
37011
|
+
if (!fs27.existsSync(dir)) fs27.mkdirSync(dir, { recursive: true });
|
|
37012
|
+
}
|
|
37013
|
+
function queueStateFilePath(queueKey) {
|
|
37014
|
+
return path30.join(getPromptQueuesDirectory(), `${queueStateFileSlug(queueKey)}.json`);
|
|
37015
|
+
}
|
|
37016
|
+
|
|
37017
|
+
// src/prompt-turn-queue/disk-store.ts
|
|
37018
|
+
function parsePersistedQueueFile(raw) {
|
|
37019
|
+
try {
|
|
37020
|
+
const o = JSON.parse(raw);
|
|
37021
|
+
const queueKey = typeof o.queueKey === "string" ? o.queueKey : typeof o.queueKeyHash === "string" ? o.queueKeyHash : null;
|
|
37022
|
+
if (!queueKey || typeof o.updatedAt !== "string" || !Array.isArray(o.turns)) return null;
|
|
37023
|
+
return { queueKey, updatedAt: o.updatedAt, turns: o.turns };
|
|
37024
|
+
} catch {
|
|
37025
|
+
return null;
|
|
37026
|
+
}
|
|
37027
|
+
}
|
|
37028
|
+
function readPersistedQueue(queueKey) {
|
|
37029
|
+
const p = queueStateFilePath(queueKey);
|
|
37030
|
+
try {
|
|
37031
|
+
return parsePersistedQueueFile(fs28.readFileSync(p, "utf8"));
|
|
37032
|
+
} catch {
|
|
37033
|
+
return null;
|
|
37034
|
+
}
|
|
37035
|
+
}
|
|
37036
|
+
function writePersistedQueue(file2) {
|
|
37037
|
+
ensurePromptQueuesDirectory();
|
|
37038
|
+
const p = queueStateFilePath(file2.queueKey);
|
|
37039
|
+
fs28.writeFileSync(p, JSON.stringify(file2, null, 2), "utf8");
|
|
37040
|
+
}
|
|
37041
|
+
function mergeServerQueueSnapshot(queueKey, serverTurns) {
|
|
37042
|
+
const prev = readPersistedQueue(queueKey);
|
|
37043
|
+
const turns = [];
|
|
37044
|
+
for (const raw of serverTurns) {
|
|
37045
|
+
if (!raw || typeof raw !== "object") continue;
|
|
37046
|
+
const o = raw;
|
|
37047
|
+
const turnId = typeof o.turnId === "string" ? o.turnId : "";
|
|
37048
|
+
const sessionId = typeof o.sessionId === "string" ? o.sessionId : "";
|
|
37049
|
+
const turnOrd = typeof o.turnOrd === "number" ? o.turnOrd : Number(o.turnOrd) || 0;
|
|
37050
|
+
const serverState = o.serverState;
|
|
37051
|
+
const lastClientState = o.lastClientState ?? null;
|
|
37052
|
+
const payload = o.payload && typeof o.payload === "object" ? o.payload : {};
|
|
37053
|
+
if (!turnId || !sessionId) continue;
|
|
37054
|
+
if (serverState !== "queued" && serverState !== "stopping" && serverState !== "discarded") continue;
|
|
37055
|
+
const old = prev?.turns.find((t) => t.turnId === turnId);
|
|
37056
|
+
const mergedClient = old?.lastClientState === "running" && lastClientState == null ? "running" : lastClientState;
|
|
37057
|
+
turns.push({
|
|
37058
|
+
turnId,
|
|
37059
|
+
sessionId,
|
|
37060
|
+
turnOrd,
|
|
37061
|
+
serverState,
|
|
37062
|
+
lastClientState: mergedClient,
|
|
37063
|
+
payload
|
|
37064
|
+
});
|
|
37065
|
+
}
|
|
37066
|
+
turns.sort((a, b) => a.turnOrd - b.turnOrd);
|
|
37067
|
+
return { queueKey, updatedAt: (/* @__PURE__ */ new Date()).toISOString(), turns };
|
|
37068
|
+
}
|
|
37069
|
+
|
|
37070
|
+
// src/prompt-turn-queue/runner.ts
|
|
37071
|
+
var runIdToQueueKey = /* @__PURE__ */ new Map();
|
|
37072
|
+
function pickNextRunnableTurn(turns) {
|
|
37073
|
+
for (const t of turns) {
|
|
37074
|
+
if (t.serverState === "discarded" || t.serverState === "stopping") continue;
|
|
37075
|
+
if (t.serverState !== "queued") continue;
|
|
37076
|
+
if (t.lastClientState === "running" || t.lastClientState === "stopped" || t.lastClientState === "failed") continue;
|
|
37077
|
+
return t;
|
|
37078
|
+
}
|
|
37079
|
+
return null;
|
|
37080
|
+
}
|
|
37081
|
+
function hasRunningTurn(turns) {
|
|
37082
|
+
return turns.some((t) => t.lastClientState === "running");
|
|
37083
|
+
}
|
|
37084
|
+
function dispatchLocalPrompt(next, deps) {
|
|
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;
|
|
37090
|
+
const msg = {
|
|
37091
|
+
type: "prompt",
|
|
37092
|
+
sessionId: next.sessionId,
|
|
37093
|
+
runId: next.turnId,
|
|
37094
|
+
prompt: pl.prompt,
|
|
37095
|
+
mode: typeof pl.mode === "string" ? pl.mode : "agent",
|
|
37096
|
+
isNewSession: pl.isNewSession === true,
|
|
37097
|
+
...sessionParent ? { sessionParent } : {},
|
|
37098
|
+
...sessionParentPath ? { sessionParentPath } : {},
|
|
37099
|
+
...typeof pl.followUpCatalogPromptId === "string" ? { followUpCatalogPromptId: pl.followUpCatalogPromptId } : {},
|
|
37100
|
+
...Array.isArray(pl.sessionChangeSummaryFilePaths) ? { sessionChangeSummaryFilePaths: pl.sessionChangeSummaryFilePaths } : {},
|
|
37101
|
+
...Array.isArray(pl.sessionChangeSummaryFileSnapshots) ? { sessionChangeSummaryFileSnapshots: pl.sessionChangeSummaryFileSnapshots } : {},
|
|
37102
|
+
...typeof pl.agentType === "string" && pl.agentType.trim() ? { agentType: pl.agentType.trim() } : {}
|
|
37103
|
+
};
|
|
37104
|
+
handleBridgePrompt(msg, deps);
|
|
37105
|
+
}
|
|
37106
|
+
function applyPromptQueueStateFromServer(msg, deps) {
|
|
37107
|
+
const raw = msg.queues;
|
|
37108
|
+
if (!raw || typeof raw !== "object") return;
|
|
37109
|
+
const getWs = deps.getWs;
|
|
37110
|
+
for (const [queueKey, serverTurns] of Object.entries(raw)) {
|
|
37111
|
+
if (!Array.isArray(serverTurns)) continue;
|
|
37112
|
+
const file2 = mergeServerQueueSnapshot(queueKey, serverTurns);
|
|
37113
|
+
writePersistedQueue(file2);
|
|
37114
|
+
}
|
|
37115
|
+
const report = {};
|
|
37116
|
+
const startedThisTick = /* @__PURE__ */ new Set();
|
|
37117
|
+
for (const [queueKey, serverTurns] of Object.entries(raw)) {
|
|
37118
|
+
if (!Array.isArray(serverTurns)) continue;
|
|
37119
|
+
const file2 = readPersistedQueue(queueKey);
|
|
37120
|
+
if (!file2) continue;
|
|
37121
|
+
if (hasRunningTurn(file2.turns)) continue;
|
|
37122
|
+
const next = pickNextRunnableTurn(file2.turns);
|
|
37123
|
+
if (!next) continue;
|
|
37124
|
+
next.lastClientState = "running";
|
|
37125
|
+
writePersistedQueue(file2);
|
|
37126
|
+
runIdToQueueKey.set(next.turnId, queueKey);
|
|
37127
|
+
startedThisTick.add(next.turnId);
|
|
37128
|
+
report[queueKey] = [{ turnId: next.turnId, clientState: "running" }];
|
|
37129
|
+
}
|
|
37130
|
+
if (Object.keys(report).length > 0) {
|
|
37131
|
+
sendPromptQueueClientReport(getWs(), report);
|
|
37132
|
+
}
|
|
37133
|
+
for (const [queueKey, serverTurns] of Object.entries(raw)) {
|
|
37134
|
+
if (!Array.isArray(serverTurns)) continue;
|
|
37135
|
+
const file2 = readPersistedQueue(queueKey);
|
|
37136
|
+
if (!file2) continue;
|
|
37137
|
+
const running = file2.turns.find((t) => t.lastClientState === "running");
|
|
37138
|
+
if (!running || !startedThisTick.has(running.turnId)) continue;
|
|
37139
|
+
if (runIdToQueueKey.get(running.turnId) !== queueKey) continue;
|
|
37140
|
+
dispatchLocalPrompt(running, deps);
|
|
37141
|
+
}
|
|
37142
|
+
}
|
|
37143
|
+
function finalizePromptTurnOnBridge(getWs, runId, success2) {
|
|
37144
|
+
if (!runId) return;
|
|
37145
|
+
const queueKey = runIdToQueueKey.get(runId);
|
|
37146
|
+
runIdToQueueKey.delete(runId);
|
|
37147
|
+
if (!queueKey) return;
|
|
37148
|
+
const f = readPersistedQueue(queueKey);
|
|
37149
|
+
if (!f) return;
|
|
37150
|
+
const t = f.turns.find((x) => x.turnId === runId);
|
|
37151
|
+
if (!t) return;
|
|
37152
|
+
t.lastClientState = success2 ? "stopped" : "failed";
|
|
37153
|
+
writePersistedQueue(f);
|
|
37154
|
+
sendPromptQueueClientReport(getWs(), { [queueKey]: [{ turnId: runId, clientState: t.lastClientState }] });
|
|
37155
|
+
}
|
|
36697
37156
|
|
|
36698
37157
|
// src/agents/acp/from-bridge/bridge-prompt-wiring.ts
|
|
36699
37158
|
function createBridgePromptSenders(deps, getWs) {
|
|
@@ -36708,6 +37167,9 @@ function createBridgePromptSenders(deps, getWs) {
|
|
|
36708
37167
|
const skipEncryptForChangeSummaryFollowUp = result.type === "prompt_result" && result.followUpCatalogPromptId === BUILTIN_SESSION_CHANGE_SUMMARY_FOLLOW_UP_CATALOG_PROMPT_ID;
|
|
36709
37168
|
const encryptedFields = result.type === "prompt_result" && !skipEncryptForChangeSummaryFollowUp ? ["output", "error"] : [];
|
|
36710
37169
|
sendBridgeMessage(result, encryptedFields);
|
|
37170
|
+
if (result.type === "prompt_result") {
|
|
37171
|
+
finalizePromptTurnOnBridge(getWs, typeof result.runId === "string" ? result.runId : void 0, result.success === true);
|
|
37172
|
+
}
|
|
36711
37173
|
};
|
|
36712
37174
|
const sendSessionUpdate = (payload) => {
|
|
36713
37175
|
const s = getWs();
|
|
@@ -36732,63 +37194,6 @@ function createBridgePromptSenders(deps, getWs) {
|
|
|
36732
37194
|
// src/agents/acp/from-bridge/bridge-prompt-preamble.ts
|
|
36733
37195
|
import { execFile as execFile10 } from "node:child_process";
|
|
36734
37196
|
import { promisify as promisify10 } from "node:util";
|
|
36735
|
-
|
|
36736
|
-
// src/git/bridge-queue-key.ts
|
|
36737
|
-
import * as path29 from "node:path";
|
|
36738
|
-
import { createHash as createHash2 } from "node:crypto";
|
|
36739
|
-
function normalizeCanonicalGitUrl(url2) {
|
|
36740
|
-
let s = url2.trim();
|
|
36741
|
-
if (!s) return s;
|
|
36742
|
-
if (s.toLowerCase().endsWith(".git")) {
|
|
36743
|
-
s = s.slice(0, -4);
|
|
36744
|
-
}
|
|
36745
|
-
s = s.replace(/\/+$/, "");
|
|
36746
|
-
const httpsMatch = /^(https?):\/\/([^/]+)(\/.*)?$/i.exec(s);
|
|
36747
|
-
if (httpsMatch) {
|
|
36748
|
-
const host = httpsMatch[2].toLowerCase();
|
|
36749
|
-
const p = httpsMatch[3] ?? "";
|
|
36750
|
-
s = `${httpsMatch[1].toLowerCase()}://${host}${p}`;
|
|
36751
|
-
} else {
|
|
36752
|
-
const sshMatch = /^git@([^:]+):(.+)$/i.exec(s);
|
|
36753
|
-
if (sshMatch) {
|
|
36754
|
-
const host = sshMatch[1].toLowerCase();
|
|
36755
|
-
s = `git@${host}:${sshMatch[2]}`;
|
|
36756
|
-
}
|
|
36757
|
-
}
|
|
36758
|
-
return s;
|
|
36759
|
-
}
|
|
36760
|
-
function canonicalUrlToRepoIdSync(url2) {
|
|
36761
|
-
const normalized = normalizeCanonicalGitUrl(url2);
|
|
36762
|
-
return createHash2("sha256").update(normalized).digest("hex").slice(0, 32);
|
|
36763
|
-
}
|
|
36764
|
-
function fallbackRepoIdFromPath(absPath) {
|
|
36765
|
-
return createHash2("sha256").update(path29.resolve(absPath)).digest("hex").slice(0, 32);
|
|
36766
|
-
}
|
|
36767
|
-
async function resolveBridgeQueueBindFields(options) {
|
|
36768
|
-
const { effectiveCwd, worktreePaths, primaryRepoRoots, log: log2 } = options;
|
|
36769
|
-
const cwdAbs = worktreePaths.length > 0 ? path29.resolve(worktreePaths[0]) : path29.resolve(effectiveCwd);
|
|
36770
|
-
if (!primaryRepoRoots.length) {
|
|
36771
|
-
log2("[Bridge service] Prompt queue bind skipped: no Git repository roots under the working directory.");
|
|
36772
|
-
return null;
|
|
36773
|
-
}
|
|
36774
|
-
let primaryRoot = primaryRepoRoots[0];
|
|
36775
|
-
let remote = await getRemoteOriginUrl(primaryRoot);
|
|
36776
|
-
if (!remote) {
|
|
36777
|
-
for (const r of primaryRepoRoots.slice(1)) {
|
|
36778
|
-
const u = await getRemoteOriginUrl(r);
|
|
36779
|
-
if (u) {
|
|
36780
|
-
primaryRoot = r;
|
|
36781
|
-
remote = u;
|
|
36782
|
-
break;
|
|
36783
|
-
}
|
|
36784
|
-
}
|
|
36785
|
-
}
|
|
36786
|
-
const repoId = remote ? canonicalUrlToRepoIdSync(remote) : fallbackRepoIdFromPath(primaryRoot);
|
|
36787
|
-
const canonicalQueueKey = `repo:${repoId}::cwd:${cwdAbs}`;
|
|
36788
|
-
return { canonicalQueueKey, repoId, cwdAbs };
|
|
36789
|
-
}
|
|
36790
|
-
|
|
36791
|
-
// src/agents/acp/from-bridge/bridge-prompt-preamble.ts
|
|
36792
37197
|
var execFileAsync9 = promisify10(execFile10);
|
|
36793
37198
|
async function readGitBranch(cwd) {
|
|
36794
37199
|
try {
|
|
@@ -36802,40 +37207,24 @@ async function readGitBranch(cwd) {
|
|
|
36802
37207
|
async function runBridgePromptPreamble(params) {
|
|
36803
37208
|
const { getWs, log: log2, sessionWorktreeManager, sessionId, runId, effectiveCwd } = params;
|
|
36804
37209
|
const s = getWs();
|
|
36805
|
-
const
|
|
37210
|
+
const repoCheckoutPaths = sessionWorktreeManager.ensureRepoCheckoutPathsForSession(sessionId);
|
|
36806
37211
|
const repoRoots = await resolveSnapshotRepoRoots({
|
|
36807
|
-
worktreePaths,
|
|
37212
|
+
worktreePaths: repoCheckoutPaths,
|
|
36808
37213
|
fallbackCwd: effectiveCwd,
|
|
37214
|
+
sessionId: sessionId?.trim() || void 0,
|
|
36809
37215
|
log: log2
|
|
36810
37216
|
});
|
|
36811
|
-
if (s && sessionId) {
|
|
36812
|
-
const bind = await resolveBridgeQueueBindFields({
|
|
36813
|
-
effectiveCwd,
|
|
36814
|
-
worktreePaths,
|
|
36815
|
-
primaryRepoRoots: repoRoots,
|
|
36816
|
-
log: log2
|
|
36817
|
-
});
|
|
36818
|
-
if (bind) {
|
|
36819
|
-
sendWsMessage(s, {
|
|
36820
|
-
type: "bridge_queue_bind",
|
|
36821
|
-
sessionId,
|
|
36822
|
-
canonicalQueueKey: bind.canonicalQueueKey,
|
|
36823
|
-
repoId: bind.repoId,
|
|
36824
|
-
cwdAbs: bind.cwdAbs
|
|
36825
|
-
});
|
|
36826
|
-
}
|
|
36827
|
-
}
|
|
36828
37217
|
if (s && sessionId) {
|
|
36829
37218
|
const cliGitBranch = await readGitBranch(effectiveCwd);
|
|
36830
37219
|
const usesWt = sessionWorktreeManager.usesWorktreeSession(sessionId);
|
|
36831
|
-
const
|
|
37220
|
+
const isolatedSessionParentPath = sessionWorktreeManager.getIsolatedSessionParentPathForSession(sessionId);
|
|
36832
37221
|
sendWsMessage(s, {
|
|
36833
37222
|
type: "session_git_context_report",
|
|
36834
37223
|
sessionId,
|
|
36835
37224
|
cliGitBranch,
|
|
36836
37225
|
agentUsesWorktree: usesWt,
|
|
36837
37226
|
sessionParent: usesWt ? "worktrees_root" : "bridge_root",
|
|
36838
|
-
sessionParentPath: usesWt ?
|
|
37227
|
+
sessionParentPath: usesWt ? isolatedSessionParentPath ?? effectiveCwd : getBridgeRoot()
|
|
36839
37228
|
});
|
|
36840
37229
|
}
|
|
36841
37230
|
if (s && sessionId && runId) {
|
|
@@ -36962,7 +37351,7 @@ function handleBridgePrompt(msg, deps) {
|
|
|
36962
37351
|
const mode = typeof msg.mode === "string" && msg.mode.trim() ? msg.mode.trim() : void 0;
|
|
36963
37352
|
acpManager.logPromptReceivedFromBridge({ agentType, mode });
|
|
36964
37353
|
async function preambleAndPrompt(resolvedCwd) {
|
|
36965
|
-
const effectiveCwd =
|
|
37354
|
+
const effectiveCwd = resolveSessionParentPathForAgentProcess(resolvedCwd);
|
|
36966
37355
|
await runBridgePromptPreamble({
|
|
36967
37356
|
getWs,
|
|
36968
37357
|
log: log2,
|
|
@@ -36995,7 +37384,7 @@ function handleBridgePrompt(msg, deps) {
|
|
|
36995
37384
|
runId,
|
|
36996
37385
|
mode,
|
|
36997
37386
|
agentType,
|
|
36998
|
-
|
|
37387
|
+
sessionParentPath: effectiveCwd,
|
|
36999
37388
|
sendResult: sendResult2,
|
|
37000
37389
|
sendSessionUpdate,
|
|
37001
37390
|
followUpCatalogPromptId,
|
|
@@ -37005,8 +37394,8 @@ function handleBridgePrompt(msg, deps) {
|
|
|
37005
37394
|
e2ee: deps.e2ee
|
|
37006
37395
|
});
|
|
37007
37396
|
}
|
|
37008
|
-
void sessionWorktreeManager.
|
|
37009
|
-
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)}`);
|
|
37010
37399
|
void preambleAndPrompt(void 0);
|
|
37011
37400
|
});
|
|
37012
37401
|
}
|
|
@@ -37016,6 +37405,11 @@ var handlePromptMessage = (msg, deps) => {
|
|
|
37016
37405
|
handleBridgePrompt(msg, deps);
|
|
37017
37406
|
};
|
|
37018
37407
|
|
|
37408
|
+
// src/bridge/routing/handlers/prompt-queue-state.ts
|
|
37409
|
+
var handlePromptQueueStateMessage = (msg, deps) => {
|
|
37410
|
+
applyPromptQueueStateFromServer(msg, deps);
|
|
37411
|
+
};
|
|
37412
|
+
|
|
37019
37413
|
// src/agents/acp/from-bridge/handle-bridge-cancel-run.ts
|
|
37020
37414
|
async function handleBridgeCancelRun(msg, { log: log2, acpManager, getWs }) {
|
|
37021
37415
|
const runId = msg.runId;
|
|
@@ -37035,6 +37429,7 @@ async function handleBridgeCancelRun(msg, { log: log2, acpManager, getWs }) {
|
|
|
37035
37429
|
error: "Stopped by user",
|
|
37036
37430
|
stopReason: "no_local_run"
|
|
37037
37431
|
});
|
|
37432
|
+
finalizePromptTurnOnBridge(getWs, runId, false);
|
|
37038
37433
|
}
|
|
37039
37434
|
|
|
37040
37435
|
// src/bridge/routing/handlers/cancel-run.ts
|
|
@@ -37134,7 +37529,7 @@ var previewSkill = {
|
|
|
37134
37529
|
const exe = parts[0];
|
|
37135
37530
|
const args = parts.slice(1);
|
|
37136
37531
|
previewProcess = spawn9(isWindows && exe === "npm" ? "npm.cmd" : exe, args, {
|
|
37137
|
-
cwd:
|
|
37532
|
+
cwd: getBridgeRoot(),
|
|
37138
37533
|
stdio: ["ignore", "pipe", "pipe"],
|
|
37139
37534
|
env: {
|
|
37140
37535
|
...process.env,
|
|
@@ -37252,12 +37647,12 @@ var handleSkillCallMessage = (msg, { getWs, log: log2 }) => {
|
|
|
37252
37647
|
};
|
|
37253
37648
|
|
|
37254
37649
|
// src/files/list-dir.ts
|
|
37255
|
-
import
|
|
37650
|
+
import fs29 from "node:fs";
|
|
37256
37651
|
import path32 from "node:path";
|
|
37257
37652
|
|
|
37258
37653
|
// src/files/ensure-under-cwd.ts
|
|
37259
37654
|
import path31 from "node:path";
|
|
37260
|
-
function ensureUnderCwd(relativePath, cwd =
|
|
37655
|
+
function ensureUnderCwd(relativePath, cwd = getBridgeRoot()) {
|
|
37261
37656
|
const normalized = path31.normalize(relativePath).replace(/^(\.\/)+/, "");
|
|
37262
37657
|
const resolved = path31.resolve(cwd, normalized);
|
|
37263
37658
|
if (!resolved.startsWith(cwd + path31.sep) && resolved !== cwd) {
|
|
@@ -37269,12 +37664,12 @@ function ensureUnderCwd(relativePath, cwd = getBridgeWorkspaceDirectory()) {
|
|
|
37269
37664
|
// src/files/list-dir.ts
|
|
37270
37665
|
var LIST_DIR_YIELD_EVERY = 256;
|
|
37271
37666
|
async function listDirAsync(relativePath) {
|
|
37272
|
-
const resolved = ensureUnderCwd(relativePath || ".",
|
|
37667
|
+
const resolved = ensureUnderCwd(relativePath || ".", getBridgeRoot());
|
|
37273
37668
|
if (!resolved) {
|
|
37274
37669
|
return { error: "Path is outside working directory" };
|
|
37275
37670
|
}
|
|
37276
37671
|
try {
|
|
37277
|
-
const names = await
|
|
37672
|
+
const names = await fs29.promises.readdir(resolved, { withFileTypes: true });
|
|
37278
37673
|
const visible = names.filter((d) => !d.name.startsWith("."));
|
|
37279
37674
|
const entries = [];
|
|
37280
37675
|
for (let i = 0; i < visible.length; i++) {
|
|
@@ -37287,7 +37682,7 @@ async function listDirAsync(relativePath) {
|
|
|
37287
37682
|
let isDir = d.isDirectory();
|
|
37288
37683
|
if (d.isSymbolicLink()) {
|
|
37289
37684
|
try {
|
|
37290
|
-
const targetStat = await
|
|
37685
|
+
const targetStat = await fs29.promises.stat(fullPath);
|
|
37291
37686
|
isDir = targetStat.isDirectory();
|
|
37292
37687
|
} catch {
|
|
37293
37688
|
isDir = false;
|
|
@@ -37312,25 +37707,25 @@ async function listDirAsync(relativePath) {
|
|
|
37312
37707
|
}
|
|
37313
37708
|
|
|
37314
37709
|
// src/files/read-file.ts
|
|
37315
|
-
import
|
|
37710
|
+
import fs30 from "node:fs";
|
|
37316
37711
|
import { StringDecoder } from "node:string_decoder";
|
|
37317
37712
|
function resolveFilePath(relativePath) {
|
|
37318
|
-
const resolved = ensureUnderCwd(relativePath,
|
|
37713
|
+
const resolved = ensureUnderCwd(relativePath, getBridgeRoot());
|
|
37319
37714
|
if (!resolved) return { error: "Path is outside working directory" };
|
|
37320
37715
|
let real;
|
|
37321
37716
|
try {
|
|
37322
|
-
real =
|
|
37717
|
+
real = fs30.realpathSync(resolved);
|
|
37323
37718
|
} catch {
|
|
37324
37719
|
real = resolved;
|
|
37325
37720
|
}
|
|
37326
|
-
const stat3 =
|
|
37721
|
+
const stat3 = fs30.statSync(real);
|
|
37327
37722
|
if (!stat3.isFile()) return { error: "Not a file" };
|
|
37328
37723
|
return real;
|
|
37329
37724
|
}
|
|
37330
37725
|
var LINE_CHUNK_SIZE = 64 * 1024;
|
|
37331
37726
|
function readFileRange(filePath, startLine, endLine, lineOffsetIn, lineChunkSize = LINE_CHUNK_SIZE) {
|
|
37332
|
-
const fileSize =
|
|
37333
|
-
const fd =
|
|
37727
|
+
const fileSize = fs30.statSync(filePath).size;
|
|
37728
|
+
const fd = fs30.openSync(filePath, "r");
|
|
37334
37729
|
const bufSize = 64 * 1024;
|
|
37335
37730
|
const buf = Buffer.alloc(bufSize);
|
|
37336
37731
|
const decoder = new StringDecoder("utf8");
|
|
@@ -37343,7 +37738,7 @@ function readFileRange(filePath, startLine, endLine, lineOffsetIn, lineChunkSize
|
|
|
37343
37738
|
let line0Accum = "";
|
|
37344
37739
|
try {
|
|
37345
37740
|
let bytesRead;
|
|
37346
|
-
while (!done && (bytesRead =
|
|
37741
|
+
while (!done && (bytesRead = fs30.readSync(fd, buf, 0, bufSize, null)) > 0) {
|
|
37347
37742
|
const text = partial2 + decoder.write(buf.subarray(0, bytesRead));
|
|
37348
37743
|
partial2 = "";
|
|
37349
37744
|
let lineStart = 0;
|
|
@@ -37478,7 +37873,7 @@ function readFileRange(filePath, startLine, endLine, lineOffsetIn, lineChunkSize
|
|
|
37478
37873
|
}
|
|
37479
37874
|
return { content: resultLines.join("\n"), size: fileSize };
|
|
37480
37875
|
} finally {
|
|
37481
|
-
|
|
37876
|
+
fs30.closeSync(fd);
|
|
37482
37877
|
}
|
|
37483
37878
|
}
|
|
37484
37879
|
function readFile3(relativePath, startLine, endLine, lineOffset, lineChunkSize = LINE_CHUNK_SIZE) {
|
|
@@ -37489,8 +37884,8 @@ function readFile3(relativePath, startLine, endLine, lineOffset, lineChunkSize =
|
|
|
37489
37884
|
if (hasRange) {
|
|
37490
37885
|
return readFileRange(result, startLine, endLine, lineOffset, lineChunkSize);
|
|
37491
37886
|
}
|
|
37492
|
-
const stat3 =
|
|
37493
|
-
const raw =
|
|
37887
|
+
const stat3 = fs30.statSync(result);
|
|
37888
|
+
const raw = fs30.readFileSync(result, "utf8");
|
|
37494
37889
|
const lines = raw.split(/\r?\n/);
|
|
37495
37890
|
return { content: raw, totalLines: lines.length, size: stat3.size };
|
|
37496
37891
|
} catch (err) {
|
|
@@ -37508,7 +37903,7 @@ function handleFileBrowserSearch(msg, socket, e2ee) {
|
|
|
37508
37903
|
void (async () => {
|
|
37509
37904
|
await yieldToEventLoop();
|
|
37510
37905
|
const q = typeof msg.q === "string" ? msg.q : "";
|
|
37511
|
-
const cwd =
|
|
37906
|
+
const cwd = getBridgeRoot();
|
|
37512
37907
|
const index = loadFileIndex(cwd);
|
|
37513
37908
|
if (index === null) {
|
|
37514
37909
|
const payload2 = {
|
|
@@ -37532,7 +37927,7 @@ function handleFileBrowserSearch(msg, socket, e2ee) {
|
|
|
37532
37927
|
}
|
|
37533
37928
|
function triggerFileIndexBuild() {
|
|
37534
37929
|
setImmediate(() => {
|
|
37535
|
-
void ensureFileIndexAsync(
|
|
37930
|
+
void ensureFileIndexAsync(getBridgeRoot()).catch((e) => {
|
|
37536
37931
|
console.error("[file-index] Background build failed:", e);
|
|
37537
37932
|
});
|
|
37538
37933
|
});
|
|
@@ -37601,14 +37996,14 @@ function handleFileBrowserSearchMessage(msg, { getWs, e2ee }) {
|
|
|
37601
37996
|
function handleSkillLayoutRequest(msg, deps) {
|
|
37602
37997
|
const socket = deps.getWs();
|
|
37603
37998
|
const id = typeof msg.id === "string" ? msg.id : "";
|
|
37604
|
-
const roots = discoverSkillLayoutRoots(
|
|
37999
|
+
const roots = discoverSkillLayoutRoots(getBridgeRoot());
|
|
37605
38000
|
if (socket) {
|
|
37606
38001
|
sendWsMessage(socket, { type: "skill_layout_response", id, roots });
|
|
37607
38002
|
}
|
|
37608
38003
|
}
|
|
37609
38004
|
|
|
37610
38005
|
// src/skills/install-remote-skills.ts
|
|
37611
|
-
import
|
|
38006
|
+
import fs31 from "node:fs";
|
|
37612
38007
|
import path33 from "node:path";
|
|
37613
38008
|
function installRemoteSkills(cwd, targetDir, items) {
|
|
37614
38009
|
const installed2 = [];
|
|
@@ -37624,11 +38019,11 @@ function installRemoteSkills(cwd, targetDir, items) {
|
|
|
37624
38019
|
for (const f of item.files) {
|
|
37625
38020
|
if (typeof f.path !== "string" || !f.text && !f.base64) continue;
|
|
37626
38021
|
const dest = path33.join(skillDir, f.path);
|
|
37627
|
-
|
|
38022
|
+
fs31.mkdirSync(path33.dirname(dest), { recursive: true });
|
|
37628
38023
|
if (f.text !== void 0) {
|
|
37629
|
-
|
|
38024
|
+
fs31.writeFileSync(dest, f.text, "utf8");
|
|
37630
38025
|
} else if (f.base64) {
|
|
37631
|
-
|
|
38026
|
+
fs31.writeFileSync(dest, Buffer.from(f.base64, "base64"));
|
|
37632
38027
|
}
|
|
37633
38028
|
}
|
|
37634
38029
|
installed2.push({
|
|
@@ -37649,7 +38044,7 @@ var handleInstallSkillsMessage = (msg, deps) => {
|
|
|
37649
38044
|
const id = typeof msg.id === "string" ? msg.id : "";
|
|
37650
38045
|
const targetDir = typeof msg.targetDir === "string" && msg.targetDir.trim() ? msg.targetDir.trim() : ".agents/skills";
|
|
37651
38046
|
const rawItems = msg.items;
|
|
37652
|
-
const cwd =
|
|
38047
|
+
const cwd = getBridgeRoot();
|
|
37653
38048
|
const result = installRemoteSkills(cwd, targetDir, rawItems);
|
|
37654
38049
|
if (!result.success) {
|
|
37655
38050
|
const err = result.error ?? "Invalid items";
|
|
@@ -37778,7 +38173,7 @@ var handleSessionDiscardedMessage = (msg, deps) => {
|
|
|
37778
38173
|
};
|
|
37779
38174
|
|
|
37780
38175
|
// src/bridge/routing/handlers/revert-turn-snapshot.ts
|
|
37781
|
-
import * as
|
|
38176
|
+
import * as fs32 from "node:fs";
|
|
37782
38177
|
var handleRevertTurnSnapshotMessage = (msg, deps) => {
|
|
37783
38178
|
const id = typeof msg.id === "string" ? msg.id : "";
|
|
37784
38179
|
const sessionId = typeof msg.sessionId === "string" ? msg.sessionId : "";
|
|
@@ -37788,9 +38183,9 @@ var handleRevertTurnSnapshotMessage = (msg, deps) => {
|
|
|
37788
38183
|
void (async () => {
|
|
37789
38184
|
const s = getWs();
|
|
37790
38185
|
if (!s) return;
|
|
37791
|
-
const agentBase = sessionWorktreeManager.
|
|
38186
|
+
const agentBase = sessionWorktreeManager.getSessionWorktreeRootForSession(sessionId) ?? getBridgeRoot();
|
|
37792
38187
|
const file2 = snapshotFilePath(agentBase, turnId);
|
|
37793
|
-
if (!
|
|
38188
|
+
if (!fs32.existsSync(file2)) {
|
|
37794
38189
|
sendWsMessage(s, {
|
|
37795
38190
|
type: "revert_turn_snapshot_result",
|
|
37796
38191
|
id,
|
|
@@ -37848,6 +38243,9 @@ function dispatchBridgeMessage(msg, deps) {
|
|
|
37848
38243
|
case "agent_config":
|
|
37849
38244
|
handleAgentConfigMessage(msg, deps);
|
|
37850
38245
|
break;
|
|
38246
|
+
case "prompt_queue_state":
|
|
38247
|
+
handlePromptQueueStateMessage(msg, deps);
|
|
38248
|
+
break;
|
|
37851
38249
|
case "prompt":
|
|
37852
38250
|
handlePromptMessage(msg, deps);
|
|
37853
38251
|
break;
|
|
@@ -38077,9 +38475,9 @@ async function createBridgeConnection(options) {
|
|
|
38077
38475
|
firehoseGeneration: 0,
|
|
38078
38476
|
firehoseQuiet: createEmptyReconnectQuietSlot()
|
|
38079
38477
|
};
|
|
38080
|
-
const
|
|
38478
|
+
const worktreesRootPath = options.worktreesRootPath ?? defaultWorktreesRootPath();
|
|
38081
38479
|
const sessionWorktreeManager = new SessionWorktreeManager({
|
|
38082
|
-
|
|
38480
|
+
worktreesRootPath,
|
|
38083
38481
|
log: logFn
|
|
38084
38482
|
});
|
|
38085
38483
|
const acpManager = await createAcpManager({ log: logFn });
|
|
@@ -38088,7 +38486,7 @@ async function createBridgeConnection(options) {
|
|
|
38088
38486
|
return state.currentWs;
|
|
38089
38487
|
}
|
|
38090
38488
|
const e2ee = options.e2eCertificate ? createCliE2eeRuntime(options.e2eCertificate) : void 0;
|
|
38091
|
-
const devServerManager = new DevServerManager({ getWs, log: logFn,
|
|
38489
|
+
const devServerManager = new DevServerManager({ getWs, log: logFn, getBridgeRoot, e2ee });
|
|
38092
38490
|
const onBridgeIdentified = createOnBridgeIdentified({
|
|
38093
38491
|
devServerManager,
|
|
38094
38492
|
firehoseServerUrl,
|
|
@@ -38112,8 +38510,8 @@ async function createBridgeConnection(options) {
|
|
|
38112
38510
|
getCloudAccessToken: () => tokens.accessToken
|
|
38113
38511
|
};
|
|
38114
38512
|
const identifyReportedPaths = {
|
|
38115
|
-
bridgeRootPath: path34.resolve(
|
|
38116
|
-
worktreesRootPath: path34.resolve(
|
|
38513
|
+
bridgeRootPath: path34.resolve(getBridgeRoot()),
|
|
38514
|
+
worktreesRootPath: path34.resolve(worktreesRootPath)
|
|
38117
38515
|
};
|
|
38118
38516
|
const { connect } = createMainBridgeWebSocketLifecycle({
|
|
38119
38517
|
state,
|
|
@@ -38130,7 +38528,7 @@ async function createBridgeConnection(options) {
|
|
|
38130
38528
|
identifyReportedPaths
|
|
38131
38529
|
});
|
|
38132
38530
|
connect();
|
|
38133
|
-
const stopFileIndexWatcher = startFileIndexWatcher(
|
|
38531
|
+
const stopFileIndexWatcher = startFileIndexWatcher(getBridgeRoot());
|
|
38134
38532
|
return {
|
|
38135
38533
|
close: async () => {
|
|
38136
38534
|
stopFileIndexWatcher();
|
|
@@ -38200,7 +38598,7 @@ async function runConnectedBridge(options, restartWithoutAuth) {
|
|
|
38200
38598
|
authToken,
|
|
38201
38599
|
refreshToken,
|
|
38202
38600
|
justAuthenticated,
|
|
38203
|
-
|
|
38601
|
+
worktreesRootPath,
|
|
38204
38602
|
e2eCertificate
|
|
38205
38603
|
} = options;
|
|
38206
38604
|
const firehoseServerUrl = options.firehoseServerUrl ?? options.proxyServerUrl;
|
|
@@ -38212,7 +38610,7 @@ async function runConnectedBridge(options, restartWithoutAuth) {
|
|
|
38212
38610
|
refreshToken,
|
|
38213
38611
|
firehoseServerUrl,
|
|
38214
38612
|
justAuthenticated,
|
|
38215
|
-
|
|
38613
|
+
worktreesRootPath,
|
|
38216
38614
|
e2eCertificate,
|
|
38217
38615
|
log,
|
|
38218
38616
|
persistTokens: (t) => {
|
|
@@ -38227,7 +38625,7 @@ async function runConnectedBridge(options, restartWithoutAuth) {
|
|
|
38227
38625
|
log("[Bridge service] Access token invalid or revoked; re-authenticating\u2026");
|
|
38228
38626
|
clearConfigForApi(apiUrl);
|
|
38229
38627
|
void handle.close().then(() => {
|
|
38230
|
-
void restartWithoutAuth({ apiUrl, firehoseServerUrl,
|
|
38628
|
+
void restartWithoutAuth({ apiUrl, firehoseServerUrl, worktreesRootPath, e2eCertificate });
|
|
38231
38629
|
});
|
|
38232
38630
|
}
|
|
38233
38631
|
});
|
|
@@ -38275,7 +38673,7 @@ async function runBridge(options) {
|
|
|
38275
38673
|
workspaceId,
|
|
38276
38674
|
authToken,
|
|
38277
38675
|
bridgeName,
|
|
38278
|
-
|
|
38676
|
+
worktreesRootPath,
|
|
38279
38677
|
e2eCertificate
|
|
38280
38678
|
} = options;
|
|
38281
38679
|
const firehoseServerUrl = options.firehoseServerUrl ?? options.proxyServerUrl;
|
|
@@ -38320,7 +38718,7 @@ async function runBridge(options) {
|
|
|
38320
38718
|
firehoseServerUrl,
|
|
38321
38719
|
bridgeName,
|
|
38322
38720
|
justAuthenticated: true,
|
|
38323
|
-
|
|
38721
|
+
worktreesRootPath,
|
|
38324
38722
|
e2eCertificate
|
|
38325
38723
|
});
|
|
38326
38724
|
return;
|
|
@@ -38344,30 +38742,31 @@ async function runCliAction(program2, opts) {
|
|
|
38344
38742
|
let workspaceId = opts.workspaceId ?? "";
|
|
38345
38743
|
let authToken = opts.token;
|
|
38346
38744
|
const firehoseServerUrl = opts.firehoseUrl ?? opts.proxyUrl ?? process.env.BUILDAUTOMATON_FIREHOSE_URL ?? process.env.BUILDAUTOMATON_PROXY_URL ?? DEFAULT_FIREHOSE_URL;
|
|
38347
|
-
|
|
38348
|
-
|
|
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);
|
|
38349
38748
|
try {
|
|
38350
|
-
const st =
|
|
38749
|
+
const st = fs33.statSync(resolvedBridgeRoot);
|
|
38351
38750
|
if (!st.isDirectory()) {
|
|
38352
|
-
console.error(
|
|
38751
|
+
console.error(`Bridge root is not a directory: ${resolvedBridgeRoot}`);
|
|
38353
38752
|
process.exit(1);
|
|
38354
38753
|
}
|
|
38355
38754
|
} catch {
|
|
38356
|
-
console.error(
|
|
38755
|
+
console.error(`Bridge root path does not exist or is not accessible: ${resolvedBridgeRoot}`);
|
|
38357
38756
|
process.exit(1);
|
|
38358
38757
|
}
|
|
38359
|
-
process.chdir(
|
|
38758
|
+
process.chdir(resolvedBridgeRoot);
|
|
38360
38759
|
}
|
|
38361
|
-
|
|
38760
|
+
initBridgeRoot();
|
|
38362
38761
|
console.log(
|
|
38363
38762
|
colorize(
|
|
38364
38763
|
GOLDEN_YELLOW,
|
|
38365
|
-
`[
|
|
38764
|
+
`[Bridge] Using ${getBridgeRoot()} as the bridge root (repos, file index, agents, and bridge_root sessions).`
|
|
38366
38765
|
)
|
|
38367
38766
|
);
|
|
38368
|
-
let
|
|
38767
|
+
let worktreesRootPath;
|
|
38369
38768
|
if (opts.worktreesRoot && opts.worktreesRoot.trim()) {
|
|
38370
|
-
|
|
38769
|
+
worktreesRootPath = path35.resolve(opts.worktreesRoot.trim());
|
|
38371
38770
|
}
|
|
38372
38771
|
const e2eCertificates = opts.e2eeCertificatesDir?.trim() ? await loadOrCreateE2eCertificates(opts.e2eeCertificatesDir.trim()) : void 0;
|
|
38373
38772
|
if (e2eCertificates) {
|
|
@@ -38402,7 +38801,7 @@ async function runCliAction(program2, opts) {
|
|
|
38402
38801
|
refreshToken,
|
|
38403
38802
|
firehoseServerUrl,
|
|
38404
38803
|
bridgeName: opts.name?.trim() || void 0,
|
|
38405
|
-
|
|
38804
|
+
worktreesRootPath,
|
|
38406
38805
|
e2eCertificate: e2eCertificates?.activeCertificate
|
|
38407
38806
|
});
|
|
38408
38807
|
}
|
|
@@ -38415,8 +38814,11 @@ async function main() {
|
|
|
38415
38814
|
"Firehose server URL (default: Fly app; or BUILDAUTOMATON_FIREHOSE_URL / legacy BUILDAUTOMATON_PROXY_URL)",
|
|
38416
38815
|
process.env.BUILDAUTOMATON_FIREHOSE_URL ?? process.env.BUILDAUTOMATON_PROXY_URL ?? DEFAULT_FIREHOSE_URL
|
|
38417
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(
|
|
38418
38820
|
"--cwd <path>",
|
|
38419
|
-
"
|
|
38821
|
+
"Deprecated: same as --bridge-root"
|
|
38420
38822
|
).option("-n, --name <name>", "Bridge name when creating via browser (alphanumeric and underscores only)").option(
|
|
38421
38823
|
"--worktrees-root <path>",
|
|
38422
38824
|
"Root directory for per-session git worktrees (default: ~/.buildautomaton/worktrees)."
|