@trazum/cli 1.32.0 → 1.33.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -259,7 +259,7 @@ const COMMAND_FLAGS = {
259
259
  ],
260
260
  check: ['max-tokens', 'level', 'exact-tokens', 'markdown-out', 'baseline'],
261
261
  baseline: ['model', 'calls', 'output-tokens', 'cache-hit-rate', 'batch', 'exact-tokens', 'out', 'o'],
262
- profile: ['json', 'pricing', 'pricing-live', 'against', 'what-if', 'markdown-out', 'csv-out', 'csv-shape', 'max-usd', 'max-growth-usd', 'max-cache-loss-usd', 'max-day-usd', 'max-session-usd', 'label', 'since', 'until'],
262
+ profile: ['json', 'pricing', 'pricing-live', 'against', 'what-if', 'markdown-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'],
263
263
  route: ['prompt-file', 'cases', 'label', 'concurrency', 'json', 'yes', 'pricing', 'pricing-live'],
264
264
  eval: ['cases', 'level', 'concurrency', 'export', 'out', 'o', 'model'],
265
265
  prune: ['cases', 'concurrency', 'json', 'yes'],
@@ -1626,6 +1626,53 @@ async function commandProfile(args, config, pricing, t) {
1626
1626
  }
1627
1627
  const n = (value) => value.toLocaleString(t.numberLocale);
1628
1628
  const pct = (share) => `${(share * 100).toFixed(1)}%`;
1629
+ /**
1630
+ * `--dry-run`: what this log could and could not answer, and no bill.
1631
+ *
1632
+ * The question somebody has *before* wiring Trazum into CI is not "what did
1633
+ * we spend" but "will this log support the gates I want" — and answering it
1634
+ * with a full report makes them read a bill to find out a field is missing.
1635
+ * This path states readiness per capability and produces no dollar figure
1636
+ * at all, so nothing here can be mistaken for spend. It also refuses to
1637
+ * coexist with the gates: a gate over a report that was never produced
1638
+ * would exit green having judged nothing.
1639
+ */
1640
+ if (boolFlag(args, 'dry-run')) {
1641
+ const gateFlags = ['max-usd', 'max-growth-usd', 'max-cache-loss-usd', 'max-day-usd', 'max-session-usd'];
1642
+ if (gateFlags.some((flag) => typeof args.flags.get(flag) === 'string')) {
1643
+ throw new Error(t.profile.dryRunNoGates());
1644
+ }
1645
+ const cov = report.fieldCoverage;
1646
+ const parsed = cov.parsed;
1647
+ console.log(c.bold(t.profile.dryRunHeading()));
1648
+ console.log(` ${wrap(t.profile.dryRunParsed(n(parsed), n(report.skippedLines.length)), 74, ' ')}`);
1649
+ if (report.unpricedModels.length > 0) {
1650
+ console.log(` ${c.yellow('!')} ${wrap(t.profile.dryRunUnpriced(report.unpricedModels.join(', ')), 74, ' ')}`);
1651
+ }
1652
+ console.log();
1653
+ const can = (ok, line) => {
1654
+ console.log(` ${ok ? c.green('✓') : c.yellow('✗')} ${wrap(line, 74, ' ')}`);
1655
+ };
1656
+ const share = (count) => (parsed > 0 ? pct(count / parsed) : '0%');
1657
+ can(parsed > 0, t.profile.dryRunTotals());
1658
+ can(cov.label > 0, t.profile.dryRunLabels(share(cov.label)));
1659
+ can(cov.ts > 0, t.profile.dryRunClock(share(cov.ts)));
1660
+ can(cov.session > 0, t.profile.dryRunSessions(share(cov.session)));
1661
+ can(cov.stopReason > 0, t.profile.dryRunStopReason(share(cov.stopReason)));
1662
+ // "No cache traffic" is not a missing field: the split can only exist on
1663
+ // records that wrote, and a log that never wrote has nothing to record.
1664
+ if (cov.cacheWrites > 0) {
1665
+ can(cov.cacheTtl > 0, t.profile.dryRunCacheTtl(n(cov.cacheTtl), n(cov.cacheWrites)));
1666
+ }
1667
+ else {
1668
+ console.log(` ${c.dim('·')} ${wrap(t.profile.dryRunNoCacheTraffic(), 74, ' ')}`);
1669
+ }
1670
+ console.log();
1671
+ console.log(` ${c.dim(wrap(t.profile.dryRunFooter(), 74, ' '))}`);
1672
+ if (parsed === 0)
1673
+ process.exitCode = 1;
1674
+ return;
1675
+ }
1629
1676
  /**
1630
1677
  * The previous log, loaded before the output paths split so the growth gate
1631
1678
  * exists under `--json` too — a CI step reads the JSON and trusts the exit