@thisisayande/tokentracker 0.1.5

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/README.md ADDED
@@ -0,0 +1,99 @@
1
+ # TokenTracker
2
+
3
+ TokenTracker is a cross-platform desktop application that monitors AI provider quotas, rate limits, and spend statistics in real-time.
4
+
5
+ | | |
6
+ |:---:|:---:|
7
+ | <img src="/public/logos/image.png" width="290"> | <img src="/public/logos/image%20copy.png" width="290"> |
8
+
9
+ ## Features
10
+
11
+ - **Multi-Provider Support** — Monitor Anthropic, OpenAI, OpenRouter, Groq, Ollama, and more
12
+ - **Real-Time Quota Tracking** — Live usage and rate limit data with configurable polling
13
+ - **Cost Analytics** — Per-provider and aggregate spend breakdowns with model-level detail
14
+ - **Secure Credential Storage** — API keys and browser cookie import
15
+ - **Browser Profile Import** — Pull authentication cookies from Chrome, Firefox, and Edge profiles
16
+ - **Dark / Light Themes** — macOS-inspired glassmorphic UI with smooth transitions
17
+ - **System Tray** — Runs quietly in the background; click to show/hide
18
+ - **Cross-Platform** — Linux, Windows, and macOS via Tauri 2
19
+
20
+ ## Tech Stack
21
+
22
+ - **Frontend** — Next.js 16 (App Router), React 19, TypeScript, Tailwind CSS 4
23
+ - **Backend** — Rust (Axum HTTP API server running as a bundled subprocess)
24
+ - **Desktop Runtime** — Tauri 2 (system tray, window management, process lifecycle)
25
+
26
+ ## Architecture
27
+
28
+ ```
29
+ Frontend (Tauri/Next.js)
30
+ → HTTP (localhost) → Rust Backend (Axum API, port 46727)
31
+ → useProviders hook manages all state
32
+ → mapProviderUsage / mapProviderCost transform raw JSON → typed objects
33
+ ```
34
+
35
+ ### Key Files
36
+
37
+ | Path | Purpose |
38
+ |------|---------|
39
+ | `backend/src/main.rs` | Rust HTTP server entry point (Axum router) |
40
+ | `backend/src/server/` | Request handlers for all API endpoints |
41
+ | `src-tauri/src/lib.rs` | Tauri app lifecycle, tray icon, window management |
42
+ | `src-tauri/src/backend.rs` | Backend process spawning, health check, lifecycle |
43
+ | `src/lib/apiClient.ts` | Frontend HTTP client (fetches port dynamically from Tauri) |
44
+ | `src/hooks/useProviders.ts` | Central hook owning all provider/cost/settings state |
45
+ | `src/lib/dataMapping.ts` | Raw JSON → typed object transformers + provider descriptors |
46
+
47
+ ## Getting Started
48
+
49
+ ### Prerequisites
50
+
51
+ - [Rust](https://rustup.rs/) (latest stable)
52
+ - [Node.js](https://nodejs.org/) 20+
53
+
54
+ ### Development
55
+
56
+ ```bash
57
+ # Install frontend dependencies
58
+ npm install
59
+
60
+ # Run the full Tauri app (frontend + bundled backend, hot-reload)
61
+ npm run tauri:dev
62
+ ```
63
+
64
+ The backend HTTP server starts automatically. The frontend connects via a port dynamically reported by Tauri (default `46727`).
65
+
66
+ ### Production Build
67
+
68
+ ```bash
69
+ npm run tauri:build
70
+ ```
71
+
72
+ Outputs native installers for your platform (AppImage, `.deb`, `.msi`, etc.).
73
+
74
+ ### Running Tests
75
+
76
+ ```bash
77
+ # Terminal 1 — start the dev server
78
+ npm run dev
79
+
80
+ # Terminal 2 — run smoke tests
81
+ npm run test:smoke
82
+ ```
83
+
84
+ ## Environment Variables
85
+
86
+ | Variable | Default | Description |
87
+ |----------|---------|-------------|
88
+ | `TOKEN_TRACKER_BACKEND_PORT` | `46727` | Port for the bundled Rust backend HTTP server |
89
+
90
+ ## Local Caching
91
+
92
+ Usage and cost data is cached locally for offline reading and fast startup:
93
+
94
+ - **Linux** — `~/.config/CodexBar/cache.json`
95
+ - **Windows** — `%USERPROFILE%\.config\CodexBar\cache.json`
96
+
97
+ ## License
98
+
99
+ MIT
@@ -0,0 +1,143 @@
1
+ #!/usr/bin/env node
2
+ import https from "https";
3
+ import http from "http";
4
+ import fs from "fs";
5
+ import path from "path";
6
+ import { spawn } from "child_process";
7
+ import os from "os";
8
+
9
+ const REPO = "ayan-de/Token-Tracker";
10
+ const OWNER = "ayan-de";
11
+ const APP_NAME = "TokenTracker";
12
+
13
+ function getAssetUrl(version, filename) {
14
+ return `https://github.com/${OWNER}/${REPO}/releases/download/v${version}/${filename}`;
15
+ }
16
+
17
+ function getLatestVersion() {
18
+ return new Promise((resolve, reject) => {
19
+ const req = https.get(
20
+ `https://api.github.com/repos/${REPO}/releases/latest`,
21
+ { headers: { "User-Agent": "tokentracker-npm" } },
22
+ (res) => {
23
+ let data = "";
24
+ res.on("data", (chunk) => (data += chunk));
25
+ res.on("end", () => {
26
+ try {
27
+ const json = JSON.parse(data);
28
+ resolve(json.tag_name.replace("v", ""));
29
+ } catch {
30
+ reject(new Error("Failed to parse GitHub API response"));
31
+ }
32
+ });
33
+ }
34
+ );
35
+ req.on("error", reject);
36
+ req.setTimeout(10000, () => {
37
+ req.destroy();
38
+ reject(new Error("Timeout reaching GitHub API"));
39
+ });
40
+ });
41
+ }
42
+
43
+ function downloadFile(url, dest) {
44
+ return new Promise((resolve, reject) => {
45
+ const file = fs.createWriteStream(dest);
46
+ const protocol = url.startsWith("https") ? https : http;
47
+ const req = protocol.get(url, { headers: { "User-Agent": "tokentracker-npm" } }, (res) => {
48
+ if (res.statusCode === 302 || res.statusCode === 301) {
49
+ file.close();
50
+ downloadFile(res.headers.location, dest).then(resolve).catch(reject);
51
+ return;
52
+ }
53
+ if (res.statusCode !== 200) {
54
+ file.close();
55
+ reject(new Error(`HTTP ${res.statusCode}`));
56
+ return;
57
+ }
58
+ res.pipe(file);
59
+ file.on("finish", () => file.close(resolve));
60
+ });
61
+ req.on("error", (err) => {
62
+ file.close();
63
+ reject(err);
64
+ });
65
+ req.setTimeout(60000, () => {
66
+ req.destroy();
67
+ reject(new Error("Download timeout"));
68
+ });
69
+ });
70
+ }
71
+
72
+ async function ensureInstalled(version) {
73
+ const platform = os.platform();
74
+ if (platform !== "linux") {
75
+ console.error("TokenTracker npm install only supports Linux currently.");
76
+ console.error("Download from: https://github.com/ayan-de/Token-Tracker/releases");
77
+ process.exit(1);
78
+ }
79
+
80
+ const arch = os.arch() === "x64" ? "amd64" : os.arch();
81
+ const filename = `TokenTracker_${version}_${arch}.deb`;
82
+ const url = getAssetUrl(version, filename);
83
+ const tmpDir = path.join(os.tmpdir(), "tokentracker-install");
84
+ const debPath = path.join(tmpDir, filename);
85
+
86
+ if (!fs.existsSync(tmpDir)) fs.mkdirSync(tmpDir, { recursive: true });
87
+
88
+ if (!fs.existsSync(debPath)) {
89
+ console.log(`Downloading TokenTracker v${version}...`);
90
+ await downloadFile(url, debPath);
91
+ }
92
+
93
+ return debPath;
94
+ }
95
+
96
+ async function installAndRun() {
97
+ try {
98
+ const version = process.env.TOKEN_TRACKER_VERSION || await getLatestVersion();
99
+ const debPath = await ensureInstalled(version);
100
+
101
+ console.log(`Installing TokenTracker v${version}...`);
102
+ const dpkg = spawn("sudo", ["dpkg", "-i", debPath], { stdio: "inherit" });
103
+ dpkg.on("close", (code) => {
104
+ if (code === 0) {
105
+ console.log("TokenTracker installed successfully!");
106
+ console.log('Run "tokentracker" to start.');
107
+ } else {
108
+ console.error("Installation failed. Try running with sudo.");
109
+ }
110
+ });
111
+ } catch (err) {
112
+ console.error("Error:", err.message);
113
+ console.error("\nDownload manually: https://github.com/ayan-de/Token-Tracker/releases");
114
+ process.exit(1);
115
+ }
116
+ }
117
+
118
+ const args = process.argv.slice(2);
119
+
120
+ if (args.includes("--install") || args.includes("-i")) {
121
+ installAndRun();
122
+ } else {
123
+ // Direct execution path
124
+ const tokentrackerBin = "/usr/lib/TokenTracker/_up_/target/release/backend";
125
+ const tauriBin = "/usr/bin/tokentracker";
126
+
127
+ let binPath = null;
128
+ if (fs.existsSync(tauriBin)) binPath = tauriBin;
129
+ else if (fs.existsSync(tokentrackerBin)) binPath = tokentrackerBin;
130
+
131
+ if (!binPath) {
132
+ console.log("TokenTracker not found. Installing...");
133
+ installAndRun();
134
+ return;
135
+ }
136
+
137
+ // Spawn the actual app
138
+ const child = spawn(binPath, [], {
139
+ stdio: "inherit",
140
+ env: { ...process.env },
141
+ });
142
+ child.on("close", (code) => process.exit(code));
143
+ }
package/package.json ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "@thisisayande/tokentracker",
3
+ "version": "0.1.5",
4
+ "private": false,
5
+ "type": "module",
6
+ "description": "AI Provider Quota Monitor - track usage, rate limits, and spend across 40+ LLM providers",
7
+ "bin": {
8
+ "tokentracker": "./bin/tokentracker.js"
9
+ },
10
+ "scripts": {
11
+ "dev": "next dev",
12
+ "build": "next build",
13
+ "tauri": "tauri",
14
+ "tauri:dev": "tauri dev",
15
+ "tauri:build": "next build && tauri build",
16
+ "test:smoke": "playwright test",
17
+ "test:smoke:ui": "playwright test --ui",
18
+ "postinstall": "node scripts/postinstall.js"
19
+ },
20
+ "files": [
21
+ "bin/",
22
+ "dist/"
23
+ ],
24
+ "publishConfig": {
25
+ "access": "public",
26
+ "registry": "https://registry.npmjs.org/"
27
+ },
28
+ "devDependencies": {
29
+ "@playwright/test": "^1.61.1",
30
+ "@tailwindcss/postcss": "^4.3.1",
31
+ "@tauri-apps/cli": "^2",
32
+ "@types/node": "^26.0.0",
33
+ "@types/react": "^19.2.17",
34
+ "autoprefixer": "^10.5.0",
35
+ "postcss": "^8.5.15",
36
+ "tailwindcss": "^4.3.1",
37
+ "typescript": "^6.0.3"
38
+ },
39
+ "dependencies": {
40
+ "@tauri-apps/api": "^2.11.1",
41
+ "lucide-react": "^1.21.0",
42
+ "next": "^16.2.9",
43
+ "react": "^19.2.7",
44
+ "react-dom": "^19.2.7"
45
+ }
46
+ }