Files
UsageKiosk/README.md
T
nessi 58eab24258 chore: initial project setup with AI usage dashboard for Codex and Devin
Add FastAPI-based monitoring dashboard with support for Codex CLI and Devin API v3 Enterprise consumption tracking. Includes Docker deployment, systemd service configuration, kiosk mode launcher, comprehensive documentation, and demo mode for testing without credentials.
2026-09-18 11:34:33 +02:00

207 lines
10 KiB
Markdown

# AI Usage Dashboard
AI Usage Dashboard is a small FastAPI monitoring kiosk for the official local Codex app-server mechanism and Devin API v3 Enterprise consumption endpoints. Provider refreshes are independent: an unavailable provider never hides a successful result from the other provider.
## Requirements
- Python 3.12
- Codex CLI installed and authenticated locally when Codex data is enabled
- Devin API v3 Enterprise access and a service user with `ViewAccountConsumption` when Devin data is enabled
- Chromium/Chrome only for the optional kiosk launcher
- Docker and Compose are optional
## Install and run directly
Unix:
```sh
python3.12 -m venv .venv
. .venv/bin/activate
python -m pip install -r requirements.txt
cp .env.example .env
python -m app.main
```
`python -m app.main` is the simplest direct command and honors `HOST` and `PORT` from `.env`. The explicit Uvicorn alternative is:
```sh
python -m uvicorn app.main:app --host 0.0.0.0 --port 8080
```
Windows PowerShell:
```powershell
py -3.12 -m venv .venv
.\.venv\Scripts\Activate.ps1
python -m pip install -r requirements.txt
Copy-Item .env.example .env
python -m app.main
```
`python -m app.main` honors `HOST` and `PORT` from `.env`. The explicit Uvicorn alternative is:
```powershell
python -m uvicorn app.main:app --host 0.0.0.0 --port 8080
```
Open `http://localhost:8080`. The application also runs in deterministic demo mode with `DEMO_MODE=true` and no provider credentials.
## Configuration
Copy `.env.example` to `.env`. Unknown environment variables are ignored and names are case-insensitive.
| Variable | Default | Meaning |
| --- | --- | --- |
| `HOST` | `0.0.0.0` | Bind host when using a launcher that reads settings |
| `PORT` | `8080` | Application port |
| `CODEX_ENABLED` | `true` | Enable the local Codex provider |
| `CODEX_COMMAND` | `codex` | One executable path; it is never shell-split |
| `CODEX_TIMEOUT` | `8` | Codex request timeout in seconds |
| `DEVIN_ENABLED` | `true` | Enable Devin |
| `DEVIN_API_KEY` | empty | Devin Bearer token, kept as a secret value |
| `DEVIN_API_URL` | `https://api.devin.ai` | Devin API base URL |
| `DEVIN_ORG_ID` | empty | Organization scope, used when no user scope is configured |
| `DEVIN_USER_ID` | empty | User scope, takes precedence over organization |
| `DEVIN_TIMEOUT` | `8` | Devin HTTP timeout in seconds |
| `PROVIDER_CACHE_TTL` | `8` | Successful provider cache duration in seconds |
| `REFRESH_INTERVAL` | `10` | Browser refresh period in seconds |
| `TIMEZONE` | `Europe/Vienna` | ZoneInfo name used for reset timestamps |
| `DEMO_MODE` | `false` | Use deterministic local demo values for both cards |
## Provider mechanisms
### Codex
The dashboard invokes the official local CLI exactly as `codex app-server --listen stdio://`, without a shell. It sends the official line-delimited `initialize`, `initialized`, and `account/rateLimits/read` messages and parses primary and secondary windows, credits, and individual limits. Install and log in to the official CLI first:
```sh
codex login
```
The app-server interface is official, but is documented as a development/debug interface and may change. If the executable is not on the service `PATH`, configure `CODEX_COMMAND` with its absolute path. ChatGPT plan rate limits are distinct from OpenAI API billing. `CODEX_API_KEY` is intentionally not used or included because an API key does not expose ChatGPT Codex plan limits.
### Devin
The provider uses the official Devin API v3 Enterprise consumption endpoints. It selects the active cycle from `/v3/enterprise/consumption/cycles`, then requests daily consumption using user scope, organization scope, or the account endpoint in that precedence order. User and organization IDs are URL-quoted. Organization usage additionally attempts the organization max cycle ACU limit; an unavailable optional organization-details request does not discard consumption.
Devin v3 consumption is Enterprise-only and requires a service user with `ViewAccountConsumption`. Reading the organization max limit may require extra organization-read permissions. Self-serve has no supported machine-readable provider endpoint; use demo mode until Devin publishes an official endpoint. Do not claim real-time freshness beyond vendor behavior: the dashboard cache and refresh schedule only control when it asks the vendors again.
## Security and behavior
- The Devin Bearer token is read from environment settings. Codex uses the official CLI's local credential store instead of an API key setting. Neither credential reaches the browser, and neither is returned, logged, or included in error messages.
- Upstream error bodies and process stderr are discarded; users see only a generic provider error.
- Provider calls have independent timeouts and successful results use a monotonic TTL cache. Only successful results are cached.
- The browser makes one `/api/usage` request immediately and repeats using the response refresh interval. A network failure keeps the previous cards visible and marks the header connection state.
- `GET /health` returns exactly `{"status":"ok"}`.
- `GET /api/usage` returns normalized Codex and Devin usage, `server_time`, `demo_mode`, and `refresh_interval`; it sends `Cache-Control: no-store`.
- `GET /` serves the dashboard HTML with `Cache-Control: no-store`. Static assets are under `/static`.
## Docker Compose
Create `.env`, then build and run:
```sh
cp .env.example .env
docker compose up --build -d
curl http://localhost:8080/health
```
The container uses Python 3.12 slim, a non-root user, read-only root storage, a `/tmp` tmpfs, `no-new-privileges`, and a healthcheck. `docker-compose.yml` uses `restart: unless-stopped` and maps port 8080. The image does not bundle the Codex CLI or a personal Codex credential store: Docker works out of the box for demo mode and Devin, while real Codex should normally run directly on the authenticated host. An operator may deliberately build and securely provision the official CLI and credential store, but credentials should never be baked into an image.
## Kiosk mode
`start-kiosk.sh` accepts `DASHBOARD_URL` (default `http://localhost:8080`) and searches in this order: `chromium`, `chromium-browser`, `google-chrome`, `google-chrome-stable`. It launches the first available browser with:
```text
--kiosk --noerrdialogs --disable-infobars --disable-session-crashed-bubble
```
The script exits clearly if no supported browser is installed.
### systemd dashboard service
Install the project at `/opt/usage-kiosk`, create `/opt/usage-kiosk/.env`, and adjust `User` if necessary:
```ini
[Unit]
Description=AI Usage Dashboard
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=usagekiosk
Group=usagekiosk
WorkingDirectory=/opt/usage-kiosk
EnvironmentFile=/opt/usage-kiosk/.env
ExecStart=/opt/usage-kiosk/.venv/bin/uvicorn app.main:app --host 0.0.0.0 --port 8080
Restart=on-failure
RestartSec=5
NoNewPrivileges=true
PrivateTmp=true
[Install]
WantedBy=multi-user.target
```
Save as `/etc/systemd/system/usage-kiosk.service`, then run `sudo systemctl daemon-reload`, `sudo systemctl enable --now usage-kiosk`, and inspect `sudo journalctl -u usage-kiosk` if needed.
### systemd browser launch
For systems where the display manager and X authority are ready before the browser unit starts, save this as `/etc/systemd/system/usage-kiosk-browser.service`:
```ini
[Unit]
Description=AI Usage Dashboard Chromium Kiosk
After=display-manager.service usage-kiosk.service
Requires=usage-kiosk.service
[Service]
Type=simple
User=usagekiosk
WorkingDirectory=/opt/usage-kiosk
Environment=DISPLAY=:0
Environment=XAUTHORITY=/home/usagekiosk/.Xauthority
ExecStart=/opt/usage-kiosk/start-kiosk.sh
Restart=on-failure
RestartSec=5
[Install]
WantedBy=graphical.target
```
Enable it with `sudo systemctl daemon-reload` and `sudo systemctl enable --now usage-kiosk-browser`. The `DISPLAY`, `XAUTHORITY`, user home, and display-manager readiness vary by distribution and graphical setup; a desktop-session autostart entry is preferable when systemd starts too early for the X session.
### Kiosk X session/autostart
For a dedicated graphical user, install the launcher at `/opt/usage-kiosk/start-kiosk.sh`, make it executable, and use an X session autostart entry such as `~/.config/autostart/usage-kiosk.desktop`:
```ini
[Desktop Entry]
Type=Application
Name=Usage Dashboard Kiosk
Exec=/opt/usage-kiosk/start-kiosk.sh
Terminal=false
X-GNOME-Autostart-enabled=true
```
A window-manager session may instead run `/opt/usage-kiosk/start-kiosk.sh` from its session startup. Configure automatic graphical login for the kiosk user only on a physically secured display. Boot ordering is: network, `usage-kiosk.service`, graphical session, then Chromium kiosk. Keep the dashboard service bound to the required network interfaces and use a firewall or reverse proxy when it is not on a trusted display network.
## Troubleshooting
- **Codex error:** verify `codex` is installed, `codex login` completed, and `CODEX_COMMAND` points to one executable. Run the CLI manually outside the dashboard to confirm its availability.
- **Devin error:** verify Enterprise entitlement, service-user permissions, token scope, API URL, and configured user or organization scope. A 401/403/404 on optional organization metadata only removes the max-limit fields.
- **No Devin limit:** the consumption endpoint remains valid, but no numeric limit is invented when the organization max limit is unavailable.
- **Connection error in the header:** the browser could not complete the dashboard request; existing provider rows remain on screen and retries continue on the configured interval.
- **Stale-looking values:** successful provider results are cached for `PROVIDER_CACHE_TTL`; vendor processing and reporting delays are outside this dashboard's control.
- **Container health failure:** inspect `docker compose logs dashboard`, verify `.env`, and test `http://localhost:8080/health`.
- **Kiosk failure:** confirm one of the four supported Chromium executable names is on `PATH`, then check `DASHBOARD_URL` and the graphical session environment.
## Development checks
```sh
python -m pytest -q
python -m compileall app
docker compose config
```