Conversation
…cher Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
| if (values.dev && !process.env.__OMK_DEV_CHILD) { | ||
| const { spawn } = await import('node:child_process'); | ||
| const { fileURLToPath } = await import('node:url'); | ||
| const cliPath: string = fileURLToPath(import.meta.url); |
There was a problem hiding this comment.
P2: --dev 分支里 cliPath 现在会指向 commands/report.ts 对应的编译产物,而不是 CLI 入口。child args 会变成类似 node --watch-path ... dist/src/cli/commands/report.js bench report ...;这个 module 只 export execute,没有 top-level main(),所以子进程会直接退出,omk bench report --dev 不会启动 report server。这里需要恢复为真正的 CLI entrypoint(例如从 command module resolve 到 ../index.js,或使用当前 process.argv[1]),最好再补一个覆盖 --dev spawn argv 的回归测试。
There was a problem hiding this comment.
已修,commit 2a1f4f1:
const cliPath = resolve(fileURLToPath(import.meta.url), '..', '..', 'index.js');import.meta.url 指 commands/report.js,往上两层到 cli/index.js — 这是真 CLI 入口,child 跑那里的 main()。
补了 test/cli/dev-spawn-argv.test.ts,mock spawn 断言 child argv 的 entrypoint 是 cli/index.js 不是 commands/report.js — 以后有人「简化」回 fileURLToPath(import.meta.url) 直接 fail。
附带观察(不在本 PR 修):libDir = resolve(cliPath, '..', 'lib') 算出来是 dist/src/cli/lib,这个目录不存在,是重构前就有的。--dev 启动不会 crash(node --watch-path 对不存在路径静默),但 hot reload 应该一直没在工作。可以另开 fix PR 处理(建议改成 dist/src/)。
|
补充建议(不阻塞):
除前面那条 P2 外,我没有再看到需要阻塞合并的行为问题。 |
CR P2 修:commands/report.ts 里 import.meta.url 指向 commands/report.js,那个 module 只 export execute 没 main(),spawn 出去的 child 立刻退出,--dev 静默坏 掉。改成 resolve(url, '..', '..', 'index.js') 算回 CLI 入口。 补两层测试: - dev-spawn-argv.test.ts:mock spawn,断言 child argv 的 entrypoint 是 cli/index.js,不是 commands/report.js - dispatcher-routing.test.ts:遍历 commands/*.ts,断言每个 module 都被 index.ts import,防止以后加文件忘接 case Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
建议两条都 follow up 了,commit 2a1f4f1:
P2 inline thread 里也回了 — 顺带看到 libDir 指向不存在的 dist/src/cli/lib 是重构前的旧 bug,--dev 的 hot reload 一直没在工作。建议另开 fix PR,不混本次 refactor。 |
附带发现:原 libDir = resolve(cliPath, '..', 'lib') = dist/src/cli/lib,那个 目录不存在,node --watch-path 对不存在路径静默,--dev 启动后 hot reload 一直 没在工作。 改成 resolve(cliPath, '..', '..') = dist/src/,覆盖整个编译产物根 (server / renderer / eval-core 等 report server 依赖的 module 都在那)。 测试加一条断言:libDir 不能以 /lib 结尾,防止有人「恢复」回旧值。 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
顺手把 libDir 也修了,commit bb2f777: const libDir: string = resolve(cliPath, '..', '..'); // dist/src/旧值 dev-spawn-argv 测试加了一条断言 libDir 不以 |
index.ts 99 → 43 行,原来 17 个 case 的 switch 换成 BENCH_COMMANDS /
DOMAIN_COMMANDS 两张 Record 表。新增子命令只要在 registry 里加一行,不再
回 index.ts 改 switch + import 两处。
dispatcher smoke test 同步改成验「文件 ⊆ registry」+「每个 entry 有 execute」,
比之前扫 import 行更直接,registry 自己漏注册也能抓到。
CommandModule 形状暂时只 { execute },--help 集中化 / process.exit 收敛走
follow-up。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
按短期路线把 registry 也并进来了,commit 26e4b07:
|
CommandModule 加 helpKey: CliMessageKey 字段,index.ts dispatcher 在调
execute 前扫 --help / -h,命中就 print + exit 0。15 个 command 文件里 11 处
inline `if (argv.includes('--help'))` 全部删除。
顺手修两个旧 bug:
1. `omk bench evolve --help` / `omk bench init --help` 之前会被
parseArgsStrictOrExit 当 unknown option 报错(它俩没像 run/report 那样
inline 拦 --help)。dispatcher 集中处理后任何子命令都响应。
2. `omk bench gate --some-flag --help` 之前只在 argv[0] 位置看 --help,中间
位置会进 parseArgs 报错。dispatcher 扫整个 argv,任何位置都响应。
回归测试覆盖三条改进。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
新加 src/cli/cli-exit.ts 暴露 CliExit Error 类。command 文件 / parse-strict / _shared 里 ~62 处 process.exit(N) 替换为 throw new CliExit(N)。index.ts main() 顶层 .catch 把 CliExit 转回 process.exit(code),其他错误打印 stack + exit 1。 唯一保留 process.exit 的地方:bench report --dev 子进程 spawn 的 exit listener (commands/report.ts) — 那是 async callback, 不在 main 调用栈,throw 出去 没人 catch。注释里写明原因。 收益:execute() 单测可以直接 try/catch 命中 CliExit 拿 exit code,不再 kill 测试进程。补 test/cli/cli-exit.test.ts 演示三条:verdict 缺 reportId → CliExit(1) / gold 缺子命令 → CliExit(1) / parseArgsStrictOrExit 未知 flag → CliExit(2)。 行为不变:CLI 用户感知层面所有 exit code 完全一致,bench / doctor / verdict 等的 0/1/2 语义全部保留。 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
短期路线最后两件事都做完,分两个 commit: b9d8c43 —
11f6c84 —
CLI 用户感知 exit code 完全一致(0/1/2 全部保留)。1071/1071 测试全绿。 |
| throw new CliExit(0); | ||
| } | ||
| throw new CliExit(1); | ||
| } catch (err: unknown) { |
There was a problem hiding this comment.
P1: 这里会把上面正常的 throw new CliExit(0) / throw new CliExit(1) 全部吞掉并改成 exit 1。process.exit(0) 改成 CliExit 后不再会绕过 catch,所以 gate 的 dry-run、PROGRESS、SOLO PASS 路径都会进入这个 catch,打印 Error: CliExit(0),最终失败。omk bench gate && deploy 会因此在通过时也挡住。这里需要先 if (err instanceof CliExit) throw err;,只把真实运行错误包装成 CliExit(1),并补一个 gate pass/dry-run 的 exit-code 回归测试。
There was a problem hiding this comment.
P1 + P3 都修了,commit f908ac6:
gate / run / evolve / doctor 的 catch 全部加 if (err instanceof CliExit) throw err;,只把真正运行时错误包成 CliExit(1)。gate 是真 bug——verdict 路径的 CliExit(0) 之前会被 catch 改成 CliExit(1),PROGRESS / SOLO PASS / dry-run 全 exit 1,omk bench gate && deploy 在 PASS 时也会挡住部署。
parseJudgeModelsArgOrExit 改成 throw new CliExit(2)(之前漏改)。
回归测试两条:
test/cli.test.ts:bench gate --dry-run退 0(反例:旧代码会被 catch 改成 exit 1)test/cli/cli-exit.test.ts:parseJudgeModelsArgOrExit('claude')抛CliExit(2)
1073/1073 全绿。
|
补充一个 P3(一致性,不阻塞):
建议把这个 helper 也改成 |
gate / run / evolve / doctor 的 try/catch 之前会把 CliExit 一起当 generic
错误吞掉,再 throw CliExit(1)。gate 这条最严重 — verdict 路径 dry-run /
PROGRESS / SOLO PASS 都 throw CliExit(0),全部被 catch 改成 exit 1,
`omk bench gate && deploy` 在 PASS 时也会挡住部署。
四个 catch 都加 `if (err instanceof CliExit) throw err;`,只把真实运行时
错误包成 CliExit(1)。
parseJudgeModelsArgOrExit 之前漏改,仍直接 process.exit(2)。改成
throw new CliExit(2),让 bench run / evolve / failures / debias-validate
在带非法 --judge-models 单测时也不会 kill 测试进程。
回归测试:
- bench gate --dry-run 退 0 (反例:之前会被 generic catch 改成 exit 1)
- parseJudgeModelsArgOrExit('claude') 抛 CliExit(2)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
复查最新 head
我本地跑了定向测试: 本轮没有新的阻塞发现。 |
自审 PR diff 时发现两类残留: 1. gen-samples / gold 的 catch 也是「catch + throw CliExit(1) without instanceof guard」,跟 gate / run / evolve / doctor 同性质。lib 调用 (generateSamples / initGoldDataset)目前不抛 CliExit,所以不是真 bug, 但加 guard 保持 6 个 catch 行为一致。 2. 拆 commands 之后,parse-run-config.ts 注释 / layer-gates.ts 注释 / README.md / README.zh.md 里仍有 handleRun / handleGate 旧名引用。 改成 commands/run.ts / commands/gate.ts。 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Summary
src/cli/index.ts1748 → 99 行,纯 dispatcher(domain/command 路由 + i18n + 顶层错误处理)handle*函数全部抽到src/cli/commands/<name>.ts,每个文件 export 一个execute(argv: string[])EvalResult/ReportServer/requireEvaluationReport/parseLastWindow)下沉到src/cli/commands/_shared.ts用户影响
无。纯代码组织重构:
yarn lint && yarn build && yarn test全绿(1062/1062)迁移说明
无。对外 CLI 表面无变化。后续添加新命令的工序:
src/cli/commands/<name>.ts,exportasync function execute(argv)src/cli/index.ts加一行import+ 一个case文件清单
🤖 Generated with Claude Code