@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/index.js
CHANGED
|
@@ -23962,7 +23962,7 @@ function installBridgeProcessResilience() {
|
|
|
23962
23962
|
}
|
|
23963
23963
|
|
|
23964
23964
|
// src/cli-version.ts
|
|
23965
|
-
var CLI_VERSION = "0.1.
|
|
23965
|
+
var CLI_VERSION = "0.1.34".length > 0 ? "0.1.34" : "0.0.0-dev";
|
|
23966
23966
|
|
|
23967
23967
|
// src/connection/heartbeat/constants.ts
|
|
23968
23968
|
var BRIDGE_APP_HEARTBEAT_INTERVAL_MS = 1e4;
|
|
@@ -25022,6 +25022,30 @@ function runPendingAuth(options) {
|
|
|
25022
25022
|
};
|
|
25023
25023
|
}
|
|
25024
25024
|
|
|
25025
|
+
// src/dev-servers/manager/dev-server-constants.ts
|
|
25026
|
+
var BRIDGE_CLOSE_DEV_SERVER_GRACE_MS = 0;
|
|
25027
|
+
var BRIDGE_SHUTDOWN_GRACE_MS = 8e3;
|
|
25028
|
+
|
|
25029
|
+
// src/runtime/cli-process-interrupt.ts
|
|
25030
|
+
var cliImmediateShutdownRequested = false;
|
|
25031
|
+
function requestCliImmediateShutdown() {
|
|
25032
|
+
cliImmediateShutdownRequested = true;
|
|
25033
|
+
}
|
|
25034
|
+
function isCliImmediateShutdownRequested() {
|
|
25035
|
+
return cliImmediateShutdownRequested;
|
|
25036
|
+
}
|
|
25037
|
+
async function delayMsUnlessShutdownRequested(ms) {
|
|
25038
|
+
if (ms <= 0) return true;
|
|
25039
|
+
const end = Date.now() + ms;
|
|
25040
|
+
while (Date.now() < end) {
|
|
25041
|
+
if (isCliImmediateShutdownRequested()) return false;
|
|
25042
|
+
const chunk = Math.min(50, end - Date.now());
|
|
25043
|
+
if (chunk <= 0) break;
|
|
25044
|
+
await new Promise((r) => setTimeout(r, chunk));
|
|
25045
|
+
}
|
|
25046
|
+
return !isCliImmediateShutdownRequested();
|
|
25047
|
+
}
|
|
25048
|
+
|
|
25025
25049
|
// src/sqlite/cli-database.ts
|
|
25026
25050
|
import sqliteWasm from "node-sqlite3-wasm";
|
|
25027
25051
|
|
|
@@ -25325,6 +25349,12 @@ var { Database: SqliteDatabase } = sqliteWasm;
|
|
|
25325
25349
|
var CLI_SQLITE_SYNC_RETRY_MAX = 40;
|
|
25326
25350
|
var CLI_SQLITE_ASYNC_RETRY_MAX = 60;
|
|
25327
25351
|
var CLI_SQLITE_ASYNC_BASE_DELAY_MS = 20;
|
|
25352
|
+
var CliSqliteInterrupted = class extends Error {
|
|
25353
|
+
name = "CliSqliteInterrupted";
|
|
25354
|
+
constructor() {
|
|
25355
|
+
super("CLI SQLite interrupted (shutdown)");
|
|
25356
|
+
}
|
|
25357
|
+
};
|
|
25328
25358
|
function applyCliSqliteConcurrencyPragmas(db) {
|
|
25329
25359
|
try {
|
|
25330
25360
|
db.exec("PRAGMA journal_mode = WAL");
|
|
@@ -25335,7 +25365,7 @@ function applyCliSqliteConcurrencyPragmas(db) {
|
|
|
25335
25365
|
} catch {
|
|
25336
25366
|
}
|
|
25337
25367
|
try {
|
|
25338
|
-
db.run("PRAGMA busy_timeout =
|
|
25368
|
+
db.run("PRAGMA busy_timeout = 500");
|
|
25339
25369
|
} catch {
|
|
25340
25370
|
}
|
|
25341
25371
|
}
|
|
@@ -25356,26 +25386,42 @@ function safeCloseCliSqliteDatabase(db) {
|
|
|
25356
25386
|
function closeAllCliSqliteConnections() {
|
|
25357
25387
|
}
|
|
25358
25388
|
function isCliSqliteLockError(e) {
|
|
25389
|
+
if (e instanceof CliSqliteInterrupted) return false;
|
|
25359
25390
|
const msg = e instanceof Error ? e.message : String(e);
|
|
25360
25391
|
const lower = msg.toLowerCase();
|
|
25361
25392
|
return lower.includes("database is locked") || lower.includes("sqlite_busy") || lower.includes("sqlite3_busy") || lower.includes("database") && lower.includes("locked");
|
|
25362
25393
|
}
|
|
25363
25394
|
function syncSleepMs(ms) {
|
|
25364
25395
|
if (ms <= 0) return;
|
|
25365
|
-
|
|
25366
|
-
|
|
25367
|
-
|
|
25368
|
-
|
|
25369
|
-
|
|
25370
|
-
|
|
25371
|
-
|
|
25396
|
+
const deadline = Date.now() + ms;
|
|
25397
|
+
while (Date.now() < deadline) {
|
|
25398
|
+
if (isCliImmediateShutdownRequested()) throw new CliSqliteInterrupted();
|
|
25399
|
+
const chunk = Math.min(80, deadline - Date.now());
|
|
25400
|
+
if (chunk <= 0) break;
|
|
25401
|
+
try {
|
|
25402
|
+
const sab = new SharedArrayBuffer(4);
|
|
25403
|
+
const ia = new Int32Array(sab);
|
|
25404
|
+
Atomics.wait(ia, 0, 0, chunk);
|
|
25405
|
+
} catch {
|
|
25406
|
+
const end = Date.now() + chunk;
|
|
25407
|
+
while (Date.now() < end) {
|
|
25408
|
+
if (isCliImmediateShutdownRequested()) throw new CliSqliteInterrupted();
|
|
25409
|
+
}
|
|
25372
25410
|
}
|
|
25373
25411
|
}
|
|
25374
25412
|
}
|
|
25375
|
-
function
|
|
25376
|
-
|
|
25413
|
+
async function asyncDelayMsInterruptible(ms) {
|
|
25414
|
+
const end = Date.now() + ms;
|
|
25415
|
+
while (Date.now() < end) {
|
|
25416
|
+
if (isCliImmediateShutdownRequested()) throw new CliSqliteInterrupted();
|
|
25417
|
+
const chunk = Math.min(50, end - Date.now());
|
|
25418
|
+
if (chunk <= 0) break;
|
|
25419
|
+
await new Promise((r) => setTimeout(r, chunk));
|
|
25420
|
+
await yieldToEventLoop();
|
|
25421
|
+
}
|
|
25377
25422
|
}
|
|
25378
25423
|
function openCliSqliteConnection(options) {
|
|
25424
|
+
if (isCliImmediateShutdownRequested()) throw new CliSqliteInterrupted();
|
|
25379
25425
|
const sqlitePath = getCliSqlitePath();
|
|
25380
25426
|
ensureCliSqliteParentDir(sqlitePath);
|
|
25381
25427
|
const db = new SqliteDatabase(sqlitePath);
|
|
@@ -25392,6 +25438,7 @@ function openCliSqliteConnection(options) {
|
|
|
25392
25438
|
}
|
|
25393
25439
|
function withCliSqliteSync(fn, options) {
|
|
25394
25440
|
for (let attempt = 1; attempt <= CLI_SQLITE_SYNC_RETRY_MAX; attempt++) {
|
|
25441
|
+
if (isCliImmediateShutdownRequested()) throw new CliSqliteInterrupted();
|
|
25395
25442
|
let db;
|
|
25396
25443
|
try {
|
|
25397
25444
|
db = openCliSqliteConnection(options);
|
|
@@ -25402,6 +25449,7 @@ function withCliSqliteSync(fn, options) {
|
|
|
25402
25449
|
}
|
|
25403
25450
|
} catch (e) {
|
|
25404
25451
|
safeCloseCliSqliteDatabase(db);
|
|
25452
|
+
if (e instanceof CliSqliteInterrupted) throw e;
|
|
25405
25453
|
if (!isCliSqliteLockError(e) || attempt === CLI_SQLITE_SYNC_RETRY_MAX) throw e;
|
|
25406
25454
|
syncSleepMs(Math.min(500, 12 * attempt));
|
|
25407
25455
|
}
|
|
@@ -25411,6 +25459,7 @@ function withCliSqliteSync(fn, options) {
|
|
|
25411
25459
|
async function withCliSqlite(fn, options) {
|
|
25412
25460
|
let lastError;
|
|
25413
25461
|
for (let attempt = 1; attempt <= CLI_SQLITE_ASYNC_RETRY_MAX; attempt++) {
|
|
25462
|
+
if (isCliImmediateShutdownRequested()) throw new CliSqliteInterrupted();
|
|
25414
25463
|
let db;
|
|
25415
25464
|
try {
|
|
25416
25465
|
db = openCliSqliteConnection(options);
|
|
@@ -25422,9 +25471,10 @@ async function withCliSqlite(fn, options) {
|
|
|
25422
25471
|
} catch (e) {
|
|
25423
25472
|
lastError = e;
|
|
25424
25473
|
safeCloseCliSqliteDatabase(db);
|
|
25474
|
+
if (e instanceof CliSqliteInterrupted) throw e;
|
|
25425
25475
|
if (!isCliSqliteLockError(e) || attempt === CLI_SQLITE_ASYNC_RETRY_MAX) throw e;
|
|
25426
25476
|
const delayMs = Math.min(600, CLI_SQLITE_ASYNC_BASE_DELAY_MS * attempt);
|
|
25427
|
-
await
|
|
25477
|
+
await asyncDelayMsInterruptible(delayMs);
|
|
25428
25478
|
await yieldToEventLoop();
|
|
25429
25479
|
}
|
|
25430
25480
|
}
|
|
@@ -25437,9 +25487,9 @@ async function ensureCliSqliteInitialized(options) {
|
|
|
25437
25487
|
|
|
25438
25488
|
// src/connection/close-bridge-connection.ts
|
|
25439
25489
|
async function closeBridgeConnection(state, acpManager, devServerManager, log2) {
|
|
25490
|
+
requestCliImmediateShutdown();
|
|
25440
25491
|
const say = log2 ?? logImmediate;
|
|
25441
25492
|
say("Cleaning up connections\u2026");
|
|
25442
|
-
await new Promise((resolve18) => setImmediate(resolve18));
|
|
25443
25493
|
state.closedByUser = true;
|
|
25444
25494
|
clearReconnectQuietTimer(state.mainQuiet);
|
|
25445
25495
|
clearReconnectQuietTimer(state.firehoseQuiet);
|
|
@@ -25476,7 +25526,7 @@ async function closeBridgeConnection(state, acpManager, devServerManager, log2)
|
|
|
25476
25526
|
}
|
|
25477
25527
|
if (devServerManager) {
|
|
25478
25528
|
say("Stopping local dev server processes\u2026");
|
|
25479
|
-
await devServerManager.shutdownAllGraceful();
|
|
25529
|
+
await devServerManager.shutdownAllGraceful({ graceMs: BRIDGE_CLOSE_DEV_SERVER_GRACE_MS });
|
|
25480
25530
|
}
|
|
25481
25531
|
try {
|
|
25482
25532
|
closeAllCliSqliteConnections();
|
|
@@ -25500,17 +25550,13 @@ function resolveSessionParentPathForAgentProcess(resolvedSessionParentPath) {
|
|
|
25500
25550
|
return getBridgeRoot();
|
|
25501
25551
|
}
|
|
25502
25552
|
|
|
25503
|
-
// src/git/session-git-queue.ts
|
|
25504
|
-
import { execFile as execFile7 } from "node:child_process";
|
|
25553
|
+
// src/git/snapshot/session-git-queue.ts
|
|
25505
25554
|
import { readFile, stat } from "node:fs/promises";
|
|
25506
|
-
import { promisify as promisify7 } from "node:util";
|
|
25507
25555
|
import * as path15 from "node:path";
|
|
25508
25556
|
|
|
25509
|
-
// src/git/pre-turn-snapshot.ts
|
|
25557
|
+
// src/git/snapshot/pre-turn-snapshot.ts
|
|
25510
25558
|
import * as fs14 from "node:fs";
|
|
25511
25559
|
import * as path14 from "node:path";
|
|
25512
|
-
import { execFile as execFile6 } from "node:child_process";
|
|
25513
|
-
import { promisify as promisify6 } from "node:util";
|
|
25514
25560
|
|
|
25515
25561
|
// src/git/discover-repos.ts
|
|
25516
25562
|
import * as fs13 from "node:fs";
|
|
@@ -30075,12 +30121,68 @@ function gitInstanceFactory(baseDir, options) {
|
|
|
30075
30121
|
init_git_response_error();
|
|
30076
30122
|
var simpleGit = gitInstanceFactory;
|
|
30077
30123
|
|
|
30124
|
+
// src/git/git-runtime.ts
|
|
30125
|
+
var activeGitChildProcesses = /* @__PURE__ */ new Set();
|
|
30126
|
+
function abortActiveGitChildProcesses() {
|
|
30127
|
+
for (const child of activeGitChildProcesses) {
|
|
30128
|
+
try {
|
|
30129
|
+
child.kill("SIGTERM");
|
|
30130
|
+
} catch {
|
|
30131
|
+
}
|
|
30132
|
+
}
|
|
30133
|
+
}
|
|
30134
|
+
var GitOperationAbortedError = class extends Error {
|
|
30135
|
+
constructor(message = "Git operation aborted") {
|
|
30136
|
+
super(message);
|
|
30137
|
+
this.name = "GitOperationAbortedError";
|
|
30138
|
+
}
|
|
30139
|
+
};
|
|
30140
|
+
function throwIfGitShutdownRequested() {
|
|
30141
|
+
if (isCliImmediateShutdownRequested()) {
|
|
30142
|
+
abortActiveGitChildProcesses();
|
|
30143
|
+
throw new GitOperationAbortedError();
|
|
30144
|
+
}
|
|
30145
|
+
}
|
|
30146
|
+
async function runGitTask(fn) {
|
|
30147
|
+
throwIfGitShutdownRequested();
|
|
30148
|
+
try {
|
|
30149
|
+
const result = await fn();
|
|
30150
|
+
throwIfGitShutdownRequested();
|
|
30151
|
+
return result;
|
|
30152
|
+
} catch (e) {
|
|
30153
|
+
if (isCliImmediateShutdownRequested()) {
|
|
30154
|
+
throw new GitOperationAbortedError();
|
|
30155
|
+
}
|
|
30156
|
+
throw e;
|
|
30157
|
+
}
|
|
30158
|
+
}
|
|
30159
|
+
async function yieldToEventLoop2() {
|
|
30160
|
+
throwIfGitShutdownRequested();
|
|
30161
|
+
await new Promise((resolve18) => {
|
|
30162
|
+
setImmediate(resolve18);
|
|
30163
|
+
});
|
|
30164
|
+
throwIfGitShutdownRequested();
|
|
30165
|
+
}
|
|
30166
|
+
async function forEachWithGitYield(items, fn) {
|
|
30167
|
+
for (let i = 0; i < items.length; i++) {
|
|
30168
|
+
if (i > 0 || items.length > 1) await yieldToEventLoop2();
|
|
30169
|
+
await fn(items[i], i);
|
|
30170
|
+
}
|
|
30171
|
+
}
|
|
30172
|
+
|
|
30078
30173
|
// src/git/cli-simple-git.ts
|
|
30079
30174
|
function cliSimpleGit(baseDir) {
|
|
30080
|
-
const git = simpleGit({
|
|
30175
|
+
const git = simpleGit({
|
|
30176
|
+
baseDir,
|
|
30177
|
+
trimmed: true,
|
|
30178
|
+
spawnOptions: {}
|
|
30179
|
+
});
|
|
30081
30180
|
git.outputHandler((command, stdout, stderr) => {
|
|
30082
30181
|
const trace = isCliTrace();
|
|
30083
30182
|
const onChunk = (label) => (chunk) => {
|
|
30183
|
+
if (isCliImmediateShutdownRequested()) {
|
|
30184
|
+
abortActiveGitChildProcesses();
|
|
30185
|
+
}
|
|
30084
30186
|
const text = typeof chunk === "string" ? chunk : chunk.toString("utf8");
|
|
30085
30187
|
const line = text.replace(/\s+$/, "");
|
|
30086
30188
|
if (trace && line) {
|
|
@@ -30169,17 +30271,37 @@ async function discoverGitReposUnderRoot(rootPath) {
|
|
|
30169
30271
|
return out;
|
|
30170
30272
|
}
|
|
30171
30273
|
|
|
30172
|
-
// src/git/
|
|
30274
|
+
// src/git/git-exec.ts
|
|
30275
|
+
import { execFile as execFile6, spawn as spawn4 } from "node:child_process";
|
|
30276
|
+
import { promisify as promisify6 } from "node:util";
|
|
30173
30277
|
var execFileAsync5 = promisify6(execFile6);
|
|
30278
|
+
async function execGitFile(args, options) {
|
|
30279
|
+
throwIfGitShutdownRequested();
|
|
30280
|
+
try {
|
|
30281
|
+
const result = await execFileAsync5("git", args, {
|
|
30282
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
30283
|
+
...options
|
|
30284
|
+
});
|
|
30285
|
+
throwIfGitShutdownRequested();
|
|
30286
|
+
return {
|
|
30287
|
+
stdout: String(result.stdout ?? ""),
|
|
30288
|
+
stderr: String(result.stderr ?? "")
|
|
30289
|
+
};
|
|
30290
|
+
} catch (e) {
|
|
30291
|
+
if (isCliImmediateShutdownRequested()) {
|
|
30292
|
+
throw new GitOperationAbortedError();
|
|
30293
|
+
}
|
|
30294
|
+
throw e;
|
|
30295
|
+
}
|
|
30296
|
+
}
|
|
30297
|
+
|
|
30298
|
+
// src/git/snapshot/pre-turn-snapshot.ts
|
|
30174
30299
|
function snapshotsDirForCwd(agentCwd) {
|
|
30175
30300
|
return path14.join(agentCwd, ".buildautomaton", "snapshots");
|
|
30176
30301
|
}
|
|
30177
30302
|
async function gitStashCreate(repoRoot, log2) {
|
|
30178
30303
|
try {
|
|
30179
|
-
const { stdout } = await
|
|
30180
|
-
cwd: repoRoot,
|
|
30181
|
-
maxBuffer: 10 * 1024 * 1024
|
|
30182
|
-
});
|
|
30304
|
+
const { stdout } = await execGitFile(["stash", "create"], { cwd: repoRoot });
|
|
30183
30305
|
return stdout.trim();
|
|
30184
30306
|
} catch (e) {
|
|
30185
30307
|
log2(
|
|
@@ -30190,7 +30312,7 @@ async function gitStashCreate(repoRoot, log2) {
|
|
|
30190
30312
|
}
|
|
30191
30313
|
async function gitRun(repoRoot, args, log2, label) {
|
|
30192
30314
|
try {
|
|
30193
|
-
await
|
|
30315
|
+
await execGitFile(args, { cwd: repoRoot });
|
|
30194
30316
|
return { ok: true };
|
|
30195
30317
|
} catch (e) {
|
|
30196
30318
|
const msg = e instanceof Error ? e.message : String(e);
|
|
@@ -30224,10 +30346,10 @@ async function capturePreTurnSnapshot(options) {
|
|
|
30224
30346
|
return { ok: false, error: "No git repos to snapshot" };
|
|
30225
30347
|
}
|
|
30226
30348
|
const repos = [];
|
|
30227
|
-
|
|
30349
|
+
await forEachWithGitYield(repoRoots, async (root) => {
|
|
30228
30350
|
const stashSha = await gitStashCreate(root, log2);
|
|
30229
30351
|
repos.push({ path: root, stashSha });
|
|
30230
|
-
}
|
|
30352
|
+
});
|
|
30231
30353
|
const dir = snapshotsDirForCwd(agentCwd);
|
|
30232
30354
|
try {
|
|
30233
30355
|
fs14.mkdirSync(dir, { recursive: true });
|
|
@@ -30262,17 +30384,25 @@ async function applyPreTurnSnapshot(filePath, log2) {
|
|
|
30262
30384
|
if (!Array.isArray(data.repos)) {
|
|
30263
30385
|
return { ok: false, error: "Invalid snapshot file" };
|
|
30264
30386
|
}
|
|
30265
|
-
|
|
30266
|
-
|
|
30387
|
+
let applyError = null;
|
|
30388
|
+
await forEachWithGitYield(data.repos, async (r) => {
|
|
30389
|
+
if (applyError || !r.path) return;
|
|
30267
30390
|
const reset = await gitRun(r.path, ["reset", "--hard", "HEAD"], log2, "reset --hard");
|
|
30268
|
-
if (!reset.ok)
|
|
30391
|
+
if (!reset.ok) {
|
|
30392
|
+
applyError = reset;
|
|
30393
|
+
return;
|
|
30394
|
+
}
|
|
30269
30395
|
const clean = await gitRun(r.path, ["clean", "-fd"], log2, "clean -fd");
|
|
30270
|
-
if (!clean.ok)
|
|
30396
|
+
if (!clean.ok) {
|
|
30397
|
+
applyError = clean;
|
|
30398
|
+
return;
|
|
30399
|
+
}
|
|
30271
30400
|
if (r.stashSha) {
|
|
30272
30401
|
const ap = await gitRun(r.path, ["stash", "apply", r.stashSha], log2, "stash apply");
|
|
30273
|
-
if (!ap.ok)
|
|
30402
|
+
if (!ap.ok) applyError = ap;
|
|
30274
30403
|
}
|
|
30275
|
-
}
|
|
30404
|
+
});
|
|
30405
|
+
if (applyError?.ok === false) return applyError;
|
|
30276
30406
|
log2(`[snapshot] Restored pre-turn state for ${data.runId.slice(0, 8)}\u2026`);
|
|
30277
30407
|
return { ok: true };
|
|
30278
30408
|
}
|
|
@@ -30280,8 +30410,7 @@ function snapshotFilePath(agentCwd, runId) {
|
|
|
30280
30410
|
return path14.join(snapshotsDirForCwd(agentCwd), `${runId}.json`);
|
|
30281
30411
|
}
|
|
30282
30412
|
|
|
30283
|
-
// src/git/session-git-queue.ts
|
|
30284
|
-
var execFileAsync6 = promisify7(execFile7);
|
|
30413
|
+
// src/git/snapshot/session-git-queue.ts
|
|
30285
30414
|
var MAX_FULL_FILE_TEXT_BYTES = 512 * 1024;
|
|
30286
30415
|
async function readWorkspaceFileAsUtf8(absPath) {
|
|
30287
30416
|
try {
|
|
@@ -30310,35 +30439,28 @@ async function collectTurnGitDiffFromPreTurnSnapshot(options) {
|
|
|
30310
30439
|
return;
|
|
30311
30440
|
}
|
|
30312
30441
|
const multiRepo = data.repos.length > 1;
|
|
30313
|
-
|
|
30314
|
-
if (!repo.stashSha)
|
|
30442
|
+
await forEachWithGitYield(data.repos, async (repo) => {
|
|
30443
|
+
if (!repo.stashSha) return;
|
|
30315
30444
|
let namesRaw;
|
|
30316
30445
|
try {
|
|
30317
|
-
const { stdout } = await
|
|
30318
|
-
cwd: repo.path,
|
|
30319
|
-
maxBuffer: 10 * 1024 * 1024
|
|
30320
|
-
});
|
|
30446
|
+
const { stdout } = await execGitFile(["diff", "--name-only", repo.stashSha], { cwd: repo.path });
|
|
30321
30447
|
namesRaw = stdout;
|
|
30322
30448
|
} catch (e) {
|
|
30323
30449
|
log2(
|
|
30324
30450
|
`[session-git-queue] Git diff --name-only failed in ${repo.path}: ${e instanceof Error ? e.message : String(e)}`
|
|
30325
30451
|
);
|
|
30326
|
-
|
|
30452
|
+
return;
|
|
30327
30453
|
}
|
|
30328
30454
|
const lines = namesRaw.split("\n").map((l) => l.trim()).filter(Boolean);
|
|
30329
30455
|
const slug = path15.basename(repo.path).replace(/[^\w.-]+/g, "_") || "repo";
|
|
30330
|
-
|
|
30331
|
-
if (rel.includes(".."))
|
|
30456
|
+
await forEachWithGitYield(lines, async (rel) => {
|
|
30457
|
+
if (rel.includes("..")) return;
|
|
30332
30458
|
try {
|
|
30333
|
-
const { stdout: patchContent } = await
|
|
30334
|
-
"git",
|
|
30459
|
+
const { stdout: patchContent } = await execGitFile(
|
|
30335
30460
|
["diff", "--no-color", repo.stashSha, "--", rel],
|
|
30336
|
-
{
|
|
30337
|
-
cwd: repo.path,
|
|
30338
|
-
maxBuffer: 50 * 1024 * 1024
|
|
30339
|
-
}
|
|
30461
|
+
{ cwd: repo.path }
|
|
30340
30462
|
);
|
|
30341
|
-
if (!patchContent.trim())
|
|
30463
|
+
if (!patchContent.trim()) return;
|
|
30342
30464
|
const displayPath = multiRepo ? `${slug}/${rel}` : rel;
|
|
30343
30465
|
const workspaceFilePath = path15.join(repo.path, rel);
|
|
30344
30466
|
const newText = await readWorkspaceFileAsUtf8(workspaceFilePath);
|
|
@@ -30355,8 +30477,8 @@ async function collectTurnGitDiffFromPreTurnSnapshot(options) {
|
|
|
30355
30477
|
`[session-git-queue] Git diff failed for ${rel}: ${e instanceof Error ? e.message : String(e)}`
|
|
30356
30478
|
);
|
|
30357
30479
|
}
|
|
30358
|
-
}
|
|
30359
|
-
}
|
|
30480
|
+
});
|
|
30481
|
+
});
|
|
30360
30482
|
}
|
|
30361
30483
|
|
|
30362
30484
|
// src/agents/acp/put-summarize-change-summaries.ts
|
|
@@ -30722,34 +30844,50 @@ __export(claude_code_acp_client_exports, {
|
|
|
30722
30844
|
createClaudeCodeAcpClient: () => createClaudeCodeAcpClient,
|
|
30723
30845
|
detectLocalAgentPresence: () => detectLocalAgentPresence
|
|
30724
30846
|
});
|
|
30725
|
-
import { execFile as execFile9 } from "node:child_process";
|
|
30726
|
-
import { promisify as promisify9 } from "node:util";
|
|
30727
30847
|
|
|
30728
30848
|
// src/agents/acp/clients/detect-command-on-path.ts
|
|
30729
|
-
import { execFile as
|
|
30730
|
-
import { promisify as
|
|
30731
|
-
var
|
|
30732
|
-
|
|
30849
|
+
import { execFile as execFile7 } from "node:child_process";
|
|
30850
|
+
import { promisify as promisify7 } from "node:util";
|
|
30851
|
+
var execFileAsync6 = promisify7(execFile7);
|
|
30852
|
+
var COMMAND_ON_PATH_PROBE_TIMEOUT_MS = 750;
|
|
30853
|
+
async function execFileShutdownAware(file2, args, timeoutMs) {
|
|
30854
|
+
if (isCliImmediateShutdownRequested()) throw new Error("shutdown");
|
|
30855
|
+
const ac = new AbortController();
|
|
30856
|
+
const shutdownPoll = setInterval(() => {
|
|
30857
|
+
if (isCliImmediateShutdownRequested()) ac.abort();
|
|
30858
|
+
}, 50);
|
|
30859
|
+
shutdownPoll.unref?.();
|
|
30860
|
+
try {
|
|
30861
|
+
await execFileAsync6(file2, args, { timeout: timeoutMs, signal: ac.signal });
|
|
30862
|
+
} finally {
|
|
30863
|
+
clearInterval(shutdownPoll);
|
|
30864
|
+
}
|
|
30865
|
+
}
|
|
30866
|
+
async function isCommandOnPath(command, timeoutMs = COMMAND_ON_PATH_PROBE_TIMEOUT_MS) {
|
|
30867
|
+
if (isCliImmediateShutdownRequested()) return false;
|
|
30733
30868
|
try {
|
|
30734
|
-
await
|
|
30869
|
+
await execFileShutdownAware("which", [command], timeoutMs);
|
|
30735
30870
|
return true;
|
|
30736
30871
|
} catch {
|
|
30737
30872
|
return false;
|
|
30738
30873
|
}
|
|
30739
30874
|
}
|
|
30740
|
-
|
|
30741
|
-
|
|
30742
|
-
var execFileAsync8 = promisify9(execFile9);
|
|
30743
|
-
var BACKEND_LOCAL_AGENT_TYPE = "claude-code";
|
|
30744
|
-
async function detectLocalAgentPresence() {
|
|
30745
|
-
if (await isCommandOnPath("claude")) return true;
|
|
30875
|
+
async function execProbeShutdownAware(file2, args, timeoutMs) {
|
|
30876
|
+
if (isCliImmediateShutdownRequested()) return false;
|
|
30746
30877
|
try {
|
|
30747
|
-
await
|
|
30878
|
+
await execFileShutdownAware(file2, args, timeoutMs);
|
|
30748
30879
|
return true;
|
|
30749
30880
|
} catch {
|
|
30750
30881
|
return false;
|
|
30751
30882
|
}
|
|
30752
30883
|
}
|
|
30884
|
+
|
|
30885
|
+
// src/agents/acp/clients/claude-code-acp-client.ts
|
|
30886
|
+
var BACKEND_LOCAL_AGENT_TYPE = "claude-code";
|
|
30887
|
+
async function detectLocalAgentPresence() {
|
|
30888
|
+
if (await isCommandOnPath("claude")) return true;
|
|
30889
|
+
return execProbeShutdownAware("npx", ["--yes", "@anthropic-ai/claude-code", "--version"], 3e3);
|
|
30890
|
+
}
|
|
30753
30891
|
function buildClaudeCodeAcpSpawnCommand(base, _sessionMode) {
|
|
30754
30892
|
return [...base];
|
|
30755
30893
|
}
|
|
@@ -30800,7 +30938,7 @@ __export(cursor_acp_client_exports, {
|
|
|
30800
30938
|
createCursorAcpClient: () => createCursorAcpClient,
|
|
30801
30939
|
detectLocalAgentPresence: () => detectLocalAgentPresence3
|
|
30802
30940
|
});
|
|
30803
|
-
import { spawn as
|
|
30941
|
+
import { spawn as spawn5 } from "node:child_process";
|
|
30804
30942
|
import * as readline from "node:readline";
|
|
30805
30943
|
|
|
30806
30944
|
// src/agents/acp/format-session-update-kind-for-log.ts
|
|
@@ -30870,7 +31008,7 @@ async function createCursorAcpClient(options) {
|
|
|
30870
31008
|
} = options;
|
|
30871
31009
|
const dbgFs = process.env.BUILDAUTOMATON_DEBUG_ACP_FS === "1";
|
|
30872
31010
|
const isWindows = process.platform === "win32";
|
|
30873
|
-
const child =
|
|
31011
|
+
const child = spawn5(command[0], command.slice(1), {
|
|
30874
31012
|
cwd,
|
|
30875
31013
|
stdio: ["pipe", "pipe", "pipe"],
|
|
30876
31014
|
env: process.env,
|
|
@@ -32316,7 +32454,7 @@ import os8 from "node:os";
|
|
|
32316
32454
|
import * as fs17 from "node:fs";
|
|
32317
32455
|
import * as path20 from "node:path";
|
|
32318
32456
|
|
|
32319
|
-
// src/git/worktree-add.ts
|
|
32457
|
+
// src/git/worktrees/worktree-add.ts
|
|
32320
32458
|
async function gitWorktreeAddBranch(mainRepoPath, worktreePath, branch) {
|
|
32321
32459
|
const mainGit = cliSimpleGit(mainRepoPath);
|
|
32322
32460
|
await mainGit.raw(["worktree", "add", "-b", branch, worktreePath, "HEAD"]);
|
|
@@ -32421,7 +32559,7 @@ async function prepareNewSessionWorktrees(options) {
|
|
|
32421
32559
|
};
|
|
32422
32560
|
}
|
|
32423
32561
|
|
|
32424
|
-
// src/git/rename-branch.ts
|
|
32562
|
+
// src/git/branches/rename-branch.ts
|
|
32425
32563
|
async function gitRenameCurrentBranch(repoDir, newName) {
|
|
32426
32564
|
const g = cliSimpleGit(repoDir);
|
|
32427
32565
|
await g.raw(["branch", "-m", newName]);
|
|
@@ -32445,10 +32583,10 @@ async function renameSessionWorktreeBranches(paths, newBranch, log2) {
|
|
|
32445
32583
|
// src/worktrees/remove-session-worktrees.ts
|
|
32446
32584
|
import * as fs20 from "node:fs";
|
|
32447
32585
|
|
|
32448
|
-
// src/git/worktree-remove.ts
|
|
32586
|
+
// src/git/worktrees/worktree-remove.ts
|
|
32449
32587
|
import * as fs19 from "node:fs";
|
|
32450
32588
|
|
|
32451
|
-
// src/git/resolve-main-repo-from-git-file.ts
|
|
32589
|
+
// src/git/worktrees/resolve-main-repo-from-git-file.ts
|
|
32452
32590
|
import * as fs18 from "node:fs";
|
|
32453
32591
|
import * as path21 from "node:path";
|
|
32454
32592
|
function resolveMainRepoFromWorktreeGitFile(wt) {
|
|
@@ -32462,7 +32600,7 @@ function resolveMainRepoFromWorktreeGitFile(wt) {
|
|
|
32462
32600
|
return path21.dirname(gitDir);
|
|
32463
32601
|
}
|
|
32464
32602
|
|
|
32465
|
-
// src/git/worktree-remove.ts
|
|
32603
|
+
// src/git/worktrees/worktree-remove.ts
|
|
32466
32604
|
async function gitWorktreeRemoveForce(worktreePath) {
|
|
32467
32605
|
const mainRepo = resolveMainRepoFromWorktreeGitFile(worktreePath);
|
|
32468
32606
|
if (mainRepo) {
|
|
@@ -32488,7 +32626,88 @@ async function removeSessionWorktrees(paths, log2) {
|
|
|
32488
32626
|
}
|
|
32489
32627
|
}
|
|
32490
32628
|
|
|
32491
|
-
// src/git/
|
|
32629
|
+
// src/git/changes/lib/parse-git-status.ts
|
|
32630
|
+
function parseNameStatusLines(lines) {
|
|
32631
|
+
const m = /* @__PURE__ */ new Map();
|
|
32632
|
+
for (const line of lines) {
|
|
32633
|
+
if (!line.trim()) continue;
|
|
32634
|
+
const tabParts = line.split(" ");
|
|
32635
|
+
if (tabParts.length < 2) continue;
|
|
32636
|
+
const status = tabParts[0].trim();
|
|
32637
|
+
const code = status[0];
|
|
32638
|
+
if (code === "A") {
|
|
32639
|
+
m.set(tabParts[tabParts.length - 1], "added");
|
|
32640
|
+
} else if (code === "D") {
|
|
32641
|
+
m.set(tabParts[tabParts.length - 1], "removed");
|
|
32642
|
+
} else if (code === "R" || code === "C") {
|
|
32643
|
+
if (tabParts.length >= 3) m.set(tabParts[tabParts.length - 1], "modified");
|
|
32644
|
+
} else if (code === "M" || code === "U" || code === "T") {
|
|
32645
|
+
m.set(tabParts[tabParts.length - 1], "modified");
|
|
32646
|
+
}
|
|
32647
|
+
}
|
|
32648
|
+
return m;
|
|
32649
|
+
}
|
|
32650
|
+
function parseNumstatFirstLine(line) {
|
|
32651
|
+
const parts = line.split(" ");
|
|
32652
|
+
if (parts.length < 3) return null;
|
|
32653
|
+
const [a, d] = parts;
|
|
32654
|
+
const additions = a === "-" ? 0 : parseInt(String(a), 10) || 0;
|
|
32655
|
+
const deletions = d === "-" ? 0 : parseInt(String(d), 10) || 0;
|
|
32656
|
+
return { additions, deletions };
|
|
32657
|
+
}
|
|
32658
|
+
function parseNumstat(lines) {
|
|
32659
|
+
const m = /* @__PURE__ */ new Map();
|
|
32660
|
+
for (const line of lines) {
|
|
32661
|
+
if (!line.trim()) continue;
|
|
32662
|
+
const parts = line.split(" ");
|
|
32663
|
+
if (parts.length < 3) continue;
|
|
32664
|
+
const [a, d, p] = parts;
|
|
32665
|
+
const additions = a === "-" ? 0 : parseInt(String(a), 10) || 0;
|
|
32666
|
+
const deletions = d === "-" ? 0 : parseInt(String(d), 10) || 0;
|
|
32667
|
+
m.set(p, { additions, deletions });
|
|
32668
|
+
}
|
|
32669
|
+
return m;
|
|
32670
|
+
}
|
|
32671
|
+
async function numstatFromGitNoIndex(g, pathInRepo) {
|
|
32672
|
+
const devNull = process.platform === "win32" ? "NUL" : "/dev/null";
|
|
32673
|
+
try {
|
|
32674
|
+
const out = await g.raw(["diff", "--numstat", "--no-index", "--", devNull, pathInRepo]);
|
|
32675
|
+
const first2 = String(out).split("\n").find((l) => l.trim()) ?? "";
|
|
32676
|
+
return parseNumstatFirstLine(first2);
|
|
32677
|
+
} catch {
|
|
32678
|
+
return null;
|
|
32679
|
+
}
|
|
32680
|
+
}
|
|
32681
|
+
|
|
32682
|
+
// src/git/changes/lib/working-tree-changed-path-count.ts
|
|
32683
|
+
function workingTreeChangedPathCount(nameStatusLines, numstatLines, untrackedLines) {
|
|
32684
|
+
const kindByPath = parseNameStatusLines(nameStatusLines);
|
|
32685
|
+
const numByPath = parseNumstat(numstatLines);
|
|
32686
|
+
const paths = /* @__PURE__ */ new Set([...kindByPath.keys(), ...numByPath.keys()]);
|
|
32687
|
+
for (const p of untrackedLines.map((s) => s.trim()).filter(Boolean)) {
|
|
32688
|
+
paths.add(p);
|
|
32689
|
+
}
|
|
32690
|
+
return paths.size;
|
|
32691
|
+
}
|
|
32692
|
+
|
|
32693
|
+
// src/git/changes/count-working-tree-changed-files.ts
|
|
32694
|
+
async function countWorkingTreeChangedFilesForRepo(repoGitCwd) {
|
|
32695
|
+
return runGitTask(async () => {
|
|
32696
|
+
const g = cliSimpleGit(repoGitCwd);
|
|
32697
|
+
const [nameStatusRaw, numstatRaw, untrackedRaw] = await Promise.all([
|
|
32698
|
+
g.raw(["diff", "--name-status", "HEAD"]).catch(() => ""),
|
|
32699
|
+
g.raw(["diff", "HEAD", "--numstat"]).catch(() => ""),
|
|
32700
|
+
g.raw(["ls-files", "--others", "--exclude-standard"]).catch(() => "")
|
|
32701
|
+
]);
|
|
32702
|
+
return workingTreeChangedPathCount(
|
|
32703
|
+
String(nameStatusRaw).split("\n"),
|
|
32704
|
+
String(numstatRaw).split("\n"),
|
|
32705
|
+
String(untrackedRaw).split("\n")
|
|
32706
|
+
);
|
|
32707
|
+
});
|
|
32708
|
+
}
|
|
32709
|
+
|
|
32710
|
+
// src/git/commits/resolve-remote-tracking.ts
|
|
32492
32711
|
async function tryConfigGet(g, key) {
|
|
32493
32712
|
try {
|
|
32494
32713
|
const out = await g.raw(["config", "--get", key]);
|
|
@@ -32571,30 +32790,6 @@ async function resolveBaseShaForUnpushedCommits(g) {
|
|
|
32571
32790
|
if (!defaultRef) return null;
|
|
32572
32791
|
return revParseSafe(g, defaultRef);
|
|
32573
32792
|
}
|
|
32574
|
-
function parseLogShaDateSubjectLines(raw) {
|
|
32575
|
-
const out = [];
|
|
32576
|
-
for (const line of String(raw).split("\n")) {
|
|
32577
|
-
const l = line.trimEnd();
|
|
32578
|
-
if (!l.trim()) continue;
|
|
32579
|
-
const parts = l.split(" ");
|
|
32580
|
-
if (parts.length < 3) continue;
|
|
32581
|
-
const sha = parts[0].trim();
|
|
32582
|
-
const committedAt = parts[1].trim();
|
|
32583
|
-
const subject = parts.slice(2).join(" ").trim();
|
|
32584
|
-
if (!/^[0-9a-f]{7,40}$/i.test(sha)) continue;
|
|
32585
|
-
out.push({ sha, shortSha: sha.slice(0, 7), subject, committedAt });
|
|
32586
|
-
}
|
|
32587
|
-
return out;
|
|
32588
|
-
}
|
|
32589
|
-
async function gitLogNotReachableFromBase(g, baseSha, headSha) {
|
|
32590
|
-
if (baseSha === headSha) return [];
|
|
32591
|
-
try {
|
|
32592
|
-
const logOut = await g.raw(["log", "--format=%H %cI %s", `${baseSha}..${headSha}`]);
|
|
32593
|
-
return parseLogShaDateSubjectLines(logOut);
|
|
32594
|
-
} catch {
|
|
32595
|
-
return [];
|
|
32596
|
-
}
|
|
32597
|
-
}
|
|
32598
32793
|
async function commitsAheadOfRemoteTracking(repoDir) {
|
|
32599
32794
|
const g = cliSimpleGit(repoDir);
|
|
32600
32795
|
const headSha = await revParseSafe(g, "HEAD");
|
|
@@ -32609,44 +32804,41 @@ async function commitsAheadOfRemoteTracking(repoDir) {
|
|
|
32609
32804
|
return 0;
|
|
32610
32805
|
}
|
|
32611
32806
|
}
|
|
32807
|
+
|
|
32808
|
+
// src/git/status/working-tree-status.ts
|
|
32612
32809
|
async function getRepoWorkingTreeStatus(repoDir) {
|
|
32613
|
-
|
|
32614
|
-
|
|
32615
|
-
|
|
32616
|
-
|
|
32617
|
-
|
|
32618
|
-
}
|
|
32619
|
-
async function listUnpushedCommits(repoDir) {
|
|
32620
|
-
const g = cliSimpleGit(repoDir);
|
|
32621
|
-
const headSha = await revParseSafe(g, "HEAD");
|
|
32622
|
-
if (!headSha) return [];
|
|
32623
|
-
const baseSha = await resolveBaseShaForUnpushedCommits(g);
|
|
32624
|
-
if (!baseSha) return [];
|
|
32625
|
-
return gitLogNotReachableFromBase(g, baseSha, headSha);
|
|
32810
|
+
return runGitTask(async () => {
|
|
32811
|
+
const uncommittedFileCount = await countWorkingTreeChangedFilesForRepo(repoDir);
|
|
32812
|
+
const hasUncommittedChanges = uncommittedFileCount > 0;
|
|
32813
|
+
const ahead = await commitsAheadOfRemoteTracking(repoDir);
|
|
32814
|
+
return { hasUncommittedChanges, hasUnpushedCommits: ahead > 0, uncommittedFileCount };
|
|
32815
|
+
});
|
|
32626
32816
|
}
|
|
32627
32817
|
async function aggregateSessionPathsWorkingTreeStatus(paths) {
|
|
32628
32818
|
let hasUncommittedChanges = false;
|
|
32629
32819
|
let hasUnpushedCommits = false;
|
|
32630
|
-
|
|
32820
|
+
let uncommittedFileCount = 0;
|
|
32821
|
+
await forEachWithGitYield(paths, async (p) => {
|
|
32631
32822
|
const s = await getRepoWorkingTreeStatus(p);
|
|
32823
|
+
uncommittedFileCount += s.uncommittedFileCount;
|
|
32632
32824
|
if (s.hasUncommittedChanges) hasUncommittedChanges = true;
|
|
32633
32825
|
if (s.hasUnpushedCommits) hasUnpushedCommits = true;
|
|
32634
|
-
}
|
|
32635
|
-
return { hasUncommittedChanges, hasUnpushedCommits };
|
|
32826
|
+
});
|
|
32827
|
+
return { hasUncommittedChanges, hasUnpushedCommits, uncommittedFileCount };
|
|
32636
32828
|
}
|
|
32637
32829
|
async function pushAheadOfUpstreamForPaths(paths) {
|
|
32638
|
-
|
|
32830
|
+
await forEachWithGitYield(paths, async (p) => {
|
|
32639
32831
|
const g = cliSimpleGit(p);
|
|
32640
32832
|
const ahead = await commitsAheadOfRemoteTracking(p);
|
|
32641
|
-
if (ahead <= 0)
|
|
32833
|
+
if (ahead <= 0) return;
|
|
32642
32834
|
await g.push();
|
|
32643
|
-
}
|
|
32835
|
+
});
|
|
32644
32836
|
}
|
|
32645
32837
|
|
|
32646
|
-
// src/git/
|
|
32838
|
+
// src/git/changes/types.ts
|
|
32647
32839
|
var MAX_PATCH_CHARS = 35e4;
|
|
32648
32840
|
|
|
32649
|
-
// src/git/
|
|
32841
|
+
// src/git/changes/lib/repo-format.ts
|
|
32650
32842
|
function posixJoinDirFile(dir, file2) {
|
|
32651
32843
|
const d = dir === "." || dir === "" ? "" : dir.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
32652
32844
|
const f = file2.replace(/\\/g, "/").replace(/^\/+/, "");
|
|
@@ -32700,63 +32892,80 @@ function formatRemoteDisplayLabel(remoteUrl) {
|
|
|
32700
32892
|
return `origin \xB7 ${hostPath}`;
|
|
32701
32893
|
}
|
|
32702
32894
|
|
|
32703
|
-
// src/git/
|
|
32895
|
+
// src/git/changes/get-working-tree-change-repo-details.ts
|
|
32704
32896
|
import * as path23 from "node:path";
|
|
32705
32897
|
|
|
32706
|
-
// src/git/
|
|
32707
|
-
function
|
|
32708
|
-
const
|
|
32709
|
-
for (const line of
|
|
32710
|
-
|
|
32711
|
-
|
|
32712
|
-
|
|
32713
|
-
|
|
32714
|
-
const
|
|
32715
|
-
|
|
32716
|
-
|
|
32717
|
-
|
|
32718
|
-
|
|
32719
|
-
} else if (code === "R" || code === "C") {
|
|
32720
|
-
if (tabParts.length >= 3) m.set(tabParts[tabParts.length - 1], "modified");
|
|
32721
|
-
} else if (code === "M" || code === "U" || code === "T") {
|
|
32722
|
-
m.set(tabParts[tabParts.length - 1], "modified");
|
|
32723
|
-
}
|
|
32898
|
+
// src/git/commits/lib/parse-log-lines.ts
|
|
32899
|
+
function parseLogShaDateSubjectLines(raw) {
|
|
32900
|
+
const out = [];
|
|
32901
|
+
for (const line of String(raw).split("\n")) {
|
|
32902
|
+
const l = line.trimEnd();
|
|
32903
|
+
if (!l.trim()) continue;
|
|
32904
|
+
const parts = l.split(" ");
|
|
32905
|
+
if (parts.length < 3) continue;
|
|
32906
|
+
const sha = parts[0].trim();
|
|
32907
|
+
const committedAt = parts[1].trim();
|
|
32908
|
+
const subject = parts.slice(2).join(" ").trim();
|
|
32909
|
+
if (!/^[0-9a-f]{7,40}$/i.test(sha)) continue;
|
|
32910
|
+
out.push({ sha, shortSha: sha.slice(0, 7), subject, committedAt });
|
|
32724
32911
|
}
|
|
32725
|
-
return
|
|
32726
|
-
}
|
|
32727
|
-
function parseNumstatFirstLine(line) {
|
|
32728
|
-
const parts = line.split(" ");
|
|
32729
|
-
if (parts.length < 3) return null;
|
|
32730
|
-
const [a, d] = parts;
|
|
32731
|
-
const additions = a === "-" ? 0 : parseInt(String(a), 10) || 0;
|
|
32732
|
-
const deletions = d === "-" ? 0 : parseInt(String(d), 10) || 0;
|
|
32733
|
-
return { additions, deletions };
|
|
32912
|
+
return out;
|
|
32734
32913
|
}
|
|
32735
|
-
|
|
32736
|
-
|
|
32737
|
-
|
|
32738
|
-
|
|
32739
|
-
|
|
32740
|
-
|
|
32741
|
-
|
|
32742
|
-
|
|
32743
|
-
|
|
32744
|
-
m.set(p, { additions, deletions });
|
|
32914
|
+
|
|
32915
|
+
// src/git/commits/list-unpushed-commits.ts
|
|
32916
|
+
async function gitLogNotReachableFromBase(g, baseSha, headSha) {
|
|
32917
|
+
if (baseSha === headSha) return [];
|
|
32918
|
+
try {
|
|
32919
|
+
const logOut = await g.raw(["log", "--format=%H %cI %s", `${baseSha}..${headSha}`]);
|
|
32920
|
+
return parseLogShaDateSubjectLines(logOut);
|
|
32921
|
+
} catch {
|
|
32922
|
+
return [];
|
|
32745
32923
|
}
|
|
32746
|
-
return m;
|
|
32747
32924
|
}
|
|
32748
|
-
async function
|
|
32749
|
-
const
|
|
32925
|
+
async function listUnpushedCommits(repoDir) {
|
|
32926
|
+
const g = cliSimpleGit(repoDir);
|
|
32927
|
+
const headSha = await revParseSafe(g, "HEAD");
|
|
32928
|
+
if (!headSha) return [];
|
|
32929
|
+
const baseSha = await resolveBaseShaForUnpushedCommits(g);
|
|
32930
|
+
if (!baseSha) return [];
|
|
32931
|
+
return gitLogNotReachableFromBase(g, baseSha, headSha);
|
|
32932
|
+
}
|
|
32933
|
+
|
|
32934
|
+
// src/git/commits/lib/sanitize-recent-commits-limit.ts
|
|
32935
|
+
var RECENT_COMMITS_LIMIT_MIN = 1;
|
|
32936
|
+
var RECENT_COMMITS_LIMIT_MAX = 50;
|
|
32937
|
+
var RECENT_COMMITS_LIMIT_DEFAULT = 2;
|
|
32938
|
+
function sanitizeRecentCommitsLimit(value) {
|
|
32939
|
+
const n = typeof value === "number" ? value : typeof value === "string" ? Number.parseInt(value.trim(), 10) : Number.NaN;
|
|
32940
|
+
if (!Number.isFinite(n)) return RECENT_COMMITS_LIMIT_DEFAULT;
|
|
32941
|
+
return Math.min(RECENT_COMMITS_LIMIT_MAX, Math.max(RECENT_COMMITS_LIMIT_MIN, Math.trunc(n)));
|
|
32942
|
+
}
|
|
32943
|
+
|
|
32944
|
+
// src/git/commits/list-recent-commits.ts
|
|
32945
|
+
async function listRecentCommits(repoDir, limitInput) {
|
|
32946
|
+
const limit = sanitizeRecentCommitsLimit(limitInput);
|
|
32947
|
+
const g = cliSimpleGit(repoDir);
|
|
32948
|
+
const headSha = await revParseSafe(g, "HEAD");
|
|
32949
|
+
if (!headSha) return { commits: [], hasMore: false };
|
|
32950
|
+
const unpushedSet = new Set((await listUnpushedCommits(repoDir)).map((c) => c.sha));
|
|
32750
32951
|
try {
|
|
32751
|
-
const
|
|
32752
|
-
const
|
|
32753
|
-
|
|
32952
|
+
const logOut = await g.raw(["log", "--format=%H %cI %s", `-n`, String(limit), "HEAD"]);
|
|
32953
|
+
const commits = parseLogShaDateSubjectLines(logOut).map((c) => ({
|
|
32954
|
+
...c,
|
|
32955
|
+
needsPush: unpushedSet.has(c.sha)
|
|
32956
|
+
}));
|
|
32957
|
+
let hasMore = false;
|
|
32958
|
+
if (commits.length === limit) {
|
|
32959
|
+
const nextOut = await g.raw(["log", "-n", "1", `--skip=${limit}`, "--format=%H", "HEAD"]);
|
|
32960
|
+
hasMore = /^[0-9a-f]{7,40}$/i.test(String(nextOut).trim());
|
|
32961
|
+
}
|
|
32962
|
+
return { commits, hasMore };
|
|
32754
32963
|
} catch {
|
|
32755
|
-
return
|
|
32964
|
+
return { commits: [], hasMore: false };
|
|
32756
32965
|
}
|
|
32757
32966
|
}
|
|
32758
32967
|
|
|
32759
|
-
// src/git/
|
|
32968
|
+
// src/git/changes/lib/patch-truncate.ts
|
|
32760
32969
|
function truncatePatch(s) {
|
|
32761
32970
|
if (s.length <= MAX_PATCH_CHARS) return s;
|
|
32762
32971
|
return `${s.slice(0, MAX_PATCH_CHARS)}
|
|
@@ -32764,7 +32973,7 @@ function truncatePatch(s) {
|
|
|
32764
32973
|
\u2026 (diff truncated)`;
|
|
32765
32974
|
}
|
|
32766
32975
|
|
|
32767
|
-
// src/git/
|
|
32976
|
+
// src/git/commits/list-changed-files-for-commit.ts
|
|
32768
32977
|
var EMPTY_TREE = "4b825dc642cb6eb9a060e54bf8d69288fbee4904";
|
|
32769
32978
|
async function parentForCommitDiff(g, sha) {
|
|
32770
32979
|
try {
|
|
@@ -32790,7 +32999,7 @@ async function listChangedFilesForCommit(repoGitCwd, repoRelPath, commitSha) {
|
|
|
32790
32999
|
const paths = new Set([...kindByPath.keys(), ...numByPath.keys()].filter(Boolean));
|
|
32791
33000
|
const rows = [];
|
|
32792
33001
|
const normRel = repoRelPath === "." || repoRelPath === "" ? "." : repoRelPath;
|
|
32793
|
-
|
|
33002
|
+
await forEachWithGitYield([...paths], async (pathInRepo) => {
|
|
32794
33003
|
const relLauncher = posixJoinDirFile(normRel, pathInRepo.replace(/\\/g, "/"));
|
|
32795
33004
|
const nums = numByPath.get(pathInRepo);
|
|
32796
33005
|
let additions = nums?.additions ?? 0;
|
|
@@ -32802,8 +33011,8 @@ async function listChangedFilesForCommit(repoGitCwd, repoRelPath, commitSha) {
|
|
|
32802
33011
|
else change = "modified";
|
|
32803
33012
|
}
|
|
32804
33013
|
rows.push({ pathRelLauncher: relLauncher, additions, deletions, change });
|
|
32805
|
-
}
|
|
32806
|
-
|
|
33014
|
+
});
|
|
33015
|
+
await forEachWithGitYield(rows, async (row) => {
|
|
32807
33016
|
let pathInRepo;
|
|
32808
33017
|
if (normRel === ".") {
|
|
32809
33018
|
pathInRepo = row.pathRelLauncher;
|
|
@@ -32815,16 +33024,16 @@ async function listChangedFilesForCommit(repoGitCwd, repoRelPath, commitSha) {
|
|
|
32815
33024
|
const raw = await g.raw(["diff", "-U20000", range, "--", pathInRepo]).catch(() => "");
|
|
32816
33025
|
const t = String(raw).trim();
|
|
32817
33026
|
row.patchContent = t ? truncatePatch(t) : void 0;
|
|
32818
|
-
}
|
|
33027
|
+
});
|
|
32819
33028
|
rows.sort((a, b) => a.pathRelLauncher.localeCompare(b.pathRelLauncher));
|
|
32820
33029
|
return rows;
|
|
32821
33030
|
}
|
|
32822
33031
|
|
|
32823
|
-
// src/git/
|
|
33032
|
+
// src/git/changes/list-changed-files-for-repo.ts
|
|
32824
33033
|
import * as fs22 from "node:fs";
|
|
32825
33034
|
import * as path22 from "node:path";
|
|
32826
33035
|
|
|
32827
|
-
// src/git/
|
|
33036
|
+
// src/git/changes/lib/count-lines.ts
|
|
32828
33037
|
import { createReadStream } from "node:fs";
|
|
32829
33038
|
import * as readline2 from "node:readline";
|
|
32830
33039
|
async function countTextFileLines(filePath) {
|
|
@@ -32845,7 +33054,7 @@ async function countTextFileLines(filePath) {
|
|
|
32845
33054
|
return lines;
|
|
32846
33055
|
}
|
|
32847
33056
|
|
|
32848
|
-
// src/git/
|
|
33057
|
+
// src/git/changes/hydrate-patch.ts
|
|
32849
33058
|
import * as fs21 from "node:fs";
|
|
32850
33059
|
var UNIFIED_HUNK_HEADER_RE = /^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/;
|
|
32851
33060
|
var MAX_HYDRATE_LINES_PER_GAP = 8e3;
|
|
@@ -32961,26 +33170,16 @@ async function hydrateUnifiedPatchWithFileContext(patch, filePath, repoGitCwd, p
|
|
|
32961
33170
|
return truncatePatch(out.join("\n"));
|
|
32962
33171
|
}
|
|
32963
33172
|
|
|
32964
|
-
// src/git/
|
|
33173
|
+
// src/git/changes/unified-diff-for-file.ts
|
|
32965
33174
|
async function unifiedDiffForFile(repoCwd, pathInRepo, change) {
|
|
32966
33175
|
const g = cliSimpleGit(repoCwd);
|
|
32967
|
-
|
|
32968
|
-
|
|
32969
|
-
|
|
32970
|
-
|
|
32971
|
-
raw = await g.raw(["diff", "--no-index", "--", devNull, pathInRepo]);
|
|
32972
|
-
} else {
|
|
32973
|
-
raw = await g.raw(["diff", "HEAD", "--", pathInRepo]);
|
|
32974
|
-
}
|
|
32975
|
-
const t = String(raw).trim();
|
|
32976
|
-
if (!t) return void 0;
|
|
32977
|
-
return truncatePatch(t);
|
|
32978
|
-
} catch {
|
|
32979
|
-
return void 0;
|
|
32980
|
-
}
|
|
33176
|
+
const args = change === "added" ? ["diff", "--no-color", "HEAD", "--", pathInRepo] : change === "removed" ? ["diff", "--no-color", "HEAD", "--", pathInRepo] : ["diff", "--no-color", "HEAD", "--", pathInRepo];
|
|
33177
|
+
const raw = await g.raw([...args]).catch(() => "");
|
|
33178
|
+
const t = String(raw).trim();
|
|
33179
|
+
return t ? truncatePatch(t) : void 0;
|
|
32981
33180
|
}
|
|
32982
33181
|
|
|
32983
|
-
// src/git/
|
|
33182
|
+
// src/git/changes/list-changed-files-for-repo.ts
|
|
32984
33183
|
async function listChangedFilesForRepo(repoGitCwd, repoRelPath) {
|
|
32985
33184
|
const g = cliSimpleGit(repoGitCwd);
|
|
32986
33185
|
const [nameStatusRaw, numstatRaw, untrackedRaw] = await Promise.all([
|
|
@@ -32994,7 +33193,7 @@ async function listChangedFilesForRepo(repoGitCwd, repoRelPath) {
|
|
|
32994
33193
|
const untracked = String(untrackedRaw).split("\n").map((s) => s.trim()).filter(Boolean);
|
|
32995
33194
|
for (const p of untracked) paths.add(p);
|
|
32996
33195
|
const rows = [];
|
|
32997
|
-
|
|
33196
|
+
await forEachWithGitYield([...paths], async (pathInRepo) => {
|
|
32998
33197
|
const relLauncher = posixJoinDirFile(repoRelPath, pathInRepo.replace(/\\/g, "/"));
|
|
32999
33198
|
const repoFilePath = path22.join(repoGitCwd, pathInRepo);
|
|
33000
33199
|
const nums = numByPath.get(pathInRepo);
|
|
@@ -33024,9 +33223,9 @@ async function listChangedFilesForRepo(repoGitCwd, repoRelPath) {
|
|
|
33024
33223
|
else change = "modified";
|
|
33025
33224
|
}
|
|
33026
33225
|
rows.push({ pathRelLauncher: relLauncher, additions, deletions, change });
|
|
33027
|
-
}
|
|
33226
|
+
});
|
|
33028
33227
|
const normRel = repoRelPath === "." || repoRelPath === "" ? "." : repoRelPath;
|
|
33029
|
-
|
|
33228
|
+
await forEachWithGitYield(rows, async (row) => {
|
|
33030
33229
|
let pathInRepo;
|
|
33031
33230
|
if (normRel === ".") {
|
|
33032
33231
|
pathInRepo = row.pathRelLauncher;
|
|
@@ -33041,11 +33240,11 @@ async function listChangedFilesForRepo(repoGitCwd, repoRelPath) {
|
|
|
33041
33240
|
patch = await hydrateUnifiedPatchWithFileContext(patch, filePath, repoGitCwd, pathInRepo, row.change);
|
|
33042
33241
|
}
|
|
33043
33242
|
row.patchContent = patch;
|
|
33044
|
-
}
|
|
33243
|
+
});
|
|
33045
33244
|
return rows;
|
|
33046
33245
|
}
|
|
33047
33246
|
|
|
33048
|
-
// src/git/
|
|
33247
|
+
// src/git/changes/get-working-tree-change-repo-details.ts
|
|
33049
33248
|
function normRepoRel(p) {
|
|
33050
33249
|
const x = p.replace(/\\/g, "/").trim();
|
|
33051
33250
|
return x === "" ? "." : x;
|
|
@@ -33064,7 +33263,9 @@ async function getWorkingTreeChangeRepoDetails(options) {
|
|
|
33064
33263
|
throw new Error("commit sha is required for commit changes");
|
|
33065
33264
|
}
|
|
33066
33265
|
const basis = filter == null && basisInput.kind === "commit" ? { kind: "working" } : basisInput;
|
|
33067
|
-
for (
|
|
33266
|
+
for (let i = 0; i < options.commitTargetPaths.length; i++) {
|
|
33267
|
+
if (i > 0) await yieldToEventLoop2();
|
|
33268
|
+
const target = options.commitTargetPaths[i];
|
|
33068
33269
|
const t = path23.resolve(target);
|
|
33069
33270
|
if (!await isGitRepoDirectory(t)) continue;
|
|
33070
33271
|
const g = cliSimpleGit(t);
|
|
@@ -33098,7 +33299,10 @@ async function getWorkingTreeChangeRepoDetails(options) {
|
|
|
33098
33299
|
const files = basis.kind === "commit" ? await listChangedFilesForCommit(t, relForList, basis.sha.trim()) : await listChangedFilesForRepo(t, relForList);
|
|
33099
33300
|
const st = await g.status();
|
|
33100
33301
|
const hasUncommittedChanges = (st.files?.length ?? 0) > 0;
|
|
33101
|
-
const unpushedCommits = await
|
|
33302
|
+
const [unpushedCommits, recentCommitList] = await Promise.all([
|
|
33303
|
+
listUnpushedCommits(t),
|
|
33304
|
+
listRecentCommits(t, options.recentCommitsLimit)
|
|
33305
|
+
]);
|
|
33102
33306
|
out.push({
|
|
33103
33307
|
repoRelPath: norm,
|
|
33104
33308
|
repoDisplayName,
|
|
@@ -33108,6 +33312,8 @@ async function getWorkingTreeChangeRepoDetails(options) {
|
|
|
33108
33312
|
files,
|
|
33109
33313
|
hasUncommittedChanges,
|
|
33110
33314
|
unpushedCommits,
|
|
33315
|
+
recentCommits: recentCommitList.commits,
|
|
33316
|
+
recentCommitsHasMore: recentCommitList.hasMore,
|
|
33111
33317
|
changesView: basis.kind === "commit" ? "commit" : "working",
|
|
33112
33318
|
changesCommitSha: basis.kind === "commit" ? basis.sha.trim() : null
|
|
33113
33319
|
});
|
|
@@ -33116,7 +33322,7 @@ async function getWorkingTreeChangeRepoDetails(options) {
|
|
|
33116
33322
|
return out;
|
|
33117
33323
|
}
|
|
33118
33324
|
|
|
33119
|
-
// src/git/commit-and-push.ts
|
|
33325
|
+
// src/git/branches/commit-and-push.ts
|
|
33120
33326
|
async function gitCommitAllIfDirty(repoDir, message, options) {
|
|
33121
33327
|
const g = cliSimpleGit(repoDir);
|
|
33122
33328
|
const st = await g.status();
|
|
@@ -33540,7 +33746,8 @@ var SessionWorktreeManager = class {
|
|
|
33540
33746
|
sessionWorktreeRootPath: sessionWorkingTreeRelRoot,
|
|
33541
33747
|
legacyRepoNestedSessionLayout: legacyNested,
|
|
33542
33748
|
repoFilterRelPath: opts?.repoRelPath?.trim() ? opts.repoRelPath.trim() : null,
|
|
33543
|
-
basis: opts?.basis
|
|
33749
|
+
basis: opts?.basis,
|
|
33750
|
+
recentCommitsLimit: opts?.recentCommitsLimit
|
|
33544
33751
|
});
|
|
33545
33752
|
}
|
|
33546
33753
|
async pushSessionUpstream(sessionId) {
|
|
@@ -33577,30 +33784,39 @@ import path27 from "node:path";
|
|
|
33577
33784
|
function shouldSkipWorkspaceWalkEntry(name) {
|
|
33578
33785
|
return name.startsWith(".");
|
|
33579
33786
|
}
|
|
33580
|
-
function
|
|
33787
|
+
async function walkWorkspaceTreeAsync(dir, baseDir, onFile, state) {
|
|
33581
33788
|
let names;
|
|
33582
33789
|
try {
|
|
33583
|
-
names = fs24.
|
|
33790
|
+
names = await fs24.promises.readdir(dir);
|
|
33584
33791
|
} catch {
|
|
33585
33792
|
return;
|
|
33586
33793
|
}
|
|
33587
33794
|
for (const name of names) {
|
|
33588
33795
|
if (shouldSkipWorkspaceWalkEntry(name)) continue;
|
|
33796
|
+
if (state.n > 0 && state.n % INDEX_WORK_YIELD_EVERY === 0) {
|
|
33797
|
+
if (isCliImmediateShutdownRequested()) throw new CliSqliteInterrupted();
|
|
33798
|
+
await yieldToEventLoop();
|
|
33799
|
+
if (isCliImmediateShutdownRequested()) throw new CliSqliteInterrupted();
|
|
33800
|
+
}
|
|
33801
|
+
state.n++;
|
|
33589
33802
|
const full = path27.join(dir, name);
|
|
33590
33803
|
let stat2;
|
|
33591
33804
|
try {
|
|
33592
|
-
stat2 = fs24.
|
|
33805
|
+
stat2 = await fs24.promises.stat(full);
|
|
33593
33806
|
} catch {
|
|
33594
33807
|
continue;
|
|
33595
33808
|
}
|
|
33596
33809
|
const relative5 = path27.relative(baseDir, full).replace(/\\/g, "/");
|
|
33597
33810
|
if (stat2.isDirectory()) {
|
|
33598
|
-
|
|
33811
|
+
await walkWorkspaceTreeAsync(full, baseDir, onFile, state);
|
|
33599
33812
|
} else if (stat2.isFile()) {
|
|
33600
33813
|
onFile(relative5);
|
|
33601
33814
|
}
|
|
33602
33815
|
}
|
|
33603
33816
|
}
|
|
33817
|
+
function createWalkYieldState() {
|
|
33818
|
+
return { n: 0 };
|
|
33819
|
+
}
|
|
33604
33820
|
|
|
33605
33821
|
// src/files/index/file-index-sqlite-lock.ts
|
|
33606
33822
|
import fs25 from "node:fs";
|
|
@@ -33633,29 +33849,28 @@ function withFileIndexSqliteLock(fn) {
|
|
|
33633
33849
|
}
|
|
33634
33850
|
|
|
33635
33851
|
// src/files/index/build-file-index.ts
|
|
33636
|
-
var
|
|
33637
|
-
function
|
|
33852
|
+
var FILE_INDEX_INTERRUPT_CHECK_EVERY = 256;
|
|
33853
|
+
function assertNotShutdown() {
|
|
33854
|
+
if (isCliImmediateShutdownRequested()) throw new CliSqliteInterrupted();
|
|
33855
|
+
}
|
|
33856
|
+
function persistFileIndexPaths(resolved, paths) {
|
|
33638
33857
|
return withCliSqliteSync((db) => {
|
|
33639
33858
|
const h = getCwdHashForFileIndex(resolved);
|
|
33640
|
-
const buf = [];
|
|
33641
33859
|
let pathCount = 0;
|
|
33642
33860
|
db.run("BEGIN IMMEDIATE");
|
|
33643
33861
|
try {
|
|
33644
33862
|
db.run("DELETE FROM file_index_path WHERE cwd_hash = ?", [h]);
|
|
33645
33863
|
const ins = db.prepare("INSERT INTO file_index_path (cwd_hash, path) VALUES (?, ?)");
|
|
33646
33864
|
try {
|
|
33647
|
-
|
|
33648
|
-
|
|
33649
|
-
|
|
33865
|
+
let batch = 0;
|
|
33866
|
+
for (const rel of paths) {
|
|
33867
|
+
if (++batch >= FILE_INDEX_INTERRUPT_CHECK_EVERY) {
|
|
33868
|
+
batch = 0;
|
|
33869
|
+
assertNotShutdown();
|
|
33650
33870
|
}
|
|
33651
|
-
|
|
33652
|
-
|
|
33653
|
-
}
|
|
33654
|
-
walkWorkspaceTreeSync(resolved, resolved, (rel) => {
|
|
33655
|
-
buf.push(rel);
|
|
33656
|
-
if (buf.length >= FILE_INDEX_INSERT_BUFFER) flushBuf();
|
|
33657
|
-
});
|
|
33658
|
-
flushBuf();
|
|
33871
|
+
ins.run([h, rel]);
|
|
33872
|
+
pathCount += 1;
|
|
33873
|
+
}
|
|
33659
33874
|
} finally {
|
|
33660
33875
|
ins.finalize();
|
|
33661
33876
|
}
|
|
@@ -33670,12 +33885,24 @@ function persistFileIndexForResolvedCwd(resolved) {
|
|
|
33670
33885
|
return pathCount;
|
|
33671
33886
|
});
|
|
33672
33887
|
}
|
|
33888
|
+
async function collectWorkspacePathsAsync(resolved) {
|
|
33889
|
+
const paths = [];
|
|
33890
|
+
const state = createWalkYieldState();
|
|
33891
|
+
await walkWorkspaceTreeAsync(resolved, resolved, (rel) => {
|
|
33892
|
+
assertNotShutdown();
|
|
33893
|
+
paths.push(rel);
|
|
33894
|
+
}, state);
|
|
33895
|
+
return paths;
|
|
33896
|
+
}
|
|
33673
33897
|
async function buildFileIndexAsync(cwd) {
|
|
33674
33898
|
return withFileIndexSqliteLock(async () => {
|
|
33675
33899
|
const resolved = path28.resolve(cwd);
|
|
33676
33900
|
await yieldToEventLoop();
|
|
33677
|
-
|
|
33901
|
+
assertNotShutdown();
|
|
33902
|
+
const paths = await collectWorkspacePathsAsync(resolved);
|
|
33678
33903
|
await yieldToEventLoop();
|
|
33904
|
+
assertNotShutdown();
|
|
33905
|
+
const pathCount = persistFileIndexPaths(resolved, paths);
|
|
33679
33906
|
return { pathCount };
|
|
33680
33907
|
});
|
|
33681
33908
|
}
|
|
@@ -33779,11 +34006,13 @@ function createFsWatcher(resolved, schedule) {
|
|
|
33779
34006
|
function startFileIndexWatcher(cwd = getBridgeRoot()) {
|
|
33780
34007
|
const resolved = path30.resolve(cwd);
|
|
33781
34008
|
void buildFileIndexAsync(resolved).catch((e) => {
|
|
34009
|
+
if (e instanceof CliSqliteInterrupted) return;
|
|
33782
34010
|
console.error("[file-index] Initial index build failed:", e);
|
|
33783
34011
|
});
|
|
33784
34012
|
let timer = null;
|
|
33785
34013
|
const runRebuild = () => {
|
|
33786
34014
|
void buildFileIndexAsync(resolved).catch((e) => {
|
|
34015
|
+
if (e instanceof CliSqliteInterrupted) return;
|
|
33787
34016
|
console.error("[file-index] Watch rebuild failed:", e);
|
|
33788
34017
|
});
|
|
33789
34018
|
};
|
|
@@ -34048,7 +34277,7 @@ function pipedStdoutStderrFor(attemptStdio) {
|
|
|
34048
34277
|
}
|
|
34049
34278
|
|
|
34050
34279
|
// src/dev-servers/manager/shell-spawn/try-spawn-piped-via-sh.ts
|
|
34051
|
-
import { spawn as
|
|
34280
|
+
import { spawn as spawn6 } from "node:child_process";
|
|
34052
34281
|
function trySpawnPipedViaSh(command, env, cwd, signal) {
|
|
34053
34282
|
const attempts = [
|
|
34054
34283
|
{ stdio: [devNullReadFd(), "pipe", "pipe"], endStdin: false },
|
|
@@ -34069,9 +34298,9 @@ function trySpawnPipedViaSh(command, env, cwd, signal) {
|
|
|
34069
34298
|
if (process.platform === "win32") {
|
|
34070
34299
|
opts.windowsHide = true;
|
|
34071
34300
|
const com = process.env.ComSpec || "cmd.exe";
|
|
34072
|
-
proc =
|
|
34301
|
+
proc = spawn6(com, ["/d", "/s", "/c", command], opts);
|
|
34073
34302
|
} else {
|
|
34074
|
-
proc =
|
|
34303
|
+
proc = spawn6("/bin/sh", ["-c", command], opts);
|
|
34075
34304
|
}
|
|
34076
34305
|
if (attempt.endStdin) {
|
|
34077
34306
|
proc.stdin?.end();
|
|
@@ -34091,7 +34320,7 @@ function trySpawnPipedViaSh(command, env, cwd, signal) {
|
|
|
34091
34320
|
}
|
|
34092
34321
|
|
|
34093
34322
|
// src/dev-servers/manager/shell-spawn/try-spawn-shell-true-piped.ts
|
|
34094
|
-
import { spawn as
|
|
34323
|
+
import { spawn as spawn7 } from "node:child_process";
|
|
34095
34324
|
function trySpawnShellTruePiped(command, env, cwd, devNullFd, signal) {
|
|
34096
34325
|
try {
|
|
34097
34326
|
const opts = {
|
|
@@ -34104,7 +34333,7 @@ function trySpawnShellTruePiped(command, env, cwd, devNullFd, signal) {
|
|
|
34104
34333
|
if (process.platform === "win32") {
|
|
34105
34334
|
opts.windowsHide = true;
|
|
34106
34335
|
}
|
|
34107
|
-
return
|
|
34336
|
+
return spawn7(command, opts);
|
|
34108
34337
|
} catch (e) {
|
|
34109
34338
|
if (isSpawnEbadf(e)) return null;
|
|
34110
34339
|
throw e;
|
|
@@ -34112,7 +34341,7 @@ function trySpawnShellTruePiped(command, env, cwd, devNullFd, signal) {
|
|
|
34112
34341
|
}
|
|
34113
34342
|
|
|
34114
34343
|
// src/dev-servers/manager/shell-spawn/try-spawn-merged-log-file.ts
|
|
34115
|
-
import { spawn as
|
|
34344
|
+
import { spawn as spawn8 } from "node:child_process";
|
|
34116
34345
|
import fs28 from "node:fs";
|
|
34117
34346
|
import { tmpdir } from "node:os";
|
|
34118
34347
|
import path31 from "node:path";
|
|
@@ -34130,7 +34359,7 @@ function trySpawnMergedLogFile(command, env, cwd, signal) {
|
|
|
34130
34359
|
try {
|
|
34131
34360
|
let proc;
|
|
34132
34361
|
if (process.platform === "win32") {
|
|
34133
|
-
proc =
|
|
34362
|
+
proc = spawn8(process.env.ComSpec || "cmd.exe", ["/d", "/s", "/c", command], {
|
|
34134
34363
|
env,
|
|
34135
34364
|
cwd,
|
|
34136
34365
|
stdio,
|
|
@@ -34138,7 +34367,7 @@ function trySpawnMergedLogFile(command, env, cwd, signal) {
|
|
|
34138
34367
|
...signal ? { signal } : {}
|
|
34139
34368
|
});
|
|
34140
34369
|
} else {
|
|
34141
|
-
proc =
|
|
34370
|
+
proc = spawn8("/bin/sh", ["-c", command], { env, cwd, stdio, ...signal ? { signal } : {} });
|
|
34142
34371
|
}
|
|
34143
34372
|
fs28.closeSync(logFd);
|
|
34144
34373
|
return {
|
|
@@ -34159,7 +34388,7 @@ function trySpawnMergedLogFile(command, env, cwd, signal) {
|
|
|
34159
34388
|
}
|
|
34160
34389
|
|
|
34161
34390
|
// src/dev-servers/manager/shell-spawn/try-spawn-shell-script-log-redirect.ts
|
|
34162
|
-
import { spawn as
|
|
34391
|
+
import { spawn as spawn9 } from "node:child_process";
|
|
34163
34392
|
import fs29 from "node:fs";
|
|
34164
34393
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
34165
34394
|
import path32 from "node:path";
|
|
@@ -34182,7 +34411,7 @@ cd ${shSingleQuote(cwd)}
|
|
|
34182
34411
|
/bin/sh ${shSingleQuote(innerPath)} >>${shSingleQuote(logPath)} 2>&1
|
|
34183
34412
|
`
|
|
34184
34413
|
);
|
|
34185
|
-
const proc =
|
|
34414
|
+
const proc = spawn9("/bin/sh", [runnerPath], {
|
|
34186
34415
|
env,
|
|
34187
34416
|
cwd: tmpRoot,
|
|
34188
34417
|
stdio: "ignore",
|
|
@@ -34214,7 +34443,7 @@ CD /D ${q(cwd)}\r
|
|
|
34214
34443
|
${command} >> ${q(logPath)} 2>&1\r
|
|
34215
34444
|
`
|
|
34216
34445
|
);
|
|
34217
|
-
const proc =
|
|
34446
|
+
const proc = spawn9(com, ["/d", "/s", "/c", q(runnerPath)], {
|
|
34218
34447
|
env,
|
|
34219
34448
|
cwd: tmpRoot,
|
|
34220
34449
|
stdio: "ignore",
|
|
@@ -34235,7 +34464,7 @@ ${command} >> ${q(logPath)} 2>&1\r
|
|
|
34235
34464
|
}
|
|
34236
34465
|
|
|
34237
34466
|
// src/dev-servers/manager/shell-spawn/try-spawn-inherit.ts
|
|
34238
|
-
import { spawn as
|
|
34467
|
+
import { spawn as spawn10 } from "node:child_process";
|
|
34239
34468
|
function trySpawnInheritStdio(command, env, cwd, signal) {
|
|
34240
34469
|
const opts = {
|
|
34241
34470
|
env,
|
|
@@ -34247,9 +34476,9 @@ function trySpawnInheritStdio(command, env, cwd, signal) {
|
|
|
34247
34476
|
if (process.platform === "win32") {
|
|
34248
34477
|
opts.windowsHide = true;
|
|
34249
34478
|
const com = process.env.ComSpec || "cmd.exe";
|
|
34250
|
-
proc =
|
|
34479
|
+
proc = spawn10(com, ["/d", "/s", "/c", command], opts);
|
|
34251
34480
|
} else {
|
|
34252
|
-
proc =
|
|
34481
|
+
proc = spawn10("/bin/sh", ["-c", command], opts);
|
|
34253
34482
|
}
|
|
34254
34483
|
return { proc, pipedStdoutStderr: false };
|
|
34255
34484
|
}
|
|
@@ -34317,9 +34546,6 @@ var StreamTail = class {
|
|
|
34317
34546
|
}
|
|
34318
34547
|
};
|
|
34319
34548
|
|
|
34320
|
-
// src/dev-servers/manager/dev-server-constants.ts
|
|
34321
|
-
var BRIDGE_SHUTDOWN_GRACE_MS = 8e3;
|
|
34322
|
-
|
|
34323
34549
|
// src/dev-servers/manager/dev-server-firehose-messages.ts
|
|
34324
34550
|
function buildFirehoseSnapshotMessage(params) {
|
|
34325
34551
|
const payload = {
|
|
@@ -34605,22 +34831,23 @@ var DevServerManager = class {
|
|
|
34605
34831
|
}
|
|
34606
34832
|
this.start(serverId);
|
|
34607
34833
|
}
|
|
34608
|
-
async shutdownAllGraceful() {
|
|
34834
|
+
async shutdownAllGraceful(opts) {
|
|
34835
|
+
const graceMs = opts?.graceMs ?? BRIDGE_SHUTDOWN_GRACE_MS;
|
|
34609
34836
|
const pairs = [...this.processes.entries()];
|
|
34610
34837
|
if (pairs.length === 0) return;
|
|
34611
34838
|
this.log(
|
|
34612
34839
|
`[dev-server] Stopping ${pairs.length} local dev server process${pairs.length === 1 ? "" : "es"}\u2026`
|
|
34613
34840
|
);
|
|
34614
|
-
await Promise.all(pairs.map(([serverId, proc]) => this.gracefulTerminateOrUnknown(serverId, proc)));
|
|
34841
|
+
await Promise.all(pairs.map(([serverId, proc]) => this.gracefulTerminateOrUnknown(serverId, proc, graceMs)));
|
|
34615
34842
|
}
|
|
34616
|
-
async gracefulTerminateOrUnknown(serverId, proc) {
|
|
34843
|
+
async gracefulTerminateOrUnknown(serverId, proc, graceMs) {
|
|
34617
34844
|
const shortId = `${serverId.slice(0, 8)}\u2026`;
|
|
34618
|
-
await sigtermAndWaitForExit(proc,
|
|
34845
|
+
await sigtermAndWaitForExit(proc, graceMs, this.log, shortId);
|
|
34619
34846
|
if (!this.processes.has(serverId) || this.processes.get(serverId) !== proc) {
|
|
34620
34847
|
return;
|
|
34621
34848
|
}
|
|
34622
34849
|
this.bumpGeneration(serverId);
|
|
34623
|
-
forceKillChild(proc, this.log, shortId,
|
|
34850
|
+
forceKillChild(proc, this.log, shortId, graceMs);
|
|
34624
34851
|
this.processes.delete(serverId);
|
|
34625
34852
|
this.clearPoll(serverId);
|
|
34626
34853
|
this.pipedCaptureByServerId.delete(serverId);
|
|
@@ -35047,10 +35274,13 @@ var LOCAL_AGENT_ACP_MODULES = [
|
|
|
35047
35274
|
];
|
|
35048
35275
|
async function detectLocalAgentTypes() {
|
|
35049
35276
|
try {
|
|
35277
|
+
if (isCliImmediateShutdownRequested()) return [];
|
|
35050
35278
|
const out = [];
|
|
35051
35279
|
for (let i = 0; i < LOCAL_AGENT_ACP_MODULES.length; i++) {
|
|
35280
|
+
if (isCliImmediateShutdownRequested()) return out;
|
|
35052
35281
|
if (i > 0) {
|
|
35053
35282
|
await yieldToEventLoop();
|
|
35283
|
+
if (isCliImmediateShutdownRequested()) return out;
|
|
35054
35284
|
}
|
|
35055
35285
|
const mod = LOCAL_AGENT_ACP_MODULES[i];
|
|
35056
35286
|
try {
|
|
@@ -35083,6 +35313,7 @@ function createSendLocalSkillsReport(getWs, logFn) {
|
|
|
35083
35313
|
}
|
|
35084
35314
|
function createReportAutoDetectedAgents(getWs, logFn) {
|
|
35085
35315
|
return async () => {
|
|
35316
|
+
if (isCliImmediateShutdownRequested()) return;
|
|
35086
35317
|
try {
|
|
35087
35318
|
const types = await detectLocalAgentTypes();
|
|
35088
35319
|
const socket = getWs();
|
|
@@ -35182,6 +35413,7 @@ var handleBridgeIdentified = (msg, deps) => {
|
|
|
35182
35413
|
});
|
|
35183
35414
|
setImmediate(() => {
|
|
35184
35415
|
void (async () => {
|
|
35416
|
+
if (isCliImmediateShutdownRequested()) return;
|
|
35185
35417
|
try {
|
|
35186
35418
|
await deps.reportAutoDetectedAgents?.();
|
|
35187
35419
|
} catch (e) {
|
|
@@ -35189,6 +35421,7 @@ var handleBridgeIdentified = (msg, deps) => {
|
|
|
35189
35421
|
`[Bridge service] Auto-detect agents failed: ${e instanceof Error ? e.message : String(e)}`
|
|
35190
35422
|
);
|
|
35191
35423
|
}
|
|
35424
|
+
if (isCliImmediateShutdownRequested()) return;
|
|
35192
35425
|
try {
|
|
35193
35426
|
await deps.warmupAgentCapabilitiesOnConnect?.();
|
|
35194
35427
|
} catch (e) {
|
|
@@ -35199,6 +35432,7 @@ var handleBridgeIdentified = (msg, deps) => {
|
|
|
35199
35432
|
})();
|
|
35200
35433
|
});
|
|
35201
35434
|
setImmediate(() => {
|
|
35435
|
+
if (isCliImmediateShutdownRequested()) return;
|
|
35202
35436
|
try {
|
|
35203
35437
|
deps.sendLocalSkillsReport?.();
|
|
35204
35438
|
} catch (e) {
|
|
@@ -35500,12 +35734,12 @@ function createBridgePromptSenders(deps, getWs) {
|
|
|
35500
35734
|
}
|
|
35501
35735
|
|
|
35502
35736
|
// src/agents/acp/from-bridge/bridge-prompt-preamble.ts
|
|
35503
|
-
import { execFile as
|
|
35504
|
-
import { promisify as
|
|
35505
|
-
var
|
|
35737
|
+
import { execFile as execFile8 } from "node:child_process";
|
|
35738
|
+
import { promisify as promisify8 } from "node:util";
|
|
35739
|
+
var execFileAsync7 = promisify8(execFile8);
|
|
35506
35740
|
async function readGitBranch(cwd) {
|
|
35507
35741
|
try {
|
|
35508
|
-
const { stdout } = await
|
|
35742
|
+
const { stdout } = await execFileAsync7("git", ["branch", "--show-current"], { cwd, maxBuffer: 64 * 1024 });
|
|
35509
35743
|
const b = stdout.trim();
|
|
35510
35744
|
return b || null;
|
|
35511
35745
|
} catch {
|
|
@@ -36215,7 +36449,8 @@ var handleSessionGitRequestMessage = (msg, deps) => {
|
|
|
36215
36449
|
reply({
|
|
36216
36450
|
ok: true,
|
|
36217
36451
|
hasUncommittedChanges: r.hasUncommittedChanges,
|
|
36218
|
-
hasUnpushedCommits: r.hasUnpushedCommits
|
|
36452
|
+
hasUnpushedCommits: r.hasUnpushedCommits,
|
|
36453
|
+
uncommittedFileCount: r.uncommittedFileCount
|
|
36219
36454
|
});
|
|
36220
36455
|
return;
|
|
36221
36456
|
}
|
|
@@ -36229,7 +36464,12 @@ var handleSessionGitRequestMessage = (msg, deps) => {
|
|
|
36229
36464
|
return;
|
|
36230
36465
|
}
|
|
36231
36466
|
}
|
|
36232
|
-
const
|
|
36467
|
+
const recentCommitsLimit = typeof msg.changesRecentCommitsLimit === "number" || typeof msg.changesRecentCommitsLimit === "string" ? msg.changesRecentCommitsLimit : void 0;
|
|
36468
|
+
const opts = repoRel && view === "commit" && commitSha ? {
|
|
36469
|
+
repoRelPath: repoRel,
|
|
36470
|
+
basis: { kind: "commit", sha: commitSha },
|
|
36471
|
+
recentCommitsLimit
|
|
36472
|
+
} : repoRel ? { repoRelPath: repoRel, basis: { kind: "working" }, recentCommitsLimit } : recentCommitsLimit !== void 0 ? { recentCommitsLimit } : void 0;
|
|
36233
36473
|
const repos = await deps.sessionWorktreeManager.getSessionWorkingTreeChangeDetails(sessionId, opts);
|
|
36234
36474
|
reply({
|
|
36235
36475
|
ok: true,
|
|
@@ -36247,7 +36487,8 @@ var handleSessionGitRequestMessage = (msg, deps) => {
|
|
|
36247
36487
|
reply({
|
|
36248
36488
|
ok: true,
|
|
36249
36489
|
hasUncommittedChanges: st2.hasUncommittedChanges,
|
|
36250
|
-
hasUnpushedCommits: st2.hasUnpushedCommits
|
|
36490
|
+
hasUnpushedCommits: st2.hasUnpushedCommits,
|
|
36491
|
+
uncommittedFileCount: st2.uncommittedFileCount
|
|
36251
36492
|
});
|
|
36252
36493
|
return;
|
|
36253
36494
|
}
|
|
@@ -36272,7 +36513,8 @@ var handleSessionGitRequestMessage = (msg, deps) => {
|
|
|
36272
36513
|
reply({
|
|
36273
36514
|
ok: true,
|
|
36274
36515
|
hasUncommittedChanges: st.hasUncommittedChanges,
|
|
36275
|
-
hasUnpushedCommits: st.hasUnpushedCommits
|
|
36516
|
+
hasUnpushedCommits: st.hasUnpushedCommits,
|
|
36517
|
+
uncommittedFileCount: st.uncommittedFileCount
|
|
36276
36518
|
});
|
|
36277
36519
|
} catch (e) {
|
|
36278
36520
|
reply({ ok: false, error: e instanceof Error ? e.message : String(e) });
|
|
@@ -36876,6 +37118,7 @@ import * as path39 from "node:path";
|
|
|
36876
37118
|
import * as path38 from "node:path";
|
|
36877
37119
|
async function probeOneAgentTypeForCapabilities(params) {
|
|
36878
37120
|
const { agentType, cwd, workspaceId, log: log2, reportAgentCapabilities, bridgeReport = true } = params;
|
|
37121
|
+
if (isCliImmediateShutdownRequested()) return false;
|
|
36879
37122
|
const resolved = resolveAgentCommand(agentType);
|
|
36880
37123
|
if (!resolved) return false;
|
|
36881
37124
|
let sqliteChanged = false;
|
|
@@ -36909,6 +37152,7 @@ async function probeOneAgentTypeForCapabilities(params) {
|
|
|
36909
37152
|
}, 28e3);
|
|
36910
37153
|
killTimer.unref?.();
|
|
36911
37154
|
try {
|
|
37155
|
+
if (isCliImmediateShutdownRequested()) return false;
|
|
36912
37156
|
handle = await resolved.createClient({
|
|
36913
37157
|
command: resolved.command,
|
|
36914
37158
|
cwd: path38.resolve(cwd),
|
|
@@ -36928,7 +37172,7 @@ async function probeOneAgentTypeForCapabilities(params) {
|
|
|
36928
37172
|
onSessionUpdate: () => {
|
|
36929
37173
|
}
|
|
36930
37174
|
});
|
|
36931
|
-
|
|
37175
|
+
if (!await delayMsUnlessShutdownRequested(1200)) return false;
|
|
36932
37176
|
} catch (e) {
|
|
36933
37177
|
log2(
|
|
36934
37178
|
`[Bridge service] Agent capability probe (${agentType}): ${e instanceof Error ? e.message : String(e)}`
|
|
@@ -36956,6 +37200,7 @@ async function probeAgentCapabilitiesForDetectedTypes(params) {
|
|
|
36956
37200
|
} = params;
|
|
36957
37201
|
let changedCount = 0;
|
|
36958
37202
|
for (let i = 0; i < agentTypes.length; i++) {
|
|
37203
|
+
if (isCliImmediateShutdownRequested()) return changedCount;
|
|
36959
37204
|
if (i > 0) await yieldToEventLoop();
|
|
36960
37205
|
const agentType = agentTypes[i];
|
|
36961
37206
|
if (!agentType.trim()) continue;
|
|
@@ -36983,6 +37228,7 @@ async function probeAgentCapabilitiesForDetectedTypes(params) {
|
|
|
36983
37228
|
// src/agents/capabilities/warmup-agent-capabilities-on-connect.ts
|
|
36984
37229
|
async function warmupAgentCapabilitiesOnConnect(params) {
|
|
36985
37230
|
const { workspaceId, log: log2, getWs } = params;
|
|
37231
|
+
if (isCliImmediateShutdownRequested()) return;
|
|
36986
37232
|
const cwd = path39.resolve(getBridgeRoot());
|
|
36987
37233
|
async function sendBatchFromCache() {
|
|
36988
37234
|
const socket = getWs();
|
|
@@ -36995,18 +37241,21 @@ async function warmupAgentCapabilitiesOnConnect(params) {
|
|
|
36995
37241
|
items: rows.map((r) => ({ agentType: r.agentType, configOptions: r.configOptions }))
|
|
36996
37242
|
});
|
|
36997
37243
|
} catch (e) {
|
|
37244
|
+
if (e instanceof CliSqliteInterrupted) return;
|
|
36998
37245
|
log2(
|
|
36999
37246
|
`[Bridge service] Agent capability batch to bridge failed: ${e instanceof Error ? e.message : String(e)}`
|
|
37000
37247
|
);
|
|
37001
37248
|
}
|
|
37002
37249
|
}
|
|
37003
37250
|
await sendBatchFromCache();
|
|
37251
|
+
if (isCliImmediateShutdownRequested()) return;
|
|
37004
37252
|
let types = [];
|
|
37005
37253
|
try {
|
|
37006
37254
|
types = [...await detectLocalAgentTypes()];
|
|
37007
37255
|
} catch (e) {
|
|
37008
37256
|
log2(`[Bridge service] detectLocalAgentTypes failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
37009
37257
|
}
|
|
37258
|
+
if (isCliImmediateShutdownRequested()) return;
|
|
37010
37259
|
try {
|
|
37011
37260
|
const n = await probeAgentCapabilitiesForDetectedTypes({
|
|
37012
37261
|
agentTypes: types,
|
|
@@ -37018,11 +37267,13 @@ async function warmupAgentCapabilitiesOnConnect(params) {
|
|
|
37018
37267
|
});
|
|
37019
37268
|
if (n > 0) await sendBatchFromCache();
|
|
37020
37269
|
} catch (e) {
|
|
37270
|
+
if (e instanceof CliSqliteInterrupted) return;
|
|
37021
37271
|
log2(`[Bridge service] Agent capability probe (missing cache) failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
37022
37272
|
}
|
|
37023
37273
|
void (async () => {
|
|
37024
37274
|
try {
|
|
37025
37275
|
await yieldToEventLoop();
|
|
37276
|
+
if (isCliImmediateShutdownRequested()) return;
|
|
37026
37277
|
const n = await probeAgentCapabilitiesForDetectedTypes({
|
|
37027
37278
|
agentTypes: types,
|
|
37028
37279
|
cwd,
|
|
@@ -37033,6 +37284,7 @@ async function warmupAgentCapabilitiesOnConnect(params) {
|
|
|
37033
37284
|
});
|
|
37034
37285
|
if (n > 0) await sendBatchFromCache();
|
|
37035
37286
|
} catch (e) {
|
|
37287
|
+
if (e instanceof CliSqliteInterrupted) return;
|
|
37036
37288
|
log2(`[Bridge service] Agent capability lazy refresh failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
37037
37289
|
}
|
|
37038
37290
|
})();
|
|
@@ -37080,7 +37332,8 @@ async function createBridgeConnection(options) {
|
|
|
37080
37332
|
configOptions: info.configOptions
|
|
37081
37333
|
})
|
|
37082
37334
|
);
|
|
37083
|
-
} catch {
|
|
37335
|
+
} catch (e) {
|
|
37336
|
+
if (e instanceof CliSqliteInterrupted) return;
|
|
37084
37337
|
}
|
|
37085
37338
|
if (!changed) return;
|
|
37086
37339
|
const socket = getWs();
|
|
@@ -37163,6 +37416,7 @@ async function createBridgeConnection(options) {
|
|
|
37163
37416
|
const stopFileIndexWatcher = startFileIndexWatcher(getBridgeRoot());
|
|
37164
37417
|
return {
|
|
37165
37418
|
close: async () => {
|
|
37419
|
+
requestCliImmediateShutdown();
|
|
37166
37420
|
stopFileIndexWatcher();
|
|
37167
37421
|
bridgeHeartbeat.stop();
|
|
37168
37422
|
await closeBridgeConnection(state, acpManager, devServerManager, logFn);
|
|
@@ -37236,47 +37490,61 @@ async function runConnectedBridge(options, restartWithoutAuth) {
|
|
|
37236
37490
|
} = options;
|
|
37237
37491
|
const firehoseServerUrl = options.firehoseServerUrl ?? options.proxyServerUrl;
|
|
37238
37492
|
let cleanupKeyCommand;
|
|
37239
|
-
|
|
37240
|
-
apiUrl,
|
|
37241
|
-
workspaceId,
|
|
37242
|
-
authToken,
|
|
37243
|
-
refreshToken,
|
|
37244
|
-
firehoseServerUrl,
|
|
37245
|
-
justAuthenticated,
|
|
37246
|
-
worktreesRootPath,
|
|
37247
|
-
e2eCertificate,
|
|
37248
|
-
log,
|
|
37249
|
-
persistTokens: (t) => {
|
|
37250
|
-
writeConfigForApi(apiUrl, {
|
|
37251
|
-
workspaceId,
|
|
37252
|
-
token: t.token,
|
|
37253
|
-
refreshToken: t.refreshToken
|
|
37254
|
-
});
|
|
37255
|
-
},
|
|
37256
|
-
onAuthInvalid: () => {
|
|
37257
|
-
cleanupKeyCommand?.();
|
|
37258
|
-
log("[Bridge service] Access token invalid or revoked; re-authenticating\u2026");
|
|
37259
|
-
clearConfigForApi(apiUrl);
|
|
37260
|
-
void handle.close().then(() => {
|
|
37261
|
-
void restartWithoutAuth({ apiUrl, firehoseServerUrl, worktreesRootPath, e2eCertificate });
|
|
37262
|
-
});
|
|
37263
|
-
}
|
|
37264
|
-
});
|
|
37493
|
+
let bridgeClose = null;
|
|
37265
37494
|
const onSignal = (kind) => {
|
|
37495
|
+
requestCliImmediateShutdown();
|
|
37496
|
+
abortActiveGitChildProcesses();
|
|
37266
37497
|
cleanupKeyCommand?.();
|
|
37267
37498
|
logImmediate(
|
|
37268
37499
|
kind === "interrupt" ? "Keyboard interrupt (Ctrl+C) \u2014 stopping\u2026" : "Stop requested \u2014 shutting down\u2026"
|
|
37269
37500
|
);
|
|
37270
|
-
|
|
37271
|
-
void
|
|
37272
|
-
|
|
37273
|
-
|
|
37274
|
-
|
|
37501
|
+
if (bridgeClose) {
|
|
37502
|
+
void bridgeClose().then(() => process.exit(0));
|
|
37503
|
+
return;
|
|
37504
|
+
}
|
|
37505
|
+
process.exit(0);
|
|
37275
37506
|
};
|
|
37276
37507
|
const onSigInt = () => onSignal("interrupt");
|
|
37277
37508
|
const onSigTerm = () => onSignal("stop");
|
|
37278
37509
|
process.on("SIGINT", onSigInt);
|
|
37279
37510
|
process.on("SIGTERM", onSigTerm);
|
|
37511
|
+
let handle;
|
|
37512
|
+
try {
|
|
37513
|
+
handle = await createBridgeConnection({
|
|
37514
|
+
apiUrl,
|
|
37515
|
+
workspaceId,
|
|
37516
|
+
authToken,
|
|
37517
|
+
refreshToken,
|
|
37518
|
+
firehoseServerUrl,
|
|
37519
|
+
justAuthenticated,
|
|
37520
|
+
worktreesRootPath,
|
|
37521
|
+
e2eCertificate,
|
|
37522
|
+
log,
|
|
37523
|
+
persistTokens: (t) => {
|
|
37524
|
+
writeConfigForApi(apiUrl, {
|
|
37525
|
+
workspaceId,
|
|
37526
|
+
token: t.token,
|
|
37527
|
+
refreshToken: t.refreshToken
|
|
37528
|
+
});
|
|
37529
|
+
},
|
|
37530
|
+
onAuthInvalid: () => {
|
|
37531
|
+
cleanupKeyCommand?.();
|
|
37532
|
+
log("[Bridge service] Access token invalid or revoked; re-authenticating\u2026");
|
|
37533
|
+
clearConfigForApi(apiUrl);
|
|
37534
|
+
void handle.close().then(() => {
|
|
37535
|
+
void restartWithoutAuth({ apiUrl, firehoseServerUrl, worktreesRootPath, e2eCertificate });
|
|
37536
|
+
});
|
|
37537
|
+
}
|
|
37538
|
+
});
|
|
37539
|
+
} catch (e) {
|
|
37540
|
+
process.off("SIGINT", onSigInt);
|
|
37541
|
+
process.off("SIGTERM", onSigTerm);
|
|
37542
|
+
if (e instanceof CliSqliteInterrupted) {
|
|
37543
|
+
process.exit(0);
|
|
37544
|
+
}
|
|
37545
|
+
throw e;
|
|
37546
|
+
}
|
|
37547
|
+
bridgeClose = () => handle.close();
|
|
37280
37548
|
if (e2eCertificate) {
|
|
37281
37549
|
let openingCertificate = false;
|
|
37282
37550
|
cleanupKeyCommand = installE2eCertificateKeyCommand({
|
|
@@ -37321,6 +37589,7 @@ async function runBridge(options) {
|
|
|
37321
37589
|
}
|
|
37322
37590
|
});
|
|
37323
37591
|
const onSignal = (kind) => {
|
|
37592
|
+
requestCliImmediateShutdown();
|
|
37324
37593
|
logImmediate(
|
|
37325
37594
|
kind === "interrupt" ? "Keyboard interrupt (Ctrl+C) \u2014 stopping\u2026" : "Stop requested \u2014 shutting down\u2026"
|
|
37326
37595
|
);
|