@brainbase-labs/cli 0.23.0 → 0.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +699 -32
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -36008,7 +36008,7 @@ function padStart(s, n) {
|
|
|
36008
36008
|
// package.json
|
|
36009
36009
|
var package_default = {
|
|
36010
36010
|
name: "@brainbase-labs/cli",
|
|
36011
|
-
version: "0.
|
|
36011
|
+
version: "0.24.0",
|
|
36012
36012
|
description: "Pack, share, and install agent templates across harnesses (Claude Code, Codex, ...).",
|
|
36013
36013
|
type: "module",
|
|
36014
36014
|
bin: {
|
|
@@ -78472,7 +78472,9 @@ import crypto6 from "node:crypto";
|
|
|
78472
78472
|
import fs81 from "node:fs";
|
|
78473
78473
|
import os16 from "node:os";
|
|
78474
78474
|
import path88 from "node:path";
|
|
78475
|
+
import { Readable, Transform as Transform2 } from "node:stream";
|
|
78475
78476
|
import { pipeline as pipeline2 } from "node:stream/promises";
|
|
78477
|
+
import { createGunzip, createInflateRaw } from "node:zlib";
|
|
78476
78478
|
var SCHEMA_VERSION = "1";
|
|
78477
78479
|
var SHA256_RE = /^[a-f0-9]{64}$/i;
|
|
78478
78480
|
var ID_RE = /^[a-zA-Z0-9][a-zA-Z0-9._-]{0,127}$/;
|
|
@@ -78480,6 +78482,9 @@ var ENV_NAME_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
|
78480
78482
|
var MAX_SPEC_BYTES = 20 * 1024 * 1024;
|
|
78481
78483
|
var MAX_FINAL_OUTPUT_BYTES = 10 * 1024 * 1024;
|
|
78482
78484
|
var MAX_TRAJECTORY_BYTES = 100 * 1024 * 1024;
|
|
78485
|
+
var MAX_REMOTE_INPUT_BYTES = 2 * 1024 * 1024 * 1024;
|
|
78486
|
+
var MAX_ARCHIVE_EXTRACTED_BYTES = 2 * 1024 * 1024 * 1024;
|
|
78487
|
+
var MAX_ARCHIVE_SCAN_BYTES = 2 * 1024 * 1024 * 1024;
|
|
78483
78488
|
var RESERVED_WORKSPACE_PATHS = new Set([
|
|
78484
78489
|
".brainbase",
|
|
78485
78490
|
".git",
|
|
@@ -78507,21 +78512,51 @@ var NonSecretEnvironmentValueSchema = exports_external.object({
|
|
|
78507
78512
|
sensitive: exports_external.literal(false)
|
|
78508
78513
|
}).strict();
|
|
78509
78514
|
var RootRelativePathSchema = exports_external.string().min(1).max(1024);
|
|
78515
|
+
var ArchivePathSchema = RootRelativePathSchema.refine((value) => {
|
|
78516
|
+
try {
|
|
78517
|
+
return safeRelPath(value) === value;
|
|
78518
|
+
} catch {
|
|
78519
|
+
return false;
|
|
78520
|
+
}
|
|
78521
|
+
}, {
|
|
78522
|
+
message: "must be a normalized safe relative path"
|
|
78523
|
+
});
|
|
78510
78524
|
var BudgetSchema = exports_external.object({
|
|
78511
78525
|
timeout_ms: exports_external.number().int().min(100).max(3600000),
|
|
78512
78526
|
max_output_bytes: exports_external.number().int().min(1024).max(100 * 1024 * 1024)
|
|
78513
78527
|
}).strict();
|
|
78514
|
-
var
|
|
78528
|
+
var MaterialBaseSchema = {
|
|
78515
78529
|
id: IdSchema,
|
|
78516
|
-
kind: exports_external.enum(["file", "tar_gz"]),
|
|
78517
78530
|
source: RootRelativePathSchema,
|
|
78531
|
+
download_url_env: EnvNameSchema.optional(),
|
|
78518
78532
|
destination: RootRelativePathSchema,
|
|
78519
78533
|
sha256: Sha256Schema,
|
|
78520
78534
|
size_bytes: exports_external.number().int().min(0).max(2 * 1024 * 1024 * 1024),
|
|
78521
|
-
mode: exports_external.number().int().min(0).max(511).optional()
|
|
78535
|
+
mode: exports_external.number().int().min(0).max(511).optional()
|
|
78536
|
+
};
|
|
78537
|
+
var ExpandableMaterialSchema = {
|
|
78522
78538
|
max_unpacked_bytes: exports_external.number().int().min(1).max(2 * 1024 * 1024 * 1024).optional(),
|
|
78523
78539
|
max_file_count: exports_external.number().int().min(1).max(1e5).optional()
|
|
78524
|
-
}
|
|
78540
|
+
};
|
|
78541
|
+
var MaterialSchema = exports_external.discriminatedUnion("kind", [
|
|
78542
|
+
exports_external.object({
|
|
78543
|
+
...MaterialBaseSchema,
|
|
78544
|
+
...ExpandableMaterialSchema,
|
|
78545
|
+
kind: exports_external.literal("file")
|
|
78546
|
+
}).strict(),
|
|
78547
|
+
exports_external.object({
|
|
78548
|
+
...MaterialBaseSchema,
|
|
78549
|
+
...ExpandableMaterialSchema,
|
|
78550
|
+
kind: exports_external.literal("tar_gz")
|
|
78551
|
+
}).strict(),
|
|
78552
|
+
exports_external.object({
|
|
78553
|
+
...MaterialBaseSchema,
|
|
78554
|
+
kind: exports_external.literal("archive_file"),
|
|
78555
|
+
archive_path: ArchivePathSchema,
|
|
78556
|
+
file_sha256: Sha256Schema,
|
|
78557
|
+
file_size_bytes: exports_external.number().int().min(0).max(2 * 1024 * 1024 * 1024)
|
|
78558
|
+
}).strict()
|
|
78559
|
+
]);
|
|
78525
78560
|
var CommandSchema = exports_external.object({
|
|
78526
78561
|
id: IdSchema,
|
|
78527
78562
|
argv: exports_external.array(exports_external.string().min(1)).min(1).max(128),
|
|
@@ -78588,6 +78623,7 @@ var EvaluatorSchema = exports_external.discriminatedUnion("type", [
|
|
|
78588
78623
|
]);
|
|
78589
78624
|
var EvidenceFileSchema = exports_external.object({
|
|
78590
78625
|
source: RootRelativePathSchema,
|
|
78626
|
+
download_url_env: EnvNameSchema.optional(),
|
|
78591
78627
|
sha256: Sha256Schema,
|
|
78592
78628
|
size_bytes: exports_external.number().int().min(0).max(2 * 1024 * 1024 * 1024)
|
|
78593
78629
|
}).strict();
|
|
@@ -78632,6 +78668,35 @@ var BenchmarkSpecSchema = exports_external.discriminatedUnion("phase", [
|
|
|
78632
78668
|
}
|
|
78633
78669
|
}
|
|
78634
78670
|
const declaredSecrets = new Set(value.secret_env);
|
|
78671
|
+
const remoteInputs = value.phase === "hydrate" ? value.materials : [
|
|
78672
|
+
value.evidence.final_output,
|
|
78673
|
+
value.evidence.trajectory,
|
|
78674
|
+
...value.references
|
|
78675
|
+
];
|
|
78676
|
+
const remoteUrlEnvNames = new Set(remoteInputs.flatMap((input) => input.download_url_env ? [input.download_url_env] : []));
|
|
78677
|
+
const uniqueRemoteInputs = new Map;
|
|
78678
|
+
for (const input of remoteInputs) {
|
|
78679
|
+
if (!input.download_url_env)
|
|
78680
|
+
continue;
|
|
78681
|
+
uniqueRemoteInputs.set([input.source, input.sha256, input.size_bytes].join("\x00"), input.size_bytes);
|
|
78682
|
+
}
|
|
78683
|
+
const remoteInputBytes = [...uniqueRemoteInputs.values()].reduce((total, size2) => total + size2, 0);
|
|
78684
|
+
if (remoteInputBytes > MAX_REMOTE_INPUT_BYTES) {
|
|
78685
|
+
context.addIssue({
|
|
78686
|
+
code: exports_external.ZodIssueCode.custom,
|
|
78687
|
+
path: [value.phase === "hydrate" ? "materials" : "references"],
|
|
78688
|
+
message: "remote inputs exceed the aggregate download byte limit"
|
|
78689
|
+
});
|
|
78690
|
+
}
|
|
78691
|
+
for (const name of remoteUrlEnvNames) {
|
|
78692
|
+
if (Object.hasOwn(value.environment, name)) {
|
|
78693
|
+
context.addIssue({
|
|
78694
|
+
code: exports_external.ZodIssueCode.custom,
|
|
78695
|
+
path: ["environment", name],
|
|
78696
|
+
message: "signed download URLs must be supplied through the process environment"
|
|
78697
|
+
});
|
|
78698
|
+
}
|
|
78699
|
+
}
|
|
78635
78700
|
const commands = value.phase === "hydrate" ? value.setup_commands.map((command, index) => ({
|
|
78636
78701
|
command,
|
|
78637
78702
|
path: ["setup_commands", index, "secret_env"]
|
|
@@ -78641,6 +78706,13 @@ var BenchmarkSpecSchema = exports_external.discriminatedUnion("phase", [
|
|
|
78641
78706
|
}] : []);
|
|
78642
78707
|
for (const { command, path: issuePath } of commands) {
|
|
78643
78708
|
for (const name of command.secret_env) {
|
|
78709
|
+
if (remoteUrlEnvNames.has(name)) {
|
|
78710
|
+
context.addIssue({
|
|
78711
|
+
code: exports_external.ZodIssueCode.custom,
|
|
78712
|
+
path: issuePath,
|
|
78713
|
+
message: `signed download URL environment variables cannot be exposed to commands: ${name}`
|
|
78714
|
+
});
|
|
78715
|
+
}
|
|
78644
78716
|
if (!declaredSecrets.has(name)) {
|
|
78645
78717
|
context.addIssue({
|
|
78646
78718
|
code: exports_external.ZodIssueCode.custom,
|
|
@@ -78667,6 +78739,15 @@ var BenchmarkSpecSchema = exports_external.discriminatedUnion("phase", [
|
|
|
78667
78739
|
});
|
|
78668
78740
|
}
|
|
78669
78741
|
}
|
|
78742
|
+
const materials = value.phase === "hydrate" ? value.materials : value.references;
|
|
78743
|
+
const archiveOutputBytes = materials.reduce((total, material) => total + (material.kind === "archive_file" ? material.file_size_bytes : 0), 0);
|
|
78744
|
+
if (archiveOutputBytes > MAX_ARCHIVE_EXTRACTED_BYTES) {
|
|
78745
|
+
context.addIssue({
|
|
78746
|
+
code: exports_external.ZodIssueCode.custom,
|
|
78747
|
+
path: [value.phase === "hydrate" ? "materials" : "references"],
|
|
78748
|
+
message: "archive-file materials exceed the aggregate extracted byte limit"
|
|
78749
|
+
});
|
|
78750
|
+
}
|
|
78670
78751
|
if (value.phase === "evaluate" && value.evaluators.filter((evaluator) => evaluator.type === "sandbox_command").length > 1) {
|
|
78671
78752
|
context.addIssue({
|
|
78672
78753
|
code: exports_external.ZodIssueCode.custom,
|
|
@@ -78680,6 +78761,10 @@ var BENCHMARK_CAPABILITIES = {
|
|
|
78680
78761
|
cli_version: VERSION,
|
|
78681
78762
|
schema_versions: [SCHEMA_VERSION],
|
|
78682
78763
|
phases: ["hydrate", "evaluate"],
|
|
78764
|
+
features: [
|
|
78765
|
+
"remote_input_references_v1",
|
|
78766
|
+
"archive_file_materials_v1"
|
|
78767
|
+
],
|
|
78683
78768
|
evaluator_types: [
|
|
78684
78769
|
"output_assertion",
|
|
78685
78770
|
"trajectory_assertion",
|
|
@@ -78703,6 +78788,11 @@ class BenchmarkPhaseError extends Error {
|
|
|
78703
78788
|
this.name = "BenchmarkPhaseError";
|
|
78704
78789
|
}
|
|
78705
78790
|
}
|
|
78791
|
+
var ZIP_EOCD_SIGNATURE = 101010256;
|
|
78792
|
+
var ZIP_CENTRAL_SIGNATURE = 33639248;
|
|
78793
|
+
var ZIP_LOCAL_SIGNATURE = 67324752;
|
|
78794
|
+
var MAX_ZIP_EOCD_BYTES = 65535 + 22;
|
|
78795
|
+
var MAX_ARCHIVE_MEMBERS = 1e5;
|
|
78706
78796
|
function nowIso() {
|
|
78707
78797
|
return new Date().toISOString();
|
|
78708
78798
|
}
|
|
@@ -78965,10 +79055,516 @@ async function verifyInput(stagingRoot, material) {
|
|
|
78965
79055
|
if (actual !== material.sha256) {
|
|
78966
79056
|
throw new BenchmarkPhaseError("digest_mismatch", `checksum mismatch for ${material.source}`, { expected: material.sha256, actual });
|
|
78967
79057
|
}
|
|
79058
|
+
return {
|
|
79059
|
+
source,
|
|
79060
|
+
sha256: actual,
|
|
79061
|
+
size: opened.stat.size,
|
|
79062
|
+
mode: opened.stat.mode & 511
|
|
79063
|
+
};
|
|
78968
79064
|
} finally {
|
|
78969
79065
|
fs81.closeSync(opened.fd);
|
|
78970
79066
|
}
|
|
78971
|
-
|
|
79067
|
+
}
|
|
79068
|
+
function inputCacheKey(input) {
|
|
79069
|
+
return [
|
|
79070
|
+
input.source,
|
|
79071
|
+
input.sha256,
|
|
79072
|
+
input.size_bytes
|
|
79073
|
+
].join(":");
|
|
79074
|
+
}
|
|
79075
|
+
async function cachedVerifiedInput(stagingRoot, input, context) {
|
|
79076
|
+
const cacheKey = inputCacheKey(input);
|
|
79077
|
+
const cached2 = context.verifiedInputs.get(cacheKey);
|
|
79078
|
+
if (cached2)
|
|
79079
|
+
return cached2;
|
|
79080
|
+
const verified = await verifyInput(stagingRoot, input);
|
|
79081
|
+
context.verifiedInputs.set(cacheKey, verified);
|
|
79082
|
+
return verified;
|
|
79083
|
+
}
|
|
79084
|
+
function stagedInputRecord(stagingRoot, verified) {
|
|
79085
|
+
return {
|
|
79086
|
+
root: "staging",
|
|
79087
|
+
path: path88.relative(stagingRoot, verified.source).replace(/\\/g, "/"),
|
|
79088
|
+
sha256: verified.sha256,
|
|
79089
|
+
size: verified.size,
|
|
79090
|
+
mode: verified.mode,
|
|
79091
|
+
kind: "file"
|
|
79092
|
+
};
|
|
79093
|
+
}
|
|
79094
|
+
function remoteUrlEnvironmentNames(spec) {
|
|
79095
|
+
const inputs = spec.phase === "hydrate" ? spec.materials : [
|
|
79096
|
+
spec.evidence.final_output,
|
|
79097
|
+
spec.evidence.trajectory,
|
|
79098
|
+
...spec.references
|
|
79099
|
+
];
|
|
79100
|
+
return new Set(inputs.flatMap((input) => input.download_url_env ? [input.download_url_env] : []));
|
|
79101
|
+
}
|
|
79102
|
+
function removeRemoteHydrationInputs(spec) {
|
|
79103
|
+
const removedSources = new Set;
|
|
79104
|
+
for (const material of spec.materials) {
|
|
79105
|
+
if (!material.download_url_env || removedSources.has(material.source))
|
|
79106
|
+
continue;
|
|
79107
|
+
const relative = safeRelPath(material.source);
|
|
79108
|
+
const source = path88.resolve(spec.staging_root, relative);
|
|
79109
|
+
if (!isWithin(spec.staging_root, source)) {
|
|
79110
|
+
throw new BenchmarkPhaseError("unsafe_path", `source escapes staging root: ${material.source}`);
|
|
79111
|
+
}
|
|
79112
|
+
assertNoSymlinkTraversal(spec.staging_root, relative);
|
|
79113
|
+
try {
|
|
79114
|
+
fs81.rmSync(source, { force: true });
|
|
79115
|
+
} catch {
|
|
79116
|
+
throw new BenchmarkPhaseError("staging_cleanup_failed", `downloaded benchmark input could not be removed: ${material.source}`);
|
|
79117
|
+
}
|
|
79118
|
+
removedSources.add(material.source);
|
|
79119
|
+
}
|
|
79120
|
+
}
|
|
79121
|
+
async function downloadInputReference(stagingRoot, input, context) {
|
|
79122
|
+
const cacheKey = inputCacheKey(input);
|
|
79123
|
+
const cached2 = context.verifiedInputs.get(cacheKey);
|
|
79124
|
+
if (cached2)
|
|
79125
|
+
return cached2;
|
|
79126
|
+
const envName = input.download_url_env;
|
|
79127
|
+
if (!envName) {
|
|
79128
|
+
return await cachedVerifiedInput(stagingRoot, input, context);
|
|
79129
|
+
}
|
|
79130
|
+
const relative = safeRelPath(input.source);
|
|
79131
|
+
const destination = path88.resolve(stagingRoot, relative);
|
|
79132
|
+
if (!isWithin(stagingRoot, destination)) {
|
|
79133
|
+
throw new BenchmarkPhaseError("unsafe_path", `source escapes staging root: ${input.source}`);
|
|
79134
|
+
}
|
|
79135
|
+
assertNoSymlinkTraversal(stagingRoot, relative);
|
|
79136
|
+
try {
|
|
79137
|
+
fs81.lstatSync(destination);
|
|
79138
|
+
return await cachedVerifiedInput(stagingRoot, input, context);
|
|
79139
|
+
} catch (error2) {
|
|
79140
|
+
if (error2.code !== "ENOENT")
|
|
79141
|
+
throw error2;
|
|
79142
|
+
}
|
|
79143
|
+
const rawUrl = process.env[envName];
|
|
79144
|
+
if (!rawUrl) {
|
|
79145
|
+
throw new BenchmarkPhaseError("missing_environment", `required signed download URL environment variable is missing: ${envName}`);
|
|
79146
|
+
}
|
|
79147
|
+
let url2;
|
|
79148
|
+
try {
|
|
79149
|
+
url2 = new URL(rawUrl);
|
|
79150
|
+
} catch {
|
|
79151
|
+
throw new BenchmarkPhaseError("invalid_download_url", `signed download URL environment variable is invalid: ${envName}`);
|
|
79152
|
+
}
|
|
79153
|
+
if (url2.protocol !== "https:" || url2.username || url2.password) {
|
|
79154
|
+
throw new BenchmarkPhaseError("invalid_download_url", `signed download URL environment variable must contain an HTTPS URL without credentials: ${envName}`);
|
|
79155
|
+
}
|
|
79156
|
+
const remainingMs = context.deadline - Date.now();
|
|
79157
|
+
if (remainingMs <= 0) {
|
|
79158
|
+
throw new BenchmarkPhaseError("phase_timeout", "phase budget expired");
|
|
79159
|
+
}
|
|
79160
|
+
fs81.mkdirSync(path88.dirname(destination), { recursive: true, mode: 448 });
|
|
79161
|
+
assertNoSymlinkTraversal(stagingRoot, relative);
|
|
79162
|
+
assertWritableDestination(stagingRoot, relative);
|
|
79163
|
+
const temporary = `${destination}.${process.pid}.${crypto6.randomBytes(6).toString("hex")}.download`;
|
|
79164
|
+
const controller = new AbortController;
|
|
79165
|
+
const timer = setTimeout(() => controller.abort(), remainingMs);
|
|
79166
|
+
let descriptor;
|
|
79167
|
+
try {
|
|
79168
|
+
let response;
|
|
79169
|
+
try {
|
|
79170
|
+
response = await fetch(url2, {
|
|
79171
|
+
redirect: "error",
|
|
79172
|
+
signal: controller.signal
|
|
79173
|
+
});
|
|
79174
|
+
} catch {
|
|
79175
|
+
if (controller.signal.aborted || Date.now() >= context.deadline) {
|
|
79176
|
+
throw new BenchmarkPhaseError("phase_timeout", "phase budget expired");
|
|
79177
|
+
}
|
|
79178
|
+
throw new BenchmarkPhaseError("download_failed", `signed input download failed for ${input.source}`);
|
|
79179
|
+
}
|
|
79180
|
+
if (response.redirected || response.status >= 300 && response.status < 400) {
|
|
79181
|
+
throw new BenchmarkPhaseError("download_redirect_rejected", `signed input download redirected for ${input.source}`);
|
|
79182
|
+
}
|
|
79183
|
+
if (!response.ok || !response.body) {
|
|
79184
|
+
throw new BenchmarkPhaseError("download_failed", `signed input download failed for ${input.source}`, { status: response.status });
|
|
79185
|
+
}
|
|
79186
|
+
const declaredLength = response.headers.get("content-length");
|
|
79187
|
+
if (declaredLength !== null && /^\d+$/.test(declaredLength) && Number(declaredLength) > input.size_bytes) {
|
|
79188
|
+
throw new BenchmarkPhaseError("size_mismatch", `size mismatch for ${input.source}`, { expected: input.size_bytes, actual: Number(declaredLength) });
|
|
79189
|
+
}
|
|
79190
|
+
descriptor = fs81.openSync(temporary, "wx", 384);
|
|
79191
|
+
const reader = response.body.getReader();
|
|
79192
|
+
let actual;
|
|
79193
|
+
let size2 = 0;
|
|
79194
|
+
try {
|
|
79195
|
+
const hash = crypto6.createHash("sha256");
|
|
79196
|
+
while (true) {
|
|
79197
|
+
const { done, value } = await reader.read();
|
|
79198
|
+
if (done)
|
|
79199
|
+
break;
|
|
79200
|
+
assertBudget(context);
|
|
79201
|
+
size2 += value.byteLength;
|
|
79202
|
+
if (size2 > input.size_bytes) {
|
|
79203
|
+
throw new BenchmarkPhaseError("size_mismatch", `size mismatch for ${input.source}`, { expected: input.size_bytes, actual: size2 });
|
|
79204
|
+
}
|
|
79205
|
+
hash.update(value);
|
|
79206
|
+
let offset = 0;
|
|
79207
|
+
while (offset < value.byteLength) {
|
|
79208
|
+
offset += fs81.writeSync(descriptor, value, offset, value.byteLength - offset);
|
|
79209
|
+
}
|
|
79210
|
+
}
|
|
79211
|
+
actual = hash.digest("hex");
|
|
79212
|
+
} finally {
|
|
79213
|
+
try {
|
|
79214
|
+
await reader.cancel();
|
|
79215
|
+
} catch {}
|
|
79216
|
+
}
|
|
79217
|
+
if (size2 !== input.size_bytes) {
|
|
79218
|
+
throw new BenchmarkPhaseError("size_mismatch", `size mismatch for ${input.source}`, { expected: input.size_bytes, actual: size2 });
|
|
79219
|
+
}
|
|
79220
|
+
if (actual !== input.sha256) {
|
|
79221
|
+
throw new BenchmarkPhaseError("digest_mismatch", `checksum mismatch for ${input.source}`, { expected: input.sha256, actual });
|
|
79222
|
+
}
|
|
79223
|
+
fs81.fsyncSync(descriptor);
|
|
79224
|
+
fs81.closeSync(descriptor);
|
|
79225
|
+
descriptor = undefined;
|
|
79226
|
+
assertNoSymlinkTraversal(stagingRoot, relative);
|
|
79227
|
+
if (fs81.existsSync(destination)) {
|
|
79228
|
+
return await cachedVerifiedInput(stagingRoot, input, context);
|
|
79229
|
+
}
|
|
79230
|
+
fs81.renameSync(temporary, destination);
|
|
79231
|
+
return await cachedVerifiedInput(stagingRoot, input, context);
|
|
79232
|
+
} catch (error2) {
|
|
79233
|
+
if (!(error2 instanceof BenchmarkPhaseError) && (controller.signal.aborted || Date.now() >= context.deadline)) {
|
|
79234
|
+
throw new BenchmarkPhaseError("phase_timeout", "phase budget expired");
|
|
79235
|
+
}
|
|
79236
|
+
if (error2 instanceof BenchmarkPhaseError)
|
|
79237
|
+
throw error2;
|
|
79238
|
+
throw new BenchmarkPhaseError("download_failed", `signed input download failed for ${input.source}`);
|
|
79239
|
+
} finally {
|
|
79240
|
+
clearTimeout(timer);
|
|
79241
|
+
controller.abort();
|
|
79242
|
+
if (descriptor !== undefined)
|
|
79243
|
+
fs81.closeSync(descriptor);
|
|
79244
|
+
fs81.rmSync(temporary, { force: true });
|
|
79245
|
+
}
|
|
79246
|
+
}
|
|
79247
|
+
function readExactly(descriptor, length, position) {
|
|
79248
|
+
const buffer = Buffer.alloc(length);
|
|
79249
|
+
let offset = 0;
|
|
79250
|
+
while (offset < length) {
|
|
79251
|
+
const count = fs81.readSync(descriptor, buffer, offset, length - offset, position + offset);
|
|
79252
|
+
if (count === 0) {
|
|
79253
|
+
throw new BenchmarkPhaseError("invalid_archive", "archive ended unexpectedly");
|
|
79254
|
+
}
|
|
79255
|
+
offset += count;
|
|
79256
|
+
}
|
|
79257
|
+
return buffer;
|
|
79258
|
+
}
|
|
79259
|
+
function decodeZipPath(value, utf8) {
|
|
79260
|
+
if (!utf8 && value.some((byte) => byte >= 128)) {
|
|
79261
|
+
throw new BenchmarkPhaseError("invalid_archive", "ZIP members with non-UTF-8 names are unsupported");
|
|
79262
|
+
}
|
|
79263
|
+
try {
|
|
79264
|
+
return new TextDecoder("utf-8", { fatal: true }).decode(value);
|
|
79265
|
+
} catch {
|
|
79266
|
+
throw new BenchmarkPhaseError("invalid_archive", "ZIP member name is not valid UTF-8");
|
|
79267
|
+
}
|
|
79268
|
+
}
|
|
79269
|
+
function findZipMembers(archivePath, requested, context) {
|
|
79270
|
+
const descriptor = fs81.openSync(archivePath, "r");
|
|
79271
|
+
try {
|
|
79272
|
+
assertBudget(context);
|
|
79273
|
+
const archiveSize = fs81.fstatSync(descriptor).size;
|
|
79274
|
+
const tailSize = Math.min(archiveSize, MAX_ZIP_EOCD_BYTES);
|
|
79275
|
+
const tailOffset = archiveSize - tailSize;
|
|
79276
|
+
const tail2 = readExactly(descriptor, tailSize, tailOffset);
|
|
79277
|
+
let eocdOffset = -1;
|
|
79278
|
+
for (let offset2 = tail2.length - 22;offset2 >= 0; offset2 -= 1) {
|
|
79279
|
+
assertBudget(context);
|
|
79280
|
+
if (tail2.readUInt32LE(offset2) === ZIP_EOCD_SIGNATURE) {
|
|
79281
|
+
const commentLength = tail2.readUInt16LE(offset2 + 20);
|
|
79282
|
+
if (offset2 + 22 + commentLength === tail2.length) {
|
|
79283
|
+
eocdOffset = offset2;
|
|
79284
|
+
break;
|
|
79285
|
+
}
|
|
79286
|
+
}
|
|
79287
|
+
}
|
|
79288
|
+
if (eocdOffset < 0) {
|
|
79289
|
+
throw new BenchmarkPhaseError("invalid_archive", "invalid ZIP archive");
|
|
79290
|
+
}
|
|
79291
|
+
const diskNumber = tail2.readUInt16LE(eocdOffset + 4);
|
|
79292
|
+
const directoryDisk = tail2.readUInt16LE(eocdOffset + 6);
|
|
79293
|
+
const diskEntries = tail2.readUInt16LE(eocdOffset + 8);
|
|
79294
|
+
const totalEntries = tail2.readUInt16LE(eocdOffset + 10);
|
|
79295
|
+
const directorySize = tail2.readUInt32LE(eocdOffset + 12);
|
|
79296
|
+
const directoryOffset = tail2.readUInt32LE(eocdOffset + 16);
|
|
79297
|
+
if (diskNumber !== 0 || directoryDisk !== 0 || diskEntries !== totalEntries) {
|
|
79298
|
+
throw new BenchmarkPhaseError("invalid_archive", "multi-disk ZIP archives are unsupported");
|
|
79299
|
+
}
|
|
79300
|
+
if (totalEntries === 65535 || directorySize === 4294967295 || directoryOffset === 4294967295) {
|
|
79301
|
+
throw new BenchmarkPhaseError("invalid_archive", "ZIP64 archives are unsupported");
|
|
79302
|
+
}
|
|
79303
|
+
if (totalEntries > MAX_ARCHIVE_MEMBERS || directoryOffset + directorySize > tailOffset + eocdOffset) {
|
|
79304
|
+
throw new BenchmarkPhaseError("invalid_archive", "ZIP central directory is invalid");
|
|
79305
|
+
}
|
|
79306
|
+
let offset = directoryOffset;
|
|
79307
|
+
const found = new Map;
|
|
79308
|
+
for (let index = 0;index < totalEntries; index += 1) {
|
|
79309
|
+
assertBudget(context);
|
|
79310
|
+
const header = readExactly(descriptor, 46, offset);
|
|
79311
|
+
if (header.readUInt32LE(0) !== ZIP_CENTRAL_SIGNATURE) {
|
|
79312
|
+
throw new BenchmarkPhaseError("invalid_archive", "ZIP central directory is invalid");
|
|
79313
|
+
}
|
|
79314
|
+
const flags = header.readUInt16LE(8);
|
|
79315
|
+
const compressionMethod = header.readUInt16LE(10);
|
|
79316
|
+
const compressedSize = header.readUInt32LE(20);
|
|
79317
|
+
const uncompressedSize = header.readUInt32LE(24);
|
|
79318
|
+
const nameLength = header.readUInt16LE(28);
|
|
79319
|
+
const extraLength = header.readUInt16LE(30);
|
|
79320
|
+
const commentLength = header.readUInt16LE(32);
|
|
79321
|
+
const diskStart = header.readUInt16LE(34);
|
|
79322
|
+
const externalAttributes = header.readUInt32LE(38);
|
|
79323
|
+
const localHeaderOffset = header.readUInt32LE(42);
|
|
79324
|
+
const recordSize = 46 + nameLength + extraLength + commentLength;
|
|
79325
|
+
if (offset + recordSize > directoryOffset + directorySize || compressedSize === 4294967295 || uncompressedSize === 4294967295 || localHeaderOffset === 4294967295 || diskStart !== 0) {
|
|
79326
|
+
throw new BenchmarkPhaseError("invalid_archive", "ZIP member metadata is invalid");
|
|
79327
|
+
}
|
|
79328
|
+
const name = decodeZipPath(readExactly(descriptor, nameLength, offset + 46), (flags & 2048) !== 0);
|
|
79329
|
+
const material = requested.get(name);
|
|
79330
|
+
if (material) {
|
|
79331
|
+
if (found.has(name)) {
|
|
79332
|
+
throw new BenchmarkPhaseError("invalid_archive", `archive member is duplicated: ${name}`);
|
|
79333
|
+
}
|
|
79334
|
+
const madeBy = header.readUInt16LE(4) >> 8;
|
|
79335
|
+
const unixMode = externalAttributes >>> 16;
|
|
79336
|
+
const fileType = unixMode & 61440;
|
|
79337
|
+
if (name.endsWith("/") || (externalAttributes & 16) !== 0 || madeBy === 3 && fileType !== 0 && fileType !== 32768) {
|
|
79338
|
+
throw new BenchmarkPhaseError("invalid_archive", `archive member must be a regular file: ${name}`);
|
|
79339
|
+
}
|
|
79340
|
+
if ((flags & 1) !== 0 || compressionMethod !== 0 && compressionMethod !== 8) {
|
|
79341
|
+
throw new BenchmarkPhaseError("invalid_archive", `archive member uses unsupported ZIP features: ${name}`);
|
|
79342
|
+
}
|
|
79343
|
+
if (uncompressedSize !== material.file_size_bytes) {
|
|
79344
|
+
throw new BenchmarkPhaseError("size_mismatch", `size mismatch for archive member ${name}`, { expected: material.file_size_bytes, actual: uncompressedSize });
|
|
79345
|
+
}
|
|
79346
|
+
const localHeader = readExactly(descriptor, 30, localHeaderOffset);
|
|
79347
|
+
if (localHeader.readUInt32LE(0) !== ZIP_LOCAL_SIGNATURE || localHeader.readUInt16LE(6) !== flags || localHeader.readUInt16LE(8) !== compressionMethod) {
|
|
79348
|
+
throw new BenchmarkPhaseError("invalid_archive", "ZIP local header is invalid");
|
|
79349
|
+
}
|
|
79350
|
+
const localNameLength = localHeader.readUInt16LE(26);
|
|
79351
|
+
const localExtraLength = localHeader.readUInt16LE(28);
|
|
79352
|
+
const localName = decodeZipPath(readExactly(descriptor, localNameLength, localHeaderOffset + 30), (flags & 2048) !== 0);
|
|
79353
|
+
const dataOffset = localHeaderOffset + 30 + localNameLength + localExtraLength;
|
|
79354
|
+
if (localName !== name || dataOffset + compressedSize > directoryOffset) {
|
|
79355
|
+
throw new BenchmarkPhaseError("invalid_archive", "ZIP member bounds are invalid");
|
|
79356
|
+
}
|
|
79357
|
+
found.set(name, {
|
|
79358
|
+
compressionMethod,
|
|
79359
|
+
compressedSize,
|
|
79360
|
+
dataOffset
|
|
79361
|
+
});
|
|
79362
|
+
}
|
|
79363
|
+
offset += recordSize;
|
|
79364
|
+
}
|
|
79365
|
+
if (offset !== directoryOffset + directorySize) {
|
|
79366
|
+
throw new BenchmarkPhaseError("invalid_archive", "ZIP central directory is invalid");
|
|
79367
|
+
}
|
|
79368
|
+
for (const memberPath of requested.keys()) {
|
|
79369
|
+
if (!found.has(memberPath)) {
|
|
79370
|
+
throw new BenchmarkPhaseError("missing_input", `archive member does not exist: ${memberPath}`);
|
|
79371
|
+
}
|
|
79372
|
+
}
|
|
79373
|
+
return found;
|
|
79374
|
+
} finally {
|
|
79375
|
+
fs81.closeSync(descriptor);
|
|
79376
|
+
}
|
|
79377
|
+
}
|
|
79378
|
+
async function writeVerifiedArchiveMember(source, destination, material, context) {
|
|
79379
|
+
fs81.mkdirSync(path88.dirname(destination), { recursive: true, mode: 448 });
|
|
79380
|
+
const temporary = `${destination}.${process.pid}.${crypto6.randomBytes(6).toString("hex")}.tmp`;
|
|
79381
|
+
const descriptor = fs81.openSync(temporary, "wx", 384);
|
|
79382
|
+
const hash = crypto6.createHash("sha256");
|
|
79383
|
+
let size2 = 0;
|
|
79384
|
+
try {
|
|
79385
|
+
for await (const value of source) {
|
|
79386
|
+
assertBudget(context);
|
|
79387
|
+
const chunk2 = Buffer.isBuffer(value) ? value : Buffer.from(value);
|
|
79388
|
+
size2 += chunk2.length;
|
|
79389
|
+
if (size2 > material.file_size_bytes) {
|
|
79390
|
+
throw new BenchmarkPhaseError("size_mismatch", `size mismatch for archive member ${material.archive_path}`, { expected: material.file_size_bytes, actual: size2 });
|
|
79391
|
+
}
|
|
79392
|
+
hash.update(chunk2);
|
|
79393
|
+
let offset = 0;
|
|
79394
|
+
while (offset < chunk2.length) {
|
|
79395
|
+
offset += fs81.writeSync(descriptor, chunk2, offset, chunk2.length - offset);
|
|
79396
|
+
}
|
|
79397
|
+
}
|
|
79398
|
+
if (size2 !== material.file_size_bytes) {
|
|
79399
|
+
throw new BenchmarkPhaseError("size_mismatch", `size mismatch for archive member ${material.archive_path}`, { expected: material.file_size_bytes, actual: size2 });
|
|
79400
|
+
}
|
|
79401
|
+
const actual = hash.digest("hex");
|
|
79402
|
+
if (actual !== material.file_sha256) {
|
|
79403
|
+
throw new BenchmarkPhaseError("digest_mismatch", `checksum mismatch for archive member ${material.archive_path}`, { expected: material.file_sha256, actual });
|
|
79404
|
+
}
|
|
79405
|
+
fs81.fsyncSync(descriptor);
|
|
79406
|
+
fs81.closeSync(descriptor);
|
|
79407
|
+
fs81.renameSync(temporary, destination);
|
|
79408
|
+
} catch (error2) {
|
|
79409
|
+
if (error2 instanceof BenchmarkPhaseError)
|
|
79410
|
+
throw error2;
|
|
79411
|
+
throw new BenchmarkPhaseError("invalid_archive", `archive member could not be extracted: ${material.archive_path}`);
|
|
79412
|
+
} finally {
|
|
79413
|
+
try {
|
|
79414
|
+
fs81.closeSync(descriptor);
|
|
79415
|
+
} catch {}
|
|
79416
|
+
fs81.rmSync(temporary, { force: true });
|
|
79417
|
+
}
|
|
79418
|
+
}
|
|
79419
|
+
function isZipArchive(archivePath) {
|
|
79420
|
+
const descriptor = fs81.openSync(archivePath, "r");
|
|
79421
|
+
try {
|
|
79422
|
+
const header = Buffer.alloc(4);
|
|
79423
|
+
return fs81.readSync(descriptor, header, 0, header.length, 0) === header.length && [
|
|
79424
|
+
ZIP_LOCAL_SIGNATURE,
|
|
79425
|
+
ZIP_EOCD_SIGNATURE,
|
|
79426
|
+
134695760
|
|
79427
|
+
].includes(header.readUInt32LE(0));
|
|
79428
|
+
} finally {
|
|
79429
|
+
fs81.closeSync(descriptor);
|
|
79430
|
+
}
|
|
79431
|
+
}
|
|
79432
|
+
function isGzipArchive(archivePath) {
|
|
79433
|
+
const descriptor = fs81.openSync(archivePath, "r");
|
|
79434
|
+
try {
|
|
79435
|
+
const header = Buffer.alloc(2);
|
|
79436
|
+
return fs81.readSync(descriptor, header, 0, header.length, 0) === header.length && header[0] === 31 && header[1] === 139;
|
|
79437
|
+
} finally {
|
|
79438
|
+
fs81.closeSync(descriptor);
|
|
79439
|
+
}
|
|
79440
|
+
}
|
|
79441
|
+
function archiveSourceCacheKey(material) {
|
|
79442
|
+
return [
|
|
79443
|
+
material.source,
|
|
79444
|
+
material.sha256,
|
|
79445
|
+
material.size_bytes,
|
|
79446
|
+
material.download_url_env ?? ""
|
|
79447
|
+
].join("\x00");
|
|
79448
|
+
}
|
|
79449
|
+
function archiveMemberCacheKey(material) {
|
|
79450
|
+
return [
|
|
79451
|
+
archiveSourceCacheKey(material),
|
|
79452
|
+
material.archive_path,
|
|
79453
|
+
material.file_sha256,
|
|
79454
|
+
material.file_size_bytes
|
|
79455
|
+
].join("\x00");
|
|
79456
|
+
}
|
|
79457
|
+
function archiveScanBudget(context) {
|
|
79458
|
+
let scanned = 0;
|
|
79459
|
+
return new Transform2({
|
|
79460
|
+
transform(chunk2, _encoding, callback) {
|
|
79461
|
+
try {
|
|
79462
|
+
assertBudget(context);
|
|
79463
|
+
scanned += chunk2.length;
|
|
79464
|
+
if (scanned > MAX_ARCHIVE_SCAN_BYTES) {
|
|
79465
|
+
throw new BenchmarkPhaseError("invalid_archive", "archive expands beyond the supported scan limit");
|
|
79466
|
+
}
|
|
79467
|
+
callback(null, chunk2);
|
|
79468
|
+
} catch (error2) {
|
|
79469
|
+
callback(error2);
|
|
79470
|
+
}
|
|
79471
|
+
}
|
|
79472
|
+
});
|
|
79473
|
+
}
|
|
79474
|
+
async function extractArchiveMembers(archivePath, outputRoot, materials, context) {
|
|
79475
|
+
const requested = new Map;
|
|
79476
|
+
for (const material of materials) {
|
|
79477
|
+
let memberPath;
|
|
79478
|
+
try {
|
|
79479
|
+
memberPath = safeRelPath(material.archive_path);
|
|
79480
|
+
} catch {
|
|
79481
|
+
throw new BenchmarkPhaseError("unsafe_path", "archive member path is unsafe");
|
|
79482
|
+
}
|
|
79483
|
+
const existing = requested.get(memberPath);
|
|
79484
|
+
if (existing && (existing.file_sha256 !== material.file_sha256 || existing.file_size_bytes !== material.file_size_bytes)) {
|
|
79485
|
+
throw new BenchmarkPhaseError("invalid_archive", `archive member has conflicting declarations: ${memberPath}`);
|
|
79486
|
+
}
|
|
79487
|
+
requested.set(memberPath, material);
|
|
79488
|
+
}
|
|
79489
|
+
const extracted = new Map;
|
|
79490
|
+
if (isZipArchive(archivePath)) {
|
|
79491
|
+
const indexed = findZipMembers(archivePath, requested, context);
|
|
79492
|
+
for (const [memberPath, material] of requested) {
|
|
79493
|
+
assertBudget(context);
|
|
79494
|
+
const destination = path88.resolve(outputRoot, memberPath);
|
|
79495
|
+
if (!isWithin(outputRoot, destination)) {
|
|
79496
|
+
throw new BenchmarkPhaseError("unsafe_path", `archive member escapes output root: ${memberPath}`);
|
|
79497
|
+
}
|
|
79498
|
+
const member = indexed.get(memberPath);
|
|
79499
|
+
const compressed = member.compressedSize === 0 ? Readable.from([]) : fs81.createReadStream(archivePath, {
|
|
79500
|
+
start: member.dataOffset,
|
|
79501
|
+
end: member.dataOffset + member.compressedSize - 1
|
|
79502
|
+
});
|
|
79503
|
+
const contents = member.compressionMethod === 8 ? compressed.pipe(createInflateRaw()) : compressed;
|
|
79504
|
+
await writeVerifiedArchiveMember(contents, destination, material, context);
|
|
79505
|
+
extracted.set(memberPath, destination);
|
|
79506
|
+
}
|
|
79507
|
+
return extracted;
|
|
79508
|
+
}
|
|
79509
|
+
const matches2 = new Map;
|
|
79510
|
+
let validationError;
|
|
79511
|
+
const unpack = co({
|
|
79512
|
+
cwd: outputRoot,
|
|
79513
|
+
strict: true,
|
|
79514
|
+
preserveOwner: false,
|
|
79515
|
+
filter: (entryPath, entryAny) => {
|
|
79516
|
+
assertBudget(context);
|
|
79517
|
+
const material = requested.get(entryPath);
|
|
79518
|
+
if (!material)
|
|
79519
|
+
return false;
|
|
79520
|
+
const count = (matches2.get(entryPath) ?? 0) + 1;
|
|
79521
|
+
matches2.set(entryPath, count);
|
|
79522
|
+
const entry = entryAny;
|
|
79523
|
+
if (count > 1) {
|
|
79524
|
+
validationError = new BenchmarkPhaseError("invalid_archive", `archive member is duplicated: ${entryPath}`);
|
|
79525
|
+
return false;
|
|
79526
|
+
}
|
|
79527
|
+
if (!["File", "OldFile", "ContiguousFile"].includes(entry.type)) {
|
|
79528
|
+
validationError = new BenchmarkPhaseError("invalid_archive", `archive member must be a regular file: ${entryPath}`);
|
|
79529
|
+
return false;
|
|
79530
|
+
}
|
|
79531
|
+
if (entry.size !== material.file_size_bytes) {
|
|
79532
|
+
validationError = new BenchmarkPhaseError("size_mismatch", `size mismatch for archive member ${entryPath}`, { expected: material.file_size_bytes, actual: entry.size });
|
|
79533
|
+
return false;
|
|
79534
|
+
}
|
|
79535
|
+
return true;
|
|
79536
|
+
}
|
|
79537
|
+
});
|
|
79538
|
+
const archive = fs81.createReadStream(archivePath);
|
|
79539
|
+
const scanBudget = archiveScanBudget(context);
|
|
79540
|
+
try {
|
|
79541
|
+
if (isGzipArchive(archivePath)) {
|
|
79542
|
+
await pipeline2(archive, createGunzip(), scanBudget, unpack);
|
|
79543
|
+
} else {
|
|
79544
|
+
await pipeline2(archive, scanBudget, unpack);
|
|
79545
|
+
}
|
|
79546
|
+
} catch (error2) {
|
|
79547
|
+
if (validationError)
|
|
79548
|
+
throw validationError;
|
|
79549
|
+
if (error2 instanceof BenchmarkPhaseError)
|
|
79550
|
+
throw error2;
|
|
79551
|
+
throw new BenchmarkPhaseError("invalid_archive", "archive members could not be extracted");
|
|
79552
|
+
}
|
|
79553
|
+
if (validationError)
|
|
79554
|
+
throw validationError;
|
|
79555
|
+
for (const [memberPath, material] of requested) {
|
|
79556
|
+
if (!matches2.has(memberPath)) {
|
|
79557
|
+
throw new BenchmarkPhaseError("missing_input", `archive member does not exist: ${memberPath}`);
|
|
79558
|
+
}
|
|
79559
|
+
const verified = await verifyInput(outputRoot, {
|
|
79560
|
+
source: memberPath,
|
|
79561
|
+
sha256: material.file_sha256,
|
|
79562
|
+
size_bytes: material.file_size_bytes
|
|
79563
|
+
});
|
|
79564
|
+
assertBudget(context);
|
|
79565
|
+
extracted.set(memberPath, verified.source);
|
|
79566
|
+
}
|
|
79567
|
+
return extracted;
|
|
78972
79568
|
}
|
|
78973
79569
|
async function atomicCopy(source, destination, mode, sourceRoot) {
|
|
78974
79570
|
fs81.mkdirSync(path88.dirname(destination), { recursive: true });
|
|
@@ -79017,21 +79613,31 @@ async function recordFile(root, filePath, rootName, kind = "file") {
|
|
|
79017
79613
|
fs81.closeSync(opened.fd);
|
|
79018
79614
|
}
|
|
79019
79615
|
}
|
|
79020
|
-
async function copyMaterial(material, sourceRoot, destinationRoot, destinationRootName, protectWorkspace) {
|
|
79021
|
-
const
|
|
79616
|
+
async function copyMaterial(material, sourceRoot, destinationRoot, destinationRootName, protectWorkspace, context) {
|
|
79617
|
+
const verifiedSource = material.kind === "archive_file" ? undefined : await cachedVerifiedInput(sourceRoot, material, context);
|
|
79618
|
+
const source = material.kind === "archive_file" ? context.preparedArchiveFiles.get(archiveMemberCacheKey(material)) : verifiedSource?.source;
|
|
79619
|
+
if (!source) {
|
|
79620
|
+
throw new BenchmarkPhaseError("invalid_archive", `archive member was not prepared: ${material.id}`);
|
|
79621
|
+
}
|
|
79022
79622
|
const destinationRel = protectWorkspace ? workspaceRel(material.destination, material.kind === "tar_gz") : normalizedRootRelative(material.destination);
|
|
79023
79623
|
if (!protectWorkspace && destinationRel.toLowerCase() === ".brainbase-benchmark-owner.json") {
|
|
79024
79624
|
throw new BenchmarkPhaseError("reserved_path", "benchmark input cannot replace the phase ownership marker");
|
|
79025
79625
|
}
|
|
79026
|
-
if (material.kind === "file") {
|
|
79626
|
+
if (material.kind === "file" || material.kind === "archive_file") {
|
|
79027
79627
|
if (destinationRel === ".") {
|
|
79028
79628
|
throw new BenchmarkPhaseError("unsafe_path", "file destination cannot be a directory root");
|
|
79029
79629
|
}
|
|
79030
79630
|
assertWritableDestination(destinationRoot, destinationRel);
|
|
79031
79631
|
const destination = path88.resolve(destinationRoot, destinationRel);
|
|
79032
|
-
|
|
79632
|
+
if (material.kind === "file") {
|
|
79633
|
+
await atomicCopy(source, destination, material.mode ?? verifiedSource?.mode, sourceRoot);
|
|
79634
|
+
} else {
|
|
79635
|
+
await atomicCopy(source, destination, material.mode, path88.dirname(source));
|
|
79636
|
+
}
|
|
79033
79637
|
const record3 = await recordFile(destinationRoot, destination, destinationRootName);
|
|
79034
|
-
|
|
79638
|
+
const expectedSha256 = material.kind === "archive_file" ? material.file_sha256 : material.sha256;
|
|
79639
|
+
const expectedSize = material.kind === "archive_file" ? material.file_size_bytes : material.size_bytes;
|
|
79640
|
+
if (record3.sha256 !== expectedSha256 || record3.size !== expectedSize) {
|
|
79035
79641
|
throw new BenchmarkPhaseError("evidence_tampered", `material changed while it was copied: ${material.source}`);
|
|
79036
79642
|
}
|
|
79037
79643
|
return [record3];
|
|
@@ -79072,17 +79678,39 @@ async function copyMaterial(material, sourceRoot, destinationRoot, destinationRo
|
|
|
79072
79678
|
fs81.rmSync(temporary, { recursive: true, force: true });
|
|
79073
79679
|
}
|
|
79074
79680
|
}
|
|
79075
|
-
async function preflightMaterial(material, sourceRoot, destinationRoot, protectWorkspace) {
|
|
79076
|
-
const source = await
|
|
79681
|
+
async function preflightMaterial(material, materials, sourceRoot, destinationRoot, protectWorkspace, context) {
|
|
79682
|
+
const source = (await cachedVerifiedInput(sourceRoot, material, context)).source;
|
|
79077
79683
|
const destinationRel = protectWorkspace ? workspaceRel(material.destination, material.kind === "tar_gz") : normalizedRootRelative(material.destination);
|
|
79078
79684
|
if (!protectWorkspace && destinationRel.toLowerCase() === ".brainbase-benchmark-owner.json") {
|
|
79079
79685
|
throw new BenchmarkPhaseError("reserved_path", "benchmark input cannot replace the phase ownership marker");
|
|
79080
79686
|
}
|
|
79081
|
-
if (material.kind === "file") {
|
|
79687
|
+
if (material.kind === "file" || material.kind === "archive_file") {
|
|
79082
79688
|
if (destinationRel === ".") {
|
|
79083
79689
|
throw new BenchmarkPhaseError("unsafe_path", "file destination cannot be a directory root");
|
|
79084
79690
|
}
|
|
79085
79691
|
assertWritableDestination(destinationRoot, destinationRel);
|
|
79692
|
+
if (material.kind === "archive_file") {
|
|
79693
|
+
const cacheKey = archiveMemberCacheKey(material);
|
|
79694
|
+
let extracted = context.preparedArchiveFiles.get(cacheKey);
|
|
79695
|
+
if (!extracted) {
|
|
79696
|
+
const temporary2 = fs81.mkdtempSync(path88.join(os16.tmpdir(), "brainbase-benchmark-member-"));
|
|
79697
|
+
context.temporaryRoots.add(temporary2);
|
|
79698
|
+
const archiveKey = archiveSourceCacheKey(material);
|
|
79699
|
+
const related = materials.filter((candidate) => candidate.kind === "archive_file" && archiveSourceCacheKey(candidate) === archiveKey);
|
|
79700
|
+
const extractedMembers = await extractArchiveMembers(source, temporary2, related, context);
|
|
79701
|
+
for (const candidate of related) {
|
|
79702
|
+
const prepared = extractedMembers.get(safeRelPath(candidate.archive_path));
|
|
79703
|
+
if (!prepared) {
|
|
79704
|
+
throw new BenchmarkPhaseError("missing_input", `archive member does not exist: ${candidate.archive_path}`);
|
|
79705
|
+
}
|
|
79706
|
+
context.preparedArchiveFiles.set(archiveMemberCacheKey(candidate), prepared);
|
|
79707
|
+
}
|
|
79708
|
+
extracted = context.preparedArchiveFiles.get(cacheKey);
|
|
79709
|
+
}
|
|
79710
|
+
if (!extracted) {
|
|
79711
|
+
throw new BenchmarkPhaseError("missing_input", `archive member does not exist: ${material.archive_path}`);
|
|
79712
|
+
}
|
|
79713
|
+
}
|
|
79086
79714
|
return [destinationRel];
|
|
79087
79715
|
}
|
|
79088
79716
|
const temporary = fs81.mkdtempSync(path88.join(os16.tmpdir(), "brainbase-benchmark-preflight-"));
|
|
@@ -79150,14 +79778,18 @@ function prepareOwnedDirectory(root, role, spec) {
|
|
|
79150
79778
|
}
|
|
79151
79779
|
function buildEnvironment(spec, secretNames, additions = {}) {
|
|
79152
79780
|
const env3 = {};
|
|
79781
|
+
const remoteUrlEnvNames = remoteUrlEnvironmentNames(spec);
|
|
79153
79782
|
for (const name of BASE_ENV_NAMES) {
|
|
79154
|
-
if (process.env[name] !== undefined)
|
|
79783
|
+
if (!remoteUrlEnvNames.has(name) && process.env[name] !== undefined) {
|
|
79155
79784
|
env3[name] = process.env[name];
|
|
79785
|
+
}
|
|
79156
79786
|
}
|
|
79157
79787
|
for (const [name, declared] of Object.entries(spec.environment)) {
|
|
79158
79788
|
env3[name] = declared.value;
|
|
79159
79789
|
}
|
|
79160
79790
|
for (const name of secretNames) {
|
|
79791
|
+
if (remoteUrlEnvNames.has(name))
|
|
79792
|
+
continue;
|
|
79161
79793
|
const value = process.env[name];
|
|
79162
79794
|
if (value === undefined) {
|
|
79163
79795
|
throw new BenchmarkPhaseError("missing_environment", `required environment variable is missing: ${name}`);
|
|
@@ -79169,7 +79801,11 @@ function buildEnvironment(spec, secretNames, additions = {}) {
|
|
|
79169
79801
|
}
|
|
79170
79802
|
function redactCommandOutput(data, spec) {
|
|
79171
79803
|
let value = data.toString("utf8");
|
|
79172
|
-
|
|
79804
|
+
const sensitiveNames = new Set([
|
|
79805
|
+
...spec.secret_env,
|
|
79806
|
+
...remoteUrlEnvironmentNames(spec)
|
|
79807
|
+
]);
|
|
79808
|
+
for (const name of sensitiveNames) {
|
|
79173
79809
|
const secret = process.env[name];
|
|
79174
79810
|
if (secret)
|
|
79175
79811
|
value = value.split(secret).join("[REDACTED]");
|
|
@@ -79339,16 +79975,19 @@ async function executeHydrate(spec, context) {
|
|
|
79339
79975
|
const plannedDestinations = [];
|
|
79340
79976
|
for (const material of spec.materials) {
|
|
79341
79977
|
assertBudget(context);
|
|
79342
|
-
|
|
79343
|
-
|
|
79344
|
-
|
|
79978
|
+
const verified = await downloadInputReference(spec.staging_root, material, context);
|
|
79979
|
+
plannedDestinations.push(...await preflightMaterial(material, spec.materials, spec.staging_root, spec.workspace_root, true, context));
|
|
79980
|
+
const record3 = stagedInputRecord(spec.staging_root, verified);
|
|
79981
|
+
if (!context.inputs.some((input) => input.path === record3.path && input.sha256 === record3.sha256 && input.size === record3.size)) {
|
|
79982
|
+
context.inputs.push(record3);
|
|
79983
|
+
}
|
|
79345
79984
|
}
|
|
79346
79985
|
validateDestinationGraph(plannedDestinations);
|
|
79347
79986
|
for (const material of spec.materials) {
|
|
79348
79987
|
assertBudget(context);
|
|
79349
79988
|
const started = Date.now();
|
|
79350
79989
|
try {
|
|
79351
|
-
const records = await copyMaterial(material, spec.staging_root, spec.workspace_root, "workspace", true);
|
|
79990
|
+
const records = await copyMaterial(material, spec.staging_root, spec.workspace_root, "workspace", true, context);
|
|
79352
79991
|
outputs.push(...records);
|
|
79353
79992
|
context.outputs.push(...records);
|
|
79354
79993
|
context.steps.push({
|
|
@@ -79422,6 +80061,7 @@ async function executeHydrate(spec, context) {
|
|
|
79422
80061
|
}
|
|
79423
80062
|
outputs.splice(0, outputs.length, ...finalOutputs);
|
|
79424
80063
|
context.outputs.splice(0, context.outputs.length, ...finalOutputs);
|
|
80064
|
+
removeRemoteHydrationInputs(spec);
|
|
79425
80065
|
assertBudget(context);
|
|
79426
80066
|
return outputs;
|
|
79427
80067
|
}
|
|
@@ -79627,6 +80267,8 @@ async function evaluateOne(evaluator, spec, finalOutput, trajectory, frozenEvide
|
|
|
79627
80267
|
};
|
|
79628
80268
|
}
|
|
79629
80269
|
async function executeEvaluate(spec, context) {
|
|
80270
|
+
await downloadInputReference(spec.staging_root, spec.evidence.final_output, context);
|
|
80271
|
+
await downloadInputReference(spec.staging_root, spec.evidence.trajectory, context);
|
|
79630
80272
|
const finalOutput = await readEvidence(spec.staging_root, spec.evidence.final_output);
|
|
79631
80273
|
const trajectoryEvidence = await readEvidence(spec.staging_root, spec.evidence.trajectory);
|
|
79632
80274
|
context.inputs.push(finalOutput.record, trajectoryEvidence.record);
|
|
@@ -79640,9 +80282,12 @@ async function executeEvaluate(spec, context) {
|
|
|
79640
80282
|
const plannedReferences = [];
|
|
79641
80283
|
for (const reference of spec.references) {
|
|
79642
80284
|
assertBudget(context);
|
|
79643
|
-
|
|
79644
|
-
|
|
79645
|
-
|
|
80285
|
+
const verified = await downloadInputReference(spec.staging_root, reference, context);
|
|
80286
|
+
plannedReferences.push(...await preflightMaterial(reference, spec.references, spec.staging_root, spec.tests_root, false, context));
|
|
80287
|
+
const record3 = stagedInputRecord(spec.staging_root, verified);
|
|
80288
|
+
if (!context.inputs.some((input) => input.path === record3.path && input.sha256 === record3.sha256 && input.size === record3.size)) {
|
|
80289
|
+
context.inputs.push(record3);
|
|
80290
|
+
}
|
|
79646
80291
|
}
|
|
79647
80292
|
validateDestinationGraph(plannedReferences);
|
|
79648
80293
|
assertBudget(context);
|
|
@@ -79707,7 +80352,7 @@ async function executeEvaluate(spec, context) {
|
|
|
79707
80352
|
}
|
|
79708
80353
|
await verifyRecordsUnchanged(manifest, spec);
|
|
79709
80354
|
for (const reference of spec.references) {
|
|
79710
|
-
const referenceOutputs = await copyMaterial(reference, spec.staging_root, spec.tests_root, "tests", false);
|
|
80355
|
+
const referenceOutputs = await copyMaterial(reference, spec.staging_root, spec.tests_root, "tests", false, context);
|
|
79711
80356
|
outputs.push(...referenceOutputs);
|
|
79712
80357
|
context.outputs.push(...referenceOutputs);
|
|
79713
80358
|
}
|
|
@@ -79857,19 +80502,26 @@ function prepareBenchmarkPhaseInvocation(specPathInput, resultPathInput, expecte
|
|
|
79857
80502
|
spec_digest: digest,
|
|
79858
80503
|
timeout_ms: spec.budget.timeout_ms
|
|
79859
80504
|
};
|
|
80505
|
+
let cachedResult;
|
|
79860
80506
|
if (fs81.existsSync(resultPath)) {
|
|
79861
80507
|
try {
|
|
79862
80508
|
const cached2 = JSON.parse(fs81.readFileSync(resultPath, "utf8"));
|
|
79863
80509
|
if (cached2.schema_version === SCHEMA_VERSION && cached2.phase === spec.phase && cached2.attempt_id === spec.attempt_id && cached2.phase_id === spec.phase_id && cached2.spec_digest === digest && cached2.status === "succeeded") {
|
|
79864
|
-
|
|
79865
|
-
ok: true,
|
|
79866
|
-
invocation,
|
|
79867
|
-
spec_bytes: bytes,
|
|
79868
|
-
cached_result: cached2
|
|
79869
|
-
};
|
|
80510
|
+
cachedResult = cached2;
|
|
79870
80511
|
}
|
|
79871
80512
|
} catch {}
|
|
79872
80513
|
}
|
|
80514
|
+
if (cachedResult) {
|
|
80515
|
+
if (spec.phase === "hydrate") {
|
|
80516
|
+
removeRemoteHydrationInputs(spec);
|
|
80517
|
+
}
|
|
80518
|
+
return {
|
|
80519
|
+
ok: true,
|
|
80520
|
+
invocation,
|
|
80521
|
+
spec_bytes: bytes,
|
|
80522
|
+
cached_result: cachedResult
|
|
80523
|
+
};
|
|
80524
|
+
}
|
|
79873
80525
|
return { ok: true, invocation, spec_bytes: bytes };
|
|
79874
80526
|
} catch (error2) {
|
|
79875
80527
|
return {
|
|
@@ -79966,14 +80618,21 @@ async function runBenchmarkPhase(specPathInput, resultPathInput, expectedPhase,
|
|
|
79966
80618
|
throw new BenchmarkPhaseError("invalid_result_path", `result path must be ${expectedResultPath}`);
|
|
79967
80619
|
}
|
|
79968
80620
|
resultPathValidated = true;
|
|
80621
|
+
let cachedResult;
|
|
79969
80622
|
if (fs81.existsSync(resultPath)) {
|
|
79970
80623
|
try {
|
|
79971
80624
|
const cached2 = JSON.parse(fs81.readFileSync(resultPath, "utf8"));
|
|
79972
80625
|
if (cached2.schema_version === SCHEMA_VERSION && cached2.phase === spec.phase && cached2.attempt_id === spec.attempt_id && cached2.phase_id === spec.phase_id && cached2.spec_digest === digest && cached2.status === "succeeded") {
|
|
79973
|
-
|
|
80626
|
+
cachedResult = cached2;
|
|
79974
80627
|
}
|
|
79975
80628
|
} catch {}
|
|
79976
80629
|
}
|
|
80630
|
+
if (cachedResult) {
|
|
80631
|
+
if (spec.phase === "hydrate") {
|
|
80632
|
+
removeRemoteHydrationInputs(spec);
|
|
80633
|
+
}
|
|
80634
|
+
return { exitCode: 0, result: cachedResult };
|
|
80635
|
+
}
|
|
79977
80636
|
context = {
|
|
79978
80637
|
deadline: started + spec.budget.timeout_ms,
|
|
79979
80638
|
remainingOutputBytes: spec.budget.max_output_bytes,
|
|
@@ -79981,7 +80640,10 @@ async function runBenchmarkPhase(specPathInput, resultPathInput, expectedPhase,
|
|
|
79981
80640
|
evaluators: [],
|
|
79982
80641
|
inputs: [],
|
|
79983
80642
|
outputs: [],
|
|
79984
|
-
logsOwned: false
|
|
80643
|
+
logsOwned: false,
|
|
80644
|
+
verifiedInputs: new Map,
|
|
80645
|
+
preparedArchiveFiles: new Map,
|
|
80646
|
+
temporaryRoots: new Set
|
|
79985
80647
|
};
|
|
79986
80648
|
if (spec.phase === "hydrate") {
|
|
79987
80649
|
outputs = await executeHydrate(spec, context);
|
|
@@ -80001,6 +80663,11 @@ async function runBenchmarkPhase(specPathInput, resultPathInput, expectedPhase,
|
|
|
80001
80663
|
evaluators = context.evaluators;
|
|
80002
80664
|
}
|
|
80003
80665
|
}
|
|
80666
|
+
if (context) {
|
|
80667
|
+
for (const temporary of context.temporaryRoots) {
|
|
80668
|
+
fs81.rmSync(temporary, { recursive: true, force: true });
|
|
80669
|
+
}
|
|
80670
|
+
}
|
|
80004
80671
|
const result2 = {
|
|
80005
80672
|
schema_version: SCHEMA_VERSION,
|
|
80006
80673
|
cli_version: VERSION,
|