@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/index.js
CHANGED
|
@@ -4065,8 +4065,8 @@ var init_parseUtil = __esm({
|
|
|
4065
4065
|
init_errors();
|
|
4066
4066
|
init_en();
|
|
4067
4067
|
makeIssue = (params) => {
|
|
4068
|
-
const { data, path:
|
|
4069
|
-
const fullPath = [...
|
|
4068
|
+
const { data, path: path44, errorMaps, issueData } = params;
|
|
4069
|
+
const fullPath = [...path44, ...issueData.path || []];
|
|
4070
4070
|
const fullIssue = {
|
|
4071
4071
|
...issueData,
|
|
4072
4072
|
path: fullPath
|
|
@@ -4374,11 +4374,11 @@ var init_types = __esm({
|
|
|
4374
4374
|
init_parseUtil();
|
|
4375
4375
|
init_util();
|
|
4376
4376
|
ParseInputLazyPath = class {
|
|
4377
|
-
constructor(parent, value,
|
|
4377
|
+
constructor(parent, value, path44, key) {
|
|
4378
4378
|
this._cachedPath = [];
|
|
4379
4379
|
this.parent = parent;
|
|
4380
4380
|
this.data = value;
|
|
4381
|
-
this._path =
|
|
4381
|
+
this._path = path44;
|
|
4382
4382
|
this._key = key;
|
|
4383
4383
|
}
|
|
4384
4384
|
get path() {
|
|
@@ -7993,10 +7993,10 @@ function assignProp(target, prop, value) {
|
|
|
7993
7993
|
configurable: true
|
|
7994
7994
|
});
|
|
7995
7995
|
}
|
|
7996
|
-
function getElementAtPath(obj,
|
|
7997
|
-
if (!
|
|
7996
|
+
function getElementAtPath(obj, path44) {
|
|
7997
|
+
if (!path44)
|
|
7998
7998
|
return obj;
|
|
7999
|
-
return
|
|
7999
|
+
return path44.reduce((acc, key) => acc?.[key], obj);
|
|
8000
8000
|
}
|
|
8001
8001
|
function promiseAllObject(promisesObj) {
|
|
8002
8002
|
const keys = Object.keys(promisesObj);
|
|
@@ -8245,11 +8245,11 @@ function aborted(x, startIndex = 0) {
|
|
|
8245
8245
|
}
|
|
8246
8246
|
return false;
|
|
8247
8247
|
}
|
|
8248
|
-
function prefixIssues(
|
|
8248
|
+
function prefixIssues(path44, issues) {
|
|
8249
8249
|
return issues.map((iss) => {
|
|
8250
8250
|
var _a2;
|
|
8251
8251
|
(_a2 = iss).path ?? (_a2.path = []);
|
|
8252
|
-
iss.path.unshift(
|
|
8252
|
+
iss.path.unshift(path44);
|
|
8253
8253
|
return iss;
|
|
8254
8254
|
});
|
|
8255
8255
|
}
|
|
@@ -8438,7 +8438,7 @@ function treeifyError(error40, _mapper) {
|
|
|
8438
8438
|
return issue2.message;
|
|
8439
8439
|
};
|
|
8440
8440
|
const result = { errors: [] };
|
|
8441
|
-
const processError = (error41,
|
|
8441
|
+
const processError = (error41, path44 = []) => {
|
|
8442
8442
|
var _a2, _b;
|
|
8443
8443
|
for (const issue2 of error41.issues) {
|
|
8444
8444
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
@@ -8448,7 +8448,7 @@ function treeifyError(error40, _mapper) {
|
|
|
8448
8448
|
} else if (issue2.code === "invalid_element") {
|
|
8449
8449
|
processError({ issues: issue2.issues }, issue2.path);
|
|
8450
8450
|
} else {
|
|
8451
|
-
const fullpath = [...
|
|
8451
|
+
const fullpath = [...path44, ...issue2.path];
|
|
8452
8452
|
if (fullpath.length === 0) {
|
|
8453
8453
|
result.errors.push(mapper(issue2));
|
|
8454
8454
|
continue;
|
|
@@ -8478,9 +8478,9 @@ function treeifyError(error40, _mapper) {
|
|
|
8478
8478
|
processError(error40);
|
|
8479
8479
|
return result;
|
|
8480
8480
|
}
|
|
8481
|
-
function toDotPath(
|
|
8481
|
+
function toDotPath(path44) {
|
|
8482
8482
|
const segs = [];
|
|
8483
|
-
for (const seg of
|
|
8483
|
+
for (const seg of path44) {
|
|
8484
8484
|
if (typeof seg === "number")
|
|
8485
8485
|
segs.push(`[${seg}]`);
|
|
8486
8486
|
else if (typeof seg === "symbol")
|
|
@@ -20943,8 +20943,8 @@ var init_acp = __esm({
|
|
|
20943
20943
|
this.#requestHandler = requestHandler;
|
|
20944
20944
|
this.#notificationHandler = notificationHandler;
|
|
20945
20945
|
this.#stream = stream;
|
|
20946
|
-
this.#closedPromise = new Promise((
|
|
20947
|
-
this.#abortController.signal.addEventListener("abort", () =>
|
|
20946
|
+
this.#closedPromise = new Promise((resolve20) => {
|
|
20947
|
+
this.#abortController.signal.addEventListener("abort", () => resolve20());
|
|
20948
20948
|
});
|
|
20949
20949
|
this.#receive();
|
|
20950
20950
|
}
|
|
@@ -21093,8 +21093,8 @@ var init_acp = __esm({
|
|
|
21093
21093
|
}
|
|
21094
21094
|
async sendRequest(method, params) {
|
|
21095
21095
|
const id = this.#nextRequestId++;
|
|
21096
|
-
const responsePromise = new Promise((
|
|
21097
|
-
this.#pendingResponses.set(id, { resolve:
|
|
21096
|
+
const responsePromise = new Promise((resolve20, reject) => {
|
|
21097
|
+
this.#pendingResponses.set(id, { resolve: resolve20, reject });
|
|
21098
21098
|
});
|
|
21099
21099
|
await this.#sendMessage({ jsonrpc: "2.0", id, method, params });
|
|
21100
21100
|
return responsePromise;
|
|
@@ -21963,10 +21963,10 @@ var require_src2 = __commonJS({
|
|
|
21963
21963
|
var fs_1 = __require("fs");
|
|
21964
21964
|
var debug_1 = __importDefault(require_src());
|
|
21965
21965
|
var log2 = debug_1.default("@kwsites/file-exists");
|
|
21966
|
-
function check2(
|
|
21967
|
-
log2(`checking %s`,
|
|
21966
|
+
function check2(path44, isFile, isDirectory) {
|
|
21967
|
+
log2(`checking %s`, path44);
|
|
21968
21968
|
try {
|
|
21969
|
-
const stat2 = fs_1.statSync(
|
|
21969
|
+
const stat2 = fs_1.statSync(path44);
|
|
21970
21970
|
if (stat2.isFile() && isFile) {
|
|
21971
21971
|
log2(`[OK] path represents a file`);
|
|
21972
21972
|
return true;
|
|
@@ -21986,8 +21986,8 @@ var require_src2 = __commonJS({
|
|
|
21986
21986
|
throw e;
|
|
21987
21987
|
}
|
|
21988
21988
|
}
|
|
21989
|
-
function exists2(
|
|
21990
|
-
return check2(
|
|
21989
|
+
function exists2(path44, type = exports.READABLE) {
|
|
21990
|
+
return check2(path44, (type & exports.FILE) > 0, (type & exports.FOLDER) > 0);
|
|
21991
21991
|
}
|
|
21992
21992
|
exports.exists = exists2;
|
|
21993
21993
|
exports.FILE = 1;
|
|
@@ -22093,9 +22093,9 @@ function attachWebSocketClientPing(ws, intervalMs) {
|
|
|
22093
22093
|
return clear;
|
|
22094
22094
|
}
|
|
22095
22095
|
function buildCliWebSocketClientOptions(wsUrl) {
|
|
22096
|
-
const wsOptions = { perMessageDeflate: false
|
|
22096
|
+
const wsOptions = { perMessageDeflate: false };
|
|
22097
22097
|
if (wsUrl.startsWith("wss://")) {
|
|
22098
|
-
wsOptions.agent = new https.Agent({ rejectUnauthorized: false
|
|
22098
|
+
wsOptions.agent = new https.Agent({ rejectUnauthorized: false });
|
|
22099
22099
|
}
|
|
22100
22100
|
return wsOptions;
|
|
22101
22101
|
}
|
|
@@ -22130,11 +22130,47 @@ function safeSendWebSocketBinary(ws, data) {
|
|
|
22130
22130
|
}
|
|
22131
22131
|
}
|
|
22132
22132
|
|
|
22133
|
+
// src/connection/parse-compact-heartbeat-ack.ts
|
|
22134
|
+
function tryParseCompactHeartbeatAck(raw) {
|
|
22135
|
+
let str = null;
|
|
22136
|
+
if (typeof raw === "string") {
|
|
22137
|
+
str = raw;
|
|
22138
|
+
} else if (Buffer.isBuffer(raw)) {
|
|
22139
|
+
str = raw.toString("utf8");
|
|
22140
|
+
} else if (raw instanceof ArrayBuffer) {
|
|
22141
|
+
str = Buffer.from(raw).toString("utf8");
|
|
22142
|
+
} else {
|
|
22143
|
+
return null;
|
|
22144
|
+
}
|
|
22145
|
+
if (str.length > 64 || !str.includes('"ha"')) return null;
|
|
22146
|
+
try {
|
|
22147
|
+
const parsed = JSON.parse(str);
|
|
22148
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) return null;
|
|
22149
|
+
const o = parsed;
|
|
22150
|
+
if (o.t !== "ha") return null;
|
|
22151
|
+
const s = o.s;
|
|
22152
|
+
if (typeof s !== "number" || !Number.isFinite(s)) return null;
|
|
22153
|
+
return Math.trunc(s);
|
|
22154
|
+
} catch {
|
|
22155
|
+
return null;
|
|
22156
|
+
}
|
|
22157
|
+
}
|
|
22158
|
+
|
|
22133
22159
|
// src/connection/create-ws-bridge.ts
|
|
22134
22160
|
var BRIDGE_AUTH_ERROR_HEADER = "x-bridge-auth-error";
|
|
22135
22161
|
var BRIDGE_AUTH_ERROR_TOKEN_INVALID = "token_invalid";
|
|
22136
22162
|
function createWsBridge(options) {
|
|
22137
|
-
const {
|
|
22163
|
+
const {
|
|
22164
|
+
url: url2,
|
|
22165
|
+
onMessage,
|
|
22166
|
+
onOpen,
|
|
22167
|
+
onClose,
|
|
22168
|
+
onError: onError2,
|
|
22169
|
+
onAuthInvalid,
|
|
22170
|
+
clientPingIntervalMs,
|
|
22171
|
+
onCompactHeartbeatAck,
|
|
22172
|
+
isActiveSocket
|
|
22173
|
+
} = options;
|
|
22138
22174
|
applyCliOutboundNetworkPreferences();
|
|
22139
22175
|
const ws = new wrapper_default(url2, buildCliWebSocketClientOptions(url2));
|
|
22140
22176
|
let clearClientPing = null;
|
|
@@ -22158,7 +22194,13 @@ function createWsBridge(options) {
|
|
|
22158
22194
|
onOpen?.();
|
|
22159
22195
|
});
|
|
22160
22196
|
ws.on("message", (raw) => {
|
|
22197
|
+
const ackSeq = tryParseCompactHeartbeatAck(raw);
|
|
22198
|
+
if (ackSeq != null) {
|
|
22199
|
+
onCompactHeartbeatAck?.(ackSeq);
|
|
22200
|
+
return;
|
|
22201
|
+
}
|
|
22161
22202
|
setImmediate(() => {
|
|
22203
|
+
if (isActiveSocket && !isActiveSocket(ws)) return;
|
|
22162
22204
|
try {
|
|
22163
22205
|
let data;
|
|
22164
22206
|
if (typeof raw === "string") {
|
|
@@ -22169,9 +22211,9 @@ function createWsBridge(options) {
|
|
|
22169
22211
|
} else {
|
|
22170
22212
|
data = raw;
|
|
22171
22213
|
}
|
|
22172
|
-
onMessage?.(data);
|
|
22214
|
+
onMessage?.(data, ws);
|
|
22173
22215
|
} catch {
|
|
22174
|
-
onMessage?.(raw);
|
|
22216
|
+
onMessage?.(raw, ws);
|
|
22175
22217
|
}
|
|
22176
22218
|
});
|
|
22177
22219
|
});
|
|
@@ -22794,9 +22836,9 @@ function parseChangeSummaryJson(raw, allowedPaths, options) {
|
|
|
22794
22836
|
const rawPath = typeof o.path === "string" ? o.path.trim() : "";
|
|
22795
22837
|
const summary = typeof o.summary === "string" ? o.summary.trim() : "";
|
|
22796
22838
|
if (!rawPath || !summary) continue;
|
|
22797
|
-
const
|
|
22798
|
-
if (!
|
|
22799
|
-
rows.push({ path:
|
|
22839
|
+
const path44 = skip ? normalizeRepoRelativePath(rawPath) || rawPath : resolveChangeSummaryPathAgainstAllowed(rawPath, allowedPaths);
|
|
22840
|
+
if (!path44) continue;
|
|
22841
|
+
rows.push({ path: path44, summary: clampSummaryToAtMostTwoLines(summary) });
|
|
22800
22842
|
}
|
|
22801
22843
|
return rows;
|
|
22802
22844
|
}
|
|
@@ -23442,11 +23484,11 @@ async function sendAcpPromptViaTransport(transport, ctx, sessionId, promptText,
|
|
|
23442
23484
|
// src/agents/acp/clients/sdk/sdk-stdio-permission-request-handshake.ts
|
|
23443
23485
|
function awaitSdkStdioPermissionRequestHandshake(params) {
|
|
23444
23486
|
const { requestId, paramsRecord, pending, onRequest } = params;
|
|
23445
|
-
return new Promise((
|
|
23446
|
-
pending.set(requestId, { resolve:
|
|
23487
|
+
return new Promise((resolve20) => {
|
|
23488
|
+
pending.set(requestId, { resolve: resolve20, params: paramsRecord });
|
|
23447
23489
|
if (onRequest == null) {
|
|
23448
23490
|
pending.delete(requestId);
|
|
23449
|
-
|
|
23491
|
+
resolve20({ outcome: { outcome: "denied" } });
|
|
23450
23492
|
return;
|
|
23451
23493
|
}
|
|
23452
23494
|
try {
|
|
@@ -23512,7 +23554,7 @@ async function createSdkStdioAcpClient(options) {
|
|
|
23512
23554
|
child.once("close", (code, signal) => {
|
|
23513
23555
|
onAgentSubprocessExit?.({ code, signal });
|
|
23514
23556
|
});
|
|
23515
|
-
return new Promise((
|
|
23557
|
+
return new Promise((resolve20, reject) => {
|
|
23516
23558
|
let initSettled = false;
|
|
23517
23559
|
const settleReject = (err) => {
|
|
23518
23560
|
if (initSettled) return;
|
|
@@ -23526,7 +23568,7 @@ async function createSdkStdioAcpClient(options) {
|
|
|
23526
23568
|
const settleResolve = (handle) => {
|
|
23527
23569
|
if (initSettled) return;
|
|
23528
23570
|
initSettled = true;
|
|
23529
|
-
|
|
23571
|
+
resolve20(handle);
|
|
23530
23572
|
};
|
|
23531
23573
|
child.on("error", (err) => {
|
|
23532
23574
|
settleReject(new Error(formatSpawnError(err, command[0])));
|
|
@@ -23746,7 +23788,7 @@ async function proxyToLocal(request) {
|
|
|
23746
23788
|
};
|
|
23747
23789
|
const maxAttempts = isIdempotentProxyMethod(request.method) ? LOCAL_PREVIEW_FETCH_RETRY_DELAYS_MS.length + 1 : 1;
|
|
23748
23790
|
for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
|
|
23749
|
-
const once = await new Promise((
|
|
23791
|
+
const once = await new Promise((resolve20) => {
|
|
23750
23792
|
const req = mod.request(opts, (res) => {
|
|
23751
23793
|
const chunks = [];
|
|
23752
23794
|
res.on("data", (c) => chunks.push(c));
|
|
@@ -23757,7 +23799,7 @@ async function proxyToLocal(request) {
|
|
|
23757
23799
|
if (typeof v === "string") headers[k] = v;
|
|
23758
23800
|
else if (Array.isArray(v) && v[0]) headers[k] = v[0];
|
|
23759
23801
|
}
|
|
23760
|
-
|
|
23802
|
+
resolve20({
|
|
23761
23803
|
id: request.id,
|
|
23762
23804
|
statusCode: res.statusCode ?? 0,
|
|
23763
23805
|
headers,
|
|
@@ -23766,7 +23808,7 @@ async function proxyToLocal(request) {
|
|
|
23766
23808
|
});
|
|
23767
23809
|
});
|
|
23768
23810
|
req.on("error", (err) => {
|
|
23769
|
-
|
|
23811
|
+
resolve20({
|
|
23770
23812
|
id: request.id,
|
|
23771
23813
|
statusCode: 0,
|
|
23772
23814
|
headers: {},
|
|
@@ -23848,8 +23890,8 @@ function randomSecret() {
|
|
|
23848
23890
|
}
|
|
23849
23891
|
return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
23850
23892
|
}
|
|
23851
|
-
async function requestPreviewApi(port, secret, method,
|
|
23852
|
-
const url2 = `http://127.0.0.1:${port}${
|
|
23893
|
+
async function requestPreviewApi(port, secret, method, path44, body) {
|
|
23894
|
+
const url2 = `http://127.0.0.1:${port}${path44}`;
|
|
23853
23895
|
const headers = {
|
|
23854
23896
|
[PREVIEW_SECRET_HEADER]: secret,
|
|
23855
23897
|
"Content-Type": "application/json"
|
|
@@ -23861,7 +23903,7 @@ async function requestPreviewApi(port, secret, method, path41, body) {
|
|
|
23861
23903
|
});
|
|
23862
23904
|
const data = await res.json().catch(() => ({}));
|
|
23863
23905
|
if (!res.ok) {
|
|
23864
|
-
throw new Error(data?.error ?? `Preview API ${method} ${
|
|
23906
|
+
throw new Error(data?.error ?? `Preview API ${method} ${path44}: ${res.status}`);
|
|
23865
23907
|
}
|
|
23866
23908
|
return data;
|
|
23867
23909
|
}
|
|
@@ -24076,7 +24118,7 @@ function installBridgeProcessResilience() {
|
|
|
24076
24118
|
}
|
|
24077
24119
|
|
|
24078
24120
|
// src/cli-version.ts
|
|
24079
|
-
var CLI_VERSION = "0.1.
|
|
24121
|
+
var CLI_VERSION = "0.1.40".length > 0 ? "0.1.40" : "0.0.0-dev";
|
|
24080
24122
|
|
|
24081
24123
|
// src/connection/heartbeat/constants.ts
|
|
24082
24124
|
var BRIDGE_APP_HEARTBEAT_INTERVAL_MS = 1e4;
|
|
@@ -24517,14 +24559,14 @@ var baseOpen = async (options) => {
|
|
|
24517
24559
|
}
|
|
24518
24560
|
const subprocess = childProcess.spawn(command, cliArguments, childProcessOptions);
|
|
24519
24561
|
if (options.wait) {
|
|
24520
|
-
return new Promise((
|
|
24562
|
+
return new Promise((resolve20, reject) => {
|
|
24521
24563
|
subprocess.once("error", reject);
|
|
24522
24564
|
subprocess.once("close", (exitCode) => {
|
|
24523
24565
|
if (!options.allowNonzeroExitCode && exitCode > 0) {
|
|
24524
24566
|
reject(new Error(`Exited with code ${exitCode}`));
|
|
24525
24567
|
return;
|
|
24526
24568
|
}
|
|
24527
|
-
|
|
24569
|
+
resolve20(subprocess);
|
|
24528
24570
|
});
|
|
24529
24571
|
});
|
|
24530
24572
|
}
|
|
@@ -24965,6 +25007,61 @@ function scheduleMainBridgeReconnect(state, connect, log2, closeMeta) {
|
|
|
24965
25007
|
});
|
|
24966
25008
|
}
|
|
24967
25009
|
|
|
25010
|
+
// src/connection/reconnect/duplicate-bridge-connection-detect.ts
|
|
25011
|
+
var BRIDGE_SUPERSEDED_CLOSE_REASON_SNIPPET = "superseded";
|
|
25012
|
+
var DUPLICATE_BRIDGE_SHORT_SESSION_MS = 25e3;
|
|
25013
|
+
var DUPLICATE_BRIDGE_FLAP_MIN_COUNT = 2;
|
|
25014
|
+
var DUPLICATE_BRIDGE_FLAP_WINDOW_MS = 9e4;
|
|
25015
|
+
var DUPLICATE_BRIDGE_STABLE_SESSION_MS = BRIDGE_APP_HEARTBEAT_INTERVAL_MS * BRIDGE_HEARTBEAT_MISSED_ACKS_BEFORE_RECONNECT + 5e3;
|
|
25016
|
+
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).";
|
|
25017
|
+
function createEmptyDuplicateBridgeFlapTracker() {
|
|
25018
|
+
return {
|
|
25019
|
+
sessionOpenedAtMs: null,
|
|
25020
|
+
shortDisconnectAtMs: []
|
|
25021
|
+
};
|
|
25022
|
+
}
|
|
25023
|
+
function isSupersededByNewBridgeClose(code, reason) {
|
|
25024
|
+
if (code !== 1e3) return false;
|
|
25025
|
+
return reason.toLowerCase().includes(BRIDGE_SUPERSEDED_CLOSE_REASON_SNIPPET);
|
|
25026
|
+
}
|
|
25027
|
+
function pruneShortDisconnects(tracker, nowMs) {
|
|
25028
|
+
const cutoff = nowMs - DUPLICATE_BRIDGE_FLAP_WINDOW_MS;
|
|
25029
|
+
tracker.shortDisconnectAtMs = tracker.shortDisconnectAtMs.filter((t) => t >= cutoff);
|
|
25030
|
+
}
|
|
25031
|
+
function recordBridgeSessionOpened(tracker, nowMs) {
|
|
25032
|
+
tracker.sessionOpenedAtMs = nowMs;
|
|
25033
|
+
}
|
|
25034
|
+
function evaluateDuplicateBridgeDisconnect(tracker, nowMs, code, reason) {
|
|
25035
|
+
if (isSupersededByNewBridgeClose(code, reason)) {
|
|
25036
|
+
return { kind: "warn", trigger: "superseded_close" };
|
|
25037
|
+
}
|
|
25038
|
+
const openedAt = tracker.sessionOpenedAtMs;
|
|
25039
|
+
tracker.sessionOpenedAtMs = null;
|
|
25040
|
+
if (openedAt == null) {
|
|
25041
|
+
return { kind: "none" };
|
|
25042
|
+
}
|
|
25043
|
+
const sessionMs = nowMs - openedAt;
|
|
25044
|
+
if (sessionMs >= DUPLICATE_BRIDGE_STABLE_SESSION_MS) {
|
|
25045
|
+
tracker.shortDisconnectAtMs = [];
|
|
25046
|
+
return { kind: "none" };
|
|
25047
|
+
}
|
|
25048
|
+
if (sessionMs < DUPLICATE_BRIDGE_SHORT_SESSION_MS) {
|
|
25049
|
+
pruneShortDisconnects(tracker, nowMs);
|
|
25050
|
+
tracker.shortDisconnectAtMs.push(nowMs);
|
|
25051
|
+
if (tracker.shortDisconnectAtMs.length >= DUPLICATE_BRIDGE_FLAP_MIN_COUNT) {
|
|
25052
|
+
return { kind: "warn", trigger: "rapid_short_disconnects" };
|
|
25053
|
+
}
|
|
25054
|
+
}
|
|
25055
|
+
return { kind: "none" };
|
|
25056
|
+
}
|
|
25057
|
+
function logDuplicateBridgeWarning(log2, result) {
|
|
25058
|
+
if (result.kind !== "warn") return;
|
|
25059
|
+
try {
|
|
25060
|
+
log2(DUPLICATE_BRIDGE_WARNING_MESSAGE);
|
|
25061
|
+
} catch {
|
|
25062
|
+
}
|
|
25063
|
+
}
|
|
25064
|
+
|
|
24968
25065
|
// src/connection/reconnect/firehose-reconnect.ts
|
|
24969
25066
|
function beginFirehoseDeferredDisconnect(ctx, code, reason, log2) {
|
|
24970
25067
|
beginTieredSilentReconnectDisconnect({
|
|
@@ -25013,8 +25110,8 @@ function runPendingAuth(options) {
|
|
|
25013
25110
|
let hasOpenedBrowser = false;
|
|
25014
25111
|
let resolved = false;
|
|
25015
25112
|
let resolveAuth;
|
|
25016
|
-
const authPromise = new Promise((
|
|
25017
|
-
resolveAuth =
|
|
25113
|
+
const authPromise = new Promise((resolve20) => {
|
|
25114
|
+
resolveAuth = resolve20;
|
|
25018
25115
|
});
|
|
25019
25116
|
let reconnectAttempt = 0;
|
|
25020
25117
|
const signInQuiet = createEmptyReconnectQuietSlot();
|
|
@@ -25165,7 +25262,7 @@ import sqliteWasm from "node-sqlite3-wasm";
|
|
|
25165
25262
|
|
|
25166
25263
|
// src/runtime/yield-to-event-loop.ts
|
|
25167
25264
|
function yieldToEventLoop() {
|
|
25168
|
-
return new Promise((
|
|
25265
|
+
return new Promise((resolve20) => setImmediate(resolve20));
|
|
25169
25266
|
}
|
|
25170
25267
|
|
|
25171
25268
|
// src/sqlite/cli-sqlite-paths.ts
|
|
@@ -25730,8 +25827,8 @@ function pathspec(...paths) {
|
|
|
25730
25827
|
cache.set(key, paths);
|
|
25731
25828
|
return key;
|
|
25732
25829
|
}
|
|
25733
|
-
function isPathSpec(
|
|
25734
|
-
return
|
|
25830
|
+
function isPathSpec(path44) {
|
|
25831
|
+
return path44 instanceof String && cache.has(path44);
|
|
25735
25832
|
}
|
|
25736
25833
|
function toPaths(pathSpec) {
|
|
25737
25834
|
return cache.get(pathSpec) || [];
|
|
@@ -25820,8 +25917,8 @@ function toLinesWithContent(input = "", trimmed2 = true, separator = "\n") {
|
|
|
25820
25917
|
function forEachLineWithContent(input, callback) {
|
|
25821
25918
|
return toLinesWithContent(input, true).map((line) => callback(line));
|
|
25822
25919
|
}
|
|
25823
|
-
function folderExists(
|
|
25824
|
-
return (0, import_file_exists.exists)(
|
|
25920
|
+
function folderExists(path44) {
|
|
25921
|
+
return (0, import_file_exists.exists)(path44, import_file_exists.FOLDER);
|
|
25825
25922
|
}
|
|
25826
25923
|
function append(target, item) {
|
|
25827
25924
|
if (Array.isArray(target)) {
|
|
@@ -26225,8 +26322,8 @@ function checkIsRepoRootTask() {
|
|
|
26225
26322
|
commands,
|
|
26226
26323
|
format: "utf-8",
|
|
26227
26324
|
onError,
|
|
26228
|
-
parser(
|
|
26229
|
-
return /^\.(git)?$/.test(
|
|
26325
|
+
parser(path44) {
|
|
26326
|
+
return /^\.(git)?$/.test(path44.trim());
|
|
26230
26327
|
}
|
|
26231
26328
|
};
|
|
26232
26329
|
}
|
|
@@ -26660,11 +26757,11 @@ function parseGrep(grep) {
|
|
|
26660
26757
|
const paths = /* @__PURE__ */ new Set();
|
|
26661
26758
|
const results = {};
|
|
26662
26759
|
forEachLineWithContent(grep, (input) => {
|
|
26663
|
-
const [
|
|
26664
|
-
paths.add(
|
|
26665
|
-
(results[
|
|
26760
|
+
const [path44, line, preview] = input.split(NULL);
|
|
26761
|
+
paths.add(path44);
|
|
26762
|
+
(results[path44] = results[path44] || []).push({
|
|
26666
26763
|
line: asNumber(line),
|
|
26667
|
-
path:
|
|
26764
|
+
path: path44,
|
|
26668
26765
|
preview
|
|
26669
26766
|
});
|
|
26670
26767
|
});
|
|
@@ -27429,14 +27526,14 @@ var init_hash_object = __esm2({
|
|
|
27429
27526
|
init_task();
|
|
27430
27527
|
}
|
|
27431
27528
|
});
|
|
27432
|
-
function parseInit(bare,
|
|
27529
|
+
function parseInit(bare, path44, text) {
|
|
27433
27530
|
const response = String(text).trim();
|
|
27434
27531
|
let result;
|
|
27435
27532
|
if (result = initResponseRegex.exec(response)) {
|
|
27436
|
-
return new InitSummary(bare,
|
|
27533
|
+
return new InitSummary(bare, path44, false, result[1]);
|
|
27437
27534
|
}
|
|
27438
27535
|
if (result = reInitResponseRegex.exec(response)) {
|
|
27439
|
-
return new InitSummary(bare,
|
|
27536
|
+
return new InitSummary(bare, path44, true, result[1]);
|
|
27440
27537
|
}
|
|
27441
27538
|
let gitDir = "";
|
|
27442
27539
|
const tokens = response.split(" ");
|
|
@@ -27447,7 +27544,7 @@ function parseInit(bare, path41, text) {
|
|
|
27447
27544
|
break;
|
|
27448
27545
|
}
|
|
27449
27546
|
}
|
|
27450
|
-
return new InitSummary(bare,
|
|
27547
|
+
return new InitSummary(bare, path44, /^re/i.test(response), gitDir);
|
|
27451
27548
|
}
|
|
27452
27549
|
var InitSummary;
|
|
27453
27550
|
var initResponseRegex;
|
|
@@ -27456,9 +27553,9 @@ var init_InitSummary = __esm2({
|
|
|
27456
27553
|
"src/lib/responses/InitSummary.ts"() {
|
|
27457
27554
|
"use strict";
|
|
27458
27555
|
InitSummary = class {
|
|
27459
|
-
constructor(bare,
|
|
27556
|
+
constructor(bare, path44, existing, gitDir) {
|
|
27460
27557
|
this.bare = bare;
|
|
27461
|
-
this.path =
|
|
27558
|
+
this.path = path44;
|
|
27462
27559
|
this.existing = existing;
|
|
27463
27560
|
this.gitDir = gitDir;
|
|
27464
27561
|
}
|
|
@@ -27470,7 +27567,7 @@ var init_InitSummary = __esm2({
|
|
|
27470
27567
|
function hasBareCommand(command) {
|
|
27471
27568
|
return command.includes(bareCommand);
|
|
27472
27569
|
}
|
|
27473
|
-
function initTask(bare = false,
|
|
27570
|
+
function initTask(bare = false, path44, customArgs) {
|
|
27474
27571
|
const commands = ["init", ...customArgs];
|
|
27475
27572
|
if (bare && !hasBareCommand(commands)) {
|
|
27476
27573
|
commands.splice(1, 0, bareCommand);
|
|
@@ -27479,7 +27576,7 @@ function initTask(bare = false, path41, customArgs) {
|
|
|
27479
27576
|
commands,
|
|
27480
27577
|
format: "utf-8",
|
|
27481
27578
|
parser(text) {
|
|
27482
|
-
return parseInit(commands.includes("--bare"),
|
|
27579
|
+
return parseInit(commands.includes("--bare"), path44, text);
|
|
27483
27580
|
}
|
|
27484
27581
|
};
|
|
27485
27582
|
}
|
|
@@ -28295,12 +28392,12 @@ var init_FileStatusSummary = __esm2({
|
|
|
28295
28392
|
"use strict";
|
|
28296
28393
|
fromPathRegex = /^(.+)\0(.+)$/;
|
|
28297
28394
|
FileStatusSummary = class {
|
|
28298
|
-
constructor(
|
|
28299
|
-
this.path =
|
|
28395
|
+
constructor(path44, index, working_dir) {
|
|
28396
|
+
this.path = path44;
|
|
28300
28397
|
this.index = index;
|
|
28301
28398
|
this.working_dir = working_dir;
|
|
28302
28399
|
if (index === "R" || working_dir === "R") {
|
|
28303
|
-
const detail = fromPathRegex.exec(
|
|
28400
|
+
const detail = fromPathRegex.exec(path44) || [null, path44, path44];
|
|
28304
28401
|
this.from = detail[2] || "";
|
|
28305
28402
|
this.path = detail[1] || "";
|
|
28306
28403
|
}
|
|
@@ -28331,14 +28428,14 @@ function splitLine(result, lineStr) {
|
|
|
28331
28428
|
default:
|
|
28332
28429
|
return;
|
|
28333
28430
|
}
|
|
28334
|
-
function data(index, workingDir,
|
|
28431
|
+
function data(index, workingDir, path44) {
|
|
28335
28432
|
const raw = `${index}${workingDir}`;
|
|
28336
28433
|
const handler = parsers6.get(raw);
|
|
28337
28434
|
if (handler) {
|
|
28338
|
-
handler(result,
|
|
28435
|
+
handler(result, path44);
|
|
28339
28436
|
}
|
|
28340
28437
|
if (raw !== "##" && raw !== "!!") {
|
|
28341
|
-
result.files.push(new FileStatusSummary(
|
|
28438
|
+
result.files.push(new FileStatusSummary(path44, index, workingDir));
|
|
28342
28439
|
}
|
|
28343
28440
|
}
|
|
28344
28441
|
}
|
|
@@ -28647,9 +28744,9 @@ var init_simple_git_api = __esm2({
|
|
|
28647
28744
|
next
|
|
28648
28745
|
);
|
|
28649
28746
|
}
|
|
28650
|
-
hashObject(
|
|
28747
|
+
hashObject(path44, write) {
|
|
28651
28748
|
return this._runTask(
|
|
28652
|
-
hashObjectTask(
|
|
28749
|
+
hashObjectTask(path44, write === true),
|
|
28653
28750
|
trailingFunctionArgument(arguments)
|
|
28654
28751
|
);
|
|
28655
28752
|
}
|
|
@@ -29002,8 +29099,8 @@ var init_branch = __esm2({
|
|
|
29002
29099
|
}
|
|
29003
29100
|
});
|
|
29004
29101
|
function toPath(input) {
|
|
29005
|
-
const
|
|
29006
|
-
return
|
|
29102
|
+
const path44 = input.trim().replace(/^["']|["']$/g, "");
|
|
29103
|
+
return path44 && normalize2(path44);
|
|
29007
29104
|
}
|
|
29008
29105
|
var parseCheckIgnore;
|
|
29009
29106
|
var init_CheckIgnore = __esm2({
|
|
@@ -29317,8 +29414,8 @@ __export2(sub_module_exports, {
|
|
|
29317
29414
|
subModuleTask: () => subModuleTask,
|
|
29318
29415
|
updateSubModuleTask: () => updateSubModuleTask
|
|
29319
29416
|
});
|
|
29320
|
-
function addSubModuleTask(repo,
|
|
29321
|
-
return subModuleTask(["add", repo,
|
|
29417
|
+
function addSubModuleTask(repo, path44) {
|
|
29418
|
+
return subModuleTask(["add", repo, path44]);
|
|
29322
29419
|
}
|
|
29323
29420
|
function initSubModuleTask(customArgs) {
|
|
29324
29421
|
return subModuleTask(["init", ...customArgs]);
|
|
@@ -29651,8 +29748,8 @@ var require_git = __commonJS2({
|
|
|
29651
29748
|
}
|
|
29652
29749
|
return this._runTask(straightThroughStringTask2(command, this._trimmed), next);
|
|
29653
29750
|
};
|
|
29654
|
-
Git2.prototype.submoduleAdd = function(repo,
|
|
29655
|
-
return this._runTask(addSubModuleTask2(repo,
|
|
29751
|
+
Git2.prototype.submoduleAdd = function(repo, path44, then) {
|
|
29752
|
+
return this._runTask(addSubModuleTask2(repo, path44), trailingFunctionArgument2(arguments));
|
|
29656
29753
|
};
|
|
29657
29754
|
Git2.prototype.submoduleUpdate = function(args, then) {
|
|
29658
29755
|
return this._runTask(
|
|
@@ -30289,8 +30386,8 @@ async function runGitTask(fn) {
|
|
|
30289
30386
|
}
|
|
30290
30387
|
async function yieldToEventLoop2() {
|
|
30291
30388
|
throwIfGitShutdownRequested();
|
|
30292
|
-
await new Promise((
|
|
30293
|
-
setImmediate(
|
|
30389
|
+
await new Promise((resolve20) => {
|
|
30390
|
+
setImmediate(resolve20);
|
|
30294
30391
|
});
|
|
30295
30392
|
throwIfGitShutdownRequested();
|
|
30296
30393
|
}
|
|
@@ -30615,9 +30712,9 @@ async function collectTurnGitDiffFromPreTurnSnapshot(options) {
|
|
|
30615
30712
|
// src/agents/acp/put-summarize-change-summaries.ts
|
|
30616
30713
|
async function putEncryptedChangeSummaryRows(params) {
|
|
30617
30714
|
const base = params.apiBaseUrl.replace(/\/+$/, "");
|
|
30618
|
-
const entries = params.rows.map(({ path:
|
|
30715
|
+
const entries = params.rows.map(({ path: path44, summary }) => {
|
|
30619
30716
|
const enc = params.e2ee.encryptFields({ summary }, ["summary"]);
|
|
30620
|
-
return { path:
|
|
30717
|
+
return { path: path44, summary: JSON.stringify(enc) };
|
|
30621
30718
|
});
|
|
30622
30719
|
const res = await fetch(
|
|
30623
30720
|
`${base}/api/sessions/${encodeURIComponent(params.sessionId)}/follow-ups/summarize-changes`,
|
|
@@ -31171,7 +31268,7 @@ async function createCursorAcpClient(options) {
|
|
|
31171
31268
|
logDebug,
|
|
31172
31269
|
getStderrText: () => stderrCapture.getText()
|
|
31173
31270
|
};
|
|
31174
|
-
return new Promise((
|
|
31271
|
+
return new Promise((resolve20, reject) => {
|
|
31175
31272
|
child.on("error", (err) => {
|
|
31176
31273
|
child.kill();
|
|
31177
31274
|
reject(new Error(formatSpawnError2(err, command[0])));
|
|
@@ -31347,7 +31444,7 @@ async function createCursorAcpClient(options) {
|
|
|
31347
31444
|
clientInfo: { name: "buildautomaton-cli", version: "0.1.0" }
|
|
31348
31445
|
});
|
|
31349
31446
|
const sessionId = established.sessionId;
|
|
31350
|
-
|
|
31447
|
+
resolve20({
|
|
31351
31448
|
sessionId,
|
|
31352
31449
|
async sendPrompt(prompt, options2) {
|
|
31353
31450
|
const imgs = options2?.images?.map((im) => ({ type: "image", mimeType: im.mimeType, data: im.dataBase64 }));
|
|
@@ -32593,266 +32690,65 @@ async function createAcpManager(options) {
|
|
|
32593
32690
|
};
|
|
32594
32691
|
}
|
|
32595
32692
|
|
|
32596
|
-
// src/
|
|
32597
|
-
|
|
32598
|
-
import os8 from "node:os";
|
|
32599
|
-
|
|
32600
|
-
// src/worktrees/prepare-new-session-worktrees.ts
|
|
32601
|
-
import * as fs17 from "node:fs";
|
|
32602
|
-
import * as path20 from "node:path";
|
|
32603
|
-
|
|
32604
|
-
// src/git/worktrees/worktree-add.ts
|
|
32605
|
-
async function gitWorktreeAddBranch(mainRepoPath, worktreePath, branch) {
|
|
32606
|
-
const mainGit = cliSimpleGit(mainRepoPath);
|
|
32607
|
-
await mainGit.raw(["worktree", "add", "-b", branch, worktreePath, "HEAD"]);
|
|
32608
|
-
}
|
|
32609
|
-
|
|
32610
|
-
// src/worktrees/worktree-layout-file.ts
|
|
32611
|
-
import * as fs16 from "node:fs";
|
|
32612
|
-
import * as path19 from "node:path";
|
|
32613
|
-
import os7 from "node:os";
|
|
32614
|
-
var LAYOUT_FILENAME = "worktree-launcher-layout.json";
|
|
32615
|
-
function defaultWorktreeLayoutPath() {
|
|
32616
|
-
return path19.join(os7.homedir(), ".buildautomaton", LAYOUT_FILENAME);
|
|
32617
|
-
}
|
|
32618
|
-
function normalizeLoadedLayout(raw) {
|
|
32619
|
-
if (raw && typeof raw === "object" && "launcherCwds" in raw) {
|
|
32620
|
-
const j = raw;
|
|
32621
|
-
if (Array.isArray(j.launcherCwds)) return { launcherCwds: j.launcherCwds };
|
|
32622
|
-
}
|
|
32623
|
-
return { launcherCwds: [] };
|
|
32624
|
-
}
|
|
32625
|
-
function loadWorktreeLayout() {
|
|
32626
|
-
try {
|
|
32627
|
-
const p = defaultWorktreeLayoutPath();
|
|
32628
|
-
if (!fs16.existsSync(p)) return { launcherCwds: [] };
|
|
32629
|
-
const raw = JSON.parse(fs16.readFileSync(p, "utf8"));
|
|
32630
|
-
return normalizeLoadedLayout(raw);
|
|
32631
|
-
} catch {
|
|
32632
|
-
return { launcherCwds: [] };
|
|
32633
|
-
}
|
|
32634
|
-
}
|
|
32635
|
-
function saveWorktreeLayout(layout) {
|
|
32636
|
-
try {
|
|
32637
|
-
const dir = path19.dirname(defaultWorktreeLayoutPath());
|
|
32638
|
-
fs16.mkdirSync(dir, { recursive: true });
|
|
32639
|
-
fs16.writeFileSync(defaultWorktreeLayoutPath(), JSON.stringify(layout, null, 2), "utf8");
|
|
32640
|
-
} catch {
|
|
32641
|
-
}
|
|
32642
|
-
}
|
|
32643
|
-
function baseNameSafe(pathString) {
|
|
32644
|
-
return path19.basename(pathString).replace(/[^a-zA-Z0-9._-]+/g, "-") || "cwd";
|
|
32645
|
-
}
|
|
32646
|
-
function getLauncherDirNameIfPresent(layout, bridgeRootPath2) {
|
|
32647
|
-
const norm = path19.resolve(bridgeRootPath2);
|
|
32648
|
-
const existing = layout.launcherCwds.find((e) => path19.resolve(e.absolutePath) === norm);
|
|
32649
|
-
return existing?.dirName;
|
|
32650
|
-
}
|
|
32651
|
-
function allocateDirNameForLauncherCwd(layout, bridgeRootPath2) {
|
|
32652
|
-
const existing = getLauncherDirNameIfPresent(layout, bridgeRootPath2);
|
|
32653
|
-
if (existing) return existing;
|
|
32654
|
-
const norm = path19.resolve(bridgeRootPath2);
|
|
32655
|
-
const base = baseNameSafe(norm);
|
|
32656
|
-
const used = new Set(layout.launcherCwds.map((e) => e.dirName));
|
|
32657
|
-
let name = base;
|
|
32658
|
-
let n = 2;
|
|
32659
|
-
while (used.has(name)) {
|
|
32660
|
-
name = `${base}-${n}`;
|
|
32661
|
-
n += 1;
|
|
32662
|
-
}
|
|
32663
|
-
layout.launcherCwds.push({ absolutePath: norm, dirName: name });
|
|
32664
|
-
saveWorktreeLayout(layout);
|
|
32665
|
-
return name;
|
|
32666
|
-
}
|
|
32667
|
-
|
|
32668
|
-
// src/worktrees/prepare-new-session-worktrees.ts
|
|
32669
|
-
async function prepareNewSessionWorktrees(options) {
|
|
32670
|
-
const { worktreesRootPath, bridgeRoot, sessionId, layout, log: log2 } = options;
|
|
32671
|
-
const bridgeResolved = path20.resolve(bridgeRoot);
|
|
32672
|
-
const cwdKey = allocateDirNameForLauncherCwd(layout, bridgeResolved);
|
|
32673
|
-
const bridgeKeyDir = path20.join(worktreesRootPath, cwdKey);
|
|
32674
|
-
const sessionDir = path20.join(bridgeKeyDir, sessionId);
|
|
32675
|
-
const repos = await discoverGitReposUnderRoot(bridgeResolved);
|
|
32676
|
-
if (repos.length === 0) {
|
|
32677
|
-
log2("[worktrees] No Git repositories under bridge root; skipping worktree creation.");
|
|
32678
|
-
return null;
|
|
32679
|
-
}
|
|
32680
|
-
const branch = `session-${sessionId}`;
|
|
32681
|
-
const worktreePaths = [];
|
|
32682
|
-
fs17.mkdirSync(sessionDir, { recursive: true });
|
|
32683
|
-
for (const repo of repos) {
|
|
32684
|
-
let rel = path20.relative(bridgeResolved, repo.absolutePath);
|
|
32685
|
-
if (rel.startsWith("..") || path20.isAbsolute(rel)) continue;
|
|
32686
|
-
const relNorm = rel === "" ? "." : rel;
|
|
32687
|
-
const wtPath = relNorm === "." ? sessionDir : path20.join(sessionDir, relNorm);
|
|
32688
|
-
if (relNorm !== ".") {
|
|
32689
|
-
fs17.mkdirSync(path20.dirname(wtPath), { recursive: true });
|
|
32690
|
-
}
|
|
32691
|
-
try {
|
|
32692
|
-
await gitWorktreeAddBranch(repo.absolutePath, wtPath, branch);
|
|
32693
|
-
log2(`[worktrees] Added worktree ${wtPath} (branch ${branch}).`);
|
|
32694
|
-
worktreePaths.push(wtPath);
|
|
32695
|
-
} catch (e) {
|
|
32696
|
-
log2(
|
|
32697
|
-
`[worktrees] Worktree add failed for ${repo.absolutePath}: ${e instanceof Error ? e.message : String(e)}`
|
|
32698
|
-
);
|
|
32699
|
-
}
|
|
32700
|
-
}
|
|
32701
|
-
if (worktreePaths.length === 0) return null;
|
|
32702
|
-
return {
|
|
32703
|
-
worktreePaths,
|
|
32704
|
-
sessionParentPath: sessionDir,
|
|
32705
|
-
workingTreeRelRoot: sessionDir
|
|
32706
|
-
};
|
|
32707
|
-
}
|
|
32708
|
-
|
|
32709
|
-
// src/git/branches/rename-branch.ts
|
|
32710
|
-
async function gitRenameCurrentBranch(repoDir, newName) {
|
|
32711
|
-
const g = cliSimpleGit(repoDir);
|
|
32712
|
-
await g.raw(["branch", "-m", newName]);
|
|
32713
|
-
}
|
|
32714
|
-
|
|
32715
|
-
// src/worktrees/rename-session-worktree-branches.ts
|
|
32716
|
-
async function renameSessionWorktreeBranches(paths, newBranch, log2) {
|
|
32717
|
-
const safe = newBranch.replace(/[^a-zA-Z0-9/_-]+/g, "-").slice(0, 80) || "session-branch";
|
|
32718
|
-
for (const wt of paths) {
|
|
32719
|
-
try {
|
|
32720
|
-
await gitRenameCurrentBranch(wt, safe);
|
|
32721
|
-
log2(`[worktrees] Renamed branch in ${wt} \u2192 ${safe}`);
|
|
32722
|
-
} catch (e) {
|
|
32723
|
-
log2(
|
|
32724
|
-
`[worktrees] Branch rename failed in ${wt}: ${e instanceof Error ? e.message : String(e)}`
|
|
32725
|
-
);
|
|
32726
|
-
}
|
|
32727
|
-
}
|
|
32728
|
-
}
|
|
32729
|
-
|
|
32730
|
-
// src/worktrees/remove-session-worktrees.ts
|
|
32731
|
-
import * as fs20 from "node:fs";
|
|
32732
|
-
|
|
32733
|
-
// src/git/worktrees/worktree-remove.ts
|
|
32734
|
-
import * as fs19 from "node:fs";
|
|
32735
|
-
|
|
32736
|
-
// src/git/worktrees/resolve-main-repo-from-git-file.ts
|
|
32737
|
-
import * as fs18 from "node:fs";
|
|
32738
|
-
import * as path21 from "node:path";
|
|
32739
|
-
function resolveMainRepoFromWorktreeGitFile(wt) {
|
|
32740
|
-
const gitDirFile = path21.join(wt, ".git");
|
|
32741
|
-
if (!fs18.existsSync(gitDirFile) || !fs18.statSync(gitDirFile).isFile()) return "";
|
|
32742
|
-
const first2 = fs18.readFileSync(gitDirFile, "utf8").trim();
|
|
32743
|
-
const m = first2.match(/^gitdir:\s*(.+)$/im);
|
|
32744
|
-
if (!m) return "";
|
|
32745
|
-
const gitWorktreePath = path21.resolve(wt, m[1].trim());
|
|
32746
|
-
const gitDir = path21.dirname(path21.dirname(gitWorktreePath));
|
|
32747
|
-
return path21.dirname(gitDir);
|
|
32748
|
-
}
|
|
32693
|
+
// src/git/changes/types.ts
|
|
32694
|
+
var MAX_PATCH_CHARS = 35e4;
|
|
32749
32695
|
|
|
32750
|
-
// src/git/
|
|
32751
|
-
|
|
32752
|
-
const
|
|
32753
|
-
|
|
32754
|
-
|
|
32755
|
-
} else {
|
|
32756
|
-
fs19.rmSync(worktreePath, { recursive: true, force: true });
|
|
32757
|
-
}
|
|
32696
|
+
// src/git/changes/lib/repo-format.ts
|
|
32697
|
+
function posixJoinDirFile(dir, file2) {
|
|
32698
|
+
const d = dir === "." || dir === "" ? "" : dir.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
32699
|
+
const f = file2.replace(/\\/g, "/").replace(/^\/+/, "");
|
|
32700
|
+
return d ? `${d}/${f}` : f;
|
|
32758
32701
|
}
|
|
32759
|
-
|
|
32760
|
-
|
|
32761
|
-
|
|
32762
|
-
for (const wt of paths) {
|
|
32702
|
+
function formatRepoShortTitle(remoteUrl, repoRelPath) {
|
|
32703
|
+
const u = remoteUrl.trim();
|
|
32704
|
+
if (u) {
|
|
32763
32705
|
try {
|
|
32764
|
-
|
|
32765
|
-
|
|
32766
|
-
|
|
32767
|
-
|
|
32768
|
-
|
|
32769
|
-
|
|
32770
|
-
}
|
|
32706
|
+
if (u.startsWith("git@")) {
|
|
32707
|
+
const colon = u.indexOf(":");
|
|
32708
|
+
if (colon > 0) {
|
|
32709
|
+
const pathPart = u.slice(colon + 1).replace(/\.git$/i, "").replace(/\/+$/, "");
|
|
32710
|
+
if (pathPart.includes("/")) return pathPart;
|
|
32711
|
+
}
|
|
32712
|
+
} else {
|
|
32713
|
+
const parsed = new URL(u);
|
|
32714
|
+
const p = parsed.pathname.replace(/^\//, "").replace(/\.git$/i, "");
|
|
32715
|
+
const parts = p.split("/").filter(Boolean);
|
|
32716
|
+
if (parts.length >= 2) {
|
|
32717
|
+
return `${parts[parts.length - 2]}/${parts[parts.length - 1]}`;
|
|
32718
|
+
}
|
|
32719
|
+
if (parts.length === 1) return parts[0];
|
|
32771
32720
|
}
|
|
32721
|
+
} catch {
|
|
32772
32722
|
}
|
|
32773
32723
|
}
|
|
32774
|
-
|
|
32775
|
-
|
|
32776
|
-
|
|
32777
|
-
|
|
32778
|
-
const m = /* @__PURE__ */ new Map();
|
|
32779
|
-
for (const line of lines) {
|
|
32780
|
-
if (!line.trim()) continue;
|
|
32781
|
-
const tabParts = line.split(" ");
|
|
32782
|
-
if (tabParts.length < 2) continue;
|
|
32783
|
-
const status = tabParts[0].trim();
|
|
32784
|
-
const code = status[0];
|
|
32785
|
-
if (code === "A") {
|
|
32786
|
-
m.set(tabParts[tabParts.length - 1], "added");
|
|
32787
|
-
} else if (code === "D") {
|
|
32788
|
-
m.set(tabParts[tabParts.length - 1], "removed");
|
|
32789
|
-
} else if (code === "R" || code === "C") {
|
|
32790
|
-
if (tabParts.length >= 3) m.set(tabParts[tabParts.length - 1], "modified");
|
|
32791
|
-
} else if (code === "M" || code === "U" || code === "T") {
|
|
32792
|
-
m.set(tabParts[tabParts.length - 1], "modified");
|
|
32793
|
-
}
|
|
32794
|
-
}
|
|
32795
|
-
return m;
|
|
32796
|
-
}
|
|
32797
|
-
function parseNumstatFirstLine(line) {
|
|
32798
|
-
const parts = line.split(" ");
|
|
32799
|
-
if (parts.length < 3) return null;
|
|
32800
|
-
const [a, d] = parts;
|
|
32801
|
-
const additions = a === "-" ? 0 : parseInt(String(a), 10) || 0;
|
|
32802
|
-
const deletions = d === "-" ? 0 : parseInt(String(d), 10) || 0;
|
|
32803
|
-
return { additions, deletions };
|
|
32804
|
-
}
|
|
32805
|
-
function parseNumstat(lines) {
|
|
32806
|
-
const m = /* @__PURE__ */ new Map();
|
|
32807
|
-
for (const line of lines) {
|
|
32808
|
-
if (!line.trim()) continue;
|
|
32809
|
-
const parts = line.split(" ");
|
|
32810
|
-
if (parts.length < 3) continue;
|
|
32811
|
-
const [a, d, p] = parts;
|
|
32812
|
-
const additions = a === "-" ? 0 : parseInt(String(a), 10) || 0;
|
|
32813
|
-
const deletions = d === "-" ? 0 : parseInt(String(d), 10) || 0;
|
|
32814
|
-
m.set(p, { additions, deletions });
|
|
32724
|
+
if (repoRelPath && repoRelPath !== ".") {
|
|
32725
|
+
const segments = repoRelPath.split("/").filter(Boolean);
|
|
32726
|
+
const last2 = segments[segments.length - 1];
|
|
32727
|
+
if (last2) return last2;
|
|
32815
32728
|
}
|
|
32816
|
-
return
|
|
32729
|
+
return "Repository";
|
|
32817
32730
|
}
|
|
32818
|
-
|
|
32819
|
-
const
|
|
32731
|
+
function formatRemoteDisplayLabel(remoteUrl) {
|
|
32732
|
+
const u = remoteUrl.trim();
|
|
32733
|
+
if (!u) return "";
|
|
32734
|
+
let hostPath = u;
|
|
32820
32735
|
try {
|
|
32821
|
-
|
|
32822
|
-
|
|
32823
|
-
|
|
32736
|
+
if (u.startsWith("git@")) {
|
|
32737
|
+
const rest = u.slice("git@".length);
|
|
32738
|
+
const slash = rest.indexOf(":");
|
|
32739
|
+
if (slash > 0) hostPath = `${rest.slice(0, slash)}/${rest.slice(slash + 1)}`;
|
|
32740
|
+
} else {
|
|
32741
|
+
const parsed = new URL(u);
|
|
32742
|
+
hostPath = `${parsed.hostname}${parsed.pathname}`.replace(/\/\.git$/i, "").replace(/\.git$/i, "");
|
|
32743
|
+
}
|
|
32824
32744
|
} catch {
|
|
32825
|
-
|
|
32826
|
-
}
|
|
32827
|
-
}
|
|
32828
|
-
|
|
32829
|
-
// src/git/changes/lib/working-tree-changed-path-count.ts
|
|
32830
|
-
function workingTreeChangedPathCount(nameStatusLines, numstatLines, untrackedLines) {
|
|
32831
|
-
const kindByPath = parseNameStatusLines(nameStatusLines);
|
|
32832
|
-
const numByPath = parseNumstat(numstatLines);
|
|
32833
|
-
const paths = /* @__PURE__ */ new Set([...kindByPath.keys(), ...numByPath.keys()]);
|
|
32834
|
-
for (const p of untrackedLines.map((s) => s.trim()).filter(Boolean)) {
|
|
32835
|
-
paths.add(p);
|
|
32745
|
+
hostPath = u.replace(/^https?:\/\//i, "").replace(/\.git$/i, "");
|
|
32836
32746
|
}
|
|
32837
|
-
return
|
|
32747
|
+
return `origin \xB7 ${hostPath}`;
|
|
32838
32748
|
}
|
|
32839
32749
|
|
|
32840
|
-
// src/git/changes/
|
|
32841
|
-
|
|
32842
|
-
return runGitTask(async () => {
|
|
32843
|
-
const g = cliSimpleGit(repoGitCwd);
|
|
32844
|
-
const [nameStatusRaw, numstatRaw, untrackedRaw] = await Promise.all([
|
|
32845
|
-
g.raw(["diff", "--name-status", "HEAD"]).catch(() => ""),
|
|
32846
|
-
g.raw(["diff", "HEAD", "--numstat"]).catch(() => ""),
|
|
32847
|
-
g.raw(["ls-files", "--others", "--exclude-standard"]).catch(() => "")
|
|
32848
|
-
]);
|
|
32849
|
-
return workingTreeChangedPathCount(
|
|
32850
|
-
String(nameStatusRaw).split("\n"),
|
|
32851
|
-
String(numstatRaw).split("\n"),
|
|
32852
|
-
String(untrackedRaw).split("\n")
|
|
32853
|
-
);
|
|
32854
|
-
});
|
|
32855
|
-
}
|
|
32750
|
+
// src/git/changes/get-working-tree-change-repo-details.ts
|
|
32751
|
+
import * as path20 from "node:path";
|
|
32856
32752
|
|
|
32857
32753
|
// src/git/commits/resolve-remote-tracking.ts
|
|
32858
32754
|
async function tryConfigGet(g, key) {
|
|
@@ -32952,96 +32848,6 @@ async function commitsAheadOfRemoteTracking(repoDir) {
|
|
|
32952
32848
|
}
|
|
32953
32849
|
}
|
|
32954
32850
|
|
|
32955
|
-
// src/git/status/working-tree-status.ts
|
|
32956
|
-
async function getRepoWorkingTreeStatus(repoDir) {
|
|
32957
|
-
return runGitTask(async () => {
|
|
32958
|
-
const uncommittedFileCount = await countWorkingTreeChangedFilesForRepo(repoDir);
|
|
32959
|
-
const hasUncommittedChanges = uncommittedFileCount > 0;
|
|
32960
|
-
const ahead = await commitsAheadOfRemoteTracking(repoDir);
|
|
32961
|
-
return { hasUncommittedChanges, hasUnpushedCommits: ahead > 0, uncommittedFileCount };
|
|
32962
|
-
});
|
|
32963
|
-
}
|
|
32964
|
-
async function aggregateSessionPathsWorkingTreeStatus(paths) {
|
|
32965
|
-
let hasUncommittedChanges = false;
|
|
32966
|
-
let hasUnpushedCommits = false;
|
|
32967
|
-
let uncommittedFileCount = 0;
|
|
32968
|
-
await forEachWithGitYield(paths, async (p) => {
|
|
32969
|
-
const s = await getRepoWorkingTreeStatus(p);
|
|
32970
|
-
uncommittedFileCount += s.uncommittedFileCount;
|
|
32971
|
-
if (s.hasUncommittedChanges) hasUncommittedChanges = true;
|
|
32972
|
-
if (s.hasUnpushedCommits) hasUnpushedCommits = true;
|
|
32973
|
-
});
|
|
32974
|
-
return { hasUncommittedChanges, hasUnpushedCommits, uncommittedFileCount };
|
|
32975
|
-
}
|
|
32976
|
-
async function pushAheadOfUpstreamForPaths(paths) {
|
|
32977
|
-
await forEachWithGitYield(paths, async (p) => {
|
|
32978
|
-
const g = cliSimpleGit(p);
|
|
32979
|
-
const ahead = await commitsAheadOfRemoteTracking(p);
|
|
32980
|
-
if (ahead <= 0) return;
|
|
32981
|
-
await g.push();
|
|
32982
|
-
});
|
|
32983
|
-
}
|
|
32984
|
-
|
|
32985
|
-
// src/git/changes/types.ts
|
|
32986
|
-
var MAX_PATCH_CHARS = 35e4;
|
|
32987
|
-
|
|
32988
|
-
// src/git/changes/lib/repo-format.ts
|
|
32989
|
-
function posixJoinDirFile(dir, file2) {
|
|
32990
|
-
const d = dir === "." || dir === "" ? "" : dir.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
32991
|
-
const f = file2.replace(/\\/g, "/").replace(/^\/+/, "");
|
|
32992
|
-
return d ? `${d}/${f}` : f;
|
|
32993
|
-
}
|
|
32994
|
-
function formatRepoShortTitle(remoteUrl, repoRelPath) {
|
|
32995
|
-
const u = remoteUrl.trim();
|
|
32996
|
-
if (u) {
|
|
32997
|
-
try {
|
|
32998
|
-
if (u.startsWith("git@")) {
|
|
32999
|
-
const colon = u.indexOf(":");
|
|
33000
|
-
if (colon > 0) {
|
|
33001
|
-
const pathPart = u.slice(colon + 1).replace(/\.git$/i, "").replace(/\/+$/, "");
|
|
33002
|
-
if (pathPart.includes("/")) return pathPart;
|
|
33003
|
-
}
|
|
33004
|
-
} else {
|
|
33005
|
-
const parsed = new URL(u);
|
|
33006
|
-
const p = parsed.pathname.replace(/^\//, "").replace(/\.git$/i, "");
|
|
33007
|
-
const parts = p.split("/").filter(Boolean);
|
|
33008
|
-
if (parts.length >= 2) {
|
|
33009
|
-
return `${parts[parts.length - 2]}/${parts[parts.length - 1]}`;
|
|
33010
|
-
}
|
|
33011
|
-
if (parts.length === 1) return parts[0];
|
|
33012
|
-
}
|
|
33013
|
-
} catch {
|
|
33014
|
-
}
|
|
33015
|
-
}
|
|
33016
|
-
if (repoRelPath && repoRelPath !== ".") {
|
|
33017
|
-
const segments = repoRelPath.split("/").filter(Boolean);
|
|
33018
|
-
const last2 = segments[segments.length - 1];
|
|
33019
|
-
if (last2) return last2;
|
|
33020
|
-
}
|
|
33021
|
-
return "Repository";
|
|
33022
|
-
}
|
|
33023
|
-
function formatRemoteDisplayLabel(remoteUrl) {
|
|
33024
|
-
const u = remoteUrl.trim();
|
|
33025
|
-
if (!u) return "";
|
|
33026
|
-
let hostPath = u;
|
|
33027
|
-
try {
|
|
33028
|
-
if (u.startsWith("git@")) {
|
|
33029
|
-
const rest = u.slice("git@".length);
|
|
33030
|
-
const slash = rest.indexOf(":");
|
|
33031
|
-
if (slash > 0) hostPath = `${rest.slice(0, slash)}/${rest.slice(slash + 1)}`;
|
|
33032
|
-
} else {
|
|
33033
|
-
const parsed = new URL(u);
|
|
33034
|
-
hostPath = `${parsed.hostname}${parsed.pathname}`.replace(/\/\.git$/i, "").replace(/\.git$/i, "");
|
|
33035
|
-
}
|
|
33036
|
-
} catch {
|
|
33037
|
-
hostPath = u.replace(/^https?:\/\//i, "").replace(/\.git$/i, "");
|
|
33038
|
-
}
|
|
33039
|
-
return `origin \xB7 ${hostPath}`;
|
|
33040
|
-
}
|
|
33041
|
-
|
|
33042
|
-
// src/git/changes/get-working-tree-change-repo-details.ts
|
|
33043
|
-
import * as path23 from "node:path";
|
|
33044
|
-
|
|
33045
32851
|
// src/git/commits/lib/parse-log-lines.ts
|
|
33046
32852
|
function parseLogShaDateSubjectLines(raw) {
|
|
33047
32853
|
const out = [];
|
|
@@ -33112,6 +32918,59 @@ async function listRecentCommits(repoDir, limitInput) {
|
|
|
33112
32918
|
}
|
|
33113
32919
|
}
|
|
33114
32920
|
|
|
32921
|
+
// src/git/changes/lib/parse-git-status.ts
|
|
32922
|
+
function parseNameStatusLines(lines) {
|
|
32923
|
+
const m = /* @__PURE__ */ new Map();
|
|
32924
|
+
for (const line of lines) {
|
|
32925
|
+
if (!line.trim()) continue;
|
|
32926
|
+
const tabParts = line.split(" ");
|
|
32927
|
+
if (tabParts.length < 2) continue;
|
|
32928
|
+
const status = tabParts[0].trim();
|
|
32929
|
+
const code = status[0];
|
|
32930
|
+
if (code === "A") {
|
|
32931
|
+
m.set(tabParts[tabParts.length - 1], "added");
|
|
32932
|
+
} else if (code === "D") {
|
|
32933
|
+
m.set(tabParts[tabParts.length - 1], "removed");
|
|
32934
|
+
} else if (code === "R" || code === "C") {
|
|
32935
|
+
if (tabParts.length >= 3) m.set(tabParts[tabParts.length - 1], "modified");
|
|
32936
|
+
} else if (code === "M" || code === "U" || code === "T") {
|
|
32937
|
+
m.set(tabParts[tabParts.length - 1], "modified");
|
|
32938
|
+
}
|
|
32939
|
+
}
|
|
32940
|
+
return m;
|
|
32941
|
+
}
|
|
32942
|
+
function parseNumstatFirstLine(line) {
|
|
32943
|
+
const parts = line.split(" ");
|
|
32944
|
+
if (parts.length < 3) return null;
|
|
32945
|
+
const [a, d] = parts;
|
|
32946
|
+
const additions = a === "-" ? 0 : parseInt(String(a), 10) || 0;
|
|
32947
|
+
const deletions = d === "-" ? 0 : parseInt(String(d), 10) || 0;
|
|
32948
|
+
return { additions, deletions };
|
|
32949
|
+
}
|
|
32950
|
+
function parseNumstat(lines) {
|
|
32951
|
+
const m = /* @__PURE__ */ new Map();
|
|
32952
|
+
for (const line of lines) {
|
|
32953
|
+
if (!line.trim()) continue;
|
|
32954
|
+
const parts = line.split(" ");
|
|
32955
|
+
if (parts.length < 3) continue;
|
|
32956
|
+
const [a, d, p] = parts;
|
|
32957
|
+
const additions = a === "-" ? 0 : parseInt(String(a), 10) || 0;
|
|
32958
|
+
const deletions = d === "-" ? 0 : parseInt(String(d), 10) || 0;
|
|
32959
|
+
m.set(p, { additions, deletions });
|
|
32960
|
+
}
|
|
32961
|
+
return m;
|
|
32962
|
+
}
|
|
32963
|
+
async function numstatFromGitNoIndex(g, pathInRepo) {
|
|
32964
|
+
const devNull = process.platform === "win32" ? "NUL" : "/dev/null";
|
|
32965
|
+
try {
|
|
32966
|
+
const out = await g.raw(["diff", "--numstat", "--no-index", "--", devNull, pathInRepo]);
|
|
32967
|
+
const first2 = String(out).split("\n").find((l) => l.trim()) ?? "";
|
|
32968
|
+
return parseNumstatFirstLine(first2);
|
|
32969
|
+
} catch {
|
|
32970
|
+
return null;
|
|
32971
|
+
}
|
|
32972
|
+
}
|
|
32973
|
+
|
|
33115
32974
|
// src/git/changes/lib/patch-truncate.ts
|
|
33116
32975
|
function truncatePatch(s) {
|
|
33117
32976
|
if (s.length <= MAX_PATCH_CHARS) return s;
|
|
@@ -33177,8 +33036,8 @@ async function listChangedFilesForCommit(repoGitCwd, repoRelPath, commitSha) {
|
|
|
33177
33036
|
}
|
|
33178
33037
|
|
|
33179
33038
|
// src/git/changes/list-changed-files-for-repo.ts
|
|
33180
|
-
import * as
|
|
33181
|
-
import * as
|
|
33039
|
+
import * as fs17 from "node:fs";
|
|
33040
|
+
import * as path19 from "node:path";
|
|
33182
33041
|
|
|
33183
33042
|
// src/git/changes/lib/count-lines.ts
|
|
33184
33043
|
import { createReadStream } from "node:fs";
|
|
@@ -33202,7 +33061,7 @@ async function countTextFileLines(filePath) {
|
|
|
33202
33061
|
}
|
|
33203
33062
|
|
|
33204
33063
|
// src/git/changes/hydrate-patch.ts
|
|
33205
|
-
import * as
|
|
33064
|
+
import * as fs16 from "node:fs";
|
|
33206
33065
|
var UNIFIED_HUNK_HEADER_RE = /^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/;
|
|
33207
33066
|
var MAX_HYDRATE_LINES_PER_GAP = 8e3;
|
|
33208
33067
|
var MAX_HYDRATE_LINES_PER_FILE = 8e4;
|
|
@@ -33217,7 +33076,7 @@ async function readGitBlobLines(repoCwd, pathInRepo) {
|
|
|
33217
33076
|
}
|
|
33218
33077
|
async function readWorktreeFileLines(filePath) {
|
|
33219
33078
|
try {
|
|
33220
|
-
const raw = await
|
|
33079
|
+
const raw = await fs16.promises.readFile(filePath, "utf8");
|
|
33221
33080
|
return raw.split(/\r?\n/);
|
|
33222
33081
|
} catch {
|
|
33223
33082
|
return null;
|
|
@@ -33342,7 +33201,7 @@ async function listChangedFilesForRepo(repoGitCwd, repoRelPath) {
|
|
|
33342
33201
|
const rows = [];
|
|
33343
33202
|
await forEachWithGitYield([...paths], async (pathInRepo) => {
|
|
33344
33203
|
const relLauncher = posixJoinDirFile(repoRelPath, pathInRepo.replace(/\\/g, "/"));
|
|
33345
|
-
const repoFilePath =
|
|
33204
|
+
const repoFilePath = path19.join(repoGitCwd, pathInRepo);
|
|
33346
33205
|
const nums = numByPath.get(pathInRepo);
|
|
33347
33206
|
let additions = nums?.additions ?? 0;
|
|
33348
33207
|
let deletions = nums?.deletions ?? 0;
|
|
@@ -33355,7 +33214,7 @@ async function listChangedFilesForRepo(repoGitCwd, repoRelPath) {
|
|
|
33355
33214
|
deletions = fromGit.deletions;
|
|
33356
33215
|
} else {
|
|
33357
33216
|
try {
|
|
33358
|
-
const st = await
|
|
33217
|
+
const st = await fs17.promises.stat(repoFilePath);
|
|
33359
33218
|
if (st.isFile()) additions = await countTextFileLines(repoFilePath);
|
|
33360
33219
|
else additions = 0;
|
|
33361
33220
|
} catch {
|
|
@@ -33381,7 +33240,7 @@ async function listChangedFilesForRepo(repoGitCwd, repoRelPath) {
|
|
|
33381
33240
|
} else {
|
|
33382
33241
|
pathInRepo = row.pathRelLauncher;
|
|
33383
33242
|
}
|
|
33384
|
-
const filePath =
|
|
33243
|
+
const filePath = path19.join(repoGitCwd, pathInRepo);
|
|
33385
33244
|
let patch = await unifiedDiffForFile(repoGitCwd, pathInRepo, row.change);
|
|
33386
33245
|
if (patch) {
|
|
33387
33246
|
patch = await hydrateUnifiedPatchWithFileContext(patch, filePath, repoGitCwd, pathInRepo, row.change);
|
|
@@ -33397,8 +33256,8 @@ function normRepoRel(p) {
|
|
|
33397
33256
|
return x === "" ? "." : x;
|
|
33398
33257
|
}
|
|
33399
33258
|
async function getWorkingTreeChangeRepoDetails(options) {
|
|
33400
|
-
const bridgeRoot =
|
|
33401
|
-
const sessionWtRoot = options.sessionWorktreeRootPath ?
|
|
33259
|
+
const bridgeRoot = path20.resolve(getBridgeRoot());
|
|
33260
|
+
const sessionWtRoot = options.sessionWorktreeRootPath ? path20.resolve(options.sessionWorktreeRootPath) : null;
|
|
33402
33261
|
const legacyNested = options.legacyRepoNestedSessionLayout === true;
|
|
33403
33262
|
const out = [];
|
|
33404
33263
|
const filter = options.repoFilterRelPath != null ? normRepoRel(options.repoFilterRelPath) : null;
|
|
@@ -33413,7 +33272,7 @@ async function getWorkingTreeChangeRepoDetails(options) {
|
|
|
33413
33272
|
for (let i = 0; i < options.commitTargetPaths.length; i++) {
|
|
33414
33273
|
if (i > 0) await yieldToEventLoop2();
|
|
33415
33274
|
const target = options.commitTargetPaths[i];
|
|
33416
|
-
const t =
|
|
33275
|
+
const t = path20.resolve(target);
|
|
33417
33276
|
if (!await isGitRepoDirectory(t)) continue;
|
|
33418
33277
|
const g = cliSimpleGit(t);
|
|
33419
33278
|
let branch = "HEAD";
|
|
@@ -33426,8 +33285,8 @@ async function getWorkingTreeChangeRepoDetails(options) {
|
|
|
33426
33285
|
const remoteDisplay = formatRemoteDisplayLabel(remoteUrl);
|
|
33427
33286
|
let repoRelPath;
|
|
33428
33287
|
if (sessionWtRoot) {
|
|
33429
|
-
const anchor = legacyNested ?
|
|
33430
|
-
const relNorm =
|
|
33288
|
+
const anchor = legacyNested ? path20.dirname(t) : t;
|
|
33289
|
+
const relNorm = path20.relative(sessionWtRoot, anchor);
|
|
33431
33290
|
repoRelPath = relNorm === "" ? "." : relNorm.replace(/\\/g, "/");
|
|
33432
33291
|
} else {
|
|
33433
33292
|
let top = t;
|
|
@@ -33436,8 +33295,8 @@ async function getWorkingTreeChangeRepoDetails(options) {
|
|
|
33436
33295
|
} catch {
|
|
33437
33296
|
top = t;
|
|
33438
33297
|
}
|
|
33439
|
-
const rel =
|
|
33440
|
-
repoRelPath = rel.startsWith("..") ?
|
|
33298
|
+
const rel = path20.relative(bridgeRoot, path20.resolve(top)).replace(/\\/g, "/") || ".";
|
|
33299
|
+
repoRelPath = rel.startsWith("..") ? path20.basename(path20.resolve(top)) : rel;
|
|
33441
33300
|
}
|
|
33442
33301
|
const norm = normRepoRel(repoRelPath === "" ? "." : repoRelPath);
|
|
33443
33302
|
if (filter && norm !== filter) continue;
|
|
@@ -33469,6 +33328,64 @@ async function getWorkingTreeChangeRepoDetails(options) {
|
|
|
33469
33328
|
return out;
|
|
33470
33329
|
}
|
|
33471
33330
|
|
|
33331
|
+
// src/git/changes/lib/working-tree-changed-path-count.ts
|
|
33332
|
+
function workingTreeChangedPathCount(nameStatusLines, numstatLines, untrackedLines) {
|
|
33333
|
+
const kindByPath = parseNameStatusLines(nameStatusLines);
|
|
33334
|
+
const numByPath = parseNumstat(numstatLines);
|
|
33335
|
+
const paths = /* @__PURE__ */ new Set([...kindByPath.keys(), ...numByPath.keys()]);
|
|
33336
|
+
for (const p of untrackedLines.map((s) => s.trim()).filter(Boolean)) {
|
|
33337
|
+
paths.add(p);
|
|
33338
|
+
}
|
|
33339
|
+
return paths.size;
|
|
33340
|
+
}
|
|
33341
|
+
|
|
33342
|
+
// src/git/changes/count-working-tree-changed-files.ts
|
|
33343
|
+
async function countWorkingTreeChangedFilesForRepo(repoGitCwd) {
|
|
33344
|
+
return runGitTask(async () => {
|
|
33345
|
+
const g = cliSimpleGit(repoGitCwd);
|
|
33346
|
+
const [nameStatusRaw, numstatRaw, untrackedRaw] = await Promise.all([
|
|
33347
|
+
g.raw(["diff", "--name-status", "HEAD"]).catch(() => ""),
|
|
33348
|
+
g.raw(["diff", "HEAD", "--numstat"]).catch(() => ""),
|
|
33349
|
+
g.raw(["ls-files", "--others", "--exclude-standard"]).catch(() => "")
|
|
33350
|
+
]);
|
|
33351
|
+
return workingTreeChangedPathCount(
|
|
33352
|
+
String(nameStatusRaw).split("\n"),
|
|
33353
|
+
String(numstatRaw).split("\n"),
|
|
33354
|
+
String(untrackedRaw).split("\n")
|
|
33355
|
+
);
|
|
33356
|
+
});
|
|
33357
|
+
}
|
|
33358
|
+
|
|
33359
|
+
// src/git/status/working-tree-status.ts
|
|
33360
|
+
async function getRepoWorkingTreeStatus(repoDir) {
|
|
33361
|
+
return runGitTask(async () => {
|
|
33362
|
+
const uncommittedFileCount = await countWorkingTreeChangedFilesForRepo(repoDir);
|
|
33363
|
+
const hasUncommittedChanges = uncommittedFileCount > 0;
|
|
33364
|
+
const ahead = await commitsAheadOfRemoteTracking(repoDir);
|
|
33365
|
+
return { hasUncommittedChanges, hasUnpushedCommits: ahead > 0, uncommittedFileCount };
|
|
33366
|
+
});
|
|
33367
|
+
}
|
|
33368
|
+
async function aggregateSessionPathsWorkingTreeStatus(paths) {
|
|
33369
|
+
let hasUncommittedChanges = false;
|
|
33370
|
+
let hasUnpushedCommits = false;
|
|
33371
|
+
let uncommittedFileCount = 0;
|
|
33372
|
+
await forEachWithGitYield(paths, async (p) => {
|
|
33373
|
+
const s = await getRepoWorkingTreeStatus(p);
|
|
33374
|
+
uncommittedFileCount += s.uncommittedFileCount;
|
|
33375
|
+
if (s.hasUncommittedChanges) hasUncommittedChanges = true;
|
|
33376
|
+
if (s.hasUnpushedCommits) hasUnpushedCommits = true;
|
|
33377
|
+
});
|
|
33378
|
+
return { hasUncommittedChanges, hasUnpushedCommits, uncommittedFileCount };
|
|
33379
|
+
}
|
|
33380
|
+
async function pushAheadOfUpstreamForPaths(paths) {
|
|
33381
|
+
await forEachWithGitYield(paths, async (p) => {
|
|
33382
|
+
const g = cliSimpleGit(p);
|
|
33383
|
+
const ahead = await commitsAheadOfRemoteTracking(p);
|
|
33384
|
+
if (ahead <= 0) return;
|
|
33385
|
+
await g.push();
|
|
33386
|
+
});
|
|
33387
|
+
}
|
|
33388
|
+
|
|
33472
33389
|
// src/git/branches/commit-and-push.ts
|
|
33473
33390
|
async function gitCommitAllIfDirty(repoDir, message, options) {
|
|
33474
33391
|
const g = cliSimpleGit(repoDir);
|
|
@@ -33506,12 +33423,137 @@ async function commitSessionWorktrees(options) {
|
|
|
33506
33423
|
}
|
|
33507
33424
|
}
|
|
33508
33425
|
|
|
33426
|
+
// src/worktrees/remove-session-worktrees.ts
|
|
33427
|
+
import * as fs20 from "node:fs";
|
|
33428
|
+
|
|
33429
|
+
// src/git/worktrees/worktree-remove.ts
|
|
33430
|
+
import * as fs19 from "node:fs";
|
|
33431
|
+
|
|
33432
|
+
// src/git/worktrees/resolve-main-repo-from-git-file.ts
|
|
33433
|
+
import * as fs18 from "node:fs";
|
|
33434
|
+
import * as path21 from "node:path";
|
|
33435
|
+
function resolveMainRepoFromWorktreeGitFile(wt) {
|
|
33436
|
+
const gitDirFile = path21.join(wt, ".git");
|
|
33437
|
+
if (!fs18.existsSync(gitDirFile) || !fs18.statSync(gitDirFile).isFile()) return "";
|
|
33438
|
+
const first2 = fs18.readFileSync(gitDirFile, "utf8").trim();
|
|
33439
|
+
const m = first2.match(/^gitdir:\s*(.+)$/im);
|
|
33440
|
+
if (!m) return "";
|
|
33441
|
+
const gitWorktreePath = path21.resolve(wt, m[1].trim());
|
|
33442
|
+
const gitDir = path21.dirname(path21.dirname(gitWorktreePath));
|
|
33443
|
+
return path21.dirname(gitDir);
|
|
33444
|
+
}
|
|
33445
|
+
|
|
33446
|
+
// src/git/worktrees/worktree-remove.ts
|
|
33447
|
+
async function gitWorktreeRemoveForce(worktreePath) {
|
|
33448
|
+
const mainRepo = resolveMainRepoFromWorktreeGitFile(worktreePath);
|
|
33449
|
+
if (mainRepo) {
|
|
33450
|
+
await cliSimpleGit(mainRepo).raw(["worktree", "remove", "--force", worktreePath]);
|
|
33451
|
+
} else {
|
|
33452
|
+
fs19.rmSync(worktreePath, { recursive: true, force: true });
|
|
33453
|
+
}
|
|
33454
|
+
}
|
|
33455
|
+
|
|
33456
|
+
// src/worktrees/remove-session-worktrees.ts
|
|
33457
|
+
async function removeSessionWorktrees(paths, log2) {
|
|
33458
|
+
for (const wt of paths) {
|
|
33459
|
+
try {
|
|
33460
|
+
await gitWorktreeRemoveForce(wt);
|
|
33461
|
+
log2(`[worktrees] Removed worktree ${wt}`);
|
|
33462
|
+
} catch (e) {
|
|
33463
|
+
log2(`[worktrees] Remove failed for ${wt}: ${e instanceof Error ? e.message : String(e)}`);
|
|
33464
|
+
try {
|
|
33465
|
+
fs20.rmSync(wt, { recursive: true, force: true });
|
|
33466
|
+
} catch {
|
|
33467
|
+
}
|
|
33468
|
+
}
|
|
33469
|
+
}
|
|
33470
|
+
}
|
|
33471
|
+
|
|
33472
|
+
// src/git/branches/rename-branch.ts
|
|
33473
|
+
async function gitRenameCurrentBranch(repoDir, newName) {
|
|
33474
|
+
const g = cliSimpleGit(repoDir);
|
|
33475
|
+
await g.raw(["branch", "-m", newName]);
|
|
33476
|
+
}
|
|
33477
|
+
|
|
33478
|
+
// src/worktrees/rename-session-worktree-branches.ts
|
|
33479
|
+
async function renameSessionWorktreeBranches(paths, newBranch, log2) {
|
|
33480
|
+
const safe = newBranch.replace(/[^a-zA-Z0-9/_-]+/g, "-").slice(0, 80) || "session-branch";
|
|
33481
|
+
for (const wt of paths) {
|
|
33482
|
+
try {
|
|
33483
|
+
await gitRenameCurrentBranch(wt, safe);
|
|
33484
|
+
log2(`[worktrees] Renamed branch in ${wt} \u2192 ${safe}`);
|
|
33485
|
+
} catch (e) {
|
|
33486
|
+
log2(
|
|
33487
|
+
`[worktrees] Branch rename failed in ${wt}: ${e instanceof Error ? e.message : String(e)}`
|
|
33488
|
+
);
|
|
33489
|
+
}
|
|
33490
|
+
}
|
|
33491
|
+
}
|
|
33492
|
+
|
|
33493
|
+
// src/worktrees/worktree-layout-file.ts
|
|
33494
|
+
import * as fs21 from "node:fs";
|
|
33495
|
+
import * as path22 from "node:path";
|
|
33496
|
+
import os7 from "node:os";
|
|
33497
|
+
var LAYOUT_FILENAME = "worktree-launcher-layout.json";
|
|
33498
|
+
function defaultWorktreeLayoutPath() {
|
|
33499
|
+
return path22.join(os7.homedir(), ".buildautomaton", LAYOUT_FILENAME);
|
|
33500
|
+
}
|
|
33501
|
+
function normalizeLoadedLayout(raw) {
|
|
33502
|
+
if (raw && typeof raw === "object" && "launcherCwds" in raw) {
|
|
33503
|
+
const j = raw;
|
|
33504
|
+
if (Array.isArray(j.launcherCwds)) return { launcherCwds: j.launcherCwds };
|
|
33505
|
+
}
|
|
33506
|
+
return { launcherCwds: [] };
|
|
33507
|
+
}
|
|
33508
|
+
function loadWorktreeLayout() {
|
|
33509
|
+
try {
|
|
33510
|
+
const p = defaultWorktreeLayoutPath();
|
|
33511
|
+
if (!fs21.existsSync(p)) return { launcherCwds: [] };
|
|
33512
|
+
const raw = JSON.parse(fs21.readFileSync(p, "utf8"));
|
|
33513
|
+
return normalizeLoadedLayout(raw);
|
|
33514
|
+
} catch {
|
|
33515
|
+
return { launcherCwds: [] };
|
|
33516
|
+
}
|
|
33517
|
+
}
|
|
33518
|
+
function saveWorktreeLayout(layout) {
|
|
33519
|
+
try {
|
|
33520
|
+
const dir = path22.dirname(defaultWorktreeLayoutPath());
|
|
33521
|
+
fs21.mkdirSync(dir, { recursive: true });
|
|
33522
|
+
fs21.writeFileSync(defaultWorktreeLayoutPath(), JSON.stringify(layout, null, 2), "utf8");
|
|
33523
|
+
} catch {
|
|
33524
|
+
}
|
|
33525
|
+
}
|
|
33526
|
+
function baseNameSafe(pathString) {
|
|
33527
|
+
return path22.basename(pathString).replace(/[^a-zA-Z0-9._-]+/g, "-") || "cwd";
|
|
33528
|
+
}
|
|
33529
|
+
function getLauncherDirNameIfPresent(layout, bridgeRootPath2) {
|
|
33530
|
+
const norm = path22.resolve(bridgeRootPath2);
|
|
33531
|
+
const existing = layout.launcherCwds.find((e) => path22.resolve(e.absolutePath) === norm);
|
|
33532
|
+
return existing?.dirName;
|
|
33533
|
+
}
|
|
33534
|
+
function allocateDirNameForLauncherCwd(layout, bridgeRootPath2) {
|
|
33535
|
+
const existing = getLauncherDirNameIfPresent(layout, bridgeRootPath2);
|
|
33536
|
+
if (existing) return existing;
|
|
33537
|
+
const norm = path22.resolve(bridgeRootPath2);
|
|
33538
|
+
const base = baseNameSafe(norm);
|
|
33539
|
+
const used = new Set(layout.launcherCwds.map((e) => e.dirName));
|
|
33540
|
+
let name = base;
|
|
33541
|
+
let n = 2;
|
|
33542
|
+
while (used.has(name)) {
|
|
33543
|
+
name = `${base}-${n}`;
|
|
33544
|
+
n += 1;
|
|
33545
|
+
}
|
|
33546
|
+
layout.launcherCwds.push({ absolutePath: norm, dirName: name });
|
|
33547
|
+
saveWorktreeLayout(layout);
|
|
33548
|
+
return name;
|
|
33549
|
+
}
|
|
33550
|
+
|
|
33509
33551
|
// src/worktrees/discover-session-worktree-on-disk.ts
|
|
33510
|
-
import * as
|
|
33511
|
-
import * as
|
|
33552
|
+
import * as fs22 from "node:fs";
|
|
33553
|
+
import * as path23 from "node:path";
|
|
33512
33554
|
function isGitDir(dirPath) {
|
|
33513
33555
|
try {
|
|
33514
|
-
return
|
|
33556
|
+
return fs22.existsSync(path23.join(dirPath, ".git"));
|
|
33515
33557
|
} catch {
|
|
33516
33558
|
return false;
|
|
33517
33559
|
}
|
|
@@ -33520,23 +33562,23 @@ function collectGitRepoRootsUnderDirectory(rootPath) {
|
|
|
33520
33562
|
const out = [];
|
|
33521
33563
|
const walk = (dir) => {
|
|
33522
33564
|
if (isGitDir(dir)) {
|
|
33523
|
-
out.push(
|
|
33565
|
+
out.push(path23.resolve(dir));
|
|
33524
33566
|
return;
|
|
33525
33567
|
}
|
|
33526
33568
|
let entries;
|
|
33527
33569
|
try {
|
|
33528
|
-
entries =
|
|
33570
|
+
entries = fs22.readdirSync(dir, { withFileTypes: true });
|
|
33529
33571
|
} catch {
|
|
33530
33572
|
return;
|
|
33531
33573
|
}
|
|
33532
33574
|
for (const e of entries) {
|
|
33533
33575
|
if (e.name.startsWith(".")) continue;
|
|
33534
|
-
const full =
|
|
33576
|
+
const full = path23.join(dir, e.name);
|
|
33535
33577
|
if (!e.isDirectory()) continue;
|
|
33536
33578
|
walk(full);
|
|
33537
33579
|
}
|
|
33538
33580
|
};
|
|
33539
|
-
walk(
|
|
33581
|
+
walk(path23.resolve(rootPath));
|
|
33540
33582
|
return [...new Set(out)];
|
|
33541
33583
|
}
|
|
33542
33584
|
function collectWorktreeRootsNamed(root, sessionId, maxDepth) {
|
|
@@ -33545,16 +33587,16 @@ function collectWorktreeRootsNamed(root, sessionId, maxDepth) {
|
|
|
33545
33587
|
if (depth > maxDepth) return;
|
|
33546
33588
|
let entries;
|
|
33547
33589
|
try {
|
|
33548
|
-
entries =
|
|
33590
|
+
entries = fs22.readdirSync(dir, { withFileTypes: true });
|
|
33549
33591
|
} catch {
|
|
33550
33592
|
return;
|
|
33551
33593
|
}
|
|
33552
33594
|
for (const e of entries) {
|
|
33553
33595
|
if (e.name.startsWith(".")) continue;
|
|
33554
|
-
const full =
|
|
33596
|
+
const full = path23.join(dir, e.name);
|
|
33555
33597
|
if (!e.isDirectory()) continue;
|
|
33556
33598
|
if (e.name === sessionId) {
|
|
33557
|
-
if (isGitDir(full)) out.push(
|
|
33599
|
+
if (isGitDir(full)) out.push(path23.resolve(full));
|
|
33558
33600
|
} else {
|
|
33559
33601
|
walk(full, depth + 1);
|
|
33560
33602
|
}
|
|
@@ -33566,14 +33608,14 @@ function collectWorktreeRootsNamed(root, sessionId, maxDepth) {
|
|
|
33566
33608
|
function tryBindingFromSessionDirectory(sessionDir) {
|
|
33567
33609
|
let st;
|
|
33568
33610
|
try {
|
|
33569
|
-
st =
|
|
33611
|
+
st = fs22.statSync(sessionDir);
|
|
33570
33612
|
} catch {
|
|
33571
33613
|
return null;
|
|
33572
33614
|
}
|
|
33573
33615
|
if (!st.isDirectory()) return null;
|
|
33574
33616
|
const worktreePaths = collectGitRepoRootsUnderDirectory(sessionDir);
|
|
33575
33617
|
if (worktreePaths.length === 0) return null;
|
|
33576
|
-
const abs =
|
|
33618
|
+
const abs = path23.resolve(sessionDir);
|
|
33577
33619
|
return {
|
|
33578
33620
|
sessionParentPath: abs,
|
|
33579
33621
|
workingTreeRelRoot: abs,
|
|
@@ -33583,20 +33625,20 @@ function tryBindingFromSessionDirectory(sessionDir) {
|
|
|
33583
33625
|
function discoverLegacyBindingAscendingFromCheckout(sessionId, checkoutPath) {
|
|
33584
33626
|
const sid = sessionId.trim();
|
|
33585
33627
|
if (!sid) return null;
|
|
33586
|
-
const hintR =
|
|
33628
|
+
const hintR = path23.resolve(checkoutPath);
|
|
33587
33629
|
let best = null;
|
|
33588
|
-
let cur =
|
|
33630
|
+
let cur = path23.dirname(hintR);
|
|
33589
33631
|
for (let i = 0; i < 40; i++) {
|
|
33590
33632
|
const paths = collectWorktreeRootsNamed(cur, sid, 24);
|
|
33591
|
-
if (paths.some((p) =>
|
|
33592
|
-
const isolated = resolveIsolatedSessionParentPathFromCheckouts(paths) ??
|
|
33633
|
+
if (paths.some((p) => path23.resolve(p) === hintR)) {
|
|
33634
|
+
const isolated = resolveIsolatedSessionParentPathFromCheckouts(paths) ?? path23.resolve(paths[0]);
|
|
33593
33635
|
best = {
|
|
33594
|
-
sessionParentPath:
|
|
33595
|
-
workingTreeRelRoot:
|
|
33596
|
-
repoCheckoutPaths: paths.map((p) =>
|
|
33636
|
+
sessionParentPath: path23.resolve(isolated),
|
|
33637
|
+
workingTreeRelRoot: path23.resolve(cur),
|
|
33638
|
+
repoCheckoutPaths: paths.map((p) => path23.resolve(p))
|
|
33597
33639
|
};
|
|
33598
33640
|
}
|
|
33599
|
-
const next =
|
|
33641
|
+
const next = path23.dirname(cur);
|
|
33600
33642
|
if (next === cur) break;
|
|
33601
33643
|
cur = next;
|
|
33602
33644
|
}
|
|
@@ -33604,33 +33646,33 @@ function discoverLegacyBindingAscendingFromCheckout(sessionId, checkoutPath) {
|
|
|
33604
33646
|
}
|
|
33605
33647
|
function discoverSessionWorktreeOnDisk(options) {
|
|
33606
33648
|
const { sessionId, worktreesRootPath, layout, bridgeRoot } = options;
|
|
33607
|
-
if (!sessionId.trim() || !
|
|
33649
|
+
if (!sessionId.trim() || !fs22.existsSync(worktreesRootPath)) return null;
|
|
33608
33650
|
const preferredKey = getLauncherDirNameIfPresent(layout, bridgeRoot);
|
|
33609
33651
|
const keys = [];
|
|
33610
33652
|
if (preferredKey) keys.push(preferredKey);
|
|
33611
33653
|
try {
|
|
33612
|
-
for (const name of
|
|
33654
|
+
for (const name of fs22.readdirSync(worktreesRootPath)) {
|
|
33613
33655
|
if (name.startsWith(".")) continue;
|
|
33614
|
-
const p =
|
|
33615
|
-
if (!
|
|
33656
|
+
const p = path23.join(worktreesRootPath, name);
|
|
33657
|
+
if (!fs22.statSync(p).isDirectory()) continue;
|
|
33616
33658
|
if (name !== preferredKey) keys.push(name);
|
|
33617
33659
|
}
|
|
33618
33660
|
} catch {
|
|
33619
33661
|
return null;
|
|
33620
33662
|
}
|
|
33621
33663
|
for (const key of keys) {
|
|
33622
|
-
const layoutRoot =
|
|
33623
|
-
if (!
|
|
33624
|
-
const sessionDir =
|
|
33664
|
+
const layoutRoot = path23.join(worktreesRootPath, key);
|
|
33665
|
+
if (!fs22.existsSync(layoutRoot) || !fs22.statSync(layoutRoot).isDirectory()) continue;
|
|
33666
|
+
const sessionDir = path23.join(layoutRoot, sessionId);
|
|
33625
33667
|
const nested = tryBindingFromSessionDirectory(sessionDir);
|
|
33626
33668
|
if (nested) return nested;
|
|
33627
33669
|
const legacyPaths = collectWorktreeRootsNamed(layoutRoot, sessionId, 24);
|
|
33628
33670
|
if (legacyPaths.length > 0) {
|
|
33629
|
-
const isolated = resolveIsolatedSessionParentPathFromCheckouts(legacyPaths) ??
|
|
33671
|
+
const isolated = resolveIsolatedSessionParentPathFromCheckouts(legacyPaths) ?? path23.resolve(legacyPaths[0]);
|
|
33630
33672
|
return {
|
|
33631
|
-
sessionParentPath:
|
|
33632
|
-
workingTreeRelRoot:
|
|
33633
|
-
repoCheckoutPaths: legacyPaths.map((p) =>
|
|
33673
|
+
sessionParentPath: path23.resolve(isolated),
|
|
33674
|
+
workingTreeRelRoot: path23.resolve(layoutRoot),
|
|
33675
|
+
repoCheckoutPaths: legacyPaths.map((p) => path23.resolve(p))
|
|
33634
33676
|
};
|
|
33635
33677
|
}
|
|
33636
33678
|
}
|
|
@@ -33639,12 +33681,12 @@ function discoverSessionWorktreeOnDisk(options) {
|
|
|
33639
33681
|
function discoverSessionWorktreesUnderSessionWorktreeRoot(sessionWorktreeRootPathOrHint, sessionId) {
|
|
33640
33682
|
const sid = sessionId.trim();
|
|
33641
33683
|
if (!sid) return null;
|
|
33642
|
-
const hint =
|
|
33643
|
-
const underHint = tryBindingFromSessionDirectory(
|
|
33684
|
+
const hint = path23.resolve(sessionWorktreeRootPathOrHint);
|
|
33685
|
+
const underHint = tryBindingFromSessionDirectory(path23.join(hint, sid));
|
|
33644
33686
|
if (underHint) return underHint;
|
|
33645
33687
|
const direct = tryBindingFromSessionDirectory(hint);
|
|
33646
33688
|
if (direct) {
|
|
33647
|
-
if (
|
|
33689
|
+
if (path23.basename(hint) === sid && isGitDir(hint)) {
|
|
33648
33690
|
const legacyFromCheckout = discoverLegacyBindingAscendingFromCheckout(sid, hint);
|
|
33649
33691
|
if (legacyFromCheckout && legacyFromCheckout.repoCheckoutPaths.length > direct.repoCheckoutPaths.length) {
|
|
33650
33692
|
return legacyFromCheckout;
|
|
@@ -33652,216 +33694,349 @@ function discoverSessionWorktreesUnderSessionWorktreeRoot(sessionWorktreeRootPat
|
|
|
33652
33694
|
}
|
|
33653
33695
|
return direct;
|
|
33654
33696
|
}
|
|
33655
|
-
if (
|
|
33697
|
+
if (path23.basename(hint) === sid && isGitDir(hint)) {
|
|
33656
33698
|
const legacyFromCheckout = discoverLegacyBindingAscendingFromCheckout(sid, hint);
|
|
33657
33699
|
if (legacyFromCheckout) return legacyFromCheckout;
|
|
33658
33700
|
}
|
|
33659
33701
|
let st;
|
|
33660
33702
|
try {
|
|
33661
|
-
st =
|
|
33703
|
+
st = fs22.statSync(hint);
|
|
33662
33704
|
} catch {
|
|
33663
33705
|
return null;
|
|
33664
33706
|
}
|
|
33665
33707
|
if (!st.isDirectory()) return null;
|
|
33666
33708
|
const legacyPaths = collectWorktreeRootsNamed(hint, sid, 24);
|
|
33667
33709
|
if (legacyPaths.length === 0) return null;
|
|
33668
|
-
const isolated = resolveIsolatedSessionParentPathFromCheckouts(legacyPaths) ??
|
|
33710
|
+
const isolated = resolveIsolatedSessionParentPathFromCheckouts(legacyPaths) ?? path23.resolve(legacyPaths[0]);
|
|
33669
33711
|
return {
|
|
33670
|
-
sessionParentPath:
|
|
33712
|
+
sessionParentPath: path23.resolve(isolated),
|
|
33671
33713
|
workingTreeRelRoot: hint,
|
|
33672
|
-
repoCheckoutPaths: legacyPaths.map((p) =>
|
|
33714
|
+
repoCheckoutPaths: legacyPaths.map((p) => path23.resolve(p))
|
|
33673
33715
|
};
|
|
33674
33716
|
}
|
|
33675
33717
|
|
|
33676
|
-
// src/worktrees/session-
|
|
33718
|
+
// src/worktrees/manager/discover-session-binding.ts
|
|
33719
|
+
function discoverSessionBinding(params) {
|
|
33720
|
+
return discoverSessionWorktreeOnDisk({
|
|
33721
|
+
sessionId: params.sessionId,
|
|
33722
|
+
worktreesRootPath: params.worktreesRootPath,
|
|
33723
|
+
layout: params.layout,
|
|
33724
|
+
bridgeRoot: getBridgeRoot()
|
|
33725
|
+
});
|
|
33726
|
+
}
|
|
33727
|
+
|
|
33728
|
+
// src/worktrees/manager/resolve-isolated-session-parent-path.ts
|
|
33729
|
+
function resolveIsolatedSessionParentPath(sessionId, cache2, ensureRepoCheckoutPaths) {
|
|
33730
|
+
if (!sessionId) return null;
|
|
33731
|
+
const sid = sessionId.trim();
|
|
33732
|
+
const cached2 = cache2.getSessionParentPath(sid);
|
|
33733
|
+
if (cached2) return cached2;
|
|
33734
|
+
const paths = ensureRepoCheckoutPaths(sid);
|
|
33735
|
+
if (!paths?.length) return null;
|
|
33736
|
+
return resolveIsolatedSessionParentPathFromCheckouts(paths);
|
|
33737
|
+
}
|
|
33738
|
+
function ensureRepoCheckoutPathsForSession(sessionId, cache2, discover) {
|
|
33739
|
+
if (!sessionId?.trim()) return void 0;
|
|
33740
|
+
const sid = sessionId.trim();
|
|
33741
|
+
const cached2 = cache2.getRepoCheckoutPaths(sid);
|
|
33742
|
+
if (cached2?.length) return cached2;
|
|
33743
|
+
const disc = discover(sid);
|
|
33744
|
+
if (disc?.repoCheckoutPaths.length) {
|
|
33745
|
+
cache2.remember(sid, disc);
|
|
33746
|
+
return [...disc.repoCheckoutPaths];
|
|
33747
|
+
}
|
|
33748
|
+
return void 0;
|
|
33749
|
+
}
|
|
33750
|
+
|
|
33751
|
+
// src/worktrees/manager/resolve-commit-targets.ts
|
|
33752
|
+
function resolveCommitTargets(sessionId, cache2, discover) {
|
|
33753
|
+
const paths = cache2.getRepoCheckoutPathsRef(sessionId);
|
|
33754
|
+
if (paths?.length) return paths;
|
|
33755
|
+
const disc = discover(sessionId);
|
|
33756
|
+
if (disc?.repoCheckoutPaths.length) {
|
|
33757
|
+
cache2.remember(sessionId, disc);
|
|
33758
|
+
return disc.repoCheckoutPaths;
|
|
33759
|
+
}
|
|
33760
|
+
return [getBridgeRoot()];
|
|
33761
|
+
}
|
|
33762
|
+
|
|
33763
|
+
// src/worktrees/manager/parse-session-parent.ts
|
|
33677
33764
|
function parseSessionParent(v) {
|
|
33678
33765
|
if (v === "bridge_root" || v === "worktrees_root") return v;
|
|
33679
33766
|
if (v === "session_worktrees_root") return "worktrees_root";
|
|
33680
33767
|
return null;
|
|
33681
33768
|
}
|
|
33682
|
-
|
|
33683
|
-
|
|
33684
|
-
|
|
33769
|
+
|
|
33770
|
+
// src/worktrees/prepare-new-session-worktrees.ts
|
|
33771
|
+
import * as fs23 from "node:fs";
|
|
33772
|
+
import * as path24 from "node:path";
|
|
33773
|
+
|
|
33774
|
+
// src/git/worktrees/worktree-add.ts
|
|
33775
|
+
async function gitWorktreeAddBranch(mainRepoPath, worktreePath, branch, baseRef = "HEAD") {
|
|
33776
|
+
const mainGit = cliSimpleGit(mainRepoPath);
|
|
33777
|
+
const base = baseRef.trim() || "HEAD";
|
|
33778
|
+
await mainGit.raw(["worktree", "add", "-b", branch, worktreePath, base]);
|
|
33779
|
+
}
|
|
33780
|
+
|
|
33781
|
+
// src/worktrees/prepare-new-session-worktrees.ts
|
|
33782
|
+
function normalizeRepoRelPath(rel) {
|
|
33783
|
+
return rel === "" ? "." : rel.replace(/\\/g, "/");
|
|
33784
|
+
}
|
|
33785
|
+
function resolveBaseRefForRepo(relNorm, baseBranches) {
|
|
33786
|
+
if (!baseBranches) return "HEAD";
|
|
33787
|
+
const direct = baseBranches[relNorm]?.trim();
|
|
33788
|
+
if (direct) return direct;
|
|
33789
|
+
if (relNorm !== "." && baseBranches["."]?.trim()) return baseBranches["."].trim();
|
|
33790
|
+
return "HEAD";
|
|
33791
|
+
}
|
|
33792
|
+
async function prepareNewSessionWorktrees(options) {
|
|
33793
|
+
const { worktreesRootPath, bridgeRoot, sessionId, layout, log: log2, worktreeBaseBranches } = options;
|
|
33794
|
+
const bridgeResolved = path24.resolve(bridgeRoot);
|
|
33795
|
+
const cwdKey = allocateDirNameForLauncherCwd(layout, bridgeResolved);
|
|
33796
|
+
const bridgeKeyDir = path24.join(worktreesRootPath, cwdKey);
|
|
33797
|
+
const sessionDir = path24.join(bridgeKeyDir, sessionId);
|
|
33798
|
+
const repos = await discoverGitReposUnderRoot(bridgeResolved);
|
|
33799
|
+
if (repos.length === 0) {
|
|
33800
|
+
log2("[worktrees] No Git repositories under bridge root; skipping worktree creation.");
|
|
33801
|
+
return null;
|
|
33802
|
+
}
|
|
33803
|
+
const branch = `session-${sessionId}`;
|
|
33804
|
+
const worktreePaths = [];
|
|
33805
|
+
fs23.mkdirSync(sessionDir, { recursive: true });
|
|
33806
|
+
for (const repo of repos) {
|
|
33807
|
+
let rel = path24.relative(bridgeResolved, repo.absolutePath);
|
|
33808
|
+
if (rel.startsWith("..") || path24.isAbsolute(rel)) continue;
|
|
33809
|
+
const relNorm = normalizeRepoRelPath(rel === "" ? "." : rel);
|
|
33810
|
+
const wtPath = relNorm === "." ? sessionDir : path24.join(sessionDir, relNorm);
|
|
33811
|
+
if (relNorm !== ".") {
|
|
33812
|
+
fs23.mkdirSync(path24.dirname(wtPath), { recursive: true });
|
|
33813
|
+
}
|
|
33814
|
+
const baseRef = resolveBaseRefForRepo(relNorm, worktreeBaseBranches);
|
|
33815
|
+
try {
|
|
33816
|
+
await gitWorktreeAddBranch(repo.absolutePath, wtPath, branch, baseRef);
|
|
33817
|
+
log2(`[worktrees] Added worktree ${wtPath} (branch ${branch}, base ${baseRef}).`);
|
|
33818
|
+
worktreePaths.push(wtPath);
|
|
33819
|
+
} catch (e) {
|
|
33820
|
+
log2(
|
|
33821
|
+
`[worktrees] Worktree add failed for ${repo.absolutePath}: ${e instanceof Error ? e.message : String(e)}`
|
|
33822
|
+
);
|
|
33823
|
+
}
|
|
33824
|
+
}
|
|
33825
|
+
if (worktreePaths.length === 0) return null;
|
|
33826
|
+
return {
|
|
33827
|
+
worktreePaths,
|
|
33828
|
+
sessionParentPath: sessionDir,
|
|
33829
|
+
workingTreeRelRoot: sessionDir
|
|
33830
|
+
};
|
|
33831
|
+
}
|
|
33832
|
+
|
|
33833
|
+
// src/worktrees/manager/prepare-and-remember-session-worktrees.ts
|
|
33834
|
+
async function prepareAndRememberSessionWorktrees(params) {
|
|
33835
|
+
const prep = await prepareNewSessionWorktrees({
|
|
33836
|
+
worktreesRootPath: params.worktreesRootPath,
|
|
33837
|
+
bridgeRoot: getBridgeRoot(),
|
|
33838
|
+
sessionId: params.sessionId,
|
|
33839
|
+
layout: params.layout,
|
|
33840
|
+
log: params.log,
|
|
33841
|
+
...params.worktreeBaseBranches && Object.keys(params.worktreeBaseBranches).length > 0 ? { worktreeBaseBranches: params.worktreeBaseBranches } : {}
|
|
33842
|
+
});
|
|
33843
|
+
if (!prep) return void 0;
|
|
33844
|
+
params.cache.remember(params.sessionId, {
|
|
33845
|
+
sessionParentPath: prep.sessionParentPath,
|
|
33846
|
+
workingTreeRelRoot: prep.workingTreeRelRoot,
|
|
33847
|
+
repoCheckoutPaths: prep.worktreePaths
|
|
33848
|
+
});
|
|
33849
|
+
return params.cache.getSessionParentPath(params.sessionId);
|
|
33850
|
+
}
|
|
33851
|
+
|
|
33852
|
+
// src/worktrees/manager/resolve-existing-session-parent-path.ts
|
|
33853
|
+
function resolveExistingSessionParentPath(sessionId, cache2, discover) {
|
|
33854
|
+
const cached2 = cache2.getSessionParentPath(sessionId);
|
|
33855
|
+
if (cached2) return cached2;
|
|
33856
|
+
const disc = discover();
|
|
33857
|
+
if (disc) {
|
|
33858
|
+
cache2.remember(sessionId, disc);
|
|
33859
|
+
return cache2.getSessionParentPath(sessionId);
|
|
33860
|
+
}
|
|
33861
|
+
return void 0;
|
|
33862
|
+
}
|
|
33863
|
+
|
|
33864
|
+
// src/worktrees/manager/resolve-explicit-session-parent-path.ts
|
|
33865
|
+
import * as path25 from "node:path";
|
|
33866
|
+
function resolveExplicitSessionParentPath(params) {
|
|
33867
|
+
const resolved = path25.resolve(params.parentPathRaw);
|
|
33868
|
+
if (parseSessionParent(params.sessionParent) !== "worktrees_root") {
|
|
33869
|
+
return resolved;
|
|
33870
|
+
}
|
|
33871
|
+
const rememberAndReturn = (binding) => {
|
|
33872
|
+
params.cache.remember(params.sessionId, binding);
|
|
33873
|
+
return params.cache.getSessionParentPath(params.sessionId) ?? resolved;
|
|
33874
|
+
};
|
|
33875
|
+
const diskFirst = params.discover();
|
|
33876
|
+
if (diskFirst) return rememberAndReturn(diskFirst);
|
|
33877
|
+
const fromRoot = discoverSessionWorktreesUnderSessionWorktreeRoot(resolved, params.sessionId);
|
|
33878
|
+
if (fromRoot) return rememberAndReturn(fromRoot);
|
|
33879
|
+
let cur = resolved;
|
|
33880
|
+
for (let i = 0; i < 16; i++) {
|
|
33881
|
+
const tryRoot = discoverSessionWorktreesUnderSessionWorktreeRoot(cur, params.sessionId);
|
|
33882
|
+
if (tryRoot) return rememberAndReturn(tryRoot);
|
|
33883
|
+
const next = path25.dirname(cur);
|
|
33884
|
+
if (next === cur) break;
|
|
33885
|
+
cur = next;
|
|
33886
|
+
}
|
|
33887
|
+
return resolved;
|
|
33888
|
+
}
|
|
33889
|
+
|
|
33890
|
+
// src/worktrees/manager/resolve-session-parent-path-for-prompt.ts
|
|
33891
|
+
async function resolveSessionParentPathForPrompt(params) {
|
|
33892
|
+
const { sessionId, cache: cache2, worktreesRootPath, layout, log: log2, discover, opts } = params;
|
|
33893
|
+
if (!sessionId) return void 0;
|
|
33894
|
+
const sid = sessionId.trim();
|
|
33895
|
+
const parentPathRaw = opts.sessionParentPath?.trim();
|
|
33896
|
+
if (parentPathRaw) {
|
|
33897
|
+
return resolveExplicitSessionParentPath({
|
|
33898
|
+
sessionId: sid,
|
|
33899
|
+
sessionParent: opts.sessionParent,
|
|
33900
|
+
parentPathRaw,
|
|
33901
|
+
cache: cache2,
|
|
33902
|
+
discover: () => discover(sid)
|
|
33903
|
+
});
|
|
33904
|
+
}
|
|
33905
|
+
const parentKind = parseSessionParent(opts.sessionParent);
|
|
33906
|
+
if (parentKind === "bridge_root") {
|
|
33907
|
+
return void 0;
|
|
33908
|
+
}
|
|
33909
|
+
if (parentKind === "worktrees_root") {
|
|
33910
|
+
if (!opts.isNewSession) {
|
|
33911
|
+
return resolveExistingSessionParentPath(sid, cache2, () => discover(sid));
|
|
33912
|
+
}
|
|
33913
|
+
return prepareAndRememberSessionWorktrees({
|
|
33914
|
+
cache: cache2,
|
|
33915
|
+
sessionId: sid,
|
|
33916
|
+
worktreesRootPath,
|
|
33917
|
+
layout,
|
|
33918
|
+
log: log2,
|
|
33919
|
+
...opts.worktreeBaseBranches ? { worktreeBaseBranches: opts.worktreeBaseBranches } : {}
|
|
33920
|
+
});
|
|
33921
|
+
}
|
|
33922
|
+
if (!opts.isNewSession) {
|
|
33923
|
+
return resolveExistingSessionParentPath(sid, cache2, () => discover(sid));
|
|
33924
|
+
}
|
|
33925
|
+
return prepareAndRememberSessionWorktrees({
|
|
33926
|
+
cache: cache2,
|
|
33927
|
+
sessionId: sid,
|
|
33928
|
+
worktreesRootPath,
|
|
33929
|
+
layout,
|
|
33930
|
+
log: log2
|
|
33931
|
+
});
|
|
33932
|
+
}
|
|
33933
|
+
|
|
33934
|
+
// src/worktrees/manager/session-worktree-cache.ts
|
|
33935
|
+
import * as path26 from "node:path";
|
|
33936
|
+
var SessionWorktreeCache = class {
|
|
33685
33937
|
sessionRepoCheckoutPaths = /* @__PURE__ */ new Map();
|
|
33686
33938
|
sessionParentPathBySession = /* @__PURE__ */ new Map();
|
|
33687
33939
|
sessionWorkingTreeRelRootBySession = /* @__PURE__ */ new Map();
|
|
33688
|
-
|
|
33689
|
-
|
|
33690
|
-
this.worktreesRootPath = options.worktreesRootPath;
|
|
33691
|
-
this.log = options.log;
|
|
33692
|
-
this.layout = loadWorktreeLayout();
|
|
33693
|
-
}
|
|
33694
|
-
rememberSessionWorktrees(sessionId, binding) {
|
|
33695
|
-
const paths = binding.repoCheckoutPaths.map((p) => path25.resolve(p));
|
|
33940
|
+
remember(sessionId, binding) {
|
|
33941
|
+
const paths = binding.repoCheckoutPaths.map((p) => path26.resolve(p));
|
|
33696
33942
|
this.sessionRepoCheckoutPaths.set(sessionId, paths);
|
|
33697
|
-
this.sessionParentPathBySession.set(sessionId,
|
|
33698
|
-
this.sessionWorkingTreeRelRootBySession.set(sessionId,
|
|
33943
|
+
this.sessionParentPathBySession.set(sessionId, path26.resolve(binding.sessionParentPath));
|
|
33944
|
+
this.sessionWorkingTreeRelRootBySession.set(sessionId, path26.resolve(binding.workingTreeRelRoot));
|
|
33945
|
+
}
|
|
33946
|
+
clearSession(sessionId) {
|
|
33947
|
+
const paths = this.sessionRepoCheckoutPaths.get(sessionId);
|
|
33948
|
+
this.sessionRepoCheckoutPaths.delete(sessionId);
|
|
33949
|
+
this.sessionParentPathBySession.delete(sessionId);
|
|
33950
|
+
this.sessionWorkingTreeRelRootBySession.delete(sessionId);
|
|
33951
|
+
return paths;
|
|
33699
33952
|
}
|
|
33700
|
-
|
|
33953
|
+
getSessionParentPath(sessionId) {
|
|
33701
33954
|
return this.sessionParentPathBySession.get(sessionId);
|
|
33702
33955
|
}
|
|
33703
|
-
|
|
33704
|
-
return
|
|
33705
|
-
|
|
33706
|
-
|
|
33707
|
-
|
|
33708
|
-
|
|
33709
|
-
|
|
33956
|
+
getWorkingTreeRelRoot(sessionId) {
|
|
33957
|
+
return this.sessionWorkingTreeRelRootBySession.get(sessionId) ?? null;
|
|
33958
|
+
}
|
|
33959
|
+
hasSession(sessionId) {
|
|
33960
|
+
return this.sessionParentPathBySession.has(sessionId);
|
|
33961
|
+
}
|
|
33962
|
+
getRepoCheckoutPaths(sessionId) {
|
|
33963
|
+
const paths = this.sessionRepoCheckoutPaths.get(sessionId);
|
|
33964
|
+
return paths?.length ? [...paths] : void 0;
|
|
33965
|
+
}
|
|
33966
|
+
getRepoCheckoutPathsRef(sessionId) {
|
|
33967
|
+
const paths = this.sessionRepoCheckoutPaths.get(sessionId);
|
|
33968
|
+
return paths?.length ? paths : void 0;
|
|
33710
33969
|
}
|
|
33711
33970
|
isLegacyNestedLayout(sessionId) {
|
|
33712
33971
|
const parent = this.sessionParentPathBySession.get(sessionId);
|
|
33713
33972
|
const relRoot = this.sessionWorkingTreeRelRootBySession.get(sessionId);
|
|
33714
33973
|
if (!parent || !relRoot) return false;
|
|
33715
|
-
return
|
|
33974
|
+
return path26.resolve(parent) !== path26.resolve(relRoot);
|
|
33975
|
+
}
|
|
33976
|
+
};
|
|
33977
|
+
|
|
33978
|
+
// src/worktrees/manager/manager.ts
|
|
33979
|
+
var SessionWorktreeManager = class {
|
|
33980
|
+
worktreesRootPath;
|
|
33981
|
+
log;
|
|
33982
|
+
cache = new SessionWorktreeCache();
|
|
33983
|
+
layout;
|
|
33984
|
+
constructor(options) {
|
|
33985
|
+
this.worktreesRootPath = options.worktreesRootPath;
|
|
33986
|
+
this.log = options.log;
|
|
33987
|
+
this.layout = loadWorktreeLayout();
|
|
33988
|
+
}
|
|
33989
|
+
discover(sessionId) {
|
|
33990
|
+
return discoverSessionBinding({
|
|
33991
|
+
sessionId,
|
|
33992
|
+
worktreesRootPath: this.worktreesRootPath,
|
|
33993
|
+
layout: this.layout
|
|
33994
|
+
});
|
|
33716
33995
|
}
|
|
33717
|
-
/**
|
|
33718
|
-
* Session parent path for `worktrees_root`: the per-session directory (new layout) or primary checkout (legacy).
|
|
33719
|
-
*/
|
|
33720
33996
|
getIsolatedSessionParentPathForSession(sessionId) {
|
|
33721
|
-
|
|
33722
|
-
|
|
33723
|
-
|
|
33724
|
-
|
|
33725
|
-
|
|
33726
|
-
|
|
33727
|
-
|
|
33728
|
-
|
|
33729
|
-
|
|
33730
|
-
|
|
33731
|
-
* or `undefined` meaning use {@link getBridgeRoot}.
|
|
33732
|
-
*/
|
|
33733
|
-
async resolveSessionParentPathForPrompt(sessionId, opts) {
|
|
33734
|
-
if (!sessionId) return void 0;
|
|
33735
|
-
const sid = sessionId.trim();
|
|
33736
|
-
const parentPathRaw = opts.sessionParentPath?.trim();
|
|
33737
|
-
if (parentPathRaw) {
|
|
33738
|
-
const resolved = path25.resolve(parentPathRaw);
|
|
33739
|
-
if (sid && parseSessionParent(opts.sessionParent) === "worktrees_root") {
|
|
33740
|
-
const diskFirst = this.tryDiscoverFromDisk(sid);
|
|
33741
|
-
if (diskFirst) {
|
|
33742
|
-
this.rememberSessionWorktrees(sid, diskFirst);
|
|
33743
|
-
return this.sessionParentPathAfterRemember(sid);
|
|
33744
|
-
}
|
|
33745
|
-
const fromRoot = discoverSessionWorktreesUnderSessionWorktreeRoot(resolved, sid);
|
|
33746
|
-
if (fromRoot) {
|
|
33747
|
-
this.rememberSessionWorktrees(sid, fromRoot);
|
|
33748
|
-
return this.sessionParentPathAfterRemember(sid);
|
|
33749
|
-
}
|
|
33750
|
-
let cur = resolved;
|
|
33751
|
-
for (let i = 0; i < 16; i++) {
|
|
33752
|
-
const tryRoot = discoverSessionWorktreesUnderSessionWorktreeRoot(cur, sid);
|
|
33753
|
-
if (tryRoot) {
|
|
33754
|
-
this.rememberSessionWorktrees(sid, tryRoot);
|
|
33755
|
-
return this.sessionParentPathAfterRemember(sid);
|
|
33756
|
-
}
|
|
33757
|
-
const next = path25.dirname(cur);
|
|
33758
|
-
if (next === cur) break;
|
|
33759
|
-
cur = next;
|
|
33760
|
-
}
|
|
33761
|
-
}
|
|
33762
|
-
return resolved;
|
|
33763
|
-
}
|
|
33764
|
-
const parentKind = parseSessionParent(opts.sessionParent);
|
|
33765
|
-
if (parentKind === "bridge_root") {
|
|
33766
|
-
return void 0;
|
|
33767
|
-
}
|
|
33768
|
-
if (parentKind === "worktrees_root") {
|
|
33769
|
-
if (!opts.isNewSession) {
|
|
33770
|
-
const cached2 = this.sessionParentPathAfterRemember(sid);
|
|
33771
|
-
if (cached2) return cached2;
|
|
33772
|
-
const disc = this.tryDiscoverFromDisk(sid);
|
|
33773
|
-
if (disc) {
|
|
33774
|
-
this.rememberSessionWorktrees(sid, disc);
|
|
33775
|
-
return this.sessionParentPathAfterRemember(sid);
|
|
33776
|
-
}
|
|
33777
|
-
return void 0;
|
|
33778
|
-
}
|
|
33779
|
-
const prep2 = await prepareNewSessionWorktrees({
|
|
33780
|
-
worktreesRootPath: this.worktreesRootPath,
|
|
33781
|
-
bridgeRoot: getBridgeRoot(),
|
|
33782
|
-
sessionId: sid,
|
|
33783
|
-
layout: this.layout,
|
|
33784
|
-
log: this.log
|
|
33785
|
-
});
|
|
33786
|
-
if (!prep2) return void 0;
|
|
33787
|
-
this.rememberSessionWorktrees(sid, {
|
|
33788
|
-
sessionParentPath: prep2.sessionParentPath,
|
|
33789
|
-
workingTreeRelRoot: prep2.workingTreeRelRoot,
|
|
33790
|
-
repoCheckoutPaths: prep2.worktreePaths
|
|
33791
|
-
});
|
|
33792
|
-
return this.sessionParentPathAfterRemember(sid);
|
|
33793
|
-
}
|
|
33794
|
-
if (!opts.isNewSession) {
|
|
33795
|
-
const cached2 = this.sessionParentPathAfterRemember(sid);
|
|
33796
|
-
if (cached2) return cached2;
|
|
33797
|
-
const disc = this.tryDiscoverFromDisk(sid);
|
|
33798
|
-
if (disc) {
|
|
33799
|
-
this.rememberSessionWorktrees(sid, disc);
|
|
33800
|
-
return this.sessionParentPathAfterRemember(sid);
|
|
33801
|
-
}
|
|
33802
|
-
return void 0;
|
|
33803
|
-
}
|
|
33804
|
-
const prep = await prepareNewSessionWorktrees({
|
|
33997
|
+
return resolveIsolatedSessionParentPath(
|
|
33998
|
+
sessionId,
|
|
33999
|
+
this.cache,
|
|
34000
|
+
(sid) => this.ensureRepoCheckoutPathsForSession(sid)
|
|
34001
|
+
);
|
|
34002
|
+
}
|
|
34003
|
+
resolveSessionParentPathForPrompt(sessionId, opts) {
|
|
34004
|
+
return resolveSessionParentPathForPrompt({
|
|
34005
|
+
sessionId,
|
|
34006
|
+
cache: this.cache,
|
|
33805
34007
|
worktreesRootPath: this.worktreesRootPath,
|
|
33806
|
-
bridgeRoot: getBridgeRoot(),
|
|
33807
|
-
sessionId: sid,
|
|
33808
34008
|
layout: this.layout,
|
|
33809
|
-
log: this.log
|
|
33810
|
-
|
|
33811
|
-
|
|
33812
|
-
this.rememberSessionWorktrees(sid, {
|
|
33813
|
-
sessionParentPath: prep.sessionParentPath,
|
|
33814
|
-
workingTreeRelRoot: prep.workingTreeRelRoot,
|
|
33815
|
-
repoCheckoutPaths: prep.worktreePaths
|
|
34009
|
+
log: this.log,
|
|
34010
|
+
discover: (sid) => this.discover(sid),
|
|
34011
|
+
opts
|
|
33816
34012
|
});
|
|
33817
|
-
return this.sessionParentPathAfterRemember(sid);
|
|
33818
34013
|
}
|
|
33819
34014
|
async renameSessionBranch(sessionId, newBranch) {
|
|
33820
|
-
const paths = this.
|
|
34015
|
+
const paths = this.cache.getRepoCheckoutPathsRef(sessionId);
|
|
33821
34016
|
if (!paths?.length) return;
|
|
33822
34017
|
await renameSessionWorktreeBranches(paths, newBranch, this.log);
|
|
33823
34018
|
}
|
|
33824
|
-
/** True when this session uses an isolated worktree layout (not the bridge root). */
|
|
33825
34019
|
usesWorktreeSession(sessionId) {
|
|
33826
34020
|
if (!sessionId) return false;
|
|
33827
|
-
return this.
|
|
34021
|
+
return this.cache.hasSession(sessionId);
|
|
33828
34022
|
}
|
|
33829
|
-
/** Per-repo git checkout directories for this session (for snapshots, commits, change lists). */
|
|
33830
34023
|
getRepoCheckoutPathsForSession(sessionId) {
|
|
33831
34024
|
if (!sessionId) return void 0;
|
|
33832
|
-
|
|
33833
|
-
return paths?.length ? [...paths] : void 0;
|
|
34025
|
+
return this.cache.getRepoCheckoutPaths(sessionId);
|
|
33834
34026
|
}
|
|
33835
|
-
/**
|
|
33836
|
-
* Same paths as {@link getRepoCheckoutPathsForSession}, but loads from disk into memory when the CLI
|
|
33837
|
-
* restarted or maps were not yet populated (avoids discovering every repo under the worktrees root).
|
|
33838
|
-
*/
|
|
33839
34027
|
ensureRepoCheckoutPathsForSession(sessionId) {
|
|
33840
|
-
|
|
33841
|
-
const sid = sessionId.trim();
|
|
33842
|
-
const cached2 = this.sessionRepoCheckoutPaths.get(sid);
|
|
33843
|
-
if (cached2?.length) return [...cached2];
|
|
33844
|
-
const disc = this.tryDiscoverFromDisk(sid);
|
|
33845
|
-
if (disc?.repoCheckoutPaths.length) {
|
|
33846
|
-
this.rememberSessionWorktrees(sid, disc);
|
|
33847
|
-
return [...disc.repoCheckoutPaths];
|
|
33848
|
-
}
|
|
33849
|
-
return void 0;
|
|
34028
|
+
return ensureRepoCheckoutPathsForSession(sessionId, this.cache, (sid) => this.discover(sid));
|
|
33850
34029
|
}
|
|
33851
|
-
/** Session parent directory when in worktrees mode; null otherwise (same as {@link getIsolatedSessionParentPathForSession} path). */
|
|
33852
34030
|
getSessionWorktreeRootForSession(sessionId) {
|
|
33853
34031
|
return this.getIsolatedSessionParentPathForSession(sessionId);
|
|
33854
34032
|
}
|
|
33855
34033
|
async removeSessionWorktrees(sessionId) {
|
|
33856
|
-
const paths = this.
|
|
33857
|
-
this.sessionRepoCheckoutPaths.delete(sessionId);
|
|
33858
|
-
this.sessionParentPathBySession.delete(sessionId);
|
|
33859
|
-
this.sessionWorkingTreeRelRootBySession.delete(sessionId);
|
|
34034
|
+
const paths = this.cache.clearSession(sessionId);
|
|
33860
34035
|
if (!paths?.length) return;
|
|
33861
34036
|
await removeSessionWorktrees(paths, this.log);
|
|
33862
34037
|
}
|
|
33863
34038
|
async commitSession(params) {
|
|
33864
|
-
const paths = this.
|
|
34039
|
+
const paths = this.cache.getRepoCheckoutPathsRef(params.sessionId);
|
|
33865
34040
|
const targets = paths?.length ? paths : [getBridgeRoot()];
|
|
33866
34041
|
return commitSessionWorktrees({
|
|
33867
34042
|
paths: targets,
|
|
@@ -33870,28 +34045,17 @@ var SessionWorktreeManager = class {
|
|
|
33870
34045
|
push: params.push
|
|
33871
34046
|
});
|
|
33872
34047
|
}
|
|
33873
|
-
resolveCommitTargets(sessionId) {
|
|
33874
|
-
const paths = this.sessionRepoCheckoutPaths.get(sessionId);
|
|
33875
|
-
if (paths?.length) return paths;
|
|
33876
|
-
const disc = this.tryDiscoverFromDisk(sessionId);
|
|
33877
|
-
if (disc?.repoCheckoutPaths.length) {
|
|
33878
|
-
this.rememberSessionWorktrees(sessionId, disc);
|
|
33879
|
-
return disc.repoCheckoutPaths;
|
|
33880
|
-
}
|
|
33881
|
-
return [getBridgeRoot()];
|
|
33882
|
-
}
|
|
33883
34048
|
async getSessionWorkingTreeStatus(sessionId) {
|
|
33884
|
-
return aggregateSessionPathsWorkingTreeStatus(
|
|
34049
|
+
return aggregateSessionPathsWorkingTreeStatus(
|
|
34050
|
+
resolveCommitTargets(sessionId, this.cache, (sid) => this.discover(sid))
|
|
34051
|
+
);
|
|
33885
34052
|
}
|
|
33886
|
-
/** Per-repo changed files vs HEAD (or a single commit vs parent) for the same git roots used for commit/push. */
|
|
33887
34053
|
async getSessionWorkingTreeChangeDetails(sessionId, opts) {
|
|
33888
|
-
const targets = this.
|
|
33889
|
-
const sessionWorkingTreeRelRoot = this.sessionWorkingTreeRelRootBySession.get(sessionId) ?? null;
|
|
33890
|
-
const legacyNested = this.isLegacyNestedLayout(sessionId);
|
|
34054
|
+
const targets = resolveCommitTargets(sessionId, this.cache, (sid) => this.discover(sid));
|
|
33891
34055
|
return getWorkingTreeChangeRepoDetails({
|
|
33892
34056
|
commitTargetPaths: targets,
|
|
33893
|
-
sessionWorktreeRootPath:
|
|
33894
|
-
legacyRepoNestedSessionLayout:
|
|
34057
|
+
sessionWorktreeRootPath: this.cache.getWorkingTreeRelRoot(sessionId),
|
|
34058
|
+
legacyRepoNestedSessionLayout: this.cache.isLegacyNestedLayout(sessionId),
|
|
33895
34059
|
repoFilterRelPath: opts?.repoRelPath?.trim() ? opts.repoRelPath.trim() : null,
|
|
33896
34060
|
basis: opts?.basis,
|
|
33897
34061
|
recentCommitsLimit: opts?.recentCommitsLimit
|
|
@@ -33899,7 +34063,9 @@ var SessionWorktreeManager = class {
|
|
|
33899
34063
|
}
|
|
33900
34064
|
async pushSessionUpstream(sessionId) {
|
|
33901
34065
|
try {
|
|
33902
|
-
await pushAheadOfUpstreamForPaths(
|
|
34066
|
+
await pushAheadOfUpstreamForPaths(
|
|
34067
|
+
resolveCommitTargets(sessionId, this.cache, (sid) => this.discover(sid))
|
|
34068
|
+
);
|
|
33903
34069
|
return { ok: true };
|
|
33904
34070
|
} catch (e) {
|
|
33905
34071
|
const err = e instanceof Error ? e.message : String(e);
|
|
@@ -33907,27 +34073,31 @@ var SessionWorktreeManager = class {
|
|
|
33907
34073
|
}
|
|
33908
34074
|
}
|
|
33909
34075
|
};
|
|
34076
|
+
|
|
34077
|
+
// src/worktrees/manager/default-worktrees-root-path.ts
|
|
34078
|
+
import * as path27 from "node:path";
|
|
34079
|
+
import os8 from "node:os";
|
|
33910
34080
|
function defaultWorktreesRootPath() {
|
|
33911
|
-
return
|
|
34081
|
+
return path27.join(os8.homedir(), ".buildautomaton", "worktrees");
|
|
33912
34082
|
}
|
|
33913
34083
|
|
|
33914
34084
|
// src/files/watch-file-index.ts
|
|
33915
34085
|
import { watch } from "node:fs";
|
|
33916
|
-
import
|
|
34086
|
+
import path32 from "node:path";
|
|
33917
34087
|
|
|
33918
34088
|
// src/files/index/paths.ts
|
|
33919
|
-
import
|
|
34089
|
+
import path28 from "node:path";
|
|
33920
34090
|
import crypto2 from "node:crypto";
|
|
33921
34091
|
function getCwdHashForFileIndex(resolvedCwd) {
|
|
33922
|
-
return crypto2.createHash("sha256").update(
|
|
34092
|
+
return crypto2.createHash("sha256").update(path28.resolve(resolvedCwd)).digest("hex").slice(0, INDEX_HASH_LEN);
|
|
33923
34093
|
}
|
|
33924
34094
|
|
|
33925
34095
|
// src/files/index/build-file-index.ts
|
|
33926
|
-
import
|
|
34096
|
+
import path30 from "node:path";
|
|
33927
34097
|
|
|
33928
34098
|
// src/files/index/walk-workspace-tree.ts
|
|
33929
34099
|
import fs24 from "node:fs";
|
|
33930
|
-
import
|
|
34100
|
+
import path29 from "node:path";
|
|
33931
34101
|
var DEPENDENCY_INSTALL_DIR_NAMES = /* @__PURE__ */ new Set([
|
|
33932
34102
|
"node_modules",
|
|
33933
34103
|
"bower_components",
|
|
@@ -33956,18 +34126,18 @@ async function walkWorkspaceTreeAsync(dir, baseDir, onFile, state) {
|
|
|
33956
34126
|
if (isCliImmediateShutdownRequested()) throw new CliSqliteInterrupted();
|
|
33957
34127
|
}
|
|
33958
34128
|
state.n++;
|
|
33959
|
-
const full =
|
|
34129
|
+
const full = path29.join(dir, name);
|
|
33960
34130
|
let stat2;
|
|
33961
34131
|
try {
|
|
33962
34132
|
stat2 = await fs24.promises.stat(full);
|
|
33963
34133
|
} catch {
|
|
33964
34134
|
continue;
|
|
33965
34135
|
}
|
|
33966
|
-
const
|
|
34136
|
+
const relative6 = path29.relative(baseDir, full).replace(/\\/g, "/");
|
|
33967
34137
|
if (stat2.isDirectory()) {
|
|
33968
34138
|
await walkWorkspaceTreeAsync(full, baseDir, onFile, state);
|
|
33969
34139
|
} else if (stat2.isFile()) {
|
|
33970
|
-
onFile(
|
|
34140
|
+
onFile(relative6);
|
|
33971
34141
|
}
|
|
33972
34142
|
}
|
|
33973
34143
|
}
|
|
@@ -34065,7 +34235,7 @@ async function collectWorkspacePathsAsync(resolved) {
|
|
|
34065
34235
|
}
|
|
34066
34236
|
async function buildFileIndexAsync(cwd) {
|
|
34067
34237
|
return withFileIndexSqliteLock(async () => {
|
|
34068
|
-
const resolved =
|
|
34238
|
+
const resolved = path30.resolve(cwd);
|
|
34069
34239
|
await yieldToEventLoop();
|
|
34070
34240
|
assertNotShutdown();
|
|
34071
34241
|
const paths = await collectWorkspacePathsAsync(resolved);
|
|
@@ -34077,7 +34247,7 @@ async function buildFileIndexAsync(cwd) {
|
|
|
34077
34247
|
}
|
|
34078
34248
|
|
|
34079
34249
|
// src/files/index/ensure-file-index.ts
|
|
34080
|
-
import
|
|
34250
|
+
import path31 from "node:path";
|
|
34081
34251
|
|
|
34082
34252
|
// src/files/index/search-file-index.ts
|
|
34083
34253
|
function escapeLikePattern(fragment) {
|
|
@@ -34129,7 +34299,7 @@ async function searchBridgeFilePathsAsync(resolvedCwd, query, limit = 100) {
|
|
|
34129
34299
|
|
|
34130
34300
|
// src/files/index/ensure-file-index.ts
|
|
34131
34301
|
async function ensureFileIndexAsync(cwd) {
|
|
34132
|
-
const resolved =
|
|
34302
|
+
const resolved = path31.resolve(cwd);
|
|
34133
34303
|
if (await bridgeFileIndexIsPopulated(resolved)) {
|
|
34134
34304
|
return { fromCache: true, pathCount: await bridgeFileIndexPathCount(resolved) };
|
|
34135
34305
|
}
|
|
@@ -34173,7 +34343,7 @@ function createFsWatcher(resolved, schedule) {
|
|
|
34173
34343
|
}
|
|
34174
34344
|
}
|
|
34175
34345
|
function startFileIndexWatcher(cwd = getBridgeRoot()) {
|
|
34176
|
-
const resolved =
|
|
34346
|
+
const resolved = path32.resolve(cwd);
|
|
34177
34347
|
void buildFileIndexAsync(resolved).catch((e) => {
|
|
34178
34348
|
if (e instanceof CliSqliteInterrupted) return;
|
|
34179
34349
|
console.error("[file-index] Initial index build failed:", e);
|
|
@@ -34203,7 +34373,7 @@ function startFileIndexWatcher(cwd = getBridgeRoot()) {
|
|
|
34203
34373
|
}
|
|
34204
34374
|
|
|
34205
34375
|
// src/connection/create-bridge-connection.ts
|
|
34206
|
-
import * as
|
|
34376
|
+
import * as path43 from "node:path";
|
|
34207
34377
|
|
|
34208
34378
|
// src/dev-servers/manager/dev-server-manager.ts
|
|
34209
34379
|
import { rm as rm2 } from "node:fs/promises";
|
|
@@ -34225,15 +34395,15 @@ function sendDevServerStatus(getWs, serverId, status, options) {
|
|
|
34225
34395
|
|
|
34226
34396
|
// src/dev-servers/process/terminate-child-process.ts
|
|
34227
34397
|
async function sigtermAndWaitForExit(proc, graceMs, log2, shortId) {
|
|
34228
|
-
const exited = new Promise((
|
|
34229
|
-
proc.once("exit", () =>
|
|
34398
|
+
const exited = new Promise((resolve20) => {
|
|
34399
|
+
proc.once("exit", () => resolve20());
|
|
34230
34400
|
});
|
|
34231
34401
|
log2(`[dev-server] Sending SIGTERM to ${shortId} (pid=${proc.pid ?? "?"}).`);
|
|
34232
34402
|
try {
|
|
34233
34403
|
proc.kill("SIGTERM");
|
|
34234
34404
|
} catch {
|
|
34235
34405
|
}
|
|
34236
|
-
await Promise.race([exited, new Promise((
|
|
34406
|
+
await Promise.race([exited, new Promise((resolve20) => setTimeout(resolve20, graceMs))]);
|
|
34237
34407
|
}
|
|
34238
34408
|
function forceKillChild(proc, log2, shortId, graceMs) {
|
|
34239
34409
|
log2(
|
|
@@ -34513,10 +34683,10 @@ function trySpawnShellTruePiped(command, env, cwd, devNullFd, signal) {
|
|
|
34513
34683
|
import { spawn as spawn8 } from "node:child_process";
|
|
34514
34684
|
import fs28 from "node:fs";
|
|
34515
34685
|
import { tmpdir } from "node:os";
|
|
34516
|
-
import
|
|
34686
|
+
import path33 from "node:path";
|
|
34517
34687
|
function trySpawnMergedLogFile(command, env, cwd, signal) {
|
|
34518
|
-
const tmpRoot = fs28.mkdtempSync(
|
|
34519
|
-
const logPath =
|
|
34688
|
+
const tmpRoot = fs28.mkdtempSync(path33.join(tmpdir(), "ba-devsrv-log-"));
|
|
34689
|
+
const logPath = path33.join(tmpRoot, "combined.log");
|
|
34520
34690
|
let logFd;
|
|
34521
34691
|
try {
|
|
34522
34692
|
logFd = fs28.openSync(logPath, "a");
|
|
@@ -34560,15 +34730,15 @@ function trySpawnMergedLogFile(command, env, cwd, signal) {
|
|
|
34560
34730
|
import { spawn as spawn9 } from "node:child_process";
|
|
34561
34731
|
import fs29 from "node:fs";
|
|
34562
34732
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
34563
|
-
import
|
|
34733
|
+
import path34 from "node:path";
|
|
34564
34734
|
function shSingleQuote(s) {
|
|
34565
34735
|
return `'${s.replace(/'/g, `'\\''`)}'`;
|
|
34566
34736
|
}
|
|
34567
34737
|
function trySpawnShellScriptLogRedirectUnix(command, env, cwd, signal) {
|
|
34568
|
-
const tmpRoot = fs29.mkdtempSync(
|
|
34569
|
-
const logPath =
|
|
34570
|
-
const innerPath =
|
|
34571
|
-
const runnerPath =
|
|
34738
|
+
const tmpRoot = fs29.mkdtempSync(path34.join(tmpdir2(), "ba-devsrv-sh-"));
|
|
34739
|
+
const logPath = path34.join(tmpRoot, "combined.log");
|
|
34740
|
+
const innerPath = path34.join(tmpRoot, "_cmd.sh");
|
|
34741
|
+
const runnerPath = path34.join(tmpRoot, "_run.sh");
|
|
34572
34742
|
try {
|
|
34573
34743
|
fs29.writeFileSync(innerPath, `#!/bin/sh
|
|
34574
34744
|
${command}
|
|
@@ -34599,9 +34769,9 @@ cd ${shSingleQuote(cwd)}
|
|
|
34599
34769
|
}
|
|
34600
34770
|
}
|
|
34601
34771
|
function trySpawnShellScriptLogRedirectWin(command, env, cwd, signal) {
|
|
34602
|
-
const tmpRoot = fs29.mkdtempSync(
|
|
34603
|
-
const logPath =
|
|
34604
|
-
const runnerPath =
|
|
34772
|
+
const tmpRoot = fs29.mkdtempSync(path34.join(tmpdir2(), "ba-devsrv-sh-"));
|
|
34773
|
+
const logPath = path34.join(tmpRoot, "combined.log");
|
|
34774
|
+
const runnerPath = path34.join(tmpRoot, "_run.bat");
|
|
34605
34775
|
const q = (p) => `"${p.replace(/"/g, '""')}"`;
|
|
34606
34776
|
const com = process.env.ComSpec || "cmd.exe";
|
|
34607
34777
|
try {
|
|
@@ -35375,13 +35545,13 @@ function createOnBridgeIdentified(opts) {
|
|
|
35375
35545
|
|
|
35376
35546
|
// src/skills/discover-local-agent-skills.ts
|
|
35377
35547
|
import fs30 from "node:fs";
|
|
35378
|
-
import
|
|
35548
|
+
import path35 from "node:path";
|
|
35379
35549
|
var SKILL_DISCOVERY_ROOTS = [".agents/skills", ".claude/skills", ".cursor/skills", "skills"];
|
|
35380
35550
|
function discoverLocalSkills(cwd) {
|
|
35381
35551
|
const out = [];
|
|
35382
35552
|
const seenKeys = /* @__PURE__ */ new Set();
|
|
35383
35553
|
for (const rel of SKILL_DISCOVERY_ROOTS) {
|
|
35384
|
-
const base =
|
|
35554
|
+
const base = path35.join(cwd, rel);
|
|
35385
35555
|
if (!fs30.existsSync(base) || !fs30.statSync(base).isDirectory()) continue;
|
|
35386
35556
|
let entries = [];
|
|
35387
35557
|
try {
|
|
@@ -35390,13 +35560,13 @@ function discoverLocalSkills(cwd) {
|
|
|
35390
35560
|
continue;
|
|
35391
35561
|
}
|
|
35392
35562
|
for (const name of entries) {
|
|
35393
|
-
const dir =
|
|
35563
|
+
const dir = path35.join(base, name);
|
|
35394
35564
|
try {
|
|
35395
35565
|
if (!fs30.statSync(dir).isDirectory()) continue;
|
|
35396
35566
|
} catch {
|
|
35397
35567
|
continue;
|
|
35398
35568
|
}
|
|
35399
|
-
const skillMd =
|
|
35569
|
+
const skillMd = path35.join(dir, "SKILL.md");
|
|
35400
35570
|
if (!fs30.existsSync(skillMd)) continue;
|
|
35401
35571
|
const key = `${rel}/${name}`;
|
|
35402
35572
|
if (seenKeys.has(key)) continue;
|
|
@@ -35409,7 +35579,7 @@ function discoverLocalSkills(cwd) {
|
|
|
35409
35579
|
function discoverSkillLayoutRoots(cwd) {
|
|
35410
35580
|
const roots = [];
|
|
35411
35581
|
for (const rel of SKILL_DISCOVERY_ROOTS) {
|
|
35412
|
-
const base =
|
|
35582
|
+
const base = path35.join(cwd, rel);
|
|
35413
35583
|
if (!fs30.existsSync(base) || !fs30.statSync(base).isDirectory()) continue;
|
|
35414
35584
|
let entries = [];
|
|
35415
35585
|
try {
|
|
@@ -35419,13 +35589,13 @@ function discoverSkillLayoutRoots(cwd) {
|
|
|
35419
35589
|
}
|
|
35420
35590
|
const skills2 = [];
|
|
35421
35591
|
for (const name of entries) {
|
|
35422
|
-
const dir =
|
|
35592
|
+
const dir = path35.join(base, name);
|
|
35423
35593
|
try {
|
|
35424
35594
|
if (!fs30.statSync(dir).isDirectory()) continue;
|
|
35425
35595
|
} catch {
|
|
35426
35596
|
continue;
|
|
35427
35597
|
}
|
|
35428
|
-
if (!fs30.existsSync(
|
|
35598
|
+
if (!fs30.existsSync(path35.join(dir, "SKILL.md"))) continue;
|
|
35429
35599
|
const relPath = `${rel}/${name}`.replace(/\\/g, "/");
|
|
35430
35600
|
skills2.push({ name, relPath });
|
|
35431
35601
|
}
|
|
@@ -35548,7 +35718,9 @@ var API_TO_BRIDGE_MESSAGE_TYPES = [
|
|
|
35548
35718
|
"file_browser_search",
|
|
35549
35719
|
"skill_layout_request",
|
|
35550
35720
|
"install_skills",
|
|
35551
|
-
"refresh_local_skills"
|
|
35721
|
+
"refresh_local_skills",
|
|
35722
|
+
"bridge_git_context_request",
|
|
35723
|
+
"list_repo_branches_request"
|
|
35552
35724
|
];
|
|
35553
35725
|
var API_TO_BRIDGE_TYPE_SET = new Set(API_TO_BRIDGE_MESSAGE_TYPES);
|
|
35554
35726
|
function parseApiToBridgeMessage(data, log2) {
|
|
@@ -35632,9 +35804,6 @@ var handleAgentConfigMessage = (msg, deps) => {
|
|
|
35632
35804
|
handleBridgeAgentConfig(msg, deps);
|
|
35633
35805
|
};
|
|
35634
35806
|
|
|
35635
|
-
// src/prompt-turn-queue/runner.ts
|
|
35636
|
-
import fs31 from "node:fs";
|
|
35637
|
-
|
|
35638
35807
|
// src/prompt-turn-queue/client-report.ts
|
|
35639
35808
|
function sendPromptQueueClientReport(ws, queues) {
|
|
35640
35809
|
if (!ws) return false;
|
|
@@ -35704,8 +35873,36 @@ async function mergeServerQueueSnapshot(queueKey, serverTurns) {
|
|
|
35704
35873
|
return { queueKey, updatedAt: (/* @__PURE__ */ new Date()).toISOString(), turns };
|
|
35705
35874
|
}
|
|
35706
35875
|
|
|
35707
|
-
// src/prompt-turn-queue/runner.ts
|
|
35708
|
-
|
|
35876
|
+
// src/prompt-turn-queue/runner/dispatch-local-prompt.ts
|
|
35877
|
+
function dispatchLocalPrompt(next, deps) {
|
|
35878
|
+
const pl = next.payload;
|
|
35879
|
+
const rawParent = pl["sessionParent"];
|
|
35880
|
+
const sessionParent = rawParent === "bridge_root" || rawParent === "worktrees_root" ? rawParent : void 0;
|
|
35881
|
+
const rawParentPath = pl["sessionParentPath"];
|
|
35882
|
+
const sessionParentPath = typeof rawParentPath === "string" && rawParentPath.trim() !== "" ? rawParentPath.trim() : void 0;
|
|
35883
|
+
const rawBaseBranches = pl["worktreeBaseBranches"];
|
|
35884
|
+
const worktreeBaseBranches = rawBaseBranches != null && typeof rawBaseBranches === "object" && !Array.isArray(rawBaseBranches) ? rawBaseBranches : void 0;
|
|
35885
|
+
const msg = {
|
|
35886
|
+
type: "prompt",
|
|
35887
|
+
sessionId: next.sessionId,
|
|
35888
|
+
runId: next.turnId,
|
|
35889
|
+
prompt: pl.prompt,
|
|
35890
|
+
mode: typeof pl.mode === "string" ? pl.mode : "agent",
|
|
35891
|
+
isNewSession: pl.isNewSession === true,
|
|
35892
|
+
...sessionParent ? { sessionParent } : {},
|
|
35893
|
+
...sessionParentPath ? { sessionParentPath } : {},
|
|
35894
|
+
...worktreeBaseBranches && Object.keys(worktreeBaseBranches).length > 0 ? { worktreeBaseBranches } : {},
|
|
35895
|
+
...typeof pl.followUpCatalogPromptId === "string" ? { followUpCatalogPromptId: pl.followUpCatalogPromptId } : {},
|
|
35896
|
+
...Array.isArray(pl.sessionChangeSummaryFilePaths) ? { sessionChangeSummaryFilePaths: pl.sessionChangeSummaryFilePaths } : {},
|
|
35897
|
+
...Array.isArray(pl.sessionChangeSummaryFileSnapshots) ? { sessionChangeSummaryFileSnapshots: pl.sessionChangeSummaryFileSnapshots } : {},
|
|
35898
|
+
...typeof pl.agentType === "string" && pl.agentType.trim() ? { agentType: pl.agentType.trim() } : {},
|
|
35899
|
+
...pl.agentConfig != null && typeof pl.agentConfig === "object" && !Array.isArray(pl.agentConfig) && Object.keys(pl.agentConfig).length > 0 ? { agentConfig: pl.agentConfig } : {},
|
|
35900
|
+
...Array.isArray(pl.attachments) && pl.attachments.length > 0 ? { attachments: pl.attachments } : {}
|
|
35901
|
+
};
|
|
35902
|
+
handleBridgePrompt(msg, deps);
|
|
35903
|
+
}
|
|
35904
|
+
|
|
35905
|
+
// src/prompt-turn-queue/runner/queue-selection.ts
|
|
35709
35906
|
function isRunnableServerState(s) {
|
|
35710
35907
|
return s === "queued" || s === "requeued" || s === "requeued_with_revert";
|
|
35711
35908
|
}
|
|
@@ -35724,6 +35921,28 @@ function pickNextRunnableTurn(turns) {
|
|
|
35724
35921
|
function hasRunningTurn(turns) {
|
|
35725
35922
|
return turns.some((t) => t.lastClientState === "running");
|
|
35726
35923
|
}
|
|
35924
|
+
|
|
35925
|
+
// src/prompt-turn-queue/runner/run-id-queue-key-map.ts
|
|
35926
|
+
var runIdToQueueKey = /* @__PURE__ */ new Map();
|
|
35927
|
+
function getRunIdQueueKey(runId) {
|
|
35928
|
+
return runIdToQueueKey.get(runId);
|
|
35929
|
+
}
|
|
35930
|
+
function setRunIdQueueKey(runId, queueKey) {
|
|
35931
|
+
runIdToQueueKey.set(runId, queueKey);
|
|
35932
|
+
}
|
|
35933
|
+
function deleteRunIdQueueKey(runId) {
|
|
35934
|
+
const queueKey = runIdToQueueKey.get(runId);
|
|
35935
|
+
runIdToQueueKey.delete(runId);
|
|
35936
|
+
return queueKey;
|
|
35937
|
+
}
|
|
35938
|
+
function syncRunningTurnQueueKeys(turns, queueKey) {
|
|
35939
|
+
for (const running of turns.filter((t) => t.lastClientState === "running")) {
|
|
35940
|
+
runIdToQueueKey.set(running.turnId, queueKey);
|
|
35941
|
+
}
|
|
35942
|
+
}
|
|
35943
|
+
|
|
35944
|
+
// src/prompt-turn-queue/runner/run-local-revert-before-queued-prompt.ts
|
|
35945
|
+
import fs31 from "node:fs";
|
|
35727
35946
|
async function runLocalRevertBeforeQueuedPrompt(next, deps) {
|
|
35728
35947
|
if (next.serverState !== "requeued_with_revert") return true;
|
|
35729
35948
|
const sid = next.sessionId;
|
|
@@ -35745,30 +35964,23 @@ async function runLocalRevertBeforeQueuedPrompt(next, deps) {
|
|
|
35745
35964
|
}
|
|
35746
35965
|
return res.ok;
|
|
35747
35966
|
}
|
|
35748
|
-
|
|
35749
|
-
|
|
35750
|
-
|
|
35751
|
-
|
|
35752
|
-
const
|
|
35753
|
-
|
|
35754
|
-
const
|
|
35755
|
-
|
|
35756
|
-
|
|
35757
|
-
|
|
35758
|
-
|
|
35759
|
-
|
|
35760
|
-
|
|
35761
|
-
|
|
35762
|
-
...sessionParentPath ? { sessionParentPath } : {},
|
|
35763
|
-
...typeof pl.followUpCatalogPromptId === "string" ? { followUpCatalogPromptId: pl.followUpCatalogPromptId } : {},
|
|
35764
|
-
...Array.isArray(pl.sessionChangeSummaryFilePaths) ? { sessionChangeSummaryFilePaths: pl.sessionChangeSummaryFilePaths } : {},
|
|
35765
|
-
...Array.isArray(pl.sessionChangeSummaryFileSnapshots) ? { sessionChangeSummaryFileSnapshots: pl.sessionChangeSummaryFileSnapshots } : {},
|
|
35766
|
-
...typeof pl.agentType === "string" && pl.agentType.trim() ? { agentType: pl.agentType.trim() } : {},
|
|
35767
|
-
...pl.agentConfig != null && typeof pl.agentConfig === "object" && !Array.isArray(pl.agentConfig) && Object.keys(pl.agentConfig).length > 0 ? { agentConfig: pl.agentConfig } : {},
|
|
35768
|
-
...Array.isArray(pl.attachments) && pl.attachments.length > 0 ? { attachments: pl.attachments } : {}
|
|
35769
|
-
};
|
|
35770
|
-
handleBridgePrompt(msg, deps);
|
|
35967
|
+
|
|
35968
|
+
// src/prompt-turn-queue/runner/finalize-prompt-turn-on-bridge.ts
|
|
35969
|
+
async function finalizePromptTurnOnBridge(getWs, runId, success2, opts) {
|
|
35970
|
+
if (!runId) return false;
|
|
35971
|
+
const queueKey = deleteRunIdQueueKey(runId);
|
|
35972
|
+
if (!queueKey) return false;
|
|
35973
|
+
const f = await readPersistedQueue(queueKey);
|
|
35974
|
+
if (!f) return false;
|
|
35975
|
+
const t = f.turns.find((x) => x.turnId === runId);
|
|
35976
|
+
if (!t) return false;
|
|
35977
|
+
t.lastClientState = opts?.terminalClientState ?? (success2 ? "stopped" : "failed");
|
|
35978
|
+
await writePersistedQueue(f);
|
|
35979
|
+
sendPromptQueueClientReport(getWs(), { [queueKey]: [{ turnId: runId, clientState: t.lastClientState }] });
|
|
35980
|
+
return true;
|
|
35771
35981
|
}
|
|
35982
|
+
|
|
35983
|
+
// src/prompt-turn-queue/runner/apply-prompt-queue-state-from-server.ts
|
|
35772
35984
|
async function applyPromptQueueStateFromServer(msg, deps) {
|
|
35773
35985
|
const raw = msg.queues;
|
|
35774
35986
|
if (!raw || typeof raw !== "object") return;
|
|
@@ -35782,9 +35994,7 @@ async function applyPromptQueueStateFromServer(msg, deps) {
|
|
|
35782
35994
|
if (!Array.isArray(serverTurns)) continue;
|
|
35783
35995
|
const file2 = await readPersistedQueue(queueKey);
|
|
35784
35996
|
if (!file2) continue;
|
|
35785
|
-
|
|
35786
|
-
runIdToQueueKey.set(running.turnId, queueKey);
|
|
35787
|
-
}
|
|
35997
|
+
syncRunningTurnQueueKeys(file2.turns, queueKey);
|
|
35788
35998
|
}
|
|
35789
35999
|
for (const [queueKey, serverTurns] of Object.entries(raw)) {
|
|
35790
36000
|
if (!Array.isArray(serverTurns)) continue;
|
|
@@ -35829,7 +36039,7 @@ async function applyPromptQueueStateFromServer(msg, deps) {
|
|
|
35829
36039
|
}
|
|
35830
36040
|
next.lastClientState = "running";
|
|
35831
36041
|
await writePersistedQueue(file2);
|
|
35832
|
-
|
|
36042
|
+
setRunIdQueueKey(next.turnId, queueKey);
|
|
35833
36043
|
startedThisTick.add(next.turnId);
|
|
35834
36044
|
report[queueKey] = [{ turnId: next.turnId, clientState: "running" }];
|
|
35835
36045
|
}
|
|
@@ -35842,24 +36052,10 @@ async function applyPromptQueueStateFromServer(msg, deps) {
|
|
|
35842
36052
|
if (!file2) continue;
|
|
35843
36053
|
const running = file2.turns.find((t) => t.lastClientState === "running");
|
|
35844
36054
|
if (!running || !startedThisTick.has(running.turnId)) continue;
|
|
35845
|
-
if (
|
|
36055
|
+
if (getRunIdQueueKey(running.turnId) !== queueKey) continue;
|
|
35846
36056
|
dispatchLocalPrompt(running, deps);
|
|
35847
36057
|
}
|
|
35848
36058
|
}
|
|
35849
|
-
async function finalizePromptTurnOnBridge(getWs, runId, success2, opts) {
|
|
35850
|
-
if (!runId) return false;
|
|
35851
|
-
const queueKey = runIdToQueueKey.get(runId);
|
|
35852
|
-
runIdToQueueKey.delete(runId);
|
|
35853
|
-
if (!queueKey) return false;
|
|
35854
|
-
const f = await readPersistedQueue(queueKey);
|
|
35855
|
-
if (!f) return false;
|
|
35856
|
-
const t = f.turns.find((x) => x.turnId === runId);
|
|
35857
|
-
if (!t) return false;
|
|
35858
|
-
t.lastClientState = opts?.terminalClientState ?? (success2 ? "stopped" : "failed");
|
|
35859
|
-
await writePersistedQueue(f);
|
|
35860
|
-
sendPromptQueueClientReport(getWs(), { [queueKey]: [{ turnId: runId, clientState: t.lastClientState }] });
|
|
35861
|
-
return true;
|
|
35862
|
-
}
|
|
35863
36059
|
|
|
35864
36060
|
// src/agents/acp/from-bridge/bridge-prompt-wiring.ts
|
|
35865
36061
|
function createBridgePromptSenders(deps, getWs) {
|
|
@@ -35906,6 +36102,91 @@ function createBridgePromptSenders(deps, getWs) {
|
|
|
35906
36102
|
return { sendBridgeMessage, sendResult: sendResult2, sendSessionUpdate };
|
|
35907
36103
|
}
|
|
35908
36104
|
|
|
36105
|
+
// src/agents/acp/from-bridge/handle-bridge-prompt/parse-bridge-attachments.ts
|
|
36106
|
+
function parseBridgeAttachments(msg) {
|
|
36107
|
+
const raw = msg.attachments;
|
|
36108
|
+
if (!Array.isArray(raw)) return [];
|
|
36109
|
+
const out = [];
|
|
36110
|
+
for (const x of raw) {
|
|
36111
|
+
if (x === null || typeof x !== "object" || Array.isArray(x)) continue;
|
|
36112
|
+
const o = x;
|
|
36113
|
+
const id = typeof o.attachmentId === "string" ? o.attachmentId.trim() : "";
|
|
36114
|
+
if (!id) continue;
|
|
36115
|
+
const mt = typeof o.mimeType === "string" && o.mimeType.trim() ? o.mimeType.trim() : "application/octet-stream";
|
|
36116
|
+
out.push({ attachmentId: id, mimeType: mt });
|
|
36117
|
+
}
|
|
36118
|
+
return out;
|
|
36119
|
+
}
|
|
36120
|
+
|
|
36121
|
+
// src/agents/acp/from-bridge/handle-bridge-prompt/parse-worktree-base-branches.ts
|
|
36122
|
+
function parseWorktreeBaseBranches(msg) {
|
|
36123
|
+
const raw = msg.worktreeBaseBranches;
|
|
36124
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return void 0;
|
|
36125
|
+
const out = {};
|
|
36126
|
+
for (const [k, v] of Object.entries(raw)) {
|
|
36127
|
+
if (typeof k !== "string" || typeof v !== "string") continue;
|
|
36128
|
+
const rel = k.trim();
|
|
36129
|
+
const branch = v.trim();
|
|
36130
|
+
if (rel && branch) out[rel] = branch;
|
|
36131
|
+
}
|
|
36132
|
+
return Object.keys(out).length > 0 ? out : void 0;
|
|
36133
|
+
}
|
|
36134
|
+
|
|
36135
|
+
// src/agents/acp/change-summary/decrypt-change-summary-file-input.ts
|
|
36136
|
+
function decryptChangeSummaryFileInput(row, e2ee) {
|
|
36137
|
+
if (!e2ee) return row;
|
|
36138
|
+
for (const field of ["path", "patchContent", "oldText", "newText"]) {
|
|
36139
|
+
const raw = row[field];
|
|
36140
|
+
if (typeof raw !== "string" || raw.trim() === "") continue;
|
|
36141
|
+
let o;
|
|
36142
|
+
try {
|
|
36143
|
+
o = JSON.parse(raw);
|
|
36144
|
+
} catch {
|
|
36145
|
+
continue;
|
|
36146
|
+
}
|
|
36147
|
+
if (!isE2eeEnvelope(o.ee)) continue;
|
|
36148
|
+
try {
|
|
36149
|
+
const d = e2ee.decryptMessage(o);
|
|
36150
|
+
const out = {
|
|
36151
|
+
path: typeof d.path === "string" ? d.path : row.path
|
|
36152
|
+
};
|
|
36153
|
+
if (d.directoryRemoved === true) out.directoryRemoved = true;
|
|
36154
|
+
else if (row.directoryRemoved === true) out.directoryRemoved = true;
|
|
36155
|
+
if (typeof d.patchContent === "string") out.patchContent = d.patchContent;
|
|
36156
|
+
else if (typeof row.patchContent === "string" && row.patchContent !== raw) out.patchContent = row.patchContent;
|
|
36157
|
+
if (typeof d.oldText === "string") out.oldText = d.oldText;
|
|
36158
|
+
else if (typeof row.oldText === "string") out.oldText = row.oldText;
|
|
36159
|
+
if (typeof d.newText === "string") out.newText = d.newText;
|
|
36160
|
+
else if (typeof row.newText === "string") out.newText = row.newText;
|
|
36161
|
+
return out;
|
|
36162
|
+
} catch {
|
|
36163
|
+
return row;
|
|
36164
|
+
}
|
|
36165
|
+
}
|
|
36166
|
+
return row;
|
|
36167
|
+
}
|
|
36168
|
+
|
|
36169
|
+
// src/agents/acp/change-summary/resolve-change-summary-prompt-for-agent.ts
|
|
36170
|
+
function hasSummarizePayload(f) {
|
|
36171
|
+
return f.directoryRemoved === true || f.patchContent != null && f.patchContent.trim() !== "" || f.oldText != null && f.oldText.trim() !== "" || f.newText != null && f.newText.trim() !== "";
|
|
36172
|
+
}
|
|
36173
|
+
function resolveChangeSummaryPromptForAgent(params) {
|
|
36174
|
+
const isBuiltin = params.followUpCatalogPromptId === BUILTIN_SESSION_CHANGE_SUMMARY_FOLLOW_UP_CATALOG_PROMPT_ID;
|
|
36175
|
+
const snaps = params.sessionChangeSummaryFileSnapshots;
|
|
36176
|
+
if (!isBuiltin || !snaps || snaps.length === 0) {
|
|
36177
|
+
return { promptText: params.bridgePromptText, sessionChangeSummaryFilePaths: void 0 };
|
|
36178
|
+
}
|
|
36179
|
+
const decrypted = dedupeSessionFileChangesByPath(snaps.map((row) => decryptChangeSummaryFileInput(row, params.e2ee)));
|
|
36180
|
+
const withPayload = decrypted.filter(hasSummarizePayload);
|
|
36181
|
+
if (withPayload.length === 0) {
|
|
36182
|
+
return { promptText: params.bridgePromptText, sessionChangeSummaryFilePaths: void 0 };
|
|
36183
|
+
}
|
|
36184
|
+
return {
|
|
36185
|
+
promptText: buildSessionChangeSummaryPrompt(withPayload),
|
|
36186
|
+
sessionChangeSummaryFilePaths: withPayload.map((f) => f.path)
|
|
36187
|
+
};
|
|
36188
|
+
}
|
|
36189
|
+
|
|
35909
36190
|
// src/agents/acp/from-bridge/bridge-prompt-preamble.ts
|
|
35910
36191
|
import { execFile as execFile8 } from "node:child_process";
|
|
35911
36192
|
import { promisify as promisify8 } from "node:util";
|
|
@@ -35958,9 +36239,9 @@ function parseChangeSummarySnapshots(raw) {
|
|
|
35958
36239
|
for (const item of raw) {
|
|
35959
36240
|
if (!item || typeof item !== "object") continue;
|
|
35960
36241
|
const o = item;
|
|
35961
|
-
const
|
|
35962
|
-
if (!
|
|
35963
|
-
const row = { path:
|
|
36242
|
+
const path44 = typeof o.path === "string" && o.path.trim() !== "" ? o.path.trim() : "";
|
|
36243
|
+
if (!path44) continue;
|
|
36244
|
+
const row = { path: path44 };
|
|
35964
36245
|
if (typeof o.patchContent === "string") row.patchContent = o.patchContent;
|
|
35965
36246
|
if (typeof o.oldText === "string") row.oldText = o.oldText;
|
|
35966
36247
|
if (typeof o.newText === "string") row.newText = o.newText;
|
|
@@ -35977,76 +36258,73 @@ function parseFollowUpFieldsFromPromptMessage(msg) {
|
|
|
35977
36258
|
return { followUpCatalogPromptId, sessionChangeSummaryFilePaths, sessionChangeSummaryFileSnapshots };
|
|
35978
36259
|
}
|
|
35979
36260
|
|
|
35980
|
-
// src/agents/acp/
|
|
35981
|
-
function
|
|
35982
|
-
|
|
35983
|
-
|
|
35984
|
-
|
|
35985
|
-
|
|
35986
|
-
|
|
35987
|
-
|
|
35988
|
-
|
|
35989
|
-
|
|
35990
|
-
|
|
35991
|
-
|
|
35992
|
-
|
|
35993
|
-
|
|
35994
|
-
|
|
35995
|
-
|
|
35996
|
-
|
|
35997
|
-
|
|
35998
|
-
|
|
35999
|
-
|
|
36000
|
-
|
|
36001
|
-
|
|
36002
|
-
|
|
36003
|
-
|
|
36004
|
-
|
|
36005
|
-
|
|
36006
|
-
|
|
36007
|
-
|
|
36008
|
-
|
|
36009
|
-
|
|
36010
|
-
|
|
36011
|
-
|
|
36012
|
-
}
|
|
36013
|
-
|
|
36014
|
-
|
|
36015
|
-
|
|
36016
|
-
|
|
36017
|
-
|
|
36018
|
-
|
|
36019
|
-
|
|
36020
|
-
|
|
36021
|
-
|
|
36022
|
-
|
|
36023
|
-
}
|
|
36024
|
-
const decrypted = dedupeSessionFileChangesByPath(snaps.map((row) => decryptChangeSummaryFileInput(row, params.e2ee)));
|
|
36025
|
-
const withPayload = decrypted.filter(hasSummarizePayload);
|
|
36026
|
-
if (withPayload.length === 0) {
|
|
36027
|
-
return { promptText: params.bridgePromptText, sessionChangeSummaryFilePaths: void 0 };
|
|
36261
|
+
// src/agents/acp/from-bridge/handle-bridge-prompt/run-preamble-and-prompt.ts
|
|
36262
|
+
async function runPreambleAndPrompt(params) {
|
|
36263
|
+
const {
|
|
36264
|
+
deps,
|
|
36265
|
+
msg,
|
|
36266
|
+
getWs,
|
|
36267
|
+
log: log2,
|
|
36268
|
+
sessionWorktreeManager,
|
|
36269
|
+
sessionId,
|
|
36270
|
+
runId,
|
|
36271
|
+
promptText,
|
|
36272
|
+
attachments,
|
|
36273
|
+
mode,
|
|
36274
|
+
agentType,
|
|
36275
|
+
agentId,
|
|
36276
|
+
agentConfig,
|
|
36277
|
+
resolvedCwd,
|
|
36278
|
+
senders: { sendResult: sendResult2, sendSessionUpdate }
|
|
36279
|
+
} = params;
|
|
36280
|
+
const effectiveCwd = resolveSessionParentPathForAgentProcess(resolvedCwd);
|
|
36281
|
+
await runBridgePromptPreamble({
|
|
36282
|
+
getWs,
|
|
36283
|
+
log: log2,
|
|
36284
|
+
sessionWorktreeManager,
|
|
36285
|
+
sessionId,
|
|
36286
|
+
runId,
|
|
36287
|
+
effectiveCwd
|
|
36288
|
+
});
|
|
36289
|
+
const {
|
|
36290
|
+
followUpCatalogPromptId,
|
|
36291
|
+
sessionChangeSummaryFilePaths: pathsFromBridge,
|
|
36292
|
+
sessionChangeSummaryFileSnapshots
|
|
36293
|
+
} = parseFollowUpFieldsFromPromptMessage(msg);
|
|
36294
|
+
const { promptText: resolvedPromptText, sessionChangeSummaryFilePaths } = resolveChangeSummaryPromptForAgent({
|
|
36295
|
+
followUpCatalogPromptId,
|
|
36296
|
+
sessionChangeSummaryFileSnapshots,
|
|
36297
|
+
bridgePromptText: promptText,
|
|
36298
|
+
e2ee: deps.e2ee
|
|
36299
|
+
});
|
|
36300
|
+
if (sessionChangeSummaryFileSnapshots && sessionChangeSummaryFileSnapshots.length > 0 && resolvedPromptText === promptText) {
|
|
36301
|
+
deps.log(
|
|
36302
|
+
"[Agent] Change-summary snapshots were present but the prompt was not rebuilt (decrypt failed or empty payloads); sending the bridge prompt as-is."
|
|
36303
|
+
);
|
|
36028
36304
|
}
|
|
36029
|
-
|
|
36030
|
-
|
|
36031
|
-
|
|
36032
|
-
|
|
36305
|
+
const pathsForUpload = sessionChangeSummaryFilePaths ?? pathsFromBridge;
|
|
36306
|
+
deps.acpManager.handlePrompt({
|
|
36307
|
+
promptText: resolvedPromptText,
|
|
36308
|
+
promptId: msg.id,
|
|
36309
|
+
sessionId,
|
|
36310
|
+
runId,
|
|
36311
|
+
mode,
|
|
36312
|
+
agentType,
|
|
36313
|
+
agentId,
|
|
36314
|
+
agentConfig,
|
|
36315
|
+
sessionParentPath: effectiveCwd,
|
|
36316
|
+
sendResult: sendResult2,
|
|
36317
|
+
sendSessionUpdate,
|
|
36318
|
+
followUpCatalogPromptId,
|
|
36319
|
+
sessionChangeSummaryFilePaths: pathsForUpload,
|
|
36320
|
+
cloudApiBaseUrl: deps.cloudApiBaseUrl,
|
|
36321
|
+
getCloudAccessToken: deps.getCloudAccessToken,
|
|
36322
|
+
e2ee: deps.e2ee,
|
|
36323
|
+
...attachments.length > 0 ? { attachments } : {}
|
|
36324
|
+
});
|
|
36033
36325
|
}
|
|
36034
36326
|
|
|
36035
|
-
// src/agents/acp/from-bridge/handle-bridge-prompt.ts
|
|
36036
|
-
function parseBridgeAttachments(msg) {
|
|
36037
|
-
const raw = msg.attachments;
|
|
36038
|
-
if (!Array.isArray(raw)) return [];
|
|
36039
|
-
const out = [];
|
|
36040
|
-
for (const x of raw) {
|
|
36041
|
-
if (x === null || typeof x !== "object" || Array.isArray(x)) continue;
|
|
36042
|
-
const o = x;
|
|
36043
|
-
const id = typeof o.attachmentId === "string" ? o.attachmentId.trim() : "";
|
|
36044
|
-
if (!id) continue;
|
|
36045
|
-
const mt = typeof o.mimeType === "string" && o.mimeType.trim() ? o.mimeType.trim() : "application/octet-stream";
|
|
36046
|
-
out.push({ attachmentId: id, mimeType: mt });
|
|
36047
|
-
}
|
|
36048
|
-
return out;
|
|
36049
|
-
}
|
|
36327
|
+
// src/agents/acp/from-bridge/handle-bridge-prompt/handle-bridge-prompt.ts
|
|
36050
36328
|
function handleBridgePrompt(msg, deps) {
|
|
36051
36329
|
const { getWs, log: log2, acpManager, sessionWorktreeManager } = deps;
|
|
36052
36330
|
const rawPrompt = msg.prompt;
|
|
@@ -36055,12 +36333,12 @@ function handleBridgePrompt(msg, deps) {
|
|
|
36055
36333
|
const sessionId = msg.sessionId;
|
|
36056
36334
|
const runId = typeof msg.runId === "string" ? msg.runId : void 0;
|
|
36057
36335
|
const promptId = typeof msg.id === "string" ? msg.id : void 0;
|
|
36058
|
-
const
|
|
36336
|
+
const senders = createBridgePromptSenders(deps, getWs);
|
|
36059
36337
|
if (!promptText.trim() && attachments.length === 0) {
|
|
36060
36338
|
log2(
|
|
36061
36339
|
`[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).`
|
|
36062
36340
|
);
|
|
36063
|
-
sendBridgeMessage(
|
|
36341
|
+
senders.sendBridgeMessage(
|
|
36064
36342
|
{
|
|
36065
36343
|
type: "prompt_result",
|
|
36066
36344
|
...promptId ? { id: promptId } : {},
|
|
@@ -36082,57 +36360,50 @@ function handleBridgePrompt(msg, deps) {
|
|
|
36082
36360
|
const agentId = typeof rawAgentId === "string" && rawAgentId.trim() !== "" ? rawAgentId.trim() : null;
|
|
36083
36361
|
const mode = typeof msg.mode === "string" && msg.mode.trim() ? msg.mode.trim() : void 0;
|
|
36084
36362
|
const agentConfig = msg.agentConfig != null && typeof msg.agentConfig === "object" && !Array.isArray(msg.agentConfig) ? msg.agentConfig : void 0;
|
|
36363
|
+
const worktreeBaseBranches = parseWorktreeBaseBranches(msg);
|
|
36085
36364
|
acpManager.logPromptReceivedFromBridge({ agentType, mode });
|
|
36086
|
-
|
|
36087
|
-
|
|
36088
|
-
|
|
36365
|
+
void sessionWorktreeManager.resolveSessionParentPathForPrompt(sessionId, {
|
|
36366
|
+
isNewSession,
|
|
36367
|
+
sessionParent,
|
|
36368
|
+
sessionParentPath,
|
|
36369
|
+
...worktreeBaseBranches ? { worktreeBaseBranches } : {}
|
|
36370
|
+
}).then(
|
|
36371
|
+
(cwd) => runPreambleAndPrompt({
|
|
36372
|
+
deps,
|
|
36373
|
+
msg,
|
|
36089
36374
|
getWs,
|
|
36090
36375
|
log: log2,
|
|
36091
36376
|
sessionWorktreeManager,
|
|
36092
36377
|
sessionId,
|
|
36093
36378
|
runId,
|
|
36094
|
-
|
|
36095
|
-
|
|
36096
|
-
|
|
36097
|
-
|
|
36098
|
-
|
|
36099
|
-
|
|
36100
|
-
|
|
36101
|
-
|
|
36102
|
-
|
|
36103
|
-
|
|
36104
|
-
|
|
36105
|
-
|
|
36106
|
-
|
|
36107
|
-
|
|
36108
|
-
|
|
36109
|
-
|
|
36110
|
-
|
|
36111
|
-
}
|
|
36112
|
-
const pathsForUpload = sessionChangeSummaryFilePaths ?? pathsFromBridge;
|
|
36113
|
-
acpManager.handlePrompt({
|
|
36114
|
-
promptText: resolvedPromptText,
|
|
36115
|
-
promptId: msg.id,
|
|
36379
|
+
promptText,
|
|
36380
|
+
attachments,
|
|
36381
|
+
mode,
|
|
36382
|
+
agentType,
|
|
36383
|
+
agentId,
|
|
36384
|
+
agentConfig,
|
|
36385
|
+
resolvedCwd: cwd,
|
|
36386
|
+
senders
|
|
36387
|
+
})
|
|
36388
|
+
).catch((err) => {
|
|
36389
|
+
log2(`[Agent] Session parent path resolve failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
36390
|
+
void runPreambleAndPrompt({
|
|
36391
|
+
deps,
|
|
36392
|
+
msg,
|
|
36393
|
+
getWs,
|
|
36394
|
+
log: log2,
|
|
36395
|
+
sessionWorktreeManager,
|
|
36116
36396
|
sessionId,
|
|
36117
36397
|
runId,
|
|
36398
|
+
promptText,
|
|
36399
|
+
attachments,
|
|
36118
36400
|
mode,
|
|
36119
36401
|
agentType,
|
|
36120
36402
|
agentId,
|
|
36121
36403
|
agentConfig,
|
|
36122
|
-
|
|
36123
|
-
|
|
36124
|
-
sendSessionUpdate,
|
|
36125
|
-
followUpCatalogPromptId,
|
|
36126
|
-
sessionChangeSummaryFilePaths: pathsForUpload,
|
|
36127
|
-
cloudApiBaseUrl: deps.cloudApiBaseUrl,
|
|
36128
|
-
getCloudAccessToken: deps.getCloudAccessToken,
|
|
36129
|
-
e2ee: deps.e2ee,
|
|
36130
|
-
...attachments.length > 0 ? { attachments } : {}
|
|
36404
|
+
resolvedCwd: void 0,
|
|
36405
|
+
senders
|
|
36131
36406
|
});
|
|
36132
|
-
}
|
|
36133
|
-
void sessionWorktreeManager.resolveSessionParentPathForPrompt(sessionId, { isNewSession, sessionParent, sessionParentPath }).then((cwd) => preambleAndPrompt(cwd)).catch((err) => {
|
|
36134
|
-
log2(`[Agent] Session parent path resolve failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
36135
|
-
void preambleAndPrompt(void 0);
|
|
36136
36407
|
});
|
|
36137
36408
|
}
|
|
36138
36409
|
|
|
@@ -36187,11 +36458,11 @@ var handleSkillCallMessage = (msg, { getWs, log: log2 }) => {
|
|
|
36187
36458
|
import fs33 from "node:fs";
|
|
36188
36459
|
|
|
36189
36460
|
// src/files/ensure-under-cwd.ts
|
|
36190
|
-
import
|
|
36461
|
+
import path36 from "node:path";
|
|
36191
36462
|
function ensureUnderCwd(relativePath, cwd = getBridgeRoot()) {
|
|
36192
|
-
const normalized =
|
|
36193
|
-
const resolved =
|
|
36194
|
-
if (!resolved.startsWith(cwd +
|
|
36463
|
+
const normalized = path36.normalize(relativePath).replace(/^(\.\/)+/, "");
|
|
36464
|
+
const resolved = path36.resolve(cwd, normalized);
|
|
36465
|
+
if (!resolved.startsWith(cwd + path36.sep) && resolved !== cwd) {
|
|
36195
36466
|
return null;
|
|
36196
36467
|
}
|
|
36197
36468
|
return resolved;
|
|
@@ -36201,11 +36472,11 @@ function ensureUnderCwd(relativePath, cwd = getBridgeRoot()) {
|
|
|
36201
36472
|
var LIST_DIR_YIELD_EVERY = 256;
|
|
36202
36473
|
|
|
36203
36474
|
// src/files/list-dir/map-dir-entry.ts
|
|
36204
|
-
import
|
|
36475
|
+
import path37 from "node:path";
|
|
36205
36476
|
import fs32 from "node:fs";
|
|
36206
36477
|
async function mapDirEntry(d, relativePath, resolved) {
|
|
36207
|
-
const entryPath =
|
|
36208
|
-
const fullPath =
|
|
36478
|
+
const entryPath = path37.join(relativePath || ".", d.name).replace(/\\/g, "/");
|
|
36479
|
+
const fullPath = path37.join(resolved, d.name);
|
|
36209
36480
|
let isDir = d.isDirectory();
|
|
36210
36481
|
if (d.isSymbolicLink()) {
|
|
36211
36482
|
try {
|
|
@@ -36556,13 +36827,13 @@ function resolveFileBrowserSessionParent(sessionWorktreeManager, sessionId) {
|
|
|
36556
36827
|
}
|
|
36557
36828
|
|
|
36558
36829
|
// src/files/handle-file-browser-search.ts
|
|
36559
|
-
import
|
|
36830
|
+
import path38 from "node:path";
|
|
36560
36831
|
var SEARCH_LIMIT = 100;
|
|
36561
36832
|
function handleFileBrowserSearch(msg, socket, e2ee, sessionWorktreeManager) {
|
|
36562
36833
|
void (async () => {
|
|
36563
36834
|
await yieldToEventLoop();
|
|
36564
36835
|
const q = typeof msg.q === "string" ? msg.q : "";
|
|
36565
|
-
const sessionParentPath =
|
|
36836
|
+
const sessionParentPath = path38.resolve(
|
|
36566
36837
|
sessionWorktreeManager != null ? resolveFileBrowserSessionParent(sessionWorktreeManager, msg.sessionId) : getBridgeRoot()
|
|
36567
36838
|
);
|
|
36568
36839
|
if (!await bridgeFileIndexIsPopulated(sessionParentPath)) {
|
|
@@ -36682,7 +36953,7 @@ function handleSkillLayoutRequest(msg, deps) {
|
|
|
36682
36953
|
|
|
36683
36954
|
// src/skills/install-remote-skills.ts
|
|
36684
36955
|
import fs37 from "node:fs";
|
|
36685
|
-
import
|
|
36956
|
+
import path39 from "node:path";
|
|
36686
36957
|
function installRemoteSkills(cwd, targetDir, items) {
|
|
36687
36958
|
const installed2 = [];
|
|
36688
36959
|
if (!Array.isArray(items)) {
|
|
@@ -36693,11 +36964,11 @@ function installRemoteSkills(cwd, targetDir, items) {
|
|
|
36693
36964
|
if (typeof item.sourceId !== "string" || typeof item.skillName !== "string" || typeof item.versionHash !== "string" || !Array.isArray(item.files)) {
|
|
36694
36965
|
continue;
|
|
36695
36966
|
}
|
|
36696
|
-
const skillDir =
|
|
36967
|
+
const skillDir = path39.join(cwd, targetDir, item.skillName);
|
|
36697
36968
|
for (const f of item.files) {
|
|
36698
36969
|
if (typeof f.path !== "string" || !f.text && !f.base64) continue;
|
|
36699
|
-
const dest =
|
|
36700
|
-
fs37.mkdirSync(
|
|
36970
|
+
const dest = path39.join(skillDir, f.path);
|
|
36971
|
+
fs37.mkdirSync(path39.dirname(dest), { recursive: true });
|
|
36701
36972
|
if (f.text !== void 0) {
|
|
36702
36973
|
fs37.writeFileSync(dest, f.text, "utf8");
|
|
36703
36974
|
} else if (f.base64) {
|
|
@@ -36913,6 +37184,148 @@ var handleDevServersConfig = (msg, deps) => {
|
|
|
36913
37184
|
deps.devServerManager?.applyConfig(devServers ?? []);
|
|
36914
37185
|
};
|
|
36915
37186
|
|
|
37187
|
+
// src/git/bridge-git-context.ts
|
|
37188
|
+
import * as path40 from "node:path";
|
|
37189
|
+
|
|
37190
|
+
// src/git/branches/get-current-branch.ts
|
|
37191
|
+
async function getCurrentBranch(repoPath) {
|
|
37192
|
+
try {
|
|
37193
|
+
const git = cliSimpleGit(repoPath);
|
|
37194
|
+
const branch = await git.revparse(["--abbrev-ref", "HEAD"]);
|
|
37195
|
+
const trimmed2 = typeof branch === "string" ? branch.trim() : "";
|
|
37196
|
+
if (!trimmed2 || trimmed2 === "HEAD") return null;
|
|
37197
|
+
return trimmed2;
|
|
37198
|
+
} catch {
|
|
37199
|
+
return null;
|
|
37200
|
+
}
|
|
37201
|
+
}
|
|
37202
|
+
|
|
37203
|
+
// src/git/branches/list-repo-branch-refs.ts
|
|
37204
|
+
function normalizeBranchRef(raw) {
|
|
37205
|
+
const trimmed2 = raw.trim();
|
|
37206
|
+
if (!trimmed2 || trimmed2 === "HEAD") return null;
|
|
37207
|
+
if (trimmed2.startsWith("refs/heads/")) return trimmed2.slice("refs/heads/".length);
|
|
37208
|
+
if (trimmed2.startsWith("refs/remotes/")) return trimmed2.slice("refs/remotes/".length);
|
|
37209
|
+
return trimmed2;
|
|
37210
|
+
}
|
|
37211
|
+
async function listRepoBranchRefs(repoPath) {
|
|
37212
|
+
try {
|
|
37213
|
+
const git = cliSimpleGit(repoPath);
|
|
37214
|
+
const out = await git.raw([
|
|
37215
|
+
"for-each-ref",
|
|
37216
|
+
"--sort=-committerdate",
|
|
37217
|
+
"--format=%(refname:short)",
|
|
37218
|
+
"refs/heads",
|
|
37219
|
+
"refs/remotes"
|
|
37220
|
+
]);
|
|
37221
|
+
const lines = out.split("\n").map((line) => normalizeBranchRef(line)).filter((line) => Boolean(line));
|
|
37222
|
+
const seen = /* @__PURE__ */ new Set();
|
|
37223
|
+
const ordered = [];
|
|
37224
|
+
for (const name of lines) {
|
|
37225
|
+
if (seen.has(name)) continue;
|
|
37226
|
+
seen.add(name);
|
|
37227
|
+
ordered.push(name);
|
|
37228
|
+
}
|
|
37229
|
+
return ordered;
|
|
37230
|
+
} catch {
|
|
37231
|
+
return [];
|
|
37232
|
+
}
|
|
37233
|
+
}
|
|
37234
|
+
|
|
37235
|
+
// src/git/bridge-git-context.ts
|
|
37236
|
+
function folderNameForRelPath(relPath, bridgeRoot) {
|
|
37237
|
+
if (relPath === "." || relPath === "") {
|
|
37238
|
+
return path40.basename(path40.resolve(bridgeRoot)) || "repo";
|
|
37239
|
+
}
|
|
37240
|
+
return path40.basename(relPath) || relPath;
|
|
37241
|
+
}
|
|
37242
|
+
async function discoverGitReposForBridgeContext(bridgeRoot) {
|
|
37243
|
+
const root = path40.resolve(bridgeRoot);
|
|
37244
|
+
if (await isGitRepoDirectory(root)) {
|
|
37245
|
+
const remoteUrl = await getRemoteOriginUrl(root);
|
|
37246
|
+
return [{ absolutePath: root, remoteUrl }];
|
|
37247
|
+
}
|
|
37248
|
+
const [deep, shallow] = await Promise.all([discoverGitReposUnderRoot(root), discoverGitRepos(root)]);
|
|
37249
|
+
const byPath = /* @__PURE__ */ new Map();
|
|
37250
|
+
for (const repo of [...deep, ...shallow]) {
|
|
37251
|
+
byPath.set(path40.resolve(repo.absolutePath), repo);
|
|
37252
|
+
}
|
|
37253
|
+
return [...byPath.values()];
|
|
37254
|
+
}
|
|
37255
|
+
async function getBridgeGitContext(bridgeRoot = getBridgeRoot()) {
|
|
37256
|
+
const bridgeResolved = path40.resolve(bridgeRoot);
|
|
37257
|
+
const repos = await discoverGitReposForBridgeContext(bridgeResolved);
|
|
37258
|
+
const rows = [];
|
|
37259
|
+
for (const repo of repos) {
|
|
37260
|
+
let rel = path40.relative(bridgeResolved, repo.absolutePath);
|
|
37261
|
+
if (rel.startsWith("..") || path40.isAbsolute(rel)) continue;
|
|
37262
|
+
const relPath = rel === "" ? "." : rel.replace(/\\/g, "/");
|
|
37263
|
+
const currentBranch = await getCurrentBranch(repo.absolutePath);
|
|
37264
|
+
const remoteUrl = repo.remoteUrl.trim() || null;
|
|
37265
|
+
rows.push({
|
|
37266
|
+
relPath,
|
|
37267
|
+
folderName: folderNameForRelPath(relPath, bridgeResolved),
|
|
37268
|
+
currentBranch,
|
|
37269
|
+
remoteUrl
|
|
37270
|
+
});
|
|
37271
|
+
}
|
|
37272
|
+
rows.sort((a, b) => a.relPath.localeCompare(b.relPath));
|
|
37273
|
+
return rows;
|
|
37274
|
+
}
|
|
37275
|
+
async function listRepoBranchesForBridge(repoRelPath, bridgeRoot = getBridgeRoot()) {
|
|
37276
|
+
const bridgeResolved = path40.resolve(bridgeRoot);
|
|
37277
|
+
const rel = repoRelPath.trim() === "." ? "" : repoRelPath.trim().replace(/\\/g, "/");
|
|
37278
|
+
const repoPath = rel === "" ? bridgeResolved : path40.join(bridgeResolved, rel);
|
|
37279
|
+
const resolved = path40.resolve(repoPath);
|
|
37280
|
+
if (!resolved.startsWith(bridgeResolved + path40.sep) && resolved !== bridgeResolved) {
|
|
37281
|
+
return [];
|
|
37282
|
+
}
|
|
37283
|
+
return listRepoBranchRefs(resolved);
|
|
37284
|
+
}
|
|
37285
|
+
|
|
37286
|
+
// src/routing/handlers/bridge-git-context-messages.ts
|
|
37287
|
+
function handleBridgeGitContextRequestMessage(msg, getWs) {
|
|
37288
|
+
void (async () => {
|
|
37289
|
+
const socket = getWs();
|
|
37290
|
+
if (!socket) return;
|
|
37291
|
+
try {
|
|
37292
|
+
const repos = await getBridgeGitContext();
|
|
37293
|
+
sendWsMessage(socket, { type: "bridge_git_context_response", id: msg.id, repos });
|
|
37294
|
+
} catch (e) {
|
|
37295
|
+
sendWsMessage(socket, {
|
|
37296
|
+
type: "bridge_git_context_response",
|
|
37297
|
+
id: msg.id,
|
|
37298
|
+
error: e instanceof Error ? e.message : String(e)
|
|
37299
|
+
});
|
|
37300
|
+
}
|
|
37301
|
+
})();
|
|
37302
|
+
}
|
|
37303
|
+
function handleListRepoBranchesRequestMessage(msg, getWs) {
|
|
37304
|
+
void (async () => {
|
|
37305
|
+
const socket = getWs();
|
|
37306
|
+
if (!socket) return;
|
|
37307
|
+
const repoRelPath = typeof msg.repoRelPath === "string" ? msg.repoRelPath.trim() : "";
|
|
37308
|
+
if (!repoRelPath) {
|
|
37309
|
+
sendWsMessage(socket, {
|
|
37310
|
+
type: "list_repo_branches_response",
|
|
37311
|
+
id: msg.id,
|
|
37312
|
+
error: "repoRelPath required"
|
|
37313
|
+
});
|
|
37314
|
+
return;
|
|
37315
|
+
}
|
|
37316
|
+
try {
|
|
37317
|
+
const branches = await listRepoBranchesForBridge(repoRelPath);
|
|
37318
|
+
sendWsMessage(socket, { type: "list_repo_branches_response", id: msg.id, branches });
|
|
37319
|
+
} catch (e) {
|
|
37320
|
+
sendWsMessage(socket, {
|
|
37321
|
+
type: "list_repo_branches_response",
|
|
37322
|
+
id: msg.id,
|
|
37323
|
+
error: e instanceof Error ? e.message : String(e)
|
|
37324
|
+
});
|
|
37325
|
+
}
|
|
37326
|
+
})();
|
|
37327
|
+
}
|
|
37328
|
+
|
|
36916
37329
|
// src/routing/dispatch-bridge-message.ts
|
|
36917
37330
|
function dispatchBridgeMessage(msg, deps) {
|
|
36918
37331
|
switch (msg.type) {
|
|
@@ -36976,6 +37389,12 @@ function dispatchBridgeMessage(msg, deps) {
|
|
|
36976
37389
|
case "refresh_local_skills":
|
|
36977
37390
|
handleRefreshLocalSkills(msg, deps);
|
|
36978
37391
|
break;
|
|
37392
|
+
case "bridge_git_context_request":
|
|
37393
|
+
handleBridgeGitContextRequestMessage(msg, deps.getWs);
|
|
37394
|
+
break;
|
|
37395
|
+
case "list_repo_branches_request":
|
|
37396
|
+
handleListRepoBranchesRequestMessage(msg, deps.getWs);
|
|
37397
|
+
break;
|
|
36979
37398
|
}
|
|
36980
37399
|
}
|
|
36981
37400
|
|
|
@@ -36988,8 +37407,10 @@ function normalizeInboundBridgeWebSocketJson(data) {
|
|
|
36988
37407
|
}
|
|
36989
37408
|
return data;
|
|
36990
37409
|
}
|
|
36991
|
-
function handleBridgeMessage(data, deps) {
|
|
36992
|
-
|
|
37410
|
+
function handleBridgeMessage(data, deps, sourceWs) {
|
|
37411
|
+
const active = deps.getWs();
|
|
37412
|
+
if (!active) return;
|
|
37413
|
+
if (sourceWs != null && sourceWs !== active) return;
|
|
36993
37414
|
const msg = parseApiToBridgeMessage(normalizeInboundBridgeWebSocketJson(data), deps.log);
|
|
36994
37415
|
if (!msg) return;
|
|
36995
37416
|
dispatchBridgeMessage(msg, deps);
|
|
@@ -37041,6 +37462,7 @@ function createMainBridgeWebSocketLifecycle(params) {
|
|
|
37041
37462
|
} = params;
|
|
37042
37463
|
let authRefreshInFlight = false;
|
|
37043
37464
|
function handleOpen() {
|
|
37465
|
+
recordBridgeSessionOpened(state.duplicateBridgeFlap, Date.now());
|
|
37044
37466
|
const logOpenAsPostRefreshReconnect = state.logBridgeOpenAsReconnect;
|
|
37045
37467
|
clearMainBridgeReconnectQuietOnOpen(state, logFn);
|
|
37046
37468
|
state.reconnectAttempt = 0;
|
|
@@ -37075,6 +37497,15 @@ function createMainBridgeWebSocketLifecycle(params) {
|
|
|
37075
37497
|
state.currentWs = null;
|
|
37076
37498
|
if (was) was.removeAllListeners();
|
|
37077
37499
|
const willReconnect = !state.closedByUser;
|
|
37500
|
+
if (willReconnect) {
|
|
37501
|
+
const duplicateResult = evaluateDuplicateBridgeDisconnect(
|
|
37502
|
+
state.duplicateBridgeFlap,
|
|
37503
|
+
Date.now(),
|
|
37504
|
+
code,
|
|
37505
|
+
reason
|
|
37506
|
+
);
|
|
37507
|
+
logDuplicateBridgeWarning(logFn, duplicateResult);
|
|
37508
|
+
}
|
|
37078
37509
|
beginMainBridgeDeferredDisconnect(state, code, reason, logFn, willReconnect);
|
|
37079
37510
|
if (willReconnect) {
|
|
37080
37511
|
state.lastReconnectCloseMeta = { code, reason };
|
|
@@ -37124,6 +37555,10 @@ function createMainBridgeWebSocketLifecycle(params) {
|
|
|
37124
37555
|
state.currentWs = createWsBridge({
|
|
37125
37556
|
url: url2,
|
|
37126
37557
|
clientPingIntervalMs: CLI_WEBSOCKET_CLIENT_PING_MS,
|
|
37558
|
+
isActiveSocket: (socket) => state.currentWs === socket,
|
|
37559
|
+
onCompactHeartbeatAck: (seq) => {
|
|
37560
|
+
messageDeps.onBridgeHeartbeatAck?.(seq);
|
|
37561
|
+
},
|
|
37127
37562
|
onAuthInvalid: () => {
|
|
37128
37563
|
if (authRefreshInFlight) return;
|
|
37129
37564
|
void (async () => {
|
|
@@ -37177,9 +37612,9 @@ function createMainBridgeWebSocketLifecycle(params) {
|
|
|
37177
37612
|
} catch {
|
|
37178
37613
|
}
|
|
37179
37614
|
},
|
|
37180
|
-
onMessage: (data) => {
|
|
37615
|
+
onMessage: (data, sourceWs) => {
|
|
37181
37616
|
try {
|
|
37182
|
-
handleBridgeMessage(data, messageDeps);
|
|
37617
|
+
handleBridgeMessage(data, messageDeps, sourceWs);
|
|
37183
37618
|
} catch {
|
|
37184
37619
|
}
|
|
37185
37620
|
}
|
|
@@ -37426,10 +37861,10 @@ function listCliAgentCapabilityCacheForWorkspace(db, workspaceId) {
|
|
|
37426
37861
|
}
|
|
37427
37862
|
|
|
37428
37863
|
// src/agents/capabilities/warmup-agent-capabilities-on-connect.ts
|
|
37429
|
-
import * as
|
|
37864
|
+
import * as path42 from "node:path";
|
|
37430
37865
|
|
|
37431
37866
|
// src/agents/capabilities/probe-one-agent-type-for-capabilities.ts
|
|
37432
|
-
import * as
|
|
37867
|
+
import * as path41 from "node:path";
|
|
37433
37868
|
async function probeOneAgentTypeForCapabilities(params) {
|
|
37434
37869
|
const { agentType, cwd, workspaceId, log: log2, reportAgentCapabilities, bridgeReport = true } = params;
|
|
37435
37870
|
if (isCliImmediateShutdownRequested()) return false;
|
|
@@ -37469,7 +37904,7 @@ async function probeOneAgentTypeForCapabilities(params) {
|
|
|
37469
37904
|
if (isCliImmediateShutdownRequested()) return false;
|
|
37470
37905
|
handle = await resolved.createClient({
|
|
37471
37906
|
command: resolved.command,
|
|
37472
|
-
cwd:
|
|
37907
|
+
cwd: path41.resolve(cwd),
|
|
37473
37908
|
backendAgentType: agentType,
|
|
37474
37909
|
sessionMode: "agent",
|
|
37475
37910
|
persistedAcpSessionId: null,
|
|
@@ -37543,7 +37978,7 @@ async function probeAgentCapabilitiesForDetectedTypes(params) {
|
|
|
37543
37978
|
async function warmupAgentCapabilitiesOnConnect(params) {
|
|
37544
37979
|
const { workspaceId, log: log2, getWs } = params;
|
|
37545
37980
|
if (isCliImmediateShutdownRequested()) return;
|
|
37546
|
-
const cwd =
|
|
37981
|
+
const cwd = path42.resolve(getBridgeRoot());
|
|
37547
37982
|
async function sendBatchFromCache() {
|
|
37548
37983
|
const socket = getWs();
|
|
37549
37984
|
if (!socket || socket.readyState !== wrapper_default.OPEN) return;
|
|
@@ -37623,6 +38058,7 @@ async function createBridgeConnection(options) {
|
|
|
37623
38058
|
currentWs: null,
|
|
37624
38059
|
mainQuiet: createEmptyReconnectQuietSlot(),
|
|
37625
38060
|
mainOutage: createEmptyReconnectOutageTracker(),
|
|
38061
|
+
duplicateBridgeFlap: createEmptyDuplicateBridgeFlapTracker(),
|
|
37626
38062
|
firehoseHandle: null,
|
|
37627
38063
|
lastFirehoseParams: null,
|
|
37628
38064
|
firehoseReconnectTimeout: null,
|
|
@@ -37710,8 +38146,8 @@ async function createBridgeConnection(options) {
|
|
|
37710
38146
|
getCloudAccessToken: () => tokens.accessToken
|
|
37711
38147
|
};
|
|
37712
38148
|
const identifyReportedPaths = {
|
|
37713
|
-
bridgeRootPath:
|
|
37714
|
-
worktreesRootPath:
|
|
38149
|
+
bridgeRootPath: path43.resolve(getBridgeRoot()),
|
|
38150
|
+
worktreesRootPath: path43.resolve(worktreesRootPath)
|
|
37715
38151
|
};
|
|
37716
38152
|
const { connect } = createMainBridgeWebSocketLifecycle({
|
|
37717
38153
|
state,
|