workos 0.3.3 → 0.5.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 (152) hide show
  1. package/dist/bin.js +57 -9
  2. package/dist/bin.js.map +1 -1
  3. package/dist/commands/doctor.d.ts +10 -0
  4. package/dist/commands/doctor.js +30 -0
  5. package/dist/commands/doctor.js.map +1 -0
  6. package/dist/commands/login.js +21 -1
  7. package/dist/commands/login.js.map +1 -1
  8. package/dist/doctor/checks/connectivity.d.ts +2 -0
  9. package/dist/doctor/checks/connectivity.js +35 -0
  10. package/dist/doctor/checks/connectivity.js.map +1 -0
  11. package/dist/doctor/checks/dashboard.d.ts +3 -0
  12. package/dist/doctor/checks/dashboard.js +123 -0
  13. package/dist/doctor/checks/dashboard.js.map +1 -0
  14. package/dist/doctor/checks/environment.d.ts +2 -0
  15. package/dist/doctor/checks/environment.js +68 -0
  16. package/dist/doctor/checks/environment.js.map +1 -0
  17. package/dist/doctor/checks/framework.d.ts +2 -0
  18. package/dist/doctor/checks/framework.js +75 -0
  19. package/dist/doctor/checks/framework.js.map +1 -0
  20. package/dist/doctor/checks/runtime.d.ts +2 -0
  21. package/dist/doctor/checks/runtime.js +20 -0
  22. package/dist/doctor/checks/runtime.js.map +1 -0
  23. package/dist/doctor/checks/sdk.d.ts +2 -0
  24. package/dist/doctor/checks/sdk.js +111 -0
  25. package/dist/doctor/checks/sdk.js.map +1 -0
  26. package/dist/doctor/clipboard.d.ts +1 -0
  27. package/dist/doctor/clipboard.js +43 -0
  28. package/dist/doctor/clipboard.js.map +1 -0
  29. package/dist/doctor/index.d.ts +6 -0
  30. package/dist/doctor/index.js +94 -0
  31. package/dist/doctor/index.js.map +1 -0
  32. package/dist/doctor/issues.d.ts +58 -0
  33. package/dist/doctor/issues.js +134 -0
  34. package/dist/doctor/issues.js.map +1 -0
  35. package/dist/doctor/json-output.d.ts +2 -0
  36. package/dist/doctor/json-output.js +4 -0
  37. package/dist/doctor/json-output.js.map +1 -0
  38. package/dist/doctor/output.d.ts +5 -0
  39. package/dist/doctor/output.js +149 -0
  40. package/dist/doctor/output.js.map +1 -0
  41. package/dist/doctor/types.d.ts +105 -0
  42. package/dist/doctor/types.js +2 -0
  43. package/dist/doctor/types.js.map +1 -0
  44. package/dist/integrations/dotnet/index.d.ts +8 -0
  45. package/dist/integrations/dotnet/index.js +163 -0
  46. package/dist/integrations/dotnet/index.js.map +1 -0
  47. package/dist/integrations/elixir/index.d.ts +8 -0
  48. package/dist/integrations/elixir/index.js +152 -0
  49. package/dist/integrations/elixir/index.js.map +1 -0
  50. package/dist/integrations/go/index.d.ts +11 -0
  51. package/dist/integrations/go/index.js +220 -0
  52. package/dist/integrations/go/index.js.map +1 -0
  53. package/dist/integrations/kotlin/index.d.ts +4 -0
  54. package/dist/integrations/kotlin/index.js +53 -0
  55. package/dist/integrations/kotlin/index.js.map +1 -0
  56. package/dist/integrations/nextjs/index.d.ts +4 -0
  57. package/dist/integrations/nextjs/index.js +90 -0
  58. package/dist/integrations/nextjs/index.js.map +1 -0
  59. package/dist/integrations/nextjs/utils.d.ts +8 -0
  60. package/dist/integrations/nextjs/utils.js +53 -0
  61. package/dist/integrations/nextjs/utils.js.map +1 -0
  62. package/dist/integrations/node/index.d.ts +4 -0
  63. package/dist/integrations/node/index.js +52 -0
  64. package/dist/integrations/node/index.js.map +1 -0
  65. package/dist/integrations/php/index.d.ts +4 -0
  66. package/dist/integrations/php/index.js +51 -0
  67. package/dist/integrations/php/index.js.map +1 -0
  68. package/dist/integrations/php-laravel/index.d.ts +4 -0
  69. package/dist/integrations/php-laravel/index.js +51 -0
  70. package/dist/integrations/php-laravel/index.js.map +1 -0
  71. package/dist/integrations/python/index.d.ts +9 -0
  72. package/dist/integrations/python/index.js +254 -0
  73. package/dist/integrations/python/index.js.map +1 -0
  74. package/dist/integrations/react/index.d.ts +4 -0
  75. package/dist/integrations/react/index.js +49 -0
  76. package/dist/integrations/react/index.js.map +1 -0
  77. package/dist/integrations/react-router/index.d.ts +4 -0
  78. package/dist/integrations/react-router/index.js +94 -0
  79. package/dist/integrations/react-router/index.js.map +1 -0
  80. package/dist/integrations/react-router/utils.d.ts +10 -0
  81. package/dist/integrations/react-router/utils.js +146 -0
  82. package/dist/integrations/react-router/utils.js.map +1 -0
  83. package/dist/integrations/ruby/index.d.ts +8 -0
  84. package/dist/integrations/ruby/index.js +142 -0
  85. package/dist/integrations/ruby/index.js.map +1 -0
  86. package/dist/integrations/sveltekit/index.d.ts +4 -0
  87. package/dist/integrations/sveltekit/index.js +50 -0
  88. package/dist/integrations/sveltekit/index.js.map +1 -0
  89. package/dist/integrations/tanstack-start/index.d.ts +4 -0
  90. package/dist/integrations/tanstack-start/index.js +51 -0
  91. package/dist/integrations/tanstack-start/index.js.map +1 -0
  92. package/dist/integrations/vanilla-js/index.d.ts +4 -0
  93. package/dist/integrations/vanilla-js/index.js +49 -0
  94. package/dist/integrations/vanilla-js/index.js.map +1 -0
  95. package/dist/lib/agent-interface.js +66 -1
  96. package/dist/lib/agent-interface.js.map +1 -1
  97. package/dist/lib/config.d.ts +32 -58
  98. package/dist/lib/config.js +19 -70
  99. package/dist/lib/config.js.map +1 -1
  100. package/dist/lib/constants.d.ts +17 -14
  101. package/dist/lib/constants.js +12 -31
  102. package/dist/lib/constants.js.map +1 -1
  103. package/dist/lib/credential-store.d.ts +28 -0
  104. package/dist/lib/credential-store.js +150 -0
  105. package/dist/lib/credential-store.js.map +1 -0
  106. package/dist/lib/credentials.d.ts +3 -37
  107. package/dist/lib/credentials.js +2 -85
  108. package/dist/lib/credentials.js.map +1 -1
  109. package/dist/lib/framework-config.d.ts +13 -4
  110. package/dist/lib/framework-config.js.map +1 -1
  111. package/dist/lib/language-detection.d.ts +20 -0
  112. package/dist/lib/language-detection.js +96 -0
  113. package/dist/lib/language-detection.js.map +1 -0
  114. package/dist/lib/port-detection.js +4 -2
  115. package/dist/lib/port-detection.js.map +1 -1
  116. package/dist/lib/registry.d.ts +43 -0
  117. package/dist/lib/registry.js +96 -0
  118. package/dist/lib/registry.js.map +1 -0
  119. package/dist/lib/run-with-core.js +70 -26
  120. package/dist/lib/run-with-core.js.map +1 -1
  121. package/dist/nextjs/nextjs-installer-agent.d.ts +3 -4
  122. package/dist/nextjs/nextjs-installer-agent.js +3 -94
  123. package/dist/nextjs/nextjs-installer-agent.js.map +1 -1
  124. package/dist/nextjs/utils.d.ts +4 -8
  125. package/dist/nextjs/utils.js +4 -52
  126. package/dist/nextjs/utils.js.map +1 -1
  127. package/dist/react/react-installer-agent.d.ts +4 -2
  128. package/dist/react/react-installer-agent.js +4 -46
  129. package/dist/react/react-installer-agent.js.map +1 -1
  130. package/dist/react-router/react-router-installer-agent.d.ts +2 -4
  131. package/dist/react-router/react-router-installer-agent.js +2 -100
  132. package/dist/react-router/react-router-installer-agent.js.map +1 -1
  133. package/dist/react-router/utils.d.ts +2 -17
  134. package/dist/react-router/utils.js +2 -207
  135. package/dist/react-router/utils.js.map +1 -1
  136. package/dist/tanstack-start/tanstack-start-installer-agent.d.ts +4 -2
  137. package/dist/tanstack-start/tanstack-start-installer-agent.js +4 -48
  138. package/dist/tanstack-start/tanstack-start-installer-agent.js.map +1 -1
  139. package/dist/vanilla-js/vanilla-js-installer-agent.d.ts +4 -2
  140. package/dist/vanilla-js/vanilla-js-installer-agent.js +4 -46
  141. package/dist/vanilla-js/vanilla-js-installer-agent.js.map +1 -1
  142. package/package.json +9 -6
  143. package/skills/workos-authkit-sveltekit/SKILL.md +160 -0
  144. package/skills/workos-dotnet/SKILL.md +163 -0
  145. package/skills/workos-elixir/SKILL.md +194 -0
  146. package/skills/workos-go/SKILL.md +191 -0
  147. package/skills/workos-kotlin/SKILL.md +161 -0
  148. package/skills/workos-node/SKILL.md +164 -0
  149. package/skills/workos-php/SKILL.md +127 -0
  150. package/skills/workos-php-laravel/SKILL.md +147 -0
  151. package/skills/workos-python/SKILL.md +159 -0
  152. package/skills/workos-ruby/SKILL.md +163 -0
package/dist/bin.js CHANGED
@@ -7,7 +7,7 @@ if (process.argv.includes('--local') || process.env.INSTALLER_DEV) {
7
7
  }
8
8
  import { satisfies } from 'semver';
9
9
  import { red } from './utils/logging.js';
10
- import { getConfig } from './lib/settings.js';
10
+ import { getConfig, getVersion } from './lib/settings.js';
11
11
  import yargs from 'yargs';
12
12
  import { hideBin } from 'yargs/helpers';
13
13
  import { ensureAuthenticated } from './lib/ensure-auth.js';
@@ -21,7 +21,21 @@ if (!satisfies(process.version, NODE_VERSION_RANGE)) {
21
21
  }
22
22
  import { isNonInteractiveEnvironment } from './utils/environment.js';
23
23
  import clack from './utils/clack.js';
24
- // Shared options for wizard commands (default and dashboard)
24
+ /** Apply insecure storage flag if set */
25
+ async function applyInsecureStorage(insecureStorage) {
26
+ if (insecureStorage) {
27
+ const { setInsecureStorage } = await import('./lib/credentials.js');
28
+ setInsecureStorage(true);
29
+ }
30
+ }
31
+ /** Shared insecure-storage option for commands that access credentials */
32
+ const insecureStorageOption = {
33
+ 'insecure-storage': {
34
+ default: false,
35
+ describe: 'Store credentials in plaintext file instead of system keyring',
36
+ type: 'boolean',
37
+ },
38
+ };
25
39
  /**
26
40
  * Wrap a command handler with authentication check.
27
41
  * Ensures valid auth before executing the handler.
@@ -29,9 +43,10 @@ import clack from './utils/clack.js';
29
43
  */
30
44
  function withAuth(handler) {
31
45
  return async (argv) => {
32
- if (!argv.skipAuth) {
46
+ const typedArgv = argv;
47
+ await applyInsecureStorage(typedArgv.insecureStorage);
48
+ if (!typedArgv.skipAuth)
33
49
  await ensureAuthenticated();
34
- }
35
50
  await handler(argv);
36
51
  };
37
52
  }
@@ -47,6 +62,7 @@ const installerOptions = {
47
62
  describe: 'Enable verbose logging',
48
63
  type: 'boolean',
49
64
  },
65
+ ...insecureStorageOption,
50
66
  // Hidden dev/automation flags (use env vars)
51
67
  local: {
52
68
  default: false,
@@ -96,7 +112,6 @@ const installerOptions = {
96
112
  },
97
113
  integration: {
98
114
  describe: 'Integration to set up',
99
- choices: ['nextjs', 'react', 'tanstack-start', 'react-router', 'vanilla-js'],
100
115
  type: 'string',
101
116
  },
102
117
  'force-install': {
@@ -115,12 +130,14 @@ const installerOptions = {
115
130
  await checkForUpdates();
116
131
  yargs(hideBin(process.argv))
117
132
  .env('WORKOS_INSTALLER')
118
- .command('login', 'Authenticate with WorkOS', {}, async () => {
133
+ .command('login', 'Authenticate with WorkOS', insecureStorageOption, async (argv) => {
134
+ await applyInsecureStorage(argv.insecureStorage);
119
135
  const { runLogin } = await import('./commands/login.js');
120
136
  await runLogin();
121
137
  process.exit(0);
122
138
  })
123
- .command('logout', 'Remove stored credentials', {}, async () => {
139
+ .command('logout', 'Remove stored credentials', insecureStorageOption, async (argv) => {
140
+ await applyInsecureStorage(argv.insecureStorage);
124
141
  const { runLogout } = await import('./commands/logout.js');
125
142
  await runLogout();
126
143
  })
@@ -151,6 +168,36 @@ yargs(hideBin(process.argv))
151
168
  agent: argv.agent,
152
169
  });
153
170
  }))
171
+ .command('doctor', 'Diagnose WorkOS integration issues', (yargs) => yargs.options({
172
+ verbose: {
173
+ type: 'boolean',
174
+ default: false,
175
+ description: 'Include additional diagnostic information',
176
+ },
177
+ 'skip-api': {
178
+ type: 'boolean',
179
+ default: false,
180
+ description: 'Skip API calls (offline mode)',
181
+ },
182
+ 'install-dir': {
183
+ type: 'string',
184
+ default: process.cwd(),
185
+ description: 'Project directory to analyze',
186
+ },
187
+ json: {
188
+ type: 'boolean',
189
+ default: false,
190
+ description: 'Output report as JSON',
191
+ },
192
+ copy: {
193
+ type: 'boolean',
194
+ default: false,
195
+ description: 'Copy report to clipboard',
196
+ },
197
+ }), async (argv) => {
198
+ const { handleDoctor } = await import('./commands/doctor.js');
199
+ await handleDoctor(argv);
200
+ })
154
201
  .command('install', 'Install WorkOS AuthKit into your project', (yargs) => yargs.options(installerOptions), withAuth(async (argv) => {
155
202
  const { handleInstall } = await import('./commands/install.js');
156
203
  await handleInstall(argv);
@@ -160,7 +207,7 @@ yargs(hideBin(process.argv))
160
207
  const { handleInstall } = await import('./commands/install.js');
161
208
  await handleInstall({ ...argv, dashboard: true });
162
209
  }))
163
- .command(['$0'], 'WorkOS AuthKit CLI', (yargs) => yargs, async () => {
210
+ .command(['$0'], 'WorkOS AuthKit CLI', (yargs) => yargs.options(insecureStorageOption), async (argv) => {
164
211
  // Non-TTY: show help
165
212
  if (isNonInteractiveEnvironment()) {
166
213
  yargs(hideBin(process.argv)).showHelp();
@@ -174,6 +221,7 @@ yargs(hideBin(process.argv))
174
221
  process.exit(0);
175
222
  }
176
223
  // Auth check happens HERE, after user confirms
224
+ await applyInsecureStorage(argv.insecureStorage);
177
225
  await ensureAuthenticated();
178
226
  const { handleInstall } = await import('./commands/install.js');
179
227
  await handleInstall({ dashboard: false });
@@ -182,7 +230,7 @@ yargs(hideBin(process.argv))
182
230
  .strict()
183
231
  .help()
184
232
  .alias('help', 'h')
185
- .version()
233
+ .version(getVersion())
186
234
  .alias('version', 'v')
187
235
  .wrap(process.stdout.isTTY && process.stdout.columns ? process.stdout.columns : 80).argv;
188
236
  //# sourceMappingURL=bin.js.map
package/dist/bin.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"bin.js","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":"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","sourcesContent":["#!/usr/bin/env node\n\n// Load .env.local for local development when --local flag is used\nif (process.argv.includes('--local') || process.env.INSTALLER_DEV) {\n const { config } = await import('dotenv');\n // bin.ts compiles to dist/bin.js, so go up one level to find .env.local\n config({ path: new URL('../.env.local', import.meta.url).pathname });\n}\n\nimport { satisfies } from 'semver';\nimport { red } from './utils/logging.js';\nimport { getConfig } from './lib/settings.js';\n\nimport yargs from 'yargs';\nimport { hideBin } from 'yargs/helpers';\nimport chalk from 'chalk';\nimport { ensureAuthenticated } from './lib/ensure-auth.js';\nimport { checkForUpdates } from './lib/version-check.js';\n\nconst NODE_VERSION_RANGE = getConfig().nodeVersion;\n\n// Have to run this above the other imports because they are importing clack that\n// has the problematic imports.\nif (!satisfies(process.version, NODE_VERSION_RANGE)) {\n red(\n `WorkOS AuthKit installer requires Node.js ${NODE_VERSION_RANGE}. You are using Node.js ${process.version}. Please upgrade your Node.js version.`,\n );\n process.exit(1);\n}\n\nimport { isNonInteractiveEnvironment } from './utils/environment.js';\nimport clack from './utils/clack.js';\n\n// Shared options for wizard commands (default and dashboard)\n/**\n * Wrap a command handler with authentication check.\n * Ensures valid auth before executing the handler.\n * Respects --skip-auth flag for CI/testing.\n */\nfunction withAuth<T>(handler: (argv: T) => Promise<void>): (argv: T) => Promise<void> {\n return async (argv: T) => {\n if (!(argv as { skipAuth?: boolean }).skipAuth) {\n await ensureAuthenticated();\n }\n await handler(argv);\n };\n}\n\nconst installerOptions = {\n direct: {\n alias: 'D',\n default: false,\n describe: 'Use your own Anthropic API key (bypass llm-gateway)',\n type: 'boolean' as const,\n },\n debug: {\n default: false,\n describe: 'Enable verbose logging',\n type: 'boolean' as const,\n },\n // Hidden dev/automation flags (use env vars)\n local: {\n default: false,\n type: 'boolean' as const,\n hidden: true,\n },\n ci: {\n default: false,\n type: 'boolean' as const,\n hidden: true,\n },\n 'skip-auth': {\n default: false,\n type: 'boolean' as const,\n hidden: true,\n },\n 'api-key': {\n type: 'string' as const,\n hidden: true,\n },\n 'client-id': {\n type: 'string' as const,\n hidden: true,\n },\n inspect: {\n default: false,\n type: 'boolean' as const,\n hidden: true,\n },\n // User-facing flags\n 'homepage-url': {\n describe: 'App homepage URL for WorkOS (defaults to http://localhost:{port})',\n type: 'string' as const,\n },\n 'redirect-uri': {\n describe: 'Redirect URI for WorkOS callback (defaults to framework convention)',\n type: 'string' as const,\n },\n 'no-validate': {\n default: false,\n describe: 'Skip post-installation validation (includes build check)',\n type: 'boolean' as const,\n },\n 'install-dir': {\n describe: 'Directory to install WorkOS AuthKit in',\n type: 'string' as const,\n },\n integration: {\n describe: 'Integration to set up',\n choices: ['nextjs', 'react', 'tanstack-start', 'react-router', 'vanilla-js'] as const,\n type: 'string' as const,\n },\n 'force-install': {\n default: false,\n describe: 'Force install packages even if peer dependency checks fail',\n type: 'boolean' as const,\n },\n dashboard: {\n alias: 'd',\n default: false,\n describe: 'Run with visual dashboard mode',\n type: 'boolean' as const,\n },\n};\n\n// Check for updates (blocks up to 500ms)\nawait checkForUpdates();\n\nyargs(hideBin(process.argv))\n .env('WORKOS_INSTALLER')\n .command('login', 'Authenticate with WorkOS', {}, async () => {\n const { runLogin } = await import('./commands/login.js');\n await runLogin();\n process.exit(0);\n })\n .command('logout', 'Remove stored credentials', {}, async () => {\n const { runLogout } = await import('./commands/logout.js');\n await runLogout();\n })\n .command(\n 'install-skill',\n 'Install bundled AuthKit skills to coding agents',\n (yargs) => {\n return yargs\n .option('list', {\n alias: 'l',\n type: 'boolean',\n description: 'List available skills without installing',\n })\n .option('skill', {\n alias: 's',\n type: 'array',\n string: true,\n description: 'Install specific skill(s)',\n })\n .option('agent', {\n alias: 'a',\n type: 'array',\n string: true,\n description: 'Target specific agent(s): claude-code, codex, cursor, goose',\n });\n },\n withAuth(async (argv) => {\n const { runInstallSkill } = await import('./commands/install-skill.js');\n await runInstallSkill({\n list: argv.list as boolean | undefined,\n skill: argv.skill as string[] | undefined,\n agent: argv.agent as string[] | undefined,\n });\n }),\n )\n .command(\n 'install',\n 'Install WorkOS AuthKit into your project',\n (yargs) => yargs.options(installerOptions),\n withAuth(async (argv) => {\n const { handleInstall } = await import('./commands/install.js');\n await handleInstall(argv);\n }),\n )\n .command(\n 'dashboard',\n false, // hidden from help\n (yargs) => yargs.options(installerOptions),\n withAuth(async (argv) => {\n const { handleInstall } = await import('./commands/install.js');\n await handleInstall({ ...argv, dashboard: true });\n }),\n )\n .command(\n ['$0'],\n 'WorkOS AuthKit CLI',\n (yargs) => yargs,\n async () => {\n // Non-TTY: show help\n if (isNonInteractiveEnvironment()) {\n yargs(hideBin(process.argv)).showHelp();\n return;\n }\n\n // TTY: ask if user wants to run installer\n const shouldInstall = await clack.confirm({\n message: 'Run the AuthKit installer?',\n });\n\n if (clack.isCancel(shouldInstall) || !shouldInstall) {\n process.exit(0);\n }\n\n // Auth check happens HERE, after user confirms\n await ensureAuthenticated();\n\n const { handleInstall } = await import('./commands/install.js');\n await handleInstall({ dashboard: false } as any);\n process.exit(0);\n },\n )\n .strict()\n .help()\n .alias('help', 'h')\n .version()\n .alias('version', 'v')\n .wrap(process.stdout.isTTY && process.stdout.columns ? process.stdout.columns : 80).argv;\n"]}
1
+ {"version":3,"file":"bin.js","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":"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","sourcesContent":["#!/usr/bin/env node\n\n// Load .env.local for local development when --local flag is used\nif (process.argv.includes('--local') || process.env.INSTALLER_DEV) {\n const { config } = await import('dotenv');\n // bin.ts compiles to dist/bin.js, so go up one level to find .env.local\n config({ path: new URL('../.env.local', import.meta.url).pathname });\n}\n\nimport { satisfies } from 'semver';\nimport { red } from './utils/logging.js';\nimport { getConfig, getVersion } from './lib/settings.js';\n\nimport yargs from 'yargs';\nimport { hideBin } from 'yargs/helpers';\nimport chalk from 'chalk';\nimport { ensureAuthenticated } from './lib/ensure-auth.js';\nimport { checkForUpdates } from './lib/version-check.js';\n\nconst NODE_VERSION_RANGE = getConfig().nodeVersion;\n\n// Have to run this above the other imports because they are importing clack that\n// has the problematic imports.\nif (!satisfies(process.version, NODE_VERSION_RANGE)) {\n red(\n `WorkOS AuthKit installer requires Node.js ${NODE_VERSION_RANGE}. You are using Node.js ${process.version}. Please upgrade your Node.js version.`,\n );\n process.exit(1);\n}\n\nimport { isNonInteractiveEnvironment } from './utils/environment.js';\nimport clack from './utils/clack.js';\n\n/** Apply insecure storage flag if set */\nasync function applyInsecureStorage(insecureStorage?: boolean): Promise<void> {\n if (insecureStorage) {\n const { setInsecureStorage } = await import('./lib/credentials.js');\n setInsecureStorage(true);\n }\n}\n\n/** Shared insecure-storage option for commands that access credentials */\nconst insecureStorageOption = {\n 'insecure-storage': {\n default: false,\n describe: 'Store credentials in plaintext file instead of system keyring',\n type: 'boolean' as const,\n },\n} as const;\n\n/**\n * Wrap a command handler with authentication check.\n * Ensures valid auth before executing the handler.\n * Respects --skip-auth flag for CI/testing.\n */\nfunction withAuth<T>(handler: (argv: T) => Promise<void>): (argv: T) => Promise<void> {\n return async (argv: T) => {\n const typedArgv = argv as { skipAuth?: boolean; insecureStorage?: boolean };\n await applyInsecureStorage(typedArgv.insecureStorage);\n if (!typedArgv.skipAuth) await ensureAuthenticated();\n await handler(argv);\n };\n}\n\nconst installerOptions = {\n direct: {\n alias: 'D',\n default: false,\n describe: 'Use your own Anthropic API key (bypass llm-gateway)',\n type: 'boolean' as const,\n },\n debug: {\n default: false,\n describe: 'Enable verbose logging',\n type: 'boolean' as const,\n },\n ...insecureStorageOption,\n // Hidden dev/automation flags (use env vars)\n local: {\n default: false,\n type: 'boolean' as const,\n hidden: true,\n },\n ci: {\n default: false,\n type: 'boolean' as const,\n hidden: true,\n },\n 'skip-auth': {\n default: false,\n type: 'boolean' as const,\n hidden: true,\n },\n 'api-key': {\n type: 'string' as const,\n hidden: true,\n },\n 'client-id': {\n type: 'string' as const,\n hidden: true,\n },\n inspect: {\n default: false,\n type: 'boolean' as const,\n hidden: true,\n },\n // User-facing flags\n 'homepage-url': {\n describe: 'App homepage URL for WorkOS (defaults to http://localhost:{port})',\n type: 'string' as const,\n },\n 'redirect-uri': {\n describe: 'Redirect URI for WorkOS callback (defaults to framework convention)',\n type: 'string' as const,\n },\n 'no-validate': {\n default: false,\n describe: 'Skip post-installation validation (includes build check)',\n type: 'boolean' as const,\n },\n 'install-dir': {\n describe: 'Directory to install WorkOS AuthKit in',\n type: 'string' as const,\n },\n integration: {\n describe: 'Integration to set up',\n type: 'string' as const,\n },\n 'force-install': {\n default: false,\n describe: 'Force install packages even if peer dependency checks fail',\n type: 'boolean' as const,\n },\n dashboard: {\n alias: 'd',\n default: false,\n describe: 'Run with visual dashboard mode',\n type: 'boolean' as const,\n },\n};\n\n// Check for updates (blocks up to 500ms)\nawait checkForUpdates();\n\nyargs(hideBin(process.argv))\n .env('WORKOS_INSTALLER')\n .command('login', 'Authenticate with WorkOS', insecureStorageOption, async (argv) => {\n await applyInsecureStorage(argv.insecureStorage);\n const { runLogin } = await import('./commands/login.js');\n await runLogin();\n process.exit(0);\n })\n .command('logout', 'Remove stored credentials', insecureStorageOption, async (argv) => {\n await applyInsecureStorage(argv.insecureStorage);\n const { runLogout } = await import('./commands/logout.js');\n await runLogout();\n })\n .command(\n 'install-skill',\n 'Install bundled AuthKit skills to coding agents',\n (yargs) => {\n return yargs\n .option('list', {\n alias: 'l',\n type: 'boolean',\n description: 'List available skills without installing',\n })\n .option('skill', {\n alias: 's',\n type: 'array',\n string: true,\n description: 'Install specific skill(s)',\n })\n .option('agent', {\n alias: 'a',\n type: 'array',\n string: true,\n description: 'Target specific agent(s): claude-code, codex, cursor, goose',\n });\n },\n withAuth(async (argv) => {\n const { runInstallSkill } = await import('./commands/install-skill.js');\n await runInstallSkill({\n list: argv.list as boolean | undefined,\n skill: argv.skill as string[] | undefined,\n agent: argv.agent as string[] | undefined,\n });\n }),\n )\n .command(\n 'doctor',\n 'Diagnose WorkOS integration issues',\n (yargs) =>\n yargs.options({\n verbose: {\n type: 'boolean',\n default: false,\n description: 'Include additional diagnostic information',\n },\n 'skip-api': {\n type: 'boolean',\n default: false,\n description: 'Skip API calls (offline mode)',\n },\n 'install-dir': {\n type: 'string',\n default: process.cwd(),\n description: 'Project directory to analyze',\n },\n json: {\n type: 'boolean',\n default: false,\n description: 'Output report as JSON',\n },\n copy: {\n type: 'boolean',\n default: false,\n description: 'Copy report to clipboard',\n },\n }),\n async (argv) => {\n const { handleDoctor } = await import('./commands/doctor.js');\n await handleDoctor(argv);\n },\n )\n .command(\n 'install',\n 'Install WorkOS AuthKit into your project',\n (yargs) => yargs.options(installerOptions),\n withAuth(async (argv) => {\n const { handleInstall } = await import('./commands/install.js');\n await handleInstall(argv);\n }),\n )\n .command(\n 'dashboard',\n false, // hidden from help\n (yargs) => yargs.options(installerOptions),\n withAuth(async (argv) => {\n const { handleInstall } = await import('./commands/install.js');\n await handleInstall({ ...argv, dashboard: true });\n }),\n )\n .command(\n ['$0'],\n 'WorkOS AuthKit CLI',\n (yargs) => yargs.options(insecureStorageOption),\n async (argv) => {\n // Non-TTY: show help\n if (isNonInteractiveEnvironment()) {\n yargs(hideBin(process.argv)).showHelp();\n return;\n }\n\n // TTY: ask if user wants to run installer\n const shouldInstall = await clack.confirm({\n message: 'Run the AuthKit installer?',\n });\n\n if (clack.isCancel(shouldInstall) || !shouldInstall) {\n process.exit(0);\n }\n\n // Auth check happens HERE, after user confirms\n await applyInsecureStorage(argv.insecureStorage);\n await ensureAuthenticated();\n\n const { handleInstall } = await import('./commands/install.js');\n await handleInstall({ dashboard: false } as any);\n process.exit(0);\n },\n )\n .strict()\n .help()\n .alias('help', 'h')\n .version(getVersion())\n .alias('version', 'v')\n .wrap(process.stdout.isTTY && process.stdout.columns ? process.stdout.columns : 80).argv;\n"]}
@@ -0,0 +1,10 @@
1
+ import type { ArgumentsCamelCase } from 'yargs';
2
+ interface DoctorArgs {
3
+ verbose?: boolean;
4
+ skipApi?: boolean;
5
+ installDir?: string;
6
+ json?: boolean;
7
+ copy?: boolean;
8
+ }
9
+ export declare function handleDoctor(argv: ArgumentsCamelCase<DoctorArgs>): Promise<void>;
10
+ export {};
@@ -0,0 +1,30 @@
1
+ import { runDoctor, outputReport } from '../doctor/index.js';
2
+ import clack from '../utils/clack.js';
3
+ export async function handleDoctor(argv) {
4
+ const options = {
5
+ installDir: argv.installDir ?? process.cwd(),
6
+ verbose: argv.verbose ?? false,
7
+ skipApi: argv.skipApi ?? false,
8
+ json: argv.json ?? false,
9
+ copy: argv.copy ?? false,
10
+ };
11
+ try {
12
+ const report = await runDoctor(options);
13
+ await outputReport(report, options);
14
+ // Exit with error code if critical issues found
15
+ if (report.summary.errors > 0) {
16
+ process.exit(1);
17
+ }
18
+ process.exit(0);
19
+ }
20
+ catch (error) {
21
+ if (!options.json) {
22
+ clack.log.error(`Doctor failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
23
+ }
24
+ else {
25
+ console.error(JSON.stringify({ error: error instanceof Error ? error.message : 'Unknown error' }));
26
+ }
27
+ process.exit(1);
28
+ }
29
+ }
30
+ //# sourceMappingURL=doctor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAC7D,OAAO,KAAK,MAAM,mBAAmB,CAAC;AAUtC,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,IAAoC;IACrE,MAAM,OAAO,GAAG;QACd,UAAU,EAAE,IAAI,CAAC,UAAU,IAAI,OAAO,CAAC,GAAG,EAAE;QAC5C,OAAO,EAAE,IAAI,CAAC,OAAO,IAAI,KAAK;QAC9B,OAAO,EAAE,IAAI,CAAC,OAAO,IAAI,KAAK;QAC9B,IAAI,EAAE,IAAI,CAAC,IAAI,IAAI,KAAK;QACxB,IAAI,EAAE,IAAI,CAAC,IAAI,IAAI,KAAK;KACzB,CAAC;IAEF,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,OAAO,CAAC,CAAC;QACxC,MAAM,YAAY,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAEpC,gDAAgD;QAChD,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC9B,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;YAClB,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,kBAAkB,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,EAAE,CAAC,CAAC;QAChG,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,EAAE,CAAC,CAAC,CAAC;QACrG,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC","sourcesContent":["import type { ArgumentsCamelCase } from 'yargs';\nimport { runDoctor, outputReport } from '../doctor/index.js';\nimport clack from '../utils/clack.js';\n\ninterface DoctorArgs {\n verbose?: boolean;\n skipApi?: boolean;\n installDir?: string;\n json?: boolean;\n copy?: boolean;\n}\n\nexport async function handleDoctor(argv: ArgumentsCamelCase<DoctorArgs>): Promise<void> {\n const options = {\n installDir: argv.installDir ?? process.cwd(),\n verbose: argv.verbose ?? false,\n skipApi: argv.skipApi ?? false,\n json: argv.json ?? false,\n copy: argv.copy ?? false,\n };\n\n try {\n const report = await runDoctor(options);\n await outputReport(report, options);\n\n // Exit with error code if critical issues found\n if (report.summary.errors > 0) {\n process.exit(1);\n }\n process.exit(0);\n } catch (error) {\n if (!options.json) {\n clack.log.error(`Doctor failed: ${error instanceof Error ? error.message : 'Unknown error'}`);\n } else {\n console.error(JSON.stringify({ error: error instanceof Error ? error.message : 'Unknown error' }));\n }\n process.exit(1);\n }\n}\n"]}
@@ -1,7 +1,8 @@
1
1
  import open from 'opn';
2
2
  import clack from '../utils/clack.js';
3
- import { saveCredentials, getCredentials, getAccessToken } from '../lib/credentials.js';
3
+ import { saveCredentials, getCredentials, getAccessToken, isTokenExpired, updateTokens } from '../lib/credentials.js';
4
4
  import { getCliAuthClientId, getAuthkitDomain } from '../lib/settings.js';
5
+ import { refreshAccessToken } from '../lib/token-refresh-client.js';
5
6
  /**
6
7
  * Parse JWT payload
7
8
  */
@@ -45,12 +46,31 @@ export async function runLogin() {
45
46
  clack.log.error('CLI auth not configured. Set WORKOS_CLI_CLIENT_ID environment variable.');
46
47
  process.exit(1);
47
48
  }
49
+ // Check if already logged in with valid token
48
50
  if (getAccessToken()) {
49
51
  const creds = getCredentials();
50
52
  clack.log.info(`Already logged in as ${creds?.email ?? 'unknown'}`);
51
53
  clack.log.info('Run `workos logout` to log out');
52
54
  return;
53
55
  }
56
+ // Try to refresh if we have expired credentials with a refresh token
57
+ const existingCreds = getCredentials();
58
+ if (existingCreds?.refreshToken && isTokenExpired(existingCreds)) {
59
+ try {
60
+ const authkitDomain = getAuthkitDomain();
61
+ const result = await refreshAccessToken(authkitDomain, clientId);
62
+ if (result.accessToken && result.expiresAt) {
63
+ updateTokens(result.accessToken, result.expiresAt, result.refreshToken);
64
+ clack.log.info(`Already logged in as ${existingCreds.email ?? 'unknown'}`);
65
+ clack.log.info('(Session refreshed)');
66
+ clack.log.info('Run `workos logout` to log out');
67
+ return;
68
+ }
69
+ }
70
+ catch {
71
+ // Refresh failed, proceed with fresh login
72
+ }
73
+ }
54
74
  clack.log.step('Starting authentication...');
55
75
  const endpoints = getConnectEndpoints();
56
76
  const authResponse = await fetch(endpoints.deviceAuthorization, {
@@ -1 +1 @@
1
- {"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"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","sourcesContent":["import open from 'opn';\nimport clack from '../utils/clack.js';\nimport { saveCredentials, getCredentials, getAccessToken } from '../lib/credentials.js';\nimport { getCliAuthClientId, getAuthkitDomain } from '../lib/settings.js';\n\n/**\n * Parse JWT payload\n */\nfunction parseJwt(token: string): Record<string, unknown> | null {\n try {\n const parts = token.split('.');\n if (parts.length !== 3) return null;\n return JSON.parse(Buffer.from(parts[1], 'base64url').toString('utf-8'));\n } catch {\n return null;\n }\n}\n\n/**\n * Extract expiry time from JWT token\n */\nfunction getJwtExpiry(token: string): number | null {\n const payload = parseJwt(token);\n if (!payload || typeof payload.exp !== 'number') return null;\n return payload.exp * 1000;\n}\n\nconst POLL_TIMEOUT_MS = 5 * 60 * 1000; // 5 minutes\n\n/**\n * Get Connect OAuth endpoints from AuthKit domain\n */\nfunction getConnectEndpoints() {\n const domain = getAuthkitDomain();\n return {\n deviceAuthorization: `${domain}/oauth2/device_authorization`,\n token: `${domain}/oauth2/token`,\n };\n}\n\ninterface DeviceAuthResponse {\n device_code: string;\n user_code: string;\n verification_uri: string;\n verification_uri_complete: string;\n expires_in: number;\n interval: number;\n}\n\ninterface ConnectTokenResponse {\n access_token: string;\n id_token: string;\n token_type: string;\n expires_in: number;\n refresh_token?: string;\n}\n\ninterface AuthErrorResponse {\n error: string;\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nexport async function runLogin(): Promise<void> {\n const clientId = getCliAuthClientId();\n\n if (!clientId) {\n clack.log.error('CLI auth not configured. Set WORKOS_CLI_CLIENT_ID environment variable.');\n process.exit(1);\n }\n\n if (getAccessToken()) {\n const creds = getCredentials();\n clack.log.info(`Already logged in as ${creds?.email ?? 'unknown'}`);\n clack.log.info('Run `workos logout` to log out');\n return;\n }\n\n clack.log.step('Starting authentication...');\n\n const endpoints = getConnectEndpoints();\n\n const authResponse = await fetch(endpoints.deviceAuthorization, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n },\n body: new URLSearchParams({\n client_id: clientId,\n scope: 'openid email staging-environment:credentials:read offline_access',\n }),\n });\n\n if (!authResponse.ok) {\n clack.log.error(`Failed to start authentication: ${authResponse.status}`);\n process.exit(1);\n }\n\n const deviceAuth = (await authResponse.json()) as DeviceAuthResponse;\n const pollIntervalMs = (deviceAuth.interval || 5) * 1000;\n\n clack.log.info(`\\nOpen this URL in your browser:\\n`);\n console.log(` ${deviceAuth.verification_uri}`);\n console.log(`\\nEnter code: ${deviceAuth.user_code}\\n`);\n\n try {\n open(deviceAuth.verification_uri_complete);\n clack.log.info('Browser opened automatically');\n } catch {\n // User can open manually\n }\n\n const spinner = clack.spinner();\n spinner.start('Waiting for authentication...');\n\n const startTime = Date.now();\n let currentInterval = pollIntervalMs;\n\n while (Date.now() - startTime < POLL_TIMEOUT_MS) {\n await sleep(currentInterval);\n\n try {\n const tokenResponse = await fetch(endpoints.token, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n },\n body: new URLSearchParams({\n grant_type: 'urn:ietf:params:oauth:grant-type:device_code',\n device_code: deviceAuth.device_code,\n client_id: clientId,\n }),\n });\n\n const data = await tokenResponse.json();\n\n if (tokenResponse.ok) {\n const result = data as ConnectTokenResponse;\n\n // Parse user info from id_token JWT\n const idTokenPayload = parseJwt(result.id_token);\n const userId = (idTokenPayload?.sub as string) || 'unknown';\n const email = (idTokenPayload?.email as string) || undefined;\n\n // Extract actual expiry from access token JWT, fallback to response or 15 min\n const jwtExpiry = getJwtExpiry(result.access_token);\n const expiresAt =\n jwtExpiry ?? (result.expires_in ? Date.now() + result.expires_in * 1000 : Date.now() + 15 * 60 * 1000);\n\n const expiresInSec = Math.round((expiresAt - Date.now()) / 1000);\n\n saveCredentials({\n accessToken: result.access_token,\n expiresAt,\n userId,\n email,\n refreshToken: result.refresh_token,\n });\n\n spinner.stop('Authentication successful!');\n clack.log.success(`Logged in as ${email || userId}`);\n clack.log.info(`Token expires in ${expiresInSec} seconds`);\n return;\n }\n\n const errorData = data as AuthErrorResponse;\n if (errorData.error === 'authorization_pending') continue;\n if (errorData.error === 'slow_down') {\n currentInterval += 5000;\n continue;\n }\n\n spinner.stop('Authentication failed');\n clack.log.error(`Authentication error: ${errorData.error}`);\n process.exit(1);\n } catch {\n continue;\n }\n }\n\n spinner.stop('Authentication timed out');\n clack.log.error('Authentication timed out. Please try again.');\n process.exit(1);\n}\n"]}
1
+ {"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"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","sourcesContent":["import open from 'opn';\nimport clack from '../utils/clack.js';\nimport { saveCredentials, getCredentials, getAccessToken, isTokenExpired, updateTokens } from '../lib/credentials.js';\nimport { getCliAuthClientId, getAuthkitDomain } from '../lib/settings.js';\nimport { refreshAccessToken } from '../lib/token-refresh-client.js';\n\n/**\n * Parse JWT payload\n */\nfunction parseJwt(token: string): Record<string, unknown> | null {\n try {\n const parts = token.split('.');\n if (parts.length !== 3) return null;\n return JSON.parse(Buffer.from(parts[1], 'base64url').toString('utf-8'));\n } catch {\n return null;\n }\n}\n\n/**\n * Extract expiry time from JWT token\n */\nfunction getJwtExpiry(token: string): number | null {\n const payload = parseJwt(token);\n if (!payload || typeof payload.exp !== 'number') return null;\n return payload.exp * 1000;\n}\n\nconst POLL_TIMEOUT_MS = 5 * 60 * 1000; // 5 minutes\n\n/**\n * Get Connect OAuth endpoints from AuthKit domain\n */\nfunction getConnectEndpoints() {\n const domain = getAuthkitDomain();\n return {\n deviceAuthorization: `${domain}/oauth2/device_authorization`,\n token: `${domain}/oauth2/token`,\n };\n}\n\ninterface DeviceAuthResponse {\n device_code: string;\n user_code: string;\n verification_uri: string;\n verification_uri_complete: string;\n expires_in: number;\n interval: number;\n}\n\ninterface ConnectTokenResponse {\n access_token: string;\n id_token: string;\n token_type: string;\n expires_in: number;\n refresh_token?: string;\n}\n\ninterface AuthErrorResponse {\n error: string;\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nexport async function runLogin(): Promise<void> {\n const clientId = getCliAuthClientId();\n\n if (!clientId) {\n clack.log.error('CLI auth not configured. Set WORKOS_CLI_CLIENT_ID environment variable.');\n process.exit(1);\n }\n\n // Check if already logged in with valid token\n if (getAccessToken()) {\n const creds = getCredentials();\n clack.log.info(`Already logged in as ${creds?.email ?? 'unknown'}`);\n clack.log.info('Run `workos logout` to log out');\n return;\n }\n\n // Try to refresh if we have expired credentials with a refresh token\n const existingCreds = getCredentials();\n if (existingCreds?.refreshToken && isTokenExpired(existingCreds)) {\n try {\n const authkitDomain = getAuthkitDomain();\n const result = await refreshAccessToken(authkitDomain, clientId);\n if (result.accessToken && result.expiresAt) {\n updateTokens(result.accessToken, result.expiresAt, result.refreshToken);\n clack.log.info(`Already logged in as ${existingCreds.email ?? 'unknown'}`);\n clack.log.info('(Session refreshed)');\n clack.log.info('Run `workos logout` to log out');\n return;\n }\n } catch {\n // Refresh failed, proceed with fresh login\n }\n }\n\n clack.log.step('Starting authentication...');\n\n const endpoints = getConnectEndpoints();\n\n const authResponse = await fetch(endpoints.deviceAuthorization, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n },\n body: new URLSearchParams({\n client_id: clientId,\n scope: 'openid email staging-environment:credentials:read offline_access',\n }),\n });\n\n if (!authResponse.ok) {\n clack.log.error(`Failed to start authentication: ${authResponse.status}`);\n process.exit(1);\n }\n\n const deviceAuth = (await authResponse.json()) as DeviceAuthResponse;\n const pollIntervalMs = (deviceAuth.interval || 5) * 1000;\n\n clack.log.info(`\\nOpen this URL in your browser:\\n`);\n console.log(` ${deviceAuth.verification_uri}`);\n console.log(`\\nEnter code: ${deviceAuth.user_code}\\n`);\n\n try {\n open(deviceAuth.verification_uri_complete);\n clack.log.info('Browser opened automatically');\n } catch {\n // User can open manually\n }\n\n const spinner = clack.spinner();\n spinner.start('Waiting for authentication...');\n\n const startTime = Date.now();\n let currentInterval = pollIntervalMs;\n\n while (Date.now() - startTime < POLL_TIMEOUT_MS) {\n await sleep(currentInterval);\n\n try {\n const tokenResponse = await fetch(endpoints.token, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n },\n body: new URLSearchParams({\n grant_type: 'urn:ietf:params:oauth:grant-type:device_code',\n device_code: deviceAuth.device_code,\n client_id: clientId,\n }),\n });\n\n const data = await tokenResponse.json();\n\n if (tokenResponse.ok) {\n const result = data as ConnectTokenResponse;\n\n // Parse user info from id_token JWT\n const idTokenPayload = parseJwt(result.id_token);\n const userId = (idTokenPayload?.sub as string) || 'unknown';\n const email = (idTokenPayload?.email as string) || undefined;\n\n // Extract actual expiry from access token JWT, fallback to response or 15 min\n const jwtExpiry = getJwtExpiry(result.access_token);\n const expiresAt =\n jwtExpiry ?? (result.expires_in ? Date.now() + result.expires_in * 1000 : Date.now() + 15 * 60 * 1000);\n\n const expiresInSec = Math.round((expiresAt - Date.now()) / 1000);\n\n saveCredentials({\n accessToken: result.access_token,\n expiresAt,\n userId,\n email,\n refreshToken: result.refresh_token,\n });\n\n spinner.stop('Authentication successful!');\n clack.log.success(`Logged in as ${email || userId}`);\n clack.log.info(`Token expires in ${expiresInSec} seconds`);\n return;\n }\n\n const errorData = data as AuthErrorResponse;\n if (errorData.error === 'authorization_pending') continue;\n if (errorData.error === 'slow_down') {\n currentInterval += 5000;\n continue;\n }\n\n spinner.stop('Authentication failed');\n clack.log.error(`Authentication error: ${errorData.error}`);\n process.exit(1);\n } catch {\n continue;\n }\n }\n\n spinner.stop('Authentication timed out');\n clack.log.error('Authentication timed out. Please try again.');\n process.exit(1);\n}\n"]}
@@ -0,0 +1,2 @@
1
+ import type { DoctorOptions, ConnectivityInfo } from '../types.js';
2
+ export declare function checkConnectivity(options: DoctorOptions, baseUrl: string): Promise<ConnectivityInfo>;
@@ -0,0 +1,35 @@
1
+ export async function checkConnectivity(options, baseUrl) {
2
+ if (options.skipApi) {
3
+ return {
4
+ apiReachable: false,
5
+ latencyMs: null,
6
+ tlsValid: false,
7
+ error: 'Skipped (--skip-api)',
8
+ };
9
+ }
10
+ const startTime = Date.now();
11
+ try {
12
+ const controller = new AbortController();
13
+ const timeoutId = setTimeout(() => controller.abort(), 10000);
14
+ const response = await fetch(`${baseUrl}/health`, {
15
+ signal: controller.signal,
16
+ });
17
+ clearTimeout(timeoutId);
18
+ const latencyMs = Date.now() - startTime;
19
+ return {
20
+ apiReachable: response.ok,
21
+ latencyMs,
22
+ tlsValid: true, // If fetch succeeded over HTTPS, TLS is valid
23
+ error: response.ok ? undefined : `HTTP ${response.status}`,
24
+ };
25
+ }
26
+ catch (error) {
27
+ return {
28
+ apiReachable: false,
29
+ latencyMs: null,
30
+ tlsValid: false,
31
+ error: error instanceof Error ? error.message : 'Unknown error',
32
+ };
33
+ }
34
+ }
35
+ //# sourceMappingURL=connectivity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connectivity.js","sourceRoot":"","sources":["../../../src/doctor/checks/connectivity.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,OAAsB,EAAE,OAAe;IAC7E,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;QACpB,OAAO;YACL,YAAY,EAAE,KAAK;YACnB,SAAS,EAAE,IAAI;YACf,QAAQ,EAAE,KAAK;YACf,KAAK,EAAE,sBAAsB;SAC9B,CAAC;IACJ,CAAC;IACD,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAE7B,IAAI,CAAC;QACH,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;QACzC,MAAM,SAAS,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,KAAK,CAAC,CAAC;QAE9D,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,OAAO,SAAS,EAAE;YAChD,MAAM,EAAE,UAAU,CAAC,MAAM;SAC1B,CAAC,CAAC;QAEH,YAAY,CAAC,SAAS,CAAC,CAAC;QACxB,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC;QAEzC,OAAO;YACL,YAAY,EAAE,QAAQ,CAAC,EAAE;YACzB,SAAS;YACT,QAAQ,EAAE,IAAI,EAAE,8CAA8C;YAC9D,KAAK,EAAE,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,QAAQ,CAAC,MAAM,EAAE;SAC3D,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO;YACL,YAAY,EAAE,KAAK;YACnB,SAAS,EAAE,IAAI;YACf,QAAQ,EAAE,KAAK;YACf,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe;SAChE,CAAC;IACJ,CAAC;AACH,CAAC","sourcesContent":["import type { DoctorOptions, ConnectivityInfo } from '../types.js';\n\nexport async function checkConnectivity(options: DoctorOptions, baseUrl: string): Promise<ConnectivityInfo> {\n if (options.skipApi) {\n return {\n apiReachable: false,\n latencyMs: null,\n tlsValid: false,\n error: 'Skipped (--skip-api)',\n };\n }\n const startTime = Date.now();\n\n try {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), 10000);\n\n const response = await fetch(`${baseUrl}/health`, {\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n const latencyMs = Date.now() - startTime;\n\n return {\n apiReachable: response.ok,\n latencyMs,\n tlsValid: true, // If fetch succeeded over HTTPS, TLS is valid\n error: response.ok ? undefined : `HTTP ${response.status}`,\n };\n } catch (error) {\n return {\n apiReachable: false,\n latencyMs: null,\n tlsValid: false,\n error: error instanceof Error ? error.message : 'Unknown error',\n };\n }\n}\n"]}
@@ -0,0 +1,3 @@
1
+ import type { DashboardFetchResult, DoctorOptions, RedirectUriComparison, EnvironmentRaw } from '../types.js';
2
+ export declare function checkDashboardSettings(options: DoctorOptions, apiKeyType: 'staging' | 'production' | null, raw: EnvironmentRaw): Promise<DashboardFetchResult>;
3
+ export declare function compareRedirectUris(codeUri: string | null, dashboardUris: string[], source?: 'env' | 'inferred'): RedirectUriComparison;
@@ -0,0 +1,123 @@
1
+ const WORKOS_API_URL = 'https://api.workos.com';
2
+ export async function checkDashboardSettings(options, apiKeyType, raw) {
3
+ // Never call API with production keys
4
+ if (apiKeyType === 'production') {
5
+ return { settings: null, error: 'Skipped (production API key)' };
6
+ }
7
+ if (options.skipApi) {
8
+ return { settings: null, error: 'Skipped (--skip-api)' };
9
+ }
10
+ const apiKey = raw.apiKey;
11
+ if (!apiKey) {
12
+ return { settings: null, error: 'No API key configured' };
13
+ }
14
+ try {
15
+ return await fetchDashboardSettings(apiKey, raw.baseUrl);
16
+ }
17
+ catch (err) {
18
+ const message = err instanceof Error ? err.message : 'Unknown error';
19
+ return { settings: null, error: message };
20
+ }
21
+ }
22
+ async function fetchDashboardSettings(apiKey, baseUrlOverride) {
23
+ const baseUrl = baseUrlOverride ?? WORKOS_API_URL;
24
+ const controller = new AbortController();
25
+ const timeoutId = setTimeout(() => controller.abort(), 10000);
26
+ try {
27
+ const redirectUris = [];
28
+ // Single /organizations?limit=1 call — validates credentials AND gets org count
29
+ const orgsResponse = await fetch(`${baseUrl}/organizations?limit=1`, {
30
+ headers: { Authorization: `Bearer ${apiKey}` },
31
+ signal: controller.signal,
32
+ });
33
+ if (orgsResponse.status === 401) {
34
+ return {
35
+ settings: null,
36
+ credentialValidation: { valid: false, clientIdMatch: true, error: 'Invalid API key' },
37
+ error: 'Invalid API key (401)',
38
+ };
39
+ }
40
+ if (orgsResponse.status === 403) {
41
+ return {
42
+ settings: null,
43
+ credentialValidation: { valid: false, clientIdMatch: true, error: 'API key lacks permissions' },
44
+ error: 'API key lacks permissions (403)',
45
+ };
46
+ }
47
+ if (!orgsResponse.ok) {
48
+ return {
49
+ settings: null,
50
+ credentialValidation: { valid: false, clientIdMatch: true, error: `API error: ${orgsResponse.status}` },
51
+ error: `API error: ${orgsResponse.status}`,
52
+ };
53
+ }
54
+ // Credentials valid — extract org count from the same response
55
+ const credentialValidation = { valid: true, clientIdMatch: true };
56
+ let organizationCount = 0;
57
+ const orgsData = (await orgsResponse.json());
58
+ organizationCount = orgsData.list_metadata?.total_count ?? orgsData.data?.length ?? 0;
59
+ // Fetch environment settings
60
+ const envResponse = await fetch(`${baseUrl}/environments/current`, {
61
+ headers: { Authorization: `Bearer ${apiKey}` },
62
+ signal: controller.signal,
63
+ });
64
+ let authMethods = [];
65
+ let sessionTimeout = null;
66
+ let mfa = null;
67
+ if (envResponse.ok) {
68
+ const envData = (await envResponse.json());
69
+ authMethods = envData.auth_methods ?? [];
70
+ sessionTimeout = envData.session_timeout ?? null;
71
+ mfa = envData.mfa_policy ?? null;
72
+ }
73
+ return {
74
+ settings: { redirectUris, authMethods, sessionTimeout, mfa, organizationCount },
75
+ credentialValidation,
76
+ };
77
+ }
78
+ catch (err) {
79
+ if (err instanceof Error && err.name === 'AbortError') {
80
+ throw new Error('Request timeout (10s)');
81
+ }
82
+ throw err;
83
+ }
84
+ finally {
85
+ clearTimeout(timeoutId);
86
+ }
87
+ }
88
+ /**
89
+ * Normalize a URI for comparison:
90
+ * - Remove trailing slashes
91
+ * - Normalize localhost variants (127.0.0.1 → localhost)
92
+ * - Lowercase the host portion
93
+ */
94
+ function normalizeUri(uri) {
95
+ try {
96
+ const url = new URL(uri);
97
+ // Normalize localhost variants
98
+ if (url.hostname === '127.0.0.1' || url.hostname === '[::1]') {
99
+ url.hostname = 'localhost';
100
+ }
101
+ // Lowercase hostname (but preserve path case for compatibility)
102
+ url.hostname = url.hostname.toLowerCase();
103
+ // Remove trailing slash from pathname (unless it's just "/")
104
+ if (url.pathname.length > 1 && url.pathname.endsWith('/')) {
105
+ url.pathname = url.pathname.slice(0, -1);
106
+ }
107
+ return url.toString();
108
+ }
109
+ catch {
110
+ // If URL parsing fails, return as-is for exact match fallback
111
+ return uri;
112
+ }
113
+ }
114
+ export function compareRedirectUris(codeUri, dashboardUris, source) {
115
+ if (!codeUri) {
116
+ return { codeUri, dashboardUris, match: false, source };
117
+ }
118
+ const normalizedCode = normalizeUri(codeUri);
119
+ const normalizedDashboard = dashboardUris.map(normalizeUri);
120
+ const match = normalizedDashboard.includes(normalizedCode);
121
+ return { codeUri, dashboardUris, match, source };
122
+ }
123
+ //# sourceMappingURL=dashboard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dashboard.js","sourceRoot":"","sources":["../../../src/doctor/checks/dashboard.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n CredentialValidation,\n DashboardSettings,\n DashboardFetchResult,\n DoctorOptions,\n RedirectUriComparison,\n EnvironmentRaw,\n} from '../types.js';\n\nconst WORKOS_API_URL = 'https://api.workos.com';\n\nexport async function checkDashboardSettings(\n options: DoctorOptions,\n apiKeyType: 'staging' | 'production' | null,\n raw: EnvironmentRaw,\n): Promise<DashboardFetchResult> {\n // Never call API with production keys\n if (apiKeyType === 'production') {\n return { settings: null, error: 'Skipped (production API key)' };\n }\n\n if (options.skipApi) {\n return { settings: null, error: 'Skipped (--skip-api)' };\n }\n\n const apiKey = raw.apiKey;\n if (!apiKey) {\n return { settings: null, error: 'No API key configured' };\n }\n\n try {\n return await fetchDashboardSettings(apiKey, raw.baseUrl);\n } catch (err) {\n const message = err instanceof Error ? err.message : 'Unknown error';\n return { settings: null, error: message };\n }\n}\n\nasync function fetchDashboardSettings(apiKey: string, baseUrlOverride: string | null): Promise<DashboardFetchResult> {\n const baseUrl = baseUrlOverride ?? WORKOS_API_URL;\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), 10000);\n\n try {\n const redirectUris: string[] = [];\n\n // Single /organizations?limit=1 call — validates credentials AND gets org count\n const orgsResponse = await fetch(`${baseUrl}/organizations?limit=1`, {\n headers: { Authorization: `Bearer ${apiKey}` },\n signal: controller.signal,\n });\n\n if (orgsResponse.status === 401) {\n return {\n settings: null,\n credentialValidation: { valid: false, clientIdMatch: true, error: 'Invalid API key' },\n error: 'Invalid API key (401)',\n };\n }\n if (orgsResponse.status === 403) {\n return {\n settings: null,\n credentialValidation: { valid: false, clientIdMatch: true, error: 'API key lacks permissions' },\n error: 'API key lacks permissions (403)',\n };\n }\n if (!orgsResponse.ok) {\n return {\n settings: null,\n credentialValidation: { valid: false, clientIdMatch: true, error: `API error: ${orgsResponse.status}` },\n error: `API error: ${orgsResponse.status}`,\n };\n }\n\n // Credentials valid — extract org count from the same response\n const credentialValidation: CredentialValidation = { valid: true, clientIdMatch: true };\n\n let organizationCount = 0;\n const orgsData = (await orgsResponse.json()) as {\n list_metadata?: { total_count?: number };\n data?: unknown[];\n };\n organizationCount = orgsData.list_metadata?.total_count ?? orgsData.data?.length ?? 0;\n\n // Fetch environment settings\n const envResponse = await fetch(`${baseUrl}/environments/current`, {\n headers: { Authorization: `Bearer ${apiKey}` },\n signal: controller.signal,\n });\n\n let authMethods: string[] = [];\n let sessionTimeout: string | null = null;\n let mfa: 'optional' | 'required' | 'disabled' | null = null;\n\n if (envResponse.ok) {\n const envData = (await envResponse.json()) as {\n auth_methods?: string[];\n session_timeout?: string;\n mfa_policy?: 'optional' | 'required' | 'disabled';\n };\n authMethods = envData.auth_methods ?? [];\n sessionTimeout = envData.session_timeout ?? null;\n mfa = envData.mfa_policy ?? null;\n }\n\n return {\n settings: { redirectUris, authMethods, sessionTimeout, mfa, organizationCount },\n credentialValidation,\n };\n } catch (err) {\n if (err instanceof Error && err.name === 'AbortError') {\n throw new Error('Request timeout (10s)');\n }\n throw err;\n } finally {\n clearTimeout(timeoutId);\n }\n}\n\n/**\n * Normalize a URI for comparison:\n * - Remove trailing slashes\n * - Normalize localhost variants (127.0.0.1 → localhost)\n * - Lowercase the host portion\n */\nfunction normalizeUri(uri: string): string {\n try {\n const url = new URL(uri);\n // Normalize localhost variants\n if (url.hostname === '127.0.0.1' || url.hostname === '[::1]') {\n url.hostname = 'localhost';\n }\n // Lowercase hostname (but preserve path case for compatibility)\n url.hostname = url.hostname.toLowerCase();\n // Remove trailing slash from pathname (unless it's just \"/\")\n if (url.pathname.length > 1 && url.pathname.endsWith('/')) {\n url.pathname = url.pathname.slice(0, -1);\n }\n return url.toString();\n } catch {\n // If URL parsing fails, return as-is for exact match fallback\n return uri;\n }\n}\n\nexport function compareRedirectUris(\n codeUri: string | null,\n dashboardUris: string[],\n source?: 'env' | 'inferred',\n): RedirectUriComparison {\n if (!codeUri) {\n return { codeUri, dashboardUris, match: false, source };\n }\n\n const normalizedCode = normalizeUri(codeUri);\n const normalizedDashboard = dashboardUris.map(normalizeUri);\n const match = normalizedDashboard.includes(normalizedCode);\n\n return { codeUri, dashboardUris, match, source };\n}\n"]}
@@ -0,0 +1,2 @@
1
+ import type { EnvironmentCheckResult, DoctorOptions } from '../types.js';
2
+ export declare function checkEnvironment(options?: DoctorOptions): EnvironmentCheckResult;
@@ -0,0 +1,68 @@
1
+ import { readFileSync, existsSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { parse as parseDotenv } from 'dotenv';
4
+ /**
5
+ * Load environment variables from project's .env files.
6
+ * Priority: .env.local > .env (matching Next.js/Vite conventions)
7
+ * Uses dotenv parser for proper handling of quotes, multiline, exports, etc.
8
+ */
9
+ function loadProjectEnv(installDir) {
10
+ const env = {};
11
+ // Load in order: .env first, then .env.local (later overrides earlier)
12
+ const envFiles = ['.env', '.env.local'];
13
+ for (const file of envFiles) {
14
+ const filePath = join(installDir, file);
15
+ if (existsSync(filePath)) {
16
+ try {
17
+ const content = readFileSync(filePath, 'utf-8');
18
+ const parsed = parseDotenv(content);
19
+ Object.assign(env, parsed);
20
+ }
21
+ catch {
22
+ // Ignore read errors
23
+ }
24
+ }
25
+ }
26
+ return env;
27
+ }
28
+ export function checkEnvironment(options) {
29
+ // Load project env files, then fall back to process.env
30
+ const projectEnv = options?.installDir ? loadProjectEnv(options.installDir) : {};
31
+ const apiKey = projectEnv.WORKOS_API_KEY ?? process.env.WORKOS_API_KEY ?? null;
32
+ const clientId = projectEnv.WORKOS_CLIENT_ID ?? process.env.WORKOS_CLIENT_ID ?? null;
33
+ const redirectUri = projectEnv.WORKOS_REDIRECT_URI ?? process.env.WORKOS_REDIRECT_URI ?? null;
34
+ const cookieDomain = projectEnv.WORKOS_COOKIE_DOMAIN ?? process.env.WORKOS_COOKIE_DOMAIN ?? null;
35
+ const baseUrl = projectEnv.WORKOS_BASE_URL ?? process.env.WORKOS_BASE_URL ?? null;
36
+ return {
37
+ info: {
38
+ apiKeyConfigured: !!apiKey,
39
+ apiKeyType: getApiKeyType(apiKey),
40
+ clientId: truncateClientId(clientId),
41
+ redirectUri: redirectUri,
42
+ cookieDomain: cookieDomain,
43
+ baseUrl: baseUrl ?? 'https://api.workos.com',
44
+ },
45
+ raw: {
46
+ apiKey,
47
+ clientId,
48
+ baseUrl: baseUrl ?? 'https://api.workos.com',
49
+ },
50
+ };
51
+ }
52
+ function getApiKeyType(apiKey) {
53
+ if (!apiKey)
54
+ return null;
55
+ if (apiKey.startsWith('sk_test_'))
56
+ return 'staging';
57
+ if (apiKey.startsWith('sk_live_'))
58
+ return 'production';
59
+ return null; // Unknown format
60
+ }
61
+ function truncateClientId(clientId) {
62
+ if (!clientId)
63
+ return null;
64
+ if (clientId.length <= 15)
65
+ return clientId;
66
+ return `${clientId.slice(0, 10)}...${clientId.slice(-3)}`;
67
+ }
68
+ //# sourceMappingURL=environment.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"environment.js","sourceRoot":"","sources":["../../../src/doctor/checks/environment.ts"],"names":[],"mappings":"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","sourcesContent":["import { readFileSync, existsSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { parse as parseDotenv } from 'dotenv';\nimport type { EnvironmentInfo, EnvironmentCheckResult, DoctorOptions } from '../types.js';\n\n/**\n * Load environment variables from project's .env files.\n * Priority: .env.local > .env (matching Next.js/Vite conventions)\n * Uses dotenv parser for proper handling of quotes, multiline, exports, etc.\n */\nfunction loadProjectEnv(installDir: string): Record<string, string> {\n const env: Record<string, string> = {};\n\n // Load in order: .env first, then .env.local (later overrides earlier)\n const envFiles = ['.env', '.env.local'];\n\n for (const file of envFiles) {\n const filePath = join(installDir, file);\n if (existsSync(filePath)) {\n try {\n const content = readFileSync(filePath, 'utf-8');\n const parsed = parseDotenv(content);\n Object.assign(env, parsed);\n } catch {\n // Ignore read errors\n }\n }\n }\n\n return env;\n}\n\nexport function checkEnvironment(options?: DoctorOptions): EnvironmentCheckResult {\n // Load project env files, then fall back to process.env\n const projectEnv = options?.installDir ? loadProjectEnv(options.installDir) : {};\n\n const apiKey = projectEnv.WORKOS_API_KEY ?? process.env.WORKOS_API_KEY ?? null;\n const clientId = projectEnv.WORKOS_CLIENT_ID ?? process.env.WORKOS_CLIENT_ID ?? null;\n const redirectUri = projectEnv.WORKOS_REDIRECT_URI ?? process.env.WORKOS_REDIRECT_URI ?? null;\n const cookieDomain = projectEnv.WORKOS_COOKIE_DOMAIN ?? process.env.WORKOS_COOKIE_DOMAIN ?? null;\n const baseUrl = projectEnv.WORKOS_BASE_URL ?? process.env.WORKOS_BASE_URL ?? null;\n\n return {\n info: {\n apiKeyConfigured: !!apiKey,\n apiKeyType: getApiKeyType(apiKey),\n clientId: truncateClientId(clientId),\n redirectUri: redirectUri,\n cookieDomain: cookieDomain,\n baseUrl: baseUrl ?? 'https://api.workos.com',\n },\n raw: {\n apiKey,\n clientId,\n baseUrl: baseUrl ?? 'https://api.workos.com',\n },\n };\n}\n\nfunction getApiKeyType(apiKey: string | undefined): 'staging' | 'production' | null {\n if (!apiKey) return null;\n if (apiKey.startsWith('sk_test_')) return 'staging';\n if (apiKey.startsWith('sk_live_')) return 'production';\n return null; // Unknown format\n}\n\nfunction truncateClientId(clientId: string | undefined): string | null {\n if (!clientId) return null;\n if (clientId.length <= 15) return clientId;\n return `${clientId.slice(0, 10)}...${clientId.slice(-3)}`;\n}\n"]}