@brainbase-labs/cli 0.29.0 → 0.30.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +128 -49
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -36018,7 +36018,7 @@ function padStart(s, n) {
36018
36018
  // package.json
36019
36019
  var package_default = {
36020
36020
  name: "@brainbase-labs/cli",
36021
- version: "0.29.0",
36021
+ version: "0.30.0",
36022
36022
  description: "Pack, share, and install agent templates across harnesses (Claude Code, Codex, ...).",
36023
36023
  type: "module",
36024
36024
  bin: {
@@ -40969,7 +40969,7 @@ function acquireLock(lockFile, timeoutMs) {
40969
40969
  function acquireAuthLock() {
40970
40970
  const release = acquireLock(AUTH_LOCK_FILE, AUTH_LOCK_TIMEOUT_MS);
40971
40971
  if (!release) {
40972
- throw new Error(`Timed out waiting to update CLI authentication; if no other brainbase process is running, remove ${AUTH_LOCK_FILE}`);
40972
+ throw new Error(`Timed out waiting to update CLI authentication.${lockHolderState(AUTH_LOCK_FILE)} Retry, or remove ${AUTH_LOCK_FILE}`);
40973
40973
  }
40974
40974
  return release;
40975
40975
  }
@@ -41021,10 +41021,44 @@ class AuthSessionChangedError extends Error {
41021
41021
  this.name = "AuthSessionChangedError";
41022
41022
  }
41023
41023
  }
41024
+ function heldFor(lockFile) {
41025
+ try {
41026
+ const ms = Date.now() - fs6.statSync(lockFile).mtimeMs;
41027
+ if (!Number.isFinite(ms) || ms < 0)
41028
+ return "";
41029
+ if (ms < 60000)
41030
+ return ` Held for ${Math.round(ms / 1000)}s.`;
41031
+ if (ms < 3600000)
41032
+ return ` Held for ${Math.round(ms / 60000)}m.`;
41033
+ return ` Held for ${Math.round(ms / 3600000)}h.`;
41034
+ } catch {
41035
+ return "";
41036
+ }
41037
+ }
41038
+ function lockHolderState(lockFile) {
41039
+ let pid;
41040
+ try {
41041
+ pid = Number.parseInt(fs6.readFileSync(lockFile, "utf8").split(":")[0] ?? "", 10);
41042
+ } catch {
41043
+ return "";
41044
+ }
41045
+ if (!Number.isInteger(pid) || pid <= 0)
41046
+ return "";
41047
+ const age = heldFor(lockFile);
41048
+ try {
41049
+ process.kill(pid, 0);
41050
+ return ` A process with pid ${pid} is running, so this may clear on its own — but pids get reused, so if that is not a brainbase command the lock is stale.${age}`;
41051
+ } catch (error) {
41052
+ if (error.code === "EPERM") {
41053
+ return ` A process with pid ${pid} is running, so this may clear on its own — but pids get reused, so if that is not a brainbase command the lock is stale.${age}`;
41054
+ }
41055
+ return ` The process that held it (pid ${pid}) is gone, so this lock is stale and will not clear itself.${age}`;
41056
+ }
41057
+ }
41024
41058
 
41025
41059
  class AuthRefreshLockTimeoutError extends Error {
41026
41060
  constructor() {
41027
- super(`Timed out waiting to refresh CLI authentication; retry, or if no other brainbase process is running, remove ${REFRESH_LOCK_FILE}`);
41061
+ super(`Timed out waiting to refresh CLI authentication.${lockHolderState(REFRESH_LOCK_FILE)} Retry, or remove ${REFRESH_LOCK_FILE}`);
41028
41062
  this.name = "AuthRefreshLockTimeoutError";
41029
41063
  }
41030
41064
  }
@@ -53890,7 +53924,7 @@ var StoredTokenSchema = exports_external.object({
53890
53924
  function withTokenLock(operation) {
53891
53925
  const release = acquireLock(TOKEN_LOCK_FILE, TOKEN_LOCK_TIMEOUT_MS);
53892
53926
  if (!release) {
53893
- throw new Error(`Timed out waiting to update the local CLI token; if no other brainbase process is running, remove ${TOKEN_LOCK_FILE}`);
53927
+ throw new Error(`Timed out waiting to update the local CLI token.${lockHolderState(TOKEN_LOCK_FILE)} Retry, or remove ${TOKEN_LOCK_FILE}`);
53894
53928
  }
53895
53929
  try {
53896
53930
  return operation();
@@ -62374,14 +62408,16 @@ var McpEntrySchema = exports_external.object({
62374
62408
  args: exports_external.array(exports_external.string()).optional(),
62375
62409
  env: exports_external.record(exports_external.string()).optional(),
62376
62410
  headers: exports_external.record(exports_external.string()).optional(),
62377
- is_enabled: exports_external.boolean().optional()
62411
+ is_enabled: exports_external.boolean().optional(),
62412
+ runtime_meta: exports_external.record(exports_external.unknown()).optional()
62378
62413
  });
62379
62414
  var CapabilitiesSchema = exports_external.object({
62380
62415
  memory: exports_external.boolean().optional(),
62381
62416
  browser: exports_external.boolean().optional(),
62382
62417
  slack: exports_external.boolean().optional(),
62383
62418
  meeting: exports_external.boolean().optional(),
62384
- github: exports_external.boolean().optional()
62419
+ github: exports_external.boolean().optional(),
62420
+ linear: exports_external.boolean().optional()
62385
62421
  });
62386
62422
  var EvalOutputShape = exports_external.enum(["binary", "rating", "classification"]);
62387
62423
  var EVAL_SLUG_RE = /^[a-z0-9][a-z0-9-]*$/;
@@ -63547,6 +63583,39 @@ function buildGithubMcpComponent(scope) {
63547
63583
  };
63548
63584
  }
63549
63585
 
63586
+ // src/core/linear-mcp.ts
63587
+ var DEFAULT_LINEAR_MCP_BASE = "https://brainbase-linear-mcp.onrender.com";
63588
+ var LINEAR_MCP_SLUG = "brainbase-linear";
63589
+ function linearMcpBaseUrl() {
63590
+ const envOverride = process.env.BRAINBASE_LINEAR_MCP_URL;
63591
+ if (envOverride)
63592
+ return envOverride.replace(/\/+$/, "");
63593
+ return DEFAULT_LINEAR_MCP_BASE;
63594
+ }
63595
+ function linearMcpUrl() {
63596
+ return `${linearMcpBaseUrl()}/t/\${BRAINBASE_THREAD_ID}/mcp`;
63597
+ }
63598
+ function linearMcpPayload() {
63599
+ return {
63600
+ url: linearMcpUrl(),
63601
+ headers: {
63602
+ Authorization: "Bearer ${BRAINBASE_TOKEN}"
63603
+ }
63604
+ };
63605
+ }
63606
+ function buildLinearMcpComponent(scope) {
63607
+ return {
63608
+ type: "mcp",
63609
+ slug: LINEAR_MCP_SLUG,
63610
+ scope,
63611
+ rootDir: "",
63612
+ description: "This agent's Linear connector (read and update issues, comment, and " + "report progress on delegated work). Scoped to this agent only.",
63613
+ meta: { mcp: linearMcpPayload() },
63614
+ payload: linearMcpPayload(),
63615
+ checksum: "builtin:brainbase-linear:v1"
63616
+ };
63617
+ }
63618
+
63550
63619
  // src/core/browser-mcp.ts
63551
63620
  var DEFAULT_BROWSER_MCP_BASE = "https://brainbase-browser-mcp.onrender.com";
63552
63621
  var BROWSER_MCP_SLUG = "brainbase-browser";
@@ -63587,7 +63656,8 @@ function capabilitiesFromAgent(agent) {
63587
63656
  browser: agent.browser_enabled !== false,
63588
63657
  slack: agent.slack_connected === true,
63589
63658
  meeting: agent.meeting_connected === true,
63590
- github: agent.github_connected === true
63659
+ github: agent.github_connected === true,
63660
+ linear: agent.linear_connected === true
63591
63661
  };
63592
63662
  }
63593
63663
  function capabilitiesFromManifest(manifest) {
@@ -63597,7 +63667,8 @@ function capabilitiesFromManifest(manifest) {
63597
63667
  browser: c2.browser !== false,
63598
63668
  slack: c2.slack === true,
63599
63669
  meeting: c2.meeting === true,
63600
- github: c2.github === true
63670
+ github: c2.github === true,
63671
+ linear: c2.linear === true
63601
63672
  };
63602
63673
  }
63603
63674
  function resolveBuiltinMcps(input) {
@@ -63608,7 +63679,8 @@ function resolveBuiltinMcps(input) {
63608
63679
  { slug: BROWSER_MCP_SLUG, enabled: caps.browser, build: buildBrowserMcpComponent },
63609
63680
  { slug: SLACK_MCP_SLUG, enabled: caps.slack, build: buildSlackMcpComponent },
63610
63681
  { slug: MEETING_MCP_SLUG, enabled: caps.meeting, build: buildMeetingMcpComponent },
63611
- { slug: GITHUB_MCP_SLUG, enabled: caps.github, build: buildGithubMcpComponent }
63682
+ { slug: GITHUB_MCP_SLUG, enabled: caps.github, build: buildGithubMcpComponent },
63683
+ { slug: LINEAR_MCP_SLUG, enabled: caps.linear, build: buildLinearMcpComponent }
63612
63684
  ];
63613
63685
  const install = [];
63614
63686
  const removeSlugs = [];
@@ -64374,7 +64446,7 @@ function diffAgentMeta(manifestMeta, lockMeta, cloudMeta) {
64374
64446
  };
64375
64447
  }
64376
64448
  var WRITABLE_CAPABILITIES = ["memory", "browser"];
64377
- var MIRRORED_CAPABILITIES = ["slack", "meeting", "github"];
64449
+ var MIRRORED_CAPABILITIES = ["slack", "meeting", "github", "linear"];
64378
64450
  function manifestConfigState(manifest) {
64379
64451
  return {
64380
64452
  ...manifest.machine_kind !== undefined ? { machine_kind: manifest.machine_kind } : {},
@@ -64398,7 +64470,8 @@ function staleConnectionMirrors(manifest, cloud) {
64398
64470
  const actualByName = {
64399
64471
  slack: cloud.slack_connected,
64400
64472
  meeting: cloud.meeting_connected,
64401
- github: cloud.github_connected
64473
+ github: cloud.github_connected,
64474
+ linear: cloud.linear_connected
64402
64475
  };
64403
64476
  const stale = [];
64404
64477
  for (const name of MIRRORED_CAPABILITIES) {
@@ -64561,6 +64634,41 @@ function capabilityBaselineAfterPush(cloudAgent, previous, diff2) {
64561
64634
  return out;
64562
64635
  }
64563
64636
 
64637
+ // src/core/mcp-merge.ts
64638
+ var RUNTIME_META_PREFIX = "runtime_";
64639
+ function runtimeMetaFromComponent(component) {
64640
+ const meta = component.meta ?? {};
64641
+ const carried = {};
64642
+ for (const [key2, value] of Object.entries(meta)) {
64643
+ if (key2.startsWith(RUNTIME_META_PREFIX) && value !== undefined) {
64644
+ carried[key2] = value;
64645
+ }
64646
+ }
64647
+ return Object.keys(carried).length > 0 ? carried : undefined;
64648
+ }
64649
+ function mcpEntriesFromCloudComponents(components) {
64650
+ return components.filter((c2) => c2.type === "mcp").map((c2) => {
64651
+ const payload = (c2.meta ?? {}).mcp ?? {};
64652
+ const entry = { name: c2.slug };
64653
+ if (typeof payload.url === "string")
64654
+ entry.url = payload.url;
64655
+ if (typeof payload.command === "string")
64656
+ entry.command = payload.command;
64657
+ if (Array.isArray(payload.args))
64658
+ entry.args = payload.args.map(String);
64659
+ if (payload.env && typeof payload.env === "object")
64660
+ entry.env = payload.env;
64661
+ if (payload.headers && typeof payload.headers === "object")
64662
+ entry.headers = payload.headers;
64663
+ if (typeof payload.is_enabled === "boolean")
64664
+ entry.is_enabled = payload.is_enabled;
64665
+ const runtimeMeta = runtimeMetaFromComponent(c2);
64666
+ if (runtimeMeta)
64667
+ entry.runtime_meta = runtimeMeta;
64668
+ return entry;
64669
+ });
64670
+ }
64671
+
64564
64672
  // src/core/secrets-env.ts
64565
64673
  import path78 from "node:path";
64566
64674
  import fs71 from "node:fs";
@@ -65411,23 +65519,7 @@ function mergeManifest(cwd2, prev, cloud, cloudAgent, harness, localOnly = {}, s
65411
65519
  content
65412
65520
  };
65413
65521
  });
65414
- const mcp = cloud.components.filter((c2) => c2.type === "mcp").map((c2) => {
65415
- const payload = (c2.meta ?? {}).mcp ?? {};
65416
- const entry = { name: c2.slug };
65417
- if (typeof payload.url === "string")
65418
- entry.url = payload.url;
65419
- if (typeof payload.command === "string")
65420
- entry.command = payload.command;
65421
- if (Array.isArray(payload.args))
65422
- entry.args = payload.args.map(String);
65423
- if (payload.env && typeof payload.env === "object")
65424
- entry.env = payload.env;
65425
- if (payload.headers && typeof payload.headers === "object")
65426
- entry.headers = payload.headers;
65427
- if (typeof payload.is_enabled === "boolean")
65428
- entry.is_enabled = payload.is_enabled;
65429
- return entry;
65430
- });
65522
+ const mcp = mcpEntriesFromCloudComponents(cloud.components);
65431
65523
  const evals = evalsFromCloudComponents(cloud.components);
65432
65524
  const caps = capabilitiesFromAgent(cloudAgent);
65433
65525
  const machineKind = cloudAgent.machine_kind ?? prev?.machine_kind;
@@ -65462,7 +65554,8 @@ function mergeManifest(cwd2, prev, cloud, cloudAgent, harness, localOnly = {}, s
65462
65554
  browser: caps.browser,
65463
65555
  slack: caps.slack,
65464
65556
  meeting: caps.meeting,
65465
- github: caps.github
65557
+ github: caps.github,
65558
+ linear: caps.linear
65466
65559
  }
65467
65560
  };
65468
65561
  }
@@ -65847,7 +65940,7 @@ async function buildOutgoingComponents(cwd2, manifest, cloud, resolvedVersions)
65847
65940
  slug: entry.name,
65848
65941
  hash: "",
65849
65942
  files: [],
65850
- meta: { mcp: payload }
65943
+ meta: { ...entry.runtime_meta ?? {}, mcp: payload }
65851
65944
  });
65852
65945
  }
65853
65946
  return out;
@@ -69010,23 +69103,7 @@ function buildManifestFromCloud(cloud, agent, localOnly = {}) {
69010
69103
  return { source: `registry:${c2.slug}` };
69011
69104
  });
69012
69105
  const hasInstructions = cloud.components.some((c2) => c2.type === "instruction" && c2.files[0]?.content?.trim());
69013
- const mcp = cloud.components.filter((c2) => c2.type === "mcp").map((c2) => {
69014
- const payload = (c2.meta ?? {}).mcp ?? {};
69015
- const entry = { name: c2.slug };
69016
- if (typeof payload.url === "string")
69017
- entry.url = payload.url;
69018
- if (typeof payload.command === "string")
69019
- entry.command = payload.command;
69020
- if (Array.isArray(payload.args))
69021
- entry.args = payload.args.map(String);
69022
- if (payload.env && typeof payload.env === "object")
69023
- entry.env = payload.env;
69024
- if (payload.headers && typeof payload.headers === "object")
69025
- entry.headers = payload.headers;
69026
- if (typeof payload.is_enabled === "boolean")
69027
- entry.is_enabled = payload.is_enabled;
69028
- return entry;
69029
- });
69106
+ const mcp = mcpEntriesFromCloudComponents(cloud.components);
69030
69107
  const playbooks = cloud.components.filter((c2) => c2.type === "playbook").map((c2) => {
69031
69108
  const raw = c2.files[0]?.content ?? "";
69032
69109
  const { frontmatter } = stripPlaybookFrontmatter(raw);
@@ -69066,7 +69143,8 @@ function buildManifestFromCloud(cloud, agent, localOnly = {}) {
69066
69143
  browser: caps.browser,
69067
69144
  slack: caps.slack,
69068
69145
  meeting: caps.meeting,
69069
- github: caps.github
69146
+ github: caps.github,
69147
+ linear: caps.linear
69070
69148
  }
69071
69149
  };
69072
69150
  }
@@ -89014,6 +89092,7 @@ function help() {
89014
89092
  out.push(` ${import_picocolors61.default.dim("BRAINBASE_SLACK_MCP_URL")} override the built-in Slack MCP host`);
89015
89093
  out.push(` ${import_picocolors61.default.dim("BRAINBASE_MEETING_MCP_URL")} override the built-in meeting MCP host`);
89016
89094
  out.push(` ${import_picocolors61.default.dim("BRAINBASE_GITHUB_MCP_URL")} override the built-in GitHub MCP host`);
89095
+ out.push(` ${import_picocolors61.default.dim("BRAINBASE_LINEAR_MCP_URL")} override the built-in Linear MCP host`);
89017
89096
  out.push(` ${import_picocolors61.default.dim("BRAINBASE_ORCHESTRATION_MCP_URL")} override the built-in orchestration MCP host`);
89018
89097
  out.push("");
89019
89098
  out.push(divider("HARNESSES"));
@@ -89253,8 +89332,8 @@ async function main() {
89253
89332
  const limitFlag = Number.isInteger(limitParsed) && limitParsed >= 1 && limitParsed <= 200 ? limitParsed : undefined;
89254
89333
  const archivedFlag = hasFlag2(sharedArgs, "--archived");
89255
89334
  ensureSkillResolversRegistered();
89256
- await requireAuth(cmd);
89257
89335
  try {
89336
+ await requireAuth(cmd);
89258
89337
  switch (cmd) {
89259
89338
  case "login": {
89260
89339
  await runLogin(cwd2, { web });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brainbase-labs/cli",
3
- "version": "0.29.0",
3
+ "version": "0.30.0",
4
4
  "description": "Pack, share, and install agent templates across harnesses (Claude Code, Codex, ...).",
5
5
  "type": "module",
6
6
  "bin": {