tokentrace 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/.next/BUILD_ID +1 -0
- package/.next/app-build-manifest.json +167 -0
- package/.next/app-path-routes-manifest.json +22 -0
- package/.next/build-manifest.json +33 -0
- package/.next/export-marker.json +6 -0
- package/.next/images-manifest.json +58 -0
- package/.next/next-minimal-server.js.nft.json +1 -0
- package/.next/next-server.js.nft.json +1 -0
- package/.next/package.json +1 -0
- package/.next/prerender-manifest.json +37 -0
- package/.next/react-loadable-manifest.json +1 -0
- package/.next/required-server-files.json +323 -0
- package/.next/routes-manifest.json +119 -0
- package/.next/server/app/_not-found/page.js +2 -0
- package/.next/server/app/_not-found/page.js.nft.json +1 -0
- package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -0
- package/.next/server/app/_not-found.html +1 -0
- package/.next/server/app/_not-found.meta +8 -0
- package/.next/server/app/_not-found.rsc +37 -0
- package/.next/server/app/api/analytics/route.js +1 -0
- package/.next/server/app/api/analytics/route.js.nft.json +1 -0
- package/.next/server/app/api/analytics/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/data/route.js +151 -0
- package/.next/server/app/api/data/route.js.nft.json +1 -0
- package/.next/server/app/api/data/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/export/route.js +1 -0
- package/.next/server/app/api/export/route.js.nft.json +1 -0
- package/.next/server/app/api/export/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/files/route.js +1 -0
- package/.next/server/app/api/files/route.js.nft.json +1 -0
- package/.next/server/app/api/files/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/prices/route.js +151 -0
- package/.next/server/app/api/prices/route.js.nft.json +1 -0
- package/.next/server/app/api/prices/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/scan/route.js +144 -0
- package/.next/server/app/api/scan/route.js.nft.json +1 -0
- package/.next/server/app/api/scan/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/settings/route.js +128 -0
- package/.next/server/app/api/settings/route.js.nft.json +1 -0
- package/.next/server/app/api/settings/route_client-reference-manifest.js +1 -0
- package/.next/server/app/debug/page.js +2 -0
- package/.next/server/app/debug/page.js.nft.json +1 -0
- package/.next/server/app/debug/page_client-reference-manifest.js +1 -0
- package/.next/server/app/diagnostics/page.js +2 -0
- package/.next/server/app/diagnostics/page.js.nft.json +1 -0
- package/.next/server/app/diagnostics/page_client-reference-manifest.js +1 -0
- package/.next/server/app/discovery/page.js +2 -0
- package/.next/server/app/discovery/page.js.nft.json +1 -0
- package/.next/server/app/discovery/page_client-reference-manifest.js +1 -0
- package/.next/server/app/models/page.js +2 -0
- package/.next/server/app/models/page.js.nft.json +1 -0
- package/.next/server/app/models/page_client-reference-manifest.js +1 -0
- package/.next/server/app/optimisation/page.js +2 -0
- package/.next/server/app/optimisation/page.js.nft.json +1 -0
- package/.next/server/app/optimisation/page_client-reference-manifest.js +1 -0
- package/.next/server/app/page.js +2 -0
- package/.next/server/app/page.js.nft.json +1 -0
- package/.next/server/app/page_client-reference-manifest.js +1 -0
- package/.next/server/app/parser-debug/page.js +2 -0
- package/.next/server/app/parser-debug/page.js.nft.json +1 -0
- package/.next/server/app/parser-debug/page_client-reference-manifest.js +1 -0
- package/.next/server/app/pricing/page.js +152 -0
- package/.next/server/app/pricing/page.js.nft.json +1 -0
- package/.next/server/app/pricing/page_client-reference-manifest.js +1 -0
- package/.next/server/app/projects/page.js +2 -0
- package/.next/server/app/projects/page.js.nft.json +1 -0
- package/.next/server/app/projects/page_client-reference-manifest.js +1 -0
- package/.next/server/app/sessions/page.js +2 -0
- package/.next/server/app/sessions/page.js.nft.json +1 -0
- package/.next/server/app/sessions/page_client-reference-manifest.js +1 -0
- package/.next/server/app/settings/page.js +129 -0
- package/.next/server/app/settings/page.js.nft.json +1 -0
- package/.next/server/app/settings/page_client-reference-manifest.js +1 -0
- package/.next/server/app/tools/page.js +2 -0
- package/.next/server/app/tools/page.js.nft.json +1 -0
- package/.next/server/app/tools/page_client-reference-manifest.js +1 -0
- package/.next/server/app-paths-manifest.json +22 -0
- package/.next/server/chunks/123.js +9 -0
- package/.next/server/chunks/153.js +1 -0
- package/.next/server/chunks/237.js +13 -0
- package/.next/server/chunks/331.js +22 -0
- package/.next/server/chunks/366.js +1 -0
- package/.next/server/chunks/444.js +267 -0
- package/.next/server/chunks/611.js +6 -0
- package/.next/server/chunks/692.js +1 -0
- package/.next/server/chunks/779.js +1 -0
- package/.next/server/chunks/815.js +1 -0
- package/.next/server/chunks/868.js +1 -0
- package/.next/server/functions-config-manifest.json +4 -0
- package/.next/server/interception-route-rewrite-manifest.js +1 -0
- package/.next/server/middleware-build-manifest.js +1 -0
- package/.next/server/middleware-manifest.json +6 -0
- package/.next/server/middleware-react-loadable-manifest.js +1 -0
- package/.next/server/next-font-manifest.js +1 -0
- package/.next/server/next-font-manifest.json +1 -0
- package/.next/server/pages/404.html +1 -0
- package/.next/server/pages/500.html +1 -0
- package/.next/server/pages/_app.js +1 -0
- package/.next/server/pages/_app.js.nft.json +1 -0
- package/.next/server/pages/_document.js +1 -0
- package/.next/server/pages/_document.js.nft.json +1 -0
- package/.next/server/pages/_error.js +19 -0
- package/.next/server/pages/_error.js.nft.json +1 -0
- package/.next/server/pages-manifest.json +6 -0
- package/.next/server/server-reference-manifest.js +1 -0
- package/.next/server/server-reference-manifest.json +1 -0
- package/.next/server/webpack-runtime.js +1 -0
- package/.next/static/Fh8usqK3dgfncUx9s3VR1/_buildManifest.js +1 -0
- package/.next/static/Fh8usqK3dgfncUx9s3VR1/_ssgManifest.js +1 -0
- package/.next/static/chunks/125-ab0f8db8f84c1166.js +1 -0
- package/.next/static/chunks/255-e881f48ae1d2333a.js +1 -0
- package/.next/static/chunks/4bd1b696-409494caf8c83275.js +1 -0
- package/.next/static/chunks/619-f072ac750404f9da.js +1 -0
- package/.next/static/chunks/850-8bc31e41590b5831.js +1 -0
- package/.next/static/chunks/938-23236de1c47554ea.js +1 -0
- package/.next/static/chunks/app/_not-found/page-6d75243350d9e0b5.js +1 -0
- package/.next/static/chunks/app/api/analytics/route-33d3f29973de91a4.js +1 -0
- package/.next/static/chunks/app/api/data/route-33d3f29973de91a4.js +1 -0
- package/.next/static/chunks/app/api/export/route-33d3f29973de91a4.js +1 -0
- package/.next/static/chunks/app/api/files/route-33d3f29973de91a4.js +1 -0
- package/.next/static/chunks/app/api/prices/route-33d3f29973de91a4.js +1 -0
- package/.next/static/chunks/app/api/scan/route-33d3f29973de91a4.js +1 -0
- package/.next/static/chunks/app/api/settings/route-33d3f29973de91a4.js +1 -0
- package/.next/static/chunks/app/debug/page-33d3f29973de91a4.js +1 -0
- package/.next/static/chunks/app/diagnostics/page-053a5e810a59e548.js +1 -0
- package/.next/static/chunks/app/discovery/page-33d3f29973de91a4.js +1 -0
- package/.next/static/chunks/app/layout-8942804176ff26f3.js +1 -0
- package/.next/static/chunks/app/models/page-c0acf74dd8197e01.js +1 -0
- package/.next/static/chunks/app/optimisation/page-33d3f29973de91a4.js +1 -0
- package/.next/static/chunks/app/page-b6886ec802c03cbf.js +1 -0
- package/.next/static/chunks/app/parser-debug/page-33d3f29973de91a4.js +1 -0
- package/.next/static/chunks/app/pricing/page-5e27b1ae27314539.js +1 -0
- package/.next/static/chunks/app/projects/page-b6886ec802c03cbf.js +1 -0
- package/.next/static/chunks/app/sessions/page-0abcdc88aac9dcaf.js +1 -0
- package/.next/static/chunks/app/settings/page-59fc80673f0750cd.js +1 -0
- package/.next/static/chunks/app/tools/page-c0acf74dd8197e01.js +1 -0
- package/.next/static/chunks/framework-3457b9c2619cdd96.js +1 -0
- package/.next/static/chunks/main-8744520a8a31e6ae.js +1 -0
- package/.next/static/chunks/main-app-e9ccddef393e28c3.js +1 -0
- package/.next/static/chunks/pages/_app-5addca2b3b969fde.js +1 -0
- package/.next/static/chunks/pages/_error-022e4ac7bbb9914f.js +1 -0
- package/.next/static/chunks/polyfills-42372ed130431b0a.js +1 -0
- package/.next/static/chunks/webpack-3fcacae817f3ffab.js +1 -0
- package/.next/static/css/366bb38b386229a5.css +3 -0
- package/LICENSE +21 -0
- package/README.md +216 -0
- package/app/api/analytics/route.ts +8 -0
- package/app/api/data/route.ts +9 -0
- package/app/api/export/route.ts +26 -0
- package/app/api/files/route.ts +8 -0
- package/app/api/prices/route.ts +33 -0
- package/app/api/scan/route.ts +15 -0
- package/app/api/settings/route.ts +25 -0
- package/app/debug/page.tsx +101 -0
- package/app/diagnostics/page.tsx +113 -0
- package/app/discovery/page.tsx +61 -0
- package/app/globals.css +51 -0
- package/app/layout.tsx +30 -0
- package/app/models/page.tsx +97 -0
- package/app/optimisation/page.tsx +67 -0
- package/app/page.tsx +164 -0
- package/app/parser-debug/page.tsx +57 -0
- package/app/pricing/page.tsx +18 -0
- package/app/projects/page.tsx +111 -0
- package/app/sessions/page.tsx +24 -0
- package/app/settings/page.tsx +26 -0
- package/app/tools/page.tsx +92 -0
- package/bin/tokentrace.js +316 -0
- package/components/charts/rank-bar-chart.tsx +69 -0
- package/components/charts/trend-chart.tsx +123 -0
- package/components/empty-state.tsx +14 -0
- package/components/pricing-settings.tsx +171 -0
- package/components/session-explorer.tsx +210 -0
- package/components/settings-panel.tsx +203 -0
- package/components/sidebar.tsx +88 -0
- package/components/ui/badge.tsx +30 -0
- package/components/ui/button.tsx +47 -0
- package/components/ui/card.tsx +22 -0
- package/components/ui/input.tsx +19 -0
- package/components/ui/label.tsx +6 -0
- package/components/ui/table.tsx +31 -0
- package/components/ui/textarea.tsx +18 -0
- package/components.json +16 -0
- package/dist/runtime/db-migrate.mjs +410 -0
- package/dist/runtime/db-seed.mjs +506 -0
- package/dist/runtime/reset.mjs +519 -0
- package/dist/runtime/scan.mjs +1817 -0
- package/fixtures/generic-jsonl/sample.jsonl +2 -0
- package/next.config.mjs +7 -0
- package/package.json +96 -0
- package/postcss.config.mjs +8 -0
- package/scripts/build-cli-runtime.mjs +40 -0
- package/scripts/db-migrate.ts +5 -0
- package/scripts/db-seed.ts +5 -0
- package/scripts/reset.ts +5 -0
- package/scripts/scan.ts +30 -0
- package/src/db/client.ts +32 -0
- package/src/db/migrate-core.ts +147 -0
- package/src/db/reset.ts +14 -0
- package/src/db/schema.ts +259 -0
- package/src/db/seed.ts +110 -0
- package/src/db/settings.ts +47 -0
- package/src/ingestion/adapters/claude-code.ts +78 -0
- package/src/ingestion/adapters/codex-cli.ts +82 -0
- package/src/ingestion/adapters/generic-json.ts +93 -0
- package/src/ingestion/adapters/generic-jsonl.ts +62 -0
- package/src/ingestion/adapters/generic-log.ts +144 -0
- package/src/ingestion/adapters/generic-records.ts +178 -0
- package/src/ingestion/adapters/helpers.ts +309 -0
- package/src/ingestion/adapters/index.ts +15 -0
- package/src/ingestion/discovery.ts +130 -0
- package/src/ingestion/persist.ts +283 -0
- package/src/ingestion/scan.ts +247 -0
- package/src/ingestion/types.ts +78 -0
- package/src/lib/analytics.ts +592 -0
- package/src/lib/cost.ts +62 -0
- package/src/lib/csv.ts +15 -0
- package/src/lib/format.ts +51 -0
- package/src/lib/ids.ts +23 -0
- package/src/lib/pricing.ts +86 -0
- package/src/lib/token-estimator.ts +24 -0
- package/src/lib/utils.ts +6 -0
- package/tailwind.config.ts +53 -0
- package/tsconfig.json +28 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { cn } from "@/src/lib/utils";
|
|
3
|
+
|
|
4
|
+
export interface TextareaProps extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {}
|
|
5
|
+
|
|
6
|
+
const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(({ className, ...props }, ref) => (
|
|
7
|
+
<textarea
|
|
8
|
+
className={cn(
|
|
9
|
+
"flex min-h-24 w-full rounded-md border bg-card px-3 py-2 text-sm shadow-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50",
|
|
10
|
+
className
|
|
11
|
+
)}
|
|
12
|
+
ref={ref}
|
|
13
|
+
{...props}
|
|
14
|
+
/>
|
|
15
|
+
));
|
|
16
|
+
Textarea.displayName = "Textarea";
|
|
17
|
+
|
|
18
|
+
export { Textarea };
|
package/components.json
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://ui.shadcn.com/schema.json",
|
|
3
|
+
"style": "default",
|
|
4
|
+
"rsc": true,
|
|
5
|
+
"tsx": true,
|
|
6
|
+
"tailwind": {
|
|
7
|
+
"config": "tailwind.config.ts",
|
|
8
|
+
"css": "app/globals.css",
|
|
9
|
+
"baseColor": "neutral",
|
|
10
|
+
"cssVariables": true
|
|
11
|
+
},
|
|
12
|
+
"aliases": {
|
|
13
|
+
"components": "@/components",
|
|
14
|
+
"utils": "@/src/lib/utils"
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,410 @@
|
|
|
1
|
+
import { createRequire as __tokentraceCreateRequire } from 'node:module'; const require = __tokentraceCreateRequire(import.meta.url);
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __export = (target, all) => {
|
|
4
|
+
for (var name in all)
|
|
5
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
// src/db/client.ts
|
|
9
|
+
import fs from "node:fs";
|
|
10
|
+
import path from "node:path";
|
|
11
|
+
import { fileURLToPath } from "node:url";
|
|
12
|
+
import Database from "better-sqlite3";
|
|
13
|
+
import { drizzle } from "drizzle-orm/better-sqlite3";
|
|
14
|
+
|
|
15
|
+
// src/db/migrate-core.ts
|
|
16
|
+
var ddl = `
|
|
17
|
+
PRAGMA journal_mode = WAL;
|
|
18
|
+
PRAGMA foreign_keys = ON;
|
|
19
|
+
|
|
20
|
+
CREATE TABLE IF NOT EXISTS providers (
|
|
21
|
+
id TEXT PRIMARY KEY,
|
|
22
|
+
name TEXT NOT NULL,
|
|
23
|
+
type TEXT NOT NULL,
|
|
24
|
+
created_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000)
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
CREATE TABLE IF NOT EXISTS tools (
|
|
28
|
+
id TEXT PRIMARY KEY,
|
|
29
|
+
provider_id TEXT NOT NULL REFERENCES providers(id) ON DELETE CASCADE,
|
|
30
|
+
name TEXT NOT NULL,
|
|
31
|
+
created_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000)
|
|
32
|
+
);
|
|
33
|
+
CREATE UNIQUE INDEX IF NOT EXISTS tools_provider_name_idx ON tools(provider_id, name);
|
|
34
|
+
|
|
35
|
+
CREATE TABLE IF NOT EXISTS models (
|
|
36
|
+
id TEXT PRIMARY KEY,
|
|
37
|
+
provider_id TEXT NOT NULL REFERENCES providers(id) ON DELETE CASCADE,
|
|
38
|
+
name TEXT NOT NULL,
|
|
39
|
+
input_token_price REAL,
|
|
40
|
+
output_token_price REAL,
|
|
41
|
+
cached_input_token_price REAL,
|
|
42
|
+
currency TEXT NOT NULL DEFAULT 'USD',
|
|
43
|
+
effective_from INTEGER,
|
|
44
|
+
raw_metadata TEXT
|
|
45
|
+
);
|
|
46
|
+
CREATE UNIQUE INDEX IF NOT EXISTS models_provider_name_idx ON models(provider_id, name);
|
|
47
|
+
|
|
48
|
+
CREATE TABLE IF NOT EXISTS projects (
|
|
49
|
+
id TEXT PRIMARY KEY,
|
|
50
|
+
name TEXT NOT NULL,
|
|
51
|
+
path TEXT NOT NULL,
|
|
52
|
+
created_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000)
|
|
53
|
+
);
|
|
54
|
+
CREATE UNIQUE INDEX IF NOT EXISTS projects_path_idx ON projects(path);
|
|
55
|
+
|
|
56
|
+
CREATE TABLE IF NOT EXISTS sessions (
|
|
57
|
+
id TEXT PRIMARY KEY,
|
|
58
|
+
source_id TEXT NOT NULL,
|
|
59
|
+
tool_id TEXT NOT NULL REFERENCES tools(id) ON DELETE CASCADE,
|
|
60
|
+
project_id TEXT REFERENCES projects(id) ON DELETE SET NULL,
|
|
61
|
+
started_at INTEGER,
|
|
62
|
+
ended_at INTEGER,
|
|
63
|
+
title TEXT,
|
|
64
|
+
source_file TEXT NOT NULL,
|
|
65
|
+
raw_metadata TEXT
|
|
66
|
+
);
|
|
67
|
+
CREATE UNIQUE INDEX IF NOT EXISTS sessions_source_id_idx ON sessions(source_id);
|
|
68
|
+
CREATE INDEX IF NOT EXISTS sessions_tool_idx ON sessions(tool_id);
|
|
69
|
+
CREATE INDEX IF NOT EXISTS sessions_project_idx ON sessions(project_id);
|
|
70
|
+
CREATE INDEX IF NOT EXISTS sessions_started_idx ON sessions(started_at);
|
|
71
|
+
|
|
72
|
+
CREATE TABLE IF NOT EXISTS interactions (
|
|
73
|
+
id TEXT PRIMARY KEY,
|
|
74
|
+
source_id TEXT NOT NULL,
|
|
75
|
+
session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
|
|
76
|
+
timestamp INTEGER,
|
|
77
|
+
role TEXT NOT NULL,
|
|
78
|
+
model_id TEXT REFERENCES models(id) ON DELETE SET NULL,
|
|
79
|
+
input_tokens INTEGER NOT NULL DEFAULT 0,
|
|
80
|
+
output_tokens INTEGER NOT NULL DEFAULT 0,
|
|
81
|
+
cache_read_tokens INTEGER NOT NULL DEFAULT 0,
|
|
82
|
+
cache_write_tokens INTEGER NOT NULL DEFAULT 0,
|
|
83
|
+
reasoning_tokens INTEGER NOT NULL DEFAULT 0,
|
|
84
|
+
total_tokens INTEGER NOT NULL DEFAULT 0,
|
|
85
|
+
estimated_tokens INTEGER NOT NULL DEFAULT 0,
|
|
86
|
+
token_confidence TEXT NOT NULL DEFAULT 'unknown',
|
|
87
|
+
cost REAL,
|
|
88
|
+
cost_estimated INTEGER NOT NULL DEFAULT 0,
|
|
89
|
+
latency_ms INTEGER,
|
|
90
|
+
raw_text_preview TEXT,
|
|
91
|
+
raw_text TEXT,
|
|
92
|
+
raw_metadata TEXT
|
|
93
|
+
);
|
|
94
|
+
CREATE UNIQUE INDEX IF NOT EXISTS interactions_source_id_idx ON interactions(source_id);
|
|
95
|
+
CREATE INDEX IF NOT EXISTS interactions_session_idx ON interactions(session_id);
|
|
96
|
+
CREATE INDEX IF NOT EXISTS interactions_model_idx ON interactions(model_id);
|
|
97
|
+
CREATE INDEX IF NOT EXISTS interactions_timestamp_idx ON interactions(timestamp);
|
|
98
|
+
|
|
99
|
+
PRAGMA user_version;
|
|
100
|
+
|
|
101
|
+
CREATE TABLE IF NOT EXISTS tool_calls (
|
|
102
|
+
id TEXT PRIMARY KEY,
|
|
103
|
+
interaction_id TEXT NOT NULL REFERENCES interactions(id) ON DELETE CASCADE,
|
|
104
|
+
name TEXT NOT NULL,
|
|
105
|
+
status TEXT,
|
|
106
|
+
duration_ms INTEGER,
|
|
107
|
+
raw_metadata TEXT
|
|
108
|
+
);
|
|
109
|
+
CREATE INDEX IF NOT EXISTS tool_calls_interaction_idx ON tool_calls(interaction_id);
|
|
110
|
+
|
|
111
|
+
CREATE TABLE IF NOT EXISTS scan_runs (
|
|
112
|
+
id TEXT PRIMARY KEY,
|
|
113
|
+
started_at INTEGER NOT NULL,
|
|
114
|
+
completed_at INTEGER,
|
|
115
|
+
files_scanned INTEGER NOT NULL DEFAULT 0,
|
|
116
|
+
records_imported INTEGER NOT NULL DEFAULT 0,
|
|
117
|
+
warnings TEXT NOT NULL DEFAULT '[]',
|
|
118
|
+
errors TEXT NOT NULL DEFAULT '[]'
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
CREATE TABLE IF NOT EXISTS scan_files (
|
|
122
|
+
id TEXT PRIMARY KEY,
|
|
123
|
+
scan_run_id TEXT NOT NULL REFERENCES scan_runs(id) ON DELETE CASCADE,
|
|
124
|
+
path TEXT NOT NULL,
|
|
125
|
+
modified_time INTEGER,
|
|
126
|
+
size_bytes INTEGER NOT NULL DEFAULT 0,
|
|
127
|
+
file_hash TEXT,
|
|
128
|
+
parser TEXT,
|
|
129
|
+
status TEXT NOT NULL,
|
|
130
|
+
records_imported INTEGER NOT NULL DEFAULT 0,
|
|
131
|
+
warnings TEXT NOT NULL DEFAULT '[]',
|
|
132
|
+
errors TEXT NOT NULL DEFAULT '[]',
|
|
133
|
+
raw_metadata TEXT
|
|
134
|
+
);
|
|
135
|
+
CREATE INDEX IF NOT EXISTS scan_files_path_hash_idx ON scan_files(path, file_hash);
|
|
136
|
+
CREATE INDEX IF NOT EXISTS scan_files_run_idx ON scan_files(scan_run_id);
|
|
137
|
+
|
|
138
|
+
CREATE TABLE IF NOT EXISTS settings (
|
|
139
|
+
key TEXT PRIMARY KEY,
|
|
140
|
+
value TEXT NOT NULL,
|
|
141
|
+
updated_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000)
|
|
142
|
+
);
|
|
143
|
+
`;
|
|
144
|
+
function applyMigrations(sqlite2) {
|
|
145
|
+
sqlite2.exec(ddl);
|
|
146
|
+
const columns = sqlite2.prepare("PRAGMA table_info(interactions)").all();
|
|
147
|
+
if (!columns.some((column) => column.name === "token_confidence")) {
|
|
148
|
+
try {
|
|
149
|
+
sqlite2.exec("ALTER TABLE interactions ADD COLUMN token_confidence TEXT NOT NULL DEFAULT 'unknown'");
|
|
150
|
+
} catch (error) {
|
|
151
|
+
if (!(error instanceof Error) || !error.message.toLowerCase().includes("duplicate column")) {
|
|
152
|
+
throw error;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// src/db/schema.ts
|
|
159
|
+
var schema_exports = {};
|
|
160
|
+
__export(schema_exports, {
|
|
161
|
+
interactionRelations: () => interactionRelations,
|
|
162
|
+
interactions: () => interactions,
|
|
163
|
+
modelRelations: () => modelRelations,
|
|
164
|
+
models: () => models,
|
|
165
|
+
projectRelations: () => projectRelations,
|
|
166
|
+
projects: () => projects,
|
|
167
|
+
providerRelations: () => providerRelations,
|
|
168
|
+
providers: () => providers,
|
|
169
|
+
scanFiles: () => scanFiles,
|
|
170
|
+
scanRuns: () => scanRuns,
|
|
171
|
+
sessionRelations: () => sessionRelations,
|
|
172
|
+
sessions: () => sessions,
|
|
173
|
+
settings: () => settings,
|
|
174
|
+
toolCalls: () => toolCalls,
|
|
175
|
+
toolRelations: () => toolRelations,
|
|
176
|
+
tools: () => tools
|
|
177
|
+
});
|
|
178
|
+
import { relations, sql } from "drizzle-orm";
|
|
179
|
+
import {
|
|
180
|
+
index,
|
|
181
|
+
integer,
|
|
182
|
+
real,
|
|
183
|
+
sqliteTable,
|
|
184
|
+
text,
|
|
185
|
+
uniqueIndex
|
|
186
|
+
} from "drizzle-orm/sqlite-core";
|
|
187
|
+
var providers = sqliteTable("providers", {
|
|
188
|
+
id: text("id").primaryKey(),
|
|
189
|
+
name: text("name").notNull(),
|
|
190
|
+
type: text("type").notNull(),
|
|
191
|
+
createdAt: integer("created_at", { mode: "timestamp_ms" }).notNull().$defaultFn(() => /* @__PURE__ */ new Date())
|
|
192
|
+
});
|
|
193
|
+
var tools = sqliteTable(
|
|
194
|
+
"tools",
|
|
195
|
+
{
|
|
196
|
+
id: text("id").primaryKey(),
|
|
197
|
+
providerId: text("provider_id").notNull().references(() => providers.id, { onDelete: "cascade" }),
|
|
198
|
+
name: text("name").notNull(),
|
|
199
|
+
createdAt: integer("created_at", { mode: "timestamp_ms" }).notNull().$defaultFn(() => /* @__PURE__ */ new Date())
|
|
200
|
+
},
|
|
201
|
+
(table) => ({
|
|
202
|
+
providerNameIdx: uniqueIndex("tools_provider_name_idx").on(
|
|
203
|
+
table.providerId,
|
|
204
|
+
table.name
|
|
205
|
+
)
|
|
206
|
+
})
|
|
207
|
+
);
|
|
208
|
+
var models = sqliteTable(
|
|
209
|
+
"models",
|
|
210
|
+
{
|
|
211
|
+
id: text("id").primaryKey(),
|
|
212
|
+
providerId: text("provider_id").notNull().references(() => providers.id, { onDelete: "cascade" }),
|
|
213
|
+
name: text("name").notNull(),
|
|
214
|
+
inputTokenPrice: real("input_token_price"),
|
|
215
|
+
outputTokenPrice: real("output_token_price"),
|
|
216
|
+
cachedInputTokenPrice: real("cached_input_token_price"),
|
|
217
|
+
currency: text("currency").notNull().default("USD"),
|
|
218
|
+
effectiveFrom: integer("effective_from", { mode: "timestamp_ms" }),
|
|
219
|
+
rawMetadata: text("raw_metadata", { mode: "json" }).$type()
|
|
220
|
+
},
|
|
221
|
+
(table) => ({
|
|
222
|
+
providerModelIdx: uniqueIndex("models_provider_name_idx").on(
|
|
223
|
+
table.providerId,
|
|
224
|
+
table.name
|
|
225
|
+
)
|
|
226
|
+
})
|
|
227
|
+
);
|
|
228
|
+
var projects = sqliteTable(
|
|
229
|
+
"projects",
|
|
230
|
+
{
|
|
231
|
+
id: text("id").primaryKey(),
|
|
232
|
+
name: text("name").notNull(),
|
|
233
|
+
path: text("path").notNull(),
|
|
234
|
+
createdAt: integer("created_at", { mode: "timestamp_ms" }).notNull().$defaultFn(() => /* @__PURE__ */ new Date())
|
|
235
|
+
},
|
|
236
|
+
(table) => ({
|
|
237
|
+
pathIdx: uniqueIndex("projects_path_idx").on(table.path)
|
|
238
|
+
})
|
|
239
|
+
);
|
|
240
|
+
var sessions = sqliteTable(
|
|
241
|
+
"sessions",
|
|
242
|
+
{
|
|
243
|
+
id: text("id").primaryKey(),
|
|
244
|
+
sourceId: text("source_id").notNull(),
|
|
245
|
+
toolId: text("tool_id").notNull().references(() => tools.id, { onDelete: "cascade" }),
|
|
246
|
+
projectId: text("project_id").references(() => projects.id, {
|
|
247
|
+
onDelete: "set null"
|
|
248
|
+
}),
|
|
249
|
+
startedAt: integer("started_at", { mode: "timestamp_ms" }),
|
|
250
|
+
endedAt: integer("ended_at", { mode: "timestamp_ms" }),
|
|
251
|
+
title: text("title"),
|
|
252
|
+
sourceFile: text("source_file").notNull(),
|
|
253
|
+
rawMetadata: text("raw_metadata", { mode: "json" }).$type()
|
|
254
|
+
},
|
|
255
|
+
(table) => ({
|
|
256
|
+
sourceIdx: uniqueIndex("sessions_source_id_idx").on(table.sourceId),
|
|
257
|
+
toolIdx: index("sessions_tool_idx").on(table.toolId),
|
|
258
|
+
projectIdx: index("sessions_project_idx").on(table.projectId),
|
|
259
|
+
startedIdx: index("sessions_started_idx").on(table.startedAt)
|
|
260
|
+
})
|
|
261
|
+
);
|
|
262
|
+
var interactions = sqliteTable(
|
|
263
|
+
"interactions",
|
|
264
|
+
{
|
|
265
|
+
id: text("id").primaryKey(),
|
|
266
|
+
sourceId: text("source_id").notNull(),
|
|
267
|
+
sessionId: text("session_id").notNull().references(() => sessions.id, { onDelete: "cascade" }),
|
|
268
|
+
timestamp: integer("timestamp", { mode: "timestamp_ms" }),
|
|
269
|
+
role: text("role").notNull(),
|
|
270
|
+
modelId: text("model_id").references(() => models.id, {
|
|
271
|
+
onDelete: "set null"
|
|
272
|
+
}),
|
|
273
|
+
inputTokens: integer("input_tokens").notNull().default(0),
|
|
274
|
+
outputTokens: integer("output_tokens").notNull().default(0),
|
|
275
|
+
cacheReadTokens: integer("cache_read_tokens").notNull().default(0),
|
|
276
|
+
cacheWriteTokens: integer("cache_write_tokens").notNull().default(0),
|
|
277
|
+
reasoningTokens: integer("reasoning_tokens").notNull().default(0),
|
|
278
|
+
totalTokens: integer("total_tokens").notNull().default(0),
|
|
279
|
+
estimatedTokens: integer("estimated_tokens", { mode: "boolean" }).notNull().default(false),
|
|
280
|
+
tokenConfidence: text("token_confidence").notNull().default("unknown"),
|
|
281
|
+
cost: real("cost"),
|
|
282
|
+
costEstimated: integer("cost_estimated", { mode: "boolean" }).notNull().default(false),
|
|
283
|
+
latencyMs: integer("latency_ms"),
|
|
284
|
+
rawTextPreview: text("raw_text_preview"),
|
|
285
|
+
rawText: text("raw_text"),
|
|
286
|
+
rawMetadata: text("raw_metadata", { mode: "json" }).$type()
|
|
287
|
+
},
|
|
288
|
+
(table) => ({
|
|
289
|
+
sourceIdx: uniqueIndex("interactions_source_id_idx").on(table.sourceId),
|
|
290
|
+
sessionIdx: index("interactions_session_idx").on(table.sessionId),
|
|
291
|
+
modelIdx: index("interactions_model_idx").on(table.modelId),
|
|
292
|
+
timestampIdx: index("interactions_timestamp_idx").on(table.timestamp)
|
|
293
|
+
})
|
|
294
|
+
);
|
|
295
|
+
var toolCalls = sqliteTable(
|
|
296
|
+
"tool_calls",
|
|
297
|
+
{
|
|
298
|
+
id: text("id").primaryKey(),
|
|
299
|
+
interactionId: text("interaction_id").notNull().references(() => interactions.id, { onDelete: "cascade" }),
|
|
300
|
+
name: text("name").notNull(),
|
|
301
|
+
status: text("status"),
|
|
302
|
+
durationMs: integer("duration_ms"),
|
|
303
|
+
rawMetadata: text("raw_metadata", { mode: "json" }).$type()
|
|
304
|
+
},
|
|
305
|
+
(table) => ({
|
|
306
|
+
interactionIdx: index("tool_calls_interaction_idx").on(table.interactionId)
|
|
307
|
+
})
|
|
308
|
+
);
|
|
309
|
+
var scanRuns = sqliteTable("scan_runs", {
|
|
310
|
+
id: text("id").primaryKey(),
|
|
311
|
+
startedAt: integer("started_at", { mode: "timestamp_ms" }).notNull(),
|
|
312
|
+
completedAt: integer("completed_at", { mode: "timestamp_ms" }),
|
|
313
|
+
filesScanned: integer("files_scanned").notNull().default(0),
|
|
314
|
+
recordsImported: integer("records_imported").notNull().default(0),
|
|
315
|
+
warnings: text("warnings", { mode: "json" }).$type().notNull().default(sql`'[]'`),
|
|
316
|
+
errors: text("errors", { mode: "json" }).$type().notNull().default(sql`'[]'`)
|
|
317
|
+
});
|
|
318
|
+
var scanFiles = sqliteTable(
|
|
319
|
+
"scan_files",
|
|
320
|
+
{
|
|
321
|
+
id: text("id").primaryKey(),
|
|
322
|
+
scanRunId: text("scan_run_id").notNull().references(() => scanRuns.id, { onDelete: "cascade" }),
|
|
323
|
+
path: text("path").notNull(),
|
|
324
|
+
modifiedTime: integer("modified_time", { mode: "timestamp_ms" }),
|
|
325
|
+
sizeBytes: integer("size_bytes").notNull().default(0),
|
|
326
|
+
fileHash: text("file_hash"),
|
|
327
|
+
parser: text("parser"),
|
|
328
|
+
status: text("status").notNull(),
|
|
329
|
+
recordsImported: integer("records_imported").notNull().default(0),
|
|
330
|
+
warnings: text("warnings", { mode: "json" }).$type().notNull().default(sql`'[]'`),
|
|
331
|
+
errors: text("errors", { mode: "json" }).$type().notNull().default(sql`'[]'`),
|
|
332
|
+
rawMetadata: text("raw_metadata", { mode: "json" }).$type()
|
|
333
|
+
},
|
|
334
|
+
(table) => ({
|
|
335
|
+
pathHashIdx: index("scan_files_path_hash_idx").on(table.path, table.fileHash),
|
|
336
|
+
scanRunIdx: index("scan_files_run_idx").on(table.scanRunId)
|
|
337
|
+
})
|
|
338
|
+
);
|
|
339
|
+
var settings = sqliteTable("settings", {
|
|
340
|
+
key: text("key").primaryKey(),
|
|
341
|
+
value: text("value", { mode: "json" }).$type().notNull(),
|
|
342
|
+
updatedAt: integer("updated_at", { mode: "timestamp_ms" }).notNull().$defaultFn(() => /* @__PURE__ */ new Date())
|
|
343
|
+
});
|
|
344
|
+
var providerRelations = relations(providers, ({ many }) => ({
|
|
345
|
+
tools: many(tools),
|
|
346
|
+
models: many(models)
|
|
347
|
+
}));
|
|
348
|
+
var toolRelations = relations(tools, ({ one, many }) => ({
|
|
349
|
+
provider: one(providers, {
|
|
350
|
+
fields: [tools.providerId],
|
|
351
|
+
references: [providers.id]
|
|
352
|
+
}),
|
|
353
|
+
sessions: many(sessions)
|
|
354
|
+
}));
|
|
355
|
+
var modelRelations = relations(models, ({ one, many }) => ({
|
|
356
|
+
provider: one(providers, {
|
|
357
|
+
fields: [models.providerId],
|
|
358
|
+
references: [providers.id]
|
|
359
|
+
}),
|
|
360
|
+
interactions: many(interactions)
|
|
361
|
+
}));
|
|
362
|
+
var projectRelations = relations(projects, ({ many }) => ({
|
|
363
|
+
sessions: many(sessions)
|
|
364
|
+
}));
|
|
365
|
+
var sessionRelations = relations(sessions, ({ one, many }) => ({
|
|
366
|
+
tool: one(tools, {
|
|
367
|
+
fields: [sessions.toolId],
|
|
368
|
+
references: [tools.id]
|
|
369
|
+
}),
|
|
370
|
+
project: one(projects, {
|
|
371
|
+
fields: [sessions.projectId],
|
|
372
|
+
references: [projects.id]
|
|
373
|
+
}),
|
|
374
|
+
interactions: many(interactions)
|
|
375
|
+
}));
|
|
376
|
+
var interactionRelations = relations(interactions, ({ one, many }) => ({
|
|
377
|
+
session: one(sessions, {
|
|
378
|
+
fields: [interactions.sessionId],
|
|
379
|
+
references: [sessions.id]
|
|
380
|
+
}),
|
|
381
|
+
model: one(models, {
|
|
382
|
+
fields: [interactions.modelId],
|
|
383
|
+
references: [models.id]
|
|
384
|
+
}),
|
|
385
|
+
toolCalls: many(toolCalls)
|
|
386
|
+
}));
|
|
387
|
+
|
|
388
|
+
// src/db/client.ts
|
|
389
|
+
var defaultDbPath = path.join(process.cwd(), ".tokentrace", "tokentrace.db");
|
|
390
|
+
function databaseUrlPath(value) {
|
|
391
|
+
if (!value?.startsWith("file:")) return null;
|
|
392
|
+
try {
|
|
393
|
+
return fileURLToPath(value);
|
|
394
|
+
} catch {
|
|
395
|
+
return value.slice("file:".length);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
var dbPath = process.env.TOKENTRACE_DB ?? databaseUrlPath(process.env.DATABASE_URL) ?? defaultDbPath;
|
|
399
|
+
fs.mkdirSync(path.dirname(dbPath), { recursive: true });
|
|
400
|
+
var sqlite = new Database(dbPath);
|
|
401
|
+
sqlite.pragma("foreign_keys = ON");
|
|
402
|
+
applyMigrations(sqlite);
|
|
403
|
+
var db = drizzle(sqlite, { schema: schema_exports });
|
|
404
|
+
function getDatabasePath() {
|
|
405
|
+
return dbPath;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// scripts/db-migrate.ts
|
|
409
|
+
applyMigrations(sqlite);
|
|
410
|
+
console.log(`TokenTrace database migrated at ${getDatabasePath()}`);
|