@buildautomaton/cli 0.1.38 → 0.1.40
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 +1306 -870
- package/dist/cli.js.map +4 -4
- package/dist/index.js +1291 -855
- package/dist/index.js.map +4 -4
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -973,7 +973,7 @@ var require_command = __commonJS({
|
|
|
973
973
|
"../../node_modules/.pnpm/commander@12.1.0/node_modules/commander/lib/command.js"(exports) {
|
|
974
974
|
var EventEmitter2 = __require("node:events").EventEmitter;
|
|
975
975
|
var childProcess2 = __require("node:child_process");
|
|
976
|
-
var
|
|
976
|
+
var path46 = __require("node:path");
|
|
977
977
|
var fs41 = __require("node:fs");
|
|
978
978
|
var process8 = __require("node:process");
|
|
979
979
|
var { Argument: Argument2, humanReadableArgName } = require_argument();
|
|
@@ -1906,9 +1906,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1906
1906
|
let launchWithNode = false;
|
|
1907
1907
|
const sourceExt = [".js", ".ts", ".tsx", ".mjs", ".cjs"];
|
|
1908
1908
|
function findFile(baseDir, baseName) {
|
|
1909
|
-
const localBin =
|
|
1909
|
+
const localBin = path46.resolve(baseDir, baseName);
|
|
1910
1910
|
if (fs41.existsSync(localBin)) return localBin;
|
|
1911
|
-
if (sourceExt.includes(
|
|
1911
|
+
if (sourceExt.includes(path46.extname(baseName))) return void 0;
|
|
1912
1912
|
const foundExt = sourceExt.find(
|
|
1913
1913
|
(ext) => fs41.existsSync(`${localBin}${ext}`)
|
|
1914
1914
|
);
|
|
@@ -1926,17 +1926,17 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1926
1926
|
} catch (err) {
|
|
1927
1927
|
resolvedScriptPath = this._scriptPath;
|
|
1928
1928
|
}
|
|
1929
|
-
executableDir =
|
|
1930
|
-
|
|
1929
|
+
executableDir = path46.resolve(
|
|
1930
|
+
path46.dirname(resolvedScriptPath),
|
|
1931
1931
|
executableDir
|
|
1932
1932
|
);
|
|
1933
1933
|
}
|
|
1934
1934
|
if (executableDir) {
|
|
1935
1935
|
let localFile = findFile(executableDir, executableFile);
|
|
1936
1936
|
if (!localFile && !subcommand._executableFile && this._scriptPath) {
|
|
1937
|
-
const legacyName =
|
|
1937
|
+
const legacyName = path46.basename(
|
|
1938
1938
|
this._scriptPath,
|
|
1939
|
-
|
|
1939
|
+
path46.extname(this._scriptPath)
|
|
1940
1940
|
);
|
|
1941
1941
|
if (legacyName !== this._name) {
|
|
1942
1942
|
localFile = findFile(
|
|
@@ -1947,7 +1947,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1947
1947
|
}
|
|
1948
1948
|
executableFile = localFile || executableFile;
|
|
1949
1949
|
}
|
|
1950
|
-
launchWithNode = sourceExt.includes(
|
|
1950
|
+
launchWithNode = sourceExt.includes(path46.extname(executableFile));
|
|
1951
1951
|
let proc;
|
|
1952
1952
|
if (process8.platform !== "win32") {
|
|
1953
1953
|
if (launchWithNode) {
|
|
@@ -2787,7 +2787,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2787
2787
|
* @return {Command}
|
|
2788
2788
|
*/
|
|
2789
2789
|
nameFromFilename(filename) {
|
|
2790
|
-
this._name =
|
|
2790
|
+
this._name = path46.basename(filename, path46.extname(filename));
|
|
2791
2791
|
return this;
|
|
2792
2792
|
}
|
|
2793
2793
|
/**
|
|
@@ -2801,9 +2801,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2801
2801
|
* @param {string} [path]
|
|
2802
2802
|
* @return {(string|null|Command)}
|
|
2803
2803
|
*/
|
|
2804
|
-
executableDir(
|
|
2805
|
-
if (
|
|
2806
|
-
this._executableDir =
|
|
2804
|
+
executableDir(path47) {
|
|
2805
|
+
if (path47 === void 0) return this._executableDir;
|
|
2806
|
+
this._executableDir = path47;
|
|
2807
2807
|
return this;
|
|
2808
2808
|
}
|
|
2809
2809
|
/**
|
|
@@ -7061,8 +7061,8 @@ var init_parseUtil = __esm({
|
|
|
7061
7061
|
init_errors();
|
|
7062
7062
|
init_en();
|
|
7063
7063
|
makeIssue = (params) => {
|
|
7064
|
-
const { data, path:
|
|
7065
|
-
const fullPath = [...
|
|
7064
|
+
const { data, path: path46, errorMaps, issueData } = params;
|
|
7065
|
+
const fullPath = [...path46, ...issueData.path || []];
|
|
7066
7066
|
const fullIssue = {
|
|
7067
7067
|
...issueData,
|
|
7068
7068
|
path: fullPath
|
|
@@ -7370,11 +7370,11 @@ var init_types = __esm({
|
|
|
7370
7370
|
init_parseUtil();
|
|
7371
7371
|
init_util();
|
|
7372
7372
|
ParseInputLazyPath = class {
|
|
7373
|
-
constructor(parent, value,
|
|
7373
|
+
constructor(parent, value, path46, key) {
|
|
7374
7374
|
this._cachedPath = [];
|
|
7375
7375
|
this.parent = parent;
|
|
7376
7376
|
this.data = value;
|
|
7377
|
-
this._path =
|
|
7377
|
+
this._path = path46;
|
|
7378
7378
|
this._key = key;
|
|
7379
7379
|
}
|
|
7380
7380
|
get path() {
|
|
@@ -11529,10 +11529,10 @@ var require_src2 = __commonJS({
|
|
|
11529
11529
|
var fs_1 = __require("fs");
|
|
11530
11530
|
var debug_1 = __importDefault(require_src());
|
|
11531
11531
|
var log2 = debug_1.default("@kwsites/file-exists");
|
|
11532
|
-
function check2(
|
|
11533
|
-
log2(`checking %s`,
|
|
11532
|
+
function check2(path46, isFile, isDirectory) {
|
|
11533
|
+
log2(`checking %s`, path46);
|
|
11534
11534
|
try {
|
|
11535
|
-
const stat3 = fs_1.statSync(
|
|
11535
|
+
const stat3 = fs_1.statSync(path46);
|
|
11536
11536
|
if (stat3.isFile() && isFile) {
|
|
11537
11537
|
log2(`[OK] path represents a file`);
|
|
11538
11538
|
return true;
|
|
@@ -11552,8 +11552,8 @@ var require_src2 = __commonJS({
|
|
|
11552
11552
|
throw e;
|
|
11553
11553
|
}
|
|
11554
11554
|
}
|
|
11555
|
-
function exists2(
|
|
11556
|
-
return check2(
|
|
11555
|
+
function exists2(path46, type = exports.READABLE) {
|
|
11556
|
+
return check2(path46, (type & exports.FILE) > 0, (type & exports.FOLDER) > 0);
|
|
11557
11557
|
}
|
|
11558
11558
|
exports.exists = exists2;
|
|
11559
11559
|
exports.FILE = 1;
|
|
@@ -11850,10 +11850,10 @@ function assignProp(target, prop, value) {
|
|
|
11850
11850
|
configurable: true
|
|
11851
11851
|
});
|
|
11852
11852
|
}
|
|
11853
|
-
function getElementAtPath(obj,
|
|
11854
|
-
if (!
|
|
11853
|
+
function getElementAtPath(obj, path46) {
|
|
11854
|
+
if (!path46)
|
|
11855
11855
|
return obj;
|
|
11856
|
-
return
|
|
11856
|
+
return path46.reduce((acc, key) => acc?.[key], obj);
|
|
11857
11857
|
}
|
|
11858
11858
|
function promiseAllObject(promisesObj) {
|
|
11859
11859
|
const keys = Object.keys(promisesObj);
|
|
@@ -12102,11 +12102,11 @@ function aborted(x, startIndex = 0) {
|
|
|
12102
12102
|
}
|
|
12103
12103
|
return false;
|
|
12104
12104
|
}
|
|
12105
|
-
function prefixIssues(
|
|
12105
|
+
function prefixIssues(path46, issues) {
|
|
12106
12106
|
return issues.map((iss) => {
|
|
12107
12107
|
var _a2;
|
|
12108
12108
|
(_a2 = iss).path ?? (_a2.path = []);
|
|
12109
|
-
iss.path.unshift(
|
|
12109
|
+
iss.path.unshift(path46);
|
|
12110
12110
|
return iss;
|
|
12111
12111
|
});
|
|
12112
12112
|
}
|
|
@@ -12295,7 +12295,7 @@ function treeifyError(error40, _mapper) {
|
|
|
12295
12295
|
return issue2.message;
|
|
12296
12296
|
};
|
|
12297
12297
|
const result = { errors: [] };
|
|
12298
|
-
const processError = (error41,
|
|
12298
|
+
const processError = (error41, path46 = []) => {
|
|
12299
12299
|
var _a2, _b;
|
|
12300
12300
|
for (const issue2 of error41.issues) {
|
|
12301
12301
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
@@ -12305,7 +12305,7 @@ function treeifyError(error40, _mapper) {
|
|
|
12305
12305
|
} else if (issue2.code === "invalid_element") {
|
|
12306
12306
|
processError({ issues: issue2.issues }, issue2.path);
|
|
12307
12307
|
} else {
|
|
12308
|
-
const fullpath = [...
|
|
12308
|
+
const fullpath = [...path46, ...issue2.path];
|
|
12309
12309
|
if (fullpath.length === 0) {
|
|
12310
12310
|
result.errors.push(mapper(issue2));
|
|
12311
12311
|
continue;
|
|
@@ -12335,9 +12335,9 @@ function treeifyError(error40, _mapper) {
|
|
|
12335
12335
|
processError(error40);
|
|
12336
12336
|
return result;
|
|
12337
12337
|
}
|
|
12338
|
-
function toDotPath(
|
|
12338
|
+
function toDotPath(path46) {
|
|
12339
12339
|
const segs = [];
|
|
12340
|
-
for (const seg of
|
|
12340
|
+
for (const seg of path46) {
|
|
12341
12341
|
if (typeof seg === "number")
|
|
12342
12342
|
segs.push(`[${seg}]`);
|
|
12343
12343
|
else if (typeof seg === "symbol")
|
|
@@ -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((resolve22) => {
|
|
24804
|
+
this.#abortController.signal.addEventListener("abort", () => resolve22());
|
|
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((resolve22, reject) => {
|
|
24954
|
+
this.#pendingResponses.set(id, { resolve: resolve22, reject });
|
|
24955
24955
|
});
|
|
24956
24956
|
await this.#sendMessage({ jsonrpc: "2.0", id, method, params });
|
|
24957
24957
|
return responsePromise;
|
|
@@ -25064,7 +25064,7 @@ var {
|
|
|
25064
25064
|
} = import_index.default;
|
|
25065
25065
|
|
|
25066
25066
|
// src/cli-version.ts
|
|
25067
|
-
var CLI_VERSION = "0.1.
|
|
25067
|
+
var CLI_VERSION = "0.1.40".length > 0 ? "0.1.40" : "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";
|
|
@@ -25072,7 +25072,7 @@ var DEFAULT_FIREHOSE_URL = "https://buildautomaton-firehose.fly.dev";
|
|
|
25072
25072
|
|
|
25073
25073
|
// src/cli/run-cli-action.ts
|
|
25074
25074
|
import * as fs40 from "node:fs";
|
|
25075
|
-
import * as
|
|
25075
|
+
import * as path45 from "node:path";
|
|
25076
25076
|
|
|
25077
25077
|
// src/cli-log-level.ts
|
|
25078
25078
|
var verbosity = "info";
|
|
@@ -25542,9 +25542,9 @@ function attachWebSocketClientPing(ws, intervalMs) {
|
|
|
25542
25542
|
return clear;
|
|
25543
25543
|
}
|
|
25544
25544
|
function buildCliWebSocketClientOptions(wsUrl) {
|
|
25545
|
-
const wsOptions = { perMessageDeflate: false
|
|
25545
|
+
const wsOptions = { perMessageDeflate: false };
|
|
25546
25546
|
if (wsUrl.startsWith("wss://")) {
|
|
25547
|
-
wsOptions.agent = new https.Agent({ rejectUnauthorized: false
|
|
25547
|
+
wsOptions.agent = new https.Agent({ rejectUnauthorized: false });
|
|
25548
25548
|
}
|
|
25549
25549
|
return wsOptions;
|
|
25550
25550
|
}
|
|
@@ -25579,11 +25579,47 @@ function safeSendWebSocketBinary(ws, data) {
|
|
|
25579
25579
|
}
|
|
25580
25580
|
}
|
|
25581
25581
|
|
|
25582
|
+
// src/connection/parse-compact-heartbeat-ack.ts
|
|
25583
|
+
function tryParseCompactHeartbeatAck(raw) {
|
|
25584
|
+
let str = null;
|
|
25585
|
+
if (typeof raw === "string") {
|
|
25586
|
+
str = raw;
|
|
25587
|
+
} else if (Buffer.isBuffer(raw)) {
|
|
25588
|
+
str = raw.toString("utf8");
|
|
25589
|
+
} else if (raw instanceof ArrayBuffer) {
|
|
25590
|
+
str = Buffer.from(raw).toString("utf8");
|
|
25591
|
+
} else {
|
|
25592
|
+
return null;
|
|
25593
|
+
}
|
|
25594
|
+
if (str.length > 64 || !str.includes('"ha"')) return null;
|
|
25595
|
+
try {
|
|
25596
|
+
const parsed = JSON.parse(str);
|
|
25597
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) return null;
|
|
25598
|
+
const o = parsed;
|
|
25599
|
+
if (o.t !== "ha") return null;
|
|
25600
|
+
const s = o.s;
|
|
25601
|
+
if (typeof s !== "number" || !Number.isFinite(s)) return null;
|
|
25602
|
+
return Math.trunc(s);
|
|
25603
|
+
} catch {
|
|
25604
|
+
return null;
|
|
25605
|
+
}
|
|
25606
|
+
}
|
|
25607
|
+
|
|
25582
25608
|
// src/connection/create-ws-bridge.ts
|
|
25583
25609
|
var BRIDGE_AUTH_ERROR_HEADER = "x-bridge-auth-error";
|
|
25584
25610
|
var BRIDGE_AUTH_ERROR_TOKEN_INVALID = "token_invalid";
|
|
25585
25611
|
function createWsBridge(options) {
|
|
25586
|
-
const {
|
|
25612
|
+
const {
|
|
25613
|
+
url: url2,
|
|
25614
|
+
onMessage,
|
|
25615
|
+
onOpen,
|
|
25616
|
+
onClose,
|
|
25617
|
+
onError: onError2,
|
|
25618
|
+
onAuthInvalid,
|
|
25619
|
+
clientPingIntervalMs,
|
|
25620
|
+
onCompactHeartbeatAck,
|
|
25621
|
+
isActiveSocket
|
|
25622
|
+
} = options;
|
|
25587
25623
|
applyCliOutboundNetworkPreferences();
|
|
25588
25624
|
const ws = new wrapper_default(url2, buildCliWebSocketClientOptions(url2));
|
|
25589
25625
|
let clearClientPing = null;
|
|
@@ -25607,7 +25643,13 @@ function createWsBridge(options) {
|
|
|
25607
25643
|
onOpen?.();
|
|
25608
25644
|
});
|
|
25609
25645
|
ws.on("message", (raw) => {
|
|
25646
|
+
const ackSeq = tryParseCompactHeartbeatAck(raw);
|
|
25647
|
+
if (ackSeq != null) {
|
|
25648
|
+
onCompactHeartbeatAck?.(ackSeq);
|
|
25649
|
+
return;
|
|
25650
|
+
}
|
|
25610
25651
|
setImmediate(() => {
|
|
25652
|
+
if (isActiveSocket && !isActiveSocket(ws)) return;
|
|
25611
25653
|
try {
|
|
25612
25654
|
let data;
|
|
25613
25655
|
if (typeof raw === "string") {
|
|
@@ -25618,9 +25660,9 @@ function createWsBridge(options) {
|
|
|
25618
25660
|
} else {
|
|
25619
25661
|
data = raw;
|
|
25620
25662
|
}
|
|
25621
|
-
onMessage?.(data);
|
|
25663
|
+
onMessage?.(data, ws);
|
|
25622
25664
|
} catch {
|
|
25623
|
-
onMessage?.(raw);
|
|
25665
|
+
onMessage?.(raw, ws);
|
|
25624
25666
|
}
|
|
25625
25667
|
});
|
|
25626
25668
|
});
|
|
@@ -26079,14 +26121,14 @@ var baseOpen = async (options) => {
|
|
|
26079
26121
|
}
|
|
26080
26122
|
const subprocess = childProcess.spawn(command, cliArguments, childProcessOptions);
|
|
26081
26123
|
if (options.wait) {
|
|
26082
|
-
return new Promise((
|
|
26124
|
+
return new Promise((resolve22, reject) => {
|
|
26083
26125
|
subprocess.once("error", reject);
|
|
26084
26126
|
subprocess.once("close", (exitCode) => {
|
|
26085
26127
|
if (!options.allowNonzeroExitCode && exitCode > 0) {
|
|
26086
26128
|
reject(new Error(`Exited with code ${exitCode}`));
|
|
26087
26129
|
return;
|
|
26088
26130
|
}
|
|
26089
|
-
|
|
26131
|
+
resolve22(subprocess);
|
|
26090
26132
|
});
|
|
26091
26133
|
});
|
|
26092
26134
|
}
|
|
@@ -26527,6 +26569,61 @@ function scheduleMainBridgeReconnect(state, connect, log2, closeMeta) {
|
|
|
26527
26569
|
});
|
|
26528
26570
|
}
|
|
26529
26571
|
|
|
26572
|
+
// src/connection/reconnect/duplicate-bridge-connection-detect.ts
|
|
26573
|
+
var BRIDGE_SUPERSEDED_CLOSE_REASON_SNIPPET = "superseded";
|
|
26574
|
+
var DUPLICATE_BRIDGE_SHORT_SESSION_MS = 25e3;
|
|
26575
|
+
var DUPLICATE_BRIDGE_FLAP_MIN_COUNT = 2;
|
|
26576
|
+
var DUPLICATE_BRIDGE_FLAP_WINDOW_MS = 9e4;
|
|
26577
|
+
var DUPLICATE_BRIDGE_STABLE_SESSION_MS = BRIDGE_APP_HEARTBEAT_INTERVAL_MS * BRIDGE_HEARTBEAT_MISSED_ACKS_BEFORE_RECONNECT + 5e3;
|
|
26578
|
+
var DUPLICATE_BRIDGE_WARNING_MESSAGE = "[Bridge service] It looks like two bridges are using the same token and disconnecting each other. Stop any duplicate @buildautomaton/cli process for this workspace/token (only one bridge should run).";
|
|
26579
|
+
function createEmptyDuplicateBridgeFlapTracker() {
|
|
26580
|
+
return {
|
|
26581
|
+
sessionOpenedAtMs: null,
|
|
26582
|
+
shortDisconnectAtMs: []
|
|
26583
|
+
};
|
|
26584
|
+
}
|
|
26585
|
+
function isSupersededByNewBridgeClose(code, reason) {
|
|
26586
|
+
if (code !== 1e3) return false;
|
|
26587
|
+
return reason.toLowerCase().includes(BRIDGE_SUPERSEDED_CLOSE_REASON_SNIPPET);
|
|
26588
|
+
}
|
|
26589
|
+
function pruneShortDisconnects(tracker, nowMs) {
|
|
26590
|
+
const cutoff = nowMs - DUPLICATE_BRIDGE_FLAP_WINDOW_MS;
|
|
26591
|
+
tracker.shortDisconnectAtMs = tracker.shortDisconnectAtMs.filter((t) => t >= cutoff);
|
|
26592
|
+
}
|
|
26593
|
+
function recordBridgeSessionOpened(tracker, nowMs) {
|
|
26594
|
+
tracker.sessionOpenedAtMs = nowMs;
|
|
26595
|
+
}
|
|
26596
|
+
function evaluateDuplicateBridgeDisconnect(tracker, nowMs, code, reason) {
|
|
26597
|
+
if (isSupersededByNewBridgeClose(code, reason)) {
|
|
26598
|
+
return { kind: "warn", trigger: "superseded_close" };
|
|
26599
|
+
}
|
|
26600
|
+
const openedAt = tracker.sessionOpenedAtMs;
|
|
26601
|
+
tracker.sessionOpenedAtMs = null;
|
|
26602
|
+
if (openedAt == null) {
|
|
26603
|
+
return { kind: "none" };
|
|
26604
|
+
}
|
|
26605
|
+
const sessionMs = nowMs - openedAt;
|
|
26606
|
+
if (sessionMs >= DUPLICATE_BRIDGE_STABLE_SESSION_MS) {
|
|
26607
|
+
tracker.shortDisconnectAtMs = [];
|
|
26608
|
+
return { kind: "none" };
|
|
26609
|
+
}
|
|
26610
|
+
if (sessionMs < DUPLICATE_BRIDGE_SHORT_SESSION_MS) {
|
|
26611
|
+
pruneShortDisconnects(tracker, nowMs);
|
|
26612
|
+
tracker.shortDisconnectAtMs.push(nowMs);
|
|
26613
|
+
if (tracker.shortDisconnectAtMs.length >= DUPLICATE_BRIDGE_FLAP_MIN_COUNT) {
|
|
26614
|
+
return { kind: "warn", trigger: "rapid_short_disconnects" };
|
|
26615
|
+
}
|
|
26616
|
+
}
|
|
26617
|
+
return { kind: "none" };
|
|
26618
|
+
}
|
|
26619
|
+
function logDuplicateBridgeWarning(log2, result) {
|
|
26620
|
+
if (result.kind !== "warn") return;
|
|
26621
|
+
try {
|
|
26622
|
+
log2(DUPLICATE_BRIDGE_WARNING_MESSAGE);
|
|
26623
|
+
} catch {
|
|
26624
|
+
}
|
|
26625
|
+
}
|
|
26626
|
+
|
|
26530
26627
|
// src/connection/reconnect/firehose-reconnect.ts
|
|
26531
26628
|
function beginFirehoseDeferredDisconnect(ctx, code, reason, log2) {
|
|
26532
26629
|
beginTieredSilentReconnectDisconnect({
|
|
@@ -26575,8 +26672,8 @@ function runPendingAuth(options) {
|
|
|
26575
26672
|
let hasOpenedBrowser = false;
|
|
26576
26673
|
let resolved = false;
|
|
26577
26674
|
let resolveAuth;
|
|
26578
|
-
const authPromise = new Promise((
|
|
26579
|
-
resolveAuth =
|
|
26675
|
+
const authPromise = new Promise((resolve22) => {
|
|
26676
|
+
resolveAuth = resolve22;
|
|
26580
26677
|
});
|
|
26581
26678
|
let reconnectAttempt = 0;
|
|
26582
26679
|
const signInQuiet = createEmptyReconnectQuietSlot();
|
|
@@ -26727,7 +26824,7 @@ import sqliteWasm from "node-sqlite3-wasm";
|
|
|
26727
26824
|
|
|
26728
26825
|
// src/runtime/yield-to-event-loop.ts
|
|
26729
26826
|
function yieldToEventLoop() {
|
|
26730
|
-
return new Promise((
|
|
26827
|
+
return new Promise((resolve22) => setImmediate(resolve22));
|
|
26731
26828
|
}
|
|
26732
26829
|
|
|
26733
26830
|
// src/sqlite/cli-sqlite-paths.ts
|
|
@@ -27570,9 +27667,9 @@ function parseChangeSummaryJson(raw, allowedPaths, options) {
|
|
|
27570
27667
|
const rawPath = typeof o.path === "string" ? o.path.trim() : "";
|
|
27571
27668
|
const summary = typeof o.summary === "string" ? o.summary.trim() : "";
|
|
27572
27669
|
if (!rawPath || !summary) continue;
|
|
27573
|
-
const
|
|
27574
|
-
if (!
|
|
27575
|
-
rows.push({ path:
|
|
27670
|
+
const path46 = skip ? normalizeRepoRelativePath(rawPath) || rawPath : resolveChangeSummaryPathAgainstAllowed(rawPath, allowedPaths);
|
|
27671
|
+
if (!path46) continue;
|
|
27672
|
+
rows.push({ path: path46, summary: clampSummaryToAtMostTwoLines(summary) });
|
|
27576
27673
|
}
|
|
27577
27674
|
return rows;
|
|
27578
27675
|
}
|
|
@@ -27866,8 +27963,8 @@ function pathspec(...paths) {
|
|
|
27866
27963
|
cache.set(key, paths);
|
|
27867
27964
|
return key;
|
|
27868
27965
|
}
|
|
27869
|
-
function isPathSpec(
|
|
27870
|
-
return
|
|
27966
|
+
function isPathSpec(path46) {
|
|
27967
|
+
return path46 instanceof String && cache.has(path46);
|
|
27871
27968
|
}
|
|
27872
27969
|
function toPaths(pathSpec) {
|
|
27873
27970
|
return cache.get(pathSpec) || [];
|
|
@@ -27956,8 +28053,8 @@ function toLinesWithContent(input = "", trimmed2 = true, separator = "\n") {
|
|
|
27956
28053
|
function forEachLineWithContent(input, callback) {
|
|
27957
28054
|
return toLinesWithContent(input, true).map((line) => callback(line));
|
|
27958
28055
|
}
|
|
27959
|
-
function folderExists(
|
|
27960
|
-
return (0, import_file_exists.exists)(
|
|
28056
|
+
function folderExists(path46) {
|
|
28057
|
+
return (0, import_file_exists.exists)(path46, import_file_exists.FOLDER);
|
|
27961
28058
|
}
|
|
27962
28059
|
function append(target, item) {
|
|
27963
28060
|
if (Array.isArray(target)) {
|
|
@@ -28361,8 +28458,8 @@ function checkIsRepoRootTask() {
|
|
|
28361
28458
|
commands,
|
|
28362
28459
|
format: "utf-8",
|
|
28363
28460
|
onError,
|
|
28364
|
-
parser(
|
|
28365
|
-
return /^\.(git)?$/.test(
|
|
28461
|
+
parser(path46) {
|
|
28462
|
+
return /^\.(git)?$/.test(path46.trim());
|
|
28366
28463
|
}
|
|
28367
28464
|
};
|
|
28368
28465
|
}
|
|
@@ -28796,11 +28893,11 @@ function parseGrep(grep) {
|
|
|
28796
28893
|
const paths = /* @__PURE__ */ new Set();
|
|
28797
28894
|
const results = {};
|
|
28798
28895
|
forEachLineWithContent(grep, (input) => {
|
|
28799
|
-
const [
|
|
28800
|
-
paths.add(
|
|
28801
|
-
(results[
|
|
28896
|
+
const [path46, line, preview] = input.split(NULL);
|
|
28897
|
+
paths.add(path46);
|
|
28898
|
+
(results[path46] = results[path46] || []).push({
|
|
28802
28899
|
line: asNumber(line),
|
|
28803
|
-
path:
|
|
28900
|
+
path: path46,
|
|
28804
28901
|
preview
|
|
28805
28902
|
});
|
|
28806
28903
|
});
|
|
@@ -29565,14 +29662,14 @@ var init_hash_object = __esm2({
|
|
|
29565
29662
|
init_task();
|
|
29566
29663
|
}
|
|
29567
29664
|
});
|
|
29568
|
-
function parseInit(bare,
|
|
29665
|
+
function parseInit(bare, path46, text) {
|
|
29569
29666
|
const response = String(text).trim();
|
|
29570
29667
|
let result;
|
|
29571
29668
|
if (result = initResponseRegex.exec(response)) {
|
|
29572
|
-
return new InitSummary(bare,
|
|
29669
|
+
return new InitSummary(bare, path46, false, result[1]);
|
|
29573
29670
|
}
|
|
29574
29671
|
if (result = reInitResponseRegex.exec(response)) {
|
|
29575
|
-
return new InitSummary(bare,
|
|
29672
|
+
return new InitSummary(bare, path46, true, result[1]);
|
|
29576
29673
|
}
|
|
29577
29674
|
let gitDir = "";
|
|
29578
29675
|
const tokens = response.split(" ");
|
|
@@ -29583,7 +29680,7 @@ function parseInit(bare, path43, text) {
|
|
|
29583
29680
|
break;
|
|
29584
29681
|
}
|
|
29585
29682
|
}
|
|
29586
|
-
return new InitSummary(bare,
|
|
29683
|
+
return new InitSummary(bare, path46, /^re/i.test(response), gitDir);
|
|
29587
29684
|
}
|
|
29588
29685
|
var InitSummary;
|
|
29589
29686
|
var initResponseRegex;
|
|
@@ -29592,9 +29689,9 @@ var init_InitSummary = __esm2({
|
|
|
29592
29689
|
"src/lib/responses/InitSummary.ts"() {
|
|
29593
29690
|
"use strict";
|
|
29594
29691
|
InitSummary = class {
|
|
29595
|
-
constructor(bare,
|
|
29692
|
+
constructor(bare, path46, existing, gitDir) {
|
|
29596
29693
|
this.bare = bare;
|
|
29597
|
-
this.path =
|
|
29694
|
+
this.path = path46;
|
|
29598
29695
|
this.existing = existing;
|
|
29599
29696
|
this.gitDir = gitDir;
|
|
29600
29697
|
}
|
|
@@ -29606,7 +29703,7 @@ var init_InitSummary = __esm2({
|
|
|
29606
29703
|
function hasBareCommand(command) {
|
|
29607
29704
|
return command.includes(bareCommand);
|
|
29608
29705
|
}
|
|
29609
|
-
function initTask(bare = false,
|
|
29706
|
+
function initTask(bare = false, path46, customArgs) {
|
|
29610
29707
|
const commands = ["init", ...customArgs];
|
|
29611
29708
|
if (bare && !hasBareCommand(commands)) {
|
|
29612
29709
|
commands.splice(1, 0, bareCommand);
|
|
@@ -29615,7 +29712,7 @@ function initTask(bare = false, path43, customArgs) {
|
|
|
29615
29712
|
commands,
|
|
29616
29713
|
format: "utf-8",
|
|
29617
29714
|
parser(text) {
|
|
29618
|
-
return parseInit(commands.includes("--bare"),
|
|
29715
|
+
return parseInit(commands.includes("--bare"), path46, text);
|
|
29619
29716
|
}
|
|
29620
29717
|
};
|
|
29621
29718
|
}
|
|
@@ -30431,12 +30528,12 @@ var init_FileStatusSummary = __esm2({
|
|
|
30431
30528
|
"use strict";
|
|
30432
30529
|
fromPathRegex = /^(.+)\0(.+)$/;
|
|
30433
30530
|
FileStatusSummary = class {
|
|
30434
|
-
constructor(
|
|
30435
|
-
this.path =
|
|
30531
|
+
constructor(path46, index, working_dir) {
|
|
30532
|
+
this.path = path46;
|
|
30436
30533
|
this.index = index;
|
|
30437
30534
|
this.working_dir = working_dir;
|
|
30438
30535
|
if (index === "R" || working_dir === "R") {
|
|
30439
|
-
const detail = fromPathRegex.exec(
|
|
30536
|
+
const detail = fromPathRegex.exec(path46) || [null, path46, path46];
|
|
30440
30537
|
this.from = detail[2] || "";
|
|
30441
30538
|
this.path = detail[1] || "";
|
|
30442
30539
|
}
|
|
@@ -30467,14 +30564,14 @@ function splitLine(result, lineStr) {
|
|
|
30467
30564
|
default:
|
|
30468
30565
|
return;
|
|
30469
30566
|
}
|
|
30470
|
-
function data(index, workingDir,
|
|
30567
|
+
function data(index, workingDir, path46) {
|
|
30471
30568
|
const raw = `${index}${workingDir}`;
|
|
30472
30569
|
const handler = parsers6.get(raw);
|
|
30473
30570
|
if (handler) {
|
|
30474
|
-
handler(result,
|
|
30571
|
+
handler(result, path46);
|
|
30475
30572
|
}
|
|
30476
30573
|
if (raw !== "##" && raw !== "!!") {
|
|
30477
|
-
result.files.push(new FileStatusSummary(
|
|
30574
|
+
result.files.push(new FileStatusSummary(path46, index, workingDir));
|
|
30478
30575
|
}
|
|
30479
30576
|
}
|
|
30480
30577
|
}
|
|
@@ -30783,9 +30880,9 @@ var init_simple_git_api = __esm2({
|
|
|
30783
30880
|
next
|
|
30784
30881
|
);
|
|
30785
30882
|
}
|
|
30786
|
-
hashObject(
|
|
30883
|
+
hashObject(path46, write) {
|
|
30787
30884
|
return this._runTask(
|
|
30788
|
-
hashObjectTask(
|
|
30885
|
+
hashObjectTask(path46, write === true),
|
|
30789
30886
|
trailingFunctionArgument(arguments)
|
|
30790
30887
|
);
|
|
30791
30888
|
}
|
|
@@ -31138,8 +31235,8 @@ var init_branch = __esm2({
|
|
|
31138
31235
|
}
|
|
31139
31236
|
});
|
|
31140
31237
|
function toPath(input) {
|
|
31141
|
-
const
|
|
31142
|
-
return
|
|
31238
|
+
const path46 = input.trim().replace(/^["']|["']$/g, "");
|
|
31239
|
+
return path46 && normalize(path46);
|
|
31143
31240
|
}
|
|
31144
31241
|
var parseCheckIgnore;
|
|
31145
31242
|
var init_CheckIgnore = __esm2({
|
|
@@ -31453,8 +31550,8 @@ __export2(sub_module_exports, {
|
|
|
31453
31550
|
subModuleTask: () => subModuleTask,
|
|
31454
31551
|
updateSubModuleTask: () => updateSubModuleTask
|
|
31455
31552
|
});
|
|
31456
|
-
function addSubModuleTask(repo,
|
|
31457
|
-
return subModuleTask(["add", repo,
|
|
31553
|
+
function addSubModuleTask(repo, path46) {
|
|
31554
|
+
return subModuleTask(["add", repo, path46]);
|
|
31458
31555
|
}
|
|
31459
31556
|
function initSubModuleTask(customArgs) {
|
|
31460
31557
|
return subModuleTask(["init", ...customArgs]);
|
|
@@ -31787,8 +31884,8 @@ var require_git = __commonJS2({
|
|
|
31787
31884
|
}
|
|
31788
31885
|
return this._runTask(straightThroughStringTask2(command, this._trimmed), next);
|
|
31789
31886
|
};
|
|
31790
|
-
Git2.prototype.submoduleAdd = function(repo,
|
|
31791
|
-
return this._runTask(addSubModuleTask2(repo,
|
|
31887
|
+
Git2.prototype.submoduleAdd = function(repo, path46, then) {
|
|
31888
|
+
return this._runTask(addSubModuleTask2(repo, path46), trailingFunctionArgument2(arguments));
|
|
31792
31889
|
};
|
|
31793
31890
|
Git2.prototype.submoduleUpdate = function(args, then) {
|
|
31794
31891
|
return this._runTask(
|
|
@@ -32425,8 +32522,8 @@ async function runGitTask(fn) {
|
|
|
32425
32522
|
}
|
|
32426
32523
|
async function yieldToEventLoop2() {
|
|
32427
32524
|
throwIfGitShutdownRequested();
|
|
32428
|
-
await new Promise((
|
|
32429
|
-
setImmediate(
|
|
32525
|
+
await new Promise((resolve22) => {
|
|
32526
|
+
setImmediate(resolve22);
|
|
32430
32527
|
});
|
|
32431
32528
|
throwIfGitShutdownRequested();
|
|
32432
32529
|
}
|
|
@@ -32751,9 +32848,9 @@ async function collectTurnGitDiffFromPreTurnSnapshot(options) {
|
|
|
32751
32848
|
// src/agents/acp/put-summarize-change-summaries.ts
|
|
32752
32849
|
async function putEncryptedChangeSummaryRows(params) {
|
|
32753
32850
|
const base = params.apiBaseUrl.replace(/\/+$/, "");
|
|
32754
|
-
const entries = params.rows.map(({ path:
|
|
32851
|
+
const entries = params.rows.map(({ path: path46, summary }) => {
|
|
32755
32852
|
const enc = params.e2ee.encryptFields({ summary }, ["summary"]);
|
|
32756
|
-
return { path:
|
|
32853
|
+
return { path: path46, summary: JSON.stringify(enc) };
|
|
32757
32854
|
});
|
|
32758
32855
|
const res = await fetch(
|
|
32759
32856
|
`${base}/api/sessions/${encodeURIComponent(params.sessionId)}/follow-ups/summarize-changes`,
|
|
@@ -33756,11 +33853,11 @@ async function sendAcpPromptViaTransport(transport, ctx, sessionId, promptText,
|
|
|
33756
33853
|
// src/agents/acp/clients/sdk/sdk-stdio-permission-request-handshake.ts
|
|
33757
33854
|
function awaitSdkStdioPermissionRequestHandshake(params) {
|
|
33758
33855
|
const { requestId, paramsRecord, pending, onRequest } = params;
|
|
33759
|
-
return new Promise((
|
|
33760
|
-
pending.set(requestId, { resolve:
|
|
33856
|
+
return new Promise((resolve22) => {
|
|
33857
|
+
pending.set(requestId, { resolve: resolve22, params: paramsRecord });
|
|
33761
33858
|
if (onRequest == null) {
|
|
33762
33859
|
pending.delete(requestId);
|
|
33763
|
-
|
|
33860
|
+
resolve22({ outcome: { outcome: "denied" } });
|
|
33764
33861
|
return;
|
|
33765
33862
|
}
|
|
33766
33863
|
try {
|
|
@@ -33826,7 +33923,7 @@ async function createSdkStdioAcpClient(options) {
|
|
|
33826
33923
|
child.once("close", (code, signal) => {
|
|
33827
33924
|
onAgentSubprocessExit?.({ code, signal });
|
|
33828
33925
|
});
|
|
33829
|
-
return new Promise((
|
|
33926
|
+
return new Promise((resolve22, reject) => {
|
|
33830
33927
|
let initSettled = false;
|
|
33831
33928
|
const settleReject = (err) => {
|
|
33832
33929
|
if (initSettled) return;
|
|
@@ -33840,7 +33937,7 @@ async function createSdkStdioAcpClient(options) {
|
|
|
33840
33937
|
const settleResolve = (handle) => {
|
|
33841
33938
|
if (initSettled) return;
|
|
33842
33939
|
initSettled = true;
|
|
33843
|
-
|
|
33940
|
+
resolve22(handle);
|
|
33844
33941
|
};
|
|
33845
33942
|
child.on("error", (err) => {
|
|
33846
33943
|
settleReject(new Error(formatSpawnError(err, command[0])));
|
|
@@ -34131,7 +34228,7 @@ async function createCursorAcpClient(options) {
|
|
|
34131
34228
|
logDebug,
|
|
34132
34229
|
getStderrText: () => stderrCapture.getText()
|
|
34133
34230
|
};
|
|
34134
|
-
return new Promise((
|
|
34231
|
+
return new Promise((resolve22, reject) => {
|
|
34135
34232
|
child.on("error", (err) => {
|
|
34136
34233
|
child.kill();
|
|
34137
34234
|
reject(new Error(formatSpawnError2(err, command[0])));
|
|
@@ -34307,7 +34404,7 @@ async function createCursorAcpClient(options) {
|
|
|
34307
34404
|
clientInfo: { name: "buildautomaton-cli", version: "0.1.0" }
|
|
34308
34405
|
});
|
|
34309
34406
|
const sessionId = established.sessionId;
|
|
34310
|
-
|
|
34407
|
+
resolve22({
|
|
34311
34408
|
sessionId,
|
|
34312
34409
|
async sendPrompt(prompt, options2) {
|
|
34313
34410
|
const imgs = options2?.images?.map((im) => ({ type: "image", mimeType: im.mimeType, data: im.dataBase64 }));
|
|
@@ -35553,266 +35650,65 @@ async function createAcpManager(options) {
|
|
|
35553
35650
|
};
|
|
35554
35651
|
}
|
|
35555
35652
|
|
|
35556
|
-
// src/
|
|
35557
|
-
|
|
35558
|
-
import os8 from "node:os";
|
|
35559
|
-
|
|
35560
|
-
// src/worktrees/prepare-new-session-worktrees.ts
|
|
35561
|
-
import * as fs18 from "node:fs";
|
|
35562
|
-
import * as path21 from "node:path";
|
|
35563
|
-
|
|
35564
|
-
// src/git/worktrees/worktree-add.ts
|
|
35565
|
-
async function gitWorktreeAddBranch(mainRepoPath, worktreePath, branch) {
|
|
35566
|
-
const mainGit = cliSimpleGit(mainRepoPath);
|
|
35567
|
-
await mainGit.raw(["worktree", "add", "-b", branch, worktreePath, "HEAD"]);
|
|
35568
|
-
}
|
|
35569
|
-
|
|
35570
|
-
// src/worktrees/worktree-layout-file.ts
|
|
35571
|
-
import * as fs17 from "node:fs";
|
|
35572
|
-
import * as path20 from "node:path";
|
|
35573
|
-
import os7 from "node:os";
|
|
35574
|
-
var LAYOUT_FILENAME = "worktree-launcher-layout.json";
|
|
35575
|
-
function defaultWorktreeLayoutPath() {
|
|
35576
|
-
return path20.join(os7.homedir(), ".buildautomaton", LAYOUT_FILENAME);
|
|
35577
|
-
}
|
|
35578
|
-
function normalizeLoadedLayout(raw) {
|
|
35579
|
-
if (raw && typeof raw === "object" && "launcherCwds" in raw) {
|
|
35580
|
-
const j = raw;
|
|
35581
|
-
if (Array.isArray(j.launcherCwds)) return { launcherCwds: j.launcherCwds };
|
|
35582
|
-
}
|
|
35583
|
-
return { launcherCwds: [] };
|
|
35584
|
-
}
|
|
35585
|
-
function loadWorktreeLayout() {
|
|
35586
|
-
try {
|
|
35587
|
-
const p = defaultWorktreeLayoutPath();
|
|
35588
|
-
if (!fs17.existsSync(p)) return { launcherCwds: [] };
|
|
35589
|
-
const raw = JSON.parse(fs17.readFileSync(p, "utf8"));
|
|
35590
|
-
return normalizeLoadedLayout(raw);
|
|
35591
|
-
} catch {
|
|
35592
|
-
return { launcherCwds: [] };
|
|
35593
|
-
}
|
|
35594
|
-
}
|
|
35595
|
-
function saveWorktreeLayout(layout) {
|
|
35596
|
-
try {
|
|
35597
|
-
const dir = path20.dirname(defaultWorktreeLayoutPath());
|
|
35598
|
-
fs17.mkdirSync(dir, { recursive: true });
|
|
35599
|
-
fs17.writeFileSync(defaultWorktreeLayoutPath(), JSON.stringify(layout, null, 2), "utf8");
|
|
35600
|
-
} catch {
|
|
35601
|
-
}
|
|
35602
|
-
}
|
|
35603
|
-
function baseNameSafe(pathString) {
|
|
35604
|
-
return path20.basename(pathString).replace(/[^a-zA-Z0-9._-]+/g, "-") || "cwd";
|
|
35605
|
-
}
|
|
35606
|
-
function getLauncherDirNameIfPresent(layout, bridgeRootPath2) {
|
|
35607
|
-
const norm = path20.resolve(bridgeRootPath2);
|
|
35608
|
-
const existing = layout.launcherCwds.find((e) => path20.resolve(e.absolutePath) === norm);
|
|
35609
|
-
return existing?.dirName;
|
|
35610
|
-
}
|
|
35611
|
-
function allocateDirNameForLauncherCwd(layout, bridgeRootPath2) {
|
|
35612
|
-
const existing = getLauncherDirNameIfPresent(layout, bridgeRootPath2);
|
|
35613
|
-
if (existing) return existing;
|
|
35614
|
-
const norm = path20.resolve(bridgeRootPath2);
|
|
35615
|
-
const base = baseNameSafe(norm);
|
|
35616
|
-
const used = new Set(layout.launcherCwds.map((e) => e.dirName));
|
|
35617
|
-
let name = base;
|
|
35618
|
-
let n = 2;
|
|
35619
|
-
while (used.has(name)) {
|
|
35620
|
-
name = `${base}-${n}`;
|
|
35621
|
-
n += 1;
|
|
35622
|
-
}
|
|
35623
|
-
layout.launcherCwds.push({ absolutePath: norm, dirName: name });
|
|
35624
|
-
saveWorktreeLayout(layout);
|
|
35625
|
-
return name;
|
|
35626
|
-
}
|
|
35627
|
-
|
|
35628
|
-
// src/worktrees/prepare-new-session-worktrees.ts
|
|
35629
|
-
async function prepareNewSessionWorktrees(options) {
|
|
35630
|
-
const { worktreesRootPath, bridgeRoot, sessionId, layout, log: log2 } = options;
|
|
35631
|
-
const bridgeResolved = path21.resolve(bridgeRoot);
|
|
35632
|
-
const cwdKey = allocateDirNameForLauncherCwd(layout, bridgeResolved);
|
|
35633
|
-
const bridgeKeyDir = path21.join(worktreesRootPath, cwdKey);
|
|
35634
|
-
const sessionDir = path21.join(bridgeKeyDir, sessionId);
|
|
35635
|
-
const repos = await discoverGitReposUnderRoot(bridgeResolved);
|
|
35636
|
-
if (repos.length === 0) {
|
|
35637
|
-
log2("[worktrees] No Git repositories under bridge root; skipping worktree creation.");
|
|
35638
|
-
return null;
|
|
35639
|
-
}
|
|
35640
|
-
const branch = `session-${sessionId}`;
|
|
35641
|
-
const worktreePaths = [];
|
|
35642
|
-
fs18.mkdirSync(sessionDir, { recursive: true });
|
|
35643
|
-
for (const repo of repos) {
|
|
35644
|
-
let rel = path21.relative(bridgeResolved, repo.absolutePath);
|
|
35645
|
-
if (rel.startsWith("..") || path21.isAbsolute(rel)) continue;
|
|
35646
|
-
const relNorm = rel === "" ? "." : rel;
|
|
35647
|
-
const wtPath = relNorm === "." ? sessionDir : path21.join(sessionDir, relNorm);
|
|
35648
|
-
if (relNorm !== ".") {
|
|
35649
|
-
fs18.mkdirSync(path21.dirname(wtPath), { recursive: true });
|
|
35650
|
-
}
|
|
35651
|
-
try {
|
|
35652
|
-
await gitWorktreeAddBranch(repo.absolutePath, wtPath, branch);
|
|
35653
|
-
log2(`[worktrees] Added worktree ${wtPath} (branch ${branch}).`);
|
|
35654
|
-
worktreePaths.push(wtPath);
|
|
35655
|
-
} catch (e) {
|
|
35656
|
-
log2(
|
|
35657
|
-
`[worktrees] Worktree add failed for ${repo.absolutePath}: ${e instanceof Error ? e.message : String(e)}`
|
|
35658
|
-
);
|
|
35659
|
-
}
|
|
35660
|
-
}
|
|
35661
|
-
if (worktreePaths.length === 0) return null;
|
|
35662
|
-
return {
|
|
35663
|
-
worktreePaths,
|
|
35664
|
-
sessionParentPath: sessionDir,
|
|
35665
|
-
workingTreeRelRoot: sessionDir
|
|
35666
|
-
};
|
|
35667
|
-
}
|
|
35668
|
-
|
|
35669
|
-
// src/git/branches/rename-branch.ts
|
|
35670
|
-
async function gitRenameCurrentBranch(repoDir, newName) {
|
|
35671
|
-
const g = cliSimpleGit(repoDir);
|
|
35672
|
-
await g.raw(["branch", "-m", newName]);
|
|
35673
|
-
}
|
|
35674
|
-
|
|
35675
|
-
// src/worktrees/rename-session-worktree-branches.ts
|
|
35676
|
-
async function renameSessionWorktreeBranches(paths, newBranch, log2) {
|
|
35677
|
-
const safe = newBranch.replace(/[^a-zA-Z0-9/_-]+/g, "-").slice(0, 80) || "session-branch";
|
|
35678
|
-
for (const wt of paths) {
|
|
35679
|
-
try {
|
|
35680
|
-
await gitRenameCurrentBranch(wt, safe);
|
|
35681
|
-
log2(`[worktrees] Renamed branch in ${wt} \u2192 ${safe}`);
|
|
35682
|
-
} catch (e) {
|
|
35683
|
-
log2(
|
|
35684
|
-
`[worktrees] Branch rename failed in ${wt}: ${e instanceof Error ? e.message : String(e)}`
|
|
35685
|
-
);
|
|
35686
|
-
}
|
|
35687
|
-
}
|
|
35688
|
-
}
|
|
35689
|
-
|
|
35690
|
-
// src/worktrees/remove-session-worktrees.ts
|
|
35691
|
-
import * as fs21 from "node:fs";
|
|
35692
|
-
|
|
35693
|
-
// src/git/worktrees/worktree-remove.ts
|
|
35694
|
-
import * as fs20 from "node:fs";
|
|
35695
|
-
|
|
35696
|
-
// src/git/worktrees/resolve-main-repo-from-git-file.ts
|
|
35697
|
-
import * as fs19 from "node:fs";
|
|
35698
|
-
import * as path22 from "node:path";
|
|
35699
|
-
function resolveMainRepoFromWorktreeGitFile(wt) {
|
|
35700
|
-
const gitDirFile = path22.join(wt, ".git");
|
|
35701
|
-
if (!fs19.existsSync(gitDirFile) || !fs19.statSync(gitDirFile).isFile()) return "";
|
|
35702
|
-
const first2 = fs19.readFileSync(gitDirFile, "utf8").trim();
|
|
35703
|
-
const m = first2.match(/^gitdir:\s*(.+)$/im);
|
|
35704
|
-
if (!m) return "";
|
|
35705
|
-
const gitWorktreePath = path22.resolve(wt, m[1].trim());
|
|
35706
|
-
const gitDir = path22.dirname(path22.dirname(gitWorktreePath));
|
|
35707
|
-
return path22.dirname(gitDir);
|
|
35708
|
-
}
|
|
35653
|
+
// src/git/changes/types.ts
|
|
35654
|
+
var MAX_PATCH_CHARS = 35e4;
|
|
35709
35655
|
|
|
35710
|
-
// src/git/
|
|
35711
|
-
|
|
35712
|
-
const
|
|
35713
|
-
|
|
35714
|
-
|
|
35715
|
-
} else {
|
|
35716
|
-
fs20.rmSync(worktreePath, { recursive: true, force: true });
|
|
35717
|
-
}
|
|
35656
|
+
// src/git/changes/lib/repo-format.ts
|
|
35657
|
+
function posixJoinDirFile(dir, file2) {
|
|
35658
|
+
const d = dir === "." || dir === "" ? "" : dir.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
35659
|
+
const f = file2.replace(/\\/g, "/").replace(/^\/+/, "");
|
|
35660
|
+
return d ? `${d}/${f}` : f;
|
|
35718
35661
|
}
|
|
35719
|
-
|
|
35720
|
-
|
|
35721
|
-
|
|
35722
|
-
for (const wt of paths) {
|
|
35662
|
+
function formatRepoShortTitle(remoteUrl, repoRelPath) {
|
|
35663
|
+
const u = remoteUrl.trim();
|
|
35664
|
+
if (u) {
|
|
35723
35665
|
try {
|
|
35724
|
-
|
|
35725
|
-
|
|
35726
|
-
|
|
35727
|
-
|
|
35728
|
-
|
|
35729
|
-
|
|
35730
|
-
}
|
|
35666
|
+
if (u.startsWith("git@")) {
|
|
35667
|
+
const colon = u.indexOf(":");
|
|
35668
|
+
if (colon > 0) {
|
|
35669
|
+
const pathPart = u.slice(colon + 1).replace(/\.git$/i, "").replace(/\/+$/, "");
|
|
35670
|
+
if (pathPart.includes("/")) return pathPart;
|
|
35671
|
+
}
|
|
35672
|
+
} else {
|
|
35673
|
+
const parsed = new URL(u);
|
|
35674
|
+
const p = parsed.pathname.replace(/^\//, "").replace(/\.git$/i, "");
|
|
35675
|
+
const parts = p.split("/").filter(Boolean);
|
|
35676
|
+
if (parts.length >= 2) {
|
|
35677
|
+
return `${parts[parts.length - 2]}/${parts[parts.length - 1]}`;
|
|
35678
|
+
}
|
|
35679
|
+
if (parts.length === 1) return parts[0];
|
|
35731
35680
|
}
|
|
35681
|
+
} catch {
|
|
35732
35682
|
}
|
|
35733
35683
|
}
|
|
35734
|
-
|
|
35735
|
-
|
|
35736
|
-
|
|
35737
|
-
|
|
35738
|
-
const m = /* @__PURE__ */ new Map();
|
|
35739
|
-
for (const line of lines) {
|
|
35740
|
-
if (!line.trim()) continue;
|
|
35741
|
-
const tabParts = line.split(" ");
|
|
35742
|
-
if (tabParts.length < 2) continue;
|
|
35743
|
-
const status = tabParts[0].trim();
|
|
35744
|
-
const code = status[0];
|
|
35745
|
-
if (code === "A") {
|
|
35746
|
-
m.set(tabParts[tabParts.length - 1], "added");
|
|
35747
|
-
} else if (code === "D") {
|
|
35748
|
-
m.set(tabParts[tabParts.length - 1], "removed");
|
|
35749
|
-
} else if (code === "R" || code === "C") {
|
|
35750
|
-
if (tabParts.length >= 3) m.set(tabParts[tabParts.length - 1], "modified");
|
|
35751
|
-
} else if (code === "M" || code === "U" || code === "T") {
|
|
35752
|
-
m.set(tabParts[tabParts.length - 1], "modified");
|
|
35753
|
-
}
|
|
35754
|
-
}
|
|
35755
|
-
return m;
|
|
35756
|
-
}
|
|
35757
|
-
function parseNumstatFirstLine(line) {
|
|
35758
|
-
const parts = line.split(" ");
|
|
35759
|
-
if (parts.length < 3) return null;
|
|
35760
|
-
const [a, d] = parts;
|
|
35761
|
-
const additions = a === "-" ? 0 : parseInt(String(a), 10) || 0;
|
|
35762
|
-
const deletions = d === "-" ? 0 : parseInt(String(d), 10) || 0;
|
|
35763
|
-
return { additions, deletions };
|
|
35764
|
-
}
|
|
35765
|
-
function parseNumstat(lines) {
|
|
35766
|
-
const m = /* @__PURE__ */ new Map();
|
|
35767
|
-
for (const line of lines) {
|
|
35768
|
-
if (!line.trim()) continue;
|
|
35769
|
-
const parts = line.split(" ");
|
|
35770
|
-
if (parts.length < 3) continue;
|
|
35771
|
-
const [a, d, p] = parts;
|
|
35772
|
-
const additions = a === "-" ? 0 : parseInt(String(a), 10) || 0;
|
|
35773
|
-
const deletions = d === "-" ? 0 : parseInt(String(d), 10) || 0;
|
|
35774
|
-
m.set(p, { additions, deletions });
|
|
35684
|
+
if (repoRelPath && repoRelPath !== ".") {
|
|
35685
|
+
const segments = repoRelPath.split("/").filter(Boolean);
|
|
35686
|
+
const last2 = segments[segments.length - 1];
|
|
35687
|
+
if (last2) return last2;
|
|
35775
35688
|
}
|
|
35776
|
-
return
|
|
35689
|
+
return "Repository";
|
|
35777
35690
|
}
|
|
35778
|
-
|
|
35779
|
-
const
|
|
35691
|
+
function formatRemoteDisplayLabel(remoteUrl) {
|
|
35692
|
+
const u = remoteUrl.trim();
|
|
35693
|
+
if (!u) return "";
|
|
35694
|
+
let hostPath = u;
|
|
35780
35695
|
try {
|
|
35781
|
-
|
|
35782
|
-
|
|
35783
|
-
|
|
35696
|
+
if (u.startsWith("git@")) {
|
|
35697
|
+
const rest = u.slice("git@".length);
|
|
35698
|
+
const slash = rest.indexOf(":");
|
|
35699
|
+
if (slash > 0) hostPath = `${rest.slice(0, slash)}/${rest.slice(slash + 1)}`;
|
|
35700
|
+
} else {
|
|
35701
|
+
const parsed = new URL(u);
|
|
35702
|
+
hostPath = `${parsed.hostname}${parsed.pathname}`.replace(/\/\.git$/i, "").replace(/\.git$/i, "");
|
|
35703
|
+
}
|
|
35784
35704
|
} catch {
|
|
35785
|
-
|
|
35786
|
-
}
|
|
35787
|
-
}
|
|
35788
|
-
|
|
35789
|
-
// src/git/changes/lib/working-tree-changed-path-count.ts
|
|
35790
|
-
function workingTreeChangedPathCount(nameStatusLines, numstatLines, untrackedLines) {
|
|
35791
|
-
const kindByPath = parseNameStatusLines(nameStatusLines);
|
|
35792
|
-
const numByPath = parseNumstat(numstatLines);
|
|
35793
|
-
const paths = /* @__PURE__ */ new Set([...kindByPath.keys(), ...numByPath.keys()]);
|
|
35794
|
-
for (const p of untrackedLines.map((s) => s.trim()).filter(Boolean)) {
|
|
35795
|
-
paths.add(p);
|
|
35705
|
+
hostPath = u.replace(/^https?:\/\//i, "").replace(/\.git$/i, "");
|
|
35796
35706
|
}
|
|
35797
|
-
return
|
|
35707
|
+
return `origin \xB7 ${hostPath}`;
|
|
35798
35708
|
}
|
|
35799
35709
|
|
|
35800
|
-
// src/git/changes/
|
|
35801
|
-
|
|
35802
|
-
return runGitTask(async () => {
|
|
35803
|
-
const g = cliSimpleGit(repoGitCwd);
|
|
35804
|
-
const [nameStatusRaw, numstatRaw, untrackedRaw] = await Promise.all([
|
|
35805
|
-
g.raw(["diff", "--name-status", "HEAD"]).catch(() => ""),
|
|
35806
|
-
g.raw(["diff", "HEAD", "--numstat"]).catch(() => ""),
|
|
35807
|
-
g.raw(["ls-files", "--others", "--exclude-standard"]).catch(() => "")
|
|
35808
|
-
]);
|
|
35809
|
-
return workingTreeChangedPathCount(
|
|
35810
|
-
String(nameStatusRaw).split("\n"),
|
|
35811
|
-
String(numstatRaw).split("\n"),
|
|
35812
|
-
String(untrackedRaw).split("\n")
|
|
35813
|
-
);
|
|
35814
|
-
});
|
|
35815
|
-
}
|
|
35710
|
+
// src/git/changes/get-working-tree-change-repo-details.ts
|
|
35711
|
+
import * as path21 from "node:path";
|
|
35816
35712
|
|
|
35817
35713
|
// src/git/commits/resolve-remote-tracking.ts
|
|
35818
35714
|
async function tryConfigGet(g, key) {
|
|
@@ -35912,96 +35808,6 @@ async function commitsAheadOfRemoteTracking(repoDir) {
|
|
|
35912
35808
|
}
|
|
35913
35809
|
}
|
|
35914
35810
|
|
|
35915
|
-
// src/git/status/working-tree-status.ts
|
|
35916
|
-
async function getRepoWorkingTreeStatus(repoDir) {
|
|
35917
|
-
return runGitTask(async () => {
|
|
35918
|
-
const uncommittedFileCount = await countWorkingTreeChangedFilesForRepo(repoDir);
|
|
35919
|
-
const hasUncommittedChanges = uncommittedFileCount > 0;
|
|
35920
|
-
const ahead = await commitsAheadOfRemoteTracking(repoDir);
|
|
35921
|
-
return { hasUncommittedChanges, hasUnpushedCommits: ahead > 0, uncommittedFileCount };
|
|
35922
|
-
});
|
|
35923
|
-
}
|
|
35924
|
-
async function aggregateSessionPathsWorkingTreeStatus(paths) {
|
|
35925
|
-
let hasUncommittedChanges = false;
|
|
35926
|
-
let hasUnpushedCommits = false;
|
|
35927
|
-
let uncommittedFileCount = 0;
|
|
35928
|
-
await forEachWithGitYield(paths, async (p) => {
|
|
35929
|
-
const s = await getRepoWorkingTreeStatus(p);
|
|
35930
|
-
uncommittedFileCount += s.uncommittedFileCount;
|
|
35931
|
-
if (s.hasUncommittedChanges) hasUncommittedChanges = true;
|
|
35932
|
-
if (s.hasUnpushedCommits) hasUnpushedCommits = true;
|
|
35933
|
-
});
|
|
35934
|
-
return { hasUncommittedChanges, hasUnpushedCommits, uncommittedFileCount };
|
|
35935
|
-
}
|
|
35936
|
-
async function pushAheadOfUpstreamForPaths(paths) {
|
|
35937
|
-
await forEachWithGitYield(paths, async (p) => {
|
|
35938
|
-
const g = cliSimpleGit(p);
|
|
35939
|
-
const ahead = await commitsAheadOfRemoteTracking(p);
|
|
35940
|
-
if (ahead <= 0) return;
|
|
35941
|
-
await g.push();
|
|
35942
|
-
});
|
|
35943
|
-
}
|
|
35944
|
-
|
|
35945
|
-
// src/git/changes/types.ts
|
|
35946
|
-
var MAX_PATCH_CHARS = 35e4;
|
|
35947
|
-
|
|
35948
|
-
// src/git/changes/lib/repo-format.ts
|
|
35949
|
-
function posixJoinDirFile(dir, file2) {
|
|
35950
|
-
const d = dir === "." || dir === "" ? "" : dir.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
35951
|
-
const f = file2.replace(/\\/g, "/").replace(/^\/+/, "");
|
|
35952
|
-
return d ? `${d}/${f}` : f;
|
|
35953
|
-
}
|
|
35954
|
-
function formatRepoShortTitle(remoteUrl, repoRelPath) {
|
|
35955
|
-
const u = remoteUrl.trim();
|
|
35956
|
-
if (u) {
|
|
35957
|
-
try {
|
|
35958
|
-
if (u.startsWith("git@")) {
|
|
35959
|
-
const colon = u.indexOf(":");
|
|
35960
|
-
if (colon > 0) {
|
|
35961
|
-
const pathPart = u.slice(colon + 1).replace(/\.git$/i, "").replace(/\/+$/, "");
|
|
35962
|
-
if (pathPart.includes("/")) return pathPart;
|
|
35963
|
-
}
|
|
35964
|
-
} else {
|
|
35965
|
-
const parsed = new URL(u);
|
|
35966
|
-
const p = parsed.pathname.replace(/^\//, "").replace(/\.git$/i, "");
|
|
35967
|
-
const parts = p.split("/").filter(Boolean);
|
|
35968
|
-
if (parts.length >= 2) {
|
|
35969
|
-
return `${parts[parts.length - 2]}/${parts[parts.length - 1]}`;
|
|
35970
|
-
}
|
|
35971
|
-
if (parts.length === 1) return parts[0];
|
|
35972
|
-
}
|
|
35973
|
-
} catch {
|
|
35974
|
-
}
|
|
35975
|
-
}
|
|
35976
|
-
if (repoRelPath && repoRelPath !== ".") {
|
|
35977
|
-
const segments = repoRelPath.split("/").filter(Boolean);
|
|
35978
|
-
const last2 = segments[segments.length - 1];
|
|
35979
|
-
if (last2) return last2;
|
|
35980
|
-
}
|
|
35981
|
-
return "Repository";
|
|
35982
|
-
}
|
|
35983
|
-
function formatRemoteDisplayLabel(remoteUrl) {
|
|
35984
|
-
const u = remoteUrl.trim();
|
|
35985
|
-
if (!u) return "";
|
|
35986
|
-
let hostPath = u;
|
|
35987
|
-
try {
|
|
35988
|
-
if (u.startsWith("git@")) {
|
|
35989
|
-
const rest = u.slice("git@".length);
|
|
35990
|
-
const slash = rest.indexOf(":");
|
|
35991
|
-
if (slash > 0) hostPath = `${rest.slice(0, slash)}/${rest.slice(slash + 1)}`;
|
|
35992
|
-
} else {
|
|
35993
|
-
const parsed = new URL(u);
|
|
35994
|
-
hostPath = `${parsed.hostname}${parsed.pathname}`.replace(/\/\.git$/i, "").replace(/\.git$/i, "");
|
|
35995
|
-
}
|
|
35996
|
-
} catch {
|
|
35997
|
-
hostPath = u.replace(/^https?:\/\//i, "").replace(/\.git$/i, "");
|
|
35998
|
-
}
|
|
35999
|
-
return `origin \xB7 ${hostPath}`;
|
|
36000
|
-
}
|
|
36001
|
-
|
|
36002
|
-
// src/git/changes/get-working-tree-change-repo-details.ts
|
|
36003
|
-
import * as path24 from "node:path";
|
|
36004
|
-
|
|
36005
35811
|
// src/git/commits/lib/parse-log-lines.ts
|
|
36006
35812
|
function parseLogShaDateSubjectLines(raw) {
|
|
36007
35813
|
const out = [];
|
|
@@ -36072,6 +35878,59 @@ async function listRecentCommits(repoDir, limitInput) {
|
|
|
36072
35878
|
}
|
|
36073
35879
|
}
|
|
36074
35880
|
|
|
35881
|
+
// src/git/changes/lib/parse-git-status.ts
|
|
35882
|
+
function parseNameStatusLines(lines) {
|
|
35883
|
+
const m = /* @__PURE__ */ new Map();
|
|
35884
|
+
for (const line of lines) {
|
|
35885
|
+
if (!line.trim()) continue;
|
|
35886
|
+
const tabParts = line.split(" ");
|
|
35887
|
+
if (tabParts.length < 2) continue;
|
|
35888
|
+
const status = tabParts[0].trim();
|
|
35889
|
+
const code = status[0];
|
|
35890
|
+
if (code === "A") {
|
|
35891
|
+
m.set(tabParts[tabParts.length - 1], "added");
|
|
35892
|
+
} else if (code === "D") {
|
|
35893
|
+
m.set(tabParts[tabParts.length - 1], "removed");
|
|
35894
|
+
} else if (code === "R" || code === "C") {
|
|
35895
|
+
if (tabParts.length >= 3) m.set(tabParts[tabParts.length - 1], "modified");
|
|
35896
|
+
} else if (code === "M" || code === "U" || code === "T") {
|
|
35897
|
+
m.set(tabParts[tabParts.length - 1], "modified");
|
|
35898
|
+
}
|
|
35899
|
+
}
|
|
35900
|
+
return m;
|
|
35901
|
+
}
|
|
35902
|
+
function parseNumstatFirstLine(line) {
|
|
35903
|
+
const parts = line.split(" ");
|
|
35904
|
+
if (parts.length < 3) return null;
|
|
35905
|
+
const [a, d] = parts;
|
|
35906
|
+
const additions = a === "-" ? 0 : parseInt(String(a), 10) || 0;
|
|
35907
|
+
const deletions = d === "-" ? 0 : parseInt(String(d), 10) || 0;
|
|
35908
|
+
return { additions, deletions };
|
|
35909
|
+
}
|
|
35910
|
+
function parseNumstat(lines) {
|
|
35911
|
+
const m = /* @__PURE__ */ new Map();
|
|
35912
|
+
for (const line of lines) {
|
|
35913
|
+
if (!line.trim()) continue;
|
|
35914
|
+
const parts = line.split(" ");
|
|
35915
|
+
if (parts.length < 3) continue;
|
|
35916
|
+
const [a, d, p] = parts;
|
|
35917
|
+
const additions = a === "-" ? 0 : parseInt(String(a), 10) || 0;
|
|
35918
|
+
const deletions = d === "-" ? 0 : parseInt(String(d), 10) || 0;
|
|
35919
|
+
m.set(p, { additions, deletions });
|
|
35920
|
+
}
|
|
35921
|
+
return m;
|
|
35922
|
+
}
|
|
35923
|
+
async function numstatFromGitNoIndex(g, pathInRepo) {
|
|
35924
|
+
const devNull = process.platform === "win32" ? "NUL" : "/dev/null";
|
|
35925
|
+
try {
|
|
35926
|
+
const out = await g.raw(["diff", "--numstat", "--no-index", "--", devNull, pathInRepo]);
|
|
35927
|
+
const first2 = String(out).split("\n").find((l) => l.trim()) ?? "";
|
|
35928
|
+
return parseNumstatFirstLine(first2);
|
|
35929
|
+
} catch {
|
|
35930
|
+
return null;
|
|
35931
|
+
}
|
|
35932
|
+
}
|
|
35933
|
+
|
|
36075
35934
|
// src/git/changes/lib/patch-truncate.ts
|
|
36076
35935
|
function truncatePatch(s) {
|
|
36077
35936
|
if (s.length <= MAX_PATCH_CHARS) return s;
|
|
@@ -36137,8 +35996,8 @@ async function listChangedFilesForCommit(repoGitCwd, repoRelPath, commitSha) {
|
|
|
36137
35996
|
}
|
|
36138
35997
|
|
|
36139
35998
|
// src/git/changes/list-changed-files-for-repo.ts
|
|
36140
|
-
import * as
|
|
36141
|
-
import * as
|
|
35999
|
+
import * as fs18 from "node:fs";
|
|
36000
|
+
import * as path20 from "node:path";
|
|
36142
36001
|
|
|
36143
36002
|
// src/git/changes/lib/count-lines.ts
|
|
36144
36003
|
import { createReadStream } from "node:fs";
|
|
@@ -36162,7 +36021,7 @@ async function countTextFileLines(filePath) {
|
|
|
36162
36021
|
}
|
|
36163
36022
|
|
|
36164
36023
|
// src/git/changes/hydrate-patch.ts
|
|
36165
|
-
import * as
|
|
36024
|
+
import * as fs17 from "node:fs";
|
|
36166
36025
|
var UNIFIED_HUNK_HEADER_RE = /^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/;
|
|
36167
36026
|
var MAX_HYDRATE_LINES_PER_GAP = 8e3;
|
|
36168
36027
|
var MAX_HYDRATE_LINES_PER_FILE = 8e4;
|
|
@@ -36177,7 +36036,7 @@ async function readGitBlobLines(repoCwd, pathInRepo) {
|
|
|
36177
36036
|
}
|
|
36178
36037
|
async function readWorktreeFileLines(filePath) {
|
|
36179
36038
|
try {
|
|
36180
|
-
const raw = await
|
|
36039
|
+
const raw = await fs17.promises.readFile(filePath, "utf8");
|
|
36181
36040
|
return raw.split(/\r?\n/);
|
|
36182
36041
|
} catch {
|
|
36183
36042
|
return null;
|
|
@@ -36302,7 +36161,7 @@ async function listChangedFilesForRepo(repoGitCwd, repoRelPath) {
|
|
|
36302
36161
|
const rows = [];
|
|
36303
36162
|
await forEachWithGitYield([...paths], async (pathInRepo) => {
|
|
36304
36163
|
const relLauncher = posixJoinDirFile(repoRelPath, pathInRepo.replace(/\\/g, "/"));
|
|
36305
|
-
const repoFilePath =
|
|
36164
|
+
const repoFilePath = path20.join(repoGitCwd, pathInRepo);
|
|
36306
36165
|
const nums = numByPath.get(pathInRepo);
|
|
36307
36166
|
let additions = nums?.additions ?? 0;
|
|
36308
36167
|
let deletions = nums?.deletions ?? 0;
|
|
@@ -36315,7 +36174,7 @@ async function listChangedFilesForRepo(repoGitCwd, repoRelPath) {
|
|
|
36315
36174
|
deletions = fromGit.deletions;
|
|
36316
36175
|
} else {
|
|
36317
36176
|
try {
|
|
36318
|
-
const st = await
|
|
36177
|
+
const st = await fs18.promises.stat(repoFilePath);
|
|
36319
36178
|
if (st.isFile()) additions = await countTextFileLines(repoFilePath);
|
|
36320
36179
|
else additions = 0;
|
|
36321
36180
|
} catch {
|
|
@@ -36341,7 +36200,7 @@ async function listChangedFilesForRepo(repoGitCwd, repoRelPath) {
|
|
|
36341
36200
|
} else {
|
|
36342
36201
|
pathInRepo = row.pathRelLauncher;
|
|
36343
36202
|
}
|
|
36344
|
-
const filePath =
|
|
36203
|
+
const filePath = path20.join(repoGitCwd, pathInRepo);
|
|
36345
36204
|
let patch = await unifiedDiffForFile(repoGitCwd, pathInRepo, row.change);
|
|
36346
36205
|
if (patch) {
|
|
36347
36206
|
patch = await hydrateUnifiedPatchWithFileContext(patch, filePath, repoGitCwd, pathInRepo, row.change);
|
|
@@ -36357,8 +36216,8 @@ function normRepoRel(p) {
|
|
|
36357
36216
|
return x === "" ? "." : x;
|
|
36358
36217
|
}
|
|
36359
36218
|
async function getWorkingTreeChangeRepoDetails(options) {
|
|
36360
|
-
const bridgeRoot =
|
|
36361
|
-
const sessionWtRoot = options.sessionWorktreeRootPath ?
|
|
36219
|
+
const bridgeRoot = path21.resolve(getBridgeRoot());
|
|
36220
|
+
const sessionWtRoot = options.sessionWorktreeRootPath ? path21.resolve(options.sessionWorktreeRootPath) : null;
|
|
36362
36221
|
const legacyNested = options.legacyRepoNestedSessionLayout === true;
|
|
36363
36222
|
const out = [];
|
|
36364
36223
|
const filter = options.repoFilterRelPath != null ? normRepoRel(options.repoFilterRelPath) : null;
|
|
@@ -36373,7 +36232,7 @@ async function getWorkingTreeChangeRepoDetails(options) {
|
|
|
36373
36232
|
for (let i = 0; i < options.commitTargetPaths.length; i++) {
|
|
36374
36233
|
if (i > 0) await yieldToEventLoop2();
|
|
36375
36234
|
const target = options.commitTargetPaths[i];
|
|
36376
|
-
const t =
|
|
36235
|
+
const t = path21.resolve(target);
|
|
36377
36236
|
if (!await isGitRepoDirectory(t)) continue;
|
|
36378
36237
|
const g = cliSimpleGit(t);
|
|
36379
36238
|
let branch = "HEAD";
|
|
@@ -36386,8 +36245,8 @@ async function getWorkingTreeChangeRepoDetails(options) {
|
|
|
36386
36245
|
const remoteDisplay = formatRemoteDisplayLabel(remoteUrl);
|
|
36387
36246
|
let repoRelPath;
|
|
36388
36247
|
if (sessionWtRoot) {
|
|
36389
|
-
const anchor = legacyNested ?
|
|
36390
|
-
const relNorm =
|
|
36248
|
+
const anchor = legacyNested ? path21.dirname(t) : t;
|
|
36249
|
+
const relNorm = path21.relative(sessionWtRoot, anchor);
|
|
36391
36250
|
repoRelPath = relNorm === "" ? "." : relNorm.replace(/\\/g, "/");
|
|
36392
36251
|
} else {
|
|
36393
36252
|
let top = t;
|
|
@@ -36396,8 +36255,8 @@ async function getWorkingTreeChangeRepoDetails(options) {
|
|
|
36396
36255
|
} catch {
|
|
36397
36256
|
top = t;
|
|
36398
36257
|
}
|
|
36399
|
-
const rel =
|
|
36400
|
-
repoRelPath = rel.startsWith("..") ?
|
|
36258
|
+
const rel = path21.relative(bridgeRoot, path21.resolve(top)).replace(/\\/g, "/") || ".";
|
|
36259
|
+
repoRelPath = rel.startsWith("..") ? path21.basename(path21.resolve(top)) : rel;
|
|
36401
36260
|
}
|
|
36402
36261
|
const norm = normRepoRel(repoRelPath === "" ? "." : repoRelPath);
|
|
36403
36262
|
if (filter && norm !== filter) continue;
|
|
@@ -36429,6 +36288,64 @@ async function getWorkingTreeChangeRepoDetails(options) {
|
|
|
36429
36288
|
return out;
|
|
36430
36289
|
}
|
|
36431
36290
|
|
|
36291
|
+
// src/git/changes/lib/working-tree-changed-path-count.ts
|
|
36292
|
+
function workingTreeChangedPathCount(nameStatusLines, numstatLines, untrackedLines) {
|
|
36293
|
+
const kindByPath = parseNameStatusLines(nameStatusLines);
|
|
36294
|
+
const numByPath = parseNumstat(numstatLines);
|
|
36295
|
+
const paths = /* @__PURE__ */ new Set([...kindByPath.keys(), ...numByPath.keys()]);
|
|
36296
|
+
for (const p of untrackedLines.map((s) => s.trim()).filter(Boolean)) {
|
|
36297
|
+
paths.add(p);
|
|
36298
|
+
}
|
|
36299
|
+
return paths.size;
|
|
36300
|
+
}
|
|
36301
|
+
|
|
36302
|
+
// src/git/changes/count-working-tree-changed-files.ts
|
|
36303
|
+
async function countWorkingTreeChangedFilesForRepo(repoGitCwd) {
|
|
36304
|
+
return runGitTask(async () => {
|
|
36305
|
+
const g = cliSimpleGit(repoGitCwd);
|
|
36306
|
+
const [nameStatusRaw, numstatRaw, untrackedRaw] = await Promise.all([
|
|
36307
|
+
g.raw(["diff", "--name-status", "HEAD"]).catch(() => ""),
|
|
36308
|
+
g.raw(["diff", "HEAD", "--numstat"]).catch(() => ""),
|
|
36309
|
+
g.raw(["ls-files", "--others", "--exclude-standard"]).catch(() => "")
|
|
36310
|
+
]);
|
|
36311
|
+
return workingTreeChangedPathCount(
|
|
36312
|
+
String(nameStatusRaw).split("\n"),
|
|
36313
|
+
String(numstatRaw).split("\n"),
|
|
36314
|
+
String(untrackedRaw).split("\n")
|
|
36315
|
+
);
|
|
36316
|
+
});
|
|
36317
|
+
}
|
|
36318
|
+
|
|
36319
|
+
// src/git/status/working-tree-status.ts
|
|
36320
|
+
async function getRepoWorkingTreeStatus(repoDir) {
|
|
36321
|
+
return runGitTask(async () => {
|
|
36322
|
+
const uncommittedFileCount = await countWorkingTreeChangedFilesForRepo(repoDir);
|
|
36323
|
+
const hasUncommittedChanges = uncommittedFileCount > 0;
|
|
36324
|
+
const ahead = await commitsAheadOfRemoteTracking(repoDir);
|
|
36325
|
+
return { hasUncommittedChanges, hasUnpushedCommits: ahead > 0, uncommittedFileCount };
|
|
36326
|
+
});
|
|
36327
|
+
}
|
|
36328
|
+
async function aggregateSessionPathsWorkingTreeStatus(paths) {
|
|
36329
|
+
let hasUncommittedChanges = false;
|
|
36330
|
+
let hasUnpushedCommits = false;
|
|
36331
|
+
let uncommittedFileCount = 0;
|
|
36332
|
+
await forEachWithGitYield(paths, async (p) => {
|
|
36333
|
+
const s = await getRepoWorkingTreeStatus(p);
|
|
36334
|
+
uncommittedFileCount += s.uncommittedFileCount;
|
|
36335
|
+
if (s.hasUncommittedChanges) hasUncommittedChanges = true;
|
|
36336
|
+
if (s.hasUnpushedCommits) hasUnpushedCommits = true;
|
|
36337
|
+
});
|
|
36338
|
+
return { hasUncommittedChanges, hasUnpushedCommits, uncommittedFileCount };
|
|
36339
|
+
}
|
|
36340
|
+
async function pushAheadOfUpstreamForPaths(paths) {
|
|
36341
|
+
await forEachWithGitYield(paths, async (p) => {
|
|
36342
|
+
const g = cliSimpleGit(p);
|
|
36343
|
+
const ahead = await commitsAheadOfRemoteTracking(p);
|
|
36344
|
+
if (ahead <= 0) return;
|
|
36345
|
+
await g.push();
|
|
36346
|
+
});
|
|
36347
|
+
}
|
|
36348
|
+
|
|
36432
36349
|
// src/git/branches/commit-and-push.ts
|
|
36433
36350
|
async function gitCommitAllIfDirty(repoDir, message, options) {
|
|
36434
36351
|
const g = cliSimpleGit(repoDir);
|
|
@@ -36466,12 +36383,137 @@ async function commitSessionWorktrees(options) {
|
|
|
36466
36383
|
}
|
|
36467
36384
|
}
|
|
36468
36385
|
|
|
36386
|
+
// src/worktrees/remove-session-worktrees.ts
|
|
36387
|
+
import * as fs21 from "node:fs";
|
|
36388
|
+
|
|
36389
|
+
// src/git/worktrees/worktree-remove.ts
|
|
36390
|
+
import * as fs20 from "node:fs";
|
|
36391
|
+
|
|
36392
|
+
// src/git/worktrees/resolve-main-repo-from-git-file.ts
|
|
36393
|
+
import * as fs19 from "node:fs";
|
|
36394
|
+
import * as path22 from "node:path";
|
|
36395
|
+
function resolveMainRepoFromWorktreeGitFile(wt) {
|
|
36396
|
+
const gitDirFile = path22.join(wt, ".git");
|
|
36397
|
+
if (!fs19.existsSync(gitDirFile) || !fs19.statSync(gitDirFile).isFile()) return "";
|
|
36398
|
+
const first2 = fs19.readFileSync(gitDirFile, "utf8").trim();
|
|
36399
|
+
const m = first2.match(/^gitdir:\s*(.+)$/im);
|
|
36400
|
+
if (!m) return "";
|
|
36401
|
+
const gitWorktreePath = path22.resolve(wt, m[1].trim());
|
|
36402
|
+
const gitDir = path22.dirname(path22.dirname(gitWorktreePath));
|
|
36403
|
+
return path22.dirname(gitDir);
|
|
36404
|
+
}
|
|
36405
|
+
|
|
36406
|
+
// src/git/worktrees/worktree-remove.ts
|
|
36407
|
+
async function gitWorktreeRemoveForce(worktreePath) {
|
|
36408
|
+
const mainRepo = resolveMainRepoFromWorktreeGitFile(worktreePath);
|
|
36409
|
+
if (mainRepo) {
|
|
36410
|
+
await cliSimpleGit(mainRepo).raw(["worktree", "remove", "--force", worktreePath]);
|
|
36411
|
+
} else {
|
|
36412
|
+
fs20.rmSync(worktreePath, { recursive: true, force: true });
|
|
36413
|
+
}
|
|
36414
|
+
}
|
|
36415
|
+
|
|
36416
|
+
// src/worktrees/remove-session-worktrees.ts
|
|
36417
|
+
async function removeSessionWorktrees(paths, log2) {
|
|
36418
|
+
for (const wt of paths) {
|
|
36419
|
+
try {
|
|
36420
|
+
await gitWorktreeRemoveForce(wt);
|
|
36421
|
+
log2(`[worktrees] Removed worktree ${wt}`);
|
|
36422
|
+
} catch (e) {
|
|
36423
|
+
log2(`[worktrees] Remove failed for ${wt}: ${e instanceof Error ? e.message : String(e)}`);
|
|
36424
|
+
try {
|
|
36425
|
+
fs21.rmSync(wt, { recursive: true, force: true });
|
|
36426
|
+
} catch {
|
|
36427
|
+
}
|
|
36428
|
+
}
|
|
36429
|
+
}
|
|
36430
|
+
}
|
|
36431
|
+
|
|
36432
|
+
// src/git/branches/rename-branch.ts
|
|
36433
|
+
async function gitRenameCurrentBranch(repoDir, newName) {
|
|
36434
|
+
const g = cliSimpleGit(repoDir);
|
|
36435
|
+
await g.raw(["branch", "-m", newName]);
|
|
36436
|
+
}
|
|
36437
|
+
|
|
36438
|
+
// src/worktrees/rename-session-worktree-branches.ts
|
|
36439
|
+
async function renameSessionWorktreeBranches(paths, newBranch, log2) {
|
|
36440
|
+
const safe = newBranch.replace(/[^a-zA-Z0-9/_-]+/g, "-").slice(0, 80) || "session-branch";
|
|
36441
|
+
for (const wt of paths) {
|
|
36442
|
+
try {
|
|
36443
|
+
await gitRenameCurrentBranch(wt, safe);
|
|
36444
|
+
log2(`[worktrees] Renamed branch in ${wt} \u2192 ${safe}`);
|
|
36445
|
+
} catch (e) {
|
|
36446
|
+
log2(
|
|
36447
|
+
`[worktrees] Branch rename failed in ${wt}: ${e instanceof Error ? e.message : String(e)}`
|
|
36448
|
+
);
|
|
36449
|
+
}
|
|
36450
|
+
}
|
|
36451
|
+
}
|
|
36452
|
+
|
|
36453
|
+
// src/worktrees/worktree-layout-file.ts
|
|
36454
|
+
import * as fs22 from "node:fs";
|
|
36455
|
+
import * as path23 from "node:path";
|
|
36456
|
+
import os7 from "node:os";
|
|
36457
|
+
var LAYOUT_FILENAME = "worktree-launcher-layout.json";
|
|
36458
|
+
function defaultWorktreeLayoutPath() {
|
|
36459
|
+
return path23.join(os7.homedir(), ".buildautomaton", LAYOUT_FILENAME);
|
|
36460
|
+
}
|
|
36461
|
+
function normalizeLoadedLayout(raw) {
|
|
36462
|
+
if (raw && typeof raw === "object" && "launcherCwds" in raw) {
|
|
36463
|
+
const j = raw;
|
|
36464
|
+
if (Array.isArray(j.launcherCwds)) return { launcherCwds: j.launcherCwds };
|
|
36465
|
+
}
|
|
36466
|
+
return { launcherCwds: [] };
|
|
36467
|
+
}
|
|
36468
|
+
function loadWorktreeLayout() {
|
|
36469
|
+
try {
|
|
36470
|
+
const p = defaultWorktreeLayoutPath();
|
|
36471
|
+
if (!fs22.existsSync(p)) return { launcherCwds: [] };
|
|
36472
|
+
const raw = JSON.parse(fs22.readFileSync(p, "utf8"));
|
|
36473
|
+
return normalizeLoadedLayout(raw);
|
|
36474
|
+
} catch {
|
|
36475
|
+
return { launcherCwds: [] };
|
|
36476
|
+
}
|
|
36477
|
+
}
|
|
36478
|
+
function saveWorktreeLayout(layout) {
|
|
36479
|
+
try {
|
|
36480
|
+
const dir = path23.dirname(defaultWorktreeLayoutPath());
|
|
36481
|
+
fs22.mkdirSync(dir, { recursive: true });
|
|
36482
|
+
fs22.writeFileSync(defaultWorktreeLayoutPath(), JSON.stringify(layout, null, 2), "utf8");
|
|
36483
|
+
} catch {
|
|
36484
|
+
}
|
|
36485
|
+
}
|
|
36486
|
+
function baseNameSafe(pathString) {
|
|
36487
|
+
return path23.basename(pathString).replace(/[^a-zA-Z0-9._-]+/g, "-") || "cwd";
|
|
36488
|
+
}
|
|
36489
|
+
function getLauncherDirNameIfPresent(layout, bridgeRootPath2) {
|
|
36490
|
+
const norm = path23.resolve(bridgeRootPath2);
|
|
36491
|
+
const existing = layout.launcherCwds.find((e) => path23.resolve(e.absolutePath) === norm);
|
|
36492
|
+
return existing?.dirName;
|
|
36493
|
+
}
|
|
36494
|
+
function allocateDirNameForLauncherCwd(layout, bridgeRootPath2) {
|
|
36495
|
+
const existing = getLauncherDirNameIfPresent(layout, bridgeRootPath2);
|
|
36496
|
+
if (existing) return existing;
|
|
36497
|
+
const norm = path23.resolve(bridgeRootPath2);
|
|
36498
|
+
const base = baseNameSafe(norm);
|
|
36499
|
+
const used = new Set(layout.launcherCwds.map((e) => e.dirName));
|
|
36500
|
+
let name = base;
|
|
36501
|
+
let n = 2;
|
|
36502
|
+
while (used.has(name)) {
|
|
36503
|
+
name = `${base}-${n}`;
|
|
36504
|
+
n += 1;
|
|
36505
|
+
}
|
|
36506
|
+
layout.launcherCwds.push({ absolutePath: norm, dirName: name });
|
|
36507
|
+
saveWorktreeLayout(layout);
|
|
36508
|
+
return name;
|
|
36509
|
+
}
|
|
36510
|
+
|
|
36469
36511
|
// src/worktrees/discover-session-worktree-on-disk.ts
|
|
36470
|
-
import * as
|
|
36471
|
-
import * as
|
|
36512
|
+
import * as fs23 from "node:fs";
|
|
36513
|
+
import * as path24 from "node:path";
|
|
36472
36514
|
function isGitDir(dirPath) {
|
|
36473
36515
|
try {
|
|
36474
|
-
return
|
|
36516
|
+
return fs23.existsSync(path24.join(dirPath, ".git"));
|
|
36475
36517
|
} catch {
|
|
36476
36518
|
return false;
|
|
36477
36519
|
}
|
|
@@ -36480,23 +36522,23 @@ function collectGitRepoRootsUnderDirectory(rootPath) {
|
|
|
36480
36522
|
const out = [];
|
|
36481
36523
|
const walk = (dir) => {
|
|
36482
36524
|
if (isGitDir(dir)) {
|
|
36483
|
-
out.push(
|
|
36525
|
+
out.push(path24.resolve(dir));
|
|
36484
36526
|
return;
|
|
36485
36527
|
}
|
|
36486
36528
|
let entries;
|
|
36487
36529
|
try {
|
|
36488
|
-
entries =
|
|
36530
|
+
entries = fs23.readdirSync(dir, { withFileTypes: true });
|
|
36489
36531
|
} catch {
|
|
36490
36532
|
return;
|
|
36491
36533
|
}
|
|
36492
36534
|
for (const e of entries) {
|
|
36493
36535
|
if (e.name.startsWith(".")) continue;
|
|
36494
|
-
const full =
|
|
36536
|
+
const full = path24.join(dir, e.name);
|
|
36495
36537
|
if (!e.isDirectory()) continue;
|
|
36496
36538
|
walk(full);
|
|
36497
36539
|
}
|
|
36498
36540
|
};
|
|
36499
|
-
walk(
|
|
36541
|
+
walk(path24.resolve(rootPath));
|
|
36500
36542
|
return [...new Set(out)];
|
|
36501
36543
|
}
|
|
36502
36544
|
function collectWorktreeRootsNamed(root, sessionId, maxDepth) {
|
|
@@ -36505,16 +36547,16 @@ function collectWorktreeRootsNamed(root, sessionId, maxDepth) {
|
|
|
36505
36547
|
if (depth > maxDepth) return;
|
|
36506
36548
|
let entries;
|
|
36507
36549
|
try {
|
|
36508
|
-
entries =
|
|
36550
|
+
entries = fs23.readdirSync(dir, { withFileTypes: true });
|
|
36509
36551
|
} catch {
|
|
36510
36552
|
return;
|
|
36511
36553
|
}
|
|
36512
36554
|
for (const e of entries) {
|
|
36513
36555
|
if (e.name.startsWith(".")) continue;
|
|
36514
|
-
const full =
|
|
36556
|
+
const full = path24.join(dir, e.name);
|
|
36515
36557
|
if (!e.isDirectory()) continue;
|
|
36516
36558
|
if (e.name === sessionId) {
|
|
36517
|
-
if (isGitDir(full)) out.push(
|
|
36559
|
+
if (isGitDir(full)) out.push(path24.resolve(full));
|
|
36518
36560
|
} else {
|
|
36519
36561
|
walk(full, depth + 1);
|
|
36520
36562
|
}
|
|
@@ -36526,14 +36568,14 @@ function collectWorktreeRootsNamed(root, sessionId, maxDepth) {
|
|
|
36526
36568
|
function tryBindingFromSessionDirectory(sessionDir) {
|
|
36527
36569
|
let st;
|
|
36528
36570
|
try {
|
|
36529
|
-
st =
|
|
36571
|
+
st = fs23.statSync(sessionDir);
|
|
36530
36572
|
} catch {
|
|
36531
36573
|
return null;
|
|
36532
36574
|
}
|
|
36533
36575
|
if (!st.isDirectory()) return null;
|
|
36534
36576
|
const worktreePaths = collectGitRepoRootsUnderDirectory(sessionDir);
|
|
36535
36577
|
if (worktreePaths.length === 0) return null;
|
|
36536
|
-
const abs =
|
|
36578
|
+
const abs = path24.resolve(sessionDir);
|
|
36537
36579
|
return {
|
|
36538
36580
|
sessionParentPath: abs,
|
|
36539
36581
|
workingTreeRelRoot: abs,
|
|
@@ -36543,20 +36585,20 @@ function tryBindingFromSessionDirectory(sessionDir) {
|
|
|
36543
36585
|
function discoverLegacyBindingAscendingFromCheckout(sessionId, checkoutPath) {
|
|
36544
36586
|
const sid = sessionId.trim();
|
|
36545
36587
|
if (!sid) return null;
|
|
36546
|
-
const hintR =
|
|
36588
|
+
const hintR = path24.resolve(checkoutPath);
|
|
36547
36589
|
let best = null;
|
|
36548
|
-
let cur =
|
|
36590
|
+
let cur = path24.dirname(hintR);
|
|
36549
36591
|
for (let i = 0; i < 40; i++) {
|
|
36550
36592
|
const paths = collectWorktreeRootsNamed(cur, sid, 24);
|
|
36551
|
-
if (paths.some((p) =>
|
|
36552
|
-
const isolated = resolveIsolatedSessionParentPathFromCheckouts(paths) ??
|
|
36593
|
+
if (paths.some((p) => path24.resolve(p) === hintR)) {
|
|
36594
|
+
const isolated = resolveIsolatedSessionParentPathFromCheckouts(paths) ?? path24.resolve(paths[0]);
|
|
36553
36595
|
best = {
|
|
36554
|
-
sessionParentPath:
|
|
36555
|
-
workingTreeRelRoot:
|
|
36556
|
-
repoCheckoutPaths: paths.map((p) =>
|
|
36596
|
+
sessionParentPath: path24.resolve(isolated),
|
|
36597
|
+
workingTreeRelRoot: path24.resolve(cur),
|
|
36598
|
+
repoCheckoutPaths: paths.map((p) => path24.resolve(p))
|
|
36557
36599
|
};
|
|
36558
36600
|
}
|
|
36559
|
-
const next =
|
|
36601
|
+
const next = path24.dirname(cur);
|
|
36560
36602
|
if (next === cur) break;
|
|
36561
36603
|
cur = next;
|
|
36562
36604
|
}
|
|
@@ -36564,33 +36606,33 @@ function discoverLegacyBindingAscendingFromCheckout(sessionId, checkoutPath) {
|
|
|
36564
36606
|
}
|
|
36565
36607
|
function discoverSessionWorktreeOnDisk(options) {
|
|
36566
36608
|
const { sessionId, worktreesRootPath, layout, bridgeRoot } = options;
|
|
36567
|
-
if (!sessionId.trim() || !
|
|
36609
|
+
if (!sessionId.trim() || !fs23.existsSync(worktreesRootPath)) return null;
|
|
36568
36610
|
const preferredKey = getLauncherDirNameIfPresent(layout, bridgeRoot);
|
|
36569
36611
|
const keys = [];
|
|
36570
36612
|
if (preferredKey) keys.push(preferredKey);
|
|
36571
36613
|
try {
|
|
36572
|
-
for (const name of
|
|
36614
|
+
for (const name of fs23.readdirSync(worktreesRootPath)) {
|
|
36573
36615
|
if (name.startsWith(".")) continue;
|
|
36574
|
-
const p =
|
|
36575
|
-
if (!
|
|
36616
|
+
const p = path24.join(worktreesRootPath, name);
|
|
36617
|
+
if (!fs23.statSync(p).isDirectory()) continue;
|
|
36576
36618
|
if (name !== preferredKey) keys.push(name);
|
|
36577
36619
|
}
|
|
36578
36620
|
} catch {
|
|
36579
36621
|
return null;
|
|
36580
36622
|
}
|
|
36581
36623
|
for (const key of keys) {
|
|
36582
|
-
const layoutRoot =
|
|
36583
|
-
if (!
|
|
36584
|
-
const sessionDir =
|
|
36624
|
+
const layoutRoot = path24.join(worktreesRootPath, key);
|
|
36625
|
+
if (!fs23.existsSync(layoutRoot) || !fs23.statSync(layoutRoot).isDirectory()) continue;
|
|
36626
|
+
const sessionDir = path24.join(layoutRoot, sessionId);
|
|
36585
36627
|
const nested = tryBindingFromSessionDirectory(sessionDir);
|
|
36586
36628
|
if (nested) return nested;
|
|
36587
36629
|
const legacyPaths = collectWorktreeRootsNamed(layoutRoot, sessionId, 24);
|
|
36588
36630
|
if (legacyPaths.length > 0) {
|
|
36589
|
-
const isolated = resolveIsolatedSessionParentPathFromCheckouts(legacyPaths) ??
|
|
36631
|
+
const isolated = resolveIsolatedSessionParentPathFromCheckouts(legacyPaths) ?? path24.resolve(legacyPaths[0]);
|
|
36590
36632
|
return {
|
|
36591
|
-
sessionParentPath:
|
|
36592
|
-
workingTreeRelRoot:
|
|
36593
|
-
repoCheckoutPaths: legacyPaths.map((p) =>
|
|
36633
|
+
sessionParentPath: path24.resolve(isolated),
|
|
36634
|
+
workingTreeRelRoot: path24.resolve(layoutRoot),
|
|
36635
|
+
repoCheckoutPaths: legacyPaths.map((p) => path24.resolve(p))
|
|
36594
36636
|
};
|
|
36595
36637
|
}
|
|
36596
36638
|
}
|
|
@@ -36599,12 +36641,12 @@ function discoverSessionWorktreeOnDisk(options) {
|
|
|
36599
36641
|
function discoverSessionWorktreesUnderSessionWorktreeRoot(sessionWorktreeRootPathOrHint, sessionId) {
|
|
36600
36642
|
const sid = sessionId.trim();
|
|
36601
36643
|
if (!sid) return null;
|
|
36602
|
-
const hint =
|
|
36603
|
-
const underHint = tryBindingFromSessionDirectory(
|
|
36644
|
+
const hint = path24.resolve(sessionWorktreeRootPathOrHint);
|
|
36645
|
+
const underHint = tryBindingFromSessionDirectory(path24.join(hint, sid));
|
|
36604
36646
|
if (underHint) return underHint;
|
|
36605
36647
|
const direct = tryBindingFromSessionDirectory(hint);
|
|
36606
36648
|
if (direct) {
|
|
36607
|
-
if (
|
|
36649
|
+
if (path24.basename(hint) === sid && isGitDir(hint)) {
|
|
36608
36650
|
const legacyFromCheckout = discoverLegacyBindingAscendingFromCheckout(sid, hint);
|
|
36609
36651
|
if (legacyFromCheckout && legacyFromCheckout.repoCheckoutPaths.length > direct.repoCheckoutPaths.length) {
|
|
36610
36652
|
return legacyFromCheckout;
|
|
@@ -36612,216 +36654,349 @@ function discoverSessionWorktreesUnderSessionWorktreeRoot(sessionWorktreeRootPat
|
|
|
36612
36654
|
}
|
|
36613
36655
|
return direct;
|
|
36614
36656
|
}
|
|
36615
|
-
if (
|
|
36657
|
+
if (path24.basename(hint) === sid && isGitDir(hint)) {
|
|
36616
36658
|
const legacyFromCheckout = discoverLegacyBindingAscendingFromCheckout(sid, hint);
|
|
36617
36659
|
if (legacyFromCheckout) return legacyFromCheckout;
|
|
36618
36660
|
}
|
|
36619
36661
|
let st;
|
|
36620
36662
|
try {
|
|
36621
|
-
st =
|
|
36663
|
+
st = fs23.statSync(hint);
|
|
36622
36664
|
} catch {
|
|
36623
36665
|
return null;
|
|
36624
36666
|
}
|
|
36625
36667
|
if (!st.isDirectory()) return null;
|
|
36626
36668
|
const legacyPaths = collectWorktreeRootsNamed(hint, sid, 24);
|
|
36627
36669
|
if (legacyPaths.length === 0) return null;
|
|
36628
|
-
const isolated = resolveIsolatedSessionParentPathFromCheckouts(legacyPaths) ??
|
|
36670
|
+
const isolated = resolveIsolatedSessionParentPathFromCheckouts(legacyPaths) ?? path24.resolve(legacyPaths[0]);
|
|
36629
36671
|
return {
|
|
36630
|
-
sessionParentPath:
|
|
36672
|
+
sessionParentPath: path24.resolve(isolated),
|
|
36631
36673
|
workingTreeRelRoot: hint,
|
|
36632
|
-
repoCheckoutPaths: legacyPaths.map((p) =>
|
|
36674
|
+
repoCheckoutPaths: legacyPaths.map((p) => path24.resolve(p))
|
|
36633
36675
|
};
|
|
36634
36676
|
}
|
|
36635
36677
|
|
|
36636
|
-
// src/worktrees/session-
|
|
36678
|
+
// src/worktrees/manager/discover-session-binding.ts
|
|
36679
|
+
function discoverSessionBinding(params) {
|
|
36680
|
+
return discoverSessionWorktreeOnDisk({
|
|
36681
|
+
sessionId: params.sessionId,
|
|
36682
|
+
worktreesRootPath: params.worktreesRootPath,
|
|
36683
|
+
layout: params.layout,
|
|
36684
|
+
bridgeRoot: getBridgeRoot()
|
|
36685
|
+
});
|
|
36686
|
+
}
|
|
36687
|
+
|
|
36688
|
+
// src/worktrees/manager/resolve-isolated-session-parent-path.ts
|
|
36689
|
+
function resolveIsolatedSessionParentPath(sessionId, cache2, ensureRepoCheckoutPaths) {
|
|
36690
|
+
if (!sessionId) return null;
|
|
36691
|
+
const sid = sessionId.trim();
|
|
36692
|
+
const cached2 = cache2.getSessionParentPath(sid);
|
|
36693
|
+
if (cached2) return cached2;
|
|
36694
|
+
const paths = ensureRepoCheckoutPaths(sid);
|
|
36695
|
+
if (!paths?.length) return null;
|
|
36696
|
+
return resolveIsolatedSessionParentPathFromCheckouts(paths);
|
|
36697
|
+
}
|
|
36698
|
+
function ensureRepoCheckoutPathsForSession(sessionId, cache2, discover) {
|
|
36699
|
+
if (!sessionId?.trim()) return void 0;
|
|
36700
|
+
const sid = sessionId.trim();
|
|
36701
|
+
const cached2 = cache2.getRepoCheckoutPaths(sid);
|
|
36702
|
+
if (cached2?.length) return cached2;
|
|
36703
|
+
const disc = discover(sid);
|
|
36704
|
+
if (disc?.repoCheckoutPaths.length) {
|
|
36705
|
+
cache2.remember(sid, disc);
|
|
36706
|
+
return [...disc.repoCheckoutPaths];
|
|
36707
|
+
}
|
|
36708
|
+
return void 0;
|
|
36709
|
+
}
|
|
36710
|
+
|
|
36711
|
+
// src/worktrees/manager/resolve-commit-targets.ts
|
|
36712
|
+
function resolveCommitTargets(sessionId, cache2, discover) {
|
|
36713
|
+
const paths = cache2.getRepoCheckoutPathsRef(sessionId);
|
|
36714
|
+
if (paths?.length) return paths;
|
|
36715
|
+
const disc = discover(sessionId);
|
|
36716
|
+
if (disc?.repoCheckoutPaths.length) {
|
|
36717
|
+
cache2.remember(sessionId, disc);
|
|
36718
|
+
return disc.repoCheckoutPaths;
|
|
36719
|
+
}
|
|
36720
|
+
return [getBridgeRoot()];
|
|
36721
|
+
}
|
|
36722
|
+
|
|
36723
|
+
// src/worktrees/manager/parse-session-parent.ts
|
|
36637
36724
|
function parseSessionParent(v) {
|
|
36638
36725
|
if (v === "bridge_root" || v === "worktrees_root") return v;
|
|
36639
36726
|
if (v === "session_worktrees_root") return "worktrees_root";
|
|
36640
36727
|
return null;
|
|
36641
36728
|
}
|
|
36642
|
-
|
|
36643
|
-
|
|
36644
|
-
|
|
36729
|
+
|
|
36730
|
+
// src/worktrees/prepare-new-session-worktrees.ts
|
|
36731
|
+
import * as fs24 from "node:fs";
|
|
36732
|
+
import * as path25 from "node:path";
|
|
36733
|
+
|
|
36734
|
+
// src/git/worktrees/worktree-add.ts
|
|
36735
|
+
async function gitWorktreeAddBranch(mainRepoPath, worktreePath, branch, baseRef = "HEAD") {
|
|
36736
|
+
const mainGit = cliSimpleGit(mainRepoPath);
|
|
36737
|
+
const base = baseRef.trim() || "HEAD";
|
|
36738
|
+
await mainGit.raw(["worktree", "add", "-b", branch, worktreePath, base]);
|
|
36739
|
+
}
|
|
36740
|
+
|
|
36741
|
+
// src/worktrees/prepare-new-session-worktrees.ts
|
|
36742
|
+
function normalizeRepoRelPath(rel) {
|
|
36743
|
+
return rel === "" ? "." : rel.replace(/\\/g, "/");
|
|
36744
|
+
}
|
|
36745
|
+
function resolveBaseRefForRepo(relNorm, baseBranches) {
|
|
36746
|
+
if (!baseBranches) return "HEAD";
|
|
36747
|
+
const direct = baseBranches[relNorm]?.trim();
|
|
36748
|
+
if (direct) return direct;
|
|
36749
|
+
if (relNorm !== "." && baseBranches["."]?.trim()) return baseBranches["."].trim();
|
|
36750
|
+
return "HEAD";
|
|
36751
|
+
}
|
|
36752
|
+
async function prepareNewSessionWorktrees(options) {
|
|
36753
|
+
const { worktreesRootPath, bridgeRoot, sessionId, layout, log: log2, worktreeBaseBranches } = options;
|
|
36754
|
+
const bridgeResolved = path25.resolve(bridgeRoot);
|
|
36755
|
+
const cwdKey = allocateDirNameForLauncherCwd(layout, bridgeResolved);
|
|
36756
|
+
const bridgeKeyDir = path25.join(worktreesRootPath, cwdKey);
|
|
36757
|
+
const sessionDir = path25.join(bridgeKeyDir, sessionId);
|
|
36758
|
+
const repos = await discoverGitReposUnderRoot(bridgeResolved);
|
|
36759
|
+
if (repos.length === 0) {
|
|
36760
|
+
log2("[worktrees] No Git repositories under bridge root; skipping worktree creation.");
|
|
36761
|
+
return null;
|
|
36762
|
+
}
|
|
36763
|
+
const branch = `session-${sessionId}`;
|
|
36764
|
+
const worktreePaths = [];
|
|
36765
|
+
fs24.mkdirSync(sessionDir, { recursive: true });
|
|
36766
|
+
for (const repo of repos) {
|
|
36767
|
+
let rel = path25.relative(bridgeResolved, repo.absolutePath);
|
|
36768
|
+
if (rel.startsWith("..") || path25.isAbsolute(rel)) continue;
|
|
36769
|
+
const relNorm = normalizeRepoRelPath(rel === "" ? "." : rel);
|
|
36770
|
+
const wtPath = relNorm === "." ? sessionDir : path25.join(sessionDir, relNorm);
|
|
36771
|
+
if (relNorm !== ".") {
|
|
36772
|
+
fs24.mkdirSync(path25.dirname(wtPath), { recursive: true });
|
|
36773
|
+
}
|
|
36774
|
+
const baseRef = resolveBaseRefForRepo(relNorm, worktreeBaseBranches);
|
|
36775
|
+
try {
|
|
36776
|
+
await gitWorktreeAddBranch(repo.absolutePath, wtPath, branch, baseRef);
|
|
36777
|
+
log2(`[worktrees] Added worktree ${wtPath} (branch ${branch}, base ${baseRef}).`);
|
|
36778
|
+
worktreePaths.push(wtPath);
|
|
36779
|
+
} catch (e) {
|
|
36780
|
+
log2(
|
|
36781
|
+
`[worktrees] Worktree add failed for ${repo.absolutePath}: ${e instanceof Error ? e.message : String(e)}`
|
|
36782
|
+
);
|
|
36783
|
+
}
|
|
36784
|
+
}
|
|
36785
|
+
if (worktreePaths.length === 0) return null;
|
|
36786
|
+
return {
|
|
36787
|
+
worktreePaths,
|
|
36788
|
+
sessionParentPath: sessionDir,
|
|
36789
|
+
workingTreeRelRoot: sessionDir
|
|
36790
|
+
};
|
|
36791
|
+
}
|
|
36792
|
+
|
|
36793
|
+
// src/worktrees/manager/prepare-and-remember-session-worktrees.ts
|
|
36794
|
+
async function prepareAndRememberSessionWorktrees(params) {
|
|
36795
|
+
const prep = await prepareNewSessionWorktrees({
|
|
36796
|
+
worktreesRootPath: params.worktreesRootPath,
|
|
36797
|
+
bridgeRoot: getBridgeRoot(),
|
|
36798
|
+
sessionId: params.sessionId,
|
|
36799
|
+
layout: params.layout,
|
|
36800
|
+
log: params.log,
|
|
36801
|
+
...params.worktreeBaseBranches && Object.keys(params.worktreeBaseBranches).length > 0 ? { worktreeBaseBranches: params.worktreeBaseBranches } : {}
|
|
36802
|
+
});
|
|
36803
|
+
if (!prep) return void 0;
|
|
36804
|
+
params.cache.remember(params.sessionId, {
|
|
36805
|
+
sessionParentPath: prep.sessionParentPath,
|
|
36806
|
+
workingTreeRelRoot: prep.workingTreeRelRoot,
|
|
36807
|
+
repoCheckoutPaths: prep.worktreePaths
|
|
36808
|
+
});
|
|
36809
|
+
return params.cache.getSessionParentPath(params.sessionId);
|
|
36810
|
+
}
|
|
36811
|
+
|
|
36812
|
+
// src/worktrees/manager/resolve-existing-session-parent-path.ts
|
|
36813
|
+
function resolveExistingSessionParentPath(sessionId, cache2, discover) {
|
|
36814
|
+
const cached2 = cache2.getSessionParentPath(sessionId);
|
|
36815
|
+
if (cached2) return cached2;
|
|
36816
|
+
const disc = discover();
|
|
36817
|
+
if (disc) {
|
|
36818
|
+
cache2.remember(sessionId, disc);
|
|
36819
|
+
return cache2.getSessionParentPath(sessionId);
|
|
36820
|
+
}
|
|
36821
|
+
return void 0;
|
|
36822
|
+
}
|
|
36823
|
+
|
|
36824
|
+
// src/worktrees/manager/resolve-explicit-session-parent-path.ts
|
|
36825
|
+
import * as path26 from "node:path";
|
|
36826
|
+
function resolveExplicitSessionParentPath(params) {
|
|
36827
|
+
const resolved = path26.resolve(params.parentPathRaw);
|
|
36828
|
+
if (parseSessionParent(params.sessionParent) !== "worktrees_root") {
|
|
36829
|
+
return resolved;
|
|
36830
|
+
}
|
|
36831
|
+
const rememberAndReturn = (binding) => {
|
|
36832
|
+
params.cache.remember(params.sessionId, binding);
|
|
36833
|
+
return params.cache.getSessionParentPath(params.sessionId) ?? resolved;
|
|
36834
|
+
};
|
|
36835
|
+
const diskFirst = params.discover();
|
|
36836
|
+
if (diskFirst) return rememberAndReturn(diskFirst);
|
|
36837
|
+
const fromRoot = discoverSessionWorktreesUnderSessionWorktreeRoot(resolved, params.sessionId);
|
|
36838
|
+
if (fromRoot) return rememberAndReturn(fromRoot);
|
|
36839
|
+
let cur = resolved;
|
|
36840
|
+
for (let i = 0; i < 16; i++) {
|
|
36841
|
+
const tryRoot = discoverSessionWorktreesUnderSessionWorktreeRoot(cur, params.sessionId);
|
|
36842
|
+
if (tryRoot) return rememberAndReturn(tryRoot);
|
|
36843
|
+
const next = path26.dirname(cur);
|
|
36844
|
+
if (next === cur) break;
|
|
36845
|
+
cur = next;
|
|
36846
|
+
}
|
|
36847
|
+
return resolved;
|
|
36848
|
+
}
|
|
36849
|
+
|
|
36850
|
+
// src/worktrees/manager/resolve-session-parent-path-for-prompt.ts
|
|
36851
|
+
async function resolveSessionParentPathForPrompt(params) {
|
|
36852
|
+
const { sessionId, cache: cache2, worktreesRootPath, layout, log: log2, discover, opts } = params;
|
|
36853
|
+
if (!sessionId) return void 0;
|
|
36854
|
+
const sid = sessionId.trim();
|
|
36855
|
+
const parentPathRaw = opts.sessionParentPath?.trim();
|
|
36856
|
+
if (parentPathRaw) {
|
|
36857
|
+
return resolveExplicitSessionParentPath({
|
|
36858
|
+
sessionId: sid,
|
|
36859
|
+
sessionParent: opts.sessionParent,
|
|
36860
|
+
parentPathRaw,
|
|
36861
|
+
cache: cache2,
|
|
36862
|
+
discover: () => discover(sid)
|
|
36863
|
+
});
|
|
36864
|
+
}
|
|
36865
|
+
const parentKind = parseSessionParent(opts.sessionParent);
|
|
36866
|
+
if (parentKind === "bridge_root") {
|
|
36867
|
+
return void 0;
|
|
36868
|
+
}
|
|
36869
|
+
if (parentKind === "worktrees_root") {
|
|
36870
|
+
if (!opts.isNewSession) {
|
|
36871
|
+
return resolveExistingSessionParentPath(sid, cache2, () => discover(sid));
|
|
36872
|
+
}
|
|
36873
|
+
return prepareAndRememberSessionWorktrees({
|
|
36874
|
+
cache: cache2,
|
|
36875
|
+
sessionId: sid,
|
|
36876
|
+
worktreesRootPath,
|
|
36877
|
+
layout,
|
|
36878
|
+
log: log2,
|
|
36879
|
+
...opts.worktreeBaseBranches ? { worktreeBaseBranches: opts.worktreeBaseBranches } : {}
|
|
36880
|
+
});
|
|
36881
|
+
}
|
|
36882
|
+
if (!opts.isNewSession) {
|
|
36883
|
+
return resolveExistingSessionParentPath(sid, cache2, () => discover(sid));
|
|
36884
|
+
}
|
|
36885
|
+
return prepareAndRememberSessionWorktrees({
|
|
36886
|
+
cache: cache2,
|
|
36887
|
+
sessionId: sid,
|
|
36888
|
+
worktreesRootPath,
|
|
36889
|
+
layout,
|
|
36890
|
+
log: log2
|
|
36891
|
+
});
|
|
36892
|
+
}
|
|
36893
|
+
|
|
36894
|
+
// src/worktrees/manager/session-worktree-cache.ts
|
|
36895
|
+
import * as path27 from "node:path";
|
|
36896
|
+
var SessionWorktreeCache = class {
|
|
36645
36897
|
sessionRepoCheckoutPaths = /* @__PURE__ */ new Map();
|
|
36646
36898
|
sessionParentPathBySession = /* @__PURE__ */ new Map();
|
|
36647
36899
|
sessionWorkingTreeRelRootBySession = /* @__PURE__ */ new Map();
|
|
36648
|
-
|
|
36649
|
-
|
|
36650
|
-
this.worktreesRootPath = options.worktreesRootPath;
|
|
36651
|
-
this.log = options.log;
|
|
36652
|
-
this.layout = loadWorktreeLayout();
|
|
36653
|
-
}
|
|
36654
|
-
rememberSessionWorktrees(sessionId, binding) {
|
|
36655
|
-
const paths = binding.repoCheckoutPaths.map((p) => path26.resolve(p));
|
|
36900
|
+
remember(sessionId, binding) {
|
|
36901
|
+
const paths = binding.repoCheckoutPaths.map((p) => path27.resolve(p));
|
|
36656
36902
|
this.sessionRepoCheckoutPaths.set(sessionId, paths);
|
|
36657
|
-
this.sessionParentPathBySession.set(sessionId,
|
|
36658
|
-
this.sessionWorkingTreeRelRootBySession.set(sessionId,
|
|
36903
|
+
this.sessionParentPathBySession.set(sessionId, path27.resolve(binding.sessionParentPath));
|
|
36904
|
+
this.sessionWorkingTreeRelRootBySession.set(sessionId, path27.resolve(binding.workingTreeRelRoot));
|
|
36905
|
+
}
|
|
36906
|
+
clearSession(sessionId) {
|
|
36907
|
+
const paths = this.sessionRepoCheckoutPaths.get(sessionId);
|
|
36908
|
+
this.sessionRepoCheckoutPaths.delete(sessionId);
|
|
36909
|
+
this.sessionParentPathBySession.delete(sessionId);
|
|
36910
|
+
this.sessionWorkingTreeRelRootBySession.delete(sessionId);
|
|
36911
|
+
return paths;
|
|
36659
36912
|
}
|
|
36660
|
-
|
|
36913
|
+
getSessionParentPath(sessionId) {
|
|
36661
36914
|
return this.sessionParentPathBySession.get(sessionId);
|
|
36662
36915
|
}
|
|
36663
|
-
|
|
36664
|
-
return
|
|
36665
|
-
|
|
36666
|
-
|
|
36667
|
-
|
|
36668
|
-
|
|
36669
|
-
|
|
36916
|
+
getWorkingTreeRelRoot(sessionId) {
|
|
36917
|
+
return this.sessionWorkingTreeRelRootBySession.get(sessionId) ?? null;
|
|
36918
|
+
}
|
|
36919
|
+
hasSession(sessionId) {
|
|
36920
|
+
return this.sessionParentPathBySession.has(sessionId);
|
|
36921
|
+
}
|
|
36922
|
+
getRepoCheckoutPaths(sessionId) {
|
|
36923
|
+
const paths = this.sessionRepoCheckoutPaths.get(sessionId);
|
|
36924
|
+
return paths?.length ? [...paths] : void 0;
|
|
36925
|
+
}
|
|
36926
|
+
getRepoCheckoutPathsRef(sessionId) {
|
|
36927
|
+
const paths = this.sessionRepoCheckoutPaths.get(sessionId);
|
|
36928
|
+
return paths?.length ? paths : void 0;
|
|
36670
36929
|
}
|
|
36671
36930
|
isLegacyNestedLayout(sessionId) {
|
|
36672
36931
|
const parent = this.sessionParentPathBySession.get(sessionId);
|
|
36673
36932
|
const relRoot = this.sessionWorkingTreeRelRootBySession.get(sessionId);
|
|
36674
36933
|
if (!parent || !relRoot) return false;
|
|
36675
|
-
return
|
|
36934
|
+
return path27.resolve(parent) !== path27.resolve(relRoot);
|
|
36935
|
+
}
|
|
36936
|
+
};
|
|
36937
|
+
|
|
36938
|
+
// src/worktrees/manager/manager.ts
|
|
36939
|
+
var SessionWorktreeManager = class {
|
|
36940
|
+
worktreesRootPath;
|
|
36941
|
+
log;
|
|
36942
|
+
cache = new SessionWorktreeCache();
|
|
36943
|
+
layout;
|
|
36944
|
+
constructor(options) {
|
|
36945
|
+
this.worktreesRootPath = options.worktreesRootPath;
|
|
36946
|
+
this.log = options.log;
|
|
36947
|
+
this.layout = loadWorktreeLayout();
|
|
36948
|
+
}
|
|
36949
|
+
discover(sessionId) {
|
|
36950
|
+
return discoverSessionBinding({
|
|
36951
|
+
sessionId,
|
|
36952
|
+
worktreesRootPath: this.worktreesRootPath,
|
|
36953
|
+
layout: this.layout
|
|
36954
|
+
});
|
|
36676
36955
|
}
|
|
36677
|
-
/**
|
|
36678
|
-
* Session parent path for `worktrees_root`: the per-session directory (new layout) or primary checkout (legacy).
|
|
36679
|
-
*/
|
|
36680
36956
|
getIsolatedSessionParentPathForSession(sessionId) {
|
|
36681
|
-
|
|
36682
|
-
|
|
36683
|
-
|
|
36684
|
-
|
|
36685
|
-
|
|
36686
|
-
|
|
36687
|
-
|
|
36688
|
-
|
|
36689
|
-
|
|
36690
|
-
|
|
36691
|
-
* or `undefined` meaning use {@link getBridgeRoot}.
|
|
36692
|
-
*/
|
|
36693
|
-
async resolveSessionParentPathForPrompt(sessionId, opts) {
|
|
36694
|
-
if (!sessionId) return void 0;
|
|
36695
|
-
const sid = sessionId.trim();
|
|
36696
|
-
const parentPathRaw = opts.sessionParentPath?.trim();
|
|
36697
|
-
if (parentPathRaw) {
|
|
36698
|
-
const resolved = path26.resolve(parentPathRaw);
|
|
36699
|
-
if (sid && parseSessionParent(opts.sessionParent) === "worktrees_root") {
|
|
36700
|
-
const diskFirst = this.tryDiscoverFromDisk(sid);
|
|
36701
|
-
if (diskFirst) {
|
|
36702
|
-
this.rememberSessionWorktrees(sid, diskFirst);
|
|
36703
|
-
return this.sessionParentPathAfterRemember(sid);
|
|
36704
|
-
}
|
|
36705
|
-
const fromRoot = discoverSessionWorktreesUnderSessionWorktreeRoot(resolved, sid);
|
|
36706
|
-
if (fromRoot) {
|
|
36707
|
-
this.rememberSessionWorktrees(sid, fromRoot);
|
|
36708
|
-
return this.sessionParentPathAfterRemember(sid);
|
|
36709
|
-
}
|
|
36710
|
-
let cur = resolved;
|
|
36711
|
-
for (let i = 0; i < 16; i++) {
|
|
36712
|
-
const tryRoot = discoverSessionWorktreesUnderSessionWorktreeRoot(cur, sid);
|
|
36713
|
-
if (tryRoot) {
|
|
36714
|
-
this.rememberSessionWorktrees(sid, tryRoot);
|
|
36715
|
-
return this.sessionParentPathAfterRemember(sid);
|
|
36716
|
-
}
|
|
36717
|
-
const next = path26.dirname(cur);
|
|
36718
|
-
if (next === cur) break;
|
|
36719
|
-
cur = next;
|
|
36720
|
-
}
|
|
36721
|
-
}
|
|
36722
|
-
return resolved;
|
|
36723
|
-
}
|
|
36724
|
-
const parentKind = parseSessionParent(opts.sessionParent);
|
|
36725
|
-
if (parentKind === "bridge_root") {
|
|
36726
|
-
return void 0;
|
|
36727
|
-
}
|
|
36728
|
-
if (parentKind === "worktrees_root") {
|
|
36729
|
-
if (!opts.isNewSession) {
|
|
36730
|
-
const cached2 = this.sessionParentPathAfterRemember(sid);
|
|
36731
|
-
if (cached2) return cached2;
|
|
36732
|
-
const disc = this.tryDiscoverFromDisk(sid);
|
|
36733
|
-
if (disc) {
|
|
36734
|
-
this.rememberSessionWorktrees(sid, disc);
|
|
36735
|
-
return this.sessionParentPathAfterRemember(sid);
|
|
36736
|
-
}
|
|
36737
|
-
return void 0;
|
|
36738
|
-
}
|
|
36739
|
-
const prep2 = await prepareNewSessionWorktrees({
|
|
36740
|
-
worktreesRootPath: this.worktreesRootPath,
|
|
36741
|
-
bridgeRoot: getBridgeRoot(),
|
|
36742
|
-
sessionId: sid,
|
|
36743
|
-
layout: this.layout,
|
|
36744
|
-
log: this.log
|
|
36745
|
-
});
|
|
36746
|
-
if (!prep2) return void 0;
|
|
36747
|
-
this.rememberSessionWorktrees(sid, {
|
|
36748
|
-
sessionParentPath: prep2.sessionParentPath,
|
|
36749
|
-
workingTreeRelRoot: prep2.workingTreeRelRoot,
|
|
36750
|
-
repoCheckoutPaths: prep2.worktreePaths
|
|
36751
|
-
});
|
|
36752
|
-
return this.sessionParentPathAfterRemember(sid);
|
|
36753
|
-
}
|
|
36754
|
-
if (!opts.isNewSession) {
|
|
36755
|
-
const cached2 = this.sessionParentPathAfterRemember(sid);
|
|
36756
|
-
if (cached2) return cached2;
|
|
36757
|
-
const disc = this.tryDiscoverFromDisk(sid);
|
|
36758
|
-
if (disc) {
|
|
36759
|
-
this.rememberSessionWorktrees(sid, disc);
|
|
36760
|
-
return this.sessionParentPathAfterRemember(sid);
|
|
36761
|
-
}
|
|
36762
|
-
return void 0;
|
|
36763
|
-
}
|
|
36764
|
-
const prep = await prepareNewSessionWorktrees({
|
|
36957
|
+
return resolveIsolatedSessionParentPath(
|
|
36958
|
+
sessionId,
|
|
36959
|
+
this.cache,
|
|
36960
|
+
(sid) => this.ensureRepoCheckoutPathsForSession(sid)
|
|
36961
|
+
);
|
|
36962
|
+
}
|
|
36963
|
+
resolveSessionParentPathForPrompt(sessionId, opts) {
|
|
36964
|
+
return resolveSessionParentPathForPrompt({
|
|
36965
|
+
sessionId,
|
|
36966
|
+
cache: this.cache,
|
|
36765
36967
|
worktreesRootPath: this.worktreesRootPath,
|
|
36766
|
-
bridgeRoot: getBridgeRoot(),
|
|
36767
|
-
sessionId: sid,
|
|
36768
36968
|
layout: this.layout,
|
|
36769
|
-
log: this.log
|
|
36770
|
-
|
|
36771
|
-
|
|
36772
|
-
this.rememberSessionWorktrees(sid, {
|
|
36773
|
-
sessionParentPath: prep.sessionParentPath,
|
|
36774
|
-
workingTreeRelRoot: prep.workingTreeRelRoot,
|
|
36775
|
-
repoCheckoutPaths: prep.worktreePaths
|
|
36969
|
+
log: this.log,
|
|
36970
|
+
discover: (sid) => this.discover(sid),
|
|
36971
|
+
opts
|
|
36776
36972
|
});
|
|
36777
|
-
return this.sessionParentPathAfterRemember(sid);
|
|
36778
36973
|
}
|
|
36779
36974
|
async renameSessionBranch(sessionId, newBranch) {
|
|
36780
|
-
const paths = this.
|
|
36975
|
+
const paths = this.cache.getRepoCheckoutPathsRef(sessionId);
|
|
36781
36976
|
if (!paths?.length) return;
|
|
36782
36977
|
await renameSessionWorktreeBranches(paths, newBranch, this.log);
|
|
36783
36978
|
}
|
|
36784
|
-
/** True when this session uses an isolated worktree layout (not the bridge root). */
|
|
36785
36979
|
usesWorktreeSession(sessionId) {
|
|
36786
36980
|
if (!sessionId) return false;
|
|
36787
|
-
return this.
|
|
36981
|
+
return this.cache.hasSession(sessionId);
|
|
36788
36982
|
}
|
|
36789
|
-
/** Per-repo git checkout directories for this session (for snapshots, commits, change lists). */
|
|
36790
36983
|
getRepoCheckoutPathsForSession(sessionId) {
|
|
36791
36984
|
if (!sessionId) return void 0;
|
|
36792
|
-
|
|
36793
|
-
return paths?.length ? [...paths] : void 0;
|
|
36985
|
+
return this.cache.getRepoCheckoutPaths(sessionId);
|
|
36794
36986
|
}
|
|
36795
|
-
/**
|
|
36796
|
-
* Same paths as {@link getRepoCheckoutPathsForSession}, but loads from disk into memory when the CLI
|
|
36797
|
-
* restarted or maps were not yet populated (avoids discovering every repo under the worktrees root).
|
|
36798
|
-
*/
|
|
36799
36987
|
ensureRepoCheckoutPathsForSession(sessionId) {
|
|
36800
|
-
|
|
36801
|
-
const sid = sessionId.trim();
|
|
36802
|
-
const cached2 = this.sessionRepoCheckoutPaths.get(sid);
|
|
36803
|
-
if (cached2?.length) return [...cached2];
|
|
36804
|
-
const disc = this.tryDiscoverFromDisk(sid);
|
|
36805
|
-
if (disc?.repoCheckoutPaths.length) {
|
|
36806
|
-
this.rememberSessionWorktrees(sid, disc);
|
|
36807
|
-
return [...disc.repoCheckoutPaths];
|
|
36808
|
-
}
|
|
36809
|
-
return void 0;
|
|
36988
|
+
return ensureRepoCheckoutPathsForSession(sessionId, this.cache, (sid) => this.discover(sid));
|
|
36810
36989
|
}
|
|
36811
|
-
/** Session parent directory when in worktrees mode; null otherwise (same as {@link getIsolatedSessionParentPathForSession} path). */
|
|
36812
36990
|
getSessionWorktreeRootForSession(sessionId) {
|
|
36813
36991
|
return this.getIsolatedSessionParentPathForSession(sessionId);
|
|
36814
36992
|
}
|
|
36815
36993
|
async removeSessionWorktrees(sessionId) {
|
|
36816
|
-
const paths = this.
|
|
36817
|
-
this.sessionRepoCheckoutPaths.delete(sessionId);
|
|
36818
|
-
this.sessionParentPathBySession.delete(sessionId);
|
|
36819
|
-
this.sessionWorkingTreeRelRootBySession.delete(sessionId);
|
|
36994
|
+
const paths = this.cache.clearSession(sessionId);
|
|
36820
36995
|
if (!paths?.length) return;
|
|
36821
36996
|
await removeSessionWorktrees(paths, this.log);
|
|
36822
36997
|
}
|
|
36823
36998
|
async commitSession(params) {
|
|
36824
|
-
const paths = this.
|
|
36999
|
+
const paths = this.cache.getRepoCheckoutPathsRef(params.sessionId);
|
|
36825
37000
|
const targets = paths?.length ? paths : [getBridgeRoot()];
|
|
36826
37001
|
return commitSessionWorktrees({
|
|
36827
37002
|
paths: targets,
|
|
@@ -36830,28 +37005,17 @@ var SessionWorktreeManager = class {
|
|
|
36830
37005
|
push: params.push
|
|
36831
37006
|
});
|
|
36832
37007
|
}
|
|
36833
|
-
resolveCommitTargets(sessionId) {
|
|
36834
|
-
const paths = this.sessionRepoCheckoutPaths.get(sessionId);
|
|
36835
|
-
if (paths?.length) return paths;
|
|
36836
|
-
const disc = this.tryDiscoverFromDisk(sessionId);
|
|
36837
|
-
if (disc?.repoCheckoutPaths.length) {
|
|
36838
|
-
this.rememberSessionWorktrees(sessionId, disc);
|
|
36839
|
-
return disc.repoCheckoutPaths;
|
|
36840
|
-
}
|
|
36841
|
-
return [getBridgeRoot()];
|
|
36842
|
-
}
|
|
36843
37008
|
async getSessionWorkingTreeStatus(sessionId) {
|
|
36844
|
-
return aggregateSessionPathsWorkingTreeStatus(
|
|
37009
|
+
return aggregateSessionPathsWorkingTreeStatus(
|
|
37010
|
+
resolveCommitTargets(sessionId, this.cache, (sid) => this.discover(sid))
|
|
37011
|
+
);
|
|
36845
37012
|
}
|
|
36846
|
-
/** Per-repo changed files vs HEAD (or a single commit vs parent) for the same git roots used for commit/push. */
|
|
36847
37013
|
async getSessionWorkingTreeChangeDetails(sessionId, opts) {
|
|
36848
|
-
const targets = this.
|
|
36849
|
-
const sessionWorkingTreeRelRoot = this.sessionWorkingTreeRelRootBySession.get(sessionId) ?? null;
|
|
36850
|
-
const legacyNested = this.isLegacyNestedLayout(sessionId);
|
|
37014
|
+
const targets = resolveCommitTargets(sessionId, this.cache, (sid) => this.discover(sid));
|
|
36851
37015
|
return getWorkingTreeChangeRepoDetails({
|
|
36852
37016
|
commitTargetPaths: targets,
|
|
36853
|
-
sessionWorktreeRootPath:
|
|
36854
|
-
legacyRepoNestedSessionLayout:
|
|
37017
|
+
sessionWorktreeRootPath: this.cache.getWorkingTreeRelRoot(sessionId),
|
|
37018
|
+
legacyRepoNestedSessionLayout: this.cache.isLegacyNestedLayout(sessionId),
|
|
36855
37019
|
repoFilterRelPath: opts?.repoRelPath?.trim() ? opts.repoRelPath.trim() : null,
|
|
36856
37020
|
basis: opts?.basis,
|
|
36857
37021
|
recentCommitsLimit: opts?.recentCommitsLimit
|
|
@@ -36859,7 +37023,9 @@ var SessionWorktreeManager = class {
|
|
|
36859
37023
|
}
|
|
36860
37024
|
async pushSessionUpstream(sessionId) {
|
|
36861
37025
|
try {
|
|
36862
|
-
await pushAheadOfUpstreamForPaths(
|
|
37026
|
+
await pushAheadOfUpstreamForPaths(
|
|
37027
|
+
resolveCommitTargets(sessionId, this.cache, (sid) => this.discover(sid))
|
|
37028
|
+
);
|
|
36863
37029
|
return { ok: true };
|
|
36864
37030
|
} catch (e) {
|
|
36865
37031
|
const err = e instanceof Error ? e.message : String(e);
|
|
@@ -36867,27 +37033,31 @@ var SessionWorktreeManager = class {
|
|
|
36867
37033
|
}
|
|
36868
37034
|
}
|
|
36869
37035
|
};
|
|
37036
|
+
|
|
37037
|
+
// src/worktrees/manager/default-worktrees-root-path.ts
|
|
37038
|
+
import * as path28 from "node:path";
|
|
37039
|
+
import os8 from "node:os";
|
|
36870
37040
|
function defaultWorktreesRootPath() {
|
|
36871
|
-
return
|
|
37041
|
+
return path28.join(os8.homedir(), ".buildautomaton", "worktrees");
|
|
36872
37042
|
}
|
|
36873
37043
|
|
|
36874
37044
|
// src/files/watch-file-index.ts
|
|
36875
37045
|
import { watch } from "node:fs";
|
|
36876
|
-
import
|
|
37046
|
+
import path33 from "node:path";
|
|
36877
37047
|
|
|
36878
37048
|
// src/files/index/paths.ts
|
|
36879
|
-
import
|
|
37049
|
+
import path29 from "node:path";
|
|
36880
37050
|
import crypto2 from "node:crypto";
|
|
36881
37051
|
function getCwdHashForFileIndex(resolvedCwd) {
|
|
36882
|
-
return crypto2.createHash("sha256").update(
|
|
37052
|
+
return crypto2.createHash("sha256").update(path29.resolve(resolvedCwd)).digest("hex").slice(0, INDEX_HASH_LEN);
|
|
36883
37053
|
}
|
|
36884
37054
|
|
|
36885
37055
|
// src/files/index/build-file-index.ts
|
|
36886
|
-
import
|
|
37056
|
+
import path31 from "node:path";
|
|
36887
37057
|
|
|
36888
37058
|
// src/files/index/walk-workspace-tree.ts
|
|
36889
37059
|
import fs25 from "node:fs";
|
|
36890
|
-
import
|
|
37060
|
+
import path30 from "node:path";
|
|
36891
37061
|
var DEPENDENCY_INSTALL_DIR_NAMES = /* @__PURE__ */ new Set([
|
|
36892
37062
|
"node_modules",
|
|
36893
37063
|
"bower_components",
|
|
@@ -36916,18 +37086,18 @@ async function walkWorkspaceTreeAsync(dir, baseDir, onFile, state) {
|
|
|
36916
37086
|
if (isCliImmediateShutdownRequested()) throw new CliSqliteInterrupted();
|
|
36917
37087
|
}
|
|
36918
37088
|
state.n++;
|
|
36919
|
-
const full =
|
|
37089
|
+
const full = path30.join(dir, name);
|
|
36920
37090
|
let stat3;
|
|
36921
37091
|
try {
|
|
36922
37092
|
stat3 = await fs25.promises.stat(full);
|
|
36923
37093
|
} catch {
|
|
36924
37094
|
continue;
|
|
36925
37095
|
}
|
|
36926
|
-
const
|
|
37096
|
+
const relative6 = path30.relative(baseDir, full).replace(/\\/g, "/");
|
|
36927
37097
|
if (stat3.isDirectory()) {
|
|
36928
37098
|
await walkWorkspaceTreeAsync(full, baseDir, onFile, state);
|
|
36929
37099
|
} else if (stat3.isFile()) {
|
|
36930
|
-
onFile(
|
|
37100
|
+
onFile(relative6);
|
|
36931
37101
|
}
|
|
36932
37102
|
}
|
|
36933
37103
|
}
|
|
@@ -37025,7 +37195,7 @@ async function collectWorkspacePathsAsync(resolved) {
|
|
|
37025
37195
|
}
|
|
37026
37196
|
async function buildFileIndexAsync(cwd) {
|
|
37027
37197
|
return withFileIndexSqliteLock(async () => {
|
|
37028
|
-
const resolved =
|
|
37198
|
+
const resolved = path31.resolve(cwd);
|
|
37029
37199
|
await yieldToEventLoop();
|
|
37030
37200
|
assertNotShutdown();
|
|
37031
37201
|
const paths = await collectWorkspacePathsAsync(resolved);
|
|
@@ -37037,7 +37207,7 @@ async function buildFileIndexAsync(cwd) {
|
|
|
37037
37207
|
}
|
|
37038
37208
|
|
|
37039
37209
|
// src/files/index/ensure-file-index.ts
|
|
37040
|
-
import
|
|
37210
|
+
import path32 from "node:path";
|
|
37041
37211
|
|
|
37042
37212
|
// src/files/index/search-file-index.ts
|
|
37043
37213
|
function escapeLikePattern(fragment) {
|
|
@@ -37089,7 +37259,7 @@ async function searchBridgeFilePathsAsync(resolvedCwd, query, limit = 100) {
|
|
|
37089
37259
|
|
|
37090
37260
|
// src/files/index/ensure-file-index.ts
|
|
37091
37261
|
async function ensureFileIndexAsync(cwd) {
|
|
37092
|
-
const resolved =
|
|
37262
|
+
const resolved = path32.resolve(cwd);
|
|
37093
37263
|
if (await bridgeFileIndexIsPopulated(resolved)) {
|
|
37094
37264
|
return { fromCache: true, pathCount: await bridgeFileIndexPathCount(resolved) };
|
|
37095
37265
|
}
|
|
@@ -37133,7 +37303,7 @@ function createFsWatcher(resolved, schedule) {
|
|
|
37133
37303
|
}
|
|
37134
37304
|
}
|
|
37135
37305
|
function startFileIndexWatcher(cwd = getBridgeRoot()) {
|
|
37136
|
-
const resolved =
|
|
37306
|
+
const resolved = path33.resolve(cwd);
|
|
37137
37307
|
void buildFileIndexAsync(resolved).catch((e) => {
|
|
37138
37308
|
if (e instanceof CliSqliteInterrupted) return;
|
|
37139
37309
|
console.error("[file-index] Initial index build failed:", e);
|
|
@@ -37163,7 +37333,7 @@ function startFileIndexWatcher(cwd = getBridgeRoot()) {
|
|
|
37163
37333
|
}
|
|
37164
37334
|
|
|
37165
37335
|
// src/connection/create-bridge-connection.ts
|
|
37166
|
-
import * as
|
|
37336
|
+
import * as path44 from "node:path";
|
|
37167
37337
|
|
|
37168
37338
|
// src/dev-servers/manager/dev-server-manager.ts
|
|
37169
37339
|
import { rm as rm2 } from "node:fs/promises";
|
|
@@ -37185,15 +37355,15 @@ function sendDevServerStatus(getWs, serverId, status, options) {
|
|
|
37185
37355
|
|
|
37186
37356
|
// src/dev-servers/process/terminate-child-process.ts
|
|
37187
37357
|
async function sigtermAndWaitForExit(proc, graceMs, log2, shortId) {
|
|
37188
|
-
const exited = new Promise((
|
|
37189
|
-
proc.once("exit", () =>
|
|
37358
|
+
const exited = new Promise((resolve22) => {
|
|
37359
|
+
proc.once("exit", () => resolve22());
|
|
37190
37360
|
});
|
|
37191
37361
|
log2(`[dev-server] Sending SIGTERM to ${shortId} (pid=${proc.pid ?? "?"}).`);
|
|
37192
37362
|
try {
|
|
37193
37363
|
proc.kill("SIGTERM");
|
|
37194
37364
|
} catch {
|
|
37195
37365
|
}
|
|
37196
|
-
await Promise.race([exited, new Promise((
|
|
37366
|
+
await Promise.race([exited, new Promise((resolve22) => setTimeout(resolve22, graceMs))]);
|
|
37197
37367
|
}
|
|
37198
37368
|
function forceKillChild(proc, log2, shortId, graceMs) {
|
|
37199
37369
|
log2(
|
|
@@ -37473,10 +37643,10 @@ function trySpawnShellTruePiped(command, env, cwd, devNullFd, signal) {
|
|
|
37473
37643
|
import { spawn as spawn7 } from "node:child_process";
|
|
37474
37644
|
import fs29 from "node:fs";
|
|
37475
37645
|
import { tmpdir } from "node:os";
|
|
37476
|
-
import
|
|
37646
|
+
import path34 from "node:path";
|
|
37477
37647
|
function trySpawnMergedLogFile(command, env, cwd, signal) {
|
|
37478
|
-
const tmpRoot = fs29.mkdtempSync(
|
|
37479
|
-
const logPath =
|
|
37648
|
+
const tmpRoot = fs29.mkdtempSync(path34.join(tmpdir(), "ba-devsrv-log-"));
|
|
37649
|
+
const logPath = path34.join(tmpRoot, "combined.log");
|
|
37480
37650
|
let logFd;
|
|
37481
37651
|
try {
|
|
37482
37652
|
logFd = fs29.openSync(logPath, "a");
|
|
@@ -37520,15 +37690,15 @@ function trySpawnMergedLogFile(command, env, cwd, signal) {
|
|
|
37520
37690
|
import { spawn as spawn8 } from "node:child_process";
|
|
37521
37691
|
import fs30 from "node:fs";
|
|
37522
37692
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
37523
|
-
import
|
|
37693
|
+
import path35 from "node:path";
|
|
37524
37694
|
function shSingleQuote(s) {
|
|
37525
37695
|
return `'${s.replace(/'/g, `'\\''`)}'`;
|
|
37526
37696
|
}
|
|
37527
37697
|
function trySpawnShellScriptLogRedirectUnix(command, env, cwd, signal) {
|
|
37528
|
-
const tmpRoot = fs30.mkdtempSync(
|
|
37529
|
-
const logPath =
|
|
37530
|
-
const innerPath =
|
|
37531
|
-
const runnerPath =
|
|
37698
|
+
const tmpRoot = fs30.mkdtempSync(path35.join(tmpdir2(), "ba-devsrv-sh-"));
|
|
37699
|
+
const logPath = path35.join(tmpRoot, "combined.log");
|
|
37700
|
+
const innerPath = path35.join(tmpRoot, "_cmd.sh");
|
|
37701
|
+
const runnerPath = path35.join(tmpRoot, "_run.sh");
|
|
37532
37702
|
try {
|
|
37533
37703
|
fs30.writeFileSync(innerPath, `#!/bin/sh
|
|
37534
37704
|
${command}
|
|
@@ -37559,9 +37729,9 @@ cd ${shSingleQuote(cwd)}
|
|
|
37559
37729
|
}
|
|
37560
37730
|
}
|
|
37561
37731
|
function trySpawnShellScriptLogRedirectWin(command, env, cwd, signal) {
|
|
37562
|
-
const tmpRoot = fs30.mkdtempSync(
|
|
37563
|
-
const logPath =
|
|
37564
|
-
const runnerPath =
|
|
37732
|
+
const tmpRoot = fs30.mkdtempSync(path35.join(tmpdir2(), "ba-devsrv-sh-"));
|
|
37733
|
+
const logPath = path35.join(tmpRoot, "combined.log");
|
|
37734
|
+
const runnerPath = path35.join(tmpRoot, "_run.bat");
|
|
37565
37735
|
const q = (p) => `"${p.replace(/"/g, '""')}"`;
|
|
37566
37736
|
const com = process.env.ComSpec || "cmd.exe";
|
|
37567
37737
|
try {
|
|
@@ -38074,7 +38244,7 @@ async function proxyToLocal(request) {
|
|
|
38074
38244
|
};
|
|
38075
38245
|
const maxAttempts = isIdempotentProxyMethod(request.method) ? LOCAL_PREVIEW_FETCH_RETRY_DELAYS_MS.length + 1 : 1;
|
|
38076
38246
|
for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
|
|
38077
|
-
const once = await new Promise((
|
|
38247
|
+
const once = await new Promise((resolve22) => {
|
|
38078
38248
|
const req = mod.request(opts, (res) => {
|
|
38079
38249
|
const chunks = [];
|
|
38080
38250
|
res.on("data", (c) => chunks.push(c));
|
|
@@ -38085,7 +38255,7 @@ async function proxyToLocal(request) {
|
|
|
38085
38255
|
if (typeof v === "string") headers[k] = v;
|
|
38086
38256
|
else if (Array.isArray(v) && v[0]) headers[k] = v[0];
|
|
38087
38257
|
}
|
|
38088
|
-
|
|
38258
|
+
resolve22({
|
|
38089
38259
|
id: request.id,
|
|
38090
38260
|
statusCode: res.statusCode ?? 0,
|
|
38091
38261
|
headers,
|
|
@@ -38094,7 +38264,7 @@ async function proxyToLocal(request) {
|
|
|
38094
38264
|
});
|
|
38095
38265
|
});
|
|
38096
38266
|
req.on("error", (err) => {
|
|
38097
|
-
|
|
38267
|
+
resolve22({
|
|
38098
38268
|
id: request.id,
|
|
38099
38269
|
statusCode: 0,
|
|
38100
38270
|
headers: {},
|
|
@@ -38503,13 +38673,13 @@ function createOnBridgeIdentified(opts) {
|
|
|
38503
38673
|
|
|
38504
38674
|
// src/skills/discover-local-agent-skills.ts
|
|
38505
38675
|
import fs31 from "node:fs";
|
|
38506
|
-
import
|
|
38676
|
+
import path36 from "node:path";
|
|
38507
38677
|
var SKILL_DISCOVERY_ROOTS = [".agents/skills", ".claude/skills", ".cursor/skills", "skills"];
|
|
38508
38678
|
function discoverLocalSkills(cwd) {
|
|
38509
38679
|
const out = [];
|
|
38510
38680
|
const seenKeys = /* @__PURE__ */ new Set();
|
|
38511
38681
|
for (const rel of SKILL_DISCOVERY_ROOTS) {
|
|
38512
|
-
const base =
|
|
38682
|
+
const base = path36.join(cwd, rel);
|
|
38513
38683
|
if (!fs31.existsSync(base) || !fs31.statSync(base).isDirectory()) continue;
|
|
38514
38684
|
let entries = [];
|
|
38515
38685
|
try {
|
|
@@ -38518,13 +38688,13 @@ function discoverLocalSkills(cwd) {
|
|
|
38518
38688
|
continue;
|
|
38519
38689
|
}
|
|
38520
38690
|
for (const name of entries) {
|
|
38521
|
-
const dir =
|
|
38691
|
+
const dir = path36.join(base, name);
|
|
38522
38692
|
try {
|
|
38523
38693
|
if (!fs31.statSync(dir).isDirectory()) continue;
|
|
38524
38694
|
} catch {
|
|
38525
38695
|
continue;
|
|
38526
38696
|
}
|
|
38527
|
-
const skillMd =
|
|
38697
|
+
const skillMd = path36.join(dir, "SKILL.md");
|
|
38528
38698
|
if (!fs31.existsSync(skillMd)) continue;
|
|
38529
38699
|
const key = `${rel}/${name}`;
|
|
38530
38700
|
if (seenKeys.has(key)) continue;
|
|
@@ -38537,7 +38707,7 @@ function discoverLocalSkills(cwd) {
|
|
|
38537
38707
|
function discoverSkillLayoutRoots(cwd) {
|
|
38538
38708
|
const roots = [];
|
|
38539
38709
|
for (const rel of SKILL_DISCOVERY_ROOTS) {
|
|
38540
|
-
const base =
|
|
38710
|
+
const base = path36.join(cwd, rel);
|
|
38541
38711
|
if (!fs31.existsSync(base) || !fs31.statSync(base).isDirectory()) continue;
|
|
38542
38712
|
let entries = [];
|
|
38543
38713
|
try {
|
|
@@ -38547,13 +38717,13 @@ function discoverSkillLayoutRoots(cwd) {
|
|
|
38547
38717
|
}
|
|
38548
38718
|
const skills2 = [];
|
|
38549
38719
|
for (const name of entries) {
|
|
38550
|
-
const dir =
|
|
38720
|
+
const dir = path36.join(base, name);
|
|
38551
38721
|
try {
|
|
38552
38722
|
if (!fs31.statSync(dir).isDirectory()) continue;
|
|
38553
38723
|
} catch {
|
|
38554
38724
|
continue;
|
|
38555
38725
|
}
|
|
38556
|
-
if (!fs31.existsSync(
|
|
38726
|
+
if (!fs31.existsSync(path36.join(dir, "SKILL.md"))) continue;
|
|
38557
38727
|
const relPath = `${rel}/${name}`.replace(/\\/g, "/");
|
|
38558
38728
|
skills2.push({ name, relPath });
|
|
38559
38729
|
}
|
|
@@ -38676,7 +38846,9 @@ var API_TO_BRIDGE_MESSAGE_TYPES = [
|
|
|
38676
38846
|
"file_browser_search",
|
|
38677
38847
|
"skill_layout_request",
|
|
38678
38848
|
"install_skills",
|
|
38679
|
-
"refresh_local_skills"
|
|
38849
|
+
"refresh_local_skills",
|
|
38850
|
+
"bridge_git_context_request",
|
|
38851
|
+
"list_repo_branches_request"
|
|
38680
38852
|
];
|
|
38681
38853
|
var API_TO_BRIDGE_TYPE_SET = new Set(API_TO_BRIDGE_MESSAGE_TYPES);
|
|
38682
38854
|
function parseApiToBridgeMessage(data, log2) {
|
|
@@ -38760,9 +38932,6 @@ var handleAgentConfigMessage = (msg, deps) => {
|
|
|
38760
38932
|
handleBridgeAgentConfig(msg, deps);
|
|
38761
38933
|
};
|
|
38762
38934
|
|
|
38763
|
-
// src/prompt-turn-queue/runner.ts
|
|
38764
|
-
import fs32 from "node:fs";
|
|
38765
|
-
|
|
38766
38935
|
// src/prompt-turn-queue/client-report.ts
|
|
38767
38936
|
function sendPromptQueueClientReport(ws, queues) {
|
|
38768
38937
|
if (!ws) return false;
|
|
@@ -38832,8 +39001,36 @@ async function mergeServerQueueSnapshot(queueKey, serverTurns) {
|
|
|
38832
39001
|
return { queueKey, updatedAt: (/* @__PURE__ */ new Date()).toISOString(), turns };
|
|
38833
39002
|
}
|
|
38834
39003
|
|
|
38835
|
-
// src/prompt-turn-queue/runner.ts
|
|
38836
|
-
|
|
39004
|
+
// src/prompt-turn-queue/runner/dispatch-local-prompt.ts
|
|
39005
|
+
function dispatchLocalPrompt(next, deps) {
|
|
39006
|
+
const pl = next.payload;
|
|
39007
|
+
const rawParent = pl["sessionParent"];
|
|
39008
|
+
const sessionParent = rawParent === "bridge_root" || rawParent === "worktrees_root" ? rawParent : void 0;
|
|
39009
|
+
const rawParentPath = pl["sessionParentPath"];
|
|
39010
|
+
const sessionParentPath = typeof rawParentPath === "string" && rawParentPath.trim() !== "" ? rawParentPath.trim() : void 0;
|
|
39011
|
+
const rawBaseBranches = pl["worktreeBaseBranches"];
|
|
39012
|
+
const worktreeBaseBranches = rawBaseBranches != null && typeof rawBaseBranches === "object" && !Array.isArray(rawBaseBranches) ? rawBaseBranches : void 0;
|
|
39013
|
+
const msg = {
|
|
39014
|
+
type: "prompt",
|
|
39015
|
+
sessionId: next.sessionId,
|
|
39016
|
+
runId: next.turnId,
|
|
39017
|
+
prompt: pl.prompt,
|
|
39018
|
+
mode: typeof pl.mode === "string" ? pl.mode : "agent",
|
|
39019
|
+
isNewSession: pl.isNewSession === true,
|
|
39020
|
+
...sessionParent ? { sessionParent } : {},
|
|
39021
|
+
...sessionParentPath ? { sessionParentPath } : {},
|
|
39022
|
+
...worktreeBaseBranches && Object.keys(worktreeBaseBranches).length > 0 ? { worktreeBaseBranches } : {},
|
|
39023
|
+
...typeof pl.followUpCatalogPromptId === "string" ? { followUpCatalogPromptId: pl.followUpCatalogPromptId } : {},
|
|
39024
|
+
...Array.isArray(pl.sessionChangeSummaryFilePaths) ? { sessionChangeSummaryFilePaths: pl.sessionChangeSummaryFilePaths } : {},
|
|
39025
|
+
...Array.isArray(pl.sessionChangeSummaryFileSnapshots) ? { sessionChangeSummaryFileSnapshots: pl.sessionChangeSummaryFileSnapshots } : {},
|
|
39026
|
+
...typeof pl.agentType === "string" && pl.agentType.trim() ? { agentType: pl.agentType.trim() } : {},
|
|
39027
|
+
...pl.agentConfig != null && typeof pl.agentConfig === "object" && !Array.isArray(pl.agentConfig) && Object.keys(pl.agentConfig).length > 0 ? { agentConfig: pl.agentConfig } : {},
|
|
39028
|
+
...Array.isArray(pl.attachments) && pl.attachments.length > 0 ? { attachments: pl.attachments } : {}
|
|
39029
|
+
};
|
|
39030
|
+
handleBridgePrompt(msg, deps);
|
|
39031
|
+
}
|
|
39032
|
+
|
|
39033
|
+
// src/prompt-turn-queue/runner/queue-selection.ts
|
|
38837
39034
|
function isRunnableServerState(s) {
|
|
38838
39035
|
return s === "queued" || s === "requeued" || s === "requeued_with_revert";
|
|
38839
39036
|
}
|
|
@@ -38852,6 +39049,28 @@ function pickNextRunnableTurn(turns) {
|
|
|
38852
39049
|
function hasRunningTurn(turns) {
|
|
38853
39050
|
return turns.some((t) => t.lastClientState === "running");
|
|
38854
39051
|
}
|
|
39052
|
+
|
|
39053
|
+
// src/prompt-turn-queue/runner/run-id-queue-key-map.ts
|
|
39054
|
+
var runIdToQueueKey = /* @__PURE__ */ new Map();
|
|
39055
|
+
function getRunIdQueueKey(runId) {
|
|
39056
|
+
return runIdToQueueKey.get(runId);
|
|
39057
|
+
}
|
|
39058
|
+
function setRunIdQueueKey(runId, queueKey) {
|
|
39059
|
+
runIdToQueueKey.set(runId, queueKey);
|
|
39060
|
+
}
|
|
39061
|
+
function deleteRunIdQueueKey(runId) {
|
|
39062
|
+
const queueKey = runIdToQueueKey.get(runId);
|
|
39063
|
+
runIdToQueueKey.delete(runId);
|
|
39064
|
+
return queueKey;
|
|
39065
|
+
}
|
|
39066
|
+
function syncRunningTurnQueueKeys(turns, queueKey) {
|
|
39067
|
+
for (const running of turns.filter((t) => t.lastClientState === "running")) {
|
|
39068
|
+
runIdToQueueKey.set(running.turnId, queueKey);
|
|
39069
|
+
}
|
|
39070
|
+
}
|
|
39071
|
+
|
|
39072
|
+
// src/prompt-turn-queue/runner/run-local-revert-before-queued-prompt.ts
|
|
39073
|
+
import fs32 from "node:fs";
|
|
38855
39074
|
async function runLocalRevertBeforeQueuedPrompt(next, deps) {
|
|
38856
39075
|
if (next.serverState !== "requeued_with_revert") return true;
|
|
38857
39076
|
const sid = next.sessionId;
|
|
@@ -38873,30 +39092,23 @@ async function runLocalRevertBeforeQueuedPrompt(next, deps) {
|
|
|
38873
39092
|
}
|
|
38874
39093
|
return res.ok;
|
|
38875
39094
|
}
|
|
38876
|
-
|
|
38877
|
-
|
|
38878
|
-
|
|
38879
|
-
|
|
38880
|
-
const
|
|
38881
|
-
|
|
38882
|
-
const
|
|
38883
|
-
|
|
38884
|
-
|
|
38885
|
-
|
|
38886
|
-
|
|
38887
|
-
|
|
38888
|
-
|
|
38889
|
-
|
|
38890
|
-
...sessionParentPath ? { sessionParentPath } : {},
|
|
38891
|
-
...typeof pl.followUpCatalogPromptId === "string" ? { followUpCatalogPromptId: pl.followUpCatalogPromptId } : {},
|
|
38892
|
-
...Array.isArray(pl.sessionChangeSummaryFilePaths) ? { sessionChangeSummaryFilePaths: pl.sessionChangeSummaryFilePaths } : {},
|
|
38893
|
-
...Array.isArray(pl.sessionChangeSummaryFileSnapshots) ? { sessionChangeSummaryFileSnapshots: pl.sessionChangeSummaryFileSnapshots } : {},
|
|
38894
|
-
...typeof pl.agentType === "string" && pl.agentType.trim() ? { agentType: pl.agentType.trim() } : {},
|
|
38895
|
-
...pl.agentConfig != null && typeof pl.agentConfig === "object" && !Array.isArray(pl.agentConfig) && Object.keys(pl.agentConfig).length > 0 ? { agentConfig: pl.agentConfig } : {},
|
|
38896
|
-
...Array.isArray(pl.attachments) && pl.attachments.length > 0 ? { attachments: pl.attachments } : {}
|
|
38897
|
-
};
|
|
38898
|
-
handleBridgePrompt(msg, deps);
|
|
39095
|
+
|
|
39096
|
+
// src/prompt-turn-queue/runner/finalize-prompt-turn-on-bridge.ts
|
|
39097
|
+
async function finalizePromptTurnOnBridge(getWs, runId, success2, opts) {
|
|
39098
|
+
if (!runId) return false;
|
|
39099
|
+
const queueKey = deleteRunIdQueueKey(runId);
|
|
39100
|
+
if (!queueKey) return false;
|
|
39101
|
+
const f = await readPersistedQueue(queueKey);
|
|
39102
|
+
if (!f) return false;
|
|
39103
|
+
const t = f.turns.find((x) => x.turnId === runId);
|
|
39104
|
+
if (!t) return false;
|
|
39105
|
+
t.lastClientState = opts?.terminalClientState ?? (success2 ? "stopped" : "failed");
|
|
39106
|
+
await writePersistedQueue(f);
|
|
39107
|
+
sendPromptQueueClientReport(getWs(), { [queueKey]: [{ turnId: runId, clientState: t.lastClientState }] });
|
|
39108
|
+
return true;
|
|
38899
39109
|
}
|
|
39110
|
+
|
|
39111
|
+
// src/prompt-turn-queue/runner/apply-prompt-queue-state-from-server.ts
|
|
38900
39112
|
async function applyPromptQueueStateFromServer(msg, deps) {
|
|
38901
39113
|
const raw = msg.queues;
|
|
38902
39114
|
if (!raw || typeof raw !== "object") return;
|
|
@@ -38910,9 +39122,7 @@ async function applyPromptQueueStateFromServer(msg, deps) {
|
|
|
38910
39122
|
if (!Array.isArray(serverTurns)) continue;
|
|
38911
39123
|
const file2 = await readPersistedQueue(queueKey);
|
|
38912
39124
|
if (!file2) continue;
|
|
38913
|
-
|
|
38914
|
-
runIdToQueueKey.set(running.turnId, queueKey);
|
|
38915
|
-
}
|
|
39125
|
+
syncRunningTurnQueueKeys(file2.turns, queueKey);
|
|
38916
39126
|
}
|
|
38917
39127
|
for (const [queueKey, serverTurns] of Object.entries(raw)) {
|
|
38918
39128
|
if (!Array.isArray(serverTurns)) continue;
|
|
@@ -38957,7 +39167,7 @@ async function applyPromptQueueStateFromServer(msg, deps) {
|
|
|
38957
39167
|
}
|
|
38958
39168
|
next.lastClientState = "running";
|
|
38959
39169
|
await writePersistedQueue(file2);
|
|
38960
|
-
|
|
39170
|
+
setRunIdQueueKey(next.turnId, queueKey);
|
|
38961
39171
|
startedThisTick.add(next.turnId);
|
|
38962
39172
|
report[queueKey] = [{ turnId: next.turnId, clientState: "running" }];
|
|
38963
39173
|
}
|
|
@@ -38970,24 +39180,10 @@ async function applyPromptQueueStateFromServer(msg, deps) {
|
|
|
38970
39180
|
if (!file2) continue;
|
|
38971
39181
|
const running = file2.turns.find((t) => t.lastClientState === "running");
|
|
38972
39182
|
if (!running || !startedThisTick.has(running.turnId)) continue;
|
|
38973
|
-
if (
|
|
39183
|
+
if (getRunIdQueueKey(running.turnId) !== queueKey) continue;
|
|
38974
39184
|
dispatchLocalPrompt(running, deps);
|
|
38975
39185
|
}
|
|
38976
39186
|
}
|
|
38977
|
-
async function finalizePromptTurnOnBridge(getWs, runId, success2, opts) {
|
|
38978
|
-
if (!runId) return false;
|
|
38979
|
-
const queueKey = runIdToQueueKey.get(runId);
|
|
38980
|
-
runIdToQueueKey.delete(runId);
|
|
38981
|
-
if (!queueKey) return false;
|
|
38982
|
-
const f = await readPersistedQueue(queueKey);
|
|
38983
|
-
if (!f) return false;
|
|
38984
|
-
const t = f.turns.find((x) => x.turnId === runId);
|
|
38985
|
-
if (!t) return false;
|
|
38986
|
-
t.lastClientState = opts?.terminalClientState ?? (success2 ? "stopped" : "failed");
|
|
38987
|
-
await writePersistedQueue(f);
|
|
38988
|
-
sendPromptQueueClientReport(getWs(), { [queueKey]: [{ turnId: runId, clientState: t.lastClientState }] });
|
|
38989
|
-
return true;
|
|
38990
|
-
}
|
|
38991
39187
|
|
|
38992
39188
|
// src/agents/acp/from-bridge/bridge-prompt-wiring.ts
|
|
38993
39189
|
function createBridgePromptSenders(deps, getWs) {
|
|
@@ -39034,6 +39230,91 @@ function createBridgePromptSenders(deps, getWs) {
|
|
|
39034
39230
|
return { sendBridgeMessage, sendResult: sendResult2, sendSessionUpdate };
|
|
39035
39231
|
}
|
|
39036
39232
|
|
|
39233
|
+
// src/agents/acp/from-bridge/handle-bridge-prompt/parse-bridge-attachments.ts
|
|
39234
|
+
function parseBridgeAttachments(msg) {
|
|
39235
|
+
const raw = msg.attachments;
|
|
39236
|
+
if (!Array.isArray(raw)) return [];
|
|
39237
|
+
const out = [];
|
|
39238
|
+
for (const x of raw) {
|
|
39239
|
+
if (x === null || typeof x !== "object" || Array.isArray(x)) continue;
|
|
39240
|
+
const o = x;
|
|
39241
|
+
const id = typeof o.attachmentId === "string" ? o.attachmentId.trim() : "";
|
|
39242
|
+
if (!id) continue;
|
|
39243
|
+
const mt = typeof o.mimeType === "string" && o.mimeType.trim() ? o.mimeType.trim() : "application/octet-stream";
|
|
39244
|
+
out.push({ attachmentId: id, mimeType: mt });
|
|
39245
|
+
}
|
|
39246
|
+
return out;
|
|
39247
|
+
}
|
|
39248
|
+
|
|
39249
|
+
// src/agents/acp/from-bridge/handle-bridge-prompt/parse-worktree-base-branches.ts
|
|
39250
|
+
function parseWorktreeBaseBranches(msg) {
|
|
39251
|
+
const raw = msg.worktreeBaseBranches;
|
|
39252
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return void 0;
|
|
39253
|
+
const out = {};
|
|
39254
|
+
for (const [k, v] of Object.entries(raw)) {
|
|
39255
|
+
if (typeof k !== "string" || typeof v !== "string") continue;
|
|
39256
|
+
const rel = k.trim();
|
|
39257
|
+
const branch = v.trim();
|
|
39258
|
+
if (rel && branch) out[rel] = branch;
|
|
39259
|
+
}
|
|
39260
|
+
return Object.keys(out).length > 0 ? out : void 0;
|
|
39261
|
+
}
|
|
39262
|
+
|
|
39263
|
+
// src/agents/acp/change-summary/decrypt-change-summary-file-input.ts
|
|
39264
|
+
function decryptChangeSummaryFileInput(row, e2ee) {
|
|
39265
|
+
if (!e2ee) return row;
|
|
39266
|
+
for (const field of ["path", "patchContent", "oldText", "newText"]) {
|
|
39267
|
+
const raw = row[field];
|
|
39268
|
+
if (typeof raw !== "string" || raw.trim() === "") continue;
|
|
39269
|
+
let o;
|
|
39270
|
+
try {
|
|
39271
|
+
o = JSON.parse(raw);
|
|
39272
|
+
} catch {
|
|
39273
|
+
continue;
|
|
39274
|
+
}
|
|
39275
|
+
if (!isE2eeEnvelope(o.ee)) continue;
|
|
39276
|
+
try {
|
|
39277
|
+
const d = e2ee.decryptMessage(o);
|
|
39278
|
+
const out = {
|
|
39279
|
+
path: typeof d.path === "string" ? d.path : row.path
|
|
39280
|
+
};
|
|
39281
|
+
if (d.directoryRemoved === true) out.directoryRemoved = true;
|
|
39282
|
+
else if (row.directoryRemoved === true) out.directoryRemoved = true;
|
|
39283
|
+
if (typeof d.patchContent === "string") out.patchContent = d.patchContent;
|
|
39284
|
+
else if (typeof row.patchContent === "string" && row.patchContent !== raw) out.patchContent = row.patchContent;
|
|
39285
|
+
if (typeof d.oldText === "string") out.oldText = d.oldText;
|
|
39286
|
+
else if (typeof row.oldText === "string") out.oldText = row.oldText;
|
|
39287
|
+
if (typeof d.newText === "string") out.newText = d.newText;
|
|
39288
|
+
else if (typeof row.newText === "string") out.newText = row.newText;
|
|
39289
|
+
return out;
|
|
39290
|
+
} catch {
|
|
39291
|
+
return row;
|
|
39292
|
+
}
|
|
39293
|
+
}
|
|
39294
|
+
return row;
|
|
39295
|
+
}
|
|
39296
|
+
|
|
39297
|
+
// src/agents/acp/change-summary/resolve-change-summary-prompt-for-agent.ts
|
|
39298
|
+
function hasSummarizePayload(f) {
|
|
39299
|
+
return f.directoryRemoved === true || f.patchContent != null && f.patchContent.trim() !== "" || f.oldText != null && f.oldText.trim() !== "" || f.newText != null && f.newText.trim() !== "";
|
|
39300
|
+
}
|
|
39301
|
+
function resolveChangeSummaryPromptForAgent(params) {
|
|
39302
|
+
const isBuiltin = params.followUpCatalogPromptId === BUILTIN_SESSION_CHANGE_SUMMARY_FOLLOW_UP_CATALOG_PROMPT_ID;
|
|
39303
|
+
const snaps = params.sessionChangeSummaryFileSnapshots;
|
|
39304
|
+
if (!isBuiltin || !snaps || snaps.length === 0) {
|
|
39305
|
+
return { promptText: params.bridgePromptText, sessionChangeSummaryFilePaths: void 0 };
|
|
39306
|
+
}
|
|
39307
|
+
const decrypted = dedupeSessionFileChangesByPath(snaps.map((row) => decryptChangeSummaryFileInput(row, params.e2ee)));
|
|
39308
|
+
const withPayload = decrypted.filter(hasSummarizePayload);
|
|
39309
|
+
if (withPayload.length === 0) {
|
|
39310
|
+
return { promptText: params.bridgePromptText, sessionChangeSummaryFilePaths: void 0 };
|
|
39311
|
+
}
|
|
39312
|
+
return {
|
|
39313
|
+
promptText: buildSessionChangeSummaryPrompt(withPayload),
|
|
39314
|
+
sessionChangeSummaryFilePaths: withPayload.map((f) => f.path)
|
|
39315
|
+
};
|
|
39316
|
+
}
|
|
39317
|
+
|
|
39037
39318
|
// src/agents/acp/from-bridge/bridge-prompt-preamble.ts
|
|
39038
39319
|
import { execFile as execFile8 } from "node:child_process";
|
|
39039
39320
|
import { promisify as promisify8 } from "node:util";
|
|
@@ -39086,9 +39367,9 @@ function parseChangeSummarySnapshots(raw) {
|
|
|
39086
39367
|
for (const item of raw) {
|
|
39087
39368
|
if (!item || typeof item !== "object") continue;
|
|
39088
39369
|
const o = item;
|
|
39089
|
-
const
|
|
39090
|
-
if (!
|
|
39091
|
-
const row = { path:
|
|
39370
|
+
const path46 = typeof o.path === "string" && o.path.trim() !== "" ? o.path.trim() : "";
|
|
39371
|
+
if (!path46) continue;
|
|
39372
|
+
const row = { path: path46 };
|
|
39092
39373
|
if (typeof o.patchContent === "string") row.patchContent = o.patchContent;
|
|
39093
39374
|
if (typeof o.oldText === "string") row.oldText = o.oldText;
|
|
39094
39375
|
if (typeof o.newText === "string") row.newText = o.newText;
|
|
@@ -39105,76 +39386,73 @@ function parseFollowUpFieldsFromPromptMessage(msg) {
|
|
|
39105
39386
|
return { followUpCatalogPromptId, sessionChangeSummaryFilePaths, sessionChangeSummaryFileSnapshots };
|
|
39106
39387
|
}
|
|
39107
39388
|
|
|
39108
|
-
// src/agents/acp/
|
|
39109
|
-
function
|
|
39110
|
-
|
|
39111
|
-
|
|
39112
|
-
|
|
39113
|
-
|
|
39114
|
-
|
|
39115
|
-
|
|
39116
|
-
|
|
39117
|
-
|
|
39118
|
-
|
|
39119
|
-
|
|
39120
|
-
|
|
39121
|
-
|
|
39122
|
-
|
|
39123
|
-
|
|
39124
|
-
|
|
39125
|
-
|
|
39126
|
-
|
|
39127
|
-
|
|
39128
|
-
|
|
39129
|
-
|
|
39130
|
-
|
|
39131
|
-
|
|
39132
|
-
|
|
39133
|
-
|
|
39134
|
-
|
|
39135
|
-
|
|
39136
|
-
|
|
39137
|
-
|
|
39138
|
-
|
|
39139
|
-
|
|
39140
|
-
}
|
|
39141
|
-
|
|
39142
|
-
|
|
39143
|
-
|
|
39144
|
-
|
|
39145
|
-
|
|
39146
|
-
|
|
39147
|
-
|
|
39148
|
-
|
|
39149
|
-
|
|
39150
|
-
|
|
39151
|
-
}
|
|
39152
|
-
const decrypted = dedupeSessionFileChangesByPath(snaps.map((row) => decryptChangeSummaryFileInput(row, params.e2ee)));
|
|
39153
|
-
const withPayload = decrypted.filter(hasSummarizePayload);
|
|
39154
|
-
if (withPayload.length === 0) {
|
|
39155
|
-
return { promptText: params.bridgePromptText, sessionChangeSummaryFilePaths: void 0 };
|
|
39389
|
+
// src/agents/acp/from-bridge/handle-bridge-prompt/run-preamble-and-prompt.ts
|
|
39390
|
+
async function runPreambleAndPrompt(params) {
|
|
39391
|
+
const {
|
|
39392
|
+
deps,
|
|
39393
|
+
msg,
|
|
39394
|
+
getWs,
|
|
39395
|
+
log: log2,
|
|
39396
|
+
sessionWorktreeManager,
|
|
39397
|
+
sessionId,
|
|
39398
|
+
runId,
|
|
39399
|
+
promptText,
|
|
39400
|
+
attachments,
|
|
39401
|
+
mode,
|
|
39402
|
+
agentType,
|
|
39403
|
+
agentId,
|
|
39404
|
+
agentConfig,
|
|
39405
|
+
resolvedCwd,
|
|
39406
|
+
senders: { sendResult: sendResult2, sendSessionUpdate }
|
|
39407
|
+
} = params;
|
|
39408
|
+
const effectiveCwd = resolveSessionParentPathForAgentProcess(resolvedCwd);
|
|
39409
|
+
await runBridgePromptPreamble({
|
|
39410
|
+
getWs,
|
|
39411
|
+
log: log2,
|
|
39412
|
+
sessionWorktreeManager,
|
|
39413
|
+
sessionId,
|
|
39414
|
+
runId,
|
|
39415
|
+
effectiveCwd
|
|
39416
|
+
});
|
|
39417
|
+
const {
|
|
39418
|
+
followUpCatalogPromptId,
|
|
39419
|
+
sessionChangeSummaryFilePaths: pathsFromBridge,
|
|
39420
|
+
sessionChangeSummaryFileSnapshots
|
|
39421
|
+
} = parseFollowUpFieldsFromPromptMessage(msg);
|
|
39422
|
+
const { promptText: resolvedPromptText, sessionChangeSummaryFilePaths } = resolveChangeSummaryPromptForAgent({
|
|
39423
|
+
followUpCatalogPromptId,
|
|
39424
|
+
sessionChangeSummaryFileSnapshots,
|
|
39425
|
+
bridgePromptText: promptText,
|
|
39426
|
+
e2ee: deps.e2ee
|
|
39427
|
+
});
|
|
39428
|
+
if (sessionChangeSummaryFileSnapshots && sessionChangeSummaryFileSnapshots.length > 0 && resolvedPromptText === promptText) {
|
|
39429
|
+
deps.log(
|
|
39430
|
+
"[Agent] Change-summary snapshots were present but the prompt was not rebuilt (decrypt failed or empty payloads); sending the bridge prompt as-is."
|
|
39431
|
+
);
|
|
39156
39432
|
}
|
|
39157
|
-
|
|
39158
|
-
|
|
39159
|
-
|
|
39160
|
-
|
|
39433
|
+
const pathsForUpload = sessionChangeSummaryFilePaths ?? pathsFromBridge;
|
|
39434
|
+
deps.acpManager.handlePrompt({
|
|
39435
|
+
promptText: resolvedPromptText,
|
|
39436
|
+
promptId: msg.id,
|
|
39437
|
+
sessionId,
|
|
39438
|
+
runId,
|
|
39439
|
+
mode,
|
|
39440
|
+
agentType,
|
|
39441
|
+
agentId,
|
|
39442
|
+
agentConfig,
|
|
39443
|
+
sessionParentPath: effectiveCwd,
|
|
39444
|
+
sendResult: sendResult2,
|
|
39445
|
+
sendSessionUpdate,
|
|
39446
|
+
followUpCatalogPromptId,
|
|
39447
|
+
sessionChangeSummaryFilePaths: pathsForUpload,
|
|
39448
|
+
cloudApiBaseUrl: deps.cloudApiBaseUrl,
|
|
39449
|
+
getCloudAccessToken: deps.getCloudAccessToken,
|
|
39450
|
+
e2ee: deps.e2ee,
|
|
39451
|
+
...attachments.length > 0 ? { attachments } : {}
|
|
39452
|
+
});
|
|
39161
39453
|
}
|
|
39162
39454
|
|
|
39163
|
-
// src/agents/acp/from-bridge/handle-bridge-prompt.ts
|
|
39164
|
-
function parseBridgeAttachments(msg) {
|
|
39165
|
-
const raw = msg.attachments;
|
|
39166
|
-
if (!Array.isArray(raw)) return [];
|
|
39167
|
-
const out = [];
|
|
39168
|
-
for (const x of raw) {
|
|
39169
|
-
if (x === null || typeof x !== "object" || Array.isArray(x)) continue;
|
|
39170
|
-
const o = x;
|
|
39171
|
-
const id = typeof o.attachmentId === "string" ? o.attachmentId.trim() : "";
|
|
39172
|
-
if (!id) continue;
|
|
39173
|
-
const mt = typeof o.mimeType === "string" && o.mimeType.trim() ? o.mimeType.trim() : "application/octet-stream";
|
|
39174
|
-
out.push({ attachmentId: id, mimeType: mt });
|
|
39175
|
-
}
|
|
39176
|
-
return out;
|
|
39177
|
-
}
|
|
39455
|
+
// src/agents/acp/from-bridge/handle-bridge-prompt/handle-bridge-prompt.ts
|
|
39178
39456
|
function handleBridgePrompt(msg, deps) {
|
|
39179
39457
|
const { getWs, log: log2, acpManager, sessionWorktreeManager } = deps;
|
|
39180
39458
|
const rawPrompt = msg.prompt;
|
|
@@ -39183,12 +39461,12 @@ function handleBridgePrompt(msg, deps) {
|
|
|
39183
39461
|
const sessionId = msg.sessionId;
|
|
39184
39462
|
const runId = typeof msg.runId === "string" ? msg.runId : void 0;
|
|
39185
39463
|
const promptId = typeof msg.id === "string" ? msg.id : void 0;
|
|
39186
|
-
const
|
|
39464
|
+
const senders = createBridgePromptSenders(deps, getWs);
|
|
39187
39465
|
if (!promptText.trim() && attachments.length === 0) {
|
|
39188
39466
|
log2(
|
|
39189
39467
|
`[Bridge service] Prompt ignored: empty or missing prompt text (session ${typeof msg.sessionId === "string" ? msg.sessionId.slice(0, 8) : "\u2014"}\u2026, run ${typeof msg.runId === "string" ? msg.runId.slice(0, 8) : "\u2014"}\u2026).`
|
|
39190
39468
|
);
|
|
39191
|
-
sendBridgeMessage(
|
|
39469
|
+
senders.sendBridgeMessage(
|
|
39192
39470
|
{
|
|
39193
39471
|
type: "prompt_result",
|
|
39194
39472
|
...promptId ? { id: promptId } : {},
|
|
@@ -39210,57 +39488,50 @@ function handleBridgePrompt(msg, deps) {
|
|
|
39210
39488
|
const agentId = typeof rawAgentId === "string" && rawAgentId.trim() !== "" ? rawAgentId.trim() : null;
|
|
39211
39489
|
const mode = typeof msg.mode === "string" && msg.mode.trim() ? msg.mode.trim() : void 0;
|
|
39212
39490
|
const agentConfig = msg.agentConfig != null && typeof msg.agentConfig === "object" && !Array.isArray(msg.agentConfig) ? msg.agentConfig : void 0;
|
|
39491
|
+
const worktreeBaseBranches = parseWorktreeBaseBranches(msg);
|
|
39213
39492
|
acpManager.logPromptReceivedFromBridge({ agentType, mode });
|
|
39214
|
-
|
|
39215
|
-
|
|
39216
|
-
|
|
39493
|
+
void sessionWorktreeManager.resolveSessionParentPathForPrompt(sessionId, {
|
|
39494
|
+
isNewSession,
|
|
39495
|
+
sessionParent,
|
|
39496
|
+
sessionParentPath,
|
|
39497
|
+
...worktreeBaseBranches ? { worktreeBaseBranches } : {}
|
|
39498
|
+
}).then(
|
|
39499
|
+
(cwd) => runPreambleAndPrompt({
|
|
39500
|
+
deps,
|
|
39501
|
+
msg,
|
|
39217
39502
|
getWs,
|
|
39218
39503
|
log: log2,
|
|
39219
39504
|
sessionWorktreeManager,
|
|
39220
39505
|
sessionId,
|
|
39221
39506
|
runId,
|
|
39222
|
-
|
|
39223
|
-
|
|
39224
|
-
|
|
39225
|
-
|
|
39226
|
-
|
|
39227
|
-
|
|
39228
|
-
|
|
39229
|
-
|
|
39230
|
-
|
|
39231
|
-
|
|
39232
|
-
|
|
39233
|
-
|
|
39234
|
-
|
|
39235
|
-
|
|
39236
|
-
|
|
39237
|
-
|
|
39238
|
-
|
|
39239
|
-
}
|
|
39240
|
-
const pathsForUpload = sessionChangeSummaryFilePaths ?? pathsFromBridge;
|
|
39241
|
-
acpManager.handlePrompt({
|
|
39242
|
-
promptText: resolvedPromptText,
|
|
39243
|
-
promptId: msg.id,
|
|
39507
|
+
promptText,
|
|
39508
|
+
attachments,
|
|
39509
|
+
mode,
|
|
39510
|
+
agentType,
|
|
39511
|
+
agentId,
|
|
39512
|
+
agentConfig,
|
|
39513
|
+
resolvedCwd: cwd,
|
|
39514
|
+
senders
|
|
39515
|
+
})
|
|
39516
|
+
).catch((err) => {
|
|
39517
|
+
log2(`[Agent] Session parent path resolve failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
39518
|
+
void runPreambleAndPrompt({
|
|
39519
|
+
deps,
|
|
39520
|
+
msg,
|
|
39521
|
+
getWs,
|
|
39522
|
+
log: log2,
|
|
39523
|
+
sessionWorktreeManager,
|
|
39244
39524
|
sessionId,
|
|
39245
39525
|
runId,
|
|
39526
|
+
promptText,
|
|
39527
|
+
attachments,
|
|
39246
39528
|
mode,
|
|
39247
39529
|
agentType,
|
|
39248
39530
|
agentId,
|
|
39249
39531
|
agentConfig,
|
|
39250
|
-
|
|
39251
|
-
|
|
39252
|
-
sendSessionUpdate,
|
|
39253
|
-
followUpCatalogPromptId,
|
|
39254
|
-
sessionChangeSummaryFilePaths: pathsForUpload,
|
|
39255
|
-
cloudApiBaseUrl: deps.cloudApiBaseUrl,
|
|
39256
|
-
getCloudAccessToken: deps.getCloudAccessToken,
|
|
39257
|
-
e2ee: deps.e2ee,
|
|
39258
|
-
...attachments.length > 0 ? { attachments } : {}
|
|
39532
|
+
resolvedCwd: void 0,
|
|
39533
|
+
senders
|
|
39259
39534
|
});
|
|
39260
|
-
}
|
|
39261
|
-
void sessionWorktreeManager.resolveSessionParentPathForPrompt(sessionId, { isNewSession, sessionParent, sessionParentPath }).then((cwd) => preambleAndPrompt(cwd)).catch((err) => {
|
|
39262
|
-
log2(`[Agent] Session parent path resolve failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
39263
|
-
void preambleAndPrompt(void 0);
|
|
39264
39535
|
});
|
|
39265
39536
|
}
|
|
39266
39537
|
|
|
@@ -39308,8 +39579,8 @@ function randomSecret() {
|
|
|
39308
39579
|
}
|
|
39309
39580
|
return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
39310
39581
|
}
|
|
39311
|
-
async function requestPreviewApi(port, secret, method,
|
|
39312
|
-
const url2 = `http://127.0.0.1:${port}${
|
|
39582
|
+
async function requestPreviewApi(port, secret, method, path46, body) {
|
|
39583
|
+
const url2 = `http://127.0.0.1:${port}${path46}`;
|
|
39313
39584
|
const headers = {
|
|
39314
39585
|
[PREVIEW_SECRET_HEADER]: secret,
|
|
39315
39586
|
"Content-Type": "application/json"
|
|
@@ -39321,7 +39592,7 @@ async function requestPreviewApi(port, secret, method, path43, body) {
|
|
|
39321
39592
|
});
|
|
39322
39593
|
const data = await res.json().catch(() => ({}));
|
|
39323
39594
|
if (!res.ok) {
|
|
39324
|
-
throw new Error(data?.error ?? `Preview API ${method} ${
|
|
39595
|
+
throw new Error(data?.error ?? `Preview API ${method} ${path46}: ${res.status}`);
|
|
39325
39596
|
}
|
|
39326
39597
|
return data;
|
|
39327
39598
|
}
|
|
@@ -39489,11 +39760,11 @@ var handleSkillCallMessage = (msg, { getWs, log: log2 }) => {
|
|
|
39489
39760
|
import fs34 from "node:fs";
|
|
39490
39761
|
|
|
39491
39762
|
// src/files/ensure-under-cwd.ts
|
|
39492
|
-
import
|
|
39763
|
+
import path37 from "node:path";
|
|
39493
39764
|
function ensureUnderCwd(relativePath, cwd = getBridgeRoot()) {
|
|
39494
|
-
const normalized =
|
|
39495
|
-
const resolved =
|
|
39496
|
-
if (!resolved.startsWith(cwd +
|
|
39765
|
+
const normalized = path37.normalize(relativePath).replace(/^(\.\/)+/, "");
|
|
39766
|
+
const resolved = path37.resolve(cwd, normalized);
|
|
39767
|
+
if (!resolved.startsWith(cwd + path37.sep) && resolved !== cwd) {
|
|
39497
39768
|
return null;
|
|
39498
39769
|
}
|
|
39499
39770
|
return resolved;
|
|
@@ -39503,11 +39774,11 @@ function ensureUnderCwd(relativePath, cwd = getBridgeRoot()) {
|
|
|
39503
39774
|
var LIST_DIR_YIELD_EVERY = 256;
|
|
39504
39775
|
|
|
39505
39776
|
// src/files/list-dir/map-dir-entry.ts
|
|
39506
|
-
import
|
|
39777
|
+
import path38 from "node:path";
|
|
39507
39778
|
import fs33 from "node:fs";
|
|
39508
39779
|
async function mapDirEntry(d, relativePath, resolved) {
|
|
39509
|
-
const entryPath =
|
|
39510
|
-
const fullPath =
|
|
39780
|
+
const entryPath = path38.join(relativePath || ".", d.name).replace(/\\/g, "/");
|
|
39781
|
+
const fullPath = path38.join(resolved, d.name);
|
|
39511
39782
|
let isDir = d.isDirectory();
|
|
39512
39783
|
if (d.isSymbolicLink()) {
|
|
39513
39784
|
try {
|
|
@@ -39858,13 +40129,13 @@ function resolveFileBrowserSessionParent(sessionWorktreeManager, sessionId) {
|
|
|
39858
40129
|
}
|
|
39859
40130
|
|
|
39860
40131
|
// src/files/handle-file-browser-search.ts
|
|
39861
|
-
import
|
|
40132
|
+
import path39 from "node:path";
|
|
39862
40133
|
var SEARCH_LIMIT = 100;
|
|
39863
40134
|
function handleFileBrowserSearch(msg, socket, e2ee, sessionWorktreeManager) {
|
|
39864
40135
|
void (async () => {
|
|
39865
40136
|
await yieldToEventLoop();
|
|
39866
40137
|
const q = typeof msg.q === "string" ? msg.q : "";
|
|
39867
|
-
const sessionParentPath =
|
|
40138
|
+
const sessionParentPath = path39.resolve(
|
|
39868
40139
|
sessionWorktreeManager != null ? resolveFileBrowserSessionParent(sessionWorktreeManager, msg.sessionId) : getBridgeRoot()
|
|
39869
40140
|
);
|
|
39870
40141
|
if (!await bridgeFileIndexIsPopulated(sessionParentPath)) {
|
|
@@ -39984,7 +40255,7 @@ function handleSkillLayoutRequest(msg, deps) {
|
|
|
39984
40255
|
|
|
39985
40256
|
// src/skills/install-remote-skills.ts
|
|
39986
40257
|
import fs38 from "node:fs";
|
|
39987
|
-
import
|
|
40258
|
+
import path40 from "node:path";
|
|
39988
40259
|
function installRemoteSkills(cwd, targetDir, items) {
|
|
39989
40260
|
const installed2 = [];
|
|
39990
40261
|
if (!Array.isArray(items)) {
|
|
@@ -39995,11 +40266,11 @@ function installRemoteSkills(cwd, targetDir, items) {
|
|
|
39995
40266
|
if (typeof item.sourceId !== "string" || typeof item.skillName !== "string" || typeof item.versionHash !== "string" || !Array.isArray(item.files)) {
|
|
39996
40267
|
continue;
|
|
39997
40268
|
}
|
|
39998
|
-
const skillDir =
|
|
40269
|
+
const skillDir = path40.join(cwd, targetDir, item.skillName);
|
|
39999
40270
|
for (const f of item.files) {
|
|
40000
40271
|
if (typeof f.path !== "string" || !f.text && !f.base64) continue;
|
|
40001
|
-
const dest =
|
|
40002
|
-
fs38.mkdirSync(
|
|
40272
|
+
const dest = path40.join(skillDir, f.path);
|
|
40273
|
+
fs38.mkdirSync(path40.dirname(dest), { recursive: true });
|
|
40003
40274
|
if (f.text !== void 0) {
|
|
40004
40275
|
fs38.writeFileSync(dest, f.text, "utf8");
|
|
40005
40276
|
} else if (f.base64) {
|
|
@@ -40215,6 +40486,148 @@ var handleDevServersConfig = (msg, deps) => {
|
|
|
40215
40486
|
deps.devServerManager?.applyConfig(devServers ?? []);
|
|
40216
40487
|
};
|
|
40217
40488
|
|
|
40489
|
+
// src/git/bridge-git-context.ts
|
|
40490
|
+
import * as path41 from "node:path";
|
|
40491
|
+
|
|
40492
|
+
// src/git/branches/get-current-branch.ts
|
|
40493
|
+
async function getCurrentBranch(repoPath) {
|
|
40494
|
+
try {
|
|
40495
|
+
const git = cliSimpleGit(repoPath);
|
|
40496
|
+
const branch = await git.revparse(["--abbrev-ref", "HEAD"]);
|
|
40497
|
+
const trimmed2 = typeof branch === "string" ? branch.trim() : "";
|
|
40498
|
+
if (!trimmed2 || trimmed2 === "HEAD") return null;
|
|
40499
|
+
return trimmed2;
|
|
40500
|
+
} catch {
|
|
40501
|
+
return null;
|
|
40502
|
+
}
|
|
40503
|
+
}
|
|
40504
|
+
|
|
40505
|
+
// src/git/branches/list-repo-branch-refs.ts
|
|
40506
|
+
function normalizeBranchRef(raw) {
|
|
40507
|
+
const trimmed2 = raw.trim();
|
|
40508
|
+
if (!trimmed2 || trimmed2 === "HEAD") return null;
|
|
40509
|
+
if (trimmed2.startsWith("refs/heads/")) return trimmed2.slice("refs/heads/".length);
|
|
40510
|
+
if (trimmed2.startsWith("refs/remotes/")) return trimmed2.slice("refs/remotes/".length);
|
|
40511
|
+
return trimmed2;
|
|
40512
|
+
}
|
|
40513
|
+
async function listRepoBranchRefs(repoPath) {
|
|
40514
|
+
try {
|
|
40515
|
+
const git = cliSimpleGit(repoPath);
|
|
40516
|
+
const out = await git.raw([
|
|
40517
|
+
"for-each-ref",
|
|
40518
|
+
"--sort=-committerdate",
|
|
40519
|
+
"--format=%(refname:short)",
|
|
40520
|
+
"refs/heads",
|
|
40521
|
+
"refs/remotes"
|
|
40522
|
+
]);
|
|
40523
|
+
const lines = out.split("\n").map((line) => normalizeBranchRef(line)).filter((line) => Boolean(line));
|
|
40524
|
+
const seen = /* @__PURE__ */ new Set();
|
|
40525
|
+
const ordered = [];
|
|
40526
|
+
for (const name of lines) {
|
|
40527
|
+
if (seen.has(name)) continue;
|
|
40528
|
+
seen.add(name);
|
|
40529
|
+
ordered.push(name);
|
|
40530
|
+
}
|
|
40531
|
+
return ordered;
|
|
40532
|
+
} catch {
|
|
40533
|
+
return [];
|
|
40534
|
+
}
|
|
40535
|
+
}
|
|
40536
|
+
|
|
40537
|
+
// src/git/bridge-git-context.ts
|
|
40538
|
+
function folderNameForRelPath(relPath, bridgeRoot) {
|
|
40539
|
+
if (relPath === "." || relPath === "") {
|
|
40540
|
+
return path41.basename(path41.resolve(bridgeRoot)) || "repo";
|
|
40541
|
+
}
|
|
40542
|
+
return path41.basename(relPath) || relPath;
|
|
40543
|
+
}
|
|
40544
|
+
async function discoverGitReposForBridgeContext(bridgeRoot) {
|
|
40545
|
+
const root = path41.resolve(bridgeRoot);
|
|
40546
|
+
if (await isGitRepoDirectory(root)) {
|
|
40547
|
+
const remoteUrl = await getRemoteOriginUrl(root);
|
|
40548
|
+
return [{ absolutePath: root, remoteUrl }];
|
|
40549
|
+
}
|
|
40550
|
+
const [deep, shallow] = await Promise.all([discoverGitReposUnderRoot(root), discoverGitRepos(root)]);
|
|
40551
|
+
const byPath = /* @__PURE__ */ new Map();
|
|
40552
|
+
for (const repo of [...deep, ...shallow]) {
|
|
40553
|
+
byPath.set(path41.resolve(repo.absolutePath), repo);
|
|
40554
|
+
}
|
|
40555
|
+
return [...byPath.values()];
|
|
40556
|
+
}
|
|
40557
|
+
async function getBridgeGitContext(bridgeRoot = getBridgeRoot()) {
|
|
40558
|
+
const bridgeResolved = path41.resolve(bridgeRoot);
|
|
40559
|
+
const repos = await discoverGitReposForBridgeContext(bridgeResolved);
|
|
40560
|
+
const rows = [];
|
|
40561
|
+
for (const repo of repos) {
|
|
40562
|
+
let rel = path41.relative(bridgeResolved, repo.absolutePath);
|
|
40563
|
+
if (rel.startsWith("..") || path41.isAbsolute(rel)) continue;
|
|
40564
|
+
const relPath = rel === "" ? "." : rel.replace(/\\/g, "/");
|
|
40565
|
+
const currentBranch = await getCurrentBranch(repo.absolutePath);
|
|
40566
|
+
const remoteUrl = repo.remoteUrl.trim() || null;
|
|
40567
|
+
rows.push({
|
|
40568
|
+
relPath,
|
|
40569
|
+
folderName: folderNameForRelPath(relPath, bridgeResolved),
|
|
40570
|
+
currentBranch,
|
|
40571
|
+
remoteUrl
|
|
40572
|
+
});
|
|
40573
|
+
}
|
|
40574
|
+
rows.sort((a, b) => a.relPath.localeCompare(b.relPath));
|
|
40575
|
+
return rows;
|
|
40576
|
+
}
|
|
40577
|
+
async function listRepoBranchesForBridge(repoRelPath, bridgeRoot = getBridgeRoot()) {
|
|
40578
|
+
const bridgeResolved = path41.resolve(bridgeRoot);
|
|
40579
|
+
const rel = repoRelPath.trim() === "." ? "" : repoRelPath.trim().replace(/\\/g, "/");
|
|
40580
|
+
const repoPath = rel === "" ? bridgeResolved : path41.join(bridgeResolved, rel);
|
|
40581
|
+
const resolved = path41.resolve(repoPath);
|
|
40582
|
+
if (!resolved.startsWith(bridgeResolved + path41.sep) && resolved !== bridgeResolved) {
|
|
40583
|
+
return [];
|
|
40584
|
+
}
|
|
40585
|
+
return listRepoBranchRefs(resolved);
|
|
40586
|
+
}
|
|
40587
|
+
|
|
40588
|
+
// src/routing/handlers/bridge-git-context-messages.ts
|
|
40589
|
+
function handleBridgeGitContextRequestMessage(msg, getWs) {
|
|
40590
|
+
void (async () => {
|
|
40591
|
+
const socket = getWs();
|
|
40592
|
+
if (!socket) return;
|
|
40593
|
+
try {
|
|
40594
|
+
const repos = await getBridgeGitContext();
|
|
40595
|
+
sendWsMessage(socket, { type: "bridge_git_context_response", id: msg.id, repos });
|
|
40596
|
+
} catch (e) {
|
|
40597
|
+
sendWsMessage(socket, {
|
|
40598
|
+
type: "bridge_git_context_response",
|
|
40599
|
+
id: msg.id,
|
|
40600
|
+
error: e instanceof Error ? e.message : String(e)
|
|
40601
|
+
});
|
|
40602
|
+
}
|
|
40603
|
+
})();
|
|
40604
|
+
}
|
|
40605
|
+
function handleListRepoBranchesRequestMessage(msg, getWs) {
|
|
40606
|
+
void (async () => {
|
|
40607
|
+
const socket = getWs();
|
|
40608
|
+
if (!socket) return;
|
|
40609
|
+
const repoRelPath = typeof msg.repoRelPath === "string" ? msg.repoRelPath.trim() : "";
|
|
40610
|
+
if (!repoRelPath) {
|
|
40611
|
+
sendWsMessage(socket, {
|
|
40612
|
+
type: "list_repo_branches_response",
|
|
40613
|
+
id: msg.id,
|
|
40614
|
+
error: "repoRelPath required"
|
|
40615
|
+
});
|
|
40616
|
+
return;
|
|
40617
|
+
}
|
|
40618
|
+
try {
|
|
40619
|
+
const branches = await listRepoBranchesForBridge(repoRelPath);
|
|
40620
|
+
sendWsMessage(socket, { type: "list_repo_branches_response", id: msg.id, branches });
|
|
40621
|
+
} catch (e) {
|
|
40622
|
+
sendWsMessage(socket, {
|
|
40623
|
+
type: "list_repo_branches_response",
|
|
40624
|
+
id: msg.id,
|
|
40625
|
+
error: e instanceof Error ? e.message : String(e)
|
|
40626
|
+
});
|
|
40627
|
+
}
|
|
40628
|
+
})();
|
|
40629
|
+
}
|
|
40630
|
+
|
|
40218
40631
|
// src/routing/dispatch-bridge-message.ts
|
|
40219
40632
|
function dispatchBridgeMessage(msg, deps) {
|
|
40220
40633
|
switch (msg.type) {
|
|
@@ -40278,6 +40691,12 @@ function dispatchBridgeMessage(msg, deps) {
|
|
|
40278
40691
|
case "refresh_local_skills":
|
|
40279
40692
|
handleRefreshLocalSkills(msg, deps);
|
|
40280
40693
|
break;
|
|
40694
|
+
case "bridge_git_context_request":
|
|
40695
|
+
handleBridgeGitContextRequestMessage(msg, deps.getWs);
|
|
40696
|
+
break;
|
|
40697
|
+
case "list_repo_branches_request":
|
|
40698
|
+
handleListRepoBranchesRequestMessage(msg, deps.getWs);
|
|
40699
|
+
break;
|
|
40281
40700
|
}
|
|
40282
40701
|
}
|
|
40283
40702
|
|
|
@@ -40290,8 +40709,10 @@ function normalizeInboundBridgeWebSocketJson(data) {
|
|
|
40290
40709
|
}
|
|
40291
40710
|
return data;
|
|
40292
40711
|
}
|
|
40293
|
-
function handleBridgeMessage(data, deps) {
|
|
40294
|
-
|
|
40712
|
+
function handleBridgeMessage(data, deps, sourceWs) {
|
|
40713
|
+
const active = deps.getWs();
|
|
40714
|
+
if (!active) return;
|
|
40715
|
+
if (sourceWs != null && sourceWs !== active) return;
|
|
40295
40716
|
const msg = parseApiToBridgeMessage(normalizeInboundBridgeWebSocketJson(data), deps.log);
|
|
40296
40717
|
if (!msg) return;
|
|
40297
40718
|
dispatchBridgeMessage(msg, deps);
|
|
@@ -40343,6 +40764,7 @@ function createMainBridgeWebSocketLifecycle(params) {
|
|
|
40343
40764
|
} = params;
|
|
40344
40765
|
let authRefreshInFlight = false;
|
|
40345
40766
|
function handleOpen() {
|
|
40767
|
+
recordBridgeSessionOpened(state.duplicateBridgeFlap, Date.now());
|
|
40346
40768
|
const logOpenAsPostRefreshReconnect = state.logBridgeOpenAsReconnect;
|
|
40347
40769
|
clearMainBridgeReconnectQuietOnOpen(state, logFn);
|
|
40348
40770
|
state.reconnectAttempt = 0;
|
|
@@ -40377,6 +40799,15 @@ function createMainBridgeWebSocketLifecycle(params) {
|
|
|
40377
40799
|
state.currentWs = null;
|
|
40378
40800
|
if (was) was.removeAllListeners();
|
|
40379
40801
|
const willReconnect = !state.closedByUser;
|
|
40802
|
+
if (willReconnect) {
|
|
40803
|
+
const duplicateResult = evaluateDuplicateBridgeDisconnect(
|
|
40804
|
+
state.duplicateBridgeFlap,
|
|
40805
|
+
Date.now(),
|
|
40806
|
+
code,
|
|
40807
|
+
reason
|
|
40808
|
+
);
|
|
40809
|
+
logDuplicateBridgeWarning(logFn, duplicateResult);
|
|
40810
|
+
}
|
|
40380
40811
|
beginMainBridgeDeferredDisconnect(state, code, reason, logFn, willReconnect);
|
|
40381
40812
|
if (willReconnect) {
|
|
40382
40813
|
state.lastReconnectCloseMeta = { code, reason };
|
|
@@ -40426,6 +40857,10 @@ function createMainBridgeWebSocketLifecycle(params) {
|
|
|
40426
40857
|
state.currentWs = createWsBridge({
|
|
40427
40858
|
url: url2,
|
|
40428
40859
|
clientPingIntervalMs: CLI_WEBSOCKET_CLIENT_PING_MS,
|
|
40860
|
+
isActiveSocket: (socket) => state.currentWs === socket,
|
|
40861
|
+
onCompactHeartbeatAck: (seq) => {
|
|
40862
|
+
messageDeps.onBridgeHeartbeatAck?.(seq);
|
|
40863
|
+
},
|
|
40429
40864
|
onAuthInvalid: () => {
|
|
40430
40865
|
if (authRefreshInFlight) return;
|
|
40431
40866
|
void (async () => {
|
|
@@ -40479,9 +40914,9 @@ function createMainBridgeWebSocketLifecycle(params) {
|
|
|
40479
40914
|
} catch {
|
|
40480
40915
|
}
|
|
40481
40916
|
},
|
|
40482
|
-
onMessage: (data) => {
|
|
40917
|
+
onMessage: (data, sourceWs) => {
|
|
40483
40918
|
try {
|
|
40484
|
-
handleBridgeMessage(data, messageDeps);
|
|
40919
|
+
handleBridgeMessage(data, messageDeps, sourceWs);
|
|
40485
40920
|
} catch {
|
|
40486
40921
|
}
|
|
40487
40922
|
}
|
|
@@ -40649,10 +41084,10 @@ function listCliAgentCapabilityCacheForWorkspace(db, workspaceId) {
|
|
|
40649
41084
|
}
|
|
40650
41085
|
|
|
40651
41086
|
// src/agents/capabilities/warmup-agent-capabilities-on-connect.ts
|
|
40652
|
-
import * as
|
|
41087
|
+
import * as path43 from "node:path";
|
|
40653
41088
|
|
|
40654
41089
|
// src/agents/capabilities/probe-one-agent-type-for-capabilities.ts
|
|
40655
|
-
import * as
|
|
41090
|
+
import * as path42 from "node:path";
|
|
40656
41091
|
async function probeOneAgentTypeForCapabilities(params) {
|
|
40657
41092
|
const { agentType, cwd, workspaceId, log: log2, reportAgentCapabilities, bridgeReport = true } = params;
|
|
40658
41093
|
if (isCliImmediateShutdownRequested()) return false;
|
|
@@ -40692,7 +41127,7 @@ async function probeOneAgentTypeForCapabilities(params) {
|
|
|
40692
41127
|
if (isCliImmediateShutdownRequested()) return false;
|
|
40693
41128
|
handle = await resolved.createClient({
|
|
40694
41129
|
command: resolved.command,
|
|
40695
|
-
cwd:
|
|
41130
|
+
cwd: path42.resolve(cwd),
|
|
40696
41131
|
backendAgentType: agentType,
|
|
40697
41132
|
sessionMode: "agent",
|
|
40698
41133
|
persistedAcpSessionId: null,
|
|
@@ -40766,7 +41201,7 @@ async function probeAgentCapabilitiesForDetectedTypes(params) {
|
|
|
40766
41201
|
async function warmupAgentCapabilitiesOnConnect(params) {
|
|
40767
41202
|
const { workspaceId, log: log2, getWs } = params;
|
|
40768
41203
|
if (isCliImmediateShutdownRequested()) return;
|
|
40769
|
-
const cwd =
|
|
41204
|
+
const cwd = path43.resolve(getBridgeRoot());
|
|
40770
41205
|
async function sendBatchFromCache() {
|
|
40771
41206
|
const socket = getWs();
|
|
40772
41207
|
if (!socket || socket.readyState !== wrapper_default.OPEN) return;
|
|
@@ -40846,6 +41281,7 @@ async function createBridgeConnection(options) {
|
|
|
40846
41281
|
currentWs: null,
|
|
40847
41282
|
mainQuiet: createEmptyReconnectQuietSlot(),
|
|
40848
41283
|
mainOutage: createEmptyReconnectOutageTracker(),
|
|
41284
|
+
duplicateBridgeFlap: createEmptyDuplicateBridgeFlapTracker(),
|
|
40849
41285
|
firehoseHandle: null,
|
|
40850
41286
|
lastFirehoseParams: null,
|
|
40851
41287
|
firehoseReconnectTimeout: null,
|
|
@@ -40933,8 +41369,8 @@ async function createBridgeConnection(options) {
|
|
|
40933
41369
|
getCloudAccessToken: () => tokens.accessToken
|
|
40934
41370
|
};
|
|
40935
41371
|
const identifyReportedPaths = {
|
|
40936
|
-
bridgeRootPath:
|
|
40937
|
-
worktreesRootPath:
|
|
41372
|
+
bridgeRootPath: path44.resolve(getBridgeRoot()),
|
|
41373
|
+
worktreesRootPath: path44.resolve(worktreesRootPath)
|
|
40938
41374
|
};
|
|
40939
41375
|
const { connect } = createMainBridgeWebSocketLifecycle({
|
|
40940
41376
|
state,
|
|
@@ -41188,7 +41624,7 @@ async function runCliAction(program2, opts) {
|
|
|
41188
41624
|
const firehoseServerUrl = opts.firehoseUrl ?? opts.proxyUrl ?? process.env.BUILDAUTOMATON_FIREHOSE_URL ?? process.env.BUILDAUTOMATON_PROXY_URL ?? DEFAULT_FIREHOSE_URL;
|
|
41189
41625
|
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);
|
|
41190
41626
|
if (bridgeRootOpt) {
|
|
41191
|
-
const resolvedBridgeRoot =
|
|
41627
|
+
const resolvedBridgeRoot = path45.resolve(process.cwd(), bridgeRootOpt);
|
|
41192
41628
|
try {
|
|
41193
41629
|
const st = fs40.statSync(resolvedBridgeRoot);
|
|
41194
41630
|
if (!st.isDirectory()) {
|
|
@@ -41210,7 +41646,7 @@ async function runCliAction(program2, opts) {
|
|
|
41210
41646
|
);
|
|
41211
41647
|
let worktreesRootPath;
|
|
41212
41648
|
if (opts.worktreesRoot && opts.worktreesRoot.trim()) {
|
|
41213
|
-
worktreesRootPath =
|
|
41649
|
+
worktreesRootPath = path45.resolve(opts.worktreesRoot.trim());
|
|
41214
41650
|
}
|
|
41215
41651
|
const e2eCertificates = opts.e2eeCertificatesDir?.trim() ? await loadOrCreateE2eCertificates(opts.e2eeCertificatesDir.trim()) : void 0;
|
|
41216
41652
|
if (e2eCertificates) {
|