workos 0.17.0 → 0.18.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 (81) hide show
  1. package/README.md +3 -4
  2. package/dist/bin.js +54 -1
  3. package/dist/bin.js.map +1 -1
  4. package/dist/cli.config.d.ts +0 -2
  5. package/dist/cli.config.js +3 -3
  6. package/dist/cli.config.js.map +1 -1
  7. package/dist/commands/debug.d.ts +11 -0
  8. package/dist/commands/debug.js +24 -8
  9. package/dist/commands/debug.js.map +1 -1
  10. package/dist/commands/install.js +4 -0
  11. package/dist/commands/install.js.map +1 -1
  12. package/dist/commands/mcp.d.ts +14 -0
  13. package/dist/commands/mcp.js +108 -0
  14. package/dist/commands/mcp.js.map +1 -0
  15. package/dist/doctor/checks/ai-analysis.js +2 -1
  16. package/dist/doctor/checks/ai-analysis.js.map +1 -1
  17. package/dist/doctor/checks/auth-patterns.js +1 -1
  18. package/dist/doctor/checks/auth-patterns.js.map +1 -1
  19. package/dist/doctor/checks/mcp.d.ts +15 -0
  20. package/dist/doctor/checks/mcp.js +34 -0
  21. package/dist/doctor/checks/mcp.js.map +1 -0
  22. package/dist/doctor/index.js +7 -1
  23. package/dist/doctor/index.js.map +1 -1
  24. package/dist/doctor/issues.js +16 -0
  25. package/dist/doctor/issues.js.map +1 -1
  26. package/dist/doctor/output.js +16 -2
  27. package/dist/doctor/output.js.map +1 -1
  28. package/dist/doctor/types.d.ts +17 -0
  29. package/dist/doctor/types.js.map +1 -1
  30. package/dist/emulate/workos/index.js.map +1 -1
  31. package/dist/lib/agent-interface.js +4 -4
  32. package/dist/lib/agent-interface.js.map +1 -1
  33. package/dist/lib/agent-runner.js +55 -16
  34. package/dist/lib/agent-runner.js.map +1 -1
  35. package/dist/lib/ai-content.js +2 -1
  36. package/dist/lib/ai-content.js.map +1 -1
  37. package/dist/lib/constants.d.ts +9 -0
  38. package/dist/lib/constants.js +9 -0
  39. package/dist/lib/constants.js.map +1 -1
  40. package/dist/lib/installer-core.d.ts +20 -20
  41. package/dist/lib/mcp-clients.d.ts +39 -0
  42. package/dist/lib/mcp-clients.js +284 -0
  43. package/dist/lib/mcp-clients.js.map +1 -0
  44. package/dist/lib/mcp-notice.d.ts +72 -0
  45. package/dist/lib/mcp-notice.js +275 -0
  46. package/dist/lib/mcp-notice.js.map +1 -0
  47. package/dist/lib/preferences.d.ts +14 -0
  48. package/dist/lib/preferences.js +3 -0
  49. package/dist/lib/preferences.js.map +1 -1
  50. package/dist/lib/run-with-core.js +2 -1
  51. package/dist/lib/run-with-core.js.map +1 -1
  52. package/dist/lib/settings.d.ts +5 -14
  53. package/dist/lib/settings.js +5 -16
  54. package/dist/lib/settings.js.map +1 -1
  55. package/dist/lib/startup-notice-gate.d.ts +21 -0
  56. package/dist/lib/startup-notice-gate.js +29 -0
  57. package/dist/lib/startup-notice-gate.js.map +1 -0
  58. package/dist/lib/telemetry-notice.js +3 -0
  59. package/dist/lib/telemetry-notice.js.map +1 -1
  60. package/dist/lib/unclaimed-warning.js +3 -0
  61. package/dist/lib/unclaimed-warning.js.map +1 -1
  62. package/dist/lib/validation/security-checks.d.ts +35 -0
  63. package/dist/lib/validation/security-checks.js +105 -0
  64. package/dist/lib/validation/security-checks.js.map +1 -0
  65. package/dist/utils/analytics.d.ts +1 -0
  66. package/dist/utils/analytics.js +5 -1
  67. package/dist/utils/analytics.js.map +1 -1
  68. package/dist/utils/help-json.js +38 -0
  69. package/dist/utils/help-json.js.map +1 -1
  70. package/dist/utils/interaction-mode.d.ts +1 -1
  71. package/dist/utils/interaction-mode.js +0 -3
  72. package/dist/utils/interaction-mode.js.map +1 -1
  73. package/dist/utils/output.d.ts +5 -3
  74. package/dist/utils/output.js +5 -5
  75. package/dist/utils/output.js.map +1 -1
  76. package/dist/utils/telemetry-types.d.ts +1 -1
  77. package/dist/utils/telemetry-types.js.map +1 -1
  78. package/dist/utils/urls.d.ts +9 -3
  79. package/dist/utils/urls.js +10 -4
  80. package/dist/utils/urls.js.map +1 -1
  81. package/package.json +12 -11
@@ -8,8 +8,6 @@ export interface InstallerConfig {
8
8
  workos: {
9
9
  clientId: string;
10
10
  authkitDomain: string;
11
- llmGatewayUrl: string;
12
- telemetryUrl: string;
13
11
  };
14
12
  telemetry: {
15
13
  enabled: boolean;
@@ -48,22 +46,15 @@ export interface InstallerConfig {
48
46
  */
49
47
  export declare function getConfig(): InstallerConfig;
50
48
  /**
51
- * Get the CLI auth client ID.
52
- * Env var overrides config default.
49
+ * Get the CLI auth client ID (from config; not env-overridable).
53
50
  */
54
51
  export declare function getCliAuthClientId(): string;
55
52
  /**
56
53
  * Get the AuthKit domain.
57
54
  * Env var overrides config default.
55
+ *
56
+ * Note: WorkOS service endpoints (API, dashboard, LLM gateway, telemetry)
57
+ * live in utils/urls.ts. AuthKit's domain stays here because it's config-
58
+ * backed rather than derived from the API host.
58
59
  */
59
60
  export declare function getAuthkitDomain(): string;
60
- /**
61
- * Get the LLM gateway URL.
62
- * Env var overrides config default.
63
- */
64
- export declare function getLlmGatewayUrl(): string;
65
- /**
66
- * Get the CLI telemetry URL.
67
- * Env var overrides config default.
68
- */
69
- export declare function getTelemetryUrl(): string;
@@ -12,8 +12,7 @@ export function getConfig() {
12
12
  return config;
13
13
  }
14
14
  /**
15
- * Get the CLI auth client ID.
16
- * Env var overrides config default.
15
+ * Get the CLI auth client ID (from config; not env-overridable).
17
16
  */
18
17
  export function getCliAuthClientId() {
19
18
  return config.workos.clientId;
@@ -21,22 +20,12 @@ export function getCliAuthClientId() {
21
20
  /**
22
21
  * Get the AuthKit domain.
23
22
  * Env var overrides config default.
23
+ *
24
+ * Note: WorkOS service endpoints (API, dashboard, LLM gateway, telemetry)
25
+ * live in utils/urls.ts. AuthKit's domain stays here because it's config-
26
+ * backed rather than derived from the API host.
24
27
  */
25
28
  export function getAuthkitDomain() {
26
29
  return process.env.WORKOS_AUTHKIT_DOMAIN || config.workos.authkitDomain;
27
30
  }
28
- /**
29
- * Get the LLM gateway URL.
30
- * Env var overrides config default.
31
- */
32
- export function getLlmGatewayUrl() {
33
- return process.env.WORKOS_LLM_GATEWAY_URL || config.workos.llmGatewayUrl;
34
- }
35
- /**
36
- * Get the CLI telemetry URL.
37
- * Env var overrides config default.
38
- */
39
- export function getTelemetryUrl() {
40
- return process.env.WORKOS_TELEMETRY_URL || config.workos.telemetryUrl;
41
- }
42
31
  //# sourceMappingURL=settings.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"settings.js","sourceRoot":"","sources":["../../src/lib/settings.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAEnD;;GAEG;AACH,MAAM,UAAU,UAAU;IACxB,OAAO,OAAO,CAAC;AACjB,CAAC;AA4CD;;GAEG;AACH,MAAM,UAAU,SAAS;IACvB,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,kBAAkB;IAChC,OAAO,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC;AAChC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB;IAC9B,OAAO,OAAO,CAAC,GAAG,CAAC,qBAAqB,IAAI,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC;AAC1E,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB;IAC9B,OAAO,OAAO,CAAC,GAAG,CAAC,sBAAsB,IAAI,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC;AAC3E,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe;IAC7B,OAAO,OAAO,CAAC,GAAG,CAAC,oBAAoB,IAAI,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC;AACxE,CAAC","sourcesContent":["import { config, version } from '../cli.config.js';\n\n/**\n * Get version from package.json\n */\nexport function getVersion(): string {\n return version;\n}\n\nexport interface InstallerConfig {\n model: string;\n doctorModel: string;\n workos: {\n clientId: string;\n authkitDomain: string;\n llmGatewayUrl: string;\n telemetryUrl: string;\n };\n telemetry: {\n enabled: boolean;\n eventName: string;\n };\n proxy: {\n refreshThresholdMs: number;\n };\n nodeVersion: string;\n logging: {\n debugMode: boolean;\n };\n documentation: {\n workosDocsUrl: string;\n dashboardUrl: string;\n issuesUrl: string;\n };\n frameworks: {\n [key: string]: {\n port: number;\n callbackPath: string;\n };\n };\n legacy: {\n oauthPort: number;\n };\n branding: {\n showAsciiArt: boolean;\n asciiArt: string;\n compactAsciiArt: string;\n useCompact: boolean;\n };\n}\n\n/**\n * Get config\n */\nexport function getConfig(): InstallerConfig {\n return config;\n}\n\n/**\n * Get the CLI auth client ID.\n * Env var overrides config default.\n */\nexport function getCliAuthClientId(): string {\n return config.workos.clientId;\n}\n\n/**\n * Get the AuthKit domain.\n * Env var overrides config default.\n */\nexport function getAuthkitDomain(): string {\n return process.env.WORKOS_AUTHKIT_DOMAIN || config.workos.authkitDomain;\n}\n\n/**\n * Get the LLM gateway URL.\n * Env var overrides config default.\n */\nexport function getLlmGatewayUrl(): string {\n return process.env.WORKOS_LLM_GATEWAY_URL || config.workos.llmGatewayUrl;\n}\n\n/**\n * Get the CLI telemetry URL.\n * Env var overrides config default.\n */\nexport function getTelemetryUrl(): string {\n return process.env.WORKOS_TELEMETRY_URL || config.workos.telemetryUrl;\n}\n"]}
1
+ {"version":3,"file":"settings.js","sourceRoot":"","sources":["../../src/lib/settings.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAEnD;;GAEG;AACH,MAAM,UAAU,UAAU;IACxB,OAAO,OAAO,CAAC;AACjB,CAAC;AA0CD;;GAEG;AACH,MAAM,UAAU,SAAS;IACvB,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,kBAAkB;IAChC,OAAO,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC;AAChC,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,gBAAgB;IAC9B,OAAO,OAAO,CAAC,GAAG,CAAC,qBAAqB,IAAI,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC;AAC1E,CAAC","sourcesContent":["import { config, version } from '../cli.config.js';\n\n/**\n * Get version from package.json\n */\nexport function getVersion(): string {\n return version;\n}\n\nexport interface InstallerConfig {\n model: string;\n doctorModel: string;\n workos: {\n clientId: string;\n authkitDomain: string;\n };\n telemetry: {\n enabled: boolean;\n eventName: string;\n };\n proxy: {\n refreshThresholdMs: number;\n };\n nodeVersion: string;\n logging: {\n debugMode: boolean;\n };\n documentation: {\n workosDocsUrl: string;\n dashboardUrl: string;\n issuesUrl: string;\n };\n frameworks: {\n [key: string]: {\n port: number;\n callbackPath: string;\n };\n };\n legacy: {\n oauthPort: number;\n };\n branding: {\n showAsciiArt: boolean;\n asciiArt: string;\n compactAsciiArt: string;\n useCompact: boolean;\n };\n}\n\n/**\n * Get config\n */\nexport function getConfig(): InstallerConfig {\n return config;\n}\n\n/**\n * Get the CLI auth client ID (from config; not env-overridable).\n */\nexport function getCliAuthClientId(): string {\n return config.workos.clientId;\n}\n\n/**\n * Get the AuthKit domain.\n * Env var overrides config default.\n *\n * Note: WorkOS service endpoints (API, dashboard, LLM gateway, telemetry)\n * live in utils/urls.ts. AuthKit's domain stays here because it's config-\n * backed rather than derived from the API host.\n */\nexport function getAuthkitDomain(): string {\n return process.env.WORKOS_AUTHKIT_DOMAIN || config.workos.authkitDomain;\n}\n"]}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * One-notice-per-run coordinator for the startup stderr notices.
3
+ *
4
+ * Three middlewares can each want to print a one-line stderr notice at startup:
5
+ * the first-run telemetry notice, the unclaimed-environment warning, and the
6
+ * MCP banner. Stacking two or three boxes on a single command is noise, so this
7
+ * module is the shared latch — whichever fires first calls
8
+ * markStartupNoticeShown(), and the lower-priority MCP banner checks
9
+ * hasStartupNoticeShown() and defers.
10
+ *
11
+ * Ordering IS priority: bin.ts runs the telemetry + unclaimed middlewares before
12
+ * the MCP banner middleware, so those two always win a contested run. In-memory
13
+ * only (a run is a single CLI process); the per-machine "already shown" state is
14
+ * persisted separately by each notice.
15
+ */
16
+ /** Record that a startup notice has displayed this run. */
17
+ export declare function markStartupNoticeShown(): void;
18
+ /** Whether any startup notice has already displayed this run. */
19
+ export declare function hasStartupNoticeShown(): boolean;
20
+ /** Reset the latch (for testing). */
21
+ export declare function resetStartupNoticeGate(): void;
@@ -0,0 +1,29 @@
1
+ /**
2
+ * One-notice-per-run coordinator for the startup stderr notices.
3
+ *
4
+ * Three middlewares can each want to print a one-line stderr notice at startup:
5
+ * the first-run telemetry notice, the unclaimed-environment warning, and the
6
+ * MCP banner. Stacking two or three boxes on a single command is noise, so this
7
+ * module is the shared latch — whichever fires first calls
8
+ * markStartupNoticeShown(), and the lower-priority MCP banner checks
9
+ * hasStartupNoticeShown() and defers.
10
+ *
11
+ * Ordering IS priority: bin.ts runs the telemetry + unclaimed middlewares before
12
+ * the MCP banner middleware, so those two always win a contested run. In-memory
13
+ * only (a run is a single CLI process); the per-machine "already shown" state is
14
+ * persisted separately by each notice.
15
+ */
16
+ let shown = false;
17
+ /** Record that a startup notice has displayed this run. */
18
+ export function markStartupNoticeShown() {
19
+ shown = true;
20
+ }
21
+ /** Whether any startup notice has already displayed this run. */
22
+ export function hasStartupNoticeShown() {
23
+ return shown;
24
+ }
25
+ /** Reset the latch (for testing). */
26
+ export function resetStartupNoticeGate() {
27
+ shown = false;
28
+ }
29
+ //# sourceMappingURL=startup-notice-gate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"startup-notice-gate.js","sourceRoot":"","sources":["../../src/lib/startup-notice-gate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,IAAI,KAAK,GAAG,KAAK,CAAC;AAElB,2DAA2D;AAC3D,MAAM,UAAU,sBAAsB;IACpC,KAAK,GAAG,IAAI,CAAC;AACf,CAAC;AAED,iEAAiE;AACjE,MAAM,UAAU,qBAAqB;IACnC,OAAO,KAAK,CAAC;AACf,CAAC;AAED,qCAAqC;AACrC,MAAM,UAAU,sBAAsB;IACpC,KAAK,GAAG,KAAK,CAAC;AAChB,CAAC","sourcesContent":["/**\n * One-notice-per-run coordinator for the startup stderr notices.\n *\n * Three middlewares can each want to print a one-line stderr notice at startup:\n * the first-run telemetry notice, the unclaimed-environment warning, and the\n * MCP banner. Stacking two or three boxes on a single command is noise, so this\n * module is the shared latch — whichever fires first calls\n * markStartupNoticeShown(), and the lower-priority MCP banner checks\n * hasStartupNoticeShown() and defers.\n *\n * Ordering IS priority: bin.ts runs the telemetry + unclaimed middlewares before\n * the MCP banner middleware, so those two always win a contested run. In-memory\n * only (a run is a single CLI process); the per-machine \"already shown\" state is\n * persisted separately by each notice.\n */\n\nlet shown = false;\n\n/** Record that a startup notice has displayed this run. */\nexport function markStartupNoticeShown(): void {\n shown = true;\n}\n\n/** Whether any startup notice has already displayed this run. */\nexport function hasStartupNoticeShown(): boolean {\n return shown;\n}\n\n/** Reset the latch (for testing). */\nexport function resetStartupNoticeGate(): void {\n shown = false;\n}\n"]}
@@ -17,6 +17,7 @@ import { isJsonMode } from '../utils/output.js';
17
17
  import { renderStderrBox } from '../utils/box.js';
18
18
  import { formatWorkOSCommand } from '../utils/command-invocation.js';
19
19
  import { isNoticeShown, markNoticeShown, isTelemetryOptedOut } from './preferences.js';
20
+ import { markStartupNoticeShown } from './startup-notice-gate.js';
20
21
  let shownThisSession = false;
21
22
  /**
22
23
  * Show the first-run telemetry notice if it has never been displayed.
@@ -44,6 +45,8 @@ export function maybeShowTelemetryNotice() {
44
45
  // rather than silently suppressing the notice for the rest of the session.
45
46
  shownThisSession = true;
46
47
  markNoticeShown();
48
+ // Claim the one-notice-per-run slot so the lower-priority MCP banner defers.
49
+ markStartupNoticeShown();
47
50
  }
48
51
  catch {
49
52
  // Never block command execution.
@@ -1 +1 @@
1
- {"version":3,"file":"telemetry-notice.js","sourceRoot":"","sources":["../../src/lib/telemetry-notice.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AACrE,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAEvF,IAAI,gBAAgB,GAAG,KAAK,CAAC;AAE7B;;;;;;;GAOG;AACH,MAAM,UAAU,wBAAwB;IACtC,IAAI,CAAC;QACH,IAAI,gBAAgB;YAAE,OAAO;QAC7B,IAAI,UAAU,EAAE;YAAE,OAAO,CAAC,mEAAmE;QAC7F,IAAI,mBAAmB,EAAE;YAAE,OAAO,CAAC,wCAAwC;QAC3E,IAAI,aAAa,EAAE;YAAE,OAAO,CAAC,2BAA2B;QAExD,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,mBAAmB,CAAC,mBAAmB,CAAC,CAAC,CAAC;QACpE,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,uDAAuD,MAAM,kBAAkB,CAAC;QACjH,eAAe,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QACnC,6EAA6E;QAC7E,2EAA2E;QAC3E,2EAA2E;QAC3E,gBAAgB,GAAG,IAAI,CAAC;QACxB,eAAe,EAAE,CAAC;IACpB,CAAC;IAAC,MAAM,CAAC;QACP,iCAAiC;IACnC,CAAC;AACH,CAAC;AAED,yCAAyC;AACzC,MAAM,UAAU,yBAAyB;IACvC,gBAAgB,GAAG,KAAK,CAAC;AAC3B,CAAC","sourcesContent":["/**\n * First-run telemetry notice.\n *\n * Prints a one-time, stderr-only box telling the user that anonymous CLI usage\n * telemetry is being collected and how to turn it off. Shown at most once ever\n * (backed by the persisted `noticeShownAt` timestamp in preferences.json), only\n * in interactive human mode, and never on the machine-readable path.\n *\n * Mirrors the structural pattern of unclaimed-warning.ts: a per-session guard,\n * a `!isJsonMode()` gate, the shared renderStderrBox helper, and a never-throws\n * contract so it can never block command execution. The one structural\n * difference is persistence — this notice writes `noticeShownAt` the first time\n * it actually displays so it never re-shows across runs.\n */\n\nimport chalk from 'chalk';\nimport { isJsonMode } from '../utils/output.js';\nimport { renderStderrBox } from '../utils/box.js';\nimport { formatWorkOSCommand } from '../utils/command-invocation.js';\nimport { isNoticeShown, markNoticeShown, isTelemetryOptedOut } from './preferences.js';\n\nlet shownThisSession = false;\n\n/**\n * Show the first-run telemetry notice if it has never been displayed.\n *\n * Gate order is load-bearing: every suppression check runs BEFORE\n * markNoticeShown(), so a non-human first run (--json / piped / CI) never\n * consumes the one-time display. The flag is set only when the box is actually\n * rendered, so a real human eventually sees it. Never throws.\n */\nexport function maybeShowTelemetryNotice(): void {\n try {\n if (shownThisSession) return;\n if (isJsonMode()) return; // suppress in --json / non-TTY / CI (output auto-switches to json)\n if (isTelemetryOptedOut()) return; // already opted out — nothing to inform\n if (isNoticeShown()) return; // already shown once, ever\n\n const optOut = chalk.cyan(formatWorkOSCommand('telemetry opt-out'));\n const inner = ` ${chalk.cyan('ℹ')} WorkOS collects anonymous CLI usage telemetry. Run ${optOut} to disable it. `;\n renderStderrBox(inner, chalk.cyan);\n // Set the per-session guard and persist ONLY after a successful render, so a\n // render failure (caught below) lets a later command in this process retry\n // rather than silently suppressing the notice for the rest of the session.\n shownThisSession = true;\n markNoticeShown();\n } catch {\n // Never block command execution.\n }\n}\n\n/** Reset session state (for testing). */\nexport function resetTelemetryNoticeState(): void {\n shownThisSession = false;\n}\n"]}
1
+ {"version":3,"file":"telemetry-notice.js","sourceRoot":"","sources":["../../src/lib/telemetry-notice.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AACrE,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AACvF,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAElE,IAAI,gBAAgB,GAAG,KAAK,CAAC;AAE7B;;;;;;;GAOG;AACH,MAAM,UAAU,wBAAwB;IACtC,IAAI,CAAC;QACH,IAAI,gBAAgB;YAAE,OAAO;QAC7B,IAAI,UAAU,EAAE;YAAE,OAAO,CAAC,mEAAmE;QAC7F,IAAI,mBAAmB,EAAE;YAAE,OAAO,CAAC,wCAAwC;QAC3E,IAAI,aAAa,EAAE;YAAE,OAAO,CAAC,2BAA2B;QAExD,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,mBAAmB,CAAC,mBAAmB,CAAC,CAAC,CAAC;QACpE,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,uDAAuD,MAAM,kBAAkB,CAAC;QACjH,eAAe,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QACnC,6EAA6E;QAC7E,2EAA2E;QAC3E,2EAA2E;QAC3E,gBAAgB,GAAG,IAAI,CAAC;QACxB,eAAe,EAAE,CAAC;QAClB,6EAA6E;QAC7E,sBAAsB,EAAE,CAAC;IAC3B,CAAC;IAAC,MAAM,CAAC;QACP,iCAAiC;IACnC,CAAC;AACH,CAAC;AAED,yCAAyC;AACzC,MAAM,UAAU,yBAAyB;IACvC,gBAAgB,GAAG,KAAK,CAAC;AAC3B,CAAC","sourcesContent":["/**\n * First-run telemetry notice.\n *\n * Prints a one-time, stderr-only box telling the user that anonymous CLI usage\n * telemetry is being collected and how to turn it off. Shown at most once ever\n * (backed by the persisted `noticeShownAt` timestamp in preferences.json), only\n * in interactive human mode, and never on the machine-readable path.\n *\n * Mirrors the structural pattern of unclaimed-warning.ts: a per-session guard,\n * a `!isJsonMode()` gate, the shared renderStderrBox helper, and a never-throws\n * contract so it can never block command execution. The one structural\n * difference is persistence — this notice writes `noticeShownAt` the first time\n * it actually displays so it never re-shows across runs.\n */\n\nimport chalk from 'chalk';\nimport { isJsonMode } from '../utils/output.js';\nimport { renderStderrBox } from '../utils/box.js';\nimport { formatWorkOSCommand } from '../utils/command-invocation.js';\nimport { isNoticeShown, markNoticeShown, isTelemetryOptedOut } from './preferences.js';\nimport { markStartupNoticeShown } from './startup-notice-gate.js';\n\nlet shownThisSession = false;\n\n/**\n * Show the first-run telemetry notice if it has never been displayed.\n *\n * Gate order is load-bearing: every suppression check runs BEFORE\n * markNoticeShown(), so a non-human first run (--json / piped / CI) never\n * consumes the one-time display. The flag is set only when the box is actually\n * rendered, so a real human eventually sees it. Never throws.\n */\nexport function maybeShowTelemetryNotice(): void {\n try {\n if (shownThisSession) return;\n if (isJsonMode()) return; // suppress in --json / non-TTY / CI (output auto-switches to json)\n if (isTelemetryOptedOut()) return; // already opted out — nothing to inform\n if (isNoticeShown()) return; // already shown once, ever\n\n const optOut = chalk.cyan(formatWorkOSCommand('telemetry opt-out'));\n const inner = ` ${chalk.cyan('ℹ')} WorkOS collects anonymous CLI usage telemetry. Run ${optOut} to disable it. `;\n renderStderrBox(inner, chalk.cyan);\n // Set the per-session guard and persist ONLY after a successful render, so a\n // render failure (caught below) lets a later command in this process retry\n // rather than silently suppressing the notice for the rest of the session.\n shownThisSession = true;\n markNoticeShown();\n // Claim the one-notice-per-run slot so the lower-priority MCP banner defers.\n markStartupNoticeShown();\n } catch {\n // Never block command execution.\n }\n}\n\n/** Reset session state (for testing). */\nexport function resetTelemetryNoticeState(): void {\n shownThisSession = false;\n}\n"]}
@@ -12,6 +12,7 @@ import { createClaimNonce, UnclaimedEnvApiError } from './unclaimed-env-api.js';
12
12
  import { logError, logInfo } from '../utils/debug.js';
13
13
  import { isJsonMode } from '../utils/output.js';
14
14
  import { renderStderrBox } from '../utils/box.js';
15
+ import { markStartupNoticeShown } from './startup-notice-gate.js';
15
16
  let warningShownThisSession = false;
16
17
  let claimCheckDoneThisSession = false;
17
18
  /**
@@ -59,6 +60,8 @@ export async function warnIfUnclaimed() {
59
60
  if (!isJsonMode()) {
60
61
  const inner = ` ${chalk.yellow('⚠ Unclaimed environment')} — Run ${chalk.cyan('workos env claim')} to keep your data. `;
61
62
  renderStderrBox(inner, chalk.yellow);
63
+ // Claim the one-notice-per-run slot so the lower-priority MCP banner defers.
64
+ markStartupNoticeShown();
62
65
  }
63
66
  }
64
67
  catch (error) {
@@ -1 +1 @@
1
- {"version":3,"file":"unclaimed-warning.js","sourceRoot":"","sources":["../../src/lib/unclaimed-warning.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AACzG,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAChF,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAElD,IAAI,uBAAuB,GAAG,KAAK,CAAC;AACpC,IAAI,yBAAyB,GAAG,KAAK,CAAC;AAEtC;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe;IACnC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,oBAAoB,EAAE,CAAC;QACnC,IAAI,CAAC,GAAG,IAAI,CAAC,sBAAsB,CAAC,GAAG,CAAC;YAAE,OAAO;QAEjD,2DAA2D;QAC3D,2EAA2E;QAC3E,IAAI,CAAC,yBAAyB,EAAE,CAAC;YAC/B,yBAAyB,GAAG,IAAI,CAAC;YACjC,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,MAAM,gBAAgB,CAAC,GAAG,CAAC,QAAQ,EAAE,GAAG,CAAC,UAAU,CAAC,CAAC;gBACpE,IAAI,MAAM,CAAC,cAAc,EAAE,CAAC;oBAC1B,sBAAsB,EAAE,CAAC;oBACzB,OAAO,CAAC,wEAAwE,CAAC,CAAC;oBAClF,OAAO;gBACT,CAAC;YACH,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAI,KAAK,YAAY,oBAAoB,IAAI,KAAK,CAAC,UAAU,KAAK,GAAG,EAAE,CAAC;oBACtE,yEAAyE;oBACzE,yDAAyD;oBACzD,sBAAsB,EAAE,CAAC;oBACzB,OAAO,CAAC,0DAA0D,CAAC,CAAC;oBACpE,OAAO;gBACT,CAAC;gBACD,wEAAwE;gBACxE,IAAI,KAAK,YAAY,oBAAoB,EAAE,CAAC;oBAC1C,QAAQ,CAAC,yCAAyC,EAAE,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;gBACvF,CAAC;qBAAM,CAAC;oBACN,QAAQ,CAAC,yCAAyC,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;gBAC9G,CAAC;YACH,CAAC;QACH,CAAC;QAED,gCAAgC;QAChC,IAAI,uBAAuB;YAAE,OAAO;QACpC,uBAAuB,GAAG,IAAI,CAAC;QAE/B,IAAI,CAAC,UAAU,EAAE,EAAE,CAAC;YAClB,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,MAAM,CAAC,yBAAyB,CAAC,UAAU,KAAK,CAAC,IAAI,CAAC,kBAAkB,CAAC,sBAAsB,CAAC;YACxH,eAAe,CAAC,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;QACvC,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,yDAAyD;QACzD,QAAQ,CAAC,uCAAuC,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IAC5G,CAAC;AACH,CAAC;AAED,wCAAwC;AACxC,MAAM,UAAU,0BAA0B;IACxC,uBAAuB,GAAG,KAAK,CAAC;IAChC,yBAAyB,GAAG,KAAK,CAAC;AACpC,CAAC","sourcesContent":["/**\n * Unclaimed environment warning module.\n *\n * Shows a one-line stderr warning when the active environment is unclaimed.\n * On first run, checks if the environment was claimed externally (e.g. via\n * browser) and updates the local config if so.\n * Never throws — all errors are caught to avoid blocking management commands.\n */\n\nimport chalk from 'chalk';\nimport { getActiveEnvironment, isUnclaimedEnvironment, markEnvironmentClaimed } from './config-store.js';\nimport { createClaimNonce, UnclaimedEnvApiError } from './unclaimed-env-api.js';\nimport { logError, logInfo } from '../utils/debug.js';\nimport { isJsonMode } from '../utils/output.js';\nimport { renderStderrBox } from '../utils/box.js';\n\nlet warningShownThisSession = false;\nlet claimCheckDoneThisSession = false;\n\n/**\n * Show a warning if the active environment is unclaimed.\n * Non-blocking — never throws.\n */\nexport async function warnIfUnclaimed(): Promise<void> {\n try {\n const env = getActiveEnvironment();\n if (!env || !isUnclaimedEnvironment(env)) return;\n\n // Check once per session if the env was claimed externally\n // claimToken and clientId guaranteed present by UnclaimedEnvironmentConfig\n if (!claimCheckDoneThisSession) {\n claimCheckDoneThisSession = true;\n try {\n const result = await createClaimNonce(env.clientId, env.claimToken);\n if (result.alreadyClaimed) {\n markEnvironmentClaimed();\n logInfo('[unclaimed-warning] Environment was claimed externally, config updated');\n return;\n }\n } catch (error) {\n if (error instanceof UnclaimedEnvApiError && error.statusCode === 401) {\n // 401 likely means the claim token was invalidated after the environment\n // was claimed. We assume claimed and promote to sandbox.\n markEnvironmentClaimed();\n logInfo('[unclaimed-warning] Claim token invalid/expired, removed');\n return;\n }\n // Log non-401 errors for diagnostics, then fall through to show warning\n if (error instanceof UnclaimedEnvApiError) {\n logError('[unclaimed-warning] Claim check failed:', error.statusCode, error.message);\n } else {\n logError('[unclaimed-warning] Claim check failed:', error instanceof Error ? error.message : String(error));\n }\n }\n }\n\n // Show warning once per session\n if (warningShownThisSession) return;\n warningShownThisSession = true;\n\n if (!isJsonMode()) {\n const inner = ` ${chalk.yellow('⚠ Unclaimed environment')} — Run ${chalk.cyan('workos env claim')} to keep your data. `;\n renderStderrBox(inner, chalk.yellow);\n }\n } catch (error) {\n // Never block command execution, but log for diagnostics\n logError('[unclaimed-warning] Unexpected error:', error instanceof Error ? error.message : String(error));\n }\n}\n\n/** Reset session state (for testing) */\nexport function resetUnclaimedWarningState(): void {\n warningShownThisSession = false;\n claimCheckDoneThisSession = false;\n}\n"]}
1
+ {"version":3,"file":"unclaimed-warning.js","sourceRoot":"","sources":["../../src/lib/unclaimed-warning.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AACzG,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAChF,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAElE,IAAI,uBAAuB,GAAG,KAAK,CAAC;AACpC,IAAI,yBAAyB,GAAG,KAAK,CAAC;AAEtC;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe;IACnC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,oBAAoB,EAAE,CAAC;QACnC,IAAI,CAAC,GAAG,IAAI,CAAC,sBAAsB,CAAC,GAAG,CAAC;YAAE,OAAO;QAEjD,2DAA2D;QAC3D,2EAA2E;QAC3E,IAAI,CAAC,yBAAyB,EAAE,CAAC;YAC/B,yBAAyB,GAAG,IAAI,CAAC;YACjC,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,MAAM,gBAAgB,CAAC,GAAG,CAAC,QAAQ,EAAE,GAAG,CAAC,UAAU,CAAC,CAAC;gBACpE,IAAI,MAAM,CAAC,cAAc,EAAE,CAAC;oBAC1B,sBAAsB,EAAE,CAAC;oBACzB,OAAO,CAAC,wEAAwE,CAAC,CAAC;oBAClF,OAAO;gBACT,CAAC;YACH,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAI,KAAK,YAAY,oBAAoB,IAAI,KAAK,CAAC,UAAU,KAAK,GAAG,EAAE,CAAC;oBACtE,yEAAyE;oBACzE,yDAAyD;oBACzD,sBAAsB,EAAE,CAAC;oBACzB,OAAO,CAAC,0DAA0D,CAAC,CAAC;oBACpE,OAAO;gBACT,CAAC;gBACD,wEAAwE;gBACxE,IAAI,KAAK,YAAY,oBAAoB,EAAE,CAAC;oBAC1C,QAAQ,CAAC,yCAAyC,EAAE,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;gBACvF,CAAC;qBAAM,CAAC;oBACN,QAAQ,CAAC,yCAAyC,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;gBAC9G,CAAC;YACH,CAAC;QACH,CAAC;QAED,gCAAgC;QAChC,IAAI,uBAAuB;YAAE,OAAO;QACpC,uBAAuB,GAAG,IAAI,CAAC;QAE/B,IAAI,CAAC,UAAU,EAAE,EAAE,CAAC;YAClB,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,MAAM,CAAC,yBAAyB,CAAC,UAAU,KAAK,CAAC,IAAI,CAAC,kBAAkB,CAAC,sBAAsB,CAAC;YACxH,eAAe,CAAC,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;YACrC,6EAA6E;YAC7E,sBAAsB,EAAE,CAAC;QAC3B,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,yDAAyD;QACzD,QAAQ,CAAC,uCAAuC,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IAC5G,CAAC;AACH,CAAC;AAED,wCAAwC;AACxC,MAAM,UAAU,0BAA0B;IACxC,uBAAuB,GAAG,KAAK,CAAC;IAChC,yBAAyB,GAAG,KAAK,CAAC;AACpC,CAAC","sourcesContent":["/**\n * Unclaimed environment warning module.\n *\n * Shows a one-line stderr warning when the active environment is unclaimed.\n * On first run, checks if the environment was claimed externally (e.g. via\n * browser) and updates the local config if so.\n * Never throws — all errors are caught to avoid blocking management commands.\n */\n\nimport chalk from 'chalk';\nimport { getActiveEnvironment, isUnclaimedEnvironment, markEnvironmentClaimed } from './config-store.js';\nimport { createClaimNonce, UnclaimedEnvApiError } from './unclaimed-env-api.js';\nimport { logError, logInfo } from '../utils/debug.js';\nimport { isJsonMode } from '../utils/output.js';\nimport { renderStderrBox } from '../utils/box.js';\nimport { markStartupNoticeShown } from './startup-notice-gate.js';\n\nlet warningShownThisSession = false;\nlet claimCheckDoneThisSession = false;\n\n/**\n * Show a warning if the active environment is unclaimed.\n * Non-blocking — never throws.\n */\nexport async function warnIfUnclaimed(): Promise<void> {\n try {\n const env = getActiveEnvironment();\n if (!env || !isUnclaimedEnvironment(env)) return;\n\n // Check once per session if the env was claimed externally\n // claimToken and clientId guaranteed present by UnclaimedEnvironmentConfig\n if (!claimCheckDoneThisSession) {\n claimCheckDoneThisSession = true;\n try {\n const result = await createClaimNonce(env.clientId, env.claimToken);\n if (result.alreadyClaimed) {\n markEnvironmentClaimed();\n logInfo('[unclaimed-warning] Environment was claimed externally, config updated');\n return;\n }\n } catch (error) {\n if (error instanceof UnclaimedEnvApiError && error.statusCode === 401) {\n // 401 likely means the claim token was invalidated after the environment\n // was claimed. We assume claimed and promote to sandbox.\n markEnvironmentClaimed();\n logInfo('[unclaimed-warning] Claim token invalid/expired, removed');\n return;\n }\n // Log non-401 errors for diagnostics, then fall through to show warning\n if (error instanceof UnclaimedEnvApiError) {\n logError('[unclaimed-warning] Claim check failed:', error.statusCode, error.message);\n } else {\n logError('[unclaimed-warning] Claim check failed:', error instanceof Error ? error.message : String(error));\n }\n }\n }\n\n // Show warning once per session\n if (warningShownThisSession) return;\n warningShownThisSession = true;\n\n if (!isJsonMode()) {\n const inner = ` ${chalk.yellow('⚠ Unclaimed environment')} — Run ${chalk.cyan('workos env claim')} to keep your data. `;\n renderStderrBox(inner, chalk.yellow);\n // Claim the one-notice-per-run slot so the lower-priority MCP banner defers.\n markStartupNoticeShown();\n }\n } catch (error) {\n // Never block command execution, but log for diagnostics\n logError('[unclaimed-warning] Unexpected error:', error instanceof Error ? error.message : String(error));\n }\n}\n\n/** Reset session state (for testing) */\nexport function resetUnclaimedWarningState(): void {\n warningShownThisSession = false;\n claimCheckDoneThisSession = false;\n}\n"]}
@@ -0,0 +1,35 @@
1
+ import type { AuthPatternFinding } from '../../doctor/types.js';
2
+ import type { ValidationIssue } from './types.js';
3
+ export interface SecurityCheckResult {
4
+ /** All security-class findings for this install (errors + warnings). */
5
+ findings: AuthPatternFinding[];
6
+ /**
7
+ * Error-severity findings that must block a successful install. Empty when the
8
+ * install is secure; a non-empty list means install should not report success.
9
+ */
10
+ blocking: AuthPatternFinding[];
11
+ }
12
+ /**
13
+ * Run the security subset of doctor's auth-pattern checks against an install
14
+ * directory. Pure file inspection — no network — so it is safe to call both
15
+ * inside the installer's self-correction loop and as the final pre-success gate.
16
+ *
17
+ * This closes the install-validate ↔ doctor gap: previously install could report
18
+ * `success: true` while `workos doctor` immediately found a security hole,
19
+ * because neither the retry loop nor `validateInstallation` ran these checks.
20
+ */
21
+ export declare function runInstallSecurityChecks(integration: string, installDir: string): Promise<SecurityCheckResult>;
22
+ /** Convert security findings into ValidationIssues for the emitter/report surfaces. */
23
+ export declare function securityFindingsToIssues(findings: AuthPatternFinding[]): ValidationIssue[];
24
+ /**
25
+ * Build an agent correction prompt from security findings so the installer's
26
+ * self-correction loop fixes them before declaring success. Returns an empty
27
+ * string when there is nothing to correct.
28
+ */
29
+ export declare function formatSecurityFindingsForAgent(findings: AuthPatternFinding[]): string;
30
+ /**
31
+ * Build the error message thrown when error-severity security findings survive
32
+ * the installer's retries — the message that turns a silent insecure "success"
33
+ * into a visible failure.
34
+ */
35
+ export declare function formatBlockingSecurityError(blocking: AuthPatternFinding[]): string;
@@ -0,0 +1,105 @@
1
+ import { checkAuthPatterns } from '../../doctor/checks/auth-patterns.js';
2
+ /**
3
+ * The "security subset" of `workos doctor`'s auth-pattern checks that the
4
+ * installer enforces. These are the patterns that are *unsafe* or *leak secrets*
5
+ * (an unsafe GET sign-out, an API key in client env/source, an ungitignored
6
+ * .env). Completeness checks (missing middleware/callback/provider) are
7
+ * intentionally excluded here — `validateInstallation` already covers those, and
8
+ * they carry higher false-positive risk than we want gating install success.
9
+ *
10
+ * Keep in sync with the codes emitted by `src/doctor/checks/auth-patterns.ts`.
11
+ */
12
+ const SECURITY_FINDING_CODES = new Set([
13
+ 'SIGNOUT_GET_HANDLER',
14
+ 'SIGNOUT_LINK_PREFETCH',
15
+ 'API_KEY_LEAKED_TO_CLIENT',
16
+ 'API_KEY_IN_SOURCE',
17
+ 'ENV_FILE_NOT_GITIGNORED',
18
+ 'MIXED_ENVIRONMENT',
19
+ ]);
20
+ /** Map an installer integration id to the framework name doctor's checks expect. */
21
+ const INTEGRATION_FRAMEWORK_NAME = {
22
+ nextjs: 'Next.js',
23
+ 'react-router': 'React Router',
24
+ 'tanstack-start': 'TanStack Start',
25
+ };
26
+ /**
27
+ * Run the security subset of doctor's auth-pattern checks against an install
28
+ * directory. Pure file inspection — no network — so it is safe to call both
29
+ * inside the installer's self-correction loop and as the final pre-success gate.
30
+ *
31
+ * This closes the install-validate ↔ doctor gap: previously install could report
32
+ * `success: true` while `workos doctor` immediately found a security hole,
33
+ * because neither the retry loop nor `validateInstallation` ran these checks.
34
+ */
35
+ export async function runInstallSecurityChecks(integration, installDir) {
36
+ const framework = {
37
+ name: INTEGRATION_FRAMEWORK_NAME[integration] ?? null,
38
+ version: null,
39
+ };
40
+ // checkAuthPatterns loads .env files itself; these structs only satisfy the
41
+ // shape its non-Next.js checks read from.
42
+ const environment = {
43
+ apiKeyConfigured: false,
44
+ apiKeyType: null,
45
+ clientId: null,
46
+ redirectUri: null,
47
+ cookieDomain: null,
48
+ baseUrl: null,
49
+ };
50
+ const sdk = {
51
+ name: null,
52
+ version: null,
53
+ latest: null,
54
+ outdated: false,
55
+ isAuthKit: false,
56
+ language: 'javascript',
57
+ };
58
+ const result = await checkAuthPatterns({ installDir }, framework, environment, sdk);
59
+ const findings = result.findings.filter((f) => SECURITY_FINDING_CODES.has(f.code));
60
+ const blocking = findings.filter((f) => f.severity === 'error');
61
+ return { findings, blocking };
62
+ }
63
+ /** Convert security findings into ValidationIssues for the emitter/report surfaces. */
64
+ export function securityFindingsToIssues(findings) {
65
+ return findings.map((f) => ({
66
+ type: 'pattern',
67
+ severity: f.severity,
68
+ message: f.filePath ? `${f.message} (${f.filePath})` : f.message,
69
+ hint: f.remediation,
70
+ }));
71
+ }
72
+ /**
73
+ * Build an agent correction prompt from security findings so the installer's
74
+ * self-correction loop fixes them before declaring success. Returns an empty
75
+ * string when there is nothing to correct.
76
+ */
77
+ export function formatSecurityFindingsForAgent(findings) {
78
+ if (findings.length === 0)
79
+ return '';
80
+ const lines = findings.map((f) => {
81
+ const loc = f.filePath ? ` in ${f.filePath}` : '';
82
+ const fix = f.remediation ? ` Fix: ${f.remediation}` : '';
83
+ return `- [${f.severity}] ${f.message}${loc}.${fix}`;
84
+ });
85
+ return `Security checks found issues that must be fixed:\n\n${lines.join('\n')}\n\nApply the fixes above, then make sure the project still builds.`;
86
+ }
87
+ /**
88
+ * Build the error message thrown when error-severity security findings survive
89
+ * the installer's retries — the message that turns a silent insecure "success"
90
+ * into a visible failure.
91
+ */
92
+ export function formatBlockingSecurityError(blocking) {
93
+ const lines = blocking.map((f) => {
94
+ const loc = f.filePath ? ` (${f.filePath})` : '';
95
+ return ` • ${f.code}: ${f.message}${loc}`;
96
+ });
97
+ return [
98
+ 'Installation produced insecure code that could not be auto-corrected:',
99
+ '',
100
+ ...lines,
101
+ '',
102
+ 'Fix the issues above (or run `workos doctor` for details and remediation) and re-run the installer.',
103
+ ].join('\n');
104
+ }
105
+ //# sourceMappingURL=security-checks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"security-checks.js","sourceRoot":"","sources":["../../../src/lib/validation/security-checks.ts"],"names":[],"mappings":"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","sourcesContent":["import { checkAuthPatterns } from '../../doctor/checks/auth-patterns.js';\nimport type { AuthPatternFinding, FrameworkInfo, EnvironmentInfo, SdkInfo } from '../../doctor/types.js';\nimport type { ValidationIssue } from './types.js';\n\n/**\n * The \"security subset\" of `workos doctor`'s auth-pattern checks that the\n * installer enforces. These are the patterns that are *unsafe* or *leak secrets*\n * (an unsafe GET sign-out, an API key in client env/source, an ungitignored\n * .env). Completeness checks (missing middleware/callback/provider) are\n * intentionally excluded here — `validateInstallation` already covers those, and\n * they carry higher false-positive risk than we want gating install success.\n *\n * Keep in sync with the codes emitted by `src/doctor/checks/auth-patterns.ts`.\n */\nconst SECURITY_FINDING_CODES = new Set<string>([\n 'SIGNOUT_GET_HANDLER',\n 'SIGNOUT_LINK_PREFETCH',\n 'API_KEY_LEAKED_TO_CLIENT',\n 'API_KEY_IN_SOURCE',\n 'ENV_FILE_NOT_GITIGNORED',\n 'MIXED_ENVIRONMENT',\n]);\n\n/** Map an installer integration id to the framework name doctor's checks expect. */\nconst INTEGRATION_FRAMEWORK_NAME: Record<string, string> = {\n nextjs: 'Next.js',\n 'react-router': 'React Router',\n 'tanstack-start': 'TanStack Start',\n};\n\nexport interface SecurityCheckResult {\n /** All security-class findings for this install (errors + warnings). */\n findings: AuthPatternFinding[];\n /**\n * Error-severity findings that must block a successful install. Empty when the\n * install is secure; a non-empty list means install should not report success.\n */\n blocking: AuthPatternFinding[];\n}\n\n/**\n * Run the security subset of doctor's auth-pattern checks against an install\n * directory. Pure file inspection — no network — so it is safe to call both\n * inside the installer's self-correction loop and as the final pre-success gate.\n *\n * This closes the install-validate ↔ doctor gap: previously install could report\n * `success: true` while `workos doctor` immediately found a security hole,\n * because neither the retry loop nor `validateInstallation` ran these checks.\n */\nexport async function runInstallSecurityChecks(integration: string, installDir: string): Promise<SecurityCheckResult> {\n const framework: FrameworkInfo = {\n name: INTEGRATION_FRAMEWORK_NAME[integration] ?? null,\n version: null,\n };\n // checkAuthPatterns loads .env files itself; these structs only satisfy the\n // shape its non-Next.js checks read from.\n const environment: EnvironmentInfo = {\n apiKeyConfigured: false,\n apiKeyType: null,\n clientId: null,\n redirectUri: null,\n cookieDomain: null,\n baseUrl: null,\n };\n const sdk: SdkInfo = {\n name: null,\n version: null,\n latest: null,\n outdated: false,\n isAuthKit: false,\n language: 'javascript',\n };\n\n const result = await checkAuthPatterns({ installDir }, framework, environment, sdk);\n const findings = result.findings.filter((f) => SECURITY_FINDING_CODES.has(f.code));\n const blocking = findings.filter((f) => f.severity === 'error');\n return { findings, blocking };\n}\n\n/** Convert security findings into ValidationIssues for the emitter/report surfaces. */\nexport function securityFindingsToIssues(findings: AuthPatternFinding[]): ValidationIssue[] {\n return findings.map((f) => ({\n type: 'pattern',\n severity: f.severity,\n message: f.filePath ? `${f.message} (${f.filePath})` : f.message,\n hint: f.remediation,\n }));\n}\n\n/**\n * Build an agent correction prompt from security findings so the installer's\n * self-correction loop fixes them before declaring success. Returns an empty\n * string when there is nothing to correct.\n */\nexport function formatSecurityFindingsForAgent(findings: AuthPatternFinding[]): string {\n if (findings.length === 0) return '';\n const lines = findings.map((f) => {\n const loc = f.filePath ? ` in ${f.filePath}` : '';\n const fix = f.remediation ? ` Fix: ${f.remediation}` : '';\n return `- [${f.severity}] ${f.message}${loc}.${fix}`;\n });\n return `Security checks found issues that must be fixed:\\n\\n${lines.join('\\n')}\\n\\nApply the fixes above, then make sure the project still builds.`;\n}\n\n/**\n * Build the error message thrown when error-severity security findings survive\n * the installer's retries — the message that turns a silent insecure \"success\"\n * into a visible failure.\n */\nexport function formatBlockingSecurityError(blocking: AuthPatternFinding[]): string {\n const lines = blocking.map((f) => {\n const loc = f.filePath ? ` (${f.filePath})` : '';\n return ` • ${f.code}: ${f.message}${loc}`;\n });\n return [\n 'Installation produced insecure code that could not be auto-corrected:',\n '',\n ...lines,\n '',\n 'Fix the issues above (or run `workos doctor` for details and remediation) and re-run the installer.',\n ].join('\\n');\n}\n"]}
@@ -55,6 +55,7 @@ export declare class Analytics {
55
55
  code?: string;
56
56
  resource?: string;
57
57
  };
58
+ extraAttributes?: Record<string, string | number | boolean>;
58
59
  }): void;
59
60
  captureUnhandledCrash(error: Error, options?: {
60
61
  command?: string;
@@ -4,7 +4,8 @@ import { v4 as uuidv4 } from 'uuid';
4
4
  import { debug } from './debug.js';
5
5
  import { telemetryClient } from './telemetry-client.js';
6
6
  import { isTelemetryEnabled } from '../lib/preferences.js';
7
- import { getTelemetryUrl, getVersion } from '../lib/settings.js';
7
+ import { getVersion } from '../lib/settings.js';
8
+ import { getTelemetryUrl } from './urls.js';
8
9
  import { getCredentials, isTokenExpired } from '../lib/credentials.js';
9
10
  import { getActiveEnvironment, isUnclaimedEnvironment } from '../lib/config-store.js';
10
11
  import { getDeviceId } from '../lib/device-id.js';
@@ -254,6 +255,9 @@ export class Analytics {
254
255
  sessionId: this.sessionId,
255
256
  timestamp: new Date().toISOString(),
256
257
  attributes: {
258
+ // Extras first: standard fields below win on key collision, so callers
259
+ // can never override command.name, error fields, or the fingerprint.
260
+ ...options?.extraAttributes,
257
261
  'command.name': name,
258
262
  'command.duration_ms': durationMs,
259
263
  'command.success': success,
@@ -1 +1 @@
1
- {"version":3,"file":"analytics.js","sourceRoot":"","sources":["../../src/utils/analytics.ts"],"names":[],"mappings":"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","sourcesContent":["import os from 'node:os';\nimport { basename } from 'node:path';\nimport { v4 as uuidv4 } from 'uuid';\nimport { debug } from './debug.js';\nimport { telemetryClient } from './telemetry-client.js';\nimport type {\n AuthMode,\n SessionStartEvent,\n SessionEndEvent,\n StepEvent,\n AgentToolEvent,\n AgentLLMEvent,\n CommandEvent,\n CrashEvent,\n TerminationReason,\n EnvFingerprint,\n} from './telemetry-types.js';\nimport { isTelemetryEnabled } from '../lib/preferences.js';\nimport { getTelemetryUrl, getVersion } from '../lib/settings.js';\nimport { getCredentials, isTokenExpired } from '../lib/credentials.js';\nimport { getActiveEnvironment, isUnclaimedEnvironment } from '../lib/config-store.js';\nimport { getDeviceId } from '../lib/device-id.js';\nimport { sanitizeMessage, sanitizeStack } from './crash-reporter.js';\n\nexport class Analytics {\n private tags: Record<string, string | boolean | number | null | undefined> = {};\n private sessionId: string;\n private sessionStartTime: Date;\n private distinctId?: string;\n private mode?: 'cli' | 'tui' | 'headless';\n private authMode: AuthMode = 'none';\n\n // Agent metrics tracking\n private totalInputTokens = 0;\n private totalOutputTokens = 0;\n private agentIterations = 0;\n\n constructor() {\n this.sessionId = uuidv4();\n this.sessionStartTime = new Date();\n this.tags = { $app_name: 'authkit-installer' };\n }\n\n setDistinctId(distinctId: string) {\n this.distinctId = distinctId;\n }\n\n setAccessToken(token: string) {\n telemetryClient.setAccessToken(token);\n }\n\n setApiKeyAuth(apiKey: string) {\n telemetryClient.setApiKeyAuth(apiKey);\n }\n\n setClaimTokenAuth(clientId: string, claimToken: string) {\n telemetryClient.setClaimTokenAuth(clientId, claimToken);\n }\n\n /**\n * Set the auth mode explicitly for special cases. Normal CLI flows should use\n * `configureAuthFromAvailableSources()` so transport and auth.mode stay aligned.\n */\n setAuthMode(mode: AuthMode) {\n this.authMode = mode;\n }\n\n setGatewayUrl(url: string) {\n telemetryClient.setGatewayUrl(url);\n }\n\n private isEnabled(): boolean {\n return isTelemetryEnabled();\n }\n\n /**\n * Configure telemetry transport and auth.mode from all available CLI auth\n * sources. Priority: stored JWT, unclaimed-environment claim token, active\n * environment API key, then WORKOS_API_KEY.\n */\n configureAuthFromAvailableSources(): AuthMode {\n if (!this.isEnabled()) return this.authMode;\n\n this.authMode = 'none';\n const creds = getCredentials();\n // Only treat the JWT as usable auth when it is still valid. An expired\n // access token would 401 against the telemetry guard and the event would\n // be dropped, so fall through to claim-token / api-key auth instead.\n if (creds?.accessToken && !isTokenExpired(creds)) {\n telemetryClient.setAccessToken(creds.accessToken);\n this.authMode = 'jwt';\n }\n // Preserve identity even when the token is expired.\n if (creds?.userId) {\n this.distinctId = creds.userId;\n }\n\n // Check for unclaimed environment — fall back to claim-token auth\n // so unclaimed users' telemetry still reaches the backend.\n try {\n const env = getActiveEnvironment();\n if (env && isUnclaimedEnvironment(env)) {\n telemetryClient.setClaimTokenAuth(env.clientId, env.claimToken);\n // Tag distinctId so unclaimed sessions are identifiable in analytics\n this.distinctId = this.distinctId ?? `unclaimed:${env.clientId}`;\n if (this.authMode === 'none') this.authMode = 'claim_token';\n } else if (env?.apiKey && this.authMode === 'none') {\n telemetryClient.setApiKeyAuth(env.apiKey);\n if (env.clientId) this.distinctId = this.distinctId ?? `env:${env.clientId}`;\n this.authMode = 'api_key';\n }\n } catch {\n // Config-store failure is non-fatal for telemetry\n }\n\n // WORKOS_API_KEY covers API-key-only users. Lowest priority — JWT and\n // claim-token auth have richer identity context when available.\n if (this.authMode === 'none' && process.env.WORKOS_API_KEY) {\n telemetryClient.setApiKeyAuth(process.env.WORKOS_API_KEY);\n this.authMode = 'api_key';\n }\n\n return this.authMode;\n }\n\n /**\n * Initialize telemetry for non-installer commands.\n * Sets telemetry URL from default config and loads auth credentials.\n */\n initForNonInstaller(): void {\n if (!this.isEnabled()) return;\n\n telemetryClient.setGatewayUrl(getTelemetryUrl());\n this.configureAuthFromAvailableSources();\n }\n\n setTag(key: string, value: string | boolean | number | null | undefined) {\n this.tags[key] = value;\n }\n\n capture(eventName: string, properties?: Record<string, unknown>) {\n if (!this.isEnabled()) return;\n\n debug(`[Analytics] capture: ${eventName}`, properties);\n\n // Accumulate primitive values as tags for the session.end event\n if (properties) {\n for (const [key, value] of Object.entries(properties)) {\n if (['string', 'number', 'boolean'].includes(typeof value)) {\n this.tags[key] = value as string | number | boolean;\n }\n }\n }\n }\n\n captureException(error: Error, properties: Record<string, unknown> = {}) {\n if (!this.isEnabled()) return;\n\n // Sanitize BEFORE logging — raw error.message can carry Bearer tokens /\n // sk_ keys / JWTs on auth-failure paths, which would surface in stdout\n // under WORKOS_DEBUG=1.\n const { type, message } = this.extractErrorFields(error);\n debug('[Analytics] captureException:', message, properties);\n this.tags['error.type'] = type;\n this.tags['error.message'] = message;\n }\n\n async getFeatureFlag(_flagKey: string): Promise<string | boolean | undefined> {\n // Feature flags not implemented yet\n return undefined;\n }\n\n /** All capture methods that record error details MUST go through this. */\n private extractErrorFields(error: Error): { type: string; message: string } {\n return {\n type: error.name,\n message: sanitizeMessage(error.message),\n };\n }\n\n private detectCiProvider(): string | undefined {\n if (process.env.GITHUB_ACTIONS) return 'github-actions';\n if (process.env.BUILDKITE) return 'buildkite';\n if (process.env.CIRCLECI) return 'circleci';\n if (process.env.GITLAB_CI) return 'gitlab-ci';\n if (process.env.JENKINS_URL) return 'jenkins';\n return undefined;\n }\n\n private getEnvFingerprint(): EnvFingerprint {\n let osVersion: string;\n try {\n osVersion = os.release();\n } catch {\n osVersion = 'unknown';\n }\n\n const ciProvider = this.detectCiProvider();\n\n return {\n 'device.id': getDeviceId(),\n 'auth.mode': this.authMode,\n 'env.os': process.platform,\n 'env.os_version': osVersion,\n 'env.node_version': process.version,\n 'env.shell': basename(process.env.SHELL ?? process.env.COMSPEC ?? 'unknown'),\n 'env.ci': Boolean(process.env.CI || process.env.GITHUB_ACTIONS || process.env.BUILDKITE),\n ...(ciProvider ? { 'env.ci_provider': ciProvider } : {}),\n };\n }\n\n sessionStart(mode: 'cli' | 'tui' | 'headless', version: string) {\n if (!this.isEnabled()) return;\n\n this.mode = mode;\n\n const event: SessionStartEvent = {\n type: 'session.start',\n sessionId: this.sessionId,\n timestamp: this.sessionStartTime.toISOString(),\n attributes: {\n 'installer.version': version,\n 'installer.mode': mode,\n 'workos.user_id': this.distinctId,\n ...this.getEnvFingerprint(),\n },\n };\n\n telemetryClient.queueEvent(event);\n }\n\n stepCompleted(name: string, durationMs: number, success: boolean, error?: Error) {\n if (!this.isEnabled()) return;\n\n const event: StepEvent = {\n type: 'step',\n sessionId: this.sessionId,\n timestamp: new Date().toISOString(),\n name,\n startTimestamp: new Date(Date.now() - durationMs).toISOString(),\n durationMs,\n success,\n error: error ? this.extractErrorFields(error) : undefined,\n };\n\n telemetryClient.queueEvent(event);\n }\n\n toolCalled(toolName: string, durationMs: number, success: boolean) {\n if (!this.isEnabled()) return;\n\n const event: AgentToolEvent = {\n type: 'agent.tool',\n sessionId: this.sessionId,\n timestamp: new Date().toISOString(),\n toolName,\n startTimestamp: new Date(Date.now() - durationMs).toISOString(),\n durationMs,\n success,\n };\n\n telemetryClient.queueEvent(event);\n }\n\n llmRequest(model: string, inputTokens: number, outputTokens: number) {\n if (!this.isEnabled()) return;\n\n this.totalInputTokens += inputTokens;\n this.totalOutputTokens += outputTokens;\n\n const event: AgentLLMEvent = {\n type: 'agent.llm',\n sessionId: this.sessionId,\n timestamp: new Date().toISOString(),\n model,\n inputTokens,\n outputTokens,\n };\n\n telemetryClient.queueEvent(event);\n }\n\n incrementAgentIterations() {\n this.agentIterations++;\n }\n\n emitCommandEvent(\n name: string,\n durationMs: number,\n success: boolean,\n options?: {\n error?: Error;\n flags?: string[];\n reason?: TerminationReason;\n errorCode?: string;\n apiContext?: { status?: number; code?: string; resource?: string };\n },\n ) {\n if (!this.isEnabled()) return;\n\n const errorFields = options?.error ? this.extractErrorFields(options.error) : undefined;\n\n const event: CommandEvent = {\n type: 'command',\n sessionId: this.sessionId,\n timestamp: new Date().toISOString(),\n attributes: {\n 'command.name': name,\n 'command.duration_ms': durationMs,\n 'command.success': success,\n 'cli.version': getVersion(),\n ...(this.distinctId ? { 'workos.user_id': this.distinctId } : {}),\n ...(errorFields\n ? {\n 'command.error_type': errorFields.type,\n 'command.error_message': errorFields.message,\n }\n : {}),\n ...(options?.flags?.length ? { 'command.flags': options.flags.join(',') } : {}),\n ...(options?.reason ? { 'termination.reason': options.reason } : {}),\n ...(options?.errorCode ? { 'error.code': options.errorCode } : {}),\n ...(options?.apiContext?.status !== undefined ? { 'api.status': options.apiContext.status } : {}),\n ...(options?.apiContext?.code ? { 'api.code': options.apiContext.code } : {}),\n ...(options?.apiContext?.resource ? { 'api.resource': options.apiContext.resource } : {}),\n ...this.getEnvFingerprint(),\n },\n };\n\n telemetryClient.queueEvent(event);\n }\n\n captureUnhandledCrash(error: Error, options?: { command?: string; version?: string }) {\n if (!this.isEnabled()) return;\n\n const { type, message } = this.extractErrorFields(error);\n\n const event: CrashEvent = {\n type: 'crash',\n sessionId: this.sessionId,\n timestamp: new Date().toISOString(),\n attributes: {\n 'crash.error_type': type,\n 'crash.error_message': message,\n 'crash.stack': sanitizeStack(error.stack),\n ...(options?.command ? { 'crash.command': options.command } : {}),\n 'cli.version': options?.version ?? getVersion(),\n ...(this.distinctId ? { 'workos.user_id': this.distinctId } : {}),\n ...this.getEnvFingerprint(),\n },\n };\n\n telemetryClient.queueEvent(event);\n }\n\n async shutdown(status: 'success' | 'error' | 'cancelled') {\n if (!this.isEnabled()) return;\n\n const duration = Date.now() - this.sessionStartTime.getTime();\n\n // Filter out null/undefined tags\n const extraAttributes = Object.fromEntries(Object.entries(this.tags).filter(([, v]) => v != null)) as Record<\n string,\n string | number | boolean\n >;\n\n const envFingerprint = this.getEnvFingerprint();\n\n const event: SessionEndEvent = {\n type: 'session.end',\n sessionId: this.sessionId,\n timestamp: new Date().toISOString(),\n attributes: {\n 'installer.outcome': status,\n 'installer.duration_ms': duration,\n 'installer.agent.iterations': this.agentIterations,\n 'installer.agent.tokens.input': this.totalInputTokens,\n 'installer.agent.tokens.output': this.totalOutputTokens,\n ...envFingerprint,\n ...(this.mode ? { 'installer.mode': this.mode } : {}),\n ...extraAttributes,\n },\n };\n\n telemetryClient.queueEvent(event);\n await telemetryClient.flush();\n }\n}\n\nexport const analytics = new Analytics();\n"]}
1
+ {"version":3,"file":"analytics.js","sourceRoot":"","sources":["../../src/utils/analytics.ts"],"names":[],"mappings":"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","sourcesContent":["import os from 'node:os';\nimport { basename } from 'node:path';\nimport { v4 as uuidv4 } from 'uuid';\nimport { debug } from './debug.js';\nimport { telemetryClient } from './telemetry-client.js';\nimport type {\n AuthMode,\n SessionStartEvent,\n SessionEndEvent,\n StepEvent,\n AgentToolEvent,\n AgentLLMEvent,\n CommandEvent,\n CrashEvent,\n TerminationReason,\n EnvFingerprint,\n} from './telemetry-types.js';\nimport { isTelemetryEnabled } from '../lib/preferences.js';\nimport { getVersion } from '../lib/settings.js';\nimport { getTelemetryUrl } from './urls.js';\nimport { getCredentials, isTokenExpired } from '../lib/credentials.js';\nimport { getActiveEnvironment, isUnclaimedEnvironment } from '../lib/config-store.js';\nimport { getDeviceId } from '../lib/device-id.js';\nimport { sanitizeMessage, sanitizeStack } from './crash-reporter.js';\n\nexport class Analytics {\n private tags: Record<string, string | boolean | number | null | undefined> = {};\n private sessionId: string;\n private sessionStartTime: Date;\n private distinctId?: string;\n private mode?: 'cli' | 'tui' | 'headless';\n private authMode: AuthMode = 'none';\n\n // Agent metrics tracking\n private totalInputTokens = 0;\n private totalOutputTokens = 0;\n private agentIterations = 0;\n\n constructor() {\n this.sessionId = uuidv4();\n this.sessionStartTime = new Date();\n this.tags = { $app_name: 'authkit-installer' };\n }\n\n setDistinctId(distinctId: string) {\n this.distinctId = distinctId;\n }\n\n setAccessToken(token: string) {\n telemetryClient.setAccessToken(token);\n }\n\n setApiKeyAuth(apiKey: string) {\n telemetryClient.setApiKeyAuth(apiKey);\n }\n\n setClaimTokenAuth(clientId: string, claimToken: string) {\n telemetryClient.setClaimTokenAuth(clientId, claimToken);\n }\n\n /**\n * Set the auth mode explicitly for special cases. Normal CLI flows should use\n * `configureAuthFromAvailableSources()` so transport and auth.mode stay aligned.\n */\n setAuthMode(mode: AuthMode) {\n this.authMode = mode;\n }\n\n setGatewayUrl(url: string) {\n telemetryClient.setGatewayUrl(url);\n }\n\n private isEnabled(): boolean {\n return isTelemetryEnabled();\n }\n\n /**\n * Configure telemetry transport and auth.mode from all available CLI auth\n * sources. Priority: stored JWT, unclaimed-environment claim token, active\n * environment API key, then WORKOS_API_KEY.\n */\n configureAuthFromAvailableSources(): AuthMode {\n if (!this.isEnabled()) return this.authMode;\n\n this.authMode = 'none';\n const creds = getCredentials();\n // Only treat the JWT as usable auth when it is still valid. An expired\n // access token would 401 against the telemetry guard and the event would\n // be dropped, so fall through to claim-token / api-key auth instead.\n if (creds?.accessToken && !isTokenExpired(creds)) {\n telemetryClient.setAccessToken(creds.accessToken);\n this.authMode = 'jwt';\n }\n // Preserve identity even when the token is expired.\n if (creds?.userId) {\n this.distinctId = creds.userId;\n }\n\n // Check for unclaimed environment — fall back to claim-token auth\n // so unclaimed users' telemetry still reaches the backend.\n try {\n const env = getActiveEnvironment();\n if (env && isUnclaimedEnvironment(env)) {\n telemetryClient.setClaimTokenAuth(env.clientId, env.claimToken);\n // Tag distinctId so unclaimed sessions are identifiable in analytics\n this.distinctId = this.distinctId ?? `unclaimed:${env.clientId}`;\n if (this.authMode === 'none') this.authMode = 'claim_token';\n } else if (env?.apiKey && this.authMode === 'none') {\n telemetryClient.setApiKeyAuth(env.apiKey);\n if (env.clientId) this.distinctId = this.distinctId ?? `env:${env.clientId}`;\n this.authMode = 'api_key';\n }\n } catch {\n // Config-store failure is non-fatal for telemetry\n }\n\n // WORKOS_API_KEY covers API-key-only users. Lowest priority — JWT and\n // claim-token auth have richer identity context when available.\n if (this.authMode === 'none' && process.env.WORKOS_API_KEY) {\n telemetryClient.setApiKeyAuth(process.env.WORKOS_API_KEY);\n this.authMode = 'api_key';\n }\n\n return this.authMode;\n }\n\n /**\n * Initialize telemetry for non-installer commands.\n * Sets telemetry URL from default config and loads auth credentials.\n */\n initForNonInstaller(): void {\n if (!this.isEnabled()) return;\n\n telemetryClient.setGatewayUrl(getTelemetryUrl());\n this.configureAuthFromAvailableSources();\n }\n\n setTag(key: string, value: string | boolean | number | null | undefined) {\n this.tags[key] = value;\n }\n\n capture(eventName: string, properties?: Record<string, unknown>) {\n if (!this.isEnabled()) return;\n\n debug(`[Analytics] capture: ${eventName}`, properties);\n\n // Accumulate primitive values as tags for the session.end event\n if (properties) {\n for (const [key, value] of Object.entries(properties)) {\n if (['string', 'number', 'boolean'].includes(typeof value)) {\n this.tags[key] = value as string | number | boolean;\n }\n }\n }\n }\n\n captureException(error: Error, properties: Record<string, unknown> = {}) {\n if (!this.isEnabled()) return;\n\n // Sanitize BEFORE logging — raw error.message can carry Bearer tokens /\n // sk_ keys / JWTs on auth-failure paths, which would surface in stdout\n // under WORKOS_DEBUG=1.\n const { type, message } = this.extractErrorFields(error);\n debug('[Analytics] captureException:', message, properties);\n this.tags['error.type'] = type;\n this.tags['error.message'] = message;\n }\n\n async getFeatureFlag(_flagKey: string): Promise<string | boolean | undefined> {\n // Feature flags not implemented yet\n return undefined;\n }\n\n /** All capture methods that record error details MUST go through this. */\n private extractErrorFields(error: Error): { type: string; message: string } {\n return {\n type: error.name,\n message: sanitizeMessage(error.message),\n };\n }\n\n private detectCiProvider(): string | undefined {\n if (process.env.GITHUB_ACTIONS) return 'github-actions';\n if (process.env.BUILDKITE) return 'buildkite';\n if (process.env.CIRCLECI) return 'circleci';\n if (process.env.GITLAB_CI) return 'gitlab-ci';\n if (process.env.JENKINS_URL) return 'jenkins';\n return undefined;\n }\n\n private getEnvFingerprint(): EnvFingerprint {\n let osVersion: string;\n try {\n osVersion = os.release();\n } catch {\n osVersion = 'unknown';\n }\n\n const ciProvider = this.detectCiProvider();\n\n return {\n 'device.id': getDeviceId(),\n 'auth.mode': this.authMode,\n 'env.os': process.platform,\n 'env.os_version': osVersion,\n 'env.node_version': process.version,\n 'env.shell': basename(process.env.SHELL ?? process.env.COMSPEC ?? 'unknown'),\n 'env.ci': Boolean(process.env.CI || process.env.GITHUB_ACTIONS || process.env.BUILDKITE),\n ...(ciProvider ? { 'env.ci_provider': ciProvider } : {}),\n };\n }\n\n sessionStart(mode: 'cli' | 'tui' | 'headless', version: string) {\n if (!this.isEnabled()) return;\n\n this.mode = mode;\n\n const event: SessionStartEvent = {\n type: 'session.start',\n sessionId: this.sessionId,\n timestamp: this.sessionStartTime.toISOString(),\n attributes: {\n 'installer.version': version,\n 'installer.mode': mode,\n 'workos.user_id': this.distinctId,\n ...this.getEnvFingerprint(),\n },\n };\n\n telemetryClient.queueEvent(event);\n }\n\n stepCompleted(name: string, durationMs: number, success: boolean, error?: Error) {\n if (!this.isEnabled()) return;\n\n const event: StepEvent = {\n type: 'step',\n sessionId: this.sessionId,\n timestamp: new Date().toISOString(),\n name,\n startTimestamp: new Date(Date.now() - durationMs).toISOString(),\n durationMs,\n success,\n error: error ? this.extractErrorFields(error) : undefined,\n };\n\n telemetryClient.queueEvent(event);\n }\n\n toolCalled(toolName: string, durationMs: number, success: boolean) {\n if (!this.isEnabled()) return;\n\n const event: AgentToolEvent = {\n type: 'agent.tool',\n sessionId: this.sessionId,\n timestamp: new Date().toISOString(),\n toolName,\n startTimestamp: new Date(Date.now() - durationMs).toISOString(),\n durationMs,\n success,\n };\n\n telemetryClient.queueEvent(event);\n }\n\n llmRequest(model: string, inputTokens: number, outputTokens: number) {\n if (!this.isEnabled()) return;\n\n this.totalInputTokens += inputTokens;\n this.totalOutputTokens += outputTokens;\n\n const event: AgentLLMEvent = {\n type: 'agent.llm',\n sessionId: this.sessionId,\n timestamp: new Date().toISOString(),\n model,\n inputTokens,\n outputTokens,\n };\n\n telemetryClient.queueEvent(event);\n }\n\n incrementAgentIterations() {\n this.agentIterations++;\n }\n\n emitCommandEvent(\n name: string,\n durationMs: number,\n success: boolean,\n options?: {\n error?: Error;\n flags?: string[];\n reason?: TerminationReason;\n errorCode?: string;\n apiContext?: { status?: number; code?: string; resource?: string };\n extraAttributes?: Record<string, string | number | boolean>;\n },\n ) {\n if (!this.isEnabled()) return;\n\n const errorFields = options?.error ? this.extractErrorFields(options.error) : undefined;\n\n const event: CommandEvent = {\n type: 'command',\n sessionId: this.sessionId,\n timestamp: new Date().toISOString(),\n attributes: {\n // Extras first: standard fields below win on key collision, so callers\n // can never override command.name, error fields, or the fingerprint.\n ...options?.extraAttributes,\n 'command.name': name,\n 'command.duration_ms': durationMs,\n 'command.success': success,\n 'cli.version': getVersion(),\n ...(this.distinctId ? { 'workos.user_id': this.distinctId } : {}),\n ...(errorFields\n ? {\n 'command.error_type': errorFields.type,\n 'command.error_message': errorFields.message,\n }\n : {}),\n ...(options?.flags?.length ? { 'command.flags': options.flags.join(',') } : {}),\n ...(options?.reason ? { 'termination.reason': options.reason } : {}),\n ...(options?.errorCode ? { 'error.code': options.errorCode } : {}),\n ...(options?.apiContext?.status !== undefined ? { 'api.status': options.apiContext.status } : {}),\n ...(options?.apiContext?.code ? { 'api.code': options.apiContext.code } : {}),\n ...(options?.apiContext?.resource ? { 'api.resource': options.apiContext.resource } : {}),\n ...this.getEnvFingerprint(),\n },\n };\n\n telemetryClient.queueEvent(event);\n }\n\n captureUnhandledCrash(error: Error, options?: { command?: string; version?: string }) {\n if (!this.isEnabled()) return;\n\n const { type, message } = this.extractErrorFields(error);\n\n const event: CrashEvent = {\n type: 'crash',\n sessionId: this.sessionId,\n timestamp: new Date().toISOString(),\n attributes: {\n 'crash.error_type': type,\n 'crash.error_message': message,\n 'crash.stack': sanitizeStack(error.stack),\n ...(options?.command ? { 'crash.command': options.command } : {}),\n 'cli.version': options?.version ?? getVersion(),\n ...(this.distinctId ? { 'workos.user_id': this.distinctId } : {}),\n ...this.getEnvFingerprint(),\n },\n };\n\n telemetryClient.queueEvent(event);\n }\n\n async shutdown(status: 'success' | 'error' | 'cancelled') {\n if (!this.isEnabled()) return;\n\n const duration = Date.now() - this.sessionStartTime.getTime();\n\n // Filter out null/undefined tags\n const extraAttributes = Object.fromEntries(Object.entries(this.tags).filter(([, v]) => v != null)) as Record<\n string,\n string | number | boolean\n >;\n\n const envFingerprint = this.getEnvFingerprint();\n\n const event: SessionEndEvent = {\n type: 'session.end',\n sessionId: this.sessionId,\n timestamp: new Date().toISOString(),\n attributes: {\n 'installer.outcome': status,\n 'installer.duration_ms': duration,\n 'installer.agent.iterations': this.agentIterations,\n 'installer.agent.tokens.input': this.totalInputTokens,\n 'installer.agent.tokens.output': this.totalOutputTokens,\n ...envFingerprint,\n ...(this.mode ? { 'installer.mode': this.mode } : {}),\n ...extraAttributes,\n },\n };\n\n telemetryClient.queueEvent(event);\n await telemetryClient.flush();\n }\n}\n\nexport const analytics = new Analytics();\n"]}
@@ -142,6 +142,44 @@ const commands = [
142
142
  },
143
143
  ],
144
144
  },
145
+ {
146
+ name: 'mcp',
147
+ description: 'Manage the WorkOS MCP server in coding agents (Claude Code, Codex, Cursor)',
148
+ commands: [
149
+ {
150
+ name: 'install',
151
+ description: 'Add the WorkOS MCP server to detected coding agents',
152
+ options: [
153
+ {
154
+ name: 'agent',
155
+ type: 'array',
156
+ description: 'Target specific agent(s): claude-code, codex, cursor',
157
+ required: false,
158
+ alias: 'a',
159
+ hidden: false,
160
+ },
161
+ ],
162
+ },
163
+ {
164
+ name: 'remove',
165
+ description: 'Remove the WorkOS MCP server from coding agents',
166
+ options: [
167
+ {
168
+ name: 'agent',
169
+ type: 'array',
170
+ description: 'Target specific agent(s): claude-code, codex, cursor',
171
+ required: false,
172
+ alias: 'a',
173
+ hidden: false,
174
+ },
175
+ ],
176
+ },
177
+ {
178
+ name: 'status',
179
+ description: 'Show which coding agents have the WorkOS MCP server configured',
180
+ },
181
+ ],
182
+ },
145
183
  {
146
184
  name: 'doctor',
147
185
  description: 'Diagnose WorkOS AuthKit integration issues in the current project',