@brainbase-labs/cli 0.25.0-eng1209.3 → 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 +172 -21
  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.3",
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: {
@@ -79114,10 +79114,15 @@ async function verifyRecordsUnchanged(records, spec) {
79114
79114
  const relative = safeRelPath(record3.path);
79115
79115
  assertNoSymlinkTraversal(root, relative);
79116
79116
  const candidate = path88.resolve(root, relative);
79117
- if (!isWithin(root, candidate) || !fs81.existsSync(candidate)) {
79117
+ if (!isWithin(root, candidate)) {
79118
+ throw new BenchmarkPhaseError("evidence_tampered", `evidence was removed during evaluation: ${record3.root}:${record3.path}`);
79119
+ }
79120
+ let stat;
79121
+ try {
79122
+ stat = fs81.lstatSync(candidate);
79123
+ } catch {
79118
79124
  throw new BenchmarkPhaseError("evidence_tampered", `evidence was removed during evaluation: ${record3.root}:${record3.path}`);
79119
79125
  }
79120
- const stat = fs81.lstatSync(candidate);
79121
79126
  if (record3.kind === "symlink") {
79122
79127
  const target = stat.isSymbolicLink() ? fs81.readlinkSync(candidate) : null;
79123
79128
  if (target === null || Buffer.byteLength(target) !== record3.size || sha256(target) !== record3.sha256) {
@@ -79212,6 +79217,15 @@ function removeRemoteHydrationInputs(spec) {
79212
79217
  removedSources.add(material.source);
79213
79218
  }
79214
79219
  }
79220
+ function removePrivateHydrationSourcesBeforeSetup(spec, context) {
79221
+ removeRemoteHydrationInputs(spec);
79222
+ for (const temporary of context.temporaryRoots) {
79223
+ fs81.rmSync(temporary, { recursive: true, force: true });
79224
+ }
79225
+ context.temporaryRoots.clear();
79226
+ context.verifiedInputs.clear();
79227
+ context.preparedArchiveFiles.clear();
79228
+ }
79215
79229
  async function downloadInputReference(stagingRoot, input, context) {
79216
79230
  const cacheKey = inputCacheKey(input);
79217
79231
  const cached2 = context.verifiedInputs.get(cacheKey);
@@ -80100,6 +80114,7 @@ async function executeHydrate(spec, context) {
80100
80114
  throw error2;
80101
80115
  }
80102
80116
  }
80117
+ removePrivateHydrationSourcesBeforeSetup(spec, context);
80103
80118
  for (const command of spec.setup_commands) {
80104
80119
  assertBudget(context);
80105
80120
  let result2;
@@ -80155,7 +80170,6 @@ async function executeHydrate(spec, context) {
80155
80170
  }
80156
80171
  outputs.splice(0, outputs.length, ...finalOutputs);
80157
80172
  context.outputs.splice(0, context.outputs.length, ...finalOutputs);
80158
- removeRemoteHydrationInputs(spec);
80159
80173
  assertBudget(context);
80160
80174
  return outputs;
80161
80175
  }
@@ -80220,6 +80234,42 @@ async function workspaceManifest(spec, context) {
80220
80234
  }
80221
80235
  return records.sort((a3, b4) => a3.path.localeCompare(b4.path));
80222
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
+ }
80223
80273
  function candidateGlob(pattern) {
80224
80274
  let source = "^";
80225
80275
  for (let index = 0;index < pattern.length; index += 1) {
@@ -80346,12 +80396,6 @@ async function copyCandidateOutput(output, manifest, spec, context) {
80346
80396
  return copied;
80347
80397
  }
80348
80398
  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
80399
  const destinationRoot = fs81.mkdtempSync(path88.join(os16.tmpdir(), "brainbase-benchmark-workspace-"));
80356
80400
  fs81.chmodSync(destinationRoot, 448);
80357
80401
  context.temporaryRoots.add(destinationRoot);
@@ -80359,6 +80403,23 @@ async function copyFrozenWorkspace(manifest, spec, context) {
80359
80403
  assertBudget(context);
80360
80404
  const source = path88.resolve(spec.workspace_root, safeRelPath(frozenFile.path));
80361
80405
  const destination = path88.resolve(destinationRoot, safeRelPath(frozenFile.path));
80406
+ if (frozenFile.kind === "symlink") {
80407
+ const stat = fs81.lstatSync(source);
80408
+ const target = stat.isSymbolicLink() ? fs81.readlinkSync(source) : null;
80409
+ if (target === null || Buffer.byteLength(target) !== frozenFile.size || sha256(target) !== frozenFile.sha256) {
80410
+ throw new BenchmarkPhaseError("evidence_tampered", `workspace changed after it was frozen: ${frozenFile.path}`);
80411
+ }
80412
+ if (path88.isAbsolute(target)) {
80413
+ throw new BenchmarkPhaseError("unsafe_workspace_symlink", `sandbox evaluator workspace cannot reproduce an absolute symlink: ${frozenFile.path}`);
80414
+ }
80415
+ const resolvedTarget = path88.resolve(path88.dirname(source), target);
80416
+ if (!isWithin(spec.workspace_root, resolvedTarget)) {
80417
+ throw new BenchmarkPhaseError("unsafe_workspace_symlink", `sandbox evaluator workspace symlink escapes the workspace: ${frozenFile.path}`);
80418
+ }
80419
+ fs81.mkdirSync(path88.dirname(destination), { recursive: true, mode: 448 });
80420
+ fs81.symlinkSync(target, destination);
80421
+ continue;
80422
+ }
80362
80423
  await atomicCopy(source, destination, frozenFile.mode, spec.workspace_root);
80363
80424
  const copied = await recordFile(destinationRoot, destination, "workspace");
80364
80425
  if (copied.sha256 !== frozenFile.sha256 || copied.size !== frozenFile.size || copied.mode !== frozenFile.mode) {
@@ -80367,13 +80428,48 @@ async function copyFrozenWorkspace(manifest, spec, context) {
80367
80428
  }
80368
80429
  return destinationRoot;
80369
80430
  }
80370
- 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) {
80371
80467
  if (!evaluator.tests_path)
80372
- return spec.tests_root;
80468
+ return testsRoot;
80373
80469
  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)) {
80470
+ assertNoSymlinkTraversal(testsRoot, relative);
80471
+ const candidate = path88.resolve(testsRoot, relative);
80472
+ if (!isWithin(testsRoot, candidate) || !fs81.existsSync(candidate)) {
80377
80473
  throw new BenchmarkPhaseError("invalid_tests_path", `evaluator tests_path does not exist: ${evaluator.tests_path}`);
80378
80474
  }
80379
80475
  const stat = fs81.lstatSync(candidate);
@@ -80510,7 +80606,21 @@ async function evaluateOne(evaluator, spec, manifest, finalOutput, trajectory, f
80510
80606
  }
80511
80607
  verdict = regexResult.exitCode === 0;
80512
80608
  }
80513
- return { ...base2, status: verdict ? "passed" : "failed", verdict, duration_ms: Date.now() - started };
80609
+ return {
80610
+ ...base2,
80611
+ status: verdict ? "passed" : "failed",
80612
+ verdict,
80613
+ duration_ms: Date.now() - started,
80614
+ details: {
80615
+ operator: assertion.operator,
80616
+ actual_size_bytes: Buffer.byteLength(finalOutput),
80617
+ ...assertion.operator === "exact" ? {
80618
+ expected_size_bytes: Buffer.byteLength(assertion.expected),
80619
+ actual_sha256: sha256(finalOutput),
80620
+ expected_sha256: sha256(assertion.expected)
80621
+ } : {}
80622
+ }
80623
+ };
80514
80624
  }
80515
80625
  if (evaluator.type === "trajectory_assertion") {
80516
80626
  const count = trajectory.filter((event) => eventType(event) === evaluator.event_type).length;
@@ -80540,6 +80650,7 @@ async function evaluateOne(evaluator, spec, manifest, finalOutput, trajectory, f
80540
80650
  }
80541
80651
  const exists2 = stat !== null;
80542
80652
  let verdict = false;
80653
+ let actualSha256;
80543
80654
  if (evaluator.assertion.operator === "exists")
80544
80655
  verdict = exists2;
80545
80656
  if (evaluator.assertion.operator === "not_exists")
@@ -80548,7 +80659,8 @@ async function evaluateOne(evaluator, spec, manifest, finalOutput, trajectory, f
80548
80659
  if (stat?.isFile()) {
80549
80660
  const opened = openRegularFileNoFollow(candidate, `workspace assertion ${relative}`, spec.workspace_root);
80550
80661
  try {
80551
- verdict = await sha256OfDescriptor(opened.fd) === evaluator.assertion.expected;
80662
+ actualSha256 = await sha256OfDescriptor(opened.fd);
80663
+ verdict = actualSha256 === evaluator.assertion.expected;
80552
80664
  } finally {
80553
80665
  fs81.closeSync(opened.fd);
80554
80666
  }
@@ -80564,15 +80676,32 @@ async function evaluateOne(evaluator, spec, manifest, finalOutput, trajectory, f
80564
80676
  }
80565
80677
  }
80566
80678
  }
80567
- return { ...base2, status: verdict ? "passed" : "failed", verdict, duration_ms: Date.now() - started };
80679
+ return {
80680
+ ...base2,
80681
+ status: verdict ? "passed" : "failed",
80682
+ verdict,
80683
+ duration_ms: Date.now() - started,
80684
+ details: {
80685
+ operator: evaluator.assertion.operator,
80686
+ path: relative,
80687
+ exists: exists2,
80688
+ ...stat ? {
80689
+ actual_kind: stat.isFile() ? "file" : stat.isDirectory() ? "directory" : "other",
80690
+ actual_size_bytes: stat.size
80691
+ } : {},
80692
+ ...actualSha256 ? { actual_sha256: actualSha256 } : {}
80693
+ }
80694
+ };
80568
80695
  }
80569
80696
  let isolatedWorkspace;
80697
+ let isolatedTestsRoot;
80570
80698
  let privateResultRoot;
80571
80699
  try {
80572
80700
  const evaluatorWorkspace = await copyFrozenWorkspace(manifest, spec, context);
80573
80701
  isolatedWorkspace = evaluatorWorkspace;
80574
- const testsPath = evaluatorTestsPath(evaluator, spec);
80575
- 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;
80576
80705
  const command = { id: evaluator.id, ...evaluator.command };
80577
80706
  const environment = {
80578
80707
  BRAINBASE_BENCHMARK_WORKSPACE: evaluatorWorkspace,
@@ -80623,7 +80752,7 @@ async function evaluateOne(evaluator, spec, manifest, finalOutput, trajectory, f
80623
80752
  ...criterionResults ? { criterion_results: criterionResults } : {}
80624
80753
  };
80625
80754
  } finally {
80626
- for (const temporary of [privateResultRoot, isolatedWorkspace]) {
80755
+ for (const temporary of [privateResultRoot, isolatedTestsRoot, isolatedWorkspace]) {
80627
80756
  if (!temporary)
80628
80757
  continue;
80629
80758
  fs81.rmSync(temporary, { recursive: true, force: true });
@@ -80672,6 +80801,7 @@ async function executeEvaluate(spec, context) {
80672
80801
  ];
80673
80802
  outputs.push(...frozenEvidenceRecords);
80674
80803
  context.outputs.push(...frozenEvidenceRecords);
80804
+ assertEvaluateOutputBudget(spec, context);
80675
80805
  assertBudget(context);
80676
80806
  const manifest = await workspaceManifest(spec, context);
80677
80807
  const manifestPath2 = path88.join(spec.logs_root, "candidate-workspace-manifest.json");
@@ -80684,6 +80814,7 @@ async function executeEvaluate(spec, context) {
80684
80814
  const manifestRecord = await recordFile(spec.logs_root, manifestPath2, "logs");
80685
80815
  outputs.push(manifestRecord);
80686
80816
  context.outputs.push(manifestRecord);
80817
+ assertEvaluateOutputBudget(spec, context);
80687
80818
  for (const artifactRelInput of spec.candidate_artifacts) {
80688
80819
  const artifactRel = workspaceRel(artifactRelInput);
80689
80820
  assertNoSymlinkTraversal(spec.workspace_root, artifactRel);
@@ -80701,11 +80832,13 @@ async function executeEvaluate(spec, context) {
80701
80832
  outputs.push(artifact);
80702
80833
  context.outputs.push(artifact);
80703
80834
  }
80835
+ assertEvaluateOutputBudget(spec, context);
80704
80836
  for (const candidateOutput of spec.candidate_outputs) {
80705
80837
  const copied = await copyCandidateOutput(candidateOutput, manifest, spec, context);
80706
80838
  outputs.push(...copied);
80707
80839
  context.outputs.push(...copied);
80708
80840
  }
80841
+ assertEvaluateOutputBudget(spec, context);
80709
80842
  if (spec.capture_workspace_archive) {
80710
80843
  const regularFiles = manifest.filter((entry) => entry.kind !== "symlink").map((entry) => entry.path);
80711
80844
  const archive = path88.join(spec.logs_root, "candidate-workspace.tar.gz");
@@ -80719,6 +80852,7 @@ async function executeEvaluate(spec, context) {
80719
80852
  const archiveRecord = await recordFile(spec.logs_root, archive, "logs");
80720
80853
  outputs.push(archiveRecord);
80721
80854
  context.outputs.push(archiveRecord);
80855
+ assertEvaluateOutputBudget(spec, context);
80722
80856
  }
80723
80857
  await verifyRecordsUnchanged(manifest, spec);
80724
80858
  for (const reference of spec.references) {
@@ -80777,12 +80911,16 @@ async function executeEvaluate(spec, context) {
80777
80911
  }
80778
80912
  if (requiredResultError)
80779
80913
  throw requiredResultError;
80914
+ if (evaluator.type === "sandbox_command") {
80915
+ assertEvaluateOutputBudget(spec, context);
80916
+ }
80780
80917
  }
80781
80918
  await verifyRecordsUnchanged([...context.inputs, ...context.outputs.slice(0, frozenOutputCount)], spec);
80782
80919
  if (!verifyOwnedDirectory(spec.tests_root, "tests", spec) || !verifyOwnedDirectory(spec.logs_root, "logs", spec)) {
80783
80920
  throw new BenchmarkPhaseError("evidence_tampered", "benchmark evaluator changed an owned phase directory marker");
80784
80921
  }
80785
80922
  assertBudget(context);
80923
+ assertEvaluateOutputBudget(spec, context);
80786
80924
  evaluators.sort((left, right) => evaluatorOrder.get(left.id) - evaluatorOrder.get(right.id));
80787
80925
  return { outputs, evaluators };
80788
80926
  }
@@ -80968,6 +81106,7 @@ async function runBenchmarkPhase(specPathInput, resultPathInput, expectedPhase,
80968
81106
  let status = "failed";
80969
81107
  let error2;
80970
81108
  let context;
81109
+ let validatedSpec;
80971
81110
  let resultPathValidated = false;
80972
81111
  try {
80973
81112
  let bytes;
@@ -80987,6 +81126,7 @@ async function runBenchmarkPhase(specPathInput, resultPathInput, expectedPhase,
80987
81126
  }
80988
81127
  identity2 = rawIdentity(raw);
80989
81128
  const spec = BenchmarkSpecSchema.parse(raw);
81129
+ validatedSpec = spec;
80990
81130
  if (expectedPhase && spec.phase !== expectedPhase) {
80991
81131
  throw new BenchmarkPhaseError("phase_mismatch", `the ${expectedPhase} command cannot execute a ${spec.phase} spec`);
80992
81132
  }
@@ -81066,6 +81206,17 @@ async function runBenchmarkPhase(specPathInput, resultPathInput, expectedPhase,
81066
81206
  if (!resultPathValidated || !context?.logsOwned) {
81067
81207
  return { exitCode: status === "succeeded" ? 0 : 1, result: result2 };
81068
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
+ }
81069
81220
  try {
81070
81221
  writeJsonAtomic(resultPath, result2);
81071
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.3",
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": {