tokentrace 0.8.0 → 0.8.2
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 +26 -0
- package/app/api/prices/refresh/route.ts +18 -3
- package/app/api/prices/route.ts +39 -13
- package/app/api/repair-items/route.ts +6 -1
- package/app/api/scan/route.ts +17 -5
- package/app/api/settings/route.ts +11 -3
- package/app/evidence/page.tsx +3 -3
- package/app/page.tsx +7 -7
- package/dist/runtime/db-seed.mjs +10 -2
- package/dist/runtime/digest.mjs +507 -336
- package/dist/runtime/doctor.mjs +2181 -396
- package/dist/runtime/insights.mjs +783 -316
- package/dist/runtime/pricing-refresh.mjs +1159 -1029
- package/dist/runtime/reset.mjs +10 -2
- package/dist/runtime/scan.mjs +1259 -764
- package/dist/runtime/status.mjs +242 -86
- package/package.json +2 -2
- package/scripts/digest.ts +21 -3
- package/scripts/doctor.ts +22 -4
- package/scripts/insights.ts +18 -2
- package/scripts/pricing-refresh.ts +25 -9
- package/scripts/scan.ts +21 -8
- package/scripts/status.ts +33 -38
- package/src/db/settings.ts +5 -2
- package/src/ingestion/adapters/claude-code.ts +51 -20
- package/src/ingestion/adapters/codex-cli.ts +204 -17
- package/src/ingestion/adapters/generic-json.ts +1 -1
- package/src/ingestion/adapters/generic-jsonl.ts +1 -1
- package/src/ingestion/adapters/generic-log.ts +72 -10
- package/src/ingestion/adapters/helpers.ts +107 -42
- package/src/ingestion/discovery.ts +11 -2
- package/src/ingestion/persist.ts +43 -4
- package/src/ingestion/scan.ts +45 -18
- package/src/lib/analytics.ts +20 -19
- package/src/lib/api-json.ts +46 -0
- package/src/lib/format.ts +12 -1
- package/src/lib/pricing-manifest.ts +10 -2
- package/src/lib/pricing-refresh-cli.ts +57 -0
- package/src/lib/report-cli.ts +36 -0
- package/src/lib/scan-cli.ts +50 -0
- package/src/lib/status-cli.ts +150 -0
package/dist/runtime/digest.mjs
CHANGED
|
@@ -1,19 +1,94 @@
|
|
|
1
1
|
import { createRequire as __tokentraceCreateRequire } from 'node:module'; const require = __tokentraceCreateRequire(import.meta.url);
|
|
2
2
|
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __esm = (fn, res) => function __init() {
|
|
5
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
6
|
+
};
|
|
3
7
|
var __export = (target, all) => {
|
|
4
8
|
for (var name in all)
|
|
5
9
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
6
10
|
};
|
|
7
11
|
|
|
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
12
|
// src/db/migrate-core.ts
|
|
16
|
-
|
|
13
|
+
function applyMigrations(sqlite2) {
|
|
14
|
+
sqlite2.exec(ddl);
|
|
15
|
+
const interactionColumns = sqlite2.prepare("PRAGMA table_info(interactions)").all();
|
|
16
|
+
if (!interactionColumns.some((column) => column.name === "token_confidence")) {
|
|
17
|
+
try {
|
|
18
|
+
sqlite2.exec("ALTER TABLE interactions ADD COLUMN token_confidence TEXT NOT NULL DEFAULT 'unknown'");
|
|
19
|
+
} catch (error) {
|
|
20
|
+
if (!(error instanceof Error) || !error.message.toLowerCase().includes("duplicate column")) {
|
|
21
|
+
throw error;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
const modelColumns = sqlite2.prepare("PRAGMA table_info(models)").all();
|
|
26
|
+
if (!modelColumns.some((column) => column.name === "cache_write_token_price")) {
|
|
27
|
+
try {
|
|
28
|
+
sqlite2.exec("ALTER TABLE models ADD COLUMN cache_write_token_price REAL");
|
|
29
|
+
} catch (error) {
|
|
30
|
+
if (!(error instanceof Error) || !error.message.toLowerCase().includes("duplicate column")) {
|
|
31
|
+
throw error;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
const reviewColumns = sqlite2.prepare("PRAGMA table_info(unknown_cost_reviews)").all();
|
|
36
|
+
const reviewColumnNames = new Set(reviewColumns.map((column) => column.name));
|
|
37
|
+
const addReviewColumn = (name, definition) => {
|
|
38
|
+
if (reviewColumnNames.has(name)) return;
|
|
39
|
+
try {
|
|
40
|
+
sqlite2.exec(`ALTER TABLE unknown_cost_reviews ADD COLUMN ${definition}`);
|
|
41
|
+
reviewColumnNames.add(name);
|
|
42
|
+
} catch (error) {
|
|
43
|
+
if (!(error instanceof Error) || !error.message.toLowerCase().includes("duplicate column")) {
|
|
44
|
+
throw error;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
addReviewColumn("source_file", "source_file TEXT NOT NULL DEFAULT ''");
|
|
49
|
+
addReviewColumn("model", "model TEXT NOT NULL DEFAULT ''");
|
|
50
|
+
addReviewColumn("cause", "cause TEXT NOT NULL DEFAULT ''");
|
|
51
|
+
addReviewColumn("status", "status TEXT NOT NULL DEFAULT 'unresolved'");
|
|
52
|
+
addReviewColumn("notes", "notes TEXT NOT NULL DEFAULT ''");
|
|
53
|
+
addReviewColumn("created_at", "created_at INTEGER NOT NULL DEFAULT 0");
|
|
54
|
+
addReviewColumn("updated_at", "updated_at INTEGER NOT NULL DEFAULT 0");
|
|
55
|
+
const upgradedReviewColumns = sqlite2.prepare("PRAGMA table_info(unknown_cost_reviews)").all();
|
|
56
|
+
const upgradedReviewColumnNames = new Set(upgradedReviewColumns.map((column) => column.name));
|
|
57
|
+
if (upgradedReviewColumnNames.has("state")) {
|
|
58
|
+
sqlite2.exec("UPDATE unknown_cost_reviews SET status = state WHERE status = 'unresolved' AND state <> ''");
|
|
59
|
+
}
|
|
60
|
+
if (upgradedReviewColumnNames.has("note")) {
|
|
61
|
+
sqlite2.exec("UPDATE unknown_cost_reviews SET notes = note WHERE notes = '' AND note <> ''");
|
|
62
|
+
}
|
|
63
|
+
sqlite2.exec(`
|
|
64
|
+
UPDATE unknown_cost_reviews
|
|
65
|
+
SET cause = substr(key, 1, instr(key, ':') - 1)
|
|
66
|
+
WHERE cause = '' AND instr(key, ':') > 0;
|
|
67
|
+
|
|
68
|
+
UPDATE unknown_cost_reviews
|
|
69
|
+
SET created_at = updated_at
|
|
70
|
+
WHERE created_at = 0 AND updated_at > 0;
|
|
71
|
+
|
|
72
|
+
UPDATE unknown_cost_reviews
|
|
73
|
+
SET created_at = unixepoch() * 1000
|
|
74
|
+
WHERE created_at = 0;
|
|
75
|
+
|
|
76
|
+
UPDATE unknown_cost_reviews
|
|
77
|
+
SET updated_at = created_at
|
|
78
|
+
WHERE updated_at = 0;
|
|
79
|
+
`);
|
|
80
|
+
const repairRows = sqlite2.prepare("SELECT key, model FROM unknown_cost_reviews WHERE model = ''").all();
|
|
81
|
+
const updateModel = sqlite2.prepare("UPDATE unknown_cost_reviews SET model = ? WHERE key = ?");
|
|
82
|
+
for (const row of repairRows) {
|
|
83
|
+
const model = row.key.split(":").at(-1) ?? "";
|
|
84
|
+
updateModel.run(model, row.key);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
var ddl;
|
|
88
|
+
var init_migrate_core = __esm({
|
|
89
|
+
"src/db/migrate-core.ts"() {
|
|
90
|
+
"use strict";
|
|
91
|
+
ddl = `
|
|
17
92
|
PRAGMA journal_mode = WAL;
|
|
18
93
|
PRAGMA foreign_keys = ON;
|
|
19
94
|
|
|
@@ -153,80 +228,8 @@ CREATE TABLE IF NOT EXISTS unknown_cost_reviews (
|
|
|
153
228
|
updated_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000)
|
|
154
229
|
);
|
|
155
230
|
`;
|
|
156
|
-
function applyMigrations(sqlite2) {
|
|
157
|
-
sqlite2.exec(ddl);
|
|
158
|
-
const interactionColumns = sqlite2.prepare("PRAGMA table_info(interactions)").all();
|
|
159
|
-
if (!interactionColumns.some((column) => column.name === "token_confidence")) {
|
|
160
|
-
try {
|
|
161
|
-
sqlite2.exec("ALTER TABLE interactions ADD COLUMN token_confidence TEXT NOT NULL DEFAULT 'unknown'");
|
|
162
|
-
} catch (error) {
|
|
163
|
-
if (!(error instanceof Error) || !error.message.toLowerCase().includes("duplicate column")) {
|
|
164
|
-
throw error;
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
const modelColumns = sqlite2.prepare("PRAGMA table_info(models)").all();
|
|
169
|
-
if (!modelColumns.some((column) => column.name === "cache_write_token_price")) {
|
|
170
|
-
try {
|
|
171
|
-
sqlite2.exec("ALTER TABLE models ADD COLUMN cache_write_token_price REAL");
|
|
172
|
-
} catch (error) {
|
|
173
|
-
if (!(error instanceof Error) || !error.message.toLowerCase().includes("duplicate column")) {
|
|
174
|
-
throw error;
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
const reviewColumns = sqlite2.prepare("PRAGMA table_info(unknown_cost_reviews)").all();
|
|
179
|
-
const reviewColumnNames = new Set(reviewColumns.map((column) => column.name));
|
|
180
|
-
const addReviewColumn = (name, definition) => {
|
|
181
|
-
if (reviewColumnNames.has(name)) return;
|
|
182
|
-
try {
|
|
183
|
-
sqlite2.exec(`ALTER TABLE unknown_cost_reviews ADD COLUMN ${definition}`);
|
|
184
|
-
reviewColumnNames.add(name);
|
|
185
|
-
} catch (error) {
|
|
186
|
-
if (!(error instanceof Error) || !error.message.toLowerCase().includes("duplicate column")) {
|
|
187
|
-
throw error;
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
};
|
|
191
|
-
addReviewColumn("source_file", "source_file TEXT NOT NULL DEFAULT ''");
|
|
192
|
-
addReviewColumn("model", "model TEXT NOT NULL DEFAULT ''");
|
|
193
|
-
addReviewColumn("cause", "cause TEXT NOT NULL DEFAULT ''");
|
|
194
|
-
addReviewColumn("status", "status TEXT NOT NULL DEFAULT 'unresolved'");
|
|
195
|
-
addReviewColumn("notes", "notes TEXT NOT NULL DEFAULT ''");
|
|
196
|
-
addReviewColumn("created_at", "created_at INTEGER NOT NULL DEFAULT 0");
|
|
197
|
-
addReviewColumn("updated_at", "updated_at INTEGER NOT NULL DEFAULT 0");
|
|
198
|
-
const upgradedReviewColumns = sqlite2.prepare("PRAGMA table_info(unknown_cost_reviews)").all();
|
|
199
|
-
const upgradedReviewColumnNames = new Set(upgradedReviewColumns.map((column) => column.name));
|
|
200
|
-
if (upgradedReviewColumnNames.has("state")) {
|
|
201
|
-
sqlite2.exec("UPDATE unknown_cost_reviews SET status = state WHERE status = 'unresolved' AND state <> ''");
|
|
202
|
-
}
|
|
203
|
-
if (upgradedReviewColumnNames.has("note")) {
|
|
204
|
-
sqlite2.exec("UPDATE unknown_cost_reviews SET notes = note WHERE notes = '' AND note <> ''");
|
|
205
|
-
}
|
|
206
|
-
sqlite2.exec(`
|
|
207
|
-
UPDATE unknown_cost_reviews
|
|
208
|
-
SET cause = substr(key, 1, instr(key, ':') - 1)
|
|
209
|
-
WHERE cause = '' AND instr(key, ':') > 0;
|
|
210
|
-
|
|
211
|
-
UPDATE unknown_cost_reviews
|
|
212
|
-
SET created_at = updated_at
|
|
213
|
-
WHERE created_at = 0 AND updated_at > 0;
|
|
214
|
-
|
|
215
|
-
UPDATE unknown_cost_reviews
|
|
216
|
-
SET created_at = unixepoch() * 1000
|
|
217
|
-
WHERE created_at = 0;
|
|
218
|
-
|
|
219
|
-
UPDATE unknown_cost_reviews
|
|
220
|
-
SET updated_at = created_at
|
|
221
|
-
WHERE updated_at = 0;
|
|
222
|
-
`);
|
|
223
|
-
const repairRows = sqlite2.prepare("SELECT key, model FROM unknown_cost_reviews WHERE model = ''").all();
|
|
224
|
-
const updateModel = sqlite2.prepare("UPDATE unknown_cost_reviews SET model = ? WHERE key = ?");
|
|
225
|
-
for (const row of repairRows) {
|
|
226
|
-
const model = row.key.split(":").at(-1) ?? "";
|
|
227
|
-
updateModel.run(model, row.key);
|
|
228
231
|
}
|
|
229
|
-
}
|
|
232
|
+
});
|
|
230
233
|
|
|
231
234
|
// src/db/schema.ts
|
|
232
235
|
var schema_exports = {};
|
|
@@ -258,220 +261,230 @@ import {
|
|
|
258
261
|
text,
|
|
259
262
|
uniqueIndex
|
|
260
263
|
} from "drizzle-orm/sqlite-core";
|
|
261
|
-
var providers
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
)
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
)
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
)
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
264
|
+
var providers, tools, models, projects, sessions, interactions, toolCalls, scanRuns, scanFiles, settings, unknownCostReviews, providerRelations, toolRelations, modelRelations, projectRelations, sessionRelations, interactionRelations;
|
|
265
|
+
var init_schema = __esm({
|
|
266
|
+
"src/db/schema.ts"() {
|
|
267
|
+
"use strict";
|
|
268
|
+
providers = sqliteTable("providers", {
|
|
269
|
+
id: text("id").primaryKey(),
|
|
270
|
+
name: text("name").notNull(),
|
|
271
|
+
type: text("type").notNull(),
|
|
272
|
+
createdAt: integer("created_at", { mode: "timestamp_ms" }).notNull().$defaultFn(() => /* @__PURE__ */ new Date())
|
|
273
|
+
});
|
|
274
|
+
tools = sqliteTable(
|
|
275
|
+
"tools",
|
|
276
|
+
{
|
|
277
|
+
id: text("id").primaryKey(),
|
|
278
|
+
providerId: text("provider_id").notNull().references(() => providers.id, { onDelete: "cascade" }),
|
|
279
|
+
name: text("name").notNull(),
|
|
280
|
+
createdAt: integer("created_at", { mode: "timestamp_ms" }).notNull().$defaultFn(() => /* @__PURE__ */ new Date())
|
|
281
|
+
},
|
|
282
|
+
(table) => ({
|
|
283
|
+
providerNameIdx: uniqueIndex("tools_provider_name_idx").on(
|
|
284
|
+
table.providerId,
|
|
285
|
+
table.name
|
|
286
|
+
)
|
|
287
|
+
})
|
|
288
|
+
);
|
|
289
|
+
models = sqliteTable(
|
|
290
|
+
"models",
|
|
291
|
+
{
|
|
292
|
+
id: text("id").primaryKey(),
|
|
293
|
+
providerId: text("provider_id").notNull().references(() => providers.id, { onDelete: "cascade" }),
|
|
294
|
+
name: text("name").notNull(),
|
|
295
|
+
inputTokenPrice: real("input_token_price"),
|
|
296
|
+
outputTokenPrice: real("output_token_price"),
|
|
297
|
+
cachedInputTokenPrice: real("cached_input_token_price"),
|
|
298
|
+
cacheWriteTokenPrice: real("cache_write_token_price"),
|
|
299
|
+
currency: text("currency").notNull().default("USD"),
|
|
300
|
+
effectiveFrom: integer("effective_from", { mode: "timestamp_ms" }),
|
|
301
|
+
rawMetadata: text("raw_metadata", { mode: "json" }).$type()
|
|
302
|
+
},
|
|
303
|
+
(table) => ({
|
|
304
|
+
providerModelIdx: uniqueIndex("models_provider_name_idx").on(
|
|
305
|
+
table.providerId,
|
|
306
|
+
table.name
|
|
307
|
+
)
|
|
308
|
+
})
|
|
309
|
+
);
|
|
310
|
+
projects = sqliteTable(
|
|
311
|
+
"projects",
|
|
312
|
+
{
|
|
313
|
+
id: text("id").primaryKey(),
|
|
314
|
+
name: text("name").notNull(),
|
|
315
|
+
path: text("path").notNull(),
|
|
316
|
+
createdAt: integer("created_at", { mode: "timestamp_ms" }).notNull().$defaultFn(() => /* @__PURE__ */ new Date())
|
|
317
|
+
},
|
|
318
|
+
(table) => ({
|
|
319
|
+
pathIdx: uniqueIndex("projects_path_idx").on(table.path)
|
|
320
|
+
})
|
|
321
|
+
);
|
|
322
|
+
sessions = sqliteTable(
|
|
323
|
+
"sessions",
|
|
324
|
+
{
|
|
325
|
+
id: text("id").primaryKey(),
|
|
326
|
+
sourceId: text("source_id").notNull(),
|
|
327
|
+
toolId: text("tool_id").notNull().references(() => tools.id, { onDelete: "cascade" }),
|
|
328
|
+
projectId: text("project_id").references(() => projects.id, {
|
|
329
|
+
onDelete: "set null"
|
|
330
|
+
}),
|
|
331
|
+
startedAt: integer("started_at", { mode: "timestamp_ms" }),
|
|
332
|
+
endedAt: integer("ended_at", { mode: "timestamp_ms" }),
|
|
333
|
+
title: text("title"),
|
|
334
|
+
sourceFile: text("source_file").notNull(),
|
|
335
|
+
rawMetadata: text("raw_metadata", { mode: "json" }).$type()
|
|
336
|
+
},
|
|
337
|
+
(table) => ({
|
|
338
|
+
sourceIdx: uniqueIndex("sessions_source_id_idx").on(table.sourceId),
|
|
339
|
+
toolIdx: index("sessions_tool_idx").on(table.toolId),
|
|
340
|
+
projectIdx: index("sessions_project_idx").on(table.projectId),
|
|
341
|
+
startedIdx: index("sessions_started_idx").on(table.startedAt)
|
|
342
|
+
})
|
|
343
|
+
);
|
|
344
|
+
interactions = sqliteTable(
|
|
345
|
+
"interactions",
|
|
346
|
+
{
|
|
347
|
+
id: text("id").primaryKey(),
|
|
348
|
+
sourceId: text("source_id").notNull(),
|
|
349
|
+
sessionId: text("session_id").notNull().references(() => sessions.id, { onDelete: "cascade" }),
|
|
350
|
+
timestamp: integer("timestamp", { mode: "timestamp_ms" }),
|
|
351
|
+
role: text("role").notNull(),
|
|
352
|
+
modelId: text("model_id").references(() => models.id, {
|
|
353
|
+
onDelete: "set null"
|
|
354
|
+
}),
|
|
355
|
+
inputTokens: integer("input_tokens").notNull().default(0),
|
|
356
|
+
outputTokens: integer("output_tokens").notNull().default(0),
|
|
357
|
+
cacheReadTokens: integer("cache_read_tokens").notNull().default(0),
|
|
358
|
+
cacheWriteTokens: integer("cache_write_tokens").notNull().default(0),
|
|
359
|
+
reasoningTokens: integer("reasoning_tokens").notNull().default(0),
|
|
360
|
+
totalTokens: integer("total_tokens").notNull().default(0),
|
|
361
|
+
estimatedTokens: integer("estimated_tokens", { mode: "boolean" }).notNull().default(false),
|
|
362
|
+
tokenConfidence: text("token_confidence").notNull().default("unknown"),
|
|
363
|
+
cost: real("cost"),
|
|
364
|
+
costEstimated: integer("cost_estimated", { mode: "boolean" }).notNull().default(false),
|
|
365
|
+
latencyMs: integer("latency_ms"),
|
|
366
|
+
rawTextPreview: text("raw_text_preview"),
|
|
367
|
+
rawText: text("raw_text"),
|
|
368
|
+
rawMetadata: text("raw_metadata", { mode: "json" }).$type()
|
|
369
|
+
},
|
|
370
|
+
(table) => ({
|
|
371
|
+
sourceIdx: uniqueIndex("interactions_source_id_idx").on(table.sourceId),
|
|
372
|
+
sessionIdx: index("interactions_session_idx").on(table.sessionId),
|
|
373
|
+
modelIdx: index("interactions_model_idx").on(table.modelId),
|
|
374
|
+
timestampIdx: index("interactions_timestamp_idx").on(table.timestamp)
|
|
375
|
+
})
|
|
376
|
+
);
|
|
377
|
+
toolCalls = sqliteTable(
|
|
378
|
+
"tool_calls",
|
|
379
|
+
{
|
|
380
|
+
id: text("id").primaryKey(),
|
|
381
|
+
interactionId: text("interaction_id").notNull().references(() => interactions.id, { onDelete: "cascade" }),
|
|
382
|
+
name: text("name").notNull(),
|
|
383
|
+
status: text("status"),
|
|
384
|
+
durationMs: integer("duration_ms"),
|
|
385
|
+
rawMetadata: text("raw_metadata", { mode: "json" }).$type()
|
|
386
|
+
},
|
|
387
|
+
(table) => ({
|
|
388
|
+
interactionIdx: index("tool_calls_interaction_idx").on(table.interactionId)
|
|
389
|
+
})
|
|
390
|
+
);
|
|
391
|
+
scanRuns = sqliteTable("scan_runs", {
|
|
392
|
+
id: text("id").primaryKey(),
|
|
393
|
+
startedAt: integer("started_at", { mode: "timestamp_ms" }).notNull(),
|
|
394
|
+
completedAt: integer("completed_at", { mode: "timestamp_ms" }),
|
|
395
|
+
filesScanned: integer("files_scanned").notNull().default(0),
|
|
396
|
+
recordsImported: integer("records_imported").notNull().default(0),
|
|
397
|
+
warnings: text("warnings", { mode: "json" }).$type().notNull().default(sql`'[]'`),
|
|
398
|
+
errors: text("errors", { mode: "json" }).$type().notNull().default(sql`'[]'`)
|
|
399
|
+
});
|
|
400
|
+
scanFiles = sqliteTable(
|
|
401
|
+
"scan_files",
|
|
402
|
+
{
|
|
403
|
+
id: text("id").primaryKey(),
|
|
404
|
+
scanRunId: text("scan_run_id").notNull().references(() => scanRuns.id, { onDelete: "cascade" }),
|
|
405
|
+
path: text("path").notNull(),
|
|
406
|
+
modifiedTime: integer("modified_time", { mode: "timestamp_ms" }),
|
|
407
|
+
sizeBytes: integer("size_bytes").notNull().default(0),
|
|
408
|
+
fileHash: text("file_hash"),
|
|
409
|
+
parser: text("parser"),
|
|
410
|
+
status: text("status").notNull(),
|
|
411
|
+
recordsImported: integer("records_imported").notNull().default(0),
|
|
412
|
+
warnings: text("warnings", { mode: "json" }).$type().notNull().default(sql`'[]'`),
|
|
413
|
+
errors: text("errors", { mode: "json" }).$type().notNull().default(sql`'[]'`),
|
|
414
|
+
rawMetadata: text("raw_metadata", { mode: "json" }).$type()
|
|
415
|
+
},
|
|
416
|
+
(table) => ({
|
|
417
|
+
pathHashIdx: index("scan_files_path_hash_idx").on(table.path, table.fileHash),
|
|
418
|
+
scanRunIdx: index("scan_files_run_idx").on(table.scanRunId)
|
|
419
|
+
})
|
|
420
|
+
);
|
|
421
|
+
settings = sqliteTable("settings", {
|
|
422
|
+
key: text("key").primaryKey(),
|
|
423
|
+
value: text("value", { mode: "json" }).$type().notNull(),
|
|
424
|
+
updatedAt: integer("updated_at", { mode: "timestamp_ms" }).notNull().$defaultFn(() => /* @__PURE__ */ new Date())
|
|
425
|
+
});
|
|
426
|
+
unknownCostReviews = sqliteTable("unknown_cost_reviews", {
|
|
427
|
+
key: text("key").primaryKey(),
|
|
428
|
+
sourceFile: text("source_file").notNull().default(""),
|
|
429
|
+
model: text("model").notNull().default(""),
|
|
430
|
+
cause: text("cause").notNull().default(""),
|
|
431
|
+
status: text("status").notNull().default("unresolved"),
|
|
432
|
+
notes: text("notes").notNull().default(""),
|
|
433
|
+
createdAt: integer("created_at", { mode: "timestamp_ms" }).notNull().$defaultFn(() => /* @__PURE__ */ new Date()),
|
|
434
|
+
updatedAt: integer("updated_at", { mode: "timestamp_ms" }).notNull().$defaultFn(() => /* @__PURE__ */ new Date())
|
|
435
|
+
});
|
|
436
|
+
providerRelations = relations(providers, ({ many }) => ({
|
|
437
|
+
tools: many(tools),
|
|
438
|
+
models: many(models)
|
|
439
|
+
}));
|
|
440
|
+
toolRelations = relations(tools, ({ one, many }) => ({
|
|
441
|
+
provider: one(providers, {
|
|
442
|
+
fields: [tools.providerId],
|
|
443
|
+
references: [providers.id]
|
|
444
|
+
}),
|
|
445
|
+
sessions: many(sessions)
|
|
446
|
+
}));
|
|
447
|
+
modelRelations = relations(models, ({ one, many }) => ({
|
|
448
|
+
provider: one(providers, {
|
|
449
|
+
fields: [models.providerId],
|
|
450
|
+
references: [providers.id]
|
|
451
|
+
}),
|
|
452
|
+
interactions: many(interactions)
|
|
453
|
+
}));
|
|
454
|
+
projectRelations = relations(projects, ({ many }) => ({
|
|
455
|
+
sessions: many(sessions)
|
|
456
|
+
}));
|
|
457
|
+
sessionRelations = relations(sessions, ({ one, many }) => ({
|
|
458
|
+
tool: one(tools, {
|
|
459
|
+
fields: [sessions.toolId],
|
|
460
|
+
references: [tools.id]
|
|
461
|
+
}),
|
|
462
|
+
project: one(projects, {
|
|
463
|
+
fields: [sessions.projectId],
|
|
464
|
+
references: [projects.id]
|
|
465
|
+
}),
|
|
466
|
+
interactions: many(interactions)
|
|
467
|
+
}));
|
|
468
|
+
interactionRelations = relations(interactions, ({ one, many }) => ({
|
|
469
|
+
session: one(sessions, {
|
|
470
|
+
fields: [interactions.sessionId],
|
|
471
|
+
references: [sessions.id]
|
|
472
|
+
}),
|
|
473
|
+
model: one(models, {
|
|
474
|
+
fields: [interactions.modelId],
|
|
475
|
+
references: [models.id]
|
|
476
|
+
}),
|
|
477
|
+
toolCalls: many(toolCalls)
|
|
478
|
+
}));
|
|
479
|
+
}
|
|
428
480
|
});
|
|
429
|
-
var providerRelations = relations(providers, ({ many }) => ({
|
|
430
|
-
tools: many(tools),
|
|
431
|
-
models: many(models)
|
|
432
|
-
}));
|
|
433
|
-
var toolRelations = relations(tools, ({ one, many }) => ({
|
|
434
|
-
provider: one(providers, {
|
|
435
|
-
fields: [tools.providerId],
|
|
436
|
-
references: [providers.id]
|
|
437
|
-
}),
|
|
438
|
-
sessions: many(sessions)
|
|
439
|
-
}));
|
|
440
|
-
var modelRelations = relations(models, ({ one, many }) => ({
|
|
441
|
-
provider: one(providers, {
|
|
442
|
-
fields: [models.providerId],
|
|
443
|
-
references: [providers.id]
|
|
444
|
-
}),
|
|
445
|
-
interactions: many(interactions)
|
|
446
|
-
}));
|
|
447
|
-
var projectRelations = relations(projects, ({ many }) => ({
|
|
448
|
-
sessions: many(sessions)
|
|
449
|
-
}));
|
|
450
|
-
var sessionRelations = relations(sessions, ({ one, many }) => ({
|
|
451
|
-
tool: one(tools, {
|
|
452
|
-
fields: [sessions.toolId],
|
|
453
|
-
references: [tools.id]
|
|
454
|
-
}),
|
|
455
|
-
project: one(projects, {
|
|
456
|
-
fields: [sessions.projectId],
|
|
457
|
-
references: [projects.id]
|
|
458
|
-
}),
|
|
459
|
-
interactions: many(interactions)
|
|
460
|
-
}));
|
|
461
|
-
var interactionRelations = relations(interactions, ({ one, many }) => ({
|
|
462
|
-
session: one(sessions, {
|
|
463
|
-
fields: [interactions.sessionId],
|
|
464
|
-
references: [sessions.id]
|
|
465
|
-
}),
|
|
466
|
-
model: one(models, {
|
|
467
|
-
fields: [interactions.modelId],
|
|
468
|
-
references: [models.id]
|
|
469
|
-
}),
|
|
470
|
-
toolCalls: many(toolCalls)
|
|
471
|
-
}));
|
|
472
481
|
|
|
473
482
|
// src/db/client.ts
|
|
474
|
-
|
|
483
|
+
import fs from "node:fs";
|
|
484
|
+
import path from "node:path";
|
|
485
|
+
import { fileURLToPath } from "node:url";
|
|
486
|
+
import Database from "better-sqlite3";
|
|
487
|
+
import { drizzle } from "drizzle-orm/better-sqlite3";
|
|
475
488
|
function databaseUrlPath(value) {
|
|
476
489
|
if (!value?.startsWith("file:")) return null;
|
|
477
490
|
try {
|
|
@@ -480,13 +493,22 @@ function databaseUrlPath(value) {
|
|
|
480
493
|
return value.slice("file:".length);
|
|
481
494
|
}
|
|
482
495
|
}
|
|
483
|
-
var dbPath
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
496
|
+
var defaultDbPath, dbPath, sqlite, db;
|
|
497
|
+
var init_client = __esm({
|
|
498
|
+
"src/db/client.ts"() {
|
|
499
|
+
"use strict";
|
|
500
|
+
init_migrate_core();
|
|
501
|
+
init_schema();
|
|
502
|
+
defaultDbPath = path.join(process.cwd(), ".tokentrace", "tokentrace.db");
|
|
503
|
+
dbPath = process.env.TOKENTRACE_DB ?? databaseUrlPath(process.env.DATABASE_URL) ?? defaultDbPath;
|
|
504
|
+
fs.mkdirSync(path.dirname(dbPath), { recursive: true });
|
|
505
|
+
sqlite = new Database(dbPath);
|
|
506
|
+
sqlite.pragma("busy_timeout = 10000");
|
|
507
|
+
sqlite.pragma("foreign_keys = ON");
|
|
508
|
+
applyMigrations(sqlite);
|
|
509
|
+
db = drizzle(sqlite, { schema: schema_exports });
|
|
510
|
+
}
|
|
511
|
+
});
|
|
490
512
|
|
|
491
513
|
// src/lib/scan-health.ts
|
|
492
514
|
function increment(target, key, amount = 1) {
|
|
@@ -538,7 +560,6 @@ function joinHealthParts(parts) {
|
|
|
538
560
|
if (parts.length <= 1) return parts[0] ?? "";
|
|
539
561
|
return `${parts.slice(0, -1).join(", ")} and ${parts[parts.length - 1]}`;
|
|
540
562
|
}
|
|
541
|
-
var staleAfterMs = 7 * 24 * 60 * 60 * 1e3;
|
|
542
563
|
function isSuccessfulRun(scanRun) {
|
|
543
564
|
return scanRun.completedAt != null && scanRun.recordsImported > 0 && scanRun.errors.length === 0;
|
|
544
565
|
}
|
|
@@ -687,6 +708,13 @@ function buildScanHealth({
|
|
|
687
708
|
actions
|
|
688
709
|
};
|
|
689
710
|
}
|
|
711
|
+
var staleAfterMs;
|
|
712
|
+
var init_scan_health = __esm({
|
|
713
|
+
"src/lib/scan-health.ts"() {
|
|
714
|
+
"use strict";
|
|
715
|
+
staleAfterMs = 7 * 24 * 60 * 60 * 1e3;
|
|
716
|
+
}
|
|
717
|
+
});
|
|
690
718
|
|
|
691
719
|
// src/lib/model-aliases.ts
|
|
692
720
|
function unique(values) {
|
|
@@ -738,11 +766,17 @@ function modelNameCandidates(modelName) {
|
|
|
738
766
|
addSnapshotDateCandidates(trimmed, candidates);
|
|
739
767
|
return unique(candidates);
|
|
740
768
|
}
|
|
769
|
+
var init_model_aliases = __esm({
|
|
770
|
+
"src/lib/model-aliases.ts"() {
|
|
771
|
+
"use strict";
|
|
772
|
+
}
|
|
773
|
+
});
|
|
741
774
|
|
|
742
775
|
// src/lib/format.ts
|
|
743
776
|
function formatTokens(value) {
|
|
744
777
|
const number3 = value ?? 0;
|
|
745
|
-
if (number3 >=
|
|
778
|
+
if (number3 >= 999995e3) return `${(number3 / 1e9).toFixed(2)}B`;
|
|
779
|
+
if (number3 >= 999950) return `${(number3 / 1e6).toFixed(2)}M`;
|
|
746
780
|
if (number3 >= 1e3) return `${(number3 / 1e3).toFixed(1)}K`;
|
|
747
781
|
return number3.toLocaleString();
|
|
748
782
|
}
|
|
@@ -769,6 +803,11 @@ function formatDate(value) {
|
|
|
769
803
|
function percent(value) {
|
|
770
804
|
return `${Math.round((value ?? 0) * 100)}%`;
|
|
771
805
|
}
|
|
806
|
+
var init_format = __esm({
|
|
807
|
+
"src/lib/format.ts"() {
|
|
808
|
+
"use strict";
|
|
809
|
+
}
|
|
810
|
+
});
|
|
772
811
|
|
|
773
812
|
// src/lib/recommendations.ts
|
|
774
813
|
function recommendation(id, severity, title, evidence, action2, href) {
|
|
@@ -897,17 +936,15 @@ function buildLocalRecommendations(input) {
|
|
|
897
936
|
const rank = { high: 0, medium: 1, low: 2 };
|
|
898
937
|
return recommendations.sort((a, b) => rank[a.severity] - rank[b.severity]).slice(0, 8);
|
|
899
938
|
}
|
|
939
|
+
var init_recommendations = __esm({
|
|
940
|
+
"src/lib/recommendations.ts"() {
|
|
941
|
+
"use strict";
|
|
942
|
+
init_format();
|
|
943
|
+
}
|
|
944
|
+
});
|
|
900
945
|
|
|
901
946
|
// src/db/settings.ts
|
|
902
947
|
import { eq } from "drizzle-orm";
|
|
903
|
-
var defaultSettings = {
|
|
904
|
-
customFolders: [],
|
|
905
|
-
storeRawMessageContent: false,
|
|
906
|
-
usageGuardrails: {
|
|
907
|
-
monthlyCostLimitUsd: null,
|
|
908
|
-
monthlyTokenLimit: null
|
|
909
|
-
}
|
|
910
|
-
};
|
|
911
948
|
function positiveLimit(value) {
|
|
912
949
|
if (typeof value === "number" && Number.isFinite(value) && value > 0) return value;
|
|
913
950
|
if (typeof value === "string" && value.trim()) {
|
|
@@ -928,8 +965,8 @@ function normalizeSettings(value) {
|
|
|
928
965
|
if (!value || typeof value !== "object") return defaultSettings;
|
|
929
966
|
const candidate = value;
|
|
930
967
|
return {
|
|
931
|
-
customFolders: Array.isArray(candidate.customFolders) ? candidate.customFolders.filter((item) => typeof item === "string") : [],
|
|
932
|
-
storeRawMessageContent:
|
|
968
|
+
customFolders: Array.isArray(candidate.customFolders) ? candidate.customFolders.filter((item) => typeof item === "string").map((item) => item.trim()).filter(Boolean) : [],
|
|
969
|
+
storeRawMessageContent: candidate.storeRawMessageContent === true,
|
|
933
970
|
usageGuardrails: normalizeUsageGuardrails(candidate.usageGuardrails)
|
|
934
971
|
};
|
|
935
972
|
}
|
|
@@ -937,6 +974,22 @@ function getAppSettings() {
|
|
|
937
974
|
const row = db.select().from(settings).where(eq(settings.key, "app")).get();
|
|
938
975
|
return normalizeSettings(row?.value);
|
|
939
976
|
}
|
|
977
|
+
var defaultSettings;
|
|
978
|
+
var init_settings = __esm({
|
|
979
|
+
"src/db/settings.ts"() {
|
|
980
|
+
"use strict";
|
|
981
|
+
init_client();
|
|
982
|
+
init_schema();
|
|
983
|
+
defaultSettings = {
|
|
984
|
+
customFolders: [],
|
|
985
|
+
storeRawMessageContent: false,
|
|
986
|
+
usageGuardrails: {
|
|
987
|
+
monthlyCostLimitUsd: null,
|
|
988
|
+
monthlyTokenLimit: null
|
|
989
|
+
}
|
|
990
|
+
};
|
|
991
|
+
}
|
|
992
|
+
});
|
|
940
993
|
|
|
941
994
|
// src/lib/usage-guardrails.ts
|
|
942
995
|
function number(value) {
|
|
@@ -1011,6 +1064,13 @@ function getUsageGuardrailProgress(now = /* @__PURE__ */ new Date()) {
|
|
|
1011
1064
|
now
|
|
1012
1065
|
});
|
|
1013
1066
|
}
|
|
1067
|
+
var init_usage_guardrails = __esm({
|
|
1068
|
+
"src/lib/usage-guardrails.ts"() {
|
|
1069
|
+
"use strict";
|
|
1070
|
+
init_client();
|
|
1071
|
+
init_settings();
|
|
1072
|
+
}
|
|
1073
|
+
});
|
|
1014
1074
|
|
|
1015
1075
|
// src/lib/review-queue.ts
|
|
1016
1076
|
function slug(value) {
|
|
@@ -1166,6 +1226,12 @@ function buildReviewQueue(input) {
|
|
|
1166
1226
|
};
|
|
1167
1227
|
return items.sort((a, b) => severityRank[a.severity] - severityRank[b.severity] || categoryRank[a.category] - categoryRank[b.category]).slice(0, 8);
|
|
1168
1228
|
}
|
|
1229
|
+
var init_review_queue = __esm({
|
|
1230
|
+
"src/lib/review-queue.ts"() {
|
|
1231
|
+
"use strict";
|
|
1232
|
+
init_format();
|
|
1233
|
+
}
|
|
1234
|
+
});
|
|
1169
1235
|
|
|
1170
1236
|
// src/lib/session-comparison.ts
|
|
1171
1237
|
function median(values) {
|
|
@@ -1227,6 +1293,12 @@ function buildSessionComparisons(sessions2) {
|
|
|
1227
1293
|
const severityRank = { high: 0, medium: 1, low: 2 };
|
|
1228
1294
|
return rows2.sort((a, b) => severityRank[a.severity] - severityRank[b.severity] || b.tokenMultiple - a.tokenMultiple).slice(0, 12);
|
|
1229
1295
|
}
|
|
1296
|
+
var init_session_comparison = __esm({
|
|
1297
|
+
"src/lib/session-comparison.ts"() {
|
|
1298
|
+
"use strict";
|
|
1299
|
+
init_format();
|
|
1300
|
+
}
|
|
1301
|
+
});
|
|
1230
1302
|
|
|
1231
1303
|
// src/lib/project-signals.ts
|
|
1232
1304
|
function slug2(value) {
|
|
@@ -1335,6 +1407,12 @@ function buildProjectSignals(input) {
|
|
|
1335
1407
|
};
|
|
1336
1408
|
return signals.sort((a, b) => signalRank[a.signal] - signalRank[b.signal] || severityRank[a.severity] - severityRank[b.severity]).slice(0, 12);
|
|
1337
1409
|
}
|
|
1410
|
+
var init_project_signals = __esm({
|
|
1411
|
+
"src/lib/project-signals.ts"() {
|
|
1412
|
+
"use strict";
|
|
1413
|
+
init_format();
|
|
1414
|
+
}
|
|
1415
|
+
});
|
|
1338
1416
|
|
|
1339
1417
|
// src/lib/evidence-trail.ts
|
|
1340
1418
|
function withQuery2(path2, params) {
|
|
@@ -1348,8 +1426,22 @@ function withQuery2(path2, params) {
|
|
|
1348
1426
|
function evidenceHref(metric2, params = {}) {
|
|
1349
1427
|
return withQuery2("/evidence", { metric: metric2, ...params });
|
|
1350
1428
|
}
|
|
1429
|
+
var init_evidence_trail = __esm({
|
|
1430
|
+
"src/lib/evidence-trail.ts"() {
|
|
1431
|
+
"use strict";
|
|
1432
|
+
init_client();
|
|
1433
|
+
}
|
|
1434
|
+
});
|
|
1351
1435
|
|
|
1352
1436
|
// src/lib/analytics.ts
|
|
1437
|
+
var analytics_exports = {};
|
|
1438
|
+
__export(analytics_exports, {
|
|
1439
|
+
getAnalyticsData: () => getAnalyticsData,
|
|
1440
|
+
getDebugData: () => getDebugData,
|
|
1441
|
+
getPricedModelCount: () => getPricedModelCount,
|
|
1442
|
+
getScanConfidenceSummary: () => getScanConfidenceSummary,
|
|
1443
|
+
getScanTrustData: () => getScanTrustData
|
|
1444
|
+
});
|
|
1353
1445
|
function number2(value) {
|
|
1354
1446
|
return typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
1355
1447
|
}
|
|
@@ -1476,7 +1568,6 @@ function getSummary(filters = {}) {
|
|
|
1476
1568
|
mostUsedModel: model?.name ?? "No data"
|
|
1477
1569
|
};
|
|
1478
1570
|
}
|
|
1479
|
-
var sevenDaysMs = 7 * 24 * 60 * 60 * 1e3;
|
|
1480
1571
|
function comparisonSnapshot(summary) {
|
|
1481
1572
|
return {
|
|
1482
1573
|
totalTokens: summary.totalTokens,
|
|
@@ -2126,6 +2217,12 @@ function getAnalyticsData(filters = {}) {
|
|
|
2126
2217
|
insights
|
|
2127
2218
|
};
|
|
2128
2219
|
}
|
|
2220
|
+
function getDebugData() {
|
|
2221
|
+
return {
|
|
2222
|
+
scanRuns: getScanRunRows(50),
|
|
2223
|
+
scanFiles: getScanFileRows(500)
|
|
2224
|
+
};
|
|
2225
|
+
}
|
|
2129
2226
|
function getScanRunRows(limit) {
|
|
2130
2227
|
return rows(
|
|
2131
2228
|
`SELECT id, started_at AS startedAt, completed_at AS completedAt,
|
|
@@ -2175,26 +2272,27 @@ function getScanConfidenceSummary() {
|
|
|
2175
2272
|
FROM interactions`
|
|
2176
2273
|
).get();
|
|
2177
2274
|
const unknownCostCauses = sqlite.prepare(
|
|
2178
|
-
`
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
AND COALESCE(i.total_tokens, 0) > 0
|
|
2185
|
-
AND (m.input_token_price IS NULL OR m.output_token_price IS NULL)
|
|
2186
|
-
THEN 1 ELSE 0 END), 0) AS missingPricing,
|
|
2187
|
-
COALESCE(SUM(CASE WHEN cost IS NULL
|
|
2188
|
-
AND NOT (
|
|
2189
|
-
lower(m.name) = 'unknown'
|
|
2190
|
-
OR COALESCE(i.total_tokens, 0) <= 0
|
|
2191
|
-
OR (lower(m.name) <> 'unknown'
|
|
2275
|
+
`WITH unknown_costs AS (
|
|
2276
|
+
SELECT
|
|
2277
|
+
CASE
|
|
2278
|
+
WHEN lower(COALESCE(m.name, 'unknown')) = 'unknown' THEN 'missingModelName'
|
|
2279
|
+
WHEN COALESCE(i.total_tokens, 0) <= 0 THEN 'missingTokenCount'
|
|
2280
|
+
WHEN lower(COALESCE(m.name, 'unknown')) <> 'unknown'
|
|
2192
2281
|
AND COALESCE(i.total_tokens, 0) > 0
|
|
2193
|
-
AND (m.input_token_price IS NULL OR m.output_token_price IS NULL)
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2282
|
+
AND (m.input_token_price IS NULL OR m.output_token_price IS NULL)
|
|
2283
|
+
THEN 'missingPricing'
|
|
2284
|
+
ELSE 'other'
|
|
2285
|
+
END AS cause
|
|
2286
|
+
FROM interactions i
|
|
2287
|
+
LEFT JOIN models m ON m.id = i.model_id
|
|
2288
|
+
WHERE i.cost IS NULL
|
|
2289
|
+
)
|
|
2290
|
+
SELECT
|
|
2291
|
+
COALESCE(SUM(CASE WHEN cause = 'missingModelName' THEN 1 ELSE 0 END), 0) AS missingModelName,
|
|
2292
|
+
COALESCE(SUM(CASE WHEN cause = 'missingTokenCount' THEN 1 ELSE 0 END), 0) AS missingTokenCount,
|
|
2293
|
+
COALESCE(SUM(CASE WHEN cause = 'missingPricing' THEN 1 ELSE 0 END), 0) AS missingPricing,
|
|
2294
|
+
COALESCE(SUM(CASE WHEN cause = 'other' THEN 1 ELSE 0 END), 0) AS other
|
|
2295
|
+
FROM unknown_costs`
|
|
2198
2296
|
).get();
|
|
2199
2297
|
return {
|
|
2200
2298
|
interactions: number2(row.interactions),
|
|
@@ -2239,8 +2337,29 @@ function getScanTrustData() {
|
|
|
2239
2337
|
})
|
|
2240
2338
|
};
|
|
2241
2339
|
}
|
|
2340
|
+
var sevenDaysMs;
|
|
2341
|
+
var init_analytics = __esm({
|
|
2342
|
+
"src/lib/analytics.ts"() {
|
|
2343
|
+
"use strict";
|
|
2344
|
+
init_client();
|
|
2345
|
+
init_scan_health();
|
|
2346
|
+
init_model_aliases();
|
|
2347
|
+
init_recommendations();
|
|
2348
|
+
init_usage_guardrails();
|
|
2349
|
+
init_review_queue();
|
|
2350
|
+
init_session_comparison();
|
|
2351
|
+
init_project_signals();
|
|
2352
|
+
init_evidence_trail();
|
|
2353
|
+
sevenDaysMs = 7 * 24 * 60 * 60 * 1e3;
|
|
2354
|
+
}
|
|
2355
|
+
});
|
|
2242
2356
|
|
|
2243
2357
|
// src/lib/daily-digest.ts
|
|
2358
|
+
var daily_digest_exports = {};
|
|
2359
|
+
__export(daily_digest_exports, {
|
|
2360
|
+
buildDailyDigest: () => buildDailyDigest,
|
|
2361
|
+
renderDailyDigestText: () => renderDailyDigestText
|
|
2362
|
+
});
|
|
2244
2363
|
function fallbackReviewItem() {
|
|
2245
2364
|
return {
|
|
2246
2365
|
id: "baseline",
|
|
@@ -2299,13 +2418,65 @@ function renderDailyDigestText(digest2) {
|
|
|
2299
2418
|
digest2.latestScan ? `Latest scan: ${digest2.latestScan.headline}, ${digest2.latestScan.recordsImported.toLocaleString()} records from ${digest2.latestScan.filesScanned.toLocaleString()} files` : "Latest scan: no scan yet"
|
|
2300
2419
|
].join("\n");
|
|
2301
2420
|
}
|
|
2421
|
+
var init_daily_digest = __esm({
|
|
2422
|
+
"src/lib/daily-digest.ts"() {
|
|
2423
|
+
"use strict";
|
|
2424
|
+
init_format();
|
|
2425
|
+
}
|
|
2426
|
+
});
|
|
2427
|
+
|
|
2428
|
+
// src/lib/report-cli.ts
|
|
2429
|
+
function jsonReportUsage(command) {
|
|
2430
|
+
return `Usage: tokentrace ${command} [--json]
|
|
2431
|
+
|
|
2432
|
+
Options:
|
|
2433
|
+
--json Print report as JSON
|
|
2434
|
+
-h, --help Print ${command} help`;
|
|
2435
|
+
}
|
|
2436
|
+
function parseJsonReportArgs(argv) {
|
|
2437
|
+
const options2 = {
|
|
2438
|
+
help: false,
|
|
2439
|
+
json: false
|
|
2440
|
+
};
|
|
2441
|
+
for (const arg of argv) {
|
|
2442
|
+
if (arg === "--help" || arg === "-h") {
|
|
2443
|
+
options2.help = true;
|
|
2444
|
+
continue;
|
|
2445
|
+
}
|
|
2446
|
+
if (arg === "--json") {
|
|
2447
|
+
options2.json = true;
|
|
2448
|
+
continue;
|
|
2449
|
+
}
|
|
2450
|
+
if (arg.startsWith("-")) {
|
|
2451
|
+
throw new Error(`Unknown option: ${arg}`);
|
|
2452
|
+
}
|
|
2453
|
+
throw new Error(`Unknown argument: ${arg}`);
|
|
2454
|
+
}
|
|
2455
|
+
return options2;
|
|
2456
|
+
}
|
|
2302
2457
|
|
|
2303
2458
|
// scripts/digest.ts
|
|
2304
2459
|
var args = process.argv.slice(2);
|
|
2305
|
-
var
|
|
2306
|
-
|
|
2460
|
+
var options;
|
|
2461
|
+
try {
|
|
2462
|
+
options = parseJsonReportArgs(args);
|
|
2463
|
+
} catch (error) {
|
|
2464
|
+
console.error(error instanceof Error ? error.message : "Invalid digest arguments.");
|
|
2465
|
+
console.error(jsonReportUsage("digest"));
|
|
2466
|
+
process.exit(1);
|
|
2467
|
+
}
|
|
2468
|
+
if (options.help) {
|
|
2469
|
+
console.log(jsonReportUsage("digest"));
|
|
2470
|
+
process.exit(0);
|
|
2471
|
+
}
|
|
2472
|
+
var [{ getAnalyticsData: getAnalyticsData2, getScanTrustData: getScanTrustData2 }, { buildDailyDigest: buildDailyDigest2, renderDailyDigestText: renderDailyDigestText2 }] = await Promise.all([
|
|
2473
|
+
Promise.resolve().then(() => (init_analytics(), analytics_exports)),
|
|
2474
|
+
Promise.resolve().then(() => (init_daily_digest(), daily_digest_exports))
|
|
2475
|
+
]);
|
|
2476
|
+
var data = getAnalyticsData2();
|
|
2477
|
+
var trust = getScanTrustData2();
|
|
2307
2478
|
var latestRun = trust.health.latestRun;
|
|
2308
|
-
var digest =
|
|
2479
|
+
var digest = buildDailyDigest2({
|
|
2309
2480
|
summary: data.summary,
|
|
2310
2481
|
usageGuardrails: data.usageGuardrails,
|
|
2311
2482
|
reviewQueue: data.reviewQueue,
|
|
@@ -2317,8 +2488,8 @@ var digest = buildDailyDigest({
|
|
|
2317
2488
|
filesScanned: latestRun.filesScanned
|
|
2318
2489
|
} : null
|
|
2319
2490
|
});
|
|
2320
|
-
if (
|
|
2491
|
+
if (options.json) {
|
|
2321
2492
|
console.log(JSON.stringify(digest, null, 2));
|
|
2322
2493
|
} else {
|
|
2323
|
-
console.log(
|
|
2494
|
+
console.log(renderDailyDigestText2(digest));
|
|
2324
2495
|
}
|