JSON 里的运行事实
每条命令都有唯一的机器形状:stdout 恰好一个 JSON 值,诊断写 stderr,错误码稳定。同一条命令同时回答人和 agent,只是读者不同。
规则
- 带普通
--json时,stdout 恰好一个 JSON 值。诊断写 stderr,绝不混进来。 - 流式命令用 NDJSON:一行一个值。
- 退出码:0 成功,1 运行失败,2 参数或选项错误,130 被 Ctrl-C 中断。
- 错误形状是
{"error":{"code":"…","message":"…","hint":"…"}}。hint 指向真实存在的证据。 - 没有字段会被接受却不被读取。发布出去的字段是每个客户端都可以依赖的承诺,所以协议构建会在没有处理器的字段上失败。
稳定错误码
| 分组 | 错误码 | 含义 |
|---|---|---|
| 输入 | invalid_params、not_found、invalid_config、ambiguous | 请求或声明是错的;消息会点名对象。 |
| 权限 | permission_denied | 操作被拒绝;hint 说明要改什么。 |
| 时间 | timeout、daemon_busy | 引擎放弃了,或另一个动作占着 daemon;先读 hint 再重试。 |
| 观察 | observation_empty、observation_unrecognized、observation_incomplete、observation_blind、observation_bind_denied | 没有一个表示“没有东西在监听”;检查 observation.state 与 doctor --only listener_observation。 |
| 内部 | internal | 意外失败;报告命令和版本,而不是只贴消息。 |
一次完整读取
部分成功也仍然是一个值。下面这次 up --json 起了一个服务、失败了另一个;退出码是 1,两个结果都在同一个文档里。
{"services":[
{"service":"api","state":"started","ready":false,"pid":1234,"port":3000},
{"service":"worker","state":"failed","reason":"start_failed",
"error":"exec: \"nosuchcmd\": executable file not found in $PATH",
"hint":"check the command and its working directory"}
],"started":["api"],"skipped":[],"errors":["worker"]}
statestarted、skipped或failed:启动动作的结果,不是就绪声明。reason- 稳定、可分支的失败原因,例如
start_failed、port_occupied或ready_timeout。 hint- 确定性的下一步,只指向真实存在的证据:进程真起来了才指向服务日志,依赖没就绪就指向依赖日志,没有可执行的下一步就什么都不给。
port_holder- 在
port_occupied时出现:占用者的端口、pid、命令和类型;如果是我们托管的服务,还带组名、服务名和 run id。 ready- CLI 的注解,不是 daemon 事实:
--wait确认就绪后为true,没有等待时为false。false不是失败。