tokentrace 0.14.2 → 0.15.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/CHANGELOG.md CHANGED
@@ -4,6 +4,26 @@ All notable changes to TokenTrace are documented here.
4
4
 
5
5
  ## Unreleased
6
6
 
7
+ ## [0.15.0] - 2026-05-22
8
+
9
+ ### Changed
10
+
11
+ - Bumped minimum Node.js to 20.0.0 (Node 18 LTS reached end of life on 2025-04-30). This is a breaking change for users still on Node 18.
12
+ - Upgraded `better-sqlite3` from 11 to 12 (latest major).
13
+ - Upgraded `lucide-react` from 0.468 to 1.16 (first stable major release).
14
+ - Upgraded `open` from 10 to 11 (now requires Node 20+, aligns with engines bump).
15
+ - Bumped minor/patch versions for `tsx`, `postcss`, `autoprefixer`, `@types/react`, `@types/node`.
16
+
17
+ ### Fixed
18
+
19
+ - `sqlite-history` adapter now uses SQLite identifier quoting (`""`) instead of `JSON.stringify()` when interpolating user table names into raw queries. Previously, a table name containing a quote could have caused a SQL parse error during read-only ingestion.
20
+ - Made `eslint-plugin-react-hooks` an explicit devDependency so lint setup no longer relies on accidental dependency hoisting from `eslint-config-next`.
21
+ - Raised global Vitest `testTimeout`/`hookTimeout` to 30s and bumped per-test/spawn timeouts in CLI subprocess tests (`mcp-server`, `serve-command`, `statusline-cli`) so they are no longer flaky under macOS process-spawn latency.
22
+
23
+ ### Documentation
24
+
25
+ - Added a Troubleshooting section to the README covering the `prebuild-install` deprecation warning from `better-sqlite3`, native build errors, and `EBADENGINE` warnings.
26
+
7
27
  ## [0.14.2] - 2026-05-21
8
28
 
9
29
  ### Added
package/README.md CHANGED
@@ -244,8 +244,6 @@ The CLI sets `TOKENTRACE_DB` and `DATABASE_URL` automatically. You can override
244
244
  TOKENTRACE_HOME=/custom/path tokentrace
245
245
  ```
246
246
 
247
- If `npm install -g tokentrace` prints a `prebuild-install` deprecation warning, it is from the native SQLite dependency chain used by `better-sqlite3`. The install should continue normally, and TokenTrace still runs locally.
248
-
249
247
  ## Where TokenTrace Looks
250
248
 
251
249
  Default discovery checks these locations when present:
@@ -478,6 +476,28 @@ The ingestion system is intentionally pluggable:
478
476
 
479
477
  Contributions are welcome. See [CONTRIBUTING.md](CONTRIBUTING.md) for local setup, parser guidelines, pricing update notes, and the release policy.
480
478
 
479
+ ## Troubleshooting
480
+
481
+ ### `prebuild-install` deprecation warning during install
482
+
483
+ ```
484
+ npm warn deprecated prebuild-install@7.1.3: No longer maintained...
485
+ ```
486
+
487
+ This is a transitive dependency of `better-sqlite3` (the native SQLite driver TokenTrace uses to store local data). The warning is harmless — the install completes normally and TokenTrace runs as expected. It will go away once `better-sqlite3` upstream migrates to a different prebuilt binary loader; there is nothing to fix on the TokenTrace side.
488
+
489
+ ### Native build errors on `better-sqlite3`
490
+
491
+ If the prebuilt binary cannot be downloaded (offline machine, restrictive proxy, unsupported Node ABI), `better-sqlite3` will try to compile from source and may fail. Workarounds:
492
+
493
+ - Ensure Node.js is a supported LTS (TokenTrace requires `>= 20.0.0`; Node 20 or 22 LTS is recommended).
494
+ - Install build tools: Xcode Command Line Tools on macOS, `build-essential` and `python3` on Linux, or the "Desktop development with C++" workload on Windows.
495
+ - Retry the install with network access to `github.com` so the prebuilt binary can be fetched.
496
+
497
+ ### `EBADENGINE` warnings
498
+
499
+ These appear when your local Node version is older than the `engines` field of a transitive dependency. They are warnings, not errors. Upgrading to the latest Node LTS resolves them.
500
+
481
501
  ## Known Limitations
482
502
 
483
503
  - Claude Code and Codex CLI log formats are inferred defensively and may need refinement with real sample logs.
@@ -2909,12 +2909,15 @@ import Database2 from "better-sqlite3";
2909
2909
  function openReadonly(filePath) {
2910
2910
  return new Database2(filePath, { readonly: true, fileMustExist: true });
2911
2911
  }
2912
+ function quoteIdent(name) {
2913
+ return `"${name.replace(/"/g, '""')}"`;
2914
+ }
2912
2915
  function candidateTables(db2) {
2913
2916
  const tables = db2.prepare("SELECT name FROM sqlite_master WHERE type = 'table' AND name NOT LIKE 'sqlite_%'").all();
2914
2917
  return tables.map((table) => table.name).filter((name) => usageTableNames.includes(name) || /usage|token|message|session/i.test(name));
2915
2918
  }
2916
2919
  function columnsFor(db2, tableName) {
2917
- const columns = db2.prepare(`PRAGMA table_info(${JSON.stringify(tableName)})`).all();
2920
+ const columns = db2.prepare(`PRAGMA table_info(${quoteIdent(tableName)})`).all();
2918
2921
  return new Set(columns.map((column) => column.name.toLowerCase()));
2919
2922
  }
2920
2923
  function hasUsageShape(columns) {
@@ -2923,7 +2926,7 @@ function hasUsageShape(columns) {
2923
2926
  return hasModel && hasUsage2;
2924
2927
  }
2925
2928
  function tableRows(db2, tableName) {
2926
- return db2.prepare(`SELECT * FROM ${JSON.stringify(tableName)} LIMIT 50000`).all();
2929
+ return db2.prepare(`SELECT * FROM ${quoteIdent(tableName)} LIMIT 50000`).all();
2927
2930
  }
2928
2931
  function providerId(value) {
2929
2932
  if (!value) return "local";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tokentrace",
3
- "version": "0.14.2",
3
+ "version": "0.15.0",
4
4
  "mcpName": "io.github.abhiyoheswaran1/tokentrace",
5
5
  "description": "Local-first dashboard for AI CLI token, cost, and session analytics.",
6
6
  "author": {
@@ -74,10 +74,10 @@
74
74
  "prepack": "npm run build:runtime"
75
75
  },
76
76
  "overrides": {
77
- "postcss": "^8.5.14"
77
+ "postcss": "^8.5.15"
78
78
  },
79
79
  "engines": {
80
- "node": ">=18.18.0"
80
+ "node": ">=20.0.0"
81
81
  },
82
82
  "keywords": [
83
83
  "tokens",
@@ -94,24 +94,24 @@
94
94
  ],
95
95
  "license": "MIT",
96
96
  "dependencies": {
97
- "@types/node": "^22.10.5",
98
- "@types/react": "^19.0.4",
99
97
  "@radix-ui/react-slot": "^1.1.1",
100
- "better-sqlite3": "^11.7.0",
98
+ "@types/node": "^22.19.19",
99
+ "@types/react": "^19.2.15",
100
+ "autoprefixer": "^10.5.0",
101
+ "better-sqlite3": "^12.10.0",
101
102
  "class-variance-authority": "^0.7.1",
102
103
  "clsx": "^2.1.1",
103
104
  "drizzle-orm": "^0.45.2",
104
105
  "get-port": "^7.2.0",
105
- "lucide-react": "^0.468.0",
106
+ "lucide-react": "^1.16.0",
106
107
  "next": "^15.5.18",
107
- "open": "^10.1.0",
108
- "autoprefixer": "^10.4.20",
109
- "postcss": "^8.5.14",
108
+ "open": "^11.0.0",
109
+ "postcss": "^8.5.15",
110
110
  "react": "^19.0.0",
111
111
  "react-dom": "^19.0.0",
112
112
  "recharts": "^2.15.0",
113
- "tailwindcss": "^3.4.17",
114
113
  "tailwind-merge": "^2.6.0",
114
+ "tailwindcss": "^3.4.17",
115
115
  "typescript": "^5.7.2"
116
116
  },
117
117
  "devDependencies": {
@@ -119,9 +119,10 @@
119
119
  "@types/better-sqlite3": "^7.6.12",
120
120
  "@types/react-dom": "^19.0.2",
121
121
  "esbuild": "^0.28.0",
122
- "eslint": "^9.17.0",
122
+ "eslint": "^9.39.4",
123
123
  "eslint-config-next": "^15.1.4",
124
- "tsx": "^4.21.0",
124
+ "eslint-plugin-react-hooks": "^5.2.0",
125
+ "tsx": "^4.22.3",
125
126
  "vitest": "^3.2.4"
126
127
  }
127
128
  }
@@ -48,8 +48,8 @@ if (packageJson.dependencies?.["drizzle-orm"] !== "^0.45.2") {
48
48
  fail("Keep the published drizzle-orm dependency floor at ^0.45.2 or newer.");
49
49
  }
50
50
 
51
- if (packageJson.overrides?.postcss !== "^8.5.14") {
52
- fail("Keep the PostCSS override at ^8.5.14 or newer.");
51
+ if (packageJson.overrides?.postcss !== "^8.5.15") {
52
+ fail("Keep the PostCSS override at ^8.5.15 or newer.");
53
53
  }
54
54
 
55
55
  const blockedPackageEntries = [
package/server.json CHANGED
@@ -8,12 +8,12 @@
8
8
  "url": "https://github.com/abhiyoheswaran1/tokentrace",
9
9
  "source": "github"
10
10
  },
11
- "version": "0.14.2",
11
+ "version": "0.15.0",
12
12
  "packages": [
13
13
  {
14
14
  "registryType": "npm",
15
15
  "identifier": "tokentrace",
16
- "version": "0.14.2",
16
+ "version": "0.15.0",
17
17
  "runtimeHint": "npx",
18
18
  "packageArguments": [
19
19
  {
@@ -24,6 +24,10 @@ function openReadonly(filePath: string) {
24
24
  return new Database(filePath, { readonly: true, fileMustExist: true });
25
25
  }
26
26
 
27
+ function quoteIdent(name: string) {
28
+ return `"${name.replace(/"/g, '""')}"`;
29
+ }
30
+
27
31
  function candidateTables(db: Database.Database) {
28
32
  const tables = db
29
33
  .prepare("SELECT name FROM sqlite_master WHERE type = 'table' AND name NOT LIKE 'sqlite_%'")
@@ -34,7 +38,7 @@ function candidateTables(db: Database.Database) {
34
38
  }
35
39
 
36
40
  function columnsFor(db: Database.Database, tableName: string) {
37
- const columns = db.prepare(`PRAGMA table_info(${JSON.stringify(tableName)})`).all() as Array<{ name: string }>;
41
+ const columns = db.prepare(`PRAGMA table_info(${quoteIdent(tableName)})`).all() as Array<{ name: string }>;
38
42
  return new Set(columns.map((column) => column.name.toLowerCase()));
39
43
  }
40
44
 
@@ -52,7 +56,7 @@ function hasUsageShape(columns: Set<string>) {
52
56
  }
53
57
 
54
58
  function tableRows(db: Database.Database, tableName: string) {
55
- return db.prepare(`SELECT * FROM ${JSON.stringify(tableName)} LIMIT 50000`).all() as UsageRow[];
59
+ return db.prepare(`SELECT * FROM ${quoteIdent(tableName)} LIMIT 50000`).all() as UsageRow[];
56
60
  }
57
61
 
58
62
  function providerId(value: string | null) {