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,2 @@
|
|
|
1
|
+
{"timestamp":"2026-01-02T10:00:00.000Z","session_id":"demo-session","role":"user","model":"gpt-4.1-mini","cwd":"/tmp/tokentrace-demo","content":"Summarize this repository.","usage":{"input_tokens":120,"output_tokens":0,"total_tokens":120}}
|
|
2
|
+
{"timestamp":"2026-01-02T10:00:03.000Z","session_id":"demo-session","role":"assistant","model":"gpt-4.1-mini","cwd":"/tmp/tokentrace-demo","content":"This repository contains a local analytics app.","usage":{"input_tokens":0,"output_tokens":85,"total_tokens":85},"tool_calls":[{"id":"tool-1","name":"read_file","status":"ok","duration_ms":34}]}
|
package/next.config.mjs
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "tokentrace",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Local-first dashboard for AI CLI token, cost, and session analytics.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"homepage": "https://github.com/abhiyoheswaran1/tokentrace#readme",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/abhiyoheswaran1/tokentrace.git"
|
|
10
|
+
},
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/abhiyoheswaran1/tokentrace/issues"
|
|
13
|
+
},
|
|
14
|
+
"bin": {
|
|
15
|
+
"tokentrace": "bin/tokentrace.js"
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"bin",
|
|
19
|
+
".next/BUILD_ID",
|
|
20
|
+
".next/*.json",
|
|
21
|
+
".next/server",
|
|
22
|
+
".next/static",
|
|
23
|
+
"public",
|
|
24
|
+
"app",
|
|
25
|
+
"components",
|
|
26
|
+
"src",
|
|
27
|
+
"scripts",
|
|
28
|
+
"dist",
|
|
29
|
+
"fixtures",
|
|
30
|
+
"next.config.mjs",
|
|
31
|
+
"postcss.config.mjs",
|
|
32
|
+
"tailwind.config.ts",
|
|
33
|
+
"tsconfig.json",
|
|
34
|
+
"components.json",
|
|
35
|
+
"README.md"
|
|
36
|
+
],
|
|
37
|
+
"scripts": {
|
|
38
|
+
"dev": "next dev",
|
|
39
|
+
"build": "next build && node scripts/build-cli-runtime.mjs",
|
|
40
|
+
"start": "next start",
|
|
41
|
+
"scan": "tsx scripts/scan.ts",
|
|
42
|
+
"db:migrate": "tsx scripts/db-migrate.ts",
|
|
43
|
+
"db:seed": "tsx scripts/db-seed.ts",
|
|
44
|
+
"reset": "tsx scripts/reset.ts",
|
|
45
|
+
"test": "vitest run",
|
|
46
|
+
"package:test": "npm test && npm pack --dry-run",
|
|
47
|
+
"prepack": "npm run build"
|
|
48
|
+
},
|
|
49
|
+
"engines": {
|
|
50
|
+
"node": ">=18.18.0"
|
|
51
|
+
},
|
|
52
|
+
"keywords": [
|
|
53
|
+
"tokens",
|
|
54
|
+
"ai",
|
|
55
|
+
"cli",
|
|
56
|
+
"analytics",
|
|
57
|
+
"codex",
|
|
58
|
+
"claude",
|
|
59
|
+
"local-first",
|
|
60
|
+
"nextjs",
|
|
61
|
+
"sqlite"
|
|
62
|
+
],
|
|
63
|
+
"license": "MIT",
|
|
64
|
+
"dependencies": {
|
|
65
|
+
"@radix-ui/react-slot": "^1.1.1",
|
|
66
|
+
"better-sqlite3": "^11.7.0",
|
|
67
|
+
"class-variance-authority": "^0.7.1",
|
|
68
|
+
"clsx": "^2.1.1",
|
|
69
|
+
"date-fns": "^4.1.0",
|
|
70
|
+
"drizzle-orm": "^0.38.3",
|
|
71
|
+
"get-port": "^7.2.0",
|
|
72
|
+
"lucide-react": "^0.468.0",
|
|
73
|
+
"next": "^15.1.4",
|
|
74
|
+
"open": "^10.1.0",
|
|
75
|
+
"react": "^19.0.0",
|
|
76
|
+
"react-dom": "^19.0.0",
|
|
77
|
+
"recharts": "^2.15.0",
|
|
78
|
+
"tailwind-merge": "^2.6.0"
|
|
79
|
+
},
|
|
80
|
+
"devDependencies": {
|
|
81
|
+
"@types/better-sqlite3": "^7.6.12",
|
|
82
|
+
"@types/node": "^22.10.5",
|
|
83
|
+
"@types/react": "^19.0.4",
|
|
84
|
+
"@types/react-dom": "^19.0.2",
|
|
85
|
+
"autoprefixer": "^10.4.20",
|
|
86
|
+
"drizzle-kit": "^0.30.1",
|
|
87
|
+
"esbuild": "^0.28.0",
|
|
88
|
+
"eslint": "^9.17.0",
|
|
89
|
+
"eslint-config-next": "^15.1.4",
|
|
90
|
+
"postcss": "^8.4.49",
|
|
91
|
+
"tailwindcss": "^3.4.17",
|
|
92
|
+
"tsx": "^4.21.0",
|
|
93
|
+
"typescript": "^5.7.2",
|
|
94
|
+
"vitest": "^2.1.8"
|
|
95
|
+
}
|
|
96
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { build } from "esbuild";
|
|
2
|
+
import fs from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
|
|
5
|
+
const outdir = path.join(process.cwd(), "dist", "runtime");
|
|
6
|
+
|
|
7
|
+
await fs.rm(outdir, { recursive: true, force: true });
|
|
8
|
+
await fs.mkdir(outdir, { recursive: true });
|
|
9
|
+
|
|
10
|
+
const entryPoints = {
|
|
11
|
+
"db-migrate": "scripts/db-migrate.ts",
|
|
12
|
+
"db-seed": "scripts/db-seed.ts",
|
|
13
|
+
reset: "scripts/reset.ts",
|
|
14
|
+
scan: "scripts/scan.ts"
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
await Promise.all(
|
|
18
|
+
Object.entries(entryPoints).map(([name, entryPoint]) =>
|
|
19
|
+
build({
|
|
20
|
+
entryPoints: [entryPoint],
|
|
21
|
+
outfile: path.join(outdir, `${name}.mjs`),
|
|
22
|
+
bundle: true,
|
|
23
|
+
platform: "node",
|
|
24
|
+
format: "esm",
|
|
25
|
+
target: "node18",
|
|
26
|
+
sourcemap: false,
|
|
27
|
+
packages: "external",
|
|
28
|
+
external: ["better-sqlite3"],
|
|
29
|
+
logLevel: "silent",
|
|
30
|
+
banner: {
|
|
31
|
+
js: "import { createRequire as __tokentraceCreateRequire } from 'node:module'; const require = __tokentraceCreateRequire(import.meta.url);"
|
|
32
|
+
},
|
|
33
|
+
alias: {
|
|
34
|
+
"@": process.cwd()
|
|
35
|
+
}
|
|
36
|
+
})
|
|
37
|
+
)
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
console.log(`TokenTrace CLI runtime built at ${outdir}`);
|
package/scripts/reset.ts
ADDED
package/scripts/scan.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { runScan } from "@/src/ingestion/scan";
|
|
2
|
+
|
|
3
|
+
const args = process.argv.slice(2);
|
|
4
|
+
const json = args.includes("--json");
|
|
5
|
+
const force = args.includes("--force");
|
|
6
|
+
const folders = args.filter((arg) => arg !== "--force" && arg !== "--json");
|
|
7
|
+
|
|
8
|
+
const result = await runScan({
|
|
9
|
+
force,
|
|
10
|
+
folders,
|
|
11
|
+
includeDefaults: folders.length === 0
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
const summary = {
|
|
15
|
+
scanRunId: result.scanRunId,
|
|
16
|
+
filesScanned: result.filesScanned,
|
|
17
|
+
recordsImported: result.recordsImported,
|
|
18
|
+
warnings: result.warnings.length,
|
|
19
|
+
errors: result.errors.length
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
if (json) {
|
|
23
|
+
console.log(JSON.stringify(summary, null, 2));
|
|
24
|
+
} else {
|
|
25
|
+
console.log("TokenTrace scan complete");
|
|
26
|
+
console.log(`Files scanned: ${summary.filesScanned}`);
|
|
27
|
+
console.log(`Records imported: ${summary.recordsImported}`);
|
|
28
|
+
console.log(`Warnings: ${summary.warnings}`);
|
|
29
|
+
console.log(`Errors: ${summary.errors}`);
|
|
30
|
+
}
|
package/src/db/client.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import Database from "better-sqlite3";
|
|
5
|
+
import { drizzle } from "drizzle-orm/better-sqlite3";
|
|
6
|
+
import { applyMigrations } from "./migrate-core";
|
|
7
|
+
import * as schema from "./schema";
|
|
8
|
+
|
|
9
|
+
const defaultDbPath = path.join(process.cwd(), ".tokentrace", "tokentrace.db");
|
|
10
|
+
|
|
11
|
+
function databaseUrlPath(value: string | undefined) {
|
|
12
|
+
if (!value?.startsWith("file:")) return null;
|
|
13
|
+
try {
|
|
14
|
+
return fileURLToPath(value);
|
|
15
|
+
} catch {
|
|
16
|
+
return value.slice("file:".length);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const dbPath = process.env.TOKENTRACE_DB ?? databaseUrlPath(process.env.DATABASE_URL) ?? defaultDbPath;
|
|
21
|
+
|
|
22
|
+
fs.mkdirSync(path.dirname(dbPath), { recursive: true });
|
|
23
|
+
|
|
24
|
+
export const sqlite = new Database(dbPath);
|
|
25
|
+
sqlite.pragma("foreign_keys = ON");
|
|
26
|
+
applyMigrations(sqlite);
|
|
27
|
+
|
|
28
|
+
export const db = drizzle(sqlite, { schema });
|
|
29
|
+
|
|
30
|
+
export function getDatabasePath() {
|
|
31
|
+
return dbPath;
|
|
32
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import Database from "better-sqlite3";
|
|
2
|
+
|
|
3
|
+
const ddl = `
|
|
4
|
+
PRAGMA journal_mode = WAL;
|
|
5
|
+
PRAGMA foreign_keys = ON;
|
|
6
|
+
|
|
7
|
+
CREATE TABLE IF NOT EXISTS providers (
|
|
8
|
+
id TEXT PRIMARY KEY,
|
|
9
|
+
name TEXT NOT NULL,
|
|
10
|
+
type TEXT NOT NULL,
|
|
11
|
+
created_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000)
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
CREATE TABLE IF NOT EXISTS tools (
|
|
15
|
+
id TEXT PRIMARY KEY,
|
|
16
|
+
provider_id TEXT NOT NULL REFERENCES providers(id) ON DELETE CASCADE,
|
|
17
|
+
name TEXT NOT NULL,
|
|
18
|
+
created_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000)
|
|
19
|
+
);
|
|
20
|
+
CREATE UNIQUE INDEX IF NOT EXISTS tools_provider_name_idx ON tools(provider_id, name);
|
|
21
|
+
|
|
22
|
+
CREATE TABLE IF NOT EXISTS models (
|
|
23
|
+
id TEXT PRIMARY KEY,
|
|
24
|
+
provider_id TEXT NOT NULL REFERENCES providers(id) ON DELETE CASCADE,
|
|
25
|
+
name TEXT NOT NULL,
|
|
26
|
+
input_token_price REAL,
|
|
27
|
+
output_token_price REAL,
|
|
28
|
+
cached_input_token_price REAL,
|
|
29
|
+
currency TEXT NOT NULL DEFAULT 'USD',
|
|
30
|
+
effective_from INTEGER,
|
|
31
|
+
raw_metadata TEXT
|
|
32
|
+
);
|
|
33
|
+
CREATE UNIQUE INDEX IF NOT EXISTS models_provider_name_idx ON models(provider_id, name);
|
|
34
|
+
|
|
35
|
+
CREATE TABLE IF NOT EXISTS projects (
|
|
36
|
+
id TEXT PRIMARY KEY,
|
|
37
|
+
name TEXT NOT NULL,
|
|
38
|
+
path TEXT NOT NULL,
|
|
39
|
+
created_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000)
|
|
40
|
+
);
|
|
41
|
+
CREATE UNIQUE INDEX IF NOT EXISTS projects_path_idx ON projects(path);
|
|
42
|
+
|
|
43
|
+
CREATE TABLE IF NOT EXISTS sessions (
|
|
44
|
+
id TEXT PRIMARY KEY,
|
|
45
|
+
source_id TEXT NOT NULL,
|
|
46
|
+
tool_id TEXT NOT NULL REFERENCES tools(id) ON DELETE CASCADE,
|
|
47
|
+
project_id TEXT REFERENCES projects(id) ON DELETE SET NULL,
|
|
48
|
+
started_at INTEGER,
|
|
49
|
+
ended_at INTEGER,
|
|
50
|
+
title TEXT,
|
|
51
|
+
source_file TEXT NOT NULL,
|
|
52
|
+
raw_metadata TEXT
|
|
53
|
+
);
|
|
54
|
+
CREATE UNIQUE INDEX IF NOT EXISTS sessions_source_id_idx ON sessions(source_id);
|
|
55
|
+
CREATE INDEX IF NOT EXISTS sessions_tool_idx ON sessions(tool_id);
|
|
56
|
+
CREATE INDEX IF NOT EXISTS sessions_project_idx ON sessions(project_id);
|
|
57
|
+
CREATE INDEX IF NOT EXISTS sessions_started_idx ON sessions(started_at);
|
|
58
|
+
|
|
59
|
+
CREATE TABLE IF NOT EXISTS interactions (
|
|
60
|
+
id TEXT PRIMARY KEY,
|
|
61
|
+
source_id TEXT NOT NULL,
|
|
62
|
+
session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
|
|
63
|
+
timestamp INTEGER,
|
|
64
|
+
role TEXT NOT NULL,
|
|
65
|
+
model_id TEXT REFERENCES models(id) ON DELETE SET NULL,
|
|
66
|
+
input_tokens INTEGER NOT NULL DEFAULT 0,
|
|
67
|
+
output_tokens INTEGER NOT NULL DEFAULT 0,
|
|
68
|
+
cache_read_tokens INTEGER NOT NULL DEFAULT 0,
|
|
69
|
+
cache_write_tokens INTEGER NOT NULL DEFAULT 0,
|
|
70
|
+
reasoning_tokens INTEGER NOT NULL DEFAULT 0,
|
|
71
|
+
total_tokens INTEGER NOT NULL DEFAULT 0,
|
|
72
|
+
estimated_tokens INTEGER NOT NULL DEFAULT 0,
|
|
73
|
+
token_confidence TEXT NOT NULL DEFAULT 'unknown',
|
|
74
|
+
cost REAL,
|
|
75
|
+
cost_estimated INTEGER NOT NULL DEFAULT 0,
|
|
76
|
+
latency_ms INTEGER,
|
|
77
|
+
raw_text_preview TEXT,
|
|
78
|
+
raw_text TEXT,
|
|
79
|
+
raw_metadata TEXT
|
|
80
|
+
);
|
|
81
|
+
CREATE UNIQUE INDEX IF NOT EXISTS interactions_source_id_idx ON interactions(source_id);
|
|
82
|
+
CREATE INDEX IF NOT EXISTS interactions_session_idx ON interactions(session_id);
|
|
83
|
+
CREATE INDEX IF NOT EXISTS interactions_model_idx ON interactions(model_id);
|
|
84
|
+
CREATE INDEX IF NOT EXISTS interactions_timestamp_idx ON interactions(timestamp);
|
|
85
|
+
|
|
86
|
+
PRAGMA user_version;
|
|
87
|
+
|
|
88
|
+
CREATE TABLE IF NOT EXISTS tool_calls (
|
|
89
|
+
id TEXT PRIMARY KEY,
|
|
90
|
+
interaction_id TEXT NOT NULL REFERENCES interactions(id) ON DELETE CASCADE,
|
|
91
|
+
name TEXT NOT NULL,
|
|
92
|
+
status TEXT,
|
|
93
|
+
duration_ms INTEGER,
|
|
94
|
+
raw_metadata TEXT
|
|
95
|
+
);
|
|
96
|
+
CREATE INDEX IF NOT EXISTS tool_calls_interaction_idx ON tool_calls(interaction_id);
|
|
97
|
+
|
|
98
|
+
CREATE TABLE IF NOT EXISTS scan_runs (
|
|
99
|
+
id TEXT PRIMARY KEY,
|
|
100
|
+
started_at INTEGER NOT NULL,
|
|
101
|
+
completed_at INTEGER,
|
|
102
|
+
files_scanned INTEGER NOT NULL DEFAULT 0,
|
|
103
|
+
records_imported INTEGER NOT NULL DEFAULT 0,
|
|
104
|
+
warnings TEXT NOT NULL DEFAULT '[]',
|
|
105
|
+
errors TEXT NOT NULL DEFAULT '[]'
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
CREATE TABLE IF NOT EXISTS scan_files (
|
|
109
|
+
id TEXT PRIMARY KEY,
|
|
110
|
+
scan_run_id TEXT NOT NULL REFERENCES scan_runs(id) ON DELETE CASCADE,
|
|
111
|
+
path TEXT NOT NULL,
|
|
112
|
+
modified_time INTEGER,
|
|
113
|
+
size_bytes INTEGER NOT NULL DEFAULT 0,
|
|
114
|
+
file_hash TEXT,
|
|
115
|
+
parser TEXT,
|
|
116
|
+
status TEXT NOT NULL,
|
|
117
|
+
records_imported INTEGER NOT NULL DEFAULT 0,
|
|
118
|
+
warnings TEXT NOT NULL DEFAULT '[]',
|
|
119
|
+
errors TEXT NOT NULL DEFAULT '[]',
|
|
120
|
+
raw_metadata TEXT
|
|
121
|
+
);
|
|
122
|
+
CREATE INDEX IF NOT EXISTS scan_files_path_hash_idx ON scan_files(path, file_hash);
|
|
123
|
+
CREATE INDEX IF NOT EXISTS scan_files_run_idx ON scan_files(scan_run_id);
|
|
124
|
+
|
|
125
|
+
CREATE TABLE IF NOT EXISTS settings (
|
|
126
|
+
key TEXT PRIMARY KEY,
|
|
127
|
+
value TEXT NOT NULL,
|
|
128
|
+
updated_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000)
|
|
129
|
+
);
|
|
130
|
+
`;
|
|
131
|
+
|
|
132
|
+
export function applyMigrations(sqlite: Database.Database) {
|
|
133
|
+
sqlite.exec(ddl);
|
|
134
|
+
const columns = sqlite.prepare("PRAGMA table_info(interactions)").all() as Array<{ name: string }>;
|
|
135
|
+
if (!columns.some((column) => column.name === "token_confidence")) {
|
|
136
|
+
try {
|
|
137
|
+
sqlite.exec("ALTER TABLE interactions ADD COLUMN token_confidence TEXT NOT NULL DEFAULT 'unknown'");
|
|
138
|
+
} catch (error) {
|
|
139
|
+
if (
|
|
140
|
+
!(error instanceof Error) ||
|
|
141
|
+
!error.message.toLowerCase().includes("duplicate column")
|
|
142
|
+
) {
|
|
143
|
+
throw error;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
package/src/db/reset.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { sqlite } from "./client";
|
|
2
|
+
import { seedDatabase } from "./seed";
|
|
3
|
+
|
|
4
|
+
export function resetDatabase() {
|
|
5
|
+
sqlite.exec(`
|
|
6
|
+
DELETE FROM tool_calls;
|
|
7
|
+
DELETE FROM interactions;
|
|
8
|
+
DELETE FROM sessions;
|
|
9
|
+
DELETE FROM projects;
|
|
10
|
+
DELETE FROM scan_files;
|
|
11
|
+
DELETE FROM scan_runs;
|
|
12
|
+
`);
|
|
13
|
+
seedDatabase();
|
|
14
|
+
}
|
package/src/db/schema.ts
ADDED
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
import { relations, sql } from "drizzle-orm";
|
|
2
|
+
import {
|
|
3
|
+
index,
|
|
4
|
+
integer,
|
|
5
|
+
real,
|
|
6
|
+
sqliteTable,
|
|
7
|
+
text,
|
|
8
|
+
uniqueIndex
|
|
9
|
+
} from "drizzle-orm/sqlite-core";
|
|
10
|
+
|
|
11
|
+
export const providers = sqliteTable("providers", {
|
|
12
|
+
id: text("id").primaryKey(),
|
|
13
|
+
name: text("name").notNull(),
|
|
14
|
+
type: text("type").notNull(),
|
|
15
|
+
createdAt: integer("created_at", { mode: "timestamp_ms" })
|
|
16
|
+
.notNull()
|
|
17
|
+
.$defaultFn(() => new Date())
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
export const tools = sqliteTable(
|
|
21
|
+
"tools",
|
|
22
|
+
{
|
|
23
|
+
id: text("id").primaryKey(),
|
|
24
|
+
providerId: text("provider_id")
|
|
25
|
+
.notNull()
|
|
26
|
+
.references(() => providers.id, { onDelete: "cascade" }),
|
|
27
|
+
name: text("name").notNull(),
|
|
28
|
+
createdAt: integer("created_at", { mode: "timestamp_ms" })
|
|
29
|
+
.notNull()
|
|
30
|
+
.$defaultFn(() => new Date())
|
|
31
|
+
},
|
|
32
|
+
(table) => ({
|
|
33
|
+
providerNameIdx: uniqueIndex("tools_provider_name_idx").on(
|
|
34
|
+
table.providerId,
|
|
35
|
+
table.name
|
|
36
|
+
)
|
|
37
|
+
})
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
export const models = sqliteTable(
|
|
41
|
+
"models",
|
|
42
|
+
{
|
|
43
|
+
id: text("id").primaryKey(),
|
|
44
|
+
providerId: text("provider_id")
|
|
45
|
+
.notNull()
|
|
46
|
+
.references(() => providers.id, { onDelete: "cascade" }),
|
|
47
|
+
name: text("name").notNull(),
|
|
48
|
+
inputTokenPrice: real("input_token_price"),
|
|
49
|
+
outputTokenPrice: real("output_token_price"),
|
|
50
|
+
cachedInputTokenPrice: real("cached_input_token_price"),
|
|
51
|
+
currency: text("currency").notNull().default("USD"),
|
|
52
|
+
effectiveFrom: integer("effective_from", { mode: "timestamp_ms" }),
|
|
53
|
+
rawMetadata: text("raw_metadata", { mode: "json" }).$type<Record<string, unknown>>()
|
|
54
|
+
},
|
|
55
|
+
(table) => ({
|
|
56
|
+
providerModelIdx: uniqueIndex("models_provider_name_idx").on(
|
|
57
|
+
table.providerId,
|
|
58
|
+
table.name
|
|
59
|
+
)
|
|
60
|
+
})
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
export const projects = sqliteTable(
|
|
64
|
+
"projects",
|
|
65
|
+
{
|
|
66
|
+
id: text("id").primaryKey(),
|
|
67
|
+
name: text("name").notNull(),
|
|
68
|
+
path: text("path").notNull(),
|
|
69
|
+
createdAt: integer("created_at", { mode: "timestamp_ms" })
|
|
70
|
+
.notNull()
|
|
71
|
+
.$defaultFn(() => new Date())
|
|
72
|
+
},
|
|
73
|
+
(table) => ({
|
|
74
|
+
pathIdx: uniqueIndex("projects_path_idx").on(table.path)
|
|
75
|
+
})
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
export const sessions = sqliteTable(
|
|
79
|
+
"sessions",
|
|
80
|
+
{
|
|
81
|
+
id: text("id").primaryKey(),
|
|
82
|
+
sourceId: text("source_id").notNull(),
|
|
83
|
+
toolId: text("tool_id")
|
|
84
|
+
.notNull()
|
|
85
|
+
.references(() => tools.id, { onDelete: "cascade" }),
|
|
86
|
+
projectId: text("project_id").references(() => projects.id, {
|
|
87
|
+
onDelete: "set null"
|
|
88
|
+
}),
|
|
89
|
+
startedAt: integer("started_at", { mode: "timestamp_ms" }),
|
|
90
|
+
endedAt: integer("ended_at", { mode: "timestamp_ms" }),
|
|
91
|
+
title: text("title"),
|
|
92
|
+
sourceFile: text("source_file").notNull(),
|
|
93
|
+
rawMetadata: text("raw_metadata", { mode: "json" }).$type<Record<string, unknown>>()
|
|
94
|
+
},
|
|
95
|
+
(table) => ({
|
|
96
|
+
sourceIdx: uniqueIndex("sessions_source_id_idx").on(table.sourceId),
|
|
97
|
+
toolIdx: index("sessions_tool_idx").on(table.toolId),
|
|
98
|
+
projectIdx: index("sessions_project_idx").on(table.projectId),
|
|
99
|
+
startedIdx: index("sessions_started_idx").on(table.startedAt)
|
|
100
|
+
})
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
export const interactions = sqliteTable(
|
|
104
|
+
"interactions",
|
|
105
|
+
{
|
|
106
|
+
id: text("id").primaryKey(),
|
|
107
|
+
sourceId: text("source_id").notNull(),
|
|
108
|
+
sessionId: text("session_id")
|
|
109
|
+
.notNull()
|
|
110
|
+
.references(() => sessions.id, { onDelete: "cascade" }),
|
|
111
|
+
timestamp: integer("timestamp", { mode: "timestamp_ms" }),
|
|
112
|
+
role: text("role").notNull(),
|
|
113
|
+
modelId: text("model_id").references(() => models.id, {
|
|
114
|
+
onDelete: "set null"
|
|
115
|
+
}),
|
|
116
|
+
inputTokens: integer("input_tokens").notNull().default(0),
|
|
117
|
+
outputTokens: integer("output_tokens").notNull().default(0),
|
|
118
|
+
cacheReadTokens: integer("cache_read_tokens").notNull().default(0),
|
|
119
|
+
cacheWriteTokens: integer("cache_write_tokens").notNull().default(0),
|
|
120
|
+
reasoningTokens: integer("reasoning_tokens").notNull().default(0),
|
|
121
|
+
totalTokens: integer("total_tokens").notNull().default(0),
|
|
122
|
+
estimatedTokens: integer("estimated_tokens", { mode: "boolean" })
|
|
123
|
+
.notNull()
|
|
124
|
+
.default(false),
|
|
125
|
+
tokenConfidence: text("token_confidence").notNull().default("unknown"),
|
|
126
|
+
cost: real("cost"),
|
|
127
|
+
costEstimated: integer("cost_estimated", { mode: "boolean" })
|
|
128
|
+
.notNull()
|
|
129
|
+
.default(false),
|
|
130
|
+
latencyMs: integer("latency_ms"),
|
|
131
|
+
rawTextPreview: text("raw_text_preview"),
|
|
132
|
+
rawText: text("raw_text"),
|
|
133
|
+
rawMetadata: text("raw_metadata", { mode: "json" }).$type<Record<string, unknown>>()
|
|
134
|
+
},
|
|
135
|
+
(table) => ({
|
|
136
|
+
sourceIdx: uniqueIndex("interactions_source_id_idx").on(table.sourceId),
|
|
137
|
+
sessionIdx: index("interactions_session_idx").on(table.sessionId),
|
|
138
|
+
modelIdx: index("interactions_model_idx").on(table.modelId),
|
|
139
|
+
timestampIdx: index("interactions_timestamp_idx").on(table.timestamp)
|
|
140
|
+
})
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
export const toolCalls = sqliteTable(
|
|
144
|
+
"tool_calls",
|
|
145
|
+
{
|
|
146
|
+
id: text("id").primaryKey(),
|
|
147
|
+
interactionId: text("interaction_id")
|
|
148
|
+
.notNull()
|
|
149
|
+
.references(() => interactions.id, { onDelete: "cascade" }),
|
|
150
|
+
name: text("name").notNull(),
|
|
151
|
+
status: text("status"),
|
|
152
|
+
durationMs: integer("duration_ms"),
|
|
153
|
+
rawMetadata: text("raw_metadata", { mode: "json" }).$type<Record<string, unknown>>()
|
|
154
|
+
},
|
|
155
|
+
(table) => ({
|
|
156
|
+
interactionIdx: index("tool_calls_interaction_idx").on(table.interactionId)
|
|
157
|
+
})
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
export const scanRuns = sqliteTable("scan_runs", {
|
|
161
|
+
id: text("id").primaryKey(),
|
|
162
|
+
startedAt: integer("started_at", { mode: "timestamp_ms" }).notNull(),
|
|
163
|
+
completedAt: integer("completed_at", { mode: "timestamp_ms" }),
|
|
164
|
+
filesScanned: integer("files_scanned").notNull().default(0),
|
|
165
|
+
recordsImported: integer("records_imported").notNull().default(0),
|
|
166
|
+
warnings: text("warnings", { mode: "json" }).$type<string[]>().notNull().default(sql`'[]'`),
|
|
167
|
+
errors: text("errors", { mode: "json" }).$type<string[]>().notNull().default(sql`'[]'`)
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
export const scanFiles = sqliteTable(
|
|
171
|
+
"scan_files",
|
|
172
|
+
{
|
|
173
|
+
id: text("id").primaryKey(),
|
|
174
|
+
scanRunId: text("scan_run_id")
|
|
175
|
+
.notNull()
|
|
176
|
+
.references(() => scanRuns.id, { onDelete: "cascade" }),
|
|
177
|
+
path: text("path").notNull(),
|
|
178
|
+
modifiedTime: integer("modified_time", { mode: "timestamp_ms" }),
|
|
179
|
+
sizeBytes: integer("size_bytes").notNull().default(0),
|
|
180
|
+
fileHash: text("file_hash"),
|
|
181
|
+
parser: text("parser"),
|
|
182
|
+
status: text("status").notNull(),
|
|
183
|
+
recordsImported: integer("records_imported").notNull().default(0),
|
|
184
|
+
warnings: text("warnings", { mode: "json" }).$type<string[]>().notNull().default(sql`'[]'`),
|
|
185
|
+
errors: text("errors", { mode: "json" }).$type<string[]>().notNull().default(sql`'[]'`),
|
|
186
|
+
rawMetadata: text("raw_metadata", { mode: "json" }).$type<Record<string, unknown>>()
|
|
187
|
+
},
|
|
188
|
+
(table) => ({
|
|
189
|
+
pathHashIdx: index("scan_files_path_hash_idx").on(table.path, table.fileHash),
|
|
190
|
+
scanRunIdx: index("scan_files_run_idx").on(table.scanRunId)
|
|
191
|
+
})
|
|
192
|
+
);
|
|
193
|
+
|
|
194
|
+
export const settings = sqliteTable("settings", {
|
|
195
|
+
key: text("key").primaryKey(),
|
|
196
|
+
value: text("value", { mode: "json" }).$type<unknown>().notNull(),
|
|
197
|
+
updatedAt: integer("updated_at", { mode: "timestamp_ms" })
|
|
198
|
+
.notNull()
|
|
199
|
+
.$defaultFn(() => new Date())
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
export const providerRelations = relations(providers, ({ many }) => ({
|
|
203
|
+
tools: many(tools),
|
|
204
|
+
models: many(models)
|
|
205
|
+
}));
|
|
206
|
+
|
|
207
|
+
export const toolRelations = relations(tools, ({ one, many }) => ({
|
|
208
|
+
provider: one(providers, {
|
|
209
|
+
fields: [tools.providerId],
|
|
210
|
+
references: [providers.id]
|
|
211
|
+
}),
|
|
212
|
+
sessions: many(sessions)
|
|
213
|
+
}));
|
|
214
|
+
|
|
215
|
+
export const modelRelations = relations(models, ({ one, many }) => ({
|
|
216
|
+
provider: one(providers, {
|
|
217
|
+
fields: [models.providerId],
|
|
218
|
+
references: [providers.id]
|
|
219
|
+
}),
|
|
220
|
+
interactions: many(interactions)
|
|
221
|
+
}));
|
|
222
|
+
|
|
223
|
+
export const projectRelations = relations(projects, ({ many }) => ({
|
|
224
|
+
sessions: many(sessions)
|
|
225
|
+
}));
|
|
226
|
+
|
|
227
|
+
export const sessionRelations = relations(sessions, ({ one, many }) => ({
|
|
228
|
+
tool: one(tools, {
|
|
229
|
+
fields: [sessions.toolId],
|
|
230
|
+
references: [tools.id]
|
|
231
|
+
}),
|
|
232
|
+
project: one(projects, {
|
|
233
|
+
fields: [sessions.projectId],
|
|
234
|
+
references: [projects.id]
|
|
235
|
+
}),
|
|
236
|
+
interactions: many(interactions)
|
|
237
|
+
}));
|
|
238
|
+
|
|
239
|
+
export const interactionRelations = relations(interactions, ({ one, many }) => ({
|
|
240
|
+
session: one(sessions, {
|
|
241
|
+
fields: [interactions.sessionId],
|
|
242
|
+
references: [sessions.id]
|
|
243
|
+
}),
|
|
244
|
+
model: one(models, {
|
|
245
|
+
fields: [interactions.modelId],
|
|
246
|
+
references: [models.id]
|
|
247
|
+
}),
|
|
248
|
+
toolCalls: many(toolCalls)
|
|
249
|
+
}));
|
|
250
|
+
|
|
251
|
+
export type Provider = typeof providers.$inferSelect;
|
|
252
|
+
export type Tool = typeof tools.$inferSelect;
|
|
253
|
+
export type Model = typeof models.$inferSelect;
|
|
254
|
+
export type Project = typeof projects.$inferSelect;
|
|
255
|
+
export type Session = typeof sessions.$inferSelect;
|
|
256
|
+
export type Interaction = typeof interactions.$inferSelect;
|
|
257
|
+
export type ToolCall = typeof toolCalls.$inferSelect;
|
|
258
|
+
export type ScanRun = typeof scanRuns.$inferSelect;
|
|
259
|
+
export type ScanFile = typeof scanFiles.$inferSelect;
|