Skip to content

Commit 5f196ca

Browse files
devartifexCopilot
andcommitted
Initial commit
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
0 parents  commit 5f196ca

69 files changed

Lines changed: 8251 additions & 0 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/ci.yml

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
8+
jobs:
9+
test:
10+
runs-on: ${{ matrix.os }}
11+
strategy:
12+
fail-fast: false
13+
matrix:
14+
os: [ubuntu-latest]
15+
node-version: [18.x, 20.x, 22.x]
16+
17+
steps:
18+
- name: Checkout
19+
uses: actions/checkout@v4
20+
21+
- name: Setup Node.js
22+
uses: actions/setup-node@v4
23+
with:
24+
node-version: ${{ matrix.node-version }}
25+
cache: npm
26+
27+
- name: Install dependencies
28+
run: npm ci
29+
30+
- name: Lint
31+
run: npm run lint
32+
33+
- name: Test
34+
run: npm test
35+
36+
- name: Build
37+
run: npm run build

.gitignore

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
__pycache__/
2+
*.pyc
3+
.coverage
4+
dist/
5+
.test-home/
6+
.test-work/
7+
node_modules/
8+
dashboard-ui/dist/
9+
*.tsbuildinfo
10+
.DS_Store

CHANGELOG.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Changelog
2+
3+
All notable changes to this project will be documented in this file.
4+
5+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7+
8+
## [0.1.0] - Unreleased
9+
10+
### Breaking
11+
12+
- Rewritten in TypeScript.
13+
- Removed runtime SQLite. Data now sourced from `~/.copilot/otel/*.jsonl`. One-shot migration via `copilot-cost migrate` (or first `dashboard` launch).
14+
15+
### Added
16+
17+
- One-line install (`copilot-cost install`); auto-configures Copilot CLI OTel JSONL exporter via shell profile.
18+
- Redesigned dashboard (Tailwind + shadcn-style tokens, light + dark, sidebar nav, Overview / Sessions / Models / Pricing / Settings pages, charts).
19+
- `/api/health`, `/api/install-otel` endpoints.
20+
21+
### Changed
22+
23+
- Dashboard server now serves redesigned UI.
24+
25+
## Pre-community
26+
27+
- Initial Python implementation of the local Copilot cost statusline and dashboard.

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2026 devartifex
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
1+
# 💸 copilot-cost
2+
3+
> **See your GitHub Copilot CLI tokens and estimated spend at a glance — right in your terminal.**
4+
5+
[![license](https://img.shields.io/badge/license-MIT-blue)](LICENSE)
6+
[![node](https://img.shields.io/badge/node-%3E%3D18-43853d?logo=node.js)](https://nodejs.org)
7+
[![OpenTelemetry](https://img.shields.io/badge/OpenTelemetry-native-425cc7?logo=opentelemetry&logoColor=white)](https://opentelemetry.io)
8+
[![Privacy: local-only](https://img.shields.io/badge/privacy-local--only-2ea44f)](#-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+
![copilot-cost statusline styles](docs/statusline-styles.svg)
17+
18+
| ☀️ Light | 🌙 Dark |
19+
| :---: | :---: |
20+
| ![dashboard light](docs/dashboard-light.png) | ![dashboard dark](docs/dashboard-dark.png) |
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).

dashboard-ui/README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Dashboard UI
2+
3+
Vanilla HTML/JS dashboard styled with Tailwind CSS. Build from the repository root:
4+
5+
```sh
6+
npm run build:ui
7+
```
8+
9+
The build compiles `styles.src.css` into `dashboard-ui/dist/styles.css` and copies `index.html` plus `app.js` into `dashboard-ui/dist/`. The `dist/` directory is generated and should be refreshed after UI changes.

0 commit comments

Comments
 (0)