@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/cli.js
CHANGED
|
@@ -974,7 +974,7 @@ var require_command = __commonJS({
|
|
|
974
974
|
var EventEmitter2 = __require("node:events").EventEmitter;
|
|
975
975
|
var childProcess = __require("node:child_process");
|
|
976
976
|
var path25 = __require("node:path");
|
|
977
|
-
var
|
|
977
|
+
var fs21 = __require("node:fs");
|
|
978
978
|
var process2 = __require("node:process");
|
|
979
979
|
var { Argument: Argument2, humanReadableArgName } = require_argument();
|
|
980
980
|
var { CommanderError: CommanderError2 } = require_error();
|
|
@@ -1907,10 +1907,10 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1907
1907
|
const sourceExt = [".js", ".ts", ".tsx", ".mjs", ".cjs"];
|
|
1908
1908
|
function findFile(baseDir, baseName) {
|
|
1909
1909
|
const localBin = path25.resolve(baseDir, baseName);
|
|
1910
|
-
if (
|
|
1910
|
+
if (fs21.existsSync(localBin)) return localBin;
|
|
1911
1911
|
if (sourceExt.includes(path25.extname(baseName))) return void 0;
|
|
1912
1912
|
const foundExt = sourceExt.find(
|
|
1913
|
-
(ext) =>
|
|
1913
|
+
(ext) => fs21.existsSync(`${localBin}${ext}`)
|
|
1914
1914
|
);
|
|
1915
1915
|
if (foundExt) return `${localBin}${foundExt}`;
|
|
1916
1916
|
return void 0;
|
|
@@ -1922,7 +1922,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1922
1922
|
if (this._scriptPath) {
|
|
1923
1923
|
let resolvedScriptPath;
|
|
1924
1924
|
try {
|
|
1925
|
-
resolvedScriptPath =
|
|
1925
|
+
resolvedScriptPath = fs21.realpathSync(this._scriptPath);
|
|
1926
1926
|
} catch (err) {
|
|
1927
1927
|
resolvedScriptPath = this._scriptPath;
|
|
1928
1928
|
}
|
|
@@ -7426,12 +7426,12 @@ var require_src2 = __commonJS({
|
|
|
7426
7426
|
function check2(path25, isFile, isDirectory) {
|
|
7427
7427
|
log2(`checking %s`, path25);
|
|
7428
7428
|
try {
|
|
7429
|
-
const
|
|
7430
|
-
if (
|
|
7429
|
+
const stat2 = fs_1.statSync(path25);
|
|
7430
|
+
if (stat2.isFile() && isFile) {
|
|
7431
7431
|
log2(`[OK] path represents a file`);
|
|
7432
7432
|
return true;
|
|
7433
7433
|
}
|
|
7434
|
-
if (
|
|
7434
|
+
if (stat2.isDirectory() && isDirectory) {
|
|
7435
7435
|
log2(`[OK] path represents a directory`);
|
|
7436
7436
|
return true;
|
|
7437
7437
|
}
|
|
@@ -25047,7 +25047,7 @@ var init_acp = __esm({
|
|
|
25047
25047
|
});
|
|
25048
25048
|
|
|
25049
25049
|
// src/cli.ts
|
|
25050
|
-
import * as
|
|
25050
|
+
import * as fs20 from "node:fs";
|
|
25051
25051
|
import * as path24 from "node:path";
|
|
25052
25052
|
|
|
25053
25053
|
// ../../node_modules/.pnpm/commander@12.1.0/node_modules/commander/esm.mjs
|
|
@@ -25179,13 +25179,31 @@ var wrapper_default = import_websocket.default;
|
|
|
25179
25179
|
|
|
25180
25180
|
// src/bridge/connection/create-ws-bridge.ts
|
|
25181
25181
|
import https from "node:https";
|
|
25182
|
+
|
|
25183
|
+
// src/net/apply-cli-outbound-network-prefs.ts
|
|
25184
|
+
import dns from "node:dns";
|
|
25185
|
+
var applied = false;
|
|
25186
|
+
function applyCliOutboundNetworkPreferences() {
|
|
25187
|
+
if (applied) return;
|
|
25188
|
+
applied = true;
|
|
25189
|
+
try {
|
|
25190
|
+
dns.setDefaultResultOrder("ipv4first");
|
|
25191
|
+
} catch {
|
|
25192
|
+
}
|
|
25193
|
+
}
|
|
25194
|
+
|
|
25195
|
+
// src/bridge/connection/create-ws-bridge.ts
|
|
25182
25196
|
var BRIDGE_AUTH_ERROR_HEADER = "x-bridge-auth-error";
|
|
25183
25197
|
var BRIDGE_AUTH_ERROR_TOKEN_INVALID = "token_invalid";
|
|
25184
25198
|
function createWsBridge(options) {
|
|
25185
25199
|
const { url: url2, onMessage, onOpen, onClose, onError: onError2, onAuthInvalid, clientPingIntervalMs } = options;
|
|
25186
|
-
|
|
25200
|
+
applyCliOutboundNetworkPreferences();
|
|
25201
|
+
const wsOptions = {
|
|
25202
|
+
perMessageDeflate: false,
|
|
25203
|
+
family: 4
|
|
25204
|
+
};
|
|
25187
25205
|
if (url2.startsWith("wss://")) {
|
|
25188
|
-
wsOptions.agent = new https.Agent({ rejectUnauthorized: false });
|
|
25206
|
+
wsOptions.agent = new https.Agent({ rejectUnauthorized: false, family: 4 });
|
|
25189
25207
|
}
|
|
25190
25208
|
const ws = new wrapper_default(url2, wsOptions);
|
|
25191
25209
|
let clientPingTimer = null;
|
|
@@ -25408,7 +25426,7 @@ function beginMainBridgeDeferredDisconnect(state, code, reason, log2, willReconn
|
|
|
25408
25426
|
});
|
|
25409
25427
|
}
|
|
25410
25428
|
function clearMainBridgeReconnectQuietOnOpen(state, log2) {
|
|
25411
|
-
clearReconnectQuietOnSuccessfulConnection(state.mainQuiet, log2, "
|
|
25429
|
+
clearReconnectQuietOnSuccessfulConnection(state.mainQuiet, log2, "Bridge connection restored.");
|
|
25412
25430
|
}
|
|
25413
25431
|
function scheduleMainBridgeReconnect(state, connect, log2) {
|
|
25414
25432
|
if (state.closedByUser || state.currentWs != null) return;
|
|
@@ -25445,7 +25463,7 @@ function clearFirehoseReconnectQuietOnOpen(ctx, log2) {
|
|
|
25445
25463
|
clearReconnectQuietOnSuccessfulConnection(
|
|
25446
25464
|
ctx.firehoseQuiet,
|
|
25447
25465
|
log2,
|
|
25448
|
-
|
|
25466
|
+
"Preview tunnel restored (local HTTP proxy and dev logs)."
|
|
25449
25467
|
);
|
|
25450
25468
|
}
|
|
25451
25469
|
|
|
@@ -30256,34 +30274,31 @@ function reportGitRepos(getWs, log2) {
|
|
|
30256
30274
|
|
|
30257
30275
|
// src/bridge/connection/close-bridge-connection.ts
|
|
30258
30276
|
async function closeBridgeConnection(state, acpManager, devServerManager, log2) {
|
|
30259
|
-
log2
|
|
30277
|
+
const say = log2 ?? logImmediate;
|
|
30278
|
+
say("Cleaning up connections\u2026");
|
|
30260
30279
|
await new Promise((resolve15) => setImmediate(resolve15));
|
|
30261
|
-
if (devServerManager) {
|
|
30262
|
-
log2?.("Requesting dev server processes to stop\u2026");
|
|
30263
|
-
await devServerManager.shutdownAllGraceful();
|
|
30264
|
-
}
|
|
30265
30280
|
state.closedByUser = true;
|
|
30266
30281
|
clearReconnectQuietTimer(state.mainQuiet);
|
|
30267
30282
|
clearReconnectQuietTimer(state.firehoseQuiet);
|
|
30268
30283
|
if (state.reconnectTimeout != null) {
|
|
30269
|
-
|
|
30284
|
+
say("Cancelling bridge reconnect timer\u2026");
|
|
30270
30285
|
clearTimeout(state.reconnectTimeout);
|
|
30271
30286
|
state.reconnectTimeout = null;
|
|
30272
30287
|
}
|
|
30273
30288
|
if (state.firehoseReconnectTimeout != null) {
|
|
30274
|
-
|
|
30289
|
+
say("Cancelling preview tunnel reconnect timer\u2026");
|
|
30275
30290
|
clearTimeout(state.firehoseReconnectTimeout);
|
|
30276
30291
|
state.firehoseReconnectTimeout = null;
|
|
30277
30292
|
}
|
|
30278
30293
|
if (state.firehoseHandle) {
|
|
30279
|
-
|
|
30294
|
+
say("Closing preview tunnel (local HTTP proxy and dev logs)\u2026");
|
|
30280
30295
|
state.firehoseHandle.close();
|
|
30281
30296
|
state.firehoseHandle = null;
|
|
30282
30297
|
}
|
|
30283
|
-
|
|
30298
|
+
say("Disconnecting local agent\u2026");
|
|
30284
30299
|
acpManager.disconnect();
|
|
30285
30300
|
if (state.currentWs) {
|
|
30286
|
-
|
|
30301
|
+
say("Closing bridge connection to the cloud\u2026");
|
|
30287
30302
|
state.currentWs.removeAllListeners();
|
|
30288
30303
|
const wsState = state.currentWs.readyState;
|
|
30289
30304
|
if (wsState === 1 || wsState === 2) {
|
|
@@ -30296,12 +30311,17 @@ async function closeBridgeConnection(state, acpManager, devServerManager, log2)
|
|
|
30296
30311
|
}
|
|
30297
30312
|
state.currentWs = null;
|
|
30298
30313
|
}
|
|
30314
|
+
if (devServerManager) {
|
|
30315
|
+
say("Stopping local dev server processes\u2026");
|
|
30316
|
+
await devServerManager.shutdownAllGraceful();
|
|
30317
|
+
}
|
|
30318
|
+
say("Shutdown complete.");
|
|
30299
30319
|
}
|
|
30300
30320
|
|
|
30301
30321
|
// src/git/session-git-queue.ts
|
|
30302
30322
|
import { execFile as execFile2 } from "node:child_process";
|
|
30323
|
+
import { readFile, stat } from "node:fs/promises";
|
|
30303
30324
|
import { promisify as promisify2 } from "node:util";
|
|
30304
|
-
import * as fs4 from "node:fs";
|
|
30305
30325
|
import * as path5 from "node:path";
|
|
30306
30326
|
|
|
30307
30327
|
// src/git/pre-turn-snapshot.ts
|
|
@@ -30416,11 +30436,11 @@ function snapshotFilePath(agentCwd, runId) {
|
|
|
30416
30436
|
// src/git/session-git-queue.ts
|
|
30417
30437
|
var execFileAsync2 = promisify2(execFile2);
|
|
30418
30438
|
var MAX_FULL_FILE_TEXT_BYTES = 512 * 1024;
|
|
30419
|
-
function readWorkspaceFileAsUtf8(absPath) {
|
|
30439
|
+
async function readWorkspaceFileAsUtf8(absPath) {
|
|
30420
30440
|
try {
|
|
30421
|
-
const st =
|
|
30441
|
+
const st = await stat(absPath);
|
|
30422
30442
|
if (!st.isFile() || st.size > MAX_FULL_FILE_TEXT_BYTES) return void 0;
|
|
30423
|
-
return
|
|
30443
|
+
return await readFile(absPath, "utf8");
|
|
30424
30444
|
} catch {
|
|
30425
30445
|
return void 0;
|
|
30426
30446
|
}
|
|
@@ -30430,7 +30450,7 @@ async function collectTurnGitDiffFromPreTurnSnapshot(options) {
|
|
|
30430
30450
|
const filePath = snapshotFilePath(agentCwd, runId);
|
|
30431
30451
|
let data;
|
|
30432
30452
|
try {
|
|
30433
|
-
const raw =
|
|
30453
|
+
const raw = await readFile(filePath, "utf8");
|
|
30434
30454
|
data = JSON.parse(raw);
|
|
30435
30455
|
} catch (e) {
|
|
30436
30456
|
log2(
|
|
@@ -30474,7 +30494,7 @@ async function collectTurnGitDiffFromPreTurnSnapshot(options) {
|
|
|
30474
30494
|
if (!patchContent.trim()) continue;
|
|
30475
30495
|
const displayPath = multiRepo ? `${slug}/${rel}` : rel;
|
|
30476
30496
|
const absFile = path5.join(repo.path, rel);
|
|
30477
|
-
const newText = readWorkspaceFileAsUtf8(absFile);
|
|
30497
|
+
const newText = await readWorkspaceFileAsUtf8(absFile);
|
|
30478
30498
|
sendSessionUpdate({
|
|
30479
30499
|
type: "session_file_change",
|
|
30480
30500
|
sessionId,
|
|
@@ -30529,7 +30549,6 @@ async function sendPromptToAgent(options) {
|
|
|
30529
30549
|
} catch (err) {
|
|
30530
30550
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
30531
30551
|
log2(`[Agent] Send failed: ${errMsg}`);
|
|
30532
|
-
if (err instanceof Error && err.stack) log2(`[Agent] ${err.stack}`);
|
|
30533
30552
|
sendResult({
|
|
30534
30553
|
type: "prompt_result",
|
|
30535
30554
|
id: promptId,
|
|
@@ -30542,7 +30561,7 @@ async function sendPromptToAgent(options) {
|
|
|
30542
30561
|
}
|
|
30543
30562
|
|
|
30544
30563
|
// src/acp/ensure-acp-client.ts
|
|
30545
|
-
import * as
|
|
30564
|
+
import * as fs4 from "node:fs";
|
|
30546
30565
|
import * as path9 from "node:path";
|
|
30547
30566
|
|
|
30548
30567
|
// src/error-message.ts
|
|
@@ -30555,9 +30574,77 @@ function errorMessage(err) {
|
|
|
30555
30574
|
return String(err);
|
|
30556
30575
|
}
|
|
30557
30576
|
|
|
30558
|
-
// src/acp/clients/acp-client.ts
|
|
30577
|
+
// src/acp/clients/sdk-stdio-acp-client.ts
|
|
30559
30578
|
import { spawn as spawn2 } from "node:child_process";
|
|
30579
|
+
import { mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
30580
|
+
import { dirname } from "node:path";
|
|
30560
30581
|
import { Readable, Writable } from "node:stream";
|
|
30582
|
+
|
|
30583
|
+
// src/files/diff/unified-diff.ts
|
|
30584
|
+
function computeLineDiff(oldText, newText) {
|
|
30585
|
+
const oldLines = oldText.split("\n");
|
|
30586
|
+
const newLines = newText.split("\n");
|
|
30587
|
+
const m = oldLines.length;
|
|
30588
|
+
const n = newLines.length;
|
|
30589
|
+
const dp = Array(m + 1);
|
|
30590
|
+
for (let i2 = 0; i2 <= m; i2++) dp[i2] = Array(n + 1).fill(0);
|
|
30591
|
+
for (let i2 = 1; i2 <= m; i2++) {
|
|
30592
|
+
for (let j2 = 1; j2 <= n; j2++) {
|
|
30593
|
+
if (oldLines[i2 - 1] === newLines[j2 - 1]) {
|
|
30594
|
+
dp[i2][j2] = dp[i2 - 1][j2 - 1] + 1;
|
|
30595
|
+
} else {
|
|
30596
|
+
dp[i2][j2] = Math.max(dp[i2 - 1][j2], dp[i2][j2 - 1]);
|
|
30597
|
+
}
|
|
30598
|
+
}
|
|
30599
|
+
}
|
|
30600
|
+
const result = [];
|
|
30601
|
+
let i = m;
|
|
30602
|
+
let j = n;
|
|
30603
|
+
while (i > 0 || j > 0) {
|
|
30604
|
+
if (i > 0 && j > 0 && oldLines[i - 1] === newLines[j - 1]) {
|
|
30605
|
+
result.unshift({ type: "context", line: oldLines[i - 1] });
|
|
30606
|
+
i--;
|
|
30607
|
+
j--;
|
|
30608
|
+
} else if (j > 0 && (i === 0 || dp[i][j - 1] >= dp[i - 1][j])) {
|
|
30609
|
+
result.unshift({ type: "add", line: newLines[j - 1] });
|
|
30610
|
+
j--;
|
|
30611
|
+
} else {
|
|
30612
|
+
result.unshift({ type: "remove", line: oldLines[i - 1] });
|
|
30613
|
+
i--;
|
|
30614
|
+
}
|
|
30615
|
+
}
|
|
30616
|
+
return result;
|
|
30617
|
+
}
|
|
30618
|
+
function editSnippetToUnifiedDiff(filePath, oldText, newText) {
|
|
30619
|
+
const lines = computeLineDiff(oldText, newText);
|
|
30620
|
+
const out = [`--- ${filePath}`, `+++ ${filePath}`];
|
|
30621
|
+
for (const d of lines) {
|
|
30622
|
+
if (d.type === "add") out.push(`+${d.line}`);
|
|
30623
|
+
else if (d.type === "remove") out.push(`-${d.line}`);
|
|
30624
|
+
else out.push(` ${d.line}`);
|
|
30625
|
+
}
|
|
30626
|
+
return out.join("\n");
|
|
30627
|
+
}
|
|
30628
|
+
|
|
30629
|
+
// src/acp/safe-fs-path.ts
|
|
30630
|
+
import * as path6 from "node:path";
|
|
30631
|
+
function resolveSafePathUnderCwd(cwd, filePath) {
|
|
30632
|
+
const trimmed2 = filePath.trim();
|
|
30633
|
+
if (!trimmed2) return null;
|
|
30634
|
+
const normalizedCwd = path6.resolve(cwd);
|
|
30635
|
+
const resolved = path6.isAbsolute(trimmed2) ? path6.normalize(trimmed2) : path6.resolve(normalizedCwd, trimmed2);
|
|
30636
|
+
const rel = path6.relative(normalizedCwd, resolved);
|
|
30637
|
+
if (rel.startsWith("..") || path6.isAbsolute(rel)) return null;
|
|
30638
|
+
return resolved;
|
|
30639
|
+
}
|
|
30640
|
+
function toDisplayPathRelativeToCwd(cwd, absolutePath) {
|
|
30641
|
+
const normalizedCwd = path6.resolve(cwd);
|
|
30642
|
+
const rel = path6.relative(normalizedCwd, path6.resolve(absolutePath));
|
|
30643
|
+
if (!rel || rel === "") return path6.basename(absolutePath);
|
|
30644
|
+
return rel.split(path6.sep).join("/");
|
|
30645
|
+
}
|
|
30646
|
+
|
|
30647
|
+
// src/acp/clients/sdk-stdio-acp-client.ts
|
|
30561
30648
|
function formatSpawnError(err, command) {
|
|
30562
30649
|
if (err.code === "ENOENT") {
|
|
30563
30650
|
return `Command "${command}" not found. Install the agent (e.g. Cursor CLI) or add it to PATH.`;
|
|
@@ -30572,9 +30659,26 @@ function toErrorMessage(err) {
|
|
|
30572
30659
|
if (err != null && typeof err === "object") return JSON.stringify(err);
|
|
30573
30660
|
return String(err);
|
|
30574
30661
|
}
|
|
30575
|
-
|
|
30576
|
-
|
|
30577
|
-
const
|
|
30662
|
+
function sliceFileContentRange(content, line, limit) {
|
|
30663
|
+
if (line == null && limit == null) return content;
|
|
30664
|
+
const lines = content.split("\n");
|
|
30665
|
+
const start = line != null && line > 0 ? line - 1 : 0;
|
|
30666
|
+
const end = limit != null && limit > 0 ? start + limit : lines.length;
|
|
30667
|
+
return lines.slice(start, end).join("\n");
|
|
30668
|
+
}
|
|
30669
|
+
function bridgePayloadFromSdkSessionNotification(params) {
|
|
30670
|
+
return { sessionId: params.sessionId, ...params.update };
|
|
30671
|
+
}
|
|
30672
|
+
async function createSdkStdioAcpClient(options) {
|
|
30673
|
+
const { ClientSideConnection: ClientSideConnection2, ndJsonStream: ndJsonStream2, PROTOCOL_VERSION: PROTOCOL_VERSION2 } = await Promise.resolve().then(() => (init_acp(), acp_exports));
|
|
30674
|
+
const {
|
|
30675
|
+
command,
|
|
30676
|
+
cwd = getBridgeWorkspaceDirectory(),
|
|
30677
|
+
onSessionUpdate,
|
|
30678
|
+
onFileChange,
|
|
30679
|
+
killSubprocessAfterCancelMs,
|
|
30680
|
+
onAgentSubprocessExit
|
|
30681
|
+
} = options;
|
|
30578
30682
|
const isWindows = process.platform === "win32";
|
|
30579
30683
|
const child = spawn2(command[0], command.slice(1), {
|
|
30580
30684
|
cwd,
|
|
@@ -30582,6 +30686,9 @@ async function createAcpClient(options) {
|
|
|
30582
30686
|
env: process.env,
|
|
30583
30687
|
shell: isWindows
|
|
30584
30688
|
});
|
|
30689
|
+
child.once("close", (code, signal) => {
|
|
30690
|
+
onAgentSubprocessExit?.({ code, signal });
|
|
30691
|
+
});
|
|
30585
30692
|
return new Promise((resolve15, reject) => {
|
|
30586
30693
|
child.on("error", (err) => {
|
|
30587
30694
|
child.kill();
|
|
@@ -30593,11 +30700,43 @@ async function createAcpClient(options) {
|
|
|
30593
30700
|
const readable = Readable.toWeb(child.stdout);
|
|
30594
30701
|
const stream = ndJsonStream2(writable, readable);
|
|
30595
30702
|
const client = (_agent) => ({
|
|
30596
|
-
async requestPermission(
|
|
30597
|
-
|
|
30703
|
+
async requestPermission(params) {
|
|
30704
|
+
const opt = params?.options?.[0];
|
|
30705
|
+
if (opt && typeof opt.optionId === "string") {
|
|
30706
|
+
return { outcome: { outcome: "selected", optionId: opt.optionId } };
|
|
30707
|
+
}
|
|
30708
|
+
return { outcome: { outcome: "cancelled" } };
|
|
30709
|
+
},
|
|
30710
|
+
async readTextFile(params) {
|
|
30711
|
+
const abs = resolveSafePathUnderCwd(cwd, params.path);
|
|
30712
|
+
if (!abs) throw new Error("Invalid or disallowed path");
|
|
30713
|
+
try {
|
|
30714
|
+
let content = readFileSync2(abs, "utf8");
|
|
30715
|
+
content = sliceFileContentRange(content, params.line, params.limit);
|
|
30716
|
+
return { content };
|
|
30717
|
+
} catch (e) {
|
|
30718
|
+
if (e.code === "ENOENT") return { content: "" };
|
|
30719
|
+
throw e;
|
|
30720
|
+
}
|
|
30721
|
+
},
|
|
30722
|
+
async writeTextFile(params) {
|
|
30723
|
+
const abs = resolveSafePathUnderCwd(cwd, params.path);
|
|
30724
|
+
if (!abs) throw new Error("Invalid or disallowed path");
|
|
30725
|
+
let oldText = "";
|
|
30726
|
+
try {
|
|
30727
|
+
oldText = readFileSync2(abs, "utf8");
|
|
30728
|
+
} catch (e) {
|
|
30729
|
+
if (e.code !== "ENOENT") throw e;
|
|
30730
|
+
}
|
|
30731
|
+
mkdirSync2(dirname(abs), { recursive: true });
|
|
30732
|
+
writeFileSync2(abs, params.content, "utf8");
|
|
30733
|
+
const displayPath = toDisplayPathRelativeToCwd(cwd, abs);
|
|
30734
|
+
const patchContent = editSnippetToUnifiedDiff(displayPath, oldText, params.content);
|
|
30735
|
+
onFileChange?.({ path: displayPath, oldText, newText: params.content, patchContent });
|
|
30736
|
+
return {};
|
|
30598
30737
|
},
|
|
30599
30738
|
async sessionUpdate(params) {
|
|
30600
|
-
onSessionUpdate?.(params);
|
|
30739
|
+
onSessionUpdate?.(bridgePayloadFromSdkSessionNotification(params));
|
|
30601
30740
|
}
|
|
30602
30741
|
});
|
|
30603
30742
|
const connection = new ClientSideConnection2(client, stream);
|
|
@@ -30605,11 +30744,13 @@ async function createAcpClient(options) {
|
|
|
30605
30744
|
child.kill();
|
|
30606
30745
|
});
|
|
30607
30746
|
await connection.initialize({
|
|
30608
|
-
protocolVersion:
|
|
30609
|
-
|
|
30747
|
+
protocolVersion: PROTOCOL_VERSION2,
|
|
30748
|
+
clientCapabilities: {
|
|
30749
|
+
fs: { readTextFile: true, writeTextFile: true }
|
|
30750
|
+
},
|
|
30610
30751
|
clientInfo: { name: "buildautomaton-cli", version: "0.1.0" }
|
|
30611
30752
|
});
|
|
30612
|
-
const newSessionRes = await connection.newSession({
|
|
30753
|
+
const newSessionRes = await connection.newSession({ cwd, mcpServers: [] });
|
|
30613
30754
|
const sessionId = newSessionRes.sessionId;
|
|
30614
30755
|
resolve15({
|
|
30615
30756
|
sessionId,
|
|
@@ -30617,7 +30758,7 @@ async function createAcpClient(options) {
|
|
|
30617
30758
|
try {
|
|
30618
30759
|
const response = await connection.prompt({
|
|
30619
30760
|
sessionId,
|
|
30620
|
-
prompt: { type: "text", text: prompt }
|
|
30761
|
+
prompt: [{ type: "text", text: prompt }]
|
|
30621
30762
|
});
|
|
30622
30763
|
const r = response;
|
|
30623
30764
|
const cancelled = (r?.stopReason ?? "").toLowerCase() === "cancelled";
|
|
@@ -30635,9 +30776,17 @@ async function createAcpClient(options) {
|
|
|
30635
30776
|
}
|
|
30636
30777
|
},
|
|
30637
30778
|
async cancel() {
|
|
30638
|
-
|
|
30639
|
-
|
|
30640
|
-
|
|
30779
|
+
try {
|
|
30780
|
+
await connection.cancel({ sessionId });
|
|
30781
|
+
} catch {
|
|
30782
|
+
}
|
|
30783
|
+
if (killSubprocessAfterCancelMs != null && killSubprocessAfterCancelMs >= 0) {
|
|
30784
|
+
const t = setTimeout(() => {
|
|
30785
|
+
if (child.exitCode == null && child.signalCode == null) {
|
|
30786
|
+
child.kill("SIGTERM");
|
|
30787
|
+
}
|
|
30788
|
+
}, killSubprocessAfterCancelMs);
|
|
30789
|
+
t.unref?.();
|
|
30641
30790
|
}
|
|
30642
30791
|
},
|
|
30643
30792
|
resolveRequest() {
|
|
@@ -30660,81 +30809,38 @@ function isCodexAcpCommand(command) {
|
|
|
30660
30809
|
const i = command.indexOf("@zed-industries/codex-acp");
|
|
30661
30810
|
return i >= 0 && (i === 0 || command[i - 1] === "npx" || command[i - 1] === "bunx");
|
|
30662
30811
|
}
|
|
30812
|
+
function buildCodexAcpSpawnCommand(base, _sessionMode) {
|
|
30813
|
+
return [...base];
|
|
30814
|
+
}
|
|
30663
30815
|
async function createCodexAcpClient(options) {
|
|
30664
|
-
const
|
|
30665
|
-
|
|
30816
|
+
const base = options.command?.length && options.command.some((a) => a.includes("codex-acp")) ? options.command : [...DEFAULT_CODEX_ACP_COMMAND];
|
|
30817
|
+
const command = buildCodexAcpSpawnCommand(base, options.sessionMode);
|
|
30818
|
+
return createSdkStdioAcpClient({ ...options, command });
|
|
30819
|
+
}
|
|
30820
|
+
|
|
30821
|
+
// src/acp/clients/claude-code-acp-client.ts
|
|
30822
|
+
function buildClaudeCodeAcpSpawnCommand(base, sessionMode) {
|
|
30823
|
+
if (!sessionMode) return [...base];
|
|
30824
|
+
const m = sessionMode.trim();
|
|
30825
|
+
if (m === "plan") return [...base, "--permission-mode", "plan"];
|
|
30826
|
+
return [...base];
|
|
30827
|
+
}
|
|
30828
|
+
async function createClaudeCodeAcpClient(options) {
|
|
30829
|
+
const command = buildClaudeCodeAcpSpawnCommand(options.command, options.sessionMode);
|
|
30830
|
+
return createSdkStdioAcpClient({
|
|
30831
|
+
...options,
|
|
30832
|
+
command,
|
|
30833
|
+
/** Claude-based agents sometimes ignore `session/cancel`; unblocks stop / stuck prompt. */
|
|
30834
|
+
killSubprocessAfterCancelMs: options.killSubprocessAfterCancelMs ?? 1e3
|
|
30835
|
+
});
|
|
30666
30836
|
}
|
|
30667
30837
|
|
|
30668
30838
|
// src/acp/clients/cursor-acp-client.ts
|
|
30669
|
-
import { readFileSync as readFileSync3, writeFileSync as
|
|
30670
|
-
import { dirname } from "node:path";
|
|
30839
|
+
import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3 } from "node:fs";
|
|
30840
|
+
import { dirname as dirname2 } from "node:path";
|
|
30671
30841
|
import { spawn as spawn3 } from "node:child_process";
|
|
30672
30842
|
import * as readline from "node:readline";
|
|
30673
30843
|
|
|
30674
|
-
// src/acp/safe-fs-path.ts
|
|
30675
|
-
import * as path6 from "node:path";
|
|
30676
|
-
function resolveSafePathUnderCwd(cwd, filePath) {
|
|
30677
|
-
const trimmed2 = filePath.trim();
|
|
30678
|
-
if (!trimmed2) return null;
|
|
30679
|
-
const normalizedCwd = path6.resolve(cwd);
|
|
30680
|
-
const resolved = path6.isAbsolute(trimmed2) ? path6.normalize(trimmed2) : path6.resolve(normalizedCwd, trimmed2);
|
|
30681
|
-
const rel = path6.relative(normalizedCwd, resolved);
|
|
30682
|
-
if (rel.startsWith("..") || path6.isAbsolute(rel)) return null;
|
|
30683
|
-
return resolved;
|
|
30684
|
-
}
|
|
30685
|
-
function toDisplayPathRelativeToCwd(cwd, absolutePath) {
|
|
30686
|
-
const normalizedCwd = path6.resolve(cwd);
|
|
30687
|
-
const rel = path6.relative(normalizedCwd, path6.resolve(absolutePath));
|
|
30688
|
-
if (!rel || rel === "") return path6.basename(absolutePath);
|
|
30689
|
-
return rel.split(path6.sep).join("/");
|
|
30690
|
-
}
|
|
30691
|
-
|
|
30692
|
-
// src/files/diff/unified-diff.ts
|
|
30693
|
-
function computeLineDiff(oldText, newText) {
|
|
30694
|
-
const oldLines = oldText.split("\n");
|
|
30695
|
-
const newLines = newText.split("\n");
|
|
30696
|
-
const m = oldLines.length;
|
|
30697
|
-
const n = newLines.length;
|
|
30698
|
-
const dp = Array(m + 1);
|
|
30699
|
-
for (let i2 = 0; i2 <= m; i2++) dp[i2] = Array(n + 1).fill(0);
|
|
30700
|
-
for (let i2 = 1; i2 <= m; i2++) {
|
|
30701
|
-
for (let j2 = 1; j2 <= n; j2++) {
|
|
30702
|
-
if (oldLines[i2 - 1] === newLines[j2 - 1]) {
|
|
30703
|
-
dp[i2][j2] = dp[i2 - 1][j2 - 1] + 1;
|
|
30704
|
-
} else {
|
|
30705
|
-
dp[i2][j2] = Math.max(dp[i2 - 1][j2], dp[i2][j2 - 1]);
|
|
30706
|
-
}
|
|
30707
|
-
}
|
|
30708
|
-
}
|
|
30709
|
-
const result = [];
|
|
30710
|
-
let i = m;
|
|
30711
|
-
let j = n;
|
|
30712
|
-
while (i > 0 || j > 0) {
|
|
30713
|
-
if (i > 0 && j > 0 && oldLines[i - 1] === newLines[j - 1]) {
|
|
30714
|
-
result.unshift({ type: "context", line: oldLines[i - 1] });
|
|
30715
|
-
i--;
|
|
30716
|
-
j--;
|
|
30717
|
-
} else if (j > 0 && (i === 0 || dp[i][j - 1] >= dp[i - 1][j])) {
|
|
30718
|
-
result.unshift({ type: "add", line: newLines[j - 1] });
|
|
30719
|
-
j--;
|
|
30720
|
-
} else {
|
|
30721
|
-
result.unshift({ type: "remove", line: oldLines[i - 1] });
|
|
30722
|
-
i--;
|
|
30723
|
-
}
|
|
30724
|
-
}
|
|
30725
|
-
return result;
|
|
30726
|
-
}
|
|
30727
|
-
function editSnippetToUnifiedDiff(filePath, oldText, newText) {
|
|
30728
|
-
const lines = computeLineDiff(oldText, newText);
|
|
30729
|
-
const out = [`--- ${filePath}`, `+++ ${filePath}`];
|
|
30730
|
-
for (const d of lines) {
|
|
30731
|
-
if (d.type === "add") out.push(`+${d.line}`);
|
|
30732
|
-
else if (d.type === "remove") out.push(`-${d.line}`);
|
|
30733
|
-
else out.push(` ${d.line}`);
|
|
30734
|
-
}
|
|
30735
|
-
return out.join("\n");
|
|
30736
|
-
}
|
|
30737
|
-
|
|
30738
30844
|
// src/acp/format-session-update-kind-for-log.ts
|
|
30739
30845
|
var SESSION_UPDATE_KIND_LABELS = {
|
|
30740
30846
|
tool_call: "Tool call",
|
|
@@ -30772,8 +30878,15 @@ function sliceLinesByRange(content, line, limit) {
|
|
|
30772
30878
|
const end = limit != null && limit > 0 ? start + limit : lines.length;
|
|
30773
30879
|
return lines.slice(start, end).join("\n");
|
|
30774
30880
|
}
|
|
30881
|
+
function buildCursorAcpSpawnCommand(base, sessionMode) {
|
|
30882
|
+
if (!sessionMode) return [...base];
|
|
30883
|
+
const m = sessionMode.trim();
|
|
30884
|
+
if (m !== "ask" && m !== "plan") return [...base];
|
|
30885
|
+
return [...base, "--mode", m];
|
|
30886
|
+
}
|
|
30775
30887
|
async function createCursorAcpClient(options) {
|
|
30776
|
-
const
|
|
30888
|
+
const command = buildCursorAcpSpawnCommand(options.command, options.sessionMode);
|
|
30889
|
+
const { cwd = getBridgeWorkspaceDirectory(), onSessionUpdate, onRequest, onFileChange } = options;
|
|
30777
30890
|
const dbgFs = process.env.BUILDAMATON_DEBUG_ACP_FS === "1";
|
|
30778
30891
|
const isWindows = process.platform === "win32";
|
|
30779
30892
|
const child = spawn3(command[0], command.slice(1), {
|
|
@@ -30905,8 +31018,8 @@ async function createCursorAcpClient(options) {
|
|
|
30905
31018
|
}
|
|
30906
31019
|
}
|
|
30907
31020
|
try {
|
|
30908
|
-
|
|
30909
|
-
|
|
31021
|
+
mkdirSync3(dirname2(abs), { recursive: true });
|
|
31022
|
+
writeFileSync3(abs, newText, "utf8");
|
|
30910
31023
|
} catch (e) {
|
|
30911
31024
|
respondJsonRpcError(id, -32e3, e instanceof Error ? e.message : String(e));
|
|
30912
31025
|
return;
|
|
@@ -31004,8 +31117,20 @@ async function createCursorAcpClient(options) {
|
|
|
31004
31117
|
var AGENT_TYPE_DEFAULT_COMMANDS = {
|
|
31005
31118
|
"cursor-cli": ["agent", "acp"],
|
|
31006
31119
|
"codex-acp": [...DEFAULT_CODEX_ACP_COMMAND],
|
|
31007
|
-
|
|
31120
|
+
/** ACP stdio agent; `@anthropic-ai/claude-code` is the interactive CLI and does not speak ACP on stdout. */
|
|
31121
|
+
"claude-code": ["npx", "--yes", "@agentclientprotocol/claude-agent-acp"]
|
|
31122
|
+
};
|
|
31123
|
+
var AGENT_TYPE_DISPLAY_NAMES = {
|
|
31124
|
+
"cursor-cli": "Cursor",
|
|
31125
|
+
"codex-acp": "Codex",
|
|
31126
|
+
"claude-code": "Claude Code"
|
|
31008
31127
|
};
|
|
31128
|
+
function getAgentTypeDisplayName(agentType) {
|
|
31129
|
+
if (agentType == null || agentType === "") return "Unknown agent";
|
|
31130
|
+
const known = AGENT_TYPE_DISPLAY_NAMES[agentType];
|
|
31131
|
+
if (known) return known;
|
|
31132
|
+
return agentType.split(/[-_]/).filter(Boolean).map((w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase()).join(" ");
|
|
31133
|
+
}
|
|
31009
31134
|
function useCursorAcp(agentType, command) {
|
|
31010
31135
|
if (agentType === "cursor-cli") return true;
|
|
31011
31136
|
return command[0] === "agent" && command[1] === "acp";
|
|
@@ -31018,14 +31143,33 @@ function resolveAgentCommand(preferredAgentType) {
|
|
|
31018
31143
|
if (!preferredAgentType) return null;
|
|
31019
31144
|
const command = AGENT_TYPE_DEFAULT_COMMANDS[preferredAgentType];
|
|
31020
31145
|
if (!command?.length) return null;
|
|
31021
|
-
|
|
31022
|
-
|
|
31023
|
-
|
|
31146
|
+
if (useCursorAcp(preferredAgentType, command)) {
|
|
31147
|
+
return {
|
|
31148
|
+
command,
|
|
31149
|
+
label: preferredAgentType,
|
|
31150
|
+
createClient: createCursorAcpClient,
|
|
31151
|
+
spawnCommandForSession: (sessionMode) => buildCursorAcpSpawnCommand(command, sessionMode)
|
|
31152
|
+
};
|
|
31153
|
+
}
|
|
31154
|
+
if (useCodexAcp(preferredAgentType, command)) {
|
|
31155
|
+
return {
|
|
31156
|
+
command,
|
|
31157
|
+
label: preferredAgentType,
|
|
31158
|
+
createClient: createCodexAcpClient,
|
|
31159
|
+
spawnCommandForSession: (sessionMode) => buildCodexAcpSpawnCommand(command, sessionMode)
|
|
31160
|
+
};
|
|
31161
|
+
}
|
|
31162
|
+
return {
|
|
31163
|
+
command,
|
|
31164
|
+
label: preferredAgentType,
|
|
31165
|
+
createClient: createClaudeCodeAcpClient,
|
|
31166
|
+
spawnCommandForSession: (sessionMode) => buildClaudeCodeAcpSpawnCommand(command, sessionMode)
|
|
31167
|
+
};
|
|
31024
31168
|
}
|
|
31025
31169
|
|
|
31026
31170
|
// src/acp/session-file-change-path-kind.ts
|
|
31027
31171
|
import { execFileSync as execFileSync3 } from "node:child_process";
|
|
31028
|
-
import { existsSync, statSync
|
|
31172
|
+
import { existsSync, statSync } from "node:fs";
|
|
31029
31173
|
|
|
31030
31174
|
// src/git/get-git-repo-root-sync.ts
|
|
31031
31175
|
import { execFileSync } from "node:child_process";
|
|
@@ -31134,7 +31278,7 @@ function getSessionFileChangeDirectoryFlags(cwd, displayPath) {
|
|
|
31134
31278
|
const abs = tryWorkspaceDisplayToAbs(cwd, displayPath);
|
|
31135
31279
|
if (abs && existsSync(abs)) {
|
|
31136
31280
|
try {
|
|
31137
|
-
if (
|
|
31281
|
+
if (statSync(abs).isDirectory()) {
|
|
31138
31282
|
return { isDirectory: true, directoryRemoved: false };
|
|
31139
31283
|
}
|
|
31140
31284
|
return { isDirectory: false, directoryRemoved: false };
|
|
@@ -31680,7 +31824,8 @@ async function ensureAcpClient(options) {
|
|
|
31680
31824
|
state.lastAcpStartError = "No agent type: ensure the app sends agentType on prompts or agent_config for this bridge.";
|
|
31681
31825
|
return null;
|
|
31682
31826
|
}
|
|
31683
|
-
const
|
|
31827
|
+
const fullCmd = resolved.spawnCommandForSession(mode);
|
|
31828
|
+
const agentKey = `${resolved.label}::${fullCmd.join("\0")}`;
|
|
31684
31829
|
if (state.acpHandle && state.acpAgentKey !== agentKey) {
|
|
31685
31830
|
try {
|
|
31686
31831
|
state.acpHandle.disconnect();
|
|
@@ -31694,7 +31839,7 @@ async function ensureAcpClient(options) {
|
|
|
31694
31839
|
if (!state.acpStartPromise) {
|
|
31695
31840
|
let statOk = false;
|
|
31696
31841
|
try {
|
|
31697
|
-
const st =
|
|
31842
|
+
const st = fs4.statSync(targetCwd);
|
|
31698
31843
|
statOk = st.isDirectory();
|
|
31699
31844
|
if (!statOk) {
|
|
31700
31845
|
state.lastAcpStartError = `Agent cwd is not a directory: ${targetCwd}`;
|
|
@@ -31707,8 +31852,6 @@ async function ensureAcpClient(options) {
|
|
|
31707
31852
|
if (!statOk) {
|
|
31708
31853
|
return null;
|
|
31709
31854
|
}
|
|
31710
|
-
const modeFlag = mode && ["ask", "plan"].includes(mode) ? ["--mode", mode] : [];
|
|
31711
|
-
const fullCmd = [...resolved.command, ...modeFlag];
|
|
31712
31855
|
const hooks = buildAcpSessionBridgeHooks({
|
|
31713
31856
|
routing,
|
|
31714
31857
|
getSendSessionUpdate: () => sendSessionUpdate,
|
|
@@ -31716,8 +31859,15 @@ async function ensureAcpClient(options) {
|
|
|
31716
31859
|
log: log2
|
|
31717
31860
|
});
|
|
31718
31861
|
state.acpStartPromise = resolved.createClient({
|
|
31719
|
-
command:
|
|
31862
|
+
command: resolved.command,
|
|
31863
|
+
sessionMode: mode,
|
|
31720
31864
|
cwd: targetCwd,
|
|
31865
|
+
onAgentSubprocessExit: () => {
|
|
31866
|
+
state.acpHandle = null;
|
|
31867
|
+
state.acpStartPromise = null;
|
|
31868
|
+
state.acpAgentKey = null;
|
|
31869
|
+
state.lastAcpStartError = "Agent subprocess exited";
|
|
31870
|
+
},
|
|
31721
31871
|
...hooks
|
|
31722
31872
|
}).then((h) => {
|
|
31723
31873
|
state.lastAcpStartError = null;
|
|
@@ -31755,6 +31905,14 @@ async function createAcpManager(options) {
|
|
|
31755
31905
|
backendFallbackAgentType = agentType;
|
|
31756
31906
|
}
|
|
31757
31907
|
}
|
|
31908
|
+
function logPromptReceivedFromBridge(opts) {
|
|
31909
|
+
const { agentType, mode } = opts;
|
|
31910
|
+
const preferredForPrompt = agentType ?? backendFallbackAgentType ?? null;
|
|
31911
|
+
const modeLabel = typeof mode === "string" && mode.trim() !== "" ? mode.trim() : "not set";
|
|
31912
|
+
log2(
|
|
31913
|
+
`[Agent] Prompt received (${getAgentTypeDisplayName(preferredForPrompt)}, mode: ${modeLabel})`
|
|
31914
|
+
);
|
|
31915
|
+
}
|
|
31758
31916
|
function handlePrompt(opts) {
|
|
31759
31917
|
const {
|
|
31760
31918
|
promptText,
|
|
@@ -31835,6 +31993,7 @@ async function createAcpManager(options) {
|
|
|
31835
31993
|
if (promptRouting.runId !== runId) return false;
|
|
31836
31994
|
const handle = state.acpHandle;
|
|
31837
31995
|
if (handle?.cancel) {
|
|
31996
|
+
log2("[Agent] Stop requested");
|
|
31838
31997
|
try {
|
|
31839
31998
|
await handle.cancel();
|
|
31840
31999
|
return true;
|
|
@@ -31843,6 +32002,7 @@ async function createAcpManager(options) {
|
|
|
31843
32002
|
return false;
|
|
31844
32003
|
}
|
|
31845
32004
|
}
|
|
32005
|
+
log2("[Agent] Stop requested (agent still starting)");
|
|
31846
32006
|
pendingCancelRunId = runId;
|
|
31847
32007
|
return true;
|
|
31848
32008
|
}
|
|
@@ -31855,7 +32015,14 @@ async function createAcpManager(options) {
|
|
|
31855
32015
|
state.acpStartPromise = null;
|
|
31856
32016
|
state.acpAgentKey = null;
|
|
31857
32017
|
}
|
|
31858
|
-
return {
|
|
32018
|
+
return {
|
|
32019
|
+
setPreferredAgentType,
|
|
32020
|
+
logPromptReceivedFromBridge,
|
|
32021
|
+
handlePrompt,
|
|
32022
|
+
cancelRun,
|
|
32023
|
+
resolveRequest,
|
|
32024
|
+
disconnect
|
|
32025
|
+
};
|
|
31859
32026
|
}
|
|
31860
32027
|
|
|
31861
32028
|
// src/bridge/routing/handlers/auth-token.ts
|
|
@@ -31990,6 +32157,8 @@ function handleBridgePrompt(msg, deps) {
|
|
|
31990
32157
|
const sessionWorktreesEnabled = msg.sessionWorktreesEnabled === true;
|
|
31991
32158
|
const agentType = typeof msg.agentType === "string" && msg.agentType.trim() ? msg.agentType.trim() : void 0;
|
|
31992
32159
|
const runId = typeof msg.runId === "string" ? msg.runId : void 0;
|
|
32160
|
+
const mode = typeof msg.mode === "string" && msg.mode.trim() ? msg.mode.trim() : void 0;
|
|
32161
|
+
acpManager.logPromptReceivedFromBridge({ agentType, mode });
|
|
31993
32162
|
const sendResult = (result) => {
|
|
31994
32163
|
const s = getWs();
|
|
31995
32164
|
if (s) sendWsMessage(s, result);
|
|
@@ -32052,7 +32221,7 @@ function handleBridgePrompt(msg, deps) {
|
|
|
32052
32221
|
promptId: msg.id,
|
|
32053
32222
|
sessionId,
|
|
32054
32223
|
runId,
|
|
32055
|
-
mode
|
|
32224
|
+
mode,
|
|
32056
32225
|
agentType,
|
|
32057
32226
|
cwd: effectiveCwd,
|
|
32058
32227
|
sendResult,
|
|
@@ -32296,7 +32465,7 @@ var handleSkillCallMessage = (msg, { getWs, log: log2 }) => {
|
|
|
32296
32465
|
};
|
|
32297
32466
|
|
|
32298
32467
|
// src/files/list-dir.ts
|
|
32299
|
-
import
|
|
32468
|
+
import fs5 from "node:fs";
|
|
32300
32469
|
import path13 from "node:path";
|
|
32301
32470
|
|
|
32302
32471
|
// src/files/ensure-under-cwd.ts
|
|
@@ -32317,14 +32486,14 @@ function listDir(relativePath) {
|
|
|
32317
32486
|
return { error: "Path is outside working directory" };
|
|
32318
32487
|
}
|
|
32319
32488
|
try {
|
|
32320
|
-
const names =
|
|
32489
|
+
const names = fs5.readdirSync(resolved, { withFileTypes: true });
|
|
32321
32490
|
const entries = names.filter((d) => !d.name.startsWith(".")).map((d) => {
|
|
32322
32491
|
const entryPath = path13.join(relativePath || ".", d.name).replace(/\\/g, "/");
|
|
32323
32492
|
const fullPath = path13.join(resolved, d.name);
|
|
32324
32493
|
let isDir = d.isDirectory();
|
|
32325
32494
|
if (d.isSymbolicLink()) {
|
|
32326
32495
|
try {
|
|
32327
|
-
const targetStat =
|
|
32496
|
+
const targetStat = fs5.statSync(fullPath);
|
|
32328
32497
|
isDir = targetStat.isDirectory();
|
|
32329
32498
|
} catch {
|
|
32330
32499
|
isDir = false;
|
|
@@ -32348,25 +32517,25 @@ function listDir(relativePath) {
|
|
|
32348
32517
|
}
|
|
32349
32518
|
|
|
32350
32519
|
// src/files/read-file.ts
|
|
32351
|
-
import
|
|
32520
|
+
import fs6 from "node:fs";
|
|
32352
32521
|
import { StringDecoder } from "node:string_decoder";
|
|
32353
32522
|
function resolveFilePath(relativePath) {
|
|
32354
32523
|
const resolved = ensureUnderCwd(relativePath, getBridgeWorkspaceDirectory());
|
|
32355
32524
|
if (!resolved) return { error: "Path is outside working directory" };
|
|
32356
32525
|
let real;
|
|
32357
32526
|
try {
|
|
32358
|
-
real =
|
|
32527
|
+
real = fs6.realpathSync(resolved);
|
|
32359
32528
|
} catch {
|
|
32360
32529
|
real = resolved;
|
|
32361
32530
|
}
|
|
32362
|
-
const
|
|
32363
|
-
if (!
|
|
32531
|
+
const stat2 = fs6.statSync(real);
|
|
32532
|
+
if (!stat2.isFile()) return { error: "Not a file" };
|
|
32364
32533
|
return real;
|
|
32365
32534
|
}
|
|
32366
32535
|
var LINE_CHUNK_SIZE = 64 * 1024;
|
|
32367
32536
|
function readFileRange(filePath, startLine, endLine, lineOffsetIn, lineChunkSize = LINE_CHUNK_SIZE) {
|
|
32368
|
-
const fileSize =
|
|
32369
|
-
const fd =
|
|
32537
|
+
const fileSize = fs6.statSync(filePath).size;
|
|
32538
|
+
const fd = fs6.openSync(filePath, "r");
|
|
32370
32539
|
const bufSize = 64 * 1024;
|
|
32371
32540
|
const buf = Buffer.alloc(bufSize);
|
|
32372
32541
|
const decoder = new StringDecoder("utf8");
|
|
@@ -32379,7 +32548,7 @@ function readFileRange(filePath, startLine, endLine, lineOffsetIn, lineChunkSize
|
|
|
32379
32548
|
let line0Accum = "";
|
|
32380
32549
|
try {
|
|
32381
32550
|
let bytesRead;
|
|
32382
|
-
while (!done && (bytesRead =
|
|
32551
|
+
while (!done && (bytesRead = fs6.readSync(fd, buf, 0, bufSize, null)) > 0) {
|
|
32383
32552
|
const text = partial2 + decoder.write(buf.subarray(0, bytesRead));
|
|
32384
32553
|
partial2 = "";
|
|
32385
32554
|
let lineStart = 0;
|
|
@@ -32514,10 +32683,10 @@ function readFileRange(filePath, startLine, endLine, lineOffsetIn, lineChunkSize
|
|
|
32514
32683
|
}
|
|
32515
32684
|
return { content: resultLines.join("\n"), size: fileSize };
|
|
32516
32685
|
} finally {
|
|
32517
|
-
|
|
32686
|
+
fs6.closeSync(fd);
|
|
32518
32687
|
}
|
|
32519
32688
|
}
|
|
32520
|
-
function
|
|
32689
|
+
function readFile2(relativePath, startLine, endLine, lineOffset, lineChunkSize = LINE_CHUNK_SIZE) {
|
|
32521
32690
|
try {
|
|
32522
32691
|
const result = resolveFilePath(relativePath);
|
|
32523
32692
|
if (typeof result === "object") return result;
|
|
@@ -32525,17 +32694,17 @@ function readFile(relativePath, startLine, endLine, lineOffset, lineChunkSize =
|
|
|
32525
32694
|
if (hasRange) {
|
|
32526
32695
|
return readFileRange(result, startLine, endLine, lineOffset, lineChunkSize);
|
|
32527
32696
|
}
|
|
32528
|
-
const
|
|
32529
|
-
const raw =
|
|
32697
|
+
const stat2 = fs6.statSync(result);
|
|
32698
|
+
const raw = fs6.readFileSync(result, "utf8");
|
|
32530
32699
|
const lines = raw.split(/\r?\n/);
|
|
32531
|
-
return { content: raw, totalLines: lines.length, size:
|
|
32700
|
+
return { content: raw, totalLines: lines.length, size: stat2.size };
|
|
32532
32701
|
} catch (err) {
|
|
32533
32702
|
return { error: err instanceof Error ? err.message : String(err) };
|
|
32534
32703
|
}
|
|
32535
32704
|
}
|
|
32536
32705
|
|
|
32537
32706
|
// src/files/file-index.ts
|
|
32538
|
-
import
|
|
32707
|
+
import fs7 from "node:fs";
|
|
32539
32708
|
import path14 from "node:path";
|
|
32540
32709
|
import os2 from "node:os";
|
|
32541
32710
|
import crypto2 from "node:crypto";
|
|
@@ -32582,23 +32751,23 @@ function binarySearch(arr, x) {
|
|
|
32582
32751
|
function walkDir(dir, baseDir, out) {
|
|
32583
32752
|
let names;
|
|
32584
32753
|
try {
|
|
32585
|
-
names =
|
|
32754
|
+
names = fs7.readdirSync(dir);
|
|
32586
32755
|
} catch {
|
|
32587
32756
|
return;
|
|
32588
32757
|
}
|
|
32589
32758
|
for (const name of names) {
|
|
32590
32759
|
if (name.startsWith(".")) continue;
|
|
32591
32760
|
const full = path14.join(dir, name);
|
|
32592
|
-
let
|
|
32761
|
+
let stat2;
|
|
32593
32762
|
try {
|
|
32594
|
-
|
|
32763
|
+
stat2 = fs7.statSync(full);
|
|
32595
32764
|
} catch {
|
|
32596
32765
|
continue;
|
|
32597
32766
|
}
|
|
32598
32767
|
const relative4 = path14.relative(baseDir, full).replace(/\\/g, "/");
|
|
32599
|
-
if (
|
|
32768
|
+
if (stat2.isDirectory()) {
|
|
32600
32769
|
walkDir(full, baseDir, out);
|
|
32601
|
-
} else if (
|
|
32770
|
+
} else if (stat2.isFile()) {
|
|
32602
32771
|
out.push(relative4);
|
|
32603
32772
|
}
|
|
32604
32773
|
}
|
|
@@ -32622,8 +32791,8 @@ function buildFileIndex(cwd) {
|
|
|
32622
32791
|
const data = { version: INDEX_VERSION, paths, trigramIndex };
|
|
32623
32792
|
const indexPath = getIndexPath(resolved);
|
|
32624
32793
|
try {
|
|
32625
|
-
if (!
|
|
32626
|
-
|
|
32794
|
+
if (!fs7.existsSync(INDEX_DIR)) fs7.mkdirSync(INDEX_DIR, { recursive: true });
|
|
32795
|
+
fs7.writeFileSync(indexPath, JSON.stringify(data), "utf8");
|
|
32627
32796
|
} catch (e) {
|
|
32628
32797
|
console.error("[file-index] Failed to write index:", e);
|
|
32629
32798
|
}
|
|
@@ -32633,7 +32802,7 @@ function loadFileIndex(cwd) {
|
|
|
32633
32802
|
const resolved = path14.resolve(cwd);
|
|
32634
32803
|
const indexPath = getIndexPath(resolved);
|
|
32635
32804
|
try {
|
|
32636
|
-
const raw =
|
|
32805
|
+
const raw = fs7.readFileSync(indexPath, "utf8");
|
|
32637
32806
|
const parsed = JSON.parse(raw);
|
|
32638
32807
|
if (parsed !== null && typeof parsed === "object" && Array.isArray(parsed.paths)) {
|
|
32639
32808
|
const obj = parsed;
|
|
@@ -32737,7 +32906,7 @@ function handleFileBrowserRequest(msg, socket) {
|
|
|
32737
32906
|
const endLine = typeof msg.endLine === "number" ? msg.endLine : void 0;
|
|
32738
32907
|
const lineOffset = typeof msg.lineOffset === "number" ? msg.lineOffset : void 0;
|
|
32739
32908
|
const lineChunkSize = typeof msg.lineChunkSize === "number" ? msg.lineChunkSize : void 0;
|
|
32740
|
-
const result =
|
|
32909
|
+
const result = readFile2(reqPath, startLine, endLine, lineOffset, lineChunkSize);
|
|
32741
32910
|
if ("error" in result) {
|
|
32742
32911
|
sendWsMessage(socket, { type: "file_browser_response", id: msg.id, error: result.error });
|
|
32743
32912
|
} else {
|
|
@@ -32772,7 +32941,7 @@ function handleFileBrowserSearchMessage(msg, { getWs }) {
|
|
|
32772
32941
|
}
|
|
32773
32942
|
|
|
32774
32943
|
// src/skills/discover-local-agent-skills.ts
|
|
32775
|
-
import
|
|
32944
|
+
import fs8 from "node:fs";
|
|
32776
32945
|
import path15 from "node:path";
|
|
32777
32946
|
var SKILL_DISCOVERY_ROOTS = [".agents/skills", ".claude/skills", ".cursor/skills", "skills"];
|
|
32778
32947
|
function discoverLocalSkills(cwd) {
|
|
@@ -32780,22 +32949,22 @@ function discoverLocalSkills(cwd) {
|
|
|
32780
32949
|
const seenKeys = /* @__PURE__ */ new Set();
|
|
32781
32950
|
for (const rel of SKILL_DISCOVERY_ROOTS) {
|
|
32782
32951
|
const base = path15.join(cwd, rel);
|
|
32783
|
-
if (!
|
|
32952
|
+
if (!fs8.existsSync(base) || !fs8.statSync(base).isDirectory()) continue;
|
|
32784
32953
|
let entries = [];
|
|
32785
32954
|
try {
|
|
32786
|
-
entries =
|
|
32955
|
+
entries = fs8.readdirSync(base);
|
|
32787
32956
|
} catch {
|
|
32788
32957
|
continue;
|
|
32789
32958
|
}
|
|
32790
32959
|
for (const name of entries) {
|
|
32791
32960
|
const dir = path15.join(base, name);
|
|
32792
32961
|
try {
|
|
32793
|
-
if (!
|
|
32962
|
+
if (!fs8.statSync(dir).isDirectory()) continue;
|
|
32794
32963
|
} catch {
|
|
32795
32964
|
continue;
|
|
32796
32965
|
}
|
|
32797
32966
|
const skillMd = path15.join(dir, "SKILL.md");
|
|
32798
|
-
if (!
|
|
32967
|
+
if (!fs8.existsSync(skillMd)) continue;
|
|
32799
32968
|
const key = `${rel}/${name}`;
|
|
32800
32969
|
if (seenKeys.has(key)) continue;
|
|
32801
32970
|
seenKeys.add(key);
|
|
@@ -32808,10 +32977,10 @@ function discoverSkillLayoutRoots(cwd) {
|
|
|
32808
32977
|
const roots = [];
|
|
32809
32978
|
for (const rel of SKILL_DISCOVERY_ROOTS) {
|
|
32810
32979
|
const base = path15.join(cwd, rel);
|
|
32811
|
-
if (!
|
|
32980
|
+
if (!fs8.existsSync(base) || !fs8.statSync(base).isDirectory()) continue;
|
|
32812
32981
|
let entries = [];
|
|
32813
32982
|
try {
|
|
32814
|
-
entries =
|
|
32983
|
+
entries = fs8.readdirSync(base);
|
|
32815
32984
|
} catch {
|
|
32816
32985
|
continue;
|
|
32817
32986
|
}
|
|
@@ -32819,11 +32988,11 @@ function discoverSkillLayoutRoots(cwd) {
|
|
|
32819
32988
|
for (const name of entries) {
|
|
32820
32989
|
const dir = path15.join(base, name);
|
|
32821
32990
|
try {
|
|
32822
|
-
if (!
|
|
32991
|
+
if (!fs8.statSync(dir).isDirectory()) continue;
|
|
32823
32992
|
} catch {
|
|
32824
32993
|
continue;
|
|
32825
32994
|
}
|
|
32826
|
-
if (!
|
|
32995
|
+
if (!fs8.existsSync(path15.join(dir, "SKILL.md"))) continue;
|
|
32827
32996
|
const relPath = `${rel}/${name}`.replace(/\\/g, "/");
|
|
32828
32997
|
skills2.push({ name, relPath });
|
|
32829
32998
|
}
|
|
@@ -32843,7 +33012,7 @@ function handleSkillLayoutRequest(msg, deps) {
|
|
|
32843
33012
|
}
|
|
32844
33013
|
|
|
32845
33014
|
// src/skills/install-remote-skills.ts
|
|
32846
|
-
import
|
|
33015
|
+
import fs9 from "node:fs";
|
|
32847
33016
|
import path16 from "node:path";
|
|
32848
33017
|
function installRemoteSkills(cwd, targetDir, items) {
|
|
32849
33018
|
const installed = [];
|
|
@@ -32859,11 +33028,11 @@ function installRemoteSkills(cwd, targetDir, items) {
|
|
|
32859
33028
|
for (const f of item.files) {
|
|
32860
33029
|
if (typeof f.path !== "string" || !f.text && !f.base64) continue;
|
|
32861
33030
|
const dest = path16.join(skillDir, f.path);
|
|
32862
|
-
|
|
33031
|
+
fs9.mkdirSync(path16.dirname(dest), { recursive: true });
|
|
32863
33032
|
if (f.text !== void 0) {
|
|
32864
|
-
|
|
33033
|
+
fs9.writeFileSync(dest, f.text, "utf8");
|
|
32865
33034
|
} else if (f.base64) {
|
|
32866
|
-
|
|
33035
|
+
fs9.writeFileSync(dest, Buffer.from(f.base64, "base64"));
|
|
32867
33036
|
}
|
|
32868
33037
|
}
|
|
32869
33038
|
installed.push({
|
|
@@ -32957,7 +33126,7 @@ var handleSessionDiscardedMessage = (msg, deps) => {
|
|
|
32957
33126
|
};
|
|
32958
33127
|
|
|
32959
33128
|
// src/bridge/routing/handlers/revert-turn-snapshot.ts
|
|
32960
|
-
import * as
|
|
33129
|
+
import * as fs10 from "node:fs";
|
|
32961
33130
|
var handleRevertTurnSnapshotMessage = (msg, deps) => {
|
|
32962
33131
|
const id = typeof msg.id === "string" ? msg.id : "";
|
|
32963
33132
|
const sessionId = typeof msg.sessionId === "string" ? msg.sessionId : "";
|
|
@@ -32969,7 +33138,7 @@ var handleRevertTurnSnapshotMessage = (msg, deps) => {
|
|
|
32969
33138
|
if (!s) return;
|
|
32970
33139
|
const agentBase = sessionWorktreeManager.getAgentCwdForSession(sessionId) ?? getBridgeWorkspaceDirectory();
|
|
32971
33140
|
const file2 = snapshotFilePath(agentBase, turnId);
|
|
32972
|
-
if (!
|
|
33141
|
+
if (!fs10.existsSync(file2)) {
|
|
32973
33142
|
sendWsMessage(s, {
|
|
32974
33143
|
type: "revert_turn_snapshot_result",
|
|
32975
33144
|
id,
|
|
@@ -33070,25 +33239,12 @@ function dispatchBridgeMessage(msg, deps) {
|
|
|
33070
33239
|
}
|
|
33071
33240
|
|
|
33072
33241
|
// src/bridge/routing/handle-bridge-message.ts
|
|
33073
|
-
var DEFERRED_INBOUND_TYPES = /* @__PURE__ */ new Set([
|
|
33074
|
-
"server_control",
|
|
33075
|
-
"prompt",
|
|
33076
|
-
"install_skills",
|
|
33077
|
-
"refresh_local_skills",
|
|
33078
|
-
"dev_servers_config"
|
|
33079
|
-
]);
|
|
33080
33242
|
function handleBridgeMessage(data, deps) {
|
|
33081
33243
|
const msg = data;
|
|
33082
|
-
|
|
33083
|
-
|
|
33084
|
-
|
|
33085
|
-
|
|
33086
|
-
setImmediate(() => {
|
|
33087
|
-
dispatchBridgeMessage(msg, deps);
|
|
33088
|
-
});
|
|
33089
|
-
return;
|
|
33090
|
-
}
|
|
33091
|
-
dispatchBridgeMessage(msg, deps);
|
|
33244
|
+
if (!deps.getWs()) return;
|
|
33245
|
+
setImmediate(() => {
|
|
33246
|
+
dispatchBridgeMessage(msg, deps);
|
|
33247
|
+
});
|
|
33092
33248
|
}
|
|
33093
33249
|
|
|
33094
33250
|
// src/worktrees/session-worktree-manager.ts
|
|
@@ -33096,7 +33252,7 @@ import * as path20 from "node:path";
|
|
|
33096
33252
|
import os4 from "node:os";
|
|
33097
33253
|
|
|
33098
33254
|
// src/worktrees/prepare-new-session-worktrees.ts
|
|
33099
|
-
import * as
|
|
33255
|
+
import * as fs12 from "node:fs";
|
|
33100
33256
|
import * as path18 from "node:path";
|
|
33101
33257
|
|
|
33102
33258
|
// src/git/worktree-add.ts
|
|
@@ -33106,7 +33262,7 @@ async function gitWorktreeAddBranch(mainRepoPath, worktreePath, branch) {
|
|
|
33106
33262
|
}
|
|
33107
33263
|
|
|
33108
33264
|
// src/worktrees/worktree-layout-file.ts
|
|
33109
|
-
import * as
|
|
33265
|
+
import * as fs11 from "node:fs";
|
|
33110
33266
|
import * as path17 from "node:path";
|
|
33111
33267
|
import os3 from "node:os";
|
|
33112
33268
|
var LAYOUT_FILENAME = "worktree-launcher-layout.json";
|
|
@@ -33123,8 +33279,8 @@ function normalizeLoadedLayout(raw) {
|
|
|
33123
33279
|
function loadWorktreeLayout() {
|
|
33124
33280
|
try {
|
|
33125
33281
|
const p = defaultWorktreeLayoutPath();
|
|
33126
|
-
if (!
|
|
33127
|
-
const raw = JSON.parse(
|
|
33282
|
+
if (!fs11.existsSync(p)) return { launcherCwds: [] };
|
|
33283
|
+
const raw = JSON.parse(fs11.readFileSync(p, "utf8"));
|
|
33128
33284
|
return normalizeLoadedLayout(raw);
|
|
33129
33285
|
} catch {
|
|
33130
33286
|
return { launcherCwds: [] };
|
|
@@ -33133,8 +33289,8 @@ function loadWorktreeLayout() {
|
|
|
33133
33289
|
function saveWorktreeLayout(layout) {
|
|
33134
33290
|
try {
|
|
33135
33291
|
const dir = path17.dirname(defaultWorktreeLayoutPath());
|
|
33136
|
-
|
|
33137
|
-
|
|
33292
|
+
fs11.mkdirSync(dir, { recursive: true });
|
|
33293
|
+
fs11.writeFileSync(defaultWorktreeLayoutPath(), JSON.stringify(layout, null, 2), "utf8");
|
|
33138
33294
|
} catch {
|
|
33139
33295
|
}
|
|
33140
33296
|
}
|
|
@@ -33171,13 +33327,13 @@ async function prepareNewSessionWorktrees(options) {
|
|
|
33171
33327
|
}
|
|
33172
33328
|
const branch = `session-${sessionId}`;
|
|
33173
33329
|
const worktreePaths = [];
|
|
33174
|
-
|
|
33330
|
+
fs12.mkdirSync(agentMirrorRoot, { recursive: true });
|
|
33175
33331
|
for (const repo of repos) {
|
|
33176
33332
|
let rel = path18.relative(launcherResolved, repo.absolutePath);
|
|
33177
33333
|
if (rel.startsWith("..") || path18.isAbsolute(rel)) continue;
|
|
33178
33334
|
const relNorm = rel === "" ? "." : rel;
|
|
33179
33335
|
const wtPath = path18.join(agentMirrorRoot, relNorm, sessionId);
|
|
33180
|
-
|
|
33336
|
+
fs12.mkdirSync(path18.dirname(wtPath), { recursive: true });
|
|
33181
33337
|
try {
|
|
33182
33338
|
await gitWorktreeAddBranch(repo.absolutePath, wtPath, branch);
|
|
33183
33339
|
log2(`[worktrees] Added worktree ${wtPath} (branch ${branch}).`);
|
|
@@ -33214,18 +33370,18 @@ async function renameSessionWorktreeBranches(paths, newBranch, log2) {
|
|
|
33214
33370
|
}
|
|
33215
33371
|
|
|
33216
33372
|
// src/worktrees/remove-session-worktrees.ts
|
|
33217
|
-
import * as
|
|
33373
|
+
import * as fs15 from "node:fs";
|
|
33218
33374
|
|
|
33219
33375
|
// src/git/worktree-remove.ts
|
|
33220
|
-
import * as
|
|
33376
|
+
import * as fs14 from "node:fs";
|
|
33221
33377
|
|
|
33222
33378
|
// src/git/resolve-main-repo-from-git-file.ts
|
|
33223
|
-
import * as
|
|
33379
|
+
import * as fs13 from "node:fs";
|
|
33224
33380
|
import * as path19 from "node:path";
|
|
33225
33381
|
function resolveMainRepoFromWorktreeGitFile(wt) {
|
|
33226
33382
|
const gitDirFile = path19.join(wt, ".git");
|
|
33227
|
-
if (!
|
|
33228
|
-
const first2 =
|
|
33383
|
+
if (!fs13.existsSync(gitDirFile) || !fs13.statSync(gitDirFile).isFile()) return "";
|
|
33384
|
+
const first2 = fs13.readFileSync(gitDirFile, "utf8").trim();
|
|
33229
33385
|
const m = first2.match(/^gitdir:\s*(.+)$/im);
|
|
33230
33386
|
if (!m) return "";
|
|
33231
33387
|
const gitWorktreePath = path19.resolve(wt, m[1].trim());
|
|
@@ -33239,7 +33395,7 @@ async function gitWorktreeRemoveForce(worktreePath) {
|
|
|
33239
33395
|
if (mainRepo) {
|
|
33240
33396
|
await simpleGit(mainRepo).raw(["worktree", "remove", "--force", worktreePath]);
|
|
33241
33397
|
} else {
|
|
33242
|
-
|
|
33398
|
+
fs14.rmSync(worktreePath, { recursive: true, force: true });
|
|
33243
33399
|
}
|
|
33244
33400
|
}
|
|
33245
33401
|
|
|
@@ -33252,7 +33408,7 @@ async function removeSessionWorktrees(paths, log2) {
|
|
|
33252
33408
|
} catch (e) {
|
|
33253
33409
|
log2(`[worktrees] Remove failed for ${wt}: ${e instanceof Error ? e.message : String(e)}`);
|
|
33254
33410
|
try {
|
|
33255
|
-
|
|
33411
|
+
fs15.rmSync(wt, { recursive: true, force: true });
|
|
33256
33412
|
} catch {
|
|
33257
33413
|
}
|
|
33258
33414
|
}
|
|
@@ -33524,7 +33680,7 @@ function forceKillChild(proc, log2, shortId, graceMs) {
|
|
|
33524
33680
|
}
|
|
33525
33681
|
|
|
33526
33682
|
// src/dev-servers/process/wire-dev-server-child-process.ts
|
|
33527
|
-
import
|
|
33683
|
+
import fs16 from "node:fs";
|
|
33528
33684
|
|
|
33529
33685
|
// src/dev-servers/manager/forward-pipe.ts
|
|
33530
33686
|
function forwardChildPipe(childReadable, terminal, onData) {
|
|
@@ -33560,7 +33716,7 @@ function wireDevServerChildProcess(d) {
|
|
|
33560
33716
|
d.setPollInterval(void 0);
|
|
33561
33717
|
return;
|
|
33562
33718
|
}
|
|
33563
|
-
|
|
33719
|
+
fs16.readFile(d.mergedLogPath, (err, buf) => {
|
|
33564
33720
|
if (err || (d.getSpawnGeneration() ?? 0) !== d.scheduledGen) return;
|
|
33565
33721
|
if (buf.length <= d.mergedReadPos.value) return;
|
|
33566
33722
|
const chunk = Buffer.from(buf.subarray(d.mergedReadPos.value));
|
|
@@ -33598,7 +33754,7 @@ ${errTail}` : ""}`);
|
|
|
33598
33754
|
d.sendStatus(code === 0 || code == null ? "stopped" : "error", detail, tails);
|
|
33599
33755
|
};
|
|
33600
33756
|
if (mergedPath) {
|
|
33601
|
-
|
|
33757
|
+
fs16.readFile(mergedPath, (err, buf) => {
|
|
33602
33758
|
if (!err && buf.length > d.mergedReadPos.value) {
|
|
33603
33759
|
const chunk = Buffer.from(buf.subarray(d.mergedReadPos.value));
|
|
33604
33760
|
if (chunk.length > 0) {
|
|
@@ -33700,13 +33856,13 @@ function parseDevServerDefs(servers) {
|
|
|
33700
33856
|
}
|
|
33701
33857
|
|
|
33702
33858
|
// src/dev-servers/manager/shell-spawn/utils.ts
|
|
33703
|
-
import
|
|
33859
|
+
import fs17 from "node:fs";
|
|
33704
33860
|
function isSpawnEbadf(e) {
|
|
33705
33861
|
return typeof e === "object" && e !== null && "code" in e && e.code === "EBADF";
|
|
33706
33862
|
}
|
|
33707
33863
|
function rmDirQuiet(dir) {
|
|
33708
33864
|
try {
|
|
33709
|
-
|
|
33865
|
+
fs17.rmSync(dir, { recursive: true, force: true });
|
|
33710
33866
|
} catch {
|
|
33711
33867
|
}
|
|
33712
33868
|
}
|
|
@@ -33714,7 +33870,7 @@ var cachedDevNullReadFd;
|
|
|
33714
33870
|
function devNullReadFd() {
|
|
33715
33871
|
if (cachedDevNullReadFd === void 0) {
|
|
33716
33872
|
const devPath = process.platform === "win32" ? "nul" : "/dev/null";
|
|
33717
|
-
cachedDevNullReadFd =
|
|
33873
|
+
cachedDevNullReadFd = fs17.openSync(devPath, "r");
|
|
33718
33874
|
}
|
|
33719
33875
|
return cachedDevNullReadFd;
|
|
33720
33876
|
}
|
|
@@ -33788,15 +33944,15 @@ function trySpawnShellTruePiped(command, env, cwd, devNullFd, signal) {
|
|
|
33788
33944
|
|
|
33789
33945
|
// src/dev-servers/manager/shell-spawn/try-spawn-merged-log-file.ts
|
|
33790
33946
|
import { spawn as spawn7 } from "node:child_process";
|
|
33791
|
-
import
|
|
33947
|
+
import fs18 from "node:fs";
|
|
33792
33948
|
import { tmpdir } from "node:os";
|
|
33793
33949
|
import path22 from "node:path";
|
|
33794
33950
|
function trySpawnMergedLogFile(command, env, cwd, signal) {
|
|
33795
|
-
const tmpRoot =
|
|
33951
|
+
const tmpRoot = fs18.mkdtempSync(path22.join(tmpdir(), "ba-devsrv-log-"));
|
|
33796
33952
|
const logPath = path22.join(tmpRoot, "combined.log");
|
|
33797
33953
|
let logFd;
|
|
33798
33954
|
try {
|
|
33799
|
-
logFd =
|
|
33955
|
+
logFd = fs18.openSync(logPath, "a");
|
|
33800
33956
|
} catch {
|
|
33801
33957
|
rmDirQuiet(tmpRoot);
|
|
33802
33958
|
return null;
|
|
@@ -33815,7 +33971,7 @@ function trySpawnMergedLogFile(command, env, cwd, signal) {
|
|
|
33815
33971
|
} else {
|
|
33816
33972
|
proc = spawn7("/bin/sh", ["-c", command], { env, cwd, stdio, ...signal ? { signal } : {} });
|
|
33817
33973
|
}
|
|
33818
|
-
|
|
33974
|
+
fs18.closeSync(logFd);
|
|
33819
33975
|
return {
|
|
33820
33976
|
proc,
|
|
33821
33977
|
pipedStdoutStderr: true,
|
|
@@ -33824,7 +33980,7 @@ function trySpawnMergedLogFile(command, env, cwd, signal) {
|
|
|
33824
33980
|
};
|
|
33825
33981
|
} catch (e) {
|
|
33826
33982
|
try {
|
|
33827
|
-
|
|
33983
|
+
fs18.closeSync(logFd);
|
|
33828
33984
|
} catch {
|
|
33829
33985
|
}
|
|
33830
33986
|
rmDirQuiet(tmpRoot);
|
|
@@ -33835,22 +33991,22 @@ function trySpawnMergedLogFile(command, env, cwd, signal) {
|
|
|
33835
33991
|
|
|
33836
33992
|
// src/dev-servers/manager/shell-spawn/try-spawn-shell-script-log-redirect.ts
|
|
33837
33993
|
import { spawn as spawn8 } from "node:child_process";
|
|
33838
|
-
import
|
|
33994
|
+
import fs19 from "node:fs";
|
|
33839
33995
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
33840
33996
|
import path23 from "node:path";
|
|
33841
33997
|
function shSingleQuote(s) {
|
|
33842
33998
|
return `'${s.replace(/'/g, `'\\''`)}'`;
|
|
33843
33999
|
}
|
|
33844
34000
|
function trySpawnShellScriptLogRedirectUnix(command, env, cwd, signal) {
|
|
33845
|
-
const tmpRoot =
|
|
34001
|
+
const tmpRoot = fs19.mkdtempSync(path23.join(tmpdir2(), "ba-devsrv-sh-"));
|
|
33846
34002
|
const logPath = path23.join(tmpRoot, "combined.log");
|
|
33847
34003
|
const innerPath = path23.join(tmpRoot, "_cmd.sh");
|
|
33848
34004
|
const runnerPath = path23.join(tmpRoot, "_run.sh");
|
|
33849
34005
|
try {
|
|
33850
|
-
|
|
34006
|
+
fs19.writeFileSync(innerPath, `#!/bin/sh
|
|
33851
34007
|
${command}
|
|
33852
34008
|
`);
|
|
33853
|
-
|
|
34009
|
+
fs19.writeFileSync(
|
|
33854
34010
|
runnerPath,
|
|
33855
34011
|
`#!/bin/sh
|
|
33856
34012
|
cd ${shSingleQuote(cwd)}
|
|
@@ -33876,13 +34032,13 @@ cd ${shSingleQuote(cwd)}
|
|
|
33876
34032
|
}
|
|
33877
34033
|
}
|
|
33878
34034
|
function trySpawnShellScriptLogRedirectWin(command, env, cwd, signal) {
|
|
33879
|
-
const tmpRoot =
|
|
34035
|
+
const tmpRoot = fs19.mkdtempSync(path23.join(tmpdir2(), "ba-devsrv-sh-"));
|
|
33880
34036
|
const logPath = path23.join(tmpRoot, "combined.log");
|
|
33881
34037
|
const runnerPath = path23.join(tmpRoot, "_run.bat");
|
|
33882
34038
|
const q = (p) => `"${p.replace(/"/g, '""')}"`;
|
|
33883
34039
|
const com = process.env.ComSpec || "cmd.exe";
|
|
33884
34040
|
try {
|
|
33885
|
-
|
|
34041
|
+
fs19.writeFileSync(
|
|
33886
34042
|
runnerPath,
|
|
33887
34043
|
`@ECHO OFF\r
|
|
33888
34044
|
CD /D ${q(cwd)}\r
|
|
@@ -34236,7 +34392,9 @@ var DevServerManager = class {
|
|
|
34236
34392
|
async shutdownAllGraceful() {
|
|
34237
34393
|
const pairs = [...this.processes.entries()];
|
|
34238
34394
|
if (pairs.length === 0) return;
|
|
34239
|
-
this.log(
|
|
34395
|
+
this.log(
|
|
34396
|
+
`[dev-server] Stopping ${pairs.length} local dev server process${pairs.length === 1 ? "" : "es"}\u2026`
|
|
34397
|
+
);
|
|
34240
34398
|
await Promise.all(pairs.map(([serverId, proc]) => this.gracefulTerminateOrUnknown(serverId, proc)));
|
|
34241
34399
|
}
|
|
34242
34400
|
async gracefulTerminateOrUnknown(serverId, proc) {
|
|
@@ -34510,9 +34668,10 @@ var FIREHOSE_CLIENT_PING_MS = 25e3;
|
|
|
34510
34668
|
function connectFirehose(options) {
|
|
34511
34669
|
const { firehoseServerUrl, workspaceId, bridgeName, proxyPorts, log: log2, devServerManager, onOpen, onClose } = options;
|
|
34512
34670
|
const wsUrl = buildFirehoseCliWsUrl(firehoseServerUrl);
|
|
34513
|
-
|
|
34671
|
+
applyCliOutboundNetworkPreferences();
|
|
34672
|
+
const wsOptions = { perMessageDeflate: false, family: 4 };
|
|
34514
34673
|
if (wsUrl.startsWith("wss://")) {
|
|
34515
|
-
wsOptions.agent = new https2.Agent({ rejectUnauthorized: false });
|
|
34674
|
+
wsOptions.agent = new https2.Agent({ rejectUnauthorized: false, family: 4 });
|
|
34516
34675
|
}
|
|
34517
34676
|
const ws = new wrapper_default(wsUrl, wsOptions);
|
|
34518
34677
|
let clientPingTimer = null;
|
|
@@ -34548,16 +34707,14 @@ function connectFirehose(options) {
|
|
|
34548
34707
|
sendWsMessage(ws, { type: "identify", workspaceId, bridgeName, proxyPorts });
|
|
34549
34708
|
});
|
|
34550
34709
|
ws.on("message", (raw) => {
|
|
34551
|
-
|
|
34552
|
-
|
|
34553
|
-
|
|
34554
|
-
|
|
34555
|
-
|
|
34556
|
-
|
|
34557
|
-
|
|
34558
|
-
|
|
34559
|
-
}
|
|
34560
|
-
});
|
|
34710
|
+
if (Buffer.isBuffer(raw) && tryConsumeBinaryProxyBody(raw, deps)) {
|
|
34711
|
+
return;
|
|
34712
|
+
}
|
|
34713
|
+
try {
|
|
34714
|
+
const text = Buffer.isBuffer(raw) ? raw.toString("utf8") : String(raw);
|
|
34715
|
+
dispatchFirehoseJsonMessage(JSON.parse(text), deps);
|
|
34716
|
+
} catch {
|
|
34717
|
+
}
|
|
34561
34718
|
});
|
|
34562
34719
|
ws.on("close", (code, reason) => {
|
|
34563
34720
|
clearClientPing();
|
|
@@ -34603,6 +34760,9 @@ function createOnBridgeIdentified(opts) {
|
|
|
34603
34760
|
function attachFirehose(params) {
|
|
34604
34761
|
state.lastFirehoseParams = params;
|
|
34605
34762
|
clearFirehoseReconnectTimer();
|
|
34763
|
+
if (state.firehoseReconnectAttempt === 0) {
|
|
34764
|
+
logFn("Connecting to preview tunnel (local HTTP proxy and dev logs)\u2026");
|
|
34765
|
+
}
|
|
34606
34766
|
state.firehoseGeneration += 1;
|
|
34607
34767
|
const myGen = state.firehoseGeneration;
|
|
34608
34768
|
if (state.firehoseHandle) {
|
|
@@ -34621,8 +34781,8 @@ function createOnBridgeIdentified(opts) {
|
|
|
34621
34781
|
clearFirehoseReconnectQuietOnOpen({ firehoseQuiet: state.firehoseQuiet }, logFn);
|
|
34622
34782
|
const logOpenAsFirehoseReconnect = state.firehoseReconnectAttempt > 0;
|
|
34623
34783
|
state.firehoseReconnectAttempt = 0;
|
|
34624
|
-
if (logOpenAsFirehoseReconnect) {
|
|
34625
|
-
logFn("
|
|
34784
|
+
if (!logOpenAsFirehoseReconnect) {
|
|
34785
|
+
logFn("Connected to preview tunnel (local HTTP proxy and dev logs).");
|
|
34626
34786
|
}
|
|
34627
34787
|
},
|
|
34628
34788
|
onClose: (code, reason) => {
|
|
@@ -34711,6 +34871,7 @@ var CHECKS = {
|
|
|
34711
34871
|
return false;
|
|
34712
34872
|
}
|
|
34713
34873
|
},
|
|
34874
|
+
/** Bridge spawns `@agentclientprotocol/claude-agent-acp` via npx; detection is “Claude toolchain likely present”. */
|
|
34714
34875
|
"claude-code": async () => {
|
|
34715
34876
|
try {
|
|
34716
34877
|
await execFileAsync4("which", ["claude"], { timeout: 4e3 });
|
|
@@ -34820,8 +34981,8 @@ async function createBridgeConnection(options) {
|
|
|
34820
34981
|
clearMainBridgeReconnectQuietOnOpen(state, logFn);
|
|
34821
34982
|
state.reconnectAttempt = 0;
|
|
34822
34983
|
state.logBridgeOpenAsReconnect = false;
|
|
34823
|
-
if (logOpenAsPostRefreshReconnect) {
|
|
34824
|
-
logFn("
|
|
34984
|
+
if (!logOpenAsPostRefreshReconnect) {
|
|
34985
|
+
logFn("Connected to bridge service.");
|
|
34825
34986
|
}
|
|
34826
34987
|
const socket = getWs();
|
|
34827
34988
|
if (socket) {
|
|
@@ -34860,6 +35021,9 @@ async function createBridgeConnection(options) {
|
|
|
34860
35021
|
clearTimeout(state.reconnectTimeout);
|
|
34861
35022
|
state.reconnectTimeout = null;
|
|
34862
35023
|
}
|
|
35024
|
+
if (state.reconnectAttempt === 0) {
|
|
35025
|
+
logFn("Connecting to bridge service\u2026");
|
|
35026
|
+
}
|
|
34863
35027
|
const prev = state.currentWs;
|
|
34864
35028
|
if (prev) {
|
|
34865
35029
|
prev.removeAllListeners();
|
|
@@ -34932,15 +35096,17 @@ async function runBridge(options) {
|
|
|
34932
35096
|
onAuth: (_auth) => {
|
|
34933
35097
|
}
|
|
34934
35098
|
});
|
|
34935
|
-
const onSignal2 = (
|
|
34936
|
-
logImmediate(
|
|
35099
|
+
const onSignal2 = (kind) => {
|
|
35100
|
+
logImmediate(
|
|
35101
|
+
kind === "interrupt" ? "Keyboard interrupt (Ctrl+C) \u2014 stopping\u2026" : "Stop requested \u2014 shutting down\u2026"
|
|
35102
|
+
);
|
|
34937
35103
|
setImmediate(() => {
|
|
34938
35104
|
handle2.close();
|
|
34939
35105
|
process.exit(0);
|
|
34940
35106
|
});
|
|
34941
35107
|
};
|
|
34942
|
-
const onSigInt2 = () => onSignal2("
|
|
34943
|
-
const onSigTerm2 = () => onSignal2("
|
|
35108
|
+
const onSigInt2 = () => onSignal2("interrupt");
|
|
35109
|
+
const onSigTerm2 = () => onSignal2("stop");
|
|
34944
35110
|
process.on("SIGINT", onSigInt2);
|
|
34945
35111
|
process.on("SIGTERM", onSigTerm2);
|
|
34946
35112
|
const auth = await handle2.authPromise;
|
|
@@ -34989,16 +35155,18 @@ async function runBridge(options) {
|
|
|
34989
35155
|
});
|
|
34990
35156
|
}
|
|
34991
35157
|
});
|
|
34992
|
-
const onSignal = (
|
|
34993
|
-
logImmediate(
|
|
35158
|
+
const onSignal = (kind) => {
|
|
35159
|
+
logImmediate(
|
|
35160
|
+
kind === "interrupt" ? "Keyboard interrupt (Ctrl+C) \u2014 stopping\u2026" : "Stop requested \u2014 shutting down\u2026"
|
|
35161
|
+
);
|
|
34994
35162
|
setImmediate(() => {
|
|
34995
35163
|
void handle.close().then(() => {
|
|
34996
35164
|
process.exit(0);
|
|
34997
35165
|
});
|
|
34998
35166
|
});
|
|
34999
35167
|
};
|
|
35000
|
-
const onSigInt = () => onSignal("
|
|
35001
|
-
const onSigTerm = () => onSignal("
|
|
35168
|
+
const onSigInt = () => onSignal("interrupt");
|
|
35169
|
+
const onSigTerm = () => onSignal("stop");
|
|
35002
35170
|
process.on("SIGINT", onSigInt);
|
|
35003
35171
|
process.on("SIGTERM", onSigTerm);
|
|
35004
35172
|
}
|
|
@@ -35029,7 +35197,7 @@ async function main() {
|
|
|
35029
35197
|
if (opts.cwd && typeof opts.cwd === "string" && opts.cwd.trim()) {
|
|
35030
35198
|
const resolvedCwd = path24.resolve(process.cwd(), opts.cwd.trim());
|
|
35031
35199
|
try {
|
|
35032
|
-
const st =
|
|
35200
|
+
const st = fs20.statSync(resolvedCwd);
|
|
35033
35201
|
if (!st.isDirectory()) {
|
|
35034
35202
|
console.error(`--cwd is not a directory: ${resolvedCwd}`);
|
|
35035
35203
|
process.exit(1);
|