@brainbase-labs/cli 0.28.0 → 0.29.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 (3) hide show
  1. package/README.md +7 -0
  2. package/dist/index.js +2255 -530
  3. package/package.json +3 -2
package/dist/index.js CHANGED
@@ -35141,7 +35141,7 @@ var require_dist2 = __commonJS((exports, module) => {
35141
35141
  });
35142
35142
 
35143
35143
  // src/index.ts
35144
- var import_picocolors54 = __toESM(require_picocolors(), 1);
35144
+ var import_picocolors61 = __toESM(require_picocolors(), 1);
35145
35145
  import process14 from "node:process";
35146
35146
  import fs87 from "node:fs";
35147
35147
 
@@ -35949,6 +35949,16 @@ function me() {
35949
35949
  return new RegExp(r2, "g");
35950
35950
  }
35951
35951
 
35952
+ // src/ui/fail.ts
35953
+ function fail(message) {
35954
+ f2.error(message);
35955
+ process.exitCode = 1;
35956
+ }
35957
+ function failWarn(message) {
35958
+ f2.warn(message);
35959
+ process.exitCode = 1;
35960
+ }
35961
+
35952
35962
  // src/cli/pack.ts
35953
35963
  var import_picocolors7 = __toESM(require_picocolors(), 1);
35954
35964
 
@@ -36008,7 +36018,7 @@ function padStart(s, n) {
36008
36018
  // package.json
36009
36019
  var package_default = {
36010
36020
  name: "@brainbase-labs/cli",
36011
- version: "0.28.0",
36021
+ version: "0.29.1",
36012
36022
  description: "Pack, share, and install agent templates across harnesses (Claude Code, Codex, ...).",
36013
36023
  type: "module",
36014
36024
  bin: {
@@ -36024,7 +36034,7 @@ var package_default = {
36024
36034
  build: "bun run scripts/build.ts",
36025
36035
  test: "bun test",
36026
36036
  typecheck: "tsc --noEmit",
36027
- verify: "bun run typecheck && bun test && bun run build",
36037
+ verify: "bun run scripts/check-bun-version.ts && bun run typecheck && bun test && bun run build",
36028
36038
  prepublishOnly: "bun run verify"
36029
36039
  },
36030
36040
  repository: {
@@ -36051,6 +36061,7 @@ var package_default = {
36051
36061
  dependencies: {
36052
36062
  "@modelcontextprotocol/sdk": "^1.18.2",
36053
36063
  "@clack/prompts": "^0.7.0",
36064
+ eventsource: "^3.0.2",
36054
36065
  ink: "^5.1.0",
36055
36066
  picocolors: "^1.0.0",
36056
36067
  react: "^18.3.1",
@@ -36156,6 +36167,13 @@ function requireInteractive(flagHint) {
36156
36167
  function autoProceed(yes) {
36157
36168
  return Boolean(yes) || !isInteractive();
36158
36169
  }
36170
+ function autoProceedDestructive(yes, opts) {
36171
+ if (yes)
36172
+ return true;
36173
+ if (isInteractive())
36174
+ return false;
36175
+ throw new NonInteractiveError(`${opts.action} is destructive and needs confirmation, but there is no interactive terminal to ask. ` + (opts.flagHint ?? "Pass --yes to confirm."));
36176
+ }
36159
36177
 
36160
36178
  // src/ui/symbols.ts
36161
36179
  var import_picocolors5 = __toESM(require_picocolors(), 1);
@@ -40951,7 +40969,7 @@ function acquireLock(lockFile, timeoutMs) {
40951
40969
  function acquireAuthLock() {
40952
40970
  const release = acquireLock(AUTH_LOCK_FILE, AUTH_LOCK_TIMEOUT_MS);
40953
40971
  if (!release) {
40954
- throw new Error(`Timed out waiting to update CLI authentication; if no other brainbase process is running, remove ${AUTH_LOCK_FILE}`);
40972
+ throw new Error(`Timed out waiting to update CLI authentication.${lockHolderState(AUTH_LOCK_FILE)} Retry, or remove ${AUTH_LOCK_FILE}`);
40955
40973
  }
40956
40974
  return release;
40957
40975
  }
@@ -41003,10 +41021,44 @@ class AuthSessionChangedError extends Error {
41003
41021
  this.name = "AuthSessionChangedError";
41004
41022
  }
41005
41023
  }
41024
+ function heldFor(lockFile) {
41025
+ try {
41026
+ const ms = Date.now() - fs6.statSync(lockFile).mtimeMs;
41027
+ if (!Number.isFinite(ms) || ms < 0)
41028
+ return "";
41029
+ if (ms < 60000)
41030
+ return ` Held for ${Math.round(ms / 1000)}s.`;
41031
+ if (ms < 3600000)
41032
+ return ` Held for ${Math.round(ms / 60000)}m.`;
41033
+ return ` Held for ${Math.round(ms / 3600000)}h.`;
41034
+ } catch {
41035
+ return "";
41036
+ }
41037
+ }
41038
+ function lockHolderState(lockFile) {
41039
+ let pid;
41040
+ try {
41041
+ pid = Number.parseInt(fs6.readFileSync(lockFile, "utf8").split(":")[0] ?? "", 10);
41042
+ } catch {
41043
+ return "";
41044
+ }
41045
+ if (!Number.isInteger(pid) || pid <= 0)
41046
+ return "";
41047
+ const age = heldFor(lockFile);
41048
+ try {
41049
+ process.kill(pid, 0);
41050
+ return ` A process with pid ${pid} is running, so this may clear on its own — but pids get reused, so if that is not a brainbase command the lock is stale.${age}`;
41051
+ } catch (error) {
41052
+ if (error.code === "EPERM") {
41053
+ return ` A process with pid ${pid} is running, so this may clear on its own — but pids get reused, so if that is not a brainbase command the lock is stale.${age}`;
41054
+ }
41055
+ return ` The process that held it (pid ${pid}) is gone, so this lock is stale and will not clear itself.${age}`;
41056
+ }
41057
+ }
41006
41058
 
41007
41059
  class AuthRefreshLockTimeoutError extends Error {
41008
41060
  constructor() {
41009
- super(`Timed out waiting to refresh CLI authentication; retry, or if no other brainbase process is running, remove ${REFRESH_LOCK_FILE}`);
41061
+ super(`Timed out waiting to refresh CLI authentication.${lockHolderState(REFRESH_LOCK_FILE)} Retry, or remove ${REFRESH_LOCK_FILE}`);
41010
41062
  this.name = "AuthRefreshLockTimeoutError";
41011
41063
  }
41012
41064
  }
@@ -53628,7 +53680,7 @@ async function runPack(cwd2) {
53628
53680
  packSpinner.stop(`Packed ${packed.length} component${packed.length === 1 ? "" : "s"}.`);
53629
53681
  const dest = registry.templateDir(name, version);
53630
53682
  if (exists(dest)) {
53631
- f2.error(`A template at ${name}@${version} already exists. Bump the version.`);
53683
+ fail(`A template at ${name}@${version} already exists. Bump the version.`);
53632
53684
  $e("Aborted.");
53633
53685
  return;
53634
53686
  }
@@ -53872,7 +53924,7 @@ var StoredTokenSchema = exports_external.object({
53872
53924
  function withTokenLock(operation) {
53873
53925
  const release = acquireLock(TOKEN_LOCK_FILE, TOKEN_LOCK_TIMEOUT_MS);
53874
53926
  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}`);
53927
+ throw new Error(`Timed out waiting to update the local CLI token.${lockHolderState(TOKEN_LOCK_FILE)} Retry, or remove ${TOKEN_LOCK_FILE}`);
53876
53928
  }
53877
53929
  try {
53878
53930
  return operation();
@@ -54017,6 +54069,8 @@ var DEFINITELY_UNSENT_NETWORK_CODES = new Set([
54017
54069
  "FailedToOpenSocket",
54018
54070
  "UND_ERR_CONNECT_TIMEOUT"
54019
54071
  ]);
54072
+ var LEGACY_DEFAULT_PAGE = 200;
54073
+ var LEGACY_MAX_PAGE = 500;
54020
54074
 
54021
54075
  class ApiError extends Error {
54022
54076
  status;
@@ -54354,6 +54408,127 @@ async function masRequest(pathname, init) {
54354
54408
  return body;
54355
54409
  }
54356
54410
  }
54411
+ async function masReadRequest(pathname, callerSignal) {
54412
+ const credential = await resolveMasCredential();
54413
+ let currentSession = credential.session;
54414
+ let refreshSessionAvailable = credential.source === "session";
54415
+ let res;
54416
+ let text2;
54417
+ for (let attempt2 = 0;; attempt2 += 1) {
54418
+ if (callerSignal?.aborted)
54419
+ throw callerSignal.reason;
54420
+ try {
54421
+ res = await sendWithAuthRetry(refreshSessionAvailable ? currentSession : null, async (refreshed) => {
54422
+ if (refreshed) {
54423
+ currentSession = refreshed;
54424
+ refreshSessionAvailable = false;
54425
+ }
54426
+ return await sendRequest(`${masApiBase(currentSession)}${pathname}`, {
54427
+ method: "GET",
54428
+ signal: withRequestDeadline(callerSignal, MAS_REQUEST_TIMEOUT_MS)
54429
+ }, currentSession?.access_token ?? credential.bearer);
54430
+ });
54431
+ try {
54432
+ text2 = await res.text();
54433
+ } catch (error) {
54434
+ throw new NetworkApiError(`Network error while reading response: ${error.message}`, false);
54435
+ }
54436
+ break;
54437
+ } catch (error) {
54438
+ if (callerSignal?.aborted || !(error instanceof NetworkApiError) || attempt2 >= GET_NETWORK_RETRY_DELAYS_MS.length) {
54439
+ throw error;
54440
+ }
54441
+ await new Promise((resolve) => setTimeout(resolve, GET_NETWORK_RETRY_DELAYS_MS[attempt2]));
54442
+ }
54443
+ }
54444
+ let body = text2;
54445
+ try {
54446
+ body = text2 ? JSON.parse(text2) : null;
54447
+ } catch {}
54448
+ if (!res.ok) {
54449
+ const message = masApiErrorMessage(body, res.status);
54450
+ throw new ApiError(res.status === 401 ? withRejectedSessionHint(message, credential.source, "managed-task") : message, res.status, body);
54451
+ }
54452
+ return body;
54453
+ }
54454
+ function withRequestDeadline(callerSignal, timeoutMs) {
54455
+ const deadline = AbortSignal.timeout(timeoutMs);
54456
+ if (!callerSignal)
54457
+ return deadline;
54458
+ if (callerSignal.aborted)
54459
+ return callerSignal;
54460
+ const combined = new AbortController;
54461
+ const abort = (reason) => combined.abort(reason);
54462
+ deadline.addEventListener("abort", () => abort(deadline.reason), { once: true });
54463
+ callerSignal.addEventListener("abort", () => abort(callerSignal.reason), {
54464
+ once: true
54465
+ });
54466
+ return combined.signal;
54467
+ }
54468
+ async function masResourceRequest(pathname, init = {}) {
54469
+ const credential = await resolveMasCredential();
54470
+ let currentSession = credential.session;
54471
+ const res = await sendWithAuthRetry(credential.source === "session" ? currentSession : null, async (refreshed) => {
54472
+ if (refreshed)
54473
+ currentSession = refreshed;
54474
+ return await sendRequest(`${masApiBase(currentSession)}${pathname}`, {
54475
+ ...init,
54476
+ signal: withRequestDeadline(init.signal ?? undefined, MAS_REQUEST_TIMEOUT_MS)
54477
+ }, currentSession?.access_token ?? credential.bearer);
54478
+ });
54479
+ let text2;
54480
+ try {
54481
+ text2 = await res.text();
54482
+ } catch (error) {
54483
+ throw new NetworkApiError(`Network error while reading response: ${error.message}`, false);
54484
+ }
54485
+ let body = text2;
54486
+ try {
54487
+ body = text2 ? JSON.parse(text2) : null;
54488
+ } catch {}
54489
+ if (!res.ok) {
54490
+ const message = masApiErrorMessage(body, res.status);
54491
+ throw new ApiError(res.status === 401 ? withRejectedSessionHint(message, credential.source, "managed-task") : res.status === 404 && isUnroutedPath(body) ? `This control plane does not serve ${pathname} yet. Update the server, or use the web app.` : message, res.status, body);
54492
+ }
54493
+ return body;
54494
+ }
54495
+
54496
+ class StreamHostMovedError extends ApiError {
54497
+ }
54498
+ async function masStreamTarget(pathname) {
54499
+ const credential = await resolveMasCredential();
54500
+ const base2 = masApiBase(credential.session);
54501
+ return {
54502
+ url: `${base2}${pathname}`,
54503
+ resolveBearer: async () => {
54504
+ const fresh = await resolveMasCredential();
54505
+ const freshBase = masApiBase(fresh.session);
54506
+ if (freshBase !== base2) {
54507
+ throw new StreamHostMovedError(`The control plane moved from ${base2} to ${freshBase} while this stream was open, so the current credential was not sent. Re-run the command to follow against the new host.`);
54508
+ }
54509
+ return fresh.session?.access_token ?? fresh.bearer;
54510
+ }
54511
+ };
54512
+ }
54513
+ function masListQuery(params) {
54514
+ const search = new URLSearchParams;
54515
+ for (const [key2, value] of Object.entries(params)) {
54516
+ if (value !== undefined)
54517
+ search.set(key2, value);
54518
+ }
54519
+ const qs = search.toString();
54520
+ return qs ? `?${qs}` : "";
54521
+ }
54522
+ function masItems(body, what) {
54523
+ if (Array.isArray(body))
54524
+ return body;
54525
+ if (body && typeof body === "object") {
54526
+ const items = body.items;
54527
+ if (Array.isArray(items))
54528
+ return items;
54529
+ }
54530
+ throw new ApiError(`MAS returned an unreadable ${what} response.`, undefined, body);
54531
+ }
54357
54532
  var masApi = {
54358
54533
  async createTask(input, options) {
54359
54534
  const body = await masRequest("/tasks", {
@@ -54362,6 +54537,49 @@ var masApi = {
54362
54537
  body: JSON.stringify(input)
54363
54538
  });
54364
54539
  return parseMasTaskCreateResponse(body);
54540
+ },
54541
+ async listTasks(options = {}) {
54542
+ const params = new URLSearchParams;
54543
+ if (options.agentId)
54544
+ params.set("agent_id", options.agentId);
54545
+ if (options.limit !== undefined)
54546
+ params.set("limit", String(options.limit));
54547
+ const qs = params.toString() ? `?${params.toString()}` : "";
54548
+ return masItems(await masReadRequest(`/tasks${qs}`), "task list");
54549
+ },
54550
+ async getTask(taskId, options = {}) {
54551
+ const body = await masReadRequest(`/tasks/${encodeURIComponent(taskId)}`, options.signal);
54552
+ if (!body || typeof body !== "object" || typeof body.id !== "string" || typeof body.status !== "string") {
54553
+ throw new ApiError("MAS returned an unreadable task response.", undefined, body);
54554
+ }
54555
+ return body;
54556
+ },
54557
+ async listTaskEvents(taskId, options = {}) {
54558
+ const params = new URLSearchParams({ order_by_received: "true" });
54559
+ if (options.limit !== undefined)
54560
+ params.set("limit", String(options.limit));
54561
+ if (options.after) {
54562
+ params.set("after_received_at", options.after.receivedAt);
54563
+ params.set("after_id", options.after.id);
54564
+ }
54565
+ if (options.desc !== undefined)
54566
+ params.set("desc", String(options.desc));
54567
+ const body = await masReadRequest(`/tasks/${encodeURIComponent(taskId)}/events?${params.toString()}`);
54568
+ return masItems(body, "task events");
54569
+ },
54570
+ async listMachines(options = {}) {
54571
+ const body = await masResourceRequest(`/machines${masListQuery({
54572
+ kind: options.kind,
54573
+ include_dead: options.includeDead === undefined ? undefined : String(options.includeDead),
54574
+ limit: options.limit === undefined ? undefined : String(options.limit)
54575
+ })}`);
54576
+ if (!body || !Array.isArray(body.items)) {
54577
+ throw new ApiError("The control plane returned an unreadable machine list", undefined, body);
54578
+ }
54579
+ return body.items;
54580
+ },
54581
+ deleteMachine(machineId) {
54582
+ return masResourceRequest(`/machines/${encodeURIComponent(machineId)}`, { method: "DELETE" });
54365
54583
  }
54366
54584
  };
54367
54585
  function isUnroutedPath(body) {
@@ -54391,20 +54609,48 @@ var api = {
54391
54609
  });
54392
54610
  },
54393
54611
  async listAgents(orgId, teamId) {
54394
- const path58 = `/orgs/${encodeURIComponent(orgId)}/teams/${encodeURIComponent(teamId)}/agents`;
54395
- let body;
54612
+ const base2 = `/orgs/${encodeURIComponent(orgId)}/teams/${encodeURIComponent(teamId)}/agents`;
54613
+ const fetchPage = async (limit) => {
54614
+ const path58 = limit === undefined ? base2 : `${base2}?limit=${limit}`;
54615
+ let body;
54616
+ try {
54617
+ body = await request(path58);
54618
+ } catch (err) {
54619
+ if (err instanceof ApiError && err.status === 404) {
54620
+ throw new ApiError("This control plane does not support listing agents yet. Update the server, or use the web app to find the agent id.", 404, err.body);
54621
+ }
54622
+ throw err;
54623
+ }
54624
+ if (!Array.isArray(body)) {
54625
+ throw new ApiError(`Unexpected response listing agents: expected an array from ${path58}.`, undefined, body);
54626
+ }
54627
+ return body;
54628
+ };
54629
+ const agents = await fetchPage();
54630
+ if (agents.length !== LEGACY_DEFAULT_PAGE)
54631
+ return { agents, complete: true };
54632
+ let retried;
54396
54633
  try {
54397
- body = await request(path58);
54634
+ retried = await fetchPage(LEGACY_MAX_PAGE);
54398
54635
  } catch (err) {
54399
- if (err instanceof ApiError && err.status === 404) {
54400
- throw new ApiError("This control plane does not support listing agents yet. Update the server, or use the web app to find the agent id.", 404, err.body);
54636
+ if (err instanceof ApiError && (err.status === 401 || err.status === 403)) {
54637
+ throw err;
54401
54638
  }
54402
- throw err;
54403
- }
54404
- if (!Array.isArray(body)) {
54405
- throw new ApiError(`Unexpected response listing agents: expected an array from ${path58}.`, undefined, body);
54639
+ const detail = err instanceof Error ? err.message : String(err);
54640
+ return {
54641
+ agents,
54642
+ complete: false,
54643
+ warning: `Warning: showing ${agents.length} agents, and could not confirm that is all of them — ` + `the follow-up request failed (${detail}). ` + "Re-run to check; if it keeps failing, the control plane may need updating."
54644
+ };
54406
54645
  }
54407
- return body;
54646
+ const best = retried.length > agents.length ? retried : agents;
54647
+ if (best.length < LEGACY_MAX_PAGE)
54648
+ return { agents: best, complete: true };
54649
+ return {
54650
+ agents: best,
54651
+ complete: false,
54652
+ warning: `Warning: this control plane caps agent listings at ${best.length} and offers no way ` + "to read past that, so this list may be incomplete. Update the control plane to see every agent."
54653
+ };
54408
54654
  },
54409
54655
  createAgent(input) {
54410
54656
  if (usesLegacyControlPlane() && (input.machine_kind !== undefined || input.default_model !== undefined)) {
@@ -58918,7 +59164,7 @@ async function runOnboard(cwd2, args) {
58918
59164
  try {
58919
59165
  templateRef = await resolveWithRemoteFallback(registry, name, version);
58920
59166
  } catch (err) {
58921
- f2.error(err.message);
59167
+ fail(err.message);
58922
59168
  $e("Aborted.");
58923
59169
  return;
58924
59170
  }
@@ -59380,7 +59626,10 @@ async function runRemove(cwd2, args) {
59380
59626
  text: c2.slug
59381
59627
  }))
59382
59628
  });
59383
- if (!autoProceed(args.yes)) {
59629
+ if (!autoProceedDestructive(args.yes, {
59630
+ action: `Uninstalling ${inst.name}@${inst.version} from ${inst.harness} (${inst.scope})`,
59631
+ flagHint: "Pass --yes to uninstall without a prompt."
59632
+ })) {
59384
59633
  const ans = await se({ message: "Proceed?", initialValue: true });
59385
59634
  if (!ensureNotCancelled(ans))
59386
59635
  continue;
@@ -59842,11 +60091,11 @@ async function publishSkillForTemplate(opts) {
59842
60091
  try {
59843
60092
  probe = await skillsApi.canPublish(creator, pkgSlug);
59844
60093
  } catch (err) {
59845
- f2.error(err.message);
60094
+ fail(err.message);
59846
60095
  return false;
59847
60096
  }
59848
60097
  if (probe.exists && !probe.can_write) {
59849
- f2.error(`You can't publish to ${import_picocolors11.default.bold(creator + "/" + pkgSlug)}.`);
60098
+ fail(`You can't publish to ${import_picocolors11.default.bold(creator + "/" + pkgSlug)}.`);
59850
60099
  return false;
59851
60100
  }
59852
60101
  if (!probe.exists) {
@@ -59862,7 +60111,7 @@ async function publishSkillForTemplate(opts) {
59862
60111
  });
59863
60112
  } catch (err) {
59864
60113
  if (!(err instanceof ApiError && err.status === 409)) {
59865
- f2.error(err.message);
60114
+ fail(err.message);
59866
60115
  return false;
59867
60116
  }
59868
60117
  }
@@ -59895,7 +60144,7 @@ async function publishSkillForTemplate(opts) {
59895
60144
  sp.stop(`Published ${creator}/${pkgSlug}@${version}.`);
59896
60145
  } catch (err) {
59897
60146
  sp.stop("Skill publish failed.");
59898
- f2.error(err.message);
60147
+ fail(err.message);
59899
60148
  return false;
59900
60149
  }
59901
60150
  } finally {
@@ -59916,7 +60165,7 @@ async function runTemplatePublish(_cwd, args) {
59916
60165
  const registry = new LocalRegistry;
59917
60166
  const localList = await registry.list();
59918
60167
  if (localList.length === 0) {
59919
- f2.error("No templates in your local registry. Run `brainbase template pack` first.");
60168
+ fail("No templates in your local registry. Run `brainbase template pack` first.");
59920
60169
  return;
59921
60170
  }
59922
60171
  let name;
@@ -59926,7 +60175,7 @@ async function runTemplatePublish(_cwd, args) {
59926
60175
  name = parsed.name;
59927
60176
  const v3 = parsed.version ?? await registry.latest(name);
59928
60177
  if (!v3) {
59929
- f2.error(`No versions of ${name} in your local registry.`);
60178
+ fail(`No versions of ${name} in your local registry.`);
59930
60179
  return;
59931
60180
  }
59932
60181
  version = v3;
@@ -59965,7 +60214,7 @@ async function runTemplatePublish(_cwd, args) {
59965
60214
  `), import_picocolors11.default.yellow("Findings"));
59966
60215
  }
59967
60216
  if (report.hasBlocker) {
59968
- f2.error("Blocking issues found. Fix them and re-pack before publishing.");
60217
+ fail("Blocking issues found. Fix them and re-pack before publishing.");
59969
60218
  return;
59970
60219
  }
59971
60220
  if (report.findings.some((f4) => f4.severity === "warn") && !autoProceed(args.yes)) {
@@ -59980,7 +60229,7 @@ async function runTemplatePublish(_cwd, args) {
59980
60229
  }
59981
60230
  const status = authStatus();
59982
60231
  if (!status.ok || !status.session) {
59983
- f2.error("Not logged in. Run `brainbase login` first.");
60232
+ fail("Not logged in. Run `brainbase login` first.");
59984
60233
  return;
59985
60234
  }
59986
60235
  const me3 = status.session;
@@ -60024,6 +60273,7 @@ async function runTemplatePublish(_cwd, args) {
60024
60273
  }
60025
60274
  const skillEntries = ref.manifest.components.filter((c2) => c2.type === "skill");
60026
60275
  let manifestMutated = false;
60276
+ const inlinedAfterFailure = [];
60027
60277
  for (const entry of skillEntries) {
60028
60278
  const src = entry.source ?? { type: "inline" };
60029
60279
  if (src.type === "github" || src.type === "git" || src.type === "brainbase") {
@@ -60051,6 +60301,8 @@ async function runTemplatePublish(_cwd, args) {
60051
60301
  });
60052
60302
  if (ok)
60053
60303
  manifestMutated = true;
60304
+ else
60305
+ inlinedAfterFailure.push(entry.slug);
60054
60306
  }
60055
60307
  if (manifestMutated) {
60056
60308
  fs57.writeFileSync(path65.join(ref.rootDir, "brainbase.json"), JSON.stringify(ref.manifest, null, 2));
@@ -60064,7 +60316,7 @@ async function runTemplatePublish(_cwd, args) {
60064
60316
  await pack({ rootDir: ref.rootDir, outFile: tarPath });
60065
60317
  } catch (err) {
60066
60318
  buildSpinner.stop("Bundle failed.");
60067
- f2.error(err.message);
60319
+ fail(err.message);
60068
60320
  return;
60069
60321
  }
60070
60322
  const sha = await sha256OfFile(tarPath);
@@ -60141,15 +60393,25 @@ async function runTemplatePublish(_cwd, args) {
60141
60393
  fs57.rmSync(tmpDir, { recursive: true, force: true });
60142
60394
  } catch {}
60143
60395
  }
60144
- $e(`${import_picocolors11.default.bold(name)}@${version} published.`);
60396
+ const partial2 = inlinedAfterFailure.length > 0;
60397
+ if (partial2) {
60398
+ const plural = inlinedAfterFailure.length === 1 ? "" : "s";
60399
+ f2.warn(`${import_picocolors11.default.bold(name)}@${version} published, but ${inlinedAfterFailure.length} skill${plural} never reached the registry and shipped inline instead: ${inlinedAfterFailure.map((s3) => import_picocolors11.default.bold(s3)).join(", ")}. Re-run ${import_picocolors11.default.cyan("brainbase skill publish")} for ${inlinedAfterFailure.length === 1 ? "it" : "them"}, then publish the template again to swap the inline bytes for a reference.`);
60400
+ $e(`${import_picocolors11.default.bold(name)}@${version} published with ${inlinedAfterFailure.length} skill failure${plural}.`);
60401
+ } else {
60402
+ $e(`${import_picocolors11.default.bold(name)}@${version} published.`);
60403
+ }
60145
60404
  await showResultCard({
60146
- title: "PUBLISHED",
60147
- tone: "ok",
60405
+ title: partial2 ? "PUBLISHED (PARTIAL)" : "PUBLISHED",
60406
+ tone: partial2 ? "warn" : "ok",
60148
60407
  subtitle: `${name}@${version}`,
60149
60408
  meta: [
60150
60409
  ["id", result2.id],
60151
60410
  ["sha256", sha],
60152
60411
  ["visibility", visibility],
60412
+ ...partial2 ? [
60413
+ ["skills inlined after failure", inlinedAfterFailure.join(", ")]
60414
+ ] : [],
60153
60415
  ...visibility === "public" ? [["note", "Quarantined for review on first public publish."]] : []
60154
60416
  ],
60155
60417
  hint: `brainbase template info ${name}`
@@ -60440,7 +60702,7 @@ async function runTemplateInfo(args) {
60440
60702
  const { name, version } = parseRef2(args.ref);
60441
60703
  const [creator, slug] = name.split("/");
60442
60704
  if (!creator || !slug) {
60443
- f2.error("Expected creator/slug.");
60705
+ fail("Expected creator/slug.");
60444
60706
  return;
60445
60707
  }
60446
60708
  const spinner = de();
@@ -60748,17 +61010,17 @@ async function runSkillAdd(cwd2, args) {
60748
61010
  ensureSkillResolversRegistered();
60749
61011
  const source = parseSkillSource(args.source);
60750
61012
  if (source.type === "local" || source.type === "inline") {
60751
- f2.error(`${describeSource(source)} sources are not fetchable. Use a github / git / brainbase source.`);
61013
+ fail(`${describeSource(source)} sources are not fetchable. Use a github / git / brainbase source.`);
60752
61014
  return;
60753
61015
  }
60754
61016
  const resolver = getSkillResolver(source.type);
60755
61017
  if (!resolver) {
60756
- f2.error(`No resolver registered for ${source.type}.`);
61018
+ fail(`No resolver registered for ${source.type}.`);
60757
61019
  return;
60758
61020
  }
60759
61021
  const slug = args.as ?? defaultSkillSlug(source);
60760
61022
  if (!/^[a-zA-Z0-9_-]+$/.test(slug)) {
60761
- f2.error(`Invalid slug ${import_picocolors13.default.bold(slug)}; pass --as <slug>.`);
61023
+ fail(`Invalid slug ${import_picocolors13.default.bold(slug)}; pass --as <slug>.`);
60762
61024
  return;
60763
61025
  }
60764
61026
  let adapterId = args.harness;
@@ -60793,7 +61055,10 @@ async function runSkillAdd(cwd2, args) {
60793
61055
  const skillsRoot = skillsRootFor(adapterId, cwd2, scope);
60794
61056
  const dest = path68.join(skillsRoot, slug);
60795
61057
  if (exists(dest)) {
60796
- if (!autoProceed(args.yes)) {
61058
+ if (!autoProceedDestructive(args.yes, {
61059
+ action: `Overwriting the existing skill at ${dest}`,
61060
+ flagHint: "Pass --yes to overwrite it without a prompt."
61061
+ })) {
60797
61062
  const confirm = await se({
60798
61063
  message: `${import_picocolors13.default.bold(slug)} already exists at ${dest}. Overwrite?`,
60799
61064
  initialValue: false
@@ -60813,7 +61078,7 @@ async function runSkillAdd(cwd2, args) {
60813
61078
  sp.stop("Fetched.");
60814
61079
  } catch (err) {
60815
61080
  sp.stop("Fetch failed.");
60816
- f2.error(err.message);
61081
+ fail(err.message);
60817
61082
  return;
60818
61083
  }
60819
61084
  writeSkillMarker(dest, source);
@@ -60908,7 +61173,10 @@ async function runSkillRemove(cwd2, args) {
60908
61173
  });
60909
61174
  target = candidates[Number(choice)];
60910
61175
  }
60911
- if (!autoProceed(args.yes)) {
61176
+ if (!autoProceedDestructive(args.yes, {
61177
+ action: `Deleting ${target.dir}`,
61178
+ flagHint: "Pass --yes to delete it without a prompt."
61179
+ })) {
60912
61180
  const ok = await se({
60913
61181
  message: `Delete ${import_picocolors15.default.bold(target.dir)}?`,
60914
61182
  initialValue: false
@@ -60937,11 +61205,11 @@ async function runSkillPublish(cwd2, args) {
60937
61205
  banner("skill publish — send a skill to the registry");
60938
61206
  const skillDir = path71.resolve(cwd2, args.dir ?? ".");
60939
61207
  if (!exists(skillDir) || !fs63.statSync(skillDir).isDirectory()) {
60940
- f2.error(`Not a directory: ${import_picocolors16.default.bold(skillDir)}`);
61208
+ fail(`Not a directory: ${import_picocolors16.default.bold(skillDir)}`);
60941
61209
  return;
60942
61210
  }
60943
61211
  if (!exists(path71.join(skillDir, "SKILL.md"))) {
60944
- f2.error(`No ${import_picocolors16.default.bold("SKILL.md")} in ${import_picocolors16.default.bold(skillDir)}. Point at a folder that contains one, or create the file first.`);
61212
+ fail(`No ${import_picocolors16.default.bold("SKILL.md")} in ${import_picocolors16.default.bold(skillDir)}. Point at a folder that contains one, or create the file first.`);
60945
61213
  return;
60946
61214
  }
60947
61215
  const folderSlug = path71.basename(skillDir).toLowerCase();
@@ -60955,12 +61223,12 @@ async function runSkillPublish(cwd2, args) {
60955
61223
  }
60956
61224
  let name = args.name ?? suggestedName;
60957
61225
  if (name && !parseName(name)) {
60958
- f2.error(`Invalid --name "${name}". Use creator/slug.`);
61226
+ fail(`Invalid --name "${name}". Use creator/slug.`);
60959
61227
  return;
60960
61228
  }
60961
61229
  if (!name) {
60962
61230
  if (args.yes) {
60963
- f2.error("Non-interactive publish (--yes) requires --name <creator/slug>.");
61231
+ fail("Non-interactive publish (--yes) requires --name <creator/slug>.");
60964
61232
  return;
60965
61233
  }
60966
61234
  const ans = await text({
@@ -60975,12 +61243,12 @@ async function runSkillPublish(cwd2, args) {
60975
61243
  const { creator, slug: pkgSlug } = parseName(name);
60976
61244
  let version = args.version;
60977
61245
  if (version && !/^\d+\.\d+\.\d+$/.test(version)) {
60978
- f2.error(`Invalid --skill-version "${version}". Use MAJOR.MINOR.PATCH.`);
61246
+ fail(`Invalid --skill-version "${version}". Use MAJOR.MINOR.PATCH.`);
60979
61247
  return;
60980
61248
  }
60981
61249
  if (!version) {
60982
61250
  if (args.yes) {
60983
- f2.error("Non-interactive publish (--yes) requires --skill-version <MAJOR.MINOR.PATCH>.");
61251
+ fail("Non-interactive publish (--yes) requires --skill-version <MAJOR.MINOR.PATCH>.");
60984
61252
  return;
60985
61253
  }
60986
61254
  const ans = await text({
@@ -60995,17 +61263,17 @@ async function runSkillPublish(cwd2, args) {
60995
61263
  try {
60996
61264
  probe = await skillsApi.canPublish(creator, pkgSlug);
60997
61265
  } catch (err) {
60998
- f2.error(`can-publish probe failed: ${err.message}`);
61266
+ fail(`can-publish probe failed: ${err.message}`);
60999
61267
  return;
61000
61268
  }
61001
61269
  if (probe.exists && !probe.can_write) {
61002
- f2.error(`You can't publish to ${import_picocolors16.default.bold(creator + "/" + pkgSlug)}. Pick a name you own.`);
61270
+ fail(`You can't publish to ${import_picocolors16.default.bold(creator + "/" + pkgSlug)}. Pick a name you own.`);
61003
61271
  return;
61004
61272
  }
61005
61273
  if (!probe.exists) {
61006
61274
  const status = authStatus();
61007
61275
  if (!status.ok || !status.session) {
61008
- f2.error("Not logged in. Run `brainbase login` first.");
61276
+ fail("Not logged in. Run `brainbase login` first.");
61009
61277
  return;
61010
61278
  }
61011
61279
  let owner;
@@ -61052,7 +61320,7 @@ async function runSkillPublish(cwd2, args) {
61052
61320
  });
61053
61321
  } catch (err) {
61054
61322
  if (err instanceof ApiError && err.status === 409) {} else {
61055
- f2.error(err.message);
61323
+ fail(err.message);
61056
61324
  return;
61057
61325
  }
61058
61326
  }
@@ -61062,7 +61330,7 @@ async function runSkillPublish(cwd2, args) {
61062
61330
  try {
61063
61331
  const files = collectSkillFiles(skillDir).filter((f4) => f4 !== SKILL_MARKER_FILE);
61064
61332
  if (files.length === 0) {
61065
- f2.error("Skill folder is empty.");
61333
+ fail("Skill folder is empty.");
61066
61334
  return;
61067
61335
  }
61068
61336
  const buildSp = de();
@@ -61194,7 +61462,7 @@ async function runSkillUpdate(cwd2, args) {
61194
61462
  candidates.push({ harness: r2.harness, scope: r2.scope, dir });
61195
61463
  }
61196
61464
  if (candidates.length === 0) {
61197
- f2.warn(`No skill named ${import_picocolors17.default.bold(args.slug)} found.`);
61465
+ failWarn(`No skill named ${import_picocolors17.default.bold(args.slug)} found.`);
61198
61466
  return;
61199
61467
  }
61200
61468
  let target = candidates[0];
@@ -61211,16 +61479,16 @@ async function runSkillUpdate(cwd2, args) {
61211
61479
  }
61212
61480
  const marker = readSkillMarker(target.dir);
61213
61481
  if (!marker) {
61214
- f2.error("No marker file. Skills without provenance can't be updated automatically.");
61482
+ fail("No marker file. Skills without provenance can't be updated automatically.");
61215
61483
  return;
61216
61484
  }
61217
61485
  if (marker.source.type === "inline" || marker.source.type === "local") {
61218
- f2.error(`Skill source is ${describeSource(marker.source)} — nothing to update from.`);
61486
+ fail(`Skill source is ${describeSource(marker.source)} — nothing to update from.`);
61219
61487
  return;
61220
61488
  }
61221
61489
  const resolver = getSkillResolver(marker.source.type);
61222
61490
  if (!resolver) {
61223
- f2.error(`No resolver for ${marker.source.type}.`);
61491
+ fail(`No resolver for ${marker.source.type}.`);
61224
61492
  return;
61225
61493
  }
61226
61494
  if (!autoProceed(args.yes)) {
@@ -61241,7 +61509,7 @@ async function runSkillUpdate(cwd2, args) {
61241
61509
  sp.stop("Fetched.");
61242
61510
  } catch (err) {
61243
61511
  sp.stop("Fetch failed.");
61244
- f2.error(err.message);
61512
+ fail(err.message);
61245
61513
  return;
61246
61514
  }
61247
61515
  writeSkillMarker(target.dir, marker.source, marker.component);
@@ -62176,9 +62444,7 @@ var EvalSchema = exports_external.object({
62176
62444
  path: ["classification_values"]
62177
62445
  });
62178
62446
  var MODEL_ID_RE = /^[A-Za-z0-9._:/-]{1,128}$/;
62179
- var UNSYNCED_MANIFEST_KEYS = ["commands", "hooks", "files"];
62180
62447
  var UnsyncedBlockSchema = exports_external.array(exports_external.record(exports_external.unknown())).optional();
62181
- var UnsyncedBlocksShape = Object.fromEntries(UNSYNCED_MANIFEST_KEYS.map((key2) => [key2, UnsyncedBlockSchema]));
62182
62448
  var AgentManifestSchema = exports_external.object({
62183
62449
  schema: exports_external.literal(1),
62184
62450
  id: exports_external.string().min(1).optional(),
@@ -62205,8 +62471,29 @@ var AgentManifestSchema = exports_external.object({
62205
62471
  });
62206
62472
  }).default([]),
62207
62473
  capabilities: CapabilitiesSchema.optional(),
62208
- ...UnsyncedBlocksShape
62474
+ commands: UnsyncedBlockSchema,
62475
+ hooks: UnsyncedBlockSchema,
62476
+ files: UnsyncedBlockSchema
62209
62477
  });
62478
+ var MANIFEST_KEY_SYNC = {
62479
+ schema: "local",
62480
+ id: "synced",
62481
+ harness: "synced",
62482
+ machine_kind: "synced",
62483
+ default_model: "synced",
62484
+ agent: "synced",
62485
+ instructions: "synced",
62486
+ entrypoint: "synced",
62487
+ playbooks: "synced",
62488
+ skills: "synced",
62489
+ mcp: "synced",
62490
+ evals: "synced",
62491
+ capabilities: "synced",
62492
+ commands: "unsynced",
62493
+ hooks: "unsynced",
62494
+ files: "unsynced"
62495
+ };
62496
+ var UNSYNCED_MANIFEST_KEYS = Object.keys(AgentManifestSchema.shape).filter((key2) => (MANIFEST_KEY_SYNC[key2] ?? "unsynced") === "unsynced");
62210
62497
  function manifestPath(cwd2) {
62211
62498
  return path73.join(cwd2, AGENT_MANIFEST_FILE);
62212
62499
  }
@@ -62879,7 +63166,10 @@ async function handleAlreadyLinked(cwd2, link2, args) {
62879
63166
  return;
62880
63167
  }
62881
63168
  if (next === "unlink") {
62882
- if (!autoProceed(args.yes)) {
63169
+ if (!autoProceedDestructive(args.yes, {
63170
+ action: `Unlinking this folder from ${link2.name}`,
63171
+ flagHint: "Pass --yes to unlink without a prompt."
63172
+ })) {
62883
63173
  const confirmed = await se({
62884
63174
  message: "Remove the link from this folder? (the cloud agent will stay)",
62885
63175
  initialValue: true
@@ -62901,7 +63191,10 @@ async function handleAlreadyLinked(cwd2, link2, args) {
62901
63191
  flagHint: "Pass --agent <id>."
62902
63192
  });
62903
63193
  const newId = ans.trim();
62904
- if (!autoProceed(args.yes)) {
63194
+ if (!autoProceedDestructive(args.yes, {
63195
+ action: `Replacing this folder's link to ${link2.name} with ${newId}`,
63196
+ flagHint: "Pass --yes to re-link without a prompt."
63197
+ })) {
62905
63198
  const confirmed = await se({
62906
63199
  message: "This will replace the current link. Continue?",
62907
63200
  initialValue: false
@@ -63072,7 +63365,10 @@ async function runUnlink(cwd2, args) {
63072
63365
  return;
63073
63366
  }
63074
63367
  f2.info(`Currently linked to ${import_picocolors23.default.bold(link2.name)} ${import_picocolors23.default.dim(`(${link2.slug})`)}.`);
63075
- if (!autoProceed(args.yes)) {
63368
+ if (!autoProceedDestructive(args.yes, {
63369
+ action: `Unlinking this folder from ${link2.name}`,
63370
+ flagHint: "Pass --yes to unlink without a prompt."
63371
+ })) {
63076
63372
  const ok = await se({
63077
63373
  message: "Remove the link from this folder? (the cloud agent will stay)",
63078
63374
  initialValue: true
@@ -63366,7 +63662,7 @@ async function runSync(cwd2, args) {
63366
63662
  banner("sync — bring in the latest changes from your team");
63367
63663
  const link2 = readLink(cwd2);
63368
63664
  if (!link2) {
63369
- f2.warn("This folder is not linked to any agent.");
63665
+ failWarn("This folder is not linked to any agent.");
63370
63666
  f2.info(`Run ${import_picocolors24.default.cyan("brainbase link")} first.`);
63371
63667
  return;
63372
63668
  }
@@ -63379,9 +63675,9 @@ async function runSync(cwd2, args) {
63379
63675
  } catch (err) {
63380
63676
  manifestSpinner.stop("Failed.");
63381
63677
  if (err instanceof ApiError && err.status === 401) {
63382
- f2.error("Your session is invalid. Run `brainbase login` and try again.");
63678
+ fail("Your session is invalid. Run `brainbase login` and try again.");
63383
63679
  } else {
63384
- f2.error(err.message);
63680
+ fail(err.message);
63385
63681
  }
63386
63682
  return;
63387
63683
  }
@@ -64418,7 +64714,7 @@ function componentsForNativeInstall(components, acp) {
64418
64714
  async function runAgentUnpack(cwd2, args) {
64419
64715
  banner("agent unpack — install this agent into a harness layout");
64420
64716
  if (!hasManifest(cwd2)) {
64421
- f2.error(`No ${import_picocolors25.default.bold(AGENT_MANIFEST_FILE)} here.`);
64717
+ fail(`No ${import_picocolors25.default.bold(AGENT_MANIFEST_FILE)} here.`);
64422
64718
  f2.info(`Run ${import_picocolors25.default.cyan("brainbase agent pull <id>")} to bring an agent into this folder first.`);
64423
64719
  return;
64424
64720
  }
@@ -64431,7 +64727,7 @@ async function runAgentUnpack(cwd2, args) {
64431
64727
  return;
64432
64728
  }
64433
64729
  if (!manifest.id) {
64434
- f2.error(`${import_picocolors25.default.bold(AGENT_MANIFEST_FILE)} is unclaimed (no ${import_picocolors25.default.cyan("id")}).`);
64730
+ fail(`${import_picocolors25.default.bold(AGENT_MANIFEST_FILE)} is unclaimed (no ${import_picocolors25.default.cyan("id")}).`);
64435
64731
  f2.info(`Run ${import_picocolors25.default.cyan("brainbase agent create")} to claim it, ` + `or ${import_picocolors25.default.cyan("brainbase agent pull <id>")} to link it to an existing agent.`);
64436
64732
  return;
64437
64733
  }
@@ -64440,7 +64736,7 @@ async function runAgentUnpack(cwd2, args) {
64440
64736
  harness = normalizeHarnessId(args.harness);
64441
64737
  } else if (args.yes) {
64442
64738
  if (!manifest.harness) {
64443
- f2.error(`--yes mode but no harness — set ${import_picocolors25.default.cyan("harness")} in the manifest or pass ${import_picocolors25.default.cyan("--harness")}.`);
64739
+ fail(`--yes mode but no harness — set ${import_picocolors25.default.cyan("harness")} in the manifest or pass ${import_picocolors25.default.cyan("--harness")}.`);
64444
64740
  return;
64445
64741
  }
64446
64742
  harness = normalizeHarnessId(manifest.harness);
@@ -64540,7 +64836,7 @@ async function runAgentUnpack(cwd2, args) {
64540
64836
  runHarnessRemoveMcp(harness, builtinRemoveSlugs, { cwd: cwd2, scope });
64541
64837
  }
64542
64838
  } catch (err) {
64543
- f2.error(`Install failed: ${err.message}`);
64839
+ fail(`Install failed: ${err.message}`);
64544
64840
  return;
64545
64841
  } finally {
64546
64842
  try {
@@ -64989,7 +65285,7 @@ function resolveTargetAgentId(cwd2, args) {
64989
65285
  const manifestId = manifest?.id;
64990
65286
  if (arg && manifestId && arg !== manifestId) {
64991
65287
  if (!args.force) {
64992
- f2.error(`This folder is already linked to a different agent (${import_picocolors26.default.dim(manifestId)}).`);
65288
+ fail(`This folder is already linked to a different agent (${import_picocolors26.default.dim(manifestId)}).`);
64993
65289
  f2.info(`Run ${import_picocolors26.default.cyan(`brainbase agent pull ${arg} --force`)} to override. ` + import_picocolors26.default.yellow("This will overwrite brainbase.agent.yaml and any local progress will be lost."));
64994
65290
  return null;
64995
65291
  }
@@ -65000,10 +65296,10 @@ function resolveTargetAgentId(cwd2, args) {
65000
65296
  if (manifestId)
65001
65297
  return { agentId: manifestId, override: false };
65002
65298
  if (manifest) {
65003
- f2.error(`${import_picocolors26.default.bold(AGENT_MANIFEST_FILE)} is unclaimed (no ${import_picocolors26.default.cyan("id")}).`);
65299
+ fail(`${import_picocolors26.default.bold(AGENT_MANIFEST_FILE)} is unclaimed (no ${import_picocolors26.default.cyan("id")}).`);
65004
65300
  f2.info(`Run ${import_picocolors26.default.cyan("brainbase agent create")} to create a new agent from this manifest, ` + `or ${import_picocolors26.default.cyan("brainbase agent pull <id>")} to pull an existing one.`);
65005
65301
  } else {
65006
- f2.error(`No ${import_picocolors26.default.bold(AGENT_MANIFEST_FILE)} here and no ${import_picocolors26.default.cyan("<id>")} given.`);
65302
+ fail(`No ${import_picocolors26.default.bold(AGENT_MANIFEST_FILE)} here and no ${import_picocolors26.default.cyan("<id>")} given.`);
65007
65303
  f2.info(`Run ${import_picocolors26.default.cyan("brainbase agent pull <id>")} to pull an existing agent into this folder.`);
65008
65304
  }
65009
65305
  return null;
@@ -65366,42 +65662,87 @@ async function pullSecrets(cwd2, agentId) {
65366
65662
  function handleApiError2(err) {
65367
65663
  if (err instanceof ApiError) {
65368
65664
  if (err.status === 401) {
65369
- f2.error("Your session is invalid. Run `brainbase login` and try again.");
65665
+ fail("Your session is invalid. Run `brainbase login` and try again.");
65370
65666
  } else if (err.status === 404) {
65371
- f2.error(`Agent not found, or you don't have access. Double-check the id.`);
65667
+ fail(`Agent not found, or you don't have access. Double-check the id.`);
65372
65668
  } else {
65373
- f2.error(err.message);
65669
+ fail(err.message);
65374
65670
  }
65375
65671
  } else {
65376
- f2.error(err.message);
65672
+ fail(err.message);
65377
65673
  }
65378
65674
  }
65379
65675
 
65380
65676
  // src/cli/agent-push.ts
65381
65677
  var import_picocolors28 = __toESM(require_picocolors(), 1);
65382
65678
 
65383
- // src/core/eval-reconcile.ts
65384
- function manifestClaimsEvals(manifest) {
65385
- return (manifest.evals ?? []).length > 0;
65386
- }
65387
- function decideEvalReconcile(input) {
65388
- const evalRows = input.rows.filter((r2) => r2.type === "eval");
65389
- const unseen = evalRows.filter((r2) => r2.status === "added-cloud").map((r2) => r2.slug).sort();
65390
- const cloudModified = evalRows.filter((r2) => r2.status === "modified-cloud" || r2.status === "modified-both" || r2.status === "added-local" && r2.localHash !== undefined && r2.cloudHash !== undefined && r2.localHash !== r2.cloudHash).map((r2) => r2.slug).sort();
65391
- const archives = evalRows.filter((r2) => r2.status === "removed-local").map((r2) => r2.slug).sort();
65392
- if (!input.claimsEvals && archives.length === 0) {
65679
+ // src/core/collection-reconcile.ts
65680
+ function untouchedRows(decision) {
65681
+ if (decision.kind === "reconcile")
65682
+ return [];
65683
+ return [...decision.unseen, ...decision.cloudModified].sort();
65684
+ }
65685
+ function decideCollectionReconcile(input) {
65686
+ const rows = input.rows.filter((r2) => r2.type === input.type);
65687
+ const unseen = rows.filter((r2) => r2.status === "added-cloud").map((r2) => r2.slug).sort();
65688
+ const cloudModified = rows.filter((r2) => r2.status === "modified-cloud" || r2.status === "modified-both" || r2.status === "added-local" && r2.localHash !== undefined && r2.cloudHash !== undefined && r2.localHash !== r2.cloudHash).map((r2) => r2.slug).sort();
65689
+ const removedLocal = rows.filter((r2) => r2.status === "removed-local");
65690
+ const archives = removedLocal.map((r2) => r2.slug).sort();
65691
+ const staleArchives = removedLocal.filter((r2) => r2.cloudHash !== undefined && r2.lockHash !== undefined && r2.cloudHash !== r2.lockHash).map((r2) => r2.slug).sort();
65692
+ if (!input.claims && archives.length === 0) {
65393
65693
  if (unseen.length > 0 || cloudModified.length > 0) {
65394
- return { kind: "skip", unseen: [...unseen, ...cloudModified].sort() };
65694
+ return { kind: "skip", unseen, cloudModified };
65395
65695
  }
65396
65696
  return { kind: "reconcile", archives: [] };
65397
65697
  }
65398
65698
  if (input.force) {
65399
65699
  return { kind: "reconcile", archives: [...archives, ...unseen].sort() };
65400
65700
  }
65401
- if (unseen.length === 0 && cloudModified.length === 0) {
65701
+ if (unseen.length === 0 && cloudModified.length === 0 && staleArchives.length === 0) {
65402
65702
  return { kind: "reconcile", archives };
65403
65703
  }
65404
- return { kind: "blocked", unseen, cloudModified, archives };
65704
+ return { kind: "blocked", unseen, cloudModified, staleArchives, archives };
65705
+ }
65706
+
65707
+ // src/core/eval-reconcile.ts
65708
+ function manifestClaimsEvals(manifest) {
65709
+ return (manifest.evals ?? []).length > 0;
65710
+ }
65711
+ function decideEvalReconcile(input) {
65712
+ return decideCollectionReconcile({
65713
+ rows: input.rows,
65714
+ type: "eval",
65715
+ claims: input.claimsEvals,
65716
+ force: input.force
65717
+ });
65718
+ }
65719
+
65720
+ // src/core/playbook-reconcile.ts
65721
+ function manifestClaimsPlaybooks(manifest) {
65722
+ return (manifest.playbooks ?? []).length > 0;
65723
+ }
65724
+ function decidePlaybookReconcile(input) {
65725
+ return decideCollectionReconcile({
65726
+ rows: input.rows,
65727
+ type: "playbook",
65728
+ claims: input.claimsPlaybooks,
65729
+ force: input.force
65730
+ });
65731
+ }
65732
+ function playbookLabels(slugs, cloudComponents) {
65733
+ const titleBySlug = new Map;
65734
+ for (const c2 of cloudComponents) {
65735
+ if (c2.type !== "playbook")
65736
+ continue;
65737
+ const title = c2.meta?.playbook?.title;
65738
+ if (typeof title === "string" && title.trim()) {
65739
+ titleBySlug.set(c2.slug, title);
65740
+ }
65741
+ }
65742
+ return slugs.map((slug) => {
65743
+ const title = titleBySlug.get(slug);
65744
+ return title && title !== slug ? `${title} (${slug})` : slug;
65745
+ });
65405
65746
  }
65406
65747
 
65407
65748
  // src/core/agent-outgoing.ts
@@ -65606,7 +65947,7 @@ function planRegistrySkillUpdates(skills, cloudComponents, latestByName) {
65606
65947
  async function runAgentPush(cwd2, args) {
65607
65948
  banner("agent push — send your local changes to the cloud");
65608
65949
  if (!hasManifest(cwd2)) {
65609
- f2.warn(`No ${import_picocolors28.default.bold(AGENT_MANIFEST_FILE)} here.`);
65950
+ failWarn(`No ${import_picocolors28.default.bold(AGENT_MANIFEST_FILE)} here.`);
65610
65951
  f2.info(`Run ${import_picocolors28.default.cyan("brainbase agent create")} to claim a new agent from a manifest, ` + `or ${import_picocolors28.default.cyan("brainbase agent pull <id>")} to pull an existing one.`);
65611
65952
  return;
65612
65953
  }
@@ -65623,7 +65964,7 @@ async function runAgentPush(cwd2, args) {
65623
65964
  return;
65624
65965
  }
65625
65966
  if (!manifest.id) {
65626
- f2.warn(`${import_picocolors28.default.bold(AGENT_MANIFEST_FILE)} is unclaimed (no ${import_picocolors28.default.cyan("id")}). Nothing to push to.`);
65967
+ failWarn(`${import_picocolors28.default.bold(AGENT_MANIFEST_FILE)} is unclaimed (no ${import_picocolors28.default.cyan("id")}). Nothing to push to.`);
65627
65968
  f2.info(`Run ${import_picocolors28.default.cyan("brainbase agent create")} first — that creates the cloud agent and stamps an id here.`);
65628
65969
  return;
65629
65970
  }
@@ -65791,7 +66132,7 @@ async function runAgentPush(cwd2, args) {
65791
66132
  force: !!args.force
65792
66133
  });
65793
66134
  if (evalDecision.kind === "blocked") {
65794
- const { unseen, cloudModified } = evalDecision;
66135
+ const { unseen, cloudModified, staleArchives } = evalDecision;
65795
66136
  f2.error(unseen.length > 0 ? "Cannot push: the cloud has eval changes this folder has never seen." : "Cannot push: evals changed both locally and in the cloud.");
65796
66137
  for (const slug of unseen) {
65797
66138
  console.error(` ${import_picocolors28.default.red("!")} ${fmtType("eval")} ${import_picocolors28.default.bold(slug)} ${import_picocolors28.default.dim("(only in the cloud — pushing would archive it)")}`);
@@ -65799,12 +66140,17 @@ async function runAgentPush(cwd2, args) {
65799
66140
  for (const slug of cloudModified) {
65800
66141
  console.error(` ${import_picocolors28.default.red("!")} ${fmtType("eval")} ${import_picocolors28.default.bold(slug)} ${import_picocolors28.default.dim("(edited in the cloud — pushing would overwrite that edit)")}`);
65801
66142
  }
65802
- f2.info(`Run ${import_picocolors28.default.cyan("brainbase agent pull")} to bring ${unseen.length + cloudModified.length === 1 ? "it" : "them"} into ${import_picocolors28.default.bold("brainbase.agent.yaml")}, then push — or ${import_picocolors28.default.cyan("brainbase agent push --force")} to make your local evals authoritative and archive the rest.`);
66143
+ for (const slug of staleArchives) {
66144
+ console.error(` ${import_picocolors28.default.red("!")} ${fmtType("eval")} ${import_picocolors28.default.bold(slug)} ${import_picocolors28.default.dim("(deleted here, edited in the cloud since — pushing would archive that edit)")}`);
66145
+ }
66146
+ const total = unseen.length + cloudModified.length + staleArchives.length;
66147
+ f2.info(`Run ${import_picocolors28.default.cyan("brainbase agent pull")} to bring ${total === 1 ? "it" : "them"} into ${import_picocolors28.default.bold("brainbase.agent.yaml")}, then push — or ${import_picocolors28.default.cyan("brainbase agent push --force")} to make your local evals authoritative and archive the rest.`);
65803
66148
  process.exitCode = 1;
65804
66149
  return;
65805
66150
  }
65806
66151
  if (evalDecision.kind === "skip") {
65807
- f2.warn(`${evalDecision.unseen.length} cloud eval${evalDecision.unseen.length === 1 ? "" : "s"} (${evalDecision.unseen.map((s3) => import_picocolors28.default.bold(s3)).join(", ")}) ${evalDecision.unseen.length === 1 ? "is" : "are"} not in ${import_picocolors28.default.bold("brainbase.agent.yaml")} and will be left untouched. Run ${import_picocolors28.default.cyan("brainbase agent pull")} to manage ${evalDecision.unseen.length === 1 ? "it" : "them"} from here.`);
66152
+ const left = untouchedRows(evalDecision);
66153
+ f2.warn(`${left.length} cloud eval${left.length === 1 ? "" : "s"} (${left.map((s3) => import_picocolors28.default.bold(s3)).join(", ")}) ${left.length === 1 ? "is" : "are"} not managed by ${import_picocolors28.default.bold("brainbase.agent.yaml")} and will be left untouched. Run ${import_picocolors28.default.cyan("brainbase agent pull")} to manage ${left.length === 1 ? "it" : "them"} from here.`);
65808
66154
  }
65809
66155
  for (const entry of manifest.skills) {
65810
66156
  let parsed;
@@ -65831,6 +66177,32 @@ async function runAgentPush(cwd2, args) {
65831
66177
  process.exitCode = 1;
65832
66178
  return;
65833
66179
  }
66180
+ const playbookDecision = decidePlaybookReconcile({
66181
+ rows,
66182
+ claimsPlaybooks: manifestClaimsPlaybooks(manifest),
66183
+ force: !!args.force
66184
+ });
66185
+ if (playbookDecision.kind === "blocked") {
66186
+ const { unseen, cloudModified, staleArchives } = playbookDecision;
66187
+ f2.error(unseen.length > 0 ? "Cannot push: the cloud has playbooks this folder has never seen." : "Cannot push: playbooks changed both locally and in the cloud.");
66188
+ for (const label of playbookLabels(unseen, cloud.components)) {
66189
+ console.error(` ${import_picocolors28.default.red("!")} ${fmtType("playbook")} ${import_picocolors28.default.bold(label)} ${import_picocolors28.default.dim("(only in the cloud — pushing would archive it)")}`);
66190
+ }
66191
+ for (const label of playbookLabels(cloudModified, cloud.components)) {
66192
+ console.error(` ${import_picocolors28.default.red("!")} ${fmtType("playbook")} ${import_picocolors28.default.bold(label)} ${import_picocolors28.default.dim("(edited in the cloud — pushing would overwrite that edit)")}`);
66193
+ }
66194
+ for (const label of playbookLabels(staleArchives, cloud.components)) {
66195
+ console.error(` ${import_picocolors28.default.red("!")} ${fmtType("playbook")} ${import_picocolors28.default.bold(label)} ${import_picocolors28.default.dim("(deleted here, edited in the cloud since — pushing would archive that edit)")}`);
66196
+ }
66197
+ const total = unseen.length + cloudModified.length + staleArchives.length;
66198
+ f2.info(`Run ${import_picocolors28.default.cyan("brainbase agent pull")} to bring ${total === 1 ? "it" : "them"} into ${import_picocolors28.default.bold(AGENT_MANIFEST_FILE)}, then push — or ${import_picocolors28.default.cyan("brainbase agent push --force")} to make your local playbooks authoritative and archive the rest.`);
66199
+ process.exitCode = 1;
66200
+ return;
66201
+ }
66202
+ if (playbookDecision.kind === "skip") {
66203
+ const labels = playbookLabels(untouchedRows(playbookDecision), cloud.components);
66204
+ f2.warn(`${labels.length} cloud playbook${labels.length === 1 ? "" : "s"} (${labels.map((s3) => import_picocolors28.default.bold(s3)).join(", ")}) ${labels.length === 1 ? "is" : "are"} not managed by ${import_picocolors28.default.bold(AGENT_MANIFEST_FILE)} and will be left untouched. Run ${import_picocolors28.default.cyan("brainbase agent pull")} to manage ${labels.length === 1 ? "it" : "them"} from here.`);
66205
+ }
65834
66206
  const { toSend, conflicts, upstreamOnly } = partitionPushRows(rows, !!args.force);
65835
66207
  const forcedOverrides = args.force ? rows.filter((r2) => r2.status === "modified-both") : [];
65836
66208
  if (conflicts.length > 0) {
@@ -65964,6 +66336,15 @@ async function runAgentPush(cwd2, args) {
65964
66336
  text: `${evalArchives.length} · ${evalArchives.join(", ")}`
65965
66337
  });
65966
66338
  }
66339
+ const playbookArchives = playbookDecision.kind === "reconcile" ? playbookDecision.archives : [];
66340
+ const playbookArchiveLabels = playbookLabels(playbookArchives, cloud.components);
66341
+ if (playbookArchives.length) {
66342
+ resultRows.push({
66343
+ type: "rem",
66344
+ label: "archive",
66345
+ text: `${playbookArchives.length} playbook${playbookArchives.length === 1 ? "" : "s"} · ${playbookArchiveLabels.join(", ")}`
66346
+ });
66347
+ }
65967
66348
  await showResultCard({
65968
66349
  title: "PUSH",
65969
66350
  tone: "info",
@@ -65973,10 +66354,14 @@ async function runAgentPush(cwd2, args) {
65973
66354
  if (evalArchives.length) {
65974
66355
  f2.warn(`${evalArchives.length} cloud eval${evalArchives.length === 1 ? "" : "s"} ${evalArchives.length === 1 ? "is" : "are"} missing from ${import_picocolors28.default.bold("brainbase.agent.yaml")} and will be archived: ${evalArchives.map((s3) => import_picocolors28.default.bold(s3)).join(", ")}. ${import_picocolors28.default.dim("Archived, not deleted — past verdicts are kept, and re-adding the eval restores it.")}`);
65975
66356
  }
66357
+ if (playbookArchives.length) {
66358
+ f2.warn(`${playbookArchives.length} cloud playbook${playbookArchives.length === 1 ? "" : "s"} ${playbookArchives.length === 1 ? "is" : "are"} missing from ${import_picocolors28.default.bold("brainbase.agent.yaml")} and will be archived: ${playbookArchiveLabels.map((s3) => import_picocolors28.default.bold(s3)).join(", ")}. ${import_picocolors28.default.dim("Archived, not deleted — the row is kept, and re-adding the playbook restores it.")}`);
66359
+ }
66360
+ const destructive = evalArchives.length + playbookArchives.length;
65976
66361
  if (!autoProceed(args.yes)) {
65977
66362
  const ok = await se({
65978
- message: evalArchives.length ? `Send these changes, archiving ${evalArchives.length} eval${evalArchives.length === 1 ? "" : "s"}?` : "Send these changes?",
65979
- initialValue: evalArchives.length === 0
66363
+ message: destructive ? `Send these changes, archiving ${describeArchiveSet(evalArchives.length, playbookArchives.length)}?` : "Send these changes?",
66364
+ initialValue: destructive === 0
65980
66365
  });
65981
66366
  if (!ensureNotCancelled(ok)) {
65982
66367
  $e("Aborted.");
@@ -66043,11 +66428,12 @@ async function runAgentPush(cwd2, args) {
66043
66428
  let updatedCloud;
66044
66429
  try {
66045
66430
  const reconcileEvals = evalDecision.kind !== "skip";
66046
- const components = reconcileEvals ? outgoing : outgoing.filter((c2) => c2.type !== "eval");
66431
+ const reconcilePlaybooks = playbookDecision.kind !== "skip";
66432
+ const components = outgoing.filter((c2) => (reconcileEvals || c2.type !== "eval") && (reconcilePlaybooks || c2.type !== "playbook"));
66047
66433
  updatedCloud = await api.pushAgentManifest(agentId, {
66048
66434
  components,
66049
66435
  base_revision: cloud.revision,
66050
- reconcile_playbooks: true,
66436
+ reconcile_playbooks: reconcilePlaybooks,
66051
66437
  reconcile_evals: reconcileEvals
66052
66438
  });
66053
66439
  pushSpinner.stop(`Pushed. New revision ${updatedCloud.revision}.`);
@@ -66095,16 +66481,21 @@ async function runAgentPush(cwd2, args) {
66095
66481
  localHashByKey.set(`eval/${entry.slug}`, hashEvalEntry(entry));
66096
66482
  }
66097
66483
  }
66098
- const lockSource = evalDecision.kind === "skip" ? [
66099
- ...updatedCloud.components.filter((c2) => c2.type !== "eval"),
66100
- ...(lock?.components ?? []).filter((c2) => c2.type === "eval").map((c2) => ({
66484
+ const skipped = new Set;
66485
+ if (evalDecision.kind === "skip")
66486
+ skipped.add("eval");
66487
+ if (playbookDecision.kind === "skip")
66488
+ skipped.add("playbook");
66489
+ const lockSource = skipped.size === 0 ? updatedCloud.components : [
66490
+ ...updatedCloud.components.filter((c2) => !skipped.has(c2.type)),
66491
+ ...(lock?.components ?? []).filter((c2) => skipped.has(c2.type)).map((c2) => ({
66101
66492
  type: c2.type,
66102
66493
  slug: c2.slug,
66103
66494
  hash: c2.hash,
66104
66495
  files: [],
66105
66496
  meta: undefined
66106
66497
  }))
66107
- ] : updatedCloud.components;
66498
+ ];
66108
66499
  const newLock = {
66109
66500
  schemaVersion: 1,
66110
66501
  agent_id: agentId,
@@ -66186,6 +66577,15 @@ async function pushSecrets(agentId, plan) {
66186
66577
  return false;
66187
66578
  }
66188
66579
  }
66580
+ function describeArchiveSet(evals, playbooks) {
66581
+ const parts = [];
66582
+ if (evals)
66583
+ parts.push(`${evals} eval${evals === 1 ? "" : "s"}`);
66584
+ if (playbooks) {
66585
+ parts.push(`${playbooks} playbook${playbooks === 1 ? "" : "s"}`);
66586
+ }
66587
+ return parts.join(" and ");
66588
+ }
66189
66589
  function secretDiffSummary(diff2) {
66190
66590
  return `${diff2.localOnly.length} added, ${diff2.changed.length} updated, ${diff2.cloudOnly.length} removed`;
66191
66591
  }
@@ -66216,9 +66616,10 @@ async function runAgentStatus(cwd2, args = {}) {
66216
66616
  if (!link2) {
66217
66617
  if (json) {
66218
66618
  emitJson({ linked: false, ignored: [], unchecked: [] });
66619
+ process.exitCode = 1;
66219
66620
  return;
66220
66621
  }
66221
- f2.warn("This folder is not linked to any agent.");
66622
+ failWarn("This folder is not linked to any agent.");
66222
66623
  f2.info(`Run ${import_picocolors29.default.cyan("brainbase link")} first.`);
66223
66624
  return;
66224
66625
  }
@@ -66243,9 +66644,9 @@ async function runAgentStatus(cwd2, args = {}) {
66243
66644
  } catch (err) {
66244
66645
  const unauthorized = err instanceof ApiError && err.status === 401;
66245
66646
  const message = unauthorized ? "Your session is invalid. Run `brainbase login` and try again." : err.message;
66647
+ process.exitCode = 1;
66246
66648
  if (json) {
66247
66649
  console.error(message);
66248
- process.exitCode = 1;
66249
66650
  return;
66250
66651
  }
66251
66652
  sp?.stop("Failed to reach brainbase.");
@@ -66310,20 +66711,25 @@ async function runAgentStatus(cwd2, args = {}) {
66310
66711
  };
66311
66712
  let secretsChecked = true;
66312
66713
  let secretsUncheckedReason = "";
66714
+ let cloudSecretNames = null;
66313
66715
  try {
66314
- const localSecrets = readLocalSecrets(cwd2);
66315
66716
  const cloudRes = await api.getAgentSecrets(link2.agent_id);
66316
- secretDrift = diffSecrets(localSecrets, cloudRes.secrets);
66717
+ cloudSecretNames = new Set(Object.keys(cloudRes.secrets));
66718
+ secretDrift = diffSecrets(readLocalSecrets(cwd2), cloudRes.secrets);
66317
66719
  } catch (err) {
66318
- if (!(err instanceof ApiError && err.status === 404)) {
66319
- secretsChecked = false;
66320
- secretsUncheckedReason = describeSecretsFailure(err);
66321
- }
66720
+ secretsChecked = false;
66721
+ secretsUncheckedReason = describeSecretsFailure(err);
66322
66722
  }
66323
66723
  const componentsDrifted = conflicts.length > 0 || toPush.length > 0 || toPull.length > 0;
66324
66724
  const metaDrifted = meta.localChanged || meta.cloudChanged;
66325
66725
  const configDrifted = config.unsupported.length > 0 || config.machineMismatch || config.machineCloudChanged || config.defaultModelLocalChanged || config.defaultModelCloudChanged || capabilityDrifted(config.memory) || capabilityDrifted(config.browser);
66326
66726
  const secretsDrifted = secretDrift.localOnly.length > 0 || secretDrift.cloudOnly.length > 0 || secretDrift.changed.length > 0;
66727
+ const secretConflicts = cloudAgent.secret_conflicts ?? [];
66728
+ const inheritedSecrets = (cloudAgent.inherited_secret_keys ?? []).map((entry) => ({
66729
+ ...entry,
66730
+ overridden: entry.overridden ?? cloudSecretNames?.has(entry.key) ?? false
66731
+ }));
66732
+ const hasInheritedSecrets = inheritedSecrets.length > 0 || secretConflicts.length > 0;
66327
66733
  const everythingInSync = !componentsDrifted && !metaDrifted && !configDrifted && !secretsDrifted;
66328
66734
  const unchecked = secretsChecked ? [] : [{ signal: "secrets", reason: secretsUncheckedReason }];
66329
66735
  const evalPlan = decideEvalReconcile({
@@ -66331,12 +66737,13 @@ async function runAgentStatus(cwd2, args = {}) {
66331
66737
  claimsEvals: manifestClaimsEvals(manifest),
66332
66738
  force: false
66333
66739
  });
66334
- const evalReport = {
66335
- archive: evalPlan.kind === "reconcile" ? evalPlan.archives : [],
66336
- unseen: evalPlan.kind === "reconcile" ? [] : evalPlan.unseen,
66337
- cloudModified: evalPlan.kind === "blocked" ? evalPlan.cloudModified : [],
66338
- pushBlocked: evalPlan.kind === "blocked"
66339
- };
66740
+ const evalReport = reconcileReport(evalPlan);
66741
+ const playbookPlan = decidePlaybookReconcile({
66742
+ rows,
66743
+ claimsPlaybooks: manifestClaimsPlaybooks(manifest),
66744
+ force: false
66745
+ });
66746
+ const playbookReport = reconcileReport(playbookPlan);
66340
66747
  if (json) {
66341
66748
  emitJson({
66342
66749
  linked: true,
@@ -66358,17 +66765,26 @@ async function runAgentStatus(cwd2, args = {}) {
66358
66765
  secrets: secretsChecked ? {
66359
66766
  localOnly: secretDrift.localOnly,
66360
66767
  cloudOnly: secretDrift.cloudOnly,
66361
- changed: secretDrift.changed
66768
+ changed: secretDrift.changed,
66769
+ inherited: inheritedSecrets,
66770
+ conflicts: secretConflicts
66362
66771
  } : null,
66772
+ orchestrationSecrets: {
66773
+ inherited: inheritedSecrets,
66774
+ conflicts: secretConflicts
66775
+ },
66363
66776
  components: {
66364
66777
  push: toPush.map(rowJson),
66365
66778
  pull: toPull.map(rowJson),
66366
66779
  conflicts: conflicts.map(rowJson)
66367
66780
  },
66368
66781
  evals: evalReport,
66782
+ playbooks: playbookReport,
66369
66783
  inSync: everythingInSync,
66370
66784
  unchecked
66371
66785
  });
66786
+ if (!secretsChecked)
66787
+ process.exitCode = 1;
66372
66788
  return;
66373
66789
  }
66374
66790
  const lines = [];
@@ -66430,7 +66846,7 @@ async function runAgentStatus(cwd2, args = {}) {
66430
66846
  }
66431
66847
  lines.push("");
66432
66848
  }
66433
- if (secretsDrifted || !secretsChecked) {
66849
+ if (secretsDrifted || !secretsChecked || hasInheritedSecrets) {
66434
66850
  lines.push(` ${import_picocolors29.default.bold("secrets")}`);
66435
66851
  if (!secretsChecked) {
66436
66852
  lines.push(` ${import_picocolors29.default.dim("? unchecked")} ${secretsUncheckedReason}`);
@@ -66441,16 +66857,34 @@ async function runAgentStatus(cwd2, args = {}) {
66441
66857
  lines.push(` ${import_picocolors29.default.yellow("→ push")} values changed: ${secretDrift.changed.join(", ")}`);
66442
66858
  if (secretDrift.cloudOnly.length)
66443
66859
  lines.push(` ${import_picocolors29.default.cyan("← pull")} new on cloud: ${secretDrift.cloudOnly.join(", ")}`);
66860
+ for (const entry of inheritedSecrets) {
66861
+ const source = entry.orchestration_name || entry.orchestration_id;
66862
+ const pendingOverride = !entry.overridden && (secretDrift.localOnly.includes(entry.key) || secretDrift.changed.includes(entry.key));
66863
+ lines.push(entry.overridden ? ` ${entry.key} ${import_picocolors29.default.dim(`(inherited from ${source}, overridden by this agent's own secret)`)}` : pendingOverride ? ` ${entry.key} ${import_picocolors29.default.dim(`(inherited from ${source}, will be overridden once pushed)`)}` : ` ${entry.key} ${import_picocolors29.default.dim(`(inherited from ${source})`)}`);
66864
+ }
66865
+ for (const entry of secretConflicts) {
66866
+ lines.push(` ${import_picocolors29.default.dim("! conflict")} ${entry.key} ${import_picocolors29.default.dim(`has different values ${conflictSources(entry)}; neither value is applied until you set one on this agent`)}`);
66867
+ }
66868
+ if (secretConflicts.length > 0 && everythingInSync) {
66869
+ lines.push(` ${import_picocolors29.default.dim("conflicts are informational; they never block sync")}`);
66870
+ }
66444
66871
  lines.push("");
66445
66872
  }
66446
66873
  if (everythingInSync) {
66447
- const qualifier = secretsChecked ? "" : ` ${import_picocolors29.default.dim("(secrets not checked)")}`;
66448
- lines.push(` ${import_picocolors29.default.green("✓")} everything is in sync${qualifier}`);
66874
+ if (secretsChecked) {
66875
+ lines.push(` ${import_picocolors29.default.green("✓")} everything is in sync`);
66876
+ } else {
66877
+ lines.push(` ${import_picocolors29.default.yellow("?")} cannot confirm sync — secrets were not compared`);
66878
+ lines.push(` ${import_picocolors29.default.dim('everything else came back clean; the reason is under "secrets" above')}`);
66879
+ process.exitCode = 1;
66880
+ }
66449
66881
  lines.push("");
66450
66882
  console.log(lines.join(`
66451
66883
  `));
66452
66884
  return;
66453
66885
  }
66886
+ if (!secretsChecked)
66887
+ process.exitCode = 1;
66454
66888
  if (toPush.length) {
66455
66889
  lines.push(` ${import_picocolors29.default.bold("changes to push")} ${import_picocolors29.default.dim(`(${toPush.length})`)}`);
66456
66890
  for (const r2 of toPush)
@@ -66485,13 +66919,55 @@ async function runAgentStatus(cwd2, args = {}) {
66485
66919
  for (const slug of evalReport.cloudModified) {
66486
66920
  lines.push(` ${import_picocolors29.default.red("!")} ${import_picocolors29.default.bold(slug)} ${import_picocolors29.default.dim("— edited in the cloud; pushing would overwrite that edit")}`);
66487
66921
  }
66922
+ for (const slug of evalReport.staleArchives) {
66923
+ lines.push(` ${import_picocolors29.default.red("!")} ${import_picocolors29.default.bold(slug)} ${import_picocolors29.default.dim("— deleted here, edited in the cloud since; pushing would archive that edit")}`);
66924
+ }
66488
66925
  lines.push(` ${import_picocolors29.default.dim("run")} ${import_picocolors29.default.cyan("brainbase agent pull")} ${import_picocolors29.default.dim("first")}`);
66489
66926
  lines.push("");
66490
- } else if (evalReport.unseen.length) {
66491
- lines.push(` ${import_picocolors29.default.bold("evals only in the cloud")} ${import_picocolors29.default.dim(`(${evalReport.unseen.length})`)}`);
66927
+ } else if (evalReport.unseen.length || evalReport.cloudModified.length) {
66928
+ const total = evalReport.unseen.length + evalReport.cloudModified.length;
66929
+ lines.push(` ${import_picocolors29.default.bold("evals this manifest does not manage")} ${import_picocolors29.default.dim(`(${total})`)}`);
66492
66930
  for (const slug of evalReport.unseen) {
66493
- lines.push(` ${import_picocolors29.default.cyan("←")} ${import_picocolors29.default.bold(slug)} ${import_picocolors29.default.dim("— left untouched by push; pull to manage it here")}`);
66931
+ lines.push(` ${import_picocolors29.default.cyan("←")} ${import_picocolors29.default.bold(slug)} ${import_picocolors29.default.dim("— only in the cloud; left untouched by push")}`);
66932
+ }
66933
+ for (const slug of evalReport.cloudModified) {
66934
+ lines.push(` ${import_picocolors29.default.cyan("←")} ${import_picocolors29.default.bold(slug)} ${import_picocolors29.default.dim("— edited in the cloud; left untouched by push")}`);
66935
+ }
66936
+ lines.push(` ${import_picocolors29.default.dim("run")} ${import_picocolors29.default.cyan("brainbase agent pull")} ${import_picocolors29.default.dim("to manage them from here")}`);
66937
+ lines.push("");
66938
+ }
66939
+ const pbLabel = (slug) => playbookLabels([slug], cloud.components)[0] ?? slug;
66940
+ if (playbookReport.archive.length) {
66941
+ lines.push(` ${import_picocolors29.default.bold("playbooks a push would archive")} ${import_picocolors29.default.dim(`(${playbookReport.archive.length})`)}`);
66942
+ for (const slug of playbookReport.archive) {
66943
+ lines.push(` ${import_picocolors29.default.yellow("⨯")} ${import_picocolors29.default.bold(pbLabel(slug))} ${import_picocolors29.default.dim("— in the cloud, not in this manifest")}`);
66494
66944
  }
66945
+ lines.push(` ${import_picocolors29.default.dim("archived, not deleted: re-adding the playbook restores it")}`);
66946
+ lines.push("");
66947
+ }
66948
+ if (playbookReport.pushBlocked) {
66949
+ lines.push(` ${import_picocolors29.default.bold(import_picocolors29.default.red("playbooks: push blocked"))}`);
66950
+ for (const slug of playbookReport.unseen) {
66951
+ lines.push(` ${import_picocolors29.default.red("!")} ${import_picocolors29.default.bold(pbLabel(slug))} ${import_picocolors29.default.dim("— only in the cloud; pushing would archive it")}`);
66952
+ }
66953
+ for (const slug of playbookReport.cloudModified) {
66954
+ lines.push(` ${import_picocolors29.default.red("!")} ${import_picocolors29.default.bold(pbLabel(slug))} ${import_picocolors29.default.dim("— edited in the cloud; pushing would overwrite that edit")}`);
66955
+ }
66956
+ for (const slug of playbookReport.staleArchives) {
66957
+ lines.push(` ${import_picocolors29.default.red("!")} ${import_picocolors29.default.bold(pbLabel(slug))} ${import_picocolors29.default.dim("— deleted here, edited in the cloud since; pushing would archive that edit")}`);
66958
+ }
66959
+ lines.push(` ${import_picocolors29.default.dim("run")} ${import_picocolors29.default.cyan("brainbase agent pull")} ${import_picocolors29.default.dim("first")}`);
66960
+ lines.push("");
66961
+ } else if (playbookReport.unseen.length || playbookReport.cloudModified.length) {
66962
+ const total = playbookReport.unseen.length + playbookReport.cloudModified.length;
66963
+ lines.push(` ${import_picocolors29.default.bold("playbooks this manifest does not manage")} ${import_picocolors29.default.dim(`(${total})`)}`);
66964
+ for (const slug of playbookReport.unseen) {
66965
+ lines.push(` ${import_picocolors29.default.cyan("←")} ${import_picocolors29.default.bold(pbLabel(slug))} ${import_picocolors29.default.dim("— only in the cloud; left untouched by push")}`);
66966
+ }
66967
+ for (const slug of playbookReport.cloudModified) {
66968
+ lines.push(` ${import_picocolors29.default.cyan("←")} ${import_picocolors29.default.bold(pbLabel(slug))} ${import_picocolors29.default.dim("— edited in the cloud; left untouched by push")}`);
66969
+ }
66970
+ lines.push(` ${import_picocolors29.default.dim("run")} ${import_picocolors29.default.cyan("brainbase agent pull")} ${import_picocolors29.default.dim("to manage them from here")}`);
66495
66971
  lines.push("");
66496
66972
  }
66497
66973
  lines.push(` ${import_picocolors29.default.dim("run")} ${import_picocolors29.default.cyan("brainbase agent pull")} ${import_picocolors29.default.dim("to apply cloud changes,")} ${import_picocolors29.default.cyan("brainbase agent push")} ${import_picocolors29.default.dim("to send yours")}`);
@@ -66499,6 +66975,25 @@ async function runAgentStatus(cwd2, args = {}) {
66499
66975
  console.log(lines.join(`
66500
66976
  `));
66501
66977
  }
66978
+ function reconcileReport(decision) {
66979
+ return {
66980
+ archive: decision.kind === "reconcile" ? decision.archives : [],
66981
+ unseen: decision.kind === "reconcile" ? [] : decision.unseen,
66982
+ cloudModified: decision.kind === "reconcile" ? [] : decision.cloudModified,
66983
+ staleArchives: decision.kind === "blocked" ? decision.staleArchives : [],
66984
+ pushBlocked: decision.kind === "blocked"
66985
+ };
66986
+ }
66987
+ function conflictSources(entry) {
66988
+ const names = (entry.orchestration_names ?? []).filter((name) => name);
66989
+ if (names.length > 1) {
66990
+ return `in ${names.slice(0, -1).join(", ")} and ${names[names.length - 1]}`;
66991
+ }
66992
+ if (names.length === 1)
66993
+ return `in ${names[0]}`;
66994
+ const count = entry.orchestration_ids?.length ?? 0;
66995
+ return count > 0 ? `in ${count} orchestrations` : "in its orchestrations";
66996
+ }
66502
66997
  function emitJson(report) {
66503
66998
  console.log(JSON.stringify(report, null, 2));
66504
66999
  }
@@ -66515,6 +67010,9 @@ function describeSecretsFailure(err) {
66515
67010
  if (remote && err.status === 401) {
66516
67011
  return "could not fetch secrets: your session is invalid — run `brainbase login`";
66517
67012
  }
67013
+ if (remote && err.status === 404) {
67014
+ return "could not fetch secrets: the control plane answered 404 — no secrets " + "route on this deployment, no access to this agent, or the agent is " + "pinned to a benchmark thread";
67015
+ }
66518
67016
  const where = remote ? "could not fetch secrets from the control plane" : `could not read ${path81.join(LINK_DIR, SECRETS_FILE)}`;
66519
67017
  const detail = oneLine(err instanceof Error ? err.message : typeof err === "string" ? err : "");
66520
67018
  return detail ? `${where}: ${detail}` : where;
@@ -66920,7 +67418,7 @@ async function runAgentCreate(cwd2, args) {
66920
67418
  if (!manifest)
66921
67419
  return;
66922
67420
  if (manifest.id) {
66923
- f2.warn(`This folder already belongs to an agent — ${import_picocolors32.default.bold(manifest.agent.name)} (${import_picocolors32.default.dim(manifest.id)}).`);
67421
+ failWarn(`This folder already belongs to an agent — ${import_picocolors32.default.bold(manifest.agent.name)} (${import_picocolors32.default.dim(manifest.id)}).`);
66924
67422
  f2.info(`If you want to detach it, run ${import_picocolors32.default.cyan("brainbase unlink")} first; or move to a different directory.`);
66925
67423
  return;
66926
67424
  }
@@ -66974,9 +67472,9 @@ async function runAgentCreate(cwd2, args) {
66974
67472
  const body = resolveEntrypoint(cwd2, manifest);
66975
67473
  if (body === null) {
66976
67474
  if (manifest.entrypoint.file) {
66977
- f2.error(`Entrypoint file ${import_picocolors32.default.bold(manifest.entrypoint.file)} not found.`);
67475
+ fail(`Entrypoint file ${import_picocolors32.default.bold(manifest.entrypoint.file)} not found.`);
66978
67476
  } else {
66979
- f2.error("Entrypoint block is empty.");
67477
+ fail("Entrypoint block is empty.");
66980
67478
  }
66981
67479
  return;
66982
67480
  }
@@ -67000,7 +67498,6 @@ async function runAgentCreate(cwd2, args) {
67000
67498
  } catch (err) {
67001
67499
  createSpinner.stop("Failed.");
67002
67500
  handleApiError4(err);
67003
- process.exitCode = 1;
67004
67501
  return;
67005
67502
  }
67006
67503
  const machineConfigMissing = manifest.machine_kind !== undefined && agent.machine_kind !== manifest.machine_kind;
@@ -67097,10 +67594,12 @@ async function runAgentCreate(cwd2, args) {
67097
67594
  manifest = readManifest(cwd2);
67098
67595
  let updatedCloud = null;
67099
67596
  const hasContent = !!manifest.instructions || manifest.skills.length > 0 || (manifest.mcp ?? []).length > 0 || (manifest.playbooks ?? []).length > 0 || (manifest.evals ?? []).length > 0;
67597
+ let contentUploadFailed = false;
67100
67598
  if (hasContent) {
67101
67599
  const outgoing = await buildOutgoingComponents(cwd2, manifest, null);
67102
67600
  if (outgoing === null) {
67103
- f2.warn(`Agent created, but local content wasn't uploaded. Fix the issue above and run ${import_picocolors32.default.cyan("brainbase agent push")}.`);
67601
+ contentUploadFailed = true;
67602
+ failWarn(`Agent created, but local content wasn't uploaded. Fix the issue above and run ${import_picocolors32.default.cyan("brainbase agent push")}.`);
67104
67603
  } else if (outgoing.length > 0) {
67105
67604
  const pushSpinner = de();
67106
67605
  pushSpinner.start("Pushing local content…");
@@ -67114,10 +67613,11 @@ async function runAgentCreate(cwd2, args) {
67114
67613
  pushSpinner.stop(`Pushed at revision ${updatedCloud.revision}.`);
67115
67614
  } catch (err) {
67116
67615
  pushSpinner.stop("Failed.");
67616
+ contentUploadFailed = true;
67117
67617
  if (err instanceof ApiError) {
67118
- f2.warn(`Content upload failed: ${err.message}. Run ${import_picocolors32.default.cyan("brainbase agent push")} to retry.`);
67618
+ failWarn(`Agent created and this folder linked, but the content upload failed: ${err.message}. Run ${import_picocolors32.default.cyan("brainbase agent push")} to retry.`);
67119
67619
  } else {
67120
- f2.warn(`Content upload failed: ${err.message}. Run ${import_picocolors32.default.cyan("brainbase agent push")} to retry.`);
67620
+ failWarn(`Agent created and this folder linked, but the content upload failed: ${err.message}. Run ${import_picocolors32.default.cyan("brainbase agent push")} to retry.`);
67121
67621
  }
67122
67622
  }
67123
67623
  }
@@ -67164,15 +67664,18 @@ async function runAgentCreate(cwd2, args) {
67164
67664
  }
67165
67665
  };
67166
67666
  writeSyncState(cwd2, state);
67167
- $e(`Created ${import_picocolors32.default.bold(agent.name)} and linked this folder.`);
67667
+ $e(contentUploadFailed ? `Created ${import_picocolors32.default.bold(agent.name)} and linked this folder, without its local content.` : `Created ${import_picocolors32.default.bold(agent.name)} and linked this folder.`);
67168
67668
  await showResultCard({
67169
- title: "CREATED",
67170
- tone: "ok",
67669
+ title: contentUploadFailed ? "CREATED (NO CONTENT)" : "CREATED",
67670
+ tone: contentUploadFailed ? "warn" : "ok",
67171
67671
  subtitle: link2.tagline ? `${link2.name} — ${link2.tagline}` : link2.name,
67172
67672
  meta: [
67173
67673
  ["slug", link2.slug],
67174
67674
  ["agent", link2.agent_id],
67175
- ...link2.url ? [["url", link2.url]] : []
67675
+ ...link2.url ? [["url", link2.url]] : [],
67676
+ ...contentUploadFailed ? [
67677
+ ["content", "not uploaded — run `brainbase agent push`"]
67678
+ ] : []
67176
67679
  ]
67177
67680
  });
67178
67681
  console.log();
@@ -67239,12 +67742,12 @@ async function pickHarness2(cwd2) {
67239
67742
  function handleApiError4(err) {
67240
67743
  if (err instanceof ApiError) {
67241
67744
  if (err.status === 401) {
67242
- f2.error("Your session is invalid. Run `brainbase login` and try again.");
67745
+ fail("Your session is invalid. Run `brainbase login` and try again.");
67243
67746
  } else {
67244
- f2.error(err.message);
67747
+ fail(err.message);
67245
67748
  }
67246
67749
  } else {
67247
- f2.error(err.message);
67750
+ fail(err.message);
67248
67751
  }
67249
67752
  $e("Aborted.");
67250
67753
  }
@@ -67335,12 +67838,18 @@ async function runAgentList(args) {
67335
67838
  allowPrompt: !args.json,
67336
67839
  announce: !args.json
67337
67840
  });
67338
- const agents = await api.listAgents(org.id, team.id);
67841
+ const { agents, complete, warning } = await api.listAgents(org.id, team.id);
67842
+ if (!complete && warning)
67843
+ console.error(warning);
67339
67844
  if (args.json) {
67340
67845
  console.log(JSON.stringify(agents, null, 2));
67341
67846
  return;
67342
67847
  }
67343
- console.log(formatAgentList(agents, { orgName: org.name, teamName: team.name }));
67848
+ console.log(formatAgentList(agents, {
67849
+ orgName: org.name,
67850
+ teamName: team.name,
67851
+ complete
67852
+ }));
67344
67853
  }
67345
67854
  function formatAgentList(agents, labels) {
67346
67855
  const lines = [""];
@@ -67349,6 +67858,8 @@ function formatAgentList(agents, labels) {
67349
67858
  return lines.join(`
67350
67859
  `);
67351
67860
  }
67861
+ const count = `${agents.length} agent${agents.length === 1 ? "" : "s"}`;
67862
+ lines.push(` ${import_picocolors34.default.dim(labels.complete ? `${count} in ${labels.orgName} → ${labels.teamName}` : `${count} in ${labels.orgName} → ${labels.teamName} (may be incomplete)`)}`, "");
67352
67863
  for (const agent of agents) {
67353
67864
  lines.push(` ${import_picocolors34.default.bold(agent.name)} ${import_picocolors34.default.dim(agent.slug)}`);
67354
67865
  if (agent.tagline)
@@ -67738,7 +68249,10 @@ async function disconnect(cwd2, target, args, json) {
67738
68249
  if (!link2) {
67739
68250
  throw new Error("This folder is not linked to any agent. Run `brainbase link` first.");
67740
68251
  }
67741
- if (!json && !autoProceed(args.yes)) {
68252
+ if (!json && !autoProceedDestructive(args.yes, {
68253
+ action: `Disconnecting ${target} from ${link2.name}`,
68254
+ flagHint: "Pass --yes to disconnect without a prompt."
68255
+ })) {
67742
68256
  const ok = ensureNotCancelled(await se({ message: `Disconnect ${target} from ${link2.name}?` }));
67743
68257
  if (!ok) {
67744
68258
  f2.info("Nothing changed.");
@@ -68281,13 +68795,26 @@ var SyncedEdgeSchema = exports_external.object({
68281
68795
  description: exports_external.string().default(""),
68282
68796
  payload_schema: exports_external.record(exports_external.unknown()).default({})
68283
68797
  });
68798
+ var SyncedScheduleTriggerSchema = exports_external.object({
68799
+ node_id: exports_external.string(),
68800
+ hash: exports_external.string()
68801
+ });
68802
+ var SyncedOrchestrationMetaSchema = exports_external.object({
68803
+ name: exports_external.string().optional(),
68804
+ description: exports_external.string().optional(),
68805
+ icon: exports_external.string().optional(),
68806
+ icon_color: exports_external.string().optional(),
68807
+ credit_limit: exports_external.number().optional()
68808
+ });
68284
68809
  var OrchestrationSyncStateSchema = exports_external.object({
68285
68810
  schemaVersion: exports_external.literal(1),
68286
68811
  orchestration_id: exports_external.string(),
68287
68812
  revision: exports_external.number(),
68288
68813
  synced_at: exports_external.string(),
68289
68814
  members: exports_external.array(SyncedMemberSchema),
68290
- edges: exports_external.array(SyncedEdgeSchema)
68815
+ edges: exports_external.array(SyncedEdgeSchema),
68816
+ triggers: exports_external.array(SyncedScheduleTriggerSchema).optional(),
68817
+ meta: SyncedOrchestrationMetaSchema.optional()
68291
68818
  });
68292
68819
  function orchLinkPath(cwd2) {
68293
68820
  return path85.join(cwd2, LINK_DIR, ORCH_LINK_FILE);
@@ -68629,6 +69156,158 @@ function formatScheduleTriggerLabel(input) {
68629
69156
  return `schedule ${input.nodeId} (${cron}, ${state})${targetText}`;
68630
69157
  }
68631
69158
 
69159
+ // src/core/orchestration-reconcile.ts
69160
+ import crypto5 from "node:crypto";
69161
+ var SCHEDULE_TRIGGER = "schedule-trigger";
69162
+ function stableJson(value) {
69163
+ if (value == null || typeof value !== "object")
69164
+ return JSON.stringify(value);
69165
+ if (Array.isArray(value))
69166
+ return `[${value.map(stableJson).join(",")}]`;
69167
+ const obj = value;
69168
+ return `{${Object.keys(obj).sort().map((key2) => `${JSON.stringify(key2)}:${stableJson(obj[key2])}`).join(",")}}`;
69169
+ }
69170
+ function scheduleTriggerHash(t) {
69171
+ const canonical = {
69172
+ is_active: t.is_active,
69173
+ config: normalizeScheduleTriggerConfig(t.config),
69174
+ targets: t.targets.map((e2) => ({
69175
+ agent: e2.agent,
69176
+ description: e2.description,
69177
+ payload_schema: e2.payload_schema
69178
+ })).sort((a3, b4) => stableJson(a3) < stableJson(b4) ? -1 : 1)
69179
+ };
69180
+ return crypto5.createHash("sha256").update(stableJson(canonical)).digest("hex");
69181
+ }
69182
+ function scheduleTriggerLabel(t) {
69183
+ return formatScheduleTriggerLabel({
69184
+ nodeId: t.node_id,
69185
+ isActive: t.is_active,
69186
+ config: t.config,
69187
+ targets: t.targets.map((e2) => e2.agent)
69188
+ });
69189
+ }
69190
+ function cloudScheduleTriggers(cloud, slugForAgent) {
69191
+ return (cloud.triggers ?? []).filter((t) => t.trigger_type === "schedule").map((t) => ({
69192
+ node_id: t.node_id,
69193
+ is_active: t.is_active ?? false,
69194
+ config: t.config ?? {},
69195
+ targets: t.edges.map((e2) => ({
69196
+ agent: slugForAgent(e2.to_agent_id, e2.to_slug ?? e2.to_agent_id),
69197
+ description: e2.description ?? "",
69198
+ payload_schema: e2.payload_schema ?? {}
69199
+ }))
69200
+ }));
69201
+ }
69202
+ function manifestScheduleTriggers(manifest) {
69203
+ return (manifest?.triggers ?? []).filter((t) => t.type === "schedule").map((t) => ({
69204
+ node_id: t.node_id,
69205
+ is_active: t.is_active ?? false,
69206
+ config: t.config ?? {},
69207
+ targets: t.to.map((e2) => ({
69208
+ agent: e2.agent,
69209
+ description: e2.description ?? "",
69210
+ payload_schema: e2.payload_schema ?? {}
69211
+ }))
69212
+ }));
69213
+ }
69214
+ function decideScheduleTriggerReconcile(input) {
69215
+ const rows = threeWayDiff({
69216
+ local: input.local.map((t) => ({
69217
+ type: SCHEDULE_TRIGGER,
69218
+ slug: t.node_id,
69219
+ hash: scheduleTriggerHash(t)
69220
+ })),
69221
+ lock: (input.baseline ?? []).map((t) => ({
69222
+ type: SCHEDULE_TRIGGER,
69223
+ slug: t.node_id,
69224
+ hash: t.hash,
69225
+ installedPaths: []
69226
+ })),
69227
+ cloud: input.cloud.map((t) => ({
69228
+ type: SCHEDULE_TRIGGER,
69229
+ slug: t.node_id,
69230
+ hash: scheduleTriggerHash(t),
69231
+ files: []
69232
+ }))
69233
+ });
69234
+ return decideCollectionReconcile({
69235
+ rows,
69236
+ type: SCHEDULE_TRIGGER,
69237
+ claims: input.local.length > 0,
69238
+ force: input.force
69239
+ });
69240
+ }
69241
+ function syncedScheduleTriggers(triggers) {
69242
+ return triggers.map((t) => ({
69243
+ node_id: t.node_id,
69244
+ hash: scheduleTriggerHash(t)
69245
+ }));
69246
+ }
69247
+ var ORCH_META_FIELDS = [
69248
+ "name",
69249
+ "description",
69250
+ "icon",
69251
+ "icon_color",
69252
+ "credit_limit"
69253
+ ];
69254
+ function metaText(value) {
69255
+ if (value === undefined)
69256
+ return;
69257
+ return value == null ? "" : String(value);
69258
+ }
69259
+ function decideOrchestrationMeta(input) {
69260
+ const meta = input.manifest?.orchestration;
69261
+ if (!meta)
69262
+ return [];
69263
+ const out = [];
69264
+ for (const field of ORCH_META_FIELDS) {
69265
+ const local = metaText(meta[field]);
69266
+ const cloud = metaText(input.cloud[field]) ?? "";
69267
+ if (local === undefined)
69268
+ continue;
69269
+ if (local === cloud)
69270
+ continue;
69271
+ const base2 = input.baseline === undefined ? undefined : metaText(input.baseline[field]) ?? "";
69272
+ let kind;
69273
+ if (base2 === undefined) {
69274
+ kind = "conflict";
69275
+ } else if (local === base2) {
69276
+ kind = "keep";
69277
+ } else if (cloud === base2) {
69278
+ kind = "push";
69279
+ } else {
69280
+ kind = "conflict";
69281
+ }
69282
+ if (kind === "conflict" && input.force)
69283
+ kind = "push";
69284
+ out.push({ field, kind, local, cloud });
69285
+ }
69286
+ return out;
69287
+ }
69288
+ function nextOrchestrationMeta(input) {
69289
+ const kept = new Set(input.decisions.filter((d3) => d3.kind === "keep").map((d3) => d3.field));
69290
+ const out = {};
69291
+ for (const field of ORCH_META_FIELDS) {
69292
+ const value = kept.has(field) ? input.previous?.[field] : input.applied[field];
69293
+ if (value == null)
69294
+ continue;
69295
+ if (field === "credit_limit") {
69296
+ out.credit_limit = typeof value === "number" ? value : Number(value);
69297
+ } else {
69298
+ out[field] = String(value);
69299
+ }
69300
+ }
69301
+ return out;
69302
+ }
69303
+ function syncedOrchestrationMeta(cloud) {
69304
+ return nextOrchestrationMeta({
69305
+ applied: cloud,
69306
+ decisions: [],
69307
+ previous: undefined
69308
+ });
69309
+ }
69310
+
68632
69311
  // src/cli/orchestration-pull.ts
68633
69312
  function triggersForManifest(triggers, slugFor) {
68634
69313
  return triggers.map((trigger) => {
@@ -68656,7 +69335,7 @@ async function runOrchestrationPull(cwd2, args) {
68656
69335
  } else if (args.orchestrationId) {
68657
69336
  orchId = args.orchestrationId;
68658
69337
  } else {
68659
- f2.warn("This folder is not linked to any orchestration.");
69338
+ failWarn("This folder is not linked to any orchestration.");
68660
69339
  f2.info(`Run ${import_picocolors42.default.cyan("brainbase orchestration pull <id>")} with an orchestration id,
68661
69340
  or ${import_picocolors42.default.cyan("brainbase orchestration list")} to find one.`);
68662
69341
  return;
@@ -68714,13 +69393,15 @@ async function runOrchestrationPull(cwd2, args) {
68714
69393
  const fallbackHarness = args.harness ?? "claude-code";
68715
69394
  fs78.mkdirSync(cwd2, { recursive: true });
68716
69395
  if (hasOrchManifest(cwd2) && existingLink && existingLink.orchestration_id !== orchId) {
68717
- f2.error(`This folder is linked to orchestration ${existingLink.orchestration_id}, not ${orchId}. Move to a fresh directory or unlink first.`);
69396
+ fail(`This folder is linked to orchestration ${existingLink.orchestration_id}, not ${orchId}. Move to a fresh directory or unlink first.`);
68718
69397
  return;
68719
69398
  }
68720
69399
  const installedMembers = [];
69400
+ const missingMembers = [];
68721
69401
  for (const m3 of cloud.members) {
68722
69402
  if (!m3.manifest) {
68723
- f2.warn(`Skipping member ${m3.slug}: server did not return a manifest.`);
69403
+ missingMembers.push(m3.slug);
69404
+ failWarn(`Skipping member ${m3.slug}: server did not return a manifest.`);
68724
69405
  continue;
68725
69406
  }
68726
69407
  const slug = slugFor(m3.agent_id);
@@ -68755,7 +69436,8 @@ async function runOrchestrationPull(cwd2, args) {
68755
69436
  });
68756
69437
  } catch (err) {
68757
69438
  memberSp.stop(`Failed to install ${slug}.`);
68758
- f2.error(err.message);
69439
+ missingMembers.push(slug);
69440
+ fail(err.message);
68759
69441
  }
68760
69442
  }
68761
69443
  const manifestTriggers = triggersForManifest(cloud.triggers ?? [], slugFor);
@@ -68806,8 +69488,15 @@ async function runOrchestrationPull(cwd2, args) {
68806
69488
  to_slug: slugFor(e2.to_agent_id),
68807
69489
  description: e2.description ?? "",
68808
69490
  payload_schema: e2.payload_schema ?? {}
68809
- }))
69491
+ })),
69492
+ triggers: syncedScheduleTriggers(cloudScheduleTriggers(cloud, (agentId, fallback) => slugFor(agentId) || fallback)),
69493
+ meta: syncedOrchestrationMeta(cloud)
68810
69494
  });
69495
+ if (missingMembers.length > 0) {
69496
+ f2.warn(`${installedMembers.length} of ${cloud.members.length} members landed in ${path87.basename(cwd2)}/ and the orchestration is linked, but ${missingMembers.length === 1 ? "this member is" : "these members are"} missing: ${missingMembers.map((s3) => import_picocolors42.default.bold(s3)).join(", ")}. ${import_picocolors42.default.cyan("brainbase orchestration push")} will refuse until ${missingMembers.length === 1 ? "it" : "they"} ${missingMembers.length === 1 ? "is" : "are"} pulled, so re-run this after fixing the cause.`);
69497
+ $e(`Pulled ${cloud.name} at revision ${cloud.revision} into ${path87.basename(cwd2)}/ ${import_picocolors42.default.dim(`(${installedMembers.length}/${cloud.members.length} members — incomplete)`)}.`);
69498
+ return;
69499
+ }
68811
69500
  $e(`Pulled ${cloud.name} at revision ${cloud.revision} into ${path87.basename(cwd2)}/ ${import_picocolors42.default.dim(`(${installedMembers.length}/${cloud.members.length} members)`)}.`);
68812
69501
  }
68813
69502
  function handleApiError5(err) {
@@ -68890,6 +69579,11 @@ function buildOrchestrationGraphPayload(manifest, slugToAgentId) {
68890
69579
  }
68891
69580
 
68892
69581
  // src/cli/orchestration-push.ts
69582
+ function joinList(items) {
69583
+ if (items.length <= 1)
69584
+ return items[0] ?? "";
69585
+ return `${items.slice(0, -1).join(", ")} and ${items[items.length - 1]}`;
69586
+ }
68893
69587
  function findUnpushableMembers(cwd2, members) {
68894
69588
  const blocked = [];
68895
69589
  for (const m3 of members) {
@@ -68922,12 +69616,12 @@ async function runOrchestrationPush(cwd2, args) {
68922
69616
  banner("orchestration push — recursively push each member, then update the graph");
68923
69617
  const link2 = readOrchLink(cwd2);
68924
69618
  if (!link2) {
68925
- f2.warn("This folder is not linked to any orchestration.");
69619
+ failWarn("This folder is not linked to any orchestration.");
68926
69620
  f2.info(`Run ${import_picocolors43.default.cyan("brainbase orchestration pull <id>")} first.`);
68927
69621
  return;
68928
69622
  }
68929
69623
  if (!hasOrchManifest(cwd2)) {
68930
- f2.warn(`No ${import_picocolors43.default.bold(ORCH_MANIFEST_FILE)} here.`);
69624
+ failWarn(`No ${import_picocolors43.default.bold(ORCH_MANIFEST_FILE)} here.`);
68931
69625
  f2.info(`Run ${import_picocolors43.default.cyan("brainbase orchestration pull")} to materialise the manifest before pushing.`);
68932
69626
  return;
68933
69627
  }
@@ -68972,6 +69666,95 @@ async function runOrchestrationPush(cwd2, args) {
68972
69666
  return;
68973
69667
  }
68974
69668
  }
69669
+ const lock = readOrchSyncState(cwd2);
69670
+ const fetchSpinner = de();
69671
+ fetchSpinner.start("Fetching cloud state…");
69672
+ let cloud;
69673
+ try {
69674
+ cloud = await api.getOrchestration(link2.orchestration_id);
69675
+ fetchSpinner.stop(`Cloud revision ${cloud.revision}.`);
69676
+ } catch (err) {
69677
+ fetchSpinner.stop("Failed.");
69678
+ handleApiError6(err);
69679
+ process.exitCode = 1;
69680
+ return;
69681
+ }
69682
+ const agentIdToSlug = new Map([...slugToAgentId.entries()].map(([slug, id]) => [id, slug]));
69683
+ const slugForAgent = (agentId, fallback) => agentIdToSlug.get(agentId) ?? fallback;
69684
+ const localTriggers = manifestScheduleTriggers(manifest);
69685
+ const cloudTriggers = cloudScheduleTriggers(cloud, slugForAgent);
69686
+ const triggerDecision = decideScheduleTriggerReconcile({
69687
+ local: localTriggers,
69688
+ cloud: cloudTriggers,
69689
+ baseline: lock?.triggers,
69690
+ force: !!args.force
69691
+ });
69692
+ const triggerByNode = new Map([...cloudTriggers, ...localTriggers].map((t) => [t.node_id, t]));
69693
+ const labelFor = (nodeId) => {
69694
+ const t = triggerByNode.get(nodeId);
69695
+ return t ? scheduleTriggerLabel(t) : nodeId;
69696
+ };
69697
+ const metaDecisions = decideOrchestrationMeta({
69698
+ manifest,
69699
+ cloud,
69700
+ baseline: lock?.meta,
69701
+ force: !!args.force
69702
+ });
69703
+ const metaConflicts = metaDecisions.filter((d3) => d3.kind === "conflict");
69704
+ const metaOverwrites = metaDecisions.filter((d3) => d3.kind === "push");
69705
+ const metaKept = metaDecisions.filter((d3) => d3.kind === "keep");
69706
+ const blockedTriggers = triggerDecision.kind === "blocked" ? triggerDecision : null;
69707
+ if (blockedTriggers || metaConflicts.length > 0) {
69708
+ const pullTargets = [];
69709
+ const forceEffects = [];
69710
+ if (blockedTriggers) {
69711
+ const { unseen, cloudModified, staleArchives } = blockedTriggers;
69712
+ f2.error(unseen.length > 0 ? "Cannot push: the cloud has schedule triggers this folder has never seen." : "Cannot push: schedule triggers changed both locally and in the cloud.");
69713
+ for (const nodeId of unseen) {
69714
+ console.error(` ${import_picocolors43.default.red("!")} ${import_picocolors43.default.bold(labelFor(nodeId))} ${import_picocolors43.default.dim("(only in the cloud — pushing would delete it)")}`);
69715
+ }
69716
+ for (const nodeId of cloudModified) {
69717
+ console.error(` ${import_picocolors43.default.red("!")} ${import_picocolors43.default.bold(labelFor(nodeId))} ${import_picocolors43.default.dim("(edited in the cloud — pushing would overwrite that edit)")}`);
69718
+ }
69719
+ for (const nodeId of staleArchives) {
69720
+ console.error(` ${import_picocolors43.default.red("!")} ${import_picocolors43.default.bold(labelFor(nodeId))} ${import_picocolors43.default.dim("(deleted here, edited in the cloud since — pushing would delete that edit)")}`);
69721
+ }
69722
+ const total = unseen.length + cloudModified.length + staleArchives.length;
69723
+ pullTargets.push(`${total} schedule trigger${total === 1 ? "" : "s"}`);
69724
+ forceEffects.push("make your local schedule triggers authoritative (deleting the cloud ones above)");
69725
+ }
69726
+ if (metaConflicts.length > 0) {
69727
+ f2.error("Cannot push: orchestration metadata changed both locally and in the cloud.");
69728
+ for (const d3 of metaConflicts) {
69729
+ console.error(` ${import_picocolors43.default.red("!")} ${import_picocolors43.default.bold(d3.field)} ${import_picocolors43.default.dim(`(local "${d3.local}" vs cloud "${d3.cloud}")`)}`);
69730
+ }
69731
+ if (!lock?.meta) {
69732
+ f2.info("This checkout has no metadata baseline, so the CLI cannot tell which side changed.");
69733
+ }
69734
+ const fields = metaConflicts.map((d3) => d3.field);
69735
+ pullTargets.push(`${fields.length} metadata field${fields.length === 1 ? "" : "s"}`);
69736
+ forceEffects.push(`overwrite the cloud's ${joinList(fields)} with the value${fields.length === 1 ? "" : "s"} in ${ORCH_MANIFEST_FILE}`);
69737
+ }
69738
+ f2.info(`Run ${import_picocolors43.default.cyan("brainbase orchestration pull")} to bring the cloud's ${joinList(pullTargets)} into ${import_picocolors43.default.bold(ORCH_MANIFEST_FILE)}, then push.`);
69739
+ f2.info(`Or ${import_picocolors43.default.cyan("brainbase orchestration push --force")} to ${joinList(forceEffects)}.${forceEffects.length > 1 ? " One flag, both of those — it settles everything listed above, not whichever conflict you had in mind." : ""}`);
69740
+ process.exitCode = 1;
69741
+ return;
69742
+ }
69743
+ if (triggerDecision.kind === "skip") {
69744
+ graph.triggers = cloudTriggers.map((t) => ({
69745
+ node_id: t.node_id,
69746
+ type: "schedule",
69747
+ is_active: t.is_active,
69748
+ config: t.config,
69749
+ edges: t.targets.map((e2) => ({
69750
+ to_agent_id: slugToAgentId.get(e2.agent) ?? e2.agent,
69751
+ description: e2.description,
69752
+ payload_schema: e2.payload_schema
69753
+ }))
69754
+ }));
69755
+ f2.warn(`${cloudTriggers.length} cloud schedule trigger${cloudTriggers.length === 1 ? "" : "s"} ${cloudTriggers.length === 1 ? "is" : "are"} not in ${import_picocolors43.default.bold(ORCH_MANIFEST_FILE)} and will be left untouched. Run ${import_picocolors43.default.cyan("brainbase orchestration pull")} to manage ${cloudTriggers.length === 1 ? "it" : "them"} from here.`);
69756
+ }
69757
+ const triggerDeletes = triggerDecision.kind === "reconcile" ? triggerDecision.archives : [];
68975
69758
  const plan = [""];
68976
69759
  plan.push(` ${import_picocolors43.default.bold(link2.name)} ${import_picocolors43.default.dim(`(${link2.orchestration_id})`)}`);
68977
69760
  plan.push(` ${import_picocolors43.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"}`)}`);
@@ -68983,12 +69766,34 @@ async function runOrchestrationPush(cwd2, args) {
68983
69766
  }
68984
69767
  plan.push("");
68985
69768
  }
69769
+ if (triggerDeletes.length) {
69770
+ plan.push(` ${import_picocolors43.default.bold("schedule triggers this push will delete")} ${import_picocolors43.default.dim(`(${triggerDeletes.length})`)}`);
69771
+ for (const nodeId of triggerDeletes) {
69772
+ plan.push(` ${import_picocolors43.default.yellow("⨯")} ${labelFor(nodeId)}`);
69773
+ }
69774
+ plan.push("");
69775
+ }
69776
+ if (metaOverwrites.length) {
69777
+ plan.push(` ${import_picocolors43.default.bold("orchestration metadata this push will overwrite")}`);
69778
+ for (const m3 of metaOverwrites) {
69779
+ plan.push(` ${import_picocolors43.default.yellow("⤒")} ${import_picocolors43.default.bold(m3.field)} ${import_picocolors43.default.dim(`cloud "${m3.cloud}" → local "${m3.local}"`)}`);
69780
+ }
69781
+ plan.push("");
69782
+ }
69783
+ if (metaKept.length) {
69784
+ plan.push(` ${import_picocolors43.default.bold("orchestration metadata changed in the cloud")}`);
69785
+ for (const m3 of metaKept) {
69786
+ plan.push(` ${import_picocolors43.default.cyan("=")} ${import_picocolors43.default.bold(m3.field)} ${import_picocolors43.default.dim(`keeping cloud "${m3.cloud}"; ${import_picocolors43.default.bold(ORCH_MANIFEST_FILE)} still has "${m3.local}"`)}`);
69787
+ }
69788
+ plan.push(` ${import_picocolors43.default.dim(`run ${import_picocolors43.default.cyan("brainbase orchestration pull")} to bring ${metaKept.length === 1 ? "it" : "them"} into the file`)}`);
69789
+ plan.push("");
69790
+ }
68986
69791
  console.log(plan.join(`
68987
69792
  `));
68988
69793
  if (!autoProceed(args.yes)) {
68989
69794
  const ok = await se({
68990
- message: args.graphOnly ? "Push graph (members, edges + triggers) only?" : "Push each member, then update the graph?",
68991
- initialValue: true
69795
+ message: triggerDeletes.length ? `Push, deleting ${triggerDeletes.length} schedule trigger${triggerDeletes.length === 1 ? "" : "s"}?` : args.graphOnly ? "Push graph (members, edges + triggers) only?" : "Push each member, then update the graph?",
69796
+ initialValue: triggerDeletes.length === 0
68992
69797
  });
68993
69798
  if (!ensureNotCancelled(ok)) {
68994
69799
  $e("Aborted.");
@@ -69015,16 +69820,22 @@ async function runOrchestrationPush(cwd2, args) {
69015
69820
  }
69016
69821
  }
69017
69822
  }
69823
+ const keptFields = new Set(metaKept.map((d3) => d3.field));
69824
+ const metaField = (field) => {
69825
+ const value = manifest.orchestration[field];
69826
+ if (value === undefined || keptFields.has(field))
69827
+ return {};
69828
+ return { [field]: value };
69829
+ };
69018
69830
  const sp = de();
69019
69831
  sp.start("Updating orchestration graph…");
69020
- const lock = readOrchSyncState(cwd2);
69021
69832
  try {
69022
69833
  const updated = await api.updateOrchestration(link2.orchestration_id, {
69023
- name: manifest.orchestration.name,
69024
- description: manifest.orchestration.description ?? "",
69025
- icon: manifest.orchestration.icon,
69026
- icon_color: manifest.orchestration.icon_color,
69027
- credit_limit: manifest.orchestration.credit_limit,
69834
+ ...metaField("name"),
69835
+ ...metaField("description"),
69836
+ ...metaField("icon"),
69837
+ ...metaField("icon_color"),
69838
+ ...metaField("credit_limit"),
69028
69839
  members: graph.memberIds,
69029
69840
  edges: graph.edges,
69030
69841
  triggers: graph.triggers,
@@ -69046,7 +69857,13 @@ async function runOrchestrationPush(cwd2, args) {
69046
69857
  to_slug: e2.to_slug ?? e2.to_agent_id,
69047
69858
  description: e2.description ?? "",
69048
69859
  payload_schema: e2.payload_schema ?? {}
69049
- }))
69860
+ })),
69861
+ triggers: triggerDecision.kind === "skip" ? lock?.triggers ?? [] : syncedScheduleTriggers(cloudScheduleTriggers(updated, slugForAgent)),
69862
+ meta: nextOrchestrationMeta({
69863
+ applied: updated,
69864
+ decisions: metaDecisions,
69865
+ previous: lock?.meta
69866
+ })
69050
69867
  });
69051
69868
  $e(`Pushed ${link2.name} at revision ${updated.revision}.`);
69052
69869
  } catch (err) {
@@ -69078,7 +69895,7 @@ async function runOrchestrationStatus(cwd2) {
69078
69895
  banner("orchestration status — what changed locally, remotely, both");
69079
69896
  const link2 = readOrchLink(cwd2);
69080
69897
  if (!link2) {
69081
- f2.warn("This folder is not linked to any orchestration.");
69898
+ failWarn("This folder is not linked to any orchestration.");
69082
69899
  f2.info(`Run ${import_picocolors44.default.cyan("brainbase orchestration pull <id>")} first.`);
69083
69900
  return;
69084
69901
  }
@@ -69126,8 +69943,8 @@ async function runOrchestrationStatus(cwd2) {
69126
69943
  }
69127
69944
  lines.push("");
69128
69945
  }
69129
- const cloudEdgeKey = (e2) => `${slugForAgent(e2.from_agent_id, e2.from_slug ?? e2.from_agent_id)}->${slugForAgent(e2.to_agent_id, e2.to_slug ?? e2.to_agent_id)}|${e2.description ?? ""}|${stableJson(e2.payload_schema ?? {})}|${stableJson(e2.settings ?? {})}`;
69130
- const localEdgeKey = (e2) => `${e2.from}->${e2.to}|${e2.description ?? ""}|${stableJson(e2.payload_schema ?? {})}|${stableJson(e2.settings ?? {})}`;
69946
+ const cloudEdgeKey = (e2) => `${slugForAgent(e2.from_agent_id, e2.from_slug ?? e2.from_agent_id)}->${slugForAgent(e2.to_agent_id, e2.to_slug ?? e2.to_agent_id)}|${e2.description ?? ""}|${stableJson2(e2.payload_schema ?? {})}|${stableJson2(e2.settings ?? {})}`;
69947
+ const localEdgeKey = (e2) => `${e2.from}->${e2.to}|${e2.description ?? ""}|${stableJson2(e2.payload_schema ?? {})}|${stableJson2(e2.settings ?? {})}`;
69131
69948
  const cloudEdges = new Map;
69132
69949
  for (const e2 of cloud.edges)
69133
69950
  cloudEdges.set(cloudEdgeKey(e2), true);
@@ -69144,46 +69961,74 @@ async function runOrchestrationStatus(cwd2) {
69144
69961
  lines.push(` ${import_picocolors44.default.cyan("← pull")} added on cloud: ${k3}`);
69145
69962
  lines.push("");
69146
69963
  }
69147
- const cloudTriggerKey = (t) => {
69148
- const edges = t.edges.map((e2) => `${slugForAgent(e2.to_agent_id, e2.to_slug ?? e2.to_agent_id)}|${e2.description ?? ""}|${stableJson(e2.payload_schema ?? {})}`).sort().join(",");
69149
- return `${t.node_id}|${t.is_active ?? false}|${stableJson(normalizeScheduleTriggerConfig(t.config))}|${edges}`;
69150
- };
69151
- const cloudTriggerLabel = (t) => formatScheduleTriggerLabel({
69152
- nodeId: t.node_id,
69153
- isActive: t.is_active ?? false,
69154
- config: t.config,
69155
- targets: t.edges.map((e2) => slugForAgent(e2.to_agent_id, e2.to_slug ?? e2.to_agent_id))
69964
+ const metaDecisions = decideOrchestrationMeta({
69965
+ manifest: localManifest,
69966
+ cloud,
69967
+ baseline: lock?.meta,
69968
+ force: false
69156
69969
  });
69157
- const localTriggerKey = (t) => {
69158
- const edges = t.to.map((e2) => `${e2.agent}|${e2.description ?? ""}|${stableJson(e2.payload_schema ?? {})}`).sort().join(",");
69159
- return `${t.node_id}|${t.is_active ?? false}|${stableJson(normalizeScheduleTriggerConfig(t.config))}|${edges}`;
69970
+ if (metaDecisions.length) {
69971
+ lines.push(` ${import_picocolors44.default.bold("orchestration metadata")}`);
69972
+ for (const m3 of metaDecisions) {
69973
+ if (m3.kind === "push") {
69974
+ lines.push(` ${import_picocolors44.default.yellow("→ push")} ${import_picocolors44.default.bold(m3.field)}: cloud ${import_picocolors44.default.dim(`"${m3.cloud}"`)} → local ${import_picocolors44.default.dim(`"${m3.local}"`)}`);
69975
+ } else if (m3.kind === "keep") {
69976
+ lines.push(` ${import_picocolors44.default.cyan("← pull")} ${import_picocolors44.default.bold(m3.field)}: changed in the cloud to ${import_picocolors44.default.dim(`"${m3.cloud}"`)}; push leaves it alone ${import_picocolors44.default.dim(`(yaml still has "${m3.local}")`)}`);
69977
+ } else {
69978
+ lines.push(` ${import_picocolors44.default.red("! conflict")} ${import_picocolors44.default.bold(m3.field)}: local ${import_picocolors44.default.dim(`"${m3.local}"`)} vs cloud ${import_picocolors44.default.dim(`"${m3.cloud}"`)} — push refuses until you pull or ${import_picocolors44.default.cyan("--force")}`);
69979
+ }
69980
+ }
69981
+ lines.push(` ${import_picocolors44.default.dim("fields the manifest omits, and fields only the cloud changed, are left alone")}`);
69982
+ lines.push("");
69983
+ }
69984
+ const cloudTriggers = cloudScheduleTriggers(cloud, slugForAgent);
69985
+ const localTriggers = manifestScheduleTriggers(localManifest);
69986
+ const triggerPlan = decideScheduleTriggerReconcile({
69987
+ local: localTriggers,
69988
+ cloud: cloudTriggers,
69989
+ baseline: lock?.triggers,
69990
+ force: false
69991
+ });
69992
+ const triggerByNode = new Map([...cloudTriggers, ...localTriggers].map((t) => [t.node_id, t]));
69993
+ const triggerLabel = (nodeId) => {
69994
+ const t = triggerByNode.get(nodeId);
69995
+ return t ? scheduleTriggerLabel(t) : nodeId;
69160
69996
  };
69161
- const localTriggerLabel = (t) => formatScheduleTriggerLabel({
69162
- nodeId: t.node_id,
69163
- isActive: t.is_active ?? false,
69164
- config: t.config,
69165
- targets: t.to.map((e2) => e2.agent)
69997
+ const localNodes = new Set(localTriggers.map((t) => t.node_id));
69998
+ const cloudByNode = new Map(cloudTriggers.map((t) => [t.node_id, t]));
69999
+ const triggersToPush = localTriggers.filter((t) => {
70000
+ const remote = cloudByNode.get(t.node_id);
70001
+ return !remote || scheduleTriggerHash(remote) !== scheduleTriggerHash(t);
69166
70002
  });
69167
- const cloudTriggers = new Map;
69168
- for (const t of cloud.triggers ?? []) {
69169
- if (t.trigger_type === "schedule") {
69170
- cloudTriggers.set(cloudTriggerKey(t), cloudTriggerLabel(t));
70003
+ const triggersToPull = cloudTriggers.filter((t) => !localNodes.has(t.node_id));
70004
+ const triggerDeletes = triggerPlan.kind === "reconcile" ? triggerPlan.archives : [];
70005
+ if (triggersToPush.length || triggersToPull.length) {
70006
+ lines.push(` ${import_picocolors44.default.bold("schedule triggers")}`);
70007
+ for (const t of triggersToPush) {
70008
+ lines.push(` ${import_picocolors44.default.yellow("→ push")} added/changed in yaml: ${scheduleTriggerLabel(t)}`);
69171
70009
  }
69172
- }
69173
- const localTriggers = new Map;
69174
- for (const t of localManifest?.triggers ?? []) {
69175
- if (t.type === "schedule") {
69176
- localTriggers.set(localTriggerKey(t), localTriggerLabel(t));
70010
+ for (const t of triggersToPull) {
70011
+ const doomed = triggerDeletes.includes(t.node_id);
70012
+ lines.push(doomed ? ` ${import_picocolors44.default.red("⨯ push deletes")} ${scheduleTriggerLabel(t)}` : ` ${import_picocolors44.default.cyan("← pull")} only on cloud: ${scheduleTriggerLabel(t)}`);
69177
70013
  }
70014
+ lines.push("");
69178
70015
  }
69179
- const triggersAdded = [...localTriggers.keys()].filter((k3) => !cloudTriggers.has(k3));
69180
- const triggersRemoved = [...cloudTriggers.keys()].filter((k3) => !localTriggers.has(k3));
69181
- if (triggersAdded.length || triggersRemoved.length) {
69182
- lines.push(` ${import_picocolors44.default.bold("schedule triggers")}`);
69183
- for (const k3 of triggersAdded)
69184
- lines.push(` ${import_picocolors44.default.yellow("→ push")} added/changed in yaml: ${localTriggers.get(k3) ?? k3}`);
69185
- for (const k3 of triggersRemoved)
69186
- lines.push(` ${import_picocolors44.default.cyan("← pull")} added/changed on cloud: ${cloudTriggers.get(k3) ?? k3}`);
70016
+ if (triggerPlan.kind === "blocked") {
70017
+ lines.push(` ${import_picocolors44.default.bold(import_picocolors44.default.red("schedule triggers: push blocked"))}`);
70018
+ for (const nodeId of triggerPlan.unseen) {
70019
+ lines.push(` ${import_picocolors44.default.red("!")} ${triggerLabel(nodeId)} ${import_picocolors44.default.dim("— only in the cloud; pushing would delete it")}`);
70020
+ }
70021
+ for (const nodeId of triggerPlan.cloudModified) {
70022
+ lines.push(` ${import_picocolors44.default.red("!")} ${triggerLabel(nodeId)} ${import_picocolors44.default.dim("— edited in the cloud; pushing would overwrite that edit")}`);
70023
+ }
70024
+ for (const nodeId of triggerPlan.staleArchives) {
70025
+ lines.push(` ${import_picocolors44.default.red("!")} ${triggerLabel(nodeId)} ${import_picocolors44.default.dim("— deleted here, edited in the cloud since; pushing would delete that edit")}`);
70026
+ }
70027
+ lines.push(` ${import_picocolors44.default.dim("run")} ${import_picocolors44.default.cyan("brainbase orchestration pull")} ${import_picocolors44.default.dim("first")}`);
70028
+ lines.push("");
70029
+ } else if (triggerPlan.kind === "skip") {
70030
+ const left = untouchedRows(triggerPlan);
70031
+ lines.push(` ${import_picocolors44.default.dim(`${left.length} cloud schedule trigger${left.length === 1 ? "" : "s"} not managed by this manifest — push leaves ${left.length === 1 ? "it" : "them"} untouched`)}`);
69187
70032
  lines.push("");
69188
70033
  }
69189
70034
  const lockByAgentId = new Map((lock?.members ?? []).map((m3) => [m3.agent_id, m3]));
@@ -69219,7 +70064,7 @@ async function runOrchestrationStatus(cwd2) {
69219
70064
  lines.push(` ${import_picocolors44.default.cyan("← pull")} cloud changed since last pull: ${import_picocolors44.default.dim(`lock ${lock.revision} → cloud ${cloud.revision}`)}`);
69220
70065
  lines.push("");
69221
70066
  }
69222
- if (!membersAdded.length && !membersRemoved.length && !edgesAdded.length && !edgesRemoved.length && !triggersAdded.length && !triggersRemoved.length && !memberDrift.length && !revisionDrift) {
70067
+ if (!membersAdded.length && !membersRemoved.length && !edgesAdded.length && !edgesRemoved.length && !triggersToPush.length && !triggersToPull.length && !metaDecisions.length && !memberDrift.length && !revisionDrift) {
69223
70068
  lines.push(` ${import_picocolors44.default.green("✓")} everything is in sync`);
69224
70069
  lines.push("");
69225
70070
  console.log(lines.join(`
@@ -69231,13 +70076,13 @@ async function runOrchestrationStatus(cwd2) {
69231
70076
  console.log(lines.join(`
69232
70077
  `));
69233
70078
  }
69234
- function stableJson(value) {
70079
+ function stableJson2(value) {
69235
70080
  if (value == null || typeof value !== "object")
69236
70081
  return JSON.stringify(value);
69237
70082
  if (Array.isArray(value))
69238
- return `[${value.map(stableJson).join(",")}]`;
70083
+ return `[${value.map(stableJson2).join(",")}]`;
69239
70084
  const obj = value;
69240
- return `{${Object.keys(obj).sort().map((key2) => `${JSON.stringify(key2)}:${stableJson(obj[key2])}`).join(",")}}`;
70085
+ return `{${Object.keys(obj).sort().map((key2) => `${JSON.stringify(key2)}:${stableJson2(obj[key2])}`).join(",")}}`;
69241
70086
  }
69242
70087
 
69243
70088
  // src/cli/orchestration-list.ts
@@ -69343,7 +70188,7 @@ async function runOrchestrationAddAgent(cwd2, args) {
69343
70188
  banner("orchestration add-agent — create a member agent and wire it into the graph");
69344
70189
  const link2 = readOrchLink(cwd2);
69345
70190
  if (!link2 || !hasOrchManifest(cwd2)) {
69346
- f2.warn("This folder is not a linked orchestration.");
70191
+ failWarn("This folder is not a linked orchestration.");
69347
70192
  f2.info(`Run ${import_picocolors46.default.cyan("brainbase orchestration pull <id>")} first.`);
69348
70193
  return;
69349
70194
  }
@@ -69351,7 +70196,7 @@ async function runOrchestrationAddAgent(cwd2, args) {
69351
70196
  try {
69352
70197
  manifest = readOrchManifest(cwd2);
69353
70198
  } catch (err) {
69354
- f2.error(err.message);
70199
+ fail(err.message);
69355
70200
  return;
69356
70201
  }
69357
70202
  let name = args.name?.trim();
@@ -69377,7 +70222,7 @@ async function runOrchestrationAddAgent(cwd2, args) {
69377
70222
  try {
69378
70223
  payloadSchema = parseEdgeSchema(args.schema);
69379
70224
  } catch (err) {
69380
- f2.error(err.message);
70225
+ fail(err.message);
69381
70226
  return;
69382
70227
  }
69383
70228
  let orgId = args.orgId;
@@ -69394,14 +70239,14 @@ async function runOrchestrationAddAgent(cwd2, args) {
69394
70239
  const resolved = resolveOrgIdForGroup(link2.group_id, orgsWithTeams);
69395
70240
  if (!resolved) {
69396
70241
  sp.stop("Failed.");
69397
- f2.error(`Could not find an org that owns group ${import_picocolors46.default.bold(link2.group_id)}. ` + `Pass ${import_picocolors46.default.cyan("--org <id>")} explicitly.`);
70242
+ fail(`Could not find an org that owns group ${import_picocolors46.default.bold(link2.group_id)}. ` + `Pass ${import_picocolors46.default.cyan("--org <id>")} explicitly.`);
69398
70243
  return;
69399
70244
  }
69400
70245
  orgId = resolved;
69401
70246
  sp.stop("Resolved org.");
69402
70247
  } catch (err) {
69403
70248
  sp.stop("Failed.");
69404
- f2.error(err.message);
70249
+ fail(err.message);
69405
70250
  return;
69406
70251
  }
69407
70252
  }
@@ -69434,7 +70279,7 @@ async function runOrchestrationAddAgent(cwd2, args) {
69434
70279
  try {
69435
70280
  updated = mergeMemberAndEdges(manifest, { slug, name, from, to: to2, description, payloadSchema });
69436
70281
  } catch (err) {
69437
- f2.error(err.message);
70282
+ fail(err.message);
69438
70283
  return;
69439
70284
  }
69440
70285
  const dest = memberDir(cwd2, slug);
@@ -69452,7 +70297,7 @@ async function runOrchestrationAddAgent(cwd2, args) {
69452
70297
  try {
69453
70298
  fs79.rmSync(dest, { recursive: true, force: true });
69454
70299
  } catch {}
69455
- f2.error(`Failed to create ${slug}: ${err.message}`);
70300
+ fail(`Failed to create ${slug}: ${err.message}`);
69456
70301
  return;
69457
70302
  }
69458
70303
  writeOrchManifest(cwd2, updated);
@@ -69468,12 +70313,12 @@ var import_picocolors47 = __toESM(require_picocolors(), 1);
69468
70313
  async function runOrchestrationCreate(cwd2, args) {
69469
70314
  banner("orchestration create — claim a brainbase-orchestration.yaml");
69470
70315
  if (readOrchLink(cwd2)) {
69471
- f2.warn("This folder is already linked to an orchestration.");
70316
+ failWarn("This folder is already linked to an orchestration.");
69472
70317
  f2.info(`Run ${import_picocolors47.default.cyan("brainbase orchestration push")} to update it.`);
69473
70318
  return;
69474
70319
  }
69475
70320
  if (!hasOrchManifest(cwd2)) {
69476
- f2.warn(`No ${import_picocolors47.default.bold(ORCH_MANIFEST_FILE)} here.`);
70321
+ failWarn(`No ${import_picocolors47.default.bold(ORCH_MANIFEST_FILE)} here.`);
69477
70322
  f2.info(`Create one, or pull an existing orchestration first.`);
69478
70323
  return;
69479
70324
  }
@@ -69612,7 +70457,8 @@ async function runOrchestration(cwd2, sub, args, opts) {
69612
70457
  case "push":
69613
70458
  await runOrchestrationPush(cwd2, {
69614
70459
  yes: opts.yes,
69615
- graphOnly: opts.graphOnly
70460
+ graphOnly: opts.graphOnly,
70461
+ force: opts.force
69616
70462
  });
69617
70463
  return;
69618
70464
  case "status":
@@ -69664,7 +70510,7 @@ function printHelp3() {
69664
70510
  out.push(` ${import_picocolors48.default.bold("Flags")}`);
69665
70511
  out.push(` ${import_picocolors48.default.dim("--yes, -y")} skip confirmations`);
69666
70512
  out.push(` ${import_picocolors48.default.dim("--harness <id>")} harness for newly-created member folders (default claude-code)`);
69667
- out.push(` ${import_picocolors48.default.dim("--graph-only")} for push: only update members + edges, skip per-member push`);
70513
+ out.push(` ${import_picocolors48.default.dim("--graph-only")} for push: only update members + edges, skip per-member push`, ` ${import_picocolors48.default.dim("--force")} for push: settle every conflict in your file's favour — deletes cloud schedule`, ` triggers this folder has never seen, and overwrites cloud-side edits to`, ` name, description, icon, icon_color and credit_limit`);
69668
70514
  out.push(` ${import_picocolors48.default.dim("--org <id>")} for create/list: org id or slug (CLI vocab — DB teams.id)`);
69669
70515
  out.push(` ${import_picocolors48.default.dim("--team <id>")} for create/list: team id (CLI vocab — DB groups.id)`);
69670
70516
  out.push("");
@@ -70467,7 +71313,10 @@ async function runTokenRevoke(args) {
70467
71313
  }
70468
71314
  const stored = readToken();
70469
71315
  const isLocalToken = Boolean(stored && stored.token.startsWith(target.prefix));
70470
- if (!autoProceed(args.yes)) {
71316
+ if (!autoProceedDestructive(args.yes, {
71317
+ action: `Revoking ${target.name} (${args.id})`,
71318
+ flagHint: "Pass --yes to revoke it without a prompt."
71319
+ })) {
70471
71320
  const ok = await se({
70472
71321
  message: isLocalToken ? `Revoke ${import_picocolors50.default.bold(target.name)} (${args.id})? This is the token this CLI is using, so it will stop working here too.` : `Revoke ${import_picocolors50.default.bold(target.name)} (${args.id})? CIs and machines using it will stop working.`,
70473
71322
  initialValue: false
@@ -77104,10 +77953,10 @@ function createFetchWithInit(baseFetch = fetch, baseInit) {
77104
77953
  }
77105
77954
 
77106
77955
  // node_modules/pkce-challenge/dist/index.node.js
77107
- var crypto5;
77108
- crypto5 = globalThis.crypto?.webcrypto ?? globalThis.crypto ?? import("node:crypto").then((m3) => m3.webcrypto);
77956
+ var crypto6;
77957
+ crypto6 = globalThis.crypto?.webcrypto ?? globalThis.crypto ?? import("node:crypto").then((m3) => m3.webcrypto);
77109
77958
  async function getRandomValues(size2) {
77110
- return (await crypto5).getRandomValues(new Uint8Array(size2));
77959
+ return (await crypto6).getRandomValues(new Uint8Array(size2));
77111
77960
  }
77112
77961
  async function random2(size2) {
77113
77962
  const mask = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~";
@@ -77127,7 +77976,7 @@ async function generateVerifier(length) {
77127
77976
  return await random2(length);
77128
77977
  }
77129
77978
  async function generateChallenge(code_verifier) {
77130
- const buffer = await (await crypto5).subtle.digest("SHA-256", new TextEncoder().encode(code_verifier));
77979
+ const buffer = await (await crypto6).subtle.digest("SHA-256", new TextEncoder().encode(code_verifier));
77131
77980
  return btoa(String.fromCharCode(...new Uint8Array(buffer))).replace(/\//g, "_").replace(/\+/g, "-").replace(/=/g, "");
77132
77981
  }
77133
77982
  async function pkceChallenge(length) {
@@ -79169,10 +80018,99 @@ async function runMcp(cwd2, sub, _argv, options) {
79169
80018
  }
79170
80019
 
79171
80020
  // src/cli/task.ts
79172
- var import_picocolors52 = __toESM(require_picocolors(), 1);
80021
+ var import_picocolors56 = __toESM(require_picocolors(), 1);
79173
80022
 
79174
80023
  // src/cli/task-create.ts
80024
+ var import_picocolors52 = __toESM(require_picocolors(), 1);
79175
80025
  import { randomUUID as randomUUID2 } from "node:crypto";
80026
+
80027
+ // src/cli/task-status.ts
80028
+ var TERMINAL_TASK_STATUSES = new Set([
80029
+ "success",
80030
+ "fail",
80031
+ "need_more_info"
80032
+ ]);
80033
+ var SUCCESS_STATUS = "success";
80034
+ function isTerminalTaskStatus(status) {
80035
+ return TERMINAL_TASK_STATUSES.has(status);
80036
+ }
80037
+ function exitCodeForTaskStatus(status) {
80038
+ return status === SUCCESS_STATUS ? 0 : 1;
80039
+ }
80040
+ var SIGINT_EXIT_CODE = 130;
80041
+ async function waitForTask(taskId, options) {
80042
+ const stop = new AbortController;
80043
+ const onExternalAbort = () => stop.abort();
80044
+ options.signal?.addEventListener("abort", onExternalAbort, { once: true });
80045
+ if (options.signal?.aborted)
80046
+ stop.abort();
80047
+ const deadlineTimer = options.timeoutMs === undefined ? null : setTimeout(() => stop.abort(), options.timeoutMs);
80048
+ let latest = null;
80049
+ let lastStatus = null;
80050
+ const givenUp = () => options.signal?.aborted ? { kind: "interrupted", task: latest } : { kind: "timeout", task: latest };
80051
+ try {
80052
+ while (true) {
80053
+ if (stop.signal.aborted)
80054
+ return givenUp();
80055
+ const polled = await raceAbort(masApi.getTask(taskId, { signal: stop.signal }), stop.signal);
80056
+ if (polled.kind === "aborted")
80057
+ return givenUp();
80058
+ if (polled.kind === "failed") {
80059
+ if (stop.signal.aborted)
80060
+ return givenUp();
80061
+ throw polled.error;
80062
+ }
80063
+ latest = polled.value;
80064
+ if (latest.status !== lastStatus) {
80065
+ lastStatus = latest.status;
80066
+ options.onStatus?.(latest);
80067
+ }
80068
+ if (isTerminalTaskStatus(latest.status)) {
80069
+ return { kind: "terminal", task: latest };
80070
+ }
80071
+ const cutShort = await sleep2(options.intervalMs, stop.signal);
80072
+ if (cutShort)
80073
+ return givenUp();
80074
+ }
80075
+ } finally {
80076
+ if (deadlineTimer !== null)
80077
+ clearTimeout(deadlineTimer);
80078
+ options.signal?.removeEventListener("abort", onExternalAbort);
80079
+ }
80080
+ }
80081
+ function raceAbort(work, signal) {
80082
+ const settled = work.then((value) => ({ kind: "value", value }), (error2) => ({ kind: "failed", error: error2 }));
80083
+ if (signal.aborted) {
80084
+ settled.catch(() => {});
80085
+ return Promise.resolve({ kind: "aborted" });
80086
+ }
80087
+ return new Promise((resolve) => {
80088
+ const onAbort = () => resolve({ kind: "aborted" });
80089
+ signal.addEventListener("abort", onAbort, { once: true });
80090
+ settled.then((outcome) => {
80091
+ signal.removeEventListener("abort", onAbort);
80092
+ resolve(outcome);
80093
+ });
80094
+ });
80095
+ }
80096
+ function sleep2(ms2, signal) {
80097
+ if (signal.aborted)
80098
+ return Promise.resolve(true);
80099
+ return new Promise((resolve) => {
80100
+ const onAbort = () => {
80101
+ clearTimeout(timer);
80102
+ resolve(true);
80103
+ };
80104
+ const timer = setTimeout(() => {
80105
+ signal.removeEventListener("abort", onAbort);
80106
+ resolve(false);
80107
+ }, ms2);
80108
+ signal.addEventListener("abort", onAbort, { once: true });
80109
+ });
80110
+ }
80111
+
80112
+ // src/cli/task-create.ts
80113
+ var DEFAULT_POLL_INTERVAL_MS2 = 2000;
79176
80114
  async function createTask(input, options) {
79177
80115
  return await masApi.createTask(input, options);
79178
80116
  }
@@ -79231,21 +80169,633 @@ async function runTaskCreate(cwd2, options, dependencies = {}) {
79231
80169
  }
79232
80170
  throw error2;
79233
80171
  }
80172
+ if (!options.wait) {
80173
+ if (options.json) {
80174
+ console.log(JSON.stringify({
80175
+ task_id: created.id,
80176
+ agent_id: created.agent_id,
80177
+ status: created.status
80178
+ }));
80179
+ return;
80180
+ }
80181
+ console.log([
80182
+ `Task ID: ${created.id}`,
80183
+ `Agent ID: ${created.agent_id}`,
80184
+ `Status: ${created.status}`,
80185
+ "First run accepted."
80186
+ ].join(`
80187
+ `));
80188
+ return;
80189
+ }
80190
+ await waitForCreatedTask(created, options, dependencies);
80191
+ }
80192
+ async function waitForCreatedTask(created, options, dependencies) {
80193
+ const setExitCode = dependencies.setExitCode ?? ((code) => process.exitCode = code);
80194
+ const controller = new AbortController;
80195
+ const onSigint = () => controller.abort();
80196
+ process.on("SIGINT", onSigint);
80197
+ if (!options.json) {
80198
+ console.log(`Task ID: ${created.id}`);
80199
+ console.log(`Agent ID: ${created.agent_id}`);
80200
+ }
80201
+ try {
80202
+ const outcome = await waitForTask(created.id, {
80203
+ intervalMs: options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS2,
80204
+ timeoutMs: options.timeoutSeconds === undefined ? undefined : options.timeoutSeconds * 1000,
80205
+ signal: controller.signal,
80206
+ onStatus: options.json ? undefined : (task) => console.log(`Status: ${task.status}`)
80207
+ });
80208
+ const status = outcome.task?.status ?? created.status;
80209
+ const code = outcome.kind === "terminal" ? exitCodeForTaskStatus(status) : outcome.kind === "interrupted" ? SIGINT_EXIT_CODE : 1;
80210
+ setExitCode(code);
80211
+ if (options.json) {
80212
+ console.log(JSON.stringify({
80213
+ task_id: created.id,
80214
+ agent_id: created.agent_id,
80215
+ status,
80216
+ outcome: outcome.kind,
80217
+ exit_code: code
80218
+ }));
80219
+ return;
80220
+ }
80221
+ if (outcome.kind === "timeout") {
80222
+ console.log(import_picocolors52.default.yellow(`Timed out after ${options.timeoutSeconds}s with the task still ${status}.`));
80223
+ return;
80224
+ }
80225
+ if (outcome.kind === "interrupted") {
80226
+ console.log(import_picocolors52.default.yellow("Interrupted. The task is still running."));
80227
+ return;
80228
+ }
80229
+ console.log(code === 0 ? import_picocolors52.default.green("Task completed.") : import_picocolors52.default.red(`Task finished as ${status}.`));
80230
+ } finally {
80231
+ process.removeListener("SIGINT", onSigint);
80232
+ }
80233
+ }
80234
+
80235
+ // src/cli/task-get.ts
80236
+ var import_picocolors54 = __toESM(require_picocolors(), 1);
80237
+
80238
+ // src/cli/task-list.ts
80239
+ var import_picocolors53 = __toESM(require_picocolors(), 1);
80240
+ async function runTaskList(options) {
80241
+ if (!options.json)
80242
+ banner("task list — recent tasks");
80243
+ const tasks = await masApi.listTasks({
80244
+ agentId: options.agentId,
80245
+ limit: options.limit
80246
+ });
79234
80247
  if (options.json) {
79235
- console.log(JSON.stringify({
79236
- task_id: created.id,
79237
- agent_id: created.agent_id,
79238
- status: created.status
79239
- }));
80248
+ console.log(JSON.stringify(tasks, null, 2));
79240
80249
  return;
79241
80250
  }
79242
- console.log([
79243
- `Task ID: ${created.id}`,
79244
- `Agent ID: ${created.agent_id}`,
79245
- `Status: ${created.status}`,
79246
- "First run accepted."
79247
- ].join(`
79248
- `));
80251
+ console.log(formatTaskList(tasks, options.agentId));
80252
+ }
80253
+ function formatTaskList(tasks, agentId) {
80254
+ const lines = [""];
80255
+ if (tasks.length === 0) {
80256
+ lines.push(` ${import_picocolors53.default.dim(agentId ? `No tasks for agent ${agentId}.` : "No tasks yet.")}`, "", ` ${import_picocolors53.default.dim("start one with")} ${import_picocolors53.default.cyan('brainbase task create --message "..."')}`, "");
80257
+ return lines.join(`
80258
+ `);
80259
+ }
80260
+ for (const task of tasks) {
80261
+ const title = task.title?.trim() || import_picocolors53.default.dim("(untitled)");
80262
+ lines.push(` ${statusLabel(task.status)} ${import_picocolors53.default.bold(title)}`);
80263
+ lines.push(` ${import_picocolors53.default.dim(task.id)}`);
80264
+ lines.push(` ${import_picocolors53.default.dim(`agent ${task.agent_id} · created ${task.created_at}`)}`);
80265
+ lines.push("");
80266
+ }
80267
+ lines.push(` ${import_picocolors53.default.dim("inspect one with")} ${import_picocolors53.default.cyan("brainbase task get <id>")}`, "");
80268
+ return lines.join(`
80269
+ `);
80270
+ }
80271
+ function statusLabel(status) {
80272
+ const padded = status.padEnd(14);
80273
+ switch (status) {
80274
+ case "success":
80275
+ return import_picocolors53.default.green(padded);
80276
+ case "fail":
80277
+ return import_picocolors53.default.red(padded);
80278
+ case "need_more_info":
80279
+ return import_picocolors53.default.yellow(padded);
80280
+ case "running":
80281
+ case "initializing":
80282
+ return import_picocolors53.default.cyan(padded);
80283
+ default:
80284
+ return import_picocolors53.default.dim(padded);
80285
+ }
80286
+ }
80287
+
80288
+ // src/cli/task-get.ts
80289
+ async function runTaskGet(taskId, options = {}) {
80290
+ if (!options.json)
80291
+ banner("task get — one task");
80292
+ const task = await masApi.getTask(taskId);
80293
+ const evals = await readEvalRuns(task);
80294
+ if (options.json) {
80295
+ const payload = { task, eval_runs: evals.runs };
80296
+ if (evals.unavailable)
80297
+ payload.eval_runs_unavailable = evals.unavailable;
80298
+ console.log(JSON.stringify(payload, null, 2));
80299
+ return;
80300
+ }
80301
+ console.log(format(task, evals));
80302
+ }
80303
+ async function readEvalRuns(task) {
80304
+ try {
80305
+ return { runs: await api.listAgentEvalRuns(task.agent_id, { taskId: task.id }) };
80306
+ } catch (error2) {
80307
+ if (error2 instanceof ApiError && (error2.status === 401 || error2.status === 403)) {
80308
+ return { runs: [], unavailable: error2.message };
80309
+ }
80310
+ if (error2 instanceof ApiError && error2.status === 404) {
80311
+ return { runs: [], unavailable: "This control plane does not expose eval runs." };
80312
+ }
80313
+ throw error2;
80314
+ }
80315
+ }
80316
+ function format(task, evals) {
80317
+ const lines = [""];
80318
+ const title = task.title?.trim() || import_picocolors54.default.dim("(untitled)");
80319
+ lines.push(` ${statusLabel(task.status)} ${import_picocolors54.default.bold(title)}`);
80320
+ lines.push("");
80321
+ lines.push(` ${import_picocolors54.default.dim("task")} ${task.id}`);
80322
+ lines.push(` ${import_picocolors54.default.dim("agent")} ${task.agent_id}`);
80323
+ lines.push(` ${import_picocolors54.default.dim("created")} ${task.created_at}`);
80324
+ if (task.parent_task_id) {
80325
+ lines.push(` ${import_picocolors54.default.dim("parent")} ${task.parent_task_id}`);
80326
+ }
80327
+ if (task.machine_id) {
80328
+ lines.push(` ${import_picocolors54.default.dim("machine")} ${task.machine_id}${task.machine_size ? ` (${task.machine_size})` : ""}`);
80329
+ }
80330
+ if (task.sandbox_id)
80331
+ lines.push(` ${import_picocolors54.default.dim("sandbox")} ${task.sandbox_id}`);
80332
+ const failure = failureSummary(task);
80333
+ if (failure) {
80334
+ lines.push("");
80335
+ lines.push(` ${import_picocolors54.default.red("failure")} ${failure}`);
80336
+ }
80337
+ const metadata = Object.entries(task.metadata ?? {});
80338
+ if (metadata.length > 0) {
80339
+ lines.push("");
80340
+ lines.push(` ${import_picocolors54.default.bold(import_picocolors54.default.dim("METADATA"))}`);
80341
+ for (const [key2, value] of metadata) {
80342
+ lines.push(` ${import_picocolors54.default.dim(key2)} ${value}`);
80343
+ }
80344
+ }
80345
+ lines.push("");
80346
+ lines.push(` ${import_picocolors54.default.bold(import_picocolors54.default.dim("EVAL VERDICTS"))}`);
80347
+ if (evals.unavailable) {
80348
+ lines.push(` ${import_picocolors54.default.dim(evals.unavailable)}`);
80349
+ } else if (evals.runs.length === 0) {
80350
+ lines.push(` ${import_picocolors54.default.dim("none")}`);
80351
+ } else {
80352
+ for (const run of evals.runs) {
80353
+ const verdict = run.passed === true ? import_picocolors54.default.green("pass") : run.passed === false ? import_picocolors54.default.red("fail") : import_picocolors54.default.dim(run.status);
80354
+ lines.push(` ${verdict} ${run.eval_slug ?? run.eval_id}`);
80355
+ if (run.reasoning)
80356
+ lines.push(` ${import_picocolors54.default.dim(run.reasoning)}`);
80357
+ }
80358
+ }
80359
+ lines.push("");
80360
+ lines.push(` ${import_picocolors54.default.dim("read its transcript with")} ${import_picocolors54.default.cyan(`brainbase task logs ${task.id}`)}`, "");
80361
+ return lines.join(`
80362
+ `);
80363
+ }
80364
+ function failureSummary(task) {
80365
+ const info = task.status_info;
80366
+ if (!info)
80367
+ return;
80368
+ for (const key2 of ["failure_summary", "error"]) {
80369
+ const value = info[key2];
80370
+ if (typeof value === "string" && value.trim())
80371
+ return value.trim();
80372
+ }
80373
+ return;
80374
+ }
80375
+
80376
+ // src/cli/task-logs.ts
80377
+ var import_picocolors55 = __toESM(require_picocolors(), 1);
80378
+
80379
+ // src/core/task-events.ts
80380
+ var DEFAULT_RETRY_BUDGET_MS = 60000;
80381
+ function followTaskEvents(options) {
80382
+ const {
80383
+ url: url2,
80384
+ resolveBearer,
80385
+ onFrame,
80386
+ onOpen,
80387
+ onReconnect,
80388
+ signal,
80389
+ retryBudgetMs = DEFAULT_RETRY_BUDGET_MS
80390
+ } = options;
80391
+ return new Promise((resolve, reject2) => {
80392
+ if (signal?.aborted) {
80393
+ resolve();
80394
+ return;
80395
+ }
80396
+ let source;
80397
+ let settled = false;
80398
+ let closeRequested = false;
80399
+ let downTimer;
80400
+ let lastReason = "connection lost";
80401
+ const clearBudget = () => {
80402
+ if (downTimer !== undefined)
80403
+ clearTimeout(downTimer);
80404
+ downTimer = undefined;
80405
+ };
80406
+ const finish = (error2) => {
80407
+ if (settled)
80408
+ return;
80409
+ settled = true;
80410
+ closeRequested = true;
80411
+ clearBudget();
80412
+ source?.close();
80413
+ if (error2)
80414
+ reject2(error2);
80415
+ else
80416
+ resolve();
80417
+ };
80418
+ source = new EventSource(url2, {
80419
+ fetch: async (input, init) => {
80420
+ let bearer;
80421
+ try {
80422
+ bearer = await resolveBearer();
80423
+ } catch (error2) {
80424
+ if (error2 instanceof StreamHostMovedError) {
80425
+ finish(error2);
80426
+ throw error2;
80427
+ }
80428
+ throw error2;
80429
+ }
80430
+ const headers = new Headers(init?.headers);
80431
+ headers.set("Authorization", `Bearer ${bearer}`);
80432
+ return await fetch(input, { ...init, headers });
80433
+ }
80434
+ });
80435
+ if (closeRequested)
80436
+ source.close();
80437
+ const dispatch = source.dispatchEvent.bind(source);
80438
+ source.dispatchEvent = (event) => {
80439
+ if (event instanceof MessageEvent) {
80440
+ onFrame({
80441
+ id: event.lastEventId,
80442
+ type: event.type,
80443
+ data: parseFrameData(event.data)
80444
+ });
80445
+ }
80446
+ return dispatch(event);
80447
+ };
80448
+ source.addEventListener("open", () => {
80449
+ clearBudget();
80450
+ onOpen?.();
80451
+ });
80452
+ source.addEventListener("error", (event) => {
80453
+ const { message, code } = event;
80454
+ if (source.readyState === source.CLOSED) {
80455
+ finish(streamError(message, code));
80456
+ return;
80457
+ }
80458
+ lastReason = message?.trim() || "connection lost";
80459
+ onReconnect?.(lastReason);
80460
+ if (downTimer === undefined) {
80461
+ downTimer = setTimeout(() => {
80462
+ finish(new ApiError(`The log stream has been unreachable for ${Math.round(retryBudgetMs / 1000)}s (${lastReason}). Giving up — the task may still be running; re-run to reattach.`));
80463
+ }, retryBudgetMs);
80464
+ }
80465
+ });
80466
+ signal?.addEventListener("abort", () => finish(), { once: true });
80467
+ });
80468
+ }
80469
+ function parseFrameData(raw) {
80470
+ if (typeof raw !== "string")
80471
+ return raw;
80472
+ try {
80473
+ return JSON.parse(raw);
80474
+ } catch {
80475
+ return raw;
80476
+ }
80477
+ }
80478
+ function streamError(message, code) {
80479
+ const detail = message?.trim() || "the log stream closed";
80480
+ switch (code) {
80481
+ case 401:
80482
+ return new ApiError(`Not authorized to read this task's events: ${detail}`, 401);
80483
+ case 403:
80484
+ return new ApiError(`Not permitted to read this task's events: ${detail}`, 403);
80485
+ case 404:
80486
+ return new ApiError("No such task, or this control plane does not stream task events yet.", 404);
80487
+ default:
80488
+ return new ApiError(detail, code);
80489
+ }
80490
+ }
80491
+
80492
+ // src/cli/task-logs.ts
80493
+ var MAX_PAGE = 1000;
80494
+ var PAGE_SIZE = 500;
80495
+ async function runTaskLogs(taskId, options = {}, dependencies = {}) {
80496
+ if (options.follow) {
80497
+ await followTranscript(taskId, options, dependencies);
80498
+ return;
80499
+ }
80500
+ const events = await readTranscript(taskId, options.limit);
80501
+ if (options.json) {
80502
+ console.log(JSON.stringify({ items: events }, null, 2));
80503
+ return;
80504
+ }
80505
+ const ordered = [...events].sort((a3, b4) => a3.ts.localeCompare(b4.ts));
80506
+ const rolledUp = rolledUpLanes(ordered);
80507
+ for (const event of ordered) {
80508
+ if (isSupersededChunk(event, rolledUp))
80509
+ continue;
80510
+ console.log(renderEvent(event));
80511
+ }
80512
+ if (ordered.length === 0) {
80513
+ console.log(import_picocolors55.default.dim("No events for this task yet."));
80514
+ }
80515
+ }
80516
+ async function followTranscript(taskId, options, dependencies) {
80517
+ const backfill = options.limit === undefined ? "" : `?backfill=${options.limit}`;
80518
+ const { url: url2, resolveBearer } = await masStreamTarget(`/tasks/${encodeURIComponent(taskId)}/events/stream${backfill}`);
80519
+ if (!options.json) {
80520
+ console.log(import_picocolors55.default.dim(`Following task ${taskId} — Ctrl-C to stop.`));
80521
+ }
80522
+ const setExitCode = dependencies.setExitCode ?? ((code) => process.exitCode = code);
80523
+ const controller = new AbortController;
80524
+ let interruptCode;
80525
+ const stopOn = (code) => () => {
80526
+ interruptCode = code;
80527
+ controller.abort();
80528
+ };
80529
+ const onSigint = stopOn(SIGINT_EXIT_CODE);
80530
+ const onSigterm = stopOn(143);
80531
+ process.once("SIGINT", onSigint);
80532
+ process.once("SIGTERM", onSigterm);
80533
+ try {
80534
+ await followTaskEvents({
80535
+ url: url2,
80536
+ resolveBearer,
80537
+ signal: controller.signal,
80538
+ retryBudgetMs: dependencies.retryBudgetMs,
80539
+ onFrame: (frame) => {
80540
+ const event = frame.data;
80541
+ if (isTaskEvent(event)) {
80542
+ console.log(options.json ? JSON.stringify(event) : renderEvent(event));
80543
+ if (isTaskSettled(event))
80544
+ controller.abort();
80545
+ } else if (options.json) {
80546
+ console.log(JSON.stringify(frame));
80547
+ } else {
80548
+ console.log(import_picocolors55.default.dim(frame.type));
80549
+ }
80550
+ },
80551
+ onReconnect: (reason) => console.error(import_picocolors55.default.dim(`reconnecting — ${reason}`))
80552
+ });
80553
+ if (interruptCode !== undefined)
80554
+ setExitCode(interruptCode);
80555
+ } finally {
80556
+ process.off("SIGINT", onSigint);
80557
+ process.off("SIGTERM", onSigterm);
80558
+ }
80559
+ }
80560
+ function isTaskSettled(event) {
80561
+ if (event.type !== "idle")
80562
+ return false;
80563
+ const status = event.data?.status;
80564
+ return typeof status === "string" && isTerminalTaskStatus(status);
80565
+ }
80566
+ function isTaskEvent(value) {
80567
+ return !!value && typeof value === "object" && typeof value.type === "string" && typeof value.ts === "string";
80568
+ }
80569
+ function rolledUpLanes(events) {
80570
+ const lanes = new Set;
80571
+ for (const event of events) {
80572
+ if (event.type === "assistant.message" || event.type === "subagent.assistant.message") {
80573
+ lanes.add(laneOf(event));
80574
+ }
80575
+ }
80576
+ return lanes;
80577
+ }
80578
+ function isSupersededChunk(event, rolledUp) {
80579
+ return event.type.endsWith(".message.chunk") && rolledUp.has(laneOf(event));
80580
+ }
80581
+ function laneOf(event) {
80582
+ return `${event.subagent_id ?? ""}\x00${event.turn_id ?? ""}`;
80583
+ }
80584
+ async function readTranscript(taskId, limit) {
80585
+ const collected = [];
80586
+ let after2;
80587
+ while (true) {
80588
+ const remaining = limit === undefined ? PAGE_SIZE : limit - collected.length;
80589
+ if (remaining <= 0)
80590
+ break;
80591
+ const pageSize = Math.min(MAX_PAGE, remaining === PAGE_SIZE ? PAGE_SIZE : remaining);
80592
+ const page = await masApi.listTaskEvents(taskId, { limit: pageSize, after: after2 });
80593
+ if (page.length < pageSize) {
80594
+ collected.push(...page);
80595
+ break;
80596
+ }
80597
+ const last2 = page[page.length - 1];
80598
+ const next = { receivedAt: last2.received_at, id: last2.id };
80599
+ if (after2 && next.receivedAt === after2.receivedAt && next.id === after2.id) {
80600
+ break;
80601
+ }
80602
+ collected.push(...page);
80603
+ after2 = next;
80604
+ }
80605
+ return collected;
80606
+ }
80607
+ function renderEvent(event) {
80608
+ const summary = summarize(event);
80609
+ const head3 = `${event.ts} ${event.type}`;
80610
+ return summary ? `${head3} ${summary}` : head3;
80611
+ }
80612
+ function summarize(event) {
80613
+ const data = event.data ?? {};
80614
+ switch (baseType(event.type)) {
80615
+ case "user.message":
80616
+ case "assistant.message":
80617
+ case "assistant.message.chunk":
80618
+ return oneLine2(textOf(data.content));
80619
+ case "assistant.thinking":
80620
+ return oneLine2(asString(data.thought));
80621
+ case "tool_call.start":
80622
+ return oneLine2(`${asString(data.name)} ${compact2(data.args)}`);
80623
+ case "tool_call.end":
80624
+ return oneLine2(`${asString(data.name)} → ${asString(data.status)}`);
80625
+ case "idle":
80626
+ return oneLine2([asString(data.status), asString(data.summary) || asString(data.message)].filter(Boolean).join(" — "));
80627
+ default:
80628
+ return oneLine2(compact2(data));
80629
+ }
80630
+ }
80631
+ function baseType(type) {
80632
+ return type.startsWith("subagent.") ? type.slice("subagent.".length) : type;
80633
+ }
80634
+ function textOf(content) {
80635
+ if (!Array.isArray(content))
80636
+ return "";
80637
+ return content.map((item) => {
80638
+ if (!item || typeof item !== "object")
80639
+ return "";
80640
+ const record3 = item;
80641
+ return record3.type === "text" ? asString(record3.content) : "";
80642
+ }).filter(Boolean).join(" ");
80643
+ }
80644
+ function asString(value) {
80645
+ return typeof value === "string" ? value : "";
80646
+ }
80647
+ function compact2(value) {
80648
+ if (value === undefined || value === null)
80649
+ return "";
80650
+ try {
80651
+ const encoded = JSON.stringify(value);
80652
+ return encoded === "{}" ? "" : encoded ?? "";
80653
+ } catch {
80654
+ return "";
80655
+ }
80656
+ }
80657
+ function oneLine2(value) {
80658
+ const flattened = value.replace(/\s+/g, " ").trim();
80659
+ return flattened.length > 300 ? `${flattened.slice(0, 299)}…` : flattened;
80660
+ }
80661
+
80662
+ // src/core/argv.ts
80663
+ class ArgvParseError extends Error {
80664
+ code = "invalid_arguments";
80665
+ constructor(message) {
80666
+ super(message);
80667
+ this.name = "ArgvParseError";
80668
+ }
80669
+ }
80670
+ function parseBoolean(value, name) {
80671
+ switch (value.trim().toLowerCase()) {
80672
+ case "":
80673
+ case "0":
80674
+ case "false":
80675
+ case "no":
80676
+ case "off":
80677
+ return false;
80678
+ case "1":
80679
+ case "true":
80680
+ case "yes":
80681
+ case "on":
80682
+ return true;
80683
+ default:
80684
+ throw new ArgvParseError(`Unrecognised value for ${name}: ${value}`);
80685
+ }
80686
+ }
80687
+ function validateDefinitions(definitions) {
80688
+ const names = new Map;
80689
+ const keys2 = new Set;
80690
+ for (const definition of definitions) {
80691
+ if (!definition.key) {
80692
+ throw new Error("argv option keys must not be empty");
80693
+ }
80694
+ if (keys2.has(definition.key)) {
80695
+ throw new Error(`duplicate argv option key: ${definition.key}`);
80696
+ }
80697
+ keys2.add(definition.key);
80698
+ for (const name of definition.names) {
80699
+ if (!name.startsWith("-") || name === "-") {
80700
+ throw new Error(`invalid argv option name: ${name}`);
80701
+ }
80702
+ if (name.includes("=")) {
80703
+ throw new Error(`argv option names must not contain "=": ${name}`);
80704
+ }
80705
+ if (names.has(name)) {
80706
+ throw new Error(`duplicate argv option name: ${name}`);
80707
+ }
80708
+ names.set(name, definition);
80709
+ }
80710
+ }
80711
+ return names;
80712
+ }
80713
+ function findDefinition(token, definitions) {
80714
+ const exact = definitions.get(token);
80715
+ if (exact)
80716
+ return { definition: exact, name: token };
80717
+ const equals = token.indexOf("=");
80718
+ if (equals < 1)
80719
+ return null;
80720
+ const name = token.slice(0, equals);
80721
+ const definition = definitions.get(name);
80722
+ return definition ? { definition, name, joined: token.slice(equals + 1) } : null;
80723
+ }
80724
+ function assignOption(options, definition, value, name) {
80725
+ const current = options[definition.key];
80726
+ if (definition.multiple) {
80727
+ if (typeof value !== "string") {
80728
+ throw new Error(`boolean argv option cannot be repeated: ${name}`);
80729
+ }
80730
+ options[definition.key] = [
80731
+ ...Array.isArray(current) ? current : [],
80732
+ value
80733
+ ];
80734
+ return;
80735
+ }
80736
+ if (current !== undefined) {
80737
+ throw new ArgvParseError(`Option ${name} may only be provided once`);
80738
+ }
80739
+ options[definition.key] = value;
80740
+ }
80741
+ function parseArgv(argv, definitions) {
80742
+ const byName = validateDefinitions(definitions);
80743
+ const options = Object.create(null);
80744
+ const positionals = [];
80745
+ let optionsEnabled = true;
80746
+ for (let index = 0;index < argv.length; index += 1) {
80747
+ const token = argv[index];
80748
+ if (!optionsEnabled) {
80749
+ positionals.push(token);
80750
+ continue;
80751
+ }
80752
+ if (token === "--") {
80753
+ optionsEnabled = false;
80754
+ continue;
80755
+ }
80756
+ if (!token.startsWith("-") || token === "-") {
80757
+ positionals.push(token);
80758
+ continue;
80759
+ }
80760
+ const hit = findDefinition(token, byName);
80761
+ if (!hit) {
80762
+ throw new ArgvParseError(`Unknown option: ${token.split("=", 1)[0]}`);
80763
+ }
80764
+ if (hit.definition.kind === "boolean") {
80765
+ assignOption(options, hit.definition, hit.joined === undefined ? true : parseBoolean(hit.joined, hit.name), hit.name);
80766
+ continue;
80767
+ }
80768
+ let value = hit.joined;
80769
+ if (value === undefined) {
80770
+ const next = argv[index + 1];
80771
+ if (next === "--") {
80772
+ value = argv[index + 2];
80773
+ if (value === undefined) {
80774
+ throw new ArgvParseError(`Option ${hit.name} requires a value`);
80775
+ }
80776
+ index += 2;
80777
+ } else {
80778
+ if (next === undefined || next.startsWith("-")) {
80779
+ throw new ArgvParseError(`Option ${hit.name} requires a value; use ${hit.name}=<value> for values beginning with "-"`);
80780
+ }
80781
+ value = next;
80782
+ index += 1;
80783
+ }
80784
+ }
80785
+ assignOption(options, hit.definition, value, hit.name);
80786
+ }
80787
+ return { options, positionals };
80788
+ }
80789
+ function stringOption(parsed, key2) {
80790
+ const value = parsed.options[key2];
80791
+ return typeof value === "string" ? value : undefined;
80792
+ }
80793
+ function stringOptions(parsed, key2) {
80794
+ const value = parsed.options[key2];
80795
+ return Array.isArray(value) ? value : [];
80796
+ }
80797
+ function booleanOption(parsed, key2) {
80798
+ return parsed.options[key2] === true;
79249
80799
  }
79250
80800
 
79251
80801
  // src/cli/task.ts
@@ -79253,13 +80803,16 @@ var VALUE_FLAGS = [
79253
80803
  ["--agent", "agentId"],
79254
80804
  ["--message", "message"],
79255
80805
  ["--title", "title"],
79256
- ["--model", "model"]
80806
+ ["--model", "model"],
80807
+ ["--timeout", "timeout"]
79257
80808
  ];
80809
+ var BOOLEAN_FLAGS = ["--json", "--wait"];
79258
80810
  var TASK_OPTION_NAMES = new Set([
79259
80811
  ...VALUE_FLAGS.map(([flag]) => flag),
79260
- "--json"
80812
+ ...BOOLEAN_FLAGS
79261
80813
  ]);
79262
80814
  var HELP_FLAGS = new Set(["--help", "-h"]);
80815
+ var LIST_LIMIT_MAX = 200;
79263
80816
  function missingValueError(flag) {
79264
80817
  if (flag === "--message" || flag === "--agent") {
79265
80818
  return new Error(`${flag} is required and must not be blank.`);
@@ -79275,14 +80828,17 @@ function parseCreateArgs(args) {
79275
80828
  for (let index = 0;index < args.length; index += 1) {
79276
80829
  const arg = args[index];
79277
80830
  if (HELP_FLAGS.has(arg)) {
79278
- return { help: true, options };
80831
+ return { help: true, options: finishCreateArgs(options) };
79279
80832
  }
79280
- if (arg === "--json") {
80833
+ if (arg === "--json" || arg === "--wait") {
79281
80834
  if (seen.has(arg)) {
79282
80835
  throw new Error(`Duplicate task create option: ${arg}`);
79283
80836
  }
79284
80837
  seen.add(arg);
79285
- options.json = true;
80838
+ if (arg === "--json")
80839
+ options.json = true;
80840
+ else
80841
+ options.wait = true;
79286
80842
  continue;
79287
80843
  }
79288
80844
  let matched = false;
@@ -79302,7 +80858,7 @@ function parseCreateArgs(args) {
79302
80858
  throw missingValueError(flag);
79303
80859
  index += 1;
79304
80860
  } else if (HELP_FLAGS.has(value) && (option === "agentId" || option === "model")) {
79305
- return { help: true, options };
80861
+ return { help: true, options: finishCreateArgs(options) };
79306
80862
  } else if (TASK_OPTION_NAMES.has(value)) {
79307
80863
  throw flagLikeValueError(flag, value);
79308
80864
  }
@@ -79326,7 +80882,38 @@ function parseCreateArgs(args) {
79326
80882
  throw new Error(`Unknown task create argument: ${arg}`);
79327
80883
  }
79328
80884
  }
79329
- return { help: false, options };
80885
+ return { help: false, options: finishCreateArgs(options) };
80886
+ }
80887
+ function finishCreateArgs(raw) {
80888
+ const { timeout, ...rest2 } = raw;
80889
+ if (timeout === undefined)
80890
+ return rest2;
80891
+ const seconds = Number(timeout.trim());
80892
+ if (!Number.isFinite(seconds) || seconds <= 0) {
80893
+ throw new Error("--timeout must be a positive number of seconds.");
80894
+ }
80895
+ if (!rest2.wait) {
80896
+ throw new Error("--timeout only applies with --wait.");
80897
+ }
80898
+ return { ...rest2, timeoutSeconds: seconds };
80899
+ }
80900
+ function positiveInteger(value, flag, max2) {
80901
+ if (value === undefined)
80902
+ return;
80903
+ const parsed = Number(value.trim());
80904
+ if (!Number.isInteger(parsed) || parsed <= 0) {
80905
+ throw new Error(`${flag} must be a positive whole number.`);
80906
+ }
80907
+ if (max2 !== undefined && parsed > max2) {
80908
+ throw new Error(`${flag} must be between 1 and ${max2}.`);
80909
+ }
80910
+ return parsed;
80911
+ }
80912
+ function requiredTaskId(positionals, usage) {
80913
+ if (positionals.length !== 1 || !positionals[0]?.trim()) {
80914
+ throw new Error(`Usage: ${usage}`);
80915
+ }
80916
+ return positionals[0].trim();
79330
80917
  }
79331
80918
  async function runTask(cwd2, sub, args) {
79332
80919
  switch (sub) {
@@ -79339,6 +80926,56 @@ async function runTask(cwd2, sub, args) {
79339
80926
  await runTaskCreate(cwd2, parsed.options);
79340
80927
  return;
79341
80928
  }
80929
+ case "list": {
80930
+ if (args.some((arg) => HELP_FLAGS.has(arg))) {
80931
+ printHelp4();
80932
+ return;
80933
+ }
80934
+ const parsed = parseArgv(args, [
80935
+ { key: "agent", names: ["--agent"], kind: "value" },
80936
+ { key: "limit", names: ["--limit"], kind: "value" },
80937
+ { key: "json", names: ["--json"], kind: "boolean" }
80938
+ ]);
80939
+ if (parsed.positionals.length > 0) {
80940
+ throw new Error("Usage: brainbase task list [--agent <id>] [--limit <n>] [--json]");
80941
+ }
80942
+ await runTaskList({
80943
+ agentId: stringOption(parsed, "agent"),
80944
+ limit: positiveInteger(stringOption(parsed, "limit"), "--limit", LIST_LIMIT_MAX),
80945
+ json: booleanOption(parsed, "json")
80946
+ });
80947
+ return;
80948
+ }
80949
+ case "get": {
80950
+ if (args.some((arg) => HELP_FLAGS.has(arg))) {
80951
+ printHelp4();
80952
+ return;
80953
+ }
80954
+ const parsed = parseArgv(args, [
80955
+ { key: "json", names: ["--json"], kind: "boolean" }
80956
+ ]);
80957
+ const taskId = requiredTaskId(parsed.positionals, "brainbase task get <task-id> [--json]");
80958
+ await runTaskGet(taskId, { json: booleanOption(parsed, "json") });
80959
+ return;
80960
+ }
80961
+ case "logs": {
80962
+ if (args.some((arg) => HELP_FLAGS.has(arg))) {
80963
+ printHelp4();
80964
+ return;
80965
+ }
80966
+ const parsed = parseArgv(args, [
80967
+ { key: "limit", names: ["--limit"], kind: "value" },
80968
+ { key: "json", names: ["--json"], kind: "boolean" },
80969
+ { key: "follow", names: ["--follow", "-f"], kind: "boolean" }
80970
+ ]);
80971
+ const taskId = requiredTaskId(parsed.positionals, "brainbase task logs <task-id> [--limit <n>] [--json] [--follow]");
80972
+ await runTaskLogs(taskId, {
80973
+ limit: positiveInteger(stringOption(parsed, "limit"), "--limit"),
80974
+ json: booleanOption(parsed, "json"),
80975
+ follow: booleanOption(parsed, "follow")
80976
+ });
80977
+ return;
80978
+ }
79342
80979
  case undefined:
79343
80980
  case "help":
79344
80981
  case "-h":
@@ -79355,18 +80992,220 @@ async function runTask(cwd2, sub, args) {
79355
80992
  function printHelp4() {
79356
80993
  const out = [];
79357
80994
  out.push("");
79358
- out.push(` ${import_picocolors52.default.bold("brainbase task")} ${import_picocolors52.default.dim("<sub> [options]")}`);
80995
+ out.push(` ${import_picocolors56.default.bold("brainbase task")} ${import_picocolors56.default.dim("<sub> [options]")}`);
80996
+ out.push("");
80997
+ out.push(` ${import_picocolors56.default.cyan("create")} ${import_picocolors56.default.dim("--message <text>")} ${import_picocolors56.default.dim("create a task and start its first run")}`);
80998
+ out.push(` ${import_picocolors56.default.cyan("list")} ${import_picocolors56.default.dim("recent tasks you can reach (--json for scripts)")}`);
80999
+ out.push(` ${import_picocolors56.default.cyan("get")} ${import_picocolors56.default.dim("<task-id>")} ${import_picocolors56.default.dim("one task: status, agent, machine, eval verdicts")}`);
81000
+ out.push(` ${import_picocolors56.default.cyan("logs")} ${import_picocolors56.default.dim("<task-id> [--follow]")} ${import_picocolors56.default.dim("one line per event in the task's transcript, or tail it live")}`);
81001
+ out.push("");
81002
+ out.push(` ${import_picocolors56.default.bold("create flags")}`);
81003
+ out.push(` ${import_picocolors56.default.dim("--message <text>")} required first user message`);
81004
+ out.push(` ${import_picocolors56.default.dim("--agent <id>")} override the claimed agent in brainbase.agent.yaml`);
81005
+ out.push(` ${import_picocolors56.default.dim("--title <text>")} optional task title`);
81006
+ out.push(` ${import_picocolors56.default.dim("--model <id>")} optional model override`);
81007
+ out.push(` ${import_picocolors56.default.dim("--wait")} block until the task finishes; exit 1 unless it succeeded`);
81008
+ out.push(` ${import_picocolors56.default.dim("--timeout <secs>")} give up waiting after <secs> and exit 1 (needs --wait)`);
81009
+ out.push("");
81010
+ out.push(` ${import_picocolors56.default.bold("list flags")}`);
81011
+ out.push(` ${import_picocolors56.default.dim("--agent <id>")} only this agent's tasks`);
81012
+ out.push(` ${import_picocolors56.default.dim("--limit <n>")} cap the page (1-200, server default 50)`);
79359
81013
  out.push("");
79360
- out.push(` ${import_picocolors52.default.cyan("create")} ${import_picocolors52.default.dim("--message <text>")} ${import_picocolors52.default.dim("create a task and start its first run")}`);
81014
+ out.push(` ${import_picocolors56.default.bold("logs flags")}`);
81015
+ out.push(` ${import_picocolors56.default.dim("--limit <n>")} stop after <n> events instead of the whole transcript; with --follow, the first page size`);
81016
+ out.push(` ${import_picocolors56.default.dim("--follow, -f")} stay attached and print events as they land, until the task finishes`);
79361
81017
  out.push("");
79362
- out.push(` ${import_picocolors52.default.bold("create flags")}`);
79363
- out.push(` ${import_picocolors52.default.dim("--message <text>")} required first user message`);
79364
- out.push(` ${import_picocolors52.default.dim("--agent <id>")} override the claimed agent in brainbase.agent.yaml`);
79365
- out.push(` ${import_picocolors52.default.dim("--title <text>")} optional task title`);
79366
- out.push(` ${import_picocolors52.default.dim("--model <id>")} optional model override`);
79367
- out.push(` ${import_picocolors52.default.dim("--json")} print task_id, agent_id, and status as JSON`);
81018
+ out.push(` ${import_picocolors56.default.bold("every subcommand")}`);
81019
+ out.push(` ${import_picocolors56.default.dim("--json")} machine-readable output create prints task_id, agent_id and status; list, get and logs print the raw records`);
81020
+ out.push(` ${import_picocolors56.default.dim(" logs --follow --json prints one record per line, since a live stream has no end to close on")}`);
79368
81021
  out.push("");
79369
- out.push(` ${import_picocolors52.default.dim("Flag-like values:")} use ${import_picocolors52.default.cyan("--flag=value")} or ${import_picocolors52.default.cyan("--flag -- <value>")}`);
81022
+ out.push(` ${import_picocolors56.default.dim("Exit codes with --wait:")} ${import_picocolors56.default.dim("0 succeeded · 1 failed, needs input, or timed out · 130 interrupted")}`);
81023
+ out.push(` ${import_picocolors56.default.dim("Exit codes with --follow:")} ${import_picocolors56.default.dim("0 task reached a terminal state · 1 stream refused, or unreachable for 60s · 130 interrupted (Ctrl-C) · 143 terminated")}`);
81024
+ out.push("");
81025
+ out.push(` ${import_picocolors56.default.dim("Flag-like values:")} use ${import_picocolors56.default.cyan("--flag=value")} or ${import_picocolors56.default.cyan("--flag -- <value>")}`);
81026
+ out.push("");
81027
+ console.log(out.join(`
81028
+ `));
81029
+ }
81030
+
81031
+ // src/cli/machine.ts
81032
+ var import_picocolors59 = __toESM(require_picocolors(), 1);
81033
+
81034
+ // src/cli/machine-list.ts
81035
+ var import_picocolors57 = __toESM(require_picocolors(), 1);
81036
+ async function runMachineList(args = {}) {
81037
+ if (!args.json)
81038
+ banner("machine list — sandboxes you can reach");
81039
+ const machines = await masApi.listMachines({
81040
+ includeDead: Boolean(args.all),
81041
+ limit: args.limit
81042
+ });
81043
+ if (args.json) {
81044
+ console.log(JSON.stringify(machines, null, 2));
81045
+ return;
81046
+ }
81047
+ console.log(formatMachineList(machines, Boolean(args.all)));
81048
+ }
81049
+ function formatMachineList(machines, includedDead) {
81050
+ const lines = [""];
81051
+ if (machines.length === 0) {
81052
+ lines.push(` ${import_picocolors57.default.dim(includedDead ? "No machines." : "No live machines. Pass --all to include torn-down ones.")}`, "");
81053
+ return lines.join(`
81054
+ `);
81055
+ }
81056
+ const rows = machines.map((machine) => ({
81057
+ id: machine.id,
81058
+ kind: machine.kind,
81059
+ status: machine.destroyed_at ? "dead" : machine.status,
81060
+ size: machine.machine_size ?? "",
81061
+ age: age(machine.created_at),
81062
+ dead: Boolean(machine.destroyed_at)
81063
+ }));
81064
+ const width = (pick3) => Math.max(...rows.map((row) => pick3(row).length), 0);
81065
+ const idWidth = width((row) => row.id);
81066
+ const kindWidth = width((row) => row.kind);
81067
+ const statusWidth = width((row) => row.status);
81068
+ const sizeWidth = width((row) => row.size);
81069
+ for (const row of rows) {
81070
+ const status = row.status.padEnd(statusWidth);
81071
+ const cells = [
81072
+ row.id.padEnd(idWidth),
81073
+ row.kind.padEnd(kindWidth),
81074
+ row.dead ? status : statusTint(row.status, status),
81075
+ row.size.padEnd(sizeWidth),
81076
+ import_picocolors57.default.dim(row.age)
81077
+ ].join(" ");
81078
+ lines.push(` ${row.dead ? import_picocolors57.default.dim(cells) : cells}`);
81079
+ }
81080
+ lines.push("", ` ${import_picocolors57.default.dim("tear one down with")} ${import_picocolors57.default.cyan("brainbase machine rm <id>")}`, "");
81081
+ return lines.join(`
81082
+ `);
81083
+ }
81084
+ function statusTint(status, padded) {
81085
+ switch (status) {
81086
+ case "running":
81087
+ return import_picocolors57.default.green(padded);
81088
+ case "stopped":
81089
+ case "starting":
81090
+ return import_picocolors57.default.yellow(padded);
81091
+ default:
81092
+ return padded;
81093
+ }
81094
+ }
81095
+ function age(createdAt) {
81096
+ if (!createdAt)
81097
+ return "";
81098
+ const started = new Date(createdAt).getTime();
81099
+ if (Number.isNaN(started))
81100
+ return "";
81101
+ const minutes = Math.floor((Date.now() - started) / 60000);
81102
+ if (minutes < 1)
81103
+ return "just now";
81104
+ if (minutes < 60)
81105
+ return `${minutes}m`;
81106
+ const hours = Math.floor(minutes / 60);
81107
+ if (hours < 24)
81108
+ return `${hours}h`;
81109
+ return `${Math.floor(hours / 24)}d`;
81110
+ }
81111
+
81112
+ // src/cli/machine-rm.ts
81113
+ var import_picocolors58 = __toESM(require_picocolors(), 1);
81114
+ async function runMachineRm(args, dependencies = {}) {
81115
+ const setExitCode = dependencies.setExitCode ?? ((code) => process.exitCode = code);
81116
+ const machineId = args.machineId?.trim();
81117
+ if (!machineId) {
81118
+ throw new Error("A machine id is required: `brainbase machine rm <id>`. List them with `brainbase machine ls`.");
81119
+ }
81120
+ if (!args.json)
81121
+ banner(`machine rm — ${machineId}`);
81122
+ const confirmTeardown = dependencies.confirmTeardown ?? promptForTeardown;
81123
+ if (!await confirmTeardown({ ...args, machineId })) {
81124
+ if (args.json) {
81125
+ console.log(JSON.stringify({ id: machineId, torn_down: false, outcome: "declined" }, null, 2));
81126
+ } else {
81127
+ console.log(` ${import_picocolors58.default.dim("Left alone.")}`);
81128
+ }
81129
+ return;
81130
+ }
81131
+ const machine = await masApi.deleteMachine(machineId);
81132
+ if (!machine.destroyed_at)
81133
+ setExitCode(1);
81134
+ if (args.json) {
81135
+ console.log(JSON.stringify(machine, null, 2));
81136
+ return;
81137
+ }
81138
+ console.log(formatMachineRm(machine));
81139
+ }
81140
+ async function promptForTeardown(args) {
81141
+ if (autoProceed(args.yes))
81142
+ return true;
81143
+ if (!args.json) {
81144
+ console.log(`
81145
+ ${import_picocolors58.default.red("This destroys the sandbox.")} ${import_picocolors58.default.dim("Anything not committed out of it is lost. The machine row is kept for cost history.")}
81146
+ `);
81147
+ }
81148
+ const answer = await se({
81149
+ message: `Tear down ${args.machineId}?`,
81150
+ initialValue: false
81151
+ });
81152
+ return ensureNotCancelled(answer);
81153
+ }
81154
+ function formatMachineRm(machine) {
81155
+ const lines = [""];
81156
+ if (machine.destroyed_at) {
81157
+ lines.push(` ${import_picocolors58.default.green("✓")} ${machine.kind} sandbox ${machine.id} is torn down.`, ` ${import_picocolors58.default.dim(`destroyed_at ${machine.destroyed_at}`)}`);
81158
+ } else {
81159
+ lines.push(` ${import_picocolors58.default.yellow("!")} ${machine.id} came back without a teardown timestamp (status ${machine.status}).`, ` ${import_picocolors58.default.dim("Re-check with `brainbase machine ls --all` before assuming it stopped billing.")}`);
81160
+ }
81161
+ lines.push("");
81162
+ return lines.join(`
81163
+ `);
81164
+ }
81165
+
81166
+ // src/cli/machine.ts
81167
+ async function runMachine(sub, args, opts) {
81168
+ if (args.some((arg) => arg === "--help" || arg === "-h")) {
81169
+ printHelp5();
81170
+ return;
81171
+ }
81172
+ switch (sub) {
81173
+ case "ls":
81174
+ case "list":
81175
+ await runMachineList({ all: opts.all, limit: opts.limit, json: opts.json });
81176
+ return;
81177
+ case "rm":
81178
+ await runMachineRm({
81179
+ machineId: args[0],
81180
+ yes: opts.yes,
81181
+ json: opts.json
81182
+ });
81183
+ return;
81184
+ case undefined:
81185
+ case "help":
81186
+ case "-h":
81187
+ case "--help":
81188
+ printHelp5();
81189
+ return;
81190
+ default:
81191
+ console.error(`Unknown machine subcommand: ${sub}
81192
+ `);
81193
+ printHelp5();
81194
+ process.exit(1);
81195
+ }
81196
+ }
81197
+ function printHelp5() {
81198
+ const out = [];
81199
+ out.push("");
81200
+ out.push(` ${import_picocolors59.default.bold("brainbase machine")} ${import_picocolors59.default.dim("<sub> [options]")}`);
81201
+ out.push("");
81202
+ out.push(` ${import_picocolors59.default.cyan("ls")} ${import_picocolors59.default.dim("list your sandboxes — live ones only unless --all")}`);
81203
+ out.push(` ${import_picocolors59.default.cyan("rm")} ${import_picocolors59.default.dim("<id>")} ${import_picocolors59.default.dim("tear a sandbox down (terminal; the row is kept for cost history)")}`);
81204
+ out.push("");
81205
+ out.push(` ${import_picocolors59.default.dim("--all")} ${import_picocolors59.default.dim("for ls: include machines already torn down")}`);
81206
+ out.push(` ${import_picocolors59.default.dim("--limit <n>")} ${import_picocolors59.default.dim("for ls: how many to fetch (1-200, default 50)")}`);
81207
+ out.push(` ${import_picocolors59.default.dim("--yes, -y")} ${import_picocolors59.default.dim("for rm: skip the confirmation")}`);
81208
+ out.push(` ${import_picocolors59.default.dim("--json")} ${import_picocolors59.default.dim("machine-readable output")}`);
79370
81209
  out.push("");
79371
81210
  console.log(out.join(`
79372
81211
  `));
@@ -79377,7 +81216,7 @@ import {
79377
81216
  execFileSync as execFileSync3,
79378
81217
  spawn as spawn5
79379
81218
  } from "node:child_process";
79380
- import crypto10 from "node:crypto";
81219
+ import crypto11 from "node:crypto";
79381
81220
  import fs86 from "node:fs";
79382
81221
  import os19 from "node:os";
79383
81222
  import path94 from "node:path";
@@ -79387,7 +81226,7 @@ import {
79387
81226
  execFileSync as execFileSync2,
79388
81227
  spawn as spawn4
79389
81228
  } from "node:child_process";
79390
- import crypto6 from "node:crypto";
81229
+ import crypto7 from "node:crypto";
79391
81230
  import fs81 from "node:fs";
79392
81231
  import os16 from "node:os";
79393
81232
  import path89 from "node:path";
@@ -79976,7 +81815,7 @@ function openRegularFileNoFollow(filePath, label, root) {
79976
81815
  return { fd, stat };
79977
81816
  }
79978
81817
  async function sha256OfDescriptor(fd) {
79979
- const hash = crypto6.createHash("sha256");
81818
+ const hash = crypto7.createHash("sha256");
79980
81819
  const stream = fs81.createReadStream("", {
79981
81820
  fd,
79982
81821
  autoClose: false,
@@ -80224,7 +82063,7 @@ async function downloadInputReference(stagingRoot, input, context) {
80224
82063
  fs81.mkdirSync(path89.dirname(destination), { recursive: true, mode: 448 });
80225
82064
  assertNoSymlinkTraversal(stagingRoot, relative);
80226
82065
  assertWritableDestination(stagingRoot, relative);
80227
- const temporary = `${destination}.${process.pid}.${crypto6.randomBytes(6).toString("hex")}.download`;
82066
+ const temporary = `${destination}.${process.pid}.${crypto7.randomBytes(6).toString("hex")}.download`;
80228
82067
  const controller = new AbortController;
80229
82068
  const timer = setTimeout(() => controller.abort(), remainingMs);
80230
82069
  let descriptor;
@@ -80256,7 +82095,7 @@ async function downloadInputReference(stagingRoot, input, context) {
80256
82095
  let actual;
80257
82096
  let size2 = 0;
80258
82097
  try {
80259
- const hash = crypto6.createHash("sha256");
82098
+ const hash = crypto7.createHash("sha256");
80260
82099
  while (true) {
80261
82100
  const { done, value } = await reader.read();
80262
82101
  if (done)
@@ -80441,9 +82280,9 @@ function findZipMembers(archivePath, requested, context) {
80441
82280
  }
80442
82281
  async function writeVerifiedArchiveMember(source, destination, material, context) {
80443
82282
  fs81.mkdirSync(path89.dirname(destination), { recursive: true, mode: 448 });
80444
- const temporary = `${destination}.${process.pid}.${crypto6.randomBytes(6).toString("hex")}.tmp`;
82283
+ const temporary = `${destination}.${process.pid}.${crypto7.randomBytes(6).toString("hex")}.tmp`;
80445
82284
  const descriptor = fs81.openSync(temporary, "wx", 384);
80446
- const hash = crypto6.createHash("sha256");
82285
+ const hash = crypto7.createHash("sha256");
80447
82286
  let size2 = 0;
80448
82287
  try {
80449
82288
  for await (const value of source) {
@@ -80632,7 +82471,7 @@ async function extractArchiveMembers(archivePath, outputRoot, materials, context
80632
82471
  }
80633
82472
  async function atomicCopy(source, destination, mode, sourceRoot) {
80634
82473
  fs81.mkdirSync(path89.dirname(destination), { recursive: true });
80635
- const temporary = `${destination}.${process.pid}.${crypto6.randomBytes(6).toString("hex")}.tmp`;
82474
+ const temporary = `${destination}.${process.pid}.${crypto7.randomBytes(6).toString("hex")}.tmp`;
80636
82475
  const opened = openRegularFileNoFollow(source, `copy source ${source}`, sourceRoot);
80637
82476
  try {
80638
82477
  await pipeline2(fs81.createReadStream("", {
@@ -81040,7 +82879,7 @@ async function runCommand(command, root, spec, context, options = {}) {
81040
82879
  const reachesPhaseDeadline = command.timeout_ms === undefined || command.timeout_ms >= remainingMs;
81041
82880
  const timeoutMs2 = reachesPhaseDeadline ? remainingMs : command.timeout_ms;
81042
82881
  const started = Date.now();
81043
- const commandMarker = crypto6.randomBytes(32).toString("hex");
82882
+ const commandMarker = crypto7.randomBytes(32).toString("hex");
81044
82883
  return await new Promise((resolve, reject2) => {
81045
82884
  const child = spawn4(command.argv[0], command.argv.slice(1), {
81046
82885
  cwd: cwd2,
@@ -81077,7 +82916,7 @@ async function runCommand(command, root, spec, context, options = {}) {
81077
82916
  return error2;
81078
82917
  return new BenchmarkCommandExecutionError(error2.code, error2.message, sanitizedOutput(stdout), sanitizedOutput(stderr), error2.durationMs);
81079
82918
  };
81080
- const fail = (error2) => {
82919
+ const fail2 = (error2) => {
81081
82920
  if (settled)
81082
82921
  return;
81083
82922
  settled = true;
@@ -81096,7 +82935,7 @@ async function runCommand(command, root, spec, context, options = {}) {
81096
82935
  captured += chunk2.length;
81097
82936
  context.remainingOutputBytes -= chunk2.length;
81098
82937
  if (captured > spec.budget.max_output_bytes || context.remainingOutputBytes < 0) {
81099
- fail(new BenchmarkPhaseError("output_limit_exceeded", `command output exceeded budget: ${command.id}`));
82938
+ fail2(new BenchmarkPhaseError("output_limit_exceeded", `command output exceeded budget: ${command.id}`));
81100
82939
  return;
81101
82940
  }
81102
82941
  target.push(chunk2);
@@ -81119,14 +82958,14 @@ async function runCommand(command, root, spec, context, options = {}) {
81119
82958
  child.stdout?.on("data", (chunk2) => capture(stdout, chunk2));
81120
82959
  child.stderr?.on("data", (chunk2) => capture(stderr, chunk2));
81121
82960
  child.on("error", (error2) => {
81122
- fail(new BenchmarkCommandExecutionError("command_start_failed", `failed to start ${command.id}: ${error2.message}`, Buffer.concat(stdout), Buffer.concat(stderr), Date.now() - started));
82961
+ fail2(new BenchmarkCommandExecutionError("command_start_failed", `failed to start ${command.id}: ${error2.message}`, Buffer.concat(stdout), Buffer.concat(stderr), Date.now() - started));
81123
82962
  });
81124
82963
  timer = setTimeout(() => {
81125
82964
  if (reachesPhaseDeadline) {
81126
- fail(new BenchmarkPhaseError("phase_timeout", "phase budget expired"));
82965
+ fail2(new BenchmarkPhaseError("phase_timeout", "phase budget expired"));
81127
82966
  return;
81128
82967
  }
81129
- fail(new BenchmarkCommandExecutionError("command_timeout", `command timed out: ${command.id}`, Buffer.concat(stdout), Buffer.concat(stderr), Date.now() - started));
82968
+ fail2(new BenchmarkCommandExecutionError("command_timeout", `command timed out: ${command.id}`, Buffer.concat(stdout), Buffer.concat(stderr), Date.now() - started));
81130
82969
  }, timeoutMs2);
81131
82970
  child.on("exit", (code, signal) => {
81132
82971
  if (settled)
@@ -81149,7 +82988,7 @@ async function runCommand(command, root, spec, context, options = {}) {
81149
82988
  async function writeLog(root, name, data, spec) {
81150
82989
  const destination = path89.join(root, name);
81151
82990
  fs81.mkdirSync(path89.dirname(destination), { recursive: true });
81152
- const temporary = `${destination}.${process.pid}.${crypto6.randomBytes(6).toString("hex")}.tmp`;
82991
+ const temporary = `${destination}.${process.pid}.${crypto7.randomBytes(6).toString("hex")}.tmp`;
81153
82992
  try {
81154
82993
  fs81.writeFileSync(temporary, redactCommandOutput(data, spec), {
81155
82994
  flag: "wx",
@@ -81163,7 +83002,7 @@ async function writeLog(root, name, data, spec) {
81163
83002
  }
81164
83003
  function writeBufferAtomic(destination, data) {
81165
83004
  fs81.mkdirSync(path89.dirname(destination), { recursive: true });
81166
- const temporary = `${destination}.${process.pid}.${crypto6.randomBytes(6).toString("hex")}.tmp`;
83005
+ const temporary = `${destination}.${process.pid}.${crypto7.randomBytes(6).toString("hex")}.tmp`;
81167
83006
  try {
81168
83007
  fs81.writeFileSync(temporary, data, { flag: "wx", mode: 384 });
81169
83008
  fs81.renameSync(temporary, destination);
@@ -82006,7 +83845,7 @@ async function executeEvaluate(spec, context) {
82006
83845
  if (spec.capture_workspace_archive) {
82007
83846
  const regularFiles = manifest.filter((entry) => entry.kind !== "symlink").map((entry) => entry.path);
82008
83847
  const archive = path89.join(spec.logs_root, "candidate-workspace.tar.gz");
82009
- const temporary = `${archive}.${process.pid}.${crypto6.randomBytes(6).toString("hex")}.tmp`;
83848
+ const temporary = `${archive}.${process.pid}.${crypto7.randomBytes(6).toString("hex")}.tmp`;
82010
83849
  try {
82011
83850
  await pack({ rootDir: spec.workspace_root, outFile: temporary, files: regularFiles });
82012
83851
  fs81.renameSync(temporary, archive);
@@ -82459,7 +84298,7 @@ async function runBenchmarkPhase(specPathInput, resultPathInput, expectedPhase,
82459
84298
  }
82460
84299
 
82461
84300
  // src/cli/benchmark-control.ts
82462
- var import_picocolors53 = __toESM(require_picocolors(), 1);
84301
+ var import_picocolors60 = __toESM(require_picocolors(), 1);
82463
84302
  import fs85 from "node:fs";
82464
84303
  import path93 from "node:path";
82465
84304
 
@@ -82957,10 +84796,10 @@ class BenchmarkApiClient {
82957
84796
  }
82958
84797
  });
82959
84798
  }
82960
- exportManifest(benchmarkId, format, revisionId) {
84799
+ exportManifest(benchmarkId, format2, revisionId) {
82961
84800
  return this.request(`/${encodeURIComponent(benchmarkId)}/export`, {
82962
84801
  mode: "binary",
82963
- query: { format, revision_id: revisionId }
84802
+ query: { format: format2, revision_id: revisionId }
82964
84803
  });
82965
84804
  }
82966
84805
  exportBundle(benchmarkId, revisionId) {
@@ -83158,7 +84997,7 @@ class BenchmarkApiClient {
83158
84997
 
83159
84998
  // src/core/benchmark-authoring.ts
83160
84999
  var import_yaml7 = __toESM(require_dist(), 1);
83161
- import crypto7 from "node:crypto";
85000
+ import crypto8 from "node:crypto";
83162
85001
  import fs82 from "node:fs";
83163
85002
  import path90 from "node:path";
83164
85003
 
@@ -83957,7 +85796,7 @@ function renderYaml(value) {
83957
85796
  return import_yaml7.default.stringify(stableValue(value), { indent: 2, lineWidth: 0 });
83958
85797
  }
83959
85798
  function digest(data) {
83960
- return crypto7.createHash("sha256").update(data).digest("hex");
85799
+ return crypto8.createHash("sha256").update(data).digest("hex");
83961
85800
  }
83962
85801
  function assertRegularFile(filePath, label) {
83963
85802
  let stat;
@@ -84327,7 +86166,7 @@ function sameDestinationObjectIdentity(target, identity2) {
84327
86166
  }
84328
86167
  }
84329
86168
  function acquireDestinationWriteLock(root) {
84330
- const digest2 = crypto7.createHash("sha256").update(root).digest("hex").slice(0, 20);
86169
+ const digest2 = crypto8.createHash("sha256").update(root).digest("hex").slice(0, 20);
84331
86170
  const lockPath = path90.join(path90.dirname(root), `.brainbase-benchmark-write-lock-${digest2}`);
84332
86171
  let descriptor;
84333
86172
  try {
@@ -84413,7 +86252,7 @@ function closeDestinationIdentity(identity2) {
84413
86252
  throw cleanupError;
84414
86253
  }
84415
86254
  function createDirectoryClaim(target, identity2) {
84416
- const claimPath = path90.join(target, `.brainbase-benchmark-write-${crypto7.randomUUID()}`);
86255
+ const claimPath = path90.join(target, `.brainbase-benchmark-write-${crypto8.randomUUID()}`);
84417
86256
  try {
84418
86257
  fs82.writeFileSync(claimPath, "", { flag: "wx", mode: 384 });
84419
86258
  } catch (error2) {
@@ -84797,145 +86636,6 @@ function scaffoldBenchmarkDirectory(rootDir, options) {
84797
86636
  return writeBenchmarkDirectory(rootDir, project);
84798
86637
  }
84799
86638
 
84800
- // src/core/argv.ts
84801
- class ArgvParseError extends Error {
84802
- code = "invalid_arguments";
84803
- constructor(message) {
84804
- super(message);
84805
- this.name = "ArgvParseError";
84806
- }
84807
- }
84808
- function parseBoolean(value, name) {
84809
- switch (value.trim().toLowerCase()) {
84810
- case "":
84811
- case "0":
84812
- case "false":
84813
- case "no":
84814
- case "off":
84815
- return false;
84816
- case "1":
84817
- case "true":
84818
- case "yes":
84819
- case "on":
84820
- return true;
84821
- default:
84822
- throw new ArgvParseError(`Unrecognised value for ${name}: ${value}`);
84823
- }
84824
- }
84825
- function validateDefinitions(definitions) {
84826
- const names = new Map;
84827
- const keys2 = new Set;
84828
- for (const definition of definitions) {
84829
- if (!definition.key) {
84830
- throw new Error("argv option keys must not be empty");
84831
- }
84832
- if (keys2.has(definition.key)) {
84833
- throw new Error(`duplicate argv option key: ${definition.key}`);
84834
- }
84835
- keys2.add(definition.key);
84836
- for (const name of definition.names) {
84837
- if (!name.startsWith("-") || name === "-") {
84838
- throw new Error(`invalid argv option name: ${name}`);
84839
- }
84840
- if (name.includes("=")) {
84841
- throw new Error(`argv option names must not contain "=": ${name}`);
84842
- }
84843
- if (names.has(name)) {
84844
- throw new Error(`duplicate argv option name: ${name}`);
84845
- }
84846
- names.set(name, definition);
84847
- }
84848
- }
84849
- return names;
84850
- }
84851
- function findDefinition(token, definitions) {
84852
- const exact = definitions.get(token);
84853
- if (exact)
84854
- return { definition: exact, name: token };
84855
- const equals = token.indexOf("=");
84856
- if (equals < 1)
84857
- return null;
84858
- const name = token.slice(0, equals);
84859
- const definition = definitions.get(name);
84860
- return definition ? { definition, name, joined: token.slice(equals + 1) } : null;
84861
- }
84862
- function assignOption(options, definition, value, name) {
84863
- const current = options[definition.key];
84864
- if (definition.multiple) {
84865
- if (typeof value !== "string") {
84866
- throw new Error(`boolean argv option cannot be repeated: ${name}`);
84867
- }
84868
- options[definition.key] = [
84869
- ...Array.isArray(current) ? current : [],
84870
- value
84871
- ];
84872
- return;
84873
- }
84874
- if (current !== undefined) {
84875
- throw new ArgvParseError(`Option ${name} may only be provided once`);
84876
- }
84877
- options[definition.key] = value;
84878
- }
84879
- function parseArgv(argv, definitions) {
84880
- const byName = validateDefinitions(definitions);
84881
- const options = Object.create(null);
84882
- const positionals = [];
84883
- let optionsEnabled = true;
84884
- for (let index = 0;index < argv.length; index += 1) {
84885
- const token = argv[index];
84886
- if (!optionsEnabled) {
84887
- positionals.push(token);
84888
- continue;
84889
- }
84890
- if (token === "--") {
84891
- optionsEnabled = false;
84892
- continue;
84893
- }
84894
- if (!token.startsWith("-") || token === "-") {
84895
- positionals.push(token);
84896
- continue;
84897
- }
84898
- const hit = findDefinition(token, byName);
84899
- if (!hit) {
84900
- throw new ArgvParseError(`Unknown option: ${token.split("=", 1)[0]}`);
84901
- }
84902
- if (hit.definition.kind === "boolean") {
84903
- assignOption(options, hit.definition, hit.joined === undefined ? true : parseBoolean(hit.joined, hit.name), hit.name);
84904
- continue;
84905
- }
84906
- let value = hit.joined;
84907
- if (value === undefined) {
84908
- const next = argv[index + 1];
84909
- if (next === "--") {
84910
- value = argv[index + 2];
84911
- if (value === undefined) {
84912
- throw new ArgvParseError(`Option ${hit.name} requires a value`);
84913
- }
84914
- index += 2;
84915
- } else {
84916
- if (next === undefined || next.startsWith("-")) {
84917
- throw new ArgvParseError(`Option ${hit.name} requires a value; use ${hit.name}=<value> for values beginning with "-"`);
84918
- }
84919
- value = next;
84920
- index += 1;
84921
- }
84922
- }
84923
- assignOption(options, hit.definition, value, hit.name);
84924
- }
84925
- return { options, positionals };
84926
- }
84927
- function stringOption(parsed, key2) {
84928
- const value = parsed.options[key2];
84929
- return typeof value === "string" ? value : undefined;
84930
- }
84931
- function stringOptions(parsed, key2) {
84932
- const value = parsed.options[key2];
84933
- return Array.isArray(value) ? value : [];
84934
- }
84935
- function booleanOption(parsed, key2) {
84936
- return parsed.options[key2] === true;
84937
- }
84938
-
84939
86639
  // src/cli/benchmark-control-options.ts
84940
86640
  var DEFINITIONS = [
84941
86641
  { key: "agent", names: ["--agent", "-a"], kind: "value" },
@@ -85112,13 +86812,13 @@ function kebab(value) {
85112
86812
  }
85113
86813
 
85114
86814
  // src/cli/benchmark-control-io.ts
85115
- import crypto9 from "node:crypto";
86815
+ import crypto10 from "node:crypto";
85116
86816
  import fs84 from "node:fs";
85117
86817
  import os18 from "node:os";
85118
86818
  import path92 from "node:path";
85119
86819
 
85120
86820
  // src/core/benchmark-bundle.ts
85121
- import crypto8 from "node:crypto";
86821
+ import crypto9 from "node:crypto";
85122
86822
  import fs83 from "node:fs";
85123
86823
  import os17 from "node:os";
85124
86824
  import path91 from "node:path";
@@ -85136,7 +86836,7 @@ var MANIFEST_NAMES = [
85136
86836
  ];
85137
86837
  var MANIFEST_NAME_SET = new Set(MANIFEST_NAMES);
85138
86838
  function sha2562(data) {
85139
- return crypto8.createHash("sha256").update(data).digest("hex");
86839
+ return crypto9.createHash("sha256").update(data).digest("hex");
85140
86840
  }
85141
86841
  function normalizeArchivePath(raw, directory) {
85142
86842
  const candidate = directory ? raw.replace(/\/+$/, "") : raw;
@@ -85267,7 +86967,7 @@ async function writeBenchmarkBundle(outputPath, input) {
85267
86967
  validateMemberTree(portableMembers);
85268
86968
  const output = path91.resolve(outputPath);
85269
86969
  const staging = fs83.mkdtempSync(path91.join(os17.tmpdir(), "brainbase-benchmark-bundle-"));
85270
- const temporaryOutput = `${output}.${process.pid}.${crypto8.randomUUID()}.tmp`;
86970
+ const temporaryOutput = `${output}.${process.pid}.${crypto9.randomUUID()}.tmp`;
85271
86971
  try {
85272
86972
  const manifestData = Buffer.from(`${canonicalBenchmarkJson(project.manifest)}
85273
86973
  `, "utf8");
@@ -85530,7 +87230,7 @@ function writeBinaryOutput(cwd2, output, bytes, fallbackName, force) {
85530
87230
  const target = path92.resolve(cwd2, output ?? safeExportFilename(undefined, fallbackName));
85531
87231
  fs84.mkdirSync(path92.dirname(target), { recursive: true });
85532
87232
  assertSafeOutputDestination(target, force);
85533
- const temporary = `${target}.${process.pid}.${crypto9.randomUUID()}.tmp`;
87233
+ const temporary = `${target}.${process.pid}.${crypto10.randomUUID()}.tmp`;
85534
87234
  const descriptor = fs84.openSync(temporary, "wx", 384);
85535
87235
  let closed = false;
85536
87236
  try {
@@ -85804,20 +87504,20 @@ async function emitCursorPages(output, fetchPage, options) {
85804
87504
  function commandHelp() {
85805
87505
  return [
85806
87506
  "",
85807
- ` ${import_picocolors53.default.bold("brainbase benchmark")} ${import_picocolors53.default.dim("<command> [options]")}`,
87507
+ ` ${import_picocolors60.default.bold("brainbase benchmark")} ${import_picocolors60.default.dim("<command> [options]")}`,
85808
87508
  "",
85809
- ` ${import_picocolors53.default.cyan("init | list | show | create | validate | pull | push")}`,
85810
- ` ${import_picocolors53.default.cyan("publish | revisions | import | export | archive | restore")}`,
85811
- ` ${import_picocolors53.default.cyan("run plan|start|watch|list|show|cancel")}`,
85812
- ` ${import_picocolors53.default.cyan("results | diagnoses | attempt | artifacts | export-results")}`,
85813
- ` ${import_picocolors53.default.cyan("history | baseline show|set|clear")}`,
87509
+ ` ${import_picocolors60.default.cyan("init | list | show | create | validate | pull | push")}`,
87510
+ ` ${import_picocolors60.default.cyan("publish | revisions | import | export | archive | restore")}`,
87511
+ ` ${import_picocolors60.default.cyan("run plan|start|watch|list|show|cancel")}`,
87512
+ ` ${import_picocolors60.default.cyan("results | diagnoses | attempt | artifacts | export-results")}`,
87513
+ ` ${import_picocolors60.default.cyan("history | baseline show|set|clear")}`,
85814
87514
  "",
85815
- ` ${import_picocolors53.default.dim("Use --agent to override brainbase.agent.yaml, and --json or --jsonl for automation.")}`,
87515
+ ` ${import_picocolors60.default.dim("Use --agent to override brainbase.agent.yaml, and --json or --jsonl for automation.")}`,
85816
87516
  "",
85817
- ` ${import_picocolors53.default.bold("benchmark runtime (machine-only)")}`,
85818
- ` ${import_picocolors53.default.cyan("hydrate")} ${import_picocolors53.default.dim("--spec <path> --result <path> --json")}`,
85819
- ` ${import_picocolors53.default.cyan("evaluate")} ${import_picocolors53.default.dim("--spec <path> --result <path> --json")}`,
85820
- ` ${import_picocolors53.default.cyan("capabilities")} ${import_picocolors53.default.dim("--json")}`,
87517
+ ` ${import_picocolors60.default.bold("benchmark runtime (machine-only)")}`,
87518
+ ` ${import_picocolors60.default.cyan("hydrate")} ${import_picocolors60.default.dim("--spec <path> --result <path> --json")}`,
87519
+ ` ${import_picocolors60.default.cyan("evaluate")} ${import_picocolors60.default.dim("--spec <path> --result <path> --json")}`,
87520
+ ` ${import_picocolors60.default.cyan("capabilities")} ${import_picocolors60.default.dim("--json")}`,
85821
87521
  ""
85822
87522
  ].join(`
85823
87523
  `);
@@ -86149,19 +87849,19 @@ async function runExport(cwd2, argv, deps) {
86149
87849
  ]);
86150
87850
  expectPositionals(options.positionals, 1, 1, "brainbase benchmark export <benchmark-id> --format <yaml|json|bundle>");
86151
87851
  const benchmarkId = options.positionals[0];
86152
- const format = optionalNonblank(options.parsed, "format") ?? "yaml";
86153
- if (!["yaml", "json", "bundle"].includes(format)) {
87852
+ const format2 = optionalNonblank(options.parsed, "format") ?? "yaml";
87853
+ if (!["yaml", "json", "bundle"].includes(format2)) {
86154
87854
  throw usageError("--format must be yaml, json, or bundle");
86155
87855
  }
86156
87856
  const { client, output } = context(cwd2, "benchmark export", options, deps);
86157
87857
  const revision = optionalNonblank(options.parsed, "revision");
86158
- const response = format === "bundle" ? await client.exportBundle(benchmarkId, revision) : await client.exportManifest(benchmarkId, format, revision);
86159
- const extension2 = format === "bundle" ? "tar.gz" : format;
87858
+ const response = format2 === "bundle" ? await client.exportBundle(benchmarkId, revision) : await client.exportManifest(benchmarkId, format2, revision);
87859
+ const extension2 = format2 === "bundle" ? "tar.gz" : format2;
86160
87860
  const fallback = safeExportFilename(response.filename, `benchmark-${benchmarkId}.${extension2}`);
86161
87861
  const written = writeBinaryOutput(cwd2, optionalNonblank(options.parsed, "output"), response.bytes, fallback, bool(options.parsed, "force"));
86162
87862
  output.result({
86163
87863
  benchmark_id: benchmarkId,
86164
- format,
87864
+ format: format2,
86165
87865
  path: written,
86166
87866
  size_bytes: response.bytes.byteLength
86167
87867
  });
@@ -86971,7 +88671,7 @@ function terminatePhase(child) {
86971
88671
  }
86972
88672
  }
86973
88673
  function createAnonymousSpecFd(bytes) {
86974
- const temporary = path94.join(os19.tmpdir(), `brainbase-benchmark-spec-${process.pid}-${crypto10.randomBytes(12).toString("hex")}`);
88674
+ const temporary = path94.join(os19.tmpdir(), `brainbase-benchmark-spec-${process.pid}-${crypto11.randomBytes(12).toString("hex")}`);
86975
88675
  fs86.writeFileSync(temporary, bytes, { flag: "wx", mode: 384 });
86976
88676
  try {
86977
88677
  const fd = fs86.openSync(temporary, "r");
@@ -87015,7 +88715,7 @@ async function runSupervisedPhase(phase, parsed, write) {
87015
88715
  `);
87016
88716
  return 1;
87017
88717
  }
87018
- const childToken = crypto10.randomBytes(32).toString("hex");
88718
+ const childToken = crypto11.randomBytes(32).toString("hex");
87019
88719
  let specFd;
87020
88720
  try {
87021
88721
  specFd = createAnonymousSpecFd(specBytes);
@@ -87200,148 +88900,161 @@ var SUBCOMMAND_OWNED_FLAGS = {
87200
88900
  function help() {
87201
88901
  const out = [];
87202
88902
  out.push("");
87203
- out.push(` ${brandTint("◆")} ${import_picocolors54.default.bold("brainbase")} ${import_picocolors54.default.dim(`v${VERSION}`)}`);
87204
- out.push(` ${import_picocolors54.default.dim("connect your local agent to the brainbase platform")}`);
88903
+ out.push(` ${brandTint("◆")} ${import_picocolors61.default.bold("brainbase")} ${import_picocolors61.default.dim(`v${VERSION}`)}`);
88904
+ out.push(` ${import_picocolors61.default.dim("connect your local agent to the brainbase platform")}`);
87205
88905
  out.push("");
87206
88906
  out.push(divider("USAGE"));
87207
88907
  out.push("");
87208
- out.push(` ${import_picocolors54.default.bold("brainbase")} ${import_picocolors54.default.dim("<command> [options]")}`);
88908
+ out.push(` ${import_picocolors61.default.bold("brainbase")} ${import_picocolors61.default.dim("<command> [options]")}`);
87209
88909
  out.push("");
87210
88910
  out.push(divider("AUTH"));
87211
88911
  out.push("");
87212
- out.push(` ${import_picocolors54.default.cyan("login")} ${import_picocolors54.default.dim(" open the web app and connect this device")}`);
87213
- out.push(` ${import_picocolors54.default.cyan("logout")} ${import_picocolors54.default.dim(" clear the local session")}`);
87214
- out.push(` ${import_picocolors54.default.cyan("whoami")} ${import_picocolors54.default.dim("[--json]")} ${import_picocolors54.default.dim(" show which credential is in use and what it covers")}`);
88912
+ out.push(` ${import_picocolors61.default.cyan("login")} ${import_picocolors61.default.dim(" open the web app and connect this device")}`);
88913
+ out.push(` ${import_picocolors61.default.cyan("logout")} ${import_picocolors61.default.dim(" clear the local session")}`);
88914
+ out.push(` ${import_picocolors61.default.cyan("whoami")} ${import_picocolors61.default.dim("[--json]")} ${import_picocolors61.default.dim(" show which credential is in use and what it covers")}`);
87215
88915
  out.push("");
87216
88916
  out.push(divider("DISCOVERY"));
87217
88917
  out.push("");
87218
- out.push(` ${import_picocolors54.default.cyan("team list")} ${import_picocolors54.default.dim("show the teams you can create agents in")}`);
87219
- out.push(` ${import_picocolors54.default.cyan("agent list")} ${import_picocolors54.default.dim("show a team's agents and their ids")}`);
88918
+ out.push(` ${import_picocolors61.default.cyan("team list")} ${import_picocolors61.default.dim("show the teams you can create agents in")}`);
88919
+ out.push(` ${import_picocolors61.default.cyan("agent list")} ${import_picocolors61.default.dim("show a team's agents and their ids")}`);
87220
88920
  out.push("");
87221
88921
  out.push(divider("LINKED AGENT"));
87222
88922
  out.push("");
87223
- out.push(` ${import_picocolors54.default.cyan("agent init")} ${import_picocolors54.default.dim("write a starter brainbase.agent.yaml here — offline, no login needed")}`);
87224
- out.push(` ${import_picocolors54.default.cyan("agent create")} ${import_picocolors54.default.dim("claim an unclaimed brainbase.agent.yaml and create the cloud agent")}`);
87225
- out.push(` ${import_picocolors54.default.cyan("agent pull")} ${import_picocolors54.default.dim("[<id>]")} ${import_picocolors54.default.dim("bring cloud changes into this folder (--force to override; --run-entrypoint to also execute the agent entrypoint)")}`);
87226
- out.push(` ${import_picocolors54.default.cyan("agent push")} ${import_picocolors54.default.dim("send local changes to the cloud (--force to overwrite cloud-side conflicts with local)")}`);
87227
- out.push(` ${import_picocolors54.default.cyan("agent unpack")} ${import_picocolors54.default.dim("install the claimed agent into a harness layout")}`);
87228
- out.push(` ${import_picocolors54.default.cyan("link")} ${import_picocolors54.default.dim("attach this folder to an existing agent")}`);
87229
- out.push(` ${import_picocolors54.default.cyan("agent status")} ${import_picocolors54.default.dim("show what would pull and what would push")}`);
87230
- out.push(` ${import_picocolors54.default.cyan("agent connections")} ${import_picocolors54.default.dim("show which integrations this agent is wired to (--json for CI)")}`);
87231
- out.push(` ${import_picocolors54.default.cyan("agent connect")} ${import_picocolors54.default.dim("<name>")} ${import_picocolors54.default.dim("connect slack or meeting from the terminal")}`);
87232
- out.push(` ${import_picocolors54.default.cyan("agent disconnect")} ${import_picocolors54.default.dim("<name>")} ${import_picocolors54.default.dim("revoke a slack or meeting install")}`);
87233
- out.push(` ${import_picocolors54.default.cyan("agent env")} ${import_picocolors54.default.dim("print export lines for `eval $(brainbase agent env)`")}`);
87234
- out.push(` ${import_picocolors54.default.cyan("run")} ${import_picocolors54.default.dim("<cmd> [args...]")} ${import_picocolors54.default.dim("run <cmd> with secrets.env loaded into env")}`);
87235
- out.push(` ${import_picocolors54.default.cyan("status")} ${import_picocolors54.default.dim("show what this folder is linked to")}`);
87236
- out.push(` ${import_picocolors54.default.cyan("unlink")} ${import_picocolors54.default.dim("disconnect this folder")}`);
88923
+ out.push(` ${import_picocolors61.default.cyan("agent init")} ${import_picocolors61.default.dim("write a starter brainbase.agent.yaml here — offline, no login needed")}`);
88924
+ out.push(` ${import_picocolors61.default.cyan("agent create")} ${import_picocolors61.default.dim("claim an unclaimed brainbase.agent.yaml and create the cloud agent")}`);
88925
+ out.push(` ${import_picocolors61.default.cyan("agent pull")} ${import_picocolors61.default.dim("[<id>]")} ${import_picocolors61.default.dim("bring cloud changes into this folder (--force to override; --run-entrypoint to also execute the agent entrypoint)")}`);
88926
+ out.push(` ${import_picocolors61.default.cyan("agent push")} ${import_picocolors61.default.dim("send local changes to the cloud (--force to overwrite cloud-side conflicts with local)")}`);
88927
+ out.push(` ${import_picocolors61.default.cyan("agent unpack")} ${import_picocolors61.default.dim("install the claimed agent into a harness layout")}`);
88928
+ out.push(` ${import_picocolors61.default.cyan("link")} ${import_picocolors61.default.dim("attach this folder to an existing agent")}`);
88929
+ out.push(` ${import_picocolors61.default.cyan("agent status")} ${import_picocolors61.default.dim("show what would pull and what would push")}`);
88930
+ out.push(` ${import_picocolors61.default.cyan("agent connections")} ${import_picocolors61.default.dim("show which integrations this agent is wired to (--json for CI)")}`);
88931
+ out.push(` ${import_picocolors61.default.cyan("agent connect")} ${import_picocolors61.default.dim("<name>")} ${import_picocolors61.default.dim("connect slack or meeting from the terminal")}`);
88932
+ out.push(` ${import_picocolors61.default.cyan("agent disconnect")} ${import_picocolors61.default.dim("<name>")} ${import_picocolors61.default.dim("revoke a slack or meeting install")}`);
88933
+ out.push(` ${import_picocolors61.default.cyan("agent env")} ${import_picocolors61.default.dim("print export lines for `eval $(brainbase agent env)`")}`);
88934
+ out.push(` ${import_picocolors61.default.cyan("run")} ${import_picocolors61.default.dim("<cmd> [args...]")} ${import_picocolors61.default.dim("run <cmd> with secrets.env loaded into env")}`);
88935
+ out.push(` ${import_picocolors61.default.cyan("status")} ${import_picocolors61.default.dim("show what this folder is linked to")}`);
88936
+ out.push(` ${import_picocolors61.default.cyan("unlink")} ${import_picocolors61.default.dim("disconnect this folder")}`);
87237
88937
  out.push("");
87238
88938
  out.push(divider("TASKS"));
87239
88939
  out.push("");
87240
- out.push(` ${import_picocolors54.default.cyan("task create")} ${import_picocolors54.default.dim("--message <text>")} ${import_picocolors54.default.dim("create a managed task and start its first run")}`);
88940
+ out.push(` ${import_picocolors61.default.cyan("task create")} ${import_picocolors61.default.dim("--message <text>")} ${import_picocolors61.default.dim("create a managed task and start its first run (--wait to block on it)")}`);
88941
+ out.push(` ${import_picocolors61.default.cyan("task list")} ${import_picocolors61.default.dim("recent tasks you can reach")}`);
88942
+ out.push(` ${import_picocolors61.default.cyan("task get")} ${import_picocolors61.default.dim("<task-id>")} ${import_picocolors61.default.dim("one task: status, agent, machine, eval verdicts")}`);
88943
+ out.push(` ${import_picocolors61.default.cyan("task logs")} ${import_picocolors61.default.dim("<task-id> [--follow]")} ${import_picocolors61.default.dim("one line per event in the task's transcript, or tail it live")}`);
88944
+ out.push("");
88945
+ out.push(divider("SANDBOXES"));
88946
+ out.push("");
88947
+ out.push(` ${import_picocolors61.default.cyan("machine ls")} ${import_picocolors61.default.dim("[--all]")} ${import_picocolors61.default.dim("list your sandboxes — live ones only unless --all")}`);
88948
+ out.push(` ${import_picocolors61.default.cyan("machine rm")} ${import_picocolors61.default.dim("<id>")} ${import_picocolors61.default.dim("tear a sandbox down (it bills until you do)")}`);
87241
88949
  out.push("");
87242
88950
  out.push(divider("BENCHMARKS"));
87243
88951
  out.push("");
87244
- out.push(` ${import_picocolors54.default.cyan("benchmark list")} ${import_picocolors54.default.dim("list benchmarks for the linked agent")}`);
87245
- out.push(` ${import_picocolors54.default.cyan("benchmark init")} ${import_picocolors54.default.dim("[directory]")} ${import_picocolors54.default.dim("scaffold a local benchmark")}`);
87246
- out.push(` ${import_picocolors54.default.cyan("benchmark create")} ${import_picocolors54.default.dim("[path|-]")} ${import_picocolors54.default.dim("create a benchmark draft")}`);
87247
- out.push(` ${import_picocolors54.default.cyan("benchmark run")} ${import_picocolors54.default.dim("<benchmark> --yes")} ${import_picocolors54.default.dim("plan and start a benchmark run")}`);
87248
- out.push(` ${import_picocolors54.default.cyan("benchmark results")} ${import_picocolors54.default.dim("<run>")} ${import_picocolors54.default.dim("inspect normalized benchmark results")}`);
88952
+ out.push(` ${import_picocolors61.default.cyan("benchmark list")} ${import_picocolors61.default.dim("list benchmarks for the linked agent")}`);
88953
+ out.push(` ${import_picocolors61.default.cyan("benchmark init")} ${import_picocolors61.default.dim("[directory]")} ${import_picocolors61.default.dim("scaffold a local benchmark")}`);
88954
+ out.push(` ${import_picocolors61.default.cyan("benchmark create")} ${import_picocolors61.default.dim("[path|-]")} ${import_picocolors61.default.dim("create a benchmark draft")}`);
88955
+ out.push(` ${import_picocolors61.default.cyan("benchmark run")} ${import_picocolors61.default.dim("<benchmark> --yes")} ${import_picocolors61.default.dim("plan and start a benchmark run")}`);
88956
+ out.push(` ${import_picocolors61.default.cyan("benchmark results")} ${import_picocolors61.default.dim("<run>")} ${import_picocolors61.default.dim("inspect normalized benchmark results")}`);
87249
88957
  out.push("");
87250
88958
  out.push(divider("BENCHMARK RUNTIME"));
87251
88959
  out.push("");
87252
- out.push(` ${import_picocolors54.default.cyan("benchmark hydrate")} ${import_picocolors54.default.dim("--spec <path> --result <path> --json")}`);
87253
- out.push(` ${import_picocolors54.default.cyan("benchmark evaluate")} ${import_picocolors54.default.dim("--spec <path> --result <path> --json")}`);
87254
- out.push(` ${import_picocolors54.default.cyan("benchmark capabilities")} ${import_picocolors54.default.dim("--json")}`);
88960
+ out.push(` ${import_picocolors61.default.cyan("benchmark hydrate")} ${import_picocolors61.default.dim("--spec <path> --result <path> --json")}`);
88961
+ out.push(` ${import_picocolors61.default.cyan("benchmark evaluate")} ${import_picocolors61.default.dim("--spec <path> --result <path> --json")}`);
88962
+ out.push(` ${import_picocolors61.default.cyan("benchmark capabilities")} ${import_picocolors61.default.dim("--json")}`);
87255
88963
  out.push("");
87256
88964
  out.push(divider("ORCHESTRATIONS"));
87257
88965
  out.push("");
87258
- out.push(` ${import_picocolors54.default.cyan("orchestration create")} ${import_picocolors54.default.dim("claim a local orchestration manifest and create it in the cloud")}`);
87259
- out.push(` ${import_picocolors54.default.cyan("orchestration list")} ${import_picocolors54.default.dim("list orchestrations under a team")}`);
87260
- out.push(` ${import_picocolors54.default.cyan("orchestration pull")} ${import_picocolors54.default.dim("<id>")} ${import_picocolors54.default.dim("recursively fetch an orchestration + every member agent")}`);
87261
- out.push(` ${import_picocolors54.default.cyan("orchestration push")} ${import_picocolors54.default.dim("recursively push each member, then update the graph")}`);
87262
- out.push(` ${import_picocolors54.default.cyan("orchestration status")} ${import_picocolors54.default.dim("show what would push and what would pull")}`);
88966
+ out.push(` ${import_picocolors61.default.cyan("orchestration create")} ${import_picocolors61.default.dim("claim a local orchestration manifest and create it in the cloud")}`);
88967
+ out.push(` ${import_picocolors61.default.cyan("orchestration list")} ${import_picocolors61.default.dim("list orchestrations under a team")}`);
88968
+ out.push(` ${import_picocolors61.default.cyan("orchestration pull")} ${import_picocolors61.default.dim("<id>")} ${import_picocolors61.default.dim("recursively fetch an orchestration + every member agent")}`);
88969
+ out.push(` ${import_picocolors61.default.cyan("orchestration push")} ${import_picocolors61.default.dim("recursively push each member, then update the graph")}`);
88970
+ out.push(` ${import_picocolors61.default.cyan("orchestration status")} ${import_picocolors61.default.dim("show what would push and what would pull")}`);
87263
88971
  out.push("");
87264
88972
  out.push(divider("TEMPLATES"));
87265
88973
  out.push("");
87266
- out.push(` ${import_picocolors54.default.cyan("template pack")} ${import_picocolors54.default.dim("bundle the current agent into a template")}`);
87267
- out.push(` ${import_picocolors54.default.cyan("template publish")} ${import_picocolors54.default.dim("upload a template to the registry")}`);
87268
- out.push(` ${import_picocolors54.default.cyan("template search")} ${import_picocolors54.default.dim("[query]")} ${import_picocolors54.default.dim("search the registry")}`);
87269
- out.push(` ${import_picocolors54.default.cyan("template info")} ${import_picocolors54.default.dim("<creator/slug>")} ${import_picocolors54.default.dim("show registry details for a template")}`);
87270
- out.push(` ${import_picocolors54.default.cyan("template onboard")} ${import_picocolors54.default.dim("<creator/slug>")} ${import_picocolors54.default.dim("install (or refresh) a template")}`);
87271
- out.push(` ${import_picocolors54.default.cyan("template list")} ${import_picocolors54.default.dim("show installed templates")}`);
87272
- out.push(` ${import_picocolors54.default.cyan("template remove")} ${import_picocolors54.default.dim("<creator/slug>")} ${import_picocolors54.default.dim("uninstall a template")}`);
88974
+ out.push(` ${import_picocolors61.default.cyan("template pack")} ${import_picocolors61.default.dim("bundle the current agent into a template")}`);
88975
+ out.push(` ${import_picocolors61.default.cyan("template publish")} ${import_picocolors61.default.dim("upload a template to the registry")}`);
88976
+ out.push(` ${import_picocolors61.default.cyan("template search")} ${import_picocolors61.default.dim("[query]")} ${import_picocolors61.default.dim("search the registry")}`);
88977
+ out.push(` ${import_picocolors61.default.cyan("template info")} ${import_picocolors61.default.dim("<creator/slug>")} ${import_picocolors61.default.dim("show registry details for a template")}`);
88978
+ out.push(` ${import_picocolors61.default.cyan("template onboard")} ${import_picocolors61.default.dim("<creator/slug>")} ${import_picocolors61.default.dim("install (or refresh) a template")}`);
88979
+ out.push(` ${import_picocolors61.default.cyan("template list")} ${import_picocolors61.default.dim("show installed templates")}`);
88980
+ out.push(` ${import_picocolors61.default.cyan("template remove")} ${import_picocolors61.default.dim("<creator/slug>")} ${import_picocolors61.default.dim("uninstall a template")}`);
87273
88981
  out.push("");
87274
88982
  out.push(divider("SKILLS"));
87275
88983
  out.push("");
87276
- out.push(` ${import_picocolors54.default.cyan("skill add")} ${import_picocolors54.default.dim("<source>")} ${import_picocolors54.default.dim("install a skill (github / git / brainbase)")}`);
87277
- out.push(` ${import_picocolors54.default.cyan("skill list")} ${import_picocolors54.default.dim("show locally installed skills + their source")}`);
87278
- out.push(` ${import_picocolors54.default.cyan("skill update")} ${import_picocolors54.default.dim("<slug>")} ${import_picocolors54.default.dim("re-fetch a skill from its recorded source")}`);
87279
- out.push(` ${import_picocolors54.default.cyan("skill remove")} ${import_picocolors54.default.dim("<slug>")} ${import_picocolors54.default.dim("uninstall a skill")}`);
87280
- out.push(` ${import_picocolors54.default.cyan("skill search")} ${import_picocolors54.default.dim("[query]")} ${import_picocolors54.default.dim("search the brainbase skill registry")}`);
87281
- out.push(` ${import_picocolors54.default.cyan("skill info")} ${import_picocolors54.default.dim("<creator/slug>")} ${import_picocolors54.default.dim("show registry details for a skill")}`);
87282
- out.push(` ${import_picocolors54.default.cyan("skill publish")} ${import_picocolors54.default.dim("[dir]")} ${import_picocolors54.default.dim("publish a SKILL.md folder (defaults to .)")}`);
88984
+ out.push(` ${import_picocolors61.default.cyan("skill add")} ${import_picocolors61.default.dim("<source>")} ${import_picocolors61.default.dim("install a skill (github / git / brainbase)")}`);
88985
+ out.push(` ${import_picocolors61.default.cyan("skill list")} ${import_picocolors61.default.dim("show locally installed skills + their source")}`);
88986
+ out.push(` ${import_picocolors61.default.cyan("skill update")} ${import_picocolors61.default.dim("<slug>")} ${import_picocolors61.default.dim("re-fetch a skill from its recorded source")}`);
88987
+ out.push(` ${import_picocolors61.default.cyan("skill remove")} ${import_picocolors61.default.dim("<slug>")} ${import_picocolors61.default.dim("uninstall a skill")}`);
88988
+ out.push(` ${import_picocolors61.default.cyan("skill search")} ${import_picocolors61.default.dim("[query]")} ${import_picocolors61.default.dim("search the brainbase skill registry")}`);
88989
+ out.push(` ${import_picocolors61.default.cyan("skill info")} ${import_picocolors61.default.dim("<creator/slug>")} ${import_picocolors61.default.dim("show registry details for a skill")}`);
88990
+ out.push(` ${import_picocolors61.default.cyan("skill publish")} ${import_picocolors61.default.dim("[dir]")} ${import_picocolors61.default.dim("publish a SKILL.md folder (defaults to .)")}`);
87283
88991
  out.push("");
87284
88992
  out.push(divider("CLI TOKENS"));
87285
88993
  out.push("");
87286
- out.push(` ${import_picocolors54.default.cyan("token create")} ${import_picocolors54.default.dim("issue a long-lived CLI key for CI / scripts")}`);
87287
- out.push(` ${import_picocolors54.default.cyan("token list")} ${import_picocolors54.default.dim("show your tokens")}`);
87288
- out.push(` ${import_picocolors54.default.cyan("token rename")} ${import_picocolors54.default.dim("<id>")} ${import_picocolors54.default.dim("relabel a token")}`);
87289
- out.push(` ${import_picocolors54.default.cyan("token revoke")} ${import_picocolors54.default.dim("<id>")} ${import_picocolors54.default.dim("revoke a token")}`);
88994
+ out.push(` ${import_picocolors61.default.cyan("token create")} ${import_picocolors61.default.dim("issue a long-lived CLI key for CI / scripts")}`);
88995
+ out.push(` ${import_picocolors61.default.cyan("token list")} ${import_picocolors61.default.dim("show your tokens")}`);
88996
+ out.push(` ${import_picocolors61.default.cyan("token rename")} ${import_picocolors61.default.dim("<id>")} ${import_picocolors61.default.dim("relabel a token")}`);
88997
+ out.push(` ${import_picocolors61.default.cyan("token revoke")} ${import_picocolors61.default.dim("<id>")} ${import_picocolors61.default.dim("revoke a token")}`);
87290
88998
  out.push("");
87291
88999
  out.push(divider("MCP"));
87292
89000
  out.push("");
87293
- out.push(` ${import_picocolors54.default.cyan("mcp check")} ${import_picocolors54.default.dim("[--json]")} ${import_picocolors54.default.dim("verify MCP server connectivity through the brainbase proxy (runs at sandbox bootstrap)")}`);
87294
- out.push(` ${import_picocolors54.default.cyan("mcp list")} ${import_picocolors54.default.dim("[--json]")} ${import_picocolors54.default.dim("show configured servers with OAuth state and expiry")}`);
89001
+ out.push(` ${import_picocolors61.default.cyan("mcp check")} ${import_picocolors61.default.dim("[--json]")} ${import_picocolors61.default.dim("verify MCP server connectivity through the brainbase proxy (runs at sandbox bootstrap)")}`);
89002
+ out.push(` ${import_picocolors61.default.cyan("mcp list")} ${import_picocolors61.default.dim("[--json]")} ${import_picocolors61.default.dim("show configured servers with OAuth state and expiry")}`);
87295
89003
  out.push("");
87296
89004
  out.push(divider("FLAGS"));
87297
89005
  out.push("");
87298
- out.push(` ${import_picocolors54.default.dim("--harness <id>")} force harness for onboard / sync (e.g. claude-code)`);
87299
- out.push(` ${import_picocolors54.default.dim("--scope <s>")} force scope: global | project`);
87300
- out.push(` ${import_picocolors54.default.dim("--yes, -y")} skip confirmations / auto-overwrite`);
87301
- out.push(` ${import_picocolors54.default.dim("--agent <id>")} for link/task create: use this agent id explicitly`);
87302
- out.push(` ${import_picocolors54.default.dim("--message <text>")} for task create: required first user message`);
87303
- out.push(` ${import_picocolors54.default.dim("--title <text>")} for task create: optional task title`);
87304
- out.push(` ${import_picocolors54.default.dim("--model <id>")} for task create: optional model override`);
87305
- out.push(` ${import_picocolors54.default.dim("--org <id-or-slug>")} pick the organization (team/agent list, agent create, orchestration create/list)`);
87306
- out.push(` ${import_picocolors54.default.dim("--team <id>")} pick the team, same commands (works without --org)`);
87307
- out.push(` ${import_picocolors54.default.dim("--json")} machine-readable output for supported commands`);
87308
- out.push(` ${import_picocolors54.default.dim("--no-tracking")} for link: skip routing LLM traffic through brainbase`);
87309
- out.push(` ${import_picocolors54.default.dim("--track")} for agent create: enable tracking non-interactively (off without a TTY)`);
87310
- out.push(` ${import_picocolors54.default.dim("--shell <sh|fish>")} for agent env: pick output format (auto-detected from $SHELL)`);
87311
- out.push(` ${import_picocolors54.default.dim("--bot-token <t>")} for agent connect slack (or BRAINBASE_SLACK_BOT_TOKEN, or stdin)`);
87312
- out.push(` ${import_picocolors54.default.dim("--signing-secret <s>")} for agent connect slack (or BRAINBASE_SLACK_SIGNING_SECRET, or stdin)`);
87313
- out.push(` ${import_picocolors54.default.dim("--bot-name <name>")} for agent connect meeting: the bot's display name`);
87314
- out.push(` ${import_picocolors54.default.dim("--full")} for agent init: write a commented template covering every block`);
87315
- out.push(` ${import_picocolors54.default.dim("--minimal")} for agent init: write the starter manifest (the default)`);
87316
- out.push(` ${import_picocolors54.default.dim("--all")} for template list: include installs from other folders`);
87317
- out.push(` ${import_picocolors54.default.dim("--web <url>")} for login: web app URL (default https://app.brainbaselabs.com)`);
89006
+ out.push(` ${import_picocolors61.default.dim("--harness <id>")} force harness for onboard / sync (e.g. claude-code)`);
89007
+ out.push(` ${import_picocolors61.default.dim("--scope <s>")} force scope: global | project`);
89008
+ out.push(` ${import_picocolors61.default.dim("--yes, -y")} skip confirmations / auto-overwrite`);
89009
+ out.push(` ${" ".repeat("--yes, -y".length)} required for destructive commands with no terminal (pipes, CI)`);
89010
+ out.push(` ${import_picocolors61.default.dim("--agent <id>")} for link/task create: use this agent id explicitly (task list: filter to it)`);
89011
+ out.push(` ${import_picocolors61.default.dim("--message <text>")} for task create: required first user message`);
89012
+ out.push(` ${import_picocolors61.default.dim("--title <text>")} for task create: optional task title`);
89013
+ out.push(` ${import_picocolors61.default.dim("--model <id>")} for task create: optional model override`);
89014
+ out.push(` ${import_picocolors61.default.dim("--wait")} for task create: block until the task finishes; exit 1 unless it succeeded`);
89015
+ out.push(` ${import_picocolors61.default.dim("--timeout <secs>")} for task create --wait: give up after <secs> and exit 1`);
89016
+ out.push(` ${import_picocolors61.default.dim("--limit <n>")} for task list (1-200), task logs (events to print; with --follow, the first page size) and machine ls`);
89017
+ out.push(` ${import_picocolors61.default.dim("--follow, -f")} for task logs: stay attached and print events as they land`);
89018
+ out.push(` ${import_picocolors61.default.dim("--org <id-or-slug>")} pick the organization (team/agent list, agent create, orchestration create/list)`);
89019
+ out.push(` ${import_picocolors61.default.dim("--team <id>")} pick the team, same commands (works without --org)`);
89020
+ out.push(` ${import_picocolors61.default.dim("--json")} machine-readable output for supported commands`);
89021
+ out.push(` ${import_picocolors61.default.dim("--no-tracking")} for link: skip routing LLM traffic through brainbase`);
89022
+ out.push(` ${import_picocolors61.default.dim("--track")} for agent create: enable tracking non-interactively (off without a TTY)`);
89023
+ out.push(` ${import_picocolors61.default.dim("--shell <sh|fish>")} for agent env: pick output format (auto-detected from $SHELL)`);
89024
+ out.push(` ${import_picocolors61.default.dim("--bot-token <t>")} for agent connect slack (or BRAINBASE_SLACK_BOT_TOKEN, or stdin)`);
89025
+ out.push(` ${import_picocolors61.default.dim("--signing-secret <s>")} for agent connect slack (or BRAINBASE_SLACK_SIGNING_SECRET, or stdin)`);
89026
+ out.push(` ${import_picocolors61.default.dim("--bot-name <name>")} for agent connect meeting: the bot's display name`);
89027
+ out.push(` ${import_picocolors61.default.dim("--full")} for agent init: write a commented template covering every block`);
89028
+ out.push(` ${import_picocolors61.default.dim("--minimal")} for agent init: write the starter manifest (the default)`);
89029
+ out.push(` ${import_picocolors61.default.dim("--all")} for template list: include installs from other folders; for machine ls: include torn-down machines`);
89030
+ out.push(` ${import_picocolors61.default.dim("--web <url>")} for login: web app URL (default https://app.brainbaselabs.com)`);
87318
89031
  out.push("");
87319
89032
  out.push(divider("ENV"));
87320
89033
  out.push("");
87321
- out.push(` ${import_picocolors54.default.dim("BRAINBASE_HOME")} override the local config dir (default ~/.brainbase)`);
87322
- out.push(` ${import_picocolors54.default.dim("BRAINBASE_DEBUG")} print full stack traces on error (any value; unset to disable)`);
87323
- out.push(` ${import_picocolors54.default.dim("BRAINBASE_WEB_URL")} override the web app URL used by login`);
87324
- out.push(` ${import_picocolors54.default.dim("BRAINBASE_CONTROL_PLANE_URL")} override the MAS host (/v2/cli; task create uses /v2/tasks)`);
87325
- out.push(` ${import_picocolors54.default.dim("BRAINBASE_API_URL")} legacy KLS host override (uses /api/cli; proxy/registry fallback)`);
87326
- out.push(` ${import_picocolors54.default.dim("BRAINBASE_PROXY_URL")} override the model-proxy URL used by harness traffic (default https://api.v1.brainbaselabs.com)`);
87327
- out.push(` ${import_picocolors54.default.dim("BRAINBASE_REGISTRY_URL")} override the registry API URL (default https://api.v1.brainbaselabs.com)`);
87328
- out.push(` ${import_picocolors54.default.dim("BRAINBASE_TOKEN")} long-lived CLI PAT; the only PAT control-plane commands accept (token.json is not read there)`);
87329
- out.push(` ${import_picocolors54.default.dim("BRAINBASE_SKIP_AUTH")} bypass the auth gate for development`);
87330
- out.push(` ${import_picocolors54.default.dim("BRAINBASE_NON_INTERACTIVE")} force non-interactive mode — skip/auto-default prompts (CI & agents)`);
87331
- out.push(` ${import_picocolors54.default.dim("BRAINBASE_RUN_ENTRYPOINT")} =1 → agent pull executes the agent entrypoint (sandbox boots; or pass --run-entrypoint)`);
89034
+ out.push(` ${import_picocolors61.default.dim("BRAINBASE_HOME")} override the local config dir (default ~/.brainbase)`);
89035
+ out.push(` ${import_picocolors61.default.dim("BRAINBASE_DEBUG")} print full stack traces on error (any value; unset to disable)`);
89036
+ out.push(` ${import_picocolors61.default.dim("BRAINBASE_WEB_URL")} override the web app URL used by login`);
89037
+ out.push(` ${import_picocolors61.default.dim("BRAINBASE_CONTROL_PLANE_URL")} override the MAS host (/v2/cli; task create uses /v2/tasks)`);
89038
+ out.push(` ${import_picocolors61.default.dim("BRAINBASE_API_URL")} legacy KLS host override (uses /api/cli; proxy/registry fallback)`);
89039
+ out.push(` ${import_picocolors61.default.dim("BRAINBASE_PROXY_URL")} override the model-proxy URL used by harness traffic (default https://api.v1.brainbaselabs.com)`);
89040
+ out.push(` ${import_picocolors61.default.dim("BRAINBASE_REGISTRY_URL")} override the registry API URL (default https://api.v1.brainbaselabs.com)`);
89041
+ out.push(` ${import_picocolors61.default.dim("BRAINBASE_TOKEN")} long-lived CLI PAT; the only PAT control-plane commands accept (token.json is not read there)`);
89042
+ out.push(` ${import_picocolors61.default.dim("BRAINBASE_SKIP_AUTH")} bypass the auth gate for development`);
89043
+ out.push(` ${import_picocolors61.default.dim("BRAINBASE_NON_INTERACTIVE")} force non-interactive mode — skip/auto-default prompts (CI & agents)`);
89044
+ out.push(` ${import_picocolors61.default.dim("BRAINBASE_RUN_ENTRYPOINT")} =1 → agent pull executes the agent entrypoint (sandbox boots; or pass --run-entrypoint)`);
87332
89045
  out.push("");
87333
- out.push(` ${import_picocolors54.default.dim("BRAINBASE_MEMORY_MCP_URL")} override the built-in memory MCP host`);
87334
- out.push(` ${import_picocolors54.default.dim("BRAINBASE_BROWSER_MCP_URL")} override the built-in browser MCP host`);
87335
- out.push(` ${import_picocolors54.default.dim("BRAINBASE_SLACK_MCP_URL")} override the built-in Slack MCP host`);
87336
- out.push(` ${import_picocolors54.default.dim("BRAINBASE_MEETING_MCP_URL")} override the built-in meeting MCP host`);
87337
- out.push(` ${import_picocolors54.default.dim("BRAINBASE_GITHUB_MCP_URL")} override the built-in GitHub MCP host`);
87338
- out.push(` ${import_picocolors54.default.dim("BRAINBASE_ORCHESTRATION_MCP_URL")} override the built-in orchestration MCP host`);
89046
+ out.push(` ${import_picocolors61.default.dim("BRAINBASE_MEMORY_MCP_URL")} override the built-in memory MCP host`);
89047
+ out.push(` ${import_picocolors61.default.dim("BRAINBASE_BROWSER_MCP_URL")} override the built-in browser MCP host`);
89048
+ out.push(` ${import_picocolors61.default.dim("BRAINBASE_SLACK_MCP_URL")} override the built-in Slack MCP host`);
89049
+ out.push(` ${import_picocolors61.default.dim("BRAINBASE_MEETING_MCP_URL")} override the built-in meeting MCP host`);
89050
+ out.push(` ${import_picocolors61.default.dim("BRAINBASE_GITHUB_MCP_URL")} override the built-in GitHub MCP host`);
89051
+ out.push(` ${import_picocolors61.default.dim("BRAINBASE_ORCHESTRATION_MCP_URL")} override the built-in orchestration MCP host`);
87339
89052
  out.push("");
87340
89053
  out.push(divider("HARNESSES"));
87341
89054
  out.push("");
87342
- out.push(` ${import_picocolors54.default.dim("•")} ${import_picocolors54.default.bold("claude-code")} ${import_picocolors54.default.dim("skills, mcps, agents, commands, playbooks, instructions, files")}`);
87343
- out.push(` ${import_picocolors54.default.dim("•")} ${import_picocolors54.default.bold("codex")} ${import_picocolors54.default.dim("skills, mcps, commands, playbooks, instructions, files")}`);
87344
- out.push(` ${import_picocolors54.default.dim("•")} ${import_picocolors54.default.bold("kafka")} ${import_picocolors54.default.dim("skills, mcps, agents, commands, playbooks, instructions, files")}`);
89055
+ out.push(` ${import_picocolors61.default.dim("•")} ${import_picocolors61.default.bold("claude-code")} ${import_picocolors61.default.dim("skills, mcps, agents, commands, playbooks, instructions, files")}`);
89056
+ out.push(` ${import_picocolors61.default.dim("•")} ${import_picocolors61.default.bold("codex")} ${import_picocolors61.default.dim("skills, mcps, commands, playbooks, instructions, files")}`);
89057
+ out.push(` ${import_picocolors61.default.dim("•")} ${import_picocolors61.default.bold("kafka")} ${import_picocolors61.default.dim("skills, mcps, agents, commands, playbooks, instructions, files")}`);
87345
89058
  out.push("");
87346
89059
  console.log(out.join(`
87347
89060
  `));
@@ -87495,13 +89208,13 @@ async function requireAuth(cmd) {
87495
89208
  if (STORED_PAT_COMMANDS.has(cmd) && readToken())
87496
89209
  return;
87497
89210
  console.error("");
87498
- console.error(` ${brandTint("◆")} ${import_picocolors54.default.bold("brainbase")}`);
89211
+ console.error(` ${brandTint("◆")} ${import_picocolors61.default.bold("brainbase")}`);
87499
89212
  console.error("");
87500
- console.error(` ${import_picocolors54.default.red("✗")} You need to sign in to use ${import_picocolors54.default.bold("brainbase " + cmd)}.`);
89213
+ console.error(` ${import_picocolors61.default.red("✗")} You need to sign in to use ${import_picocolors61.default.bold("brainbase " + cmd)}.`);
87501
89214
  if (status.reason)
87502
- console.error(` ${import_picocolors54.default.dim(status.reason)}`);
89215
+ console.error(` ${import_picocolors61.default.dim(status.reason)}`);
87503
89216
  console.error("");
87504
- console.error(` Run ${import_picocolors54.default.cyan("brainbase login")} to connect this device.`);
89217
+ console.error(` Run ${import_picocolors61.default.cyan("brainbase login")} to connect this device.`);
87505
89218
  console.error("");
87506
89219
  process14.exit(1);
87507
89220
  }
@@ -87574,8 +89287,8 @@ async function main() {
87574
89287
  const limitFlag = Number.isInteger(limitParsed) && limitParsed >= 1 && limitParsed <= 200 ? limitParsed : undefined;
87575
89288
  const archivedFlag = hasFlag2(sharedArgs, "--archived");
87576
89289
  ensureSkillResolversRegistered();
87577
- await requireAuth(cmd);
87578
89290
  try {
89291
+ await requireAuth(cmd);
87579
89292
  switch (cmd) {
87580
89293
  case "login": {
87581
89294
  await runLogin(cwd2, { web });
@@ -87682,6 +89395,17 @@ async function main() {
87682
89395
  await runTask(cwd2, sub, argv);
87683
89396
  break;
87684
89397
  }
89398
+ case "machine":
89399
+ case "machines": {
89400
+ const sub = argv.shift();
89401
+ await runMachine(sub, argv, {
89402
+ all,
89403
+ limit: limitFlag,
89404
+ yes,
89405
+ json: jsonFlag
89406
+ });
89407
+ break;
89408
+ }
87685
89409
  case "benchmark": {
87686
89410
  const sub = argv.shift();
87687
89411
  process14.exitCode = await runBenchmark(sub, argv, undefined, cwd2);
@@ -87696,6 +89420,7 @@ async function main() {
87696
89420
  orgId: orgIdFlag,
87697
89421
  teamId: teamIdFlag,
87698
89422
  graphOnly: graphOnlyFlag,
89423
+ force: forceFlag,
87699
89424
  name: nameFlag,
87700
89425
  from: fromFlags,
87701
89426
  to: toFlags,
@@ -87726,10 +89451,10 @@ async function main() {
87726
89451
  process14.exit(1);
87727
89452
  }
87728
89453
  } catch (err) {
87729
- console.error(import_picocolors54.default.red(`
89454
+ console.error(import_picocolors61.default.red(`
87730
89455
  ${err.message}`));
87731
89456
  if (err instanceof ApiError && err.status === 401) {
87732
- console.error(` Run ${import_picocolors54.default.cyan("brainbase login")} to connect this device.`);
89457
+ console.error(` Run ${import_picocolors61.default.cyan("brainbase login")} to connect this device.`);
87733
89458
  }
87734
89459
  if (process14.env.BRAINBASE_DEBUG)
87735
89460
  console.error(err.stack);