@openwop/cli 0.2.1 → 0.18.1

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.
Files changed (4) hide show
  1. package/README.md +241 -2
  2. package/dist/cli.js +10350 -2953
  3. package/dist/openwop.js +10349 -2953
  4. package/package.json +2 -2
package/README.md CHANGED
@@ -4,6 +4,25 @@ The OpenWOP CLI is the local control plane for the [OpenWOP](https://github.com/
4
4
 
5
5
  This is the standalone home of the `@openwop/cli` package; it ships independently of the [`openwop/openwop`](https://github.com/openwop/openwop) spec corpus.
6
6
 
7
+ ## Command groups
8
+
9
+ The CLI is a host-agnostic **control plane**: every group drives one protocol surface a host advertises, and degrades gracefully when the host doesn't serve it. Full catalog in [`FEATURES.md`](./FEATURES.md); shape + boundaries in [`ARCHITECTURE.md`](./ARCHITECTURE.md); what's planned next in [`ROADMAP.md`](./ROADMAP.md).
10
+
11
+ | Area | Groups |
12
+ |---|---|
13
+ | **Setup & health** | `onboard` · `doctor` · `demo` · `health` · `capabilities` · `config` · `completion` · `upgrade` |
14
+ | **Run lifecycle** | `runs` · `chat` · `interrupts` · `workflows` · `catalog` · `media` |
15
+ | **Agents & orchestration** | `agents` · `roster` · `org-chart` · `kanban` · `workforces` (`fleet`) |
16
+ | **Governance & safety** | `approvals` · `governance` (`policy`) · `consent` · `toggles` |
17
+ | **Identity & access** | `orgs` · `users` · `profiles` · `auth` (`sso`) |
18
+ | **Extensibility & connections** | `packs` · `mcp` · `connections` (`conn`) · `providers` · `byok` |
19
+ | **Memory & workspace** | `memory` · `workspace` · `prompts` |
20
+ | **Automation & messaging** | `cron` · `webhooks` · `messaging` · `relay` · `notifications` · `notify` |
21
+ | **Observability & admin** | `analytics` (`usage`) · `account` · `admin` · `brand` · `conformance` |
22
+ | **Product surfaces** | `crm` · `csm` · `comments` · `sharing` · `forms` · `email` · `chat-widget` · `marketplace` · `kb` · `cms` · `documents` · `projects` |
23
+
24
+ > **Recent sprint (`[Unreleased]`, slated for `0.3.0`):** 11 new groups + a `cron enable/disable` extension surfaced the host's governance/safety, identity, extensibility, orchestration, and observability surfaces — `approvals`, `governance`, `consent`, `toggles`, `users`, `profiles`, `auth`, `mcp`, `connections`, `workforces`, `analytics`. See [`CHANGELOG.md`](./CHANGELOG.md) for the per-group detail.
25
+
7
26
  ## Install
8
27
 
9
28
  One-line install (detects the OS, ensures Node 22+, installs the package, then onboards):
@@ -172,6 +191,8 @@ openwop workspace put notes/todo.md --content "- ship it"
172
191
  openwop byok list # BYOK credential refs (never values)
173
192
  openwop byok set --ref anthropic-prod # prompts for the secret (no shell history)
174
193
  openwop agents create --persona "Triage bot" --model-class fast # user-defined agent CRUD
194
+ openwop brand public # the applied white-label identity (anonymous)
195
+ openwop brand set --accent 'oklch(58% 0.13 250)' --product-name 'Acme Ops' # generative theme (super-admin)
175
196
  ```
176
197
 
177
198
  Destructive commands (`roster delete`, `kanban board-delete`/`card-delete`, `orgs … delete`, `workspace delete`, `byok delete`, `agents delete`) require an explicit `--yes`.
@@ -186,6 +207,222 @@ openwop admin cleanup --status # read-only liveness probe
186
207
 
187
208
  `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`.
188
209
 
210
+ ## Governance
211
+
212
+ Tenant governance administration (ADR 0028 — a superadmin-gated host extension). The host is the policy authority; the CLI only **renders** its resolved view and never evaluates a policy outcome locally. Because the surface is non-normative (not advertised in `/.well-known/openwop`), the command **fails closed legibly** (exit 2) when a host doesn't expose it.
213
+
214
+ ```bash
215
+ openwop governance policy # render stored policy + the host's declared defaults
216
+ openwop governance policy get --json # raw host view
217
+ openwop governance policy set --action email.send=approval-required --action nudge=disabled
218
+ openwop governance policy set --provider-allowlist anthropic,openai --retention-graph-days 90
219
+ openwop governance audit --prefix governance. --limit 20 # tenant-scoped audit read view
220
+ ```
221
+
222
+ Action kinds are `email.send`, `calendar.invite`, `calendar.reschedule`, `nudge`; policies are `disabled | draft-only | approval-required` (unset kinds fall back to the host's declared default). Aliased as `openwop policy`.
223
+ The `approvals` group drives the host's approval inbox — the human side of "agents propose, humans dispose". A review-mode roster member's heartbeat queues a proposal instead of starting the run; you resolve it here:
224
+
225
+ ```bash
226
+ openwop approvals list --status pending # the queue (also: approved | rejected)
227
+ openwop approvals get appr_123 --json # inspect one proposal
228
+ openwop approvals claim appr_123 --note "LGTM" # affirmative sign-off → starts the run
229
+ openwop approvals reject appr_123 # dismiss the proposal (alias: deny)
230
+ ```
231
+
232
+ The host is the authority for every decision: the CLI renders the host's resolved queue and relays your claim/reject — it never decides locally, and fails closed if the host doesn't advertise the surface. Exit codes reflect the verdict so scripts can gate on it: `0` approved · `3` pending · `1` rejected/error. (Distinct from `interrupts`, which are run-level HITL tokens.)
233
+ ## Consent (privacy / GDPR)
234
+
235
+ `consent` drives the host's tenant-scoped, region-aware consent surface (ADR 0020). The host is the authority — the CLI **renders** its resolved view and never computes a consent decision itself; when the org-tenant's `consent` toggle is off the host returns a uniform 404 and the command fails closed with a legible message.
236
+
237
+ ```bash
238
+ openwop consent policy <orgId> # show the tenant policy (defaultMode + regulated regions)
239
+ openwop consent set-policy <orgId> --default-mode opt-out --regulated-region EU --regulated-region UK
240
+ openwop consent records <orgId> --json # list all consent records for the tenant
241
+ openwop consent get <orgId> <subjectKey> # one subject's record (or none)
242
+ openwop consent erase <orgId> <subjectKey> --yes # GDPR erasure (irreversible; --yes required)
243
+ openwop consent public get <orgId> <subjectKey> # public, unauthed: a visitor's recorded categories (or the policy default)
244
+ openwop consent public record <orgId> <subjectKey> --category analytics=true --category marketing=false
245
+ ```
246
+
247
+ Exit codes: `0` success · `1` host/HTTP error (including consent not enabled) · `2` usage error.
248
+
249
+ ## MCP server mount
250
+
251
+ An MCP client for the host's JSON-RPC server mount (RFC 0020) — a single JSON-RPC 2.0 endpoint at `POST /v1/host/sample/mcp` speaking modelcontextprotocol.io 2025-06-18. The mount is **host-controlled**: it's env-gated (`OPENWOP_MCP_SERVER_ENABLED`, **OFF by default**) and the CLI cannot toggle it. When the mount isn't exposed the endpoint 404s and these commands **fail closed legibly** (exit 2).
252
+
253
+ ```bash
254
+ openwop mcp ping # liveness probe of the mount
255
+ openwop mcp info --json # initialize: server/protocol/capabilities
256
+ openwop mcp tools list # workflows exposed as MCP tools
257
+ openwop mcp tools call sample.demo.uppercase --args '{"text":"hi"}'
258
+ openwop mcp resources list # + `templates` / `read <uri>`
259
+ openwop mcp prompts get greet --args '{"name":"Ada"}'
260
+ ```
261
+
262
+ `--json` emits the raw JSON-RPC result on any read. JSON-RPC errors surface the host's own message (contract errors → exit 2, host errors → exit 1); a tool whose result is `isError` exits 1.
263
+
264
+ The `connections` group (alias `conn`) inspects the host's third-party connections and their OAuth client configuration (ADR 0024):
265
+
266
+ ```bash
267
+ openwop connections list # connections + status
268
+ openwop connections test conn:abc # health-probe (exit 0 healthy / 1 not)
269
+ openwop connections authorize google --scope https://www.googleapis.com/auth/calendar --write
270
+ openwop connections oauth-clients list # host-configured OAuth apps (superadmin)
271
+ ```
272
+
273
+ `authorize` mints a consent URL and **prints it only** — the CLI never completes the OAuth round-trip (the browser + host callback do). **Secrets stay host-side:** client secrets and tokens are never returned, printed, or logged — the CLI runs every response through a recursive redactor before output, surfacing refs and status only.
274
+ ## Profiles (self-service persona)
275
+
276
+ `profiles` drives the host's user-profile surface (ADR 0005): your job title, bio, contact, skills, availability, portfolio, and pinned agents. The host is the authority and the CLI renders its resolved view — reads are visible to any signed-in tenant member, **writes only ever touch your own profile**. Requires a durable signed-in account (bearer via `--api-key`).
277
+
278
+ ```bash
279
+ openwop profiles me # your own profile
280
+ openwop profiles get <userId> # a tenant member's profile (team-visible)
281
+ openwop profiles list # the tenant persona directory
282
+ openwop profiles edit --job-title "Staff Engineer" --availability-status busy --timezone Europe/London
283
+ openwop profiles skills set --skill TypeScript=5 --skill Rust=3 # replace your skill list (proficiency 1..5)
284
+ openwop profiles endorse <userId> <skill> # endorse a peer's skill (unendorse to remove)
285
+ openwop profiles pin <rosterId> # pin/unpin an agent to your sidebar
286
+ openwop profiles portfolio add --token <mediaToken> # attach an image asset (remove <token> to drop)
287
+ openwop profiles activity --limit 10 --status failed # your run-activity feed
288
+ ```
289
+
290
+ **Boundary:** `profiles` is the persona/skills surface — **not** the user directory (account lifecycle) and **not** RBAC (that's `orgs`). Exit codes: `0` success · `1` host/HTTP error (incl. not found / not signed in) · `2` usage error.
291
+ ## Users (identity directory + lifecycle)
292
+
293
+ `users` drives the host's tenant identity directory and account lifecycle (ADR 0002). It is the durable record of **who** exists in a tenant and whether their account is active — **not** authorization (role/permission membership lives in `orgs`) and **not** editable persona (that's `profiles` / `users me`). The `groups` field is the raw IdP `groups[]` captured at sign-in for the RBAC handoff; it grants nothing by itself. The host is the authority: the surface 404s when not served (fail closed, exit 2), and a disabled account is denied 403.
294
+
295
+ ```bash
296
+ openwop users list # the tenant's users
297
+ openwop users me # your own durable record
298
+ openwop users me --display-name "Ada L." # self-serve rename (PATCH /me)
299
+ openwop users create --principal oidc:abc123 --email a@b.dev --group eng --source oidc
300
+ openwop users get <userId> --json
301
+ openwop users update <userId> --display-name "Ada" --group eng --group oncall # groups replace; --email '' clears
302
+ openwop users disable <userId> # lifecycle (fail-closed control)
303
+ openwop users enable <userId>
304
+ openwop users delete <userId> --yes # irreversible; --yes required
305
+ ```
306
+
307
+ Identity `source` is one of `oidc | password | saml | scim | manual`. Exit codes: `0` ok · `1` host error · `2` usage / surface not served / account disabled.
308
+
309
+ ## Feature toggles
310
+
311
+ `toggles` renders the caller's **resolved** feature-toggle assignments (a non-normative host extension under `/v1/host/sample/feature-toggles/assignments`). The **host** is the sole authority — it resolves every toggle server-side from the authenticated principal — and the CLI only *displays* what the host returns. It **never** computes, asserts, or overrides a toggle decision locally, and it does not author config (the superadmin config surface is intentionally not exposed). When the host doesn't serve the surface the command fails closed (exit 2).
312
+
313
+ ```bash
314
+ openwop toggles list # every toggle's resolved state for you (incl. off)
315
+ openwop toggles list --json # raw host view
316
+ openwop toggles get crm.triageAgent # one toggle: status / enabled / variant / bindings
317
+ ```
318
+
319
+ Resolved fields are host-authored: `status` (`on | off | beta`), `enabled` (bool), assigned `variant`, and the variant's `bindings` (slot → ref@version). Exit codes: `0` ok · `1` host error · `2` usage / surface not served / unknown toggle.
320
+
321
+ The `workforces` group (alias `fleet`) drives the host's durable multi-agent orchestration — a *Governed Workforce* is a bundle of agent specs with graduated-autonomy posture and aggregate telemetry:
322
+
323
+ ```bash
324
+ openwop workforces list # governed workforces + status
325
+ openwop fleet metrics wf_support # aggregate telemetry (runs, rates, cost)
326
+ openwop workforces governance wf_support # autonomy graduation + governance posture
327
+ openwop workforces trace wf_support --q batch_42 # cross-run trace/audit search
328
+ openwop workforces status wf_support production # request a cutover (host gates on graduation)
329
+ ```
330
+
331
+ This group **composes with** `kanban` (task boards) and `roster` (standing agents) but does not duplicate them — it renders the workforce's governance/metrics view, never re-modelling boards or roster entries. The host is the authority; the CLI renders its resolved view and fails closed if the surface isn't advertised. `status production` is gated host-side on the workforce having graduated to bounded-autonomous (a 409 surfaces legibly); `eval` needs the host's eval suite enabled.
332
+
333
+ ## Enterprise SSO (SAML / SCIM)
334
+
335
+ `auth` (alias `sso`) drives the host's enterprise identity surface (RFC 0050). The host is the authority; the CLI surfaces **status, public SP metadata, and provisioning results only**. SAML signing certs, SCIM bearer tokens, and client secrets are host-side and are never printed — every response is run through a recursive redactor before output (the public SP metadata XML, meant to be uploaded to your IdP, is the deliberate exception).
336
+
337
+ ```bash
338
+ openwop auth status # which SSO/SCIM profiles this host advertises + whether SAML SSO is live
339
+ openwop auth saml metadata # SP metadata XML to upload to your IdP
340
+ openwop auth saml login-url --return-to /dashboard # the SP-initiated IdP redirect URL (not followed)
341
+ openwop auth saml validate --idp-url http://localhost:9100/idp --variant valid # conformance: validate a synthetic-IdP assertion
342
+ openwop auth scim provision --op create-user --user-name jo@acme.com --email jo@acme.com
343
+ ```
344
+
345
+ `auth status` trusts discovery (`/.well-known/openwop`) as authoritative — an unadvertised profile is never probed, so a host's SPA catch-all can't false-report a surface as live. Each surface fails closed with a legible message when the host hasn't configured it.
346
+
347
+ **Boundary:** `auth` is SSO/SAML/SCIM identity-provider config — **not** the user directory (`users`), RBAC (`orgs`), or BYOK provider credentials (`byok`/`providers`). Exit codes: `0` success (`saml validate`: `0` = authenticated) · `1` host error / not configured (`saml validate`: `1` = rejected) · `2` usage error.
348
+ ## Analytics (usage)
349
+
350
+ `analytics` (alias `usage`) reads a host's org-scoped usage analytics (ADR 0018). The **host** aggregates server-side and gates each org read by RBAC (`workspace:read`) plus the org-tenant's `analytics` toggle; the CLI only **renders** the rollup it returns — it never computes usage locally — and fails closed (exit 2) when analytics isn't served. This is **usage / cost / observability**, deliberately distinct from `governance audit` (the policy-decision log) — hence the alias is `usage`, not `audit`.
351
+
352
+ ```bash
353
+ openwop analytics summary <orgId> # aggregate rollup (total, sessions, by-type, top paths, utm)
354
+ openwop usage events <orgId> --json # recent raw events (max 100)
355
+ openwop analytics collect <orgId> --session s_abc --type pageview --path /pricing # public consent-gated beacon
356
+ ```
357
+
358
+ The `collect` beacon is **public** (sent without auth) and consent-gated host-side: a `201` records the event; a `202` honestly reports that analytics consent wasn't granted (nothing recorded). Event type is one of `pageview | event | conversion`. Exit codes: `0` ok · `1` host error · `2` usage / analytics not served / not authorized.
359
+
360
+ ### Reviewable-learning proposals (`proposals`, RFC 0096)
361
+
362
+ The `proposals` group drives the host's reviewable-learning proposal lifecycle. An agent's learned change lands as an **inert** proposal — a stored artifact draft that does nothing until a human reviews and applies it. The **host** is the authority: `apply` asks it to materialize the byte image last persisted on the proposal (no re-synthesis) and route activation through its advertised mode (e.g. an RFC 0051 approval-gate); the CLI never activates locally.
363
+
364
+ ```bash
365
+ openwop proposals list --state pending # the review queue (also --kind <artifactKind>)
366
+ openwop proposals get prop_123 --json # inspect one proposal
367
+ openwop proposals revise prop_123 --artifact-json '{"systemPrompt":"…"}' # edit the draft (never activates)
368
+ openwop proposals apply prop_123 # host materializes + routes through its activation gate
369
+ openwop proposals reject prop_123 --note "out of scope"
370
+ openwop proposals archive prop_123 --yes # soft-delete
371
+ ```
372
+
373
+ Capability-gated on `agents.proposals` (advertised in `/.well-known/openwop`); fails closed (exit 1) when the host doesn't serve the surface. Boundary: distinct from `approvals` (the human run-action queue) — proposals are the artifact *drafts* reviewed before they become live behavior. Exit codes (get/apply/reject reflect the verdict): `0` applied · `3` pending (in review) · `1` rejected/archived or error.
374
+
375
+ ### Standing goals (`goals`, RFC 0097)
376
+
377
+ The `goals` group drives the host's standing goals — an objective the host pursues across runs until a **judge** (RFC 0090) verdicts it satisfied or a **bound** (RFC 0058) stops it. **Completion is the judge's verdict**: there is no `complete`/`satisfy` verb and the CLI never sets `satisfied` — you cannot declare victory from the client.
378
+
379
+ ```bash
380
+ openwop goals list --state active # the goal list (also satisfied | escalated | bound-exceeded | paused)
381
+ openwop goals get goal_123 --json # inspect (objective, judge, continuation, bounds, iterations)
382
+ openwop goals create --objective "Keep the triage backlog under 20" \
383
+ --judge verifier --continuation schedule --max-iterations 50 --max-cost 5.00 --timeout-ms 600000
384
+ openwop goals pause goal_123 # suspend continuation (resume to restart)
385
+ openwop goals abandon goal_123 --yes # close the goal
386
+ ```
387
+
388
+ A bounds-less goal is rejected `422` when the host advertises `requiresBounds` — pass `--max-iterations`/`--max-cost`/`--timeout-ms`. The create body maps to the Goal entity shape (`completion.check` / `continuation.mode` / `bounds.{maxLoopIterations,runTimeoutMs,maxCostUsd}`). Capability-gated on `agents.goals`; fails closed (exit 1) when the surface isn't served. Boundary: a goal is **not** an RFC 0068 commitment — it *uses* a commitment/schedule/heartbeat as a continuation arm and adds a judge loop + termination guarantee. Exit codes: `0` satisfied · `3` escalated or still open (active/paused) · `1` bound-exceeded/abandoned or error.
389
+
390
+ ### Agent-platform portability — `export` / `import` (RFC 0098)
391
+
392
+ Move reusable estate (agents, packs, schedules, rosters, templates) between hosts as a portable bundle. **Secrets are refs, never values:** the bundle carries only `secretsToRebind` references — never credential material — and the host rejects a bundle that smuggles a literal credential before it applies anything. Always **dry-run first**.
393
+
394
+ ```bash
395
+ openwop export --json # the full refs-only bundle
396
+ openwop export --kinds agent --kinds schedule --out estate.json # filtered, to a file
397
+ openwop import estate.json --dry-run # preview: creates/updates/skips/conflicts (no writes)
398
+ openwop import estate.json # apply (idempotent; entities re-owned to you)
399
+ ```
400
+
401
+ The CLI runs every response through a secret redactor before printing or writing to disk (defense-in-depth on top of the host's refs-only guarantee). Capability-gated on top-level `portability`; fails closed when the host doesn't serve the surface. This subsumes the old SPA-only `migrate-tenant` bootstrap. Import exit codes: `0` applied (or a clean dry-run plan) · `2` the plan has conflicts (nothing overwritten) · `1` error — a literal credential or `dependsOn` cycle (422), or no import scope (403).
402
+
403
+ ### External-event trigger subscriptions (`triggers`, RFC 0099)
404
+
405
+ The `triggers` group drives the host's **normative** trigger bridge: a subscription binds an external source (`webhook` / `email` / `form`) to a workflow the caller can start, and a verified inbound event then starts a run with the event as `ctx.triggerData`.
406
+
407
+ ```bash
408
+ openwop triggers register --source webhook --workflow wf_intake --dedup --verification required
409
+ openwop triggers list --state active
410
+ openwop triggers get sub_123 --json
411
+ ```
412
+
413
+ `register` returns the created subscription plus its source `binding` (the ingest URL/address). A webhook **binding secret is shown once** — store it then; the CLI never persists it and re-reads return only the `secretFingerprint`. Capability-gated on `capabilities.triggerBridge` (register also honesty-gates on `triggerBridge.ingestion.externalSources`); fails closed when absent. Exit codes: `0` active · `3` paused · `1` failed/dead-lettered or error.
414
+
415
+ ### Async / durable A2A tasks (`a2a`, RFC 0100)
416
+
417
+ When a host advertises `capabilities.a2a.durableTasks`, every backing run has a durable `A2ATaskState` (taskId === runId) readable after the caller disconnects.
418
+
419
+ ```bash
420
+ openwop a2a status # what the host advertises (supported/streaming/push/durableTasks)
421
+ openwop a2a task run_abc123 --json # the durable task's live state
422
+ ```
423
+
424
+ The record is content-free by design (state, `interruptKind`, push config — never run inputs/outputs/credentials); the CLI renders the host's resolved state and never derives one locally. Gated on `capabilities.a2a` (the durable read needs `durableTasks: true`). `task` exit codes: `0` completed · `3` submitted/working/input-required/auth-required · `1` failed/canceled/rejected or error.
425
+
189
426
  ## Config
190
427
 
191
428
  `~/.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.**
@@ -200,8 +437,10 @@ openwop config unset credentialRef
200
437
 
201
438
  ## Defaults
202
439
 
203
- - Host URL: `OPENWOP_BASE_URL` or `http://localhost:8080`. Flag (`--base-url`) wins over env; env wins over default.
204
- - 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.
440
+ - Host URL: `--base-url` > `OPENWOP_BASE_URL` > **saved config** (`onboard`'s `~/.openwop/config.json`) > `http://localhost:8080`. After `onboard`, everyday commands use the saved host with no flag.
441
+ - Profiles: `--profile <name>` (or `OPENWOP_PROFILE`) selects `~/.openwop-<name>/config.json`, so you can keep prod/staging/local hosts side by side.
442
+ - OpenWOP host bearer (NOT the LLM provider key): `--api-key` > `OPENWOP_API_KEY` > saved config > `sample-token` for localhost demo URLs.
443
+ - Shell completion: `openwop completion <bash|zsh|fish>`. Update check: `openwop upgrade`.
205
444
  - LLM provider key: `--provider-key <key>` or `--api-key-env <VAR>` on `onboard` / `providers add` (not stored locally).
206
445
  - Frontend URL: `http://localhost:5173`.
207
446