Execution and operation
Run one tool call from a terminal, read the schema before you do, follow the session it ran in, and tail what it logged.
Seven leaf commands run a tool and account for what it did: execute, ship, tools show, sessions list, sessions show, sessions close and logs tail. sessions, tools and logs are groups with no action of their own. Running one alone prints its own usage to stderr and exits 1.
They are not one client. sessions *, tools * and logs tail speak HTTP directly from the CLI. execute and ship go through the SDK, which opens a session, runs the call inside it and deletes the session in a finally. That seam is not cosmetic: measured, the two halves answer the same rejected key with different exit codes and disagree about what --project accepts. Both are below.
Every flag, message and exit code on this page was run against @codespar/cli 0.10.0 — the version on npm — and the transcripts are the captured bytes of those runs.
What every command here shares
Credentials and scope
Resolution order, first match wins: the root flag, then the environment variable, then ~/.codespar/config.json.
| Setting | Root flag | Environment | Config key |
|---|---|---|---|
| API key | --api-key <key> | CODESPAR_API_KEY | apiKey |
| API host | --base-url <url> | CODESPAR_BASE_URL | baseUrl |
| Project | --project <id> | CODESPAR_PROJECT | project |
With nothing set, the host is https://api.codespar.dev. A resolved project rides on every request as the x-codespar-project header; with no project resolved the header is absent.
With no key resolved, all seven refuse before opening a socket, with the same line:
✗ Not logged in. Run `codespar login` or set CODESPAR_API_KEY.That check runs inside the command, so an argument Commander itself rejects is reported first: codespar execute with no credential and no --server answers error: required option '-s, --server <id>' not specified, not the credential line.
--project is validated on one half and not the other. codespar --project proj_abc sessions list sent x-codespar-project: proj_abc without complaint. The same --project proj_abc on execute or ship exits 2 with Error: CodeSpar projectId must match ^prj_[A-Za-z0-9]{16}$, because the SDK constructor checks the shape and the direct-HTTP client does not. A project id of the prj_ + 16 characters form works on both.
stdout, stderr and --json
Tables, key/value blocks and JSON go to stdout. Status lines go to stderr, prefixed ℹ, ✓ or ⚠, and an empty table prints (no results) there too. A pipe into jq receives the data alone.
--json is declared on the root command, but Commander accepts it on either side of the subcommand: codespar --json sessions list and codespar sessions list --json printed identical JSON with an empty stderr. Colour is dropped when stdout is not a TTY, when NO_COLOR is set, or when FORCE_COLOR=0.
Two commands on this page take --json and ignore it, because they have no JSON path at all: sessions close prints its confirmation to stderr and leaves stdout empty either way, and ship without --json writes nothing to stdout.
Exit codes
| Code | When | First line of stderr |
|---|---|---|
0 | The command finished, including --help | nothing |
1 | A refusal the CLI raises itself, or an argument Commander rejects | ✗ <message>, or error: <message> for Commander's own |
2 | Any other exception. The stack trace follows | ✗ internal error: |
There is no uniform exit code for "the tool call failed." Measured against one stub answering {"success": false, "error": "no_eligible_providers"} to the same route: codespar execute printed Tool call failed: no_eligible_providers to stderr, printed the envelope to stdout, and exited 0. codespar ship, on the identical response, exited 2 with ✗ internal error: and a stack trace reading Error: ship failed: no_eligible_providers. The typed SDK wrapper behind ship throws a plain Error, which the CLI does not recognise as its own refusal type. A script that branches on the exit code of ship is branching on something else.
The same seam shows on rejected credentials. A 401 on sessions list is one line and exit 1; a 401 on execute or ship, raised while creating the session, is exit 2 with a stack trace naming createSession failed: 401.
codespar execute <tool>
Opens a throwaway session against one server, sends one tool call into it, prints the result envelope, and deletes the session in a finally. Three requests per run: POST /v1/sessions, then the execute, then DELETE /v1/sessions/{id}.
https://api.codespar.dev/v1/sessions/{id}/executeResolves `tool` as a CodeSpar meta-tool first, then as a catalog tool
| Name | Type | Required | What it does |
|---|---|---|---|
<tool> | positional | yes | The tool name sent as tool in the request body |
-s, --server <id> | string | yes | The session is opened with servers: ["<id>"]. Declared as a required option, though --help does not mark it |
-i, --input <json> | string | no | Parsed as the input object. Mutually exclusive with --input-file |
-f, --input-file <path> | string | no | Same, read from a file |
-u, --user <id> | string | no | Sent as user_id when the session is created. Defaults to the literal cli-user, set in the command body and therefore absent from --help |
With neither --input nor --input-file, the input is the empty object {}.
codespar execute codespar_ship \
--server melhor-envio \
--input '{"action":"quote"}'{
"success": true,
"data": {
"id": "shp_01J",
"status": "quoted",
"carrier": "Correios",
"cost_minor": 2140,
"estimated_delivery": "2026-09-17"
},
"duration": 734,
"server": "melhor-envio",
"tool": "codespar_ship",
"tool_call_id": "tcl_01J"
}Without --json, stdout carries the same envelope and stderr carries one line, ✓ codespar_ship succeeded in 734ms. With --json, stderr is empty and stdout is unchanged: the envelope is already the machine-readable form.
Calls POST /v1/sessions with {"servers": ["<--server>"], "user_id": "<--user>"}, then POST /v1/sessions/{id}/execute with {"tool": "<tool>", "input": {...}}, then DELETE /v1/sessions/{id}. All three measured on the wire. The SDK equivalent is session.execute.
| When | What you get | Exit |
|---|---|---|
--server omitted | error: required option '-s, --server <id>' not specified | 1 |
--input and --input-file both given | ✗ Pass either --input or --input-file, not both. | 1 |
--input is not an object | ✗ --input must be a JSON object. (an array or a scalar is refused) | 1 |
--input is not JSON | ✗ --input is not valid JSON: <the parser's own message> | 1 |
--input-file does not exist | ✗ internal error: and a stack trace reading ENOENT: no such file or directory | 2 |
| Key rejected while opening the session | ✗ internal error: and a stack trace reading createSession failed: 401 <body> | 2 |
| The tool ran and failed | Tool call failed: <message> on stderr, the envelope on stdout | 0 |
codespar ship
Opens a session attached to no server, runs the codespar_ship meta-tool inside it, and closes it. The meta-tool picks the carrier, so there is no --server: the whole operation arrives as one JSON object under --input.
https://api.codespar.dev/v1/sessions/{id}/executeResolves `tool` as a CodeSpar meta-tool first, then as a catalog tool
| Name | Type | Required | What it does |
|---|---|---|---|
-i, --input <json> | string | one of the two | The codespar_ship arguments as a JSON object |
-f, --input-file <path> | string | one of the two | The same object, read from a file |
-u, --user <id> | string | no | Sent as user_id when the session is created. Defaults to cli-user |
The CLI checks four things before it opens the session, and nothing else. These are the fields it names, read from the command's own validator:
| Field | Required | What the CLI enforces |
|---|---|---|
action | yes | One of label, quote, track. Anything else is refused |
tracking_code | for track | Must be present when action is track |
origin, destination | for label, quote | Both must be present |
items | for label, quote | Must be a non-empty array |
That table is the CLI's check, not the tool's schema. The shapes of origin, destination and each item, the optional service_level and metadata, and every result field beyond the ones printed below are the server's contract, not this command's. Read them on codespar_ship before building a payload.
codespar ship --input '{
"action": "quote",
"origin": { "postal_code": "01310100" },
"destination": { "postal_code": "22041011" },
"items": [{ "weight_g": 500 }]
}'✓ ship shp_01J → quoted
ℹ Carrier: Correios
ℹ ETA: 2026-09-17
ℹ Cost (minor units): 2140The human form prints, when the result carries them and only then, the tracking code, the carrier, the label URL, the estimated delivery and the cost in minor units. Every one of those lines goes to stderr; without --json this command writes nothing at all to stdout. With --json, stdout carries the typed result object and stderr is empty.
Calls POST /v1/sessions with {"servers": [], "user_id": "<--user>"}, then POST /v1/sessions/{id}/execute with {"tool": "codespar_ship", "input": {...}}, then DELETE /v1/sessions/{id}. The SDK equivalent is session.ship.
| When | What you get | Exit |
|---|---|---|
Neither --input nor --input-file | ✗ ship requires --input '<json>' or --input-file <path>. followed by a worked example | 1 |
| Both given | ✗ Pass either --input or --input-file, not both. | 1 |
action missing or unknown | ✗ ship.action must be one of: label, quote, track. | 1 |
action: "track" with no code | ✗ ship.tracking_code is required when action=track. | 1 |
label or quote without both addresses | ✗ ship.origin and ship.destination are required when action=label|quote. | 1 |
label or quote with an empty items | ✗ ship.items must contain at least one item when action=label|quote. | 1 |
| The tool ran and failed | ✗ internal error: and a stack trace reading Error: ship failed: <message> | 2 |
All six validations run before the session is opened, so a rejected payload costs no request.
codespar tools show <name>
Prints one tool's identity and its prose description, found inside its server's tool listing.
https://api.codespar.dev/v1/servers/{id}/tools| Name | Type | Required | What it does |
|---|---|---|---|
<name> | positional | yes | The tool to find in that server's listing |
-s, --server <id> | string | yes | The server that exposes it |
--json | boolean | no | Root flag. Prints the tool's object verbatim |
Changed in 0.8.0. --server is now required, and the command no longer prints input and output schemas: the per-server listing carries a name and a description. For a meta-tool's full schema use codespar tools meta <name>, which reads the published definitions without a network call.
codespar tools show codespar_shipName codespar_ship
Server codespar
Quote, label or track a shipment.
Input schema:
{
"type": "object",
"properties": {
"action": {
"enum": [
"label",
"quote",
"track"
]
}
},
"required": [
"action"
]
}The description, the input schema and the output schema each print only when the route returns them; the Name and Server pair always prints. An Output schema: block follows the input one under the same rule.
Calls GET /v1/servers/{id}/tools and finds the named tool in the listing.
| When | What you get | Exit |
|---|---|---|
<name> omitted | error: missing required argument 'name' | 1 |
No --server | The refusal above, naming codespar servers list. Nothing is sent | 1 |
| Name not on that server | ✗ <server> exposes no tool called "<name>". Run `codespar tools list --server <server>` … | 1 |
| Key rejected | ✗ GET /v1/servers/<id>/tools → 401: <the API's own message> | 1 |
codespar tools list
Lists tools across the catalog, or one server's, with each description cut to fit a terminal column. It takes -s, --server <id>, which is sent as the server query parameter, and the root --json. Its full block, including the truncation rule and the empty-result behaviour, is on Catalog and discovery, because it reads the catalog rather than a running session, which is why it lives there.
codespar sessions list
Lists the sessions the key can see as a six-column table, newest first as the route returns them. Both filters are forwarded to the route verbatim; the CLI parses neither and defaults neither.
https://api.codespar.dev/v1/sessions| Name | Type | Required | What it does |
|---|---|---|---|
--status <s> | string | no | Sent as the status query parameter, unparsed |
--limit <n> | string | no | Sent as the limit query parameter, unparsed |
--json | boolean | no | Root flag. Prints the array inside the envelope, not the envelope |
--status is checked against the vocabulary the listing declares — active, closed, error — and --limit must be a positive whole number. Either one outside that fails before anything is sent:
✗ --status expects one of active, closed, error, got "inventado".
✗ --limit expects a positive whole number, got "abc".When a flag is omitted its query parameter is simply absent: codespar sessions list with no flags sends GET /v1/sessions with no query string, and the server applies its own default page size.
codespar sessions list --status active --limit 5ID USER STATUS SERVERS TOOL CALLS CREATED
ses_01J8 cli-user active asaas 3 2026-09-12 14:02:11
ses_01J7 agent-7 closed 12 2026-09-12 11:47:03A missing field prints -; SERVERS is the list joined with commas, and CREATED is the timestamp cut to YYYY-MM-DD HH:MM:SS. An empty result prints (no results) on stderr with nothing on stdout, and still exits 0; --json prints [].
Calls GET /v1/sessions, reading the rows from a data array in the response.
| When | What you get | Exit |
|---|---|---|
| Key rejected | ✗ GET /v1/sessions → 401: <the API's own message> | 1 |
| Scope rejected | ✗ GET /v1/sessions → 403: <the API's own message> | 1 |
| No sessions match | (no results) on stderr, empty stdout | 0 |
| Request exceeds 30s | ✗ Request to GET /v1/sessions timed out after 30000ms. | 1 |
codespar sessions show <id>
Prints one session as a seven-line key/value block, and with --logs fetches that session's tool calls in a second request and prints them as a table underneath.
https://api.codespar.dev/v1/sessions/{id}| Name | Type | Required | What it does |
|---|---|---|---|
<id> | positional | yes | Percent-encoded into the path |
--logs | boolean | no | Makes a second request for the session's tool calls and prints them |
--json | boolean | no | Root flag. See the warning below: --logs changes what the JSON root is |
codespar sessions show ses_01J8 --logsID ses_01J8
User cli-user
Status active
Servers asaas
Tool calls 2
Created 2026-09-12T14:02:11.000Z
Closed -
Logs:
TOOL SERVER STATUS MS AT
codespar_ship codespar success 812 14:02:14
codespar_charge codespar error 190 14:03:01Created and Closed print the raw timestamp, unlike the list, which trims it. The log table's AT column is the time of day only.
--logs changes the root of the JSON. Without it, --json prints the session object itself. With it, the root becomes an envelope, {"session": {...}, "logs": [...]}. A parser written against one form breaks on the other, and the flag that switches them is not a formatting flag.
Calls GET /v1/sessions/{id}, plus GET /v1/sessions/{id}/tool-calls when --logs is given.
| When | What you get | Exit |
|---|---|---|
<id> omitted | error: missing required argument 'id' | 1 |
| Key rejected | ✗ GET /v1/sessions/<id> → 401: <the API's own message> | 1 |
| Session not found | ✗ GET /v1/sessions/<id> → 404: <the API's own message> | 1 |
codespar sessions close <id>
Closes an active session by id and prints one confirmation line. It takes no flags of its own, and --json does nothing: stdout stays empty either way.
https://api.codespar.dev/v1/sessions/{id}| Name | Type | Required | What it does |
|---|---|---|---|
<id> | positional | yes | Percent-encoded into the path |
codespar sessions close ses_01J8✓ Session ses_01J8 closed at 2026-09-14T16:41:02.118Z.| When | What you get | Exit |
|---|---|---|
<id> omitted | error: missing required argument 'id' | 1 |
| Key rejected | ✗ DELETE /v1/sessions/<id> → 401: <the API's own message> | 1 |
| Session not found | ✗ DELETE /v1/sessions/<id> → 404: <the API's own message> | 1 |
codespar logs tail
Reads the project's recent tool calls and prints one fixed-width line each, oldest first. With --follow it keeps polling for new ones until you interrupt it.
https://api.codespar.dev/v1/tool-calls| Name | Type | Required | What it does |
|---|---|---|---|
--limit <n> | number | no | How many recent calls to read. Default 20; a non-integer is refused before anything is sent |
-f, --follow | boolean | no | Keep polling every 3s, printing rows not seen yet |
-s, --server <id> | string | no | Keeps only this server — applied to the page fetched, not by the API |
--status <s> | string | no | Same, and same caveat |
-t, --tool <name> | string | no | Same, and same caveat |
This is a poll, not a stream. The API has no push channel for the tool-call log, so --follow re-reads the most recent page every three seconds and prints the rows it has not seen.
The three filters are applied client-side, to the page that was fetched, because the route takes no such query parameters. Whenever one is set, the command prints how many of the fetched rows matched — so an empty result cannot be read as "nothing happened".
| --json | boolean | no | Root flag. One JSON document per entry (see below) |
codespar logs tail --status error --tool codespar_charge14:02:14 SUCCESS codespar_ship melhor-envio 812ms
14:03:01 ERROR codespar_charge asaas 190ms no_eligible_providersThe columns are padded to fixed widths (status to 7, tool to 24, server to 16), with the status green, red or yellow for success, error and running. Tailing logs — press Ctrl-C to stop goes to stderr before the first frame. A frame the parser cannot read is skipped in silence, which is how keep-alives pass through.
--json here is not one document. It prints a separate JSON object per entry onto the same stdout, back to back, with no array around them and no delimiter between them. A plain | jq over the stream will not parse it; a line-delimited or streaming reader will.
Calls GET /v1/tool-calls, once without --follow and once per poll with it.
| When | What you get | Exit |
|---|---|---|
| The route answers 404 | The streaming-not-available message below, naming the one-shot alternative | 1 |
| The route answers 401 or 403 | The auth message below, carrying the status it got | 1 |
| Any other status | ✗ Log stream returned <status>. and an invitation to report it | 1 |
| The connection cannot be opened | ✗ Failed to connect: <the network error's own message> | 1 |
The first two, verbatim:
✗ Auth failed (401). Check CODESPAR_API_KEY or re-run `codespar login`.Related
codespar_ship: the schema, the carriers and the result shapes behindcodespar ship- Sessions: the HTTP routes the session commands call, and the async-settlement flow a tool call starts
session.executeandsession.ship: the SDK callsexecuteandshipwrap- Catalog and discovery:
tools list, and the commands that find a tool worth executing - Debugging: what to do with a tool call once you have its id