@brainbase-labs/cli 0.21.0 → 0.21.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.
Files changed (2) hide show
  1. package/dist/index.js +816 -298
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -31708,8 +31708,8 @@ var require_utils = __commonJS((exports, module) => {
31708
31708
  }
31709
31709
  return ind;
31710
31710
  }
31711
- function removeDotSegments(path87) {
31712
- let input = path87;
31711
+ function removeDotSegments(path88) {
31712
+ let input = path88;
31713
31713
  const output = [];
31714
31714
  let nextSlash = -1;
31715
31715
  let len = 0;
@@ -31952,8 +31952,8 @@ var require_schemes = __commonJS((exports, module) => {
31952
31952
  wsComponent.secure = undefined;
31953
31953
  }
31954
31954
  if (wsComponent.resourceName) {
31955
- const [path87, query] = wsComponent.resourceName.split("?");
31956
- wsComponent.path = path87 && path87 !== "/" ? path87 : undefined;
31955
+ const [path88, query] = wsComponent.resourceName.split("?");
31956
+ wsComponent.path = path88 && path88 !== "/" ? path88 : undefined;
31957
31957
  wsComponent.query = query;
31958
31958
  wsComponent.resourceName = undefined;
31959
31959
  }
@@ -35128,12 +35128,12 @@ var require_dist2 = __commonJS((exports, module) => {
35128
35128
  throw new Error(`Unknown format "${name}"`);
35129
35129
  return f4;
35130
35130
  };
35131
- function addFormats(ajv, list, fs79, exportName) {
35131
+ function addFormats(ajv, list, fs80, exportName) {
35132
35132
  var _a;
35133
35133
  var _b;
35134
35134
  (_a = (_b = ajv.opts.code).formats) !== null && _a !== undefined || (_b.formats = (0, codegen_1._)`require("ajv-formats/dist/formats").${exportName}`);
35135
35135
  for (const f4 of list)
35136
- ajv.addFormat(f4, fs79[f4]);
35136
+ ajv.addFormat(f4, fs80[f4]);
35137
35137
  }
35138
35138
  module.exports = exports = formatsPlugin;
35139
35139
  Object.defineProperty(exports, "__esModule", { value: true });
@@ -35143,7 +35143,7 @@ var require_dist2 = __commonJS((exports, module) => {
35143
35143
  // src/index.ts
35144
35144
  var import_picocolors49 = __toESM(require_picocolors(), 1);
35145
35145
  import process14 from "node:process";
35146
- import fs81 from "node:fs";
35146
+ import fs82 from "node:fs";
35147
35147
 
35148
35148
  // src/cli/template.ts
35149
35149
  var import_picocolors12 = __toESM(require_picocolors(), 1);
@@ -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.21.0",
36011
+ version: "0.21.1",
36012
36012
  description: "Pack, share, and install agent templates across harnesses (Claude Code, Codex, ...).",
36013
36013
  type: "module",
36014
36014
  bin: {
@@ -40917,7 +40917,7 @@ function readAuth() {
40917
40917
  }
40918
40918
  }
40919
40919
  function acquireLock(lockFile, timeoutMs) {
40920
- ensureDir(BRAINBASE_HOME);
40920
+ ensureDir(path8.dirname(lockFile));
40921
40921
  const deadline = Date.now() + timeoutMs;
40922
40922
  const lockId = randomUUID();
40923
40923
  const owner = `${process.pid}:${lockId}`;
@@ -53845,6 +53845,8 @@ function apiErrorMessage(body, status) {
53845
53845
  import path57 from "node:path";
53846
53846
  import fs47 from "node:fs";
53847
53847
  var TOKEN_FILE = path57.join(BRAINBASE_HOME, "token.json");
53848
+ var TOKEN_LOCK_FILE = `${TOKEN_FILE}.lock`;
53849
+ var TOKEN_LOCK_TIMEOUT_MS = 5000;
53848
53850
  var TOKEN_PREFIX = "bbpat_";
53849
53851
  var TOKEN_PREFIX_LEN = 14;
53850
53852
  function tokenPrefix(token) {
@@ -53867,6 +53869,17 @@ var StoredTokenSchema = exports_external.object({
53867
53869
  name: exports_external.string().optional(),
53868
53870
  createdAt: exports_external.string()
53869
53871
  });
53872
+ function withTokenLock(operation) {
53873
+ const release = acquireLock(TOKEN_LOCK_FILE, TOKEN_LOCK_TIMEOUT_MS);
53874
+ if (!release) {
53875
+ throw new Error(`Timed out waiting to update the local CLI token; if no other brainbase process is running, remove ${TOKEN_LOCK_FILE}`);
53876
+ }
53877
+ try {
53878
+ return operation();
53879
+ } finally {
53880
+ release();
53881
+ }
53882
+ }
53870
53883
  function readToken() {
53871
53884
  if (!exists(TOKEN_FILE))
53872
53885
  return null;
@@ -53880,7 +53893,6 @@ function writeToken(token, name) {
53880
53893
  if (!isValidTokenFormat(token)) {
53881
53894
  throw new Error("refusing to store token: invalid format");
53882
53895
  }
53883
- ensureDir(BRAINBASE_HOME);
53884
53896
  const stored = {
53885
53897
  schemaVersion: 1,
53886
53898
  token,
@@ -53888,15 +53900,35 @@ function writeToken(token, name) {
53888
53900
  name,
53889
53901
  createdAt: new Date().toISOString()
53890
53902
  };
53891
- writeJson(TOKEN_FILE, stored);
53892
- try {
53893
- fs47.chmodSync(TOKEN_FILE, 384);
53894
- } catch {}
53903
+ withTokenLock(() => writeJsonAtomic(TOKEN_FILE, stored));
53895
53904
  return stored;
53896
53905
  }
53897
53906
  function clearToken() {
53898
- if (exists(TOKEN_FILE))
53899
- fs47.rmSync(TOKEN_FILE);
53907
+ withTokenLock(() => {
53908
+ if (exists(TOKEN_FILE))
53909
+ fs47.rmSync(TOKEN_FILE);
53910
+ });
53911
+ }
53912
+ function clearTokenIfMatches(prefix) {
53913
+ if (!prefix.startsWith(TOKEN_PREFIX))
53914
+ return "unverifiable";
53915
+ if (prefix.length < TOKEN_PREFIX_LEN)
53916
+ return "unverifiable";
53917
+ const before2 = readToken();
53918
+ if (!before2)
53919
+ return "absent";
53920
+ if (!before2.token.startsWith(prefix))
53921
+ return "kept";
53922
+ return withTokenLock(() => {
53923
+ const stored = readToken();
53924
+ if (!stored)
53925
+ return "absent";
53926
+ if (!stored.token.startsWith(prefix))
53927
+ return "kept";
53928
+ if (exists(TOKEN_FILE))
53929
+ fs47.rmSync(TOKEN_FILE);
53930
+ return "cleared";
53931
+ });
53900
53932
  }
53901
53933
 
53902
53934
  // src/core/api.ts
@@ -54297,8 +54329,13 @@ var api = {
54297
54329
  body: JSON.stringify(input)
54298
54330
  });
54299
54331
  },
54300
- getAgentSecrets(agentId) {
54301
- return request(`/agents/${encodeURIComponent(agentId)}/secrets`);
54332
+ async getAgentSecrets(agentId) {
54333
+ const body = await request(`/agents/${encodeURIComponent(agentId)}/secrets`);
54334
+ const secrets = body && typeof body === "object" ? body.secrets : undefined;
54335
+ if (!secrets || typeof secrets !== "object" || Array.isArray(secrets) || !Object.values(secrets).every((value) => typeof value === "string")) {
54336
+ throw new ApiError("The control plane returned an unreadable secrets response", undefined);
54337
+ }
54338
+ return { secrets };
54302
54339
  },
54303
54340
  putAgentSecrets(agentId, secrets) {
54304
54341
  return request(`/agents/${encodeURIComponent(agentId)}/secrets`, {
@@ -54599,6 +54636,12 @@ var registryApi = {
54599
54636
  body: JSON.stringify(input)
54600
54637
  });
54601
54638
  },
54639
+ renameCliToken(id, name) {
54640
+ return jsonRequest(`/v1/registry/cli-tokens/${encodeURIComponent(id)}`, {
54641
+ method: "PATCH",
54642
+ body: JSON.stringify({ name })
54643
+ });
54644
+ },
54602
54645
  revokeCliToken(id) {
54603
54646
  return jsonRequest(`/v1/registry/cli-tokens/${encodeURIComponent(id)}`, { method: "DELETE" });
54604
54647
  }
@@ -61797,6 +61840,9 @@ var EvalSchema = exports_external.object({
61797
61840
  path: ["classification_values"]
61798
61841
  });
61799
61842
  var MODEL_ID_RE = /^[A-Za-z0-9._:/-]{1,128}$/;
61843
+ var UNSYNCED_MANIFEST_KEYS = ["commands", "hooks", "files"];
61844
+ var UnsyncedBlockSchema = exports_external.array(exports_external.record(exports_external.unknown())).optional();
61845
+ var UnsyncedBlocksShape = Object.fromEntries(UNSYNCED_MANIFEST_KEYS.map((key2) => [key2, UnsyncedBlockSchema]));
61800
61846
  var AgentManifestSchema = exports_external.object({
61801
61847
  schema: exports_external.literal(1),
61802
61848
  id: exports_external.string().min(1).optional(),
@@ -61823,9 +61869,7 @@ var AgentManifestSchema = exports_external.object({
61823
61869
  });
61824
61870
  }).default([]),
61825
61871
  capabilities: CapabilitiesSchema.optional(),
61826
- commands: exports_external.array(exports_external.record(exports_external.unknown())).optional(),
61827
- hooks: exports_external.array(exports_external.record(exports_external.unknown())).optional(),
61828
- files: exports_external.array(exports_external.record(exports_external.unknown())).optional()
61872
+ ...UnsyncedBlocksShape
61829
61873
  });
61830
61874
  function manifestPath(cwd2) {
61831
61875
  return path73.join(cwd2, AGENT_MANIFEST_FILE);
@@ -63244,13 +63288,132 @@ var import_picocolors34 = __toESM(require_picocolors(), 1);
63244
63288
  // src/cli/agent-pull.ts
63245
63289
  import { spawn as spawn2 } from "node:child_process";
63246
63290
  import path80 from "node:path";
63247
- import fs72 from "node:fs";
63291
+ import fs73 from "node:fs";
63248
63292
  import os14 from "node:os";
63249
63293
  var import_picocolors26 = __toESM(require_picocolors(), 1);
63250
63294
 
63295
+ // src/core/manifest-unsynced.ts
63296
+ import fs69 from "node:fs";
63297
+ var import_yaml3 = __toESM(require_dist(), 1);
63298
+ function findUnsyncedBlocks(manifest) {
63299
+ const found = [];
63300
+ for (const key2 of UNSYNCED_MANIFEST_KEYS) {
63301
+ const block = manifest[key2];
63302
+ if (Array.isArray(block) && block.length > 0) {
63303
+ found.push({ key: key2, entries: block.length });
63304
+ }
63305
+ }
63306
+ return found;
63307
+ }
63308
+ function unsyncedBlockReason(block) {
63309
+ const entries = `${block.entries} ${block.entries === 1 ? "entry" : "entries"}`;
63310
+ return `\`${block.key}\` is declared in ${AGENT_MANIFEST_FILE} (${entries}) but this ` + `CLI does not sync ${block.key} yet — pushing would discard it.`;
63311
+ }
63312
+ function unsyncedBlockRemedy(blocks) {
63313
+ const keys2 = blocks.map((b4) => `\`${b4.key}\``).join(", ");
63314
+ const plural = blocks.length === 1 ? "" : "s";
63315
+ return `Remove the ${keys2} block${plural} from ${AGENT_MANIFEST_FILE} to continue. ` + `These blocks are unsupported with no target release — see "Reserved ` + `fields" in the agent manifest reference.`;
63316
+ }
63317
+ function reportUnsyncedBlocks(manifest, opts = {}) {
63318
+ const blocks = findUnsyncedBlocks(manifest);
63319
+ if (blocks.length === 0)
63320
+ return false;
63321
+ const prefix = opts.label ? `${opts.label}: ` : "";
63322
+ for (const block of blocks) {
63323
+ f2.error(`${prefix}${unsyncedBlockReason(block)}`);
63324
+ }
63325
+ f2.info(unsyncedBlockRemedy(blocks));
63326
+ return true;
63327
+ }
63328
+ function carryForwardUnsyncedBlocks(prev) {
63329
+ const carried = {};
63330
+ for (const key2 of UNSYNCED_MANIFEST_KEYS) {
63331
+ const block = prev?.[key2];
63332
+ if (block !== undefined)
63333
+ carried[key2] = block;
63334
+ }
63335
+ return carried;
63336
+ }
63337
+ function salvageLocalOnlyContent(raw) {
63338
+ if (!raw || typeof raw !== "object" || Array.isArray(raw))
63339
+ return {};
63340
+ const doc = raw;
63341
+ const salvaged = {};
63342
+ for (const key2 of UNSYNCED_MANIFEST_KEYS) {
63343
+ if (doc[key2] === undefined)
63344
+ continue;
63345
+ const result2 = AgentManifestSchema.shape[key2].safeParse(doc[key2]);
63346
+ if (result2.success && result2.data !== undefined) {
63347
+ salvaged[key2] = result2.data;
63348
+ }
63349
+ }
63350
+ if (doc.evals !== undefined) {
63351
+ const evals = AgentManifestSchema.shape.evals.safeParse(doc.evals);
63352
+ if (evals.success)
63353
+ salvaged.evals = evals.data;
63354
+ }
63355
+ return salvaged;
63356
+ }
63357
+ function readLocalOnlyContent(cwd2) {
63358
+ if (!hasManifest(cwd2))
63359
+ return { content: {}, status: "absent" };
63360
+ try {
63361
+ const prev = readManifest(cwd2);
63362
+ return {
63363
+ content: {
63364
+ evals: prev?.evals ?? [],
63365
+ ...carryForwardUnsyncedBlocks(prev)
63366
+ },
63367
+ status: "parsed"
63368
+ };
63369
+ } catch {}
63370
+ const file = existingManifestPath(cwd2);
63371
+ let raw = null;
63372
+ if (file) {
63373
+ try {
63374
+ raw = import_yaml3.default.parse(fs69.readFileSync(file, "utf8"));
63375
+ } catch {
63376
+ raw = null;
63377
+ }
63378
+ }
63379
+ const content = salvageLocalOnlyContent(raw);
63380
+ return {
63381
+ content,
63382
+ status: Object.keys(content).length > 0 ? "recovered" : "unreadable"
63383
+ };
63384
+ }
63385
+ function readManifestAgentId(cwd2) {
63386
+ if (!hasManifest(cwd2))
63387
+ return;
63388
+ const clean = (value) => typeof value === "string" && value.trim() ? value.trim() : undefined;
63389
+ try {
63390
+ return clean(readManifest(cwd2)?.id);
63391
+ } catch {}
63392
+ const file = existingManifestPath(cwd2);
63393
+ if (!file)
63394
+ return;
63395
+ try {
63396
+ const raw = import_yaml3.default.parse(fs69.readFileSync(file, "utf8"));
63397
+ if (!raw || typeof raw !== "object")
63398
+ return;
63399
+ return clean(raw.id);
63400
+ } catch {
63401
+ return;
63402
+ }
63403
+ }
63404
+ function readLocalOnlyContentReporting(cwd2) {
63405
+ const { content, status } = readLocalOnlyContent(cwd2);
63406
+ if (status === "recovered") {
63407
+ f2.warn(`${AGENT_MANIFEST_FILE} could not be read; kept its ${Object.keys(content).join(", ")} and rebuilt the rest from the cloud.`);
63408
+ } else if (status === "unreadable") {
63409
+ f2.warn(`${AGENT_MANIFEST_FILE} could not be read and was replaced with cloud state.`);
63410
+ }
63411
+ return content;
63412
+ }
63413
+
63251
63414
  // src/core/agent-diff.ts
63252
63415
  import path77 from "node:path";
63253
- import fs69 from "node:fs";
63416
+ import fs70 from "node:fs";
63254
63417
  import crypto4 from "node:crypto";
63255
63418
  function compKey(type, slug) {
63256
63419
  return `${type}/${slug}`;
@@ -63300,7 +63463,7 @@ function fileHash(p2) {
63300
63463
  if (!exists(p2))
63301
63464
  return null;
63302
63465
  try {
63303
- const buf = fs69.readFileSync(p2);
63466
+ const buf = fs70.readFileSync(p2);
63304
63467
  return crypto4.createHash("sha256").update(buf).digest("hex");
63305
63468
  } catch {
63306
63469
  return null;
@@ -63322,7 +63485,7 @@ function hashDirectoryAsComponent(dir) {
63322
63485
  const walk = (sub) => {
63323
63486
  let entries;
63324
63487
  try {
63325
- entries = fs69.readdirSync(sub, { withFileTypes: true });
63488
+ entries = fs70.readdirSync(sub, { withFileTypes: true });
63326
63489
  } catch {
63327
63490
  return;
63328
63491
  }
@@ -63590,7 +63753,7 @@ function diffAgentConfig(manifest, lock, cloud) {
63590
63753
 
63591
63754
  // src/core/secrets-env.ts
63592
63755
  import path78 from "node:path";
63593
- import fs70 from "node:fs";
63756
+ import fs71 from "node:fs";
63594
63757
  var SECRETS_FILE = "secrets.env";
63595
63758
  function secretsPath(cwd2) {
63596
63759
  return path78.join(cwd2, LINK_DIR, SECRETS_FILE);
@@ -63638,18 +63801,18 @@ function readLocalSecrets(cwd2) {
63638
63801
  const p2 = secretsPath(cwd2);
63639
63802
  if (!exists(p2))
63640
63803
  return {};
63641
- return parseSecretsEnv(fs70.readFileSync(p2, "utf8"));
63804
+ return parseSecretsEnv(fs71.readFileSync(p2, "utf8"));
63642
63805
  }
63643
63806
  function writeLocalSecrets(cwd2, secrets) {
63644
63807
  ensureDir(path78.join(cwd2, LINK_DIR));
63645
- fs70.writeFileSync(secretsPath(cwd2), formatSecretsEnv(secrets), "utf8");
63808
+ fs71.writeFileSync(secretsPath(cwd2), formatSecretsEnv(secrets), "utf8");
63646
63809
  ensureSecretsGitignore(cwd2);
63647
63810
  }
63648
63811
  function ensureSecretsGitignore(cwd2) {
63649
63812
  const ignorePath = path78.join(cwd2, LINK_DIR, ".gitignore");
63650
63813
  const needed = [SYNC_STATE_FILE, SECRETS_FILE];
63651
63814
  try {
63652
- const current = exists(ignorePath) ? fs70.readFileSync(ignorePath, "utf8") : "";
63815
+ const current = exists(ignorePath) ? fs71.readFileSync(ignorePath, "utf8") : "";
63653
63816
  const lines = new Set(current.split(/\r?\n/).map((l2) => l2.trim()).filter(Boolean));
63654
63817
  let changed = false;
63655
63818
  for (const n of needed) {
@@ -63659,7 +63822,7 @@ function ensureSecretsGitignore(cwd2) {
63659
63822
  }
63660
63823
  }
63661
63824
  if (changed) {
63662
- fs70.writeFileSync(ignorePath, [...lines].join(`
63825
+ fs71.writeFileSync(ignorePath, [...lines].join(`
63663
63826
  `) + `
63664
63827
  `);
63665
63828
  }
@@ -63698,7 +63861,7 @@ function entrypointExecutionAllowed(opts) {
63698
63861
 
63699
63862
  // src/cli/agent-unpack.ts
63700
63863
  import path79 from "node:path";
63701
- import fs71 from "node:fs";
63864
+ import fs72 from "node:fs";
63702
63865
  import os13 from "node:os";
63703
63866
  var import_picocolors25 = __toESM(require_picocolors(), 1);
63704
63867
  function componentsForNativeInstall(components, acp) {
@@ -63747,14 +63910,14 @@ async function runAgentUnpack(cwd2, args) {
63747
63910
  }
63748
63911
  }
63749
63912
  const scope = args.scope ?? "project";
63750
- const stageRoot = fs71.mkdtempSync(path79.join(os13.tmpdir(), "brainbase-unpack-"));
63913
+ const stageRoot = fs72.mkdtempSync(path79.join(os13.tmpdir(), "brainbase-unpack-"));
63751
63914
  try {
63752
63915
  const toInstall = [];
63753
63916
  const instructionsBody = readInstructions(cwd2, manifest);
63754
63917
  if (instructionsBody && instructionsBody.trim()) {
63755
63918
  const compDir = path79.join(stageRoot, "instruction", "agent-instructions");
63756
63919
  ensureDir(compDir);
63757
- fs71.writeFileSync(path79.join(compDir, "instructions.md"), instructionsBody, "utf8");
63920
+ fs72.writeFileSync(path79.join(compDir, "instructions.md"), instructionsBody, "utf8");
63758
63921
  toInstall.push({
63759
63922
  type: "instruction",
63760
63923
  slug: "agent-instructions",
@@ -63833,7 +63996,7 @@ async function runAgentUnpack(cwd2, args) {
63833
63996
  return;
63834
63997
  } finally {
63835
63998
  try {
63836
- fs71.rmSync(stageRoot, { recursive: true, force: true });
63999
+ fs72.rmSync(stageRoot, { recursive: true, force: true });
63837
64000
  } catch {}
63838
64001
  }
63839
64002
  if (manifest.harness !== harness) {
@@ -63854,8 +64017,8 @@ async function runAgentUnpack(cwd2, args) {
63854
64017
  function writeResolvedMcps(workdir, toInstall) {
63855
64018
  const mcps = toInstall.filter((c2) => c2.type === "mcp").map((c2) => ({ name: c2.slug, ...c2.payload }));
63856
64019
  const dir = path79.join(workdir, ".brainbase");
63857
- fs71.mkdirSync(dir, { recursive: true });
63858
- fs71.writeFileSync(path79.join(dir, "resolved-mcps.json"), JSON.stringify(mcps, null, 2));
64020
+ fs72.mkdirSync(dir, { recursive: true });
64021
+ fs72.writeFileSync(path79.join(dir, "resolved-mcps.json"), JSON.stringify(mcps, null, 2));
63859
64022
  }
63860
64023
  function stageLocalPlaybook(entry, cwd2, stageRoot, scope, toInstall) {
63861
64024
  if (entry.content.text !== undefined && entry.content.file !== undefined) {
@@ -63869,7 +64032,7 @@ function stageLocalPlaybook(entry, cwd2, stageRoot, scope, toInstall) {
63869
64032
  const compDir = path79.join(stageRoot, "playbook", slug);
63870
64033
  ensureDir(compDir);
63871
64034
  const wireBody = /^---\s*\n/.test(body) ? body : assembleFrontmatter(entry.title, entry.description) + body.replace(/^\n+/, "");
63872
- fs71.writeFileSync(path79.join(compDir, `${slug}.md`), wireBody, "utf8");
64035
+ fs72.writeFileSync(path79.join(compDir, `${slug}.md`), wireBody, "utf8");
63873
64036
  toInstall.push({
63874
64037
  type: "playbook",
63875
64038
  slug,
@@ -63883,7 +64046,7 @@ function stageLocalPlaybook(entry, cwd2, stageRoot, scope, toInstall) {
63883
64046
  function stageLocalSkill(source, cwd2, stageRoot, scope, toInstall) {
63884
64047
  if (source.startsWith("./") || source.startsWith("../") || source.startsWith("/")) {
63885
64048
  const abs = path79.resolve(cwd2, source);
63886
- if (!fs71.existsSync(abs)) {
64049
+ if (!fs72.existsSync(abs)) {
63887
64050
  return `Skill ${source}: not found on disk — skipped.`;
63888
64051
  }
63889
64052
  const slug = path79.basename(abs);
@@ -63956,13 +64119,13 @@ function defaultSlugForSource(source) {
63956
64119
  }
63957
64120
  function copyDirRecursive(src, dest) {
63958
64121
  ensureDir(dest);
63959
- for (const entry of fs71.readdirSync(src, { withFileTypes: true })) {
64122
+ for (const entry of fs72.readdirSync(src, { withFileTypes: true })) {
63960
64123
  const s3 = path79.join(src, entry.name);
63961
64124
  const d3 = path79.join(dest, entry.name);
63962
64125
  if (entry.isDirectory())
63963
64126
  copyDirRecursive(s3, d3);
63964
64127
  else if (entry.isFile())
63965
- fs71.copyFileSync(s3, d3);
64128
+ fs72.copyFileSync(s3, d3);
63966
64129
  }
63967
64130
  }
63968
64131
  function assembleFrontmatter(title, description) {
@@ -64214,14 +64377,14 @@ async function runAgentPull(cwd2, args) {
64214
64377
  if (!prior)
64215
64378
  continue;
64216
64379
  for (const filePath of prior.installedPaths) {
64217
- if (!fs72.existsSync(filePath))
64380
+ if (!fs73.existsSync(filePath))
64218
64381
  continue;
64219
64382
  try {
64220
- const stat = fs72.statSync(filePath);
64383
+ const stat = fs73.statSync(filePath);
64221
64384
  if (stat.isDirectory())
64222
- fs72.rmSync(filePath, { recursive: true, force: true });
64385
+ fs73.rmSync(filePath, { recursive: true, force: true });
64223
64386
  else
64224
- fs72.rmSync(filePath);
64387
+ fs73.rmSync(filePath);
64225
64388
  } catch (err) {
64226
64389
  f2.warn(`Failed to remove ${filePath}: ${err.message}`);
64227
64390
  }
@@ -64230,7 +64393,7 @@ async function runAgentPull(cwd2, args) {
64230
64393
  materializeInstructions(cwd2, cloud, toInstallKeys, keepLocalKeys, existingManifest);
64231
64394
  materializeEntrypoint(cwd2, cloudAgent.entrypoint ?? "", existingManifest);
64232
64395
  materializePlaybooks(cwd2, cloud, toInstallKeys, keepLocalKeys, existingManifest);
64233
- const yaml = mergeManifest(cwd2, existingManifest, cloud, cloudAgent, harness);
64396
+ const yaml = mergeManifest(cwd2, existingManifest, cloud, cloudAgent, harness, override ? {} : readLocalOnlyContentReporting(cwd2));
64234
64397
  writeManifest(cwd2, yaml);
64235
64398
  writeLink(cwd2, buildLinkFromAgent(cloudAgent, harness, readLink(cwd2)));
64236
64399
  const lockComponents = buildLockComponents({
@@ -64260,7 +64423,7 @@ async function runAgentPull(cwd2, args) {
64260
64423
  $e(`Pulled ${cloudAgent.name} at revision ${cloud.revision}.`);
64261
64424
  } finally {
64262
64425
  try {
64263
- fs72.rmSync(stageRoot, { recursive: true, force: true });
64426
+ fs73.rmSync(stageRoot, { recursive: true, force: true });
64264
64427
  } catch {}
64265
64428
  }
64266
64429
  }
@@ -64316,14 +64479,14 @@ function skillSourceFromMeta(c2) {
64316
64479
  }
64317
64480
  }
64318
64481
  function stageManifestComponents(components) {
64319
- const root = fs72.mkdtempSync(path80.join(os14.tmpdir(), "brainbase-pull-"));
64482
+ const root = fs73.mkdtempSync(path80.join(os14.tmpdir(), "brainbase-pull-"));
64320
64483
  for (const c2 of components) {
64321
64484
  const compDir = path80.join(root, c2.type, c2.slug);
64322
64485
  ensureDir(compDir);
64323
64486
  for (const f4 of c2.files) {
64324
64487
  const target = path80.join(compDir, f4.path);
64325
64488
  ensureDir(path80.dirname(target));
64326
- fs72.writeFileSync(target, f4.content);
64489
+ fs73.writeFileSync(target, f4.content);
64327
64490
  }
64328
64491
  }
64329
64492
  return root;
@@ -64366,7 +64529,7 @@ function materializeInstructions(cwd2, cloud, toInstall, keepLocal, existingMani
64366
64529
  const targetRel = existingManifest?.instructions?.file ?? DEFAULT_INSTRUCTIONS_FILE;
64367
64530
  const target = path80.resolve(cwd2, targetRel);
64368
64531
  ensureDir(path80.dirname(target));
64369
- fs72.writeFileSync(target, normalizeInstructionBody(body), "utf8");
64532
+ fs73.writeFileSync(target, normalizeInstructionBody(body), "utf8");
64370
64533
  }
64371
64534
  }
64372
64535
  function materializePlaybooks(cwd2, cloud, toInstall, keepLocal, existingManifest) {
@@ -64388,10 +64551,10 @@ function materializePlaybooks(cwd2, cloud, toInstall, keepLocal, existingManifes
64388
64551
  const targetRel = existing?.content?.file ?? path80.join(DEFAULT_PLAYBOOKS_DIR, `${c2.slug}.md`);
64389
64552
  const target = path80.resolve(cwd2, targetRel);
64390
64553
  ensureDir(path80.dirname(target));
64391
- fs72.writeFileSync(target, body, "utf8");
64554
+ fs73.writeFileSync(target, body, "utf8");
64392
64555
  }
64393
64556
  }
64394
- function mergeManifest(cwd2, prev, cloud, cloudAgent, harness) {
64557
+ function mergeManifest(cwd2, prev, cloud, cloudAgent, harness, localOnly = {}) {
64395
64558
  const skills = cloud.components.filter((c2) => c2.type === "skill").map((c2) => {
64396
64559
  const localDecl = prev?.skills.find((s3) => looseSkillComponentSlug(s3.source) === c2.slug);
64397
64560
  if (localDecl)
@@ -64472,7 +64635,8 @@ function mergeManifest(cwd2, prev, cloud, cloudAgent, harness) {
64472
64635
  playbooks,
64473
64636
  skills,
64474
64637
  mcp,
64475
- evals: prev?.evals ?? [],
64638
+ evals: [],
64639
+ ...localOnly,
64476
64640
  capabilities: {
64477
64641
  memory: caps.memory,
64478
64642
  browser: caps.browser,
@@ -64491,9 +64655,9 @@ function materializeEntrypoint(cwd2, cloudEntrypoint, prev) {
64491
64655
  return;
64492
64656
  const filename = prev?.entrypoint?.file ?? DEFAULT_ENTRYPOINT_FILE;
64493
64657
  const target = path80.resolve(cwd2, filename);
64494
- fs72.writeFileSync(target, cloudEntrypoint, "utf8");
64658
+ fs73.writeFileSync(target, cloudEntrypoint, "utf8");
64495
64659
  try {
64496
- fs72.chmodSync(target, 493);
64660
+ fs73.chmodSync(target, 493);
64497
64661
  } catch {}
64498
64662
  }
64499
64663
  function buildLockComponents(input) {
@@ -64572,9 +64736,9 @@ async function runEntrypointIfPresent(cwd2, manifest, execute) {
64572
64736
  ensureDir(stateDir);
64573
64737
  const scriptPath = path80.join(stateDir, "entrypoint.sh");
64574
64738
  const logPath = path80.join(stateDir, "entrypoint.log");
64575
- fs72.writeFileSync(scriptPath, body, "utf8");
64739
+ fs73.writeFileSync(scriptPath, body, "utf8");
64576
64740
  try {
64577
- fs72.chmodSync(scriptPath, 493);
64741
+ fs73.chmodSync(scriptPath, 493);
64578
64742
  } catch {}
64579
64743
  if (!execute) {
64580
64744
  f2.info(`Agent has an entrypoint — written to ${import_picocolors26.default.dim(path80.relative(cwd2, scriptPath))}, not executed. ` + `Run it with ${import_picocolors26.default.cyan("brainbase run bash .brainbase/entrypoint.sh")} or re-pull with ${import_picocolors26.default.cyan("--run-entrypoint")}. Sandboxes run it automatically.`);
@@ -64583,7 +64747,7 @@ async function runEntrypointIfPresent(cwd2, manifest, execute) {
64583
64747
  f2.info(`Running entrypoint ${import_picocolors26.default.dim(`(${path80.relative(cwd2, scriptPath)})`)}`);
64584
64748
  const secrets = readLocalSecrets(cwd2);
64585
64749
  const env3 = { ...process.env, ...secrets };
64586
- const logStream = fs72.createWriteStream(logPath, { flags: "w" });
64750
+ const logStream = fs73.createWriteStream(logPath, { flags: "w" });
64587
64751
  const exitCode = await new Promise((resolve) => {
64588
64752
  const child = spawn2("bash", [scriptPath], {
64589
64753
  cwd: cwd2,
@@ -64618,7 +64782,7 @@ async function pullSecrets(cwd2, agentId) {
64618
64782
  sp.start("Fetching secrets…");
64619
64783
  try {
64620
64784
  const res = await api.getAgentSecrets(agentId);
64621
- cloudSecrets = res.secrets ?? {};
64785
+ cloudSecrets = res.secrets;
64622
64786
  sp.stop(Object.keys(cloudSecrets).length === 0 ? "No secrets on cloud." : `Got ${Object.keys(cloudSecrets).length} secret${Object.keys(cloudSecrets).length === 1 ? "" : "s"}.`);
64623
64787
  } catch (err) {
64624
64788
  sp.stop("Failed to fetch secrets.");
@@ -64859,6 +65023,10 @@ async function runAgentPush(cwd2, args) {
64859
65023
  process.exitCode = 1;
64860
65024
  return;
64861
65025
  }
65026
+ if (reportUnsyncedBlocks(manifest)) {
65027
+ process.exitCode = 1;
65028
+ return;
65029
+ }
64862
65030
  if (!manifest.id) {
64863
65031
  f2.warn(`${import_picocolors28.default.bold(AGENT_MANIFEST_FILE)} is unclaimed (no ${import_picocolors28.default.cyan("id")}). Nothing to push to.`);
64864
65032
  f2.info(`Run ${import_picocolors28.default.cyan("brainbase agent create")} first — that creates the cloud agent and stamps an id here.`);
@@ -64961,6 +65129,7 @@ async function runAgentPush(cwd2, args) {
64961
65129
  const version = registryRefs.get(name)?.version ?? "0.1.0";
64962
65130
  f2.info(` ${import_picocolors28.default.cyan(`brainbase skill publish ./path/to/skill --name ${name} --skill-version ${version} --yes`)}`);
64963
65131
  }
65132
+ process.exitCode = 1;
64964
65133
  return;
64965
65134
  }
64966
65135
  const skillUpdates = planRegistrySkillUpdates(manifest.skills, cloud.components, latestByName);
@@ -64981,6 +65150,7 @@ async function runAgentPush(cwd2, args) {
64981
65150
  } else {
64982
65151
  f2.error("Entrypoint block is empty.");
64983
65152
  }
65153
+ process.exitCode = 1;
64984
65154
  return;
64985
65155
  }
64986
65156
  resolvedEntrypoint = body;
@@ -64993,6 +65163,7 @@ async function runAgentPush(cwd2, args) {
64993
65163
  for (const r2 of rows) {
64994
65164
  if (r2.type !== "instruction" && r2.type !== "skill" && r2.type !== "mcp" && r2.type !== "playbook") {
64995
65165
  f2.error(`Component ${fmtType(r2.type)} ${import_picocolors28.default.bold(r2.slug)} can't be pushed yet — the server accepts instructions, skills, and mcps in this version.`);
65166
+ process.exitCode = 1;
64996
65167
  return;
64997
65168
  }
64998
65169
  }
@@ -65005,6 +65176,7 @@ async function runAgentPush(cwd2, args) {
65005
65176
  }
65006
65177
  if (parsed.kind === "local") {
65007
65178
  f2.error(`Skill ${import_picocolors28.default.bold(entry.source)} is a local-authored skill. Local skill push isn't supported yet — publish it to the registry first (\`brainbase skill publish\`).`);
65179
+ process.exitCode = 1;
65008
65180
  return;
65009
65181
  }
65010
65182
  }
@@ -65017,6 +65189,7 @@ async function runAgentPush(cwd2, args) {
65017
65189
  }).map((c2) => c2.slug);
65018
65190
  if (preIdSchemaSlugs.length > 0) {
65019
65191
  f2.error(`Playbook ${preIdSchemaSlugs.length === 1 ? "entry" : "entries"} ${preIdSchemaSlugs.map((s3) => import_picocolors28.default.bold(s3)).join(", ")} in ${import_picocolors28.default.bold("brainbase.agent.yaml")} ${preIdSchemaSlugs.length === 1 ? "is" : "are"} missing ${import_picocolors28.default.cyan("id:")}. Run ${import_picocolors28.default.bold("brainbase agent pull")} first to sync playbook ids, then push.`);
65192
+ process.exitCode = 1;
65020
65193
  return;
65021
65194
  }
65022
65195
  const { toSend, conflicts, upstreamOnly } = partitionPushRows(rows, !!args.force);
@@ -65027,6 +65200,7 @@ async function runAgentPush(cwd2, args) {
65027
65200
  console.error(` ${import_picocolors28.default.red("!")} ${fmtType(r2.type)} ${import_picocolors28.default.bold(r2.slug)}`);
65028
65201
  }
65029
65202
  f2.info(`Run ${import_picocolors28.default.cyan("brainbase agent pull")} first to reconcile, then push again — or ${import_picocolors28.default.cyan("brainbase agent push --force")} to overwrite the cloud with your local version.`);
65203
+ process.exitCode = 1;
65030
65204
  return;
65031
65205
  }
65032
65206
  if (forcedOverrides.length > 0) {
@@ -65303,7 +65477,7 @@ async function planSecretPush(cwd2, agentId) {
65303
65477
  if (Object.keys(localSecrets).length === 0)
65304
65478
  return null;
65305
65479
  const res = await api.getAgentSecrets(agentId);
65306
- const cloudSecrets = res.secrets ?? {};
65480
+ const cloudSecrets = res.secrets;
65307
65481
  const diff2 = diffSecrets(localSecrets, cloudSecrets);
65308
65482
  if (diff2.localOnly.length === 0 && diff2.changed.length === 0 && diff2.cloudOnly.length === 0) {
65309
65483
  return null;
@@ -65340,37 +65514,64 @@ function handleApiError3(err) {
65340
65514
  }
65341
65515
 
65342
65516
  // src/cli/agent-status.ts
65517
+ import path81 from "node:path";
65343
65518
  var import_picocolors29 = __toESM(require_picocolors(), 1);
65344
- async function runAgentStatus(cwd2) {
65345
- banner("agent status what changed locally, remotely, both");
65519
+ async function runAgentStatus(cwd2, args = {}) {
65520
+ const json = args.json === true;
65521
+ if (!json)
65522
+ banner("agent status — what changed locally, remotely, both");
65346
65523
  const link2 = readLink(cwd2);
65347
65524
  if (!link2) {
65525
+ if (json) {
65526
+ emitJson({ linked: false, ignored: [], unchecked: [] });
65527
+ return;
65528
+ }
65348
65529
  f2.warn("This folder is not linked to any agent.");
65349
65530
  f2.info(`Run ${import_picocolors29.default.cyan("brainbase link")} first.`);
65350
65531
  return;
65351
65532
  }
65352
65533
  const manifest = hasManifest(cwd2) ? readManifest(cwd2) : null;
65353
65534
  const lock = readSyncState(cwd2);
65535
+ const ignoredBlocks = manifest ? findUnsyncedBlocks(manifest) : [];
65536
+ const ignored = ignoredBlocks.map((block) => ({
65537
+ block: block.key,
65538
+ entries: block.entries,
65539
+ reason: unsyncedBlockReason(block)
65540
+ }));
65354
65541
  let cloud = null;
65355
65542
  let cloudAgent = null;
65356
- const sp = de();
65357
- sp.start(`Fetching ${link2.name}…`);
65543
+ const sp = json ? null : de();
65544
+ sp?.start(`Fetching ${link2.name}…`);
65358
65545
  try {
65359
65546
  [cloud, cloudAgent] = await Promise.all([
65360
65547
  api.getAgentManifest(link2.agent_id),
65361
65548
  api.getAgent(link2.agent_id)
65362
65549
  ]);
65363
- sp.stop(`Cloud revision ${cloud.revision}.`);
65550
+ sp?.stop(`Cloud revision ${cloud.revision}.`);
65364
65551
  } catch (err) {
65365
- sp.stop("Failed to reach brainbase.");
65366
- if (err instanceof ApiError && err.status === 401) {
65367
- f2.error("Your session is invalid. Run `brainbase login` and try again.");
65368
- } else {
65369
- f2.error(err.message);
65552
+ const unauthorized = err instanceof ApiError && err.status === 401;
65553
+ const message = unauthorized ? "Your session is invalid. Run `brainbase login` and try again." : err.message;
65554
+ if (json) {
65555
+ console.error(message);
65556
+ process.exitCode = 1;
65557
+ return;
65370
65558
  }
65559
+ sp?.stop("Failed to reach brainbase.");
65560
+ f2.error(message);
65371
65561
  return;
65372
65562
  }
65373
65563
  if (!manifest) {
65564
+ if (json) {
65565
+ emitJson({
65566
+ linked: true,
65567
+ manifest: false,
65568
+ agent: { id: link2.agent_id, name: link2.name, slug: link2.slug },
65569
+ revision: { cloud: cloud.revision, lock: lock?.revision ?? null },
65570
+ ignored,
65571
+ unchecked: []
65572
+ });
65573
+ return;
65574
+ }
65374
65575
  f2.info(`${import_picocolors29.default.dim("No")} ${import_picocolors29.default.bold("brainbase.agent.yaml")} ${import_picocolors29.default.dim("here yet.")} Run ${import_picocolors29.default.cyan("brainbase agent pull")} to populate this folder.`);
65375
65576
  f2.info(`Cloud has ${import_picocolors29.default.bold(String(cloud.components.length))} component${cloud.components.length === 1 ? "" : "s"} at revision ${cloud.revision}.`);
65376
65577
  return;
@@ -65415,21 +65616,68 @@ async function runAgentStatus(cwd2) {
65415
65616
  cloudOnly: [],
65416
65617
  changed: []
65417
65618
  };
65619
+ let secretsChecked = true;
65620
+ let secretsUncheckedReason = "";
65418
65621
  try {
65419
65622
  const localSecrets = readLocalSecrets(cwd2);
65420
65623
  const cloudRes = await api.getAgentSecrets(link2.agent_id);
65421
- secretDrift = diffSecrets(localSecrets, cloudRes.secrets ?? {});
65422
- } catch {}
65624
+ secretDrift = diffSecrets(localSecrets, cloudRes.secrets);
65625
+ } catch (err) {
65626
+ if (!(err instanceof ApiError && err.status === 404)) {
65627
+ secretsChecked = false;
65628
+ secretsUncheckedReason = describeSecretsFailure(err);
65629
+ }
65630
+ }
65423
65631
  const componentsDrifted = conflicts.length > 0 || toPush.length > 0 || toPull.length > 0;
65424
65632
  const metaDrifted = meta.localChanged || meta.cloudChanged;
65425
65633
  const configDrifted = config.unsupported.length > 0 || config.machineMismatch || config.machineCloudChanged || config.defaultModelLocalChanged || config.defaultModelCloudChanged;
65426
65634
  const secretsDrifted = secretDrift.localOnly.length > 0 || secretDrift.cloudOnly.length > 0 || secretDrift.changed.length > 0;
65635
+ const everythingInSync = !componentsDrifted && !metaDrifted && !configDrifted && !secretsDrifted;
65636
+ const unchecked = secretsChecked ? [] : [{ signal: "secrets", reason: secretsUncheckedReason }];
65637
+ if (json) {
65638
+ emitJson({
65639
+ linked: true,
65640
+ manifest: true,
65641
+ agent: { id: link2.agent_id, name: link2.name, slug: link2.slug },
65642
+ revision: { cloud: cloud.revision, lock: lock?.revision ?? null },
65643
+ ignored,
65644
+ metadata: { push: meta.localChanged, pull: meta.cloudChanged },
65645
+ runtimeConfig: {
65646
+ unsupported: config.unsupported,
65647
+ machineMismatch: config.machineMismatch,
65648
+ machineCloudChanged: config.machineCloudChanged,
65649
+ defaultModelLocalChanged: config.defaultModelLocalChanged,
65650
+ defaultModelCloudChanged: config.defaultModelCloudChanged,
65651
+ defaultModelConflict: config.defaultModelConflict
65652
+ },
65653
+ secrets: secretsChecked ? {
65654
+ localOnly: secretDrift.localOnly,
65655
+ cloudOnly: secretDrift.cloudOnly,
65656
+ changed: secretDrift.changed
65657
+ } : null,
65658
+ components: {
65659
+ push: toPush.map(rowJson),
65660
+ pull: toPull.map(rowJson),
65661
+ conflicts: conflicts.map(rowJson)
65662
+ },
65663
+ inSync: everythingInSync,
65664
+ unchecked
65665
+ });
65666
+ return;
65667
+ }
65427
65668
  const lines = [];
65428
65669
  lines.push("");
65429
65670
  lines.push(` ${import_picocolors29.default.bold(link2.name)} ${import_picocolors29.default.dim(`(${link2.slug})`)}`);
65430
65671
  lines.push(` ${import_picocolors29.default.dim("agent_id")} ${link2.agent_id}`);
65431
65672
  lines.push(` ${import_picocolors29.default.dim("revision")} cloud ${cloud.revision}${lock ? ` · lock ${lock.revision}` : " · never pulled"}`);
65432
65673
  lines.push("");
65674
+ if (ignoredBlocks.length > 0) {
65675
+ lines.push(` ${import_picocolors29.default.bold("ignored manifest blocks")}`);
65676
+ for (const block of ignoredBlocks) {
65677
+ lines.push(` ${import_picocolors29.default.red("! ignored")} ${import_picocolors29.default.bold(block.key)} (${block.entries}) — not synced by this CLI; ${import_picocolors29.default.cyan("agent push")} will refuse it`);
65678
+ }
65679
+ lines.push("");
65680
+ }
65433
65681
  if (metaDrifted) {
65434
65682
  lines.push(` ${import_picocolors29.default.bold("agent metadata")}`);
65435
65683
  if (meta.localChanged) {
@@ -65463,8 +65711,11 @@ async function runAgentStatus(cwd2) {
65463
65711
  }
65464
65712
  lines.push("");
65465
65713
  }
65466
- if (secretsDrifted) {
65714
+ if (secretsDrifted || !secretsChecked) {
65467
65715
  lines.push(` ${import_picocolors29.default.bold("secrets")}`);
65716
+ if (!secretsChecked) {
65717
+ lines.push(` ${import_picocolors29.default.dim("? unchecked")} ${secretsUncheckedReason}`);
65718
+ }
65468
65719
  if (secretDrift.localOnly.length)
65469
65720
  lines.push(` ${import_picocolors29.default.yellow("→ push")} new locally: ${secretDrift.localOnly.join(", ")}`);
65470
65721
  if (secretDrift.changed.length)
@@ -65473,8 +65724,9 @@ async function runAgentStatus(cwd2) {
65473
65724
  lines.push(` ${import_picocolors29.default.cyan("← pull")} new on cloud: ${secretDrift.cloudOnly.join(", ")}`);
65474
65725
  lines.push("");
65475
65726
  }
65476
- if (!componentsDrifted && !metaDrifted && !configDrifted && !secretsDrifted) {
65477
- lines.push(` ${import_picocolors29.default.green("")} everything is in sync`);
65727
+ if (everythingInSync) {
65728
+ const qualifier = secretsChecked ? "" : ` ${import_picocolors29.default.dim("(secrets not checked)")}`;
65729
+ lines.push(` ${import_picocolors29.default.green("✓")} everything is in sync${qualifier}`);
65478
65730
  lines.push("");
65479
65731
  console.log(lines.join(`
65480
65732
  `));
@@ -65503,6 +65755,26 @@ async function runAgentStatus(cwd2) {
65503
65755
  console.log(lines.join(`
65504
65756
  `));
65505
65757
  }
65758
+ function emitJson(report) {
65759
+ console.log(JSON.stringify(report, null, 2));
65760
+ }
65761
+ function rowJson(r2) {
65762
+ return { type: r2.type, slug: r2.slug, status: r2.status };
65763
+ }
65764
+ var MAX_REASON_LENGTH = 120;
65765
+ function oneLine(text2) {
65766
+ const collapsed = text2.replace(/\s+/g, " ").trim();
65767
+ return collapsed.length > MAX_REASON_LENGTH ? `${collapsed.slice(0, MAX_REASON_LENGTH - 1)}…` : collapsed;
65768
+ }
65769
+ function describeSecretsFailure(err) {
65770
+ const remote = err instanceof ApiError;
65771
+ if (remote && err.status === 401) {
65772
+ return "could not fetch secrets: your session is invalid — run `brainbase login`";
65773
+ }
65774
+ const where = remote ? "could not fetch secrets from the control plane" : `could not read ${path81.join(LINK_DIR, SECRETS_FILE)}`;
65775
+ const detail = oneLine(err instanceof Error ? err.message : typeof err === "string" ? err : "");
65776
+ return detail ? `${where}: ${detail}` : where;
65777
+ }
65506
65778
  function fmtRow(r2) {
65507
65779
  const head3 = `${fmtType(r2.type)} ${import_picocolors29.default.bold(r2.slug)}`;
65508
65780
  switch (r2.status) {
@@ -65558,7 +65830,7 @@ function formatExport(shell, key2, value) {
65558
65830
  }
65559
65831
 
65560
65832
  // src/cli/agent-create.ts
65561
- import path81 from "node:path";
65833
+ import path82 from "node:path";
65562
65834
  var import_picocolors32 = __toESM(require_picocolors(), 1);
65563
65835
 
65564
65836
  // src/ui/box.ts
@@ -65762,6 +66034,10 @@ async function runAgentCreate(cwd2, args) {
65762
66034
  f2.info(`If you want to detach it, run ${import_picocolors32.default.cyan("brainbase unlink")} first; or move to a different directory.`);
65763
66035
  return;
65764
66036
  }
66037
+ if (reportUnsyncedBlocks(manifest)) {
66038
+ process.exitCode = 1;
66039
+ return;
66040
+ }
65765
66041
  const { org, team } = await resolveOrgAndTeam({
65766
66042
  orgId: args.orgId,
65767
66043
  teamId: args.teamId,
@@ -66030,7 +66306,7 @@ async function loadOrScaffoldManifest(cwd2, args) {
66030
66306
  return null;
66031
66307
  }
66032
66308
  }
66033
- const seedName = args.name?.trim() ?? path81.basename(path81.resolve(cwd2)) ?? "My Agent";
66309
+ const seedName = args.name?.trim() ?? path82.basename(path82.resolve(cwd2)) ?? "My Agent";
66034
66310
  const seedHarness = args.harness ? normalizeHarnessId(args.harness) : undefined;
66035
66311
  const scaffold = {
66036
66312
  schema: 1,
@@ -66168,7 +66444,7 @@ async function runAgent(cwd2, sub, args, opts) {
66168
66444
  });
66169
66445
  return;
66170
66446
  case "status":
66171
- await runAgentStatus(cwd2);
66447
+ await runAgentStatus(cwd2, { json: opts.json });
66172
66448
  return;
66173
66449
  case "env":
66174
66450
  await runAgentEnv(cwd2, { shell: opts.shell });
@@ -66196,7 +66472,7 @@ function printHelp() {
66196
66472
  out.push(` ${import_picocolors34.default.cyan("pull")} ${import_picocolors34.default.dim("[<id>]")} ${import_picocolors34.default.dim("apply cloud changes into this folder — pass <id> to switch (--force to override); --run-entrypoint to also execute the agent entrypoint")}`);
66197
66473
  out.push(` ${import_picocolors34.default.cyan("push")} ${import_picocolors34.default.dim("send local changes to the cloud — instructions, playbooks, skills, MCPs, entrypoint (--force to overwrite cloud-side conflicts with local)")}`);
66198
66474
  out.push(` ${import_picocolors34.default.cyan("unpack")} ${import_picocolors34.default.dim("install the claimed agent into a harness layout (--harness to override)")}`);
66199
- out.push(` ${import_picocolors34.default.cyan("status")} ${import_picocolors34.default.dim("show what would push and what would pull")}`);
66475
+ out.push(` ${import_picocolors34.default.cyan("status")} ${import_picocolors34.default.dim("show what would push, what would pull, and which manifest blocks are ignored (--json for scripts)")}`);
66200
66476
  out.push(` ${import_picocolors34.default.cyan("env")} ${import_picocolors34.default.dim('print export statements — use with `eval "$(brainbase agent env)"`')}`);
66201
66477
  out.push("");
66202
66478
  console.log(out.join(`
@@ -66291,14 +66567,14 @@ function printHelp2() {
66291
66567
  var import_picocolors43 = __toESM(require_picocolors(), 1);
66292
66568
 
66293
66569
  // src/cli/orchestration-pull.ts
66294
- import path85 from "node:path";
66295
- import fs76 from "node:fs";
66570
+ import path86 from "node:path";
66571
+ import fs77 from "node:fs";
66296
66572
  var import_picocolors37 = __toESM(require_picocolors(), 1);
66297
66573
 
66298
66574
  // src/core/orchestration-manifest.ts
66299
- import path82 from "node:path";
66300
- import fs73 from "node:fs";
66301
- var import_yaml3 = __toESM(require_dist(), 1);
66575
+ import path83 from "node:path";
66576
+ import fs74 from "node:fs";
66577
+ var import_yaml4 = __toESM(require_dist(), 1);
66302
66578
  var ORCH_MANIFEST_FILE = "brainbase-orchestration.yaml";
66303
66579
  var ORCH_MEMBERS_DIR = "agents";
66304
66580
  var OrchMetaSchema = exports_external.object({
@@ -66350,19 +66626,19 @@ var OrchestrationManifestSchema = exports_external.object({
66350
66626
  triggers: exports_external.array(TriggerSchema).optional()
66351
66627
  });
66352
66628
  function orchManifestPath(cwd2) {
66353
- return path82.join(cwd2, ORCH_MANIFEST_FILE);
66629
+ return path83.join(cwd2, ORCH_MANIFEST_FILE);
66354
66630
  }
66355
66631
  function hasOrchManifest(cwd2) {
66356
- return fs73.existsSync(orchManifestPath(cwd2));
66632
+ return fs74.existsSync(orchManifestPath(cwd2));
66357
66633
  }
66358
66634
  function readOrchManifest(cwd2) {
66359
66635
  const p2 = orchManifestPath(cwd2);
66360
- if (!fs73.existsSync(p2))
66636
+ if (!fs74.existsSync(p2))
66361
66637
  return null;
66362
- const raw = fs73.readFileSync(p2, "utf8");
66638
+ const raw = fs74.readFileSync(p2, "utf8");
66363
66639
  let parsed;
66364
66640
  try {
66365
- parsed = import_yaml3.default.parse(raw);
66641
+ parsed = import_yaml4.default.parse(raw);
66366
66642
  } catch (err) {
66367
66643
  throw new Error(`${ORCH_MANIFEST_FILE} is not valid YAML: ${err.message}`);
66368
66644
  }
@@ -66373,17 +66649,17 @@ function readOrchManifest(cwd2) {
66373
66649
  return result2.data;
66374
66650
  }
66375
66651
  function writeOrchManifest(cwd2, manifest) {
66376
- const doc = new import_yaml3.default.Document;
66652
+ const doc = new import_yaml4.default.Document;
66377
66653
  doc.contents = manifest;
66378
66654
  doc.commentBefore = ` brainbase-orchestration.yaml — declarative orchestration manifest.
66379
66655
  ` + ` Committed to source control. Edit by hand, then
66380
66656
  ` + " `brainbase orchestration push`. Member agents live under ./agents/." + `
66381
66657
  Schedule triggers are writable. App/Pipedream triggers are preserved
66382
66658
  ` + " as read-only context and ignored by `orchestration push`.";
66383
- fs73.writeFileSync(orchManifestPath(cwd2), String(doc), "utf8");
66659
+ fs74.writeFileSync(orchManifestPath(cwd2), String(doc), "utf8");
66384
66660
  }
66385
66661
  function memberDir(cwd2, slug) {
66386
- return path82.join(cwd2, ORCH_MEMBERS_DIR, slug);
66662
+ return path83.join(cwd2, ORCH_MEMBERS_DIR, slug);
66387
66663
  }
66388
66664
  var MEMBER_SLUG_MAX = 50;
66389
66665
  function slugifyRaw(raw) {
@@ -66417,8 +66693,8 @@ function resolveMemberSlugs(members) {
66417
66693
  }
66418
66694
 
66419
66695
  // src/core/orchestration-link.ts
66420
- import path83 from "node:path";
66421
- import fs74 from "node:fs";
66696
+ import path84 from "node:path";
66697
+ import fs75 from "node:fs";
66422
66698
  var ORCH_LINK_FILE = "orchestration-link.json";
66423
66699
  var ORCH_SYNC_STATE_FILE = "orchestration-sync-state.json";
66424
66700
  var OrchestrationLinkSchema = exports_external.object({
@@ -66452,10 +66728,10 @@ var OrchestrationSyncStateSchema = exports_external.object({
66452
66728
  edges: exports_external.array(SyncedEdgeSchema)
66453
66729
  });
66454
66730
  function orchLinkPath(cwd2) {
66455
- return path83.join(cwd2, LINK_DIR, ORCH_LINK_FILE);
66731
+ return path84.join(cwd2, LINK_DIR, ORCH_LINK_FILE);
66456
66732
  }
66457
66733
  function orchSyncStatePath(cwd2) {
66458
- return path83.join(cwd2, LINK_DIR, ORCH_SYNC_STATE_FILE);
66734
+ return path84.join(cwd2, LINK_DIR, ORCH_SYNC_STATE_FILE);
66459
66735
  }
66460
66736
  function readOrchLink(cwd2) {
66461
66737
  const p2 = orchLinkPath(cwd2);
@@ -66468,7 +66744,7 @@ function readOrchLink(cwd2) {
66468
66744
  }
66469
66745
  }
66470
66746
  function writeOrchLink(cwd2, link2) {
66471
- ensureDir(path83.join(cwd2, LINK_DIR));
66747
+ ensureDir(path84.join(cwd2, LINK_DIR));
66472
66748
  const clean = {};
66473
66749
  for (const [k3, v3] of Object.entries(link2)) {
66474
66750
  if (v3 !== null && v3 !== undefined)
@@ -66488,22 +66764,22 @@ function readOrchSyncState(cwd2) {
66488
66764
  }
66489
66765
  }
66490
66766
  function writeOrchSyncState(cwd2, state) {
66491
- ensureDir(path83.join(cwd2, LINK_DIR));
66767
+ ensureDir(path84.join(cwd2, LINK_DIR));
66492
66768
  writeJson(orchSyncStatePath(cwd2), state);
66493
66769
  ensureGitignore2(cwd2);
66494
66770
  }
66495
66771
  function ensureGitignore2(cwd2) {
66496
- const ignorePath = path83.join(cwd2, LINK_DIR, ".gitignore");
66772
+ const ignorePath = path84.join(cwd2, LINK_DIR, ".gitignore");
66497
66773
  const desired = `${ORCH_SYNC_STATE_FILE}
66498
66774
  `;
66499
66775
  try {
66500
66776
  if (!exists(ignorePath)) {
66501
- fs74.writeFileSync(ignorePath, desired);
66777
+ fs75.writeFileSync(ignorePath, desired);
66502
66778
  return;
66503
66779
  }
66504
- const current = fs74.readFileSync(ignorePath, "utf8");
66780
+ const current = fs75.readFileSync(ignorePath, "utf8");
66505
66781
  if (!current.split(/\r?\n/).some((l2) => l2.trim() === ORCH_SYNC_STATE_FILE)) {
66506
- fs74.writeFileSync(ignorePath, current.endsWith(`
66782
+ fs75.writeFileSync(ignorePath, current.endsWith(`
66507
66783
  `) ? current + desired : current + `
66508
66784
  ` + desired);
66509
66785
  }
@@ -66511,8 +66787,8 @@ function ensureGitignore2(cwd2) {
66511
66787
  }
66512
66788
 
66513
66789
  // src/core/agent-fresh-install.ts
66514
- import path84 from "node:path";
66515
- import fs75 from "node:fs";
66790
+ import path85 from "node:path";
66791
+ import fs76 from "node:fs";
66516
66792
  import os15 from "node:os";
66517
66793
  async function installAgentFresh(input) {
66518
66794
  const { cwd: cwd2, agent, cloud, harness } = input;
@@ -66534,7 +66810,7 @@ async function installAgentFresh(input) {
66534
66810
  type: c2.type,
66535
66811
  slug: c2.slug,
66536
66812
  scope,
66537
- rootDir: path84.join(stageRoot, c2.type, c2.slug),
66813
+ rootDir: path85.join(stageRoot, c2.type, c2.slug),
66538
66814
  description: c2.description,
66539
66815
  meta: c2.meta,
66540
66816
  payload: proxifyMcpPayload(structuredClone(c2.meta?.mcp)),
@@ -66556,7 +66832,12 @@ async function installAgentFresh(input) {
66556
66832
  }
66557
66833
  materializeInstructions2(cwd2, cloud);
66558
66834
  materializePlaybooks2(cwd2, cloud);
66559
- const manifest = input.preserveManifest ? null : buildManifestFromCloud(cloud, agent);
66835
+ const claimedByOther = folderClaimedByOtherAgent(cwd2, agent.id);
66836
+ const localOnly = input.preserveManifest || claimedByOther ? {} : readLocalOnlyContentReporting(cwd2);
66837
+ if (claimedByOther) {
66838
+ f2.warn(`${path85.basename(cwd2)} was linked to a different agent; its local-only blocks were left out of the rebuilt manifest.`);
66839
+ }
66840
+ const manifest = input.preserveManifest ? null : buildManifestFromCloud(cloud, agent, localOnly);
66560
66841
  if (manifest)
66561
66842
  writeManifest(cwd2, manifest);
66562
66843
  writeLink(cwd2, {
@@ -66590,7 +66871,7 @@ async function installAgentFresh(input) {
66590
66871
  ...Object.prototype.hasOwnProperty.call(agent, "default_model") ? { default_model: agent.default_model ?? null } : {}
66591
66872
  }
66592
66873
  });
66593
- const returnedManifest = manifest ?? buildManifestFromCloud(cloud, agent);
66874
+ const returnedManifest = manifest ?? buildManifestFromCloud(cloud, agent, localOnly);
66594
66875
  return {
66595
66876
  installedPaths: justInstalledPaths,
66596
66877
  manifest: returnedManifest,
@@ -66598,19 +66879,19 @@ async function installAgentFresh(input) {
66598
66879
  };
66599
66880
  } finally {
66600
66881
  try {
66601
- fs75.rmSync(stageRoot, { recursive: true, force: true });
66882
+ fs76.rmSync(stageRoot, { recursive: true, force: true });
66602
66883
  } catch {}
66603
66884
  }
66604
66885
  }
66605
66886
  function stageManifestComponents2(components) {
66606
- const root = fs75.mkdtempSync(path84.join(os15.tmpdir(), "brainbase-orch-pull-"));
66887
+ const root = fs76.mkdtempSync(path85.join(os15.tmpdir(), "brainbase-orch-pull-"));
66607
66888
  for (const c2 of components) {
66608
- const compDir = path84.join(root, c2.type, c2.slug);
66889
+ const compDir = path85.join(root, c2.type, c2.slug);
66609
66890
  ensureDir(compDir);
66610
66891
  for (const f4 of c2.files) {
66611
- const target = path84.join(compDir, f4.path);
66612
- ensureDir(path84.dirname(target));
66613
- fs75.writeFileSync(target, f4.content);
66892
+ const target = path85.join(compDir, f4.path);
66893
+ ensureDir(path85.dirname(target));
66894
+ fs76.writeFileSync(target, f4.content);
66614
66895
  }
66615
66896
  }
66616
66897
  return root;
@@ -66643,9 +66924,9 @@ function materializeInstructions2(cwd2, cloud) {
66643
66924
  const body = c2.files[0]?.content ?? "";
66644
66925
  if (!body.trim())
66645
66926
  continue;
66646
- const target = path84.join(cwd2, DEFAULT_INSTRUCTIONS_FILE);
66647
- ensureDir(path84.dirname(target));
66648
- fs75.writeFileSync(target, normalizeInstructionBody(body), "utf8");
66927
+ const target = path85.join(cwd2, DEFAULT_INSTRUCTIONS_FILE);
66928
+ ensureDir(path85.dirname(target));
66929
+ fs76.writeFileSync(target, normalizeInstructionBody(body), "utf8");
66649
66930
  return;
66650
66931
  }
66651
66932
  }
@@ -66657,12 +66938,12 @@ function materializePlaybooks2(cwd2, cloud) {
66657
66938
  if (!raw.trim())
66658
66939
  continue;
66659
66940
  const { body } = stripPlaybookFrontmatter(raw);
66660
- const target = path84.join(cwd2, DEFAULT_PLAYBOOKS_DIR, `${c2.slug}.md`);
66661
- ensureDir(path84.dirname(target));
66662
- fs75.writeFileSync(target, body, "utf8");
66941
+ const target = path85.join(cwd2, DEFAULT_PLAYBOOKS_DIR, `${c2.slug}.md`);
66942
+ ensureDir(path85.dirname(target));
66943
+ fs76.writeFileSync(target, body, "utf8");
66663
66944
  }
66664
66945
  }
66665
- function buildManifestFromCloud(cloud, agent) {
66946
+ function buildManifestFromCloud(cloud, agent, localOnly = {}) {
66666
66947
  const skills = cloud.components.filter((c2) => c2.type === "skill").map((c2) => {
66667
66948
  const meta = c2.meta ?? {};
66668
66949
  if (meta.name && meta.name.includes("/")) {
@@ -66701,7 +66982,7 @@ function buildManifestFromCloud(cloud, agent) {
66701
66982
  title,
66702
66983
  ...description ? { description } : {},
66703
66984
  ...typeof pbMeta.icon === "string" && pbMeta.icon ? { icon: pbMeta.icon } : {},
66704
- content: { file: path84.join(DEFAULT_PLAYBOOKS_DIR, `${c2.slug}.md`) }
66985
+ content: { file: path85.join(DEFAULT_PLAYBOOKS_DIR, `${c2.slug}.md`) }
66705
66986
  };
66706
66987
  });
66707
66988
  const caps = capabilitiesFromAgent(agent);
@@ -66715,7 +66996,8 @@ function buildManifestFromCloud(cloud, agent) {
66715
66996
  },
66716
66997
  ...hasInstructions ? { instructions: { file: DEFAULT_INSTRUCTIONS_FILE } } : {},
66717
66998
  playbooks,
66718
- evals: [],
66999
+ evals: localOnly.evals ?? [],
67000
+ ...localOnly,
66719
67001
  skills,
66720
67002
  mcp,
66721
67003
  capabilities: {
@@ -66730,7 +67012,7 @@ function buildManifestFromCloud(cloud, agent) {
66730
67012
  async function pullAgentSecrets(cwd2, agentId) {
66731
67013
  try {
66732
67014
  const res = await api.getAgentSecrets(agentId);
66733
- const secrets = res.secrets ?? {};
67015
+ const secrets = res.secrets;
66734
67016
  if (Object.keys(secrets).length > 0) {
66735
67017
  writeLocalSecrets(cwd2, secrets);
66736
67018
  }
@@ -66742,6 +67024,17 @@ async function pullAgentSecrets(cwd2, agentId) {
66742
67024
  return {};
66743
67025
  }
66744
67026
  }
67027
+ function folderClaimedByOtherAgent(cwd2, agentId) {
67028
+ const claimedBy = readManifestAgentId(cwd2) ?? safeRead(() => readLink(cwd2)?.agent_id) ?? safeRead(() => readSyncState(cwd2)?.agent_id);
67029
+ return claimedBy !== undefined && claimedBy !== agentId;
67030
+ }
67031
+ function safeRead(read) {
67032
+ try {
67033
+ return read();
67034
+ } catch {
67035
+ return;
67036
+ }
67037
+ }
66745
67038
 
66746
67039
  // src/core/orchestration-trigger-config.ts
66747
67040
  function normalizeScheduleTriggerConfig(config) {
@@ -66851,7 +67144,7 @@ async function runOrchestrationPull(cwd2, args) {
66851
67144
  }
66852
67145
  }
66853
67146
  const fallbackHarness = args.harness ?? "claude-code";
66854
- fs76.mkdirSync(cwd2, { recursive: true });
67147
+ fs77.mkdirSync(cwd2, { recursive: true });
66855
67148
  if (hasOrchManifest(cwd2) && existingLink && existingLink.orchestration_id !== orchId) {
66856
67149
  f2.error(`This folder is linked to orchestration ${existingLink.orchestration_id}, not ${orchId}. Move to a fresh directory or unlink first.`);
66857
67150
  return;
@@ -66947,7 +67240,7 @@ async function runOrchestrationPull(cwd2, args) {
66947
67240
  payload_schema: e2.payload_schema ?? {}
66948
67241
  }))
66949
67242
  });
66950
- $e(`Pulled ${cloud.name} at revision ${cloud.revision} into ${path85.basename(cwd2)}/ ${import_picocolors37.default.dim(`(${installedMembers.length}/${cloud.members.length} members)`)}.`);
67243
+ $e(`Pulled ${cloud.name} at revision ${cloud.revision} into ${path86.basename(cwd2)}/ ${import_picocolors37.default.dim(`(${installedMembers.length}/${cloud.members.length} members)`)}.`);
66951
67244
  }
66952
67245
  function handleApiError5(err) {
66953
67246
  if (err instanceof ApiError) {
@@ -67029,6 +67322,34 @@ function buildOrchestrationGraphPayload(manifest, slugToAgentId) {
67029
67322
  }
67030
67323
 
67031
67324
  // src/cli/orchestration-push.ts
67325
+ function findUnpushableMembers(cwd2, members) {
67326
+ const blocked = [];
67327
+ for (const m3 of members) {
67328
+ const dir = memberDir(cwd2, m3.slug);
67329
+ if (!hasManifest(dir)) {
67330
+ f2.error(`${m3.slug}: no ${AGENT_MANIFEST_FILE} in this member folder.`);
67331
+ blocked.push(m3.slug);
67332
+ continue;
67333
+ }
67334
+ let memberManifest;
67335
+ try {
67336
+ memberManifest = readManifest(dir);
67337
+ } catch (err) {
67338
+ f2.error(`${m3.slug}: ${err.message}`);
67339
+ blocked.push(m3.slug);
67340
+ continue;
67341
+ }
67342
+ if (!memberManifest.id) {
67343
+ f2.error(`${m3.slug}: ${AGENT_MANIFEST_FILE} is unclaimed (no ${import_picocolors38.default.cyan("id")}), so there is nothing to push to.`);
67344
+ blocked.push(m3.slug);
67345
+ continue;
67346
+ }
67347
+ if (reportUnsyncedBlocks(memberManifest, { label: m3.slug })) {
67348
+ blocked.push(m3.slug);
67349
+ }
67350
+ }
67351
+ return blocked;
67352
+ }
67032
67353
  async function runOrchestrationPush(cwd2, args) {
67033
67354
  banner("orchestration push — recursively push each member, then update the graph");
67034
67355
  const link2 = readOrchLink(cwd2);
@@ -67064,6 +67385,7 @@ async function runOrchestrationPush(cwd2, args) {
67064
67385
  if (missing.length) {
67065
67386
  f2.error(`Missing local checkouts for: ${missing.join(", ")}.`);
67066
67387
  f2.info(`Run ${import_picocolors38.default.cyan("brainbase orchestration pull")} to materialise the missing folders.`);
67388
+ process.exitCode = 1;
67067
67389
  return;
67068
67390
  }
67069
67391
  let graph;
@@ -67074,6 +67396,14 @@ async function runOrchestrationPush(cwd2, args) {
67074
67396
  process.exitCode = 1;
67075
67397
  return;
67076
67398
  }
67399
+ if (!args.graphOnly) {
67400
+ const blockedMembers = findUnpushableMembers(cwd2, manifest.members);
67401
+ if (blockedMembers.length > 0) {
67402
+ f2.error(`Aborted — ${blockedMembers.join(", ")} cannot be pushed, so no member was pushed and the graph is unchanged.`);
67403
+ process.exitCode = 1;
67404
+ return;
67405
+ }
67406
+ }
67077
67407
  const plan = [""];
67078
67408
  plan.push(` ${import_picocolors38.default.bold(link2.name)} ${import_picocolors38.default.dim(`(${link2.orchestration_id})`)}`);
67079
67409
  plan.push(` ${import_picocolors38.default.dim(`${manifest.members.length} member${manifest.members.length === 1 ? "" : "s"}, ${manifest.edges.length} edge${manifest.edges.length === 1 ? "" : "s"}, ${graph.triggers.length} trigger${graph.triggers.length === 1 ? "" : "s"}`)}`);
@@ -67102,6 +67432,7 @@ async function runOrchestrationPush(cwd2, args) {
67102
67432
  const dir = memberDir(cwd2, m3.slug);
67103
67433
  console.log("");
67104
67434
  console.log(`${import_picocolors38.default.dim("───")} ${import_picocolors38.default.bold(m3.slug)} ${import_picocolors38.default.dim("───")}`);
67435
+ const exitCodeBeforePush = process.exitCode;
67105
67436
  try {
67106
67437
  await runAgentPush(dir, { yes: true });
67107
67438
  } catch (err) {
@@ -67109,6 +67440,11 @@ async function runOrchestrationPush(cwd2, args) {
67109
67440
  process.exitCode = 1;
67110
67441
  return;
67111
67442
  }
67443
+ if (process.exitCode !== exitCodeBeforePush) {
67444
+ f2.error(`Aborted — ${m3.slug} was not pushed, so the graph is left unchanged.`);
67445
+ process.exitCode = 1;
67446
+ return;
67447
+ }
67112
67448
  }
67113
67449
  }
67114
67450
  const sp = de();
@@ -67374,7 +67710,7 @@ async function runOrchestrationList(args) {
67374
67710
  }
67375
67711
 
67376
67712
  // src/cli/orchestration-add-agent.ts
67377
- import fs77 from "node:fs";
67713
+ import fs78 from "node:fs";
67378
67714
  var import_picocolors41 = __toESM(require_picocolors(), 1);
67379
67715
 
67380
67716
  // src/core/orchestration-add.ts
@@ -67460,10 +67796,10 @@ async function runOrchestrationAddAgent(cwd2, args) {
67460
67796
  })).trim();
67461
67797
  }
67462
67798
  let slug = slugifyMemberName(name);
67463
- if (manifest.members.some((m3) => m3.slug === slug) || fs77.existsSync(memberDir(cwd2, slug))) {
67799
+ if (manifest.members.some((m3) => m3.slug === slug) || fs78.existsSync(memberDir(cwd2, slug))) {
67464
67800
  let n = 2;
67465
67801
  let candidate = `${slug}-${n}`;
67466
- while (manifest.members.some((m3) => m3.slug === candidate) || fs77.existsSync(memberDir(cwd2, candidate))) {
67802
+ while (manifest.members.some((m3) => m3.slug === candidate) || fs78.existsSync(memberDir(cwd2, candidate))) {
67467
67803
  candidate = `${slug}-${++n}`;
67468
67804
  }
67469
67805
  f2.info(`Slug ${import_picocolors41.default.bold(slug)} is taken — using ${import_picocolors41.default.bold(candidate)}.`);
@@ -67535,7 +67871,7 @@ async function runOrchestrationAddAgent(cwd2, args) {
67535
67871
  }
67536
67872
  const dest = memberDir(cwd2, slug);
67537
67873
  try {
67538
- fs77.mkdirSync(dest, { recursive: true });
67874
+ fs78.mkdirSync(dest, { recursive: true });
67539
67875
  await runAgentCreate(dest, {
67540
67876
  name,
67541
67877
  orgId,
@@ -67546,7 +67882,7 @@ async function runOrchestrationAddAgent(cwd2, args) {
67546
67882
  });
67547
67883
  } catch (err) {
67548
67884
  try {
67549
- fs77.rmSync(dest, { recursive: true, force: true });
67885
+ fs78.rmSync(dest, { recursive: true, force: true });
67550
67886
  } catch {}
67551
67887
  f2.error(`Failed to create ${slug}: ${err.message}`);
67552
67888
  return;
@@ -68234,10 +68570,13 @@ function TokenListCard(props) {
68234
68570
  ]
68235
68571
  }, undefined, true, undefined, this);
68236
68572
  }
68573
+ const liveCount = props.active.filter((t) => !t.expired).length;
68574
+ const expiredCount = props.active.length - liveCount;
68575
+ const subtitle = expiredCount ? `${liveCount} active, ${expiredCount} expired` : `${liveCount} active`;
68237
68576
  return /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Card, {
68238
68577
  title: "TOKENS",
68239
68578
  tone: "info",
68240
- subtitle: `${props.active.length} active`,
68579
+ subtitle,
68241
68580
  children: [
68242
68581
  /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Box_default, {
68243
68582
  flexDirection: "column",
@@ -68249,8 +68588,17 @@ function TokenListCard(props) {
68249
68588
  children: [
68250
68589
  /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Text, {
68251
68590
  bold: true,
68591
+ dimColor: t.expired,
68252
68592
  children: t.name
68253
68593
  }, undefined, false, undefined, this),
68594
+ t.expired && /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Box_default, {
68595
+ marginLeft: 1,
68596
+ children: /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Badge, {
68597
+ tone: "warn",
68598
+ outline: true,
68599
+ children: "expired"
68600
+ }, undefined, false, undefined, this)
68601
+ }, undefined, false, undefined, this),
68254
68602
  t.isThisCli && /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Box_default, {
68255
68603
  marginLeft: 1,
68256
68604
  children: /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Badge, {
@@ -68335,11 +68683,18 @@ function TokenListCard(props) {
68335
68683
  }, undefined, true, undefined, this),
68336
68684
  /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Box_default, {
68337
68685
  marginTop: 1,
68338
- children: /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Text, {
68339
- dimColor: true,
68340
- children: "› brainbase token revoke <id>"
68341
- }, undefined, false, undefined, this)
68342
- }, undefined, false, undefined, this)
68686
+ flexDirection: "column",
68687
+ children: [
68688
+ /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Text, {
68689
+ dimColor: true,
68690
+ children: "› brainbase token rename <id> --name <label>"
68691
+ }, undefined, false, undefined, this),
68692
+ /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Text, {
68693
+ dimColor: true,
68694
+ children: "› brainbase token revoke <id>"
68695
+ }, undefined, false, undefined, this)
68696
+ ]
68697
+ }, undefined, true, undefined, this)
68343
68698
  ]
68344
68699
  }, undefined, true, undefined, this);
68345
68700
  }
@@ -68351,6 +68706,16 @@ async function showTokenListCard(props) {
68351
68706
 
68352
68707
  // src/cli/token.ts
68353
68708
  var DEFAULT_SCOPES = ["read", "publish"];
68709
+ var MAX_NAME_LENGTH = 128;
68710
+ function isExpired2(token) {
68711
+ return Boolean(token.expires_at && Date.parse(token.expires_at) <= Date.now());
68712
+ }
68713
+ function withLoginHint(error) {
68714
+ if (error instanceof ApiError && error.status === 403) {
68715
+ return new Error("Managing tokens needs a logged-in session; a PAT (BRAINBASE_TOKEN) cannot. " + "Run `brainbase login` and try again.");
68716
+ }
68717
+ return error;
68718
+ }
68354
68719
  async function runTokenCreate(args) {
68355
68720
  banner("token create — make a long-lived CLI key");
68356
68721
  let name = args.name;
@@ -68370,7 +68735,15 @@ async function runTokenCreate(args) {
68370
68735
  scopes
68371
68736
  });
68372
68737
  spinner.stop("Token created.");
68373
- writeToken(created.token, name);
68738
+ try {
68739
+ writeToken(created.token, name);
68740
+ } catch (error) {
68741
+ throw new Error(`Created token ${created.id}, but could not save it locally: ${error.message}
68742
+ ` + `This is the only time it is shown — copy it now:
68743
+
68744
+ ${created.token}
68745
+ `);
68746
+ }
68374
68747
  await showTokenCreatedCard({
68375
68748
  token: created.token,
68376
68749
  id: created.id,
@@ -68392,30 +68765,147 @@ async function runTokenList() {
68392
68765
  prefix: t.prefix,
68393
68766
  scopes: t.scopes,
68394
68767
  lastUsed: t.last_used_at ?? null,
68395
- isThisCli: !!(local && local.token.startsWith(t.prefix))
68768
+ isThisCli: !!(local && local.token.startsWith(t.prefix)),
68769
+ expired: isExpired2(t)
68396
68770
  })),
68397
68771
  revokedCount: revoked.length
68398
68772
  });
68399
68773
  }
68774
+ async function runTokenRename(args) {
68775
+ if (!args.id) {
68776
+ throw new Error("Usage: brainbase token rename <id> --name <label>");
68777
+ }
68778
+ banner("token rename — relabel a CLI key");
68779
+ let tokens;
68780
+ try {
68781
+ tokens = await registryApi.listCliTokens();
68782
+ } catch (error) {
68783
+ throw withLoginHint(error);
68784
+ }
68785
+ const target = tokens.find((t) => t.id === args.id);
68786
+ if (!target) {
68787
+ throw new Error(`No token with id ${args.id}. Run \`brainbase token list\` to see yours.`);
68788
+ }
68789
+ if (target.revoked_at) {
68790
+ throw new Error(`Token ${args.id} is revoked, and revoked tokens cannot be renamed.`);
68791
+ }
68792
+ if (isExpired2(target)) {
68793
+ throw new Error(`Token ${args.id} expired on ${target.expires_at}, and expired tokens cannot be renamed.`);
68794
+ }
68795
+ const takenBy = new Map;
68796
+ for (const t of tokens) {
68797
+ if (t.revoked_at || isExpired2(t) || t.id === target.id)
68798
+ continue;
68799
+ takenBy.set(t.name.trim().toLowerCase(), t);
68800
+ }
68801
+ const validate2 = (value) => {
68802
+ const candidate = value.trim();
68803
+ if (!candidate)
68804
+ return "Required.";
68805
+ if (candidate.length > MAX_NAME_LENGTH) {
68806
+ return `Too long — keep the label to ${MAX_NAME_LENGTH} characters or fewer.`;
68807
+ }
68808
+ const clash = takenBy.get(candidate.toLowerCase());
68809
+ if (clash) {
68810
+ return `Token ${clash.id} already uses the label "${clash.name}". Labels are how \`token list\` tells keys apart, so pick a different one.`;
68811
+ }
68812
+ return;
68813
+ };
68814
+ let name;
68815
+ if (args.name === undefined) {
68816
+ const answer = await text({
68817
+ message: "New label",
68818
+ placeholder: target.name,
68819
+ validate: validate2,
68820
+ flagHint: "Pass --name <label>."
68821
+ });
68822
+ name = answer.trim();
68823
+ } else {
68824
+ name = args.name.trim();
68825
+ const problem = validate2(name);
68826
+ if (problem) {
68827
+ throw new Error(name ? problem : "--name cannot be blank.");
68828
+ }
68829
+ }
68830
+ if (name === target.name.trim()) {
68831
+ console.log(`${sym.ok} ${import_picocolors45.default.bold(target.name.trim())} already has that label; nothing to do.`);
68832
+ return;
68833
+ }
68834
+ try {
68835
+ await registryApi.renameCliToken(target.id, name);
68836
+ } catch (error) {
68837
+ throw withLoginHint(error);
68838
+ }
68839
+ console.log(`${sym.ok} Renamed ${import_picocolors45.default.dim(target.name)} → ${import_picocolors45.default.bold(name)}`);
68840
+ }
68400
68841
  async function runTokenRevoke(args) {
68401
68842
  if (!args.id) {
68402
68843
  console.error("Usage: brainbase token revoke <id>");
68403
68844
  process.exit(1);
68404
68845
  }
68846
+ const tokens = await registryApi.listCliTokens();
68847
+ const target = tokens.find((t) => t.id === args.id);
68848
+ if (!target) {
68849
+ throw new Error(`No token with id ${args.id}. Run \`brainbase token list\` to see yours.`);
68850
+ }
68851
+ if (target.revoked_at) {
68852
+ reconcileDeadToken(target, `${import_picocolors45.default.bold(target.name)} is already revoked.`);
68853
+ return;
68854
+ }
68855
+ const stored = readToken();
68856
+ const isLocalToken = Boolean(stored && stored.token.startsWith(target.prefix));
68405
68857
  if (!autoProceed(args.yes)) {
68406
68858
  const ok = await se({
68407
- message: `Revoke token ${import_picocolors45.default.bold(args.id)}? CIs and machines using it will stop working.`,
68859
+ message: isLocalToken ? `Revoke ${import_picocolors45.default.bold(target.name)} (${args.id})? This is the token this CLI is using, so it will stop working here too.` : `Revoke ${import_picocolors45.default.bold(target.name)} (${args.id})? CIs and machines using it will stop working.`,
68408
68860
  initialValue: false
68409
68861
  });
68410
68862
  if (!ensureNotCancelled(ok))
68411
68863
  return;
68412
68864
  }
68413
- await registryApi.revokeCliToken(args.id);
68414
- const local = readToken();
68415
- if (local) {
68416
- clearToken();
68865
+ try {
68866
+ await registryApi.revokeCliToken(args.id);
68867
+ } catch (error) {
68868
+ if (error instanceof ApiError && error.status === 404) {
68869
+ const expiryPassed = Boolean(target.expires_at && Date.parse(target.expires_at) <= Date.now());
68870
+ if (expiryPassed) {
68871
+ reconcileDeadToken(target, `${import_picocolors45.default.bold(target.name)} had already expired.`);
68872
+ return;
68873
+ }
68874
+ throw new Error(`The server reported no active token with id ${args.id}, but it listed one a moment ago. ` + "The local token has been left alone, since that key may still work. " + "If this server predates `DELETE /v1/registry/cli-tokens/{id}`, revoke from the web app instead.");
68875
+ }
68876
+ throw error;
68877
+ }
68878
+ reconcileDeadToken(target, `Revoked ${import_picocolors45.default.bold(target.name)}.`);
68879
+ }
68880
+ function reconcileDeadToken(target, headline) {
68881
+ let outcome;
68882
+ try {
68883
+ outcome = clearTokenIfMatches(target.prefix);
68884
+ } catch (error) {
68885
+ throw new Error(`${headline} That key is dead server-side, but the local token could not be ` + `cleared: ${error.message}
68886
+ ` + "Run `brainbase token clear` to remove it.");
68887
+ }
68888
+ reportLocalToken(headline, outcome);
68889
+ }
68890
+ function reportLocalToken(headline, outcome) {
68891
+ switch (outcome) {
68892
+ case "cleared":
68893
+ console.log(`${sym.ok} ${headline} Cleared the local token too.`);
68894
+ return;
68895
+ case "kept":
68896
+ console.log(`${sym.ok} ${headline} The local token is a different key and is untouched.`);
68897
+ return;
68898
+ case "absent":
68899
+ console.log(`${sym.ok} ${headline}`);
68900
+ return;
68901
+ case "unverifiable":
68902
+ console.log(`${sym.ok} ${headline} Left the local token alone.`);
68903
+ return;
68904
+ default: {
68905
+ const exhaustive = outcome;
68906
+ throw new Error(`unhandled outcome: ${String(exhaustive)}`);
68907
+ }
68417
68908
  }
68418
- console.log(`${sym.ok} Revoked.`);
68419
68909
  }
68420
68910
  async function runTokenClear() {
68421
68911
  if (!readToken()) {
@@ -68433,22 +68923,45 @@ function pickFlag(rest2, ...names) {
68433
68923
  }
68434
68924
  return;
68435
68925
  }
68926
+ function firstPositional(rest2, ...valueFlags) {
68927
+ const consumesValue = new Set(valueFlags);
68928
+ for (let i = 0;i < rest2.length; i++) {
68929
+ const arg = rest2[i];
68930
+ if (consumesValue.has(arg)) {
68931
+ i++;
68932
+ continue;
68933
+ }
68934
+ if (arg.startsWith("-"))
68935
+ continue;
68936
+ return arg;
68937
+ }
68938
+ return;
68939
+ }
68436
68940
  function parseScopes(raw) {
68437
68941
  if (!raw)
68438
68942
  return;
68439
68943
  return raw.split(",").map((s3) => s3.trim()).filter((s3) => s3.length > 0);
68440
68944
  }
68441
68945
  async function runToken(sub, rest2, args) {
68946
+ const nameFlag = args.name || pickFlag(rest2, "--name", "-n");
68442
68947
  switch (sub) {
68443
68948
  case "create":
68444
68949
  case "new": {
68445
- const name = pickFlag(rest2, "--name", "-n");
68446
68950
  const scopes = parseScopes(pickFlag(rest2, "--scope", "--scopes"));
68447
- return runTokenCreate({ name, scopes });
68951
+ return runTokenCreate({ name: nameFlag, scopes });
68448
68952
  }
68449
68953
  case "list":
68450
68954
  case "ls":
68451
68955
  return runTokenList();
68956
+ case "rename": {
68957
+ if (pickFlag(rest2, "--scope", "--scopes") !== undefined) {
68958
+ throw new Error("token rename only changes the label; a token's scopes are fixed when it is created. " + "Mint a replacement with `brainbase token create --scopes <list>`.");
68959
+ }
68960
+ return runTokenRename({
68961
+ id: firstPositional(rest2, "--name", "-n") ?? "",
68962
+ name: nameFlag
68963
+ });
68964
+ }
68452
68965
  case "revoke":
68453
68966
  case "rm":
68454
68967
  return runTokenRevoke({ id: rest2[0] ?? "", yes: args.yes });
@@ -68473,7 +68986,8 @@ function printTokenHelp() {
68473
68986
  out.push(` ${import_picocolors45.default.bold("brainbase token")} ${import_picocolors45.default.dim("<command>")}`);
68474
68987
  out.push("");
68475
68988
  out.push(` ${import_picocolors45.default.cyan("create")} ${import_picocolors45.default.dim("issue a new long-lived CLI key (PAT)")}`);
68476
- out.push(` ${import_picocolors45.default.cyan("list")} ${import_picocolors45.default.dim("show your active tokens")}`);
68989
+ out.push(` ${import_picocolors45.default.cyan("list")} ${import_picocolors45.default.dim("show your tokens")}`);
68990
+ out.push(` ${import_picocolors45.default.cyan("rename")} ${import_picocolors45.default.dim("<id>")} ${import_picocolors45.default.dim("relabel a token by id")}`);
68477
68991
  out.push(` ${import_picocolors45.default.cyan("revoke")} ${import_picocolors45.default.dim("<id>")} ${import_picocolors45.default.dim("revoke a token by id")}`);
68478
68992
  out.push(` ${import_picocolors45.default.cyan("clear")} ${import_picocolors45.default.dim("forget the local token (does not revoke)")}`);
68479
68993
  out.push("");
@@ -68482,6 +68996,9 @@ function printTokenHelp() {
68482
68996
  out.push(` ${import_picocolors45.default.cyan("--scopes")} ${import_picocolors45.default.dim("<list>")} ${import_picocolors45.default.dim("comma-separated; allowed: read, publish, admin")}`);
68483
68997
  out.push(` ${import_picocolors45.default.dim("default: read,publish")}`);
68484
68998
  out.push("");
68999
+ out.push(` ${import_picocolors45.default.bold("rename flags")}`);
69000
+ out.push(` ${import_picocolors45.default.cyan("--name, -n")} ${import_picocolors45.default.dim("<label>")} ${import_picocolors45.default.dim("new label (prompted if omitted)")}`);
69001
+ out.push("");
68485
69002
  console.log(out.join(`
68486
69003
  `));
68487
69004
  }
@@ -68490,8 +69007,8 @@ function printTokenHelp() {
68490
69007
  var import_picocolors46 = __toESM(require_picocolors(), 1);
68491
69008
 
68492
69009
  // src/core/mcp-check/collect-servers.ts
68493
- import path86 from "node:path";
68494
- import fs78 from "node:fs";
69010
+ import path87 from "node:path";
69011
+ import fs79 from "node:fs";
68495
69012
  function collectServers(cwd2, env3 = process.env) {
68496
69013
  const out = [];
68497
69014
  const seen = new Set;
@@ -68542,10 +69059,10 @@ function pushResolved(out, seen, name, entry, env3) {
68542
69059
  out.push({ name, url: finalUrl, headers });
68543
69060
  }
68544
69061
  function* readResolvedMcps(cwd2) {
68545
- const p2 = path86.join(cwd2, ".brainbase", "resolved-mcps.json");
69062
+ const p2 = path87.join(cwd2, ".brainbase", "resolved-mcps.json");
68546
69063
  let raw;
68547
69064
  try {
68548
- raw = fs78.readFileSync(p2, "utf-8");
69065
+ raw = fs79.readFileSync(p2, "utf-8");
68549
69066
  } catch {
68550
69067
  return;
68551
69068
  }
@@ -68569,12 +69086,12 @@ function* readResolvedMcps(cwd2) {
68569
69086
  }
68570
69087
  }
68571
69088
  function readClaudeCode(cwd2) {
68572
- const file = path86.join(cwd2, ".mcp.json");
69089
+ const file = path87.join(cwd2, ".mcp.json");
68573
69090
  const map2 = listMcpServersFromMcpJson(file);
68574
69091
  return Object.entries(map2);
68575
69092
  }
68576
69093
  function readCodex(cwd2) {
68577
- const file = path86.join(cwd2, ".codex", "config.toml");
69094
+ const file = path87.join(cwd2, ".codex", "config.toml");
68578
69095
  try {
68579
69096
  return Object.entries(listMcpServers2(file));
68580
69097
  } catch {
@@ -68582,7 +69099,7 @@ function readCodex(cwd2) {
68582
69099
  }
68583
69100
  }
68584
69101
  function readKafka(cwd2) {
68585
- const file = path86.join(cwd2, ".kafka", "kafka.json");
69102
+ const file = path87.join(cwd2, ".kafka", "kafka.json");
68586
69103
  try {
68587
69104
  return Object.entries(listMcpServers3(file));
68588
69105
  } catch {
@@ -68861,10 +69378,10 @@ function assignProp(target, prop, value) {
68861
69378
  configurable: true
68862
69379
  });
68863
69380
  }
68864
- function getElementAtPath(obj, path87) {
68865
- if (!path87)
69381
+ function getElementAtPath(obj, path88) {
69382
+ if (!path88)
68866
69383
  return obj;
68867
- return path87.reduce((acc, key2) => acc?.[key2], obj);
69384
+ return path88.reduce((acc, key2) => acc?.[key2], obj);
68868
69385
  }
68869
69386
  function promiseAllObject(promisesObj) {
68870
69387
  const keys2 = Object.keys(promisesObj);
@@ -69180,11 +69697,11 @@ function aborted(x3, startIndex = 0) {
69180
69697
  }
69181
69698
  return false;
69182
69699
  }
69183
- function prefixIssues(path87, issues) {
69700
+ function prefixIssues(path88, issues) {
69184
69701
  return issues.map((iss) => {
69185
69702
  var _a;
69186
69703
  (_a = iss).path ?? (_a.path = []);
69187
- iss.path.unshift(path87);
69704
+ iss.path.unshift(path88);
69188
69705
  return iss;
69189
69706
  });
69190
69707
  }
@@ -77078,9 +77595,9 @@ import {
77078
77595
  spawn as spawn5
77079
77596
  } from "node:child_process";
77080
77597
  import crypto7 from "node:crypto";
77081
- import fs80 from "node:fs";
77598
+ import fs81 from "node:fs";
77082
77599
  import os17 from "node:os";
77083
- import path88 from "node:path";
77600
+ import path89 from "node:path";
77084
77601
 
77085
77602
  // src/core/benchmark-phase.ts
77086
77603
  import {
@@ -77088,9 +77605,9 @@ import {
77088
77605
  spawn as spawn4
77089
77606
  } from "node:child_process";
77090
77607
  import crypto6 from "node:crypto";
77091
- import fs79 from "node:fs";
77608
+ import fs80 from "node:fs";
77092
77609
  import os16 from "node:os";
77093
- import path87 from "node:path";
77610
+ import path88 from "node:path";
77094
77611
  import { pipeline as pipeline2 } from "node:stream/promises";
77095
77612
  var SCHEMA_VERSION = "1";
77096
77613
  var SHA256_RE = /^[a-f0-9]{64}$/i;
@@ -77115,7 +77632,7 @@ var BASE_ENV_NAMES = [
77115
77632
  "USER"
77116
77633
  ];
77117
77634
  var SENSITIVE_ENV_NAME_RE = /(?:^|_)(?:TOKEN|SECRET|PASSWORD|PASSWD|API_KEY|PRIVATE_KEY|ACCESS_KEY|PAT|CREDENTIAL)(?:$|_)|^(?:PGPASSWORD|DATABASE_URL|REDIS_URL|MONGODB_URI)$/i;
77118
- var AbsolutePathSchema = exports_external.string().min(1).refine(path87.isAbsolute, {
77635
+ var AbsolutePathSchema = exports_external.string().min(1).refine(path88.isAbsolute, {
77119
77636
  message: "must be an absolute path"
77120
77637
  });
77121
77638
  var Sha256Schema = exports_external.string().regex(SHA256_RE).transform((value) => value.toLowerCase());
@@ -77331,35 +77848,35 @@ function normalizedRootRelative(input) {
77331
77848
  return safeRelPath(input);
77332
77849
  }
77333
77850
  function isWithin(root, candidate) {
77334
- const relative = path87.relative(path87.resolve(root), path87.resolve(candidate));
77335
- return relative === "" || !relative.startsWith("..") && !path87.isAbsolute(relative);
77851
+ const relative = path88.relative(path88.resolve(root), path88.resolve(candidate));
77852
+ return relative === "" || !relative.startsWith("..") && !path88.isAbsolute(relative);
77336
77853
  }
77337
77854
  function canonicalFuturePath(input) {
77338
- const resolved = path87.resolve(input);
77855
+ const resolved = path88.resolve(input);
77339
77856
  const suffix = [];
77340
77857
  let current = resolved;
77341
- while (!fs79.existsSync(current)) {
77342
- const parent = path87.dirname(current);
77858
+ while (!fs80.existsSync(current)) {
77859
+ const parent = path88.dirname(current);
77343
77860
  if (parent === current)
77344
77861
  break;
77345
- suffix.unshift(path87.basename(current));
77862
+ suffix.unshift(path88.basename(current));
77346
77863
  current = parent;
77347
77864
  }
77348
- const canonicalBase = fs79.realpathSync(current);
77349
- return path87.join(canonicalBase, ...suffix);
77865
+ const canonicalBase = fs80.realpathSync(current);
77866
+ return path88.join(canonicalBase, ...suffix);
77350
77867
  }
77351
77868
  function validateRoots(spec) {
77352
- const workspace = path87.resolve(spec.workspace_root);
77353
- if (!fs79.existsSync(workspace) || fs79.lstatSync(workspace).isSymbolicLink() || !fs79.lstatSync(workspace).isDirectory()) {
77869
+ const workspace = path88.resolve(spec.workspace_root);
77870
+ if (!fs80.existsSync(workspace) || fs80.lstatSync(workspace).isSymbolicLink() || !fs80.lstatSync(workspace).isDirectory()) {
77354
77871
  throw new BenchmarkPhaseError("invalid_workspace_root", "workspace_root must be an existing real directory");
77355
77872
  }
77356
77873
  const canonicalWorkspace = canonicalFuturePath(workspace);
77357
- const staging = path87.resolve(spec.staging_root);
77358
- const expectedStaging = path87.join(workspace, ".brainbase", "benchmark", spec.attempt_id, "incoming");
77874
+ const staging = path88.resolve(spec.staging_root);
77875
+ const expectedStaging = path88.join(workspace, ".brainbase", "benchmark", spec.attempt_id, "incoming");
77359
77876
  if (staging !== expectedStaging) {
77360
77877
  throw new BenchmarkPhaseError("invalid_staging_root", `staging_root must be ${expectedStaging}`);
77361
77878
  }
77362
- if (!fs79.existsSync(staging) || fs79.lstatSync(staging).isSymbolicLink() || !fs79.lstatSync(staging).isDirectory() || fs79.realpathSync(staging) !== path87.join(canonicalWorkspace, ".brainbase", "benchmark", spec.attempt_id, "incoming")) {
77879
+ if (!fs80.existsSync(staging) || fs80.lstatSync(staging).isSymbolicLink() || !fs80.lstatSync(staging).isDirectory() || fs80.realpathSync(staging) !== path88.join(canonicalWorkspace, ".brainbase", "benchmark", spec.attempt_id, "incoming")) {
77363
77880
  throw new BenchmarkPhaseError("invalid_staging_root", "staging_root must be a real directory under workspace_root");
77364
77881
  }
77365
77882
  const canonicalLogs = validateExternalRoot("logs_root", spec.logs_root, canonicalWorkspace);
@@ -77371,11 +77888,11 @@ function validateRoots(spec) {
77371
77888
  }
77372
77889
  }
77373
77890
  function validateExternalRoot(label, input, canonicalWorkspace) {
77374
- const candidate = path87.resolve(input);
77375
- if (candidate === path87.parse(candidate).root) {
77891
+ const candidate = path88.resolve(input);
77892
+ if (candidate === path88.parse(candidate).root) {
77376
77893
  throw new BenchmarkPhaseError(`invalid_${label}`, `${label} cannot be a filesystem root`);
77377
77894
  }
77378
- if (fs79.existsSync(candidate) && fs79.lstatSync(candidate).isSymbolicLink()) {
77895
+ if (fs80.existsSync(candidate) && fs80.lstatSync(candidate).isSymbolicLink()) {
77379
77896
  throw new BenchmarkPhaseError(`invalid_${label}`, `${label} cannot be a symlink`);
77380
77897
  }
77381
77898
  const canonicalCandidate = canonicalFuturePath(candidate);
@@ -77402,12 +77919,12 @@ function assertNoSymlinkTraversal(root, relative) {
77402
77919
  const rel = normalizedRootRelative(relative);
77403
77920
  if (rel === ".")
77404
77921
  return;
77405
- let current = path87.resolve(root);
77922
+ let current = path88.resolve(root);
77406
77923
  for (const segment of rel.split("/").slice(0, -1)) {
77407
- current = path87.join(current, segment);
77408
- if (!fs79.existsSync(current))
77924
+ current = path88.join(current, segment);
77925
+ if (!fs80.existsSync(current))
77409
77926
  continue;
77410
- if (fs79.lstatSync(current).isSymbolicLink()) {
77927
+ if (fs80.lstatSync(current).isSymbolicLink()) {
77411
77928
  throw new BenchmarkPhaseError("unsafe_path", `path traverses symlink: ${relative}`);
77412
77929
  }
77413
77930
  }
@@ -77417,9 +77934,9 @@ function assertOpenedFileWithinRoot(root, filePath, openedStat, label) {
77417
77934
  let canonicalFile;
77418
77935
  let currentStat;
77419
77936
  try {
77420
- canonicalRoot = fs79.realpathSync(root);
77421
- canonicalFile = fs79.realpathSync(filePath);
77422
- currentStat = fs79.statSync(filePath);
77937
+ canonicalRoot = fs80.realpathSync(root);
77938
+ canonicalFile = fs80.realpathSync(filePath);
77939
+ currentStat = fs80.statSync(filePath);
77423
77940
  } catch {
77424
77941
  throw new BenchmarkPhaseError("unsafe_path", `${label} changed while it was opened`);
77425
77942
  }
@@ -77428,10 +77945,10 @@ function assertOpenedFileWithinRoot(root, filePath, openedStat, label) {
77428
77945
  }
77429
77946
  }
77430
77947
  function openRegularFileNoFollow(filePath, label, root) {
77431
- const noFollow = typeof fs79.constants.O_NOFOLLOW === "number" ? fs79.constants.O_NOFOLLOW : 0;
77948
+ const noFollow = typeof fs80.constants.O_NOFOLLOW === "number" ? fs80.constants.O_NOFOLLOW : 0;
77432
77949
  let fd;
77433
77950
  try {
77434
- fd = fs79.openSync(filePath, fs79.constants.O_RDONLY | noFollow);
77951
+ fd = fs80.openSync(filePath, fs80.constants.O_RDONLY | noFollow);
77435
77952
  } catch (error2) {
77436
77953
  const code = error2.code;
77437
77954
  if (code === "ELOOP") {
@@ -77439,16 +77956,16 @@ function openRegularFileNoFollow(filePath, label, root) {
77439
77956
  }
77440
77957
  throw error2;
77441
77958
  }
77442
- const stat = fs79.fstatSync(fd);
77959
+ const stat = fs80.fstatSync(fd);
77443
77960
  if (!stat.isFile()) {
77444
- fs79.closeSync(fd);
77961
+ fs80.closeSync(fd);
77445
77962
  throw new BenchmarkPhaseError("invalid_input", `${label} must be a regular file`);
77446
77963
  }
77447
77964
  if (root) {
77448
77965
  try {
77449
77966
  assertOpenedFileWithinRoot(root, filePath, stat, label);
77450
77967
  } catch (error2) {
77451
- fs79.closeSync(fd);
77968
+ fs80.closeSync(fd);
77452
77969
  throw error2;
77453
77970
  }
77454
77971
  }
@@ -77456,7 +77973,7 @@ function openRegularFileNoFollow(filePath, label, root) {
77456
77973
  }
77457
77974
  async function sha256OfDescriptor(fd) {
77458
77975
  const hash = crypto6.createHash("sha256");
77459
- const stream = fs79.createReadStream("", {
77976
+ const stream = fs80.createReadStream("", {
77460
77977
  fd,
77461
77978
  autoClose: false,
77462
77979
  start: 0
@@ -77467,19 +77984,19 @@ async function sha256OfDescriptor(fd) {
77467
77984
  return hash.digest("hex");
77468
77985
  }
77469
77986
  function readDescriptor(fd) {
77470
- return fs79.readFileSync(fd);
77987
+ return fs80.readFileSync(fd);
77471
77988
  }
77472
77989
  function assertWritableDestination(root, relative) {
77473
77990
  const rel = normalizedRootRelative(relative);
77474
77991
  if (rel === ".")
77475
77992
  return;
77476
- let current = path87.resolve(root);
77993
+ let current = path88.resolve(root);
77477
77994
  const segments = rel.split("/");
77478
77995
  for (const segment of segments.slice(0, -1)) {
77479
- current = path87.join(current, segment);
77480
- if (!fs79.existsSync(current))
77996
+ current = path88.join(current, segment);
77997
+ if (!fs80.existsSync(current))
77481
77998
  continue;
77482
- const stat = fs79.lstatSync(current);
77999
+ const stat = fs80.lstatSync(current);
77483
78000
  if (stat.isSymbolicLink()) {
77484
78001
  throw new BenchmarkPhaseError("unsafe_path", `path traverses symlink: ${relative}`);
77485
78002
  }
@@ -77487,8 +78004,8 @@ function assertWritableDestination(root, relative) {
77487
78004
  throw new BenchmarkPhaseError("destination_conflict", `destination parent is not a directory: ${relative}`);
77488
78005
  }
77489
78006
  }
77490
- const destination = path87.resolve(root, rel);
77491
- if (fs79.existsSync(destination) && fs79.lstatSync(destination).isDirectory()) {
78007
+ const destination = path88.resolve(root, rel);
78008
+ if (fs80.existsSync(destination) && fs80.lstatSync(destination).isDirectory()) {
77492
78009
  throw new BenchmarkPhaseError("destination_conflict", `file destination is an existing directory: ${relative}`);
77493
78010
  }
77494
78011
  }
@@ -77513,14 +78030,14 @@ function validateDestinationGraph(paths) {
77513
78030
  }
77514
78031
  function sourcePath(stagingRoot, relative) {
77515
78032
  const rel = safeRelPath(relative);
77516
- const source = path87.resolve(stagingRoot, rel);
78033
+ const source = path88.resolve(stagingRoot, rel);
77517
78034
  if (!isWithin(stagingRoot, source)) {
77518
78035
  throw new BenchmarkPhaseError("unsafe_path", `source escapes staging root: ${relative}`);
77519
78036
  }
77520
78037
  assertNoSymlinkTraversal(stagingRoot, rel);
77521
78038
  let stat;
77522
78039
  try {
77523
- stat = fs79.lstatSync(source);
78040
+ stat = fs80.lstatSync(source);
77524
78041
  } catch {
77525
78042
  throw new BenchmarkPhaseError("missing_input", `staged input does not exist: ${relative}`);
77526
78043
  }
@@ -77548,13 +78065,13 @@ async function verifyRecordsUnchanged(records, spec) {
77548
78065
  }
77549
78066
  const relative = safeRelPath(record3.path);
77550
78067
  assertNoSymlinkTraversal(root, relative);
77551
- const candidate = path87.resolve(root, relative);
77552
- if (!isWithin(root, candidate) || !fs79.existsSync(candidate)) {
78068
+ const candidate = path88.resolve(root, relative);
78069
+ if (!isWithin(root, candidate) || !fs80.existsSync(candidate)) {
77553
78070
  throw new BenchmarkPhaseError("evidence_tampered", `evidence was removed during evaluation: ${record3.root}:${record3.path}`);
77554
78071
  }
77555
- const stat = fs79.lstatSync(candidate);
78072
+ const stat = fs80.lstatSync(candidate);
77556
78073
  if (record3.kind === "symlink") {
77557
- const target = stat.isSymbolicLink() ? fs79.readlinkSync(candidate) : null;
78074
+ const target = stat.isSymbolicLink() ? fs80.readlinkSync(candidate) : null;
77558
78075
  if (target === null || Buffer.byteLength(target) !== record3.size || sha256(target) !== record3.sha256) {
77559
78076
  throw new BenchmarkPhaseError("evidence_tampered", `evidence changed during evaluation: ${record3.root}:${record3.path}`);
77560
78077
  }
@@ -77569,7 +78086,7 @@ async function verifyRecordsUnchanged(records, spec) {
77569
78086
  throw new BenchmarkPhaseError("evidence_tampered", `evidence changed during evaluation: ${record3.root}:${record3.path}`);
77570
78087
  }
77571
78088
  } finally {
77572
- fs79.closeSync(opened.fd);
78089
+ fs80.closeSync(opened.fd);
77573
78090
  }
77574
78091
  }
77575
78092
  }
@@ -77585,35 +78102,35 @@ async function verifyInput(stagingRoot, material) {
77585
78102
  throw new BenchmarkPhaseError("digest_mismatch", `checksum mismatch for ${material.source}`, { expected: material.sha256, actual });
77586
78103
  }
77587
78104
  } finally {
77588
- fs79.closeSync(opened.fd);
78105
+ fs80.closeSync(opened.fd);
77589
78106
  }
77590
78107
  return source;
77591
78108
  }
77592
78109
  async function atomicCopy(source, destination, mode, sourceRoot) {
77593
- fs79.mkdirSync(path87.dirname(destination), { recursive: true });
78110
+ fs80.mkdirSync(path88.dirname(destination), { recursive: true });
77594
78111
  const temporary = `${destination}.${process.pid}.${crypto6.randomBytes(6).toString("hex")}.tmp`;
77595
78112
  const opened = openRegularFileNoFollow(source, `copy source ${source}`, sourceRoot);
77596
78113
  try {
77597
- await pipeline2(fs79.createReadStream("", {
78114
+ await pipeline2(fs80.createReadStream("", {
77598
78115
  fd: opened.fd,
77599
78116
  autoClose: false,
77600
78117
  start: 0
77601
- }), fs79.createWriteStream(temporary, {
78118
+ }), fs80.createWriteStream(temporary, {
77602
78119
  flags: "wx",
77603
78120
  mode: 384
77604
78121
  }));
77605
- fs79.chmodSync(temporary, mode ?? opened.stat.mode & 511);
77606
- fs79.renameSync(temporary, destination);
78122
+ fs80.chmodSync(temporary, mode ?? opened.stat.mode & 511);
78123
+ fs80.renameSync(temporary, destination);
77607
78124
  } finally {
77608
- fs79.closeSync(opened.fd);
77609
- fs79.rmSync(temporary, { force: true });
78125
+ fs80.closeSync(opened.fd);
78126
+ fs80.rmSync(temporary, { force: true });
77610
78127
  }
77611
78128
  }
77612
78129
  async function recordFile(root, filePath, rootName, kind = "file") {
77613
- const relative = path87.relative(root, filePath).replace(/\\/g, "/");
78130
+ const relative = path88.relative(root, filePath).replace(/\\/g, "/");
77614
78131
  if (kind === "symlink") {
77615
- const stat = fs79.lstatSync(filePath);
77616
- const target = fs79.readlinkSync(filePath);
78132
+ const stat = fs80.lstatSync(filePath);
78133
+ const target = fs80.readlinkSync(filePath);
77617
78134
  return {
77618
78135
  root: rootName,
77619
78136
  path: relative,
@@ -77633,7 +78150,7 @@ async function recordFile(root, filePath, rootName, kind = "file") {
77633
78150
  mode: opened.stat.mode & 511
77634
78151
  };
77635
78152
  } finally {
77636
- fs79.closeSync(opened.fd);
78153
+ fs80.closeSync(opened.fd);
77637
78154
  }
77638
78155
  }
77639
78156
  async function copyMaterial(material, sourceRoot, destinationRoot, destinationRootName, protectWorkspace) {
@@ -77647,7 +78164,7 @@ async function copyMaterial(material, sourceRoot, destinationRoot, destinationRo
77647
78164
  throw new BenchmarkPhaseError("unsafe_path", "file destination cannot be a directory root");
77648
78165
  }
77649
78166
  assertWritableDestination(destinationRoot, destinationRel);
77650
- const destination = path87.resolve(destinationRoot, destinationRel);
78167
+ const destination = path88.resolve(destinationRoot, destinationRel);
77651
78168
  await atomicCopy(source, destination, material.mode, sourceRoot);
77652
78169
  const record3 = await recordFile(destinationRoot, destination, destinationRootName);
77653
78170
  if (record3.sha256 !== material.sha256 || record3.size !== material.size_bytes) {
@@ -77655,15 +78172,15 @@ async function copyMaterial(material, sourceRoot, destinationRoot, destinationRo
77655
78172
  }
77656
78173
  return [record3];
77657
78174
  }
77658
- const temporary = fs79.mkdtempSync(path87.join(os16.tmpdir(), "brainbase-benchmark-"));
78175
+ const temporary = fs80.mkdtempSync(path88.join(os16.tmpdir(), "brainbase-benchmark-"));
77659
78176
  try {
77660
- const verifiedArchive = path87.join(temporary, "material.tar.gz");
78177
+ const verifiedArchive = path88.join(temporary, "material.tar.gz");
77661
78178
  await atomicCopy(source, verifiedArchive, 384, sourceRoot);
77662
78179
  const archiveRecord = await recordFile(temporary, verifiedArchive, "staging");
77663
78180
  if (archiveRecord.sha256 !== material.sha256 || archiveRecord.size !== material.size_bytes) {
77664
78181
  throw new BenchmarkPhaseError("evidence_tampered", `material changed while it was copied: ${material.source}`);
77665
78182
  }
77666
- const extractedRoot = path87.join(temporary, "extracted");
78183
+ const extractedRoot = path88.join(temporary, "extracted");
77667
78184
  const extracted = await extract({
77668
78185
  tarFile: verifiedArchive,
77669
78186
  outDir: extractedRoot,
@@ -77672,23 +78189,23 @@ async function copyMaterial(material, sourceRoot, destinationRoot, destinationRo
77672
78189
  });
77673
78190
  const outputs = [];
77674
78191
  for (const extractedRel of extracted.sort()) {
77675
- const sourceFile = path87.resolve(extractedRoot, safeRelPath(extractedRel));
77676
- const stat = fs79.lstatSync(sourceFile);
78192
+ const sourceFile = path88.resolve(extractedRoot, safeRelPath(extractedRel));
78193
+ const stat = fs80.lstatSync(sourceFile);
77677
78194
  if (!stat.isFile())
77678
78195
  continue;
77679
- const combined = destinationRel === "." ? safeRelPath(extractedRel) : safeRelPath(path87.posix.join(destinationRel, extractedRel));
78196
+ const combined = destinationRel === "." ? safeRelPath(extractedRel) : safeRelPath(path88.posix.join(destinationRel, extractedRel));
77680
78197
  const checked = protectWorkspace ? workspaceRel(combined) : combined;
77681
78198
  if (!protectWorkspace && checked.toLowerCase() === ".brainbase-benchmark-owner.json") {
77682
78199
  throw new BenchmarkPhaseError("reserved_path", "benchmark input cannot replace the phase ownership marker");
77683
78200
  }
77684
78201
  assertWritableDestination(destinationRoot, checked);
77685
- const destination = path87.resolve(destinationRoot, checked);
78202
+ const destination = path88.resolve(destinationRoot, checked);
77686
78203
  await atomicCopy(sourceFile, destination, material.mode, extractedRoot);
77687
78204
  outputs.push(await recordFile(destinationRoot, destination, destinationRootName));
77688
78205
  }
77689
78206
  return outputs;
77690
78207
  } finally {
77691
- fs79.rmSync(temporary, { recursive: true, force: true });
78208
+ fs80.rmSync(temporary, { recursive: true, force: true });
77692
78209
  }
77693
78210
  }
77694
78211
  async function preflightMaterial(material, sourceRoot, destinationRoot, protectWorkspace) {
@@ -77704,15 +78221,15 @@ async function preflightMaterial(material, sourceRoot, destinationRoot, protectW
77704
78221
  assertWritableDestination(destinationRoot, destinationRel);
77705
78222
  return [destinationRel];
77706
78223
  }
77707
- const temporary = fs79.mkdtempSync(path87.join(os16.tmpdir(), "brainbase-benchmark-preflight-"));
78224
+ const temporary = fs80.mkdtempSync(path88.join(os16.tmpdir(), "brainbase-benchmark-preflight-"));
77708
78225
  try {
77709
- const verifiedArchive = path87.join(temporary, "material.tar.gz");
78226
+ const verifiedArchive = path88.join(temporary, "material.tar.gz");
77710
78227
  await atomicCopy(source, verifiedArchive, 384, sourceRoot);
77711
78228
  const archiveRecord = await recordFile(temporary, verifiedArchive, "staging");
77712
78229
  if (archiveRecord.sha256 !== material.sha256 || archiveRecord.size !== material.size_bytes) {
77713
78230
  throw new BenchmarkPhaseError("evidence_tampered", `material changed while it was copied: ${material.source}`);
77714
78231
  }
77715
- const extractedRoot = path87.join(temporary, "extracted");
78232
+ const extractedRoot = path88.join(temporary, "extracted");
77716
78233
  const extracted = await extract({
77717
78234
  tarFile: verifiedArchive,
77718
78235
  outDir: extractedRoot,
@@ -77721,7 +78238,7 @@ async function preflightMaterial(material, sourceRoot, destinationRoot, protectW
77721
78238
  });
77722
78239
  const planned = [];
77723
78240
  for (const extractedRel of extracted) {
77724
- const combined = destinationRel === "." ? safeRelPath(extractedRel) : safeRelPath(path87.posix.join(destinationRel, extractedRel));
78241
+ const combined = destinationRel === "." ? safeRelPath(extractedRel) : safeRelPath(path88.posix.join(destinationRel, extractedRel));
77725
78242
  const checked = protectWorkspace ? workspaceRel(combined) : combined;
77726
78243
  if (!protectWorkspace && checked.toLowerCase() === ".brainbase-benchmark-owner.json") {
77727
78244
  throw new BenchmarkPhaseError("reserved_path", "benchmark input cannot replace the phase ownership marker");
@@ -77731,34 +78248,34 @@ async function preflightMaterial(material, sourceRoot, destinationRoot, protectW
77731
78248
  }
77732
78249
  return planned;
77733
78250
  } finally {
77734
- fs79.rmSync(temporary, { recursive: true, force: true });
78251
+ fs80.rmSync(temporary, { recursive: true, force: true });
77735
78252
  }
77736
78253
  }
77737
78254
  function ownerMarker(root) {
77738
- return path87.join(root, ".brainbase-benchmark-owner.json");
78255
+ return path88.join(root, ".brainbase-benchmark-owner.json");
77739
78256
  }
77740
78257
  function verifyOwnedDirectory(root, role, spec) {
77741
- if (!fs79.existsSync(root) || fs79.lstatSync(root).isSymbolicLink())
78258
+ if (!fs80.existsSync(root) || fs80.lstatSync(root).isSymbolicLink())
77742
78259
  return false;
77743
78260
  try {
77744
- const marker = JSON.parse(fs79.readFileSync(ownerMarker(root), "utf8"));
78261
+ const marker = JSON.parse(fs80.readFileSync(ownerMarker(root), "utf8"));
77745
78262
  return marker.attempt_id === spec.attempt_id && marker.phase === spec.phase && marker.phase_id === spec.phase_id && marker.role === role;
77746
78263
  } catch {
77747
78264
  return false;
77748
78265
  }
77749
78266
  }
77750
78267
  function prepareOwnedDirectory(root, role, spec) {
77751
- if (fs79.existsSync(root)) {
78268
+ if (fs80.existsSync(root)) {
77752
78269
  if (!verifyOwnedDirectory(root, role, spec)) {
77753
- const stat = fs79.lstatSync(root);
77754
- if (!stat.isDirectory() || fs79.readdirSync(root).length > 0) {
78270
+ const stat = fs80.lstatSync(root);
78271
+ if (!stat.isDirectory() || fs80.readdirSync(root).length > 0) {
77755
78272
  throw new BenchmarkPhaseError(`unowned_${role}_root`, `${role}_root exists without a matching attempt ownership marker`);
77756
78273
  }
77757
78274
  } else {
77758
- fs79.rmSync(root, { recursive: true, force: true });
78275
+ fs80.rmSync(root, { recursive: true, force: true });
77759
78276
  }
77760
78277
  }
77761
- fs79.mkdirSync(root, { recursive: true, mode: 448 });
78278
+ fs80.mkdirSync(root, { recursive: true, mode: 448 });
77762
78279
  writeJsonAtomic(ownerMarker(root), {
77763
78280
  schema_version: SCHEMA_VERSION,
77764
78281
  attempt_id: spec.attempt_id,
@@ -77847,10 +78364,10 @@ function terminate(child) {
77847
78364
  async function runCommand(command, root, spec, context, additions = {}) {
77848
78365
  const cwdRel = normalizedRootRelative(command.cwd);
77849
78366
  assertNoSymlinkTraversal(root, cwdRel);
77850
- const cwd2 = path87.resolve(root, cwdRel);
78367
+ const cwd2 = path88.resolve(root, cwdRel);
77851
78368
  let cwdStat;
77852
78369
  try {
77853
- cwdStat = fs79.lstatSync(cwd2);
78370
+ cwdStat = fs80.lstatSync(cwd2);
77854
78371
  } catch {
77855
78372
  throw new BenchmarkPhaseError("invalid_command_cwd", `command cwd is invalid: ${command.cwd}`);
77856
78373
  }
@@ -77921,28 +78438,28 @@ async function runCommand(command, root, spec, context, additions = {}) {
77921
78438
  });
77922
78439
  }
77923
78440
  async function writeLog(root, name, data, spec) {
77924
- const destination = path87.join(root, name);
77925
- fs79.mkdirSync(path87.dirname(destination), { recursive: true });
78441
+ const destination = path88.join(root, name);
78442
+ fs80.mkdirSync(path88.dirname(destination), { recursive: true });
77926
78443
  const temporary = `${destination}.${process.pid}.${crypto6.randomBytes(6).toString("hex")}.tmp`;
77927
78444
  try {
77928
- fs79.writeFileSync(temporary, redactCommandOutput(data, spec), {
78445
+ fs80.writeFileSync(temporary, redactCommandOutput(data, spec), {
77929
78446
  flag: "wx",
77930
78447
  mode: 384
77931
78448
  });
77932
- fs79.renameSync(temporary, destination);
78449
+ fs80.renameSync(temporary, destination);
77933
78450
  } finally {
77934
- fs79.rmSync(temporary, { force: true });
78451
+ fs80.rmSync(temporary, { force: true });
77935
78452
  }
77936
78453
  return await recordFile(root, destination, "logs");
77937
78454
  }
77938
78455
  function writeBufferAtomic(destination, data) {
77939
- fs79.mkdirSync(path87.dirname(destination), { recursive: true });
78456
+ fs80.mkdirSync(path88.dirname(destination), { recursive: true });
77940
78457
  const temporary = `${destination}.${process.pid}.${crypto6.randomBytes(6).toString("hex")}.tmp`;
77941
78458
  try {
77942
- fs79.writeFileSync(temporary, data, { flag: "wx", mode: 384 });
77943
- fs79.renameSync(temporary, destination);
78459
+ fs80.writeFileSync(temporary, data, { flag: "wx", mode: 384 });
78460
+ fs80.renameSync(temporary, destination);
77944
78461
  } finally {
77945
- fs79.rmSync(temporary, { force: true });
78462
+ fs80.rmSync(temporary, { force: true });
77946
78463
  }
77947
78464
  }
77948
78465
  function assertBudget(context) {
@@ -77951,7 +78468,7 @@ function assertBudget(context) {
77951
78468
  }
77952
78469
  }
77953
78470
  async function executeHydrate(spec, context) {
77954
- fs79.mkdirSync(spec.workspace_root, { recursive: true });
78471
+ fs80.mkdirSync(spec.workspace_root, { recursive: true });
77955
78472
  prepareOwnedDirectory(spec.logs_root, "logs", spec);
77956
78473
  context.logsOwned = true;
77957
78474
  const outputs = [];
@@ -78030,11 +78547,11 @@ async function executeHydrate(spec, context) {
78030
78547
  finalOutputs.push(output);
78031
78548
  continue;
78032
78549
  }
78033
- const candidate = path87.resolve(spec.workspace_root, safeRelPath(output.path));
78550
+ const candidate = path88.resolve(spec.workspace_root, safeRelPath(output.path));
78034
78551
  assertNoSymlinkTraversal(spec.workspace_root, output.path);
78035
- if (!fs79.existsSync(candidate))
78552
+ if (!fs80.existsSync(candidate))
78036
78553
  continue;
78037
- const stat = fs79.lstatSync(candidate);
78554
+ const stat = fs80.lstatSync(candidate);
78038
78555
  if (!stat.isFile() && !stat.isSymbolicLink())
78039
78556
  continue;
78040
78557
  finalOutputs.push(await recordFile(spec.workspace_root, candidate, "workspace", stat.isSymbolicLink() ? "symlink" : "file"));
@@ -78061,27 +78578,27 @@ async function readEvidence(stagingRoot, evidence) {
78061
78578
  buffer,
78062
78579
  record: {
78063
78580
  root: "staging",
78064
- path: path87.relative(stagingRoot, filePath).replace(/\\/g, "/"),
78581
+ path: path88.relative(stagingRoot, filePath).replace(/\\/g, "/"),
78065
78582
  sha256: evidence.sha256,
78066
78583
  size: opened.stat.size,
78067
78584
  mode: opened.stat.mode & 511
78068
78585
  }
78069
78586
  };
78070
78587
  } finally {
78071
- fs79.closeSync(opened.fd);
78588
+ fs80.closeSync(opened.fd);
78072
78589
  }
78073
78590
  }
78074
78591
  async function workspaceManifest(spec, context) {
78075
78592
  const records = [];
78076
78593
  let totalBytes = 0;
78077
- const stack = [path87.resolve(spec.workspace_root)];
78594
+ const stack = [path88.resolve(spec.workspace_root)];
78078
78595
  while (stack.length > 0) {
78079
78596
  const directory = stack.pop();
78080
- const entries = fs79.readdirSync(directory, { withFileTypes: true }).sort((a3, b4) => a3.name.localeCompare(b4.name));
78597
+ const entries = fs80.readdirSync(directory, { withFileTypes: true }).sort((a3, b4) => a3.name.localeCompare(b4.name));
78081
78598
  for (const entry of entries) {
78082
78599
  assertBudget(context);
78083
- const full = path87.join(directory, entry.name);
78084
- const relative = path87.relative(spec.workspace_root, full).replace(/\\/g, "/");
78600
+ const full = path88.join(directory, entry.name);
78601
+ const relative = path88.relative(spec.workspace_root, full).replace(/\\/g, "/");
78085
78602
  if (relative === ".brainbase" || relative.startsWith(".brainbase/"))
78086
78603
  continue;
78087
78604
  if (relative === ".git" || relative.startsWith(".git/"))
@@ -78183,10 +78700,10 @@ async function evaluateOne(evaluator, spec, finalOutput, trajectory, frozenEvide
78183
78700
  if (evaluator.type === "workspace_assertion") {
78184
78701
  const relative = workspaceRel(evaluator.path);
78185
78702
  assertNoSymlinkTraversal(spec.workspace_root, relative);
78186
- const candidate = path87.resolve(spec.workspace_root, relative);
78703
+ const candidate = path88.resolve(spec.workspace_root, relative);
78187
78704
  let stat = null;
78188
78705
  try {
78189
- stat = fs79.lstatSync(candidate);
78706
+ stat = fs80.lstatSync(candidate);
78190
78707
  } catch (error2) {
78191
78708
  const code = error2.code;
78192
78709
  if (code !== "ENOENT" && code !== "ENOTDIR")
@@ -78207,7 +78724,7 @@ async function evaluateOne(evaluator, spec, finalOutput, trajectory, frozenEvide
78207
78724
  try {
78208
78725
  verdict2 = await sha256OfDescriptor(opened.fd) === evaluator.assertion.expected;
78209
78726
  } finally {
78210
- fs79.closeSync(opened.fd);
78727
+ fs80.closeSync(opened.fd);
78211
78728
  }
78212
78729
  }
78213
78730
  }
@@ -78217,7 +78734,7 @@ async function evaluateOne(evaluator, spec, finalOutput, trajectory, frozenEvide
78217
78734
  try {
78218
78735
  verdict2 = readDescriptor(opened.fd).toString("utf8").includes(evaluator.assertion.expected);
78219
78736
  } finally {
78220
- fs79.closeSync(opened.fd);
78737
+ fs80.closeSync(opened.fd);
78221
78738
  }
78222
78739
  }
78223
78740
  }
@@ -78271,8 +78788,8 @@ async function executeEvaluate(spec, context) {
78271
78788
  context.logsOwned = true;
78272
78789
  validateRoots(spec);
78273
78790
  const outputs = [];
78274
- const finalOutputPath = path87.join(spec.logs_root, "candidate-evidence", "final-output");
78275
- const trajectoryPath = path87.join(spec.logs_root, "candidate-evidence", "trajectory.json");
78791
+ const finalOutputPath = path88.join(spec.logs_root, "candidate-evidence", "final-output");
78792
+ const trajectoryPath = path88.join(spec.logs_root, "candidate-evidence", "trajectory.json");
78276
78793
  writeBufferAtomic(finalOutputPath, finalOutput.buffer);
78277
78794
  writeBufferAtomic(trajectoryPath, trajectoryEvidence.buffer);
78278
78795
  const frozenEvidenceRecords = [
@@ -78283,7 +78800,7 @@ async function executeEvaluate(spec, context) {
78283
78800
  context.outputs.push(...frozenEvidenceRecords);
78284
78801
  assertBudget(context);
78285
78802
  const manifest = await workspaceManifest(spec, context);
78286
- const manifestPath2 = path87.join(spec.logs_root, "candidate-workspace-manifest.json");
78803
+ const manifestPath2 = path88.join(spec.logs_root, "candidate-workspace-manifest.json");
78287
78804
  writeJsonAtomic(manifestPath2, {
78288
78805
  schema_version: SCHEMA_VERSION,
78289
78806
  attempt_id: spec.attempt_id,
@@ -78296,12 +78813,12 @@ async function executeEvaluate(spec, context) {
78296
78813
  for (const artifactRelInput of spec.candidate_artifacts) {
78297
78814
  const artifactRel = workspaceRel(artifactRelInput);
78298
78815
  assertNoSymlinkTraversal(spec.workspace_root, artifactRel);
78299
- const source = path87.resolve(spec.workspace_root, artifactRel);
78816
+ const source = path88.resolve(spec.workspace_root, artifactRel);
78300
78817
  const frozenArtifact = manifest.find((entry) => entry.path === artifactRel && entry.kind !== "symlink");
78301
- if (!frozenArtifact || !fs79.existsSync(source) || !fs79.lstatSync(source).isFile()) {
78818
+ if (!frozenArtifact || !fs80.existsSync(source) || !fs80.lstatSync(source).isFile()) {
78302
78819
  throw new BenchmarkPhaseError("missing_artifact", `candidate artifact is missing: ${artifactRel}`);
78303
78820
  }
78304
- const destination = path87.resolve(spec.logs_root, "candidate-artifacts", artifactRel);
78821
+ const destination = path88.resolve(spec.logs_root, "candidate-artifacts", artifactRel);
78305
78822
  await atomicCopy(source, destination, undefined, spec.workspace_root);
78306
78823
  const artifact = await recordFile(spec.logs_root, destination, "logs");
78307
78824
  if (artifact.sha256 !== frozenArtifact.sha256 || artifact.size !== frozenArtifact.size || artifact.mode !== frozenArtifact.mode) {
@@ -78312,13 +78829,13 @@ async function executeEvaluate(spec, context) {
78312
78829
  }
78313
78830
  if (spec.capture_workspace_archive) {
78314
78831
  const regularFiles = manifest.filter((entry) => entry.kind !== "symlink").map((entry) => entry.path);
78315
- const archive = path87.join(spec.logs_root, "candidate-workspace.tar.gz");
78832
+ const archive = path88.join(spec.logs_root, "candidate-workspace.tar.gz");
78316
78833
  const temporary = `${archive}.${process.pid}.${crypto6.randomBytes(6).toString("hex")}.tmp`;
78317
78834
  try {
78318
78835
  await pack({ rootDir: spec.workspace_root, outFile: temporary, files: regularFiles });
78319
- fs79.renameSync(temporary, archive);
78836
+ fs80.renameSync(temporary, archive);
78320
78837
  } finally {
78321
- fs79.rmSync(temporary, { force: true });
78838
+ fs80.rmSync(temporary, { force: true });
78322
78839
  }
78323
78840
  const archiveRecord = await recordFile(spec.logs_root, archive, "logs");
78324
78841
  outputs.push(archiveRecord);
@@ -78417,22 +78934,22 @@ function rawIdentity(value) {
78417
78934
  };
78418
78935
  }
78419
78936
  function readSpecBytes(specPathInput) {
78420
- const specPath = path87.resolve(specPathInput);
78421
- const noFollow = typeof fs79.constants.O_NOFOLLOW === "number" ? fs79.constants.O_NOFOLLOW : 0;
78937
+ const specPath = path88.resolve(specPathInput);
78938
+ const noFollow = typeof fs80.constants.O_NOFOLLOW === "number" ? fs80.constants.O_NOFOLLOW : 0;
78422
78939
  let fd;
78423
78940
  try {
78424
- fd = fs79.openSync(specPath, fs79.constants.O_RDONLY | noFollow);
78941
+ fd = fs80.openSync(specPath, fs80.constants.O_RDONLY | noFollow);
78425
78942
  } catch {
78426
78943
  throw new BenchmarkPhaseError("spec_read_failed", "spec file could not be read");
78427
78944
  }
78428
78945
  try {
78429
- const stat = fs79.fstatSync(fd);
78946
+ const stat = fs80.fstatSync(fd);
78430
78947
  if (!stat.isFile() || stat.size > MAX_SPEC_BYTES) {
78431
78948
  throw new BenchmarkPhaseError("invalid_spec_file", "spec must be a regular JSON file no larger than 20 MiB");
78432
78949
  }
78433
78950
  return readDescriptor(fd);
78434
78951
  } finally {
78435
- fs79.closeSync(fd);
78952
+ fs80.closeSync(fd);
78436
78953
  }
78437
78954
  }
78438
78955
  function validateBenchmarkInvocationBytes(bytes, resultPathInput, expectedPhase) {
@@ -78448,8 +78965,8 @@ function validateBenchmarkInvocationBytes(bytes, resultPathInput, expectedPhase)
78448
78965
  throw new BenchmarkPhaseError("phase_mismatch", `the ${expectedPhase} command cannot execute a ${spec.phase} spec`);
78449
78966
  }
78450
78967
  validateRoots(spec);
78451
- const resultPath = path87.resolve(resultPathInput);
78452
- const expectedResultPath = path87.join(path87.resolve(spec.logs_root), "result.json");
78968
+ const resultPath = path88.resolve(resultPathInput);
78969
+ const expectedResultPath = path88.join(path88.resolve(spec.logs_root), "result.json");
78453
78970
  if (resultPath !== expectedResultPath) {
78454
78971
  throw new BenchmarkPhaseError("invalid_result_path", `result path must be ${expectedResultPath}`);
78455
78972
  }
@@ -78476,9 +78993,9 @@ function prepareBenchmarkPhaseInvocation(specPathInput, resultPathInput, expecte
78476
78993
  spec_digest: digest,
78477
78994
  timeout_ms: spec.budget.timeout_ms
78478
78995
  };
78479
- if (fs79.existsSync(resultPath)) {
78996
+ if (fs80.existsSync(resultPath)) {
78480
78997
  try {
78481
- const cached2 = JSON.parse(fs79.readFileSync(resultPath, "utf8"));
78998
+ const cached2 = JSON.parse(fs80.readFileSync(resultPath, "utf8"));
78482
78999
  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") {
78483
79000
  return {
78484
79001
  ok: true,
@@ -78546,7 +79063,7 @@ function writeBenchmarkPhaseTimeoutResult(specBytes, resultPathInput, expectedPh
78546
79063
  async function runBenchmarkPhase(specPathInput, resultPathInput, expectedPhase, immutableSpecBytes) {
78547
79064
  const startedAt = nowIso();
78548
79065
  const started = Date.now();
78549
- const resultPath = path87.resolve(resultPathInput);
79066
+ const resultPath = path88.resolve(resultPathInput);
78550
79067
  let raw = undefined;
78551
79068
  let digest = null;
78552
79069
  let identity2 = rawIdentity(raw);
@@ -78580,14 +79097,14 @@ async function runBenchmarkPhase(specPathInput, resultPathInput, expectedPhase,
78580
79097
  throw new BenchmarkPhaseError("phase_mismatch", `the ${expectedPhase} command cannot execute a ${spec.phase} spec`);
78581
79098
  }
78582
79099
  validateRoots(spec);
78583
- const expectedResultPath = path87.join(path87.resolve(spec.logs_root), "result.json");
79100
+ const expectedResultPath = path88.join(path88.resolve(spec.logs_root), "result.json");
78584
79101
  if (resultPath !== expectedResultPath) {
78585
79102
  throw new BenchmarkPhaseError("invalid_result_path", `result path must be ${expectedResultPath}`);
78586
79103
  }
78587
79104
  resultPathValidated = true;
78588
- if (fs79.existsSync(resultPath)) {
79105
+ if (fs80.existsSync(resultPath)) {
78589
79106
  try {
78590
- const cached2 = JSON.parse(fs79.readFileSync(resultPath, "utf8"));
79107
+ const cached2 = JSON.parse(fs80.readFileSync(resultPath, "utf8"));
78591
79108
  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") {
78592
79109
  return { exitCode: 0, result: cached2 };
78593
79110
  }
@@ -78783,14 +79300,14 @@ function terminatePhase(child) {
78783
79300
  }
78784
79301
  }
78785
79302
  function createAnonymousSpecFd(bytes) {
78786
- const temporary = path88.join(os17.tmpdir(), `brainbase-benchmark-spec-${process.pid}-${crypto7.randomBytes(12).toString("hex")}`);
78787
- fs80.writeFileSync(temporary, bytes, { flag: "wx", mode: 384 });
79303
+ const temporary = path89.join(os17.tmpdir(), `brainbase-benchmark-spec-${process.pid}-${crypto7.randomBytes(12).toString("hex")}`);
79304
+ fs81.writeFileSync(temporary, bytes, { flag: "wx", mode: 384 });
78788
79305
  try {
78789
- const fd = fs80.openSync(temporary, "r");
78790
- fs80.unlinkSync(temporary);
79306
+ const fd = fs81.openSync(temporary, "r");
79307
+ fs81.unlinkSync(temporary);
78791
79308
  return fd;
78792
79309
  } catch (error2) {
78793
- fs80.rmSync(temporary, { force: true });
79310
+ fs81.rmSync(temporary, { force: true });
78794
79311
  throw error2;
78795
79312
  }
78796
79313
  }
@@ -78859,13 +79376,13 @@ async function runSupervisedPhase(phase, parsed, write) {
78859
79376
  detached: process.platform !== "win32"
78860
79377
  });
78861
79378
  } catch {
78862
- fs80.closeSync(specFd);
79379
+ fs81.closeSync(specFd);
78863
79380
  const failure = phaseFailure(invocation, startedAt, started, "phase_supervisor_failed", "benchmark phase child process could not be started");
78864
79381
  write(`${JSON.stringify(failure)}
78865
79382
  `);
78866
79383
  return 1;
78867
79384
  }
78868
- fs80.closeSync(specFd);
79385
+ fs81.closeSync(specFd);
78869
79386
  return await new Promise((resolve) => {
78870
79387
  const stdout = [];
78871
79388
  let settled = false;
@@ -78947,7 +79464,7 @@ async function runBenchmark(sub, args, write = (value) => process.stdout.write(v
78947
79464
  if (phase !== "hydrate" && phase !== "evaluate" || resultFlag !== "--result" || !resultPath || specFdFlag !== "--spec-fd" || !Number.isInteger(specFd) || specFd < 3 || tokenFlag !== "--token" || !token || token !== process.env.BRAINBASE_BENCHMARK_PHASE_CHILD_TOKEN) {
78948
79465
  throw new Error("Invalid internal benchmark phase invocation");
78949
79466
  }
78950
- const specBytes = fs80.readFileSync(specFd);
79467
+ const specBytes = fs81.readFileSync(specFd);
78951
79468
  const { exitCode, result: result2 } = await runBenchmarkPhase("", resultPath, phase, specBytes);
78952
79469
  write(`${JSON.stringify(result2)}
78953
79470
  `);
@@ -79092,7 +79609,8 @@ function help() {
79092
79609
  out.push(divider("CLI TOKENS"));
79093
79610
  out.push("");
79094
79611
  out.push(` ${import_picocolors49.default.cyan("token create")} ${import_picocolors49.default.dim("issue a long-lived CLI key for CI / scripts")}`);
79095
- out.push(` ${import_picocolors49.default.cyan("token list")} ${import_picocolors49.default.dim("show your active tokens")}`);
79612
+ out.push(` ${import_picocolors49.default.cyan("token list")} ${import_picocolors49.default.dim("show your tokens")}`);
79613
+ out.push(` ${import_picocolors49.default.cyan("token rename")} ${import_picocolors49.default.dim("<id>")} ${import_picocolors49.default.dim("relabel a token")}`);
79096
79614
  out.push(` ${import_picocolors49.default.cyan("token revoke")} ${import_picocolors49.default.dim("<id>")} ${import_picocolors49.default.dim("revoke a token")}`);
79097
79615
  out.push("");
79098
79616
  out.push(divider("MCP"));
@@ -79222,7 +79740,7 @@ async function main() {
79222
79740
  const rawCwd = process14.cwd();
79223
79741
  const cwd2 = (() => {
79224
79742
  try {
79225
- return fs81.realpathSync(rawCwd);
79743
+ return fs82.realpathSync(rawCwd);
79226
79744
  } catch {
79227
79745
  return rawCwd;
79228
79746
  }
@@ -79319,7 +79837,7 @@ async function main() {
79319
79837
  }
79320
79838
  case "token": {
79321
79839
  const sub = argv.shift();
79322
- await runToken(sub, argv, { yes });
79840
+ await runToken(sub, argv, { yes, name: nameFlag });
79323
79841
  break;
79324
79842
  }
79325
79843
  case "link": {