Files

81 lines
2.8 KiB
JavaScript

#!/usr/bin/env node
// Usage:
// node probe.mjs list
// node probe.mjs <provider> [--raw] [--save]
// Credentials come from environment variables only. Output is redacted by default:
// keys + types are shown, numbers/booleans/ISO dates are kept, other strings are masked.
// --raw print full JSON responses (local terminal only; may contain personal data)
// --save write the redacted shape to samples/<provider>.json (for snapshot tests)
import { mkdir, writeFile } from "node:fs/promises";
import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
import { http, shape } from "./lib.mjs";
import { providers } from "./providers.mjs";
const here = dirname(fileURLToPath(import.meta.url));
const [cmd, ...flags] = process.argv.slice(2);
const raw = flags.includes("--raw");
const save = flags.includes("--save");
if (!cmd || cmd === "list" || cmd === "--help") {
console.log("Providers:\n");
for (const [id, p] of Object.entries(providers)) {
console.log(` ${id.padEnd(14)} ${p.title}`);
console.log(` ${"".padEnd(14)} env: ${p.env.join(", ")}${p.optionalEnv ? ` optional: ${p.optionalEnv.join(", ")}` : ""}`);
}
console.log("\nExample (PowerShell): $env:OPENROUTER_API_KEY='sk-or-...'; node probe.mjs openrouter");
process.exit(0);
}
const p = providers[cmd];
if (!p) {
console.error(`unknown provider "${cmd}". Run: node probe.mjs list`);
process.exit(2);
}
const missing = p.env.filter((k) => !process.env[k]);
if (missing.length) {
console.error(`missing env: ${missing.join(", ")}`);
process.exit(2);
}
const env = process.env;
const results = {};
const report = { provider: cmd, at: new Date().toISOString(), requests: [] };
for (const r of p.requests(env)) {
let res;
try {
res = await http(r.url, r);
} catch (e) {
res = { status: 0, ok: false, ms: 0, error: String(e?.name === "AbortError" ? "timeout" : e) };
}
results[r.name] = res;
report.requests.push({
name: r.name,
url: r.url,
status: res.status,
ms: res.ms,
retryAfter: res.retryAfter ?? undefined,
error: res.error,
body: res.json !== undefined ? (raw ? res.json : shape(res.json)) : res.text,
});
}
report.meters = p.toMeters(results, env);
report.verdict = report.meters.length > 0 ? "OK: meters parsed" : "UNSUPPORTED/EMPTY: no meters parsed — compare body shapes with the mapping";
console.log(JSON.stringify(report, null, 2));
if (save) {
const dir = join(here, "samples");
await mkdir(dir, { recursive: true });
const sample = Object.fromEntries(
report.requests.filter((r) => r.status >= 200 && r.status < 300).map((r) => [r.name, raw ? shape(results[r.name].json) : r.body]),
);
await writeFile(join(dir, `${cmd}.json`), JSON.stringify(sample, null, 2) + "\n");
console.error(`saved redacted sample -> samples/${cmd}.json`);
}
process.exit(report.meters.length > 0 ? 0 : 1);