@buildautomaton/cli 0.1.32 → 0.1.34
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 +556 -287
- package/dist/cli.js.map +4 -4
- package/dist/index.js +552 -283
- package/dist/index.js.map +4 -4
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -25064,7 +25064,7 @@ var {
|
|
|
25064
25064
|
} = import_index.default;
|
|
25065
25065
|
|
|
25066
25066
|
// src/cli-version.ts
|
|
25067
|
-
var CLI_VERSION = "0.1.
|
|
25067
|
+
var CLI_VERSION = "0.1.34".length > 0 ? "0.1.34" : "0.0.0-dev";
|
|
25068
25068
|
|
|
25069
25069
|
// src/cli/defaults.ts
|
|
25070
25070
|
var DEFAULT_API_URL = process.env.BUILDAUTOMATON_API_URL ?? "https://api.buildautomaton.com";
|
|
@@ -26696,6 +26696,30 @@ function runPendingAuth(options) {
|
|
|
26696
26696
|
};
|
|
26697
26697
|
}
|
|
26698
26698
|
|
|
26699
|
+
// src/dev-servers/manager/dev-server-constants.ts
|
|
26700
|
+
var BRIDGE_CLOSE_DEV_SERVER_GRACE_MS = 0;
|
|
26701
|
+
var BRIDGE_SHUTDOWN_GRACE_MS = 8e3;
|
|
26702
|
+
|
|
26703
|
+
// src/runtime/cli-process-interrupt.ts
|
|
26704
|
+
var cliImmediateShutdownRequested = false;
|
|
26705
|
+
function requestCliImmediateShutdown() {
|
|
26706
|
+
cliImmediateShutdownRequested = true;
|
|
26707
|
+
}
|
|
26708
|
+
function isCliImmediateShutdownRequested() {
|
|
26709
|
+
return cliImmediateShutdownRequested;
|
|
26710
|
+
}
|
|
26711
|
+
async function delayMsUnlessShutdownRequested(ms) {
|
|
26712
|
+
if (ms <= 0) return true;
|
|
26713
|
+
const end = Date.now() + ms;
|
|
26714
|
+
while (Date.now() < end) {
|
|
26715
|
+
if (isCliImmediateShutdownRequested()) return false;
|
|
26716
|
+
const chunk = Math.min(50, end - Date.now());
|
|
26717
|
+
if (chunk <= 0) break;
|
|
26718
|
+
await new Promise((r) => setTimeout(r, chunk));
|
|
26719
|
+
}
|
|
26720
|
+
return !isCliImmediateShutdownRequested();
|
|
26721
|
+
}
|
|
26722
|
+
|
|
26699
26723
|
// src/sqlite/cli-database.ts
|
|
26700
26724
|
import sqliteWasm from "node-sqlite3-wasm";
|
|
26701
26725
|
|
|
@@ -26999,6 +27023,12 @@ var { Database: SqliteDatabase } = sqliteWasm;
|
|
|
26999
27023
|
var CLI_SQLITE_SYNC_RETRY_MAX = 40;
|
|
27000
27024
|
var CLI_SQLITE_ASYNC_RETRY_MAX = 60;
|
|
27001
27025
|
var CLI_SQLITE_ASYNC_BASE_DELAY_MS = 20;
|
|
27026
|
+
var CliSqliteInterrupted = class extends Error {
|
|
27027
|
+
name = "CliSqliteInterrupted";
|
|
27028
|
+
constructor() {
|
|
27029
|
+
super("CLI SQLite interrupted (shutdown)");
|
|
27030
|
+
}
|
|
27031
|
+
};
|
|
27002
27032
|
function applyCliSqliteConcurrencyPragmas(db) {
|
|
27003
27033
|
try {
|
|
27004
27034
|
db.exec("PRAGMA journal_mode = WAL");
|
|
@@ -27009,7 +27039,7 @@ function applyCliSqliteConcurrencyPragmas(db) {
|
|
|
27009
27039
|
} catch {
|
|
27010
27040
|
}
|
|
27011
27041
|
try {
|
|
27012
|
-
db.run("PRAGMA busy_timeout =
|
|
27042
|
+
db.run("PRAGMA busy_timeout = 500");
|
|
27013
27043
|
} catch {
|
|
27014
27044
|
}
|
|
27015
27045
|
}
|
|
@@ -27030,26 +27060,42 @@ function safeCloseCliSqliteDatabase(db) {
|
|
|
27030
27060
|
function closeAllCliSqliteConnections() {
|
|
27031
27061
|
}
|
|
27032
27062
|
function isCliSqliteLockError(e) {
|
|
27063
|
+
if (e instanceof CliSqliteInterrupted) return false;
|
|
27033
27064
|
const msg = e instanceof Error ? e.message : String(e);
|
|
27034
27065
|
const lower = msg.toLowerCase();
|
|
27035
27066
|
return lower.includes("database is locked") || lower.includes("sqlite_busy") || lower.includes("sqlite3_busy") || lower.includes("database") && lower.includes("locked");
|
|
27036
27067
|
}
|
|
27037
27068
|
function syncSleepMs(ms) {
|
|
27038
27069
|
if (ms <= 0) return;
|
|
27039
|
-
|
|
27040
|
-
|
|
27041
|
-
|
|
27042
|
-
|
|
27043
|
-
|
|
27044
|
-
|
|
27045
|
-
|
|
27070
|
+
const deadline = Date.now() + ms;
|
|
27071
|
+
while (Date.now() < deadline) {
|
|
27072
|
+
if (isCliImmediateShutdownRequested()) throw new CliSqliteInterrupted();
|
|
27073
|
+
const chunk = Math.min(80, deadline - Date.now());
|
|
27074
|
+
if (chunk <= 0) break;
|
|
27075
|
+
try {
|
|
27076
|
+
const sab = new SharedArrayBuffer(4);
|
|
27077
|
+
const ia = new Int32Array(sab);
|
|
27078
|
+
Atomics.wait(ia, 0, 0, chunk);
|
|
27079
|
+
} catch {
|
|
27080
|
+
const end = Date.now() + chunk;
|
|
27081
|
+
while (Date.now() < end) {
|
|
27082
|
+
if (isCliImmediateShutdownRequested()) throw new CliSqliteInterrupted();
|
|
27083
|
+
}
|
|
27046
27084
|
}
|
|
27047
27085
|
}
|
|
27048
27086
|
}
|
|
27049
|
-
function
|
|
27050
|
-
|
|
27087
|
+
async function asyncDelayMsInterruptible(ms) {
|
|
27088
|
+
const end = Date.now() + ms;
|
|
27089
|
+
while (Date.now() < end) {
|
|
27090
|
+
if (isCliImmediateShutdownRequested()) throw new CliSqliteInterrupted();
|
|
27091
|
+
const chunk = Math.min(50, end - Date.now());
|
|
27092
|
+
if (chunk <= 0) break;
|
|
27093
|
+
await new Promise((r) => setTimeout(r, chunk));
|
|
27094
|
+
await yieldToEventLoop();
|
|
27095
|
+
}
|
|
27051
27096
|
}
|
|
27052
27097
|
function openCliSqliteConnection(options) {
|
|
27098
|
+
if (isCliImmediateShutdownRequested()) throw new CliSqliteInterrupted();
|
|
27053
27099
|
const sqlitePath = getCliSqlitePath();
|
|
27054
27100
|
ensureCliSqliteParentDir(sqlitePath);
|
|
27055
27101
|
const db = new SqliteDatabase(sqlitePath);
|
|
@@ -27066,6 +27112,7 @@ function openCliSqliteConnection(options) {
|
|
|
27066
27112
|
}
|
|
27067
27113
|
function withCliSqliteSync(fn, options) {
|
|
27068
27114
|
for (let attempt = 1; attempt <= CLI_SQLITE_SYNC_RETRY_MAX; attempt++) {
|
|
27115
|
+
if (isCliImmediateShutdownRequested()) throw new CliSqliteInterrupted();
|
|
27069
27116
|
let db;
|
|
27070
27117
|
try {
|
|
27071
27118
|
db = openCliSqliteConnection(options);
|
|
@@ -27076,6 +27123,7 @@ function withCliSqliteSync(fn, options) {
|
|
|
27076
27123
|
}
|
|
27077
27124
|
} catch (e) {
|
|
27078
27125
|
safeCloseCliSqliteDatabase(db);
|
|
27126
|
+
if (e instanceof CliSqliteInterrupted) throw e;
|
|
27079
27127
|
if (!isCliSqliteLockError(e) || attempt === CLI_SQLITE_SYNC_RETRY_MAX) throw e;
|
|
27080
27128
|
syncSleepMs(Math.min(500, 12 * attempt));
|
|
27081
27129
|
}
|
|
@@ -27085,6 +27133,7 @@ function withCliSqliteSync(fn, options) {
|
|
|
27085
27133
|
async function withCliSqlite(fn, options) {
|
|
27086
27134
|
let lastError;
|
|
27087
27135
|
for (let attempt = 1; attempt <= CLI_SQLITE_ASYNC_RETRY_MAX; attempt++) {
|
|
27136
|
+
if (isCliImmediateShutdownRequested()) throw new CliSqliteInterrupted();
|
|
27088
27137
|
let db;
|
|
27089
27138
|
try {
|
|
27090
27139
|
db = openCliSqliteConnection(options);
|
|
@@ -27096,9 +27145,10 @@ async function withCliSqlite(fn, options) {
|
|
|
27096
27145
|
} catch (e) {
|
|
27097
27146
|
lastError = e;
|
|
27098
27147
|
safeCloseCliSqliteDatabase(db);
|
|
27148
|
+
if (e instanceof CliSqliteInterrupted) throw e;
|
|
27099
27149
|
if (!isCliSqliteLockError(e) || attempt === CLI_SQLITE_ASYNC_RETRY_MAX) throw e;
|
|
27100
27150
|
const delayMs = Math.min(600, CLI_SQLITE_ASYNC_BASE_DELAY_MS * attempt);
|
|
27101
|
-
await
|
|
27151
|
+
await asyncDelayMsInterruptible(delayMs);
|
|
27102
27152
|
await yieldToEventLoop();
|
|
27103
27153
|
}
|
|
27104
27154
|
}
|
|
@@ -27111,9 +27161,9 @@ async function ensureCliSqliteInitialized(options) {
|
|
|
27111
27161
|
|
|
27112
27162
|
// src/connection/close-bridge-connection.ts
|
|
27113
27163
|
async function closeBridgeConnection(state, acpManager, devServerManager, log2) {
|
|
27164
|
+
requestCliImmediateShutdown();
|
|
27114
27165
|
const say = log2 ?? logImmediate;
|
|
27115
27166
|
say("Cleaning up connections\u2026");
|
|
27116
|
-
await new Promise((resolve20) => setImmediate(resolve20));
|
|
27117
27167
|
state.closedByUser = true;
|
|
27118
27168
|
clearReconnectQuietTimer(state.mainQuiet);
|
|
27119
27169
|
clearReconnectQuietTimer(state.firehoseQuiet);
|
|
@@ -27150,7 +27200,7 @@ async function closeBridgeConnection(state, acpManager, devServerManager, log2)
|
|
|
27150
27200
|
}
|
|
27151
27201
|
if (devServerManager) {
|
|
27152
27202
|
say("Stopping local dev server processes\u2026");
|
|
27153
|
-
await devServerManager.shutdownAllGraceful();
|
|
27203
|
+
await devServerManager.shutdownAllGraceful({ graceMs: BRIDGE_CLOSE_DEV_SERVER_GRACE_MS });
|
|
27154
27204
|
}
|
|
27155
27205
|
try {
|
|
27156
27206
|
closeAllCliSqliteConnections();
|
|
@@ -27733,17 +27783,13 @@ function getAgentModelFromAgentConfig(config2) {
|
|
|
27733
27783
|
return t !== "" ? t : null;
|
|
27734
27784
|
}
|
|
27735
27785
|
|
|
27736
|
-
// src/git/session-git-queue.ts
|
|
27737
|
-
import { execFile as execFile7 } from "node:child_process";
|
|
27786
|
+
// src/git/snapshot/session-git-queue.ts
|
|
27738
27787
|
import { readFile as readFile2, stat as stat2 } from "node:fs/promises";
|
|
27739
|
-
import { promisify as promisify7 } from "node:util";
|
|
27740
27788
|
import * as path15 from "node:path";
|
|
27741
27789
|
|
|
27742
|
-
// src/git/pre-turn-snapshot.ts
|
|
27790
|
+
// src/git/snapshot/pre-turn-snapshot.ts
|
|
27743
27791
|
import * as fs15 from "node:fs";
|
|
27744
27792
|
import * as path14 from "node:path";
|
|
27745
|
-
import { execFile as execFile6 } from "node:child_process";
|
|
27746
|
-
import { promisify as promisify6 } from "node:util";
|
|
27747
27793
|
|
|
27748
27794
|
// src/git/discover-repos.ts
|
|
27749
27795
|
import * as fs14 from "node:fs";
|
|
@@ -32308,12 +32354,68 @@ function gitInstanceFactory(baseDir, options) {
|
|
|
32308
32354
|
init_git_response_error();
|
|
32309
32355
|
var simpleGit = gitInstanceFactory;
|
|
32310
32356
|
|
|
32357
|
+
// src/git/git-runtime.ts
|
|
32358
|
+
var activeGitChildProcesses = /* @__PURE__ */ new Set();
|
|
32359
|
+
function abortActiveGitChildProcesses() {
|
|
32360
|
+
for (const child of activeGitChildProcesses) {
|
|
32361
|
+
try {
|
|
32362
|
+
child.kill("SIGTERM");
|
|
32363
|
+
} catch {
|
|
32364
|
+
}
|
|
32365
|
+
}
|
|
32366
|
+
}
|
|
32367
|
+
var GitOperationAbortedError = class extends Error {
|
|
32368
|
+
constructor(message = "Git operation aborted") {
|
|
32369
|
+
super(message);
|
|
32370
|
+
this.name = "GitOperationAbortedError";
|
|
32371
|
+
}
|
|
32372
|
+
};
|
|
32373
|
+
function throwIfGitShutdownRequested() {
|
|
32374
|
+
if (isCliImmediateShutdownRequested()) {
|
|
32375
|
+
abortActiveGitChildProcesses();
|
|
32376
|
+
throw new GitOperationAbortedError();
|
|
32377
|
+
}
|
|
32378
|
+
}
|
|
32379
|
+
async function runGitTask(fn) {
|
|
32380
|
+
throwIfGitShutdownRequested();
|
|
32381
|
+
try {
|
|
32382
|
+
const result = await fn();
|
|
32383
|
+
throwIfGitShutdownRequested();
|
|
32384
|
+
return result;
|
|
32385
|
+
} catch (e) {
|
|
32386
|
+
if (isCliImmediateShutdownRequested()) {
|
|
32387
|
+
throw new GitOperationAbortedError();
|
|
32388
|
+
}
|
|
32389
|
+
throw e;
|
|
32390
|
+
}
|
|
32391
|
+
}
|
|
32392
|
+
async function yieldToEventLoop2() {
|
|
32393
|
+
throwIfGitShutdownRequested();
|
|
32394
|
+
await new Promise((resolve20) => {
|
|
32395
|
+
setImmediate(resolve20);
|
|
32396
|
+
});
|
|
32397
|
+
throwIfGitShutdownRequested();
|
|
32398
|
+
}
|
|
32399
|
+
async function forEachWithGitYield(items, fn) {
|
|
32400
|
+
for (let i = 0; i < items.length; i++) {
|
|
32401
|
+
if (i > 0 || items.length > 1) await yieldToEventLoop2();
|
|
32402
|
+
await fn(items[i], i);
|
|
32403
|
+
}
|
|
32404
|
+
}
|
|
32405
|
+
|
|
32311
32406
|
// src/git/cli-simple-git.ts
|
|
32312
32407
|
function cliSimpleGit(baseDir) {
|
|
32313
|
-
const git = simpleGit({
|
|
32408
|
+
const git = simpleGit({
|
|
32409
|
+
baseDir,
|
|
32410
|
+
trimmed: true,
|
|
32411
|
+
spawnOptions: {}
|
|
32412
|
+
});
|
|
32314
32413
|
git.outputHandler((command, stdout, stderr) => {
|
|
32315
32414
|
const trace = isCliTrace();
|
|
32316
32415
|
const onChunk = (label) => (chunk) => {
|
|
32416
|
+
if (isCliImmediateShutdownRequested()) {
|
|
32417
|
+
abortActiveGitChildProcesses();
|
|
32418
|
+
}
|
|
32317
32419
|
const text = typeof chunk === "string" ? chunk : chunk.toString("utf8");
|
|
32318
32420
|
const line = text.replace(/\s+$/, "");
|
|
32319
32421
|
if (trace && line) {
|
|
@@ -32402,17 +32504,37 @@ async function discoverGitReposUnderRoot(rootPath) {
|
|
|
32402
32504
|
return out;
|
|
32403
32505
|
}
|
|
32404
32506
|
|
|
32405
|
-
// src/git/
|
|
32507
|
+
// src/git/git-exec.ts
|
|
32508
|
+
import { execFile as execFile6, spawn as spawn2 } from "node:child_process";
|
|
32509
|
+
import { promisify as promisify6 } from "node:util";
|
|
32406
32510
|
var execFileAsync5 = promisify6(execFile6);
|
|
32511
|
+
async function execGitFile(args, options) {
|
|
32512
|
+
throwIfGitShutdownRequested();
|
|
32513
|
+
try {
|
|
32514
|
+
const result = await execFileAsync5("git", args, {
|
|
32515
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
32516
|
+
...options
|
|
32517
|
+
});
|
|
32518
|
+
throwIfGitShutdownRequested();
|
|
32519
|
+
return {
|
|
32520
|
+
stdout: String(result.stdout ?? ""),
|
|
32521
|
+
stderr: String(result.stderr ?? "")
|
|
32522
|
+
};
|
|
32523
|
+
} catch (e) {
|
|
32524
|
+
if (isCliImmediateShutdownRequested()) {
|
|
32525
|
+
throw new GitOperationAbortedError();
|
|
32526
|
+
}
|
|
32527
|
+
throw e;
|
|
32528
|
+
}
|
|
32529
|
+
}
|
|
32530
|
+
|
|
32531
|
+
// src/git/snapshot/pre-turn-snapshot.ts
|
|
32407
32532
|
function snapshotsDirForCwd(agentCwd) {
|
|
32408
32533
|
return path14.join(agentCwd, ".buildautomaton", "snapshots");
|
|
32409
32534
|
}
|
|
32410
32535
|
async function gitStashCreate(repoRoot, log2) {
|
|
32411
32536
|
try {
|
|
32412
|
-
const { stdout } = await
|
|
32413
|
-
cwd: repoRoot,
|
|
32414
|
-
maxBuffer: 10 * 1024 * 1024
|
|
32415
|
-
});
|
|
32537
|
+
const { stdout } = await execGitFile(["stash", "create"], { cwd: repoRoot });
|
|
32416
32538
|
return stdout.trim();
|
|
32417
32539
|
} catch (e) {
|
|
32418
32540
|
log2(
|
|
@@ -32423,7 +32545,7 @@ async function gitStashCreate(repoRoot, log2) {
|
|
|
32423
32545
|
}
|
|
32424
32546
|
async function gitRun(repoRoot, args, log2, label) {
|
|
32425
32547
|
try {
|
|
32426
|
-
await
|
|
32548
|
+
await execGitFile(args, { cwd: repoRoot });
|
|
32427
32549
|
return { ok: true };
|
|
32428
32550
|
} catch (e) {
|
|
32429
32551
|
const msg = e instanceof Error ? e.message : String(e);
|
|
@@ -32457,10 +32579,10 @@ async function capturePreTurnSnapshot(options) {
|
|
|
32457
32579
|
return { ok: false, error: "No git repos to snapshot" };
|
|
32458
32580
|
}
|
|
32459
32581
|
const repos = [];
|
|
32460
|
-
|
|
32582
|
+
await forEachWithGitYield(repoRoots, async (root) => {
|
|
32461
32583
|
const stashSha = await gitStashCreate(root, log2);
|
|
32462
32584
|
repos.push({ path: root, stashSha });
|
|
32463
|
-
}
|
|
32585
|
+
});
|
|
32464
32586
|
const dir = snapshotsDirForCwd(agentCwd);
|
|
32465
32587
|
try {
|
|
32466
32588
|
fs15.mkdirSync(dir, { recursive: true });
|
|
@@ -32495,17 +32617,25 @@ async function applyPreTurnSnapshot(filePath, log2) {
|
|
|
32495
32617
|
if (!Array.isArray(data.repos)) {
|
|
32496
32618
|
return { ok: false, error: "Invalid snapshot file" };
|
|
32497
32619
|
}
|
|
32498
|
-
|
|
32499
|
-
|
|
32620
|
+
let applyError = null;
|
|
32621
|
+
await forEachWithGitYield(data.repos, async (r) => {
|
|
32622
|
+
if (applyError || !r.path) return;
|
|
32500
32623
|
const reset = await gitRun(r.path, ["reset", "--hard", "HEAD"], log2, "reset --hard");
|
|
32501
|
-
if (!reset.ok)
|
|
32624
|
+
if (!reset.ok) {
|
|
32625
|
+
applyError = reset;
|
|
32626
|
+
return;
|
|
32627
|
+
}
|
|
32502
32628
|
const clean = await gitRun(r.path, ["clean", "-fd"], log2, "clean -fd");
|
|
32503
|
-
if (!clean.ok)
|
|
32629
|
+
if (!clean.ok) {
|
|
32630
|
+
applyError = clean;
|
|
32631
|
+
return;
|
|
32632
|
+
}
|
|
32504
32633
|
if (r.stashSha) {
|
|
32505
32634
|
const ap = await gitRun(r.path, ["stash", "apply", r.stashSha], log2, "stash apply");
|
|
32506
|
-
if (!ap.ok)
|
|
32635
|
+
if (!ap.ok) applyError = ap;
|
|
32507
32636
|
}
|
|
32508
|
-
}
|
|
32637
|
+
});
|
|
32638
|
+
if (applyError?.ok === false) return applyError;
|
|
32509
32639
|
log2(`[snapshot] Restored pre-turn state for ${data.runId.slice(0, 8)}\u2026`);
|
|
32510
32640
|
return { ok: true };
|
|
32511
32641
|
}
|
|
@@ -32513,8 +32643,7 @@ function snapshotFilePath(agentCwd, runId) {
|
|
|
32513
32643
|
return path14.join(snapshotsDirForCwd(agentCwd), `${runId}.json`);
|
|
32514
32644
|
}
|
|
32515
32645
|
|
|
32516
|
-
// src/git/session-git-queue.ts
|
|
32517
|
-
var execFileAsync6 = promisify7(execFile7);
|
|
32646
|
+
// src/git/snapshot/session-git-queue.ts
|
|
32518
32647
|
var MAX_FULL_FILE_TEXT_BYTES = 512 * 1024;
|
|
32519
32648
|
async function readWorkspaceFileAsUtf8(absPath) {
|
|
32520
32649
|
try {
|
|
@@ -32543,35 +32672,28 @@ async function collectTurnGitDiffFromPreTurnSnapshot(options) {
|
|
|
32543
32672
|
return;
|
|
32544
32673
|
}
|
|
32545
32674
|
const multiRepo = data.repos.length > 1;
|
|
32546
|
-
|
|
32547
|
-
if (!repo.stashSha)
|
|
32675
|
+
await forEachWithGitYield(data.repos, async (repo) => {
|
|
32676
|
+
if (!repo.stashSha) return;
|
|
32548
32677
|
let namesRaw;
|
|
32549
32678
|
try {
|
|
32550
|
-
const { stdout } = await
|
|
32551
|
-
cwd: repo.path,
|
|
32552
|
-
maxBuffer: 10 * 1024 * 1024
|
|
32553
|
-
});
|
|
32679
|
+
const { stdout } = await execGitFile(["diff", "--name-only", repo.stashSha], { cwd: repo.path });
|
|
32554
32680
|
namesRaw = stdout;
|
|
32555
32681
|
} catch (e) {
|
|
32556
32682
|
log2(
|
|
32557
32683
|
`[session-git-queue] Git diff --name-only failed in ${repo.path}: ${e instanceof Error ? e.message : String(e)}`
|
|
32558
32684
|
);
|
|
32559
|
-
|
|
32685
|
+
return;
|
|
32560
32686
|
}
|
|
32561
32687
|
const lines = namesRaw.split("\n").map((l) => l.trim()).filter(Boolean);
|
|
32562
32688
|
const slug = path15.basename(repo.path).replace(/[^\w.-]+/g, "_") || "repo";
|
|
32563
|
-
|
|
32564
|
-
if (rel.includes(".."))
|
|
32689
|
+
await forEachWithGitYield(lines, async (rel) => {
|
|
32690
|
+
if (rel.includes("..")) return;
|
|
32565
32691
|
try {
|
|
32566
|
-
const { stdout: patchContent } = await
|
|
32567
|
-
"git",
|
|
32692
|
+
const { stdout: patchContent } = await execGitFile(
|
|
32568
32693
|
["diff", "--no-color", repo.stashSha, "--", rel],
|
|
32569
|
-
{
|
|
32570
|
-
cwd: repo.path,
|
|
32571
|
-
maxBuffer: 50 * 1024 * 1024
|
|
32572
|
-
}
|
|
32694
|
+
{ cwd: repo.path }
|
|
32573
32695
|
);
|
|
32574
|
-
if (!patchContent.trim())
|
|
32696
|
+
if (!patchContent.trim()) return;
|
|
32575
32697
|
const displayPath = multiRepo ? `${slug}/${rel}` : rel;
|
|
32576
32698
|
const workspaceFilePath = path15.join(repo.path, rel);
|
|
32577
32699
|
const newText = await readWorkspaceFileAsUtf8(workspaceFilePath);
|
|
@@ -32588,8 +32710,8 @@ async function collectTurnGitDiffFromPreTurnSnapshot(options) {
|
|
|
32588
32710
|
`[session-git-queue] Git diff failed for ${rel}: ${e instanceof Error ? e.message : String(e)}`
|
|
32589
32711
|
);
|
|
32590
32712
|
}
|
|
32591
|
-
}
|
|
32592
|
-
}
|
|
32713
|
+
});
|
|
32714
|
+
});
|
|
32593
32715
|
}
|
|
32594
32716
|
|
|
32595
32717
|
// src/agents/acp/put-summarize-change-summaries.ts
|
|
@@ -32929,16 +33051,38 @@ __export(claude_code_acp_client_exports, {
|
|
|
32929
33051
|
createClaudeCodeAcpClient: () => createClaudeCodeAcpClient,
|
|
32930
33052
|
detectLocalAgentPresence: () => detectLocalAgentPresence
|
|
32931
33053
|
});
|
|
32932
|
-
import { execFile as execFile9 } from "node:child_process";
|
|
32933
|
-
import { promisify as promisify9 } from "node:util";
|
|
32934
33054
|
|
|
32935
33055
|
// src/agents/acp/clients/detect-command-on-path.ts
|
|
32936
|
-
import { execFile as
|
|
32937
|
-
import { promisify as
|
|
32938
|
-
var
|
|
32939
|
-
|
|
33056
|
+
import { execFile as execFile7 } from "node:child_process";
|
|
33057
|
+
import { promisify as promisify7 } from "node:util";
|
|
33058
|
+
var execFileAsync6 = promisify7(execFile7);
|
|
33059
|
+
var COMMAND_ON_PATH_PROBE_TIMEOUT_MS = 750;
|
|
33060
|
+
async function execFileShutdownAware(file2, args, timeoutMs) {
|
|
33061
|
+
if (isCliImmediateShutdownRequested()) throw new Error("shutdown");
|
|
33062
|
+
const ac = new AbortController();
|
|
33063
|
+
const shutdownPoll = setInterval(() => {
|
|
33064
|
+
if (isCliImmediateShutdownRequested()) ac.abort();
|
|
33065
|
+
}, 50);
|
|
33066
|
+
shutdownPoll.unref?.();
|
|
33067
|
+
try {
|
|
33068
|
+
await execFileAsync6(file2, args, { timeout: timeoutMs, signal: ac.signal });
|
|
33069
|
+
} finally {
|
|
33070
|
+
clearInterval(shutdownPoll);
|
|
33071
|
+
}
|
|
33072
|
+
}
|
|
33073
|
+
async function isCommandOnPath(command, timeoutMs = COMMAND_ON_PATH_PROBE_TIMEOUT_MS) {
|
|
33074
|
+
if (isCliImmediateShutdownRequested()) return false;
|
|
33075
|
+
try {
|
|
33076
|
+
await execFileShutdownAware("which", [command], timeoutMs);
|
|
33077
|
+
return true;
|
|
33078
|
+
} catch {
|
|
33079
|
+
return false;
|
|
33080
|
+
}
|
|
33081
|
+
}
|
|
33082
|
+
async function execProbeShutdownAware(file2, args, timeoutMs) {
|
|
33083
|
+
if (isCliImmediateShutdownRequested()) return false;
|
|
32940
33084
|
try {
|
|
32941
|
-
await
|
|
33085
|
+
await execFileShutdownAware(file2, args, timeoutMs);
|
|
32942
33086
|
return true;
|
|
32943
33087
|
} catch {
|
|
32944
33088
|
return false;
|
|
@@ -32946,7 +33090,7 @@ async function isCommandOnPath(command, timeoutMs = 4e3) {
|
|
|
32946
33090
|
}
|
|
32947
33091
|
|
|
32948
33092
|
// src/agents/acp/clients/sdk/sdk-stdio-acp-client.ts
|
|
32949
|
-
import { spawn as
|
|
33093
|
+
import { spawn as spawn3 } from "node:child_process";
|
|
32950
33094
|
import { Readable, Writable } from "node:stream";
|
|
32951
33095
|
|
|
32952
33096
|
// src/agents/acp/clients/agent-stderr-capture.ts
|
|
@@ -33541,7 +33685,7 @@ async function createSdkStdioAcpClient(options) {
|
|
|
33541
33685
|
getActiveConfigOptions
|
|
33542
33686
|
} = options;
|
|
33543
33687
|
const isWindows = process.platform === "win32";
|
|
33544
|
-
const child =
|
|
33688
|
+
const child = spawn3(command[0], command.slice(1), {
|
|
33545
33689
|
cwd,
|
|
33546
33690
|
stdio: ["pipe", "pipe", "pipe"],
|
|
33547
33691
|
env: process.env,
|
|
@@ -33699,16 +33843,10 @@ async function createSdkStdioAcpClient(options) {
|
|
|
33699
33843
|
}
|
|
33700
33844
|
|
|
33701
33845
|
// src/agents/acp/clients/claude-code-acp-client.ts
|
|
33702
|
-
var execFileAsync8 = promisify9(execFile9);
|
|
33703
33846
|
var BACKEND_LOCAL_AGENT_TYPE = "claude-code";
|
|
33704
33847
|
async function detectLocalAgentPresence() {
|
|
33705
33848
|
if (await isCommandOnPath("claude")) return true;
|
|
33706
|
-
|
|
33707
|
-
await execFileAsync8("npx", ["--yes", "@anthropic-ai/claude-code", "--version"], { timeout: 25e3 });
|
|
33708
|
-
return true;
|
|
33709
|
-
} catch {
|
|
33710
|
-
return false;
|
|
33711
|
-
}
|
|
33849
|
+
return execProbeShutdownAware("npx", ["--yes", "@anthropic-ai/claude-code", "--version"], 3e3);
|
|
33712
33850
|
}
|
|
33713
33851
|
function buildClaudeCodeAcpSpawnCommand(base, _sessionMode) {
|
|
33714
33852
|
return [...base];
|
|
@@ -33760,7 +33898,7 @@ __export(cursor_acp_client_exports, {
|
|
|
33760
33898
|
createCursorAcpClient: () => createCursorAcpClient,
|
|
33761
33899
|
detectLocalAgentPresence: () => detectLocalAgentPresence3
|
|
33762
33900
|
});
|
|
33763
|
-
import { spawn as
|
|
33901
|
+
import { spawn as spawn4 } from "node:child_process";
|
|
33764
33902
|
import * as readline from "node:readline";
|
|
33765
33903
|
|
|
33766
33904
|
// src/agents/acp/format-session-update-kind-for-log.ts
|
|
@@ -33830,7 +33968,7 @@ async function createCursorAcpClient(options) {
|
|
|
33830
33968
|
} = options;
|
|
33831
33969
|
const dbgFs = process.env.BUILDAUTOMATON_DEBUG_ACP_FS === "1";
|
|
33832
33970
|
const isWindows = process.platform === "win32";
|
|
33833
|
-
const child =
|
|
33971
|
+
const child = spawn4(command[0], command.slice(1), {
|
|
33834
33972
|
cwd,
|
|
33835
33973
|
stdio: ["pipe", "pipe", "pipe"],
|
|
33836
33974
|
env: process.env,
|
|
@@ -35276,7 +35414,7 @@ import os8 from "node:os";
|
|
|
35276
35414
|
import * as fs18 from "node:fs";
|
|
35277
35415
|
import * as path21 from "node:path";
|
|
35278
35416
|
|
|
35279
|
-
// src/git/worktree-add.ts
|
|
35417
|
+
// src/git/worktrees/worktree-add.ts
|
|
35280
35418
|
async function gitWorktreeAddBranch(mainRepoPath, worktreePath, branch) {
|
|
35281
35419
|
const mainGit = cliSimpleGit(mainRepoPath);
|
|
35282
35420
|
await mainGit.raw(["worktree", "add", "-b", branch, worktreePath, "HEAD"]);
|
|
@@ -35381,7 +35519,7 @@ async function prepareNewSessionWorktrees(options) {
|
|
|
35381
35519
|
};
|
|
35382
35520
|
}
|
|
35383
35521
|
|
|
35384
|
-
// src/git/rename-branch.ts
|
|
35522
|
+
// src/git/branches/rename-branch.ts
|
|
35385
35523
|
async function gitRenameCurrentBranch(repoDir, newName) {
|
|
35386
35524
|
const g = cliSimpleGit(repoDir);
|
|
35387
35525
|
await g.raw(["branch", "-m", newName]);
|
|
@@ -35405,10 +35543,10 @@ async function renameSessionWorktreeBranches(paths, newBranch, log2) {
|
|
|
35405
35543
|
// src/worktrees/remove-session-worktrees.ts
|
|
35406
35544
|
import * as fs21 from "node:fs";
|
|
35407
35545
|
|
|
35408
|
-
// src/git/worktree-remove.ts
|
|
35546
|
+
// src/git/worktrees/worktree-remove.ts
|
|
35409
35547
|
import * as fs20 from "node:fs";
|
|
35410
35548
|
|
|
35411
|
-
// src/git/resolve-main-repo-from-git-file.ts
|
|
35549
|
+
// src/git/worktrees/resolve-main-repo-from-git-file.ts
|
|
35412
35550
|
import * as fs19 from "node:fs";
|
|
35413
35551
|
import * as path22 from "node:path";
|
|
35414
35552
|
function resolveMainRepoFromWorktreeGitFile(wt) {
|
|
@@ -35422,7 +35560,7 @@ function resolveMainRepoFromWorktreeGitFile(wt) {
|
|
|
35422
35560
|
return path22.dirname(gitDir);
|
|
35423
35561
|
}
|
|
35424
35562
|
|
|
35425
|
-
// src/git/worktree-remove.ts
|
|
35563
|
+
// src/git/worktrees/worktree-remove.ts
|
|
35426
35564
|
async function gitWorktreeRemoveForce(worktreePath) {
|
|
35427
35565
|
const mainRepo = resolveMainRepoFromWorktreeGitFile(worktreePath);
|
|
35428
35566
|
if (mainRepo) {
|
|
@@ -35448,7 +35586,88 @@ async function removeSessionWorktrees(paths, log2) {
|
|
|
35448
35586
|
}
|
|
35449
35587
|
}
|
|
35450
35588
|
|
|
35451
|
-
// src/git/
|
|
35589
|
+
// src/git/changes/lib/parse-git-status.ts
|
|
35590
|
+
function parseNameStatusLines(lines) {
|
|
35591
|
+
const m = /* @__PURE__ */ new Map();
|
|
35592
|
+
for (const line of lines) {
|
|
35593
|
+
if (!line.trim()) continue;
|
|
35594
|
+
const tabParts = line.split(" ");
|
|
35595
|
+
if (tabParts.length < 2) continue;
|
|
35596
|
+
const status = tabParts[0].trim();
|
|
35597
|
+
const code = status[0];
|
|
35598
|
+
if (code === "A") {
|
|
35599
|
+
m.set(tabParts[tabParts.length - 1], "added");
|
|
35600
|
+
} else if (code === "D") {
|
|
35601
|
+
m.set(tabParts[tabParts.length - 1], "removed");
|
|
35602
|
+
} else if (code === "R" || code === "C") {
|
|
35603
|
+
if (tabParts.length >= 3) m.set(tabParts[tabParts.length - 1], "modified");
|
|
35604
|
+
} else if (code === "M" || code === "U" || code === "T") {
|
|
35605
|
+
m.set(tabParts[tabParts.length - 1], "modified");
|
|
35606
|
+
}
|
|
35607
|
+
}
|
|
35608
|
+
return m;
|
|
35609
|
+
}
|
|
35610
|
+
function parseNumstatFirstLine(line) {
|
|
35611
|
+
const parts = line.split(" ");
|
|
35612
|
+
if (parts.length < 3) return null;
|
|
35613
|
+
const [a, d] = parts;
|
|
35614
|
+
const additions = a === "-" ? 0 : parseInt(String(a), 10) || 0;
|
|
35615
|
+
const deletions = d === "-" ? 0 : parseInt(String(d), 10) || 0;
|
|
35616
|
+
return { additions, deletions };
|
|
35617
|
+
}
|
|
35618
|
+
function parseNumstat(lines) {
|
|
35619
|
+
const m = /* @__PURE__ */ new Map();
|
|
35620
|
+
for (const line of lines) {
|
|
35621
|
+
if (!line.trim()) continue;
|
|
35622
|
+
const parts = line.split(" ");
|
|
35623
|
+
if (parts.length < 3) continue;
|
|
35624
|
+
const [a, d, p] = parts;
|
|
35625
|
+
const additions = a === "-" ? 0 : parseInt(String(a), 10) || 0;
|
|
35626
|
+
const deletions = d === "-" ? 0 : parseInt(String(d), 10) || 0;
|
|
35627
|
+
m.set(p, { additions, deletions });
|
|
35628
|
+
}
|
|
35629
|
+
return m;
|
|
35630
|
+
}
|
|
35631
|
+
async function numstatFromGitNoIndex(g, pathInRepo) {
|
|
35632
|
+
const devNull = process.platform === "win32" ? "NUL" : "/dev/null";
|
|
35633
|
+
try {
|
|
35634
|
+
const out = await g.raw(["diff", "--numstat", "--no-index", "--", devNull, pathInRepo]);
|
|
35635
|
+
const first2 = String(out).split("\n").find((l) => l.trim()) ?? "";
|
|
35636
|
+
return parseNumstatFirstLine(first2);
|
|
35637
|
+
} catch {
|
|
35638
|
+
return null;
|
|
35639
|
+
}
|
|
35640
|
+
}
|
|
35641
|
+
|
|
35642
|
+
// src/git/changes/lib/working-tree-changed-path-count.ts
|
|
35643
|
+
function workingTreeChangedPathCount(nameStatusLines, numstatLines, untrackedLines) {
|
|
35644
|
+
const kindByPath = parseNameStatusLines(nameStatusLines);
|
|
35645
|
+
const numByPath = parseNumstat(numstatLines);
|
|
35646
|
+
const paths = /* @__PURE__ */ new Set([...kindByPath.keys(), ...numByPath.keys()]);
|
|
35647
|
+
for (const p of untrackedLines.map((s) => s.trim()).filter(Boolean)) {
|
|
35648
|
+
paths.add(p);
|
|
35649
|
+
}
|
|
35650
|
+
return paths.size;
|
|
35651
|
+
}
|
|
35652
|
+
|
|
35653
|
+
// src/git/changes/count-working-tree-changed-files.ts
|
|
35654
|
+
async function countWorkingTreeChangedFilesForRepo(repoGitCwd) {
|
|
35655
|
+
return runGitTask(async () => {
|
|
35656
|
+
const g = cliSimpleGit(repoGitCwd);
|
|
35657
|
+
const [nameStatusRaw, numstatRaw, untrackedRaw] = await Promise.all([
|
|
35658
|
+
g.raw(["diff", "--name-status", "HEAD"]).catch(() => ""),
|
|
35659
|
+
g.raw(["diff", "HEAD", "--numstat"]).catch(() => ""),
|
|
35660
|
+
g.raw(["ls-files", "--others", "--exclude-standard"]).catch(() => "")
|
|
35661
|
+
]);
|
|
35662
|
+
return workingTreeChangedPathCount(
|
|
35663
|
+
String(nameStatusRaw).split("\n"),
|
|
35664
|
+
String(numstatRaw).split("\n"),
|
|
35665
|
+
String(untrackedRaw).split("\n")
|
|
35666
|
+
);
|
|
35667
|
+
});
|
|
35668
|
+
}
|
|
35669
|
+
|
|
35670
|
+
// src/git/commits/resolve-remote-tracking.ts
|
|
35452
35671
|
async function tryConfigGet(g, key) {
|
|
35453
35672
|
try {
|
|
35454
35673
|
const out = await g.raw(["config", "--get", key]);
|
|
@@ -35531,30 +35750,6 @@ async function resolveBaseShaForUnpushedCommits(g) {
|
|
|
35531
35750
|
if (!defaultRef) return null;
|
|
35532
35751
|
return revParseSafe(g, defaultRef);
|
|
35533
35752
|
}
|
|
35534
|
-
function parseLogShaDateSubjectLines(raw) {
|
|
35535
|
-
const out = [];
|
|
35536
|
-
for (const line of String(raw).split("\n")) {
|
|
35537
|
-
const l = line.trimEnd();
|
|
35538
|
-
if (!l.trim()) continue;
|
|
35539
|
-
const parts = l.split(" ");
|
|
35540
|
-
if (parts.length < 3) continue;
|
|
35541
|
-
const sha = parts[0].trim();
|
|
35542
|
-
const committedAt = parts[1].trim();
|
|
35543
|
-
const subject = parts.slice(2).join(" ").trim();
|
|
35544
|
-
if (!/^[0-9a-f]{7,40}$/i.test(sha)) continue;
|
|
35545
|
-
out.push({ sha, shortSha: sha.slice(0, 7), subject, committedAt });
|
|
35546
|
-
}
|
|
35547
|
-
return out;
|
|
35548
|
-
}
|
|
35549
|
-
async function gitLogNotReachableFromBase(g, baseSha, headSha) {
|
|
35550
|
-
if (baseSha === headSha) return [];
|
|
35551
|
-
try {
|
|
35552
|
-
const logOut = await g.raw(["log", "--format=%H %cI %s", `${baseSha}..${headSha}`]);
|
|
35553
|
-
return parseLogShaDateSubjectLines(logOut);
|
|
35554
|
-
} catch {
|
|
35555
|
-
return [];
|
|
35556
|
-
}
|
|
35557
|
-
}
|
|
35558
35753
|
async function commitsAheadOfRemoteTracking(repoDir) {
|
|
35559
35754
|
const g = cliSimpleGit(repoDir);
|
|
35560
35755
|
const headSha = await revParseSafe(g, "HEAD");
|
|
@@ -35569,44 +35764,41 @@ async function commitsAheadOfRemoteTracking(repoDir) {
|
|
|
35569
35764
|
return 0;
|
|
35570
35765
|
}
|
|
35571
35766
|
}
|
|
35767
|
+
|
|
35768
|
+
// src/git/status/working-tree-status.ts
|
|
35572
35769
|
async function getRepoWorkingTreeStatus(repoDir) {
|
|
35573
|
-
|
|
35574
|
-
|
|
35575
|
-
|
|
35576
|
-
|
|
35577
|
-
|
|
35578
|
-
}
|
|
35579
|
-
async function listUnpushedCommits(repoDir) {
|
|
35580
|
-
const g = cliSimpleGit(repoDir);
|
|
35581
|
-
const headSha = await revParseSafe(g, "HEAD");
|
|
35582
|
-
if (!headSha) return [];
|
|
35583
|
-
const baseSha = await resolveBaseShaForUnpushedCommits(g);
|
|
35584
|
-
if (!baseSha) return [];
|
|
35585
|
-
return gitLogNotReachableFromBase(g, baseSha, headSha);
|
|
35770
|
+
return runGitTask(async () => {
|
|
35771
|
+
const uncommittedFileCount = await countWorkingTreeChangedFilesForRepo(repoDir);
|
|
35772
|
+
const hasUncommittedChanges = uncommittedFileCount > 0;
|
|
35773
|
+
const ahead = await commitsAheadOfRemoteTracking(repoDir);
|
|
35774
|
+
return { hasUncommittedChanges, hasUnpushedCommits: ahead > 0, uncommittedFileCount };
|
|
35775
|
+
});
|
|
35586
35776
|
}
|
|
35587
35777
|
async function aggregateSessionPathsWorkingTreeStatus(paths) {
|
|
35588
35778
|
let hasUncommittedChanges = false;
|
|
35589
35779
|
let hasUnpushedCommits = false;
|
|
35590
|
-
|
|
35780
|
+
let uncommittedFileCount = 0;
|
|
35781
|
+
await forEachWithGitYield(paths, async (p) => {
|
|
35591
35782
|
const s = await getRepoWorkingTreeStatus(p);
|
|
35783
|
+
uncommittedFileCount += s.uncommittedFileCount;
|
|
35592
35784
|
if (s.hasUncommittedChanges) hasUncommittedChanges = true;
|
|
35593
35785
|
if (s.hasUnpushedCommits) hasUnpushedCommits = true;
|
|
35594
|
-
}
|
|
35595
|
-
return { hasUncommittedChanges, hasUnpushedCommits };
|
|
35786
|
+
});
|
|
35787
|
+
return { hasUncommittedChanges, hasUnpushedCommits, uncommittedFileCount };
|
|
35596
35788
|
}
|
|
35597
35789
|
async function pushAheadOfUpstreamForPaths(paths) {
|
|
35598
|
-
|
|
35790
|
+
await forEachWithGitYield(paths, async (p) => {
|
|
35599
35791
|
const g = cliSimpleGit(p);
|
|
35600
35792
|
const ahead = await commitsAheadOfRemoteTracking(p);
|
|
35601
|
-
if (ahead <= 0)
|
|
35793
|
+
if (ahead <= 0) return;
|
|
35602
35794
|
await g.push();
|
|
35603
|
-
}
|
|
35795
|
+
});
|
|
35604
35796
|
}
|
|
35605
35797
|
|
|
35606
|
-
// src/git/
|
|
35798
|
+
// src/git/changes/types.ts
|
|
35607
35799
|
var MAX_PATCH_CHARS = 35e4;
|
|
35608
35800
|
|
|
35609
|
-
// src/git/
|
|
35801
|
+
// src/git/changes/lib/repo-format.ts
|
|
35610
35802
|
function posixJoinDirFile(dir, file2) {
|
|
35611
35803
|
const d = dir === "." || dir === "" ? "" : dir.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
35612
35804
|
const f = file2.replace(/\\/g, "/").replace(/^\/+/, "");
|
|
@@ -35660,63 +35852,80 @@ function formatRemoteDisplayLabel(remoteUrl) {
|
|
|
35660
35852
|
return `origin \xB7 ${hostPath}`;
|
|
35661
35853
|
}
|
|
35662
35854
|
|
|
35663
|
-
// src/git/
|
|
35855
|
+
// src/git/changes/get-working-tree-change-repo-details.ts
|
|
35664
35856
|
import * as path24 from "node:path";
|
|
35665
35857
|
|
|
35666
|
-
// src/git/
|
|
35667
|
-
function
|
|
35668
|
-
const
|
|
35669
|
-
for (const line of
|
|
35670
|
-
|
|
35671
|
-
|
|
35672
|
-
|
|
35673
|
-
|
|
35674
|
-
const
|
|
35675
|
-
|
|
35676
|
-
|
|
35677
|
-
|
|
35678
|
-
|
|
35679
|
-
} else if (code === "R" || code === "C") {
|
|
35680
|
-
if (tabParts.length >= 3) m.set(tabParts[tabParts.length - 1], "modified");
|
|
35681
|
-
} else if (code === "M" || code === "U" || code === "T") {
|
|
35682
|
-
m.set(tabParts[tabParts.length - 1], "modified");
|
|
35683
|
-
}
|
|
35858
|
+
// src/git/commits/lib/parse-log-lines.ts
|
|
35859
|
+
function parseLogShaDateSubjectLines(raw) {
|
|
35860
|
+
const out = [];
|
|
35861
|
+
for (const line of String(raw).split("\n")) {
|
|
35862
|
+
const l = line.trimEnd();
|
|
35863
|
+
if (!l.trim()) continue;
|
|
35864
|
+
const parts = l.split(" ");
|
|
35865
|
+
if (parts.length < 3) continue;
|
|
35866
|
+
const sha = parts[0].trim();
|
|
35867
|
+
const committedAt = parts[1].trim();
|
|
35868
|
+
const subject = parts.slice(2).join(" ").trim();
|
|
35869
|
+
if (!/^[0-9a-f]{7,40}$/i.test(sha)) continue;
|
|
35870
|
+
out.push({ sha, shortSha: sha.slice(0, 7), subject, committedAt });
|
|
35684
35871
|
}
|
|
35685
|
-
return
|
|
35686
|
-
}
|
|
35687
|
-
function parseNumstatFirstLine(line) {
|
|
35688
|
-
const parts = line.split(" ");
|
|
35689
|
-
if (parts.length < 3) return null;
|
|
35690
|
-
const [a, d] = parts;
|
|
35691
|
-
const additions = a === "-" ? 0 : parseInt(String(a), 10) || 0;
|
|
35692
|
-
const deletions = d === "-" ? 0 : parseInt(String(d), 10) || 0;
|
|
35693
|
-
return { additions, deletions };
|
|
35872
|
+
return out;
|
|
35694
35873
|
}
|
|
35695
|
-
|
|
35696
|
-
|
|
35697
|
-
|
|
35698
|
-
|
|
35699
|
-
|
|
35700
|
-
|
|
35701
|
-
|
|
35702
|
-
|
|
35703
|
-
|
|
35704
|
-
m.set(p, { additions, deletions });
|
|
35874
|
+
|
|
35875
|
+
// src/git/commits/list-unpushed-commits.ts
|
|
35876
|
+
async function gitLogNotReachableFromBase(g, baseSha, headSha) {
|
|
35877
|
+
if (baseSha === headSha) return [];
|
|
35878
|
+
try {
|
|
35879
|
+
const logOut = await g.raw(["log", "--format=%H %cI %s", `${baseSha}..${headSha}`]);
|
|
35880
|
+
return parseLogShaDateSubjectLines(logOut);
|
|
35881
|
+
} catch {
|
|
35882
|
+
return [];
|
|
35705
35883
|
}
|
|
35706
|
-
return m;
|
|
35707
35884
|
}
|
|
35708
|
-
async function
|
|
35709
|
-
const
|
|
35885
|
+
async function listUnpushedCommits(repoDir) {
|
|
35886
|
+
const g = cliSimpleGit(repoDir);
|
|
35887
|
+
const headSha = await revParseSafe(g, "HEAD");
|
|
35888
|
+
if (!headSha) return [];
|
|
35889
|
+
const baseSha = await resolveBaseShaForUnpushedCommits(g);
|
|
35890
|
+
if (!baseSha) return [];
|
|
35891
|
+
return gitLogNotReachableFromBase(g, baseSha, headSha);
|
|
35892
|
+
}
|
|
35893
|
+
|
|
35894
|
+
// src/git/commits/lib/sanitize-recent-commits-limit.ts
|
|
35895
|
+
var RECENT_COMMITS_LIMIT_MIN = 1;
|
|
35896
|
+
var RECENT_COMMITS_LIMIT_MAX = 50;
|
|
35897
|
+
var RECENT_COMMITS_LIMIT_DEFAULT = 2;
|
|
35898
|
+
function sanitizeRecentCommitsLimit(value) {
|
|
35899
|
+
const n = typeof value === "number" ? value : typeof value === "string" ? Number.parseInt(value.trim(), 10) : Number.NaN;
|
|
35900
|
+
if (!Number.isFinite(n)) return RECENT_COMMITS_LIMIT_DEFAULT;
|
|
35901
|
+
return Math.min(RECENT_COMMITS_LIMIT_MAX, Math.max(RECENT_COMMITS_LIMIT_MIN, Math.trunc(n)));
|
|
35902
|
+
}
|
|
35903
|
+
|
|
35904
|
+
// src/git/commits/list-recent-commits.ts
|
|
35905
|
+
async function listRecentCommits(repoDir, limitInput) {
|
|
35906
|
+
const limit = sanitizeRecentCommitsLimit(limitInput);
|
|
35907
|
+
const g = cliSimpleGit(repoDir);
|
|
35908
|
+
const headSha = await revParseSafe(g, "HEAD");
|
|
35909
|
+
if (!headSha) return { commits: [], hasMore: false };
|
|
35910
|
+
const unpushedSet = new Set((await listUnpushedCommits(repoDir)).map((c) => c.sha));
|
|
35710
35911
|
try {
|
|
35711
|
-
const
|
|
35712
|
-
const
|
|
35713
|
-
|
|
35912
|
+
const logOut = await g.raw(["log", "--format=%H %cI %s", `-n`, String(limit), "HEAD"]);
|
|
35913
|
+
const commits = parseLogShaDateSubjectLines(logOut).map((c) => ({
|
|
35914
|
+
...c,
|
|
35915
|
+
needsPush: unpushedSet.has(c.sha)
|
|
35916
|
+
}));
|
|
35917
|
+
let hasMore = false;
|
|
35918
|
+
if (commits.length === limit) {
|
|
35919
|
+
const nextOut = await g.raw(["log", "-n", "1", `--skip=${limit}`, "--format=%H", "HEAD"]);
|
|
35920
|
+
hasMore = /^[0-9a-f]{7,40}$/i.test(String(nextOut).trim());
|
|
35921
|
+
}
|
|
35922
|
+
return { commits, hasMore };
|
|
35714
35923
|
} catch {
|
|
35715
|
-
return
|
|
35924
|
+
return { commits: [], hasMore: false };
|
|
35716
35925
|
}
|
|
35717
35926
|
}
|
|
35718
35927
|
|
|
35719
|
-
// src/git/
|
|
35928
|
+
// src/git/changes/lib/patch-truncate.ts
|
|
35720
35929
|
function truncatePatch(s) {
|
|
35721
35930
|
if (s.length <= MAX_PATCH_CHARS) return s;
|
|
35722
35931
|
return `${s.slice(0, MAX_PATCH_CHARS)}
|
|
@@ -35724,7 +35933,7 @@ function truncatePatch(s) {
|
|
|
35724
35933
|
\u2026 (diff truncated)`;
|
|
35725
35934
|
}
|
|
35726
35935
|
|
|
35727
|
-
// src/git/
|
|
35936
|
+
// src/git/commits/list-changed-files-for-commit.ts
|
|
35728
35937
|
var EMPTY_TREE = "4b825dc642cb6eb9a060e54bf8d69288fbee4904";
|
|
35729
35938
|
async function parentForCommitDiff(g, sha) {
|
|
35730
35939
|
try {
|
|
@@ -35750,7 +35959,7 @@ async function listChangedFilesForCommit(repoGitCwd, repoRelPath, commitSha) {
|
|
|
35750
35959
|
const paths = new Set([...kindByPath.keys(), ...numByPath.keys()].filter(Boolean));
|
|
35751
35960
|
const rows = [];
|
|
35752
35961
|
const normRel = repoRelPath === "." || repoRelPath === "" ? "." : repoRelPath;
|
|
35753
|
-
|
|
35962
|
+
await forEachWithGitYield([...paths], async (pathInRepo) => {
|
|
35754
35963
|
const relLauncher = posixJoinDirFile(normRel, pathInRepo.replace(/\\/g, "/"));
|
|
35755
35964
|
const nums = numByPath.get(pathInRepo);
|
|
35756
35965
|
let additions = nums?.additions ?? 0;
|
|
@@ -35762,8 +35971,8 @@ async function listChangedFilesForCommit(repoGitCwd, repoRelPath, commitSha) {
|
|
|
35762
35971
|
else change = "modified";
|
|
35763
35972
|
}
|
|
35764
35973
|
rows.push({ pathRelLauncher: relLauncher, additions, deletions, change });
|
|
35765
|
-
}
|
|
35766
|
-
|
|
35974
|
+
});
|
|
35975
|
+
await forEachWithGitYield(rows, async (row) => {
|
|
35767
35976
|
let pathInRepo;
|
|
35768
35977
|
if (normRel === ".") {
|
|
35769
35978
|
pathInRepo = row.pathRelLauncher;
|
|
@@ -35775,16 +35984,16 @@ async function listChangedFilesForCommit(repoGitCwd, repoRelPath, commitSha) {
|
|
|
35775
35984
|
const raw = await g.raw(["diff", "-U20000", range, "--", pathInRepo]).catch(() => "");
|
|
35776
35985
|
const t = String(raw).trim();
|
|
35777
35986
|
row.patchContent = t ? truncatePatch(t) : void 0;
|
|
35778
|
-
}
|
|
35987
|
+
});
|
|
35779
35988
|
rows.sort((a, b) => a.pathRelLauncher.localeCompare(b.pathRelLauncher));
|
|
35780
35989
|
return rows;
|
|
35781
35990
|
}
|
|
35782
35991
|
|
|
35783
|
-
// src/git/
|
|
35992
|
+
// src/git/changes/list-changed-files-for-repo.ts
|
|
35784
35993
|
import * as fs23 from "node:fs";
|
|
35785
35994
|
import * as path23 from "node:path";
|
|
35786
35995
|
|
|
35787
|
-
// src/git/
|
|
35996
|
+
// src/git/changes/lib/count-lines.ts
|
|
35788
35997
|
import { createReadStream } from "node:fs";
|
|
35789
35998
|
import * as readline2 from "node:readline";
|
|
35790
35999
|
async function countTextFileLines(filePath) {
|
|
@@ -35805,7 +36014,7 @@ async function countTextFileLines(filePath) {
|
|
|
35805
36014
|
return lines;
|
|
35806
36015
|
}
|
|
35807
36016
|
|
|
35808
|
-
// src/git/
|
|
36017
|
+
// src/git/changes/hydrate-patch.ts
|
|
35809
36018
|
import * as fs22 from "node:fs";
|
|
35810
36019
|
var UNIFIED_HUNK_HEADER_RE = /^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/;
|
|
35811
36020
|
var MAX_HYDRATE_LINES_PER_GAP = 8e3;
|
|
@@ -35921,26 +36130,16 @@ async function hydrateUnifiedPatchWithFileContext(patch, filePath, repoGitCwd, p
|
|
|
35921
36130
|
return truncatePatch(out.join("\n"));
|
|
35922
36131
|
}
|
|
35923
36132
|
|
|
35924
|
-
// src/git/
|
|
36133
|
+
// src/git/changes/unified-diff-for-file.ts
|
|
35925
36134
|
async function unifiedDiffForFile(repoCwd, pathInRepo, change) {
|
|
35926
36135
|
const g = cliSimpleGit(repoCwd);
|
|
35927
|
-
|
|
35928
|
-
|
|
35929
|
-
|
|
35930
|
-
|
|
35931
|
-
raw = await g.raw(["diff", "--no-index", "--", devNull, pathInRepo]);
|
|
35932
|
-
} else {
|
|
35933
|
-
raw = await g.raw(["diff", "HEAD", "--", pathInRepo]);
|
|
35934
|
-
}
|
|
35935
|
-
const t = String(raw).trim();
|
|
35936
|
-
if (!t) return void 0;
|
|
35937
|
-
return truncatePatch(t);
|
|
35938
|
-
} catch {
|
|
35939
|
-
return void 0;
|
|
35940
|
-
}
|
|
36136
|
+
const args = change === "added" ? ["diff", "--no-color", "HEAD", "--", pathInRepo] : change === "removed" ? ["diff", "--no-color", "HEAD", "--", pathInRepo] : ["diff", "--no-color", "HEAD", "--", pathInRepo];
|
|
36137
|
+
const raw = await g.raw([...args]).catch(() => "");
|
|
36138
|
+
const t = String(raw).trim();
|
|
36139
|
+
return t ? truncatePatch(t) : void 0;
|
|
35941
36140
|
}
|
|
35942
36141
|
|
|
35943
|
-
// src/git/
|
|
36142
|
+
// src/git/changes/list-changed-files-for-repo.ts
|
|
35944
36143
|
async function listChangedFilesForRepo(repoGitCwd, repoRelPath) {
|
|
35945
36144
|
const g = cliSimpleGit(repoGitCwd);
|
|
35946
36145
|
const [nameStatusRaw, numstatRaw, untrackedRaw] = await Promise.all([
|
|
@@ -35954,7 +36153,7 @@ async function listChangedFilesForRepo(repoGitCwd, repoRelPath) {
|
|
|
35954
36153
|
const untracked = String(untrackedRaw).split("\n").map((s) => s.trim()).filter(Boolean);
|
|
35955
36154
|
for (const p of untracked) paths.add(p);
|
|
35956
36155
|
const rows = [];
|
|
35957
|
-
|
|
36156
|
+
await forEachWithGitYield([...paths], async (pathInRepo) => {
|
|
35958
36157
|
const relLauncher = posixJoinDirFile(repoRelPath, pathInRepo.replace(/\\/g, "/"));
|
|
35959
36158
|
const repoFilePath = path23.join(repoGitCwd, pathInRepo);
|
|
35960
36159
|
const nums = numByPath.get(pathInRepo);
|
|
@@ -35984,9 +36183,9 @@ async function listChangedFilesForRepo(repoGitCwd, repoRelPath) {
|
|
|
35984
36183
|
else change = "modified";
|
|
35985
36184
|
}
|
|
35986
36185
|
rows.push({ pathRelLauncher: relLauncher, additions, deletions, change });
|
|
35987
|
-
}
|
|
36186
|
+
});
|
|
35988
36187
|
const normRel = repoRelPath === "." || repoRelPath === "" ? "." : repoRelPath;
|
|
35989
|
-
|
|
36188
|
+
await forEachWithGitYield(rows, async (row) => {
|
|
35990
36189
|
let pathInRepo;
|
|
35991
36190
|
if (normRel === ".") {
|
|
35992
36191
|
pathInRepo = row.pathRelLauncher;
|
|
@@ -36001,11 +36200,11 @@ async function listChangedFilesForRepo(repoGitCwd, repoRelPath) {
|
|
|
36001
36200
|
patch = await hydrateUnifiedPatchWithFileContext(patch, filePath, repoGitCwd, pathInRepo, row.change);
|
|
36002
36201
|
}
|
|
36003
36202
|
row.patchContent = patch;
|
|
36004
|
-
}
|
|
36203
|
+
});
|
|
36005
36204
|
return rows;
|
|
36006
36205
|
}
|
|
36007
36206
|
|
|
36008
|
-
// src/git/
|
|
36207
|
+
// src/git/changes/get-working-tree-change-repo-details.ts
|
|
36009
36208
|
function normRepoRel(p) {
|
|
36010
36209
|
const x = p.replace(/\\/g, "/").trim();
|
|
36011
36210
|
return x === "" ? "." : x;
|
|
@@ -36024,7 +36223,9 @@ async function getWorkingTreeChangeRepoDetails(options) {
|
|
|
36024
36223
|
throw new Error("commit sha is required for commit changes");
|
|
36025
36224
|
}
|
|
36026
36225
|
const basis = filter == null && basisInput.kind === "commit" ? { kind: "working" } : basisInput;
|
|
36027
|
-
for (
|
|
36226
|
+
for (let i = 0; i < options.commitTargetPaths.length; i++) {
|
|
36227
|
+
if (i > 0) await yieldToEventLoop2();
|
|
36228
|
+
const target = options.commitTargetPaths[i];
|
|
36028
36229
|
const t = path24.resolve(target);
|
|
36029
36230
|
if (!await isGitRepoDirectory(t)) continue;
|
|
36030
36231
|
const g = cliSimpleGit(t);
|
|
@@ -36058,7 +36259,10 @@ async function getWorkingTreeChangeRepoDetails(options) {
|
|
|
36058
36259
|
const files = basis.kind === "commit" ? await listChangedFilesForCommit(t, relForList, basis.sha.trim()) : await listChangedFilesForRepo(t, relForList);
|
|
36059
36260
|
const st = await g.status();
|
|
36060
36261
|
const hasUncommittedChanges = (st.files?.length ?? 0) > 0;
|
|
36061
|
-
const unpushedCommits = await
|
|
36262
|
+
const [unpushedCommits, recentCommitList] = await Promise.all([
|
|
36263
|
+
listUnpushedCommits(t),
|
|
36264
|
+
listRecentCommits(t, options.recentCommitsLimit)
|
|
36265
|
+
]);
|
|
36062
36266
|
out.push({
|
|
36063
36267
|
repoRelPath: norm,
|
|
36064
36268
|
repoDisplayName,
|
|
@@ -36068,6 +36272,8 @@ async function getWorkingTreeChangeRepoDetails(options) {
|
|
|
36068
36272
|
files,
|
|
36069
36273
|
hasUncommittedChanges,
|
|
36070
36274
|
unpushedCommits,
|
|
36275
|
+
recentCommits: recentCommitList.commits,
|
|
36276
|
+
recentCommitsHasMore: recentCommitList.hasMore,
|
|
36071
36277
|
changesView: basis.kind === "commit" ? "commit" : "working",
|
|
36072
36278
|
changesCommitSha: basis.kind === "commit" ? basis.sha.trim() : null
|
|
36073
36279
|
});
|
|
@@ -36076,7 +36282,7 @@ async function getWorkingTreeChangeRepoDetails(options) {
|
|
|
36076
36282
|
return out;
|
|
36077
36283
|
}
|
|
36078
36284
|
|
|
36079
|
-
// src/git/commit-and-push.ts
|
|
36285
|
+
// src/git/branches/commit-and-push.ts
|
|
36080
36286
|
async function gitCommitAllIfDirty(repoDir, message, options) {
|
|
36081
36287
|
const g = cliSimpleGit(repoDir);
|
|
36082
36288
|
const st = await g.status();
|
|
@@ -36500,7 +36706,8 @@ var SessionWorktreeManager = class {
|
|
|
36500
36706
|
sessionWorktreeRootPath: sessionWorkingTreeRelRoot,
|
|
36501
36707
|
legacyRepoNestedSessionLayout: legacyNested,
|
|
36502
36708
|
repoFilterRelPath: opts?.repoRelPath?.trim() ? opts.repoRelPath.trim() : null,
|
|
36503
|
-
basis: opts?.basis
|
|
36709
|
+
basis: opts?.basis,
|
|
36710
|
+
recentCommitsLimit: opts?.recentCommitsLimit
|
|
36504
36711
|
});
|
|
36505
36712
|
}
|
|
36506
36713
|
async pushSessionUpstream(sessionId) {
|
|
@@ -36537,30 +36744,39 @@ import path28 from "node:path";
|
|
|
36537
36744
|
function shouldSkipWorkspaceWalkEntry(name) {
|
|
36538
36745
|
return name.startsWith(".");
|
|
36539
36746
|
}
|
|
36540
|
-
function
|
|
36747
|
+
async function walkWorkspaceTreeAsync(dir, baseDir, onFile, state) {
|
|
36541
36748
|
let names;
|
|
36542
36749
|
try {
|
|
36543
|
-
names = fs25.
|
|
36750
|
+
names = await fs25.promises.readdir(dir);
|
|
36544
36751
|
} catch {
|
|
36545
36752
|
return;
|
|
36546
36753
|
}
|
|
36547
36754
|
for (const name of names) {
|
|
36548
36755
|
if (shouldSkipWorkspaceWalkEntry(name)) continue;
|
|
36756
|
+
if (state.n > 0 && state.n % INDEX_WORK_YIELD_EVERY === 0) {
|
|
36757
|
+
if (isCliImmediateShutdownRequested()) throw new CliSqliteInterrupted();
|
|
36758
|
+
await yieldToEventLoop();
|
|
36759
|
+
if (isCliImmediateShutdownRequested()) throw new CliSqliteInterrupted();
|
|
36760
|
+
}
|
|
36761
|
+
state.n++;
|
|
36549
36762
|
const full = path28.join(dir, name);
|
|
36550
36763
|
let stat3;
|
|
36551
36764
|
try {
|
|
36552
|
-
stat3 = fs25.
|
|
36765
|
+
stat3 = await fs25.promises.stat(full);
|
|
36553
36766
|
} catch {
|
|
36554
36767
|
continue;
|
|
36555
36768
|
}
|
|
36556
36769
|
const relative5 = path28.relative(baseDir, full).replace(/\\/g, "/");
|
|
36557
36770
|
if (stat3.isDirectory()) {
|
|
36558
|
-
|
|
36771
|
+
await walkWorkspaceTreeAsync(full, baseDir, onFile, state);
|
|
36559
36772
|
} else if (stat3.isFile()) {
|
|
36560
36773
|
onFile(relative5);
|
|
36561
36774
|
}
|
|
36562
36775
|
}
|
|
36563
36776
|
}
|
|
36777
|
+
function createWalkYieldState() {
|
|
36778
|
+
return { n: 0 };
|
|
36779
|
+
}
|
|
36564
36780
|
|
|
36565
36781
|
// src/files/index/file-index-sqlite-lock.ts
|
|
36566
36782
|
import fs26 from "node:fs";
|
|
@@ -36593,29 +36809,28 @@ function withFileIndexSqliteLock(fn) {
|
|
|
36593
36809
|
}
|
|
36594
36810
|
|
|
36595
36811
|
// src/files/index/build-file-index.ts
|
|
36596
|
-
var
|
|
36597
|
-
function
|
|
36812
|
+
var FILE_INDEX_INTERRUPT_CHECK_EVERY = 256;
|
|
36813
|
+
function assertNotShutdown() {
|
|
36814
|
+
if (isCliImmediateShutdownRequested()) throw new CliSqliteInterrupted();
|
|
36815
|
+
}
|
|
36816
|
+
function persistFileIndexPaths(resolved, paths) {
|
|
36598
36817
|
return withCliSqliteSync((db) => {
|
|
36599
36818
|
const h = getCwdHashForFileIndex(resolved);
|
|
36600
|
-
const buf = [];
|
|
36601
36819
|
let pathCount = 0;
|
|
36602
36820
|
db.run("BEGIN IMMEDIATE");
|
|
36603
36821
|
try {
|
|
36604
36822
|
db.run("DELETE FROM file_index_path WHERE cwd_hash = ?", [h]);
|
|
36605
36823
|
const ins = db.prepare("INSERT INTO file_index_path (cwd_hash, path) VALUES (?, ?)");
|
|
36606
36824
|
try {
|
|
36607
|
-
|
|
36608
|
-
|
|
36609
|
-
|
|
36825
|
+
let batch = 0;
|
|
36826
|
+
for (const rel of paths) {
|
|
36827
|
+
if (++batch >= FILE_INDEX_INTERRUPT_CHECK_EVERY) {
|
|
36828
|
+
batch = 0;
|
|
36829
|
+
assertNotShutdown();
|
|
36610
36830
|
}
|
|
36611
|
-
|
|
36612
|
-
|
|
36613
|
-
}
|
|
36614
|
-
walkWorkspaceTreeSync(resolved, resolved, (rel) => {
|
|
36615
|
-
buf.push(rel);
|
|
36616
|
-
if (buf.length >= FILE_INDEX_INSERT_BUFFER) flushBuf();
|
|
36617
|
-
});
|
|
36618
|
-
flushBuf();
|
|
36831
|
+
ins.run([h, rel]);
|
|
36832
|
+
pathCount += 1;
|
|
36833
|
+
}
|
|
36619
36834
|
} finally {
|
|
36620
36835
|
ins.finalize();
|
|
36621
36836
|
}
|
|
@@ -36630,12 +36845,24 @@ function persistFileIndexForResolvedCwd(resolved) {
|
|
|
36630
36845
|
return pathCount;
|
|
36631
36846
|
});
|
|
36632
36847
|
}
|
|
36848
|
+
async function collectWorkspacePathsAsync(resolved) {
|
|
36849
|
+
const paths = [];
|
|
36850
|
+
const state = createWalkYieldState();
|
|
36851
|
+
await walkWorkspaceTreeAsync(resolved, resolved, (rel) => {
|
|
36852
|
+
assertNotShutdown();
|
|
36853
|
+
paths.push(rel);
|
|
36854
|
+
}, state);
|
|
36855
|
+
return paths;
|
|
36856
|
+
}
|
|
36633
36857
|
async function buildFileIndexAsync(cwd) {
|
|
36634
36858
|
return withFileIndexSqliteLock(async () => {
|
|
36635
36859
|
const resolved = path29.resolve(cwd);
|
|
36636
36860
|
await yieldToEventLoop();
|
|
36637
|
-
|
|
36861
|
+
assertNotShutdown();
|
|
36862
|
+
const paths = await collectWorkspacePathsAsync(resolved);
|
|
36638
36863
|
await yieldToEventLoop();
|
|
36864
|
+
assertNotShutdown();
|
|
36865
|
+
const pathCount = persistFileIndexPaths(resolved, paths);
|
|
36639
36866
|
return { pathCount };
|
|
36640
36867
|
});
|
|
36641
36868
|
}
|
|
@@ -36739,11 +36966,13 @@ function createFsWatcher(resolved, schedule) {
|
|
|
36739
36966
|
function startFileIndexWatcher(cwd = getBridgeRoot()) {
|
|
36740
36967
|
const resolved = path31.resolve(cwd);
|
|
36741
36968
|
void buildFileIndexAsync(resolved).catch((e) => {
|
|
36969
|
+
if (e instanceof CliSqliteInterrupted) return;
|
|
36742
36970
|
console.error("[file-index] Initial index build failed:", e);
|
|
36743
36971
|
});
|
|
36744
36972
|
let timer = null;
|
|
36745
36973
|
const runRebuild = () => {
|
|
36746
36974
|
void buildFileIndexAsync(resolved).catch((e) => {
|
|
36975
|
+
if (e instanceof CliSqliteInterrupted) return;
|
|
36747
36976
|
console.error("[file-index] Watch rebuild failed:", e);
|
|
36748
36977
|
});
|
|
36749
36978
|
};
|
|
@@ -37008,7 +37237,7 @@ function pipedStdoutStderrFor(attemptStdio) {
|
|
|
37008
37237
|
}
|
|
37009
37238
|
|
|
37010
37239
|
// src/dev-servers/manager/shell-spawn/try-spawn-piped-via-sh.ts
|
|
37011
|
-
import { spawn as
|
|
37240
|
+
import { spawn as spawn5 } from "node:child_process";
|
|
37012
37241
|
function trySpawnPipedViaSh(command, env, cwd, signal) {
|
|
37013
37242
|
const attempts = [
|
|
37014
37243
|
{ stdio: [devNullReadFd(), "pipe", "pipe"], endStdin: false },
|
|
@@ -37029,9 +37258,9 @@ function trySpawnPipedViaSh(command, env, cwd, signal) {
|
|
|
37029
37258
|
if (process.platform === "win32") {
|
|
37030
37259
|
opts.windowsHide = true;
|
|
37031
37260
|
const com = process.env.ComSpec || "cmd.exe";
|
|
37032
|
-
proc =
|
|
37261
|
+
proc = spawn5(com, ["/d", "/s", "/c", command], opts);
|
|
37033
37262
|
} else {
|
|
37034
|
-
proc =
|
|
37263
|
+
proc = spawn5("/bin/sh", ["-c", command], opts);
|
|
37035
37264
|
}
|
|
37036
37265
|
if (attempt.endStdin) {
|
|
37037
37266
|
proc.stdin?.end();
|
|
@@ -37051,7 +37280,7 @@ function trySpawnPipedViaSh(command, env, cwd, signal) {
|
|
|
37051
37280
|
}
|
|
37052
37281
|
|
|
37053
37282
|
// src/dev-servers/manager/shell-spawn/try-spawn-shell-true-piped.ts
|
|
37054
|
-
import { spawn as
|
|
37283
|
+
import { spawn as spawn6 } from "node:child_process";
|
|
37055
37284
|
function trySpawnShellTruePiped(command, env, cwd, devNullFd, signal) {
|
|
37056
37285
|
try {
|
|
37057
37286
|
const opts = {
|
|
@@ -37064,7 +37293,7 @@ function trySpawnShellTruePiped(command, env, cwd, devNullFd, signal) {
|
|
|
37064
37293
|
if (process.platform === "win32") {
|
|
37065
37294
|
opts.windowsHide = true;
|
|
37066
37295
|
}
|
|
37067
|
-
return
|
|
37296
|
+
return spawn6(command, opts);
|
|
37068
37297
|
} catch (e) {
|
|
37069
37298
|
if (isSpawnEbadf(e)) return null;
|
|
37070
37299
|
throw e;
|
|
@@ -37072,7 +37301,7 @@ function trySpawnShellTruePiped(command, env, cwd, devNullFd, signal) {
|
|
|
37072
37301
|
}
|
|
37073
37302
|
|
|
37074
37303
|
// src/dev-servers/manager/shell-spawn/try-spawn-merged-log-file.ts
|
|
37075
|
-
import { spawn as
|
|
37304
|
+
import { spawn as spawn7 } from "node:child_process";
|
|
37076
37305
|
import fs29 from "node:fs";
|
|
37077
37306
|
import { tmpdir } from "node:os";
|
|
37078
37307
|
import path32 from "node:path";
|
|
@@ -37090,7 +37319,7 @@ function trySpawnMergedLogFile(command, env, cwd, signal) {
|
|
|
37090
37319
|
try {
|
|
37091
37320
|
let proc;
|
|
37092
37321
|
if (process.platform === "win32") {
|
|
37093
|
-
proc =
|
|
37322
|
+
proc = spawn7(process.env.ComSpec || "cmd.exe", ["/d", "/s", "/c", command], {
|
|
37094
37323
|
env,
|
|
37095
37324
|
cwd,
|
|
37096
37325
|
stdio,
|
|
@@ -37098,7 +37327,7 @@ function trySpawnMergedLogFile(command, env, cwd, signal) {
|
|
|
37098
37327
|
...signal ? { signal } : {}
|
|
37099
37328
|
});
|
|
37100
37329
|
} else {
|
|
37101
|
-
proc =
|
|
37330
|
+
proc = spawn7("/bin/sh", ["-c", command], { env, cwd, stdio, ...signal ? { signal } : {} });
|
|
37102
37331
|
}
|
|
37103
37332
|
fs29.closeSync(logFd);
|
|
37104
37333
|
return {
|
|
@@ -37119,7 +37348,7 @@ function trySpawnMergedLogFile(command, env, cwd, signal) {
|
|
|
37119
37348
|
}
|
|
37120
37349
|
|
|
37121
37350
|
// src/dev-servers/manager/shell-spawn/try-spawn-shell-script-log-redirect.ts
|
|
37122
|
-
import { spawn as
|
|
37351
|
+
import { spawn as spawn8 } from "node:child_process";
|
|
37123
37352
|
import fs30 from "node:fs";
|
|
37124
37353
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
37125
37354
|
import path33 from "node:path";
|
|
@@ -37142,7 +37371,7 @@ cd ${shSingleQuote(cwd)}
|
|
|
37142
37371
|
/bin/sh ${shSingleQuote(innerPath)} >>${shSingleQuote(logPath)} 2>&1
|
|
37143
37372
|
`
|
|
37144
37373
|
);
|
|
37145
|
-
const proc =
|
|
37374
|
+
const proc = spawn8("/bin/sh", [runnerPath], {
|
|
37146
37375
|
env,
|
|
37147
37376
|
cwd: tmpRoot,
|
|
37148
37377
|
stdio: "ignore",
|
|
@@ -37174,7 +37403,7 @@ CD /D ${q(cwd)}\r
|
|
|
37174
37403
|
${command} >> ${q(logPath)} 2>&1\r
|
|
37175
37404
|
`
|
|
37176
37405
|
);
|
|
37177
|
-
const proc =
|
|
37406
|
+
const proc = spawn8(com, ["/d", "/s", "/c", q(runnerPath)], {
|
|
37178
37407
|
env,
|
|
37179
37408
|
cwd: tmpRoot,
|
|
37180
37409
|
stdio: "ignore",
|
|
@@ -37195,7 +37424,7 @@ ${command} >> ${q(logPath)} 2>&1\r
|
|
|
37195
37424
|
}
|
|
37196
37425
|
|
|
37197
37426
|
// src/dev-servers/manager/shell-spawn/try-spawn-inherit.ts
|
|
37198
|
-
import { spawn as
|
|
37427
|
+
import { spawn as spawn9 } from "node:child_process";
|
|
37199
37428
|
function trySpawnInheritStdio(command, env, cwd, signal) {
|
|
37200
37429
|
const opts = {
|
|
37201
37430
|
env,
|
|
@@ -37207,9 +37436,9 @@ function trySpawnInheritStdio(command, env, cwd, signal) {
|
|
|
37207
37436
|
if (process.platform === "win32") {
|
|
37208
37437
|
opts.windowsHide = true;
|
|
37209
37438
|
const com = process.env.ComSpec || "cmd.exe";
|
|
37210
|
-
proc =
|
|
37439
|
+
proc = spawn9(com, ["/d", "/s", "/c", command], opts);
|
|
37211
37440
|
} else {
|
|
37212
|
-
proc =
|
|
37441
|
+
proc = spawn9("/bin/sh", ["-c", command], opts);
|
|
37213
37442
|
}
|
|
37214
37443
|
return { proc, pipedStdoutStderr: false };
|
|
37215
37444
|
}
|
|
@@ -37277,9 +37506,6 @@ var StreamTail = class {
|
|
|
37277
37506
|
}
|
|
37278
37507
|
};
|
|
37279
37508
|
|
|
37280
|
-
// src/dev-servers/manager/dev-server-constants.ts
|
|
37281
|
-
var BRIDGE_SHUTDOWN_GRACE_MS = 8e3;
|
|
37282
|
-
|
|
37283
37509
|
// src/dev-servers/manager/dev-server-firehose-messages.ts
|
|
37284
37510
|
function buildFirehoseSnapshotMessage(params) {
|
|
37285
37511
|
const payload = {
|
|
@@ -37565,22 +37791,23 @@ var DevServerManager = class {
|
|
|
37565
37791
|
}
|
|
37566
37792
|
this.start(serverId);
|
|
37567
37793
|
}
|
|
37568
|
-
async shutdownAllGraceful() {
|
|
37794
|
+
async shutdownAllGraceful(opts) {
|
|
37795
|
+
const graceMs = opts?.graceMs ?? BRIDGE_SHUTDOWN_GRACE_MS;
|
|
37569
37796
|
const pairs = [...this.processes.entries()];
|
|
37570
37797
|
if (pairs.length === 0) return;
|
|
37571
37798
|
this.log(
|
|
37572
37799
|
`[dev-server] Stopping ${pairs.length} local dev server process${pairs.length === 1 ? "" : "es"}\u2026`
|
|
37573
37800
|
);
|
|
37574
|
-
await Promise.all(pairs.map(([serverId, proc]) => this.gracefulTerminateOrUnknown(serverId, proc)));
|
|
37801
|
+
await Promise.all(pairs.map(([serverId, proc]) => this.gracefulTerminateOrUnknown(serverId, proc, graceMs)));
|
|
37575
37802
|
}
|
|
37576
|
-
async gracefulTerminateOrUnknown(serverId, proc) {
|
|
37803
|
+
async gracefulTerminateOrUnknown(serverId, proc, graceMs) {
|
|
37577
37804
|
const shortId = `${serverId.slice(0, 8)}\u2026`;
|
|
37578
|
-
await sigtermAndWaitForExit(proc,
|
|
37805
|
+
await sigtermAndWaitForExit(proc, graceMs, this.log, shortId);
|
|
37579
37806
|
if (!this.processes.has(serverId) || this.processes.get(serverId) !== proc) {
|
|
37580
37807
|
return;
|
|
37581
37808
|
}
|
|
37582
37809
|
this.bumpGeneration(serverId);
|
|
37583
|
-
forceKillChild(proc, this.log, shortId,
|
|
37810
|
+
forceKillChild(proc, this.log, shortId, graceMs);
|
|
37584
37811
|
this.processes.delete(serverId);
|
|
37585
37812
|
this.clearPoll(serverId);
|
|
37586
37813
|
this.pipedCaptureByServerId.delete(serverId);
|
|
@@ -38175,10 +38402,13 @@ var LOCAL_AGENT_ACP_MODULES = [
|
|
|
38175
38402
|
];
|
|
38176
38403
|
async function detectLocalAgentTypes() {
|
|
38177
38404
|
try {
|
|
38405
|
+
if (isCliImmediateShutdownRequested()) return [];
|
|
38178
38406
|
const out = [];
|
|
38179
38407
|
for (let i = 0; i < LOCAL_AGENT_ACP_MODULES.length; i++) {
|
|
38408
|
+
if (isCliImmediateShutdownRequested()) return out;
|
|
38180
38409
|
if (i > 0) {
|
|
38181
38410
|
await yieldToEventLoop();
|
|
38411
|
+
if (isCliImmediateShutdownRequested()) return out;
|
|
38182
38412
|
}
|
|
38183
38413
|
const mod = LOCAL_AGENT_ACP_MODULES[i];
|
|
38184
38414
|
try {
|
|
@@ -38211,6 +38441,7 @@ function createSendLocalSkillsReport(getWs, logFn) {
|
|
|
38211
38441
|
}
|
|
38212
38442
|
function createReportAutoDetectedAgents(getWs, logFn) {
|
|
38213
38443
|
return async () => {
|
|
38444
|
+
if (isCliImmediateShutdownRequested()) return;
|
|
38214
38445
|
try {
|
|
38215
38446
|
const types = await detectLocalAgentTypes();
|
|
38216
38447
|
const socket = getWs();
|
|
@@ -38310,6 +38541,7 @@ var handleBridgeIdentified = (msg, deps) => {
|
|
|
38310
38541
|
});
|
|
38311
38542
|
setImmediate(() => {
|
|
38312
38543
|
void (async () => {
|
|
38544
|
+
if (isCliImmediateShutdownRequested()) return;
|
|
38313
38545
|
try {
|
|
38314
38546
|
await deps.reportAutoDetectedAgents?.();
|
|
38315
38547
|
} catch (e) {
|
|
@@ -38317,6 +38549,7 @@ var handleBridgeIdentified = (msg, deps) => {
|
|
|
38317
38549
|
`[Bridge service] Auto-detect agents failed: ${e instanceof Error ? e.message : String(e)}`
|
|
38318
38550
|
);
|
|
38319
38551
|
}
|
|
38552
|
+
if (isCliImmediateShutdownRequested()) return;
|
|
38320
38553
|
try {
|
|
38321
38554
|
await deps.warmupAgentCapabilitiesOnConnect?.();
|
|
38322
38555
|
} catch (e) {
|
|
@@ -38327,6 +38560,7 @@ var handleBridgeIdentified = (msg, deps) => {
|
|
|
38327
38560
|
})();
|
|
38328
38561
|
});
|
|
38329
38562
|
setImmediate(() => {
|
|
38563
|
+
if (isCliImmediateShutdownRequested()) return;
|
|
38330
38564
|
try {
|
|
38331
38565
|
deps.sendLocalSkillsReport?.();
|
|
38332
38566
|
} catch (e) {
|
|
@@ -38628,12 +38862,12 @@ function createBridgePromptSenders(deps, getWs) {
|
|
|
38628
38862
|
}
|
|
38629
38863
|
|
|
38630
38864
|
// src/agents/acp/from-bridge/bridge-prompt-preamble.ts
|
|
38631
|
-
import { execFile as
|
|
38632
|
-
import { promisify as
|
|
38633
|
-
var
|
|
38865
|
+
import { execFile as execFile8 } from "node:child_process";
|
|
38866
|
+
import { promisify as promisify8 } from "node:util";
|
|
38867
|
+
var execFileAsync7 = promisify8(execFile8);
|
|
38634
38868
|
async function readGitBranch(cwd) {
|
|
38635
38869
|
try {
|
|
38636
|
-
const { stdout } = await
|
|
38870
|
+
const { stdout } = await execFileAsync7("git", ["branch", "--show-current"], { cwd, maxBuffer: 64 * 1024 });
|
|
38637
38871
|
const b = stdout.trim();
|
|
38638
38872
|
return b || null;
|
|
38639
38873
|
} catch {
|
|
@@ -38881,7 +39115,7 @@ var handleSessionRequestResponseMessage = (msg, deps) => {
|
|
|
38881
39115
|
};
|
|
38882
39116
|
|
|
38883
39117
|
// src/skills/preview.ts
|
|
38884
|
-
import { spawn as
|
|
39118
|
+
import { spawn as spawn10 } from "node:child_process";
|
|
38885
39119
|
var PREVIEW_API_BASE_PATH = "/__preview";
|
|
38886
39120
|
var PREVIEW_SECRET_HEADER = "X-Preview-Secret";
|
|
38887
39121
|
var DEFAULT_PORT = 3e3;
|
|
@@ -38960,7 +39194,7 @@ var previewSkill = {
|
|
|
38960
39194
|
const parts = command.split(/\s+/);
|
|
38961
39195
|
const exe = parts[0];
|
|
38962
39196
|
const args = parts.slice(1);
|
|
38963
|
-
previewProcess =
|
|
39197
|
+
previewProcess = spawn10(isWindows && exe === "npm" ? "npm.cmd" : exe, args, {
|
|
38964
39198
|
cwd: getBridgeRoot(),
|
|
38965
39199
|
stdio: ["ignore", "pipe", "pipe"],
|
|
38966
39200
|
env: {
|
|
@@ -39517,7 +39751,8 @@ var handleSessionGitRequestMessage = (msg, deps) => {
|
|
|
39517
39751
|
reply({
|
|
39518
39752
|
ok: true,
|
|
39519
39753
|
hasUncommittedChanges: r.hasUncommittedChanges,
|
|
39520
|
-
hasUnpushedCommits: r.hasUnpushedCommits
|
|
39754
|
+
hasUnpushedCommits: r.hasUnpushedCommits,
|
|
39755
|
+
uncommittedFileCount: r.uncommittedFileCount
|
|
39521
39756
|
});
|
|
39522
39757
|
return;
|
|
39523
39758
|
}
|
|
@@ -39531,7 +39766,12 @@ var handleSessionGitRequestMessage = (msg, deps) => {
|
|
|
39531
39766
|
return;
|
|
39532
39767
|
}
|
|
39533
39768
|
}
|
|
39534
|
-
const
|
|
39769
|
+
const recentCommitsLimit = typeof msg.changesRecentCommitsLimit === "number" || typeof msg.changesRecentCommitsLimit === "string" ? msg.changesRecentCommitsLimit : void 0;
|
|
39770
|
+
const opts = repoRel && view === "commit" && commitSha ? {
|
|
39771
|
+
repoRelPath: repoRel,
|
|
39772
|
+
basis: { kind: "commit", sha: commitSha },
|
|
39773
|
+
recentCommitsLimit
|
|
39774
|
+
} : repoRel ? { repoRelPath: repoRel, basis: { kind: "working" }, recentCommitsLimit } : recentCommitsLimit !== void 0 ? { recentCommitsLimit } : void 0;
|
|
39535
39775
|
const repos = await deps.sessionWorktreeManager.getSessionWorkingTreeChangeDetails(sessionId, opts);
|
|
39536
39776
|
reply({
|
|
39537
39777
|
ok: true,
|
|
@@ -39549,7 +39789,8 @@ var handleSessionGitRequestMessage = (msg, deps) => {
|
|
|
39549
39789
|
reply({
|
|
39550
39790
|
ok: true,
|
|
39551
39791
|
hasUncommittedChanges: st2.hasUncommittedChanges,
|
|
39552
|
-
hasUnpushedCommits: st2.hasUnpushedCommits
|
|
39792
|
+
hasUnpushedCommits: st2.hasUnpushedCommits,
|
|
39793
|
+
uncommittedFileCount: st2.uncommittedFileCount
|
|
39553
39794
|
});
|
|
39554
39795
|
return;
|
|
39555
39796
|
}
|
|
@@ -39574,7 +39815,8 @@ var handleSessionGitRequestMessage = (msg, deps) => {
|
|
|
39574
39815
|
reply({
|
|
39575
39816
|
ok: true,
|
|
39576
39817
|
hasUncommittedChanges: st.hasUncommittedChanges,
|
|
39577
|
-
hasUnpushedCommits: st.hasUnpushedCommits
|
|
39818
|
+
hasUnpushedCommits: st.hasUnpushedCommits,
|
|
39819
|
+
uncommittedFileCount: st.uncommittedFileCount
|
|
39578
39820
|
});
|
|
39579
39821
|
} catch (e) {
|
|
39580
39822
|
reply({ ok: false, error: e instanceof Error ? e.message : String(e) });
|
|
@@ -40099,6 +40341,7 @@ import * as path40 from "node:path";
|
|
|
40099
40341
|
import * as path39 from "node:path";
|
|
40100
40342
|
async function probeOneAgentTypeForCapabilities(params) {
|
|
40101
40343
|
const { agentType, cwd, workspaceId, log: log2, reportAgentCapabilities, bridgeReport = true } = params;
|
|
40344
|
+
if (isCliImmediateShutdownRequested()) return false;
|
|
40102
40345
|
const resolved = resolveAgentCommand(agentType);
|
|
40103
40346
|
if (!resolved) return false;
|
|
40104
40347
|
let sqliteChanged = false;
|
|
@@ -40132,6 +40375,7 @@ async function probeOneAgentTypeForCapabilities(params) {
|
|
|
40132
40375
|
}, 28e3);
|
|
40133
40376
|
killTimer.unref?.();
|
|
40134
40377
|
try {
|
|
40378
|
+
if (isCliImmediateShutdownRequested()) return false;
|
|
40135
40379
|
handle = await resolved.createClient({
|
|
40136
40380
|
command: resolved.command,
|
|
40137
40381
|
cwd: path39.resolve(cwd),
|
|
@@ -40151,7 +40395,7 @@ async function probeOneAgentTypeForCapabilities(params) {
|
|
|
40151
40395
|
onSessionUpdate: () => {
|
|
40152
40396
|
}
|
|
40153
40397
|
});
|
|
40154
|
-
|
|
40398
|
+
if (!await delayMsUnlessShutdownRequested(1200)) return false;
|
|
40155
40399
|
} catch (e) {
|
|
40156
40400
|
log2(
|
|
40157
40401
|
`[Bridge service] Agent capability probe (${agentType}): ${e instanceof Error ? e.message : String(e)}`
|
|
@@ -40179,6 +40423,7 @@ async function probeAgentCapabilitiesForDetectedTypes(params) {
|
|
|
40179
40423
|
} = params;
|
|
40180
40424
|
let changedCount = 0;
|
|
40181
40425
|
for (let i = 0; i < agentTypes.length; i++) {
|
|
40426
|
+
if (isCliImmediateShutdownRequested()) return changedCount;
|
|
40182
40427
|
if (i > 0) await yieldToEventLoop();
|
|
40183
40428
|
const agentType = agentTypes[i];
|
|
40184
40429
|
if (!agentType.trim()) continue;
|
|
@@ -40206,6 +40451,7 @@ async function probeAgentCapabilitiesForDetectedTypes(params) {
|
|
|
40206
40451
|
// src/agents/capabilities/warmup-agent-capabilities-on-connect.ts
|
|
40207
40452
|
async function warmupAgentCapabilitiesOnConnect(params) {
|
|
40208
40453
|
const { workspaceId, log: log2, getWs } = params;
|
|
40454
|
+
if (isCliImmediateShutdownRequested()) return;
|
|
40209
40455
|
const cwd = path40.resolve(getBridgeRoot());
|
|
40210
40456
|
async function sendBatchFromCache() {
|
|
40211
40457
|
const socket = getWs();
|
|
@@ -40218,18 +40464,21 @@ async function warmupAgentCapabilitiesOnConnect(params) {
|
|
|
40218
40464
|
items: rows.map((r) => ({ agentType: r.agentType, configOptions: r.configOptions }))
|
|
40219
40465
|
});
|
|
40220
40466
|
} catch (e) {
|
|
40467
|
+
if (e instanceof CliSqliteInterrupted) return;
|
|
40221
40468
|
log2(
|
|
40222
40469
|
`[Bridge service] Agent capability batch to bridge failed: ${e instanceof Error ? e.message : String(e)}`
|
|
40223
40470
|
);
|
|
40224
40471
|
}
|
|
40225
40472
|
}
|
|
40226
40473
|
await sendBatchFromCache();
|
|
40474
|
+
if (isCliImmediateShutdownRequested()) return;
|
|
40227
40475
|
let types = [];
|
|
40228
40476
|
try {
|
|
40229
40477
|
types = [...await detectLocalAgentTypes()];
|
|
40230
40478
|
} catch (e) {
|
|
40231
40479
|
log2(`[Bridge service] detectLocalAgentTypes failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
40232
40480
|
}
|
|
40481
|
+
if (isCliImmediateShutdownRequested()) return;
|
|
40233
40482
|
try {
|
|
40234
40483
|
const n = await probeAgentCapabilitiesForDetectedTypes({
|
|
40235
40484
|
agentTypes: types,
|
|
@@ -40241,11 +40490,13 @@ async function warmupAgentCapabilitiesOnConnect(params) {
|
|
|
40241
40490
|
});
|
|
40242
40491
|
if (n > 0) await sendBatchFromCache();
|
|
40243
40492
|
} catch (e) {
|
|
40493
|
+
if (e instanceof CliSqliteInterrupted) return;
|
|
40244
40494
|
log2(`[Bridge service] Agent capability probe (missing cache) failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
40245
40495
|
}
|
|
40246
40496
|
void (async () => {
|
|
40247
40497
|
try {
|
|
40248
40498
|
await yieldToEventLoop();
|
|
40499
|
+
if (isCliImmediateShutdownRequested()) return;
|
|
40249
40500
|
const n = await probeAgentCapabilitiesForDetectedTypes({
|
|
40250
40501
|
agentTypes: types,
|
|
40251
40502
|
cwd,
|
|
@@ -40256,6 +40507,7 @@ async function warmupAgentCapabilitiesOnConnect(params) {
|
|
|
40256
40507
|
});
|
|
40257
40508
|
if (n > 0) await sendBatchFromCache();
|
|
40258
40509
|
} catch (e) {
|
|
40510
|
+
if (e instanceof CliSqliteInterrupted) return;
|
|
40259
40511
|
log2(`[Bridge service] Agent capability lazy refresh failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
40260
40512
|
}
|
|
40261
40513
|
})();
|
|
@@ -40303,7 +40555,8 @@ async function createBridgeConnection(options) {
|
|
|
40303
40555
|
configOptions: info.configOptions
|
|
40304
40556
|
})
|
|
40305
40557
|
);
|
|
40306
|
-
} catch {
|
|
40558
|
+
} catch (e) {
|
|
40559
|
+
if (e instanceof CliSqliteInterrupted) return;
|
|
40307
40560
|
}
|
|
40308
40561
|
if (!changed) return;
|
|
40309
40562
|
const socket = getWs();
|
|
@@ -40386,6 +40639,7 @@ async function createBridgeConnection(options) {
|
|
|
40386
40639
|
const stopFileIndexWatcher = startFileIndexWatcher(getBridgeRoot());
|
|
40387
40640
|
return {
|
|
40388
40641
|
close: async () => {
|
|
40642
|
+
requestCliImmediateShutdown();
|
|
40389
40643
|
stopFileIndexWatcher();
|
|
40390
40644
|
bridgeHeartbeat.stop();
|
|
40391
40645
|
await closeBridgeConnection(state, acpManager, devServerManager, logFn);
|
|
@@ -40459,47 +40713,61 @@ async function runConnectedBridge(options, restartWithoutAuth) {
|
|
|
40459
40713
|
} = options;
|
|
40460
40714
|
const firehoseServerUrl = options.firehoseServerUrl ?? options.proxyServerUrl;
|
|
40461
40715
|
let cleanupKeyCommand;
|
|
40462
|
-
|
|
40463
|
-
apiUrl,
|
|
40464
|
-
workspaceId,
|
|
40465
|
-
authToken,
|
|
40466
|
-
refreshToken,
|
|
40467
|
-
firehoseServerUrl,
|
|
40468
|
-
justAuthenticated,
|
|
40469
|
-
worktreesRootPath,
|
|
40470
|
-
e2eCertificate,
|
|
40471
|
-
log,
|
|
40472
|
-
persistTokens: (t) => {
|
|
40473
|
-
writeConfigForApi(apiUrl, {
|
|
40474
|
-
workspaceId,
|
|
40475
|
-
token: t.token,
|
|
40476
|
-
refreshToken: t.refreshToken
|
|
40477
|
-
});
|
|
40478
|
-
},
|
|
40479
|
-
onAuthInvalid: () => {
|
|
40480
|
-
cleanupKeyCommand?.();
|
|
40481
|
-
log("[Bridge service] Access token invalid or revoked; re-authenticating\u2026");
|
|
40482
|
-
clearConfigForApi(apiUrl);
|
|
40483
|
-
void handle.close().then(() => {
|
|
40484
|
-
void restartWithoutAuth({ apiUrl, firehoseServerUrl, worktreesRootPath, e2eCertificate });
|
|
40485
|
-
});
|
|
40486
|
-
}
|
|
40487
|
-
});
|
|
40716
|
+
let bridgeClose = null;
|
|
40488
40717
|
const onSignal = (kind) => {
|
|
40718
|
+
requestCliImmediateShutdown();
|
|
40719
|
+
abortActiveGitChildProcesses();
|
|
40489
40720
|
cleanupKeyCommand?.();
|
|
40490
40721
|
logImmediate(
|
|
40491
40722
|
kind === "interrupt" ? "Keyboard interrupt (Ctrl+C) \u2014 stopping\u2026" : "Stop requested \u2014 shutting down\u2026"
|
|
40492
40723
|
);
|
|
40493
|
-
|
|
40494
|
-
void
|
|
40495
|
-
|
|
40496
|
-
|
|
40497
|
-
|
|
40724
|
+
if (bridgeClose) {
|
|
40725
|
+
void bridgeClose().then(() => process.exit(0));
|
|
40726
|
+
return;
|
|
40727
|
+
}
|
|
40728
|
+
process.exit(0);
|
|
40498
40729
|
};
|
|
40499
40730
|
const onSigInt = () => onSignal("interrupt");
|
|
40500
40731
|
const onSigTerm = () => onSignal("stop");
|
|
40501
40732
|
process.on("SIGINT", onSigInt);
|
|
40502
40733
|
process.on("SIGTERM", onSigTerm);
|
|
40734
|
+
let handle;
|
|
40735
|
+
try {
|
|
40736
|
+
handle = await createBridgeConnection({
|
|
40737
|
+
apiUrl,
|
|
40738
|
+
workspaceId,
|
|
40739
|
+
authToken,
|
|
40740
|
+
refreshToken,
|
|
40741
|
+
firehoseServerUrl,
|
|
40742
|
+
justAuthenticated,
|
|
40743
|
+
worktreesRootPath,
|
|
40744
|
+
e2eCertificate,
|
|
40745
|
+
log,
|
|
40746
|
+
persistTokens: (t) => {
|
|
40747
|
+
writeConfigForApi(apiUrl, {
|
|
40748
|
+
workspaceId,
|
|
40749
|
+
token: t.token,
|
|
40750
|
+
refreshToken: t.refreshToken
|
|
40751
|
+
});
|
|
40752
|
+
},
|
|
40753
|
+
onAuthInvalid: () => {
|
|
40754
|
+
cleanupKeyCommand?.();
|
|
40755
|
+
log("[Bridge service] Access token invalid or revoked; re-authenticating\u2026");
|
|
40756
|
+
clearConfigForApi(apiUrl);
|
|
40757
|
+
void handle.close().then(() => {
|
|
40758
|
+
void restartWithoutAuth({ apiUrl, firehoseServerUrl, worktreesRootPath, e2eCertificate });
|
|
40759
|
+
});
|
|
40760
|
+
}
|
|
40761
|
+
});
|
|
40762
|
+
} catch (e) {
|
|
40763
|
+
process.off("SIGINT", onSigInt);
|
|
40764
|
+
process.off("SIGTERM", onSigTerm);
|
|
40765
|
+
if (e instanceof CliSqliteInterrupted) {
|
|
40766
|
+
process.exit(0);
|
|
40767
|
+
}
|
|
40768
|
+
throw e;
|
|
40769
|
+
}
|
|
40770
|
+
bridgeClose = () => handle.close();
|
|
40503
40771
|
if (e2eCertificate) {
|
|
40504
40772
|
let openingCertificate = false;
|
|
40505
40773
|
cleanupKeyCommand = installE2eCertificateKeyCommand({
|
|
@@ -40544,6 +40812,7 @@ async function runBridge(options) {
|
|
|
40544
40812
|
}
|
|
40545
40813
|
});
|
|
40546
40814
|
const onSignal = (kind) => {
|
|
40815
|
+
requestCliImmediateShutdown();
|
|
40547
40816
|
logImmediate(
|
|
40548
40817
|
kind === "interrupt" ? "Keyboard interrupt (Ctrl+C) \u2014 stopping\u2026" : "Stop requested \u2014 shutting down\u2026"
|
|
40549
40818
|
);
|