#!/usr/bin/env node // Operator report for the privacy-safe model counters (issue #19). // // It reads one snapshot from a running Timmy server and prints it. The token is a credential for the // operator surface, so it is read from the environment or argv and never echoed — not in errors, not // in the report, not in a stack trace. import { formatOperatorReport } from '../src/model-metrics-report.js'; const USAGE = `Usage: node scripts/model_metrics_report.mjs [options] --url base URL of the Timmy server (default http://127.0.0.1:4173) --token operator token (default: $TIMMY_METRICS_TOKEN) --json print the raw snapshot instead of the text report --timeout-ms request deadline (default 5000) --help show this text `; function parseArgs(argv) { const options = { url: process.env.TIMMY_METRICS_URL || 'http://127.0.0.1:4173', token: process.env.TIMMY_METRICS_TOKEN || '', json: false, timeoutMs: 5_000, help: false, }; for (let index = 0; index < argv.length; index += 1) { const arg = argv[index]; if (arg === '--help' || arg === '-h') { options.help = true; } else if (arg === '--json') { options.json = true; } else if (arg === '--url') { options.url = argv[++index] ?? ''; } else if (arg === '--token') { options.token = argv[++index] ?? ''; } else if (arg === '--timeout-ms') { options.timeoutMs = Number(argv[++index]); } else { throw new Error(`unknown option: ${arg}`); } } return options; } async function main(argv) { const options = parseArgs(argv); if (options.help) { process.stdout.write(USAGE); return 0; } if (!options.token) { throw new Error('operator token required: pass --token or set TIMMY_METRICS_TOKEN'); } const endpoint = new URL('api/metrics', options.url.endsWith('/') ? options.url : `${options.url}/`); let response; try { response = await fetch(endpoint, { headers: { authorization: `Bearer ${options.token}`, accept: 'application/json' }, signal: AbortSignal.timeout(Number.isFinite(options.timeoutMs) ? options.timeoutMs : 5_000), }); } catch (error) { process.stderr.write(`Could not read ${endpoint.origin}/api/metrics: ${error?.message || 'request failed'}\n`); return 1; } if (!response.ok) { if (response.status === 404) { // The route is absent rather than guarded: that is a deployment fact, not a credential problem. process.stderr.write('Metrics are disabled on this server: the route only exists when TIMMY_METRICS_TOKEN is set.\n'); return 1; } if (response.status === 403) { process.stderr.write('The server rejected the operator token.\n'); return 1; } process.stderr.write(`The server answered ${response.status} for /api/metrics.\n`); return 1; } const snapshot = await response.json(); process.stdout.write(options.json ? `${JSON.stringify(snapshot, null, 2)}\n` : formatOperatorReport(snapshot)); return 0; } main(process.argv.slice(2)).then(code => { process.exitCode = code; }).catch(error => { process.stderr.write(`${error?.message || 'report failed'}\n`); process.exitCode = 2; });