feat(cli): base/space 命令 + 执行守则 + eval harness 入仓 + 多轮 eval (0.1.9)

- 新命令: space list/create、base create/delete(实测通过,冒烟覆盖)
- SKILL.md §0 执行守则:完成全部步骤再答复/不谎报/分批/撤销指引
  (snapshot 无恢复 API,用 deactivate+节点改回)
- hint 补 429 限流与超时模式
- evals/ 入仓: 14+6 题 harness 参数化(EVAL_ROOT/EVAL_ENV/EVAL_MODEL/
  EVAL_CFG/auto_skill 触发模式)+ 多轮修正式 eval 与灵活判分
- 判分器加固: workflow list 错误负载不崩、官方 CLI 探测(command -v)不算使用
- 实测: auto 触发批(入仓版重跑) 11/11、99.0% 首试、100% ≤2 次;
  多轮修正 2/2+3/3(修改而非重建);冒烟 85 用例含 token 路由(低权会话)
- 运维事实入档: POST/DELETE /api/plugin 为管理员专属(PAT 403)
This commit is contained in:
yeuimu
2026-09-30 08:12:16 +00:00
parent 517dde9dee
commit c9694511a1
20 changed files with 1607 additions and 9 deletions
+78 -1
View File
@@ -50,7 +50,7 @@ async function fieldNameMap(client: Client, tableId: string): Promise<Record<str
}
export function baseResourceCommands(): Command[] {
return [baseCmd(), tableCmd(), fieldCmd(), recordCmd(), viewCmd(), presetCmd()];
return [baseCmd(), spaceCmd(), tableCmd(), fieldCmd(), recordCmd(), viewCmd(), presetCmd()];
}
// ===================== base =====================
@@ -82,6 +82,35 @@ function baseCmd(): Command {
})
);
cmd
.command('create')
.description('Create a base in a space')
.requiredOption('--space-id <id>', 'Space id (see `space list`)')
.requiredOption('--name <name>', 'New base name')
.action(
run(async (ctx, _a, o) => {
const opts = o as { spaceId: string; name: string };
const base = await ctx.client.createBase(opts.spaceId, opts.name);
if (ctx.json) printJson(base);
else {
const b = base as Record<string, unknown>;
printKv([['id', b.id], ['name', b.name]]);
}
})
);
cmd
.command('delete <baseId>')
.description('Soft-delete a base (DESTRUCTIVE: hides all its tables)')
.action(
run(async (ctx, args) => {
const baseId = args[0] as string;
await ctx.client.deleteBase(baseId);
if (ctx.json) printJson({ ok: true, id: baseId });
else process.stdout.write(`✓ Base ${baseId} deleted (soft)\n`);
})
);
cmd
.command('get <baseId>')
.description('Get base details')
@@ -100,6 +129,54 @@ function baseCmd(): Command {
return cmd;
}
// ===================== space =====================
function spaceCmd(): Command {
const cmd = new Command('space');
cmd.description('Manage spaces (top-level containers for bases)');
cmd
.command('list')
.description('List spaces you can access')
.action(
run(async (ctx) => {
const list = (await ctx.client.listSpaces()) as Array<Record<string, unknown>>;
if (ctx.json) printJson(list);
else {
if (!list.length) {
process.stdout.write('No spaces found.\n');
return;
}
printTable(
[
{ key: 'id', header: 'ID' },
{ key: 'name', header: 'NAME' },
],
list.map((sp) => ({ id: truncate(sp.id), name: truncate(sp.name, 40) }))
);
}
})
);
cmd
.command('create')
.description('Create a space')
.requiredOption('--name <name>', 'New space name')
.action(
run(async (ctx, _a, o) => {
const opts = o as { name: string };
const sp = await ctx.client.createSpace(opts.name);
if (ctx.json) printJson(sp);
else {
const s2 = sp as Record<string, unknown>;
printKv([['id', s2.id], ['name', s2.name]]);
}
})
);
return cmd;
}
// ===================== table =====================
function tableCmd(): Command {
+12
View File
@@ -54,6 +54,18 @@ export class Client {
getBase(baseId: string) {
return this.call<unknown>('GET', `/api/base/${baseId}`);
}
createBase(spaceId: string, name: string) {
return this.call<unknown>('POST', '/api/base', { spaceId, name });
}
deleteBase(baseId: string) {
return this.call<{ id: string }>('DELETE', `/api/base/${baseId}`);
}
listSpaces() {
return this.call<unknown[]>('GET', '/api/space');
}
createSpace(name: string) {
return this.call<unknown>('POST', '/api/space', { name });
}
// ---- table ----
listTables(baseId: string) {
+4
View File
@@ -28,6 +28,10 @@ export function buildHint(status: number, message: string): string | undefined {
return '违反了字段的 visibilityFilter(行级条件)—— `inkreach-teable field visible-choices <tableId> <recordId> <field>` 查看该行可见选项。';
if (/expected array, received/i.test(m))
return '参数形状不符 —— 批量接口要数组;单条可改用 --fields,批量用 --records。';
if (status === 429)
return '触发限流 —— 稍等后重试;批量操作请降低并发或分批执行。';
if (/timeout|ETIMEDOUT|ECONNRESET/i.test(m))
return '请求超时/连接中断 —— 先用只读命令(如 table list)确认服务可达,再重试原操作。';
if (/VALIDATION|Validation error/i.test(m) && status === 400)
return '请求校验失败 —— 用 --json 查看完整错误;对照 `<cmd> --help` 的参数示例。';
return undefined;