mirror of
https://github.com/alkimake/paperclip.git
synced 2026-06-14 01:50:39 +09:00
Improve CLI API parity coverage (#6626)
## Thinking Path > - Paperclip is a control plane for AI-agent companies, with the CLI acting as a scriptable operator and agent interface to that control plane. > - The REST API surface has grown across companies, agents, issues, routines, plugins, auth, workspaces, secrets, and operational inspection commands. > - The CLI had drifted from that API surface: some commands were missing, some command shapes differed from docs/reference material, and several edge cases only failed during end-to-end local-source testing. > - The local development runbook requires these tests to be disposable and isolated from a real `~/.paperclip`, `~/.codex`, or `~/.claude` installation. > - This pull request adds broad CLI/API parity coverage, fixes the actionable bugs found during that pass, and records the reproducible test log under `doc/logs`. > - The benefit is a more complete, scriptable CLI surface with regression coverage for the command families exercised by the parity run. ## What Changed - Added or expanded CLI command coverage for access/auth, companies, agents, projects, goals, issues and subresources, routines, plugins, workspaces, activity/run/cost/dashboard inspection, assets, skills, secrets, tokens, prompt/wake flows, and local setup helpers. - Fixed CLI/API parity bugs found during the run, including context profile patching, issue interaction optional payloads, malformed tree-hold errors, environment duplicate handling, configure invalid-section exit codes, worktree pnpm invocation, token agent ID resolution, plugin tool worker lookup, and routine webhook secret cleanup. - Added missing CLI wrappers and route coverage for health/access, invite resolution URL forwarding, join status normalization, secret lifecycle commands, LLM docs routes, available-skill isolation, positive board-claim coverage, and interactive `connect` prompt-flow tests. - Added a schema-backed `/api/openapi.json` route sufficient for CLI parity and `paperclipai openapi --json` smoke coverage. - Added `doc/logs/2026-05-24-cli-api-parity-e2e-log.md` with the detailed living test/bug log and renamed the log directory from `doc/bugs` to `doc/logs`. - Added `doc/plans/2026-05-23-cli-api-parity.md` and the OpenAPI parity reference used during the pass. OpenAPI note: this PR intentionally does not try to subsume `feature/openapi-spec`. The OpenAPI implementation here is schema-backed and better than the earlier route-inventory stub, but `feature/openapi-spec` is the fuller/better OpenAPI branch because it includes exact mounted-route coverage tests and additional current route coverage. That branch should stay as its own PR and can supersede this OpenAPI route implementation. ## Verification Targeted automated checks run: - `pnpm exec vitest run server/src/__tests__/openapi-routes.test.ts` - `pnpm exec vitest run server/src/__tests__/board-claim.test.ts` - `pnpm exec vitest run cli/src/__tests__/connect.test.ts` - `pnpm exec vitest run cli/src/__tests__/agent-lifecycle.test.ts` - `pnpm exec vitest run server/src/__tests__/plugin-database.test.ts` - `pnpm exec vitest run server/src/__tests__/routines-service.test.ts` - `pnpm --dir cli typecheck` - `pnpm --dir server typecheck` Manual/local E2E verification: - Ran the full disposable local-source CLI/API parity pass with isolated `PAPERCLIP_HOME`, `PAPERCLIP_CONFIG`, `PAPERCLIP_CONTEXT`, `PAPERCLIP_AUTH_STORE`, `CODEX_HOME`, and `CLAUDE_HOME` under `tmp/cli-api-parity`. - Verified `DATABASE_URL` and `DATABASE_MIGRATION_URL` stayed unset for the scratch server. - Verified live health and schema-backed OpenAPI responses on non-default port `3197`. - Revoked created board/agent tokens and cleaned up temporary plugins, secrets, non-default environments, and project workspaces. - See `doc/logs/2026-05-24-cli-api-parity-e2e-log.md` for the full command-by-command reproduction log. Not run: - Full `pnpm test`, `pnpm test:run`, or `pnpm build` were not run after the entire branch because the branch is broad and the parity pass used focused test/typecheck verification plus live isolated CLI reruns. ## Risks - This is a broad PR and touches many CLI command modules, so review surface is high. The changes are grouped around one theme, but a split may be easier if maintainers prefer narrower PRs. - The OpenAPI route in this PR is not the final/best OpenAPI implementation. `feature/openapi-spec` has stronger exact-route coverage and should remain the source for the dedicated OpenAPI PR. - The living log is intentionally detailed and large. It is useful for reproducibility but adds documentation weight. - No UI changes are intended; screenshots are not applicable. > For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and discuss it in `#dev` before opening the PR. Feature PRs that overlap with planned core work may need to be redirected — check the roadmap first. See `CONTRIBUTING.md`. ## Model Used - OpenAI Codex, GPT-5-based coding agent in Codex desktop. Exact served model/context-window identifier was not exposed in the local app. Work used shell/Git/GitHub CLI tooling, local source inspection, targeted test execution, and live isolated Paperclip CLI/API smoke testing. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] If this change affects the UI, I have included before/after screenshots - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [x] I will address all Greptile and reviewer comments before requesting merge --------- Co-authored-by: Devin Foley <devin@devinfoley.com>
This commit is contained in:
parent
68401f82f3
commit
70b1a9109d
74 changed files with 18175 additions and 111 deletions
465
doc/CLI.md
465
doc/CLI.md
|
|
@ -65,6 +65,29 @@ All client commands support:
|
|||
|
||||
Company-scoped commands also support `--company-id <id>`.
|
||||
|
||||
API base resolution order:
|
||||
|
||||
1. `--api-base <url>`
|
||||
2. `PAPERCLIP_API_URL`
|
||||
3. selected context profile `apiBase`
|
||||
4. local Paperclip config server port
|
||||
5. `http://localhost:3100`
|
||||
|
||||
Connection failures include the attempted URL and a `GET /api/health` check hint.
|
||||
|
||||
## Connect Wizard
|
||||
|
||||
```sh
|
||||
pnpm paperclipai connect
|
||||
```
|
||||
|
||||
`connect` confirms the resolved API base, verifies `GET /api/health`, authenticates board access when needed, and saves a persona-aware profile:
|
||||
|
||||
- `persona=board` for board operator profiles
|
||||
- `persona=agent` with `agentId` and `agentName` for agent profiles
|
||||
|
||||
Profiles store token env-var names, not plaintext tokens. The wizard prints shell exports for the newly created token.
|
||||
|
||||
Use `--data-dir` on any CLI command to isolate all default local state (config/context/db/logs/storage/secrets) away from `~/.paperclip`:
|
||||
|
||||
```sh
|
||||
|
|
@ -78,6 +101,7 @@ Store local defaults in `~/.paperclip/context.json`:
|
|||
|
||||
```sh
|
||||
pnpm paperclipai context set --api-base http://localhost:3100 --company-id <company-id>
|
||||
pnpm paperclipai context set --persona agent --agent-id <agent-id> --api-key-env-var-name PAPERCLIP_API_KEY
|
||||
pnpm paperclipai context show
|
||||
pnpm paperclipai context list
|
||||
pnpm paperclipai context use default
|
||||
|
|
@ -95,6 +119,17 @@ export PAPERCLIP_API_KEY=...
|
|||
```sh
|
||||
pnpm paperclipai company list
|
||||
pnpm paperclipai company get <company-id>
|
||||
pnpm paperclipai company stats
|
||||
pnpm paperclipai company create --payload-json '{...}'
|
||||
pnpm paperclipai company update <company-id> --payload-json '{...}'
|
||||
pnpm paperclipai company branding:update <company-id> --payload-json '{...}'
|
||||
pnpm paperclipai company archive <company-id>
|
||||
pnpm paperclipai company export <company-id> --out ./company --include company,agents,projects,issues,skills
|
||||
pnpm paperclipai company export:preview <company-id> --payload-json '{...}'
|
||||
pnpm paperclipai company export:api <company-id> --payload-json '{...}'
|
||||
pnpm paperclipai company import ./company --target new --new-company-name "Imported Company"
|
||||
pnpm paperclipai company import:preview <company-id> --payload-json '{...}'
|
||||
pnpm paperclipai company import:apply <company-id> --payload-json '{...}'
|
||||
pnpm paperclipai company delete <company-id-or-prefix> --yes --confirm <same-id-or-prefix>
|
||||
```
|
||||
|
||||
|
|
@ -117,9 +152,102 @@ pnpm paperclipai issue list --company-id <company-id> [--status todo,in_progress
|
|||
pnpm paperclipai issue get <issue-id-or-identifier>
|
||||
pnpm paperclipai issue create --company-id <company-id> --title "..." [--description "..."] [--status todo] [--priority high]
|
||||
pnpm paperclipai issue update <issue-id> [--status in_progress] [--comment "..."]
|
||||
pnpm paperclipai issue delete <issue-id> --yes
|
||||
pnpm paperclipai issue comment <issue-id> --body "..." [--reopen]
|
||||
pnpm paperclipai issue comments <issue-id> [--limit 50]
|
||||
pnpm paperclipai issue comment:get <issue-id> <comment-id>
|
||||
pnpm paperclipai issue comment:delete <issue-id> <comment-id>
|
||||
pnpm paperclipai issue runs <issue-id-or-identifier>
|
||||
pnpm paperclipai issue live-runs <issue-id-or-identifier>
|
||||
pnpm paperclipai issue active-run <issue-id-or-identifier>
|
||||
pnpm paperclipai issue heartbeat-context <issue-id>
|
||||
pnpm paperclipai issue checkout <issue-id> --agent-id <agent-id> [--expected-statuses todo,backlog,blocked]
|
||||
pnpm paperclipai issue release <issue-id>
|
||||
pnpm paperclipai issue force-release <issue-id>
|
||||
```
|
||||
|
||||
Issue subresources are exposed as Paperclip API wrappers. Commands that map to broad server schemas accept JSON payloads and validate them with shared schemas before sending.
|
||||
|
||||
```sh
|
||||
pnpm paperclipai issue child:create <issue-id> --payload-json '{"title":"Child task"}'
|
||||
pnpm paperclipai issue approvals <issue-id>
|
||||
pnpm paperclipai issue approval:link <issue-id> <approval-id>
|
||||
pnpm paperclipai issue approval:unlink <issue-id> <approval-id>
|
||||
pnpm paperclipai issue read <issue-id>
|
||||
pnpm paperclipai issue unread <issue-id>
|
||||
pnpm paperclipai issue archive <issue-id>
|
||||
pnpm paperclipai issue unarchive <issue-id>
|
||||
pnpm paperclipai issue recovery-actions <issue-id>
|
||||
pnpm paperclipai issue recovery:resolve <issue-id> --outcome restored --source-issue-status todo
|
||||
```
|
||||
|
||||
```sh
|
||||
pnpm paperclipai issue documents <issue-id> [--include-system]
|
||||
pnpm paperclipai issue document:get <issue-id> <key>
|
||||
pnpm paperclipai issue document:put <issue-id> <key> --body-file ./plan.md [--title Plan]
|
||||
pnpm paperclipai issue document:lock <issue-id> <key>
|
||||
pnpm paperclipai issue document:unlock <issue-id> <key>
|
||||
pnpm paperclipai issue document:revisions <issue-id> <key>
|
||||
pnpm paperclipai issue document:restore <issue-id> <key> <revision-id>
|
||||
pnpm paperclipai issue document:delete <issue-id> <key>
|
||||
```
|
||||
|
||||
```sh
|
||||
pnpm paperclipai issue work-products <issue-id>
|
||||
pnpm paperclipai issue work-product:create <issue-id> --payload-json '{"type":"pull_request","provider":"github","title":"PR"}'
|
||||
pnpm paperclipai issue work-product:update <work-product-id> --payload-json '{"status":"archived"}'
|
||||
pnpm paperclipai issue work-product:delete <work-product-id>
|
||||
pnpm paperclipai issue interactions <issue-id>
|
||||
pnpm paperclipai issue interaction:create <issue-id> --payload-json '{"kind":"request_confirmation","payload":{"version":1,"prompt":"Continue?"}}'
|
||||
pnpm paperclipai issue interaction:accept <issue-id> <interaction-id> [--selected-client-keys key1,key2]
|
||||
pnpm paperclipai issue interaction:reject <issue-id> <interaction-id> [--reason "..."]
|
||||
pnpm paperclipai issue interaction:respond <issue-id> <interaction-id> --answers-json '[{"questionId":"q1","optionIds":["yes"]}]'
|
||||
pnpm paperclipai issue interaction:cancel <issue-id> <interaction-id> [--reason "..."]
|
||||
```
|
||||
|
||||
```sh
|
||||
pnpm paperclipai issue tree-state <issue-id>
|
||||
pnpm paperclipai issue tree-preview <issue-id> --payload-json '{"mode":"pause"}'
|
||||
pnpm paperclipai issue tree-holds <issue-id> [--status active] [--include-members]
|
||||
pnpm paperclipai issue tree-hold:create <issue-id> --payload-json '{"mode":"pause","reason":"review"}'
|
||||
pnpm paperclipai issue tree-hold:get <issue-id> <hold-id>
|
||||
pnpm paperclipai issue tree-hold:release <issue-id> <hold-id> [--payload-json '{"reason":"done"}']
|
||||
pnpm paperclipai issue attachments <issue-id>
|
||||
pnpm paperclipai issue attachment:upload <issue-id> --company-id <company-id> --file ./artifact.txt
|
||||
pnpm paperclipai issue attachment:download <attachment-id> [--out ./artifact.txt]
|
||||
pnpm paperclipai issue attachment:delete <attachment-id>
|
||||
pnpm paperclipai issue label:list --company-id <company-id>
|
||||
pnpm paperclipai issue label:create --company-id <company-id> --name bug --color '#ff0000'
|
||||
pnpm paperclipai issue label:delete <label-id>
|
||||
pnpm paperclipai issue feedback:votes <issue-id>
|
||||
pnpm paperclipai issue feedback:vote <issue-id> --payload-json '{"targetType":"issue_comment","targetId":"...","vote":"up"}'
|
||||
```
|
||||
|
||||
## Project Commands
|
||||
|
||||
```sh
|
||||
pnpm paperclipai project list --company-id <company-id>
|
||||
pnpm paperclipai project get <project-id-or-shortname> [--company-id <company-id>]
|
||||
pnpm paperclipai project create --company-id <company-id> --name "Launch Site" [--goal-ids <id1,id2>] [--lead-agent-id <id>]
|
||||
pnpm paperclipai project update <project-id-or-shortname> [--status in_progress] [--company-id <company-id>]
|
||||
pnpm paperclipai project delete <project-id-or-shortname> --yes [--company-id <company-id>]
|
||||
```
|
||||
|
||||
Advanced project fields accept JSON:
|
||||
|
||||
```sh
|
||||
pnpm paperclipai project create --company-id <company-id> --name "Ops" --env-json '{"OPENAI_API_KEY":{"kind":"secret","secretName":"openai-api-key"}}'
|
||||
pnpm paperclipai project update <project-id> --execution-workspace-policy-json '{"enabled":true,"defaultMode":"shared_workspace"}'
|
||||
```
|
||||
|
||||
## Goal Commands
|
||||
|
||||
```sh
|
||||
pnpm paperclipai goal list --company-id <company-id>
|
||||
pnpm paperclipai goal get <goal-id>
|
||||
pnpm paperclipai goal create --company-id <company-id> --title "Grow revenue" [--level company] [--status active]
|
||||
pnpm paperclipai goal update <goal-id> [--title "..."] [--status achieved]
|
||||
pnpm paperclipai goal delete <goal-id> --yes
|
||||
```
|
||||
|
||||
## Agent Commands
|
||||
|
|
@ -127,9 +255,44 @@ pnpm paperclipai issue release <issue-id>
|
|||
```sh
|
||||
pnpm paperclipai agent list --company-id <company-id>
|
||||
pnpm paperclipai agent get <agent-id>
|
||||
pnpm paperclipai agent create --company-id <company-id> --payload-json '{"name":"Builder","adapterType":"codex_local"}'
|
||||
pnpm paperclipai agent hire --company-id <company-id> --payload-json '{...}'
|
||||
pnpm paperclipai agent update <agent-id> --payload-json '{"title":"Senior Builder"}'
|
||||
pnpm paperclipai agent delete <agent-id> --yes
|
||||
pnpm paperclipai agent me
|
||||
pnpm paperclipai agent inbox
|
||||
pnpm paperclipai agent inbox-mine --user-id <board-user-id>
|
||||
pnpm paperclipai agent wake <agent-id-or-shortname> [--company-id <company-id>] [--reason "..."] [--payload '{"issueId":"..."}']
|
||||
pnpm paperclipai agent pause <agent-id>
|
||||
pnpm paperclipai agent resume <agent-id>
|
||||
pnpm paperclipai agent approve <agent-id>
|
||||
pnpm paperclipai agent terminate <agent-id>
|
||||
pnpm paperclipai agent heartbeat:invoke <agent-id>
|
||||
pnpm paperclipai agent claude-login <agent-id>
|
||||
pnpm paperclipai agent local-cli <agent-id-or-shortname> --company-id <company-id>
|
||||
```
|
||||
|
||||
Agent configuration and runtime endpoints:
|
||||
|
||||
```sh
|
||||
pnpm paperclipai agent permissions:update <agent-id> --payload-json '{"canCreateAgents":true,"canAssignTasks":true}'
|
||||
pnpm paperclipai agent configuration <agent-id>
|
||||
pnpm paperclipai agent config-revisions <agent-id>
|
||||
pnpm paperclipai agent config-revision:get <agent-id> <revision-id>
|
||||
pnpm paperclipai agent config-revision:rollback <agent-id> <revision-id>
|
||||
pnpm paperclipai agent runtime-state <agent-id>
|
||||
pnpm paperclipai agent runtime-state:reset-session <agent-id> [--task-key <key>]
|
||||
pnpm paperclipai agent task-sessions <agent-id>
|
||||
pnpm paperclipai agent skills <agent-id>
|
||||
pnpm paperclipai agent skills:sync <agent-id> --desired-skills paperclip,github
|
||||
pnpm paperclipai agent instructions-path:update <agent-id> --payload-json '{"path":"/path/to/AGENTS.md"}'
|
||||
pnpm paperclipai agent instructions-bundle <agent-id>
|
||||
pnpm paperclipai agent instructions-bundle:update <agent-id> --payload-json '{"mode":"managed"}'
|
||||
pnpm paperclipai agent instructions-file:get <agent-id> --path AGENTS.md
|
||||
pnpm paperclipai agent instructions-file:put <agent-id> --path AGENTS.md --content-file ./AGENTS.md
|
||||
pnpm paperclipai agent instructions-file:delete <agent-id> --path AGENTS.md
|
||||
```
|
||||
|
||||
`agent local-cli` is the quickest way to run local Claude/Codex manually as a Paperclip agent:
|
||||
|
||||
- creates a new long-lived agent API key
|
||||
|
|
@ -143,6 +306,75 @@ pnpm paperclipai agent local-cli codexcoder --company-id <company-id>
|
|||
pnpm paperclipai agent local-cli claudecoder --company-id <company-id>
|
||||
```
|
||||
|
||||
## Token Commands
|
||||
|
||||
Agent API keys are scoped to one company and one agent. Plaintext tokens are printed once at creation.
|
||||
|
||||
```sh
|
||||
pnpm paperclipai token agent create --company-id <company-id> --agent <agent-id-or-name> --name external-worker
|
||||
pnpm paperclipai token agent list --company-id <company-id> --agent <agent-id-or-name>
|
||||
pnpm paperclipai token agent revoke --company-id <company-id> --agent <agent-id-or-name> <key-id>
|
||||
```
|
||||
|
||||
Named board API keys use the board authorization model, support revocation and expiration metadata, and are audited server-side.
|
||||
|
||||
```sh
|
||||
pnpm paperclipai token board create --company-id <company-id> --name external-admin
|
||||
pnpm paperclipai token board create --name short-lived --ttl-days 7
|
||||
pnpm paperclipai token board list
|
||||
pnpm paperclipai token board revoke <key-id>
|
||||
```
|
||||
|
||||
## Run Commands
|
||||
|
||||
`paperclipai run` without a subcommand still bootstraps and starts a local Paperclip instance. The subcommands below inspect and control API heartbeat runs.
|
||||
|
||||
```sh
|
||||
pnpm paperclipai run list --company-id <company-id> [--agent-id <agent-id>] [--limit 50]
|
||||
pnpm paperclipai run live --company-id <company-id> [--limit 50] [--min-count 0]
|
||||
pnpm paperclipai run get <run-id>
|
||||
pnpm paperclipai run events <run-id> [--after-seq 0] [--limit 200]
|
||||
pnpm paperclipai run log <run-id> [--offset 0] [--limit-bytes 16384] [--text]
|
||||
pnpm paperclipai run cancel <run-id>
|
||||
pnpm paperclipai run issues <run-id>
|
||||
pnpm paperclipai run workspace-operations <run-id>
|
||||
pnpm paperclipai run workspace-log <operation-id> [--offset 0] [--limit-bytes 16384] [--text]
|
||||
pnpm paperclipai run watchdog-decision <run-id> --decision continue [--reason "..."]
|
||||
```
|
||||
|
||||
## Routine Commands
|
||||
|
||||
`paperclipai routines disable-all` remains the local maintenance command. The singular `routine` group maps to the REST API.
|
||||
|
||||
```sh
|
||||
pnpm paperclipai routine list --company-id <company-id> [--project-id <project-id>]
|
||||
pnpm paperclipai routine create --company-id <company-id> --payload-json '{...}'
|
||||
pnpm paperclipai routine get <routine-id>
|
||||
pnpm paperclipai routine update <routine-id> --payload-json '{...}'
|
||||
pnpm paperclipai routine revisions <routine-id>
|
||||
pnpm paperclipai routine revision:restore <routine-id> <revision-id>
|
||||
pnpm paperclipai routine runs <routine-id> [--limit 50]
|
||||
pnpm paperclipai routine run <routine-id> [--payload-json '{...}']
|
||||
pnpm paperclipai routine trigger:create <routine-id> --payload-json '{...}'
|
||||
pnpm paperclipai routine trigger:update <trigger-id> --payload-json '{...}'
|
||||
pnpm paperclipai routine trigger:delete <trigger-id>
|
||||
pnpm paperclipai routine trigger:rotate-secret <trigger-id>
|
||||
pnpm paperclipai routine trigger:fire <public-id> [--payload-json '{...}']
|
||||
```
|
||||
|
||||
## Prompt Handoff
|
||||
|
||||
Prompt handoff creates Paperclip work. It does not create a chat session.
|
||||
|
||||
```sh
|
||||
pnpm paperclipai agent-prompt <agent-name-or-id> <agent-api-key> "Prompt here"
|
||||
pnpm paperclipai agent prompt --agent <agent-name-or-id> --api-key-env PAPERCLIP_API_KEY "Prompt here"
|
||||
pnpm paperclipai agent prompt --profile my-agent "Prompt here"
|
||||
pnpm paperclipai board prompt --company-id <company-id> --agent <agent-name-or-id> "Prompt here"
|
||||
```
|
||||
|
||||
By default the command creates a `todo` issue assigned to the target agent and wakes the agent. Use `--issue <issue-id>` to add a comment to existing work, and `--no-wake` to skip the wakeup.
|
||||
|
||||
## Skills Commands
|
||||
|
||||
`paperclipai skills` covers three distinct operations:
|
||||
|
|
@ -269,6 +501,16 @@ pnpm paperclipai secrets declarations --company-id <company-id> [--include agent
|
|||
pnpm paperclipai secrets create --company-id <company-id> --name anthropic-api-key --value-env ANTHROPIC_API_KEY
|
||||
pnpm paperclipai secrets link --company-id <company-id> --name prod-stripe-key --provider aws_secrets_manager --external-ref <provider-ref>
|
||||
pnpm paperclipai secrets doctor --company-id <company-id>
|
||||
pnpm paperclipai secrets provider-configs --company-id <company-id>
|
||||
pnpm paperclipai secrets provider-config:create --company-id <company-id> --payload-json '{...}'
|
||||
pnpm paperclipai secrets provider-config:discovery-preview --company-id <company-id> --payload-json '{...}'
|
||||
pnpm paperclipai secrets provider-config:get <config-id>
|
||||
pnpm paperclipai secrets provider-config:update <config-id> --payload-json '{...}'
|
||||
pnpm paperclipai secrets provider-config:default <config-id>
|
||||
pnpm paperclipai secrets provider-config:health <config-id>
|
||||
pnpm paperclipai secrets provider-config:delete <config-id>
|
||||
pnpm paperclipai secrets remote-import:preview --company-id <company-id> --payload-json '{...}'
|
||||
pnpm paperclipai secrets remote-import --company-id <company-id> --payload-json '{...}'
|
||||
pnpm paperclipai secrets migrate-inline-env --company-id <company-id> [--apply]
|
||||
```
|
||||
|
||||
|
|
@ -280,10 +522,9 @@ env and the expected AWS SDK runtime credential source; do not store AWS
|
|||
bootstrap credentials in Paperclip secrets.
|
||||
|
||||
Per-company provider vaults (multiple vault instances per provider, default
|
||||
vault selection, coming-soon GCP/Vault) are configured from the board UI under
|
||||
`Company Settings → Secrets → Provider vaults` or through
|
||||
`/api/companies/{companyId}/secret-provider-configs`. There is no CLI surface
|
||||
for vault management today. See the
|
||||
vault selection, coming-soon GCP/Vault) can be configured from the board UI under
|
||||
`Company Settings → Secrets → Provider vaults` or through the provider-config CLI
|
||||
commands above. See the
|
||||
[secrets deploy guide](../docs/deploy/secrets.md#provider-vaults) and
|
||||
[API reference](../docs/api/secrets.md#provider-vaults) for the contract.
|
||||
|
||||
|
|
@ -304,6 +545,8 @@ pnpm paperclipai approval comment <approval-id> --body "..."
|
|||
|
||||
```sh
|
||||
pnpm paperclipai activity list --company-id <company-id> [--agent-id <agent-id>] [--entity-type issue] [--entity-id <id>]
|
||||
pnpm paperclipai activity create --company-id <company-id> --payload-json '{...}'
|
||||
pnpm paperclipai activity issue <issue-id>
|
||||
```
|
||||
|
||||
## Dashboard Commands
|
||||
|
|
@ -312,6 +555,220 @@ pnpm paperclipai activity list --company-id <company-id> [--agent-id <agent-id>]
|
|||
pnpm paperclipai dashboard get --company-id <company-id>
|
||||
```
|
||||
|
||||
## Org And Agent Config Commands
|
||||
|
||||
```sh
|
||||
pnpm paperclipai whoami
|
||||
pnpm paperclipai openapi
|
||||
pnpm paperclipai org get --company-id <company-id>
|
||||
pnpm paperclipai org svg --company-id <company-id> [--out org.svg]
|
||||
pnpm paperclipai org png --company-id <company-id> [--out org.png]
|
||||
pnpm paperclipai agent-config list --company-id <company-id>
|
||||
```
|
||||
|
||||
## Access, Profile, And Instance Commands
|
||||
|
||||
```sh
|
||||
pnpm paperclipai profile session
|
||||
pnpm paperclipai profile get
|
||||
pnpm paperclipai profile update --payload-json '{...}'
|
||||
pnpm paperclipai profile company-user <user-slug> --company-id <company-id>
|
||||
pnpm paperclipai invite list --company-id <company-id>
|
||||
pnpm paperclipai invite create --company-id <company-id> --payload-json '{...}'
|
||||
pnpm paperclipai invite revoke <invite-id>
|
||||
pnpm paperclipai invite show <token>
|
||||
pnpm paperclipai invite accept <token> [--payload-json '{...}']
|
||||
pnpm paperclipai invite onboarding:text <token>
|
||||
pnpm paperclipai join list --company-id <company-id> [--status pending_approval]
|
||||
pnpm paperclipai join approve <request-id> --company-id <company-id>
|
||||
pnpm paperclipai join reject <request-id> --company-id <company-id>
|
||||
pnpm paperclipai join claim-key <request-id> --claim-secret <secret>
|
||||
pnpm paperclipai member list --company-id <company-id>
|
||||
pnpm paperclipai member update <member-id> --company-id <company-id> --payload-json '{...}'
|
||||
pnpm paperclipai member role-and-grants <member-id> --company-id <company-id> --payload-json '{...}'
|
||||
pnpm paperclipai member permissions <member-id> --company-id <company-id> --payload-json '{...}'
|
||||
pnpm paperclipai member archive <member-id> --company-id <company-id> [--payload-json '{...}']
|
||||
pnpm paperclipai admin user list [--query <text>]
|
||||
pnpm paperclipai admin user promote <user-id>
|
||||
pnpm paperclipai admin user demote <user-id>
|
||||
pnpm paperclipai admin user company-access <user-id>
|
||||
pnpm paperclipai admin user company-access:update <user-id> --payload-json '{...}'
|
||||
```
|
||||
|
||||
CLI auth challenge endpoints are also exposed for tooling that needs the raw challenge lifecycle:
|
||||
|
||||
```sh
|
||||
pnpm paperclipai auth challenge create --payload-json '{...}'
|
||||
PAPERCLIP_CHALLENGE_SECRET=<challenge-secret> pnpm paperclipai auth challenge get <challenge-id> --token-env PAPERCLIP_CHALLENGE_SECRET
|
||||
PAPERCLIP_CHALLENGE_SECRET=<challenge-secret> pnpm paperclipai auth challenge approve <challenge-id> --token-env PAPERCLIP_CHALLENGE_SECRET
|
||||
PAPERCLIP_CHALLENGE_SECRET=<challenge-secret> pnpm paperclipai auth challenge cancel <challenge-id> --token-env PAPERCLIP_CHALLENGE_SECRET
|
||||
pnpm paperclipai auth revoke-current
|
||||
```
|
||||
|
||||
`--token <challenge-secret>` is still supported for compatibility, but `--token-env` avoids putting challenge secrets in shell history or process arguments.
|
||||
|
||||
```sh
|
||||
pnpm paperclipai instance scheduler-heartbeats
|
||||
pnpm paperclipai instance settings:general
|
||||
pnpm paperclipai instance settings:general:update --payload-json '{...}'
|
||||
pnpm paperclipai instance settings:experimental
|
||||
pnpm paperclipai instance settings:experimental:update --payload-json '{...}'
|
||||
pnpm paperclipai instance database-backup
|
||||
pnpm paperclipai sidebar preferences
|
||||
pnpm paperclipai sidebar preferences:update --payload-json '{...}'
|
||||
pnpm paperclipai sidebar project-preferences --company-id <company-id>
|
||||
pnpm paperclipai sidebar project-preferences:update --company-id <company-id> --payload-json '{...}'
|
||||
pnpm paperclipai sidebar badges --company-id <company-id>
|
||||
pnpm paperclipai inbox dismissals --company-id <company-id>
|
||||
pnpm paperclipai inbox dismiss --company-id <company-id> --payload-json '{"itemKey":"run:<run-id>"}'
|
||||
pnpm paperclipai board-claim show <token>
|
||||
pnpm paperclipai board-claim claim <token> [--payload-json '{...}']
|
||||
pnpm paperclipai openclaw invite-prompt --company-id <company-id> --payload-json '{...}'
|
||||
pnpm paperclipai available-skill list
|
||||
pnpm paperclipai available-skill index
|
||||
pnpm paperclipai available-skill get <skill-name>
|
||||
pnpm paperclipai llm agent-configuration
|
||||
pnpm paperclipai llm agent-configuration:adapter <adapter-type>
|
||||
pnpm paperclipai llm agent-icons
|
||||
```
|
||||
|
||||
## Adapter, Asset, And Skill Commands
|
||||
|
||||
```sh
|
||||
pnpm paperclipai adapter list
|
||||
pnpm paperclipai adapter install --payload-json '{"packageName":"@scope/adapter","version":"1.2.3"}'
|
||||
pnpm paperclipai adapter get <adapter-type>
|
||||
pnpm paperclipai adapter update <adapter-type> --payload-json '{"disabled":true}'
|
||||
pnpm paperclipai adapter override <adapter-type> --payload-json '{"paused":true}'
|
||||
pnpm paperclipai adapter reload <adapter-type>
|
||||
pnpm paperclipai adapter reinstall <adapter-type>
|
||||
pnpm paperclipai adapter delete <adapter-type>
|
||||
pnpm paperclipai adapter config-schema <adapter-type>
|
||||
pnpm paperclipai adapter ui-parser <adapter-type>
|
||||
pnpm paperclipai adapter models <adapter-type> --company-id <company-id> [--refresh] [--environment-id <id>]
|
||||
pnpm paperclipai adapter model-profiles <adapter-type> --company-id <company-id>
|
||||
pnpm paperclipai adapter detect-model <adapter-type> --company-id <company-id>
|
||||
pnpm paperclipai adapter test-environment <adapter-type> --company-id <company-id> --payload-json '{...}'
|
||||
```
|
||||
|
||||
```sh
|
||||
pnpm paperclipai asset image:upload --company-id <company-id> --file ./image.png [--namespace docs] [--alt "..."]
|
||||
pnpm paperclipai asset logo:upload --company-id <company-id> --file ./logo.svg
|
||||
pnpm paperclipai asset content <asset-id> --out ./asset.bin
|
||||
```
|
||||
|
||||
```sh
|
||||
pnpm paperclipai skill list --company-id <company-id>
|
||||
pnpm paperclipai skill get <skill-id> --company-id <company-id>
|
||||
pnpm paperclipai skill file <skill-id> --company-id <company-id> [--path SKILL.md]
|
||||
pnpm paperclipai skill create --company-id <company-id> --payload-json '{...}'
|
||||
pnpm paperclipai skill file:update <skill-id> --company-id <company-id> --payload-json '{...}'
|
||||
pnpm paperclipai skill import --company-id <company-id> --payload-json '{"source":"github:owner/repo/path"}'
|
||||
pnpm paperclipai skill scan-projects --company-id <company-id> --payload-json '{...}'
|
||||
pnpm paperclipai skill update-status <skill-id> --company-id <company-id>
|
||||
pnpm paperclipai skill install-update <skill-id> --company-id <company-id>
|
||||
pnpm paperclipai skill delete <skill-id> --company-id <company-id>
|
||||
```
|
||||
|
||||
## Cost, Finance, And Budget Commands
|
||||
|
||||
```sh
|
||||
pnpm paperclipai cost summary --company-id <company-id>
|
||||
pnpm paperclipai cost by-agent --company-id <company-id>
|
||||
pnpm paperclipai cost by-agent-model --company-id <company-id>
|
||||
pnpm paperclipai cost by-provider --company-id <company-id>
|
||||
pnpm paperclipai cost by-biller --company-id <company-id>
|
||||
pnpm paperclipai cost by-project --company-id <company-id>
|
||||
pnpm paperclipai cost window-spend --company-id <company-id>
|
||||
pnpm paperclipai cost quota-windows --company-id <company-id>
|
||||
pnpm paperclipai cost issue <issue-id>
|
||||
pnpm paperclipai cost event:create --company-id <company-id> --payload-json '{...}'
|
||||
```
|
||||
|
||||
```sh
|
||||
pnpm paperclipai finance event:create --company-id <company-id> --payload-json '{...}'
|
||||
pnpm paperclipai finance events --company-id <company-id>
|
||||
pnpm paperclipai finance summary --company-id <company-id>
|
||||
pnpm paperclipai finance by-biller --company-id <company-id>
|
||||
pnpm paperclipai finance by-kind --company-id <company-id>
|
||||
pnpm paperclipai budget overview --company-id <company-id>
|
||||
pnpm paperclipai budget policy:upsert --company-id <company-id> --payload-json '{...}'
|
||||
pnpm paperclipai budget company:update --company-id <company-id> --payload-json '{...}'
|
||||
pnpm paperclipai budget agent:update <agent-id> --payload-json '{...}'
|
||||
pnpm paperclipai budget incident:resolve <incident-id> --company-id <company-id> [--payload-json '{...}']
|
||||
```
|
||||
|
||||
## Workspace And Environment Commands
|
||||
|
||||
```sh
|
||||
pnpm paperclipai workspace list --company-id <company-id>
|
||||
pnpm paperclipai workspace get <execution-workspace-id>
|
||||
pnpm paperclipai workspace close-readiness <execution-workspace-id>
|
||||
pnpm paperclipai workspace operations <execution-workspace-id>
|
||||
pnpm paperclipai workspace update <execution-workspace-id> --payload-json '{...}'
|
||||
pnpm paperclipai workspace runtime-service <execution-workspace-id> start --payload-json '{...}'
|
||||
pnpm paperclipai workspace runtime-command <execution-workspace-id> run --payload-json '{...}'
|
||||
```
|
||||
|
||||
```sh
|
||||
pnpm paperclipai environment list --company-id <company-id>
|
||||
pnpm paperclipai environment capabilities --company-id <company-id>
|
||||
pnpm paperclipai environment create --company-id <company-id> --payload-json '{...}'
|
||||
pnpm paperclipai environment get <environment-id>
|
||||
pnpm paperclipai environment leases <environment-id>
|
||||
pnpm paperclipai environment lease <lease-id>
|
||||
pnpm paperclipai environment update <environment-id> --payload-json '{...}'
|
||||
pnpm paperclipai environment delete <environment-id>
|
||||
pnpm paperclipai environment probe <environment-id>
|
||||
pnpm paperclipai environment probe-config --company-id <company-id> --payload-json '{...}'
|
||||
```
|
||||
|
||||
```sh
|
||||
pnpm paperclipai project-workspace list <project-id>
|
||||
pnpm paperclipai project-workspace create <project-id> --payload-json '{...}'
|
||||
pnpm paperclipai project-workspace update <project-id> <workspace-id> --payload-json '{...}'
|
||||
pnpm paperclipai project-workspace delete <project-id> <workspace-id>
|
||||
pnpm paperclipai project-workspace runtime-service <project-id> <workspace-id> restart --payload-json '{...}'
|
||||
pnpm paperclipai project-workspace runtime-command <project-id> <workspace-id> run --payload-json '{...}'
|
||||
```
|
||||
|
||||
## Plugin Commands
|
||||
|
||||
Existing plugin lifecycle commands remain available: `plugin init`, `list`, `install`, `uninstall`, `enable`, `disable`, `inspect`, and `examples`.
|
||||
|
||||
```sh
|
||||
pnpm paperclipai plugin ui-contributions
|
||||
pnpm paperclipai plugin tools
|
||||
pnpm paperclipai plugin tool:execute --payload-json '{...}'
|
||||
pnpm paperclipai plugin health <plugin-id>
|
||||
pnpm paperclipai plugin logs <plugin-id>
|
||||
pnpm paperclipai plugin upgrade <plugin-id>
|
||||
pnpm paperclipai plugin config <plugin-id>
|
||||
pnpm paperclipai plugin config:set <plugin-id> --payload-json '{"configJson":{...}}'
|
||||
pnpm paperclipai plugin config:test <plugin-id> --payload-json '{"configJson":{...}}'
|
||||
pnpm paperclipai plugin jobs <plugin-id>
|
||||
pnpm paperclipai plugin job:runs <plugin-id> <job-id>
|
||||
pnpm paperclipai plugin job:trigger <plugin-id> <job-id> [--payload-json '{...}']
|
||||
pnpm paperclipai plugin webhook <plugin-id> <endpoint-key> [--payload-json '{...}']
|
||||
pnpm paperclipai plugin dashboard <plugin-id>
|
||||
pnpm paperclipai plugin bridge:data <plugin-id> --payload-json '{...}'
|
||||
pnpm paperclipai plugin bridge:action <plugin-id> --payload-json '{...}'
|
||||
pnpm paperclipai plugin bridge:stream <plugin-id> <channel> [--duration-ms 10000]
|
||||
pnpm paperclipai plugin data <plugin-id> <key> --payload-json '{...}'
|
||||
pnpm paperclipai plugin action <plugin-id> <key> --payload-json '{...}'
|
||||
pnpm paperclipai plugin local-folders <plugin-id> --company-id <company-id>
|
||||
pnpm paperclipai plugin local-folder:status <plugin-id> <folder-key> --company-id <company-id>
|
||||
pnpm paperclipai plugin local-folder:validate <plugin-id> <folder-key> --company-id <company-id> [--payload-json '{...}']
|
||||
pnpm paperclipai plugin local-folder:set <plugin-id> <folder-key> --company-id <company-id> --payload-json '{...}'
|
||||
```
|
||||
|
||||
Feedback traces can be fetched directly by ID when automating export workflows:
|
||||
|
||||
```sh
|
||||
pnpm paperclipai feedback trace <trace-id>
|
||||
pnpm paperclipai feedback bundle <trace-id>
|
||||
```
|
||||
|
||||
## Heartbeat Command
|
||||
|
||||
`heartbeat run` now also supports context/api-key options and uses the shared client stack:
|
||||
|
|
|
|||
1030
doc/logs/2026-05-24-cli-api-parity-e2e-log.md
Normal file
1030
doc/logs/2026-05-24-cli-api-parity-e2e-log.md
Normal file
File diff suppressed because it is too large
Load diff
3585
doc/plans/2026-05-23-cli-api-parity-openapi-reference.ts
Normal file
3585
doc/plans/2026-05-23-cli-api-parity-openapi-reference.ts
Normal file
File diff suppressed because it is too large
Load diff
623
doc/plans/2026-05-23-cli-api-parity.md
Normal file
623
doc/plans/2026-05-23-cli-api-parity.md
Normal file
|
|
@ -0,0 +1,623 @@
|
|||
# CLI API Parity PRD
|
||||
|
||||
Date: 2026-05-23
|
||||
Branch: `improvement/cli-api-parity`
|
||||
Status: PRD
|
||||
|
||||
## Summary
|
||||
|
||||
Paperclip already exposes a broad REST API, but the CLI only covers a narrow operator slice: setup/configuration, context profiles, board auth, companies import/export/delete, issues basic CRUD/comments/checkout/release, approvals, agents list/get/local CLI key export, activity, dashboard, secrets basics, plugin lifecycle basics, feedback export, and cloud sync.
|
||||
|
||||
The next CLI product slice should make the CLI a real external API entry point:
|
||||
|
||||
1. Connect interactively as a board operator or as one agent in one company.
|
||||
2. Mint, list, revoke, and use board and agent tokens intentionally.
|
||||
3. Provide single-command agent execution and prompt handoff for scripts.
|
||||
4. Add CLI coverage for API surfaces that are currently UI-only or curl-only.
|
||||
|
||||
The most important requirement is credential ergonomics. External integrations need a reliable "way in" to Paperclip:
|
||||
|
||||
- full board access via a board token approved by a user
|
||||
- individual agent access via an agent API key scoped to a specific company and agent
|
||||
- saved CLI profiles that know whether they are board or agent personas
|
||||
- non-interactive commands that can run from shell scripts without a prior wizard
|
||||
|
||||
## Existing CLI Coverage
|
||||
|
||||
Current top-level command families:
|
||||
|
||||
- Setup/runtime: `onboard`, `doctor`, `configure`, `env`, `run`, `db:backup`, `allowed-hostname`, `env-lab`, `worktree`
|
||||
- Context/auth: `context`, `auth login`, `auth logout`, `auth whoami`, `auth bootstrap-ceo`
|
||||
- Companies: `company list`, `company get`, `company export`, `company import`, `company delete`, company feedback export
|
||||
- Issues: `issue list`, `issue get`, `issue create`, `issue update`, `issue comment`, `issue checkout`, `issue release`, issue feedback export
|
||||
- Agents: `agent list`, `agent get`, `agent local-cli`
|
||||
- Approvals: `approval list/get/create/approve/reject/request-revision/resubmit/comment`
|
||||
- Activity/dashboard: `activity list`, `dashboard get`
|
||||
- Secrets: `secrets list/declarations/create/link/doctor/providers/migrate-inline-env`
|
||||
- Plugins/cloud/feedback: basic lifecycle and transfer workflows
|
||||
|
||||
Current auth behavior:
|
||||
|
||||
- `auth login` creates a CLI auth challenge, opens the board approval URL, and stores the approved board token locally.
|
||||
- `agent local-cli` creates an agent API key through board access, installs local skills, and prints `PAPERCLIP_API_URL`, `PAPERCLIP_COMPANY_ID`, `PAPERCLIP_AGENT_ID`, and `PAPERCLIP_API_KEY`.
|
||||
- Every client command can accept `--api-base`, `--api-key`, `--context`, `--profile`, `--company-id`, and `--json`.
|
||||
|
||||
Main limitation:
|
||||
|
||||
- The CLI has no explicit concept of "I am connected as board" versus "I am connected as this agent in this company". It only has a raw bearer token plus optional company context.
|
||||
|
||||
## Product Goals
|
||||
|
||||
1. Make the CLI the canonical external connection surface for scripts, local agents, and human operators.
|
||||
2. Reach near-parity with first-class REST API domains, starting with company-scoped control-plane operations.
|
||||
3. Make token creation safe and auditable: keys are named, scoped, shown once, and easy to revoke.
|
||||
4. Support both interactive and single-command flows.
|
||||
5. Preserve existing API authorization boundaries: board has operator control; agent keys remain company and agent scoped.
|
||||
|
||||
## Non-Goals
|
||||
|
||||
- Do not turn the CLI into a full TUI replacement for the board UI.
|
||||
- Do not weaken agent authorization to make script flows easier.
|
||||
- Do not store plaintext tokens in repo files.
|
||||
- Do not add project/issue privacy semantics; V1 visibility remains company-scoped.
|
||||
- Do not make a generic `curl` passthrough the primary parity story.
|
||||
|
||||
## API Location Requirements
|
||||
|
||||
The CLI must always know which Paperclip API it is operating against. This is especially important for fork/local development, where Paperclip may run on `3101+` rather than the upstream default `3100`.
|
||||
|
||||
Resolution order:
|
||||
|
||||
1. Explicit `--api-base <url>`.
|
||||
2. `PAPERCLIP_API_URL`.
|
||||
3. Selected context profile `apiBase`.
|
||||
4. Repo-local or instance config port, when available.
|
||||
5. Default `http://localhost:3100`.
|
||||
|
||||
Behavior requirements:
|
||||
|
||||
- `paperclipai connect` must show the resolved API base before any auth or mutation and allow the user to override it.
|
||||
- Non-interactive commands must accept `--api-base` and produce a clear connection error that includes the attempted URL and a health-check hint.
|
||||
- Profiles must persist `apiBase` so a board/agent persona is always tied to the API instance it was created for.
|
||||
- Commands that mint or use tokens must not silently fall back to a different API base if a stored credential is missing. They should ask interactively or fail with instructions in non-interactive mode.
|
||||
- The quick verification after `connect` should call `GET /api/health` against the selected API base.
|
||||
|
||||
## Target User Flows
|
||||
|
||||
### Interactive Connection Wizard
|
||||
|
||||
Command:
|
||||
|
||||
```sh
|
||||
paperclipai connect
|
||||
```
|
||||
|
||||
Flow:
|
||||
|
||||
1. Resolve or ask for API base.
|
||||
2. Fetch accessible companies with current board auth, or trigger `auth login`.
|
||||
3. Ask whether the user wants to connect as:
|
||||
- Board operator
|
||||
- Agent in a company
|
||||
4. If board:
|
||||
- Mint or reuse a named board token.
|
||||
- Save profile with `persona=board`, `apiBase`, `companyId`, and token env-var preference.
|
||||
5. If agent:
|
||||
- Ask for company.
|
||||
- List agents in that company.
|
||||
- Create a named agent API key for the selected agent.
|
||||
- Save profile with `persona=agent`, `companyId`, `agentId`, `agentName`, and token env-var preference.
|
||||
6. Print shell exports and a verification command.
|
||||
|
||||
Expected profile shape should evolve from today's context:
|
||||
|
||||
```json
|
||||
{
|
||||
"version": 2,
|
||||
"currentProfile": "default",
|
||||
"profiles": {
|
||||
"default": {
|
||||
"apiBase": "http://localhost:3100",
|
||||
"companyId": "company-id",
|
||||
"persona": "agent",
|
||||
"agentId": "agent-id",
|
||||
"apiKeyEnvVarName": "PAPERCLIP_API_KEY"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Board Token Flow
|
||||
|
||||
Commands:
|
||||
|
||||
```sh
|
||||
paperclipai token board create --company-id <company-id> --name "external-admin"
|
||||
paperclipai token board list
|
||||
paperclipai token board revoke <key-id>
|
||||
```
|
||||
|
||||
Requirements:
|
||||
|
||||
- Board token creation must require an authenticated board approval or an existing board token with sufficient authority.
|
||||
- Token output shows plaintext once.
|
||||
- Tokens should have names, creation time, last-used time, expiration, and revoked status.
|
||||
- A company ID in the profile selects the operating company, but full board tokens retain the server's board authorization model.
|
||||
- If product wants company-limited board keys, add that as an explicit server-side scope rather than relying on client context.
|
||||
|
||||
Current API support:
|
||||
|
||||
- Existing challenge flow supports browser-approved board token minting via `/api/cli-auth/challenges`.
|
||||
- Existing revocation only covers the current CLI key via `/api/cli-auth/revoke-current`.
|
||||
|
||||
API gap:
|
||||
|
||||
- There is no first-class board API key list/create/revoke endpoint for named external tokens. Add endpoints such as:
|
||||
- `GET /api/board-api-keys`
|
||||
- `POST /api/board-api-keys`
|
||||
- `DELETE /api/board-api-keys/:keyId`
|
||||
|
||||
### Agent Token Flow
|
||||
|
||||
Commands:
|
||||
|
||||
```sh
|
||||
paperclipai token agent create --company-id <company-id> --agent <agent-id-or-name> --name "external-worker"
|
||||
paperclipai token agent list --company-id <company-id> --agent <agent-id-or-name>
|
||||
paperclipai token agent revoke --agent <agent-id-or-name> <key-id>
|
||||
```
|
||||
|
||||
Requirements:
|
||||
|
||||
- Requires board access to create/list/revoke long-lived agent keys.
|
||||
- Agent selector accepts UUID, url key, or unambiguous name within company.
|
||||
- Output includes `agentId`, `companyId`, key id, key name, and plaintext token once.
|
||||
- Agent keys remain scoped to one agent and one company, matching `agent_api_keys`.
|
||||
|
||||
Current API support:
|
||||
|
||||
- `GET /api/agents/:id/keys`
|
||||
- `POST /api/agents/:id/keys`
|
||||
- `DELETE /api/agents/:id/keys/:keyId`
|
||||
|
||||
CLI gap:
|
||||
|
||||
- `agent local-cli` can create a key, but it is bundled with skill installation and local CLI setup.
|
||||
- There is no generic token command for list/revoke/create.
|
||||
|
||||
### Single-Command Prompt Handoff
|
||||
|
||||
Required user-facing shape:
|
||||
|
||||
```sh
|
||||
paperclipai agent-prompt <agent-name-or-id> <agent-api-key> "Prompt here"
|
||||
```
|
||||
|
||||
Recommended safer variants:
|
||||
|
||||
```sh
|
||||
paperclipai agent prompt --agent <agent-name-or-id> --api-key-env PAPERCLIP_API_KEY "Prompt here"
|
||||
paperclipai agent prompt --profile my-agent "Prompt here"
|
||||
paperclipai board prompt --agent <agent-name-or-id> "Prompt here"
|
||||
```
|
||||
|
||||
Behavior:
|
||||
|
||||
- With an agent key:
|
||||
- Verify identity with `GET /api/agents/me`.
|
||||
- Resolve the provided agent name/id against the authenticated agent. If they do not match, fail clearly.
|
||||
- Create a new issue assigned to that agent, or append to a specified issue when `--issue` is passed.
|
||||
- Optionally invoke/wake the agent when the authenticated agent is allowed to do so.
|
||||
- With board auth:
|
||||
- Resolve company and target agent.
|
||||
- Create a board-authored issue assigned to that agent.
|
||||
- Wake/invoke the agent when requested.
|
||||
|
||||
Open decision:
|
||||
|
||||
- Default prompt target should be `issue create + assign + wake`, because Paperclip's communication model is tasks/comments, not chat.
|
||||
- A direct "send message" mode can be `--issue <id>` and should add an issue comment plus optional wake.
|
||||
|
||||
## Missing CLI Coverage By API Domain
|
||||
|
||||
Priority is based on external API usefulness, not raw endpoint count.
|
||||
|
||||
OpenAPI source audit:
|
||||
|
||||
- Source branch: `feature/openapi-spec`
|
||||
- Source file: `server/src/openapi.ts`
|
||||
- Local snapshot for this PRD: `doc/plans/2026-05-23-cli-api-parity-openapi-reference.ts`
|
||||
- Extracted operations: 307
|
||||
- Validation context: that branch includes `server/src/__tests__/openapi-spec.test.ts`, which asserts the OpenAPI document covers mounted server routes exactly.
|
||||
- Snapshot purpose: keep the full operation registrations, request schemas, auth annotations, response status overrides, and tag/summary values next to the CLI parity plan even before the OpenAPI branch is merged.
|
||||
|
||||
Additional gaps made explicit by the OpenAPI branch:
|
||||
|
||||
- Public/bootstrap surfaces need CLI decisions, not just board UI paths: `GET /api/openapi.json`, board-claim get/claim, invite onboarding docs, skill docs, join key claim, and CLI auth challenge status/approve/cancel.
|
||||
- User/profile and admin surfaces were under-specified in the first PRD: auth session/profile, company user profile lookup, admin user list/promote/demote/company access.
|
||||
- Legacy compatibility routes still exist and need an explicit stance: `/api/companies/:companyId/export`, `/api/companies/import/preview`, `/api/companies/import`, `/api/companies/issues`, and bare `GET /api/issues`.
|
||||
- Agent operations need several extra CLI items: skills list/sync, `claude-login`, scheduler heartbeat visibility, org SVG/PNG export, adapter UI parser, and agent approval.
|
||||
- Cost/budget coverage must reconcile the OpenAPI branch and current main. The OpenAPI branch lists `GET /api/companies/:companyId/cost-events`; current main exposes `POST /api/companies/:companyId/cost-events` plus additional summary and finance read endpoints. Treat this as a spec/code drift item before implementation.
|
||||
- The current main branch includes secrets provider-config and remote-import routes beyond the OpenAPI branch list. Keep them in scope for CLI parity even though they are absent from that branch's generated spec.
|
||||
|
||||
### P0: Connection, Tokens, and Identity
|
||||
|
||||
Missing or incomplete CLI surfaces:
|
||||
|
||||
- Board token lifecycle:
|
||||
- `GET /api/cli-auth/me` is covered by `auth whoami`.
|
||||
- `POST /api/cli-auth/revoke-current` is covered by `auth logout`.
|
||||
- Missing named board key list/create/revoke API and CLI.
|
||||
- Agent identity:
|
||||
- Missing `agent me` for `GET /api/agents/me`.
|
||||
- Missing `agent inbox` for `GET /api/agents/me/inbox-lite` and `GET /api/agents/me/inbox/mine`.
|
||||
- Agent token lifecycle:
|
||||
- Missing generic CLI for `GET/POST/DELETE /api/agents/:id/keys`.
|
||||
- Connect wizard:
|
||||
- No CLI command combines company selection, persona selection, token minting, profile saving, and verification.
|
||||
- Public/bootstrap auth helpers:
|
||||
- `GET /api/board-claim/:token`
|
||||
- `POST /api/board-claim/:token/claim`
|
||||
- `POST /api/cli-auth/challenges`
|
||||
- `GET /api/cli-auth/challenges/:id`
|
||||
- `POST /api/cli-auth/challenges/:id/approve`
|
||||
- `POST /api/cli-auth/challenges/:id/cancel`
|
||||
- `POST /api/join-requests/:requestId/claim-api-key`
|
||||
|
||||
### P0: Prompt, Wake, and Run Control
|
||||
|
||||
Missing CLI surfaces:
|
||||
|
||||
- `POST /api/agents/:id/wakeup`
|
||||
- `POST /api/agents/:id/heartbeat/invoke` is partially covered by `heartbeat run`, but not integrated with prompt handoff.
|
||||
- `GET /api/companies/:companyId/heartbeat-runs`
|
||||
- `GET /api/companies/:companyId/live-runs`
|
||||
- `GET /api/heartbeat-runs/:runId`
|
||||
- `POST /api/heartbeat-runs/:runId/cancel`
|
||||
- `GET /api/heartbeat-runs/:runId/events`
|
||||
- `GET /api/heartbeat-runs/:runId/log`
|
||||
- `GET /api/issues/:issueId/live-runs`
|
||||
- `GET /api/issues/:issueId/active-run`
|
||||
- `GET /api/issues/:id/runs`
|
||||
- `GET /api/heartbeat-runs/:runId/issues`
|
||||
- `POST /api/heartbeat-runs/:runId/watchdog-decisions`
|
||||
- `GET /api/heartbeat-runs/:runId/workspace-operations`
|
||||
- `GET /api/workspace-operations/:operationId/log`
|
||||
|
||||
CLI commands to add:
|
||||
|
||||
```sh
|
||||
paperclipai agent wake <agent>
|
||||
paperclipai run list --company-id <company-id>
|
||||
paperclipai run get <run-id>
|
||||
paperclipai run log <run-id>
|
||||
paperclipai run cancel <run-id>
|
||||
paperclipai issue runs <issue-id>
|
||||
```
|
||||
|
||||
### P1: Projects and Goals
|
||||
|
||||
Missing CLI surfaces:
|
||||
|
||||
- `GET /api/companies/:companyId/projects`
|
||||
- `POST /api/companies/:companyId/projects`
|
||||
- `GET /api/projects/:id`
|
||||
- `PATCH /api/projects/:id`
|
||||
- `DELETE /api/projects/:id`
|
||||
- `GET /api/companies/:companyId/goals`
|
||||
- `POST /api/companies/:companyId/goals`
|
||||
- `GET /api/goals/:id`
|
||||
- `PATCH /api/goals/:id`
|
||||
- `DELETE /api/goals/:id`
|
||||
|
||||
Commands:
|
||||
|
||||
```sh
|
||||
paperclipai project list|get|create|update|delete
|
||||
paperclipai goal list|get|create|update|delete
|
||||
```
|
||||
|
||||
### P1: Issue Parity Beyond Basic CRUD
|
||||
|
||||
Missing CLI surfaces:
|
||||
|
||||
- Issue counts/search/labels:
|
||||
- `GET /api/issues`
|
||||
- `GET /api/companies/:companyId/search`
|
||||
- `GET /api/companies/:companyId/issues/count`
|
||||
- `GET /api/companies/issues`
|
||||
- `GET/POST /api/companies/:companyId/labels`
|
||||
- `DELETE /api/labels/:labelId`
|
||||
- Child issues:
|
||||
- `POST /api/issues/:id/children`
|
||||
- Force-release/admin recovery:
|
||||
- `POST /api/issues/:id/admin/force-release`
|
||||
- Documents:
|
||||
- `GET /api/issues/:id/documents`
|
||||
- `GET/PUT/DELETE /api/issues/:id/documents/:key`
|
||||
- lock/unlock/revisions/restore endpoints
|
||||
- Work products:
|
||||
- `GET/POST /api/issues/:id/work-products`
|
||||
- `PATCH/DELETE /api/work-products/:id`
|
||||
- Interactions:
|
||||
- `GET/POST /api/issues/:id/interactions`
|
||||
- accept/reject/respond/cancel endpoints
|
||||
- Read/archive state:
|
||||
- `POST/DELETE /api/issues/:id/read`
|
||||
- `POST/DELETE /api/issues/:id/inbox-archive`
|
||||
- Attachments:
|
||||
- `GET /api/issues/:id/attachments`
|
||||
- `POST /api/companies/:companyId/issues/:issueId/attachments`
|
||||
- `GET /api/attachments/:attachmentId/content`
|
||||
- `DELETE /api/attachments/:attachmentId`
|
||||
- Comment-specific access:
|
||||
- `GET /api/issues/:id/comments/:commentId`
|
||||
- `DELETE /api/issues/:id/comments/:commentId`
|
||||
- Recovery/tree control:
|
||||
- `GET /api/issues/:id/recovery-actions`
|
||||
- `POST /api/issues/:id/recovery-actions/resolve`
|
||||
- tree hold and preview endpoints
|
||||
|
||||
Commands:
|
||||
|
||||
```sh
|
||||
paperclipai issue child create <issue-id>
|
||||
paperclipai issue document list|get|put|delete|lock|unlock|revisions|restore
|
||||
paperclipai issue work-product list|create|update|delete
|
||||
paperclipai issue interaction list|create|accept|reject|respond|cancel
|
||||
paperclipai issue attachment list|upload|download|delete
|
||||
paperclipai issue force-release <issue-id>
|
||||
paperclipai issue label list|create|delete
|
||||
paperclipai issue read|unread|archive|unarchive
|
||||
```
|
||||
|
||||
### P1: Agent Lifecycle and Configuration
|
||||
|
||||
Missing CLI surfaces:
|
||||
|
||||
- Create/update/pause/resume/approve/terminate/delete:
|
||||
- `POST /api/companies/:companyId/agents`
|
||||
- `PATCH /api/agents/:id`
|
||||
- `POST /api/agents/:id/pause`
|
||||
- `POST /api/agents/:id/resume`
|
||||
- `POST /api/agents/:id/approve`
|
||||
- `POST /api/agents/:id/terminate`
|
||||
- `DELETE /api/agents/:id`
|
||||
- Org and config:
|
||||
- `GET /api/companies/:companyId/org`
|
||||
- `GET /api/companies/:companyId/org.svg`
|
||||
- `GET /api/companies/:companyId/org.png`
|
||||
- `GET /api/companies/:companyId/agent-configurations`
|
||||
- `GET /api/agents/:id/configuration`
|
||||
- config revision list/get/rollback
|
||||
- runtime state and task sessions
|
||||
- Instructions:
|
||||
- instructions bundle, path, and file endpoints
|
||||
- Adapter support:
|
||||
- models, model profiles, detect model, test environment
|
||||
- Skills and local-auth helpers:
|
||||
- `GET /api/agents/:id/skills`
|
||||
- `POST /api/agents/:id/skills/sync`
|
||||
- `POST /api/agents/:id/claude-login`
|
||||
- Scheduler visibility:
|
||||
- `GET /api/instance/scheduler-heartbeats`
|
||||
|
||||
Commands:
|
||||
|
||||
```sh
|
||||
paperclipai agent create|update|pause|resume|approve|terminate|delete
|
||||
paperclipai agent org
|
||||
paperclipai agent config get|revisions|rollback
|
||||
paperclipai agent instructions get|set|file
|
||||
paperclipai adapter list|models|profiles|detect|test|install|enable|disable|reload
|
||||
```
|
||||
|
||||
### P1: Costs, Budgets, and Finance
|
||||
|
||||
Missing CLI surfaces:
|
||||
|
||||
- `POST /api/companies/:companyId/cost-events`
|
||||
- `GET /api/companies/:companyId/cost-events` from the OpenAPI branch needs reconciliation with main before implementation.
|
||||
- `POST /api/companies/:companyId/finance-events`
|
||||
- cost summaries by agent/model/provider/biller/project
|
||||
- finance summaries by biller/kind and finance events
|
||||
- quota windows and window spend
|
||||
- budget overview, budget policies, budget incident resolution
|
||||
- `PATCH /api/companies/:companyId/budgets`
|
||||
- `PATCH /api/agents/:agentId/budgets`
|
||||
- `GET /api/issues/:id/cost-summary`
|
||||
|
||||
Commands:
|
||||
|
||||
```sh
|
||||
paperclipai cost summary|by-agent|by-project|by-provider|issue
|
||||
paperclipai cost event create
|
||||
paperclipai finance event create|list|summary
|
||||
paperclipai budget overview|set-company|set-agent|policy-create|incident-resolve
|
||||
```
|
||||
|
||||
### P1: Access, Invites, and Memberships
|
||||
|
||||
Missing CLI surfaces:
|
||||
|
||||
- Invite creation/list/revoke and onboarding manifests
|
||||
- Join request list/approve/reject/claim API key
|
||||
- Company members and user directory
|
||||
- Member role/grant/permission updates
|
||||
- Admin users and company access management
|
||||
- Board claim endpoints
|
||||
- Skills index/invite onboarding docs
|
||||
- Auth/profile endpoints:
|
||||
- `GET /api/auth/get-session`
|
||||
- `GET /api/auth/profile`
|
||||
- `PATCH /api/auth/profile`
|
||||
- `GET /api/companies/:companyId/users/:userSlug/profile`
|
||||
|
||||
Commands:
|
||||
|
||||
```sh
|
||||
paperclipai invite create|list|revoke|show|onboarding
|
||||
paperclipai join list|approve|reject|claim-key
|
||||
paperclipai member list|update|archive|permissions
|
||||
paperclipai admin user list|promote|demote|company-access
|
||||
```
|
||||
|
||||
### P2: Routines, Workspaces, Environments
|
||||
|
||||
Missing CLI surfaces:
|
||||
|
||||
- Routines API:
|
||||
- list/create/get/update/revisions/restore/runs/run/triggers/rotate-secret/public fire
|
||||
- Environments API:
|
||||
- list/capabilities/create/get/update/delete/probe/leases
|
||||
- Execution and project workspaces:
|
||||
- execution workspace list/get/patch/close readiness/operations/runtime actions
|
||||
- project workspace list/create/update/delete/runtime actions
|
||||
|
||||
Commands:
|
||||
|
||||
```sh
|
||||
paperclipai routine list|create|get|update|run|runs|trigger|revision
|
||||
paperclipai environment list|create|get|update|delete|probe|leases
|
||||
paperclipai workspace list|get|update|operations|runtime
|
||||
paperclipai project workspace list|create|update|delete|runtime
|
||||
```
|
||||
|
||||
### P2: Instance, Sidebar, Assets, Profile, and Miscellaneous
|
||||
|
||||
Missing CLI surfaces:
|
||||
|
||||
- Instance settings general/experimental and database backups API
|
||||
- Sidebar preferences and sidebar badges
|
||||
- Asset image/logo upload and asset content download
|
||||
- User profile read/update and company user profile lookup
|
||||
- LLM prompt docs endpoints
|
||||
- Public API documentation endpoint:
|
||||
- `GET /api/openapi.json`
|
||||
- Plugin deeper surfaces:
|
||||
- tools list/execute
|
||||
- UI contributions
|
||||
- plugin config/test
|
||||
- plugin health/logs/jobs/webhooks/local folders/dashboard
|
||||
- Company create/update/archive/branding/stats are missing or partial in CLI.
|
||||
- Company portability compatibility routes:
|
||||
- `POST /api/companies/:companyId/export`
|
||||
- `POST /api/companies/import/preview`
|
||||
- `POST /api/companies/import`
|
||||
- `POST /api/companies/:companyId/exports`
|
||||
- `POST /api/companies/:companyId/exports/preview`
|
||||
- `POST /api/companies/:companyId/imports/preview`
|
||||
- `POST /api/companies/:companyId/imports/apply`
|
||||
|
||||
## Command Taxonomy
|
||||
|
||||
Recommended command hierarchy:
|
||||
|
||||
```text
|
||||
paperclipai connect
|
||||
paperclipai token board|agent create|list|revoke
|
||||
paperclipai whoami
|
||||
paperclipai prompt ...
|
||||
paperclipai board ...
|
||||
paperclipai agent ...
|
||||
paperclipai issue ...
|
||||
paperclipai project ...
|
||||
paperclipai goal ...
|
||||
paperclipai run ...
|
||||
paperclipai cost ...
|
||||
paperclipai budget ...
|
||||
paperclipai routine ...
|
||||
paperclipai environment ...
|
||||
paperclipai workspace ...
|
||||
paperclipai invite ...
|
||||
paperclipai member ...
|
||||
paperclipai plugin ...
|
||||
paperclipai instance ...
|
||||
```
|
||||
|
||||
Alias policy:
|
||||
|
||||
- Keep existing commands working.
|
||||
- Add aliases only for high-frequency flows, for example `paperclipai ask` as an alias for `paperclipai prompt`.
|
||||
|
||||
## Authorization Rules
|
||||
|
||||
- Board commands should use board tokens and fail clearly when an agent key is supplied.
|
||||
- Agent commands should prefer `GET /api/agents/me` to establish identity instead of trusting CLI flags.
|
||||
- `--company-id` is a context selector, not an authorization bypass.
|
||||
- Token creation and revocation must log activity through existing server routes.
|
||||
- Commands that mutate company state should print the actor type and target company in `--json` output when practical.
|
||||
|
||||
## Testing Rules
|
||||
|
||||
Automated tests should prefer mocked HTTP/server fixtures where possible. Live/API verification is allowed, but it must be isolated:
|
||||
|
||||
- Live tests must create a new disposable company specifically for that test run.
|
||||
- Live tests must never use an existing company from the operator's profile, local instance, or shared environment.
|
||||
- The disposable company name should include a clear prefix such as `CLI Parity Test` plus a timestamp or random suffix.
|
||||
- All agents, projects, issues, tokens, budgets, secrets, routines, workspaces, and other test data must be created inside the disposable company.
|
||||
- Agent API keys used in tests must be minted only for agents created inside the disposable company.
|
||||
- Board token tests must use a test-specific key name and revoke the key during cleanup when the API supports it.
|
||||
- Cleanup should archive or delete the disposable company when the server permits it. If deletion is disabled, the test must leave the company clearly named as disposable and report its ID.
|
||||
- Commands must provide a `--yes` or non-interactive path for test setup so CI and local verification do not depend on manual prompts.
|
||||
- Destructive tests must require an explicit test opt-in such as an env var or a dedicated test command; normal unit tests must not mutate a real running Paperclip instance.
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
### Phase 1: Credential and Persona Foundation
|
||||
|
||||
- Extend CLI context to version 2 with `persona`, `agentId`, and token metadata.
|
||||
- Add `connect` wizard.
|
||||
- Add `token agent create/list/revoke`.
|
||||
- Add `agent me`.
|
||||
- Add `agent prompt` and `prompt` using issue create/comment plus optional wake.
|
||||
- Harden API base resolution and connection diagnostics.
|
||||
- Add tests around context migration, explicit token precedence, and persona mismatch failures.
|
||||
- Add live-test helpers that always create a disposable company before exercising real API mutations.
|
||||
|
||||
### Phase 2: Board Token Management
|
||||
|
||||
- Add server endpoints for named board API key lifecycle if product approves direct token management.
|
||||
- Add CLI `token board create/list/revoke`.
|
||||
- Keep browser approval as the default interactive path.
|
||||
- Add expiration and naming options.
|
||||
|
||||
### Phase 3: Core API Parity
|
||||
|
||||
- Add projects/goals.
|
||||
- Add agent lifecycle/config/instructions.
|
||||
- Add run/heartbeat inspection and cancellation.
|
||||
- Add issue documents/work products/interactions/attachments/labels.
|
||||
|
||||
### Phase 4: Operations Parity
|
||||
|
||||
- Add costs/budgets/finance.
|
||||
- Add access/invites/members/admin users.
|
||||
- Add routines/environments/workspaces.
|
||||
- Expand plugin and instance settings surfaces.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- A new user can run `paperclipai connect`, confirm or override the API base, select board or agent, and get a saved working profile tied to that API base.
|
||||
- A board operator can mint an agent key for a selected agent in a selected company without using `agent local-cli`.
|
||||
- A script can run a one-liner equivalent to:
|
||||
|
||||
```sh
|
||||
paperclipai agent-prompt AgentName "$AGENT_API_KEY" "Prompt here"
|
||||
```
|
||||
|
||||
- The one-liner creates or updates Paperclip work, does not require a browser, and fails with a clear company/agent mismatch error when the token does not belong to the requested agent.
|
||||
- Live/API verification creates and uses a disposable company only; no existing company is used for testing.
|
||||
- CLI docs list which API route families are covered and which remain UI-only.
|
||||
- Token creation, revocation, and prompt handoff have tests for board and agent auth paths.
|
||||
|
||||
## Risks
|
||||
|
||||
- Board token lifecycle endpoints may create a broader security surface if expiration, revocation, and audit logging are incomplete.
|
||||
- A raw prompt command can look like chat; the implementation must keep prompts attached to issues/comments.
|
||||
- Agent name selectors can be ambiguous; require exact UUID/urlKey or fail on duplicate names.
|
||||
- CLI parity can sprawl. Ship by user workflow, not by endpoint count alone.
|
||||
|
||||
## OpenAPI Reference
|
||||
|
||||
The full OpenAPI source snapshot is kept next to this PRD at `doc/plans/2026-05-23-cli-api-parity-openapi-reference.ts`. Use that file when request body schemas, auth levels, response statuses, tags, operation summaries, or the complete endpoint inventory are needed.
|
||||
Loading…
Add table
Add a link
Reference in a new issue