feat+test: F4 CLI/plugin quota_v2 migration + v1.x #7 AUTH_MISSING test pin (#59)

## F4 — bin/olp.mjs + olp-plugin/index.js migrate to quota_v2

Codex post-v0.5.0 review Q4: both `olp usage` CLI and `/olp usage`
plugin handler read legacy `body.quota` shape, which never has
`percent_used` or meaningful `available` data → display always fell
through to "no quota api" even when anthropic live quota was visible
on the dashboard (D81/D82).

Authority: codex review Q4 (PR #58); ADR 0008 Amendment 2 (quota_v2
shape: { provider, status, utilization, reset, representative_claim,
fallback_percentage, overage, failure?, failure_kind?, backoff_until? }).

### bin/olp.mjs cmdUsage

- When `body.quota_v2` present (non-empty array, server v0.5.0+): render
  per-provider rows with status badge (live/stale/unreachable/unavailable),
  5h + 7d utilization % (color-coded: green <50% / yellow 50-80% / red ≥80%),
  reset countdowns, binding claim, ⚠ stale /  unreachable annotations.
- When `body.quota_v2` absent: fall through to existing legacy `body.quota`
  rendering (preserves backwards compat with pre-v0.5.0 servers).
- Added `formatResetCountdown(epochSeconds)` — 5-range formatter (past /
  <1h / <24h / <7d / ≥7d), ported from dashboard.html D82. Exported for
  tests. Kept in bin/olp.mjs (not lib/) per spec guidance.
- Added `formatAgo(diffMs)` — small helper for stale-row age display.

### olp-plugin/index.js fmtUsage()

- Same quota_v2 migration. Plain-text output (no ANSI), one line per
  provider. Legacy body.quota fallback preserved.
- Added `pluginFormatResetCountdown(epochSeconds)` — intentionally
  duplicated from bin/olp.mjs (olp-plugin ships as a separate package
  and must not import from bin/). Exported for tests.
- Also fixed fmtUsage to read `w.request_count` (dashboard-data shape)
  in addition to legacy `w.requests` (OCP-era shape) for completeness.

### Backwards compat

- Pre-v0.5.0 server returns body.quota only → both surfaces use legacy
  display (unchanged behaviour).
- v0.5.0+ server returns both quota and quota_v2 → both surfaces prefer
  quota_v2.
- Legacy code paths kept (5-10 lines each, not deleted).

## v1.x roadmap #7 — AUTH_MISSING tuple path test coverage — CLOSED

The dedicated test was already shipped at D56 (test-features.mjs line
6255: 'engine: AUTH_MISSING terminates chain, fallbackDetail tuple
records trigger_type:"auth_missing" (D56, v1.x roadmap #7)'). This
commit closes the roadmap entry with a date stamp and PR reference.

Authority: docs/v1x-roadmap.md § "#7 — AUTH_MISSING tuple path test
coverage (D40 follow-up)".

## Tests

Suite 40 (9 new tests — 40a through 40i):
  40a — cmdUsage parses quota_v2 live rows (mock server)
  40b — cmdUsage falls back to legacy body.quota when quota_v2 absent
  40c — olp-plugin fmtUsage parses quota_v2 live row
  40d — olp-plugin fmtUsage falls back to legacy body.quota
  40e — formatResetCountdown covers all 5 time ranges
  40f — pluginFormatResetCountdown covers past/<1h/<24h/<7d/≥7d
  40g — cmdUsage renders quota_v2 stale row with ⚠ stale note
  40h — cmdUsage renders quota_v2 unreachable row with  indicator
  40i — cmdUsage renders quota_v2 unavailable rows correctly

759 → 768 tests, 0 fail.

Co-authored-by: dtzp555 <dtzp555@gmail.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
dtzp555-max
2026-05-27 11:24:46 +10:00
committed by GitHub
co-authored by taodeng Claude Sonnet 4.6
parent bddf2cba1e
commit fa2d1af130
5 changed files with 551 additions and 8 deletions
+79 -4
View File
@@ -169,26 +169,101 @@ export function fmtHealth(body) {
return out;
}
/**
* formatResetCountdown(epochSeconds) → human-readable reset countdown.
*
* Mirrors bin/olp.mjs + dashboard.html versions. Five ranges:
* past / < 1h / < 24h / < 7d / ≥ 7d
*
* Authority: ADR 0008 Amendment 2 (quota_v2 shape), ported from dashboard.html (D82).
* No external deps. Duplicated here intentionally (olp-plugin ships separately).
*/
export function pluginFormatResetCountdown(epochSeconds) {
if (epochSeconds == null) return "—";
const nowMs = Date.now();
const targetMs = epochSeconds * 1000;
const diffMs = targetMs - nowMs;
if (diffMs <= 0) return "resetting now";
const diffMin = Math.floor(diffMs / 60000);
const diffHr = Math.floor(diffMin / 60);
const diffDay = Math.floor(diffHr / 24);
if (diffMin < 60) return `resets in ${diffMin}m`;
if (diffHr < 24) {
const remMin = diffMin - diffHr * 60;
if (remMin === 0) return `resets in ${diffHr}h`;
return `resets in ${diffHr}h ${remMin}m`;
}
const target = new Date(targetMs);
const timeStr = target.toLocaleString("en-US", { hour: "numeric", minute: "2-digit", hour12: true });
if (diffDay < 7) {
const dayStr = target.toLocaleString("en-US", { weekday: "short" });
return `resets ${dayStr} ${timeStr}`;
}
const dateStr = target.toLocaleString("en-US", { month: "short", day: "numeric" });
return `resets ${dateStr} ${timeStr}`;
}
export function fmtUsage(body) {
let out = "OLP usage (24h)\n";
out += "─────────────────────────────\n";
const w = body.window_24h ?? body.usage_24h ?? {};
if (w.requests !== undefined) {
if (w.request_count !== undefined) {
out += `Requests: ${w.request_count}\n`;
const c = body.cache_hit_24h ?? {};
if (typeof c.hit_rate === "number") {
out += `Cache hit: ${(c.hit_rate * 100).toFixed(1)}%\n`;
}
} else if (w.requests !== undefined) {
out += `Requests: ${w.requests}\n`;
out += `Cache hit: ${w.cache_hit_rate != null ? `${(w.cache_hit_rate * 100).toFixed(1)}%` : "?"}\n`;
out += `Fallbacks: ${w.fallbacks ?? "?"}\n`;
} else if (typeof body.cache_hit_24h === "number") {
// Dashboard-data shape: cache_hit_24h is a rate ∈ [0,1]
// Legacy: cache_hit_24h as a bare number
out += `Cache hit (24h): ${(body.cache_hit_24h * 100).toFixed(1)}%\n`;
}
if (Array.isArray(body.quota) && body.quota.length > 0) {
// F4: prefer quota_v2 when present (server v0.5.0+), fall back to legacy quota.
// Authority: ADR 0008 Amendment 2 (quota_v2 shape).
if (Array.isArray(body.quota_v2) && body.quota_v2.length > 0) {
out += `\nPer-provider quota (live):\n`;
for (const p of body.quota_v2) {
const name = String(p.provider ?? "?").toUpperCase().padEnd(10);
const status = p.status ?? "unavailable";
if (status === "unavailable") {
out += ` ${name} unavailable ${p.reason ?? "no public quota api"}\n`;
} else if (status === "unreachable") {
const fk = p.failure?.kind ?? "unknown";
out += ` ${name} no cached data — failure: ${fk}\n`;
} else {
// live or stale
const util = p.utilization ?? {};
const reset = p.reset ?? {};
const parts = [];
for (const window of ["5h", "7d"]) {
const frac = util[window];
const resetEpoch = reset[window];
if (frac != null) {
const pct = `${Math.round(frac * 100)}%`;
const rst = pluginFormatResetCountdown(resetEpoch);
parts.push(`${window}: ${pct} (${rst})`);
}
}
const staleNote = status === "stale"
? ` ⚠ stale (${p.failure?.kind ?? "unknown"})`
: "";
out += ` ${name} ${status.padEnd(6)} ${parts.join(" ")}${staleNote}\n`;
}
}
} else if (Array.isArray(body.quota) && body.quota.length > 0) {
// Legacy fallback for pre-v0.5.0 servers
out += `\nPer-provider quota:\n`;
for (const q of body.quota) {
const pct = typeof q.percent_used === "number" ? q.percent_used : null;
const bar0 = pct != null ? ` ${bar(pct / 100, 12)} ${pct.toFixed(0)}%` : " no quota api";
out += ` ${String(q.name ?? "?").padEnd(10)}${bar0}\n`;
out += ` ${String(q.provider ?? q.name ?? "?").padEnd(10)}${bar0}\n`;
}
}
if (Array.isArray(body.top_fallback_chains_24h) && body.top_fallback_chains_24h.length > 0) {
out += `\nTop fallback chains (24h):\n`;
for (const f of body.top_fallback_chains_24h.slice(0, 5)) {