Four ways in, depending on what you have: a shell agent driving bench.mjs, a direct-API loop for models with only an endpoint (plain text in, one JSON line out, so weak and local models can play), a rule-following bot that sweeps 1000 levels in 12 seconds, and a folder you can hand to someone else. bench-kit.mjs builds that folder: CLI, engine, level pack, observer page and tile atlas, about 1.5 MB, needing only Node 20 — no repo, no install, no network. The engine files are discovered by following imports so a new module can't be left behind, every path rewrite is asserted so a rename fails the build loudly, and the builder plays a real game inside the result before declaring it done. bench-report.mjs aggregates two ways: per level (is this level any good) and per model (how does this agent play). bench-import.mjs brings runs played elsewhere back in under a tag. bench-pack.mjs is the earlier, smaller export with no observer page; superseded by bench-kit but kept in history rather than dropped silently. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
34 lines
1.9 KiB
JavaScript
34 lines
1.9 KiB
JavaScript
#!/usr/bin/env node
|
|
// Bring runs played elsewhere (a bench-pack folder, another machine, another
|
|
// harness) into bench/runs so the observer page and the report can see them.
|
|
// node scripts/bench-import.mjs --from=bench/pack/runs [--tag=grok] [--force]
|
|
import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
import { resolve } from 'node:path';
|
|
import { rebuildIndex } from './bench-index.mjs';
|
|
|
|
const flags = Object.fromEntries(process.argv.slice(2).map(a => { const [k, ...v] = a.replace(/^--/, '').split('='); return [k, v.length ? v.join('=') : true]; }));
|
|
if (!flags.from) { console.error('需要 --from=<存放 runs 的目录>'); process.exit(1); }
|
|
const from = resolve(String(flags.from));
|
|
const to = resolve('bench/runs');
|
|
if (!existsSync(from)) { console.error(`没有 ${from}`); process.exit(1); }
|
|
mkdirSync(to, { recursive: true });
|
|
|
|
const tag = flags.tag ? String(flags.tag).replace(/[^\w.-]+/g, '-') : null;
|
|
let imported = 0, skipped = 0, bad = 0;
|
|
for (const file of readdirSync(from)) {
|
|
if (!file.endsWith('.json') || file === 'index.json') continue;
|
|
let run;
|
|
try { run = JSON.parse(readFileSync(resolve(from, file), 'utf8')); } catch { bad++; continue; }
|
|
if (!run?.run || !run.level?.cells || !Array.isArray(run.steps)) { bad++; continue; }
|
|
const name = tag ? `${tag}-${run.run}` : run.run;
|
|
const target = resolve(to, `${name}.json`);
|
|
if (existsSync(target) && !flags.force) { skipped++; continue; }
|
|
run.run = name;
|
|
run.imported = { from: String(flags.from), at: new Date().toISOString() };
|
|
writeFileSync(target, JSON.stringify(run));
|
|
imported++;
|
|
}
|
|
const index = rebuildIndex();
|
|
console.log(`导入 ${imported} 局${skipped ? `,跳过 ${skipped} 局(已存在,用 --force 覆盖)` : ''}${bad ? `,忽略 ${bad} 个无效文件` : ''}`);
|
|
console.log(`bench/runs 现有 ${index.length} 局 · 汇总:node scripts/bench-report.mjs`);
|