@haven_ai/connect 0.6.0-alpha.0 → 0.7.0-alpha.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.
package/dist/cli.js CHANGED
@@ -613,9 +613,9 @@ var init_runtime_manifest = __esm({
613
613
  mcpPackage: "@haven_ai/mcp",
614
614
  mcpVersion: MCP_VERSION,
615
615
  sdkPackage: "@haven_ai/sdk",
616
- sdkVersion: "0.6.0-alpha.0",
616
+ sdkVersion: "0.7.0-alpha.0",
617
617
  signerPackage: "@haven_ai/signer",
618
- signerVersion: "0.6.0-alpha.0",
618
+ signerVersion: "0.7.0-alpha.0",
619
619
  // Sourced from the SDK, never a literal (#1161). This field read '20.0.0'
620
620
  // while every package's `engines` said `>=24` and the docs said `>=24.0.0`,
621
621
  // so the guard that was supposed to enforce the floor waved Node v23 through
@@ -4444,7 +4444,7 @@ var init_runtime = __esm({
4444
4444
  init_tombstone();
4445
4445
  init_local_mcp_runtime();
4446
4446
  init_runtime_manifest();
4447
- CONNECTOR_VERSION = "0.6.0-alpha.0";
4447
+ CONNECTOR_VERSION = "0.7.0-alpha.0";
4448
4448
  CONNECTOR_CLIENT_IDENTITY = `@haven_ai/connect/${CONNECTOR_VERSION}`;
4449
4449
  CONNECT_OUTCOME_SCHEMA_VERSION = 1;
4450
4450
  failureOutcomesByError = /* @__PURE__ */ new WeakMap();
@@ -4927,9 +4927,23 @@ function rollUpLevel(checks) {
4927
4927
  function runtimeFlagFor(runtime) {
4928
4928
  return normalizeRuntimeName(runtime) ? ` --runtime ${runtime}` : "";
4929
4929
  }
4930
- function repairCommandFor(runtime, suffix = "") {
4931
- if (normalizeRuntimeName(runtime)) return `Run: ${RERUN} --doctor --repair --runtime ${runtime}${suffix}`;
4932
- return `Run: ${RERUN} --doctor --repair --runtime <runtime>${suffix.replace(/\.$/, "")} \u2014 --repair needs the runtime named; one of: ${RUNTIME_FLAG_VALUE_LIST.join(", ")}.`;
4930
+ async function pathExists3(path) {
4931
+ try {
4932
+ await stat(path);
4933
+ return true;
4934
+ } catch {
4935
+ return false;
4936
+ }
4937
+ }
4938
+ function credentialsDirFlagFor(credentialsDir) {
4939
+ if (!credentialsDir) return "";
4940
+ const quoted = /^[\w@%+=:,./~-]+$/.test(credentialsDir) ? credentialsDir : `'${credentialsDir.replace(/'/g, `'\\''`)}'`;
4941
+ return ` --credentials-dir ${quoted}`;
4942
+ }
4943
+ function repairCommandFor(runtime, suffix = "", credentialsDir) {
4944
+ const dirFlag = credentialsDirFlagFor(credentialsDir);
4945
+ if (normalizeRuntimeName(runtime)) return `Run: ${RERUN} --doctor --repair --runtime ${runtime}${dirFlag}${suffix}`;
4946
+ return `Run: ${RERUN} --doctor --repair --runtime <runtime>${dirFlag}${suffix.replace(/\.$/, "")} \u2014 --repair needs the runtime named; one of: ${RUNTIME_FLAG_VALUE_LIST.join(", ")}.`;
4933
4947
  }
4934
4948
  async function resolveDoctorRuntime(input, directory) {
4935
4949
  const explicit = input.runtime.trim();
@@ -4948,7 +4962,7 @@ async function discoverCredentialDirectory(homeDir, explicit) {
4948
4962
  try {
4949
4963
  entries = await readdir(root);
4950
4964
  } catch {
4951
- return explicit ? { directory: explicit, others: [], parkedOnly: /* @__PURE__ */ new Set() } : { others: [], parkedOnly: /* @__PURE__ */ new Set() };
4965
+ return explicit ? { directory: explicit, others: [], parkedOnly: /* @__PURE__ */ new Set(), identityDirectories: [explicit] } : { others: [], parkedOnly: /* @__PURE__ */ new Set(), identityDirectories: [] };
4952
4966
  }
4953
4967
  const candidates = [];
4954
4968
  const tombstonedOnly = [];
@@ -4973,20 +4987,23 @@ async function discoverCredentialDirectory(homeDir, explicit) {
4973
4987
  }
4974
4988
  const parkedOnlySet = new Set(parkedOnly);
4975
4989
  candidates.sort((a, b) => b.mtimeMs - a.mtimeMs);
4990
+ const identityDirectories = candidates.map((c) => c.directory);
4976
4991
  if (explicit) {
4977
4992
  return {
4978
4993
  directory: explicit,
4979
4994
  others: [...candidates.map((c) => c.directory), ...tombstonedOnly, ...parkedOnly].filter((d) => d !== explicit),
4980
- parkedOnly: parkedOnlySet
4995
+ parkedOnly: parkedOnlySet,
4996
+ identityDirectories: [explicit, ...identityDirectories.filter((d) => d !== explicit)]
4981
4997
  };
4982
4998
  }
4983
4999
  if (candidates.length === 0 && tombstonedOnly.length === 0 && parkedOnly.length === 0) {
4984
- return { others: [], parkedOnly: parkedOnlySet };
5000
+ return { others: [], parkedOnly: parkedOnlySet, identityDirectories };
4985
5001
  }
4986
5002
  return {
4987
5003
  directory: candidates[0]?.directory,
4988
5004
  others: [...candidates.slice(1).map((c) => c.directory), ...tombstonedOnly, ...parkedOnly],
4989
- parkedOnly: parkedOnlySet
5005
+ parkedOnly: parkedOnlySet,
5006
+ identityDirectories
4990
5007
  };
4991
5008
  }
4992
5009
  function agentIsWired(configText, names, slug, identity, sidecar, isPrimary, bareOwnerExists) {
@@ -5045,7 +5062,7 @@ function rekeyPendingVerdict(status, hostedDelegateAddress, runtime, slug) {
5045
5062
  detail: `A re-key started ${started} is still open (expires ${status.expiresAt ?? "unknown"}). Paste this address into "Replace signing key" on the Haven agent page: ${address}. Parked at ${status.path}. ` + wedgeNote
5046
5063
  };
5047
5064
  }
5048
- async function runtimeSpecOverrideCheck(directory, sidecar, env) {
5065
+ async function runtimeSpecOverrideCheck(directory, sidecar, env, repairDir) {
5049
5066
  const facts = [];
5050
5067
  const installed = sidecar?.runtime_spec_override;
5051
5068
  if (installed) {
@@ -5074,7 +5091,7 @@ async function runtimeSpecOverrideCheck(directory, sidecar, env) {
5074
5091
  label: "Runtime spec override",
5075
5092
  level: "failed",
5076
5093
  detail: `runtime spec overridden \u2014 not the pinned manifest (${MCP_RUNTIME_MANIFEST.signerPackage}@${MCP_RUNTIME_MANIFEST.signerVersion}, ${MCP_RUNTIME_MANIFEST.sdkPackage}@${MCP_RUNTIME_MANIFEST.sdkVersion}). ${facts.join(". ")}.`,
5077
- repair: `Developer override (#2424). To return to the pinned manifest: unset ${variables}, then run ${RERUN} --doctor --repair --runtime <runtime>. If the override is intentional, this finding is the record of it.`
5094
+ repair: `Developer override (#2424). To return to the pinned manifest: unset ${variables}, then run ${RERUN} --doctor --repair --runtime <runtime>${credentialsDirFlagFor(repairDir)}. If the override is intentional, this finding is the record of it.`
5078
5095
  });
5079
5096
  }
5080
5097
  async function readMcpSidecarOverride(directory) {
@@ -5133,7 +5150,7 @@ async function verdictsForAgent(entry, input, deps) {
5133
5150
  label: "Signer runtime (preinstalled wrapper)",
5134
5151
  level: "failed",
5135
5152
  detail: "No signer-runtime.json sidecar \u2014 the pinned signer runtime was never prepared (or a pre-#1586 npx config).",
5136
- repair: repairCommandFor(input.runtime)
5153
+ repair: repairCommandFor(input.runtime, "", input.repairDir)
5137
5154
  });
5138
5155
  } else if (sidecar.runtime_spec_override) {
5139
5156
  const matches = await installedRuntimeMatchesVersions(sidecar.runtime_directory, sidecar.cli_path, {
@@ -5145,7 +5162,7 @@ async function verdictsForAgent(entry, input, deps) {
5145
5162
  label: "Signer runtime (preinstalled wrapper)",
5146
5163
  level: matches ? "ok" : "failed",
5147
5164
  detail: matches ? `Installed ${sidecar.signer_package}@${sidecar.signer_version} at ${sidecar.runtime_directory} (override install \u2014 see runtime_spec_override)` : `Override runtime directory is stale or empty (${sidecar.runtime_directory}) \u2014 the CLI or package versions are missing.`,
5148
- ...matches ? {} : { repair: repairCommandFor(input.runtime, " with the same HAVEN_*_SPEC variables set.") }
5165
+ ...matches ? {} : { repair: repairCommandFor(input.runtime, " with the same HAVEN_*_SPEC variables set.", input.repairDir) }
5149
5166
  });
5150
5167
  } else {
5151
5168
  const intact = await installedRuntimeMatchesVersions(sidecar.runtime_directory, sidecar.cli_path, {
@@ -5159,10 +5176,10 @@ async function verdictsForAgent(entry, input, deps) {
5159
5176
  label: "Signer runtime (preinstalled wrapper)",
5160
5177
  level: ok ? "ok" : intact ? "advisory" : "failed",
5161
5178
  detail: ok ? `Installed ${sidecar.signer_package}@${sidecar.signer_version} at ${sidecar.runtime_directory}` : intact ? `Installed version ${sidecar.signer_version} does not match the connector's pinned ${MCP_RUNTIME_MANIFEST.signerVersion} \u2014 intact, but outdated.` : `Runtime directory is stale or empty (${sidecar.runtime_directory}) \u2014 the CLI or package versions are missing.`,
5162
- ...ok ? {} : { repair: repairCommandFor(input.runtime) }
5179
+ ...ok ? {} : { repair: repairCommandFor(input.runtime, "", input.repairDir) }
5163
5180
  });
5164
5181
  }
5165
- const overrideCheck = await runtimeSpecOverrideCheck(directory, sidecar, deps.env ?? process.env);
5182
+ const overrideCheck = await runtimeSpecOverrideCheck(directory, sidecar, deps.env ?? process.env, input.repairDir);
5166
5183
  if (overrideCheck) checks.push(overrideCheck);
5167
5184
  const hostedUrl = identity?.hosted_mcp_url ?? (identity?.api_url ? `${identity.api_url}/mcp` : void 0);
5168
5185
  if (identity?.api_key && hostedUrl) {
@@ -5252,7 +5269,7 @@ async function verdictsForAgent(entry, input, deps) {
5252
5269
  label: "Signer stdio handshake",
5253
5270
  level: probe.status === "ok" ? "ok" : "failed",
5254
5271
  detail: probe.status === "ok" ? `Signer started, listed ${probe.toolNames?.length ?? 0} tools${probe.serverInfo?.version ? ` (v${probe.serverInfo.version})` : ""}.${compatDetail}` : `Handshake failed: ${probe.status}.`,
5255
- ...probe.status === "ok" ? {} : { repair: repairCommandFor(input.runtime) }
5272
+ ...probe.status === "ok" ? {} : { repair: repairCommandFor(input.runtime, "", input.repairDir) }
5256
5273
  });
5257
5274
  }
5258
5275
  } else {
@@ -5261,7 +5278,7 @@ async function verdictsForAgent(entry, input, deps) {
5261
5278
  label: "Signer stdio handshake",
5262
5279
  level: "failed",
5263
5280
  detail: "Skipped \u2014 no prepared signer runtime to probe.",
5264
- repair: repairCommandFor(input.runtime)
5281
+ repair: repairCommandFor(input.runtime, "", input.repairDir)
5265
5282
  });
5266
5283
  }
5267
5284
  return { checks, ...signerCapabilities ? { signerCapabilities } : {} };
@@ -5270,7 +5287,8 @@ async function runDoctor(input, deps = {}) {
5270
5287
  const homeDir = deps.homeDir ?? homedir();
5271
5288
  const checks = [];
5272
5289
  let signerCapabilities;
5273
- const { directory, others, parkedOnly } = await discoverCredentialDirectory(homeDir, input.credentialsDir);
5290
+ const { directory, others, parkedOnly, identityDirectories } = await discoverCredentialDirectory(homeDir, input.credentialsDir);
5291
+ const nameRepairDir = identityDirectories.length > 1 || Boolean(input.credentialsDir);
5274
5292
  const resolution = await resolveDoctorRuntime(input, directory);
5275
5293
  const runtime = resolution.runtime;
5276
5294
  const input2 = { ...input, runtime };
@@ -5327,7 +5345,11 @@ async function runDoctor(input, deps = {}) {
5327
5345
  ...rekeyPending ? { rekeyPending } : {}
5328
5346
  };
5329
5347
  if (wired) {
5330
- const result = await checksForAgent({ directory: dir, identity, sidecar }, input2, deps);
5348
+ const result = await checksForAgent(
5349
+ { directory: dir, identity, sidecar },
5350
+ { ...input2, ...nameRepairDir ? { repairDir: dir } : {} },
5351
+ deps
5352
+ );
5331
5353
  entry.checks = result.checks;
5332
5354
  capabilitiesByDirectory.set(dir, result.signerCapabilities);
5333
5355
  } else if (rekeyPending) {
@@ -5356,7 +5378,7 @@ async function runDoctor(input, deps = {}) {
5356
5378
  if (!primaryChecksById.has("credentials")) {
5357
5379
  const result = await checksForAgent(
5358
5380
  { directory: primaryDirectory, identity: primaryIdentity, sidecar: primarySidecar },
5359
- input2,
5381
+ { ...input2, ...nameRepairDir ? { repairDir: primaryDirectory } : {} },
5360
5382
  deps
5361
5383
  );
5362
5384
  signerCapabilities = result.signerCapabilities;
@@ -5397,7 +5419,7 @@ async function runDoctor(input, deps = {}) {
5397
5419
  label: "Runtime MCP config",
5398
5420
  level: "failed",
5399
5421
  detail: `No runtime config at ${configPath}.`,
5400
- repair: repairCommandFor(input2.runtime)
5422
+ repair: repairCommandFor(input2.runtime, "", nameRepairDir ? primaryDirectory : void 0)
5401
5423
  });
5402
5424
  } else {
5403
5425
  const primaryIdentity = await readIdentity(primaryDirectory ?? "");
@@ -5411,7 +5433,7 @@ async function runDoctor(input, deps = {}) {
5411
5433
  label: "Runtime MCP config",
5412
5434
  level: ok ? "ok" : "failed",
5413
5435
  detail: ok ? `Config at ${configPath} references the hosted server and the prepared signer wrapper.` : signerViaNpx ? `Config at ${configPath} still launches the signer via npx \u2014 the pre-#1586 shape that cannot start under a 120s startup timeout.` : `Config at ${configPath} is missing the Haven entries${primarySidecar && !wrapperReferenced ? " (or references a different signer wrapper)" : ""}.`,
5414
- ...ok ? {} : { repair: repairCommandFor(input2.runtime) }
5436
+ ...ok ? {} : { repair: repairCommandFor(input2.runtime, "", nameRepairDir ? primaryDirectory : void 0) }
5415
5437
  });
5416
5438
  }
5417
5439
  for (const id of ["hosted_mcp", "identity_match", "rekey_pending"]) {
@@ -5554,7 +5576,25 @@ async function runDoctor(input, deps = {}) {
5554
5576
  async function runRepair(input, deps = {}) {
5555
5577
  const homeDir = deps.homeDir ?? homedir();
5556
5578
  const messages = [];
5557
- const { directory, others } = await discoverCredentialDirectory(homeDir, input.credentialsDir);
5579
+ const { directory, others, identityDirectories } = await discoverCredentialDirectory(homeDir, input.credentialsDir);
5580
+ if (!input.credentialsDir && identityDirectories.length > 1) {
5581
+ const lines = await Promise.all(
5582
+ identityDirectories.map(async (dir) => {
5583
+ const identity2 = await readIdentity(dir);
5584
+ const who = identity2?.agent_id ? `agent ${identity2.agent_id}` : "agent (no agent_id recorded)";
5585
+ const retired = !identity2?.api_key || await pathExists3(join(dir, TOMBSTONE_FILENAME));
5586
+ return retired ? ` \u2022 ${who} (${dir}): retired \u2014 not a repair target` : ` \u2022 ${who}: ${repairCommandFor(input.runtime, "", dir).replace(/^Run: /, "")}`;
5587
+ })
5588
+ );
5589
+ return {
5590
+ ok: false,
5591
+ messages: [
5592
+ `${identityDirectories.length} agent credential directories exist on this machine, and a repair rewrites the runtime config from ONE of them. Name the agent to repair with --credentials-dir \u2014 nothing was changed:`,
5593
+ ...lines,
5594
+ "Not sure which agent is which? Compare the agent ids against the Haven agent page, or run --doctor first."
5595
+ ]
5596
+ };
5597
+ }
5558
5598
  if (others.length > 0) {
5559
5599
  messages.push(`Note: ${others.length} other agent credential dir(s) exist \u2014 run --doctor for their status.`);
5560
5600
  }