@loftyai/flow-cli
v0.3.0
Published
CLI for the PapagoAI Flow API - manage agents, preview chat, conversations, run-chain logs, and more
Readme
flow-cli
CLI for the PapagoAI Flow workspace open API — manage agent projects, edit agent config, preview-chat, publish, and inspect conversations and run-chain execution traces.
The full OpenAPI contract ships inside the package as contract/workspace-api.json (find it under $(npm root -g)/@loftyai/flow-cli/ after a global install) — useful when you need exact request/response schemas beyond what --help shows.
Install
Prerequisite: Node.js ≥ 18.
npm i -g @loftyai/flow-cliThe package ships a single bundled file plus an AI skill (skills/flow-cli/) that coding agents such as Claude Code can load to drive the CLI.
Authentication
flow-cli authenticates with a workspace API key (a key id + secret pair).
Getting an API key
- Log in to the Flow web console.
- Open Workspace Settings → API Keys (visible to workspace admins/owners only).
- Click Create API Key. You get a key id (
fwk_...) and a secret. - Copy the secret immediately — it is shown only once. If lost, revoke the key and create a new one.
The key is scoped to its workspace: it can only see and modify that workspace's projects and resources.
Method 1: Save to local config (recommended for humans)
flow-cli auth set <keyId> <secret>Credentials are stored in ~/.config/flow/config.json. Multiple keys (e.g. one per workspace) are stored as profiles — save with -p team-b auth set ... and select per command with -p/--profile team-b.
Method 2: Environment variables (CI/CD & automation — highest priority)
export FLOW_API_KEY_ID="fwk_..."
export FLOW_API_KEY_SECRET="..."Env vars override the config file per field.
Verify Authentication
flow-cli auth status # where each credential comes from + target endpoint
flow-cli auth test # verify with a real API callEnvironment Variables
| Variable | Required | Description |
|---|---|---|
| FLOW_API_KEY_ID | No | Workspace API key id (overrides the config file) |
| FLOW_API_KEY_SECRET | No | Workspace API key secret (overrides the config file) |
| FLOW_BASE_URL | No | Override the API base URL (default: production). For staging/private deployments; must match the environment that issued your key |
| FLOW_CONFIG | No | Alternative config file path (default: ~/.config/flow/config.json) |
A profile can also pin its own baseUrl in the config file:
{
"defaultProfile": "default",
"profiles": {
"default": { "keyId": "fwk_aaa", "secret": "..." },
"stage": { "baseUrl": "https://api.s.papagoai.com/core/api/open/v1", "keyId": "fwk_bbb", "secret": "..." }
}
}Usage
# View all available resources
flow-cli --help
# Projects (agents)
flow-cli project list --mode MultiAgentV3
flow-cli project get <projectId> --config
flow-cli project create --name "My Agent" --mode SingleAgent
flow-cli project update <projectId> --name "New Name" --streaming true \
--spend-limit '{"enabled":true,"period":"DAILY","costLimit":5}'
flow-cli project config get <projectId>
flow-cli project config patch <projectId> --body '{"agents":[{"prompt":"..."}]}'
flow-cli project chat <projectId> "hello" --var lead_first_name=Bob
flow-cli project chat <projectId> "and then?" --conversation <conversationId>
flow-cli project publish <projectId>
# Conversations & execution traces
flow-cli conversation list <projectId> --begin 2026-07-01T00:00:00Z --end 2026-07-20T00:00:00Z
flow-cli conversation messages <conversationId>
flow-cli run-chain <messageId> # per-node trace incl. each tool call's input/output
flow-cli run-chain <messageId> <runChainId> # one node's raw provider payload
# Daily token usage & model cost (USD); range at most 10 days, data from 2026-06-25 (UTC)
flow-cli usage <projectId> --start 2026-07-28 --end 2026-07-31
# Read-only catalogs (the ids they list are what a project config references)
flow-cli model list # incl. each model's supported reply formats + max output tokens
flow-cli skill list
flow-cli kb list
flow-cli workflow list
flow-cli plugin-group list
flow-cli plugin list <groupId>Pagination is 0-based, mirroring the API. The client retries 429/5xx with backoff — except chat, which is never retried (a turn is not idempotent).
Project Lifecycle
create (Draft skeleton) → config patch (edits land in a DRAFT) → chat (try the draft) → publish (go live)Config edits are not live until project publish. project chat always runs the draft — the API twin of the web preview panel — so a config patch can be verified before publishing. publish returns an error when the current version is already published (nothing new to release).
| Mode | Description | Notes |
|---|---|---|
| SingleAgent | One agent, optional voice via media stream | |
| VoiceAgent | Realtime voice agent | No welcomeMessage; chat is rejected (voice-only preview) |
| SingleBatchAgent | Batch-processing agent | |
| MultiAgentV3 | Multi-agent graph (classifier/taskforce/agents + edges) | agents bucket is a full replace — see below |
Config Write Semantics
Config is written bucket-by-bucket. Top-level buckets: name, featureSettings, welcomeMessage, memory, voiceCfg, agents, edges, variables.
config patch— a present bucket replaces that bucket; absent buckets are untouched.config put— same, except absent list buckets (agents/edges/variables) are cleared. Only for deliberate full replaces.
Safe agents writes
The server treats a present agents bucket as a full replace: on multi-agent projects, any agent you omit is deleted. config patch protects you — it reads the current config first and re-sends every unreferenced existing agent as a bare {agentId} (preserved verbatim by the server). Pass --no-merge to send your payload as-is. Two server-side guarantees on top: single-agent projects pin the bucket to the sole agent (exactly one element; an omitted agentId binds to it, a mismatched one is rejected), and the startV2 entry node of a multi-agent graph is always preserved.
Model config
model is selected by modelId (a model list row id) — model/provider names are derived server-side and caller-sent values are ignored. Validated at write time (explicit 400s):
| Rule | Detail |
|---|---|
| replyFormat ∈ model's outputFormat | text / json_object / json_schema, per model — shown by model list. Also enforced when switching models while keeping a stored format: send replyFormat together with modelId |
| maxOutputToken ≤ model's maxCompletionTokens | The web silently clamps; the API rejects |
| json_schema requires jsonSchema | {name, description, schema, strict} in the same write |
| temperature 0–2, topP 0–1 | -1 unsets either |
flow-cli project config patch <id> --body '{"agents":[{"model":{
"modelId":"459","replyFormat":"json_schema",
"jsonSchema":{"name":"charge","description":"Extract a charge","strict":false,
"schema":"{\"type\":\"object\",\"properties\":{\"serviceType\":{\"type\":\"string\"}},\"required\":[\"serviceType\"],\"additionalProperties\":false}"}
}}]}'Welcome message & variables
welcomeMessage.variables is derived by the server from the {{placeholders}} in content — every referenced name must be a defined project variable (existing, or written in the same request), otherwise the write is rejected.
Metadata updates (project update)
Omitted flags keep current values. aiAssistSettings is merge-patched per key: --spend-limit null removes the limit without touching streaming_mode; --spend-limit '{"enabled":true,...}' requires costLimit > 0. --tags takes tag ids and replaces the whole tag set.
Chat (Preview)
flow-cli project chat <projectId> "hello" --var lead_first_name=Bob
flow-cli project chat <projectId> "and then?" --conversation <conversationId>Conversation context lives server-side — continuing needs only the returned conversationId, never the history. Reply fields (text output renders all of them losslessly; --json for raw values):
| Field | Content |
|---|---|
| content | Reply text (single-style agents) |
| trace | Per-node reply list (multi-agent graphs): agent name + content |
| contents | Multimodal reply items |
| files | Generated files (name + URL) |
| toolCalls / toolOutputs | Function-tool handshake — execute the calls, then send the next turn with both echoed back |
| workflowMessages | Workflow progress messages |
| responseMetadata | model_name, finish_reason, token_usage |
| conversationId / messageId | Continuation key / trace key (run-chain <messageId>) |
VoiceAgent projects are rejected (voice-only preview). Chat is never retried by the client — on a timeout, check conversation messages before resending.
Debugging with run-chain
flow-cli run-chain <messageId> returns the execution trace: one entry per node (type, title, tokens) with each node's input/output — for tool nodes that is the tool call's parameters and return value. flow-cli run-chain <messageId> <runChainId> fetches one node's raw provider payload. The messageId comes from a chat reply or conversation messages.
List Filters
| Command | Flags |
|---|---|
| project list | --mode <SingleAgent\|VoiceAgent\|SingleBatchAgent\|MultiAgentV3>, --page, --size |
| project get | --config (include the draft configuration) |
| conversation list | --begin <iso> (inclusive), --end <iso> (exclusive) — defaults to the last 7 days; --page, --size |
| model list | --all (include disabled models) |
| skill list | --name, --page, --size |
| kb list | --search, --page, --size |
| workflow list | --name, --status, --page, --size |
| plugin-group list | --name, --type, --status, --page, --size |
Resources
| Resource | Description |
|---|---|
| project | Agent project lifecycle: list, get, create, update, config get/patch/put, chat, publish |
| conversation | Conversation history: list <projectId>, messages <conversationId> |
| run-chain | Execution trace of a message, incl. tool call inputs/outputs |
| model | Models available to the workspace (reply formats, token limits) |
| skill | Skill catalog (read-only) |
| kb | Knowledge base catalog (read-only) |
| workflow | Workflow catalog (read-only) |
| plugin-group / plugin | Plugin catalogs (read-only) |
| auth | Credential management (see below) |
Run flow-cli <resource> --help for detailed commands and options.
Auth Commands
| Command | Description |
|---|---|
| flow-cli auth set <keyId> <secret> | Save an API key to the local config file |
| flow-cli auth show | Display the current key (secret masked) |
| flow-cli auth show --raw | Display the full secret |
| flow-cli auth remove | Delete the profile's credentials |
| flow-cli auth status | Show where credentials come from and the target endpoint |
| flow-cli auth test | Verify auth by making a real API call |
Update Notifications
flow-cli checks for new versions in the background (at most once every 24 hours). If a newer version is available, a one-line upgrade hint is printed to stderr:
Update available: 0.2.0 → 0.3.0
Run: npm install -g @loftyai/flow-cliGlobal Flags
All commands support:
| Flag | Description |
|---|---|
| --json | Output as JSON |
| --format <type> | Output format: text, json, yaml |
| -p, --profile <name> | Credential profile from the config file |
| --no-color | Disable colored output |
| --no-header | Omit table headers |
Output Format
The default text output is field-complete: every field is rendered, but long values are truncated for readability (a hint is shown when that happens). For full, untruncated content use --json, which returns a standardized envelope:
{ "ok": true, "data": { ... }, "meta": { "total": 42 } }On error: { "ok": false, "error": { "message": "...", "status": 401 } } — and on the default text format, a one-line message on stderr.
Exit codes: 0 success, 1 API error, 2 usage error.
Boolean Flags
Flags shown as --flag <bool> in --help require an explicit true or false value:
flow-cli project update <id> --streaming true
flow-cli project update <id> --streaming falseOmitting the flag keeps the current value. Boolean flags do not use --no-flag syntax (the real toggles --no-merge, --no-color, --no-header are the exceptions).
Development
npm install
npm run dev -- <args> # run from source (tsx)
npm test # vitest
npm run typecheck
npm run build # esbuild single-file bundle → dist/flow-cli.js
npm link # global `flow-cli` for developmentPublish (maintainers)
Releases go to the public npm registry under the @loftyai scope. CI publishes automatically on a vX.Y.Z tag; to release, bump the version and push the tag:
npm version patch|minor|major
git push --follow-tagsprepublishOnly runs typecheck + tests + build, so a broken bundle can't ship.
