@buildautomaton/cli 0.1.5 → 0.1.6
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 +411 -243
- package/dist/cli.js.map +4 -4
- package/dist/index.js +423 -259
- package/dist/index.js.map +4 -4
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -21966,12 +21966,12 @@ var require_src2 = __commonJS({
|
|
|
21966
21966
|
function check2(path24, isFile, isDirectory) {
|
|
21967
21967
|
log2(`checking %s`, path24);
|
|
21968
21968
|
try {
|
|
21969
|
-
const
|
|
21970
|
-
if (
|
|
21969
|
+
const stat2 = fs_1.statSync(path24);
|
|
21970
|
+
if (stat2.isFile() && isFile) {
|
|
21971
21971
|
log2(`[OK] path represents a file`);
|
|
21972
21972
|
return true;
|
|
21973
21973
|
}
|
|
21974
|
-
if (
|
|
21974
|
+
if (stat2.isDirectory() && isDirectory) {
|
|
21975
21975
|
log2(`[OK] path represents a directory`);
|
|
21976
21976
|
return true;
|
|
21977
21977
|
}
|
|
@@ -22061,14 +22061,30 @@ var import_websocket = __toESM(require_websocket(), 1);
|
|
|
22061
22061
|
var import_websocket_server = __toESM(require_websocket_server(), 1);
|
|
22062
22062
|
var wrapper_default = import_websocket.default;
|
|
22063
22063
|
|
|
22064
|
+
// src/net/apply-cli-outbound-network-prefs.ts
|
|
22065
|
+
import dns from "node:dns";
|
|
22066
|
+
var applied = false;
|
|
22067
|
+
function applyCliOutboundNetworkPreferences() {
|
|
22068
|
+
if (applied) return;
|
|
22069
|
+
applied = true;
|
|
22070
|
+
try {
|
|
22071
|
+
dns.setDefaultResultOrder("ipv4first");
|
|
22072
|
+
} catch {
|
|
22073
|
+
}
|
|
22074
|
+
}
|
|
22075
|
+
|
|
22064
22076
|
// src/bridge/connection/create-ws-bridge.ts
|
|
22065
22077
|
var BRIDGE_AUTH_ERROR_HEADER = "x-bridge-auth-error";
|
|
22066
22078
|
var BRIDGE_AUTH_ERROR_TOKEN_INVALID = "token_invalid";
|
|
22067
22079
|
function createWsBridge(options) {
|
|
22068
22080
|
const { url: url2, onMessage, onOpen, onClose, onError: onError2, onAuthInvalid, clientPingIntervalMs } = options;
|
|
22069
|
-
|
|
22081
|
+
applyCliOutboundNetworkPreferences();
|
|
22082
|
+
const wsOptions = {
|
|
22083
|
+
perMessageDeflate: false,
|
|
22084
|
+
family: 4
|
|
22085
|
+
};
|
|
22070
22086
|
if (url2.startsWith("wss://")) {
|
|
22071
|
-
wsOptions.agent = new https.Agent({ rejectUnauthorized: false });
|
|
22087
|
+
wsOptions.agent = new https.Agent({ rejectUnauthorized: false, family: 4 });
|
|
22072
22088
|
}
|
|
22073
22089
|
const ws = new wrapper_default(url2, wsOptions);
|
|
22074
22090
|
let clientPingTimer = null;
|
|
@@ -22132,10 +22148,58 @@ function sendWsMessage(ws, payload) {
|
|
|
22132
22148
|
}
|
|
22133
22149
|
}
|
|
22134
22150
|
|
|
22135
|
-
// src/acp/clients/acp-client.ts
|
|
22151
|
+
// src/acp/clients/sdk-stdio-acp-client.ts
|
|
22136
22152
|
import { spawn } from "node:child_process";
|
|
22153
|
+
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
22154
|
+
import { dirname } from "node:path";
|
|
22137
22155
|
import { Readable, Writable } from "node:stream";
|
|
22138
22156
|
|
|
22157
|
+
// src/files/diff/unified-diff.ts
|
|
22158
|
+
function computeLineDiff(oldText, newText) {
|
|
22159
|
+
const oldLines = oldText.split("\n");
|
|
22160
|
+
const newLines = newText.split("\n");
|
|
22161
|
+
const m = oldLines.length;
|
|
22162
|
+
const n = newLines.length;
|
|
22163
|
+
const dp = Array(m + 1);
|
|
22164
|
+
for (let i2 = 0; i2 <= m; i2++) dp[i2] = Array(n + 1).fill(0);
|
|
22165
|
+
for (let i2 = 1; i2 <= m; i2++) {
|
|
22166
|
+
for (let j2 = 1; j2 <= n; j2++) {
|
|
22167
|
+
if (oldLines[i2 - 1] === newLines[j2 - 1]) {
|
|
22168
|
+
dp[i2][j2] = dp[i2 - 1][j2 - 1] + 1;
|
|
22169
|
+
} else {
|
|
22170
|
+
dp[i2][j2] = Math.max(dp[i2 - 1][j2], dp[i2][j2 - 1]);
|
|
22171
|
+
}
|
|
22172
|
+
}
|
|
22173
|
+
}
|
|
22174
|
+
const result = [];
|
|
22175
|
+
let i = m;
|
|
22176
|
+
let j = n;
|
|
22177
|
+
while (i > 0 || j > 0) {
|
|
22178
|
+
if (i > 0 && j > 0 && oldLines[i - 1] === newLines[j - 1]) {
|
|
22179
|
+
result.unshift({ type: "context", line: oldLines[i - 1] });
|
|
22180
|
+
i--;
|
|
22181
|
+
j--;
|
|
22182
|
+
} else if (j > 0 && (i === 0 || dp[i][j - 1] >= dp[i - 1][j])) {
|
|
22183
|
+
result.unshift({ type: "add", line: newLines[j - 1] });
|
|
22184
|
+
j--;
|
|
22185
|
+
} else {
|
|
22186
|
+
result.unshift({ type: "remove", line: oldLines[i - 1] });
|
|
22187
|
+
i--;
|
|
22188
|
+
}
|
|
22189
|
+
}
|
|
22190
|
+
return result;
|
|
22191
|
+
}
|
|
22192
|
+
function editSnippetToUnifiedDiff(filePath, oldText, newText) {
|
|
22193
|
+
const lines = computeLineDiff(oldText, newText);
|
|
22194
|
+
const out = [`--- ${filePath}`, `+++ ${filePath}`];
|
|
22195
|
+
for (const d of lines) {
|
|
22196
|
+
if (d.type === "add") out.push(`+${d.line}`);
|
|
22197
|
+
else if (d.type === "remove") out.push(`-${d.line}`);
|
|
22198
|
+
else out.push(` ${d.line}`);
|
|
22199
|
+
}
|
|
22200
|
+
return out.join("\n");
|
|
22201
|
+
}
|
|
22202
|
+
|
|
22139
22203
|
// src/files/cwd/bridge-workspace-directory.ts
|
|
22140
22204
|
import * as path from "node:path";
|
|
22141
22205
|
var bridgeWorkspaceDirectory = null;
|
|
@@ -22146,7 +22210,25 @@ function getBridgeWorkspaceDirectory() {
|
|
|
22146
22210
|
return bridgeWorkspaceDirectory;
|
|
22147
22211
|
}
|
|
22148
22212
|
|
|
22149
|
-
// src/acp/
|
|
22213
|
+
// src/acp/safe-fs-path.ts
|
|
22214
|
+
import * as path2 from "node:path";
|
|
22215
|
+
function resolveSafePathUnderCwd(cwd, filePath) {
|
|
22216
|
+
const trimmed2 = filePath.trim();
|
|
22217
|
+
if (!trimmed2) return null;
|
|
22218
|
+
const normalizedCwd = path2.resolve(cwd);
|
|
22219
|
+
const resolved = path2.isAbsolute(trimmed2) ? path2.normalize(trimmed2) : path2.resolve(normalizedCwd, trimmed2);
|
|
22220
|
+
const rel = path2.relative(normalizedCwd, resolved);
|
|
22221
|
+
if (rel.startsWith("..") || path2.isAbsolute(rel)) return null;
|
|
22222
|
+
return resolved;
|
|
22223
|
+
}
|
|
22224
|
+
function toDisplayPathRelativeToCwd(cwd, absolutePath) {
|
|
22225
|
+
const normalizedCwd = path2.resolve(cwd);
|
|
22226
|
+
const rel = path2.relative(normalizedCwd, path2.resolve(absolutePath));
|
|
22227
|
+
if (!rel || rel === "") return path2.basename(absolutePath);
|
|
22228
|
+
return rel.split(path2.sep).join("/");
|
|
22229
|
+
}
|
|
22230
|
+
|
|
22231
|
+
// src/acp/clients/sdk-stdio-acp-client.ts
|
|
22150
22232
|
function formatSpawnError(err, command) {
|
|
22151
22233
|
if (err.code === "ENOENT") {
|
|
22152
22234
|
return `Command "${command}" not found. Install the agent (e.g. Cursor CLI) or add it to PATH.`;
|
|
@@ -22161,9 +22243,26 @@ function toErrorMessage(err) {
|
|
|
22161
22243
|
if (err != null && typeof err === "object") return JSON.stringify(err);
|
|
22162
22244
|
return String(err);
|
|
22163
22245
|
}
|
|
22164
|
-
|
|
22165
|
-
|
|
22166
|
-
const
|
|
22246
|
+
function sliceFileContentRange(content, line, limit) {
|
|
22247
|
+
if (line == null && limit == null) return content;
|
|
22248
|
+
const lines = content.split("\n");
|
|
22249
|
+
const start = line != null && line > 0 ? line - 1 : 0;
|
|
22250
|
+
const end = limit != null && limit > 0 ? start + limit : lines.length;
|
|
22251
|
+
return lines.slice(start, end).join("\n");
|
|
22252
|
+
}
|
|
22253
|
+
function bridgePayloadFromSdkSessionNotification(params) {
|
|
22254
|
+
return { sessionId: params.sessionId, ...params.update };
|
|
22255
|
+
}
|
|
22256
|
+
async function createSdkStdioAcpClient(options) {
|
|
22257
|
+
const { ClientSideConnection: ClientSideConnection2, ndJsonStream: ndJsonStream2, PROTOCOL_VERSION: PROTOCOL_VERSION2 } = await Promise.resolve().then(() => (init_acp(), acp_exports));
|
|
22258
|
+
const {
|
|
22259
|
+
command,
|
|
22260
|
+
cwd = getBridgeWorkspaceDirectory(),
|
|
22261
|
+
onSessionUpdate,
|
|
22262
|
+
onFileChange,
|
|
22263
|
+
killSubprocessAfterCancelMs,
|
|
22264
|
+
onAgentSubprocessExit
|
|
22265
|
+
} = options;
|
|
22167
22266
|
const isWindows = process.platform === "win32";
|
|
22168
22267
|
const child = spawn(command[0], command.slice(1), {
|
|
22169
22268
|
cwd,
|
|
@@ -22171,6 +22270,9 @@ async function createAcpClient(options) {
|
|
|
22171
22270
|
env: process.env,
|
|
22172
22271
|
shell: isWindows
|
|
22173
22272
|
});
|
|
22273
|
+
child.once("close", (code, signal) => {
|
|
22274
|
+
onAgentSubprocessExit?.({ code, signal });
|
|
22275
|
+
});
|
|
22174
22276
|
return new Promise((resolve14, reject) => {
|
|
22175
22277
|
child.on("error", (err) => {
|
|
22176
22278
|
child.kill();
|
|
@@ -22182,11 +22284,43 @@ async function createAcpClient(options) {
|
|
|
22182
22284
|
const readable = Readable.toWeb(child.stdout);
|
|
22183
22285
|
const stream = ndJsonStream2(writable, readable);
|
|
22184
22286
|
const client = (_agent) => ({
|
|
22185
|
-
async requestPermission(
|
|
22186
|
-
|
|
22287
|
+
async requestPermission(params) {
|
|
22288
|
+
const opt = params?.options?.[0];
|
|
22289
|
+
if (opt && typeof opt.optionId === "string") {
|
|
22290
|
+
return { outcome: { outcome: "selected", optionId: opt.optionId } };
|
|
22291
|
+
}
|
|
22292
|
+
return { outcome: { outcome: "cancelled" } };
|
|
22293
|
+
},
|
|
22294
|
+
async readTextFile(params) {
|
|
22295
|
+
const abs = resolveSafePathUnderCwd(cwd, params.path);
|
|
22296
|
+
if (!abs) throw new Error("Invalid or disallowed path");
|
|
22297
|
+
try {
|
|
22298
|
+
let content = readFileSync(abs, "utf8");
|
|
22299
|
+
content = sliceFileContentRange(content, params.line, params.limit);
|
|
22300
|
+
return { content };
|
|
22301
|
+
} catch (e) {
|
|
22302
|
+
if (e.code === "ENOENT") return { content: "" };
|
|
22303
|
+
throw e;
|
|
22304
|
+
}
|
|
22305
|
+
},
|
|
22306
|
+
async writeTextFile(params) {
|
|
22307
|
+
const abs = resolveSafePathUnderCwd(cwd, params.path);
|
|
22308
|
+
if (!abs) throw new Error("Invalid or disallowed path");
|
|
22309
|
+
let oldText = "";
|
|
22310
|
+
try {
|
|
22311
|
+
oldText = readFileSync(abs, "utf8");
|
|
22312
|
+
} catch (e) {
|
|
22313
|
+
if (e.code !== "ENOENT") throw e;
|
|
22314
|
+
}
|
|
22315
|
+
mkdirSync(dirname(abs), { recursive: true });
|
|
22316
|
+
writeFileSync(abs, params.content, "utf8");
|
|
22317
|
+
const displayPath = toDisplayPathRelativeToCwd(cwd, abs);
|
|
22318
|
+
const patchContent = editSnippetToUnifiedDiff(displayPath, oldText, params.content);
|
|
22319
|
+
onFileChange?.({ path: displayPath, oldText, newText: params.content, patchContent });
|
|
22320
|
+
return {};
|
|
22187
22321
|
},
|
|
22188
22322
|
async sessionUpdate(params) {
|
|
22189
|
-
onSessionUpdate?.(params);
|
|
22323
|
+
onSessionUpdate?.(bridgePayloadFromSdkSessionNotification(params));
|
|
22190
22324
|
}
|
|
22191
22325
|
});
|
|
22192
22326
|
const connection = new ClientSideConnection2(client, stream);
|
|
@@ -22194,11 +22328,13 @@ async function createAcpClient(options) {
|
|
|
22194
22328
|
child.kill();
|
|
22195
22329
|
});
|
|
22196
22330
|
await connection.initialize({
|
|
22197
|
-
protocolVersion:
|
|
22198
|
-
|
|
22331
|
+
protocolVersion: PROTOCOL_VERSION2,
|
|
22332
|
+
clientCapabilities: {
|
|
22333
|
+
fs: { readTextFile: true, writeTextFile: true }
|
|
22334
|
+
},
|
|
22199
22335
|
clientInfo: { name: "buildautomaton-cli", version: "0.1.0" }
|
|
22200
22336
|
});
|
|
22201
|
-
const newSessionRes = await connection.newSession({
|
|
22337
|
+
const newSessionRes = await connection.newSession({ cwd, mcpServers: [] });
|
|
22202
22338
|
const sessionId = newSessionRes.sessionId;
|
|
22203
22339
|
resolve14({
|
|
22204
22340
|
sessionId,
|
|
@@ -22206,7 +22342,7 @@ async function createAcpClient(options) {
|
|
|
22206
22342
|
try {
|
|
22207
22343
|
const response = await connection.prompt({
|
|
22208
22344
|
sessionId,
|
|
22209
|
-
prompt: { type: "text", text: prompt }
|
|
22345
|
+
prompt: [{ type: "text", text: prompt }]
|
|
22210
22346
|
});
|
|
22211
22347
|
const r = response;
|
|
22212
22348
|
const cancelled = (r?.stopReason ?? "").toLowerCase() === "cancelled";
|
|
@@ -22224,9 +22360,17 @@ async function createAcpClient(options) {
|
|
|
22224
22360
|
}
|
|
22225
22361
|
},
|
|
22226
22362
|
async cancel() {
|
|
22227
|
-
|
|
22228
|
-
|
|
22229
|
-
|
|
22363
|
+
try {
|
|
22364
|
+
await connection.cancel({ sessionId });
|
|
22365
|
+
} catch {
|
|
22366
|
+
}
|
|
22367
|
+
if (killSubprocessAfterCancelMs != null && killSubprocessAfterCancelMs >= 0) {
|
|
22368
|
+
const t = setTimeout(() => {
|
|
22369
|
+
if (child.exitCode == null && child.signalCode == null) {
|
|
22370
|
+
child.kill("SIGTERM");
|
|
22371
|
+
}
|
|
22372
|
+
}, killSubprocessAfterCancelMs);
|
|
22373
|
+
t.unref?.();
|
|
22230
22374
|
}
|
|
22231
22375
|
},
|
|
22232
22376
|
resolveRequest() {
|
|
@@ -22540,11 +22684,11 @@ function logImmediate(line) {
|
|
|
22540
22684
|
|
|
22541
22685
|
// src/config.ts
|
|
22542
22686
|
import fs from "node:fs";
|
|
22543
|
-
import
|
|
22687
|
+
import path3 from "node:path";
|
|
22544
22688
|
import os from "node:os";
|
|
22545
22689
|
function getConfigPath() {
|
|
22546
|
-
const dir =
|
|
22547
|
-
return
|
|
22690
|
+
const dir = path3.join(os.homedir(), ".buildautomaton");
|
|
22691
|
+
return path3.join(dir, "config.json");
|
|
22548
22692
|
}
|
|
22549
22693
|
function normalizeApiUrl(url2) {
|
|
22550
22694
|
return url2.replace(/\/$/, "");
|
|
@@ -22560,7 +22704,7 @@ function readRawConfig() {
|
|
|
22560
22704
|
}
|
|
22561
22705
|
function writeConfigForApi(apiUrl, auth) {
|
|
22562
22706
|
const p = getConfigPath();
|
|
22563
|
-
const dir =
|
|
22707
|
+
const dir = path3.dirname(p);
|
|
22564
22708
|
const key = normalizeApiUrl(apiUrl);
|
|
22565
22709
|
const prev = readRawConfig() ?? {};
|
|
22566
22710
|
const servers = { ...prev.servers ?? {}, [key]: { ...auth } };
|
|
@@ -22755,7 +22899,7 @@ function beginMainBridgeDeferredDisconnect(state, code, reason, log2, willReconn
|
|
|
22755
22899
|
});
|
|
22756
22900
|
}
|
|
22757
22901
|
function clearMainBridgeReconnectQuietOnOpen(state, log2) {
|
|
22758
|
-
clearReconnectQuietOnSuccessfulConnection(state.mainQuiet, log2, "
|
|
22902
|
+
clearReconnectQuietOnSuccessfulConnection(state.mainQuiet, log2, "Bridge connection restored.");
|
|
22759
22903
|
}
|
|
22760
22904
|
function scheduleMainBridgeReconnect(state, connect, log2) {
|
|
22761
22905
|
if (state.closedByUser || state.currentWs != null) return;
|
|
@@ -22792,7 +22936,7 @@ function clearFirehoseReconnectQuietOnOpen(ctx, log2) {
|
|
|
22792
22936
|
clearReconnectQuietOnSuccessfulConnection(
|
|
22793
22937
|
ctx.firehoseQuiet,
|
|
22794
22938
|
log2,
|
|
22795
|
-
|
|
22939
|
+
"Preview tunnel restored (local HTTP proxy and dev logs)."
|
|
22796
22940
|
);
|
|
22797
22941
|
}
|
|
22798
22942
|
|
|
@@ -22943,7 +23087,7 @@ function buildBridgeUrl(apiUrl, workspaceId, authToken) {
|
|
|
22943
23087
|
|
|
22944
23088
|
// src/git/discover-repos.ts
|
|
22945
23089
|
import * as fs2 from "node:fs";
|
|
22946
|
-
import * as
|
|
23090
|
+
import * as path4 from "node:path";
|
|
22947
23091
|
|
|
22948
23092
|
// ../../node_modules/.pnpm/simple-git@3.32.3/node_modules/simple-git/dist/esm/index.js
|
|
22949
23093
|
var import_file_exists = __toESM(require_dist(), 1);
|
|
@@ -22952,7 +23096,7 @@ var import_promise_deferred = __toESM(require_dist2(), 1);
|
|
|
22952
23096
|
var import_promise_deferred2 = __toESM(require_dist2(), 1);
|
|
22953
23097
|
import { Buffer as Buffer2 } from "node:buffer";
|
|
22954
23098
|
import { spawn as spawn3 } from "child_process";
|
|
22955
|
-
import { normalize } from "node:path";
|
|
23099
|
+
import { normalize as normalize2 } from "node:path";
|
|
22956
23100
|
import { EventEmitter } from "node:events";
|
|
22957
23101
|
var __defProp2 = Object.defineProperty;
|
|
22958
23102
|
var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
|
|
@@ -26255,7 +26399,7 @@ var init_branch = __esm2({
|
|
|
26255
26399
|
});
|
|
26256
26400
|
function toPath(input) {
|
|
26257
26401
|
const path24 = input.trim().replace(/^["']|["']$/g, "");
|
|
26258
|
-
return path24 &&
|
|
26402
|
+
return path24 && normalize2(path24);
|
|
26259
26403
|
}
|
|
26260
26404
|
var parseCheckIgnore;
|
|
26261
26405
|
var init_CheckIgnore = __esm2({
|
|
@@ -27530,7 +27674,7 @@ async function isGitRepoDirectory(dirPath) {
|
|
|
27530
27674
|
// src/git/discover-repos.ts
|
|
27531
27675
|
async function discoverGitRepos(cwd = getBridgeWorkspaceDirectory()) {
|
|
27532
27676
|
const result = [];
|
|
27533
|
-
const cwdResolved =
|
|
27677
|
+
const cwdResolved = path4.resolve(cwd);
|
|
27534
27678
|
if (await isGitRepoDirectory(cwdResolved)) {
|
|
27535
27679
|
const remoteUrl = await getRemoteOriginUrl(cwdResolved);
|
|
27536
27680
|
result.push({ absolutePath: cwdResolved, remoteUrl });
|
|
@@ -27543,7 +27687,7 @@ async function discoverGitRepos(cwd = getBridgeWorkspaceDirectory()) {
|
|
|
27543
27687
|
}
|
|
27544
27688
|
for (const ent of entries) {
|
|
27545
27689
|
if (!ent.isDirectory()) continue;
|
|
27546
|
-
const childPath =
|
|
27690
|
+
const childPath = path4.join(cwdResolved, ent.name);
|
|
27547
27691
|
if (await isGitRepoDirectory(childPath)) {
|
|
27548
27692
|
const remoteUrl = await getRemoteOriginUrl(childPath);
|
|
27549
27693
|
result.push({ absolutePath: childPath, remoteUrl });
|
|
@@ -27552,11 +27696,11 @@ async function discoverGitRepos(cwd = getBridgeWorkspaceDirectory()) {
|
|
|
27552
27696
|
return result;
|
|
27553
27697
|
}
|
|
27554
27698
|
async function discoverGitReposUnderRoot(rootAbs) {
|
|
27555
|
-
const root =
|
|
27699
|
+
const root = path4.resolve(rootAbs);
|
|
27556
27700
|
const roots = [];
|
|
27557
27701
|
async function walk(dir) {
|
|
27558
27702
|
if (await isGitRepoDirectory(dir)) {
|
|
27559
|
-
roots.push(
|
|
27703
|
+
roots.push(path4.resolve(dir));
|
|
27560
27704
|
return;
|
|
27561
27705
|
}
|
|
27562
27706
|
let entries;
|
|
@@ -27567,7 +27711,7 @@ async function discoverGitReposUnderRoot(rootAbs) {
|
|
|
27567
27711
|
}
|
|
27568
27712
|
for (const ent of entries) {
|
|
27569
27713
|
if (!ent.isDirectory() || ent.name === ".git") continue;
|
|
27570
|
-
await walk(
|
|
27714
|
+
await walk(path4.join(dir, ent.name));
|
|
27571
27715
|
}
|
|
27572
27716
|
}
|
|
27573
27717
|
await walk(root);
|
|
@@ -27603,34 +27747,31 @@ function reportGitRepos(getWs, log2) {
|
|
|
27603
27747
|
|
|
27604
27748
|
// src/bridge/connection/close-bridge-connection.ts
|
|
27605
27749
|
async function closeBridgeConnection(state, acpManager, devServerManager, log2) {
|
|
27606
|
-
log2
|
|
27750
|
+
const say = log2 ?? logImmediate;
|
|
27751
|
+
say("Cleaning up connections\u2026");
|
|
27607
27752
|
await new Promise((resolve14) => setImmediate(resolve14));
|
|
27608
|
-
if (devServerManager) {
|
|
27609
|
-
log2?.("Requesting dev server processes to stop\u2026");
|
|
27610
|
-
await devServerManager.shutdownAllGraceful();
|
|
27611
|
-
}
|
|
27612
27753
|
state.closedByUser = true;
|
|
27613
27754
|
clearReconnectQuietTimer(state.mainQuiet);
|
|
27614
27755
|
clearReconnectQuietTimer(state.firehoseQuiet);
|
|
27615
27756
|
if (state.reconnectTimeout != null) {
|
|
27616
|
-
|
|
27757
|
+
say("Cancelling bridge reconnect timer\u2026");
|
|
27617
27758
|
clearTimeout(state.reconnectTimeout);
|
|
27618
27759
|
state.reconnectTimeout = null;
|
|
27619
27760
|
}
|
|
27620
27761
|
if (state.firehoseReconnectTimeout != null) {
|
|
27621
|
-
|
|
27762
|
+
say("Cancelling preview tunnel reconnect timer\u2026");
|
|
27622
27763
|
clearTimeout(state.firehoseReconnectTimeout);
|
|
27623
27764
|
state.firehoseReconnectTimeout = null;
|
|
27624
27765
|
}
|
|
27625
27766
|
if (state.firehoseHandle) {
|
|
27626
|
-
|
|
27767
|
+
say("Closing preview tunnel (local HTTP proxy and dev logs)\u2026");
|
|
27627
27768
|
state.firehoseHandle.close();
|
|
27628
27769
|
state.firehoseHandle = null;
|
|
27629
27770
|
}
|
|
27630
|
-
|
|
27771
|
+
say("Disconnecting local agent\u2026");
|
|
27631
27772
|
acpManager.disconnect();
|
|
27632
27773
|
if (state.currentWs) {
|
|
27633
|
-
|
|
27774
|
+
say("Closing bridge connection to the cloud\u2026");
|
|
27634
27775
|
state.currentWs.removeAllListeners();
|
|
27635
27776
|
const wsState = state.currentWs.readyState;
|
|
27636
27777
|
if (wsState === 1 || wsState === 2) {
|
|
@@ -27643,22 +27784,27 @@ async function closeBridgeConnection(state, acpManager, devServerManager, log2)
|
|
|
27643
27784
|
}
|
|
27644
27785
|
state.currentWs = null;
|
|
27645
27786
|
}
|
|
27787
|
+
if (devServerManager) {
|
|
27788
|
+
say("Stopping local dev server processes\u2026");
|
|
27789
|
+
await devServerManager.shutdownAllGraceful();
|
|
27790
|
+
}
|
|
27791
|
+
say("Shutdown complete.");
|
|
27646
27792
|
}
|
|
27647
27793
|
|
|
27648
27794
|
// src/git/session-git-queue.ts
|
|
27649
27795
|
import { execFile as execFile2 } from "node:child_process";
|
|
27796
|
+
import { readFile, stat } from "node:fs/promises";
|
|
27650
27797
|
import { promisify as promisify2 } from "node:util";
|
|
27651
|
-
import * as
|
|
27652
|
-
import * as path5 from "node:path";
|
|
27798
|
+
import * as path6 from "node:path";
|
|
27653
27799
|
|
|
27654
27800
|
// src/git/pre-turn-snapshot.ts
|
|
27655
27801
|
import * as fs3 from "node:fs";
|
|
27656
|
-
import * as
|
|
27802
|
+
import * as path5 from "node:path";
|
|
27657
27803
|
import { execFile } from "node:child_process";
|
|
27658
27804
|
import { promisify } from "node:util";
|
|
27659
27805
|
var execFileAsync = promisify(execFile);
|
|
27660
27806
|
function snapshotsDirForCwd(agentCwd) {
|
|
27661
|
-
return
|
|
27807
|
+
return path5.join(agentCwd, ".buildautomaton", "snapshots");
|
|
27662
27808
|
}
|
|
27663
27809
|
async function gitStashCreate(repoRoot, log2) {
|
|
27664
27810
|
try {
|
|
@@ -27687,7 +27833,7 @@ async function gitRun(repoRoot, args, log2, label) {
|
|
|
27687
27833
|
async function resolveSnapshotRepoRoots(options) {
|
|
27688
27834
|
const { worktreePaths, fallbackCwd, log: log2 } = options;
|
|
27689
27835
|
if (worktreePaths?.length) {
|
|
27690
|
-
const uniq = [...new Set(worktreePaths.map((p) =>
|
|
27836
|
+
const uniq = [...new Set(worktreePaths.map((p) => path5.resolve(p)))];
|
|
27691
27837
|
return uniq;
|
|
27692
27838
|
}
|
|
27693
27839
|
try {
|
|
@@ -27719,7 +27865,7 @@ async function capturePreTurnSnapshot(options) {
|
|
|
27719
27865
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
27720
27866
|
repos
|
|
27721
27867
|
};
|
|
27722
|
-
const filePath =
|
|
27868
|
+
const filePath = path5.join(dir, `${runId}.json`);
|
|
27723
27869
|
try {
|
|
27724
27870
|
fs3.writeFileSync(filePath, JSON.stringify(payload, null, 2), "utf8");
|
|
27725
27871
|
} catch (e) {
|
|
@@ -27757,17 +27903,17 @@ async function applyPreTurnSnapshot(filePath, log2) {
|
|
|
27757
27903
|
return { ok: true };
|
|
27758
27904
|
}
|
|
27759
27905
|
function snapshotFilePath(agentCwd, runId) {
|
|
27760
|
-
return
|
|
27906
|
+
return path5.join(snapshotsDirForCwd(agentCwd), `${runId}.json`);
|
|
27761
27907
|
}
|
|
27762
27908
|
|
|
27763
27909
|
// src/git/session-git-queue.ts
|
|
27764
27910
|
var execFileAsync2 = promisify2(execFile2);
|
|
27765
27911
|
var MAX_FULL_FILE_TEXT_BYTES = 512 * 1024;
|
|
27766
|
-
function readWorkspaceFileAsUtf8(absPath) {
|
|
27912
|
+
async function readWorkspaceFileAsUtf8(absPath) {
|
|
27767
27913
|
try {
|
|
27768
|
-
const st =
|
|
27914
|
+
const st = await stat(absPath);
|
|
27769
27915
|
if (!st.isFile() || st.size > MAX_FULL_FILE_TEXT_BYTES) return void 0;
|
|
27770
|
-
return
|
|
27916
|
+
return await readFile(absPath, "utf8");
|
|
27771
27917
|
} catch {
|
|
27772
27918
|
return void 0;
|
|
27773
27919
|
}
|
|
@@ -27777,7 +27923,7 @@ async function collectTurnGitDiffFromPreTurnSnapshot(options) {
|
|
|
27777
27923
|
const filePath = snapshotFilePath(agentCwd, runId);
|
|
27778
27924
|
let data;
|
|
27779
27925
|
try {
|
|
27780
|
-
const raw =
|
|
27926
|
+
const raw = await readFile(filePath, "utf8");
|
|
27781
27927
|
data = JSON.parse(raw);
|
|
27782
27928
|
} catch (e) {
|
|
27783
27929
|
log2(
|
|
@@ -27806,7 +27952,7 @@ async function collectTurnGitDiffFromPreTurnSnapshot(options) {
|
|
|
27806
27952
|
continue;
|
|
27807
27953
|
}
|
|
27808
27954
|
const lines = namesRaw.split("\n").map((l) => l.trim()).filter(Boolean);
|
|
27809
|
-
const slug =
|
|
27955
|
+
const slug = path6.basename(repo.path).replace(/[^\w.-]+/g, "_") || "repo";
|
|
27810
27956
|
for (const rel of lines) {
|
|
27811
27957
|
if (rel.includes("..")) continue;
|
|
27812
27958
|
try {
|
|
@@ -27820,8 +27966,8 @@ async function collectTurnGitDiffFromPreTurnSnapshot(options) {
|
|
|
27820
27966
|
);
|
|
27821
27967
|
if (!patchContent.trim()) continue;
|
|
27822
27968
|
const displayPath = multiRepo ? `${slug}/${rel}` : rel;
|
|
27823
|
-
const absFile =
|
|
27824
|
-
const newText = readWorkspaceFileAsUtf8(absFile);
|
|
27969
|
+
const absFile = path6.join(repo.path, rel);
|
|
27970
|
+
const newText = await readWorkspaceFileAsUtf8(absFile);
|
|
27825
27971
|
sendSessionUpdate({
|
|
27826
27972
|
type: "session_file_change",
|
|
27827
27973
|
sessionId,
|
|
@@ -27876,7 +28022,6 @@ async function sendPromptToAgent(options) {
|
|
|
27876
28022
|
} catch (err) {
|
|
27877
28023
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
27878
28024
|
log2(`[Agent] Send failed: ${errMsg}`);
|
|
27879
|
-
if (err instanceof Error && err.stack) log2(`[Agent] ${err.stack}`);
|
|
27880
28025
|
sendResult({
|
|
27881
28026
|
type: "prompt_result",
|
|
27882
28027
|
id: promptId,
|
|
@@ -27889,7 +28034,7 @@ async function sendPromptToAgent(options) {
|
|
|
27889
28034
|
}
|
|
27890
28035
|
|
|
27891
28036
|
// src/acp/ensure-acp-client.ts
|
|
27892
|
-
import * as
|
|
28037
|
+
import * as fs4 from "node:fs";
|
|
27893
28038
|
import * as path9 from "node:path";
|
|
27894
28039
|
|
|
27895
28040
|
// src/error-message.ts
|
|
@@ -27908,81 +28053,38 @@ function isCodexAcpCommand(command) {
|
|
|
27908
28053
|
const i = command.indexOf("@zed-industries/codex-acp");
|
|
27909
28054
|
return i >= 0 && (i === 0 || command[i - 1] === "npx" || command[i - 1] === "bunx");
|
|
27910
28055
|
}
|
|
28056
|
+
function buildCodexAcpSpawnCommand(base, _sessionMode) {
|
|
28057
|
+
return [...base];
|
|
28058
|
+
}
|
|
27911
28059
|
async function createCodexAcpClient(options) {
|
|
27912
|
-
const
|
|
27913
|
-
|
|
28060
|
+
const base = options.command?.length && options.command.some((a) => a.includes("codex-acp")) ? options.command : [...DEFAULT_CODEX_ACP_COMMAND];
|
|
28061
|
+
const command = buildCodexAcpSpawnCommand(base, options.sessionMode);
|
|
28062
|
+
return createSdkStdioAcpClient({ ...options, command });
|
|
28063
|
+
}
|
|
28064
|
+
|
|
28065
|
+
// src/acp/clients/claude-code-acp-client.ts
|
|
28066
|
+
function buildClaudeCodeAcpSpawnCommand(base, sessionMode) {
|
|
28067
|
+
if (!sessionMode) return [...base];
|
|
28068
|
+
const m = sessionMode.trim();
|
|
28069
|
+
if (m === "plan") return [...base, "--permission-mode", "plan"];
|
|
28070
|
+
return [...base];
|
|
28071
|
+
}
|
|
28072
|
+
async function createClaudeCodeAcpClient(options) {
|
|
28073
|
+
const command = buildClaudeCodeAcpSpawnCommand(options.command, options.sessionMode);
|
|
28074
|
+
return createSdkStdioAcpClient({
|
|
28075
|
+
...options,
|
|
28076
|
+
command,
|
|
28077
|
+
/** Claude-based agents sometimes ignore `session/cancel`; unblocks stop / stuck prompt. */
|
|
28078
|
+
killSubprocessAfterCancelMs: options.killSubprocessAfterCancelMs ?? 1e3
|
|
28079
|
+
});
|
|
27914
28080
|
}
|
|
27915
28081
|
|
|
27916
28082
|
// src/acp/clients/cursor-acp-client.ts
|
|
27917
|
-
import { readFileSync as readFileSync3, writeFileSync as
|
|
27918
|
-
import { dirname } from "node:path";
|
|
28083
|
+
import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3 } from "node:fs";
|
|
28084
|
+
import { dirname as dirname2 } from "node:path";
|
|
27919
28085
|
import { spawn as spawn4 } from "node:child_process";
|
|
27920
28086
|
import * as readline from "node:readline";
|
|
27921
28087
|
|
|
27922
|
-
// src/acp/safe-fs-path.ts
|
|
27923
|
-
import * as path6 from "node:path";
|
|
27924
|
-
function resolveSafePathUnderCwd(cwd, filePath) {
|
|
27925
|
-
const trimmed2 = filePath.trim();
|
|
27926
|
-
if (!trimmed2) return null;
|
|
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;
|
|
27931
|
-
return resolved;
|
|
27932
|
-
}
|
|
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("/");
|
|
27938
|
-
}
|
|
27939
|
-
|
|
27940
|
-
// src/files/diff/unified-diff.ts
|
|
27941
|
-
function computeLineDiff(oldText, newText) {
|
|
27942
|
-
const oldLines = oldText.split("\n");
|
|
27943
|
-
const newLines = newText.split("\n");
|
|
27944
|
-
const m = oldLines.length;
|
|
27945
|
-
const n = newLines.length;
|
|
27946
|
-
const dp = Array(m + 1);
|
|
27947
|
-
for (let i2 = 0; i2 <= m; i2++) dp[i2] = Array(n + 1).fill(0);
|
|
27948
|
-
for (let i2 = 1; i2 <= m; i2++) {
|
|
27949
|
-
for (let j2 = 1; j2 <= n; j2++) {
|
|
27950
|
-
if (oldLines[i2 - 1] === newLines[j2 - 1]) {
|
|
27951
|
-
dp[i2][j2] = dp[i2 - 1][j2 - 1] + 1;
|
|
27952
|
-
} else {
|
|
27953
|
-
dp[i2][j2] = Math.max(dp[i2 - 1][j2], dp[i2][j2 - 1]);
|
|
27954
|
-
}
|
|
27955
|
-
}
|
|
27956
|
-
}
|
|
27957
|
-
const result = [];
|
|
27958
|
-
let i = m;
|
|
27959
|
-
let j = n;
|
|
27960
|
-
while (i > 0 || j > 0) {
|
|
27961
|
-
if (i > 0 && j > 0 && oldLines[i - 1] === newLines[j - 1]) {
|
|
27962
|
-
result.unshift({ type: "context", line: oldLines[i - 1] });
|
|
27963
|
-
i--;
|
|
27964
|
-
j--;
|
|
27965
|
-
} else if (j > 0 && (i === 0 || dp[i][j - 1] >= dp[i - 1][j])) {
|
|
27966
|
-
result.unshift({ type: "add", line: newLines[j - 1] });
|
|
27967
|
-
j--;
|
|
27968
|
-
} else {
|
|
27969
|
-
result.unshift({ type: "remove", line: oldLines[i - 1] });
|
|
27970
|
-
i--;
|
|
27971
|
-
}
|
|
27972
|
-
}
|
|
27973
|
-
return result;
|
|
27974
|
-
}
|
|
27975
|
-
function editSnippetToUnifiedDiff(filePath, oldText, newText) {
|
|
27976
|
-
const lines = computeLineDiff(oldText, newText);
|
|
27977
|
-
const out = [`--- ${filePath}`, `+++ ${filePath}`];
|
|
27978
|
-
for (const d of lines) {
|
|
27979
|
-
if (d.type === "add") out.push(`+${d.line}`);
|
|
27980
|
-
else if (d.type === "remove") out.push(`-${d.line}`);
|
|
27981
|
-
else out.push(` ${d.line}`);
|
|
27982
|
-
}
|
|
27983
|
-
return out.join("\n");
|
|
27984
|
-
}
|
|
27985
|
-
|
|
27986
28088
|
// src/acp/format-session-update-kind-for-log.ts
|
|
27987
28089
|
var SESSION_UPDATE_KIND_LABELS = {
|
|
27988
28090
|
tool_call: "Tool call",
|
|
@@ -28020,8 +28122,15 @@ function sliceLinesByRange(content, line, limit) {
|
|
|
28020
28122
|
const end = limit != null && limit > 0 ? start + limit : lines.length;
|
|
28021
28123
|
return lines.slice(start, end).join("\n");
|
|
28022
28124
|
}
|
|
28125
|
+
function buildCursorAcpSpawnCommand(base, sessionMode) {
|
|
28126
|
+
if (!sessionMode) return [...base];
|
|
28127
|
+
const m = sessionMode.trim();
|
|
28128
|
+
if (m !== "ask" && m !== "plan") return [...base];
|
|
28129
|
+
return [...base, "--mode", m];
|
|
28130
|
+
}
|
|
28023
28131
|
async function createCursorAcpClient(options) {
|
|
28024
|
-
const
|
|
28132
|
+
const command = buildCursorAcpSpawnCommand(options.command, options.sessionMode);
|
|
28133
|
+
const { cwd = getBridgeWorkspaceDirectory(), onSessionUpdate, onRequest, onFileChange } = options;
|
|
28025
28134
|
const dbgFs = process.env.BUILDAMATON_DEBUG_ACP_FS === "1";
|
|
28026
28135
|
const isWindows = process.platform === "win32";
|
|
28027
28136
|
const child = spawn4(command[0], command.slice(1), {
|
|
@@ -28153,8 +28262,8 @@ async function createCursorAcpClient(options) {
|
|
|
28153
28262
|
}
|
|
28154
28263
|
}
|
|
28155
28264
|
try {
|
|
28156
|
-
|
|
28157
|
-
|
|
28265
|
+
mkdirSync3(dirname2(abs), { recursive: true });
|
|
28266
|
+
writeFileSync3(abs, newText, "utf8");
|
|
28158
28267
|
} catch (e) {
|
|
28159
28268
|
respondJsonRpcError(id, -32e3, e instanceof Error ? e.message : String(e));
|
|
28160
28269
|
return;
|
|
@@ -28252,8 +28361,20 @@ async function createCursorAcpClient(options) {
|
|
|
28252
28361
|
var AGENT_TYPE_DEFAULT_COMMANDS = {
|
|
28253
28362
|
"cursor-cli": ["agent", "acp"],
|
|
28254
28363
|
"codex-acp": [...DEFAULT_CODEX_ACP_COMMAND],
|
|
28255
|
-
|
|
28364
|
+
/** ACP stdio agent; `@anthropic-ai/claude-code` is the interactive CLI and does not speak ACP on stdout. */
|
|
28365
|
+
"claude-code": ["npx", "--yes", "@agentclientprotocol/claude-agent-acp"]
|
|
28256
28366
|
};
|
|
28367
|
+
var AGENT_TYPE_DISPLAY_NAMES = {
|
|
28368
|
+
"cursor-cli": "Cursor",
|
|
28369
|
+
"codex-acp": "Codex",
|
|
28370
|
+
"claude-code": "Claude Code"
|
|
28371
|
+
};
|
|
28372
|
+
function getAgentTypeDisplayName(agentType) {
|
|
28373
|
+
if (agentType == null || agentType === "") return "Unknown agent";
|
|
28374
|
+
const known = AGENT_TYPE_DISPLAY_NAMES[agentType];
|
|
28375
|
+
if (known) return known;
|
|
28376
|
+
return agentType.split(/[-_]/).filter(Boolean).map((w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase()).join(" ");
|
|
28377
|
+
}
|
|
28257
28378
|
function useCursorAcp(agentType, command) {
|
|
28258
28379
|
if (agentType === "cursor-cli") return true;
|
|
28259
28380
|
return command[0] === "agent" && command[1] === "acp";
|
|
@@ -28266,14 +28387,33 @@ function resolveAgentCommand(preferredAgentType) {
|
|
|
28266
28387
|
if (!preferredAgentType) return null;
|
|
28267
28388
|
const command = AGENT_TYPE_DEFAULT_COMMANDS[preferredAgentType];
|
|
28268
28389
|
if (!command?.length) return null;
|
|
28269
|
-
|
|
28270
|
-
|
|
28271
|
-
|
|
28390
|
+
if (useCursorAcp(preferredAgentType, command)) {
|
|
28391
|
+
return {
|
|
28392
|
+
command,
|
|
28393
|
+
label: preferredAgentType,
|
|
28394
|
+
createClient: createCursorAcpClient,
|
|
28395
|
+
spawnCommandForSession: (sessionMode) => buildCursorAcpSpawnCommand(command, sessionMode)
|
|
28396
|
+
};
|
|
28397
|
+
}
|
|
28398
|
+
if (useCodexAcp(preferredAgentType, command)) {
|
|
28399
|
+
return {
|
|
28400
|
+
command,
|
|
28401
|
+
label: preferredAgentType,
|
|
28402
|
+
createClient: createCodexAcpClient,
|
|
28403
|
+
spawnCommandForSession: (sessionMode) => buildCodexAcpSpawnCommand(command, sessionMode)
|
|
28404
|
+
};
|
|
28405
|
+
}
|
|
28406
|
+
return {
|
|
28407
|
+
command,
|
|
28408
|
+
label: preferredAgentType,
|
|
28409
|
+
createClient: createClaudeCodeAcpClient,
|
|
28410
|
+
spawnCommandForSession: (sessionMode) => buildClaudeCodeAcpSpawnCommand(command, sessionMode)
|
|
28411
|
+
};
|
|
28272
28412
|
}
|
|
28273
28413
|
|
|
28274
28414
|
// src/acp/session-file-change-path-kind.ts
|
|
28275
28415
|
import { execFileSync as execFileSync3 } from "node:child_process";
|
|
28276
|
-
import { existsSync, statSync
|
|
28416
|
+
import { existsSync, statSync } from "node:fs";
|
|
28277
28417
|
|
|
28278
28418
|
// src/git/get-git-repo-root-sync.ts
|
|
28279
28419
|
import { execFileSync } from "node:child_process";
|
|
@@ -28382,7 +28522,7 @@ function getSessionFileChangeDirectoryFlags(cwd, displayPath) {
|
|
|
28382
28522
|
const abs = tryWorkspaceDisplayToAbs(cwd, displayPath);
|
|
28383
28523
|
if (abs && existsSync(abs)) {
|
|
28384
28524
|
try {
|
|
28385
|
-
if (
|
|
28525
|
+
if (statSync(abs).isDirectory()) {
|
|
28386
28526
|
return { isDirectory: true, directoryRemoved: false };
|
|
28387
28527
|
}
|
|
28388
28528
|
return { isDirectory: false, directoryRemoved: false };
|
|
@@ -28928,7 +29068,8 @@ async function ensureAcpClient(options) {
|
|
|
28928
29068
|
state.lastAcpStartError = "No agent type: ensure the app sends agentType on prompts or agent_config for this bridge.";
|
|
28929
29069
|
return null;
|
|
28930
29070
|
}
|
|
28931
|
-
const
|
|
29071
|
+
const fullCmd = resolved.spawnCommandForSession(mode);
|
|
29072
|
+
const agentKey = `${resolved.label}::${fullCmd.join("\0")}`;
|
|
28932
29073
|
if (state.acpHandle && state.acpAgentKey !== agentKey) {
|
|
28933
29074
|
try {
|
|
28934
29075
|
state.acpHandle.disconnect();
|
|
@@ -28942,7 +29083,7 @@ async function ensureAcpClient(options) {
|
|
|
28942
29083
|
if (!state.acpStartPromise) {
|
|
28943
29084
|
let statOk = false;
|
|
28944
29085
|
try {
|
|
28945
|
-
const st =
|
|
29086
|
+
const st = fs4.statSync(targetCwd);
|
|
28946
29087
|
statOk = st.isDirectory();
|
|
28947
29088
|
if (!statOk) {
|
|
28948
29089
|
state.lastAcpStartError = `Agent cwd is not a directory: ${targetCwd}`;
|
|
@@ -28955,8 +29096,6 @@ async function ensureAcpClient(options) {
|
|
|
28955
29096
|
if (!statOk) {
|
|
28956
29097
|
return null;
|
|
28957
29098
|
}
|
|
28958
|
-
const modeFlag = mode && ["ask", "plan"].includes(mode) ? ["--mode", mode] : [];
|
|
28959
|
-
const fullCmd = [...resolved.command, ...modeFlag];
|
|
28960
29099
|
const hooks = buildAcpSessionBridgeHooks({
|
|
28961
29100
|
routing,
|
|
28962
29101
|
getSendSessionUpdate: () => sendSessionUpdate,
|
|
@@ -28964,8 +29103,15 @@ async function ensureAcpClient(options) {
|
|
|
28964
29103
|
log: log2
|
|
28965
29104
|
});
|
|
28966
29105
|
state.acpStartPromise = resolved.createClient({
|
|
28967
|
-
command:
|
|
29106
|
+
command: resolved.command,
|
|
29107
|
+
sessionMode: mode,
|
|
28968
29108
|
cwd: targetCwd,
|
|
29109
|
+
onAgentSubprocessExit: () => {
|
|
29110
|
+
state.acpHandle = null;
|
|
29111
|
+
state.acpStartPromise = null;
|
|
29112
|
+
state.acpAgentKey = null;
|
|
29113
|
+
state.lastAcpStartError = "Agent subprocess exited";
|
|
29114
|
+
},
|
|
28969
29115
|
...hooks
|
|
28970
29116
|
}).then((h) => {
|
|
28971
29117
|
state.lastAcpStartError = null;
|
|
@@ -29003,6 +29149,14 @@ async function createAcpManager(options) {
|
|
|
29003
29149
|
backendFallbackAgentType = agentType;
|
|
29004
29150
|
}
|
|
29005
29151
|
}
|
|
29152
|
+
function logPromptReceivedFromBridge(opts) {
|
|
29153
|
+
const { agentType, mode } = opts;
|
|
29154
|
+
const preferredForPrompt = agentType ?? backendFallbackAgentType ?? null;
|
|
29155
|
+
const modeLabel = typeof mode === "string" && mode.trim() !== "" ? mode.trim() : "not set";
|
|
29156
|
+
log2(
|
|
29157
|
+
`[Agent] Prompt received (${getAgentTypeDisplayName(preferredForPrompt)}, mode: ${modeLabel})`
|
|
29158
|
+
);
|
|
29159
|
+
}
|
|
29006
29160
|
function handlePrompt(opts) {
|
|
29007
29161
|
const {
|
|
29008
29162
|
promptText,
|
|
@@ -29083,6 +29237,7 @@ async function createAcpManager(options) {
|
|
|
29083
29237
|
if (promptRouting.runId !== runId) return false;
|
|
29084
29238
|
const handle = state.acpHandle;
|
|
29085
29239
|
if (handle?.cancel) {
|
|
29240
|
+
log2("[Agent] Stop requested");
|
|
29086
29241
|
try {
|
|
29087
29242
|
await handle.cancel();
|
|
29088
29243
|
return true;
|
|
@@ -29091,6 +29246,7 @@ async function createAcpManager(options) {
|
|
|
29091
29246
|
return false;
|
|
29092
29247
|
}
|
|
29093
29248
|
}
|
|
29249
|
+
log2("[Agent] Stop requested (agent still starting)");
|
|
29094
29250
|
pendingCancelRunId = runId;
|
|
29095
29251
|
return true;
|
|
29096
29252
|
}
|
|
@@ -29103,7 +29259,14 @@ async function createAcpManager(options) {
|
|
|
29103
29259
|
state.acpStartPromise = null;
|
|
29104
29260
|
state.acpAgentKey = null;
|
|
29105
29261
|
}
|
|
29106
|
-
return {
|
|
29262
|
+
return {
|
|
29263
|
+
setPreferredAgentType,
|
|
29264
|
+
logPromptReceivedFromBridge,
|
|
29265
|
+
handlePrompt,
|
|
29266
|
+
cancelRun,
|
|
29267
|
+
resolveRequest,
|
|
29268
|
+
disconnect
|
|
29269
|
+
};
|
|
29107
29270
|
}
|
|
29108
29271
|
|
|
29109
29272
|
// src/bridge/routing/handlers/auth-token.ts
|
|
@@ -29238,6 +29401,8 @@ function handleBridgePrompt(msg, deps) {
|
|
|
29238
29401
|
const sessionWorktreesEnabled = msg.sessionWorktreesEnabled === true;
|
|
29239
29402
|
const agentType = typeof msg.agentType === "string" && msg.agentType.trim() ? msg.agentType.trim() : void 0;
|
|
29240
29403
|
const runId = typeof msg.runId === "string" ? msg.runId : void 0;
|
|
29404
|
+
const mode = typeof msg.mode === "string" && msg.mode.trim() ? msg.mode.trim() : void 0;
|
|
29405
|
+
acpManager.logPromptReceivedFromBridge({ agentType, mode });
|
|
29241
29406
|
const sendResult = (result) => {
|
|
29242
29407
|
const s = getWs();
|
|
29243
29408
|
if (s) sendWsMessage(s, result);
|
|
@@ -29300,7 +29465,7 @@ function handleBridgePrompt(msg, deps) {
|
|
|
29300
29465
|
promptId: msg.id,
|
|
29301
29466
|
sessionId,
|
|
29302
29467
|
runId,
|
|
29303
|
-
mode
|
|
29468
|
+
mode,
|
|
29304
29469
|
agentType,
|
|
29305
29470
|
cwd: effectiveCwd,
|
|
29306
29471
|
sendResult,
|
|
@@ -29370,7 +29535,7 @@ var handleSkillCallMessage = (msg, { getWs, log: log2 }) => {
|
|
|
29370
29535
|
};
|
|
29371
29536
|
|
|
29372
29537
|
// src/files/list-dir.ts
|
|
29373
|
-
import
|
|
29538
|
+
import fs5 from "node:fs";
|
|
29374
29539
|
import path13 from "node:path";
|
|
29375
29540
|
|
|
29376
29541
|
// src/files/ensure-under-cwd.ts
|
|
@@ -29391,14 +29556,14 @@ function listDir(relativePath) {
|
|
|
29391
29556
|
return { error: "Path is outside working directory" };
|
|
29392
29557
|
}
|
|
29393
29558
|
try {
|
|
29394
|
-
const names =
|
|
29559
|
+
const names = fs5.readdirSync(resolved, { withFileTypes: true });
|
|
29395
29560
|
const entries = names.filter((d) => !d.name.startsWith(".")).map((d) => {
|
|
29396
29561
|
const entryPath = path13.join(relativePath || ".", d.name).replace(/\\/g, "/");
|
|
29397
29562
|
const fullPath = path13.join(resolved, d.name);
|
|
29398
29563
|
let isDir = d.isDirectory();
|
|
29399
29564
|
if (d.isSymbolicLink()) {
|
|
29400
29565
|
try {
|
|
29401
|
-
const targetStat =
|
|
29566
|
+
const targetStat = fs5.statSync(fullPath);
|
|
29402
29567
|
isDir = targetStat.isDirectory();
|
|
29403
29568
|
} catch {
|
|
29404
29569
|
isDir = false;
|
|
@@ -29422,25 +29587,25 @@ function listDir(relativePath) {
|
|
|
29422
29587
|
}
|
|
29423
29588
|
|
|
29424
29589
|
// src/files/read-file.ts
|
|
29425
|
-
import
|
|
29590
|
+
import fs6 from "node:fs";
|
|
29426
29591
|
import { StringDecoder } from "node:string_decoder";
|
|
29427
29592
|
function resolveFilePath(relativePath) {
|
|
29428
29593
|
const resolved = ensureUnderCwd(relativePath, getBridgeWorkspaceDirectory());
|
|
29429
29594
|
if (!resolved) return { error: "Path is outside working directory" };
|
|
29430
29595
|
let real;
|
|
29431
29596
|
try {
|
|
29432
|
-
real =
|
|
29597
|
+
real = fs6.realpathSync(resolved);
|
|
29433
29598
|
} catch {
|
|
29434
29599
|
real = resolved;
|
|
29435
29600
|
}
|
|
29436
|
-
const
|
|
29437
|
-
if (!
|
|
29601
|
+
const stat2 = fs6.statSync(real);
|
|
29602
|
+
if (!stat2.isFile()) return { error: "Not a file" };
|
|
29438
29603
|
return real;
|
|
29439
29604
|
}
|
|
29440
29605
|
var LINE_CHUNK_SIZE = 64 * 1024;
|
|
29441
29606
|
function readFileRange(filePath, startLine, endLine, lineOffsetIn, lineChunkSize = LINE_CHUNK_SIZE) {
|
|
29442
|
-
const fileSize =
|
|
29443
|
-
const fd =
|
|
29607
|
+
const fileSize = fs6.statSync(filePath).size;
|
|
29608
|
+
const fd = fs6.openSync(filePath, "r");
|
|
29444
29609
|
const bufSize = 64 * 1024;
|
|
29445
29610
|
const buf = Buffer.alloc(bufSize);
|
|
29446
29611
|
const decoder = new StringDecoder("utf8");
|
|
@@ -29453,7 +29618,7 @@ function readFileRange(filePath, startLine, endLine, lineOffsetIn, lineChunkSize
|
|
|
29453
29618
|
let line0Accum = "";
|
|
29454
29619
|
try {
|
|
29455
29620
|
let bytesRead;
|
|
29456
|
-
while (!done && (bytesRead =
|
|
29621
|
+
while (!done && (bytesRead = fs6.readSync(fd, buf, 0, bufSize, null)) > 0) {
|
|
29457
29622
|
const text = partial2 + decoder.write(buf.subarray(0, bytesRead));
|
|
29458
29623
|
partial2 = "";
|
|
29459
29624
|
let lineStart = 0;
|
|
@@ -29588,10 +29753,10 @@ function readFileRange(filePath, startLine, endLine, lineOffsetIn, lineChunkSize
|
|
|
29588
29753
|
}
|
|
29589
29754
|
return { content: resultLines.join("\n"), size: fileSize };
|
|
29590
29755
|
} finally {
|
|
29591
|
-
|
|
29756
|
+
fs6.closeSync(fd);
|
|
29592
29757
|
}
|
|
29593
29758
|
}
|
|
29594
|
-
function
|
|
29759
|
+
function readFile2(relativePath, startLine, endLine, lineOffset, lineChunkSize = LINE_CHUNK_SIZE) {
|
|
29595
29760
|
try {
|
|
29596
29761
|
const result = resolveFilePath(relativePath);
|
|
29597
29762
|
if (typeof result === "object") return result;
|
|
@@ -29599,17 +29764,17 @@ function readFile(relativePath, startLine, endLine, lineOffset, lineChunkSize =
|
|
|
29599
29764
|
if (hasRange) {
|
|
29600
29765
|
return readFileRange(result, startLine, endLine, lineOffset, lineChunkSize);
|
|
29601
29766
|
}
|
|
29602
|
-
const
|
|
29603
|
-
const raw =
|
|
29767
|
+
const stat2 = fs6.statSync(result);
|
|
29768
|
+
const raw = fs6.readFileSync(result, "utf8");
|
|
29604
29769
|
const lines = raw.split(/\r?\n/);
|
|
29605
|
-
return { content: raw, totalLines: lines.length, size:
|
|
29770
|
+
return { content: raw, totalLines: lines.length, size: stat2.size };
|
|
29606
29771
|
} catch (err) {
|
|
29607
29772
|
return { error: err instanceof Error ? err.message : String(err) };
|
|
29608
29773
|
}
|
|
29609
29774
|
}
|
|
29610
29775
|
|
|
29611
29776
|
// src/files/file-index.ts
|
|
29612
|
-
import
|
|
29777
|
+
import fs7 from "node:fs";
|
|
29613
29778
|
import path14 from "node:path";
|
|
29614
29779
|
import os2 from "node:os";
|
|
29615
29780
|
import crypto2 from "node:crypto";
|
|
@@ -29656,23 +29821,23 @@ function binarySearch(arr, x) {
|
|
|
29656
29821
|
function walkDir(dir, baseDir, out) {
|
|
29657
29822
|
let names;
|
|
29658
29823
|
try {
|
|
29659
|
-
names =
|
|
29824
|
+
names = fs7.readdirSync(dir);
|
|
29660
29825
|
} catch {
|
|
29661
29826
|
return;
|
|
29662
29827
|
}
|
|
29663
29828
|
for (const name of names) {
|
|
29664
29829
|
if (name.startsWith(".")) continue;
|
|
29665
29830
|
const full = path14.join(dir, name);
|
|
29666
|
-
let
|
|
29831
|
+
let stat2;
|
|
29667
29832
|
try {
|
|
29668
|
-
|
|
29833
|
+
stat2 = fs7.statSync(full);
|
|
29669
29834
|
} catch {
|
|
29670
29835
|
continue;
|
|
29671
29836
|
}
|
|
29672
29837
|
const relative4 = path14.relative(baseDir, full).replace(/\\/g, "/");
|
|
29673
|
-
if (
|
|
29838
|
+
if (stat2.isDirectory()) {
|
|
29674
29839
|
walkDir(full, baseDir, out);
|
|
29675
|
-
} else if (
|
|
29840
|
+
} else if (stat2.isFile()) {
|
|
29676
29841
|
out.push(relative4);
|
|
29677
29842
|
}
|
|
29678
29843
|
}
|
|
@@ -29696,8 +29861,8 @@ function buildFileIndex(cwd) {
|
|
|
29696
29861
|
const data = { version: INDEX_VERSION, paths, trigramIndex };
|
|
29697
29862
|
const indexPath = getIndexPath(resolved);
|
|
29698
29863
|
try {
|
|
29699
|
-
if (!
|
|
29700
|
-
|
|
29864
|
+
if (!fs7.existsSync(INDEX_DIR)) fs7.mkdirSync(INDEX_DIR, { recursive: true });
|
|
29865
|
+
fs7.writeFileSync(indexPath, JSON.stringify(data), "utf8");
|
|
29701
29866
|
} catch (e) {
|
|
29702
29867
|
console.error("[file-index] Failed to write index:", e);
|
|
29703
29868
|
}
|
|
@@ -29707,7 +29872,7 @@ function loadFileIndex(cwd) {
|
|
|
29707
29872
|
const resolved = path14.resolve(cwd);
|
|
29708
29873
|
const indexPath = getIndexPath(resolved);
|
|
29709
29874
|
try {
|
|
29710
|
-
const raw =
|
|
29875
|
+
const raw = fs7.readFileSync(indexPath, "utf8");
|
|
29711
29876
|
const parsed = JSON.parse(raw);
|
|
29712
29877
|
if (parsed !== null && typeof parsed === "object" && Array.isArray(parsed.paths)) {
|
|
29713
29878
|
const obj = parsed;
|
|
@@ -29811,7 +29976,7 @@ function handleFileBrowserRequest(msg, socket) {
|
|
|
29811
29976
|
const endLine = typeof msg.endLine === "number" ? msg.endLine : void 0;
|
|
29812
29977
|
const lineOffset = typeof msg.lineOffset === "number" ? msg.lineOffset : void 0;
|
|
29813
29978
|
const lineChunkSize = typeof msg.lineChunkSize === "number" ? msg.lineChunkSize : void 0;
|
|
29814
|
-
const result =
|
|
29979
|
+
const result = readFile2(reqPath, startLine, endLine, lineOffset, lineChunkSize);
|
|
29815
29980
|
if ("error" in result) {
|
|
29816
29981
|
sendWsMessage(socket, { type: "file_browser_response", id: msg.id, error: result.error });
|
|
29817
29982
|
} else {
|
|
@@ -29846,7 +30011,7 @@ function handleFileBrowserSearchMessage(msg, { getWs }) {
|
|
|
29846
30011
|
}
|
|
29847
30012
|
|
|
29848
30013
|
// src/skills/discover-local-agent-skills.ts
|
|
29849
|
-
import
|
|
30014
|
+
import fs8 from "node:fs";
|
|
29850
30015
|
import path15 from "node:path";
|
|
29851
30016
|
var SKILL_DISCOVERY_ROOTS = [".agents/skills", ".claude/skills", ".cursor/skills", "skills"];
|
|
29852
30017
|
function discoverLocalSkills(cwd) {
|
|
@@ -29854,22 +30019,22 @@ function discoverLocalSkills(cwd) {
|
|
|
29854
30019
|
const seenKeys = /* @__PURE__ */ new Set();
|
|
29855
30020
|
for (const rel of SKILL_DISCOVERY_ROOTS) {
|
|
29856
30021
|
const base = path15.join(cwd, rel);
|
|
29857
|
-
if (!
|
|
30022
|
+
if (!fs8.existsSync(base) || !fs8.statSync(base).isDirectory()) continue;
|
|
29858
30023
|
let entries = [];
|
|
29859
30024
|
try {
|
|
29860
|
-
entries =
|
|
30025
|
+
entries = fs8.readdirSync(base);
|
|
29861
30026
|
} catch {
|
|
29862
30027
|
continue;
|
|
29863
30028
|
}
|
|
29864
30029
|
for (const name of entries) {
|
|
29865
30030
|
const dir = path15.join(base, name);
|
|
29866
30031
|
try {
|
|
29867
|
-
if (!
|
|
30032
|
+
if (!fs8.statSync(dir).isDirectory()) continue;
|
|
29868
30033
|
} catch {
|
|
29869
30034
|
continue;
|
|
29870
30035
|
}
|
|
29871
30036
|
const skillMd = path15.join(dir, "SKILL.md");
|
|
29872
|
-
if (!
|
|
30037
|
+
if (!fs8.existsSync(skillMd)) continue;
|
|
29873
30038
|
const key = `${rel}/${name}`;
|
|
29874
30039
|
if (seenKeys.has(key)) continue;
|
|
29875
30040
|
seenKeys.add(key);
|
|
@@ -29882,10 +30047,10 @@ function discoverSkillLayoutRoots(cwd) {
|
|
|
29882
30047
|
const roots = [];
|
|
29883
30048
|
for (const rel of SKILL_DISCOVERY_ROOTS) {
|
|
29884
30049
|
const base = path15.join(cwd, rel);
|
|
29885
|
-
if (!
|
|
30050
|
+
if (!fs8.existsSync(base) || !fs8.statSync(base).isDirectory()) continue;
|
|
29886
30051
|
let entries = [];
|
|
29887
30052
|
try {
|
|
29888
|
-
entries =
|
|
30053
|
+
entries = fs8.readdirSync(base);
|
|
29889
30054
|
} catch {
|
|
29890
30055
|
continue;
|
|
29891
30056
|
}
|
|
@@ -29893,11 +30058,11 @@ function discoverSkillLayoutRoots(cwd) {
|
|
|
29893
30058
|
for (const name of entries) {
|
|
29894
30059
|
const dir = path15.join(base, name);
|
|
29895
30060
|
try {
|
|
29896
|
-
if (!
|
|
30061
|
+
if (!fs8.statSync(dir).isDirectory()) continue;
|
|
29897
30062
|
} catch {
|
|
29898
30063
|
continue;
|
|
29899
30064
|
}
|
|
29900
|
-
if (!
|
|
30065
|
+
if (!fs8.existsSync(path15.join(dir, "SKILL.md"))) continue;
|
|
29901
30066
|
const relPath = `${rel}/${name}`.replace(/\\/g, "/");
|
|
29902
30067
|
skills2.push({ name, relPath });
|
|
29903
30068
|
}
|
|
@@ -29917,7 +30082,7 @@ function handleSkillLayoutRequest(msg, deps) {
|
|
|
29917
30082
|
}
|
|
29918
30083
|
|
|
29919
30084
|
// src/skills/install-remote-skills.ts
|
|
29920
|
-
import
|
|
30085
|
+
import fs9 from "node:fs";
|
|
29921
30086
|
import path16 from "node:path";
|
|
29922
30087
|
function installRemoteSkills(cwd, targetDir, items) {
|
|
29923
30088
|
const installed = [];
|
|
@@ -29933,11 +30098,11 @@ function installRemoteSkills(cwd, targetDir, items) {
|
|
|
29933
30098
|
for (const f of item.files) {
|
|
29934
30099
|
if (typeof f.path !== "string" || !f.text && !f.base64) continue;
|
|
29935
30100
|
const dest = path16.join(skillDir, f.path);
|
|
29936
|
-
|
|
30101
|
+
fs9.mkdirSync(path16.dirname(dest), { recursive: true });
|
|
29937
30102
|
if (f.text !== void 0) {
|
|
29938
|
-
|
|
30103
|
+
fs9.writeFileSync(dest, f.text, "utf8");
|
|
29939
30104
|
} else if (f.base64) {
|
|
29940
|
-
|
|
30105
|
+
fs9.writeFileSync(dest, Buffer.from(f.base64, "base64"));
|
|
29941
30106
|
}
|
|
29942
30107
|
}
|
|
29943
30108
|
installed.push({
|
|
@@ -30031,7 +30196,7 @@ var handleSessionDiscardedMessage = (msg, deps) => {
|
|
|
30031
30196
|
};
|
|
30032
30197
|
|
|
30033
30198
|
// src/bridge/routing/handlers/revert-turn-snapshot.ts
|
|
30034
|
-
import * as
|
|
30199
|
+
import * as fs10 from "node:fs";
|
|
30035
30200
|
var handleRevertTurnSnapshotMessage = (msg, deps) => {
|
|
30036
30201
|
const id = typeof msg.id === "string" ? msg.id : "";
|
|
30037
30202
|
const sessionId = typeof msg.sessionId === "string" ? msg.sessionId : "";
|
|
@@ -30043,7 +30208,7 @@ var handleRevertTurnSnapshotMessage = (msg, deps) => {
|
|
|
30043
30208
|
if (!s) return;
|
|
30044
30209
|
const agentBase = sessionWorktreeManager.getAgentCwdForSession(sessionId) ?? getBridgeWorkspaceDirectory();
|
|
30045
30210
|
const file2 = snapshotFilePath(agentBase, turnId);
|
|
30046
|
-
if (!
|
|
30211
|
+
if (!fs10.existsSync(file2)) {
|
|
30047
30212
|
sendWsMessage(s, {
|
|
30048
30213
|
type: "revert_turn_snapshot_result",
|
|
30049
30214
|
id,
|
|
@@ -30144,25 +30309,12 @@ function dispatchBridgeMessage(msg, deps) {
|
|
|
30144
30309
|
}
|
|
30145
30310
|
|
|
30146
30311
|
// src/bridge/routing/handle-bridge-message.ts
|
|
30147
|
-
var DEFERRED_INBOUND_TYPES = /* @__PURE__ */ new Set([
|
|
30148
|
-
"server_control",
|
|
30149
|
-
"prompt",
|
|
30150
|
-
"install_skills",
|
|
30151
|
-
"refresh_local_skills",
|
|
30152
|
-
"dev_servers_config"
|
|
30153
|
-
]);
|
|
30154
30312
|
function handleBridgeMessage(data, deps) {
|
|
30155
30313
|
const msg = data;
|
|
30156
|
-
|
|
30157
|
-
|
|
30158
|
-
|
|
30159
|
-
|
|
30160
|
-
setImmediate(() => {
|
|
30161
|
-
dispatchBridgeMessage(msg, deps);
|
|
30162
|
-
});
|
|
30163
|
-
return;
|
|
30164
|
-
}
|
|
30165
|
-
dispatchBridgeMessage(msg, deps);
|
|
30314
|
+
if (!deps.getWs()) return;
|
|
30315
|
+
setImmediate(() => {
|
|
30316
|
+
dispatchBridgeMessage(msg, deps);
|
|
30317
|
+
});
|
|
30166
30318
|
}
|
|
30167
30319
|
|
|
30168
30320
|
// src/worktrees/session-worktree-manager.ts
|
|
@@ -30170,7 +30322,7 @@ import * as path20 from "node:path";
|
|
|
30170
30322
|
import os4 from "node:os";
|
|
30171
30323
|
|
|
30172
30324
|
// src/worktrees/prepare-new-session-worktrees.ts
|
|
30173
|
-
import * as
|
|
30325
|
+
import * as fs12 from "node:fs";
|
|
30174
30326
|
import * as path18 from "node:path";
|
|
30175
30327
|
|
|
30176
30328
|
// src/git/worktree-add.ts
|
|
@@ -30180,7 +30332,7 @@ async function gitWorktreeAddBranch(mainRepoPath, worktreePath, branch) {
|
|
|
30180
30332
|
}
|
|
30181
30333
|
|
|
30182
30334
|
// src/worktrees/worktree-layout-file.ts
|
|
30183
|
-
import * as
|
|
30335
|
+
import * as fs11 from "node:fs";
|
|
30184
30336
|
import * as path17 from "node:path";
|
|
30185
30337
|
import os3 from "node:os";
|
|
30186
30338
|
var LAYOUT_FILENAME = "worktree-launcher-layout.json";
|
|
@@ -30197,8 +30349,8 @@ function normalizeLoadedLayout(raw) {
|
|
|
30197
30349
|
function loadWorktreeLayout() {
|
|
30198
30350
|
try {
|
|
30199
30351
|
const p = defaultWorktreeLayoutPath();
|
|
30200
|
-
if (!
|
|
30201
|
-
const raw = JSON.parse(
|
|
30352
|
+
if (!fs11.existsSync(p)) return { launcherCwds: [] };
|
|
30353
|
+
const raw = JSON.parse(fs11.readFileSync(p, "utf8"));
|
|
30202
30354
|
return normalizeLoadedLayout(raw);
|
|
30203
30355
|
} catch {
|
|
30204
30356
|
return { launcherCwds: [] };
|
|
@@ -30207,8 +30359,8 @@ function loadWorktreeLayout() {
|
|
|
30207
30359
|
function saveWorktreeLayout(layout) {
|
|
30208
30360
|
try {
|
|
30209
30361
|
const dir = path17.dirname(defaultWorktreeLayoutPath());
|
|
30210
|
-
|
|
30211
|
-
|
|
30362
|
+
fs11.mkdirSync(dir, { recursive: true });
|
|
30363
|
+
fs11.writeFileSync(defaultWorktreeLayoutPath(), JSON.stringify(layout, null, 2), "utf8");
|
|
30212
30364
|
} catch {
|
|
30213
30365
|
}
|
|
30214
30366
|
}
|
|
@@ -30245,13 +30397,13 @@ async function prepareNewSessionWorktrees(options) {
|
|
|
30245
30397
|
}
|
|
30246
30398
|
const branch = `session-${sessionId}`;
|
|
30247
30399
|
const worktreePaths = [];
|
|
30248
|
-
|
|
30400
|
+
fs12.mkdirSync(agentMirrorRoot, { recursive: true });
|
|
30249
30401
|
for (const repo of repos) {
|
|
30250
30402
|
let rel = path18.relative(launcherResolved, repo.absolutePath);
|
|
30251
30403
|
if (rel.startsWith("..") || path18.isAbsolute(rel)) continue;
|
|
30252
30404
|
const relNorm = rel === "" ? "." : rel;
|
|
30253
30405
|
const wtPath = path18.join(agentMirrorRoot, relNorm, sessionId);
|
|
30254
|
-
|
|
30406
|
+
fs12.mkdirSync(path18.dirname(wtPath), { recursive: true });
|
|
30255
30407
|
try {
|
|
30256
30408
|
await gitWorktreeAddBranch(repo.absolutePath, wtPath, branch);
|
|
30257
30409
|
log2(`[worktrees] Added worktree ${wtPath} (branch ${branch}).`);
|
|
@@ -30288,18 +30440,18 @@ async function renameSessionWorktreeBranches(paths, newBranch, log2) {
|
|
|
30288
30440
|
}
|
|
30289
30441
|
|
|
30290
30442
|
// src/worktrees/remove-session-worktrees.ts
|
|
30291
|
-
import * as
|
|
30443
|
+
import * as fs15 from "node:fs";
|
|
30292
30444
|
|
|
30293
30445
|
// src/git/worktree-remove.ts
|
|
30294
|
-
import * as
|
|
30446
|
+
import * as fs14 from "node:fs";
|
|
30295
30447
|
|
|
30296
30448
|
// src/git/resolve-main-repo-from-git-file.ts
|
|
30297
|
-
import * as
|
|
30449
|
+
import * as fs13 from "node:fs";
|
|
30298
30450
|
import * as path19 from "node:path";
|
|
30299
30451
|
function resolveMainRepoFromWorktreeGitFile(wt) {
|
|
30300
30452
|
const gitDirFile = path19.join(wt, ".git");
|
|
30301
|
-
if (!
|
|
30302
|
-
const first2 =
|
|
30453
|
+
if (!fs13.existsSync(gitDirFile) || !fs13.statSync(gitDirFile).isFile()) return "";
|
|
30454
|
+
const first2 = fs13.readFileSync(gitDirFile, "utf8").trim();
|
|
30303
30455
|
const m = first2.match(/^gitdir:\s*(.+)$/im);
|
|
30304
30456
|
if (!m) return "";
|
|
30305
30457
|
const gitWorktreePath = path19.resolve(wt, m[1].trim());
|
|
@@ -30313,7 +30465,7 @@ async function gitWorktreeRemoveForce(worktreePath) {
|
|
|
30313
30465
|
if (mainRepo) {
|
|
30314
30466
|
await simpleGit(mainRepo).raw(["worktree", "remove", "--force", worktreePath]);
|
|
30315
30467
|
} else {
|
|
30316
|
-
|
|
30468
|
+
fs14.rmSync(worktreePath, { recursive: true, force: true });
|
|
30317
30469
|
}
|
|
30318
30470
|
}
|
|
30319
30471
|
|
|
@@ -30326,7 +30478,7 @@ async function removeSessionWorktrees(paths, log2) {
|
|
|
30326
30478
|
} catch (e) {
|
|
30327
30479
|
log2(`[worktrees] Remove failed for ${wt}: ${e instanceof Error ? e.message : String(e)}`);
|
|
30328
30480
|
try {
|
|
30329
|
-
|
|
30481
|
+
fs15.rmSync(wt, { recursive: true, force: true });
|
|
30330
30482
|
} catch {
|
|
30331
30483
|
}
|
|
30332
30484
|
}
|
|
@@ -30598,7 +30750,7 @@ function forceKillChild(proc, log2, shortId, graceMs) {
|
|
|
30598
30750
|
}
|
|
30599
30751
|
|
|
30600
30752
|
// src/dev-servers/process/wire-dev-server-child-process.ts
|
|
30601
|
-
import
|
|
30753
|
+
import fs16 from "node:fs";
|
|
30602
30754
|
|
|
30603
30755
|
// src/dev-servers/manager/forward-pipe.ts
|
|
30604
30756
|
function forwardChildPipe(childReadable, terminal, onData) {
|
|
@@ -30634,7 +30786,7 @@ function wireDevServerChildProcess(d) {
|
|
|
30634
30786
|
d.setPollInterval(void 0);
|
|
30635
30787
|
return;
|
|
30636
30788
|
}
|
|
30637
|
-
|
|
30789
|
+
fs16.readFile(d.mergedLogPath, (err, buf) => {
|
|
30638
30790
|
if (err || (d.getSpawnGeneration() ?? 0) !== d.scheduledGen) return;
|
|
30639
30791
|
if (buf.length <= d.mergedReadPos.value) return;
|
|
30640
30792
|
const chunk = Buffer.from(buf.subarray(d.mergedReadPos.value));
|
|
@@ -30672,7 +30824,7 @@ ${errTail}` : ""}`);
|
|
|
30672
30824
|
d.sendStatus(code === 0 || code == null ? "stopped" : "error", detail, tails);
|
|
30673
30825
|
};
|
|
30674
30826
|
if (mergedPath) {
|
|
30675
|
-
|
|
30827
|
+
fs16.readFile(mergedPath, (err, buf) => {
|
|
30676
30828
|
if (!err && buf.length > d.mergedReadPos.value) {
|
|
30677
30829
|
const chunk = Buffer.from(buf.subarray(d.mergedReadPos.value));
|
|
30678
30830
|
if (chunk.length > 0) {
|
|
@@ -30774,13 +30926,13 @@ function parseDevServerDefs(servers) {
|
|
|
30774
30926
|
}
|
|
30775
30927
|
|
|
30776
30928
|
// src/dev-servers/manager/shell-spawn/utils.ts
|
|
30777
|
-
import
|
|
30929
|
+
import fs17 from "node:fs";
|
|
30778
30930
|
function isSpawnEbadf(e) {
|
|
30779
30931
|
return typeof e === "object" && e !== null && "code" in e && e.code === "EBADF";
|
|
30780
30932
|
}
|
|
30781
30933
|
function rmDirQuiet(dir) {
|
|
30782
30934
|
try {
|
|
30783
|
-
|
|
30935
|
+
fs17.rmSync(dir, { recursive: true, force: true });
|
|
30784
30936
|
} catch {
|
|
30785
30937
|
}
|
|
30786
30938
|
}
|
|
@@ -30788,7 +30940,7 @@ var cachedDevNullReadFd;
|
|
|
30788
30940
|
function devNullReadFd() {
|
|
30789
30941
|
if (cachedDevNullReadFd === void 0) {
|
|
30790
30942
|
const devPath = process.platform === "win32" ? "nul" : "/dev/null";
|
|
30791
|
-
cachedDevNullReadFd =
|
|
30943
|
+
cachedDevNullReadFd = fs17.openSync(devPath, "r");
|
|
30792
30944
|
}
|
|
30793
30945
|
return cachedDevNullReadFd;
|
|
30794
30946
|
}
|
|
@@ -30862,15 +31014,15 @@ function trySpawnShellTruePiped(command, env, cwd, devNullFd, signal) {
|
|
|
30862
31014
|
|
|
30863
31015
|
// src/dev-servers/manager/shell-spawn/try-spawn-merged-log-file.ts
|
|
30864
31016
|
import { spawn as spawn7 } from "node:child_process";
|
|
30865
|
-
import
|
|
31017
|
+
import fs18 from "node:fs";
|
|
30866
31018
|
import { tmpdir } from "node:os";
|
|
30867
31019
|
import path22 from "node:path";
|
|
30868
31020
|
function trySpawnMergedLogFile(command, env, cwd, signal) {
|
|
30869
|
-
const tmpRoot =
|
|
31021
|
+
const tmpRoot = fs18.mkdtempSync(path22.join(tmpdir(), "ba-devsrv-log-"));
|
|
30870
31022
|
const logPath = path22.join(tmpRoot, "combined.log");
|
|
30871
31023
|
let logFd;
|
|
30872
31024
|
try {
|
|
30873
|
-
logFd =
|
|
31025
|
+
logFd = fs18.openSync(logPath, "a");
|
|
30874
31026
|
} catch {
|
|
30875
31027
|
rmDirQuiet(tmpRoot);
|
|
30876
31028
|
return null;
|
|
@@ -30889,7 +31041,7 @@ function trySpawnMergedLogFile(command, env, cwd, signal) {
|
|
|
30889
31041
|
} else {
|
|
30890
31042
|
proc = spawn7("/bin/sh", ["-c", command], { env, cwd, stdio, ...signal ? { signal } : {} });
|
|
30891
31043
|
}
|
|
30892
|
-
|
|
31044
|
+
fs18.closeSync(logFd);
|
|
30893
31045
|
return {
|
|
30894
31046
|
proc,
|
|
30895
31047
|
pipedStdoutStderr: true,
|
|
@@ -30898,7 +31050,7 @@ function trySpawnMergedLogFile(command, env, cwd, signal) {
|
|
|
30898
31050
|
};
|
|
30899
31051
|
} catch (e) {
|
|
30900
31052
|
try {
|
|
30901
|
-
|
|
31053
|
+
fs18.closeSync(logFd);
|
|
30902
31054
|
} catch {
|
|
30903
31055
|
}
|
|
30904
31056
|
rmDirQuiet(tmpRoot);
|
|
@@ -30909,22 +31061,22 @@ function trySpawnMergedLogFile(command, env, cwd, signal) {
|
|
|
30909
31061
|
|
|
30910
31062
|
// src/dev-servers/manager/shell-spawn/try-spawn-shell-script-log-redirect.ts
|
|
30911
31063
|
import { spawn as spawn8 } from "node:child_process";
|
|
30912
|
-
import
|
|
31064
|
+
import fs19 from "node:fs";
|
|
30913
31065
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
30914
31066
|
import path23 from "node:path";
|
|
30915
31067
|
function shSingleQuote(s) {
|
|
30916
31068
|
return `'${s.replace(/'/g, `'\\''`)}'`;
|
|
30917
31069
|
}
|
|
30918
31070
|
function trySpawnShellScriptLogRedirectUnix(command, env, cwd, signal) {
|
|
30919
|
-
const tmpRoot =
|
|
31071
|
+
const tmpRoot = fs19.mkdtempSync(path23.join(tmpdir2(), "ba-devsrv-sh-"));
|
|
30920
31072
|
const logPath = path23.join(tmpRoot, "combined.log");
|
|
30921
31073
|
const innerPath = path23.join(tmpRoot, "_cmd.sh");
|
|
30922
31074
|
const runnerPath = path23.join(tmpRoot, "_run.sh");
|
|
30923
31075
|
try {
|
|
30924
|
-
|
|
31076
|
+
fs19.writeFileSync(innerPath, `#!/bin/sh
|
|
30925
31077
|
${command}
|
|
30926
31078
|
`);
|
|
30927
|
-
|
|
31079
|
+
fs19.writeFileSync(
|
|
30928
31080
|
runnerPath,
|
|
30929
31081
|
`#!/bin/sh
|
|
30930
31082
|
cd ${shSingleQuote(cwd)}
|
|
@@ -30950,13 +31102,13 @@ cd ${shSingleQuote(cwd)}
|
|
|
30950
31102
|
}
|
|
30951
31103
|
}
|
|
30952
31104
|
function trySpawnShellScriptLogRedirectWin(command, env, cwd, signal) {
|
|
30953
|
-
const tmpRoot =
|
|
31105
|
+
const tmpRoot = fs19.mkdtempSync(path23.join(tmpdir2(), "ba-devsrv-sh-"));
|
|
30954
31106
|
const logPath = path23.join(tmpRoot, "combined.log");
|
|
30955
31107
|
const runnerPath = path23.join(tmpRoot, "_run.bat");
|
|
30956
31108
|
const q = (p) => `"${p.replace(/"/g, '""')}"`;
|
|
30957
31109
|
const com = process.env.ComSpec || "cmd.exe";
|
|
30958
31110
|
try {
|
|
30959
|
-
|
|
31111
|
+
fs19.writeFileSync(
|
|
30960
31112
|
runnerPath,
|
|
30961
31113
|
`@ECHO OFF\r
|
|
30962
31114
|
CD /D ${q(cwd)}\r
|
|
@@ -31310,7 +31462,9 @@ var DevServerManager = class {
|
|
|
31310
31462
|
async shutdownAllGraceful() {
|
|
31311
31463
|
const pairs = [...this.processes.entries()];
|
|
31312
31464
|
if (pairs.length === 0) return;
|
|
31313
|
-
this.log(
|
|
31465
|
+
this.log(
|
|
31466
|
+
`[dev-server] Stopping ${pairs.length} local dev server process${pairs.length === 1 ? "" : "es"}\u2026`
|
|
31467
|
+
);
|
|
31314
31468
|
await Promise.all(pairs.map(([serverId, proc]) => this.gracefulTerminateOrUnknown(serverId, proc)));
|
|
31315
31469
|
}
|
|
31316
31470
|
async gracefulTerminateOrUnknown(serverId, proc) {
|
|
@@ -31477,9 +31631,10 @@ var FIREHOSE_CLIENT_PING_MS = 25e3;
|
|
|
31477
31631
|
function connectFirehose(options) {
|
|
31478
31632
|
const { firehoseServerUrl, workspaceId, bridgeName, proxyPorts, log: log2, devServerManager, onOpen, onClose } = options;
|
|
31479
31633
|
const wsUrl = buildFirehoseCliWsUrl(firehoseServerUrl);
|
|
31480
|
-
|
|
31634
|
+
applyCliOutboundNetworkPreferences();
|
|
31635
|
+
const wsOptions = { perMessageDeflate: false, family: 4 };
|
|
31481
31636
|
if (wsUrl.startsWith("wss://")) {
|
|
31482
|
-
wsOptions.agent = new https2.Agent({ rejectUnauthorized: false });
|
|
31637
|
+
wsOptions.agent = new https2.Agent({ rejectUnauthorized: false, family: 4 });
|
|
31483
31638
|
}
|
|
31484
31639
|
const ws = new wrapper_default(wsUrl, wsOptions);
|
|
31485
31640
|
let clientPingTimer = null;
|
|
@@ -31515,16 +31670,14 @@ function connectFirehose(options) {
|
|
|
31515
31670
|
sendWsMessage(ws, { type: "identify", workspaceId, bridgeName, proxyPorts });
|
|
31516
31671
|
});
|
|
31517
31672
|
ws.on("message", (raw) => {
|
|
31518
|
-
|
|
31519
|
-
|
|
31520
|
-
|
|
31521
|
-
|
|
31522
|
-
|
|
31523
|
-
|
|
31524
|
-
|
|
31525
|
-
|
|
31526
|
-
}
|
|
31527
|
-
});
|
|
31673
|
+
if (Buffer.isBuffer(raw) && tryConsumeBinaryProxyBody(raw, deps)) {
|
|
31674
|
+
return;
|
|
31675
|
+
}
|
|
31676
|
+
try {
|
|
31677
|
+
const text = Buffer.isBuffer(raw) ? raw.toString("utf8") : String(raw);
|
|
31678
|
+
dispatchFirehoseJsonMessage(JSON.parse(text), deps);
|
|
31679
|
+
} catch {
|
|
31680
|
+
}
|
|
31528
31681
|
});
|
|
31529
31682
|
ws.on("close", (code, reason) => {
|
|
31530
31683
|
clearClientPing();
|
|
@@ -31570,6 +31723,9 @@ function createOnBridgeIdentified(opts) {
|
|
|
31570
31723
|
function attachFirehose(params) {
|
|
31571
31724
|
state.lastFirehoseParams = params;
|
|
31572
31725
|
clearFirehoseReconnectTimer();
|
|
31726
|
+
if (state.firehoseReconnectAttempt === 0) {
|
|
31727
|
+
logFn("Connecting to preview tunnel (local HTTP proxy and dev logs)\u2026");
|
|
31728
|
+
}
|
|
31573
31729
|
state.firehoseGeneration += 1;
|
|
31574
31730
|
const myGen = state.firehoseGeneration;
|
|
31575
31731
|
if (state.firehoseHandle) {
|
|
@@ -31588,8 +31744,8 @@ function createOnBridgeIdentified(opts) {
|
|
|
31588
31744
|
clearFirehoseReconnectQuietOnOpen({ firehoseQuiet: state.firehoseQuiet }, logFn);
|
|
31589
31745
|
const logOpenAsFirehoseReconnect = state.firehoseReconnectAttempt > 0;
|
|
31590
31746
|
state.firehoseReconnectAttempt = 0;
|
|
31591
|
-
if (logOpenAsFirehoseReconnect) {
|
|
31592
|
-
logFn("
|
|
31747
|
+
if (!logOpenAsFirehoseReconnect) {
|
|
31748
|
+
logFn("Connected to preview tunnel (local HTTP proxy and dev logs).");
|
|
31593
31749
|
}
|
|
31594
31750
|
},
|
|
31595
31751
|
onClose: (code, reason) => {
|
|
@@ -31678,6 +31834,7 @@ var CHECKS = {
|
|
|
31678
31834
|
return false;
|
|
31679
31835
|
}
|
|
31680
31836
|
},
|
|
31837
|
+
/** Bridge spawns `@agentclientprotocol/claude-agent-acp` via npx; detection is “Claude toolchain likely present”. */
|
|
31681
31838
|
"claude-code": async () => {
|
|
31682
31839
|
try {
|
|
31683
31840
|
await execFileAsync4("which", ["claude"], { timeout: 4e3 });
|
|
@@ -31787,8 +31944,8 @@ async function createBridgeConnection(options) {
|
|
|
31787
31944
|
clearMainBridgeReconnectQuietOnOpen(state, logFn);
|
|
31788
31945
|
state.reconnectAttempt = 0;
|
|
31789
31946
|
state.logBridgeOpenAsReconnect = false;
|
|
31790
|
-
if (logOpenAsPostRefreshReconnect) {
|
|
31791
|
-
logFn("
|
|
31947
|
+
if (!logOpenAsPostRefreshReconnect) {
|
|
31948
|
+
logFn("Connected to bridge service.");
|
|
31792
31949
|
}
|
|
31793
31950
|
const socket = getWs();
|
|
31794
31951
|
if (socket) {
|
|
@@ -31827,6 +31984,9 @@ async function createBridgeConnection(options) {
|
|
|
31827
31984
|
clearTimeout(state.reconnectTimeout);
|
|
31828
31985
|
state.reconnectTimeout = null;
|
|
31829
31986
|
}
|
|
31987
|
+
if (state.reconnectAttempt === 0) {
|
|
31988
|
+
logFn("Connecting to bridge service\u2026");
|
|
31989
|
+
}
|
|
31830
31990
|
const prev = state.currentWs;
|
|
31831
31991
|
if (prev) {
|
|
31832
31992
|
prev.removeAllListeners();
|
|
@@ -31899,15 +32059,17 @@ async function runBridge(options) {
|
|
|
31899
32059
|
onAuth: (_auth) => {
|
|
31900
32060
|
}
|
|
31901
32061
|
});
|
|
31902
|
-
const onSignal2 = (
|
|
31903
|
-
logImmediate(
|
|
32062
|
+
const onSignal2 = (kind) => {
|
|
32063
|
+
logImmediate(
|
|
32064
|
+
kind === "interrupt" ? "Keyboard interrupt (Ctrl+C) \u2014 stopping\u2026" : "Stop requested \u2014 shutting down\u2026"
|
|
32065
|
+
);
|
|
31904
32066
|
setImmediate(() => {
|
|
31905
32067
|
handle2.close();
|
|
31906
32068
|
process.exit(0);
|
|
31907
32069
|
});
|
|
31908
32070
|
};
|
|
31909
|
-
const onSigInt2 = () => onSignal2("
|
|
31910
|
-
const onSigTerm2 = () => onSignal2("
|
|
32071
|
+
const onSigInt2 = () => onSignal2("interrupt");
|
|
32072
|
+
const onSigTerm2 = () => onSignal2("stop");
|
|
31911
32073
|
process.on("SIGINT", onSigInt2);
|
|
31912
32074
|
process.on("SIGTERM", onSigTerm2);
|
|
31913
32075
|
const auth = await handle2.authPromise;
|
|
@@ -31956,22 +32118,24 @@ async function runBridge(options) {
|
|
|
31956
32118
|
});
|
|
31957
32119
|
}
|
|
31958
32120
|
});
|
|
31959
|
-
const onSignal = (
|
|
31960
|
-
logImmediate(
|
|
32121
|
+
const onSignal = (kind) => {
|
|
32122
|
+
logImmediate(
|
|
32123
|
+
kind === "interrupt" ? "Keyboard interrupt (Ctrl+C) \u2014 stopping\u2026" : "Stop requested \u2014 shutting down\u2026"
|
|
32124
|
+
);
|
|
31961
32125
|
setImmediate(() => {
|
|
31962
32126
|
void handle.close().then(() => {
|
|
31963
32127
|
process.exit(0);
|
|
31964
32128
|
});
|
|
31965
32129
|
});
|
|
31966
32130
|
};
|
|
31967
|
-
const onSigInt = () => onSignal("
|
|
31968
|
-
const onSigTerm = () => onSignal("
|
|
32131
|
+
const onSigInt = () => onSignal("interrupt");
|
|
32132
|
+
const onSigTerm = () => onSignal("stop");
|
|
31969
32133
|
process.on("SIGINT", onSigInt);
|
|
31970
32134
|
process.on("SIGTERM", onSigTerm);
|
|
31971
32135
|
}
|
|
31972
32136
|
export {
|
|
31973
32137
|
callSkill,
|
|
31974
|
-
createAcpClient,
|
|
32138
|
+
createSdkStdioAcpClient as createAcpClient,
|
|
31975
32139
|
createBridgeConnection,
|
|
31976
32140
|
createWsBridge,
|
|
31977
32141
|
getSkill,
|