# The OpenWOP CLI

> Source: https://openwop.dev/cli/ · OpenWOP, the open-source protocol for multi-agent workflow orchestration.

`@openwop/cli` is the host-agnostic control plane for any OpenWOP-compatible host — a single `openwop` command that drives the protocol from your terminal. It is a reference *client*, not an engine: every command surfaces a capability the host advertises at `/.well-known/openwop`, sends the wire shape the host expects, and renders the host's resolved view. It bundles to a single file with **zero runtime dependencies** and ships independently of the spec corpus.

## Install

```bash
npm install -g @openwop/cli
openwop --version
```

One-off, no install (handy in CI or a scratch shell):

```bash
npx -y @openwop/cli@latest --help
```

Requires Node 22+ to install (the one-line installer ensures it; the package's `engines` floor is ≥ 20). Channel adapters for Discord and WhatsApp are optional peer dependencies — install them only if you use those channels.

## Point it at a host

The CLI reads `OPENWOP_BASE_URL` (or `--base-url`) and `OPENWOP_API_KEY` (or `--api-key`). Discovery needs no credential, so against the reference deployment these work straight away:

```bash
export OPENWOP_BASE_URL=https://app.openwop.dev/api
openwop capabilities      # read /.well-known/openwop
openwop doctor            # connectivity + advertised-capability check
```

Protocol calls — creating a run, listing runs — need an API key; without one the host answers `401`. To get a key for the reference deployment, sign in at [app.openwop.dev](https://app.openwop.dev/), open [Access → API keys](https://app.openwop.dev/access/api-keys), and create a key with the **CLI — act as me** preset. It covers the protocol surface and workspace commands such as `openwop agents list` and `openwop workflows register`. **Run workflows** is for scripts that only start and read runs: the host answers `403` when such a key reaches a workspace command. **Read only** cannot start a run. The key is shown once, with the two lines the CLI reads:

```bash
export OPENWOP_BASE_URL=https://app.openwop.dev/api
export OPENWOP_API_KEY=owk_…
```

The key is read from `--api-key`, then `OPENWOP_API_KEY`, then `openwop config set host.apiKey`. The CLI sends it in the `Authorization` header. A key acts as the account that created it, expires after 90 days by default, and can be revoked on the same screen.

From CLI 1.4.0 you can skip copying a key. `openwop login` shows a short code; type it on the same Access → API keys page under "Sign in the OpenWOP CLI" and approve it. The host issues the CLI a 30-day key, saved to your config and listed in the app as `CLI: <label>`; `openwop logout` revokes it. Approve a code only if you just ran `openwop login` yourself.

```bash
export OPENWOP_BASE_URL=https://app.openwop.dev/api
openwop login
```

A new account starts with no workflows, so register one before you create a run:

```bash
cat > hello.json <<'JSON'
{"workflowId":"walk-hello","name":"Walk hello","nodes":[{"nodeId":"done","typeId":"core.noop","config":{}}],"edges":[]}
JSON
openwop workflows register hello.json
openwop runs create walk-hello --wait
```

To run against a host of your own instead, the [quickstart](https://openwop.dev/quickstart/) starts one locally with a development key, and the [install page](https://openwop.dev/install/) covers a full deployment.

`openwop onboard` saves your host URL and stores your own model-provider key (BYOK) on the host. It does not sign you in or issue an OpenWOP API key.

The in-app command reference — with one-click copyable snippets, rendered against whatever host you're pointed at — lives at [app.openwop.dev/cli](https://app.openwop.dev/cli).

## How it behaves — the contract

Three properties hold for every command, so the CLI is safe to script against a shared host:

- **Capability-honest, fail closed.** A command drives a surface only when the host advertises it. When the host doesn't, the command fails closed with a legible message and a non-zero exit — never a stack trace, never a fabricated success.
- **The host is the authority.** Policy, RBAC, toggle/variant resolution, consent, approvals, and goal/proposal completion are all decided host-side. The CLI renders the host's decision; it never computes or asserts one locally.
- **Secrets are refs, never values.** BYOK keys, OAuth client secrets, SAML certs, and SCIM tokens stay host-side. The CLI handles refs and status only, and runs every response through a redactor before output — nothing secret is printed, logged, or written to local config.

Meaningful exit codes make commands scriptable: `0` ok, `3` escalated / pending, `1` failed (with per-group nuances documented in `--help`).

## Command groups

Point at a host and run `openwop --help` for the live list. The groups cluster by area:

| Area | Groups |
|---|---|
| Setup &amp; health | `onboard` · `doctor` · `capabilities` · `config` |
| Run lifecycle | `runs` · `chat` · `interrupts` · `workflows` · `catalog` · `media` |
| Agents &amp; orchestration | `agents` · `roster` · `workforces` (`fleet`) · `a2a` |
| Governance &amp; safety | `approvals` · `governance` (`policy`) · `consent` · `toggles` |
| Identity &amp; access | `users` · `profiles` · `auth` (`sso`) |
| Extensibility &amp; connections | `packs` · `mcp` · `connections` (`conn`) · `providers` · `byok` |
| Memory &amp; workspace | `memory` · `workspace` · `prompts` |
| Automation &amp; messaging | `cron` · `webhooks` · `triggers` · `messaging` · `relay` · `notifications` |
| Observability | `analytics` (`usage`) |

## Agent-platform capabilities

Three higher-level capabilities for driving an agent platform — each capability-gated, so the group activates only when the host advertises it.

### Reviewable learning — `proposals`

An agent's learned change is stored as an **inert** proposal. It does nothing until a human reviews it and applies it through the host's activation gate (which may route through an approval). The CLI renders the host's verdict and never activates locally.

```bash
openwop proposals list --state pending
openwop proposals revise prop_123 --artifact-json '{"systemPrompt":"…"}'
openwop proposals apply prop_123          # host materializes + routes through its activation mode
```

### Standing goals — `goals`

A durable objective the host pursues across runs until a **judge** verdicts it satisfied or a **bound** stops it. Completion is the judge's verdict — there is no `satisfy` verb, and the CLI never declares victory from the client. A bounds-less goal is refused when the host requires bounds, so unattended continuation always terminates.

```bash
openwop goals create --objective "Keep the triage backlog under 20" \
  --judge verifier --continuation schedule --max-iterations 50
openwop goals pause goal_123
```

### Portability — `export` / `import`

Move reusable estate — agents, packs, schedules, rosters, templates — between hosts as a **refs-only** bundle. Secrets never travel as values; the bundle reports `secretsToRebind` references you bind at the destination. Always dry-run first; apply is idempotent and re-owns every entity to you.

```bash
openwop export --kinds agent --kinds schedule --out estate.json
openwop import estate.json --dry-run      # preview creates/updates/skips/conflicts (no writes)
openwop import estate.json                 # apply
```

### Trigger bridge — `triggers`

Bind an external **webhook / email / form** source to a workflow, then let a verified inbound event start a run with the payload as `ctx.triggerData`. The host verifies the source, drops duplicates, and tracks each binding's state (`active`, `paused`, `failed`, `dead-lettered`). The CLI registers the subscription and shows that state.

A webhook binding secret is returned **once** at creation; the CLI prints the fingerprint and never persists the value.

```bash
openwop triggers register --source webhook --workflow wf_intake --dedup --verification required
openwop triggers list --state active
openwop triggers get tgsub_123            # exit code reflects state: 0 active · 3 paused · 1 failed
```

### Durable A2A tasks — `a2a`

When a host advertises `a2a.durableTasks`, every backing run carries a persisted `A2ATaskState` (`taskId === runId`) that survives caller disconnect, host restart, and HITL pauses. `status` shows what the host advertises; `task` reads one durable task's live state. The record is content-free — no run inputs, outputs, artifacts, or credentials — and the CLI renders the host's projection rather than deriving a state locally.

```bash
openwop a2a status
openwop a2a task <taskId>                  # exit: 0 completed · 3 in-progress/needs-input · 1 failed
```

## Channel relay (optional)

The `messaging` and `relay` commands are a non-normative host extension: the CLI can run a local adapter that bridges a chat channel (Signal, iMessage, WhatsApp, Discord) into a workflow or agent on the host. The CLI pairs with the host over an authenticated session via a short code; channel-side auth lives only in the local adapter process and never reaches the host as a primary credential.

## Source

`@openwop/cli` is [on npm](https://www.npmjs.com/package/@openwop/cli); source, docs, and the issue tracker are at [github.com/openwop/openwop-cli](https://github.com/openwop/openwop-cli). The CLI evolves additively within the current major.

## Next steps

- [Install the demo app](https://openwop.dev/install/) — stand up a host for the CLI to drive.
- [Host implementers](https://openwop.dev/for/host-implementers/) — the capability advertisement contract every command checks against.
- [Run the quickstart](https://openwop.dev/quickstart/) — your first workflow against any host.
