@haven_ai/connect 0.1.30-alpha.0 → 0.1.31-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
@@ -153,6 +153,9 @@ var init_key = __esm({
153
153
  function redactSecrets(value) {
154
154
  return value.replace(API_KEY_RE, "sk_agent_[redacted]").replace(PRIVATE_KEY_RE, "0x[redacted-private-key]");
155
155
  }
156
+ function redactForAutomation(value) {
157
+ return redactSecrets(value).replace(/(?:~|\/)[^\s`"']*\/(?:identity|signer|agent)\.json\b/g, "[credential-file-redacted]").replace(/(?:~|\/)[^\s`"']*\/\.env\b/g, "[credential-env-redacted]");
158
+ }
156
159
  function shortAddress(address) {
157
160
  if (!/^0x[0-9a-fA-F]{40}$/.test(address)) return address;
158
161
  return `${address.slice(0, 6)}...${address.slice(-4)}`;
@@ -523,9 +526,9 @@ var init_runtime_manifest = __esm({
523
526
  mcpPackage: "@haven_ai/mcp",
524
527
  mcpVersion: mcp.MCP_VERSION,
525
528
  sdkPackage: "@haven_ai/sdk",
526
- sdkVersion: "0.1.30-alpha.0",
529
+ sdkVersion: "0.1.31-alpha.0",
527
530
  signerPackage: "@haven_ai/signer",
528
- signerVersion: "0.1.30-alpha.0",
531
+ signerVersion: "0.1.31-alpha.0",
529
532
  // Sourced from the SDK, never a literal (#1161). This field read '20.0.0'
530
533
  // while every package's `engines` said `>=24` and the docs said `>=24.0.0`,
531
534
  // so the guard that was supposed to enforce the floor waved Node v23 through
@@ -1948,11 +1951,13 @@ var init_connect_error = __esm({
1948
1951
  exports.ConnectError = class extends Error {
1949
1952
  code;
1950
1953
  nextAction;
1951
- constructor(code, message, nextAction2) {
1954
+ details;
1955
+ constructor(code, message, nextAction2, details = {}) {
1952
1956
  super(message);
1953
1957
  this.name = "ConnectError";
1954
1958
  this.code = code;
1955
1959
  this.nextAction = nextAction2;
1960
+ this.details = details;
1956
1961
  }
1957
1962
  };
1958
1963
  }
@@ -1975,7 +1980,8 @@ async function resolveRuntimeSelection(explicit, force, options = {}) {
1975
1980
  throw new exports.ConnectError(
1976
1981
  "runtime_force_unrecognized",
1977
1982
  `Unknown --runtime-force value "${force}". Valid values: ${exports.RUNTIME_FLAG_VALUES}.`,
1978
- "rerun_connect_with_a_valid_runtime_name"
1983
+ "rerun_connect_with_a_valid_runtime_name",
1984
+ { allowedRuntimes: RUNTIME_FLAG_VALUE_LIST }
1979
1985
  );
1980
1986
  }
1981
1987
  return { runtime: forced, source: "force" };
@@ -1988,7 +1994,8 @@ async function resolveRuntimeSelection(explicit, force, options = {}) {
1988
1994
  throw new exports.ConnectError(
1989
1995
  "runtime_unrecognized",
1990
1996
  `"${supplied}" is not an agent runtime Haven knows. Valid values: ${exports.RUNTIME_FLAG_VALUES} (the aliases cowork, codex and openclaw are accepted too). Re-run with one of those, or --runtime other to store credentials and finish the MCP setup by hand. Nothing was written and the Haven setup token is still unused.`,
1991
- "rerun_connect_with_a_valid_runtime_name"
1997
+ "rerun_connect_with_a_valid_runtime_name",
1998
+ { allowedRuntimes: RUNTIME_FLAG_VALUE_LIST }
1992
1999
  );
1993
2000
  }
1994
2001
  return { runtime: detected, source: "detected", discardedHint: supplied };
@@ -2023,7 +2030,7 @@ function detectRuntime(env) {
2023
2030
  if (env.HERMES_HOME || env.HERMES_AGENT) return "hermes";
2024
2031
  return null;
2025
2032
  }
2026
- var RUNTIME_PROFILES, RUNTIME_ALIASES; exports.RUNTIME_FLAG_VALUES = void 0;
2033
+ var RUNTIME_PROFILES, RUNTIME_ALIASES, RUNTIME_FLAG_VALUE_LIST; exports.RUNTIME_FLAG_VALUES = void 0;
2027
2034
  var init_runtime_registry = __esm({
2028
2035
  "src/runtime-registry.ts"() {
2029
2036
  init_connect_error();
@@ -2142,7 +2149,18 @@ var init_runtime_registry = __esm({
2142
2149
  other: "other",
2143
2150
  manual: "other"
2144
2151
  };
2145
- exports.RUNTIME_FLAG_VALUES = "claude-code, codex-cli, codex-desktop, cursor, vscode, vscode-insiders, claude-desktop, hermes, other";
2152
+ RUNTIME_FLAG_VALUE_LIST = [
2153
+ "claude-code",
2154
+ "codex-cli",
2155
+ "codex-desktop",
2156
+ "cursor",
2157
+ "vscode",
2158
+ "vscode-insiders",
2159
+ "claude-desktop",
2160
+ "hermes",
2161
+ "other"
2162
+ ];
2163
+ exports.RUNTIME_FLAG_VALUES = RUNTIME_FLAG_VALUE_LIST.join(", ");
2146
2164
  }
2147
2165
  });
2148
2166
  async function acknowledgeLocalSignerConsent(signerPath, log) {
@@ -3705,7 +3723,7 @@ function defaultPromptIo() {
3705
3723
  // src/runtime.ts
3706
3724
  init_local_mcp_runtime();
3707
3725
  init_runtime_manifest();
3708
- var CONNECTOR_VERSION = "0.1.30-alpha.0";
3726
+ var CONNECTOR_VERSION = "0.1.31-alpha.0";
3709
3727
  var CONNECT_OUTCOME_SCHEMA_VERSION = 1;
3710
3728
  async function runConnect(options, deps = {}) {
3711
3729
  assertSupportedNodeVersion(deps.nodeVersion, exports.MCP_RUNTIME_MANIFEST.minimumNodeVersion);
@@ -3730,7 +3748,14 @@ async function runConnect(options, deps = {}) {
3730
3748
  throw new exports.ConnectError(
3731
3749
  "runtime_undetermined",
3732
3750
  `Could not determine the agent runtime: nothing was detected in this environment and no --runtime was given. If you are an AI agent running this command: re-run it once, unchanged except for adding --runtime <name>, naming the harness you are running in \u2014 one of: ${exports.RUNTIME_FLAG_VALUES} (the aliases cowork, codex and openclaw are accepted too). Do not guess: if your harness is not one of those, use --runtime other, which stores the credentials and prints the manual MCP steps. Nothing was written and the Haven setup token is still unused.`,
3733
- "rerun_connect_with_explicit_runtime"
3751
+ "rerun_connect_with_explicit_runtime",
3752
+ // #2091: the values must ride structurally too. The backend's setup
3753
+ // prompt permits a retry only with "one of the values that refusal
3754
+ // lists" — and --json discards prose, so a prose-only list deadlocked
3755
+ // every automation run in an undetected runtime (Codex in the field:
3756
+ // npx needs network, Codex runs network commands unsandboxed, and the
3757
+ // unsandboxed path carries none of the CODEX_* detection vars).
3758
+ { allowedRuntimes: RUNTIME_FLAG_VALUE_LIST }
3734
3759
  );
3735
3760
  }
3736
3761
  const runtime = selection.runtime;
@@ -3752,11 +3777,16 @@ async function runConnect(options, deps = {}) {
3752
3777
  log(`runtime: ${runtime} (chosen at the prompt \u2014 nothing was detected in this environment)`);
3753
3778
  }
3754
3779
  log("Warming up your connection to Haven\u2026");
3755
- const setup = await api.resolveSetup({
3756
- setupToken: options.setupToken,
3757
- connectorVersion,
3758
- runtime
3759
- });
3780
+ let setup;
3781
+ try {
3782
+ setup = await api.resolveSetup({
3783
+ setupToken: options.setupToken,
3784
+ connectorVersion,
3785
+ runtime
3786
+ });
3787
+ } catch (err) {
3788
+ throw deadSetupTokenError(err) ?? err;
3789
+ }
3760
3790
  assertSetupChallengeIsUsable(setup.challenge.expires_at);
3761
3791
  printSetupSummary(setup, log);
3762
3792
  await preflightStorage({ baseDir: options.credentialsDir, warn: log });
@@ -3793,6 +3823,8 @@ async function runConnect(options, deps = {}) {
3793
3823
  installCapabilities
3794
3824
  });
3795
3825
  } catch (err) {
3826
+ const dead = deadSetupTokenError(err);
3827
+ if (dead) throw dead;
3796
3828
  if (isExpiredSetupChallenge(err)) {
3797
3829
  throw new Error(
3798
3830
  "The Haven setup challenge expired while connecting. Return to Haven, start a fresh connection, and run its new Connect command. Do not reuse or paste credentials."
@@ -3990,7 +4022,17 @@ function failedConnectOutcome(runtimeHint, error) {
3990
4022
  tools: ["haven_get_agent", "haven_get_allowances"],
3991
4023
  instruction: "After a successful setup and activation, verify only with haven_get_agent and haven_get_allowances."
3992
4024
  },
3993
- error: { code, next_action: nextAction2 }
4025
+ error: {
4026
+ code,
4027
+ next_action: nextAction2,
4028
+ // Only a ConnectError's message enters the JSON record: the vocabulary's
4029
+ // prose is connector-authored and safe to serialize, while a plain
4030
+ // Error can carry arbitrary server or filesystem detail (that stance is
4031
+ // pinned by test). Redaction stays on as belt-and-braces; plain-Error
4032
+ // runs still get their redacted message on stderr via the CLI mirror.
4033
+ ...error instanceof exports.ConnectError && message ? { message: redactForAutomation(message) } : {},
4034
+ ...error instanceof exports.ConnectError && error.details.allowedRuntimes ? { allowed_runtimes: error.details.allowedRuntimes } : {}
4035
+ }
3994
4036
  };
3995
4037
  }
3996
4038
  function printSetupSummary(setup, log) {
@@ -4012,16 +4054,20 @@ function assertSetupChallengeIsUsable(expiresAt) {
4012
4054
  "This Haven setup challenge is expired or invalid. Return to Haven, start a fresh connection, and rerun Connect. No local credentials were written."
4013
4055
  );
4014
4056
  }
4057
+ function deadSetupTokenError(err) {
4058
+ if (!(err instanceof ConnectRequestError) || err.status !== 410 && err.status !== 401) return null;
4059
+ return new exports.ConnectError(
4060
+ "setup_challenge_expired_or_invalid",
4061
+ "This Haven setup token is expired or invalid \u2014 tokens are single-use and expire 30 minutes after the dashboard issues them, and a mistyped token reads the same way. Return to Haven, start a fresh connection, and run its new Connect command. No local credentials were written.",
4062
+ "return_to_haven_for_fresh_setup"
4063
+ );
4064
+ }
4015
4065
  function isExpiredSetupChallenge(err) {
4016
4066
  return err instanceof Error && /(?:setup )?challenge.*expir|expir.*(?:setup )?challenge/i.test(err.message);
4017
4067
  }
4018
4068
  function secureLogger(log, redactPaths = false) {
4019
4069
  return (message) => {
4020
- let safe = redactSecrets(message);
4021
- if (redactPaths) {
4022
- safe = safe.replace(/(?:~|\/)[^\s`"']*\/(?:identity|signer|agent)\.json\b/g, "[credential-file-redacted]").replace(/(?:~|\/)[^\s`"']*\/\.env\b/g, "[credential-env-redacted]");
4023
- }
4024
- log(safe);
4070
+ log(redactPaths ? redactForAutomation(message) : redactSecrets(message));
4025
4071
  };
4026
4072
  }
4027
4073
  function printRuntimeInstall(result, log) {
@@ -4365,6 +4411,8 @@ async function runCli(argv, io = {
4365
4411
  parsed = parseArgs(argv);
4366
4412
  } catch (err) {
4367
4413
  if (wantsJson) {
4414
+ io.stderr(`${redactForAutomation(err instanceof Error ? err.message : String(err))}
4415
+ `);
4368
4416
  io.stdout(`${JSON.stringify(failedConnectOutcome(void 0, err))}
4369
4417
  `);
4370
4418
  } else {
@@ -4545,6 +4593,8 @@ async function runCli(argv, io = {
4545
4593
  return 0;
4546
4594
  } catch (err) {
4547
4595
  if (parsed.json) {
4596
+ io.stderr(`${redactForAutomation(err instanceof Error ? err.message : String(err))}
4597
+ `);
4548
4598
  io.stdout(`${JSON.stringify(failedConnectOutcome(parsed.options.runtime, err))}
4549
4599
  `);
4550
4600
  } else {