@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.
- package/README.md +7 -0
- package/dist/index.js +2255 -530
- 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
|
|
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.
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
54395
|
-
|
|
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
|
-
|
|
54634
|
+
retried = await fetchPage(LEGACY_MAX_PAGE);
|
|
54398
54635
|
} catch (err) {
|
|
54399
|
-
if (err instanceof ApiError && err.status ===
|
|
54400
|
-
throw
|
|
54636
|
+
if (err instanceof ApiError && (err.status === 401 || err.status === 403)) {
|
|
54637
|
+
throw err;
|
|
54401
54638
|
}
|
|
54402
|
-
|
|
54403
|
-
|
|
54404
|
-
|
|
54405
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 (!
|
|
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
|
-
|
|
60094
|
+
fail(err.message);
|
|
59846
60095
|
return false;
|
|
59847
60096
|
}
|
|
59848
60097
|
if (probe.exists && !probe.can_write) {
|
|
59849
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 (!
|
|
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
|
-
|
|
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 (!
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
61226
|
+
fail(`Invalid --name "${name}". Use creator/slug.`);
|
|
60959
61227
|
return;
|
|
60960
61228
|
}
|
|
60961
61229
|
if (!name) {
|
|
60962
61230
|
if (args.yes) {
|
|
60963
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
61266
|
+
fail(`can-publish probe failed: ${err.message}`);
|
|
60999
61267
|
return;
|
|
61000
61268
|
}
|
|
61001
61269
|
if (probe.exists && !probe.can_write) {
|
|
61002
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 (!
|
|
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 (!
|
|
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 (!
|
|
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
|
-
|
|
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
|
-
|
|
63678
|
+
fail("Your session is invalid. Run `brainbase login` and try again.");
|
|
63383
63679
|
} else {
|
|
63384
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
65665
|
+
fail("Your session is invalid. Run `brainbase login` and try again.");
|
|
65370
65666
|
} else if (err.status === 404) {
|
|
65371
|
-
|
|
65667
|
+
fail(`Agent not found, or you don't have access. Double-check the id.`);
|
|
65372
65668
|
} else {
|
|
65373
|
-
|
|
65669
|
+
fail(err.message);
|
|
65374
65670
|
}
|
|
65375
65671
|
} else {
|
|
65376
|
-
|
|
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/
|
|
65384
|
-
function
|
|
65385
|
-
|
|
65386
|
-
|
|
65387
|
-
|
|
65388
|
-
|
|
65389
|
-
|
|
65390
|
-
const
|
|
65391
|
-
const
|
|
65392
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
65979
|
-
initialValue:
|
|
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
|
|
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:
|
|
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
|
|
66099
|
-
|
|
66100
|
-
|
|
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
|
-
]
|
|
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
|
-
|
|
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
|
-
|
|
66717
|
+
cloudSecretNames = new Set(Object.keys(cloudRes.secrets));
|
|
66718
|
+
secretDrift = diffSecrets(readLocalSecrets(cwd2), cloudRes.secrets);
|
|
66317
66719
|
} catch (err) {
|
|
66318
|
-
|
|
66319
|
-
|
|
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
|
-
|
|
66336
|
-
|
|
66337
|
-
|
|
66338
|
-
|
|
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
|
-
|
|
66448
|
-
|
|
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
|
-
|
|
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("—
|
|
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
|
-
|
|
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
|
-
|
|
67475
|
+
fail(`Entrypoint file ${import_picocolors32.default.bold(manifest.entrypoint.file)} not found.`);
|
|
66978
67476
|
} else {
|
|
66979
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
67745
|
+
fail("Your session is invalid. Run `brainbase login` and try again.");
|
|
67243
67746
|
} else {
|
|
67244
|
-
|
|
67747
|
+
fail(err.message);
|
|
67245
67748
|
}
|
|
67246
67749
|
} else {
|
|
67247
|
-
|
|
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, {
|
|
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 && !
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
|
69024
|
-
description
|
|
69025
|
-
icon
|
|
69026
|
-
icon_color
|
|
69027
|
-
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
|
-
|
|
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 ?? ""}|${
|
|
69130
|
-
const localEdgeKey = (e2) => `${e2.from}->${e2.to}|${e2.description ?? ""}|${
|
|
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
|
|
69148
|
-
|
|
69149
|
-
|
|
69150
|
-
|
|
69151
|
-
|
|
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
|
-
|
|
69158
|
-
|
|
69159
|
-
|
|
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
|
|
69162
|
-
|
|
69163
|
-
|
|
69164
|
-
|
|
69165
|
-
|
|
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
|
|
69168
|
-
|
|
69169
|
-
|
|
69170
|
-
|
|
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
|
-
|
|
69174
|
-
|
|
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
|
-
|
|
69180
|
-
|
|
69181
|
-
|
|
69182
|
-
|
|
69183
|
-
|
|
69184
|
-
|
|
69185
|
-
|
|
69186
|
-
|
|
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 && !
|
|
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
|
|
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(
|
|
70083
|
+
return `[${value.map(stableJson2).join(",")}]`;
|
|
69239
70084
|
const obj = value;
|
|
69240
|
-
return `{${Object.keys(obj).sort().map((key2) => `${JSON.stringify(key2)}:${
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 (!
|
|
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
|
|
77108
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
79244
|
-
|
|
79245
|
-
|
|
79246
|
-
|
|
79247
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(` ${
|
|
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(` ${
|
|
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(` ${
|
|
79363
|
-
out.push(` ${
|
|
79364
|
-
out.push(` ${
|
|
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(` ${
|
|
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
|
|
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
|
|
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 =
|
|
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}.${
|
|
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 =
|
|
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}.${
|
|
82283
|
+
const temporary = `${destination}.${process.pid}.${crypto7.randomBytes(6).toString("hex")}.tmp`;
|
|
80445
82284
|
const descriptor = fs81.openSync(temporary, "wx", 384);
|
|
80446
|
-
const hash =
|
|
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}.${
|
|
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 =
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
82965
|
+
fail2(new BenchmarkPhaseError("phase_timeout", "phase budget expired"));
|
|
81127
82966
|
return;
|
|
81128
82967
|
}
|
|
81129
|
-
|
|
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}.${
|
|
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}.${
|
|
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}.${
|
|
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
|
|
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,
|
|
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
|
|
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
|
|
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 =
|
|
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-${
|
|
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
|
|
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
|
|
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
|
|
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}.${
|
|
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}.${
|
|
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
|
-
` ${
|
|
87507
|
+
` ${import_picocolors60.default.bold("brainbase benchmark")} ${import_picocolors60.default.dim("<command> [options]")}`,
|
|
85808
87508
|
"",
|
|
85809
|
-
` ${
|
|
85810
|
-
` ${
|
|
85811
|
-
` ${
|
|
85812
|
-
` ${
|
|
85813
|
-
` ${
|
|
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
|
-
` ${
|
|
87515
|
+
` ${import_picocolors60.default.dim("Use --agent to override brainbase.agent.yaml, and --json or --jsonl for automation.")}`,
|
|
85816
87516
|
"",
|
|
85817
|
-
` ${
|
|
85818
|
-
` ${
|
|
85819
|
-
` ${
|
|
85820
|
-
` ${
|
|
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
|
|
86153
|
-
if (!["yaml", "json", "bundle"].includes(
|
|
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 =
|
|
86159
|
-
const extension2 =
|
|
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}-${
|
|
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 =
|
|
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("◆")} ${
|
|
87204
|
-
out.push(` ${
|
|
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(` ${
|
|
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(` ${
|
|
87213
|
-
out.push(` ${
|
|
87214
|
-
out.push(` ${
|
|
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(` ${
|
|
87219
|
-
out.push(` ${
|
|
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(` ${
|
|
87224
|
-
out.push(` ${
|
|
87225
|
-
out.push(` ${
|
|
87226
|
-
out.push(` ${
|
|
87227
|
-
out.push(` ${
|
|
87228
|
-
out.push(` ${
|
|
87229
|
-
out.push(` ${
|
|
87230
|
-
out.push(` ${
|
|
87231
|
-
out.push(` ${
|
|
87232
|
-
out.push(` ${
|
|
87233
|
-
out.push(` ${
|
|
87234
|
-
out.push(` ${
|
|
87235
|
-
out.push(` ${
|
|
87236
|
-
out.push(` ${
|
|
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(` ${
|
|
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(` ${
|
|
87245
|
-
out.push(` ${
|
|
87246
|
-
out.push(` ${
|
|
87247
|
-
out.push(` ${
|
|
87248
|
-
out.push(` ${
|
|
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(` ${
|
|
87253
|
-
out.push(` ${
|
|
87254
|
-
out.push(` ${
|
|
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(` ${
|
|
87259
|
-
out.push(` ${
|
|
87260
|
-
out.push(` ${
|
|
87261
|
-
out.push(` ${
|
|
87262
|
-
out.push(` ${
|
|
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(` ${
|
|
87267
|
-
out.push(` ${
|
|
87268
|
-
out.push(` ${
|
|
87269
|
-
out.push(` ${
|
|
87270
|
-
out.push(` ${
|
|
87271
|
-
out.push(` ${
|
|
87272
|
-
out.push(` ${
|
|
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(` ${
|
|
87277
|
-
out.push(` ${
|
|
87278
|
-
out.push(` ${
|
|
87279
|
-
out.push(` ${
|
|
87280
|
-
out.push(` ${
|
|
87281
|
-
out.push(` ${
|
|
87282
|
-
out.push(` ${
|
|
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(` ${
|
|
87287
|
-
out.push(` ${
|
|
87288
|
-
out.push(` ${
|
|
87289
|
-
out.push(` ${
|
|
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(` ${
|
|
87294
|
-
out.push(` ${
|
|
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(` ${
|
|
87299
|
-
out.push(` ${
|
|
87300
|
-
out.push(` ${
|
|
87301
|
-
out.push(` ${
|
|
87302
|
-
out.push(` ${
|
|
87303
|
-
out.push(` ${
|
|
87304
|
-
out.push(` ${
|
|
87305
|
-
out.push(` ${
|
|
87306
|
-
out.push(` ${
|
|
87307
|
-
out.push(` ${
|
|
87308
|
-
out.push(` ${
|
|
87309
|
-
out.push(` ${
|
|
87310
|
-
out.push(` ${
|
|
87311
|
-
out.push(` ${
|
|
87312
|
-
out.push(` ${
|
|
87313
|
-
out.push(` ${
|
|
87314
|
-
out.push(` ${
|
|
87315
|
-
out.push(` ${
|
|
87316
|
-
out.push(` ${
|
|
87317
|
-
out.push(` ${
|
|
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(` ${
|
|
87322
|
-
out.push(` ${
|
|
87323
|
-
out.push(` ${
|
|
87324
|
-
out.push(` ${
|
|
87325
|
-
out.push(` ${
|
|
87326
|
-
out.push(` ${
|
|
87327
|
-
out.push(` ${
|
|
87328
|
-
out.push(` ${
|
|
87329
|
-
out.push(` ${
|
|
87330
|
-
out.push(` ${
|
|
87331
|
-
out.push(` ${
|
|
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(` ${
|
|
87334
|
-
out.push(` ${
|
|
87335
|
-
out.push(` ${
|
|
87336
|
-
out.push(` ${
|
|
87337
|
-
out.push(` ${
|
|
87338
|
-
out.push(` ${
|
|
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(` ${
|
|
87343
|
-
out.push(` ${
|
|
87344
|
-
out.push(` ${
|
|
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("◆")} ${
|
|
89211
|
+
console.error(` ${brandTint("◆")} ${import_picocolors61.default.bold("brainbase")}`);
|
|
87499
89212
|
console.error("");
|
|
87500
|
-
console.error(` ${
|
|
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(` ${
|
|
89215
|
+
console.error(` ${import_picocolors61.default.dim(status.reason)}`);
|
|
87503
89216
|
console.error("");
|
|
87504
|
-
console.error(` Run ${
|
|
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(
|
|
89454
|
+
console.error(import_picocolors61.default.red(`
|
|
87730
89455
|
${err.message}`));
|
|
87731
89456
|
if (err instanceof ApiError && err.status === 401) {
|
|
87732
|
-
console.error(` Run ${
|
|
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);
|