feat(quota): add live commandcode-quota dashboard and OMP auth fix

Fetches live account quota from Command Code alpha usage endpoints
(whoami, billing/credits, billing/subscriptions, usage/summary) and
renders a plain-text dashboard via ui.notify.

- Graceful degradation: optional endpoint transport/timeout/parse
  failures degrade to null sections instead of aborting; only 401/403
  are hard failures. 429 is transient, not fatal.
- Overall deadline bounds the whole command to QUOTA_TIMEOUT_MS
  (per-request controllers are chained; post-deadline phases fail fast).
- OMP auth: filter unresolved $COMMANDCODE_API_KEY placeholder and fall
  back to the host resolver via pickCommandCodeApiKey.
- ZDR privacy header respected on quota requests.
- Redaction reuses redactCommandCodeErrorText plus JSON-quoted
  credential fields; outer-catch errors are redacted too.
- resetAt parsed from seconds, ms, numeric string, or ISO string.
- 21 hermetic unit tests wired into npm test (test:quota).
This commit is contained in:
José Galliano
2026-08-21 11:21:25 -06:00
parent 6de4626349
commit 307714b051
9 changed files with 1128 additions and 3 deletions
+23
View File
@@ -138,6 +138,29 @@ export function getApiKey(
return undefined
}
// Hosts such as OMP may pass the literal env-var name "$COMMANDCODE_API_KEY"
// (or "COMMANDCODE_API_KEY") as the "resolved" registry key instead of the
// actual credential. Treat those as unresolved.
export const COMMAND_CODE_PLACEHOLDER_KEYS = new Set([
"$COMMANDCODE_API_KEY",
"COMMANDCODE_API_KEY",
])
/**
* Pick the real API key from a host registry value and/or the env/auth-file
* fallback, never returning a literal placeholder or an empty/whitespace value.
* Pure/testable.
*/
export function pickCommandCodeApiKey(
registryKey: string | undefined,
hostKey: string | undefined,
): string | undefined {
const trimmed = typeof registryKey === "string" ? registryKey.trim() : undefined
if (!trimmed) return hostKey
if (COMMAND_CODE_PLACEHOLDER_KEYS.has(trimmed)) return hostKey
return trimmed
}
export function textContent(message: { content?: unknown }): string {
return recordArray(message.content)
.filter((part) => part.type === "text")