@haven_ai/connect 0.0.0-dev.202609181713.990ba70 → 0.0.0-dev.202609181807.87b28b1

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.cjs CHANGED
@@ -533,6 +533,16 @@ async function writeConnectOutcomeRecord(directory, outcome, warn) {
533
533
  await restrictPermissions(path$1, 384, warn);
534
534
  return path$1;
535
535
  }
536
+ async function readConnectOutcomeRuntime(directory) {
537
+ try {
538
+ const parsed = JSON.parse(await promises.readFile(path.join(directory, CONNECT_OUTCOME_FILENAME), "utf8"));
539
+ if (typeof parsed !== "object" || parsed === null) return null;
540
+ const runtime = parsed.runtime;
541
+ return typeof runtime === "string" && runtime.length > 0 ? runtime : null;
542
+ } catch {
543
+ return null;
544
+ }
545
+ }
536
546
  var REKEY_PENDING_FILENAME, REKEY_PENDING_TTL_MS, CONNECT_OUTCOME_FILENAME;
537
547
  var init_storage = __esm({
538
548
  "src/storage.ts"() {
@@ -557,9 +567,9 @@ var init_runtime_manifest = __esm({
557
567
  mcpPackage: "@haven_ai/mcp",
558
568
  mcpVersion: mcp.MCP_VERSION,
559
569
  sdkPackage: "@haven_ai/sdk",
560
- sdkVersion: "0.0.0-dev.202609181713.990ba70",
570
+ sdkVersion: "0.0.0-dev.202609181807.87b28b1",
561
571
  signerPackage: "@haven_ai/signer",
562
- signerVersion: "0.0.0-dev.202609181713.990ba70",
572
+ signerVersion: "0.0.0-dev.202609181807.87b28b1",
563
573
  // Sourced from the SDK, never a literal (#1161). This field read '20.0.0'
564
574
  // while every package's `engines` said `>=24` and the docs said `>=24.0.0`,
565
575
  // so the guard that was supposed to enforce the floor waved Node v23 through
@@ -3520,6 +3530,20 @@ __export(doctor_exports, {
3520
3530
  runDoctor: () => runDoctor,
3521
3531
  runRepair: () => runRepair
3522
3532
  });
3533
+ function runtimeFlagFor(runtime) {
3534
+ return normalizeRuntimeName(runtime) ? ` --runtime ${runtime}` : "";
3535
+ }
3536
+ async function resolveDoctorRuntime(input, directory) {
3537
+ const explicit = input.runtime.trim();
3538
+ if (explicit) return { runtime: explicit, origin: "flag" };
3539
+ if (directory) {
3540
+ const recorded = await readConnectOutcomeRuntime(directory);
3541
+ if (recorded !== null && normalizeRuntimeName(recorded)) {
3542
+ return { runtime: recorded, origin: "record" };
3543
+ }
3544
+ }
3545
+ return { runtime: "", origin: "unknown" };
3546
+ }
3523
3547
  async function discoverCredentialDirectory(homeDir, explicit) {
3524
3548
  const root = explicit ? path.dirname(explicit) : path.join(homeDir, ".haven", "agents");
3525
3549
  let entries = [];
@@ -3600,7 +3624,7 @@ function rekeyPendingCheck(status, hostedDelegateAddress, runtime, slug) {
3600
3624
  label,
3601
3625
  ok: false,
3602
3626
  detail: `A re-key started ${started} has COMPLETED on Haven \u2014 the agent's signing address is already ${address}, the one this machine generated \u2014 but the local half was never finished, so the credential files here still hold the old key. Parked at ${status.path}.` + (status.state === "expired" ? " The local file is also past its 24h TTL, which --rekey-finish refuses, so the finish command below will not accept it any more." : ""),
3603
- repair: status.state === "expired" ? `The parked key expired. Start again \u2014 ${RERUN} --rekey${nameFlag} \u2014 and re-run "Replace signing key" on the Haven agent page with the new address it prints.` : `Run: ${RERUN} --rekey-finish${nameFlag} --api-key <the key the agent page showed you> --runtime ${runtime}`
3627
+ repair: status.state === "expired" ? `The parked key expired. Start again \u2014 ${RERUN} --rekey${nameFlag} \u2014 and re-run "Replace signing key" on the Haven agent page with the new address it prints.` : `Run: ${RERUN} --rekey-finish${nameFlag} --api-key <the key the agent page showed you>${runtimeFlagFor(runtime)}`
3604
3628
  };
3605
3629
  }
3606
3630
  const wedgeNote = "Haven is NOT yet on this address, so the re-key did not complete. This machine cannot tell whether the on-chain revoke on the agent page already ran: if it did not, closing this costs nothing; if it did, the agent's old delegations are revoked, no new ones were issued, and only an owner re-grant restores its spend authority (#1868). Check the agent page before assuming the harmless case.";
@@ -3701,7 +3725,7 @@ async function checksForAgent(entry, input, deps) {
3701
3725
  label: "Signer runtime (preinstalled wrapper)",
3702
3726
  ok: false,
3703
3727
  detail: "No signer-runtime.json sidecar \u2014 the pinned signer runtime was never prepared (or a pre-#1586 npx config).",
3704
- repair: `Run: ${RERUN} --doctor --repair --runtime ${input.runtime}`
3728
+ repair: `Run: ${RERUN} --doctor --repair${runtimeFlagFor(input.runtime)}`
3705
3729
  });
3706
3730
  } else if (sidecar.runtime_spec_override) {
3707
3731
  const matches = await installedRuntimeMatchesVersions(sidecar.runtime_directory, sidecar.cli_path, {
@@ -3713,7 +3737,7 @@ async function checksForAgent(entry, input, deps) {
3713
3737
  label: "Signer runtime (preinstalled wrapper)",
3714
3738
  ok: matches,
3715
3739
  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.`,
3716
- ...matches ? {} : { repair: `Run: ${RERUN} --doctor --repair --runtime ${input.runtime} with the same HAVEN_*_SPEC variables set.` }
3740
+ ...matches ? {} : { repair: `Run: ${RERUN} --doctor --repair${runtimeFlagFor(input.runtime)} with the same HAVEN_*_SPEC variables set.` }
3717
3741
  });
3718
3742
  } else {
3719
3743
  const intact = await installedRuntimeMatchesVersions(sidecar.runtime_directory, sidecar.cli_path, {
@@ -3727,7 +3751,7 @@ async function checksForAgent(entry, input, deps) {
3727
3751
  label: "Signer runtime (preinstalled wrapper)",
3728
3752
  ok,
3729
3753
  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.`,
3730
- ...ok ? {} : { repair: `Run: ${RERUN} --doctor --repair --runtime ${input.runtime}` }
3754
+ ...ok ? {} : { repair: `Run: ${RERUN} --doctor --repair${runtimeFlagFor(input.runtime)}` }
3731
3755
  });
3732
3756
  }
3733
3757
  const overrideCheck = await runtimeSpecOverrideCheck(directory, sidecar, deps.env ?? process.env);
@@ -3768,7 +3792,7 @@ async function checksForAgent(entry, input, deps) {
3768
3792
  label: "Hosted identity matches the local signing key",
3769
3793
  ok: false,
3770
3794
  detail: probe.status === "unauthorized" ? "The stored API key was rejected, so the agent it authenticates as cannot be compared with the local signing key." : `Could not read the hosted identity (${probe.status}) \u2014 the comparison did not happen, so it cannot be reported as a match.`,
3771
- repair: probe.status === "unauthorized" ? `Re-run the full setup with a fresh token: ${RERUN} --setup <token>.` : `Restore network access to the Haven API, then re-run: ${RERUN} --doctor --runtime ${input.runtime}`
3795
+ repair: probe.status === "unauthorized" ? `Re-run the full setup with a fresh token: ${RERUN} --setup <token>.` : `Restore network access to the Haven API, then re-run: ${RERUN} --doctor${runtimeFlagFor(input.runtime)}`
3772
3796
  });
3773
3797
  } else if (!localDelegate) {
3774
3798
  checks.push({
@@ -3820,7 +3844,7 @@ async function checksForAgent(entry, input, deps) {
3820
3844
  label: "Signer stdio handshake",
3821
3845
  ok: probe.status === "ok",
3822
3846
  detail: probe.status === "ok" ? `Signer started, listed ${probe.toolNames?.length ?? 0} tools${probe.serverInfo?.version ? ` (v${probe.serverInfo.version})` : ""}.${compatDetail}` : `Handshake failed: ${probe.status}.`,
3823
- ...probe.status === "ok" ? {} : { repair: `Run: ${RERUN} --doctor --repair --runtime ${input.runtime}` }
3847
+ ...probe.status === "ok" ? {} : { repair: `Run: ${RERUN} --doctor --repair${runtimeFlagFor(input.runtime)}` }
3824
3848
  });
3825
3849
  }
3826
3850
  } else {
@@ -3829,7 +3853,7 @@ async function checksForAgent(entry, input, deps) {
3829
3853
  label: "Signer stdio handshake",
3830
3854
  ok: false,
3831
3855
  detail: "Skipped \u2014 no prepared signer runtime to probe.",
3832
- repair: `Run: ${RERUN} --doctor --repair --runtime ${input.runtime}`
3856
+ repair: `Run: ${RERUN} --doctor --repair${runtimeFlagFor(input.runtime)}`
3833
3857
  });
3834
3858
  }
3835
3859
  return { checks, ...signerCapabilities ? { signerCapabilities } : {} };
@@ -3839,7 +3863,10 @@ async function runDoctor(input, deps = {}) {
3839
3863
  const checks = [];
3840
3864
  let signerCapabilities;
3841
3865
  const { directory, others, parkedOnly } = await discoverCredentialDirectory(homeDir, input.credentialsDir);
3842
- const configPath = runtimeConfigPathFor(input.runtime, homeDir);
3866
+ const resolution = await resolveDoctorRuntime(input, directory);
3867
+ const runtime = resolution.runtime;
3868
+ const input2 = { ...input, runtime };
3869
+ const configPath = runtimeConfigPathFor(input2.runtime, homeDir);
3843
3870
  let configText = null;
3844
3871
  if (configPath !== null) {
3845
3872
  try {
@@ -3876,7 +3903,7 @@ async function runDoctor(input, deps = {}) {
3876
3903
  // A tombstone is a deliberate record and outranks the discovery tell:
3877
3904
  // a retired directory that also holds a parked key stays `retired`.
3878
3905
  classification: tombstone ? "retired" : parkedOnly.has(dir) ? "parked" : "orphaned",
3879
- checks: rekeyPending ? [rekeyPendingCheck(rekeyPending, void 0, input.runtime, slug)] : [],
3906
+ checks: rekeyPending ? [rekeyPendingCheck(rekeyPending, void 0, input2.runtime, slug)] : [],
3880
3907
  ...rekeyPending ? { rekeyPending } : {}
3881
3908
  });
3882
3909
  continue;
@@ -3891,11 +3918,11 @@ async function runDoctor(input, deps = {}) {
3891
3918
  ...rekeyPending ? { rekeyPending } : {}
3892
3919
  };
3893
3920
  if (wired) {
3894
- const result = await checksForAgent({ directory: dir, identity, sidecar }, input, deps);
3921
+ const result = await checksForAgent({ directory: dir, identity, sidecar }, input2, deps);
3895
3922
  entry.checks = result.checks;
3896
3923
  capabilitiesByDirectory.set(dir, result.signerCapabilities);
3897
3924
  } else if (rekeyPending) {
3898
- entry.checks = [rekeyPendingCheck(rekeyPending, void 0, input.runtime, slug)];
3925
+ entry.checks = [rekeyPendingCheck(rekeyPending, void 0, input2.runtime, slug)];
3899
3926
  }
3900
3927
  inventory.push(entry);
3901
3928
  }
@@ -3920,7 +3947,7 @@ async function runDoctor(input, deps = {}) {
3920
3947
  if (!primaryChecksById.has("credentials")) {
3921
3948
  const result = await checksForAgent(
3922
3949
  { directory: primaryDirectory, identity: primaryIdentity, sidecar: primarySidecar },
3923
- input,
3950
+ input2,
3924
3951
  deps
3925
3952
  );
3926
3953
  signerCapabilities = result.signerCapabilities;
@@ -3931,12 +3958,20 @@ async function runDoctor(input, deps = {}) {
3931
3958
  if (check) checks.push(check);
3932
3959
  }
3933
3960
  }
3934
- if (configPath === null) {
3961
+ if (configPath === null && input2.runtime === "") {
3962
+ checks.push({
3963
+ id: "runtime_config",
3964
+ label: "Runtime MCP config",
3965
+ ok: false,
3966
+ detail: `Runtime is unknown \u2014 no runtime flag was given and the connector's record in ${primaryDirectory ?? input.credentialsDir ?? "~/.haven/agents"} carries no resolvable ${CONNECT_OUTCOME_FILENAME} runtime. The runtime config was NOT checked. Re-run the doctor naming the runtime \u2014 one of: ${RUNTIME_FLAG_VALUE_LIST.join(", ")}.`,
3967
+ repair: `Re-run the doctor naming the runtime \u2014 one of: ${RUNTIME_FLAG_VALUE_LIST.join(", ")}.`
3968
+ });
3969
+ } else if (configPath === null) {
3935
3970
  checks.push({
3936
3971
  id: "runtime_config",
3937
3972
  label: "Runtime MCP config",
3938
3973
  ok: true,
3939
- detail: `Runtime '${input.runtime}' has no file-based config the connector owns (CLI-managed) \u2014 skipping the file check.`
3974
+ detail: `Runtime '${input2.runtime}' is configured through its own CLI or by hand (${input2.runtime === "other" ? "manual runtime" : "CLI-managed"}) and has no file-based config the connector owns \u2014 skipping the file check.`
3940
3975
  });
3941
3976
  } else if (configText === null) {
3942
3977
  checks.push({
@@ -3944,7 +3979,7 @@ async function runDoctor(input, deps = {}) {
3944
3979
  label: "Runtime MCP config",
3945
3980
  ok: false,
3946
3981
  detail: `No runtime config at ${configPath}.`,
3947
- repair: `Run: ${RERUN} --doctor --repair --runtime ${input.runtime}`
3982
+ repair: `Run: ${RERUN} --doctor --repair${runtimeFlagFor(input2.runtime)}`
3948
3983
  });
3949
3984
  } else {
3950
3985
  const primaryIdentity = await readIdentity(primaryDirectory ?? "");
@@ -3958,7 +3993,7 @@ async function runDoctor(input, deps = {}) {
3958
3993
  label: "Runtime MCP config",
3959
3994
  ok,
3960
3995
  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)" : ""}.`,
3961
- ...ok ? {} : { repair: `Run: ${RERUN} --doctor --repair --runtime ${input.runtime}` }
3996
+ ...ok ? {} : { repair: `Run: ${RERUN} --doctor --repair${runtimeFlagFor(input2.runtime)}` }
3962
3997
  });
3963
3998
  }
3964
3999
  for (const id of ["hosted_mcp", "identity_match", "rekey_pending"]) {
@@ -4031,18 +4066,18 @@ async function runDoctor(input, deps = {}) {
4031
4066
  }
4032
4067
  const signerProcess = primaryChecksById.get("signer_process");
4033
4068
  if (signerProcess) checks.push(signerProcess);
4034
- const restart = restartRequiredForRuntime(input.runtime, deps.env);
4069
+ const restart = restartRequiredForRuntime(input2.runtime, deps.env);
4035
4070
  checks.push({
4036
4071
  id: "restart",
4037
4072
  label: "Runtime restart",
4038
4073
  ok: true,
4039
- detail: restart ? "This runtime loads MCP config at startup \u2014 restart it after any repair before expecting the tools to appear." : "No restart requirement known for this runtime."
4074
+ detail: restart ? "This runtime loads MCP config at startup \u2014 restart it after any repair before expecting the tools to appear." : input2.runtime === "" ? "Runtime is unknown \u2014 whether a restart is needed cannot be determined. Re-run the doctor naming the runtime for a definitive answer." : "No restart requirement known for this runtime."
4040
4075
  });
4041
4076
  const wiredOk = inventory.filter((entry) => entry.classification === "wired").every((entry) => entry.checks.every((check) => check.ok));
4042
4077
  return {
4043
4078
  version: 1,
4044
4079
  ok: checks.every((check) => check.ok) && wiredOk,
4045
- runtime: input.runtime,
4080
+ runtime: input2.runtime,
4046
4081
  credentialDirectory: primaryDirectory,
4047
4082
  checks,
4048
4083
  agents: inventory,
@@ -4062,6 +4097,21 @@ async function runRepair(input, deps = {}) {
4062
4097
  messages: [`No agent credentials found to repair \u2014 run the full setup: ${RERUN} --setup <token>.`]
4063
4098
  };
4064
4099
  }
4100
+ const resolution = await resolveDoctorRuntime(input, directory);
4101
+ if (resolution.origin === "record") {
4102
+ messages.push(`Runtime not given \u2014 resolved '${resolution.runtime}' from ${path.join(directory, CONNECT_OUTCOME_FILENAME)}.`);
4103
+ }
4104
+ const runtime = resolution.runtime;
4105
+ const input2 = { ...input, runtime };
4106
+ if (runtime === "") {
4107
+ return {
4108
+ ok: false,
4109
+ messages: [
4110
+ "Runtime is unknown \u2014 no runtime flag was given and the connector record carries no resolvable runtime.",
4111
+ `Repair rewrites the runtime config, so it will not guess. Re-run repair naming the runtime \u2014 one of: ${RUNTIME_FLAG_VALUE_LIST.join(", ")}.`
4112
+ ]
4113
+ };
4114
+ }
4065
4115
  let identity;
4066
4116
  try {
4067
4117
  identity = JSON.parse(await promises.readFile(path.join(directory, "identity.json"), "utf8"));
@@ -4071,7 +4121,7 @@ async function runRepair(input, deps = {}) {
4071
4121
  if (!identity.api_key || !(identity.hosted_mcp_url || identity.api_url)) {
4072
4122
  return { ok: false, messages: ["identity.json lacks the stored API key / hosted URL \u2014 re-run the full setup."] };
4073
4123
  }
4074
- const configPath = runtimeConfigPathFor(input.runtime, homeDir);
4124
+ const configPath = runtimeConfigPathFor(input2.runtime, homeDir);
4075
4125
  if (configPath) {
4076
4126
  try {
4077
4127
  const existing = await promises.readFile(configPath, "utf8");
@@ -4098,7 +4148,7 @@ async function runRepair(input, deps = {}) {
4098
4148
  const names = serverNamesFor(serverName);
4099
4149
  messages.push(`Rewriting MCP entries ${names.hosted} / ${names.signer}${serverName ? ` (agent "${serverName}")` : " (unnamed pair)"} \u2014 no other pair is touched.`);
4100
4150
  const configResult = await writeRuntimeConfig({
4101
- runtime: input.runtime,
4151
+ runtime: input2.runtime,
4102
4152
  hostedMcpUrl: identity.hosted_mcp_url ?? `${identity.api_url}/mcp`,
4103
4153
  apiKey: identity.api_key,
4104
4154
  identityPath: path.join(directory, "identity.json"),
@@ -4429,7 +4479,7 @@ async function pathExists2(path) {
4429
4479
  init_unwire();
4430
4480
  init_local_mcp_runtime();
4431
4481
  init_runtime_manifest();
4432
- var CONNECTOR_VERSION = "0.0.0-dev.202609181713.990ba70";
4482
+ var CONNECTOR_VERSION = "0.0.0-dev.202609181807.87b28b1";
4433
4483
  var CONNECT_OUTCOME_SCHEMA_VERSION = 1;
4434
4484
  var failureOutcomesByError = /* @__PURE__ */ new WeakMap();
4435
4485
  function failureOutcomeFor(runtimeHint, error) {