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