@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/cli.js
CHANGED
|
@@ -25064,7 +25064,7 @@ var {
|
|
|
25064
25064
|
} = import_index.default;
|
|
25065
25065
|
|
|
25066
25066
|
// src/cli-version.ts
|
|
25067
|
-
var CLI_VERSION = "0.1.
|
|
25067
|
+
var CLI_VERSION = "0.1.33".length > 0 ? "0.1.33" : "0.0.0-dev";
|
|
25068
25068
|
|
|
25069
25069
|
// src/cli/defaults.ts
|
|
25070
25070
|
var DEFAULT_API_URL = process.env.BUILDAUTOMATON_API_URL ?? "https://api.buildautomaton.com";
|
|
@@ -26696,9 +26696,38 @@ function runPendingAuth(options) {
|
|
|
26696
26696
|
};
|
|
26697
26697
|
}
|
|
26698
26698
|
|
|
26699
|
+
// src/dev-servers/manager/dev-server-constants.ts
|
|
26700
|
+
var BRIDGE_CLOSE_DEV_SERVER_GRACE_MS = 0;
|
|
26701
|
+
var BRIDGE_SHUTDOWN_GRACE_MS = 8e3;
|
|
26702
|
+
|
|
26703
|
+
// src/runtime/cli-process-interrupt.ts
|
|
26704
|
+
var cliImmediateShutdownRequested = false;
|
|
26705
|
+
function requestCliImmediateShutdown() {
|
|
26706
|
+
cliImmediateShutdownRequested = true;
|
|
26707
|
+
}
|
|
26708
|
+
function isCliImmediateShutdownRequested() {
|
|
26709
|
+
return cliImmediateShutdownRequested;
|
|
26710
|
+
}
|
|
26711
|
+
async function delayMsUnlessShutdownRequested(ms) {
|
|
26712
|
+
if (ms <= 0) return true;
|
|
26713
|
+
const end = Date.now() + ms;
|
|
26714
|
+
while (Date.now() < end) {
|
|
26715
|
+
if (isCliImmediateShutdownRequested()) return false;
|
|
26716
|
+
const chunk = Math.min(50, end - Date.now());
|
|
26717
|
+
if (chunk <= 0) break;
|
|
26718
|
+
await new Promise((r) => setTimeout(r, chunk));
|
|
26719
|
+
}
|
|
26720
|
+
return !isCliImmediateShutdownRequested();
|
|
26721
|
+
}
|
|
26722
|
+
|
|
26699
26723
|
// src/sqlite/cli-database.ts
|
|
26700
26724
|
import sqliteWasm from "node-sqlite3-wasm";
|
|
26701
26725
|
|
|
26726
|
+
// src/runtime/yield-to-event-loop.ts
|
|
26727
|
+
function yieldToEventLoop() {
|
|
26728
|
+
return new Promise((resolve20) => setImmediate(resolve20));
|
|
26729
|
+
}
|
|
26730
|
+
|
|
26702
26731
|
// src/sqlite/cli-sqlite-paths.ts
|
|
26703
26732
|
import fs8 from "node:fs";
|
|
26704
26733
|
import path5 from "node:path";
|
|
@@ -26991,6 +27020,29 @@ function migrateCliSqlite(db) {
|
|
|
26991
27020
|
|
|
26992
27021
|
// src/sqlite/cli-database.ts
|
|
26993
27022
|
var { Database: SqliteDatabase } = sqliteWasm;
|
|
27023
|
+
var CLI_SQLITE_SYNC_RETRY_MAX = 40;
|
|
27024
|
+
var CLI_SQLITE_ASYNC_RETRY_MAX = 60;
|
|
27025
|
+
var CLI_SQLITE_ASYNC_BASE_DELAY_MS = 20;
|
|
27026
|
+
var CliSqliteInterrupted = class extends Error {
|
|
27027
|
+
name = "CliSqliteInterrupted";
|
|
27028
|
+
constructor() {
|
|
27029
|
+
super("CLI SQLite interrupted (shutdown)");
|
|
27030
|
+
}
|
|
27031
|
+
};
|
|
27032
|
+
function applyCliSqliteConcurrencyPragmas(db) {
|
|
27033
|
+
try {
|
|
27034
|
+
db.exec("PRAGMA journal_mode = WAL");
|
|
27035
|
+
} catch {
|
|
27036
|
+
}
|
|
27037
|
+
try {
|
|
27038
|
+
db.run("PRAGMA synchronous = NORMAL");
|
|
27039
|
+
} catch {
|
|
27040
|
+
}
|
|
27041
|
+
try {
|
|
27042
|
+
db.run("PRAGMA busy_timeout = 500");
|
|
27043
|
+
} catch {
|
|
27044
|
+
}
|
|
27045
|
+
}
|
|
26994
27046
|
function applyCliSqliteMemoryPragmas(db) {
|
|
26995
27047
|
try {
|
|
26996
27048
|
db.run("PRAGMA cache_size = -8192");
|
|
@@ -26998,18 +27050,6 @@ function applyCliSqliteMemoryPragmas(db) {
|
|
|
26998
27050
|
} catch {
|
|
26999
27051
|
}
|
|
27000
27052
|
}
|
|
27001
|
-
var openDatabases = /* @__PURE__ */ new Map();
|
|
27002
|
-
var processExitCloseRegistered = false;
|
|
27003
|
-
function registerProcessExitSqliteClose() {
|
|
27004
|
-
if (processExitCloseRegistered) return;
|
|
27005
|
-
processExitCloseRegistered = true;
|
|
27006
|
-
process.once("exit", () => {
|
|
27007
|
-
for (const db of openDatabases.values()) {
|
|
27008
|
-
safeCloseCliSqliteDatabase(db);
|
|
27009
|
-
}
|
|
27010
|
-
openDatabases.clear();
|
|
27011
|
-
});
|
|
27012
|
-
}
|
|
27013
27053
|
function safeCloseCliSqliteDatabase(db) {
|
|
27014
27054
|
if (db == null) return;
|
|
27015
27055
|
try {
|
|
@@ -27018,22 +27058,49 @@ function safeCloseCliSqliteDatabase(db) {
|
|
|
27018
27058
|
}
|
|
27019
27059
|
}
|
|
27020
27060
|
function closeAllCliSqliteConnections() {
|
|
27021
|
-
|
|
27022
|
-
|
|
27061
|
+
}
|
|
27062
|
+
function isCliSqliteLockError(e) {
|
|
27063
|
+
if (e instanceof CliSqliteInterrupted) return false;
|
|
27064
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
27065
|
+
const lower = msg.toLowerCase();
|
|
27066
|
+
return lower.includes("database is locked") || lower.includes("sqlite_busy") || lower.includes("sqlite3_busy") || lower.includes("database") && lower.includes("locked");
|
|
27067
|
+
}
|
|
27068
|
+
function syncSleepMs(ms) {
|
|
27069
|
+
if (ms <= 0) return;
|
|
27070
|
+
const deadline = Date.now() + ms;
|
|
27071
|
+
while (Date.now() < deadline) {
|
|
27072
|
+
if (isCliImmediateShutdownRequested()) throw new CliSqliteInterrupted();
|
|
27073
|
+
const chunk = Math.min(80, deadline - Date.now());
|
|
27074
|
+
if (chunk <= 0) break;
|
|
27075
|
+
try {
|
|
27076
|
+
const sab = new SharedArrayBuffer(4);
|
|
27077
|
+
const ia = new Int32Array(sab);
|
|
27078
|
+
Atomics.wait(ia, 0, 0, chunk);
|
|
27079
|
+
} catch {
|
|
27080
|
+
const end = Date.now() + chunk;
|
|
27081
|
+
while (Date.now() < end) {
|
|
27082
|
+
if (isCliImmediateShutdownRequested()) throw new CliSqliteInterrupted();
|
|
27083
|
+
}
|
|
27084
|
+
}
|
|
27023
27085
|
}
|
|
27024
|
-
openDatabases.clear();
|
|
27025
27086
|
}
|
|
27026
|
-
function
|
|
27027
|
-
const
|
|
27028
|
-
|
|
27029
|
-
|
|
27030
|
-
|
|
27031
|
-
|
|
27032
|
-
|
|
27087
|
+
async function asyncDelayMsInterruptible(ms) {
|
|
27088
|
+
const end = Date.now() + ms;
|
|
27089
|
+
while (Date.now() < end) {
|
|
27090
|
+
if (isCliImmediateShutdownRequested()) throw new CliSqliteInterrupted();
|
|
27091
|
+
const chunk = Math.min(50, end - Date.now());
|
|
27092
|
+
if (chunk <= 0) break;
|
|
27093
|
+
await new Promise((r) => setTimeout(r, chunk));
|
|
27094
|
+
await yieldToEventLoop();
|
|
27033
27095
|
}
|
|
27096
|
+
}
|
|
27097
|
+
function openCliSqliteConnection(options) {
|
|
27098
|
+
if (isCliImmediateShutdownRequested()) throw new CliSqliteInterrupted();
|
|
27099
|
+
const sqlitePath = getCliSqlitePath();
|
|
27034
27100
|
ensureCliSqliteParentDir(sqlitePath);
|
|
27035
27101
|
const db = new SqliteDatabase(sqlitePath);
|
|
27036
27102
|
try {
|
|
27103
|
+
applyCliSqliteConcurrencyPragmas(db);
|
|
27037
27104
|
applyCliSqliteMemoryPragmas(db);
|
|
27038
27105
|
migrateCliSqlite(db);
|
|
27039
27106
|
importCliSqliteLegacyDiskData(db, options?.logLegacyMigration);
|
|
@@ -27041,16 +27108,62 @@ function getCliDatabase(options) {
|
|
|
27041
27108
|
safeCloseCliSqliteDatabase(db);
|
|
27042
27109
|
throw e;
|
|
27043
27110
|
}
|
|
27044
|
-
openDatabases.set(sqlitePath, db);
|
|
27045
|
-
registerProcessExitSqliteClose();
|
|
27046
27111
|
return db;
|
|
27047
27112
|
}
|
|
27113
|
+
function withCliSqliteSync(fn, options) {
|
|
27114
|
+
for (let attempt = 1; attempt <= CLI_SQLITE_SYNC_RETRY_MAX; attempt++) {
|
|
27115
|
+
if (isCliImmediateShutdownRequested()) throw new CliSqliteInterrupted();
|
|
27116
|
+
let db;
|
|
27117
|
+
try {
|
|
27118
|
+
db = openCliSqliteConnection(options);
|
|
27119
|
+
try {
|
|
27120
|
+
return fn(db);
|
|
27121
|
+
} finally {
|
|
27122
|
+
safeCloseCliSqliteDatabase(db);
|
|
27123
|
+
}
|
|
27124
|
+
} catch (e) {
|
|
27125
|
+
safeCloseCliSqliteDatabase(db);
|
|
27126
|
+
if (e instanceof CliSqliteInterrupted) throw e;
|
|
27127
|
+
if (!isCliSqliteLockError(e) || attempt === CLI_SQLITE_SYNC_RETRY_MAX) throw e;
|
|
27128
|
+
syncSleepMs(Math.min(500, 12 * attempt));
|
|
27129
|
+
}
|
|
27130
|
+
}
|
|
27131
|
+
throw new Error("withCliSqliteSync: exhausted retries");
|
|
27132
|
+
}
|
|
27133
|
+
async function withCliSqlite(fn, options) {
|
|
27134
|
+
let lastError;
|
|
27135
|
+
for (let attempt = 1; attempt <= CLI_SQLITE_ASYNC_RETRY_MAX; attempt++) {
|
|
27136
|
+
if (isCliImmediateShutdownRequested()) throw new CliSqliteInterrupted();
|
|
27137
|
+
let db;
|
|
27138
|
+
try {
|
|
27139
|
+
db = openCliSqliteConnection(options);
|
|
27140
|
+
try {
|
|
27141
|
+
return await Promise.resolve(fn(db));
|
|
27142
|
+
} finally {
|
|
27143
|
+
safeCloseCliSqliteDatabase(db);
|
|
27144
|
+
}
|
|
27145
|
+
} catch (e) {
|
|
27146
|
+
lastError = e;
|
|
27147
|
+
safeCloseCliSqliteDatabase(db);
|
|
27148
|
+
if (e instanceof CliSqliteInterrupted) throw e;
|
|
27149
|
+
if (!isCliSqliteLockError(e) || attempt === CLI_SQLITE_ASYNC_RETRY_MAX) throw e;
|
|
27150
|
+
const delayMs = Math.min(600, CLI_SQLITE_ASYNC_BASE_DELAY_MS * attempt);
|
|
27151
|
+
await asyncDelayMsInterruptible(delayMs);
|
|
27152
|
+
await yieldToEventLoop();
|
|
27153
|
+
}
|
|
27154
|
+
}
|
|
27155
|
+
if (lastError instanceof Error) throw lastError;
|
|
27156
|
+
throw new Error(String(lastError));
|
|
27157
|
+
}
|
|
27158
|
+
async function ensureCliSqliteInitialized(options) {
|
|
27159
|
+
await withCliSqlite(() => void 0, options);
|
|
27160
|
+
}
|
|
27048
27161
|
|
|
27049
27162
|
// src/connection/close-bridge-connection.ts
|
|
27050
27163
|
async function closeBridgeConnection(state, acpManager, devServerManager, log2) {
|
|
27164
|
+
requestCliImmediateShutdown();
|
|
27051
27165
|
const say = log2 ?? logImmediate;
|
|
27052
27166
|
say("Cleaning up connections\u2026");
|
|
27053
|
-
await new Promise((resolve20) => setImmediate(resolve20));
|
|
27054
27167
|
state.closedByUser = true;
|
|
27055
27168
|
clearReconnectQuietTimer(state.mainQuiet);
|
|
27056
27169
|
clearReconnectQuietTimer(state.firehoseQuiet);
|
|
@@ -27087,7 +27200,7 @@ async function closeBridgeConnection(state, acpManager, devServerManager, log2)
|
|
|
27087
27200
|
}
|
|
27088
27201
|
if (devServerManager) {
|
|
27089
27202
|
say("Stopping local dev server processes\u2026");
|
|
27090
|
-
await devServerManager.shutdownAllGraceful();
|
|
27203
|
+
await devServerManager.shutdownAllGraceful({ graceMs: BRIDGE_CLOSE_DEV_SERVER_GRACE_MS });
|
|
27091
27204
|
}
|
|
27092
27205
|
try {
|
|
27093
27206
|
closeAllCliSqliteConnections();
|
|
@@ -32866,16 +32979,38 @@ __export(claude_code_acp_client_exports, {
|
|
|
32866
32979
|
createClaudeCodeAcpClient: () => createClaudeCodeAcpClient,
|
|
32867
32980
|
detectLocalAgentPresence: () => detectLocalAgentPresence
|
|
32868
32981
|
});
|
|
32869
|
-
import { execFile as execFile9 } from "node:child_process";
|
|
32870
|
-
import { promisify as promisify9 } from "node:util";
|
|
32871
32982
|
|
|
32872
32983
|
// src/agents/acp/clients/detect-command-on-path.ts
|
|
32873
32984
|
import { execFile as execFile8 } from "node:child_process";
|
|
32874
32985
|
import { promisify as promisify8 } from "node:util";
|
|
32875
32986
|
var execFileAsync7 = promisify8(execFile8);
|
|
32876
|
-
|
|
32987
|
+
var COMMAND_ON_PATH_PROBE_TIMEOUT_MS = 750;
|
|
32988
|
+
async function execFileShutdownAware(file2, args, timeoutMs) {
|
|
32989
|
+
if (isCliImmediateShutdownRequested()) throw new Error("shutdown");
|
|
32990
|
+
const ac = new AbortController();
|
|
32991
|
+
const shutdownPoll = setInterval(() => {
|
|
32992
|
+
if (isCliImmediateShutdownRequested()) ac.abort();
|
|
32993
|
+
}, 50);
|
|
32994
|
+
shutdownPoll.unref?.();
|
|
32995
|
+
try {
|
|
32996
|
+
await execFileAsync7(file2, args, { timeout: timeoutMs, signal: ac.signal });
|
|
32997
|
+
} finally {
|
|
32998
|
+
clearInterval(shutdownPoll);
|
|
32999
|
+
}
|
|
33000
|
+
}
|
|
33001
|
+
async function isCommandOnPath(command, timeoutMs = COMMAND_ON_PATH_PROBE_TIMEOUT_MS) {
|
|
33002
|
+
if (isCliImmediateShutdownRequested()) return false;
|
|
33003
|
+
try {
|
|
33004
|
+
await execFileShutdownAware("which", [command], timeoutMs);
|
|
33005
|
+
return true;
|
|
33006
|
+
} catch {
|
|
33007
|
+
return false;
|
|
33008
|
+
}
|
|
33009
|
+
}
|
|
33010
|
+
async function execProbeShutdownAware(file2, args, timeoutMs) {
|
|
33011
|
+
if (isCliImmediateShutdownRequested()) return false;
|
|
32877
33012
|
try {
|
|
32878
|
-
await
|
|
33013
|
+
await execFileShutdownAware(file2, args, timeoutMs);
|
|
32879
33014
|
return true;
|
|
32880
33015
|
} catch {
|
|
32881
33016
|
return false;
|
|
@@ -33636,16 +33771,10 @@ async function createSdkStdioAcpClient(options) {
|
|
|
33636
33771
|
}
|
|
33637
33772
|
|
|
33638
33773
|
// src/agents/acp/clients/claude-code-acp-client.ts
|
|
33639
|
-
var execFileAsync8 = promisify9(execFile9);
|
|
33640
33774
|
var BACKEND_LOCAL_AGENT_TYPE = "claude-code";
|
|
33641
33775
|
async function detectLocalAgentPresence() {
|
|
33642
33776
|
if (await isCommandOnPath("claude")) return true;
|
|
33643
|
-
|
|
33644
|
-
await execFileAsync8("npx", ["--yes", "@anthropic-ai/claude-code", "--version"], { timeout: 25e3 });
|
|
33645
|
-
return true;
|
|
33646
|
-
} catch {
|
|
33647
|
-
return false;
|
|
33648
|
-
}
|
|
33777
|
+
return execProbeShutdownAware("npx", ["--yes", "@anthropic-ai/claude-code", "--version"], 3e3);
|
|
33649
33778
|
}
|
|
33650
33779
|
function buildClaudeCodeAcpSpawnCommand(base, _sessionMode) {
|
|
33651
33780
|
return [...base];
|
|
@@ -34812,56 +34941,80 @@ function sessionKeyForCloudSessionId(cloudSessionId) {
|
|
|
34812
34941
|
}
|
|
34813
34942
|
function readLocalAgentSessionFile(cloudSessionId) {
|
|
34814
34943
|
try {
|
|
34815
|
-
|
|
34816
|
-
|
|
34817
|
-
|
|
34818
|
-
|
|
34819
|
-
|
|
34820
|
-
|
|
34821
|
-
|
|
34822
|
-
|
|
34823
|
-
|
|
34824
|
-
|
|
34825
|
-
|
|
34826
|
-
|
|
34827
|
-
|
|
34828
|
-
|
|
34944
|
+
return withCliSqliteSync((db) => {
|
|
34945
|
+
const key = sessionKeyForCloudSessionId(cloudSessionId);
|
|
34946
|
+
const row = db.get(
|
|
34947
|
+
"SELECT acp_session_id, backend_agent_type, config_options_json, updated_at FROM agent_session WHERE session_key = ?",
|
|
34948
|
+
[key]
|
|
34949
|
+
);
|
|
34950
|
+
if (!row) return null;
|
|
34951
|
+
let configOptions = null;
|
|
34952
|
+
if (row.config_options_json != null && row.config_options_json !== "") {
|
|
34953
|
+
try {
|
|
34954
|
+
const parsed = JSON.parse(row.config_options_json);
|
|
34955
|
+
configOptions = Array.isArray(parsed) ? parsed : null;
|
|
34956
|
+
} catch {
|
|
34957
|
+
configOptions = null;
|
|
34958
|
+
}
|
|
34829
34959
|
}
|
|
34830
|
-
|
|
34831
|
-
|
|
34832
|
-
|
|
34833
|
-
|
|
34834
|
-
|
|
34835
|
-
|
|
34836
|
-
|
|
34837
|
-
};
|
|
34960
|
+
return {
|
|
34961
|
+
v: 1,
|
|
34962
|
+
acpSessionId: row.acp_session_id,
|
|
34963
|
+
backendAgentType: row.backend_agent_type,
|
|
34964
|
+
configOptions,
|
|
34965
|
+
updatedAt: row.updated_at
|
|
34966
|
+
};
|
|
34967
|
+
});
|
|
34838
34968
|
} catch {
|
|
34839
34969
|
return null;
|
|
34840
34970
|
}
|
|
34841
34971
|
}
|
|
34842
34972
|
function writeLocalAgentSessionFile(cloudSessionId, patch) {
|
|
34843
34973
|
try {
|
|
34844
|
-
|
|
34845
|
-
|
|
34846
|
-
|
|
34847
|
-
|
|
34848
|
-
|
|
34849
|
-
|
|
34850
|
-
|
|
34851
|
-
|
|
34852
|
-
|
|
34853
|
-
|
|
34854
|
-
|
|
34855
|
-
|
|
34856
|
-
|
|
34857
|
-
|
|
34858
|
-
|
|
34859
|
-
|
|
34860
|
-
|
|
34861
|
-
|
|
34862
|
-
|
|
34863
|
-
|
|
34864
|
-
|
|
34974
|
+
withCliSqliteSync((db) => {
|
|
34975
|
+
const key = sessionKeyForCloudSessionId(cloudSessionId);
|
|
34976
|
+
const prevRow = db.get(
|
|
34977
|
+
"SELECT acp_session_id, backend_agent_type, config_options_json, updated_at FROM agent_session WHERE session_key = ?",
|
|
34978
|
+
[key]
|
|
34979
|
+
);
|
|
34980
|
+
let prev = null;
|
|
34981
|
+
if (prevRow) {
|
|
34982
|
+
let configOptions = null;
|
|
34983
|
+
if (prevRow.config_options_json != null && prevRow.config_options_json !== "") {
|
|
34984
|
+
try {
|
|
34985
|
+
const parsed = JSON.parse(prevRow.config_options_json);
|
|
34986
|
+
configOptions = Array.isArray(parsed) ? parsed : null;
|
|
34987
|
+
} catch {
|
|
34988
|
+
configOptions = null;
|
|
34989
|
+
}
|
|
34990
|
+
}
|
|
34991
|
+
prev = {
|
|
34992
|
+
v: 1,
|
|
34993
|
+
acpSessionId: prevRow.acp_session_id,
|
|
34994
|
+
backendAgentType: prevRow.backend_agent_type,
|
|
34995
|
+
configOptions,
|
|
34996
|
+
updatedAt: prevRow.updated_at
|
|
34997
|
+
};
|
|
34998
|
+
}
|
|
34999
|
+
const next = {
|
|
35000
|
+
v: 1,
|
|
35001
|
+
acpSessionId: patch.acpSessionId !== void 0 ? patch.acpSessionId : prev?.acpSessionId ?? null,
|
|
35002
|
+
backendAgentType: patch.backendAgentType !== void 0 ? patch.backendAgentType : prev?.backendAgentType ?? null,
|
|
35003
|
+
configOptions: patch.configOptions !== void 0 ? patch.configOptions : prev?.configOptions ?? null,
|
|
35004
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
35005
|
+
};
|
|
35006
|
+
const configJson = next.configOptions != null ? JSON.stringify(next.configOptions) : null;
|
|
35007
|
+
db.run(
|
|
35008
|
+
`INSERT INTO agent_session (session_key, acp_session_id, backend_agent_type, config_options_json, updated_at)
|
|
35009
|
+
VALUES (?, ?, ?, ?, ?)
|
|
35010
|
+
ON CONFLICT(session_key) DO UPDATE SET
|
|
35011
|
+
acp_session_id = excluded.acp_session_id,
|
|
35012
|
+
backend_agent_type = excluded.backend_agent_type,
|
|
35013
|
+
config_options_json = excluded.config_options_json,
|
|
35014
|
+
updated_at = excluded.updated_at`,
|
|
35015
|
+
[key, next.acpSessionId, next.backendAgentType, configJson, next.updatedAt]
|
|
35016
|
+
);
|
|
35017
|
+
});
|
|
34865
35018
|
} catch {
|
|
34866
35019
|
}
|
|
34867
35020
|
}
|
|
@@ -36444,41 +36597,45 @@ function getCwdHashForFileIndex(resolvedCwd) {
|
|
|
36444
36597
|
// src/files/index/build-file-index.ts
|
|
36445
36598
|
import path29 from "node:path";
|
|
36446
36599
|
|
|
36447
|
-
// src/runtime/yield-to-event-loop.ts
|
|
36448
|
-
function yieldToEventLoop() {
|
|
36449
|
-
return new Promise((resolve20) => setImmediate(resolve20));
|
|
36450
|
-
}
|
|
36451
|
-
|
|
36452
36600
|
// src/files/index/walk-workspace-tree.ts
|
|
36453
36601
|
import fs25 from "node:fs";
|
|
36454
36602
|
import path28 from "node:path";
|
|
36455
36603
|
function shouldSkipWorkspaceWalkEntry(name) {
|
|
36456
36604
|
return name.startsWith(".");
|
|
36457
36605
|
}
|
|
36458
|
-
function
|
|
36606
|
+
async function walkWorkspaceTreeAsync(dir, baseDir, onFile, state) {
|
|
36459
36607
|
let names;
|
|
36460
36608
|
try {
|
|
36461
|
-
names = fs25.
|
|
36609
|
+
names = await fs25.promises.readdir(dir);
|
|
36462
36610
|
} catch {
|
|
36463
36611
|
return;
|
|
36464
36612
|
}
|
|
36465
36613
|
for (const name of names) {
|
|
36466
36614
|
if (shouldSkipWorkspaceWalkEntry(name)) continue;
|
|
36615
|
+
if (state.n > 0 && state.n % INDEX_WORK_YIELD_EVERY === 0) {
|
|
36616
|
+
if (isCliImmediateShutdownRequested()) throw new CliSqliteInterrupted();
|
|
36617
|
+
await yieldToEventLoop();
|
|
36618
|
+
if (isCliImmediateShutdownRequested()) throw new CliSqliteInterrupted();
|
|
36619
|
+
}
|
|
36620
|
+
state.n++;
|
|
36467
36621
|
const full = path28.join(dir, name);
|
|
36468
36622
|
let stat3;
|
|
36469
36623
|
try {
|
|
36470
|
-
stat3 = fs25.
|
|
36624
|
+
stat3 = await fs25.promises.stat(full);
|
|
36471
36625
|
} catch {
|
|
36472
36626
|
continue;
|
|
36473
36627
|
}
|
|
36474
36628
|
const relative5 = path28.relative(baseDir, full).replace(/\\/g, "/");
|
|
36475
36629
|
if (stat3.isDirectory()) {
|
|
36476
|
-
|
|
36630
|
+
await walkWorkspaceTreeAsync(full, baseDir, onFile, state);
|
|
36477
36631
|
} else if (stat3.isFile()) {
|
|
36478
36632
|
onFile(relative5);
|
|
36479
36633
|
}
|
|
36480
36634
|
}
|
|
36481
36635
|
}
|
|
36636
|
+
function createWalkYieldState() {
|
|
36637
|
+
return { n: 0 };
|
|
36638
|
+
}
|
|
36482
36639
|
|
|
36483
36640
|
// src/files/index/file-index-sqlite-lock.ts
|
|
36484
36641
|
import fs26 from "node:fs";
|
|
@@ -36511,48 +36668,60 @@ function withFileIndexSqliteLock(fn) {
|
|
|
36511
36668
|
}
|
|
36512
36669
|
|
|
36513
36670
|
// src/files/index/build-file-index.ts
|
|
36514
|
-
var
|
|
36515
|
-
function
|
|
36516
|
-
|
|
36517
|
-
|
|
36518
|
-
|
|
36519
|
-
|
|
36520
|
-
|
|
36521
|
-
|
|
36522
|
-
db.run("
|
|
36523
|
-
const ins = db.prepare("INSERT INTO file_index_path (cwd_hash, path) VALUES (?, ?)");
|
|
36671
|
+
var FILE_INDEX_INTERRUPT_CHECK_EVERY = 256;
|
|
36672
|
+
function assertNotShutdown() {
|
|
36673
|
+
if (isCliImmediateShutdownRequested()) throw new CliSqliteInterrupted();
|
|
36674
|
+
}
|
|
36675
|
+
function persistFileIndexPaths(resolved, paths) {
|
|
36676
|
+
return withCliSqliteSync((db) => {
|
|
36677
|
+
const h = getCwdHashForFileIndex(resolved);
|
|
36678
|
+
let pathCount = 0;
|
|
36679
|
+
db.run("BEGIN IMMEDIATE");
|
|
36524
36680
|
try {
|
|
36525
|
-
|
|
36526
|
-
|
|
36681
|
+
db.run("DELETE FROM file_index_path WHERE cwd_hash = ?", [h]);
|
|
36682
|
+
const ins = db.prepare("INSERT INTO file_index_path (cwd_hash, path) VALUES (?, ?)");
|
|
36683
|
+
try {
|
|
36684
|
+
let batch = 0;
|
|
36685
|
+
for (const rel of paths) {
|
|
36686
|
+
if (++batch >= FILE_INDEX_INTERRUPT_CHECK_EVERY) {
|
|
36687
|
+
batch = 0;
|
|
36688
|
+
assertNotShutdown();
|
|
36689
|
+
}
|
|
36527
36690
|
ins.run([h, rel]);
|
|
36691
|
+
pathCount += 1;
|
|
36528
36692
|
}
|
|
36529
|
-
|
|
36530
|
-
|
|
36531
|
-
}
|
|
36532
|
-
|
|
36533
|
-
|
|
36534
|
-
|
|
36535
|
-
|
|
36536
|
-
|
|
36537
|
-
|
|
36538
|
-
|
|
36539
|
-
}
|
|
36540
|
-
db.run("COMMIT");
|
|
36541
|
-
} catch (e) {
|
|
36542
|
-
try {
|
|
36543
|
-
db.run("ROLLBACK");
|
|
36544
|
-
} catch {
|
|
36693
|
+
} finally {
|
|
36694
|
+
ins.finalize();
|
|
36695
|
+
}
|
|
36696
|
+
db.run("COMMIT");
|
|
36697
|
+
} catch (e) {
|
|
36698
|
+
try {
|
|
36699
|
+
db.run("ROLLBACK");
|
|
36700
|
+
} catch {
|
|
36701
|
+
}
|
|
36702
|
+
throw e;
|
|
36545
36703
|
}
|
|
36546
|
-
|
|
36547
|
-
}
|
|
36548
|
-
|
|
36704
|
+
return pathCount;
|
|
36705
|
+
});
|
|
36706
|
+
}
|
|
36707
|
+
async function collectWorkspacePathsAsync(resolved) {
|
|
36708
|
+
const paths = [];
|
|
36709
|
+
const state = createWalkYieldState();
|
|
36710
|
+
await walkWorkspaceTreeAsync(resolved, resolved, (rel) => {
|
|
36711
|
+
assertNotShutdown();
|
|
36712
|
+
paths.push(rel);
|
|
36713
|
+
}, state);
|
|
36714
|
+
return paths;
|
|
36549
36715
|
}
|
|
36550
36716
|
async function buildFileIndexAsync(cwd) {
|
|
36551
36717
|
return withFileIndexSqliteLock(async () => {
|
|
36552
36718
|
const resolved = path29.resolve(cwd);
|
|
36553
36719
|
await yieldToEventLoop();
|
|
36554
|
-
|
|
36720
|
+
assertNotShutdown();
|
|
36721
|
+
const paths = await collectWorkspacePathsAsync(resolved);
|
|
36555
36722
|
await yieldToEventLoop();
|
|
36723
|
+
assertNotShutdown();
|
|
36724
|
+
const pathCount = persistFileIndexPaths(resolved, paths);
|
|
36556
36725
|
return { pathCount };
|
|
36557
36726
|
});
|
|
36558
36727
|
}
|
|
@@ -36569,23 +36738,20 @@ function sqliteExprBridgeFileIndexDependencyRank() {
|
|
|
36569
36738
|
function escapeLikePattern(fragment) {
|
|
36570
36739
|
return fragment.replace(/\\/g, "\\\\").replace(/%/g, "\\%").replace(/_/g, "\\_");
|
|
36571
36740
|
}
|
|
36572
|
-
function
|
|
36573
|
-
const db = getCliDatabase();
|
|
36741
|
+
function bridgeFileIndexIsPopulatedWithDb(resolvedCwd, db) {
|
|
36574
36742
|
const h = getCwdHashForFileIndex(resolvedCwd);
|
|
36575
36743
|
const row = db.get("SELECT 1 as ok FROM file_index_path WHERE cwd_hash = ? LIMIT 1", [h]);
|
|
36576
36744
|
return row != null;
|
|
36577
36745
|
}
|
|
36578
|
-
function
|
|
36579
|
-
const db = getCliDatabase();
|
|
36746
|
+
function bridgeFileIndexPathCountWithDb(resolvedCwd, db) {
|
|
36580
36747
|
const h = getCwdHashForFileIndex(resolvedCwd);
|
|
36581
36748
|
const row = db.get("SELECT COUNT(*) as c FROM file_index_path WHERE cwd_hash = ?", [h]);
|
|
36582
36749
|
const c = row?.c ?? 0;
|
|
36583
36750
|
return Number(c);
|
|
36584
36751
|
}
|
|
36585
|
-
function
|
|
36752
|
+
function searchBridgeFilePathsWithDb(resolvedCwd, query, limit, db) {
|
|
36586
36753
|
const q = query.trim().toLowerCase();
|
|
36587
36754
|
if (!q) return [];
|
|
36588
|
-
const db = getCliDatabase();
|
|
36589
36755
|
const h = getCwdHashForFileIndex(resolvedCwd);
|
|
36590
36756
|
const pattern = `%${escapeLikePattern(q)}%`;
|
|
36591
36757
|
const lim = Math.max(0, Math.min(1e4, Math.floor(limit)));
|
|
@@ -36596,9 +36762,15 @@ function searchBridgeFilePaths(resolvedCwd, query, limit = 100) {
|
|
|
36596
36762
|
);
|
|
36597
36763
|
return rows.map((r) => String(r.path));
|
|
36598
36764
|
}
|
|
36765
|
+
async function bridgeFileIndexIsPopulated(resolvedCwd) {
|
|
36766
|
+
return withCliSqlite((db) => bridgeFileIndexIsPopulatedWithDb(resolvedCwd, db));
|
|
36767
|
+
}
|
|
36768
|
+
async function bridgeFileIndexPathCount(resolvedCwd) {
|
|
36769
|
+
return withCliSqlite((db) => bridgeFileIndexPathCountWithDb(resolvedCwd, db));
|
|
36770
|
+
}
|
|
36599
36771
|
async function searchBridgeFilePathsAsync(resolvedCwd, query, limit = 100) {
|
|
36600
36772
|
await yieldToEventLoop();
|
|
36601
|
-
const out =
|
|
36773
|
+
const out = await withCliSqlite((db) => searchBridgeFilePathsWithDb(resolvedCwd, query, limit, db));
|
|
36602
36774
|
if (out.length >= INDEX_WORK_YIELD_EVERY) await yieldToEventLoop();
|
|
36603
36775
|
return out;
|
|
36604
36776
|
}
|
|
@@ -36606,8 +36778,8 @@ async function searchBridgeFilePathsAsync(resolvedCwd, query, limit = 100) {
|
|
|
36606
36778
|
// src/files/index/ensure-file-index.ts
|
|
36607
36779
|
async function ensureFileIndexAsync(cwd) {
|
|
36608
36780
|
const resolved = path30.resolve(cwd);
|
|
36609
|
-
if (bridgeFileIndexIsPopulated(resolved)) {
|
|
36610
|
-
return { fromCache: true, pathCount: bridgeFileIndexPathCount(resolved) };
|
|
36781
|
+
if (await bridgeFileIndexIsPopulated(resolved)) {
|
|
36782
|
+
return { fromCache: true, pathCount: await bridgeFileIndexPathCount(resolved) };
|
|
36611
36783
|
}
|
|
36612
36784
|
return { ...await buildFileIndexAsync(resolved), fromCache: false };
|
|
36613
36785
|
}
|
|
@@ -36653,11 +36825,13 @@ function createFsWatcher(resolved, schedule) {
|
|
|
36653
36825
|
function startFileIndexWatcher(cwd = getBridgeRoot()) {
|
|
36654
36826
|
const resolved = path31.resolve(cwd);
|
|
36655
36827
|
void buildFileIndexAsync(resolved).catch((e) => {
|
|
36828
|
+
if (e instanceof CliSqliteInterrupted) return;
|
|
36656
36829
|
console.error("[file-index] Initial index build failed:", e);
|
|
36657
36830
|
});
|
|
36658
36831
|
let timer = null;
|
|
36659
36832
|
const runRebuild = () => {
|
|
36660
36833
|
void buildFileIndexAsync(resolved).catch((e) => {
|
|
36834
|
+
if (e instanceof CliSqliteInterrupted) return;
|
|
36661
36835
|
console.error("[file-index] Watch rebuild failed:", e);
|
|
36662
36836
|
});
|
|
36663
36837
|
};
|
|
@@ -37191,9 +37365,6 @@ var StreamTail = class {
|
|
|
37191
37365
|
}
|
|
37192
37366
|
};
|
|
37193
37367
|
|
|
37194
|
-
// src/dev-servers/manager/dev-server-constants.ts
|
|
37195
|
-
var BRIDGE_SHUTDOWN_GRACE_MS = 8e3;
|
|
37196
|
-
|
|
37197
37368
|
// src/dev-servers/manager/dev-server-firehose-messages.ts
|
|
37198
37369
|
function buildFirehoseSnapshotMessage(params) {
|
|
37199
37370
|
const payload = {
|
|
@@ -37479,22 +37650,23 @@ var DevServerManager = class {
|
|
|
37479
37650
|
}
|
|
37480
37651
|
this.start(serverId);
|
|
37481
37652
|
}
|
|
37482
|
-
async shutdownAllGraceful() {
|
|
37653
|
+
async shutdownAllGraceful(opts) {
|
|
37654
|
+
const graceMs = opts?.graceMs ?? BRIDGE_SHUTDOWN_GRACE_MS;
|
|
37483
37655
|
const pairs = [...this.processes.entries()];
|
|
37484
37656
|
if (pairs.length === 0) return;
|
|
37485
37657
|
this.log(
|
|
37486
37658
|
`[dev-server] Stopping ${pairs.length} local dev server process${pairs.length === 1 ? "" : "es"}\u2026`
|
|
37487
37659
|
);
|
|
37488
|
-
await Promise.all(pairs.map(([serverId, proc]) => this.gracefulTerminateOrUnknown(serverId, proc)));
|
|
37660
|
+
await Promise.all(pairs.map(([serverId, proc]) => this.gracefulTerminateOrUnknown(serverId, proc, graceMs)));
|
|
37489
37661
|
}
|
|
37490
|
-
async gracefulTerminateOrUnknown(serverId, proc) {
|
|
37662
|
+
async gracefulTerminateOrUnknown(serverId, proc, graceMs) {
|
|
37491
37663
|
const shortId = `${serverId.slice(0, 8)}\u2026`;
|
|
37492
|
-
await sigtermAndWaitForExit(proc,
|
|
37664
|
+
await sigtermAndWaitForExit(proc, graceMs, this.log, shortId);
|
|
37493
37665
|
if (!this.processes.has(serverId) || this.processes.get(serverId) !== proc) {
|
|
37494
37666
|
return;
|
|
37495
37667
|
}
|
|
37496
37668
|
this.bumpGeneration(serverId);
|
|
37497
|
-
forceKillChild(proc, this.log, shortId,
|
|
37669
|
+
forceKillChild(proc, this.log, shortId, graceMs);
|
|
37498
37670
|
this.processes.delete(serverId);
|
|
37499
37671
|
this.clearPoll(serverId);
|
|
37500
37672
|
this.pipedCaptureByServerId.delete(serverId);
|
|
@@ -38089,10 +38261,13 @@ var LOCAL_AGENT_ACP_MODULES = [
|
|
|
38089
38261
|
];
|
|
38090
38262
|
async function detectLocalAgentTypes() {
|
|
38091
38263
|
try {
|
|
38264
|
+
if (isCliImmediateShutdownRequested()) return [];
|
|
38092
38265
|
const out = [];
|
|
38093
38266
|
for (let i = 0; i < LOCAL_AGENT_ACP_MODULES.length; i++) {
|
|
38267
|
+
if (isCliImmediateShutdownRequested()) return out;
|
|
38094
38268
|
if (i > 0) {
|
|
38095
38269
|
await yieldToEventLoop();
|
|
38270
|
+
if (isCliImmediateShutdownRequested()) return out;
|
|
38096
38271
|
}
|
|
38097
38272
|
const mod = LOCAL_AGENT_ACP_MODULES[i];
|
|
38098
38273
|
try {
|
|
@@ -38125,6 +38300,7 @@ function createSendLocalSkillsReport(getWs, logFn) {
|
|
|
38125
38300
|
}
|
|
38126
38301
|
function createReportAutoDetectedAgents(getWs, logFn) {
|
|
38127
38302
|
return async () => {
|
|
38303
|
+
if (isCliImmediateShutdownRequested()) return;
|
|
38128
38304
|
try {
|
|
38129
38305
|
const types = await detectLocalAgentTypes();
|
|
38130
38306
|
const socket = getWs();
|
|
@@ -38224,6 +38400,7 @@ var handleBridgeIdentified = (msg, deps) => {
|
|
|
38224
38400
|
});
|
|
38225
38401
|
setImmediate(() => {
|
|
38226
38402
|
void (async () => {
|
|
38403
|
+
if (isCliImmediateShutdownRequested()) return;
|
|
38227
38404
|
try {
|
|
38228
38405
|
await deps.reportAutoDetectedAgents?.();
|
|
38229
38406
|
} catch (e) {
|
|
@@ -38231,6 +38408,7 @@ var handleBridgeIdentified = (msg, deps) => {
|
|
|
38231
38408
|
`[Bridge service] Auto-detect agents failed: ${e instanceof Error ? e.message : String(e)}`
|
|
38232
38409
|
);
|
|
38233
38410
|
}
|
|
38411
|
+
if (isCliImmediateShutdownRequested()) return;
|
|
38234
38412
|
try {
|
|
38235
38413
|
await deps.warmupAgentCapabilitiesOnConnect?.();
|
|
38236
38414
|
} catch (e) {
|
|
@@ -38241,6 +38419,7 @@ var handleBridgeIdentified = (msg, deps) => {
|
|
|
38241
38419
|
})();
|
|
38242
38420
|
});
|
|
38243
38421
|
setImmediate(() => {
|
|
38422
|
+
if (isCliImmediateShutdownRequested()) return;
|
|
38244
38423
|
try {
|
|
38245
38424
|
deps.sendLocalSkillsReport?.();
|
|
38246
38425
|
} catch (e) {
|
|
@@ -38288,30 +38467,32 @@ var MERGEABLE_SERVER_STATES = /* @__PURE__ */ new Set([
|
|
|
38288
38467
|
"stopping",
|
|
38289
38468
|
"discarded"
|
|
38290
38469
|
]);
|
|
38291
|
-
function readPersistedQueue(queueKey) {
|
|
38292
|
-
|
|
38293
|
-
|
|
38294
|
-
|
|
38295
|
-
|
|
38296
|
-
|
|
38297
|
-
|
|
38298
|
-
|
|
38299
|
-
|
|
38300
|
-
|
|
38301
|
-
|
|
38302
|
-
|
|
38303
|
-
|
|
38470
|
+
async function readPersistedQueue(queueKey) {
|
|
38471
|
+
return withCliSqlite((db) => {
|
|
38472
|
+
const row = db.get("SELECT queue_key, updated_at, turns_json FROM prompt_queue WHERE queue_key = ?", [
|
|
38473
|
+
queueKey
|
|
38474
|
+
]);
|
|
38475
|
+
if (!row) return null;
|
|
38476
|
+
try {
|
|
38477
|
+
const turns = JSON.parse(row.turns_json);
|
|
38478
|
+
if (!Array.isArray(turns)) return null;
|
|
38479
|
+
return { queueKey: row.queue_key, updatedAt: row.updated_at, turns };
|
|
38480
|
+
} catch {
|
|
38481
|
+
return null;
|
|
38482
|
+
}
|
|
38483
|
+
});
|
|
38304
38484
|
}
|
|
38305
|
-
function writePersistedQueue(file2) {
|
|
38306
|
-
|
|
38307
|
-
|
|
38308
|
-
|
|
38309
|
-
|
|
38310
|
-
|
|
38311
|
-
|
|
38485
|
+
async function writePersistedQueue(file2) {
|
|
38486
|
+
await withCliSqlite((db) => {
|
|
38487
|
+
db.run(
|
|
38488
|
+
`INSERT INTO prompt_queue (queue_key, updated_at, turns_json) VALUES (?, ?, ?)
|
|
38489
|
+
ON CONFLICT(queue_key) DO UPDATE SET updated_at = excluded.updated_at, turns_json = excluded.turns_json`,
|
|
38490
|
+
[file2.queueKey, file2.updatedAt, JSON.stringify(file2.turns)]
|
|
38491
|
+
);
|
|
38492
|
+
});
|
|
38312
38493
|
}
|
|
38313
|
-
function mergeServerQueueSnapshot(queueKey, serverTurns) {
|
|
38314
|
-
const prev = readPersistedQueue(queueKey);
|
|
38494
|
+
async function mergeServerQueueSnapshot(queueKey, serverTurns) {
|
|
38495
|
+
const prev = await readPersistedQueue(queueKey);
|
|
38315
38496
|
const turns = [];
|
|
38316
38497
|
for (const raw of serverTurns) {
|
|
38317
38498
|
if (!raw || typeof raw !== "object") continue;
|
|
@@ -38408,12 +38589,12 @@ async function applyPromptQueueStateFromServer(msg, deps) {
|
|
|
38408
38589
|
const getWs = deps.getWs;
|
|
38409
38590
|
for (const [queueKey, serverTurns] of Object.entries(raw)) {
|
|
38410
38591
|
if (!Array.isArray(serverTurns)) continue;
|
|
38411
|
-
const file2 = mergeServerQueueSnapshot(queueKey, serverTurns);
|
|
38412
|
-
writePersistedQueue(file2);
|
|
38592
|
+
const file2 = await mergeServerQueueSnapshot(queueKey, serverTurns);
|
|
38593
|
+
await writePersistedQueue(file2);
|
|
38413
38594
|
}
|
|
38414
38595
|
for (const [queueKey, serverTurns] of Object.entries(raw)) {
|
|
38415
38596
|
if (!Array.isArray(serverTurns)) continue;
|
|
38416
|
-
const file2 = readPersistedQueue(queueKey);
|
|
38597
|
+
const file2 = await readPersistedQueue(queueKey);
|
|
38417
38598
|
if (!file2) continue;
|
|
38418
38599
|
for (const running of file2.turns.filter((t) => t.lastClientState === "running")) {
|
|
38419
38600
|
runIdToQueueKey.set(running.turnId, queueKey);
|
|
@@ -38421,7 +38602,7 @@ async function applyPromptQueueStateFromServer(msg, deps) {
|
|
|
38421
38602
|
}
|
|
38422
38603
|
for (const [queueKey, serverTurns] of Object.entries(raw)) {
|
|
38423
38604
|
if (!Array.isArray(serverTurns)) continue;
|
|
38424
|
-
const file2 = readPersistedQueue(queueKey);
|
|
38605
|
+
const file2 = await readPersistedQueue(queueKey);
|
|
38425
38606
|
if (!file2) continue;
|
|
38426
38607
|
const cancelRow = file2.turns.find((t) => t.serverState === "cancel_requested" && t.lastClientState === "running");
|
|
38427
38608
|
if (cancelRow) {
|
|
@@ -38430,7 +38611,7 @@ async function applyPromptQueueStateFromServer(msg, deps) {
|
|
|
38430
38611
|
deps.log(
|
|
38431
38612
|
`[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.`
|
|
38432
38613
|
);
|
|
38433
|
-
finalizePromptTurnOnBridge(deps.getWs, cancelRow.turnId, false, { terminalClientState: "cancelled" });
|
|
38614
|
+
await finalizePromptTurnOnBridge(deps.getWs, cancelRow.turnId, false, { terminalClientState: "cancelled" });
|
|
38434
38615
|
const ws = deps.getWs();
|
|
38435
38616
|
if (ws && cancelRow.sessionId) {
|
|
38436
38617
|
sendWsMessage(ws, {
|
|
@@ -38449,19 +38630,19 @@ async function applyPromptQueueStateFromServer(msg, deps) {
|
|
|
38449
38630
|
const startedThisTick = /* @__PURE__ */ new Set();
|
|
38450
38631
|
for (const [queueKey, serverTurns] of Object.entries(raw)) {
|
|
38451
38632
|
if (!Array.isArray(serverTurns)) continue;
|
|
38452
|
-
const file2 = readPersistedQueue(queueKey);
|
|
38633
|
+
const file2 = await readPersistedQueue(queueKey);
|
|
38453
38634
|
if (!file2) continue;
|
|
38454
38635
|
if (hasRunningTurn(file2.turns)) continue;
|
|
38455
38636
|
const next = pickNextRunnableTurn(file2.turns);
|
|
38456
38637
|
if (!next) continue;
|
|
38457
38638
|
if (!await runLocalRevertBeforeQueuedPrompt(next, deps)) {
|
|
38458
38639
|
next.lastClientState = "failed";
|
|
38459
|
-
writePersistedQueue(file2);
|
|
38640
|
+
await writePersistedQueue(file2);
|
|
38460
38641
|
sendPromptQueueClientReport(getWs(), { [queueKey]: [{ turnId: next.turnId, clientState: "failed" }] });
|
|
38461
38642
|
continue;
|
|
38462
38643
|
}
|
|
38463
38644
|
next.lastClientState = "running";
|
|
38464
|
-
writePersistedQueue(file2);
|
|
38645
|
+
await writePersistedQueue(file2);
|
|
38465
38646
|
runIdToQueueKey.set(next.turnId, queueKey);
|
|
38466
38647
|
startedThisTick.add(next.turnId);
|
|
38467
38648
|
report[queueKey] = [{ turnId: next.turnId, clientState: "running" }];
|
|
@@ -38471,7 +38652,7 @@ async function applyPromptQueueStateFromServer(msg, deps) {
|
|
|
38471
38652
|
}
|
|
38472
38653
|
for (const [queueKey, serverTurns] of Object.entries(raw)) {
|
|
38473
38654
|
if (!Array.isArray(serverTurns)) continue;
|
|
38474
|
-
const file2 = readPersistedQueue(queueKey);
|
|
38655
|
+
const file2 = await readPersistedQueue(queueKey);
|
|
38475
38656
|
if (!file2) continue;
|
|
38476
38657
|
const running = file2.turns.find((t) => t.lastClientState === "running");
|
|
38477
38658
|
if (!running || !startedThisTick.has(running.turnId)) continue;
|
|
@@ -38479,17 +38660,17 @@ async function applyPromptQueueStateFromServer(msg, deps) {
|
|
|
38479
38660
|
dispatchLocalPrompt(running, deps);
|
|
38480
38661
|
}
|
|
38481
38662
|
}
|
|
38482
|
-
function finalizePromptTurnOnBridge(getWs, runId, success2, opts) {
|
|
38663
|
+
async function finalizePromptTurnOnBridge(getWs, runId, success2, opts) {
|
|
38483
38664
|
if (!runId) return false;
|
|
38484
38665
|
const queueKey = runIdToQueueKey.get(runId);
|
|
38485
38666
|
runIdToQueueKey.delete(runId);
|
|
38486
38667
|
if (!queueKey) return false;
|
|
38487
|
-
const f = readPersistedQueue(queueKey);
|
|
38668
|
+
const f = await readPersistedQueue(queueKey);
|
|
38488
38669
|
if (!f) return false;
|
|
38489
38670
|
const t = f.turns.find((x) => x.turnId === runId);
|
|
38490
38671
|
if (!t) return false;
|
|
38491
38672
|
t.lastClientState = opts?.terminalClientState ?? (success2 ? "stopped" : "failed");
|
|
38492
|
-
writePersistedQueue(f);
|
|
38673
|
+
await writePersistedQueue(f);
|
|
38493
38674
|
sendPromptQueueClientReport(getWs(), { [queueKey]: [{ turnId: runId, clientState: t.lastClientState }] });
|
|
38494
38675
|
return true;
|
|
38495
38676
|
}
|
|
@@ -38510,12 +38691,13 @@ function createBridgePromptSenders(deps, getWs) {
|
|
|
38510
38691
|
if (result.type === "prompt_result") {
|
|
38511
38692
|
const pr = result;
|
|
38512
38693
|
const cancelled = pr.stopReason === "cancelled";
|
|
38513
|
-
finalizePromptTurnOnBridge(
|
|
38694
|
+
void finalizePromptTurnOnBridge(
|
|
38514
38695
|
getWs,
|
|
38515
38696
|
typeof pr.runId === "string" ? pr.runId : void 0,
|
|
38516
38697
|
pr.success === true,
|
|
38517
38698
|
cancelled ? { terminalClientState: "cancelled" } : void 0
|
|
38518
|
-
)
|
|
38699
|
+
).catch(() => {
|
|
38700
|
+
});
|
|
38519
38701
|
}
|
|
38520
38702
|
};
|
|
38521
38703
|
const sendSessionUpdate = (payload) => {
|
|
@@ -38539,12 +38721,12 @@ function createBridgePromptSenders(deps, getWs) {
|
|
|
38539
38721
|
}
|
|
38540
38722
|
|
|
38541
38723
|
// src/agents/acp/from-bridge/bridge-prompt-preamble.ts
|
|
38542
|
-
import { execFile as
|
|
38543
|
-
import { promisify as
|
|
38544
|
-
var
|
|
38724
|
+
import { execFile as execFile9 } from "node:child_process";
|
|
38725
|
+
import { promisify as promisify9 } from "node:util";
|
|
38726
|
+
var execFileAsync8 = promisify9(execFile9);
|
|
38545
38727
|
async function readGitBranch(cwd) {
|
|
38546
38728
|
try {
|
|
38547
|
-
const { stdout } = await
|
|
38729
|
+
const { stdout } = await execFileAsync8("git", ["branch", "--show-current"], { cwd, maxBuffer: 64 * 1024 });
|
|
38548
38730
|
const b = stdout.trim();
|
|
38549
38731
|
return b || null;
|
|
38550
38732
|
} catch {
|
|
@@ -39248,7 +39430,7 @@ function handleFileBrowserSearch(msg, socket, e2ee) {
|
|
|
39248
39430
|
await yieldToEventLoop();
|
|
39249
39431
|
const q = typeof msg.q === "string" ? msg.q : "";
|
|
39250
39432
|
const cwd = path37.resolve(getBridgeRoot());
|
|
39251
|
-
if (!bridgeFileIndexIsPopulated(cwd)) {
|
|
39433
|
+
if (!await bridgeFileIndexIsPopulated(cwd)) {
|
|
39252
39434
|
const payload2 = {
|
|
39253
39435
|
type: "file_browser_search_response",
|
|
39254
39436
|
id: msg.id,
|
|
@@ -40009,7 +40191,8 @@ import * as path40 from "node:path";
|
|
|
40009
40191
|
// src/agents/capabilities/probe-one-agent-type-for-capabilities.ts
|
|
40010
40192
|
import * as path39 from "node:path";
|
|
40011
40193
|
async function probeOneAgentTypeForCapabilities(params) {
|
|
40012
|
-
const { agentType, cwd, workspaceId, log: log2,
|
|
40194
|
+
const { agentType, cwd, workspaceId, log: log2, reportAgentCapabilities, bridgeReport = true } = params;
|
|
40195
|
+
if (isCliImmediateShutdownRequested()) return false;
|
|
40013
40196
|
const resolved = resolveAgentCommand(agentType);
|
|
40014
40197
|
if (!resolved) return false;
|
|
40015
40198
|
let sqliteChanged = false;
|
|
@@ -40020,11 +40203,13 @@ async function probeOneAgentTypeForCapabilities(params) {
|
|
|
40020
40203
|
reportedRef.done = true;
|
|
40021
40204
|
let changed = false;
|
|
40022
40205
|
try {
|
|
40023
|
-
changed =
|
|
40024
|
-
|
|
40025
|
-
|
|
40026
|
-
|
|
40027
|
-
|
|
40206
|
+
changed = withCliSqliteSync(
|
|
40207
|
+
(db) => upsertCliAgentCapabilityCache(db, {
|
|
40208
|
+
workspaceId,
|
|
40209
|
+
agentType,
|
|
40210
|
+
configOptions: co
|
|
40211
|
+
})
|
|
40212
|
+
);
|
|
40028
40213
|
} catch {
|
|
40029
40214
|
}
|
|
40030
40215
|
sqliteChanged ||= changed;
|
|
@@ -40041,6 +40226,7 @@ async function probeOneAgentTypeForCapabilities(params) {
|
|
|
40041
40226
|
}, 28e3);
|
|
40042
40227
|
killTimer.unref?.();
|
|
40043
40228
|
try {
|
|
40229
|
+
if (isCliImmediateShutdownRequested()) return false;
|
|
40044
40230
|
handle = await resolved.createClient({
|
|
40045
40231
|
command: resolved.command,
|
|
40046
40232
|
cwd: path39.resolve(cwd),
|
|
@@ -40060,7 +40246,7 @@ async function probeOneAgentTypeForCapabilities(params) {
|
|
|
40060
40246
|
onSessionUpdate: () => {
|
|
40061
40247
|
}
|
|
40062
40248
|
});
|
|
40063
|
-
|
|
40249
|
+
if (!await delayMsUnlessShutdownRequested(1200)) return false;
|
|
40064
40250
|
} catch (e) {
|
|
40065
40251
|
log2(
|
|
40066
40252
|
`[Bridge service] Agent capability probe (${agentType}): ${e instanceof Error ? e.message : String(e)}`
|
|
@@ -40082,19 +40268,19 @@ async function probeAgentCapabilitiesForDetectedTypes(params) {
|
|
|
40082
40268
|
cwd,
|
|
40083
40269
|
workspaceId,
|
|
40084
40270
|
log: log2,
|
|
40085
|
-
getDb,
|
|
40086
40271
|
reportAgentCapabilities,
|
|
40087
40272
|
bridgeReport = true,
|
|
40088
40273
|
forceAllTypes = false
|
|
40089
40274
|
} = params;
|
|
40090
40275
|
let changedCount = 0;
|
|
40091
40276
|
for (let i = 0; i < agentTypes.length; i++) {
|
|
40277
|
+
if (isCliImmediateShutdownRequested()) return changedCount;
|
|
40092
40278
|
if (i > 0) await yieldToEventLoop();
|
|
40093
40279
|
const agentType = agentTypes[i];
|
|
40094
40280
|
if (!agentType.trim()) continue;
|
|
40095
40281
|
if (!forceAllTypes) {
|
|
40096
40282
|
try {
|
|
40097
|
-
if (process.env.BUILDAUTOMATON_FORCE_PROBE_ACP_CAPABILITIES !== "1" &&
|
|
40283
|
+
if (process.env.BUILDAUTOMATON_FORCE_PROBE_ACP_CAPABILITIES !== "1" && await withCliSqlite((db) => hasNonEmptyAgentCapabilityCache(db, workspaceId, agentType))) {
|
|
40098
40284
|
continue;
|
|
40099
40285
|
}
|
|
40100
40286
|
} catch {
|
|
@@ -40105,7 +40291,6 @@ async function probeAgentCapabilitiesForDetectedTypes(params) {
|
|
|
40105
40291
|
cwd,
|
|
40106
40292
|
workspaceId,
|
|
40107
40293
|
log: log2,
|
|
40108
|
-
getDb,
|
|
40109
40294
|
reportAgentCapabilities,
|
|
40110
40295
|
bridgeReport
|
|
40111
40296
|
});
|
|
@@ -40116,60 +40301,64 @@ async function probeAgentCapabilitiesForDetectedTypes(params) {
|
|
|
40116
40301
|
|
|
40117
40302
|
// src/agents/capabilities/warmup-agent-capabilities-on-connect.ts
|
|
40118
40303
|
async function warmupAgentCapabilitiesOnConnect(params) {
|
|
40119
|
-
const { workspaceId, log: log2,
|
|
40304
|
+
const { workspaceId, log: log2, getWs } = params;
|
|
40305
|
+
if (isCliImmediateShutdownRequested()) return;
|
|
40120
40306
|
const cwd = path40.resolve(getBridgeRoot());
|
|
40121
|
-
|
|
40122
|
-
function sendBatchFromCache() {
|
|
40307
|
+
async function sendBatchFromCache() {
|
|
40123
40308
|
const socket = getWs();
|
|
40124
40309
|
if (!socket || socket.readyState !== wrapper_default.OPEN) return;
|
|
40125
40310
|
try {
|
|
40126
|
-
const rows = listCliAgentCapabilityCacheForWorkspace(db, workspaceId);
|
|
40311
|
+
const rows = await withCliSqlite((db) => listCliAgentCapabilityCacheForWorkspace(db, workspaceId));
|
|
40127
40312
|
if (rows.length === 0) return;
|
|
40128
40313
|
sendWsMessage(socket, {
|
|
40129
40314
|
type: "agent_capabilities_batch",
|
|
40130
40315
|
items: rows.map((r) => ({ agentType: r.agentType, configOptions: r.configOptions }))
|
|
40131
40316
|
});
|
|
40132
40317
|
} catch (e) {
|
|
40318
|
+
if (e instanceof CliSqliteInterrupted) return;
|
|
40133
40319
|
log2(
|
|
40134
40320
|
`[Bridge service] Agent capability batch to bridge failed: ${e instanceof Error ? e.message : String(e)}`
|
|
40135
40321
|
);
|
|
40136
40322
|
}
|
|
40137
40323
|
}
|
|
40138
|
-
sendBatchFromCache();
|
|
40324
|
+
await sendBatchFromCache();
|
|
40325
|
+
if (isCliImmediateShutdownRequested()) return;
|
|
40139
40326
|
let types = [];
|
|
40140
40327
|
try {
|
|
40141
40328
|
types = [...await detectLocalAgentTypes()];
|
|
40142
40329
|
} catch (e) {
|
|
40143
40330
|
log2(`[Bridge service] detectLocalAgentTypes failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
40144
40331
|
}
|
|
40332
|
+
if (isCliImmediateShutdownRequested()) return;
|
|
40145
40333
|
try {
|
|
40146
40334
|
const n = await probeAgentCapabilitiesForDetectedTypes({
|
|
40147
40335
|
agentTypes: types,
|
|
40148
40336
|
cwd,
|
|
40149
40337
|
workspaceId,
|
|
40150
40338
|
log: log2,
|
|
40151
|
-
getDb,
|
|
40152
40339
|
bridgeReport: false,
|
|
40153
40340
|
forceAllTypes: false
|
|
40154
40341
|
});
|
|
40155
|
-
if (n > 0) sendBatchFromCache();
|
|
40342
|
+
if (n > 0) await sendBatchFromCache();
|
|
40156
40343
|
} catch (e) {
|
|
40344
|
+
if (e instanceof CliSqliteInterrupted) return;
|
|
40157
40345
|
log2(`[Bridge service] Agent capability probe (missing cache) failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
40158
40346
|
}
|
|
40159
40347
|
void (async () => {
|
|
40160
40348
|
try {
|
|
40161
40349
|
await yieldToEventLoop();
|
|
40350
|
+
if (isCliImmediateShutdownRequested()) return;
|
|
40162
40351
|
const n = await probeAgentCapabilitiesForDetectedTypes({
|
|
40163
40352
|
agentTypes: types,
|
|
40164
40353
|
cwd,
|
|
40165
40354
|
workspaceId,
|
|
40166
40355
|
log: log2,
|
|
40167
|
-
getDb,
|
|
40168
40356
|
bridgeReport: false,
|
|
40169
40357
|
forceAllTypes: true
|
|
40170
40358
|
});
|
|
40171
|
-
if (n > 0) sendBatchFromCache();
|
|
40359
|
+
if (n > 0) await sendBatchFromCache();
|
|
40172
40360
|
} catch (e) {
|
|
40361
|
+
if (e instanceof CliSqliteInterrupted) return;
|
|
40173
40362
|
log2(`[Bridge service] Agent capability lazy refresh failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
40174
40363
|
}
|
|
40175
40364
|
})();
|
|
@@ -40180,7 +40369,7 @@ async function createBridgeConnection(options) {
|
|
|
40180
40369
|
const { apiUrl, workspaceId, justAuthenticated, onAuthInvalid, persistTokens } = options;
|
|
40181
40370
|
const firehoseServerUrl = options.firehoseServerUrl ?? options.proxyServerUrl;
|
|
40182
40371
|
const logFn = options.log ?? log;
|
|
40183
|
-
|
|
40372
|
+
await ensureCliSqliteInitialized({ logLegacyMigration: logFn });
|
|
40184
40373
|
const tokens = {
|
|
40185
40374
|
accessToken: options.authToken,
|
|
40186
40375
|
refreshToken: options.refreshToken
|
|
@@ -40210,12 +40399,15 @@ async function createBridgeConnection(options) {
|
|
|
40210
40399
|
if (!Array.isArray(info.configOptions) || info.configOptions.length === 0) return;
|
|
40211
40400
|
let changed = false;
|
|
40212
40401
|
try {
|
|
40213
|
-
changed =
|
|
40214
|
-
|
|
40215
|
-
|
|
40216
|
-
|
|
40217
|
-
|
|
40218
|
-
|
|
40402
|
+
changed = withCliSqliteSync(
|
|
40403
|
+
(db) => upsertCliAgentCapabilityCache(db, {
|
|
40404
|
+
workspaceId,
|
|
40405
|
+
agentType: info.agentType,
|
|
40406
|
+
configOptions: info.configOptions
|
|
40407
|
+
})
|
|
40408
|
+
);
|
|
40409
|
+
} catch (e) {
|
|
40410
|
+
if (e instanceof CliSqliteInterrupted) return;
|
|
40219
40411
|
}
|
|
40220
40412
|
if (!changed) return;
|
|
40221
40413
|
const socket = getWs();
|
|
@@ -40267,7 +40459,6 @@ async function createBridgeConnection(options) {
|
|
|
40267
40459
|
await warmupAgentCapabilitiesOnConnect({
|
|
40268
40460
|
workspaceId,
|
|
40269
40461
|
log: logFn,
|
|
40270
|
-
getDb: getCliDatabase,
|
|
40271
40462
|
getWs
|
|
40272
40463
|
});
|
|
40273
40464
|
},
|
|
@@ -40299,6 +40490,7 @@ async function createBridgeConnection(options) {
|
|
|
40299
40490
|
const stopFileIndexWatcher = startFileIndexWatcher(getBridgeRoot());
|
|
40300
40491
|
return {
|
|
40301
40492
|
close: async () => {
|
|
40493
|
+
requestCliImmediateShutdown();
|
|
40302
40494
|
stopFileIndexWatcher();
|
|
40303
40495
|
bridgeHeartbeat.stop();
|
|
40304
40496
|
await closeBridgeConnection(state, acpManager, devServerManager, logFn);
|
|
@@ -40372,47 +40564,60 @@ async function runConnectedBridge(options, restartWithoutAuth) {
|
|
|
40372
40564
|
} = options;
|
|
40373
40565
|
const firehoseServerUrl = options.firehoseServerUrl ?? options.proxyServerUrl;
|
|
40374
40566
|
let cleanupKeyCommand;
|
|
40375
|
-
|
|
40376
|
-
apiUrl,
|
|
40377
|
-
workspaceId,
|
|
40378
|
-
authToken,
|
|
40379
|
-
refreshToken,
|
|
40380
|
-
firehoseServerUrl,
|
|
40381
|
-
justAuthenticated,
|
|
40382
|
-
worktreesRootPath,
|
|
40383
|
-
e2eCertificate,
|
|
40384
|
-
log,
|
|
40385
|
-
persistTokens: (t) => {
|
|
40386
|
-
writeConfigForApi(apiUrl, {
|
|
40387
|
-
workspaceId,
|
|
40388
|
-
token: t.token,
|
|
40389
|
-
refreshToken: t.refreshToken
|
|
40390
|
-
});
|
|
40391
|
-
},
|
|
40392
|
-
onAuthInvalid: () => {
|
|
40393
|
-
cleanupKeyCommand?.();
|
|
40394
|
-
log("[Bridge service] Access token invalid or revoked; re-authenticating\u2026");
|
|
40395
|
-
clearConfigForApi(apiUrl);
|
|
40396
|
-
void handle.close().then(() => {
|
|
40397
|
-
void restartWithoutAuth({ apiUrl, firehoseServerUrl, worktreesRootPath, e2eCertificate });
|
|
40398
|
-
});
|
|
40399
|
-
}
|
|
40400
|
-
});
|
|
40567
|
+
let bridgeClose = null;
|
|
40401
40568
|
const onSignal = (kind) => {
|
|
40569
|
+
requestCliImmediateShutdown();
|
|
40402
40570
|
cleanupKeyCommand?.();
|
|
40403
40571
|
logImmediate(
|
|
40404
40572
|
kind === "interrupt" ? "Keyboard interrupt (Ctrl+C) \u2014 stopping\u2026" : "Stop requested \u2014 shutting down\u2026"
|
|
40405
40573
|
);
|
|
40406
|
-
|
|
40407
|
-
void
|
|
40408
|
-
|
|
40409
|
-
|
|
40410
|
-
|
|
40574
|
+
if (bridgeClose) {
|
|
40575
|
+
void bridgeClose().then(() => process.exit(0));
|
|
40576
|
+
return;
|
|
40577
|
+
}
|
|
40578
|
+
process.exit(0);
|
|
40411
40579
|
};
|
|
40412
40580
|
const onSigInt = () => onSignal("interrupt");
|
|
40413
40581
|
const onSigTerm = () => onSignal("stop");
|
|
40414
40582
|
process.on("SIGINT", onSigInt);
|
|
40415
40583
|
process.on("SIGTERM", onSigTerm);
|
|
40584
|
+
let handle;
|
|
40585
|
+
try {
|
|
40586
|
+
handle = await createBridgeConnection({
|
|
40587
|
+
apiUrl,
|
|
40588
|
+
workspaceId,
|
|
40589
|
+
authToken,
|
|
40590
|
+
refreshToken,
|
|
40591
|
+
firehoseServerUrl,
|
|
40592
|
+
justAuthenticated,
|
|
40593
|
+
worktreesRootPath,
|
|
40594
|
+
e2eCertificate,
|
|
40595
|
+
log,
|
|
40596
|
+
persistTokens: (t) => {
|
|
40597
|
+
writeConfigForApi(apiUrl, {
|
|
40598
|
+
workspaceId,
|
|
40599
|
+
token: t.token,
|
|
40600
|
+
refreshToken: t.refreshToken
|
|
40601
|
+
});
|
|
40602
|
+
},
|
|
40603
|
+
onAuthInvalid: () => {
|
|
40604
|
+
cleanupKeyCommand?.();
|
|
40605
|
+
log("[Bridge service] Access token invalid or revoked; re-authenticating\u2026");
|
|
40606
|
+
clearConfigForApi(apiUrl);
|
|
40607
|
+
void handle.close().then(() => {
|
|
40608
|
+
void restartWithoutAuth({ apiUrl, firehoseServerUrl, worktreesRootPath, e2eCertificate });
|
|
40609
|
+
});
|
|
40610
|
+
}
|
|
40611
|
+
});
|
|
40612
|
+
} catch (e) {
|
|
40613
|
+
process.off("SIGINT", onSigInt);
|
|
40614
|
+
process.off("SIGTERM", onSigTerm);
|
|
40615
|
+
if (e instanceof CliSqliteInterrupted) {
|
|
40616
|
+
process.exit(0);
|
|
40617
|
+
}
|
|
40618
|
+
throw e;
|
|
40619
|
+
}
|
|
40620
|
+
bridgeClose = () => handle.close();
|
|
40416
40621
|
if (e2eCertificate) {
|
|
40417
40622
|
let openingCertificate = false;
|
|
40418
40623
|
cleanupKeyCommand = installE2eCertificateKeyCommand({
|
|
@@ -40457,6 +40662,7 @@ async function runBridge(options) {
|
|
|
40457
40662
|
}
|
|
40458
40663
|
});
|
|
40459
40664
|
const onSignal = (kind) => {
|
|
40665
|
+
requestCliImmediateShutdown();
|
|
40460
40666
|
logImmediate(
|
|
40461
40667
|
kind === "interrupt" ? "Keyboard interrupt (Ctrl+C) \u2014 stopping\u2026" : "Stop requested \u2014 shutting down\u2026"
|
|
40462
40668
|
);
|