@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/index.cjs CHANGED
@@ -619,9 +619,9 @@ var init_runtime_manifest = __esm({
619
619
  mcpPackage: "@haven_ai/mcp",
620
620
  mcpVersion: mcp.MCP_VERSION,
621
621
  sdkPackage: "@haven_ai/sdk",
622
- sdkVersion: "0.6.0-alpha.0",
622
+ sdkVersion: "0.7.0-alpha.0",
623
623
  signerPackage: "@haven_ai/signer",
624
- signerVersion: "0.6.0-alpha.0",
624
+ signerVersion: "0.7.0-alpha.0",
625
625
  // Sourced from the SDK, never a literal (#1161). This field read '20.0.0'
626
626
  // while every package's `engines` said `>=24` and the docs said `>=24.0.0`,
627
627
  // so the guard that was supposed to enforce the floor waved Node v23 through
@@ -4450,7 +4450,7 @@ var init_runtime = __esm({
4450
4450
  init_tombstone();
4451
4451
  init_local_mcp_runtime();
4452
4452
  init_runtime_manifest();
4453
- exports.CONNECTOR_VERSION = "0.6.0-alpha.0";
4453
+ exports.CONNECTOR_VERSION = "0.7.0-alpha.0";
4454
4454
  CONNECTOR_CLIENT_IDENTITY = `@haven_ai/connect/${exports.CONNECTOR_VERSION}`;
4455
4455
  exports.CONNECT_OUTCOME_SCHEMA_VERSION = 1;
4456
4456
  failureOutcomesByError = /* @__PURE__ */ new WeakMap();
@@ -4933,9 +4933,23 @@ function rollUpLevel(checks) {
4933
4933
  function runtimeFlagFor(runtime) {
4934
4934
  return normalizeRuntimeName(runtime) ? ` --runtime ${runtime}` : "";
4935
4935
  }
4936
- function repairCommandFor(runtime, suffix = "") {
4937
- if (normalizeRuntimeName(runtime)) return `Run: ${RERUN} --doctor --repair --runtime ${runtime}${suffix}`;
4938
- return `Run: ${RERUN} --doctor --repair --runtime <runtime>${suffix.replace(/\.$/, "")} \u2014 --repair needs the runtime named; one of: ${RUNTIME_FLAG_VALUE_LIST.join(", ")}.`;
4936
+ async function pathExists3(path) {
4937
+ try {
4938
+ await promises.stat(path);
4939
+ return true;
4940
+ } catch {
4941
+ return false;
4942
+ }
4943
+ }
4944
+ function credentialsDirFlagFor(credentialsDir) {
4945
+ if (!credentialsDir) return "";
4946
+ const quoted = /^[\w@%+=:,./~-]+$/.test(credentialsDir) ? credentialsDir : `'${credentialsDir.replace(/'/g, `'\\''`)}'`;
4947
+ return ` --credentials-dir ${quoted}`;
4948
+ }
4949
+ function repairCommandFor(runtime, suffix = "", credentialsDir) {
4950
+ const dirFlag = credentialsDirFlagFor(credentialsDir);
4951
+ if (normalizeRuntimeName(runtime)) return `Run: ${RERUN} --doctor --repair --runtime ${runtime}${dirFlag}${suffix}`;
4952
+ return `Run: ${RERUN} --doctor --repair --runtime <runtime>${dirFlag}${suffix.replace(/\.$/, "")} \u2014 --repair needs the runtime named; one of: ${RUNTIME_FLAG_VALUE_LIST.join(", ")}.`;
4939
4953
  }
4940
4954
  async function resolveDoctorRuntime(input, directory) {
4941
4955
  const explicit = input.runtime.trim();
@@ -4954,7 +4968,7 @@ async function discoverCredentialDirectory(homeDir, explicit) {
4954
4968
  try {
4955
4969
  entries = await promises.readdir(root);
4956
4970
  } catch {
4957
- return explicit ? { directory: explicit, others: [], parkedOnly: /* @__PURE__ */ new Set() } : { others: [], parkedOnly: /* @__PURE__ */ new Set() };
4971
+ return explicit ? { directory: explicit, others: [], parkedOnly: /* @__PURE__ */ new Set(), identityDirectories: [explicit] } : { others: [], parkedOnly: /* @__PURE__ */ new Set(), identityDirectories: [] };
4958
4972
  }
4959
4973
  const candidates = [];
4960
4974
  const tombstonedOnly = [];
@@ -4979,20 +4993,23 @@ async function discoverCredentialDirectory(homeDir, explicit) {
4979
4993
  }
4980
4994
  const parkedOnlySet = new Set(parkedOnly);
4981
4995
  candidates.sort((a, b) => b.mtimeMs - a.mtimeMs);
4996
+ const identityDirectories = candidates.map((c) => c.directory);
4982
4997
  if (explicit) {
4983
4998
  return {
4984
4999
  directory: explicit,
4985
5000
  others: [...candidates.map((c) => c.directory), ...tombstonedOnly, ...parkedOnly].filter((d) => d !== explicit),
4986
- parkedOnly: parkedOnlySet
5001
+ parkedOnly: parkedOnlySet,
5002
+ identityDirectories: [explicit, ...identityDirectories.filter((d) => d !== explicit)]
4987
5003
  };
4988
5004
  }
4989
5005
  if (candidates.length === 0 && tombstonedOnly.length === 0 && parkedOnly.length === 0) {
4990
- return { others: [], parkedOnly: parkedOnlySet };
5006
+ return { others: [], parkedOnly: parkedOnlySet, identityDirectories };
4991
5007
  }
4992
5008
  return {
4993
5009
  directory: candidates[0]?.directory,
4994
5010
  others: [...candidates.slice(1).map((c) => c.directory), ...tombstonedOnly, ...parkedOnly],
4995
- parkedOnly: parkedOnlySet
5011
+ parkedOnly: parkedOnlySet,
5012
+ identityDirectories
4996
5013
  };
4997
5014
  }
4998
5015
  function agentIsWired(configText, names, slug, identity, sidecar, isPrimary, bareOwnerExists) {
@@ -5051,7 +5068,7 @@ function rekeyPendingVerdict(status, hostedDelegateAddress, runtime, slug) {
5051
5068
  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
5052
5069
  };
5053
5070
  }
5054
- async function runtimeSpecOverrideCheck(directory, sidecar, env) {
5071
+ async function runtimeSpecOverrideCheck(directory, sidecar, env, repairDir) {
5055
5072
  const facts = [];
5056
5073
  const installed = sidecar?.runtime_spec_override;
5057
5074
  if (installed) {
@@ -5080,7 +5097,7 @@ async function runtimeSpecOverrideCheck(directory, sidecar, env) {
5080
5097
  label: "Runtime spec override",
5081
5098
  level: "failed",
5082
5099
  detail: `runtime spec overridden \u2014 not the pinned manifest (${exports.MCP_RUNTIME_MANIFEST.signerPackage}@${exports.MCP_RUNTIME_MANIFEST.signerVersion}, ${exports.MCP_RUNTIME_MANIFEST.sdkPackage}@${exports.MCP_RUNTIME_MANIFEST.sdkVersion}). ${facts.join(". ")}.`,
5083
- 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.`
5100
+ 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.`
5084
5101
  });
5085
5102
  }
5086
5103
  async function readMcpSidecarOverride(directory) {
@@ -5139,7 +5156,7 @@ async function verdictsForAgent(entry, input, deps) {
5139
5156
  label: "Signer runtime (preinstalled wrapper)",
5140
5157
  level: "failed",
5141
5158
  detail: "No signer-runtime.json sidecar \u2014 the pinned signer runtime was never prepared (or a pre-#1586 npx config).",
5142
- repair: repairCommandFor(input.runtime)
5159
+ repair: repairCommandFor(input.runtime, "", input.repairDir)
5143
5160
  });
5144
5161
  } else if (sidecar.runtime_spec_override) {
5145
5162
  const matches = await installedRuntimeMatchesVersions(sidecar.runtime_directory, sidecar.cli_path, {
@@ -5151,7 +5168,7 @@ async function verdictsForAgent(entry, input, deps) {
5151
5168
  label: "Signer runtime (preinstalled wrapper)",
5152
5169
  level: matches ? "ok" : "failed",
5153
5170
  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.`,
5154
- ...matches ? {} : { repair: repairCommandFor(input.runtime, " with the same HAVEN_*_SPEC variables set.") }
5171
+ ...matches ? {} : { repair: repairCommandFor(input.runtime, " with the same HAVEN_*_SPEC variables set.", input.repairDir) }
5155
5172
  });
5156
5173
  } else {
5157
5174
  const intact = await installedRuntimeMatchesVersions(sidecar.runtime_directory, sidecar.cli_path, {
@@ -5165,10 +5182,10 @@ async function verdictsForAgent(entry, input, deps) {
5165
5182
  label: "Signer runtime (preinstalled wrapper)",
5166
5183
  level: ok ? "ok" : intact ? "advisory" : "failed",
5167
5184
  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 ${exports.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.`,
5168
- ...ok ? {} : { repair: repairCommandFor(input.runtime) }
5185
+ ...ok ? {} : { repair: repairCommandFor(input.runtime, "", input.repairDir) }
5169
5186
  });
5170
5187
  }
5171
- const overrideCheck = await runtimeSpecOverrideCheck(directory, sidecar, deps.env ?? process.env);
5188
+ const overrideCheck = await runtimeSpecOverrideCheck(directory, sidecar, deps.env ?? process.env, input.repairDir);
5172
5189
  if (overrideCheck) checks.push(overrideCheck);
5173
5190
  const hostedUrl = identity?.hosted_mcp_url ?? (identity?.api_url ? `${identity.api_url}/mcp` : void 0);
5174
5191
  if (identity?.api_key && hostedUrl) {
@@ -5258,7 +5275,7 @@ async function verdictsForAgent(entry, input, deps) {
5258
5275
  label: "Signer stdio handshake",
5259
5276
  level: probe.status === "ok" ? "ok" : "failed",
5260
5277
  detail: probe.status === "ok" ? `Signer started, listed ${probe.toolNames?.length ?? 0} tools${probe.serverInfo?.version ? ` (v${probe.serverInfo.version})` : ""}.${compatDetail}` : `Handshake failed: ${probe.status}.`,
5261
- ...probe.status === "ok" ? {} : { repair: repairCommandFor(input.runtime) }
5278
+ ...probe.status === "ok" ? {} : { repair: repairCommandFor(input.runtime, "", input.repairDir) }
5262
5279
  });
5263
5280
  }
5264
5281
  } else {
@@ -5267,7 +5284,7 @@ async function verdictsForAgent(entry, input, deps) {
5267
5284
  label: "Signer stdio handshake",
5268
5285
  level: "failed",
5269
5286
  detail: "Skipped \u2014 no prepared signer runtime to probe.",
5270
- repair: repairCommandFor(input.runtime)
5287
+ repair: repairCommandFor(input.runtime, "", input.repairDir)
5271
5288
  });
5272
5289
  }
5273
5290
  return { checks, ...signerCapabilities ? { signerCapabilities } : {} };
@@ -5276,7 +5293,8 @@ async function runDoctor(input, deps = {}) {
5276
5293
  const homeDir = deps.homeDir ?? os.homedir();
5277
5294
  const checks = [];
5278
5295
  let signerCapabilities;
5279
- const { directory, others, parkedOnly } = await discoverCredentialDirectory(homeDir, input.credentialsDir);
5296
+ const { directory, others, parkedOnly, identityDirectories } = await discoverCredentialDirectory(homeDir, input.credentialsDir);
5297
+ const nameRepairDir = identityDirectories.length > 1 || Boolean(input.credentialsDir);
5280
5298
  const resolution = await resolveDoctorRuntime(input, directory);
5281
5299
  const runtime = resolution.runtime;
5282
5300
  const input2 = { ...input, runtime };
@@ -5333,7 +5351,11 @@ async function runDoctor(input, deps = {}) {
5333
5351
  ...rekeyPending ? { rekeyPending } : {}
5334
5352
  };
5335
5353
  if (wired) {
5336
- const result = await checksForAgent({ directory: dir, identity, sidecar }, input2, deps);
5354
+ const result = await checksForAgent(
5355
+ { directory: dir, identity, sidecar },
5356
+ { ...input2, ...nameRepairDir ? { repairDir: dir } : {} },
5357
+ deps
5358
+ );
5337
5359
  entry.checks = result.checks;
5338
5360
  capabilitiesByDirectory.set(dir, result.signerCapabilities);
5339
5361
  } else if (rekeyPending) {
@@ -5362,7 +5384,7 @@ async function runDoctor(input, deps = {}) {
5362
5384
  if (!primaryChecksById.has("credentials")) {
5363
5385
  const result = await checksForAgent(
5364
5386
  { directory: primaryDirectory, identity: primaryIdentity, sidecar: primarySidecar },
5365
- input2,
5387
+ { ...input2, ...nameRepairDir ? { repairDir: primaryDirectory } : {} },
5366
5388
  deps
5367
5389
  );
5368
5390
  signerCapabilities = result.signerCapabilities;
@@ -5403,7 +5425,7 @@ async function runDoctor(input, deps = {}) {
5403
5425
  label: "Runtime MCP config",
5404
5426
  level: "failed",
5405
5427
  detail: `No runtime config at ${configPath}.`,
5406
- repair: repairCommandFor(input2.runtime)
5428
+ repair: repairCommandFor(input2.runtime, "", nameRepairDir ? primaryDirectory : void 0)
5407
5429
  });
5408
5430
  } else {
5409
5431
  const primaryIdentity = await readIdentity(primaryDirectory ?? "");
@@ -5417,7 +5439,7 @@ async function runDoctor(input, deps = {}) {
5417
5439
  label: "Runtime MCP config",
5418
5440
  level: ok ? "ok" : "failed",
5419
5441
  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)" : ""}.`,
5420
- ...ok ? {} : { repair: repairCommandFor(input2.runtime) }
5442
+ ...ok ? {} : { repair: repairCommandFor(input2.runtime, "", nameRepairDir ? primaryDirectory : void 0) }
5421
5443
  });
5422
5444
  }
5423
5445
  for (const id of ["hosted_mcp", "identity_match", "rekey_pending"]) {
@@ -5560,7 +5582,25 @@ async function runDoctor(input, deps = {}) {
5560
5582
  async function runRepair(input, deps = {}) {
5561
5583
  const homeDir = deps.homeDir ?? os.homedir();
5562
5584
  const messages = [];
5563
- const { directory, others } = await discoverCredentialDirectory(homeDir, input.credentialsDir);
5585
+ const { directory, others, identityDirectories } = await discoverCredentialDirectory(homeDir, input.credentialsDir);
5586
+ if (!input.credentialsDir && identityDirectories.length > 1) {
5587
+ const lines = await Promise.all(
5588
+ identityDirectories.map(async (dir) => {
5589
+ const identity2 = await readIdentity(dir);
5590
+ const who = identity2?.agent_id ? `agent ${identity2.agent_id}` : "agent (no agent_id recorded)";
5591
+ const retired = !identity2?.api_key || await pathExists3(path.join(dir, TOMBSTONE_FILENAME));
5592
+ return retired ? ` \u2022 ${who} (${dir}): retired \u2014 not a repair target` : ` \u2022 ${who}: ${repairCommandFor(input.runtime, "", dir).replace(/^Run: /, "")}`;
5593
+ })
5594
+ );
5595
+ return {
5596
+ ok: false,
5597
+ messages: [
5598
+ `${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:`,
5599
+ ...lines,
5600
+ "Not sure which agent is which? Compare the agent ids against the Haven agent page, or run --doctor first."
5601
+ ]
5602
+ };
5603
+ }
5564
5604
  if (others.length > 0) {
5565
5605
  messages.push(`Note: ${others.length} other agent credential dir(s) exist \u2014 run --doctor for their status.`);
5566
5606
  }