输出与错误码

区分结构化结果与子进程日志,按当前输出字段和错误码处理自动化。

约 6 分钟3 天前更新在 GitHub 编辑

One 自己的结果支持 -o json;stdout 不是 TTY 时默认输出 JSON。帮助和 one mise、one hk 的透传输出保留各自格式,one exec 的子进程输出也保持原样。自动化应先区分命令结果与子进程日志,再按对应字段处理。

1. 输出格式自动判定

# 终端里人类格式:
one templates list

# 管道时自动 JSON(或显式 -o json):
one templates list -o json | jq
one templates list | tee templates.json   # 管道 → JSON

自动判定看 stdout 是不是 TTY。显式控制:

one templates list -o json       # 强制 JSON
one templates list -o yaml       # 强制 YAML
one templates list -o text       # 强制人类格式

2. 成功输出

输出结构按命令区分。下面是 one templates -o json 的节选:

{
  "schema": "one-cli/templates/v1",
  "templates": [
    {
      "id": "nestjs-api",
      "name": "NestJS API",
      "toolchain": "node",
      "category": "backend"
    }
  ]
}

schema 是结果类型标识,不是网页地址。按对应命令的实际字段解析:

命令结果字段
one templatesschema、total、templates[]
one addschema、subproject_name、target_path、template_id、toolchain
one whoamiloggedIn、expired,以及可用时的账号信息;没有 schema 或 profiles
one run <task>schema、status、exit_code、entries、tasks

不要假定所有结果有同一层包络。模板名称和说明跟随 CLI 语言,脚本应使用稳定的 ID。

3. 错误包络

One 自己的结构化错误通过 stderr 输出,使用以下包络。子进程失败也可能直接返回原始日志和退出码:

{
  "schema": "one-cli/error/v1",
  "error": {
    "code": "TEMPLATE_NOT_FOUND",
    "message": "Template 'nestjs-api-typescript' not found.",
    "context": {
      "requested_template": "nestjs-api-typescript",
      "available_templates": ["nestjs-api", "go-api", "nextjs-app", "..."]
    }
  }
}

三条规则:

  1. 按 error.code 分流,永远不要按 error.message 分流。message 是给人看的,会随用户 locale(zh-CN vs en-US)变。
  2. 读 error.context 取结构化细节。可用字段取决于失败位置;错误码含义见错误码大全。
  3. 先看退出码和输出来源。成功结果通常在 stdout,One 的错误在 stderr。one run 的 JSON 模式把子进程日志送到 stderr,可能与错误信息混合;one exec 保留子进程输出。

4. shell 里的分流写法

保存 stdout 和 stderr,保留日志。只有 stderr 是可解析的错误包络时,才按错误码分流;否则显示原始错误。

error_file=$(mktemp)
trap 'rm -f "$error_file"' EXIT

if output=$(one add nestjs-api --name api -o json --yes 2>"$error_file"); then
  printf '%s\n' "$output" | jq '.subproject_name'
  cat "$error_file" >&2
else
  exit_code=$?
  if code=$(jq -er '.error.code' "$error_file" 2>/dev/null); then
    case "$code" in
      TARGET_EXISTS) printf '%s\n' "项目目录已存在,请选择其他名称。" >&2 ;;
      NOT_ONE_PROJECT) printf '%s\n' "请先进入已有工作区,或创建工作区。" >&2 ;;
      *) printf '%s: %s\n' "未处理的错误" "$code" >&2 ;;
    esac
  fi
  cat "$error_file" >&2
  exit "$exit_code"
fi

解析时注意:

  • message 文本因 i18n 变动
  • 未来新增的错误码(* 兜底)
  • 带有 schema 的结果应按命令检查支持的类型

5. agent 的同款模式

读取 One 结构化结果时显式加 -o json。错误包络中读 error.code。永远不要解析 error.message — 它随用户 locale 变。

实际中,"加一个叫 user-api 的 NestJS 服务"的 agent 会:

  1. 跑 one add nestjs-api --name user-api -o json --yes
  2. 退出码 0 → 解析成功包络、确认
  3. error.code == "TARGET_EXISTS" → 试 user-api-2,或问用户
  4. error.code == "NOT_ONE_PROJECT" → 先 one create 或问用户

agent 可以直接根据命令的结构化输出使用这些恢复模式。

6. 关键错误码组

优先处理与你的操作有关的错误,其他错误保留原始原因。常用分组如下:

组例子典型处理
工作区状态NOT_ONE_PROJECT、WORKSPACE_NESTED_FORBIDDEN、MANIFEST_MISSING_OR_EMPTY"从工作区根跑"或"先 one create"
模板TEMPLATE_NOT_FOUND、TEMPLATE_REQUIRED、INVALID_NAME从 error.context 列出可用模板
工具与任务MISE_INSTALL_FAILED、MISE_CONFIG_CONFLICT、RUNTIME_TASK_NOT_FOUND检查下载或配置错误,使用 one run 查看任务
InfisicalINFISICAL_NOT_CONFIGURED、INFISICAL_AUTH_MISSING、INFISICAL_AUTH_FAILED登录后保存首个变量以绑定工作区;会话失效时重新登录
环境变量ENV_UNKNOWN_ENVIRONMENT、ENV_SET_OVERWRITE_REQUIRED选择已声明环境,或确认覆盖后传 --yes
ServeSERVE_PORT_BUSY、SERVE_BIND_FORBIDDEN修改 host 或 port flag 后重启

当前错误码及恢复说明:错误码大全。

7. JSON 输出作为 CI 契约

CI 中显式指定 -o json,并通过 ONE_VERSION 固定 CLI 版本。将示例版本替换为团队使用的发布版本:

- run: curl -fsSL https://1cli.dev/install.sh | ONE_VERSION=v0.1.0 bash
- run: one -o json add nestjs-api --name api --yes

常见踩坑

现象原因修法
CI 里拿到人类格式的表格而非 JSONstdout 是 TTY(CI runner 偶尔会)显式传 -o json
jq 无法解析把日志和结构化结果合并了,或命令透传原始输出分开保存 stdout / stderr;无法解析时保留原始日志
不同机器的 error.message 不一样用户 locale 不同正常,按 code 分流即可
schema 字段缺失并非每个命令都提供该字段按命令参考解析;例如 whoami 使用 loggedIn

下一步