#!/usr/bin/env node import { createHash } from 'node:crypto'; import { readdirSync, readFileSync } from 'node:fs'; import { mkdir, mkdtemp, open, readdir, readFile, rm, stat, writeFile } from 'node:fs/promises'; import { cpus, tmpdir } from 'node:os'; import { createInterface } from 'node:readline/promises'; import { dirname, join, resolve as resolvePath } from 'node:path'; import { fileURLToPath } from 'node:url'; import { gunzipSync } from 'node:zlib'; import { CONTRACT_NAMES, contractSchema, applyRewrites, BASELINE_FILENAME, BASELINE_VERSION, breaches, cacheableMinimum, analyzeCachePrefix, billLevers, bucketedCacheEconomics, bucketedProfile, buildHistory, buildPlan, connectorFor, CONNECTORS, normalizeAnthropicUsage, normalizeOpenAIUsage, bucketsFromRecords, evaluateWatch, firedKey, pruneRecords, recordsFromBuckets, storeInventory, storedReportFrom, verifyPlan, cacheEconomics, cacheHitRate, contextPressure, comparePrompts, compareToBaseline, computeSavings, bandGoverns, countTokensAnthropic, DEFAULT_USAGE, budgetPositions, conform, BREAK_EVEN_BAND, allocate, annualRecord, replayCommitment, coversTheTerm, runExperiment, qualityGate, semanticPassCost, verifySemanticProposals, SEMANTIC_SYSTEM_PROMPT, ladderPosition, validateLadder, outcomeReport, rankPerOutcome, FAILURE_POLICIES, detectFromSource, matchLocale, parsePlanDocument, waiverDay, waiverHistory, proposeInit, MIN_RATE_DAYS, parseConfig, coverageDrift, driversBetween, explainGateFailure, assignSources, fleetRollup, rollUp, heartbeats, ruleYield, labelCoverage, measuredUsage, gateMargin, GATE_MARGIN_TIGHT, estimateTokens, evaluate, extractPrompts, findExamples, formatBaseline, formatSignedUsd, formatUsd, receiptFrom, type ReceiptDocument, getMessages, getModel, hasMarker, listModels, LOCALES, MAX_BASELINE_BYTES, matchGlob, MAX_INPUT_CHARS, moneyIsComparable, mostSpecificMatch, assemble, interview, nearestName, slot, SLOT_IDS, optimize, parseBaseline, PHRASE_LANGUAGES, detectTextLanguage, dictionaryStanding, languagesWithStanding, indexUsage, parseUsageLine, plannedCalls, claudeCodeRecords, type CwdLabel, type WorkspaceLabel, type ProjectLabel, type OpenrouterWorkspaceLabel, ESTIMATE_ERROR_BAND_PCT, bandFor, foreignTokenizer, measuredForeignError, anthropicCostReport, anthropicUsageRecords, looksLikeOpenaiCost, looksLikeAnthropicCost, looksLikeAnthropicUsage, looksLikeClaudeCodeTranscript, looksLikeHelicone, looksLikeLangsmith, looksLikeLiteLlm, looksLikeOpenaiUsage, looksLikeOpenrouterActivity, looksLikeOtel, openaiCostReport, openaiUsageRecords, openrouterActivityRecords, reconcile, heliconeRecords, langsmithRecords, litellmRecords, otelRecords, ownRate, positionAt, positionReport, PRICING_LAST_REVIEWED, reviewedForModels, PROVIDER_REVIEWED, STALE_PRICING_DAYS, profilePrompt, profileToCsv, profileUsage, promptId, providerFromEnv, pruneExamples, refineWithLlm, rejectionText, reorderForCache, repriceProfile, switchAnalysis, reviewAgeDays, reviewExamples, RULES, sharedPrefixes, sharesOf, isOffered, SOURCE_EXTENSIONS, suggestRewrites, toOtlpMetrics, toPromptfoo, TTL_1H_MS, UNLABELLED, withExactTokenCounts, } from '@trazum/core'; import { cacheDir, cacheStats, cachingProvider, clearCache } from './suggest-cache.js'; import { OPTIONAL_COUNTERS } from './optional-counters.js'; import { dayOf, formatGap, median, spanDays } from './time.js'; import type { BucketedReport, EvalReport, FleetSource, HistoryRun, MeasuredUsage, PruneReport, PlanDocument, StoredReport, VerifiedAction, BaselineBreach, BaselineChange, BaselineComparison, BaselineDocument, Advisory, ExampleReview, PromptComparison, ReorderResult, ExtractedPrompt, DeclinedPrompt, Locale, OptimizationResult, RuleId, RejectedReason, PromptProfile, RuleLevel, SharedPrefix, SuggestResult, UsageProfile, } from '@trazum/core'; import type { BudgetReport, ContractName, ExperimentArm, GateSide, SemanticProposal, FailurePolicy, GatewayStanding, UsageProfileReport, WaiverUse, InitDecline, InitJustification, InitObservations, InitProposal, ProviderSighting, UsageSighting, } from '@trazum/core'; // Everything that reads the filesystem, on its own entry point so the web // bundle cannot reach it. See packages/core/src/node.ts. import { CONFIG_FILENAME, DEFAULT_EXTENSIONS, budgetFor, BUNDLED_CATALOGUE, SAFE_FETCH_INIT, applyPricingOverlay, catalogueFromOverlay, checkedEndpoint, openrouterOverlay, detectHost, loadConfig, walkPrompts, } from '@trazum/core/node'; import type { HostEnvironment, LoadedConfig, PricingCatalogue, ResolvedBudget, TrazumConfig, } from '@trazum/core/node'; import { contentAt, gitAvailable, namesByRevision, pathInRepository, repositoryRoot, revisionsFor, runSelf, } from './git.js'; import type { Revision } from './git.js'; import { fetchProviderUsage, findCredential } from './connect.js'; import { STORE_DIR, appendRecords, readStore, rewriteStore } from './store-fs.js'; import { WAIVER_LOG, appendWaiverUse, readWaiverLog } from './waiver-log.js'; import { DEFAULT_PORT, buildServer, listen } from './serve.js'; import { DEFAULT_GATEWAY_PORT, UPSTREAMS, buildGateway, listenGateway, } from './gateway-server.js'; import { WATCH_STATE_VERSION, checkWebhook, postWebhook, readWatchState, writeWatchState, } from './watch-run.js'; import { LOCALE_ENV_VARS, detectLocale, getCliMessages } from './i18n/index.js'; import { MAX_SUMMARY_CHARS, fitWithin, renderBlameMarkdown, renderCheckMarkdown, renderDiffMarkdown, renderRankMarkdown, renderProfileMarkdown, } from './markdown.js'; import { renderPositionHtml, renderProfileHtml, renderRollupHtml } from './html.js'; import type { CliMessages } from './i18n/index.js'; // -------------------------------------------------------------------------- // Presentation // -------------------------------------------------------------------------- // The painters, the ANSI-aware measurer, the one table renderer, the // proportion bar and the heading rule — the 1.75 style module. The guard in // style.test.js holds the contract: stripped colour output is byte-identical // to plain output, and a pipe stays plain. import { bar, c, sectionHeading, table } from './style.js'; // -------------------------------------------------------------------------- // Argument parsing // -------------------------------------------------------------------------- interface Args { command: string; positional: string[]; flags: Map; /** * How a flag was spelled, when that differs from the key it is stored under. * * Only `--no-x` differs today, and it exists so an error quotes what was * actually typed. Telling somebody "unknown option --nonsense" when they * wrote `--no-nonsense` sends them looking for a flag they never used. */ asTyped: Map; } const VALUE_FLAGS = new Set([ 'answers', 'label-by-cwd', 'label-by-workspace', 'label-by-project', 'against', 'calls', 'files-from', 'log', 'avg-output', 'a', 'at', 'year', 'b', 'floor', 'discount', 'months', 'min-outcomes', 'against', 'contract', 'on-cannot-tell', 'from-log', 'min-usd', 'payload', 'keep', 'interval', 'webhook', 'port', 'socket', // `route` takes a path here, and the flag is deliberately not `--prompt`: // everywhere else in this tool `--prompt` names a marked prompt *inside* a // source file, and reusing it for a path would be a trap laid for the reader. 'prompt-file', 'label', 'level', 'model', 'calls', 'output-tokens', 'cache-hit-rate', 'disable', 'max-tokens', 'cases', 'concurrency', 'max-growth', 'max-usd', 'max-growth-usd', 'max-cache-loss-usd', 'max-stale-hours', 'measure', 'workload', 'record', 'max-ratio', 'max-input', 'max-day-usd', 'max-session-usd', 'csv-out', 'csv-shape', 'what-if', 'since', 'until', 'export', 'limit', 'locale', 'config', 'markdown-out', 'html-out', 'otlp-out', 'pricing', 'prompt', 'out', 'o', 'to', 'migration-usd', 'cases', 'gpu-usd-hour', 'tokens-per-second', 'utilization', 'state', ]); function parseArgs(argv: string[], t: CliMessages): Args { const flags = new Map(); const asTyped = new Map(); const positional: string[] = []; for (let i = 0; i < argv.length; i++) { const arg = argv[i]!; // The POSIX escape: everything after `--` is a path, whatever it looks // like. Without it there is no way to name a file called `-x.txt` or // `--output=…` on the command line at all — the parser sees a flag and // refuses before the path reaches the code that knows what to do with it. if (arg === '--') { positional.push(...argv.slice(i + 1)); break; } if (!arg.startsWith('-') || arg === '-') { positional.push(arg); continue; } const typed = arg.replace(/^--?/, ''); let name = typed; // `--no-batch` stores `batch: false`. This exists because a config file can // switch a boolean on, and a setting that cannot be switched back off from // the command line is one you have to edit the repository to escape. let value: string | boolean = true; if (name.startsWith('no-') && !VALUE_FLAGS.has(name)) { name = name.slice(3); value = false; asTyped.set(name, typed); } if (VALUE_FLAGS.has(name)) { if (value === false) throw new Error(t.errors.cannotNegate(name)); const given = argv[++i]; if (given === undefined) throw new Error(t.errors.optionNeedsValue(name)); flags.set(name === 'o' ? 'out' : name, given); } else { flags.set(name, value); } } return { command: positional[0] ?? '', positional: positional.slice(1), flags, asTyped }; } /** * Reads a boolean flag, honouring `--no-` and a project default. * * `flags.has(name)` is the wrong test once negation exists: `--no-batch` stores * the key with the value `false`, and `has` would report it as set. */ /** A numberFlag that is also a fraction: the config's own 0-to-1 rule. */ function fractionFlag(args: Args, name: string, fallback: number, t: CliMessages): number { const value = numberFlag(args, name, fallback, t); if (value > 1) { throw new Error(t.errors.fractionFlag(name, String(args.flags.get(name)))); } return value; } function boolFlag(args: Args, name: string, fallback = false): boolean { const raw = args.flags.get(name); return typeof raw === 'boolean' ? raw : fallback; } /** * Reads `--locale` before the rest of the parsing, so even a parse error is * reported in the language the user asked for. */ function localeFromArgv(argv: string[]): Locale { const index = argv.indexOf('--locale'); const flag = index >= 0 ? argv[index + 1] : undefined; return detectLocale(flag); } function stringFlag(args: Args, name: string): string | undefined { const raw = args.flags.get(name); return typeof raw === 'string' ? raw : undefined; } function numberFlag(args: Args, name: string, fallback: number, t: CliMessages): number { const raw = args.flags.get(name); if (raw === undefined || typeof raw === 'boolean') return fallback; const value = Number(raw); if (!Number.isFinite(value) || value < 0) { throw new Error(t.errors.mustBeNonNegative(name, raw)); } return value; } /** * Resolves the rule level: flag, then config, then `safe`. * * The layering order is the same for every setting in this file — the command * line beats the project, and the project beats the built-in default. A config * file that could override an explicit flag would make the flag a suggestion. */ function levelFlag(args: Args, config: TrazumConfig, t: CliMessages): RuleLevel { const level = (args.flags.get('level') ?? config.level ?? 'safe') as RuleLevel; if (level !== 'safe' && level !== 'aggressive') { throw new Error(t.errors.badLevel(String(level))); } return level; } /** * Usage profile from flags over config over detection over the built-in default. * * `detected` is what the source file said — an SDK import, a base URL, a quoted * model id. It beats the default because reading the code is better than * assuming, and loses to config because being told is better than reading. */ /** * The file names a usage log answers to, shared by every command that reads a * directory of them. One list, because two commands disagreeing on what counts * as a log would be the same directory billing differently by verb. */ const LOG_EXTENSIONS = ['.jsonl', '.ndjson', '.log', '.json']; /** * One usage log, gzip included, shared by every command that reads one. * * A `.gz` that will not decompress is an error naming the file — skipping it * would be a figure quietly missing a day, the failure this repository * refuses everywhere it can occur. */ /** * The measured side of the `limits` policy, from `--log` — shared by the two * HTTP doors. Returns null when no log was named: the doors then judge every * ceiling `cannot-tell`, which is the honest answer to "what has this label * spent" when nobody handed over the record of what labels spent. */ async function usageIndexFrom( args: Args, pricing: PricingCatalogue, t: CliMessages, ): Promise | null> { const logPath = stringFlag(args, 'log'); if (logPath === undefined) return null; const text = await readUsageLog(logPath, t); const records = text .split('\n') .filter((line) => line.trim() !== '') .map((line) => parseUsageLine(line)) .filter((record): record is NonNullable> => record !== null); return indexUsage(records, { catalogue: pricing }); } async function readUsageLog(file: string, t: CliMessages): Promise { if (!file.endsWith('.gz')) return readFile(file, 'utf8'); const compressed = await readFile(file); try { return gunzipSync(compressed).toString('utf8'); } catch (error) { throw new Error(t.profile.badGzip(file, error instanceof Error ? error.message : String(error))); } } function usageFrom( args: Args, config: TrazumConfig, t: CliMessages, detected?: string, ): UsageProfile { const fromConfig = config.usage ?? {}; const model = stringFlag(args, 'model') ?? fromConfig.model ?? detected ?? DEFAULT_USAGE.model; return { model, callsPerMonth: numberFlag( args, 'calls', fromConfig.callsPerMonth ?? DEFAULT_USAGE.callsPerMonth, t, ), avgOutputTokens: numberFlag( args, 'output-tokens', fromConfig.avgOutputTokens ?? DEFAULT_USAGE.avgOutputTokens, t, ), /* Bounded above as well as below, because the config already is. `usage.cacheHitRate: 2` in trazum.config.json is refused as "a fraction between 0 and 1"; `--cache-hit-rate 2` on the command line was accepted and quietly skewed the caching advisory. Two doors to the same value cannot disagree about what fits through. */ cacheHitRate: fractionFlag( args, 'cache-hit-rate', fromConfig.cacheHitRate ?? DEFAULT_USAGE.cacheHitRate, t, ), batchEligible: boolFlag(args, 'batch', fromConfig.batchEligible ?? false), }; } /** * Prices for this run: `--pricing` beats the config's overlay, which beats the * bundled catalogue — the same layering as every other setting. */ /** * OpenRouter's public catalogue. Overridable for an operator behind a mirror. * * Not a secret and not a credential: the models endpoint is unauthenticated, * which is why this can be a flag rather than a key. */ const OPENROUTER_MODELS_URL = process.env.TRAZUM_OPENROUTER_URL ?? 'https://openrouter.ai/api/v1/models'; /** * Prices from a live source, and the reasoning for why this is opt-in. * * The bundled catalogue is a table somebody typed, so it is stale the day after * it is written and it only ever covered the providers whoever typed it reached * for. `--pricing-live` replaces the price half of it with today's figures for * hundreds of models across dozens of providers. * * **Opt-in, because it is a network call.** Rule 1 of this project is that no * feature makes a network call a prerequisite for optimising a prompt. This is * the CLI reaching out on request and handing the core a value; the core never * fetches anything, which is what keeps `optimize()` free, offline and * deterministic. * * Through `checkedEndpoint` and `SAFE_FETCH_INIT` like every other outbound * call here: URL validated before the request, redirects refused, so an * endpoint that passes the check cannot answer `302` and send the request * somewhere on the metadata network. */ async function livePricing(source: string, t: CliMessages): Promise { const endpoint = checkedEndpoint(source, { name: 'openrouter' }); let payload: unknown; try { const response = await fetch(endpoint, { ...SAFE_FETCH_INIT, method: 'GET' }); if (!response.ok) throw new Error(`${response.status} ${response.statusText}`); payload = await response.json(); } catch (error) { const detail = error instanceof Error ? error.message : String(error); throw new Error(t.errors.livePricingFailed(endpoint, detail)); } const known = new Set(BUNDLED_CATALOGUE.models.map((model) => model.id)); const { overlay, skipped } = openrouterOverlay(payload, { knownIds: known, lastReviewed: new Date().toISOString().slice(0, 10), }); const catalogue = applyPricingOverlay(BUNDLED_CATALOGUE, overlay, endpoint); // Said out loud, on stderr so it never lands in `--json`. A price feed that // silently dropped a third of its entries would leave somebody wondering why // their model is still missing. console.error( t.pricing.liveLoaded(catalogue.addedModels.length, catalogue.overriddenModels.length, skipped.length), ); return catalogue; } async function pricingFor( args: Args, loaded: { pricing: PricingCatalogue }, t: CliMessages, ): Promise { const flag = stringFlag(args, 'pricing'); if (flag) { const raw = await readFile(flag, 'utf8'); return catalogueFromOverlay(raw, flag); } // A file beats the network: somebody who wrote prices down meant them. if (boolFlag(args, 'pricing-live')) return livePricing(OPENROUTER_MODELS_URL, t); return loaded.pricing; } /** Rules to disable: the flag replaces the config list rather than adding to it. */ function disabledRules(args: Args, config: TrazumConfig): RuleId[] | undefined { const flag = stringFlag(args, 'disable'); if (flag !== undefined) { return flag.split(',').map((id) => id.trim()).filter(Boolean) as RuleId[]; } return config.disable; } /** * Flags each command accepts. An unrecognised flag used to be accepted * silently, which on a gate command means CI passing while the author believes * a threshold is set — `--max-growh 5` would have been ignored and the build * gone green. Silence is the wrong answer for a typo. */ const GLOBAL_FLAGS = ['help', 'h', 'version', 'v', 'locale', 'json', 'config', 'pricing', 'pricing-live', 'max-input']; const COMMAND_FLAGS: Record = { optimize: [ 'level', 'model', 'calls', 'output-tokens', 'cache-hit-rate', 'batch', 'disable', 'llm', 'exact-tokens', 'diff', 'reorder', 'out', 'o', 'tokens-only', 'cost', 'prompt', 'suggest', 'apply-suggestions', 'cache-suggestions', 'from-log', 'label', 'all-labels', ], check: ['max-tokens', 'level', 'exact-tokens', 'markdown-out', 'baseline', 'files-from'], baseline: ['model', 'calls', 'output-tokens', 'cache-hit-rate', 'batch', 'exact-tokens', 'out', 'o'], profile: ['json', 'pricing', 'pricing-live', 'against', 'what-if', 'markdown-out', 'html-out', 'csv-out', 'csv-shape', 'max-usd', 'max-growth-usd', 'max-cache-loss-usd', 'max-day-usd', 'max-session-usd', 'label', 'since', 'until', 'dry-run', 'markdown-summary', 'by-source', 'allow-empty'], plan: ['json', 'out', 'markdown-out', 'min-usd', 'pricing', 'pricing-live'], verify: ['against', 'gate', 'json', 'markdown-out', 'pricing', 'pricing-live'], history: ['store', 'json', 'markdown-out'], connect: ['since', 'until', 'payload', 'store', 'json', 'out', 'markdown-out', 'pricing', 'pricing-live', 'dry-run'], store: ['prune', 'keep', 'json', 'pricing', 'pricing-live', 'dry-run'], watch: ['once', 'interval', 'since', 'payload', 'webhook', 'json', 'pricing', 'pricing-live'], serve: ['port', 'socket', 'log', 'pricing', 'pricing-live'], route: ['prompt-file', 'cases', 'label', 'concurrency', 'json', 'yes', 'pricing', 'pricing-live'], eval: ['cases', 'level', 'concurrency', 'export', 'out', 'o', 'model'], prune: ['cases', 'concurrency', 'json', 'yes'], diff: ['level', 'model', 'calls', 'output-tokens', 'batch', 'max-growth', 'optimized', 'markdown-out', 'all', 'prompt'], models: [], rank: ['level', 'model', 'calls', 'output-tokens', 'batch', 'disable', 'prompt', 'markdown-out'], init: ['dry-run', 'yes', 'json', 'pricing', 'pricing-live'], conform: ['contract', 'json'], schema: [], rollup: ['json', 'html-out'], position: ['json', 'html-out', 'pricing', 'pricing-live'], receipt: ['out', 'o', 'stamp', 'pricing', 'pricing-live'], 'from-claude-code': ['label', 'label-by-cwd', 'label-from-project', 'out', 'o', 'state'], 'from-otel': ['label-from-service', 'out', 'o'], 'from-litellm': ['out', 'o'], reconcile: ['against', 'out', 'o'], 'from-anthropic': ['label', 'label-by-workspace', 'out', 'o'], 'from-openai': ['label', 'label-by-project', 'out', 'o'], 'from-openrouter': ['label', 'label-by-workspace', 'out', 'o'], bill: ['label', 'out', 'o', 'stamp', 'pricing', 'pricing-live'], 'from-helicone': ['out', 'o'], 'from-langsmith': ['out', 'o'], switch: ['to', 'migration-usd', 'cases'], ownrate: ['gpu-usd-hour', 'tokens-per-second', 'utilization'], pulse: ['json', 'max-stale-hours'], bench: ['workload', 'json', 'record', 'against', 'max-ratio'], write: ['answers', 'json', 'out', 'o', 'calls', 'avg-output'], feedback: [], gateway: ['on-cannot-tell', 'port', 'socket', 'log', 'pricing', 'pricing-live'], ladder: ['pricing', 'pricing-live', 'since', 'until', 'label'], experiment: ['a', 'b', 'min-outcomes', 'pricing', 'pricing-live'], quality: ['label', 'at', 'gate', 'pricing', 'pricing-live'], semantic: ['yes', 'model', 'pricing', 'pricing-live'], owners: ['pricing', 'pricing-live', 'since', 'until'], commitment: ['floor', 'discount', 'months', 'pricing', 'pricing-live'], report: ['year', 'json', 'pricing', 'pricing-live'], where: [], rules: ['measure', 'level', 'json'], blame: ['limit', 'model', 'calls', 'output-tokens', 'batch', 'prompt', 'markdown-out'], doctor: ['level', 'model', 'calls', 'output-tokens', 'batch', 'disable', 'prompt', 'otlp-out'], }; function rejectUnknownFlags(args: Args, t: CliMessages): void { const known = COMMAND_FLAGS[args.command]; if (!known) return; // Deduplicated: a command that declares a flag the globals also carry // (`--json`, `--pricing`, `--pricing-live`) listed it twice in the error, // and a refusal that stutters reads like the tool is unsure what it takes. const allowed = [...new Set([...known, ...GLOBAL_FLAGS])]; for (const name of args.flags.keys()) { // `out` is stored under its long name even when given as `-o`, and a // negated boolean under its base name, so both validate against the list. if (allowed.includes(name)) continue; // Quoted as typed, so `--no-nonsense` is not reported as `--nonsense`. const spelled = args.asTyped.get(name) ?? name; const nearest = nearestName(name, allowed); throw new Error( nearest ? t.errors.unknownFlagDidYouMean(spelled, nearest) : t.errors.unknownFlag(spelled, allowed.slice().sort().join(', ')), ); } } // -------------------------------------------------------------------------- // Line-by-line diff // -------------------------------------------------------------------------- /** * Largest diff this will attempt, in lines per side. * * The alignment table is quadratic: at 6,000 lines it is 36 million cells and * roughly 288 MB before anything else runs. There is no prompt worth reading a * 6,000-line diff of, so past this the diff is declined rather than the process * being taken down by someone passing a large file. */ const MAX_DIFF_LINES = 2500; /** Longest common subsequence, used to align the two versions. */ function lcsTable(a: string[], b: string[]): number[][] { const table: number[][] = Array.from({ length: a.length + 1 }, () => new Array(b.length + 1).fill(0), ); for (let i = a.length - 1; i >= 0; i--) { for (let j = b.length - 1; j >= 0; j--) { table[i]![j] = a[i] === b[j] ? table[i + 1]![j + 1]! + 1 : Math.max(table[i + 1]![j]!, table[i]![j + 1]!); } } return table; } function renderDiff(before: string, after: string, t: CliMessages): string { const a = before.split('\n'); const b = after.split('\n'); if (a.length > MAX_DIFF_LINES || b.length > MAX_DIFF_LINES) { return c.dim(t.report.diffTooLarge(Math.max(a.length, b.length), MAX_DIFF_LINES)); } const table = lcsTable(a, b); const lines: string[] = []; let i = 0; let j = 0; while (i < a.length && j < b.length) { if (a[i] === b[j]) { lines.push(c.dim(` ${a[i]}`)); i++; j++; } else if (table[i + 1]![j]! >= table[i]![j + 1]!) { lines.push(c.red(`- ${a[i]}`)); i++; } else { lines.push(c.green(`+ ${b[j]}`)); j++; } } while (i < a.length) lines.push(c.red(`- ${a[i++]}`)); while (j < b.length) lines.push(c.green(`+ ${b[j++]}`)); return lines.join('\n'); } // -------------------------------------------------------------------------- // Report // -------------------------------------------------------------------------- /** * The provider's name when the estimator was not calibrated for it. * * `estimateTokens` is a heuristic tuned against Claude's tokenizer, and every * band descends from that. Printing one beside a GPT or Kimi figure states a * precision nobody has measured for that family — and since the catalogue grew * past Anthropic, that is most of it. Returns null when the model is * Anthropic's, where the band is at least the claim it was written for. */ function offFamilyName(modelId: string): string | null { const provider = getModel(modelId).provider; if (provider === undefined || provider === 'anthropic') return null; return getModel(modelId).displayName; } /** * How far off the estimator has been measured on this model's family. * * Read from the catalogue's provider rather than from the display name, because * the name is what a reader sees and the id is what was measured. Null on the * calibrated family and on every family nobody has run — an unmeasured one gets * a sentence saying so, never a borrowed figure. */ function offFamilyError(modelId: string): number | null { return measuredForeignError(foreignTokenizer(getModel(modelId).provider ?? null)); } /** * Language codes as names, in the reader's language. * * Built from `PHRASE_LANGUAGES` rather than written out, so a language added to * the dictionaries appears here without anybody remembering to edit a sentence. */ function languageNames(codes: readonly string[], t: CliMessages): string { const names = codes.map((code) => t.languages[code] ?? code); if (names.length <= 1) return names[0] ?? ''; return `${names.slice(0, -1).join(', ')} ${t.languages.and} ${names[names.length - 1]}`; } /** * Advisories whose entire pitch is money. * * On a subscription these are not weaker advice, they are not advice: "use a * cheaper model" saves nothing on a flat plan, and its detail text quotes dollars * per month, so suppressing only the price tag beside the title left the money in * the sentence underneath. */ const MONEY_ONLY_ADVISORIES: ReadonlySet = new Set([ 'model-downgrade', 'batch-api', 'output-dominated', 'promo-pricing', 'prompt-caching-not-worth-it', ]); /** * The one thing worth doing about this prompt, and how it compares to shortening it. * * `null` when there is nothing to say: no advisory carries a figure, or the * reader is on a subscription where a monthly saving is meaningless. A heading * with a shrug under it is worse than no heading. */ function biggestLever( result: OptimizationResult, tokensOnly: boolean, t: CliMessages, ): { line: string } | null { /** * One guard, and it is the only thing deciding. * * The first version also filtered the candidate list by `!tokensOnly`, which * duplicated this and made it untestable: removing the guard left the filter * still suppressing the line, so a mutation that priced a subscription passed * the suite. Two checks for one condition is one check and one place for a bug. */ if (tokensOnly) return null; const best = result.advisories.find((a) => (a.estimatedMonthlyUsd ?? 0) > 0); if (!best?.estimatedMonthlyUsd) return null; const ruleSaving = result.savings.monthlySavingsUsd; return { line: t.report.biggestLeverDetail( best.title, formatUsd(best.estimatedMonthlyUsd), // The multiple is the point of the line, and it is only honest when there // is something to divide by. A prompt the rules could not improve at all // gets the amount and no ratio rather than a division by zero dressed up. ruleSaving > 0 ? Math.round(best.estimatedMonthlyUsd / ruleSaving) : null, ), }; } function printReport( result: OptimizationResult, showDiff: boolean, t: CliMessages, examplesReview: ExampleReview | null = null, reorder: ReorderResult | null = null, tokensOnly = false, host: HostEnvironment = { id: 'terminal', displayName: 'terminal', billing: 'unknown', evidence: null }, suggestions: { result: SuggestResult; applied: boolean; locale: Locale } | null = null, /** They named a scenario, and the host is suppressing the money anyway. */ namedScenario = false, /** Present when the usage came from a log rather than from typing. */ measured: MeasuredUsage | null = null, ): void { const n = (value: number): string => value.toLocaleString(t.numberLocale); const sourceNote = result.tokenSource === 'heuristic' ? c.dim( t.report.estimated( offFamilyName(result.usage.model), bandFor(result.optimized), offFamilyError(result.usage.model), ), ) : c.dim(t.report.exactCount()); /** * The largest lever, first. * * This line used to be the last thing in the report and it is the most useful * thing in it. Measured on an ordinary support prompt — already reasonably * written, which is what a real one is — the rules recover **three tokens of * 306**, worth $0.75 a month, while the cache reorder sitting below them is * worth $48. The report opened with the 1.3% and closed with the 64×. * * That ordering is not a presentation quibble. It teaches the reader that * shortening the prompt is what this tool is for, and on any prompt somebody * competent wrote, shortening it is the smallest thing available. The rules * earn their keep on genuine bloat — a duplicated paragraph, "due to the fact * that" — and recover close to nothing once that is gone, because they recover * waste rather than creating savings. * * So the answer to "what should I do about this prompt" goes at the top, and * the token count follows as the detail it is. */ const best = biggestLever(result, tokensOnly, t); if (best) { console.log(); console.log(c.bold(t.report.biggestLever())); console.log(` ${c.dim(wrap(best.line, 74, ' '))}`); } console.log(); console.log(c.bold(t.report.inputTokens())); console.log( ` ${n(result.tokensBefore)} → ${c.green(n(result.tokensAfter))} ${c.bold( `-${result.reductionPct.toFixed(1)}%`, )}${sourceNote}`, ); // Before the rules, because the rearrangement is the bigger change and the // one the reader has to make a judgement about. // // Only when there is something to say. "Nothing could safely move" with no // refusals underneath is a heading, a blank line and a shrug — the reader // asked for a rearrangement, there was none available, and the token count // above already told them nothing changed. if (reorder !== null && (reorder.moved.length > 0 || reorder.declined.length > 0)) { console.log(); console.log(sectionHeading(t.report.reorderHeading())); if (reorder.moved.length === 0) { console.log(` ${c.dim(t.report.reorderNothing())}`); } else { console.log(` ${t.report.reorderMoved(reorder.moved.length, n(reorder.tokensMoved))}`); console.log( ` ${c.green( t.report.reorderPrefix(n(reorder.prefixTokensBefore), n(reorder.prefixTokensAfter)), )}`, ); } // Refusals are reported even when the move succeeded: a saving Trazum chose // not to take is one the author cannot evaluate unless they are told. if (reorder.declined.length > 0) { console.log(` ${c.dim(t.report.reorderDeclined(reorder.declined.length))}`); const SHOWN = 3; for (const d of reorder.declined.slice(0, SHOWN)) { const excerpt = truncate(d.text.trim().replace(/\s+/g, ' '), 48); console.log( ` ${c.dim( d.reason === 'uncovered-script' ? t.report.reorderDeclinedScript(d.script ?? '') : d.reason === 'backward-reference' ? t.report.reorderDeclinedRef(d.phrase ?? '', excerpt) : t.report.reorderDeclinedAfter(excerpt), )}`, ); } // Say that the list was cut. A report that shows three of nine reads as // "three" unless it admits otherwise. if (reorder.declined.length > SHOWN) { console.log(` ${c.dim(t.report.reorderDeclinedMore(reorder.declined.length - SHOWN))}`); } } if (reorder.moved.length > 0) console.log(` ${c.yellow(t.report.reorderReview())}`); } if (result.rules.length > 0) { console.log(); console.log(c.bold(t.report.rulesApplied())); /** * Whose judgement just edited this prompt. * * Five of the seven dictionaries were written without anybody who reads the * language agreeing to an entry, and the branch where that matters most is * this one: the rules did not stay silent, they changed somebody's text. * The coverage line under `nothingToTrim` cannot cover it — by then the * prompt is untouched. * * Gated on the prompt's own detected language, so an English or Spanish * prompt never sees it and it never becomes a footer. `detectTextLanguage` * answers null on a short or mixed prompt, and this stays silent then: * not-detected is not not-unreviewed, and guessing the language in order to * warn about it would be the same overreach the detector exists to refuse. */ const promptLanguage = detectTextLanguage(result.original); if (promptLanguage !== null && dictionaryStanding(promptLanguage)?.standing === 'unreviewed') { console.log( c.yellow(t.report.dictionaryAppliedUnreviewed(t.languages[promptLanguage] ?? promptLanguage)), ); } for (const rule of result.rules) { const tag = rule.level === 'aggressive' ? c.yellow(t.report.levelAggressive()) : c.dim(t.report.levelSafe()); console.log(` ${tag} ${rule.title} ${c.dim(t.report.ruleHits(rule.hits, rule.tokensSaved))}`); // What the rule actually did. Shown under the aggressive level by // default because that is the one whose advice is "read the diff", and // a diff of everything at once is not something anyone reads. const showChanges = showDiff || rule.level === 'aggressive'; if (showChanges) { for (const change of rule.changes) { const from = c.red(truncate(change.before, 46)); const to = change.after ? c.green(truncate(change.after, 30)) : c.dim('—'); console.log(` ${from} ${c.dim('→')} ${to}`); } if (rule.hits > rule.changes.length && rule.changes.length > 0) { console.log(c.dim(` ${t.report.moreChanges(rule.hits - rule.changes.length)}`)); } } } } else { console.log(); console.log(c.dim(t.report.nothingToTrim())); // Which languages the dictionaries actually cover. Only here, because this // is the one branch where silence reads as "your prompt is already clean". console.log(c.dim(t.report.dictionaryCoverage(languageNames(PHRASE_LANGUAGES, t)))); // And which of them nobody here reads. Naming the seven and stopping there // reads as seven equal dictionaries; five of them have never been agreed by // a speaker of the language, and a reader deciding whether to trust an // empty result deserves the difference. const unreviewed = languagesWithStanding(PHRASE_LANGUAGES, 'unreviewed'); if (unreviewed.length > 0) { console.log(c.dim(t.report.dictionaryUnreviewed(languageNames(unreviewed, t)))); } } if (result.llm) { console.log(); console.log(c.bold(t.report.llmPass())); if (result.llm.applied) { console.log( ` ${c.green( t.report.llmApplied( result.llm.provider, result.llm.model, result.llm.tokensBefore, result.llm.tokensAfter, ), )}`, ); } else { console.log(` ${c.yellow(t.report.llmRejected(result.llm.rejectedReason ?? ''))}`); } } // On a subscription there is no bill to reduce. Everything below this point // would be arithmetic about tokens dressed as money, and "$184/month" told to // somebody on a flat plan is wrong in the direction that matters most. // // What replaces it is the thing that *is* scarce there: the context window. if (tokensOnly) { printTokensOnly(result, host, t, n, namedScenario); } else { printMoney(result, t, n, measured); } // On a subscription, an advisory whose entire pitch is money is not weaker // advice — it is not advice. "Use a cheaper model" saves nothing on a flat // plan, and its detail text quotes dollars per month, so suppressing only the // price tag beside the title left the money in the sentence underneath. // // The rest stay: an overflowing context window still fails the call, a // contradiction is still wrong, redundant examples still cost tokens, and // caching still buys latency and rate-limit headroom. const MONEY_ONLY = MONEY_ONLY_ADVISORIES; const advisories = tokensOnly ? result.advisories.filter((a) => !MONEY_ONLY.has(a.id)) : result.advisories; if (advisories.length > 0) { console.log(); console.log(c.bold(t.report.beyondShortening())); // The amount goes in a column of its own rather than trailing the title. // Four advisories worth $506, $422, $170 and nothing are meant to be // compared, and comparing them meant reading to the end of four different // sentences to find where the numbers were. // // The advisory itself still applies on a subscription — caching and a // smaller model both buy back context and rate-limit headroom. Only the // price tag is meaningless, so only the price tag goes. const amountOf = (a: (typeof advisories)[number]): string => !tokensOnly && a.estimatedMonthlyUsd !== null ? formatUsd(a.estimatedMonthlyUsd) : ''; const width = Math.max(0, ...advisories.map((a) => amountOf(a).length)); // Indent the wrapped detail to the start of the title, so the prose forms // one block instead of stepping around the numbers. const gutter = ' '.repeat(4 + (width > 0 ? width + 2 : 0)); for (const advisory of advisories) { const marker = advisory.severity === 'warning' ? c.yellow('!') : advisory.severity === 'opportunity' ? c.cyan('→') : c.dim('·'); const amount = amountOf(advisory); const column = width > 0 ? `${c.green(amount.padStart(width))} ` : ''; console.log(` ${marker} ${column}${c.bold(advisory.title)}`); console.log(`${gutter}${c.dim(wrap(advisory.detail, 78 - gutter.length, gutter))}`); } // The "start here" line is printed at the top of the report now, where a // reader who stops after four lines still sees it. } printSuggestions(suggestions, t, n); printRest(result, showDiff, t, examplesReview, n); /** * Where the money actually is, said at the front door. * * `optimize` is the first command anybody runs, and it reports the smallest * line item on the bill: measured, about 1% of a monthly figure. Everything * that moves 60% to 80% — which model the call goes to, the Batch API, * caching, what re-sending the conversation costs — lives in `profile`, which * needs a usage log a new reader does not have and has no reason to go looking * for. * * A tool that learned that and only said it in the command you reach last has * not said it. So it prints here, once, at the end, on every run: this is the * small lever, and the big ones are one file away. */ console.log(); console.log( ` ${c.dim(wrap(tokensOnly ? t.report.beyondThisPromptTokensOnly() : t.report.beyondThisPrompt(), 74, ' '))}`, ); } /** The cost section, for anyone billed by the token. */ function printMoney( result: OptimizationResult, t: CliMessages, n: (v: number) => string, /** Present when the usage came from a log rather than from typing. */ measured: MeasuredUsage | null = null, ): void { const { savings } = result; console.log(); console.log(c.bold(t.report.costWith(savings.modelDisplayName))); /** * The usage line names its provenance. "1,000 calls/month" typed and * "1,043 calls measured over 12 days, scaled" are different claims about * the same multiplication, and the reader budgeting on the result must * know which one they are holding. Under the week floor nothing is scaled * and nothing says "month": the figures cover exactly the period measured. */ if (measured !== null) { if (measured.scaled !== null) { console.log( ` ${t.report.usageLineMeasured( n(measured.calls), measured.scaled.fromDays.toFixed(1), n(result.usage.callsPerMonth), result.usage.avgOutputTokens, result.usage.batchEligible, )}`, ); } else { console.log( ` ${t.report.usageLineMeasuredPeriod( n(measured.calls), measured.spanDays === null ? null : measured.spanDays.toFixed(1), result.usage.avgOutputTokens, result.usage.batchEligible, )}`, ); } if (measured.models.count > 1) { console.log( ` ${c.dim(wrap(t.report.measuredModelShare(measured.models.chosen, `${(measured.models.chosenShareOfSpend * 100).toFixed(0)}%`, n(measured.models.count)), 74, ' '))}`, ); } if (measured.outputUnmeasured) { console.log(` ${c.dim(wrap(t.report.measuredNoOutput(), 74, ' '))}`); } } else { console.log( ` ${t.report.usageLine( n(result.usage.callsPerMonth), result.usage.avgOutputTokens, result.usage.batchEligible, )}`, ); } // Said, not assumed. Once prices can be overlaid locally, a figure from the // bundled catalogue and a figure from somebody's JSON file look identical, and // the reader has to be able to tell which one they are about to budget against. const touched = [ ...result.pricingSource.overriddenModels, ...result.pricingSource.addedModels, ]; if (touched.length > 0) { console.log( ` ${c.yellow(t.report.pricingOverlaid(touched.join(', '), result.pricingSource.lastReviewed))}`, ); } const periodOnly = measured !== null && measured.scaled === null; console.log( ` ${formatUsd(savings.perMonth.before.totalUsd)} → ` + `${c.green(formatUsd(savings.perMonth.after.totalUsd))} ` + c.bold( periodOnly ? t.report.perPeriodSaving( formatUsd(savings.monthlySavingsUsd), savings.monthlySavingsPct.toFixed(1), ) : t.report.perMonthSaving( formatUsd(savings.monthlySavingsUsd), savings.monthlySavingsPct.toFixed(1), ), ), ); if (periodOnly) { console.log( ` ${c.dim(wrap(t.report.periodNotScaled(measured!.spanDays === null ? null : measured!.spanDays.toFixed(1)), 74, ' '))}`, ); } } /** * What the saving buys when there is no bill: room. * * The context window is the scarce thing inside an agent — every token the * system prompt holds is one the conversation cannot. That is a real saving and * a measurable one, and it is the honest answer to "what did I gain" on a plan * that costs the same either way. */ function printTokensOnly( result: OptimizationResult, host: HostEnvironment, t: CliMessages, n: (v: number) => string, /** Whether they named a scenario while the money was being withheld. */ namedScenario = false, ): void { const model = getModel(result.usage.model); const saved = result.tokensBefore - result.tokensAfter; console.log(); console.log(sectionHeading(t.report.tokensOnlyHeading(host.displayName))); // Only claim the host bills by subscription when it does. Forced with the // flag on GitHub Actions, the first version said "GitHub Actions bills by // subscription", which is simply false. console.log( ` ${ host.billing === 'subscription' ? t.report.tokensOnlyWhy(host.displayName) : t.report.tokensOnlyAsked() }`, ); console.log(); console.log(` ${c.green(t.report.tokensSaved(n(saved)))}`); /** * Share of the window, which is what a saved token is actually worth here. * * A 225-token prompt against a million-token window printed `0.0% → 0.0%`: a * line whose whole job is to say what a token buys, saying nothing twice. When * both sides round to the same figure the honest statement is the other one — * that the window is not the constraint on this prompt. */ const share = (tokens: number): string => `${((tokens / model.contextWindow) * 100).toFixed(1)}%`; const before = share(result.tokensBefore); const after = share(result.tokensAfter); /** * Three cases, and the first version had two. * * Equal shares mean either "this prompt is nothing against a million tokens" or * "this prompt is 10% of the window and one token did not move it". Using the * negligible message for both told a reader holding a tenth of a Haiku window * that they were under a tenth of a percent — off by two orders of magnitude, * on a line whose only job is to size the prompt against the window. */ const unchanged = before === after; const negligible = after === '0.0%'; console.log( ` ${c.dim( !unchanged ? t.report.windowUse(before, after, model.displayName, n(model.contextWindow)) : negligible ? t.report.windowNegligible(n(result.tokensAfter), model.displayName, n(model.contextWindow)) : t.report.windowUnmoved(after, model.displayName, n(model.contextWindow)), )}`, ); console.log(` ${c.dim(namedScenario ? t.report.tokensOnlyAskedFor() : t.report.tokensOnlyCost())}`); } /** * The proposed rewrites. * * A list, not a diff, because that is the shape of the decision: each line is * one phrase and its replacement, and the reader is answering "yes" or "no" to * that phrase rather than to a rewritten prompt. * * Rejections are summarised rather than listed one by one. "Four proposals did * not survive checking" is the useful fact; which four is noise unless you are * debugging the model, and `--json` has them for when you are. */ function printSuggestions( suggestions: { result: SuggestResult; applied: boolean; locale: Locale } | null, t: CliMessages, n: (value: number) => string, ): void { if (!suggestions) return; const { result, applied, locale } = suggestions; if (result.suggestions.length === 0) { // Say so. A silent absence reads as "the flag did nothing". console.log(`\n${c.bold(t.report.suggestHeading())}`); console.log(` ${c.dim(t.report.suggestNothing(result.provider, result.model))}`); if (result.rejected.length > 0) { console.log(` ${c.dim(t.report.suggestRejected(result.rejected.length))}`); } return; } const total = result.suggestions.reduce((sum, s) => sum + s.tokensSaved, 0); console.log(`\n${c.bold(t.report.suggestHeading())}`); console.log( ` ${c.dim( applied ? t.report.suggestApplied(result.suggestions.length, n(total)) : t.report.suggestOffered(result.suggestions.length, n(total)), )}`, ); for (const s of result.suggestions) { const after = s.after === '' ? c.dim(t.report.suggestRemoved()) : c.green(truncate(s.after, 40)); const times = s.offsets.length > 1 ? c.dim(` ×${s.offsets.length}`) : ''; console.log( ` ${c.red(truncate(s.before, 44))} ${c.dim('→')} ${after}` + ` ${c.dim(`~${n(s.tokensSaved)}`)}${times}`, ); } if (result.rejected.length > 0) { console.log(` ${c.dim(t.report.suggestRejected(result.rejected.length))}`); // The most common reason, named. Four rejections all saying "the model // paraphrased what it quoted" is a fact about the model worth knowing. const counts = new Map(); for (const r of result.rejected) counts.set(r.reason, (counts.get(r.reason) ?? 0) + 1); const [reason] = [...counts.entries()].sort((a, b) => b[1] - a[1])[0]!; console.log(` ${c.dim(rejectionText(reason as RejectedReason, locale))}`); } if (!applied) console.log(` ${c.dim(t.report.suggestHowToApply())}`); } function printRest( result: OptimizationResult, showDiff: boolean, t: CliMessages, examplesReview: ExampleReview | null, n: (v: number) => string, ): void { if (examplesReview && examplesReview.groups.length > 0) { console.log(); console.log(c.bold(t.report.examplesReview())); console.log( c.dim( ` ${t.report.examplesReviewNote( examplesReview.provider, examplesReview.model, examplesReview.exampleCount, )}`, ), ); for (const group of examplesReview.groups) { console.log( ` ${c.yellow(t.report.exampleRedundant(group.redundant, group.keep))}` + c.dim(` (~${group.tokens} tokens)`), ); if (group.reason) console.log(` ${c.dim(group.reason)}`); } } if (showDiff) { console.log(); console.log(c.bold(t.report.diff())); console.log(renderDiff(result.original, result.optimized, t)); } console.log(); } /** Shortens a snippet for the change list, keeping it on one line. */ function truncate(text: string, max: number): string { const clean = text.replace(/\s+/g, ' ').trim(); return clean.length <= max ? clean : `${clean.slice(0, max - 1)}\u2026`; } /** Wraps a paragraph to a given width. */ function wrap(text: string, width: number, indent: string): string { const words = text.split(/\s+/); const lines: string[] = []; let line = ''; for (const word of words) { if (line.length + word.length + 1 > width) { lines.push(line); line = word; } else { line = line ? `${line} ${word}` : word; } } if (line) lines.push(line); return lines.join(`\n${indent}`); } // -------------------------------------------------------------------------- // Subcommands // -------------------------------------------------------------------------- /** * A provider's stand-in model, for when the code names who but not which. * * The same capability as the global default, so the figure is comparable with * what Trazum would have printed anyway, and the cheapest at that capability so * the guess errs downwards — overstating somebody's bill on a model they never * chose is the worse direction to be wrong in. */ function defaultModelFor(provider: string, pricing: PricingCatalogue): string | null { // Nearest capability, not an exact match. Matching exactly returned nothing // for OpenAI and DeepSeek — neither has a `large` model, so the code fell // through to the global default and printed "goes to openai / priced as // Claude Opus 5" anyway. A ladder with different rungs is the normal case, // not an edge one. const RANK: Record = { small: 0, mid: 1, large: 2, frontier: 3 }; const want = RANK[getModel(DEFAULT_USAGE.model).capability] ?? 2; /** * Offered first, and a retired model only when the provider has nothing else. * * `isOffered` is the rule everywhere a model is recommended, and this is not * quite that: nothing is being recommended here, a provider's call is being * priced. The distinction matters because three providers in this catalogue * have **only** retired ids today, and dropping them would return null and * fall through to the global default — printing "goes to deepseek / priced as * Claude Opus 5", which is wrong by an order of magnitude in the direction * this function exists to avoid. * * So a retired model's price is still that provider's price and is used as a * last resort. What the reader is not left to infer is that it is current: * `buildAdvisories` warns by name on a retired id, quoting the provider. */ const offered = pricing.models.filter((m) => m.provider === provider && isOffered(m)); const candidates = offered.length > 0 ? offered : pricing.models.filter((m) => m.provider === provider); const best = candidates.reduce<(typeof candidates)[number] | null>((chosen, m) => { if (chosen === null) return m; const distance = Math.abs((RANK[m.capability] ?? 2) - want); const chosenDistance = Math.abs((RANK[chosen.capability] ?? 2) - want); if (distance !== chosenDistance) return distance < chosenDistance ? m : chosen; // Same distance: the cheaper one, so the guess errs downwards. Overstating // somebody's bill on a model they never chose is the worse way to be wrong. return m.inputPerMTok < chosen.inputPerMTok ? m : chosen; }, null); return best?.id ?? null; } /** * Reads a source file as the prompts it holds, rather than as one big prompt. * * Returns null for anything that is not a source file, which is the ordinary * case: a `.txt` or `.md` prompt goes through untouched. * * For a source file it **refuses rather than guesses**. Optimising TypeScript * as if it were prose does not produce a worse prompt, it produces broken code * — `import OpenAI` came back as `Import OpenAI` from the capitalisation rule — * and `-o` would write that over the file. A refusal with the marker syntax in * it costs the reader one comment; the alternative cost them a compile. */ function sourceFileOf( target: string, raw: string, pricing: PricingCatalogue, wanted: string | undefined, ): { text: string; model?: string } | null { const isSource = SOURCE_EXTENSIONS.some((ext) => target.toLowerCase().endsWith(ext)); if (!isSource) return null; // The catalogue in effect rather than the bundled one: an overlay can add a // model, and a detection that cannot see it would fall back for no reason. const detection = detectFromSource(raw, { models: pricing.models }); // An import names who, never which — so a file that plainly calls OpenAI was // still being priced against Claude Opus 5. The provider's own stand-in is a // guess about which of their models rather than about whose, which is the // difference that matters. `trazum where` says which it picked and why. const model = detection.model ?? (detection.provider !== null ? (defaultModelFor(detection.provider, pricing) ?? undefined) : undefined); if (!hasMarker(raw)) { throw new Error(t_sourceNeedsMarker(target)); } const { prompts, declined } = extractPrompts(raw); if (prompts.length === 0) { const why = declined[0]; throw new Error( why ? `${target}: the marker on line ${why.line} could not be read — ${why.detail}` : `${target}: nothing was extracted from the markers in this file.`, ); } // One prompt is unambiguous. Several need naming, because optimising "the // first one" silently is how the wrong prompt ends up rewritten. const chosen = wanted !== undefined ? prompts.find((p) => p.name === wanted || promptId(target, p) === wanted) : prompts.length === 1 ? prompts[0] : undefined; if (!chosen) { const names = prompts.map((p) => promptId(target, p)).join('\n '); throw new Error( wanted !== undefined ? `${target} has no marked prompt called "${wanted}". It holds:\n ${names}` : `${target} holds ${prompts.length} marked prompts. Name one with --prompt:\n ${names}`, ); } return { text: chosen.text, ...(model ? { model } : {}) }; } /** Kept as a function so the sentence is in one place rather than two. */ const t_sourceNeedsMarker = (target: string): string => `${target} looks like source, not a prompt. Optimising it would rewrite your code — ` + 'mark the prompt with a `// trazum:prompt` comment above the literal, or pass the ' + 'prompt itself in a .txt file.'; /** * Says which provider a prompt is actually sent to, and how it knows. * * Trazum priced one vendor, so the default cost nothing. Pricing seven made it a * wrong number: a file calling OpenAI was billed against Claude Opus 5 without * comment. This reads what the code already says instead. * * Every answer names the line it came from. A detection this command cannot * justify is a guess, and the number that follows from it would be a guess too. */ async function commandWhere( args: Args, config: TrazumConfig, pricing: PricingCatalogue, t: CliMessages, ): Promise { const host = detectHost(); console.log(); console.log(c.bold(t.where.hostHeading())); console.log( ` ${host.displayName}${host.evidence ? c.dim(` (${host.evidence})`) : ''}`, ); // The reason this is worth printing at all. Inside a flat plan the monthly // figure Trazum computes is arithmetic about tokens, not money anybody gets // back, and saying so is more useful than saying nothing. if (host.billing === 'subscription') { console.log(` ${c.yellow(t.where.subscription(host.displayName))}`); } const target = args.positional[0]; if (target === undefined) { console.log(); console.log(c.dim(t.where.noTarget())); console.log(); return; } const source = await readFile(target, 'utf8'); const detection = detectFromSource(source, { models: pricing.models }); console.log(); console.log(c.bold(t.where.sourceHeading(target))); if (detection.conflicts.length > 0) { // Two answers is not a weaker version of one answer. Naming both and // declining is the only honest output here. console.log(` ${c.red(t.where.conflict())}`); for (const e of detection.evidence.slice(0, 4)) { console.log(` ${c.dim(t.where.evidenceLine(e.line ?? 0, e.kind, e.detail))}`); } console.log(` ${c.dim(t.where.conflictFallback())}`); } else if (detection.provider === null) { console.log(` ${c.dim(t.where.nothingFound())}`); } else { const model = detection.model ? getModel(detection.model) : null; console.log( ` ${detection.provider}${model ? ` · ${model.displayName}` : c.dim(t.where.providerOnly())}`, ); for (const e of detection.evidence.slice(0, 3)) { console.log(` ${c.dim(t.where.evidenceLine(e.line ?? 0, e.kind, e.detail))}`); } } // What would actually be used, which is the question behind the question. // Flags beat config, config beats detection, detection beats the default — // and a reader deciding whether to pass --model needs to see which won. // // Knowing the provider but not the model is the common case: an import names // who, never which. Falling through to the built-in default there would print // "goes to openai" and "priced as Claude Opus 5" three lines apart, which is // the wrong number this command exists to catch, produced by the command // itself. A provider's own default is a guess, but it is a guess about which // of their models rather than about whose. const configured = config.usage?.model; const detected = detection.model ?? (detection.provider !== null ? defaultModelFor(detection.provider, pricing) : null); const effective = configured ?? detected ?? DEFAULT_USAGE.model; const reason = configured ? t.where.fromConfig() : detection.model ? t.where.fromDetection() : detected ? t.where.fromProviderDefault(detection.provider ?? '') : t.where.fromDefault(); console.log(); console.log(c.bold(t.where.pricedAs())); console.log(` ${getModel(effective).displayName} ${c.dim(reason)}`); console.log(); } /** * Where `init` looks for a usage log before it gives up and says so. * * A short list of the names people actually use, checked in order — not a * glob over the whole tree. A first run that finds a log by searching two * thousand directories has spent the patience it was given, and a log found * in `vendor/fixtures/` is more likely to be somebody's test data than their * bill. */ const INIT_LOG_CANDIDATES = [ 'usage.jsonl', 'usage.ndjson', 'usage.log', 'logs/usage.jsonl', 'logs', '.trazum/usage.jsonl', ]; /** * Extensions worth reading for a provider sighting. * * `SOURCE_EXTENSIONS`, the same list `rank` and `doctor` walk, rather than a * second copy that drifts — a language added for extraction is a language * `init` should be able to detect a provider in, and one list is how that * stays true. Documentation is deliberately not on it: a `.md` file quoting * `from 'openai'` inside a code fence would be read as evidence, and `where` * answers for a file somebody named while this answers for a repository * nobody has vouched for. */ /** * Where feedback goes. Compiled in, never configurable. * * A flag or a config key naming this host would let a fork — or anything that * had rewritten a config on disk — point somebody's bug report, and the * prefilled body with it, at a machine they did not choose. It is one string * and it stays one string. */ /** * Which Trazum this is. * * Read from the manifest beside the built entry point rather than baked in by * a generator, so it cannot drift from what npm installed — the one number a * bug report is useless without is the one that must not be a copy. * * `readFileSync` at module load, deliberately: every other read in this file * is async and inside a command, but a version has to be available to * `--version` before any command is chosen, and one small synchronous read at * startup is cheaper than making the whole entry point await. * * A failure falls back to `unknown` rather than throwing. A tool that will not * start because it cannot find its own manifest is worse than one that admits * it does not know — and `unknown` in a bug report is itself a useful fact * about how somebody installed it. */ const VERSION: string = (() => { try { const here = dirname(fileURLToPath(import.meta.url)); const manifest: unknown = JSON.parse(readFileSync(join(here, '..', 'package.json'), 'utf8')); const found = (manifest as { version?: unknown }).version; return typeof found === 'string' ? found : 'unknown'; } catch { return 'unknown'; } })(); const FEEDBACK_REPO = 'https://github.com/Davmunrey/Trazum'; /** Problems listed before the rest are counted. A wall of them helps nobody. */ const MAX_CONFORM_PROBLEMS = 20; /** * The contracts `--contract` accepts, so a typo is refused with the list. * * Imported, never retyped. This was a hand-written copy of the union in * `conform.ts` and it stopped at `cost-answer`: `outcome-report` (1.50.4) and * `annual-record` (1.51.0) had rules, had cross-rules, and were refused by name * with "is not a contract" — the list telling the caller they had made a typo * when the list was the thing that was wrong. */ /** How many source files, and how large each may be. Both reported when they bite. */ const INIT_MAX_SOURCE_FILES = 400; const INIT_MAX_SOURCE_BYTES = 256 * 1024; interface InitRenderContext { host: HostEnvironment; prompts: { files: string[]; truncated: boolean }; usage: UsageSighting[]; unreadable: { where: string; because: string } | null; truncated: boolean; t: CliMessages; pricing: PricingCatalogue; } /** * The first run, printed. * * **The arithmetic comes before the figure**, everywhere below. A tool that * opens with a dollar amount nobody can check gets closed, and the reader has * no reason yet to believe anything this command says — so the headline shows * the calls, the model and the rate it is being compared against, and only * then the money. */ function renderInit(proposal: InitProposal, ctx: InitRenderContext): void { const { t } = ctx; const n = (value: number): string => value.toLocaleString(t.numberLocale); console.log(); console.log(sectionHeading(t.init.heading())); console.log(); // 1. Where this is running. console.log(` ${t.init.host(ctx.host.displayName)}`); if (ctx.host.billing === 'subscription') { console.log(` ${c.yellow(t.where.subscription(ctx.host.displayName))}`); } // 2. The prompts. console.log( ` ${ctx.prompts.files.length === 0 ? c.dim(t.init.noPrompts()) : t.init.prompts(ctx.prompts.files.length)}`, ); if (ctx.truncated) console.log(` ${c.dim(t.init.sourcesTruncated(INIT_MAX_SOURCE_FILES))}`); // 3. The usage, or the two ways there is none. if (ctx.unreadable !== null) { console.log(` ${c.red(t.init.usageUnreadable(ctx.unreadable.where, ctx.unreadable.because))}`); } else if (ctx.usage.length === 0) { console.log(` ${c.dim(t.init.noUsage())}`); } else { for (const sighting of ctx.usage) { console.log(` ${t.init.usageFound(sighting.kind, sighting.where)}`); } } console.log(); // 4. What the config would say, and what it will not. console.log(sectionHeading(t.init.configHeading())); if (proposal.justified.length === 0) { console.log(` ${c.dim(t.init.nothingJustified())}`); } for (const why of proposal.justified) { console.log(` ${c.green('+')} ${c.bold(why.key)} ${initJustification(why, t)}`); } for (const decline of proposal.declined) { console.log(` ${c.dim('·')} ${c.dim(decline.key)} ${c.dim(initDecline(decline, t))}`); } if (proposal.overwrites !== null && proposal.overwrites.keys.length > 0) { console.log(); console.log(` ${c.yellow(t.init.wouldOverwrite(proposal.overwrites.keys.join(', ')))}`); } console.log(); // 5. The single most valuable thing found — arithmetic first. console.log(sectionHeading(t.init.findingHeading())); if (proposal.headline === null) { console.log(` ${c.dim(t.init.noFinding(proposal.noHeadline ?? 'nothing-measured'))}`); console.log(); return; } const { slice, lever, savingUsd, days } = proposal.headline; console.log(` ${t.init.findingCalls(n(slice.calls), slice.label, slice.modelName, days)}`); console.log(` ${t.init.findingSpent(slice.spentUsd.toFixed(2))}`); if (lever !== 'batch' && slice.route !== null) { console.log(` ${t.init.findingRoute(slice.route.candidate.displayName)}`); } if (lever !== 'route' && slice.batch !== null) { console.log(` ${t.init.findingBatch()}`); } console.log(` ${c.bold(t.init.findingTotal(savingUsd.toFixed(2), days))}`); console.log(` ${c.dim(t.init.findingNext())}`); console.log(); } /** Why a key was written, in one line a person reads. */ function initJustification(why: InitJustification, t: CliMessages): string { switch (why.key) { case 'locale': return c.dim(t.init.whyLocale(why.value)); case 'extensions': return c.dim(t.init.whyExtensions(why.value.join(' '), why.files)); case 'usage.model': return c.dim( why.from === 'measured' ? t.init.whyModelMeasured(why.value, Math.round(why.share * 100)) : t.init.whyModelSource(why.value, why.file, why.line), ); case 'usage.callsPerMonth': return c.dim(t.init.whyCalls(why.value, why.calls, why.days)); case 'usage.avgOutputTokens': return c.dim(t.init.whyOutput(why.value, why.outputTokens, why.calls)); case 'usage.cacheHitRate': return c.dim(t.init.whyCache(why.value, why.cacheReadTokens, why.inputTokens)); } } /** Why a key was not written, and what would settle it. */ function initDecline(decline: InitDecline, t: CliMessages): string { switch (decline.why) { case 'no-evidence': return t.init.noModelEvidence(); case 'conflicting-evidence': return t.init.modelConflict(decline.files.join(', ')); case 'provider-only': return t.init.modelProviderOnly(decline.provider, decline.file); case 'nothing-measured': return t.init.nothingMeasured(); case 'window-too-short': return t.init.windowTooShort(decline.days, MIN_RATE_DAYS); case 'undated-calls': return t.init.undatedCalls(decline.undated, decline.calls); case 'not-recorded': return t.init.cacheNotRecorded(); case 'only-you-know': return t.init.batchOnlyYouKnow(); case 'unprovable': return t.init.labelsUnprovable(decline.labels); case 'a-budget-is-a-policy': return decline.measuredUsd === null ? t.init.budgetIsPolicy() : t.init.budgetIsPolicyMeasured(decline.measuredUsd.toFixed(2), decline.days ?? 0); } } /** * `trazum init [dir]` — the first five minutes. * * The floor, not the ceiling. Everything else in this tool assumes you know * which of twenty-two commands answers your question; this one assumes you * have just typed `npx @trazum/cli` and have thirty seconds of patience left. * * It is a **detection, not a wizard**. Nothing is asked. Each step reports * what it found and moves on, and the only decision is whether to write the * file — which `--yes` skips and `--dry-run` refuses. A first run that * interrogates somebody is a first run that gets abandoned halfway. * * The judgement lives in `proposeInit`, in the core, with no filesystem * anywhere near it. This function's whole job is to *look*: walk for prompts, * read a few source files, notice a log or a credential, and hand the lot over * as data. That split is why `--dry-run` cannot drift from the real thing — * they are the same call, and one of them stops before `writeFile`. */ async function commandInit( args: Args, config: TrazumConfig, pricing: PricingCatalogue, t: CliMessages, ): Promise { const root = args.positional[0] ?? '.'; const dryRun = args.flags.get('dry-run') === true; const asJson = args.flags.get('json') === true; // --- what is here ------------------------------------------------------- const host = detectHost(); const prompts = await walkPrompts(root, { extensions: config.extensions ?? DEFAULT_EXTENSIONS, ignore: config.ignore, }); /** * Source files, read only to be asked which provider they call. * * Capped hard and deliberately low. `where` reads one file because somebody * named it; this reads whatever is lying around, and a first run that spends * forty seconds walking a monorepo has already lost. The cap is reported * when it bites, because "no provider found" and "stopped looking" are * different sentences. */ const sourceWalk = await walkPrompts(root, { extensions: SOURCE_EXTENSIONS, maxFiles: INIT_MAX_SOURCE_FILES, ignore: config.ignore, }); const sightings: ProviderSighting[] = []; for (const relative of sourceWalk.files) { const path = join(root, relative); let source: string; /** * Measured and read through **one open handle**, not by path twice. * * A bundle or a lockfile named `.js` is not worth reading, and reading it * is how this command becomes slow on exactly the repositories that need * it most — so the size is checked first. Checking it with `stat(path)` * and then reading `path` is two lookups of the same name, and what * arrives the second time need not be what was measured the first: the * bound would be enforced against a file that is no longer there. One * handle, stat'ed and read, is the same inode by construction. */ let handle; try { handle = await open(path, 'r'); } catch { continue; } try { const info = await handle.stat(); if (info.size > INIT_MAX_SOURCE_BYTES) continue; source = await handle.readFile('utf8'); } catch { continue; } finally { await handle.close(); } const detection = detectFromSource(source, { models: pricing.models }); if (detection.provider !== null || detection.model !== null || detection.conflicts.length > 0) { sightings.push({ file: relative, detection }); } } // --- where the usage is, if it is anywhere ------------------------------ const usage: UsageSighting[] = []; for (const candidate of INIT_LOG_CANDIDATES) { /** * An existence check and nothing more — what is recorded is the *name* * that was tried, and whether it is a file or a directory. Anything read * later is opened then, on its own terms, so there is no measurement here * for a later read to disagree with. */ try { const info = await stat(join(root, candidate)); usage.push({ kind: info.isDirectory() ? 'log-directory' : 'log-file', where: candidate, provider: null, }); } catch { // Absent is the common case and not an error. } } try { const info = await stat(join(root, STORE_DIR)); if (info.isDirectory()) { usage.push({ kind: 'store', where: STORE_DIR, provider: null }); } } catch { // No store yet. } /** * A credential is named by its **variable**, never read. * * `findCredential` returns the value as well because the connector needs it; * this takes the name and drops the rest on the floor. A first-run summary * is the single most likely output in this product to be pasted into a chat * window, and the rule that has held since 1.41 holds here. */ for (const connector of CONNECTORS) { const found = findCredential(connector, process.env); if (found !== null) { usage.push({ kind: 'connector-credential', where: found.source.variable, provider: connector.id, }); } } // --- read what can be read ---------------------------------------------- let measured: UsageProfileReport | null = null; let unreadable: { where: string; because: string } | null = null; const readable = usage.find((u) => u.kind === 'log-file' || u.kind === 'log-directory'); if (readable !== undefined) { try { const files = readable.kind === 'log-file' ? [join(root, readable.where)] : (await readdir(join(root, readable.where))) .filter((name) => LOG_EXTENSIONS.some((extension) => name.endsWith(extension))) .sort() .map((name) => join(root, readable.where, name)); if (files.length > 0) { const texts = await Promise.all(files.map((file) => readUsageLog(file, t))); measured = profileUsage(texts.join('\n'), { catalogue: pricing }); } } catch (error) { // Named, never swallowed. A log that is there and cannot be read is the // single most useful thing this command can tell somebody, and treating // it as "no usage found" would send them to configure a connector they // do not need. unreadable = { where: readable.where, because: error instanceof Error ? error.message : String(error), }; } } // --- the config already there ------------------------------------------- const configPath = join(root, CONFIG_FILENAME); let existing: InitObservations['existing'] = null; try { existing = { path: configPath, config: parseConfig(await readFile(configPath, 'utf8'), configPath) }; } catch { // Absent, or unparseable. Either way there is nothing to compare against, // and `init` refuses to overwrite below rather than reasoning about it. } let unparseable = false; if (existing === null) { try { await stat(configPath); unparseable = true; } catch { // Genuinely absent. } } const askedLocale = matchLocale( LOCALE_ENV_VARS.map((name) => process.env[name]).find((value) => matchLocale(value)), ); const proposal = proposeInit( { host, sightings, promptFiles: prompts.files, usage, measured, locale: askedLocale ?? null, existing, }, { catalogue: pricing }, ); if (asJson) { console.log(JSON.stringify({ ...proposal, unreadable, truncated: sourceWalk.truncated }, null, 2)); return; } renderInit(proposal, { host, prompts, usage, unreadable, truncated: sourceWalk.truncated, t, pricing }); // --- writing it --------------------------------------------------------- // // Three ways this ends and they are kept apart: nothing to write, refused to // overwrite, written. "Nothing happened" with no reason is the output that // makes somebody run the command twice. if (Object.keys(proposal.config).length === 0) { console.log(c.dim(t.init.nothingToWrite())); console.log(); return; } const body = `${JSON.stringify(proposal.config, null, 2)}\n`; if (dryRun) { console.log(c.bold(t.init.wouldWrite(configPath))); console.log(); console.log(body.trimEnd()); console.log(); return; } if (unparseable) { console.log(c.yellow(t.init.existingUnparseable(configPath))); console.log(); return; } if (existing !== null && args.flags.get('yes') !== true) { console.log(c.yellow(t.init.existingRefused(configPath))); console.log(); return; } await writeFile(configPath, body, 'utf8'); console.log(c.green(t.init.wrote(configPath))); console.log(); } /** * `trazum conform ` — does this document conform, and what will it not * be able to answer? * * The command that makes the five contracts something to build against rather * than something to read about. An emitter — a logging wrapper somebody wrote * this afternoon, a connector for a provider this repository has never heard * of, a dashboard writing profile documents of its own — points this at what * it produced and finds out before shipping. * * **The second half is the useful half.** "Valid" is a yes or no. "Here is * what a valid document of this shape cannot tell you, and the field that * would unlock each" is the answer somebody acts on: a usage log with no * `session` is perfectly conformant and simply has no conversation growth in * it, and an emitter that only ever hears "valid" ships it and never finds out * why half the report is empty. * * Exits 1 on a problem, so it gates. It never exits 1 on an *unavailable * finding*: choosing not to log sessions is a decision, not a defect, and a * gate that failed on it would be this tool telling somebody what to record. */ async function commandConform(args: Args, t: CliMessages): Promise { const target = args.positional[0]; if (target === undefined) throw new Error(t.conform.noTarget()); const named = stringFlag(args, 'contract'); // Widened for the membership test only: the array is `as const` so the union // is derived from it, and narrowing back is what `isContract` does below. const isContract = (value: string): value is ContractName => (CONTRACT_NAMES as readonly string[]).includes(value); if (named !== undefined && !isContract(named)) { throw new Error(t.conform.badContract(named, CONTRACT_NAMES.join(', '))); } // A document or a log, not a prompt: deliberately uncapped (limit null). const text = target === '-' ? await readInput('-', t, null) : await readUsageLog(target, t); const report = conform(text, named === undefined ? {} : { contract: named }); if (boolFlag(args, 'json')) { console.log(JSON.stringify(report, null, 2)); if (!report.conforms) process.exitCode = 1; return; } console.log(); if (report.contract === null) { console.log(c.red(t.conform.unrecognised(target))); console.log(` ${c.dim(wrap(report.because ?? '', 74, ' '))}`); console.log(); process.exitCode = 1; return; } console.log( c.bold( report.records === null ? t.conform.heading(target, report.contract) : t.conform.headingLog(target, report.contract, report.records), ), ); if (report.problems.length === 0) { console.log(` ${c.green(t.conform.conforms())}`); } else { for (const problem of report.problems.slice(0, MAX_CONFORM_PROBLEMS)) { console.log(` ${c.red(t.conform.problem(problem.at, problem.kind, problem.detail))}`); } if (report.problems.length > MAX_CONFORM_PROBLEMS) { console.log(` ${c.dim(t.conform.moreProblems(report.problems.length - MAX_CONFORM_PROBLEMS))}`); } process.exitCode = 1; } if (report.unavailable.length > 0) { console.log(); console.log(sectionHeading(t.conform.unavailableHeading())); for (const gap of report.unavailable) { console.log(` ${c.dim(wrap(t.conform.unavailable(gap.finding, gap.because, gap.unlockedBy), 74, ' '))}`); } // Said out loud, because the exit code says it silently and somebody // reading a red-and-yellow screen will assume both halves gated. console.log(` ${c.dim(wrap(t.conform.unavailableNeverGates(), 74, ' '))}`); } console.log(); } /** * `trazum rollup ` — several people's bills, one roll-up. * * The first command here that reads *documents* rather than logs. Everything * else assumes one operator with the files on disk; this assumes four people * who each already ran `trazum profile --json` where their traffic is, and one * of them wants the total. * * **A format and a merge, not a service.** There is no upload, no account and * no server: the documents arrive however the team already moves files, and * this reads them off the filesystem. That is the whole design — a tool whose * argument is that it reads your bill without uploading it cannot also be the * place everybody's bill is uploaded. * * A directory argument is expanded to the `.json` files directly inside it, so * a shared folder people drop a document into is a roll-up without anybody * writing a shell loop. * * The rendering leads with the total and then spends most of its lines on what * the merge could **not** do: the contributors' own gaps, the findings that do * not roll up, and the overlap between contributors that nothing here can see. * A roll-up is the document most likely to be pasted into a slide, and a total * with its caveats one screen away is a total that will be quoted alone. */ async function commandRollup(args: Args, t: CliMessages): Promise { if (args.positional.length === 0) throw new Error(t.rollup.noTargets()); /** Every document to merge, in the order the caller named them. */ const inputs: { name: string; text: string }[] = []; /** What a failed filesystem call was refusing, when it said. */ const codeOf = (error: unknown): string | undefined => typeof error === 'object' && error !== null ? (error as { code?: string }).code : undefined; for (const target of args.positional) { /** * Attempted, not checked first. * * The obvious shape is `stat` and then branch on `isDirectory()`, and it * is a check-then-act: between the answer and the read the path can become * something else, and CodeQL flagged exactly that on the pull request that * introduced this command. Reading the error code has no window between * the two operations, because there is only one operation — and it is the * same reasoning the gateway applies to a budget decision, which happens * before the upstream is opened rather than between two things. * * `ENOTDIR` is an answer — this is a file — and every other failure is a * failure. */ const listing = await readdir(target, { withFileTypes: true }).catch((error: unknown) => { const code = codeOf(error); if (code === 'ENOTDIR') return null; if (code === 'ENOENT') throw new Error(t.rollup.noSuchTarget(target)); throw error; }); if (listing !== null) { const found = listing .filter((child) => child.isFile() && child.name.endsWith('.json')) .map((child) => join(target, child.name)) .sort((a, b) => a.localeCompare(b)); // An empty directory is named rather than quietly contributing nothing: // a roll-up of a folder somebody spelled wrong would otherwise report a // total of zero and look like a team that spent nothing. if (found.length === 0) throw new Error(t.rollup.emptyDirectory(target)); for (const file of found) inputs.push({ name: file, text: await readFile(file, 'utf8') }); continue; } const text = await readFile(target, 'utf8').catch((error: unknown) => { // Gone between the two calls, which is the race the shape above avoids // deciding on: the read is what says so, and it says so by name. if (codeOf(error) === 'ENOENT') throw new Error(t.rollup.noSuchTarget(target)); throw error; }); inputs.push({ name: target, text }); } const document = rollUp(inputs); // The HTML door, on both output paths — a side file that vanished under // --json is the fault the profile's --csv-out already taught this file. const htmlOut = stringFlag(args, 'html-out'); if (htmlOut !== undefined) { await writeFile(htmlOut, renderRollupHtml(document, t), 'utf8'); console.error(c.dim(t.html.written(htmlOut))); } if (boolFlag(args, 'json')) { console.log(JSON.stringify(document, null, 2)); // A rejected contribution is a machine missing from the total, so it gates // — the same reason `conform` exits 1 on a problem. Every other caveat is // a property of merging summaries and would gate on every honest roll-up. if (document.rejected.length > 0) process.exitCode = 1; return; } const day = (ms: number): string => new Date(ms).toISOString().slice(0, 10); console.log(); console.log( c.bold( t.rollup.heading(document.contributors.length, formatUsd(document.total.totalUsd), document.total.calls), ), ); console.log( ` ${c.dim( document.span === null ? t.rollup.noSpan() : t.rollup.span(day(document.span.fromMs), day(document.span.toMs)), )}`, ); if (document.claimedSpan !== null) { console.log( ` ${c.dim( wrap( t.rollup.claimedSpan( day(document.claimedSpan.fromMs), day(document.claimedSpan.toMs - 1), document.claimedSpan.contributors, ), 72, ' ', ), )}`, ); } console.log(); console.log(sectionHeading(t.rollup.contributorsHeading())); for (const contributor of document.contributors) { console.log( ` ${t.rollup.contributor( contributor.name, formatUsd(contributor.totalUsd), contributor.calls, contributor.spanDays === null ? null : Math.round(contributor.spanDays), )}${contributor.via === null ? '' : ` ${c.dim(t.rollup.via(contributor.via))}`}`, ); /** * What this contributor asked for, before what it found. * * A span is not a period: a log whose latest record is the 5th may be a * log of a quiet week or a log that stopped being written on the 5th, and * only a claim tells those apart. Printed above the gaps because the * silence below is measured against it. */ if (contributor.claimed !== null) { const { sinceMs, untilMs } = contributor.claimed; if (sinceMs !== null && untilMs !== null) { // The window is half-open, so the last claimed day is the one before // `until` — printed as the reader would say it, not as the filter // stores it. console.log(` ${c.dim(t.rollup.claimedRow(day(sinceMs), day(untilMs - 1)))}`); } if (contributor.undatedExcluded !== null && contributor.undatedExcluded > 0) { console.log(` ${c.yellow(wrap(t.rollup.undated(contributor.undatedExcluded), 70, ' '))}`); } } // Every gap under the contributor that has it, never summed into one // figure: "one of your four machines is 90% unpriced" is the finding, and // an average is what hides it. for (const gap of contributor.gaps) { console.log(` ${c.yellow(wrap(gap.detail, 70, ' '))}`); } // The silent stretches by name, under the gap that counted them: "eight // days recorded nothing" is a number, and which eight is the finding. if (contributor.silence !== null && contributor.silence.runs.length > 0) { const runs = contributor.silence.runs .map((run) => (run.from === run.to ? run.from : `${run.from} to ${run.to}`)) .join(', '); console.log(` ${c.dim(wrap(t.rollup.silentRuns(runs), 68, ' '))}`); } } if (document.rejected.length > 0) { console.log(); console.log(sectionHeading(t.rollup.rejectedHeading())); for (const rejection of document.rejected) { // The roll-up it arrived through, when it came through one: a rejection // whose origin got lost is a machine nobody knows to go and fix. const line = rejection.via === null ? t.rollup.rejected(rejection.name, rejection.because) : t.rollup.rejectedVia(rejection.name, rejection.via, rejection.because); console.log(` ${c.red(wrap(line, 72, ' '))}`); } process.exitCode = 1; } if (document.identicalContributions.groups.length > 0) { console.log(); for (const group of document.identicalContributions.groups) { console.log(` ${c.yellow(wrap(t.rollup.identical(group.join(', ')), 72, ' '))}`); } console.log(` ${c.dim(t.rollup.identicalUsd(formatUsd(document.identicalContributions.usd)))}`); } if (document.repeatedContributors.length > 0) { console.log(); console.log(` ${c.yellow(wrap(t.rollup.repeated(document.repeatedContributors.join(', ')), 72, ' '))}`); } if (document.byLabel.length > 0) { console.log(); console.log(sectionHeading(t.rollup.byLabelHeading())); for (const row of document.byLabel.slice(0, 8)) { console.log(` ${t.rollup.labelRow(row.label, formatUsd(row.breakdown.totalUsd), row.breakdown.calls)}`); } } if (document.notMerged.length > 0) { console.log(); console.log(sectionHeading(t.rollup.notMergedHeading())); for (const finding of document.notMerged) { console.log(` ${wrap(t.rollup.notMerged(finding.finding, finding.because), 72, ' ')}`); if (finding.presentIn.length > 0) { console.log(` ${c.dim(t.rollup.presentIn(finding.presentIn.join(', ')))}`); } } } if (document.cannotSay.length > 0) { console.log(); console.log(sectionHeading(t.rollup.cannotSayHeading())); for (const caveat of document.cannotSay) { console.log(` ${c.dim(wrap(t.rollup.caveat(caveat), 72, ' '))}`); } } console.log(); } /** * `trazum pulse [--max-stale-hours ] [--json]` — did the things that are * supposed to run, run? * * `watch --once` is built for a scheduler: a cron entry is the whole daemon, * and its state file records each cycle precisely so a restart is honest about * the stretch it did not watch. That file is read by exactly one thing, and * that thing is the next cycle. * * **So nothing could tell you the watcher had stopped, because the thing that * would tell you was the thing that stopped.** A dead cron produces silence, * and a watcher with nothing to report produces silence too. This command is * the outside view: the age of the last watch cycle, the age of the last pull * into the store, and how far the stored measurements reach. * * **It is not a service and does not run itself.** Something has to notice, * and this product's answer is that the something is already in your CI: a * step that runs this with `--max-stale-hours` turns a dead cron into a red * build, on the schedule your CI already has, without Trazum holding anybody's * metrics. Where that answer runs out is written down in docs/ rather than * left to be discovered. * * Exits 1 only when a **run** that has happened before is past a **stated** * threshold. Never on a first run that never happened, and never on how far * the measurements reach — that is a provider reporting on its own schedule, * not a job that failed. */ /** * `trazum write` — the interview, on a terminal. * * Two ways in, and the same document out. Interactive, it asks the open slots * one at a time and takes an empty line as a decline. With `--answers`, it * reads a JSON object of slot ids and asks nothing, which is what a script or * a second run needs. * * **The prompt goes to stdout and everything else to stderr**, so * `trazum write --answers a.json > prompt.txt` is a file with a prompt in it * and not a file with an interview in it. */ async function commandWrite(args: Args, t: CliMessages): Promise { const n = (value: number): string => value.toLocaleString(t.numberLocale); const answersPath = stringFlag(args, 'answers'); let answers: Record = {}; if (answersPath !== undefined) { const parsed: unknown = JSON.parse(await readFile(answersPath, 'utf8')); if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) { throw new Error(t.write.answersNotAnObject(answersPath)); } for (const [id, value] of Object.entries(parsed as Record)) { if (slot(id) === undefined) { const nearest = nearestName(id, [...SLOT_IDS]); throw new Error(t.write.unknownSlot(id, nearest)); } if (value !== null && typeof value !== 'string') throw new Error(t.write.answerNotText(id)); answers[id] = value as string | null; } } else { /** * Interactive on a terminal, and a list of lines when it is not. * * An empty line is a **decline**, which is an answer: it closes the * follow-up a real one would have opened. Input running out is not a * decline — the remaining slots stay unasked and the refusal below names * them. * * The two paths exist because `readline` on a piped stream closes as soon * as the buffer drains, and a question asked after that never settles: the * event loop empties and the process leaves with status 0 and nothing * printed, which is an interview that stopped halfway and reported * success. A script piping answers is really handing over an ordered list, * so that is what this reads. */ let next: () => Promise; let close = () => {}; if (process.stdin.isTTY) { const rl = createInterface({ input: process.stdin, output: process.stderr }); next = async () => { try { return await rl.question('> '); } catch { return null; } }; close = () => rl.close(); } else { const piped: string[] = []; for await (const chunk of process.stdin) piped.push(String(chunk)); const lines = piped.join('').split('\n'); // A trailing newline is the end of the last answer, not an extra one. if (lines.length > 0 && lines[lines.length - 1] === '') lines.pop(); let at = 0; next = async () => (at < lines.length ? (lines[at++] as string) : null); } let ended = false; try { for (;;) { const state = interview(answers); if (state.done) break; const id = state.next as string; const copy = t.write.slots[id] as { question: string; unlocks: string }; process.stderr.write(`${copy.question}\n`); const typed = await next(); if (typed === null) { ended = true; break; } answers = { ...answers, [id]: typed.trim().length > 0 ? typed.trim() : null }; } } finally { close(); } if (!ended) console.error(t.write.done()); } const draft = assemble(answers, { callsPerMonth: numberFlag(args, 'calls', Number.NaN, t) || undefined, avgOutputTokens: numberFlag(args, 'avg-output', Number.NaN, t) || undefined, }); if (boolFlag(args, 'json')) { console.log(JSON.stringify(draft, null, 2)); if (draft.prompt === null) process.exitCode = 1; return; } if (draft.prompt === null) { // A refusal never arrives bare: each missing slot with what it unlocks. console.error(c.red(t.write.missing(draft.missing.length))); for (const id of draft.missing) { console.error(` ${c.bold(id)} — ${(t.write.slots[id] as { unlocks: string }).unlocks}`); } process.exitCode = 1; return; } /* `out`, not `o`. The parser normalises `-o` to `out` on the way in, so `stringFlag(args, 'o')` can never match anything: reading it meant `-o` was accepted, ignored, and the prompt went to stdout instead of the file somebody named. A flag that parses and does nothing is the defect this CLI already refuses one layer up with "Did you mean --max-growth?". */ const out = stringFlag(args, 'out'); if (out !== undefined) await writeFile(out, `${draft.prompt}\n`, 'utf8'); else console.log(draft.prompt); const m = draft.measured; if (m !== null) { console.error(''); console.error(t.write.tokens(n(m.cheap.tokens))); if (m.cheap.monthlyUsd !== null) console.error(t.write.monthly(formatUsd(m.cheap.monthlyUsd))); if (m.cheap.verdict !== 'cannot-tell') { console.error(t.write.budget(m.cheap.verdict, formatUsd(m.cheap.budgetUsd as number))); } else if (m.cheap.reason !== null) { console.error(t.write.noVerdict(m.cheap.reason)); } console.error( m.clean.rules.length === 0 ? t.write.clean() : t.write.notClean(m.clean.rules.map((rule) => rule.id).join(', '), n(m.clean.tokensRecoverable)), ); if (m.complete.declined.length > 0) console.error(t.write.declined(m.complete.declined.join(', '))); } } /** * `trazum from-claude-code ` — transcripts as a usage log. * * The conversion is `claudeCodeRecords` in core, and this command is only * the walk and the honesty: every transcript under an explicit path (never * a silent default reach into somebody's home), the records on stdout or * `-o`, and a stderr summary of what was collapsed — one API call arrives * as one line per content block, and counting lines would overbill by a * third on a real session — and what was passed over. No message text, no * `cwd`, no branch name crosses the conversion; the suite plants one of * each in a fixture and greps the whole output for them. */ /** * The workload name inside a Claude Code project folder. * * Claude Code names those folders by encoding the project's absolute path * with `/` replaced by `-`: `~/.claude/projects/-Users-mac-Trazum`. **That * encoding cannot be undone.** Both `/` and `-` map to `-`, so nothing in * the folder name says which dashes were separators, and 1.77.0 shipped a * decoder that guessed the last segment and was wrong on every project * whose own name contains a hyphen — real examples from the run that found * it: `-Users-mac-ai-job-search-ai-job-search` labelled `search`, and * `-Users-mac-Desktop-Pulse-Coffee-pulse-coffee` labelled `coffee`. Two * different projects, both renamed to a word that was never their name. * * So the folder name stands as it is, minus the leading separator. It is * longer than a tidy guess and it is the one thing here that is certainly * true: a reader can find the folder it names. Presenting a decoding as a * fact when the encoding cannot support one is the failure this product * exists to refuse, and it does not get an exception for being convenient. */ function projectLabelFor(file: string): string | undefined { const folder = dirname(resolvePath(file)).split('/').pop(); if (folder === undefined || folder === '') return undefined; const trimmed = folder.replace(/^-+/, ''); return trimmed !== '' ? trimmed : folder; } /** * The directory rules, read from a JSON file, or nothing when none was asked * for. * * **Every malformed entry is a refusal, never a skip.** A rules file with one * bad line that quietly labelled nothing would put a project's money on * another project's bill, silently, in the one direction nobody checks. The * message names the entry so it can be found. */ /** * The workspace mapping, read the way the cwd rules are read. * * Every refusal here is the same refusal: a rules file that half-parsed would * put one project's money on another's bill, silently, in the direction * nobody checks. `workspace` may be `null` — that is the default workspace, * which the report names by absence — but it may not be missing, because a * missing field is a typo and `null` is a decision. */ async function workspaceRulesFrom( file: string | undefined, t: CliMessages, ): Promise { if (file === undefined) return undefined; let parsed: unknown; try { parsed = JSON.parse(await readFile(file, 'utf8')); } catch { throw new Error(t.fromAnthropic.rulesUnreadable(file)); } if (!Array.isArray(parsed)) throw new Error(t.fromAnthropic.rulesUnreadable(file)); const rules: WorkspaceLabel[] = []; for (const [at, entry] of parsed.entries()) { const rule = entry as Record | null; if ( typeof rule !== 'object' || rule === null || !('workspace' in rule) || (rule.workspace !== null && (typeof rule.workspace !== 'string' || rule.workspace === '')) || typeof rule.label !== 'string' || rule.label === '' ) { throw new Error(t.fromAnthropic.ruleBad(file, at)); } rules.push({ workspace: rule.workspace as string | null, label: rule.label }); } if (rules.length === 0) throw new Error(t.fromAnthropic.rulesEmpty(file)); return rules; } /** * The project mapping `from-openai` labels by, under the same refusals as * the workspace one. `project` may not be `null` here: every OpenAI request * belongs to a project with an id, so there is no default named by absence * and a `null` would be a rule for nothing. */ async function projectRulesFrom( file: string | undefined, t: CliMessages, ): Promise { if (file === undefined) return undefined; let parsed: unknown; try { parsed = JSON.parse(await readFile(file, 'utf8')); } catch { throw new Error(t.fromOpenai.rulesUnreadable(file)); } if (!Array.isArray(parsed)) throw new Error(t.fromOpenai.rulesUnreadable(file)); const rules: ProjectLabel[] = []; for (const [at, entry] of parsed.entries()) { const rule = entry as Record | null; if ( typeof rule !== 'object' || rule === null || typeof rule.project !== 'string' || rule.project === '' || typeof rule.label !== 'string' || rule.label === '' ) { throw new Error(t.fromOpenai.ruleBad(file, at)); } rules.push({ project: rule.project, label: rule.label }); } if (rules.length === 0) throw new Error(t.fromOpenai.rulesEmpty(file)); return rules; } /** * The workspace mapping `from-openrouter` labels by. Same refusals as the * other two; `workspace` may not be `null`, because OpenRouter's report * names no workspace by absence — a `null` there only means the request did * not group by workspace. */ async function openrouterWorkspaceRulesFrom( file: string | undefined, t: CliMessages, ): Promise { if (file === undefined) return undefined; let parsed: unknown; try { parsed = JSON.parse(await readFile(file, 'utf8')); } catch { throw new Error(t.fromOpenrouter.rulesUnreadable(file)); } if (!Array.isArray(parsed)) throw new Error(t.fromOpenrouter.rulesUnreadable(file)); const rules: OpenrouterWorkspaceLabel[] = []; for (const [at, entry] of parsed.entries()) { const rule = entry as Record | null; if ( typeof rule !== 'object' || rule === null || typeof rule.workspace !== 'string' || rule.workspace === '' || typeof rule.label !== 'string' || rule.label === '' ) { throw new Error(t.fromOpenrouter.ruleBad(file, at)); } rules.push({ workspace: rule.workspace, label: rule.label }); } if (rules.length === 0) throw new Error(t.fromOpenrouter.rulesEmpty(file)); return rules; } async function cwdRulesFrom( file: string | undefined, t: CliMessages, ): Promise { if (file === undefined) return undefined; let parsed: unknown; try { parsed = JSON.parse(await readFile(file, 'utf8')); } catch { throw new Error(t.fromClaudeCode.cwdRulesUnreadable(file)); } if (!Array.isArray(parsed)) throw new Error(t.fromClaudeCode.cwdRulesUnreadable(file)); const rules: CwdLabel[] = []; for (const [at, entry] of parsed.entries()) { const rule = entry as Record | null; if ( typeof rule !== 'object' || rule === null || typeof rule.prefix !== 'string' || rule.prefix === '' || typeof rule.label !== 'string' || rule.label === '' ) { throw new Error(t.fromClaudeCode.cwdRuleBad(file, at)); } rules.push({ prefix: rule.prefix, label: rule.label }); } /* An empty list is refused rather than treated as "no rules": a caller who passed the flag meant to narrow something, and silently doing nothing is the shape `policy.ts` refuses one repository over. */ if (rules.length === 0) throw new Error(t.fromClaudeCode.cwdRulesEmpty(file)); return rules; } /** * `--state`: read the part of a transcript that is new since last time. * * A Claude Code transcript is append-only and can be enormous — the largest on * one real machine is 212 MB, and re-reading it to price the last thirty * seconds takes six and a half seconds. That is the whole cost of the status * line in `plugin/statusline`, and it is paid on every turn for a file whose * first two hundred megabytes cannot have changed. * * The state file ties three numbers together, and it is the third that makes * this exact rather than approximately right: * * - `offset`: where to start reading the transcript. * - `out`: how long the output file was when everything before `offset` had * been written. The tail after that is the last call as it looked last time, * and it is dropped and re-derived, because the converter's rule is that a * call arrives as several lines and the last one stands. * - `digest`: what the bytes just before `offset` were. A transcript that was * truncated, rotated or replaced would otherwise be resumed into the middle * of a different file, and the output would be a bill assembled from two * unrelated sessions. On a mismatch the whole thing is re-read, which is * slow and correct. */ interface TranscriptState { schemaVersion: 1; files: Record; } /** How much of the run-up to `offset` is fingerprinted. */ const STATE_DIGEST_BYTES = 4096; const digestOf = (bytes: Buffer): string => createHash('sha256').update(bytes).digest('hex'); const readState = async (path: string): Promise => { try { const parsed: unknown = JSON.parse(await readFile(path, 'utf8')); if ( typeof parsed === 'object' && parsed !== null && (parsed as TranscriptState).schemaVersion === 1 && typeof (parsed as TranscriptState).files === 'object' ) { return parsed as TranscriptState; } } catch { // A missing, unreadable or unrecognised state file is a cold start, not an // error: the answer it produces is the same one, computed the slow way. } return { schemaVersion: 1, files: {} }; }; /** * The byte offset of a line index within a chunk, counted in bytes rather than * characters. * * `String.prototype.split` counts UTF-16 code units and a transcript is UTF-8 * with prompts in it, so a character index used as a byte offset would land * mid-sequence on the first accented character and corrupt every resume after * it. The newlines are found in the Buffer. */ const byteOffsetOfLine = (chunk: Buffer, line: number): number => { if (line <= 0) return 0; let seen = 0; for (let i = 0; i < chunk.length; i += 1) { if (chunk[i] !== 0x0a) continue; seen += 1; if (seen === line) return i + 1; } return chunk.length; }; /** What a resumable read decided, so the caller can report it honestly. */ interface ResumedRead { text: string; /** Bytes skipped because a previous run had already settled them. */ skipped: number; /** Where the output must be truncated to before appending. */ truncateOutTo: number; /** Records the previous run had settled, and this one must not re-emit. */ chunkStart: number; } const resumableRead = async ( file: string, state: TranscriptState, ): Promise<{ chunk: Buffer; read: ResumedRead }> => { const key = resolvePath(file); const entry = state.files[key]; const handle = await open(key, 'r'); try { const { size } = await handle.stat(); let start = 0; let truncateOutTo = 0; if (entry !== undefined && entry.offset > 0 && entry.offset <= size) { const runUp = Math.min(STATE_DIGEST_BYTES, entry.offset); const before = Buffer.alloc(runUp); await handle.read(before, 0, runUp, entry.offset - runUp); if (digestOf(before) === entry.digest) { start = entry.offset; truncateOutTo = entry.out; } } const chunk = Buffer.alloc(size - start); if (chunk.length > 0) await handle.read(chunk, 0, chunk.length, start); return { chunk, read: { text: chunk.toString('utf8'), skipped: start, truncateOutTo, chunkStart: start }, }; } finally { await handle.close(); } }; async function commandFromClaudeCode(args: Args, t: CliMessages): Promise { const target = args.positional[0]; if (target === undefined) throw new Error(t.fromClaudeCode.noPath()); let info; try { info = await stat(target); } catch { throw new Error(t.fromClaudeCode.notFound(target)); } const files: string[] = []; if (info.isDirectory()) { const entries = await readdir(target, { recursive: true, withFileTypes: true }); for (const entry of entries) { if (entry.isFile() && entry.name.endsWith('.jsonl')) { files.push(join(entry.parentPath, entry.name)); } } files.sort(); if (files.length === 0) throw new Error(t.fromClaudeCode.noTranscripts(target)); } else { files.push(target); } const label = stringFlag(args, 'label'); /** * A folder of projects is a folder of workloads — the 1.77 default. * * The flag has existed since the folder walk did, and nobody found it: a * real forty-day run produced `label` on 0 of 10,393 records, and the * report could then only describe a mixture, which is exactly what it * said. The web app's folder drop has labelled by project since 1.70, so * the two surfaces disagreed about the same gesture. * * Default-on for a directory, `--no-label-from-project` to decline. A * single file is untouched: one file is one workload only if the caller * says so. */ const labelFromProject = boolFlag(args, 'label-from-project', info.isDirectory()); /** * `--label-by-cwd`: the one question a folder name cannot answer. * * `--label-from-project` labels by the transcript's own folder, which is * right when one folder is one project and useless when it is not. Two * repositories worked on in a single session share a transcript, share a * folder, and the transcript has no field saying which was which. It has a * `cwd`, per line. * * **A JSON file rather than a repeated flag or a delimited string.** The * values are absolute paths, and every delimiter worth choosing — comma, * colon, semicolon, equals — is a character a directory is allowed to * contain. A file has no such problem, and the rules are the kind of thing * written once and kept beside the config. * * Read here and never emitted: `claude-code.ts` states that contract and a * test plants a secret in `cwd` and greps the output for it. */ const cwdRules = await cwdRulesFrom(stringFlag(args, 'label-by-cwd'), t); /** * `--state` is for one transcript, deliberately. * * The state ties a transcript offset to a length of the output file, and * with several transcripts appending to one output there is no single length * that means "everything settled": the second file's records sit after the * first file's unsettled tail. A directory is re-read in full, which is what * it was doing before and is the right cost for a walk that is not a status * line refreshing every turn. */ const statePath = stringFlag(args, 'state'); if (statePath !== undefined && info.isDirectory()) throw new Error(t.fromClaudeCode.stateNeedsFile()); if (statePath !== undefined && stringFlag(args, 'out') === undefined && stringFlag(args, 'o') === undefined) { throw new Error(t.fromClaudeCode.stateNeedsOut()); } const state = statePath !== undefined ? await readState(statePath) : undefined; let resumed: ResumedRead | undefined; let chunk: Buffer | undefined; let resumeLine = 0; let settledRecords = 0; let settledOutBytes = 0; const lines: string[] = []; let records = 0; let collapsed = 0; let noRequestId = 0; let otherLines = 0; let unparseable = 0; let withoutUsage = 0; let streamed = 0; let disagreements = 0; let synthetic = 0; for (const file of files) { let text: string; if (state !== undefined) { const read = await resumableRead(file, state); chunk = read.chunk; resumed = read.read; text = read.read.text; } else { text = await readFile(file, 'utf8'); } const projectLabel = labelFromProject ? projectLabelFor(file) : undefined; const conversion = claudeCodeRecords(text, { ...(label !== undefined ? { label } : projectLabel !== undefined && projectLabel !== '' ? { label: projectLabel } : {}), /* The directory rules sit on top: whatever the flat label would have been becomes the fallback for work outside every rule. */ ...(cwdRules !== undefined ? { labelByCwd: cwdRules } : {}), }); for (const record of conversion.records) lines.push(JSON.stringify(record)); resumeLine = conversion.resume.line; settledRecords = conversion.resume.records; records += conversion.records.length; collapsed += conversion.collapsed; noRequestId += conversion.noRequestId; otherLines += conversion.otherLines; unparseable += conversion.unparseable; withoutUsage += conversion.assistantWithoutUsage; streamed += conversion.streamed; disagreements += conversion.disagreements; synthetic += conversion.synthetic; } const out = stringFlag(args, 'out') ?? stringFlag(args, 'o'); if (out !== undefined) { if (statePath !== undefined) { /** * Append rather than rewrite, and drop the tail first. * * The tail is the last call as the previous run recorded it, and this * run has just re-derived it from its own first line. Truncating is what * makes the two runs add up to exactly one reading of the transcript * rather than one and a bit. */ const handle = await open(out, resumed!.truncateOutTo > 0 ? 'r+' : 'w'); try { await handle.truncate(resumed!.truncateOutTo); const body = lines.join('\n') + (lines.length > 0 ? '\n' : ''); if (body !== '') await handle.write(body, resumed!.truncateOutTo, 'utf8'); settledOutBytes = resumed!.truncateOutTo + lines .slice(0, settledRecords) .reduce((sum, line) => sum + Buffer.byteLength(line + '\n', 'utf8'), 0); } finally { await handle.close(); } } else { await writeFile(out, lines.join('\n') + (lines.length > 0 ? '\n' : ''), 'utf8'); } console.error(t.fromClaudeCode.written(out)); } else { for (const line of lines) console.log(line); } if (statePath !== undefined) { const key = resolvePath(files[0]!); const nextOffset = resumed!.chunkStart + byteOffsetOfLine(chunk!, resumeLine); const runUp = Math.min(STATE_DIGEST_BYTES, nextOffset); const before = Buffer.alloc(runUp); if (runUp > 0) { const handle = await open(key, 'r'); try { await handle.read(before, 0, runUp, nextOffset - runUp); } finally { await handle.close(); } } const next: TranscriptState = { schemaVersion: 1, files: { [key]: { offset: nextOffset, out: settledOutBytes, digest: digestOf(before) } }, }; await writeFile(statePath, JSON.stringify(next, null, 2) + '\n', 'utf8'); console.error(t.fromClaudeCode.resumed(resumed!.skipped, nextOffset)); } console.error(t.fromClaudeCode.summary(files.length, records)); if (collapsed > 0) console.error(t.fromClaudeCode.collapsed(collapsed)); if (streamed > 0) console.error(t.fromClaudeCode.streamed(streamed)); if (disagreements > 0) console.error(t.fromClaudeCode.disagreements(disagreements)); if (noRequestId > 0) console.error(t.fromClaudeCode.noRequestId(noRequestId)); if (synthetic > 0) console.error(t.fromClaudeCode.synthetic(synthetic)); if (labelFromProject && label === undefined && cwdRules === undefined) { console.error(t.fromClaudeCode.labelled()); } if (cwdRules !== undefined) console.error(t.fromClaudeCode.labelledByCwd(cwdRules.length)); console.error(t.fromClaudeCode.skipped(otherLines, unparseable, withoutUsage)); } /** * `trazum receipt ` — the bill's counts, with the provenance * of every figure attached. * * A profile answers a question on the terminal that ran it. The same figures * filed against an invoice, or read next month by somebody who was not there, * stop answering it: a dollar total with no provenance cannot tell a repricing * from a team whose spend moved. This writes the shape that keeps answering. * * **It sends nothing anywhere.** There is no endpoint, no key, no retry and no * queue: the document goes to a path you name or to standard output, and what * happens to it next is not this command's business. A command in this package * that phoned home would break the roadmap's first rule in the same release * that claims to be protecting it. * * **The document is undated unless you ask for a stamp**, and that is a * deliberate default rather than an omission. This product's first promise is * the same answer every time; a command that wrote the current clock into its * output would produce different bytes on every run, so it could not be * committed, diffed in a pull request, or compared against yesterday's. The * period the figures actually cover is already in the document, read from the * log's own clock. `--stamp` adds the emission time for whoever wants it, and * an undated receipt is valid: `receiptFrom` says so, and says why. * * The offline guard found this. The command stamped by default, two runs * disagreed, and the test that exists to catch a hidden network call caught a * hidden clock instead. * * The redaction property is not enforced here. It is a property of * `receiptFrom`, which takes a `UsageProfileReport` -- a shape with no field * that can hold prompt text -- and it is held by `receipt-redaction.test.js` * planting the 4 things that must never appear. This function only chooses the * log and the destination. */ /** * What Trazum computed, beside what the provider billed. * * Two documents in, one comparison out. The receipt is a figure this product * derived from token counts and a rate table; the cost report is what the * provider charged. Neither corrects the other and neither is merged into the * other -- `anthropic-cost.ts` opens by arguing why -- and the remainder is * printed as its own number rather than folded into an explanation. */ async function commandReconcile(args: Args, t: CliMessages): Promise { const file = args.positional[0]; if (file === undefined) throw new Error(t.reconcile.noReceipt()); const against = stringFlag(args, 'against'); if (against === undefined) throw new Error(t.reconcile.noReport()); let receipt: { total?: { usd?: unknown }; span?: { fromMs?: unknown; toMs?: unknown } }; try { receipt = JSON.parse(await readFile(file, 'utf8')); } catch { throw new Error(t.reconcile.receiptUnreadable(file)); } const usd = receipt?.total?.usd; const fromMs = receipt?.span?.fromMs; const toMs = receipt?.span?.toMs; if (typeof usd !== 'number' || typeof fromMs !== 'number' || typeof toMs !== 'number') { throw new Error(t.reconcile.notAReceipt(file)); } let billedText: string; try { billedText = await readFile(against, 'utf8'); } catch { throw new Error(t.reconcile.reportUnreadable(against)); } /* Which provider's report this is, told from the text itself: OpenAI's buckets carry a numeric `start_time`, Anthropic's a `starting_at`. */ const openai = looksLikeOpenaiCost(billedText) ? openaiCostReport(billedText) : null; const billed = openai ?? anthropicCostReport(billedText); if (billed.unparseable) throw new Error(t.reconcile.notAReport(against)); const answer = reconcile({ usd, fromMs, toMs }, billed); const json = JSON.stringify(answer, null, 2); const out = stringFlag(args, 'out') ?? stringFlag(args, 'o'); if (out !== undefined) { await writeFile(out, json + '\n', 'utf8'); console.error(t.reconcile.written(out)); } else { console.log(json); } /* Everything below on stderr, so the document redirects cleanly. */ if (answer.refusal !== null) { if (answer.refusal.reason === 'other-currency') { console.error(t.reconcile.otherCurrency(answer.refusal.currencies.join(', '))); } else if (answer.refusal.reason === 'no-billed-window') { console.error(t.reconcile.noBilledWindow()); } else { console.error( t.reconcile.windowNotCovered( new Date(answer.refusal.computed.fromMs).toISOString(), new Date(answer.refusal.computed.toMs).toISOString(), new Date(answer.refusal.billed.fromMs).toISOString(), new Date(answer.refusal.billed.toMs).toISOString(), ), ); } return; } console.error(t.reconcile.summary(answer.computedUsd, answer.billedUsd, answer.differenceUsd)); if (answer.attributable) { if (answer.notTokensUsd !== 0) console.error(t.reconcile.notTokens(answer.notTokensUsd)); if (answer.batchUsd !== 0) console.error(t.reconcile.batch(answer.batchUsd)); console.error(t.reconcile.remainder(answer.remainderUsd)); if (!answer.batchSeparable) console.error(t.reconcile.batchNotSeparable()); if (openai !== null && openai.unknownUnitUsd !== 0) { console.error(t.reconcile.unknownUnit(openai.unknownUnitUsd)); } } else { console.error(openai === null ? t.reconcile.notAttributable() : t.reconcile.notAttributableByLineItem()); } if (billed.truncated) console.error(t.reconcile.truncated()); if (billed.unreadableAmount > 0) console.error(t.reconcile.unreadableAmount(billed.unreadableAmount)); } async function commandReceipt( args: Args, pricing: PricingCatalogue, t: CliMessages, ): Promise { const file = args.positional[0]; if (file === undefined) throw new Error(t.receipt.noLog()); const text = await readUsageLog(file, t); const report = profileUsage(text, { catalogue: pricing }); const document = receiptFrom( report, pricing, boolFlag(args, 'stamp') ? { emittedAt: new Date() } : {}, ); const json = JSON.stringify(document, null, 2); const out = stringFlag(args, 'out') ?? stringFlag(args, 'o'); if (out !== undefined) { await writeFile(out, json + '\n', 'utf8'); console.error(t.receipt.written(out, document.lines.length)); } else { console.log(json); } printReceiptSummary(document, t); } /* * Everything here goes to stderr, so `trazum receipt log.jsonl > receipt.json` * writes a document and not a document with a summary stapled to the front. * The gaps are read off the document rather than recomputed, so what the * reader is told and what the file carries cannot drift apart. Shared with * `bill`, which ends on the same receipt. */ function printReceiptSummary(document: ReceiptDocument, t: CliMessages): void { if (document.lines.length === 0) { console.error(t.receipt.nothingToBill()); } else { console.error(t.receipt.summary(document.lines.length, formatUsd(document.total.usd))); } for (const gap of document.gaps) { if (gap.kind === 'unpriced') console.error(t.receipt.unpriced(gap.models.length, gap.calls)); if (gap.kind === 'unread-lines') console.error(t.receipt.unread(gap.count)); if (gap.kind === 'no-clock') console.error(t.receipt.noClock()); } } /** The shapes `bill` can tell apart from a file's own text. */ type UsageSource = | 'claude-code' | 'otel' | 'litellm' | 'helicone' | 'langsmith' | 'anthropic-usage' | 'openai-usage' | 'openrouter' | 'usage-log'; /** * Which shapes claim a text. Every sniffer is asked, not the first that says * yes: a file two shapes claim is a file this must not convert, because * whichever it picked would be a guess wearing a result's clothes. */ function usageSourcesOf(text: string): UsageSource[] { const claims: UsageSource[] = []; if (looksLikeClaudeCodeTranscript(text)) claims.push('claude-code'); if (looksLikeOtel(text)) claims.push('otel'); if (looksLikeAnthropicUsage(text)) claims.push('anthropic-usage'); if (looksLikeOpenaiUsage(text)) claims.push('openai-usage'); if (looksLikeOpenrouterActivity(text)) claims.push('openrouter'); if (looksLikeHelicone(text)) claims.push('helicone'); if (looksLikeLangsmith(text)) claims.push('langsmith'); if (looksLikeLiteLlm(text)) claims.push('litellm'); if (claims.length > 0) return claims; /* A plain usage log has no signature but its own lines: if the first non-blank one parses as a usage line, that is what it is. */ const first = text.split('\n').find((line) => line.trim() !== ''); if (first !== undefined && parseUsageLine(first) !== null) claims.push('usage-log'); return claims; } /** * `trazum bill `: one door, from `docs/plan-2.4.md`. * * Forty-nine commands behind two hundred downloads a month said the product * was deep and nobody arrived, and one reason was that a person with a log * had to know what their log was called before Trazum would read it. This * reads anything the converters read, tells each file's shape from its own * text, converts in memory, prices, and ends on the same receipt `receipt` * writes. * * It is the dedicated commands composed, not a looser version of them: each * file's rows go through the same converter `from-` uses, so every * refusal those make is made here. What differs is how the refusals are * told. This names each file, its shape, the records it became and how many * rows were left out, and points at the dedicated command for the reasons, * rather than repeating eight commands' worth of explanation on one screen. * A file no shape claims is named and not guessed; a file two shapes claim is * named as ambiguous and not converted; a provider's cost report is named as * a bill rather than usage, and pointed at `reconcile`. */ async function commandBill(args: Args, pricing: PricingCatalogue, t: CliMessages): Promise { const target = args.positional[0]; if (target === undefined) throw new Error(t.bill.noPath()); let info; try { info = await stat(target); } catch { throw new Error(t.bill.notFound(target)); } const files: string[] = []; if (info.isDirectory()) { const entries = await readdir(target, { recursive: true, withFileTypes: true }); for (const entry of entries) { if (entry.isFile() && /\.(json|jsonl|ndjson)(\.gz)?$/i.test(entry.name)) { files.push(join(entry.parentPath, entry.name)); } } files.sort(); if (files.length === 0) throw new Error(t.bill.noFiles(target)); } else { files.push(target); } const label = stringFlag(args, 'label'); const withLabel = label === undefined ? {} : { label }; const lines: string[] = []; let sources = 0; for (const file of files) { const text = await readUsageLog(file, t); const claims = usageSourcesOf(text); if (claims.length === 0) { if (looksLikeAnthropicCost(text) || looksLikeOpenaiCost(text)) { console.error(t.bill.costReport(file)); } else { console.error(t.bill.unknown(file)); } continue; } if (claims.length > 1) { console.error(t.bill.ambiguous(file, claims.join(', '))); continue; } const [shape] = claims; if (shape === undefined) continue; let records: unknown[] = []; let leftOut = 0; switch (shape) { case 'claude-code': { const c = claudeCodeRecords(text, withLabel); records = c.records; leftOut = c.assistantWithoutUsage + c.unparseable; break; } case 'otel': { const c = otelRecords(text); records = c.records; leftOut = c.otherSpans + c.unparseable; break; } case 'litellm': { const c = litellmRecords(text); records = c.records; leftOut = c.unnamedModel + c.unparseable; break; } case 'helicone': { const c = heliconeRecords(text); records = c.records; leftOut = c.unnamedModel + c.unparseable; break; } case 'langsmith': { const c = langsmithRecords(text); records = c.records; leftOut = c.notModelCalls + c.unnamedModel + c.unparseable; break; } case 'anthropic-usage': { const c = anthropicUsageRecords(text, withLabel); records = c.records; leftOut = c.unnamedModel + c.nonStandardTier; break; } case 'openai-usage': { const c = openaiUsageRecords(text, withLabel); records = c.records; leftOut = c.unnamedModel + c.batch + c.nonDefaultTier + c.unsplitRows; break; } case 'openrouter': { const c = openrouterActivityRecords(text, withLabel); records = c.records; leftOut = c.unnamedModel + c.undatedRows; break; } case 'usage-log': { /* Already the shape every door reads: passed through line by line, and what does not parse is the receipt's own unread-lines gap. */ for (const line of text.split('\n')) if (line.trim() !== '') lines.push(line); break; } } for (const record of records) lines.push(JSON.stringify(record)); sources += 1; console.error(t.bill.file(file, shape, shape === 'usage-log' ? null : records.length, leftOut)); } if (sources === 0) throw new Error(t.bill.nothingRead()); const report = profileUsage(lines.join('\n'), { catalogue: pricing }); const document = receiptFrom(report, pricing, boolFlag(args, 'stamp') ? { emittedAt: new Date() } : {}); const json = JSON.stringify(document, null, 2); const out = stringFlag(args, 'out') ?? stringFlag(args, 'o'); if (out !== undefined) { await writeFile(out, json + '\n', 'utf8'); console.error(t.bill.written(out)); } else { console.log(json); } console.error(t.bill.sources(sources, files.length)); printReceiptSummary(document, t); /* A slug with a slash in it is how OpenRouter names a model, and the bundled catalogue does not carry those: the live overlay does. Said only when the receipt's own unpriced gap holds one, so the hint is derived from what was refused rather than from which shape the file happened to be. */ const slugged = document.gaps.flatMap((gap) => gap.kind === 'unpriced' ? gap.models.filter((model) => model.includes('/')) : [], ); if (slugged.length > 0 && !boolFlag(args, 'pricing-live')) { console.error(t.bill.pricingLiveHint(slugged.length)); } } /** * `trazum from-otel ` — OpenTelemetry GenAI spans as a usage log. * * The 1.71 move: the same pure-converter pattern generalised to the standard * the ecosystem is converging on, so Trazum prices whatever telemetry a team * already emits. `otelRecords` in core does the reading; this is the walk and * the honesty — how many spans were LLM calls, how many were skipped as * non-LLM, how many carried no cache data (the OTel norm, since it has not * standardised the TTL split). Prompt content, trace ids and every other span * attribute stay in the span; a fixture greps the whole output to prove it. */ /** * `trazum switch` — the forty-first command, from the 1.74 plan: the decision * every what-if serves, priced. Rests on `switchAnalysis`, which rests on * `repriceProfile`, so over-context slices and cache minimums keep their * honesty. Ends, always, on the refusal: quality is `trazum route`'s verdict. */ async function commandSwitch(args: Args, pricing: PricingCatalogue, t: CliMessages): Promise { const file = args.positional[0]; if (file === undefined) throw new Error(t.switchCmd.noLog()); const target = stringFlag(args, 'to'); if (target === undefined) throw new Error(t.switchCmd.noTarget()); // Optional numbers: absent stays absent — a defaulted migration cost or // case count would be an invented one. const optional = (name: string): number | undefined => { const raw = stringFlag(args, name); if (raw === undefined) return undefined; const value = Number(raw); if (!Number.isFinite(value) || value < 0) throw new Error(t.errors.mustBeNonNegative(name, raw)); return value; }; const migrationUsd = optional('migration-usd'); const cases = optional('cases'); const text = await readUsageLog(file, t); const report = profileUsage(text, { catalogue: pricing }); const analysis = switchAnalysis(report, target, { catalogue: pricing, ...(migrationUsd !== undefined ? { migrationUsd } : {}), ...(cases !== undefined ? { evalCases: cases } : {}), }); if (analysis === null) throw new Error(t.switchCmd.unknownModel(target)); const { reprice, savingUsd, measuredDays, breakEven, evalCost } = analysis; console.log(); console.log(sectionHeading(t.switchCmd.heading(reprice.target.displayName))); if (reprice.slices.length === 0) { console.log(` ${wrap(t.switchCmd.nothingMovable(), 74, ' ')}`); } else { const line = savingUsd >= 0 ? t.switchCmd.saves(formatUsd(reprice.currentUsd), formatUsd(reprice.targetUsd), formatUsd(savingUsd)) : t.switchCmd.costs(formatUsd(reprice.currentUsd), formatUsd(reprice.targetUsd), formatUsd(-savingUsd)); // The same arrow the levers use. `switch` shipped with an ASCII `->` // in 1.74 and the 1.75 arc gave every report one visual vocabulary; // two glyphs for one meaning is the reader doing translation work. console.log(` ${savingUsd >= 0 ? c.green('\u2192') : c.red('\u2192')} ${wrap(line, 72, ' ')}`); const movableCalls = reprice.slices.reduce((sum, slice) => sum + slice.calls, 0); console.log(` ${wrap(t.switchCmd.movable(movableCalls, reprice.slices.length), 74, ' ')}`); } if (reprice.overContext.length > 0) { const usd = reprice.overContext.reduce((sum, slice) => sum + slice.currentUsd, 0); console.log(` ${c.yellow('!')} ${wrap(t.switchCmd.overContext(reprice.overContext.length, formatUsd(usd)), 72, ' ')}`); } if (reprice.alreadyOnTarget.calls > 0) { console.log(` ${wrap(t.switchCmd.alreadyOnTarget(reprice.alreadyOnTarget.calls, formatUsd(reprice.alreadyOnTarget.usd)), 74, ' ')}`); } console.log( ` ${wrap(measuredDays !== null ? t.switchCmd.window(measuredDays) : t.switchCmd.noWindow(), 74, ' ')}`, ); if (breakEven !== null) { const sentence = 'days' in breakEven ? t.switchCmd.breakEvenDays(formatUsd(breakEven.migrationUsd), Math.ceil(breakEven.days), measuredDays ?? 0) : breakEven.refused === 'no-saving' ? t.switchCmd.breakEvenNoSaving(formatUsd(breakEven.migrationUsd)) : t.switchCmd.breakEvenNoClock(formatUsd(breakEven.migrationUsd)); console.log(` ${wrap(sentence, 74, ' ')}`); } if (evalCost !== null) { console.log(` ${wrap(t.switchCmd.evalCost(evalCost.cases, formatUsd(evalCost.totalUsd)), 74, ' ')}`); } else if (reprice.slices.length > 0) { console.log(` ${c.dim(wrap(t.switchCmd.evalCostHint(), 74, ' '))}`); } console.log(); console.log( ` ${wrap(t.switchCmd.quality(`trazum route ${file} --prompt-file --cases --yes`), 74, ' ')}`, ); } /** * `trazum ownrate` — the forty-second: a self-hosted model's $/MTok, derived * from the operator's own declared numbers, with the overlay snippet ready to * paste. Division and a label, nothing modelled. */ function commandOwnrate(args: Args, t: CliMessages): void { const optional = (name: string): number | undefined => { const raw = stringFlag(args, name); if (raw === undefined) return undefined; const value = Number(raw); if (!Number.isFinite(value)) throw new Error(t.ownrate.invalid(name)); return value; }; const gpuUsdPerHour = optional('gpu-usd-hour'); const tokensPerSecond = optional('tokens-per-second'); const utilization = optional('utilization'); if (gpuUsdPerHour === undefined || tokensPerSecond === undefined) throw new Error(t.ownrate.missing()); if (!(gpuUsdPerHour > 0)) throw new Error(t.ownrate.invalid('gpu-usd-hour')); if (!(tokensPerSecond > 0)) throw new Error(t.ownrate.invalid('tokens-per-second')); if (utilization !== undefined && !(utilization > 0 && utilization <= 1)) throw new Error(t.ownrate.invalid('utilization')); const { usdPerMTok } = ownRate({ gpuUsdPerHour, tokensPerSecond, ...(utilization !== undefined ? { utilization } : {}), }); const pct = Math.round((utilization ?? 1) * 100); console.log(); console.log( ` ${wrap(t.ownrate.result(formatUsd(usdPerMTok), tokensPerSecond, formatUsd(gpuUsdPerHour), pct), 74, ' ')}`, ); console.log(` ${c.dim(wrap(t.ownrate.declared(), 74, ' '))}`); console.log(); console.log(` ${t.ownrate.snippetHeading()}`); // Complete on purpose: the overlay parser refuses a new model with fields // missing, and a snippet that does not paste is worse than none. The // honest values for what a self-hosted model has not measured are the // catalogue's own unknowns, never a guess. const snippet = { lastReviewed: new Date().toISOString().slice(0, 10), models: { 'my-self-hosted-model': { displayName: 'My self-hosted model', inputPerMTok: Number(usdPerMTok.toFixed(4)), outputPerMTok: Number(usdPerMTok.toFixed(4)), contextWindow: 32768, cacheMinTokens: null, tier: 'unknown', capability: 'unknown', }, }, }; console.log(JSON.stringify(snippet, null, 2)); } async function commandFromOtel(args: Args, t: CliMessages): Promise { const target = args.positional[0]; if (target === undefined) throw new Error(t.fromOtel.noPath()); let info; try { info = await stat(target); } catch { throw new Error(t.fromOtel.notFound(target)); } const files: string[] = []; if (info.isDirectory()) { const entries = await readdir(target, { recursive: true, withFileTypes: true }); for (const entry of entries) { if (entry.isFile() && /\.(json|jsonl|ndjson)$/i.test(entry.name)) { files.push(join(entry.parentPath, entry.name)); } } files.sort(); if (files.length === 0) throw new Error(t.fromOtel.noExports(target)); } else { files.push(target); } const lines: string[] = []; let llmSpans = 0; let otherSpans = 0; let noCacheData = 0; let unparseable = 0; for (const file of files) { const conversion = otelRecords(await readFile(file, 'utf8')); for (const record of conversion.records) lines.push(JSON.stringify(record)); llmSpans += conversion.llmSpans; otherSpans += conversion.otherSpans; noCacheData += conversion.noCacheData; unparseable += conversion.unparseable; } const out = stringFlag(args, 'out') ?? stringFlag(args, 'o'); if (out !== undefined) { await writeFile(out, lines.join('\n') + (lines.length > 0 ? '\n' : ''), 'utf8'); console.error(t.fromOtel.written(out)); } else { for (const line of lines) console.log(line); } console.error(t.fromOtel.summary(files.length, llmSpans)); if (otherSpans > 0) console.error(t.fromOtel.skipped(otherSpans)); if (noCacheData > 0) console.error(t.fromOtel.noCache(noCacheData)); if (unparseable > 0) console.error(t.fromOtel.unparseable(unparseable)); } /** * `trazum from-litellm ` — a LiteLLM spend log as a usage log. * * The same pure-converter pattern again, pointed at the gateway a great many * teams already put in front of every provider: `LiteLLM_SpendLogs` is the * export most likely to already exist on somebody's disk. `litellmRecords` in * core does the reading; this is the walk and the honesty. * * Three of the four counts it prints exist because the alternative is a * flattering silence. Rows naming no model are not priced by the route they * took, rows with no tokens are logged calls nobody can price, and a * `cache_hit` flag is not a token split — so the cache verdicts read "cannot * tell" rather than a fabricated one, exactly as `from-otel` does. * * The fourth is the one that matters most: LiteLLM prices the same calls with * its own table, and that figure is printed beside Trazum's and never merged * into it. Two price tables summed into one total is how a report becomes * quietly wrong. * * The row carries `messages`, `response`, `api_key`, `requester_ip_address` * and `end_user`. None of it is read; a fixture plants a marker in each and * greps the whole output. */ async function commandFromLiteLlm(args: Args, t: CliMessages): Promise { const target = args.positional[0]; if (target === undefined) throw new Error(t.fromLiteLlm.noPath()); let info; try { info = await stat(target); } catch { throw new Error(t.fromLiteLlm.notFound(target)); } const files: string[] = []; if (info.isDirectory()) { const entries = await readdir(target, { recursive: true, withFileTypes: true }); for (const entry of entries) { if (entry.isFile() && /\.(json|jsonl|ndjson)$/i.test(entry.name)) { files.push(join(entry.parentPath, entry.name)); } } files.sort(); if (files.length === 0) throw new Error(t.fromLiteLlm.noExports(target)); } else { files.push(target); } const lines: string[] = []; let rows = 0; let unnamedModel = 0; let noTokens = 0; let cacheFlagged = 0; let unparseable = 0; let reportedSpend = 0; let sawSpend = false; for (const file of files) { const conversion = litellmRecords(await readFile(file, 'utf8')); for (const record of conversion.records) lines.push(JSON.stringify(record)); rows += conversion.rows; unnamedModel += conversion.unnamedModel; noTokens += conversion.noTokens; cacheFlagged += conversion.cacheFlagged; unparseable += conversion.unparseable; if (conversion.reportedSpendUsd !== null) { reportedSpend += conversion.reportedSpendUsd; sawSpend = true; } } const out = stringFlag(args, 'out') ?? stringFlag(args, 'o'); if (out !== undefined) { await writeFile(out, lines.join('\n') + (lines.length > 0 ? '\n' : ''), 'utf8'); console.error(t.fromLiteLlm.written(out)); } else { for (const line of lines) console.log(line); } console.error(t.fromLiteLlm.summary(files.length, rows)); if (unnamedModel > 0) console.error(t.fromLiteLlm.unnamedModel(unnamedModel)); if (noTokens > 0) console.error(t.fromLiteLlm.noTokens(noTokens)); if (cacheFlagged > 0) console.error(t.fromLiteLlm.cacheFlagged(cacheFlagged)); if (sawSpend) console.error(t.fromLiteLlm.reportedSpend(formatUsd(reportedSpend))); if (unparseable > 0) console.error(t.fromLiteLlm.unparseable(unparseable)); } /** * `trazum from-langsmith ` — a LangSmith run export as a usage log. * * The fifth converter, and the one where the unit is wrong before anything * else can be right: LangSmith records a **run**, and a trace is a tree of * them. The chain that wrapped a model call carries the same tokens as the * call, so summing the export bills them once per level. Only `run_type: llm` * is a call, and every run that is not one is counted out loud — skipping two * thirds of a file silently would look exactly like reading it. * * The model is refused rather than inferred. There is no model column; the * name lives in the metadata, and the obvious substitute is the run's own * `name`, which LangChain sets to the client class. Pricing a call by * `ChatAnthropic` would attribute a figure to something it does not describe. * * LangSmith's own cost is reported on its own line and never merged into * anything Trazum computes, the way `from-litellm` keeps the gateway's * arithmetic apart. Two price tables summed into one total is how a report * becomes quietly wrong. */ async function commandFromLangsmith(args: Args, t: CliMessages): Promise { const target = args.positional[0]; if (target === undefined) throw new Error(t.fromLangsmith.noPath()); let info; try { info = await stat(target); } catch { throw new Error(t.fromLangsmith.notFound(target)); } const files: string[] = []; if (info.isDirectory()) { const entries = await readdir(target, { recursive: true, withFileTypes: true }); for (const entry of entries) { if (entry.isFile() && /\.(json|jsonl|ndjson)$/i.test(entry.name)) { files.push(join(entry.parentPath, entry.name)); } } files.sort(); if (files.length === 0) throw new Error(t.fromLangsmith.noExports(target)); } else { files.push(target); } const lines: string[] = []; let rows = 0; let notModelCalls = 0; let unnamedModel = 0; let noTokens = 0; let unparseable = 0; let reportedCostUsd: number | null = null; for (const file of files) { const conversion = langsmithRecords(await readFile(file, 'utf8')); for (const record of conversion.records) lines.push(JSON.stringify(record)); rows += conversion.rows; notModelCalls += conversion.notModelCalls; unnamedModel += conversion.unnamedModel; noTokens += conversion.noTokens; unparseable += conversion.unparseable; if (conversion.reportedCostUsd !== null) { reportedCostUsd = (reportedCostUsd ?? 0) + conversion.reportedCostUsd; } } const out = stringFlag(args, 'out') ?? stringFlag(args, 'o'); if (out !== undefined) { await writeFile(out, lines.join('\n') + (lines.length > 0 ? '\n' : ''), 'utf8'); console.error(t.fromLangsmith.written(out)); } else { for (const line of lines) console.log(line); } console.error(t.fromLangsmith.summary(files.length, rows)); if (notModelCalls > 0) console.error(t.fromLangsmith.notModelCalls(notModelCalls)); if (unnamedModel > 0) console.error(t.fromLangsmith.unnamedModel(unnamedModel)); if (noTokens > 0) console.error(t.fromLangsmith.noTokens(noTokens)); if (reportedCostUsd !== null) { console.error(t.fromLangsmith.reportedCost(formatUsd(reportedCostUsd))); } if (unparseable > 0) console.error(t.fromLangsmith.unparseable(unparseable)); } /** * `trazum from-helicone ` — a Helicone request export as a usage log. * * The third converter, and the one that needs three columns where the others * need one: Helicone carries `request_model`, `model_override` and * `response_model`, and they can disagree. The response wins, because a bill * is about what was billed rather than what was intended, and the * disagreements are counted so a substitution is something the reader sees. * * Two absences are stated rather than filled in. There is no cache token * split on the row, only a flag; and a Helicone request id names one call and * never a conversation, so the records carry no session and the * conversation-shaped findings stay unavailable. Both are printed, because a * gap that is not said reads as a gap that is not there. */ /** * The provider's own usage report, priced from the catalogue. * * One file in, usage-log records out, like every other converter here. What * makes this one different is where the file comes from: the operator runs * the `curl` themselves, with their own admin credential, and this command * never sees it. `anthropic-usage.ts` opens by arguing why that is the only * arrangement this project can offer. */ async function commandFromAnthropic(args: Args, t: CliMessages): Promise { const target = args.positional[0]; if (target === undefined) throw new Error(t.fromAnthropic.noPath()); let text: string; try { text = await readFile(target, 'utf8'); } catch { throw new Error(t.fromAnthropic.notFound(target)); } const label = stringFlag(args, 'label'); const byWorkspace = await workspaceRulesFrom(stringFlag(args, 'label-by-workspace'), t); const conversion = anthropicUsageRecords(text, { ...(label === undefined ? {} : { label }), ...(byWorkspace === undefined ? {} : { labelByWorkspace: byWorkspace }), }); if (conversion.unparseable > 0) throw new Error(t.fromAnthropic.unparseable()); const lines = conversion.records.map((record) => JSON.stringify(record)); const out = stringFlag(args, 'out') ?? stringFlag(args, 'o'); if (out !== undefined) { await writeFile(out, lines.join('\n') + (lines.length > 0 ? '\n' : ''), 'utf8'); console.error(t.fromAnthropic.written(out)); } else if (lines.length > 0) { console.log(lines.join('\n')); } /* Every refusal on stderr, so a pipeline reads records on stdout and a person reads what could not be priced. The order is the order somebody acts in: what was read, then what was left out, then what to ask for. */ console.error(t.fromAnthropic.summary(conversion.buckets, conversion.rows)); if (conversion.unnamedModel > 0) console.error(t.fromAnthropic.unnamedModel(conversion.unnamedModel)); if (conversion.nonStandardTier > 0) { console.error(t.fromAnthropic.nonStandardTier(conversion.nonStandardTier)); } if (!conversion.tierNamed && conversion.rows > 0) console.error(t.fromAnthropic.tierUnknown()); if (conversion.webSearchRequests > 0) console.error(t.fromAnthropic.webSearch(conversion.webSearchRequests)); if (conversion.labelledByWorkspace > 0) { console.error(t.fromAnthropic.labelledByWorkspace(conversion.labelledByWorkspace)); } if (conversion.unruledWorkspace > 0) { console.error(t.fromAnthropic.unruledWorkspace(conversion.unruledWorkspace)); } if (conversion.workspaceNotGrouped) console.error(t.fromAnthropic.workspaceNotGrouped()); if (conversion.truncated) console.error(t.fromAnthropic.truncated()); } /** * `from-openai`: the other provider's usage report, under the same * arrangement as `from-anthropic` — the operator's curl, the operator's * admin key, and this command reading only what came back. What differs is * in `openai-usage.ts`: the record is written in the Chat Completions shape * because that is how this report counts, and audio and image tokens are * set aside rather than priced at a text rate. */ async function commandFromOpenai(args: Args, t: CliMessages): Promise { const target = args.positional[0]; if (target === undefined) throw new Error(t.fromOpenai.noPath()); let text: string; try { text = await readFile(target, 'utf8'); } catch { throw new Error(t.fromOpenai.notFound(target)); } const label = stringFlag(args, 'label'); const byProject = await projectRulesFrom(stringFlag(args, 'label-by-project'), t); const conversion = openaiUsageRecords(text, { ...(label === undefined ? {} : { label }), ...(byProject === undefined ? {} : { labelByProject: byProject }), }); if (conversion.unparseable > 0) throw new Error(t.fromOpenai.unparseable()); const lines = conversion.records.map((record) => JSON.stringify(record)); const out = stringFlag(args, 'out') ?? stringFlag(args, 'o'); if (out !== undefined) { await writeFile(out, lines.join('\n') + (lines.length > 0 ? '\n' : ''), 'utf8'); console.error(t.fromOpenai.written(out)); } else if (lines.length > 0) { console.log(lines.join('\n')); } /* Refusals on stderr, in the order somebody acts in: what was read, what was left out and why, what to ask the endpoint for next time. */ console.error(t.fromOpenai.summary(conversion.buckets, conversion.rows, conversion.requests)); if (conversion.unnamedModel > 0) console.error(t.fromOpenai.unnamedModel(conversion.unnamedModel)); if (conversion.batch > 0) console.error(t.fromOpenai.batch(conversion.batch)); if (!conversion.batchNamed && conversion.rows > 0) console.error(t.fromOpenai.batchUnknown()); if (conversion.nonDefaultTier > 0) { console.error(t.fromOpenai.nonDefaultTier(conversion.nonDefaultTier, conversion.tiersRefused.join(', '))); } if (!conversion.tierNamed && conversion.rows > 0) console.error(t.fromOpenai.tierUnknown()); if (conversion.mixedRows > 0) { console.error(t.fromOpenai.mixed(conversion.mixedRows, conversion.nonTextTokens, conversion.cacheWriteUnplaced)); } if (conversion.unsplitRows > 0) console.error(t.fromOpenai.unsplit(conversion.unsplitRows)); if (conversion.labelledByProject > 0) console.error(t.fromOpenai.labelledByProject(conversion.labelledByProject)); if (conversion.unruledProject > 0) console.error(t.fromOpenai.unruledProject(conversion.unruledProject)); if (conversion.projectNotGrouped) console.error(t.fromOpenai.projectNotGrouped()); if (conversion.truncated) console.error(t.fromOpenai.truncated()); } /** * `from-openrouter`: the router's activity report, read as a log. The one * provider Trazum already prices from a live catalogue (`pricing --from * openrouter`), keyed by the same slugs this report carries. What OpenRouter * charged is printed beside the records and never merged into them. */ async function commandFromOpenrouter(args: Args, t: CliMessages): Promise { const target = args.positional[0]; if (target === undefined) throw new Error(t.fromOpenrouter.noPath()); let text: string; try { text = await readFile(target, 'utf8'); } catch { throw new Error(t.fromOpenrouter.notFound(target)); } const label = stringFlag(args, 'label'); const byWorkspace = await openrouterWorkspaceRulesFrom(stringFlag(args, 'label-by-workspace'), t); const conversion = openrouterActivityRecords(text, { ...(label === undefined ? {} : { label }), ...(byWorkspace === undefined ? {} : { labelByWorkspace: byWorkspace }), }); if (conversion.unparseable > 0) throw new Error(t.fromOpenrouter.unparseable()); const lines = conversion.records.map((record) => JSON.stringify(record)); const out = stringFlag(args, 'out') ?? stringFlag(args, 'o'); if (out !== undefined) { await writeFile(out, lines.join('\n') + (lines.length > 0 ? '\n' : ''), 'utf8'); console.error(t.fromOpenrouter.written(out)); } else if (lines.length > 0) { console.log(lines.join('\n')); } console.error(t.fromOpenrouter.summary(conversion.rows, conversion.days, conversion.requests)); if (conversion.reportedUsageUsd > 0 || conversion.byokUsd > 0) { console.error(t.fromOpenrouter.reportedUsage(conversion.reportedUsageUsd, conversion.byokUsd)); } if (conversion.reasoningTokens > 0) console.error(t.fromOpenrouter.reasoning(conversion.reasoningTokens)); if (conversion.unnamedModel > 0) console.error(t.fromOpenrouter.unnamedModel(conversion.unnamedModel)); if (conversion.undatedRows > 0) console.error(t.fromOpenrouter.undated(conversion.undatedRows)); if (conversion.labelledByWorkspace > 0) { console.error(t.fromOpenrouter.labelledByWorkspace(conversion.labelledByWorkspace)); } if (conversion.unruledWorkspace > 0) console.error(t.fromOpenrouter.unruledWorkspace(conversion.unruledWorkspace)); if (conversion.workspaceNotGrouped) console.error(t.fromOpenrouter.workspaceNotGrouped()); } async function commandFromHelicone(args: Args, t: CliMessages): Promise { const target = args.positional[0]; if (target === undefined) throw new Error(t.fromHelicone.noPath()); let info; try { info = await stat(target); } catch { throw new Error(t.fromHelicone.notFound(target)); } const files: string[] = []; if (info.isDirectory()) { const entries = await readdir(target, { recursive: true, withFileTypes: true }); for (const entry of entries) { if (entry.isFile() && /\.(json|jsonl|ndjson)$/i.test(entry.name)) { files.push(join(entry.parentPath, entry.name)); } } files.sort(); if (files.length === 0) throw new Error(t.fromHelicone.noExports(target)); } else { files.push(target); } const lines: string[] = []; let rows = 0; let unnamedModel = 0; let disagreements = 0; let noTokens = 0; let cacheFlagged = 0; let unparseable = 0; for (const file of files) { const conversion = heliconeRecords(await readFile(file, 'utf8')); for (const record of conversion.records) lines.push(JSON.stringify(record)); rows += conversion.rows; unnamedModel += conversion.unnamedModel; disagreements += conversion.modelDisagreements; noTokens += conversion.noTokens; cacheFlagged += conversion.cacheFlagged; unparseable += conversion.unparseable; } const out = stringFlag(args, 'out') ?? stringFlag(args, 'o'); if (out !== undefined) { await writeFile(out, lines.join('\n') + (lines.length > 0 ? '\n' : ''), 'utf8'); console.error(t.fromHelicone.written(out)); } else { for (const line of lines) console.log(line); } console.error(t.fromHelicone.summary(files.length, rows)); if (unnamedModel > 0) console.error(t.fromHelicone.unnamedModel(unnamedModel)); if (disagreements > 0) console.error(t.fromHelicone.disagreements(disagreements)); if (noTokens > 0) console.error(t.fromHelicone.noTokens(noTokens)); if (cacheFlagged > 0) console.error(t.fromHelicone.cacheFlagged(cacheFlagged)); // Said on every run that produced records, not only when something is // missing: the absence is a property of the format, and a reader who has // just converted a month needs to know which questions this log cannot // answer before they go looking for the answers. if (rows > 0) console.error(t.fromHelicone.noSessions()); if (unparseable > 0) console.error(t.fromHelicone.unparseable(unparseable)); } /** * `trazum position ` — where the month stands, measured. * * One answer where `profile`, `budgetPositions` and `watch` each held a * piece: every configured ceiling with its measurement, its window and its * denominators, from the named log alone. The distance line is division on * the past — `positionReport` withholds it under the floor, on an over and * on a zero rate, so if it prints, its denominator prints with it. */ async function commandPosition( args: Args, config: TrazumConfig, pricing: PricingCatalogue, t: CliMessages, ): Promise { const file = args.positional[0]; if (file === undefined) throw new Error(t.position.noLog()); const text = await readUsageLog(file, t); const records = text .split('\n') .filter((line) => line.trim() !== '') .map((line) => parseUsageLine(line)) .filter((record): record is NonNullable> => record !== null); const document = positionReport( records, { ...(config.spend === undefined ? {} : { spend: config.spend }), ...(config.limits === undefined ? {} : { limits: config.limits }), }, { catalogue: pricing }, ); /** * The HTML door, written on both output paths — the 1.64 rule: the page a * person forwards must exist whether the run was for a human or a pipe. */ const htmlOut = stringFlag(args, 'html-out'); if (htmlOut !== undefined) { await writeFile(htmlOut, renderPositionHtml(document, t), 'utf8'); if (!boolFlag(args, 'json')) console.error(c.dim(t.html.written(htmlOut))); } if (boolFlag(args, 'json')) { console.log(JSON.stringify(document, null, 2)); return; } const scopeName = (position: { scope: string; label: string | null }): string => position.scope === 'month' ? t.position.scopeMonth() : position.scope === 'day' ? t.position.scopeDay() : t.position.scopeLabel(position.label ?? ''); console.log(); console.log(sectionHeading(t.position.heading(document.month.id))); for (const position of document.positions) { const name = scopeName(position); if (position.verdict === 'cannot-tell') { console.log(` ${c.yellow('?')} ${wrap(t.position.cannotTell(name), 72, ' ')}`); continue; } if (position.verdict === 'over') { console.log( ` ${c.red('✗')} ${wrap( t.position.over(name, formatUsd(position.measuredUsd), formatUsd(position.limitUsd), formatUsd(-position.remainingUsd)), 72, ' ', )}`, ); continue; } console.log( ` ${c.green('✓')} ${wrap( t.position.within( name, formatUsd(position.measuredUsd), formatUsd(position.limitUsd), formatUsd(position.remainingUsd), position.daysMeasured, position.daysElapsed, ), 72, ' ', )}`, ); if (position.distance !== null) { console.log( ` ${c.dim(wrap( t.position.distance( position.distance.daysAway.toFixed(1), formatUsd(position.distance.usdPerDay), position.distance.overDays, ), 70, ' ', ))}`, ); } } if (document.unmeasured.length > 0) { console.log(); console.log(` ${c.bold(t.position.unmeasuredHeading())}`); for (const entry of document.unmeasured) { console.log(` ${c.yellow(wrap(t.position.unmeasured(scopeName(entry), t.position.why(entry.why)), 70, ' '))}`); } } if (document.cannotSay.length > 0) { console.log(); console.log(` ${c.bold(t.position.cannotSayHeading())}`); for (const code of document.cannotSay) { console.log(` ${c.dim(wrap(t.position.cannotSay(code), 70, ' '))}`); } } if (document.unpricedRecords > 0) { console.log(); console.log(` ${c.yellow(wrap(t.position.unpriced(document.unpricedRecords), 72, ' '))}`); } console.log(); console.log(` ${c.dim(wrap(t.position.source(), 74, ' '))}`); } async function commandPulse(args: Args, t: CliMessages): Promise { const root = process.cwd(); const maxStaleHours = numberFlag(args, 'max-stale-hours', Number.NaN, t); const state = await readWatchState(root); const { resolved } = await readStore(root); /** * The newest pull and the furthest reach, kept apart. * * One says a job ran; the other says how far its answers go. A store pulled * ten minutes ago whose newest record stops two days back is a healthy cron * in front of a provider that reports late, and a single figure would call * that either a failure or a success depending which half it took. */ let storePulledMs: number | null = null; let storeCoveredToMs: number | null = null; for (const record of resolved.records) { if (storePulledMs === null || record.pulledAtMs > storePulledMs) storePulledMs = record.pulledAtMs; if (storeCoveredToMs === null || record.toMs > storeCoveredToMs) storeCoveredToMs = record.toMs; } const report = heartbeats( { watchCycleMs: state?.lastCycleMs ?? null, storePulledMs, storeCoveredToMs }, Number.isFinite(maxStaleHours) ? { nowMs: Date.now(), maxStaleHours } : { nowMs: Date.now() }, ); if (boolFlag(args, 'json')) { console.log(JSON.stringify(report, null, 2)); if (report.stale) process.exitCode = 1; return; } const when = (ms: number): string => new Date(ms).toISOString().replace('T', ' ').slice(0, 16); console.log(); console.log(sectionHeading(t.pulse.heading())); for (const heartbeat of report.beats) { const name = t.pulse.kind(heartbeat.kind); if (heartbeat.lastMs === null) { // Never run is not late. It is a thing nobody has started here, and a // line that shouted about it would be this tool nagging. console.log(` ${c.dim(t.pulse.neverRun(name))}`); continue; } const line = t.pulse.age(name, when(heartbeat.lastMs), heartbeat.ageHours ?? 0); if (heartbeat.verdict === 'stale') console.log(` ${c.red(`✗ ${line}`)}`); else if (heartbeat.verdict === 'within') console.log(` ${c.green(`✓ ${line}`)}`); else console.log(` ${line}`); } console.log(); if (report.maxStaleHours === null) { // Nothing was judged, and the exit code says so silently. Said out loud, // because a screen of green ticks with no threshold behind them is the // shape somebody reads as "checked". console.log(` ${c.dim(wrap(t.pulse.noThreshold(), 74, ' '))}`); } else if (report.stale) { process.exitCode = 1; console.log(` ${c.red(wrap(t.pulse.stale(report.maxStaleHours), 74, ' '))}`); } else { console.log(` ${c.dim(wrap(t.pulse.within(report.maxStaleHours), 74, ' '))}`); } console.log(` ${c.dim(wrap(t.pulse.notAService(), 74, ' '))}`); console.log(); } /** * `trazum bench` — measures this machine, honestly. * * The standard workloads, one shot each, wall time and peak RSS. **No * comparison and no judgement**: a number a person runs before and after a * change and reads side by side. The pathological cases were timed once, by * hand, during a stress session — 1MB of prose in about a second, a * 200,000-line log in about 1.3 — and nothing held them there. This command is * that measurement made repeatable; the gate that holds it belongs to a ratio * against in-process calibration, not to these wall clocks. * * Each workload runs in its own child process, because peak RSS is a fact * about a process: five workloads sharing one heap would each report the * high-water mark of whichever ran biggest before them. The child is this same * CLI with `--workload`, so what the bench measures is exactly what a user * runs. * * The workloads are **generated, deterministic and never committed** — the * fuzzer's own LCG, a fixed pricing date — so two runs on one machine differ * by the machine's weather, never by the input. Generation happens outside the * timed window: the bench times the product, not the bench. * * "Peak heap" in the plan is reported here as **peak RSS** — * `process.resourceUsage().maxRSS`, what the operating system actually billed * the process — because a true heap high-water mark is not observable from * inside a synchronous run without instrumentation that would itself move the * number. The field says what it is. */ const BENCH_WORKLOADS = [ 'optimize-1mb-safe', 'optimize-1mb-aggressive', 'profile-200k', 'walk-10k', 'rollup-20k', ] as const; type BenchWorkloadId = (typeof BENCH_WORKLOADS)[number]; interface BenchMeasurement { id: string; wallMs: number; /** The calibration loop's wall time, in this same process, right after the workload. */ calibrationMs: number; /** wallMs over calibrationMs — the number a gate can hold, because the machine cancels out. */ ratio: number; maxRssBytes: number; /** Input size in the unit the workload is named by; the others are null, never zero. */ bytes: number | null; lines: number | null; files: number | null; } /** The committed ratio baseline `--record` writes and `--against` reads. */ interface BenchBaseline { schemaVersion: 1; workloads: { id: string; ratio: number }[]; } interface BenchDocument { schemaVersion: 1; node: string; platform: string; arch: string; cpus: number; cpuModel: string | null; workloads: BenchMeasurement[]; } /** The hostile-input suite's LCG: same seed, same workload, any machine. */ function benchGenerator(seed: number): () => number { let state = seed; return () => (state = (state * 1103515245 + 12345) % 2147483648) / 2147483648; } /** Prices resolve against a fixed date, so the workload cannot drift with the calendar. */ const BENCH_DATE = new Date('2026-01-01T00:00:00Z'); /** * Enough integer work for the loop to take a stable fraction of a second on * anything that can run Node, and little enough that calibrating five * workloads is not itself the bench. */ const CALIBRATION_ITERATIONS = 1 << 24; /** * The yardstick a ratio divides by: a fixed integer loop, timed in the same * process as the workload it calibrates. * * **Deliberately not the product's own code.** A calibration built on * `estimateTokens` would speed up when the tokenizer does, and every ratio in * every committed baseline would silently mean something new. This loop is * arithmetic that no release has a reason to touch — the same LCG the corpus * generators use, run hot — so a ratio moves only when the workload does. * * CI machines lie about wall time; they lie to the workload and the yardstick * by roughly the same amount, and the ratio is what is left when the lie * cancels out. That is the whole of chapter two's argument, and the reason a * gate on `ratio` can hold where a gate on `wallMs` would fail on weather. */ function benchCalibration(): number { const startedAt = performance.now(); let state = 1; for (let i = 0; i < CALIBRATION_ITERATIONS; i += 1) { state = (state * 1103515245 + 12345) % 2147483648; } const elapsed = performance.now() - startedAt; // Reading the accumulator keeps the loop observable — a JIT that could prove // the result unused could also skip the work being timed. if (state < 0) throw new Error('unreachable: the LCG stays in [0, 2^31)'); return elapsed; } /** * Prose the rules have work in — verbose phrases, duplicate lines, emphasis, * spacing — with code fences and URLs mixed in so the segmenter and the masks * are part of what is timed, not skipped by an input too clean to exercise them. */ function benchPrompt(targetBytes: number): string { const rnd = benchGenerator(97); const sentences = [ 'Please kindly note that in order to get the best results you should always read the entire document.\n', 'It is very very important that the answer is concise and complete.\n', 'IMPORTANT: the sections below repeat their own headers.\n', 'The quick summary follows the long summary, which follows the summary.\n', 'See https://example.com/guide/section?step=3&mode=full for the walkthrough.\n', '```js\nconst total = items.reduce((sum, item) => sum + item.cost, 0);\n```\n', '- keep the tone neutral\n- keep the tone neutral\n- cite every claim\n', 'In the event that the input is empty, respond with an empty list.\n', ]; let text = ''; while (text.length < targetBytes) { text += sentences[Math.floor(rnd() * sentences.length)]; if (rnd() < 0.15) text += '\n\n'; } return text; } /** A usage log in the documented shape: timestamps, labels, sessions, cache fields. */ function benchLog(lines: number): string { const rnd = benchGenerator(53); const models = ['claude-opus-5', 'claude-sonnet-5', 'claude-haiku-4-5']; const labels = ['support-rag', 'classify', 'agent']; const startMs = Date.parse('2026-01-01T00:00:00Z'); const out: string[] = []; for (let i = 0; i < lines; i += 1) { const record = { timestamp: new Date(startMs + i * 7000).toISOString(), model: models[Math.floor(rnd() * models.length)], label: labels[Math.floor(rnd() * labels.length)], session: `conv-${Math.floor(rnd() * 400)}`, stop_reason: rnd() < 0.05 ? 'max_tokens' : 'end_turn', usage: { input_tokens: 200 + Math.floor(rnd() * 4000), output_tokens: 50 + Math.floor(rnd() * 800), cache_read_input_tokens: rnd() < 0.6 ? Math.floor(rnd() * 16000) : 0, cache_creation_input_tokens: rnd() < 0.2 ? Math.floor(rnd() * 4000) : 0, }, }; out.push(JSON.stringify(record)); } return out.join('\n'); } /** Runs one workload: generation outside the window, the product inside it. */ async function benchRun(id: BenchWorkloadId): Promise { const measure = (work: () => unknown): number => { const startedAt = performance.now(); work(); return performance.now() - startedAt; }; const done = (wallMs: number, sizes: Partial>): BenchMeasurement => { const calibrationMs = benchCalibration(); return { id, wallMs, calibrationMs, ratio: wallMs / calibrationMs, // getrusage reports kilobytes; the field name promises bytes, so convert here. maxRssBytes: process.resourceUsage().maxRSS * 1024, bytes: sizes.bytes ?? null, lines: sizes.lines ?? null, files: sizes.files ?? null, }; }; switch (id) { case 'optimize-1mb-safe': case 'optimize-1mb-aggressive': { const prompt = benchPrompt(1024 * 1024); const level = id === 'optimize-1mb-safe' ? 'safe' : 'aggressive'; return done(measure(() => optimize(prompt, { level })), { bytes: Buffer.byteLength(prompt, 'utf8') }); } case 'profile-200k': { const lines = 200_000; const text = benchLog(lines); return done( measure(() => profileUsage(text, { catalogue: BUNDLED_CATALOGUE, on: BENCH_DATE })), { lines }, ); } case 'walk-10k': { /** * The discovery half of `rank` and directory `check`: find every file, * read it, estimate it. Generated under the system temp directory and * removed afterwards — ten thousand files are a workload, not a residue. */ const files = 10_000; const root = await mkdtemp(join(tmpdir(), 'trazum-bench-')); try { const rnd = benchGenerator(11); for (let dir = 0; dir < 100; dir += 1) { const dirPath = join(root, `d${dir}`); await mkdir(dirPath); for (let i = 0; i < files / 100; i += 1) { await writeFile( join(dirPath, `p${i}.txt`), `Summarise the report in ${3 + Math.floor(rnd() * 9)} bullet points.\nKeep every figure.\n`, ); } } const startedAt = performance.now(); let estimated = 0; const walk = (path: string): void => { for (const entry of readdirSync(path, { withFileTypes: true })) { const child = join(path, entry.name); if (entry.isDirectory()) walk(child); else estimated += estimateTokens(readFileSync(child, 'utf8')); } }; walk(root); const wallMs = performance.now() - startedAt; if (estimated <= 0) throw new Error('bench walk read nothing — the workload is broken'); return done(wallMs, { files }); } finally { await rm(root, { recursive: true, force: true }); } } case 'rollup-20k': { /** * Twenty contributors of a thousand lines each. Their profiles are built * outside the window — profiling is `profile-200k`'s measurement — so * what this times is the roll-up itself: twenty documents parsed, * checked and merged. */ const contributors = 20; const linesEach = 1000; const inputs: { name: string; text: string }[] = []; for (let i = 0; i < contributors; i += 1) { const report = profileUsage(benchLog(linesEach), { catalogue: BUNDLED_CATALOGUE, on: BENCH_DATE }); inputs.push({ name: `team-${i}`, text: JSON.stringify(report) }); } return done(measure(() => rollUp(inputs)), { lines: contributors * linesEach }); } } } function printBenchTable(measurements: BenchMeasurement[], t: CliMessages, machine?: BenchDocument): void { const n = (value: number): string => Math.round(value).toLocaleString(t.numberLocale); const mb = (bytes: number): string => `${(bytes / (1024 * 1024)).toFixed(1)} MB`; const idWidth = Math.max(...measurements.map((m) => m.id.length), t.bench.colWorkload().length); console.log(); console.log(sectionHeading(t.bench.heading())); if (machine !== undefined) { console.log(` ${c.dim(t.bench.machine(machine.node, machine.platform, machine.cpus, machine.cpuModel))}`); } console.log(); console.log( ` ${c.dim(t.bench.colWorkload().padEnd(idWidth))} ${c.dim(t.bench.colWall().padStart(10))} ${c.dim(t.bench.colRatio().padStart(7))} ${c.dim(t.bench.colPeakRss().padStart(10))}`, ); for (const m of measurements) { console.log( ` ${m.id.padEnd(idWidth)} ${n(m.wallMs).padStart(10)} ${m.ratio.toFixed(2).padStart(7)} ${mb(m.maxRssBytes).padStart(10)}`, ); } console.log(); console.log(` ${c.dim(wrap(t.bench.note(), 74, ' '))}`); console.log(); } /** Reads and validates a committed ratio baseline, loudly on anything else. */ async function readBenchBaseline(path: string, t: CliMessages): Promise { let parsed: unknown; try { parsed = JSON.parse(await readFile(path, 'utf8')); } catch { throw new Error(t.bench.unreadableBaseline(path)); } const candidate = parsed as BenchBaseline; // A version this Trazum does not know is a loud error naming the fix, never // a best-effort read: the file is committed, so it crosses upgrades, and a // gate on misread numbers is a gate on numbers somebody invented. if (candidate?.schemaVersion !== 1 || !Array.isArray(candidate.workloads)) { throw new Error(t.bench.badBaseline(path, JSON.stringify((parsed as { schemaVersion?: unknown })?.schemaVersion))); } for (const entry of candidate.workloads) { if (typeof entry?.id !== 'string' || !Number.isFinite(entry?.ratio) || entry.ratio <= 0) { throw new Error(t.bench.badBaseline(path, 'workloads')); } } return candidate; } /** * `trazum schema ` — the format, portable. * * Prints the JSON Schema for a named contract, so a document can be checked * by any off-the-shelf validator with no Trazum installed. The output is the * schema and nothing else — pipeable by construction, like `--json` * everywhere — and the refusal with no or an unknown name lists every * contract, derived from the same list `--contract` accepts, the way the * gateway names its providers. */ function commandSchema(args: Args, t: CliMessages): void { const name = args.positional[0]; if (name === undefined) throw new Error(t.schema.noTarget(CONTRACT_NAMES.join(', '))); if (!(CONTRACT_NAMES as readonly string[]).includes(name)) { throw new Error(t.schema.unknown(name, CONTRACT_NAMES.join(', '))); } console.log(JSON.stringify(contractSchema(name as (typeof CONTRACT_NAMES)[number]), null, 2)); } async function commandBench(args: Args, t: CliMessages): Promise { const asJson = boolFlag(args, 'json'); const chosen = stringFlag(args, 'workload'); const recordPath = stringFlag(args, 'record'); const againstPath = stringFlag(args, 'against'); const maxRatioRaw = args.flags.get('max-ratio'); if (recordPath !== undefined && againstPath !== undefined) { throw new Error(t.bench.recordAndAgainst()); } let maxRatio: number | null = null; if (againstPath !== undefined) { // The factor is a policy, so it is stated by the caller rather than // defaulted here — the same rule as pulse's threshold. if (maxRatioRaw === undefined) throw new Error(t.bench.needsMaxRatio()); const factor = Number(maxRatioRaw); if (!Number.isFinite(factor) || factor < 1) throw new Error(t.bench.badMaxRatio(String(maxRatioRaw))); maxRatio = factor; } else if (maxRatioRaw !== undefined) { throw new Error(t.bench.maxRatioNeedsAgainst()); } // Read before measuring, so a baseline this run cannot gate on refuses in // milliseconds instead of after the workloads have been paid for. const baseline = againstPath !== undefined ? await readBenchBaseline(againstPath, t) : null; let measurements: BenchMeasurement[]; let machine: BenchDocument | undefined; if (chosen !== undefined) { if (!(BENCH_WORKLOADS as readonly string[]).includes(chosen)) { throw new Error(t.bench.unknownWorkload(chosen, BENCH_WORKLOADS.join(', '))); } measurements = [await benchRun(chosen as BenchWorkloadId)]; } else { const script = fileURLToPath(import.meta.url); measurements = []; for (const id of BENCH_WORKLOADS) { const stdout = runSelf(script, ['bench', '--workload', id, '--json', '--locale', t.locale]); measurements.push(JSON.parse(stdout) as BenchMeasurement); } machine = { schemaVersion: 1, node: process.version, platform: process.platform, arch: process.arch, cpus: cpus().length, cpuModel: cpus()[0]?.model ?? null, workloads: measurements, }; } if (recordPath !== undefined) { const baseline: BenchBaseline = { schemaVersion: 1, workloads: measurements.map((m) => ({ id: m.id, ratio: m.ratio })), }; await writeFile(recordPath, `${JSON.stringify(baseline, null, 2)}\n`); } if (asJson) { // The JSON shape never changes with the gate flags: a gate verdict is the // exit code and the sentences on stderr, the way `check` has always gated. console.log(JSON.stringify(chosen !== undefined ? measurements[0] : machine, null, 2)); } else { printBenchTable(measurements, t, machine); } if (recordPath !== undefined) { console.error(t.bench.recorded(recordPath)); } if (againstPath !== undefined && baseline !== null && maxRatio !== null) { const byId = new Map(baseline.workloads.map((entry) => [entry.id, entry.ratio])); let over = false; for (const m of measurements) { const recorded = byId.get(m.id); // Measured but never recorded is not a pass: a gate that silently skips // a workload reads as green coverage it does not have. if (recorded === undefined) throw new Error(t.bench.notInBaseline(m.id, againstPath)); const allowed = recorded * maxRatio; if (m.ratio > allowed) { over = true; console.error(c.red(t.bench.gateOver(m.id, m.ratio.toFixed(2), allowed.toFixed(2)))); } } if (over) { process.exitCode = 1; } else { console.error(c.dim(t.bench.gateWithin(String(maxRatio)))); } } } /** * `trazum feedback` — where to say it, and what to say. * * **This command sends nothing.** Trazum has no telemetry: the CLI makes no * network call it was not explicitly asked to make, and there is no ping, no * install hook and no anonymous counter anywhere in it. That is not an * omission somebody has been meaning to fix — a tool whose entire argument is * that it reads your bill without uploading it cannot also be quietly * reporting on you, and the security suite fails the build if this command * ever reaches the network. * * So the loop is closed the only honest way: the person decides to send * something, and this makes that as cheap as possible. It prints the four * places worth writing to, and a **prefilled link** carrying the facts a * maintainer always has to ask for — version, runtime, platform — printed in * full first, so nothing travels that the sender has not read. * * Nothing about *their work* is in it. Not the config, not a prompt, not a * label, not a figure. Those are the things a bug report needs and the things * only the reporter can decide to share, and a command that helpfully attached * them would be the leak this product exists not to be. */ function commandFeedback(t: CliMessages): void { const version = VERSION; /** * Facts about the machine, and nothing about the person. * * `process.platform` and the Node version are what every "cannot reproduce" * thread eventually asks for. The locale is here because Trazum ships two * languages and a report reading wrong in one of them is a real bug class. */ const environment = [ `Trazum ${version}`, `Node ${process.version}`, `${process.platform} ${process.arch}`, `locale ${t.locale}`, ]; const body = [ '', '', '', '---', ...environment.map((line) => `- ${line}`), ].join('\n'); const url = `${FEEDBACK_REPO}/issues/new?body=${encodeURIComponent(body)}`; console.log(); console.log(sectionHeading(t.feedback.heading())); console.log(` ${c.dim(wrap(t.feedback.sendsNothing(), 74, ' '))}`); console.log(); console.log(sectionHeading(t.feedback.whereHeading())); console.log(` ${t.feedback.wrongOptimisation()}`); console.log(` ${c.dim(`${FEEDBACK_REPO}/issues/new?template=wrong_optimisation.yml`)}`); console.log(` ${t.feedback.bug()}`); console.log(` ${c.dim(`${FEEDBACK_REPO}/issues/new?template=bug_report.yml`)}`); console.log(` ${t.feedback.question()}`); console.log(` ${c.dim(`${FEEDBACK_REPO}/discussions`)}`); console.log(` ${t.feedback.security()}`); console.log(` ${c.dim(`${FEEDBACK_REPO}/security/advisories/new`)}`); console.log(); console.log(sectionHeading(t.feedback.environmentHeading())); for (const line of environment) console.log(` ${line}`); console.log(` ${c.dim(wrap(t.feedback.environmentOnly(), 74, ' '))}`); console.log(); console.log(sectionHeading(t.feedback.linkHeading())); console.log(` ${url}`); console.log(); } /** * `trazum gateway ` — in the path, and refusing rather than advising. * * The last thing this product could not do. `serve` answers a question an * implementation may ignore; a connector reports the runaway after it ran. * Standing between the caller and the provider fixes both — usage is measured * from the provider's own response as it comes back, and a refusal is a * refusal. * * **The failure policy is required.** `--on-cannot-tell fail-open` keeps the * product working and lets the bill run; `fail-closed` stops the bill and takes * the product down with it. Both are defensible and there is deliberately no * default: a proxy that picks silently has made the most consequential decision * in somebody's architecture on their behalf, at install time, without saying * so. * * **Substitution is off unless it is written down.** `spend.substitute` in the * config, with the operator's own reason, and every substituted call is marked * so no later report treats it as the call the caller made. */ /** * Every provider the catalogue prices. * * `provider` is optional on a model — an overlay may add one without it — so a * missing provider is skipped rather than coerced. A model with no provider * cannot make its provider "supported" by accident. */ function pricedProviders(catalogue: PricingCatalogue): Set { const models = catalogue.models ?? []; return new Set(models.map((m) => m.provider).filter((p): p is string => typeof p === 'string')); } async function commandGateway( args: Args, config: TrazumConfig, configDir: string, pricing: PricingCatalogue, t: CliMessages, ): Promise { const provider = args.positional[0]; if (provider === undefined || UPSTREAMS[provider] === undefined) { /** * Three answers, not two. * * A provider Trazum **prices** but does not front is a different situation * from a name it has never heard, and until 1.53 both got the same * sentence. One is a gap in this tool with a workaround; the other is a * typo. Telling them apart is the difference between a user reaching for * `profile` and a user checking their spelling. * * Derived from the catalogue, so a provider added to pricing starts * getting the better answer without anyone remembering to update a list. */ const priced = pricedProviders(pricing); if (provider !== undefined && priced.has(provider)) { throw new Error(t.gateway.pricedNotFronted(provider, Object.keys(UPSTREAMS).join(', '))); } throw new Error(t.gateway.badProvider(provider ?? '', Object.keys(UPSTREAMS).join(', '))); } /** * No default, and the error says why rather than just what. * * The one flag in this product that refuses to guess on the reader's behalf, * because the two answers differ in which failure they accept and nobody but * the operator knows which their product can survive. */ const policyFlag = stringFlag(args, 'on-cannot-tell'); if (policyFlag === undefined || !FAILURE_POLICIES.includes(policyFlag as FailurePolicy)) { throw new Error(t.gateway.needsPolicy(FAILURE_POLICIES.join(', '))); } const { resolved } = await readStore(configDir); const budget = budgetPositions(resolved.records, config.spend, { catalogue: pricing }); const position = budget.positions[0] ?? null; /** * Read once at start, like `serve`'s. * * A file read in the request path would put Trazum's own latency between a * caller and their provider on every call, which is a cost this product * would otherwise be reporting on somebody else. The staleness is real, so a * refusal carries `asOfMs` and says what it rested on. */ const standing: GatewayStanding | null = position === null || position.coverage === 'none' ? null : { limitUsd: position.limitUsd, consumedUsd: position.consumedUsd, provenance: 'measured', asOfMs: Date.now(), }; /** Same measured side as `serve`'s, from `--log` — see `usageIndexFrom`. */ const limitsIndex = await usageIndexFrom(args, pricing, t); const measured: { calls: number; usd: number } = { calls: 0, usd: 0 }; /** * Forwarded calls whose cost this session cannot see. * * Counted separately and never folded into `measured`: the money was spent, * so the two are not interchangeable, and a total that quietly absorbed them * would be the flattering direction. */ let unmeasured = 0; const server = buildGateway({ provider, catalogue: pricing, policy: { onCannotTell: policyFlag as FailurePolicy, ...(config.spend?.substitute === undefined ? {} : { substitute: config.spend.substitute }), }, ...(config.limits === undefined ? {} : { limits: config.limits }), ...(limitsIndex === null ? {} : { position: (call: { label?: string; session?: string }) => positionAt(limitsIndex, call) }), ...(config.waive === undefined ? {} : { waivers: config.waive }), standing: () => standing, record: (call) => { measured.calls += 1; console.error( c.dim( t.gateway.measured( call.model, call.label, call.inputTokens, call.outputTokens, call.substituted, ), ), ); }, unmeasured: (cause) => { unmeasured += 1; console.error(c.yellow(` ${t.gateway.unmeasured(cause, unmeasured)}`)); }, note: (line) => { console.error(c.yellow(` ${line}`)); }, }); const socket = stringFlag(args, 'socket'); const portRaw = stringFlag(args, 'port'); const port = portRaw === undefined ? DEFAULT_GATEWAY_PORT : Number(portRaw); if (socket === undefined && (!Number.isInteger(port) || port < 0 || port > 65_535)) { throw new Error(t.serve.badPort(String(portRaw))); } const where = await listenGateway(server, socket !== undefined ? { socket } : { port }); console.log(c.bold(t.gateway.listening(where, provider))); console.log(` ${c.dim(wrap(t.gateway.pointYourSdk(where), 74, ' '))}`); console.log(` ${c.dim(wrap(t.gateway.credential(), 74, ' '))}`); console.log(` ${c.dim(wrap(t.gateway.neverSubstitutes(), 74, ' '))}`); console.log( ` ${c.dim(wrap(standing === null ? t.gateway.noStanding() : t.gateway.standing(formatUsd(standing.consumedUsd), formatUsd(standing.limitUsd)), 74, ' '))}`, ); console.log(` ${c.dim(wrap(t.gateway.policy(policyFlag), 74, ' '))}`); if (config.limits !== undefined && limitsIndex === null) { console.log(` ${c.yellow(wrap(t.serve.limitsNoLog(), 74, ' '))}`); } if (limitsIndex !== null && limitsIndex.unpriced > 0) { console.log(` ${c.yellow(wrap(t.serve.limitsUnpriced(limitsIndex.unpriced), 74, ' '))}`); } console.log(); } /** * `trazum ladder ` — is the ladder saving money, or is it a bill? * * The one number this command exists to print is the **break-even escalation * rate**. "We route to the cheap model first" describes a policy that saves * money and a policy that costs money equally well; only the rate separates * them, and nobody works it out in their head because the shape of the * arithmetic is not obvious — an escalation pays twice, since the cheap * attempt is not refunded. */ async function commandLadder( args: Args, config: TrazumConfig, pricing: PricingCatalogue, t: CliMessages, ): Promise { const path = args.positional[0]; if (path === undefined) { throw new Error(t.errors.missingInputFile()); } const report = profileUsage(await readUsageLog(path, t), { catalogue: pricing }); const ladders = config.ladders ?? {}; const n = (value: number): string => value.toLocaleString(t.numberLocale); const pct = (value: number): string => `${(value * 100).toFixed(1)}%`; console.log(); console.log(sectionHeading(t.ladder.heading())); if (Object.keys(ladders).length === 0) { console.log(` ${c.dim(wrap(t.ladder.noLadders(), 74, ' '))}`); console.log(); return; } console.log(` ${c.dim(wrap(t.ladder.theDoubleSpend(), 74, ' '))}`); console.log(); const vocabulary = config.outcomes ?? null; let anyProblem = false; for (const [label, policy] of Object.entries(ladders)) { /** * Validated before it is measured, and loudly. * * A ladder that escalates on a value declared a *success* pays twice for * work that already worked, on every call, while looking exactly like a * cost-saving measure in the config. Printing its measured position first * would bury that under a number. */ const problems = validateLadder(policy, vocabulary, pricing); if (problems.length > 0) { anyProblem = true; console.log(` ${c.red('✗')} ${c.bold(t.ladder.problemsHeading(label))}`); for (const problem of problems) { const detail = 'value' in problem ? problem.value : 'model' in problem ? problem.model : String(problem.tiers); console.log(` ${wrap(t.ladder.problem(problem.kind, detail), 70, ' ')}`); } console.log(); continue; } const slice = report.outcomeTallyByLabel.find((entry) => entry.label === label); const breakdown = report.byLabel.find((entry) => entry.label === label); /** * The shape of the work comes from the measured calls, so the break-even * rate is priced against what this workload actually sends rather than * against a token count somebody guessed at. */ const calls = breakdown?.breakdown.calls ?? 0; const shape = breakdown === undefined || calls === 0 ? { inputTokens: 0, outputTokens: 0 } : { inputTokens: Math.round( (breakdown.breakdown.inputTokens + breakdown.breakdown.cacheReadTokens + breakdown.breakdown.cacheWriteTokens) / calls, ), outputTokens: Math.round(breakdown.breakdown.outputTokens / calls), }; const empty = { byValue: [], recorded: 0, parsed: 0, unrecordedUsd: 0 }; const position = ladderPosition(policy, slice?.tally ?? empty, shape, vocabulary, pricing); console.log(` ${c.bold(t.ladder.workload(label))} ${c.dim(policy.tiers.join(' → '))}`); console.log( ` ${c.dim( t.ladder.arithmetic( formatUsd(position.arithmetic.cheapUsd), formatUsd(position.arithmetic.dearUsd), position.arithmetic.breakEvenRate === null ? '—' : pct(position.arithmetic.breakEvenRate), ), )}`, ); if (position.verdict === 'cannot-tell') { console.log( ` ${c.yellow('?')} ${wrap(t.ladder.cannotTell(position.unknown ?? '', n(position.calls)), 70, ' ')}`, ); } else { console.log( ` ${t.ladder.measured(pct(position.measuredRate ?? 0), n(position.escalations), n(position.calls))}`, ); const delta = formatUsd(Math.abs(position.deltaUsdPerCall ?? 0)); if (position.verdict === 'saving') { console.log(` ${c.green('✓')} ${wrap(t.ladder.saving(delta), 70, ' ')}`); } else if (position.verdict === 'costing') { console.log(` ${c.red('✗')} ${wrap(t.ladder.costing(delta), 70, ' ')}`); } else { console.log(` ${c.dim('·')} ${wrap(t.ladder.atBreakEven(pct(BREAK_EVEN_BAND)), 70, ' ')}`); } } console.log(); } console.log(` ${c.dim(wrap(t.ladder.notExecuted(), 74, ' '))}`); console.log(); /** * A misconfigured ladder fails the command, because it is the one finding * here that is wrong *now* rather than a measurement somebody should look * at. Everything else exits 0: this is a survey, like `doctor`. */ if (anyProblem) process.exitCode = 1; } /** * `trazum experiment --a