farai 0.2.7 → 0.2.8
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/README.md +11 -0
- package/dist/cli/index.js +1540 -477
- package/dist/cli/index.js.map +16 -10
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -5147,6 +5147,7 @@ function normalizeConfig(raw) {
|
|
|
5147
5147
|
const context = contextConfig(raw.context);
|
|
5148
5148
|
const lsp = lspConfig(raw.lsp);
|
|
5149
5149
|
const web = webConfig(raw.web);
|
|
5150
|
+
const updates = updatesConfig(raw.updates);
|
|
5150
5151
|
return {
|
|
5151
5152
|
...positiveInteger(raw.config_version ?? raw.configVersion) !== undefined ? {
|
|
5152
5153
|
configVersion: positiveInteger(raw.config_version ?? raw.configVersion)
|
|
@@ -5206,8 +5207,29 @@ function normalizeConfig(raw) {
|
|
|
5206
5207
|
} : {},
|
|
5207
5208
|
...web ? {
|
|
5208
5209
|
web
|
|
5210
|
+
} : {},
|
|
5211
|
+
...updates ? {
|
|
5212
|
+
updates
|
|
5213
|
+
} : {}
|
|
5214
|
+
};
|
|
5215
|
+
}
|
|
5216
|
+
function updatesConfig(value) {
|
|
5217
|
+
if (!isRecord2(value))
|
|
5218
|
+
return;
|
|
5219
|
+
const rawContentEnabled = value.content_enabled ?? value.contentEnabled;
|
|
5220
|
+
const contentManifestUrl = typeof (value.content_manifest_url ?? value.contentManifestUrl) === "string" ? String(value.content_manifest_url ?? value.contentManifestUrl).trim() : undefined;
|
|
5221
|
+
const config = {
|
|
5222
|
+
...typeof rawContentEnabled === "boolean" ? {
|
|
5223
|
+
contentEnabled: rawContentEnabled
|
|
5224
|
+
} : {},
|
|
5225
|
+
...contentManifestUrl ? {
|
|
5226
|
+
contentManifestUrl
|
|
5227
|
+
} : {},
|
|
5228
|
+
...typeof value.prompt === "boolean" ? {
|
|
5229
|
+
prompt: value.prompt
|
|
5209
5230
|
} : {}
|
|
5210
5231
|
};
|
|
5232
|
+
return Object.keys(config).length ? config : undefined;
|
|
5211
5233
|
}
|
|
5212
5234
|
function webConfig(value) {
|
|
5213
5235
|
if (!isRecord2(value))
|
|
@@ -5389,6 +5411,12 @@ function mergeConfig(base, over) {
|
|
|
5389
5411
|
...base.web,
|
|
5390
5412
|
...over.web
|
|
5391
5413
|
}
|
|
5414
|
+
} : {},
|
|
5415
|
+
...base.updates || over.updates ? {
|
|
5416
|
+
updates: {
|
|
5417
|
+
...base.updates,
|
|
5418
|
+
...over.updates
|
|
5419
|
+
}
|
|
5392
5420
|
} : {}
|
|
5393
5421
|
};
|
|
5394
5422
|
}
|
|
@@ -5444,6 +5472,18 @@ function serializeConfigToml(config) {
|
|
|
5444
5472
|
searxng_url: config.web.searxngUrl
|
|
5445
5473
|
} : {}
|
|
5446
5474
|
});
|
|
5475
|
+
if (config.updates && Object.keys(config.updates).length)
|
|
5476
|
+
emitTable(lines, ["updates"], {
|
|
5477
|
+
...config.updates.contentEnabled === undefined ? {} : {
|
|
5478
|
+
content_enabled: config.updates.contentEnabled
|
|
5479
|
+
},
|
|
5480
|
+
...config.updates.contentManifestUrl ? {
|
|
5481
|
+
content_manifest_url: config.updates.contentManifestUrl
|
|
5482
|
+
} : {},
|
|
5483
|
+
...config.updates.prompt === undefined ? {} : {
|
|
5484
|
+
prompt: config.updates.prompt
|
|
5485
|
+
}
|
|
5486
|
+
});
|
|
5447
5487
|
for (const [selection, limit] of Object.entries(config.modelLimits ?? {})) {
|
|
5448
5488
|
emitTable(lines, ["model_limits", selection], {
|
|
5449
5489
|
...limit.contextWindow ? {
|
|
@@ -5606,6 +5646,10 @@ model = "big-pickle"
|
|
|
5606
5646
|
mode = "explicit"
|
|
5607
5647
|
tls = "relaxed"
|
|
5608
5648
|
|
|
5649
|
+
[updates]
|
|
5650
|
+
content_enabled = true
|
|
5651
|
+
prompt = true
|
|
5652
|
+
|
|
5609
5653
|
[mcp_servers.mitmproxy-mcp]
|
|
5610
5654
|
command = "mitmproxy-mcp"
|
|
5611
5655
|
args = []
|
|
@@ -7419,8 +7463,8 @@ var init_process_output = __esm(() => {
|
|
|
7419
7463
|
|
|
7420
7464
|
// src/version.ts
|
|
7421
7465
|
function resolveFaraiVersion() {
|
|
7422
|
-
if ("0.2.
|
|
7423
|
-
return "0.2.
|
|
7466
|
+
if ("0.2.8")
|
|
7467
|
+
return "0.2.8";
|
|
7424
7468
|
try {
|
|
7425
7469
|
const parsed = JSON.parse(readBoundedFileTextSync(new URL("../package.json", import.meta.url), 1024 * 1024, "package metadata"));
|
|
7426
7470
|
if (typeof parsed.version === "string" && parsed.version)
|
|
@@ -17293,11 +17337,112 @@ var init_memory_mark_failed = __esm(() => {
|
|
|
17293
17337
|
};
|
|
17294
17338
|
});
|
|
17295
17339
|
|
|
17340
|
+
// src/agent-content/paths.ts
|
|
17341
|
+
import { existsSync as existsSync8 } from "fs";
|
|
17342
|
+
import { isAbsolute as isAbsolute4, join as join13, resolve as resolve7 } from "path";
|
|
17343
|
+
function contentRoot() {
|
|
17344
|
+
const configured = process.env.FARAI_CONTENT_DIR?.trim();
|
|
17345
|
+
if (!configured)
|
|
17346
|
+
return join13(globalDataDir(), "content");
|
|
17347
|
+
if (!isAbsolute4(configured))
|
|
17348
|
+
throw new Error("farai_content_dir must be an absolute path");
|
|
17349
|
+
return resolve7(configured);
|
|
17350
|
+
}
|
|
17351
|
+
function contentVersionsDir() {
|
|
17352
|
+
return join13(contentRoot(), "versions");
|
|
17353
|
+
}
|
|
17354
|
+
function contentActivePath() {
|
|
17355
|
+
return join13(contentRoot(), "active.json");
|
|
17356
|
+
}
|
|
17357
|
+
function contentManifestCachePath() {
|
|
17358
|
+
return join13(contentRoot(), "manifest-cache.json");
|
|
17359
|
+
}
|
|
17360
|
+
function contentPreferencesPath() {
|
|
17361
|
+
return join13(contentRoot(), "preferences.json");
|
|
17362
|
+
}
|
|
17363
|
+
function contentLockPath() {
|
|
17364
|
+
return join13(contentRoot(), "update.lock");
|
|
17365
|
+
}
|
|
17366
|
+
function contentVersionDir(version) {
|
|
17367
|
+
if (!VERSION_PATTERN.test(version))
|
|
17368
|
+
throw new Error(`invalid content version: ${version}`);
|
|
17369
|
+
return join13(contentVersionsDir(), version);
|
|
17370
|
+
}
|
|
17371
|
+
function readActiveContent() {
|
|
17372
|
+
const path = contentActivePath();
|
|
17373
|
+
try {
|
|
17374
|
+
if (!existsSync8(path))
|
|
17375
|
+
return;
|
|
17376
|
+
ensurePrivateRegularFileIfExists(path, "active content pointer");
|
|
17377
|
+
const parsed = JSON.parse(readBoundedFileTextSyncNoFollow(path, POINTER_MAX_BYTES, "active content pointer"));
|
|
17378
|
+
return parseActiveContent(parsed);
|
|
17379
|
+
} catch {
|
|
17380
|
+
return;
|
|
17381
|
+
}
|
|
17382
|
+
}
|
|
17383
|
+
function activeContentKnowledgePath() {
|
|
17384
|
+
const active = readActiveContent();
|
|
17385
|
+
if (!active?.knowledge)
|
|
17386
|
+
return;
|
|
17387
|
+
const path = join13(contentVersionDir(active.version), "knowledge.db");
|
|
17388
|
+
return existsSync8(path) ? path : undefined;
|
|
17389
|
+
}
|
|
17390
|
+
function activeContentSkillsDir() {
|
|
17391
|
+
const active = readActiveContent();
|
|
17392
|
+
if (!active?.skills)
|
|
17393
|
+
return;
|
|
17394
|
+
const path = join13(contentVersionDir(active.version), "skills");
|
|
17395
|
+
return existsSync8(path) ? path : undefined;
|
|
17396
|
+
}
|
|
17397
|
+
function ensureContentDirectories() {
|
|
17398
|
+
ensurePrivateDirectory(contentRoot(), "farai content directory");
|
|
17399
|
+
ensurePrivateDirectory(contentVersionsDir(), "farai content versions directory");
|
|
17400
|
+
}
|
|
17401
|
+
function parseActiveContent(value) {
|
|
17402
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
17403
|
+
return;
|
|
17404
|
+
const record2 = value;
|
|
17405
|
+
if (record2.schemaVersion !== 1)
|
|
17406
|
+
return;
|
|
17407
|
+
if (typeof record2.version !== "string" || !VERSION_PATTERN.test(record2.version))
|
|
17408
|
+
return;
|
|
17409
|
+
if (!validDate(record2.generatedAt) || !validDate(record2.activatedAt))
|
|
17410
|
+
return;
|
|
17411
|
+
if (typeof record2.manifestUrl !== "string" || !record2.manifestUrl)
|
|
17412
|
+
return;
|
|
17413
|
+
if (typeof record2.knowledge !== "boolean" || typeof record2.skills !== "boolean")
|
|
17414
|
+
return;
|
|
17415
|
+
const previousVersion = typeof record2.previousVersion === "string" && VERSION_PATTERN.test(record2.previousVersion) ? record2.previousVersion : undefined;
|
|
17416
|
+
return {
|
|
17417
|
+
schemaVersion: 1,
|
|
17418
|
+
version: record2.version,
|
|
17419
|
+
generatedAt: record2.generatedAt,
|
|
17420
|
+
activatedAt: record2.activatedAt,
|
|
17421
|
+
manifestUrl: record2.manifestUrl,
|
|
17422
|
+
...previousVersion ? {
|
|
17423
|
+
previousVersion
|
|
17424
|
+
} : {},
|
|
17425
|
+
knowledge: record2.knowledge,
|
|
17426
|
+
skills: record2.skills
|
|
17427
|
+
};
|
|
17428
|
+
}
|
|
17429
|
+
function validDate(value) {
|
|
17430
|
+
return typeof value === "string" && value.length > 0 && Number.isFinite(Date.parse(value));
|
|
17431
|
+
}
|
|
17432
|
+
var POINTER_MAX_BYTES, VERSION_PATTERN;
|
|
17433
|
+
var init_paths2 = __esm(() => {
|
|
17434
|
+
init_paths();
|
|
17435
|
+
init_private_path();
|
|
17436
|
+
init_file_read();
|
|
17437
|
+
POINTER_MAX_BYTES = 64 * 1024;
|
|
17438
|
+
VERSION_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
|
|
17439
|
+
});
|
|
17440
|
+
|
|
17296
17441
|
// src/agent-skills/registry.ts
|
|
17297
17442
|
import { createHash as createHash5 } from "crypto";
|
|
17298
|
-
import { existsSync as
|
|
17443
|
+
import { existsSync as existsSync9, readdirSync as readdirSync3, realpathSync as realpathSync2, statSync as statSync3 } from "fs";
|
|
17299
17444
|
import { homedir as homedir4 } from "os";
|
|
17300
|
-
import { delimiter, isAbsolute as
|
|
17445
|
+
import { delimiter, isAbsolute as isAbsolute5, join as join14, relative as relative4, resolve as resolve8, sep as sep2 } from "path";
|
|
17301
17446
|
function discoverSkills(options = {}) {
|
|
17302
17447
|
const diagnostics = [];
|
|
17303
17448
|
const selected = new Map;
|
|
@@ -17352,7 +17497,7 @@ function loadSkill(name, options = {}) {
|
|
|
17352
17497
|
const resourcePath = normalizeResourcePath(options.resource);
|
|
17353
17498
|
if (!resourcePath || !skill.resources.includes(resourcePath))
|
|
17354
17499
|
return;
|
|
17355
|
-
const absolute =
|
|
17500
|
+
const absolute = resolve8(skill.directory, resourcePath);
|
|
17356
17501
|
const canonical = realpathSync2(absolute);
|
|
17357
17502
|
if (!inside(skill.directory, canonical))
|
|
17358
17503
|
return;
|
|
@@ -17393,28 +17538,35 @@ function skillRoots(options) {
|
|
|
17393
17538
|
source: "builtin",
|
|
17394
17539
|
priority: 0
|
|
17395
17540
|
}];
|
|
17541
|
+
const content = activeContentSkillsDir();
|
|
17542
|
+
if (content)
|
|
17543
|
+
roots.push({
|
|
17544
|
+
path: content,
|
|
17545
|
+
source: "content",
|
|
17546
|
+
priority: 5
|
|
17547
|
+
});
|
|
17396
17548
|
if (options.includeUser !== false)
|
|
17397
17549
|
roots.push({
|
|
17398
|
-
path:
|
|
17550
|
+
path: join14(homedir4(), ".agents", "skills"),
|
|
17399
17551
|
source: "user",
|
|
17400
17552
|
priority: 10
|
|
17401
17553
|
});
|
|
17402
17554
|
const environmentRoots = [...process.env.FARAI_SKILLS_DIR?.split(delimiter) ?? [], ...options.extraRoots ?? []].map((path) => path.trim()).filter(Boolean);
|
|
17403
17555
|
for (const path of environmentRoots)
|
|
17404
17556
|
roots.push({
|
|
17405
|
-
path:
|
|
17557
|
+
path: resolve8(path),
|
|
17406
17558
|
source: "environment",
|
|
17407
17559
|
priority: 20
|
|
17408
17560
|
});
|
|
17409
17561
|
if (options.workspace)
|
|
17410
17562
|
roots.push({
|
|
17411
|
-
path:
|
|
17563
|
+
path: join14(resolve8(options.workspace), ".agents", "skills"),
|
|
17412
17564
|
source: "project",
|
|
17413
17565
|
priority: 30
|
|
17414
17566
|
});
|
|
17415
17567
|
const seen = new Set;
|
|
17416
17568
|
return roots.filter((root) => {
|
|
17417
|
-
const key =
|
|
17569
|
+
const key = resolve8(root.path);
|
|
17418
17570
|
if (seen.has(key))
|
|
17419
17571
|
return false;
|
|
17420
17572
|
seen.add(key);
|
|
@@ -17422,7 +17574,7 @@ function skillRoots(options) {
|
|
|
17422
17574
|
});
|
|
17423
17575
|
}
|
|
17424
17576
|
function scanRoot(root, diagnostics) {
|
|
17425
|
-
if (!
|
|
17577
|
+
if (!existsSync9(root.path))
|
|
17426
17578
|
return [];
|
|
17427
17579
|
let entries;
|
|
17428
17580
|
try {
|
|
@@ -17442,9 +17594,9 @@ function scanRoot(root, diagnostics) {
|
|
|
17442
17594
|
for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
17443
17595
|
if (!entry.isDirectory() && !entry.isSymbolicLink())
|
|
17444
17596
|
continue;
|
|
17445
|
-
const directory =
|
|
17446
|
-
const file =
|
|
17447
|
-
if (!
|
|
17597
|
+
const directory = join14(root.path, entry.name);
|
|
17598
|
+
const file = join14(directory, "SKILL.md");
|
|
17599
|
+
if (!existsSync9(file))
|
|
17448
17600
|
continue;
|
|
17449
17601
|
const parsed = parseSkill(file, directory, entry.name, root, diagnostics);
|
|
17450
17602
|
if (parsed)
|
|
@@ -17453,8 +17605,8 @@ function scanRoot(root, diagnostics) {
|
|
|
17453
17605
|
return skills;
|
|
17454
17606
|
}
|
|
17455
17607
|
function resolveBuiltinSkillDir() {
|
|
17456
|
-
const candidates = [
|
|
17457
|
-
return candidates.find((candidate) =>
|
|
17608
|
+
const candidates = [join14(import.meta.dir, "library"), join14(import.meta.dir, "..", "..", "src", "agent-skills", "library")];
|
|
17609
|
+
return candidates.find((candidate) => existsSync9(candidate)) ?? candidates[0];
|
|
17458
17610
|
}
|
|
17459
17611
|
function parseSkill(file, directory, directoryName, root, diagnostics) {
|
|
17460
17612
|
let raw;
|
|
@@ -17589,7 +17741,7 @@ function listResources(directory, diagnostics) {
|
|
|
17589
17741
|
for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
17590
17742
|
if (resources.length >= MAX_RESOURCES || entry.name.startsWith("."))
|
|
17591
17743
|
continue;
|
|
17592
|
-
const path =
|
|
17744
|
+
const path = join14(current, entry.name);
|
|
17593
17745
|
let canonical;
|
|
17594
17746
|
try {
|
|
17595
17747
|
canonical = realpathSync2(path);
|
|
@@ -17600,7 +17752,7 @@ function listResources(directory, diagnostics) {
|
|
|
17600
17752
|
continue;
|
|
17601
17753
|
if (entry.isDirectory())
|
|
17602
17754
|
visit(canonical);
|
|
17603
|
-
else if ((entry.isFile() || entry.isSymbolicLink()) && canonical !==
|
|
17755
|
+
else if ((entry.isFile() || entry.isSymbolicLink()) && canonical !== join14(directory, "SKILL.md")) {
|
|
17604
17756
|
resources.push(relative4(directory, canonical).split(sep2).join("/"));
|
|
17605
17757
|
}
|
|
17606
17758
|
}
|
|
@@ -17642,7 +17794,7 @@ function optionalScalar(value) {
|
|
|
17642
17794
|
}
|
|
17643
17795
|
function normalizeResourcePath(path) {
|
|
17644
17796
|
const trimmed = path.trim().replaceAll("\\", "/");
|
|
17645
|
-
if (!trimmed ||
|
|
17797
|
+
if (!trimmed || isAbsolute5(trimmed) || trimmed.includes("\x00"))
|
|
17646
17798
|
return;
|
|
17647
17799
|
const normalized = trimmed.split("/").filter((part) => part && part !== ".").join("/");
|
|
17648
17800
|
if (!normalized || normalized.split("/").includes(".."))
|
|
@@ -17651,7 +17803,7 @@ function normalizeResourcePath(path) {
|
|
|
17651
17803
|
}
|
|
17652
17804
|
function inside(directory, path) {
|
|
17653
17805
|
const rel = relative4(realpathSync2(directory), path);
|
|
17654
|
-
return rel === "" || !rel.startsWith(`..${sep2}`) && rel !== ".." && !
|
|
17806
|
+
return rel === "" || !rel.startsWith(`..${sep2}`) && rel !== ".." && !isAbsolute5(rel);
|
|
17655
17807
|
}
|
|
17656
17808
|
function compactText(value, max) {
|
|
17657
17809
|
const compact = value.replace(/\s+/g, " ").trim();
|
|
@@ -17663,6 +17815,7 @@ function errorMessage(error) {
|
|
|
17663
17815
|
var BUILTIN_DIR, MAX_SKILL_BYTES, RECOMMENDED_SKILL_BYTES, MAX_RESOURCE_BYTES, MAX_RESOURCES = 256, NAME_PATTERN;
|
|
17664
17816
|
var init_registry2 = __esm(() => {
|
|
17665
17817
|
init_file_read();
|
|
17818
|
+
init_paths2();
|
|
17666
17819
|
BUILTIN_DIR = resolveBuiltinSkillDir();
|
|
17667
17820
|
MAX_SKILL_BYTES = 64 * 1024;
|
|
17668
17821
|
RECOMMENDED_SKILL_BYTES = 20 * 1024;
|
|
@@ -17798,8 +17951,8 @@ var init_global_config = __esm(() => {
|
|
|
17798
17951
|
|
|
17799
17952
|
// src/agent-core/context-builder.ts
|
|
17800
17953
|
import { execFileSync } from "child_process";
|
|
17801
|
-
import { existsSync as
|
|
17802
|
-
import { isAbsolute as
|
|
17954
|
+
import { existsSync as existsSync10, readdirSync as readdirSync4, statSync as statSync4 } from "fs";
|
|
17955
|
+
import { isAbsolute as isAbsolute6, join as join15, normalize as normalize2, relative as relative5 } from "path";
|
|
17803
17956
|
|
|
17804
17957
|
class ContextBuilderCache {
|
|
17805
17958
|
workspaceFiles = new Map;
|
|
@@ -17831,7 +17984,7 @@ class ContextBuilderCache {
|
|
|
17831
17984
|
loadInstructionFragments(workspace, referencedPaths = []) {
|
|
17832
17985
|
const dirs = instructionCandidateDirs(workspace, referencedPaths);
|
|
17833
17986
|
const key = `${workspace}\x00${dirs.join("\x00")}`;
|
|
17834
|
-
const fingerprint = dirs.flatMap((dir) => INSTRUCTION_FILENAMES.map((filename) => statFingerprint(
|
|
17987
|
+
const fingerprint = dirs.flatMap((dir) => INSTRUCTION_FILENAMES.map((filename) => statFingerprint(join15(dir, filename)))).join("|");
|
|
17835
17988
|
const cached = this.instructions.get(key);
|
|
17836
17989
|
if (cached?.fingerprint === fingerprint)
|
|
17837
17990
|
return cached.fragments;
|
|
@@ -17852,7 +18005,7 @@ class ContextBuilderCache {
|
|
|
17852
18005
|
extractSymbols(workspace, file) {
|
|
17853
18006
|
if (!/\.(?:[cm]?[jt]sx?|py|go|rs)$/i.test(file))
|
|
17854
18007
|
return [];
|
|
17855
|
-
const path =
|
|
18008
|
+
const path = join15(workspace, file);
|
|
17856
18009
|
const fingerprint = statFingerprint(path);
|
|
17857
18010
|
const cached = this.symbols.get(path);
|
|
17858
18011
|
if (cached?.fingerprint === fingerprint)
|
|
@@ -17913,7 +18066,7 @@ function collectWorkspaceFilesFromFs(workspace) {
|
|
|
17913
18066
|
break;
|
|
17914
18067
|
if (entry.name.startsWith(".") || FALLBACK_IGNORED_DIRECTORIES.has(entry.name))
|
|
17915
18068
|
continue;
|
|
17916
|
-
const absolute =
|
|
18069
|
+
const absolute = join15(directory, entry.name);
|
|
17917
18070
|
if (entry.isDirectory())
|
|
17918
18071
|
pending.push(absolute);
|
|
17919
18072
|
else if (entry.isFile())
|
|
@@ -18006,8 +18159,8 @@ function fragment(input) {
|
|
|
18006
18159
|
}
|
|
18007
18160
|
function readFirstInstructionFile(dir) {
|
|
18008
18161
|
for (const filename of INSTRUCTION_FILENAMES) {
|
|
18009
|
-
const path =
|
|
18010
|
-
if (!
|
|
18162
|
+
const path = join15(dir, filename);
|
|
18163
|
+
if (!existsSync10(path))
|
|
18011
18164
|
continue;
|
|
18012
18165
|
try {
|
|
18013
18166
|
const body = readFileTextPrefixSync(path, PROJECT_INSTRUCTIONS_MAX_BYTES, "project instructions").text.trim();
|
|
@@ -18035,7 +18188,7 @@ function referencedInstructionDirs(workspace, referencedPaths) {
|
|
|
18035
18188
|
parts.pop();
|
|
18036
18189
|
let current = workspace;
|
|
18037
18190
|
for (const part of parts) {
|
|
18038
|
-
current =
|
|
18191
|
+
current = join15(current, part);
|
|
18039
18192
|
if (seen.has(current))
|
|
18040
18193
|
continue;
|
|
18041
18194
|
seen.add(current);
|
|
@@ -18048,7 +18201,7 @@ function instructionCandidateDirs(workspace, referencedPaths) {
|
|
|
18048
18201
|
return [...new Set([...globalInstructionDirs(), workspace, ...referencedInstructionDirs(workspace, referencedPaths)])];
|
|
18049
18202
|
}
|
|
18050
18203
|
function workspaceFileFingerprint(workspace) {
|
|
18051
|
-
return `${statFingerprint(workspace)}|${statFingerprint(
|
|
18204
|
+
return `${statFingerprint(workspace)}|${statFingerprint(join15(workspace, ".git", "index"))}`;
|
|
18052
18205
|
}
|
|
18053
18206
|
function statFingerprint(path) {
|
|
18054
18207
|
try {
|
|
@@ -18064,7 +18217,7 @@ function validWorkspaceFiles(workspace, paths) {
|
|
|
18064
18217
|
const rel = workspaceRelativeReference(workspace, value);
|
|
18065
18218
|
if (!rel)
|
|
18066
18219
|
continue;
|
|
18067
|
-
const absolute =
|
|
18220
|
+
const absolute = join15(workspace, rel);
|
|
18068
18221
|
try {
|
|
18069
18222
|
if (statSync4(absolute).isFile())
|
|
18070
18223
|
files.push(rel);
|
|
@@ -18078,9 +18231,9 @@ function workspaceRelativeReference(workspace, value) {
|
|
|
18078
18231
|
return;
|
|
18079
18232
|
const containerPrefix = `${normalize2("/workspace")}/`;
|
|
18080
18233
|
const worktreeMatch = normalized.match(/^[/\\]worktrees[/\\][^/\\]+[/\\](.+)$/);
|
|
18081
|
-
const absolute = normalized.startsWith(containerPrefix) ? normalize2(
|
|
18234
|
+
const absolute = normalized.startsWith(containerPrefix) ? normalize2(join15(workspace, normalized.slice(containerPrefix.length))) : worktreeMatch ? normalize2(join15(workspace, worktreeMatch[1])) : isAbsolute6(normalized) ? normalized : normalize2(join15(workspace, normalized.replace(/^\/+/, "")));
|
|
18082
18235
|
const rel = relative5(workspace, absolute);
|
|
18083
|
-
if (!rel || rel.startsWith("..") ||
|
|
18236
|
+
if (!rel || rel.startsWith("..") || isAbsolute6(rel))
|
|
18084
18237
|
return;
|
|
18085
18238
|
return rel;
|
|
18086
18239
|
}
|
|
@@ -18822,7 +18975,7 @@ class HostProcessBackend {
|
|
|
18822
18975
|
}
|
|
18823
18976
|
async runOnce(command, opts) {
|
|
18824
18977
|
const started = Date.now();
|
|
18825
|
-
return await new Promise((
|
|
18978
|
+
return await new Promise((resolve9) => {
|
|
18826
18979
|
const child = spawn4("bash", ["-lc", command], {
|
|
18827
18980
|
cwd: this.cwd,
|
|
18828
18981
|
stdio: ["pipe", "pipe", "pipe"],
|
|
@@ -18842,7 +18995,7 @@ class HostProcessBackend {
|
|
|
18842
18995
|
const {
|
|
18843
18996
|
sessionId
|
|
18844
18997
|
} = sessions.register(child);
|
|
18845
|
-
|
|
18998
|
+
resolve9({
|
|
18846
18999
|
exitCode: null,
|
|
18847
19000
|
stdout: stdout.text(),
|
|
18848
19001
|
stderr: stderr.text(),
|
|
@@ -18882,7 +19035,7 @@ class HostProcessBackend {
|
|
|
18882
19035
|
return;
|
|
18883
19036
|
settled = true;
|
|
18884
19037
|
stderr.push(error.message);
|
|
18885
|
-
|
|
19038
|
+
resolve9({
|
|
18886
19039
|
exitCode: 127,
|
|
18887
19040
|
stdout: stdout.text(),
|
|
18888
19041
|
stderr: stderr.text(),
|
|
@@ -18897,7 +19050,7 @@ class HostProcessBackend {
|
|
|
18897
19050
|
if (converted || settled)
|
|
18898
19051
|
return;
|
|
18899
19052
|
settled = true;
|
|
18900
|
-
|
|
19053
|
+
resolve9({
|
|
18901
19054
|
exitCode,
|
|
18902
19055
|
stdout: stdout.text(),
|
|
18903
19056
|
stderr: stderr.text(),
|
|
@@ -19868,17 +20021,17 @@ var init_next_action = __esm(() => {
|
|
|
19868
20021
|
});
|
|
19869
20022
|
|
|
19870
20023
|
// src/agent-core/subagents/lanes.ts
|
|
19871
|
-
import { existsSync as
|
|
19872
|
-
import { join as
|
|
20024
|
+
import { existsSync as existsSync11 } from "fs";
|
|
20025
|
+
import { join as join16 } from "path";
|
|
19873
20026
|
function laneConfigPaths(workspace) {
|
|
19874
20027
|
if (false)
|
|
19875
20028
|
;
|
|
19876
|
-
return [
|
|
20029
|
+
return [join16(localFaraiDir(), "agents.json"), join16(workspace, ".farai", "agents.json")];
|
|
19877
20030
|
}
|
|
19878
20031
|
function loadLanes(workspace) {
|
|
19879
20032
|
const merged = new Map(BUILTIN_LANES.map((lane) => [lane.id, structuredClone(lane)]));
|
|
19880
20033
|
for (const path of laneConfigPaths(workspace)) {
|
|
19881
|
-
if (!
|
|
20034
|
+
if (!existsSync11(path))
|
|
19882
20035
|
continue;
|
|
19883
20036
|
try {
|
|
19884
20037
|
const parsed = JSON.parse(readBoundedFileTextSync(path, LANE_CONFIG_MAX_BYTES, "agent lane config"));
|
|
@@ -20593,8 +20746,8 @@ class BrowserContextManager {
|
|
|
20593
20746
|
const config = isolatedBrowserConfig(base, contextId);
|
|
20594
20747
|
let resolveReady = () => {};
|
|
20595
20748
|
let rejectReady = (_error) => {};
|
|
20596
|
-
const ready = new Promise((
|
|
20597
|
-
resolveReady =
|
|
20749
|
+
const ready = new Promise((resolve9, reject) => {
|
|
20750
|
+
resolveReady = resolve9;
|
|
20598
20751
|
rejectReady = reject;
|
|
20599
20752
|
});
|
|
20600
20753
|
ready.catch(() => {});
|
|
@@ -20787,9 +20940,9 @@ class AsyncMutex {
|
|
|
20787
20940
|
this.locked = true;
|
|
20788
20941
|
return Promise.resolve();
|
|
20789
20942
|
}
|
|
20790
|
-
return new Promise((
|
|
20943
|
+
return new Promise((resolve9, reject) => {
|
|
20791
20944
|
const waiter = {
|
|
20792
|
-
resolve:
|
|
20945
|
+
resolve: resolve9,
|
|
20793
20946
|
reject,
|
|
20794
20947
|
...signal ? {
|
|
20795
20948
|
signal
|
|
@@ -20825,7 +20978,7 @@ async function waitForSignal(promise, signal) {
|
|
|
20825
20978
|
return await promise;
|
|
20826
20979
|
if (signal.aborted)
|
|
20827
20980
|
throw signal.reason ?? new Error("Browser context operation cancelled");
|
|
20828
|
-
return await new Promise((
|
|
20981
|
+
return await new Promise((resolve9, reject) => {
|
|
20829
20982
|
const abort = () => {
|
|
20830
20983
|
cleanup();
|
|
20831
20984
|
reject(signal.reason ?? new Error("Browser context operation cancelled"));
|
|
@@ -20836,7 +20989,7 @@ async function waitForSignal(promise, signal) {
|
|
|
20836
20989
|
});
|
|
20837
20990
|
promise.then((value) => {
|
|
20838
20991
|
cleanup();
|
|
20839
|
-
|
|
20992
|
+
resolve9(value);
|
|
20840
20993
|
}, (error) => {
|
|
20841
20994
|
cleanup();
|
|
20842
20995
|
reject(error);
|
|
@@ -22247,14 +22400,14 @@ var init_shared2 = __esm(() => {
|
|
|
22247
22400
|
// src/agent-tools/web/web-fetch.ts
|
|
22248
22401
|
import { mkdirSync as mkdirSync5, unlinkSync as unlinkSync4 } from "fs";
|
|
22249
22402
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
22250
|
-
import { join as
|
|
22403
|
+
import { join as join17 } from "path";
|
|
22251
22404
|
async function extractPdf(context, bytes) {
|
|
22252
|
-
const directory =
|
|
22405
|
+
const directory = join17(context.workspace, ".farai", "tmp");
|
|
22253
22406
|
mkdirSync5(directory, {
|
|
22254
22407
|
recursive: true
|
|
22255
22408
|
});
|
|
22256
22409
|
const name = `web-fetch-${randomUUID3()}.pdf`;
|
|
22257
|
-
const path =
|
|
22410
|
+
const path = join17(directory, name);
|
|
22258
22411
|
atomicWriteFile(path, bytes, 384);
|
|
22259
22412
|
try {
|
|
22260
22413
|
const backendPath = context.executionBackend?.kind === "host" ? path : `/workspace/.farai/tmp/${name}`;
|
|
@@ -23276,10 +23429,10 @@ var init_worktree = __esm(() => {
|
|
|
23276
23429
|
});
|
|
23277
23430
|
|
|
23278
23431
|
// src/agent-tools/services/mitmproxy/flows.ts
|
|
23279
|
-
import { existsSync as
|
|
23432
|
+
import { existsSync as existsSync12 } from "fs";
|
|
23280
23433
|
import { isIP as isIP2 } from "net";
|
|
23281
23434
|
async function readProxyFlows(file, query = {}) {
|
|
23282
|
-
if (!
|
|
23435
|
+
if (!existsSync12(file))
|
|
23283
23436
|
return [];
|
|
23284
23437
|
const limit = proxyFlowLimit(query.limit);
|
|
23285
23438
|
const flows = [];
|
|
@@ -23311,7 +23464,7 @@ async function readProxyFlows(file, query = {}) {
|
|
|
23311
23464
|
return flows;
|
|
23312
23465
|
}
|
|
23313
23466
|
async function readProxyFlowDetail(file, id2) {
|
|
23314
|
-
if (!
|
|
23467
|
+
if (!existsSync12(file))
|
|
23315
23468
|
return;
|
|
23316
23469
|
let found;
|
|
23317
23470
|
await forEachFileLine(file, {
|
|
@@ -24947,7 +25100,7 @@ function detailMatches(detail, options) {
|
|
|
24947
25100
|
return !options.body || detail.text.toLowerCase().includes(options.body.toLowerCase());
|
|
24948
25101
|
}
|
|
24949
25102
|
async function waitForMailboxChange(client, timeoutMs, signal) {
|
|
24950
|
-
await new Promise((
|
|
25103
|
+
await new Promise((resolve9, reject) => {
|
|
24951
25104
|
let settled = false;
|
|
24952
25105
|
const finish = (error) => {
|
|
24953
25106
|
if (settled)
|
|
@@ -24959,7 +25112,7 @@ async function waitForMailboxChange(client, timeoutMs, signal) {
|
|
|
24959
25112
|
if (error)
|
|
24960
25113
|
reject(error);
|
|
24961
25114
|
else
|
|
24962
|
-
|
|
25115
|
+
resolve9();
|
|
24963
25116
|
};
|
|
24964
25117
|
const onAbort = () => finish(signal?.reason ?? new Error("email wait cancelled"));
|
|
24965
25118
|
const onExists = () => finish();
|
|
@@ -24976,8 +25129,8 @@ async function waitForMailboxChange(client, timeoutMs, signal) {
|
|
|
24976
25129
|
async function closeImap(client) {
|
|
24977
25130
|
let timer;
|
|
24978
25131
|
try {
|
|
24979
|
-
const closed = await Promise.race([client.logout().then(() => true, () => false), new Promise((
|
|
24980
|
-
timer = setTimeout(() =>
|
|
25132
|
+
const closed = await Promise.race([client.logout().then(() => true, () => false), new Promise((resolve9) => {
|
|
25133
|
+
timer = setTimeout(() => resolve9(false), IMAP_LOGOUT_TIMEOUT_MS);
|
|
24981
25134
|
timer.unref?.();
|
|
24982
25135
|
})]);
|
|
24983
25136
|
if (!closed)
|
|
@@ -25001,7 +25154,7 @@ async function withAbort(promise, signal) {
|
|
|
25001
25154
|
signal?.throwIfAborted();
|
|
25002
25155
|
if (!signal)
|
|
25003
25156
|
return await promise;
|
|
25004
|
-
return await new Promise((
|
|
25157
|
+
return await new Promise((resolve9, reject) => {
|
|
25005
25158
|
const onAbort = () => {
|
|
25006
25159
|
signal.removeEventListener("abort", onAbort);
|
|
25007
25160
|
reject(signal.reason ?? new Error("operation cancelled"));
|
|
@@ -25011,7 +25164,7 @@ async function withAbort(promise, signal) {
|
|
|
25011
25164
|
});
|
|
25012
25165
|
promise.then((value) => {
|
|
25013
25166
|
signal.removeEventListener("abort", onAbort);
|
|
25014
|
-
|
|
25167
|
+
resolve9(value);
|
|
25015
25168
|
}, (error) => {
|
|
25016
25169
|
signal.removeEventListener("abort", onAbort);
|
|
25017
25170
|
reject(error);
|
|
@@ -25994,7 +26147,7 @@ function toActivity2(entry) {
|
|
|
25994
26147
|
};
|
|
25995
26148
|
}
|
|
25996
26149
|
function delay2(ms, signal) {
|
|
25997
|
-
return new Promise((
|
|
26150
|
+
return new Promise((resolve9, reject) => {
|
|
25998
26151
|
let settled = false;
|
|
25999
26152
|
const finish = (error) => {
|
|
26000
26153
|
if (settled)
|
|
@@ -26005,7 +26158,7 @@ function delay2(ms, signal) {
|
|
|
26005
26158
|
if (error)
|
|
26006
26159
|
reject(error);
|
|
26007
26160
|
else
|
|
26008
|
-
|
|
26161
|
+
resolve9();
|
|
26009
26162
|
};
|
|
26010
26163
|
const abort = () => finish(signal?.reason ?? new Error("email wait cancelled"));
|
|
26011
26164
|
const timer = setTimeout(() => finish(), ms);
|
|
@@ -27541,8 +27694,8 @@ var init_model_profiles = __esm(() => {
|
|
|
27541
27694
|
});
|
|
27542
27695
|
|
|
27543
27696
|
// src/agent-core/model-catalog.ts
|
|
27544
|
-
import { existsSync as
|
|
27545
|
-
import { dirname as dirname7, join as
|
|
27697
|
+
import { existsSync as existsSync13 } from "fs";
|
|
27698
|
+
import { dirname as dirname7, join as join18 } from "path";
|
|
27546
27699
|
async function buildModelCatalog(workspace, profiles = loadModelProfiles(workspace)) {
|
|
27547
27700
|
const providers = [];
|
|
27548
27701
|
const models = [];
|
|
@@ -28118,7 +28271,7 @@ async function fetchModelsDevCatalog(fetchImpl = globalThis.fetch, timeoutMs = M
|
|
|
28118
28271
|
function readCachedModelsDevCatalog() {
|
|
28119
28272
|
try {
|
|
28120
28273
|
const path = modelsDevCachePath();
|
|
28121
|
-
if (!
|
|
28274
|
+
if (!existsSync13(path))
|
|
28122
28275
|
return;
|
|
28123
28276
|
ensurePrivateDirectory(dirname7(path), "model cache directory");
|
|
28124
28277
|
ensurePrivateRegularFileIfExists(path, "models.dev cache");
|
|
@@ -28170,7 +28323,7 @@ function ensureConcrete(resolved) {
|
|
|
28170
28323
|
return resolved;
|
|
28171
28324
|
}
|
|
28172
28325
|
function modelsDevCachePath() {
|
|
28173
|
-
return
|
|
28326
|
+
return join18(globalDataDir(), "cache", "models-dev.json");
|
|
28174
28327
|
}
|
|
28175
28328
|
var DEFAULT_PROVIDER_ID = "default", OPENCODE_PROVIDER_ID, OPENCODE_DEFAULT_MODEL_ID, OPENCODE_PUBLIC_API_KEY, MODELS_DEV_URL = "https://models.dev/api.json", MODELS_DEV_CACHE_TTL_MS, MODELS_DEV_STALE_TTL_MS, MODELS_DEV_FETCH_TIMEOUT_MS = 4000, MODELS_DEV_MAX_BYTES, RECENT_MODEL_LIMIT = 12, modelsDevRefreshes;
|
|
28176
28329
|
var init_model_catalog = __esm(() => {
|
|
@@ -30505,17 +30658,17 @@ var init_compaction = __esm(() => {
|
|
|
30505
30658
|
});
|
|
30506
30659
|
|
|
30507
30660
|
// src/agent-core/hooks/host.ts
|
|
30508
|
-
import { existsSync as
|
|
30509
|
-
import { join as
|
|
30661
|
+
import { existsSync as existsSync14 } from "fs";
|
|
30662
|
+
import { join as join19 } from "path";
|
|
30510
30663
|
function hookConfigPaths(workspace) {
|
|
30511
30664
|
if (false)
|
|
30512
30665
|
;
|
|
30513
|
-
return [
|
|
30666
|
+
return [join19(localFaraiDir(), "hooks.json"), join19(workspace, ".farai", "hooks.json")];
|
|
30514
30667
|
}
|
|
30515
30668
|
function loadHooks(workspace) {
|
|
30516
30669
|
const hooks = [];
|
|
30517
30670
|
for (const path of hookConfigPaths(workspace)) {
|
|
30518
|
-
if (!
|
|
30671
|
+
if (!existsSync14(path))
|
|
30519
30672
|
continue;
|
|
30520
30673
|
try {
|
|
30521
30674
|
const parsed = JSON.parse(readBoundedFileTextSync(path, HOOK_CONFIG_MAX_BYTES, "hook config"));
|
|
@@ -30639,7 +30792,7 @@ class SubagentGate {
|
|
|
30639
30792
|
idle() {
|
|
30640
30793
|
if (this.active === 0 && this.queue.length === 0)
|
|
30641
30794
|
return Promise.resolve();
|
|
30642
|
-
return new Promise((
|
|
30795
|
+
return new Promise((resolve9) => this.idleResolvers.add(resolve9));
|
|
30643
30796
|
}
|
|
30644
30797
|
async run(work, signal) {
|
|
30645
30798
|
const release = await this.acquire(signal);
|
|
@@ -30651,13 +30804,13 @@ class SubagentGate {
|
|
|
30651
30804
|
}
|
|
30652
30805
|
}
|
|
30653
30806
|
acquire(signal) {
|
|
30654
|
-
return new Promise((
|
|
30807
|
+
return new Promise((resolve9, reject) => {
|
|
30655
30808
|
if (signal?.aborted) {
|
|
30656
30809
|
reject(signal.reason ?? new Error("subagent task cancelled before start"));
|
|
30657
30810
|
return;
|
|
30658
30811
|
}
|
|
30659
30812
|
const waiter = {
|
|
30660
|
-
resolve:
|
|
30813
|
+
resolve: resolve9,
|
|
30661
30814
|
reject,
|
|
30662
30815
|
...signal ? {
|
|
30663
30816
|
signal
|
|
@@ -30704,8 +30857,8 @@ class SubagentGate {
|
|
|
30704
30857
|
notifyIdle() {
|
|
30705
30858
|
if (this.active !== 0 || this.queue.length !== 0)
|
|
30706
30859
|
return;
|
|
30707
|
-
for (const
|
|
30708
|
-
|
|
30860
|
+
for (const resolve9 of this.idleResolvers)
|
|
30861
|
+
resolve9();
|
|
30709
30862
|
this.idleResolvers.clear();
|
|
30710
30863
|
}
|
|
30711
30864
|
}
|
|
@@ -30719,10 +30872,10 @@ class SessionActor {
|
|
|
30719
30872
|
run(work) {
|
|
30720
30873
|
if (this.closed)
|
|
30721
30874
|
return Promise.reject(new Error("Session actor is closed"));
|
|
30722
|
-
return new Promise((
|
|
30875
|
+
return new Promise((resolve9, reject) => {
|
|
30723
30876
|
this.queue.push({
|
|
30724
30877
|
work,
|
|
30725
|
-
resolve: (value) =>
|
|
30878
|
+
resolve: (value) => resolve9(value),
|
|
30726
30879
|
reject
|
|
30727
30880
|
});
|
|
30728
30881
|
this.drain();
|
|
@@ -30734,7 +30887,7 @@ class SessionActor {
|
|
|
30734
30887
|
idle() {
|
|
30735
30888
|
if (!this.running && this.queue.length === 0)
|
|
30736
30889
|
return Promise.resolve();
|
|
30737
|
-
return new Promise((
|
|
30890
|
+
return new Promise((resolve9) => this.idleResolvers.add(resolve9));
|
|
30738
30891
|
}
|
|
30739
30892
|
close() {
|
|
30740
30893
|
if (this.closed)
|
|
@@ -30767,8 +30920,8 @@ class SessionActor {
|
|
|
30767
30920
|
resolveIdle() {
|
|
30768
30921
|
if (this.running || this.queue.length > 0)
|
|
30769
30922
|
return;
|
|
30770
|
-
for (const
|
|
30771
|
-
|
|
30923
|
+
for (const resolve9 of this.idleResolvers)
|
|
30924
|
+
resolve9();
|
|
30772
30925
|
this.idleResolvers.clear();
|
|
30773
30926
|
}
|
|
30774
30927
|
}
|
|
@@ -32882,7 +33035,7 @@ function rebuildFtsIndexes(db) {
|
|
|
32882
33035
|
var KNOWLEDGE_SCHEMA_VERSION = 2;
|
|
32883
33036
|
|
|
32884
33037
|
// src/agent-knowledge/store.ts
|
|
32885
|
-
import { existsSync as
|
|
33038
|
+
import { existsSync as existsSync15 } from "fs";
|
|
32886
33039
|
import { Database as Database3 } from "bun:sqlite";
|
|
32887
33040
|
|
|
32888
33041
|
class KnowledgeStore {
|
|
@@ -32891,7 +33044,7 @@ class KnowledgeStore {
|
|
|
32891
33044
|
this.create = create;
|
|
32892
33045
|
}
|
|
32893
33046
|
static openIfExists(path) {
|
|
32894
|
-
if (!
|
|
33047
|
+
if (!existsSync15(path))
|
|
32895
33048
|
return;
|
|
32896
33049
|
const store = new KnowledgeStore(path);
|
|
32897
33050
|
try {
|
|
@@ -33466,24 +33619,28 @@ var init_store = __esm(() => {
|
|
|
33466
33619
|
});
|
|
33467
33620
|
|
|
33468
33621
|
// src/agent-knowledge/paths.ts
|
|
33469
|
-
import { join as
|
|
33622
|
+
import { join as join20 } from "path";
|
|
33470
33623
|
function knowledgeDbPath() {
|
|
33471
|
-
return
|
|
33624
|
+
return activeContentKnowledgePath() ?? legacyKnowledgeDbPath();
|
|
33625
|
+
}
|
|
33626
|
+
function legacyKnowledgeDbPath() {
|
|
33627
|
+
return join20(localFaraiDir(), "knowledge.db");
|
|
33472
33628
|
}
|
|
33473
33629
|
function knowledgeRoot() {
|
|
33474
|
-
return process.env.FARAI_KNOWLEDGE_DIR ??
|
|
33630
|
+
return process.env.FARAI_KNOWLEDGE_DIR ?? join20(localFaraiDir(), "knowledge");
|
|
33475
33631
|
}
|
|
33476
33632
|
function packsDir() {
|
|
33477
|
-
return
|
|
33633
|
+
return join20(knowledgeRoot(), "packs");
|
|
33478
33634
|
}
|
|
33479
33635
|
function taxonomyDir() {
|
|
33480
|
-
return
|
|
33636
|
+
return join20(knowledgeRoot(), "taxonomy");
|
|
33481
33637
|
}
|
|
33482
33638
|
function cacheDir() {
|
|
33483
|
-
return
|
|
33639
|
+
return join20(localFaraiDir(), "knowledge-cache");
|
|
33484
33640
|
}
|
|
33485
|
-
var
|
|
33641
|
+
var init_paths3 = __esm(() => {
|
|
33486
33642
|
init_config();
|
|
33643
|
+
init_paths2();
|
|
33487
33644
|
});
|
|
33488
33645
|
|
|
33489
33646
|
// src/agent-core/model-pricing.ts
|
|
@@ -33517,14 +33674,14 @@ function nonNegative(value) {
|
|
|
33517
33674
|
}
|
|
33518
33675
|
|
|
33519
33676
|
// src/session-catalog.ts
|
|
33520
|
-
import { existsSync as
|
|
33521
|
-
import { join as
|
|
33677
|
+
import { existsSync as existsSync16, readdirSync as readdirSync5, rmSync as rmSync2 } from "fs";
|
|
33678
|
+
import { join as join21, resolve as resolve9 } from "path";
|
|
33522
33679
|
function recordSessionLocation(session) {
|
|
33523
33680
|
const directory = catalogDirectory();
|
|
33524
33681
|
ensurePrivateDirectory(directory, "session catalog directory");
|
|
33525
33682
|
const entry = {
|
|
33526
33683
|
id: session.id,
|
|
33527
|
-
workspace:
|
|
33684
|
+
workspace: resolve9(session.workspace),
|
|
33528
33685
|
...session.title ? {
|
|
33529
33686
|
title: session.title
|
|
33530
33687
|
} : {},
|
|
@@ -33533,13 +33690,13 @@ function recordSessionLocation(session) {
|
|
|
33533
33690
|
} : {},
|
|
33534
33691
|
updatedAt: session.updatedAt
|
|
33535
33692
|
};
|
|
33536
|
-
const path =
|
|
33693
|
+
const path = join21(directory, `${session.id}.json`);
|
|
33537
33694
|
ensurePrivateRegularFileIfExists(path, "session catalog entry");
|
|
33538
33695
|
atomicWriteFile(path, `${JSON.stringify(entry)}
|
|
33539
33696
|
`, 384);
|
|
33540
33697
|
}
|
|
33541
33698
|
function removeSessionLocation(sessionId) {
|
|
33542
|
-
rmSync2(
|
|
33699
|
+
rmSync2(join21(catalogDirectory(), `${sessionId}.json`), {
|
|
33543
33700
|
force: true
|
|
33544
33701
|
});
|
|
33545
33702
|
}
|
|
@@ -33559,7 +33716,7 @@ function resolveSessionLocation(query) {
|
|
|
33559
33716
|
}
|
|
33560
33717
|
function listSessionLocations() {
|
|
33561
33718
|
const directory = catalogDirectory();
|
|
33562
|
-
if (!
|
|
33719
|
+
if (!existsSync16(directory))
|
|
33563
33720
|
return [];
|
|
33564
33721
|
try {
|
|
33565
33722
|
ensurePrivateDirectory(directory, "session catalog directory");
|
|
@@ -33568,7 +33725,7 @@ function listSessionLocations() {
|
|
|
33568
33725
|
}
|
|
33569
33726
|
return readdirSync5(directory).filter((name) => name.endsWith(".json")).flatMap((name) => {
|
|
33570
33727
|
try {
|
|
33571
|
-
const path =
|
|
33728
|
+
const path = join21(directory, name);
|
|
33572
33729
|
ensurePrivateRegularFileIfExists(path, "session catalog entry");
|
|
33573
33730
|
const value = JSON.parse(readBoundedFileTextSyncNoFollow(path, SESSION_CATALOG_ENTRY_MAX_BYTES, "session catalog entry"));
|
|
33574
33731
|
if (typeof value.id !== "string" || typeof value.workspace !== "string" || typeof value.updatedAt !== "string")
|
|
@@ -33590,10 +33747,10 @@ function listSessionLocations() {
|
|
|
33590
33747
|
}).sort((left, right) => right.updatedAt.localeCompare(left.updatedAt));
|
|
33591
33748
|
}
|
|
33592
33749
|
function catalogDirectory() {
|
|
33593
|
-
return
|
|
33750
|
+
return join21(localFaraiDir(), "sessions");
|
|
33594
33751
|
}
|
|
33595
33752
|
function sessionDatabaseExists(workspace) {
|
|
33596
|
-
return
|
|
33753
|
+
return existsSync16(join21(workspace, ".farai", "farai.db"));
|
|
33597
33754
|
}
|
|
33598
33755
|
var SESSION_CATALOG_ENTRY_MAX_BYTES;
|
|
33599
33756
|
var init_session_catalog = __esm(() => {
|
|
@@ -33629,7 +33786,7 @@ class SessionUserInputCoordinator {
|
|
|
33629
33786
|
expiresAt
|
|
33630
33787
|
} : {}
|
|
33631
33788
|
};
|
|
33632
|
-
return new Promise((
|
|
33789
|
+
return new Promise((resolve10, reject) => {
|
|
33633
33790
|
const detach = () => signal?.removeEventListener("abort", abort);
|
|
33634
33791
|
const abort = () => {
|
|
33635
33792
|
if (this.pending.get(sessionId)?.request.id !== request.id)
|
|
@@ -33646,7 +33803,7 @@ class SessionUserInputCoordinator {
|
|
|
33646
33803
|
request,
|
|
33647
33804
|
resolve: (answer) => {
|
|
33648
33805
|
detach();
|
|
33649
|
-
|
|
33806
|
+
resolve10(answer);
|
|
33650
33807
|
},
|
|
33651
33808
|
reject: (error) => {
|
|
33652
33809
|
detach();
|
|
@@ -33979,7 +34136,7 @@ class ToolExecutionGate {
|
|
|
33979
34136
|
idle() {
|
|
33980
34137
|
if (this.states.size === 0)
|
|
33981
34138
|
return Promise.resolve();
|
|
33982
|
-
return new Promise((
|
|
34139
|
+
return new Promise((resolve10) => this.idleResolvers.add(resolve10));
|
|
33983
34140
|
}
|
|
33984
34141
|
async run(key, parallel, fn, signal) {
|
|
33985
34142
|
const release = await this.acquire(key, parallel ? "read" : "write", signal);
|
|
@@ -33991,7 +34148,7 @@ class ToolExecutionGate {
|
|
|
33991
34148
|
}
|
|
33992
34149
|
}
|
|
33993
34150
|
acquire(key, mode, signal) {
|
|
33994
|
-
return new Promise((
|
|
34151
|
+
return new Promise((resolve10, reject) => {
|
|
33995
34152
|
if (signal?.aborted) {
|
|
33996
34153
|
reject(signal.reason ?? new Error("tool gate acquisition cancelled"));
|
|
33997
34154
|
return;
|
|
@@ -34004,7 +34161,7 @@ class ToolExecutionGate {
|
|
|
34004
34161
|
this.states.set(key, state);
|
|
34005
34162
|
const waiter = {
|
|
34006
34163
|
mode,
|
|
34007
|
-
resolve:
|
|
34164
|
+
resolve: resolve10,
|
|
34008
34165
|
reject,
|
|
34009
34166
|
...signal ? {
|
|
34010
34167
|
signal
|
|
@@ -34061,8 +34218,8 @@ class ToolExecutionGate {
|
|
|
34061
34218
|
if (state.activeReaders === 0 && !state.activeWriter && state.queue.length === 0 && this.states.get(key) === state) {
|
|
34062
34219
|
this.states.delete(key);
|
|
34063
34220
|
if (this.states.size === 0) {
|
|
34064
|
-
for (const
|
|
34065
|
-
|
|
34221
|
+
for (const resolve10 of this.idleResolvers)
|
|
34222
|
+
resolve10();
|
|
34066
34223
|
this.idleResolvers.clear();
|
|
34067
34224
|
}
|
|
34068
34225
|
}
|
|
@@ -34109,7 +34266,7 @@ function abortablePromise(promise, signal) {
|
|
|
34109
34266
|
return promise;
|
|
34110
34267
|
if (signal.aborted)
|
|
34111
34268
|
return Promise.reject(abortReason(signal));
|
|
34112
|
-
return new Promise((
|
|
34269
|
+
return new Promise((resolve10, reject) => {
|
|
34113
34270
|
const cleanup = () => signal.removeEventListener("abort", onAbort);
|
|
34114
34271
|
const onAbort = () => {
|
|
34115
34272
|
cleanup();
|
|
@@ -34120,7 +34277,7 @@ function abortablePromise(promise, signal) {
|
|
|
34120
34277
|
});
|
|
34121
34278
|
promise.then((value) => {
|
|
34122
34279
|
cleanup();
|
|
34123
|
-
|
|
34280
|
+
resolve10(value);
|
|
34124
34281
|
}, (error) => {
|
|
34125
34282
|
cleanup();
|
|
34126
34283
|
reject(error);
|
|
@@ -34505,8 +34662,8 @@ var init_tool_call_journal = () => {};
|
|
|
34505
34662
|
|
|
34506
34663
|
// src/agent-core/runtime.ts
|
|
34507
34664
|
import { createHash as createHash9 } from "crypto";
|
|
34508
|
-
import { existsSync as
|
|
34509
|
-
import { isAbsolute as
|
|
34665
|
+
import { existsSync as existsSync17, mkdirSync as mkdirSync6, realpathSync as realpathSync3 } from "fs";
|
|
34666
|
+
import { isAbsolute as isAbsolute7, join as join22, relative as relative7 } from "path";
|
|
34510
34667
|
function assertProviderToolIndex2(index, max) {
|
|
34511
34668
|
if (!Number.isInteger(index) || index < 0 || index >= max)
|
|
34512
34669
|
throw new Error(`provider tool call index must be between 0 and ${max - 1}`);
|
|
@@ -34552,7 +34709,7 @@ class AgentRuntime {
|
|
|
34552
34709
|
this.workspace = workspace;
|
|
34553
34710
|
this.inheritConfig = options.inheritConfig !== false;
|
|
34554
34711
|
const config = this.inheritConfig ? loadConfig(workspace) : {};
|
|
34555
|
-
this.store = new SqliteStore(
|
|
34712
|
+
this.store = new SqliteStore(join22(workspace, ".farai"));
|
|
34556
34713
|
this.toolCalls = new ToolCallJournal(this.store, (sessionId, type, payload) => this.event(sessionId, type, payload));
|
|
34557
34714
|
this.knowledgeEnabled = options.enableKnowledge !== false;
|
|
34558
34715
|
this.hooksEnabled = options.enableHooks !== false;
|
|
@@ -35374,16 +35531,16 @@ class AgentRuntime {
|
|
|
35374
35531
|
}
|
|
35375
35532
|
waitForSteering(sessionId) {
|
|
35376
35533
|
let settled = false;
|
|
35377
|
-
let
|
|
35534
|
+
let resolve10;
|
|
35378
35535
|
const promise = new Promise((done) => {
|
|
35379
|
-
|
|
35536
|
+
resolve10 = done;
|
|
35380
35537
|
});
|
|
35381
35538
|
const wake = () => {
|
|
35382
35539
|
if (settled)
|
|
35383
35540
|
return;
|
|
35384
35541
|
settled = true;
|
|
35385
35542
|
this.steeringWaiters.get(sessionId)?.delete(wake);
|
|
35386
|
-
|
|
35543
|
+
resolve10();
|
|
35387
35544
|
};
|
|
35388
35545
|
const waiters = this.steeringWaiters.get(sessionId) ?? new Set;
|
|
35389
35546
|
waiters.add(wake);
|
|
@@ -35731,12 +35888,12 @@ class AgentRuntime {
|
|
|
35731
35888
|
return {
|
|
35732
35889
|
markdown
|
|
35733
35890
|
};
|
|
35734
|
-
const dir =
|
|
35891
|
+
const dir = join22(this.workspace, ".farai", "reports");
|
|
35735
35892
|
mkdirSync6(dir, {
|
|
35736
35893
|
recursive: true
|
|
35737
35894
|
});
|
|
35738
35895
|
const stamp = new Date().toISOString().slice(0, 10);
|
|
35739
|
-
const path =
|
|
35896
|
+
const path = join22(dir, `${sessionId}-${stamp}.md`);
|
|
35740
35897
|
atomicWriteFile(path, markdown, 384);
|
|
35741
35898
|
return {
|
|
35742
35899
|
markdown,
|
|
@@ -37836,7 +37993,7 @@ This completion is already terminal and was delivered automatically. Do not call
|
|
|
37836
37993
|
}
|
|
37837
37994
|
if (entries.length === 0 || entries.some((entry) => !entry.running) || Date.now() - started >= timeoutMs)
|
|
37838
37995
|
return entries;
|
|
37839
|
-
await new Promise((
|
|
37996
|
+
await new Promise((resolve10) => setTimeout(resolve10, 200));
|
|
37840
37997
|
}
|
|
37841
37998
|
},
|
|
37842
37999
|
message: (childSessionId, text2) => {
|
|
@@ -37896,15 +38053,15 @@ This completion is already terminal and was delivered automatically. Do not call
|
|
|
37896
38053
|
const root = (await runHostGit(this.workspace, ["rev-parse", "--show-toplevel"])).trim();
|
|
37897
38054
|
if (realpathSync3(root) !== realpathSync3(this.workspace))
|
|
37898
38055
|
throw new Error(`Farai workspace is not the Git repository root: ${root}`);
|
|
37899
|
-
const worktreesRoot =
|
|
38056
|
+
const worktreesRoot = join22(this.workspace, ".farai", "worktrees");
|
|
37900
38057
|
mkdirSync6(worktreesRoot, {
|
|
37901
38058
|
recursive: true
|
|
37902
38059
|
});
|
|
37903
|
-
const path =
|
|
38060
|
+
const path = join22(worktreesRoot, safeName);
|
|
37904
38061
|
const registered = await registeredWorktree(this.workspace, path);
|
|
37905
|
-
if (
|
|
38062
|
+
if (existsSync17(path) && !registered)
|
|
37906
38063
|
throw new Error(`worktree path already exists but is not a registered Git worktree: ${path}`);
|
|
37907
|
-
if (!
|
|
38064
|
+
if (!existsSync17(path) && registered)
|
|
37908
38065
|
throw new Error(`worktree registration exists but its directory is missing: ${path}; repair or prune it before re-entry`);
|
|
37909
38066
|
if (registered) {
|
|
37910
38067
|
if (ref || branch)
|
|
@@ -37954,9 +38111,9 @@ This completion is already terminal and was delivered automatically. Do not call
|
|
|
37954
38111
|
if (current.workspace === this.workspace)
|
|
37955
38112
|
throw new Error("session is not inside an isolated worktree");
|
|
37956
38113
|
this.assertWorkspaceTransitionIdle(session.id);
|
|
37957
|
-
const worktreesRoot =
|
|
38114
|
+
const worktreesRoot = join22(this.workspace, ".farai", "worktrees");
|
|
37958
38115
|
const managedPath = relative7(worktreesRoot, current.workspace);
|
|
37959
|
-
if (!managedPath || managedPath.startsWith("..") ||
|
|
38116
|
+
if (!managedPath || managedPath.startsWith("..") || isAbsolute7(managedPath)) {
|
|
37960
38117
|
throw new Error(`refusing to leave an unmanaged worktree: ${current.workspace}`);
|
|
37961
38118
|
}
|
|
37962
38119
|
if (remove) {
|
|
@@ -38838,16 +38995,16 @@ function plannerRetryState(error, attempt, safeToReplay) {
|
|
|
38838
38995
|
function abortableSleep(ms, signal) {
|
|
38839
38996
|
if (signal?.aborted)
|
|
38840
38997
|
return Promise.resolve();
|
|
38841
|
-
return new Promise((
|
|
38998
|
+
return new Promise((resolve10) => {
|
|
38842
38999
|
const cleanup = () => signal?.removeEventListener("abort", onAbort);
|
|
38843
39000
|
const onAbort = () => {
|
|
38844
39001
|
clearTimeout(timer);
|
|
38845
39002
|
cleanup();
|
|
38846
|
-
|
|
39003
|
+
resolve10();
|
|
38847
39004
|
};
|
|
38848
39005
|
const timer = setTimeout(() => {
|
|
38849
39006
|
cleanup();
|
|
38850
|
-
|
|
39007
|
+
resolve10();
|
|
38851
39008
|
}, ms);
|
|
38852
39009
|
signal?.addEventListener("abort", onAbort, {
|
|
38853
39010
|
once: true
|
|
@@ -38897,18 +39054,18 @@ function shutdownGracePeriod(value) {
|
|
|
38897
39054
|
return Math.max(0, value);
|
|
38898
39055
|
}
|
|
38899
39056
|
function waitForShutdownFinalization(finalization, gracePeriodMs) {
|
|
38900
|
-
return new Promise((
|
|
39057
|
+
return new Promise((resolve10, reject) => {
|
|
38901
39058
|
let settled = false;
|
|
38902
39059
|
const timer = setTimeout(() => {
|
|
38903
39060
|
settled = true;
|
|
38904
|
-
|
|
39061
|
+
resolve10(false);
|
|
38905
39062
|
}, gracePeriodMs);
|
|
38906
39063
|
finalization.then(() => {
|
|
38907
39064
|
if (settled)
|
|
38908
39065
|
return;
|
|
38909
39066
|
settled = true;
|
|
38910
39067
|
clearTimeout(timer);
|
|
38911
|
-
|
|
39068
|
+
resolve10(true);
|
|
38912
39069
|
}, (error) => {
|
|
38913
39070
|
if (settled)
|
|
38914
39071
|
return;
|
|
@@ -39035,7 +39192,7 @@ var init_runtime = __esm(() => {
|
|
|
39035
39192
|
init_retry();
|
|
39036
39193
|
init_reasoning_summary();
|
|
39037
39194
|
init_store();
|
|
39038
|
-
|
|
39195
|
+
init_paths3();
|
|
39039
39196
|
init_session_catalog();
|
|
39040
39197
|
init_session_user_input();
|
|
39041
39198
|
init_tool_execution_control();
|
|
@@ -39086,37 +39243,796 @@ var init_branding = __esm(() => {
|
|
|
39086
39243
|
`);
|
|
39087
39244
|
});
|
|
39088
39245
|
|
|
39089
|
-
// src/agent-
|
|
39090
|
-
|
|
39246
|
+
// src/agent-core/semver.ts
|
|
39247
|
+
function compareSemver(left, right) {
|
|
39248
|
+
const a = parseSemver(left);
|
|
39249
|
+
const b = parseSemver(right);
|
|
39250
|
+
if (!a || !b)
|
|
39251
|
+
return 0;
|
|
39252
|
+
for (let index = 0;index < 3; index += 1) {
|
|
39253
|
+
const delta = a.core[index] - b.core[index];
|
|
39254
|
+
if (delta !== 0)
|
|
39255
|
+
return delta < 0 ? -1 : 1;
|
|
39256
|
+
}
|
|
39257
|
+
if (a.prerelease.length === 0 || b.prerelease.length === 0) {
|
|
39258
|
+
if (a.prerelease.length === b.prerelease.length)
|
|
39259
|
+
return 0;
|
|
39260
|
+
return a.prerelease.length === 0 ? 1 : -1;
|
|
39261
|
+
}
|
|
39262
|
+
const length = Math.max(a.prerelease.length, b.prerelease.length);
|
|
39263
|
+
for (let index = 0;index < length; index += 1) {
|
|
39264
|
+
const aPart = a.prerelease[index];
|
|
39265
|
+
const bPart = b.prerelease[index];
|
|
39266
|
+
if (aPart === undefined || bPart === undefined)
|
|
39267
|
+
return aPart === undefined ? -1 : 1;
|
|
39268
|
+
if (aPart === bPart)
|
|
39269
|
+
continue;
|
|
39270
|
+
const aNumber = numericIdentifier(aPart);
|
|
39271
|
+
const bNumber = numericIdentifier(bPart);
|
|
39272
|
+
if (aNumber !== undefined && bNumber !== undefined)
|
|
39273
|
+
return aNumber < bNumber ? -1 : 1;
|
|
39274
|
+
if (aNumber !== undefined || bNumber !== undefined)
|
|
39275
|
+
return aNumber !== undefined ? -1 : 1;
|
|
39276
|
+
return aPart < bPart ? -1 : 1;
|
|
39277
|
+
}
|
|
39278
|
+
return 0;
|
|
39279
|
+
}
|
|
39280
|
+
function isSemver(value) {
|
|
39281
|
+
return parseSemver(value) !== undefined;
|
|
39282
|
+
}
|
|
39283
|
+
function parseSemver(value) {
|
|
39284
|
+
const match = value.trim().match(/^(?:v)?(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/);
|
|
39285
|
+
if (!match)
|
|
39286
|
+
return;
|
|
39287
|
+
return {
|
|
39288
|
+
core: [Number(match[1]), Number(match[2]), Number(match[3])],
|
|
39289
|
+
prerelease: match[4]?.split(".") ?? []
|
|
39290
|
+
};
|
|
39291
|
+
}
|
|
39292
|
+
function numericIdentifier(value) {
|
|
39293
|
+
if (!/^(0|[1-9]\d*)$/.test(value))
|
|
39294
|
+
return;
|
|
39295
|
+
return Number(value);
|
|
39296
|
+
}
|
|
39297
|
+
|
|
39298
|
+
// src/agent-content/manifest.ts
|
|
39299
|
+
async function fetchContentManifest(manifestUrl, options = {}) {
|
|
39300
|
+
const resolved = new URL(manifestUrl);
|
|
39301
|
+
let parsed;
|
|
39302
|
+
if (resolved.protocol === "file:") {
|
|
39303
|
+
parsed = JSON.parse(await readBoundedFileText(resolved, CONTENT_MANIFEST_MAX_BYTES, "content manifest"));
|
|
39304
|
+
} else {
|
|
39305
|
+
if (resolved.protocol !== "https:")
|
|
39306
|
+
throw new Error("content manifest must use https or file");
|
|
39307
|
+
const controller = new AbortController;
|
|
39308
|
+
const timer = setTimeout(() => controller.abort(), Math.max(1, options.timeoutMs ?? 5000));
|
|
39309
|
+
timer.unref?.();
|
|
39310
|
+
try {
|
|
39311
|
+
const response = await (options.fetcher ?? fetch)(resolved, {
|
|
39312
|
+
headers: {
|
|
39313
|
+
accept: "application/json"
|
|
39314
|
+
},
|
|
39315
|
+
signal: controller.signal
|
|
39316
|
+
});
|
|
39317
|
+
if (!response.ok) {
|
|
39318
|
+
await discardResponseBody(response);
|
|
39319
|
+
throw new Error(`content manifest returned ${response.status}`);
|
|
39320
|
+
}
|
|
39321
|
+
parsed = await readBoundedResponseJson(response, CONTENT_MANIFEST_MAX_BYTES, "content manifest");
|
|
39322
|
+
} finally {
|
|
39323
|
+
clearTimeout(timer);
|
|
39324
|
+
}
|
|
39325
|
+
}
|
|
39326
|
+
return parseContentManifest(parsed, resolved);
|
|
39327
|
+
}
|
|
39328
|
+
function parseContentManifest(value, baseUrl) {
|
|
39329
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
39330
|
+
throw new Error("content manifest must be an object");
|
|
39331
|
+
const record3 = value;
|
|
39332
|
+
if (record3.schemaVersion !== CONTENT_MANIFEST_SCHEMA_VERSION)
|
|
39333
|
+
throw new Error(`unsupported content manifest schema: ${String(record3.schemaVersion)}`);
|
|
39334
|
+
if (typeof record3.contentVersion !== "string" || !CONTENT_VERSION_PATTERN.test(record3.contentVersion))
|
|
39335
|
+
throw new Error("invalid content version");
|
|
39336
|
+
if (typeof record3.generatedAt !== "string" || !Number.isFinite(Date.parse(record3.generatedAt)))
|
|
39337
|
+
throw new Error("invalid content generatedAt");
|
|
39338
|
+
const minFaraiVersion = optionalString2(record3.minFaraiVersion, 128, "minFaraiVersion");
|
|
39339
|
+
if (minFaraiVersion && !isSemver(minFaraiVersion))
|
|
39340
|
+
throw new Error("invalid content minFaraiVersion");
|
|
39341
|
+
const releaseNotes = optionalString2(record3.releaseNotes, 4096, "releaseNotes");
|
|
39342
|
+
const knowledge = artifact(record3.knowledge, baseUrl, "knowledge");
|
|
39343
|
+
const skills = artifact(record3.skills, baseUrl, "skills");
|
|
39344
|
+
return {
|
|
39345
|
+
schemaVersion: 1,
|
|
39346
|
+
contentVersion: record3.contentVersion,
|
|
39347
|
+
generatedAt: new Date(record3.generatedAt).toISOString(),
|
|
39348
|
+
...minFaraiVersion ? {
|
|
39349
|
+
minFaraiVersion
|
|
39350
|
+
} : {},
|
|
39351
|
+
...releaseNotes ? {
|
|
39352
|
+
releaseNotes
|
|
39353
|
+
} : {},
|
|
39354
|
+
...knowledge ? {
|
|
39355
|
+
knowledge
|
|
39356
|
+
} : {},
|
|
39357
|
+
...skills ? {
|
|
39358
|
+
skills
|
|
39359
|
+
} : {}
|
|
39360
|
+
};
|
|
39361
|
+
}
|
|
39362
|
+
function artifact(value, baseUrl, label) {
|
|
39363
|
+
if (value === undefined)
|
|
39364
|
+
return;
|
|
39365
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
39366
|
+
throw new Error(`${label} artifact must be an object`);
|
|
39367
|
+
const record3 = value;
|
|
39368
|
+
if (typeof record3.url !== "string" || !record3.url || record3.url.length > 2048)
|
|
39369
|
+
throw new Error(`${label} artifact has an invalid url`);
|
|
39370
|
+
const url = new URL(record3.url, baseUrl);
|
|
39371
|
+
if (baseUrl.protocol === "https:" && url.protocol !== "https:")
|
|
39372
|
+
throw new Error(`${label} artifact must use https when the manifest is remote`);
|
|
39373
|
+
if (url.protocol !== "https:" && url.protocol !== "file:")
|
|
39374
|
+
throw new Error(`${label} artifact must use https or file`);
|
|
39375
|
+
if (typeof record3.sha256 !== "string" || !SHA256_PATTERN.test(record3.sha256))
|
|
39376
|
+
throw new Error(`${label} artifact has an invalid sha256`);
|
|
39377
|
+
if (!Number.isSafeInteger(record3.size) || Number(record3.size) < 1 || Number(record3.size) > 2147483648)
|
|
39378
|
+
throw new Error(`${label} artifact has an invalid size`);
|
|
39379
|
+
const schemaVersion = record3.schemaVersion === undefined ? undefined : Number.isSafeInteger(record3.schemaVersion) && Number(record3.schemaVersion) > 0 ? Number(record3.schemaVersion) : undefined;
|
|
39380
|
+
if (record3.schemaVersion !== undefined && schemaVersion === undefined)
|
|
39381
|
+
throw new Error(`${label} artifact has an invalid schemaVersion`);
|
|
39382
|
+
return {
|
|
39383
|
+
url: url.href,
|
|
39384
|
+
sha256: record3.sha256,
|
|
39385
|
+
size: Number(record3.size),
|
|
39386
|
+
...schemaVersion ? {
|
|
39387
|
+
schemaVersion
|
|
39388
|
+
} : {}
|
|
39389
|
+
};
|
|
39390
|
+
}
|
|
39391
|
+
function optionalString2(value, maxLength, label) {
|
|
39392
|
+
if (value === undefined)
|
|
39393
|
+
return;
|
|
39394
|
+
if (typeof value !== "string" || !value.trim() || value.length > maxLength)
|
|
39395
|
+
throw new Error(`invalid content ${label}`);
|
|
39396
|
+
return value.trim();
|
|
39397
|
+
}
|
|
39398
|
+
var CONTENT_MANIFEST_SCHEMA_VERSION = 1, CONTENT_MANIFEST_MAX_BYTES, DEFAULT_CONTENT_MANIFEST_URL = "https://github.com/pajarori/farai-data/releases/latest/download/manifest.json", CONTENT_VERSION_PATTERN, SHA256_PATTERN;
|
|
39399
|
+
var init_manifest = __esm(() => {
|
|
39400
|
+
init_http_response();
|
|
39401
|
+
init_file_read();
|
|
39402
|
+
CONTENT_MANIFEST_MAX_BYTES = 256 * 1024;
|
|
39403
|
+
CONTENT_VERSION_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
|
|
39404
|
+
SHA256_PATTERN = /^[a-f0-9]{64}$/;
|
|
39405
|
+
});
|
|
39406
|
+
|
|
39407
|
+
// src/agent-content/updater.ts
|
|
39408
|
+
var exports_updater = {};
|
|
39409
|
+
__export(exports_updater, {
|
|
39410
|
+
rollbackContentUpdate: () => rollbackContentUpdate,
|
|
39411
|
+
isContentVersionDismissed: () => isContentVersionDismissed,
|
|
39412
|
+
dismissContentVersion: () => dismissContentVersion,
|
|
39413
|
+
contentStatus: () => contentStatus,
|
|
39414
|
+
checkContentUpdate: () => checkContentUpdate,
|
|
39415
|
+
applyContentUpdate: () => applyContentUpdate,
|
|
39416
|
+
CONTENT_UPDATE_TIMEOUT_MS: () => CONTENT_UPDATE_TIMEOUT_MS,
|
|
39417
|
+
CONTENT_MANIFEST_CACHE_TTL_MS: () => CONTENT_MANIFEST_CACHE_TTL_MS
|
|
39418
|
+
});
|
|
39091
39419
|
import { createHash as createHash10, randomUUID as randomUUID4 } from "crypto";
|
|
39092
|
-
import {
|
|
39420
|
+
import { closeSync as closeSync4, existsSync as existsSync18, lstatSync as lstatSync4, mkdirSync as mkdirSync7, openSync as openSync4, readSync as readSync2, readdirSync as readdirSync6, renameSync as renameSync3, rmSync as rmSync3, statSync as statSync6, unlinkSync as unlinkSync6, writeSync } from "fs";
|
|
39421
|
+
import { dirname as dirname9, join as join23 } from "path";
|
|
39422
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
39423
|
+
async function checkContentUpdate(options = {}) {
|
|
39424
|
+
const config = loadConfig(options.workspace);
|
|
39425
|
+
const manifestUrl = options.manifestUrl ?? process.env.FARAI_CONTENT_MANIFEST_URL ?? config.updates?.contentManifestUrl ?? DEFAULT_CONTENT_MANIFEST_URL;
|
|
39426
|
+
if (contentUpdateDisabled(config.updates?.contentEnabled))
|
|
39427
|
+
return {
|
|
39428
|
+
state: "disabled",
|
|
39429
|
+
manifestUrl,
|
|
39430
|
+
fromCache: false
|
|
39431
|
+
};
|
|
39432
|
+
const now = options.now ?? Date.now();
|
|
39433
|
+
const cached = readManifestCache();
|
|
39434
|
+
const cacheMatches = cached?.manifestUrl === manifestUrl;
|
|
39435
|
+
let manifest;
|
|
39436
|
+
let fromCache = false;
|
|
39437
|
+
if (!options.force && cacheMatches && cached && isFresh(cached.checkedAt, now)) {
|
|
39438
|
+
if (!cached.manifest)
|
|
39439
|
+
return {
|
|
39440
|
+
state: "error",
|
|
39441
|
+
manifestUrl,
|
|
39442
|
+
fromCache: true,
|
|
39443
|
+
...cached.error ? {
|
|
39444
|
+
error: cached.error
|
|
39445
|
+
} : {}
|
|
39446
|
+
};
|
|
39447
|
+
manifest = cached.manifest;
|
|
39448
|
+
fromCache = true;
|
|
39449
|
+
} else {
|
|
39450
|
+
try {
|
|
39451
|
+
manifest = await fetchContentManifest(manifestUrl, {
|
|
39452
|
+
...options.fetcher ? {
|
|
39453
|
+
fetcher: options.fetcher
|
|
39454
|
+
} : {},
|
|
39455
|
+
timeoutMs: options.timeoutMs ?? CONTENT_UPDATE_TIMEOUT_MS
|
|
39456
|
+
});
|
|
39457
|
+
writeManifestCache({
|
|
39458
|
+
checkedAt: now,
|
|
39459
|
+
manifestUrl,
|
|
39460
|
+
manifest
|
|
39461
|
+
});
|
|
39462
|
+
} catch (error) {
|
|
39463
|
+
const message = errorMessage6(error);
|
|
39464
|
+
if (cacheMatches && cached?.manifest) {
|
|
39465
|
+
manifest = cached.manifest;
|
|
39466
|
+
fromCache = true;
|
|
39467
|
+
writeManifestCache({
|
|
39468
|
+
checkedAt: now,
|
|
39469
|
+
manifestUrl,
|
|
39470
|
+
manifest,
|
|
39471
|
+
error: message
|
|
39472
|
+
});
|
|
39473
|
+
} else {
|
|
39474
|
+
writeManifestCache({
|
|
39475
|
+
checkedAt: now,
|
|
39476
|
+
manifestUrl,
|
|
39477
|
+
error: message
|
|
39478
|
+
});
|
|
39479
|
+
return {
|
|
39480
|
+
state: "error",
|
|
39481
|
+
manifestUrl,
|
|
39482
|
+
fromCache: false,
|
|
39483
|
+
error: message
|
|
39484
|
+
};
|
|
39485
|
+
}
|
|
39486
|
+
}
|
|
39487
|
+
}
|
|
39488
|
+
const active = readActiveContent();
|
|
39489
|
+
if (!manifest || !manifest.knowledge && !manifest.skills)
|
|
39490
|
+
return {
|
|
39491
|
+
state: "unavailable",
|
|
39492
|
+
manifestUrl,
|
|
39493
|
+
fromCache,
|
|
39494
|
+
...active ? {
|
|
39495
|
+
active
|
|
39496
|
+
} : {},
|
|
39497
|
+
...manifest ? {
|
|
39498
|
+
manifest
|
|
39499
|
+
} : {}
|
|
39500
|
+
};
|
|
39501
|
+
if (manifest.minFaraiVersion && isSemver(manifest.minFaraiVersion) && compareSemver(FARAI_VERSION, manifest.minFaraiVersion) < 0) {
|
|
39502
|
+
return {
|
|
39503
|
+
state: "incompatible",
|
|
39504
|
+
manifestUrl,
|
|
39505
|
+
fromCache,
|
|
39506
|
+
manifest,
|
|
39507
|
+
...active ? {
|
|
39508
|
+
active
|
|
39509
|
+
} : {}
|
|
39510
|
+
};
|
|
39511
|
+
}
|
|
39512
|
+
if (!isNewerManifest(manifest, active))
|
|
39513
|
+
return {
|
|
39514
|
+
state: "up_to_date",
|
|
39515
|
+
manifestUrl,
|
|
39516
|
+
fromCache,
|
|
39517
|
+
manifest,
|
|
39518
|
+
...active ? {
|
|
39519
|
+
active
|
|
39520
|
+
} : {}
|
|
39521
|
+
};
|
|
39522
|
+
return {
|
|
39523
|
+
state: "update_available",
|
|
39524
|
+
manifestUrl,
|
|
39525
|
+
fromCache,
|
|
39526
|
+
manifest,
|
|
39527
|
+
...active ? {
|
|
39528
|
+
active
|
|
39529
|
+
} : {}
|
|
39530
|
+
};
|
|
39531
|
+
}
|
|
39532
|
+
async function applyContentUpdate(manifest, manifestUrl, options = {}) {
|
|
39533
|
+
if (!manifest.knowledge && !manifest.skills)
|
|
39534
|
+
throw new Error("content manifest has no artifacts");
|
|
39535
|
+
ensureContentDirectories();
|
|
39536
|
+
const release = acquireLock();
|
|
39537
|
+
const stage = join23(contentVersionsDir(), `.staging-${process.pid}-${randomUUID4()}`);
|
|
39538
|
+
const finalPath = contentVersionDir(manifest.contentVersion);
|
|
39539
|
+
try {
|
|
39540
|
+
mkdirSync7(stage, {
|
|
39541
|
+
recursive: true,
|
|
39542
|
+
mode: 448
|
|
39543
|
+
});
|
|
39544
|
+
ensurePrivateDirectory(stage, "content staging directory");
|
|
39545
|
+
let knowledge = false;
|
|
39546
|
+
let skills = false;
|
|
39547
|
+
if (manifest.knowledge) {
|
|
39548
|
+
const path = join23(stage, "knowledge.db");
|
|
39549
|
+
await downloadArtifact(manifest.knowledge, path, options);
|
|
39550
|
+
validateKnowledge(path, manifest.knowledge.schemaVersion);
|
|
39551
|
+
knowledge = true;
|
|
39552
|
+
}
|
|
39553
|
+
if (manifest.skills) {
|
|
39554
|
+
if (manifest.skills.size > CONTENT_SKILLS_MAX_BYTES)
|
|
39555
|
+
throw new Error("skills artifact is too large");
|
|
39556
|
+
const archive = join23(stage, "skills.tar.gz");
|
|
39557
|
+
await downloadArtifact(manifest.skills, archive, options);
|
|
39558
|
+
await extractSkills(archive, join23(stage, "skills"));
|
|
39559
|
+
rmSync3(archive, {
|
|
39560
|
+
force: true
|
|
39561
|
+
});
|
|
39562
|
+
skills = true;
|
|
39563
|
+
}
|
|
39564
|
+
atomicWriteFile(join23(stage, "manifest.json"), `${JSON.stringify(manifest, null, 2)}
|
|
39565
|
+
`, 384);
|
|
39566
|
+
ensurePrivateRegularFileIfExists(join23(stage, "manifest.json"), "staged content manifest");
|
|
39567
|
+
if (existsSync18(finalPath)) {
|
|
39568
|
+
const existing = join23(finalPath, "manifest.json");
|
|
39569
|
+
if (existsSync18(existing)) {
|
|
39570
|
+
const current = parseStoredManifest(existing);
|
|
39571
|
+
if (JSON.stringify(current) !== JSON.stringify(manifest))
|
|
39572
|
+
throw new Error(`content version already exists with different metadata: ${manifest.contentVersion}`);
|
|
39573
|
+
rmSync3(stage, {
|
|
39574
|
+
recursive: true,
|
|
39575
|
+
force: true
|
|
39576
|
+
});
|
|
39577
|
+
} else {
|
|
39578
|
+
rmSync3(finalPath, {
|
|
39579
|
+
recursive: true,
|
|
39580
|
+
force: true
|
|
39581
|
+
});
|
|
39582
|
+
renameSync3(stage, finalPath);
|
|
39583
|
+
}
|
|
39584
|
+
} else {
|
|
39585
|
+
renameSync3(stage, finalPath);
|
|
39586
|
+
}
|
|
39587
|
+
ensurePrivateDirectory(finalPath, "content version directory");
|
|
39588
|
+
const previous = readActiveContent();
|
|
39589
|
+
const pointer = {
|
|
39590
|
+
schemaVersion: 1,
|
|
39591
|
+
version: manifest.contentVersion,
|
|
39592
|
+
generatedAt: manifest.generatedAt,
|
|
39593
|
+
activatedAt: new Date().toISOString(),
|
|
39594
|
+
manifestUrl,
|
|
39595
|
+
...previous && previous.version !== manifest.contentVersion ? {
|
|
39596
|
+
previousVersion: previous.version
|
|
39597
|
+
} : {},
|
|
39598
|
+
knowledge,
|
|
39599
|
+
skills
|
|
39600
|
+
};
|
|
39601
|
+
atomicWriteFile(contentActivePath(), `${JSON.stringify(pointer, null, 2)}
|
|
39602
|
+
`, 384);
|
|
39603
|
+
syncDirectory(contentVersionsDir());
|
|
39604
|
+
pruneVersions(pointer);
|
|
39605
|
+
return {
|
|
39606
|
+
version: pointer.version,
|
|
39607
|
+
...pointer.previousVersion ? {
|
|
39608
|
+
previousVersion: pointer.previousVersion
|
|
39609
|
+
} : {},
|
|
39610
|
+
knowledge,
|
|
39611
|
+
skills,
|
|
39612
|
+
path: finalPath
|
|
39613
|
+
};
|
|
39614
|
+
} finally {
|
|
39615
|
+
if (existsSync18(stage))
|
|
39616
|
+
rmSync3(stage, {
|
|
39617
|
+
recursive: true,
|
|
39618
|
+
force: true
|
|
39619
|
+
});
|
|
39620
|
+
release();
|
|
39621
|
+
}
|
|
39622
|
+
}
|
|
39623
|
+
function rollbackContentUpdate() {
|
|
39624
|
+
ensureContentDirectories();
|
|
39625
|
+
const release = acquireLock();
|
|
39626
|
+
try {
|
|
39627
|
+
const active = readActiveContent();
|
|
39628
|
+
if (!active?.previousVersion)
|
|
39629
|
+
throw new Error("no previous content version is available");
|
|
39630
|
+
const previousPath = contentVersionDir(active.previousVersion);
|
|
39631
|
+
if (!existsSync18(previousPath))
|
|
39632
|
+
throw new Error(`previous content version is missing: ${active.previousVersion}`);
|
|
39633
|
+
const manifest = parseStoredManifest(join23(previousPath, "manifest.json"));
|
|
39634
|
+
const next = {
|
|
39635
|
+
schemaVersion: 1,
|
|
39636
|
+
version: active.previousVersion,
|
|
39637
|
+
generatedAt: manifest.generatedAt,
|
|
39638
|
+
activatedAt: new Date().toISOString(),
|
|
39639
|
+
manifestUrl: active.manifestUrl,
|
|
39640
|
+
previousVersion: active.version,
|
|
39641
|
+
knowledge: Boolean(manifest.knowledge && existsSync18(join23(previousPath, "knowledge.db"))),
|
|
39642
|
+
skills: Boolean(manifest.skills && existsSync18(join23(previousPath, "skills")))
|
|
39643
|
+
};
|
|
39644
|
+
atomicWriteFile(contentActivePath(), `${JSON.stringify(next, null, 2)}
|
|
39645
|
+
`, 384);
|
|
39646
|
+
return {
|
|
39647
|
+
version: next.version,
|
|
39648
|
+
previousVersion: next.previousVersion,
|
|
39649
|
+
knowledge: next.knowledge,
|
|
39650
|
+
skills: next.skills,
|
|
39651
|
+
path: previousPath
|
|
39652
|
+
};
|
|
39653
|
+
} finally {
|
|
39654
|
+
release();
|
|
39655
|
+
}
|
|
39656
|
+
}
|
|
39657
|
+
function contentStatus() {
|
|
39658
|
+
ensureContentDirectories();
|
|
39659
|
+
const active = readActiveContent();
|
|
39660
|
+
const versions = readdirVersions();
|
|
39661
|
+
const knowledgePath = activeContentKnowledgePath();
|
|
39662
|
+
const skillsPath = activeContentSkillsDir();
|
|
39663
|
+
return {
|
|
39664
|
+
...active ? {
|
|
39665
|
+
active
|
|
39666
|
+
} : {},
|
|
39667
|
+
...knowledgePath ? {
|
|
39668
|
+
knowledgePath
|
|
39669
|
+
} : {},
|
|
39670
|
+
...skillsPath ? {
|
|
39671
|
+
skillsPath
|
|
39672
|
+
} : {},
|
|
39673
|
+
versions
|
|
39674
|
+
};
|
|
39675
|
+
}
|
|
39676
|
+
function isContentVersionDismissed(version) {
|
|
39677
|
+
try {
|
|
39678
|
+
const value = JSON.parse(readBoundedFileTextSyncNoFollow(contentPreferencesPath(), CONTENT_PREFERENCES_MAX_BYTES, "content preferences"));
|
|
39679
|
+
return value.dismissedVersion === version;
|
|
39680
|
+
} catch {
|
|
39681
|
+
return false;
|
|
39682
|
+
}
|
|
39683
|
+
}
|
|
39684
|
+
function dismissContentVersion(version) {
|
|
39685
|
+
ensureContentDirectories();
|
|
39686
|
+
atomicWriteFile(contentPreferencesPath(), `${JSON.stringify({
|
|
39687
|
+
dismissedVersion: version
|
|
39688
|
+
})}
|
|
39689
|
+
`, 384);
|
|
39690
|
+
}
|
|
39691
|
+
function contentUpdateDisabled(configured) {
|
|
39692
|
+
if (configured === false)
|
|
39693
|
+
return true;
|
|
39694
|
+
return [process.env.FARAI_DISABLE_CONTENT_UPDATE, process.env.FARAI_DISABLE_UPDATE_CHECK].some((value) => value === "1" || value?.toLowerCase() === "true" || value?.toLowerCase() === "yes");
|
|
39695
|
+
}
|
|
39696
|
+
function isNewerManifest(manifest, active) {
|
|
39697
|
+
if (!active)
|
|
39698
|
+
return true;
|
|
39699
|
+
if (manifest.contentVersion === active.version)
|
|
39700
|
+
return false;
|
|
39701
|
+
const generated = Date.parse(manifest.generatedAt);
|
|
39702
|
+
const activeGenerated = Date.parse(active.generatedAt);
|
|
39703
|
+
if (Number.isFinite(generated) && Number.isFinite(activeGenerated))
|
|
39704
|
+
return generated > activeGenerated;
|
|
39705
|
+
return manifest.contentVersion > active.version;
|
|
39706
|
+
}
|
|
39707
|
+
function readManifestCache() {
|
|
39708
|
+
try {
|
|
39709
|
+
ensureContentDirectories();
|
|
39710
|
+
const parsed = JSON.parse(readBoundedFileTextSyncNoFollow(contentManifestCachePath(), CONTENT_MANIFEST_MAX_BYTES * 2, "content manifest cache"));
|
|
39711
|
+
if (typeof parsed.checkedAt !== "number" || !Number.isFinite(parsed.checkedAt) || typeof parsed.manifestUrl !== "string" || !parsed.manifestUrl)
|
|
39712
|
+
return;
|
|
39713
|
+
const manifest = parsed.manifest ? parseContentManifest(parsed.manifest, new URL(parsed.manifestUrl)) : undefined;
|
|
39714
|
+
const error = typeof parsed.error === "string" && parsed.error ? parsed.error : undefined;
|
|
39715
|
+
if (!manifest && !error)
|
|
39716
|
+
return;
|
|
39717
|
+
return {
|
|
39718
|
+
checkedAt: parsed.checkedAt,
|
|
39719
|
+
manifestUrl: parsed.manifestUrl,
|
|
39720
|
+
...manifest ? {
|
|
39721
|
+
manifest
|
|
39722
|
+
} : {},
|
|
39723
|
+
...error ? {
|
|
39724
|
+
error
|
|
39725
|
+
} : {}
|
|
39726
|
+
};
|
|
39727
|
+
} catch {
|
|
39728
|
+
return;
|
|
39729
|
+
}
|
|
39730
|
+
}
|
|
39731
|
+
function writeManifestCache(cache) {
|
|
39732
|
+
try {
|
|
39733
|
+
ensureContentDirectories();
|
|
39734
|
+
atomicWriteFile(contentManifestCachePath(), `${JSON.stringify(cache)}
|
|
39735
|
+
`, 384);
|
|
39736
|
+
} catch {}
|
|
39737
|
+
}
|
|
39738
|
+
function isFresh(checkedAt, now) {
|
|
39739
|
+
const age = now - checkedAt;
|
|
39740
|
+
return age >= 0 && age < CONTENT_MANIFEST_CACHE_TTL_MS;
|
|
39741
|
+
}
|
|
39742
|
+
function acquireLock() {
|
|
39743
|
+
const path = contentLockPath();
|
|
39744
|
+
ensureContentDirectories();
|
|
39745
|
+
try {
|
|
39746
|
+
const descriptor = openSync4(path, "wx", 384);
|
|
39747
|
+
try {
|
|
39748
|
+
writeSync(descriptor, JSON.stringify({
|
|
39749
|
+
pid: process.pid,
|
|
39750
|
+
createdAt: Date.now()
|
|
39751
|
+
}));
|
|
39752
|
+
} finally {
|
|
39753
|
+
closeSync4(descriptor);
|
|
39754
|
+
}
|
|
39755
|
+
cleanupStagingDirectories();
|
|
39756
|
+
return () => {
|
|
39757
|
+
try {
|
|
39758
|
+
unlinkSync6(path);
|
|
39759
|
+
} catch {}
|
|
39760
|
+
};
|
|
39761
|
+
} catch (error) {
|
|
39762
|
+
if (!isAlreadyExists(error) || !staleLock(path))
|
|
39763
|
+
throw new Error("another farai content update is already running");
|
|
39764
|
+
try {
|
|
39765
|
+
unlinkSync6(path);
|
|
39766
|
+
} catch {
|
|
39767
|
+
throw new Error("another farai content update is already running");
|
|
39768
|
+
}
|
|
39769
|
+
return acquireLock();
|
|
39770
|
+
}
|
|
39771
|
+
}
|
|
39772
|
+
function staleLock(path) {
|
|
39773
|
+
try {
|
|
39774
|
+
const stats = statSync6(path);
|
|
39775
|
+
const parsed = JSON.parse(readBoundedFileTextSyncNoFollow(path, 4 * 1024, "content update lock"));
|
|
39776
|
+
if (typeof parsed.pid === "number" && parsed.pid > 0) {
|
|
39777
|
+
try {
|
|
39778
|
+
process.kill(parsed.pid, 0);
|
|
39779
|
+
return false;
|
|
39780
|
+
} catch (error) {
|
|
39781
|
+
if (!isNoSuchProcess(error))
|
|
39782
|
+
return false;
|
|
39783
|
+
}
|
|
39784
|
+
}
|
|
39785
|
+
return Date.now() - stats.mtimeMs >= CONTENT_LOCK_STALE_MS || typeof parsed.pid === "number";
|
|
39786
|
+
} catch {
|
|
39787
|
+
return true;
|
|
39788
|
+
}
|
|
39789
|
+
}
|
|
39790
|
+
function cleanupStagingDirectories() {
|
|
39791
|
+
for (const entry of readdirSync6(contentVersionsDir())) {
|
|
39792
|
+
if (!entry.startsWith(".staging-"))
|
|
39793
|
+
continue;
|
|
39794
|
+
try {
|
|
39795
|
+
rmSync3(join23(contentVersionsDir(), entry), {
|
|
39796
|
+
recursive: true,
|
|
39797
|
+
force: true
|
|
39798
|
+
});
|
|
39799
|
+
} catch {}
|
|
39800
|
+
}
|
|
39801
|
+
}
|
|
39802
|
+
async function downloadArtifact(artifact2, destination, options) {
|
|
39803
|
+
if (artifact2.size > CONTENT_ARTIFACT_MAX_BYTES)
|
|
39804
|
+
throw new Error("content artifact is too large");
|
|
39805
|
+
const url = new URL(artifact2.url);
|
|
39806
|
+
mkdirSync7(dirname9(destination), {
|
|
39807
|
+
recursive: true,
|
|
39808
|
+
mode: 448
|
|
39809
|
+
});
|
|
39810
|
+
let bytes;
|
|
39811
|
+
if (url.protocol === "file:") {
|
|
39812
|
+
const source = Bun.file(url);
|
|
39813
|
+
const sourcePath = fileURLToPath2(url);
|
|
39814
|
+
const stats = lstatSync4(sourcePath);
|
|
39815
|
+
if (!stats.isFile())
|
|
39816
|
+
throw new Error("content artifact file must be a regular file");
|
|
39817
|
+
if (stats.size !== artifact2.size)
|
|
39818
|
+
throw new Error(`content artifact size mismatch: expected ${artifact2.size}, received ${stats.size}`);
|
|
39819
|
+
await Bun.write(destination, source);
|
|
39820
|
+
bytes = stats.size;
|
|
39821
|
+
} else {
|
|
39822
|
+
if (url.protocol !== "https:")
|
|
39823
|
+
throw new Error("content artifact must use https or file");
|
|
39824
|
+
const controller = new AbortController;
|
|
39825
|
+
const timer = setTimeout(() => controller.abort(), Math.max(1, options.timeoutMs ?? CONTENT_UPDATE_TIMEOUT_MS));
|
|
39826
|
+
timer.unref?.();
|
|
39827
|
+
let descriptor;
|
|
39828
|
+
try {
|
|
39829
|
+
const response = await (options.fetcher ?? fetch)(url, {
|
|
39830
|
+
signal: controller.signal
|
|
39831
|
+
});
|
|
39832
|
+
if (!response.ok || !response.body) {
|
|
39833
|
+
try {
|
|
39834
|
+
await response.body?.cancel();
|
|
39835
|
+
} catch {}
|
|
39836
|
+
throw new Error(`content artifact returned ${response.status}`);
|
|
39837
|
+
}
|
|
39838
|
+
const declared = Number(response.headers.get("content-length"));
|
|
39839
|
+
if (Number.isSafeInteger(declared) && declared !== artifact2.size)
|
|
39840
|
+
throw new Error(`content artifact size mismatch: expected ${artifact2.size}, received ${declared}`);
|
|
39841
|
+
descriptor = openSync4(destination, "wx", 384);
|
|
39842
|
+
const reader = response.body.getReader();
|
|
39843
|
+
bytes = 0;
|
|
39844
|
+
let completed = false;
|
|
39845
|
+
try {
|
|
39846
|
+
for (;; ) {
|
|
39847
|
+
const next = await reader.read();
|
|
39848
|
+
if (next.done) {
|
|
39849
|
+
completed = true;
|
|
39850
|
+
break;
|
|
39851
|
+
}
|
|
39852
|
+
bytes += next.value.byteLength;
|
|
39853
|
+
if (bytes > artifact2.size)
|
|
39854
|
+
throw new Error("content artifact exceeded declared size");
|
|
39855
|
+
writeSync(descriptor, next.value);
|
|
39856
|
+
}
|
|
39857
|
+
} finally {
|
|
39858
|
+
if (!completed)
|
|
39859
|
+
reader.cancel().catch(() => {
|
|
39860
|
+
return;
|
|
39861
|
+
});
|
|
39862
|
+
reader.releaseLock();
|
|
39863
|
+
}
|
|
39864
|
+
closeSync4(descriptor);
|
|
39865
|
+
descriptor = undefined;
|
|
39866
|
+
} finally {
|
|
39867
|
+
clearTimeout(timer);
|
|
39868
|
+
if (descriptor !== undefined)
|
|
39869
|
+
try {
|
|
39870
|
+
closeSync4(descriptor);
|
|
39871
|
+
} catch {}
|
|
39872
|
+
}
|
|
39873
|
+
}
|
|
39874
|
+
if (bytes !== artifact2.size)
|
|
39875
|
+
throw new Error(`content artifact size mismatch: expected ${artifact2.size}, received ${bytes}`);
|
|
39876
|
+
const digest2 = hashFile(destination);
|
|
39877
|
+
if (digest2 !== artifact2.sha256)
|
|
39878
|
+
throw new Error(`content artifact checksum mismatch for ${url.href}`);
|
|
39879
|
+
ensurePrivateRegularFileIfExists(destination, "downloaded content artifact");
|
|
39880
|
+
}
|
|
39881
|
+
function validateKnowledge(path, schemaVersion) {
|
|
39882
|
+
ensurePrivateSqlitePath(path, "staged knowledge database");
|
|
39883
|
+
if (schemaVersion !== undefined && schemaVersion !== KNOWLEDGE_SCHEMA_VERSION)
|
|
39884
|
+
throw new Error(`unsupported knowledge schema: ${schemaVersion}`);
|
|
39885
|
+
const store = KnowledgeStore.openIfExists(path);
|
|
39886
|
+
if (!store)
|
|
39887
|
+
throw new Error("staged knowledge database could not be opened");
|
|
39888
|
+
try {
|
|
39889
|
+
const integrity = store.verifyIntegrity();
|
|
39890
|
+
if (!integrity.ok)
|
|
39891
|
+
throw new Error(`staged knowledge database failed integrity: ${integrity.issues.map((issue) => `${issue.kind}=${issue.count}`).join(", ")}`);
|
|
39892
|
+
} finally {
|
|
39893
|
+
store.close();
|
|
39894
|
+
}
|
|
39895
|
+
}
|
|
39896
|
+
async function extractSkills(archive, destination) {
|
|
39897
|
+
const listing = await spawnTar(["-tzf", archive]);
|
|
39898
|
+
if (Buffer.byteLength(listing, "utf8") > CONTENT_SKILLS_LISTING_MAX_BYTES)
|
|
39899
|
+
throw new Error("skills archive listing is too large");
|
|
39900
|
+
const listingEntries = listing.split(/\r?\n/).map((item) => item.trim()).filter(Boolean);
|
|
39901
|
+
if (listingEntries.length > CONTENT_SKILLS_MAX_ENTRIES)
|
|
39902
|
+
throw new Error("skills archive contains too many entries");
|
|
39903
|
+
for (const entry of listingEntries) {
|
|
39904
|
+
if (entry.startsWith("/") || entry.split("/").includes("..") || entry.includes("\\"))
|
|
39905
|
+
throw new Error("skills archive contains an unsafe path");
|
|
39906
|
+
}
|
|
39907
|
+
mkdirSync7(destination, {
|
|
39908
|
+
recursive: true,
|
|
39909
|
+
mode: 448
|
|
39910
|
+
});
|
|
39911
|
+
await spawnTar(["-xzf", archive, "-C", destination]);
|
|
39912
|
+
const extractedEntries = [...walk(destination)];
|
|
39913
|
+
if (extractedEntries.some((path) => lstatSync4(path).isSymbolicLink()))
|
|
39914
|
+
throw new Error("skills archive contains a symbolic link");
|
|
39915
|
+
if (!extractedEntries.some((path) => path.endsWith("/SKILL.md")))
|
|
39916
|
+
throw new Error("skills archive contains no skills");
|
|
39917
|
+
}
|
|
39918
|
+
async function spawnTar(args) {
|
|
39919
|
+
const proc = Bun.spawn(["tar", ...args], {
|
|
39920
|
+
stdout: "pipe",
|
|
39921
|
+
stderr: "pipe"
|
|
39922
|
+
});
|
|
39923
|
+
const [stdout, stderr, code] = await Promise.all([new Response(proc.stdout).text(), new Response(proc.stderr).text(), proc.exited]);
|
|
39924
|
+
if (code !== 0)
|
|
39925
|
+
throw new Error(`skills archive operation failed${stderr.trim() ? `: ${stderr.trim()}` : ""}`);
|
|
39926
|
+
return stdout;
|
|
39927
|
+
}
|
|
39928
|
+
function* walk(root) {
|
|
39929
|
+
const entries = readdirSync6(root).sort();
|
|
39930
|
+
for (const entry of entries) {
|
|
39931
|
+
const path = join23(root, entry);
|
|
39932
|
+
const stat = lstatSync4(path);
|
|
39933
|
+
yield path;
|
|
39934
|
+
if (stat.isDirectory())
|
|
39935
|
+
yield* walk(path);
|
|
39936
|
+
}
|
|
39937
|
+
}
|
|
39938
|
+
function hashFile(path) {
|
|
39939
|
+
const descriptor = openSync4(path, "r");
|
|
39940
|
+
const hash = createHash10("sha256");
|
|
39941
|
+
const buffer = Buffer.allocUnsafe(64 * 1024);
|
|
39942
|
+
try {
|
|
39943
|
+
for (;; ) {
|
|
39944
|
+
const count2 = readSync2(descriptor, buffer, 0, buffer.length, null);
|
|
39945
|
+
if (count2 === 0)
|
|
39946
|
+
break;
|
|
39947
|
+
hash.update(buffer.subarray(0, count2));
|
|
39948
|
+
}
|
|
39949
|
+
return hash.digest("hex");
|
|
39950
|
+
} finally {
|
|
39951
|
+
closeSync4(descriptor);
|
|
39952
|
+
}
|
|
39953
|
+
}
|
|
39954
|
+
function parseStoredManifest(path) {
|
|
39955
|
+
ensurePrivateRegularFileIfExists(path, "stored content manifest");
|
|
39956
|
+
const raw = JSON.parse(readBoundedFileTextSyncNoFollow(path, CONTENT_MANIFEST_MAX_BYTES, "stored content manifest"));
|
|
39957
|
+
return parseContentManifest(raw, new URL("file:///stored/manifest.json"));
|
|
39958
|
+
}
|
|
39959
|
+
function pruneVersions(active) {
|
|
39960
|
+
const keep = new Set([active.version, active.previousVersion].filter((value) => Boolean(value)));
|
|
39961
|
+
for (const version of readdirVersions()) {
|
|
39962
|
+
if (keep.has(version))
|
|
39963
|
+
continue;
|
|
39964
|
+
try {
|
|
39965
|
+
rmSync3(contentVersionDir(version), {
|
|
39966
|
+
recursive: true,
|
|
39967
|
+
force: true
|
|
39968
|
+
});
|
|
39969
|
+
} catch {}
|
|
39970
|
+
}
|
|
39971
|
+
}
|
|
39972
|
+
function readdirVersions() {
|
|
39973
|
+
try {
|
|
39974
|
+
return readdirSync6(contentVersionsDir()).sort().filter((entry) => !entry.startsWith(".") && /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(entry));
|
|
39975
|
+
} catch {
|
|
39976
|
+
return [];
|
|
39977
|
+
}
|
|
39978
|
+
}
|
|
39979
|
+
function errorMessage6(error) {
|
|
39980
|
+
return error instanceof Error ? error.message : String(error);
|
|
39981
|
+
}
|
|
39982
|
+
function isAlreadyExists(error) {
|
|
39983
|
+
return Boolean(error && typeof error === "object" && "code" in error && error.code === "EEXIST");
|
|
39984
|
+
}
|
|
39985
|
+
function isNoSuchProcess(error) {
|
|
39986
|
+
return Boolean(error && typeof error === "object" && "code" in error && error.code === "ESRCH");
|
|
39987
|
+
}
|
|
39988
|
+
var CONTENT_MANIFEST_CACHE_TTL_MS, CONTENT_UPDATE_TIMEOUT_MS = 8000, CONTENT_ARTIFACT_MAX_BYTES = 2147483648, CONTENT_SKILLS_MAX_BYTES, CONTENT_SKILLS_MAX_ENTRIES = 8192, CONTENT_SKILLS_LISTING_MAX_BYTES, CONTENT_LOCK_STALE_MS, CONTENT_PREFERENCES_MAX_BYTES;
|
|
39989
|
+
var init_updater = __esm(() => {
|
|
39990
|
+
init_version();
|
|
39991
|
+
init_config();
|
|
39992
|
+
init_atomic_file();
|
|
39993
|
+
init_private_path();
|
|
39994
|
+
init_file_read();
|
|
39995
|
+
init_store();
|
|
39996
|
+
init_paths2();
|
|
39997
|
+
init_manifest();
|
|
39998
|
+
CONTENT_MANIFEST_CACHE_TTL_MS = 20 * 60 * 60 * 1000;
|
|
39999
|
+
CONTENT_SKILLS_MAX_BYTES = 256 * 1024 * 1024;
|
|
40000
|
+
CONTENT_SKILLS_LISTING_MAX_BYTES = 4 * 1024 * 1024;
|
|
40001
|
+
CONTENT_LOCK_STALE_MS = 15 * 60 * 1000;
|
|
40002
|
+
CONTENT_PREFERENCES_MAX_BYTES = 32 * 1024;
|
|
40003
|
+
});
|
|
40004
|
+
|
|
40005
|
+
// src/agent-knowledge/pack.ts
|
|
40006
|
+
import { closeSync as closeSync5, existsSync as existsSync19, fsyncSync as fsyncSync2, lstatSync as lstatSync5, mkdirSync as mkdirSync8, openSync as openSync5, readdirSync as readdirSync7, renameSync as renameSync4, rmSync as rmSync4, writeSync as writeSync2 } from "fs";
|
|
40007
|
+
import { createHash as createHash11, randomUUID as randomUUID5 } from "crypto";
|
|
40008
|
+
import { join as join24 } from "path";
|
|
39093
40009
|
function packDir(meta) {
|
|
39094
40010
|
assertPackPathPart(meta.id, "knowledge pack id");
|
|
39095
40011
|
assertPackPathPart(meta.pin, "knowledge pack pin");
|
|
39096
|
-
return
|
|
40012
|
+
return join24(packsDir(), `${meta.id}@${meta.pin.slice(0, 12)}`);
|
|
39097
40013
|
}
|
|
39098
40014
|
function writePack(meta, records, entities) {
|
|
39099
40015
|
const root = packsDir();
|
|
39100
40016
|
ensurePrivateDirectory(root, "knowledge pack directory");
|
|
39101
40017
|
const dir = packDir(meta);
|
|
39102
|
-
const temporary = `${dir}.tmp-${process.pid}-${
|
|
39103
|
-
|
|
40018
|
+
const temporary = `${dir}.tmp-${process.pid}-${randomUUID5()}`;
|
|
40019
|
+
mkdirSync8(temporary, {
|
|
39104
40020
|
mode: 448
|
|
39105
40021
|
});
|
|
39106
40022
|
try {
|
|
39107
|
-
atomicWriteFile(
|
|
40023
|
+
atomicWriteFile(join24(temporary, "meta.json"), `${JSON.stringify(meta, null, 2)}
|
|
39108
40024
|
`, 384);
|
|
39109
|
-
writeJsonl(
|
|
39110
|
-
writeJsonl(
|
|
40025
|
+
writeJsonl(join24(temporary, "records.jsonl"), records, "knowledge records");
|
|
40026
|
+
writeJsonl(join24(temporary, "entities.jsonl"), entities, "knowledge entities");
|
|
39111
40027
|
if (realDirectoryExists(dir, "knowledge pack"))
|
|
39112
|
-
|
|
40028
|
+
rmSync4(dir, {
|
|
39113
40029
|
recursive: true,
|
|
39114
40030
|
force: true
|
|
39115
40031
|
});
|
|
39116
|
-
|
|
40032
|
+
renameSync4(temporary, dir);
|
|
39117
40033
|
syncDirectory(root);
|
|
39118
40034
|
} catch (error) {
|
|
39119
|
-
|
|
40035
|
+
rmSync4(temporary, {
|
|
39120
40036
|
recursive: true,
|
|
39121
40037
|
force: true
|
|
39122
40038
|
});
|
|
@@ -39126,19 +40042,19 @@ function writePack(meta, records, entities) {
|
|
|
39126
40042
|
}
|
|
39127
40043
|
function listPacks() {
|
|
39128
40044
|
const root = packsDir();
|
|
39129
|
-
if (!
|
|
40045
|
+
if (!existsSync19(root))
|
|
39130
40046
|
return [];
|
|
39131
40047
|
assertRealDirectory(root, "knowledge pack directory");
|
|
39132
|
-
const entries =
|
|
40048
|
+
const entries = readdirSync7(root);
|
|
39133
40049
|
if (entries.length > PACK_DIRECTORY_MAX_COUNT)
|
|
39134
40050
|
throw new Error(`knowledge pack directory exceeded ${PACK_DIRECTORY_MAX_COUNT} entries`);
|
|
39135
40051
|
const out = [];
|
|
39136
40052
|
for (const entry of entries) {
|
|
39137
|
-
const dir =
|
|
40053
|
+
const dir = join24(root, entry);
|
|
39138
40054
|
try {
|
|
39139
40055
|
assertRealDirectory(dir, "knowledge pack");
|
|
39140
|
-
const metaPath =
|
|
39141
|
-
if (!
|
|
40056
|
+
const metaPath = join24(dir, "meta.json");
|
|
40057
|
+
if (!existsSync19(metaPath))
|
|
39142
40058
|
continue;
|
|
39143
40059
|
const meta = JSON.parse(readBoundedFileTextSyncNoFollow(metaPath, PACK_META_MAX_BYTES, "knowledge pack metadata"));
|
|
39144
40060
|
if (!validPackMeta(meta))
|
|
@@ -39162,14 +40078,14 @@ function latestPacks() {
|
|
|
39162
40078
|
}
|
|
39163
40079
|
function readRecords(dir) {
|
|
39164
40080
|
assertRealDirectory(dir, "knowledge pack");
|
|
39165
|
-
return readJsonl(
|
|
40081
|
+
return readJsonl(join24(dir, "records.jsonl"), "knowledge records");
|
|
39166
40082
|
}
|
|
39167
40083
|
function readEntities(dir) {
|
|
39168
40084
|
assertRealDirectory(dir, "knowledge pack");
|
|
39169
|
-
return readJsonl(
|
|
40085
|
+
return readJsonl(join24(dir, "entities.jsonl"), "knowledge entities");
|
|
39170
40086
|
}
|
|
39171
40087
|
function readJsonl(path, label) {
|
|
39172
|
-
if (!
|
|
40088
|
+
if (!existsSync19(path))
|
|
39173
40089
|
return [];
|
|
39174
40090
|
const out = [];
|
|
39175
40091
|
forEachFileLineSync(path, {
|
|
@@ -39190,7 +40106,7 @@ function readJsonl(path, label) {
|
|
|
39190
40106
|
return out;
|
|
39191
40107
|
}
|
|
39192
40108
|
function writeJsonl(path, values, label) {
|
|
39193
|
-
const descriptor =
|
|
40109
|
+
const descriptor = openSync5(path, "wx", 384);
|
|
39194
40110
|
let totalBytes = 0;
|
|
39195
40111
|
try {
|
|
39196
40112
|
for (const value of values) {
|
|
@@ -39203,7 +40119,7 @@ function writeJsonl(path, values, label) {
|
|
|
39203
40119
|
throw new Error(`${label} exceeded ${PACK_JSONL_MAX_BYTES} bytes`);
|
|
39204
40120
|
let offset = 0;
|
|
39205
40121
|
while (offset < line.byteLength) {
|
|
39206
|
-
const written =
|
|
40122
|
+
const written = writeSync2(descriptor, line, offset, line.byteLength - offset);
|
|
39207
40123
|
if (written <= 0)
|
|
39208
40124
|
throw new Error(`${label} write made no progress`);
|
|
39209
40125
|
offset += written;
|
|
@@ -39211,7 +40127,7 @@ function writeJsonl(path, values, label) {
|
|
|
39211
40127
|
}
|
|
39212
40128
|
fsyncSync2(descriptor);
|
|
39213
40129
|
} finally {
|
|
39214
|
-
|
|
40130
|
+
closeSync5(descriptor);
|
|
39215
40131
|
}
|
|
39216
40132
|
}
|
|
39217
40133
|
function validPackMeta(value) {
|
|
@@ -39222,21 +40138,21 @@ function assertPackPathPart(value, label) {
|
|
|
39222
40138
|
throw new Error(`${label} is invalid`);
|
|
39223
40139
|
}
|
|
39224
40140
|
function realDirectoryExists(path, label) {
|
|
39225
|
-
if (!
|
|
40141
|
+
if (!existsSync19(path))
|
|
39226
40142
|
return false;
|
|
39227
40143
|
assertRealDirectory(path, label);
|
|
39228
40144
|
return true;
|
|
39229
40145
|
}
|
|
39230
40146
|
function assertRealDirectory(path, label) {
|
|
39231
|
-
const stat =
|
|
40147
|
+
const stat = lstatSync5(path);
|
|
39232
40148
|
if (stat.isSymbolicLink() || !stat.isDirectory())
|
|
39233
40149
|
throw new Error(`${label} must be a real directory`);
|
|
39234
40150
|
}
|
|
39235
40151
|
function recordId(pack, pin, discriminator) {
|
|
39236
|
-
return
|
|
40152
|
+
return createHash11("sha256").update(`${pack}\x00${pin}\x00${discriminator}`).digest("hex").slice(0, 16);
|
|
39237
40153
|
}
|
|
39238
40154
|
function sourceHash(body) {
|
|
39239
|
-
return `sha256:${
|
|
40155
|
+
return `sha256:${createHash11("sha256").update(body).digest("hex")}`;
|
|
39240
40156
|
}
|
|
39241
40157
|
function extractEntities(recordId2, text2) {
|
|
39242
40158
|
const found = new Set;
|
|
@@ -39265,7 +40181,7 @@ var init_pack = __esm(() => {
|
|
|
39265
40181
|
init_atomic_file();
|
|
39266
40182
|
init_private_path();
|
|
39267
40183
|
init_file_read();
|
|
39268
|
-
|
|
40184
|
+
init_paths3();
|
|
39269
40185
|
PACK_META_MAX_BYTES = 1024 * 1024;
|
|
39270
40186
|
PACK_JSONL_MAX_BYTES = 256 * 1024 * 1024;
|
|
39271
40187
|
PACK_LINE_MAX_BYTES = 64 * 1024 * 1024;
|
|
@@ -39289,34 +40205,34 @@ var init_pack = __esm(() => {
|
|
|
39289
40205
|
});
|
|
39290
40206
|
|
|
39291
40207
|
// src/agent-knowledge/ingest/taxonomy-pack.ts
|
|
39292
|
-
import { closeSync as
|
|
39293
|
-
import { randomUUID as
|
|
39294
|
-
import { join as
|
|
40208
|
+
import { closeSync as closeSync6, existsSync as existsSync20, fsyncSync as fsyncSync3, lstatSync as lstatSync6, mkdirSync as mkdirSync9, openSync as openSync6, readdirSync as readdirSync8, renameSync as renameSync5, rmSync as rmSync5, writeSync as writeSync3 } from "fs";
|
|
40209
|
+
import { randomUUID as randomUUID6 } from "crypto";
|
|
40210
|
+
import { join as join25 } from "path";
|
|
39295
40211
|
function writeTaxonomy(meta, nodes, edges) {
|
|
39296
40212
|
assertPathPart(meta.id, "taxonomy id");
|
|
39297
40213
|
assertPathPart(meta.pin, "taxonomy pin");
|
|
39298
40214
|
const root = taxonomyDir();
|
|
39299
40215
|
ensurePrivateDirectory(root, "knowledge taxonomy directory");
|
|
39300
|
-
const dir =
|
|
39301
|
-
const temporary = `${dir}.tmp-${process.pid}-${
|
|
39302
|
-
|
|
40216
|
+
const dir = join25(root, `${meta.id}@${meta.pin}`);
|
|
40217
|
+
const temporary = `${dir}.tmp-${process.pid}-${randomUUID6()}`;
|
|
40218
|
+
mkdirSync9(temporary, {
|
|
39303
40219
|
recursive: true,
|
|
39304
40220
|
mode: 448
|
|
39305
40221
|
});
|
|
39306
40222
|
try {
|
|
39307
|
-
atomicWriteFile(
|
|
40223
|
+
atomicWriteFile(join25(temporary, "meta.json"), `${JSON.stringify(meta, null, 2)}
|
|
39308
40224
|
`, 384);
|
|
39309
|
-
writeJsonl2(
|
|
39310
|
-
writeJsonl2(
|
|
40225
|
+
writeJsonl2(join25(temporary, "nodes.jsonl"), nodes, "taxonomy nodes");
|
|
40226
|
+
writeJsonl2(join25(temporary, "edges.jsonl"), edges, "taxonomy edges");
|
|
39311
40227
|
if (realDirectoryExists2(dir, "knowledge taxonomy"))
|
|
39312
|
-
|
|
40228
|
+
rmSync5(dir, {
|
|
39313
40229
|
recursive: true,
|
|
39314
40230
|
force: true
|
|
39315
40231
|
});
|
|
39316
|
-
|
|
40232
|
+
renameSync5(temporary, dir);
|
|
39317
40233
|
syncDirectory(root);
|
|
39318
40234
|
} catch (error) {
|
|
39319
|
-
|
|
40235
|
+
rmSync5(temporary, {
|
|
39320
40236
|
recursive: true,
|
|
39321
40237
|
force: true
|
|
39322
40238
|
});
|
|
@@ -39325,7 +40241,7 @@ function writeTaxonomy(meta, nodes, edges) {
|
|
|
39325
40241
|
return dir;
|
|
39326
40242
|
}
|
|
39327
40243
|
function writeJsonl2(path, values, label) {
|
|
39328
|
-
const descriptor =
|
|
40244
|
+
const descriptor = openSync6(path, "wx", 384);
|
|
39329
40245
|
let totalBytes = 0;
|
|
39330
40246
|
try {
|
|
39331
40247
|
for (const value of values) {
|
|
@@ -39338,7 +40254,7 @@ function writeJsonl2(path, values, label) {
|
|
|
39338
40254
|
throw new Error(`${label} exceeded ${TAXONOMY_JSONL_MAX_BYTES} bytes`);
|
|
39339
40255
|
let offset = 0;
|
|
39340
40256
|
while (offset < line.byteLength) {
|
|
39341
|
-
const written =
|
|
40257
|
+
const written = writeSync3(descriptor, line, offset, line.byteLength - offset);
|
|
39342
40258
|
if (written <= 0)
|
|
39343
40259
|
throw new Error(`${label} write made no progress`);
|
|
39344
40260
|
offset += written;
|
|
@@ -39346,24 +40262,24 @@ function writeJsonl2(path, values, label) {
|
|
|
39346
40262
|
}
|
|
39347
40263
|
fsyncSync3(descriptor);
|
|
39348
40264
|
} finally {
|
|
39349
|
-
|
|
40265
|
+
closeSync6(descriptor);
|
|
39350
40266
|
}
|
|
39351
40267
|
}
|
|
39352
40268
|
function listTaxonomies() {
|
|
39353
40269
|
const root = taxonomyDir();
|
|
39354
|
-
if (!
|
|
40270
|
+
if (!existsSync20(root))
|
|
39355
40271
|
return [];
|
|
39356
40272
|
assertRealDirectory2(root, "knowledge taxonomy directory");
|
|
39357
|
-
const entries =
|
|
40273
|
+
const entries = readdirSync8(root);
|
|
39358
40274
|
if (entries.length > TAXONOMY_DIRECTORY_MAX_COUNT)
|
|
39359
40275
|
throw new Error(`knowledge taxonomy directory exceeded ${TAXONOMY_DIRECTORY_MAX_COUNT} entries`);
|
|
39360
40276
|
const out = [];
|
|
39361
40277
|
for (const entry of entries) {
|
|
39362
|
-
const dir =
|
|
40278
|
+
const dir = join25(root, entry);
|
|
39363
40279
|
try {
|
|
39364
40280
|
assertRealDirectory2(dir, "knowledge taxonomy");
|
|
39365
|
-
const metaPath =
|
|
39366
|
-
if (!
|
|
40281
|
+
const metaPath = join25(dir, "meta.json");
|
|
40282
|
+
if (!existsSync20(metaPath))
|
|
39367
40283
|
continue;
|
|
39368
40284
|
const meta = JSON.parse(readBoundedFileTextSyncNoFollow(metaPath, TAXONOMY_META_MAX_BYTES, "taxonomy metadata"));
|
|
39369
40285
|
if (typeof meta?.id !== "string" || typeof meta.pin !== "string" || typeof meta.retrievedAt !== "string")
|
|
@@ -39387,14 +40303,14 @@ function latestTaxonomies() {
|
|
|
39387
40303
|
}
|
|
39388
40304
|
function readNodes(dir) {
|
|
39389
40305
|
assertRealDirectory2(dir, "knowledge taxonomy");
|
|
39390
|
-
return readJsonl2(
|
|
40306
|
+
return readJsonl2(join25(dir, "nodes.jsonl"));
|
|
39391
40307
|
}
|
|
39392
40308
|
function readEdges(dir) {
|
|
39393
40309
|
assertRealDirectory2(dir, "knowledge taxonomy");
|
|
39394
|
-
return readJsonl2(
|
|
40310
|
+
return readJsonl2(join25(dir, "edges.jsonl"));
|
|
39395
40311
|
}
|
|
39396
40312
|
function readJsonl2(path) {
|
|
39397
|
-
if (!
|
|
40313
|
+
if (!existsSync20(path))
|
|
39398
40314
|
return [];
|
|
39399
40315
|
const out = [];
|
|
39400
40316
|
forEachFileLineSync(path, {
|
|
@@ -39419,19 +40335,19 @@ function assertPathPart(value, label) {
|
|
|
39419
40335
|
throw new Error(`${label} is invalid`);
|
|
39420
40336
|
}
|
|
39421
40337
|
function realDirectoryExists2(path, label) {
|
|
39422
|
-
if (!
|
|
40338
|
+
if (!existsSync20(path))
|
|
39423
40339
|
return false;
|
|
39424
40340
|
assertRealDirectory2(path, label);
|
|
39425
40341
|
return true;
|
|
39426
40342
|
}
|
|
39427
40343
|
function assertRealDirectory2(path, label) {
|
|
39428
|
-
const stat =
|
|
40344
|
+
const stat = lstatSync6(path);
|
|
39429
40345
|
if (stat.isSymbolicLink() || !stat.isDirectory())
|
|
39430
40346
|
throw new Error(`${label} must be a real directory`);
|
|
39431
40347
|
}
|
|
39432
40348
|
var TAXONOMY_META_MAX_BYTES, TAXONOMY_JSONL_MAX_BYTES, TAXONOMY_LINE_MAX_BYTES, TAXONOMY_ENTRY_MAX_COUNT = 1e6, TAXONOMY_DIRECTORY_MAX_COUNT = 4096;
|
|
39433
40349
|
var init_taxonomy_pack = __esm(() => {
|
|
39434
|
-
|
|
40350
|
+
init_paths3();
|
|
39435
40351
|
init_file_read();
|
|
39436
40352
|
init_atomic_file();
|
|
39437
40353
|
init_private_path();
|
|
@@ -39474,16 +40390,16 @@ var init_http2 = __esm(() => {
|
|
|
39474
40390
|
});
|
|
39475
40391
|
|
|
39476
40392
|
// src/agent-knowledge/ingest/enrichment.ts
|
|
39477
|
-
import { createReadStream, existsSync as
|
|
40393
|
+
import { createReadStream, existsSync as existsSync21 } from "fs";
|
|
39478
40394
|
import { open as open3, rename, unlink as unlink2 } from "fs/promises";
|
|
39479
|
-
import { randomUUID as
|
|
40395
|
+
import { randomUUID as randomUUID7 } from "crypto";
|
|
39480
40396
|
import { Writable } from "stream";
|
|
39481
40397
|
import { pipeline } from "stream/promises";
|
|
39482
40398
|
import { StringDecoder as StringDecoder3 } from "string_decoder";
|
|
39483
40399
|
import { createGunzip } from "zlib";
|
|
39484
|
-
import { join as
|
|
40400
|
+
import { join as join26 } from "path";
|
|
39485
40401
|
function enrichmentDir() {
|
|
39486
|
-
return
|
|
40402
|
+
return join26(knowledgeRoot(), "enrichment");
|
|
39487
40403
|
}
|
|
39488
40404
|
async function ingestEnrichment() {
|
|
39489
40405
|
const map = new Map;
|
|
@@ -39547,8 +40463,8 @@ async function ingestEnrichment() {
|
|
|
39547
40463
|
};
|
|
39548
40464
|
}
|
|
39549
40465
|
function readEnrichment() {
|
|
39550
|
-
const path =
|
|
39551
|
-
if (!
|
|
40466
|
+
const path = join26(enrichmentDir(), "enrichment.jsonl");
|
|
40467
|
+
if (!existsSync21(path))
|
|
39552
40468
|
return [];
|
|
39553
40469
|
const out = [];
|
|
39554
40470
|
forEachFileLineSync(path, {
|
|
@@ -39569,7 +40485,7 @@ function readEnrichment() {
|
|
|
39569
40485
|
return out;
|
|
39570
40486
|
}
|
|
39571
40487
|
async function forEachEpssLine(dir, consume) {
|
|
39572
|
-
const archive =
|
|
40488
|
+
const archive = join26(dir, `.epss-${randomUUID7()}.csv.gz`);
|
|
39573
40489
|
try {
|
|
39574
40490
|
await downloadKnowledgeFile(EPSS_URL, archive, EPSS_GZIP_MAX_BYTES, "epss archive");
|
|
39575
40491
|
await pipeline(createReadStream(archive), createGunzip(), new BoundedLineSink(EPSS_CSV_MAX_BYTES, EPSS_LINE_MAX_BYTES, consume));
|
|
@@ -39580,8 +40496,8 @@ async function forEachEpssLine(dir, consume) {
|
|
|
39580
40496
|
}
|
|
39581
40497
|
}
|
|
39582
40498
|
async function writeEnrichmentRows(dir, rows) {
|
|
39583
|
-
const output =
|
|
39584
|
-
const temporary =
|
|
40499
|
+
const output = join26(dir, "enrichment.jsonl");
|
|
40500
|
+
const temporary = join26(dir, `.enrichment-${randomUUID7()}.jsonl`);
|
|
39585
40501
|
let file;
|
|
39586
40502
|
try {
|
|
39587
40503
|
file = await open3(temporary, "wx", 384);
|
|
@@ -39642,7 +40558,7 @@ function recordValue2(value) {
|
|
|
39642
40558
|
}
|
|
39643
40559
|
var KEV_URL = "https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json", EPSS_URL = "https://epss.empiricalsecurity.com/epss_scores-current.csv.gz", KEV_MAX_BYTES, EPSS_GZIP_MAX_BYTES, EPSS_CSV_MAX_BYTES, EPSS_LINE_MAX_BYTES, OUTPUT_CHUNK_BYTES, ENRICHMENT_FILE_MAX_BYTES, KEV_ENTRY_MAX_COUNT = 1e5, ENRICHMENT_ROW_MAX_COUNT = 1e6, BoundedLineSink;
|
|
39644
40560
|
var init_enrichment = __esm(() => {
|
|
39645
|
-
|
|
40561
|
+
init_paths3();
|
|
39646
40562
|
init_http2();
|
|
39647
40563
|
init_file_read();
|
|
39648
40564
|
init_private_path();
|
|
@@ -39699,18 +40615,18 @@ var init_enrichment = __esm(() => {
|
|
|
39699
40615
|
});
|
|
39700
40616
|
|
|
39701
40617
|
// src/agent-knowledge/build.ts
|
|
39702
|
-
import { createHash as
|
|
39703
|
-
import { mkdirSync as
|
|
39704
|
-
import { dirname as
|
|
40618
|
+
import { createHash as createHash12, randomUUID as randomUUID8 } from "crypto";
|
|
40619
|
+
import { mkdirSync as mkdirSync10, renameSync as renameSync6, rmSync as rmSync6 } from "fs";
|
|
40620
|
+
import { dirname as dirname10 } from "path";
|
|
39705
40621
|
function buildKnowledgeDb(options = {}) {
|
|
39706
|
-
const path = options.path ??
|
|
39707
|
-
const temporary = `${path}.tmp-${process.pid}-${
|
|
39708
|
-
|
|
40622
|
+
const path = options.path ?? legacyKnowledgeDbPath();
|
|
40623
|
+
const temporary = `${path}.tmp-${process.pid}-${randomUUID8()}`;
|
|
40624
|
+
mkdirSync10(dirname10(path), {
|
|
39709
40625
|
recursive: true,
|
|
39710
40626
|
mode: 448
|
|
39711
40627
|
});
|
|
39712
40628
|
if (options.path === undefined)
|
|
39713
|
-
ensurePrivateDirectory(
|
|
40629
|
+
ensurePrivateDirectory(dirname10(path), "farai home directory");
|
|
39714
40630
|
ensurePrivateRegularFileIfExists(path, "knowledge database");
|
|
39715
40631
|
const store = new KnowledgeStore(temporary, true);
|
|
39716
40632
|
const db = store.writable();
|
|
@@ -39767,18 +40683,18 @@ function buildKnowledgeDb(options = {}) {
|
|
|
39767
40683
|
const actualEdges = rowCount(db, "kb_edges");
|
|
39768
40684
|
store.close();
|
|
39769
40685
|
ensurePrivateSqlitePath(temporary, "staged knowledge database");
|
|
39770
|
-
|
|
40686
|
+
rmSync6(`${temporary}-wal`, {
|
|
39771
40687
|
force: true
|
|
39772
40688
|
});
|
|
39773
|
-
|
|
40689
|
+
rmSync6(`${temporary}-shm`, {
|
|
39774
40690
|
force: true
|
|
39775
40691
|
});
|
|
39776
|
-
|
|
40692
|
+
rmSync6(`${temporary}-journal`, {
|
|
39777
40693
|
force: true
|
|
39778
40694
|
});
|
|
39779
|
-
|
|
40695
|
+
renameSync6(temporary, path);
|
|
39780
40696
|
ensurePrivateRegularFileIfExists(path, "knowledge database");
|
|
39781
|
-
syncDirectory(
|
|
40697
|
+
syncDirectory(dirname10(path));
|
|
39782
40698
|
return {
|
|
39783
40699
|
path,
|
|
39784
40700
|
packs: packs.length,
|
|
@@ -39793,16 +40709,16 @@ function buildKnowledgeDb(options = {}) {
|
|
|
39793
40709
|
try {
|
|
39794
40710
|
store.close();
|
|
39795
40711
|
} catch {}
|
|
39796
|
-
|
|
40712
|
+
rmSync6(temporary, {
|
|
39797
40713
|
force: true
|
|
39798
40714
|
});
|
|
39799
|
-
|
|
40715
|
+
rmSync6(`${temporary}-wal`, {
|
|
39800
40716
|
force: true
|
|
39801
40717
|
});
|
|
39802
|
-
|
|
40718
|
+
rmSync6(`${temporary}-shm`, {
|
|
39803
40719
|
force: true
|
|
39804
40720
|
});
|
|
39805
|
-
|
|
40721
|
+
rmSync6(`${temporary}-journal`, {
|
|
39806
40722
|
force: true
|
|
39807
40723
|
});
|
|
39808
40724
|
throw error;
|
|
@@ -39826,7 +40742,7 @@ function trackDuplicate(groups, record3) {
|
|
|
39826
40742
|
const normalized = record3.answer.replace(/\s+/g, " ").trim().toLowerCase();
|
|
39827
40743
|
if (normalized.length < 64)
|
|
39828
40744
|
return;
|
|
39829
|
-
const key =
|
|
40745
|
+
const key = createHash12("sha256").update(normalized).digest("hex");
|
|
39830
40746
|
const list = groups.get(key) ?? [];
|
|
39831
40747
|
list.push(record3.id);
|
|
39832
40748
|
groups.set(key, list);
|
|
@@ -39849,24 +40765,24 @@ var init_build = __esm(() => {
|
|
|
39849
40765
|
init_atomic_file();
|
|
39850
40766
|
init_private_path();
|
|
39851
40767
|
init_store();
|
|
39852
|
-
|
|
40768
|
+
init_paths3();
|
|
39853
40769
|
init_pack();
|
|
39854
40770
|
init_taxonomy_pack();
|
|
39855
40771
|
init_enrichment();
|
|
39856
40772
|
});
|
|
39857
40773
|
|
|
39858
40774
|
// src/agent-knowledge/ingest/git-source.ts
|
|
39859
|
-
import { existsSync as
|
|
39860
|
-
import { randomUUID as
|
|
39861
|
-
import { join as
|
|
40775
|
+
import { existsSync as existsSync22, lstatSync as lstatSync7, renameSync as renameSync7, rmSync as rmSync7 } from "fs";
|
|
40776
|
+
import { randomUUID as randomUUID9 } from "crypto";
|
|
40777
|
+
import { join as join27 } from "path";
|
|
39862
40778
|
async function fetchGitSource(source) {
|
|
39863
40779
|
assertSource(source);
|
|
39864
40780
|
const root = cacheDir();
|
|
39865
40781
|
ensurePrivateDirectory(root, "knowledge cache directory");
|
|
39866
|
-
const dir =
|
|
40782
|
+
const dir = join27(root, source.id);
|
|
39867
40783
|
const cached = cachedRepositoryExists(dir);
|
|
39868
40784
|
if (!cached) {
|
|
39869
|
-
const staging =
|
|
40785
|
+
const staging = join27(root, `.${source.id}-clone-${randomUUID9()}`);
|
|
39870
40786
|
try {
|
|
39871
40787
|
const args = ["clone", "--depth", "1", "--branch", source.branch];
|
|
39872
40788
|
if (source.sparse?.length)
|
|
@@ -39875,9 +40791,9 @@ async function fetchGitSource(source) {
|
|
|
39875
40791
|
await run("git", args);
|
|
39876
40792
|
if (source.sparse?.length)
|
|
39877
40793
|
await run("git", ["-C", staging, "sparse-checkout", "set", "--no-cone", "--", ...source.sparse]);
|
|
39878
|
-
|
|
40794
|
+
renameSync7(staging, dir);
|
|
39879
40795
|
} finally {
|
|
39880
|
-
|
|
40796
|
+
rmSync7(staging, {
|
|
39881
40797
|
recursive: true,
|
|
39882
40798
|
force: true
|
|
39883
40799
|
});
|
|
@@ -39945,22 +40861,22 @@ function assertSource(source) {
|
|
|
39945
40861
|
throw new Error(`invalid sparse path for git source: ${source.id}`);
|
|
39946
40862
|
}
|
|
39947
40863
|
function cachedRepositoryExists(dir) {
|
|
39948
|
-
if (!
|
|
40864
|
+
if (!existsSync22(dir))
|
|
39949
40865
|
return false;
|
|
39950
|
-
const directory =
|
|
40866
|
+
const directory = lstatSync7(dir);
|
|
39951
40867
|
if (directory.isSymbolicLink() || !directory.isDirectory())
|
|
39952
40868
|
throw new Error(`git source cache must be a real directory: ${dir}`);
|
|
39953
|
-
const git =
|
|
39954
|
-
if (!
|
|
40869
|
+
const git = join27(dir, ".git");
|
|
40870
|
+
if (!existsSync22(git))
|
|
39955
40871
|
throw new Error(`git source cache is not a repository: ${dir}`);
|
|
39956
|
-
const metadata =
|
|
40872
|
+
const metadata = lstatSync7(git);
|
|
39957
40873
|
if (metadata.isSymbolicLink() || !metadata.isDirectory())
|
|
39958
40874
|
throw new Error(`git source metadata must be a real directory: ${dir}`);
|
|
39959
40875
|
return true;
|
|
39960
40876
|
}
|
|
39961
40877
|
var GIT_TIMEOUT_MS;
|
|
39962
40878
|
var init_git_source = __esm(() => {
|
|
39963
|
-
|
|
40879
|
+
init_paths3();
|
|
39964
40880
|
init_captured_process();
|
|
39965
40881
|
init_output_buffer();
|
|
39966
40882
|
init_private_path();
|
|
@@ -40072,11 +40988,11 @@ var init_markdown_chunk = __esm(() => {
|
|
|
40072
40988
|
});
|
|
40073
40989
|
|
|
40074
40990
|
// src/agent-knowledge/ingest/hacktricks.ts
|
|
40075
|
-
import { existsSync as
|
|
40076
|
-
import { join as
|
|
40991
|
+
import { existsSync as existsSync23, lstatSync as lstatSync8, readdirSync as readdirSync9 } from "fs";
|
|
40992
|
+
import { join as join28, relative as relative8 } from "path";
|
|
40077
40993
|
async function ingestHacktricks() {
|
|
40078
40994
|
const fetched = await fetchGitSource(SOURCE);
|
|
40079
|
-
const srcDir =
|
|
40995
|
+
const srcDir = join28(fetched.dir, "src");
|
|
40080
40996
|
const meta = {
|
|
40081
40997
|
id: "hacktricks",
|
|
40082
40998
|
sourceUrl: "https://github.com/HackTricks-wiki/hacktricks",
|
|
@@ -40128,9 +41044,9 @@ async function ingestHacktricks() {
|
|
|
40128
41044
|
};
|
|
40129
41045
|
}
|
|
40130
41046
|
function markdownFiles(root) {
|
|
40131
|
-
if (!
|
|
41047
|
+
if (!existsSync23(root))
|
|
40132
41048
|
return [];
|
|
40133
|
-
const rootInfo =
|
|
41049
|
+
const rootInfo = lstatSync8(root);
|
|
40134
41050
|
if (rootInfo.isSymbolicLink() || !rootInfo.isDirectory())
|
|
40135
41051
|
throw new Error("hacktricks source root must be a real directory");
|
|
40136
41052
|
const out = [];
|
|
@@ -40138,12 +41054,12 @@ function markdownFiles(root) {
|
|
|
40138
41054
|
let entries = 0;
|
|
40139
41055
|
while (pending.length) {
|
|
40140
41056
|
const dir = pending.pop();
|
|
40141
|
-
for (const entry of
|
|
41057
|
+
for (const entry of readdirSync9(dir)) {
|
|
40142
41058
|
entries += 1;
|
|
40143
41059
|
if (entries > WALK_ENTRY_MAX_COUNT)
|
|
40144
41060
|
throw new Error(`hacktricks source exceeded ${WALK_ENTRY_MAX_COUNT} entries`);
|
|
40145
|
-
const full =
|
|
40146
|
-
const info =
|
|
41061
|
+
const full = join28(dir, entry);
|
|
41062
|
+
const info = lstatSync8(full);
|
|
40147
41063
|
if (info.isSymbolicLink())
|
|
40148
41064
|
continue;
|
|
40149
41065
|
if (info.isDirectory())
|
|
@@ -40173,8 +41089,8 @@ var init_hacktricks = __esm(() => {
|
|
|
40173
41089
|
});
|
|
40174
41090
|
|
|
40175
41091
|
// src/agent-knowledge/ingest/payloads.ts
|
|
40176
|
-
import { existsSync as
|
|
40177
|
-
import { join as
|
|
41092
|
+
import { existsSync as existsSync24, lstatSync as lstatSync9, readdirSync as readdirSync10 } from "fs";
|
|
41093
|
+
import { join as join29, relative as relative9 } from "path";
|
|
40178
41094
|
async function ingestPayloads() {
|
|
40179
41095
|
const fetched = await fetchGitSource(SOURCE2);
|
|
40180
41096
|
const meta = {
|
|
@@ -40230,9 +41146,9 @@ async function ingestPayloads() {
|
|
|
40230
41146
|
};
|
|
40231
41147
|
}
|
|
40232
41148
|
function markdownFiles2(root) {
|
|
40233
|
-
if (!
|
|
41149
|
+
if (!existsSync24(root))
|
|
40234
41150
|
return [];
|
|
40235
|
-
const rootInfo =
|
|
41151
|
+
const rootInfo = lstatSync9(root);
|
|
40236
41152
|
if (rootInfo.isSymbolicLink() || !rootInfo.isDirectory())
|
|
40237
41153
|
throw new Error("payload source root must be a real directory");
|
|
40238
41154
|
const out = [];
|
|
@@ -40240,14 +41156,14 @@ function markdownFiles2(root) {
|
|
|
40240
41156
|
let entries = 0;
|
|
40241
41157
|
while (pending.length) {
|
|
40242
41158
|
const dir = pending.pop();
|
|
40243
|
-
for (const entry of
|
|
41159
|
+
for (const entry of readdirSync10(dir)) {
|
|
40244
41160
|
if (entry === ".git")
|
|
40245
41161
|
continue;
|
|
40246
41162
|
entries += 1;
|
|
40247
41163
|
if (entries > WALK_ENTRY_MAX_COUNT2)
|
|
40248
41164
|
throw new Error(`payload source exceeded ${WALK_ENTRY_MAX_COUNT2} entries`);
|
|
40249
|
-
const full =
|
|
40250
|
-
const info =
|
|
41165
|
+
const full = join29(dir, entry);
|
|
41166
|
+
const info = lstatSync9(full);
|
|
40251
41167
|
if (info.isSymbolicLink())
|
|
40252
41168
|
continue;
|
|
40253
41169
|
if (info.isDirectory())
|
|
@@ -40489,15 +41405,15 @@ var init_attack = __esm(() => {
|
|
|
40489
41405
|
});
|
|
40490
41406
|
|
|
40491
41407
|
// src/agent-knowledge/ingest/zip-fetch.ts
|
|
40492
|
-
import { existsSync as
|
|
40493
|
-
import { randomUUID as
|
|
40494
|
-
import { basename as basename5, join as
|
|
41408
|
+
import { existsSync as existsSync25, lstatSync as lstatSync10, mkdirSync as mkdirSync11, readdirSync as readdirSync11, renameSync as renameSync8, rmSync as rmSync8, unlinkSync as unlinkSync7 } from "fs";
|
|
41409
|
+
import { randomUUID as randomUUID10 } from "crypto";
|
|
41410
|
+
import { basename as basename5, join as join30, relative as relative10, resolve as resolve10 } from "path";
|
|
40495
41411
|
async function fetchZippedXml(id2, url, pattern) {
|
|
40496
|
-
const dir =
|
|
41412
|
+
const dir = join30(cacheDir(), id2);
|
|
40497
41413
|
ensurePrivateDirectory(dir, `${id2} knowledge cache directory`);
|
|
40498
|
-
const token =
|
|
40499
|
-
const zipPath =
|
|
40500
|
-
const staging =
|
|
41414
|
+
const token = randomUUID10();
|
|
41415
|
+
const zipPath = join30(dir, `.download-${token}.zip`);
|
|
41416
|
+
const staging = join30(dir, `.extract-${token}`);
|
|
40501
41417
|
try {
|
|
40502
41418
|
await downloadKnowledgeFile(url, zipPath, ZIP_MAX_BYTES, `${id2} zip archive`);
|
|
40503
41419
|
const entries = await listArchiveEntries(zipPath);
|
|
@@ -40507,7 +41423,7 @@ async function fetchZippedXml(id2, url, pattern) {
|
|
|
40507
41423
|
const declaredSize = await archiveEntrySize(zipPath, selected);
|
|
40508
41424
|
if (declaredSize > XML_MAX_BYTES)
|
|
40509
41425
|
throw new Error(`${id2} xml exceeded the ${XML_MAX_BYTES}-byte expanded limit`);
|
|
40510
|
-
|
|
41426
|
+
mkdirSync11(staging, {
|
|
40511
41427
|
recursive: true
|
|
40512
41428
|
});
|
|
40513
41429
|
const extracted = await runCapturedProcess("unzip", ["-o", "-q", zipPath, selected, "-d", staging], {
|
|
@@ -40518,26 +41434,26 @@ async function fetchZippedXml(id2, url, pattern) {
|
|
|
40518
41434
|
throw new Error(`${id2} archive extraction timed out`);
|
|
40519
41435
|
if (extracted.exitCode !== 0)
|
|
40520
41436
|
throw new Error(extracted.stderr.trim() || `${id2} archive extraction failed`);
|
|
40521
|
-
const stagedPath =
|
|
41437
|
+
const stagedPath = resolve10(staging, selected);
|
|
40522
41438
|
const stagedRelative = relative10(staging, stagedPath);
|
|
40523
41439
|
if (stagedRelative.startsWith("..") || stagedRelative.startsWith("/"))
|
|
40524
41440
|
throw new Error(`${id2} archive entry escaped the extraction directory`);
|
|
40525
|
-
const info =
|
|
41441
|
+
const info = lstatSync10(stagedPath);
|
|
40526
41442
|
if (!info.isFile() || info.isSymbolicLink())
|
|
40527
41443
|
throw new Error(`${id2} archive entry was not a regular file`);
|
|
40528
41444
|
if (info.size > XML_MAX_BYTES || info.size !== declaredSize)
|
|
40529
41445
|
throw new Error(`${id2} archive entry size did not match its validated metadata`);
|
|
40530
|
-
const path =
|
|
40531
|
-
|
|
41446
|
+
const path = join30(dir, `${id2}.xml`);
|
|
41447
|
+
renameSync8(stagedPath, path);
|
|
40532
41448
|
return {
|
|
40533
41449
|
xml: readBoundedXml(path),
|
|
40534
41450
|
path
|
|
40535
41451
|
};
|
|
40536
41452
|
} finally {
|
|
40537
41453
|
try {
|
|
40538
|
-
|
|
41454
|
+
unlinkSync7(zipPath);
|
|
40539
41455
|
} catch {}
|
|
40540
|
-
|
|
41456
|
+
rmSync8(staging, {
|
|
40541
41457
|
recursive: true,
|
|
40542
41458
|
force: true
|
|
40543
41459
|
});
|
|
@@ -40595,7 +41511,7 @@ function readBoundedXml(path) {
|
|
|
40595
41511
|
}
|
|
40596
41512
|
var ZIP_MAX_BYTES, XML_MAX_BYTES, ZIP_LIST_MAX_BYTES, ZIP_ENTRY_MAX_COUNT = 4096, UNZIP_TIMEOUT_MS = 60000;
|
|
40597
41513
|
var init_zip_fetch = __esm(() => {
|
|
40598
|
-
|
|
41514
|
+
init_paths3();
|
|
40599
41515
|
init_http2();
|
|
40600
41516
|
init_captured_process();
|
|
40601
41517
|
init_file_read();
|
|
@@ -40898,7 +41814,7 @@ var init_command = __esm(() => {
|
|
|
40898
41814
|
init_pack();
|
|
40899
41815
|
init_taxonomy_pack();
|
|
40900
41816
|
init_store();
|
|
40901
|
-
|
|
41817
|
+
init_paths3();
|
|
40902
41818
|
init_hacktricks();
|
|
40903
41819
|
init_payloads();
|
|
40904
41820
|
init_attack();
|
|
@@ -40918,6 +41834,80 @@ var init_command = __esm(() => {
|
|
|
40918
41834
|
CORPUS_INGEST = new Set(["hacktricks", "payloads", ...TAXONOMY_INGEST]);
|
|
40919
41835
|
});
|
|
40920
41836
|
|
|
41837
|
+
// src/agent-content/preflight.ts
|
|
41838
|
+
var exports_preflight = {};
|
|
41839
|
+
__export(exports_preflight, {
|
|
41840
|
+
runStartupContentPreflight: () => runStartupContentPreflight
|
|
41841
|
+
});
|
|
41842
|
+
import { createInterface } from "readline";
|
|
41843
|
+
async function runStartupContentPreflight(workspace) {
|
|
41844
|
+
const status2 = await checkContentUpdate({
|
|
41845
|
+
workspace
|
|
41846
|
+
});
|
|
41847
|
+
if (status2.state !== "update_available" || !status2.manifest)
|
|
41848
|
+
return "continue";
|
|
41849
|
+
if (isContentVersionDismissed(status2.manifest.contentVersion))
|
|
41850
|
+
return "continue";
|
|
41851
|
+
const config = loadConfig(workspace);
|
|
41852
|
+
if (config.updates?.prompt === false || !process.stdin.isTTY || !process.stdout.isTTY)
|
|
41853
|
+
return "continue";
|
|
41854
|
+
const answer = await promptForUpdate(status2.manifest.contentVersion, Boolean(status2.manifest.knowledge), Boolean(status2.manifest.skills));
|
|
41855
|
+
if (answer === "cancelled")
|
|
41856
|
+
return "cancelled";
|
|
41857
|
+
if (answer === "dismiss") {
|
|
41858
|
+
dismissContentVersion(status2.manifest.contentVersion);
|
|
41859
|
+
return "continue";
|
|
41860
|
+
}
|
|
41861
|
+
if (answer === "later")
|
|
41862
|
+
return "continue";
|
|
41863
|
+
console.log("updating farai content...");
|
|
41864
|
+
try {
|
|
41865
|
+
const applied = await applyContentUpdate(status2.manifest, status2.manifestUrl);
|
|
41866
|
+
const parts = [applied.knowledge ? "knowledge" : undefined, applied.skills ? "skills" : undefined].filter(Boolean).join(" + ");
|
|
41867
|
+
console.log(`updated farai content to ${applied.version}${parts ? ` (${parts})` : ""}`);
|
|
41868
|
+
} catch (error) {
|
|
41869
|
+
console.error(`content update failed: ${errorMessage7(error)}`);
|
|
41870
|
+
console.error("starting farai with the current content");
|
|
41871
|
+
}
|
|
41872
|
+
return "continue";
|
|
41873
|
+
}
|
|
41874
|
+
async function promptForUpdate(version, knowledge, skills) {
|
|
41875
|
+
const contents = [knowledge ? "knowledge" : undefined, skills ? "skills" : undefined].filter(Boolean).join(" + ");
|
|
41876
|
+
console.log("");
|
|
41877
|
+
console.log(`farai content ${version} is available${contents ? ` (${contents})` : ""}`);
|
|
41878
|
+
const interfaceHandle = createInterface({
|
|
41879
|
+
input: process.stdin,
|
|
41880
|
+
output: process.stdout
|
|
41881
|
+
});
|
|
41882
|
+
return await new Promise((resolve11) => {
|
|
41883
|
+
let settled = false;
|
|
41884
|
+
const finish = (value) => {
|
|
41885
|
+
if (settled)
|
|
41886
|
+
return;
|
|
41887
|
+
settled = true;
|
|
41888
|
+
interfaceHandle.close();
|
|
41889
|
+
resolve11(value);
|
|
41890
|
+
};
|
|
41891
|
+
interfaceHandle.once("SIGINT", () => finish("cancelled"));
|
|
41892
|
+
interfaceHandle.question("update before starting? [enter=yes, n=later, d=skip version] ", (value) => {
|
|
41893
|
+
const normalized = value.trim().toLowerCase();
|
|
41894
|
+
if (normalized === "d" || normalized === "dismiss")
|
|
41895
|
+
finish("dismiss");
|
|
41896
|
+
else if (normalized === "n" || normalized === "no")
|
|
41897
|
+
finish("later");
|
|
41898
|
+
else
|
|
41899
|
+
finish("apply");
|
|
41900
|
+
});
|
|
41901
|
+
});
|
|
41902
|
+
}
|
|
41903
|
+
function errorMessage7(error) {
|
|
41904
|
+
return error instanceof Error ? error.message : String(error);
|
|
41905
|
+
}
|
|
41906
|
+
var init_preflight = __esm(() => {
|
|
41907
|
+
init_config();
|
|
41908
|
+
init_updater();
|
|
41909
|
+
});
|
|
41910
|
+
|
|
40921
41911
|
// node_modules/solid-js/dist/solid.js
|
|
40922
41912
|
function getContextId(count2) {
|
|
40923
41913
|
const num2 = String(count2), len = num2.length - 1;
|
|
@@ -49538,10 +50528,10 @@ function artifactRow(part, width) {
|
|
|
49538
50528
|
return row;
|
|
49539
50529
|
}
|
|
49540
50530
|
const note = extractObject(payload, "note");
|
|
49541
|
-
const
|
|
49542
|
-
const title = artifactTitle(kind, note,
|
|
49543
|
-
const detail = firstSemanticLine(stringField2(note, "text")) ?? stringField2(
|
|
49544
|
-
const body = artifactBody(payload, note,
|
|
50531
|
+
const artifact2 = extractObject(payload, "artifact") ?? extractObject(payload, "outputArtifact");
|
|
50532
|
+
const title = artifactTitle(kind, note, artifact2);
|
|
50533
|
+
const detail = firstSemanticLine(stringField2(note, "text")) ?? stringField2(artifact2, "path") ?? extractField(payload, "path") ?? extractField(payload, "title") ?? humanLabel(kind);
|
|
50534
|
+
const body = artifactBody(payload, note, artifact2);
|
|
49545
50535
|
return {
|
|
49546
50536
|
kind: "artifact",
|
|
49547
50537
|
title,
|
|
@@ -49552,8 +50542,8 @@ function artifactRow(part, width) {
|
|
|
49552
50542
|
id: part.id
|
|
49553
50543
|
};
|
|
49554
50544
|
}
|
|
49555
|
-
function artifactBody(payload, note,
|
|
49556
|
-
const candidates = [stringField2(note, "text"), stringField2(
|
|
50545
|
+
function artifactBody(payload, note, artifact2) {
|
|
50546
|
+
const candidates = [stringField2(note, "text"), stringField2(artifact2, "content"), stringField2(artifact2, "body"), stringField2(artifact2, "text"), stringField2(artifact2, "summary"), stringField2(artifact2, "output"), extractField(payload, "content"), extractField(payload, "body"), extractField(payload, "text"), extractField(payload, "summary"), extractField(payload, "output")];
|
|
49557
50547
|
return candidates.find((value) => Boolean(value?.trim()))?.trim();
|
|
49558
50548
|
}
|
|
49559
50549
|
function firstSemanticLine(value) {
|
|
@@ -49568,10 +50558,10 @@ function mcpInventoryRow(part, width) {
|
|
|
49568
50558
|
id: part.id
|
|
49569
50559
|
};
|
|
49570
50560
|
}
|
|
49571
|
-
function artifactTitle(kind, note,
|
|
50561
|
+
function artifactTitle(kind, note, artifact2) {
|
|
49572
50562
|
if (stringField2(note, "text"))
|
|
49573
50563
|
return "saved note";
|
|
49574
|
-
if (stringField2(
|
|
50564
|
+
if (stringField2(artifact2, "path"))
|
|
49575
50565
|
return "saved artifact";
|
|
49576
50566
|
return humanLabel(kind);
|
|
49577
50567
|
}
|
|
@@ -50147,7 +51137,7 @@ function createStoreResourceController(input) {
|
|
|
50147
51137
|
actions.servicesSet(services.value);
|
|
50148
51138
|
if (catalog.status === "fulfilled")
|
|
50149
51139
|
actions.mcpCatalogSet(catalog.value.servers, catalog.value.statuses);
|
|
50150
|
-
const errors = [refreshError, services.status === "rejected" ?
|
|
51140
|
+
const errors = [refreshError, services.status === "rejected" ? errorMessage8(services.reason) : undefined, catalog.status === "rejected" ? errorMessage8(catalog.reason) : undefined].filter((message) => Boolean(message));
|
|
50151
51141
|
actions.mcpStatusErrorSet(errors.length ? [...new Set(errors)].join(" \xB7 ") : undefined);
|
|
50152
51142
|
} finally {
|
|
50153
51143
|
if (mcpOverlayGeneration === generation && sessions2.owns(owner) && store.ui.statusDetail === "refreshing mcp") {
|
|
@@ -50169,7 +51159,7 @@ function createStoreResourceController(input) {
|
|
|
50169
51159
|
actions.emailCatalogSet(catalog.accounts);
|
|
50170
51160
|
} catch (error) {
|
|
50171
51161
|
if (emailOverlayGeneration === generation && sessions2.owns(owner))
|
|
50172
|
-
actions.errorSet(
|
|
51162
|
+
actions.errorSet(errorMessage8(error));
|
|
50173
51163
|
} finally {
|
|
50174
51164
|
if (emailOverlayGeneration === generation && sessions2.owns(owner) && store.ui.statusDetail === "loading email") {
|
|
50175
51165
|
input.setStatusDetail(undefined);
|
|
@@ -50215,7 +51205,7 @@ function createStoreResourceController(input) {
|
|
|
50215
51205
|
if (!sessions2.owns(owner))
|
|
50216
51206
|
return;
|
|
50217
51207
|
if (options.reportError !== false) {
|
|
50218
|
-
actions.errorSet(
|
|
51208
|
+
actions.errorSet(errorMessage8(error));
|
|
50219
51209
|
return;
|
|
50220
51210
|
}
|
|
50221
51211
|
throw error;
|
|
@@ -50258,7 +51248,7 @@ function containerState(imageExists, imageContractCurrent, persistentRunning, pe
|
|
|
50258
51248
|
return "missing";
|
|
50259
51249
|
return persistentRunning && persistentImageCurrent ? "running" : "stopped";
|
|
50260
51250
|
}
|
|
50261
|
-
function
|
|
51251
|
+
function errorMessage8(error) {
|
|
50262
51252
|
return error instanceof Error ? error.message : String(error);
|
|
50263
51253
|
}
|
|
50264
51254
|
|
|
@@ -50302,7 +51292,7 @@ function createStorePromptController(input) {
|
|
|
50302
51292
|
await port.prompt(sessionId, text2);
|
|
50303
51293
|
} catch (error) {
|
|
50304
51294
|
if (isActiveSession(sessionId))
|
|
50305
|
-
actions.errorSet(
|
|
51295
|
+
actions.errorSet(errorMessage9(error));
|
|
50306
51296
|
} finally {
|
|
50307
51297
|
if (submissions.get(sessionId) !== submission)
|
|
50308
51298
|
return;
|
|
@@ -50314,7 +51304,7 @@ function createStorePromptController(input) {
|
|
|
50314
51304
|
await sessions2.requestSnapshotRefresh(sessionId);
|
|
50315
51305
|
} catch (error) {
|
|
50316
51306
|
if (isActiveSession(sessionId))
|
|
50317
|
-
actions.errorSet(
|
|
51307
|
+
actions.errorSet(errorMessage9(error));
|
|
50318
51308
|
}
|
|
50319
51309
|
}
|
|
50320
51310
|
})();
|
|
@@ -50337,7 +51327,7 @@ function createStorePromptController(input) {
|
|
|
50337
51327
|
return submitted;
|
|
50338
51328
|
} catch (error) {
|
|
50339
51329
|
if (sessions2.owns(owner))
|
|
50340
|
-
actions.errorSet(
|
|
51330
|
+
actions.errorSet(errorMessage9(error));
|
|
50341
51331
|
return false;
|
|
50342
51332
|
} finally {
|
|
50343
51333
|
if (sessions2.owns(owner) && store.ui.statusDetail === status2)
|
|
@@ -50364,7 +51354,7 @@ function createStorePromptController(input) {
|
|
|
50364
51354
|
} catch (error) {
|
|
50365
51355
|
if (sessions2.owns(owner) && store.snapshot.pendingUserInput?.id === requestId) {
|
|
50366
51356
|
actions.requestUserInputSubmittingSet(false);
|
|
50367
|
-
actions.errorSet(
|
|
51357
|
+
actions.errorSet(errorMessage9(error));
|
|
50368
51358
|
}
|
|
50369
51359
|
return false;
|
|
50370
51360
|
}
|
|
@@ -50407,7 +51397,7 @@ function createStorePromptController(input) {
|
|
|
50407
51397
|
await sessions2.requestSnapshotRefresh(owner.sessionId);
|
|
50408
51398
|
} catch (error) {
|
|
50409
51399
|
if (sessions2.owns(owner) && store.snapshot.pendingUserInput?.id === requestId)
|
|
50410
|
-
actions.errorSet(
|
|
51400
|
+
actions.errorSet(errorMessage9(error));
|
|
50411
51401
|
}
|
|
50412
51402
|
}
|
|
50413
51403
|
function queuePrompt(text2) {
|
|
@@ -50425,7 +51415,7 @@ function createStorePromptController(input) {
|
|
|
50425
51415
|
return true;
|
|
50426
51416
|
} catch (error) {
|
|
50427
51417
|
if (owner && sessions2.owns(owner))
|
|
50428
|
-
actions.errorSet(
|
|
51418
|
+
actions.errorSet(errorMessage9(error));
|
|
50429
51419
|
return false;
|
|
50430
51420
|
}
|
|
50431
51421
|
}
|
|
@@ -50451,7 +51441,7 @@ function createStorePromptController(input) {
|
|
|
50451
51441
|
} catch (error) {
|
|
50452
51442
|
if (!sessions2.owns(owner))
|
|
50453
51443
|
return;
|
|
50454
|
-
const message =
|
|
51444
|
+
const message = errorMessage9(error);
|
|
50455
51445
|
if (!/abort|cancel/i.test(message))
|
|
50456
51446
|
actions.errorSet(message);
|
|
50457
51447
|
} finally {
|
|
@@ -50476,7 +51466,7 @@ function createStorePromptController(input) {
|
|
|
50476
51466
|
input.setStatusDetail("conversation cleared", 1500);
|
|
50477
51467
|
} catch (error) {
|
|
50478
51468
|
if (sessions2.owns(owner))
|
|
50479
|
-
actions.errorSet(
|
|
51469
|
+
actions.errorSet(errorMessage9(error));
|
|
50480
51470
|
}
|
|
50481
51471
|
}
|
|
50482
51472
|
async function cancelCurrentTurn() {
|
|
@@ -50499,7 +51489,7 @@ function createStorePromptController(input) {
|
|
|
50499
51489
|
await port.cancelTurn(turnId, "cancelled by user");
|
|
50500
51490
|
} catch (error) {
|
|
50501
51491
|
if (sessions2.owns(owner))
|
|
50502
|
-
actions.errorSet(
|
|
51492
|
+
actions.errorSet(errorMessage9(error));
|
|
50503
51493
|
}
|
|
50504
51494
|
if (!sessions2.owns(owner))
|
|
50505
51495
|
return;
|
|
@@ -50507,7 +51497,7 @@ function createStorePromptController(input) {
|
|
|
50507
51497
|
await sessions2.requestSnapshotRefresh(owner.sessionId);
|
|
50508
51498
|
} catch (error) {
|
|
50509
51499
|
if (sessions2.owns(owner))
|
|
50510
|
-
actions.errorSet(
|
|
51500
|
+
actions.errorSet(errorMessage9(error));
|
|
50511
51501
|
}
|
|
50512
51502
|
}
|
|
50513
51503
|
return {
|
|
@@ -50531,7 +51521,7 @@ function mergeQueuedPrompts(current, queued) {
|
|
|
50531
51521
|
return current;
|
|
50532
51522
|
return [...current, queued].sort((left, right) => left.sequence - right.sequence);
|
|
50533
51523
|
}
|
|
50534
|
-
function
|
|
51524
|
+
function errorMessage9(error) {
|
|
50535
51525
|
return error instanceof Error ? error.message : String(error);
|
|
50536
51526
|
}
|
|
50537
51527
|
var init_store_prompt_controller = __esm(() => {
|
|
@@ -54218,9 +55208,9 @@ function parseCommandArguments(value) {
|
|
|
54218
55208
|
|
|
54219
55209
|
// src/agent-tui/clipboard.ts
|
|
54220
55210
|
import { spawnSync } from "child_process";
|
|
54221
|
-
import { mkdtempSync, rmSync as
|
|
55211
|
+
import { mkdtempSync, rmSync as rmSync9, writeFileSync as writeFileSync3 } from "fs";
|
|
54222
55212
|
import { tmpdir as tmpdir2 } from "os";
|
|
54223
|
-
import { join as
|
|
55213
|
+
import { join as join31 } from "path";
|
|
54224
55214
|
function writeClipboard(text2) {
|
|
54225
55215
|
if (!text2)
|
|
54226
55216
|
return {
|
|
@@ -54272,8 +55262,8 @@ function runClipboardCommand(command, text2) {
|
|
|
54272
55262
|
};
|
|
54273
55263
|
}
|
|
54274
55264
|
function writeClipboardWithAppleScript(text2) {
|
|
54275
|
-
const dir = mkdtempSync(
|
|
54276
|
-
const path =
|
|
55265
|
+
const dir = mkdtempSync(join31(tmpdir2(), "farai-clipboard-"));
|
|
55266
|
+
const path = join31(dir, "clipboard.txt");
|
|
54277
55267
|
try {
|
|
54278
55268
|
writeFileSync3(path, text2, {
|
|
54279
55269
|
encoding: "utf8",
|
|
@@ -54297,7 +55287,7 @@ function writeClipboardWithAppleScript(text2) {
|
|
|
54297
55287
|
error: `osascript: ${detail}`
|
|
54298
55288
|
};
|
|
54299
55289
|
} finally {
|
|
54300
|
-
|
|
55290
|
+
rmSync9(dir, {
|
|
54301
55291
|
recursive: true,
|
|
54302
55292
|
force: true
|
|
54303
55293
|
});
|
|
@@ -54318,9 +55308,9 @@ function ctrlCDecision(text2, armedUntil, now = Date.now()) {
|
|
|
54318
55308
|
}
|
|
54319
55309
|
|
|
54320
55310
|
// src/agent-tui/input/composer-controller.ts
|
|
54321
|
-
import { mkdtempSync as mkdtempSync2, rmSync as
|
|
55311
|
+
import { mkdtempSync as mkdtempSync2, rmSync as rmSync10, writeFileSync as writeFileSync4 } from "fs";
|
|
54322
55312
|
import { tmpdir as tmpdir3 } from "os";
|
|
54323
|
-
import { join as
|
|
55313
|
+
import { join as join32 } from "path";
|
|
54324
55314
|
function createComposerController(input) {
|
|
54325
55315
|
const {
|
|
54326
55316
|
tui,
|
|
@@ -54588,8 +55578,8 @@ function createComposerController(input) {
|
|
|
54588
55578
|
});
|
|
54589
55579
|
return;
|
|
54590
55580
|
}
|
|
54591
|
-
const dir = mkdtempSync2(
|
|
54592
|
-
const file =
|
|
55581
|
+
const dir = mkdtempSync2(join32(tmpdir3(), "farai-editor-"));
|
|
55582
|
+
const file = join32(dir, "prompt.md");
|
|
54593
55583
|
writeFileSync4(file, composer.ref()?.plainText ?? composer.text(), {
|
|
54594
55584
|
encoding: "utf8",
|
|
54595
55585
|
mode: 384
|
|
@@ -54623,7 +55613,7 @@ function createComposerController(input) {
|
|
|
54623
55613
|
renderer.resume();
|
|
54624
55614
|
} catch {}
|
|
54625
55615
|
try {
|
|
54626
|
-
|
|
55616
|
+
rmSync10(dir, {
|
|
54627
55617
|
recursive: true,
|
|
54628
55618
|
force: true
|
|
54629
55619
|
});
|
|
@@ -57472,7 +58462,7 @@ function ToolResultContext(props) {
|
|
|
57472
58462
|
const evidence = () => result()?.evidence ?? [];
|
|
57473
58463
|
const attachments = () => result()?.attachments ?? [];
|
|
57474
58464
|
const metadata = () => result()?.metadata ?? {};
|
|
57475
|
-
const
|
|
58465
|
+
const artifact2 = () => {
|
|
57476
58466
|
const value = metadata().outputArtifact;
|
|
57477
58467
|
return value && typeof value === "object" && !Array.isArray(value) ? value : undefined;
|
|
57478
58468
|
};
|
|
@@ -57514,7 +58504,7 @@ function ToolResultContext(props) {
|
|
|
57514
58504
|
}), null);
|
|
57515
58505
|
insert(_el$31, createComponent2(Show, {
|
|
57516
58506
|
get when() {
|
|
57517
|
-
return result()?.outputArtifactId ||
|
|
58507
|
+
return result()?.outputArtifactId || artifact2();
|
|
57518
58508
|
},
|
|
57519
58509
|
get children() {
|
|
57520
58510
|
var _el$35 = createElement("box"), _el$36 = createElement("text");
|
|
@@ -57530,7 +58520,7 @@ function ToolResultContext(props) {
|
|
|
57530
58520
|
...result()?.outputArtifactId ? {
|
|
57531
58521
|
id: result()?.outputArtifactId
|
|
57532
58522
|
} : {},
|
|
57533
|
-
...
|
|
58523
|
+
...artifact2() ?? {}
|
|
57534
58524
|
});
|
|
57535
58525
|
},
|
|
57536
58526
|
children: (line) => (() => {
|
|
@@ -65904,7 +66894,7 @@ var init_app = __esm(() => {
|
|
|
65904
66894
|
});
|
|
65905
66895
|
|
|
65906
66896
|
// src/agent-tui/update-check.ts
|
|
65907
|
-
import { dirname as
|
|
66897
|
+
import { dirname as dirname11, join as join33 } from "path";
|
|
65908
66898
|
function prepareUpdateCheck(options = {}) {
|
|
65909
66899
|
if (updateCheckDisabled())
|
|
65910
66900
|
return {
|
|
@@ -65947,42 +66937,9 @@ function createUpdateNotice(currentVersion, latestVersion) {
|
|
|
65947
66937
|
updateCommand: "npm install -g farai@latest"
|
|
65948
66938
|
};
|
|
65949
66939
|
}
|
|
65950
|
-
function compareSemver(left, right) {
|
|
65951
|
-
const a = parseSemver(left);
|
|
65952
|
-
const b = parseSemver(right);
|
|
65953
|
-
if (!a || !b)
|
|
65954
|
-
return 0;
|
|
65955
|
-
for (let index = 0;index < 3; index += 1) {
|
|
65956
|
-
const delta = a.core[index] - b.core[index];
|
|
65957
|
-
if (delta !== 0)
|
|
65958
|
-
return delta < 0 ? -1 : 1;
|
|
65959
|
-
}
|
|
65960
|
-
if (a.prerelease.length === 0 || b.prerelease.length === 0) {
|
|
65961
|
-
if (a.prerelease.length === b.prerelease.length)
|
|
65962
|
-
return 0;
|
|
65963
|
-
return a.prerelease.length === 0 ? 1 : -1;
|
|
65964
|
-
}
|
|
65965
|
-
const length = Math.max(a.prerelease.length, b.prerelease.length);
|
|
65966
|
-
for (let index = 0;index < length; index += 1) {
|
|
65967
|
-
const aPart = a.prerelease[index];
|
|
65968
|
-
const bPart = b.prerelease[index];
|
|
65969
|
-
if (aPart === undefined || bPart === undefined)
|
|
65970
|
-
return aPart === undefined ? -1 : 1;
|
|
65971
|
-
if (aPart === bPart)
|
|
65972
|
-
continue;
|
|
65973
|
-
const aNumber = numericIdentifier(aPart);
|
|
65974
|
-
const bNumber = numericIdentifier(bPart);
|
|
65975
|
-
if (aNumber !== undefined && bNumber !== undefined)
|
|
65976
|
-
return aNumber < bNumber ? -1 : 1;
|
|
65977
|
-
if (aNumber !== undefined || bNumber !== undefined)
|
|
65978
|
-
return aNumber !== undefined ? -1 : 1;
|
|
65979
|
-
return aPart < bPart ? -1 : 1;
|
|
65980
|
-
}
|
|
65981
|
-
return 0;
|
|
65982
|
-
}
|
|
65983
66940
|
function readUpdateCache(path = updateCachePath()) {
|
|
65984
66941
|
try {
|
|
65985
|
-
ensurePrivateDirectory(
|
|
66942
|
+
ensurePrivateDirectory(dirname11(path), "update cache directory");
|
|
65986
66943
|
ensurePrivateRegularFileIfExists(path, "update cache");
|
|
65987
66944
|
const parsed = JSON.parse(readBoundedFileTextSyncNoFollow(path, UPDATE_RESPONSE_MAX_BYTES, "update cache"));
|
|
65988
66945
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
@@ -65990,7 +66947,7 @@ function readUpdateCache(path = updateCachePath()) {
|
|
|
65990
66947
|
const value = parsed;
|
|
65991
66948
|
if (typeof value.checkedAt !== "number" || !Number.isFinite(value.checkedAt))
|
|
65992
66949
|
return;
|
|
65993
|
-
if (typeof value.latestVersion !== "string" || !
|
|
66950
|
+
if (typeof value.latestVersion !== "string" || !isSemver(value.latestVersion))
|
|
65994
66951
|
return;
|
|
65995
66952
|
return {
|
|
65996
66953
|
checkedAt: value.checkedAt,
|
|
@@ -66001,13 +66958,13 @@ function readUpdateCache(path = updateCachePath()) {
|
|
|
66001
66958
|
}
|
|
66002
66959
|
}
|
|
66003
66960
|
function updateCachePath() {
|
|
66004
|
-
return
|
|
66961
|
+
return join33(globalDataDir(), "update.json");
|
|
66005
66962
|
}
|
|
66006
66963
|
function readCurrentVersion() {
|
|
66007
66964
|
try {
|
|
66008
|
-
const packagePath =
|
|
66965
|
+
const packagePath = join33(import.meta.dir, "..", "..", "package.json");
|
|
66009
66966
|
const parsed = JSON.parse(readBoundedFileTextSync(packagePath, 1024 * 1024, "package metadata"));
|
|
66010
|
-
return typeof parsed.version === "string" &&
|
|
66967
|
+
return typeof parsed.version === "string" && isSemver(parsed.version) ? parsed.version : undefined;
|
|
66011
66968
|
} catch {
|
|
66012
66969
|
return;
|
|
66013
66970
|
}
|
|
@@ -66043,7 +67000,7 @@ async function fetchLatestVersion(fetcher, timeoutMs) {
|
|
|
66043
67000
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
66044
67001
|
throw new Error("invalid npm registry response");
|
|
66045
67002
|
const version = parsed.version;
|
|
66046
|
-
if (typeof version !== "string" || !
|
|
67003
|
+
if (typeof version !== "string" || !isSemver(version))
|
|
66047
67004
|
throw new Error("invalid npm package version");
|
|
66048
67005
|
return version;
|
|
66049
67006
|
} finally {
|
|
@@ -66052,7 +67009,7 @@ async function fetchLatestVersion(fetcher, timeoutMs) {
|
|
|
66052
67009
|
}
|
|
66053
67010
|
function writeUpdateCache(path, cache) {
|
|
66054
67011
|
try {
|
|
66055
|
-
ensurePrivateDirectory(
|
|
67012
|
+
ensurePrivateDirectory(dirname11(path), "update cache directory");
|
|
66056
67013
|
ensurePrivateRegularFileIfExists(path, "update cache");
|
|
66057
67014
|
atomicWriteFile(path, `${JSON.stringify(cache)}
|
|
66058
67015
|
`, 384);
|
|
@@ -66068,20 +67025,6 @@ function updateCheckDisabled() {
|
|
|
66068
67025
|
function envEnabled(value) {
|
|
66069
67026
|
return value === "1" || value?.toLowerCase() === "true" || value?.toLowerCase() === "yes";
|
|
66070
67027
|
}
|
|
66071
|
-
function parseSemver(value) {
|
|
66072
|
-
const match = value.trim().match(/^(?:v)?(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/);
|
|
66073
|
-
if (!match)
|
|
66074
|
-
return;
|
|
66075
|
-
return {
|
|
66076
|
-
core: [Number(match[1]), Number(match[2]), Number(match[3])],
|
|
66077
|
-
prerelease: match[4]?.split(".") ?? []
|
|
66078
|
-
};
|
|
66079
|
-
}
|
|
66080
|
-
function numericIdentifier(value) {
|
|
66081
|
-
if (!/^(0|[1-9]\d*)$/.test(value))
|
|
66082
|
-
return;
|
|
66083
|
-
return Number(value);
|
|
66084
|
-
}
|
|
66085
67028
|
var UPDATE_CACHE_TTL_MS, UPDATE_CHECK_TIMEOUT_MS = 4000, UPDATE_REGISTRY_URL = "https://registry.npmjs.org/farai/latest", UPDATE_RESPONSE_MAX_BYTES;
|
|
66086
67029
|
var init_update_check = __esm(() => {
|
|
66087
67030
|
init_config();
|
|
@@ -66148,8 +67091,8 @@ async function runOpenTui(input) {
|
|
|
66148
67091
|
const renderer = managedRenderer.renderer;
|
|
66149
67092
|
const updateCheck = prepareUpdateCheck();
|
|
66150
67093
|
let done;
|
|
66151
|
-
const finished = new Promise((
|
|
66152
|
-
done =
|
|
67094
|
+
const finished = new Promise((resolve11) => {
|
|
67095
|
+
done = resolve11;
|
|
66153
67096
|
});
|
|
66154
67097
|
let exitPromise;
|
|
66155
67098
|
const onSigint = () => {
|
|
@@ -66390,6 +67333,80 @@ no sessions are available in this workspace`}`);
|
|
|
66390
67333
|
};
|
|
66391
67334
|
});
|
|
66392
67335
|
|
|
67336
|
+
// src/agent-content/command.ts
|
|
67337
|
+
var exports_command2 = {};
|
|
67338
|
+
__export(exports_command2, {
|
|
67339
|
+
runContentUpdateCommand: () => runContentUpdateCommand
|
|
67340
|
+
});
|
|
67341
|
+
import { existsSync as existsSync26 } from "fs";
|
|
67342
|
+
async function runContentUpdateCommand(parsed, workspace) {
|
|
67343
|
+
if (parsed.kind === "status") {
|
|
67344
|
+
const status3 = contentStatus();
|
|
67345
|
+
if (!status3.active) {
|
|
67346
|
+
const knowledge = legacyKnowledgeDbPath();
|
|
67347
|
+
console.log("content: bundled defaults");
|
|
67348
|
+
console.log("active release: none");
|
|
67349
|
+
console.log(`knowledge: ${existsSync26(knowledge) ? knowledge : "not installed"}`);
|
|
67350
|
+
console.log("skills: bundled");
|
|
67351
|
+
return 0;
|
|
67352
|
+
}
|
|
67353
|
+
console.log(`content: ${status3.active.version}`);
|
|
67354
|
+
console.log(`activated: ${status3.active.activatedAt}`);
|
|
67355
|
+
console.log(`knowledge: ${status3.knowledgePath ?? "local fallback"}`);
|
|
67356
|
+
console.log(`skills: ${status3.skillsPath ?? "bundled fallback"}`);
|
|
67357
|
+
console.log(`available versions: ${status3.versions.join(", ") || "none"}`);
|
|
67358
|
+
return 0;
|
|
67359
|
+
}
|
|
67360
|
+
if (parsed.kind === "rollback") {
|
|
67361
|
+
const result2 = rollbackContentUpdate();
|
|
67362
|
+
console.log(`rolled back farai content to ${result2.version}`);
|
|
67363
|
+
return 0;
|
|
67364
|
+
}
|
|
67365
|
+
const status2 = await checkContentUpdate({
|
|
67366
|
+
workspace,
|
|
67367
|
+
force: true
|
|
67368
|
+
});
|
|
67369
|
+
if (parsed.kind === "check")
|
|
67370
|
+
return printContentUpdateStatus(status2);
|
|
67371
|
+
if (status2.state === "up_to_date") {
|
|
67372
|
+
console.log(`farai content is up to date${status2.active ? ` (${status2.active.version})` : ""}`);
|
|
67373
|
+
return 0;
|
|
67374
|
+
}
|
|
67375
|
+
if (status2.state !== "update_available" || !status2.manifest)
|
|
67376
|
+
return printContentUpdateStatus(status2);
|
|
67377
|
+
const result = await applyContentUpdate(status2.manifest, status2.manifestUrl);
|
|
67378
|
+
console.log(`updated farai content to ${result.version}`);
|
|
67379
|
+
return 0;
|
|
67380
|
+
}
|
|
67381
|
+
function printContentUpdateStatus(status2) {
|
|
67382
|
+
if (status2.state === "update_available") {
|
|
67383
|
+
console.log(`content update available: ${status2.active?.version ?? "none"} -> ${status2.manifest?.contentVersion}`);
|
|
67384
|
+
return 0;
|
|
67385
|
+
}
|
|
67386
|
+
if (status2.state === "up_to_date") {
|
|
67387
|
+
console.log(`farai content is up to date${status2.active ? ` (${status2.active.version})` : ""}`);
|
|
67388
|
+
return 0;
|
|
67389
|
+
}
|
|
67390
|
+
if (status2.state === "disabled") {
|
|
67391
|
+
console.log("farai content updates are disabled");
|
|
67392
|
+
return 0;
|
|
67393
|
+
}
|
|
67394
|
+
if (status2.state === "unavailable") {
|
|
67395
|
+
console.log("the content channel has no published artifacts yet");
|
|
67396
|
+
return 0;
|
|
67397
|
+
}
|
|
67398
|
+
if (status2.state === "incompatible") {
|
|
67399
|
+
console.error(`content ${status2.manifest?.contentVersion} requires farai ${status2.manifest?.minFaraiVersion} or newer`);
|
|
67400
|
+
return 1;
|
|
67401
|
+
}
|
|
67402
|
+
console.error(`content update check failed: ${status2.error ?? "unknown error"}`);
|
|
67403
|
+
return 1;
|
|
67404
|
+
}
|
|
67405
|
+
var init_command2 = __esm(() => {
|
|
67406
|
+
init_paths3();
|
|
67407
|
+
init_updater();
|
|
67408
|
+
});
|
|
67409
|
+
|
|
66393
67410
|
// src/agent-benchmark/csi-cybench-33.ts
|
|
66394
67411
|
var figureTimeout = (minutes, line) => ({
|
|
66395
67412
|
status: "verified",
|
|
@@ -66651,40 +67668,40 @@ var init_csi_cybench_33 = __esm(() => {
|
|
|
66651
67668
|
});
|
|
66652
67669
|
|
|
66653
67670
|
// src/agent-benchmark/hash.ts
|
|
66654
|
-
import { createHash as
|
|
66655
|
-
import { closeSync as
|
|
66656
|
-
import { join as
|
|
67671
|
+
import { createHash as createHash13 } from "crypto";
|
|
67672
|
+
import { closeSync as closeSync7, constants as constants3, fstatSync as fstatSync3, lstatSync as lstatSync11, openSync as openSync7, readSync as readSync3, readdirSync as readdirSync12 } from "fs";
|
|
67673
|
+
import { join as join34 } from "path";
|
|
66657
67674
|
function stableStringify(value) {
|
|
66658
67675
|
return JSON.stringify(sortValue(value));
|
|
66659
67676
|
}
|
|
66660
67677
|
function sha256(value) {
|
|
66661
|
-
return
|
|
67678
|
+
return createHash13("sha256").update(value).digest("hex");
|
|
66662
67679
|
}
|
|
66663
67680
|
function hashPath(path) {
|
|
66664
|
-
const stat =
|
|
67681
|
+
const stat = lstatSync11(path);
|
|
66665
67682
|
if (stat.isFile())
|
|
66666
|
-
return
|
|
67683
|
+
return hashFile2(path);
|
|
66667
67684
|
if (!stat.isDirectory())
|
|
66668
67685
|
throw new Error(`unsupported benchmark input type: ${path}`);
|
|
66669
|
-
const hash =
|
|
67686
|
+
const hash = createHash13("sha256");
|
|
66670
67687
|
hash.update("farai-directory-v2\x00");
|
|
66671
67688
|
hashDirectory(path, Buffer.alloc(0), hash);
|
|
66672
67689
|
return hash.digest("hex");
|
|
66673
67690
|
}
|
|
66674
|
-
function
|
|
67691
|
+
function hashFile2(path) {
|
|
66675
67692
|
return hashFileDetails(path).digest;
|
|
66676
67693
|
}
|
|
66677
67694
|
function hashFileDetails(path) {
|
|
66678
|
-
const descriptor =
|
|
67695
|
+
const descriptor = openSync7(path, constants3.O_RDONLY | (constants3.O_NOFOLLOW ?? 0));
|
|
66679
67696
|
try {
|
|
66680
67697
|
const before = fstatSync3(descriptor);
|
|
66681
67698
|
if (!before.isFile())
|
|
66682
67699
|
throw new Error(`unsupported benchmark input type: ${path}`);
|
|
66683
|
-
const hash =
|
|
67700
|
+
const hash = createHash13("sha256");
|
|
66684
67701
|
let remaining2 = before.size;
|
|
66685
67702
|
while (remaining2 > 0) {
|
|
66686
67703
|
const chunk = Buffer.allocUnsafe(Math.min(1024 * 1024, remaining2));
|
|
66687
|
-
const count2 =
|
|
67704
|
+
const count2 = readSync3(descriptor, chunk, 0, chunk.length, null);
|
|
66688
67705
|
if (count2 === 0)
|
|
66689
67706
|
throw new Error(`benchmark input changed while hashing: ${path}`);
|
|
66690
67707
|
hash.update(chunk.subarray(0, count2));
|
|
@@ -66700,7 +67717,7 @@ function hashFileDetails(path) {
|
|
|
66700
67717
|
size: before.size
|
|
66701
67718
|
};
|
|
66702
67719
|
} finally {
|
|
66703
|
-
|
|
67720
|
+
closeSync7(descriptor);
|
|
66704
67721
|
}
|
|
66705
67722
|
}
|
|
66706
67723
|
function canonicalBenchmarkManifest(manifest) {
|
|
@@ -66755,22 +67772,22 @@ function sortValue(value) {
|
|
|
66755
67772
|
return Object.fromEntries(Object.entries(value).sort(([a], [b]) => a.localeCompare(b)).map(([key, item]) => [key, sortValue(item)]));
|
|
66756
67773
|
}
|
|
66757
67774
|
function hashDirectory(path, localPath, hash) {
|
|
66758
|
-
const before =
|
|
67775
|
+
const before = lstatSync11(path);
|
|
66759
67776
|
if (!before.isDirectory() || before.isSymbolicLink())
|
|
66760
67777
|
throw new Error(`unsupported benchmark input type: ${path}`);
|
|
66761
67778
|
hash.update("directory\x00");
|
|
66762
67779
|
hash.update(localPath);
|
|
66763
67780
|
hash.update(`\x00${before.mode & 511}\x00`);
|
|
66764
|
-
const names =
|
|
67781
|
+
const names = readdirSync12(path, {
|
|
66765
67782
|
encoding: "buffer"
|
|
66766
67783
|
}).map((name) => Buffer.from(name)).sort(Buffer.compare);
|
|
66767
67784
|
for (const encodedName of names) {
|
|
66768
67785
|
const name = encodedName.toString("utf8");
|
|
66769
67786
|
if (!Buffer.from(name, "utf8").equals(encodedName))
|
|
66770
67787
|
throw new Error(`benchmark input path is not valid utf-8: ${path}`);
|
|
66771
|
-
const childPath =
|
|
67788
|
+
const childPath = join34(path, name);
|
|
66772
67789
|
const childLocalPath = localPath.length === 0 ? encodedName : Buffer.concat([localPath, Buffer.from("/"), encodedName]);
|
|
66773
|
-
const stat =
|
|
67790
|
+
const stat = lstatSync11(childPath);
|
|
66774
67791
|
if (stat.isDirectory() && !stat.isSymbolicLink()) {
|
|
66775
67792
|
hashDirectory(childPath, childLocalPath, hash);
|
|
66776
67793
|
continue;
|
|
@@ -66782,7 +67799,7 @@ function hashDirectory(path, localPath, hash) {
|
|
|
66782
67799
|
hash.update(childLocalPath);
|
|
66783
67800
|
hash.update(`\x00${file.mode}\x00${file.size}\x00${file.digest}\x00`);
|
|
66784
67801
|
}
|
|
66785
|
-
const after =
|
|
67802
|
+
const after = lstatSync11(path);
|
|
66786
67803
|
if (!after.isDirectory() || after.dev !== before.dev || after.ino !== before.ino || after.mtimeMs !== before.mtimeMs || after.ctimeMs !== before.ctimeMs) {
|
|
66787
67804
|
throw new Error(`benchmark input changed while hashing: ${path}`);
|
|
66788
67805
|
}
|
|
@@ -66790,7 +67807,7 @@ function hashDirectory(path, localPath, hash) {
|
|
|
66790
67807
|
var init_hash = () => {};
|
|
66791
67808
|
|
|
66792
67809
|
// src/agent-benchmark/manifest.ts
|
|
66793
|
-
import { isAbsolute as
|
|
67810
|
+
import { isAbsolute as isAbsolute8, normalize as normalize3 } from "path";
|
|
66794
67811
|
async function loadBenchmarkManifest(path) {
|
|
66795
67812
|
return normalizeBenchmarkManifest(JSON.parse(await readBoundedFileText(path, BENCHMARK_MANIFEST_MAX_BYTES, "benchmark manifest")));
|
|
66796
67813
|
}
|
|
@@ -66829,27 +67846,27 @@ function normalizeBenchmarkManifest(value) {
|
|
|
66829
67846
|
id: requiredString(suite.id, "suite.id"),
|
|
66830
67847
|
version: requiredString(suite.version, "suite.version"),
|
|
66831
67848
|
source: requiredString(suite.source, "suite.source"),
|
|
66832
|
-
...
|
|
66833
|
-
sourceDigest:
|
|
67849
|
+
...optionalString3(suite.sourceDigest ?? suite.source_digest) ? {
|
|
67850
|
+
sourceDigest: optionalString3(suite.sourceDigest ?? suite.source_digest)
|
|
66834
67851
|
} : {}
|
|
66835
67852
|
},
|
|
66836
67853
|
challenge: {
|
|
66837
67854
|
id: requiredString(challenge.id, "challenge.id"),
|
|
66838
67855
|
prompt: requiredString(challenge.prompt, "challenge.prompt"),
|
|
66839
|
-
...
|
|
66840
|
-
category:
|
|
67856
|
+
...optionalString3(challenge.category) ? {
|
|
67857
|
+
category: optionalString3(challenge.category)
|
|
66841
67858
|
} : {},
|
|
66842
|
-
...
|
|
66843
|
-
difficulty:
|
|
67859
|
+
...optionalString3(challenge.difficulty) ? {
|
|
67860
|
+
difficulty: optionalString3(challenge.difficulty)
|
|
66844
67861
|
} : {},
|
|
66845
|
-
...
|
|
66846
|
-
source:
|
|
67862
|
+
...optionalString3(challenge.source) ? {
|
|
67863
|
+
source: optionalString3(challenge.source)
|
|
66847
67864
|
} : {},
|
|
66848
|
-
...
|
|
66849
|
-
targetImage:
|
|
67865
|
+
...optionalString3(challenge.targetImage ?? challenge.target_image) ? {
|
|
67866
|
+
targetImage: optionalString3(challenge.targetImage ?? challenge.target_image)
|
|
66850
67867
|
} : {},
|
|
66851
|
-
...
|
|
66852
|
-
targetImageDigest:
|
|
67868
|
+
...optionalString3(challenge.targetImageDigest ?? challenge.target_image_digest) ? {
|
|
67869
|
+
targetImageDigest: optionalString3(challenge.targetImageDigest ?? challenge.target_image_digest)
|
|
66853
67870
|
} : {},
|
|
66854
67871
|
...challenge.targetCommand ?? challenge.target_command ? {
|
|
66855
67872
|
targetCommand: stringArray3(challenge.targetCommand ?? challenge.target_command, "challenge.targetCommand")
|
|
@@ -66857,11 +67874,11 @@ function normalizeBenchmarkManifest(value) {
|
|
|
66857
67874
|
},
|
|
66858
67875
|
model: {
|
|
66859
67876
|
selection: requiredString(model.selection, "model.selection"),
|
|
66860
|
-
...
|
|
66861
|
-
provider:
|
|
67877
|
+
...optionalString3(model.provider) ? {
|
|
67878
|
+
provider: optionalString3(model.provider)
|
|
66862
67879
|
} : {},
|
|
66863
|
-
...
|
|
66864
|
-
protocol:
|
|
67880
|
+
...optionalString3(model.protocol) ? {
|
|
67881
|
+
protocol: optionalString3(model.protocol)
|
|
66865
67882
|
} : {},
|
|
66866
67883
|
...optionalPositiveNumber(model.contextWindow ?? model.context_window, "model.contextWindow") ? {
|
|
66867
67884
|
contextWindow: optionalPositiveNumber(model.contextWindow ?? model.context_window, "model.contextWindow")
|
|
@@ -66920,7 +67937,7 @@ function normalizeBenchmarkSuiteManifest(value) {
|
|
|
66920
67937
|
const id2 = requiredString(raw.id, "id");
|
|
66921
67938
|
const version = requiredString(raw.version, "version");
|
|
66922
67939
|
const source = requiredString(raw.source, "source");
|
|
66923
|
-
const sourceDigest =
|
|
67940
|
+
const sourceDigest = optionalString3(raw.sourceDigest ?? raw.source_digest);
|
|
66924
67941
|
const repetitions = positiveInteger4(raw.repetitions, "repetitions");
|
|
66925
67942
|
const concurrency = positiveInteger4(raw.concurrency, "concurrency");
|
|
66926
67943
|
const runs = raw.runs.map((entry, index) => {
|
|
@@ -66977,8 +67994,8 @@ function normalizeOracle(value) {
|
|
|
66977
67994
|
executableSha256: optionalSha256(raw.executableSha256 ?? raw.executable_sha256, "oracle.executableSha256")
|
|
66978
67995
|
} : {},
|
|
66979
67996
|
flagPattern: requiredString(raw.flagPattern ?? raw.flag_pattern, "oracle.flagPattern"),
|
|
66980
|
-
...
|
|
66981
|
-
flags:
|
|
67997
|
+
...optionalString3(raw.flags) ? {
|
|
67998
|
+
flags: optionalString3(raw.flags)
|
|
66982
67999
|
} : {},
|
|
66983
68000
|
...optionalPositiveNumber(raw.timeoutSeconds ?? raw.timeout_seconds, "oracle.timeoutSeconds") ? {
|
|
66984
68001
|
timeoutSeconds: optionalPositiveNumber(raw.timeoutSeconds ?? raw.timeout_seconds, "oracle.timeoutSeconds")
|
|
@@ -67034,7 +68051,7 @@ function optionalResource(raw, key, integer2, snake = key) {
|
|
|
67034
68051
|
};
|
|
67035
68052
|
}
|
|
67036
68053
|
function safeRelativePath(value) {
|
|
67037
|
-
if (
|
|
68054
|
+
if (isAbsolute8(value))
|
|
67038
68055
|
throw new Error(`benchmark destination must be relative: ${value}`);
|
|
67039
68056
|
const normalized = normalize3(value).replace(/\\/g, "/");
|
|
67040
68057
|
if (!normalized || normalized === "." || normalized.split("/").includes(".."))
|
|
@@ -67066,7 +68083,7 @@ function requiredString(value, name) {
|
|
|
67066
68083
|
throw new Error(`${name} must be a non-empty string`);
|
|
67067
68084
|
return value.trim();
|
|
67068
68085
|
}
|
|
67069
|
-
function
|
|
68086
|
+
function optionalString3(value) {
|
|
67070
68087
|
return typeof value === "string" && value.trim() ? value.trim() : undefined;
|
|
67071
68088
|
}
|
|
67072
68089
|
function optionalSha256(value, name) {
|
|
@@ -67113,7 +68130,7 @@ function optionalInteger(value, name, minimum) {
|
|
|
67113
68130
|
return number;
|
|
67114
68131
|
}
|
|
67115
68132
|
var BENCHMARK_MANIFEST_MAX_BYTES;
|
|
67116
|
-
var
|
|
68133
|
+
var init_manifest2 = __esm(() => {
|
|
67117
68134
|
init_file_read();
|
|
67118
68135
|
BENCHMARK_MANIFEST_MAX_BYTES = 32 * 1024 * 1024;
|
|
67119
68136
|
});
|
|
@@ -67128,17 +68145,17 @@ __export(exports_csi_suite, {
|
|
|
67128
68145
|
loadCsiCampaignConfig: () => loadCsiCampaignConfig,
|
|
67129
68146
|
generateCsiBenchmarkSuite: () => generateCsiBenchmarkSuite
|
|
67130
68147
|
});
|
|
67131
|
-
import { existsSync as
|
|
67132
|
-
import { dirname as
|
|
68148
|
+
import { existsSync as existsSync27, readdirSync as readdirSync13, statSync as statSync7 } from "fs";
|
|
68149
|
+
import { dirname as dirname12, isAbsolute as isAbsolute9, join as join35, relative as relative11, resolve as resolve11 } from "path";
|
|
67133
68150
|
async function loadCsiCampaignConfig(path) {
|
|
67134
68151
|
return normalizeCsiCampaignConfig(JSON.parse(await readBoundedFileText(path, CSI_CAMPAIGN_MAX_BYTES, "csi campaign config")));
|
|
67135
68152
|
}
|
|
67136
68153
|
async function loadCsiMaterialIndex(root) {
|
|
67137
|
-
return normalizeCsiMaterialIndex(JSON.parse(await readBoundedFileText(
|
|
68154
|
+
return normalizeCsiMaterialIndex(JSON.parse(await readBoundedFileText(resolve11(root, "index.json"), CSI_MATERIAL_INDEX_MAX_BYTES, "csi material index")));
|
|
67138
68155
|
}
|
|
67139
68156
|
async function generateCsiBenchmarkSuite(configInput, materialRoot) {
|
|
67140
68157
|
const config = normalizeCsiCampaignConfig(configInput);
|
|
67141
|
-
const root =
|
|
68158
|
+
const root = resolve11(materialRoot);
|
|
67142
68159
|
const materials = await loadCsiMaterialIndex(root);
|
|
67143
68160
|
const selectedIds = config.challenges ?? CSI_CYBENCH_33.challenges.map((challenge) => challenge.id);
|
|
67144
68161
|
const selected = selectedIds.map((id2) => {
|
|
@@ -67154,16 +68171,16 @@ async function generateCsiBenchmarkSuite(configInput, materialRoot) {
|
|
|
67154
68171
|
if (config.isolation.backend === "host" && material.requiresTarget)
|
|
67155
68172
|
throw new Error(`host csi challenge requires a live target and cannot run in host smoke mode: ${challenge.id}`);
|
|
67156
68173
|
const promptPath = protectedPath(root, material.promptFile, `${challenge.id}.promptFile`);
|
|
67157
|
-
if (!
|
|
68174
|
+
if (!existsSync27(promptPath) || !statSync7(promptPath).isFile())
|
|
67158
68175
|
throw new Error(`missing prompt file for csi challenge: ${challenge.id}`);
|
|
67159
68176
|
const prompt = readBoundedFileTextSync(promptPath, CSI_PROMPT_MAX_BYTES, `csi prompt ${challenge.id}`).trim();
|
|
67160
68177
|
if (!prompt)
|
|
67161
68178
|
throw new Error(`empty prompt file for csi challenge: ${challenge.id}`);
|
|
67162
68179
|
const files = material.files?.map((file, index) => {
|
|
67163
68180
|
const source = protectedPath(root, file.source, `${challenge.id}.files[${index}].source`);
|
|
67164
|
-
if (!
|
|
68181
|
+
if (!existsSync27(source))
|
|
67165
68182
|
throw new Error(`missing input for csi challenge ${challenge.id}: ${file.source}`);
|
|
67166
|
-
if (
|
|
68183
|
+
if (statSync7(source).isDirectory() && !listFiles(source).length)
|
|
67167
68184
|
throw new Error(`empty input directory for csi challenge ${challenge.id}: ${file.source}`);
|
|
67168
68185
|
const digest2 = hashPath(source);
|
|
67169
68186
|
if (file.sha256 && file.sha256.toLowerCase() !== digest2)
|
|
@@ -67183,14 +68200,14 @@ async function generateCsiBenchmarkSuite(configInput, materialRoot) {
|
|
|
67183
68200
|
throw new Error(`missing required protected files for csi challenge ${challenge.id}: ${missing.join(", ")}`);
|
|
67184
68201
|
}
|
|
67185
68202
|
const executable = protectedPath(root, material.oracle.executable, `${challenge.id}.oracle.executable`);
|
|
67186
|
-
if (!
|
|
68203
|
+
if (!existsSync27(executable) || !statSync7(executable).isFile())
|
|
67187
68204
|
throw new Error(`missing oracle executable for csi challenge: ${challenge.id}`);
|
|
67188
|
-
if ((
|
|
68205
|
+
if ((statSync7(executable).mode & 73) === 0)
|
|
67189
68206
|
throw new Error(`oracle executable is not executable for csi challenge: ${challenge.id}`);
|
|
67190
68207
|
const antiCheatExecutable = material.antiCheat ? protectedPath(root, material.antiCheat.executable, `${challenge.id}.antiCheat.executable`) : undefined;
|
|
67191
|
-
if (antiCheatExecutable && (!
|
|
68208
|
+
if (antiCheatExecutable && (!existsSync27(antiCheatExecutable) || !statSync7(antiCheatExecutable).isFile()))
|
|
67192
68209
|
throw new Error(`missing anti-cheat executable for csi challenge: ${challenge.id}`);
|
|
67193
|
-
if (antiCheatExecutable && (
|
|
68210
|
+
if (antiCheatExecutable && (statSync7(antiCheatExecutable).mode & 73) === 0)
|
|
67194
68211
|
throw new Error(`anti-cheat executable is not executable for csi challenge: ${challenge.id}`);
|
|
67195
68212
|
if (config.isolation.backend === "docker" && !material.target)
|
|
67196
68213
|
throw new Error(`docker csi challenge requires a pinned target image: ${challenge.id}`);
|
|
@@ -67263,8 +68280,8 @@ async function generateCsiBenchmarkSuite(configInput, materialRoot) {
|
|
|
67263
68280
|
});
|
|
67264
68281
|
}
|
|
67265
68282
|
function writeCsiBenchmarkSuite(suite, path) {
|
|
67266
|
-
const directory =
|
|
67267
|
-
if (!
|
|
68283
|
+
const directory = dirname12(resolve11(path));
|
|
68284
|
+
if (!existsSync27(directory))
|
|
67268
68285
|
throw new Error(`suite output directory does not exist: ${directory}`);
|
|
67269
68286
|
atomicWriteFile(path, `${JSON.stringify(suite, null, 2)}
|
|
67270
68287
|
`, 384);
|
|
@@ -67343,8 +68360,8 @@ function normalizeCsiMaterialIndex(value) {
|
|
|
67343
68360
|
args: stringArray4(oracle.args, `challenges.${id2}.oracle.args`, true)
|
|
67344
68361
|
},
|
|
67345
68362
|
flagPattern: requiredString2(oracle.flagPattern ?? oracle.flag_pattern, `challenges.${id2}.oracle.flagPattern`),
|
|
67346
|
-
...
|
|
67347
|
-
flags:
|
|
68363
|
+
...optionalString4(oracle.flags) ? {
|
|
68364
|
+
flags: optionalString4(oracle.flags)
|
|
67348
68365
|
} : {},
|
|
67349
68366
|
...oracle.timeoutSeconds ?? oracle.timeout_seconds ? {
|
|
67350
68367
|
timeoutSeconds: positiveNumber3(oracle.timeoutSeconds ?? oracle.timeout_seconds, `challenges.${id2}.oracle.timeoutSeconds`)
|
|
@@ -67390,18 +68407,18 @@ function resolveTimeoutMinutes(id2, catalog, material) {
|
|
|
67390
68407
|
return material.minutes;
|
|
67391
68408
|
}
|
|
67392
68409
|
function protectedPath(root, path, name) {
|
|
67393
|
-
if (
|
|
68410
|
+
if (isAbsolute9(path))
|
|
67394
68411
|
throw new Error(`${name} must be relative to the protected material root`);
|
|
67395
|
-
const resolved =
|
|
68412
|
+
const resolved = resolve11(root, path);
|
|
67396
68413
|
const difference = relative11(root, resolved);
|
|
67397
|
-
if (!difference || difference.startsWith("..") ||
|
|
68414
|
+
if (!difference || difference.startsWith("..") || isAbsolute9(difference))
|
|
67398
68415
|
throw new Error(`${name} escapes the protected material root`);
|
|
67399
68416
|
return resolved;
|
|
67400
68417
|
}
|
|
67401
68418
|
function listFiles(rootPath) {
|
|
67402
|
-
if (!
|
|
68419
|
+
if (!statSync7(rootPath).isDirectory())
|
|
67403
68420
|
return [rootPath];
|
|
67404
|
-
return
|
|
68421
|
+
return readdirSync13(rootPath).flatMap((name) => listFiles(join35(rootPath, name)));
|
|
67405
68422
|
}
|
|
67406
68423
|
function object2(value, name) {
|
|
67407
68424
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
@@ -67413,7 +68430,7 @@ function requiredString2(value, name) {
|
|
|
67413
68430
|
throw new Error(`${name} must be a non-empty string`);
|
|
67414
68431
|
return value.trim();
|
|
67415
68432
|
}
|
|
67416
|
-
function
|
|
68433
|
+
function optionalString4(value) {
|
|
67417
68434
|
return typeof value === "string" && value.trim() ? value.trim() : undefined;
|
|
67418
68435
|
}
|
|
67419
68436
|
function stringArray4(value, name, allowEmpty) {
|
|
@@ -67455,7 +68472,7 @@ var CSI_CAMPAIGN_MAX_BYTES, CSI_MATERIAL_INDEX_MAX_BYTES, CSI_PROMPT_MAX_BYTES;
|
|
|
67455
68472
|
var init_csi_suite = __esm(() => {
|
|
67456
68473
|
init_csi_cybench_33();
|
|
67457
68474
|
init_hash();
|
|
67458
|
-
|
|
68475
|
+
init_manifest2();
|
|
67459
68476
|
init_file_read();
|
|
67460
68477
|
init_atomic_file();
|
|
67461
68478
|
CSI_CAMPAIGN_MAX_BYTES = 4 * 1024 * 1024;
|
|
@@ -67464,28 +68481,28 @@ var init_csi_suite = __esm(() => {
|
|
|
67464
68481
|
});
|
|
67465
68482
|
|
|
67466
68483
|
// src/agent-benchmark/bundle.ts
|
|
67467
|
-
import { createHash as
|
|
67468
|
-
import { chmodSync as chmodSync3, mkdirSync as
|
|
67469
|
-
import { join as
|
|
68484
|
+
import { createHash as createHash14 } from "crypto";
|
|
68485
|
+
import { chmodSync as chmodSync3, mkdirSync as mkdirSync12 } from "fs";
|
|
68486
|
+
import { join as join36 } from "path";
|
|
67470
68487
|
function writeBenchmarkBundle(bundle, directory) {
|
|
67471
|
-
|
|
68488
|
+
mkdirSync12(directory, {
|
|
67472
68489
|
recursive: true
|
|
67473
68490
|
});
|
|
67474
68491
|
const files = new Map([["manifest.json", json(redactManifest(bundle.manifest))], ["result.json", json(bundle.result)], ["environment.json", json(bundle.result.frozen)], ["sessions.jsonl", jsonl(bundle.sessions)], ["turns.jsonl", jsonl(bundle.turns)], ["messages.jsonl", jsonl(bundle.messages)], ["events.jsonl", jsonl(bundle.events)], ["tool-calls.jsonl", jsonl(bundle.toolCalls)], ["jobs.jsonl", jsonl(bundle.jobs)], ["usage.jsonl", jsonl(bundle.usage)], ["compactions.jsonl", jsonl(bundle.compactions)], ["evidence.jsonl", jsonl(bundle.evidence)]]);
|
|
67475
68492
|
for (const [name, content] of files)
|
|
67476
|
-
atomicWriteFile(
|
|
68493
|
+
atomicWriteFile(join36(directory, name), content, 384);
|
|
67477
68494
|
const checksums = [...files.keys()].sort().map((name) => `${sha2562(files.get(name))} ${name}`).join(`
|
|
67478
68495
|
`);
|
|
67479
|
-
atomicWriteFile(
|
|
68496
|
+
atomicWriteFile(join36(directory, "checksums.sha256"), `${checksums}
|
|
67480
68497
|
`, 384);
|
|
67481
68498
|
for (const name of [...files.keys(), "checksums.sha256"])
|
|
67482
|
-
chmodSync3(
|
|
68499
|
+
chmodSync3(join36(directory, name), 292);
|
|
67483
68500
|
return directory;
|
|
67484
68501
|
}
|
|
67485
68502
|
function writeBenchmarkResult(result, path) {
|
|
67486
68503
|
const directory = path.slice(0, Math.max(path.lastIndexOf("/"), path.lastIndexOf("\\")));
|
|
67487
68504
|
if (directory)
|
|
67488
|
-
|
|
68505
|
+
mkdirSync12(directory, {
|
|
67489
68506
|
recursive: true
|
|
67490
68507
|
});
|
|
67491
68508
|
atomicWriteFile(path, json(result), 384);
|
|
@@ -67503,7 +68520,7 @@ function jsonl(values) {
|
|
|
67503
68520
|
` : "";
|
|
67504
68521
|
}
|
|
67505
68522
|
function sha2562(value) {
|
|
67506
|
-
return
|
|
68523
|
+
return createHash14("sha256").update(value).digest("hex");
|
|
67507
68524
|
}
|
|
67508
68525
|
var init_bundle = __esm(() => {
|
|
67509
68526
|
init_hash();
|
|
@@ -67511,8 +68528,8 @@ var init_bundle = __esm(() => {
|
|
|
67511
68528
|
});
|
|
67512
68529
|
|
|
67513
68530
|
// src/agent-benchmark/docker-lifecycle.ts
|
|
67514
|
-
import { existsSync as
|
|
67515
|
-
import { resolve as
|
|
68531
|
+
import { existsSync as existsSync28 } from "fs";
|
|
68532
|
+
import { resolve as resolve12 } from "path";
|
|
67516
68533
|
|
|
67517
68534
|
class BenchmarkDockerLifecycle {
|
|
67518
68535
|
constructor(manifest, workspace, runId, runner = runProcess2) {
|
|
@@ -67630,7 +68647,7 @@ function buildBenchmarkDockerPlan(manifest, workspace, runId, agentImageId) {
|
|
|
67630
68647
|
throw new Error("docker benchmark requires a pinned target image");
|
|
67631
68648
|
if (!manifest.antiCheat)
|
|
67632
68649
|
throw new Error("docker benchmark requires an external anti-cheat hook");
|
|
67633
|
-
if (!
|
|
68650
|
+
if (!existsSync28(manifest.antiCheat.executable))
|
|
67634
68651
|
throw new Error("anti-cheat executable is missing");
|
|
67635
68652
|
if (hashPath(manifest.antiCheat.executable) !== manifest.antiCheat.executableSha256)
|
|
67636
68653
|
throw new Error("anti-cheat executable hash mismatch");
|
|
@@ -67649,7 +68666,7 @@ function buildBenchmarkDockerPlan(manifest, workspace, runId, agentImageId) {
|
|
|
67649
68666
|
const resourceArgs = [...resources?.cpus ? ["--cpus", String(resources.cpus)] : [], ...resources?.memoryMb ? ["--memory", `${resources.memoryMb}m`] : []];
|
|
67650
68667
|
const targetImage = pinnedImage(manifest.challenge.targetImage, manifest.challenge.targetImageDigest);
|
|
67651
68668
|
const targetStart = ["run", "-d", "--name", names.target, "--network", names.network, "--network-alias", "target", "--cap-drop", "ALL", "--cap-add", "NET_BIND_SERVICE", ...common, ...resourceArgs, targetImage, ...manifest.challenge.targetCommand ?? []];
|
|
67652
|
-
const resolvedWorkspace =
|
|
68669
|
+
const resolvedWorkspace = resolve12(workspace);
|
|
67653
68670
|
const agentStart = ["run", "-d", "--name", names.agent, "--network", names.network, "--workdir", "/workspace", "--volume", `${resolvedWorkspace}:/workspace:rw`, "--volume", "/workspace/.farai", "--read-only", "--tmpfs", "/tmp:rw,nosuid,nodev,size=512m", "--tmpfs", "/root:rw,nosuid,nodev,size=256m", "--tmpfs", "/run:rw,nosuid,nodev,size=64m", "--cap-drop", "ALL", "--cap-add", "NET_ADMIN", "--cap-add", "NET_RAW", ...common, ...resourceArgs, agentImageId, "sleep", "infinity"];
|
|
67654
68671
|
return {
|
|
67655
68672
|
names,
|
|
@@ -67699,9 +68716,9 @@ var init_docker_lifecycle = __esm(() => {
|
|
|
67699
68716
|
});
|
|
67700
68717
|
|
|
67701
68718
|
// src/agent-benchmark/git-state.ts
|
|
67702
|
-
import { createHash as
|
|
67703
|
-
import { lstatSync as
|
|
67704
|
-
import { isAbsolute as
|
|
68719
|
+
import { createHash as createHash15 } from "crypto";
|
|
68720
|
+
import { lstatSync as lstatSync12, readlinkSync } from "fs";
|
|
68721
|
+
import { isAbsolute as isAbsolute10, relative as relative12, resolve as resolve13 } from "path";
|
|
67705
68722
|
import { spawn as spawn5 } from "child_process";
|
|
67706
68723
|
async function freezeGitSourceState(root) {
|
|
67707
68724
|
if (!await isGitWorktree(root))
|
|
@@ -67738,7 +68755,7 @@ async function readGitCommit(root) {
|
|
|
67738
68755
|
return commit.toLowerCase();
|
|
67739
68756
|
}
|
|
67740
68757
|
async function hashGitWorktree(root, hasCommit) {
|
|
67741
|
-
const hash =
|
|
68758
|
+
const hash = createHash15("sha256");
|
|
67742
68759
|
hash.update("farai-git-worktree-v2\x00");
|
|
67743
68760
|
await hashCommand(root, ["status", "--porcelain=v1", "-z", "--untracked-files=no", "--ignore-submodules=none"], hash, "status");
|
|
67744
68761
|
await hashCommand(root, ["diff", "--no-ext-diff", "--no-textconv", "--binary", "--full-index", "--submodule=diff", "--"], hash, "unstaged");
|
|
@@ -67780,17 +68797,17 @@ function hashUntrackedPath(root, encodedPath, hash) {
|
|
|
67780
68797
|
const path = encodedPath.toString("utf8");
|
|
67781
68798
|
if (!Buffer.from(path, "utf8").equals(encodedPath))
|
|
67782
68799
|
throw new Error("git returned an untracked path that is not valid utf-8");
|
|
67783
|
-
const absolute =
|
|
67784
|
-
const local = relative12(
|
|
67785
|
-
if (!path ||
|
|
68800
|
+
const absolute = resolve13(root, path);
|
|
68801
|
+
const local = relative12(resolve13(root), absolute);
|
|
68802
|
+
if (!path || isAbsolute10(path) || local === ".." || local.startsWith(`..${process.platform === "win32" ? "\\" : "/"}`) || isAbsolute10(local)) {
|
|
67786
68803
|
throw new Error(`git returned an unsafe untracked path: ${path}`);
|
|
67787
68804
|
}
|
|
67788
|
-
const stat =
|
|
68805
|
+
const stat = lstatSync12(absolute);
|
|
67789
68806
|
hash.update("path\x00");
|
|
67790
68807
|
hash.update(encodedPath);
|
|
67791
68808
|
hash.update("\x00");
|
|
67792
68809
|
if (stat.isFile()) {
|
|
67793
|
-
hash.update(`file\x00${stat.mode & 511}\x00${stat.size}\x00${
|
|
68810
|
+
hash.update(`file\x00${stat.mode & 511}\x00${stat.size}\x00${hashFile2(absolute)}\x00`);
|
|
67794
68811
|
return;
|
|
67795
68812
|
}
|
|
67796
68813
|
if (stat.isSymbolicLink()) {
|
|
@@ -67891,27 +68908,27 @@ __export(exports_runner, {
|
|
|
67891
68908
|
normalizeBenchmarkManifest: () => normalizeBenchmarkManifest,
|
|
67892
68909
|
loadBenchmarkManifest: () => loadBenchmarkManifest
|
|
67893
68910
|
});
|
|
67894
|
-
import { cpSync, existsSync as
|
|
68911
|
+
import { cpSync, existsSync as existsSync29, lstatSync as lstatSync13, mkdirSync as mkdirSync13, mkdtempSync as mkdtempSync3, readdirSync as readdirSync14, renameSync as renameSync9, rmSync as rmSync11, rmdirSync } from "fs";
|
|
67895
68912
|
import { arch, platform, tmpdir as tmpdir4 } from "os";
|
|
67896
|
-
import { dirname as
|
|
67897
|
-
import { randomUUID as
|
|
68913
|
+
import { dirname as dirname13, isAbsolute as isAbsolute11, join as join37, relative as relative13, resolve as resolve14 } from "path";
|
|
68914
|
+
import { randomUUID as randomUUID11 } from "crypto";
|
|
67898
68915
|
async function runBenchmark(input, options = {}) {
|
|
67899
68916
|
const manifest = normalizeBenchmarkManifest(input);
|
|
67900
68917
|
assertExecutableIsolation(manifest);
|
|
67901
|
-
const workspace = options.workspace ?? mkdtempSync3(
|
|
67902
|
-
const artifactsRoot = options.artifactsDir ?? mkdtempSync3(
|
|
68918
|
+
const workspace = options.workspace ?? mkdtempSync3(join37(tmpdir4(), "farai-benchmark-"));
|
|
68919
|
+
const artifactsRoot = options.artifactsDir ?? mkdtempSync3(join37(tmpdir4(), "farai-benchmark-artifacts-"));
|
|
67903
68920
|
const repetition = options.repetition ?? 1;
|
|
67904
|
-
|
|
68921
|
+
mkdirSync13(workspace, {
|
|
67905
68922
|
recursive: true
|
|
67906
68923
|
});
|
|
67907
68924
|
assertCleanWorkspace(workspace);
|
|
67908
68925
|
assertArtifactsOutsideWorkspace(workspace, artifactsRoot);
|
|
67909
|
-
|
|
68926
|
+
mkdirSync13(artifactsRoot, {
|
|
67910
68927
|
recursive: true
|
|
67911
68928
|
});
|
|
67912
68929
|
stageFiles(manifest, workspace);
|
|
67913
68930
|
const runId = id();
|
|
67914
|
-
const bundlePath =
|
|
68931
|
+
const bundlePath = join37(artifactsRoot, `${safeName2(manifest.challenge.id)}-r${repetition}-${runId}`);
|
|
67915
68932
|
const provider = options.provider ?? await createChatProviderForSession(syntheticSession(workspace, manifest));
|
|
67916
68933
|
assertProvider(manifest, provider);
|
|
67917
68934
|
const dockerLifecycle = manifest.isolation.backend === "docker" ? new BenchmarkDockerLifecycle(manifest, workspace, runId, options.dockerProcessRunner) : undefined;
|
|
@@ -67955,7 +68972,7 @@ async function runBenchmark(input, options = {}) {
|
|
|
67955
68972
|
session = runtime.updateSession(session.id, {
|
|
67956
68973
|
toolScope: activeTools.map((tool) => tool.name)
|
|
67957
68974
|
});
|
|
67958
|
-
const faraiRoot = options.faraiRoot ??
|
|
68975
|
+
const faraiRoot = options.faraiRoot ?? resolve14(import.meta.dir, "..", "..");
|
|
67959
68976
|
const frozen = await freezeRun(manifest, session, activeTools, faraiRoot, provider, dockerState?.agentImageId);
|
|
67960
68977
|
const promptPromise = runtime.prompt(session, manifest.challenge.prompt).then((result2) => {
|
|
67961
68978
|
response = result2.response;
|
|
@@ -68086,13 +69103,13 @@ function assertExecutableIsolation(manifest) {
|
|
|
68086
69103
|
}
|
|
68087
69104
|
}
|
|
68088
69105
|
function assertCleanWorkspace(workspace) {
|
|
68089
|
-
const entries =
|
|
69106
|
+
const entries = readdirSync14(workspace);
|
|
68090
69107
|
if (entries.length)
|
|
68091
69108
|
throw new Error(`benchmark workspace must be an empty scratch directory: ${workspace}`);
|
|
68092
69109
|
}
|
|
68093
69110
|
function assertArtifactsOutsideWorkspace(workspace, artifactsRoot) {
|
|
68094
|
-
const difference = relative13(
|
|
68095
|
-
const reverse = relative13(
|
|
69111
|
+
const difference = relative13(resolve14(workspace), resolve14(artifactsRoot));
|
|
69112
|
+
const reverse = relative13(resolve14(artifactsRoot), resolve14(workspace));
|
|
68096
69113
|
if (!difference || !difference.startsWith("..") || !reverse.startsWith("..")) {
|
|
68097
69114
|
throw new Error("benchmark artifacts directory must be outside the scratch workspace");
|
|
68098
69115
|
}
|
|
@@ -68189,18 +69206,18 @@ function collectRunData(runtime, rootSessionId) {
|
|
|
68189
69206
|
};
|
|
68190
69207
|
}
|
|
68191
69208
|
function stageFiles(manifest, workspace) {
|
|
68192
|
-
const root =
|
|
68193
|
-
const workspaceStat =
|
|
69209
|
+
const root = resolve14(workspace);
|
|
69210
|
+
const workspaceStat = lstatSync13(root);
|
|
68194
69211
|
if (!workspaceStat.isDirectory() || workspaceStat.isSymbolicLink())
|
|
68195
69212
|
throw new Error("benchmark workspace must be a real directory");
|
|
68196
69213
|
const inputs = (manifest.files ?? []).map((file) => {
|
|
68197
|
-
const source =
|
|
68198
|
-
if (!
|
|
69214
|
+
const source = resolve14(file.source);
|
|
69215
|
+
if (!existsSync29(source))
|
|
68199
69216
|
throw new Error(`benchmark input does not exist: ${file.source}`);
|
|
68200
69217
|
const expectedHash = file.sha256?.toLowerCase() ?? hashPath(source);
|
|
68201
69218
|
if (hashPath(source) !== expectedHash)
|
|
68202
69219
|
throw new Error(`benchmark input hash mismatch: ${file.source}`);
|
|
68203
|
-
const target =
|
|
69220
|
+
const target = resolve14(root, file.destination);
|
|
68204
69221
|
if (!isDescendantPath(relative13(root, target)))
|
|
68205
69222
|
throw new Error(`benchmark destination escapes scratch workspace: ${file.destination}`);
|
|
68206
69223
|
return {
|
|
@@ -68222,10 +69239,10 @@ function stageFiles(manifest, workspace) {
|
|
|
68222
69239
|
const createdDirectories = [];
|
|
68223
69240
|
try {
|
|
68224
69241
|
for (const input of inputs) {
|
|
68225
|
-
ensureStagingParent(root,
|
|
68226
|
-
const staged =
|
|
69242
|
+
ensureStagingParent(root, dirname13(input.target), createdDirectories);
|
|
69243
|
+
const staged = join37(dirname13(input.target), `.${randomUUID11()}.farai-stage`);
|
|
68227
69244
|
try {
|
|
68228
|
-
const sourceStat =
|
|
69245
|
+
const sourceStat = lstatSync13(input.source);
|
|
68229
69246
|
cpSync(input.source, staged, {
|
|
68230
69247
|
recursive: sourceStat.isDirectory(),
|
|
68231
69248
|
dereference: false,
|
|
@@ -68237,14 +69254,14 @@ function stageFiles(manifest, workspace) {
|
|
|
68237
69254
|
throw new Error(`benchmark input was not staged: ${input.file.destination}`);
|
|
68238
69255
|
if (hashPath(staged) !== input.expectedHash)
|
|
68239
69256
|
throw new Error(`benchmark input changed while staging: ${input.file.source}`);
|
|
68240
|
-
if (
|
|
69257
|
+
if (lstatSync13(staged).isDirectory() && !listFiles2(staged).length)
|
|
68241
69258
|
throw new Error(`benchmark input staged an empty directory: ${input.file.destination}`);
|
|
68242
69259
|
if (pathExists(input.target))
|
|
68243
69260
|
throw new Error(`benchmark destinations must not overlap: ${input.file.destination}`);
|
|
68244
|
-
|
|
69261
|
+
renameSync9(staged, input.target);
|
|
68245
69262
|
published.push(input.target);
|
|
68246
69263
|
} finally {
|
|
68247
|
-
|
|
69264
|
+
rmSync11(staged, {
|
|
68248
69265
|
recursive: true,
|
|
68249
69266
|
force: true
|
|
68250
69267
|
});
|
|
@@ -68252,7 +69269,7 @@ function stageFiles(manifest, workspace) {
|
|
|
68252
69269
|
}
|
|
68253
69270
|
} catch (error) {
|
|
68254
69271
|
for (const target of published.reverse())
|
|
68255
|
-
|
|
69272
|
+
rmSync11(target, {
|
|
68256
69273
|
recursive: true,
|
|
68257
69274
|
force: true
|
|
68258
69275
|
});
|
|
@@ -68272,42 +69289,42 @@ function pathsOverlap(left, right) {
|
|
|
68272
69289
|
return isDescendantPath(leftToRight) || isDescendantPath(rightToLeft);
|
|
68273
69290
|
}
|
|
68274
69291
|
function isDescendantPath(value) {
|
|
68275
|
-
return Boolean(value) && value !== ".." && !value.startsWith(`..${process.platform === "win32" ? "\\" : "/"}`) && !
|
|
69292
|
+
return Boolean(value) && value !== ".." && !value.startsWith(`..${process.platform === "win32" ? "\\" : "/"}`) && !isAbsolute11(value);
|
|
68276
69293
|
}
|
|
68277
69294
|
function listFiles2(rootPath) {
|
|
68278
|
-
if (!
|
|
69295
|
+
if (!existsSync29(rootPath))
|
|
68279
69296
|
return [];
|
|
68280
|
-
const stat =
|
|
69297
|
+
const stat = lstatSync13(rootPath);
|
|
68281
69298
|
if (stat.isFile())
|
|
68282
69299
|
return [rootPath];
|
|
68283
69300
|
if (!stat.isDirectory())
|
|
68284
69301
|
throw new Error(`unsupported staged benchmark input type: ${rootPath}`);
|
|
68285
|
-
return
|
|
69302
|
+
return readdirSync14(rootPath).flatMap((name) => listFiles2(join37(rootPath, name)));
|
|
68286
69303
|
}
|
|
68287
69304
|
function ensureStagingParent(workspace, parent, createdDirectories) {
|
|
68288
|
-
const root =
|
|
68289
|
-
const local = relative13(root,
|
|
69305
|
+
const root = resolve14(workspace);
|
|
69306
|
+
const local = relative13(root, resolve14(parent));
|
|
68290
69307
|
if (local === "")
|
|
68291
69308
|
return;
|
|
68292
69309
|
if (!isDescendantPath(local))
|
|
68293
69310
|
throw new Error("benchmark staging parent escapes scratch workspace");
|
|
68294
69311
|
let current = root;
|
|
68295
69312
|
for (const segment of local.split(/[\\/]+/).filter(Boolean)) {
|
|
68296
|
-
current =
|
|
69313
|
+
current = join37(current, segment);
|
|
68297
69314
|
if (!pathExists(current)) {
|
|
68298
|
-
|
|
69315
|
+
mkdirSync13(current, {
|
|
68299
69316
|
mode: 448
|
|
68300
69317
|
});
|
|
68301
69318
|
createdDirectories.push(current);
|
|
68302
69319
|
}
|
|
68303
|
-
const stat =
|
|
69320
|
+
const stat = lstatSync13(current);
|
|
68304
69321
|
if (!stat.isDirectory() || stat.isSymbolicLink())
|
|
68305
69322
|
throw new Error(`benchmark staging parent must be a real directory: ${current}`);
|
|
68306
69323
|
}
|
|
68307
69324
|
}
|
|
68308
69325
|
function pathExists(path) {
|
|
68309
69326
|
try {
|
|
68310
|
-
|
|
69327
|
+
lstatSync13(path);
|
|
68311
69328
|
return true;
|
|
68312
69329
|
} catch (error) {
|
|
68313
69330
|
if (error && typeof error === "object" && "code" in error && error.code === "ENOENT")
|
|
@@ -68357,7 +69374,7 @@ class BenchmarkHostBackend {
|
|
|
68357
69374
|
hostPath(path) {
|
|
68358
69375
|
if (path === "/workspace")
|
|
68359
69376
|
return this.workspace;
|
|
68360
|
-
return
|
|
69377
|
+
return join37(this.workspace, path.slice("/workspace/".length));
|
|
68361
69378
|
}
|
|
68362
69379
|
}
|
|
68363
69380
|
async function freezeRun(manifest, session, tools, faraiRoot, provider, kaliImageId) {
|
|
@@ -68505,7 +69522,7 @@ async function validateCandidates(manifest, sources, workspace) {
|
|
|
68505
69522
|
}
|
|
68506
69523
|
async function runOracle(oracle, candidate2, workspace, challengeId) {
|
|
68507
69524
|
if (oracle.executableSha256) {
|
|
68508
|
-
if (!
|
|
69525
|
+
if (!existsSync29(oracle.command[0]))
|
|
68509
69526
|
return {
|
|
68510
69527
|
ok: false,
|
|
68511
69528
|
error: "oracle executable missing"
|
|
@@ -68602,7 +69619,7 @@ var init_runner = __esm(() => {
|
|
|
68602
69619
|
init_docker_lifecycle();
|
|
68603
69620
|
init_hash();
|
|
68604
69621
|
init_git_state();
|
|
68605
|
-
|
|
69622
|
+
init_manifest2();
|
|
68606
69623
|
});
|
|
68607
69624
|
|
|
68608
69625
|
// src/agent-benchmark/suite.ts
|
|
@@ -68613,16 +69630,16 @@ __export(exports_suite, {
|
|
|
68613
69630
|
normalizeBenchmarkSuiteManifest: () => normalizeBenchmarkSuiteManifest,
|
|
68614
69631
|
loadBenchmarkSuiteManifest: () => loadBenchmarkSuiteManifest
|
|
68615
69632
|
});
|
|
68616
|
-
import { mkdirSync as
|
|
69633
|
+
import { mkdirSync as mkdirSync14, mkdtempSync as mkdtempSync4 } from "fs";
|
|
68617
69634
|
import { tmpdir as tmpdir5 } from "os";
|
|
68618
|
-
import { join as
|
|
69635
|
+
import { join as join38 } from "path";
|
|
68619
69636
|
async function runBenchmarkSuite(input, options = {}) {
|
|
68620
69637
|
const manifest = normalizeBenchmarkSuiteManifest(input);
|
|
68621
69638
|
const campaignId = id();
|
|
68622
|
-
const root = options.artifactsDir ?? mkdtempSync4(
|
|
68623
|
-
const bundlePath =
|
|
68624
|
-
const runsPath =
|
|
68625
|
-
|
|
69639
|
+
const root = options.artifactsDir ?? mkdtempSync4(join38(tmpdir5(), "farai-benchmark-campaign-"));
|
|
69640
|
+
const bundlePath = join38(root, `${safeName3(manifest.id)}-${campaignId}`);
|
|
69641
|
+
const runsPath = join38(bundlePath, "runs");
|
|
69642
|
+
mkdirSync14(runsPath, {
|
|
68626
69643
|
recursive: true
|
|
68627
69644
|
});
|
|
68628
69645
|
const attempts = [];
|
|
@@ -68715,9 +69732,9 @@ async function runBenchmarkSuite(input, options = {}) {
|
|
|
68715
69732
|
error: outcome.error
|
|
68716
69733
|
})
|
|
68717
69734
|
};
|
|
68718
|
-
atomicWriteFile(
|
|
69735
|
+
atomicWriteFile(join38(bundlePath, "campaign.json"), `${JSON.stringify(result, null, 2)}
|
|
68719
69736
|
`, 384);
|
|
68720
|
-
atomicWriteFile(
|
|
69737
|
+
atomicWriteFile(join38(bundlePath, "suite.sha256"), `${result.manifestHash}
|
|
68721
69738
|
`, 384);
|
|
68722
69739
|
return result;
|
|
68723
69740
|
}
|
|
@@ -68758,7 +69775,7 @@ function safeName3(value) {
|
|
|
68758
69775
|
var init_suite = __esm(() => {
|
|
68759
69776
|
init_atomic_file();
|
|
68760
69777
|
init_hash();
|
|
68761
|
-
|
|
69778
|
+
init_manifest2();
|
|
68762
69779
|
init_runner();
|
|
68763
69780
|
});
|
|
68764
69781
|
|
|
@@ -68773,6 +69790,7 @@ init_global_config();
|
|
|
68773
69790
|
init_config();
|
|
68774
69791
|
init_branding();
|
|
68775
69792
|
init_version();
|
|
69793
|
+
init_session_catalog();
|
|
68776
69794
|
|
|
68777
69795
|
// src/cli/command-arguments.ts
|
|
68778
69796
|
init_model_provider_validation();
|
|
@@ -68781,6 +69799,16 @@ function parseNoArguments(command, args) {
|
|
|
68781
69799
|
if (args.length > 0)
|
|
68782
69800
|
throw new Error(`${command} does not accept arguments`);
|
|
68783
69801
|
}
|
|
69802
|
+
function parseUpdateArguments(args) {
|
|
69803
|
+
const subcommand = args[0] ?? "status";
|
|
69804
|
+
if (subcommand !== "check" && subcommand !== "apply" && subcommand !== "status" && subcommand !== "rollback") {
|
|
69805
|
+
throw new Error(`unknown update command: ${subcommand}`);
|
|
69806
|
+
}
|
|
69807
|
+
parseNoArguments(`update ${subcommand}`, args.slice(1));
|
|
69808
|
+
return {
|
|
69809
|
+
kind: subcommand
|
|
69810
|
+
};
|
|
69811
|
+
}
|
|
68784
69812
|
function parseSetupArguments(args) {
|
|
68785
69813
|
const {
|
|
68786
69814
|
values,
|
|
@@ -69280,6 +70308,13 @@ async function main() {
|
|
|
69280
70308
|
ensureDefaultUserConfig();
|
|
69281
70309
|
console.log(globalConfigPath());
|
|
69282
70310
|
break;
|
|
70311
|
+
case "update":
|
|
70312
|
+
if (wantsHelp(args2)) {
|
|
70313
|
+
help("update");
|
|
70314
|
+
break;
|
|
70315
|
+
}
|
|
70316
|
+
await updateContent(args2);
|
|
70317
|
+
break;
|
|
69283
70318
|
default:
|
|
69284
70319
|
console.error(`unknown command: ${command}`);
|
|
69285
70320
|
help();
|
|
@@ -69311,6 +70346,11 @@ async function doctor() {
|
|
|
69311
70346
|
console.log(`kali image: ${backend2.image} (${image.exists ? "exists" : "missing"})`);
|
|
69312
70347
|
console.log(`kali contract: ${image.contract ?? "missing"}`);
|
|
69313
70348
|
console.log(`kali capabilities: ${image.contract === KALI_IMAGE_CONTRACT ? "ready" : "rebuild required"}`);
|
|
70349
|
+
const {
|
|
70350
|
+
contentStatus: contentStatus2
|
|
70351
|
+
} = await Promise.resolve().then(() => (init_updater(), exports_updater));
|
|
70352
|
+
const content = contentStatus2();
|
|
70353
|
+
console.log(`content: ${content.active?.version ?? "local fallback"}`);
|
|
69314
70354
|
console.log(`setup command: farai setup`);
|
|
69315
70355
|
}
|
|
69316
70356
|
async function setup(args2) {
|
|
@@ -69451,6 +70491,14 @@ async function initLab(args2) {
|
|
|
69451
70491
|
}
|
|
69452
70492
|
async function launchTui(workspace, sessionId) {
|
|
69453
70493
|
ensureDefaultUserConfig();
|
|
70494
|
+
const {
|
|
70495
|
+
runStartupContentPreflight: runStartupContentPreflight2
|
|
70496
|
+
} = await Promise.resolve().then(() => (init_preflight(), exports_preflight));
|
|
70497
|
+
const effectiveWorkspace = sessionId ? resolveSessionLocation(sessionId)?.workspace ?? workspace : workspace;
|
|
70498
|
+
if (await runStartupContentPreflight2(effectiveWorkspace) === "cancelled") {
|
|
70499
|
+
process.exitCode = 130;
|
|
70500
|
+
return;
|
|
70501
|
+
}
|
|
69454
70502
|
if (import.meta.path.endsWith(".ts")) {
|
|
69455
70503
|
const sourceTuiPreload = "@opentui/solid/preload";
|
|
69456
70504
|
await import(sourceTuiPreload);
|
|
@@ -69468,6 +70516,13 @@ async function launchTui(workspace, sessionId) {
|
|
|
69468
70516
|
process.exitCode = 1;
|
|
69469
70517
|
}
|
|
69470
70518
|
}
|
|
70519
|
+
async function updateContent(args2) {
|
|
70520
|
+
const parsed = parseUpdateArguments(args2);
|
|
70521
|
+
const {
|
|
70522
|
+
runContentUpdateCommand: runContentUpdateCommand2
|
|
70523
|
+
} = await Promise.resolve().then(() => (init_command2(), exports_command2));
|
|
70524
|
+
process.exitCode = await runContentUpdateCommand2(parsed, process.cwd());
|
|
70525
|
+
}
|
|
69471
70526
|
async function run2(args2) {
|
|
69472
70527
|
const {
|
|
69473
70528
|
sessionId,
|
|
@@ -69633,7 +70688,14 @@ Usage:
|
|
|
69633
70688
|
config: `Farai config
|
|
69634
70689
|
|
|
69635
70690
|
Usage:
|
|
69636
|
-
farai config
|
|
70691
|
+
farai config`,
|
|
70692
|
+
update: `Farai update
|
|
70693
|
+
|
|
70694
|
+
Usage:
|
|
70695
|
+
farai update status
|
|
70696
|
+
farai update check
|
|
70697
|
+
farai update apply
|
|
70698
|
+
farai update rollback`
|
|
69637
70699
|
};
|
|
69638
70700
|
if (topic && !pages[topic])
|
|
69639
70701
|
throw new Error(`unknown help topic: ${topic}`);
|
|
@@ -69651,6 +70713,7 @@ Usage:
|
|
|
69651
70713
|
farai bench run <manifest.json> [--output result.json] [--workspace scratch-dir] [--artifacts dir]
|
|
69652
70714
|
farai bench suite <suite.json> [--artifacts dir]
|
|
69653
70715
|
farai config
|
|
70716
|
+
farai update [status|check|apply|rollback]
|
|
69654
70717
|
|
|
69655
70718
|
settings live in ~/.local/pajarori/farai/config.toml; credentials use the system keyring.
|
|
69656
70719
|
|
|
@@ -69664,5 +70727,5 @@ Examples:
|
|
|
69664
70727
|
`);
|
|
69665
70728
|
}
|
|
69666
70729
|
|
|
69667
|
-
//# debugId=
|
|
70730
|
+
//# debugId=59D0DF40CE3D0BDB64756E2164756E21
|
|
69668
70731
|
//# sourceMappingURL=index.js.map
|