@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/cli.cjs CHANGED
@@ -154,6 +154,9 @@ var init_key = __esm({
154
154
  function redactSecrets(value) {
155
155
  return value.replace(API_KEY_RE, "sk_agent_[redacted]").replace(PRIVATE_KEY_RE, "0x[redacted-private-key]");
156
156
  }
157
+ function redactForAutomation(value) {
158
+ return redactSecrets(value).replace(/(?:~|\/)[^\s`"']*\/(?:identity|signer|agent)\.json\b/g, "[credential-file-redacted]").replace(/(?:~|\/)[^\s`"']*\/\.env\b/g, "[credential-env-redacted]");
159
+ }
157
160
  function shortAddress(address) {
158
161
  if (!/^0x[0-9a-fA-F]{40}$/.test(address)) return address;
159
162
  return `${address.slice(0, 6)}...${address.slice(-4)}`;
@@ -524,9 +527,9 @@ var init_runtime_manifest = __esm({
524
527
  mcpPackage: "@haven_ai/mcp",
525
528
  mcpVersion: mcp.MCP_VERSION,
526
529
  sdkPackage: "@haven_ai/sdk",
527
- sdkVersion: "0.1.30-alpha.0",
530
+ sdkVersion: "0.1.31-alpha.0",
528
531
  signerPackage: "@haven_ai/signer",
529
- signerVersion: "0.1.30-alpha.0",
532
+ signerVersion: "0.1.31-alpha.0",
530
533
  // Sourced from the SDK, never a literal (#1161). This field read '20.0.0'
531
534
  // while every package's `engines` said `>=24` and the docs said `>=24.0.0`,
532
535
  // so the guard that was supposed to enforce the floor waved Node v23 through
@@ -1946,11 +1949,13 @@ var init_connect_error = __esm({
1946
1949
  ConnectError = class extends Error {
1947
1950
  code;
1948
1951
  nextAction;
1949
- constructor(code, message, nextAction2) {
1952
+ details;
1953
+ constructor(code, message, nextAction2, details = {}) {
1950
1954
  super(message);
1951
1955
  this.name = "ConnectError";
1952
1956
  this.code = code;
1953
1957
  this.nextAction = nextAction2;
1958
+ this.details = details;
1954
1959
  }
1955
1960
  };
1956
1961
  }
@@ -1973,7 +1978,8 @@ async function resolveRuntimeSelection(explicit, force, options = {}) {
1973
1978
  throw new ConnectError(
1974
1979
  "runtime_force_unrecognized",
1975
1980
  `Unknown --runtime-force value "${force}". Valid values: ${RUNTIME_FLAG_VALUES}.`,
1976
- "rerun_connect_with_a_valid_runtime_name"
1981
+ "rerun_connect_with_a_valid_runtime_name",
1982
+ { allowedRuntimes: RUNTIME_FLAG_VALUE_LIST }
1977
1983
  );
1978
1984
  }
1979
1985
  return { runtime: forced, source: "force" };
@@ -1986,7 +1992,8 @@ async function resolveRuntimeSelection(explicit, force, options = {}) {
1986
1992
  throw new ConnectError(
1987
1993
  "runtime_unrecognized",
1988
1994
  `"${supplied}" is not an agent runtime Haven knows. Valid values: ${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.`,
1989
- "rerun_connect_with_a_valid_runtime_name"
1995
+ "rerun_connect_with_a_valid_runtime_name",
1996
+ { allowedRuntimes: RUNTIME_FLAG_VALUE_LIST }
1990
1997
  );
1991
1998
  }
1992
1999
  return { runtime: detected, source: "detected", discardedHint: supplied };
@@ -2021,7 +2028,7 @@ function detectRuntime(env) {
2021
2028
  if (env.HERMES_HOME || env.HERMES_AGENT) return "hermes";
2022
2029
  return null;
2023
2030
  }
2024
- var RUNTIME_PROFILES, RUNTIME_ALIASES, RUNTIME_FLAG_VALUES;
2031
+ var RUNTIME_PROFILES, RUNTIME_ALIASES, RUNTIME_FLAG_VALUE_LIST, RUNTIME_FLAG_VALUES;
2025
2032
  var init_runtime_registry = __esm({
2026
2033
  "src/runtime-registry.ts"() {
2027
2034
  init_connect_error();
@@ -2140,7 +2147,18 @@ var init_runtime_registry = __esm({
2140
2147
  other: "other",
2141
2148
  manual: "other"
2142
2149
  };
2143
- RUNTIME_FLAG_VALUES = "claude-code, codex-cli, codex-desktop, cursor, vscode, vscode-insiders, claude-desktop, hermes, other";
2150
+ RUNTIME_FLAG_VALUE_LIST = [
2151
+ "claude-code",
2152
+ "codex-cli",
2153
+ "codex-desktop",
2154
+ "cursor",
2155
+ "vscode",
2156
+ "vscode-insiders",
2157
+ "claude-desktop",
2158
+ "hermes",
2159
+ "other"
2160
+ ];
2161
+ RUNTIME_FLAG_VALUES = RUNTIME_FLAG_VALUE_LIST.join(", ");
2144
2162
  }
2145
2163
  });
2146
2164
  async function acknowledgeLocalSignerConsent(signerPath, log) {
@@ -3703,7 +3721,7 @@ function defaultPromptIo() {
3703
3721
  // src/runtime.ts
3704
3722
  init_local_mcp_runtime();
3705
3723
  init_runtime_manifest();
3706
- var CONNECTOR_VERSION = "0.1.30-alpha.0";
3724
+ var CONNECTOR_VERSION = "0.1.31-alpha.0";
3707
3725
  var CONNECT_OUTCOME_SCHEMA_VERSION = 1;
3708
3726
  async function runConnect(options, deps = {}) {
3709
3727
  assertSupportedNodeVersion(deps.nodeVersion, MCP_RUNTIME_MANIFEST.minimumNodeVersion);
@@ -3728,7 +3746,14 @@ async function runConnect(options, deps = {}) {
3728
3746
  throw new ConnectError(
3729
3747
  "runtime_undetermined",
3730
3748
  `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: ${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.`,
3731
- "rerun_connect_with_explicit_runtime"
3749
+ "rerun_connect_with_explicit_runtime",
3750
+ // #2091: the values must ride structurally too. The backend's setup
3751
+ // prompt permits a retry only with "one of the values that refusal
3752
+ // lists" — and --json discards prose, so a prose-only list deadlocked
3753
+ // every automation run in an undetected runtime (Codex in the field:
3754
+ // npx needs network, Codex runs network commands unsandboxed, and the
3755
+ // unsandboxed path carries none of the CODEX_* detection vars).
3756
+ { allowedRuntimes: RUNTIME_FLAG_VALUE_LIST }
3732
3757
  );
3733
3758
  }
3734
3759
  const runtime = selection.runtime;
@@ -3750,11 +3775,16 @@ async function runConnect(options, deps = {}) {
3750
3775
  log(`runtime: ${runtime} (chosen at the prompt \u2014 nothing was detected in this environment)`);
3751
3776
  }
3752
3777
  log("Warming up your connection to Haven\u2026");
3753
- const setup = await api.resolveSetup({
3754
- setupToken: options.setupToken,
3755
- connectorVersion,
3756
- runtime
3757
- });
3778
+ let setup;
3779
+ try {
3780
+ setup = await api.resolveSetup({
3781
+ setupToken: options.setupToken,
3782
+ connectorVersion,
3783
+ runtime
3784
+ });
3785
+ } catch (err) {
3786
+ throw deadSetupTokenError(err) ?? err;
3787
+ }
3758
3788
  assertSetupChallengeIsUsable(setup.challenge.expires_at);
3759
3789
  printSetupSummary(setup, log);
3760
3790
  await preflightStorage({ baseDir: options.credentialsDir, warn: log });
@@ -3791,6 +3821,8 @@ async function runConnect(options, deps = {}) {
3791
3821
  installCapabilities
3792
3822
  });
3793
3823
  } catch (err) {
3824
+ const dead = deadSetupTokenError(err);
3825
+ if (dead) throw dead;
3794
3826
  if (isExpiredSetupChallenge(err)) {
3795
3827
  throw new Error(
3796
3828
  "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."
@@ -3988,7 +4020,17 @@ function failedConnectOutcome(runtimeHint, error) {
3988
4020
  tools: ["haven_get_agent", "haven_get_allowances"],
3989
4021
  instruction: "After a successful setup and activation, verify only with haven_get_agent and haven_get_allowances."
3990
4022
  },
3991
- error: { code, next_action: nextAction2 }
4023
+ error: {
4024
+ code,
4025
+ next_action: nextAction2,
4026
+ // Only a ConnectError's message enters the JSON record: the vocabulary's
4027
+ // prose is connector-authored and safe to serialize, while a plain
4028
+ // Error can carry arbitrary server or filesystem detail (that stance is
4029
+ // pinned by test). Redaction stays on as belt-and-braces; plain-Error
4030
+ // runs still get their redacted message on stderr via the CLI mirror.
4031
+ ...error instanceof ConnectError && message ? { message: redactForAutomation(message) } : {},
4032
+ ...error instanceof ConnectError && error.details.allowedRuntimes ? { allowed_runtimes: error.details.allowedRuntimes } : {}
4033
+ }
3992
4034
  };
3993
4035
  }
3994
4036
  function printSetupSummary(setup, log) {
@@ -4010,16 +4052,20 @@ function assertSetupChallengeIsUsable(expiresAt) {
4010
4052
  "This Haven setup challenge is expired or invalid. Return to Haven, start a fresh connection, and rerun Connect. No local credentials were written."
4011
4053
  );
4012
4054
  }
4055
+ function deadSetupTokenError(err) {
4056
+ if (!(err instanceof ConnectRequestError) || err.status !== 410 && err.status !== 401) return null;
4057
+ return new ConnectError(
4058
+ "setup_challenge_expired_or_invalid",
4059
+ "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.",
4060
+ "return_to_haven_for_fresh_setup"
4061
+ );
4062
+ }
4013
4063
  function isExpiredSetupChallenge(err) {
4014
4064
  return err instanceof Error && /(?:setup )?challenge.*expir|expir.*(?:setup )?challenge/i.test(err.message);
4015
4065
  }
4016
4066
  function secureLogger(log, redactPaths = false) {
4017
4067
  return (message) => {
4018
- let safe = redactSecrets(message);
4019
- if (redactPaths) {
4020
- safe = safe.replace(/(?:~|\/)[^\s`"']*\/(?:identity|signer|agent)\.json\b/g, "[credential-file-redacted]").replace(/(?:~|\/)[^\s`"']*\/\.env\b/g, "[credential-env-redacted]");
4021
- }
4022
- log(safe);
4068
+ log(redactPaths ? redactForAutomation(message) : redactSecrets(message));
4023
4069
  };
4024
4070
  }
4025
4071
  function printRuntimeInstall(result, log) {
@@ -4360,6 +4406,8 @@ async function runCli(argv, io = {
4360
4406
  parsed = parseArgs(argv);
4361
4407
  } catch (err) {
4362
4408
  if (wantsJson) {
4409
+ io.stderr(`${redactForAutomation(err instanceof Error ? err.message : String(err))}
4410
+ `);
4363
4411
  io.stdout(`${JSON.stringify(failedConnectOutcome(void 0, err))}
4364
4412
  `);
4365
4413
  } else {
@@ -4540,6 +4588,8 @@ async function runCli(argv, io = {
4540
4588
  return 0;
4541
4589
  } catch (err) {
4542
4590
  if (parsed.json) {
4591
+ io.stderr(`${redactForAutomation(err instanceof Error ? err.message : String(err))}
4592
+ `);
4543
4593
  io.stdout(`${JSON.stringify(failedConnectOutcome(parsed.options.runtime, err))}
4544
4594
  `);
4545
4595
  } else {