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

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 +68 -33
  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.5",
36011
+ version: "0.25.0-eng1209.7",
36012
36012
  description: "Pack, share, and install agent templates across harnesses (Claude Code, Codex, ...).",
36013
36013
  type: "module",
36014
36014
  bin: {
@@ -78882,6 +78882,19 @@ class BenchmarkPhaseError extends Error {
78882
78882
  this.name = "BenchmarkPhaseError";
78883
78883
  }
78884
78884
  }
78885
+
78886
+ class BenchmarkCommandExecutionError extends BenchmarkPhaseError {
78887
+ stdout;
78888
+ stderr;
78889
+ durationMs;
78890
+ constructor(code, message, stdout, stderr, durationMs) {
78891
+ super(code, message);
78892
+ this.stdout = stdout;
78893
+ this.stderr = stderr;
78894
+ this.durationMs = durationMs;
78895
+ this.name = "BenchmarkCommandExecutionError";
78896
+ }
78897
+ }
78885
78898
  var ZIP_EOCD_SIGNATURE = 101010256;
78886
78899
  var ZIP_CENTRAL_SIGNATURE = 33639248;
78887
78900
  var ZIP_LOCAL_SIGNATURE = 67324752;
@@ -79985,7 +79998,8 @@ async function runCommand(command, root, spec, context, additions = {}) {
79985
79998
  const remainingMs = context.deadline - Date.now();
79986
79999
  if (remainingMs <= 0)
79987
80000
  throw new BenchmarkPhaseError("phase_timeout", "phase budget expired");
79988
- const timeoutMs2 = Math.min(command.timeout_ms ?? remainingMs, remainingMs);
80001
+ const reachesPhaseDeadline = command.timeout_ms === undefined || command.timeout_ms >= remainingMs;
80002
+ const timeoutMs2 = reachesPhaseDeadline ? remainingMs : command.timeout_ms;
79989
80003
  const started = Date.now();
79990
80004
  return await new Promise((resolve, reject2) => {
79991
80005
  const child = spawn4(command.argv[0], command.argv.slice(1), {
@@ -80021,11 +80035,18 @@ async function runCommand(command, root, spec, context, additions = {}) {
80021
80035
  };
80022
80036
  child.stdout?.on("data", (chunk2) => capture(stdout, chunk2));
80023
80037
  child.stderr?.on("data", (chunk2) => capture(stderr, chunk2));
80038
+ child.once("exit", () => {
80039
+ terminate(child);
80040
+ });
80024
80041
  child.on("error", (error2) => {
80025
- fail(new BenchmarkPhaseError("command_start_failed", `failed to start ${command.id}: ${error2.message}`));
80042
+ fail(new BenchmarkCommandExecutionError("command_start_failed", `failed to start ${command.id}: ${error2.message}`, Buffer.concat(stdout), Buffer.concat(stderr), Date.now() - started));
80026
80043
  });
80027
80044
  timer = setTimeout(() => {
80028
- fail(new BenchmarkPhaseError("command_timeout", `command timed out: ${command.id}`));
80045
+ if (reachesPhaseDeadline) {
80046
+ fail(new BenchmarkPhaseError("phase_timeout", "phase budget expired"));
80047
+ return;
80048
+ }
80049
+ fail(new BenchmarkCommandExecutionError("command_timeout", `command timed out: ${command.id}`, Buffer.concat(stdout), Buffer.concat(stderr), Date.now() - started));
80029
80050
  }, timeoutMs2);
80030
80051
  child.on("close", (code, signal) => {
80031
80052
  if (settled)
@@ -80033,7 +80054,7 @@ async function runCommand(command, root, spec, context, additions = {}) {
80033
80054
  settled = true;
80034
80055
  clearTimeout(timer);
80035
80056
  if (code === null) {
80036
- reject2(new BenchmarkPhaseError("command_terminated", `command was terminated by ${signal ?? "an unknown signal"}: ${command.id}`));
80057
+ reject2(new BenchmarkCommandExecutionError("command_terminated", `command was terminated by ${signal ?? "an unknown signal"}: ${command.id}`, Buffer.concat(stdout), Buffer.concat(stderr), Date.now() - started));
80037
80058
  return;
80038
80059
  }
80039
80060
  resolve({
@@ -80395,13 +80416,13 @@ async function copyCandidateOutput(output, manifest, spec, context) {
80395
80416
  }
80396
80417
  return copied;
80397
80418
  }
80398
- async function copyFrozenWorkspace(manifest, spec, context) {
80419
+ async function copyFrozenWorkspace(manifest, spec, context, sourceRoot = spec.workspace_root, preserveUnsafeSymlinks = false) {
80399
80420
  const destinationRoot = fs81.mkdtempSync(path88.join(os16.tmpdir(), "brainbase-benchmark-workspace-"));
80400
80421
  fs81.chmodSync(destinationRoot, 448);
80401
80422
  context.temporaryRoots.add(destinationRoot);
80402
80423
  for (const frozenFile of manifest) {
80403
80424
  assertBudget(context);
80404
- const source = path88.resolve(spec.workspace_root, safeRelPath(frozenFile.path));
80425
+ const source = path88.resolve(sourceRoot, safeRelPath(frozenFile.path));
80405
80426
  const destination = path88.resolve(destinationRoot, safeRelPath(frozenFile.path));
80406
80427
  if (frozenFile.kind === "symlink") {
80407
80428
  const stat = fs81.lstatSync(source);
@@ -80409,18 +80430,18 @@ async function copyFrozenWorkspace(manifest, spec, context) {
80409
80430
  if (target === null || Buffer.byteLength(target) !== frozenFile.size || sha256(target) !== frozenFile.sha256) {
80410
80431
  throw new BenchmarkPhaseError("evidence_tampered", `workspace changed after it was frozen: ${frozenFile.path}`);
80411
80432
  }
80412
- if (path88.isAbsolute(target)) {
80433
+ if (!preserveUnsafeSymlinks && path88.isAbsolute(target)) {
80413
80434
  throw new BenchmarkPhaseError("unsafe_workspace_symlink", `sandbox evaluator workspace cannot reproduce an absolute symlink: ${frozenFile.path}`);
80414
80435
  }
80415
80436
  const resolvedTarget = path88.resolve(path88.dirname(source), target);
80416
- if (!isWithin(spec.workspace_root, resolvedTarget)) {
80437
+ if (!preserveUnsafeSymlinks && !isWithin(sourceRoot, resolvedTarget)) {
80417
80438
  throw new BenchmarkPhaseError("unsafe_workspace_symlink", `sandbox evaluator workspace symlink escapes the workspace: ${frozenFile.path}`);
80418
80439
  }
80419
80440
  fs81.mkdirSync(path88.dirname(destination), { recursive: true, mode: 448 });
80420
80441
  fs81.symlinkSync(target, destination);
80421
80442
  continue;
80422
80443
  }
80423
- await atomicCopy(source, destination, frozenFile.mode, spec.workspace_root);
80444
+ await atomicCopy(source, destination, frozenFile.mode, sourceRoot);
80424
80445
  const copied = await recordFile(destinationRoot, destination, "workspace");
80425
80446
  if (copied.sha256 !== frozenFile.sha256 || copied.size !== frozenFile.size || copied.mode !== frozenFile.mode) {
80426
80447
  throw new BenchmarkPhaseError("evidence_tampered", `workspace changed after it was frozen: ${frozenFile.path}`);
@@ -80495,7 +80516,7 @@ var CriterionResultSchema = exports_external.object({
80495
80516
  var CriteriaResultFileSchema = exports_external.object({
80496
80517
  criteria: exports_external.array(CriterionResultSchema).max(MAX_CRITERIA_PER_EVALUATOR)
80497
80518
  }).strict();
80498
- function readCriterionResults(resultPath, criterionKeys, allowedEvidenceIds) {
80519
+ function readCriterionResults(resultPath, criterionKeys, allowedEvidenceIds, spec) {
80499
80520
  let opened;
80500
80521
  try {
80501
80522
  opened = openRegularFileNoFollow(resultPath, "criterion result", path88.dirname(resultPath));
@@ -80539,7 +80560,13 @@ function readCriterionResults(resultPath, criterionKeys, allowedEvidenceIds) {
80539
80560
  if (unknownEvidenceIds.length > 0) {
80540
80561
  throw new BenchmarkPhaseError("invalid_criterion_result", "criterion result cites evidence outside the evaluator plan", { unknown_evidence_ids: unknownEvidenceIds });
80541
80562
  }
80542
- return criterionKeys.map((key2) => byKey.get(key2));
80563
+ return criterionKeys.map((key2) => {
80564
+ const criterion = byKey.get(key2);
80565
+ return {
80566
+ ...criterion,
80567
+ explanation: redactCommandOutput(Buffer.from(criterion.explanation), spec).toString("utf8")
80568
+ };
80569
+ });
80543
80570
  } finally {
80544
80571
  fs81.closeSync(opened.fd);
80545
80572
  }
@@ -80562,7 +80589,7 @@ function eventType(event) {
80562
80589
  }
80563
80590
  return;
80564
80591
  }
80565
- async function evaluateOne(evaluator, spec, manifest, finalOutput, trajectory, frozenEvidence, context) {
80592
+ async function evaluateOne(evaluator, spec, manifest, frozenWorkspaceRoot, finalOutput, trajectory, frozenEvidence, context) {
80566
80593
  const started = Date.now();
80567
80594
  const base2 = {
80568
80595
  id: evaluator.id,
@@ -80635,8 +80662,8 @@ async function evaluateOne(evaluator, spec, manifest, finalOutput, trajectory, f
80635
80662
  }
80636
80663
  if (evaluator.type === "workspace_assertion") {
80637
80664
  const relative = workspaceRel(evaluator.path);
80638
- assertNoSymlinkTraversal(spec.workspace_root, relative);
80639
- const candidate = path88.resolve(spec.workspace_root, relative);
80665
+ assertNoSymlinkTraversal(frozenWorkspaceRoot, relative);
80666
+ const candidate = path88.resolve(frozenWorkspaceRoot, relative);
80640
80667
  let stat = null;
80641
80668
  try {
80642
80669
  stat = fs81.lstatSync(candidate);
@@ -80657,7 +80684,7 @@ async function evaluateOne(evaluator, spec, manifest, finalOutput, trajectory, f
80657
80684
  verdict = !exists2;
80658
80685
  if (evaluator.assertion.operator === "sha256") {
80659
80686
  if (stat?.isFile()) {
80660
- const opened = openRegularFileNoFollow(candidate, `workspace assertion ${relative}`, spec.workspace_root);
80687
+ const opened = openRegularFileNoFollow(candidate, `workspace assertion ${relative}`, frozenWorkspaceRoot);
80661
80688
  try {
80662
80689
  actualSha256 = await sha256OfDescriptor(opened.fd);
80663
80690
  verdict = actualSha256 === evaluator.assertion.expected;
@@ -80668,7 +80695,7 @@ async function evaluateOne(evaluator, spec, manifest, finalOutput, trajectory, f
80668
80695
  }
80669
80696
  if (evaluator.assertion.operator === "contains") {
80670
80697
  if (stat?.isFile()) {
80671
- const opened = openRegularFileNoFollow(candidate, `workspace assertion ${relative}`, spec.workspace_root);
80698
+ const opened = openRegularFileNoFollow(candidate, `workspace assertion ${relative}`, frozenWorkspaceRoot);
80672
80699
  try {
80673
80700
  verdict = readDescriptor(opened.fd).toString("utf8").includes(evaluator.assertion.expected);
80674
80701
  } finally {
@@ -80697,7 +80724,7 @@ async function evaluateOne(evaluator, spec, manifest, finalOutput, trajectory, f
80697
80724
  let isolatedTestsRoot;
80698
80725
  let privateResultRoot;
80699
80726
  try {
80700
- const evaluatorWorkspace = await copyFrozenWorkspace(manifest, spec, context);
80727
+ const evaluatorWorkspace = await copyFrozenWorkspace(manifest, spec, context, frozenWorkspaceRoot);
80701
80728
  isolatedWorkspace = evaluatorWorkspace;
80702
80729
  isolatedTestsRoot = await copyEvaluatorTests(spec, context);
80703
80730
  const testsPath = evaluatorTestsPath(evaluator, isolatedTestsRoot);
@@ -80723,7 +80750,7 @@ async function evaluateOne(evaluator, spec, manifest, finalOutput, trajectory, f
80723
80750
  const stderr = await writeLog(spec.logs_root, `${evaluator.id}.stderr.log`, result2.stderr, spec);
80724
80751
  let criterionResults;
80725
80752
  try {
80726
- criterionResults = evaluator.criterion_keys && criterionResultPath ? readCriterionResults(criterionResultPath, evaluator.criterion_keys, evaluator.allowed_evidence_ids ?? []) : undefined;
80753
+ criterionResults = evaluator.criterion_keys && criterionResultPath ? readCriterionResults(criterionResultPath, evaluator.criterion_keys, evaluator.allowed_evidence_ids ?? [], spec) : undefined;
80727
80754
  } catch (error2) {
80728
80755
  const normalized = stableError(error2);
80729
80756
  return {
@@ -80855,21 +80882,20 @@ async function executeEvaluate(spec, context) {
80855
80882
  assertEvaluateOutputBudget(spec, context);
80856
80883
  }
80857
80884
  await verifyRecordsUnchanged(manifest, spec);
80885
+ const frozenWorkspaceRoot = await copyFrozenWorkspace(manifest, spec, context, spec.workspace_root, true);
80858
80886
  for (const reference of spec.references) {
80859
80887
  const referenceOutputs = await copyMaterial(reference, spec.staging_root, spec.tests_root, "tests", false, context);
80860
80888
  outputs.push(...referenceOutputs);
80861
80889
  context.outputs.push(...referenceOutputs);
80862
80890
  }
80863
- const frozenOutputCount = context.outputs.length;
80864
80891
  const evaluators = [];
80865
80892
  const evaluatorOrder = new Map(spec.evaluators.map((evaluator, index) => [evaluator.id, index]));
80866
80893
  const executionOrder = [...spec.evaluators].sort((left, right) => Number(left.type === "sandbox_command") - Number(right.type === "sandbox_command"));
80867
80894
  for (const evaluator of executionOrder) {
80868
80895
  assertBudget(context);
80869
80896
  const started = Date.now();
80870
- let requiredResultError;
80871
80897
  try {
80872
- const evaluated = await evaluateOne(evaluator, spec, manifest, finalOutput.buffer.toString("utf8"), trajectory, { finalOutputPath, trajectoryPath }, context);
80898
+ const evaluated = await evaluateOne(evaluator, spec, manifest, frozenWorkspaceRoot, finalOutput.buffer.toString("utf8"), trajectory, { finalOutputPath, trajectoryPath }, context);
80873
80899
  assertBudget(context);
80874
80900
  evaluators.push(evaluated);
80875
80901
  context.evaluators.push(evaluated);
@@ -80881,13 +80907,14 @@ async function executeEvaluate(spec, context) {
80881
80907
  outputs.push(evaluated.stderr);
80882
80908
  context.outputs.push(evaluated.stderr);
80883
80909
  }
80884
- if (evaluated.status === "errored" && evaluator.required) {
80885
- const errorCode = typeof evaluated.details?.error_code === "string" ? evaluated.details.error_code : "evaluator_execution_failed";
80886
- const errorMessage2 = typeof evaluated.details?.error_message === "string" ? evaluated.details.error_message : "required evaluator execution failed";
80887
- requiredResultError = new BenchmarkPhaseError(errorCode, errorMessage2);
80888
- }
80889
80910
  } catch (error2) {
80890
80911
  const normalized = stableError(error2);
80912
+ let stdout;
80913
+ let stderr;
80914
+ if (error2 instanceof BenchmarkCommandExecutionError) {
80915
+ stdout = await writeLog(spec.logs_root, `${evaluator.id}.stdout.log`, error2.stdout, spec);
80916
+ stderr = await writeLog(spec.logs_root, `${evaluator.id}.stderr.log`, error2.stderr, spec);
80917
+ }
80891
80918
  const errored = {
80892
80919
  id: evaluator.id,
80893
80920
  type: evaluator.type,
@@ -80897,25 +80924,33 @@ async function executeEvaluate(spec, context) {
80897
80924
  verdict: null,
80898
80925
  engine: "brainbase-cli",
80899
80926
  engine_version: VERSION,
80900
- duration_ms: Date.now() - started,
80927
+ duration_ms: error2 instanceof BenchmarkCommandExecutionError ? error2.durationMs : Date.now() - started,
80901
80928
  details: {
80902
80929
  error_code: normalized?.code ?? "phase_failed",
80903
80930
  error_message: normalized?.message ?? "evaluator execution failed"
80904
- }
80931
+ },
80932
+ ...stdout ? { stdout } : {},
80933
+ ...stderr ? { stderr } : {}
80905
80934
  };
80906
80935
  evaluators.push(errored);
80907
80936
  context.evaluators.push(errored);
80908
- if (evaluator.required)
80937
+ if (stdout) {
80938
+ outputs.push(stdout);
80939
+ context.outputs.push(stdout);
80940
+ }
80941
+ if (stderr) {
80942
+ outputs.push(stderr);
80943
+ context.outputs.push(stderr);
80944
+ }
80945
+ if (!(error2 instanceof BenchmarkCommandExecutionError))
80909
80946
  throw error2;
80910
80947
  assertBudget(context);
80911
80948
  }
80912
- if (requiredResultError)
80913
- throw requiredResultError;
80914
80949
  if (evaluator.type === "sandbox_command") {
80915
80950
  assertEvaluateOutputBudget(spec, context);
80916
80951
  }
80917
80952
  }
80918
- await verifyRecordsUnchanged([...context.inputs, ...context.outputs.slice(0, frozenOutputCount)], spec);
80953
+ await verifyRecordsUnchanged([...context.inputs, ...context.outputs], spec);
80919
80954
  if (!verifyOwnedDirectory(spec.tests_root, "tests", spec) || !verifyOwnedDirectory(spec.logs_root, "logs", spec)) {
80920
80955
  throw new BenchmarkPhaseError("evidence_tampered", "benchmark evaluator changed an owned phase directory marker");
80921
80956
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brainbase-labs/cli",
3
- "version": "0.25.0-eng1209.5",
3
+ "version": "0.25.0-eng1209.7",
4
4
  "description": "Pack, share, and install agent templates across harnesses (Claude Code, Codex, ...).",
5
5
  "type": "module",
6
6
  "bin": {