@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/cli.js
CHANGED
|
@@ -974,7 +974,7 @@ var require_command = __commonJS({
|
|
|
974
974
|
var EventEmitter2 = __require("node:events").EventEmitter;
|
|
975
975
|
var childProcess2 = __require("node:child_process");
|
|
976
976
|
var path43 = __require("node:path");
|
|
977
|
-
var
|
|
977
|
+
var fs41 = __require("node:fs");
|
|
978
978
|
var process8 = __require("node:process");
|
|
979
979
|
var { Argument: Argument2, humanReadableArgName } = require_argument();
|
|
980
980
|
var { CommanderError: CommanderError2 } = require_error();
|
|
@@ -1907,10 +1907,10 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1907
1907
|
const sourceExt = [".js", ".ts", ".tsx", ".mjs", ".cjs"];
|
|
1908
1908
|
function findFile(baseDir, baseName) {
|
|
1909
1909
|
const localBin = path43.resolve(baseDir, baseName);
|
|
1910
|
-
if (
|
|
1910
|
+
if (fs41.existsSync(localBin)) return localBin;
|
|
1911
1911
|
if (sourceExt.includes(path43.extname(baseName))) return void 0;
|
|
1912
1912
|
const foundExt = sourceExt.find(
|
|
1913
|
-
(ext) =>
|
|
1913
|
+
(ext) => fs41.existsSync(`${localBin}${ext}`)
|
|
1914
1914
|
);
|
|
1915
1915
|
if (foundExt) return `${localBin}${foundExt}`;
|
|
1916
1916
|
return void 0;
|
|
@@ -1922,7 +1922,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1922
1922
|
if (this._scriptPath) {
|
|
1923
1923
|
let resolvedScriptPath;
|
|
1924
1924
|
try {
|
|
1925
|
-
resolvedScriptPath =
|
|
1925
|
+
resolvedScriptPath = fs41.realpathSync(this._scriptPath);
|
|
1926
1926
|
} catch (err) {
|
|
1927
1927
|
resolvedScriptPath = this._scriptPath;
|
|
1928
1928
|
}
|
|
@@ -25064,14 +25064,14 @@ 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.36".length > 0 ? "0.1.36" : "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";
|
|
25071
25071
|
var DEFAULT_FIREHOSE_URL = "https://buildautomaton-firehose.fly.dev";
|
|
25072
25072
|
|
|
25073
25073
|
// src/cli/run-cli-action.ts
|
|
25074
|
-
import * as
|
|
25074
|
+
import * as fs40 from "node:fs";
|
|
25075
25075
|
import * as path42 from "node:path";
|
|
25076
25076
|
|
|
25077
25077
|
// src/cli-log-level.ts
|
|
@@ -26961,6 +26961,7 @@ function recordMigrationAndPruneCheckpointLegacy(db, migration, applied2) {
|
|
|
26961
26961
|
var CHECKPOINT_V1 = "001_cli_sqlite_checkpoint_v1";
|
|
26962
26962
|
var CHECKPOINT_V1_SQL = readCliSqliteMigrationSql("001_cli_sqlite_checkpoint_v1.sql");
|
|
26963
26963
|
var AGENT_CAPABILITIES_SQL = readCliSqliteMigrationSql("002_agent_capabilities.sql");
|
|
26964
|
+
var FILE_INDEX_PARENT_PATHS_SQL = readCliSqliteMigrationSql("003_file_index_parent_paths.sql");
|
|
26964
26965
|
function agentCapabilitiesTableState(db) {
|
|
26965
26966
|
const rows = db.all(
|
|
26966
26967
|
`SELECT name FROM sqlite_master WHERE type='table' AND name IN ('agent_capabilities', 'agent_capability_cache')`
|
|
@@ -26994,6 +26995,18 @@ var CLI_SQLITE_MIGRATIONS = [
|
|
|
26994
26995
|
db.exec(AGENT_CAPABILITIES_SQL);
|
|
26995
26996
|
},
|
|
26996
26997
|
alreadyApplied: (db) => agentCapabilitiesTableState(db) === "current"
|
|
26998
|
+
},
|
|
26999
|
+
{
|
|
27000
|
+
name: "003_file_index_parent_paths",
|
|
27001
|
+
migrate: (db) => {
|
|
27002
|
+
db.exec(FILE_INDEX_PARENT_PATHS_SQL);
|
|
27003
|
+
},
|
|
27004
|
+
alreadyApplied: (db) => {
|
|
27005
|
+
const row = db.get(
|
|
27006
|
+
`SELECT 1 as ok FROM sqlite_master WHERE type='table' AND name='file_index_parent_path' LIMIT 1`
|
|
27007
|
+
);
|
|
27008
|
+
return row != null;
|
|
27009
|
+
}
|
|
26997
27010
|
}
|
|
26998
27011
|
];
|
|
26999
27012
|
function migrateCliSqlite(db) {
|
|
@@ -27032,6 +27045,10 @@ var CliSqliteInterrupted = class extends Error {
|
|
|
27032
27045
|
}
|
|
27033
27046
|
};
|
|
27034
27047
|
function applyCliSqliteConcurrencyPragmas(db) {
|
|
27048
|
+
try {
|
|
27049
|
+
db.run("PRAGMA foreign_keys = ON");
|
|
27050
|
+
} catch {
|
|
27051
|
+
}
|
|
27035
27052
|
try {
|
|
27036
27053
|
db.exec("PRAGMA journal_mode = WAL");
|
|
27037
27054
|
} catch {
|
|
@@ -36743,8 +36760,18 @@ import path29 from "node:path";
|
|
|
36743
36760
|
// src/files/index/walk-workspace-tree.ts
|
|
36744
36761
|
import fs25 from "node:fs";
|
|
36745
36762
|
import path28 from "node:path";
|
|
36763
|
+
var DEPENDENCY_INSTALL_DIR_NAMES = /* @__PURE__ */ new Set([
|
|
36764
|
+
"node_modules",
|
|
36765
|
+
"bower_components",
|
|
36766
|
+
"vendor",
|
|
36767
|
+
"Pods",
|
|
36768
|
+
"Carthage",
|
|
36769
|
+
"DerivedData",
|
|
36770
|
+
".yarn",
|
|
36771
|
+
".pnpm-store"
|
|
36772
|
+
]);
|
|
36746
36773
|
function shouldSkipWorkspaceWalkEntry(name) {
|
|
36747
|
-
return
|
|
36774
|
+
return DEPENDENCY_INSTALL_DIR_NAMES.has(name);
|
|
36748
36775
|
}
|
|
36749
36776
|
async function walkWorkspaceTreeAsync(dir, baseDir, onFile, state) {
|
|
36750
36777
|
let names;
|
|
@@ -36815,14 +36842,26 @@ var FILE_INDEX_INTERRUPT_CHECK_EVERY = 256;
|
|
|
36815
36842
|
function assertNotShutdown() {
|
|
36816
36843
|
if (isCliImmediateShutdownRequested()) throw new CliSqliteInterrupted();
|
|
36817
36844
|
}
|
|
36845
|
+
function upsertFileIndexParentPath(db, resolved) {
|
|
36846
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
36847
|
+
db.run(
|
|
36848
|
+
`INSERT INTO file_index_parent_path (path, path_hash, updated_at)
|
|
36849
|
+
VALUES (?, ?, ?)
|
|
36850
|
+
ON CONFLICT(path) DO UPDATE SET path_hash = excluded.path_hash, updated_at = excluded.updated_at`,
|
|
36851
|
+
[resolved, getCwdHashForFileIndex(resolved), now]
|
|
36852
|
+
);
|
|
36853
|
+
const row = db.get("SELECT id FROM file_index_parent_path WHERE path = ?", [resolved]);
|
|
36854
|
+
if (row == null) throw new Error(`Failed to upsert file index parent path: ${resolved}`);
|
|
36855
|
+
return Number(row.id);
|
|
36856
|
+
}
|
|
36818
36857
|
function persistFileIndexPaths(resolved, paths) {
|
|
36819
36858
|
return withCliSqliteSync((db) => {
|
|
36820
|
-
const h = getCwdHashForFileIndex(resolved);
|
|
36821
36859
|
let pathCount = 0;
|
|
36822
36860
|
db.run("BEGIN IMMEDIATE");
|
|
36823
36861
|
try {
|
|
36824
|
-
|
|
36825
|
-
|
|
36862
|
+
const parentId = upsertFileIndexParentPath(db, resolved);
|
|
36863
|
+
db.run("DELETE FROM file_index_child_path WHERE parent_id = ?", [parentId]);
|
|
36864
|
+
const ins = db.prepare("INSERT INTO file_index_child_path (parent_id, path) VALUES (?, ?)");
|
|
36826
36865
|
try {
|
|
36827
36866
|
let batch = 0;
|
|
36828
36867
|
for (const rel of paths) {
|
|
@@ -36830,7 +36869,7 @@ function persistFileIndexPaths(resolved, paths) {
|
|
|
36830
36869
|
batch = 0;
|
|
36831
36870
|
assertNotShutdown();
|
|
36832
36871
|
}
|
|
36833
|
-
ins.run([
|
|
36872
|
+
ins.run([parentId, rel]);
|
|
36834
36873
|
pathCount += 1;
|
|
36835
36874
|
}
|
|
36836
36875
|
} finally {
|
|
@@ -36872,36 +36911,38 @@ async function buildFileIndexAsync(cwd) {
|
|
|
36872
36911
|
// src/files/index/ensure-file-index.ts
|
|
36873
36912
|
import path30 from "node:path";
|
|
36874
36913
|
|
|
36875
|
-
// src/files/index/file-index-dependency-path.ts
|
|
36876
|
-
function sqliteExprBridgeFileIndexDependencyRank() {
|
|
36877
|
-
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`;
|
|
36878
|
-
}
|
|
36879
|
-
|
|
36880
36914
|
// src/files/index/search-file-index.ts
|
|
36881
36915
|
function escapeLikePattern(fragment) {
|
|
36882
36916
|
return fragment.replace(/\\/g, "\\\\").replace(/%/g, "\\%").replace(/_/g, "\\_");
|
|
36883
36917
|
}
|
|
36884
36918
|
function bridgeFileIndexIsPopulatedWithDb(resolvedCwd, db) {
|
|
36885
|
-
const
|
|
36886
|
-
const row = db.get("SELECT 1 as ok FROM file_index_path WHERE cwd_hash = ? LIMIT 1", [h]);
|
|
36919
|
+
const row = db.get("SELECT 1 as ok FROM file_index_parent_path WHERE path = ? LIMIT 1", [resolvedCwd]);
|
|
36887
36920
|
return row != null;
|
|
36888
36921
|
}
|
|
36889
36922
|
function bridgeFileIndexPathCountWithDb(resolvedCwd, db) {
|
|
36890
|
-
const
|
|
36891
|
-
|
|
36923
|
+
const row = db.get(
|
|
36924
|
+
`SELECT COUNT(*) as c
|
|
36925
|
+
FROM file_index_child_path child
|
|
36926
|
+
JOIN file_index_parent_path parent ON parent.id = child.parent_id
|
|
36927
|
+
WHERE parent.path = ?`,
|
|
36928
|
+
[resolvedCwd]
|
|
36929
|
+
);
|
|
36892
36930
|
const c = row?.c ?? 0;
|
|
36893
36931
|
return Number(c);
|
|
36894
36932
|
}
|
|
36895
36933
|
function searchBridgeFilePathsWithDb(resolvedCwd, query, limit, db) {
|
|
36896
36934
|
const q = query.trim().toLowerCase();
|
|
36897
36935
|
if (!q) return [];
|
|
36898
|
-
const h = getCwdHashForFileIndex(resolvedCwd);
|
|
36899
36936
|
const pattern = `%${escapeLikePattern(q)}%`;
|
|
36900
36937
|
const lim = Math.max(0, Math.min(1e4, Math.floor(limit)));
|
|
36901
|
-
const depRank = sqliteExprBridgeFileIndexDependencyRank();
|
|
36902
36938
|
const rows = db.all(
|
|
36903
|
-
`SELECT path
|
|
36904
|
-
|
|
36939
|
+
`SELECT child.path
|
|
36940
|
+
FROM file_index_child_path child
|
|
36941
|
+
JOIN file_index_parent_path parent ON parent.id = child.parent_id
|
|
36942
|
+
WHERE parent.path = ? AND lower(child.path) LIKE ? ESCAPE '\\'
|
|
36943
|
+
ORDER BY child.path
|
|
36944
|
+
LIMIT ?`,
|
|
36945
|
+
[resolvedCwd, pattern, lim]
|
|
36905
36946
|
);
|
|
36906
36947
|
return rows.map((r) => String(r.path));
|
|
36907
36948
|
}
|
|
@@ -36931,9 +36972,7 @@ async function ensureFileIndexAsync(cwd) {
|
|
|
36931
36972
|
var DEBOUNCE_MS = 900;
|
|
36932
36973
|
function shouldIgnoreRelative(rel) {
|
|
36933
36974
|
const n = rel.replace(/\\/g, "/");
|
|
36934
|
-
|
|
36935
|
-
if (n.includes("/.buildautomaton/") || n.startsWith(".buildautomaton/")) return true;
|
|
36936
|
-
return false;
|
|
36975
|
+
return n.split("/").some((segment) => shouldSkipWorkspaceWalkEntry(segment));
|
|
36937
36976
|
}
|
|
36938
36977
|
function attachWatchErrorLog(w) {
|
|
36939
36978
|
w.on("error", (err) => {
|
|
@@ -39318,9 +39357,8 @@ var handleSkillCallMessage = (msg, { getWs, log: log2 }) => {
|
|
|
39318
39357
|
);
|
|
39319
39358
|
};
|
|
39320
39359
|
|
|
39321
|
-
// src/files/list-dir.ts
|
|
39322
|
-
import
|
|
39323
|
-
import path36 from "node:path";
|
|
39360
|
+
// src/files/list-dir/index.ts
|
|
39361
|
+
import fs34 from "node:fs";
|
|
39324
39362
|
|
|
39325
39363
|
// src/files/ensure-under-cwd.ts
|
|
39326
39364
|
import path35 from "node:path";
|
|
@@ -39333,71 +39371,93 @@ function ensureUnderCwd(relativePath, cwd = getBridgeRoot()) {
|
|
|
39333
39371
|
return resolved;
|
|
39334
39372
|
}
|
|
39335
39373
|
|
|
39336
|
-
// src/files/list-dir.ts
|
|
39374
|
+
// src/files/list-dir/types.ts
|
|
39337
39375
|
var LIST_DIR_YIELD_EVERY = 256;
|
|
39338
|
-
|
|
39339
|
-
|
|
39376
|
+
|
|
39377
|
+
// src/files/list-dir/map-dir-entry.ts
|
|
39378
|
+
import path36 from "node:path";
|
|
39379
|
+
import fs33 from "node:fs";
|
|
39380
|
+
async function mapDirEntry(d, relativePath, resolved) {
|
|
39381
|
+
const entryPath = path36.join(relativePath || ".", d.name).replace(/\\/g, "/");
|
|
39382
|
+
const fullPath = path36.join(resolved, d.name);
|
|
39383
|
+
let isDir = d.isDirectory();
|
|
39384
|
+
if (d.isSymbolicLink()) {
|
|
39385
|
+
try {
|
|
39386
|
+
const targetStat = await fs33.promises.stat(fullPath);
|
|
39387
|
+
isDir = targetStat.isDirectory();
|
|
39388
|
+
} catch {
|
|
39389
|
+
isDir = false;
|
|
39390
|
+
}
|
|
39391
|
+
}
|
|
39392
|
+
return {
|
|
39393
|
+
name: d.name,
|
|
39394
|
+
path: entryPath,
|
|
39395
|
+
isDir,
|
|
39396
|
+
isSymlink: d.isSymbolicLink()
|
|
39397
|
+
};
|
|
39398
|
+
}
|
|
39399
|
+
|
|
39400
|
+
// src/files/list-dir/sort-entries.ts
|
|
39401
|
+
function sortListEntries(entries) {
|
|
39402
|
+
return entries.sort((a, b) => {
|
|
39403
|
+
if (a.isDir !== b.isDir) return a.isDir ? -1 : 1;
|
|
39404
|
+
return a.name.localeCompare(b.name, void 0, { sensitivity: "base" });
|
|
39405
|
+
});
|
|
39406
|
+
}
|
|
39407
|
+
|
|
39408
|
+
// src/files/list-dir/index.ts
|
|
39409
|
+
async function listDirAsync(relativePath, sessionParentPath = getBridgeRoot()) {
|
|
39410
|
+
await yieldToEventLoop();
|
|
39411
|
+
const resolved = ensureUnderCwd(relativePath || ".", sessionParentPath);
|
|
39340
39412
|
if (!resolved) {
|
|
39341
39413
|
return { error: "Path is outside working directory" };
|
|
39342
39414
|
}
|
|
39343
39415
|
try {
|
|
39344
|
-
const names = await
|
|
39345
|
-
const visible = names.filter((d) => !d.name.startsWith("."));
|
|
39416
|
+
const names = await fs34.promises.readdir(resolved, { withFileTypes: true });
|
|
39346
39417
|
const entries = [];
|
|
39347
|
-
for (let i = 0; i <
|
|
39418
|
+
for (let i = 0; i < names.length; i++) {
|
|
39348
39419
|
if (i > 0 && i % LIST_DIR_YIELD_EVERY === 0) {
|
|
39349
39420
|
await yieldToEventLoop();
|
|
39350
39421
|
}
|
|
39351
|
-
|
|
39352
|
-
const entryPath = path36.join(relativePath || ".", d.name).replace(/\\/g, "/");
|
|
39353
|
-
const fullPath = path36.join(resolved, d.name);
|
|
39354
|
-
let isDir = d.isDirectory();
|
|
39355
|
-
if (d.isSymbolicLink()) {
|
|
39356
|
-
try {
|
|
39357
|
-
const targetStat = await fs33.promises.stat(fullPath);
|
|
39358
|
-
isDir = targetStat.isDirectory();
|
|
39359
|
-
} catch {
|
|
39360
|
-
isDir = false;
|
|
39361
|
-
}
|
|
39362
|
-
}
|
|
39363
|
-
entries.push({
|
|
39364
|
-
name: d.name,
|
|
39365
|
-
path: entryPath,
|
|
39366
|
-
isDir,
|
|
39367
|
-
isSymlink: d.isSymbolicLink()
|
|
39368
|
-
});
|
|
39422
|
+
entries.push(await mapDirEntry(names[i], relativePath, resolved));
|
|
39369
39423
|
}
|
|
39370
|
-
entries
|
|
39371
|
-
if (a.isDir !== b.isDir) return a.isDir ? -1 : 1;
|
|
39372
|
-
return a.name.localeCompare(b.name, void 0, { sensitivity: "base" });
|
|
39373
|
-
});
|
|
39374
|
-
return { entries };
|
|
39424
|
+
return { entries: sortListEntries(entries) };
|
|
39375
39425
|
} catch (err) {
|
|
39376
39426
|
const message = err instanceof Error ? err.message : String(err);
|
|
39377
39427
|
return { error: message };
|
|
39378
39428
|
}
|
|
39379
39429
|
}
|
|
39380
39430
|
|
|
39381
|
-
// src/files/read-file.ts
|
|
39382
|
-
|
|
39383
|
-
|
|
39384
|
-
|
|
39385
|
-
|
|
39431
|
+
// src/files/read-file/types.ts
|
|
39432
|
+
var LINE_CHUNK_SIZE = 64 * 1024;
|
|
39433
|
+
var READ_RANGE_YIELD_EVERY_BYTES = 256 * 1024;
|
|
39434
|
+
|
|
39435
|
+
// src/files/read-file/resolve-file-path.ts
|
|
39436
|
+
import fs35 from "node:fs";
|
|
39437
|
+
async function resolveFilePathAsync(relativePath, sessionParentPath = getBridgeRoot()) {
|
|
39438
|
+
const resolved = ensureUnderCwd(relativePath, sessionParentPath);
|
|
39386
39439
|
if (!resolved) return { error: "Path is outside working directory" };
|
|
39387
39440
|
let real;
|
|
39388
39441
|
try {
|
|
39389
|
-
real =
|
|
39442
|
+
real = await fs35.promises.realpath(resolved);
|
|
39390
39443
|
} catch {
|
|
39391
39444
|
real = resolved;
|
|
39392
39445
|
}
|
|
39393
|
-
|
|
39394
|
-
|
|
39395
|
-
|
|
39446
|
+
try {
|
|
39447
|
+
const stat3 = await fs35.promises.stat(real);
|
|
39448
|
+
if (!stat3.isFile()) return { error: "Not a file" };
|
|
39449
|
+
return real;
|
|
39450
|
+
} catch (err) {
|
|
39451
|
+
return { error: err instanceof Error ? err.message : String(err) };
|
|
39452
|
+
}
|
|
39396
39453
|
}
|
|
39397
|
-
|
|
39398
|
-
|
|
39399
|
-
|
|
39400
|
-
|
|
39454
|
+
|
|
39455
|
+
// src/files/read-file/read-file-range-async.ts
|
|
39456
|
+
import fs36 from "node:fs";
|
|
39457
|
+
import { StringDecoder } from "node:string_decoder";
|
|
39458
|
+
async function readFileRangeAsync(filePath, startLine, endLine, lineOffsetIn, lineChunkSize = LINE_CHUNK_SIZE) {
|
|
39459
|
+
const fileSize = (await fs36.promises.stat(filePath)).size;
|
|
39460
|
+
const fd = await fs36.promises.open(filePath, "r");
|
|
39401
39461
|
const bufSize = 64 * 1024;
|
|
39402
39462
|
const buf = Buffer.alloc(bufSize);
|
|
39403
39463
|
const decoder = new StringDecoder("utf8");
|
|
@@ -39408,9 +39468,18 @@ function readFileRange(filePath, startLine, endLine, lineOffsetIn, lineChunkSize
|
|
|
39408
39468
|
let skipLine0Chars = typeof lineOffsetIn === "number" ? lineOffsetIn : 0;
|
|
39409
39469
|
let line0CharsReturned = 0;
|
|
39410
39470
|
let line0Accum = "";
|
|
39471
|
+
let bytesSinceYield = 0;
|
|
39411
39472
|
try {
|
|
39412
|
-
let
|
|
39413
|
-
while (!done
|
|
39473
|
+
let position = 0;
|
|
39474
|
+
while (!done) {
|
|
39475
|
+
const { bytesRead } = await fd.read(buf, 0, bufSize, position);
|
|
39476
|
+
if (bytesRead === 0) break;
|
|
39477
|
+
position += bytesRead;
|
|
39478
|
+
bytesSinceYield += bytesRead;
|
|
39479
|
+
if (bytesSinceYield >= READ_RANGE_YIELD_EVERY_BYTES) {
|
|
39480
|
+
await yieldToEventLoop();
|
|
39481
|
+
bytesSinceYield = 0;
|
|
39482
|
+
}
|
|
39414
39483
|
const text = partial2 + decoder.write(buf.subarray(0, bytesRead));
|
|
39415
39484
|
partial2 = "";
|
|
39416
39485
|
let lineStart = 0;
|
|
@@ -39545,39 +39614,132 @@ function readFileRange(filePath, startLine, endLine, lineOffsetIn, lineChunkSize
|
|
|
39545
39614
|
}
|
|
39546
39615
|
return { content: resultLines.join("\n"), size: fileSize };
|
|
39547
39616
|
} finally {
|
|
39548
|
-
|
|
39617
|
+
await fd.close();
|
|
39549
39618
|
}
|
|
39550
39619
|
}
|
|
39551
|
-
|
|
39620
|
+
|
|
39621
|
+
// src/files/read-file/read-file-buffer-full-async.ts
|
|
39622
|
+
import fs37 from "node:fs";
|
|
39623
|
+
var READ_CHUNK_BYTES = 256 * 1024;
|
|
39624
|
+
async function readFileBufferFullAsync(filePath) {
|
|
39625
|
+
const stat3 = await fs37.promises.stat(filePath);
|
|
39626
|
+
const fd = await fs37.promises.open(filePath, "r");
|
|
39627
|
+
const chunks = [];
|
|
39628
|
+
let position = 0;
|
|
39629
|
+
let bytesSinceYield = 0;
|
|
39552
39630
|
try {
|
|
39553
|
-
|
|
39631
|
+
while (position < stat3.size) {
|
|
39632
|
+
const buf = Buffer.alloc(Math.min(READ_CHUNK_BYTES, stat3.size - position));
|
|
39633
|
+
const { bytesRead } = await fd.read(buf, 0, buf.length, position);
|
|
39634
|
+
if (bytesRead === 0) break;
|
|
39635
|
+
chunks.push(buf.subarray(0, bytesRead));
|
|
39636
|
+
position += bytesRead;
|
|
39637
|
+
bytesSinceYield += bytesRead;
|
|
39638
|
+
if (bytesSinceYield >= READ_RANGE_YIELD_EVERY_BYTES) {
|
|
39639
|
+
await yieldToEventLoop();
|
|
39640
|
+
bytesSinceYield = 0;
|
|
39641
|
+
}
|
|
39642
|
+
}
|
|
39643
|
+
} finally {
|
|
39644
|
+
await fd.close();
|
|
39645
|
+
}
|
|
39646
|
+
return { buffer: Buffer.concat(chunks), size: stat3.size };
|
|
39647
|
+
}
|
|
39648
|
+
|
|
39649
|
+
// src/files/read-file/read-file-full-async.ts
|
|
39650
|
+
async function readFileFullAsync(filePath) {
|
|
39651
|
+
const { buffer, size } = await readFileBufferFullAsync(filePath);
|
|
39652
|
+
const raw = buffer.toString("utf8");
|
|
39653
|
+
const lines = raw.split(/\r?\n/);
|
|
39654
|
+
return { content: raw, totalLines: lines.length, size };
|
|
39655
|
+
}
|
|
39656
|
+
|
|
39657
|
+
// src/files/read-file/guess-mime-type.ts
|
|
39658
|
+
var MIME_BY_EXT = {
|
|
39659
|
+
png: "image/png",
|
|
39660
|
+
jpg: "image/jpeg",
|
|
39661
|
+
jpeg: "image/jpeg",
|
|
39662
|
+
gif: "image/gif",
|
|
39663
|
+
bmp: "image/bmp",
|
|
39664
|
+
ico: "image/x-icon",
|
|
39665
|
+
webp: "image/webp",
|
|
39666
|
+
avif: "image/avif",
|
|
39667
|
+
svg: "image/svg+xml",
|
|
39668
|
+
pdf: "application/pdf",
|
|
39669
|
+
json: "application/json",
|
|
39670
|
+
html: "text/html",
|
|
39671
|
+
htm: "text/html",
|
|
39672
|
+
css: "text/css",
|
|
39673
|
+
js: "text/javascript",
|
|
39674
|
+
mjs: "text/javascript",
|
|
39675
|
+
ts: "text/typescript",
|
|
39676
|
+
txt: "text/plain",
|
|
39677
|
+
md: "text/markdown",
|
|
39678
|
+
xml: "application/xml",
|
|
39679
|
+
zip: "application/zip",
|
|
39680
|
+
gz: "application/gzip",
|
|
39681
|
+
wasm: "application/wasm"
|
|
39682
|
+
};
|
|
39683
|
+
function guessMimeType(filePath) {
|
|
39684
|
+
const ext = filePath.split(".").pop()?.toLowerCase() ?? "";
|
|
39685
|
+
return MIME_BY_EXT[ext] ?? "application/octet-stream";
|
|
39686
|
+
}
|
|
39687
|
+
|
|
39688
|
+
// src/files/read-file/read-file-binary-full-async.ts
|
|
39689
|
+
async function readFileBinaryFullAsync(filePath) {
|
|
39690
|
+
const { buffer, size } = await readFileBufferFullAsync(filePath);
|
|
39691
|
+
return {
|
|
39692
|
+
content: buffer.toString("base64"),
|
|
39693
|
+
size,
|
|
39694
|
+
mimeType: guessMimeType(filePath)
|
|
39695
|
+
};
|
|
39696
|
+
}
|
|
39697
|
+
|
|
39698
|
+
// src/files/read-file/index.ts
|
|
39699
|
+
async function readFileAsync(relativePath, startLine, endLine, lineOffset, lineChunkSize = LINE_CHUNK_SIZE, sessionParentPath = getBridgeRoot(), encoding = "utf8") {
|
|
39700
|
+
await yieldToEventLoop();
|
|
39701
|
+
try {
|
|
39702
|
+
const result = await resolveFilePathAsync(relativePath, sessionParentPath);
|
|
39554
39703
|
if (typeof result === "object") return result;
|
|
39704
|
+
const resolvedPath = result;
|
|
39555
39705
|
const hasRange = typeof startLine === "number" && typeof endLine === "number";
|
|
39706
|
+
if (encoding === "base64") {
|
|
39707
|
+
if (hasRange) return { error: "base64 encoding requires a full file read (no line range)" };
|
|
39708
|
+
const read2 = await readFileBinaryFullAsync(resolvedPath);
|
|
39709
|
+
return { ...read2, resolvedPath };
|
|
39710
|
+
}
|
|
39556
39711
|
if (hasRange) {
|
|
39557
|
-
return
|
|
39712
|
+
return readFileRangeAsync(resolvedPath, startLine, endLine, lineOffset, lineChunkSize);
|
|
39558
39713
|
}
|
|
39559
|
-
const
|
|
39560
|
-
|
|
39561
|
-
const lines = raw.split(/\r?\n/);
|
|
39562
|
-
return { content: raw, totalLines: lines.length, size: stat3.size };
|
|
39714
|
+
const read = await readFileFullAsync(resolvedPath);
|
|
39715
|
+
return { ...read, resolvedPath };
|
|
39563
39716
|
} catch (err) {
|
|
39564
39717
|
return { error: err instanceof Error ? err.message : String(err) };
|
|
39565
39718
|
}
|
|
39566
39719
|
}
|
|
39567
|
-
|
|
39568
|
-
|
|
39569
|
-
|
|
39720
|
+
|
|
39721
|
+
// src/files/resolve-file-browser-session-parent.ts
|
|
39722
|
+
function resolveFileBrowserSessionParent(sessionWorktreeManager, sessionId) {
|
|
39723
|
+
const sid = sessionId?.trim();
|
|
39724
|
+
if (sid) {
|
|
39725
|
+
sessionWorktreeManager.ensureRepoCheckoutPathsForSession(sid);
|
|
39726
|
+
const worktreeRoot = sessionWorktreeManager.getSessionWorktreeRootForSession(sid);
|
|
39727
|
+
if (worktreeRoot) return worktreeRoot;
|
|
39728
|
+
}
|
|
39729
|
+
return getBridgeRoot();
|
|
39570
39730
|
}
|
|
39571
39731
|
|
|
39572
39732
|
// src/files/handle-file-browser-search.ts
|
|
39573
39733
|
import path37 from "node:path";
|
|
39574
39734
|
var SEARCH_LIMIT = 100;
|
|
39575
|
-
function handleFileBrowserSearch(msg, socket, e2ee) {
|
|
39735
|
+
function handleFileBrowserSearch(msg, socket, e2ee, sessionWorktreeManager) {
|
|
39576
39736
|
void (async () => {
|
|
39577
39737
|
await yieldToEventLoop();
|
|
39578
39738
|
const q = typeof msg.q === "string" ? msg.q : "";
|
|
39579
|
-
const
|
|
39580
|
-
|
|
39739
|
+
const sessionParentPath = path37.resolve(
|
|
39740
|
+
sessionWorktreeManager != null ? resolveFileBrowserSessionParent(sessionWorktreeManager, msg.sessionId) : getBridgeRoot()
|
|
39741
|
+
);
|
|
39742
|
+
if (!await bridgeFileIndexIsPopulated(sessionParentPath)) {
|
|
39581
39743
|
const payload2 = {
|
|
39582
39744
|
type: "file_browser_search_response",
|
|
39583
39745
|
id: msg.id,
|
|
@@ -39587,7 +39749,7 @@ function handleFileBrowserSearch(msg, socket, e2ee) {
|
|
|
39587
39749
|
sendWsMessage(socket, e2ee ? e2ee.encryptFields(payload2, ["paths"]) : payload2);
|
|
39588
39750
|
return;
|
|
39589
39751
|
}
|
|
39590
|
-
const results = await searchBridgeFilePathsAsync(
|
|
39752
|
+
const results = await searchBridgeFilePathsAsync(sessionParentPath, q, SEARCH_LIMIT);
|
|
39591
39753
|
const payload = {
|
|
39592
39754
|
type: "file_browser_search_response",
|
|
39593
39755
|
id: msg.id,
|
|
@@ -39597,9 +39759,9 @@ function handleFileBrowserSearch(msg, socket, e2ee) {
|
|
|
39597
39759
|
sendWsMessage(socket, e2ee ? e2ee.encryptFields(payload, ["paths"]) : payload);
|
|
39598
39760
|
})();
|
|
39599
39761
|
}
|
|
39600
|
-
function triggerFileIndexBuild() {
|
|
39762
|
+
function triggerFileIndexBuild(sessionParentPath = getBridgeRoot()) {
|
|
39601
39763
|
setImmediate(() => {
|
|
39602
|
-
void ensureFileIndexAsync(
|
|
39764
|
+
void ensureFileIndexAsync(sessionParentPath).catch((e) => {
|
|
39603
39765
|
console.error("[file-index] Background build failed:", e);
|
|
39604
39766
|
});
|
|
39605
39767
|
});
|
|
@@ -39609,18 +39771,19 @@ function triggerFileIndexBuild() {
|
|
|
39609
39771
|
function sendFileBrowserMessage(socket, e2ee, payload) {
|
|
39610
39772
|
sendWsMessage(socket, e2ee ? e2ee.encryptFields(payload, ["entries", "content", "totalLines", "size", "lineOffset"]) : payload);
|
|
39611
39773
|
}
|
|
39612
|
-
function handleFileBrowserRequest(msg, socket, e2ee) {
|
|
39774
|
+
function handleFileBrowserRequest(msg, socket, e2ee, sessionWorktreeManager) {
|
|
39613
39775
|
void (async () => {
|
|
39614
39776
|
const reqPath = msg.path.replace(/^\/+/, "") || ".";
|
|
39615
39777
|
const op = msg.op === "read" ? "read" : "list";
|
|
39778
|
+
const sessionParentPath = sessionWorktreeManager != null ? resolveFileBrowserSessionParent(sessionWorktreeManager, msg.sessionId) : void 0;
|
|
39616
39779
|
if (op === "list") {
|
|
39617
|
-
const result = await listDirAsync(reqPath);
|
|
39780
|
+
const result = await listDirAsync(reqPath, sessionParentPath);
|
|
39618
39781
|
if ("error" in result) {
|
|
39619
39782
|
sendWsMessage(socket, { type: "file_browser_response", id: msg.id, error: result.error });
|
|
39620
39783
|
} else {
|
|
39621
39784
|
sendFileBrowserMessage(socket, e2ee, { type: "file_browser_response", id: msg.id, entries: result.entries });
|
|
39622
39785
|
if (reqPath === "." || reqPath === "") {
|
|
39623
|
-
triggerFileIndexBuild();
|
|
39786
|
+
triggerFileIndexBuild(sessionParentPath);
|
|
39624
39787
|
}
|
|
39625
39788
|
}
|
|
39626
39789
|
} else {
|
|
@@ -39628,7 +39791,16 @@ function handleFileBrowserRequest(msg, socket, e2ee) {
|
|
|
39628
39791
|
const endLine = typeof msg.endLine === "number" ? msg.endLine : void 0;
|
|
39629
39792
|
const lineOffset = typeof msg.lineOffset === "number" ? msg.lineOffset : void 0;
|
|
39630
39793
|
const lineChunkSize = typeof msg.lineChunkSize === "number" ? msg.lineChunkSize : void 0;
|
|
39631
|
-
const
|
|
39794
|
+
const encoding = msg.encoding === "base64" ? "base64" : "utf8";
|
|
39795
|
+
const result = await readFileAsync(
|
|
39796
|
+
reqPath,
|
|
39797
|
+
startLine,
|
|
39798
|
+
endLine,
|
|
39799
|
+
lineOffset,
|
|
39800
|
+
lineChunkSize,
|
|
39801
|
+
sessionParentPath,
|
|
39802
|
+
encoding
|
|
39803
|
+
);
|
|
39632
39804
|
if ("error" in result) {
|
|
39633
39805
|
sendWsMessage(socket, { type: "file_browser_response", id: msg.id, error: result.error });
|
|
39634
39806
|
} else {
|
|
@@ -39640,6 +39812,8 @@ function handleFileBrowserRequest(msg, socket, e2ee) {
|
|
|
39640
39812
|
size: result.size
|
|
39641
39813
|
};
|
|
39642
39814
|
if (result.lineOffset != null) payload.lineOffset = result.lineOffset;
|
|
39815
|
+
if (result.mimeType != null) payload.mimeType = result.mimeType;
|
|
39816
|
+
if (result.resolvedPath != null) payload.resolvedPath = result.resolvedPath;
|
|
39643
39817
|
sendFileBrowserMessage(socket, e2ee, payload);
|
|
39644
39818
|
}
|
|
39645
39819
|
}
|
|
@@ -39647,21 +39821,27 @@ function handleFileBrowserRequest(msg, socket, e2ee) {
|
|
|
39647
39821
|
}
|
|
39648
39822
|
|
|
39649
39823
|
// src/routing/handlers/file-browser-messages.ts
|
|
39650
|
-
function handleFileBrowserRequestMessage(msg, { getWs, e2ee }) {
|
|
39824
|
+
function handleFileBrowserRequestMessage(msg, { getWs, e2ee, sessionWorktreeManager }) {
|
|
39651
39825
|
if (typeof msg.id !== "string" || typeof msg.path !== "string") return;
|
|
39652
39826
|
const socket = getWs();
|
|
39653
39827
|
if (!socket) return;
|
|
39654
39828
|
handleFileBrowserRequest(
|
|
39655
39829
|
msg,
|
|
39656
39830
|
socket,
|
|
39657
|
-
e2ee
|
|
39831
|
+
e2ee,
|
|
39832
|
+
sessionWorktreeManager
|
|
39658
39833
|
);
|
|
39659
39834
|
}
|
|
39660
|
-
function handleFileBrowserSearchMessage(msg, { getWs, e2ee }) {
|
|
39835
|
+
function handleFileBrowserSearchMessage(msg, { getWs, e2ee, sessionWorktreeManager }) {
|
|
39661
39836
|
if (typeof msg.id !== "string") return;
|
|
39662
39837
|
const socket = getWs();
|
|
39663
39838
|
if (!socket) return;
|
|
39664
|
-
handleFileBrowserSearch(
|
|
39839
|
+
handleFileBrowserSearch(
|
|
39840
|
+
msg,
|
|
39841
|
+
socket,
|
|
39842
|
+
e2ee,
|
|
39843
|
+
sessionWorktreeManager
|
|
39844
|
+
);
|
|
39665
39845
|
}
|
|
39666
39846
|
|
|
39667
39847
|
// src/routing/handlers/skill-layout-request.ts
|
|
@@ -39675,7 +39855,7 @@ function handleSkillLayoutRequest(msg, deps) {
|
|
|
39675
39855
|
}
|
|
39676
39856
|
|
|
39677
39857
|
// src/skills/install-remote-skills.ts
|
|
39678
|
-
import
|
|
39858
|
+
import fs38 from "node:fs";
|
|
39679
39859
|
import path38 from "node:path";
|
|
39680
39860
|
function installRemoteSkills(cwd, targetDir, items) {
|
|
39681
39861
|
const installed2 = [];
|
|
@@ -39691,11 +39871,11 @@ function installRemoteSkills(cwd, targetDir, items) {
|
|
|
39691
39871
|
for (const f of item.files) {
|
|
39692
39872
|
if (typeof f.path !== "string" || !f.text && !f.base64) continue;
|
|
39693
39873
|
const dest = path38.join(skillDir, f.path);
|
|
39694
|
-
|
|
39874
|
+
fs38.mkdirSync(path38.dirname(dest), { recursive: true });
|
|
39695
39875
|
if (f.text !== void 0) {
|
|
39696
|
-
|
|
39876
|
+
fs38.writeFileSync(dest, f.text, "utf8");
|
|
39697
39877
|
} else if (f.base64) {
|
|
39698
|
-
|
|
39878
|
+
fs38.writeFileSync(dest, Buffer.from(f.base64, "base64"));
|
|
39699
39879
|
}
|
|
39700
39880
|
}
|
|
39701
39881
|
installed2.push({
|
|
@@ -39853,7 +40033,7 @@ var handleSessionDiscardedMessage = (msg, deps) => {
|
|
|
39853
40033
|
};
|
|
39854
40034
|
|
|
39855
40035
|
// src/routing/handlers/revert-turn-snapshot.ts
|
|
39856
|
-
import * as
|
|
40036
|
+
import * as fs39 from "node:fs";
|
|
39857
40037
|
var handleRevertTurnSnapshotMessage = (msg, deps) => {
|
|
39858
40038
|
const id = typeof msg.id === "string" ? msg.id : "";
|
|
39859
40039
|
const sessionId = typeof msg.sessionId === "string" ? msg.sessionId : "";
|
|
@@ -39866,7 +40046,7 @@ var handleRevertTurnSnapshotMessage = (msg, deps) => {
|
|
|
39866
40046
|
const agentBase = sessionWorktreeManager.getSessionWorktreeRootForSession(sessionId) ?? getBridgeRoot();
|
|
39867
40047
|
const file2 = snapshotFilePath(agentBase, turnId);
|
|
39868
40048
|
try {
|
|
39869
|
-
await
|
|
40049
|
+
await fs39.promises.access(file2, fs39.constants.F_OK);
|
|
39870
40050
|
} catch {
|
|
39871
40051
|
sendWsMessage(s, {
|
|
39872
40052
|
type: "revert_turn_snapshot_result",
|
|
@@ -40882,7 +41062,7 @@ async function runCliAction(program2, opts) {
|
|
|
40882
41062
|
if (bridgeRootOpt) {
|
|
40883
41063
|
const resolvedBridgeRoot = path42.resolve(process.cwd(), bridgeRootOpt);
|
|
40884
41064
|
try {
|
|
40885
|
-
const st =
|
|
41065
|
+
const st = fs40.statSync(resolvedBridgeRoot);
|
|
40886
41066
|
if (!st.isDirectory()) {
|
|
40887
41067
|
console.error(`Bridge root is not a directory: ${resolvedBridgeRoot}`);
|
|
40888
41068
|
process.exit(1);
|