Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
bfb6d30
feat: allow overriding CLI auth client ID via WORKOS_CLIENT_ID
nicknisi Jun 15, 2026
d6ec31a
feat: point the CLI at a non-prod API via WORKOS_API_URL
nicknisi Jun 22, 2026
d91c70c
feat: add `workos whoami` command
nicknisi Jun 23, 2026
14fc444
feat: vendor Management catalog snapshot + loader
nicknisi Jun 25, 2026
d372878
feat(catalog): add justification manifest, curation, and reused confi…
nicknisi Jun 25, 2026
d51e4b1
feat: add account-plane lifecycle commands (environment, project, team)
nicknisi Jun 25, 2026
e06b3e6
feat: add authkit app config commands (redirect URIs, CORS, logout UR…
nicknisi Jun 25, 2026
4ce58a3
docs(catalog): note deferred userland->user ops and retained test fix…
nicknisi Jul 10, 2026
fa7b58f
feat: command-path auth guard with silent token refresh
nicknisi Jul 22, 2026
e628838
feat: target the active environment on dashboard-plane commands
nicknisi Jul 22, 2026
bfa922f
feat!: migrate organization + user commands to the dashboard account …
nicknisi Jul 22, 2026
fd75956
feat!: migrate membership, invitation, session to the dashboard accou…
nicknisi Jul 22, 2026
f4b55e0
feat!: migrate role, permission, feature-flag to the dashboard accoun…
nicknisi Jul 23, 2026
35c10ff
feat!: migrate event + org-domain to the dashboard account plane; dir…
nicknisi Jul 23, 2026
873c7ea
feat!: migrate webhook, portal, config to the dashboard account plane
nicknisi Jul 23, 2026
4b2f7a5
chore: remove dead REST-plane code and align docs with dashboard auth
nicknisi Jul 23, 2026
ff89236
test: add live smoke script for dashboard-plane resource commands
nicknisi Jul 23, 2026
c1bcbf8
chore: formatting
nicknisi Jul 23, 2026
da3b913
chore: remove unused declarations flagged by oxlint
nicknisi Jul 23, 2026
68925e2
chore: formatting
nicknisi Jul 23, 2026
0db2ed4
fix: address review feedback on client-id override, 403 handling, and…
nicknisi Jul 23, 2026
e22b447
refactor: extract dashboard-operation executor seam and shared resour…
nicknisi Jul 23, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ jobs:
- name: Typecheck
run: pnpm typecheck

- name: Justification check
run: pnpm justification:check

- name: Build
run: pnpm build

Expand Down
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ WorkOS CLI for installing AuthKit integrations and managing WorkOS resources (or
## Non-TTY Behavior

- **Output**: Auto-switches to JSON when piped or `--json` flag. `WORKOS_FORCE_TTY=1` overrides.
- **Auth**: Exits code 4 instead of opening browser. Requires prior `workos auth login` or `WORKOS_API_KEY` env var.
- **Auth**: Exits code 4 instead of opening browser. Resource commands (organization, user, role, permission, membership, invitation, session, event, feature-flag, org-domain, portal, webhook, config) use the dashboard session from a prior `workos auth login`; expired access tokens refresh silently while the stored refresh token is valid, so only a truly dead session exits 4. `WORKOS_API_KEY` applies only to `workos api` and the still-REST commands (`connection`, `directory`, `audit-log`, `api-key`, `vault`, plus the workflow/debug commands `seed`, `setup-org`, `onboard-user`, `verify-login`, `debug-sso`, `debug-sync`, `migrations`).
- **Errors**: Structured JSON to stderr: `{ "error": { "code": "...", "message": "..." } }`
- **Exit codes**: 0=success, 1=error, 2=cancelled, 4=auth required (follows `gh` CLI convention)
- **Headless flags**: `--no-branch`, `--no-commit`, `--create-pr`, `--no-git-check`. CI mode (`WORKOS_MODE=ci`) auto-continues past a dirty tree without `--no-git-check`; agent mode requires the flag.
Expand Down
97 changes: 55 additions & 42 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ Workflows:
debug-sync Diagnose directory sync issues
```

All management commands support `--json` for structured output (auto-enabled in non-TTY) and `--api-key` to override the active environment's key.
All management commands support `--json` for structured output (auto-enabled in non-TTY). Still-REST commands (`connection`, `directory`, `audit-log`, `api-key`, `vault`, `workos api`, and the workflow/debug commands) also accept `--api-key` to override the active environment's key; the other resource commands authenticate with your `workos auth login` session (see [Resource Management](#resource-management)).

### Unclaimed Environments

Expand Down Expand Up @@ -255,7 +255,11 @@ API keys are stored in the system keychain via `@napi-rs/keyring`, with a JSON f

### Resource Management

All resource commands follow the same pattern: `workos <resource> <action> [args] [--options]`. API keys resolve via: `--api-key` flag → `WORKOS_API_KEY` env var → active environment's stored key.
All resource commands follow the same pattern: `workos <resource> <action> [args] [--options]`.

Most resource commands — organization, user, role, permission, membership, invitation, session, event, feature-flag, org-domain, portal, webhook, config — authenticate with your WorkOS dashboard session from `workos auth login` and target the active environment (see `workos env`). Pass `--environment-id <id>` on any of them to target a different environment for a single invocation. Access tokens refresh automatically while the session is valid, so a logged-in machine keeps working headlessly; a dead session exits with code 4.

The remaining commands (`connection`, `directory`, `audit-log`, `api-key`, `vault`) still use the REST plane, as does the raw escape hatch `workos api`. Those resolve an API key via: `--api-key` flag → `WORKOS_API_KEY` env var → active environment's stored key.

#### organization

Expand All @@ -264,68 +268,72 @@ workos organization create <name> [domain:state ...]
workos organization update <orgId> <name> [domain] [state]
workos organization get <orgId>
workos organization list [--domain] [--limit] [--before] [--after] [--order]
workos organization delete <orgId>
workos organization delete <orgId> [--yes]
```

#### user

```bash
workos user get <userId>
workos user list [--email] [--organization] [--limit]
workos user update <userId> [--first-name] [--last-name] [--email-verified] [--password] [--external-id]
workos user delete <userId>
workos user list [--email] [--limit] [--before] [--after] [--order]
workos user update <userId> [--first-name] [--last-name] [--email] [--locale] [--external-id]
workos user delete <userId> [--yes]
```

#### role

Role mutations change the privilege surface; non-interactive callers must pass `--yes`.

```bash
workos role list [--org <orgId>]
workos role get <slug> [--org <orgId>]
workos role create --slug <slug> --name <name> [--org <orgId>]
workos role update <slug> [--name] [--description] [--org <orgId>]
workos role delete <slug> --org <orgId>
workos role set-permissions <slug> --permissions <slugs> [--org <orgId>]
workos role add-permission <slug> <permissionSlug> [--org <orgId>]
workos role remove-permission <slug> <permissionSlug> --org <orgId>
workos role create --slug <slug> --name <name> [--description] [--org <orgId>] [--yes]
workos role update <slug> [--name] [--description] [--org <orgId>] [--yes]
workos role delete <slug> --org <orgId> [--yes]
workos role set-permissions <slug> --permissions <slugs> [--org <orgId>] [--yes]
workos role add-permission <slug> <permissionSlug> [--org <orgId>] [--yes]
workos role remove-permission <slug> <permissionSlug> --org <orgId> [--yes]
```

#### permission

```bash
workos permission list [--limit]
workos permission list
workos permission get <slug>
workos permission create --slug <slug> --name <name> [--description]
workos permission update <slug> [--name] [--description]
workos permission delete <slug>
workos permission create --slug <slug> --name <name> [--description] [--yes]
workos permission update <slug> [--name] [--description] [--yes]
workos permission delete <slug> [--yes]
```

#### membership

`--org` and `--user` are mutually exclusive on `list`; pagination flags apply to `--org` listings only.

```bash
workos membership list [--org] [--user] [--limit]
workos membership list (--org <orgId> | --user <userId>) [--limit] [--before] [--after] [--order]
workos membership get <id>
workos membership create --org <orgId> --user <userId> [--role]
workos membership update <id> [--role]
workos membership delete <id>
workos membership deactivate <id>
workos membership update <id> [--role] [--yes]
workos membership delete <id> [--yes]
workos membership deactivate <id> [--yes]
workos membership reactivate <id>
```

#### invitation

```bash
workos invitation list [--org] [--email] [--limit]
workos invitation list [--org] [--email] [--limit] [--before] [--after]
workos invitation get <id>
workos invitation send --email <email> [--org] [--role] [--expires-in-days]
workos invitation revoke <id>
workos invitation revoke <id> [--yes]
workos invitation resend <id>
```

#### session

```bash
workos session list <userId> [--limit]
workos session revoke <sessionId>
workos session list <userId> [--limit] [--before] [--after]
workos session revoke <sessionId> [--yes]
```

#### connection
Expand All @@ -349,7 +357,7 @@ workos directory list-groups --directory <id> [--limit]
#### event

```bash
workos event list --events <types> [--org] [--range-start] [--range-end] [--limit]
workos event list --events <types> [--range-start] [--range-end] [--after] [--limit]
```

#### audit-log
Expand All @@ -366,7 +374,7 @@ workos audit-log get-retention <orgId>
#### feature-flag

```bash
workos feature-flag list [--limit]
workos feature-flag list [--limit] [--before] [--after] [--order]
workos feature-flag get <slug>
workos feature-flag enable <slug>
workos feature-flag disable <slug>
Expand All @@ -379,7 +387,7 @@ workos feature-flag remove-target <slug> <targetId>
```bash
workos webhook list
workos webhook create --url <endpoint> --events <types>
workos webhook delete <id>
workos webhook delete <id> [--yes]
```

#### config
Expand All @@ -393,9 +401,11 @@ workos config homepage-url set <url>
#### portal

```bash
workos portal generate-link --intent <intent> --org <orgId> [--return-url] [--success-url]
workos portal generate-link --intent <intent> --org <orgId>
```

Supported intents: `sso`, `dsync`, `log_streams`, `domain_verification`, `certificate_renewal`. Generating a link expires prior links of the same intent.

#### vault

```bash
Expand Down Expand Up @@ -425,7 +435,7 @@ workos api-key delete <id>
workos org-domain get <id>
workos org-domain create <domain> --org <orgId>
workos org-domain verify <id>
workos org-domain delete <id>
workos org-domain delete <id> [--yes]
```

### Installer Options
Expand Down Expand Up @@ -464,11 +474,11 @@ mkdir my-app && cd my-app && npx workos@latest install
# With visual dashboard (experimental)
npx workos@latest dashboard

# JSON output (explicit)
workos org list --json --api-key sk_test_xxx
# JSON output (explicit; requires a prior `workos auth login`)
workos org list --json

# Pipe-friendly (auto-detects non-TTY)
workos org list --api-key sk_test_xxx | jq '.data[].name'
workos org list | jq '.data[].name'

# Machine-readable command discovery
workos --help --json | jq '.commands[].name'
Expand All @@ -494,7 +504,7 @@ The CLI also auto-detects non-TTY environments (piped output, CI, coding agents)
All commands produce structured JSON when piped or with `--json`:

```bash
workos org list --api-key sk_test_xxx | jq .
workos org list | jq .
# → { "data": [...], "list_metadata": { "before": null, "after": "..." } }

workos env list --json
Expand All @@ -504,8 +514,8 @@ workos env list --json
Errors go to stderr as structured JSON:

```bash
workos org list 2>&1
# → { "error": { "code": "no_api_key", "message": "No API key configured..." } }
workos org list 2>&1 # not logged in → exit code 4
# → { "error": { "code": "auth_required", "message": "Not logged in..." } }
```

### Agent Mode
Expand Down Expand Up @@ -555,13 +565,16 @@ workos install --api-key sk_test_xxx --client-id client_xxx --no-commit 2>/dev/n

### Environment Variables

| Variable | Effect |
| ------------------------ | --------------------------------------------------------- |
| `WORKOS_API_KEY` | API key for management commands (bypasses stored config) |
| `WORKOS_API_BASE_URL` | Override API base URL (set automatically by `workos dev`) |
| `WORKOS_MODE` | Interaction mode: `human`, `agent`, or `ci` |
| `WORKOS_FORCE_TTY=1` | Force human (non-JSON) **output** mode even when piped |
| `WORKOS_TELEMETRY=false` | Disable telemetry |
| Variable | Effect |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `WORKOS_API_KEY` | API key for `workos api`, the still-REST commands (connection, directory, audit-log, api-key, vault), and workflow/debug commands. Resource commands use the `workos auth login` session instead |
| `WORKOS_API_URL` | Override the API base URL for all CLI commands (e.g. a local API) |
| `WORKOS_API_BASE_URL` | Accepted alias for `WORKOS_API_URL` (what `workos dev` sets) |
| `WORKOS_MODE` | Interaction mode: `human`, `agent`, or `ci` |
| `WORKOS_FORCE_TTY=1` | Force human (non-JSON) **output** mode even when piped |
| `WORKOS_TELEMETRY=false` | Disable telemetry |

> `WORKOS_API_URL` controls where the **CLI's own** commands (`workos user`, `workos api`, etc.) send requests — set it to point the CLI at a locally-running API. This is distinct from `workos dev`, which sets API vars for your **app's** dev process so it talks to the in-process emulator.

### Command Discovery

Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,9 @@
"eval:diff": "tsx tests/evals/index.ts diff",
"eval:prune": "tsx tests/evals/index.ts prune",
"eval:logs": "tsx tests/evals/index.ts logs",
"eval:show": "tsx tests/evals/index.ts show"
"eval:show": "tsx tests/evals/index.ts show",
"catalog:vendor": "tsx scripts/vendor-catalog.ts",
"justification:check": "tsx scripts/check-justification.ts"
},
"author": "WorkOS",
"license": "MIT"
Expand Down
39 changes: 39 additions & 0 deletions scripts/check-justification.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#!/usr/bin/env tsx
/**
* CI gate for the command justification manifest.
*
* Loads the curated manifest and the vendored Management catalog, runs
* `validateManifest`, and exits non-zero if any entry is incomplete or maps to
* an operation the catalog does not contain. Wired to `pnpm justification:check`
* and run in CI so an unjustified or drifted command cannot ship.
*
* Usage:
* pnpm justification:check
*/

import { getManifest } from '../src/catalog/manifest.js';
import { loadManagementCatalog } from '../src/catalog/loader.js';
import { validateManifest } from '../src/catalog/justification.js';

function main(): void {
const manifest = getManifest();
// Validate against the full catalog (including feature-flag-gated ops): a
// manifest entry may legitimately target a flagged operation, and we only
// want to fail on genuinely-unknown `mapsTo` values, not on visibility.
const catalog = loadManagementCatalog(undefined, { includeFeatureFlagged: true });

const { ok, errors } = validateManifest(manifest, catalog);

if (ok) {
console.log(`justification:check — OK (${manifest.length} command(s) validated)`);
process.exit(0);
}

console.error(`justification:check — FAILED (${errors.length} error(s)):`);
for (const error of errors) {
console.error(` - ${error}`);
}
process.exit(1);
}

main();
Loading