@trazum/cli 1.33.0 → 1.35.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', 'dry-run'],
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', 'markdown-summary'],
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'],
@@ -1778,6 +1778,41 @@ async function commandProfile(args, config, pricing, t) {
1778
1778
  */
1779
1779
  const gateVerdicts = [];
1780
1780
  let gateFailed = false;
1781
+ /**
1782
+ * Findings as policy. A waiver silences one gate's exit code for a bounded
1783
+ * time, on the record: the failure still prints (waived is shown as
1784
+ * waived, never hidden — the bill still counts it), the reason and the
1785
+ * days left print beside it, and the day the waiver expires the gate
1786
+ * fails again louder, naming the date and the reason somebody wrote.
1787
+ * That expiry is the entire mechanism by which a waiver stays a decision
1788
+ * instead of becoming a habit.
1789
+ */
1790
+ const waiverFor = (gate) => {
1791
+ const entry = (config.waive ?? []).find((w) => w.gate === gate);
1792
+ if (entry === undefined)
1793
+ return null;
1794
+ // The waiver covers its named day whole: expiry begins the next UTC day.
1795
+ const expiresMs = Date.parse(`${entry.until}T00:00:00Z`) + 86_400_000;
1796
+ return { entry, expired: Date.now() >= expiresMs };
1797
+ };
1798
+ /**
1799
+ * Applies a waiver to one failing gate. Returns true when the failure is
1800
+ * silenced — the caller skips its exitCode — and prints the record either
1801
+ * way, because a waived failure that vanished from the output would be a
1802
+ * finding deleted with extra steps.
1803
+ */
1804
+ const waived = (gate) => {
1805
+ const found = waiverFor(gate);
1806
+ if (found === null)
1807
+ return false;
1808
+ if (found.expired) {
1809
+ console.error(c.red(t.profile.waiveExpired(gate, found.entry.until, found.entry.reason)));
1810
+ return false;
1811
+ }
1812
+ const daysLeft = Math.max(0, Math.ceil((Date.parse(`${found.entry.until}T00:00:00Z`) + 86_400_000 - Date.now()) / 86_400_000));
1813
+ console.error(c.yellow(t.profile.waiveActive(gate, found.entry.reason, found.entry.until, String(daysLeft))));
1814
+ return true;
1815
+ };
1781
1816
  const applyGates = () => {
1782
1817
  /**
1783
1818
  * Before any verdict: whether the gated figure is the whole bill. A gate
@@ -1825,7 +1860,8 @@ async function commandProfile(args, config, pricing, t) {
1825
1860
  }
1826
1861
  if (usd > limit) {
1827
1862
  console.error(c.red(t.profile.labelBudgetFailed(label, formatUsd(usd), formatUsd(limit))));
1828
- process.exitCode = 1;
1863
+ if (!waived(`byLabel:${label}`))
1864
+ process.exitCode = 1;
1829
1865
  }
1830
1866
  else {
1831
1867
  console.error(c.dim(t.profile.labelBudgetOk(label, formatUsd(usd), formatUsd(limit))));
@@ -1886,7 +1922,8 @@ async function commandProfile(args, config, pricing, t) {
1886
1922
  * copy says so.
1887
1923
  */
1888
1924
  explainFailure(report.total.totalUsd - maxUsd);
1889
- process.exitCode = 1;
1925
+ if (!waived('maxUsd'))
1926
+ process.exitCode = 1;
1890
1927
  }
1891
1928
  else {
1892
1929
  console.error(c.dim(t.profile.maxUsdOk(formatUsd(report.total.totalUsd), formatUsd(maxUsd))));
@@ -1913,11 +1950,15 @@ async function commandProfile(args, config, pricing, t) {
1913
1950
  const worst = blinded[0];
1914
1951
  if (worst !== undefined) {
1915
1952
  console.error(c.red(t.profile.maxGrowthCoverageLost(blinded.map((d) => t.profile.coverageField(d.field)).join(', '), pct(worst.was), pct(worst.now))));
1953
+ // Deliberately unwaivable: this failure is "the comparison cannot
1954
+ // be made", and a waiver on unmeasurability would be a decision to
1955
+ // stop measuring — not a budget decision with an end date.
1916
1956
  process.exitCode = 1;
1917
1957
  }
1918
1958
  else if (againstDelta > maxGrowth) {
1919
1959
  console.error(c.red(t.profile.maxGrowthUsdFailed(formatSignedUsd(againstDelta), formatUsd(maxGrowth))));
1920
- process.exitCode = 1;
1960
+ if (!waived('maxGrowthUsd'))
1961
+ process.exitCode = 1;
1921
1962
  }
1922
1963
  }
1923
1964
  /**
@@ -1933,11 +1974,13 @@ async function commandProfile(args, config, pricing, t) {
1933
1974
  const gateCache = cacheEconomics(report.total);
1934
1975
  if (gateCache.deltaUsd > maxLoss) {
1935
1976
  console.error(c.red(t.profile.maxCacheLossFailed(formatUsd(gateCache.deltaUsd), formatUsd(maxLoss))));
1936
- process.exitCode = 1;
1977
+ if (!waived('maxCacheLossUsd'))
1978
+ process.exitCode = 1;
1937
1979
  }
1938
1980
  else if (gateCache.worstCaseDeltaUsd > maxLoss) {
1939
1981
  console.error(c.red(t.profile.maxCacheLossWorstCase(report.total.assumedWriteTtlCalls, formatUsd(gateCache.worstCaseDeltaUsd), formatUsd(maxLoss))));
1940
- process.exitCode = 1;
1982
+ if (!waived('maxCacheLossUsd'))
1983
+ process.exitCode = 1;
1941
1984
  }
1942
1985
  else {
1943
1986
  console.error(c.dim(t.profile.maxCacheLossOk(formatUsd(Math.max(0, gateCache.worstCaseDeltaUsd)), formatUsd(maxLoss))));
@@ -1982,7 +2025,8 @@ async function commandProfile(args, config, pricing, t) {
1982
2025
  if (worst.usd > maxDay) {
1983
2026
  console.error(c.red(`${t.profile.maxDayFailed(worst.day, formatUsd(worst.usd), formatUsd(maxDay))}${suspect}`));
1984
2027
  explainFailure(worst.usd - maxDay, { namesLargest: true });
1985
- process.exitCode = 1;
2028
+ if (!waived('maxDayUsd'))
2029
+ process.exitCode = 1;
1986
2030
  }
1987
2031
  else {
1988
2032
  console.error(c.dim(t.profile.maxDayOk(worst.day, formatUsd(worst.usd), formatUsd(maxDay))));
@@ -2023,7 +2067,8 @@ async function commandProfile(args, config, pricing, t) {
2023
2067
  else if (report.sessionSpend.maxUsd > maxSession) {
2024
2068
  console.error(c.red(t.profile.maxSessionFailed(formatUsd(report.sessionSpend.maxUsd), formatUsd(maxSession), n(report.sessionSpend.sessions))));
2025
2069
  explainFailure(report.sessionSpend.maxUsd - maxSession);
2026
- process.exitCode = 1;
2070
+ if (!waived('maxSessionUsd'))
2071
+ process.exitCode = 1;
2027
2072
  }
2028
2073
  else {
2029
2074
  console.error(c.dim(t.profile.maxSessionOk(formatUsd(report.sessionSpend.maxUsd), formatUsd(maxSession), n(report.sessionSpend.sessions))));
@@ -2091,6 +2136,8 @@ async function commandProfile(args, config, pricing, t) {
2091
2136
  // The verdict, where the person reading CI will see it. recordGates()
2092
2137
  // runs before the side files for exactly this.
2093
2138
  ...(gateVerdicts.length > 0 ? { gates: { failed: gateFailed, lines: gateVerdicts } } : {}),
2139
+ // The short form, for a reader who is not in the terminal.
2140
+ ...(boolFlag(args, 'markdown-summary') ? { summary: true } : {}),
2094
2141
  // The repricing, when --what-if was given: computed once above and
2095
2142
  // handed over, so the summary in a pull request cannot disagree
2096
2143
  // with the terminal about what a move would cost.