@posthog/wizard 2.32.0 → 2.34.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.
Files changed (78) hide show
  1. package/dist/{add-mcp-server-to-clients-Dc0yssCM.js → add-mcp-server-to-clients-BjH4z7gH.js} +5 -5
  2. package/dist/{add-mcp-server-to-clients-Dc0yssCM.js.map → add-mcp-server-to-clients-BjH4z7gH.js.map} +1 -1
  3. package/dist/{agent-interface-c7B2JZEd.js → agent-interface-BcUv1qeh.js} +330 -45
  4. package/dist/agent-interface-BcUv1qeh.js.map +1 -0
  5. package/dist/{agent-runner-Am34bBUT.js → agent-runner-8g_pDAVs.js} +30 -11
  6. package/dist/agent-runner-8g_pDAVs.js.map +1 -0
  7. package/dist/{analytics-CBIKy9PZ.js → analytics-DQ8IS1Hu.js} +3 -3
  8. package/dist/analytics-DQ8IS1Hu.js.map +1 -0
  9. package/dist/{api-Blg3nvvZ.js → api-EghEV_u9.js} +3 -3
  10. package/dist/{api-Blg3nvvZ.js.map → api-EghEV_u9.js.map} +1 -1
  11. package/dist/bin.js +203 -75
  12. package/dist/bin.js.map +1 -1
  13. package/dist/check-screens.tsx +25 -0
  14. package/dist/ci-install-ouGSf4zp.js +113 -0
  15. package/dist/ci-install-ouGSf4zp.js.map +1 -0
  16. package/dist/{debug-cy_jyRb4.js → debug-BM7yop-T.js} +1 -1
  17. package/dist/{debug-AdvgwKEw.js → debug-Cn4L5Ype.js} +2 -2
  18. package/dist/{debug-AdvgwKEw.js.map → debug-Cn4L5Ype.js.map} +1 -1
  19. package/dist/{defaults-BNWIWzjc.js → defaults-DII5CAog.js} +1 -1
  20. package/dist/{defaults-BNWIWzjc.js.map → defaults-DII5CAog.js.map} +1 -1
  21. package/dist/{environment-B6TW5v9d.js → environment-D2WUIm8o.js} +3 -3
  22. package/dist/{environment-B6TW5v9d.js.map → environment-D2WUIm8o.js.map} +1 -1
  23. package/dist/{file-utils-8tUk_eEX.js → file-utils-CdQn6073.js} +2 -2
  24. package/dist/{file-utils-8tUk_eEX.js.map → file-utils-CdQn6073.js.map} +1 -1
  25. package/dist/headless-ui-CSqtmW-d.js +24 -0
  26. package/dist/headless-ui-CSqtmW-d.js.map +1 -0
  27. package/dist/{interactive-CApktTrj.js → interactive-CFGcWw43.js} +3 -3
  28. package/dist/{interactive-CApktTrj.js.map → interactive-CFGcWw43.js.map} +1 -1
  29. package/dist/{mcp-prompt-streaming-BKcU9yuz.js → mcp-prompt-streaming-XZSpcXkG.js} +9 -6
  30. package/dist/mcp-prompt-streaming-XZSpcXkG.js.map +1 -0
  31. package/dist/{non-interactive-DejTdRTW.js → non-interactive-D6eksWIw.js} +2 -2
  32. package/dist/{non-interactive-DejTdRTW.js.map → non-interactive-D6eksWIw.js.map} +1 -1
  33. package/dist/{package-manager-DBfgSXNn.js → package-manager-Dozr4x6K.js} +2 -2
  34. package/dist/{package-manager-DBfgSXNn.js.map → package-manager-Dozr4x6K.js.map} +1 -1
  35. package/dist/{playground-BOg2U1AT.js → playground-BB2O8GyY.js} +7 -5
  36. package/dist/{playground-BOg2U1AT.js.map → playground-BB2O8GyY.js.map} +1 -1
  37. package/dist/{posthog-Cr37rnla.js → posthog-DU6JXG00.js} +1 -1
  38. package/dist/{posthog-Cr37rnla.js.map → posthog-DU6JXG00.js.map} +1 -1
  39. package/dist/{posthog-integration-gLhOUdPJ.js → posthog-integration-C3m_uR5A.js} +13 -13
  40. package/dist/{posthog-integration-gLhOUdPJ.js.map → posthog-integration-C3m_uR5A.js.map} +1 -1
  41. package/dist/{provisioning-DuzclqPB.js → provisioning-wMzPfaaP.js} +3 -3
  42. package/dist/{provisioning-DuzclqPB.js.map → provisioning-wMzPfaaP.js.map} +1 -1
  43. package/dist/{registry-Dbl-5SnO.js → registry-BfTeAX2z.js} +4 -4
  44. package/dist/{registry-Dbl-5SnO.js.map → registry-BfTeAX2z.js.map} +1 -1
  45. package/dist/{setup-utils-B6wbp3s0.js → setup-utils-B0oz3raQ.js} +53 -11
  46. package/dist/setup-utils-B0oz3raQ.js.map +1 -0
  47. package/dist/smoke-test.sh +25 -0
  48. package/dist/{start-tui-IoQh-Nhj.js → start-tui-B9qDcoGA.js} +151 -31
  49. package/dist/{start-tui-IoQh-Nhj.js.map → start-tui-B9qDcoGA.js.map} +1 -1
  50. package/dist/{steps-CJrqlHbo.js → steps-BhERMRRI.js} +7 -7
  51. package/dist/{steps-CJrqlHbo.js.map → steps-BhERMRRI.js.map} +1 -1
  52. package/dist/store-J2Rv955W.js +763 -0
  53. package/dist/store-J2Rv955W.js.map +1 -0
  54. package/dist/{task-stream-BQNSp0qR.js → task-stream-CPjpHFhI.js} +4 -4
  55. package/dist/{task-stream-BQNSp0qR.js.map → task-stream-CPjpHFhI.js.map} +1 -1
  56. package/dist/{telemetry-1m0CyTry.js → telemetry-BTLJ-YnG.js} +3 -3
  57. package/dist/{telemetry-1m0CyTry.js.map → telemetry-BTLJ-YnG.js.map} +1 -1
  58. package/dist/{terminal-BKI4i72f.js → terminal-UwqFnPrL.js} +87 -786
  59. package/dist/terminal-UwqFnPrL.js.map +1 -0
  60. package/dist/{urls-B3JumpLT.js → urls-DAVzgHGZ.js} +2 -2
  61. package/dist/{urls-B3JumpLT.js.map → urls-DAVzgHGZ.js.map} +1 -1
  62. package/dist/{wizard-abort-D7SzKUgE.js → wizard-abort-BAB8yvGF.js} +1 -1
  63. package/dist/{wizard-abort-PqLMKSh1.js → wizard-abort-DqeMgCVr.js} +4 -3
  64. package/dist/{wizard-abort-PqLMKSh1.js.map → wizard-abort-DqeMgCVr.js.map} +1 -1
  65. package/dist/wizard-session-BKEdX9mO.js +2 -0
  66. package/dist/{wizard-session-G3VWD6hv.js → wizard-session-CN55LYyZ.js} +9 -2
  67. package/dist/{wizard-session-G3VWD6hv.js.map → wizard-session-CN55LYyZ.js.map} +1 -1
  68. package/dist/wizard-ui-WZ48rUgr.js.map +1 -1
  69. package/package.json +1 -1
  70. package/dist/agent-interface-c7B2JZEd.js.map +0 -1
  71. package/dist/agent-runner-Am34bBUT.js.map +0 -1
  72. package/dist/analytics-CBIKy9PZ.js.map +0 -1
  73. package/dist/ci-install-51ntd9x5.js +0 -73
  74. package/dist/ci-install-51ntd9x5.js.map +0 -1
  75. package/dist/mcp-prompt-streaming-BKcU9yuz.js.map +0 -1
  76. package/dist/setup-utils-B6wbp3s0.js.map +0 -1
  77. package/dist/terminal-BKI4i72f.js.map +0 -1
  78. package/dist/wizard-session-wPJtNl4c.js +0 -2
@@ -1,14 +1,15 @@
1
1
  import { n as __require } from "./rolldown-runtime-B_-DWIq7.js";
2
- import { J as WIZARD_ORCHESTRATOR_FLAG_KEY, L as POSTHOG_FLAG_HEADER_PREFIX, Q as WIZARD_USER_AGENT, V as POSTHOG_PROPERTY_HEADER_PREFIX, X as WIZARD_REMARK_EVENT_NAME, a as getLogFilePath, f as skillTmpPath, o as initLogFile, p as getUI, r as debug, rt as runtimeEnv, s as logToFile, u as WIZARD_YARA_REPORT_FILE } from "./debug-AdvgwKEw.js";
3
- import { t as analytics } from "./analytics-CBIKy9PZ.js";
4
- import { i as getLlmGatewayUrlFromHost } from "./urls-B3JumpLT.js";
5
- import { n as ADDITIONAL_FEATURE_PROMPTS } from "./wizard-session-G3VWD6hv.js";
6
- import { i as wizardAbort, n as registerCleanup, t as WizardError } from "./wizard-abort-PqLMKSh1.js";
2
+ import { J as WIZARD_ORCHESTRATOR_FLAG_KEY, L as POSTHOG_FLAG_HEADER_PREFIX, Q as WIZARD_USER_AGENT, V as POSTHOG_PROPERTY_HEADER_PREFIX, X as WIZARD_REMARK_EVENT_NAME, a as getLogFilePath, f as skillTmpPath, o as initLogFile, p as getUI, r as debug, rt as runtimeEnv, s as logToFile, u as WIZARD_YARA_REPORT_FILE } from "./debug-Cn4L5Ype.js";
3
+ import { t as analytics } from "./analytics-DQ8IS1Hu.js";
4
+ import { i as getLlmGatewayUrlFromHost } from "./urls-DAVzgHGZ.js";
5
+ import { n as ADDITIONAL_FEATURE_PROMPTS } from "./wizard-session-CN55LYyZ.js";
6
+ import { i as wizardAbort, n as registerCleanup, t as WizardError } from "./wizard-abort-DqeMgCVr.js";
7
7
  import { createRequire } from "node:module";
8
8
  import * as fs$1 from "fs";
9
9
  import fs from "fs";
10
10
  import * as path$1 from "path";
11
11
  import path from "path";
12
+ import { spawnSync } from "node:child_process";
12
13
  import axios from "axios";
13
14
  import { z } from "zod";
14
15
  import fg from "fast-glob";
@@ -1909,10 +1910,31 @@ const OUTPUT_SIGNALS = {
1909
1910
  const SIGNAL_NEEDLES = Object.values(OUTPUT_SIGNALS);
1910
1911
  var AgentOutputSignals = class {
1911
1912
  lines = [];
1913
+ apiKeySourceValue;
1912
1914
  /** Parse step: keep the line only if it carries a known signal; drop prose. */
1913
1915
  push(text) {
1914
1916
  if (SIGNAL_NEEDLES.some((n) => text.includes(n))) this.lines.push(text);
1915
1917
  }
1918
+ /**
1919
+ * Record the SDK's `apiKeySource` from its `init` message (e.g.
1920
+ * `"/login managed key"`, `"ANTHROPIC_API_KEY"`). Used to triage a 401:
1921
+ * a managed-login source means the SDK authenticated with a stored Claude
1922
+ * login rather than the gateway token the wizard injected.
1923
+ */
1924
+ recordApiKeySource(source) {
1925
+ if (source) this.apiKeySourceValue = source;
1926
+ }
1927
+ get apiKeySource() {
1928
+ return this.apiKeySourceValue;
1929
+ }
1930
+ /**
1931
+ * True when the SDK authed from a stored `/login` credential (the
1932
+ * "managed key") instead of an explicit `ANTHROPIC_API_KEY` — the signature
1933
+ * of conflicting Anthropic credentials behind a gateway 401.
1934
+ */
1935
+ usedManagedLogin() {
1936
+ return /login|managed key/i.test(this.apiKeySourceValue ?? "");
1937
+ }
1916
1938
  get text() {
1917
1939
  return this.lines.join("\n");
1918
1940
  }
@@ -1939,6 +1961,128 @@ var AgentOutputSignals = class {
1939
1961
  }
1940
1962
  };
1941
1963
  //#endregion
1964
+ //#region src/lib/agent/agent-env-isolation.ts
1965
+ /**
1966
+ * Credential isolation for the Claude Agent SDK subprocess.
1967
+ *
1968
+ * The SDK doesn't authenticate in-process — `query()` extracts and spawns a
1969
+ * native `claude` binary that resolves model routing and credentials from its
1970
+ * own environment. That binary honors a large, ever-growing surface of
1971
+ * `ANTHROPIC_*` / `CLAUDE_CODE_*` env vars: provider-activation flags
1972
+ * (`CLAUDE_CODE_USE_BEDROCK` / `_VERTEX` / `_FOUNDRY` / `_MANTLE` /
1973
+ * `_ANTHROPIC_AWS`), alternate base URLs, file-descriptor / indirection token
1974
+ * sources, inline identity / OAuth / bearer tokens, and host-auth-deferral
1975
+ * flags. ANY of them, inherited from the user's shell, can route the agent OFF
1976
+ * the PostHog LLM gateway — billing the user's own Anthropic / Bedrock / Vertex
1977
+ * account ("wrong credits") or redirecting the binary to an arbitrary endpoint
1978
+ * and leaking the gateway token.
1979
+ *
1980
+ * Rather than chase each new knob by name — a denylist the binary outpaces every
1981
+ * release — we DROP THE ENTIRE PROVIDER NAMESPACE from the subprocess env and
1982
+ * re-inject the wizard's own gateway routing explicitly at the spawn site (see
1983
+ * `agent-interface.ts` / `mcp-prompt-streaming.ts`). The spawned binary then
1984
+ * runs with exactly the `ANTHROPIC_*` / `CLAUDE_CODE_*` values the wizard sets
1985
+ * and nothing else — no user shell/env value can leak in or outrank them.
1986
+ *
1987
+ * Generic system / build env (`PATH`, `HOME`, proxies, toolchain, `AWS_*` /
1988
+ * `GOOGLE_*`) is deliberately preserved: the agent's install / build commands
1989
+ * need it, and it's inert for model routing once every provider-activation flag
1990
+ * is stripped.
1991
+ */
1992
+ /**
1993
+ * The model-provider env namespace. Every routing / credential knob the binary
1994
+ * honors lives under one of these prefixes, so stripping the whole namespace is
1995
+ * a complete, maintenance-free replacement for an enumerated denylist.
1996
+ */
1997
+ const PROVIDER_ENV_NAMESPACE = /^(ANTHROPIC_|CLAUDE_CODE_)/;
1998
+ /**
1999
+ * Off-namespace credential that the binary can use without a provider-activation
2000
+ * flag, so the namespace rule alone wouldn't catch it. (Bedrock ignores it once
2001
+ * `CLAUDE_CODE_USE_BEDROCK` is gone, but strip it anyway — defense in depth.)
2002
+ */
2003
+ const BLOCKED_OFF_NAMESPACE_KEYS = new Set(["AWS_BEARER_TOKEN_BEDROCK"]);
2004
+ /**
2005
+ * Credential / routing knobs that, set in an on-disk Claude **settings** `env`
2006
+ * block, redirect the binary. A settings file is applied by the binary itself,
2007
+ * so the subprocess-env strip below cannot remove it — `claude-settings.ts`
2008
+ * detects these in settings files and backs up / fails closed instead. Kept as
2009
+ * a curated list there (not the broad namespace rule) so a benign
2010
+ * `CLAUDE_CODE_*` toggle in an org-managed settings file doesn't needlessly
2011
+ * fail the run closed. The subprocess env needs no such list — it strips the
2012
+ * whole namespace (`sanitizeAgentSubprocessEnv`).
2013
+ *
2014
+ * Keep `ANTHROPIC_BASE_URL` / `ANTHROPIC_AUTH_TOKEN` / `CLAUDE_CODE_OAUTH_TOKEN`
2015
+ * OUT of this list — `claude-settings.ts` adds those (the gateway routing the
2016
+ * wizard sets) to its own detection set separately.
2017
+ */
2018
+ const BLOCKED_AGENT_ENV_KEYS = [
2019
+ "ANTHROPIC_API_KEY",
2020
+ "CLAUDE_CODE_USE_BEDROCK",
2021
+ "CLAUDE_CODE_USE_VERTEX",
2022
+ "CLAUDE_CODE_USE_FOUNDRY",
2023
+ "CLAUDE_CODE_USE_MANTLE",
2024
+ "CLAUDE_CODE_USE_ANTHROPIC_AWS",
2025
+ "ANTHROPIC_BEDROCK_BASE_URL",
2026
+ "ANTHROPIC_AWS_BASE_URL",
2027
+ "ANTHROPIC_VERTEX_BASE_URL",
2028
+ "ANTHROPIC_FOUNDRY_BASE_URL",
2029
+ "ANTHROPIC_BEDROCK_MANTLE_BASE_URL",
2030
+ "CLAUDE_CODE_API_BASE_URL",
2031
+ "ANTHROPIC_AWS_API_KEY",
2032
+ "ANTHROPIC_FOUNDRY_API_KEY",
2033
+ "AWS_BEARER_TOKEN_BEDROCK",
2034
+ "ANTHROPIC_VERTEX_PROJECT_ID",
2035
+ "ANTHROPIC_PROFILE",
2036
+ "ANTHROPIC_ENVIRONMENT_KEY",
2037
+ "ANTHROPIC_IDENTITY_TOKEN",
2038
+ "ANTHROPIC_FEDERATION_RULE_ID",
2039
+ "ANTHROPIC_SERVICE_ACCOUNT_ID",
2040
+ "CLAUDE_CODE_API_KEY_FILE_DESCRIPTOR",
2041
+ "CLAUDE_CODE_OAUTH_TOKEN_FILE_DESCRIPTOR",
2042
+ "CLAUDE_CODE_WEBSOCKET_AUTH_FILE_DESCRIPTOR",
2043
+ "ANTHROPIC_IDENTITY_TOKEN_FILE",
2044
+ "CLAUDE_CODE_SESSION_ACCESS_TOKEN",
2045
+ "CLAUDE_CODE_CLIENT_KEY",
2046
+ "CLAUDE_CODE_OAUTH_REFRESH_TOKEN",
2047
+ "CLAUDE_CODE_HFI_BEARER_TOKEN",
2048
+ "CLAUDE_CODE_HOST_AUTH_ENV_VAR",
2049
+ "CLAUDE_CODE_PROVIDER_MANAGED_BY_HOST",
2050
+ "CLAUDE_CODE_SDK_HAS_HOST_AUTH_REFRESH",
2051
+ "CLAUDE_CODE_SDK_HAS_OAUTH_REFRESH",
2052
+ "CLAUDE_CODE_API_KEY_HELPER_TTL_MS"
2053
+ ];
2054
+ /**
2055
+ * Pattern-matched settings-file knobs (companion to {@link BLOCKED_AGENT_ENV_KEYS}):
2056
+ * - `CLAUDE_CODE_SKIP_*_AUTH` — skip-auth flags for every alt provider.
2057
+ * - `ANTHROPIC_*_BASE_URL` — alternate base URLs (NOT bare `ANTHROPIC_BASE_URL`,
2058
+ * which is the gateway routing the wizard sets).
2059
+ */
2060
+ const BLOCKED_AGENT_ENV_PATTERNS = [/^CLAUDE_CODE_SKIP_.+_AUTH$/, /^ANTHROPIC_.+_BASE_URL$/];
2061
+ /**
2062
+ * True if `key` must NOT be inherited by the agent subprocess: anything in the
2063
+ * provider namespace (the wizard re-injects its own values at the spawn site),
2064
+ * plus the lone off-namespace credential.
2065
+ */
2066
+ function isBlockedAgentEnvKey(key) {
2067
+ return PROVIDER_ENV_NAMESPACE.test(key) || BLOCKED_OFF_NAMESPACE_KEYS.has(key);
2068
+ }
2069
+ /**
2070
+ * Return a copy of `env` with the entire model-provider namespace removed, so
2071
+ * the spawned `claude` binary inherits no `ANTHROPIC_*` / `CLAUDE_CODE_*` value
2072
+ * from the user's shell. The wizard's own gateway routing is injected back
2073
+ * explicitly at the spawn site, so the binary can authenticate only through it.
2074
+ * Removed keys are absent from the result (Node's spawn omits them), not set to
2075
+ * `undefined`.
2076
+ */
2077
+ function sanitizeAgentSubprocessEnv(env = process.env) {
2078
+ const sanitized = {};
2079
+ for (const [key, value] of Object.entries(env)) {
2080
+ if (isBlockedAgentEnvKey(key)) continue;
2081
+ sanitized[key] = value;
2082
+ }
2083
+ return sanitized;
2084
+ }
2085
+ //#endregion
1942
2086
  //#region src/lib/agent/claude-settings.ts
1943
2087
  /**
1944
2088
  * Claude Code settings handling: conflict detection, backup/restore, and
@@ -1948,10 +2092,31 @@ var AgentOutputSignals = class {
1948
2092
  * process start without dragging in the agent stack (wizard-tools, yara
1949
2093
  * hooks, the SDK loader).
1950
2094
  */
2095
+ /** The file names Claude Code accepts for project settings, in priority order. */
2096
+ const SETTINGS_FILENAMES = ["settings.json", "settings"];
2097
+ /** Suffix for the wizard's pristine-copy backup of a settings file. */
2098
+ const BACKUP_SUFFIX = ".wizard-backup";
2099
+ /**
2100
+ * The candidate settings files under `<workingDirectory>/.claude` and their
2101
+ * backup paths. Centralizes the file-name list + backup suffix that backup,
2102
+ * restore, and orphan-recovery all walk.
2103
+ */
2104
+ function settingsBackupTargets(workingDirectory) {
2105
+ const claudeDir = path.join(workingDirectory, ".claude");
2106
+ return SETTINGS_FILENAMES.map((name) => {
2107
+ const original = path.join(claudeDir, name);
2108
+ return {
2109
+ claudeDir,
2110
+ original,
2111
+ backup: original + BACKUP_SUFFIX
2112
+ };
2113
+ });
2114
+ }
1951
2115
  const BLOCKING_ENV_KEYS = [
1952
- "ANTHROPIC_API_KEY",
1953
2116
  "ANTHROPIC_BASE_URL",
1954
- "ANTHROPIC_AUTH_TOKEN"
2117
+ "ANTHROPIC_AUTH_TOKEN",
2118
+ "CLAUDE_CODE_OAUTH_TOKEN",
2119
+ ...BLOCKED_AGENT_ENV_KEYS
1955
2120
  ];
1956
2121
  const BLOCKING_SETTINGS_KEYS = ["apiKeyHelper"];
1957
2122
  /**
@@ -1964,18 +2129,31 @@ function checkSettingsFile(filePath) {
1964
2129
  const parsed = JSON.parse(raw);
1965
2130
  const matched = [];
1966
2131
  const envBlock = parsed?.env;
1967
- if (envBlock && typeof envBlock === "object") matched.push(...BLOCKING_ENV_KEYS.filter((key) => key in envBlock));
2132
+ if (envBlock && typeof envBlock === "object") {
2133
+ matched.push(...BLOCKING_ENV_KEYS.filter((key) => key in envBlock));
2134
+ matched.push(...Object.keys(envBlock).filter((key) => BLOCKED_AGENT_ENV_PATTERNS.some((pattern) => pattern.test(key))));
2135
+ }
1968
2136
  matched.push(...BLOCKING_SETTINGS_KEYS.filter((key) => key in parsed && parsed[key] !== "" && parsed[key] != null));
1969
- return matched;
2137
+ return [...new Set(matched)];
1970
2138
  } catch {
1971
2139
  return [];
1972
2140
  }
1973
2141
  }
1974
2142
  /**
1975
- * Managed settings path on macOS.
1976
- * IT/MDM-deployed settings — readable by all users, writable only by root.
2143
+ * Org-managed (IT/MDM-deployed) settings path, per platform. Readable by all
2144
+ * users, writable only by root/admin. Claude Code always reads this file
2145
+ * regardless of `settingSources`, so an `apiKeyHelper` / `env` override here
2146
+ * redirects the agent off the gateway and the wizard cannot remove it — it
2147
+ * must fail closed. Checking only the macOS path (the prior behavior) let a
2148
+ * Linux/Windows managed override slip past the fail-closed gate undetected.
1977
2149
  */
1978
- const MANAGED_SETTINGS_PATH = "/Library/Application Support/ClaudeCode/managed-settings.json";
2150
+ function managedSettingsPath(platform = process.platform) {
2151
+ switch (platform) {
2152
+ case "darwin": return "/Library/Application Support/ClaudeCode/managed-settings.json";
2153
+ case "win32": return path.join(process.env.ProgramData || "C:\\ProgramData", "ClaudeCode", "managed-settings.json");
2154
+ default: return "/etc/claude-code/managed-settings.json";
2155
+ }
2156
+ }
1979
2157
  /**
1980
2158
  * Check every settings file Claude Code reads for blocking keys that conflict
1981
2159
  * with the wizard's gateway auth: org-managed, the user's global config, the
@@ -1983,13 +2161,13 @@ const MANAGED_SETTINGS_PATH = "/Library/Application Support/ClaudeCode/managed-s
1983
2161
  * an `apiKeyHelper` or `ANTHROPIC_*` override commonly lives, and any of them
1984
2162
  * can outrank the env the wizard sets and make every agent call 401.
1985
2163
  */
1986
- function checkAllSettingsConflicts(workingDirectory, homeDir = os.homedir()) {
2164
+ function checkAllSettingsConflicts(workingDirectory, homeDir = os.homedir(), platform = process.platform) {
1987
2165
  const conflicts = [];
1988
2166
  const home = homeDir;
1989
2167
  const sources = [
1990
2168
  {
1991
2169
  source: "managed",
1992
- paths: [MANAGED_SETTINGS_PATH],
2170
+ paths: [managedSettingsPath(platform)],
1993
2171
  writable: false
1994
2172
  },
1995
2173
  {
@@ -1999,7 +2177,7 @@ function checkAllSettingsConflicts(workingDirectory, homeDir = os.homedir()) {
1999
2177
  },
2000
2178
  {
2001
2179
  source: "project",
2002
- paths: [path.join(workingDirectory, ".claude", "settings.json"), path.join(workingDirectory, ".claude", "settings")],
2180
+ paths: SETTINGS_FILENAMES.map((name) => path.join(workingDirectory, ".claude", name)),
2003
2181
  writable: true
2004
2182
  },
2005
2183
  {
@@ -2023,6 +2201,22 @@ function checkAllSettingsConflicts(workingDirectory, homeDir = os.homedir()) {
2023
2201
  return conflicts;
2024
2202
  }
2025
2203
  /**
2204
+ * Sort detected conflicts by how the wizard can respond, following the rule:
2205
+ * **neutralize where possible; where it can't, let the user make the change.**
2206
+ *
2207
+ * NOTE: the `warnOnly` bucket assumes the agent runs with
2208
+ * `settingSources: ['project']` (set in `agent-interface.ts` and
2209
+ * `mcp-prompt-streaming.ts`). If that ever widens to include `'user'` / `'local'`,
2210
+ * those sources stop being neutralized and must move out of `warnOnly`.
2211
+ */
2212
+ function classifySettingsConflicts(conflicts) {
2213
+ return {
2214
+ autoFix: conflicts.filter((c) => c.writable),
2215
+ failClosed: conflicts.filter((c) => !c.writable && c.source === "managed"),
2216
+ warnOnly: conflicts.filter((c) => !c.writable && c.source !== "managed")
2217
+ };
2218
+ }
2219
+ /**
2026
2220
  * Ensure `.claude/.gitignore` excludes `*.wizard-backup` so a backup left
2027
2221
  * orphaned by an interrupted run can't get committed to git — settings files
2028
2222
  * commonly hold `apiKeyHelper` or `ANTHROPIC_API_KEY`, which we don't want
@@ -2030,7 +2224,7 @@ function checkAllSettingsConflicts(workingDirectory, homeDir = os.homedir()) {
2030
2224
  */
2031
2225
  function ensureBackupGitignored(claudeDir) {
2032
2226
  const gitignorePath = path.join(claudeDir, ".gitignore");
2033
- const entry = "*.wizard-backup";
2227
+ const entry = `*${BACKUP_SUFFIX}`;
2034
2228
  try {
2035
2229
  const existing = fs$1.existsSync(gitignorePath) ? fs$1.readFileSync(gitignorePath, "utf-8") : "";
2036
2230
  if (existing.split("\n").map((l) => l.trim()).includes(entry)) return;
@@ -2046,18 +2240,16 @@ function ensureBackupGitignored(claudeDir) {
2046
2240
  * cleanup.
2047
2241
  */
2048
2242
  function backupAndFixClaudeSettings(workingDirectory) {
2049
- for (const name of ["settings.json", "settings"]) {
2050
- const filePath = path.join(workingDirectory, ".claude", name);
2051
- if (!fs$1.existsSync(filePath)) continue;
2052
- const backupPath = `${filePath}.wizard-backup`;
2243
+ for (const { claudeDir, original, backup } of settingsBackupTargets(workingDirectory)) {
2244
+ if (!fs$1.existsSync(original)) continue;
2053
2245
  try {
2054
- if (!fs$1.existsSync(backupPath)) {
2055
- fs$1.copyFileSync(filePath, backupPath);
2056
- const mode = fs$1.statSync(filePath).mode & 511;
2057
- fs$1.chmodSync(backupPath, mode);
2246
+ if (!fs$1.existsSync(backup)) {
2247
+ fs$1.copyFileSync(original, backup);
2248
+ const mode = fs$1.statSync(original).mode & 511;
2249
+ fs$1.chmodSync(backup, mode);
2058
2250
  }
2059
- ensureBackupGitignored(path.join(workingDirectory, ".claude"));
2060
- fs$1.unlinkSync(filePath);
2251
+ ensureBackupGitignored(claudeDir);
2252
+ fs$1.unlinkSync(original);
2061
2253
  analytics.wizardCapture("backedup-claude-settings");
2062
2254
  registerCleanup(() => {
2063
2255
  try {
@@ -2083,9 +2275,7 @@ function backupAndFixClaudeSettings(workingDirectory) {
2083
2275
  * calls (outro then cleanup) idempotent and avoids leaving an orphan behind.
2084
2276
  */
2085
2277
  function restoreClaudeSettings(workingDirectory) {
2086
- for (const name of ["settings.json", "settings"]) {
2087
- const original = path.join(workingDirectory, ".claude", name);
2088
- const backup = `${original}.wizard-backup`;
2278
+ for (const { original, backup } of settingsBackupTargets(workingDirectory)) {
2089
2279
  if (!fs$1.existsSync(backup)) continue;
2090
2280
  try {
2091
2281
  fs$1.copyFileSync(backup, original);
@@ -2112,9 +2302,7 @@ function restoreClaudeSettings(workingDirectory) {
2112
2302
  * already moved them aside.
2113
2303
  */
2114
2304
  function recoverOrphanedSettingsBackups(workingDirectory) {
2115
- for (const name of ["settings.json", "settings"]) {
2116
- const original = path.join(workingDirectory, ".claude", name);
2117
- const backup = `${original}.wizard-backup`;
2305
+ for (const { original, backup } of settingsBackupTargets(workingDirectory)) {
2118
2306
  if (!fs$1.existsSync(backup) || fs$1.existsSync(original)) continue;
2119
2307
  try {
2120
2308
  fs$1.copyFileSync(backup, original);
@@ -2126,6 +2314,70 @@ function recoverOrphanedSettingsBackups(workingDirectory) {
2126
2314
  }
2127
2315
  }
2128
2316
  //#endregion
2317
+ //#region src/lib/agent/stored-login.ts
2318
+ /**
2319
+ * Detect a pre-existing Claude Code login on the machine.
2320
+ *
2321
+ * The wizard authenticates the agent's LLM calls by injecting its PostHog OAuth
2322
+ * token via `CLAUDE_CODE_OAUTH_TOKEN` / `ANTHROPIC_AUTH_TOKEN`. If the user is
2323
+ * already logged into Claude Code/Desktop, the Agent SDK can instead use that
2324
+ * stored `/login` credential (reported as `apiKeySource: "/login managed key"`)
2325
+ * and send it to the PostHog gateway, which rejects it with a 401.
2326
+ *
2327
+ * The settings-conflict scan ({@link checkAllSettingsConflicts}) only inspects
2328
+ * `settings.json` for `ANTHROPIC_*` overrides / `apiKeyHelper`; it has no
2329
+ * awareness of an actual logged-in session. This fills that gap so a run can
2330
+ * log and report the potential conflict behind a gateway 401.
2331
+ *
2332
+ * Existence-only: it never reads the credential value (no `-w`), so no secret
2333
+ * is ever exposed. The locations checked mirror the SDK's own resolution
2334
+ * (npmjs.com/package/@anthropic-ai/claude-agent-sdk → sdk.mjs): a
2335
+ * `.credentials.json` under `CLAUDE_CONFIG_DIR` / `~/.claude`, and the macOS
2336
+ * keychain item below.
2337
+ */
2338
+ /** macOS keychain service name Claude Code stores its login under. */
2339
+ const KEYCHAIN_SERVICE = "Claude Code-credentials";
2340
+ /** True when any stored login was found. */
2341
+ const hasStoredClaudeLogin = (login) => login.credentialsFile || login.keychain;
2342
+ /**
2343
+ * The directory Claude Code resolves credentials/config from — `CLAUDE_CONFIG_DIR`
2344
+ * if set, else `~/.claude`. Mirrors the SDK's own resolution; single source of
2345
+ * truth so callers can't drift from it.
2346
+ */
2347
+ const claudeConfigDir = (homeDir = os.homedir()) => process.env.CLAUDE_CONFIG_DIR || path.join(homeDir, ".claude");
2348
+ /**
2349
+ * Look for a stored Claude login. `homeDir` / `platform` are injectable for
2350
+ * tests; production uses the real home dir and platform.
2351
+ */
2352
+ function detectStoredClaudeLogin(homeDir = os.homedir(), platform = process.platform) {
2353
+ const credentialsPath = path.join(claudeConfigDir(homeDir), ".credentials.json");
2354
+ let credentialsFile = false;
2355
+ try {
2356
+ credentialsFile = fs$1.existsSync(credentialsPath);
2357
+ } catch (error) {
2358
+ logToFile(`[stored-login] checked ${credentialsPath} — unreadable (${error instanceof Error ? error.message : String(error)}), treating as absent`);
2359
+ analytics.wizardCapture("stored login check failed", { step: "credentials-file" });
2360
+ }
2361
+ let keychain = false;
2362
+ if (platform === "darwin") try {
2363
+ keychain = spawnSync("security", [
2364
+ "find-generic-password",
2365
+ "-s",
2366
+ KEYCHAIN_SERVICE
2367
+ ], {
2368
+ stdio: "ignore",
2369
+ timeout: 5e3
2370
+ }).status === 0;
2371
+ } catch (error) {
2372
+ logToFile(`[stored-login] checked keychain '${KEYCHAIN_SERVICE}' — lookup cleared (${error instanceof Error ? error.message : String(error)}), treating as absent`);
2373
+ analytics.wizardCapture("stored login check failed", { step: "keychain" });
2374
+ }
2375
+ return {
2376
+ credentialsFile,
2377
+ keychain
2378
+ };
2379
+ }
2380
+ //#endregion
2129
2381
  //#region src/lib/agent/agent-interface.ts
2130
2382
  /**
2131
2383
  * Shared agent interface for PostHog wizards
@@ -2149,15 +2401,29 @@ function regionFromGatewayUrl(gatewayUrl) {
2149
2401
  if (gatewayUrl.includes("localhost")) return "local";
2150
2402
  return gatewayUrl.includes("gateway.eu.") ? "eu" : "us";
2151
2403
  }
2152
- function buildAuthErrorContext(workingDirectory, gatewayUrl, homeDir = os.homedir()) {
2404
+ /** Places the agent could have picked up a non-PostHog credential. */
2405
+ function findCredentialPlaces(conflicts, homeDir) {
2406
+ const places = [];
2407
+ const stored = detectStoredClaudeLogin(homeDir);
2408
+ const configDir = claudeConfigDir(homeDir);
2409
+ if (stored.credentialsFile) places.push(`A logged-in Claude session: ${path.join(configDir, ".credentials.json")}`);
2410
+ if (stored.keychain) places.push("A logged-in Claude session: macOS keychain item \"Claude Code-credentials\"");
2411
+ for (const c of conflicts) places.push(`${c.path} sets ${c.keys.join(", ")}`);
2412
+ return places;
2413
+ }
2414
+ function buildAuthErrorContext(workingDirectory, gatewayUrl, homeDir = os.homedir(), apiKeySource) {
2153
2415
  const conflicts = checkAllSettingsConflicts(workingDirectory, homeDir);
2416
+ const usingManagedLogin = /login|managed key/i.test(apiKeySource ?? "");
2154
2417
  return {
2155
2418
  hasSettingsConflict: conflicts.length > 0,
2156
2419
  conflicts,
2157
2420
  conflictSources: conflicts.map((c) => c.source),
2158
2421
  conflictKeys: [...new Set(conflicts.flatMap((c) => c.keys))],
2159
2422
  gatewayUrl,
2160
- region: regionFromGatewayUrl(gatewayUrl)
2423
+ region: regionFromGatewayUrl(gatewayUrl),
2424
+ apiKeySource,
2425
+ usingManagedLogin,
2426
+ credentialPlaces: findCredentialPlaces(conflicts, homeDir)
2161
2427
  };
2162
2428
  }
2163
2429
  /**
@@ -2451,6 +2717,14 @@ async function initializeAgent(config, options) {
2451
2717
  process.env.CLAUDE_CODE_OAUTH_TOKEN = config.posthogApiKey;
2452
2718
  logToFile("Configured LLM gateway:", gatewayUrl);
2453
2719
  logToFile("API key prefix:", config.posthogApiKey ? `${config.posthogApiKey.slice(0, 4)}***` : "(missing)");
2720
+ const storedLogin = detectStoredClaudeLogin();
2721
+ if (hasStoredClaudeLogin(storedLogin)) {
2722
+ logToFile(`Pre-existing Claude login detected (credentialsFile=${storedLogin.credentialsFile}, keychain=${storedLogin.keychain}). It can outrank the wizard's gateway token and cause a 401 — 'claude auth logout' clears it.`);
2723
+ analytics.wizardCapture("claude stored login detected", {
2724
+ credentials_file: storedLogin.credentialsFile,
2725
+ keychain: storedLogin.keychain
2726
+ });
2727
+ }
2454
2728
  const initConflicts = checkAllSettingsConflicts(options.installDir);
2455
2729
  logToFile("Settings conflicts at agent init:", initConflicts.length > 0 ? initConflicts.map((c) => `${c.source}(${c.keys.join(",")})`).join("; ") : "none");
2456
2730
  const mcpServers = {
@@ -2650,8 +2924,11 @@ async function runAgent(agentConfig, prompt, options, spinner, config, middlewar
2650
2924
  ] }
2651
2925
  },
2652
2926
  env: {
2653
- ...process.env,
2654
- ANTHROPIC_API_KEY: void 0,
2927
+ ...sanitizeAgentSubprocessEnv(process.env),
2928
+ ANTHROPIC_BASE_URL: process.env.ANTHROPIC_BASE_URL,
2929
+ ANTHROPIC_AUTH_TOKEN: process.env.ANTHROPIC_AUTH_TOKEN,
2930
+ CLAUDE_CODE_OAUTH_TOKEN: process.env.CLAUDE_CODE_OAUTH_TOKEN,
2931
+ CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS: "true",
2655
2932
  ENABLE_TOOL_SEARCH: "auto:0",
2656
2933
  MCP_CONNECTION_NONBLOCKING: "0",
2657
2934
  ANTHROPIC_CUSTOM_HEADERS: buildAgentEnv(agentConfig.wizardMetadata ?? {}, agentConfig.wizardFlags ?? {})
@@ -2728,11 +3005,13 @@ async function runAgent(agentConfig, prompt, options, spinner, config, middlewar
2728
3005
  if (message.type === "assistant" && signals.hasApiErrorStatus(401)) {
2729
3006
  signalDone();
2730
3007
  spinner.stop("Authentication failed");
2731
- const authError = buildAuthErrorContext(options.installDir, process.env.ANTHROPIC_BASE_URL ?? "");
3008
+ const authError = buildAuthErrorContext(options.installDir, process.env.ANTHROPIC_BASE_URL ?? "", os.homedir(), signals.apiKeySource);
2732
3009
  logToFile("Agent error: 401, showing auth error screen", authError);
2733
3010
  getUI().showAuthError({
2734
3011
  hasSettingsConflict: authError.hasSettingsConflict,
2735
3012
  conflicts: authError.conflicts,
3013
+ usingManagedLogin: authError.usingManagedLogin,
3014
+ credentialPlaces: authError.credentialPlaces,
2736
3015
  logFilePath: getLogFilePath()
2737
3016
  });
2738
3017
  await wizardAbort({
@@ -2742,7 +3021,9 @@ async function runAgent(agentConfig, prompt, options, spinner, config, middlewar
2742
3021
  conflictSources: authError.conflictSources,
2743
3022
  conflictKeys: authError.conflictKeys,
2744
3023
  gatewayUrl: authError.gatewayUrl,
2745
- region: authError.region
3024
+ region: authError.region,
3025
+ usingManagedLogin: authError.usingManagedLogin,
3026
+ apiKeySource: authError.apiKeySource
2746
3027
  })
2747
3028
  });
2748
3029
  }
@@ -3044,11 +3325,15 @@ function handleSDKMessage(message, options, spinner, signals, receivedSuccessRes
3044
3325
  }
3045
3326
  break;
3046
3327
  case "system":
3047
- if (message.subtype === "init") logToFile("Agent session initialized", {
3048
- model: message.model,
3049
- tools: message.tools?.length,
3050
- mcpServers: message.mcp_servers
3051
- });
3328
+ if (message.subtype === "init") {
3329
+ signals.recordApiKeySource(message.apiKeySource);
3330
+ logToFile("Agent session initialized", {
3331
+ model: message.model,
3332
+ tools: message.tools?.length,
3333
+ mcpServers: message.mcp_servers,
3334
+ apiKeySource: message.apiKeySource
3335
+ });
3336
+ }
3052
3337
  break;
3053
3338
  default:
3054
3339
  if (options.debug) debug(`Unhandled message type: ${message.type}`);
@@ -3056,6 +3341,6 @@ function handleSDKMessage(message, options, spinner, signals, receivedSuccessRes
3056
3341
  }
3057
3342
  }
3058
3343
  //#endregion
3059
- export { AUDIT_CHECKS_FILE as C, coerceAuditChecks as D, AUDIT_SEVERITY_STYLE as E, getAuditChecks as O, TaskStatus as S, AUDIT_REPORT_FILE as T, downloadSkill as _, runAgent as a, QUEUE_DIR_NAME as b, recoverOrphanedSettingsBackups as c, flushScanReport as d, formatScanReport as f, WIZARD_TOOL_NAMES as g, SETUP_REPORT_FILE as h, isOrchestratorEnabled as i, restoreClaudeSettings as l, EVENT_PLAN_FILE as m, buildRunTags as n, backupAndFixClaudeSettings as o, writeScanReport as p, initializeAgent as r, checkAllSettingsConflicts as s, buildAgentEnv as t, AgentSignals as u, fetchSkillMenu as v, AUDIT_CHECKS_KEY as w, QueueStore as x, installSkillById as y };
3344
+ export { getAuditChecks as A, QueueStore as C, AUDIT_REPORT_FILE as D, AUDIT_CHECKS_KEY as E, AUDIT_SEVERITY_STYLE as O, QUEUE_DIR_NAME as S, AUDIT_CHECKS_FILE as T, SETUP_REPORT_FILE as _, runAgent as a, fetchSkillMenu as b, classifySettingsConflicts as c, sanitizeAgentSubprocessEnv as d, AgentSignals as f, EVENT_PLAN_FILE as g, writeScanReport as h, isOrchestratorEnabled as i, coerceAuditChecks as k, recoverOrphanedSettingsBackups as l, formatScanReport as m, buildRunTags as n, backupAndFixClaudeSettings as o, flushScanReport as p, initializeAgent as r, checkAllSettingsConflicts as s, buildAgentEnv as t, restoreClaudeSettings as u, WIZARD_TOOL_NAMES as v, TaskStatus as w, installSkillById as x, downloadSkill as y };
3060
3345
 
3061
- //# sourceMappingURL=agent-interface-c7B2JZEd.js.map
3346
+ //# sourceMappingURL=agent-interface-BcUv1qeh.js.map