@tokentop/ttop 0.4.0 → 0.5.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.
Files changed (75) hide show
  1. package/package.json +9 -4
  2. package/src/agents/aggregator.ts +8 -1
  3. package/src/agents/types.ts +3 -0
  4. package/src/cli.ts +2 -2
  5. package/src/demo/simulator.ts +1 -1
  6. package/src/plugins/agents/index.ts +2 -0
  7. package/src/plugins/auth-sources.ts +2 -3
  8. package/src/plugins/lifecycle.ts +1 -1
  9. package/src/plugins/loader.test.ts +1 -1
  10. package/src/plugins/loader.ts +15 -7
  11. package/src/plugins/notification-bus.ts +5 -2
  12. package/src/plugins/notifications/terminal-bell.ts +1 -1
  13. package/src/plugins/notifications/visual-flash.ts +1 -1
  14. package/src/plugins/plugin-context-factory.ts +4 -19
  15. package/src/plugins/providers/anthropic.ts +2 -2
  16. package/src/plugins/providers/antigravity.ts +1 -1
  17. package/src/plugins/providers/chutes.ts +189 -0
  18. package/src/plugins/providers/codex.ts +1 -1
  19. package/src/plugins/providers/gemini.ts +1 -1
  20. package/src/plugins/providers/github-copilot.ts +1 -1
  21. package/src/plugins/providers/index.ts +1 -0
  22. package/src/plugins/providers/minimax.ts +1 -1
  23. package/src/plugins/providers/openai-api.ts +1 -1
  24. package/src/plugins/providers/opencode-zen.ts +1 -1
  25. package/src/plugins/providers/perplexity.ts +1 -1
  26. package/src/plugins/providers/zai.ts +1 -1
  27. package/src/plugins/registry.ts +9 -9
  28. package/src/plugins/sandbox-guard.test.ts +1 -1
  29. package/src/plugins/sandbox-guard.ts +1 -1
  30. package/src/plugins/sandbox.ts +1 -1
  31. package/src/plugins/themes/catppuccin-latte.ts +1 -1
  32. package/src/plugins/themes/catppuccin-mocha.ts +1 -1
  33. package/src/plugins/themes/claude-code.ts +1 -1
  34. package/src/plugins/themes/dracula.ts +1 -1
  35. package/src/plugins/themes/github-light.ts +1 -1
  36. package/src/plugins/themes/gruvbox-dark.ts +1 -1
  37. package/src/plugins/themes/gruvbox-light.ts +1 -1
  38. package/src/plugins/themes/kanagawa.ts +1 -1
  39. package/src/plugins/themes/nord.ts +1 -1
  40. package/src/plugins/themes/one-dark.ts +1 -1
  41. package/src/plugins/themes/opencode.ts +1 -1
  42. package/src/plugins/themes/rose-pine-dawn.ts +1 -1
  43. package/src/plugins/themes/rose-pine.ts +1 -1
  44. package/src/plugins/themes/solarized-light.ts +1 -1
  45. package/src/plugins/themes/tokyo-night.ts +1 -1
  46. package/src/plugins/update-checker.ts +2 -2
  47. package/src/pricing/estimator.test.ts +1 -1
  48. package/src/pricing/estimator.ts +1 -1
  49. package/src/pricing/fallback.ts +1 -1
  50. package/src/pricing/index.ts +1 -1
  51. package/src/pricing/models-dev.ts +1 -1
  52. package/src/storage/index.ts +6 -0
  53. package/src/storage/migrations/index.ts +17 -0
  54. package/src/storage/repos/pluginStorage.ts +35 -0
  55. package/src/tui/App.tsx +1 -1
  56. package/src/tui/components/ProviderCard.tsx +1 -1
  57. package/src/tui/components/ProviderDetailPanel.tsx +1 -1
  58. package/src/tui/components/SessionDetailsDrawer.tsx +10 -0
  59. package/src/tui/components/SessionsTable.tsx +9 -2
  60. package/src/tui/components/SettingsModal.tsx +1 -1
  61. package/src/tui/components/ThemePicker.tsx +1 -1
  62. package/src/tui/contexts/AgentSessionContext.tsx +1 -1
  63. package/src/tui/contexts/PluginContext.tsx +8 -4
  64. package/src/tui/contexts/RealTimeActivityContext.tsx +33 -20
  65. package/src/tui/contexts/ThemeContext.tsx +3 -3
  66. package/src/tui/hooks/useSessionEnrichedProviders.ts +1 -1
  67. package/src/tui/views/SettingsView.tsx +1 -1
  68. package/src/version.ts +1 -1
  69. package/src/plugins/types/agent.ts +0 -108
  70. package/src/plugins/types/base.ts +0 -278
  71. package/src/plugins/types/index.ts +0 -19
  72. package/src/plugins/types/notification.ts +0 -51
  73. package/src/plugins/types/provider.ts +0 -237
  74. package/src/plugins/types/theme.ts +0 -78
  75. package/src/storage/database.ts +0 -623
@@ -1,623 +0,0 @@
1
- import { Database } from "bun:sqlite";
2
- import * as fs from "fs/promises";
3
- import { PATHS } from "./paths.ts";
4
-
5
- let db: Database | null = null;
6
-
7
- const SCHEMA = `
8
- CREATE TABLE IF NOT EXISTS usage (
9
- id INTEGER PRIMARY KEY AUTOINCREMENT,
10
- timestamp INTEGER NOT NULL,
11
- provider TEXT NOT NULL,
12
- model TEXT NOT NULL,
13
- agent TEXT,
14
- session_id TEXT,
15
- input_tokens INTEGER NOT NULL DEFAULT 0,
16
- output_tokens INTEGER NOT NULL DEFAULT 0,
17
- cache_read_tokens INTEGER DEFAULT 0,
18
- cache_write_tokens INTEGER DEFAULT 0,
19
- cost_usd REAL NOT NULL DEFAULT 0,
20
- project_path TEXT,
21
- created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now'))
22
- );
23
-
24
- CREATE INDEX IF NOT EXISTS idx_usage_timestamp ON usage(timestamp);
25
- CREATE INDEX IF NOT EXISTS idx_usage_provider ON usage(provider, timestamp);
26
- CREATE INDEX IF NOT EXISTS idx_usage_model ON usage(model, timestamp);
27
- CREATE INDEX IF NOT EXISTS idx_usage_agent ON usage(agent, timestamp);
28
- CREATE INDEX IF NOT EXISTS idx_usage_session ON usage(session_id);
29
-
30
- CREATE TABLE IF NOT EXISTS provider_snapshots (
31
- id INTEGER PRIMARY KEY AUTOINCREMENT,
32
- timestamp INTEGER NOT NULL,
33
- provider TEXT NOT NULL,
34
- used_percent REAL,
35
- limit_reached INTEGER DEFAULT 0,
36
- tokens_input INTEGER,
37
- tokens_output INTEGER,
38
- cost_usd REAL,
39
- raw_json TEXT,
40
- created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now'))
41
- );
42
-
43
- CREATE INDEX IF NOT EXISTS idx_snapshots_provider ON provider_snapshots(provider, timestamp);
44
-
45
- CREATE TABLE IF NOT EXISTS daily_aggregates (
46
- id INTEGER PRIMARY KEY AUTOINCREMENT,
47
- date TEXT NOT NULL,
48
- provider TEXT NOT NULL,
49
- model TEXT,
50
- total_input_tokens INTEGER NOT NULL DEFAULT 0,
51
- total_output_tokens INTEGER NOT NULL DEFAULT 0,
52
- total_cache_read INTEGER DEFAULT 0,
53
- total_cache_write INTEGER DEFAULT 0,
54
- total_cost_usd REAL NOT NULL DEFAULT 0,
55
- request_count INTEGER NOT NULL DEFAULT 0,
56
- UNIQUE(date, provider, model)
57
- );
58
-
59
- CREATE INDEX IF NOT EXISTS idx_daily_date ON daily_aggregates(date);
60
- CREATE INDEX IF NOT EXISTS idx_daily_provider ON daily_aggregates(provider, date);
61
-
62
- CREATE TABLE IF NOT EXISTS plugin_storage (
63
- plugin_id TEXT NOT NULL,
64
- key TEXT NOT NULL,
65
- value TEXT NOT NULL,
66
- updated_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')),
67
- PRIMARY KEY (plugin_id, key)
68
- );
69
-
70
- CREATE INDEX IF NOT EXISTS idx_plugin_storage_plugin ON plugin_storage(plugin_id);
71
- `;
72
-
73
- export async function initDatabase(): Promise<Database> {
74
- if (db) return db;
75
-
76
- await fs.mkdir(PATHS.data.dir, { recursive: true });
77
-
78
- db = new Database(PATHS.data.database, { create: true });
79
- db.exec("PRAGMA journal_mode = WAL");
80
- db.exec("PRAGMA synchronous = NORMAL");
81
- db.exec(SCHEMA);
82
-
83
- return db;
84
- }
85
-
86
- export function getDatabase(): Database {
87
- if (!db) {
88
- throw new Error("Database not initialized. Call initDatabase() first.");
89
- }
90
- return db;
91
- }
92
-
93
- export function closeDatabase(): void {
94
- if (db) {
95
- db.close();
96
- db = null;
97
- }
98
- }
99
-
100
- export interface UsageRecord {
101
- timestamp: number;
102
- provider: string;
103
- model: string;
104
- agent?: string | undefined;
105
- sessionId?: string | undefined;
106
- inputTokens: number;
107
- outputTokens: number;
108
- cacheReadTokens?: number | undefined;
109
- cacheWriteTokens?: number | undefined;
110
- costUsd: number;
111
- projectPath?: string | undefined;
112
- }
113
-
114
- export interface ProviderSnapshot {
115
- timestamp: number;
116
- provider: string;
117
- usedPercent?: number | undefined;
118
- limitReached?: boolean | undefined;
119
- tokensInput?: number | undefined;
120
- tokensOutput?: number | undefined;
121
- costUsd?: number | undefined;
122
- rawJson?: string | undefined;
123
- }
124
-
125
- export interface DailyAggregate {
126
- date: string;
127
- provider: string;
128
- model?: string | undefined;
129
- totalInputTokens: number;
130
- totalOutputTokens: number;
131
- totalCacheRead: number;
132
- totalCacheWrite: number;
133
- totalCostUsd: number;
134
- requestCount: number;
135
- }
136
-
137
- export interface UsageQuery {
138
- startTime?: number;
139
- endTime?: number;
140
- provider?: string;
141
- model?: string;
142
- agent?: string;
143
- sessionId?: string;
144
- limit?: number;
145
- offset?: number;
146
- }
147
-
148
- export function insertUsage(record: UsageRecord): void {
149
- const db = getDatabase();
150
- const stmt = db.prepare(`
151
- INSERT INTO usage (
152
- timestamp, provider, model, agent, session_id,
153
- input_tokens, output_tokens, cache_read_tokens, cache_write_tokens,
154
- cost_usd, project_path
155
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
156
- `);
157
-
158
- stmt.run(
159
- record.timestamp,
160
- record.provider,
161
- record.model,
162
- record.agent ?? null,
163
- record.sessionId ?? null,
164
- record.inputTokens,
165
- record.outputTokens,
166
- record.cacheReadTokens ?? 0,
167
- record.cacheWriteTokens ?? 0,
168
- record.costUsd,
169
- record.projectPath ?? null,
170
- );
171
- }
172
-
173
- export function insertUsageBatch(records: UsageRecord[]): void {
174
- const db = getDatabase();
175
- const stmt = db.prepare(`
176
- INSERT INTO usage (
177
- timestamp, provider, model, agent, session_id,
178
- input_tokens, output_tokens, cache_read_tokens, cache_write_tokens,
179
- cost_usd, project_path
180
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
181
- `);
182
-
183
- const transaction = db.transaction((records: UsageRecord[]) => {
184
- for (const record of records) {
185
- stmt.run(
186
- record.timestamp,
187
- record.provider,
188
- record.model,
189
- record.agent ?? null,
190
- record.sessionId ?? null,
191
- record.inputTokens,
192
- record.outputTokens,
193
- record.cacheReadTokens ?? 0,
194
- record.cacheWriteTokens ?? 0,
195
- record.costUsd,
196
- record.projectPath ?? null,
197
- );
198
- }
199
- });
200
-
201
- transaction(records);
202
- }
203
-
204
- export function insertProviderSnapshot(snapshot: ProviderSnapshot): void {
205
- const db = getDatabase();
206
- const stmt = db.prepare(`
207
- INSERT INTO provider_snapshots (
208
- timestamp, provider, used_percent, limit_reached,
209
- tokens_input, tokens_output, cost_usd, raw_json
210
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
211
- `);
212
-
213
- stmt.run(
214
- snapshot.timestamp,
215
- snapshot.provider,
216
- snapshot.usedPercent ?? null,
217
- snapshot.limitReached ? 1 : 0,
218
- snapshot.tokensInput ?? null,
219
- snapshot.tokensOutput ?? null,
220
- snapshot.costUsd ?? null,
221
- snapshot.rawJson ?? null,
222
- );
223
- }
224
-
225
- export function queryUsage(query: UsageQuery): UsageRecord[] {
226
- const db = getDatabase();
227
- const conditions: string[] = [];
228
- const params: (string | number)[] = [];
229
-
230
- if (query.startTime !== undefined) {
231
- conditions.push("timestamp >= ?");
232
- params.push(query.startTime);
233
- }
234
- if (query.endTime !== undefined) {
235
- conditions.push("timestamp <= ?");
236
- params.push(query.endTime);
237
- }
238
- if (query.provider) {
239
- conditions.push("provider = ?");
240
- params.push(query.provider);
241
- }
242
- if (query.model) {
243
- conditions.push("model = ?");
244
- params.push(query.model);
245
- }
246
- if (query.agent) {
247
- conditions.push("agent = ?");
248
- params.push(query.agent);
249
- }
250
- if (query.sessionId) {
251
- conditions.push("session_id = ?");
252
- params.push(query.sessionId);
253
- }
254
-
255
- const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
256
- const limitClause = query.limit ? `LIMIT ${query.limit}` : "";
257
- const offsetClause = query.offset ? `OFFSET ${query.offset}` : "";
258
-
259
- const sql = `
260
- SELECT * FROM usage
261
- ${whereClause}
262
- ORDER BY timestamp DESC
263
- ${limitClause} ${offsetClause}
264
- `;
265
-
266
- const rows = db.prepare(sql).all(...params) as Array<{
267
- timestamp: number;
268
- provider: string;
269
- model: string;
270
- agent: string | null;
271
- session_id: string | null;
272
- input_tokens: number;
273
- output_tokens: number;
274
- cache_read_tokens: number;
275
- cache_write_tokens: number;
276
- cost_usd: number;
277
- project_path: string | null;
278
- }>;
279
-
280
- return rows.map((row) => ({
281
- timestamp: row.timestamp,
282
- provider: row.provider,
283
- model: row.model,
284
- agent: row.agent ?? undefined,
285
- sessionId: row.session_id ?? undefined,
286
- inputTokens: row.input_tokens,
287
- outputTokens: row.output_tokens,
288
- cacheReadTokens: row.cache_read_tokens,
289
- cacheWriteTokens: row.cache_write_tokens,
290
- costUsd: row.cost_usd,
291
- projectPath: row.project_path ?? undefined,
292
- }));
293
- }
294
-
295
- export interface UsageSummary {
296
- totalInputTokens: number;
297
- totalOutputTokens: number;
298
- totalCacheRead: number;
299
- totalCacheWrite: number;
300
- totalCost: number;
301
- requestCount: number;
302
- }
303
-
304
- export function getUsageSummary(startTime: number, endTime: number): UsageSummary {
305
- const db = getDatabase();
306
- const row = db
307
- .prepare(`
308
- SELECT
309
- COALESCE(SUM(input_tokens), 0) as total_input,
310
- COALESCE(SUM(output_tokens), 0) as total_output,
311
- COALESCE(SUM(cache_read_tokens), 0) as total_cache_read,
312
- COALESCE(SUM(cache_write_tokens), 0) as total_cache_write,
313
- COALESCE(SUM(cost_usd), 0) as total_cost,
314
- COUNT(*) as request_count
315
- FROM usage
316
- WHERE timestamp >= ? AND timestamp <= ?
317
- `)
318
- .get(startTime, endTime) as {
319
- total_input: number;
320
- total_output: number;
321
- total_cache_read: number;
322
- total_cache_write: number;
323
- total_cost: number;
324
- request_count: number;
325
- };
326
-
327
- return {
328
- totalInputTokens: row.total_input,
329
- totalOutputTokens: row.total_output,
330
- totalCacheRead: row.total_cache_read,
331
- totalCacheWrite: row.total_cache_write,
332
- totalCost: row.total_cost,
333
- requestCount: row.request_count,
334
- };
335
- }
336
-
337
- export interface ProviderSummary {
338
- provider: string;
339
- totalInputTokens: number;
340
- totalOutputTokens: number;
341
- totalCost: number;
342
- requestCount: number;
343
- }
344
-
345
- export function getUsageByProvider(startTime: number, endTime: number): ProviderSummary[] {
346
- const db = getDatabase();
347
- const rows = db
348
- .prepare(`
349
- SELECT
350
- provider,
351
- COALESCE(SUM(input_tokens), 0) as total_input,
352
- COALESCE(SUM(output_tokens), 0) as total_output,
353
- COALESCE(SUM(cost_usd), 0) as total_cost,
354
- COUNT(*) as request_count
355
- FROM usage
356
- WHERE timestamp >= ? AND timestamp <= ?
357
- GROUP BY provider
358
- ORDER BY total_cost DESC
359
- `)
360
- .all(startTime, endTime) as Array<{
361
- provider: string;
362
- total_input: number;
363
- total_output: number;
364
- total_cost: number;
365
- request_count: number;
366
- }>;
367
-
368
- return rows.map((row) => ({
369
- provider: row.provider,
370
- totalInputTokens: row.total_input,
371
- totalOutputTokens: row.total_output,
372
- totalCost: row.total_cost,
373
- requestCount: row.request_count,
374
- }));
375
- }
376
-
377
- export interface ModelSummary {
378
- model: string;
379
- provider: string;
380
- totalInputTokens: number;
381
- totalOutputTokens: number;
382
- totalCost: number;
383
- requestCount: number;
384
- }
385
-
386
- export function getUsageByModel(startTime: number, endTime: number): ModelSummary[] {
387
- const db = getDatabase();
388
- const rows = db
389
- .prepare(`
390
- SELECT
391
- model,
392
- provider,
393
- COALESCE(SUM(input_tokens), 0) as total_input,
394
- COALESCE(SUM(output_tokens), 0) as total_output,
395
- COALESCE(SUM(cost_usd), 0) as total_cost,
396
- COUNT(*) as request_count
397
- FROM usage
398
- WHERE timestamp >= ? AND timestamp <= ?
399
- GROUP BY model, provider
400
- ORDER BY total_cost DESC
401
- `)
402
- .all(startTime, endTime) as Array<{
403
- model: string;
404
- provider: string;
405
- total_input: number;
406
- total_output: number;
407
- total_cost: number;
408
- request_count: number;
409
- }>;
410
-
411
- return rows.map((row) => ({
412
- model: row.model,
413
- provider: row.provider,
414
- totalInputTokens: row.total_input,
415
- totalOutputTokens: row.total_output,
416
- totalCost: row.total_cost,
417
- requestCount: row.request_count,
418
- }));
419
- }
420
-
421
- export interface TimeSeriesPoint {
422
- timestamp: number;
423
- totalTokens: number;
424
- totalCost: number;
425
- requestCount: number;
426
- }
427
-
428
- export function getUsageTimeSeries(
429
- startTime: number,
430
- endTime: number,
431
- bucketMinutes: number = 5,
432
- ): TimeSeriesPoint[] {
433
- const db = getDatabase();
434
- const bucketSeconds = bucketMinutes * 60;
435
-
436
- const rows = db
437
- .prepare(`
438
- SELECT
439
- (timestamp / ? * ?) as bucket_time,
440
- COALESCE(SUM(input_tokens + output_tokens), 0) as total_tokens,
441
- COALESCE(SUM(cost_usd), 0) as total_cost,
442
- COUNT(*) as request_count
443
- FROM usage
444
- WHERE timestamp >= ? AND timestamp <= ?
445
- GROUP BY bucket_time
446
- ORDER BY bucket_time ASC
447
- `)
448
- .all(bucketSeconds, bucketSeconds, startTime, endTime) as Array<{
449
- bucket_time: number;
450
- total_tokens: number;
451
- total_cost: number;
452
- request_count: number;
453
- }>;
454
-
455
- return rows.map((row) => ({
456
- timestamp: row.bucket_time,
457
- totalTokens: row.total_tokens,
458
- totalCost: row.total_cost,
459
- requestCount: row.request_count,
460
- }));
461
- }
462
-
463
- export function getProviderSnapshots(
464
- provider: string,
465
- startTime: number,
466
- endTime: number,
467
- limit: number = 100,
468
- ): ProviderSnapshot[] {
469
- const db = getDatabase();
470
- const rows = db
471
- .prepare(`
472
- SELECT * FROM provider_snapshots
473
- WHERE provider = ? AND timestamp >= ? AND timestamp <= ?
474
- ORDER BY timestamp DESC
475
- LIMIT ?
476
- `)
477
- .all(provider, startTime, endTime, limit) as Array<{
478
- timestamp: number;
479
- provider: string;
480
- used_percent: number | null;
481
- limit_reached: number;
482
- tokens_input: number | null;
483
- tokens_output: number | null;
484
- cost_usd: number | null;
485
- raw_json: string | null;
486
- }>;
487
-
488
- return rows.map((row) => ({
489
- timestamp: row.timestamp,
490
- provider: row.provider,
491
- usedPercent: row.used_percent ?? undefined,
492
- limitReached: row.limit_reached === 1,
493
- tokensInput: row.tokens_input ?? undefined,
494
- tokensOutput: row.tokens_output ?? undefined,
495
- costUsd: row.cost_usd ?? undefined,
496
- rawJson: row.raw_json ?? undefined,
497
- }));
498
- }
499
-
500
- export function upsertDailyAggregate(aggregate: DailyAggregate): void {
501
- const db = getDatabase();
502
- const stmt = db.prepare(`
503
- INSERT INTO daily_aggregates (
504
- date, provider, model,
505
- total_input_tokens, total_output_tokens,
506
- total_cache_read, total_cache_write,
507
- total_cost_usd, request_count
508
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
509
- ON CONFLICT(date, provider, model) DO UPDATE SET
510
- total_input_tokens = total_input_tokens + excluded.total_input_tokens,
511
- total_output_tokens = total_output_tokens + excluded.total_output_tokens,
512
- total_cache_read = total_cache_read + excluded.total_cache_read,
513
- total_cache_write = total_cache_write + excluded.total_cache_write,
514
- total_cost_usd = total_cost_usd + excluded.total_cost_usd,
515
- request_count = request_count + excluded.request_count
516
- `);
517
-
518
- stmt.run(
519
- aggregate.date,
520
- aggregate.provider,
521
- aggregate.model ?? null,
522
- aggregate.totalInputTokens,
523
- aggregate.totalOutputTokens,
524
- aggregate.totalCacheRead,
525
- aggregate.totalCacheWrite,
526
- aggregate.totalCostUsd,
527
- aggregate.requestCount,
528
- );
529
- }
530
-
531
- export function getDailyAggregates(
532
- startDate: string,
533
- endDate: string,
534
- provider?: string,
535
- ): DailyAggregate[] {
536
- const db = getDatabase();
537
- const conditions = ["date >= ?", "date <= ?"];
538
- const params: string[] = [startDate, endDate];
539
-
540
- if (provider) {
541
- conditions.push("provider = ?");
542
- params.push(provider);
543
- }
544
-
545
- const rows = db
546
- .prepare(`
547
- SELECT * FROM daily_aggregates
548
- WHERE ${conditions.join(" AND ")}
549
- ORDER BY date DESC, total_cost_usd DESC
550
- `)
551
- .all(...params) as Array<{
552
- date: string;
553
- provider: string;
554
- model: string | null;
555
- total_input_tokens: number;
556
- total_output_tokens: number;
557
- total_cache_read: number;
558
- total_cache_write: number;
559
- total_cost_usd: number;
560
- request_count: number;
561
- }>;
562
-
563
- return rows.map((row) => ({
564
- date: row.date,
565
- provider: row.provider,
566
- model: row.model ?? undefined,
567
- totalInputTokens: row.total_input_tokens,
568
- totalOutputTokens: row.total_output_tokens,
569
- totalCacheRead: row.total_cache_read,
570
- totalCacheWrite: row.total_cache_write,
571
- totalCostUsd: row.total_cost_usd,
572
- requestCount: row.request_count,
573
- }));
574
- }
575
-
576
- export function getTodaySummary(): UsageSummary {
577
- const now = Date.now();
578
- const startOfDay = new Date();
579
- startOfDay.setHours(0, 0, 0, 0);
580
- return getUsageSummary(startOfDay.getTime(), now);
581
- }
582
-
583
- export function getThisMonthSummary(): UsageSummary {
584
- const now = Date.now();
585
- const startOfMonth = new Date();
586
- startOfMonth.setDate(1);
587
- startOfMonth.setHours(0, 0, 0, 0);
588
- return getUsageSummary(startOfMonth.getTime(), now);
589
- }
590
-
591
- // ---------------------------------------------------------------------------
592
- // Plugin KV storage
593
- // ---------------------------------------------------------------------------
594
-
595
- export function pluginStorageGet(pluginId: string, key: string): string | null {
596
- const db = getDatabase();
597
- const row = db
598
- .prepare("SELECT value FROM plugin_storage WHERE plugin_id = ? AND key = ?")
599
- .get(pluginId, key) as { value: string } | null;
600
- return row?.value ?? null;
601
- }
602
-
603
- export function pluginStorageSet(pluginId: string, key: string, value: string): void {
604
- const db = getDatabase();
605
- db.prepare(
606
- `INSERT INTO plugin_storage (plugin_id, key, value, updated_at)
607
- VALUES (?, ?, ?, ?)
608
- ON CONFLICT(plugin_id, key) DO UPDATE SET value = excluded.value, updated_at = excluded.updated_at`,
609
- ).run(pluginId, key, value, Math.floor(Date.now() / 1000));
610
- }
611
-
612
- export function pluginStorageDelete(pluginId: string, key: string): void {
613
- const db = getDatabase();
614
- db.prepare("DELETE FROM plugin_storage WHERE plugin_id = ? AND key = ?").run(pluginId, key);
615
- }
616
-
617
- export function pluginStorageHas(pluginId: string, key: string): boolean {
618
- const db = getDatabase();
619
- const row = db
620
- .prepare("SELECT 1 FROM plugin_storage WHERE plugin_id = ? AND key = ?")
621
- .get(pluginId, key);
622
- return row !== null && row !== undefined;
623
- }