@neuralmindlabs/tokn 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/ci.yml +22 -0
- package/.oxlintrc.json +8 -0
- package/LICENSE +21 -0
- package/README.md +71 -0
- package/dist/assets/index-Dq74RfU-.css +1 -0
- package/dist/assets/index-_9e8Z58Z.js +66 -0
- package/dist/favicon.svg +1 -0
- package/dist/icons.svg +24 -0
- package/dist/index.html +14 -0
- package/index.html +13 -0
- package/package.json +45 -0
- package/packages/tokn/LICENSE +21 -0
- package/packages/tokn/README.md +85 -0
- package/packages/tokn/package.json +56 -0
- package/packages/tokn/src/aggregate.ts +83 -0
- package/packages/tokn/src/cli.ts +181 -0
- package/packages/tokn/src/cursor.ts +85 -0
- package/packages/tokn/src/index.ts +6 -0
- package/packages/tokn/src/node.ts +50 -0
- package/packages/tokn/src/parsers/claude.ts +53 -0
- package/packages/tokn/src/parsers/codex.ts +74 -0
- package/packages/tokn/src/pricing/index.ts +106 -0
- package/packages/tokn/src/pricing/prices.json +948 -0
- package/packages/tokn/src/pricing/prices.ts +947 -0
- package/packages/tokn/src/types.ts +51 -0
- package/packages/tokn/tests/__snapshots__/eval.test.ts.snap +12 -0
- package/packages/tokn/tests/aggregate.test.ts +73 -0
- package/packages/tokn/tests/claude.test.ts +87 -0
- package/packages/tokn/tests/codex.test.ts +54 -0
- package/packages/tokn/tests/cursor.test.ts +126 -0
- package/packages/tokn/tests/eval.test.ts +69 -0
- package/packages/tokn/tests/fixtures/claude-session.jsonl +3 -0
- package/packages/tokn/tests/fixtures/codex-session.jsonl +5 -0
- package/packages/tokn/tests/pricing.test.ts +137 -0
- package/packages/tokn/tsconfig.json +21 -0
- package/public/favicon.svg +1 -0
- package/public/icons.svg +24 -0
- package/scripts/validate.ts +122 -0
- package/src/App.tsx +12 -0
- package/src/assets/hero.png +0 -0
- package/src/assets/vite.svg +1 -0
- package/src/index.css +250 -0
- package/src/lib/refresh.ts +134 -0
- package/src/lib/scanner.ts +108 -0
- package/src/main.tsx +7 -0
- package/src/ui/Dashboard.tsx +203 -0
- package/src/ui/Popover.tsx +91 -0
- package/src/ui/Settings.tsx +114 -0
- package/src/ui/useAppData.ts +71 -0
- package/src-tauri/Cargo.lock +5972 -0
- package/src-tauri/Cargo.toml +35 -0
- package/src-tauri/build.rs +3 -0
- package/src-tauri/capabilities/default.json +114 -0
- package/src-tauri/icons/128x128.png +0 -0
- package/src-tauri/icons/128x128@2x.png +0 -0
- package/src-tauri/icons/32x32.png +0 -0
- package/src-tauri/icons/64x64.png +0 -0
- package/src-tauri/icons/Square107x107Logo.png +0 -0
- package/src-tauri/icons/Square142x142Logo.png +0 -0
- package/src-tauri/icons/Square150x150Logo.png +0 -0
- package/src-tauri/icons/Square284x284Logo.png +0 -0
- package/src-tauri/icons/Square30x30Logo.png +0 -0
- package/src-tauri/icons/Square310x310Logo.png +0 -0
- package/src-tauri/icons/Square44x44Logo.png +0 -0
- package/src-tauri/icons/Square71x71Logo.png +0 -0
- package/src-tauri/icons/Square89x89Logo.png +0 -0
- package/src-tauri/icons/StoreLogo.png +0 -0
- package/src-tauri/icons/android/mipmap-anydpi-v26/ic_launcher.xml +5 -0
- package/src-tauri/icons/android/mipmap-hdpi/ic_launcher.png +0 -0
- package/src-tauri/icons/android/mipmap-hdpi/ic_launcher_foreground.png +0 -0
- package/src-tauri/icons/android/mipmap-hdpi/ic_launcher_round.png +0 -0
- package/src-tauri/icons/android/mipmap-mdpi/ic_launcher.png +0 -0
- package/src-tauri/icons/android/mipmap-mdpi/ic_launcher_foreground.png +0 -0
- package/src-tauri/icons/android/mipmap-mdpi/ic_launcher_round.png +0 -0
- package/src-tauri/icons/android/mipmap-xhdpi/ic_launcher.png +0 -0
- package/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_foreground.png +0 -0
- package/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_round.png +0 -0
- package/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher.png +0 -0
- package/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_foreground.png +0 -0
- package/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_round.png +0 -0
- package/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher.png +0 -0
- package/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_foreground.png +0 -0
- package/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_round.png +0 -0
- package/src-tauri/icons/android/values/ic_launcher_background.xml +4 -0
- package/src-tauri/icons/icon.icns +0 -0
- package/src-tauri/icons/icon.ico +0 -0
- package/src-tauri/icons/icon.png +0 -0
- package/src-tauri/icons/ios/AppIcon-20x20@1x.png +0 -0
- package/src-tauri/icons/ios/AppIcon-20x20@2x-1.png +0 -0
- package/src-tauri/icons/ios/AppIcon-20x20@2x.png +0 -0
- package/src-tauri/icons/ios/AppIcon-20x20@3x.png +0 -0
- package/src-tauri/icons/ios/AppIcon-29x29@1x.png +0 -0
- package/src-tauri/icons/ios/AppIcon-29x29@2x-1.png +0 -0
- package/src-tauri/icons/ios/AppIcon-29x29@2x.png +0 -0
- package/src-tauri/icons/ios/AppIcon-29x29@3x.png +0 -0
- package/src-tauri/icons/ios/AppIcon-40x40@1x.png +0 -0
- package/src-tauri/icons/ios/AppIcon-40x40@2x-1.png +0 -0
- package/src-tauri/icons/ios/AppIcon-40x40@2x.png +0 -0
- package/src-tauri/icons/ios/AppIcon-40x40@3x.png +0 -0
- package/src-tauri/icons/ios/AppIcon-512@2x.png +0 -0
- package/src-tauri/icons/ios/AppIcon-60x60@2x.png +0 -0
- package/src-tauri/icons/ios/AppIcon-60x60@3x.png +0 -0
- package/src-tauri/icons/ios/AppIcon-76x76@1x.png +0 -0
- package/src-tauri/icons/ios/AppIcon-76x76@2x.png +0 -0
- package/src-tauri/icons/ios/AppIcon-83.5x83.5@2x.png +0 -0
- package/src-tauri/icons/source/app-icon-1024.png +0 -0
- package/src-tauri/icons/source/app-icon.svg +74 -0
- package/src-tauri/icons/source/tray-22.png +0 -0
- package/src-tauri/icons/source/tray-44.png +0 -0
- package/src-tauri/icons/source/tray-icon.svg +14 -0
- package/src-tauri/permissions/allow-custom-commands.toml +11 -0
- package/src-tauri/src/lib.rs +238 -0
- package/src-tauri/src/main.rs +6 -0
- package/src-tauri/tauri.conf.json +57 -0
- package/tsconfig.app.json +27 -0
- package/tsconfig.json +7 -0
- package/tsconfig.node.json +23 -0
- package/vite.config.ts +7 -0
- package/vitest.config.ts +28 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [master]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
# Ubuntu-only to stay within the free Actions tier on a private repo.
|
|
9
|
+
# The Tauri macOS build is not run in CI (a macOS runner bills at 10x
|
|
10
|
+
# minutes); build it locally with `npm run tauri build`.
|
|
11
|
+
jobs:
|
|
12
|
+
test:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
- uses: actions/setup-node@v4
|
|
17
|
+
with:
|
|
18
|
+
node-version: 22
|
|
19
|
+
cache: npm
|
|
20
|
+
- run: npm ci
|
|
21
|
+
- run: npm run lint
|
|
22
|
+
- run: npm run test:coverage
|
package/.oxlintrc.json
ADDED
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Suryakant Pandey
|
|
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.
|
package/README.md
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# Tokn
|
|
2
|
+
|
|
3
|
+
Daily AI token usage and cost across **Claude Code**, **Codex**, and
|
|
4
|
+
**Cursor**, in two forms:
|
|
5
|
+
|
|
6
|
+
- **[tokn](packages/tokn)** — npm package: `npx tokn` CLI + reusable
|
|
7
|
+
parsing/pricing core (publish with `npm publish -w tokn`)
|
|
8
|
+
- **Tokn.app** — macOS menu bar app (this repo's root), built on the
|
|
9
|
+
same `tokn` core
|
|
10
|
+
|
|
11
|
+
The menu bar shows today's total cost at a glance. Clicking it opens a popover
|
|
12
|
+
with per-tool breakdown and a 14-day trend; "Open dashboard" opens a full
|
|
13
|
+
window with 30/90-day charts, per-model and per-project tables, CSV export,
|
|
14
|
+
and settings.
|
|
15
|
+
|
|
16
|
+
## How it computes cost
|
|
17
|
+
|
|
18
|
+
| Source | Method | Meaning |
|
|
19
|
+
|---|---|---|
|
|
20
|
+
| Claude Code | Parses `~/.claude/projects/**/*.jsonl` locally | API-equivalent value (you're on a subscription) |
|
|
21
|
+
| Codex | Parses `~/.codex/sessions/**/*.jsonl` locally | API-equivalent value |
|
|
22
|
+
| Cursor | cursor.com dashboard API (session cookie, set in Settings) | Actual billed dollars |
|
|
23
|
+
|
|
24
|
+
Pricing comes from LiteLLM's community-maintained price table
|
|
25
|
+
(`src/core/pricing/prices.json`), including Anthropic cache-write (5m at
|
|
26
|
+
1.25x, 1h at 2x input rate) and cache-read rates. Codex token counts are
|
|
27
|
+
derived from deltas of the session-cumulative counter, which is immune to the
|
|
28
|
+
duplicated `token_count` events Codex writes (ccusage double-counts these).
|
|
29
|
+
|
|
30
|
+
## Connecting Cursor
|
|
31
|
+
|
|
32
|
+
Cursor keeps usage server-side, so the app needs your dashboard session:
|
|
33
|
+
1. Open [cursor.com/dashboard](https://cursor.com/dashboard) logged in
|
|
34
|
+
2. DevTools → Application → Cookies → copy the value of `WorkosCursorSessionToken`
|
|
35
|
+
3. Paste it in the dashboard's Settings section
|
|
36
|
+
|
|
37
|
+
The cookie expires every few weeks; the popover shows "reconnect in settings"
|
|
38
|
+
when that happens. Claude/Codex keep working regardless.
|
|
39
|
+
|
|
40
|
+
## Development
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
npm install
|
|
44
|
+
npx tauri dev # run the app
|
|
45
|
+
npm test # unit tests (parsers, pricing, aggregation)
|
|
46
|
+
npm run validate # cross-check against ccusage + Codex session counters
|
|
47
|
+
npx tauri build # produce .app / .dmg in src-tauri/target/release/bundle
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Architecture: `packages/tokn/` is the published package — pure TypeScript
|
|
51
|
+
parsers, pricing, aggregation (browser-safe `tokn` entry) plus the CLI and
|
|
52
|
+
Node scanner (`tokn/node`). The app's `src/lib/` is Tauri-side glue (fs
|
|
53
|
+
scanning with an incremental per-file cache, refresh loop) and `src-tauri/`
|
|
54
|
+
is the Rust shell (tray icon, popover + dashboard windows).
|
|
55
|
+
|
|
56
|
+
## Privacy & security
|
|
57
|
+
|
|
58
|
+
Everything is computed locally: Claude Code and Codex logs never leave your
|
|
59
|
+
machine. The only network calls are to cursor.com (if you connect Cursor) and
|
|
60
|
+
to fetch the pricing table. The Cursor session cookie is stored in plain text
|
|
61
|
+
in the app's local data directory (`~/Library/Application Support/dev.surya.tokn`)
|
|
62
|
+
— treat it like a saved login. The macOS app is menu-bar only and mac-only by
|
|
63
|
+
design; the `tokn` CLI works anywhere Node 20+ runs.
|
|
64
|
+
|
|
65
|
+
**Nothing sensitive is committed.** The Cursor cookie and cached usage live in
|
|
66
|
+
gitignored store files (`settings.json` / `cache.json`), never in the repo. CI
|
|
67
|
+
runs Ubuntu-only to stay in the free Actions tier — build the macOS app locally.
|
|
68
|
+
|
|
69
|
+
## License
|
|
70
|
+
|
|
71
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
:root{--sans:-apple-system, BlinkMacSystemFont, "SF Pro Text", system-ui, sans-serif;--mono:ui-monospace, "SF Mono", "JetBrains Mono", Menlo, monospace;font-family:var(--sans);--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light dark;--bg:#f1f1ef;--card:#fff;--text:#18181b;--muted:#6b6b73;--faint:#a6a6ad;--border:#00000017;--hairline:#0000000f;--hover:#00000009;--accent:#6663e6;--accent-soft:#6663e61a;--shadow:0 1px 2px #1414190d, 0 10px 30px #14141912;font-size:13px}@media (prefers-color-scheme:dark){:root{--lightningcss-light: ;--lightningcss-dark:initial}:root:not([data-theme=light]){--lightningcss-light: ;--lightningcss-dark:initial;color-scheme:dark;--bg:#131316;--card:#1c1c20;--text:#e8e8ec;--muted:#8b8b94;--faint:#55555e;--border:#ffffff14;--hairline:#ffffff0e;--hover:#ffffff0a;--accent:#8b87ff;--accent-soft:#8b87ff29;--shadow:0 1px 2px #0000004d, 0 10px 30px #00000061}}:root[data-theme=dark]{--lightningcss-light: ;--lightningcss-dark:initial;color-scheme:dark;--bg:#131316;--card:#1c1c20;--text:#e8e8ec;--muted:#8b8b94;--faint:#55555e;--border:#ffffff14;--hairline:#ffffff0e;--hover:#ffffff0a;--accent:#8b87ff;--accent-soft:#8b87ff29;--shadow:0 1px 2px #0000004d, 0 10px 30px #00000061}:root[data-theme=light]{--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light}*{box-sizing:border-box}html,body,#root{margin:0;padding:0}body{color:var(--text);-webkit-font-smoothing:antialiased;-webkit-user-select:none;user-select:none;background:0 0}.muted{color:var(--muted)}.small{font-size:11px}.right{text-align:right}.big{font-family:var(--mono);letter-spacing:-1px;font-variant-numeric:tabular-nums;font-size:27px;font-weight:600;line-height:1.05}.mid{font-family:var(--mono);letter-spacing:-.4px;font-variant-numeric:tabular-nums;font-size:15px;font-weight:600}.dot{border-radius:2px;flex:none;width:7px;height:7px;display:inline-block}.sc,h2,th{letter-spacing:.9px;text-transform:uppercase;color:var(--muted);font-size:10px;font-weight:600}.popover{background:var(--bg);border:.5px solid var(--border);border-radius:16px;flex-direction:column;gap:8px;height:100vh;padding:8px;display:flex;position:relative;overflow:hidden}.pop-card{background:var(--card);border:.5px solid var(--hairline);border-radius:12px;padding:13px 14px}.pop-hero{padding-bottom:14px}.pop-header{justify-content:space-between;align-items:baseline;display:flex}.pop-tools{flex-direction:column;gap:10px;display:flex}.tool-row{align-items:baseline;gap:8px;display:flex}.tool-row .dot{align-self:center}.tool-name{flex:none;font-weight:500}.leader{border-bottom:1px dotted var(--faint);opacity:.6;flex:1;min-width:12px;transform:translateY(-3px)}.tool-cost{font-family:var(--mono);text-align:right;font-variant-numeric:tabular-nums;letter-spacing:-.3px;min-width:56px;font-weight:600}.pop-chart .small{color:var(--muted)}.bars{align-items:flex-end;gap:3px;height:60px;margin-top:8px;display:flex}.bar-col{flex-direction:column-reverse;flex:1;gap:1px;height:100%;display:flex}.bar-col div{border-radius:2px 2px 0 0}.bar-labels{font-family:var(--mono);font-variant-numeric:tabular-nums;justify-content:space-between;margin-top:5px;display:flex}.pop-footer{justify-content:space-between;align-items:center;margin-top:auto;padding:4px 8px 2px;display:flex}.linkish{color:var(--accent);cursor:pointer;background:0 0;border:none;padding:0;font-size:12px;font-weight:550}.linkish:hover{text-decoration:underline}.dashboard{background:var(--bg);-webkit-user-select:text;user-select:text;min-height:100vh;padding:26px 30px 36px;position:relative}.dash-header{justify-content:space-between;align-items:flex-end;margin-bottom:18px;display:flex}h1{letter-spacing:-.4px;margin:0 0 3px;font-size:21px;font-weight:650}h2{margin:0 0 10px}.controls{gap:6px;display:flex}.seg{background:var(--card);border:.5px solid var(--border);cursor:pointer;color:var(--text);font-variant-numeric:tabular-nums;border-radius:8px;padding:5px 13px;font-size:12px;font-weight:500;transition:background .12s,color .12s,border-color .12s}.seg:hover{background:var(--hover)}.seg.active{background:var(--accent);color:#fff;border-color:var(--accent)}.filters{gap:18px;margin-bottom:12px;display:flex}.filter{cursor:pointer;align-items:center;gap:6px;font-size:12.5px;font-weight:500;display:flex}.filter input{accent-color:var(--accent)}.chart-box{background:var(--card);border:.5px solid var(--border);box-shadow:var(--shadow);border-radius:12px;margin-bottom:18px;padding:14px 10px 6px}.tables{grid-template-columns:1fr 1fr;gap:18px;margin-bottom:18px;display:grid}.tables section{background:var(--card);border:.5px solid var(--border);box-shadow:var(--shadow);border-radius:12px;padding:16px 18px}table{border-collapse:collapse;width:100%;font-size:12.5px}th{text-align:left;border-bottom:.5px solid var(--border);padding:3px 0 6px}th:not(:first-child),td:not(:first-child){text-align:right}td{border-bottom:.5px solid var(--hairline);padding:5px 0}td:not(:first-child){font-family:var(--mono);font-variant-numeric:tabular-nums;letter-spacing:-.3px}tbody tr:last-child td{border-bottom:none}td:first-child{text-overflow:ellipsis;white-space:nowrap;max-width:260px;overflow:hidden}tbody tr{transition:background .12s}tbody tr:hover{background:var(--hover)}tfoot td{border-top:1px solid var(--border);padding:6px 0 2px;font-weight:650}.warn{color:#c98a1b}.settings{background:var(--card);border:.5px solid var(--border);box-shadow:var(--shadow);border-radius:12px;padding:16px 18px}.setting-row{justify-content:space-between;align-items:center;gap:24px;padding:9px 0;display:flex}.setting-row+.setting-row{border-top:.5px solid var(--hairline)}.setting-name{font-weight:550}.cookie-input{flex:none;gap:6px;display:flex}.cookie-input input{background:var(--bg);border:.5px solid var(--border);width:260px;color:var(--text);font-size:12px;font-family:var(--mono);border-radius:8px;padding:6px 8px}.cookie-input input:focus{border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-soft);outline:none}.cookie-input button,.settings button{background:var(--card);border:.5px solid var(--border);cursor:pointer;color:var(--text);border-radius:8px;padding:6px 12px;font-weight:500;transition:background .12s}.cookie-input button:hover,.settings button:hover{background:var(--hover)}.help-link{color:var(--accent);cursor:pointer;text-decoration:none}.help-link:hover{text-decoration:underline}
|