@brainbase-labs/cli 0.32.0 → 0.34.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 +617 -504
  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.32.0",
36021
+ version: "0.34.0",
36022
36022
  description: "Pack, share, and install agent templates across harnesses (Claude Code, Codex, ...).",
36023
36023
  type: "module",
36024
36024
  bin: {
@@ -41663,6 +41663,12 @@ ${block}
41663
41663
  var DEFAULT_MCP_PROXY_BASE = "https://brainbase-mcp-proxy.onrender.com";
41664
41664
  var THREAD_ID_TEMPLATE = "${BRAINBASE_THREAD_ID}";
41665
41665
  var PROXY_AUTH_HEADER = "Bearer ${BRAINBASE_TOKEN}";
41666
+ function isRuntimeMcpAuth(value) {
41667
+ if (!value || typeof value !== "object")
41668
+ return false;
41669
+ const auth = value;
41670
+ return auth.version === 1 && auth.requires_oauth === true && (auth.kind === "agent" || auth.kind === "orchestration") && typeof auth.server_id === "string" && /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(auth.server_id);
41671
+ }
41666
41672
  function mcpProxyBaseUrl(env = process.env) {
41667
41673
  const envOverride = env.BRAINBASE_MCP_PROXY_URL;
41668
41674
  if (envOverride) {
@@ -41675,19 +41681,31 @@ function mcpProxyBaseUrl(env = process.env) {
41675
41681
  function isBrainbaseTaskRuntime(env = process.env) {
41676
41682
  return typeof env.BRAINBASE_THREAD_ID === "string" && env.BRAINBASE_THREAD_ID.trim().length > 0 && typeof env.BRAINBASE_TOKEN === "string" && env.BRAINBASE_TOKEN.trim().length > 0;
41677
41683
  }
41678
- function proxifyMcpPayload(payload, env = process.env) {
41684
+ function proxifyMcpPayload(payload, env = process.env, identity) {
41679
41685
  if (!payload)
41680
41686
  return payload;
41681
41687
  if (!isBrainbaseTaskRuntime(env))
41682
41688
  return payload;
41683
- if (!shouldProxy(payload, env))
41689
+ if (typeof payload.url !== "string" || !payload.url || payload.command)
41690
+ return payload;
41691
+ let route = "";
41692
+ let keepInstalledUrl = false;
41693
+ let upstreamUrl = payload.url;
41694
+ if (identity !== undefined) {
41695
+ if (!isRuntimeMcpAuth(identity))
41696
+ throw new Error("mcp_server_identity_required: incompatible runtime_mcp_auth");
41697
+ route = `s/${identity.kind}/${identity.server_id}/`;
41698
+ const installed = parseMcpProxyUrl(payload.url, env.BRAINBASE_THREAD_ID?.trim() ?? "", mcpProxyBaseUrl(env));
41699
+ keepInstalledUrl = installed?.kind === identity.kind && installed.serverId === identity.server_id;
41700
+ if (installed)
41701
+ upstreamUrl = installed.upstreamUrl;
41702
+ } else if (!shouldProxy(payload, env))
41684
41703
  return payload;
41685
41704
  const base = mcpProxyBaseUrl(env);
41686
- const upstreamUrl = payload.url;
41687
- const existingHeaders = payload.headers ?? {};
41705
+ const existingHeaders = Object.fromEntries(Object.entries(payload.headers ?? {}).filter(([key]) => key.toLowerCase() !== "authorization"));
41688
41706
  return {
41689
41707
  ...payload,
41690
- url: `${base}/t/${THREAD_ID_TEMPLATE}/${upstreamUrl}`,
41708
+ url: keepInstalledUrl ? payload.url : `${base}/t/${THREAD_ID_TEMPLATE}/${route}${upstreamUrl}`,
41691
41709
  headers: { ...existingHeaders, Authorization: PROXY_AUTH_HEADER }
41692
41710
  };
41693
41711
  }
@@ -41701,46 +41719,57 @@ function shouldProxy(payload, env) {
41701
41719
  if (headers && typeof headers === "object" && Object.keys(headers).length > 0) {
41702
41720
  return false;
41703
41721
  }
41704
- if (isMcpProxyUrl(url, env.BRAINBASE_THREAD_ID?.trim() ?? "", mcpProxyBaseUrl(env))) {
41722
+ if (parseMcpProxyUrl(url, env.BRAINBASE_THREAD_ID?.trim() ?? "", mcpProxyBaseUrl(env))) {
41705
41723
  return false;
41706
41724
  }
41707
41725
  return true;
41708
41726
  }
41709
- function isMcpProxyUrl(value, threadId, proxyBase) {
41727
+ function parseMcpProxyUrl(value, threadId, proxyBase) {
41710
41728
  let outer;
41711
41729
  try {
41712
41730
  outer = new URL(value);
41713
41731
  } catch {
41714
- return false;
41732
+ return;
41715
41733
  }
41716
41734
  if (outer.protocol !== "http:" && outer.protocol !== "https:")
41717
- return false;
41718
- const candidatePaths = [outer.pathname];
41719
- try {
41720
- const base = new URL(proxyBase);
41721
- const basePath = base.pathname.replace(/\/+$/, "");
41722
- if (basePath && outer.origin === base.origin && outer.pathname.startsWith(`${basePath}/`)) {
41723
- candidatePaths.push(outer.pathname.slice(basePath.length));
41724
- }
41725
- } catch {}
41726
- const match = candidatePaths.map((path13) => /^\/t\/([^/]+)\/(https?:\/\/.+)$/.exec(path13)).find((candidate) => candidate !== null);
41735
+ return;
41736
+ const candidatePaths = [];
41737
+ for (const trusted of [proxyBase, DEFAULT_MCP_PROXY_BASE]) {
41738
+ try {
41739
+ const base = new URL(trusted);
41740
+ if (outer.origin !== base.origin || outer.username || outer.password)
41741
+ continue;
41742
+ const basePath = base.pathname.replace(/\/+$/, "");
41743
+ if (outer.pathname.startsWith(`${basePath}/`)) {
41744
+ candidatePaths.push(outer.pathname.slice(basePath.length));
41745
+ }
41746
+ } catch {}
41747
+ }
41748
+ const match = candidatePaths.map((path13) => /^\/t\/([^/]+)\/(?:s\/(agent|orchestration)\/([0-9a-f-]+)\/)?(https?:\/\/.+)$/i.exec(path13)).find((candidate) => candidate !== null);
41727
41749
  if (!match)
41728
- return false;
41750
+ return;
41729
41751
  let pathThreadId = match[1];
41730
41752
  try {
41731
41753
  pathThreadId = decodeURIComponent(pathThreadId);
41732
41754
  } catch {
41733
- return false;
41755
+ return;
41734
41756
  }
41735
41757
  if (pathThreadId !== threadId && pathThreadId !== THREAD_ID_TEMPLATE)
41736
- return false;
41758
+ return;
41737
41759
  try {
41738
- const upstream = new URL(match[2]);
41739
- return (upstream.protocol === "http:" || upstream.protocol === "https:") && upstream.hostname.length > 0;
41760
+ const upstreamUrl = match[4] + outer.search + outer.hash;
41761
+ const upstream = new URL(upstreamUrl);
41762
+ if (upstream.protocol !== "http:" && upstream.protocol !== "https:" || !upstream.hostname)
41763
+ return;
41764
+ return { upstreamUrl, kind: match[2], serverId: match[3] };
41740
41765
  } catch {
41741
- return false;
41766
+ return;
41742
41767
  }
41743
41768
  }
41769
+ function mcpUpstreamUrl(value, env = process.env) {
41770
+ const resolved = resolveMcpEnvTemplates({ url: value }, env)?.url;
41771
+ return parseMcpProxyUrl(resolved, env.BRAINBASE_THREAD_ID?.trim() ?? "", mcpProxyBaseUrl(env))?.upstreamUrl ?? resolved;
41772
+ }
41744
41773
  function resolveMcpEnvTemplates(payload, env = process.env) {
41745
41774
  if (!payload)
41746
41775
  return payload;
@@ -44176,8 +44205,8 @@ function normalizeMcpPayload2(payload) {
44176
44205
  out.type = "stdio";
44177
44206
  }
44178
44207
  if (out.type === "http" || out.type === "sse") {
44179
- if (Array.isArray(out.args) && out.args.length === 0)
44180
- delete out.args;
44208
+ delete out.command;
44209
+ delete out.args;
44181
44210
  if (out.env && typeof out.env === "object" && Object.keys(out.env).length === 0) {
44182
44211
  delete out.env;
44183
44212
  }
@@ -53941,6 +53970,7 @@ var InstalledComponentSchema = exports_external.object({
53941
53970
  slug: exports_external.string(),
53942
53971
  installedPaths: exports_external.array(exports_external.string()),
53943
53972
  sourceChecksum: exports_external.string(),
53973
+ runtime_mcp_auth: exports_external.unknown().optional(),
53944
53974
  scope: ScopeSchema.optional(),
53945
53975
  embeddedKey: exports_external.string().optional(),
53946
53976
  settingsFile: exports_external.string().optional()
@@ -59276,7 +59306,7 @@ function buildTemplateComponents(manifest, rootDir) {
59276
59306
  rootDir: path63.join(rootDir, c2.path),
59277
59307
  description: c2.description,
59278
59308
  target: c2.target,
59279
- payload: proxifyMcpPayload(c2.meta?.mcp),
59309
+ payload: proxifyMcpPayload(c2.meta?.mcp, undefined, c2.meta?.runtime_mcp_auth),
59280
59310
  meta: c2.meta,
59281
59311
  checksum: c2.checksum,
59282
59312
  source: c2.source
@@ -59446,6 +59476,7 @@ async function runOnboard(cwd2, args) {
59446
59476
  slug: o2.slug,
59447
59477
  installedPaths: o2.installedPaths,
59448
59478
  sourceChecksum: o2.sourceChecksum,
59479
+ ...o2.type === "mcp" ? { runtime_mcp_auth: components.find((c2) => c2.type === o2.type && c2.slug === o2.slug)?.meta?.runtime_mcp_auth } : {},
59449
59480
  scope: o2.scope,
59450
59481
  embeddedKey: o2.embeddedKey,
59451
59482
  settingsFile: o2.settingsFile
@@ -63542,10 +63573,462 @@ async function runUnlink(cwd2, args) {
63542
63573
  }
63543
63574
 
63544
63575
  // src/cli/sync.ts
63576
+ import { isDeepStrictEqual } from "node:util";
63577
+
63578
+ // src/core/agent-diff.ts
63545
63579
  import path77 from "node:path";
63546
63580
  import fs69 from "node:fs";
63547
- import os12 from "node:os";
63548
63581
  import crypto3 from "node:crypto";
63582
+ function compKey(type, slug) {
63583
+ return `${type}/${slug}`;
63584
+ }
63585
+ function hashString(s3) {
63586
+ return crypto3.createHash("sha256").update(s3).digest("hex");
63587
+ }
63588
+ function jsonStringAsciiSafe(s3) {
63589
+ return JSON.stringify(s3).replace(/[\u007f-\uffff]/g, (c2) => "\\u" + c2.charCodeAt(0).toString(16).padStart(4, "0"));
63590
+ }
63591
+ function canonicalJson2(value) {
63592
+ if (value === null)
63593
+ return "null";
63594
+ if (typeof value === "boolean")
63595
+ return value ? "true" : "false";
63596
+ if (typeof value === "number") {
63597
+ if (!Number.isFinite(value))
63598
+ throw new Error("Non-finite numbers are not JSON");
63599
+ return String(value);
63600
+ }
63601
+ if (typeof value === "string")
63602
+ return jsonStringAsciiSafe(value);
63603
+ if (Array.isArray(value)) {
63604
+ return "[" + value.map(canonicalJson2).join(",") + "]";
63605
+ }
63606
+ if (typeof value === "object") {
63607
+ const obj = value;
63608
+ const keys2 = Object.keys(obj).sort();
63609
+ return "{" + keys2.map((k3) => `${jsonStringAsciiSafe(k3)}:${canonicalJson2(obj[k3])}`).join(",") + "}";
63610
+ }
63611
+ throw new Error(`Unsupported value in canonicalJson: ${typeof value}`);
63612
+ }
63613
+ function hashMcpEntry(entry) {
63614
+ const payload = {
63615
+ args: entry.args ?? [],
63616
+ env: entry.env ?? {},
63617
+ headers: entry.headers ?? {}
63618
+ };
63619
+ if (entry.url !== undefined)
63620
+ payload.url = entry.url;
63621
+ if (entry.command !== undefined)
63622
+ payload.command = entry.command;
63623
+ payload.is_enabled = entry.is_enabled ?? true;
63624
+ return crypto3.createHash("sha256").update(canonicalJson2(payload)).digest("hex");
63625
+ }
63626
+ function evalWirePayload(entry) {
63627
+ return {
63628
+ criteria: entry.criteria,
63629
+ enabled: entry.enabled,
63630
+ icon: entry.icon || null,
63631
+ judge_agent: entry.judge_agent ?? null,
63632
+ judge_model: entry.judge_model,
63633
+ judge_type: entry.judge_type,
63634
+ output_shape: entry.output_shape,
63635
+ classification_values: entry.classification_values && entry.classification_values.length > 0 ? entry.classification_values : null
63636
+ };
63637
+ }
63638
+ function hashEvalEntry(entry) {
63639
+ return crypto3.createHash("sha256").update(canonicalJson2(evalWirePayload(entry))).digest("hex");
63640
+ }
63641
+ function fileHash(p2) {
63642
+ if (!exists(p2))
63643
+ return null;
63644
+ try {
63645
+ const buf = fs69.readFileSync(p2);
63646
+ return crypto3.createHash("sha256").update(buf).digest("hex");
63647
+ } catch {
63648
+ return null;
63649
+ }
63650
+ }
63651
+ function componentHashFromFileHashes(fileHashes) {
63652
+ const h2 = crypto3.createHash("sha256");
63653
+ for (const fh of [...fileHashes].sort()) {
63654
+ h2.update(fh);
63655
+ h2.update(`
63656
+ `);
63657
+ }
63658
+ return h2.digest("hex");
63659
+ }
63660
+ function hashDirectoryAsComponent(dir) {
63661
+ if (!exists(dir))
63662
+ return null;
63663
+ const fileHashes = [];
63664
+ const walk = (sub) => {
63665
+ let entries;
63666
+ try {
63667
+ entries = fs69.readdirSync(sub, { withFileTypes: true });
63668
+ } catch {
63669
+ return;
63670
+ }
63671
+ for (const e2 of entries) {
63672
+ const full = path77.join(sub, e2.name);
63673
+ if (e2.isFile()) {
63674
+ const fh = fileHash(full);
63675
+ if (fh)
63676
+ fileHashes.push(fh);
63677
+ } else if (e2.isDirectory()) {
63678
+ walk(full);
63679
+ }
63680
+ }
63681
+ };
63682
+ walk(dir);
63683
+ return componentHashFromFileHashes(fileHashes);
63684
+ }
63685
+ function readLocalComponents(cwd2, manifest) {
63686
+ const out = [];
63687
+ const instr = readInstructions(cwd2, manifest);
63688
+ if (instr !== null && instr.trim().length > 0) {
63689
+ out.push({
63690
+ type: "instruction",
63691
+ slug: "agent-instructions",
63692
+ hash: componentHashFromFileHashes([hashString(normalizeInstructionBody(instr))])
63693
+ });
63694
+ }
63695
+ for (const entry of manifest.skills) {
63696
+ let parsed;
63697
+ try {
63698
+ parsed = parseSkillSource2(entry.source);
63699
+ } catch {
63700
+ try {
63701
+ const core = parseSkillSource(entry.source);
63702
+ if (core.type !== "inline" && core.type !== "local") {
63703
+ out.push({
63704
+ type: "skill",
63705
+ slug: defaultSkillSlug(core),
63706
+ hash: null,
63707
+ declaredSource: entry.source
63708
+ });
63709
+ }
63710
+ } catch {}
63711
+ continue;
63712
+ }
63713
+ if (parsed.kind === "registry") {
63714
+ out.push({
63715
+ type: "skill",
63716
+ slug: registrySkillComponentSlug(parsed),
63717
+ hash: null,
63718
+ declaredSource: entry.source
63719
+ });
63720
+ } else {
63721
+ const abs = path77.resolve(cwd2, parsed.path);
63722
+ const slug = path77.basename(abs);
63723
+ out.push({
63724
+ type: "skill",
63725
+ slug,
63726
+ hash: hashDirectoryAsComponent(abs),
63727
+ declaredSource: entry.source,
63728
+ localPath: abs
63729
+ });
63730
+ }
63731
+ }
63732
+ for (const entry of manifest.mcp ?? []) {
63733
+ out.push({
63734
+ type: "mcp",
63735
+ slug: entry.name,
63736
+ hash: hashMcpEntry(entry)
63737
+ });
63738
+ }
63739
+ const pbEntries = manifest.playbooks ?? [];
63740
+ const pbSlugs = assignPlaybookSlugs(pbEntries.map((e2) => e2.title));
63741
+ for (let i = 0;i < pbEntries.length; i++) {
63742
+ const entry = pbEntries[i];
63743
+ const slug = pbSlugs[i];
63744
+ const body = resolvePlaybookContent(cwd2, entry);
63745
+ if (body === null) {
63746
+ out.push({ type: "playbook", slug, hash: null });
63747
+ continue;
63748
+ }
63749
+ const wireBody = assemblePlaybookWireBody(entry, body);
63750
+ out.push({
63751
+ type: "playbook",
63752
+ slug,
63753
+ hash: componentHashFromFileHashes([hashString(wireBody)])
63754
+ });
63755
+ }
63756
+ for (const entry of manifest.evals ?? []) {
63757
+ out.push({
63758
+ type: "eval",
63759
+ slug: entry.slug,
63760
+ hash: hashEvalEntry(entry)
63761
+ });
63762
+ }
63763
+ return out;
63764
+ }
63765
+ function threeWayDiff(input) {
63766
+ const lockMap = new Map;
63767
+ for (const c2 of input.lock)
63768
+ lockMap.set(compKey(c2.type, c2.slug), c2);
63769
+ const cloudMap = new Map;
63770
+ for (const c2 of input.cloud)
63771
+ cloudMap.set(compKey(c2.type, c2.slug), c2);
63772
+ const localMap = new Map;
63773
+ for (const c2 of input.local)
63774
+ localMap.set(compKey(c2.type, c2.slug), c2);
63775
+ const keys2 = new Set;
63776
+ for (const k3 of lockMap.keys())
63777
+ keys2.add(k3);
63778
+ for (const k3 of cloudMap.keys())
63779
+ keys2.add(k3);
63780
+ for (const k3 of localMap.keys())
63781
+ keys2.add(k3);
63782
+ const rows = [];
63783
+ for (const key2 of [...keys2].sort()) {
63784
+ const lock = lockMap.get(key2);
63785
+ const cloud = cloudMap.get(key2);
63786
+ const local = localMap.get(key2);
63787
+ const [type, slug] = key2.split("/", 2);
63788
+ let localHash = local?.hash ?? null;
63789
+ if (local && local.hash === null && local.declaredSource && lock?.source === local.declaredSource && cloud) {
63790
+ localHash = cloud.hash;
63791
+ }
63792
+ const cloudHash = cloud?.hash;
63793
+ const lockHash = lock?.hash;
63794
+ const declaredSource = local?.declaredSource;
63795
+ const present = {
63796
+ local: !!local,
63797
+ lock: !!lock,
63798
+ cloud: !!cloud
63799
+ };
63800
+ let status;
63801
+ if (present.local && present.cloud && !present.lock) {
63802
+ status = "added-local";
63803
+ } else if (!present.local && present.cloud && !present.lock) {
63804
+ status = "added-cloud";
63805
+ } else if (present.local && !present.cloud && !present.lock) {
63806
+ status = "added-only-local";
63807
+ } else if (!present.local && present.lock) {
63808
+ status = "removed-local";
63809
+ } else if (present.local && !present.cloud && present.lock) {
63810
+ status = "removed-cloud";
63811
+ } else {
63812
+ const sourceChanged = !!local && local.hash === null && !!local.declaredSource && lock?.source !== undefined && lock.source !== local.declaredSource;
63813
+ const localChanged = sourceChanged || localHash !== null && lockHash !== undefined && localHash !== lockHash;
63814
+ const cloudChanged = cloudHash !== undefined && lockHash !== undefined && cloudHash !== lockHash;
63815
+ const converged = !sourceChanged && localHash !== null && cloudHash !== undefined && localHash === cloudHash;
63816
+ if (converged)
63817
+ status = "in-sync";
63818
+ else if (localChanged && cloudChanged)
63819
+ status = "modified-both";
63820
+ else if (localChanged)
63821
+ status = "modified-local";
63822
+ else if (cloudChanged)
63823
+ status = "modified-cloud";
63824
+ else
63825
+ status = "in-sync";
63826
+ }
63827
+ rows.push({
63828
+ key: key2,
63829
+ type,
63830
+ slug,
63831
+ status,
63832
+ localHash: localHash ?? undefined,
63833
+ lockHash,
63834
+ cloudHash,
63835
+ declaredSource
63836
+ });
63837
+ }
63838
+ return rows;
63839
+ }
63840
+ function partitionPushRows(rows, force) {
63841
+ const toSend = [];
63842
+ const conflicts = [];
63843
+ const upstreamOnly = [];
63844
+ for (const r2 of rows) {
63845
+ switch (r2.status) {
63846
+ case "modified-local":
63847
+ case "added-only-local":
63848
+ case "added-local":
63849
+ case "removed-local":
63850
+ toSend.push(r2);
63851
+ break;
63852
+ case "modified-both":
63853
+ if (force)
63854
+ toSend.push(r2);
63855
+ else
63856
+ conflicts.push(r2);
63857
+ break;
63858
+ case "modified-cloud":
63859
+ case "added-cloud":
63860
+ case "removed-cloud":
63861
+ upstreamOnly.push(r2);
63862
+ break;
63863
+ case "in-sync":
63864
+ break;
63865
+ default: {
63866
+ const _exhaustive = r2.status;
63867
+ }
63868
+ }
63869
+ }
63870
+ return { toSend, conflicts, upstreamOnly };
63871
+ }
63872
+ function diffAgentMeta(manifestMeta, lockMeta, cloudMeta) {
63873
+ const eq = (a3, b4) => {
63874
+ if (!a3 && !b4)
63875
+ return true;
63876
+ if (!a3 || !b4)
63877
+ return false;
63878
+ return a3.name === b4.name && (a3.tagline ?? "") === (b4.tagline ?? "");
63879
+ };
63880
+ return {
63881
+ localChanged: !!manifestMeta && !!lockMeta && !eq(manifestMeta, lockMeta),
63882
+ cloudChanged: !!cloudMeta && !!lockMeta && !eq(cloudMeta, lockMeta)
63883
+ };
63884
+ }
63885
+ var WRITABLE_CAPABILITIES = ["memory", "browser"];
63886
+ var MIRRORED_CAPABILITIES = ["slack", "meeting", "github", "linear"];
63887
+ function manifestConfigState(manifest) {
63888
+ return {
63889
+ ...manifest.machine_kind !== undefined ? { machine_kind: manifest.machine_kind } : {},
63890
+ ...hasOwn(manifest, "default_model") ? { default_model: manifest.default_model ?? null } : {},
63891
+ ...manifest.capabilities?.memory !== undefined ? { memory: manifest.capabilities.memory } : {},
63892
+ ...manifest.capabilities?.browser !== undefined ? { browser: manifest.capabilities.browser } : {}
63893
+ };
63894
+ }
63895
+ function cloudConfigState(agent) {
63896
+ return {
63897
+ ...agent.machine_kind !== undefined ? { machine_kind: agent.machine_kind } : {},
63898
+ ...hasOwn(agent, "default_model") ? { default_model: agent.default_model ?? null } : {},
63899
+ ...agent.memory_enabled !== undefined ? { memory: agent.memory_enabled } : {},
63900
+ ...agent.browser_enabled !== undefined ? { browser: agent.browser_enabled } : {}
63901
+ };
63902
+ }
63903
+ function hasOwn(value, key2) {
63904
+ return !!value && Object.prototype.hasOwnProperty.call(value, key2);
63905
+ }
63906
+ function staleConnectionMirrors(manifest, cloud) {
63907
+ const actualByName = {
63908
+ slack: cloud.slack_connected,
63909
+ meeting: cloud.meeting_connected,
63910
+ github: cloud.github_connected,
63911
+ linear: cloud.linear_connected
63912
+ };
63913
+ const stale = [];
63914
+ for (const name of MIRRORED_CAPABILITIES) {
63915
+ const authored = manifest.capabilities?.[name];
63916
+ const actual = actualByName[name];
63917
+ if (authored === undefined || actual === undefined)
63918
+ continue;
63919
+ if (authored !== actual)
63920
+ stale.push({ name, authored, actual });
63921
+ }
63922
+ return stale;
63923
+ }
63924
+ function diffAgentConfig(manifest, lock, cloud) {
63925
+ const unsupported = [];
63926
+ const machineSupported = cloud.machine_kind !== undefined;
63927
+ const defaultModelSupported = hasOwn(cloud, "default_model");
63928
+ if (manifest.machine_kind !== undefined && !machineSupported) {
63929
+ unsupported.push("machine_kind");
63930
+ }
63931
+ if (manifest.default_model !== undefined && !defaultModelSupported) {
63932
+ unsupported.push("default_model");
63933
+ }
63934
+ const machineMismatch = manifest.machine_kind !== undefined && machineSupported && manifest.machine_kind !== cloud.machine_kind;
63935
+ const machineLocalChanged = manifest.machine_kind !== undefined && machineSupported && (lock?.machine_kind !== undefined ? manifest.machine_kind !== lock.machine_kind && manifest.machine_kind !== cloud.machine_kind : manifest.machine_kind !== cloud.machine_kind);
63936
+ const machineCloudChanged = lock?.machine_kind !== undefined && machineSupported && lock.machine_kind !== cloud.machine_kind && manifest.machine_kind !== cloud.machine_kind;
63937
+ const machineConverged = manifest.machine_kind !== undefined && machineSupported && manifest.machine_kind === cloud.machine_kind && (lock?.machine_kind === undefined || lock.machine_kind !== cloud.machine_kind);
63938
+ const defaultModel = threeWayField(defaultModelSupported, manifest.default_model, cloud.default_model, lock, "default_model");
63939
+ const memory = threeWayField(hasOwn(cloud, "memory"), manifest.memory, cloud.memory, lock, "memory");
63940
+ const browser = threeWayField(hasOwn(cloud, "browser"), manifest.browser, cloud.browser, lock, "browser");
63941
+ if (manifest.memory !== undefined && !hasOwn(cloud, "memory")) {
63942
+ unsupported.push("memory");
63943
+ }
63944
+ if (manifest.browser !== undefined && !hasOwn(cloud, "browser")) {
63945
+ unsupported.push("browser");
63946
+ }
63947
+ return {
63948
+ unsupported,
63949
+ machineMismatch,
63950
+ machineLocalChanged,
63951
+ machineCloudChanged,
63952
+ defaultModelLocalChanged: defaultModel.localChanged,
63953
+ defaultModelCloudChanged: defaultModel.cloudChanged,
63954
+ defaultModelConflict: defaultModel.conflict,
63955
+ memory,
63956
+ browser,
63957
+ baselineConverged: machineConverged || defaultModel.converged || memory.converged || browser.converged
63958
+ };
63959
+ }
63960
+ function threeWayField(supported, authored, cloudRaw, lock, key2) {
63961
+ const result2 = {
63962
+ localChanged: false,
63963
+ cloudChanged: false,
63964
+ conflict: false,
63965
+ converged: false
63966
+ };
63967
+ if (!supported)
63968
+ return result2;
63969
+ const cloudValue = cloudRaw ?? null;
63970
+ const lockSupported = hasOwn(lock, key2);
63971
+ const lockValue = lock?.[key2] ?? null;
63972
+ if (authored === undefined) {
63973
+ if (lockSupported)
63974
+ result2.cloudChanged = cloudValue !== lockValue;
63975
+ return result2;
63976
+ }
63977
+ const localValue = authored ?? null;
63978
+ if (!lockSupported) {
63979
+ result2.localChanged = localValue !== cloudValue;
63980
+ result2.converged = localValue === cloudValue;
63981
+ return result2;
63982
+ }
63983
+ const localMoved = localValue !== lockValue;
63984
+ const cloudMoved = cloudValue !== lockValue;
63985
+ result2.conflict = localMoved && cloudMoved && localValue !== cloudValue;
63986
+ result2.localChanged = localMoved && localValue !== cloudValue;
63987
+ result2.cloudChanged = cloudMoved && localValue !== cloudValue;
63988
+ result2.converged = localMoved && cloudMoved && localValue === cloudValue;
63989
+ return result2;
63990
+ }
63991
+
63992
+ // src/core/mcp-merge.ts
63993
+ var RUNTIME_META_PREFIX = "runtime_";
63994
+ function runtimeMetaFromComponent(component) {
63995
+ const meta = component.meta ?? {};
63996
+ const carried = {};
63997
+ for (const [key2, value] of Object.entries(meta)) {
63998
+ if (key2.startsWith(RUNTIME_META_PREFIX) && value !== undefined) {
63999
+ carried[key2] = value;
64000
+ }
64001
+ }
64002
+ return Object.keys(carried).length > 0 ? carried : undefined;
64003
+ }
64004
+ function mcpEntriesFromCloudComponents(components) {
64005
+ return components.filter((c2) => c2.type === "mcp").map((c2) => {
64006
+ const payload = (c2.meta ?? {}).mcp ?? {};
64007
+ const entry = { name: c2.slug };
64008
+ if (typeof payload.url === "string")
64009
+ entry.url = payload.url;
64010
+ if (typeof payload.command === "string")
64011
+ entry.command = payload.command;
64012
+ if (Array.isArray(payload.args))
64013
+ entry.args = payload.args.map(String);
64014
+ if (payload.env && typeof payload.env === "object")
64015
+ entry.env = payload.env;
64016
+ if (payload.headers && typeof payload.headers === "object")
64017
+ entry.headers = payload.headers;
64018
+ if (typeof payload.is_enabled === "boolean")
64019
+ entry.is_enabled = payload.is_enabled;
64020
+ const runtimeMeta = runtimeMetaFromComponent(c2);
64021
+ if (runtimeMeta)
64022
+ entry.runtime_meta = runtimeMeta;
64023
+ return entry;
64024
+ });
64025
+ }
64026
+
64027
+ // src/cli/sync.ts
64028
+ import path78 from "node:path";
64029
+ import fs70 from "node:fs";
64030
+ import os12 from "node:os";
64031
+ import crypto4 from "node:crypto";
63549
64032
  var import_picocolors24 = __toESM(require_picocolors(), 1);
63550
64033
 
63551
64034
  // src/core/memory-mcp.ts
@@ -63866,6 +64349,21 @@ async function runSync(cwd2, args) {
63866
64349
  declaredSlugs: declaredMcpSlugs,
63867
64350
  scope
63868
64351
  });
64352
+ const localManifest = readManifest(cwd2);
64353
+ for (const component of manifest.components) {
64354
+ if (component.type !== "mcp")
64355
+ continue;
64356
+ const prior = prevState?.components.find((c2) => c2.type === "mcp" && c2.slug === component.slug);
64357
+ const entry = localManifest?.mcp.find((c2) => c2.name === component.slug);
64358
+ if (!prior || prior.hash !== component.hash || isDeepStrictEqual(component.meta?.runtime_mcp_auth, entry?.runtime_meta?.runtime_mcp_auth))
64359
+ continue;
64360
+ const harness = args.harness ?? link2.harness ?? link2.tracking?.harness;
64361
+ const locallyReplaced = harness && readHarnessMcpEntry(harness, cwd2, scope, component.slug) !== undefined && !lockEntryOwnsMcpInStore(prior, { harness, cwd: cwd2, scope });
64362
+ if (entry && hashMcpEntry(entry) !== prior.hash || locallyReplaced)
64363
+ diff2.localModified.push(component);
64364
+ else
64365
+ diff2.upstreamUpdated.push(component);
64366
+ }
63869
64367
  if (diff2.added.length === 0 && diff2.upstreamUpdated.length === 0 && diff2.localModified.length === 0 && diff2.deletedUpstream.length === 0 && builtinInstall.length === 0 && builtinRemoveSlugs.length === 0) {
63870
64368
  f2.info(`${sym.same} You're up to date.`);
63871
64369
  writeSyncState(cwd2, {
@@ -63962,10 +64460,10 @@ async function runSync(cwd2, args) {
63962
64460
  type: c2.type,
63963
64461
  slug: c2.slug,
63964
64462
  scope,
63965
- rootDir: path77.join(stageRoot, c2.type, c2.slug),
64463
+ rootDir: path78.join(stageRoot, c2.type, c2.slug),
63966
64464
  description: c2.description,
63967
64465
  meta: c2.meta,
63968
- payload: proxifyMcpPayload(c2.meta?.mcp),
64466
+ payload: proxifyMcpPayload(c2.meta?.mcp, undefined, c2.meta?.runtime_mcp_auth),
63969
64467
  checksum: c2.hash
63970
64468
  }));
63971
64469
  toInstall.push(...builtinInstall);
@@ -64032,11 +64530,11 @@ async function runSync(cwd2, args) {
64032
64530
  if (!exists(filePath))
64033
64531
  continue;
64034
64532
  try {
64035
- const stat = fs69.statSync(filePath);
64533
+ const stat = fs70.statSync(filePath);
64036
64534
  if (stat.isDirectory())
64037
- fs69.rmSync(filePath, { recursive: true, force: true });
64535
+ fs70.rmSync(filePath, { recursive: true, force: true });
64038
64536
  else
64039
- fs69.rmSync(filePath);
64537
+ fs70.rmSync(filePath);
64040
64538
  } catch (err) {
64041
64539
  f2.warn(`Failed to remove ${filePath}: ${err.message}`);
64042
64540
  }
@@ -64077,10 +64575,23 @@ async function runSync(cwd2, args) {
64077
64575
  agentMeta: prevState?.agentMeta
64078
64576
  };
64079
64577
  writeSyncState(cwd2, newState);
64578
+ if (localManifest) {
64579
+ for (const component of manifest.components) {
64580
+ if (component.type !== "mcp" || !justInstalledPaths.has(`mcp/${component.slug}`))
64581
+ continue;
64582
+ const index = localManifest.mcp.findIndex((entry2) => entry2.name === component.slug);
64583
+ const entry = mcpEntriesFromCloudComponents([component])[0];
64584
+ if (index >= 0)
64585
+ localManifest.mcp[index] = entry;
64586
+ else
64587
+ localManifest.mcp.push(entry);
64588
+ }
64589
+ writeManifest(cwd2, localManifest);
64590
+ }
64080
64591
  $e(`Synced ${link2.name} to revision ${manifest.revision}.`);
64081
64592
  } finally {
64082
64593
  try {
64083
- fs69.rmSync(stageRoot, { recursive: true, force: true });
64594
+ fs70.rmSync(stageRoot, { recursive: true, force: true });
64084
64595
  } catch {}
64085
64596
  }
64086
64597
  }
@@ -64123,19 +64634,19 @@ function computeDiff(upstream, prev) {
64123
64634
  function computeLocalHash(paths) {
64124
64635
  if (paths.length === 0)
64125
64636
  return null;
64126
- const h2 = crypto3.createHash("sha256");
64637
+ const h2 = crypto4.createHash("sha256");
64127
64638
  for (const filePath of paths) {
64128
64639
  if (!exists(filePath))
64129
64640
  return null;
64130
64641
  try {
64131
- const stat = fs69.statSync(filePath);
64642
+ const stat = fs70.statSync(filePath);
64132
64643
  if (stat.isFile()) {
64133
64644
  h2.update("F " + filePath + " ");
64134
- h2.update(fs69.readFileSync(filePath));
64645
+ h2.update(fs70.readFileSync(filePath));
64135
64646
  h2.update(`
64136
64647
  `);
64137
64648
  } else if (stat.isDirectory()) {
64138
- for (const name of fs69.readdirSync(filePath).sort()) {
64649
+ for (const name of fs70.readdirSync(filePath).sort()) {
64139
64650
  h2.update("E " + name + `
64140
64651
  `);
64141
64652
  }
@@ -64147,14 +64658,14 @@ function computeLocalHash(paths) {
64147
64658
  return h2.digest("hex");
64148
64659
  }
64149
64660
  function stageManifest(components) {
64150
- const root = fs69.mkdtempSync(path77.join(os12.tmpdir(), "brainbase-sync-"));
64661
+ const root = fs70.mkdtempSync(path78.join(os12.tmpdir(), "brainbase-sync-"));
64151
64662
  for (const c2 of components) {
64152
- const compDir = path77.join(root, c2.type, c2.slug);
64663
+ const compDir = path78.join(root, c2.type, c2.slug);
64153
64664
  ensureDir(compDir);
64154
64665
  for (const f4 of c2.files) {
64155
- const target = path77.join(compDir, f4.path);
64156
- ensureDir(path77.dirname(target));
64157
- fs69.writeFileSync(target, f4.content);
64666
+ const target = path78.join(compDir, f4.path);
64667
+ ensureDir(path78.dirname(target));
64668
+ fs70.writeFileSync(target, f4.content);
64158
64669
  }
64159
64670
  }
64160
64671
  return root;
@@ -64171,6 +64682,7 @@ function runHarnessInstall(harnessId, components, opts, agentName) {
64171
64682
  var import_picocolors39 = __toESM(require_picocolors(), 1);
64172
64683
 
64173
64684
  // src/cli/agent-pull.ts
64685
+ import { isDeepStrictEqual as isDeepStrictEqual2 } from "node:util";
64174
64686
  import { spawn as spawn2 } from "node:child_process";
64175
64687
  import path81 from "node:path";
64176
64688
  import fs74 from "node:fs";
@@ -64178,7 +64690,7 @@ import os14 from "node:os";
64178
64690
  var import_picocolors26 = __toESM(require_picocolors(), 1);
64179
64691
 
64180
64692
  // src/core/manifest-unsynced.ts
64181
- import fs70 from "node:fs";
64693
+ import fs71 from "node:fs";
64182
64694
  var import_yaml3 = __toESM(require_dist(), 1);
64183
64695
  function findUnsyncedBlocks(manifest) {
64184
64696
  const found = [];
@@ -64256,7 +64768,7 @@ function readLocalOnlyContent(cwd2) {
64256
64768
  let raw = null;
64257
64769
  if (file) {
64258
64770
  try {
64259
- raw = import_yaml3.default.parse(fs70.readFileSync(file, "utf8"));
64771
+ raw = import_yaml3.default.parse(fs71.readFileSync(file, "utf8"));
64260
64772
  } catch {
64261
64773
  raw = null;
64262
64774
  }
@@ -64278,7 +64790,7 @@ function readManifestAgentId(cwd2) {
64278
64790
  if (!file)
64279
64791
  return;
64280
64792
  try {
64281
- const raw = import_yaml3.default.parse(fs70.readFileSync(file, "utf8"));
64793
+ const raw = import_yaml3.default.parse(fs71.readFileSync(file, "utf8"));
64282
64794
  if (!raw || typeof raw !== "object")
64283
64795
  return;
64284
64796
  return clean(raw.id);
@@ -64296,420 +64808,6 @@ function readLocalOnlyContentReporting(cwd2) {
64296
64808
  return content;
64297
64809
  }
64298
64810
 
64299
- // src/core/agent-diff.ts
64300
- import path78 from "node:path";
64301
- import fs71 from "node:fs";
64302
- import crypto4 from "node:crypto";
64303
- function compKey(type, slug) {
64304
- return `${type}/${slug}`;
64305
- }
64306
- function hashString(s3) {
64307
- return crypto4.createHash("sha256").update(s3).digest("hex");
64308
- }
64309
- function jsonStringAsciiSafe(s3) {
64310
- return JSON.stringify(s3).replace(/[\u007f-\uffff]/g, (c2) => "\\u" + c2.charCodeAt(0).toString(16).padStart(4, "0"));
64311
- }
64312
- function canonicalJson2(value) {
64313
- if (value === null)
64314
- return "null";
64315
- if (typeof value === "boolean")
64316
- return value ? "true" : "false";
64317
- if (typeof value === "number") {
64318
- if (!Number.isFinite(value))
64319
- throw new Error("Non-finite numbers are not JSON");
64320
- return String(value);
64321
- }
64322
- if (typeof value === "string")
64323
- return jsonStringAsciiSafe(value);
64324
- if (Array.isArray(value)) {
64325
- return "[" + value.map(canonicalJson2).join(",") + "]";
64326
- }
64327
- if (typeof value === "object") {
64328
- const obj = value;
64329
- const keys2 = Object.keys(obj).sort();
64330
- return "{" + keys2.map((k3) => `${jsonStringAsciiSafe(k3)}:${canonicalJson2(obj[k3])}`).join(",") + "}";
64331
- }
64332
- throw new Error(`Unsupported value in canonicalJson: ${typeof value}`);
64333
- }
64334
- function hashMcpEntry(entry) {
64335
- const payload = {
64336
- args: entry.args ?? [],
64337
- env: entry.env ?? {},
64338
- headers: entry.headers ?? {}
64339
- };
64340
- if (entry.url !== undefined)
64341
- payload.url = entry.url;
64342
- if (entry.command !== undefined)
64343
- payload.command = entry.command;
64344
- payload.is_enabled = entry.is_enabled ?? true;
64345
- return crypto4.createHash("sha256").update(canonicalJson2(payload)).digest("hex");
64346
- }
64347
- function evalWirePayload(entry) {
64348
- return {
64349
- criteria: entry.criteria,
64350
- enabled: entry.enabled,
64351
- icon: entry.icon || null,
64352
- judge_agent: entry.judge_agent ?? null,
64353
- judge_model: entry.judge_model,
64354
- judge_type: entry.judge_type,
64355
- output_shape: entry.output_shape,
64356
- classification_values: entry.classification_values && entry.classification_values.length > 0 ? entry.classification_values : null
64357
- };
64358
- }
64359
- function hashEvalEntry(entry) {
64360
- return crypto4.createHash("sha256").update(canonicalJson2(evalWirePayload(entry))).digest("hex");
64361
- }
64362
- function fileHash(p2) {
64363
- if (!exists(p2))
64364
- return null;
64365
- try {
64366
- const buf = fs71.readFileSync(p2);
64367
- return crypto4.createHash("sha256").update(buf).digest("hex");
64368
- } catch {
64369
- return null;
64370
- }
64371
- }
64372
- function componentHashFromFileHashes(fileHashes) {
64373
- const h2 = crypto4.createHash("sha256");
64374
- for (const fh of [...fileHashes].sort()) {
64375
- h2.update(fh);
64376
- h2.update(`
64377
- `);
64378
- }
64379
- return h2.digest("hex");
64380
- }
64381
- function hashDirectoryAsComponent(dir) {
64382
- if (!exists(dir))
64383
- return null;
64384
- const fileHashes = [];
64385
- const walk = (sub) => {
64386
- let entries;
64387
- try {
64388
- entries = fs71.readdirSync(sub, { withFileTypes: true });
64389
- } catch {
64390
- return;
64391
- }
64392
- for (const e2 of entries) {
64393
- const full = path78.join(sub, e2.name);
64394
- if (e2.isFile()) {
64395
- const fh = fileHash(full);
64396
- if (fh)
64397
- fileHashes.push(fh);
64398
- } else if (e2.isDirectory()) {
64399
- walk(full);
64400
- }
64401
- }
64402
- };
64403
- walk(dir);
64404
- return componentHashFromFileHashes(fileHashes);
64405
- }
64406
- function readLocalComponents(cwd2, manifest) {
64407
- const out = [];
64408
- const instr = readInstructions(cwd2, manifest);
64409
- if (instr !== null && instr.trim().length > 0) {
64410
- out.push({
64411
- type: "instruction",
64412
- slug: "agent-instructions",
64413
- hash: componentHashFromFileHashes([hashString(normalizeInstructionBody(instr))])
64414
- });
64415
- }
64416
- for (const entry of manifest.skills) {
64417
- let parsed;
64418
- try {
64419
- parsed = parseSkillSource2(entry.source);
64420
- } catch {
64421
- try {
64422
- const core = parseSkillSource(entry.source);
64423
- if (core.type !== "inline" && core.type !== "local") {
64424
- out.push({
64425
- type: "skill",
64426
- slug: defaultSkillSlug(core),
64427
- hash: null,
64428
- declaredSource: entry.source
64429
- });
64430
- }
64431
- } catch {}
64432
- continue;
64433
- }
64434
- if (parsed.kind === "registry") {
64435
- out.push({
64436
- type: "skill",
64437
- slug: registrySkillComponentSlug(parsed),
64438
- hash: null,
64439
- declaredSource: entry.source
64440
- });
64441
- } else {
64442
- const abs = path78.resolve(cwd2, parsed.path);
64443
- const slug = path78.basename(abs);
64444
- out.push({
64445
- type: "skill",
64446
- slug,
64447
- hash: hashDirectoryAsComponent(abs),
64448
- declaredSource: entry.source,
64449
- localPath: abs
64450
- });
64451
- }
64452
- }
64453
- for (const entry of manifest.mcp ?? []) {
64454
- out.push({
64455
- type: "mcp",
64456
- slug: entry.name,
64457
- hash: hashMcpEntry(entry)
64458
- });
64459
- }
64460
- const pbEntries = manifest.playbooks ?? [];
64461
- const pbSlugs = assignPlaybookSlugs(pbEntries.map((e2) => e2.title));
64462
- for (let i = 0;i < pbEntries.length; i++) {
64463
- const entry = pbEntries[i];
64464
- const slug = pbSlugs[i];
64465
- const body = resolvePlaybookContent(cwd2, entry);
64466
- if (body === null) {
64467
- out.push({ type: "playbook", slug, hash: null });
64468
- continue;
64469
- }
64470
- const wireBody = assemblePlaybookWireBody(entry, body);
64471
- out.push({
64472
- type: "playbook",
64473
- slug,
64474
- hash: componentHashFromFileHashes([hashString(wireBody)])
64475
- });
64476
- }
64477
- for (const entry of manifest.evals ?? []) {
64478
- out.push({
64479
- type: "eval",
64480
- slug: entry.slug,
64481
- hash: hashEvalEntry(entry)
64482
- });
64483
- }
64484
- return out;
64485
- }
64486
- function threeWayDiff(input) {
64487
- const lockMap = new Map;
64488
- for (const c2 of input.lock)
64489
- lockMap.set(compKey(c2.type, c2.slug), c2);
64490
- const cloudMap = new Map;
64491
- for (const c2 of input.cloud)
64492
- cloudMap.set(compKey(c2.type, c2.slug), c2);
64493
- const localMap = new Map;
64494
- for (const c2 of input.local)
64495
- localMap.set(compKey(c2.type, c2.slug), c2);
64496
- const keys2 = new Set;
64497
- for (const k3 of lockMap.keys())
64498
- keys2.add(k3);
64499
- for (const k3 of cloudMap.keys())
64500
- keys2.add(k3);
64501
- for (const k3 of localMap.keys())
64502
- keys2.add(k3);
64503
- const rows = [];
64504
- for (const key2 of [...keys2].sort()) {
64505
- const lock = lockMap.get(key2);
64506
- const cloud = cloudMap.get(key2);
64507
- const local = localMap.get(key2);
64508
- const [type, slug] = key2.split("/", 2);
64509
- let localHash = local?.hash ?? null;
64510
- if (local && local.hash === null && local.declaredSource && lock?.source === local.declaredSource && cloud) {
64511
- localHash = cloud.hash;
64512
- }
64513
- const cloudHash = cloud?.hash;
64514
- const lockHash = lock?.hash;
64515
- const declaredSource = local?.declaredSource;
64516
- const present = {
64517
- local: !!local,
64518
- lock: !!lock,
64519
- cloud: !!cloud
64520
- };
64521
- let status;
64522
- if (present.local && present.cloud && !present.lock) {
64523
- status = "added-local";
64524
- } else if (!present.local && present.cloud && !present.lock) {
64525
- status = "added-cloud";
64526
- } else if (present.local && !present.cloud && !present.lock) {
64527
- status = "added-only-local";
64528
- } else if (!present.local && present.lock) {
64529
- status = "removed-local";
64530
- } else if (present.local && !present.cloud && present.lock) {
64531
- status = "removed-cloud";
64532
- } else {
64533
- const sourceChanged = !!local && local.hash === null && !!local.declaredSource && lock?.source !== undefined && lock.source !== local.declaredSource;
64534
- const localChanged = sourceChanged || localHash !== null && lockHash !== undefined && localHash !== lockHash;
64535
- const cloudChanged = cloudHash !== undefined && lockHash !== undefined && cloudHash !== lockHash;
64536
- const converged = !sourceChanged && localHash !== null && cloudHash !== undefined && localHash === cloudHash;
64537
- if (converged)
64538
- status = "in-sync";
64539
- else if (localChanged && cloudChanged)
64540
- status = "modified-both";
64541
- else if (localChanged)
64542
- status = "modified-local";
64543
- else if (cloudChanged)
64544
- status = "modified-cloud";
64545
- else
64546
- status = "in-sync";
64547
- }
64548
- rows.push({
64549
- key: key2,
64550
- type,
64551
- slug,
64552
- status,
64553
- localHash: localHash ?? undefined,
64554
- lockHash,
64555
- cloudHash,
64556
- declaredSource
64557
- });
64558
- }
64559
- return rows;
64560
- }
64561
- function partitionPushRows(rows, force) {
64562
- const toSend = [];
64563
- const conflicts = [];
64564
- const upstreamOnly = [];
64565
- for (const r2 of rows) {
64566
- switch (r2.status) {
64567
- case "modified-local":
64568
- case "added-only-local":
64569
- case "added-local":
64570
- case "removed-local":
64571
- toSend.push(r2);
64572
- break;
64573
- case "modified-both":
64574
- if (force)
64575
- toSend.push(r2);
64576
- else
64577
- conflicts.push(r2);
64578
- break;
64579
- case "modified-cloud":
64580
- case "added-cloud":
64581
- case "removed-cloud":
64582
- upstreamOnly.push(r2);
64583
- break;
64584
- case "in-sync":
64585
- break;
64586
- default: {
64587
- const _exhaustive = r2.status;
64588
- }
64589
- }
64590
- }
64591
- return { toSend, conflicts, upstreamOnly };
64592
- }
64593
- function diffAgentMeta(manifestMeta, lockMeta, cloudMeta) {
64594
- const eq = (a3, b4) => {
64595
- if (!a3 && !b4)
64596
- return true;
64597
- if (!a3 || !b4)
64598
- return false;
64599
- return a3.name === b4.name && (a3.tagline ?? "") === (b4.tagline ?? "");
64600
- };
64601
- return {
64602
- localChanged: !!manifestMeta && !!lockMeta && !eq(manifestMeta, lockMeta),
64603
- cloudChanged: !!cloudMeta && !!lockMeta && !eq(cloudMeta, lockMeta)
64604
- };
64605
- }
64606
- var WRITABLE_CAPABILITIES = ["memory", "browser"];
64607
- var MIRRORED_CAPABILITIES = ["slack", "meeting", "github", "linear"];
64608
- function manifestConfigState(manifest) {
64609
- return {
64610
- ...manifest.machine_kind !== undefined ? { machine_kind: manifest.machine_kind } : {},
64611
- ...hasOwn(manifest, "default_model") ? { default_model: manifest.default_model ?? null } : {},
64612
- ...manifest.capabilities?.memory !== undefined ? { memory: manifest.capabilities.memory } : {},
64613
- ...manifest.capabilities?.browser !== undefined ? { browser: manifest.capabilities.browser } : {}
64614
- };
64615
- }
64616
- function cloudConfigState(agent) {
64617
- return {
64618
- ...agent.machine_kind !== undefined ? { machine_kind: agent.machine_kind } : {},
64619
- ...hasOwn(agent, "default_model") ? { default_model: agent.default_model ?? null } : {},
64620
- ...agent.memory_enabled !== undefined ? { memory: agent.memory_enabled } : {},
64621
- ...agent.browser_enabled !== undefined ? { browser: agent.browser_enabled } : {}
64622
- };
64623
- }
64624
- function hasOwn(value, key2) {
64625
- return !!value && Object.prototype.hasOwnProperty.call(value, key2);
64626
- }
64627
- function staleConnectionMirrors(manifest, cloud) {
64628
- const actualByName = {
64629
- slack: cloud.slack_connected,
64630
- meeting: cloud.meeting_connected,
64631
- github: cloud.github_connected,
64632
- linear: cloud.linear_connected
64633
- };
64634
- const stale = [];
64635
- for (const name of MIRRORED_CAPABILITIES) {
64636
- const authored = manifest.capabilities?.[name];
64637
- const actual = actualByName[name];
64638
- if (authored === undefined || actual === undefined)
64639
- continue;
64640
- if (authored !== actual)
64641
- stale.push({ name, authored, actual });
64642
- }
64643
- return stale;
64644
- }
64645
- function diffAgentConfig(manifest, lock, cloud) {
64646
- const unsupported = [];
64647
- const machineSupported = cloud.machine_kind !== undefined;
64648
- const defaultModelSupported = hasOwn(cloud, "default_model");
64649
- if (manifest.machine_kind !== undefined && !machineSupported) {
64650
- unsupported.push("machine_kind");
64651
- }
64652
- if (manifest.default_model !== undefined && !defaultModelSupported) {
64653
- unsupported.push("default_model");
64654
- }
64655
- const machineMismatch = manifest.machine_kind !== undefined && machineSupported && manifest.machine_kind !== cloud.machine_kind;
64656
- const machineLocalChanged = manifest.machine_kind !== undefined && machineSupported && (lock?.machine_kind !== undefined ? manifest.machine_kind !== lock.machine_kind && manifest.machine_kind !== cloud.machine_kind : manifest.machine_kind !== cloud.machine_kind);
64657
- const machineCloudChanged = lock?.machine_kind !== undefined && machineSupported && lock.machine_kind !== cloud.machine_kind && manifest.machine_kind !== cloud.machine_kind;
64658
- const machineConverged = manifest.machine_kind !== undefined && machineSupported && manifest.machine_kind === cloud.machine_kind && (lock?.machine_kind === undefined || lock.machine_kind !== cloud.machine_kind);
64659
- const defaultModel = threeWayField(defaultModelSupported, manifest.default_model, cloud.default_model, lock, "default_model");
64660
- const memory = threeWayField(hasOwn(cloud, "memory"), manifest.memory, cloud.memory, lock, "memory");
64661
- const browser = threeWayField(hasOwn(cloud, "browser"), manifest.browser, cloud.browser, lock, "browser");
64662
- if (manifest.memory !== undefined && !hasOwn(cloud, "memory")) {
64663
- unsupported.push("memory");
64664
- }
64665
- if (manifest.browser !== undefined && !hasOwn(cloud, "browser")) {
64666
- unsupported.push("browser");
64667
- }
64668
- return {
64669
- unsupported,
64670
- machineMismatch,
64671
- machineLocalChanged,
64672
- machineCloudChanged,
64673
- defaultModelLocalChanged: defaultModel.localChanged,
64674
- defaultModelCloudChanged: defaultModel.cloudChanged,
64675
- defaultModelConflict: defaultModel.conflict,
64676
- memory,
64677
- browser,
64678
- baselineConverged: machineConverged || defaultModel.converged || memory.converged || browser.converged
64679
- };
64680
- }
64681
- function threeWayField(supported, authored, cloudRaw, lock, key2) {
64682
- const result2 = {
64683
- localChanged: false,
64684
- cloudChanged: false,
64685
- conflict: false,
64686
- converged: false
64687
- };
64688
- if (!supported)
64689
- return result2;
64690
- const cloudValue = cloudRaw ?? null;
64691
- const lockSupported = hasOwn(lock, key2);
64692
- const lockValue = lock?.[key2] ?? null;
64693
- if (authored === undefined) {
64694
- if (lockSupported)
64695
- result2.cloudChanged = cloudValue !== lockValue;
64696
- return result2;
64697
- }
64698
- const localValue = authored ?? null;
64699
- if (!lockSupported) {
64700
- result2.localChanged = localValue !== cloudValue;
64701
- result2.converged = localValue === cloudValue;
64702
- return result2;
64703
- }
64704
- const localMoved = localValue !== lockValue;
64705
- const cloudMoved = cloudValue !== lockValue;
64706
- result2.conflict = localMoved && cloudMoved && localValue !== cloudValue;
64707
- result2.localChanged = localMoved && localValue !== cloudValue;
64708
- result2.cloudChanged = cloudMoved && localValue !== cloudValue;
64709
- result2.converged = localMoved && cloudMoved && localValue === cloudValue;
64710
- return result2;
64711
- }
64712
-
64713
64811
  // src/core/eval-merge.ts
64714
64812
  function evalsFromCloudComponents(components) {
64715
64813
  return components.filter((c2) => c2.type === "eval").map((c2) => {
@@ -64792,41 +64890,6 @@ function capabilityBaselineAfterPush(cloudAgent, previous, diff2) {
64792
64890
  return out;
64793
64891
  }
64794
64892
 
64795
- // src/core/mcp-merge.ts
64796
- var RUNTIME_META_PREFIX = "runtime_";
64797
- function runtimeMetaFromComponent(component) {
64798
- const meta = component.meta ?? {};
64799
- const carried = {};
64800
- for (const [key2, value] of Object.entries(meta)) {
64801
- if (key2.startsWith(RUNTIME_META_PREFIX) && value !== undefined) {
64802
- carried[key2] = value;
64803
- }
64804
- }
64805
- return Object.keys(carried).length > 0 ? carried : undefined;
64806
- }
64807
- function mcpEntriesFromCloudComponents(components) {
64808
- return components.filter((c2) => c2.type === "mcp").map((c2) => {
64809
- const payload = (c2.meta ?? {}).mcp ?? {};
64810
- const entry = { name: c2.slug };
64811
- if (typeof payload.url === "string")
64812
- entry.url = payload.url;
64813
- if (typeof payload.command === "string")
64814
- entry.command = payload.command;
64815
- if (Array.isArray(payload.args))
64816
- entry.args = payload.args.map(String);
64817
- if (payload.env && typeof payload.env === "object")
64818
- entry.env = payload.env;
64819
- if (payload.headers && typeof payload.headers === "object")
64820
- entry.headers = payload.headers;
64821
- if (typeof payload.is_enabled === "boolean")
64822
- entry.is_enabled = payload.is_enabled;
64823
- const runtimeMeta = runtimeMetaFromComponent(c2);
64824
- if (runtimeMeta)
64825
- entry.runtime_meta = runtimeMeta;
64826
- return entry;
64827
- });
64828
- }
64829
-
64830
64893
  // src/core/secrets-env.ts
64831
64894
  import path79 from "node:path";
64832
64895
  import fs72 from "node:fs";
@@ -64904,6 +64967,12 @@ function ensureSecretsGitignore(cwd2) {
64904
64967
  }
64905
64968
  } catch {}
64906
64969
  }
64970
+ function runtimeEnvSecret(name, env3 = process.env) {
64971
+ if (!isBrainbaseTaskRuntime(env3))
64972
+ return null;
64973
+ const value = env3[name];
64974
+ return typeof value === "string" && value.length > 0 ? value : null;
64975
+ }
64907
64976
  function diffSecrets(local, cloud) {
64908
64977
  const localKeys = new Set(Object.keys(local));
64909
64978
  const cloudKeys = new Set(Object.keys(cloud));
@@ -65035,7 +65104,8 @@ async function runAgentUnpack(cwd2, args) {
65035
65104
  slug: entry.name,
65036
65105
  scope,
65037
65106
  rootDir: compDir,
65038
- payload: proxifyMcpPayload(payload),
65107
+ payload: proxifyMcpPayload(payload, undefined, entry.runtime_meta?.runtime_mcp_auth),
65108
+ meta: entry.runtime_meta,
65039
65109
  checksum: ""
65040
65110
  });
65041
65111
  }
@@ -65426,13 +65496,28 @@ async function runAgentPull(cwd2, args) {
65426
65496
  break;
65427
65497
  }
65428
65498
  }
65499
+ for (const r2 of rows) {
65500
+ if (r2.type !== "mcp" || !["in-sync", "added-local"].includes(r2.status))
65501
+ continue;
65502
+ const remote = cloud.components.find((c2) => c2.type === "mcp" && c2.slug === r2.slug);
65503
+ const local = existingManifest?.mcp.find((entry) => entry.name === r2.slug);
65504
+ if (!remote || isDeepStrictEqual2(remote.meta?.runtime_mcp_auth, local?.runtime_meta?.runtime_mcp_auth))
65505
+ continue;
65506
+ const prior = lock?.components.find((c2) => c2.type === "mcp" && c2.slug === r2.slug);
65507
+ const target = { harness, cwd: cwd2, scope: args.scope ?? "project" };
65508
+ const locallyReplaced = prior && readHarnessMcpEntry(harness, cwd2, target.scope, r2.slug) !== undefined && !lockEntryOwnsMcpInStore(prior, target);
65509
+ if (locallyReplaced && !override)
65510
+ conflicts.push(r2);
65511
+ else
65512
+ toInstallKeys.add(r2.key);
65513
+ }
65429
65514
  const keepLocalKeys = new Set;
65430
65515
  if (conflicts.length > 0) {
65431
65516
  requireInteractive(`${conflicts.length} file(s) changed both locally and in the cloud — resolve them in an interactive terminal, or pass --force to take the cloud copy.`);
65432
65517
  }
65433
65518
  for (const r2 of conflicts) {
65434
65519
  const choice = await ie({
65435
- message: r2.status === "modified-both" ? `${fmtType(r2.type)} ${import_picocolors26.default.bold(r2.slug)} — both you and the cloud edited it` : `${fmtType(r2.type)} ${import_picocolors26.default.bold(r2.slug)} — you have local edits not yet pushed`,
65520
+ message: r2.status === "in-sync" || r2.status === "added-local" ? `${fmtType(r2.type)} ${import_picocolors26.default.bold(r2.slug)} — your installed server was replaced and its cloud authentication changed` : r2.status === "modified-both" ? `${fmtType(r2.type)} ${import_picocolors26.default.bold(r2.slug)} — both you and the cloud edited it` : `${fmtType(r2.type)} ${import_picocolors26.default.bold(r2.slug)} — you have local edits not yet pushed`,
65436
65521
  options: [
65437
65522
  { value: "cloud", label: "Use the cloud's version (discard local edits)" },
65438
65523
  { value: "keep", label: "Keep your local edits (skip this one)" }
@@ -65512,6 +65597,7 @@ async function runAgentPull(cwd2, args) {
65512
65597
  const installComponents = cloud.components.filter((c2) => toInstallKeys.has(`${c2.type}/${c2.slug}`));
65513
65598
  const stageRoot = stageManifestComponents(installComponents);
65514
65599
  const justInstalledPaths = new Map;
65600
+ const skippedMcpSlugs = new Set;
65515
65601
  const justInstalledFingerprints = new Map;
65516
65602
  try {
65517
65603
  const availableSecrets = await pullSecrets(cwd2, agentId);
@@ -65525,7 +65611,7 @@ async function runAgentPull(cwd2, args) {
65525
65611
  rootDir: path81.join(stageRoot, c2.type, c2.slug),
65526
65612
  description: c2.description,
65527
65613
  meta: c2.meta,
65528
- payload: proxifyMcpPayload(structuredClone(c2.meta?.mcp)),
65614
+ payload: proxifyMcpPayload(structuredClone(c2.meta?.mcp), undefined, c2.meta?.runtime_mcp_auth),
65529
65615
  checksum: c2.hash,
65530
65616
  source: skillSourceFromMeta(c2)
65531
65617
  }));
@@ -65534,7 +65620,7 @@ async function runAgentPull(cwd2, args) {
65534
65620
  cwd: cwd2,
65535
65621
  scope,
65536
65622
  resolveConflict: async (_c) => "overwrite",
65537
- resolveSecret: async (name) => availableSecrets[name] ?? null,
65623
+ resolveSecret: async (name) => availableSecrets[name] ?? runtimeEnvSecret(name),
65538
65624
  ownedMcpSlugs: ownedMcpSlugsForStore(lock?.components ?? [], {
65539
65625
  harness,
65540
65626
  cwd: cwd2,
@@ -65550,6 +65636,8 @@ async function runAgentPull(cwd2, args) {
65550
65636
  }
65551
65637
  }
65552
65638
  for (const s3 of result2.skipped) {
65639
+ if (s3.type === "mcp")
65640
+ skippedMcpSlugs.add(s3.slug);
65553
65641
  if (s3.reason !== KEPT_UNOWNED_MCP_SKIP)
65554
65642
  continue;
65555
65643
  f2.warn(`Kept local MCP server ${import_picocolors26.default.bold(s3.slug)}: not installed by this agent; the cloud has it disabled. Remove it from your harness config to let the cloud's setting apply.`);
@@ -65597,6 +65685,24 @@ async function runAgentPull(cwd2, args) {
65597
65685
  materializeEntrypoint(cwd2, cloudAgent.entrypoint ?? "", existingManifest);
65598
65686
  materializePlaybooks(cwd2, cloud, toInstallKeys, keepLocalKeys, existingManifest);
65599
65687
  const yaml = mergeManifest(cwd2, existingManifest, cloud, cloudAgent, harness, override ? {} : readLocalOnlyContentReporting(cwd2), new Map((lock?.components ?? []).filter((c2) => c2.type === "eval").map((c2) => [c2.slug, c2.hash])), new Set([...keepLocalKeys].filter((key2) => key2.startsWith("eval/")).map((key2) => key2.slice("eval/".length))));
65688
+ for (const entry of existingManifest?.mcp ?? []) {
65689
+ if (!keepLocalKeys.has(`mcp/${entry.name}`))
65690
+ continue;
65691
+ const index = yaml.mcp.findIndex((c2) => c2.name === entry.name);
65692
+ if (index >= 0)
65693
+ yaml.mcp[index] = entry;
65694
+ }
65695
+ for (const entry of yaml.mcp) {
65696
+ if (!skippedMcpSlugs.has(entry.name))
65697
+ continue;
65698
+ const priorAuth = existingManifest?.mcp.find((c2) => c2.name === entry.name)?.runtime_meta?.runtime_mcp_auth;
65699
+ const runtimeMeta = { ...entry.runtime_meta };
65700
+ if (priorAuth === undefined)
65701
+ delete runtimeMeta.runtime_mcp_auth;
65702
+ else
65703
+ runtimeMeta.runtime_mcp_auth = priorAuth;
65704
+ entry.runtime_meta = Object.keys(runtimeMeta).length > 0 ? runtimeMeta : undefined;
65705
+ }
65600
65706
  writeManifest(cwd2, yaml);
65601
65707
  writeLink(cwd2, buildLinkFromAgent(cloudAgent, harness, readLink(cwd2)));
65602
65708
  const lockComponents = buildLockComponents({
@@ -69285,7 +69391,7 @@ async function installAgentFresh(input) {
69285
69391
  rootDir: path87.join(stageRoot, c2.type, c2.slug),
69286
69392
  description: c2.description,
69287
69393
  meta: c2.meta,
69288
- payload: proxifyMcpPayload(structuredClone(c2.meta?.mcp)),
69394
+ payload: proxifyMcpPayload(structuredClone(c2.meta?.mcp), undefined, c2.meta?.runtime_mcp_auth),
69289
69395
  checksum: c2.hash
69290
69396
  }));
69291
69397
  toInstall.push(...builtinInstall);
@@ -71867,14 +71973,21 @@ import fs81 from "node:fs";
71867
71973
  function collectServers(cwd2, env3 = process.env) {
71868
71974
  const out = [];
71869
71975
  const seen = new Set;
71976
+ let manifestEntries = [];
71977
+ try {
71978
+ manifestEntries = readManifest(cwd2)?.mcp ?? [];
71979
+ } catch {}
71980
+ const identities = new Map(manifestEntries.map((entry) => [entry.name, entry]));
71870
71981
  for (const source of [readClaudeCode, readCodex, readKafka, readResolvedMcps]) {
71871
71982
  for (const [name, entry] of source(cwd2)) {
71872
- pushResolved(out, seen, name, entry, env3);
71983
+ const authored = identities.get(name);
71984
+ const identity2 = typeof entry.url === "string" && typeof authored?.url === "string" && mcpUpstreamUrl(entry.url, env3) === mcpUpstreamUrl(authored.url, env3) ? authored.runtime_meta?.runtime_mcp_auth : undefined;
71985
+ pushResolved(out, seen, name, entry, env3, identity2);
71873
71986
  }
71874
71987
  }
71875
71988
  return out;
71876
71989
  }
71877
- function pushResolved(out, seen, name, entry, env3) {
71990
+ function pushResolved(out, seen, name, entry, env3, identity2) {
71878
71991
  if (seen.has(name))
71879
71992
  return;
71880
71993
  if (entry.is_enabled === false) {
@@ -71897,7 +72010,7 @@ function pushResolved(out, seen, name, entry, env3) {
71897
72010
  };
71898
72011
  }
71899
72012
  delete normalized.http_headers;
71900
- const proxied = proxifyMcpPayload(normalized, env3) ?? normalized;
72013
+ const proxied = proxifyMcpPayload(normalized, env3, identity2) ?? normalized;
71901
72014
  const resolved = resolveMcpEnvTemplates(proxied, env3) ?? proxied;
71902
72015
  const finalUrl = resolved.url;
71903
72016
  if (typeof finalUrl !== "string" || finalUrl.length === 0)