@brainbase-labs/cli 0.2.3 → 0.2.4
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 +1055 -95
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
4
|
import process2 from "node:process";
|
|
5
|
-
import
|
|
6
|
-
import
|
|
5
|
+
import fs44 from "node:fs";
|
|
6
|
+
import pc36 from "picocolors";
|
|
7
7
|
|
|
8
8
|
// src/cli/template.ts
|
|
9
9
|
import pc10 from "picocolors";
|
|
@@ -398,11 +398,13 @@ function globalPaths() {
|
|
|
398
398
|
agents: path4.join(GLOBAL_ROOT, "agents"),
|
|
399
399
|
settings: path4.join(GLOBAL_ROOT, "settings.json"),
|
|
400
400
|
claudeMd: path4.join(GLOBAL_ROOT, "CLAUDE.md"),
|
|
401
|
-
userClaudeJson: USER_CLAUDE_JSON
|
|
401
|
+
userClaudeJson: USER_CLAUDE_JSON,
|
|
402
|
+
mcpStore: USER_CLAUDE_JSON
|
|
402
403
|
};
|
|
403
404
|
}
|
|
404
405
|
function projectPaths(cwd) {
|
|
405
406
|
const root = projectRoot(cwd);
|
|
407
|
+
const mcpJson = projectMcpJson(cwd);
|
|
406
408
|
return {
|
|
407
409
|
root,
|
|
408
410
|
skills: path4.join(root, "skills"),
|
|
@@ -410,8 +412,9 @@ function projectPaths(cwd) {
|
|
|
410
412
|
agents: path4.join(root, "agents"),
|
|
411
413
|
settings: path4.join(root, "settings.json"),
|
|
412
414
|
claudeMd: path4.join(cwd, "CLAUDE.md"),
|
|
413
|
-
mcpJson
|
|
414
|
-
userClaudeJson: USER_CLAUDE_JSON
|
|
415
|
+
mcpJson,
|
|
416
|
+
userClaudeJson: USER_CLAUDE_JSON,
|
|
417
|
+
mcpStore: mcpJson
|
|
415
418
|
};
|
|
416
419
|
}
|
|
417
420
|
function scopePaths(cwd, scope) {
|
|
@@ -491,10 +494,6 @@ function readSettings(file) {
|
|
|
491
494
|
function writeSettings(file, settings) {
|
|
492
495
|
writeJson(file, settings);
|
|
493
496
|
}
|
|
494
|
-
function ensureSettings(file) {
|
|
495
|
-
if (!exists(file))
|
|
496
|
-
writeSettings(file, {});
|
|
497
|
-
}
|
|
498
497
|
function setMcpServer(file, name, entry) {
|
|
499
498
|
const s = readSettings(file);
|
|
500
499
|
if (!s.mcpServers)
|
|
@@ -518,6 +517,33 @@ function getMcpServer(file, name) {
|
|
|
518
517
|
function listMcpServers(file) {
|
|
519
518
|
return readSettings(file).mcpServers ?? {};
|
|
520
519
|
}
|
|
520
|
+
function normalizeMcpPayload(payload) {
|
|
521
|
+
const out = { ...payload };
|
|
522
|
+
delete out.is_enabled;
|
|
523
|
+
const hasUrl = typeof out.url === "string" && out.url.length > 0;
|
|
524
|
+
const hasCommand = typeof out.command === "string" && out.command.length > 0;
|
|
525
|
+
if (!out.type) {
|
|
526
|
+
if (hasUrl)
|
|
527
|
+
out.type = "http";
|
|
528
|
+
else if (hasCommand)
|
|
529
|
+
out.type = "stdio";
|
|
530
|
+
}
|
|
531
|
+
if (out.type === "http" || out.type === "sse") {
|
|
532
|
+
if (Array.isArray(out.args) && out.args.length === 0)
|
|
533
|
+
delete out.args;
|
|
534
|
+
if (out.env && typeof out.env === "object" && Object.keys(out.env).length === 0) {
|
|
535
|
+
delete out.env;
|
|
536
|
+
}
|
|
537
|
+
if (out.headers && typeof out.headers === "object" && Object.keys(out.headers).length === 0) {
|
|
538
|
+
delete out.headers;
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
if (out.type === "stdio") {
|
|
542
|
+
delete out.url;
|
|
543
|
+
delete out.headers;
|
|
544
|
+
}
|
|
545
|
+
return out;
|
|
546
|
+
}
|
|
521
547
|
function listMcpServersFromMcpJson(file) {
|
|
522
548
|
return readJsonOr(file, {}).mcpServers ?? {};
|
|
523
549
|
}
|
|
@@ -1028,14 +1054,13 @@ async function installAgent(comp, opts) {
|
|
|
1028
1054
|
return installSingleFileComponent(comp, agents, `${comp.slug}.md`, opts);
|
|
1029
1055
|
}
|
|
1030
1056
|
async function installMcp(comp, opts, ctx) {
|
|
1031
|
-
const {
|
|
1032
|
-
|
|
1033
|
-
const existing = getMcpServer(settings, comp.slug);
|
|
1057
|
+
const { mcpStore } = scopePaths(opts.cwd, opts.scope);
|
|
1058
|
+
const existing = getMcpServer(mcpStore, comp.slug);
|
|
1034
1059
|
if (existing) {
|
|
1035
1060
|
const choice = await opts.resolveConflict({
|
|
1036
1061
|
type: "mcp",
|
|
1037
1062
|
slug: comp.slug,
|
|
1038
|
-
existingPath:
|
|
1063
|
+
existingPath: mcpStore
|
|
1039
1064
|
});
|
|
1040
1065
|
if (choice === "skip")
|
|
1041
1066
|
return { skip: "user skipped existing mcp" };
|
|
@@ -1064,15 +1089,15 @@ async function installMcp(comp, opts, ctx) {
|
|
|
1064
1089
|
}
|
|
1065
1090
|
}
|
|
1066
1091
|
}
|
|
1067
|
-
setMcpServer(
|
|
1092
|
+
setMcpServer(mcpStore, comp.slug, normalizeMcpPayload(payload));
|
|
1068
1093
|
return {
|
|
1069
1094
|
type: "mcp",
|
|
1070
1095
|
slug: comp.slug,
|
|
1071
|
-
installedPaths: [
|
|
1096
|
+
installedPaths: [mcpStore],
|
|
1072
1097
|
sourceChecksum: comp.checksum,
|
|
1073
1098
|
scope: opts.scope,
|
|
1074
1099
|
embeddedKey: comp.slug,
|
|
1075
|
-
settingsFile:
|
|
1100
|
+
settingsFile: mcpStore
|
|
1076
1101
|
};
|
|
1077
1102
|
}
|
|
1078
1103
|
async function installInstruction(comp, opts, ctx) {
|
|
@@ -1296,8 +1321,8 @@ async function diffClaudeCode(components, opts) {
|
|
|
1296
1321
|
break;
|
|
1297
1322
|
}
|
|
1298
1323
|
case "mcp": {
|
|
1299
|
-
if (exists(sp.
|
|
1300
|
-
const servers = listMcpServers(sp.
|
|
1324
|
+
if (exists(sp.mcpStore)) {
|
|
1325
|
+
const servers = listMcpServers(sp.mcpStore);
|
|
1301
1326
|
const cur = servers[comp.slug];
|
|
1302
1327
|
if (cur)
|
|
1303
1328
|
installedChecksum = sha256(JSON.stringify(cur));
|
|
@@ -2880,6 +2905,21 @@ var api = {
|
|
|
2880
2905
|
return request(`/api/cli/keys/${encodeURIComponent(keyId)}`, {
|
|
2881
2906
|
method: "DELETE"
|
|
2882
2907
|
});
|
|
2908
|
+
},
|
|
2909
|
+
listOrchestrations(orgId, teamId) {
|
|
2910
|
+
return request(`/api/cli/orgs/${encodeURIComponent(orgId)}/teams/${encodeURIComponent(teamId)}/orchestrations`);
|
|
2911
|
+
},
|
|
2912
|
+
getOrchestration(orchId) {
|
|
2913
|
+
return request(`/api/cli/orchestrations/${encodeURIComponent(orchId)}`);
|
|
2914
|
+
},
|
|
2915
|
+
getOrchestrationManifest(orchId) {
|
|
2916
|
+
return request(`/api/cli/orchestrations/${encodeURIComponent(orchId)}/manifest`);
|
|
2917
|
+
},
|
|
2918
|
+
updateOrchestration(orchId, input) {
|
|
2919
|
+
return request(`/api/cli/orchestrations/${encodeURIComponent(orchId)}`, {
|
|
2920
|
+
method: "PUT",
|
|
2921
|
+
body: JSON.stringify(input)
|
|
2922
|
+
});
|
|
2883
2923
|
}
|
|
2884
2924
|
};
|
|
2885
2925
|
function proxyBaseUrl(session) {
|
|
@@ -9310,6 +9350,906 @@ function printHelp() {
|
|
|
9310
9350
|
`));
|
|
9311
9351
|
}
|
|
9312
9352
|
|
|
9353
|
+
// src/cli/orchestration.ts
|
|
9354
|
+
import pc33 from "picocolors";
|
|
9355
|
+
|
|
9356
|
+
// src/cli/orchestration-pull.ts
|
|
9357
|
+
import path46 from "node:path";
|
|
9358
|
+
import fs43 from "node:fs";
|
|
9359
|
+
import * as p21 from "@clack/prompts";
|
|
9360
|
+
import pc29 from "picocolors";
|
|
9361
|
+
|
|
9362
|
+
// src/core/orchestration-manifest.ts
|
|
9363
|
+
import path43 from "node:path";
|
|
9364
|
+
import fs40 from "node:fs";
|
|
9365
|
+
import { z as z8 } from "zod";
|
|
9366
|
+
import YAML2 from "yaml";
|
|
9367
|
+
var ORCH_MANIFEST_FILE = "brainbase-orchestration.yaml";
|
|
9368
|
+
var ORCH_MEMBERS_DIR = "agents";
|
|
9369
|
+
var OrchMetaSchema = z8.object({
|
|
9370
|
+
name: z8.string().min(1),
|
|
9371
|
+
description: z8.string().optional(),
|
|
9372
|
+
icon: z8.string().optional(),
|
|
9373
|
+
icon_color: z8.string().optional(),
|
|
9374
|
+
credit_limit: z8.number().optional()
|
|
9375
|
+
});
|
|
9376
|
+
var MemberSchema = z8.object({
|
|
9377
|
+
slug: z8.string().min(1),
|
|
9378
|
+
name: z8.string().optional()
|
|
9379
|
+
});
|
|
9380
|
+
var EdgeSchema = z8.object({
|
|
9381
|
+
from: z8.string().min(1),
|
|
9382
|
+
to: z8.string().min(1),
|
|
9383
|
+
description: z8.string().optional(),
|
|
9384
|
+
payload_schema: z8.record(z8.unknown()).optional()
|
|
9385
|
+
});
|
|
9386
|
+
var OrchestrationManifestSchema = z8.object({
|
|
9387
|
+
schema: z8.literal(1),
|
|
9388
|
+
orchestration: OrchMetaSchema,
|
|
9389
|
+
members: z8.array(MemberSchema).default([]),
|
|
9390
|
+
edges: z8.array(EdgeSchema).default([])
|
|
9391
|
+
});
|
|
9392
|
+
function orchManifestPath(cwd) {
|
|
9393
|
+
return path43.join(cwd, ORCH_MANIFEST_FILE);
|
|
9394
|
+
}
|
|
9395
|
+
function hasOrchManifest(cwd) {
|
|
9396
|
+
return fs40.existsSync(orchManifestPath(cwd));
|
|
9397
|
+
}
|
|
9398
|
+
function readOrchManifest(cwd) {
|
|
9399
|
+
const p20 = orchManifestPath(cwd);
|
|
9400
|
+
if (!fs40.existsSync(p20))
|
|
9401
|
+
return null;
|
|
9402
|
+
const raw = fs40.readFileSync(p20, "utf8");
|
|
9403
|
+
let parsed;
|
|
9404
|
+
try {
|
|
9405
|
+
parsed = YAML2.parse(raw);
|
|
9406
|
+
} catch (err) {
|
|
9407
|
+
throw new Error(`${ORCH_MANIFEST_FILE} is not valid YAML: ${err.message}`);
|
|
9408
|
+
}
|
|
9409
|
+
const result = OrchestrationManifestSchema.safeParse(parsed);
|
|
9410
|
+
if (!result.success) {
|
|
9411
|
+
throw new Error(`${ORCH_MANIFEST_FILE} is invalid: ${result.error.issues.map((i) => `${i.path.join(".") || "(root)"} — ${i.message}`).join("; ")}`);
|
|
9412
|
+
}
|
|
9413
|
+
return result.data;
|
|
9414
|
+
}
|
|
9415
|
+
function writeOrchManifest(cwd, manifest) {
|
|
9416
|
+
const doc = new YAML2.Document;
|
|
9417
|
+
doc.contents = manifest;
|
|
9418
|
+
doc.commentBefore = ` brainbase-orchestration.yaml — declarative orchestration manifest.
|
|
9419
|
+
` + ` Committed to source control. Edit by hand, then
|
|
9420
|
+
` + " `brainbase orchestration push`. Member agents live under ./agents/.";
|
|
9421
|
+
fs40.writeFileSync(orchManifestPath(cwd), String(doc), "utf8");
|
|
9422
|
+
}
|
|
9423
|
+
function memberDir(cwd, slug) {
|
|
9424
|
+
return path43.join(cwd, ORCH_MEMBERS_DIR, slug);
|
|
9425
|
+
}
|
|
9426
|
+
|
|
9427
|
+
// src/core/orchestration-link.ts
|
|
9428
|
+
import path44 from "node:path";
|
|
9429
|
+
import fs41 from "node:fs";
|
|
9430
|
+
import { z as z9 } from "zod";
|
|
9431
|
+
var ORCH_LINK_FILE = "orchestration-link.json";
|
|
9432
|
+
var ORCH_SYNC_STATE_FILE = "orchestration-sync-state.json";
|
|
9433
|
+
var OrchestrationLinkSchema = z9.object({
|
|
9434
|
+
schemaVersion: z9.literal(1),
|
|
9435
|
+
orchestration_id: z9.string(),
|
|
9436
|
+
group_id: z9.string(),
|
|
9437
|
+
org_id: z9.string(),
|
|
9438
|
+
team_id: z9.string(),
|
|
9439
|
+
name: z9.string(),
|
|
9440
|
+
description: z9.string().nullish().transform((v) => v ?? undefined),
|
|
9441
|
+
linked_at: z9.string(),
|
|
9442
|
+
linked_by: z9.string().nullish().transform((v) => v ?? undefined)
|
|
9443
|
+
});
|
|
9444
|
+
var SyncedMemberSchema = z9.object({
|
|
9445
|
+
agent_id: z9.string(),
|
|
9446
|
+
slug: z9.string(),
|
|
9447
|
+
revision: z9.number()
|
|
9448
|
+
});
|
|
9449
|
+
var SyncedEdgeSchema = z9.object({
|
|
9450
|
+
from_slug: z9.string(),
|
|
9451
|
+
to_slug: z9.string(),
|
|
9452
|
+
description: z9.string().default(""),
|
|
9453
|
+
payload_schema: z9.record(z9.unknown()).default({})
|
|
9454
|
+
});
|
|
9455
|
+
var OrchestrationSyncStateSchema = z9.object({
|
|
9456
|
+
schemaVersion: z9.literal(1),
|
|
9457
|
+
orchestration_id: z9.string(),
|
|
9458
|
+
revision: z9.number(),
|
|
9459
|
+
synced_at: z9.string(),
|
|
9460
|
+
members: z9.array(SyncedMemberSchema),
|
|
9461
|
+
edges: z9.array(SyncedEdgeSchema)
|
|
9462
|
+
});
|
|
9463
|
+
function orchLinkPath(cwd) {
|
|
9464
|
+
return path44.join(cwd, LINK_DIR, ORCH_LINK_FILE);
|
|
9465
|
+
}
|
|
9466
|
+
function orchSyncStatePath(cwd) {
|
|
9467
|
+
return path44.join(cwd, LINK_DIR, ORCH_SYNC_STATE_FILE);
|
|
9468
|
+
}
|
|
9469
|
+
function readOrchLink(cwd) {
|
|
9470
|
+
const p20 = orchLinkPath(cwd);
|
|
9471
|
+
if (!exists(p20))
|
|
9472
|
+
return null;
|
|
9473
|
+
try {
|
|
9474
|
+
return OrchestrationLinkSchema.parse(readJson(p20));
|
|
9475
|
+
} catch {
|
|
9476
|
+
return null;
|
|
9477
|
+
}
|
|
9478
|
+
}
|
|
9479
|
+
function writeOrchLink(cwd, link) {
|
|
9480
|
+
ensureDir(path44.join(cwd, LINK_DIR));
|
|
9481
|
+
const clean = {};
|
|
9482
|
+
for (const [k, v] of Object.entries(link)) {
|
|
9483
|
+
if (v !== null && v !== undefined)
|
|
9484
|
+
clean[k] = v;
|
|
9485
|
+
}
|
|
9486
|
+
writeJson(orchLinkPath(cwd), clean);
|
|
9487
|
+
ensureGitignore2(cwd);
|
|
9488
|
+
}
|
|
9489
|
+
function readOrchSyncState(cwd) {
|
|
9490
|
+
const p20 = orchSyncStatePath(cwd);
|
|
9491
|
+
if (!exists(p20))
|
|
9492
|
+
return null;
|
|
9493
|
+
try {
|
|
9494
|
+
return OrchestrationSyncStateSchema.parse(readJson(p20));
|
|
9495
|
+
} catch {
|
|
9496
|
+
return null;
|
|
9497
|
+
}
|
|
9498
|
+
}
|
|
9499
|
+
function writeOrchSyncState(cwd, state) {
|
|
9500
|
+
ensureDir(path44.join(cwd, LINK_DIR));
|
|
9501
|
+
writeJson(orchSyncStatePath(cwd), state);
|
|
9502
|
+
ensureGitignore2(cwd);
|
|
9503
|
+
}
|
|
9504
|
+
function ensureGitignore2(cwd) {
|
|
9505
|
+
const ignorePath = path44.join(cwd, LINK_DIR, ".gitignore");
|
|
9506
|
+
const desired = `${ORCH_SYNC_STATE_FILE}
|
|
9507
|
+
`;
|
|
9508
|
+
try {
|
|
9509
|
+
if (!exists(ignorePath)) {
|
|
9510
|
+
fs41.writeFileSync(ignorePath, desired);
|
|
9511
|
+
return;
|
|
9512
|
+
}
|
|
9513
|
+
const current = fs41.readFileSync(ignorePath, "utf8");
|
|
9514
|
+
if (!current.split(/\r?\n/).some((l) => l.trim() === ORCH_SYNC_STATE_FILE)) {
|
|
9515
|
+
fs41.writeFileSync(ignorePath, current.endsWith(`
|
|
9516
|
+
`) ? current + desired : current + `
|
|
9517
|
+
` + desired);
|
|
9518
|
+
}
|
|
9519
|
+
} catch {}
|
|
9520
|
+
}
|
|
9521
|
+
|
|
9522
|
+
// src/core/agent-fresh-install.ts
|
|
9523
|
+
import path45 from "node:path";
|
|
9524
|
+
import fs42 from "node:fs";
|
|
9525
|
+
import os10 from "node:os";
|
|
9526
|
+
import * as p20 from "@clack/prompts";
|
|
9527
|
+
async function installAgentFresh(input) {
|
|
9528
|
+
const { cwd, agent, cloud, harness } = input;
|
|
9529
|
+
const scope = input.scope ?? "project";
|
|
9530
|
+
ensureDir(cwd);
|
|
9531
|
+
const stageRoot = stageManifestComponents2(cloud.components);
|
|
9532
|
+
const justInstalledPaths = new Map;
|
|
9533
|
+
try {
|
|
9534
|
+
if (cloud.components.length > 0) {
|
|
9535
|
+
const toInstall = cloud.components.map((c) => ({
|
|
9536
|
+
type: c.type,
|
|
9537
|
+
slug: c.slug,
|
|
9538
|
+
scope,
|
|
9539
|
+
rootDir: path45.join(stageRoot, c.type, c.slug),
|
|
9540
|
+
description: c.description,
|
|
9541
|
+
meta: c.meta,
|
|
9542
|
+
payload: c.meta?.mcp,
|
|
9543
|
+
checksum: c.hash
|
|
9544
|
+
}));
|
|
9545
|
+
const installOpts = {
|
|
9546
|
+
cwd,
|
|
9547
|
+
scope,
|
|
9548
|
+
resolveConflict: async (_c) => "overwrite",
|
|
9549
|
+
resolveSecret: async () => null
|
|
9550
|
+
};
|
|
9551
|
+
const result = await runHarnessInstall3(harness, toInstall, installOpts, agent.name);
|
|
9552
|
+
for (const o of result.installed) {
|
|
9553
|
+
justInstalledPaths.set(`${o.type}/${o.slug}`, o.installedPaths);
|
|
9554
|
+
}
|
|
9555
|
+
}
|
|
9556
|
+
materializeInstructions2(cwd, cloud);
|
|
9557
|
+
const manifest = buildManifestFromCloud(cloud, agent);
|
|
9558
|
+
writeManifest(cwd, manifest);
|
|
9559
|
+
writeLink(cwd, {
|
|
9560
|
+
schemaVersion: 1,
|
|
9561
|
+
agent_id: agent.id,
|
|
9562
|
+
org_id: agent.org_id,
|
|
9563
|
+
team_id: agent.team_id,
|
|
9564
|
+
slug: agent.slug,
|
|
9565
|
+
name: agent.name,
|
|
9566
|
+
tagline: agent.tagline,
|
|
9567
|
+
url: agent.url,
|
|
9568
|
+
linked_at: new Date().toISOString(),
|
|
9569
|
+
harness
|
|
9570
|
+
});
|
|
9571
|
+
const syncedComponents = cloud.components.map((c) => ({
|
|
9572
|
+
type: c.type,
|
|
9573
|
+
slug: c.slug,
|
|
9574
|
+
hash: c.hash,
|
|
9575
|
+
installedPaths: justInstalledPaths.get(`${c.type}/${c.slug}`) ?? []
|
|
9576
|
+
}));
|
|
9577
|
+
writeSyncState(cwd, {
|
|
9578
|
+
schemaVersion: 1,
|
|
9579
|
+
agent_id: agent.id,
|
|
9580
|
+
revision: cloud.revision,
|
|
9581
|
+
synced_at: new Date().toISOString(),
|
|
9582
|
+
components: syncedComponents,
|
|
9583
|
+
agentMeta: { name: agent.name, tagline: agent.tagline }
|
|
9584
|
+
});
|
|
9585
|
+
if (input.pullSecrets !== false) {
|
|
9586
|
+
await pullAgentSecrets(cwd, agent.id);
|
|
9587
|
+
}
|
|
9588
|
+
return {
|
|
9589
|
+
installedPaths: justInstalledPaths,
|
|
9590
|
+
manifest,
|
|
9591
|
+
syncedComponents
|
|
9592
|
+
};
|
|
9593
|
+
} finally {
|
|
9594
|
+
try {
|
|
9595
|
+
fs42.rmSync(stageRoot, { recursive: true, force: true });
|
|
9596
|
+
} catch {}
|
|
9597
|
+
}
|
|
9598
|
+
}
|
|
9599
|
+
function stageManifestComponents2(components) {
|
|
9600
|
+
const root = fs42.mkdtempSync(path45.join(os10.tmpdir(), "brainbase-orch-pull-"));
|
|
9601
|
+
for (const c of components) {
|
|
9602
|
+
const compDir = path45.join(root, c.type, c.slug);
|
|
9603
|
+
ensureDir(compDir);
|
|
9604
|
+
for (const f of c.files) {
|
|
9605
|
+
const target = path45.join(compDir, f.path);
|
|
9606
|
+
ensureDir(path45.dirname(target));
|
|
9607
|
+
fs42.writeFileSync(target, f.content);
|
|
9608
|
+
}
|
|
9609
|
+
}
|
|
9610
|
+
return root;
|
|
9611
|
+
}
|
|
9612
|
+
function runHarnessInstall3(harnessId, components, opts, agentName) {
|
|
9613
|
+
if (harnessId === "claude-code")
|
|
9614
|
+
return installClaudeCodeWithCtx(components, opts, agentName);
|
|
9615
|
+
if (harnessId === "codex")
|
|
9616
|
+
return installCodexWithCtx(components, opts, agentName);
|
|
9617
|
+
return getAdapter(harnessId).install(components, opts);
|
|
9618
|
+
}
|
|
9619
|
+
function materializeInstructions2(cwd, cloud) {
|
|
9620
|
+
for (const c of cloud.components) {
|
|
9621
|
+
if (c.type !== "instruction")
|
|
9622
|
+
continue;
|
|
9623
|
+
const body = c.files[0]?.content ?? "";
|
|
9624
|
+
if (!body.trim())
|
|
9625
|
+
continue;
|
|
9626
|
+
fs42.writeFileSync(path45.join(cwd, DEFAULT_INSTRUCTIONS_FILE), body, "utf8");
|
|
9627
|
+
return;
|
|
9628
|
+
}
|
|
9629
|
+
}
|
|
9630
|
+
function buildManifestFromCloud(cloud, agent) {
|
|
9631
|
+
const skills = cloud.components.filter((c) => c.type === "skill").map((c) => {
|
|
9632
|
+
const meta = c.meta ?? {};
|
|
9633
|
+
if (meta.name && meta.name.includes("/")) {
|
|
9634
|
+
return {
|
|
9635
|
+
source: meta.version ? `registry:${meta.name}@${meta.version}` : `registry:${meta.name}`
|
|
9636
|
+
};
|
|
9637
|
+
}
|
|
9638
|
+
return { source: `registry:${c.slug}` };
|
|
9639
|
+
});
|
|
9640
|
+
const hasInstructions = cloud.components.some((c) => c.type === "instruction" && c.files[0]?.content?.trim());
|
|
9641
|
+
const mcp = cloud.components.filter((c) => c.type === "mcp").map((c) => {
|
|
9642
|
+
const payload = (c.meta ?? {}).mcp ?? {};
|
|
9643
|
+
const entry = { name: c.slug };
|
|
9644
|
+
if (typeof payload.url === "string")
|
|
9645
|
+
entry.url = payload.url;
|
|
9646
|
+
if (typeof payload.command === "string")
|
|
9647
|
+
entry.command = payload.command;
|
|
9648
|
+
if (Array.isArray(payload.args))
|
|
9649
|
+
entry.args = payload.args.map(String);
|
|
9650
|
+
if (payload.env && typeof payload.env === "object")
|
|
9651
|
+
entry.env = payload.env;
|
|
9652
|
+
if (payload.headers && typeof payload.headers === "object")
|
|
9653
|
+
entry.headers = payload.headers;
|
|
9654
|
+
if (typeof payload.is_enabled === "boolean")
|
|
9655
|
+
entry.is_enabled = payload.is_enabled;
|
|
9656
|
+
return entry;
|
|
9657
|
+
});
|
|
9658
|
+
return {
|
|
9659
|
+
schema: 1,
|
|
9660
|
+
agent: {
|
|
9661
|
+
name: agent.name,
|
|
9662
|
+
...agent.tagline ? { tagline: agent.tagline } : {}
|
|
9663
|
+
},
|
|
9664
|
+
...hasInstructions ? { instructions: { file: DEFAULT_INSTRUCTIONS_FILE } } : {},
|
|
9665
|
+
skills,
|
|
9666
|
+
mcp
|
|
9667
|
+
};
|
|
9668
|
+
}
|
|
9669
|
+
async function pullAgentSecrets(cwd, agentId) {
|
|
9670
|
+
try {
|
|
9671
|
+
const res = await api.getAgentSecrets(agentId);
|
|
9672
|
+
const secrets = res.secrets ?? {};
|
|
9673
|
+
if (Object.keys(secrets).length > 0) {
|
|
9674
|
+
writeLocalSecrets(cwd, secrets);
|
|
9675
|
+
}
|
|
9676
|
+
} catch (err) {
|
|
9677
|
+
if (err instanceof ApiError && err.status !== 404) {
|
|
9678
|
+
p20.log.warn(`Skipped secrets for agent ${agentId}: ${err.message}`);
|
|
9679
|
+
}
|
|
9680
|
+
}
|
|
9681
|
+
}
|
|
9682
|
+
|
|
9683
|
+
// src/cli/orchestration-pull.ts
|
|
9684
|
+
async function runOrchestrationPull(cwd, args) {
|
|
9685
|
+
banner("orchestration pull — fetch orchestration + all member agents");
|
|
9686
|
+
let orchId = null;
|
|
9687
|
+
const existingLink = readOrchLink(cwd);
|
|
9688
|
+
if (existingLink) {
|
|
9689
|
+
orchId = existingLink.orchestration_id;
|
|
9690
|
+
} else if (args.orchestrationId) {
|
|
9691
|
+
orchId = args.orchestrationId;
|
|
9692
|
+
} else {
|
|
9693
|
+
p21.log.warn("This folder is not linked to any orchestration.");
|
|
9694
|
+
p21.log.info(`Run ${pc29.cyan("brainbase orchestration pull <id>")} with an orchestration id,
|
|
9695
|
+
or ${pc29.cyan("brainbase orchestration list")} to find one.`);
|
|
9696
|
+
return;
|
|
9697
|
+
}
|
|
9698
|
+
const sp = p21.spinner();
|
|
9699
|
+
sp.start(`Fetching orchestration ${orchId}…`);
|
|
9700
|
+
let cloud;
|
|
9701
|
+
try {
|
|
9702
|
+
cloud = await api.getOrchestrationManifest(orchId);
|
|
9703
|
+
sp.stop(`Cloud revision ${cloud.revision} — ${cloud.members.length} member${cloud.members.length === 1 ? "" : "s"}, ${cloud.edges.length} edge${cloud.edges.length === 1 ? "" : "s"}.`);
|
|
9704
|
+
} catch (err) {
|
|
9705
|
+
sp.stop("Failed.");
|
|
9706
|
+
handleApiError5(err);
|
|
9707
|
+
return;
|
|
9708
|
+
}
|
|
9709
|
+
const planLines = [];
|
|
9710
|
+
planLines.push("");
|
|
9711
|
+
planLines.push(` ${pc29.bold(cloud.name)} ${pc29.dim(`(${cloud.id})`)}`);
|
|
9712
|
+
if (cloud.description)
|
|
9713
|
+
planLines.push(` ${pc29.dim(cloud.description)}`);
|
|
9714
|
+
planLines.push("");
|
|
9715
|
+
planLines.push(` ${pc29.dim("members:")}`);
|
|
9716
|
+
for (const m of cloud.members) {
|
|
9717
|
+
const skipped = !m.manifest;
|
|
9718
|
+
const tail = skipped ? pc29.red(" (manifest unavailable — skipped)") : "";
|
|
9719
|
+
planLines.push(` ${pc29.cyan("•")} ${pc29.bold(m.slug)} ${pc29.dim(`(${m.name})`)}${tail}`);
|
|
9720
|
+
}
|
|
9721
|
+
if (cloud.edges.length) {
|
|
9722
|
+
planLines.push("");
|
|
9723
|
+
planLines.push(` ${pc29.dim("edges:")}`);
|
|
9724
|
+
for (const e of cloud.edges) {
|
|
9725
|
+
const from = e.from_slug ?? e.from_agent_id;
|
|
9726
|
+
const to = e.to_slug ?? e.to_agent_id;
|
|
9727
|
+
const desc = e.description ? ` ${pc29.dim("— " + e.description)}` : "";
|
|
9728
|
+
planLines.push(` ${pc29.cyan(from)} ${pc29.dim("→")} ${pc29.cyan(to)}${desc}`);
|
|
9729
|
+
}
|
|
9730
|
+
}
|
|
9731
|
+
planLines.push("");
|
|
9732
|
+
console.log(planLines.join(`
|
|
9733
|
+
`));
|
|
9734
|
+
const isRefresh = !!existingLink;
|
|
9735
|
+
if (!args.yes && !isRefresh) {
|
|
9736
|
+
const ok = await p21.confirm({
|
|
9737
|
+
message: `Pull into ${pc29.bold(cwd)}?`,
|
|
9738
|
+
initialValue: true
|
|
9739
|
+
});
|
|
9740
|
+
if (!ensureNotCancelled(ok)) {
|
|
9741
|
+
p21.outro("Aborted.");
|
|
9742
|
+
return;
|
|
9743
|
+
}
|
|
9744
|
+
}
|
|
9745
|
+
const fallbackHarness = args.harness ?? "claude-code";
|
|
9746
|
+
fs43.mkdirSync(cwd, { recursive: true });
|
|
9747
|
+
if (hasOrchManifest(cwd) && existingLink && existingLink.orchestration_id !== orchId) {
|
|
9748
|
+
p21.log.error(`This folder is linked to orchestration ${existingLink.orchestration_id}, not ${orchId}. Move to a fresh directory or unlink first.`);
|
|
9749
|
+
return;
|
|
9750
|
+
}
|
|
9751
|
+
const installedMembers = [];
|
|
9752
|
+
for (const m of cloud.members) {
|
|
9753
|
+
if (!m.manifest) {
|
|
9754
|
+
p21.log.warn(`Skipping member ${m.slug}: server did not return a manifest.`);
|
|
9755
|
+
continue;
|
|
9756
|
+
}
|
|
9757
|
+
const dest = memberDir(cwd, m.slug);
|
|
9758
|
+
const memberSp = p21.spinner();
|
|
9759
|
+
memberSp.start(`Installing ${m.slug}…`);
|
|
9760
|
+
try {
|
|
9761
|
+
await installAgentFresh({
|
|
9762
|
+
cwd: dest,
|
|
9763
|
+
agent: {
|
|
9764
|
+
id: m.agent_id,
|
|
9765
|
+
name: m.name || m.slug,
|
|
9766
|
+
slug: m.slug,
|
|
9767
|
+
tagline: undefined,
|
|
9768
|
+
org_id: cloud.group_id,
|
|
9769
|
+
team_id: cloud.group_id,
|
|
9770
|
+
url: undefined,
|
|
9771
|
+
harness: fallbackHarness
|
|
9772
|
+
},
|
|
9773
|
+
cloud: m.manifest,
|
|
9774
|
+
harness: fallbackHarness,
|
|
9775
|
+
scope: "project",
|
|
9776
|
+
pullSecrets: true
|
|
9777
|
+
});
|
|
9778
|
+
memberSp.stop(`Installed ${pc29.bold(m.slug)} ${pc29.dim(`(${m.manifest.components.length} components)`)}.`);
|
|
9779
|
+
installedMembers.push({
|
|
9780
|
+
agent_id: m.agent_id,
|
|
9781
|
+
slug: m.slug,
|
|
9782
|
+
revision: m.manifest.revision
|
|
9783
|
+
});
|
|
9784
|
+
} catch (err) {
|
|
9785
|
+
memberSp.stop(`Failed to install ${m.slug}.`);
|
|
9786
|
+
p21.log.error(err.message);
|
|
9787
|
+
}
|
|
9788
|
+
}
|
|
9789
|
+
const manifest = {
|
|
9790
|
+
schema: 1,
|
|
9791
|
+
orchestration: {
|
|
9792
|
+
name: cloud.name,
|
|
9793
|
+
...cloud.description ? { description: cloud.description } : {},
|
|
9794
|
+
...cloud.icon ? { icon: cloud.icon } : {},
|
|
9795
|
+
...cloud.icon_color ? { icon_color: cloud.icon_color } : {},
|
|
9796
|
+
...cloud.credit_limit != null ? { credit_limit: cloud.credit_limit } : {}
|
|
9797
|
+
},
|
|
9798
|
+
members: cloud.members.map((m) => ({
|
|
9799
|
+
slug: m.slug,
|
|
9800
|
+
...m.name && m.name !== m.slug ? { name: m.name } : {}
|
|
9801
|
+
})),
|
|
9802
|
+
edges: cloud.edges.map((e) => ({
|
|
9803
|
+
from: e.from_slug ?? e.from_agent_id,
|
|
9804
|
+
to: e.to_slug ?? e.to_agent_id,
|
|
9805
|
+
...e.description ? { description: e.description } : {},
|
|
9806
|
+
...e.payload_schema && Object.keys(e.payload_schema).length ? { payload_schema: e.payload_schema } : {}
|
|
9807
|
+
}))
|
|
9808
|
+
};
|
|
9809
|
+
writeOrchManifest(cwd, manifest);
|
|
9810
|
+
writeOrchLink(cwd, {
|
|
9811
|
+
schemaVersion: 1,
|
|
9812
|
+
orchestration_id: cloud.id,
|
|
9813
|
+
group_id: cloud.group_id,
|
|
9814
|
+
org_id: cloud.group_id,
|
|
9815
|
+
team_id: cloud.group_id,
|
|
9816
|
+
name: cloud.name,
|
|
9817
|
+
description: cloud.description || undefined,
|
|
9818
|
+
linked_at: new Date().toISOString()
|
|
9819
|
+
});
|
|
9820
|
+
writeOrchSyncState(cwd, {
|
|
9821
|
+
schemaVersion: 1,
|
|
9822
|
+
orchestration_id: cloud.id,
|
|
9823
|
+
revision: cloud.revision,
|
|
9824
|
+
synced_at: new Date().toISOString(),
|
|
9825
|
+
members: installedMembers,
|
|
9826
|
+
edges: cloud.edges.map((e) => ({
|
|
9827
|
+
from_slug: e.from_slug ?? e.from_agent_id,
|
|
9828
|
+
to_slug: e.to_slug ?? e.to_agent_id,
|
|
9829
|
+
description: e.description ?? "",
|
|
9830
|
+
payload_schema: e.payload_schema ?? {}
|
|
9831
|
+
}))
|
|
9832
|
+
});
|
|
9833
|
+
p21.outro(`Pulled ${cloud.name} at revision ${cloud.revision} into ${path46.basename(cwd)}/ ${pc29.dim(`(${installedMembers.length}/${cloud.members.length} members)`)}.`);
|
|
9834
|
+
}
|
|
9835
|
+
function handleApiError5(err) {
|
|
9836
|
+
if (err instanceof ApiError) {
|
|
9837
|
+
if (err.status === 401) {
|
|
9838
|
+
p21.log.error("Your session is invalid. Run `brainbase login` and try again.");
|
|
9839
|
+
} else if (err.status === 403) {
|
|
9840
|
+
p21.log.error("You do not have access to this orchestration. Ask the team owner to grant you access via `group_memberships`.");
|
|
9841
|
+
} else {
|
|
9842
|
+
p21.log.error(err.message);
|
|
9843
|
+
}
|
|
9844
|
+
} else {
|
|
9845
|
+
p21.log.error(err.message);
|
|
9846
|
+
}
|
|
9847
|
+
}
|
|
9848
|
+
|
|
9849
|
+
// src/cli/orchestration-push.ts
|
|
9850
|
+
import * as p22 from "@clack/prompts";
|
|
9851
|
+
import pc30 from "picocolors";
|
|
9852
|
+
async function runOrchestrationPush(cwd, args) {
|
|
9853
|
+
banner("orchestration push — recursively push each member, then update the graph");
|
|
9854
|
+
const link = readOrchLink(cwd);
|
|
9855
|
+
if (!link) {
|
|
9856
|
+
p22.log.warn("This folder is not linked to any orchestration.");
|
|
9857
|
+
p22.log.info(`Run ${pc30.cyan("brainbase orchestration pull <id>")} first.`);
|
|
9858
|
+
return;
|
|
9859
|
+
}
|
|
9860
|
+
if (!hasOrchManifest(cwd)) {
|
|
9861
|
+
p22.log.warn(`No ${pc30.bold(ORCH_MANIFEST_FILE)} here.`);
|
|
9862
|
+
p22.log.info(`Run ${pc30.cyan("brainbase orchestration pull")} to materialise the manifest before pushing.`);
|
|
9863
|
+
return;
|
|
9864
|
+
}
|
|
9865
|
+
let manifest;
|
|
9866
|
+
try {
|
|
9867
|
+
manifest = readOrchManifest(cwd);
|
|
9868
|
+
} catch (err) {
|
|
9869
|
+
p22.log.error(err.message);
|
|
9870
|
+
return;
|
|
9871
|
+
}
|
|
9872
|
+
const memberSlugs = new Set(manifest.members.map((m) => m.slug));
|
|
9873
|
+
for (const e of manifest.edges) {
|
|
9874
|
+
if (!memberSlugs.has(e.from)) {
|
|
9875
|
+
p22.log.error(`Edge from "${pc30.bold(e.from)}" references a slug that isn't in members.`);
|
|
9876
|
+
return;
|
|
9877
|
+
}
|
|
9878
|
+
if (!memberSlugs.has(e.to)) {
|
|
9879
|
+
p22.log.error(`Edge to "${pc30.bold(e.to)}" references a slug that isn't in members.`);
|
|
9880
|
+
return;
|
|
9881
|
+
}
|
|
9882
|
+
if (e.from === e.to) {
|
|
9883
|
+
p22.log.error(`Edge ${pc30.bold(e.from)} → ${pc30.bold(e.to)}: self-loops are not allowed.`);
|
|
9884
|
+
return;
|
|
9885
|
+
}
|
|
9886
|
+
}
|
|
9887
|
+
const slugToAgentId = new Map;
|
|
9888
|
+
const missing = [];
|
|
9889
|
+
for (const m of manifest.members) {
|
|
9890
|
+
const dir = memberDir(cwd, m.slug);
|
|
9891
|
+
const memberLink = readLink(dir);
|
|
9892
|
+
if (!memberLink) {
|
|
9893
|
+
missing.push(m.slug);
|
|
9894
|
+
continue;
|
|
9895
|
+
}
|
|
9896
|
+
slugToAgentId.set(m.slug, memberLink.agent_id);
|
|
9897
|
+
}
|
|
9898
|
+
if (missing.length) {
|
|
9899
|
+
p22.log.error(`These members have no local checkout (expected at agents/<slug>/.brainbase/link.json): ${missing.join(", ")}.`);
|
|
9900
|
+
p22.log.info(`Run ${pc30.cyan("brainbase orchestration pull")} to materialise the missing folders.`);
|
|
9901
|
+
return;
|
|
9902
|
+
}
|
|
9903
|
+
const plan = [""];
|
|
9904
|
+
plan.push(` ${pc30.bold(link.name)} ${pc30.dim(`(${link.orchestration_id})`)}`);
|
|
9905
|
+
plan.push(` ${pc30.dim(`${manifest.members.length} member${manifest.members.length === 1 ? "" : "s"}, ${manifest.edges.length} edge${manifest.edges.length === 1 ? "" : "s"}`)}`);
|
|
9906
|
+
plan.push("");
|
|
9907
|
+
if (!args.graphOnly) {
|
|
9908
|
+
plan.push(` ${pc30.dim("per-member agent push:")}`);
|
|
9909
|
+
for (const m of manifest.members) {
|
|
9910
|
+
plan.push(` ${pc30.cyan("•")} ${pc30.bold(m.slug)}`);
|
|
9911
|
+
}
|
|
9912
|
+
plan.push("");
|
|
9913
|
+
}
|
|
9914
|
+
console.log(plan.join(`
|
|
9915
|
+
`));
|
|
9916
|
+
if (!args.yes) {
|
|
9917
|
+
const ok = await p22.confirm({
|
|
9918
|
+
message: args.graphOnly ? "Push graph (members + edges) only?" : "Push each member, then update the graph?",
|
|
9919
|
+
initialValue: true
|
|
9920
|
+
});
|
|
9921
|
+
if (!ensureNotCancelled(ok)) {
|
|
9922
|
+
p22.outro("Aborted.");
|
|
9923
|
+
return;
|
|
9924
|
+
}
|
|
9925
|
+
}
|
|
9926
|
+
if (!args.graphOnly) {
|
|
9927
|
+
for (const m of manifest.members) {
|
|
9928
|
+
const dir = memberDir(cwd, m.slug);
|
|
9929
|
+
console.log("");
|
|
9930
|
+
console.log(`${pc30.dim("───")} ${pc30.bold(m.slug)} ${pc30.dim("───")}`);
|
|
9931
|
+
try {
|
|
9932
|
+
await runAgentPush(dir, { yes: true });
|
|
9933
|
+
} catch (err) {
|
|
9934
|
+
p22.log.error(`Failed to push ${m.slug}: ${err.message}`);
|
|
9935
|
+
return;
|
|
9936
|
+
}
|
|
9937
|
+
}
|
|
9938
|
+
}
|
|
9939
|
+
const memberIds = manifest.members.map((m) => slugToAgentId.get(m.slug));
|
|
9940
|
+
const edges = manifest.edges.map((e) => ({
|
|
9941
|
+
from_agent_id: slugToAgentId.get(e.from),
|
|
9942
|
+
to_agent_id: slugToAgentId.get(e.to),
|
|
9943
|
+
description: e.description ?? "",
|
|
9944
|
+
payload_schema: e.payload_schema ?? {}
|
|
9945
|
+
}));
|
|
9946
|
+
const sp = p22.spinner();
|
|
9947
|
+
sp.start("Updating orchestration graph…");
|
|
9948
|
+
const lock = readOrchSyncState(cwd);
|
|
9949
|
+
try {
|
|
9950
|
+
const updated = await api.updateOrchestration(link.orchestration_id, {
|
|
9951
|
+
name: manifest.orchestration.name,
|
|
9952
|
+
description: manifest.orchestration.description ?? "",
|
|
9953
|
+
icon: manifest.orchestration.icon,
|
|
9954
|
+
icon_color: manifest.orchestration.icon_color,
|
|
9955
|
+
credit_limit: manifest.orchestration.credit_limit,
|
|
9956
|
+
members: memberIds,
|
|
9957
|
+
edges,
|
|
9958
|
+
...lock?.revision != null ? { base_revision: lock.revision } : {}
|
|
9959
|
+
});
|
|
9960
|
+
sp.stop(`Graph updated. New cloud revision ${updated.revision}.`);
|
|
9961
|
+
writeOrchSyncState(cwd, {
|
|
9962
|
+
schemaVersion: 1,
|
|
9963
|
+
orchestration_id: updated.id,
|
|
9964
|
+
revision: updated.revision,
|
|
9965
|
+
synced_at: new Date().toISOString(),
|
|
9966
|
+
members: updated.members.map((m) => ({
|
|
9967
|
+
agent_id: m.agent_id,
|
|
9968
|
+
slug: m.slug,
|
|
9969
|
+
revision: m.revision ?? 0
|
|
9970
|
+
})),
|
|
9971
|
+
edges: updated.edges.map((e) => ({
|
|
9972
|
+
from_slug: e.from_slug ?? e.from_agent_id,
|
|
9973
|
+
to_slug: e.to_slug ?? e.to_agent_id,
|
|
9974
|
+
description: e.description ?? "",
|
|
9975
|
+
payload_schema: e.payload_schema ?? {}
|
|
9976
|
+
}))
|
|
9977
|
+
});
|
|
9978
|
+
p22.outro(`Pushed ${link.name} at revision ${updated.revision}.`);
|
|
9979
|
+
} catch (err) {
|
|
9980
|
+
sp.stop("Failed.");
|
|
9981
|
+
handleApiError6(err);
|
|
9982
|
+
}
|
|
9983
|
+
}
|
|
9984
|
+
function handleApiError6(err) {
|
|
9985
|
+
if (err instanceof ApiError) {
|
|
9986
|
+
if (err.status === 401) {
|
|
9987
|
+
p22.log.error("Your session is invalid. Run `brainbase login` and try again.");
|
|
9988
|
+
} else if (err.status === 403) {
|
|
9989
|
+
p22.log.error("You do not have access to this orchestration.");
|
|
9990
|
+
} else if (err.status === 409) {
|
|
9991
|
+
p22.log.error(err.message);
|
|
9992
|
+
p22.log.info(`Run ${pc30.cyan("brainbase orchestration pull")} to reconcile, then push again.`);
|
|
9993
|
+
} else {
|
|
9994
|
+
p22.log.error(err.message);
|
|
9995
|
+
}
|
|
9996
|
+
} else {
|
|
9997
|
+
p22.log.error(err.message);
|
|
9998
|
+
}
|
|
9999
|
+
}
|
|
10000
|
+
|
|
10001
|
+
// src/cli/orchestration-status.ts
|
|
10002
|
+
import * as p23 from "@clack/prompts";
|
|
10003
|
+
import pc31 from "picocolors";
|
|
10004
|
+
async function runOrchestrationStatus(cwd) {
|
|
10005
|
+
banner("orchestration status — what changed locally, remotely, both");
|
|
10006
|
+
const link = readOrchLink(cwd);
|
|
10007
|
+
if (!link) {
|
|
10008
|
+
p23.log.warn("This folder is not linked to any orchestration.");
|
|
10009
|
+
p23.log.info(`Run ${pc31.cyan("brainbase orchestration pull <id>")} first.`);
|
|
10010
|
+
return;
|
|
10011
|
+
}
|
|
10012
|
+
const localManifest = hasOrchManifest(cwd) ? readOrchManifest(cwd) : null;
|
|
10013
|
+
const lock = readOrchSyncState(cwd);
|
|
10014
|
+
let cloud;
|
|
10015
|
+
const sp = p23.spinner();
|
|
10016
|
+
sp.start(`Fetching ${link.name}…`);
|
|
10017
|
+
try {
|
|
10018
|
+
cloud = await api.getOrchestration(link.orchestration_id);
|
|
10019
|
+
sp.stop(`Cloud revision ${cloud.revision}.`);
|
|
10020
|
+
} catch (err) {
|
|
10021
|
+
sp.stop("Failed to reach brainbase.");
|
|
10022
|
+
if (err instanceof ApiError && err.status === 401) {
|
|
10023
|
+
p23.log.error("Your session is invalid. Run `brainbase login` and try again.");
|
|
10024
|
+
} else {
|
|
10025
|
+
p23.log.error(err.message);
|
|
10026
|
+
}
|
|
10027
|
+
return;
|
|
10028
|
+
}
|
|
10029
|
+
const lines = [];
|
|
10030
|
+
lines.push("");
|
|
10031
|
+
lines.push(` ${pc31.bold(link.name)} ${pc31.dim(`(${link.orchestration_id})`)}`);
|
|
10032
|
+
lines.push(` ${pc31.dim("revision")} cloud ${cloud.revision}${lock ? ` · lock ${lock.revision}` : " · never pulled"}`);
|
|
10033
|
+
lines.push("");
|
|
10034
|
+
const cloudMemberSet = new Set(cloud.members.map((m) => m.slug));
|
|
10035
|
+
const localMemberSet = new Set((localManifest?.members ?? []).map((m) => m.slug));
|
|
10036
|
+
const membersAdded = [...localMemberSet].filter((s) => !cloudMemberSet.has(s));
|
|
10037
|
+
const membersRemoved = [...cloudMemberSet].filter((s) => !localMemberSet.has(s));
|
|
10038
|
+
if (membersAdded.length || membersRemoved.length) {
|
|
10039
|
+
lines.push(` ${pc31.bold("members")}`);
|
|
10040
|
+
for (const slug of membersAdded) {
|
|
10041
|
+
lines.push(` ${pc31.yellow("→ push")} added in yaml: ${pc31.bold(slug)}`);
|
|
10042
|
+
}
|
|
10043
|
+
for (const slug of membersRemoved) {
|
|
10044
|
+
lines.push(` ${pc31.cyan("← pull")} added on cloud: ${pc31.bold(slug)}`);
|
|
10045
|
+
}
|
|
10046
|
+
lines.push("");
|
|
10047
|
+
}
|
|
10048
|
+
const cloudEdgeKey = (e) => `${e.from_slug ?? e.from_agent_id}->${e.to_slug ?? e.to_agent_id}|${e.description ?? ""}`;
|
|
10049
|
+
const localEdgeKey = (e) => `${e.from}->${e.to}|${e.description ?? ""}`;
|
|
10050
|
+
const cloudEdges = new Map;
|
|
10051
|
+
for (const e of cloud.edges)
|
|
10052
|
+
cloudEdges.set(cloudEdgeKey(e), true);
|
|
10053
|
+
const localEdges = new Map;
|
|
10054
|
+
for (const e of localManifest?.edges ?? [])
|
|
10055
|
+
localEdges.set(localEdgeKey(e), true);
|
|
10056
|
+
const edgesAdded = [...localEdges.keys()].filter((k) => !cloudEdges.has(k));
|
|
10057
|
+
const edgesRemoved = [...cloudEdges.keys()].filter((k) => !localEdges.has(k));
|
|
10058
|
+
if (edgesAdded.length || edgesRemoved.length) {
|
|
10059
|
+
lines.push(` ${pc31.bold("edges")}`);
|
|
10060
|
+
for (const k of edgesAdded)
|
|
10061
|
+
lines.push(` ${pc31.yellow("→ push")} added in yaml: ${k}`);
|
|
10062
|
+
for (const k of edgesRemoved)
|
|
10063
|
+
lines.push(` ${pc31.cyan("← pull")} added on cloud: ${k}`);
|
|
10064
|
+
lines.push("");
|
|
10065
|
+
}
|
|
10066
|
+
const lockByAgentId = new Map((lock?.members ?? []).map((m) => [m.agent_id, m]));
|
|
10067
|
+
const memberDrift = [];
|
|
10068
|
+
for (const m of cloud.members) {
|
|
10069
|
+
const dir = memberDir(cwd, m.slug);
|
|
10070
|
+
const memberSync = readSyncState(dir);
|
|
10071
|
+
if (!memberSync) {
|
|
10072
|
+
memberDrift.push({ slug: m.slug, reason: "no local checkout" });
|
|
10073
|
+
continue;
|
|
10074
|
+
}
|
|
10075
|
+
const lockEntry = lockByAgentId.get(m.agent_id);
|
|
10076
|
+
const lockedRev = lockEntry?.revision;
|
|
10077
|
+
if (m.revision != null && lockedRev != null && m.revision !== lockedRev) {
|
|
10078
|
+
memberDrift.push({
|
|
10079
|
+
slug: m.slug,
|
|
10080
|
+
reason: `cloud rev ${m.revision} ≠ lock ${lockedRev}`
|
|
10081
|
+
});
|
|
10082
|
+
}
|
|
10083
|
+
}
|
|
10084
|
+
if (memberDrift.length) {
|
|
10085
|
+
lines.push(` ${pc31.bold("member content drift")}`);
|
|
10086
|
+
for (const d of memberDrift) {
|
|
10087
|
+
lines.push(` ${pc31.cyan("?")} ${pc31.bold(d.slug)} ${pc31.dim("— " + d.reason)}`);
|
|
10088
|
+
}
|
|
10089
|
+
lines.push(` ${pc31.dim("cd into each member folder and run")} ${pc31.cyan("brainbase agent status")}`);
|
|
10090
|
+
lines.push("");
|
|
10091
|
+
}
|
|
10092
|
+
if (!membersAdded.length && !membersRemoved.length && !edgesAdded.length && !edgesRemoved.length && !memberDrift.length) {
|
|
10093
|
+
lines.push(` ${pc31.green("✓")} everything is in sync`);
|
|
10094
|
+
lines.push("");
|
|
10095
|
+
console.log(lines.join(`
|
|
10096
|
+
`));
|
|
10097
|
+
return;
|
|
10098
|
+
}
|
|
10099
|
+
lines.push(` ${pc31.dim("run")} ${pc31.cyan("brainbase orchestration pull")} ${pc31.dim("to apply cloud changes,")} ${pc31.cyan("brainbase orchestration push")} ${pc31.dim("to send yours")}`);
|
|
10100
|
+
lines.push("");
|
|
10101
|
+
console.log(lines.join(`
|
|
10102
|
+
`));
|
|
10103
|
+
}
|
|
10104
|
+
|
|
10105
|
+
// src/cli/orchestration-list.ts
|
|
10106
|
+
import * as p24 from "@clack/prompts";
|
|
10107
|
+
import pc32 from "picocolors";
|
|
10108
|
+
async function runOrchestrationList(args) {
|
|
10109
|
+
banner("orchestration list — orchestrations under a team");
|
|
10110
|
+
let orgId = args.orgId;
|
|
10111
|
+
let teamId = args.teamId;
|
|
10112
|
+
if (!orgId) {
|
|
10113
|
+
let orgs;
|
|
10114
|
+
try {
|
|
10115
|
+
orgs = await api.listOrgs();
|
|
10116
|
+
} catch (err) {
|
|
10117
|
+
handleApiError7(err);
|
|
10118
|
+
return;
|
|
10119
|
+
}
|
|
10120
|
+
if (orgs.length === 0) {
|
|
10121
|
+
p24.log.warn("You are not a member of any organization.");
|
|
10122
|
+
return;
|
|
10123
|
+
}
|
|
10124
|
+
if (orgs.length === 1) {
|
|
10125
|
+
orgId = orgs[0].id;
|
|
10126
|
+
} else {
|
|
10127
|
+
const choice = await p24.select({
|
|
10128
|
+
message: "Which organization?",
|
|
10129
|
+
options: orgs.map((o) => ({ value: o.id, label: o.name }))
|
|
10130
|
+
});
|
|
10131
|
+
orgId = ensureNotCancelled(choice);
|
|
10132
|
+
}
|
|
10133
|
+
}
|
|
10134
|
+
if (!teamId) {
|
|
10135
|
+
let teams;
|
|
10136
|
+
try {
|
|
10137
|
+
teams = await api.listTeams(orgId);
|
|
10138
|
+
} catch (err) {
|
|
10139
|
+
handleApiError7(err);
|
|
10140
|
+
return;
|
|
10141
|
+
}
|
|
10142
|
+
if (teams.length === 0) {
|
|
10143
|
+
p24.log.warn("No teams under this organization. Create one in the web app first.");
|
|
10144
|
+
return;
|
|
10145
|
+
}
|
|
10146
|
+
if (teams.length === 1) {
|
|
10147
|
+
teamId = teams[0].id;
|
|
10148
|
+
} else {
|
|
10149
|
+
const choice = await p24.select({
|
|
10150
|
+
message: "Which team?",
|
|
10151
|
+
options: teams.map((t) => ({ value: t.id, label: t.name }))
|
|
10152
|
+
});
|
|
10153
|
+
teamId = ensureNotCancelled(choice);
|
|
10154
|
+
}
|
|
10155
|
+
}
|
|
10156
|
+
let items;
|
|
10157
|
+
const sp = p24.spinner();
|
|
10158
|
+
sp.start("Fetching orchestrations…");
|
|
10159
|
+
try {
|
|
10160
|
+
items = await api.listOrchestrations(orgId, teamId);
|
|
10161
|
+
sp.stop(`${items.length} orchestration${items.length === 1 ? "" : "s"}.`);
|
|
10162
|
+
} catch (err) {
|
|
10163
|
+
sp.stop("Failed.");
|
|
10164
|
+
handleApiError7(err);
|
|
10165
|
+
return;
|
|
10166
|
+
}
|
|
10167
|
+
if (items.length === 0) {
|
|
10168
|
+
p24.log.info("This team has no orchestrations yet.");
|
|
10169
|
+
return;
|
|
10170
|
+
}
|
|
10171
|
+
const lines = [""];
|
|
10172
|
+
for (const o of items) {
|
|
10173
|
+
lines.push(` ${pc32.bold(o.name)} ${pc32.dim(o.id)}`);
|
|
10174
|
+
if (o.description)
|
|
10175
|
+
lines.push(` ${pc32.dim(o.description)}`);
|
|
10176
|
+
lines.push(` ${pc32.dim(`${o.member_count} member${o.member_count === 1 ? "" : "s"} · ${o.edge_count} edge${o.edge_count === 1 ? "" : "s"}`)}`);
|
|
10177
|
+
lines.push("");
|
|
10178
|
+
}
|
|
10179
|
+
lines.push(` ${pc32.dim("pull one with")} ${pc32.cyan("brainbase orchestration pull <id>")}`);
|
|
10180
|
+
lines.push("");
|
|
10181
|
+
console.log(lines.join(`
|
|
10182
|
+
`));
|
|
10183
|
+
}
|
|
10184
|
+
function handleApiError7(err) {
|
|
10185
|
+
if (err instanceof ApiError) {
|
|
10186
|
+
if (err.status === 401) {
|
|
10187
|
+
p24.log.error("Your session is invalid. Run `brainbase login` and try again.");
|
|
10188
|
+
} else {
|
|
10189
|
+
p24.log.error(err.message);
|
|
10190
|
+
}
|
|
10191
|
+
} else {
|
|
10192
|
+
p24.log.error(err.message);
|
|
10193
|
+
}
|
|
10194
|
+
}
|
|
10195
|
+
|
|
10196
|
+
// src/cli/orchestration.ts
|
|
10197
|
+
async function runOrchestration(cwd, sub, args, opts) {
|
|
10198
|
+
switch (sub) {
|
|
10199
|
+
case "pull":
|
|
10200
|
+
await runOrchestrationPull(cwd, {
|
|
10201
|
+
orchestrationId: args[0],
|
|
10202
|
+
yes: opts.yes,
|
|
10203
|
+
harness: opts.harness
|
|
10204
|
+
});
|
|
10205
|
+
return;
|
|
10206
|
+
case "push":
|
|
10207
|
+
await runOrchestrationPush(cwd, {
|
|
10208
|
+
yes: opts.yes,
|
|
10209
|
+
graphOnly: opts.graphOnly
|
|
10210
|
+
});
|
|
10211
|
+
return;
|
|
10212
|
+
case "status":
|
|
10213
|
+
await runOrchestrationStatus(cwd);
|
|
10214
|
+
return;
|
|
10215
|
+
case "list":
|
|
10216
|
+
case "ls":
|
|
10217
|
+
await runOrchestrationList({ orgId: opts.orgId, teamId: opts.teamId });
|
|
10218
|
+
return;
|
|
10219
|
+
case undefined:
|
|
10220
|
+
case "help":
|
|
10221
|
+
case "-h":
|
|
10222
|
+
case "--help":
|
|
10223
|
+
printHelp2();
|
|
10224
|
+
return;
|
|
10225
|
+
default:
|
|
10226
|
+
console.error(`Unknown orchestration subcommand: ${sub}
|
|
10227
|
+
`);
|
|
10228
|
+
printHelp2();
|
|
10229
|
+
process.exit(1);
|
|
10230
|
+
}
|
|
10231
|
+
}
|
|
10232
|
+
function printHelp2() {
|
|
10233
|
+
const out = [];
|
|
10234
|
+
out.push("");
|
|
10235
|
+
out.push(` ${pc33.bold("brainbase orchestration")} ${pc33.dim("<sub> [options]")}`);
|
|
10236
|
+
out.push("");
|
|
10237
|
+
out.push(` ${pc33.cyan("pull")} ${pc33.dim("<id>")} ${pc33.dim("fetch orchestration + every member agent into this folder")}`);
|
|
10238
|
+
out.push(` ${pc33.cyan("push")} ${pc33.dim("push each member, then update the orchestration graph")}`);
|
|
10239
|
+
out.push(` ${pc33.cyan("status")} ${pc33.dim("show what would push and what would pull")}`);
|
|
10240
|
+
out.push(` ${pc33.cyan("list")} ${pc33.dim("list orchestrations under a team")}`);
|
|
10241
|
+
out.push("");
|
|
10242
|
+
out.push(` ${pc33.bold("Flags")}`);
|
|
10243
|
+
out.push(` ${pc33.dim("--yes, -y")} skip confirmations`);
|
|
10244
|
+
out.push(` ${pc33.dim("--harness <id>")} harness for newly-created member folders (default claude-code)`);
|
|
10245
|
+
out.push(` ${pc33.dim("--graph-only")} for push: only update members + edges, skip per-member push`);
|
|
10246
|
+
out.push(` ${pc33.dim("--org <id>")} for list: org id (CLI vocab — DB teams.id)`);
|
|
10247
|
+
out.push(` ${pc33.dim("--team <id>")} for list: team id (CLI vocab — DB groups.id)`);
|
|
10248
|
+
out.push("");
|
|
10249
|
+
console.log(out.join(`
|
|
10250
|
+
`));
|
|
10251
|
+
}
|
|
10252
|
+
|
|
9313
10253
|
// src/cli/run.ts
|
|
9314
10254
|
import { spawn as spawn2 } from "node:child_process";
|
|
9315
10255
|
async function runRun(cwd, args) {
|
|
@@ -9349,17 +10289,17 @@ async function runRun(cwd, args) {
|
|
|
9349
10289
|
}
|
|
9350
10290
|
|
|
9351
10291
|
// src/cli/publish.ts
|
|
9352
|
-
import * as
|
|
9353
|
-
import
|
|
10292
|
+
import * as p25 from "@clack/prompts";
|
|
10293
|
+
import pc34 from "picocolors";
|
|
9354
10294
|
async function runPublish(cwd, _args) {
|
|
9355
10295
|
banner("publish — send your changes to the team");
|
|
9356
10296
|
const link = readLink(cwd);
|
|
9357
10297
|
if (!link) {
|
|
9358
|
-
|
|
9359
|
-
|
|
10298
|
+
p25.log.warn("This folder is not linked to any agent.");
|
|
10299
|
+
p25.log.info(`Run ${pc34.cyan("brainbase link")} first.`);
|
|
9360
10300
|
return;
|
|
9361
10301
|
}
|
|
9362
|
-
|
|
10302
|
+
p25.log.info(`${pc34.bold("publish")} is coming soon — for now, edit the agent on the web app and run ${pc34.cyan("brainbase sync")} to bring changes here.`);
|
|
9363
10303
|
}
|
|
9364
10304
|
|
|
9365
10305
|
// src/ui/ink/StatusCard.tsx
|
|
@@ -9658,8 +10598,8 @@ async function runStatus(cwd) {
|
|
|
9658
10598
|
}
|
|
9659
10599
|
|
|
9660
10600
|
// src/cli/token.ts
|
|
9661
|
-
import * as
|
|
9662
|
-
import
|
|
10601
|
+
import * as p26 from "@clack/prompts";
|
|
10602
|
+
import pc35 from "picocolors";
|
|
9663
10603
|
|
|
9664
10604
|
// src/ui/ink/TokenCards.tsx
|
|
9665
10605
|
import { Box as Box15, Text as Text16 } from "ink";
|
|
@@ -9905,7 +10845,7 @@ async function runTokenCreate(args) {
|
|
|
9905
10845
|
banner("token create — make a long-lived CLI key");
|
|
9906
10846
|
let name = args.name;
|
|
9907
10847
|
if (!name) {
|
|
9908
|
-
const ans = await
|
|
10848
|
+
const ans = await p26.text({
|
|
9909
10849
|
message: "Token label",
|
|
9910
10850
|
placeholder: "my-laptop or ci-runner",
|
|
9911
10851
|
validate: (v) => v.length === 0 ? "Required." : undefined
|
|
@@ -9913,13 +10853,13 @@ async function runTokenCreate(args) {
|
|
|
9913
10853
|
name = ensureNotCancelled(ans);
|
|
9914
10854
|
}
|
|
9915
10855
|
const scopes = args.scopes && args.scopes.length > 0 ? args.scopes : DEFAULT_SCOPES;
|
|
9916
|
-
const
|
|
9917
|
-
|
|
10856
|
+
const spinner21 = p26.spinner();
|
|
10857
|
+
spinner21.start("Creating token…");
|
|
9918
10858
|
const created = await registryApi.createCliToken({
|
|
9919
10859
|
name,
|
|
9920
10860
|
scopes
|
|
9921
10861
|
});
|
|
9922
|
-
|
|
10862
|
+
spinner21.stop("Token created.");
|
|
9923
10863
|
writeToken(created.token, name);
|
|
9924
10864
|
await showTokenCreatedCard({
|
|
9925
10865
|
token: created.token,
|
|
@@ -9953,8 +10893,8 @@ async function runTokenRevoke(args) {
|
|
|
9953
10893
|
process.exit(1);
|
|
9954
10894
|
}
|
|
9955
10895
|
if (!args.yes) {
|
|
9956
|
-
const ok = await
|
|
9957
|
-
message: `Revoke token ${
|
|
10896
|
+
const ok = await p26.confirm({
|
|
10897
|
+
message: `Revoke token ${pc35.bold(args.id)}? CIs and machines using it will stop working.`,
|
|
9958
10898
|
initialValue: false
|
|
9959
10899
|
});
|
|
9960
10900
|
if (!ensureNotCancelled(ok))
|
|
@@ -9969,7 +10909,7 @@ async function runTokenRevoke(args) {
|
|
|
9969
10909
|
}
|
|
9970
10910
|
async function runTokenClear() {
|
|
9971
10911
|
if (!readToken()) {
|
|
9972
|
-
console.log(
|
|
10912
|
+
console.log(pc35.dim("No local token stored."));
|
|
9973
10913
|
return;
|
|
9974
10914
|
}
|
|
9975
10915
|
clearToken();
|
|
@@ -10020,17 +10960,17 @@ async function runToken(sub, rest, args) {
|
|
|
10020
10960
|
function printTokenHelp() {
|
|
10021
10961
|
const out = [];
|
|
10022
10962
|
out.push("");
|
|
10023
|
-
out.push(` ${
|
|
10963
|
+
out.push(` ${pc35.bold("brainbase token")} ${pc35.dim("<command>")}`);
|
|
10024
10964
|
out.push("");
|
|
10025
|
-
out.push(` ${
|
|
10026
|
-
out.push(` ${
|
|
10027
|
-
out.push(` ${
|
|
10028
|
-
out.push(` ${
|
|
10965
|
+
out.push(` ${pc35.cyan("create")} ${pc35.dim("issue a new long-lived CLI key (PAT)")}`);
|
|
10966
|
+
out.push(` ${pc35.cyan("list")} ${pc35.dim("show your active tokens")}`);
|
|
10967
|
+
out.push(` ${pc35.cyan("revoke")} ${pc35.dim("<id>")} ${pc35.dim("revoke a token by id")}`);
|
|
10968
|
+
out.push(` ${pc35.cyan("clear")} ${pc35.dim("forget the local token (does not revoke)")}`);
|
|
10029
10969
|
out.push("");
|
|
10030
|
-
out.push(` ${
|
|
10031
|
-
out.push(` ${
|
|
10032
|
-
out.push(` ${
|
|
10033
|
-
out.push(` ${
|
|
10970
|
+
out.push(` ${pc35.bold("create flags")}`);
|
|
10971
|
+
out.push(` ${pc35.cyan("--name, -n")} ${pc35.dim("<label>")} ${pc35.dim("token label (prompted if omitted)")}`);
|
|
10972
|
+
out.push(` ${pc35.cyan("--scopes")} ${pc35.dim("<list>")} ${pc35.dim("comma-separated; allowed: read, publish, admin")}`);
|
|
10973
|
+
out.push(` ${pc35.dim("default: read,publish")}`);
|
|
10034
10974
|
out.push("");
|
|
10035
10975
|
console.log(out.join(`
|
|
10036
10976
|
`));
|
|
@@ -10050,81 +10990,88 @@ var PROTECTED = new Set([
|
|
|
10050
10990
|
function help() {
|
|
10051
10991
|
const out = [];
|
|
10052
10992
|
out.push("");
|
|
10053
|
-
out.push(` ${brandTint("◆")} ${
|
|
10054
|
-
out.push(` ${
|
|
10993
|
+
out.push(` ${brandTint("◆")} ${pc36.bold("brainbase")} ${pc36.dim("v0.2.0")}`);
|
|
10994
|
+
out.push(` ${pc36.dim("connect your local agent to the brainbase platform")}`);
|
|
10055
10995
|
out.push("");
|
|
10056
10996
|
out.push(divider("USAGE"));
|
|
10057
10997
|
out.push("");
|
|
10058
|
-
out.push(` ${
|
|
10998
|
+
out.push(` ${pc36.bold("brainbase")} ${pc36.dim("<command> [options]")}`);
|
|
10059
10999
|
out.push("");
|
|
10060
11000
|
out.push(divider("AUTH"));
|
|
10061
11001
|
out.push("");
|
|
10062
|
-
out.push(` ${
|
|
10063
|
-
out.push(` ${
|
|
10064
|
-
out.push(` ${
|
|
11002
|
+
out.push(` ${pc36.cyan("login")} ${pc36.dim(" open the web app and connect this device")}`);
|
|
11003
|
+
out.push(` ${pc36.cyan("logout")} ${pc36.dim(" clear the local session")}`);
|
|
11004
|
+
out.push(` ${pc36.cyan("whoami")} ${pc36.dim(" show the current user")}`);
|
|
10065
11005
|
out.push("");
|
|
10066
11006
|
out.push(divider("LINKED AGENT"));
|
|
10067
11007
|
out.push("");
|
|
10068
|
-
out.push(` ${
|
|
10069
|
-
out.push(` ${
|
|
10070
|
-
out.push(` ${
|
|
10071
|
-
out.push(` ${
|
|
10072
|
-
out.push(` ${
|
|
10073
|
-
out.push(` ${
|
|
10074
|
-
out.push(` ${
|
|
10075
|
-
out.push(` ${
|
|
10076
|
-
out.push(` ${
|
|
11008
|
+
out.push(` ${pc36.cyan("agent create")} ${pc36.dim("make a new agent on the cloud and link this folder")}`);
|
|
11009
|
+
out.push(` ${pc36.cyan("link")} ${pc36.dim("attach this folder to an existing agent")}`);
|
|
11010
|
+
out.push(` ${pc36.cyan("agent pull")} ${pc36.dim("bring cloud changes into this folder")}`);
|
|
11011
|
+
out.push(` ${pc36.cyan("agent push")} ${pc36.dim("send local changes to the cloud")}`);
|
|
11012
|
+
out.push(` ${pc36.cyan("agent status")} ${pc36.dim("show what would pull and what would push")}`);
|
|
11013
|
+
out.push(` ${pc36.cyan("agent env")} ${pc36.dim("print export lines for `eval $(brainbase agent env)`")}`);
|
|
11014
|
+
out.push(` ${pc36.cyan("run")} ${pc36.dim("<cmd> [args...]")} ${pc36.dim("run <cmd> with secrets.env loaded into env")}`);
|
|
11015
|
+
out.push(` ${pc36.cyan("status")} ${pc36.dim("show what this folder is linked to")}`);
|
|
11016
|
+
out.push(` ${pc36.cyan("unlink")} ${pc36.dim("disconnect this folder")}`);
|
|
11017
|
+
out.push("");
|
|
11018
|
+
out.push(divider("ORCHESTRATIONS"));
|
|
11019
|
+
out.push("");
|
|
11020
|
+
out.push(` ${pc36.cyan("orchestration list")} ${pc36.dim("list orchestrations under a team")}`);
|
|
11021
|
+
out.push(` ${pc36.cyan("orchestration pull")} ${pc36.dim("<id>")} ${pc36.dim("recursively fetch an orchestration + every member agent")}`);
|
|
11022
|
+
out.push(` ${pc36.cyan("orchestration push")} ${pc36.dim("recursively push each member, then update the graph")}`);
|
|
11023
|
+
out.push(` ${pc36.cyan("orchestration status")} ${pc36.dim("show what would push and what would pull")}`);
|
|
10077
11024
|
out.push("");
|
|
10078
11025
|
out.push(divider("TEMPLATES"));
|
|
10079
11026
|
out.push("");
|
|
10080
|
-
out.push(` ${
|
|
10081
|
-
out.push(` ${
|
|
10082
|
-
out.push(` ${
|
|
10083
|
-
out.push(` ${
|
|
10084
|
-
out.push(` ${
|
|
10085
|
-
out.push(` ${
|
|
10086
|
-
out.push(` ${
|
|
11027
|
+
out.push(` ${pc36.cyan("template pack")} ${pc36.dim("bundle the current agent into a template")}`);
|
|
11028
|
+
out.push(` ${pc36.cyan("template publish")} ${pc36.dim("upload a template to the registry")}`);
|
|
11029
|
+
out.push(` ${pc36.cyan("template search")} ${pc36.dim("[query]")} ${pc36.dim("search the registry")}`);
|
|
11030
|
+
out.push(` ${pc36.cyan("template info")} ${pc36.dim("<creator/slug>")} ${pc36.dim("show registry details for a template")}`);
|
|
11031
|
+
out.push(` ${pc36.cyan("template onboard")} ${pc36.dim("<creator/slug>")} ${pc36.dim("install (or refresh) a template")}`);
|
|
11032
|
+
out.push(` ${pc36.cyan("template list")} ${pc36.dim("show installed templates")}`);
|
|
11033
|
+
out.push(` ${pc36.cyan("template remove")} ${pc36.dim("<creator/slug>")} ${pc36.dim("uninstall a template")}`);
|
|
10087
11034
|
out.push("");
|
|
10088
11035
|
out.push(divider("SKILLS"));
|
|
10089
11036
|
out.push("");
|
|
10090
|
-
out.push(` ${
|
|
10091
|
-
out.push(` ${
|
|
10092
|
-
out.push(` ${
|
|
10093
|
-
out.push(` ${
|
|
10094
|
-
out.push(` ${
|
|
10095
|
-
out.push(` ${
|
|
10096
|
-
out.push(` ${
|
|
11037
|
+
out.push(` ${pc36.cyan("skill add")} ${pc36.dim("<source>")} ${pc36.dim("install a skill (github / git / brainbase)")}`);
|
|
11038
|
+
out.push(` ${pc36.cyan("skill list")} ${pc36.dim("show locally installed skills + their source")}`);
|
|
11039
|
+
out.push(` ${pc36.cyan("skill update")} ${pc36.dim("<slug>")} ${pc36.dim("re-fetch a skill from its recorded source")}`);
|
|
11040
|
+
out.push(` ${pc36.cyan("skill remove")} ${pc36.dim("<slug>")} ${pc36.dim("uninstall a skill")}`);
|
|
11041
|
+
out.push(` ${pc36.cyan("skill search")} ${pc36.dim("[query]")} ${pc36.dim("search the brainbase skill registry")}`);
|
|
11042
|
+
out.push(` ${pc36.cyan("skill info")} ${pc36.dim("<creator/slug>")} ${pc36.dim("show registry details for a skill")}`);
|
|
11043
|
+
out.push(` ${pc36.cyan("skill publish")} ${pc36.dim("[creator/slug][@v]")} ${pc36.dim("publish a local skill to the registry")}`);
|
|
10097
11044
|
out.push("");
|
|
10098
11045
|
out.push(divider("CLI TOKENS"));
|
|
10099
11046
|
out.push("");
|
|
10100
|
-
out.push(` ${
|
|
10101
|
-
out.push(` ${
|
|
10102
|
-
out.push(` ${
|
|
11047
|
+
out.push(` ${pc36.cyan("token create")} ${pc36.dim("issue a long-lived CLI key for CI / scripts")}`);
|
|
11048
|
+
out.push(` ${pc36.cyan("token list")} ${pc36.dim("show your active tokens")}`);
|
|
11049
|
+
out.push(` ${pc36.cyan("token revoke")} ${pc36.dim("<id>")} ${pc36.dim("revoke a token")}`);
|
|
10103
11050
|
out.push("");
|
|
10104
11051
|
out.push(divider("FLAGS"));
|
|
10105
11052
|
out.push("");
|
|
10106
|
-
out.push(` ${
|
|
10107
|
-
out.push(` ${
|
|
10108
|
-
out.push(` ${
|
|
10109
|
-
out.push(` ${
|
|
10110
|
-
out.push(` ${
|
|
10111
|
-
out.push(` ${
|
|
10112
|
-
out.push(` ${
|
|
10113
|
-
out.push(` ${
|
|
11053
|
+
out.push(` ${pc36.dim("--harness <id>")} force harness for onboard / sync (e.g. claude-code)`);
|
|
11054
|
+
out.push(` ${pc36.dim("--scope <s>")} force scope: global | project`);
|
|
11055
|
+
out.push(` ${pc36.dim("--yes, -y")} skip confirmations / auto-overwrite`);
|
|
11056
|
+
out.push(` ${pc36.dim("--agent <id>")} for link: attach this folder to an existing agent non-interactively`);
|
|
11057
|
+
out.push(` ${pc36.dim("--no-tracking")} for link: skip routing LLM traffic through brainbase`);
|
|
11058
|
+
out.push(` ${pc36.dim("--shell <sh|fish>")} for agent env: pick output format (auto-detected from $SHELL)`);
|
|
11059
|
+
out.push(` ${pc36.dim("--all")} for template list: include installs from other folders`);
|
|
11060
|
+
out.push(` ${pc36.dim("--web <url>")} for login: web app URL (default https://new.usekafka.com)`);
|
|
10114
11061
|
out.push("");
|
|
10115
11062
|
out.push(divider("ENV"));
|
|
10116
11063
|
out.push("");
|
|
10117
|
-
out.push(` ${
|
|
10118
|
-
out.push(` ${
|
|
10119
|
-
out.push(` ${
|
|
10120
|
-
out.push(` ${
|
|
10121
|
-
out.push(` ${
|
|
10122
|
-
out.push(` ${
|
|
11064
|
+
out.push(` ${pc36.dim("BRAINBASE_HOME")} override the local config dir (default ~/.brainbase)`);
|
|
11065
|
+
out.push(` ${pc36.dim("BRAINBASE_WEB_URL")} override the web app URL used by login`);
|
|
11066
|
+
out.push(` ${pc36.dim("BRAINBASE_API_URL")} override the API URL used by link / sync`);
|
|
11067
|
+
out.push(` ${pc36.dim("BRAINBASE_REGISTRY_URL")} override the registry API URL`);
|
|
11068
|
+
out.push(` ${pc36.dim("BRAINBASE_TOKEN")} long-lived CLI PAT (overrides token.json)`);
|
|
11069
|
+
out.push(` ${pc36.dim("BRAINBASE_SKIP_AUTH")} bypass the auth gate for development`);
|
|
10123
11070
|
out.push("");
|
|
10124
11071
|
out.push(divider("HARNESSES"));
|
|
10125
11072
|
out.push("");
|
|
10126
|
-
out.push(` ${
|
|
10127
|
-
out.push(` ${
|
|
11073
|
+
out.push(` ${pc36.dim("•")} ${pc36.bold("claude-code")} ${pc36.dim("skills, mcps, agents, commands, instructions, files")}`);
|
|
11074
|
+
out.push(` ${pc36.dim("•")} ${pc36.bold("codex")} ${pc36.dim("skills, mcps, commands, instructions, files")}`);
|
|
10128
11075
|
out.push("");
|
|
10129
11076
|
console.log(out.join(`
|
|
10130
11077
|
`));
|
|
@@ -10168,13 +11115,13 @@ async function requireAuth(cmd) {
|
|
|
10168
11115
|
if (status.ok)
|
|
10169
11116
|
return;
|
|
10170
11117
|
console.error("");
|
|
10171
|
-
console.error(` ${brandTint("◆")} ${
|
|
11118
|
+
console.error(` ${brandTint("◆")} ${pc36.bold("brainbase")}`);
|
|
10172
11119
|
console.error("");
|
|
10173
|
-
console.error(` ${
|
|
11120
|
+
console.error(` ${pc36.red("✗")} You need to sign in to use ${pc36.bold("brainbase " + cmd)}.`);
|
|
10174
11121
|
if (status.reason)
|
|
10175
|
-
console.error(` ${
|
|
11122
|
+
console.error(` ${pc36.dim(status.reason)}`);
|
|
10176
11123
|
console.error("");
|
|
10177
|
-
console.error(` Run ${
|
|
11124
|
+
console.error(` Run ${pc36.cyan("brainbase login")} to connect this device.`);
|
|
10178
11125
|
console.error("");
|
|
10179
11126
|
process2.exit(1);
|
|
10180
11127
|
}
|
|
@@ -10184,7 +11131,7 @@ async function main() {
|
|
|
10184
11131
|
const rawCwd = process2.cwd();
|
|
10185
11132
|
const cwd = (() => {
|
|
10186
11133
|
try {
|
|
10187
|
-
return
|
|
11134
|
+
return fs44.realpathSync(rawCwd);
|
|
10188
11135
|
} catch {
|
|
10189
11136
|
return rawCwd;
|
|
10190
11137
|
}
|
|
@@ -10211,6 +11158,7 @@ async function main() {
|
|
|
10211
11158
|
const agentFlag = getFlag(argv, "--agent");
|
|
10212
11159
|
const shellFlag = getFlag(argv, "--shell");
|
|
10213
11160
|
const noTracking = hasFlag(argv, "--no-tracking");
|
|
11161
|
+
const graphOnlyFlag = hasFlag(argv, "--graph-only");
|
|
10214
11162
|
const nameFlag = getFlag(argv, "--name");
|
|
10215
11163
|
const taglineFlag = getFlag(argv, "--tagline");
|
|
10216
11164
|
const orgIdFlag = getFlag(argv, "--org");
|
|
@@ -10295,6 +11243,18 @@ async function main() {
|
|
|
10295
11243
|
});
|
|
10296
11244
|
break;
|
|
10297
11245
|
}
|
|
11246
|
+
case "orchestration":
|
|
11247
|
+
case "orch": {
|
|
11248
|
+
const sub = argv.shift();
|
|
11249
|
+
await runOrchestration(cwd, sub, argv, {
|
|
11250
|
+
yes,
|
|
11251
|
+
harness,
|
|
11252
|
+
orgId: orgIdFlag,
|
|
11253
|
+
teamId: teamIdFlag,
|
|
11254
|
+
graphOnly: graphOnlyFlag
|
|
11255
|
+
});
|
|
11256
|
+
break;
|
|
11257
|
+
}
|
|
10298
11258
|
case "publish": {
|
|
10299
11259
|
await runPublish(cwd, { yes });
|
|
10300
11260
|
break;
|
|
@@ -10310,7 +11270,7 @@ async function main() {
|
|
|
10310
11270
|
process2.exit(1);
|
|
10311
11271
|
}
|
|
10312
11272
|
} catch (err) {
|
|
10313
|
-
console.error(
|
|
11273
|
+
console.error(pc36.red(`
|
|
10314
11274
|
${err.message}`));
|
|
10315
11275
|
if (process2.env.BRAINBASE_DEBUG)
|
|
10316
11276
|
console.error(err.stack);
|