@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 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.