|
| 1 | +# 💸 copilot-cost |
| 2 | + |
| 3 | +> **See your GitHub Copilot CLI tokens and estimated spend at a glance — right in your terminal.** |
| 4 | +
|
| 5 | +[](LICENSE) |
| 6 | +[](https://nodejs.org) |
| 7 | +[](https://opentelemetry.io) |
| 8 | +[](#-privacy-promise) |
| 9 | + |
| 10 | +`copilot-cost` is a **zero-config statusline + local dashboard** for the [GitHub Copilot CLI](https://docs.github.com/en/copilot/concepts/agents/copilot-cli/about-copilot-cli). It watches the OpenTelemetry traces the CLI already emits, and turns them into a real-time, model-aware view of your **token usage and estimated spend** — without ever leaving your machine. |
| 11 | + |
| 12 | +```text |
| 13 | +$0.2934 · 23.1k in / 6.1k out · 15.1k cache |
| 14 | +``` |
| 15 | + |
| 16 | + |
| 17 | + |
| 18 | +| ☀️ Light | 🌙 Dark | |
| 19 | +| :---: | :---: | |
| 20 | +|  |  | |
| 21 | + |
| 22 | +> _Screenshots are generated from synthetic demo telemetry in [`docs/demo-otel/synthetic-usage.jsonl`](docs/demo-otel/synthetic-usage.jsonl). Regenerate them with `npm run screenshots:demo` after cloning the repo._ |
| 23 | +
|
| 24 | +--- |
| 25 | + |
| 26 | +## ✨ Why copilot-cost? |
| 27 | + |
| 28 | +- **🔭 Zero-blindspot** — cost and tokens visible on every prompt, not at the end of the month. |
| 29 | +- **🔌 OpenTelemetry-native** — no monkey-patching, no proxy, no auth: it reads the **same JSONL trace files the Copilot CLI already writes**. |
| 30 | +- **🔒 100% local** — your usage data never leaves your machine. The dashboard binds only to `127.0.0.1`. |
| 31 | +- **🧠 Model-aware** — separate accounting for input / output / cached / reasoning tokens across every Copilot model. |
| 32 | +- **📈 Statusline + dashboard** — a glanceable one-liner *and* a deeper view when you want trends and breakdowns. |
| 33 | +- **🪶 Lightweight** — no runtime database, no daemon, no analytics. Just files and aggregation on demand. |
| 34 | + |
| 35 | +--- |
| 36 | + |
| 37 | +## 🚀 Quickstart |
| 38 | + |
| 39 | +> The package is not published to a registry yet. Install it locally from this repository. |
| 40 | +
|
| 41 | +```bash |
| 42 | +git clone https://github.com/devartifex/copilot-cost.git |
| 43 | +cd copilot-cost |
| 44 | +npm install |
| 45 | +npm run build |
| 46 | +npm link # makes the `copilot-cost` command available on your PATH |
| 47 | +copilot-cost install |
| 48 | +``` |
| 49 | + |
| 50 | +Then **restart your shell and restart `copilot`**. That's it. |
| 51 | + |
| 52 | +The installer: |
| 53 | + |
| 54 | +- 🔧 Configures the Copilot CLI statusline command |
| 55 | +- 📝 Appends an idempotent OpenTelemetry block to your shell profile |
| 56 | +- 📁 Enables local JSONL span output under `~/.copilot/otel/` |
| 57 | +- 🚫 Does **not** start the dashboard (run it on demand) |
| 58 | + |
| 59 | +Prefer to edit your shell profile yourself? |
| 60 | + |
| 61 | +```bash |
| 62 | +copilot-cost install --no-otel-profile |
| 63 | +``` |
| 64 | + |
| 65 | +That still installs the statusline and prints the OpenTelemetry block for manual setup. |
| 66 | + |
| 67 | +Verify setup any time: |
| 68 | + |
| 69 | +```bash |
| 70 | +copilot-cost doctor |
| 71 | +``` |
| 72 | + |
| 73 | +--- |
| 74 | + |
| 75 | +## 🧬 How it works |
| 76 | + |
| 77 | +The Copilot CLI has built-in **[OpenTelemetry](https://opentelemetry.io)** instrumentation. When you set a few env vars, every prompt, tool call, and model response is recorded as a trace span — including token counts and the model that handled it. |
| 78 | + |
| 79 | +`copilot-cost` plugs into that stream end-to-end: |
| 80 | + |
| 81 | +``` |
| 82 | +┌────────────────────┐ OTel spans (JSONL) ┌──────────────────────┐ |
| 83 | +│ GitHub Copilot CLI│ ───────────────────────▶ │ ~/.copilot/otel/ │ |
| 84 | +└────────────────────┘ COPILOT_OTEL_*=… │ copilot-otel.jsonl │ |
| 85 | + └──────────┬───────────┘ |
| 86 | + │ tail + parse |
| 87 | + ┌────────────────────────────┴──────────────┐ |
| 88 | + ▼ ▼ |
| 89 | + ┌────────────────────────┐ ┌───────────────────────────┐ |
| 90 | + │ statusline (render) │ │ dashboard (local web UI) │ |
| 91 | + │ • $cost · in/out/cache│ │ • lifetime / today / week│ |
| 92 | + │ • compact / full / std│ │ • per-model & per-session│ |
| 93 | + └───────────┬────────────┘ │ • CSV export │ |
| 94 | + │ └───────────────────────────┘ |
| 95 | + ▼ |
| 96 | + Copilot CLI status bar |
| 97 | +``` |
| 98 | + |
| 99 | +1. **Capture** — the installer adds three env vars to your shell profile so the Copilot CLI writes spans to a JSONL file: |
| 100 | + ```bash |
| 101 | + export COPILOT_OTEL_ENABLED=true |
| 102 | + export COPILOT_OTEL_EXPORTER_TYPE=file |
| 103 | + export COPILOT_OTEL_FILE_EXPORTER_PATH="$HOME/.copilot/otel/copilot-otel.jsonl" |
| 104 | + ``` |
| 105 | +2. **Aggregate** — on every render the tool reads recent spans, extracts `gen_ai.usage.*` token counters and the `gen_ai.request.model`, and rolls them up by session / model / day. |
| 106 | +3. **Price** — token counts are multiplied by a bundled pricing snapshot (refreshable from [GitHub Docs: Copilot models & pricing](https://docs.github.com/en/copilot/reference/copilot-billing/models-and-pricing)) to produce an **estimated** USD cost. |
| 107 | +4. **Render** — a one-line statusline for the Copilot CLI status bar, plus an optional local web dashboard for trends. |
| 108 | + |
| 109 | +> 📚 More on the underlying telemetry pipeline: [GitHub Docs — OpenTelemetry observability for Copilot](https://docs.github.com/en/copilot/how-tos/copilot-sdk/observability/opentelemetry). |
| 110 | +
|
| 111 | +### 🤔 Why OpenTelemetry (and not API scraping)? |
| 112 | + |
| 113 | +- **It's the official, supported integration point.** GitHub designed the Copilot CLI to emit OTel spans. We don't intercept HTTP, wrap the binary, or parse logs. |
| 114 | +- **It's vendor-neutral.** The same JSONL files you give to `copilot-cost` can be sent to Jaeger, Honeycomb, Grafana Tempo, or any OTel collector. |
| 115 | +- **It's complete.** Every span carries token counters, model names, timing, and request IDs — exactly what's needed for accurate cost accounting. |
| 116 | +- **It's local by default.** The `file` exporter writes to disk on your machine; nothing has to leave the box. |
| 117 | + |
| 118 | +--- |
| 119 | + |
| 120 | +## 🎨 Statusline styles |
| 121 | + |
| 122 | +Choose how much detail the statusline shows with `COPILOT_COST_FORMAT`: |
| 123 | + |
| 124 | +| Format | Aliases | Example | |
| 125 | +| --- | --- | --- | |
| 126 | +| `standard` | _default_ | `$0.2934 · 23.1k in / 6.1k out · 15.1k cache` | |
| 127 | +| `compact` | `minimal` | `$0.2934` | |
| 128 | +| `full` | `verbose` | `$0.2934 (29.34 cr) · 23.1k fresh / 12.0k cache↻ / 3.1k cache✎ / 6.1k out · Σ 44.3k · 900 reason` | |
| 129 | + |
| 130 | +Set it next to the OpenTelemetry block in your shell profile: |
| 131 | + |
| 132 | +```bash |
| 133 | +export COPILOT_COST_FORMAT=compact |
| 134 | +``` |
| 135 | + |
| 136 | +You can also set `COPILOT_COST_NO_COLOR=1` (or `NO_COLOR=1`) for plain output, and `COPILOT_COST_COLOR=<ansi-code>` to change the statusline color. |
| 137 | + |
| 138 | +--- |
| 139 | + |
| 140 | +## 📊 Dashboard |
| 141 | + |
| 142 | +```bash |
| 143 | +copilot-cost dashboard |
| 144 | +``` |
| 145 | + |
| 146 | +Binds to `127.0.0.1` by default, reads local OpenTelemetry JSONL files, and shows: |
| 147 | + |
| 148 | +- 📅 lifetime, today, week, and month totals |
| 149 | +- 📈 token and cost trends |
| 150 | +- 🧵 session-level usage |
| 151 | +- 🤖 model breakdowns |
| 152 | +- 🏷️ pricing status |
| 153 | +- 🩺 local setup health |
| 154 | +- 📤 CSV export |
| 155 | + |
| 156 | +Use `--port`, `--host 127.0.0.1`, or `--no-open` as needed. |
| 157 | + |
| 158 | +--- |
| 159 | + |
| 160 | +## 🔒 Privacy promise |
| 161 | + |
| 162 | +- ✅ Usage data **stays on your machine**. |
| 163 | +- ✅ The dashboard only supports local binds (`127.0.0.1` or `localhost`). |
| 164 | +- ✅ This package emits **no telemetry or analytics** of its own. |
| 165 | +- ✅ Runtime usage data is read from local OpenTelemetry JSONL files. |
| 166 | +- 🌐 Pricing refresh contacts `docs.github.com` only when requested or when the cache needs refreshing. |
| 167 | + |
| 168 | +--- |
| 169 | + |
| 170 | +## 🛠️ Commands |
| 171 | + |
| 172 | +```bash |
| 173 | +copilot-cost render |
| 174 | +copilot-cost install [--yes] [--no-otel-profile] |
| 175 | +copilot-cost uninstall [--yes] |
| 176 | +copilot-cost doctor |
| 177 | +copilot-cost dashboard [--port <number>] [--host <host>] [--no-open] |
| 178 | +copilot-cost refresh-pricing [--force] |
| 179 | +copilot-cost migrate |
| 180 | +``` |
| 181 | + |
| 182 | +| Command | What it does | |
| 183 | +| --- | --- | |
| 184 | +| `render` | Render the statusline from Copilot CLI status JSON on stdin. This is also the default command. | |
| 185 | +| `install` | Install the statusline command and, unless `--no-otel-profile` is used, append local OpenTelemetry shell profile settings. | |
| 186 | +| `uninstall` | Remove settings installed by this package when they point at this tool. | |
| 187 | +| `doctor` | Check statusline setup, OpenTelemetry output, pricing, and dashboard readiness. | |
| 188 | +| `dashboard` | Serve the local dashboard. | |
| 189 | +| `refresh-pricing` | Refresh model pricing; `--force` bypasses the cache TTL. | |
| 190 | +| `migrate` | Export legacy 0.1.x SQLite data to OpenTelemetry-compatible JSONL snapshots. | |
| 191 | + |
| 192 | +--- |
| 193 | + |
| 194 | +## 🩺 Troubleshooting |
| 195 | + |
| 196 | +- **The statusline does not appear.** Run `copilot-cost doctor`, then confirm the Copilot CLI was restarted after install. |
| 197 | +- **No usage shows up yet.** Restart your shell, restart `copilot`, send a Copilot CLI prompt, then check for JSONL files in `~/.copilot/otel/`. |
| 198 | +- **I do not want profile edits.** Run `copilot-cost install --no-otel-profile` and paste the printed OpenTelemetry block into the shell profile you choose. |
| 199 | +- **The dashboard will not bind.** Use a local host only, e.g. `copilot-cost dashboard --host 127.0.0.1 --port 4567`. |
| 200 | +- **Pricing looks stale.** Run `copilot-cost refresh-pricing --force`. |
| 201 | + |
| 202 | +--- |
| 203 | + |
| 204 | +## 📚 Deeper docs |
| 205 | + |
| 206 | +- [`CHANGELOG.md`](CHANGELOG.md) — release notes. |
| 207 | +- [`docs/statusline-preview.txt`](docs/statusline-preview.txt) — text preview used by docs. |
| 208 | +- [`docs/statusline-styles.svg`](docs/statusline-styles.svg) — terminal-style preview of every statusline format. |
| 209 | +- [`docs/demo-otel/synthetic-usage.jsonl`](docs/demo-otel/synthetic-usage.jsonl) — synthetic demo data for screenshots. |
| 210 | +- 🔗 [GitHub Copilot CLI docs](https://docs.github.com/en/copilot/concepts/agents/copilot-cli/about-copilot-cli) · [Copilot OpenTelemetry observability](https://docs.github.com/en/copilot/how-tos/copilot-sdk/observability/opentelemetry) · [Copilot models & pricing](https://docs.github.com/en/copilot/reference/copilot-billing/models-and-pricing) |
| 211 | + |
| 212 | +--- |
| 213 | + |
| 214 | +## 🧪 Development |
| 215 | + |
| 216 | +```bash |
| 217 | +npm install |
| 218 | +npm test |
| 219 | +npm run build |
| 220 | +``` |
| 221 | + |
| 222 | +--- |
| 223 | + |
| 224 | +📄 MIT — see [LICENSE](LICENSE). |
0 commit comments