@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.d.cts CHANGED
@@ -743,7 +743,7 @@ type WiringCollisionResolution = {
743
743
  action: 'abort';
744
744
  };
745
745
 
746
- declare const CONNECTOR_VERSION = "0.6.0-alpha.0";
746
+ declare const CONNECTOR_VERSION = "0.7.0-alpha.0";
747
747
  interface ConnectOptions {
748
748
  setupToken: string;
749
749
  apiBaseUrl: string;
@@ -1194,11 +1194,11 @@ declare function isConnectError(err: unknown): err is ConnectError;
1194
1194
 
1195
1195
  declare const MCP_RUNTIME_MANIFEST: {
1196
1196
  readonly mcpPackage: "@haven_ai/mcp";
1197
- readonly mcpVersion: "0.6.0-alpha.0";
1197
+ readonly mcpVersion: "0.7.0-alpha.0";
1198
1198
  readonly sdkPackage: "@haven_ai/sdk";
1199
- readonly sdkVersion: "0.6.0-alpha.0";
1199
+ readonly sdkVersion: "0.7.0-alpha.0";
1200
1200
  readonly signerPackage: "@haven_ai/signer";
1201
- readonly signerVersion: "0.6.0-alpha.0";
1201
+ readonly signerVersion: "0.7.0-alpha.0";
1202
1202
  readonly minimumNodeVersion: "22.0.0";
1203
1203
  readonly supportedClients: readonly ["codex-cli", "codex-desktop", "claude-code"];
1204
1204
  readonly requiredTools: readonly string[];
package/dist/index.d.ts CHANGED
@@ -743,7 +743,7 @@ type WiringCollisionResolution = {
743
743
  action: 'abort';
744
744
  };
745
745
 
746
- declare const CONNECTOR_VERSION = "0.6.0-alpha.0";
746
+ declare const CONNECTOR_VERSION = "0.7.0-alpha.0";
747
747
  interface ConnectOptions {
748
748
  setupToken: string;
749
749
  apiBaseUrl: string;
@@ -1194,11 +1194,11 @@ declare function isConnectError(err: unknown): err is ConnectError;
1194
1194
 
1195
1195
  declare const MCP_RUNTIME_MANIFEST: {
1196
1196
  readonly mcpPackage: "@haven_ai/mcp";
1197
- readonly mcpVersion: "0.6.0-alpha.0";
1197
+ readonly mcpVersion: "0.7.0-alpha.0";
1198
1198
  readonly sdkPackage: "@haven_ai/sdk";
1199
- readonly sdkVersion: "0.6.0-alpha.0";
1199
+ readonly sdkVersion: "0.7.0-alpha.0";
1200
1200
  readonly signerPackage: "@haven_ai/signer";
1201
- readonly signerVersion: "0.6.0-alpha.0";
1201
+ readonly signerVersion: "0.7.0-alpha.0";
1202
1202
  readonly minimumNodeVersion: "22.0.0";
1203
1203
  readonly supportedClients: readonly ["codex-cli", "codex-desktop", "claude-code"];
1204
1204
  readonly requiredTools: readonly string[];
package/dist/index.js CHANGED
@@ -612,9 +612,9 @@ var init_runtime_manifest = __esm({
612
612
  mcpPackage: "@haven_ai/mcp",
613
613
  mcpVersion: MCP_VERSION,
614
614
  sdkPackage: "@haven_ai/sdk",
615
- sdkVersion: "0.6.0-alpha.0",
615
+ sdkVersion: "0.7.0-alpha.0",
616
616
  signerPackage: "@haven_ai/signer",
617
- signerVersion: "0.6.0-alpha.0",
617
+ signerVersion: "0.7.0-alpha.0",
618
618
  // Sourced from the SDK, never a literal (#1161). This field read '20.0.0'
619
619
  // while every package's `engines` said `>=24` and the docs said `>=24.0.0`,
620
620
  // so the guard that was supposed to enforce the floor waved Node v23 through
@@ -4443,7 +4443,7 @@ var init_runtime = __esm({
4443
4443
  init_tombstone();
4444
4444
  init_local_mcp_runtime();
4445
4445
  init_runtime_manifest();
4446
- CONNECTOR_VERSION = "0.6.0-alpha.0";
4446
+ CONNECTOR_VERSION = "0.7.0-alpha.0";
4447
4447
  CONNECTOR_CLIENT_IDENTITY = `@haven_ai/connect/${CONNECTOR_VERSION}`;
4448
4448
  CONNECT_OUTCOME_SCHEMA_VERSION = 1;
4449
4449
  failureOutcomesByError = /* @__PURE__ */ new WeakMap();
@@ -4926,9 +4926,23 @@ function rollUpLevel(checks) {
4926
4926
  function runtimeFlagFor(runtime) {
4927
4927
  return normalizeRuntimeName(runtime) ? ` --runtime ${runtime}` : "";
4928
4928
  }
4929
- function repairCommandFor(runtime, suffix = "") {
4930
- if (normalizeRuntimeName(runtime)) return `Run: ${RERUN} --doctor --repair --runtime ${runtime}${suffix}`;
4931
- return `Run: ${RERUN} --doctor --repair --runtime <runtime>${suffix.replace(/\.$/, "")} \u2014 --repair needs the runtime named; one of: ${RUNTIME_FLAG_VALUE_LIST.join(", ")}.`;
4929
+ async function pathExists3(path) {
4930
+ try {
4931
+ await stat(path);
4932
+ return true;
4933
+ } catch {
4934
+ return false;
4935
+ }
4936
+ }
4937
+ function credentialsDirFlagFor(credentialsDir) {
4938
+ if (!credentialsDir) return "";
4939
+ const quoted = /^[\w@%+=:,./~-]+$/.test(credentialsDir) ? credentialsDir : `'${credentialsDir.replace(/'/g, `'\\''`)}'`;
4940
+ return ` --credentials-dir ${quoted}`;
4941
+ }
4942
+ function repairCommandFor(runtime, suffix = "", credentialsDir) {
4943
+ const dirFlag = credentialsDirFlagFor(credentialsDir);
4944
+ if (normalizeRuntimeName(runtime)) return `Run: ${RERUN} --doctor --repair --runtime ${runtime}${dirFlag}${suffix}`;
4945
+ return `Run: ${RERUN} --doctor --repair --runtime <runtime>${dirFlag}${suffix.replace(/\.$/, "")} \u2014 --repair needs the runtime named; one of: ${RUNTIME_FLAG_VALUE_LIST.join(", ")}.`;
4932
4946
  }
4933
4947
  async function resolveDoctorRuntime(input, directory) {
4934
4948
  const explicit = input.runtime.trim();
@@ -4947,7 +4961,7 @@ async function discoverCredentialDirectory(homeDir, explicit) {
4947
4961
  try {
4948
4962
  entries = await readdir(root);
4949
4963
  } catch {
4950
- return explicit ? { directory: explicit, others: [], parkedOnly: /* @__PURE__ */ new Set() } : { others: [], parkedOnly: /* @__PURE__ */ new Set() };
4964
+ return explicit ? { directory: explicit, others: [], parkedOnly: /* @__PURE__ */ new Set(), identityDirectories: [explicit] } : { others: [], parkedOnly: /* @__PURE__ */ new Set(), identityDirectories: [] };
4951
4965
  }
4952
4966
  const candidates = [];
4953
4967
  const tombstonedOnly = [];
@@ -4972,20 +4986,23 @@ async function discoverCredentialDirectory(homeDir, explicit) {
4972
4986
  }
4973
4987
  const parkedOnlySet = new Set(parkedOnly);
4974
4988
  candidates.sort((a, b) => b.mtimeMs - a.mtimeMs);
4989
+ const identityDirectories = candidates.map((c) => c.directory);
4975
4990
  if (explicit) {
4976
4991
  return {
4977
4992
  directory: explicit,
4978
4993
  others: [...candidates.map((c) => c.directory), ...tombstonedOnly, ...parkedOnly].filter((d) => d !== explicit),
4979
- parkedOnly: parkedOnlySet
4994
+ parkedOnly: parkedOnlySet,
4995
+ identityDirectories: [explicit, ...identityDirectories.filter((d) => d !== explicit)]
4980
4996
  };
4981
4997
  }
4982
4998
  if (candidates.length === 0 && tombstonedOnly.length === 0 && parkedOnly.length === 0) {
4983
- return { others: [], parkedOnly: parkedOnlySet };
4999
+ return { others: [], parkedOnly: parkedOnlySet, identityDirectories };
4984
5000
  }
4985
5001
  return {
4986
5002
  directory: candidates[0]?.directory,
4987
5003
  others: [...candidates.slice(1).map((c) => c.directory), ...tombstonedOnly, ...parkedOnly],
4988
- parkedOnly: parkedOnlySet
5004
+ parkedOnly: parkedOnlySet,
5005
+ identityDirectories
4989
5006
  };
4990
5007
  }
4991
5008
  function agentIsWired(configText, names, slug, identity, sidecar, isPrimary, bareOwnerExists) {
@@ -5044,7 +5061,7 @@ function rekeyPendingVerdict(status, hostedDelegateAddress, runtime, slug) {
5044
5061
  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
5045
5062
  };
5046
5063
  }
5047
- async function runtimeSpecOverrideCheck(directory, sidecar, env) {
5064
+ async function runtimeSpecOverrideCheck(directory, sidecar, env, repairDir) {
5048
5065
  const facts = [];
5049
5066
  const installed = sidecar?.runtime_spec_override;
5050
5067
  if (installed) {
@@ -5073,7 +5090,7 @@ async function runtimeSpecOverrideCheck(directory, sidecar, env) {
5073
5090
  label: "Runtime spec override",
5074
5091
  level: "failed",
5075
5092
  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(". ")}.`,
5076
- 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.`
5093
+ 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.`
5077
5094
  });
5078
5095
  }
5079
5096
  async function readMcpSidecarOverride(directory) {
@@ -5132,7 +5149,7 @@ async function verdictsForAgent(entry, input, deps) {
5132
5149
  label: "Signer runtime (preinstalled wrapper)",
5133
5150
  level: "failed",
5134
5151
  detail: "No signer-runtime.json sidecar \u2014 the pinned signer runtime was never prepared (or a pre-#1586 npx config).",
5135
- repair: repairCommandFor(input.runtime)
5152
+ repair: repairCommandFor(input.runtime, "", input.repairDir)
5136
5153
  });
5137
5154
  } else if (sidecar.runtime_spec_override) {
5138
5155
  const matches = await installedRuntimeMatchesVersions(sidecar.runtime_directory, sidecar.cli_path, {
@@ -5144,7 +5161,7 @@ async function verdictsForAgent(entry, input, deps) {
5144
5161
  label: "Signer runtime (preinstalled wrapper)",
5145
5162
  level: matches ? "ok" : "failed",
5146
5163
  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.`,
5147
- ...matches ? {} : { repair: repairCommandFor(input.runtime, " with the same HAVEN_*_SPEC variables set.") }
5164
+ ...matches ? {} : { repair: repairCommandFor(input.runtime, " with the same HAVEN_*_SPEC variables set.", input.repairDir) }
5148
5165
  });
5149
5166
  } else {
5150
5167
  const intact = await installedRuntimeMatchesVersions(sidecar.runtime_directory, sidecar.cli_path, {
@@ -5158,10 +5175,10 @@ async function verdictsForAgent(entry, input, deps) {
5158
5175
  label: "Signer runtime (preinstalled wrapper)",
5159
5176
  level: ok ? "ok" : intact ? "advisory" : "failed",
5160
5177
  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.`,
5161
- ...ok ? {} : { repair: repairCommandFor(input.runtime) }
5178
+ ...ok ? {} : { repair: repairCommandFor(input.runtime, "", input.repairDir) }
5162
5179
  });
5163
5180
  }
5164
- const overrideCheck = await runtimeSpecOverrideCheck(directory, sidecar, deps.env ?? process.env);
5181
+ const overrideCheck = await runtimeSpecOverrideCheck(directory, sidecar, deps.env ?? process.env, input.repairDir);
5165
5182
  if (overrideCheck) checks.push(overrideCheck);
5166
5183
  const hostedUrl = identity?.hosted_mcp_url ?? (identity?.api_url ? `${identity.api_url}/mcp` : void 0);
5167
5184
  if (identity?.api_key && hostedUrl) {
@@ -5251,7 +5268,7 @@ async function verdictsForAgent(entry, input, deps) {
5251
5268
  label: "Signer stdio handshake",
5252
5269
  level: probe.status === "ok" ? "ok" : "failed",
5253
5270
  detail: probe.status === "ok" ? `Signer started, listed ${probe.toolNames?.length ?? 0} tools${probe.serverInfo?.version ? ` (v${probe.serverInfo.version})` : ""}.${compatDetail}` : `Handshake failed: ${probe.status}.`,
5254
- ...probe.status === "ok" ? {} : { repair: repairCommandFor(input.runtime) }
5271
+ ...probe.status === "ok" ? {} : { repair: repairCommandFor(input.runtime, "", input.repairDir) }
5255
5272
  });
5256
5273
  }
5257
5274
  } else {
@@ -5260,7 +5277,7 @@ async function verdictsForAgent(entry, input, deps) {
5260
5277
  label: "Signer stdio handshake",
5261
5278
  level: "failed",
5262
5279
  detail: "Skipped \u2014 no prepared signer runtime to probe.",
5263
- repair: repairCommandFor(input.runtime)
5280
+ repair: repairCommandFor(input.runtime, "", input.repairDir)
5264
5281
  });
5265
5282
  }
5266
5283
  return { checks, ...signerCapabilities ? { signerCapabilities } : {} };
@@ -5269,7 +5286,8 @@ async function runDoctor(input, deps = {}) {
5269
5286
  const homeDir = deps.homeDir ?? homedir();
5270
5287
  const checks = [];
5271
5288
  let signerCapabilities;
5272
- const { directory, others, parkedOnly } = await discoverCredentialDirectory(homeDir, input.credentialsDir);
5289
+ const { directory, others, parkedOnly, identityDirectories } = await discoverCredentialDirectory(homeDir, input.credentialsDir);
5290
+ const nameRepairDir = identityDirectories.length > 1 || Boolean(input.credentialsDir);
5273
5291
  const resolution = await resolveDoctorRuntime(input, directory);
5274
5292
  const runtime = resolution.runtime;
5275
5293
  const input2 = { ...input, runtime };
@@ -5326,7 +5344,11 @@ async function runDoctor(input, deps = {}) {
5326
5344
  ...rekeyPending ? { rekeyPending } : {}
5327
5345
  };
5328
5346
  if (wired) {
5329
- const result = await checksForAgent({ directory: dir, identity, sidecar }, input2, deps);
5347
+ const result = await checksForAgent(
5348
+ { directory: dir, identity, sidecar },
5349
+ { ...input2, ...nameRepairDir ? { repairDir: dir } : {} },
5350
+ deps
5351
+ );
5330
5352
  entry.checks = result.checks;
5331
5353
  capabilitiesByDirectory.set(dir, result.signerCapabilities);
5332
5354
  } else if (rekeyPending) {
@@ -5355,7 +5377,7 @@ async function runDoctor(input, deps = {}) {
5355
5377
  if (!primaryChecksById.has("credentials")) {
5356
5378
  const result = await checksForAgent(
5357
5379
  { directory: primaryDirectory, identity: primaryIdentity, sidecar: primarySidecar },
5358
- input2,
5380
+ { ...input2, ...nameRepairDir ? { repairDir: primaryDirectory } : {} },
5359
5381
  deps
5360
5382
  );
5361
5383
  signerCapabilities = result.signerCapabilities;
@@ -5396,7 +5418,7 @@ async function runDoctor(input, deps = {}) {
5396
5418
  label: "Runtime MCP config",
5397
5419
  level: "failed",
5398
5420
  detail: `No runtime config at ${configPath}.`,
5399
- repair: repairCommandFor(input2.runtime)
5421
+ repair: repairCommandFor(input2.runtime, "", nameRepairDir ? primaryDirectory : void 0)
5400
5422
  });
5401
5423
  } else {
5402
5424
  const primaryIdentity = await readIdentity(primaryDirectory ?? "");
@@ -5410,7 +5432,7 @@ async function runDoctor(input, deps = {}) {
5410
5432
  label: "Runtime MCP config",
5411
5433
  level: ok ? "ok" : "failed",
5412
5434
  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)" : ""}.`,
5413
- ...ok ? {} : { repair: repairCommandFor(input2.runtime) }
5435
+ ...ok ? {} : { repair: repairCommandFor(input2.runtime, "", nameRepairDir ? primaryDirectory : void 0) }
5414
5436
  });
5415
5437
  }
5416
5438
  for (const id of ["hosted_mcp", "identity_match", "rekey_pending"]) {
@@ -5553,7 +5575,25 @@ async function runDoctor(input, deps = {}) {
5553
5575
  async function runRepair(input, deps = {}) {
5554
5576
  const homeDir = deps.homeDir ?? homedir();
5555
5577
  const messages = [];
5556
- const { directory, others } = await discoverCredentialDirectory(homeDir, input.credentialsDir);
5578
+ const { directory, others, identityDirectories } = await discoverCredentialDirectory(homeDir, input.credentialsDir);
5579
+ if (!input.credentialsDir && identityDirectories.length > 1) {
5580
+ const lines = await Promise.all(
5581
+ identityDirectories.map(async (dir) => {
5582
+ const identity2 = await readIdentity(dir);
5583
+ const who = identity2?.agent_id ? `agent ${identity2.agent_id}` : "agent (no agent_id recorded)";
5584
+ const retired = !identity2?.api_key || await pathExists3(join(dir, TOMBSTONE_FILENAME));
5585
+ return retired ? ` \u2022 ${who} (${dir}): retired \u2014 not a repair target` : ` \u2022 ${who}: ${repairCommandFor(input.runtime, "", dir).replace(/^Run: /, "")}`;
5586
+ })
5587
+ );
5588
+ return {
5589
+ ok: false,
5590
+ messages: [
5591
+ `${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:`,
5592
+ ...lines,
5593
+ "Not sure which agent is which? Compare the agent ids against the Haven agent page, or run --doctor first."
5594
+ ]
5595
+ };
5596
+ }
5557
5597
  if (others.length > 0) {
5558
5598
  messages.push(`Note: ${others.length} other agent credential dir(s) exist \u2014 run --doctor for their status.`);
5559
5599
  }