Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 3 additions & 18 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Guidance for Claude Code working in this repository. Every line here is loaded i
Token Tracker is a local-first AI token usage tracker.

- **CLI** (`src/`, CommonJS, Node ≥20) — entry `bin/tracker.js` → `src/cli.js`. `serve` runs a local HTTP server on `:7680`, `sync` parses logs into `~/.tokentracker/queue.jsonl`.
- **Dashboard** (`dashboard/`, React 18 + Vite 7 + TS strict + Tailwind) — built to `dashboard/dist/`, served by the CLI locally and by Vercel at `www.tokentracker.cc`.
- **Dashboard** (`dashboard/`, React 18 + Vite 7 + TS strict + Tailwind) — built to `dashboard/dist/`, served by the CLI on localhost. Local-only: there is no hosted deployment.
- **macOS app** (`TokenTrackerBar/`, Swift 5.9, XcodeGen) — menu bar + WidgetKit. `EmbeddedServer/` bundles the CLI runtime + built dashboard so the `.app` is self-contained.
- **Windows app** (`TokenTrackerWin/`, .NET 8 WinForms + WPF + WebView2) — system-tray counterpart of the macOS app. Launches the bundled CLI `serve` on a dynamic loopback port (avoids the DoSvc-held `:7680`), hosts the dashboard in WebView2, registers the `tokentracker://` deep-link for OAuth. Built `EmbeddedServer/` (Node + CLI + dashboard) is bundled by `scripts/bundle-node.ps1` so the `.exe` is self-contained. Dashboard adaptations are gated behind `isNativeWindowsApp()` (`dashboard/src/lib/native-bridge.js`) so macOS/web paths are untouched.

Expand Down Expand Up @@ -40,7 +40,7 @@ node bin/tracker.js serve --no-sync # local dashboard server on :7680
| Add a local API endpoint | `src/lib/local-api.js` — search `/functions/tokentracker-` |
| Wire a provider into sync | `src/commands/sync.js` (call site + totals aggregation) + `src/commands/status.js` (status reporting) |
| Add pricing for a model | `src/lib/pricing/curated-overrides.json` |
| Add a dashboard page | `dashboard/src/pages/` (lazy-loaded via `React.lazy()` in `App.jsx` — **except `NativeAuthCallbackPage`, which must stay eager-imported**, see Lessons learned) |
| Add a dashboard page | `dashboard/src/pages/` (lazy-loaded via `React.lazy()` in `App.jsx`) |
| Add UI components | `dashboard/src/ui/dashboard/components/` |
| Add a provider icon | `dashboard/src/ui/dashboard/components/ProviderIcon.jsx` (`PROVIDER_ICON_MAP` keyed by `source.toUpperCase()`) |
| Add user-facing text | `dashboard/src/content/copy.csv` — never hardcode |
Expand Down Expand Up @@ -163,23 +163,8 @@ openspec validate <id> --strict

### Dashboard layout

- **`AppLayout`-wrapped pages use `flex flex-col flex-1`** as the outer wrapper, not `min-h-screen` + own sticky header/footer. Reference: `LimitsPage.jsx` / `LeaderboardPage.jsx` / `SettingsPage.jsx`. `LeaderboardProfilePage.jsx` is intentionally excluded via `isLeaderboardIndexPath` in `App.jsx`.
- **`AppLayout`-wrapped pages use `flex flex-col flex-1`** as the outer wrapper, not `min-h-screen` + own sticky header/footer. Reference: `LimitsPage.jsx` / `SettingsPage.jsx`.
- **Motion height animations clip box-shadow focus rings.** Use `focus:ring-inset` on inputs inside `AnimatePresence` height-collapsing containers (see `SettingsPage.jsx` Account section).
- **`NativeAuthCallbackPage` must stay eager-imported in `App.jsx`** (do NOT convert it to `React.lazy()` even when adding other lazy pages). Its module captures the OAuth `insforge_code` query param synchronously at module-load time, BEFORE the InsForge SDK's `detectAuthCallback()` runs `cleanUrlParams("insforge_code")` to strip it. Lazy-loading delays the module until the route mounts, by which point the SDK has already wiped the URL — the captured code is `null` and the page falls through to the "Sign-in incomplete" failure state. Regression history: PR splitting the 1.9MB main bundle broke OAuth callback for every user until reverted for this one page.

### Native ↔ web bridge

```
JS → window.webkit.messageHandlers.nativeBridge.postMessage({ type, key?, value?, name? })
Swift → NativeBridge.shared.handle(message:) via WKScriptMessageHandler
Swift → JS: webView.evaluateJavaScript("window.dispatchEvent(new CustomEvent('native:settings', { detail }))")
React → useNativeSettings() subscribes
```

After `SMAppService.mainApp.register/unregister` from the bridge (not via `LaunchAtLoginManager.toggle`), call `launchAtLoginManager.refresh()` so the popover menu reflects `@Published isEnabled`.

### Parser correctness

- **Parser dedup**: use `claudeMessageDedupKey()`. Bare `if (msgId && reqId)` fails open on DeepSeek/Kimi/Mimo/MiniMax/Claude sub-agents (no `reqId`) and over-counts 1.6–3.7×.
- **Don't trust `input_tokens` semantics blindly** when adding a new provider. Codex/every-code's `input` includes cached tokens — naive copy inflates cost 6–7×. Verify with raw usage + provider billing dashboard before shipping.
- **`contextTokensUsed`-style fields are usually snapshots, not cumulative.** PR #74 (Grok) shipped on that bad assumption.
Expand Down
4 changes: 2 additions & 2 deletions PRODUCT.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ register: product

## Product purpose

Local-first AI token-usage tracker. Parses logs from AI coding CLIs (Claude Code, Codex, Cursor, Gemini, Copilot, Kimi, and more) into a local dashboard so developers can see how many tokens they burn, the estimated cost, and how it trends. Privacy-first: token counts only, never prompts or conversation bodies. Ships as a CLI (`serve` on :7680), a web dashboard (www.tokentracker.cc), and a self-contained macOS menu-bar app.
Local-first AI token-usage tracker. Parses logs from AI coding CLIs (Claude Code, Codex, Cursor, Gemini, Copilot, Kimi, and more) into a local dashboard so developers can see how many tokens they burn, the estimated cost, and how it trends. Privacy-first: token counts only, never prompts or conversation bodies. Ships as a CLI (`serve` on :7680) with a dashboard it serves locally, plus a self-contained macOS menu-bar app. Nothing is uploaded: there is no account, no sync, and no server component.

## Users

Developers and AI-power-users who run multiple agent CLIs daily and want a single, trustworthy view of consumption and cost. They are fluent in tools like Linear, Raycast, Vercel, and GitHub. They check usage both at a desk (deep review) and on a phone (quick glance: "how much did I burn today / where am I on the leaderboard"). They distrust inflated numbers, so accuracy and legible key metrics matter more than decoration.
Developers and AI-power-users who run multiple agent CLIs daily and want a single, trustworthy view of consumption and cost. They are fluent in tools like Linear, Raycast, Vercel, and GitHub. They check usage at a desk for deep review and at a glance for "how much did I burn today". They distrust inflated numbers, so accuracy and legible key metrics matter more than decoration.

## Tone & principles

Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ tokentracker doctor # health check
- 🔌 **22 tools out of the box.** Claude Code, Codex, Cursor, Gemini, Copilot, Antigravity, OpenCode, Kiro, Zed, Goose, and more — auto-detected.
- 💰 **Accurate cost.** 2,200+ models priced from [LiteLLM](https://github.com/BerriAI/litellm) (refreshed daily) with a bundled offline snapshot, so USD totals are right even without a network. Cross-provider records are de-duplicated so your numbers match each provider's own billing.
- 📈 **Rate-limit tracking.** Live quota windows with reset countdowns for Claude, Codex, Cursor, Gemini, Kimi, Z.AI, Kiro, Copilot, and Antigravity.
- 🧩 **Optional extras.** A worldwide leaderboard (opt-in) and a Skills tab that syncs 250+ public skills across your tools.
- 🧩 **Skills tab.** Syncs 250+ public skills across your tools.

---

Expand Down Expand Up @@ -105,7 +105,7 @@ AI CLI tools → hooks / passive readers → local SQLite → dashboard
3. Counts are aggregated into 30-minute UTC buckets in a local SQLite snapshot.
4. The dashboard reads that snapshot and renders it in your browser's timezone.

The leaderboard is the only feature that ever leaves your machine, and only when you opt in.
Nothing leaves your machine. There is no account, no upload, and no server to sign in to.

---

Expand All @@ -114,7 +114,7 @@ The leaderboard is the only feature that ever leaves your machine, and only when
| Protection | What it means |
|---|---|
| **No content** | Only token counts and timestamps. Never prompts, responses, or files. |
| **Local by default** | All data stays on your machine. The leaderboard is fully opt-in. |
| **Local only** | All data stays on your machine. There is no upload path at all. |
| **Auditable** | Open source — read [`src/lib/rollout.js`](src/lib/rollout.js); it's just numbers and timestamps. |
| **No telemetry** | No analytics, no crash reporting, no phone-home. |

Expand Down
22 changes: 5 additions & 17 deletions dashboard/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,10 @@
<meta property="og:site_name" content="__TOKENTRACKER_OG_SITE_NAME__" />
<meta property="og:title" content="__TOKENTRACKER_OG_TITLE__" />
<meta property="og:description" content="__TOKENTRACKER_OG_DESCRIPTION__" />
<meta property="og:image" content="__TOKENTRACKER_OG_IMAGE__" />
<meta property="og:type" content="__TOKENTRACKER_OG_TYPE__" />
<meta property="og:url" content="__TOKENTRACKER_OG_URL__" />
<meta name="twitter:card" content="__TOKENTRACKER_TWITTER_CARD__" />
<meta name="twitter:title" content="__TOKENTRACKER_TWITTER_TITLE__" />
<meta name="twitter:description" content="__TOKENTRACKER_TWITTER_DESCRIPTION__" />
<meta name="twitter:image" content="__TOKENTRACKER_TWITTER_IMAGE__" />
<link rel="canonical" href="__TOKENTRACKER_OG_URL__" />
<link rel="icon" href="/icon.svg" type="image/svg+xml" />
<link rel="icon" href="/favicon.ico" sizes="any" />
<link rel="icon" href="/favicon-32.png" sizes="32x32" type="image/png" />
Expand Down Expand Up @@ -146,12 +142,6 @@
"name": "Home",
"item": "https://www.tokentracker.cc/"
},
{
"@type": "ListItem",
"position": 2,
"name": "Leaderboard",
"item": "https://www.tokentracker.cc/leaderboard"
},
{
"@type": "ListItem",
"position": 3,
Expand Down Expand Up @@ -227,7 +217,7 @@
"name": "Does Token Tracker work offline?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Yes. Token Tracker is local-first. All parsing, aggregation, and dashboard rendering happens on your machine. Cloud sync is optional and used only for the public leaderboard."
"text": "Yes. Token Tracker is local-only. All parsing, aggregation, and dashboard rendering happens on your machine, and nothing is uploaded anywhere."
}
}
]
Expand Down Expand Up @@ -260,7 +250,7 @@ <h2>What you get after installing</h2>
<p>
Token Tracker runs entirely on your machine. After <code>npx tokentracker-cli</code>, a local
dashboard at <code>http://localhost:7680</code> exposes the following views — your data stays on
your device unless you opt into cloud sync.
your device. There is no upload path.
</p>
<ul>
<li><strong>Unified usage dashboard</strong> — input, output, cached, and cache-creation tokens across all 13 AI coding tools.</li>
Expand All @@ -271,7 +261,7 @@ <h2>What you get after installing</h2>
<li><strong>Skills browser</strong> — discover and install agent skills from the open ecosystem.</li>
<li><strong>macOS menu bar app & desktop widget</strong> — at-a-glance usage without opening the dashboard.</li>
</ul>
<p><em>Privacy by design</em>: token counts only, never prompts or conversation content. Local-first; cloud sync is optional and powers only the public leaderboard.</p>
<p><em>Privacy by design</em>: token counts only, never prompts or conversation content. Local-only — nothing is uploaded.</p>

<h2>Supported AI coding agent CLIs</h2>
<ul>
Expand Down Expand Up @@ -309,8 +299,7 @@ <h2>How it works</h2>
(<code>src/lib/rollout.js</code>) normalizes 9 distinct log formats into a unified schema: half-hour
UTC buckets keyed by <code>(source, model, hour_start)</code>. Aggregated buckets are written to
<code>queue.jsonl</code> and served from a local HTTP API on port 7680. The React dashboard reads
from those endpoints; cloud sync (optional) uploads bucketed counts to InsForge for the public
leaderboard.
from those endpoints. Nothing is uploaded anywhere.
</p>

<h2>Frequently asked questions</h2>
Expand All @@ -330,12 +319,11 @@ <h3>What does Token Tracker track?</h3>
<p>Input, output, cached, and cache-creation tokens per model, project, and time window. It also surfaces rate limits and subscription status for Claude Pro, ChatGPT plans, Cursor, Gemini, Kiro, and Antigravity.</p>

<h3>Does Token Tracker work offline?</h3>
<p>Yes. Token Tracker is local-first. All parsing, aggregation, and dashboard rendering happen on your machine. Cloud sync is optional and powers only the public leaderboard.</p>
<p>Yes. Token Tracker is local-only. All parsing, aggregation, and dashboard rendering happen on your machine, and nothing is uploaded.</p>

<h2>Resources</h2>
<ul>
<li><a href="https://www.tokentracker.cc/">Homepage and install instructions</a></li>
<li><a href="https://www.tokentracker.cc/leaderboard">Public token usage leaderboard</a></li>
<li><a href="https://www.tokentracker.cc/ip-check">Claude IP region checker</a></li>
<li><a href="https://www.npmjs.com/package/tokentracker-cli">npm: tokentracker-cli</a></li>
<li><a href="https://github.com/mm7894215/TokenTracker">GitHub: mm7894215/TokenTracker</a></li>
Expand Down
13 changes: 1 addition & 12 deletions dashboard/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,32 +12,21 @@
"preview": "vite preview",
"screenshot": "node scripts/capture-dashboard-screenshot.mjs",
"compare:baselines": "node scripts/compare-visual-baselines.mjs",
"verify:share-clipboard": "node scripts/verify-share-clipboard.mjs",
"test": "vitest run",
"test:watch": "vitest"
},
"dependencies": {
"@base-ui/react": "1.1.0",
"@dnd-kit/core": "^6.3.1",
"@dnd-kit/modifiers": "^7.0.0",
"@dnd-kit/sortable": "^8.0.0",
"@dnd-kit/utilities": "^3.2.2",
"@fontsource/geist-mono": "^5.2.0",
"@insforge/sdk": "^1.2.2",
"@vercel/analytics": "^2.0.1",
"@vercel/speed-insights": "^2.0.0",
"clsx": "^2.1.1",
"date-fns": "^4.1.0",
"html-to-image": "^1.11.13",
"lucide-react": "^1.7.0",
"motion": "^12.38.0",
"ogl": "^1.0.11",
"react": "^18.2.0",
"react-day-picker": "^9.14.0",
"react-dom": "^18.2.0",
"react-router-dom": "^7.17.0",
"tailwind-merge": "^3.5.0",
"three": "^0.183.2"
"tailwind-merge": "^3.5.0"
},
"devDependencies": {
"@testing-library/jest-dom": "^6.4.5",
Expand Down
2 changes: 0 additions & 2 deletions dashboard/public/llms.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ Kiro, Hermes Agent, GitHub Copilot, Kimi Code, oh-my-pi (omp), and CodeBuddy.

## Priority URLs
- [Homepage](https://www.tokentracker.cc/)
- [Leaderboard](https://www.tokentracker.cc/leaderboard)
- [Claude IP check](https://www.tokentracker.cc/ip-check)

## Post-install (local) views
Expand Down Expand Up @@ -47,7 +46,6 @@ on the user's own machine — they are not public URLs:
- [feed.xml](https://www.tokentracker.cc/feed.xml)
- [robots.txt](https://www.tokentracker.cc/robots.txt)
- Data principle: token counts only, never prompts or conversation content.
- Local-first: all parsing, aggregation, and dashboard rendering happen on-device. Cloud sync is optional.

## Contact
- [GitHub issues](https://github.com/mm7894215/TokenTracker/issues)
12 changes: 0 additions & 12 deletions dashboard/public/sitemap.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,10 @@
<changefreq>weekly</changefreq>
<priority>1.0</priority>
</url>
<url>
<loc>https://www.tokentracker.cc/leaderboard</loc>
<lastmod>2026-05-07</lastmod>
<changefreq>daily</changefreq>
<priority>0.9</priority>
</url>
<url>
<loc>https://www.tokentracker.cc/ip-check</loc>
<lastmod>2026-05-07</lastmod>
<changefreq>monthly</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://www.tokentracker.cc/share</loc>
<lastmod>2026-05-07</lastmod>
<changefreq>weekly</changefreq>
<priority>0.4</priority>
</url>
</urlset>
56 changes: 0 additions & 56 deletions dashboard/scripts/verify-share-clipboard.mjs

This file was deleted.

Loading