@buildautomaton/cli 0.1.35 → 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 +286 -106
- package/dist/cli.js.map +4 -4
- package/dist/index.js +280 -100
- 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
|
@@ -23964,7 +23964,7 @@ function installBridgeProcessResilience() {
|
|
|
23964
23964
|
}
|
|
23965
23965
|
|
|
23966
23966
|
// src/cli-version.ts
|
|
23967
|
-
var CLI_VERSION = "0.1.
|
|
23967
|
+
var CLI_VERSION = "0.1.36".length > 0 ? "0.1.36" : "0.0.0-dev";
|
|
23968
23968
|
|
|
23969
23969
|
// src/connection/heartbeat/constants.ts
|
|
23970
23970
|
var BRIDGE_APP_HEARTBEAT_INTERVAL_MS = 1e4;
|
|
@@ -25287,6 +25287,7 @@ function recordMigrationAndPruneCheckpointLegacy(db, migration, applied2) {
|
|
|
25287
25287
|
var CHECKPOINT_V1 = "001_cli_sqlite_checkpoint_v1";
|
|
25288
25288
|
var CHECKPOINT_V1_SQL = readCliSqliteMigrationSql("001_cli_sqlite_checkpoint_v1.sql");
|
|
25289
25289
|
var AGENT_CAPABILITIES_SQL = readCliSqliteMigrationSql("002_agent_capabilities.sql");
|
|
25290
|
+
var FILE_INDEX_PARENT_PATHS_SQL = readCliSqliteMigrationSql("003_file_index_parent_paths.sql");
|
|
25290
25291
|
function agentCapabilitiesTableState(db) {
|
|
25291
25292
|
const rows = db.all(
|
|
25292
25293
|
`SELECT name FROM sqlite_master WHERE type='table' AND name IN ('agent_capabilities', 'agent_capability_cache')`
|
|
@@ -25320,6 +25321,18 @@ var CLI_SQLITE_MIGRATIONS = [
|
|
|
25320
25321
|
db.exec(AGENT_CAPABILITIES_SQL);
|
|
25321
25322
|
},
|
|
25322
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
|
+
}
|
|
25323
25336
|
}
|
|
25324
25337
|
];
|
|
25325
25338
|
function migrateCliSqlite(db) {
|
|
@@ -25358,6 +25371,10 @@ var CliSqliteInterrupted = class extends Error {
|
|
|
25358
25371
|
}
|
|
25359
25372
|
};
|
|
25360
25373
|
function applyCliSqliteConcurrencyPragmas(db) {
|
|
25374
|
+
try {
|
|
25375
|
+
db.run("PRAGMA foreign_keys = ON");
|
|
25376
|
+
} catch {
|
|
25377
|
+
}
|
|
25361
25378
|
try {
|
|
25362
25379
|
db.exec("PRAGMA journal_mode = WAL");
|
|
25363
25380
|
} catch {
|
|
@@ -33783,8 +33800,18 @@ import path28 from "node:path";
|
|
|
33783
33800
|
// src/files/index/walk-workspace-tree.ts
|
|
33784
33801
|
import fs24 from "node:fs";
|
|
33785
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
|
+
]);
|
|
33786
33813
|
function shouldSkipWorkspaceWalkEntry(name) {
|
|
33787
|
-
return
|
|
33814
|
+
return DEPENDENCY_INSTALL_DIR_NAMES.has(name);
|
|
33788
33815
|
}
|
|
33789
33816
|
async function walkWorkspaceTreeAsync(dir, baseDir, onFile, state) {
|
|
33790
33817
|
let names;
|
|
@@ -33855,14 +33882,26 @@ var FILE_INDEX_INTERRUPT_CHECK_EVERY = 256;
|
|
|
33855
33882
|
function assertNotShutdown() {
|
|
33856
33883
|
if (isCliImmediateShutdownRequested()) throw new CliSqliteInterrupted();
|
|
33857
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
|
+
}
|
|
33858
33897
|
function persistFileIndexPaths(resolved, paths) {
|
|
33859
33898
|
return withCliSqliteSync((db) => {
|
|
33860
|
-
const h = getCwdHashForFileIndex(resolved);
|
|
33861
33899
|
let pathCount = 0;
|
|
33862
33900
|
db.run("BEGIN IMMEDIATE");
|
|
33863
33901
|
try {
|
|
33864
|
-
|
|
33865
|
-
|
|
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 (?, ?)");
|
|
33866
33905
|
try {
|
|
33867
33906
|
let batch = 0;
|
|
33868
33907
|
for (const rel of paths) {
|
|
@@ -33870,7 +33909,7 @@ function persistFileIndexPaths(resolved, paths) {
|
|
|
33870
33909
|
batch = 0;
|
|
33871
33910
|
assertNotShutdown();
|
|
33872
33911
|
}
|
|
33873
|
-
ins.run([
|
|
33912
|
+
ins.run([parentId, rel]);
|
|
33874
33913
|
pathCount += 1;
|
|
33875
33914
|
}
|
|
33876
33915
|
} finally {
|
|
@@ -33912,36 +33951,38 @@ async function buildFileIndexAsync(cwd) {
|
|
|
33912
33951
|
// src/files/index/ensure-file-index.ts
|
|
33913
33952
|
import path29 from "node:path";
|
|
33914
33953
|
|
|
33915
|
-
// src/files/index/file-index-dependency-path.ts
|
|
33916
|
-
function sqliteExprBridgeFileIndexDependencyRank() {
|
|
33917
|
-
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`;
|
|
33918
|
-
}
|
|
33919
|
-
|
|
33920
33954
|
// src/files/index/search-file-index.ts
|
|
33921
33955
|
function escapeLikePattern(fragment) {
|
|
33922
33956
|
return fragment.replace(/\\/g, "\\\\").replace(/%/g, "\\%").replace(/_/g, "\\_");
|
|
33923
33957
|
}
|
|
33924
33958
|
function bridgeFileIndexIsPopulatedWithDb(resolvedCwd, db) {
|
|
33925
|
-
const
|
|
33926
|
-
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]);
|
|
33927
33960
|
return row != null;
|
|
33928
33961
|
}
|
|
33929
33962
|
function bridgeFileIndexPathCountWithDb(resolvedCwd, db) {
|
|
33930
|
-
const
|
|
33931
|
-
|
|
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
|
+
);
|
|
33932
33970
|
const c = row?.c ?? 0;
|
|
33933
33971
|
return Number(c);
|
|
33934
33972
|
}
|
|
33935
33973
|
function searchBridgeFilePathsWithDb(resolvedCwd, query, limit, db) {
|
|
33936
33974
|
const q = query.trim().toLowerCase();
|
|
33937
33975
|
if (!q) return [];
|
|
33938
|
-
const h = getCwdHashForFileIndex(resolvedCwd);
|
|
33939
33976
|
const pattern = `%${escapeLikePattern(q)}%`;
|
|
33940
33977
|
const lim = Math.max(0, Math.min(1e4, Math.floor(limit)));
|
|
33941
|
-
const depRank = sqliteExprBridgeFileIndexDependencyRank();
|
|
33942
33978
|
const rows = db.all(
|
|
33943
|
-
`SELECT path
|
|
33944
|
-
|
|
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]
|
|
33945
33986
|
);
|
|
33946
33987
|
return rows.map((r) => String(r.path));
|
|
33947
33988
|
}
|
|
@@ -33971,9 +34012,7 @@ async function ensureFileIndexAsync(cwd) {
|
|
|
33971
34012
|
var DEBOUNCE_MS = 900;
|
|
33972
34013
|
function shouldIgnoreRelative(rel) {
|
|
33973
34014
|
const n = rel.replace(/\\/g, "/");
|
|
33974
|
-
|
|
33975
|
-
if (n.includes("/.buildautomaton/") || n.startsWith(".buildautomaton/")) return true;
|
|
33976
|
-
return false;
|
|
34015
|
+
return n.split("/").some((segment) => shouldSkipWorkspaceWalkEntry(segment));
|
|
33977
34016
|
}
|
|
33978
34017
|
function attachWatchErrorLog(w) {
|
|
33979
34018
|
w.on("error", (err) => {
|
|
@@ -36016,9 +36055,8 @@ var handleSkillCallMessage = (msg, { getWs, log: log2 }) => {
|
|
|
36016
36055
|
);
|
|
36017
36056
|
};
|
|
36018
36057
|
|
|
36019
|
-
// src/files/list-dir.ts
|
|
36020
|
-
import
|
|
36021
|
-
import path35 from "node:path";
|
|
36058
|
+
// src/files/list-dir/index.ts
|
|
36059
|
+
import fs33 from "node:fs";
|
|
36022
36060
|
|
|
36023
36061
|
// src/files/ensure-under-cwd.ts
|
|
36024
36062
|
import path34 from "node:path";
|
|
@@ -36031,71 +36069,93 @@ function ensureUnderCwd(relativePath, cwd = getBridgeRoot()) {
|
|
|
36031
36069
|
return resolved;
|
|
36032
36070
|
}
|
|
36033
36071
|
|
|
36034
|
-
// src/files/list-dir.ts
|
|
36072
|
+
// src/files/list-dir/types.ts
|
|
36035
36073
|
var LIST_DIR_YIELD_EVERY = 256;
|
|
36036
|
-
|
|
36037
|
-
|
|
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);
|
|
36038
36110
|
if (!resolved) {
|
|
36039
36111
|
return { error: "Path is outside working directory" };
|
|
36040
36112
|
}
|
|
36041
36113
|
try {
|
|
36042
|
-
const names = await
|
|
36043
|
-
const visible = names.filter((d) => !d.name.startsWith("."));
|
|
36114
|
+
const names = await fs33.promises.readdir(resolved, { withFileTypes: true });
|
|
36044
36115
|
const entries = [];
|
|
36045
|
-
for (let i = 0; i <
|
|
36116
|
+
for (let i = 0; i < names.length; i++) {
|
|
36046
36117
|
if (i > 0 && i % LIST_DIR_YIELD_EVERY === 0) {
|
|
36047
36118
|
await yieldToEventLoop();
|
|
36048
36119
|
}
|
|
36049
|
-
|
|
36050
|
-
const entryPath = path35.join(relativePath || ".", d.name).replace(/\\/g, "/");
|
|
36051
|
-
const fullPath = path35.join(resolved, d.name);
|
|
36052
|
-
let isDir = d.isDirectory();
|
|
36053
|
-
if (d.isSymbolicLink()) {
|
|
36054
|
-
try {
|
|
36055
|
-
const targetStat = await fs32.promises.stat(fullPath);
|
|
36056
|
-
isDir = targetStat.isDirectory();
|
|
36057
|
-
} catch {
|
|
36058
|
-
isDir = false;
|
|
36059
|
-
}
|
|
36060
|
-
}
|
|
36061
|
-
entries.push({
|
|
36062
|
-
name: d.name,
|
|
36063
|
-
path: entryPath,
|
|
36064
|
-
isDir,
|
|
36065
|
-
isSymlink: d.isSymbolicLink()
|
|
36066
|
-
});
|
|
36120
|
+
entries.push(await mapDirEntry(names[i], relativePath, resolved));
|
|
36067
36121
|
}
|
|
36068
|
-
entries
|
|
36069
|
-
if (a.isDir !== b.isDir) return a.isDir ? -1 : 1;
|
|
36070
|
-
return a.name.localeCompare(b.name, void 0, { sensitivity: "base" });
|
|
36071
|
-
});
|
|
36072
|
-
return { entries };
|
|
36122
|
+
return { entries: sortListEntries(entries) };
|
|
36073
36123
|
} catch (err) {
|
|
36074
36124
|
const message = err instanceof Error ? err.message : String(err);
|
|
36075
36125
|
return { error: message };
|
|
36076
36126
|
}
|
|
36077
36127
|
}
|
|
36078
36128
|
|
|
36079
|
-
// src/files/read-file.ts
|
|
36080
|
-
|
|
36081
|
-
|
|
36082
|
-
|
|
36083
|
-
|
|
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);
|
|
36084
36137
|
if (!resolved) return { error: "Path is outside working directory" };
|
|
36085
36138
|
let real;
|
|
36086
36139
|
try {
|
|
36087
|
-
real =
|
|
36140
|
+
real = await fs34.promises.realpath(resolved);
|
|
36088
36141
|
} catch {
|
|
36089
36142
|
real = resolved;
|
|
36090
36143
|
}
|
|
36091
|
-
|
|
36092
|
-
|
|
36093
|
-
|
|
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
|
+
}
|
|
36094
36151
|
}
|
|
36095
|
-
|
|
36096
|
-
|
|
36097
|
-
|
|
36098
|
-
|
|
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");
|
|
36099
36159
|
const bufSize = 64 * 1024;
|
|
36100
36160
|
const buf = Buffer.alloc(bufSize);
|
|
36101
36161
|
const decoder = new StringDecoder("utf8");
|
|
@@ -36106,9 +36166,18 @@ function readFileRange(filePath, startLine, endLine, lineOffsetIn, lineChunkSize
|
|
|
36106
36166
|
let skipLine0Chars = typeof lineOffsetIn === "number" ? lineOffsetIn : 0;
|
|
36107
36167
|
let line0CharsReturned = 0;
|
|
36108
36168
|
let line0Accum = "";
|
|
36169
|
+
let bytesSinceYield = 0;
|
|
36109
36170
|
try {
|
|
36110
|
-
let
|
|
36111
|
-
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
|
+
}
|
|
36112
36181
|
const text = partial2 + decoder.write(buf.subarray(0, bytesRead));
|
|
36113
36182
|
partial2 = "";
|
|
36114
36183
|
let lineStart = 0;
|
|
@@ -36243,39 +36312,132 @@ function readFileRange(filePath, startLine, endLine, lineOffsetIn, lineChunkSize
|
|
|
36243
36312
|
}
|
|
36244
36313
|
return { content: resultLines.join("\n"), size: fileSize };
|
|
36245
36314
|
} finally {
|
|
36246
|
-
|
|
36315
|
+
await fd.close();
|
|
36247
36316
|
}
|
|
36248
36317
|
}
|
|
36249
|
-
|
|
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;
|
|
36250
36328
|
try {
|
|
36251
|
-
|
|
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);
|
|
36252
36401
|
if (typeof result === "object") return result;
|
|
36402
|
+
const resolvedPath = result;
|
|
36253
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
|
+
}
|
|
36254
36409
|
if (hasRange) {
|
|
36255
|
-
return
|
|
36410
|
+
return readFileRangeAsync(resolvedPath, startLine, endLine, lineOffset, lineChunkSize);
|
|
36256
36411
|
}
|
|
36257
|
-
const
|
|
36258
|
-
|
|
36259
|
-
const lines = raw.split(/\r?\n/);
|
|
36260
|
-
return { content: raw, totalLines: lines.length, size: stat2.size };
|
|
36412
|
+
const read = await readFileFullAsync(resolvedPath);
|
|
36413
|
+
return { ...read, resolvedPath };
|
|
36261
36414
|
} catch (err) {
|
|
36262
36415
|
return { error: err instanceof Error ? err.message : String(err) };
|
|
36263
36416
|
}
|
|
36264
36417
|
}
|
|
36265
|
-
|
|
36266
|
-
|
|
36267
|
-
|
|
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();
|
|
36268
36428
|
}
|
|
36269
36429
|
|
|
36270
36430
|
// src/files/handle-file-browser-search.ts
|
|
36271
36431
|
import path36 from "node:path";
|
|
36272
36432
|
var SEARCH_LIMIT = 100;
|
|
36273
|
-
function handleFileBrowserSearch(msg, socket, e2ee) {
|
|
36433
|
+
function handleFileBrowserSearch(msg, socket, e2ee, sessionWorktreeManager) {
|
|
36274
36434
|
void (async () => {
|
|
36275
36435
|
await yieldToEventLoop();
|
|
36276
36436
|
const q = typeof msg.q === "string" ? msg.q : "";
|
|
36277
|
-
const
|
|
36278
|
-
|
|
36437
|
+
const sessionParentPath = path36.resolve(
|
|
36438
|
+
sessionWorktreeManager != null ? resolveFileBrowserSessionParent(sessionWorktreeManager, msg.sessionId) : getBridgeRoot()
|
|
36439
|
+
);
|
|
36440
|
+
if (!await bridgeFileIndexIsPopulated(sessionParentPath)) {
|
|
36279
36441
|
const payload2 = {
|
|
36280
36442
|
type: "file_browser_search_response",
|
|
36281
36443
|
id: msg.id,
|
|
@@ -36285,7 +36447,7 @@ function handleFileBrowserSearch(msg, socket, e2ee) {
|
|
|
36285
36447
|
sendWsMessage(socket, e2ee ? e2ee.encryptFields(payload2, ["paths"]) : payload2);
|
|
36286
36448
|
return;
|
|
36287
36449
|
}
|
|
36288
|
-
const results = await searchBridgeFilePathsAsync(
|
|
36450
|
+
const results = await searchBridgeFilePathsAsync(sessionParentPath, q, SEARCH_LIMIT);
|
|
36289
36451
|
const payload = {
|
|
36290
36452
|
type: "file_browser_search_response",
|
|
36291
36453
|
id: msg.id,
|
|
@@ -36295,9 +36457,9 @@ function handleFileBrowserSearch(msg, socket, e2ee) {
|
|
|
36295
36457
|
sendWsMessage(socket, e2ee ? e2ee.encryptFields(payload, ["paths"]) : payload);
|
|
36296
36458
|
})();
|
|
36297
36459
|
}
|
|
36298
|
-
function triggerFileIndexBuild() {
|
|
36460
|
+
function triggerFileIndexBuild(sessionParentPath = getBridgeRoot()) {
|
|
36299
36461
|
setImmediate(() => {
|
|
36300
|
-
void ensureFileIndexAsync(
|
|
36462
|
+
void ensureFileIndexAsync(sessionParentPath).catch((e) => {
|
|
36301
36463
|
console.error("[file-index] Background build failed:", e);
|
|
36302
36464
|
});
|
|
36303
36465
|
});
|
|
@@ -36307,18 +36469,19 @@ function triggerFileIndexBuild() {
|
|
|
36307
36469
|
function sendFileBrowserMessage(socket, e2ee, payload) {
|
|
36308
36470
|
sendWsMessage(socket, e2ee ? e2ee.encryptFields(payload, ["entries", "content", "totalLines", "size", "lineOffset"]) : payload);
|
|
36309
36471
|
}
|
|
36310
|
-
function handleFileBrowserRequest(msg, socket, e2ee) {
|
|
36472
|
+
function handleFileBrowserRequest(msg, socket, e2ee, sessionWorktreeManager) {
|
|
36311
36473
|
void (async () => {
|
|
36312
36474
|
const reqPath = msg.path.replace(/^\/+/, "") || ".";
|
|
36313
36475
|
const op = msg.op === "read" ? "read" : "list";
|
|
36476
|
+
const sessionParentPath = sessionWorktreeManager != null ? resolveFileBrowserSessionParent(sessionWorktreeManager, msg.sessionId) : void 0;
|
|
36314
36477
|
if (op === "list") {
|
|
36315
|
-
const result = await listDirAsync(reqPath);
|
|
36478
|
+
const result = await listDirAsync(reqPath, sessionParentPath);
|
|
36316
36479
|
if ("error" in result) {
|
|
36317
36480
|
sendWsMessage(socket, { type: "file_browser_response", id: msg.id, error: result.error });
|
|
36318
36481
|
} else {
|
|
36319
36482
|
sendFileBrowserMessage(socket, e2ee, { type: "file_browser_response", id: msg.id, entries: result.entries });
|
|
36320
36483
|
if (reqPath === "." || reqPath === "") {
|
|
36321
|
-
triggerFileIndexBuild();
|
|
36484
|
+
triggerFileIndexBuild(sessionParentPath);
|
|
36322
36485
|
}
|
|
36323
36486
|
}
|
|
36324
36487
|
} else {
|
|
@@ -36326,7 +36489,16 @@ function handleFileBrowserRequest(msg, socket, e2ee) {
|
|
|
36326
36489
|
const endLine = typeof msg.endLine === "number" ? msg.endLine : void 0;
|
|
36327
36490
|
const lineOffset = typeof msg.lineOffset === "number" ? msg.lineOffset : void 0;
|
|
36328
36491
|
const lineChunkSize = typeof msg.lineChunkSize === "number" ? msg.lineChunkSize : void 0;
|
|
36329
|
-
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
|
+
);
|
|
36330
36502
|
if ("error" in result) {
|
|
36331
36503
|
sendWsMessage(socket, { type: "file_browser_response", id: msg.id, error: result.error });
|
|
36332
36504
|
} else {
|
|
@@ -36338,6 +36510,8 @@ function handleFileBrowserRequest(msg, socket, e2ee) {
|
|
|
36338
36510
|
size: result.size
|
|
36339
36511
|
};
|
|
36340
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;
|
|
36341
36515
|
sendFileBrowserMessage(socket, e2ee, payload);
|
|
36342
36516
|
}
|
|
36343
36517
|
}
|
|
@@ -36345,21 +36519,27 @@ function handleFileBrowserRequest(msg, socket, e2ee) {
|
|
|
36345
36519
|
}
|
|
36346
36520
|
|
|
36347
36521
|
// src/routing/handlers/file-browser-messages.ts
|
|
36348
|
-
function handleFileBrowserRequestMessage(msg, { getWs, e2ee }) {
|
|
36522
|
+
function handleFileBrowserRequestMessage(msg, { getWs, e2ee, sessionWorktreeManager }) {
|
|
36349
36523
|
if (typeof msg.id !== "string" || typeof msg.path !== "string") return;
|
|
36350
36524
|
const socket = getWs();
|
|
36351
36525
|
if (!socket) return;
|
|
36352
36526
|
handleFileBrowserRequest(
|
|
36353
36527
|
msg,
|
|
36354
36528
|
socket,
|
|
36355
|
-
e2ee
|
|
36529
|
+
e2ee,
|
|
36530
|
+
sessionWorktreeManager
|
|
36356
36531
|
);
|
|
36357
36532
|
}
|
|
36358
|
-
function handleFileBrowserSearchMessage(msg, { getWs, e2ee }) {
|
|
36533
|
+
function handleFileBrowserSearchMessage(msg, { getWs, e2ee, sessionWorktreeManager }) {
|
|
36359
36534
|
if (typeof msg.id !== "string") return;
|
|
36360
36535
|
const socket = getWs();
|
|
36361
36536
|
if (!socket) return;
|
|
36362
|
-
handleFileBrowserSearch(
|
|
36537
|
+
handleFileBrowserSearch(
|
|
36538
|
+
msg,
|
|
36539
|
+
socket,
|
|
36540
|
+
e2ee,
|
|
36541
|
+
sessionWorktreeManager
|
|
36542
|
+
);
|
|
36363
36543
|
}
|
|
36364
36544
|
|
|
36365
36545
|
// src/routing/handlers/skill-layout-request.ts
|
|
@@ -36373,7 +36553,7 @@ function handleSkillLayoutRequest(msg, deps) {
|
|
|
36373
36553
|
}
|
|
36374
36554
|
|
|
36375
36555
|
// src/skills/install-remote-skills.ts
|
|
36376
|
-
import
|
|
36556
|
+
import fs37 from "node:fs";
|
|
36377
36557
|
import path37 from "node:path";
|
|
36378
36558
|
function installRemoteSkills(cwd, targetDir, items) {
|
|
36379
36559
|
const installed2 = [];
|
|
@@ -36389,11 +36569,11 @@ function installRemoteSkills(cwd, targetDir, items) {
|
|
|
36389
36569
|
for (const f of item.files) {
|
|
36390
36570
|
if (typeof f.path !== "string" || !f.text && !f.base64) continue;
|
|
36391
36571
|
const dest = path37.join(skillDir, f.path);
|
|
36392
|
-
|
|
36572
|
+
fs37.mkdirSync(path37.dirname(dest), { recursive: true });
|
|
36393
36573
|
if (f.text !== void 0) {
|
|
36394
|
-
|
|
36574
|
+
fs37.writeFileSync(dest, f.text, "utf8");
|
|
36395
36575
|
} else if (f.base64) {
|
|
36396
|
-
|
|
36576
|
+
fs37.writeFileSync(dest, Buffer.from(f.base64, "base64"));
|
|
36397
36577
|
}
|
|
36398
36578
|
}
|
|
36399
36579
|
installed2.push({
|
|
@@ -36551,7 +36731,7 @@ var handleSessionDiscardedMessage = (msg, deps) => {
|
|
|
36551
36731
|
};
|
|
36552
36732
|
|
|
36553
36733
|
// src/routing/handlers/revert-turn-snapshot.ts
|
|
36554
|
-
import * as
|
|
36734
|
+
import * as fs38 from "node:fs";
|
|
36555
36735
|
var handleRevertTurnSnapshotMessage = (msg, deps) => {
|
|
36556
36736
|
const id = typeof msg.id === "string" ? msg.id : "";
|
|
36557
36737
|
const sessionId = typeof msg.sessionId === "string" ? msg.sessionId : "";
|
|
@@ -36564,7 +36744,7 @@ var handleRevertTurnSnapshotMessage = (msg, deps) => {
|
|
|
36564
36744
|
const agentBase = sessionWorktreeManager.getSessionWorktreeRootForSession(sessionId) ?? getBridgeRoot();
|
|
36565
36745
|
const file2 = snapshotFilePath(agentBase, turnId);
|
|
36566
36746
|
try {
|
|
36567
|
-
await
|
|
36747
|
+
await fs38.promises.access(file2, fs38.constants.F_OK);
|
|
36568
36748
|
} catch {
|
|
36569
36749
|
sendWsMessage(s, {
|
|
36570
36750
|
type: "revert_turn_snapshot_result",
|