@brainbase-labs/cli 0.25.0-eng1209.4 → 0.25.0-eng1209.5

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.
Files changed (2) hide show
  1. package/dist/index.js +104 -9
  2. 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.25.0-eng1209.4",
36011
+ version: "0.25.0-eng1209.5",
36012
36012
  description: "Pack, share, and install agent templates across harnesses (Claude Code, Codex, ...).",
36013
36013
  type: "module",
36014
36014
  bin: {
@@ -80234,6 +80234,42 @@ async function workspaceManifest(spec, context) {
80234
80234
  }
80235
80235
  return records.sort((a3, b4) => a3.path.localeCompare(b4.path));
80236
80236
  }
80237
+ function treeBytes(root, context) {
80238
+ let totalBytes = 0;
80239
+ const stack = [path88.resolve(root)];
80240
+ while (stack.length > 0) {
80241
+ const directory = stack.pop();
80242
+ const entries = fs81.readdirSync(directory, { withFileTypes: true });
80243
+ for (const entry of entries) {
80244
+ assertBudget(context);
80245
+ const candidate = path88.join(directory, entry.name);
80246
+ const stat = fs81.lstatSync(candidate);
80247
+ if (stat.isDirectory()) {
80248
+ stack.push(candidate);
80249
+ continue;
80250
+ }
80251
+ if (stat.isFile()) {
80252
+ totalBytes += stat.size;
80253
+ continue;
80254
+ }
80255
+ if (stat.isSymbolicLink()) {
80256
+ totalBytes += Buffer.byteLength(fs81.readlinkSync(candidate));
80257
+ continue;
80258
+ }
80259
+ throw new BenchmarkPhaseError("unsafe_path", `evaluate output contains an unsupported filesystem entry: ${path88.relative(root, candidate)}`);
80260
+ }
80261
+ }
80262
+ return totalBytes;
80263
+ }
80264
+ function assertEvaluateOutputBudget(spec, context, additionalBytes = 0) {
80265
+ const actual = treeBytes(spec.logs_root, context) + additionalBytes;
80266
+ if (actual > spec.workspace_limits.max_total_bytes) {
80267
+ throw new BenchmarkPhaseError("workspace_limit_exceeded", "evaluate output bytes exceed the workspace byte limit", {
80268
+ actual,
80269
+ max_total_bytes: spec.workspace_limits.max_total_bytes
80270
+ });
80271
+ }
80272
+ }
80237
80273
  function candidateGlob(pattern) {
80238
80274
  let source = "^";
80239
80275
  for (let index = 0;index < pattern.length; index += 1) {
@@ -80392,13 +80428,48 @@ async function copyFrozenWorkspace(manifest, spec, context) {
80392
80428
  }
80393
80429
  return destinationRoot;
80394
80430
  }
80395
- function evaluatorTestsPath(evaluator, spec) {
80431
+ async function copyEvaluatorTests(spec, context) {
80432
+ const destinationRoot = fs81.mkdtempSync(path88.join(os16.tmpdir(), "brainbase-benchmark-tests-"));
80433
+ fs81.chmodSync(destinationRoot, 448);
80434
+ context.temporaryRoots.add(destinationRoot);
80435
+ const stack = [{ source: spec.tests_root, destination: destinationRoot }];
80436
+ while (stack.length > 0) {
80437
+ assertBudget(context);
80438
+ const current = stack.pop();
80439
+ const entries = fs81.readdirSync(current.source, { withFileTypes: true }).sort((left, right) => left.name.localeCompare(right.name));
80440
+ for (const entry of entries) {
80441
+ assertBudget(context);
80442
+ const source = path88.join(current.source, entry.name);
80443
+ const destination = path88.join(current.destination, entry.name);
80444
+ const stat = fs81.lstatSync(source);
80445
+ if (stat.isSymbolicLink()) {
80446
+ throw new BenchmarkPhaseError("unsafe_path", `evaluator reference tree contains a symlink: ${path88.relative(spec.tests_root, source)}`);
80447
+ }
80448
+ if (stat.isDirectory()) {
80449
+ fs81.mkdirSync(destination, { recursive: true, mode: stat.mode & 511 });
80450
+ stack.push({ source, destination });
80451
+ continue;
80452
+ }
80453
+ if (!stat.isFile()) {
80454
+ throw new BenchmarkPhaseError("unsafe_path", `evaluator reference tree contains an unsupported filesystem entry: ${path88.relative(spec.tests_root, source)}`);
80455
+ }
80456
+ await atomicCopy(source, destination, stat.mode & 511, spec.tests_root);
80457
+ const sourceRecord = await recordFile(spec.tests_root, source, "tests");
80458
+ const copiedRecord = await recordFile(destinationRoot, destination, "tests");
80459
+ if (copiedRecord.path !== sourceRecord.path || copiedRecord.sha256 !== sourceRecord.sha256 || copiedRecord.size !== sourceRecord.size || copiedRecord.mode !== sourceRecord.mode) {
80460
+ throw new BenchmarkPhaseError("evidence_tampered", `evaluator reference changed while it was copied: ${sourceRecord.path}`);
80461
+ }
80462
+ }
80463
+ }
80464
+ return destinationRoot;
80465
+ }
80466
+ function evaluatorTestsPath(evaluator, testsRoot) {
80396
80467
  if (!evaluator.tests_path)
80397
- return spec.tests_root;
80468
+ return testsRoot;
80398
80469
  const relative = normalizedRootRelative(evaluator.tests_path);
80399
- assertNoSymlinkTraversal(spec.tests_root, relative);
80400
- const candidate = path88.resolve(spec.tests_root, relative);
80401
- if (!isWithin(spec.tests_root, candidate) || !fs81.existsSync(candidate)) {
80470
+ assertNoSymlinkTraversal(testsRoot, relative);
80471
+ const candidate = path88.resolve(testsRoot, relative);
80472
+ if (!isWithin(testsRoot, candidate) || !fs81.existsSync(candidate)) {
80402
80473
  throw new BenchmarkPhaseError("invalid_tests_path", `evaluator tests_path does not exist: ${evaluator.tests_path}`);
80403
80474
  }
80404
80475
  const stat = fs81.lstatSync(candidate);
@@ -80623,12 +80694,14 @@ async function evaluateOne(evaluator, spec, manifest, finalOutput, trajectory, f
80623
80694
  };
80624
80695
  }
80625
80696
  let isolatedWorkspace;
80697
+ let isolatedTestsRoot;
80626
80698
  let privateResultRoot;
80627
80699
  try {
80628
80700
  const evaluatorWorkspace = await copyFrozenWorkspace(manifest, spec, context);
80629
80701
  isolatedWorkspace = evaluatorWorkspace;
80630
- const testsPath = evaluatorTestsPath(evaluator, spec);
80631
- const commandRoot = evaluator.root === "workspace" ? evaluatorWorkspace : spec.tests_root;
80702
+ isolatedTestsRoot = await copyEvaluatorTests(spec, context);
80703
+ const testsPath = evaluatorTestsPath(evaluator, isolatedTestsRoot);
80704
+ const commandRoot = evaluator.root === "workspace" ? evaluatorWorkspace : isolatedTestsRoot;
80632
80705
  const command = { id: evaluator.id, ...evaluator.command };
80633
80706
  const environment = {
80634
80707
  BRAINBASE_BENCHMARK_WORKSPACE: evaluatorWorkspace,
@@ -80679,7 +80752,7 @@ async function evaluateOne(evaluator, spec, manifest, finalOutput, trajectory, f
80679
80752
  ...criterionResults ? { criterion_results: criterionResults } : {}
80680
80753
  };
80681
80754
  } finally {
80682
- for (const temporary of [privateResultRoot, isolatedWorkspace]) {
80755
+ for (const temporary of [privateResultRoot, isolatedTestsRoot, isolatedWorkspace]) {
80683
80756
  if (!temporary)
80684
80757
  continue;
80685
80758
  fs81.rmSync(temporary, { recursive: true, force: true });
@@ -80728,6 +80801,7 @@ async function executeEvaluate(spec, context) {
80728
80801
  ];
80729
80802
  outputs.push(...frozenEvidenceRecords);
80730
80803
  context.outputs.push(...frozenEvidenceRecords);
80804
+ assertEvaluateOutputBudget(spec, context);
80731
80805
  assertBudget(context);
80732
80806
  const manifest = await workspaceManifest(spec, context);
80733
80807
  const manifestPath2 = path88.join(spec.logs_root, "candidate-workspace-manifest.json");
@@ -80740,6 +80814,7 @@ async function executeEvaluate(spec, context) {
80740
80814
  const manifestRecord = await recordFile(spec.logs_root, manifestPath2, "logs");
80741
80815
  outputs.push(manifestRecord);
80742
80816
  context.outputs.push(manifestRecord);
80817
+ assertEvaluateOutputBudget(spec, context);
80743
80818
  for (const artifactRelInput of spec.candidate_artifacts) {
80744
80819
  const artifactRel = workspaceRel(artifactRelInput);
80745
80820
  assertNoSymlinkTraversal(spec.workspace_root, artifactRel);
@@ -80757,11 +80832,13 @@ async function executeEvaluate(spec, context) {
80757
80832
  outputs.push(artifact);
80758
80833
  context.outputs.push(artifact);
80759
80834
  }
80835
+ assertEvaluateOutputBudget(spec, context);
80760
80836
  for (const candidateOutput of spec.candidate_outputs) {
80761
80837
  const copied = await copyCandidateOutput(candidateOutput, manifest, spec, context);
80762
80838
  outputs.push(...copied);
80763
80839
  context.outputs.push(...copied);
80764
80840
  }
80841
+ assertEvaluateOutputBudget(spec, context);
80765
80842
  if (spec.capture_workspace_archive) {
80766
80843
  const regularFiles = manifest.filter((entry) => entry.kind !== "symlink").map((entry) => entry.path);
80767
80844
  const archive = path88.join(spec.logs_root, "candidate-workspace.tar.gz");
@@ -80775,6 +80852,7 @@ async function executeEvaluate(spec, context) {
80775
80852
  const archiveRecord = await recordFile(spec.logs_root, archive, "logs");
80776
80853
  outputs.push(archiveRecord);
80777
80854
  context.outputs.push(archiveRecord);
80855
+ assertEvaluateOutputBudget(spec, context);
80778
80856
  }
80779
80857
  await verifyRecordsUnchanged(manifest, spec);
80780
80858
  for (const reference of spec.references) {
@@ -80833,12 +80911,16 @@ async function executeEvaluate(spec, context) {
80833
80911
  }
80834
80912
  if (requiredResultError)
80835
80913
  throw requiredResultError;
80914
+ if (evaluator.type === "sandbox_command") {
80915
+ assertEvaluateOutputBudget(spec, context);
80916
+ }
80836
80917
  }
80837
80918
  await verifyRecordsUnchanged([...context.inputs, ...context.outputs.slice(0, frozenOutputCount)], spec);
80838
80919
  if (!verifyOwnedDirectory(spec.tests_root, "tests", spec) || !verifyOwnedDirectory(spec.logs_root, "logs", spec)) {
80839
80920
  throw new BenchmarkPhaseError("evidence_tampered", "benchmark evaluator changed an owned phase directory marker");
80840
80921
  }
80841
80922
  assertBudget(context);
80923
+ assertEvaluateOutputBudget(spec, context);
80842
80924
  evaluators.sort((left, right) => evaluatorOrder.get(left.id) - evaluatorOrder.get(right.id));
80843
80925
  return { outputs, evaluators };
80844
80926
  }
@@ -81024,6 +81106,7 @@ async function runBenchmarkPhase(specPathInput, resultPathInput, expectedPhase,
81024
81106
  let status = "failed";
81025
81107
  let error2;
81026
81108
  let context;
81109
+ let validatedSpec;
81027
81110
  let resultPathValidated = false;
81028
81111
  try {
81029
81112
  let bytes;
@@ -81043,6 +81126,7 @@ async function runBenchmarkPhase(specPathInput, resultPathInput, expectedPhase,
81043
81126
  }
81044
81127
  identity2 = rawIdentity(raw);
81045
81128
  const spec = BenchmarkSpecSchema.parse(raw);
81129
+ validatedSpec = spec;
81046
81130
  if (expectedPhase && spec.phase !== expectedPhase) {
81047
81131
  throw new BenchmarkPhaseError("phase_mismatch", `the ${expectedPhase} command cannot execute a ${spec.phase} spec`);
81048
81132
  }
@@ -81122,6 +81206,17 @@ async function runBenchmarkPhase(specPathInput, resultPathInput, expectedPhase,
81122
81206
  if (!resultPathValidated || !context?.logsOwned) {
81123
81207
  return { exitCode: status === "succeeded" ? 0 : 1, result: result2 };
81124
81208
  }
81209
+ if (status === "succeeded" && validatedSpec?.phase === "evaluate") {
81210
+ try {
81211
+ const resultBytes = Buffer.byteLength(`${JSON.stringify(result2, null, 2)}
81212
+ `);
81213
+ assertEvaluateOutputBudget(validatedSpec, context, resultBytes);
81214
+ } catch (budgetError) {
81215
+ status = "failed";
81216
+ result2.status = "failed";
81217
+ result2.error = stableError(budgetError);
81218
+ }
81219
+ }
81125
81220
  try {
81126
81221
  writeJsonAtomic(resultPath, result2);
81127
81222
  } catch (writeError) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brainbase-labs/cli",
3
- "version": "0.25.0-eng1209.4",
3
+ "version": "0.25.0-eng1209.5",
4
4
  "description": "Pack, share, and install agent templates across harnesses (Claude Code, Codex, ...).",
5
5
  "type": "module",
6
6
  "bin": {