Open
Conversation
Add foundational layer for non-interactive CLI usage following gh CLI patterns. Agents and CI pipelines can now use the CLI without TTY. - Add output mode system (src/utils/output.ts) with JSON/human modes, auto-detecting non-TTY and supporting --json global flag - Add standardized exit codes (0=success, 1=error, 2=cancelled, 4=auth) - Add WORKOS_NO_PROMPT and WORKOS_FORCE_TTY env var support - Guard ensure-auth.ts to exit code 4 instead of opening browser in non-TTY mode (silent token refresh still works) - Update handleApiError in org/user commands to use structured errors - Add ideation specs for remaining phases (management commands, help, headless installer)
… installer
Phase 2 — Management commands JSON output:
- env/org/user commands produce structured JSON in non-TTY mode
- Non-interactive guards for env add (requires args) and env switch
(requires name)
- Empty list states return valid JSON ({ data: [] })
Phase 3 — Agent-discoverable help:
- --help --json outputs machine-readable command tree with types,
defaults, and positional schemas
- Subcommand scoping (e.g. workos env --help --json)
- Improved all command descriptions for agent readability
Phase 4 — Headless installer with NDJSON streaming:
- HeadlessAdapter auto-resolves all interactive decisions with
sensible defaults
- Streams progress as NDJSON events to stdout
- Flag overrides: --no-branch, --no-commit, --create-pr, --no-git-check
- --api-key and --client-id now visible flags
- Removed non-TTY error block in install command
When the refresh token is expired or invalidated (e.g., after rotation), the old credentials remained in the keyring. This caused a loop where every CLI invocation found dead creds, tried to refresh, failed, and opened the browser for login. Now clearCredentials() is called before triggering login on all failure paths: invalid_grant, network/server errors, no refresh token, and invalid credentials file. The next run sees "no credentials" and prompts a clean login instead of retrying with stale tokens.
- env list --json no longer leaks full API keys; outputs hasApiKey and hasClientId booleans instead - WORKOS_FORCE_TTY now requires explicit '1' or 'true' value, matching WORKOS_NO_PROMPT behavior (setting WORKOS_FORCE_TTY=false no longer incorrectly forces TTY mode)
- Change outputSuccess data param from Record<string, unknown> to object, eliminating as unknown as Record<string, unknown> double casts in org/user commands - Remove redundant section divider comment in headless-adapter
- Extract duplicated handleApiError from organization.ts and user.ts into shared createApiErrorHandler() factory in lib/api-error-handler.ts - Cache hideBin(process.argv) result to avoid double-parsing in bin.ts - Simplify flag inversion mapping in run.ts (remove verbose ternaries) - Lazy-import HeadlessAdapter in run-with-core.ts to avoid loading it in interactive mode
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The WorkOS CLI currently assumes a human at a terminal — 26+ interactive prompts (via clack), browser-based OAuth, and chalk-formatted tables make it unusable by coding agents like Claude Code, Codex, or Cursor. Agents must either avoid the CLI entirely or rely on users to copy-paste credentials and command output, defeating the purpose of automation.
This PR adds comprehensive non-TTY support following the
ghCLI model: automatic non-TTY detection, structured JSON output, standardized exit codes, and a headless installer adapter. Zero breaking changes for humans — all existing interactive behavior is preserved.What changed
Core infrastructure
src/utils/output.ts) —OutputModeresolved once at startup. Auto-detects non-TTY → JSON. Global--jsonflag for explicit control. Shared helpers:outputJson(),outputSuccess(),outputError(),outputTable(),exitWithError()src/utils/exit-codes.ts) — Standardized following gh convention: 0=success, 1=general error, 2=cancelled, 4=auth requiredWORKOS_NO_PROMPT=1forces non-interactive mode (suppresses all prompts + switches to JSON).WORKOS_FORCE_TTY=1forces interactive mode even when pipedManagement commands (env, organization, user)
--jsonflagenv add(requires name + apiKey args) andenv switch(requires name arg){ "data": [...] }with proper empty statesenv listJSON output (onlyhasApiKeyboolean exposed)orgalias fororganizationcommandAgent-discoverable help
--help --jsonoutputs machine-readable command tree (commands, options, positionals, types, defaults)workos env --help --jsonreturns only the env subtreeHeadless installer
src/lib/adapters/headless-adapter.ts) — third adapter alongside CLI and Dashboard. Subscribes to same state machine events, auto-resolves all interactive decisions with sensible defaultssrc/utils/ndjson.ts) — progress events streamed as newline-delimited JSON to stdout (detection, auth, git status, agent progress, validation, completion)--no-branch,--no-commit,--create-pr,--no-git-check--api-keyand--client-idnow visible flags (were hidden)Auth improvements
ensureAuthenticated()exits code 4 instead of opening browser when no TTYWORKOS_API_KEYenv var respected for management commands (bypasses OAuth)resolveApiKey()uses structured error output instead of throwing raw errorsclearCredentials()called on all refresh failure paths (invalid_grant, network errors, no refresh token). Prevents the loop where expired tokens cause repeated browser opensFlag naming fix
--no-validateoption tovalidate(default: true) so yargs--no-validatenegation works correctly with strict mode. Same for--no-branch→branch,--no-commit→commit,--no-git-check→git-check. The--no-*flags still work as before from user perspective.Environment variables
WORKOS_NO_PROMPT=1WORKOS_FORCE_TTY=1WORKOS_API_KEYExit codes