- TypeScript 88.3%
- CSS 11.3%
- HTML 0.4%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| .dev/wip | ||
| server | ||
| src | ||
| .env.example | ||
| .gitignore | ||
| index.html | ||
| package.json | ||
| pnpm-lock.yaml | ||
| README.md | ||
| tsconfig.json | ||
| vite.config.ts | ||
pi usage atlas
A local historical usage dashboard built directly from canonical Pi session JSONL. It keeps a disposable, metrics-only SQLite cache so opening the page is incremental rather than a full archive scan.
Run and use
Requirements: Node.js 24+ (for node:sqlite) and the repository's existing Vite+ toolchain.
vp install
vp dev
Open http://127.0.0.1:55173 (or the pinned http://pi-usage.localhost route). vp dev is the complete development app: Vite serves both the UI and the loopback-only API in one process. The dashboard reads its metrics-only cache immediately, then indexes canonical sessions in the background. Refresh data explicitly starts another index. Presets cover today, this week (Monday through today), the last 7 or 30 local-calendar days, the current month, and the last 90 local-calendar days; custom dates are inclusive. Every selected range is compared with the immediately preceding range of the same duration. Today supports 15-minute and hourly buckets; longer ranges use local calendar days.
For a built app:
vp build
vp run serve
# open http://127.0.0.1:4174
The production server binds only to loopback and accepts the exact local hosts localhost, 127.0.0.1, [::1], pi-usage.localhost, and ppc (with optional ports). Development uses the same API handler and Host, method, and input validation as production.
Data locations
Defaults:
source: ~/.pi/agent/sessions/**/*.jsonl
cache: ~/.cache/pi-usage-viz/metrics-v2.sqlite
Optional overrides:
| Variable | Purpose |
|---|---|
PI_SESSIONS_DIR |
Canonical Pi session tree to read |
PI_USAGE_CACHE_PATH |
Disposable derived SQLite cache |
PORT |
API/static server port; default 4174 |
The dashboard does not read or migrate ~/.pi/agent/data/usage.sqlite. Deleting the new cache is safe: the next page load shows the empty cached report while its background index reconstructs metrics from session JSONL.
Incremental and deletion behavior
The cache has explicit schema version 2. Each source file records its path, session ID, byte offset, observed size, mtime, and a SHA-256 hash of the indexed prefix. Unchanged files are skipped without reading their bodies. A growing file is appended only after its prior prefix hash matches; a rewritten prefix, same-size change, or truncation rebuilds only that session. Parsing and prefix validation happen before short SQLite write transactions. If a canonical session file disappears, its session, entry references, and response metrics are removed from the cache on the next scan. A schema-version mismatch discards and rebuilds this disposable cache rather than migrating it.
Refresh indexing runs in one worker thread so the local API remains responsive. Concurrent refresh requests join that worker. Progress uses the discovered session IDs plus invalid session files as work units: it starts at 0 after discovery, advances by completed units as monotonic integers capped at 99, and reaches 100 only after deletion and final bookkeeping. An empty tree reports 0 then 100.
Malformed or temporarily incomplete files do not replace previously valid metrics. Duplicate copies with the same Pi session ID are tried largest, then newest; a malformed preferred copy falls back to the prior valid copy without deleting its metrics. The footer reports freshness, indexed/appended/rebuilt/deleted/skipped/duplicate counts, and per-file errors.
Stored data categories
SQLite stores only:
- source bookkeeping: path, session ID, byte offset, size, mtime, indexed-prefix hash, indexed timestamp;
- session metadata: session ID, file path, working directory, start time, parent session path, format version, display name;
- narrow entry linkage: entry ID, parent ID, role, timestamp (needed to associate a response with its user turn);
- response facts: stable session/entry/turn IDs, timestamp, provider, model, stop reason, token lanes, and Pi's recorded cost lanes.
Prompt text, assistant text, thinking, tool arguments/results, and raw JSON payloads are parsed in memory and never persisted. The cache is still local metadata and may contain project and session paths.
Product semantics
The dashboard separates Estimated metered spend from Subscription equivalent activity. openai-codex responses are shown as subscription equivalent; other provider costs are shown as metered estimates. All cost values come from Pi's recorded model-cost estimates. They are useful for equivalent activity and comparison, not a claim of invoice or billing precision.
Views include current/previous headlines, time-series activity, provider/model/project breakdowns, token and equivalent-cost response histograms, cache read/write/input lanes and effectiveness, plus exact top response/turn/session outliers with copyable pi --session commands.
Validation
vp run test
vp check
vp build
The focused tests use temporary session trees and caches. They cover append/skip/rebuild/delete indexing, growing and same-size rewrites, malformed duplicate fallback, content-free persistence, local/DST ranges and buckets, per-bucket counts, cache/cost aggregation, shell-safe outlier resumes, latest-request cancellation, and production HTTP host/method/input guards.