@haven_ai/connect 0.0.0-dev.202610071327.3adb563 → 0.0.0-dev.202610071535.a5affd7

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
@@ -620,9 +620,9 @@ var init_runtime_manifest = __esm({
620
620
  mcpPackage: "@haven_ai/mcp",
621
621
  mcpVersion: mcp.MCP_VERSION,
622
622
  sdkPackage: "@haven_ai/sdk",
623
- sdkVersion: "0.0.0-dev.202610071327.3adb563",
623
+ sdkVersion: "0.0.0-dev.202610071535.a5affd7",
624
624
  signerPackage: "@haven_ai/signer",
625
- signerVersion: "0.0.0-dev.202610071327.3adb563",
625
+ signerVersion: "0.0.0-dev.202610071535.a5affd7",
626
626
  // Sourced from the SDK, never a literal (#1161). This field read '20.0.0'
627
627
  // while every package's `engines` said `>=24` and the docs said `>=24.0.0`,
628
628
  // so the guard that was supposed to enforce the floor waved Node v23 through
@@ -3003,9 +3003,23 @@ function installedClientTargets(homeDir = os.homedir(), cwd = process.cwd(), env
3003
3003
  runtime: "claude-code",
3004
3004
  label: "Claude Code",
3005
3005
  // Claude Code is configured through its own CLI (`claude mcp add-json`),
3006
- // not by writing a file this module owns — so its evidence is the
3007
- // client directory, never a config path.
3006
+ // not by writing a file this module owns — so `configPath` stays null
3007
+ // and the write still goes through the CLI. But a user-scope
3008
+ // `~/.claude.json` carrying an `mcpServers` key IS config-file
3009
+ // evidence: the user has already pointed Claude Code at some MCP
3010
+ // server. It is not the file the connector would write, hence the
3011
+ // separate evidence path (#3732): without it, a Codex config won by
3012
+ // construction on any machine where the user runs Claude Code.
3008
3013
  configPath: null,
3014
+ evidencePath: path.join(homeDir, ".claude.json"),
3015
+ evidenceMatches: (contents) => {
3016
+ try {
3017
+ const parsed = JSON.parse(contents);
3018
+ return typeof parsed === "object" && parsed !== null && "mcpServers" in parsed;
3019
+ } catch {
3020
+ return false;
3021
+ }
3022
+ },
3009
3023
  markers: [path.join(homeDir, ".claude"), path.join(homeDir, ".claude.json")]
3010
3024
  },
3011
3025
  {
@@ -3037,7 +3051,10 @@ function installedClientTargets(homeDir = os.homedir(), cwd = process.cwd(), env
3037
3051
  },
3038
3052
  {
3039
3053
  runtime: "claude-desktop",
3040
- label: "Claude Desktop",
3054
+ // The chat app, a separate runtime from Claude Code. Named so a user in
3055
+ // the desktop app's Code tab cannot read this row as "where I am" —
3056
+ // it sorted above Claude Code whenever both had config (#3732).
3057
+ label: "Claude Desktop (chat app)",
3041
3058
  configPath: runtimeConfigPathFor("claude-desktop", homeDir),
3042
3059
  markers: []
3043
3060
  },
@@ -3052,6 +3069,7 @@ function installedClientTargets(homeDir = os.homedir(), cwd = process.cwd(), env
3052
3069
  }
3053
3070
  async function scanInstalledClients(options = {}) {
3054
3071
  const exists = options.exists ?? pathExists;
3072
+ const readFile15 = options.readFile ?? ((path) => promises.readFile(path, "utf8"));
3055
3073
  const targets = installedClientTargets(options.homeDir, options.cwd, options.env ?? process.env);
3056
3074
  const found = [];
3057
3075
  for (const target of targets) {
@@ -3061,6 +3079,18 @@ async function scanInstalledClients(options = {}) {
3061
3079
  label: target.label,
3062
3080
  detail: `MCP config found at ${target.configPath}`,
3063
3081
  configPath: target.configPath,
3082
+ evidencePath: target.configPath,
3083
+ evidence: "config-file"
3084
+ });
3085
+ continue;
3086
+ }
3087
+ if (target.evidencePath && await hasConfigEvidence(target, exists, readFile15)) {
3088
+ found.push({
3089
+ runtime: target.runtime,
3090
+ label: target.label,
3091
+ detail: `MCP config found at ${target.evidencePath}`,
3092
+ configPath: target.configPath,
3093
+ evidencePath: target.evidencePath,
3064
3094
  evidence: "config-file"
3065
3095
  });
3066
3096
  continue;
@@ -3072,6 +3102,7 @@ async function scanInstalledClients(options = {}) {
3072
3102
  label: target.label,
3073
3103
  detail: `installed (${marker})`,
3074
3104
  configPath: target.configPath,
3105
+ evidencePath: null,
3075
3106
  evidence: "client-directory"
3076
3107
  });
3077
3108
  break;
@@ -3082,6 +3113,16 @@ async function scanInstalledClients(options = {}) {
3082
3113
  return SCAN_ORDER.indexOf(a.runtime) - SCAN_ORDER.indexOf(b.runtime);
3083
3114
  });
3084
3115
  }
3116
+ async function hasConfigEvidence(target, exists, readFile15) {
3117
+ const path = target.evidencePath;
3118
+ if (!await exists(path)) return false;
3119
+ if (!target.evidenceMatches) return true;
3120
+ try {
3121
+ return target.evidenceMatches(await readFile15(path));
3122
+ } catch {
3123
+ return false;
3124
+ }
3125
+ }
3085
3126
  function installedClientHint(candidates) {
3086
3127
  const installedClients = candidates.map((candidate) => candidate.runtime);
3087
3128
  if (candidates.length === 1) {
@@ -3092,18 +3133,24 @@ function installedClientHint(candidates) {
3092
3133
  }
3093
3134
  async function promptForInstalledClient(candidates, io = defaultPromptIo()) {
3094
3135
  if (candidates.length === 0) throw noInstalledClientsError();
3136
+ const hint = installedClientHint(candidates);
3095
3137
  io.write("Haven could not detect which agent runtime this is.\n");
3096
3138
  io.write("These agent clients are installed on this machine:\n");
3097
3139
  candidates.forEach((candidate, index) => {
3098
- io.write(` ${index + 1}) ${candidate.label} \u2014 ${candidate.detail}
3140
+ const suggested = hint.suggestedRuntime === candidate.runtime ? " (suggested)" : "";
3141
+ io.write(` ${index + 1}) ${candidate.label}${suggested} \u2014 ${candidate.detail}
3099
3142
  `);
3100
3143
  });
3101
3144
  io.write("Haven writes an API key and a signing key into the client you pick, so pick the one your agent actually runs in.\n");
3102
3145
  for (let attempt = 0; attempt < MAX_PROMPT_ATTEMPTS; attempt += 1) {
3103
- const answer = await io.question(`Which one? [1-${candidates.length}] (default 1 \u2014 ${candidates[0].label}): `);
3146
+ const answer = await io.question(`Which one? [1-${candidates.length}]: `);
3104
3147
  if (answer === null) throw promptAbortedError("the prompt was cancelled");
3105
3148
  const trimmed = answer.trim();
3106
- if (trimmed === "") return candidates[0].runtime;
3149
+ if (trimmed === "") {
3150
+ io.write(`Type the number of the client to configure (1-${candidates.length}).
3151
+ `);
3152
+ continue;
3153
+ }
3107
3154
  const picked = Number.parseInt(trimmed, 10);
3108
3155
  if (Number.isInteger(picked) && picked >= 1 && picked <= candidates.length) {
3109
3156
  return candidates[picked - 1].runtime;
@@ -4127,7 +4174,8 @@ function installedClientProse(hint) {
4127
4174
  }
4128
4175
  function runtimeSelectionPrompt(options, deps) {
4129
4176
  if (!interactivePromptAllowed(options, deps)) return void 0;
4130
- return deps.promptRuntime ?? (() => resolveRuntimeByInstalledClientPrompt());
4177
+ if (deps.promptRuntime) return deps.promptRuntime;
4178
+ return () => (deps.promptRuntimeByInstalledClient ?? resolveRuntimeByInstalledClientPrompt)({ env: deps.env });
4131
4179
  }
4132
4180
  function interactivePromptAllowed(options, deps) {
4133
4181
  if (options.interactive !== true) return false;
@@ -4451,7 +4499,7 @@ var init_runtime = __esm({
4451
4499
  init_tombstone();
4452
4500
  init_local_mcp_runtime();
4453
4501
  init_runtime_manifest();
4454
- CONNECTOR_VERSION = "0.0.0-dev.202610071327.3adb563";
4502
+ CONNECTOR_VERSION = "0.0.0-dev.202610071535.a5affd7";
4455
4503
  CONNECTOR_CLIENT_IDENTITY = `@haven_ai/connect/${CONNECTOR_VERSION}`;
4456
4504
  CONNECT_OUTCOME_SCHEMA_VERSION = 1;
4457
4505
  failureOutcomesByError = /* @__PURE__ */ new WeakMap();