@buildautomaton/cli 0.1.31 → 0.1.33
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 +444 -238
- package/dist/cli.js.map +4 -4
- package/dist/index.js +444 -238
- 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.33".length > 0 ? "0.1.33" : "0.0.0-dev";
|
|
23966
23966
|
|
|
23967
23967
|
// src/connection/heartbeat/constants.ts
|
|
23968
23968
|
var BRIDGE_APP_HEARTBEAT_INTERVAL_MS = 1e4;
|
|
@@ -25022,9 +25022,38 @@ 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
|
|
|
25052
|
+
// src/runtime/yield-to-event-loop.ts
|
|
25053
|
+
function yieldToEventLoop() {
|
|
25054
|
+
return new Promise((resolve18) => setImmediate(resolve18));
|
|
25055
|
+
}
|
|
25056
|
+
|
|
25028
25057
|
// src/sqlite/cli-sqlite-paths.ts
|
|
25029
25058
|
import fs7 from "node:fs";
|
|
25030
25059
|
import path5 from "node:path";
|
|
@@ -25317,6 +25346,29 @@ function migrateCliSqlite(db) {
|
|
|
25317
25346
|
|
|
25318
25347
|
// src/sqlite/cli-database.ts
|
|
25319
25348
|
var { Database: SqliteDatabase } = sqliteWasm;
|
|
25349
|
+
var CLI_SQLITE_SYNC_RETRY_MAX = 40;
|
|
25350
|
+
var CLI_SQLITE_ASYNC_RETRY_MAX = 60;
|
|
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
|
+
};
|
|
25358
|
+
function applyCliSqliteConcurrencyPragmas(db) {
|
|
25359
|
+
try {
|
|
25360
|
+
db.exec("PRAGMA journal_mode = WAL");
|
|
25361
|
+
} catch {
|
|
25362
|
+
}
|
|
25363
|
+
try {
|
|
25364
|
+
db.run("PRAGMA synchronous = NORMAL");
|
|
25365
|
+
} catch {
|
|
25366
|
+
}
|
|
25367
|
+
try {
|
|
25368
|
+
db.run("PRAGMA busy_timeout = 500");
|
|
25369
|
+
} catch {
|
|
25370
|
+
}
|
|
25371
|
+
}
|
|
25320
25372
|
function applyCliSqliteMemoryPragmas(db) {
|
|
25321
25373
|
try {
|
|
25322
25374
|
db.run("PRAGMA cache_size = -8192");
|
|
@@ -25324,18 +25376,6 @@ function applyCliSqliteMemoryPragmas(db) {
|
|
|
25324
25376
|
} catch {
|
|
25325
25377
|
}
|
|
25326
25378
|
}
|
|
25327
|
-
var openDatabases = /* @__PURE__ */ new Map();
|
|
25328
|
-
var processExitCloseRegistered = false;
|
|
25329
|
-
function registerProcessExitSqliteClose() {
|
|
25330
|
-
if (processExitCloseRegistered) return;
|
|
25331
|
-
processExitCloseRegistered = true;
|
|
25332
|
-
process.once("exit", () => {
|
|
25333
|
-
for (const db of openDatabases.values()) {
|
|
25334
|
-
safeCloseCliSqliteDatabase(db);
|
|
25335
|
-
}
|
|
25336
|
-
openDatabases.clear();
|
|
25337
|
-
});
|
|
25338
|
-
}
|
|
25339
25379
|
function safeCloseCliSqliteDatabase(db) {
|
|
25340
25380
|
if (db == null) return;
|
|
25341
25381
|
try {
|
|
@@ -25344,22 +25384,49 @@ function safeCloseCliSqliteDatabase(db) {
|
|
|
25344
25384
|
}
|
|
25345
25385
|
}
|
|
25346
25386
|
function closeAllCliSqliteConnections() {
|
|
25347
|
-
|
|
25348
|
-
|
|
25387
|
+
}
|
|
25388
|
+
function isCliSqliteLockError(e) {
|
|
25389
|
+
if (e instanceof CliSqliteInterrupted) return false;
|
|
25390
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
25391
|
+
const lower = msg.toLowerCase();
|
|
25392
|
+
return lower.includes("database is locked") || lower.includes("sqlite_busy") || lower.includes("sqlite3_busy") || lower.includes("database") && lower.includes("locked");
|
|
25393
|
+
}
|
|
25394
|
+
function syncSleepMs(ms) {
|
|
25395
|
+
if (ms <= 0) return;
|
|
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
|
+
}
|
|
25410
|
+
}
|
|
25349
25411
|
}
|
|
25350
|
-
openDatabases.clear();
|
|
25351
25412
|
}
|
|
25352
|
-
function
|
|
25353
|
-
const
|
|
25354
|
-
|
|
25355
|
-
|
|
25356
|
-
|
|
25357
|
-
|
|
25358
|
-
|
|
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();
|
|
25359
25421
|
}
|
|
25422
|
+
}
|
|
25423
|
+
function openCliSqliteConnection(options) {
|
|
25424
|
+
if (isCliImmediateShutdownRequested()) throw new CliSqliteInterrupted();
|
|
25425
|
+
const sqlitePath = getCliSqlitePath();
|
|
25360
25426
|
ensureCliSqliteParentDir(sqlitePath);
|
|
25361
25427
|
const db = new SqliteDatabase(sqlitePath);
|
|
25362
25428
|
try {
|
|
25429
|
+
applyCliSqliteConcurrencyPragmas(db);
|
|
25363
25430
|
applyCliSqliteMemoryPragmas(db);
|
|
25364
25431
|
migrateCliSqlite(db);
|
|
25365
25432
|
importCliSqliteLegacyDiskData(db, options?.logLegacyMigration);
|
|
@@ -25367,16 +25434,62 @@ function getCliDatabase(options) {
|
|
|
25367
25434
|
safeCloseCliSqliteDatabase(db);
|
|
25368
25435
|
throw e;
|
|
25369
25436
|
}
|
|
25370
|
-
openDatabases.set(sqlitePath, db);
|
|
25371
|
-
registerProcessExitSqliteClose();
|
|
25372
25437
|
return db;
|
|
25373
25438
|
}
|
|
25439
|
+
function withCliSqliteSync(fn, options) {
|
|
25440
|
+
for (let attempt = 1; attempt <= CLI_SQLITE_SYNC_RETRY_MAX; attempt++) {
|
|
25441
|
+
if (isCliImmediateShutdownRequested()) throw new CliSqliteInterrupted();
|
|
25442
|
+
let db;
|
|
25443
|
+
try {
|
|
25444
|
+
db = openCliSqliteConnection(options);
|
|
25445
|
+
try {
|
|
25446
|
+
return fn(db);
|
|
25447
|
+
} finally {
|
|
25448
|
+
safeCloseCliSqliteDatabase(db);
|
|
25449
|
+
}
|
|
25450
|
+
} catch (e) {
|
|
25451
|
+
safeCloseCliSqliteDatabase(db);
|
|
25452
|
+
if (e instanceof CliSqliteInterrupted) throw e;
|
|
25453
|
+
if (!isCliSqliteLockError(e) || attempt === CLI_SQLITE_SYNC_RETRY_MAX) throw e;
|
|
25454
|
+
syncSleepMs(Math.min(500, 12 * attempt));
|
|
25455
|
+
}
|
|
25456
|
+
}
|
|
25457
|
+
throw new Error("withCliSqliteSync: exhausted retries");
|
|
25458
|
+
}
|
|
25459
|
+
async function withCliSqlite(fn, options) {
|
|
25460
|
+
let lastError;
|
|
25461
|
+
for (let attempt = 1; attempt <= CLI_SQLITE_ASYNC_RETRY_MAX; attempt++) {
|
|
25462
|
+
if (isCliImmediateShutdownRequested()) throw new CliSqliteInterrupted();
|
|
25463
|
+
let db;
|
|
25464
|
+
try {
|
|
25465
|
+
db = openCliSqliteConnection(options);
|
|
25466
|
+
try {
|
|
25467
|
+
return await Promise.resolve(fn(db));
|
|
25468
|
+
} finally {
|
|
25469
|
+
safeCloseCliSqliteDatabase(db);
|
|
25470
|
+
}
|
|
25471
|
+
} catch (e) {
|
|
25472
|
+
lastError = e;
|
|
25473
|
+
safeCloseCliSqliteDatabase(db);
|
|
25474
|
+
if (e instanceof CliSqliteInterrupted) throw e;
|
|
25475
|
+
if (!isCliSqliteLockError(e) || attempt === CLI_SQLITE_ASYNC_RETRY_MAX) throw e;
|
|
25476
|
+
const delayMs = Math.min(600, CLI_SQLITE_ASYNC_BASE_DELAY_MS * attempt);
|
|
25477
|
+
await asyncDelayMsInterruptible(delayMs);
|
|
25478
|
+
await yieldToEventLoop();
|
|
25479
|
+
}
|
|
25480
|
+
}
|
|
25481
|
+
if (lastError instanceof Error) throw lastError;
|
|
25482
|
+
throw new Error(String(lastError));
|
|
25483
|
+
}
|
|
25484
|
+
async function ensureCliSqliteInitialized(options) {
|
|
25485
|
+
await withCliSqlite(() => void 0, options);
|
|
25486
|
+
}
|
|
25374
25487
|
|
|
25375
25488
|
// src/connection/close-bridge-connection.ts
|
|
25376
25489
|
async function closeBridgeConnection(state, acpManager, devServerManager, log2) {
|
|
25490
|
+
requestCliImmediateShutdown();
|
|
25377
25491
|
const say = log2 ?? logImmediate;
|
|
25378
25492
|
say("Cleaning up connections\u2026");
|
|
25379
|
-
await new Promise((resolve18) => setImmediate(resolve18));
|
|
25380
25493
|
state.closedByUser = true;
|
|
25381
25494
|
clearReconnectQuietTimer(state.mainQuiet);
|
|
25382
25495
|
clearReconnectQuietTimer(state.firehoseQuiet);
|
|
@@ -25413,7 +25526,7 @@ async function closeBridgeConnection(state, acpManager, devServerManager, log2)
|
|
|
25413
25526
|
}
|
|
25414
25527
|
if (devServerManager) {
|
|
25415
25528
|
say("Stopping local dev server processes\u2026");
|
|
25416
|
-
await devServerManager.shutdownAllGraceful();
|
|
25529
|
+
await devServerManager.shutdownAllGraceful({ graceMs: BRIDGE_CLOSE_DEV_SERVER_GRACE_MS });
|
|
25417
25530
|
}
|
|
25418
25531
|
try {
|
|
25419
25532
|
closeAllCliSqliteConnections();
|
|
@@ -30659,34 +30772,50 @@ __export(claude_code_acp_client_exports, {
|
|
|
30659
30772
|
createClaudeCodeAcpClient: () => createClaudeCodeAcpClient,
|
|
30660
30773
|
detectLocalAgentPresence: () => detectLocalAgentPresence
|
|
30661
30774
|
});
|
|
30662
|
-
import { execFile as execFile9 } from "node:child_process";
|
|
30663
|
-
import { promisify as promisify9 } from "node:util";
|
|
30664
30775
|
|
|
30665
30776
|
// src/agents/acp/clients/detect-command-on-path.ts
|
|
30666
30777
|
import { execFile as execFile8 } from "node:child_process";
|
|
30667
30778
|
import { promisify as promisify8 } from "node:util";
|
|
30668
30779
|
var execFileAsync7 = promisify8(execFile8);
|
|
30669
|
-
|
|
30780
|
+
var COMMAND_ON_PATH_PROBE_TIMEOUT_MS = 750;
|
|
30781
|
+
async function execFileShutdownAware(file2, args, timeoutMs) {
|
|
30782
|
+
if (isCliImmediateShutdownRequested()) throw new Error("shutdown");
|
|
30783
|
+
const ac = new AbortController();
|
|
30784
|
+
const shutdownPoll = setInterval(() => {
|
|
30785
|
+
if (isCliImmediateShutdownRequested()) ac.abort();
|
|
30786
|
+
}, 50);
|
|
30787
|
+
shutdownPoll.unref?.();
|
|
30788
|
+
try {
|
|
30789
|
+
await execFileAsync7(file2, args, { timeout: timeoutMs, signal: ac.signal });
|
|
30790
|
+
} finally {
|
|
30791
|
+
clearInterval(shutdownPoll);
|
|
30792
|
+
}
|
|
30793
|
+
}
|
|
30794
|
+
async function isCommandOnPath(command, timeoutMs = COMMAND_ON_PATH_PROBE_TIMEOUT_MS) {
|
|
30795
|
+
if (isCliImmediateShutdownRequested()) return false;
|
|
30670
30796
|
try {
|
|
30671
|
-
await
|
|
30797
|
+
await execFileShutdownAware("which", [command], timeoutMs);
|
|
30672
30798
|
return true;
|
|
30673
30799
|
} catch {
|
|
30674
30800
|
return false;
|
|
30675
30801
|
}
|
|
30676
30802
|
}
|
|
30677
|
-
|
|
30678
|
-
|
|
30679
|
-
var execFileAsync8 = promisify9(execFile9);
|
|
30680
|
-
var BACKEND_LOCAL_AGENT_TYPE = "claude-code";
|
|
30681
|
-
async function detectLocalAgentPresence() {
|
|
30682
|
-
if (await isCommandOnPath("claude")) return true;
|
|
30803
|
+
async function execProbeShutdownAware(file2, args, timeoutMs) {
|
|
30804
|
+
if (isCliImmediateShutdownRequested()) return false;
|
|
30683
30805
|
try {
|
|
30684
|
-
await
|
|
30806
|
+
await execFileShutdownAware(file2, args, timeoutMs);
|
|
30685
30807
|
return true;
|
|
30686
30808
|
} catch {
|
|
30687
30809
|
return false;
|
|
30688
30810
|
}
|
|
30689
30811
|
}
|
|
30812
|
+
|
|
30813
|
+
// src/agents/acp/clients/claude-code-acp-client.ts
|
|
30814
|
+
var BACKEND_LOCAL_AGENT_TYPE = "claude-code";
|
|
30815
|
+
async function detectLocalAgentPresence() {
|
|
30816
|
+
if (await isCommandOnPath("claude")) return true;
|
|
30817
|
+
return execProbeShutdownAware("npx", ["--yes", "@anthropic-ai/claude-code", "--version"], 3e3);
|
|
30818
|
+
}
|
|
30690
30819
|
function buildClaudeCodeAcpSpawnCommand(base, _sessionMode) {
|
|
30691
30820
|
return [...base];
|
|
30692
30821
|
}
|
|
@@ -31852,56 +31981,80 @@ function sessionKeyForCloudSessionId(cloudSessionId) {
|
|
|
31852
31981
|
}
|
|
31853
31982
|
function readLocalAgentSessionFile(cloudSessionId) {
|
|
31854
31983
|
try {
|
|
31855
|
-
|
|
31856
|
-
|
|
31857
|
-
|
|
31858
|
-
|
|
31859
|
-
|
|
31860
|
-
|
|
31861
|
-
|
|
31862
|
-
|
|
31863
|
-
|
|
31864
|
-
|
|
31865
|
-
|
|
31866
|
-
|
|
31867
|
-
|
|
31868
|
-
|
|
31984
|
+
return withCliSqliteSync((db) => {
|
|
31985
|
+
const key = sessionKeyForCloudSessionId(cloudSessionId);
|
|
31986
|
+
const row = db.get(
|
|
31987
|
+
"SELECT acp_session_id, backend_agent_type, config_options_json, updated_at FROM agent_session WHERE session_key = ?",
|
|
31988
|
+
[key]
|
|
31989
|
+
);
|
|
31990
|
+
if (!row) return null;
|
|
31991
|
+
let configOptions = null;
|
|
31992
|
+
if (row.config_options_json != null && row.config_options_json !== "") {
|
|
31993
|
+
try {
|
|
31994
|
+
const parsed = JSON.parse(row.config_options_json);
|
|
31995
|
+
configOptions = Array.isArray(parsed) ? parsed : null;
|
|
31996
|
+
} catch {
|
|
31997
|
+
configOptions = null;
|
|
31998
|
+
}
|
|
31869
31999
|
}
|
|
31870
|
-
|
|
31871
|
-
|
|
31872
|
-
|
|
31873
|
-
|
|
31874
|
-
|
|
31875
|
-
|
|
31876
|
-
|
|
31877
|
-
};
|
|
32000
|
+
return {
|
|
32001
|
+
v: 1,
|
|
32002
|
+
acpSessionId: row.acp_session_id,
|
|
32003
|
+
backendAgentType: row.backend_agent_type,
|
|
32004
|
+
configOptions,
|
|
32005
|
+
updatedAt: row.updated_at
|
|
32006
|
+
};
|
|
32007
|
+
});
|
|
31878
32008
|
} catch {
|
|
31879
32009
|
return null;
|
|
31880
32010
|
}
|
|
31881
32011
|
}
|
|
31882
32012
|
function writeLocalAgentSessionFile(cloudSessionId, patch) {
|
|
31883
32013
|
try {
|
|
31884
|
-
|
|
31885
|
-
|
|
31886
|
-
|
|
31887
|
-
|
|
31888
|
-
|
|
31889
|
-
|
|
31890
|
-
|
|
31891
|
-
|
|
31892
|
-
|
|
31893
|
-
|
|
31894
|
-
|
|
31895
|
-
|
|
31896
|
-
|
|
31897
|
-
|
|
31898
|
-
|
|
31899
|
-
|
|
31900
|
-
|
|
31901
|
-
|
|
31902
|
-
|
|
31903
|
-
|
|
31904
|
-
|
|
32014
|
+
withCliSqliteSync((db) => {
|
|
32015
|
+
const key = sessionKeyForCloudSessionId(cloudSessionId);
|
|
32016
|
+
const prevRow = db.get(
|
|
32017
|
+
"SELECT acp_session_id, backend_agent_type, config_options_json, updated_at FROM agent_session WHERE session_key = ?",
|
|
32018
|
+
[key]
|
|
32019
|
+
);
|
|
32020
|
+
let prev = null;
|
|
32021
|
+
if (prevRow) {
|
|
32022
|
+
let configOptions = null;
|
|
32023
|
+
if (prevRow.config_options_json != null && prevRow.config_options_json !== "") {
|
|
32024
|
+
try {
|
|
32025
|
+
const parsed = JSON.parse(prevRow.config_options_json);
|
|
32026
|
+
configOptions = Array.isArray(parsed) ? parsed : null;
|
|
32027
|
+
} catch {
|
|
32028
|
+
configOptions = null;
|
|
32029
|
+
}
|
|
32030
|
+
}
|
|
32031
|
+
prev = {
|
|
32032
|
+
v: 1,
|
|
32033
|
+
acpSessionId: prevRow.acp_session_id,
|
|
32034
|
+
backendAgentType: prevRow.backend_agent_type,
|
|
32035
|
+
configOptions,
|
|
32036
|
+
updatedAt: prevRow.updated_at
|
|
32037
|
+
};
|
|
32038
|
+
}
|
|
32039
|
+
const next = {
|
|
32040
|
+
v: 1,
|
|
32041
|
+
acpSessionId: patch.acpSessionId !== void 0 ? patch.acpSessionId : prev?.acpSessionId ?? null,
|
|
32042
|
+
backendAgentType: patch.backendAgentType !== void 0 ? patch.backendAgentType : prev?.backendAgentType ?? null,
|
|
32043
|
+
configOptions: patch.configOptions !== void 0 ? patch.configOptions : prev?.configOptions ?? null,
|
|
32044
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
32045
|
+
};
|
|
32046
|
+
const configJson = next.configOptions != null ? JSON.stringify(next.configOptions) : null;
|
|
32047
|
+
db.run(
|
|
32048
|
+
`INSERT INTO agent_session (session_key, acp_session_id, backend_agent_type, config_options_json, updated_at)
|
|
32049
|
+
VALUES (?, ?, ?, ?, ?)
|
|
32050
|
+
ON CONFLICT(session_key) DO UPDATE SET
|
|
32051
|
+
acp_session_id = excluded.acp_session_id,
|
|
32052
|
+
backend_agent_type = excluded.backend_agent_type,
|
|
32053
|
+
config_options_json = excluded.config_options_json,
|
|
32054
|
+
updated_at = excluded.updated_at`,
|
|
32055
|
+
[key, next.acpSessionId, next.backendAgentType, configJson, next.updatedAt]
|
|
32056
|
+
);
|
|
32057
|
+
});
|
|
31905
32058
|
} catch {
|
|
31906
32059
|
}
|
|
31907
32060
|
}
|
|
@@ -33484,41 +33637,45 @@ function getCwdHashForFileIndex(resolvedCwd) {
|
|
|
33484
33637
|
// src/files/index/build-file-index.ts
|
|
33485
33638
|
import path28 from "node:path";
|
|
33486
33639
|
|
|
33487
|
-
// src/runtime/yield-to-event-loop.ts
|
|
33488
|
-
function yieldToEventLoop() {
|
|
33489
|
-
return new Promise((resolve18) => setImmediate(resolve18));
|
|
33490
|
-
}
|
|
33491
|
-
|
|
33492
33640
|
// src/files/index/walk-workspace-tree.ts
|
|
33493
33641
|
import fs24 from "node:fs";
|
|
33494
33642
|
import path27 from "node:path";
|
|
33495
33643
|
function shouldSkipWorkspaceWalkEntry(name) {
|
|
33496
33644
|
return name.startsWith(".");
|
|
33497
33645
|
}
|
|
33498
|
-
function
|
|
33646
|
+
async function walkWorkspaceTreeAsync(dir, baseDir, onFile, state) {
|
|
33499
33647
|
let names;
|
|
33500
33648
|
try {
|
|
33501
|
-
names = fs24.
|
|
33649
|
+
names = await fs24.promises.readdir(dir);
|
|
33502
33650
|
} catch {
|
|
33503
33651
|
return;
|
|
33504
33652
|
}
|
|
33505
33653
|
for (const name of names) {
|
|
33506
33654
|
if (shouldSkipWorkspaceWalkEntry(name)) continue;
|
|
33655
|
+
if (state.n > 0 && state.n % INDEX_WORK_YIELD_EVERY === 0) {
|
|
33656
|
+
if (isCliImmediateShutdownRequested()) throw new CliSqliteInterrupted();
|
|
33657
|
+
await yieldToEventLoop();
|
|
33658
|
+
if (isCliImmediateShutdownRequested()) throw new CliSqliteInterrupted();
|
|
33659
|
+
}
|
|
33660
|
+
state.n++;
|
|
33507
33661
|
const full = path27.join(dir, name);
|
|
33508
33662
|
let stat2;
|
|
33509
33663
|
try {
|
|
33510
|
-
stat2 = fs24.
|
|
33664
|
+
stat2 = await fs24.promises.stat(full);
|
|
33511
33665
|
} catch {
|
|
33512
33666
|
continue;
|
|
33513
33667
|
}
|
|
33514
33668
|
const relative5 = path27.relative(baseDir, full).replace(/\\/g, "/");
|
|
33515
33669
|
if (stat2.isDirectory()) {
|
|
33516
|
-
|
|
33670
|
+
await walkWorkspaceTreeAsync(full, baseDir, onFile, state);
|
|
33517
33671
|
} else if (stat2.isFile()) {
|
|
33518
33672
|
onFile(relative5);
|
|
33519
33673
|
}
|
|
33520
33674
|
}
|
|
33521
33675
|
}
|
|
33676
|
+
function createWalkYieldState() {
|
|
33677
|
+
return { n: 0 };
|
|
33678
|
+
}
|
|
33522
33679
|
|
|
33523
33680
|
// src/files/index/file-index-sqlite-lock.ts
|
|
33524
33681
|
import fs25 from "node:fs";
|
|
@@ -33551,48 +33708,60 @@ function withFileIndexSqliteLock(fn) {
|
|
|
33551
33708
|
}
|
|
33552
33709
|
|
|
33553
33710
|
// src/files/index/build-file-index.ts
|
|
33554
|
-
var
|
|
33555
|
-
function
|
|
33556
|
-
|
|
33557
|
-
|
|
33558
|
-
|
|
33559
|
-
|
|
33560
|
-
|
|
33561
|
-
|
|
33562
|
-
db.run("
|
|
33563
|
-
const ins = db.prepare("INSERT INTO file_index_path (cwd_hash, path) VALUES (?, ?)");
|
|
33711
|
+
var FILE_INDEX_INTERRUPT_CHECK_EVERY = 256;
|
|
33712
|
+
function assertNotShutdown() {
|
|
33713
|
+
if (isCliImmediateShutdownRequested()) throw new CliSqliteInterrupted();
|
|
33714
|
+
}
|
|
33715
|
+
function persistFileIndexPaths(resolved, paths) {
|
|
33716
|
+
return withCliSqliteSync((db) => {
|
|
33717
|
+
const h = getCwdHashForFileIndex(resolved);
|
|
33718
|
+
let pathCount = 0;
|
|
33719
|
+
db.run("BEGIN IMMEDIATE");
|
|
33564
33720
|
try {
|
|
33565
|
-
|
|
33566
|
-
|
|
33721
|
+
db.run("DELETE FROM file_index_path WHERE cwd_hash = ?", [h]);
|
|
33722
|
+
const ins = db.prepare("INSERT INTO file_index_path (cwd_hash, path) VALUES (?, ?)");
|
|
33723
|
+
try {
|
|
33724
|
+
let batch = 0;
|
|
33725
|
+
for (const rel of paths) {
|
|
33726
|
+
if (++batch >= FILE_INDEX_INTERRUPT_CHECK_EVERY) {
|
|
33727
|
+
batch = 0;
|
|
33728
|
+
assertNotShutdown();
|
|
33729
|
+
}
|
|
33567
33730
|
ins.run([h, rel]);
|
|
33731
|
+
pathCount += 1;
|
|
33568
33732
|
}
|
|
33569
|
-
|
|
33570
|
-
|
|
33571
|
-
}
|
|
33572
|
-
|
|
33573
|
-
|
|
33574
|
-
|
|
33575
|
-
|
|
33576
|
-
|
|
33577
|
-
|
|
33578
|
-
|
|
33579
|
-
}
|
|
33580
|
-
db.run("COMMIT");
|
|
33581
|
-
} catch (e) {
|
|
33582
|
-
try {
|
|
33583
|
-
db.run("ROLLBACK");
|
|
33584
|
-
} catch {
|
|
33733
|
+
} finally {
|
|
33734
|
+
ins.finalize();
|
|
33735
|
+
}
|
|
33736
|
+
db.run("COMMIT");
|
|
33737
|
+
} catch (e) {
|
|
33738
|
+
try {
|
|
33739
|
+
db.run("ROLLBACK");
|
|
33740
|
+
} catch {
|
|
33741
|
+
}
|
|
33742
|
+
throw e;
|
|
33585
33743
|
}
|
|
33586
|
-
|
|
33587
|
-
}
|
|
33588
|
-
|
|
33744
|
+
return pathCount;
|
|
33745
|
+
});
|
|
33746
|
+
}
|
|
33747
|
+
async function collectWorkspacePathsAsync(resolved) {
|
|
33748
|
+
const paths = [];
|
|
33749
|
+
const state = createWalkYieldState();
|
|
33750
|
+
await walkWorkspaceTreeAsync(resolved, resolved, (rel) => {
|
|
33751
|
+
assertNotShutdown();
|
|
33752
|
+
paths.push(rel);
|
|
33753
|
+
}, state);
|
|
33754
|
+
return paths;
|
|
33589
33755
|
}
|
|
33590
33756
|
async function buildFileIndexAsync(cwd) {
|
|
33591
33757
|
return withFileIndexSqliteLock(async () => {
|
|
33592
33758
|
const resolved = path28.resolve(cwd);
|
|
33593
33759
|
await yieldToEventLoop();
|
|
33594
|
-
|
|
33760
|
+
assertNotShutdown();
|
|
33761
|
+
const paths = await collectWorkspacePathsAsync(resolved);
|
|
33595
33762
|
await yieldToEventLoop();
|
|
33763
|
+
assertNotShutdown();
|
|
33764
|
+
const pathCount = persistFileIndexPaths(resolved, paths);
|
|
33596
33765
|
return { pathCount };
|
|
33597
33766
|
});
|
|
33598
33767
|
}
|
|
@@ -33609,23 +33778,20 @@ function sqliteExprBridgeFileIndexDependencyRank() {
|
|
|
33609
33778
|
function escapeLikePattern(fragment) {
|
|
33610
33779
|
return fragment.replace(/\\/g, "\\\\").replace(/%/g, "\\%").replace(/_/g, "\\_");
|
|
33611
33780
|
}
|
|
33612
|
-
function
|
|
33613
|
-
const db = getCliDatabase();
|
|
33781
|
+
function bridgeFileIndexIsPopulatedWithDb(resolvedCwd, db) {
|
|
33614
33782
|
const h = getCwdHashForFileIndex(resolvedCwd);
|
|
33615
33783
|
const row = db.get("SELECT 1 as ok FROM file_index_path WHERE cwd_hash = ? LIMIT 1", [h]);
|
|
33616
33784
|
return row != null;
|
|
33617
33785
|
}
|
|
33618
|
-
function
|
|
33619
|
-
const db = getCliDatabase();
|
|
33786
|
+
function bridgeFileIndexPathCountWithDb(resolvedCwd, db) {
|
|
33620
33787
|
const h = getCwdHashForFileIndex(resolvedCwd);
|
|
33621
33788
|
const row = db.get("SELECT COUNT(*) as c FROM file_index_path WHERE cwd_hash = ?", [h]);
|
|
33622
33789
|
const c = row?.c ?? 0;
|
|
33623
33790
|
return Number(c);
|
|
33624
33791
|
}
|
|
33625
|
-
function
|
|
33792
|
+
function searchBridgeFilePathsWithDb(resolvedCwd, query, limit, db) {
|
|
33626
33793
|
const q = query.trim().toLowerCase();
|
|
33627
33794
|
if (!q) return [];
|
|
33628
|
-
const db = getCliDatabase();
|
|
33629
33795
|
const h = getCwdHashForFileIndex(resolvedCwd);
|
|
33630
33796
|
const pattern = `%${escapeLikePattern(q)}%`;
|
|
33631
33797
|
const lim = Math.max(0, Math.min(1e4, Math.floor(limit)));
|
|
@@ -33636,9 +33802,15 @@ function searchBridgeFilePaths(resolvedCwd, query, limit = 100) {
|
|
|
33636
33802
|
);
|
|
33637
33803
|
return rows.map((r) => String(r.path));
|
|
33638
33804
|
}
|
|
33805
|
+
async function bridgeFileIndexIsPopulated(resolvedCwd) {
|
|
33806
|
+
return withCliSqlite((db) => bridgeFileIndexIsPopulatedWithDb(resolvedCwd, db));
|
|
33807
|
+
}
|
|
33808
|
+
async function bridgeFileIndexPathCount(resolvedCwd) {
|
|
33809
|
+
return withCliSqlite((db) => bridgeFileIndexPathCountWithDb(resolvedCwd, db));
|
|
33810
|
+
}
|
|
33639
33811
|
async function searchBridgeFilePathsAsync(resolvedCwd, query, limit = 100) {
|
|
33640
33812
|
await yieldToEventLoop();
|
|
33641
|
-
const out =
|
|
33813
|
+
const out = await withCliSqlite((db) => searchBridgeFilePathsWithDb(resolvedCwd, query, limit, db));
|
|
33642
33814
|
if (out.length >= INDEX_WORK_YIELD_EVERY) await yieldToEventLoop();
|
|
33643
33815
|
return out;
|
|
33644
33816
|
}
|
|
@@ -33646,8 +33818,8 @@ async function searchBridgeFilePathsAsync(resolvedCwd, query, limit = 100) {
|
|
|
33646
33818
|
// src/files/index/ensure-file-index.ts
|
|
33647
33819
|
async function ensureFileIndexAsync(cwd) {
|
|
33648
33820
|
const resolved = path29.resolve(cwd);
|
|
33649
|
-
if (bridgeFileIndexIsPopulated(resolved)) {
|
|
33650
|
-
return { fromCache: true, pathCount: bridgeFileIndexPathCount(resolved) };
|
|
33821
|
+
if (await bridgeFileIndexIsPopulated(resolved)) {
|
|
33822
|
+
return { fromCache: true, pathCount: await bridgeFileIndexPathCount(resolved) };
|
|
33651
33823
|
}
|
|
33652
33824
|
return { ...await buildFileIndexAsync(resolved), fromCache: false };
|
|
33653
33825
|
}
|
|
@@ -33693,11 +33865,13 @@ function createFsWatcher(resolved, schedule) {
|
|
|
33693
33865
|
function startFileIndexWatcher(cwd = getBridgeRoot()) {
|
|
33694
33866
|
const resolved = path30.resolve(cwd);
|
|
33695
33867
|
void buildFileIndexAsync(resolved).catch((e) => {
|
|
33868
|
+
if (e instanceof CliSqliteInterrupted) return;
|
|
33696
33869
|
console.error("[file-index] Initial index build failed:", e);
|
|
33697
33870
|
});
|
|
33698
33871
|
let timer = null;
|
|
33699
33872
|
const runRebuild = () => {
|
|
33700
33873
|
void buildFileIndexAsync(resolved).catch((e) => {
|
|
33874
|
+
if (e instanceof CliSqliteInterrupted) return;
|
|
33701
33875
|
console.error("[file-index] Watch rebuild failed:", e);
|
|
33702
33876
|
});
|
|
33703
33877
|
};
|
|
@@ -34231,9 +34405,6 @@ var StreamTail = class {
|
|
|
34231
34405
|
}
|
|
34232
34406
|
};
|
|
34233
34407
|
|
|
34234
|
-
// src/dev-servers/manager/dev-server-constants.ts
|
|
34235
|
-
var BRIDGE_SHUTDOWN_GRACE_MS = 8e3;
|
|
34236
|
-
|
|
34237
34408
|
// src/dev-servers/manager/dev-server-firehose-messages.ts
|
|
34238
34409
|
function buildFirehoseSnapshotMessage(params) {
|
|
34239
34410
|
const payload = {
|
|
@@ -34519,22 +34690,23 @@ var DevServerManager = class {
|
|
|
34519
34690
|
}
|
|
34520
34691
|
this.start(serverId);
|
|
34521
34692
|
}
|
|
34522
|
-
async shutdownAllGraceful() {
|
|
34693
|
+
async shutdownAllGraceful(opts) {
|
|
34694
|
+
const graceMs = opts?.graceMs ?? BRIDGE_SHUTDOWN_GRACE_MS;
|
|
34523
34695
|
const pairs = [...this.processes.entries()];
|
|
34524
34696
|
if (pairs.length === 0) return;
|
|
34525
34697
|
this.log(
|
|
34526
34698
|
`[dev-server] Stopping ${pairs.length} local dev server process${pairs.length === 1 ? "" : "es"}\u2026`
|
|
34527
34699
|
);
|
|
34528
|
-
await Promise.all(pairs.map(([serverId, proc]) => this.gracefulTerminateOrUnknown(serverId, proc)));
|
|
34700
|
+
await Promise.all(pairs.map(([serverId, proc]) => this.gracefulTerminateOrUnknown(serverId, proc, graceMs)));
|
|
34529
34701
|
}
|
|
34530
|
-
async gracefulTerminateOrUnknown(serverId, proc) {
|
|
34702
|
+
async gracefulTerminateOrUnknown(serverId, proc, graceMs) {
|
|
34531
34703
|
const shortId = `${serverId.slice(0, 8)}\u2026`;
|
|
34532
|
-
await sigtermAndWaitForExit(proc,
|
|
34704
|
+
await sigtermAndWaitForExit(proc, graceMs, this.log, shortId);
|
|
34533
34705
|
if (!this.processes.has(serverId) || this.processes.get(serverId) !== proc) {
|
|
34534
34706
|
return;
|
|
34535
34707
|
}
|
|
34536
34708
|
this.bumpGeneration(serverId);
|
|
34537
|
-
forceKillChild(proc, this.log, shortId,
|
|
34709
|
+
forceKillChild(proc, this.log, shortId, graceMs);
|
|
34538
34710
|
this.processes.delete(serverId);
|
|
34539
34711
|
this.clearPoll(serverId);
|
|
34540
34712
|
this.pipedCaptureByServerId.delete(serverId);
|
|
@@ -34961,10 +35133,13 @@ var LOCAL_AGENT_ACP_MODULES = [
|
|
|
34961
35133
|
];
|
|
34962
35134
|
async function detectLocalAgentTypes() {
|
|
34963
35135
|
try {
|
|
35136
|
+
if (isCliImmediateShutdownRequested()) return [];
|
|
34964
35137
|
const out = [];
|
|
34965
35138
|
for (let i = 0; i < LOCAL_AGENT_ACP_MODULES.length; i++) {
|
|
35139
|
+
if (isCliImmediateShutdownRequested()) return out;
|
|
34966
35140
|
if (i > 0) {
|
|
34967
35141
|
await yieldToEventLoop();
|
|
35142
|
+
if (isCliImmediateShutdownRequested()) return out;
|
|
34968
35143
|
}
|
|
34969
35144
|
const mod = LOCAL_AGENT_ACP_MODULES[i];
|
|
34970
35145
|
try {
|
|
@@ -34997,6 +35172,7 @@ function createSendLocalSkillsReport(getWs, logFn) {
|
|
|
34997
35172
|
}
|
|
34998
35173
|
function createReportAutoDetectedAgents(getWs, logFn) {
|
|
34999
35174
|
return async () => {
|
|
35175
|
+
if (isCliImmediateShutdownRequested()) return;
|
|
35000
35176
|
try {
|
|
35001
35177
|
const types = await detectLocalAgentTypes();
|
|
35002
35178
|
const socket = getWs();
|
|
@@ -35096,6 +35272,7 @@ var handleBridgeIdentified = (msg, deps) => {
|
|
|
35096
35272
|
});
|
|
35097
35273
|
setImmediate(() => {
|
|
35098
35274
|
void (async () => {
|
|
35275
|
+
if (isCliImmediateShutdownRequested()) return;
|
|
35099
35276
|
try {
|
|
35100
35277
|
await deps.reportAutoDetectedAgents?.();
|
|
35101
35278
|
} catch (e) {
|
|
@@ -35103,6 +35280,7 @@ var handleBridgeIdentified = (msg, deps) => {
|
|
|
35103
35280
|
`[Bridge service] Auto-detect agents failed: ${e instanceof Error ? e.message : String(e)}`
|
|
35104
35281
|
);
|
|
35105
35282
|
}
|
|
35283
|
+
if (isCliImmediateShutdownRequested()) return;
|
|
35106
35284
|
try {
|
|
35107
35285
|
await deps.warmupAgentCapabilitiesOnConnect?.();
|
|
35108
35286
|
} catch (e) {
|
|
@@ -35113,6 +35291,7 @@ var handleBridgeIdentified = (msg, deps) => {
|
|
|
35113
35291
|
})();
|
|
35114
35292
|
});
|
|
35115
35293
|
setImmediate(() => {
|
|
35294
|
+
if (isCliImmediateShutdownRequested()) return;
|
|
35116
35295
|
try {
|
|
35117
35296
|
deps.sendLocalSkillsReport?.();
|
|
35118
35297
|
} catch (e) {
|
|
@@ -35160,30 +35339,32 @@ var MERGEABLE_SERVER_STATES = /* @__PURE__ */ new Set([
|
|
|
35160
35339
|
"stopping",
|
|
35161
35340
|
"discarded"
|
|
35162
35341
|
]);
|
|
35163
|
-
function readPersistedQueue(queueKey) {
|
|
35164
|
-
|
|
35165
|
-
|
|
35166
|
-
|
|
35167
|
-
|
|
35168
|
-
|
|
35169
|
-
|
|
35170
|
-
|
|
35171
|
-
|
|
35172
|
-
|
|
35173
|
-
|
|
35174
|
-
|
|
35175
|
-
|
|
35342
|
+
async function readPersistedQueue(queueKey) {
|
|
35343
|
+
return withCliSqlite((db) => {
|
|
35344
|
+
const row = db.get("SELECT queue_key, updated_at, turns_json FROM prompt_queue WHERE queue_key = ?", [
|
|
35345
|
+
queueKey
|
|
35346
|
+
]);
|
|
35347
|
+
if (!row) return null;
|
|
35348
|
+
try {
|
|
35349
|
+
const turns = JSON.parse(row.turns_json);
|
|
35350
|
+
if (!Array.isArray(turns)) return null;
|
|
35351
|
+
return { queueKey: row.queue_key, updatedAt: row.updated_at, turns };
|
|
35352
|
+
} catch {
|
|
35353
|
+
return null;
|
|
35354
|
+
}
|
|
35355
|
+
});
|
|
35176
35356
|
}
|
|
35177
|
-
function writePersistedQueue(file2) {
|
|
35178
|
-
|
|
35179
|
-
|
|
35180
|
-
|
|
35181
|
-
|
|
35182
|
-
|
|
35183
|
-
|
|
35357
|
+
async function writePersistedQueue(file2) {
|
|
35358
|
+
await withCliSqlite((db) => {
|
|
35359
|
+
db.run(
|
|
35360
|
+
`INSERT INTO prompt_queue (queue_key, updated_at, turns_json) VALUES (?, ?, ?)
|
|
35361
|
+
ON CONFLICT(queue_key) DO UPDATE SET updated_at = excluded.updated_at, turns_json = excluded.turns_json`,
|
|
35362
|
+
[file2.queueKey, file2.updatedAt, JSON.stringify(file2.turns)]
|
|
35363
|
+
);
|
|
35364
|
+
});
|
|
35184
35365
|
}
|
|
35185
|
-
function mergeServerQueueSnapshot(queueKey, serverTurns) {
|
|
35186
|
-
const prev = readPersistedQueue(queueKey);
|
|
35366
|
+
async function mergeServerQueueSnapshot(queueKey, serverTurns) {
|
|
35367
|
+
const prev = await readPersistedQueue(queueKey);
|
|
35187
35368
|
const turns = [];
|
|
35188
35369
|
for (const raw of serverTurns) {
|
|
35189
35370
|
if (!raw || typeof raw !== "object") continue;
|
|
@@ -35280,12 +35461,12 @@ async function applyPromptQueueStateFromServer(msg, deps) {
|
|
|
35280
35461
|
const getWs = deps.getWs;
|
|
35281
35462
|
for (const [queueKey, serverTurns] of Object.entries(raw)) {
|
|
35282
35463
|
if (!Array.isArray(serverTurns)) continue;
|
|
35283
|
-
const file2 = mergeServerQueueSnapshot(queueKey, serverTurns);
|
|
35284
|
-
writePersistedQueue(file2);
|
|
35464
|
+
const file2 = await mergeServerQueueSnapshot(queueKey, serverTurns);
|
|
35465
|
+
await writePersistedQueue(file2);
|
|
35285
35466
|
}
|
|
35286
35467
|
for (const [queueKey, serverTurns] of Object.entries(raw)) {
|
|
35287
35468
|
if (!Array.isArray(serverTurns)) continue;
|
|
35288
|
-
const file2 = readPersistedQueue(queueKey);
|
|
35469
|
+
const file2 = await readPersistedQueue(queueKey);
|
|
35289
35470
|
if (!file2) continue;
|
|
35290
35471
|
for (const running of file2.turns.filter((t) => t.lastClientState === "running")) {
|
|
35291
35472
|
runIdToQueueKey.set(running.turnId, queueKey);
|
|
@@ -35293,7 +35474,7 @@ async function applyPromptQueueStateFromServer(msg, deps) {
|
|
|
35293
35474
|
}
|
|
35294
35475
|
for (const [queueKey, serverTurns] of Object.entries(raw)) {
|
|
35295
35476
|
if (!Array.isArray(serverTurns)) continue;
|
|
35296
|
-
const file2 = readPersistedQueue(queueKey);
|
|
35477
|
+
const file2 = await readPersistedQueue(queueKey);
|
|
35297
35478
|
if (!file2) continue;
|
|
35298
35479
|
const cancelRow = file2.turns.find((t) => t.serverState === "cancel_requested" && t.lastClientState === "running");
|
|
35299
35480
|
if (cancelRow) {
|
|
@@ -35302,7 +35483,7 @@ async function applyPromptQueueStateFromServer(msg, deps) {
|
|
|
35302
35483
|
deps.log(
|
|
35303
35484
|
`[Queue] server cancel_requested for ${cancelRow.turnId.slice(0, 8)}\u2026 but no local agent run is active (e.g. after CLI restart); marking cancelled and notifying bridge.`
|
|
35304
35485
|
);
|
|
35305
|
-
finalizePromptTurnOnBridge(deps.getWs, cancelRow.turnId, false, { terminalClientState: "cancelled" });
|
|
35486
|
+
await finalizePromptTurnOnBridge(deps.getWs, cancelRow.turnId, false, { terminalClientState: "cancelled" });
|
|
35306
35487
|
const ws = deps.getWs();
|
|
35307
35488
|
if (ws && cancelRow.sessionId) {
|
|
35308
35489
|
sendWsMessage(ws, {
|
|
@@ -35321,19 +35502,19 @@ async function applyPromptQueueStateFromServer(msg, deps) {
|
|
|
35321
35502
|
const startedThisTick = /* @__PURE__ */ new Set();
|
|
35322
35503
|
for (const [queueKey, serverTurns] of Object.entries(raw)) {
|
|
35323
35504
|
if (!Array.isArray(serverTurns)) continue;
|
|
35324
|
-
const file2 = readPersistedQueue(queueKey);
|
|
35505
|
+
const file2 = await readPersistedQueue(queueKey);
|
|
35325
35506
|
if (!file2) continue;
|
|
35326
35507
|
if (hasRunningTurn(file2.turns)) continue;
|
|
35327
35508
|
const next = pickNextRunnableTurn(file2.turns);
|
|
35328
35509
|
if (!next) continue;
|
|
35329
35510
|
if (!await runLocalRevertBeforeQueuedPrompt(next, deps)) {
|
|
35330
35511
|
next.lastClientState = "failed";
|
|
35331
|
-
writePersistedQueue(file2);
|
|
35512
|
+
await writePersistedQueue(file2);
|
|
35332
35513
|
sendPromptQueueClientReport(getWs(), { [queueKey]: [{ turnId: next.turnId, clientState: "failed" }] });
|
|
35333
35514
|
continue;
|
|
35334
35515
|
}
|
|
35335
35516
|
next.lastClientState = "running";
|
|
35336
|
-
writePersistedQueue(file2);
|
|
35517
|
+
await writePersistedQueue(file2);
|
|
35337
35518
|
runIdToQueueKey.set(next.turnId, queueKey);
|
|
35338
35519
|
startedThisTick.add(next.turnId);
|
|
35339
35520
|
report[queueKey] = [{ turnId: next.turnId, clientState: "running" }];
|
|
@@ -35343,7 +35524,7 @@ async function applyPromptQueueStateFromServer(msg, deps) {
|
|
|
35343
35524
|
}
|
|
35344
35525
|
for (const [queueKey, serverTurns] of Object.entries(raw)) {
|
|
35345
35526
|
if (!Array.isArray(serverTurns)) continue;
|
|
35346
|
-
const file2 = readPersistedQueue(queueKey);
|
|
35527
|
+
const file2 = await readPersistedQueue(queueKey);
|
|
35347
35528
|
if (!file2) continue;
|
|
35348
35529
|
const running = file2.turns.find((t) => t.lastClientState === "running");
|
|
35349
35530
|
if (!running || !startedThisTick.has(running.turnId)) continue;
|
|
@@ -35351,17 +35532,17 @@ async function applyPromptQueueStateFromServer(msg, deps) {
|
|
|
35351
35532
|
dispatchLocalPrompt(running, deps);
|
|
35352
35533
|
}
|
|
35353
35534
|
}
|
|
35354
|
-
function finalizePromptTurnOnBridge(getWs, runId, success2, opts) {
|
|
35535
|
+
async function finalizePromptTurnOnBridge(getWs, runId, success2, opts) {
|
|
35355
35536
|
if (!runId) return false;
|
|
35356
35537
|
const queueKey = runIdToQueueKey.get(runId);
|
|
35357
35538
|
runIdToQueueKey.delete(runId);
|
|
35358
35539
|
if (!queueKey) return false;
|
|
35359
|
-
const f = readPersistedQueue(queueKey);
|
|
35540
|
+
const f = await readPersistedQueue(queueKey);
|
|
35360
35541
|
if (!f) return false;
|
|
35361
35542
|
const t = f.turns.find((x) => x.turnId === runId);
|
|
35362
35543
|
if (!t) return false;
|
|
35363
35544
|
t.lastClientState = opts?.terminalClientState ?? (success2 ? "stopped" : "failed");
|
|
35364
|
-
writePersistedQueue(f);
|
|
35545
|
+
await writePersistedQueue(f);
|
|
35365
35546
|
sendPromptQueueClientReport(getWs(), { [queueKey]: [{ turnId: runId, clientState: t.lastClientState }] });
|
|
35366
35547
|
return true;
|
|
35367
35548
|
}
|
|
@@ -35382,12 +35563,13 @@ function createBridgePromptSenders(deps, getWs) {
|
|
|
35382
35563
|
if (result.type === "prompt_result") {
|
|
35383
35564
|
const pr = result;
|
|
35384
35565
|
const cancelled = pr.stopReason === "cancelled";
|
|
35385
|
-
finalizePromptTurnOnBridge(
|
|
35566
|
+
void finalizePromptTurnOnBridge(
|
|
35386
35567
|
getWs,
|
|
35387
35568
|
typeof pr.runId === "string" ? pr.runId : void 0,
|
|
35388
35569
|
pr.success === true,
|
|
35389
35570
|
cancelled ? { terminalClientState: "cancelled" } : void 0
|
|
35390
|
-
)
|
|
35571
|
+
).catch(() => {
|
|
35572
|
+
});
|
|
35391
35573
|
}
|
|
35392
35574
|
};
|
|
35393
35575
|
const sendSessionUpdate = (payload) => {
|
|
@@ -35411,12 +35593,12 @@ function createBridgePromptSenders(deps, getWs) {
|
|
|
35411
35593
|
}
|
|
35412
35594
|
|
|
35413
35595
|
// src/agents/acp/from-bridge/bridge-prompt-preamble.ts
|
|
35414
|
-
import { execFile as
|
|
35415
|
-
import { promisify as
|
|
35416
|
-
var
|
|
35596
|
+
import { execFile as execFile9 } from "node:child_process";
|
|
35597
|
+
import { promisify as promisify9 } from "node:util";
|
|
35598
|
+
var execFileAsync8 = promisify9(execFile9);
|
|
35417
35599
|
async function readGitBranch(cwd) {
|
|
35418
35600
|
try {
|
|
35419
|
-
const { stdout } = await
|
|
35601
|
+
const { stdout } = await execFileAsync8("git", ["branch", "--show-current"], { cwd, maxBuffer: 64 * 1024 });
|
|
35420
35602
|
const b = stdout.trim();
|
|
35421
35603
|
return b || null;
|
|
35422
35604
|
} catch {
|
|
@@ -35946,7 +36128,7 @@ function handleFileBrowserSearch(msg, socket, e2ee) {
|
|
|
35946
36128
|
await yieldToEventLoop();
|
|
35947
36129
|
const q = typeof msg.q === "string" ? msg.q : "";
|
|
35948
36130
|
const cwd = path36.resolve(getBridgeRoot());
|
|
35949
|
-
if (!bridgeFileIndexIsPopulated(cwd)) {
|
|
36131
|
+
if (!await bridgeFileIndexIsPopulated(cwd)) {
|
|
35950
36132
|
const payload2 = {
|
|
35951
36133
|
type: "file_browser_search_response",
|
|
35952
36134
|
id: msg.id,
|
|
@@ -36786,7 +36968,8 @@ import * as path39 from "node:path";
|
|
|
36786
36968
|
// src/agents/capabilities/probe-one-agent-type-for-capabilities.ts
|
|
36787
36969
|
import * as path38 from "node:path";
|
|
36788
36970
|
async function probeOneAgentTypeForCapabilities(params) {
|
|
36789
|
-
const { agentType, cwd, workspaceId, log: log2,
|
|
36971
|
+
const { agentType, cwd, workspaceId, log: log2, reportAgentCapabilities, bridgeReport = true } = params;
|
|
36972
|
+
if (isCliImmediateShutdownRequested()) return false;
|
|
36790
36973
|
const resolved = resolveAgentCommand(agentType);
|
|
36791
36974
|
if (!resolved) return false;
|
|
36792
36975
|
let sqliteChanged = false;
|
|
@@ -36797,11 +36980,13 @@ async function probeOneAgentTypeForCapabilities(params) {
|
|
|
36797
36980
|
reportedRef.done = true;
|
|
36798
36981
|
let changed = false;
|
|
36799
36982
|
try {
|
|
36800
|
-
changed =
|
|
36801
|
-
|
|
36802
|
-
|
|
36803
|
-
|
|
36804
|
-
|
|
36983
|
+
changed = withCliSqliteSync(
|
|
36984
|
+
(db) => upsertCliAgentCapabilityCache(db, {
|
|
36985
|
+
workspaceId,
|
|
36986
|
+
agentType,
|
|
36987
|
+
configOptions: co
|
|
36988
|
+
})
|
|
36989
|
+
);
|
|
36805
36990
|
} catch {
|
|
36806
36991
|
}
|
|
36807
36992
|
sqliteChanged ||= changed;
|
|
@@ -36818,6 +37003,7 @@ async function probeOneAgentTypeForCapabilities(params) {
|
|
|
36818
37003
|
}, 28e3);
|
|
36819
37004
|
killTimer.unref?.();
|
|
36820
37005
|
try {
|
|
37006
|
+
if (isCliImmediateShutdownRequested()) return false;
|
|
36821
37007
|
handle = await resolved.createClient({
|
|
36822
37008
|
command: resolved.command,
|
|
36823
37009
|
cwd: path38.resolve(cwd),
|
|
@@ -36837,7 +37023,7 @@ async function probeOneAgentTypeForCapabilities(params) {
|
|
|
36837
37023
|
onSessionUpdate: () => {
|
|
36838
37024
|
}
|
|
36839
37025
|
});
|
|
36840
|
-
|
|
37026
|
+
if (!await delayMsUnlessShutdownRequested(1200)) return false;
|
|
36841
37027
|
} catch (e) {
|
|
36842
37028
|
log2(
|
|
36843
37029
|
`[Bridge service] Agent capability probe (${agentType}): ${e instanceof Error ? e.message : String(e)}`
|
|
@@ -36859,19 +37045,19 @@ async function probeAgentCapabilitiesForDetectedTypes(params) {
|
|
|
36859
37045
|
cwd,
|
|
36860
37046
|
workspaceId,
|
|
36861
37047
|
log: log2,
|
|
36862
|
-
getDb,
|
|
36863
37048
|
reportAgentCapabilities,
|
|
36864
37049
|
bridgeReport = true,
|
|
36865
37050
|
forceAllTypes = false
|
|
36866
37051
|
} = params;
|
|
36867
37052
|
let changedCount = 0;
|
|
36868
37053
|
for (let i = 0; i < agentTypes.length; i++) {
|
|
37054
|
+
if (isCliImmediateShutdownRequested()) return changedCount;
|
|
36869
37055
|
if (i > 0) await yieldToEventLoop();
|
|
36870
37056
|
const agentType = agentTypes[i];
|
|
36871
37057
|
if (!agentType.trim()) continue;
|
|
36872
37058
|
if (!forceAllTypes) {
|
|
36873
37059
|
try {
|
|
36874
|
-
if (process.env.BUILDAUTOMATON_FORCE_PROBE_ACP_CAPABILITIES !== "1" &&
|
|
37060
|
+
if (process.env.BUILDAUTOMATON_FORCE_PROBE_ACP_CAPABILITIES !== "1" && await withCliSqlite((db) => hasNonEmptyAgentCapabilityCache(db, workspaceId, agentType))) {
|
|
36875
37061
|
continue;
|
|
36876
37062
|
}
|
|
36877
37063
|
} catch {
|
|
@@ -36882,7 +37068,6 @@ async function probeAgentCapabilitiesForDetectedTypes(params) {
|
|
|
36882
37068
|
cwd,
|
|
36883
37069
|
workspaceId,
|
|
36884
37070
|
log: log2,
|
|
36885
|
-
getDb,
|
|
36886
37071
|
reportAgentCapabilities,
|
|
36887
37072
|
bridgeReport
|
|
36888
37073
|
});
|
|
@@ -36893,60 +37078,64 @@ async function probeAgentCapabilitiesForDetectedTypes(params) {
|
|
|
36893
37078
|
|
|
36894
37079
|
// src/agents/capabilities/warmup-agent-capabilities-on-connect.ts
|
|
36895
37080
|
async function warmupAgentCapabilitiesOnConnect(params) {
|
|
36896
|
-
const { workspaceId, log: log2,
|
|
37081
|
+
const { workspaceId, log: log2, getWs } = params;
|
|
37082
|
+
if (isCliImmediateShutdownRequested()) return;
|
|
36897
37083
|
const cwd = path39.resolve(getBridgeRoot());
|
|
36898
|
-
|
|
36899
|
-
function sendBatchFromCache() {
|
|
37084
|
+
async function sendBatchFromCache() {
|
|
36900
37085
|
const socket = getWs();
|
|
36901
37086
|
if (!socket || socket.readyState !== wrapper_default.OPEN) return;
|
|
36902
37087
|
try {
|
|
36903
|
-
const rows = listCliAgentCapabilityCacheForWorkspace(db, workspaceId);
|
|
37088
|
+
const rows = await withCliSqlite((db) => listCliAgentCapabilityCacheForWorkspace(db, workspaceId));
|
|
36904
37089
|
if (rows.length === 0) return;
|
|
36905
37090
|
sendWsMessage(socket, {
|
|
36906
37091
|
type: "agent_capabilities_batch",
|
|
36907
37092
|
items: rows.map((r) => ({ agentType: r.agentType, configOptions: r.configOptions }))
|
|
36908
37093
|
});
|
|
36909
37094
|
} catch (e) {
|
|
37095
|
+
if (e instanceof CliSqliteInterrupted) return;
|
|
36910
37096
|
log2(
|
|
36911
37097
|
`[Bridge service] Agent capability batch to bridge failed: ${e instanceof Error ? e.message : String(e)}`
|
|
36912
37098
|
);
|
|
36913
37099
|
}
|
|
36914
37100
|
}
|
|
36915
|
-
sendBatchFromCache();
|
|
37101
|
+
await sendBatchFromCache();
|
|
37102
|
+
if (isCliImmediateShutdownRequested()) return;
|
|
36916
37103
|
let types = [];
|
|
36917
37104
|
try {
|
|
36918
37105
|
types = [...await detectLocalAgentTypes()];
|
|
36919
37106
|
} catch (e) {
|
|
36920
37107
|
log2(`[Bridge service] detectLocalAgentTypes failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
36921
37108
|
}
|
|
37109
|
+
if (isCliImmediateShutdownRequested()) return;
|
|
36922
37110
|
try {
|
|
36923
37111
|
const n = await probeAgentCapabilitiesForDetectedTypes({
|
|
36924
37112
|
agentTypes: types,
|
|
36925
37113
|
cwd,
|
|
36926
37114
|
workspaceId,
|
|
36927
37115
|
log: log2,
|
|
36928
|
-
getDb,
|
|
36929
37116
|
bridgeReport: false,
|
|
36930
37117
|
forceAllTypes: false
|
|
36931
37118
|
});
|
|
36932
|
-
if (n > 0) sendBatchFromCache();
|
|
37119
|
+
if (n > 0) await sendBatchFromCache();
|
|
36933
37120
|
} catch (e) {
|
|
37121
|
+
if (e instanceof CliSqliteInterrupted) return;
|
|
36934
37122
|
log2(`[Bridge service] Agent capability probe (missing cache) failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
36935
37123
|
}
|
|
36936
37124
|
void (async () => {
|
|
36937
37125
|
try {
|
|
36938
37126
|
await yieldToEventLoop();
|
|
37127
|
+
if (isCliImmediateShutdownRequested()) return;
|
|
36939
37128
|
const n = await probeAgentCapabilitiesForDetectedTypes({
|
|
36940
37129
|
agentTypes: types,
|
|
36941
37130
|
cwd,
|
|
36942
37131
|
workspaceId,
|
|
36943
37132
|
log: log2,
|
|
36944
|
-
getDb,
|
|
36945
37133
|
bridgeReport: false,
|
|
36946
37134
|
forceAllTypes: true
|
|
36947
37135
|
});
|
|
36948
|
-
if (n > 0) sendBatchFromCache();
|
|
37136
|
+
if (n > 0) await sendBatchFromCache();
|
|
36949
37137
|
} catch (e) {
|
|
37138
|
+
if (e instanceof CliSqliteInterrupted) return;
|
|
36950
37139
|
log2(`[Bridge service] Agent capability lazy refresh failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
36951
37140
|
}
|
|
36952
37141
|
})();
|
|
@@ -36957,7 +37146,7 @@ async function createBridgeConnection(options) {
|
|
|
36957
37146
|
const { apiUrl, workspaceId, justAuthenticated, onAuthInvalid, persistTokens } = options;
|
|
36958
37147
|
const firehoseServerUrl = options.firehoseServerUrl ?? options.proxyServerUrl;
|
|
36959
37148
|
const logFn = options.log ?? log;
|
|
36960
|
-
|
|
37149
|
+
await ensureCliSqliteInitialized({ logLegacyMigration: logFn });
|
|
36961
37150
|
const tokens = {
|
|
36962
37151
|
accessToken: options.authToken,
|
|
36963
37152
|
refreshToken: options.refreshToken
|
|
@@ -36987,12 +37176,15 @@ async function createBridgeConnection(options) {
|
|
|
36987
37176
|
if (!Array.isArray(info.configOptions) || info.configOptions.length === 0) return;
|
|
36988
37177
|
let changed = false;
|
|
36989
37178
|
try {
|
|
36990
|
-
changed =
|
|
36991
|
-
|
|
36992
|
-
|
|
36993
|
-
|
|
36994
|
-
|
|
36995
|
-
|
|
37179
|
+
changed = withCliSqliteSync(
|
|
37180
|
+
(db) => upsertCliAgentCapabilityCache(db, {
|
|
37181
|
+
workspaceId,
|
|
37182
|
+
agentType: info.agentType,
|
|
37183
|
+
configOptions: info.configOptions
|
|
37184
|
+
})
|
|
37185
|
+
);
|
|
37186
|
+
} catch (e) {
|
|
37187
|
+
if (e instanceof CliSqliteInterrupted) return;
|
|
36996
37188
|
}
|
|
36997
37189
|
if (!changed) return;
|
|
36998
37190
|
const socket = getWs();
|
|
@@ -37044,7 +37236,6 @@ async function createBridgeConnection(options) {
|
|
|
37044
37236
|
await warmupAgentCapabilitiesOnConnect({
|
|
37045
37237
|
workspaceId,
|
|
37046
37238
|
log: logFn,
|
|
37047
|
-
getDb: getCliDatabase,
|
|
37048
37239
|
getWs
|
|
37049
37240
|
});
|
|
37050
37241
|
},
|
|
@@ -37076,6 +37267,7 @@ async function createBridgeConnection(options) {
|
|
|
37076
37267
|
const stopFileIndexWatcher = startFileIndexWatcher(getBridgeRoot());
|
|
37077
37268
|
return {
|
|
37078
37269
|
close: async () => {
|
|
37270
|
+
requestCliImmediateShutdown();
|
|
37079
37271
|
stopFileIndexWatcher();
|
|
37080
37272
|
bridgeHeartbeat.stop();
|
|
37081
37273
|
await closeBridgeConnection(state, acpManager, devServerManager, logFn);
|
|
@@ -37149,47 +37341,60 @@ async function runConnectedBridge(options, restartWithoutAuth) {
|
|
|
37149
37341
|
} = options;
|
|
37150
37342
|
const firehoseServerUrl = options.firehoseServerUrl ?? options.proxyServerUrl;
|
|
37151
37343
|
let cleanupKeyCommand;
|
|
37152
|
-
|
|
37153
|
-
apiUrl,
|
|
37154
|
-
workspaceId,
|
|
37155
|
-
authToken,
|
|
37156
|
-
refreshToken,
|
|
37157
|
-
firehoseServerUrl,
|
|
37158
|
-
justAuthenticated,
|
|
37159
|
-
worktreesRootPath,
|
|
37160
|
-
e2eCertificate,
|
|
37161
|
-
log,
|
|
37162
|
-
persistTokens: (t) => {
|
|
37163
|
-
writeConfigForApi(apiUrl, {
|
|
37164
|
-
workspaceId,
|
|
37165
|
-
token: t.token,
|
|
37166
|
-
refreshToken: t.refreshToken
|
|
37167
|
-
});
|
|
37168
|
-
},
|
|
37169
|
-
onAuthInvalid: () => {
|
|
37170
|
-
cleanupKeyCommand?.();
|
|
37171
|
-
log("[Bridge service] Access token invalid or revoked; re-authenticating\u2026");
|
|
37172
|
-
clearConfigForApi(apiUrl);
|
|
37173
|
-
void handle.close().then(() => {
|
|
37174
|
-
void restartWithoutAuth({ apiUrl, firehoseServerUrl, worktreesRootPath, e2eCertificate });
|
|
37175
|
-
});
|
|
37176
|
-
}
|
|
37177
|
-
});
|
|
37344
|
+
let bridgeClose = null;
|
|
37178
37345
|
const onSignal = (kind) => {
|
|
37346
|
+
requestCliImmediateShutdown();
|
|
37179
37347
|
cleanupKeyCommand?.();
|
|
37180
37348
|
logImmediate(
|
|
37181
37349
|
kind === "interrupt" ? "Keyboard interrupt (Ctrl+C) \u2014 stopping\u2026" : "Stop requested \u2014 shutting down\u2026"
|
|
37182
37350
|
);
|
|
37183
|
-
|
|
37184
|
-
void
|
|
37185
|
-
|
|
37186
|
-
|
|
37187
|
-
|
|
37351
|
+
if (bridgeClose) {
|
|
37352
|
+
void bridgeClose().then(() => process.exit(0));
|
|
37353
|
+
return;
|
|
37354
|
+
}
|
|
37355
|
+
process.exit(0);
|
|
37188
37356
|
};
|
|
37189
37357
|
const onSigInt = () => onSignal("interrupt");
|
|
37190
37358
|
const onSigTerm = () => onSignal("stop");
|
|
37191
37359
|
process.on("SIGINT", onSigInt);
|
|
37192
37360
|
process.on("SIGTERM", onSigTerm);
|
|
37361
|
+
let handle;
|
|
37362
|
+
try {
|
|
37363
|
+
handle = await createBridgeConnection({
|
|
37364
|
+
apiUrl,
|
|
37365
|
+
workspaceId,
|
|
37366
|
+
authToken,
|
|
37367
|
+
refreshToken,
|
|
37368
|
+
firehoseServerUrl,
|
|
37369
|
+
justAuthenticated,
|
|
37370
|
+
worktreesRootPath,
|
|
37371
|
+
e2eCertificate,
|
|
37372
|
+
log,
|
|
37373
|
+
persistTokens: (t) => {
|
|
37374
|
+
writeConfigForApi(apiUrl, {
|
|
37375
|
+
workspaceId,
|
|
37376
|
+
token: t.token,
|
|
37377
|
+
refreshToken: t.refreshToken
|
|
37378
|
+
});
|
|
37379
|
+
},
|
|
37380
|
+
onAuthInvalid: () => {
|
|
37381
|
+
cleanupKeyCommand?.();
|
|
37382
|
+
log("[Bridge service] Access token invalid or revoked; re-authenticating\u2026");
|
|
37383
|
+
clearConfigForApi(apiUrl);
|
|
37384
|
+
void handle.close().then(() => {
|
|
37385
|
+
void restartWithoutAuth({ apiUrl, firehoseServerUrl, worktreesRootPath, e2eCertificate });
|
|
37386
|
+
});
|
|
37387
|
+
}
|
|
37388
|
+
});
|
|
37389
|
+
} catch (e) {
|
|
37390
|
+
process.off("SIGINT", onSigInt);
|
|
37391
|
+
process.off("SIGTERM", onSigTerm);
|
|
37392
|
+
if (e instanceof CliSqliteInterrupted) {
|
|
37393
|
+
process.exit(0);
|
|
37394
|
+
}
|
|
37395
|
+
throw e;
|
|
37396
|
+
}
|
|
37397
|
+
bridgeClose = () => handle.close();
|
|
37193
37398
|
if (e2eCertificate) {
|
|
37194
37399
|
let openingCertificate = false;
|
|
37195
37400
|
cleanupKeyCommand = installE2eCertificateKeyCommand({
|
|
@@ -37234,6 +37439,7 @@ async function runBridge(options) {
|
|
|
37234
37439
|
}
|
|
37235
37440
|
});
|
|
37236
37441
|
const onSignal = (kind) => {
|
|
37442
|
+
requestCliImmediateShutdown();
|
|
37237
37443
|
logImmediate(
|
|
37238
37444
|
kind === "interrupt" ? "Keyboard interrupt (Ctrl+C) \u2014 stopping\u2026" : "Stop requested \u2014 shutting down\u2026"
|
|
37239
37445
|
);
|