@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.js CHANGED
@@ -147,6 +147,9 @@ var init_key = __esm({
147
147
  function redactSecrets(value) {
148
148
  return value.replace(API_KEY_RE, "sk_agent_[redacted]").replace(PRIVATE_KEY_RE, "0x[redacted-private-key]");
149
149
  }
150
+ function redactForAutomation(value) {
151
+ return redactSecrets(value).replace(/(?:~|\/)[^\s`"']*\/(?:identity|signer|agent)\.json\b/g, "[credential-file-redacted]").replace(/(?:~|\/)[^\s`"']*\/\.env\b/g, "[credential-env-redacted]");
152
+ }
150
153
  function shortAddress(address) {
151
154
  if (!/^0x[0-9a-fA-F]{40}$/.test(address)) return address;
152
155
  return `${address.slice(0, 6)}...${address.slice(-4)}`;
@@ -517,9 +520,9 @@ var init_runtime_manifest = __esm({
517
520
  mcpPackage: "@haven_ai/mcp",
518
521
  mcpVersion: MCP_VERSION,
519
522
  sdkPackage: "@haven_ai/sdk",
520
- sdkVersion: "0.1.30-alpha.0",
523
+ sdkVersion: "0.1.31-alpha.0",
521
524
  signerPackage: "@haven_ai/signer",
522
- signerVersion: "0.1.30-alpha.0",
525
+ signerVersion: "0.1.31-alpha.0",
523
526
  // Sourced from the SDK, never a literal (#1161). This field read '20.0.0'
524
527
  // while every package's `engines` said `>=24` and the docs said `>=24.0.0`,
525
528
  // so the guard that was supposed to enforce the floor waved Node v23 through
@@ -1939,11 +1942,13 @@ var init_connect_error = __esm({
1939
1942
  ConnectError = class extends Error {
1940
1943
  code;
1941
1944
  nextAction;
1942
- constructor(code, message, nextAction2) {
1945
+ details;
1946
+ constructor(code, message, nextAction2, details = {}) {
1943
1947
  super(message);
1944
1948
  this.name = "ConnectError";
1945
1949
  this.code = code;
1946
1950
  this.nextAction = nextAction2;
1951
+ this.details = details;
1947
1952
  }
1948
1953
  };
1949
1954
  }
@@ -1966,7 +1971,8 @@ async function resolveRuntimeSelection(explicit, force, options = {}) {
1966
1971
  throw new ConnectError(
1967
1972
  "runtime_force_unrecognized",
1968
1973
  `Unknown --runtime-force value "${force}". Valid values: ${RUNTIME_FLAG_VALUES}.`,
1969
- "rerun_connect_with_a_valid_runtime_name"
1974
+ "rerun_connect_with_a_valid_runtime_name",
1975
+ { allowedRuntimes: RUNTIME_FLAG_VALUE_LIST }
1970
1976
  );
1971
1977
  }
1972
1978
  return { runtime: forced, source: "force" };
@@ -1979,7 +1985,8 @@ async function resolveRuntimeSelection(explicit, force, options = {}) {
1979
1985
  throw new ConnectError(
1980
1986
  "runtime_unrecognized",
1981
1987
  `"${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.`,
1982
- "rerun_connect_with_a_valid_runtime_name"
1988
+ "rerun_connect_with_a_valid_runtime_name",
1989
+ { allowedRuntimes: RUNTIME_FLAG_VALUE_LIST }
1983
1990
  );
1984
1991
  }
1985
1992
  return { runtime: detected, source: "detected", discardedHint: supplied };
@@ -2014,7 +2021,7 @@ function detectRuntime(env) {
2014
2021
  if (env.HERMES_HOME || env.HERMES_AGENT) return "hermes";
2015
2022
  return null;
2016
2023
  }
2017
- var RUNTIME_PROFILES, RUNTIME_ALIASES, RUNTIME_FLAG_VALUES;
2024
+ var RUNTIME_PROFILES, RUNTIME_ALIASES, RUNTIME_FLAG_VALUE_LIST, RUNTIME_FLAG_VALUES;
2018
2025
  var init_runtime_registry = __esm({
2019
2026
  "src/runtime-registry.ts"() {
2020
2027
  init_connect_error();
@@ -2133,7 +2140,18 @@ var init_runtime_registry = __esm({
2133
2140
  other: "other",
2134
2141
  manual: "other"
2135
2142
  };
2136
- RUNTIME_FLAG_VALUES = "claude-code, codex-cli, codex-desktop, cursor, vscode, vscode-insiders, claude-desktop, hermes, other";
2143
+ RUNTIME_FLAG_VALUE_LIST = [
2144
+ "claude-code",
2145
+ "codex-cli",
2146
+ "codex-desktop",
2147
+ "cursor",
2148
+ "vscode",
2149
+ "vscode-insiders",
2150
+ "claude-desktop",
2151
+ "hermes",
2152
+ "other"
2153
+ ];
2154
+ RUNTIME_FLAG_VALUES = RUNTIME_FLAG_VALUE_LIST.join(", ");
2137
2155
  }
2138
2156
  });
2139
2157
  async function acknowledgeLocalSignerConsent(signerPath, log) {
@@ -3696,7 +3714,7 @@ function defaultPromptIo() {
3696
3714
  // src/runtime.ts
3697
3715
  init_local_mcp_runtime();
3698
3716
  init_runtime_manifest();
3699
- var CONNECTOR_VERSION = "0.1.30-alpha.0";
3717
+ var CONNECTOR_VERSION = "0.1.31-alpha.0";
3700
3718
  var CONNECT_OUTCOME_SCHEMA_VERSION = 1;
3701
3719
  async function runConnect(options, deps = {}) {
3702
3720
  assertSupportedNodeVersion(deps.nodeVersion, MCP_RUNTIME_MANIFEST.minimumNodeVersion);
@@ -3721,7 +3739,14 @@ async function runConnect(options, deps = {}) {
3721
3739
  throw new ConnectError(
3722
3740
  "runtime_undetermined",
3723
3741
  `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.`,
3724
- "rerun_connect_with_explicit_runtime"
3742
+ "rerun_connect_with_explicit_runtime",
3743
+ // #2091: the values must ride structurally too. The backend's setup
3744
+ // prompt permits a retry only with "one of the values that refusal
3745
+ // lists" — and --json discards prose, so a prose-only list deadlocked
3746
+ // every automation run in an undetected runtime (Codex in the field:
3747
+ // npx needs network, Codex runs network commands unsandboxed, and the
3748
+ // unsandboxed path carries none of the CODEX_* detection vars).
3749
+ { allowedRuntimes: RUNTIME_FLAG_VALUE_LIST }
3725
3750
  );
3726
3751
  }
3727
3752
  const runtime = selection.runtime;
@@ -3743,11 +3768,16 @@ async function runConnect(options, deps = {}) {
3743
3768
  log(`runtime: ${runtime} (chosen at the prompt \u2014 nothing was detected in this environment)`);
3744
3769
  }
3745
3770
  log("Warming up your connection to Haven\u2026");
3746
- const setup = await api.resolveSetup({
3747
- setupToken: options.setupToken,
3748
- connectorVersion,
3749
- runtime
3750
- });
3771
+ let setup;
3772
+ try {
3773
+ setup = await api.resolveSetup({
3774
+ setupToken: options.setupToken,
3775
+ connectorVersion,
3776
+ runtime
3777
+ });
3778
+ } catch (err) {
3779
+ throw deadSetupTokenError(err) ?? err;
3780
+ }
3751
3781
  assertSetupChallengeIsUsable(setup.challenge.expires_at);
3752
3782
  printSetupSummary(setup, log);
3753
3783
  await preflightStorage({ baseDir: options.credentialsDir, warn: log });
@@ -3784,6 +3814,8 @@ async function runConnect(options, deps = {}) {
3784
3814
  installCapabilities
3785
3815
  });
3786
3816
  } catch (err) {
3817
+ const dead = deadSetupTokenError(err);
3818
+ if (dead) throw dead;
3787
3819
  if (isExpiredSetupChallenge(err)) {
3788
3820
  throw new Error(
3789
3821
  "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."
@@ -3981,7 +4013,17 @@ function failedConnectOutcome(runtimeHint, error) {
3981
4013
  tools: ["haven_get_agent", "haven_get_allowances"],
3982
4014
  instruction: "After a successful setup and activation, verify only with haven_get_agent and haven_get_allowances."
3983
4015
  },
3984
- error: { code, next_action: nextAction2 }
4016
+ error: {
4017
+ code,
4018
+ next_action: nextAction2,
4019
+ // Only a ConnectError's message enters the JSON record: the vocabulary's
4020
+ // prose is connector-authored and safe to serialize, while a plain
4021
+ // Error can carry arbitrary server or filesystem detail (that stance is
4022
+ // pinned by test). Redaction stays on as belt-and-braces; plain-Error
4023
+ // runs still get their redacted message on stderr via the CLI mirror.
4024
+ ...error instanceof ConnectError && message ? { message: redactForAutomation(message) } : {},
4025
+ ...error instanceof ConnectError && error.details.allowedRuntimes ? { allowed_runtimes: error.details.allowedRuntimes } : {}
4026
+ }
3985
4027
  };
3986
4028
  }
3987
4029
  function printSetupSummary(setup, log) {
@@ -4003,16 +4045,20 @@ function assertSetupChallengeIsUsable(expiresAt) {
4003
4045
  "This Haven setup challenge is expired or invalid. Return to Haven, start a fresh connection, and rerun Connect. No local credentials were written."
4004
4046
  );
4005
4047
  }
4048
+ function deadSetupTokenError(err) {
4049
+ if (!(err instanceof ConnectRequestError) || err.status !== 410 && err.status !== 401) return null;
4050
+ return new ConnectError(
4051
+ "setup_challenge_expired_or_invalid",
4052
+ "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.",
4053
+ "return_to_haven_for_fresh_setup"
4054
+ );
4055
+ }
4006
4056
  function isExpiredSetupChallenge(err) {
4007
4057
  return err instanceof Error && /(?:setup )?challenge.*expir|expir.*(?:setup )?challenge/i.test(err.message);
4008
4058
  }
4009
4059
  function secureLogger(log, redactPaths = false) {
4010
4060
  return (message) => {
4011
- let safe = redactSecrets(message);
4012
- if (redactPaths) {
4013
- safe = safe.replace(/(?:~|\/)[^\s`"']*\/(?:identity|signer|agent)\.json\b/g, "[credential-file-redacted]").replace(/(?:~|\/)[^\s`"']*\/\.env\b/g, "[credential-env-redacted]");
4014
- }
4015
- log(safe);
4061
+ log(redactPaths ? redactForAutomation(message) : redactSecrets(message));
4016
4062
  };
4017
4063
  }
4018
4064
  function printRuntimeInstall(result, log) {
@@ -4353,6 +4399,8 @@ async function runCli(argv, io = {
4353
4399
  parsed = parseArgs(argv);
4354
4400
  } catch (err) {
4355
4401
  if (wantsJson) {
4402
+ io.stderr(`${redactForAutomation(err instanceof Error ? err.message : String(err))}
4403
+ `);
4356
4404
  io.stdout(`${JSON.stringify(failedConnectOutcome(void 0, err))}
4357
4405
  `);
4358
4406
  } else {
@@ -4533,6 +4581,8 @@ async function runCli(argv, io = {
4533
4581
  return 0;
4534
4582
  } catch (err) {
4535
4583
  if (parsed.json) {
4584
+ io.stderr(`${redactForAutomation(err instanceof Error ? err.message : String(err))}
4585
+ `);
4536
4586
  io.stdout(`${JSON.stringify(failedConnectOutcome(parsed.options.runtime, err))}
4537
4587
  `);
4538
4588
  } else {