@buildautomaton/cli 0.1.4 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +815 -542
- package/dist/cli.js.map +4 -4
- package/dist/index.js +810 -537
- package/dist/index.js.map +4 -4
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -22065,12 +22065,19 @@ var wrapper_default = import_websocket.default;
|
|
|
22065
22065
|
var BRIDGE_AUTH_ERROR_HEADER = "x-bridge-auth-error";
|
|
22066
22066
|
var BRIDGE_AUTH_ERROR_TOKEN_INVALID = "token_invalid";
|
|
22067
22067
|
function createWsBridge(options) {
|
|
22068
|
-
const { url: url2, onMessage, onOpen, onClose, onError: onError2, onAuthInvalid } = options;
|
|
22068
|
+
const { url: url2, onMessage, onOpen, onClose, onError: onError2, onAuthInvalid, clientPingIntervalMs } = options;
|
|
22069
22069
|
const wsOptions = {};
|
|
22070
22070
|
if (url2.startsWith("wss://")) {
|
|
22071
22071
|
wsOptions.agent = new https.Agent({ rejectUnauthorized: false });
|
|
22072
22072
|
}
|
|
22073
22073
|
const ws = new wrapper_default(url2, wsOptions);
|
|
22074
|
+
let clientPingTimer = null;
|
|
22075
|
+
function clearClientPing() {
|
|
22076
|
+
if (clientPingTimer != null) {
|
|
22077
|
+
clearInterval(clientPingTimer);
|
|
22078
|
+
clientPingTimer = null;
|
|
22079
|
+
}
|
|
22080
|
+
}
|
|
22074
22081
|
ws.on("unexpected-response", (request, response) => {
|
|
22075
22082
|
const status = response?.statusCode ?? 0;
|
|
22076
22083
|
const headers = response?.headers ?? {};
|
|
@@ -22080,6 +22087,17 @@ function createWsBridge(options) {
|
|
|
22080
22087
|
}
|
|
22081
22088
|
});
|
|
22082
22089
|
ws.on("open", () => {
|
|
22090
|
+
clearClientPing();
|
|
22091
|
+
if (clientPingIntervalMs != null && clientPingIntervalMs > 0) {
|
|
22092
|
+
clientPingTimer = setInterval(() => {
|
|
22093
|
+
if (ws.readyState === wrapper_default.OPEN) {
|
|
22094
|
+
try {
|
|
22095
|
+
ws.ping();
|
|
22096
|
+
} catch {
|
|
22097
|
+
}
|
|
22098
|
+
}
|
|
22099
|
+
}, clientPingIntervalMs);
|
|
22100
|
+
}
|
|
22083
22101
|
onOpen?.();
|
|
22084
22102
|
});
|
|
22085
22103
|
ws.on("message", (raw) => {
|
|
@@ -22099,9 +22117,11 @@ function createWsBridge(options) {
|
|
|
22099
22117
|
}
|
|
22100
22118
|
});
|
|
22101
22119
|
ws.on("close", (code, reason) => {
|
|
22120
|
+
clearClientPing();
|
|
22102
22121
|
onClose?.(code, reason.toString());
|
|
22103
22122
|
});
|
|
22104
22123
|
ws.on("error", (err) => {
|
|
22124
|
+
clearClientPing();
|
|
22105
22125
|
onError2?.(err);
|
|
22106
22126
|
});
|
|
22107
22127
|
return ws;
|
|
@@ -22115,6 +22135,18 @@ function sendWsMessage(ws, payload) {
|
|
|
22115
22135
|
// src/acp/clients/acp-client.ts
|
|
22116
22136
|
import { spawn } from "node:child_process";
|
|
22117
22137
|
import { Readable, Writable } from "node:stream";
|
|
22138
|
+
|
|
22139
|
+
// src/files/cwd/bridge-workspace-directory.ts
|
|
22140
|
+
import * as path from "node:path";
|
|
22141
|
+
var bridgeWorkspaceDirectory = null;
|
|
22142
|
+
function getBridgeWorkspaceDirectory() {
|
|
22143
|
+
if (bridgeWorkspaceDirectory == null) {
|
|
22144
|
+
bridgeWorkspaceDirectory = path.resolve(process.cwd());
|
|
22145
|
+
}
|
|
22146
|
+
return bridgeWorkspaceDirectory;
|
|
22147
|
+
}
|
|
22148
|
+
|
|
22149
|
+
// src/acp/clients/acp-client.ts
|
|
22118
22150
|
function formatSpawnError(err, command) {
|
|
22119
22151
|
if (err.code === "ENOENT") {
|
|
22120
22152
|
return `Command "${command}" not found. Install the agent (e.g. Cursor CLI) or add it to PATH.`;
|
|
@@ -22131,10 +22163,10 @@ function toErrorMessage(err) {
|
|
|
22131
22163
|
}
|
|
22132
22164
|
async function createAcpClient(options) {
|
|
22133
22165
|
const { ClientSideConnection: ClientSideConnection2, ndJsonStream: ndJsonStream2 } = await Promise.resolve().then(() => (init_acp(), acp_exports));
|
|
22134
|
-
const { command, cwd
|
|
22166
|
+
const { command, cwd = getBridgeWorkspaceDirectory(), onSessionUpdate } = options;
|
|
22135
22167
|
const isWindows = process.platform === "win32";
|
|
22136
22168
|
const child = spawn(command[0], command.slice(1), {
|
|
22137
|
-
cwd
|
|
22169
|
+
cwd,
|
|
22138
22170
|
stdio: ["pipe", "pipe", "inherit"],
|
|
22139
22171
|
env: process.env,
|
|
22140
22172
|
shell: isWindows
|
|
@@ -22166,7 +22198,7 @@ async function createAcpClient(options) {
|
|
|
22166
22198
|
capabilities: {},
|
|
22167
22199
|
clientInfo: { name: "buildautomaton-cli", version: "0.1.0" }
|
|
22168
22200
|
});
|
|
22169
|
-
const newSessionRes = await connection.newSession({ workingDirectory:
|
|
22201
|
+
const newSessionRes = await connection.newSession({ workingDirectory: cwd });
|
|
22170
22202
|
const sessionId = newSessionRes.sessionId;
|
|
22171
22203
|
resolve14({
|
|
22172
22204
|
sessionId,
|
|
@@ -22399,7 +22431,7 @@ var previewSkill = {
|
|
|
22399
22431
|
const exe = parts[0];
|
|
22400
22432
|
const args = parts.slice(1);
|
|
22401
22433
|
previewProcess = spawn2(isWindows && exe === "npm" ? "npm.cmd" : exe, args, {
|
|
22402
|
-
cwd:
|
|
22434
|
+
cwd: getBridgeWorkspaceDirectory(),
|
|
22403
22435
|
stdio: ["ignore", "pipe", "pipe"],
|
|
22404
22436
|
env: {
|
|
22405
22437
|
...process.env,
|
|
@@ -22508,11 +22540,11 @@ function logImmediate(line) {
|
|
|
22508
22540
|
|
|
22509
22541
|
// src/config.ts
|
|
22510
22542
|
import fs from "node:fs";
|
|
22511
|
-
import
|
|
22543
|
+
import path2 from "node:path";
|
|
22512
22544
|
import os from "node:os";
|
|
22513
22545
|
function getConfigPath() {
|
|
22514
|
-
const dir =
|
|
22515
|
-
return
|
|
22546
|
+
const dir = path2.join(os.homedir(), ".buildautomaton");
|
|
22547
|
+
return path2.join(dir, "config.json");
|
|
22516
22548
|
}
|
|
22517
22549
|
function normalizeApiUrl(url2) {
|
|
22518
22550
|
return url2.replace(/\/$/, "");
|
|
@@ -22528,7 +22560,7 @@ function readRawConfig() {
|
|
|
22528
22560
|
}
|
|
22529
22561
|
function writeConfigForApi(apiUrl, auth) {
|
|
22530
22562
|
const p = getConfigPath();
|
|
22531
|
-
const dir =
|
|
22563
|
+
const dir = path2.dirname(p);
|
|
22532
22564
|
const key = normalizeApiUrl(apiUrl);
|
|
22533
22565
|
const prev = readRawConfig() ?? {};
|
|
22534
22566
|
const servers = { ...prev.servers ?? {}, [key]: { ...auth } };
|
|
@@ -22537,7 +22569,7 @@ function writeConfigForApi(apiUrl, auth) {
|
|
|
22537
22569
|
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
22538
22570
|
fs.writeFileSync(p, JSON.stringify(next, null, 2), "utf8");
|
|
22539
22571
|
} catch (e) {
|
|
22540
|
-
console.error("Could not save
|
|
22572
|
+
console.error("Could not save authentication config:", e);
|
|
22541
22573
|
}
|
|
22542
22574
|
}
|
|
22543
22575
|
function clearConfigForApi(apiUrl) {
|
|
@@ -22599,6 +22631,28 @@ function openBrowser(connectionId, initialWorkspaceId, preferredBridgeName, apiU
|
|
|
22599
22631
|
}
|
|
22600
22632
|
}
|
|
22601
22633
|
|
|
22634
|
+
// src/bridge/connection/reconnect/constants.ts
|
|
22635
|
+
var RECONNECT_FIRST_MS = 100;
|
|
22636
|
+
var RECONNECT_MAX_MS = 3e4;
|
|
22637
|
+
var RECONNECT_QUIET_MS = 2e3;
|
|
22638
|
+
function reconnectDelayMs(attemptBeforeIncrement) {
|
|
22639
|
+
return Math.min(RECONNECT_FIRST_MS * 2 ** attemptBeforeIncrement, RECONNECT_MAX_MS);
|
|
22640
|
+
}
|
|
22641
|
+
|
|
22642
|
+
// src/bridge/connection/reconnect/format-reconnect-delay-for-log.ts
|
|
22643
|
+
function formatReconnectDelayForLog(delayMs) {
|
|
22644
|
+
if (delayMs < 1e3) return `${delayMs}ms`;
|
|
22645
|
+
const s = delayMs / 1e3;
|
|
22646
|
+
return Number.isInteger(s) ? `${s}s` : `${s.toFixed(1)}s`;
|
|
22647
|
+
}
|
|
22648
|
+
|
|
22649
|
+
// src/bridge/connection/reconnect/log-next-reconnect-attempt.ts
|
|
22650
|
+
function logNextReconnectAttempt(log2, serviceLabel, quiet, delayMs, attempt) {
|
|
22651
|
+
if (!quiet.verboseLogs) return;
|
|
22652
|
+
const delayLabel = formatReconnectDelayForLog(delayMs);
|
|
22653
|
+
log2(`${serviceLabel} Next connection attempt in ${delayLabel} (attempt ${attempt}).`);
|
|
22654
|
+
}
|
|
22655
|
+
|
|
22602
22656
|
// src/bridge/connection/ws-close-diagnostics.ts
|
|
22603
22657
|
function describeWebSocketCloseCode(code) {
|
|
22604
22658
|
const known = {
|
|
@@ -22627,11 +22681,122 @@ function formatWebSocketClose(label, code, reason, extra) {
|
|
|
22627
22681
|
const r = reason.trim();
|
|
22628
22682
|
const reasonPart = r ? ` reason="${r}"` : "";
|
|
22629
22683
|
const extraPart = extra ? ` ${extra}` : "";
|
|
22630
|
-
return `${label}
|
|
22684
|
+
return `${label} Disconnected: code=${code} (${describeWebSocketCloseCode(code)})${reasonPart}${extraPart}`;
|
|
22685
|
+
}
|
|
22686
|
+
|
|
22687
|
+
// src/bridge/connection/reconnect/reconnect-quiet-slot.ts
|
|
22688
|
+
function createEmptyReconnectQuietSlot() {
|
|
22689
|
+
return { timer: null, verboseLogs: false, pendingCloseLog: null };
|
|
22690
|
+
}
|
|
22691
|
+
function clearReconnectQuietTimer(quiet) {
|
|
22692
|
+
if (quiet.timer != null) {
|
|
22693
|
+
clearTimeout(quiet.timer);
|
|
22694
|
+
quiet.timer = null;
|
|
22695
|
+
}
|
|
22696
|
+
}
|
|
22697
|
+
function clearReconnectQuietOnSuccessfulConnection(quiet, log2, reconnectedMessage) {
|
|
22698
|
+
clearReconnectQuietTimer(quiet);
|
|
22699
|
+
quiet.pendingCloseLog = null;
|
|
22700
|
+
if (quiet.verboseLogs) {
|
|
22701
|
+
log2(reconnectedMessage);
|
|
22702
|
+
quiet.verboseLogs = false;
|
|
22703
|
+
}
|
|
22704
|
+
}
|
|
22705
|
+
function beginDeferredDisconnectForReconnect(options) {
|
|
22706
|
+
const {
|
|
22707
|
+
isClosedByUser,
|
|
22708
|
+
quiet,
|
|
22709
|
+
code,
|
|
22710
|
+
reason,
|
|
22711
|
+
willReconnect,
|
|
22712
|
+
log: log2,
|
|
22713
|
+
serviceLabel,
|
|
22714
|
+
shutdownDetail,
|
|
22715
|
+
reconnectingDetail,
|
|
22716
|
+
quietMs = RECONNECT_QUIET_MS,
|
|
22717
|
+
shouldAbortQuietWindow
|
|
22718
|
+
} = options;
|
|
22719
|
+
if (!willReconnect) {
|
|
22720
|
+
log2(formatWebSocketClose(serviceLabel, code, reason, shutdownDetail));
|
|
22721
|
+
return;
|
|
22722
|
+
}
|
|
22723
|
+
quiet.pendingCloseLog = { code, reason };
|
|
22724
|
+
if (quiet.timer == null) {
|
|
22725
|
+
quiet.timer = setTimeout(() => {
|
|
22726
|
+
quiet.timer = null;
|
|
22727
|
+
if (isClosedByUser()) return;
|
|
22728
|
+
if (shouldAbortQuietWindow()) return;
|
|
22729
|
+
if (quiet.pendingCloseLog) {
|
|
22730
|
+
const { code: c, reason: r } = quiet.pendingCloseLog;
|
|
22731
|
+
quiet.pendingCloseLog = null;
|
|
22732
|
+
log2(formatWebSocketClose(serviceLabel, c, r, reconnectingDetail));
|
|
22733
|
+
}
|
|
22734
|
+
quiet.verboseLogs = true;
|
|
22735
|
+
}, quietMs);
|
|
22736
|
+
}
|
|
22737
|
+
}
|
|
22738
|
+
|
|
22739
|
+
// src/bridge/connection/reconnect/bridge-main-reconnect.ts
|
|
22740
|
+
function beginMainBridgeDeferredDisconnect(state, code, reason, log2, willReconnect) {
|
|
22741
|
+
beginDeferredDisconnectForReconnect({
|
|
22742
|
+
isClosedByUser: () => state.closedByUser,
|
|
22743
|
+
quiet: state.mainQuiet,
|
|
22744
|
+
code,
|
|
22745
|
+
reason,
|
|
22746
|
+
willReconnect,
|
|
22747
|
+
log: log2,
|
|
22748
|
+
serviceLabel: "[Bridge service]",
|
|
22749
|
+
shutdownDetail: "Not reconnecting (shutting down).",
|
|
22750
|
+
reconnectingDetail: "Reconnecting\u2026",
|
|
22751
|
+
shouldAbortQuietWindow: () => {
|
|
22752
|
+
const w = state.currentWs;
|
|
22753
|
+
return w != null && w.readyState === wrapper_default.OPEN;
|
|
22754
|
+
}
|
|
22755
|
+
});
|
|
22756
|
+
}
|
|
22757
|
+
function clearMainBridgeReconnectQuietOnOpen(state, log2) {
|
|
22758
|
+
clearReconnectQuietOnSuccessfulConnection(state.mainQuiet, log2, "[Bridge service] Reconnected.");
|
|
22759
|
+
}
|
|
22760
|
+
function scheduleMainBridgeReconnect(state, connect, log2) {
|
|
22761
|
+
if (state.closedByUser || state.currentWs != null) return;
|
|
22762
|
+
const delay2 = reconnectDelayMs(state.reconnectAttempt);
|
|
22763
|
+
state.reconnectAttempt += 1;
|
|
22764
|
+
logNextReconnectAttempt(log2, "[Bridge service]", state.mainQuiet, delay2, state.reconnectAttempt);
|
|
22765
|
+
state.reconnectTimeout = setTimeout(() => {
|
|
22766
|
+
state.reconnectTimeout = null;
|
|
22767
|
+
connect();
|
|
22768
|
+
}, delay2);
|
|
22769
|
+
}
|
|
22770
|
+
|
|
22771
|
+
// src/bridge/connection/reconnect/firehose-reconnect.ts
|
|
22772
|
+
var PROXY_AND_LOG_SERVICE_LABEL = "[Proxy and log service]";
|
|
22773
|
+
function beginFirehoseDeferredDisconnect(ctx, code, reason, log2) {
|
|
22774
|
+
beginDeferredDisconnectForReconnect({
|
|
22775
|
+
isClosedByUser: () => ctx.closedByUser,
|
|
22776
|
+
quiet: ctx.firehoseQuiet,
|
|
22777
|
+
code,
|
|
22778
|
+
reason,
|
|
22779
|
+
willReconnect: true,
|
|
22780
|
+
log: log2,
|
|
22781
|
+
serviceLabel: PROXY_AND_LOG_SERVICE_LABEL,
|
|
22782
|
+
shutdownDetail: "Not reconnecting (shutting down).",
|
|
22783
|
+
reconnectingDetail: "Reconnecting\u2026",
|
|
22784
|
+
shouldAbortQuietWindow: () => {
|
|
22785
|
+
const w = ctx.currentWs;
|
|
22786
|
+
if (!w || w.readyState !== wrapper_default.OPEN) return true;
|
|
22787
|
+
return ctx.firehoseHandle?.isConnected() ?? false;
|
|
22788
|
+
}
|
|
22789
|
+
});
|
|
22790
|
+
}
|
|
22791
|
+
function clearFirehoseReconnectQuietOnOpen(ctx, log2) {
|
|
22792
|
+
clearReconnectQuietOnSuccessfulConnection(
|
|
22793
|
+
ctx.firehoseQuiet,
|
|
22794
|
+
log2,
|
|
22795
|
+
`${PROXY_AND_LOG_SERVICE_LABEL} Reconnected.`
|
|
22796
|
+
);
|
|
22631
22797
|
}
|
|
22632
22798
|
|
|
22633
22799
|
// src/auth/run-pending-auth.ts
|
|
22634
|
-
var PENDING_RECONNECT_MS = 2e3;
|
|
22635
22800
|
var PENDING_KEEPALIVE_MS = 25e3;
|
|
22636
22801
|
var BROWSER_OPEN_FALLBACK_MS = 4e3;
|
|
22637
22802
|
function buildPendingBridgeUrl(apiUrl, connectionId) {
|
|
@@ -22653,7 +22818,32 @@ function runPendingAuth(options) {
|
|
|
22653
22818
|
const authPromise = new Promise((resolve14) => {
|
|
22654
22819
|
resolveAuth = resolve14;
|
|
22655
22820
|
});
|
|
22821
|
+
let reconnectAttempt = 0;
|
|
22822
|
+
const signInQuiet = createEmptyReconnectQuietSlot();
|
|
22823
|
+
function clearQuietOnOpen() {
|
|
22824
|
+
clearReconnectQuietOnSuccessfulConnection(
|
|
22825
|
+
signInQuiet,
|
|
22826
|
+
logFn,
|
|
22827
|
+
"[Bridge service] Sign-in connection restored."
|
|
22828
|
+
);
|
|
22829
|
+
reconnectAttempt = 0;
|
|
22830
|
+
}
|
|
22831
|
+
function beginDeferredPendingCloseLog(code, reason) {
|
|
22832
|
+
beginDeferredDisconnectForReconnect({
|
|
22833
|
+
isClosedByUser: () => resolved,
|
|
22834
|
+
quiet: signInQuiet,
|
|
22835
|
+
code,
|
|
22836
|
+
reason,
|
|
22837
|
+
willReconnect: true,
|
|
22838
|
+
log: logFn,
|
|
22839
|
+
serviceLabel: "[Bridge service]",
|
|
22840
|
+
shutdownDetail: "Not reconnecting (shutting down).",
|
|
22841
|
+
reconnectingDetail: "Waiting for browser sign-in; reconnecting\u2026",
|
|
22842
|
+
shouldAbortQuietWindow: () => ws != null && ws.readyState === wrapper_default.OPEN
|
|
22843
|
+
});
|
|
22844
|
+
}
|
|
22656
22845
|
function cleanup() {
|
|
22846
|
+
clearReconnectQuietTimer(signInQuiet);
|
|
22657
22847
|
if (reconnectTimeout) {
|
|
22658
22848
|
clearTimeout(reconnectTimeout);
|
|
22659
22849
|
reconnectTimeout = null;
|
|
@@ -22677,6 +22867,7 @@ function runPendingAuth(options) {
|
|
|
22677
22867
|
ws = createWsBridge({
|
|
22678
22868
|
url: url2,
|
|
22679
22869
|
onOpen: () => {
|
|
22870
|
+
clearQuietOnOpen();
|
|
22680
22871
|
sendWsMessage(ws, { type: "identify", role: "cli" });
|
|
22681
22872
|
keepaliveInterval = setInterval(() => {
|
|
22682
22873
|
if (resolved || !ws || ws.readyState !== 1) return;
|
|
@@ -22697,15 +22888,21 @@ function runPendingAuth(options) {
|
|
|
22697
22888
|
keepaliveInterval = null;
|
|
22698
22889
|
}
|
|
22699
22890
|
if (resolved) return;
|
|
22700
|
-
|
|
22701
|
-
|
|
22702
|
-
|
|
22891
|
+
beginDeferredPendingCloseLog(code, reason);
|
|
22892
|
+
const delay2 = reconnectDelayMs(reconnectAttempt);
|
|
22893
|
+
reconnectAttempt += 1;
|
|
22894
|
+
if (signInQuiet.verboseLogs) {
|
|
22895
|
+
const delayLabel = formatReconnectDelayForLog(delay2);
|
|
22896
|
+
logFn(
|
|
22897
|
+
`[Bridge service] Next sign-in connection attempt in ${delayLabel} (attempt ${reconnectAttempt}).`
|
|
22898
|
+
);
|
|
22899
|
+
}
|
|
22703
22900
|
reconnectTimeout = setTimeout(() => {
|
|
22704
22901
|
reconnectTimeout = null;
|
|
22705
22902
|
connect();
|
|
22706
|
-
},
|
|
22903
|
+
}, delay2);
|
|
22707
22904
|
},
|
|
22708
|
-
onError: (err) => logFn(`[Bridge service] WebSocket error
|
|
22905
|
+
onError: (err) => logFn(`[Bridge service] WebSocket error while waiting for sign-in: ${err.message}`),
|
|
22709
22906
|
onMessage: (data) => {
|
|
22710
22907
|
const msg = data;
|
|
22711
22908
|
if (msg.type === "auth_token" && typeof msg.token === "string") {
|
|
@@ -22746,7 +22943,7 @@ function buildBridgeUrl(apiUrl, workspaceId, authToken) {
|
|
|
22746
22943
|
|
|
22747
22944
|
// src/git/discover-repos.ts
|
|
22748
22945
|
import * as fs2 from "node:fs";
|
|
22749
|
-
import * as
|
|
22946
|
+
import * as path3 from "node:path";
|
|
22750
22947
|
|
|
22751
22948
|
// ../../node_modules/.pnpm/simple-git@3.32.3/node_modules/simple-git/dist/esm/index.js
|
|
22752
22949
|
var import_file_exists = __toESM(require_dist(), 1);
|
|
@@ -24008,8 +24205,8 @@ var init_git_executor_chain = __esm2({
|
|
|
24008
24205
|
get cwd() {
|
|
24009
24206
|
return this._cwd || this._executor.cwd;
|
|
24010
24207
|
}
|
|
24011
|
-
set cwd(
|
|
24012
|
-
this._cwd =
|
|
24208
|
+
set cwd(cwd) {
|
|
24209
|
+
this._cwd = cwd;
|
|
24013
24210
|
}
|
|
24014
24211
|
get env() {
|
|
24015
24212
|
return this._executor.env;
|
|
@@ -24200,8 +24397,8 @@ var init_git_executor = __esm2({
|
|
|
24200
24397
|
"use strict";
|
|
24201
24398
|
init_git_executor_chain();
|
|
24202
24399
|
GitExecutor = class {
|
|
24203
|
-
constructor(
|
|
24204
|
-
this.cwd =
|
|
24400
|
+
constructor(cwd, _scheduler, _plugins) {
|
|
24401
|
+
this.cwd = cwd;
|
|
24205
24402
|
this._scheduler = _scheduler;
|
|
24206
24403
|
this._plugins = _plugins;
|
|
24207
24404
|
this._chain = new GitExecutorChain(this, this._scheduler, this._plugins);
|
|
@@ -27331,9 +27528,9 @@ async function isGitRepoDirectory(dirPath) {
|
|
|
27331
27528
|
}
|
|
27332
27529
|
|
|
27333
27530
|
// src/git/discover-repos.ts
|
|
27334
|
-
async function discoverGitRepos(
|
|
27531
|
+
async function discoverGitRepos(cwd = getBridgeWorkspaceDirectory()) {
|
|
27335
27532
|
const result = [];
|
|
27336
|
-
const cwdResolved =
|
|
27533
|
+
const cwdResolved = path3.resolve(cwd);
|
|
27337
27534
|
if (await isGitRepoDirectory(cwdResolved)) {
|
|
27338
27535
|
const remoteUrl = await getRemoteOriginUrl(cwdResolved);
|
|
27339
27536
|
result.push({ absolutePath: cwdResolved, remoteUrl });
|
|
@@ -27346,7 +27543,7 @@ async function discoverGitRepos(cwd3 = process.cwd()) {
|
|
|
27346
27543
|
}
|
|
27347
27544
|
for (const ent of entries) {
|
|
27348
27545
|
if (!ent.isDirectory()) continue;
|
|
27349
|
-
const childPath =
|
|
27546
|
+
const childPath = path3.join(cwdResolved, ent.name);
|
|
27350
27547
|
if (await isGitRepoDirectory(childPath)) {
|
|
27351
27548
|
const remoteUrl = await getRemoteOriginUrl(childPath);
|
|
27352
27549
|
result.push({ absolutePath: childPath, remoteUrl });
|
|
@@ -27355,11 +27552,11 @@ async function discoverGitRepos(cwd3 = process.cwd()) {
|
|
|
27355
27552
|
return result;
|
|
27356
27553
|
}
|
|
27357
27554
|
async function discoverGitReposUnderRoot(rootAbs) {
|
|
27358
|
-
const root =
|
|
27555
|
+
const root = path3.resolve(rootAbs);
|
|
27359
27556
|
const roots = [];
|
|
27360
27557
|
async function walk(dir) {
|
|
27361
27558
|
if (await isGitRepoDirectory(dir)) {
|
|
27362
|
-
roots.push(
|
|
27559
|
+
roots.push(path3.resolve(dir));
|
|
27363
27560
|
return;
|
|
27364
27561
|
}
|
|
27365
27562
|
let entries;
|
|
@@ -27370,7 +27567,7 @@ async function discoverGitReposUnderRoot(rootAbs) {
|
|
|
27370
27567
|
}
|
|
27371
27568
|
for (const ent of entries) {
|
|
27372
27569
|
if (!ent.isDirectory() || ent.name === ".git") continue;
|
|
27373
|
-
await walk(
|
|
27570
|
+
await walk(path3.join(dir, ent.name));
|
|
27374
27571
|
}
|
|
27375
27572
|
}
|
|
27376
27573
|
await walk(root);
|
|
@@ -27397,28 +27594,13 @@ function reportGitRepos(getWs, log2) {
|
|
|
27397
27594
|
}
|
|
27398
27595
|
}
|
|
27399
27596
|
}).catch((err) => {
|
|
27400
|
-
log2(
|
|
27597
|
+
log2(
|
|
27598
|
+
`[Bridge service] Git repository discovery failed: ${err instanceof Error ? err.message : String(err)}`
|
|
27599
|
+
);
|
|
27401
27600
|
});
|
|
27402
27601
|
});
|
|
27403
27602
|
}
|
|
27404
27603
|
|
|
27405
|
-
// src/bridge/connection/schedule-reconnect.ts
|
|
27406
|
-
var RECONNECT_BASE_MS = 1e3;
|
|
27407
|
-
var RECONNECT_MAX_MS = 3e4;
|
|
27408
|
-
function scheduleReconnect(state, connect, log2) {
|
|
27409
|
-
if (state.closedByUser || state.currentWs != null) return;
|
|
27410
|
-
const delay2 = Math.min(
|
|
27411
|
-
RECONNECT_BASE_MS * 2 ** state.reconnectAttempt,
|
|
27412
|
-
RECONNECT_MAX_MS
|
|
27413
|
-
);
|
|
27414
|
-
state.reconnectAttempt += 1;
|
|
27415
|
-
log2(`[Bridge service] reconnect attempt ${state.reconnectAttempt} in ${delay2 / 1e3}s\u2026`);
|
|
27416
|
-
state.reconnectTimeout = setTimeout(() => {
|
|
27417
|
-
state.reconnectTimeout = null;
|
|
27418
|
-
connect();
|
|
27419
|
-
}, delay2);
|
|
27420
|
-
}
|
|
27421
|
-
|
|
27422
27604
|
// src/bridge/connection/close-bridge-connection.ts
|
|
27423
27605
|
async function closeBridgeConnection(state, acpManager, devServerManager, log2) {
|
|
27424
27606
|
log2?.("Shutting down\u2026");
|
|
@@ -27428,25 +27610,27 @@ async function closeBridgeConnection(state, acpManager, devServerManager, log2)
|
|
|
27428
27610
|
await devServerManager.shutdownAllGraceful();
|
|
27429
27611
|
}
|
|
27430
27612
|
state.closedByUser = true;
|
|
27613
|
+
clearReconnectQuietTimer(state.mainQuiet);
|
|
27614
|
+
clearReconnectQuietTimer(state.firehoseQuiet);
|
|
27431
27615
|
if (state.reconnectTimeout != null) {
|
|
27432
27616
|
log2?.("Cancelling reconnect timer\u2026");
|
|
27433
27617
|
clearTimeout(state.reconnectTimeout);
|
|
27434
27618
|
state.reconnectTimeout = null;
|
|
27435
27619
|
}
|
|
27436
27620
|
if (state.firehoseReconnectTimeout != null) {
|
|
27437
|
-
log2?.("[Proxy and log service]
|
|
27621
|
+
log2?.("[Proxy and log service] Cancelling reconnect timer\u2026");
|
|
27438
27622
|
clearTimeout(state.firehoseReconnectTimeout);
|
|
27439
27623
|
state.firehoseReconnectTimeout = null;
|
|
27440
27624
|
}
|
|
27441
27625
|
if (state.firehoseHandle) {
|
|
27442
|
-
log2?.("[Proxy and log service]
|
|
27626
|
+
log2?.("[Proxy and log service] Closing connection (CLI shutdown)\u2026");
|
|
27443
27627
|
state.firehoseHandle.close();
|
|
27444
27628
|
state.firehoseHandle = null;
|
|
27445
27629
|
}
|
|
27446
27630
|
log2?.("Disconnecting local agents (ACP)\u2026");
|
|
27447
27631
|
acpManager.disconnect();
|
|
27448
27632
|
if (state.currentWs) {
|
|
27449
|
-
log2?.("[Bridge service]
|
|
27633
|
+
log2?.("[Bridge service] Closing connection (CLI shutdown)\u2026");
|
|
27450
27634
|
state.currentWs.removeAllListeners();
|
|
27451
27635
|
const wsState = state.currentWs.readyState;
|
|
27452
27636
|
if (wsState === 1 || wsState === 2) {
|
|
@@ -27462,11 +27646,20 @@ async function closeBridgeConnection(state, acpManager, devServerManager, log2)
|
|
|
27462
27646
|
}
|
|
27463
27647
|
|
|
27464
27648
|
// src/git/session-git-queue.ts
|
|
27649
|
+
import { execFile as execFile2 } from "node:child_process";
|
|
27650
|
+
import { promisify as promisify2 } from "node:util";
|
|
27651
|
+
import * as fs4 from "node:fs";
|
|
27652
|
+
import * as path5 from "node:path";
|
|
27653
|
+
|
|
27654
|
+
// src/git/pre-turn-snapshot.ts
|
|
27655
|
+
import * as fs3 from "node:fs";
|
|
27656
|
+
import * as path4 from "node:path";
|
|
27465
27657
|
import { execFile } from "node:child_process";
|
|
27466
27658
|
import { promisify } from "node:util";
|
|
27467
|
-
import * as path3 from "node:path";
|
|
27468
27659
|
var execFileAsync = promisify(execFile);
|
|
27469
|
-
|
|
27660
|
+
function snapshotsDirForCwd(agentCwd) {
|
|
27661
|
+
return path4.join(agentCwd, ".buildautomaton", "snapshots");
|
|
27662
|
+
}
|
|
27470
27663
|
async function gitStashCreate(repoRoot, log2) {
|
|
27471
27664
|
try {
|
|
27472
27665
|
const { stdout } = await execFileAsync("git", ["stash", "create"], {
|
|
@@ -27475,51 +27668,149 @@ async function gitStashCreate(repoRoot, log2) {
|
|
|
27475
27668
|
});
|
|
27476
27669
|
return stdout.trim();
|
|
27477
27670
|
} catch (e) {
|
|
27478
|
-
log2(
|
|
27671
|
+
log2(
|
|
27672
|
+
`[snapshot] Git stash create failed in ${repoRoot}: ${e instanceof Error ? e.message : String(e)}`
|
|
27673
|
+
);
|
|
27479
27674
|
return "";
|
|
27480
27675
|
}
|
|
27481
27676
|
}
|
|
27482
|
-
async function
|
|
27483
|
-
|
|
27484
|
-
|
|
27677
|
+
async function gitRun(repoRoot, args, log2, label) {
|
|
27678
|
+
try {
|
|
27679
|
+
await execFileAsync("git", args, { cwd: repoRoot, maxBuffer: 10 * 1024 * 1024 });
|
|
27680
|
+
return { ok: true };
|
|
27681
|
+
} catch (e) {
|
|
27682
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
27683
|
+
log2(`[snapshot] Git ${label} failed in ${repoRoot}: ${msg}`);
|
|
27684
|
+
return { ok: false, error: msg };
|
|
27685
|
+
}
|
|
27686
|
+
}
|
|
27687
|
+
async function resolveSnapshotRepoRoots(options) {
|
|
27688
|
+
const { worktreePaths, fallbackCwd, log: log2 } = options;
|
|
27689
|
+
if (worktreePaths?.length) {
|
|
27690
|
+
const uniq = [...new Set(worktreePaths.map((p) => path4.resolve(p)))];
|
|
27691
|
+
return uniq;
|
|
27692
|
+
}
|
|
27693
|
+
try {
|
|
27694
|
+
const repos = await discoverGitReposUnderRoot(fallbackCwd);
|
|
27695
|
+
return repos.map((r) => r.absolutePath);
|
|
27696
|
+
} catch (e) {
|
|
27697
|
+
log2(`[snapshot] Discover repositories failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
27698
|
+
return [];
|
|
27699
|
+
}
|
|
27700
|
+
}
|
|
27701
|
+
async function capturePreTurnSnapshot(options) {
|
|
27702
|
+
const { runId, repoRoots, agentCwd, log: log2 } = options;
|
|
27703
|
+
if (!runId || !repoRoots.length) {
|
|
27704
|
+
return { ok: false, error: "No git repos to snapshot" };
|
|
27705
|
+
}
|
|
27485
27706
|
const repos = [];
|
|
27486
27707
|
for (const root of repoRoots) {
|
|
27487
27708
|
const stashSha = await gitStashCreate(root, log2);
|
|
27488
27709
|
repos.push({ path: root, stashSha });
|
|
27489
27710
|
}
|
|
27490
|
-
|
|
27491
|
-
|
|
27711
|
+
const dir = snapshotsDirForCwd(agentCwd);
|
|
27712
|
+
try {
|
|
27713
|
+
fs3.mkdirSync(dir, { recursive: true });
|
|
27714
|
+
} catch (e) {
|
|
27715
|
+
return { ok: false, error: e instanceof Error ? e.message : String(e) };
|
|
27716
|
+
}
|
|
27717
|
+
const payload = {
|
|
27718
|
+
runId,
|
|
27719
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
27720
|
+
repos
|
|
27721
|
+
};
|
|
27722
|
+
const filePath = path4.join(dir, `${runId}.json`);
|
|
27723
|
+
try {
|
|
27724
|
+
fs3.writeFileSync(filePath, JSON.stringify(payload, null, 2), "utf8");
|
|
27725
|
+
} catch (e) {
|
|
27726
|
+
return { ok: false, error: e instanceof Error ? e.message : String(e) };
|
|
27727
|
+
}
|
|
27728
|
+
const repoList = repos.map((r) => r.path).join(", ");
|
|
27729
|
+
log2(
|
|
27730
|
+
`[snapshot] Saved pre-turn snapshot ${runId.slice(0, 8)}\u2026 (${repos.length} repo(s)): ${repoList}`
|
|
27731
|
+
);
|
|
27732
|
+
return { ok: true, filePath, repos };
|
|
27733
|
+
}
|
|
27734
|
+
async function applyPreTurnSnapshot(filePath, log2) {
|
|
27735
|
+
let data;
|
|
27736
|
+
try {
|
|
27737
|
+
const raw = fs3.readFileSync(filePath, "utf8");
|
|
27738
|
+
data = JSON.parse(raw);
|
|
27739
|
+
} catch (e) {
|
|
27740
|
+
return { ok: false, error: e instanceof Error ? e.message : String(e) };
|
|
27741
|
+
}
|
|
27742
|
+
if (!Array.isArray(data.repos)) {
|
|
27743
|
+
return { ok: false, error: "Invalid snapshot file" };
|
|
27744
|
+
}
|
|
27745
|
+
for (const r of data.repos) {
|
|
27746
|
+
if (!r.path) continue;
|
|
27747
|
+
const reset = await gitRun(r.path, ["reset", "--hard", "HEAD"], log2, "reset --hard");
|
|
27748
|
+
if (!reset.ok) return reset;
|
|
27749
|
+
const clean = await gitRun(r.path, ["clean", "-fd"], log2, "clean -fd");
|
|
27750
|
+
if (!clean.ok) return clean;
|
|
27751
|
+
if (r.stashSha) {
|
|
27752
|
+
const ap = await gitRun(r.path, ["stash", "apply", r.stashSha], log2, "stash apply");
|
|
27753
|
+
if (!ap.ok) return ap;
|
|
27754
|
+
}
|
|
27755
|
+
}
|
|
27756
|
+
log2(`[snapshot] Restored pre-turn state for ${data.runId.slice(0, 8)}\u2026`);
|
|
27757
|
+
return { ok: true };
|
|
27758
|
+
}
|
|
27759
|
+
function snapshotFilePath(agentCwd, runId) {
|
|
27760
|
+
return path4.join(snapshotsDirForCwd(agentCwd), `${runId}.json`);
|
|
27761
|
+
}
|
|
27762
|
+
|
|
27763
|
+
// src/git/session-git-queue.ts
|
|
27764
|
+
var execFileAsync2 = promisify2(execFile2);
|
|
27765
|
+
var MAX_FULL_FILE_TEXT_BYTES = 512 * 1024;
|
|
27766
|
+
function readWorkspaceFileAsUtf8(absPath) {
|
|
27767
|
+
try {
|
|
27768
|
+
const st = fs4.statSync(absPath);
|
|
27769
|
+
if (!st.isFile() || st.size > MAX_FULL_FILE_TEXT_BYTES) return void 0;
|
|
27770
|
+
return fs4.readFileSync(absPath, "utf8");
|
|
27771
|
+
} catch {
|
|
27772
|
+
return void 0;
|
|
27773
|
+
}
|
|
27492
27774
|
}
|
|
27493
|
-
async function
|
|
27494
|
-
const { sessionId, runId, sendSessionUpdate, log: log2 } = options;
|
|
27495
|
-
const
|
|
27496
|
-
|
|
27497
|
-
|
|
27775
|
+
async function collectTurnGitDiffFromPreTurnSnapshot(options) {
|
|
27776
|
+
const { sessionId, runId, agentCwd, sendSessionUpdate, log: log2 } = options;
|
|
27777
|
+
const filePath = snapshotFilePath(agentCwd, runId);
|
|
27778
|
+
let data;
|
|
27779
|
+
try {
|
|
27780
|
+
const raw = fs4.readFileSync(filePath, "utf8");
|
|
27781
|
+
data = JSON.parse(raw);
|
|
27782
|
+
} catch (e) {
|
|
27783
|
+
log2(
|
|
27784
|
+
`[session-git-queue] No pre-turn snapshot for run ${runId.slice(0, 8)}\u2026: ${e instanceof Error ? e.message : String(e)}`
|
|
27785
|
+
);
|
|
27498
27786
|
return;
|
|
27499
27787
|
}
|
|
27500
|
-
|
|
27501
|
-
|
|
27502
|
-
|
|
27788
|
+
if (!Array.isArray(data.repos) || !data.repos.length) {
|
|
27789
|
+
log2(`[session-git-queue] Empty repos in snapshot ${runId.slice(0, 8)}\u2026; skipping aggregate diff.`);
|
|
27790
|
+
return;
|
|
27791
|
+
}
|
|
27792
|
+
const multiRepo = data.repos.length > 1;
|
|
27793
|
+
for (const repo of data.repos) {
|
|
27503
27794
|
if (!repo.stashSha) continue;
|
|
27504
27795
|
let namesRaw;
|
|
27505
27796
|
try {
|
|
27506
|
-
const { stdout } = await
|
|
27797
|
+
const { stdout } = await execFileAsync2("git", ["diff", "--name-only", repo.stashSha], {
|
|
27507
27798
|
cwd: repo.path,
|
|
27508
27799
|
maxBuffer: 10 * 1024 * 1024
|
|
27509
27800
|
});
|
|
27510
27801
|
namesRaw = stdout;
|
|
27511
27802
|
} catch (e) {
|
|
27512
27803
|
log2(
|
|
27513
|
-
`[session-git-queue]
|
|
27804
|
+
`[session-git-queue] Git diff --name-only failed in ${repo.path}: ${e instanceof Error ? e.message : String(e)}`
|
|
27514
27805
|
);
|
|
27515
27806
|
continue;
|
|
27516
27807
|
}
|
|
27517
27808
|
const lines = namesRaw.split("\n").map((l) => l.trim()).filter(Boolean);
|
|
27518
|
-
const slug =
|
|
27809
|
+
const slug = path5.basename(repo.path).replace(/[^\w.-]+/g, "_") || "repo";
|
|
27519
27810
|
for (const rel of lines) {
|
|
27520
27811
|
if (rel.includes("..")) continue;
|
|
27521
27812
|
try {
|
|
27522
|
-
const { stdout: patchContent } = await
|
|
27813
|
+
const { stdout: patchContent } = await execFileAsync2(
|
|
27523
27814
|
"git",
|
|
27524
27815
|
["diff", "--no-color", repo.stashSha, "--", rel],
|
|
27525
27816
|
{
|
|
@@ -27529,15 +27820,20 @@ async function collectSessionDiffAndNotify(options) {
|
|
|
27529
27820
|
);
|
|
27530
27821
|
if (!patchContent.trim()) continue;
|
|
27531
27822
|
const displayPath = multiRepo ? `${slug}/${rel}` : rel;
|
|
27823
|
+
const absFile = path5.join(repo.path, rel);
|
|
27824
|
+
const newText = readWorkspaceFileAsUtf8(absFile);
|
|
27532
27825
|
sendSessionUpdate({
|
|
27533
27826
|
type: "session_file_change",
|
|
27534
27827
|
sessionId,
|
|
27535
27828
|
runId,
|
|
27536
27829
|
path: displayPath,
|
|
27537
|
-
patchContent
|
|
27830
|
+
patchContent,
|
|
27831
|
+
...newText !== void 0 ? { newText } : {}
|
|
27538
27832
|
});
|
|
27539
27833
|
} catch (e) {
|
|
27540
|
-
log2(
|
|
27834
|
+
log2(
|
|
27835
|
+
`[session-git-queue] Git diff failed for ${rel}: ${e instanceof Error ? e.message : String(e)}`
|
|
27836
|
+
);
|
|
27541
27837
|
}
|
|
27542
27838
|
}
|
|
27543
27839
|
}
|
|
@@ -27551,15 +27847,21 @@ async function sendPromptToAgent(options) {
|
|
|
27551
27847
|
promptId,
|
|
27552
27848
|
sessionId,
|
|
27553
27849
|
runId,
|
|
27850
|
+
agentCwd,
|
|
27554
27851
|
sendResult,
|
|
27555
27852
|
sendSessionUpdate,
|
|
27556
|
-
collectSessionDiffAfterTurn,
|
|
27557
27853
|
log: log2
|
|
27558
27854
|
} = options;
|
|
27559
27855
|
try {
|
|
27560
27856
|
const result = await handle.sendPrompt(promptText, {});
|
|
27561
|
-
if (
|
|
27562
|
-
await
|
|
27857
|
+
if (sessionId && runId && sendSessionUpdate && agentCwd && result.success) {
|
|
27858
|
+
await collectTurnGitDiffFromPreTurnSnapshot({
|
|
27859
|
+
sessionId,
|
|
27860
|
+
runId,
|
|
27861
|
+
agentCwd,
|
|
27862
|
+
sendSessionUpdate,
|
|
27863
|
+
log: log2
|
|
27864
|
+
});
|
|
27563
27865
|
}
|
|
27564
27866
|
sendResult({
|
|
27565
27867
|
type: "prompt_result",
|
|
@@ -27569,12 +27871,12 @@ async function sendPromptToAgent(options) {
|
|
|
27569
27871
|
...result
|
|
27570
27872
|
});
|
|
27571
27873
|
if (!result.success) {
|
|
27572
|
-
log2(`[
|
|
27874
|
+
log2(`[Agent] ${result.error ?? "Error"}`);
|
|
27573
27875
|
}
|
|
27574
27876
|
} catch (err) {
|
|
27575
27877
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
27576
|
-
log2(`[
|
|
27577
|
-
if (err instanceof Error && err.stack) log2(`[
|
|
27878
|
+
log2(`[Agent] Send failed: ${errMsg}`);
|
|
27879
|
+
if (err instanceof Error && err.stack) log2(`[Agent] ${err.stack}`);
|
|
27578
27880
|
sendResult({
|
|
27579
27881
|
type: "prompt_result",
|
|
27580
27882
|
id: promptId,
|
|
@@ -27587,8 +27889,8 @@ async function sendPromptToAgent(options) {
|
|
|
27587
27889
|
}
|
|
27588
27890
|
|
|
27589
27891
|
// src/acp/ensure-acp-client.ts
|
|
27590
|
-
import * as
|
|
27591
|
-
import * as
|
|
27892
|
+
import * as fs5 from "node:fs";
|
|
27893
|
+
import * as path9 from "node:path";
|
|
27592
27894
|
|
|
27593
27895
|
// src/error-message.ts
|
|
27594
27896
|
function errorMessage(err) {
|
|
@@ -27612,27 +27914,27 @@ async function createCodexAcpClient(options) {
|
|
|
27612
27914
|
}
|
|
27613
27915
|
|
|
27614
27916
|
// src/acp/clients/cursor-acp-client.ts
|
|
27615
|
-
import { readFileSync, writeFileSync, mkdirSync } from "node:fs";
|
|
27917
|
+
import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "node:fs";
|
|
27616
27918
|
import { dirname } from "node:path";
|
|
27617
27919
|
import { spawn as spawn4 } from "node:child_process";
|
|
27618
27920
|
import * as readline from "node:readline";
|
|
27619
27921
|
|
|
27620
27922
|
// src/acp/safe-fs-path.ts
|
|
27621
|
-
import * as
|
|
27622
|
-
function resolveSafePathUnderCwd(
|
|
27923
|
+
import * as path6 from "node:path";
|
|
27924
|
+
function resolveSafePathUnderCwd(cwd, filePath) {
|
|
27623
27925
|
const trimmed2 = filePath.trim();
|
|
27624
27926
|
if (!trimmed2) return null;
|
|
27625
|
-
const normalizedCwd =
|
|
27626
|
-
const resolved =
|
|
27627
|
-
const rel =
|
|
27628
|
-
if (rel.startsWith("..") ||
|
|
27927
|
+
const normalizedCwd = path6.resolve(cwd);
|
|
27928
|
+
const resolved = path6.isAbsolute(trimmed2) ? path6.normalize(trimmed2) : path6.resolve(normalizedCwd, trimmed2);
|
|
27929
|
+
const rel = path6.relative(normalizedCwd, resolved);
|
|
27930
|
+
if (rel.startsWith("..") || path6.isAbsolute(rel)) return null;
|
|
27629
27931
|
return resolved;
|
|
27630
27932
|
}
|
|
27631
|
-
function toDisplayPathRelativeToCwd(
|
|
27632
|
-
const normalizedCwd =
|
|
27633
|
-
const rel =
|
|
27634
|
-
if (!rel || rel === "") return
|
|
27635
|
-
return rel.split(
|
|
27933
|
+
function toDisplayPathRelativeToCwd(cwd, absolutePath) {
|
|
27934
|
+
const normalizedCwd = path6.resolve(cwd);
|
|
27935
|
+
const rel = path6.relative(normalizedCwd, path6.resolve(absolutePath));
|
|
27936
|
+
if (!rel || rel === "") return path6.basename(absolutePath);
|
|
27937
|
+
return rel.split(path6.sep).join("/");
|
|
27636
27938
|
}
|
|
27637
27939
|
|
|
27638
27940
|
// src/files/diff/unified-diff.ts
|
|
@@ -27681,6 +27983,19 @@ function editSnippetToUnifiedDiff(filePath, oldText, newText) {
|
|
|
27681
27983
|
return out.join("\n");
|
|
27682
27984
|
}
|
|
27683
27985
|
|
|
27986
|
+
// src/acp/format-session-update-kind-for-log.ts
|
|
27987
|
+
var SESSION_UPDATE_KIND_LABELS = {
|
|
27988
|
+
tool_call: "Tool call",
|
|
27989
|
+
tool_call_update: "Tool call status",
|
|
27990
|
+
agent_message_chunk: "Agent message chunk",
|
|
27991
|
+
update: "Session update"
|
|
27992
|
+
};
|
|
27993
|
+
function formatSessionUpdateKindForLog(kind) {
|
|
27994
|
+
const known = SESSION_UPDATE_KIND_LABELS[kind];
|
|
27995
|
+
if (known) return known;
|
|
27996
|
+
return kind.split("_").filter(Boolean).map((w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase()).join(" ");
|
|
27997
|
+
}
|
|
27998
|
+
|
|
27684
27999
|
// src/acp/clients/cursor-acp-client.ts
|
|
27685
28000
|
var FS_READ_METHODS = /* @__PURE__ */ new Set(["fs/read_text_file", "fs/readTextFile"]);
|
|
27686
28001
|
var FS_WRITE_METHODS = /* @__PURE__ */ new Set(["fs/write_text_file", "fs/writeTextFile"]);
|
|
@@ -27706,11 +28021,11 @@ function sliceLinesByRange(content, line, limit) {
|
|
|
27706
28021
|
return lines.slice(start, end).join("\n");
|
|
27707
28022
|
}
|
|
27708
28023
|
async function createCursorAcpClient(options) {
|
|
27709
|
-
const { command, cwd
|
|
28024
|
+
const { command, cwd = getBridgeWorkspaceDirectory(), onSessionUpdate, onRequest, onFileChange } = options;
|
|
27710
28025
|
const dbgFs = process.env.BUILDAMATON_DEBUG_ACP_FS === "1";
|
|
27711
28026
|
const isWindows = process.platform === "win32";
|
|
27712
28027
|
const child = spawn4(command[0], command.slice(1), {
|
|
27713
|
-
cwd
|
|
28028
|
+
cwd,
|
|
27714
28029
|
stdio: ["pipe", "pipe", "inherit"],
|
|
27715
28030
|
env: process.env,
|
|
27716
28031
|
shell: isWindows
|
|
@@ -27768,7 +28083,10 @@ async function createCursorAcpClient(options) {
|
|
|
27768
28083
|
const toolCall = update.toolCall ?? update.tool_call;
|
|
27769
28084
|
const toolName = typeof toolCall?.name === "string" ? toolCall.name : "";
|
|
27770
28085
|
if (dbgFs && (sessionUpdate === "tool_call" || sessionUpdate === "tool_call_update")) {
|
|
27771
|
-
|
|
28086
|
+
const kindLabel = formatSessionUpdateKindForLog(sessionUpdate ?? "update");
|
|
28087
|
+
console.error(
|
|
28088
|
+
`[acp] Received session update (${kindLabel}) tool=${toolName || "(none)"}`
|
|
28089
|
+
);
|
|
27772
28090
|
}
|
|
27773
28091
|
const isTextChunk = sessionUpdate === "agent_message_chunk" && update.content?.text;
|
|
27774
28092
|
if (isTextChunk && update.content?.text) {
|
|
@@ -27788,14 +28106,14 @@ async function createCursorAcpClient(options) {
|
|
|
27788
28106
|
if (dbgFs) {
|
|
27789
28107
|
console.error(`[acp-fs] ${method} path=${filePath.slice(0, 200)}${filePath.length > 200 ? "\u2026" : ""}`);
|
|
27790
28108
|
}
|
|
27791
|
-
const abs = resolveSafePathUnderCwd(
|
|
28109
|
+
const abs = resolveSafePathUnderCwd(cwd, filePath);
|
|
27792
28110
|
if (!abs) {
|
|
27793
28111
|
if (dbgFs) console.error(`[acp-fs] ${method} rejected path (outside cwd or empty)`);
|
|
27794
28112
|
respondJsonRpcError(id, -32602, "Invalid or disallowed path");
|
|
27795
28113
|
return;
|
|
27796
28114
|
}
|
|
27797
28115
|
try {
|
|
27798
|
-
let content =
|
|
28116
|
+
let content = readFileSync3(abs, "utf8");
|
|
27799
28117
|
const line2 = typeof params.line === "number" ? params.line : void 0;
|
|
27800
28118
|
const limit = typeof params.limit === "number" ? params.limit : void 0;
|
|
27801
28119
|
content = sliceLinesByRange(content, line2, limit);
|
|
@@ -27819,7 +28137,7 @@ async function createCursorAcpClient(options) {
|
|
|
27819
28137
|
`[acp-fs] ${method} path=${filePath.slice(0, 200)}${filePath.length > 200 ? "\u2026" : ""} newBytes=${newText.length}`
|
|
27820
28138
|
);
|
|
27821
28139
|
}
|
|
27822
|
-
const abs = resolveSafePathUnderCwd(
|
|
28140
|
+
const abs = resolveSafePathUnderCwd(cwd, filePath);
|
|
27823
28141
|
if (!abs) {
|
|
27824
28142
|
if (dbgFs) console.error(`[acp-fs] ${method} rejected path (outside cwd or empty): ${filePath.slice(0, 120)}`);
|
|
27825
28143
|
respondJsonRpcError(id, -32602, "Invalid or disallowed path");
|
|
@@ -27827,7 +28145,7 @@ async function createCursorAcpClient(options) {
|
|
|
27827
28145
|
}
|
|
27828
28146
|
let oldText = "";
|
|
27829
28147
|
try {
|
|
27830
|
-
oldText =
|
|
28148
|
+
oldText = readFileSync3(abs, "utf8");
|
|
27831
28149
|
} catch (e) {
|
|
27832
28150
|
if (e.code !== "ENOENT") {
|
|
27833
28151
|
respondJsonRpcError(id, -32e3, e instanceof Error ? e.message : String(e));
|
|
@@ -27835,13 +28153,13 @@ async function createCursorAcpClient(options) {
|
|
|
27835
28153
|
}
|
|
27836
28154
|
}
|
|
27837
28155
|
try {
|
|
27838
|
-
|
|
27839
|
-
|
|
28156
|
+
mkdirSync2(dirname(abs), { recursive: true });
|
|
28157
|
+
writeFileSync2(abs, newText, "utf8");
|
|
27840
28158
|
} catch (e) {
|
|
27841
28159
|
respondJsonRpcError(id, -32e3, e instanceof Error ? e.message : String(e));
|
|
27842
28160
|
return;
|
|
27843
28161
|
}
|
|
27844
|
-
const displayPath = toDisplayPathRelativeToCwd(
|
|
28162
|
+
const displayPath = toDisplayPathRelativeToCwd(cwd, abs);
|
|
27845
28163
|
const patchContent = editSnippetToUnifiedDiff(displayPath, oldText, newText);
|
|
27846
28164
|
onFileChange?.({ path: displayPath, oldText, newText, patchContent });
|
|
27847
28165
|
respond(id, null);
|
|
@@ -27883,7 +28201,7 @@ async function createCursorAcpClient(options) {
|
|
|
27883
28201
|
clientInfo: { name: "buildautomaton-cli", version: "0.1.0" }
|
|
27884
28202
|
});
|
|
27885
28203
|
await send("authenticate", { methodId: "cursor_login" });
|
|
27886
|
-
const newResult = await send("session/new", { cwd
|
|
28204
|
+
const newResult = await send("session/new", { cwd, mcpServers: [] });
|
|
27887
28205
|
const sessionId = newResult?.sessionId ?? "";
|
|
27888
28206
|
if (!sessionId) throw new Error("Cursor ACP session/new did not return sessionId");
|
|
27889
28207
|
resolve14({
|
|
@@ -27955,20 +28273,20 @@ function resolveAgentCommand(preferredAgentType) {
|
|
|
27955
28273
|
|
|
27956
28274
|
// src/acp/session-file-change-path-kind.ts
|
|
27957
28275
|
import { execFileSync as execFileSync3 } from "node:child_process";
|
|
27958
|
-
import { existsSync, statSync } from "node:fs";
|
|
28276
|
+
import { existsSync, statSync as statSync2 } from "node:fs";
|
|
27959
28277
|
|
|
27960
28278
|
// src/git/get-git-repo-root-sync.ts
|
|
27961
28279
|
import { execFileSync } from "node:child_process";
|
|
27962
|
-
import * as
|
|
28280
|
+
import * as path7 from "node:path";
|
|
27963
28281
|
function getGitRepoRootSync(startDir) {
|
|
27964
28282
|
try {
|
|
27965
28283
|
const out = execFileSync("git", ["rev-parse", "--show-toplevel"], {
|
|
27966
|
-
cwd:
|
|
28284
|
+
cwd: path7.resolve(startDir),
|
|
27967
28285
|
encoding: "utf8",
|
|
27968
28286
|
stdio: ["ignore", "pipe", "ignore"],
|
|
27969
28287
|
maxBuffer: 1024 * 1024
|
|
27970
28288
|
}).trim();
|
|
27971
|
-
return out ?
|
|
28289
|
+
return out ? path7.resolve(out) : null;
|
|
27972
28290
|
} catch {
|
|
27973
28291
|
return null;
|
|
27974
28292
|
}
|
|
@@ -27976,65 +28294,65 @@ function getGitRepoRootSync(startDir) {
|
|
|
27976
28294
|
|
|
27977
28295
|
// src/acp/workspace-files.ts
|
|
27978
28296
|
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
27979
|
-
import { readFileSync as
|
|
27980
|
-
import * as
|
|
27981
|
-
function resolveWorkspaceFilePath(
|
|
28297
|
+
import { readFileSync as readFileSync4 } from "node:fs";
|
|
28298
|
+
import * as path8 from "node:path";
|
|
28299
|
+
function resolveWorkspaceFilePath(cwd, rawPath) {
|
|
27982
28300
|
const trimmed2 = rawPath.trim();
|
|
27983
28301
|
if (!trimmed2) return null;
|
|
27984
|
-
const normalizedCwd =
|
|
27985
|
-
let abs = resolveSafePathUnderCwd(
|
|
28302
|
+
const normalizedCwd = path8.resolve(cwd);
|
|
28303
|
+
let abs = resolveSafePathUnderCwd(cwd, trimmed2);
|
|
27986
28304
|
if (!abs) {
|
|
27987
|
-
const candidate =
|
|
27988
|
-
const gitRoot2 = getGitRepoRootSync(
|
|
28305
|
+
const candidate = path8.isAbsolute(trimmed2) ? path8.normalize(trimmed2) : path8.normalize(path8.resolve(normalizedCwd, trimmed2));
|
|
28306
|
+
const gitRoot2 = getGitRepoRootSync(cwd);
|
|
27989
28307
|
if (!gitRoot2) return null;
|
|
27990
|
-
const rel =
|
|
27991
|
-
if (rel.startsWith("..") ||
|
|
28308
|
+
const rel = path8.relative(gitRoot2, candidate);
|
|
28309
|
+
if (rel.startsWith("..") || path8.isAbsolute(rel)) return null;
|
|
27992
28310
|
abs = candidate;
|
|
27993
28311
|
}
|
|
27994
|
-
const gitRoot = getGitRepoRootSync(
|
|
28312
|
+
const gitRoot = getGitRepoRootSync(cwd);
|
|
27995
28313
|
if (gitRoot) {
|
|
27996
|
-
const relFromRoot =
|
|
27997
|
-
if (!relFromRoot.startsWith("..") && !
|
|
27998
|
-
return { abs, display: relFromRoot.split(
|
|
28314
|
+
const relFromRoot = path8.relative(gitRoot, abs);
|
|
28315
|
+
if (!relFromRoot.startsWith("..") && !path8.isAbsolute(relFromRoot)) {
|
|
28316
|
+
return { abs, display: relFromRoot.split(path8.sep).join("/") };
|
|
27999
28317
|
}
|
|
28000
28318
|
}
|
|
28001
|
-
return { abs, display: toDisplayPathRelativeToCwd(
|
|
28319
|
+
return { abs, display: toDisplayPathRelativeToCwd(cwd, abs) };
|
|
28002
28320
|
}
|
|
28003
|
-
function readUtf8WorkspaceFile(
|
|
28321
|
+
function readUtf8WorkspaceFile(cwd, displayPath) {
|
|
28004
28322
|
if (!displayPath || displayPath.includes("..")) return "";
|
|
28005
|
-
const gitRoot = getGitRepoRootSync(
|
|
28323
|
+
const gitRoot = getGitRepoRootSync(cwd);
|
|
28006
28324
|
if (gitRoot) {
|
|
28007
|
-
const abs2 =
|
|
28008
|
-
const rel =
|
|
28009
|
-
if (!rel.startsWith("..") && !
|
|
28325
|
+
const abs2 = path8.resolve(gitRoot, displayPath);
|
|
28326
|
+
const rel = path8.relative(gitRoot, abs2);
|
|
28327
|
+
if (!rel.startsWith("..") && !path8.isAbsolute(rel)) {
|
|
28010
28328
|
try {
|
|
28011
|
-
return
|
|
28329
|
+
return readFileSync4(abs2, "utf8");
|
|
28012
28330
|
} catch {
|
|
28013
28331
|
}
|
|
28014
28332
|
}
|
|
28015
28333
|
}
|
|
28016
|
-
const abs = resolveSafePathUnderCwd(
|
|
28334
|
+
const abs = resolveSafePathUnderCwd(cwd, displayPath);
|
|
28017
28335
|
if (!abs) return "";
|
|
28018
28336
|
try {
|
|
28019
|
-
return
|
|
28337
|
+
return readFileSync4(abs, "utf8");
|
|
28020
28338
|
} catch {
|
|
28021
28339
|
return "";
|
|
28022
28340
|
}
|
|
28023
28341
|
}
|
|
28024
|
-
function tryWorkspaceDisplayToAbs(
|
|
28342
|
+
function tryWorkspaceDisplayToAbs(cwd, displayPath) {
|
|
28025
28343
|
if (!displayPath || displayPath.includes("..")) return null;
|
|
28026
|
-
const gitRoot = getGitRepoRootSync(
|
|
28344
|
+
const gitRoot = getGitRepoRootSync(cwd);
|
|
28027
28345
|
if (gitRoot) {
|
|
28028
|
-
const abs =
|
|
28029
|
-
const rel =
|
|
28030
|
-
if (!rel.startsWith("..") && !
|
|
28346
|
+
const abs = path8.resolve(gitRoot, displayPath);
|
|
28347
|
+
const rel = path8.relative(gitRoot, abs);
|
|
28348
|
+
if (!rel.startsWith("..") && !path8.isAbsolute(rel)) return abs;
|
|
28031
28349
|
}
|
|
28032
|
-
return resolveSafePathUnderCwd(
|
|
28350
|
+
return resolveSafePathUnderCwd(cwd, displayPath);
|
|
28033
28351
|
}
|
|
28034
|
-
function readGitHeadBlob(
|
|
28352
|
+
function readGitHeadBlob(cwd, displayPath) {
|
|
28035
28353
|
if (!displayPath || displayPath.includes("..")) return "";
|
|
28036
|
-
const gitRoot = getGitRepoRootSync(
|
|
28037
|
-
const execCwd = gitRoot ??
|
|
28354
|
+
const gitRoot = getGitRepoRootSync(cwd);
|
|
28355
|
+
const execCwd = gitRoot ?? cwd;
|
|
28038
28356
|
try {
|
|
28039
28357
|
return execFileSync2("git", ["show", `HEAD:${displayPath}`], {
|
|
28040
28358
|
cwd: execCwd,
|
|
@@ -28047,9 +28365,9 @@ function readGitHeadBlob(cwd3, displayPath) {
|
|
|
28047
28365
|
}
|
|
28048
28366
|
|
|
28049
28367
|
// src/acp/session-file-change-path-kind.ts
|
|
28050
|
-
function gitHeadPathObjectType(
|
|
28368
|
+
function gitHeadPathObjectType(cwd, displayPath) {
|
|
28051
28369
|
if (!displayPath || displayPath.includes("..")) return null;
|
|
28052
|
-
const gitRoot = getGitRepoRootSync(
|
|
28370
|
+
const gitRoot = getGitRepoRootSync(cwd);
|
|
28053
28371
|
if (!gitRoot) return null;
|
|
28054
28372
|
try {
|
|
28055
28373
|
return execFileSync3("git", ["cat-file", "-t", `HEAD:${displayPath}`], {
|
|
@@ -28060,11 +28378,11 @@ function gitHeadPathObjectType(cwd3, displayPath) {
|
|
|
28060
28378
|
return null;
|
|
28061
28379
|
}
|
|
28062
28380
|
}
|
|
28063
|
-
function getSessionFileChangeDirectoryFlags(
|
|
28064
|
-
const abs = tryWorkspaceDisplayToAbs(
|
|
28381
|
+
function getSessionFileChangeDirectoryFlags(cwd, displayPath) {
|
|
28382
|
+
const abs = tryWorkspaceDisplayToAbs(cwd, displayPath);
|
|
28065
28383
|
if (abs && existsSync(abs)) {
|
|
28066
28384
|
try {
|
|
28067
|
-
if (
|
|
28385
|
+
if (statSync2(abs).isDirectory()) {
|
|
28068
28386
|
return { isDirectory: true, directoryRemoved: false };
|
|
28069
28387
|
}
|
|
28070
28388
|
return { isDirectory: false, directoryRemoved: false };
|
|
@@ -28072,7 +28390,7 @@ function getSessionFileChangeDirectoryFlags(cwd3, displayPath) {
|
|
|
28072
28390
|
return { isDirectory: false, directoryRemoved: false };
|
|
28073
28391
|
}
|
|
28074
28392
|
}
|
|
28075
|
-
if (gitHeadPathObjectType(
|
|
28393
|
+
if (gitHeadPathObjectType(cwd, displayPath) === "tree") {
|
|
28076
28394
|
return { isDirectory: true, directoryRemoved: true };
|
|
28077
28395
|
}
|
|
28078
28396
|
return { isDirectory: false, directoryRemoved: false };
|
|
@@ -28086,11 +28404,13 @@ function createBridgeOnFileChange(opts) {
|
|
|
28086
28404
|
const sessionId = routing.sessionId;
|
|
28087
28405
|
const send = getSendSessionUpdate();
|
|
28088
28406
|
if (!send || !runId || !sessionId) {
|
|
28089
|
-
log2(
|
|
28407
|
+
log2(
|
|
28408
|
+
`[Bridge service] File change not forwarded (${evt.path}): session or run not wired to the bridge.`
|
|
28409
|
+
);
|
|
28090
28410
|
return;
|
|
28091
28411
|
}
|
|
28092
|
-
const
|
|
28093
|
-
const dirFlags = getSessionFileChangeDirectoryFlags(
|
|
28412
|
+
const cwd = getBridgeWorkspaceDirectory();
|
|
28413
|
+
const dirFlags = getSessionFileChangeDirectoryFlags(cwd, evt.path);
|
|
28094
28414
|
try {
|
|
28095
28415
|
send({
|
|
28096
28416
|
type: "session_file_change",
|
|
@@ -28104,7 +28424,7 @@ function createBridgeOnFileChange(opts) {
|
|
|
28104
28424
|
directoryRemoved: dirFlags.directoryRemoved
|
|
28105
28425
|
});
|
|
28106
28426
|
} catch (err) {
|
|
28107
|
-
log2(`[Bridge service]
|
|
28427
|
+
log2(`[Bridge service] Session file change failed for ${evt.path}: ${errorMessage(err)}`);
|
|
28108
28428
|
}
|
|
28109
28429
|
};
|
|
28110
28430
|
}
|
|
@@ -28131,7 +28451,9 @@ function createBridgeOnRequest(opts) {
|
|
|
28131
28451
|
}
|
|
28132
28452
|
});
|
|
28133
28453
|
} catch (err) {
|
|
28134
|
-
log2(
|
|
28454
|
+
log2(
|
|
28455
|
+
`[Bridge service] Agent protocol request forward failed (${request.method}): ${errorMessage(err)}`
|
|
28456
|
+
);
|
|
28135
28457
|
}
|
|
28136
28458
|
};
|
|
28137
28459
|
}
|
|
@@ -28163,12 +28485,12 @@ function extractDiffPath(o) {
|
|
|
28163
28485
|
}
|
|
28164
28486
|
|
|
28165
28487
|
// src/acp/hooks/extract-acp-file-diffs-from-update/push-diff.ts
|
|
28166
|
-
function pushDiffIfComplete(o,
|
|
28488
|
+
function pushDiffIfComplete(o, cwd, out) {
|
|
28167
28489
|
const t = o.type;
|
|
28168
28490
|
if (typeof t !== "string" || t.toLowerCase() !== "diff") return;
|
|
28169
28491
|
const rawPath = extractDiffPath(o);
|
|
28170
28492
|
if (!rawPath) return;
|
|
28171
|
-
const resolved = resolveWorkspaceFilePath(
|
|
28493
|
+
const resolved = resolveWorkspaceFilePath(cwd, rawPath);
|
|
28172
28494
|
if (!resolved) return;
|
|
28173
28495
|
const oldText = readOptionalTextField(o.oldText ?? o.old_text ?? o.before ?? o.oldContent);
|
|
28174
28496
|
const newText = readOptionalTextField(o.newText ?? o.new_text ?? o.after ?? o.newContent);
|
|
@@ -28187,17 +28509,17 @@ var NEST_KEYS = [
|
|
|
28187
28509
|
"data",
|
|
28188
28510
|
"arguments"
|
|
28189
28511
|
];
|
|
28190
|
-
function walkValue(value,
|
|
28512
|
+
function walkValue(value, cwd, depth, out) {
|
|
28191
28513
|
if (depth > 12 || value == null) return;
|
|
28192
28514
|
if (Array.isArray(value)) {
|
|
28193
|
-
for (const x of value) walkValue(x,
|
|
28515
|
+
for (const x of value) walkValue(x, cwd, depth + 1, out);
|
|
28194
28516
|
return;
|
|
28195
28517
|
}
|
|
28196
28518
|
if (typeof value !== "object") return;
|
|
28197
28519
|
const o = value;
|
|
28198
|
-
pushDiffIfComplete(o,
|
|
28520
|
+
pushDiffIfComplete(o, cwd, out);
|
|
28199
28521
|
if (o.type === "content" && o.content != null && typeof o.content === "object") {
|
|
28200
|
-
walkValue(o.content,
|
|
28522
|
+
walkValue(o.content, cwd, depth + 1, out);
|
|
28201
28523
|
}
|
|
28202
28524
|
for (const k of NEST_KEYS) {
|
|
28203
28525
|
if (o.type === "content" && k === "content") continue;
|
|
@@ -28205,23 +28527,23 @@ function walkValue(value, cwd3, depth, out) {
|
|
|
28205
28527
|
if (v == null) continue;
|
|
28206
28528
|
if (k === "arguments" && typeof v === "string") {
|
|
28207
28529
|
try {
|
|
28208
|
-
walkValue(JSON.parse(v),
|
|
28530
|
+
walkValue(JSON.parse(v), cwd, depth + 1, out);
|
|
28209
28531
|
} catch {
|
|
28210
28532
|
}
|
|
28211
28533
|
continue;
|
|
28212
28534
|
}
|
|
28213
|
-
walkValue(v,
|
|
28535
|
+
walkValue(v, cwd, depth + 1, out);
|
|
28214
28536
|
}
|
|
28215
28537
|
}
|
|
28216
28538
|
|
|
28217
28539
|
// src/acp/hooks/extract-acp-file-diffs-from-update/extract.ts
|
|
28218
|
-
function extractAcpFileDiffsFromUpdate(update,
|
|
28540
|
+
function extractAcpFileDiffsFromUpdate(update, cwd) {
|
|
28219
28541
|
if (!update || typeof update !== "object") return [];
|
|
28220
28542
|
const u = update;
|
|
28221
28543
|
const out = [];
|
|
28222
28544
|
const content = u.content;
|
|
28223
28545
|
if (Array.isArray(content)) {
|
|
28224
|
-
for (const x of content) walkValue(x,
|
|
28546
|
+
for (const x of content) walkValue(x, cwd, 0, out);
|
|
28225
28547
|
}
|
|
28226
28548
|
const byPath = /* @__PURE__ */ new Map();
|
|
28227
28549
|
for (const d of out) byPath.set(d.path, d);
|
|
@@ -28229,18 +28551,18 @@ function extractAcpFileDiffsFromUpdate(update, cwd3) {
|
|
|
28229
28551
|
}
|
|
28230
28552
|
|
|
28231
28553
|
// src/acp/hooks/extract-tool-target-paths.ts
|
|
28232
|
-
function addPath(
|
|
28554
|
+
function addPath(cwd, raw, out) {
|
|
28233
28555
|
if (typeof raw !== "string") return;
|
|
28234
28556
|
const trimmed2 = raw.trim();
|
|
28235
28557
|
if (!trimmed2) return;
|
|
28236
|
-
const resolved = resolveWorkspaceFilePath(
|
|
28558
|
+
const resolved = resolveWorkspaceFilePath(cwd, trimmed2);
|
|
28237
28559
|
if (!resolved) return;
|
|
28238
28560
|
out.add(resolved.display);
|
|
28239
28561
|
}
|
|
28240
|
-
function walkLocations(
|
|
28562
|
+
function walkLocations(cwd, loc, out) {
|
|
28241
28563
|
if (!Array.isArray(loc)) return;
|
|
28242
28564
|
for (const item of loc) {
|
|
28243
|
-
if (item && typeof item === "object") addPath(
|
|
28565
|
+
if (item && typeof item === "object") addPath(cwd, item.path, out);
|
|
28244
28566
|
}
|
|
28245
28567
|
}
|
|
28246
28568
|
var PATH_KEYS = [
|
|
@@ -28253,56 +28575,56 @@ var PATH_KEYS = [
|
|
|
28253
28575
|
"file_path",
|
|
28254
28576
|
"target_file"
|
|
28255
28577
|
];
|
|
28256
|
-
function collectFromObject(
|
|
28578
|
+
function collectFromObject(cwd, obj, out, depth) {
|
|
28257
28579
|
if (depth > 10) return;
|
|
28258
28580
|
for (const k of PATH_KEYS) {
|
|
28259
|
-
if (k in obj) addPath(
|
|
28581
|
+
if (k in obj) addPath(cwd, obj[k], out);
|
|
28260
28582
|
}
|
|
28261
28583
|
for (const v of Object.values(obj)) {
|
|
28262
|
-
if (v != null && typeof v === "object") collectUnknown(
|
|
28584
|
+
if (v != null && typeof v === "object") collectUnknown(cwd, v, out, depth + 1);
|
|
28263
28585
|
}
|
|
28264
28586
|
}
|
|
28265
|
-
function collectUnknown(
|
|
28587
|
+
function collectUnknown(cwd, v, out, depth) {
|
|
28266
28588
|
if (depth > 10 || v == null) return;
|
|
28267
28589
|
if (Array.isArray(v)) {
|
|
28268
|
-
for (const x of v) collectUnknown(
|
|
28590
|
+
for (const x of v) collectUnknown(cwd, x, out, depth + 1);
|
|
28269
28591
|
return;
|
|
28270
28592
|
}
|
|
28271
|
-
if (typeof v === "object") collectFromObject(
|
|
28593
|
+
if (typeof v === "object") collectFromObject(cwd, v, out, depth);
|
|
28272
28594
|
}
|
|
28273
|
-
function walkContentArray(
|
|
28595
|
+
function walkContentArray(cwd, content, out) {
|
|
28274
28596
|
if (!Array.isArray(content)) return;
|
|
28275
28597
|
for (const item of content) {
|
|
28276
28598
|
if (!item || typeof item !== "object") continue;
|
|
28277
28599
|
const o = item;
|
|
28278
28600
|
if (typeof o.type === "string" && o.type.toLowerCase() === "diff") {
|
|
28279
|
-
for (const k of PATH_KEYS) if (k in o) addPath(
|
|
28601
|
+
for (const k of PATH_KEYS) if (k in o) addPath(cwd, o[k], out);
|
|
28280
28602
|
}
|
|
28281
28603
|
if (o.type === "content" && o.content != null && typeof o.content === "object") {
|
|
28282
28604
|
const inner = o.content;
|
|
28283
28605
|
if (typeof inner.type === "string" && inner.type.toLowerCase() === "diff") {
|
|
28284
|
-
for (const k of PATH_KEYS) if (k in inner) addPath(
|
|
28606
|
+
for (const k of PATH_KEYS) if (k in inner) addPath(cwd, inner[k], out);
|
|
28285
28607
|
}
|
|
28286
28608
|
}
|
|
28287
28609
|
}
|
|
28288
28610
|
}
|
|
28289
|
-
function extractToolTargetDisplayPaths(update,
|
|
28611
|
+
function extractToolTargetDisplayPaths(update, cwd) {
|
|
28290
28612
|
const out = /* @__PURE__ */ new Set();
|
|
28291
28613
|
if (!update || typeof update !== "object") return [];
|
|
28292
28614
|
const u = update;
|
|
28293
|
-
walkLocations(
|
|
28294
|
-
walkLocations(
|
|
28295
|
-
walkLocations(
|
|
28615
|
+
walkLocations(cwd, u.locations, out);
|
|
28616
|
+
walkLocations(cwd, u.fileLocations, out);
|
|
28617
|
+
walkLocations(cwd, u.file_locations, out);
|
|
28296
28618
|
const tc = u.toolCall ?? u.tool_call;
|
|
28297
28619
|
if (tc && typeof tc.arguments === "string") {
|
|
28298
28620
|
try {
|
|
28299
28621
|
const parsed = JSON.parse(tc.arguments);
|
|
28300
|
-
collectUnknown(
|
|
28622
|
+
collectUnknown(cwd, parsed, out, 0);
|
|
28301
28623
|
} catch {
|
|
28302
28624
|
}
|
|
28303
28625
|
}
|
|
28304
|
-
walkContentArray(
|
|
28305
|
-
collectFromObject(
|
|
28626
|
+
walkContentArray(cwd, u.content, out);
|
|
28627
|
+
collectFromObject(cwd, u, out, 0);
|
|
28306
28628
|
return [...out];
|
|
28307
28629
|
}
|
|
28308
28630
|
|
|
@@ -28368,7 +28690,7 @@ var PathSnapshotTracker = class {
|
|
|
28368
28690
|
this.beforeByToolKey.delete(toolKey);
|
|
28369
28691
|
this.accumulatedPathsByToolKey.delete(toolKey);
|
|
28370
28692
|
}
|
|
28371
|
-
captureBeforeFromDisk(toolKey, paths,
|
|
28693
|
+
captureBeforeFromDisk(toolKey, paths, cwd) {
|
|
28372
28694
|
if (paths.length === 0) return;
|
|
28373
28695
|
let m = this.beforeByToolKey.get(toolKey);
|
|
28374
28696
|
if (!m) {
|
|
@@ -28377,10 +28699,10 @@ var PathSnapshotTracker = class {
|
|
|
28377
28699
|
}
|
|
28378
28700
|
for (const p of paths) {
|
|
28379
28701
|
if (m.has(p)) continue;
|
|
28380
|
-
m.set(p, readUtf8WorkspaceFile(
|
|
28702
|
+
m.set(p, readUtf8WorkspaceFile(cwd, p));
|
|
28381
28703
|
}
|
|
28382
28704
|
}
|
|
28383
|
-
ensureBeforeFromHeadForMissing(toolKey, paths,
|
|
28705
|
+
ensureBeforeFromHeadForMissing(toolKey, paths, cwd) {
|
|
28384
28706
|
let m = this.beforeByToolKey.get(toolKey);
|
|
28385
28707
|
if (!m) {
|
|
28386
28708
|
m = /* @__PURE__ */ new Map();
|
|
@@ -28388,10 +28710,10 @@ var PathSnapshotTracker = class {
|
|
|
28388
28710
|
}
|
|
28389
28711
|
for (const p of paths) {
|
|
28390
28712
|
if (m.has(p)) continue;
|
|
28391
|
-
m.set(p, readGitHeadBlob(
|
|
28713
|
+
m.set(p, readGitHeadBlob(cwd, p));
|
|
28392
28714
|
}
|
|
28393
28715
|
}
|
|
28394
|
-
flushPathSnapshots(toolKey,
|
|
28716
|
+
flushPathSnapshots(toolKey, cwd, sentPaths, send, runId, sessionId, log2) {
|
|
28395
28717
|
const t = this.debouncers.get(toolKey);
|
|
28396
28718
|
if (t) clearTimeout(t);
|
|
28397
28719
|
this.debouncers.delete(toolKey);
|
|
@@ -28400,10 +28722,10 @@ var PathSnapshotTracker = class {
|
|
|
28400
28722
|
this.beforeByToolKey.delete(toolKey);
|
|
28401
28723
|
if (!send || !runId || !sessionId) return;
|
|
28402
28724
|
for (const [displayPath, oldText] of beforeMap) {
|
|
28403
|
-
const newText = readUtf8WorkspaceFile(
|
|
28725
|
+
const newText = readUtf8WorkspaceFile(cwd, displayPath);
|
|
28404
28726
|
if (oldText === newText) continue;
|
|
28405
28727
|
const patchContent = editSnippetToUnifiedDiff(displayPath, oldText, newText);
|
|
28406
|
-
const dirFlags = getSessionFileChangeDirectoryFlags(
|
|
28728
|
+
const dirFlags = getSessionFileChangeDirectoryFlags(cwd, displayPath);
|
|
28407
28729
|
try {
|
|
28408
28730
|
send({
|
|
28409
28731
|
type: "session_file_change",
|
|
@@ -28418,35 +28740,35 @@ var PathSnapshotTracker = class {
|
|
|
28418
28740
|
});
|
|
28419
28741
|
sentPaths.add(displayPath);
|
|
28420
28742
|
} catch (err) {
|
|
28421
|
-
log2(`[Bridge service]
|
|
28743
|
+
log2(`[Bridge service] Session file change failed for ${displayPath}: ${errorMessage(err)}`);
|
|
28422
28744
|
}
|
|
28423
28745
|
}
|
|
28424
28746
|
}
|
|
28425
|
-
scheduleDebouncedFlush(toolKey,
|
|
28747
|
+
scheduleDebouncedFlush(toolKey, cwd, sentPaths, send, runId, sessionId, log2) {
|
|
28426
28748
|
const prev = this.debouncers.get(toolKey);
|
|
28427
28749
|
if (prev) clearTimeout(prev);
|
|
28428
28750
|
this.debouncers.set(
|
|
28429
28751
|
toolKey,
|
|
28430
28752
|
setTimeout(() => {
|
|
28431
28753
|
this.debouncers.delete(toolKey);
|
|
28432
|
-
this.flushPathSnapshots(toolKey,
|
|
28754
|
+
this.flushPathSnapshots(toolKey, cwd, sentPaths, send, runId, sessionId, log2);
|
|
28433
28755
|
}, PATH_SNAPSHOT_DEBOUNCE_MS)
|
|
28434
28756
|
);
|
|
28435
28757
|
}
|
|
28436
|
-
handleToolCallLifecycle(updateKind, toolKey, toolPaths, status,
|
|
28758
|
+
handleToolCallLifecycle(updateKind, toolKey, toolPaths, status, cwd, sentPaths, send, runId, sessionId, log2) {
|
|
28437
28759
|
if (updateKind === "tool_call") {
|
|
28438
28760
|
this.resetToolSnapshots(toolKey);
|
|
28439
28761
|
}
|
|
28440
28762
|
if (updateKind === "tool_call") {
|
|
28441
|
-
this.captureBeforeFromDisk(toolKey, toolPaths,
|
|
28763
|
+
this.captureBeforeFromDisk(toolKey, toolPaths, cwd);
|
|
28442
28764
|
} else if (updateKind === "tool_call_update") {
|
|
28443
28765
|
if (isCompletedToolStatus(status)) {
|
|
28444
|
-
this.ensureBeforeFromHeadForMissing(toolKey, toolPaths,
|
|
28445
|
-
this.flushPathSnapshots(toolKey,
|
|
28766
|
+
this.ensureBeforeFromHeadForMissing(toolKey, toolPaths, cwd);
|
|
28767
|
+
this.flushPathSnapshots(toolKey, cwd, sentPaths, send, runId, sessionId, log2);
|
|
28446
28768
|
} else {
|
|
28447
|
-
this.captureBeforeFromDisk(toolKey, toolPaths,
|
|
28769
|
+
this.captureBeforeFromDisk(toolKey, toolPaths, cwd);
|
|
28448
28770
|
if (this.beforeByToolKey.has(toolKey)) {
|
|
28449
|
-
this.scheduleDebouncedFlush(toolKey,
|
|
28771
|
+
this.scheduleDebouncedFlush(toolKey, cwd, sentPaths, send, runId, sessionId, log2);
|
|
28450
28772
|
}
|
|
28451
28773
|
}
|
|
28452
28774
|
}
|
|
@@ -28454,11 +28776,11 @@ var PathSnapshotTracker = class {
|
|
|
28454
28776
|
};
|
|
28455
28777
|
|
|
28456
28778
|
// src/acp/hooks/bridge-on-session-update/send-structured-file-changes.ts
|
|
28457
|
-
function sendExtractedDiffsAsSessionFileChanges(diffs, send,
|
|
28779
|
+
function sendExtractedDiffsAsSessionFileChanges(diffs, send, cwd, sessionId, runId, sentPaths, log2) {
|
|
28458
28780
|
for (const d of diffs) {
|
|
28459
28781
|
try {
|
|
28460
28782
|
const patchContent = editSnippetToUnifiedDiff(d.path, d.oldText, d.newText);
|
|
28461
|
-
const dirFlags = getSessionFileChangeDirectoryFlags(
|
|
28783
|
+
const dirFlags = getSessionFileChangeDirectoryFlags(cwd, d.path);
|
|
28462
28784
|
send({
|
|
28463
28785
|
type: "session_file_change",
|
|
28464
28786
|
sessionId,
|
|
@@ -28472,19 +28794,19 @@ function sendExtractedDiffsAsSessionFileChanges(diffs, send, cwd3, sessionId, ru
|
|
|
28472
28794
|
});
|
|
28473
28795
|
sentPaths.add(d.path);
|
|
28474
28796
|
} catch (err) {
|
|
28475
|
-
log2(`[Bridge service]
|
|
28797
|
+
log2(`[Bridge service] Session file change failed for ${d.path}: ${errorMessage(err)}`);
|
|
28476
28798
|
}
|
|
28477
28799
|
}
|
|
28478
28800
|
}
|
|
28479
|
-
function sendGitHeadVsWorkspaceForToolPaths(mergedPaths, sentPaths, send,
|
|
28801
|
+
function sendGitHeadVsWorkspaceForToolPaths(mergedPaths, sentPaths, send, cwd, sessionId, runId, log2) {
|
|
28480
28802
|
for (const displayPath of mergedPaths) {
|
|
28481
28803
|
if (sentPaths.has(displayPath)) continue;
|
|
28482
|
-
const oldText = readGitHeadBlob(
|
|
28483
|
-
const newText = readUtf8WorkspaceFile(
|
|
28804
|
+
const oldText = readGitHeadBlob(cwd, displayPath);
|
|
28805
|
+
const newText = readUtf8WorkspaceFile(cwd, displayPath);
|
|
28484
28806
|
if (oldText === newText) continue;
|
|
28485
28807
|
try {
|
|
28486
28808
|
const patchContent = editSnippetToUnifiedDiff(displayPath, oldText, newText);
|
|
28487
|
-
const dirFlags = getSessionFileChangeDirectoryFlags(
|
|
28809
|
+
const dirFlags = getSessionFileChangeDirectoryFlags(cwd, displayPath);
|
|
28488
28810
|
send({
|
|
28489
28811
|
type: "session_file_change",
|
|
28490
28812
|
sessionId,
|
|
@@ -28498,7 +28820,7 @@ function sendGitHeadVsWorkspaceForToolPaths(mergedPaths, sentPaths, send, cwd3,
|
|
|
28498
28820
|
});
|
|
28499
28821
|
sentPaths.add(displayPath);
|
|
28500
28822
|
} catch (err) {
|
|
28501
|
-
log2(`[Bridge service]
|
|
28823
|
+
log2(`[Bridge service] Session file change failed for ${displayPath}: ${errorMessage(err)}`);
|
|
28502
28824
|
}
|
|
28503
28825
|
}
|
|
28504
28826
|
}
|
|
@@ -28511,7 +28833,7 @@ function createBridgeOnSessionUpdate(opts) {
|
|
|
28511
28833
|
const runId = routing.runId;
|
|
28512
28834
|
const sessionId = routing.sessionId;
|
|
28513
28835
|
pathTracker.onRunIdChanged(runId);
|
|
28514
|
-
const
|
|
28836
|
+
const cwd = getBridgeWorkspaceDirectory();
|
|
28515
28837
|
const send = getSendSessionUpdate();
|
|
28516
28838
|
const sentFileChangePaths = /* @__PURE__ */ new Set();
|
|
28517
28839
|
const p = params;
|
|
@@ -28519,7 +28841,7 @@ function createBridgeOnSessionUpdate(opts) {
|
|
|
28519
28841
|
const isCompletedToolCallUpdate = updateKind === "tool_call_update" && isCompletedToolStatus(p.status);
|
|
28520
28842
|
const toolName = p.toolCall?.name ?? p.tool_call?.name ?? "";
|
|
28521
28843
|
const isToolUpdate = updateKind === "tool_call" || updateKind === "tool_call_update" || typeof toolName === "string" && toolName.length > 0;
|
|
28522
|
-
const toolPaths = isToolUpdate ? extractToolTargetDisplayPaths(params,
|
|
28844
|
+
const toolPaths = isToolUpdate ? extractToolTargetDisplayPaths(params, cwd) : [];
|
|
28523
28845
|
const toolKey = isToolUpdate ? pathTracker.resolveToolKey(params, updateKind) : "";
|
|
28524
28846
|
if (updateKind === "tool_call") {
|
|
28525
28847
|
pathTracker.resetToolSnapshots(toolKey);
|
|
@@ -28534,7 +28856,7 @@ function createBridgeOnSessionUpdate(opts) {
|
|
|
28534
28856
|
toolKey,
|
|
28535
28857
|
toolPaths,
|
|
28536
28858
|
p.status,
|
|
28537
|
-
|
|
28859
|
+
cwd,
|
|
28538
28860
|
sentFileChangePaths,
|
|
28539
28861
|
deliver,
|
|
28540
28862
|
runId ?? "",
|
|
@@ -28542,18 +28864,18 @@ function createBridgeOnSessionUpdate(opts) {
|
|
|
28542
28864
|
log2
|
|
28543
28865
|
);
|
|
28544
28866
|
}
|
|
28545
|
-
const diffs = extractAcpFileDiffsFromUpdate(params,
|
|
28867
|
+
const diffs = extractAcpFileDiffsFromUpdate(params, cwd);
|
|
28546
28868
|
if (diffs.length > 0 && send && runId && sessionId) {
|
|
28547
|
-
sendExtractedDiffsAsSessionFileChanges(diffs, send,
|
|
28869
|
+
sendExtractedDiffsAsSessionFileChanges(diffs, send, cwd, sessionId, runId, sentFileChangePaths, log2);
|
|
28548
28870
|
} else if (diffs.length > 0) {
|
|
28549
28871
|
log2(
|
|
28550
|
-
`[Bridge service]
|
|
28872
|
+
`[Bridge service] Agent file diff(s) not forwarded (${diffs.length}): session or run not wired to the bridge.`
|
|
28551
28873
|
);
|
|
28552
28874
|
}
|
|
28553
28875
|
if (isCompletedToolCallUpdate && send && runId && sessionId) {
|
|
28554
28876
|
const acc = pathTracker.accumulatedPathsByToolKey.get(toolKey);
|
|
28555
28877
|
const merged = [.../* @__PURE__ */ new Set([...acc ? [...acc] : [], ...toolPaths])];
|
|
28556
|
-
sendGitHeadVsWorkspaceForToolPaths(merged, sentFileChangePaths, send,
|
|
28878
|
+
sendGitHeadVsWorkspaceForToolPaths(merged, sentFileChangePaths, send, cwd, sessionId, runId, log2);
|
|
28557
28879
|
pathTracker.accumulatedPathsByToolKey.delete(toolKey);
|
|
28558
28880
|
}
|
|
28559
28881
|
if (runId && send) {
|
|
@@ -28566,7 +28888,9 @@ function createBridgeOnSessionUpdate(opts) {
|
|
|
28566
28888
|
payload: params
|
|
28567
28889
|
});
|
|
28568
28890
|
} catch (err) {
|
|
28569
|
-
log2(
|
|
28891
|
+
log2(
|
|
28892
|
+
`[Bridge service] Session update send failed (${formatSessionUpdateKindForLog(updateKind)}): ${errorMessage(err)}`
|
|
28893
|
+
);
|
|
28570
28894
|
}
|
|
28571
28895
|
}
|
|
28572
28896
|
};
|
|
@@ -28583,11 +28907,11 @@ function buildAcpSessionBridgeHooks(opts) {
|
|
|
28583
28907
|
|
|
28584
28908
|
// src/acp/ensure-acp-client.ts
|
|
28585
28909
|
async function ensureAcpClient(options) {
|
|
28586
|
-
const { state, preferredAgentType, mode, cwd
|
|
28587
|
-
const targetCwd =
|
|
28588
|
-
|
|
28910
|
+
const { state, preferredAgentType, mode, cwd, routing, sendSessionUpdate, sendRequest, log: log2 } = options;
|
|
28911
|
+
const targetCwd = path9.resolve(
|
|
28912
|
+
cwd != null && String(cwd).trim() !== "" ? String(cwd).trim() : getBridgeWorkspaceDirectory()
|
|
28589
28913
|
);
|
|
28590
|
-
if (state.acpHandle && state.lastAcpCwd != null &&
|
|
28914
|
+
if (state.acpHandle && state.lastAcpCwd != null && path9.resolve(state.lastAcpCwd) !== path9.resolve(targetCwd)) {
|
|
28591
28915
|
try {
|
|
28592
28916
|
state.acpHandle.disconnect();
|
|
28593
28917
|
} catch {
|
|
@@ -28599,7 +28923,7 @@ async function ensureAcpClient(options) {
|
|
|
28599
28923
|
const resolved = resolveAgentCommand(preferredAgentType);
|
|
28600
28924
|
if (!resolved) {
|
|
28601
28925
|
log2(
|
|
28602
|
-
`[
|
|
28926
|
+
`[Agent] No local agent type (${preferredAgentType === null ? "none" : `"${preferredAgentType}"`}). Send agent type on prompts or agent configuration after identify.`
|
|
28603
28927
|
);
|
|
28604
28928
|
state.lastAcpStartError = "No agent type: ensure the app sends agentType on prompts or agent_config for this bridge.";
|
|
28605
28929
|
return null;
|
|
@@ -28618,15 +28942,15 @@ async function ensureAcpClient(options) {
|
|
|
28618
28942
|
if (!state.acpStartPromise) {
|
|
28619
28943
|
let statOk = false;
|
|
28620
28944
|
try {
|
|
28621
|
-
const st =
|
|
28945
|
+
const st = fs5.statSync(targetCwd);
|
|
28622
28946
|
statOk = st.isDirectory();
|
|
28623
28947
|
if (!statOk) {
|
|
28624
28948
|
state.lastAcpStartError = `Agent cwd is not a directory: ${targetCwd}`;
|
|
28625
|
-
log2(`[
|
|
28949
|
+
log2(`[Agent] ${state.lastAcpStartError}`);
|
|
28626
28950
|
}
|
|
28627
28951
|
} catch {
|
|
28628
28952
|
state.lastAcpStartError = `Agent cwd missing or inaccessible: ${targetCwd}`;
|
|
28629
|
-
log2(`[
|
|
28953
|
+
log2(`[Agent] ${state.lastAcpStartError}`);
|
|
28630
28954
|
}
|
|
28631
28955
|
if (!statOk) {
|
|
28632
28956
|
return null;
|
|
@@ -28651,7 +28975,7 @@ async function ensureAcpClient(options) {
|
|
|
28651
28975
|
return h;
|
|
28652
28976
|
}).catch((err) => {
|
|
28653
28977
|
state.lastAcpStartError = errorMessage(err);
|
|
28654
|
-
log2(`[
|
|
28978
|
+
log2(`[Agent] Failed to start: ${state.lastAcpStartError}`);
|
|
28655
28979
|
state.acpStartPromise = null;
|
|
28656
28980
|
state.acpAgentKey = null;
|
|
28657
28981
|
return null;
|
|
@@ -28687,10 +29011,9 @@ async function createAcpManager(options) {
|
|
|
28687
29011
|
runId,
|
|
28688
29012
|
mode,
|
|
28689
29013
|
agentType,
|
|
28690
|
-
cwd
|
|
29014
|
+
cwd,
|
|
28691
29015
|
sendResult,
|
|
28692
|
-
sendSessionUpdate
|
|
28693
|
-
collectSessionDiffAfterTurn
|
|
29016
|
+
sendSessionUpdate
|
|
28694
29017
|
} = opts;
|
|
28695
29018
|
const preferredForPrompt = agentType ?? backendFallbackAgentType ?? null;
|
|
28696
29019
|
pendingCancelRunId = void 0;
|
|
@@ -28702,7 +29025,7 @@ async function createAcpManager(options) {
|
|
|
28702
29025
|
state,
|
|
28703
29026
|
preferredAgentType: preferredForPrompt,
|
|
28704
29027
|
mode,
|
|
28705
|
-
cwd
|
|
29028
|
+
cwd,
|
|
28706
29029
|
routing: promptRouting,
|
|
28707
29030
|
sendSessionUpdate,
|
|
28708
29031
|
sendRequest: sendSessionUpdate,
|
|
@@ -28743,9 +29066,9 @@ async function createAcpManager(options) {
|
|
|
28743
29066
|
promptId,
|
|
28744
29067
|
sessionId,
|
|
28745
29068
|
runId,
|
|
29069
|
+
agentCwd: cwd,
|
|
28746
29070
|
sendResult,
|
|
28747
29071
|
sendSessionUpdate,
|
|
28748
|
-
collectSessionDiffAfterTurn,
|
|
28749
29072
|
log: log2
|
|
28750
29073
|
});
|
|
28751
29074
|
}
|
|
@@ -28764,7 +29087,7 @@ async function createAcpManager(options) {
|
|
|
28764
29087
|
await handle.cancel();
|
|
28765
29088
|
return true;
|
|
28766
29089
|
} catch (err) {
|
|
28767
|
-
log2(`[
|
|
29090
|
+
log2(`[Agent] Cancel failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
28768
29091
|
return false;
|
|
28769
29092
|
}
|
|
28770
29093
|
}
|
|
@@ -28801,7 +29124,9 @@ var handleBridgeIdentified = (msg, deps) => {
|
|
|
28801
29124
|
try {
|
|
28802
29125
|
await deps.reportAutoDetectedAgents?.();
|
|
28803
29126
|
} catch (e) {
|
|
28804
|
-
deps.log(
|
|
29127
|
+
deps.log(
|
|
29128
|
+
`[Bridge service] Auto-detect agents failed: ${e instanceof Error ? e.message : String(e)}`
|
|
29129
|
+
);
|
|
28805
29130
|
}
|
|
28806
29131
|
})();
|
|
28807
29132
|
});
|
|
@@ -28809,7 +29134,9 @@ var handleBridgeIdentified = (msg, deps) => {
|
|
|
28809
29134
|
try {
|
|
28810
29135
|
deps.sendLocalSkillsReport?.();
|
|
28811
29136
|
} catch (e) {
|
|
28812
|
-
deps.log(
|
|
29137
|
+
deps.log(
|
|
29138
|
+
`[Bridge service] Local skills report failed: ${e instanceof Error ? e.message : String(e)}`
|
|
29139
|
+
);
|
|
28813
29140
|
}
|
|
28814
29141
|
});
|
|
28815
29142
|
};
|
|
@@ -28826,12 +29153,12 @@ var handleAgentConfigMessage = (msg, deps) => {
|
|
|
28826
29153
|
};
|
|
28827
29154
|
|
|
28828
29155
|
// src/acp/from-bridge/handle-bridge-prompt.ts
|
|
28829
|
-
import * as
|
|
29156
|
+
import * as path11 from "node:path";
|
|
28830
29157
|
import { execFile as execFile3 } from "node:child_process";
|
|
28831
29158
|
import { promisify as promisify3 } from "node:util";
|
|
28832
29159
|
|
|
28833
29160
|
// src/git/bridge-queue-key.ts
|
|
28834
|
-
import * as
|
|
29161
|
+
import * as path10 from "node:path";
|
|
28835
29162
|
import { createHash } from "node:crypto";
|
|
28836
29163
|
function normalizeCanonicalGitUrl(url2) {
|
|
28837
29164
|
let s = url2.trim();
|
|
@@ -28859,13 +29186,13 @@ function canonicalUrlToRepoIdSync(url2) {
|
|
|
28859
29186
|
return createHash("sha256").update(normalized).digest("hex").slice(0, 32);
|
|
28860
29187
|
}
|
|
28861
29188
|
function fallbackRepoIdFromPath(absPath) {
|
|
28862
|
-
return createHash("sha256").update(
|
|
29189
|
+
return createHash("sha256").update(path10.resolve(absPath)).digest("hex").slice(0, 32);
|
|
28863
29190
|
}
|
|
28864
29191
|
async function resolveBridgeQueueBindFields(options) {
|
|
28865
29192
|
const { effectiveCwd, worktreePaths, primaryRepoRoots, log: log2 } = options;
|
|
28866
|
-
const cwdAbs = worktreePaths.length > 0 ?
|
|
29193
|
+
const cwdAbs = worktreePaths.length > 0 ? path10.resolve(worktreePaths[0]) : path10.resolve(effectiveCwd);
|
|
28867
29194
|
if (!primaryRepoRoots.length) {
|
|
28868
|
-
log2("[Bridge service]
|
|
29195
|
+
log2("[Bridge service] Prompt queue bind skipped: no Git repository roots under the working directory.");
|
|
28869
29196
|
return null;
|
|
28870
29197
|
}
|
|
28871
29198
|
let primaryRoot = primaryRepoRoots[0];
|
|
@@ -28885,115 +29212,11 @@ async function resolveBridgeQueueBindFields(options) {
|
|
|
28885
29212
|
return { canonicalQueueKey, repoId, cwdAbs };
|
|
28886
29213
|
}
|
|
28887
29214
|
|
|
28888
|
-
// src/git/pre-turn-snapshot.ts
|
|
28889
|
-
import * as fs4 from "node:fs";
|
|
28890
|
-
import * as path9 from "node:path";
|
|
28891
|
-
import { execFile as execFile2 } from "node:child_process";
|
|
28892
|
-
import { promisify as promisify2 } from "node:util";
|
|
28893
|
-
var execFileAsync2 = promisify2(execFile2);
|
|
28894
|
-
function snapshotsDirForCwd(agentCwd) {
|
|
28895
|
-
return path9.join(agentCwd, ".buildautomaton", "snapshots");
|
|
28896
|
-
}
|
|
28897
|
-
async function gitStashCreate2(repoRoot, log2) {
|
|
28898
|
-
try {
|
|
28899
|
-
const { stdout } = await execFileAsync2("git", ["stash", "create"], {
|
|
28900
|
-
cwd: repoRoot,
|
|
28901
|
-
maxBuffer: 10 * 1024 * 1024
|
|
28902
|
-
});
|
|
28903
|
-
return stdout.trim();
|
|
28904
|
-
} catch (e) {
|
|
28905
|
-
log2(`[snapshot] git stash create failed in ${repoRoot}: ${e instanceof Error ? e.message : String(e)}`);
|
|
28906
|
-
return "";
|
|
28907
|
-
}
|
|
28908
|
-
}
|
|
28909
|
-
async function gitRun(repoRoot, args, log2, label) {
|
|
28910
|
-
try {
|
|
28911
|
-
await execFileAsync2("git", args, { cwd: repoRoot, maxBuffer: 10 * 1024 * 1024 });
|
|
28912
|
-
return { ok: true };
|
|
28913
|
-
} catch (e) {
|
|
28914
|
-
const msg = e instanceof Error ? e.message : String(e);
|
|
28915
|
-
log2(`[snapshot] git ${label} failed in ${repoRoot}: ${msg}`);
|
|
28916
|
-
return { ok: false, error: msg };
|
|
28917
|
-
}
|
|
28918
|
-
}
|
|
28919
|
-
async function resolveSnapshotRepoRoots(options) {
|
|
28920
|
-
const { worktreePaths, fallbackCwd, log: log2 } = options;
|
|
28921
|
-
if (worktreePaths?.length) {
|
|
28922
|
-
const uniq = [...new Set(worktreePaths.map((p) => path9.resolve(p)))];
|
|
28923
|
-
return uniq;
|
|
28924
|
-
}
|
|
28925
|
-
try {
|
|
28926
|
-
const repos = await discoverGitReposUnderRoot(fallbackCwd);
|
|
28927
|
-
return repos.map((r) => r.absolutePath);
|
|
28928
|
-
} catch (e) {
|
|
28929
|
-
log2(`[snapshot] discover repos failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
28930
|
-
return [];
|
|
28931
|
-
}
|
|
28932
|
-
}
|
|
28933
|
-
async function capturePreTurnSnapshot(options) {
|
|
28934
|
-
const { runId, repoRoots, agentCwd, log: log2 } = options;
|
|
28935
|
-
if (!runId || !repoRoots.length) {
|
|
28936
|
-
return { ok: false, error: "No git repos to snapshot" };
|
|
28937
|
-
}
|
|
28938
|
-
const repos = [];
|
|
28939
|
-
for (const root of repoRoots) {
|
|
28940
|
-
const stashSha = await gitStashCreate2(root, log2);
|
|
28941
|
-
repos.push({ path: root, stashSha });
|
|
28942
|
-
}
|
|
28943
|
-
const dir = snapshotsDirForCwd(agentCwd);
|
|
28944
|
-
try {
|
|
28945
|
-
fs4.mkdirSync(dir, { recursive: true });
|
|
28946
|
-
} catch (e) {
|
|
28947
|
-
return { ok: false, error: e instanceof Error ? e.message : String(e) };
|
|
28948
|
-
}
|
|
28949
|
-
const payload = {
|
|
28950
|
-
runId,
|
|
28951
|
-
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
28952
|
-
repos
|
|
28953
|
-
};
|
|
28954
|
-
const filePath = path9.join(dir, `${runId}.json`);
|
|
28955
|
-
try {
|
|
28956
|
-
fs4.writeFileSync(filePath, JSON.stringify(payload, null, 2), "utf8");
|
|
28957
|
-
} catch (e) {
|
|
28958
|
-
return { ok: false, error: e instanceof Error ? e.message : String(e) };
|
|
28959
|
-
}
|
|
28960
|
-
log2(`[snapshot] Saved pre-turn snapshot ${runId.slice(0, 8)}\u2026 (${repos.length} repo(s)) \u2192 ${filePath}`);
|
|
28961
|
-
return { ok: true, filePath, repos };
|
|
28962
|
-
}
|
|
28963
|
-
async function applyPreTurnSnapshot(filePath, log2) {
|
|
28964
|
-
let data;
|
|
28965
|
-
try {
|
|
28966
|
-
const raw = fs4.readFileSync(filePath, "utf8");
|
|
28967
|
-
data = JSON.parse(raw);
|
|
28968
|
-
} catch (e) {
|
|
28969
|
-
return { ok: false, error: e instanceof Error ? e.message : String(e) };
|
|
28970
|
-
}
|
|
28971
|
-
if (!Array.isArray(data.repos)) {
|
|
28972
|
-
return { ok: false, error: "Invalid snapshot file" };
|
|
28973
|
-
}
|
|
28974
|
-
for (const r of data.repos) {
|
|
28975
|
-
if (!r.path) continue;
|
|
28976
|
-
const reset = await gitRun(r.path, ["reset", "--hard", "HEAD"], log2, "reset --hard");
|
|
28977
|
-
if (!reset.ok) return reset;
|
|
28978
|
-
const clean = await gitRun(r.path, ["clean", "-fd"], log2, "clean -fd");
|
|
28979
|
-
if (!clean.ok) return clean;
|
|
28980
|
-
if (r.stashSha) {
|
|
28981
|
-
const ap = await gitRun(r.path, ["stash", "apply", r.stashSha], log2, "stash apply");
|
|
28982
|
-
if (!ap.ok) return ap;
|
|
28983
|
-
}
|
|
28984
|
-
}
|
|
28985
|
-
log2(`[snapshot] Restored pre-turn state for ${data.runId.slice(0, 8)}\u2026`);
|
|
28986
|
-
return { ok: true };
|
|
28987
|
-
}
|
|
28988
|
-
function snapshotFilePath(agentCwd, runId) {
|
|
28989
|
-
return path9.join(snapshotsDirForCwd(agentCwd), `${runId}.json`);
|
|
28990
|
-
}
|
|
28991
|
-
|
|
28992
29215
|
// src/acp/from-bridge/handle-bridge-prompt.ts
|
|
28993
29216
|
var execFileAsync3 = promisify3(execFile3);
|
|
28994
|
-
async function readGitBranch(
|
|
29217
|
+
async function readGitBranch(cwd) {
|
|
28995
29218
|
try {
|
|
28996
|
-
const { stdout } = await execFileAsync3("git", ["branch", "--show-current"], { cwd
|
|
29219
|
+
const { stdout } = await execFileAsync3("git", ["branch", "--show-current"], { cwd, maxBuffer: 64 * 1024 });
|
|
28997
29220
|
const b = stdout.trim();
|
|
28998
29221
|
return b || null;
|
|
28999
29222
|
} catch {
|
|
@@ -29006,7 +29229,7 @@ function handleBridgePrompt(msg, deps) {
|
|
|
29006
29229
|
const promptText = typeof rawPrompt === "string" ? rawPrompt : rawPrompt != null ? String(rawPrompt) : "";
|
|
29007
29230
|
if (!promptText.trim()) {
|
|
29008
29231
|
log2(
|
|
29009
|
-
`[Bridge service]
|
|
29232
|
+
`[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).`
|
|
29010
29233
|
);
|
|
29011
29234
|
return;
|
|
29012
29235
|
}
|
|
@@ -29015,8 +29238,6 @@ function handleBridgePrompt(msg, deps) {
|
|
|
29015
29238
|
const sessionWorktreesEnabled = msg.sessionWorktreesEnabled === true;
|
|
29016
29239
|
const agentType = typeof msg.agentType === "string" && msg.agentType.trim() ? msg.agentType.trim() : void 0;
|
|
29017
29240
|
const runId = typeof msg.runId === "string" ? msg.runId : void 0;
|
|
29018
|
-
const sessionGitQueueStart = msg.sessionGitQueueStart === true;
|
|
29019
|
-
const collectSessionDiffAfterTurn = msg.collectSessionDiffAfterTurn === true;
|
|
29020
29241
|
const sendResult = (result) => {
|
|
29021
29242
|
const s = getWs();
|
|
29022
29243
|
if (s) sendWsMessage(s, result);
|
|
@@ -29024,7 +29245,7 @@ function handleBridgePrompt(msg, deps) {
|
|
|
29024
29245
|
const sendSessionUpdate = (payload) => {
|
|
29025
29246
|
const s = getWs();
|
|
29026
29247
|
if (!s) {
|
|
29027
|
-
log2("[Bridge service]
|
|
29248
|
+
log2("[Bridge service] Session update not sent: not connected to the bridge.");
|
|
29028
29249
|
return;
|
|
29029
29250
|
}
|
|
29030
29251
|
const p = payload;
|
|
@@ -29032,7 +29253,7 @@ function handleBridgePrompt(msg, deps) {
|
|
|
29032
29253
|
};
|
|
29033
29254
|
async function preambleAndPrompt(resolvedCwd) {
|
|
29034
29255
|
const s = getWs();
|
|
29035
|
-
const effectiveCwd =
|
|
29256
|
+
const effectiveCwd = path11.resolve(resolvedCwd ?? getBridgeWorkspaceDirectory());
|
|
29036
29257
|
const worktreePaths = sessionWorktreeManager.getWorktreePathsForSession(sessionId) ?? [];
|
|
29037
29258
|
const repoRoots = await resolveSnapshotRepoRoots({
|
|
29038
29259
|
worktreePaths,
|
|
@@ -29065,9 +29286,6 @@ function handleBridgePrompt(msg, deps) {
|
|
|
29065
29286
|
agentUsesWorktree: sessionWorktreeManager.usesWorktreeSession(sessionId)
|
|
29066
29287
|
});
|
|
29067
29288
|
}
|
|
29068
|
-
if (sessionGitQueueStart && sessionId && repoRoots.length > 0) {
|
|
29069
|
-
await captureSessionGitBoundary({ sessionId, repoRoots, log: log2 });
|
|
29070
|
-
}
|
|
29071
29289
|
if (s && sessionId && runId) {
|
|
29072
29290
|
const cap = repoRoots.length > 0 ? await capturePreTurnSnapshot({ runId, repoRoots, agentCwd: effectiveCwd, log: log2 }) : { ok: false, error: "No git repos" };
|
|
29073
29291
|
sendWsMessage(s, {
|
|
@@ -29086,12 +29304,11 @@ function handleBridgePrompt(msg, deps) {
|
|
|
29086
29304
|
agentType,
|
|
29087
29305
|
cwd: effectiveCwd,
|
|
29088
29306
|
sendResult,
|
|
29089
|
-
sendSessionUpdate
|
|
29090
|
-
collectSessionDiffAfterTurn
|
|
29307
|
+
sendSessionUpdate
|
|
29091
29308
|
});
|
|
29092
29309
|
}
|
|
29093
|
-
void sessionWorktreeManager.resolveCwdForPrompt(sessionId, { isNewSession, sessionWorktreesEnabled }).then((
|
|
29094
|
-
log2(`[
|
|
29310
|
+
void sessionWorktreeManager.resolveCwdForPrompt(sessionId, { isNewSession, sessionWorktreesEnabled }).then((cwd) => preambleAndPrompt(cwd)).catch((err) => {
|
|
29311
|
+
log2(`[Agent] Worktree resolve failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
29095
29312
|
void preambleAndPrompt(void 0);
|
|
29096
29313
|
});
|
|
29097
29314
|
}
|
|
@@ -29108,7 +29325,7 @@ function handleBridgeCancelRun(msg, { log: log2, acpManager }) {
|
|
|
29108
29325
|
void acpManager.cancelRun(runId).then((sent) => {
|
|
29109
29326
|
if (!sent) {
|
|
29110
29327
|
log2(
|
|
29111
|
-
`[
|
|
29328
|
+
`[Agent] Cancel ignored for run ${runId.slice(0, 8)}\u2026 (no active run or cancel not available).`
|
|
29112
29329
|
);
|
|
29113
29330
|
}
|
|
29114
29331
|
});
|
|
@@ -29136,7 +29353,7 @@ function handleSkillCall(msg, socket, log2) {
|
|
|
29136
29353
|
sendWsMessage(socket, { type: "skill_result", id: msg.id, result });
|
|
29137
29354
|
}).catch((err) => {
|
|
29138
29355
|
sendWsMessage(socket, { type: "skill_result", id: msg.id, error: String(err) });
|
|
29139
|
-
log2(`[Bridge service]
|
|
29356
|
+
log2(`[Bridge service] Skill invocation failed (${msg.skillId}/${msg.operationId}): ${err}`);
|
|
29140
29357
|
});
|
|
29141
29358
|
}
|
|
29142
29359
|
|
|
@@ -29153,36 +29370,35 @@ var handleSkillCallMessage = (msg, { getWs, log: log2 }) => {
|
|
|
29153
29370
|
};
|
|
29154
29371
|
|
|
29155
29372
|
// src/files/list-dir.ts
|
|
29156
|
-
import
|
|
29157
|
-
import
|
|
29373
|
+
import fs6 from "node:fs";
|
|
29374
|
+
import path13 from "node:path";
|
|
29158
29375
|
|
|
29159
29376
|
// src/files/ensure-under-cwd.ts
|
|
29160
|
-
import
|
|
29161
|
-
function ensureUnderCwd(relativePath,
|
|
29162
|
-
const normalized =
|
|
29163
|
-
const resolved =
|
|
29164
|
-
if (!resolved.startsWith(
|
|
29377
|
+
import path12 from "node:path";
|
|
29378
|
+
function ensureUnderCwd(relativePath, cwd = getBridgeWorkspaceDirectory()) {
|
|
29379
|
+
const normalized = path12.normalize(relativePath).replace(/^(\.\/)+/, "");
|
|
29380
|
+
const resolved = path12.resolve(cwd, normalized);
|
|
29381
|
+
if (!resolved.startsWith(cwd + path12.sep) && resolved !== cwd) {
|
|
29165
29382
|
return null;
|
|
29166
29383
|
}
|
|
29167
29384
|
return resolved;
|
|
29168
29385
|
}
|
|
29169
29386
|
|
|
29170
29387
|
// src/files/list-dir.ts
|
|
29171
|
-
var cwd = process.cwd();
|
|
29172
29388
|
function listDir(relativePath) {
|
|
29173
|
-
const resolved = ensureUnderCwd(relativePath || ".",
|
|
29389
|
+
const resolved = ensureUnderCwd(relativePath || ".", getBridgeWorkspaceDirectory());
|
|
29174
29390
|
if (!resolved) {
|
|
29175
29391
|
return { error: "Path is outside working directory" };
|
|
29176
29392
|
}
|
|
29177
29393
|
try {
|
|
29178
|
-
const names =
|
|
29394
|
+
const names = fs6.readdirSync(resolved, { withFileTypes: true });
|
|
29179
29395
|
const entries = names.filter((d) => !d.name.startsWith(".")).map((d) => {
|
|
29180
|
-
const entryPath =
|
|
29181
|
-
const fullPath =
|
|
29396
|
+
const entryPath = path13.join(relativePath || ".", d.name).replace(/\\/g, "/");
|
|
29397
|
+
const fullPath = path13.join(resolved, d.name);
|
|
29182
29398
|
let isDir = d.isDirectory();
|
|
29183
29399
|
if (d.isSymbolicLink()) {
|
|
29184
29400
|
try {
|
|
29185
|
-
const targetStat =
|
|
29401
|
+
const targetStat = fs6.statSync(fullPath);
|
|
29186
29402
|
isDir = targetStat.isDirectory();
|
|
29187
29403
|
} catch {
|
|
29188
29404
|
isDir = false;
|
|
@@ -29206,26 +29422,25 @@ function listDir(relativePath) {
|
|
|
29206
29422
|
}
|
|
29207
29423
|
|
|
29208
29424
|
// src/files/read-file.ts
|
|
29209
|
-
import
|
|
29425
|
+
import fs7 from "node:fs";
|
|
29210
29426
|
import { StringDecoder } from "node:string_decoder";
|
|
29211
|
-
var cwd2 = process.cwd();
|
|
29212
29427
|
function resolveFilePath(relativePath) {
|
|
29213
|
-
const resolved = ensureUnderCwd(relativePath,
|
|
29428
|
+
const resolved = ensureUnderCwd(relativePath, getBridgeWorkspaceDirectory());
|
|
29214
29429
|
if (!resolved) return { error: "Path is outside working directory" };
|
|
29215
29430
|
let real;
|
|
29216
29431
|
try {
|
|
29217
|
-
real =
|
|
29432
|
+
real = fs7.realpathSync(resolved);
|
|
29218
29433
|
} catch {
|
|
29219
29434
|
real = resolved;
|
|
29220
29435
|
}
|
|
29221
|
-
const stat =
|
|
29436
|
+
const stat = fs7.statSync(real);
|
|
29222
29437
|
if (!stat.isFile()) return { error: "Not a file" };
|
|
29223
29438
|
return real;
|
|
29224
29439
|
}
|
|
29225
29440
|
var LINE_CHUNK_SIZE = 64 * 1024;
|
|
29226
29441
|
function readFileRange(filePath, startLine, endLine, lineOffsetIn, lineChunkSize = LINE_CHUNK_SIZE) {
|
|
29227
|
-
const fileSize =
|
|
29228
|
-
const fd =
|
|
29442
|
+
const fileSize = fs7.statSync(filePath).size;
|
|
29443
|
+
const fd = fs7.openSync(filePath, "r");
|
|
29229
29444
|
const bufSize = 64 * 1024;
|
|
29230
29445
|
const buf = Buffer.alloc(bufSize);
|
|
29231
29446
|
const decoder = new StringDecoder("utf8");
|
|
@@ -29238,7 +29453,7 @@ function readFileRange(filePath, startLine, endLine, lineOffsetIn, lineChunkSize
|
|
|
29238
29453
|
let line0Accum = "";
|
|
29239
29454
|
try {
|
|
29240
29455
|
let bytesRead;
|
|
29241
|
-
while (!done && (bytesRead =
|
|
29456
|
+
while (!done && (bytesRead = fs7.readSync(fd, buf, 0, bufSize, null)) > 0) {
|
|
29242
29457
|
const text = partial2 + decoder.write(buf.subarray(0, bytesRead));
|
|
29243
29458
|
partial2 = "";
|
|
29244
29459
|
let lineStart = 0;
|
|
@@ -29373,7 +29588,7 @@ function readFileRange(filePath, startLine, endLine, lineOffsetIn, lineChunkSize
|
|
|
29373
29588
|
}
|
|
29374
29589
|
return { content: resultLines.join("\n"), size: fileSize };
|
|
29375
29590
|
} finally {
|
|
29376
|
-
|
|
29591
|
+
fs7.closeSync(fd);
|
|
29377
29592
|
}
|
|
29378
29593
|
}
|
|
29379
29594
|
function readFile(relativePath, startLine, endLine, lineOffset, lineChunkSize = LINE_CHUNK_SIZE) {
|
|
@@ -29384,8 +29599,8 @@ function readFile(relativePath, startLine, endLine, lineOffset, lineChunkSize =
|
|
|
29384
29599
|
if (hasRange) {
|
|
29385
29600
|
return readFileRange(result, startLine, endLine, lineOffset, lineChunkSize);
|
|
29386
29601
|
}
|
|
29387
|
-
const stat =
|
|
29388
|
-
const raw =
|
|
29602
|
+
const stat = fs7.statSync(result);
|
|
29603
|
+
const raw = fs7.readFileSync(result, "utf8");
|
|
29389
29604
|
const lines = raw.split(/\r?\n/);
|
|
29390
29605
|
return { content: raw, totalLines: lines.length, size: stat.size };
|
|
29391
29606
|
} catch (err) {
|
|
@@ -29394,16 +29609,16 @@ function readFile(relativePath, startLine, endLine, lineOffset, lineChunkSize =
|
|
|
29394
29609
|
}
|
|
29395
29610
|
|
|
29396
29611
|
// src/files/file-index.ts
|
|
29397
|
-
import
|
|
29398
|
-
import
|
|
29612
|
+
import fs8 from "node:fs";
|
|
29613
|
+
import path14 from "node:path";
|
|
29399
29614
|
import os2 from "node:os";
|
|
29400
29615
|
import crypto2 from "node:crypto";
|
|
29401
|
-
var INDEX_DIR =
|
|
29616
|
+
var INDEX_DIR = path14.join(os2.homedir(), ".buildautomaton");
|
|
29402
29617
|
var HASH_LEN = 16;
|
|
29403
29618
|
var INDEX_VERSION = 2;
|
|
29404
|
-
function getIndexPath(
|
|
29405
|
-
const hash = crypto2.createHash("sha256").update(
|
|
29406
|
-
return
|
|
29619
|
+
function getIndexPath(cwd) {
|
|
29620
|
+
const hash = crypto2.createHash("sha256").update(cwd).digest("hex").slice(0, HASH_LEN);
|
|
29621
|
+
return path14.join(INDEX_DIR, `.file-index-${hash}.json`);
|
|
29407
29622
|
}
|
|
29408
29623
|
function getTrigrams(s) {
|
|
29409
29624
|
const lower = s.toLowerCase();
|
|
@@ -29441,20 +29656,20 @@ function binarySearch(arr, x) {
|
|
|
29441
29656
|
function walkDir(dir, baseDir, out) {
|
|
29442
29657
|
let names;
|
|
29443
29658
|
try {
|
|
29444
|
-
names =
|
|
29659
|
+
names = fs8.readdirSync(dir);
|
|
29445
29660
|
} catch {
|
|
29446
29661
|
return;
|
|
29447
29662
|
}
|
|
29448
29663
|
for (const name of names) {
|
|
29449
29664
|
if (name.startsWith(".")) continue;
|
|
29450
|
-
const full =
|
|
29665
|
+
const full = path14.join(dir, name);
|
|
29451
29666
|
let stat;
|
|
29452
29667
|
try {
|
|
29453
|
-
stat =
|
|
29668
|
+
stat = fs8.statSync(full);
|
|
29454
29669
|
} catch {
|
|
29455
29670
|
continue;
|
|
29456
29671
|
}
|
|
29457
|
-
const relative4 =
|
|
29672
|
+
const relative4 = path14.relative(baseDir, full).replace(/\\/g, "/");
|
|
29458
29673
|
if (stat.isDirectory()) {
|
|
29459
29674
|
walkDir(full, baseDir, out);
|
|
29460
29675
|
} else if (stat.isFile()) {
|
|
@@ -29462,8 +29677,8 @@ function walkDir(dir, baseDir, out) {
|
|
|
29462
29677
|
}
|
|
29463
29678
|
}
|
|
29464
29679
|
}
|
|
29465
|
-
function buildFileIndex(
|
|
29466
|
-
const resolved =
|
|
29680
|
+
function buildFileIndex(cwd) {
|
|
29681
|
+
const resolved = path14.resolve(cwd);
|
|
29467
29682
|
const paths = [];
|
|
29468
29683
|
walkDir(resolved, resolved, paths);
|
|
29469
29684
|
paths.sort((a, b) => a.localeCompare(b, void 0, { sensitivity: "base" }));
|
|
@@ -29481,18 +29696,18 @@ function buildFileIndex(cwd3) {
|
|
|
29481
29696
|
const data = { version: INDEX_VERSION, paths, trigramIndex };
|
|
29482
29697
|
const indexPath = getIndexPath(resolved);
|
|
29483
29698
|
try {
|
|
29484
|
-
if (!
|
|
29485
|
-
|
|
29699
|
+
if (!fs8.existsSync(INDEX_DIR)) fs8.mkdirSync(INDEX_DIR, { recursive: true });
|
|
29700
|
+
fs8.writeFileSync(indexPath, JSON.stringify(data), "utf8");
|
|
29486
29701
|
} catch (e) {
|
|
29487
29702
|
console.error("[file-index] Failed to write index:", e);
|
|
29488
29703
|
}
|
|
29489
29704
|
return data;
|
|
29490
29705
|
}
|
|
29491
|
-
function loadFileIndex(
|
|
29492
|
-
const resolved =
|
|
29706
|
+
function loadFileIndex(cwd) {
|
|
29707
|
+
const resolved = path14.resolve(cwd);
|
|
29493
29708
|
const indexPath = getIndexPath(resolved);
|
|
29494
29709
|
try {
|
|
29495
|
-
const raw =
|
|
29710
|
+
const raw = fs8.readFileSync(indexPath, "utf8");
|
|
29496
29711
|
const parsed = JSON.parse(raw);
|
|
29497
29712
|
if (parsed !== null && typeof parsed === "object" && Array.isArray(parsed.paths)) {
|
|
29498
29713
|
const obj = parsed;
|
|
@@ -29509,8 +29724,8 @@ function loadFileIndex(cwd3) {
|
|
|
29509
29724
|
return null;
|
|
29510
29725
|
}
|
|
29511
29726
|
}
|
|
29512
|
-
function ensureFileIndex(
|
|
29513
|
-
const resolved =
|
|
29727
|
+
function ensureFileIndex(cwd) {
|
|
29728
|
+
const resolved = path14.resolve(cwd);
|
|
29514
29729
|
const cached2 = loadFileIndex(resolved);
|
|
29515
29730
|
if (cached2 !== null) return { data: cached2, fromCache: true };
|
|
29516
29731
|
const data = buildFileIndex(resolved);
|
|
@@ -29548,8 +29763,8 @@ function searchFileIndex(index, query, limit = 100) {
|
|
|
29548
29763
|
var SEARCH_LIMIT = 100;
|
|
29549
29764
|
function handleFileBrowserSearch(msg, socket) {
|
|
29550
29765
|
const q = typeof msg.q === "string" ? msg.q : "";
|
|
29551
|
-
const
|
|
29552
|
-
const index = loadFileIndex(
|
|
29766
|
+
const cwd = getBridgeWorkspaceDirectory();
|
|
29767
|
+
const index = loadFileIndex(cwd);
|
|
29553
29768
|
if (index === null) {
|
|
29554
29769
|
sendWsMessage(socket, {
|
|
29555
29770
|
type: "file_browser_search_response",
|
|
@@ -29570,7 +29785,7 @@ function handleFileBrowserSearch(msg, socket) {
|
|
|
29570
29785
|
function triggerFileIndexBuild() {
|
|
29571
29786
|
setImmediate(() => {
|
|
29572
29787
|
try {
|
|
29573
|
-
ensureFileIndex(
|
|
29788
|
+
ensureFileIndex(getBridgeWorkspaceDirectory());
|
|
29574
29789
|
} catch (e) {
|
|
29575
29790
|
console.error("[file-index] Background build failed:", e);
|
|
29576
29791
|
}
|
|
@@ -29631,30 +29846,30 @@ function handleFileBrowserSearchMessage(msg, { getWs }) {
|
|
|
29631
29846
|
}
|
|
29632
29847
|
|
|
29633
29848
|
// src/skills/discover-local-agent-skills.ts
|
|
29634
|
-
import
|
|
29635
|
-
import
|
|
29849
|
+
import fs9 from "node:fs";
|
|
29850
|
+
import path15 from "node:path";
|
|
29636
29851
|
var SKILL_DISCOVERY_ROOTS = [".agents/skills", ".claude/skills", ".cursor/skills", "skills"];
|
|
29637
|
-
function discoverLocalSkills(
|
|
29852
|
+
function discoverLocalSkills(cwd) {
|
|
29638
29853
|
const out = [];
|
|
29639
29854
|
const seenKeys = /* @__PURE__ */ new Set();
|
|
29640
29855
|
for (const rel of SKILL_DISCOVERY_ROOTS) {
|
|
29641
|
-
const base =
|
|
29642
|
-
if (!
|
|
29856
|
+
const base = path15.join(cwd, rel);
|
|
29857
|
+
if (!fs9.existsSync(base) || !fs9.statSync(base).isDirectory()) continue;
|
|
29643
29858
|
let entries = [];
|
|
29644
29859
|
try {
|
|
29645
|
-
entries =
|
|
29860
|
+
entries = fs9.readdirSync(base);
|
|
29646
29861
|
} catch {
|
|
29647
29862
|
continue;
|
|
29648
29863
|
}
|
|
29649
29864
|
for (const name of entries) {
|
|
29650
|
-
const dir =
|
|
29865
|
+
const dir = path15.join(base, name);
|
|
29651
29866
|
try {
|
|
29652
|
-
if (!
|
|
29867
|
+
if (!fs9.statSync(dir).isDirectory()) continue;
|
|
29653
29868
|
} catch {
|
|
29654
29869
|
continue;
|
|
29655
29870
|
}
|
|
29656
|
-
const skillMd =
|
|
29657
|
-
if (!
|
|
29871
|
+
const skillMd = path15.join(dir, "SKILL.md");
|
|
29872
|
+
if (!fs9.existsSync(skillMd)) continue;
|
|
29658
29873
|
const key = `${rel}/${name}`;
|
|
29659
29874
|
if (seenKeys.has(key)) continue;
|
|
29660
29875
|
seenKeys.add(key);
|
|
@@ -29663,26 +29878,26 @@ function discoverLocalSkills(cwd3) {
|
|
|
29663
29878
|
}
|
|
29664
29879
|
return out;
|
|
29665
29880
|
}
|
|
29666
|
-
function discoverSkillLayoutRoots(
|
|
29881
|
+
function discoverSkillLayoutRoots(cwd) {
|
|
29667
29882
|
const roots = [];
|
|
29668
29883
|
for (const rel of SKILL_DISCOVERY_ROOTS) {
|
|
29669
|
-
const base =
|
|
29670
|
-
if (!
|
|
29884
|
+
const base = path15.join(cwd, rel);
|
|
29885
|
+
if (!fs9.existsSync(base) || !fs9.statSync(base).isDirectory()) continue;
|
|
29671
29886
|
let entries = [];
|
|
29672
29887
|
try {
|
|
29673
|
-
entries =
|
|
29888
|
+
entries = fs9.readdirSync(base);
|
|
29674
29889
|
} catch {
|
|
29675
29890
|
continue;
|
|
29676
29891
|
}
|
|
29677
29892
|
const skills2 = [];
|
|
29678
29893
|
for (const name of entries) {
|
|
29679
|
-
const dir =
|
|
29894
|
+
const dir = path15.join(base, name);
|
|
29680
29895
|
try {
|
|
29681
|
-
if (!
|
|
29896
|
+
if (!fs9.statSync(dir).isDirectory()) continue;
|
|
29682
29897
|
} catch {
|
|
29683
29898
|
continue;
|
|
29684
29899
|
}
|
|
29685
|
-
if (!
|
|
29900
|
+
if (!fs9.existsSync(path15.join(dir, "SKILL.md"))) continue;
|
|
29686
29901
|
const relPath = `${rel}/${name}`.replace(/\\/g, "/");
|
|
29687
29902
|
skills2.push({ name, relPath });
|
|
29688
29903
|
}
|
|
@@ -29697,14 +29912,14 @@ function discoverSkillLayoutRoots(cwd3) {
|
|
|
29697
29912
|
function handleSkillLayoutRequest(msg, deps) {
|
|
29698
29913
|
const socket = deps.getWs();
|
|
29699
29914
|
const id = typeof msg.id === "string" ? msg.id : "";
|
|
29700
|
-
const roots = discoverSkillLayoutRoots(
|
|
29915
|
+
const roots = discoverSkillLayoutRoots(getBridgeWorkspaceDirectory());
|
|
29701
29916
|
socket?.send(JSON.stringify({ type: "skill_layout_response", id, roots }));
|
|
29702
29917
|
}
|
|
29703
29918
|
|
|
29704
29919
|
// src/skills/install-remote-skills.ts
|
|
29705
|
-
import
|
|
29706
|
-
import
|
|
29707
|
-
function installRemoteSkills(
|
|
29920
|
+
import fs10 from "node:fs";
|
|
29921
|
+
import path16 from "node:path";
|
|
29922
|
+
function installRemoteSkills(cwd, targetDir, items) {
|
|
29708
29923
|
const installed = [];
|
|
29709
29924
|
if (!Array.isArray(items)) {
|
|
29710
29925
|
return { success: false, error: "Invalid items" };
|
|
@@ -29714,15 +29929,15 @@ function installRemoteSkills(cwd3, targetDir, items) {
|
|
|
29714
29929
|
if (typeof item.sourceId !== "string" || typeof item.skillName !== "string" || typeof item.versionHash !== "string" || !Array.isArray(item.files)) {
|
|
29715
29930
|
continue;
|
|
29716
29931
|
}
|
|
29717
|
-
const skillDir =
|
|
29932
|
+
const skillDir = path16.join(cwd, targetDir, item.skillName);
|
|
29718
29933
|
for (const f of item.files) {
|
|
29719
29934
|
if (typeof f.path !== "string" || !f.text && !f.base64) continue;
|
|
29720
|
-
const dest =
|
|
29721
|
-
|
|
29935
|
+
const dest = path16.join(skillDir, f.path);
|
|
29936
|
+
fs10.mkdirSync(path16.dirname(dest), { recursive: true });
|
|
29722
29937
|
if (f.text !== void 0) {
|
|
29723
|
-
|
|
29938
|
+
fs10.writeFileSync(dest, f.text, "utf8");
|
|
29724
29939
|
} else if (f.base64) {
|
|
29725
|
-
|
|
29940
|
+
fs10.writeFileSync(dest, Buffer.from(f.base64, "base64"));
|
|
29726
29941
|
}
|
|
29727
29942
|
}
|
|
29728
29943
|
installed.push({
|
|
@@ -29743,11 +29958,11 @@ var handleInstallSkillsMessage = (msg, deps) => {
|
|
|
29743
29958
|
const id = typeof msg.id === "string" ? msg.id : "";
|
|
29744
29959
|
const targetDir = typeof msg.targetDir === "string" && msg.targetDir.trim() ? msg.targetDir.trim() : ".agents/skills";
|
|
29745
29960
|
const rawItems = msg.items;
|
|
29746
|
-
const
|
|
29747
|
-
const result = installRemoteSkills(
|
|
29961
|
+
const cwd = getBridgeWorkspaceDirectory();
|
|
29962
|
+
const result = installRemoteSkills(cwd, targetDir, rawItems);
|
|
29748
29963
|
if (!result.success) {
|
|
29749
29964
|
const err = result.error ?? "Invalid items";
|
|
29750
|
-
deps.log(`[Bridge service]
|
|
29965
|
+
deps.log(`[Bridge service] Install skills failed: ${err}`);
|
|
29751
29966
|
socket?.send(JSON.stringify({ type: "install_skills_result", id, success: false, error: err }));
|
|
29752
29967
|
return;
|
|
29753
29968
|
}
|
|
@@ -29816,8 +30031,7 @@ var handleSessionDiscardedMessage = (msg, deps) => {
|
|
|
29816
30031
|
};
|
|
29817
30032
|
|
|
29818
30033
|
// src/bridge/routing/handlers/revert-turn-snapshot.ts
|
|
29819
|
-
import * as
|
|
29820
|
-
import * as path16 from "node:path";
|
|
30034
|
+
import * as fs11 from "node:fs";
|
|
29821
30035
|
var handleRevertTurnSnapshotMessage = (msg, deps) => {
|
|
29822
30036
|
const id = typeof msg.id === "string" ? msg.id : "";
|
|
29823
30037
|
const sessionId = typeof msg.sessionId === "string" ? msg.sessionId : "";
|
|
@@ -29827,9 +30041,9 @@ var handleRevertTurnSnapshotMessage = (msg, deps) => {
|
|
|
29827
30041
|
void (async () => {
|
|
29828
30042
|
const s = getWs();
|
|
29829
30043
|
if (!s) return;
|
|
29830
|
-
const agentBase = sessionWorktreeManager.getAgentCwdForSession(sessionId) ??
|
|
30044
|
+
const agentBase = sessionWorktreeManager.getAgentCwdForSession(sessionId) ?? getBridgeWorkspaceDirectory();
|
|
29831
30045
|
const file2 = snapshotFilePath(agentBase, turnId);
|
|
29832
|
-
if (!
|
|
30046
|
+
if (!fs11.existsSync(file2)) {
|
|
29833
30047
|
sendWsMessage(s, {
|
|
29834
30048
|
type: "revert_turn_snapshot_result",
|
|
29835
30049
|
id,
|
|
@@ -29956,7 +30170,7 @@ import * as path20 from "node:path";
|
|
|
29956
30170
|
import os4 from "node:os";
|
|
29957
30171
|
|
|
29958
30172
|
// src/worktrees/prepare-new-session-worktrees.ts
|
|
29959
|
-
import * as
|
|
30173
|
+
import * as fs13 from "node:fs";
|
|
29960
30174
|
import * as path18 from "node:path";
|
|
29961
30175
|
|
|
29962
30176
|
// src/git/worktree-add.ts
|
|
@@ -29966,7 +30180,7 @@ async function gitWorktreeAddBranch(mainRepoPath, worktreePath, branch) {
|
|
|
29966
30180
|
}
|
|
29967
30181
|
|
|
29968
30182
|
// src/worktrees/worktree-layout-file.ts
|
|
29969
|
-
import * as
|
|
30183
|
+
import * as fs12 from "node:fs";
|
|
29970
30184
|
import * as path17 from "node:path";
|
|
29971
30185
|
import os3 from "node:os";
|
|
29972
30186
|
var LAYOUT_FILENAME = "worktree-launcher-layout.json";
|
|
@@ -29983,8 +30197,8 @@ function normalizeLoadedLayout(raw) {
|
|
|
29983
30197
|
function loadWorktreeLayout() {
|
|
29984
30198
|
try {
|
|
29985
30199
|
const p = defaultWorktreeLayoutPath();
|
|
29986
|
-
if (!
|
|
29987
|
-
const raw = JSON.parse(
|
|
30200
|
+
if (!fs12.existsSync(p)) return { launcherCwds: [] };
|
|
30201
|
+
const raw = JSON.parse(fs12.readFileSync(p, "utf8"));
|
|
29988
30202
|
return normalizeLoadedLayout(raw);
|
|
29989
30203
|
} catch {
|
|
29990
30204
|
return { launcherCwds: [] };
|
|
@@ -29993,8 +30207,8 @@ function loadWorktreeLayout() {
|
|
|
29993
30207
|
function saveWorktreeLayout(layout) {
|
|
29994
30208
|
try {
|
|
29995
30209
|
const dir = path17.dirname(defaultWorktreeLayoutPath());
|
|
29996
|
-
|
|
29997
|
-
|
|
30210
|
+
fs12.mkdirSync(dir, { recursive: true });
|
|
30211
|
+
fs12.writeFileSync(defaultWorktreeLayoutPath(), JSON.stringify(layout, null, 2), "utf8");
|
|
29998
30212
|
} catch {
|
|
29999
30213
|
}
|
|
30000
30214
|
}
|
|
@@ -30026,25 +30240,25 @@ async function prepareNewSessionWorktrees(options) {
|
|
|
30026
30240
|
const agentMirrorRoot = path18.join(rootAbs, cwdKey);
|
|
30027
30241
|
const repos = await discoverGitReposUnderRoot(launcherResolved);
|
|
30028
30242
|
if (repos.length === 0) {
|
|
30029
|
-
log2("[worktrees] No
|
|
30243
|
+
log2("[worktrees] No Git repositories under launcher working directory; skipping worktree creation.");
|
|
30030
30244
|
return null;
|
|
30031
30245
|
}
|
|
30032
30246
|
const branch = `session-${sessionId}`;
|
|
30033
30247
|
const worktreePaths = [];
|
|
30034
|
-
|
|
30248
|
+
fs13.mkdirSync(agentMirrorRoot, { recursive: true });
|
|
30035
30249
|
for (const repo of repos) {
|
|
30036
30250
|
let rel = path18.relative(launcherResolved, repo.absolutePath);
|
|
30037
30251
|
if (rel.startsWith("..") || path18.isAbsolute(rel)) continue;
|
|
30038
30252
|
const relNorm = rel === "" ? "." : rel;
|
|
30039
30253
|
const wtPath = path18.join(agentMirrorRoot, relNorm, sessionId);
|
|
30040
|
-
|
|
30254
|
+
fs13.mkdirSync(path18.dirname(wtPath), { recursive: true });
|
|
30041
30255
|
try {
|
|
30042
30256
|
await gitWorktreeAddBranch(repo.absolutePath, wtPath, branch);
|
|
30043
|
-
log2(`[worktrees] Added worktree ${wtPath} (branch ${branch})
|
|
30257
|
+
log2(`[worktrees] Added worktree ${wtPath} (branch ${branch}).`);
|
|
30044
30258
|
worktreePaths.push(wtPath);
|
|
30045
30259
|
} catch (e) {
|
|
30046
30260
|
log2(
|
|
30047
|
-
`[worktrees]
|
|
30261
|
+
`[worktrees] Worktree add failed for ${repo.absolutePath}: ${e instanceof Error ? e.message : String(e)}`
|
|
30048
30262
|
);
|
|
30049
30263
|
}
|
|
30050
30264
|
}
|
|
@@ -30066,24 +30280,26 @@ async function renameSessionWorktreeBranches(paths, newBranch, log2) {
|
|
|
30066
30280
|
await gitRenameCurrentBranch(wt, safe);
|
|
30067
30281
|
log2(`[worktrees] Renamed branch in ${wt} \u2192 ${safe}`);
|
|
30068
30282
|
} catch (e) {
|
|
30069
|
-
log2(
|
|
30283
|
+
log2(
|
|
30284
|
+
`[worktrees] Branch rename failed in ${wt}: ${e instanceof Error ? e.message : String(e)}`
|
|
30285
|
+
);
|
|
30070
30286
|
}
|
|
30071
30287
|
}
|
|
30072
30288
|
}
|
|
30073
30289
|
|
|
30074
30290
|
// src/worktrees/remove-session-worktrees.ts
|
|
30075
|
-
import * as
|
|
30291
|
+
import * as fs16 from "node:fs";
|
|
30076
30292
|
|
|
30077
30293
|
// src/git/worktree-remove.ts
|
|
30078
|
-
import * as
|
|
30294
|
+
import * as fs15 from "node:fs";
|
|
30079
30295
|
|
|
30080
30296
|
// src/git/resolve-main-repo-from-git-file.ts
|
|
30081
|
-
import * as
|
|
30297
|
+
import * as fs14 from "node:fs";
|
|
30082
30298
|
import * as path19 from "node:path";
|
|
30083
30299
|
function resolveMainRepoFromWorktreeGitFile(wt) {
|
|
30084
30300
|
const gitDirFile = path19.join(wt, ".git");
|
|
30085
|
-
if (!
|
|
30086
|
-
const first2 =
|
|
30301
|
+
if (!fs14.existsSync(gitDirFile) || !fs14.statSync(gitDirFile).isFile()) return "";
|
|
30302
|
+
const first2 = fs14.readFileSync(gitDirFile, "utf8").trim();
|
|
30087
30303
|
const m = first2.match(/^gitdir:\s*(.+)$/im);
|
|
30088
30304
|
if (!m) return "";
|
|
30089
30305
|
const gitWorktreePath = path19.resolve(wt, m[1].trim());
|
|
@@ -30097,7 +30313,7 @@ async function gitWorktreeRemoveForce(worktreePath) {
|
|
|
30097
30313
|
if (mainRepo) {
|
|
30098
30314
|
await simpleGit(mainRepo).raw(["worktree", "remove", "--force", worktreePath]);
|
|
30099
30315
|
} else {
|
|
30100
|
-
|
|
30316
|
+
fs15.rmSync(worktreePath, { recursive: true, force: true });
|
|
30101
30317
|
}
|
|
30102
30318
|
}
|
|
30103
30319
|
|
|
@@ -30108,9 +30324,9 @@ async function removeSessionWorktrees(paths, log2) {
|
|
|
30108
30324
|
await gitWorktreeRemoveForce(wt);
|
|
30109
30325
|
log2(`[worktrees] Removed worktree ${wt}`);
|
|
30110
30326
|
} catch (e) {
|
|
30111
|
-
log2(`[worktrees]
|
|
30327
|
+
log2(`[worktrees] Remove failed for ${wt}: ${e instanceof Error ? e.message : String(e)}`);
|
|
30112
30328
|
try {
|
|
30113
|
-
|
|
30329
|
+
fs16.rmSync(wt, { recursive: true, force: true });
|
|
30114
30330
|
} catch {
|
|
30115
30331
|
}
|
|
30116
30332
|
}
|
|
@@ -30174,7 +30390,7 @@ var SessionWorktreeManager = class {
|
|
|
30174
30390
|
return this.bridgeWantsWorktrees;
|
|
30175
30391
|
}
|
|
30176
30392
|
/**
|
|
30177
|
-
* Returns cwd for the agent (mirror of launcher tree), or undefined to use
|
|
30393
|
+
* Returns cwd for the agent (mirror of launcher tree), or undefined to use the bridge workspace directory.
|
|
30178
30394
|
*/
|
|
30179
30395
|
async resolveCwdForPrompt(sessionId, opts) {
|
|
30180
30396
|
if (!sessionId || !this.effective() || !opts.sessionWorktreesEnabled) {
|
|
@@ -30187,7 +30403,7 @@ var SessionWorktreeManager = class {
|
|
|
30187
30403
|
}
|
|
30188
30404
|
const prep = await prepareNewSessionWorktrees({
|
|
30189
30405
|
rootAbs: this.rootAbs,
|
|
30190
|
-
launcherCwd:
|
|
30406
|
+
launcherCwd: getBridgeWorkspaceDirectory(),
|
|
30191
30407
|
sessionId,
|
|
30192
30408
|
layout: this.layout,
|
|
30193
30409
|
log: this.log
|
|
@@ -30227,7 +30443,7 @@ var SessionWorktreeManager = class {
|
|
|
30227
30443
|
}
|
|
30228
30444
|
async commitSession(params) {
|
|
30229
30445
|
const paths = this.sessionPaths.get(params.sessionId);
|
|
30230
|
-
const targets = paths?.length ? paths : [
|
|
30446
|
+
const targets = paths?.length ? paths : [getBridgeWorkspaceDirectory()];
|
|
30231
30447
|
return commitSessionWorktrees({
|
|
30232
30448
|
paths: targets,
|
|
30233
30449
|
branch: params.branch,
|
|
@@ -30280,7 +30496,7 @@ function shouldIgnoreRelative(rel) {
|
|
|
30280
30496
|
}
|
|
30281
30497
|
function attachWatchErrorLog(w) {
|
|
30282
30498
|
w.on("error", (err) => {
|
|
30283
|
-
console.error("[file-index]
|
|
30499
|
+
console.error("[file-index] File watcher error:", err);
|
|
30284
30500
|
});
|
|
30285
30501
|
}
|
|
30286
30502
|
function createFsWatcher(resolved, schedule) {
|
|
@@ -30299,7 +30515,7 @@ function createFsWatcher(resolved, schedule) {
|
|
|
30299
30515
|
const code = typeof e === "object" && e !== null && "code" in e ? e.code : void 0;
|
|
30300
30516
|
if (code === "ERR_FEATURE_UNAVAILABLE_ON_PLATFORM") {
|
|
30301
30517
|
console.warn(
|
|
30302
|
-
"[file-index]
|
|
30518
|
+
"[file-index] Recursive file watching is unavailable on this platform; using non-recursive watch (Node 20+ on Linux enables recursive). Nested file changes may be missed until you upgrade."
|
|
30303
30519
|
);
|
|
30304
30520
|
const w = watch(resolved, { recursive: false }, onEvent);
|
|
30305
30521
|
attachWatchErrorLog(w);
|
|
@@ -30308,8 +30524,8 @@ function createFsWatcher(resolved, schedule) {
|
|
|
30308
30524
|
throw e;
|
|
30309
30525
|
}
|
|
30310
30526
|
}
|
|
30311
|
-
function startFileIndexWatcher(
|
|
30312
|
-
const resolved = path21.resolve(
|
|
30527
|
+
function startFileIndexWatcher(cwd = getBridgeWorkspaceDirectory()) {
|
|
30528
|
+
const resolved = path21.resolve(cwd);
|
|
30313
30529
|
try {
|
|
30314
30530
|
buildFileIndex(resolved);
|
|
30315
30531
|
} catch (e) {
|
|
@@ -30363,7 +30579,7 @@ async function sigtermAndWaitForExit(proc, graceMs, log2, shortId) {
|
|
|
30363
30579
|
const exited = new Promise((resolve14) => {
|
|
30364
30580
|
proc.once("exit", () => resolve14());
|
|
30365
30581
|
});
|
|
30366
|
-
log2(`[dev-server] Sending SIGTERM to ${shortId} (pid=${proc.pid ?? "?"})
|
|
30582
|
+
log2(`[dev-server] Sending SIGTERM to ${shortId} (pid=${proc.pid ?? "?"}).`);
|
|
30367
30583
|
try {
|
|
30368
30584
|
proc.kill("SIGTERM");
|
|
30369
30585
|
} catch {
|
|
@@ -30371,7 +30587,9 @@ async function sigtermAndWaitForExit(proc, graceMs, log2, shortId) {
|
|
|
30371
30587
|
await Promise.race([exited, new Promise((resolve14) => setTimeout(resolve14, graceMs))]);
|
|
30372
30588
|
}
|
|
30373
30589
|
function forceKillChild(proc, log2, shortId, graceMs) {
|
|
30374
|
-
log2(
|
|
30590
|
+
log2(
|
|
30591
|
+
`[dev-server] ${shortId} did not exit within ${graceMs}ms; sending SIGKILL (pid=${proc.pid ?? "?"}).`
|
|
30592
|
+
);
|
|
30375
30593
|
proc.removeAllListeners();
|
|
30376
30594
|
try {
|
|
30377
30595
|
proc.kill("SIGKILL");
|
|
@@ -30380,7 +30598,7 @@ function forceKillChild(proc, log2, shortId, graceMs) {
|
|
|
30380
30598
|
}
|
|
30381
30599
|
|
|
30382
30600
|
// src/dev-servers/process/wire-dev-server-child-process.ts
|
|
30383
|
-
import
|
|
30601
|
+
import fs17 from "node:fs";
|
|
30384
30602
|
|
|
30385
30603
|
// src/dev-servers/manager/forward-pipe.ts
|
|
30386
30604
|
function forwardChildPipe(childReadable, terminal, onData) {
|
|
@@ -30416,7 +30634,7 @@ function wireDevServerChildProcess(d) {
|
|
|
30416
30634
|
d.setPollInterval(void 0);
|
|
30417
30635
|
return;
|
|
30418
30636
|
}
|
|
30419
|
-
|
|
30637
|
+
fs17.readFile(d.mergedLogPath, (err, buf) => {
|
|
30420
30638
|
if (err || (d.getSpawnGeneration() ?? 0) !== d.scheduledGen) return;
|
|
30421
30639
|
if (buf.length <= d.mergedReadPos.value) return;
|
|
30422
30640
|
const chunk = Buffer.from(buf.subarray(d.mergedReadPos.value));
|
|
@@ -30441,7 +30659,7 @@ function wireDevServerChildProcess(d) {
|
|
|
30441
30659
|
d.rmMergedCleanupDir(cleanupDir);
|
|
30442
30660
|
}
|
|
30443
30661
|
if (signal) {
|
|
30444
|
-
d.log(`[dev-server] ${title} stopped (signal ${String(signal)})
|
|
30662
|
+
d.log(`[dev-server] ${title} stopped (signal: ${String(signal)}).`);
|
|
30445
30663
|
} else if (code !== null && code !== 0) {
|
|
30446
30664
|
const errTail = d.stderrTail.getTail().slice(-3).join("\n");
|
|
30447
30665
|
d.log(`[dev-server] ${title} exited with code ${code}${errTail ? `
|
|
@@ -30454,7 +30672,7 @@ ${errTail}` : ""}`);
|
|
|
30454
30672
|
d.sendStatus(code === 0 || code == null ? "stopped" : "error", detail, tails);
|
|
30455
30673
|
};
|
|
30456
30674
|
if (mergedPath) {
|
|
30457
|
-
|
|
30675
|
+
fs17.readFile(mergedPath, (err, buf) => {
|
|
30458
30676
|
if (!err && buf.length > d.mergedReadPos.value) {
|
|
30459
30677
|
const chunk = Buffer.from(buf.subarray(d.mergedReadPos.value));
|
|
30460
30678
|
if (chunk.length > 0) {
|
|
@@ -30556,13 +30774,13 @@ function parseDevServerDefs(servers) {
|
|
|
30556
30774
|
}
|
|
30557
30775
|
|
|
30558
30776
|
// src/dev-servers/manager/shell-spawn/utils.ts
|
|
30559
|
-
import
|
|
30777
|
+
import fs18 from "node:fs";
|
|
30560
30778
|
function isSpawnEbadf(e) {
|
|
30561
30779
|
return typeof e === "object" && e !== null && "code" in e && e.code === "EBADF";
|
|
30562
30780
|
}
|
|
30563
30781
|
function rmDirQuiet(dir) {
|
|
30564
30782
|
try {
|
|
30565
|
-
|
|
30783
|
+
fs18.rmSync(dir, { recursive: true, force: true });
|
|
30566
30784
|
} catch {
|
|
30567
30785
|
}
|
|
30568
30786
|
}
|
|
@@ -30570,7 +30788,7 @@ var cachedDevNullReadFd;
|
|
|
30570
30788
|
function devNullReadFd() {
|
|
30571
30789
|
if (cachedDevNullReadFd === void 0) {
|
|
30572
30790
|
const devPath = process.platform === "win32" ? "nul" : "/dev/null";
|
|
30573
|
-
cachedDevNullReadFd =
|
|
30791
|
+
cachedDevNullReadFd = fs18.openSync(devPath, "r");
|
|
30574
30792
|
}
|
|
30575
30793
|
return cachedDevNullReadFd;
|
|
30576
30794
|
}
|
|
@@ -30580,7 +30798,7 @@ function pipedStdoutStderrFor(attemptStdio) {
|
|
|
30580
30798
|
|
|
30581
30799
|
// src/dev-servers/manager/shell-spawn/try-spawn-piped-via-sh.ts
|
|
30582
30800
|
import { spawn as spawn5 } from "node:child_process";
|
|
30583
|
-
function trySpawnPipedViaSh(command, env,
|
|
30801
|
+
function trySpawnPipedViaSh(command, env, cwd, signal) {
|
|
30584
30802
|
const attempts = [
|
|
30585
30803
|
{ stdio: [devNullReadFd(), "pipe", "pipe"], endStdin: false },
|
|
30586
30804
|
{ stdio: ["ignore", "pipe", "pipe"], endStdin: true },
|
|
@@ -30591,7 +30809,7 @@ function trySpawnPipedViaSh(command, env, cwd3, signal) {
|
|
|
30591
30809
|
const attempt = attempts[i];
|
|
30592
30810
|
const opts = {
|
|
30593
30811
|
env,
|
|
30594
|
-
cwd
|
|
30812
|
+
cwd,
|
|
30595
30813
|
stdio: attempt.stdio,
|
|
30596
30814
|
...signal ? { signal } : {}
|
|
30597
30815
|
};
|
|
@@ -30623,11 +30841,11 @@ function trySpawnPipedViaSh(command, env, cwd3, signal) {
|
|
|
30623
30841
|
|
|
30624
30842
|
// src/dev-servers/manager/shell-spawn/try-spawn-shell-true-piped.ts
|
|
30625
30843
|
import { spawn as spawn6 } from "node:child_process";
|
|
30626
|
-
function trySpawnShellTruePiped(command, env,
|
|
30844
|
+
function trySpawnShellTruePiped(command, env, cwd, devNullFd, signal) {
|
|
30627
30845
|
try {
|
|
30628
30846
|
const opts = {
|
|
30629
30847
|
env,
|
|
30630
|
-
cwd
|
|
30848
|
+
cwd,
|
|
30631
30849
|
stdio: [devNullFd, "pipe", "pipe"],
|
|
30632
30850
|
shell: true,
|
|
30633
30851
|
...signal ? { signal } : {}
|
|
@@ -30644,15 +30862,15 @@ function trySpawnShellTruePiped(command, env, cwd3, devNullFd, signal) {
|
|
|
30644
30862
|
|
|
30645
30863
|
// src/dev-servers/manager/shell-spawn/try-spawn-merged-log-file.ts
|
|
30646
30864
|
import { spawn as spawn7 } from "node:child_process";
|
|
30647
|
-
import
|
|
30865
|
+
import fs19 from "node:fs";
|
|
30648
30866
|
import { tmpdir } from "node:os";
|
|
30649
30867
|
import path22 from "node:path";
|
|
30650
|
-
function trySpawnMergedLogFile(command, env,
|
|
30651
|
-
const tmpRoot =
|
|
30868
|
+
function trySpawnMergedLogFile(command, env, cwd, signal) {
|
|
30869
|
+
const tmpRoot = fs19.mkdtempSync(path22.join(tmpdir(), "ba-devsrv-log-"));
|
|
30652
30870
|
const logPath = path22.join(tmpRoot, "combined.log");
|
|
30653
30871
|
let logFd;
|
|
30654
30872
|
try {
|
|
30655
|
-
logFd =
|
|
30873
|
+
logFd = fs19.openSync(logPath, "a");
|
|
30656
30874
|
} catch {
|
|
30657
30875
|
rmDirQuiet(tmpRoot);
|
|
30658
30876
|
return null;
|
|
@@ -30663,15 +30881,15 @@ function trySpawnMergedLogFile(command, env, cwd3, signal) {
|
|
|
30663
30881
|
if (process.platform === "win32") {
|
|
30664
30882
|
proc = spawn7(process.env.ComSpec || "cmd.exe", ["/d", "/s", "/c", command], {
|
|
30665
30883
|
env,
|
|
30666
|
-
cwd
|
|
30884
|
+
cwd,
|
|
30667
30885
|
stdio,
|
|
30668
30886
|
windowsHide: true,
|
|
30669
30887
|
...signal ? { signal } : {}
|
|
30670
30888
|
});
|
|
30671
30889
|
} else {
|
|
30672
|
-
proc = spawn7("/bin/sh", ["-c", command], { env, cwd
|
|
30890
|
+
proc = spawn7("/bin/sh", ["-c", command], { env, cwd, stdio, ...signal ? { signal } : {} });
|
|
30673
30891
|
}
|
|
30674
|
-
|
|
30892
|
+
fs19.closeSync(logFd);
|
|
30675
30893
|
return {
|
|
30676
30894
|
proc,
|
|
30677
30895
|
pipedStdoutStderr: true,
|
|
@@ -30680,7 +30898,7 @@ function trySpawnMergedLogFile(command, env, cwd3, signal) {
|
|
|
30680
30898
|
};
|
|
30681
30899
|
} catch (e) {
|
|
30682
30900
|
try {
|
|
30683
|
-
|
|
30901
|
+
fs19.closeSync(logFd);
|
|
30684
30902
|
} catch {
|
|
30685
30903
|
}
|
|
30686
30904
|
rmDirQuiet(tmpRoot);
|
|
@@ -30691,25 +30909,25 @@ function trySpawnMergedLogFile(command, env, cwd3, signal) {
|
|
|
30691
30909
|
|
|
30692
30910
|
// src/dev-servers/manager/shell-spawn/try-spawn-shell-script-log-redirect.ts
|
|
30693
30911
|
import { spawn as spawn8 } from "node:child_process";
|
|
30694
|
-
import
|
|
30912
|
+
import fs20 from "node:fs";
|
|
30695
30913
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
30696
30914
|
import path23 from "node:path";
|
|
30697
30915
|
function shSingleQuote(s) {
|
|
30698
30916
|
return `'${s.replace(/'/g, `'\\''`)}'`;
|
|
30699
30917
|
}
|
|
30700
|
-
function trySpawnShellScriptLogRedirectUnix(command, env,
|
|
30701
|
-
const tmpRoot =
|
|
30918
|
+
function trySpawnShellScriptLogRedirectUnix(command, env, cwd, signal) {
|
|
30919
|
+
const tmpRoot = fs20.mkdtempSync(path23.join(tmpdir2(), "ba-devsrv-sh-"));
|
|
30702
30920
|
const logPath = path23.join(tmpRoot, "combined.log");
|
|
30703
30921
|
const innerPath = path23.join(tmpRoot, "_cmd.sh");
|
|
30704
30922
|
const runnerPath = path23.join(tmpRoot, "_run.sh");
|
|
30705
30923
|
try {
|
|
30706
|
-
|
|
30924
|
+
fs20.writeFileSync(innerPath, `#!/bin/sh
|
|
30707
30925
|
${command}
|
|
30708
30926
|
`);
|
|
30709
|
-
|
|
30927
|
+
fs20.writeFileSync(
|
|
30710
30928
|
runnerPath,
|
|
30711
30929
|
`#!/bin/sh
|
|
30712
|
-
cd ${shSingleQuote(
|
|
30930
|
+
cd ${shSingleQuote(cwd)}
|
|
30713
30931
|
/bin/sh ${shSingleQuote(innerPath)} >>${shSingleQuote(logPath)} 2>&1
|
|
30714
30932
|
`
|
|
30715
30933
|
);
|
|
@@ -30731,17 +30949,17 @@ cd ${shSingleQuote(cwd3)}
|
|
|
30731
30949
|
throw e;
|
|
30732
30950
|
}
|
|
30733
30951
|
}
|
|
30734
|
-
function trySpawnShellScriptLogRedirectWin(command, env,
|
|
30735
|
-
const tmpRoot =
|
|
30952
|
+
function trySpawnShellScriptLogRedirectWin(command, env, cwd, signal) {
|
|
30953
|
+
const tmpRoot = fs20.mkdtempSync(path23.join(tmpdir2(), "ba-devsrv-sh-"));
|
|
30736
30954
|
const logPath = path23.join(tmpRoot, "combined.log");
|
|
30737
30955
|
const runnerPath = path23.join(tmpRoot, "_run.bat");
|
|
30738
30956
|
const q = (p) => `"${p.replace(/"/g, '""')}"`;
|
|
30739
30957
|
const com = process.env.ComSpec || "cmd.exe";
|
|
30740
30958
|
try {
|
|
30741
|
-
|
|
30959
|
+
fs20.writeFileSync(
|
|
30742
30960
|
runnerPath,
|
|
30743
30961
|
`@ECHO OFF\r
|
|
30744
|
-
CD /D ${q(
|
|
30962
|
+
CD /D ${q(cwd)}\r
|
|
30745
30963
|
${command} >> ${q(logPath)} 2>&1\r
|
|
30746
30964
|
`
|
|
30747
30965
|
);
|
|
@@ -30767,10 +30985,10 @@ ${command} >> ${q(logPath)} 2>&1\r
|
|
|
30767
30985
|
|
|
30768
30986
|
// src/dev-servers/manager/shell-spawn/try-spawn-inherit.ts
|
|
30769
30987
|
import { spawn as spawn9 } from "node:child_process";
|
|
30770
|
-
function trySpawnInheritStdio(command, env,
|
|
30988
|
+
function trySpawnInheritStdio(command, env, cwd, signal) {
|
|
30771
30989
|
const opts = {
|
|
30772
30990
|
env,
|
|
30773
|
-
cwd
|
|
30991
|
+
cwd,
|
|
30774
30992
|
stdio: "inherit",
|
|
30775
30993
|
...signal ? { signal } : {}
|
|
30776
30994
|
};
|
|
@@ -30786,27 +31004,27 @@ function trySpawnInheritStdio(command, env, cwd3, signal) {
|
|
|
30786
31004
|
}
|
|
30787
31005
|
|
|
30788
31006
|
// src/dev-servers/manager/shell-spawn/shell-spawn.ts
|
|
30789
|
-
function shellSpawn(command, env,
|
|
31007
|
+
function shellSpawn(command, env, cwd, options) {
|
|
30790
31008
|
const signal = options?.signal;
|
|
30791
|
-
const piped = trySpawnPipedViaSh(command, env,
|
|
31009
|
+
const piped = trySpawnPipedViaSh(command, env, cwd, signal);
|
|
30792
31010
|
if (piped.ok) {
|
|
30793
31011
|
return piped.result;
|
|
30794
31012
|
}
|
|
30795
31013
|
let lastErr = piped.lastErr;
|
|
30796
|
-
const shellTrueProc = trySpawnShellTruePiped(command, env,
|
|
31014
|
+
const shellTrueProc = trySpawnShellTruePiped(command, env, cwd, devNullReadFd(), signal);
|
|
30797
31015
|
if (shellTrueProc) {
|
|
30798
31016
|
return { proc: shellTrueProc, pipedStdoutStderr: true };
|
|
30799
31017
|
}
|
|
30800
|
-
const fileCapture = trySpawnMergedLogFile(command, env,
|
|
31018
|
+
const fileCapture = trySpawnMergedLogFile(command, env, cwd, signal);
|
|
30801
31019
|
if (fileCapture) {
|
|
30802
31020
|
return fileCapture;
|
|
30803
31021
|
}
|
|
30804
|
-
const scriptCapture = process.platform === "win32" ? trySpawnShellScriptLogRedirectWin(command, env,
|
|
31022
|
+
const scriptCapture = process.platform === "win32" ? trySpawnShellScriptLogRedirectWin(command, env, cwd, signal) : trySpawnShellScriptLogRedirectUnix(command, env, cwd, signal);
|
|
30805
31023
|
if (scriptCapture) {
|
|
30806
31024
|
return scriptCapture;
|
|
30807
31025
|
}
|
|
30808
31026
|
try {
|
|
30809
|
-
return trySpawnInheritStdio(command, env,
|
|
31027
|
+
return trySpawnInheritStdio(command, env, cwd, signal);
|
|
30810
31028
|
} catch (e) {
|
|
30811
31029
|
throw lastErr instanceof Error ? lastErr : e instanceof Error ? e : new Error(String(e));
|
|
30812
31030
|
}
|
|
@@ -30864,9 +31082,11 @@ var DevServerManager = class {
|
|
|
30864
31082
|
abortControllersByServerId = /* @__PURE__ */ new Map();
|
|
30865
31083
|
getWs;
|
|
30866
31084
|
log;
|
|
31085
|
+
getBridgeCwd;
|
|
30867
31086
|
constructor(options) {
|
|
30868
31087
|
this.getWs = options.getWs;
|
|
30869
31088
|
this.log = options.log;
|
|
31089
|
+
this.getBridgeCwd = options.getBridgeCwd ?? (() => process.cwd());
|
|
30870
31090
|
}
|
|
30871
31091
|
attachFirehose(send) {
|
|
30872
31092
|
this.firehoseSend = send;
|
|
@@ -30990,7 +31210,7 @@ var DevServerManager = class {
|
|
|
30990
31210
|
this.sendStatus(serverId, "starting", void 0, emptyTails());
|
|
30991
31211
|
const ac = new AbortController();
|
|
30992
31212
|
this.abortControllersByServerId.set(serverId, ac);
|
|
30993
|
-
const
|
|
31213
|
+
const cwd = this.getBridgeCwd();
|
|
30994
31214
|
const childEnv = envForSpawn(process.env, def.env, def.ports);
|
|
30995
31215
|
const cmd = substituteCommand(def.command.trim(), childEnv);
|
|
30996
31216
|
const title = def.name.trim() || serverId.slice(0, 8);
|
|
@@ -31005,7 +31225,7 @@ var DevServerManager = class {
|
|
|
31005
31225
|
let mergedLogPath;
|
|
31006
31226
|
let mergedCleanupDir;
|
|
31007
31227
|
try {
|
|
31008
|
-
const spawned = shellSpawn(cmd, childEnv,
|
|
31228
|
+
const spawned = shellSpawn(cmd, childEnv, cwd, {
|
|
31009
31229
|
signal: ac.signal
|
|
31010
31230
|
});
|
|
31011
31231
|
proc = spawned.proc;
|
|
@@ -31014,7 +31234,7 @@ var DevServerManager = class {
|
|
|
31014
31234
|
mergedCleanupDir = spawned.mergedLogCleanupDir;
|
|
31015
31235
|
} catch (e) {
|
|
31016
31236
|
const msg = e instanceof Error ? e.message : String(e);
|
|
31017
|
-
this.log(`[dev-server] ${title}
|
|
31237
|
+
this.log(`[dev-server] Failed to start ${title}: ${msg}`);
|
|
31018
31238
|
this.abortControllersByServerId.delete(serverId);
|
|
31019
31239
|
this.sendStatus(serverId, "error", msg, emptyTails());
|
|
31020
31240
|
return;
|
|
@@ -31139,7 +31359,7 @@ function startStreamingProxy(ws, log2, pr) {
|
|
|
31139
31359
|
},
|
|
31140
31360
|
onEnd: () => sendWsMessage(ws, { type: "proxy_result_end", id: pr.id }),
|
|
31141
31361
|
onError: (error40) => {
|
|
31142
|
-
log2(`[Proxy and log service]
|
|
31362
|
+
log2(`[Proxy and log service] Streaming preview failed: ${error40}`);
|
|
31143
31363
|
sendWsMessage(ws, { type: "proxy_result_error", id: pr.id, error: error40 });
|
|
31144
31364
|
}
|
|
31145
31365
|
});
|
|
@@ -31212,11 +31432,11 @@ var handleProxyMessage = (msg, deps) => {
|
|
|
31212
31432
|
return;
|
|
31213
31433
|
}
|
|
31214
31434
|
void proxyToLocal(pr).then((res) => {
|
|
31215
|
-
if (res.error) deps.log(`[Proxy and log service]
|
|
31435
|
+
if (res.error) deps.log(`[Proxy and log service] Preview proxy failed: ${res.error}`);
|
|
31216
31436
|
sendWsMessage(deps.ws, { type: "proxy_result", ...res, id: pr.id });
|
|
31217
31437
|
}).catch((err) => {
|
|
31218
31438
|
deps.log(
|
|
31219
|
-
`[Proxy and log service]
|
|
31439
|
+
`[Proxy and log service] Preview proxy failed: ${err instanceof Error ? err.message : String(err)}`
|
|
31220
31440
|
);
|
|
31221
31441
|
sendWsMessage(deps.ws, { type: "proxy_result", id: pr.id, error: String(err) });
|
|
31222
31442
|
});
|
|
@@ -31253,6 +31473,7 @@ function tryConsumeBinaryProxyBody(raw, deps) {
|
|
|
31253
31473
|
}
|
|
31254
31474
|
|
|
31255
31475
|
// src/firehose/connect-firehose.ts
|
|
31476
|
+
var FIREHOSE_CLIENT_PING_MS = 25e3;
|
|
31256
31477
|
function connectFirehose(options) {
|
|
31257
31478
|
const { firehoseServerUrl, workspaceId, bridgeName, proxyPorts, log: log2, devServerManager, onOpen, onClose } = options;
|
|
31258
31479
|
const wsUrl = buildFirehoseCliWsUrl(firehoseServerUrl);
|
|
@@ -31261,6 +31482,13 @@ function connectFirehose(options) {
|
|
|
31261
31482
|
wsOptions.agent = new https2.Agent({ rejectUnauthorized: false });
|
|
31262
31483
|
}
|
|
31263
31484
|
const ws = new wrapper_default(wsUrl, wsOptions);
|
|
31485
|
+
let clientPingTimer = null;
|
|
31486
|
+
function clearClientPing() {
|
|
31487
|
+
if (clientPingTimer != null) {
|
|
31488
|
+
clearInterval(clientPingTimer);
|
|
31489
|
+
clientPingTimer = null;
|
|
31490
|
+
}
|
|
31491
|
+
}
|
|
31264
31492
|
const firehoseSend = (payload) => {
|
|
31265
31493
|
sendWsMessage(ws, payload);
|
|
31266
31494
|
};
|
|
@@ -31273,6 +31501,15 @@ function connectFirehose(options) {
|
|
|
31273
31501
|
startStreamingProxy: (pr) => startStreamingProxy(ws, log2, pr)
|
|
31274
31502
|
};
|
|
31275
31503
|
ws.on("open", () => {
|
|
31504
|
+
clearClientPing();
|
|
31505
|
+
clientPingTimer = setInterval(() => {
|
|
31506
|
+
if (ws.readyState === wrapper_default.OPEN) {
|
|
31507
|
+
try {
|
|
31508
|
+
ws.ping();
|
|
31509
|
+
} catch {
|
|
31510
|
+
}
|
|
31511
|
+
}
|
|
31512
|
+
}, FIREHOSE_CLIENT_PING_MS);
|
|
31276
31513
|
onOpen?.();
|
|
31277
31514
|
devServerManager.attachFirehose(firehoseSend);
|
|
31278
31515
|
sendWsMessage(ws, { type: "identify", workspaceId, bridgeName, proxyPorts });
|
|
@@ -31290,35 +31527,30 @@ function connectFirehose(options) {
|
|
|
31290
31527
|
});
|
|
31291
31528
|
});
|
|
31292
31529
|
ws.on("close", (code, reason) => {
|
|
31530
|
+
clearClientPing();
|
|
31293
31531
|
devServerManager.detachFirehose();
|
|
31294
|
-
|
|
31295
|
-
|
|
31296
|
-
"[Proxy and log service]",
|
|
31297
|
-
code,
|
|
31298
|
-
typeof reason === "string" ? reason : reason.toString(),
|
|
31299
|
-
"reconnects automatically if the bridge service stays connected"
|
|
31300
|
-
)
|
|
31301
|
-
);
|
|
31302
|
-
onClose?.();
|
|
31532
|
+
const reasonStr = typeof reason === "string" ? reason : reason.toString();
|
|
31533
|
+
onClose?.(code, reasonStr);
|
|
31303
31534
|
});
|
|
31304
31535
|
ws.on("error", (err) => {
|
|
31536
|
+
clearClientPing();
|
|
31305
31537
|
log2(`[Proxy and log service] WebSocket error: ${err.message}`);
|
|
31306
31538
|
});
|
|
31307
31539
|
return {
|
|
31308
31540
|
close() {
|
|
31541
|
+
clearClientPing();
|
|
31309
31542
|
devServerManager.detachFirehose();
|
|
31310
31543
|
try {
|
|
31311
31544
|
ws.removeAllListeners();
|
|
31312
31545
|
ws.close();
|
|
31313
31546
|
} catch {
|
|
31314
31547
|
}
|
|
31315
|
-
}
|
|
31548
|
+
},
|
|
31549
|
+
isConnected: () => ws.readyState === wrapper_default.OPEN
|
|
31316
31550
|
};
|
|
31317
31551
|
}
|
|
31318
31552
|
|
|
31319
31553
|
// src/bridge/connection/create-bridge-identified-handler.ts
|
|
31320
|
-
var FH_RECONNECT_BASE_MS = 2e3;
|
|
31321
|
-
var FH_RECONNECT_MAX_MS = 3e4;
|
|
31322
31554
|
function createOnBridgeIdentified(opts) {
|
|
31323
31555
|
const { sessionWorktreeManager, devServerManager, firehoseServerUrl, workspaceId, state, logFn } = opts;
|
|
31324
31556
|
function clearFirehoseReconnectTimer() {
|
|
@@ -31327,6 +31559,14 @@ function createOnBridgeIdentified(opts) {
|
|
|
31327
31559
|
state.firehoseReconnectTimeout = null;
|
|
31328
31560
|
}
|
|
31329
31561
|
}
|
|
31562
|
+
function firehoseCtx() {
|
|
31563
|
+
return {
|
|
31564
|
+
closedByUser: state.closedByUser,
|
|
31565
|
+
currentWs: state.currentWs,
|
|
31566
|
+
firehoseHandle: state.firehoseHandle,
|
|
31567
|
+
firehoseQuiet: state.firehoseQuiet
|
|
31568
|
+
};
|
|
31569
|
+
}
|
|
31330
31570
|
function attachFirehose(params) {
|
|
31331
31571
|
state.lastFirehoseParams = params;
|
|
31332
31572
|
clearFirehoseReconnectTimer();
|
|
@@ -31345,37 +31585,52 @@ function createOnBridgeIdentified(opts) {
|
|
|
31345
31585
|
devServerManager,
|
|
31346
31586
|
onOpen: () => {
|
|
31347
31587
|
if (myGen !== state.firehoseGeneration) return;
|
|
31588
|
+
clearFirehoseReconnectQuietOnOpen({ firehoseQuiet: state.firehoseQuiet }, logFn);
|
|
31589
|
+
const logOpenAsFirehoseReconnect = state.firehoseReconnectAttempt > 0;
|
|
31348
31590
|
state.firehoseReconnectAttempt = 0;
|
|
31591
|
+
if (logOpenAsFirehoseReconnect) {
|
|
31592
|
+
logFn("[Proxy and log service] reconnected.");
|
|
31593
|
+
}
|
|
31349
31594
|
},
|
|
31350
|
-
onClose: () => {
|
|
31595
|
+
onClose: (code, reason) => {
|
|
31351
31596
|
if (myGen !== state.firehoseGeneration) return;
|
|
31352
31597
|
state.firehoseHandle = null;
|
|
31353
31598
|
if (state.closedByUser) return;
|
|
31354
31599
|
const main = state.currentWs;
|
|
31355
31600
|
if (!main || main.readyState !== wrapper_default.OPEN) {
|
|
31356
|
-
logFn(
|
|
31601
|
+
logFn(
|
|
31602
|
+
`${PROXY_AND_LOG_SERVICE_LABEL} Not reconnecting preview and log stream: main bridge connection is not open.`
|
|
31603
|
+
);
|
|
31357
31604
|
return;
|
|
31358
31605
|
}
|
|
31606
|
+
beginFirehoseDeferredDisconnect(firehoseCtx(), code, reason, logFn);
|
|
31359
31607
|
clearFirehoseReconnectTimer();
|
|
31360
|
-
const delay2 =
|
|
31361
|
-
FH_RECONNECT_BASE_MS * 2 ** state.firehoseReconnectAttempt,
|
|
31362
|
-
FH_RECONNECT_MAX_MS
|
|
31363
|
-
);
|
|
31608
|
+
const delay2 = reconnectDelayMs(state.firehoseReconnectAttempt);
|
|
31364
31609
|
state.firehoseReconnectAttempt += 1;
|
|
31365
|
-
|
|
31366
|
-
|
|
31610
|
+
logNextReconnectAttempt(
|
|
31611
|
+
logFn,
|
|
31612
|
+
PROXY_AND_LOG_SERVICE_LABEL,
|
|
31613
|
+
state.firehoseQuiet,
|
|
31614
|
+
delay2,
|
|
31615
|
+
state.firehoseReconnectAttempt
|
|
31367
31616
|
);
|
|
31368
31617
|
state.firehoseReconnectTimeout = setTimeout(() => {
|
|
31369
31618
|
state.firehoseReconnectTimeout = null;
|
|
31370
31619
|
if (state.closedByUser) return;
|
|
31371
31620
|
const w = state.currentWs;
|
|
31372
31621
|
if (!w || w.readyState !== wrapper_default.OPEN) {
|
|
31373
|
-
|
|
31622
|
+
if (state.firehoseQuiet.verboseLogs) {
|
|
31623
|
+
logFn(
|
|
31624
|
+
`${PROXY_AND_LOG_SERVICE_LABEL} Reconnect skipped: main bridge connection closed before preview stream could reconnect.`
|
|
31625
|
+
);
|
|
31626
|
+
}
|
|
31374
31627
|
return;
|
|
31375
31628
|
}
|
|
31376
31629
|
const p = state.lastFirehoseParams;
|
|
31377
31630
|
if (!p) {
|
|
31378
|
-
|
|
31631
|
+
if (state.firehoseQuiet.verboseLogs) {
|
|
31632
|
+
logFn(`${PROXY_AND_LOG_SERVICE_LABEL} Reconnect skipped: no stored connection parameters.`);
|
|
31633
|
+
}
|
|
31379
31634
|
return;
|
|
31380
31635
|
}
|
|
31381
31636
|
attachFirehose(p);
|
|
@@ -31458,10 +31713,12 @@ function createSendLocalSkillsReport(getWs, logFn) {
|
|
|
31458
31713
|
try {
|
|
31459
31714
|
const socket = getWs();
|
|
31460
31715
|
if (!socket || socket.readyState !== wrapper_default.OPEN) return;
|
|
31461
|
-
const skills2 = discoverLocalSkills(
|
|
31716
|
+
const skills2 = discoverLocalSkills(getBridgeWorkspaceDirectory());
|
|
31462
31717
|
socket.send(JSON.stringify({ type: "local_skills", skills: skills2 }));
|
|
31463
31718
|
} catch (e) {
|
|
31464
|
-
logFn(
|
|
31719
|
+
logFn(
|
|
31720
|
+
`[Bridge service] Local skills report failed: ${e instanceof Error ? e.message : String(e)}`
|
|
31721
|
+
);
|
|
31465
31722
|
}
|
|
31466
31723
|
};
|
|
31467
31724
|
}
|
|
@@ -31474,12 +31731,15 @@ function createReportAutoDetectedAgents(getWs, logFn) {
|
|
|
31474
31731
|
sendWsMessage(socket, { type: "auto_detected_agents_report", agentTypes: types });
|
|
31475
31732
|
}
|
|
31476
31733
|
} catch (e) {
|
|
31477
|
-
logFn(
|
|
31734
|
+
logFn(
|
|
31735
|
+
`[Bridge service] Auto-detected agents report failed: ${e instanceof Error ? e.message : String(e)}`
|
|
31736
|
+
);
|
|
31478
31737
|
}
|
|
31479
31738
|
};
|
|
31480
31739
|
}
|
|
31481
31740
|
|
|
31482
31741
|
// src/bridge/connection/create-bridge-connection.ts
|
|
31742
|
+
var BRIDGE_CLIENT_PING_MS = 25e3;
|
|
31483
31743
|
async function createBridgeConnection(options) {
|
|
31484
31744
|
const { apiUrl, workspaceId, justAuthenticated, onAuthInvalid, persistTokens } = options;
|
|
31485
31745
|
const firehoseServerUrl = options.firehoseServerUrl ?? options.proxyServerUrl;
|
|
@@ -31490,13 +31750,16 @@ async function createBridgeConnection(options) {
|
|
|
31490
31750
|
const state = {
|
|
31491
31751
|
closedByUser: false,
|
|
31492
31752
|
reconnectAttempt: 0,
|
|
31753
|
+
logBridgeOpenAsReconnect: false,
|
|
31493
31754
|
reconnectTimeout: null,
|
|
31494
31755
|
currentWs: null,
|
|
31756
|
+
mainQuiet: createEmptyReconnectQuietSlot(),
|
|
31495
31757
|
firehoseHandle: null,
|
|
31496
31758
|
lastFirehoseParams: null,
|
|
31497
31759
|
firehoseReconnectTimeout: null,
|
|
31498
31760
|
firehoseReconnectAttempt: 0,
|
|
31499
|
-
firehoseGeneration: 0
|
|
31761
|
+
firehoseGeneration: 0,
|
|
31762
|
+
firehoseQuiet: createEmptyReconnectQuietSlot()
|
|
31500
31763
|
};
|
|
31501
31764
|
const worktreesRootAbs = options.worktreesRootAbs ?? defaultWorktreesRootAbs();
|
|
31502
31765
|
const sessionWorktreeManager = new SessionWorktreeManager({
|
|
@@ -31508,7 +31771,7 @@ async function createBridgeConnection(options) {
|
|
|
31508
31771
|
function getWs() {
|
|
31509
31772
|
return state.currentWs;
|
|
31510
31773
|
}
|
|
31511
|
-
const devServerManager = new DevServerManager({ getWs, log: logFn });
|
|
31774
|
+
const devServerManager = new DevServerManager({ getWs, log: logFn, getBridgeCwd: getBridgeWorkspaceDirectory });
|
|
31512
31775
|
const onBridgeIdentified = createOnBridgeIdentified({
|
|
31513
31776
|
sessionWorktreeManager,
|
|
31514
31777
|
devServerManager,
|
|
@@ -31520,7 +31783,13 @@ async function createBridgeConnection(options) {
|
|
|
31520
31783
|
const sendLocalSkillsReport = createSendLocalSkillsReport(getWs, logFn);
|
|
31521
31784
|
const reportAutoDetectedAgents = createReportAutoDetectedAgents(getWs, logFn);
|
|
31522
31785
|
function handleOpen() {
|
|
31786
|
+
const logOpenAsPostRefreshReconnect = state.logBridgeOpenAsReconnect;
|
|
31787
|
+
clearMainBridgeReconnectQuietOnOpen(state, logFn);
|
|
31523
31788
|
state.reconnectAttempt = 0;
|
|
31789
|
+
state.logBridgeOpenAsReconnect = false;
|
|
31790
|
+
if (logOpenAsPostRefreshReconnect) {
|
|
31791
|
+
logFn("[Bridge service] reconnected.");
|
|
31792
|
+
}
|
|
31524
31793
|
const socket = getWs();
|
|
31525
31794
|
if (socket) {
|
|
31526
31795
|
sendWsMessage(socket, { type: "identify", role: "cli" });
|
|
@@ -31537,11 +31806,9 @@ async function createBridgeConnection(options) {
|
|
|
31537
31806
|
state.currentWs = null;
|
|
31538
31807
|
if (was) was.removeAllListeners();
|
|
31539
31808
|
const willReconnect = !state.closedByUser;
|
|
31540
|
-
logFn
|
|
31541
|
-
formatWebSocketClose("[Bridge service]", code, reason, willReconnect ? "will schedule reconnect" : "not reconnecting (CLI shutting down)")
|
|
31542
|
-
);
|
|
31809
|
+
beginMainBridgeDeferredDisconnect(state, code, reason, logFn, willReconnect);
|
|
31543
31810
|
if (willReconnect) {
|
|
31544
|
-
|
|
31811
|
+
scheduleMainBridgeReconnect(state, connect, logFn);
|
|
31545
31812
|
}
|
|
31546
31813
|
}
|
|
31547
31814
|
const messageDeps = {
|
|
@@ -31556,6 +31823,10 @@ async function createBridgeConnection(options) {
|
|
|
31556
31823
|
};
|
|
31557
31824
|
function connect() {
|
|
31558
31825
|
if (state.closedByUser) return;
|
|
31826
|
+
if (state.reconnectTimeout != null) {
|
|
31827
|
+
clearTimeout(state.reconnectTimeout);
|
|
31828
|
+
state.reconnectTimeout = null;
|
|
31829
|
+
}
|
|
31559
31830
|
const prev = state.currentWs;
|
|
31560
31831
|
if (prev) {
|
|
31561
31832
|
prev.removeAllListeners();
|
|
@@ -31568,6 +31839,7 @@ async function createBridgeConnection(options) {
|
|
|
31568
31839
|
const url2 = buildBridgeUrl(apiUrl, workspaceId, accessToken);
|
|
31569
31840
|
state.currentWs = createWsBridge({
|
|
31570
31841
|
url: url2,
|
|
31842
|
+
clientPingIntervalMs: BRIDGE_CLIENT_PING_MS,
|
|
31571
31843
|
onAuthInvalid: () => {
|
|
31572
31844
|
if (authRefreshInFlight) return;
|
|
31573
31845
|
void (async () => {
|
|
@@ -31579,8 +31851,9 @@ async function createBridgeConnection(options) {
|
|
|
31579
31851
|
accessToken = next.token;
|
|
31580
31852
|
refreshTok = next.refreshToken;
|
|
31581
31853
|
persistTokens?.({ token: accessToken, refreshToken: refreshTok });
|
|
31582
|
-
logFn("[Bridge service]
|
|
31854
|
+
logFn("[Bridge service] Access token refreshed; reconnecting\u2026");
|
|
31583
31855
|
state.reconnectAttempt = 0;
|
|
31856
|
+
state.logBridgeOpenAsReconnect = true;
|
|
31584
31857
|
authRefreshInFlight = false;
|
|
31585
31858
|
connect();
|
|
31586
31859
|
return;
|
|
@@ -31603,7 +31876,7 @@ async function createBridgeConnection(options) {
|
|
|
31603
31876
|
});
|
|
31604
31877
|
}
|
|
31605
31878
|
connect();
|
|
31606
|
-
const stopFileIndexWatcher = startFileIndexWatcher(
|
|
31879
|
+
const stopFileIndexWatcher = startFileIndexWatcher(getBridgeWorkspaceDirectory());
|
|
31607
31880
|
return {
|
|
31608
31881
|
close: async () => {
|
|
31609
31882
|
stopFileIndexWatcher();
|
|
@@ -31676,7 +31949,7 @@ async function runBridge(options) {
|
|
|
31676
31949
|
});
|
|
31677
31950
|
},
|
|
31678
31951
|
onAuthInvalid: () => {
|
|
31679
|
-
log("[Bridge service] token invalid or revoked; re-authenticating\u2026");
|
|
31952
|
+
log("[Bridge service] Access token invalid or revoked; re-authenticating\u2026");
|
|
31680
31953
|
clearConfigForApi(apiUrl);
|
|
31681
31954
|
void handle.close().then(() => {
|
|
31682
31955
|
void runBridge({ apiUrl, firehoseServerUrl, worktreesRootAbs });
|