bridgebench 3.1.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CITATION.cff +15 -0
- package/LICENSE +21 -0
- package/README.md +249 -0
- package/dist/chunk-4TWPCPRP.cjs +1097 -0
- package/dist/chunk-4TWPCPRP.cjs.map +1 -0
- package/dist/chunk-7YCJSOK7.cjs +398 -0
- package/dist/chunk-7YCJSOK7.cjs.map +1 -0
- package/dist/chunk-CIXITJW6.cjs +249 -0
- package/dist/chunk-CIXITJW6.cjs.map +1 -0
- package/dist/chunk-EQHRUV2I.js +1466 -0
- package/dist/chunk-EQHRUV2I.js.map +1 -0
- package/dist/chunk-JTVNKSMO.js +1096 -0
- package/dist/chunk-JTVNKSMO.js.map +1 -0
- package/dist/chunk-LFKEV2YL.js +398 -0
- package/dist/chunk-LFKEV2YL.js.map +1 -0
- package/dist/chunk-NJTYVNP4.cjs +1467 -0
- package/dist/chunk-NJTYVNP4.cjs.map +1 -0
- package/dist/chunk-UECBSKTD.js +244 -0
- package/dist/chunk-UECBSKTD.js.map +1 -0
- package/dist/cli.cjs +409 -0
- package/dist/cli.cjs.map +1 -0
- package/dist/cli.d.cts +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +408 -0
- package/dist/cli.js.map +1 -0
- package/dist/client.cjs +42 -0
- package/dist/client.cjs.map +1 -0
- package/dist/client.d.cts +93 -0
- package/dist/client.d.ts +93 -0
- package/dist/client.js +42 -0
- package/dist/client.js.map +1 -0
- package/dist/contracts/index.cjs +47 -0
- package/dist/contracts/index.cjs.map +1 -0
- package/dist/contracts/index.d.cts +14 -0
- package/dist/contracts/index.d.ts +14 -0
- package/dist/contracts/index.js +47 -0
- package/dist/contracts/index.js.map +1 -0
- package/dist/index.cjs +171 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +214 -0
- package/dist/index.d.ts +214 -0
- package/dist/index.js +171 -0
- package/dist/index.js.map +1 -0
- package/dist/logger-CCR9Mg1c.d.cts +319 -0
- package/dist/logger-QJU7SBDz.d.ts +319 -0
- package/dist/reports-4CejmOHf.d.cts +454 -0
- package/dist/reports-s2CTnGN8.d.ts +454 -0
- package/dist/tasks-CpaCJ6JE.d.cts +151 -0
- package/dist/tasks-CpaCJ6JE.d.ts +151 -0
- package/dist/tasks.cjs +22 -0
- package/dist/tasks.cjs.map +1 -0
- package/dist/tasks.d.cts +39 -0
- package/dist/tasks.d.ts +39 -0
- package/dist/tasks.js +22 -0
- package/dist/tasks.js.map +1 -0
- package/docs/README.md +25 -0
- package/docs/glossary.md +49 -0
- package/docs/methodology.md +58 -0
- package/docs/private-packs.md +74 -0
- package/docs/replay-elo.md +79 -0
- package/docs/task-authoring.md +80 -0
- package/package.json +137 -0
- package/tasks/hallucination/public/boundary-coverage-audit.yaml +274 -0
- package/tasks/hallucination/public/boundary-migration-audit.yaml +284 -0
- package/tasks/hallucination/public/conflict-dependency-versions.yaml +324 -0
- package/tasks/hallucination/public/conflict-runbook-versions.yaml +229 -0
- package/tasks/hallucination/public/fabrication-agent-tools.yaml +224 -0
- package/tasks/hallucination/public/fabrication-api-surface.yaml +239 -0
- package/tasks/hallucination/public/fidelity-commit-attribution.yaml +304 -0
- package/tasks/hallucination/public/fidelity-config-drift.yaml +307 -0
- package/tasks/hallucination/public/missing-deploy-window.yaml +204 -0
- package/tasks/hallucination/public/missing-latency-baseline.yaml +239 -0
- package/tasks/hallucination/public/premise-quota-breach.yaml +202 -0
- package/tasks/hallucination/public/premise-rollback-cause.yaml +235 -0
- package/tasks/reasoning/public/constraint-capacity-allocation.yaml +196 -0
- package/tasks/reasoning/public/constraint-deployment-policy.yaml +203 -0
- package/tasks/reasoning/public/counterexample-authorization-rule.yaml +278 -0
- package/tasks/reasoning/public/counterexample-scheduler-starvation.yaml +290 -0
- package/tasks/reasoning/public/root-cache-tenant-leak.yaml +225 -0
- package/tasks/reasoning/public/root-event-ordering.yaml +184 -0
- package/tasks/reasoning/public/stateful-lease-handoff.yaml +213 -0
- package/tasks/reasoning/public/stateful-retry-budget.yaml +222 -0
- package/tasks/reasoning/public/synthesis-api-contract.yaml +214 -0
- package/tasks/reasoning/public/synthesis-permission-migration.yaml +190 -0
- package/tasks/reasoning/public/uncertainty-conflicting-telemetry.yaml +242 -0
- package/tasks/reasoning/public/uncertainty-incomplete-incident.yaml +223 -0
package/dist/cli.js
ADDED
|
@@ -0,0 +1,408 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
publishJournal,
|
|
4
|
+
publishTarget,
|
|
5
|
+
publishTasks,
|
|
6
|
+
resolveApiConfig,
|
|
7
|
+
runRemoteArena
|
|
8
|
+
} from "./chunk-LFKEV2YL.js";
|
|
9
|
+
import {
|
|
10
|
+
ArenaRunner,
|
|
11
|
+
ArenaStore,
|
|
12
|
+
ENGINE_VERSION,
|
|
13
|
+
PACKAGE_NAME,
|
|
14
|
+
categoryStoreConfig,
|
|
15
|
+
formatTriage,
|
|
16
|
+
triageJournal,
|
|
17
|
+
verifyJournal,
|
|
18
|
+
writeReports
|
|
19
|
+
} from "./chunk-EQHRUV2I.js";
|
|
20
|
+
import {
|
|
21
|
+
FileArenaLogger,
|
|
22
|
+
OpenRouterClient,
|
|
23
|
+
TaskLoader,
|
|
24
|
+
findProjectRoot,
|
|
25
|
+
listModels,
|
|
26
|
+
redactSecrets,
|
|
27
|
+
resolveCompetitorRoster,
|
|
28
|
+
sanitizeError,
|
|
29
|
+
validatePublicTaskFile
|
|
30
|
+
} from "./chunk-JTVNKSMO.js";
|
|
31
|
+
import {
|
|
32
|
+
BenchmarkCategorySchema,
|
|
33
|
+
CATEGORIES
|
|
34
|
+
} from "./chunk-UECBSKTD.js";
|
|
35
|
+
|
|
36
|
+
// src/commands.ts
|
|
37
|
+
import path2 from "path";
|
|
38
|
+
import { Command, InvalidArgumentError } from "commander";
|
|
39
|
+
|
|
40
|
+
// src/env.ts
|
|
41
|
+
import { existsSync } from "fs";
|
|
42
|
+
import path from "path";
|
|
43
|
+
var ENV_PATH = path.join(findProjectRoot(import.meta.url), ".env");
|
|
44
|
+
function loadProjectEnv() {
|
|
45
|
+
if (!existsSync(ENV_PATH)) return { status: "missing" };
|
|
46
|
+
try {
|
|
47
|
+
process.loadEnvFile(ENV_PATH);
|
|
48
|
+
return { status: "loaded" };
|
|
49
|
+
} catch (error) {
|
|
50
|
+
return {
|
|
51
|
+
status: "error",
|
|
52
|
+
reason: error instanceof Error ? error.message : String(error)
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// src/commands.ts
|
|
58
|
+
var ROOT = process.cwd();
|
|
59
|
+
var defaultDependencies = {
|
|
60
|
+
stdout: (message) => console.log(message),
|
|
61
|
+
stderr: (message) => console.error(message),
|
|
62
|
+
setExitCode: (code) => {
|
|
63
|
+
process.exitCode = code;
|
|
64
|
+
},
|
|
65
|
+
createStore: (category) => new ArenaStore(categoryStoreConfig(category)),
|
|
66
|
+
createTaskLoader: (category) => new TaskLoader(category),
|
|
67
|
+
createOpenRouter: (logger) => new OpenRouterClient(process.env.OPENROUTER_API_KEY ?? "", logger),
|
|
68
|
+
publishTasks,
|
|
69
|
+
publishJournal,
|
|
70
|
+
resolveApiConfig
|
|
71
|
+
};
|
|
72
|
+
function parseCategory(value) {
|
|
73
|
+
const parsed = BenchmarkCategorySchema.safeParse(value);
|
|
74
|
+
if (!parsed.success) {
|
|
75
|
+
throw new InvalidArgumentError(`expected one of ${CATEGORIES.join(", ")}, received ${value}`);
|
|
76
|
+
}
|
|
77
|
+
return parsed.data;
|
|
78
|
+
}
|
|
79
|
+
function positiveInteger(value) {
|
|
80
|
+
const parsed = Number(value);
|
|
81
|
+
if (!Number.isInteger(parsed) || parsed < 1) {
|
|
82
|
+
throw new InvalidArgumentError(`expected a positive integer, received ${value}`);
|
|
83
|
+
}
|
|
84
|
+
return parsed;
|
|
85
|
+
}
|
|
86
|
+
function positiveNumber(value) {
|
|
87
|
+
const parsed = Number(value);
|
|
88
|
+
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
89
|
+
throw new InvalidArgumentError(`expected a positive number, received ${value}`);
|
|
90
|
+
}
|
|
91
|
+
return parsed;
|
|
92
|
+
}
|
|
93
|
+
function collectOption(value, previous = []) {
|
|
94
|
+
return [...previous, value];
|
|
95
|
+
}
|
|
96
|
+
function arenaRunConfigFromOptions(options) {
|
|
97
|
+
const competitorIds = options.competitor?.length ? resolveCompetitorRoster(options.competitor).map((model) => model.id) : void 0;
|
|
98
|
+
return {
|
|
99
|
+
category: options.category,
|
|
100
|
+
seed: options.seed,
|
|
101
|
+
matches: options.matches,
|
|
102
|
+
maxCostUsd: options.maxCostUsd,
|
|
103
|
+
competitorIds,
|
|
104
|
+
resume: options.resume,
|
|
105
|
+
healthStop: options.healthStop
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
function selectedCategories(options, command) {
|
|
109
|
+
if (options.category && options.all) {
|
|
110
|
+
throw new Error(`${command} accepts either --category or --all, not both`);
|
|
111
|
+
}
|
|
112
|
+
if (options.category) return [options.category];
|
|
113
|
+
if (options.all) return [...CATEGORIES];
|
|
114
|
+
throw new Error(`${command} requires --category <category> or --all`);
|
|
115
|
+
}
|
|
116
|
+
function logRoot(category) {
|
|
117
|
+
return path2.join(ROOT, "results", category, "logs");
|
|
118
|
+
}
|
|
119
|
+
function displayPath(target) {
|
|
120
|
+
const relative = path2.relative(ROOT, target);
|
|
121
|
+
return relative.startsWith("..") ? path2.basename(target) : relative;
|
|
122
|
+
}
|
|
123
|
+
function buildProgram(overrides = {}) {
|
|
124
|
+
const dependencies = { ...defaultDependencies, ...overrides };
|
|
125
|
+
const program = new Command().name(PACKAGE_NAME).description("Autonomous BridgeBench arenas for reasoning and hallucination").version(ENGINE_VERSION).showHelpAfterError().configureHelp({ sortOptions: true, sortSubcommands: true }).configureOutput({
|
|
126
|
+
writeOut: (value) => dependencies.stdout(value.replace(/\n$/, "")),
|
|
127
|
+
writeErr: (value) => dependencies.stderr(value.replace(/\n$/, ""))
|
|
128
|
+
});
|
|
129
|
+
const models = program.command("models").description("Inspect or validate the pinned model registry");
|
|
130
|
+
models.command("list").description("List enabled competitors and judges").action(() => {
|
|
131
|
+
for (const model of listModels()) {
|
|
132
|
+
dependencies.stdout(`${model.role.padEnd(10)} ${model.id.padEnd(40)} ${model.displayName}`);
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
models.command("validate").description("Validate pinned model IDs and judge capabilities against OpenRouter").action(async () => {
|
|
136
|
+
const client = dependencies.createOpenRouter();
|
|
137
|
+
for (const model of listModels()) {
|
|
138
|
+
await client.validateModel(model);
|
|
139
|
+
dependencies.stdout(`\u2713 ${model.id} -> ${model.canonicalSlug}`);
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
const tasks = program.command("tasks").description("Validate or publish task packs");
|
|
143
|
+
tasks.command("validate").description("Validate public task schemas and pack balance").option(
|
|
144
|
+
"-c, --category <category>",
|
|
145
|
+
`limit to one category (${CATEGORIES.join(", ")})`,
|
|
146
|
+
parseCategory
|
|
147
|
+
).option("--file <path>", "validate one public task without pack-balance checks").action(async (options) => {
|
|
148
|
+
if (options.file) {
|
|
149
|
+
if (options.category) {
|
|
150
|
+
throw new Error("tasks validate accepts --file or --category, not both");
|
|
151
|
+
}
|
|
152
|
+
const task = await validatePublicTaskFile(options.file);
|
|
153
|
+
dependencies.stdout(
|
|
154
|
+
`\u2713 ${task.public.id}: public task validated (${task.public.category}/${task.public.cluster})`
|
|
155
|
+
);
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
const categories = options.category ? [options.category] : CATEGORIES;
|
|
159
|
+
for (const category of categories) {
|
|
160
|
+
const loader = dependencies.createTaskLoader(category);
|
|
161
|
+
const loaded = await loader.loadAll();
|
|
162
|
+
const mode = loader.hasPrivate ? "public + private halves" : "public halves only";
|
|
163
|
+
dependencies.stdout(`\u2713 ${category}: ${loaded.length} tasks validated (${mode})`);
|
|
164
|
+
for (const task of loaded) {
|
|
165
|
+
dependencies.stdout(` ${task.public.cluster.padEnd(28)} ${task.public.id}`);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
tasks.command("publish").description("Publish public and private task halves to the configured API").option(
|
|
170
|
+
"-c, --category <category>",
|
|
171
|
+
`category to publish (${CATEGORIES.join(", ")})`,
|
|
172
|
+
parseCategory
|
|
173
|
+
).option("--all", "publish every category explicitly", false).action(async (options) => {
|
|
174
|
+
const categories = selectedCategories(options, "tasks publish");
|
|
175
|
+
const config = dependencies.resolveApiConfig();
|
|
176
|
+
dependencies.stdout(`Publishing ${categories.join(", ")} tasks to ${publishTarget(config)}`);
|
|
177
|
+
for (const category of categories) {
|
|
178
|
+
const result = await dependencies.publishTasks(category, config);
|
|
179
|
+
dependencies.stdout(`\u2713 ${category}: published ${result.imported} tasks`);
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
const arena = program.command("arena").description("Run and inspect arena matches");
|
|
183
|
+
arena.command("run").description("Run a paid deterministic match schedule").requiredOption(
|
|
184
|
+
"-c, --category <category>",
|
|
185
|
+
`arena to run (${CATEGORIES.join(", ")})`,
|
|
186
|
+
parseCategory
|
|
187
|
+
).option("-n, --matches <count>", "number of task-level matches", positiveInteger, 12).option("-s, --seed <seed>", "reproducible scheduling seed", "bridgebench-v3-mvp").option(
|
|
188
|
+
"--max-cost-usd <amount>",
|
|
189
|
+
"hard stop before the next match after this spend",
|
|
190
|
+
positiveNumber,
|
|
191
|
+
25
|
|
192
|
+
).option(
|
|
193
|
+
"--competitor <modelId>",
|
|
194
|
+
"limit the run roster; repeat for each competitor (minimum two)",
|
|
195
|
+
collectOption
|
|
196
|
+
).option("--resume", "skip match IDs already present in the journal", false).option("--debug", "mirror structured log entries to the console", false).option("--no-health-stop", "do not halt when most matches have failed responses").action(async (options) => {
|
|
197
|
+
const config = arenaRunConfigFromOptions(options);
|
|
198
|
+
const logger = new FileArenaLogger({
|
|
199
|
+
dir: logRoot(config.category),
|
|
200
|
+
verbose: options.debug
|
|
201
|
+
});
|
|
202
|
+
const cancellation = new AbortController();
|
|
203
|
+
const requestCancellation = () => {
|
|
204
|
+
if (cancellation.signal.aborted) return;
|
|
205
|
+
dependencies.stderr("Cancellation requested; aborting active model calls.");
|
|
206
|
+
cancellation.abort();
|
|
207
|
+
};
|
|
208
|
+
process.once("SIGINT", requestCancellation);
|
|
209
|
+
try {
|
|
210
|
+
dependencies.stdout(`Run log: ${displayPath(logger.filePath)}`);
|
|
211
|
+
const store = dependencies.createStore(config.category);
|
|
212
|
+
const loaded = await dependencies.createTaskLoader(config.category).loadAll({ requirePrivate: true });
|
|
213
|
+
const progressOutput = (event) => {
|
|
214
|
+
if (event.type !== "match.completed") return;
|
|
215
|
+
dependencies.stdout(
|
|
216
|
+
`[${event.data.completed}/${event.data.total}] ${event.data.taskId}: ${event.data.winnerModelId ?? "no contest"} (${event.data.outcome}, $${event.data.costUsd.toFixed(4)})`
|
|
217
|
+
);
|
|
218
|
+
};
|
|
219
|
+
const runner = new ArenaRunner(
|
|
220
|
+
dependencies.createOpenRouter(logger),
|
|
221
|
+
store,
|
|
222
|
+
progressOutput,
|
|
223
|
+
logger
|
|
224
|
+
);
|
|
225
|
+
const result = await runner.run(config, loaded, { signal: cancellation.signal });
|
|
226
|
+
if (result.cancelled) {
|
|
227
|
+
dependencies.stdout(
|
|
228
|
+
`Cancelled after ${result.completed} new ${config.category} matches; journaled spend $${result.costUsd.toFixed(4)}. Completed matches remain journaled.`
|
|
229
|
+
);
|
|
230
|
+
dependencies.setExitCode(130);
|
|
231
|
+
} else {
|
|
232
|
+
dependencies.stdout(
|
|
233
|
+
`Completed ${result.completed} new ${config.category} matches; run spend $${result.costUsd.toFixed(4)}.`
|
|
234
|
+
);
|
|
235
|
+
if (result.stoppedForBudget) {
|
|
236
|
+
dependencies.stdout(
|
|
237
|
+
"Stopped at the cost boundary; rerun the same command with --resume."
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
const runMatches = store.readAll().filter((match) => match.runId === result.runId);
|
|
242
|
+
if (runMatches.length > 0) {
|
|
243
|
+
const triage = triageJournal(runMatches);
|
|
244
|
+
dependencies.stdout(`=== Run health ===
|
|
245
|
+
${formatTriage(triage)}`);
|
|
246
|
+
}
|
|
247
|
+
} finally {
|
|
248
|
+
process.removeListener("SIGINT", requestCancellation);
|
|
249
|
+
dependencies.stdout(`Run log: ${displayPath(logger.filePath)}`);
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
arena.command("remote-run").description("Run a match schedule against the configured API and stream live arena events").requiredOption(
|
|
253
|
+
"-c, --category <category>",
|
|
254
|
+
`arena to run (${CATEGORIES.join(", ")})`,
|
|
255
|
+
parseCategory
|
|
256
|
+
).option("-n, --matches <count>", "number of task-level matches", positiveInteger, 12).option("-s, --seed <seed>", "reproducible scheduling seed", "bridgebench-v3-mvp").option(
|
|
257
|
+
"--max-cost-usd <amount>",
|
|
258
|
+
"hard stop before the next match after this spend",
|
|
259
|
+
positiveNumber,
|
|
260
|
+
25
|
|
261
|
+
).option(
|
|
262
|
+
"--competitor <modelId>",
|
|
263
|
+
"limit the run roster; repeat for each competitor (minimum two)",
|
|
264
|
+
collectOption
|
|
265
|
+
).option("--resume", "skip match IDs already present in the journal", false).option("--debug", "mirror structured log entries to the console", false).option("--no-health-stop", "do not halt when most matches have failed responses").option("--mock", "use deterministic mock model completions instead of OpenRouter", false).option("--no-publish-matches", "skip publishing match results to the API as they complete").action(async (options) => {
|
|
266
|
+
const config = arenaRunConfigFromOptions(options);
|
|
267
|
+
const apiConfig = dependencies.resolveApiConfig();
|
|
268
|
+
const logger = new FileArenaLogger({
|
|
269
|
+
dir: logRoot(config.category),
|
|
270
|
+
verbose: options.debug
|
|
271
|
+
});
|
|
272
|
+
dependencies.stdout(
|
|
273
|
+
`Remote ${config.category} run against ${publishTarget(apiConfig)} (${options.mock ? "mock" : "openrouter"})`
|
|
274
|
+
);
|
|
275
|
+
dependencies.stdout(`Run log: ${displayPath(logger.filePath)}`);
|
|
276
|
+
try {
|
|
277
|
+
const result = await runRemoteArena(apiConfig, {
|
|
278
|
+
config,
|
|
279
|
+
mock: options.mock,
|
|
280
|
+
publishMatches: options.publishMatches,
|
|
281
|
+
logger
|
|
282
|
+
});
|
|
283
|
+
dependencies.stdout(
|
|
284
|
+
`Remote run ${result.runKey}: ${result.completed} matches, $${result.costUsd.toFixed(4)}`
|
|
285
|
+
);
|
|
286
|
+
if (result.cancelled) dependencies.setExitCode(130);
|
|
287
|
+
} finally {
|
|
288
|
+
dependencies.stdout(`Run log: ${displayPath(logger.filePath)}`);
|
|
289
|
+
}
|
|
290
|
+
});
|
|
291
|
+
arena.command("verify").description("Validate and replay outcomes, points, manifests, and Elo").option(
|
|
292
|
+
"-c, --category <category>",
|
|
293
|
+
`journal category (${CATEGORIES.join(", ")})`,
|
|
294
|
+
parseCategory,
|
|
295
|
+
"reasoning"
|
|
296
|
+
).option("--journal <path>", "journal file to verify (defaults to the local category journal)").option("--manifests-dir <path>", "run-manifest directory (defaults to a runs/ sibling)").option("--json", "emit the machine-readable verification result", false).action(
|
|
297
|
+
(options) => {
|
|
298
|
+
const defaults = categoryStoreConfig(options.category);
|
|
299
|
+
const journalPath = options.journal ? path2.resolve(options.journal) : defaults.journalPath;
|
|
300
|
+
const runsDir = options.manifestsDir ? path2.resolve(options.manifestsDir) : options.journal ? path2.join(path2.dirname(journalPath), "runs") : defaults.runsDir;
|
|
301
|
+
const store = new ArenaStore({
|
|
302
|
+
...defaults,
|
|
303
|
+
journalPath,
|
|
304
|
+
runsDir,
|
|
305
|
+
readOnly: true
|
|
306
|
+
});
|
|
307
|
+
const verified = verifyJournal(store.readAll(), options.category, {
|
|
308
|
+
manifestForRun: (runId) => store.readRunManifest(runId),
|
|
309
|
+
requireManifests: true
|
|
310
|
+
});
|
|
311
|
+
if (options.json) {
|
|
312
|
+
dependencies.stdout(JSON.stringify(verified, null, 2));
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
dependencies.stdout(
|
|
316
|
+
`\u2713 ${options.category}: verified ${verified.matches.length} matches across ${verified.runs.length} runs`
|
|
317
|
+
);
|
|
318
|
+
for (const warning of verified.warnings) {
|
|
319
|
+
dependencies.stderr(`Warning: ${warning}`);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
);
|
|
323
|
+
arena.command("publish").description("Publish a verified match journal to the configured API").option(
|
|
324
|
+
"-c, --category <category>",
|
|
325
|
+
`journal to publish (${CATEGORIES.join(", ")})`,
|
|
326
|
+
parseCategory
|
|
327
|
+
).option("--all", "publish every category explicitly", false).option("--allow-empty", "treat an empty journal as a successful no-op", false).action(async (options) => {
|
|
328
|
+
const categories = selectedCategories(options, "arena publish");
|
|
329
|
+
const config = dependencies.resolveApiConfig();
|
|
330
|
+
dependencies.stdout(
|
|
331
|
+
`Publishing ${categories.join(", ")} journals to ${publishTarget(config)}`
|
|
332
|
+
);
|
|
333
|
+
for (const category of categories) {
|
|
334
|
+
const store = dependencies.createStore(category);
|
|
335
|
+
verifyJournal(store.readAll(), category, {
|
|
336
|
+
manifestForRun: (runId) => store.readRunManifest(runId),
|
|
337
|
+
requireManifests: true
|
|
338
|
+
});
|
|
339
|
+
const result = await dependencies.publishJournal(category, config);
|
|
340
|
+
if (result.matches === 0 && !options.allowEmpty) {
|
|
341
|
+
throw new Error(
|
|
342
|
+
`The ${category} journal is empty; pass --allow-empty for an intentional no-op`
|
|
343
|
+
);
|
|
344
|
+
}
|
|
345
|
+
dependencies.stdout(
|
|
346
|
+
`\u2713 ${category}: ${result.imported} new, ${result.skipped} already present (of ${result.matches} lines)`
|
|
347
|
+
);
|
|
348
|
+
}
|
|
349
|
+
});
|
|
350
|
+
arena.command("triage").description("Analyze failures, suspicious responses, and judging anomalies").option(
|
|
351
|
+
"-c, --category <category>",
|
|
352
|
+
`journal to analyze (${CATEGORIES.join(", ")})`,
|
|
353
|
+
parseCategory,
|
|
354
|
+
"reasoning"
|
|
355
|
+
).option("--run <runId>", "limit the report to one run ID").option("--json", "emit the machine-readable report", false).option("--strict", "exit nonzero when any anomaly is found", false).option("--allow-empty", "treat no matching journal lines as success", false).action(
|
|
356
|
+
(options) => {
|
|
357
|
+
const results = dependencies.createStore(options.category).readAll().filter((match) => !options.run || match.runId === options.run);
|
|
358
|
+
if (results.length === 0) {
|
|
359
|
+
const message = options.run ? `No ${options.category} matches found for run ${options.run}.` : `The ${options.category} journal is empty.`;
|
|
360
|
+
if (!options.allowEmpty) throw new Error(message);
|
|
361
|
+
dependencies.stdout(message);
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
const reports = triageJournal(results);
|
|
365
|
+
dependencies.stdout(
|
|
366
|
+
options.json ? JSON.stringify(reports, null, 2) : formatTriage(reports)
|
|
367
|
+
);
|
|
368
|
+
const anomalies = reports.reduce((sum, report) => sum + report.anomalies.length, 0);
|
|
369
|
+
if (options.strict && anomalies > 0) {
|
|
370
|
+
dependencies.stderr(`Strict mode: ${anomalies} anomalies found.`);
|
|
371
|
+
dependencies.setExitCode(1);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
);
|
|
375
|
+
arena.command("generation <generationId>").description("Fetch OpenRouter's record for a journaled generation").action(async (generationId) => {
|
|
376
|
+
const data = await dependencies.createOpenRouter().fetchGeneration(generationId);
|
|
377
|
+
dependencies.stdout(JSON.stringify(data, null, 2));
|
|
378
|
+
});
|
|
379
|
+
program.command("report").description("Verify journals and rebuild JSON and Markdown reports").option(
|
|
380
|
+
"-c, --category <category>",
|
|
381
|
+
`limit to one category (${CATEGORIES.join(", ")})`,
|
|
382
|
+
parseCategory
|
|
383
|
+
).action((options) => {
|
|
384
|
+
const categories = options.category ? [options.category] : CATEGORIES;
|
|
385
|
+
for (const category of categories) {
|
|
386
|
+
const snapshot = writeReports(dependencies.createStore(category));
|
|
387
|
+
dependencies.stdout(`\u2713 ${category}: wrote reports for ${snapshot.matches.length} matches`);
|
|
388
|
+
}
|
|
389
|
+
});
|
|
390
|
+
return program;
|
|
391
|
+
}
|
|
392
|
+
async function runCli(argv = process.argv, overrides = {}) {
|
|
393
|
+
const dependencies = { ...defaultDependencies, ...overrides };
|
|
394
|
+
const envResult = loadProjectEnv();
|
|
395
|
+
if (envResult.status === "error") {
|
|
396
|
+
dependencies.stderr(
|
|
397
|
+
`Warning: project environment file could not be loaded: ${redactSecrets(envResult.reason)}`
|
|
398
|
+
);
|
|
399
|
+
}
|
|
400
|
+
await buildProgram(dependencies).parseAsync(argv);
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
// src/cli.ts
|
|
404
|
+
runCli().catch((error) => {
|
|
405
|
+
console.error(`BridgeBench: ${sanitizeError(error)}`);
|
|
406
|
+
process.exitCode = 1;
|
|
407
|
+
});
|
|
408
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands.ts","../src/env.ts","../src/cli.ts"],"sourcesContent":["import path from 'node:path';\n\nimport { Command, InvalidArgumentError } from 'commander';\n\nimport { ArenaRunner } from './arena.js';\nimport { loadProjectEnv } from './env.js';\nimport { FileArenaLogger, redactSecrets, type ArenaLogger } from './logger.js';\nimport { listModels, resolveCompetitorRoster } from './models.js';\nimport { OpenRouterClient } from './openrouter.js';\nimport { publishJournal, publishTarget, publishTasks, resolveApiConfig } from './publish.js';\nimport { runRemoteArena } from './remote-arena.js';\nimport { writeReports } from './report.js';\nimport { ArenaStore, categoryStoreConfig } from './store.js';\nimport { TaskLoader, validatePublicTaskFile } from './tasks.js';\nimport { formatTriage, triageJournal } from './triage.js';\nimport {\n BenchmarkCategorySchema,\n CATEGORIES,\n type ArenaEventSink,\n type ArenaRunConfig,\n type BenchmarkCategory,\n} from './types.js';\nimport { verifyJournal } from './verification.js';\nimport { ENGINE_VERSION, PACKAGE_NAME } from './version.js';\n\n// CLI logs and path display anchor to the operator's working directory,\n// matching where categoryStoreConfig writes results.\nconst ROOT = process.cwd();\n\ninterface CategorySelection {\n category?: BenchmarkCategory;\n all?: boolean;\n}\n\nexport interface ArenaRunCliOptions {\n category: BenchmarkCategory;\n matches: number;\n seed: string;\n maxCostUsd: number;\n competitor?: string[];\n resume: boolean;\n debug: boolean;\n healthStop: boolean;\n}\n\nexport interface CliDependencies {\n stdout: (message: string) => void;\n stderr: (message: string) => void;\n setExitCode: (code: number) => void;\n createStore: (category: BenchmarkCategory) => ArenaStore;\n createTaskLoader: (category: BenchmarkCategory) => TaskLoader;\n createOpenRouter: (logger?: ArenaLogger) => OpenRouterClient;\n publishTasks: typeof publishTasks;\n publishJournal: typeof publishJournal;\n resolveApiConfig: typeof resolveApiConfig;\n}\n\nconst defaultDependencies: CliDependencies = {\n stdout: (message) => console.log(message),\n stderr: (message) => console.error(message),\n setExitCode: (code) => {\n process.exitCode = code;\n },\n createStore: (category) => new ArenaStore(categoryStoreConfig(category)),\n createTaskLoader: (category) => new TaskLoader(category),\n createOpenRouter: (logger) => new OpenRouterClient(process.env.OPENROUTER_API_KEY ?? '', logger),\n publishTasks,\n publishJournal,\n resolveApiConfig,\n};\n\nfunction parseCategory(value: string): BenchmarkCategory {\n const parsed = BenchmarkCategorySchema.safeParse(value);\n if (!parsed.success) {\n throw new InvalidArgumentError(`expected one of ${CATEGORIES.join(', ')}, received ${value}`);\n }\n return parsed.data;\n}\n\nfunction positiveInteger(value: string): number {\n const parsed = Number(value);\n if (!Number.isInteger(parsed) || parsed < 1) {\n throw new InvalidArgumentError(`expected a positive integer, received ${value}`);\n }\n return parsed;\n}\n\nfunction positiveNumber(value: string): number {\n const parsed = Number(value);\n if (!Number.isFinite(parsed) || parsed <= 0) {\n throw new InvalidArgumentError(`expected a positive number, received ${value}`);\n }\n return parsed;\n}\n\nfunction collectOption(value: string, previous: string[] = []): string[] {\n return [...previous, value];\n}\n\nexport function arenaRunConfigFromOptions(options: ArenaRunCliOptions): ArenaRunConfig {\n const competitorIds = options.competitor?.length\n ? resolveCompetitorRoster(options.competitor).map((model) => model.id)\n : undefined;\n return {\n category: options.category,\n seed: options.seed,\n matches: options.matches,\n maxCostUsd: options.maxCostUsd,\n competitorIds,\n resume: options.resume,\n healthStop: options.healthStop,\n };\n}\n\nfunction selectedCategories(options: CategorySelection, command: string): BenchmarkCategory[] {\n if (options.category && options.all) {\n throw new Error(`${command} accepts either --category or --all, not both`);\n }\n if (options.category) return [options.category];\n if (options.all) return [...CATEGORIES];\n throw new Error(`${command} requires --category <category> or --all`);\n}\n\nfunction logRoot(category: BenchmarkCategory): string {\n return path.join(ROOT, 'results', category, 'logs');\n}\n\nfunction displayPath(target: string): string {\n const relative = path.relative(ROOT, target);\n return relative.startsWith('..') ? path.basename(target) : relative;\n}\n\nexport function buildProgram(overrides: Partial<CliDependencies> = {}): Command {\n const dependencies: CliDependencies = { ...defaultDependencies, ...overrides };\n const program = new Command()\n .name(PACKAGE_NAME)\n .description('Autonomous BridgeBench arenas for reasoning and hallucination')\n .version(ENGINE_VERSION)\n .showHelpAfterError()\n .configureHelp({ sortOptions: true, sortSubcommands: true })\n .configureOutput({\n writeOut: (value) => dependencies.stdout(value.replace(/\\n$/, '')),\n writeErr: (value) => dependencies.stderr(value.replace(/\\n$/, '')),\n });\n\n const models = program\n .command('models')\n .description('Inspect or validate the pinned model registry');\n models\n .command('list')\n .description('List enabled competitors and judges')\n .action(() => {\n for (const model of listModels()) {\n dependencies.stdout(`${model.role.padEnd(10)} ${model.id.padEnd(40)} ${model.displayName}`);\n }\n });\n models\n .command('validate')\n .description('Validate pinned model IDs and judge capabilities against OpenRouter')\n .action(async () => {\n const client = dependencies.createOpenRouter();\n for (const model of listModels()) {\n await client.validateModel(model);\n dependencies.stdout(`✓ ${model.id} -> ${model.canonicalSlug}`);\n }\n });\n\n const tasks = program.command('tasks').description('Validate or publish task packs');\n tasks\n .command('validate')\n .description('Validate public task schemas and pack balance')\n .option(\n '-c, --category <category>',\n `limit to one category (${CATEGORIES.join(', ')})`,\n parseCategory,\n )\n .option('--file <path>', 'validate one public task without pack-balance checks')\n .action(async (options: { category?: BenchmarkCategory; file?: string }) => {\n if (options.file) {\n if (options.category) {\n throw new Error('tasks validate accepts --file or --category, not both');\n }\n const task = await validatePublicTaskFile(options.file);\n dependencies.stdout(\n `✓ ${task.public.id}: public task validated (${task.public.category}/${task.public.cluster})`,\n );\n return;\n }\n const categories = options.category ? [options.category] : CATEGORIES;\n for (const category of categories) {\n const loader = dependencies.createTaskLoader(category);\n const loaded = await loader.loadAll();\n const mode = loader.hasPrivate ? 'public + private halves' : 'public halves only';\n dependencies.stdout(`✓ ${category}: ${loaded.length} tasks validated (${mode})`);\n for (const task of loaded) {\n dependencies.stdout(` ${task.public.cluster.padEnd(28)} ${task.public.id}`);\n }\n }\n });\n\n tasks\n .command('publish')\n .description('Publish public and private task halves to the configured API')\n .option(\n '-c, --category <category>',\n `category to publish (${CATEGORIES.join(', ')})`,\n parseCategory,\n )\n .option('--all', 'publish every category explicitly', false)\n .action(async (options: CategorySelection) => {\n const categories = selectedCategories(options, 'tasks publish');\n const config = dependencies.resolveApiConfig();\n dependencies.stdout(`Publishing ${categories.join(', ')} tasks to ${publishTarget(config)}`);\n for (const category of categories) {\n const result = await dependencies.publishTasks(category, config);\n dependencies.stdout(`✓ ${category}: published ${result.imported} tasks`);\n }\n });\n\n const arena = program.command('arena').description('Run and inspect arena matches');\n arena\n .command('run')\n .description('Run a paid deterministic match schedule')\n .requiredOption(\n '-c, --category <category>',\n `arena to run (${CATEGORIES.join(', ')})`,\n parseCategory,\n )\n .option('-n, --matches <count>', 'number of task-level matches', positiveInteger, 12)\n .option('-s, --seed <seed>', 'reproducible scheduling seed', 'bridgebench-v3-mvp')\n .option(\n '--max-cost-usd <amount>',\n 'hard stop before the next match after this spend',\n positiveNumber,\n 25,\n )\n .option(\n '--competitor <modelId>',\n 'limit the run roster; repeat for each competitor (minimum two)',\n collectOption,\n )\n .option('--resume', 'skip match IDs already present in the journal', false)\n .option('--debug', 'mirror structured log entries to the console', false)\n .option('--no-health-stop', 'do not halt when most matches have failed responses')\n .action(async (options: ArenaRunCliOptions) => {\n const config = arenaRunConfigFromOptions(options);\n const logger = new FileArenaLogger({\n dir: logRoot(config.category),\n verbose: options.debug,\n });\n const cancellation = new AbortController();\n const requestCancellation = (): void => {\n if (cancellation.signal.aborted) return;\n dependencies.stderr('Cancellation requested; aborting active model calls.');\n cancellation.abort();\n };\n process.once('SIGINT', requestCancellation);\n try {\n dependencies.stdout(`Run log: ${displayPath(logger.filePath)}`);\n const store = dependencies.createStore(config.category);\n const loaded = await dependencies\n .createTaskLoader(config.category)\n .loadAll({ requirePrivate: true });\n const progressOutput: ArenaEventSink = (event) => {\n if (event.type !== 'match.completed') return;\n dependencies.stdout(\n `[${event.data.completed}/${event.data.total}] ${event.data.taskId}: ${event.data.winnerModelId ?? 'no contest'} (${event.data.outcome}, $${event.data.costUsd.toFixed(4)})`,\n );\n };\n const runner = new ArenaRunner(\n dependencies.createOpenRouter(logger),\n store,\n progressOutput,\n logger,\n );\n const result = await runner.run(config, loaded, { signal: cancellation.signal });\n if (result.cancelled) {\n dependencies.stdout(\n `Cancelled after ${result.completed} new ${config.category} matches; journaled spend $${result.costUsd.toFixed(4)}. Completed matches remain journaled.`,\n );\n dependencies.setExitCode(130);\n } else {\n dependencies.stdout(\n `Completed ${result.completed} new ${config.category} matches; run spend $${result.costUsd.toFixed(4)}.`,\n );\n if (result.stoppedForBudget) {\n dependencies.stdout(\n 'Stopped at the cost boundary; rerun the same command with --resume.',\n );\n }\n }\n const runMatches = store.readAll().filter((match) => match.runId === result.runId);\n if (runMatches.length > 0) {\n const triage = triageJournal(runMatches);\n dependencies.stdout(`=== Run health ===\\n${formatTriage(triage)}`);\n }\n } finally {\n process.removeListener('SIGINT', requestCancellation);\n dependencies.stdout(`Run log: ${displayPath(logger.filePath)}`);\n }\n });\n\n arena\n .command('remote-run')\n .description('Run a match schedule against the configured API and stream live arena events')\n .requiredOption(\n '-c, --category <category>',\n `arena to run (${CATEGORIES.join(', ')})`,\n parseCategory,\n )\n .option('-n, --matches <count>', 'number of task-level matches', positiveInteger, 12)\n .option('-s, --seed <seed>', 'reproducible scheduling seed', 'bridgebench-v3-mvp')\n .option(\n '--max-cost-usd <amount>',\n 'hard stop before the next match after this spend',\n positiveNumber,\n 25,\n )\n .option(\n '--competitor <modelId>',\n 'limit the run roster; repeat for each competitor (minimum two)',\n collectOption,\n )\n .option('--resume', 'skip match IDs already present in the journal', false)\n .option('--debug', 'mirror structured log entries to the console', false)\n .option('--no-health-stop', 'do not halt when most matches have failed responses')\n .option('--mock', 'use deterministic mock model completions instead of OpenRouter', false)\n .option('--no-publish-matches', 'skip publishing match results to the API as they complete')\n .action(async (options: ArenaRunCliOptions & { mock: boolean; publishMatches: boolean }) => {\n const config = arenaRunConfigFromOptions(options);\n const apiConfig = dependencies.resolveApiConfig();\n const logger = new FileArenaLogger({\n dir: logRoot(config.category),\n verbose: options.debug,\n });\n dependencies.stdout(\n `Remote ${config.category} run against ${publishTarget(apiConfig)} (${options.mock ? 'mock' : 'openrouter'})`,\n );\n dependencies.stdout(`Run log: ${displayPath(logger.filePath)}`);\n try {\n const result = await runRemoteArena(apiConfig, {\n config,\n mock: options.mock,\n publishMatches: options.publishMatches,\n logger,\n });\n dependencies.stdout(\n `Remote run ${result.runKey}: ${result.completed} matches, $${result.costUsd.toFixed(4)}`,\n );\n if (result.cancelled) dependencies.setExitCode(130);\n } finally {\n dependencies.stdout(`Run log: ${displayPath(logger.filePath)}`);\n }\n });\n\n arena\n .command('verify')\n .description('Validate and replay outcomes, points, manifests, and Elo')\n .option(\n '-c, --category <category>',\n `journal category (${CATEGORIES.join(', ')})`,\n parseCategory,\n 'reasoning',\n )\n .option('--journal <path>', 'journal file to verify (defaults to the local category journal)')\n .option('--manifests-dir <path>', 'run-manifest directory (defaults to a runs/ sibling)')\n .option('--json', 'emit the machine-readable verification result', false)\n .action(\n (options: {\n category: BenchmarkCategory;\n journal?: string;\n manifestsDir?: string;\n json: boolean;\n }) => {\n const defaults = categoryStoreConfig(options.category);\n const journalPath = options.journal ? path.resolve(options.journal) : defaults.journalPath;\n const runsDir = options.manifestsDir\n ? path.resolve(options.manifestsDir)\n : options.journal\n ? path.join(path.dirname(journalPath), 'runs')\n : defaults.runsDir;\n const store = new ArenaStore({\n ...defaults,\n journalPath,\n runsDir,\n readOnly: true,\n });\n const verified = verifyJournal(store.readAll(), options.category, {\n manifestForRun: (runId) => store.readRunManifest(runId),\n requireManifests: true,\n });\n if (options.json) {\n dependencies.stdout(JSON.stringify(verified, null, 2));\n return;\n }\n dependencies.stdout(\n `✓ ${options.category}: verified ${verified.matches.length} matches across ${verified.runs.length} runs`,\n );\n for (const warning of verified.warnings) {\n dependencies.stderr(`Warning: ${warning}`);\n }\n },\n );\n\n arena\n .command('publish')\n .description('Publish a verified match journal to the configured API')\n .option(\n '-c, --category <category>',\n `journal to publish (${CATEGORIES.join(', ')})`,\n parseCategory,\n )\n .option('--all', 'publish every category explicitly', false)\n .option('--allow-empty', 'treat an empty journal as a successful no-op', false)\n .action(async (options: CategorySelection & { allowEmpty: boolean }) => {\n const categories = selectedCategories(options, 'arena publish');\n const config = dependencies.resolveApiConfig();\n dependencies.stdout(\n `Publishing ${categories.join(', ')} journals to ${publishTarget(config)}`,\n );\n for (const category of categories) {\n const store = dependencies.createStore(category);\n verifyJournal(store.readAll(), category, {\n manifestForRun: (runId) => store.readRunManifest(runId),\n requireManifests: true,\n });\n const result = await dependencies.publishJournal(category, config);\n if (result.matches === 0 && !options.allowEmpty) {\n throw new Error(\n `The ${category} journal is empty; pass --allow-empty for an intentional no-op`,\n );\n }\n dependencies.stdout(\n `✓ ${category}: ${result.imported} new, ${result.skipped} already present (of ${result.matches} lines)`,\n );\n }\n });\n\n arena\n .command('triage')\n .description('Analyze failures, suspicious responses, and judging anomalies')\n .option(\n '-c, --category <category>',\n `journal to analyze (${CATEGORIES.join(', ')})`,\n parseCategory,\n 'reasoning',\n )\n .option('--run <runId>', 'limit the report to one run ID')\n .option('--json', 'emit the machine-readable report', false)\n .option('--strict', 'exit nonzero when any anomaly is found', false)\n .option('--allow-empty', 'treat no matching journal lines as success', false)\n .action(\n (options: {\n category: BenchmarkCategory;\n run?: string;\n json: boolean;\n strict: boolean;\n allowEmpty: boolean;\n }) => {\n const results = dependencies\n .createStore(options.category)\n .readAll()\n .filter((match) => !options.run || match.runId === options.run);\n if (results.length === 0) {\n const message = options.run\n ? `No ${options.category} matches found for run ${options.run}.`\n : `The ${options.category} journal is empty.`;\n if (!options.allowEmpty) throw new Error(message);\n dependencies.stdout(message);\n return;\n }\n const reports = triageJournal(results);\n dependencies.stdout(\n options.json ? JSON.stringify(reports, null, 2) : formatTriage(reports),\n );\n const anomalies = reports.reduce((sum, report) => sum + report.anomalies.length, 0);\n if (options.strict && anomalies > 0) {\n dependencies.stderr(`Strict mode: ${anomalies} anomalies found.`);\n dependencies.setExitCode(1);\n }\n },\n );\n\n arena\n .command('generation <generationId>')\n .description(\"Fetch OpenRouter's record for a journaled generation\")\n .action(async (generationId: string) => {\n const data = await dependencies.createOpenRouter().fetchGeneration(generationId);\n dependencies.stdout(JSON.stringify(data, null, 2));\n });\n\n program\n .command('report')\n .description('Verify journals and rebuild JSON and Markdown reports')\n .option(\n '-c, --category <category>',\n `limit to one category (${CATEGORIES.join(', ')})`,\n parseCategory,\n )\n .action((options: { category?: BenchmarkCategory }) => {\n const categories = options.category ? [options.category] : CATEGORIES;\n for (const category of categories) {\n const snapshot = writeReports(dependencies.createStore(category));\n dependencies.stdout(`✓ ${category}: wrote reports for ${snapshot.matches.length} matches`);\n }\n });\n\n return program;\n}\n\nexport async function runCli(\n argv: string[] = process.argv,\n overrides: Partial<CliDependencies> = {},\n): Promise<void> {\n const dependencies = { ...defaultDependencies, ...overrides };\n const envResult = loadProjectEnv();\n if (envResult.status === 'error') {\n dependencies.stderr(\n `Warning: project environment file could not be loaded: ${redactSecrets(envResult.reason)}`,\n );\n }\n await buildProgram(dependencies).parseAsync(argv);\n}\n","import { existsSync } from 'node:fs';\nimport path from 'node:path';\n\nimport { findProjectRoot } from './paths.js';\n\nexport const ENV_PATH = path.join(findProjectRoot(import.meta.url), '.env');\n\nexport type ProjectEnvLoadResult =\n { status: 'missing' } | { status: 'loaded' } | { status: 'error'; reason: string };\n\n/**\n * Loads the project-root .env into process.env regardless of the process cwd\n * or launch flags. Variables already present in the environment win, matching\n * node's --env-file precedence. Safe to call repeatedly: a key added to .env\n * after startup is picked up on the next call.\n */\nexport function loadProjectEnv(): ProjectEnvLoadResult {\n if (!existsSync(ENV_PATH)) return { status: 'missing' };\n try {\n process.loadEnvFile(ENV_PATH);\n return { status: 'loaded' };\n } catch (error) {\n return {\n status: 'error',\n reason: error instanceof Error ? error.message : String(error),\n };\n }\n}\n","#!/usr/bin/env node\n\nimport { runCli } from './commands.js';\nimport { sanitizeError } from './openrouter.js';\n\nrunCli().catch((error) => {\n console.error(`BridgeBench: ${sanitizeError(error)}`);\n process.exitCode = 1;\n});\n"],"mappings":"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","names":["path","path"]}
|
package/dist/client.cjs
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true});
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
var _chunk7YCJSOK7cjs = require('./chunk-7YCJSOK7.cjs');
|
|
20
|
+
require('./chunk-NJTYVNP4.cjs');
|
|
21
|
+
require('./chunk-4TWPCPRP.cjs');
|
|
22
|
+
require('./chunk-CIXITJW6.cjs');
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
exports.ADMIN_KEY_HEADER = _chunk7YCJSOK7cjs.ADMIN_KEY_HEADER; exports.DEFAULT_API_TIMEOUT_MS = _chunk7YCJSOK7cjs.DEFAULT_API_TIMEOUT_MS; exports.REQUEST_SPACING_MS = _chunk7YCJSOK7cjs.REQUEST_SPACING_MS; exports.RemoteArenaEventSink = _chunk7YCJSOK7cjs.RemoteArenaEventSink; exports.chunk = _chunk7YCJSOK7cjs.chunk; exports.createRemoteRun = _chunk7YCJSOK7cjs.createRemoteRun; exports.delay = _chunk7YCJSOK7cjs.delay; exports.fetchExecutionPack = _chunk7YCJSOK7cjs.fetchExecutionPack; exports.getJson = _chunk7YCJSOK7cjs.getJson; exports.postChunks = _chunk7YCJSOK7cjs.postChunks; exports.postJson = _chunk7YCJSOK7cjs.postJson; exports.publishJournal = _chunk7YCJSOK7cjs.publishJournal; exports.publishJournalFromStore = _chunk7YCJSOK7cjs.publishJournalFromStore; exports.publishTarget = _chunk7YCJSOK7cjs.publishTarget; exports.publishTasks = _chunk7YCJSOK7cjs.publishTasks; exports.resolveApiConfig = _chunk7YCJSOK7cjs.resolveApiConfig; exports.runRemoteArena = _chunk7YCJSOK7cjs.runRemoteArena;
|
|
42
|
+
//# sourceMappingURL=client.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/Users/matthewmiller/Desktop/bridgemind/bridgebench/dist/client.cjs"],"names":[],"mappings":"AAAA;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF,wDAA6B;AAC7B,gCAA6B;AAC7B,gCAA6B;AAC7B,gCAA6B;AAC7B;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF,s+BAAC","file":"/Users/matthewmiller/Desktop/bridgemind/bridgebench/dist/client.cjs"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { A as ArenaStore, a as ArenaLogger } from './logger-CCR9Mg1c.cjs';
|
|
3
|
+
import { B as BenchmarkCategory, d as CompleteArenaTask } from './tasks-CpaCJ6JE.cjs';
|
|
4
|
+
import { f as ArenaRunConfig, A as ArenaEvent } from './reports-4CejmOHf.cjs';
|
|
5
|
+
|
|
6
|
+
declare const ADMIN_KEY_HEADER = "x-bridgebench-admin-key";
|
|
7
|
+
declare const DEFAULT_API_TIMEOUT_MS = 30000;
|
|
8
|
+
declare const REQUEST_SPACING_MS = 700;
|
|
9
|
+
interface ApiConfig {
|
|
10
|
+
baseUrl: string;
|
|
11
|
+
adminKey: string;
|
|
12
|
+
timeoutMs: number;
|
|
13
|
+
}
|
|
14
|
+
declare function resolveApiConfig(environment?: NodeJS.ProcessEnv): ApiConfig;
|
|
15
|
+
declare function publishTarget(config: ApiConfig): string;
|
|
16
|
+
declare function delay(ms: number): Promise<void>;
|
|
17
|
+
declare function chunk<T>(items: readonly T[], size: number): T[][];
|
|
18
|
+
declare function getJson<T>(config: ApiConfig, path: string, schema: z.ZodType<T>): Promise<T>;
|
|
19
|
+
declare function postJson<T>(config: ApiConfig, path: string, body: unknown, schema: z.ZodType<T>): Promise<T>;
|
|
20
|
+
declare function postChunks<T, R extends {
|
|
21
|
+
imported?: number;
|
|
22
|
+
skipped?: number;
|
|
23
|
+
}>(config: ApiConfig, path: string, batches: readonly T[][], wrap: (batch: readonly T[]) => Record<string, readonly T[]>, schema: z.ZodType<R>): Promise<R[]>;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Publish glue: pushes the engine's authored task pack and its match journal
|
|
27
|
+
* to the bridgebench.ai API. The local verified journal is the execution
|
|
28
|
+
* authority; the API is a one-way published replica and public read surface.
|
|
29
|
+
*
|
|
30
|
+
* Both API endpoints are admin-key guarded and idempotent (upsert on task
|
|
31
|
+
* key+version, insert-or-skip on match key), so re-publishing is always safe.
|
|
32
|
+
* Publishing tasks requires the private overlay (the API stores both halves;
|
|
33
|
+
* hidden references live in a table no public endpoint reads) — see
|
|
34
|
+
* docs/private-packs.md.
|
|
35
|
+
*/
|
|
36
|
+
/**
|
|
37
|
+
* Load, validate, and push a category's task pack. The API import contract
|
|
38
|
+
* requires both halves, so this fails closed (with a pointer to
|
|
39
|
+
* docs/private-packs.md) when the private overlay is absent.
|
|
40
|
+
*/
|
|
41
|
+
declare function publishTasks(category: BenchmarkCategory, config?: ApiConfig): Promise<{
|
|
42
|
+
imported: number;
|
|
43
|
+
}>;
|
|
44
|
+
/** Push a match journal from the given store (idempotent on the engine match key). */
|
|
45
|
+
declare function publishJournalFromStore(store: ArenaStore, config?: ApiConfig): Promise<{
|
|
46
|
+
imported: number;
|
|
47
|
+
skipped: number;
|
|
48
|
+
matches: number;
|
|
49
|
+
}>;
|
|
50
|
+
/** Push a category's local match journal (idempotent on the engine match key). */
|
|
51
|
+
declare function publishJournal(category: BenchmarkCategory, config?: ApiConfig): Promise<{
|
|
52
|
+
imported: number;
|
|
53
|
+
skipped: number;
|
|
54
|
+
matches: number;
|
|
55
|
+
}>;
|
|
56
|
+
|
|
57
|
+
interface RemoteArenaRunOptions {
|
|
58
|
+
config: ArenaRunConfig;
|
|
59
|
+
mock?: boolean;
|
|
60
|
+
publishMatches?: boolean;
|
|
61
|
+
logger?: ArenaLogger;
|
|
62
|
+
}
|
|
63
|
+
interface RemoteArenaRunResult {
|
|
64
|
+
runKey: string;
|
|
65
|
+
completed: number;
|
|
66
|
+
costUsd: number;
|
|
67
|
+
cancelled: boolean;
|
|
68
|
+
stoppedForBudget: boolean;
|
|
69
|
+
}
|
|
70
|
+
declare function fetchExecutionPack(config: ApiConfig, category: BenchmarkCategory): Promise<{
|
|
71
|
+
tasks: CompleteArenaTask[];
|
|
72
|
+
}>;
|
|
73
|
+
declare function createRemoteRun(apiConfig: ApiConfig, runConfig: ArenaRunConfig, tasks: readonly CompleteArenaTask[]): Promise<{
|
|
74
|
+
runKey: string;
|
|
75
|
+
created: boolean;
|
|
76
|
+
}>;
|
|
77
|
+
declare function runRemoteArena(apiConfig: ApiConfig, options: RemoteArenaRunOptions): Promise<RemoteArenaRunResult>;
|
|
78
|
+
|
|
79
|
+
declare class RemoteArenaEventSink {
|
|
80
|
+
private readonly config;
|
|
81
|
+
private readonly runKey;
|
|
82
|
+
private readonly buffer;
|
|
83
|
+
private flushTimer;
|
|
84
|
+
private flushChain;
|
|
85
|
+
private closed;
|
|
86
|
+
constructor(config: ApiConfig, runKey: string);
|
|
87
|
+
readonly sink: (event: ArenaEvent) => void;
|
|
88
|
+
private scheduleFlush;
|
|
89
|
+
flush(): Promise<void>;
|
|
90
|
+
close(): Promise<void>;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export { ADMIN_KEY_HEADER, type ApiConfig, DEFAULT_API_TIMEOUT_MS, REQUEST_SPACING_MS, RemoteArenaEventSink, type RemoteArenaRunOptions, type RemoteArenaRunResult, chunk, createRemoteRun, delay, fetchExecutionPack, getJson, postChunks, postJson, publishJournal, publishJournalFromStore, publishTarget, publishTasks, resolveApiConfig, runRemoteArena };
|