<h1 align="center">Gravitee AM CLI</h1>

<p align="center">
  <strong>The command-line interface for Gravitee Access Management.</strong><br/>
  Manage domains, applications, users, and OIDC flows — from your terminal.
</p>

<p align="center">
  <code>am login</code> &nbsp;&rarr;&nbsp; <code>am set domain</code> &nbsp;&rarr;&nbsp; <code>am app list</code> &nbsp;&mdash;&nbsp; you're in.
</p>

---

## Why?

| Without CLI | With CLI |
|---|---|
| Open browser &rarr; navigate &rarr; click &rarr; click &rarr; click | `am app create --name myapp --type web` |
| Copy UUID from URL bar to use in scripts | `am app get myapp` (works with names) |
| Manually compare staging vs production | `am domain copy --to prod --dry-run` |
| Ask colleague for their domain config | `am domain export -f domain.json` |
| No quick way to test OIDC flows | `am test login --app myapp --username admin --password admin` |
| Debug "is AM even running?" | `am health && am doctor` |

---

## AI Agent Integration

The CLI ships with `SKILL.md` — a skill definition that lets AI coding agents use AM CLI commands on your behalf. Works with Claude Code, Cursor, Copilot, and custom agent frameworks.

### Claude Code

```bash
# Copy the skill to your global skills directory
mkdir -p ~/.claude/skills/gravitee-am
cp SKILL.md ~/.claude/skills/gravitee-am/SKILL.md
```

Then just ask naturally:

```
create a web app called "my-portal" with redirect URI on localhost:4200
lock user john@example.com
show me the grant types on my-portal
change token lifetime to 1 hour on my-portal
export the domain config to a file
test ROPC login on my-portal with user admin
what's the AM status?
```

### Cursor / Other IDEs

Add `SKILL.md` to your project context or `.cursorrules`.

### Custom Agents

Use `SKILL.md` as a tool/function description in your agent framework. The CLI outputs JSON (`-o json`) for easy parsing, and all commands are non-interactive when flags are provided.

---

## Quick Start

```bash
# Install
cd gravitee-am-cli && npm install

# Login (interactive — asks for URL, username, password)
npm run am -- login

# Or non-interactive
npm run am -- login --url http://localhost:8093 --username admin --password admin

# Pick a domain
npm run am -- set domain my-domain

# You're ready
npm run am -- app list
npm run am -- user list
```

> **Tip:** For convenience, build and link globally:
> ```bash
> npm run build && npm link
> am login  # now works directly
> ```

---

## Features at a Glance

### Smart Resource Resolution

Forget UUIDs. Use names, client IDs, usernames, or emails — the CLI figures it out.

```bash
am app get my-web-app              # by name
am app get aB3kR7xWdF             # by clientId
am app get 8f3a-...-c2e1          # by UUID — still works

am user lock admin                 # by username
am user get john@example.com       # by email
```

### Three Output Formats

```bash
am domain list                     # pretty table (default)
am domain list -o json             # JSON for scripting / jq
am domain list -o compact          # key-value pairs
```

```
# Table output
┌──────────────┬────────────┬──────────┐
│ name         │ hrid       │ enabled  │
├──────────────┼────────────┼──────────┤
│ My Domain    │ my-domain  │ true     │
│ Staging      │ staging    │ true     │
└──────────────┴────────────┴──────────┘
```

### Multi-Environment Workspaces

Manage local, staging, and production from the same terminal.

```bash
am config set-workspace local   --url http://localhost:8093
am config set-workspace staging --url https://am-staging.example.com --org myorg
am config set-workspace prod    --url https://am.example.com --org myorg

am config use-workspace staging
am login
am domain list                    # lists staging domains

am config use-workspace prod
am login
am domain list                    # lists production domains
```

### Cross-Environment Operations

```bash
# Copy a domain from staging to production (with preview)
am config use-workspace staging
am set domain my-domain
am domain copy --to prod --dry-run
am domain copy --to prod

# Export / import
am domain export -f domain.json
am config use-workspace prod
am domain import domain.json
```

### Interactive & Non-Interactive Modes

Every create/update command works both ways:

```bash
# Interactive — prompts for every field
am app create

# Non-interactive — perfect for scripts
am app create --name myapp --type web --redirect-uris http://localhost:3000/callback
```

### CI/CD Ready

Environment variables replace all interactive configuration:

```bash
export AM_URL=https://am.example.com
export AM_TOKEN=eyJhbGciOi...
export AM_DOMAIN=my-domain

# Now every command works without login or context setup
am app list -o json
am user list -o json | jq '.[] | .username'
```

### Built-in OIDC Testing

Test OAuth2/OIDC flows directly from the terminal:

```bash
# Check OIDC discovery
am test discover

# Test Resource Owner Password Credentials flow (confidential client)
am test login --app <clientId> --secret <clientSecret> --username user --password pass

# Test Resource Owner Password Credentials flow (public client)
am test login --app <clientId> --username user --password pass

# Test Client Credentials flow
am test client-credentials --app <clientId> --secret <clientSecret>

# Output includes decoded JWT, issuer validation, expiry check
# Tip: make sure your app has an IdP assigned (am app update myapp --idp <idp-id>)
```

### Diagnostics & Debugging

```bash
am status                         # current context at a glance
am health                         # is AM reachable?
am health --gateway http://localhost:8092
am doctor                         # full config & connectivity check
am logs                           # tail audit logs live
am logs --type USER_LOGIN --status FAILURE

# Debug mode — see every HTTP request
am --verbose app list
# [debug] GET http://localhost:8093/management/organizations/DEFAULT/...
# [debug] 200 http://localhost:8093/... (42ms)
```

---

## Full Command Reference

### Authentication

| Command | Description |
|---|---|
| `am login` | Interactive login (URL + credentials) |
| `am login --url <url> --username <u> --password <p>` | Non-interactive login |
| `am login --url <url> --token <token>` | Service account token login |
| `am logout` | Clear token for current workspace |
| `am logout --all` | Clear all stored tokens |
| `am whoami` | Show authenticated user info |

### Context

| Command | Description |
|---|---|
| `am set domain <idOrName>` | Set active domain (by ID, HRID, or name) |
| `am set domain --clear` | Unset current domain |
| `am get domain` | Show current domain |
| `am status` | Full context overview |

### Domains

| Command | Description |
|---|---|
| `am domain list [--all] [-q <query>]` | List domains (paginated or all) |
| `am domain get [id]` | Get domain details |
| `am domain create [--name <n>]` | Create domain (interactive or flags) |
| `am domain enable [id]` | Enable a domain |
| `am domain disable [id]` | Disable a domain |
| `am domain delete <id> [-f]` | Delete domain (with confirmation) |
| `am domain export [-f <file>]` | Export full domain config to JSON |
| `am domain import <file> [--target <id>] [--dry-run]` | Import domain from JSON |
| `am domain copy --to <workspace> [--dry-run]` | Copy domain across workspaces |

### Applications

| Command | Description |
|---|---|
| `am app list [--all] [-q <query>]` | List applications |
| `am app get <idOrName>` | Get app by ID, clientId, or name |
| `am app create [--name <n> --type <t>]` | Create app (interactive or flags) |
| `am app update <idOrName> [--name] [--enabled] [--idp <ids>]` | Update application or assign IdPs |
| `am app delete <idOrName> [-f]` | Delete application |
| `am app settings <idOrName>` | View OAuth2/OIDC settings |
| `am app settings <idOrName> --grant-types <types>` | Update grant types |
| `am app settings <idOrName> --token-lifetime <sec>` | Set token lifetimes |

### Users

| Command | Description |
|---|---|
| `am user list [--all] [-q <query>] [--filter <scim>]` | List users |
| `am user get <idOrName>` | Get user by ID, username, or email |
| `am user create [--username <u>]` | Create user |
| `am user update <idOrName> [--email] [--enabled]` | Update user |
| `am user lock <idOrName>` | Lock user account |
| `am user unlock <idOrName>` | Unlock user account |
| `am user reset-password <idOrName>` | Reset password |
| `am user delete <idOrName> [-f]` | Delete user |

### Roles & Scopes

| Command | Description |
|---|---|
| `am role list [--all]` | List roles |
| `am role get <id>` | Get role details |
| `am role create [--name <n> --type <t>]` | Create role (DOMAIN or APPLICATION) |
| `am scope list [--all]` | List OAuth2 scopes |
| `am scope get <id>` | Get scope details |
| `am scope create [--key <k> --name <n>]` | Create scope |

### Identity Providers, Certificates, Factors, Groups, Flows

| Command | Description |
|---|---|
| `am idp list` / `am idp get <id>` | Identity providers |
| `am certificate list` / `get` / `delete` | Certificates |
| `am factor list` / `am factor get <id>` | MFA factors |
| `am group list` / `get` / `create` / `delete` | Groups |
| `am flow list` / `am flow get <id>` | Authentication flows |

### Audit Logs

| Command | Description |
|---|---|
| `am audit list [--type <t>] [--status <s>] [--from <date>] [--to <date>]` | List events |
| `am audit get <id>` | Get event details |
| `am logs [-f] [--type <t>] [--status <s>]` | Tail logs in real-time |

### Tokens (Service Accounts)

| Command | Description |
|---|---|
| `am token create <userId> [--name <n>]` | Create access token |
| `am token list <userId>` | List tokens |
| `am token revoke <userId> <tokenId> [-f]` | Revoke a token |

### Plugins (Schema-Driven)

| Command | Description |
|---|---|
| `am plugin list <type>` | List available plugins (idp, factor, certificate, ...) |
| `am plugin schema <type> <pluginId>` | Show configuration schema |
| `am plugin create <type> <pluginId>` | Create instance from schema (interactive) |

### OIDC Testing

| Command | Description |
|---|---|
| `am test discover` | Fetch OIDC discovery document |
| `am test login [--app <clientId>] [--username <u>]` | Test ROPC flow |
| `am test client-credentials [--app <clientId>] [--secret <s>]` | Test client_credentials |

### Configuration & Workspaces

| Command | Description |
|---|---|
| `am config set-workspace <name> --url <url>` | Create/update workspace |
| `am config use-workspace <name>` | Switch workspace |
| `am config delete-workspace <name>` | Remove workspace |
| `am config list` | List all workspaces |
| `am config current` | Show current context |
| `am config set-default-output <format>` | Set default output (table/json/compact) |
| `am config path` | Show config file location |

### Diagnostics

| Command | Description |
|---|---|
| `am status` | CLI context and session status |
| `am health [--gateway <url>]` | Check AM reachability |
| `am doctor` | Full configuration diagnosis |
| `am support-dump [-f <file>] [--all-domains]` | Generate diagnostic dump |

### Shell & Completion

| Command | Description |
|---|---|
| `am shell` | Interactive REPL (with tab completion) |
| `am completion bash\|zsh\|fish` | Generate shell completion script |

---

## Global Options

| Flag | Description |
|---|---|
| `-o, --output <format>` | Output format: `table` (default), `json`, `compact` |
| `--domain <id>` | Override current domain for this command |
| `--workspace <name>` | Override current workspace for this command |
| `--verbose` | Log HTTP requests, responses, and timing |
| `-V, --version` | Show CLI version |
| `-h, --help` | Show help |

---

## Environment Variables

For CI/CD pipelines and automation — these override stored config entirely:

| Variable | Description | Replaces |
|---|---|---|
| `AM_URL` | Management API base URL | `am config set-workspace` |
| `AM_TOKEN` | Bearer token | `am login` |
| `AM_DOMAIN` | Domain ID | `am set domain` |
| `AM_ORG` | Organisation ID (default: `DEFAULT`) | `--org` flag |
| `AM_ENV` | Environment ID (default: `DEFAULT`) | `--env` flag |

### GitHub Actions Example

```yaml
jobs:
  deploy:
    runs-on: ubuntu-latest
    env:
      AM_URL: ${{ secrets.AM_URL }}
      AM_TOKEN: ${{ secrets.AM_TOKEN }}
      AM_DOMAIN: my-domain
    steps:
      - uses: actions/checkout@v4
      - run: npm ci --prefix gravitee-am-cli
      - run: |
          cd gravitee-am-cli
          # Export current config
          npm run am -- domain export -f current.json
          # List applications as JSON
          npm run am -- app list -o json --all > apps.json
```

---

## Recipes

### Bulk export all domains

```bash
am domain list --all -o json | jq -r '.[].id' | while read id; do
  am domain export "$id" -f "export-${id}.json"
done
```

### Find all disabled applications

```bash
am app list --all -o json | jq '.[] | select(.enabled == false) | .name'
```

### Lock all users matching a pattern

```bash
am user list --all -o json | jq -r '.[] | select(.email | test("@old-domain.com$")) | .id' | while read id; do
  am user lock "$id"
done
```

### Compare application settings between environments

```bash
diff <(am --workspace staging app settings myapp -o json) \
     <(am --workspace prod app settings myapp -o json)
```

### Generate a support dump for all domains

```bash
am support-dump --all-domains -f full-dump.json
# Secrets are redacted by default. Use --no-redact for raw output (caution!).
```

### Full ROPC (password grant) flow from scratch

```bash
# 1. Create an application
am app create --name my-ropc-app --type web --redirect-uris http://localhost:3000/callback
# → Note the Client ID and Client Secret from the output

# 2. Set grant type to password
am app settings my-ropc-app --grant-types password

# 3. Assign an identity provider (required for ROPC)
am idp list                         # find the IdP ID
am app update my-ropc-app --idp <idp-id>

# 4. Create a test user
am user create --username testuser --password 'MyPassword123!'

# 5. Test the flow
am test login --app <clientId> --secret <clientSecret> --username testuser --password 'MyPassword123!'
# ✓ Token obtained successfully
# Access Token: eyJraWQiOi...
```

### Quick OIDC checks

```bash
am test discover                                            # verify OIDC discovery
am test client-credentials --app my-service --secret s3cret # machine-to-machine
```

---

## Configuration

The CLI stores configuration in `~/.gravitee-am/config.json` with secure file permissions (`0600`).

```
~/.gravitee-am/
  config.json        # workspaces, tokens, preferences
```

The config file contains:
- **Workspaces** — named environments (URL, org, env)
- **Auth tokens** — stored per workspace (with expiry tracking)
- **Defaults** — preferred output format
- **Current context** — active workspace and domain

> Tokens expire automatically. The CLI warns you 5 minutes before expiry and shows clear error messages when re-authentication is needed.

---

## Architecture (for contributors)

```
bin/am.ts                      # Entry point, command registration, help text
src/
  auth/
    auth.ts                    # Login, token storage, logout
    token-refresh.ts           # Token validity checking, expiry warnings
  commands/
    app/index.ts               # am app *
    user/index.ts              # am user *
    domain/index.ts            # am domain *
    ...                        # One file per command group
  config/
    config-manager.ts          # ~/.gravitee-am/ persistence, require* guards
  output/
    formatter.ts               # Table/JSON/compact output, resource detection
  sdk/
    client.ts                  # OpenAPI SDK client factory with caching
  utils/
    api-error.ts               # Unified API error handling
    cli-error.ts               # Typed CLI errors (replaces process.exit)
    command-context.ts          # Shared context extraction (getContext/getDomainContext)
    debug.ts                   # --verbose request/response logging
    pagination.ts              # Universal paginated fetch
    resolvers.ts               # Smart ID/name/email resolution
    retry.ts                   # Exponential backoff for transient failures
    schema-parser.ts           # Plugin schema → interactive prompts
    spinner.ts                 # TTY-aware progress spinners
    version.ts                 # Version from package.json
test/
  *.test.ts                    # Jest unit tests (266 tests, 16 suites)
```

### Key Design Decisions

- **Commander.js** for argument parsing — industry standard, excellent help generation
- **OpenAPI SDK** reused from `gravitee-am-test-sdk` — type-safe API calls, always in sync
- **Smart resolution** — commands accept human-friendly identifiers, resolved via try-ID-then-search
- **CliError over process.exit** — throwable errors enable clean shell mode and testability
- **Client caching** — API clients cached per (workspace, token) to avoid re-creation overhead
- **Retry with backoff** — transient failures (502/503/504, timeouts) retried automatically in bulk operations
- **TTY-aware output** — spinners and colours only when output is a terminal, not when piped

---

## Development

```bash
# Install dependencies
npm install

# Run in development (no build step)
npm run am -- <command>

# Run tests
npm test

# Run tests with coverage
npx jest --coverage

# Type check
npm run typecheck

# Build for distribution
npm run build
```

---

## Requirements

- Node.js >= 20.11.1
- A running Gravitee AM instance (Management API)

---

<p align="center">
  <sub>Built with <a href="https://gravitee.io">Gravitee</a> &hearts;</sub>
</p>
