---
name: ai-quota-dashboard
description: Use when building AI quota dashboards for web and Telegram.
version: 1.0.0
author: self.md
license: MIT
metadata:
  hermes:
    tags: [quota, telegram, dashboard, pillow, codex, claude, grok]
    related_skills: [hermes-agent, static-dashboard-development]
---

# AI Quota Dashboard

Build a readable English-language quota photo and matching static web view from a normalized, private JSON snapshot. Keep collection, rendering and delivery separate. The bundled starter renders a clearly labelled demo without provider credentials. Live account collection is an integration step, not a feature this renderer pretends to provide.

## When to use

- Build a `/usage`-style view for Codex, Claude, Grok or similar providers.
- Compare remaining account quota and reset times without opening several account pages.
- Generate one three-column Telegram photo and a static web version.
- Diagnose a font preview that looks better than the actual command output.

Not for estimating subscription quota from token counts, bypassing provider restrictions, managing payment settings, or accessing accounts the user has not authorized.

## Fast start

Run commands from this skill's directory. Its `scripts/` folder is self-contained; no Proxmox, Grafana, Docker or root access is required.

```bash
uv venv .venv
uv pip install --python .venv/bin/python Pillow
.venv/bin/python scripts/render.py --input scripts/demo.json --output out
.venv/bin/python -m unittest discover -s scripts/tests -p 'test_*.py'
python3 -m http.server 8080 --directory out
```

Open `out/index.html` or serve the output directory. Inspect the generated photo at its actual pixel size and at phone scale. Demo values are illustrative, never evidence about an account. IBM Plex Sans assets and their OFL license are bundled with the script.

Read `scripts/README.md` and the complete `scripts/demo.json` before writing an adapter. They define the executable schema and file names. Never guess provider field names from a screenshot.

## Assembly workflow

### 1. Discover authorized sources

Ask only for genuinely missing access. First inspect the user's existing quota collectors, account UI, approved exports or local integration. Use documented interfaces where available. Keep provider authentication in the collector, outside the public renderer and skill package.

Record which account and quota window each observation belongs to. Internal account fingerprints may be useful in a private collector, but do not publish them. Do not mix observations from two accounts just to fill a chart.

### 2. Produce a normalized snapshot

Use the bundled JSON example as the exact input contract. Preserve these semantics:

- Observation time and display timezone are explicit.
- Each provider has separate, meaningful quota windows.
- Remaining quota is a finite percentage between 0 and 100.
- Reset timestamps and manual credit expiry dates come from the source.
- History contains observed timestamps, remaining percentages and cycle identity where available.
- Token totals have an explicit scope and comparison period.
- Missing fields stay unavailable; they are not silently zero.
- Public examples carry the demo marker, which must remain visible in the image and web page.

Never ship raw session databases, cookies, auth JSON, emails, private hostnames, internal IPs or conversation transcripts in a public bundle. Prefer an allowlist export over removing a few known sensitive keys.

### 3. Render one typography system

Use IBM Plex Sans for account cards, chart titles, tick labels and legends. Match actual weights and ink bounds, not just a CSS family string. Keep three columns with consistent spacing: Codex blue, Claude orange, Grok charcoal.

The standalone starter draws at the final three-column photo size, 2560 by 864 pixels, without resizing already-rasterized text afterward. This is a design target, not a universal Telegram limit. Keep a lossless image when useful for the web view. Let the renderer choose a readable fallback for unavailable values instead of making a number up.

Do not independently recreate the same headline in browser CSS, Matplotlib and Pillow. If integrating into a legacy multi-engine system, export actual tick labels/positions from the source chart and render typography consistently without altering plotted data.

### 4. Preserve honest history

Clamp the rolling seven-day view to the first trustworthy observation. Keep weekly and five-hour windows separate. Break lines at substantial sample gaps and quota-cycle changes. A reset is not continuous consumption, and an outage is not an observed flat line.

Do not use token counts to infer account quota. Input, output and cache accounting varies by source; disclose inclusion rules. When the preceding seven-day token total is zero or missing, do not print a fabricated percentage delta. A local daily average is not an account billing statistic.

### 5. Integrate the actual command

Use a narrow adapter: collect, validate, render into an immutable run directory, return the final photo path. Native Bot API delivery uses `sendPhoto`, not `sendDocument`, unless the user explicitly wants a file. Preserve the exact chat and topic. Do not put bot tokens in command arguments, generated HTML or public logs.

Test the command handler's final media envelope. Testing only an intermediate `build_report()` or sending a manually prepared JPEG does not prove `/usage` returns that file. The website and command may have different final preparation stages even when they share source images.

In a resident bot, changing a hook file is not activation. Use its supported reload/restart mechanism and verify the new process loaded the intended hook. Do not kill an unrelated gateway or start a second poller with the same token. Follow the `hermes-agent` skill and current official docs when integrating Hermes.

### 6. Verify the delivered artifact

1. Run the bundled tests, then render a demo.
2. Render an authorized private snapshot and inspect all three providers.
3. Invoke the actual command handler and inspect the exact returned file.
4. Verify its format, dimensions, text bounds, labels and timestamps.
5. Send it to the authorized test chat/topic once. Preserve the returned message ID.
6. Read `sendPhoto`'s returned sizes, use `getFile` to download the largest representation, and inspect that delivered photo too.
7. Verify the web route, image assets and any manifest by reading them back.

Telegram compression and available sizes can vary. Do not declare quality fixed just because the source PNG is sharp or the upload succeeded. Compare the reference and delivered images at the same scale. Treat user reports of worse quality as a failed acceptance check.

## Operational boundaries

- Never automatically reset quotas, spend credits, buy a plan or change billing.
- Read-only collectors should be bounded and retried conservatively.
- Fail closed on malformed or non-finite quota values.
- A collection failure must not be disguised as a fresh successful snapshot.
- Multiple requests must not overwrite media paths during upload.
- Publish assets before atomically switching a web manifest.
- Only notify about meaningful changes; don't create recurring alerts without a requested cadence.

## Deliverables

A completed installation has a working English photo, matching web view, validated private input adapter, an actual command-path check, and a Telegram readback receipt where delivery is authorized. Report live collection separately from a demo-only renderer. Link the deployed page or pull request, and state any remaining access or deployment blocker plainly.
