Skip to content
Select themeSelect language

Commands API (CIP)

The Commands API (/api/commands) is the client-facing contract of SupaCloud’s Command-Interaction-Protocol (CIP). It exposes the same typed command set the web terminal and the Telegram/Discord bots use, so a new surface — a bot, a CLI, a mobile or voice client — drives SupaCloud commands without re-implementing parsing, option lists or validation. The registry, option providers and the resolve state machine all live once in the server (server/src/services/command_registry/); this API is a thin contract over them (ADR 0049 §2.2).

Method Path Purpose
GET /api/commands List the command descriptors available to the caller.
GET /api/commands/options/{provider_id}?q= Resolve a named option provider’s choices, filtered by q.
POST /api/commands/{word}/resolve Step the resolver: ready | need_param | invalid.
POST /api/commands/{word}/invoke Resolve and, when ready, return the rendered reply.

Returns the array of command descriptors the caller can run. Each descriptor carries:

  • word and aliases — the canonical word plus alternates (e.g. ffollow).
  • category — the discovery bucket (control, tasks, resources, admin).
  • description_i18n — an i18n key you resolve to your own locale.
  • scopetask | project | workspace | global.
  • surfaces — where the command is offered (always includes api here).
  • params — the ordered, typed parameters (name, description_i18n, required, kind, options).

A parameter’s kind is one of enum, ref (with a ref_kind such as task/project/ model), free_text, int_range (with min/max) or attachment. Its options is static (a closed value list), dynamic (resolved by a named provider) or none.

Resolves the live choice list for a dynamic parameter against your tenancy. Provider ids include recent_tasks, models, efforts, projects and agents. The optional q query is a case-insensitive substring filter (empty = the full, capped list). An unregistered provider id returns 404.

Each Choice is { value, label, hint?, glyph? }value is the wire value you bind into an invocation; label is the display text.

Body: { "args": { "<param>": "<value>", … } } (omit args for a bare invocation). The response status is one of:

  • ready — every required param is bound and valid; input is the resolved { word, args } invocation.
  • need_param — a required param is still missing; the body carries param, description_i18n, free_text (whether to also offer a free-text entry) and the enumerated options.
  • invalid — a supplied value failed validation; the body carries param, value and a render-ready hint.

Drive a picker by calling resolve after each captured value until it returns ready.

Same body as resolve. When the invocation is under-specified or invalid, invoke returns the same need_param / invalid guidance (never a half-run command). When it is fully resolved, it returns status: "executed" with the resolved invocation and a rendered plan:

{
"status": "executed",
"invocation": { "word": "status", "args": {} },
"plan": {
"text": "Resolved /status",
"html": false,
"media": [],
"affordances": [
{ "label": "/status", "action": { "word": "status", "args": {} } }
]
}
}

The plan is a surface-neutral reply: text/html body, optional media (image, screenshot, file or audio — carried by url or inline data), and affordances (next actions, each carrying a replayable CommandInvocation a client renders as a native button / component / numbered key).

The full request/response schemas are in the generated Web API reference (commands tag).