spec.md 7.9 KB

ADDED Requirements

Requirement: Fetch subscription detail from ZenMux per account

The CLI SHALL call GET https://zenmux.ai/api/v1/management/subscription/detail with an Authorization: Bearer <key> header for each resolved account and parse the JSON response into typed structures covering plan, currency, base_usd_per_flow, effective_usd_per_flow, account_status, quota_5_hour, quota_7_day, and quota_monthly. Accounts MUST be fetched sequentially in the order they were resolved. A failure on one account MUST NOT abort fetches for the remaining accounts.

Scenario: Single-account fetch

  • WHEN the user runs zenmux-usage with exactly one resolved account
  • THEN the CLI issues exactly one GET request to the subscription detail endpoint with Authorization: Bearer <key> and parses success: true into typed structs

Scenario: Multi-account fetch order

  • WHEN the user runs zenmux-usage with accounts personal then work resolved
  • THEN the CLI first fetches personal, then work, rendering each as soon as its response is available

Scenario: Partial failure does not abort

  • WHEN three accounts are resolved and the second account returns HTTP 401
  • THEN the CLI still fetches the third account and renders blocks for all three (one with an error body)

Scenario: Request respects a configurable timeout

  • WHEN the user runs zenmux-usage --timeout 2s and a server has not responded within 2 seconds
  • THEN the CLI aborts that account's request and records it as a timeout error while continuing with any remaining accounts

Requirement: Render three quota windows per account

In human output mode the CLI SHALL render, for each resolved account, one labeled row per window (5 hour, 7 day, month). Each row MUST include a fixed-width progress bar, the usage percentage to two decimals, the used-vs-max flows, and the used-vs-max USD value when the API provides them. Bars MUST be color-coded by usage band: green below 60%, yellow from 60% through 85%, red above 85%. The monthly window MUST render the max_flows and max_value_usd values and display (em-dash) in place of used values since the API does not return used_* fields for that window.

Scenario: Healthy 5-hour window

  • WHEN the API returns quota_5_hour.usage_percentage = 0.0715, used_flows = 57.2, max_flows = 800, used_value_usd = 1.88, max_value_usd = 26.26 for an account
  • THEN that account's block contains a row labeled 5 hour, a green-colored bar roughly 7% filled, 7.15%, 57.2 / 800 flows, and $1.88 / $26.26

Scenario: Monthly window has no used values

  • WHEN the API returns quota_monthly with only max_flows and max_value_usd populated
  • THEN the monthly row prints in place of used flows and used USD, and shows the max_flows and max_value_usd values

Scenario: Color disabled via flag

  • WHEN the user runs zenmux-usage --no-color
  • THEN no ANSI color escape sequences appear in the output

Scenario: Non-TTY output is automatically plain

  • WHEN stdout is piped or redirected to a file
  • THEN the CLI omits ANSI color escape sequences regardless of the --no-color flag

Requirement: Render per-account header block

For every resolved account the CLI SHALL print, before the quota rows, a header that identifies the account by name followed by a plan/status line. The account header MUST be visually distinct (e.g., ━━━ <name> ━━━ with bold styling when colors are enabled). The plan line MUST include the plan tier (capitalized), the monthly amount_usd, the account_status, and the effective_usd_per_flow rate.

Scenario: Single account header

  • WHEN one resolved account named personal returns plan.tier = "ultra", plan.amount_usd = 200, account_status = "healthy", effective_usd_per_flow = 0.03283
  • THEN the output contains a header block showing personal, and a plan line containing Ultra plan, $200/mo, healthy, and $0.03283/flow

Scenario: Multiple accounts separated by blank lines

  • WHEN two accounts personal and work are rendered in human mode
  • THEN each account has its own header block and the two blocks are separated by at least one blank line, in the order they were resolved

Requirement: Display token USD value consumed per account

Below each account's three quota rows the CLI SHALL print a summary line labeled "Tokens consumed (estimated USD value)" showing the used_value_usd from the 7-day window for that account, formatted as USD to two decimals.

Scenario: 7-day used value present

  • WHEN an account's quota_7_day.used_value_usd = 13.66
  • THEN that account's summary line reads Tokens consumed (estimated USD value): $13.66

Requirement: JSON passthrough mode

When invoked with --json the CLI SHALL write machine-readable JSON to stdout and MUST NOT print any human-formatted output.

  • If exactly one account was resolved, the output MUST be the single API response body unchanged (pretty-printing is not required; a trailing newline is permitted).
  • If more than one account was resolved, the output MUST be a JSON array whose elements are objects with shape {"account": <name>, "success": <bool>, "data": <API data object or null>, "error": <string or null>} in the order accounts were resolved. Successful accounts MUST have error: null; failed accounts MUST have data: null and a human-readable error string.

Exit codes behave identically to human mode.

Scenario: Single-account JSON

  • WHEN exactly one account is resolved and the user runs zenmux-usage --json | jq .data.plan.tier
  • THEN jq receives valid JSON and extracts the plan tier from data.plan.tier

Scenario: Multi-account JSON

  • WHEN two accounts personal and work are resolved and work returns HTTP 429
  • THEN stdout is a JSON array of length 2; the personal element has success: true and a populated data field; the work element has success: false, data: null, and a non-null error

Scenario: JSON mode on full failure

  • WHEN the CLI fails before any account was fetched (e.g., config parse error)
  • THEN the CLI writes the error message to stderr, exits with the corresponding non-zero code, and writes nothing to stdout

Requirement: Map API and transport failures to distinct exit codes

The CLI SHALL exit with these codes and write a single human-readable line to stderr for CLI-global failures:

  • 2 — invalid flag or argument (including unknown --account name)
  • 3 — no account resolvable (missing config and env, or explicit --config file missing)
  • 4 — authentication rejected (HTTP 401 or 403) — only when every fetched account failed with this cause
  • 5 — rate limited (HTTP 422) — only when every fetched account failed with this cause
  • 6 — network error or timeout — only when every fetched account failed with this cause
  • 7 — config file parse error or schema validation failure
  • 1 — any other unexpected error, or a mix of failure causes across accounts, or at least one account failed while others succeeded

On success: false in a response body the CLI SHALL treat that account as a failure and surface any error message from the payload.

Scenario: All accounts auth-rejected

  • WHEN every resolved account returns HTTP 401
  • THEN the CLI exits with code 4 after rendering each account's error block

Scenario: Mixed outcomes

  • WHEN account personal returns 200 and account work returns 401
  • THEN the CLI exits with code 1 after rendering both accounts

Scenario: All transport timeouts

  • WHEN every resolved account's HTTP request exceeds the configured timeout
  • THEN the CLI exits with code 6 after rendering each account's timeout block

Scenario: Single-account run preserves specific code

  • WHEN a single-account run (--account work, --api-key, or env-var fallback) fails with HTTP 401
  • THEN the CLI exits with code 4