Skip to content

输出约定 ​

sp 在加上 --json 时输出什么、以什么退出码结束。写脚本、接 CI、让 AI 代理调用 sp 时以本页为准。

要点:ok: true 只表示命令执行了,不表示检查都通过;sp policy gate、sp doctor、sp upgrade 在结果为失败时仍把结果写到标准输出,同时以 1 退出。sp agent command --format shell|docker|maven 即使带 --json 也输出纯文本;sp tunnel 一直运行到被停止,没有结果信封(进度写到标准错误,带 --json 时不输出)。

本页保留英文

这是给脚本和 AI 代理用的机器契约,字段名、取值、错误码都和程序输出逐字对应,所以正文保留英文。

Success: envelope on stdout ​

json
{
  "ok": true,
  "command": "app list",
  "data": { }
}
FieldTypeDescription
okbooleantrue: the command ran and produced a result
commandstringNormalized command name for logging
dataobjectCommand-specific payload (see Common data shapes)

ok: true means the command ran — not that everything it checked passed. Three commands write their result with ok: true and exit 1 when that result is a failure: see Results that are failures.

Failure: envelope on stderr ​

When a command can't do its job — bad arguments, missing config, an unreachable backend, a backend error — it writes an error object to stderr and exits non-zero:

json
{
  "ok": false,
  "command": "replay run",
  "error": {
    "code": "API_ERROR",
    "message": "Human-readable summary",
    "httpStatus": 500,
    "backend": { }
  }
}

httpStatus is optional status context: an HTTP error usually carries the response status, but a business error returned with HTTP 200 carries 200, and some paths set the value themselves — it never means the request succeeded. backend is optional extra context: usually a summary of the backend's error (responseCode/responseDesc, or result/desc), sometimes built by the CLI itself (for example next steps when the agent jar is missing). Don't treat either as proof that a request reached the backend, and don't parse backend as the backend's raw response. Without --json, the same failure is printed as error: <message> (suppressed by --quiet).

If a command needs a token and none is configured, the CLI does not prompt in --json mode; it exits 3 with "code": "AUTH_REQUIRED". This is checked by the CLI before calling the backend.

Flags or arguments the command parser itself rejects (an unknown flag, a missing positional argument) exit 1 without a JSON envelope and may print nothing. Check the exit code, not only stderr.

Results that are failures ​

CommandWhenOutput
sp policy gateAt least one policy file is invalidResult on stdout (ok: true, data.valid: false), exit 1
sp doctorAt least one check failedResult on stdout (ok: true, data.status: "failed"), exit 1
sp upgradeThe installer failedResult on stdout (ok: true, data.status: "failed"), exit 1. The installer's own output is streamed to stdout/stderr as well, so stdout is not a single JSON document

sp policy <type> validate is different again: when the backend completes the check and finds the policy invalid, the command exits 0 with data.valid: false. A file that can't be read or parsed is a USAGE error (exit 2), and a failed request is exit 1. In CI, check both the exit code and data.valid.

Exit codes ​

CodeError codesMeaningRetry?
0—SuccessNo
1API_ERROR, NO_RECORDED_CASES, NO_PINNED_CASES, CONFIG_MISSING, CONFIG_PARSE_ERROR, CONFIG_WRITE_ERROR; parser errors; the result failures aboveThe backend returned an error or couldn't be reached, there was nothing to replay, the config couldn't be read or written, or a check failedSometimes — after fixing data or config, or for a transient backend error
2USAGE, PROFILE_NOT_FOUNDInvalid input the command validated itself, or an unknown profileNo — fix the invocation
3AUTH_REQUIREDNo token available in non-interactive modeAfter sp auth login or setting SP_TOKEN
Error codeCause
CONFIG_PARSE_ERRORA config file exists but isn't valid JSONC or fails validation
CONFIG_WRITE_ERRORA config file couldn't be written
CONFIG_MISSINGA command that needs a config file found none. Most commands don't: with no file they fall back to defaults and environment variables such as SP_API_URL
PROFILE_NOT_FOUNDThe selected profile doesn't exist in the merged config

These are the exit codes of sp itself. The script in Replay after deployment defines its own exit codes on top.

How backend errors map to exit codes ​

Most console APIs return:

json
{
  "responseStatusType": { "responseCode": 0, "responseDesc": "success" },
  "body": { }
}

The CLI maps responseCode !== 0 to exit 1, and unwraps body into data (an array body becomes data.items).

Replay control (createPlan, progress, …) returns:

json
{
  "result": 1,
  "desc": "success",
  "data": { }
}

The CLI maps result !== 1 to exit 1. A non-2xx HTTP status is exit 1 too, with one exception: sp replay run, sp replay rerun, sp replay compare, sp replay noise exclude and sp replay realtime create are judged by result only. Every other command, including sp replay status and sp replay case list, checks the HTTP status first. Network, read and JSON parse errors always fail.

Large output: artifacts ​

Some commands write their payload to a file and put only a pointer on stdout:

json
{
  "ok": true,
  "command": "replay diff get",
  "data": {
    "diffId": "abc123",
    "artifact": ".sp-work/diff-abc123.json",
    "summary": {
      "diffId": "abc123",
      "diffResultCode": 1,
      "categoryName": "..."
    }
  }
}

replay diff get and replay mock-tree always do this; record query does it when the payload is larger than 4 KiB; diagnose replay writes a file for each failed case whose diff it could find and lists them in data.artifacts. Cases that failed to replay, and cases without a readable diff, get no file, so the number of files is not the number of failures. sp logs never writes a file: redirect its stdout. Check for data.artifact before reading the payload from stdout. The default --out-dir is .sp-work/ in the current working directory.

Pagination ​

List commands that page accept:

FlagDefaultDescription
--page11-based page index
--limit20Page size, at most 100

Not every list endpoint pages: for example sp replay case list --plan <id> (without --plan-item) returns the plan's cases in one response. Some commands take their own --limit with a different meaning; see the command's page.

A paged result looks like:

json
{
  "ok": true,
  "command": "replay case list",
  "data": {
    "items": [],
    "page": 1,
    "pageSize": 20,
    "total": 142
  }
}

total is present only when the backend reports a count. There is no field filter; select fields with jq, for example jq '[.data.items[] | {replayId, traceId}]'.

Without --json ​

Results go to stdout as readable text or indented JSON (no envelope), and errors to stderr as error: <message>.

Common data shapes ​

These pass through what the backend returns, so extra fields may appear; ignore fields you don't know.

ApplicationListItem ​

sp app list → data.items[].

json
{
  "id": "string",
  "appId": "string",
  "appName": "string",
  "name": "string",
  "env": "string",
  "agentVersion": "string",
  "agentStatus": "online | degraded | offline | never",
  "lastSeenAt": 0,
  "tags": ["string"],
  "worktreeDirectory": "string"
}

AgentStatus ​

sp app status <appId> → data.

json
{
  "appId": "string",
  "status": "online | degraded | offline | never",
  "agentVersion": "string",
  "lastSeenAt": 0,
  "instanceCount": 0
}

instanceCount counts instances whose last heartbeat is within the online threshold, not every instance ever registered. agentVersion, lastSeenAt and similar fields can be empty or missing. What each status means: Concepts and IDs — Application.

ApplicationCreateResult ​

sp app create → data.

json
{
  "success": true,
  "appId": "string",
  "msg": "string"
}

PolicyValidateResult ​

sp policy <type> validate → data. details is the backend's validation result as is.

json
{
  "valid": false,
  "details": {
    "valid": false,
    "errors": ["..."],
    "warnings": []
  }
}

ReplayPlanCreated ​

sp replay run → data.

json
{
  "planId": "string",
  "result": 1,
  "desc": "string"
}

ReplayProgress ​

sp replay status <planId> → data.

json
{
  "percent": 0,
  "lastUpdateTime": "2026-06-27 10:00:05"
}

lastUpdateTime is the scheduler's local time in GMT+8, without a time zone suffix.

With --watch (on status or run), the command prints one envelope per poll until the plan finishes; the last one adds "finished": true. If the plan hasn't finished after 10 minutes, it stops with API_ERROR (exit 1).

ArtifactResult ​

json
{
  "artifact": "relative/path.json",
  "summary": {}
}

Versions ​

sp version (or sp -v, sp --version) prints the CLI version; sp version --json wraps it in the envelope. Log it with each session so behavior can be matched to a release, and pin the CLI version in CI.

The CLI talks to one sp-backend URL; the storage and scheduling services behind it are internal and not addressed separately.

捕获 Session · 审查 Steps · 改进关键所在