feat(cli): 错误提示行动 hint + 发布门禁 + 分发收尾 (0.1.7)

- 错误输出附 hint: 404 Invalid tableId / Workflow not found / 路由不存在 /
  401 / 403 / 非法选项值 / visibilityFilter 等 9 类模式,JSON 模式含 hint 字段
- prepublishOnly 门禁: build + test + 全命令冒烟(81 用例)
- skills/ 移除 evals/(判分标准不随技能分发) + scripts/sync-skills.sh 一键同步
- 冒烟建库固定在 agent-playground 专用空间(生产保护红线)
This commit is contained in:
yeuimu
2026-09-30 06:19:48 +00:00
parent e584b7ebbe
commit d6a8bc4063
5 changed files with 59 additions and 57 deletions
+29 -1
View File
@@ -7,6 +7,32 @@ import { resolveConfig } from './config';
import { Client, ApiError } from './client';
import { describeError } from './request';
/** Actionable hints for recurring backend error shapes — the goal is that an
* agent (or human) reading the error knows the next command to try, instead
* of spending a round-trip re-deriving the cause. */
export function buildHint(status: number, message: string): string | undefined {
const m = message || '';
if (/Invalid tableId/i.test(m))
return '表不存在或不属于当前 base —— 用 `inkreach-teable table list` 核对 ID;确认 --base-id / INKREACH_TEABLE_BASE_ID 指向正确的基地。';
if (/Workflow not found/i.test(m))
return '工作流不存在(或已被软删除)—— 用 `inkreach-teable workflow list` 核对 ID;workflow delete 是软删除,列表里可能已不可见。';
if (/Cannot (GET|POST|PUT|PATCH|DELETE)/i.test(m))
return '路由不存在 —— CLI 与后端版本可能不匹配,升级 CLI:`npm i -g @inkreach/inkreach-teable`;仍失败则用 --debug 反馈。';
if (status === 401)
return '鉴权失败 —— `inkreach-teable auth status` 检查 token;必要时到 Web UI Settings → Personal Access Tokens 重新签发并 `auth login`。';
if (status === 403 || /restricted_resource|Forbidden/i.test(m))
return '权限不足 —— 该操作需要更高角色(base owner/creator)或 PAT 授权范围不含此 base;插件上传还需 plugin_developer 白名单。';
if (/Invalid value for field|Invalid option/i.test(m))
return '值不在字段合法选项内 —— `inkreach-teable field list <tableId> --with-options` 查看可选值;新增选项用 `field update <tableId> <field> --options`。';
if (/does not meet the current row conditions/i.test(m))
return '违反了字段的 visibilityFilter(行级条件)—— `inkreach-teable field visible-choices <tableId> <recordId> <field>` 查看该行可见选项。';
if (/expected array, received/i.test(m))
return '参数形状不符 —— 批量接口要数组;单条可改用 --fields,批量用 --records。';
if (/VALIDATION|Validation error/i.test(m) && status === 400)
return '请求校验失败 —— 用 --json 查看完整错误;对照 `<cmd> --help` 的参数示例。';
return undefined;
}
export interface Ctx {
client: Client;
json: boolean;
@@ -62,10 +88,12 @@ export function run<TArgs extends unknown[] = unknown[]>(
await fn(ctx, positional, options);
} catch (err) {
const { status, code, message } = describeError(err);
const hint = status ? buildHint(status, message) : undefined;
if (ctx.json) {
process.stdout.write(JSON.stringify({ error: { status, code, message } }) + '\n');
process.stdout.write(JSON.stringify({ error: { status, code, message, ...(hint ? { hint } : {}) } }) + '\n');
} else {
process.stderr.write(`✗ ${code}${status ? ` (${status})` : ''}: ${message}\n`);
if (hint) process.stderr.write(` hint: ${hint}\n`);
}
process.exitCode = 1;
}