From 2e35fdf9ba21d21a0b01636cc0a0847ff48c0992 Mon Sep 17 00:00:00 2001 From: Aikins Laryea Date: Mon, 19 Oct 2026 02:31:13 +0000 Subject: [PATCH] docs: rewrite the readme with agent data paths and privacy details The README said nothing leaves the machine, but the tool downloads the models.dev price catalog. It now names that request and states that usage data stays local, warns that the dashboard has no authentication when bound beyond localhost, and lists where each agent's data is read from, with environment overrides. --- README.md | 248 +++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 209 insertions(+), 39 deletions(-) diff --git a/README.md b/README.md index f524a30..61efb3b 100644 --- a/README.md +++ b/README.md @@ -1,57 +1,227 @@ -# @telemetry-dev/stats +# Telemetry Stats -Local web dashboard for token usage across AI coding agents. One command -scans the agent logs on your machine, stores normalized usage in a local -SQLite database, and opens a dashboard in your browser. Nothing leaves your -machine. +Telemetry Stats is a local dashboard for token usage and cost across AI coding +agents. It reads the usage logs and databases that about 50 agents already write +on your machine, stores the usage numbers in a local SQLite database, and shows +them in your browser. ![The Telemetry Stats dashboard](https://raw.githubusercontent.com/telemetry-dev/stats/main/.github/preview.png) -## Usage +## Quick start + +You need Node.js 22 or newer. + +```bash +npx @telemetry-dev/stats +``` + +This syncs your agent logs, starts the dashboard at http://127.0.0.1:3847, and +opens it in your browser. To install the command globally instead: ```bash -npx @telemetry-dev/stats # sync, start the dashboard, open browser -npx @telemetry-dev/stats --json # sync and print stats as JSON -npx @telemetry-dev/stats --sync # sync and print a summary +npm i -g @telemetry-dev/stats +telemetry-stats ``` -Options: `--port ` (default 3847), `--host ` (default 127.0.0.1), -`--no-open`, `--help`. A port held by another Telemetry Stats dashboard is -reused; a port held by anything else is skipped for the next free one. +You can also skip the dashboard and print results in the terminal: + +```bash +telemetry-stats --sync # sync, then print a summary +telemetry-stats --json # sync, then print totals and per-agent and per-model breakdowns as JSON +``` + +## Options + +| Option | Default | Description | +| ------------------ | ----------- | ----------------------------------- | +| `-p`, `--port ` | `3847` | Port for the dashboard server | +| `--host ` | `127.0.0.1` | Address to bind | +| `--no-open` | | Don't open the browser | +| `-s`, `--sync` | | Sync, print a summary, and exit | +| `-j`, `--json` | | Sync, print stats as JSON, and exit | +| `-h`, `--help` | | Show help | + +If the port is already used by another Telemetry Stats dashboard, the command +reuses that dashboard. If something else holds the port, it tries the next free +port. + +> [!WARNING] +> The dashboard and its API have no authentication. With the default host only +> your own machine can reach it. `--host 0.0.0.0` exposes your usage data to +> your network, along with the `/api/settings` and `/api/sync` endpoints, which +> let anyone who can reach the port change settings and start syncs. + +## How syncing works + +Starting the dashboard runs a sync in the background, and the **Sync** button +runs another. Syncs are incremental: files that haven't changed since the last +sync are skipped, and log files that only grew are read from where the last sync +stopped. Open pages refresh their data every 30 seconds, and within a few +seconds of a sync finishing. + +The **Settings** page shows each agent's collection status and lets you set: + +- **Extra directories** to scan, for agent data stored outside the default + locations. Each must be an existing absolute path. Not every agent checks + them, and each agent expects its own folder layout inside them; see its + adapter in `src/lib/agents/adapters/`. +- **Timezone** used for daily totals. It defaults to your system timezone on the + first run. ## Supported agents -Claude Code, Codex CLI (incl. Sakana Fugu), Gemini CLI, OpenCode, Amp, Droid, -Pi, Oh My Pi, Qwen CLI, Kimi, Mux, Grok Build, OpenClaw, Prime Agent, Senpi, -Kimchi, Reasonix, Gajae-Code, Jcode, Junie, Augment Code, OpenCodeReview, -CodeBuddy, WorkBuddy, Cherry Studio, Command Code, DeepSeek Harness, Codebuff, -Freebuff, Hermes, Kilo CLI, Goose, Zed, Kiro, Antigravity CLI, MiMo Code, -ZCode, Devin CLI, Devin Desktop, Octofriend (incl. Synthetic), Crush, Cline, -GitHub Copilot CLI, Roo Code, Kilo, and — via local usage caches — Cursor, -Google Antigravity, Trae, Warp/Oz, and MiniMax Code. - -## Data and privacy - -- Only usage metadata is stored: agent, model, provider, session id, project - path, timestamps, token counts, and costs. Never prompts, completions, tool - arguments, or credentials. -- Data lives in the platform data directory (`~/Library/Application - Support/telemetry-stats` on macOS, `$XDG_DATA_HOME/telemetry-stats` on - Linux, `%LOCALAPPDATA%\telemetry-stats` on Windows). -- Costs recorded by an agent are kept as reported. Other events are priced - from a locally cached [models.dev](https://models.dev) catalog and marked - "estimated": exact provider+model first, then an unambiguous model id, then - the rate tuple a strict plurality of providers agree on for that model id - (aggregators republish delisted first-party models). Unresolved models stay - "unpriced" and are never folded into a total silently. +Telemetry Stats reads data that each agent writes locally. It doesn't need an +account or API key of its own. + +Five agents don't write usage records that Telemetry Stats can read directly. +For these it reads files under `~/.config/tokscale` (or +`$XDG_CONFIG_HOME/tokscale`), the folder of the separate +[tokscale](https://github.com/junhoyeo/tokscale) tool: the caches that tokscale +syncs for Cursor IDE, Google Antigravity, Trae, and Warp / Oz, and MiniMax Code +headless output saved in tokscale's `headless/mcode` folder. Until those files +exist, the Settings page lists these agents as "sync required". + +
+Where each agent's data is read from + +`` means `$XDG_DATA_HOME` if set, otherwise `~/.local/share`, on every +platform. `**` means any number of subdirectories. + +| Agent | Reads | Env override | +| ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------- | +| Claude Code | `~/.claude/projects/**/*.jsonl`, `~/.claude/transcripts/**/*.jsonl` | | +| Codex CLI | `~/.codex/sessions/**/*.jsonl`, `~/.codex/archived_sessions/**/*.jsonl`. Sakana Fugu usage in these sessions is shown as its own agent. | `CODEX_HOME` | +| Gemini CLI | `~/.gemini/tmp/**/chats/**/*.json` | `GEMINI_CLI_HOME` | +| OpenCode | `/opencode/**/opencode.db` and `opencode-*.db`, `/opencode/storage/message/**/*.json` | `XDG_DATA_HOME` | +| Amp | `/amp/threads/**/*.json` | `XDG_DATA_HOME` | +| Droid | `~/.factory/sessions/**/*.settings.json` | | +| Pi | `~/.pi/agent/sessions/**/*.jsonl` | | +| Oh My Pi | `~/.omp/agent/sessions/**/*.jsonl` | | +| Qwen CLI | `~/.qwen/projects/**/*.jsonl` | | +| Kimi | `~/.kimi/sessions/**/*.jsonl`, `~/.kimi-code/sessions/**/*.jsonl` | `KIMI_CODE_HOME` (replaces `~/.kimi-code`) | +| Mux | `~/.mux/sessions/**/*.jsonl` | | +| Grok Build | `~/.grok/sessions/**/updates.jsonl` | `GROK_HOME` | +| OpenClaw | `~/.openclaw/agents/**/*.jsonl`, plus the same under `~/.clawdbot`, `~/.moltbot`, and `~/.moldbot` | | +| Prime Agent | `~/.prime/agent/sessions/**/*.jsonl`, `~/.prime/agent/session-artifacts/**/*.jsonl` | | +| Senpi | `~/.senpi/agent/sessions/**/*.jsonl` | `SENPI_CODING_AGENT_DIR` | +| Kimchi Coding | `~/.config/kimchi/harness/sessions/**/*.jsonl` | `KIMCHI_CODING_AGENT_DIR` | +| Reasonix | `~/.reasonix/stats/**/*.jsonl` | `REASONIX_STATE_HOME`, then `REASONIX_HOME` | +| Gajae-Code | `~/.gjc/agent/sessions/**/*.jsonl`, plus `$XDG_DATA_HOME/gjc/sessions` if `XDG_DATA_HOME` is set | `GJC_CODING_AGENT_DIR`; `GJC_CONFIG_DIR` or `PI_CONFIG_DIR` | +| Jcode | `~/.jcode/sessions/**/session_*.json`, `session_*.journal.jsonl` | `JCODE_HOME` | +| Junie | `~/.junie/sessions/**/events.jsonl` | | +| Augment Code | `~/.augment/sessions/**/*.json` | | +| OpenCodeReview | `~/.opencodereview/sessions/**/*.jsonl` | | +| CodeBuddy | `~/.codebuddy/projects/**/*.jsonl` | | +| WorkBuddy | `~/.workbuddy/projects/**/*.jsonl` | | +| Cherry Studio | `CherryStudio/Data/Agents/.claude/projects/**/*.jsonl` and `CherryStudio/.claude/projects/**/*.jsonl` in `~/Library/Application Support` (macOS), `%APPDATA%` (Windows), or `~/.config` | `XDG_CONFIG_HOME` (Linux) | +| Command Code | `~/.commandcode/projects/**/*.jsonl` | | +| DeepSeek Harness | `~/.dsh/sessions/**/session.jsonl` and `session.jsonl.zstd` | `DSH_HOME` | +| Codebuff | `~/.config/manicode/**/chat-messages.json`, plus `manicode-dev` and `manicode-staging` | `CODEBUFF_DATA_DIR` | +| Freebuff | Same files as Codebuff | `FREEBUFF_DATA_DIR` | +| Hermes Agent | `~/.hermes/state.db`, `~/.hermes/profiles/*/state.db` | `HERMES_HOME` | +| Kilo CLI | `/kilo/kilo.db` | `XDG_DATA_HOME` | +| Goose | `~/.local/share/goose/sessions/sessions.db` and `~/Library/Application Support/goose/sessions/sessions.db`, plus the older `Block/goose` versions of both | `GOOSE_PATH_ROOT` | +| Zed Agent | `~/Library/Application Support/Zed/threads/threads.db` (macOS), `/zed/threads/threads.db` (Linux), `%LOCALAPPDATA%\Zed\threads\threads.db` (Windows) | | +| Kiro | `~/.kiro/sessions/cli/**/*.json` and `*.jsonl`, `kiro-cli/data.sqlite3`, `Kiro/User/globalStorage/kiro.kiroagent/data.sqlite3` (both databases in the same per-platform folder as Zed Agent) | | +| Antigravity CLI | `~/.gemini/antigravity-cli/conversations/**/*.db` | `GEMINI_CLI_HOME` | +| MiMo Code | `/mimocode/mimocode.db` | `XDG_DATA_HOME` | +| ZCode | `~/.zcode/cli/db/db.sqlite`, `~/.zcode/projects/**/*.jsonl` | | +| Devin CLI | `/devin/cli/sessions.db` | `XDG_DATA_HOME` | +| Devin Desktop | `Devin/User/acp-events/**/*.json` and `*.jsonl` in `~/Library/Application Support` (macOS), `%APPDATA%` (Windows), or `~/.config` | `XDG_CONFIG_HOME` (Linux) | +| Octofriend | `/octofriend/sqlite.db`. Usage through Synthetic is shown as its own agent. | `XDG_DATA_HOME` | +| Crush | `/crush/projects.json`, then the `crush.db` of each project it lists | `XDG_DATA_HOME` | +| Cline | `Code/User/globalStorage/saoudrizwan.claude-dev/tasks/**/ui_messages.json` like Roo Code, and CLI sessions in `~/.cline/data/sessions/**/*.messages.json` | `CLINE_SESSION_DATA_DIR`, `CLINE_DATA_DIR`, `CLINE_DIR` | +| GitHub Copilot CLI | `~/.copilot/otel/**/*.jsonl` | `COPILOT_OTEL_FILE_EXPORTER_PATH` (read in addition) | +| Roo Code | `Code/User/globalStorage/rooveterinaryinc.roo-cline/tasks/**/ui_messages.json` in the VS Code config folder, and the same under `~/.vscode-server/data` | `XDG_CONFIG_HOME` (Linux) | +| Kilo | Same as Roo Code, with the `kilocode.kilo-code` extension folder | `XDG_CONFIG_HOME` (Linux) | +| Cursor IDE | `~/.config/tokscale/cursor-cache/usage*.csv` (tokscale cache) | `XDG_CONFIG_HOME` | +| Google Antigravity | `~/.config/tokscale/antigravity-cache/sessions/**/*.jsonl` (tokscale cache) | `XDG_CONFIG_HOME` | +| Trae | `~/.config/tokscale/trae-cache/sessions/**/*.json` (tokscale cache) | `XDG_CONFIG_HOME` | +| Warp / Oz | `~/.config/tokscale/warp-cache/usage.json` (tokscale cache) | `XDG_CONFIG_HOME` | +| MiniMax Code | `~/.config/tokscale/headless/mcode/**/*.jsonl` (MiniMax Code headless output saved in tokscale's headless folder) | `TOKSCALE_HEADLESS_DIR` (reads its `mcode` folder) | + +
+ +## Privacy and data + +Your usage data never leaves your machine unless you share it yourself (see +[Share your stats](#share-your-stats)). + +### What's stored + +For each request, Telemetry Stats stores usage metadata only: agent, model, +provider, session ID, project path, timestamp, duration, token counts, cost, and +the path of the log file it came from. It also keeps per-file sync bookkeeping +(file size, modification time, a content hash, and how far it has read) and your +settings. It never stores prompts, responses, tool arguments, or credentials. + +### Where it's stored + +Everything is in one directory: + +| Platform | Directory | +| -------- | --------------------------------------------------------------------- | +| macOS | `~/Library/Application Support/telemetry-stats` | +| Linux | `$XDG_DATA_HOME/telemetry-stats`, or `~/.local/share/telemetry-stats` | +| Windows | `%LOCALAPPDATA%\telemetry-stats` | + +Set `TELEMETRY_STATS_DATA_DIR` to use a different directory. It contains: + +- `stats.db`: the SQLite database with usage, sync state, and settings +- `models-dev.json`: the cached price catalog +- `sync.lock`: present while a sync is running + +To reset everything, stop the dashboard and delete the directory. The next run +rebuilds the database from whatever agent logs are still on disk. + +### The one outbound request + +To price usage, Telemetry Stats downloads the public price catalog from +[models.dev](https://models.dev) with a GET to `https://models.dev/api.json`. +It reuses the cached copy for an hour before downloading again. If the download +fails, costs use the last cached copy, or show as unpriced if there's no cache +yet. The request sends no usage data. + +### How costs are calculated + +Costs recorded by an agent are kept as reported. Other requests are priced from +the models.dev catalog and marked "estimated". The lookup tries the exact +provider and model first, then the model ID alone if it's unambiguous, then the +rates that a strict plurality of providers agree on for that model ID (model +aggregators often keep listing models that the original provider has dropped). +Models that still don't match stay "unpriced" and are never silently added to a +total. + +## Share your stats + +The **Share stats** button on the overview page draws a card with your usage for +the selected period. The image is built in your browser and never uploaded. +You can download it as a PNG, or use **Post on X**, which opens an X draft with +a caption containing your total token count and downloads the image for you to +attach. Nothing is posted until you post it. Project names, session names, and +filter values are never included. + +![Example share card](https://raw.githubusercontent.com/telemetry-dev/stats/main/.github/usage-card.png) ## Development ```bash npm install -npm run dev # vite dev server -npm test # vitest -npm run typecheck -npm run build # nitro server + client + headless CLI to .output/ +npm run dev # dev server on http://localhost:3000 +npm test # vitest +npm run typecheck # tsc +npm run lint # eslint +npm run check # prettier check (JS and TS files) +npm run format # prettier write (JS and TS files) +npm run build # server, client, and headless CLI into .output/ node bin/telemetry-stats.mjs --no-open ``` + +## About + +Telemetry Stats is built by the [telemetry.dev](https://telemetry.dev) team. It +is a standalone tool and sends nothing to telemetry.dev. + +## License + +[MIT](https://github.com/telemetry-dev/stats/blob/main/LICENSE)