@brainbase-labs/cli 0.23.0 → 0.25.0-eng1209.1
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 +1119 -74
- 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.25.0-eng1209.1",
|
|
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,16 @@ 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;
|
|
78488
|
+
var MAX_SANDBOX_COMMANDS = 20;
|
|
78489
|
+
var MAX_CRITERIA_PER_EVALUATOR = 100;
|
|
78490
|
+
var MAX_CRITERIA_RESULT_BYTES = 1024 * 1024;
|
|
78491
|
+
var MAX_CRITERION_EXPLANATION_LENGTH = 16384;
|
|
78492
|
+
var MAX_CRITERION_EVIDENCE_IDS = 100;
|
|
78493
|
+
var MAX_CRITERION_EVIDENCE_ID_LENGTH = 256;
|
|
78494
|
+
var MAX_ALLOWED_EVIDENCE_IDS = 1e4;
|
|
78483
78495
|
var RESERVED_WORKSPACE_PATHS = new Set([
|
|
78484
78496
|
".brainbase",
|
|
78485
78497
|
".git",
|
|
@@ -78507,21 +78519,62 @@ var NonSecretEnvironmentValueSchema = exports_external.object({
|
|
|
78507
78519
|
sensitive: exports_external.literal(false)
|
|
78508
78520
|
}).strict();
|
|
78509
78521
|
var RootRelativePathSchema = exports_external.string().min(1).max(1024);
|
|
78522
|
+
var CandidateOutputPatternSchema = RootRelativePathSchema.refine((value) => {
|
|
78523
|
+
if (value.includes("\\") || /[\[\]{}]/.test(value))
|
|
78524
|
+
return false;
|
|
78525
|
+
try {
|
|
78526
|
+
return safeRelPath(value) === value;
|
|
78527
|
+
} catch {
|
|
78528
|
+
return false;
|
|
78529
|
+
}
|
|
78530
|
+
}, {
|
|
78531
|
+
message: "must be a normalized safe relative glob using only *, ?, and ** wildcards"
|
|
78532
|
+
});
|
|
78533
|
+
var ArchivePathSchema = RootRelativePathSchema.refine((value) => {
|
|
78534
|
+
try {
|
|
78535
|
+
return safeRelPath(value) === value;
|
|
78536
|
+
} catch {
|
|
78537
|
+
return false;
|
|
78538
|
+
}
|
|
78539
|
+
}, {
|
|
78540
|
+
message: "must be a normalized safe relative path"
|
|
78541
|
+
});
|
|
78510
78542
|
var BudgetSchema = exports_external.object({
|
|
78511
78543
|
timeout_ms: exports_external.number().int().min(100).max(3600000),
|
|
78512
78544
|
max_output_bytes: exports_external.number().int().min(1024).max(100 * 1024 * 1024)
|
|
78513
78545
|
}).strict();
|
|
78514
|
-
var
|
|
78546
|
+
var MaterialBaseSchema = {
|
|
78515
78547
|
id: IdSchema,
|
|
78516
|
-
kind: exports_external.enum(["file", "tar_gz"]),
|
|
78517
78548
|
source: RootRelativePathSchema,
|
|
78549
|
+
download_url_env: EnvNameSchema.optional(),
|
|
78518
78550
|
destination: RootRelativePathSchema,
|
|
78519
78551
|
sha256: Sha256Schema,
|
|
78520
78552
|
size_bytes: exports_external.number().int().min(0).max(2 * 1024 * 1024 * 1024),
|
|
78521
|
-
mode: exports_external.number().int().min(0).max(511).optional()
|
|
78553
|
+
mode: exports_external.number().int().min(0).max(511).optional()
|
|
78554
|
+
};
|
|
78555
|
+
var ExpandableMaterialSchema = {
|
|
78522
78556
|
max_unpacked_bytes: exports_external.number().int().min(1).max(2 * 1024 * 1024 * 1024).optional(),
|
|
78523
78557
|
max_file_count: exports_external.number().int().min(1).max(1e5).optional()
|
|
78524
|
-
}
|
|
78558
|
+
};
|
|
78559
|
+
var MaterialSchema = exports_external.discriminatedUnion("kind", [
|
|
78560
|
+
exports_external.object({
|
|
78561
|
+
...MaterialBaseSchema,
|
|
78562
|
+
...ExpandableMaterialSchema,
|
|
78563
|
+
kind: exports_external.literal("file")
|
|
78564
|
+
}).strict(),
|
|
78565
|
+
exports_external.object({
|
|
78566
|
+
...MaterialBaseSchema,
|
|
78567
|
+
...ExpandableMaterialSchema,
|
|
78568
|
+
kind: exports_external.literal("tar_gz")
|
|
78569
|
+
}).strict(),
|
|
78570
|
+
exports_external.object({
|
|
78571
|
+
...MaterialBaseSchema,
|
|
78572
|
+
kind: exports_external.literal("archive_file"),
|
|
78573
|
+
archive_path: ArchivePathSchema,
|
|
78574
|
+
file_sha256: Sha256Schema,
|
|
78575
|
+
file_size_bytes: exports_external.number().int().min(0).max(2 * 1024 * 1024 * 1024)
|
|
78576
|
+
}).strict()
|
|
78577
|
+
]);
|
|
78525
78578
|
var CommandSchema = exports_external.object({
|
|
78526
78579
|
id: IdSchema,
|
|
78527
78580
|
argv: exports_external.array(exports_external.string().min(1)).min(1).max(128),
|
|
@@ -78583,11 +78636,16 @@ var EvaluatorSchema = exports_external.discriminatedUnion("type", [
|
|
|
78583
78636
|
...EvaluatorBase,
|
|
78584
78637
|
type: exports_external.literal("sandbox_command"),
|
|
78585
78638
|
command: CommandSchema.omit({ id: true }),
|
|
78586
|
-
root: exports_external.enum(["workspace", "tests"]).default("tests")
|
|
78639
|
+
root: exports_external.enum(["workspace", "tests"]).default("tests"),
|
|
78640
|
+
criterion_keys: exports_external.array(IdSchema).min(1).max(MAX_CRITERIA_PER_EVALUATOR).optional(),
|
|
78641
|
+
allowed_evidence_ids: exports_external.array(exports_external.string().min(1).max(MAX_CRITERION_EVIDENCE_ID_LENGTH)).max(MAX_ALLOWED_EVIDENCE_IDS).optional(),
|
|
78642
|
+
tests_path: ArchivePathSchema.optional(),
|
|
78643
|
+
workspace_mode: exports_external.enum(["read_only", "isolated_copy"]).default("read_only")
|
|
78587
78644
|
}).strict()
|
|
78588
78645
|
]);
|
|
78589
78646
|
var EvidenceFileSchema = exports_external.object({
|
|
78590
78647
|
source: RootRelativePathSchema,
|
|
78648
|
+
download_url_env: EnvNameSchema.optional(),
|
|
78591
78649
|
sha256: Sha256Schema,
|
|
78592
78650
|
size_bytes: exports_external.number().int().min(0).max(2 * 1024 * 1024 * 1024)
|
|
78593
78651
|
}).strict();
|
|
@@ -78602,6 +78660,37 @@ var HydrateSpecSchema = BaseSpecSchema.extend({
|
|
|
78602
78660
|
materials: exports_external.array(MaterialSchema).max(1e4).default([]),
|
|
78603
78661
|
setup_commands: exports_external.array(CommandSchema).max(128).default([])
|
|
78604
78662
|
}).strict();
|
|
78663
|
+
var CandidateOutputSchema = exports_external.object({
|
|
78664
|
+
id: IdSchema,
|
|
78665
|
+
pattern: CandidateOutputPatternSchema,
|
|
78666
|
+
kind: exports_external.enum(["file", "directory"]),
|
|
78667
|
+
required: exports_external.boolean(),
|
|
78668
|
+
min_matches: exports_external.number().int().min(0).max(1e5),
|
|
78669
|
+
max_matches: exports_external.number().int().min(1).max(1e5),
|
|
78670
|
+
max_total_bytes: exports_external.number().int().min(0).max(2 * 1024 * 1024 * 1024)
|
|
78671
|
+
}).strict().superRefine((value, context) => {
|
|
78672
|
+
if (value.min_matches > value.max_matches) {
|
|
78673
|
+
context.addIssue({
|
|
78674
|
+
code: exports_external.ZodIssueCode.custom,
|
|
78675
|
+
path: ["min_matches"],
|
|
78676
|
+
message: "min_matches cannot exceed max_matches"
|
|
78677
|
+
});
|
|
78678
|
+
}
|
|
78679
|
+
if (value.required && value.min_matches === 0) {
|
|
78680
|
+
context.addIssue({
|
|
78681
|
+
code: exports_external.ZodIssueCode.custom,
|
|
78682
|
+
path: ["min_matches"],
|
|
78683
|
+
message: "required candidate outputs must require at least one match"
|
|
78684
|
+
});
|
|
78685
|
+
}
|
|
78686
|
+
if (!value.required && value.min_matches > 0) {
|
|
78687
|
+
context.addIssue({
|
|
78688
|
+
code: exports_external.ZodIssueCode.custom,
|
|
78689
|
+
path: ["min_matches"],
|
|
78690
|
+
message: "optional candidate outputs must allow zero matches"
|
|
78691
|
+
});
|
|
78692
|
+
}
|
|
78693
|
+
});
|
|
78605
78694
|
var EvaluateSpecSchema = BaseSpecSchema.extend({
|
|
78606
78695
|
phase: exports_external.literal("evaluate"),
|
|
78607
78696
|
tests_root: AbsolutePathSchema,
|
|
@@ -78612,6 +78701,7 @@ var EvaluateSpecSchema = BaseSpecSchema.extend({
|
|
|
78612
78701
|
references: exports_external.array(MaterialSchema).max(1e4).default([]),
|
|
78613
78702
|
evaluators: exports_external.array(EvaluatorSchema).min(1).max(1000),
|
|
78614
78703
|
candidate_artifacts: exports_external.array(RootRelativePathSchema).max(1000).default([]),
|
|
78704
|
+
candidate_outputs: exports_external.array(CandidateOutputSchema).max(1000).default([]),
|
|
78615
78705
|
capture_workspace_archive: exports_external.boolean().default(false),
|
|
78616
78706
|
workspace_limits: exports_external.object({
|
|
78617
78707
|
max_file_count: exports_external.number().int().min(1).max(1e6).default(1e5),
|
|
@@ -78632,6 +78722,35 @@ var BenchmarkSpecSchema = exports_external.discriminatedUnion("phase", [
|
|
|
78632
78722
|
}
|
|
78633
78723
|
}
|
|
78634
78724
|
const declaredSecrets = new Set(value.secret_env);
|
|
78725
|
+
const remoteInputs = value.phase === "hydrate" ? value.materials : [
|
|
78726
|
+
value.evidence.final_output,
|
|
78727
|
+
value.evidence.trajectory,
|
|
78728
|
+
...value.references
|
|
78729
|
+
];
|
|
78730
|
+
const remoteUrlEnvNames = new Set(remoteInputs.flatMap((input) => input.download_url_env ? [input.download_url_env] : []));
|
|
78731
|
+
const uniqueRemoteInputs = new Map;
|
|
78732
|
+
for (const input of remoteInputs) {
|
|
78733
|
+
if (!input.download_url_env)
|
|
78734
|
+
continue;
|
|
78735
|
+
uniqueRemoteInputs.set([input.source, input.sha256, input.size_bytes].join("\x00"), input.size_bytes);
|
|
78736
|
+
}
|
|
78737
|
+
const remoteInputBytes = [...uniqueRemoteInputs.values()].reduce((total, size2) => total + size2, 0);
|
|
78738
|
+
if (remoteInputBytes > MAX_REMOTE_INPUT_BYTES) {
|
|
78739
|
+
context.addIssue({
|
|
78740
|
+
code: exports_external.ZodIssueCode.custom,
|
|
78741
|
+
path: [value.phase === "hydrate" ? "materials" : "references"],
|
|
78742
|
+
message: "remote inputs exceed the aggregate download byte limit"
|
|
78743
|
+
});
|
|
78744
|
+
}
|
|
78745
|
+
for (const name of remoteUrlEnvNames) {
|
|
78746
|
+
if (Object.hasOwn(value.environment, name)) {
|
|
78747
|
+
context.addIssue({
|
|
78748
|
+
code: exports_external.ZodIssueCode.custom,
|
|
78749
|
+
path: ["environment", name],
|
|
78750
|
+
message: "signed download URLs must be supplied through the process environment"
|
|
78751
|
+
});
|
|
78752
|
+
}
|
|
78753
|
+
}
|
|
78635
78754
|
const commands = value.phase === "hydrate" ? value.setup_commands.map((command, index) => ({
|
|
78636
78755
|
command,
|
|
78637
78756
|
path: ["setup_commands", index, "secret_env"]
|
|
@@ -78641,6 +78760,13 @@ var BenchmarkSpecSchema = exports_external.discriminatedUnion("phase", [
|
|
|
78641
78760
|
}] : []);
|
|
78642
78761
|
for (const { command, path: issuePath } of commands) {
|
|
78643
78762
|
for (const name of command.secret_env) {
|
|
78763
|
+
if (remoteUrlEnvNames.has(name)) {
|
|
78764
|
+
context.addIssue({
|
|
78765
|
+
code: exports_external.ZodIssueCode.custom,
|
|
78766
|
+
path: issuePath,
|
|
78767
|
+
message: `signed download URL environment variables cannot be exposed to commands: ${name}`
|
|
78768
|
+
});
|
|
78769
|
+
}
|
|
78644
78770
|
if (!declaredSecrets.has(name)) {
|
|
78645
78771
|
context.addIssue({
|
|
78646
78772
|
code: exports_external.ZodIssueCode.custom,
|
|
@@ -78655,7 +78781,8 @@ var BenchmarkSpecSchema = exports_external.discriminatedUnion("phase", [
|
|
|
78655
78781
|
{ items: value.setup_commands, path: "setup_commands" }
|
|
78656
78782
|
] : [
|
|
78657
78783
|
{ items: value.references, path: "references" },
|
|
78658
|
-
{ items: value.evaluators, path: "evaluators" }
|
|
78784
|
+
{ items: value.evaluators, path: "evaluators" },
|
|
78785
|
+
{ items: value.candidate_outputs, path: "candidate_outputs" }
|
|
78659
78786
|
];
|
|
78660
78787
|
for (const { items, path: issuePath } of uniqueLists) {
|
|
78661
78788
|
const ids = items.map((item) => item.id);
|
|
@@ -78667,11 +78794,53 @@ var BenchmarkSpecSchema = exports_external.discriminatedUnion("phase", [
|
|
|
78667
78794
|
});
|
|
78668
78795
|
}
|
|
78669
78796
|
}
|
|
78670
|
-
if (value.phase === "evaluate"
|
|
78797
|
+
if (value.phase === "evaluate") {
|
|
78798
|
+
value.evaluators.forEach((evaluator, index) => {
|
|
78799
|
+
if (evaluator.type === "sandbox_command" && evaluator.criterion_keys && new Set(evaluator.criterion_keys).size !== evaluator.criterion_keys.length) {
|
|
78800
|
+
context.addIssue({
|
|
78801
|
+
code: exports_external.ZodIssueCode.custom,
|
|
78802
|
+
path: ["evaluators", index, "criterion_keys"],
|
|
78803
|
+
message: "criterion_keys must be unique"
|
|
78804
|
+
});
|
|
78805
|
+
}
|
|
78806
|
+
if (evaluator.type === "sandbox_command" && evaluator.criterion_keys && evaluator.allowed_evidence_ids === undefined) {
|
|
78807
|
+
context.addIssue({
|
|
78808
|
+
code: exports_external.ZodIssueCode.custom,
|
|
78809
|
+
path: ["evaluators", index, "allowed_evidence_ids"],
|
|
78810
|
+
message: "structured criteria require allowed_evidence_ids"
|
|
78811
|
+
});
|
|
78812
|
+
}
|
|
78813
|
+
if (evaluator.type === "sandbox_command" && evaluator.allowed_evidence_ids && new Set(evaluator.allowed_evidence_ids).size !== evaluator.allowed_evidence_ids.length) {
|
|
78814
|
+
context.addIssue({
|
|
78815
|
+
code: exports_external.ZodIssueCode.custom,
|
|
78816
|
+
path: ["evaluators", index, "allowed_evidence_ids"],
|
|
78817
|
+
message: "allowed_evidence_ids must be unique"
|
|
78818
|
+
});
|
|
78819
|
+
}
|
|
78820
|
+
});
|
|
78821
|
+
const candidateOutputBytes = value.candidate_outputs.reduce((total, output) => total + output.max_total_bytes, 0);
|
|
78822
|
+
if (candidateOutputBytes > value.workspace_limits.max_total_bytes) {
|
|
78823
|
+
context.addIssue({
|
|
78824
|
+
code: exports_external.ZodIssueCode.custom,
|
|
78825
|
+
path: ["candidate_outputs"],
|
|
78826
|
+
message: "candidate output byte limits exceed the workspace byte limit"
|
|
78827
|
+
});
|
|
78828
|
+
}
|
|
78829
|
+
}
|
|
78830
|
+
const materials = value.phase === "hydrate" ? value.materials : value.references;
|
|
78831
|
+
const archiveOutputBytes = materials.reduce((total, material) => total + (material.kind === "archive_file" ? material.file_size_bytes : 0), 0);
|
|
78832
|
+
if (archiveOutputBytes > MAX_ARCHIVE_EXTRACTED_BYTES) {
|
|
78833
|
+
context.addIssue({
|
|
78834
|
+
code: exports_external.ZodIssueCode.custom,
|
|
78835
|
+
path: [value.phase === "hydrate" ? "materials" : "references"],
|
|
78836
|
+
message: "archive-file materials exceed the aggregate extracted byte limit"
|
|
78837
|
+
});
|
|
78838
|
+
}
|
|
78839
|
+
if (value.phase === "evaluate" && value.evaluators.filter((evaluator) => evaluator.type === "sandbox_command").length > MAX_SANDBOX_COMMANDS) {
|
|
78671
78840
|
context.addIssue({
|
|
78672
78841
|
code: exports_external.ZodIssueCode.custom,
|
|
78673
78842
|
path: ["evaluators"],
|
|
78674
|
-
message:
|
|
78843
|
+
message: `schema version 1 supports at most ${MAX_SANDBOX_COMMANDS} sandbox_command evaluators`
|
|
78675
78844
|
});
|
|
78676
78845
|
}
|
|
78677
78846
|
});
|
|
@@ -78680,6 +78849,15 @@ var BENCHMARK_CAPABILITIES = {
|
|
|
78680
78849
|
cli_version: VERSION,
|
|
78681
78850
|
schema_versions: [SCHEMA_VERSION],
|
|
78682
78851
|
phases: ["hydrate", "evaluate"],
|
|
78852
|
+
features: [
|
|
78853
|
+
"remote_input_references_v1",
|
|
78854
|
+
"archive_file_materials_v1",
|
|
78855
|
+
"structured_criterion_results_v1",
|
|
78856
|
+
"multiple_sandbox_commands_v1",
|
|
78857
|
+
"sandbox_command_workspace_modes_v1",
|
|
78858
|
+
"candidate_outputs_v1",
|
|
78859
|
+
"criterion_evidence_allowlist_v1"
|
|
78860
|
+
],
|
|
78683
78861
|
evaluator_types: [
|
|
78684
78862
|
"output_assertion",
|
|
78685
78863
|
"trajectory_assertion",
|
|
@@ -78689,7 +78867,8 @@ var BENCHMARK_CAPABILITIES = {
|
|
|
78689
78867
|
limits: {
|
|
78690
78868
|
max_secret_bindings: 100,
|
|
78691
78869
|
max_evaluators: 1000,
|
|
78692
|
-
max_sandbox_commands:
|
|
78870
|
+
max_sandbox_commands: MAX_SANDBOX_COMMANDS,
|
|
78871
|
+
max_criteria_per_evaluator: MAX_CRITERIA_PER_EVALUATOR
|
|
78693
78872
|
}
|
|
78694
78873
|
};
|
|
78695
78874
|
|
|
@@ -78703,6 +78882,11 @@ class BenchmarkPhaseError extends Error {
|
|
|
78703
78882
|
this.name = "BenchmarkPhaseError";
|
|
78704
78883
|
}
|
|
78705
78884
|
}
|
|
78885
|
+
var ZIP_EOCD_SIGNATURE = 101010256;
|
|
78886
|
+
var ZIP_CENTRAL_SIGNATURE = 33639248;
|
|
78887
|
+
var ZIP_LOCAL_SIGNATURE = 67324752;
|
|
78888
|
+
var MAX_ZIP_EOCD_BYTES = 65535 + 22;
|
|
78889
|
+
var MAX_ARCHIVE_MEMBERS = 1e5;
|
|
78706
78890
|
function nowIso() {
|
|
78707
78891
|
return new Date().toISOString();
|
|
78708
78892
|
}
|
|
@@ -78965,10 +79149,516 @@ async function verifyInput(stagingRoot, material) {
|
|
|
78965
79149
|
if (actual !== material.sha256) {
|
|
78966
79150
|
throw new BenchmarkPhaseError("digest_mismatch", `checksum mismatch for ${material.source}`, { expected: material.sha256, actual });
|
|
78967
79151
|
}
|
|
79152
|
+
return {
|
|
79153
|
+
source,
|
|
79154
|
+
sha256: actual,
|
|
79155
|
+
size: opened.stat.size,
|
|
79156
|
+
mode: opened.stat.mode & 511
|
|
79157
|
+
};
|
|
78968
79158
|
} finally {
|
|
78969
79159
|
fs81.closeSync(opened.fd);
|
|
78970
79160
|
}
|
|
78971
|
-
|
|
79161
|
+
}
|
|
79162
|
+
function inputCacheKey(input) {
|
|
79163
|
+
return [
|
|
79164
|
+
input.source,
|
|
79165
|
+
input.sha256,
|
|
79166
|
+
input.size_bytes
|
|
79167
|
+
].join(":");
|
|
79168
|
+
}
|
|
79169
|
+
async function cachedVerifiedInput(stagingRoot, input, context) {
|
|
79170
|
+
const cacheKey = inputCacheKey(input);
|
|
79171
|
+
const cached2 = context.verifiedInputs.get(cacheKey);
|
|
79172
|
+
if (cached2)
|
|
79173
|
+
return cached2;
|
|
79174
|
+
const verified = await verifyInput(stagingRoot, input);
|
|
79175
|
+
context.verifiedInputs.set(cacheKey, verified);
|
|
79176
|
+
return verified;
|
|
79177
|
+
}
|
|
79178
|
+
function stagedInputRecord(stagingRoot, verified) {
|
|
79179
|
+
return {
|
|
79180
|
+
root: "staging",
|
|
79181
|
+
path: path88.relative(stagingRoot, verified.source).replace(/\\/g, "/"),
|
|
79182
|
+
sha256: verified.sha256,
|
|
79183
|
+
size: verified.size,
|
|
79184
|
+
mode: verified.mode,
|
|
79185
|
+
kind: "file"
|
|
79186
|
+
};
|
|
79187
|
+
}
|
|
79188
|
+
function remoteUrlEnvironmentNames(spec) {
|
|
79189
|
+
const inputs = spec.phase === "hydrate" ? spec.materials : [
|
|
79190
|
+
spec.evidence.final_output,
|
|
79191
|
+
spec.evidence.trajectory,
|
|
79192
|
+
...spec.references
|
|
79193
|
+
];
|
|
79194
|
+
return new Set(inputs.flatMap((input) => input.download_url_env ? [input.download_url_env] : []));
|
|
79195
|
+
}
|
|
79196
|
+
function removeRemoteHydrationInputs(spec) {
|
|
79197
|
+
const removedSources = new Set;
|
|
79198
|
+
for (const material of spec.materials) {
|
|
79199
|
+
if (!material.download_url_env || removedSources.has(material.source))
|
|
79200
|
+
continue;
|
|
79201
|
+
const relative = safeRelPath(material.source);
|
|
79202
|
+
const source = path88.resolve(spec.staging_root, relative);
|
|
79203
|
+
if (!isWithin(spec.staging_root, source)) {
|
|
79204
|
+
throw new BenchmarkPhaseError("unsafe_path", `source escapes staging root: ${material.source}`);
|
|
79205
|
+
}
|
|
79206
|
+
assertNoSymlinkTraversal(spec.staging_root, relative);
|
|
79207
|
+
try {
|
|
79208
|
+
fs81.rmSync(source, { force: true });
|
|
79209
|
+
} catch {
|
|
79210
|
+
throw new BenchmarkPhaseError("staging_cleanup_failed", `downloaded benchmark input could not be removed: ${material.source}`);
|
|
79211
|
+
}
|
|
79212
|
+
removedSources.add(material.source);
|
|
79213
|
+
}
|
|
79214
|
+
}
|
|
79215
|
+
async function downloadInputReference(stagingRoot, input, context) {
|
|
79216
|
+
const cacheKey = inputCacheKey(input);
|
|
79217
|
+
const cached2 = context.verifiedInputs.get(cacheKey);
|
|
79218
|
+
if (cached2)
|
|
79219
|
+
return cached2;
|
|
79220
|
+
const envName = input.download_url_env;
|
|
79221
|
+
if (!envName) {
|
|
79222
|
+
return await cachedVerifiedInput(stagingRoot, input, context);
|
|
79223
|
+
}
|
|
79224
|
+
const relative = safeRelPath(input.source);
|
|
79225
|
+
const destination = path88.resolve(stagingRoot, relative);
|
|
79226
|
+
if (!isWithin(stagingRoot, destination)) {
|
|
79227
|
+
throw new BenchmarkPhaseError("unsafe_path", `source escapes staging root: ${input.source}`);
|
|
79228
|
+
}
|
|
79229
|
+
assertNoSymlinkTraversal(stagingRoot, relative);
|
|
79230
|
+
try {
|
|
79231
|
+
fs81.lstatSync(destination);
|
|
79232
|
+
return await cachedVerifiedInput(stagingRoot, input, context);
|
|
79233
|
+
} catch (error2) {
|
|
79234
|
+
if (error2.code !== "ENOENT")
|
|
79235
|
+
throw error2;
|
|
79236
|
+
}
|
|
79237
|
+
const rawUrl = process.env[envName];
|
|
79238
|
+
if (!rawUrl) {
|
|
79239
|
+
throw new BenchmarkPhaseError("missing_environment", `required signed download URL environment variable is missing: ${envName}`);
|
|
79240
|
+
}
|
|
79241
|
+
let url2;
|
|
79242
|
+
try {
|
|
79243
|
+
url2 = new URL(rawUrl);
|
|
79244
|
+
} catch {
|
|
79245
|
+
throw new BenchmarkPhaseError("invalid_download_url", `signed download URL environment variable is invalid: ${envName}`);
|
|
79246
|
+
}
|
|
79247
|
+
if (url2.protocol !== "https:" || url2.username || url2.password) {
|
|
79248
|
+
throw new BenchmarkPhaseError("invalid_download_url", `signed download URL environment variable must contain an HTTPS URL without credentials: ${envName}`);
|
|
79249
|
+
}
|
|
79250
|
+
const remainingMs = context.deadline - Date.now();
|
|
79251
|
+
if (remainingMs <= 0) {
|
|
79252
|
+
throw new BenchmarkPhaseError("phase_timeout", "phase budget expired");
|
|
79253
|
+
}
|
|
79254
|
+
fs81.mkdirSync(path88.dirname(destination), { recursive: true, mode: 448 });
|
|
79255
|
+
assertNoSymlinkTraversal(stagingRoot, relative);
|
|
79256
|
+
assertWritableDestination(stagingRoot, relative);
|
|
79257
|
+
const temporary = `${destination}.${process.pid}.${crypto6.randomBytes(6).toString("hex")}.download`;
|
|
79258
|
+
const controller = new AbortController;
|
|
79259
|
+
const timer = setTimeout(() => controller.abort(), remainingMs);
|
|
79260
|
+
let descriptor;
|
|
79261
|
+
try {
|
|
79262
|
+
let response;
|
|
79263
|
+
try {
|
|
79264
|
+
response = await fetch(url2, {
|
|
79265
|
+
redirect: "error",
|
|
79266
|
+
signal: controller.signal
|
|
79267
|
+
});
|
|
79268
|
+
} catch {
|
|
79269
|
+
if (controller.signal.aborted || Date.now() >= context.deadline) {
|
|
79270
|
+
throw new BenchmarkPhaseError("phase_timeout", "phase budget expired");
|
|
79271
|
+
}
|
|
79272
|
+
throw new BenchmarkPhaseError("download_failed", `signed input download failed for ${input.source}`);
|
|
79273
|
+
}
|
|
79274
|
+
if (response.redirected || response.status >= 300 && response.status < 400) {
|
|
79275
|
+
throw new BenchmarkPhaseError("download_redirect_rejected", `signed input download redirected for ${input.source}`);
|
|
79276
|
+
}
|
|
79277
|
+
if (!response.ok || !response.body) {
|
|
79278
|
+
throw new BenchmarkPhaseError("download_failed", `signed input download failed for ${input.source}`, { status: response.status });
|
|
79279
|
+
}
|
|
79280
|
+
const declaredLength = response.headers.get("content-length");
|
|
79281
|
+
if (declaredLength !== null && /^\d+$/.test(declaredLength) && Number(declaredLength) > input.size_bytes) {
|
|
79282
|
+
throw new BenchmarkPhaseError("size_mismatch", `size mismatch for ${input.source}`, { expected: input.size_bytes, actual: Number(declaredLength) });
|
|
79283
|
+
}
|
|
79284
|
+
descriptor = fs81.openSync(temporary, "wx", 384);
|
|
79285
|
+
const reader = response.body.getReader();
|
|
79286
|
+
let actual;
|
|
79287
|
+
let size2 = 0;
|
|
79288
|
+
try {
|
|
79289
|
+
const hash = crypto6.createHash("sha256");
|
|
79290
|
+
while (true) {
|
|
79291
|
+
const { done, value } = await reader.read();
|
|
79292
|
+
if (done)
|
|
79293
|
+
break;
|
|
79294
|
+
assertBudget(context);
|
|
79295
|
+
size2 += value.byteLength;
|
|
79296
|
+
if (size2 > input.size_bytes) {
|
|
79297
|
+
throw new BenchmarkPhaseError("size_mismatch", `size mismatch for ${input.source}`, { expected: input.size_bytes, actual: size2 });
|
|
79298
|
+
}
|
|
79299
|
+
hash.update(value);
|
|
79300
|
+
let offset = 0;
|
|
79301
|
+
while (offset < value.byteLength) {
|
|
79302
|
+
offset += fs81.writeSync(descriptor, value, offset, value.byteLength - offset);
|
|
79303
|
+
}
|
|
79304
|
+
}
|
|
79305
|
+
actual = hash.digest("hex");
|
|
79306
|
+
} finally {
|
|
79307
|
+
try {
|
|
79308
|
+
await reader.cancel();
|
|
79309
|
+
} catch {}
|
|
79310
|
+
}
|
|
79311
|
+
if (size2 !== input.size_bytes) {
|
|
79312
|
+
throw new BenchmarkPhaseError("size_mismatch", `size mismatch for ${input.source}`, { expected: input.size_bytes, actual: size2 });
|
|
79313
|
+
}
|
|
79314
|
+
if (actual !== input.sha256) {
|
|
79315
|
+
throw new BenchmarkPhaseError("digest_mismatch", `checksum mismatch for ${input.source}`, { expected: input.sha256, actual });
|
|
79316
|
+
}
|
|
79317
|
+
fs81.fsyncSync(descriptor);
|
|
79318
|
+
fs81.closeSync(descriptor);
|
|
79319
|
+
descriptor = undefined;
|
|
79320
|
+
assertNoSymlinkTraversal(stagingRoot, relative);
|
|
79321
|
+
if (fs81.existsSync(destination)) {
|
|
79322
|
+
return await cachedVerifiedInput(stagingRoot, input, context);
|
|
79323
|
+
}
|
|
79324
|
+
fs81.renameSync(temporary, destination);
|
|
79325
|
+
return await cachedVerifiedInput(stagingRoot, input, context);
|
|
79326
|
+
} catch (error2) {
|
|
79327
|
+
if (!(error2 instanceof BenchmarkPhaseError) && (controller.signal.aborted || Date.now() >= context.deadline)) {
|
|
79328
|
+
throw new BenchmarkPhaseError("phase_timeout", "phase budget expired");
|
|
79329
|
+
}
|
|
79330
|
+
if (error2 instanceof BenchmarkPhaseError)
|
|
79331
|
+
throw error2;
|
|
79332
|
+
throw new BenchmarkPhaseError("download_failed", `signed input download failed for ${input.source}`);
|
|
79333
|
+
} finally {
|
|
79334
|
+
clearTimeout(timer);
|
|
79335
|
+
controller.abort();
|
|
79336
|
+
if (descriptor !== undefined)
|
|
79337
|
+
fs81.closeSync(descriptor);
|
|
79338
|
+
fs81.rmSync(temporary, { force: true });
|
|
79339
|
+
}
|
|
79340
|
+
}
|
|
79341
|
+
function readExactly(descriptor, length, position) {
|
|
79342
|
+
const buffer = Buffer.alloc(length);
|
|
79343
|
+
let offset = 0;
|
|
79344
|
+
while (offset < length) {
|
|
79345
|
+
const count = fs81.readSync(descriptor, buffer, offset, length - offset, position + offset);
|
|
79346
|
+
if (count === 0) {
|
|
79347
|
+
throw new BenchmarkPhaseError("invalid_archive", "archive ended unexpectedly");
|
|
79348
|
+
}
|
|
79349
|
+
offset += count;
|
|
79350
|
+
}
|
|
79351
|
+
return buffer;
|
|
79352
|
+
}
|
|
79353
|
+
function decodeZipPath(value, utf8) {
|
|
79354
|
+
if (!utf8 && value.some((byte) => byte >= 128)) {
|
|
79355
|
+
throw new BenchmarkPhaseError("invalid_archive", "ZIP members with non-UTF-8 names are unsupported");
|
|
79356
|
+
}
|
|
79357
|
+
try {
|
|
79358
|
+
return new TextDecoder("utf-8", { fatal: true }).decode(value);
|
|
79359
|
+
} catch {
|
|
79360
|
+
throw new BenchmarkPhaseError("invalid_archive", "ZIP member name is not valid UTF-8");
|
|
79361
|
+
}
|
|
79362
|
+
}
|
|
79363
|
+
function findZipMembers(archivePath, requested, context) {
|
|
79364
|
+
const descriptor = fs81.openSync(archivePath, "r");
|
|
79365
|
+
try {
|
|
79366
|
+
assertBudget(context);
|
|
79367
|
+
const archiveSize = fs81.fstatSync(descriptor).size;
|
|
79368
|
+
const tailSize = Math.min(archiveSize, MAX_ZIP_EOCD_BYTES);
|
|
79369
|
+
const tailOffset = archiveSize - tailSize;
|
|
79370
|
+
const tail2 = readExactly(descriptor, tailSize, tailOffset);
|
|
79371
|
+
let eocdOffset = -1;
|
|
79372
|
+
for (let offset2 = tail2.length - 22;offset2 >= 0; offset2 -= 1) {
|
|
79373
|
+
assertBudget(context);
|
|
79374
|
+
if (tail2.readUInt32LE(offset2) === ZIP_EOCD_SIGNATURE) {
|
|
79375
|
+
const commentLength = tail2.readUInt16LE(offset2 + 20);
|
|
79376
|
+
if (offset2 + 22 + commentLength === tail2.length) {
|
|
79377
|
+
eocdOffset = offset2;
|
|
79378
|
+
break;
|
|
79379
|
+
}
|
|
79380
|
+
}
|
|
79381
|
+
}
|
|
79382
|
+
if (eocdOffset < 0) {
|
|
79383
|
+
throw new BenchmarkPhaseError("invalid_archive", "invalid ZIP archive");
|
|
79384
|
+
}
|
|
79385
|
+
const diskNumber = tail2.readUInt16LE(eocdOffset + 4);
|
|
79386
|
+
const directoryDisk = tail2.readUInt16LE(eocdOffset + 6);
|
|
79387
|
+
const diskEntries = tail2.readUInt16LE(eocdOffset + 8);
|
|
79388
|
+
const totalEntries = tail2.readUInt16LE(eocdOffset + 10);
|
|
79389
|
+
const directorySize = tail2.readUInt32LE(eocdOffset + 12);
|
|
79390
|
+
const directoryOffset = tail2.readUInt32LE(eocdOffset + 16);
|
|
79391
|
+
if (diskNumber !== 0 || directoryDisk !== 0 || diskEntries !== totalEntries) {
|
|
79392
|
+
throw new BenchmarkPhaseError("invalid_archive", "multi-disk ZIP archives are unsupported");
|
|
79393
|
+
}
|
|
79394
|
+
if (totalEntries === 65535 || directorySize === 4294967295 || directoryOffset === 4294967295) {
|
|
79395
|
+
throw new BenchmarkPhaseError("invalid_archive", "ZIP64 archives are unsupported");
|
|
79396
|
+
}
|
|
79397
|
+
if (totalEntries > MAX_ARCHIVE_MEMBERS || directoryOffset + directorySize > tailOffset + eocdOffset) {
|
|
79398
|
+
throw new BenchmarkPhaseError("invalid_archive", "ZIP central directory is invalid");
|
|
79399
|
+
}
|
|
79400
|
+
let offset = directoryOffset;
|
|
79401
|
+
const found = new Map;
|
|
79402
|
+
for (let index = 0;index < totalEntries; index += 1) {
|
|
79403
|
+
assertBudget(context);
|
|
79404
|
+
const header = readExactly(descriptor, 46, offset);
|
|
79405
|
+
if (header.readUInt32LE(0) !== ZIP_CENTRAL_SIGNATURE) {
|
|
79406
|
+
throw new BenchmarkPhaseError("invalid_archive", "ZIP central directory is invalid");
|
|
79407
|
+
}
|
|
79408
|
+
const flags = header.readUInt16LE(8);
|
|
79409
|
+
const compressionMethod = header.readUInt16LE(10);
|
|
79410
|
+
const compressedSize = header.readUInt32LE(20);
|
|
79411
|
+
const uncompressedSize = header.readUInt32LE(24);
|
|
79412
|
+
const nameLength = header.readUInt16LE(28);
|
|
79413
|
+
const extraLength = header.readUInt16LE(30);
|
|
79414
|
+
const commentLength = header.readUInt16LE(32);
|
|
79415
|
+
const diskStart = header.readUInt16LE(34);
|
|
79416
|
+
const externalAttributes = header.readUInt32LE(38);
|
|
79417
|
+
const localHeaderOffset = header.readUInt32LE(42);
|
|
79418
|
+
const recordSize = 46 + nameLength + extraLength + commentLength;
|
|
79419
|
+
if (offset + recordSize > directoryOffset + directorySize || compressedSize === 4294967295 || uncompressedSize === 4294967295 || localHeaderOffset === 4294967295 || diskStart !== 0) {
|
|
79420
|
+
throw new BenchmarkPhaseError("invalid_archive", "ZIP member metadata is invalid");
|
|
79421
|
+
}
|
|
79422
|
+
const name = decodeZipPath(readExactly(descriptor, nameLength, offset + 46), (flags & 2048) !== 0);
|
|
79423
|
+
const material = requested.get(name);
|
|
79424
|
+
if (material) {
|
|
79425
|
+
if (found.has(name)) {
|
|
79426
|
+
throw new BenchmarkPhaseError("invalid_archive", `archive member is duplicated: ${name}`);
|
|
79427
|
+
}
|
|
79428
|
+
const madeBy = header.readUInt16LE(4) >> 8;
|
|
79429
|
+
const unixMode = externalAttributes >>> 16;
|
|
79430
|
+
const fileType = unixMode & 61440;
|
|
79431
|
+
if (name.endsWith("/") || (externalAttributes & 16) !== 0 || madeBy === 3 && fileType !== 0 && fileType !== 32768) {
|
|
79432
|
+
throw new BenchmarkPhaseError("invalid_archive", `archive member must be a regular file: ${name}`);
|
|
79433
|
+
}
|
|
79434
|
+
if ((flags & 1) !== 0 || compressionMethod !== 0 && compressionMethod !== 8) {
|
|
79435
|
+
throw new BenchmarkPhaseError("invalid_archive", `archive member uses unsupported ZIP features: ${name}`);
|
|
79436
|
+
}
|
|
79437
|
+
if (uncompressedSize !== material.file_size_bytes) {
|
|
79438
|
+
throw new BenchmarkPhaseError("size_mismatch", `size mismatch for archive member ${name}`, { expected: material.file_size_bytes, actual: uncompressedSize });
|
|
79439
|
+
}
|
|
79440
|
+
const localHeader = readExactly(descriptor, 30, localHeaderOffset);
|
|
79441
|
+
if (localHeader.readUInt32LE(0) !== ZIP_LOCAL_SIGNATURE || localHeader.readUInt16LE(6) !== flags || localHeader.readUInt16LE(8) !== compressionMethod) {
|
|
79442
|
+
throw new BenchmarkPhaseError("invalid_archive", "ZIP local header is invalid");
|
|
79443
|
+
}
|
|
79444
|
+
const localNameLength = localHeader.readUInt16LE(26);
|
|
79445
|
+
const localExtraLength = localHeader.readUInt16LE(28);
|
|
79446
|
+
const localName = decodeZipPath(readExactly(descriptor, localNameLength, localHeaderOffset + 30), (flags & 2048) !== 0);
|
|
79447
|
+
const dataOffset = localHeaderOffset + 30 + localNameLength + localExtraLength;
|
|
79448
|
+
if (localName !== name || dataOffset + compressedSize > directoryOffset) {
|
|
79449
|
+
throw new BenchmarkPhaseError("invalid_archive", "ZIP member bounds are invalid");
|
|
79450
|
+
}
|
|
79451
|
+
found.set(name, {
|
|
79452
|
+
compressionMethod,
|
|
79453
|
+
compressedSize,
|
|
79454
|
+
dataOffset
|
|
79455
|
+
});
|
|
79456
|
+
}
|
|
79457
|
+
offset += recordSize;
|
|
79458
|
+
}
|
|
79459
|
+
if (offset !== directoryOffset + directorySize) {
|
|
79460
|
+
throw new BenchmarkPhaseError("invalid_archive", "ZIP central directory is invalid");
|
|
79461
|
+
}
|
|
79462
|
+
for (const memberPath of requested.keys()) {
|
|
79463
|
+
if (!found.has(memberPath)) {
|
|
79464
|
+
throw new BenchmarkPhaseError("missing_input", `archive member does not exist: ${memberPath}`);
|
|
79465
|
+
}
|
|
79466
|
+
}
|
|
79467
|
+
return found;
|
|
79468
|
+
} finally {
|
|
79469
|
+
fs81.closeSync(descriptor);
|
|
79470
|
+
}
|
|
79471
|
+
}
|
|
79472
|
+
async function writeVerifiedArchiveMember(source, destination, material, context) {
|
|
79473
|
+
fs81.mkdirSync(path88.dirname(destination), { recursive: true, mode: 448 });
|
|
79474
|
+
const temporary = `${destination}.${process.pid}.${crypto6.randomBytes(6).toString("hex")}.tmp`;
|
|
79475
|
+
const descriptor = fs81.openSync(temporary, "wx", 384);
|
|
79476
|
+
const hash = crypto6.createHash("sha256");
|
|
79477
|
+
let size2 = 0;
|
|
79478
|
+
try {
|
|
79479
|
+
for await (const value of source) {
|
|
79480
|
+
assertBudget(context);
|
|
79481
|
+
const chunk2 = Buffer.isBuffer(value) ? value : Buffer.from(value);
|
|
79482
|
+
size2 += chunk2.length;
|
|
79483
|
+
if (size2 > material.file_size_bytes) {
|
|
79484
|
+
throw new BenchmarkPhaseError("size_mismatch", `size mismatch for archive member ${material.archive_path}`, { expected: material.file_size_bytes, actual: size2 });
|
|
79485
|
+
}
|
|
79486
|
+
hash.update(chunk2);
|
|
79487
|
+
let offset = 0;
|
|
79488
|
+
while (offset < chunk2.length) {
|
|
79489
|
+
offset += fs81.writeSync(descriptor, chunk2, offset, chunk2.length - offset);
|
|
79490
|
+
}
|
|
79491
|
+
}
|
|
79492
|
+
if (size2 !== material.file_size_bytes) {
|
|
79493
|
+
throw new BenchmarkPhaseError("size_mismatch", `size mismatch for archive member ${material.archive_path}`, { expected: material.file_size_bytes, actual: size2 });
|
|
79494
|
+
}
|
|
79495
|
+
const actual = hash.digest("hex");
|
|
79496
|
+
if (actual !== material.file_sha256) {
|
|
79497
|
+
throw new BenchmarkPhaseError("digest_mismatch", `checksum mismatch for archive member ${material.archive_path}`, { expected: material.file_sha256, actual });
|
|
79498
|
+
}
|
|
79499
|
+
fs81.fsyncSync(descriptor);
|
|
79500
|
+
fs81.closeSync(descriptor);
|
|
79501
|
+
fs81.renameSync(temporary, destination);
|
|
79502
|
+
} catch (error2) {
|
|
79503
|
+
if (error2 instanceof BenchmarkPhaseError)
|
|
79504
|
+
throw error2;
|
|
79505
|
+
throw new BenchmarkPhaseError("invalid_archive", `archive member could not be extracted: ${material.archive_path}`);
|
|
79506
|
+
} finally {
|
|
79507
|
+
try {
|
|
79508
|
+
fs81.closeSync(descriptor);
|
|
79509
|
+
} catch {}
|
|
79510
|
+
fs81.rmSync(temporary, { force: true });
|
|
79511
|
+
}
|
|
79512
|
+
}
|
|
79513
|
+
function isZipArchive(archivePath) {
|
|
79514
|
+
const descriptor = fs81.openSync(archivePath, "r");
|
|
79515
|
+
try {
|
|
79516
|
+
const header = Buffer.alloc(4);
|
|
79517
|
+
return fs81.readSync(descriptor, header, 0, header.length, 0) === header.length && [
|
|
79518
|
+
ZIP_LOCAL_SIGNATURE,
|
|
79519
|
+
ZIP_EOCD_SIGNATURE,
|
|
79520
|
+
134695760
|
|
79521
|
+
].includes(header.readUInt32LE(0));
|
|
79522
|
+
} finally {
|
|
79523
|
+
fs81.closeSync(descriptor);
|
|
79524
|
+
}
|
|
79525
|
+
}
|
|
79526
|
+
function isGzipArchive(archivePath) {
|
|
79527
|
+
const descriptor = fs81.openSync(archivePath, "r");
|
|
79528
|
+
try {
|
|
79529
|
+
const header = Buffer.alloc(2);
|
|
79530
|
+
return fs81.readSync(descriptor, header, 0, header.length, 0) === header.length && header[0] === 31 && header[1] === 139;
|
|
79531
|
+
} finally {
|
|
79532
|
+
fs81.closeSync(descriptor);
|
|
79533
|
+
}
|
|
79534
|
+
}
|
|
79535
|
+
function archiveSourceCacheKey(material) {
|
|
79536
|
+
return [
|
|
79537
|
+
material.source,
|
|
79538
|
+
material.sha256,
|
|
79539
|
+
material.size_bytes,
|
|
79540
|
+
material.download_url_env ?? ""
|
|
79541
|
+
].join("\x00");
|
|
79542
|
+
}
|
|
79543
|
+
function archiveMemberCacheKey(material) {
|
|
79544
|
+
return [
|
|
79545
|
+
archiveSourceCacheKey(material),
|
|
79546
|
+
material.archive_path,
|
|
79547
|
+
material.file_sha256,
|
|
79548
|
+
material.file_size_bytes
|
|
79549
|
+
].join("\x00");
|
|
79550
|
+
}
|
|
79551
|
+
function archiveScanBudget(context) {
|
|
79552
|
+
let scanned = 0;
|
|
79553
|
+
return new Transform2({
|
|
79554
|
+
transform(chunk2, _encoding, callback) {
|
|
79555
|
+
try {
|
|
79556
|
+
assertBudget(context);
|
|
79557
|
+
scanned += chunk2.length;
|
|
79558
|
+
if (scanned > MAX_ARCHIVE_SCAN_BYTES) {
|
|
79559
|
+
throw new BenchmarkPhaseError("invalid_archive", "archive expands beyond the supported scan limit");
|
|
79560
|
+
}
|
|
79561
|
+
callback(null, chunk2);
|
|
79562
|
+
} catch (error2) {
|
|
79563
|
+
callback(error2);
|
|
79564
|
+
}
|
|
79565
|
+
}
|
|
79566
|
+
});
|
|
79567
|
+
}
|
|
79568
|
+
async function extractArchiveMembers(archivePath, outputRoot, materials, context) {
|
|
79569
|
+
const requested = new Map;
|
|
79570
|
+
for (const material of materials) {
|
|
79571
|
+
let memberPath;
|
|
79572
|
+
try {
|
|
79573
|
+
memberPath = safeRelPath(material.archive_path);
|
|
79574
|
+
} catch {
|
|
79575
|
+
throw new BenchmarkPhaseError("unsafe_path", "archive member path is unsafe");
|
|
79576
|
+
}
|
|
79577
|
+
const existing = requested.get(memberPath);
|
|
79578
|
+
if (existing && (existing.file_sha256 !== material.file_sha256 || existing.file_size_bytes !== material.file_size_bytes)) {
|
|
79579
|
+
throw new BenchmarkPhaseError("invalid_archive", `archive member has conflicting declarations: ${memberPath}`);
|
|
79580
|
+
}
|
|
79581
|
+
requested.set(memberPath, material);
|
|
79582
|
+
}
|
|
79583
|
+
const extracted = new Map;
|
|
79584
|
+
if (isZipArchive(archivePath)) {
|
|
79585
|
+
const indexed = findZipMembers(archivePath, requested, context);
|
|
79586
|
+
for (const [memberPath, material] of requested) {
|
|
79587
|
+
assertBudget(context);
|
|
79588
|
+
const destination = path88.resolve(outputRoot, memberPath);
|
|
79589
|
+
if (!isWithin(outputRoot, destination)) {
|
|
79590
|
+
throw new BenchmarkPhaseError("unsafe_path", `archive member escapes output root: ${memberPath}`);
|
|
79591
|
+
}
|
|
79592
|
+
const member = indexed.get(memberPath);
|
|
79593
|
+
const compressed = member.compressedSize === 0 ? Readable.from([]) : fs81.createReadStream(archivePath, {
|
|
79594
|
+
start: member.dataOffset,
|
|
79595
|
+
end: member.dataOffset + member.compressedSize - 1
|
|
79596
|
+
});
|
|
79597
|
+
const contents = member.compressionMethod === 8 ? compressed.pipe(createInflateRaw()) : compressed;
|
|
79598
|
+
await writeVerifiedArchiveMember(contents, destination, material, context);
|
|
79599
|
+
extracted.set(memberPath, destination);
|
|
79600
|
+
}
|
|
79601
|
+
return extracted;
|
|
79602
|
+
}
|
|
79603
|
+
const matches2 = new Map;
|
|
79604
|
+
let validationError;
|
|
79605
|
+
const unpack = co({
|
|
79606
|
+
cwd: outputRoot,
|
|
79607
|
+
strict: true,
|
|
79608
|
+
preserveOwner: false,
|
|
79609
|
+
filter: (entryPath, entryAny) => {
|
|
79610
|
+
assertBudget(context);
|
|
79611
|
+
const material = requested.get(entryPath);
|
|
79612
|
+
if (!material)
|
|
79613
|
+
return false;
|
|
79614
|
+
const count = (matches2.get(entryPath) ?? 0) + 1;
|
|
79615
|
+
matches2.set(entryPath, count);
|
|
79616
|
+
const entry = entryAny;
|
|
79617
|
+
if (count > 1) {
|
|
79618
|
+
validationError = new BenchmarkPhaseError("invalid_archive", `archive member is duplicated: ${entryPath}`);
|
|
79619
|
+
return false;
|
|
79620
|
+
}
|
|
79621
|
+
if (!["File", "OldFile", "ContiguousFile"].includes(entry.type)) {
|
|
79622
|
+
validationError = new BenchmarkPhaseError("invalid_archive", `archive member must be a regular file: ${entryPath}`);
|
|
79623
|
+
return false;
|
|
79624
|
+
}
|
|
79625
|
+
if (entry.size !== material.file_size_bytes) {
|
|
79626
|
+
validationError = new BenchmarkPhaseError("size_mismatch", `size mismatch for archive member ${entryPath}`, { expected: material.file_size_bytes, actual: entry.size });
|
|
79627
|
+
return false;
|
|
79628
|
+
}
|
|
79629
|
+
return true;
|
|
79630
|
+
}
|
|
79631
|
+
});
|
|
79632
|
+
const archive = fs81.createReadStream(archivePath);
|
|
79633
|
+
const scanBudget = archiveScanBudget(context);
|
|
79634
|
+
try {
|
|
79635
|
+
if (isGzipArchive(archivePath)) {
|
|
79636
|
+
await pipeline2(archive, createGunzip(), scanBudget, unpack);
|
|
79637
|
+
} else {
|
|
79638
|
+
await pipeline2(archive, scanBudget, unpack);
|
|
79639
|
+
}
|
|
79640
|
+
} catch (error2) {
|
|
79641
|
+
if (validationError)
|
|
79642
|
+
throw validationError;
|
|
79643
|
+
if (error2 instanceof BenchmarkPhaseError)
|
|
79644
|
+
throw error2;
|
|
79645
|
+
throw new BenchmarkPhaseError("invalid_archive", "archive members could not be extracted");
|
|
79646
|
+
}
|
|
79647
|
+
if (validationError)
|
|
79648
|
+
throw validationError;
|
|
79649
|
+
for (const [memberPath, material] of requested) {
|
|
79650
|
+
if (!matches2.has(memberPath)) {
|
|
79651
|
+
throw new BenchmarkPhaseError("missing_input", `archive member does not exist: ${memberPath}`);
|
|
79652
|
+
}
|
|
79653
|
+
const verified = await verifyInput(outputRoot, {
|
|
79654
|
+
source: memberPath,
|
|
79655
|
+
sha256: material.file_sha256,
|
|
79656
|
+
size_bytes: material.file_size_bytes
|
|
79657
|
+
});
|
|
79658
|
+
assertBudget(context);
|
|
79659
|
+
extracted.set(memberPath, verified.source);
|
|
79660
|
+
}
|
|
79661
|
+
return extracted;
|
|
78972
79662
|
}
|
|
78973
79663
|
async function atomicCopy(source, destination, mode, sourceRoot) {
|
|
78974
79664
|
fs81.mkdirSync(path88.dirname(destination), { recursive: true });
|
|
@@ -79017,21 +79707,31 @@ async function recordFile(root, filePath, rootName, kind = "file") {
|
|
|
79017
79707
|
fs81.closeSync(opened.fd);
|
|
79018
79708
|
}
|
|
79019
79709
|
}
|
|
79020
|
-
async function copyMaterial(material, sourceRoot, destinationRoot, destinationRootName, protectWorkspace) {
|
|
79021
|
-
const
|
|
79710
|
+
async function copyMaterial(material, sourceRoot, destinationRoot, destinationRootName, protectWorkspace, context) {
|
|
79711
|
+
const verifiedSource = material.kind === "archive_file" ? undefined : await cachedVerifiedInput(sourceRoot, material, context);
|
|
79712
|
+
const source = material.kind === "archive_file" ? context.preparedArchiveFiles.get(archiveMemberCacheKey(material)) : verifiedSource?.source;
|
|
79713
|
+
if (!source) {
|
|
79714
|
+
throw new BenchmarkPhaseError("invalid_archive", `archive member was not prepared: ${material.id}`);
|
|
79715
|
+
}
|
|
79022
79716
|
const destinationRel = protectWorkspace ? workspaceRel(material.destination, material.kind === "tar_gz") : normalizedRootRelative(material.destination);
|
|
79023
79717
|
if (!protectWorkspace && destinationRel.toLowerCase() === ".brainbase-benchmark-owner.json") {
|
|
79024
79718
|
throw new BenchmarkPhaseError("reserved_path", "benchmark input cannot replace the phase ownership marker");
|
|
79025
79719
|
}
|
|
79026
|
-
if (material.kind === "file") {
|
|
79720
|
+
if (material.kind === "file" || material.kind === "archive_file") {
|
|
79027
79721
|
if (destinationRel === ".") {
|
|
79028
79722
|
throw new BenchmarkPhaseError("unsafe_path", "file destination cannot be a directory root");
|
|
79029
79723
|
}
|
|
79030
79724
|
assertWritableDestination(destinationRoot, destinationRel);
|
|
79031
79725
|
const destination = path88.resolve(destinationRoot, destinationRel);
|
|
79032
|
-
|
|
79726
|
+
if (material.kind === "file") {
|
|
79727
|
+
await atomicCopy(source, destination, material.mode ?? verifiedSource?.mode, sourceRoot);
|
|
79728
|
+
} else {
|
|
79729
|
+
await atomicCopy(source, destination, material.mode, path88.dirname(source));
|
|
79730
|
+
}
|
|
79033
79731
|
const record3 = await recordFile(destinationRoot, destination, destinationRootName);
|
|
79034
|
-
|
|
79732
|
+
const expectedSha256 = material.kind === "archive_file" ? material.file_sha256 : material.sha256;
|
|
79733
|
+
const expectedSize = material.kind === "archive_file" ? material.file_size_bytes : material.size_bytes;
|
|
79734
|
+
if (record3.sha256 !== expectedSha256 || record3.size !== expectedSize) {
|
|
79035
79735
|
throw new BenchmarkPhaseError("evidence_tampered", `material changed while it was copied: ${material.source}`);
|
|
79036
79736
|
}
|
|
79037
79737
|
return [record3];
|
|
@@ -79072,17 +79772,39 @@ async function copyMaterial(material, sourceRoot, destinationRoot, destinationRo
|
|
|
79072
79772
|
fs81.rmSync(temporary, { recursive: true, force: true });
|
|
79073
79773
|
}
|
|
79074
79774
|
}
|
|
79075
|
-
async function preflightMaterial(material, sourceRoot, destinationRoot, protectWorkspace) {
|
|
79076
|
-
const source = await
|
|
79775
|
+
async function preflightMaterial(material, materials, sourceRoot, destinationRoot, protectWorkspace, context) {
|
|
79776
|
+
const source = (await cachedVerifiedInput(sourceRoot, material, context)).source;
|
|
79077
79777
|
const destinationRel = protectWorkspace ? workspaceRel(material.destination, material.kind === "tar_gz") : normalizedRootRelative(material.destination);
|
|
79078
79778
|
if (!protectWorkspace && destinationRel.toLowerCase() === ".brainbase-benchmark-owner.json") {
|
|
79079
79779
|
throw new BenchmarkPhaseError("reserved_path", "benchmark input cannot replace the phase ownership marker");
|
|
79080
79780
|
}
|
|
79081
|
-
if (material.kind === "file") {
|
|
79781
|
+
if (material.kind === "file" || material.kind === "archive_file") {
|
|
79082
79782
|
if (destinationRel === ".") {
|
|
79083
79783
|
throw new BenchmarkPhaseError("unsafe_path", "file destination cannot be a directory root");
|
|
79084
79784
|
}
|
|
79085
79785
|
assertWritableDestination(destinationRoot, destinationRel);
|
|
79786
|
+
if (material.kind === "archive_file") {
|
|
79787
|
+
const cacheKey = archiveMemberCacheKey(material);
|
|
79788
|
+
let extracted = context.preparedArchiveFiles.get(cacheKey);
|
|
79789
|
+
if (!extracted) {
|
|
79790
|
+
const temporary2 = fs81.mkdtempSync(path88.join(os16.tmpdir(), "brainbase-benchmark-member-"));
|
|
79791
|
+
context.temporaryRoots.add(temporary2);
|
|
79792
|
+
const archiveKey = archiveSourceCacheKey(material);
|
|
79793
|
+
const related = materials.filter((candidate) => candidate.kind === "archive_file" && archiveSourceCacheKey(candidate) === archiveKey);
|
|
79794
|
+
const extractedMembers = await extractArchiveMembers(source, temporary2, related, context);
|
|
79795
|
+
for (const candidate of related) {
|
|
79796
|
+
const prepared = extractedMembers.get(safeRelPath(candidate.archive_path));
|
|
79797
|
+
if (!prepared) {
|
|
79798
|
+
throw new BenchmarkPhaseError("missing_input", `archive member does not exist: ${candidate.archive_path}`);
|
|
79799
|
+
}
|
|
79800
|
+
context.preparedArchiveFiles.set(archiveMemberCacheKey(candidate), prepared);
|
|
79801
|
+
}
|
|
79802
|
+
extracted = context.preparedArchiveFiles.get(cacheKey);
|
|
79803
|
+
}
|
|
79804
|
+
if (!extracted) {
|
|
79805
|
+
throw new BenchmarkPhaseError("missing_input", `archive member does not exist: ${material.archive_path}`);
|
|
79806
|
+
}
|
|
79807
|
+
}
|
|
79086
79808
|
return [destinationRel];
|
|
79087
79809
|
}
|
|
79088
79810
|
const temporary = fs81.mkdtempSync(path88.join(os16.tmpdir(), "brainbase-benchmark-preflight-"));
|
|
@@ -79150,14 +79872,18 @@ function prepareOwnedDirectory(root, role, spec) {
|
|
|
79150
79872
|
}
|
|
79151
79873
|
function buildEnvironment(spec, secretNames, additions = {}) {
|
|
79152
79874
|
const env3 = {};
|
|
79875
|
+
const remoteUrlEnvNames = remoteUrlEnvironmentNames(spec);
|
|
79153
79876
|
for (const name of BASE_ENV_NAMES) {
|
|
79154
|
-
if (process.env[name] !== undefined)
|
|
79877
|
+
if (!remoteUrlEnvNames.has(name) && process.env[name] !== undefined) {
|
|
79155
79878
|
env3[name] = process.env[name];
|
|
79879
|
+
}
|
|
79156
79880
|
}
|
|
79157
79881
|
for (const [name, declared] of Object.entries(spec.environment)) {
|
|
79158
79882
|
env3[name] = declared.value;
|
|
79159
79883
|
}
|
|
79160
79884
|
for (const name of secretNames) {
|
|
79885
|
+
if (remoteUrlEnvNames.has(name))
|
|
79886
|
+
continue;
|
|
79161
79887
|
const value = process.env[name];
|
|
79162
79888
|
if (value === undefined) {
|
|
79163
79889
|
throw new BenchmarkPhaseError("missing_environment", `required environment variable is missing: ${name}`);
|
|
@@ -79169,7 +79895,11 @@ function buildEnvironment(spec, secretNames, additions = {}) {
|
|
|
79169
79895
|
}
|
|
79170
79896
|
function redactCommandOutput(data, spec) {
|
|
79171
79897
|
let value = data.toString("utf8");
|
|
79172
|
-
|
|
79898
|
+
const sensitiveNames = new Set([
|
|
79899
|
+
...spec.secret_env,
|
|
79900
|
+
...remoteUrlEnvironmentNames(spec)
|
|
79901
|
+
]);
|
|
79902
|
+
for (const name of sensitiveNames) {
|
|
79173
79903
|
const secret = process.env[name];
|
|
79174
79904
|
if (secret)
|
|
79175
79905
|
value = value.split(secret).join("[REDACTED]");
|
|
@@ -79339,16 +80069,19 @@ async function executeHydrate(spec, context) {
|
|
|
79339
80069
|
const plannedDestinations = [];
|
|
79340
80070
|
for (const material of spec.materials) {
|
|
79341
80071
|
assertBudget(context);
|
|
79342
|
-
|
|
79343
|
-
|
|
79344
|
-
|
|
80072
|
+
const verified = await downloadInputReference(spec.staging_root, material, context);
|
|
80073
|
+
plannedDestinations.push(...await preflightMaterial(material, spec.materials, spec.staging_root, spec.workspace_root, true, context));
|
|
80074
|
+
const record3 = stagedInputRecord(spec.staging_root, verified);
|
|
80075
|
+
if (!context.inputs.some((input) => input.path === record3.path && input.sha256 === record3.sha256 && input.size === record3.size)) {
|
|
80076
|
+
context.inputs.push(record3);
|
|
80077
|
+
}
|
|
79345
80078
|
}
|
|
79346
80079
|
validateDestinationGraph(plannedDestinations);
|
|
79347
80080
|
for (const material of spec.materials) {
|
|
79348
80081
|
assertBudget(context);
|
|
79349
80082
|
const started = Date.now();
|
|
79350
80083
|
try {
|
|
79351
|
-
const records = await copyMaterial(material, spec.staging_root, spec.workspace_root, "workspace", true);
|
|
80084
|
+
const records = await copyMaterial(material, spec.staging_root, spec.workspace_root, "workspace", true, context);
|
|
79352
80085
|
outputs.push(...records);
|
|
79353
80086
|
context.outputs.push(...records);
|
|
79354
80087
|
context.steps.push({
|
|
@@ -79422,6 +80155,7 @@ async function executeHydrate(spec, context) {
|
|
|
79422
80155
|
}
|
|
79423
80156
|
outputs.splice(0, outputs.length, ...finalOutputs);
|
|
79424
80157
|
context.outputs.splice(0, context.outputs.length, ...finalOutputs);
|
|
80158
|
+
removeRemoteHydrationInputs(spec);
|
|
79425
80159
|
assertBudget(context);
|
|
79426
80160
|
return outputs;
|
|
79427
80161
|
}
|
|
@@ -79486,6 +80220,234 @@ async function workspaceManifest(spec, context) {
|
|
|
79486
80220
|
}
|
|
79487
80221
|
return records.sort((a3, b4) => a3.path.localeCompare(b4.path));
|
|
79488
80222
|
}
|
|
80223
|
+
function candidateGlob(pattern) {
|
|
80224
|
+
let source = "^";
|
|
80225
|
+
for (let index = 0;index < pattern.length; index += 1) {
|
|
80226
|
+
const char = pattern[index];
|
|
80227
|
+
if (char === "*") {
|
|
80228
|
+
if (pattern[index + 1] === "*") {
|
|
80229
|
+
if (pattern[index + 2] === "/") {
|
|
80230
|
+
source += "(?:.*/)?";
|
|
80231
|
+
index += 2;
|
|
80232
|
+
} else {
|
|
80233
|
+
source += ".*";
|
|
80234
|
+
index += 1;
|
|
80235
|
+
}
|
|
80236
|
+
} else {
|
|
80237
|
+
source += "[^/]*";
|
|
80238
|
+
}
|
|
80239
|
+
continue;
|
|
80240
|
+
}
|
|
80241
|
+
if (char === "?") {
|
|
80242
|
+
source += "[^/]";
|
|
80243
|
+
continue;
|
|
80244
|
+
}
|
|
80245
|
+
source += char.replace(/[\\^$.*+?()[\]{}|]/g, "\\$&");
|
|
80246
|
+
}
|
|
80247
|
+
return new RegExp(`${source}$`);
|
|
80248
|
+
}
|
|
80249
|
+
function manifestDirectories(manifest, context) {
|
|
80250
|
+
const directories = new Set;
|
|
80251
|
+
for (const entry of manifest) {
|
|
80252
|
+
assertBudget(context);
|
|
80253
|
+
let current = path88.posix.dirname(entry.path);
|
|
80254
|
+
while (current !== ".") {
|
|
80255
|
+
assertBudget(context);
|
|
80256
|
+
directories.add(current);
|
|
80257
|
+
current = path88.posix.dirname(current);
|
|
80258
|
+
}
|
|
80259
|
+
}
|
|
80260
|
+
return [...directories].sort();
|
|
80261
|
+
}
|
|
80262
|
+
function candidateOutputFiles(output, manifest, context) {
|
|
80263
|
+
const matcher = candidateGlob(output.pattern);
|
|
80264
|
+
if (output.kind === "file") {
|
|
80265
|
+
const matched = [];
|
|
80266
|
+
for (const entry of manifest) {
|
|
80267
|
+
assertBudget(context);
|
|
80268
|
+
if (!matcher.test(entry.path))
|
|
80269
|
+
continue;
|
|
80270
|
+
if (entry.kind === "symlink") {
|
|
80271
|
+
throw new BenchmarkPhaseError("unsafe_path", `candidate output ${output.id} matches a symlink`);
|
|
80272
|
+
}
|
|
80273
|
+
matched.push(entry);
|
|
80274
|
+
}
|
|
80275
|
+
return {
|
|
80276
|
+
matchedCount: matched.length,
|
|
80277
|
+
files: matched
|
|
80278
|
+
};
|
|
80279
|
+
}
|
|
80280
|
+
for (const entry of manifest) {
|
|
80281
|
+
assertBudget(context);
|
|
80282
|
+
if (entry.kind === "symlink" && matcher.test(entry.path)) {
|
|
80283
|
+
throw new BenchmarkPhaseError("unsafe_path", `candidate output ${output.id} matches a symlink`);
|
|
80284
|
+
}
|
|
80285
|
+
}
|
|
80286
|
+
const directories = [];
|
|
80287
|
+
for (const directory of manifestDirectories(manifest, context)) {
|
|
80288
|
+
assertBudget(context);
|
|
80289
|
+
if (matcher.test(directory))
|
|
80290
|
+
directories.push(directory);
|
|
80291
|
+
}
|
|
80292
|
+
const selected = new Map;
|
|
80293
|
+
for (const directory of directories) {
|
|
80294
|
+
assertBudget(context);
|
|
80295
|
+
const prefix = `${directory}/`;
|
|
80296
|
+
for (const entry of manifest) {
|
|
80297
|
+
assertBudget(context);
|
|
80298
|
+
if (!entry.path.startsWith(prefix))
|
|
80299
|
+
continue;
|
|
80300
|
+
if (entry.kind === "symlink") {
|
|
80301
|
+
throw new BenchmarkPhaseError("unsafe_path", `candidate output ${output.id} contains a symlink`);
|
|
80302
|
+
}
|
|
80303
|
+
selected.set(entry.path, entry);
|
|
80304
|
+
}
|
|
80305
|
+
}
|
|
80306
|
+
return {
|
|
80307
|
+
matchedCount: directories.length,
|
|
80308
|
+
files: [...selected.values()].sort((left, right) => left.path.localeCompare(right.path))
|
|
80309
|
+
};
|
|
80310
|
+
}
|
|
80311
|
+
async function copyCandidateOutput(output, manifest, spec, context) {
|
|
80312
|
+
const selected = candidateOutputFiles(output, manifest, context);
|
|
80313
|
+
if (!output.required && selected.matchedCount === 0)
|
|
80314
|
+
return [];
|
|
80315
|
+
const minimumMatches = output.required ? Math.max(1, output.min_matches) : output.min_matches;
|
|
80316
|
+
if (selected.matchedCount < minimumMatches || selected.matchedCount > output.max_matches) {
|
|
80317
|
+
throw new BenchmarkPhaseError("candidate_output_match_count", `candidate output ${output.id} matched ${selected.matchedCount} ${output.kind}s`, {
|
|
80318
|
+
actual: selected.matchedCount,
|
|
80319
|
+
min_matches: minimumMatches,
|
|
80320
|
+
max_matches: output.max_matches
|
|
80321
|
+
});
|
|
80322
|
+
}
|
|
80323
|
+
let totalBytes = 0;
|
|
80324
|
+
for (const file of selected.files) {
|
|
80325
|
+
assertBudget(context);
|
|
80326
|
+
totalBytes += file.size;
|
|
80327
|
+
}
|
|
80328
|
+
if (totalBytes > output.max_total_bytes) {
|
|
80329
|
+
throw new BenchmarkPhaseError("candidate_output_size_exceeded", `candidate output ${output.id} exceeds its byte limit`, { actual: totalBytes, max_total_bytes: output.max_total_bytes });
|
|
80330
|
+
}
|
|
80331
|
+
const copied = [];
|
|
80332
|
+
for (const frozenFile of selected.files) {
|
|
80333
|
+
assertBudget(context);
|
|
80334
|
+
const source = path88.resolve(spec.workspace_root, safeRelPath(frozenFile.path));
|
|
80335
|
+
const destination = path88.resolve(spec.logs_root, "candidate-artifacts", output.id, safeRelPath(frozenFile.path));
|
|
80336
|
+
if (fs81.existsSync(destination)) {
|
|
80337
|
+
throw new BenchmarkPhaseError("destination_conflict", `candidate output destination already exists: ${output.id}/${frozenFile.path}`);
|
|
80338
|
+
}
|
|
80339
|
+
await atomicCopy(source, destination, frozenFile.mode, spec.workspace_root);
|
|
80340
|
+
const record3 = await recordFile(spec.logs_root, destination, "logs");
|
|
80341
|
+
if (record3.sha256 !== frozenFile.sha256 || record3.size !== frozenFile.size || record3.mode !== frozenFile.mode) {
|
|
80342
|
+
throw new BenchmarkPhaseError("evidence_tampered", `candidate output changed after the workspace freeze: ${frozenFile.path}`);
|
|
80343
|
+
}
|
|
80344
|
+
copied.push(record3);
|
|
80345
|
+
}
|
|
80346
|
+
return copied;
|
|
80347
|
+
}
|
|
80348
|
+
async function copyFrozenWorkspace(manifest, spec, context) {
|
|
80349
|
+
for (const entry of manifest) {
|
|
80350
|
+
assertBudget(context);
|
|
80351
|
+
if (entry.kind === "symlink") {
|
|
80352
|
+
throw new BenchmarkPhaseError("unsupported_workspace_symlink", `sandbox evaluator workspace cannot safely reproduce symlink: ${entry.path}`);
|
|
80353
|
+
}
|
|
80354
|
+
}
|
|
80355
|
+
const destinationRoot = fs81.mkdtempSync(path88.join(os16.tmpdir(), "brainbase-benchmark-workspace-"));
|
|
80356
|
+
fs81.chmodSync(destinationRoot, 448);
|
|
80357
|
+
context.temporaryRoots.add(destinationRoot);
|
|
80358
|
+
for (const frozenFile of manifest) {
|
|
80359
|
+
assertBudget(context);
|
|
80360
|
+
const source = path88.resolve(spec.workspace_root, safeRelPath(frozenFile.path));
|
|
80361
|
+
const destination = path88.resolve(destinationRoot, safeRelPath(frozenFile.path));
|
|
80362
|
+
await atomicCopy(source, destination, frozenFile.mode, spec.workspace_root);
|
|
80363
|
+
const copied = await recordFile(destinationRoot, destination, "workspace");
|
|
80364
|
+
if (copied.sha256 !== frozenFile.sha256 || copied.size !== frozenFile.size || copied.mode !== frozenFile.mode) {
|
|
80365
|
+
throw new BenchmarkPhaseError("evidence_tampered", `workspace changed after it was frozen: ${frozenFile.path}`);
|
|
80366
|
+
}
|
|
80367
|
+
}
|
|
80368
|
+
return destinationRoot;
|
|
80369
|
+
}
|
|
80370
|
+
function evaluatorTestsPath(evaluator, spec) {
|
|
80371
|
+
if (!evaluator.tests_path)
|
|
80372
|
+
return spec.tests_root;
|
|
80373
|
+
const relative = normalizedRootRelative(evaluator.tests_path);
|
|
80374
|
+
assertNoSymlinkTraversal(spec.tests_root, relative);
|
|
80375
|
+
const candidate = path88.resolve(spec.tests_root, relative);
|
|
80376
|
+
if (!isWithin(spec.tests_root, candidate) || !fs81.existsSync(candidate)) {
|
|
80377
|
+
throw new BenchmarkPhaseError("invalid_tests_path", `evaluator tests_path does not exist: ${evaluator.tests_path}`);
|
|
80378
|
+
}
|
|
80379
|
+
const stat = fs81.lstatSync(candidate);
|
|
80380
|
+
if (stat.isSymbolicLink() || !stat.isDirectory()) {
|
|
80381
|
+
throw new BenchmarkPhaseError("invalid_tests_path", `evaluator tests_path must be a directory: ${evaluator.tests_path}`);
|
|
80382
|
+
}
|
|
80383
|
+
return candidate;
|
|
80384
|
+
}
|
|
80385
|
+
var CriterionResultSchema = exports_external.object({
|
|
80386
|
+
criterion_key: IdSchema,
|
|
80387
|
+
outcome: exports_external.enum(["satisfied", "not_satisfied", "not_applicable"]),
|
|
80388
|
+
explanation: exports_external.string().min(1).max(MAX_CRITERION_EXPLANATION_LENGTH),
|
|
80389
|
+
evidence_ids: exports_external.array(exports_external.string().min(1).max(MAX_CRITERION_EVIDENCE_ID_LENGTH)).max(MAX_CRITERION_EVIDENCE_IDS)
|
|
80390
|
+
}).strict().superRefine((value, context) => {
|
|
80391
|
+
if (new Set(value.evidence_ids).size !== value.evidence_ids.length) {
|
|
80392
|
+
context.addIssue({
|
|
80393
|
+
code: exports_external.ZodIssueCode.custom,
|
|
80394
|
+
path: ["evidence_ids"],
|
|
80395
|
+
message: "evidence_ids must be unique"
|
|
80396
|
+
});
|
|
80397
|
+
}
|
|
80398
|
+
});
|
|
80399
|
+
var CriteriaResultFileSchema = exports_external.object({
|
|
80400
|
+
criteria: exports_external.array(CriterionResultSchema).max(MAX_CRITERIA_PER_EVALUATOR)
|
|
80401
|
+
}).strict();
|
|
80402
|
+
function readCriterionResults(resultPath, criterionKeys, allowedEvidenceIds) {
|
|
80403
|
+
let opened;
|
|
80404
|
+
try {
|
|
80405
|
+
opened = openRegularFileNoFollow(resultPath, "criterion result", path88.dirname(resultPath));
|
|
80406
|
+
} catch (error2) {
|
|
80407
|
+
if (error2.code === "ENOENT") {
|
|
80408
|
+
throw new BenchmarkPhaseError("missing_criterion_result", "sandbox evaluator did not write its criterion result");
|
|
80409
|
+
}
|
|
80410
|
+
throw error2;
|
|
80411
|
+
}
|
|
80412
|
+
try {
|
|
80413
|
+
if (opened.stat.size > MAX_CRITERIA_RESULT_BYTES) {
|
|
80414
|
+
throw new BenchmarkPhaseError("invalid_criterion_result", "criterion result exceeds the 1 MiB limit");
|
|
80415
|
+
}
|
|
80416
|
+
let raw;
|
|
80417
|
+
try {
|
|
80418
|
+
raw = JSON.parse(readDescriptor(opened.fd).toString("utf8"));
|
|
80419
|
+
} catch {
|
|
80420
|
+
throw new BenchmarkPhaseError("invalid_criterion_result", "criterion result is not valid JSON");
|
|
80421
|
+
}
|
|
80422
|
+
let parsed;
|
|
80423
|
+
try {
|
|
80424
|
+
parsed = CriteriaResultFileSchema.parse(raw);
|
|
80425
|
+
} catch (error2) {
|
|
80426
|
+
throw new BenchmarkPhaseError("invalid_criterion_result", "criterion result does not match the required schema", error2 instanceof exports_external.ZodError ? error2.issues : undefined);
|
|
80427
|
+
}
|
|
80428
|
+
const byKey = new Map;
|
|
80429
|
+
for (const criterion of parsed.criteria) {
|
|
80430
|
+
if (byKey.has(criterion.criterion_key)) {
|
|
80431
|
+
throw new BenchmarkPhaseError("invalid_criterion_result", `criterion result is duplicated: ${criterion.criterion_key}`);
|
|
80432
|
+
}
|
|
80433
|
+
byKey.set(criterion.criterion_key, criterion);
|
|
80434
|
+
}
|
|
80435
|
+
const expected = new Set(criterionKeys);
|
|
80436
|
+
const extra = [...byKey.keys()].filter((key2) => !expected.has(key2));
|
|
80437
|
+
const missing = criterionKeys.filter((key2) => !byKey.has(key2));
|
|
80438
|
+
if (extra.length > 0 || missing.length > 0) {
|
|
80439
|
+
throw new BenchmarkPhaseError("invalid_criterion_result", "criterion result keys do not match the evaluator plan", { missing, extra });
|
|
80440
|
+
}
|
|
80441
|
+
const allowedEvidence = new Set(allowedEvidenceIds);
|
|
80442
|
+
const unknownEvidenceIds = [...new Set(parsed.criteria.flatMap((criterion) => criterion.evidence_ids))].filter((evidenceId) => !allowedEvidence.has(evidenceId));
|
|
80443
|
+
if (unknownEvidenceIds.length > 0) {
|
|
80444
|
+
throw new BenchmarkPhaseError("invalid_criterion_result", "criterion result cites evidence outside the evaluator plan", { unknown_evidence_ids: unknownEvidenceIds });
|
|
80445
|
+
}
|
|
80446
|
+
return criterionKeys.map((key2) => byKey.get(key2));
|
|
80447
|
+
} finally {
|
|
80448
|
+
fs81.closeSync(opened.fd);
|
|
80449
|
+
}
|
|
80450
|
+
}
|
|
79489
80451
|
function trajectoryEvents(value) {
|
|
79490
80452
|
if (Array.isArray(value))
|
|
79491
80453
|
return value;
|
|
@@ -79504,7 +80466,7 @@ function eventType(event) {
|
|
|
79504
80466
|
}
|
|
79505
80467
|
return;
|
|
79506
80468
|
}
|
|
79507
|
-
async function evaluateOne(evaluator, spec, finalOutput, trajectory, frozenEvidence, context) {
|
|
80469
|
+
async function evaluateOne(evaluator, spec, manifest, finalOutput, trajectory, frozenEvidence, context) {
|
|
79508
80470
|
const started = Date.now();
|
|
79509
80471
|
const base2 = {
|
|
79510
80472
|
id: evaluator.id,
|
|
@@ -79516,11 +80478,11 @@ async function evaluateOne(evaluator, spec, finalOutput, trajectory, frozenEvide
|
|
|
79516
80478
|
};
|
|
79517
80479
|
if (evaluator.type === "output_assertion") {
|
|
79518
80480
|
const assertion = evaluator.assertion;
|
|
79519
|
-
let
|
|
80481
|
+
let verdict = false;
|
|
79520
80482
|
if (assertion.operator === "exact")
|
|
79521
|
-
|
|
80483
|
+
verdict = finalOutput === assertion.expected;
|
|
79522
80484
|
if (assertion.operator === "contains")
|
|
79523
|
-
|
|
80485
|
+
verdict = finalOutput.includes(assertion.expected);
|
|
79524
80486
|
if (assertion.operator === "regex") {
|
|
79525
80487
|
const regexResult = await runCommand({
|
|
79526
80488
|
id: `${evaluator.id}.regex`,
|
|
@@ -79546,17 +80508,17 @@ async function evaluateOne(evaluator, spec, finalOutput, trajectory, frozenEvide
|
|
|
79546
80508
|
if (regexResult.exitCode === 2) {
|
|
79547
80509
|
throw new BenchmarkPhaseError("invalid_evaluator", `invalid regex in evaluator: ${evaluator.id}`);
|
|
79548
80510
|
}
|
|
79549
|
-
|
|
80511
|
+
verdict = regexResult.exitCode === 0;
|
|
79550
80512
|
}
|
|
79551
|
-
return { ...base2, status:
|
|
80513
|
+
return { ...base2, status: verdict ? "passed" : "failed", verdict, duration_ms: Date.now() - started };
|
|
79552
80514
|
}
|
|
79553
80515
|
if (evaluator.type === "trajectory_assertion") {
|
|
79554
80516
|
const count = trajectory.filter((event) => eventType(event) === evaluator.event_type).length;
|
|
79555
|
-
const
|
|
80517
|
+
const verdict = count >= evaluator.min_count && (evaluator.max_count === undefined || count <= evaluator.max_count);
|
|
79556
80518
|
return {
|
|
79557
80519
|
...base2,
|
|
79558
|
-
status:
|
|
79559
|
-
verdict
|
|
80520
|
+
status: verdict ? "passed" : "failed",
|
|
80521
|
+
verdict,
|
|
79560
80522
|
duration_ms: Date.now() - started,
|
|
79561
80523
|
details: { count, min_count: evaluator.min_count, max_count: evaluator.max_count }
|
|
79562
80524
|
};
|
|
@@ -79577,16 +80539,16 @@ async function evaluateOne(evaluator, spec, finalOutput, trajectory, frozenEvide
|
|
|
79577
80539
|
throw new BenchmarkPhaseError("unsafe_path", `workspace assertion cannot target a symlink: ${relative}`);
|
|
79578
80540
|
}
|
|
79579
80541
|
const exists2 = stat !== null;
|
|
79580
|
-
let
|
|
80542
|
+
let verdict = false;
|
|
79581
80543
|
if (evaluator.assertion.operator === "exists")
|
|
79582
|
-
|
|
80544
|
+
verdict = exists2;
|
|
79583
80545
|
if (evaluator.assertion.operator === "not_exists")
|
|
79584
|
-
|
|
80546
|
+
verdict = !exists2;
|
|
79585
80547
|
if (evaluator.assertion.operator === "sha256") {
|
|
79586
80548
|
if (stat?.isFile()) {
|
|
79587
80549
|
const opened = openRegularFileNoFollow(candidate, `workspace assertion ${relative}`, spec.workspace_root);
|
|
79588
80550
|
try {
|
|
79589
|
-
|
|
80551
|
+
verdict = await sha256OfDescriptor(opened.fd) === evaluator.assertion.expected;
|
|
79590
80552
|
} finally {
|
|
79591
80553
|
fs81.closeSync(opened.fd);
|
|
79592
80554
|
}
|
|
@@ -79596,37 +80558,82 @@ async function evaluateOne(evaluator, spec, finalOutput, trajectory, frozenEvide
|
|
|
79596
80558
|
if (stat?.isFile()) {
|
|
79597
80559
|
const opened = openRegularFileNoFollow(candidate, `workspace assertion ${relative}`, spec.workspace_root);
|
|
79598
80560
|
try {
|
|
79599
|
-
|
|
80561
|
+
verdict = readDescriptor(opened.fd).toString("utf8").includes(evaluator.assertion.expected);
|
|
79600
80562
|
} finally {
|
|
79601
80563
|
fs81.closeSync(opened.fd);
|
|
79602
80564
|
}
|
|
79603
80565
|
}
|
|
79604
80566
|
}
|
|
79605
|
-
return { ...base2, status:
|
|
80567
|
+
return { ...base2, status: verdict ? "passed" : "failed", verdict, duration_ms: Date.now() - started };
|
|
80568
|
+
}
|
|
80569
|
+
let isolatedWorkspace;
|
|
80570
|
+
let privateResultRoot;
|
|
80571
|
+
try {
|
|
80572
|
+
const evaluatorWorkspace = await copyFrozenWorkspace(manifest, spec, context);
|
|
80573
|
+
isolatedWorkspace = evaluatorWorkspace;
|
|
80574
|
+
const testsPath = evaluatorTestsPath(evaluator, spec);
|
|
80575
|
+
const commandRoot = evaluator.root === "workspace" ? evaluatorWorkspace : spec.tests_root;
|
|
80576
|
+
const command = { id: evaluator.id, ...evaluator.command };
|
|
80577
|
+
const environment = {
|
|
80578
|
+
BRAINBASE_BENCHMARK_WORKSPACE: evaluatorWorkspace,
|
|
80579
|
+
BRAINBASE_BENCHMARK_TESTS: testsPath,
|
|
80580
|
+
BRAINBASE_BENCHMARK_LOGS: spec.logs_root,
|
|
80581
|
+
BRAINBASE_BENCHMARK_FINAL_OUTPUT: frozenEvidence.finalOutputPath,
|
|
80582
|
+
BRAINBASE_BENCHMARK_TRAJECTORY: frozenEvidence.trajectoryPath
|
|
80583
|
+
};
|
|
80584
|
+
let criterionResultPath;
|
|
80585
|
+
if (evaluator.criterion_keys) {
|
|
80586
|
+
privateResultRoot = fs81.mkdtempSync(path88.join(os16.tmpdir(), "brainbase-benchmark-criteria-"));
|
|
80587
|
+
fs81.chmodSync(privateResultRoot, 448);
|
|
80588
|
+
context.temporaryRoots.add(privateResultRoot);
|
|
80589
|
+
criterionResultPath = path88.join(privateResultRoot, "result.json");
|
|
80590
|
+
environment.BRAINBASE_BENCHMARK_CRITERIA_RESULT = criterionResultPath;
|
|
80591
|
+
}
|
|
80592
|
+
const result2 = await runCommand(command, commandRoot, spec, context, environment);
|
|
80593
|
+
const stdout = await writeLog(spec.logs_root, `${evaluator.id}.stdout.log`, result2.stdout, spec);
|
|
80594
|
+
const stderr = await writeLog(spec.logs_root, `${evaluator.id}.stderr.log`, result2.stderr, spec);
|
|
80595
|
+
let criterionResults;
|
|
80596
|
+
try {
|
|
80597
|
+
criterionResults = evaluator.criterion_keys && criterionResultPath ? readCriterionResults(criterionResultPath, evaluator.criterion_keys, evaluator.allowed_evidence_ids ?? []) : undefined;
|
|
80598
|
+
} catch (error2) {
|
|
80599
|
+
const normalized = stableError(error2);
|
|
80600
|
+
return {
|
|
80601
|
+
...base2,
|
|
80602
|
+
status: "errored",
|
|
80603
|
+
verdict: null,
|
|
80604
|
+
duration_ms: result2.durationMs,
|
|
80605
|
+
details: {
|
|
80606
|
+
exit_code: result2.exitCode,
|
|
80607
|
+
error_code: normalized?.code ?? "invalid_criterion_result",
|
|
80608
|
+
error_message: normalized?.message ?? "criterion result validation failed"
|
|
80609
|
+
},
|
|
80610
|
+
stdout,
|
|
80611
|
+
stderr
|
|
80612
|
+
};
|
|
80613
|
+
}
|
|
80614
|
+
const verdict = result2.exitCode === 0;
|
|
80615
|
+
return {
|
|
80616
|
+
...base2,
|
|
80617
|
+
status: verdict ? "passed" : "failed",
|
|
80618
|
+
verdict,
|
|
80619
|
+
duration_ms: result2.durationMs,
|
|
80620
|
+
details: { exit_code: result2.exitCode },
|
|
80621
|
+
stdout,
|
|
80622
|
+
stderr,
|
|
80623
|
+
...criterionResults ? { criterion_results: criterionResults } : {}
|
|
80624
|
+
};
|
|
80625
|
+
} finally {
|
|
80626
|
+
for (const temporary of [privateResultRoot, isolatedWorkspace]) {
|
|
80627
|
+
if (!temporary)
|
|
80628
|
+
continue;
|
|
80629
|
+
fs81.rmSync(temporary, { recursive: true, force: true });
|
|
80630
|
+
context.temporaryRoots.delete(temporary);
|
|
80631
|
+
}
|
|
79606
80632
|
}
|
|
79607
|
-
const commandRoot = evaluator.root === "workspace" ? spec.workspace_root : spec.tests_root;
|
|
79608
|
-
const command = { id: evaluator.id, ...evaluator.command };
|
|
79609
|
-
const result2 = await runCommand(command, commandRoot, spec, context, {
|
|
79610
|
-
BRAINBASE_BENCHMARK_WORKSPACE: spec.workspace_root,
|
|
79611
|
-
BRAINBASE_BENCHMARK_TESTS: spec.tests_root,
|
|
79612
|
-
BRAINBASE_BENCHMARK_LOGS: spec.logs_root,
|
|
79613
|
-
BRAINBASE_BENCHMARK_FINAL_OUTPUT: frozenEvidence.finalOutputPath,
|
|
79614
|
-
BRAINBASE_BENCHMARK_TRAJECTORY: frozenEvidence.trajectoryPath
|
|
79615
|
-
});
|
|
79616
|
-
const stdout = await writeLog(spec.logs_root, `${evaluator.id}.stdout.log`, result2.stdout, spec);
|
|
79617
|
-
const stderr = await writeLog(spec.logs_root, `${evaluator.id}.stderr.log`, result2.stderr, spec);
|
|
79618
|
-
const verdict = result2.exitCode === 0;
|
|
79619
|
-
return {
|
|
79620
|
-
...base2,
|
|
79621
|
-
status: verdict ? "passed" : "failed",
|
|
79622
|
-
verdict,
|
|
79623
|
-
duration_ms: result2.durationMs,
|
|
79624
|
-
details: { exit_code: result2.exitCode },
|
|
79625
|
-
stdout,
|
|
79626
|
-
stderr
|
|
79627
|
-
};
|
|
79628
80633
|
}
|
|
79629
80634
|
async function executeEvaluate(spec, context) {
|
|
80635
|
+
await downloadInputReference(spec.staging_root, spec.evidence.final_output, context);
|
|
80636
|
+
await downloadInputReference(spec.staging_root, spec.evidence.trajectory, context);
|
|
79630
80637
|
const finalOutput = await readEvidence(spec.staging_root, spec.evidence.final_output);
|
|
79631
80638
|
const trajectoryEvidence = await readEvidence(spec.staging_root, spec.evidence.trajectory);
|
|
79632
80639
|
context.inputs.push(finalOutput.record, trajectoryEvidence.record);
|
|
@@ -79640,9 +80647,12 @@ async function executeEvaluate(spec, context) {
|
|
|
79640
80647
|
const plannedReferences = [];
|
|
79641
80648
|
for (const reference of spec.references) {
|
|
79642
80649
|
assertBudget(context);
|
|
79643
|
-
|
|
79644
|
-
|
|
79645
|
-
|
|
80650
|
+
const verified = await downloadInputReference(spec.staging_root, reference, context);
|
|
80651
|
+
plannedReferences.push(...await preflightMaterial(reference, spec.references, spec.staging_root, spec.tests_root, false, context));
|
|
80652
|
+
const record3 = stagedInputRecord(spec.staging_root, verified);
|
|
80653
|
+
if (!context.inputs.some((input) => input.path === record3.path && input.sha256 === record3.sha256 && input.size === record3.size)) {
|
|
80654
|
+
context.inputs.push(record3);
|
|
80655
|
+
}
|
|
79646
80656
|
}
|
|
79647
80657
|
validateDestinationGraph(plannedReferences);
|
|
79648
80658
|
assertBudget(context);
|
|
@@ -79691,6 +80701,11 @@ async function executeEvaluate(spec, context) {
|
|
|
79691
80701
|
outputs.push(artifact);
|
|
79692
80702
|
context.outputs.push(artifact);
|
|
79693
80703
|
}
|
|
80704
|
+
for (const candidateOutput of spec.candidate_outputs) {
|
|
80705
|
+
const copied = await copyCandidateOutput(candidateOutput, manifest, spec, context);
|
|
80706
|
+
outputs.push(...copied);
|
|
80707
|
+
context.outputs.push(...copied);
|
|
80708
|
+
}
|
|
79694
80709
|
if (spec.capture_workspace_archive) {
|
|
79695
80710
|
const regularFiles = manifest.filter((entry) => entry.kind !== "symlink").map((entry) => entry.path);
|
|
79696
80711
|
const archive = path88.join(spec.logs_root, "candidate-workspace.tar.gz");
|
|
@@ -79707,7 +80722,7 @@ async function executeEvaluate(spec, context) {
|
|
|
79707
80722
|
}
|
|
79708
80723
|
await verifyRecordsUnchanged(manifest, spec);
|
|
79709
80724
|
for (const reference of spec.references) {
|
|
79710
|
-
const referenceOutputs = await copyMaterial(reference, spec.staging_root, spec.tests_root, "tests", false);
|
|
80725
|
+
const referenceOutputs = await copyMaterial(reference, spec.staging_root, spec.tests_root, "tests", false, context);
|
|
79711
80726
|
outputs.push(...referenceOutputs);
|
|
79712
80727
|
context.outputs.push(...referenceOutputs);
|
|
79713
80728
|
}
|
|
@@ -79718,8 +80733,9 @@ async function executeEvaluate(spec, context) {
|
|
|
79718
80733
|
for (const evaluator of executionOrder) {
|
|
79719
80734
|
assertBudget(context);
|
|
79720
80735
|
const started = Date.now();
|
|
80736
|
+
let requiredResultError;
|
|
79721
80737
|
try {
|
|
79722
|
-
const evaluated = await evaluateOne(evaluator, spec, finalOutput.buffer.toString("utf8"), trajectory, { finalOutputPath, trajectoryPath }, context);
|
|
80738
|
+
const evaluated = await evaluateOne(evaluator, spec, manifest, finalOutput.buffer.toString("utf8"), trajectory, { finalOutputPath, trajectoryPath }, context);
|
|
79723
80739
|
assertBudget(context);
|
|
79724
80740
|
evaluators.push(evaluated);
|
|
79725
80741
|
context.evaluators.push(evaluated);
|
|
@@ -79731,6 +80747,11 @@ async function executeEvaluate(spec, context) {
|
|
|
79731
80747
|
outputs.push(evaluated.stderr);
|
|
79732
80748
|
context.outputs.push(evaluated.stderr);
|
|
79733
80749
|
}
|
|
80750
|
+
if (evaluated.status === "errored" && evaluator.required) {
|
|
80751
|
+
const errorCode = typeof evaluated.details?.error_code === "string" ? evaluated.details.error_code : "evaluator_execution_failed";
|
|
80752
|
+
const errorMessage2 = typeof evaluated.details?.error_message === "string" ? evaluated.details.error_message : "required evaluator execution failed";
|
|
80753
|
+
requiredResultError = new BenchmarkPhaseError(errorCode, errorMessage2);
|
|
80754
|
+
}
|
|
79734
80755
|
} catch (error2) {
|
|
79735
80756
|
const normalized = stableError(error2);
|
|
79736
80757
|
const errored = {
|
|
@@ -79754,6 +80775,8 @@ async function executeEvaluate(spec, context) {
|
|
|
79754
80775
|
throw error2;
|
|
79755
80776
|
assertBudget(context);
|
|
79756
80777
|
}
|
|
80778
|
+
if (requiredResultError)
|
|
80779
|
+
throw requiredResultError;
|
|
79757
80780
|
}
|
|
79758
80781
|
await verifyRecordsUnchanged([...context.inputs, ...context.outputs.slice(0, frozenOutputCount)], spec);
|
|
79759
80782
|
if (!verifyOwnedDirectory(spec.tests_root, "tests", spec) || !verifyOwnedDirectory(spec.logs_root, "logs", spec)) {
|
|
@@ -79857,19 +80880,26 @@ function prepareBenchmarkPhaseInvocation(specPathInput, resultPathInput, expecte
|
|
|
79857
80880
|
spec_digest: digest,
|
|
79858
80881
|
timeout_ms: spec.budget.timeout_ms
|
|
79859
80882
|
};
|
|
80883
|
+
let cachedResult;
|
|
79860
80884
|
if (fs81.existsSync(resultPath)) {
|
|
79861
80885
|
try {
|
|
79862
80886
|
const cached2 = JSON.parse(fs81.readFileSync(resultPath, "utf8"));
|
|
79863
80887
|
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
|
-
};
|
|
80888
|
+
cachedResult = cached2;
|
|
79870
80889
|
}
|
|
79871
80890
|
} catch {}
|
|
79872
80891
|
}
|
|
80892
|
+
if (cachedResult) {
|
|
80893
|
+
if (spec.phase === "hydrate") {
|
|
80894
|
+
removeRemoteHydrationInputs(spec);
|
|
80895
|
+
}
|
|
80896
|
+
return {
|
|
80897
|
+
ok: true,
|
|
80898
|
+
invocation,
|
|
80899
|
+
spec_bytes: bytes,
|
|
80900
|
+
cached_result: cachedResult
|
|
80901
|
+
};
|
|
80902
|
+
}
|
|
79873
80903
|
return { ok: true, invocation, spec_bytes: bytes };
|
|
79874
80904
|
} catch (error2) {
|
|
79875
80905
|
return {
|
|
@@ -79966,14 +80996,21 @@ async function runBenchmarkPhase(specPathInput, resultPathInput, expectedPhase,
|
|
|
79966
80996
|
throw new BenchmarkPhaseError("invalid_result_path", `result path must be ${expectedResultPath}`);
|
|
79967
80997
|
}
|
|
79968
80998
|
resultPathValidated = true;
|
|
80999
|
+
let cachedResult;
|
|
79969
81000
|
if (fs81.existsSync(resultPath)) {
|
|
79970
81001
|
try {
|
|
79971
81002
|
const cached2 = JSON.parse(fs81.readFileSync(resultPath, "utf8"));
|
|
79972
81003
|
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
|
-
|
|
81004
|
+
cachedResult = cached2;
|
|
79974
81005
|
}
|
|
79975
81006
|
} catch {}
|
|
79976
81007
|
}
|
|
81008
|
+
if (cachedResult) {
|
|
81009
|
+
if (spec.phase === "hydrate") {
|
|
81010
|
+
removeRemoteHydrationInputs(spec);
|
|
81011
|
+
}
|
|
81012
|
+
return { exitCode: 0, result: cachedResult };
|
|
81013
|
+
}
|
|
79977
81014
|
context = {
|
|
79978
81015
|
deadline: started + spec.budget.timeout_ms,
|
|
79979
81016
|
remainingOutputBytes: spec.budget.max_output_bytes,
|
|
@@ -79981,7 +81018,10 @@ async function runBenchmarkPhase(specPathInput, resultPathInput, expectedPhase,
|
|
|
79981
81018
|
evaluators: [],
|
|
79982
81019
|
inputs: [],
|
|
79983
81020
|
outputs: [],
|
|
79984
|
-
logsOwned: false
|
|
81021
|
+
logsOwned: false,
|
|
81022
|
+
verifiedInputs: new Map,
|
|
81023
|
+
preparedArchiveFiles: new Map,
|
|
81024
|
+
temporaryRoots: new Set
|
|
79985
81025
|
};
|
|
79986
81026
|
if (spec.phase === "hydrate") {
|
|
79987
81027
|
outputs = await executeHydrate(spec, context);
|
|
@@ -80001,6 +81041,11 @@ async function runBenchmarkPhase(specPathInput, resultPathInput, expectedPhase,
|
|
|
80001
81041
|
evaluators = context.evaluators;
|
|
80002
81042
|
}
|
|
80003
81043
|
}
|
|
81044
|
+
if (context) {
|
|
81045
|
+
for (const temporary of context.temporaryRoots) {
|
|
81046
|
+
fs81.rmSync(temporary, { recursive: true, force: true });
|
|
81047
|
+
}
|
|
81048
|
+
}
|
|
80004
81049
|
const result2 = {
|
|
80005
81050
|
schema_version: SCHEMA_VERSION,
|
|
80006
81051
|
cli_version: VERSION,
|