tokentrax 0.0.0-stage → 0.1.1

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.
Files changed (51) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +412 -2
  3. package/assets/built-with-ai-assistance.png +0 -0
  4. package/assets/tokentrax.png +0 -0
  5. package/bin/postinstall.js +61 -0
  6. package/bin/start-server.sh +38 -0
  7. package/bin/stop-server.sh +18 -0
  8. package/bin/tracker.js +59 -0
  9. package/dashboard/index.html +9427 -0
  10. package/data/curated-overrides.json +46 -0
  11. package/data/pricing-seed.json +26 -0
  12. package/package.json +52 -4
  13. package/src/cli.js +94 -0
  14. package/src/commands/backup.js +214 -0
  15. package/src/commands/costs.js +78 -0
  16. package/src/commands/doctor.js +173 -0
  17. package/src/commands/export.js +74 -0
  18. package/src/commands/init.js +156 -0
  19. package/src/commands/migratePricing.js +131 -0
  20. package/src/commands/price.js +93 -0
  21. package/src/commands/serve.js +1132 -0
  22. package/src/commands/status.js +198 -0
  23. package/src/commands/sync.js +1001 -0
  24. package/src/commands/syncPricing.js +24 -0
  25. package/src/commands/top.js +95 -0
  26. package/src/commands/uninstall.js +79 -0
  27. package/src/lib/config.js +54 -0
  28. package/src/lib/fs.js +111 -0
  29. package/src/lib/hooks.js +466 -0
  30. package/src/lib/local-time.js +193 -0
  31. package/src/lib/model.js +28 -0
  32. package/src/lib/normalization.js +49 -0
  33. package/src/lib/parsers/claude.js +75 -0
  34. package/src/lib/parsers/codebuddy.js +97 -0
  35. package/src/lib/parsers/copilot.js +89 -0
  36. package/src/lib/parsers/cursor.js +60 -0
  37. package/src/lib/parsers/gemini.js +97 -0
  38. package/src/lib/parsers/hermes.js +112 -0
  39. package/src/lib/parsers/kimi.js +97 -0
  40. package/src/lib/parsers/kiro.js +112 -0
  41. package/src/lib/parsers/omp.js +97 -0
  42. package/src/lib/parsers/opencode.js +131 -0
  43. package/src/lib/parsers/opencodedb.js +189 -0
  44. package/src/lib/parsers/pi.js +166 -0
  45. package/src/lib/parsers/rollout.js +106 -0
  46. package/src/lib/parsers/stream.js +117 -0
  47. package/src/lib/pricing-fetch.js +130 -0
  48. package/src/lib/pricing-overrides.js +108 -0
  49. package/src/lib/pricing.js +181 -0
  50. package/src/lib/sqlite.js +1657 -0
  51. package/src/lib/ui.js +153 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Josh Greene
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 CHANGED
@@ -1,3 +1,413 @@
1
- # Temporary Holding Version
1
+ <img src="assets/tokentrax.png" width=50%>
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ **Lean, local-first AI token usage tracker — zero bloat**
4
+
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://opensource.org/licenses/MIT)
6
+ [![Node](https://img.shields.io/badge/node-%3E%3D20-brightgreen)](https://nodejs.org) <img src="assets/built-with-ai-assistance.png" height=20>
7
+ <img src="assets/phone-high.jpg" alt="TokenTrax Dashboard" width="100%">
8
+
9
+ ---
10
+
11
+ ## Installation
12
+
13
+ ### Via npm (recommended)
14
+
15
+ ```bash
16
+ npm install -g tokentrax --allow-scripts=better-sqlite3
17
+ ```
18
+
19
+ > **Why the flag?** npm 12 and newer block dependency install scripts by
20
+ > default, and `better-sqlite3` is a native module — its install script is
21
+ > what fetches or builds the binding. Without the flag you get an install
22
+ > that *looks* fine and then fails the first time it opens a database.
23
+ > Older npm versions ignore the flag, so the command works everywhere.
24
+ >
25
+ > If you'd rather set it once:
26
+ >
27
+ > ```bash
28
+ > npm config set allow-scripts=better-sqlite3 --location=user
29
+ > ```
30
+
31
+ ### Via git clone
32
+
33
+ ```bash
34
+ git clone https://github.com/jeshuwah/TokenTrax.git
35
+ cd tokentrax
36
+ ./install.sh
37
+ ```
38
+
39
+ Or manually:
40
+
41
+ ```bash
42
+ git clone https://github.com/jeshuwah/TokenTrax.git
43
+ cd tokentrax
44
+ npm install
45
+ npm link
46
+ ```
47
+
48
+ This installs TokenTrax globally and automatically:
49
+
50
+ - Runs database migrations (schema updates are applied automatically)
51
+ - Backfills pricing for any existing data without costs
52
+
53
+ ### Requirements
54
+
55
+ - Node.js >= 20
56
+ - `better-sqlite3` (installed automatically as a native module)
57
+
58
+ ### Setup
59
+
60
+ ```bash
61
+ tokentrax init # Install hooks for detected AI tools
62
+ tokentrax sync # Parse existing logs → SQLite
63
+ tokentrax serve # Open dashboard at localhost:7680
64
+ ```
65
+
66
+ ### Updating
67
+
68
+ ```bash
69
+ npm update -g tokentrax
70
+ ```
71
+
72
+ Updates are safe — the postinstall script automatically migrates your existing database schema and backfills any missing pricing data.
73
+
74
+ ### Remote agent
75
+
76
+ To report usage from a *second* machine — a laptop, a work box, anywhere
77
+ with an AI harness — install the agent there. It ships as its own npm
78
+ package:
79
+
80
+ ```bash
81
+ npm install -g tokentrax-agent --allow-scripts=better-sqlite3
82
+ tokentrax-agent setup --server http://your-dashboard:7680 --key <sync-key>
83
+ ```
84
+
85
+ The `--allow-scripts` flag is for npm 12+, which blocks dependency install
86
+ scripts by default — `better-sqlite3` needs its install script to build a
87
+ native module. Older npm ignores the flag. See the note in the
88
+ [Installation](#installation) section above.
89
+
90
+ `setup` writes the configuration, installs a systemd user timer, and starts it.
91
+ The machine then reports its sessions, tokens and cost to the dashboard every
92
+ five minutes.
93
+
94
+ No npm? Bootstrap straight from the repository:
95
+
96
+ ```bash
97
+ curl -fsSL https://raw.githubusercontent.com/jeshuwah/TokenTrax/main/agent/install.sh \
98
+ | bash -s -- --server http://your-dashboard:7680 --key <sync-key>
99
+ ```
100
+
101
+ Full details in [`agent/README.md`](agent/README.md) and
102
+ [`docs/agent-configuration.md`](docs/agent-configuration.md). See
103
+ [Remote agents](#remote-agents) for how it fits together.
104
+
105
+ ---
106
+
107
+ ## Features
108
+
109
+ - **Zero runtime dependencies** — only `better-sqlite3` (native module)
110
+ - **Single-file dashboard** — no bundler, no framework, no build step
111
+ - **SQLite storage** — indexed upserts, bounded file size, WAL mode
112
+ - **12 AI tool integrations** — Claude Code, Codex, Gemini, Cursor, Kiro, Hermes, and more
113
+ - **Bundled + live pricing** — 2200+ model prices cached locally, auto-refreshed
114
+ - **Normalization engine** — maps 100+ model aliases to canonical names
115
+ - **Privacy-first** — all data stays in `~/.tokentrax/`, no telemetry
116
+ - **Optional remote agents** — one dashboard, many machines, via `tokentrax-agent`
117
+
118
+ ---
119
+
120
+ ## Dashboard
121
+
122
+ ### Overview
123
+
124
+ Real-time metrics, daily usage chart, and source breakdown — everything at a glance.
125
+
126
+ ### Activity Heatmap
127
+
128
+ GitHub-style contribution grid showing weekly token usage across 52 weeks.
129
+
130
+ <img src="docs/screenshots/heatmap.png" alt="Activity Heatmap" width="100%">
131
+
132
+ ### Models Breakdown
133
+
134
+ Per-model and per-source token tracking with cost estimates, sorted by usage.
135
+
136
+ <img src="docs/screenshots/models.png" alt="Models Breakdown" width="100%">
137
+
138
+ ### Import & Upload
139
+
140
+ Drag-and-drop JSONL/CSV files from unsupported tools, or upload API usage logs.
141
+
142
+ <img src="docs/screenshots/import.png" alt="Import & Upload" width="100%">
143
+
144
+ ### Settings
145
+
146
+ Theme toggle (dark/light), pricing freshness indicator, normalization rule editor, and sync controls.
147
+
148
+ <img src="docs/screenshots/settings.png" alt="Settings" width="100%">
149
+
150
+ ### Agents
151
+
152
+ Every machine reporting into this dashboard — sessions, tokens, cost and
153
+ context-reduction percentage — with a per-machine breakdown by model and
154
+ per-session detail. See [Remote agents](#remote-agents).
155
+
156
+ ---
157
+
158
+ ## Supported AI Tools
159
+
160
+ | Tool | Integration | Type |
161
+ | ------ | ------------- | ------ |
162
+ | Claude Code | SessionEnd hook | Hook |
163
+ | Codex CLI | TOML notify | Hook |
164
+ | Gemini CLI | SessionEnd hook | Hook |
165
+ | CodeBuddy | SessionEnd hook | Hook |
166
+ | Every Code | TOML notify | Hook |
167
+ | OpenCode | Plugin + SQLite | Hook + Passive |
168
+ | Cursor | API + SQLite | Passive |
169
+ | Kiro | SQLite + JSONL | Passive |
170
+ | Hermes | SQLite | Passive |
171
+ | GitHub Copilot | OpenTelemetry | Passive |
172
+ | Kimi Code | JSONL reader | Passive |
173
+ | oh-my-pi | JSONL reader | Passive |
174
+ | Pi Coding Agent | JSONL reader | Passive |
175
+
176
+ ---
177
+
178
+ ## Remote agents
179
+
180
+ TokenTrax is local-first: one machine, one SQLite file, no server. The
181
+ optional `tokentrax-agent` package adds the other half — reporting from
182
+ *other* machines into the same dashboard.
183
+
184
+ ```
185
+ laptop (omarchy) server (olympus)
186
+ ┌──────────────────────┐ ┌────────────────────────┐
187
+ │ pi / Claude / Codex │ POST │ tokentrax serve :7680 │
188
+ │ tokentrax-agent │────────▶│ agent_sessions table │
189
+ │ systemd timer 5min │ │ "Agents" dashboard tab │
190
+ └──────────────────────┘ └────────────────────────┘
191
+ ```
192
+
193
+ Each remote machine runs the agent on a timer. It reads that machine's
194
+ harness session data, condenses it, and POSTs it to the dashboard. The
195
+ dashboard shows every machine in one list with sessions, tokens, cost and
196
+ context-reduction percentage.
197
+
198
+ ### Install on the remote machine
199
+
200
+ ```bash
201
+ npm install -g tokentrax-agent --allow-scripts=better-sqlite3
202
+ tokentrax-agent setup --server http://your-dashboard:7680 --key <sync-key>
203
+ ```
204
+
205
+ `setup` writes the configuration, verifies the native SQLite binding loads,
206
+ installs a systemd user service and timer, and enables it. It is safe to
207
+ re-run.
208
+
209
+ ### Prepare the dashboard
210
+
211
+ The server needs a shared key before it will accept anything:
212
+
213
+ ```bash
214
+ export TOKENTRAX_SYNC_KEY=$(openssl rand -base64 48)
215
+ npm start serve
216
+ ```
217
+
218
+ The same value goes on every remote machine. With a key set, the server
219
+ refuses writes from anything that is not loopback, so it is safe to expose
220
+ on a LAN or over a VPN.
221
+
222
+ ### What crosses the wire
223
+
224
+ Session metadata and usage numbers only — never prompts, file contents or
225
+ tool arguments. Project paths are hashed by default
226
+ (`TOKENTRAX_TRACK_PROJECT=hash`), so the dashboard never learns your
227
+ directory names. Set it to `full` only if you want the real paths.
228
+
229
+ ### A note on hooks
230
+
231
+ The agent installs post-session hooks for harnesses that support them. The
232
+ harness with the most data — pi — has no hook mechanism at all, so a hook
233
+ would silently never fire there. The systemd timer is what actually keeps
234
+ the data flowing, and it works for every harness.
235
+
236
+ ### Agent commands
237
+
238
+ | Command | Description |
239
+ | --------- | ------------- |
240
+ | `tokentrax-agent setup --server <url> --key <key>` | Configure the machine and start the timer |
241
+ | `tokentrax-agent sync [--auto]` | Send new sessions to the server |
242
+ | `tokentrax-agent status` | Show configuration, machine id and cursor |
243
+ | `tokentrax-agent init` | Install post-session hooks where supported |
244
+ | `tokentrax-agent uninstall` | Remove post-session hooks |
245
+
246
+ See [`agent/README.md`](agent/README.md) for the full package documentation.
247
+
248
+ ---
249
+
250
+ ## Commands
251
+
252
+ ### Core Commands
253
+
254
+ | Command | Description |
255
+ | --------- | ------------- |
256
+ | `tokentrax` | Open dashboard (alias for `serve`) |
257
+ | `tokentrax serve [--port N] [--no-browser]` | Start HTTP server on :7680 |
258
+ | `tokentrax init [--force] [--source NAME]` | Install hooks for detected AI tools |
259
+ | `tokentrax sync [--auto] [--source NAME]` | Parse logs → SQLite |
260
+ | `tokentrax sync-pricing` | Fetch latest model prices from OpenRouter |
261
+ | `tokentrax migrate-pricing` | Lock costs at current prices (one-time) |
262
+ | `tokentrax status [--verbose]` | Check hook configuration |
263
+ | `tokentrax doctor [--fix]` | Health check (DB, paths, hooks) |
264
+ | `tokentrax uninstall [--purge]` | Remove hooks, optionally delete all data |
265
+
266
+ ### Query Commands
267
+
268
+ | Command | Description |
269
+ | --------- | ------------- |
270
+ | `tokentrax costs [--days N] [--source NAME] [--json]` | Show cost summary |
271
+ | `tokentrax top [--limit N] [--days N] [--by model\|source]` | Top models/sources by usage |
272
+ | `tokentrax price [model] [--json]` | Look up model prices |
273
+ | `tokentrax export [--format csv\|json] [--days N] [--source NAME]` | Export usage data |
274
+
275
+ ---
276
+
277
+ ## Data & Privacy
278
+
279
+ All data stored in `~/.tokentrax/`:
280
+
281
+ | File | Purpose |
282
+ | ------ | --------- |
283
+ | `tokentrax.db` | SQLite database with token records |
284
+ | `pricing-cache.json` | Cached model prices (auto-refreshed) |
285
+ | `model-norm.json` | Custom model name normalization rules |
286
+ | `config.json` | User preferences (theme, timezone) |
287
+
288
+ ### Environment Variables
289
+
290
+ | Variable | Description | Default |
291
+ | ---------- | ------------- | --------- |
292
+ | `TOKENTRAX_DEBUG` | Enable debug output | — |
293
+ | `TOKENTRAX_HOME` | Override TokenTrax data directory | `~/.tokentrax` |
294
+ | `TOKENTRAX_CONFIG_PATH` | Override config file path | `~/.tokentrax/config.json` |
295
+ | `GEMINI_HOME` | Override Gemini config dir | `~/.gemini` |
296
+ | `CODEX_HOME` | Override Codex config dir | `~/.codex` |
297
+ | `CLAUDE_HOME` | Override Claude config dir | `~/.claude` |
298
+ | `CODEBUDDY_HOME` | Override Codebuddy config dir | `~/.codebuddy` |
299
+ | `EVERYCODE_HOME` | Override Everycode config dir | `~/.code` |
300
+ | `PI_CODING_AGENT_DIR` | Override Pi Coding Agent base directory | `~/.pi/agent` |
301
+ | `PI_CODING_AGENT_SESSION_DIR` | Override Pi sessions directory | `~/.pi/agent/sessions` |
302
+ | `COPILOT_OTEL_FILE_EXPORTER_PATH` | Override Copilot OTEL export directory | — |
303
+
304
+ ---
305
+
306
+ ## Privacy
307
+
308
+ - **No content uploaded** — only token counts and timestamps are stored
309
+ - **Local-only by default** — all data in `~/.tokentrax/tokentrax.db`
310
+ - **Auditable** — open source, read the parser code in `src/lib/parsers/`
311
+ - **No telemetry** — no analytics, no crash reporting, no phone-home
312
+
313
+ ### …unless you install the remote agent
314
+
315
+ `tokentrax-agent` is the one exception, and it is entirely opt-in: you
316
+ install it, on a machine you choose, and point it at a dashboard you
317
+ control. It sends session metadata and usage numbers to that one URL —
318
+ never prompts, file contents or tool arguments — and it sends nothing at
319
+ all until you run `tokentrax-agent setup`.
320
+
321
+ TokenTrax itself never phones home. The agent is a separate package, so
322
+ installing TokenTrax does not install it.
323
+
324
+ See [Remote agents](#remote-agents).
325
+
326
+ ---
327
+
328
+ ## Development
329
+
330
+ ```bash
331
+ git clone https://github.com/jeshuwah/TokenTrax.git
332
+ cd tokentrax
333
+ npm install
334
+ npm start # Run CLI
335
+ npm test # Run tests
336
+ ```
337
+
338
+ ### Project Structure
339
+
340
+ ```
341
+ tokentrax/
342
+ ├── bin/tracker.js # npm entry point
343
+ ├── src/
344
+ │ ├── cli.js # Command dispatcher
345
+ │ ├── commands/ # init, sync, serve, status, doctor, uninstall
346
+ │ └── lib/
347
+ │ ├── sqlite.js # DB schema + upsert logic
348
+ │ ├── pricing.js # Cost computation
349
+ │ └── parsers/ # One parser per AI tool (13 total)
350
+ ├── agent/ # tokentrax-agent — separate npm package
351
+ │ ├── bin/agent.js # Remote agent entry point
352
+ │ ├── install.sh # Bootstrap script
353
+ │ └── src/ # setup, sync, session parsing, hooks
354
+ ├── dashboard/
355
+ │ └── index.html # Single-file SPA dashboard
356
+ ├── docs/
357
+ │ ├── screenshots/ # Dashboard screenshots
358
+ │ └── IMPLEMENTATION.md # Architecture & design notes
359
+ ├── data/ # Bundled pricing seed data
360
+ └── tests/ # Test suite
361
+ ```
362
+
363
+ ---
364
+
365
+ ## Comparison: TokenTracker vs TokenTrax
366
+
367
+ | Metric | TokenTracker | TokenTrax |
368
+ | -------- | -------------- | ----------- |
369
+ | CLI LOC | ~8000 | ~2500 |
370
+ | Dashboard files | 146 | 1 |
371
+ | Runtime deps | 413 packages | 1 (better-sqlite3) |
372
+ | Storage | queue.jsonl | SQLite |
373
+ | Pricing | Live API + cache | Cached + auto-refresh |
374
+ | Build step | Vite + TypeScript | None |
375
+ | Dashboard size | ~2MB | ~10KB |
376
+
377
+ ---
378
+
379
+ ## Troubleshooting
380
+
381
+ ### Database locked
382
+
383
+ ```bash
384
+ pkill -f tokentrax # Kill any running server
385
+ rm ~/.tokentrax/tokentrax.db-wal
386
+ rm ~/.tokentrax/tokentrax.db-shm
387
+ ```
388
+
389
+ ### Hook not triggering
390
+
391
+ ```bash
392
+ tokentrax status # Check configuration
393
+ tokentrax uninstall
394
+ tokentrax init # Reinstall hooks
395
+ ```
396
+
397
+ ### Missing provider
398
+
399
+ 1. Verify the tool is installed (e.g., `claude --version`)
400
+ 2. Check the config file exists (e.g., `~/.claude/settings.json`)
401
+ 3. Run `tokentrax init --source <name>` to force installation
402
+
403
+ ---
404
+
405
+ ## License
406
+
407
+ [MIT](LICENSE)
408
+
409
+ ---
410
+
411
+ ## Credits
412
+
413
+ Inspired by [TokenTracker](https://github.com/mm7894215/TokenTracker). TokenTrax is a minimal reimplementation focused on local-only usage with zero bloat.
Binary file
@@ -0,0 +1,61 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * tokentrax postinstall
4
+ *
5
+ * Runs automatically after `npm install` or `npm update`.
6
+ * Handles database migrations silently to ensure schema is up-to-date.
7
+ */
8
+ const { createDB } = require("../src/lib/sqlite");
9
+
10
+ try {
11
+ // Initialize DB (triggers schema migrations automatically)
12
+ const db = createDB();
13
+
14
+ // Check if there are any token rows without pricing
15
+ const unpricedCount = db.db
16
+ .prepare("SELECT COUNT(*) as cnt FROM tokens WHERE total_cost_usd IS NULL")
17
+ .get();
18
+
19
+ if (unpricedCount.cnt > 0) {
20
+ // Silently migrate pricing for existing rows
21
+ // This is safe to run multiple times - it only updates NULL values
22
+ const { getPricing, computeCost } = require("../src/lib/pricing");
23
+
24
+ const CHUNK = 1000;
25
+ const updateStmt = db.db.prepare(
26
+ "UPDATE tokens SET total_cost_usd = ? WHERE id = ? AND total_cost_usd IS NULL",
27
+ );
28
+
29
+ const migrate = db.db.transaction(() => {
30
+ let rowsUpdated;
31
+
32
+ do {
33
+ const rows = db.db
34
+ .prepare(`
35
+ SELECT id, hour_start, source, model, input_tokens, output_tokens,
36
+ cached_input_tokens, cache_creation_input_tokens,
37
+ reasoning_output_tokens, conversation_count
38
+ FROM tokens WHERE total_cost_usd IS NULL
39
+ LIMIT ?
40
+ `)
41
+ .all(CHUNK);
42
+
43
+ for (const row of rows) {
44
+ const pricing = getPricing(row.model);
45
+ const cost = computeCost(row, pricing);
46
+ updateStmt.run(cost, row.id);
47
+ }
48
+
49
+ rowsUpdated = rows.length;
50
+ } while (rowsUpdated === CHUNK);
51
+ });
52
+
53
+ migrate();
54
+ }
55
+
56
+ db.close();
57
+ } catch (err) {
58
+ // Non-fatal: log but don't fail install
59
+ console.error("tokentrax: postinstall warning:", err.message);
60
+ process.exit(0);
61
+ }
@@ -0,0 +1,38 @@
1
+ #!/bin/bash
2
+ set -e
3
+
4
+ PID_FILE="${TOKENTRAX_PID_FILE:-$HOME/.tokentrax/server.pid}"
5
+ PORT="${TOKENTRAX_PORT:-7680}"
6
+ # Default to localhost. Set TOKENTRAX_HOST=0.0.0.0 explicitly to expose on LAN.
7
+ # WARNING: exposing 0.0.0.0 without setting TOKENTRAX_SYNC_KEY leaves mutating
8
+ # endpoints (/api/upload, /api/settings, /api/agent-sync) unauthenticated.
9
+ HOST="${TOKENTRAX_HOST:-localhost}"
10
+
11
+ cd "$(dirname "$0")/.."
12
+
13
+ if [ -f "$PID_FILE" ]; then
14
+ OLD_PID=$(cat "$PID_FILE")
15
+ if kill -0 "$OLD_PID" 2>/dev/null; then
16
+ echo "Stopping existing server (PID $OLD_PID)..."
17
+ kill "$OLD_PID" 2>/dev/null || true
18
+ sleep 1
19
+ fi
20
+ rm -f "$PID_FILE"
21
+ fi
22
+
23
+ echo "Starting TokenTrax dashboard at http://$HOST:$PORT ..."
24
+
25
+ # Run node in the foreground so systemd (Type=simple) supervises the real
26
+ # server process instead of the exiting wrapper. Disable JOBCTL errexit
27
+ # just for the launch so a non-zero exit from the server itself still
28
+ # propagates through `exec` rather than being masked by `set -e`.
29
+ set +e
30
+ echo "$$" > "$PID_FILE"
31
+ echo "Server starting (wrapper PID $$, will exec node)"
32
+ # Print the address the server is actually bound to. This used to be hardcoded
33
+ # to localhost, which contradicted the line above whenever TOKENTRAX_HOST was
34
+ # set to a LAN or container address — the log advertised a URL that refused
35
+ # connections while the real one worked.
36
+ echo "Dashboard: http://$HOST:$PORT"
37
+ echo "PID saved to: $PID_FILE"
38
+ exec node bin/tracker.js serve --host "$HOST" --port "$PORT" --no-open
@@ -0,0 +1,18 @@
1
+ #!/bin/bash
2
+
3
+ PID_FILE="${TOKENTRAX_PID_FILE:-$HOME/.tokentrax/server.pid}"
4
+
5
+ if [ -f "$PID_FILE" ]; then
6
+ OLD_PID=$(cat "$PID_FILE")
7
+ if kill -0 "$OLD_PID" 2>/dev/null; then
8
+ echo "Stopping server (PID $OLD_PID)..."
9
+ kill "$OLD_PID"
10
+ rm -f "$PID_FILE"
11
+ echo "Server stopped"
12
+ else
13
+ echo "Server not running (stale PID file)"
14
+ rm -f "$PID_FILE"
15
+ fi
16
+ else
17
+ echo "No PID file found"
18
+ fi
package/bin/tracker.js ADDED
@@ -0,0 +1,59 @@
1
+ #!/usr/bin/env node
2
+ // Entry point for the TokenTrax CLI.
3
+ //
4
+ // One failure deserves a friendly explanation: a missing better-sqlite3
5
+ // native binding. npm 12+ blocks dependency install scripts by default, and
6
+ // better-sqlite3's binding is built by its install script. `require()` of
7
+ // the module succeeds without it, so the failure only surfaces when a
8
+ // database is opened — which for `tokentrax sync` is a runtime error deep
9
+ // inside a parser.
10
+ //
11
+ // Left alone, the user gets a wall of "Could not locate the bindings file.
12
+ // Tried: ..." listing eight paths, which tells them nothing about what to
13
+ // do about it.
14
+
15
+ /**
16
+ * Is this the missing-native-binding failure?
17
+ * @param {unknown} err
18
+ * @returns {boolean}
19
+ */
20
+ function isMissingBinding(err) {
21
+ const message = String((err && err.message) || "");
22
+ return /bindings|Could not locate the bindings file/i.test(message);
23
+ }
24
+
25
+ function explainMissingBinding() {
26
+ console.error(
27
+ "\n✗ better-sqlite3's native binding is missing.\n\n" +
28
+ " This normally means the package's install script was blocked.\n" +
29
+ " Fix it with:\n\n" +
30
+ " npm install-scripts approve better-sqlite3\n" +
31
+ " npm rebuild better-sqlite3\n\n" +
32
+ " Or reinstall with:\n\n" +
33
+ " npm install -g tokentrax --allow-scripts=better-sqlite3\n",
34
+ );
35
+ }
36
+
37
+ let run;
38
+ try {
39
+ ({ run } = require("../src/cli.js"));
40
+ } catch (err) {
41
+ if (isMissingBinding(err)) {
42
+ explainMissingBinding();
43
+ process.exit(1);
44
+ }
45
+ throw err;
46
+ }
47
+
48
+ (async () => {
49
+ try {
50
+ await run(process.argv.slice(2));
51
+ } catch (err) {
52
+ if (isMissingBinding(err)) {
53
+ explainMissingBinding();
54
+ process.exit(1);
55
+ }
56
+ console.error("tokentrax:", err.message);
57
+ process.exit(1);
58
+ }
59
+ })();