@buildautomaton/cli 0.1.34 → 0.1.36
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 +338 -150
- package/dist/cli.js.map +4 -4
- package/dist/index.js +332 -144
- package/dist/index.js.map +4 -4
- package/dist/migrations/003_file_index_parent_paths.sql +17 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -8000,8 +8000,8 @@ function getElementAtPath(obj, path41) {
|
|
|
8000
8000
|
}
|
|
8001
8001
|
function promiseAllObject(promisesObj) {
|
|
8002
8002
|
const keys = Object.keys(promisesObj);
|
|
8003
|
-
const
|
|
8004
|
-
return Promise.all(
|
|
8003
|
+
const promises5 = keys.map((key) => promisesObj[key]);
|
|
8004
|
+
return Promise.all(promises5).then((results) => {
|
|
8005
8005
|
const resolvedObj = {};
|
|
8006
8006
|
for (let i = 0; i < keys.length; i++) {
|
|
8007
8007
|
resolvedObj[keys[i]] = results[i];
|
|
@@ -22158,20 +22158,22 @@ function createWsBridge(options) {
|
|
|
22158
22158
|
onOpen?.();
|
|
22159
22159
|
});
|
|
22160
22160
|
ws.on("message", (raw) => {
|
|
22161
|
-
|
|
22162
|
-
|
|
22163
|
-
|
|
22164
|
-
|
|
22165
|
-
|
|
22166
|
-
|
|
22167
|
-
|
|
22168
|
-
|
|
22169
|
-
|
|
22161
|
+
setImmediate(() => {
|
|
22162
|
+
try {
|
|
22163
|
+
let data;
|
|
22164
|
+
if (typeof raw === "string") {
|
|
22165
|
+
data = JSON.parse(raw);
|
|
22166
|
+
} else if (Buffer.isBuffer(raw) || raw instanceof ArrayBuffer) {
|
|
22167
|
+
const str = Buffer.isBuffer(raw) ? raw.toString("utf8") : Buffer.from(raw).toString("utf8");
|
|
22168
|
+
data = JSON.parse(str);
|
|
22169
|
+
} else {
|
|
22170
|
+
data = raw;
|
|
22171
|
+
}
|
|
22172
|
+
onMessage?.(data);
|
|
22173
|
+
} catch {
|
|
22174
|
+
onMessage?.(raw);
|
|
22170
22175
|
}
|
|
22171
|
-
|
|
22172
|
-
} catch {
|
|
22173
|
-
onMessage?.(raw);
|
|
22174
|
-
}
|
|
22176
|
+
});
|
|
22175
22177
|
});
|
|
22176
22178
|
ws.on("close", (code, reason) => {
|
|
22177
22179
|
disposeClientPing();
|
|
@@ -23962,7 +23964,7 @@ function installBridgeProcessResilience() {
|
|
|
23962
23964
|
}
|
|
23963
23965
|
|
|
23964
23966
|
// src/cli-version.ts
|
|
23965
|
-
var CLI_VERSION = "0.1.
|
|
23967
|
+
var CLI_VERSION = "0.1.36".length > 0 ? "0.1.36" : "0.0.0-dev";
|
|
23966
23968
|
|
|
23967
23969
|
// src/connection/heartbeat/constants.ts
|
|
23968
23970
|
var BRIDGE_APP_HEARTBEAT_INTERVAL_MS = 1e4;
|
|
@@ -25285,6 +25287,7 @@ function recordMigrationAndPruneCheckpointLegacy(db, migration, applied2) {
|
|
|
25285
25287
|
var CHECKPOINT_V1 = "001_cli_sqlite_checkpoint_v1";
|
|
25286
25288
|
var CHECKPOINT_V1_SQL = readCliSqliteMigrationSql("001_cli_sqlite_checkpoint_v1.sql");
|
|
25287
25289
|
var AGENT_CAPABILITIES_SQL = readCliSqliteMigrationSql("002_agent_capabilities.sql");
|
|
25290
|
+
var FILE_INDEX_PARENT_PATHS_SQL = readCliSqliteMigrationSql("003_file_index_parent_paths.sql");
|
|
25288
25291
|
function agentCapabilitiesTableState(db) {
|
|
25289
25292
|
const rows = db.all(
|
|
25290
25293
|
`SELECT name FROM sqlite_master WHERE type='table' AND name IN ('agent_capabilities', 'agent_capability_cache')`
|
|
@@ -25318,6 +25321,18 @@ var CLI_SQLITE_MIGRATIONS = [
|
|
|
25318
25321
|
db.exec(AGENT_CAPABILITIES_SQL);
|
|
25319
25322
|
},
|
|
25320
25323
|
alreadyApplied: (db) => agentCapabilitiesTableState(db) === "current"
|
|
25324
|
+
},
|
|
25325
|
+
{
|
|
25326
|
+
name: "003_file_index_parent_paths",
|
|
25327
|
+
migrate: (db) => {
|
|
25328
|
+
db.exec(FILE_INDEX_PARENT_PATHS_SQL);
|
|
25329
|
+
},
|
|
25330
|
+
alreadyApplied: (db) => {
|
|
25331
|
+
const row = db.get(
|
|
25332
|
+
`SELECT 1 as ok FROM sqlite_master WHERE type='table' AND name='file_index_parent_path' LIMIT 1`
|
|
25333
|
+
);
|
|
25334
|
+
return row != null;
|
|
25335
|
+
}
|
|
25321
25336
|
}
|
|
25322
25337
|
];
|
|
25323
25338
|
function migrateCliSqlite(db) {
|
|
@@ -25356,6 +25371,10 @@ var CliSqliteInterrupted = class extends Error {
|
|
|
25356
25371
|
}
|
|
25357
25372
|
};
|
|
25358
25373
|
function applyCliSqliteConcurrencyPragmas(db) {
|
|
25374
|
+
try {
|
|
25375
|
+
db.run("PRAGMA foreign_keys = ON");
|
|
25376
|
+
} catch {
|
|
25377
|
+
}
|
|
25359
25378
|
try {
|
|
25360
25379
|
db.exec("PRAGMA journal_mode = WAL");
|
|
25361
25380
|
} catch {
|
|
@@ -32186,7 +32205,7 @@ async function ensureAcpClient(options) {
|
|
|
32186
32205
|
if (!state.acpStartPromise) {
|
|
32187
32206
|
let statOk = false;
|
|
32188
32207
|
try {
|
|
32189
|
-
const st = fs15.
|
|
32208
|
+
const st = await fs15.promises.stat(targetSessionParentPath);
|
|
32190
32209
|
statOk = st.isDirectory();
|
|
32191
32210
|
if (!statOk) {
|
|
32192
32211
|
state.lastAcpStartError = `Agent cwd is not a directory: ${targetSessionParentPath}`;
|
|
@@ -33781,8 +33800,18 @@ import path28 from "node:path";
|
|
|
33781
33800
|
// src/files/index/walk-workspace-tree.ts
|
|
33782
33801
|
import fs24 from "node:fs";
|
|
33783
33802
|
import path27 from "node:path";
|
|
33803
|
+
var DEPENDENCY_INSTALL_DIR_NAMES = /* @__PURE__ */ new Set([
|
|
33804
|
+
"node_modules",
|
|
33805
|
+
"bower_components",
|
|
33806
|
+
"vendor",
|
|
33807
|
+
"Pods",
|
|
33808
|
+
"Carthage",
|
|
33809
|
+
"DerivedData",
|
|
33810
|
+
".yarn",
|
|
33811
|
+
".pnpm-store"
|
|
33812
|
+
]);
|
|
33784
33813
|
function shouldSkipWorkspaceWalkEntry(name) {
|
|
33785
|
-
return
|
|
33814
|
+
return DEPENDENCY_INSTALL_DIR_NAMES.has(name);
|
|
33786
33815
|
}
|
|
33787
33816
|
async function walkWorkspaceTreeAsync(dir, baseDir, onFile, state) {
|
|
33788
33817
|
let names;
|
|
@@ -33853,14 +33882,26 @@ var FILE_INDEX_INTERRUPT_CHECK_EVERY = 256;
|
|
|
33853
33882
|
function assertNotShutdown() {
|
|
33854
33883
|
if (isCliImmediateShutdownRequested()) throw new CliSqliteInterrupted();
|
|
33855
33884
|
}
|
|
33885
|
+
function upsertFileIndexParentPath(db, resolved) {
|
|
33886
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
33887
|
+
db.run(
|
|
33888
|
+
`INSERT INTO file_index_parent_path (path, path_hash, updated_at)
|
|
33889
|
+
VALUES (?, ?, ?)
|
|
33890
|
+
ON CONFLICT(path) DO UPDATE SET path_hash = excluded.path_hash, updated_at = excluded.updated_at`,
|
|
33891
|
+
[resolved, getCwdHashForFileIndex(resolved), now]
|
|
33892
|
+
);
|
|
33893
|
+
const row = db.get("SELECT id FROM file_index_parent_path WHERE path = ?", [resolved]);
|
|
33894
|
+
if (row == null) throw new Error(`Failed to upsert file index parent path: ${resolved}`);
|
|
33895
|
+
return Number(row.id);
|
|
33896
|
+
}
|
|
33856
33897
|
function persistFileIndexPaths(resolved, paths) {
|
|
33857
33898
|
return withCliSqliteSync((db) => {
|
|
33858
|
-
const h = getCwdHashForFileIndex(resolved);
|
|
33859
33899
|
let pathCount = 0;
|
|
33860
33900
|
db.run("BEGIN IMMEDIATE");
|
|
33861
33901
|
try {
|
|
33862
|
-
|
|
33863
|
-
|
|
33902
|
+
const parentId = upsertFileIndexParentPath(db, resolved);
|
|
33903
|
+
db.run("DELETE FROM file_index_child_path WHERE parent_id = ?", [parentId]);
|
|
33904
|
+
const ins = db.prepare("INSERT INTO file_index_child_path (parent_id, path) VALUES (?, ?)");
|
|
33864
33905
|
try {
|
|
33865
33906
|
let batch = 0;
|
|
33866
33907
|
for (const rel of paths) {
|
|
@@ -33868,7 +33909,7 @@ function persistFileIndexPaths(resolved, paths) {
|
|
|
33868
33909
|
batch = 0;
|
|
33869
33910
|
assertNotShutdown();
|
|
33870
33911
|
}
|
|
33871
|
-
ins.run([
|
|
33912
|
+
ins.run([parentId, rel]);
|
|
33872
33913
|
pathCount += 1;
|
|
33873
33914
|
}
|
|
33874
33915
|
} finally {
|
|
@@ -33910,36 +33951,38 @@ async function buildFileIndexAsync(cwd) {
|
|
|
33910
33951
|
// src/files/index/ensure-file-index.ts
|
|
33911
33952
|
import path29 from "node:path";
|
|
33912
33953
|
|
|
33913
|
-
// src/files/index/file-index-dependency-path.ts
|
|
33914
|
-
function sqliteExprBridgeFileIndexDependencyRank() {
|
|
33915
|
-
return `CASE WHEN lower(path) = 'node_modules' OR lower(path) LIKE 'node_modules/%' OR lower(path) LIKE '%/node_modules/%' OR lower(path) = 'bower_components' OR lower(path) LIKE 'bower_components/%' OR lower(path) LIKE '%/bower_components/%' THEN 1 ELSE 0 END`;
|
|
33916
|
-
}
|
|
33917
|
-
|
|
33918
33954
|
// src/files/index/search-file-index.ts
|
|
33919
33955
|
function escapeLikePattern(fragment) {
|
|
33920
33956
|
return fragment.replace(/\\/g, "\\\\").replace(/%/g, "\\%").replace(/_/g, "\\_");
|
|
33921
33957
|
}
|
|
33922
33958
|
function bridgeFileIndexIsPopulatedWithDb(resolvedCwd, db) {
|
|
33923
|
-
const
|
|
33924
|
-
const row = db.get("SELECT 1 as ok FROM file_index_path WHERE cwd_hash = ? LIMIT 1", [h]);
|
|
33959
|
+
const row = db.get("SELECT 1 as ok FROM file_index_parent_path WHERE path = ? LIMIT 1", [resolvedCwd]);
|
|
33925
33960
|
return row != null;
|
|
33926
33961
|
}
|
|
33927
33962
|
function bridgeFileIndexPathCountWithDb(resolvedCwd, db) {
|
|
33928
|
-
const
|
|
33929
|
-
|
|
33963
|
+
const row = db.get(
|
|
33964
|
+
`SELECT COUNT(*) as c
|
|
33965
|
+
FROM file_index_child_path child
|
|
33966
|
+
JOIN file_index_parent_path parent ON parent.id = child.parent_id
|
|
33967
|
+
WHERE parent.path = ?`,
|
|
33968
|
+
[resolvedCwd]
|
|
33969
|
+
);
|
|
33930
33970
|
const c = row?.c ?? 0;
|
|
33931
33971
|
return Number(c);
|
|
33932
33972
|
}
|
|
33933
33973
|
function searchBridgeFilePathsWithDb(resolvedCwd, query, limit, db) {
|
|
33934
33974
|
const q = query.trim().toLowerCase();
|
|
33935
33975
|
if (!q) return [];
|
|
33936
|
-
const h = getCwdHashForFileIndex(resolvedCwd);
|
|
33937
33976
|
const pattern = `%${escapeLikePattern(q)}%`;
|
|
33938
33977
|
const lim = Math.max(0, Math.min(1e4, Math.floor(limit)));
|
|
33939
|
-
const depRank = sqliteExprBridgeFileIndexDependencyRank();
|
|
33940
33978
|
const rows = db.all(
|
|
33941
|
-
`SELECT path
|
|
33942
|
-
|
|
33979
|
+
`SELECT child.path
|
|
33980
|
+
FROM file_index_child_path child
|
|
33981
|
+
JOIN file_index_parent_path parent ON parent.id = child.parent_id
|
|
33982
|
+
WHERE parent.path = ? AND lower(child.path) LIKE ? ESCAPE '\\'
|
|
33983
|
+
ORDER BY child.path
|
|
33984
|
+
LIMIT ?`,
|
|
33985
|
+
[resolvedCwd, pattern, lim]
|
|
33943
33986
|
);
|
|
33944
33987
|
return rows.map((r) => String(r.path));
|
|
33945
33988
|
}
|
|
@@ -33969,9 +34012,7 @@ async function ensureFileIndexAsync(cwd) {
|
|
|
33969
34012
|
var DEBOUNCE_MS = 900;
|
|
33970
34013
|
function shouldIgnoreRelative(rel) {
|
|
33971
34014
|
const n = rel.replace(/\\/g, "/");
|
|
33972
|
-
|
|
33973
|
-
if (n.includes("/.buildautomaton/") || n.startsWith(".buildautomaton/")) return true;
|
|
33974
|
-
return false;
|
|
34015
|
+
return n.split("/").some((segment) => shouldSkipWorkspaceWalkEntry(segment));
|
|
33975
34016
|
}
|
|
33976
34017
|
function attachWatchErrorLog(w) {
|
|
33977
34018
|
w.on("error", (err) => {
|
|
@@ -35040,11 +35081,13 @@ function connectFirehose(options) {
|
|
|
35040
35081
|
if (Buffer.isBuffer(raw) && tryConsumeBinaryProxyBody(raw, deps)) {
|
|
35041
35082
|
return;
|
|
35042
35083
|
}
|
|
35043
|
-
|
|
35044
|
-
|
|
35045
|
-
|
|
35046
|
-
|
|
35047
|
-
|
|
35084
|
+
setImmediate(() => {
|
|
35085
|
+
try {
|
|
35086
|
+
const text = Buffer.isBuffer(raw) ? raw.toString("utf8") : String(raw);
|
|
35087
|
+
dispatchFirehoseJsonMessage(JSON.parse(text), deps);
|
|
35088
|
+
} catch {
|
|
35089
|
+
}
|
|
35090
|
+
});
|
|
35048
35091
|
});
|
|
35049
35092
|
ws.on("close", (code, reason) => {
|
|
35050
35093
|
disposeClientPing();
|
|
@@ -35560,7 +35603,9 @@ async function runLocalRevertBeforeQueuedPrompt(next, deps) {
|
|
|
35560
35603
|
const tid = typeof pl.snapshotRevertTurnId === "string" && pl.snapshotRevertTurnId.trim() !== "" ? pl.snapshotRevertTurnId.trim() : next.turnId;
|
|
35561
35604
|
const agentBase = deps.sessionWorktreeManager.getSessionWorktreeRootForSession(sid) ?? getBridgeRoot();
|
|
35562
35605
|
const file2 = snapshotFilePath(agentBase, tid);
|
|
35563
|
-
|
|
35606
|
+
try {
|
|
35607
|
+
await fs31.promises.access(file2, fs31.constants.F_OK);
|
|
35608
|
+
} catch {
|
|
35564
35609
|
deps.log(
|
|
35565
35610
|
`[Queue] requeued_with_revert: no pre-turn snapshot for ${tid.slice(0, 8)}\u2026; continuing without revert.`
|
|
35566
35611
|
);
|
|
@@ -36010,9 +36055,8 @@ var handleSkillCallMessage = (msg, { getWs, log: log2 }) => {
|
|
|
36010
36055
|
);
|
|
36011
36056
|
};
|
|
36012
36057
|
|
|
36013
|
-
// src/files/list-dir.ts
|
|
36014
|
-
import
|
|
36015
|
-
import path35 from "node:path";
|
|
36058
|
+
// src/files/list-dir/index.ts
|
|
36059
|
+
import fs33 from "node:fs";
|
|
36016
36060
|
|
|
36017
36061
|
// src/files/ensure-under-cwd.ts
|
|
36018
36062
|
import path34 from "node:path";
|
|
@@ -36025,71 +36069,93 @@ function ensureUnderCwd(relativePath, cwd = getBridgeRoot()) {
|
|
|
36025
36069
|
return resolved;
|
|
36026
36070
|
}
|
|
36027
36071
|
|
|
36028
|
-
// src/files/list-dir.ts
|
|
36072
|
+
// src/files/list-dir/types.ts
|
|
36029
36073
|
var LIST_DIR_YIELD_EVERY = 256;
|
|
36030
|
-
|
|
36031
|
-
|
|
36074
|
+
|
|
36075
|
+
// src/files/list-dir/map-dir-entry.ts
|
|
36076
|
+
import path35 from "node:path";
|
|
36077
|
+
import fs32 from "node:fs";
|
|
36078
|
+
async function mapDirEntry(d, relativePath, resolved) {
|
|
36079
|
+
const entryPath = path35.join(relativePath || ".", d.name).replace(/\\/g, "/");
|
|
36080
|
+
const fullPath = path35.join(resolved, d.name);
|
|
36081
|
+
let isDir = d.isDirectory();
|
|
36082
|
+
if (d.isSymbolicLink()) {
|
|
36083
|
+
try {
|
|
36084
|
+
const targetStat = await fs32.promises.stat(fullPath);
|
|
36085
|
+
isDir = targetStat.isDirectory();
|
|
36086
|
+
} catch {
|
|
36087
|
+
isDir = false;
|
|
36088
|
+
}
|
|
36089
|
+
}
|
|
36090
|
+
return {
|
|
36091
|
+
name: d.name,
|
|
36092
|
+
path: entryPath,
|
|
36093
|
+
isDir,
|
|
36094
|
+
isSymlink: d.isSymbolicLink()
|
|
36095
|
+
};
|
|
36096
|
+
}
|
|
36097
|
+
|
|
36098
|
+
// src/files/list-dir/sort-entries.ts
|
|
36099
|
+
function sortListEntries(entries) {
|
|
36100
|
+
return entries.sort((a, b) => {
|
|
36101
|
+
if (a.isDir !== b.isDir) return a.isDir ? -1 : 1;
|
|
36102
|
+
return a.name.localeCompare(b.name, void 0, { sensitivity: "base" });
|
|
36103
|
+
});
|
|
36104
|
+
}
|
|
36105
|
+
|
|
36106
|
+
// src/files/list-dir/index.ts
|
|
36107
|
+
async function listDirAsync(relativePath, sessionParentPath = getBridgeRoot()) {
|
|
36108
|
+
await yieldToEventLoop();
|
|
36109
|
+
const resolved = ensureUnderCwd(relativePath || ".", sessionParentPath);
|
|
36032
36110
|
if (!resolved) {
|
|
36033
36111
|
return { error: "Path is outside working directory" };
|
|
36034
36112
|
}
|
|
36035
36113
|
try {
|
|
36036
|
-
const names = await
|
|
36037
|
-
const visible = names.filter((d) => !d.name.startsWith("."));
|
|
36114
|
+
const names = await fs33.promises.readdir(resolved, { withFileTypes: true });
|
|
36038
36115
|
const entries = [];
|
|
36039
|
-
for (let i = 0; i <
|
|
36116
|
+
for (let i = 0; i < names.length; i++) {
|
|
36040
36117
|
if (i > 0 && i % LIST_DIR_YIELD_EVERY === 0) {
|
|
36041
36118
|
await yieldToEventLoop();
|
|
36042
36119
|
}
|
|
36043
|
-
|
|
36044
|
-
const entryPath = path35.join(relativePath || ".", d.name).replace(/\\/g, "/");
|
|
36045
|
-
const fullPath = path35.join(resolved, d.name);
|
|
36046
|
-
let isDir = d.isDirectory();
|
|
36047
|
-
if (d.isSymbolicLink()) {
|
|
36048
|
-
try {
|
|
36049
|
-
const targetStat = await fs32.promises.stat(fullPath);
|
|
36050
|
-
isDir = targetStat.isDirectory();
|
|
36051
|
-
} catch {
|
|
36052
|
-
isDir = false;
|
|
36053
|
-
}
|
|
36054
|
-
}
|
|
36055
|
-
entries.push({
|
|
36056
|
-
name: d.name,
|
|
36057
|
-
path: entryPath,
|
|
36058
|
-
isDir,
|
|
36059
|
-
isSymlink: d.isSymbolicLink()
|
|
36060
|
-
});
|
|
36120
|
+
entries.push(await mapDirEntry(names[i], relativePath, resolved));
|
|
36061
36121
|
}
|
|
36062
|
-
entries
|
|
36063
|
-
if (a.isDir !== b.isDir) return a.isDir ? -1 : 1;
|
|
36064
|
-
return a.name.localeCompare(b.name, void 0, { sensitivity: "base" });
|
|
36065
|
-
});
|
|
36066
|
-
return { entries };
|
|
36122
|
+
return { entries: sortListEntries(entries) };
|
|
36067
36123
|
} catch (err) {
|
|
36068
36124
|
const message = err instanceof Error ? err.message : String(err);
|
|
36069
36125
|
return { error: message };
|
|
36070
36126
|
}
|
|
36071
36127
|
}
|
|
36072
36128
|
|
|
36073
|
-
// src/files/read-file.ts
|
|
36074
|
-
|
|
36075
|
-
|
|
36076
|
-
|
|
36077
|
-
|
|
36129
|
+
// src/files/read-file/types.ts
|
|
36130
|
+
var LINE_CHUNK_SIZE = 64 * 1024;
|
|
36131
|
+
var READ_RANGE_YIELD_EVERY_BYTES = 256 * 1024;
|
|
36132
|
+
|
|
36133
|
+
// src/files/read-file/resolve-file-path.ts
|
|
36134
|
+
import fs34 from "node:fs";
|
|
36135
|
+
async function resolveFilePathAsync(relativePath, sessionParentPath = getBridgeRoot()) {
|
|
36136
|
+
const resolved = ensureUnderCwd(relativePath, sessionParentPath);
|
|
36078
36137
|
if (!resolved) return { error: "Path is outside working directory" };
|
|
36079
36138
|
let real;
|
|
36080
36139
|
try {
|
|
36081
|
-
real =
|
|
36140
|
+
real = await fs34.promises.realpath(resolved);
|
|
36082
36141
|
} catch {
|
|
36083
36142
|
real = resolved;
|
|
36084
36143
|
}
|
|
36085
|
-
|
|
36086
|
-
|
|
36087
|
-
|
|
36144
|
+
try {
|
|
36145
|
+
const stat2 = await fs34.promises.stat(real);
|
|
36146
|
+
if (!stat2.isFile()) return { error: "Not a file" };
|
|
36147
|
+
return real;
|
|
36148
|
+
} catch (err) {
|
|
36149
|
+
return { error: err instanceof Error ? err.message : String(err) };
|
|
36150
|
+
}
|
|
36088
36151
|
}
|
|
36089
|
-
|
|
36090
|
-
|
|
36091
|
-
|
|
36092
|
-
|
|
36152
|
+
|
|
36153
|
+
// src/files/read-file/read-file-range-async.ts
|
|
36154
|
+
import fs35 from "node:fs";
|
|
36155
|
+
import { StringDecoder } from "node:string_decoder";
|
|
36156
|
+
async function readFileRangeAsync(filePath, startLine, endLine, lineOffsetIn, lineChunkSize = LINE_CHUNK_SIZE) {
|
|
36157
|
+
const fileSize = (await fs35.promises.stat(filePath)).size;
|
|
36158
|
+
const fd = await fs35.promises.open(filePath, "r");
|
|
36093
36159
|
const bufSize = 64 * 1024;
|
|
36094
36160
|
const buf = Buffer.alloc(bufSize);
|
|
36095
36161
|
const decoder = new StringDecoder("utf8");
|
|
@@ -36100,9 +36166,18 @@ function readFileRange(filePath, startLine, endLine, lineOffsetIn, lineChunkSize
|
|
|
36100
36166
|
let skipLine0Chars = typeof lineOffsetIn === "number" ? lineOffsetIn : 0;
|
|
36101
36167
|
let line0CharsReturned = 0;
|
|
36102
36168
|
let line0Accum = "";
|
|
36169
|
+
let bytesSinceYield = 0;
|
|
36103
36170
|
try {
|
|
36104
|
-
let
|
|
36105
|
-
while (!done
|
|
36171
|
+
let position = 0;
|
|
36172
|
+
while (!done) {
|
|
36173
|
+
const { bytesRead } = await fd.read(buf, 0, bufSize, position);
|
|
36174
|
+
if (bytesRead === 0) break;
|
|
36175
|
+
position += bytesRead;
|
|
36176
|
+
bytesSinceYield += bytesRead;
|
|
36177
|
+
if (bytesSinceYield >= READ_RANGE_YIELD_EVERY_BYTES) {
|
|
36178
|
+
await yieldToEventLoop();
|
|
36179
|
+
bytesSinceYield = 0;
|
|
36180
|
+
}
|
|
36106
36181
|
const text = partial2 + decoder.write(buf.subarray(0, bytesRead));
|
|
36107
36182
|
partial2 = "";
|
|
36108
36183
|
let lineStart = 0;
|
|
@@ -36237,39 +36312,132 @@ function readFileRange(filePath, startLine, endLine, lineOffsetIn, lineChunkSize
|
|
|
36237
36312
|
}
|
|
36238
36313
|
return { content: resultLines.join("\n"), size: fileSize };
|
|
36239
36314
|
} finally {
|
|
36240
|
-
|
|
36315
|
+
await fd.close();
|
|
36241
36316
|
}
|
|
36242
36317
|
}
|
|
36243
|
-
|
|
36318
|
+
|
|
36319
|
+
// src/files/read-file/read-file-buffer-full-async.ts
|
|
36320
|
+
import fs36 from "node:fs";
|
|
36321
|
+
var READ_CHUNK_BYTES = 256 * 1024;
|
|
36322
|
+
async function readFileBufferFullAsync(filePath) {
|
|
36323
|
+
const stat2 = await fs36.promises.stat(filePath);
|
|
36324
|
+
const fd = await fs36.promises.open(filePath, "r");
|
|
36325
|
+
const chunks = [];
|
|
36326
|
+
let position = 0;
|
|
36327
|
+
let bytesSinceYield = 0;
|
|
36244
36328
|
try {
|
|
36245
|
-
|
|
36329
|
+
while (position < stat2.size) {
|
|
36330
|
+
const buf = Buffer.alloc(Math.min(READ_CHUNK_BYTES, stat2.size - position));
|
|
36331
|
+
const { bytesRead } = await fd.read(buf, 0, buf.length, position);
|
|
36332
|
+
if (bytesRead === 0) break;
|
|
36333
|
+
chunks.push(buf.subarray(0, bytesRead));
|
|
36334
|
+
position += bytesRead;
|
|
36335
|
+
bytesSinceYield += bytesRead;
|
|
36336
|
+
if (bytesSinceYield >= READ_RANGE_YIELD_EVERY_BYTES) {
|
|
36337
|
+
await yieldToEventLoop();
|
|
36338
|
+
bytesSinceYield = 0;
|
|
36339
|
+
}
|
|
36340
|
+
}
|
|
36341
|
+
} finally {
|
|
36342
|
+
await fd.close();
|
|
36343
|
+
}
|
|
36344
|
+
return { buffer: Buffer.concat(chunks), size: stat2.size };
|
|
36345
|
+
}
|
|
36346
|
+
|
|
36347
|
+
// src/files/read-file/read-file-full-async.ts
|
|
36348
|
+
async function readFileFullAsync(filePath) {
|
|
36349
|
+
const { buffer, size } = await readFileBufferFullAsync(filePath);
|
|
36350
|
+
const raw = buffer.toString("utf8");
|
|
36351
|
+
const lines = raw.split(/\r?\n/);
|
|
36352
|
+
return { content: raw, totalLines: lines.length, size };
|
|
36353
|
+
}
|
|
36354
|
+
|
|
36355
|
+
// src/files/read-file/guess-mime-type.ts
|
|
36356
|
+
var MIME_BY_EXT = {
|
|
36357
|
+
png: "image/png",
|
|
36358
|
+
jpg: "image/jpeg",
|
|
36359
|
+
jpeg: "image/jpeg",
|
|
36360
|
+
gif: "image/gif",
|
|
36361
|
+
bmp: "image/bmp",
|
|
36362
|
+
ico: "image/x-icon",
|
|
36363
|
+
webp: "image/webp",
|
|
36364
|
+
avif: "image/avif",
|
|
36365
|
+
svg: "image/svg+xml",
|
|
36366
|
+
pdf: "application/pdf",
|
|
36367
|
+
json: "application/json",
|
|
36368
|
+
html: "text/html",
|
|
36369
|
+
htm: "text/html",
|
|
36370
|
+
css: "text/css",
|
|
36371
|
+
js: "text/javascript",
|
|
36372
|
+
mjs: "text/javascript",
|
|
36373
|
+
ts: "text/typescript",
|
|
36374
|
+
txt: "text/plain",
|
|
36375
|
+
md: "text/markdown",
|
|
36376
|
+
xml: "application/xml",
|
|
36377
|
+
zip: "application/zip",
|
|
36378
|
+
gz: "application/gzip",
|
|
36379
|
+
wasm: "application/wasm"
|
|
36380
|
+
};
|
|
36381
|
+
function guessMimeType(filePath) {
|
|
36382
|
+
const ext = filePath.split(".").pop()?.toLowerCase() ?? "";
|
|
36383
|
+
return MIME_BY_EXT[ext] ?? "application/octet-stream";
|
|
36384
|
+
}
|
|
36385
|
+
|
|
36386
|
+
// src/files/read-file/read-file-binary-full-async.ts
|
|
36387
|
+
async function readFileBinaryFullAsync(filePath) {
|
|
36388
|
+
const { buffer, size } = await readFileBufferFullAsync(filePath);
|
|
36389
|
+
return {
|
|
36390
|
+
content: buffer.toString("base64"),
|
|
36391
|
+
size,
|
|
36392
|
+
mimeType: guessMimeType(filePath)
|
|
36393
|
+
};
|
|
36394
|
+
}
|
|
36395
|
+
|
|
36396
|
+
// src/files/read-file/index.ts
|
|
36397
|
+
async function readFileAsync(relativePath, startLine, endLine, lineOffset, lineChunkSize = LINE_CHUNK_SIZE, sessionParentPath = getBridgeRoot(), encoding = "utf8") {
|
|
36398
|
+
await yieldToEventLoop();
|
|
36399
|
+
try {
|
|
36400
|
+
const result = await resolveFilePathAsync(relativePath, sessionParentPath);
|
|
36246
36401
|
if (typeof result === "object") return result;
|
|
36402
|
+
const resolvedPath = result;
|
|
36247
36403
|
const hasRange = typeof startLine === "number" && typeof endLine === "number";
|
|
36404
|
+
if (encoding === "base64") {
|
|
36405
|
+
if (hasRange) return { error: "base64 encoding requires a full file read (no line range)" };
|
|
36406
|
+
const read2 = await readFileBinaryFullAsync(resolvedPath);
|
|
36407
|
+
return { ...read2, resolvedPath };
|
|
36408
|
+
}
|
|
36248
36409
|
if (hasRange) {
|
|
36249
|
-
return
|
|
36410
|
+
return readFileRangeAsync(resolvedPath, startLine, endLine, lineOffset, lineChunkSize);
|
|
36250
36411
|
}
|
|
36251
|
-
const
|
|
36252
|
-
|
|
36253
|
-
const lines = raw.split(/\r?\n/);
|
|
36254
|
-
return { content: raw, totalLines: lines.length, size: stat2.size };
|
|
36412
|
+
const read = await readFileFullAsync(resolvedPath);
|
|
36413
|
+
return { ...read, resolvedPath };
|
|
36255
36414
|
} catch (err) {
|
|
36256
36415
|
return { error: err instanceof Error ? err.message : String(err) };
|
|
36257
36416
|
}
|
|
36258
36417
|
}
|
|
36259
|
-
|
|
36260
|
-
|
|
36261
|
-
|
|
36418
|
+
|
|
36419
|
+
// src/files/resolve-file-browser-session-parent.ts
|
|
36420
|
+
function resolveFileBrowserSessionParent(sessionWorktreeManager, sessionId) {
|
|
36421
|
+
const sid = sessionId?.trim();
|
|
36422
|
+
if (sid) {
|
|
36423
|
+
sessionWorktreeManager.ensureRepoCheckoutPathsForSession(sid);
|
|
36424
|
+
const worktreeRoot = sessionWorktreeManager.getSessionWorktreeRootForSession(sid);
|
|
36425
|
+
if (worktreeRoot) return worktreeRoot;
|
|
36426
|
+
}
|
|
36427
|
+
return getBridgeRoot();
|
|
36262
36428
|
}
|
|
36263
36429
|
|
|
36264
36430
|
// src/files/handle-file-browser-search.ts
|
|
36265
36431
|
import path36 from "node:path";
|
|
36266
36432
|
var SEARCH_LIMIT = 100;
|
|
36267
|
-
function handleFileBrowserSearch(msg, socket, e2ee) {
|
|
36433
|
+
function handleFileBrowserSearch(msg, socket, e2ee, sessionWorktreeManager) {
|
|
36268
36434
|
void (async () => {
|
|
36269
36435
|
await yieldToEventLoop();
|
|
36270
36436
|
const q = typeof msg.q === "string" ? msg.q : "";
|
|
36271
|
-
const
|
|
36272
|
-
|
|
36437
|
+
const sessionParentPath = path36.resolve(
|
|
36438
|
+
sessionWorktreeManager != null ? resolveFileBrowserSessionParent(sessionWorktreeManager, msg.sessionId) : getBridgeRoot()
|
|
36439
|
+
);
|
|
36440
|
+
if (!await bridgeFileIndexIsPopulated(sessionParentPath)) {
|
|
36273
36441
|
const payload2 = {
|
|
36274
36442
|
type: "file_browser_search_response",
|
|
36275
36443
|
id: msg.id,
|
|
@@ -36279,7 +36447,7 @@ function handleFileBrowserSearch(msg, socket, e2ee) {
|
|
|
36279
36447
|
sendWsMessage(socket, e2ee ? e2ee.encryptFields(payload2, ["paths"]) : payload2);
|
|
36280
36448
|
return;
|
|
36281
36449
|
}
|
|
36282
|
-
const results = await searchBridgeFilePathsAsync(
|
|
36450
|
+
const results = await searchBridgeFilePathsAsync(sessionParentPath, q, SEARCH_LIMIT);
|
|
36283
36451
|
const payload = {
|
|
36284
36452
|
type: "file_browser_search_response",
|
|
36285
36453
|
id: msg.id,
|
|
@@ -36289,9 +36457,9 @@ function handleFileBrowserSearch(msg, socket, e2ee) {
|
|
|
36289
36457
|
sendWsMessage(socket, e2ee ? e2ee.encryptFields(payload, ["paths"]) : payload);
|
|
36290
36458
|
})();
|
|
36291
36459
|
}
|
|
36292
|
-
function triggerFileIndexBuild() {
|
|
36460
|
+
function triggerFileIndexBuild(sessionParentPath = getBridgeRoot()) {
|
|
36293
36461
|
setImmediate(() => {
|
|
36294
|
-
void ensureFileIndexAsync(
|
|
36462
|
+
void ensureFileIndexAsync(sessionParentPath).catch((e) => {
|
|
36295
36463
|
console.error("[file-index] Background build failed:", e);
|
|
36296
36464
|
});
|
|
36297
36465
|
});
|
|
@@ -36301,18 +36469,19 @@ function triggerFileIndexBuild() {
|
|
|
36301
36469
|
function sendFileBrowserMessage(socket, e2ee, payload) {
|
|
36302
36470
|
sendWsMessage(socket, e2ee ? e2ee.encryptFields(payload, ["entries", "content", "totalLines", "size", "lineOffset"]) : payload);
|
|
36303
36471
|
}
|
|
36304
|
-
function handleFileBrowserRequest(msg, socket, e2ee) {
|
|
36472
|
+
function handleFileBrowserRequest(msg, socket, e2ee, sessionWorktreeManager) {
|
|
36305
36473
|
void (async () => {
|
|
36306
36474
|
const reqPath = msg.path.replace(/^\/+/, "") || ".";
|
|
36307
36475
|
const op = msg.op === "read" ? "read" : "list";
|
|
36476
|
+
const sessionParentPath = sessionWorktreeManager != null ? resolveFileBrowserSessionParent(sessionWorktreeManager, msg.sessionId) : void 0;
|
|
36308
36477
|
if (op === "list") {
|
|
36309
|
-
const result = await listDirAsync(reqPath);
|
|
36478
|
+
const result = await listDirAsync(reqPath, sessionParentPath);
|
|
36310
36479
|
if ("error" in result) {
|
|
36311
36480
|
sendWsMessage(socket, { type: "file_browser_response", id: msg.id, error: result.error });
|
|
36312
36481
|
} else {
|
|
36313
36482
|
sendFileBrowserMessage(socket, e2ee, { type: "file_browser_response", id: msg.id, entries: result.entries });
|
|
36314
36483
|
if (reqPath === "." || reqPath === "") {
|
|
36315
|
-
triggerFileIndexBuild();
|
|
36484
|
+
triggerFileIndexBuild(sessionParentPath);
|
|
36316
36485
|
}
|
|
36317
36486
|
}
|
|
36318
36487
|
} else {
|
|
@@ -36320,7 +36489,16 @@ function handleFileBrowserRequest(msg, socket, e2ee) {
|
|
|
36320
36489
|
const endLine = typeof msg.endLine === "number" ? msg.endLine : void 0;
|
|
36321
36490
|
const lineOffset = typeof msg.lineOffset === "number" ? msg.lineOffset : void 0;
|
|
36322
36491
|
const lineChunkSize = typeof msg.lineChunkSize === "number" ? msg.lineChunkSize : void 0;
|
|
36323
|
-
const
|
|
36492
|
+
const encoding = msg.encoding === "base64" ? "base64" : "utf8";
|
|
36493
|
+
const result = await readFileAsync(
|
|
36494
|
+
reqPath,
|
|
36495
|
+
startLine,
|
|
36496
|
+
endLine,
|
|
36497
|
+
lineOffset,
|
|
36498
|
+
lineChunkSize,
|
|
36499
|
+
sessionParentPath,
|
|
36500
|
+
encoding
|
|
36501
|
+
);
|
|
36324
36502
|
if ("error" in result) {
|
|
36325
36503
|
sendWsMessage(socket, { type: "file_browser_response", id: msg.id, error: result.error });
|
|
36326
36504
|
} else {
|
|
@@ -36332,6 +36510,8 @@ function handleFileBrowserRequest(msg, socket, e2ee) {
|
|
|
36332
36510
|
size: result.size
|
|
36333
36511
|
};
|
|
36334
36512
|
if (result.lineOffset != null) payload.lineOffset = result.lineOffset;
|
|
36513
|
+
if (result.mimeType != null) payload.mimeType = result.mimeType;
|
|
36514
|
+
if (result.resolvedPath != null) payload.resolvedPath = result.resolvedPath;
|
|
36335
36515
|
sendFileBrowserMessage(socket, e2ee, payload);
|
|
36336
36516
|
}
|
|
36337
36517
|
}
|
|
@@ -36339,21 +36519,27 @@ function handleFileBrowserRequest(msg, socket, e2ee) {
|
|
|
36339
36519
|
}
|
|
36340
36520
|
|
|
36341
36521
|
// src/routing/handlers/file-browser-messages.ts
|
|
36342
|
-
function handleFileBrowserRequestMessage(msg, { getWs, e2ee }) {
|
|
36522
|
+
function handleFileBrowserRequestMessage(msg, { getWs, e2ee, sessionWorktreeManager }) {
|
|
36343
36523
|
if (typeof msg.id !== "string" || typeof msg.path !== "string") return;
|
|
36344
36524
|
const socket = getWs();
|
|
36345
36525
|
if (!socket) return;
|
|
36346
36526
|
handleFileBrowserRequest(
|
|
36347
36527
|
msg,
|
|
36348
36528
|
socket,
|
|
36349
|
-
e2ee
|
|
36529
|
+
e2ee,
|
|
36530
|
+
sessionWorktreeManager
|
|
36350
36531
|
);
|
|
36351
36532
|
}
|
|
36352
|
-
function handleFileBrowserSearchMessage(msg, { getWs, e2ee }) {
|
|
36533
|
+
function handleFileBrowserSearchMessage(msg, { getWs, e2ee, sessionWorktreeManager }) {
|
|
36353
36534
|
if (typeof msg.id !== "string") return;
|
|
36354
36535
|
const socket = getWs();
|
|
36355
36536
|
if (!socket) return;
|
|
36356
|
-
handleFileBrowserSearch(
|
|
36537
|
+
handleFileBrowserSearch(
|
|
36538
|
+
msg,
|
|
36539
|
+
socket,
|
|
36540
|
+
e2ee,
|
|
36541
|
+
sessionWorktreeManager
|
|
36542
|
+
);
|
|
36357
36543
|
}
|
|
36358
36544
|
|
|
36359
36545
|
// src/routing/handlers/skill-layout-request.ts
|
|
@@ -36367,7 +36553,7 @@ function handleSkillLayoutRequest(msg, deps) {
|
|
|
36367
36553
|
}
|
|
36368
36554
|
|
|
36369
36555
|
// src/skills/install-remote-skills.ts
|
|
36370
|
-
import
|
|
36556
|
+
import fs37 from "node:fs";
|
|
36371
36557
|
import path37 from "node:path";
|
|
36372
36558
|
function installRemoteSkills(cwd, targetDir, items) {
|
|
36373
36559
|
const installed2 = [];
|
|
@@ -36383,11 +36569,11 @@ function installRemoteSkills(cwd, targetDir, items) {
|
|
|
36383
36569
|
for (const f of item.files) {
|
|
36384
36570
|
if (typeof f.path !== "string" || !f.text && !f.base64) continue;
|
|
36385
36571
|
const dest = path37.join(skillDir, f.path);
|
|
36386
|
-
|
|
36572
|
+
fs37.mkdirSync(path37.dirname(dest), { recursive: true });
|
|
36387
36573
|
if (f.text !== void 0) {
|
|
36388
|
-
|
|
36574
|
+
fs37.writeFileSync(dest, f.text, "utf8");
|
|
36389
36575
|
} else if (f.base64) {
|
|
36390
|
-
|
|
36576
|
+
fs37.writeFileSync(dest, Buffer.from(f.base64, "base64"));
|
|
36391
36577
|
}
|
|
36392
36578
|
}
|
|
36393
36579
|
installed2.push({
|
|
@@ -36545,7 +36731,7 @@ var handleSessionDiscardedMessage = (msg, deps) => {
|
|
|
36545
36731
|
};
|
|
36546
36732
|
|
|
36547
36733
|
// src/routing/handlers/revert-turn-snapshot.ts
|
|
36548
|
-
import * as
|
|
36734
|
+
import * as fs38 from "node:fs";
|
|
36549
36735
|
var handleRevertTurnSnapshotMessage = (msg, deps) => {
|
|
36550
36736
|
const id = typeof msg.id === "string" ? msg.id : "";
|
|
36551
36737
|
const sessionId = typeof msg.sessionId === "string" ? msg.sessionId : "";
|
|
@@ -36557,7 +36743,9 @@ var handleRevertTurnSnapshotMessage = (msg, deps) => {
|
|
|
36557
36743
|
if (!s) return;
|
|
36558
36744
|
const agentBase = sessionWorktreeManager.getSessionWorktreeRootForSession(sessionId) ?? getBridgeRoot();
|
|
36559
36745
|
const file2 = snapshotFilePath(agentBase, turnId);
|
|
36560
|
-
|
|
36746
|
+
try {
|
|
36747
|
+
await fs38.promises.access(file2, fs38.constants.F_OK);
|
|
36748
|
+
} catch {
|
|
36561
36749
|
sendWsMessage(s, {
|
|
36562
36750
|
type: "revert_turn_snapshot_result",
|
|
36563
36751
|
id,
|
|
@@ -36676,9 +36864,7 @@ function handleBridgeMessage(data, deps) {
|
|
|
36676
36864
|
if (!deps.getWs()) return;
|
|
36677
36865
|
const msg = parseApiToBridgeMessage(normalizeInboundBridgeWebSocketJson(data), deps.log);
|
|
36678
36866
|
if (!msg) return;
|
|
36679
|
-
|
|
36680
|
-
dispatchBridgeMessage(msg, deps);
|
|
36681
|
-
});
|
|
36867
|
+
dispatchBridgeMessage(msg, deps);
|
|
36682
36868
|
}
|
|
36683
36869
|
|
|
36684
36870
|
// src/auth/refresh-bridge-tokens.ts
|
|
@@ -37323,25 +37509,27 @@ async function createBridgeConnection(options) {
|
|
|
37323
37509
|
}
|
|
37324
37510
|
function sendAgentCapabilitiesToBridge(info) {
|
|
37325
37511
|
if (!Array.isArray(info.configOptions) || info.configOptions.length === 0) return;
|
|
37326
|
-
|
|
37327
|
-
|
|
37328
|
-
|
|
37329
|
-
|
|
37330
|
-
|
|
37331
|
-
|
|
37332
|
-
|
|
37333
|
-
|
|
37334
|
-
|
|
37335
|
-
|
|
37336
|
-
|
|
37337
|
-
|
|
37338
|
-
|
|
37339
|
-
|
|
37340
|
-
|
|
37341
|
-
|
|
37342
|
-
|
|
37343
|
-
|
|
37344
|
-
|
|
37512
|
+
setImmediate(() => {
|
|
37513
|
+
let changed = false;
|
|
37514
|
+
try {
|
|
37515
|
+
changed = withCliSqliteSync(
|
|
37516
|
+
(db) => upsertCliAgentCapabilityCache(db, {
|
|
37517
|
+
workspaceId,
|
|
37518
|
+
agentType: info.agentType,
|
|
37519
|
+
configOptions: info.configOptions
|
|
37520
|
+
})
|
|
37521
|
+
);
|
|
37522
|
+
} catch (e) {
|
|
37523
|
+
if (e instanceof CliSqliteInterrupted) return;
|
|
37524
|
+
}
|
|
37525
|
+
if (!changed) return;
|
|
37526
|
+
const socket = getWs();
|
|
37527
|
+
if (!socket || socket.readyState !== wrapper_default.OPEN) return;
|
|
37528
|
+
sendWsMessage(socket, {
|
|
37529
|
+
type: "agent_capabilities",
|
|
37530
|
+
agentType: info.agentType,
|
|
37531
|
+
configOptions: info.configOptions
|
|
37532
|
+
});
|
|
37345
37533
|
});
|
|
37346
37534
|
}
|
|
37347
37535
|
const worktreesRootPath = options.worktreesRootPath ?? defaultWorktreesRootPath();
|