Files
HexTrails/scripts/bench-import.mjs
T
cabinwandClaude Opus 5 f3e91e3597 feat(bench): batch runners, aggregation and a standalone kit
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>
2026-09-14 14:00:33 +08:00

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`);