@posthog/wizard 2.33.0 → 2.35.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 (83) hide show
  1. package/dist/{add-mcp-server-to-clients-Dhq75Hda.js → add-mcp-server-to-clients-to65gLPl.js} +5 -5
  2. package/dist/{add-mcp-server-to-clients-Dhq75Hda.js.map → add-mcp-server-to-clients-to65gLPl.js.map} +1 -1
  3. package/dist/{agent-interface-DCeQ5Oiw.js → agent-interface-CXL9i_vX.js} +362 -48
  4. package/dist/agent-interface-CXL9i_vX.js.map +1 -0
  5. package/dist/{agent-runner-BRajaO84.js → agent-runner-CCTTG_Rn.js} +33 -14
  6. package/dist/agent-runner-CCTTG_Rn.js.map +1 -0
  7. package/dist/{analytics-CU4o3eF8.js → analytics-DP_j88IC.js} +2 -2
  8. package/dist/{analytics-CU4o3eF8.js.map → analytics-DP_j88IC.js.map} +1 -1
  9. package/dist/{api-BBRfHEZ-.js → api-Bdxv7mWH.js} +3 -3
  10. package/dist/{api-BBRfHEZ-.js.map → api-Bdxv7mWH.js.map} +1 -1
  11. package/dist/bin.js +78 -61
  12. package/dist/bin.js.map +1 -1
  13. package/dist/check-screens.tsx +25 -0
  14. package/dist/{ci-install-D-otkGW6.js → ci-install-BO-KLPLD.js} +5 -5
  15. package/dist/ci-install-BO-KLPLD.js.map +1 -0
  16. package/dist/{debug-BewTLNNr.js → debug-Clw8ctxh.js} +1 -1
  17. package/dist/{debug-BGMe1wP6.js → debug-DXWxKT5f.js} +3 -4
  18. package/dist/debug-DXWxKT5f.js.map +1 -0
  19. package/dist/{defaults-DII5CAog.js → defaults-4rSse24X.js} +1 -1
  20. package/dist/{defaults-DII5CAog.js.map → defaults-4rSse24X.js.map} +1 -1
  21. package/dist/{env-api-key-MlzJYAvt.js → env-api-key-B3gE9Un0.js} +1 -1
  22. package/dist/{env-api-key-MlzJYAvt.js.map → env-api-key-B3gE9Un0.js.map} +1 -1
  23. package/dist/{environment-G41UFzou.js → environment-C4HyCWTC.js} +3 -3
  24. package/dist/{environment-G41UFzou.js.map → environment-C4HyCWTC.js.map} +1 -1
  25. package/dist/{file-utils-D9InAvZd.js → file-utils-CW8XbbZP.js} +2 -2
  26. package/dist/{file-utils-D9InAvZd.js.map → file-utils-CW8XbbZP.js.map} +1 -1
  27. package/dist/{headless-ui-xjHVVUqe.js → headless-ui-BoZYhNpE.js} +2 -2
  28. package/dist/{headless-ui-xjHVVUqe.js.map → headless-ui-BoZYhNpE.js.map} +1 -1
  29. package/dist/host-resolution-Dw0fS8h5.js +115 -0
  30. package/dist/host-resolution-Dw0fS8h5.js.map +1 -0
  31. package/dist/{interactive-D2CKikzz.js → interactive-pwrc7QM-.js} +2 -2
  32. package/dist/{interactive-D2CKikzz.js.map → interactive-pwrc7QM-.js.map} +1 -1
  33. package/dist/{mcp-prompt-streaming-FrArGwfv.js → mcp-prompt-streaming-DZjjoeu3.js} +10 -7
  34. package/dist/mcp-prompt-streaming-DZjjoeu3.js.map +1 -0
  35. package/dist/{non-interactive-BXx6Q-D0.js → non-interactive-BW-MZYrP.js} +2 -2
  36. package/dist/{non-interactive-BXx6Q-D0.js.map → non-interactive-BW-MZYrP.js.map} +1 -1
  37. package/dist/{package-manager-D9ojA4jO.js → package-manager-BgXgJ1Mu.js} +2 -2
  38. package/dist/{package-manager-D9ojA4jO.js.map → package-manager-BgXgJ1Mu.js.map} +1 -1
  39. package/dist/{playground-Bgtxrusl.js → playground-xuLBqzJM.js} +6 -6
  40. package/dist/{playground-Bgtxrusl.js.map → playground-xuLBqzJM.js.map} +1 -1
  41. package/dist/{posthog-DU6JXG00.js → posthog-BrLFkaji.js} +1 -1
  42. package/dist/{posthog-DU6JXG00.js.map → posthog-BrLFkaji.js.map} +1 -1
  43. package/dist/{posthog-integration-gnC9v4kg.js → posthog-integration-Bj9WmwDq.js} +16 -16
  44. package/dist/{posthog-integration-gnC9v4kg.js.map → posthog-integration-Bj9WmwDq.js.map} +1 -1
  45. package/dist/{provisioning-C3qglLdc.js → provisioning-BF2reo5_.js} +28 -9
  46. package/dist/provisioning-BF2reo5_.js.map +1 -0
  47. package/dist/{registry-BVrvFTZ0.js → registry-Dj17F0a-.js} +4 -4
  48. package/dist/{registry-BVrvFTZ0.js.map → registry-Dj17F0a-.js.map} +1 -1
  49. package/dist/{setup-utils-CanVlGbX.js → setup-utils-DyTlaGOV.js} +100 -31
  50. package/dist/setup-utils-DyTlaGOV.js.map +1 -0
  51. package/dist/{start-tui-YE7bybIr.js → start-tui-S4fBBfmO.js} +176 -38
  52. package/dist/{start-tui-YE7bybIr.js.map → start-tui-S4fBBfmO.js.map} +1 -1
  53. package/dist/{steps-N20e4IoE.js → steps-CHgikk6q.js} +7 -7
  54. package/dist/{steps-N20e4IoE.js.map → steps-CHgikk6q.js.map} +1 -1
  55. package/dist/{store-D15C9YSW.js → store-vr5j37CU.js} +4 -4
  56. package/dist/{store-D15C9YSW.js.map → store-vr5j37CU.js.map} +1 -1
  57. package/dist/{task-stream-CPjpHFhI.js → task-stream-BVF6gi8x.js} +3 -3
  58. package/dist/{task-stream-CPjpHFhI.js.map → task-stream-BVF6gi8x.js.map} +1 -1
  59. package/dist/{telemetry-DknCDWP6.js → telemetry-CR-ilpWe.js} +3 -3
  60. package/dist/{telemetry-DknCDWP6.js.map → telemetry-CR-ilpWe.js.map} +1 -1
  61. package/dist/{terminal-Cvv0a-7Q.js → terminal-uzjco-wz.js} +86 -35
  62. package/dist/terminal-uzjco-wz.js.map +1 -0
  63. package/dist/urls-ChWit8SE.js +67 -0
  64. package/dist/urls-ChWit8SE.js.map +1 -0
  65. package/dist/{wizard-abort-Cw818xPr.js → wizard-abort-BE_j8xUt.js} +4 -4
  66. package/dist/{wizard-abort-Cw818xPr.js.map → wizard-abort-BE_j8xUt.js.map} +1 -1
  67. package/dist/{wizard-abort-DMvS0YXD.js → wizard-abort-CNYxdjJl.js} +1 -1
  68. package/dist/{wizard-session-CN55LYyZ.js → wizard-session-BHoLQueN.js} +2 -1
  69. package/dist/wizard-session-BHoLQueN.js.map +1 -0
  70. package/dist/{wizard-session-BKEdX9mO.js → wizard-session-D0PJ9lsl.js} +1 -1
  71. package/dist/wizard-ui-WZ48rUgr.js.map +1 -1
  72. package/package.json +1 -1
  73. package/dist/agent-interface-DCeQ5Oiw.js.map +0 -1
  74. package/dist/agent-runner-BRajaO84.js.map +0 -1
  75. package/dist/ci-install-D-otkGW6.js.map +0 -1
  76. package/dist/debug-BGMe1wP6.js.map +0 -1
  77. package/dist/mcp-prompt-streaming-FrArGwfv.js.map +0 -1
  78. package/dist/provisioning-C3qglLdc.js.map +0 -1
  79. package/dist/setup-utils-CanVlGbX.js.map +0 -1
  80. package/dist/terminal-Cvv0a-7Q.js.map +0 -1
  81. package/dist/urls-DrR6F_A3.js +0 -35
  82. package/dist/urls-DrR6F_A3.js.map +0 -1
  83. package/dist/wizard-session-CN55LYyZ.js.map +0 -1
@@ -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-BGMe1wP6.js";
3
- import { t as analytics } from "./analytics-CU4o3eF8.js";
4
- import { i as getLlmGatewayUrlFromHost } from "./urls-DrR6F_A3.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-Cw818xPr.js";
2
+ import { B as POSTHOG_PROPERTY_HEADER_PREFIX, L as POSTHOG_FLAG_HEADER_PREFIX, Y as WIZARD_REMARK_EVENT_NAME, Z as WIZARD_USER_AGENT, a as getLogFilePath, f as skillTmpPath, nt as runtimeEnv, o as initLogFile, p as getUI, q as WIZARD_ORCHESTRATOR_FLAG_KEY, r as debug, s as logToFile, u as WIZARD_YARA_REPORT_FILE } from "./debug-DXWxKT5f.js";
3
+ import { t as analytics } from "./analytics-DP_j88IC.js";
4
+ import { t as HostResolution } from "./host-resolution-Dw0fS8h5.js";
5
+ import { n as ADDITIONAL_FEATURE_PROMPTS } from "./wizard-session-BHoLQueN.js";
6
+ import { i as wizardAbort, n as registerCleanup, t as WizardError } from "./wizard-abort-BE_j8xUt.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";
@@ -1362,6 +1363,35 @@ function writeScanReport() {
1362
1363
  }
1363
1364
  return WIZARD_YARA_REPORT_FILE;
1364
1365
  }
1366
+ const CATEGORY_DESCRIPTIONS = {
1367
+ prompt_injection: "hit hidden instructions in a file it read, a possible attempt to hijack the setup",
1368
+ exfiltration: "tried to send data to an outside location",
1369
+ destructive_operations: "tried to run a destructive command",
1370
+ supply_chain: "tried to pull in untrusted code",
1371
+ posthog_pii: "tried to write personal data (PII) into your project",
1372
+ posthog_hardcoded_key: "tried to hardcode a PostHog key into your project",
1373
+ hardcoded_secret: "tried to hardcode a secret into your project"
1374
+ };
1375
+ /** Most recent violation that terminated the run, or null. */
1376
+ function lastTerminatingViolation() {
1377
+ for (let i = scanViolations.length - 1; i >= 0; i--) if (scanViolations[i].action === "aborted") return scanViolations[i];
1378
+ return null;
1379
+ }
1380
+ /**
1381
+ * Build the user-facing copy for the security abort screen: what the scanner
1382
+ * caught and what we did about it. This screen renders only because the run was
1383
+ * stopped, so the "what we did" half is constant. Falls back to a generic line
1384
+ * when no specific violation was recorded (e.g. the scanner itself errored and
1385
+ * failed closed before a match was logged).
1386
+ */
1387
+ function formatYaraAbortMessage() {
1388
+ const v = lastTerminatingViolation();
1389
+ return `Security check stopped the setup.
1390
+
1391
+ The agent ${(v && CATEGORY_DESCRIPTIONS[v.category]) ?? "did something our security scanner flagged"}. We stopped the run to keep your project safe.\nNo unsafe changes were left in your project.
1392
+
1393
+ If this looks wrong, let us know: wizard@posthog.com`;
1394
+ }
1365
1395
  /** Timeout for scan hooks (PreToolUse, PostToolUse Write/Edit/Read/Grep) */
1366
1396
  const HOOK_TIMEOUT_MS = 3e4;
1367
1397
  /** Timeout for the skill install hook (filesystem I/O + multiple scans) */
@@ -1909,10 +1939,31 @@ const OUTPUT_SIGNALS = {
1909
1939
  const SIGNAL_NEEDLES = Object.values(OUTPUT_SIGNALS);
1910
1940
  var AgentOutputSignals = class {
1911
1941
  lines = [];
1942
+ apiKeySourceValue;
1912
1943
  /** Parse step: keep the line only if it carries a known signal; drop prose. */
1913
1944
  push(text) {
1914
1945
  if (SIGNAL_NEEDLES.some((n) => text.includes(n))) this.lines.push(text);
1915
1946
  }
1947
+ /**
1948
+ * Record the SDK's `apiKeySource` from its `init` message (e.g.
1949
+ * `"/login managed key"`, `"ANTHROPIC_API_KEY"`). Used to triage a 401:
1950
+ * a managed-login source means the SDK authenticated with a stored Claude
1951
+ * login rather than the gateway token the wizard injected.
1952
+ */
1953
+ recordApiKeySource(source) {
1954
+ if (source) this.apiKeySourceValue = source;
1955
+ }
1956
+ get apiKeySource() {
1957
+ return this.apiKeySourceValue;
1958
+ }
1959
+ /**
1960
+ * True when the SDK authed from a stored `/login` credential (the
1961
+ * "managed key") instead of an explicit `ANTHROPIC_API_KEY` — the signature
1962
+ * of conflicting Anthropic credentials behind a gateway 401.
1963
+ */
1964
+ usedManagedLogin() {
1965
+ return /login|managed key/i.test(this.apiKeySourceValue ?? "");
1966
+ }
1916
1967
  get text() {
1917
1968
  return this.lines.join("\n");
1918
1969
  }
@@ -1939,6 +1990,128 @@ var AgentOutputSignals = class {
1939
1990
  }
1940
1991
  };
1941
1992
  //#endregion
1993
+ //#region src/lib/agent/agent-env-isolation.ts
1994
+ /**
1995
+ * Credential isolation for the Claude Agent SDK subprocess.
1996
+ *
1997
+ * The SDK doesn't authenticate in-process — `query()` extracts and spawns a
1998
+ * native `claude` binary that resolves model routing and credentials from its
1999
+ * own environment. That binary honors a large, ever-growing surface of
2000
+ * `ANTHROPIC_*` / `CLAUDE_CODE_*` env vars: provider-activation flags
2001
+ * (`CLAUDE_CODE_USE_BEDROCK` / `_VERTEX` / `_FOUNDRY` / `_MANTLE` /
2002
+ * `_ANTHROPIC_AWS`), alternate base URLs, file-descriptor / indirection token
2003
+ * sources, inline identity / OAuth / bearer tokens, and host-auth-deferral
2004
+ * flags. ANY of them, inherited from the user's shell, can route the agent OFF
2005
+ * the PostHog LLM gateway — billing the user's own Anthropic / Bedrock / Vertex
2006
+ * account ("wrong credits") or redirecting the binary to an arbitrary endpoint
2007
+ * and leaking the gateway token.
2008
+ *
2009
+ * Rather than chase each new knob by name — a denylist the binary outpaces every
2010
+ * release — we DROP THE ENTIRE PROVIDER NAMESPACE from the subprocess env and
2011
+ * re-inject the wizard's own gateway routing explicitly at the spawn site (see
2012
+ * `agent-interface.ts` / `mcp-prompt-streaming.ts`). The spawned binary then
2013
+ * runs with exactly the `ANTHROPIC_*` / `CLAUDE_CODE_*` values the wizard sets
2014
+ * and nothing else — no user shell/env value can leak in or outrank them.
2015
+ *
2016
+ * Generic system / build env (`PATH`, `HOME`, proxies, toolchain, `AWS_*` /
2017
+ * `GOOGLE_*`) is deliberately preserved: the agent's install / build commands
2018
+ * need it, and it's inert for model routing once every provider-activation flag
2019
+ * is stripped.
2020
+ */
2021
+ /**
2022
+ * The model-provider env namespace. Every routing / credential knob the binary
2023
+ * honors lives under one of these prefixes, so stripping the whole namespace is
2024
+ * a complete, maintenance-free replacement for an enumerated denylist.
2025
+ */
2026
+ const PROVIDER_ENV_NAMESPACE = /^(ANTHROPIC_|CLAUDE_CODE_)/;
2027
+ /**
2028
+ * Off-namespace credential that the binary can use without a provider-activation
2029
+ * flag, so the namespace rule alone wouldn't catch it. (Bedrock ignores it once
2030
+ * `CLAUDE_CODE_USE_BEDROCK` is gone, but strip it anyway — defense in depth.)
2031
+ */
2032
+ const BLOCKED_OFF_NAMESPACE_KEYS = new Set(["AWS_BEARER_TOKEN_BEDROCK"]);
2033
+ /**
2034
+ * Credential / routing knobs that, set in an on-disk Claude **settings** `env`
2035
+ * block, redirect the binary. A settings file is applied by the binary itself,
2036
+ * so the subprocess-env strip below cannot remove it — `claude-settings.ts`
2037
+ * detects these in settings files and backs up / fails closed instead. Kept as
2038
+ * a curated list there (not the broad namespace rule) so a benign
2039
+ * `CLAUDE_CODE_*` toggle in an org-managed settings file doesn't needlessly
2040
+ * fail the run closed. The subprocess env needs no such list — it strips the
2041
+ * whole namespace (`sanitizeAgentSubprocessEnv`).
2042
+ *
2043
+ * Keep `ANTHROPIC_BASE_URL` / `ANTHROPIC_AUTH_TOKEN` / `CLAUDE_CODE_OAUTH_TOKEN`
2044
+ * OUT of this list — `claude-settings.ts` adds those (the gateway routing the
2045
+ * wizard sets) to its own detection set separately.
2046
+ */
2047
+ const BLOCKED_AGENT_ENV_KEYS = [
2048
+ "ANTHROPIC_API_KEY",
2049
+ "CLAUDE_CODE_USE_BEDROCK",
2050
+ "CLAUDE_CODE_USE_VERTEX",
2051
+ "CLAUDE_CODE_USE_FOUNDRY",
2052
+ "CLAUDE_CODE_USE_MANTLE",
2053
+ "CLAUDE_CODE_USE_ANTHROPIC_AWS",
2054
+ "ANTHROPIC_BEDROCK_BASE_URL",
2055
+ "ANTHROPIC_AWS_BASE_URL",
2056
+ "ANTHROPIC_VERTEX_BASE_URL",
2057
+ "ANTHROPIC_FOUNDRY_BASE_URL",
2058
+ "ANTHROPIC_BEDROCK_MANTLE_BASE_URL",
2059
+ "CLAUDE_CODE_API_BASE_URL",
2060
+ "ANTHROPIC_AWS_API_KEY",
2061
+ "ANTHROPIC_FOUNDRY_API_KEY",
2062
+ "AWS_BEARER_TOKEN_BEDROCK",
2063
+ "ANTHROPIC_VERTEX_PROJECT_ID",
2064
+ "ANTHROPIC_PROFILE",
2065
+ "ANTHROPIC_ENVIRONMENT_KEY",
2066
+ "ANTHROPIC_IDENTITY_TOKEN",
2067
+ "ANTHROPIC_FEDERATION_RULE_ID",
2068
+ "ANTHROPIC_SERVICE_ACCOUNT_ID",
2069
+ "CLAUDE_CODE_API_KEY_FILE_DESCRIPTOR",
2070
+ "CLAUDE_CODE_OAUTH_TOKEN_FILE_DESCRIPTOR",
2071
+ "CLAUDE_CODE_WEBSOCKET_AUTH_FILE_DESCRIPTOR",
2072
+ "ANTHROPIC_IDENTITY_TOKEN_FILE",
2073
+ "CLAUDE_CODE_SESSION_ACCESS_TOKEN",
2074
+ "CLAUDE_CODE_CLIENT_KEY",
2075
+ "CLAUDE_CODE_OAUTH_REFRESH_TOKEN",
2076
+ "CLAUDE_CODE_HFI_BEARER_TOKEN",
2077
+ "CLAUDE_CODE_HOST_AUTH_ENV_VAR",
2078
+ "CLAUDE_CODE_PROVIDER_MANAGED_BY_HOST",
2079
+ "CLAUDE_CODE_SDK_HAS_HOST_AUTH_REFRESH",
2080
+ "CLAUDE_CODE_SDK_HAS_OAUTH_REFRESH",
2081
+ "CLAUDE_CODE_API_KEY_HELPER_TTL_MS"
2082
+ ];
2083
+ /**
2084
+ * Pattern-matched settings-file knobs (companion to {@link BLOCKED_AGENT_ENV_KEYS}):
2085
+ * - `CLAUDE_CODE_SKIP_*_AUTH` — skip-auth flags for every alt provider.
2086
+ * - `ANTHROPIC_*_BASE_URL` — alternate base URLs (NOT bare `ANTHROPIC_BASE_URL`,
2087
+ * which is the gateway routing the wizard sets).
2088
+ */
2089
+ const BLOCKED_AGENT_ENV_PATTERNS = [/^CLAUDE_CODE_SKIP_.+_AUTH$/, /^ANTHROPIC_.+_BASE_URL$/];
2090
+ /**
2091
+ * True if `key` must NOT be inherited by the agent subprocess: anything in the
2092
+ * provider namespace (the wizard re-injects its own values at the spawn site),
2093
+ * plus the lone off-namespace credential.
2094
+ */
2095
+ function isBlockedAgentEnvKey(key) {
2096
+ return PROVIDER_ENV_NAMESPACE.test(key) || BLOCKED_OFF_NAMESPACE_KEYS.has(key);
2097
+ }
2098
+ /**
2099
+ * Return a copy of `env` with the entire model-provider namespace removed, so
2100
+ * the spawned `claude` binary inherits no `ANTHROPIC_*` / `CLAUDE_CODE_*` value
2101
+ * from the user's shell. The wizard's own gateway routing is injected back
2102
+ * explicitly at the spawn site, so the binary can authenticate only through it.
2103
+ * Removed keys are absent from the result (Node's spawn omits them), not set to
2104
+ * `undefined`.
2105
+ */
2106
+ function sanitizeAgentSubprocessEnv(env = process.env) {
2107
+ const sanitized = {};
2108
+ for (const [key, value] of Object.entries(env)) {
2109
+ if (isBlockedAgentEnvKey(key)) continue;
2110
+ sanitized[key] = value;
2111
+ }
2112
+ return sanitized;
2113
+ }
2114
+ //#endregion
1942
2115
  //#region src/lib/agent/claude-settings.ts
1943
2116
  /**
1944
2117
  * Claude Code settings handling: conflict detection, backup/restore, and
@@ -1948,10 +2121,31 @@ var AgentOutputSignals = class {
1948
2121
  * process start without dragging in the agent stack (wizard-tools, yara
1949
2122
  * hooks, the SDK loader).
1950
2123
  */
2124
+ /** The file names Claude Code accepts for project settings, in priority order. */
2125
+ const SETTINGS_FILENAMES = ["settings.json", "settings"];
2126
+ /** Suffix for the wizard's pristine-copy backup of a settings file. */
2127
+ const BACKUP_SUFFIX = ".wizard-backup";
2128
+ /**
2129
+ * The candidate settings files under `<workingDirectory>/.claude` and their
2130
+ * backup paths. Centralizes the file-name list + backup suffix that backup,
2131
+ * restore, and orphan-recovery all walk.
2132
+ */
2133
+ function settingsBackupTargets(workingDirectory) {
2134
+ const claudeDir = path.join(workingDirectory, ".claude");
2135
+ return SETTINGS_FILENAMES.map((name) => {
2136
+ const original = path.join(claudeDir, name);
2137
+ return {
2138
+ claudeDir,
2139
+ original,
2140
+ backup: original + BACKUP_SUFFIX
2141
+ };
2142
+ });
2143
+ }
1951
2144
  const BLOCKING_ENV_KEYS = [
1952
- "ANTHROPIC_API_KEY",
1953
2145
  "ANTHROPIC_BASE_URL",
1954
- "ANTHROPIC_AUTH_TOKEN"
2146
+ "ANTHROPIC_AUTH_TOKEN",
2147
+ "CLAUDE_CODE_OAUTH_TOKEN",
2148
+ ...BLOCKED_AGENT_ENV_KEYS
1955
2149
  ];
1956
2150
  const BLOCKING_SETTINGS_KEYS = ["apiKeyHelper"];
1957
2151
  /**
@@ -1964,18 +2158,31 @@ function checkSettingsFile(filePath) {
1964
2158
  const parsed = JSON.parse(raw);
1965
2159
  const matched = [];
1966
2160
  const envBlock = parsed?.env;
1967
- if (envBlock && typeof envBlock === "object") matched.push(...BLOCKING_ENV_KEYS.filter((key) => key in envBlock));
2161
+ if (envBlock && typeof envBlock === "object") {
2162
+ matched.push(...BLOCKING_ENV_KEYS.filter((key) => key in envBlock));
2163
+ matched.push(...Object.keys(envBlock).filter((key) => BLOCKED_AGENT_ENV_PATTERNS.some((pattern) => pattern.test(key))));
2164
+ }
1968
2165
  matched.push(...BLOCKING_SETTINGS_KEYS.filter((key) => key in parsed && parsed[key] !== "" && parsed[key] != null));
1969
- return matched;
2166
+ return [...new Set(matched)];
1970
2167
  } catch {
1971
2168
  return [];
1972
2169
  }
1973
2170
  }
1974
2171
  /**
1975
- * Managed settings path on macOS.
1976
- * IT/MDM-deployed settings — readable by all users, writable only by root.
2172
+ * Org-managed (IT/MDM-deployed) settings path, per platform. Readable by all
2173
+ * users, writable only by root/admin. Claude Code always reads this file
2174
+ * regardless of `settingSources`, so an `apiKeyHelper` / `env` override here
2175
+ * redirects the agent off the gateway and the wizard cannot remove it — it
2176
+ * must fail closed. Checking only the macOS path (the prior behavior) let a
2177
+ * Linux/Windows managed override slip past the fail-closed gate undetected.
1977
2178
  */
1978
- const MANAGED_SETTINGS_PATH = "/Library/Application Support/ClaudeCode/managed-settings.json";
2179
+ function managedSettingsPath(platform = process.platform) {
2180
+ switch (platform) {
2181
+ case "darwin": return "/Library/Application Support/ClaudeCode/managed-settings.json";
2182
+ case "win32": return path.join(process.env.ProgramData || "C:\\ProgramData", "ClaudeCode", "managed-settings.json");
2183
+ default: return "/etc/claude-code/managed-settings.json";
2184
+ }
2185
+ }
1979
2186
  /**
1980
2187
  * Check every settings file Claude Code reads for blocking keys that conflict
1981
2188
  * with the wizard's gateway auth: org-managed, the user's global config, the
@@ -1983,13 +2190,13 @@ const MANAGED_SETTINGS_PATH = "/Library/Application Support/ClaudeCode/managed-s
1983
2190
  * an `apiKeyHelper` or `ANTHROPIC_*` override commonly lives, and any of them
1984
2191
  * can outrank the env the wizard sets and make every agent call 401.
1985
2192
  */
1986
- function checkAllSettingsConflicts(workingDirectory, homeDir = os.homedir()) {
2193
+ function checkAllSettingsConflicts(workingDirectory, homeDir = os.homedir(), platform = process.platform) {
1987
2194
  const conflicts = [];
1988
2195
  const home = homeDir;
1989
2196
  const sources = [
1990
2197
  {
1991
2198
  source: "managed",
1992
- paths: [MANAGED_SETTINGS_PATH],
2199
+ paths: [managedSettingsPath(platform)],
1993
2200
  writable: false
1994
2201
  },
1995
2202
  {
@@ -1999,7 +2206,7 @@ function checkAllSettingsConflicts(workingDirectory, homeDir = os.homedir()) {
1999
2206
  },
2000
2207
  {
2001
2208
  source: "project",
2002
- paths: [path.join(workingDirectory, ".claude", "settings.json"), path.join(workingDirectory, ".claude", "settings")],
2209
+ paths: SETTINGS_FILENAMES.map((name) => path.join(workingDirectory, ".claude", name)),
2003
2210
  writable: true
2004
2211
  },
2005
2212
  {
@@ -2023,6 +2230,22 @@ function checkAllSettingsConflicts(workingDirectory, homeDir = os.homedir()) {
2023
2230
  return conflicts;
2024
2231
  }
2025
2232
  /**
2233
+ * Sort detected conflicts by how the wizard can respond, following the rule:
2234
+ * **neutralize where possible; where it can't, let the user make the change.**
2235
+ *
2236
+ * NOTE: the `warnOnly` bucket assumes the agent runs with
2237
+ * `settingSources: ['project']` (set in `agent-interface.ts` and
2238
+ * `mcp-prompt-streaming.ts`). If that ever widens to include `'user'` / `'local'`,
2239
+ * those sources stop being neutralized and must move out of `warnOnly`.
2240
+ */
2241
+ function classifySettingsConflicts(conflicts) {
2242
+ return {
2243
+ autoFix: conflicts.filter((c) => c.writable),
2244
+ failClosed: conflicts.filter((c) => !c.writable && c.source === "managed"),
2245
+ warnOnly: conflicts.filter((c) => !c.writable && c.source !== "managed")
2246
+ };
2247
+ }
2248
+ /**
2026
2249
  * Ensure `.claude/.gitignore` excludes `*.wizard-backup` so a backup left
2027
2250
  * orphaned by an interrupted run can't get committed to git — settings files
2028
2251
  * commonly hold `apiKeyHelper` or `ANTHROPIC_API_KEY`, which we don't want
@@ -2030,7 +2253,7 @@ function checkAllSettingsConflicts(workingDirectory, homeDir = os.homedir()) {
2030
2253
  */
2031
2254
  function ensureBackupGitignored(claudeDir) {
2032
2255
  const gitignorePath = path.join(claudeDir, ".gitignore");
2033
- const entry = "*.wizard-backup";
2256
+ const entry = `*${BACKUP_SUFFIX}`;
2034
2257
  try {
2035
2258
  const existing = fs$1.existsSync(gitignorePath) ? fs$1.readFileSync(gitignorePath, "utf-8") : "";
2036
2259
  if (existing.split("\n").map((l) => l.trim()).includes(entry)) return;
@@ -2046,18 +2269,16 @@ function ensureBackupGitignored(claudeDir) {
2046
2269
  * cleanup.
2047
2270
  */
2048
2271
  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`;
2272
+ for (const { claudeDir, original, backup } of settingsBackupTargets(workingDirectory)) {
2273
+ if (!fs$1.existsSync(original)) continue;
2053
2274
  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);
2275
+ if (!fs$1.existsSync(backup)) {
2276
+ fs$1.copyFileSync(original, backup);
2277
+ const mode = fs$1.statSync(original).mode & 511;
2278
+ fs$1.chmodSync(backup, mode);
2058
2279
  }
2059
- ensureBackupGitignored(path.join(workingDirectory, ".claude"));
2060
- fs$1.unlinkSync(filePath);
2280
+ ensureBackupGitignored(claudeDir);
2281
+ fs$1.unlinkSync(original);
2061
2282
  analytics.wizardCapture("backedup-claude-settings");
2062
2283
  registerCleanup(() => {
2063
2284
  try {
@@ -2083,9 +2304,7 @@ function backupAndFixClaudeSettings(workingDirectory) {
2083
2304
  * calls (outro then cleanup) idempotent and avoids leaving an orphan behind.
2084
2305
  */
2085
2306
  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`;
2307
+ for (const { original, backup } of settingsBackupTargets(workingDirectory)) {
2089
2308
  if (!fs$1.existsSync(backup)) continue;
2090
2309
  try {
2091
2310
  fs$1.copyFileSync(backup, original);
@@ -2112,9 +2331,7 @@ function restoreClaudeSettings(workingDirectory) {
2112
2331
  * already moved them aside.
2113
2332
  */
2114
2333
  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`;
2334
+ for (const { original, backup } of settingsBackupTargets(workingDirectory)) {
2118
2335
  if (!fs$1.existsSync(backup) || fs$1.existsSync(original)) continue;
2119
2336
  try {
2120
2337
  fs$1.copyFileSync(backup, original);
@@ -2126,6 +2343,70 @@ function recoverOrphanedSettingsBackups(workingDirectory) {
2126
2343
  }
2127
2344
  }
2128
2345
  //#endregion
2346
+ //#region src/lib/agent/stored-login.ts
2347
+ /**
2348
+ * Detect a pre-existing Claude Code login on the machine.
2349
+ *
2350
+ * The wizard authenticates the agent's LLM calls by injecting its PostHog OAuth
2351
+ * token via `CLAUDE_CODE_OAUTH_TOKEN` / `ANTHROPIC_AUTH_TOKEN`. If the user is
2352
+ * already logged into Claude Code/Desktop, the Agent SDK can instead use that
2353
+ * stored `/login` credential (reported as `apiKeySource: "/login managed key"`)
2354
+ * and send it to the PostHog gateway, which rejects it with a 401.
2355
+ *
2356
+ * The settings-conflict scan ({@link checkAllSettingsConflicts}) only inspects
2357
+ * `settings.json` for `ANTHROPIC_*` overrides / `apiKeyHelper`; it has no
2358
+ * awareness of an actual logged-in session. This fills that gap so a run can
2359
+ * log and report the potential conflict behind a gateway 401.
2360
+ *
2361
+ * Existence-only: it never reads the credential value (no `-w`), so no secret
2362
+ * is ever exposed. The locations checked mirror the SDK's own resolution
2363
+ * (npmjs.com/package/@anthropic-ai/claude-agent-sdk → sdk.mjs): a
2364
+ * `.credentials.json` under `CLAUDE_CONFIG_DIR` / `~/.claude`, and the macOS
2365
+ * keychain item below.
2366
+ */
2367
+ /** macOS keychain service name Claude Code stores its login under. */
2368
+ const KEYCHAIN_SERVICE = "Claude Code-credentials";
2369
+ /** True when any stored login was found. */
2370
+ const hasStoredClaudeLogin = (login) => login.credentialsFile || login.keychain;
2371
+ /**
2372
+ * The directory Claude Code resolves credentials/config from — `CLAUDE_CONFIG_DIR`
2373
+ * if set, else `~/.claude`. Mirrors the SDK's own resolution; single source of
2374
+ * truth so callers can't drift from it.
2375
+ */
2376
+ const claudeConfigDir = (homeDir = os.homedir()) => process.env.CLAUDE_CONFIG_DIR || path.join(homeDir, ".claude");
2377
+ /**
2378
+ * Look for a stored Claude login. `homeDir` / `platform` are injectable for
2379
+ * tests; production uses the real home dir and platform.
2380
+ */
2381
+ function detectStoredClaudeLogin(homeDir = os.homedir(), platform = process.platform) {
2382
+ const credentialsPath = path.join(claudeConfigDir(homeDir), ".credentials.json");
2383
+ let credentialsFile = false;
2384
+ try {
2385
+ credentialsFile = fs$1.existsSync(credentialsPath);
2386
+ } catch (error) {
2387
+ logToFile(`[stored-login] checked ${credentialsPath} — unreadable (${error instanceof Error ? error.message : String(error)}), treating as absent`);
2388
+ analytics.wizardCapture("stored login check failed", { step: "credentials-file" });
2389
+ }
2390
+ let keychain = false;
2391
+ if (platform === "darwin") try {
2392
+ keychain = spawnSync("security", [
2393
+ "find-generic-password",
2394
+ "-s",
2395
+ KEYCHAIN_SERVICE
2396
+ ], {
2397
+ stdio: "ignore",
2398
+ timeout: 5e3
2399
+ }).status === 0;
2400
+ } catch (error) {
2401
+ logToFile(`[stored-login] checked keychain '${KEYCHAIN_SERVICE}' — lookup cleared (${error instanceof Error ? error.message : String(error)}), treating as absent`);
2402
+ analytics.wizardCapture("stored login check failed", { step: "keychain" });
2403
+ }
2404
+ return {
2405
+ credentialsFile,
2406
+ keychain
2407
+ };
2408
+ }
2409
+ //#endregion
2129
2410
  //#region src/lib/agent/agent-interface.ts
2130
2411
  /**
2131
2412
  * Shared agent interface for PostHog wizards
@@ -2149,15 +2430,29 @@ function regionFromGatewayUrl(gatewayUrl) {
2149
2430
  if (gatewayUrl.includes("localhost")) return "local";
2150
2431
  return gatewayUrl.includes("gateway.eu.") ? "eu" : "us";
2151
2432
  }
2152
- function buildAuthErrorContext(workingDirectory, gatewayUrl, homeDir = os.homedir()) {
2433
+ /** Places the agent could have picked up a non-PostHog credential. */
2434
+ function findCredentialPlaces(conflicts, homeDir) {
2435
+ const places = [];
2436
+ const stored = detectStoredClaudeLogin(homeDir);
2437
+ const configDir = claudeConfigDir(homeDir);
2438
+ if (stored.credentialsFile) places.push(`A logged-in Claude session: ${path.join(configDir, ".credentials.json")}`);
2439
+ if (stored.keychain) places.push("A logged-in Claude session: macOS keychain item \"Claude Code-credentials\"");
2440
+ for (const c of conflicts) places.push(`${c.path} sets ${c.keys.join(", ")}`);
2441
+ return places;
2442
+ }
2443
+ function buildAuthErrorContext(workingDirectory, gatewayUrl, homeDir = os.homedir(), apiKeySource) {
2153
2444
  const conflicts = checkAllSettingsConflicts(workingDirectory, homeDir);
2445
+ const usingManagedLogin = /login|managed key/i.test(apiKeySource ?? "");
2154
2446
  return {
2155
2447
  hasSettingsConflict: conflicts.length > 0,
2156
2448
  conflicts,
2157
2449
  conflictSources: conflicts.map((c) => c.source),
2158
2450
  conflictKeys: [...new Set(conflicts.flatMap((c) => c.keys))],
2159
2451
  gatewayUrl,
2160
- region: regionFromGatewayUrl(gatewayUrl)
2452
+ region: regionFromGatewayUrl(gatewayUrl),
2453
+ apiKeySource,
2454
+ usingManagedLogin,
2455
+ credentialPlaces: findCredentialPlaces(conflicts, homeDir)
2161
2456
  };
2162
2457
  }
2163
2458
  /**
@@ -2444,13 +2739,21 @@ async function initializeAgent(config, options) {
2444
2739
  logToFile("Agent initialization starting");
2445
2740
  logToFile("Install directory:", options.installDir);
2446
2741
  try {
2742
+ const gatewayUrl = HostResolution.fromApiHost(config.posthogApiHost).gatewayUrl;
2447
2743
  process.env.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS = "true";
2448
- const gatewayUrl = getLlmGatewayUrlFromHost(config.posthogApiHost);
2449
2744
  process.env.ANTHROPIC_BASE_URL = gatewayUrl;
2450
2745
  process.env.ANTHROPIC_AUTH_TOKEN = config.posthogApiKey;
2451
2746
  process.env.CLAUDE_CODE_OAUTH_TOKEN = config.posthogApiKey;
2452
2747
  logToFile("Configured LLM gateway:", gatewayUrl);
2453
2748
  logToFile("API key prefix:", config.posthogApiKey ? `${config.posthogApiKey.slice(0, 4)}***` : "(missing)");
2749
+ const storedLogin = detectStoredClaudeLogin();
2750
+ if (hasStoredClaudeLogin(storedLogin)) {
2751
+ 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.`);
2752
+ analytics.wizardCapture("claude stored login detected", {
2753
+ credentials_file: storedLogin.credentialsFile,
2754
+ keychain: storedLogin.keychain
2755
+ });
2756
+ }
2454
2757
  const initConflicts = checkAllSettingsConflicts(options.installDir);
2455
2758
  logToFile("Settings conflicts at agent init:", initConflicts.length > 0 ? initConflicts.map((c) => `${c.source}(${c.keys.join(",")})`).join("; ") : "none");
2456
2759
  const mcpServers = {
@@ -2650,8 +2953,11 @@ async function runAgent(agentConfig, prompt, options, spinner, config, middlewar
2650
2953
  ] }
2651
2954
  },
2652
2955
  env: {
2653
- ...process.env,
2654
- ANTHROPIC_API_KEY: void 0,
2956
+ ...sanitizeAgentSubprocessEnv(process.env),
2957
+ ANTHROPIC_BASE_URL: process.env.ANTHROPIC_BASE_URL,
2958
+ ANTHROPIC_AUTH_TOKEN: process.env.ANTHROPIC_AUTH_TOKEN,
2959
+ CLAUDE_CODE_OAUTH_TOKEN: process.env.CLAUDE_CODE_OAUTH_TOKEN,
2960
+ CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS: "true",
2655
2961
  ENABLE_TOOL_SEARCH: "auto:0",
2656
2962
  MCP_CONNECTION_NONBLOCKING: "0",
2657
2963
  ANTHROPIC_CUSTOM_HEADERS: buildAgentEnv(agentConfig.wizardMetadata ?? {}, agentConfig.wizardFlags ?? {})
@@ -2728,11 +3034,13 @@ async function runAgent(agentConfig, prompt, options, spinner, config, middlewar
2728
3034
  if (message.type === "assistant" && signals.hasApiErrorStatus(401)) {
2729
3035
  signalDone();
2730
3036
  spinner.stop("Authentication failed");
2731
- const authError = buildAuthErrorContext(options.installDir, process.env.ANTHROPIC_BASE_URL ?? "");
3037
+ const authError = buildAuthErrorContext(options.installDir, process.env.ANTHROPIC_BASE_URL ?? "", os.homedir(), signals.apiKeySource);
2732
3038
  logToFile("Agent error: 401, showing auth error screen", authError);
2733
3039
  getUI().showAuthError({
2734
3040
  hasSettingsConflict: authError.hasSettingsConflict,
2735
3041
  conflicts: authError.conflicts,
3042
+ usingManagedLogin: authError.usingManagedLogin,
3043
+ credentialPlaces: authError.credentialPlaces,
2736
3044
  logFilePath: getLogFilePath()
2737
3045
  });
2738
3046
  await wizardAbort({
@@ -2742,7 +3050,9 @@ async function runAgent(agentConfig, prompt, options, spinner, config, middlewar
2742
3050
  conflictSources: authError.conflictSources,
2743
3051
  conflictKeys: authError.conflictKeys,
2744
3052
  gatewayUrl: authError.gatewayUrl,
2745
- region: authError.region
3053
+ region: authError.region,
3054
+ usingManagedLogin: authError.usingManagedLogin,
3055
+ apiKeySource: authError.apiKeySource
2746
3056
  })
2747
3057
  });
2748
3058
  }
@@ -2761,7 +3071,7 @@ async function runAgent(agentConfig, prompt, options, spinner, config, middlewar
2761
3071
  }
2762
3072
  if (yaraViolationReason) {
2763
3073
  logToFile("Agent error: YARA_VIOLATION");
2764
- spinner.stop("Security violation detected");
3074
+ spinner.stop("Security check stopped the setup");
2765
3075
  return { error: "WIZARD_YARA_VIOLATION" };
2766
3076
  }
2767
3077
  if (abortReason) {
@@ -2804,7 +3114,7 @@ async function runAgent(agentConfig, prompt, options, spinner, config, middlewar
2804
3114
  signalDone();
2805
3115
  if (yaraViolationReason) {
2806
3116
  logToFile("Agent error: YARA_VIOLATION");
2807
- spinner.stop("Security violation detected");
3117
+ spinner.stop("Security check stopped the setup");
2808
3118
  return { error: "WIZARD_YARA_VIOLATION" };
2809
3119
  }
2810
3120
  if (abortReason) {
@@ -3044,11 +3354,15 @@ function handleSDKMessage(message, options, spinner, signals, receivedSuccessRes
3044
3354
  }
3045
3355
  break;
3046
3356
  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
- });
3357
+ if (message.subtype === "init") {
3358
+ signals.recordApiKeySource(message.apiKeySource);
3359
+ logToFile("Agent session initialized", {
3360
+ model: message.model,
3361
+ tools: message.tools?.length,
3362
+ mcpServers: message.mcp_servers,
3363
+ apiKeySource: message.apiKeySource
3364
+ });
3365
+ }
3052
3366
  break;
3053
3367
  default:
3054
3368
  if (options.debug) debug(`Unhandled message type: ${message.type}`);
@@ -3056,6 +3370,6 @@ function handleSDKMessage(message, options, spinner, signals, receivedSuccessRes
3056
3370
  }
3057
3371
  }
3058
3372
  //#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 };
3373
+ export { coerceAuditChecks as A, QUEUE_DIR_NAME as C, AUDIT_CHECKS_KEY as D, AUDIT_CHECKS_FILE as E, AUDIT_REPORT_FILE as O, installSkillById as S, TaskStatus as T, EVENT_PLAN_FILE as _, runAgent as a, downloadSkill as b, classifySettingsConflicts as c, sanitizeAgentSubprocessEnv as d, AgentSignals as f, writeScanReport as g, formatYaraAbortMessage as h, isOrchestratorEnabled as i, getAuditChecks as j, AUDIT_SEVERITY_STYLE 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, SETUP_REPORT_FILE as v, QueueStore as w, fetchSkillMenu as x, WIZARD_TOOL_NAMES as y };
3060
3374
 
3061
- //# sourceMappingURL=agent-interface-DCeQ5Oiw.js.map
3375
+ //# sourceMappingURL=agent-interface-CXL9i_vX.js.map