@openwop/cli 0.1.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.
- package/README.md +197 -0
- package/dist/cli.js +5769 -0
- package/dist/openwop.js +5740 -0
- package/install.sh +74 -0
- package/package.json +36 -0
package/README.md
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
# OpenWOP CLI
|
|
2
|
+
|
|
3
|
+
The OpenWOP CLI is the local control plane for the `apps/workflow-engine` demo app and a lightweight client for OpenWOP-compatible hosts.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
One-line install (detects the OS, ensures Node 22+, installs the package, then onboards):
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
curl -fsSL https://openwop.dev/install.sh | bash
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Or directly: `npm i -g @openwop/cli` (needs Node 22+), then `openwop onboard`.
|
|
14
|
+
|
|
15
|
+
From a repo clone the CLI is a TypeScript package — build it once, then run the bundled entry (or `npm run cli -- <command>` from the repo root, which builds first):
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm --prefix cli install && npm --prefix cli run build # → cli/dist/openwop.js
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Quick start
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
node cli/dist/openwop.js --help
|
|
25
|
+
node cli/dist/openwop.js doctor # check prerequisites
|
|
26
|
+
node cli/dist/openwop.js onboard # guided setup (host + provider + key + model)
|
|
27
|
+
node cli/dist/openwop.js demo start # boot local backend + frontend (optional)
|
|
28
|
+
node cli/dist/openwop.js demo status
|
|
29
|
+
node cli/dist/openwop.js catalog nodes --search ai
|
|
30
|
+
node cli/dist/openwop.js runs create sample.demo.uppercase --input text=hello --wait
|
|
31
|
+
node cli/dist/openwop.js packs search ads # browse the signed pack registry
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Onboarding
|
|
35
|
+
|
|
36
|
+
The `onboard` wizard walks you through:
|
|
37
|
+
|
|
38
|
+
1. **Host URL** — `https://app.openwop.dev/api` (shared demo), `http://localhost:8080` (local), or a custom URL.
|
|
39
|
+
2. **AI provider** — `anthropic`, `openai`, `google`, or `minimax` (matches what the demo backend dispatches to).
|
|
40
|
+
3. **API key** — auto-detects `ANTHROPIC_API_KEY` / `OPENAI_API_KEY` / `GOOGLE_API_KEY` / `MINIMAX_API_KEY`, or hidden-input via raw-mode stdin. The key is POSTed to `/v1/host/sample/byok/secrets` on the configured host. **The key is never written to your local config file** — only a credential ref pointer is stored.
|
|
41
|
+
4. **Model** — provider-specific recommended defaults plus a custom option.
|
|
42
|
+
5. **Test the connection** — verifies the credential ref appears in the host's BYOK list.
|
|
43
|
+
|
|
44
|
+
Re-running `openwop onboard` is safe: it detects existing config and asks Keep / Modify / Reset.
|
|
45
|
+
|
|
46
|
+
For scripted use:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
openwop onboard --non-interactive \
|
|
50
|
+
--base-url-choice shared \
|
|
51
|
+
--provider anthropic \
|
|
52
|
+
--api-key-env ANTHROPIC_API_KEY \
|
|
53
|
+
--model claude-sonnet-4-6
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Provider management
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
openwop providers list
|
|
60
|
+
openwop providers add openai --api-key-env OPENAI_API_KEY --model gpt-4o
|
|
61
|
+
openwop providers remove openai
|
|
62
|
+
openwop providers test anthropic
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
`providers add` POSTs to `/v1/host/sample/byok/secrets`; `remove` DELETEs; `list` and `test` read.
|
|
66
|
+
|
|
67
|
+
## Chat
|
|
68
|
+
|
|
69
|
+
`openwop chat <workflowId>` opens an interactive streaming REPL. Each message you type creates a run for the workflow carrying the running conversation as a `messages` array, then streams that run's events to the terminal as they arrive.
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
openwop chat sample.chat.turn
|
|
73
|
+
openwop chat sample.chat.turn --inputs-json '{"credentialRef":"anthropic-default"}'
|
|
74
|
+
openwop chat sample.chat.turn --no-stream --json
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
- **Streaming** — prefers Server-Sent Events (`GET /v1/runs/{runId}/events`). When the host does not stream (it answers with JSON or a non-streamable body), the CLI falls back to polling `GET /v1/runs/{runId}/events/poll`.
|
|
78
|
+
- **Turns** — type a line and press Enter. Conversation history is threaded across turns; the assistant's reply is fed back as context. Use `--no-history` to send only the latest turn.
|
|
79
|
+
- **Quitting** — `/exit`, `/quit`, or Ctrl-D (EOF).
|
|
80
|
+
- **`--json`** — emits raw event records (one JSON object per event) instead of the pretty `assistant>` rendering.
|
|
81
|
+
- Extra per-turn inputs (`--input k=v`, `--inputs-json`) ride along on every run, so you can pin a `credentialRef`, model, or other configurable input.
|
|
82
|
+
## Packs (signed node-pack registry)
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
openwop packs search ads # filter the catalog
|
|
86
|
+
openwop packs info community.openwop-team.demo # metadata + versions
|
|
87
|
+
openwop packs install community.openwop-team.demo@0.1.0 # download + verify
|
|
88
|
+
openwop packs publish ./my-pack --key ~/key.pem --key-id me-1
|
|
89
|
+
openwop packs yank community.openwop-team.demo@0.1.0 # local registry edit
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
These talk to the signed node-pack registry — a **separate surface** from the host `--base-url`. The default registry is `https://packs.openwop.dev`; override with `--registry-url` or `OPENWOP_REGISTRY_URL`.
|
|
93
|
+
|
|
94
|
+
- **search** reads `/v1/index.json` and filters the catalog client-side (the dynamic host `/v1/packs/-/search` only knows in-process nodes, not the published catalog).
|
|
95
|
+
- **info** reads `/v1/packs/{name}/index.json`; `--version v` also fetches that version's manifest.
|
|
96
|
+
- **install** downloads `/v1/packs/{name}/-/{version}.tgz`, checks its `sha256` against the manifest `integrity`, and verifies the detached Ed25519 `.sig` against the publisher key at `/keys/{keyId}.pub` (matching `signing.method` — `ed25519` signs the tarball, `manual` signs the in-tarball `pack.json`). Skip verification with `--no-verify`. Artifacts land under `~/.openwop/packs/{name}/{version}/` (override with `--dir`). Yanked versions are refused.
|
|
97
|
+
- **publish** — the reference registry has **no write API** (`writeApi.supported=false`; publish is a GitHub pull request). This command performs the local **packaging + Ed25519 signing** flow (mirrors `scripts/build-pack-tarball.mjs --signed`): it emits a deterministic signed `.tgz`, a 64-byte `.sig`, and a sidecar manifest into `dist/packs/` (override with `--out`), ready to commit and PR. The private key comes from `--key <pem>`, else `~/.openwop-keys/{keyId}.private.pem`; if neither exists an ephemeral key is generated and its public half printed for pre-registration.
|
|
98
|
+
- **yank** edits a **local registry checkout** — flips `"yanked": true` in the version manifest (`--undo` reverses), so the change is ready to commit, rerun `registry/scripts/build-index.mjs`, and PR. Run it from inside the repo.
|
|
99
|
+
|
|
100
|
+
## Messaging & relay
|
|
101
|
+
|
|
102
|
+
Connect chat channels (Signal / WhatsApp / iMessage) to the host so inbound messages drive workflow runs and replies are delivered back. Channels are a **host-extension** surface (`/v1/host/sample/messaging`), not part of the normative OpenWOP wire — the protocol stays channel-agnostic.
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
openwop relay setup --channel signal # register + activate a device, store its token
|
|
106
|
+
openwop relay start # bridge loop: heartbeat → poll outbound → deliver → ack
|
|
107
|
+
openwop relay send --conversation +15551234 --text "hi" # operator-side: queue an outbound
|
|
108
|
+
openwop relay status # probe the device token against the host
|
|
109
|
+
openwop messaging connectors list|add|enable|disable|test
|
|
110
|
+
openwop messaging sessions list|inspect|close
|
|
111
|
+
openwop messaging policy get|set <connectorId> # DM/group access + require-mention
|
|
112
|
+
openwop messaging routing list|add|remove # inbound match → bound workflow
|
|
113
|
+
openwop messaging identity list|show|create|link|unlink|delete # cross-channel peer linking
|
|
114
|
+
openwop messaging logs [--channel c] [--direction inbound|outbound] [--status s] [--limit n]
|
|
115
|
+
openwop notify email --to a@b.dev --text "hi" [--subject S] # one-off dispatch
|
|
116
|
+
openwop notify sms --to +15551234 --text "pong"
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
The operator subcommands manage the gateway beyond device wiring:
|
|
120
|
+
|
|
121
|
+
- **policy** — per-connector access: `set <id> --dm <pairing|allowlist|open|disabled> --group <allowlist|open|disabled> --require-mention <true|false>`. `get` returns the host default (DM pairing, groups allowlist-only, mention required) when none is stored.
|
|
122
|
+
- **routing** — `add --pattern "*" --workflow <id> [--channel c] [--priority n]` binds matching inbound conversations to a workflow; higher priority wins. `remove <ruleId>` deletes one.
|
|
123
|
+
- **identity** — links platform peers across channels into one logical person: `create --name N --peer <channel>:<peerId>`, `link <id> --peer …` (merges, de-duped), `unlink <id> --peer …`, `delete <id>`.
|
|
124
|
+
- **logs** — queries the delivery log (inbound ingested / outbound queued), filterable by channel, direction, status; `--limit` is clamped to `[1, 1000]`.
|
|
125
|
+
- **notify** — a one-off email/SMS dispatch (`POST …/messaging/notify`). The reference app returns a **synthetic receipt**; wiring a real provider (SES / Twilio) is a host concern.
|
|
126
|
+
|
|
127
|
+
How it works: the relay device owns the platform connection and bridges it to the host. Inbound messages POST to the host, which runs the bound workflow (default `sample.demo.uppercase`; override with `OPENWOP_MESSAGING_WORKFLOW_ID`) and queues the reply; the device pulls + delivers it. `doctor` reports per-channel readiness:
|
|
128
|
+
|
|
129
|
+
- **Signal** needs [`signal-cli`](https://github.com/AsamK/signal-cli) on `PATH`.
|
|
130
|
+
- **iMessage** needs macOS (Messages signed in + Full Disk Access for `chat.db`).
|
|
131
|
+
- **WhatsApp** ships with the channel build (Baileys), not the core CLI.
|
|
132
|
+
|
|
133
|
+
`relay start` also **receives**: each channel plugin streams inbound platform messages and forwards them to the host's `/device/inbound` (which runs the bound workflow and queues a reply). Signal uses `signal-cli ... receive`, iMessage polls `chat.db` by ROWID, WhatsApp binds Baileys `messages.upsert`. Disable inbound with `--no-receive`. When a channel's tooling isn't present, inbound is skipped and outbound prints to the console instead of silently dropping it.
|
|
134
|
+
|
|
135
|
+
The relay **device token** is a host credential, so it is stored separately from `config.json` in `~/.openwop/relay-credentials.json` (mode `0600`), not in your main config. Revoke it any time with `openwop relay revoke`.
|
|
136
|
+
|
|
137
|
+
## Runs
|
|
138
|
+
|
|
139
|
+
Beyond `runs create` / `list` / `get` / `cancel`, the CLI surfaces a run's full inspection + review surface:
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
openwop runs events <runId> [--since <seq>] [--limit n] # JSON event poll (GET .../events/poll)
|
|
143
|
+
openwop runs annotations <runId> # list review signals
|
|
144
|
+
openwop runs annotate <runId> --rating 5 [--note "great"] # attach a rating/label/correction/flag
|
|
145
|
+
openwop runs annotate <runId> --label triage --event-id <id>
|
|
146
|
+
openwop runs debug-bundle <runId> [--max-events n] [--out bundle.json]
|
|
147
|
+
openwop runs ancestry <runId> # RFC 0040 cross-host parent chain
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
`runs events --since N` maps to the spec-canonical `lastSequence` query param (events with `sequence > N`). `runs annotate` posts exactly one signal kind (`--rating 1-5` | `--label` | `--correction` | `--flag`), validated client-side. `runs debug-bundle --out <file>` saves the full event bundle to disk.
|
|
151
|
+
|
|
152
|
+
## Operator surfaces
|
|
153
|
+
|
|
154
|
+
Beyond runs/workflows/catalog, the CLI surfaces the host's operator endpoints: `agents`, `memory`, `media`, `webhooks`, `chat`, plus `notifications` (inbox), `interrupts` (list/resolve human-in-the-loop pauses), and `prompts` (RFC 0029 library list/get/render). Every command supports `--json`.
|
|
155
|
+
|
|
156
|
+
Two maintenance commands guard destructive operations behind a confirmation prompt (default *no*, bypass with `--confirm`):
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
openwop account delete # irreversibly wipe ALL data for the signed-in account
|
|
160
|
+
openwop admin cleanup # wipe expired ephemeral secrets for idle tenants
|
|
161
|
+
openwop admin cleanup --status # read-only liveness probe
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
`account delete` requires a signed-in user (OIDC bearer). `admin` routes are gated by the host's `OPENWOP_ADMIN_TOKEN` — pass it via `--api-key`.
|
|
165
|
+
|
|
166
|
+
## Config
|
|
167
|
+
|
|
168
|
+
`~/.openwop/config.json` (or `$OPENWOP_CONFIG_HOME/.openwop/`) stores the host URL, default provider, default model, and credential ref. **API keys are never stored locally.**
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
openwop config file # print path
|
|
172
|
+
openwop config get # print full config
|
|
173
|
+
openwop config get host.baseUrl # dotted-path lookup
|
|
174
|
+
openwop config set defaultModel gpt-4o
|
|
175
|
+
openwop config unset credentialRef
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
## Defaults
|
|
179
|
+
|
|
180
|
+
- Host URL: `OPENWOP_BASE_URL` or `http://localhost:8080`. Flag (`--base-url`) wins over env; env wins over default.
|
|
181
|
+
- OpenWOP host bearer (NOT the LLM provider key): `OPENWOP_API_KEY`, or `sample-token` for localhost demo URLs. Pass via global `--api-key` if you need to override.
|
|
182
|
+
- LLM provider key: `--provider-key <key>` or `--api-key-env <VAR>` on `onboard` / `providers add` (not stored locally).
|
|
183
|
+
- Frontend URL: `http://localhost:5173`.
|
|
184
|
+
|
|
185
|
+
Input parsing for `runs create`:
|
|
186
|
+
|
|
187
|
+
- `--input k=v` — each value is `JSON.parse`d first; on parse failure it falls back to a string. So `--input n=5` is the number `5`, `--input enabled=true` is the boolean `true`, `--input text=hello` is the string `"hello"`, and `--input list=[1,2,3]` is an array. Quote shell-special characters.
|
|
188
|
+
- `--inputs-json '{"a":1}'` passes the whole `inputs` object as one JSON literal. Merged BEFORE `--input` pairs (which override).
|
|
189
|
+
- `--wait` polls `GET /v1/runs/{runId}` every 250ms until terminal status or `--timeout-ms` (default 30000) elapses. Exit 0 only on `completed`.
|
|
190
|
+
|
|
191
|
+
Exit codes:
|
|
192
|
+
|
|
193
|
+
- `0` — success.
|
|
194
|
+
- `1` — server-side failure (5xx) or run terminated in `failed` / `cancelled` under `--wait`.
|
|
195
|
+
- `2` — user-fixable error (unknown command, missing argument, 4xx response).
|
|
196
|
+
|
|
197
|
+
The CLI is dependency-light by design. It uses Node built-ins so contributors can operate the demo app without installing another command framework.
|