workos 0.13.4 → 0.15.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 (88) hide show
  1. package/README.md +39 -3
  2. package/dist/bin.js +129 -13
  3. package/dist/bin.js.map +1 -1
  4. package/dist/commands/api/catalog.d.ts +23 -0
  5. package/dist/commands/api/catalog.js +97 -0
  6. package/dist/commands/api/catalog.js.map +1 -0
  7. package/dist/commands/api/format.d.ts +5 -0
  8. package/dist/commands/api/format.js +46 -0
  9. package/dist/commands/api/format.js.map +1 -0
  10. package/dist/commands/api/index.d.ts +15 -0
  11. package/dist/commands/api/index.js +200 -0
  12. package/dist/commands/api/index.js.map +1 -0
  13. package/dist/commands/api/interactive.d.ts +3 -0
  14. package/dist/commands/api/interactive.js +127 -0
  15. package/dist/commands/api/interactive.js.map +1 -0
  16. package/dist/commands/api/request.d.ts +14 -0
  17. package/dist/commands/api/request.js +38 -0
  18. package/dist/commands/api/request.js.map +1 -0
  19. package/dist/commands/claim.js +21 -2
  20. package/dist/commands/claim.js.map +1 -1
  21. package/dist/commands/connection.js +5 -3
  22. package/dist/commands/connection.js.map +1 -1
  23. package/dist/commands/debug.js +5 -4
  24. package/dist/commands/debug.js.map +1 -1
  25. package/dist/commands/directory.js +5 -3
  26. package/dist/commands/directory.js.map +1 -1
  27. package/dist/commands/env.js +13 -3
  28. package/dist/commands/env.js.map +1 -1
  29. package/dist/commands/login.js +20 -4
  30. package/dist/commands/login.js.map +1 -1
  31. package/dist/commands/migrations.d.ts +2 -0
  32. package/dist/commands/migrations.js +44 -0
  33. package/dist/commands/migrations.js.map +1 -0
  34. package/dist/doctor/checks/auth-patterns.js +12 -1
  35. package/dist/doctor/checks/auth-patterns.js.map +1 -1
  36. package/dist/doctor/checks/host-execution.d.ts +2 -0
  37. package/dist/doctor/checks/host-execution.js +21 -0
  38. package/dist/doctor/checks/host-execution.js.map +1 -0
  39. package/dist/doctor/index.js +9 -1
  40. package/dist/doctor/index.js.map +1 -1
  41. package/dist/doctor/issues.d.ts +5 -0
  42. package/dist/doctor/issues.js +12 -0
  43. package/dist/doctor/issues.js.map +1 -1
  44. package/dist/doctor/output.d.ts +2 -0
  45. package/dist/doctor/output.js +42 -0
  46. package/dist/doctor/output.js.map +1 -1
  47. package/dist/doctor/types.d.ts +16 -0
  48. package/dist/doctor/types.js.map +1 -1
  49. package/dist/lib/config-store.js +51 -7
  50. package/dist/lib/config-store.js.map +1 -1
  51. package/dist/lib/credential-proxy.js +14 -1
  52. package/dist/lib/credential-proxy.js.map +1 -1
  53. package/dist/lib/credential-store.js +51 -7
  54. package/dist/lib/credential-store.js.map +1 -1
  55. package/dist/lib/ensure-auth.d.ts +0 -10
  56. package/dist/lib/ensure-auth.js +27 -9
  57. package/dist/lib/ensure-auth.js.map +1 -1
  58. package/dist/lib/host-probe.d.ts +28 -0
  59. package/dist/lib/host-probe.js +154 -0
  60. package/dist/lib/host-probe.js.map +1 -0
  61. package/dist/lib/run-with-core.js +26 -7
  62. package/dist/lib/run-with-core.js.map +1 -1
  63. package/dist/utils/cli-symbols.d.ts +1 -1
  64. package/dist/utils/command-invocation.d.ts +2 -0
  65. package/dist/utils/command-invocation.js +9 -0
  66. package/dist/utils/command-invocation.js.map +1 -1
  67. package/dist/utils/debug.d.ts +1 -0
  68. package/dist/utils/debug.js +10 -2
  69. package/dist/utils/debug.js.map +1 -1
  70. package/dist/utils/environment.d.ts +6 -0
  71. package/dist/utils/environment.js +8 -16
  72. package/dist/utils/environment.js.map +1 -1
  73. package/dist/utils/exit-codes.d.ts +9 -5
  74. package/dist/utils/exit-codes.js +10 -2
  75. package/dist/utils/exit-codes.js.map +1 -1
  76. package/dist/utils/help-json.d.ts +7 -0
  77. package/dist/utils/help-json.js +120 -0
  78. package/dist/utils/help-json.js.map +1 -1
  79. package/dist/utils/interaction-mode.d.ts +25 -0
  80. package/dist/utils/interaction-mode.js +102 -0
  81. package/dist/utils/interaction-mode.js.map +1 -0
  82. package/dist/utils/output.d.ts +20 -12
  83. package/dist/utils/output.js +16 -4
  84. package/dist/utils/output.js.map +1 -1
  85. package/dist/utils/recovery-hints.d.ts +37 -0
  86. package/dist/utils/recovery-hints.js +80 -0
  87. package/dist/utils/recovery-hints.js.map +1 -0
  88. package/package.json +7 -5
package/README.md CHANGED
@@ -584,7 +584,18 @@ workos --help --json | jq '.commands[].name'
584
584
 
585
585
  ## Scripting & Automation
586
586
 
587
- The CLI auto-detects non-TTY environments (piped output, CI, coding agents) and switches to machine-friendly behavior. No flags required — just pipe it.
587
+ The CLI separates **output mode** from **interaction mode**:
588
+
589
+ - `--json` (or non-TTY auto-detection) controls **output formatting** only.
590
+ - `--mode human|agent|ci` (or `WORKOS_MODE=...`) controls **interaction behavior** — prompts, browser launch, host trust, destructive confirmation.
591
+
592
+ For coding agents, set both axes explicitly:
593
+
594
+ ```bash
595
+ WORKOS_MODE=agent workos doctor --json --skip-ai
596
+ ```
597
+
598
+ The CLI also auto-detects non-TTY environments (piped output, CI, coding agents) and falls back to machine-friendly defaults. No flags are required — just pipe it — but explicit mode is recommended for agents.
588
599
 
589
600
  ### JSON Output
590
601
 
@@ -605,6 +616,30 @@ workos org list 2>&1
605
616
  # → { "error": { "code": "no_api_key", "message": "No API key configured..." } }
606
617
  ```
607
618
 
619
+ ### Agent Mode
620
+
621
+ When a coding agent drives the CLI, set agent mode explicitly so behavior is deterministic regardless of TTY:
622
+
623
+ ```bash
624
+ WORKOS_MODE=agent workos doctor --json --skip-ai
625
+ WORKOS_MODE=agent workos install --api-key ... --client-id ...
626
+ ```
627
+
628
+ In agent mode the CLI:
629
+
630
+ - Never prompts. Missing required arguments fail with structured errors instead of opening prompts.
631
+ - Treats browser launch as best-effort. Auth flows always print the manual URL and code.
632
+ - Probes host capabilities (home directory, keychain, browser launch). Host failures emit a `HOST_EXECUTION_UNTRUSTED` issue from `workos doctor` so agents can recognize sandboxed runs.
633
+ - Requires explicit confirmation flags (e.g. `--yes`, `--force`) for destructive operations.
634
+
635
+ In `ci` mode the CLI additionally refuses browser-based auth flows and prefers terse failures over recovery handoff text.
636
+
637
+ Legacy compatibility:
638
+
639
+ - `WORKOS_NO_PROMPT=1` continues to work and is treated as agent interaction behavior plus JSON output.
640
+ - `WORKOS_FORCE_TTY=1` continues to force human **output** mode but does not change interaction mode.
641
+ - Non-TTY without an explicit mode still defaults output to JSON and interaction to agent.
642
+
608
643
  ### Headless Installer
609
644
 
610
645
  In non-TTY, the installer streams progress as NDJSON (one JSON object per line):
@@ -632,8 +667,9 @@ workos install --api-key sk_test_xxx --client-id client_xxx --no-commit 2>/dev/n
632
667
  | ------------------------ | --------------------------------------------------------- |
633
668
  | `WORKOS_API_KEY` | API key for management commands (bypasses stored config) |
634
669
  | `WORKOS_API_BASE_URL` | Override API base URL (set automatically by `workos dev`) |
635
- | `WORKOS_NO_PROMPT=1` | Force non-interactive mode + JSON output |
636
- | `WORKOS_FORCE_TTY=1` | Force interactive mode even when piped |
670
+ | `WORKOS_MODE` | Interaction mode: `human`, `agent`, or `ci` |
671
+ | `WORKOS_NO_PROMPT=1` | Legacy alias: agent interaction behavior + JSON output |
672
+ | `WORKOS_FORCE_TTY=1` | Force human (non-JSON) **output** mode even when piped |
637
673
  | `WORKOS_TELEMETRY=false` | Disable telemetry |
638
674
 
639
675
  ### Command Discovery
package/dist/bin.js CHANGED
@@ -19,20 +19,30 @@ if (!satisfies(process.version, NODE_VERSION_RANGE)) {
19
19
  red(`WorkOS AuthKit installer requires Node.js ${NODE_VERSION_RANGE}. You are using Node.js ${process.version}. Please upgrade your Node.js version.`);
20
20
  process.exit(1);
21
21
  }
22
- import { isNonInteractiveEnvironment } from './utils/environment.js';
23
- import { resolveOutputMode, setOutputMode, isJsonMode, outputJson, exitWithError } from './utils/output.js';
22
+ import { InvalidInteractionModeError, isPromptAllowed, resolveInteractionMode, setInteractionMode, } from './utils/interaction-mode.js';
23
+ import { resolveEffectiveOutputMode, resolveOutputMode, setOutputMode, isJsonMode, outputJson, exitWithError, } from './utils/output.js';
24
24
  import clack from './utils/clack.js';
25
25
  import { registerSubcommand } from './utils/register-subcommand.js';
26
26
  // Resolve output mode early from raw argv (before yargs parses)
27
27
  const rawArgs = hideBin(process.argv);
28
28
  const hasJsonFlag = rawArgs.includes('--json');
29
- setOutputMode(resolveOutputMode(hasJsonFlag));
29
+ const baseOutputMode = resolveOutputMode(hasJsonFlag);
30
+ setOutputMode(baseOutputMode);
31
+ try {
32
+ const interaction = resolveInteractionMode({ argv: rawArgs });
33
+ setInteractionMode(interaction);
34
+ setOutputMode(resolveEffectiveOutputMode(baseOutputMode, interaction));
35
+ }
36
+ catch (error) {
37
+ if (error instanceof InvalidInteractionModeError) {
38
+ exitWithError({ code: 'invalid_mode', message: error.message });
39
+ }
40
+ throw error;
41
+ }
30
42
  // Intercept --help --json before yargs parses (yargs exits on --help)
31
43
  if (hasJsonFlag && (rawArgs.includes('--help') || rawArgs.includes('-h'))) {
32
- const { buildCommandTree } = await import('./utils/help-json.js');
33
- const commandAliases = { org: 'organization' };
34
- const rawCommand = rawArgs.find((a) => !a.startsWith('-'));
35
- const command = rawCommand ? (commandAliases[rawCommand] ?? rawCommand) : undefined;
44
+ const { buildCommandTree, extractHelpJsonCommand } = await import('./utils/help-json.js');
45
+ const command = extractHelpJsonCommand(rawArgs);
36
46
  outputJson(buildCommandTree(command));
37
47
  process.exit(0);
38
48
  }
@@ -155,8 +165,8 @@ const installerOptions = {
155
165
  type: 'boolean',
156
166
  },
157
167
  };
158
- // Check for updates (blocks up to 500ms, skip in JSON mode to keep stdout clean)
159
- if (!isJsonMode())
168
+ // Check for updates (blocks up to 500ms, skip in JSON/non-human modes to keep machine streams clean)
169
+ if (!isJsonMode() && isPromptAllowed())
160
170
  await checkForUpdates();
161
171
  yargs(rawArgs)
162
172
  .parserConfiguration({ 'populate--': true })
@@ -166,13 +176,32 @@ yargs(rawArgs)
166
176
  default: false,
167
177
  describe: 'Output results as JSON (auto-enabled in non-TTY)',
168
178
  global: true,
179
+ })
180
+ .option('mode', {
181
+ type: 'string',
182
+ choices: ['human', 'agent', 'ci'],
183
+ describe: 'Interaction mode: human, coding agent, or CI automation',
184
+ global: true,
169
185
  })
170
186
  .middleware(async (argv) => {
171
187
  // Warn about unclaimed environments before management commands.
172
188
  // Excluded: auth/claim/install/dashboard handle their own credential flows;
173
189
  // skills/doctor/env/debug are utility commands where the warning is unnecessary.
174
190
  const command = String(argv._?.[0] ?? '');
175
- if (['auth', 'skills', 'doctor', 'env', 'claim', 'install', 'debug', 'dashboard', 'emulate', 'dev', ''].includes(command))
191
+ if ([
192
+ 'auth',
193
+ 'skills',
194
+ 'doctor',
195
+ 'env',
196
+ 'claim',
197
+ 'install',
198
+ 'debug',
199
+ 'dashboard',
200
+ 'emulate',
201
+ 'dev',
202
+ 'migrations',
203
+ '',
204
+ ].includes(command))
176
205
  return;
177
206
  await applyInsecureStorage(argv.insecureStorage);
178
207
  await maybeWarnUnclaimed();
@@ -312,7 +341,7 @@ yargs(rawArgs)
312
341
  await runEnvRemove(argv.name);
313
342
  });
314
343
  registerSubcommand(yargs, 'switch [name]', 'Switch active environment', (y) => y.positional('name', { type: 'string', describe: 'Environment name' }), async (argv) => {
315
- if (!argv.name && isNonInteractiveEnvironment()) {
344
+ if (!argv.name && !isPromptAllowed()) {
316
345
  exitWithError({
317
346
  code: 'missing_args',
318
347
  message: 'Environment name required. Usage: workos env switch <name>',
@@ -333,6 +362,78 @@ yargs(rawArgs)
333
362
  await runClaim();
334
363
  });
335
364
  return yargs.demandCommand(1, 'Please specify an env subcommand').strict();
365
+ })
366
+ .command('api [endpoint] [filter]', 'Make authenticated requests to the WorkOS API', (yargs) => yargs
367
+ .options(insecureStorageOption)
368
+ .positional('endpoint', {
369
+ type: 'string',
370
+ describe: "API endpoint path (e.g. /users), or 'ls' to list endpoints",
371
+ })
372
+ .positional('filter', {
373
+ type: 'string',
374
+ describe: 'Filter keyword (used with ls)',
375
+ })
376
+ .option('method', {
377
+ alias: 'X',
378
+ type: 'string',
379
+ describe: 'HTTP method (default: GET, or POST if body provided)',
380
+ })
381
+ .option('data', {
382
+ alias: 'd',
383
+ type: 'string',
384
+ describe: 'JSON request body',
385
+ })
386
+ .option('file', {
387
+ type: 'string',
388
+ describe: 'Read request body from a file (or - for stdin)',
389
+ })
390
+ .option('include', {
391
+ alias: 'i',
392
+ type: 'boolean',
393
+ default: false,
394
+ describe: 'Show response headers',
395
+ })
396
+ .option('api-key', {
397
+ type: 'string',
398
+ describe: 'Override the API key',
399
+ })
400
+ .option('dry-run', {
401
+ type: 'boolean',
402
+ default: false,
403
+ describe: 'Show the request without executing it',
404
+ })
405
+ .option('yes', {
406
+ alias: 'y',
407
+ type: 'boolean',
408
+ default: false,
409
+ describe: 'Skip confirmation for mutating requests',
410
+ })
411
+ .example('workos api ls', 'List all available endpoints')
412
+ .example('workos api ls users', 'List endpoints matching "users"')
413
+ .example('workos api /user_management/users', 'GET /user_management/users')
414
+ .example('workos api /organizations -d \'{"name":"Acme"}\'', 'POST with a JSON body')
415
+ .example('workos api /organizations/org_123 -X DELETE', 'DELETE an organization'), async (argv) => {
416
+ await applyInsecureStorage(argv.insecureStorage);
417
+ const endpoint = argv.endpoint;
418
+ const filter = argv.filter;
419
+ const { runApiLs, runApiRequest, runApiInteractive } = await import('./commands/api/index.js');
420
+ if (!endpoint) {
421
+ await runApiInteractive({ apiKey: argv.apiKey });
422
+ return;
423
+ }
424
+ if (endpoint === 'ls') {
425
+ await runApiLs(filter);
426
+ return;
427
+ }
428
+ await runApiRequest(endpoint, {
429
+ method: argv.method,
430
+ data: argv.data,
431
+ file: argv.file,
432
+ include: argv.include,
433
+ apiKey: argv.apiKey,
434
+ dryRun: argv.dryRun,
435
+ yes: argv.yes,
436
+ });
336
437
  })
337
438
  .command(['organization', 'org'], 'Manage WorkOS organizations (create, update, get, list, delete)', (yargs) => {
338
439
  yargs.options({
@@ -1317,6 +1418,21 @@ yargs(rawArgs)
1317
1418
  await runDebugToken();
1318
1419
  });
1319
1420
  return yargs.demandCommand(1, 'Run "workos debug <command>" for debug tools.').strict();
1421
+ })
1422
+ .command('migrations', 'Migrate users from identity providers (Auth0, Cognito, Clerk, Firebase) to WorkOS', (yargs) => yargs
1423
+ .strictCommands(false)
1424
+ .strict(false)
1425
+ .help(false)
1426
+ .version(false)
1427
+ .options({
1428
+ ...insecureStorageOption,
1429
+ 'api-key': { type: 'string', describe: 'WorkOS API key' },
1430
+ }), async (argv) => {
1431
+ await applyInsecureStorage(argv.insecureStorage);
1432
+ const { resolveApiKey } = await import('./lib/api-key.js');
1433
+ const { getMigrationsPassthroughArgs, runMigrations } = await import('./commands/migrations.js');
1434
+ const passthrough = getMigrationsPassthroughArgs(rawArgs);
1435
+ await runMigrations(passthrough, resolveApiKey({ apiKey: argv.apiKey }));
1320
1436
  })
1321
1437
  .command('dashboard', false, // hidden from help
1322
1438
  (yargs) => yargs.options(installerOptions), async (argv) => {
@@ -1326,8 +1442,8 @@ yargs(rawArgs)
1326
1442
  await handleInstall({ ...argv, dashboard: true });
1327
1443
  })
1328
1444
  .command(['$0'], 'WorkOS AuthKit CLI', (yargs) => yargs.options(insecureStorageOption), async (argv) => {
1329
- // Non-TTY: show help
1330
- if (isNonInteractiveEnvironment()) {
1445
+ // Non-human modes: show help instead of prompting
1446
+ if (!isPromptAllowed()) {
1331
1447
  yargs(rawArgs).showHelp();
1332
1448
  return;
1333
1449
  }