@tokentop/ttop 0.2.2 → 0.3.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/README.md +2 -5
- package/package.json +8 -5
- package/src/agents/aggregator.ts +39 -19
- package/src/agents/costing.ts +11 -9
- package/src/agents/index.ts +7 -9
- package/src/agents/types.ts +2 -2
- package/src/cli.ts +28 -23
- package/src/config/schema.test.ts +42 -40
- package/src/config/schema.ts +26 -26
- package/src/demo/simulator.ts +161 -122
- package/src/plugins/agents/index.ts +2 -2
- package/src/plugins/auth-sources.ts +42 -38
- package/src/plugins/lifecycle.ts +32 -32
- package/src/plugins/loader.test.ts +49 -49
- package/src/plugins/loader.ts +67 -63
- package/src/plugins/notification-bus.ts +21 -16
- package/src/plugins/notifications/index.ts +2 -2
- package/src/plugins/notifications/terminal-bell.ts +29 -23
- package/src/plugins/notifications/visual-flash.ts +21 -19
- package/src/plugins/npm-installer.ts +23 -25
- package/src/plugins/plugin-context-factory.ts +10 -10
- package/src/plugins/plugin-host.ts +12 -5
- package/src/plugins/providers/anthropic.ts +64 -57
- package/src/plugins/providers/antigravity.ts +81 -69
- package/src/plugins/providers/codex.ts +42 -42
- package/src/plugins/providers/gemini.ts +106 -87
- package/src/plugins/providers/github-copilot.ts +49 -45
- package/src/plugins/providers/index.ts +10 -12
- package/src/plugins/providers/minimax.ts +50 -44
- package/src/plugins/providers/openai-api.ts +82 -62
- package/src/plugins/providers/opencode-zen.ts +46 -39
- package/src/plugins/providers/perplexity.ts +55 -46
- package/src/plugins/providers/zai.ts +66 -57
- package/src/plugins/registry.ts +47 -43
- package/src/plugins/sandbox-guard.test.ts +45 -48
- package/src/plugins/sandbox-guard.ts +20 -25
- package/src/plugins/sandbox.ts +26 -48
- package/src/plugins/themes/catppuccin-latte.ts +29 -29
- package/src/plugins/themes/catppuccin-mocha.ts +29 -29
- package/src/plugins/themes/claude-code.ts +29 -29
- package/src/plugins/themes/dracula.ts +29 -29
- package/src/plugins/themes/github-light.ts +29 -29
- package/src/plugins/themes/gruvbox-dark.ts +29 -29
- package/src/plugins/themes/gruvbox-light.ts +29 -29
- package/src/plugins/themes/index.ts +15 -15
- package/src/plugins/themes/kanagawa.ts +29 -29
- package/src/plugins/themes/nord.ts +29 -29
- package/src/plugins/themes/one-dark.ts +29 -29
- package/src/plugins/themes/opencode.ts +31 -31
- package/src/plugins/themes/rose-pine-dawn.ts +29 -29
- package/src/plugins/themes/rose-pine.ts +29 -29
- package/src/plugins/themes/solarized-light.ts +29 -29
- package/src/plugins/themes/tokyo-night.ts +29 -29
- package/src/plugins/types/agent.ts +5 -5
- package/src/plugins/types/base.ts +35 -24
- package/src/plugins/types/index.ts +9 -9
- package/src/plugins/types/notification.ts +12 -12
- package/src/plugins/types/provider.ts +12 -8
- package/src/plugins/types/theme.ts +31 -23
- package/src/plugins/update-checker.ts +6 -6
- package/src/pricing/estimator.test.ts +39 -44
- package/src/pricing/estimator.ts +7 -13
- package/src/pricing/fallback.test.ts +30 -34
- package/src/pricing/fallback.ts +54 -27
- package/src/pricing/index.ts +18 -12
- package/src/pricing/models-dev.ts +6 -6
- package/src/storage/database.ts +56 -46
- package/src/storage/db.ts +12 -12
- package/src/storage/index.ts +44 -45
- package/src/storage/migrations/index.ts +2 -2
- package/src/storage/paths.ts +13 -13
- package/src/storage/repos/agentSessions.ts +33 -23
- package/src/storage/repos/providerSnapshots.ts +5 -5
- package/src/storage/repos/usageEvents.ts +40 -28
- package/src/storage/types.ts +5 -5
- package/src/tui/App.tsx +180 -137
- package/src/tui/components/CommandPalette.tsx +46 -34
- package/src/tui/components/DebugConsole.tsx +39 -33
- package/src/tui/components/DebugPanel.tsx +295 -177
- package/src/tui/components/FooterHints.tsx +2 -2
- package/src/tui/components/GhostProviderCard.tsx +37 -35
- package/src/tui/components/Header.tsx +68 -35
- package/src/tui/components/HelpOverlay.tsx +99 -29
- package/src/tui/components/InlineGauge.tsx +10 -7
- package/src/tui/components/InlineSparkline.tsx +17 -11
- package/src/tui/components/KpiStrip.tsx +128 -98
- package/src/tui/components/LimitGauge.tsx +96 -59
- package/src/tui/components/ModalBackdrop.tsx +4 -4
- package/src/tui/components/ProviderAggregateStrip.tsx +69 -38
- package/src/tui/components/ProviderCard.tsx +232 -226
- package/src/tui/components/ProviderDetailPanel.tsx +133 -58
- package/src/tui/components/ProviderLimitsPanel.tsx +126 -89
- package/src/tui/components/ProvidersList.tsx +215 -126
- package/src/tui/components/SessionDetailsDrawer.tsx +191 -94
- package/src/tui/components/SessionsTable.tsx +313 -166
- package/src/tui/components/SettingsModal.tsx +231 -214
- package/src/tui/components/SidebarBreakdown.tsx +30 -16
- package/src/tui/components/Skeleton.tsx +24 -17
- package/src/tui/components/SmartSidebar.tsx +360 -258
- package/src/tui/components/Sparkline.tsx +64 -52
- package/src/tui/components/Spinner.tsx +3 -3
- package/src/tui/components/StatusBar.tsx +27 -27
- package/src/tui/components/ThemePicker.tsx +67 -66
- package/src/tui/components/Toast.tsx +9 -6
- package/src/tui/components/UsageGauge.tsx +10 -13
- package/src/tui/components/index.ts +5 -5
- package/src/tui/contexts/AgentSessionContext.tsx +193 -144
- package/src/tui/contexts/ConfigContext.tsx +15 -13
- package/src/tui/contexts/DashboardRuntimeContext.tsx +26 -26
- package/src/tui/contexts/DemoModeContext.tsx +22 -15
- package/src/tui/contexts/DrawerContext.tsx +11 -9
- package/src/tui/contexts/InputContext.tsx +2 -2
- package/src/tui/contexts/LogContext.tsx +62 -47
- package/src/tui/contexts/PluginContext.tsx +232 -183
- package/src/tui/contexts/RealTimeActivityContext.tsx +70 -36
- package/src/tui/contexts/StorageContext.tsx +151 -117
- package/src/tui/contexts/ThemeContext.tsx +21 -16
- package/src/tui/contexts/TimeWindowContext.tsx +72 -61
- package/src/tui/contexts/ToastContext.tsx +10 -7
- package/src/tui/contexts/index.ts +9 -5
- package/src/tui/createApp.tsx +8 -12
- package/src/tui/debug/captureFrame.ts +25 -28
- package/src/tui/debug/snapshot.tsx +179 -162
- package/src/tui/driver/assertions.ts +30 -22
- package/src/tui/driver/cli.ts +231 -221
- package/src/tui/driver/coverage.ts +27 -44
- package/src/tui/driver/demo-snapshot.ts +33 -33
- package/src/tui/driver/diff.ts +26 -32
- package/src/tui/driver/driver.ts +43 -48
- package/src/tui/driver/index.ts +36 -40
- package/src/tui/driver/recorder.ts +37 -48
- package/src/tui/driver/test-workflow.ts +51 -51
- package/src/tui/driver/test.ts +25 -25
- package/src/tui/hooks/index.ts +24 -20
- package/src/tui/hooks/useAnimatedValue.ts +13 -18
- package/src/tui/hooks/useDashboardKeyboard.ts +118 -98
- package/src/tui/hooks/useDashboardState.ts +26 -24
- package/src/tui/hooks/useEmaActivity.ts +82 -74
- package/src/tui/hooks/useEntranceAnimation.ts +11 -11
- package/src/tui/hooks/useExitAnimation.ts +3 -3
- package/src/tui/hooks/usePulse.ts +4 -4
- package/src/tui/hooks/useSafeRenderer.ts +5 -5
- package/src/tui/hooks/useSessionEnrichedProviders.ts +150 -0
- package/src/tui/hooks/useValueFlash.ts +10 -14
- package/src/tui/index.tsx +29 -6
- package/src/tui/shutdown.ts +19 -0
- package/src/tui/utils/providerColor.ts +1 -1
- package/src/tui/views/Dashboard.tsx +127 -102
- package/src/tui/views/HistoricalTrendsView.tsx +579 -250
- package/src/tui/views/ProjectsView.tsx +466 -237
- package/src/tui/views/RealTimeDashboard.tsx +150 -111
- package/src/tui/views/SettingsView.tsx +222 -191
- package/src/tui/views/index.ts +2 -2
- package/src/types/opentui-test-utils.d.ts +7 -7
- package/src/utils/clipboard.ts +29 -29
- package/src/utils/currency.test.ts +53 -53
- package/src/utils/currency.ts +35 -32
- package/src/version.ts +1 -1
package/src/pricing/index.ts
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
|
-
import type { ModelPricing, ProviderPlugin, ProviderPricing } from
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import type { ModelPricing, ProviderPlugin, ProviderPricing } from "@/plugins/types/provider.ts";
|
|
2
|
+
import { getFallbackPricing, getFallbackProviderPricing } from "./fallback.ts";
|
|
3
|
+
import {
|
|
4
|
+
clearCache as clearModelsDevCache,
|
|
5
|
+
getModelPricing,
|
|
6
|
+
getProviderModels,
|
|
7
|
+
normalizeProviderName,
|
|
8
|
+
setProviderAliases,
|
|
9
|
+
} from "./models-dev.ts";
|
|
4
10
|
|
|
5
11
|
export function initPricingFromPlugins(
|
|
6
12
|
plugins: readonly ProviderPlugin[],
|
|
@@ -12,8 +18,8 @@ export function initPricingFromPlugins(
|
|
|
12
18
|
const pricing = plugin.pricing;
|
|
13
19
|
if (
|
|
14
20
|
pricing &&
|
|
15
|
-
|
|
16
|
-
typeof (pricing as ProviderPricing).modelsDevProviderId ===
|
|
21
|
+
"modelsDevProviderId" in pricing &&
|
|
22
|
+
typeof (pricing as ProviderPricing).modelsDevProviderId === "string"
|
|
17
23
|
) {
|
|
18
24
|
aliases.set(plugin.id, (pricing as ProviderPricing).modelsDevProviderId!);
|
|
19
25
|
}
|
|
@@ -30,10 +36,10 @@ export function initPricingFromPlugins(
|
|
|
30
36
|
|
|
31
37
|
export async function getPricing(
|
|
32
38
|
providerId: string,
|
|
33
|
-
modelId: string
|
|
39
|
+
modelId: string,
|
|
34
40
|
): Promise<ModelPricing | null> {
|
|
35
41
|
const normalizedProvider = normalizeProviderName(providerId);
|
|
36
|
-
|
|
42
|
+
|
|
37
43
|
const modelsDevPricing = await getModelPricing(normalizedProvider, modelId);
|
|
38
44
|
if (modelsDevPricing) return modelsDevPricing;
|
|
39
45
|
|
|
@@ -44,10 +50,10 @@ export async function getPricing(
|
|
|
44
50
|
}
|
|
45
51
|
|
|
46
52
|
export async function getProviderPricing(
|
|
47
|
-
providerId: string
|
|
53
|
+
providerId: string,
|
|
48
54
|
): Promise<Record<string, ModelPricing>> {
|
|
49
55
|
const normalizedProvider = normalizeProviderName(providerId);
|
|
50
|
-
|
|
56
|
+
|
|
51
57
|
const modelsDevPricing = await getProviderModels(normalizedProvider);
|
|
52
58
|
if (modelsDevPricing && Object.keys(modelsDevPricing).length > 0) {
|
|
53
59
|
return modelsDevPricing;
|
|
@@ -61,6 +67,6 @@ export function clearPricingCache(): void {
|
|
|
61
67
|
clearModelsDevCache();
|
|
62
68
|
}
|
|
63
69
|
|
|
64
|
-
export {
|
|
65
|
-
export
|
|
66
|
-
export { FALLBACK_PRICING } from
|
|
70
|
+
export type { TokenUsage } from "./estimator.ts";
|
|
71
|
+
export { estimateCost, estimateSessionCost, formatCost, formatTokenCount } from "./estimator.ts";
|
|
72
|
+
export { FALLBACK_PRICING } from "./fallback.ts";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { ModelPricing } from
|
|
1
|
+
import type { ModelPricing } from "@/plugins/types/provider.ts";
|
|
2
2
|
|
|
3
|
-
const MODELS_DEV_API =
|
|
3
|
+
const MODELS_DEV_API = "https://models.dev/api.json";
|
|
4
4
|
const CACHE_TTL_MS = 3600000;
|
|
5
5
|
|
|
6
6
|
interface ModelsDevCost {
|
|
@@ -63,7 +63,7 @@ export function normalizeProviderName(providerId: string): string {
|
|
|
63
63
|
|
|
64
64
|
export async function getModelPricing(
|
|
65
65
|
providerId: string,
|
|
66
|
-
modelId: string
|
|
66
|
+
modelId: string,
|
|
67
67
|
): Promise<ModelPricing | null> {
|
|
68
68
|
const data = await fetchModelsDevData();
|
|
69
69
|
if (!data) return null;
|
|
@@ -78,7 +78,7 @@ export async function getModelPricing(
|
|
|
78
78
|
const pricing: ModelPricing = {
|
|
79
79
|
input: model.cost.input,
|
|
80
80
|
output: model.cost.output,
|
|
81
|
-
source:
|
|
81
|
+
source: "models.dev",
|
|
82
82
|
};
|
|
83
83
|
|
|
84
84
|
if (model.cost.cache_read !== undefined) {
|
|
@@ -92,7 +92,7 @@ export async function getModelPricing(
|
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
export async function getProviderModels(
|
|
95
|
-
providerId: string
|
|
95
|
+
providerId: string,
|
|
96
96
|
): Promise<Record<string, ModelPricing> | null> {
|
|
97
97
|
const data = await fetchModelsDevData();
|
|
98
98
|
if (!data) return null;
|
|
@@ -109,7 +109,7 @@ export async function getProviderModels(
|
|
|
109
109
|
const pricing: ModelPricing = {
|
|
110
110
|
input: model.cost.input,
|
|
111
111
|
output: model.cost.output,
|
|
112
|
-
source:
|
|
112
|
+
source: "models.dev",
|
|
113
113
|
};
|
|
114
114
|
|
|
115
115
|
if (model.cost.cache_read !== undefined) {
|
package/src/storage/database.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { Database } from
|
|
2
|
-
import * as fs from
|
|
3
|
-
import { PATHS } from
|
|
1
|
+
import { Database } from "bun:sqlite";
|
|
2
|
+
import * as fs from "fs/promises";
|
|
3
|
+
import { PATHS } from "./paths.ts";
|
|
4
4
|
|
|
5
5
|
let db: Database | null = null;
|
|
6
6
|
|
|
@@ -76,8 +76,8 @@ export async function initDatabase(): Promise<Database> {
|
|
|
76
76
|
await fs.mkdir(PATHS.data.dir, { recursive: true });
|
|
77
77
|
|
|
78
78
|
db = new Database(PATHS.data.database, { create: true });
|
|
79
|
-
db.exec(
|
|
80
|
-
db.exec(
|
|
79
|
+
db.exec("PRAGMA journal_mode = WAL");
|
|
80
|
+
db.exec("PRAGMA synchronous = NORMAL");
|
|
81
81
|
db.exec(SCHEMA);
|
|
82
82
|
|
|
83
83
|
return db;
|
|
@@ -85,7 +85,7 @@ export async function initDatabase(): Promise<Database> {
|
|
|
85
85
|
|
|
86
86
|
export function getDatabase(): Database {
|
|
87
87
|
if (!db) {
|
|
88
|
-
throw new Error(
|
|
88
|
+
throw new Error("Database not initialized. Call initDatabase() first.");
|
|
89
89
|
}
|
|
90
90
|
return db;
|
|
91
91
|
}
|
|
@@ -166,7 +166,7 @@ export function insertUsage(record: UsageRecord): void {
|
|
|
166
166
|
record.cacheReadTokens ?? 0,
|
|
167
167
|
record.cacheWriteTokens ?? 0,
|
|
168
168
|
record.costUsd,
|
|
169
|
-
record.projectPath ?? null
|
|
169
|
+
record.projectPath ?? null,
|
|
170
170
|
);
|
|
171
171
|
}
|
|
172
172
|
|
|
@@ -193,7 +193,7 @@ export function insertUsageBatch(records: UsageRecord[]): void {
|
|
|
193
193
|
record.cacheReadTokens ?? 0,
|
|
194
194
|
record.cacheWriteTokens ?? 0,
|
|
195
195
|
record.costUsd,
|
|
196
|
-
record.projectPath ?? null
|
|
196
|
+
record.projectPath ?? null,
|
|
197
197
|
);
|
|
198
198
|
}
|
|
199
199
|
});
|
|
@@ -218,7 +218,7 @@ export function insertProviderSnapshot(snapshot: ProviderSnapshot): void {
|
|
|
218
218
|
snapshot.tokensInput ?? null,
|
|
219
219
|
snapshot.tokensOutput ?? null,
|
|
220
220
|
snapshot.costUsd ?? null,
|
|
221
|
-
snapshot.rawJson ?? null
|
|
221
|
+
snapshot.rawJson ?? null,
|
|
222
222
|
);
|
|
223
223
|
}
|
|
224
224
|
|
|
@@ -228,33 +228,33 @@ export function queryUsage(query: UsageQuery): UsageRecord[] {
|
|
|
228
228
|
const params: (string | number)[] = [];
|
|
229
229
|
|
|
230
230
|
if (query.startTime !== undefined) {
|
|
231
|
-
conditions.push(
|
|
231
|
+
conditions.push("timestamp >= ?");
|
|
232
232
|
params.push(query.startTime);
|
|
233
233
|
}
|
|
234
234
|
if (query.endTime !== undefined) {
|
|
235
|
-
conditions.push(
|
|
235
|
+
conditions.push("timestamp <= ?");
|
|
236
236
|
params.push(query.endTime);
|
|
237
237
|
}
|
|
238
238
|
if (query.provider) {
|
|
239
|
-
conditions.push(
|
|
239
|
+
conditions.push("provider = ?");
|
|
240
240
|
params.push(query.provider);
|
|
241
241
|
}
|
|
242
242
|
if (query.model) {
|
|
243
|
-
conditions.push(
|
|
243
|
+
conditions.push("model = ?");
|
|
244
244
|
params.push(query.model);
|
|
245
245
|
}
|
|
246
246
|
if (query.agent) {
|
|
247
|
-
conditions.push(
|
|
247
|
+
conditions.push("agent = ?");
|
|
248
248
|
params.push(query.agent);
|
|
249
249
|
}
|
|
250
250
|
if (query.sessionId) {
|
|
251
|
-
conditions.push(
|
|
251
|
+
conditions.push("session_id = ?");
|
|
252
252
|
params.push(query.sessionId);
|
|
253
253
|
}
|
|
254
254
|
|
|
255
|
-
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(
|
|
256
|
-
const limitClause = query.limit ? `LIMIT ${query.limit}` :
|
|
257
|
-
const offsetClause = query.offset ? `OFFSET ${query.offset}` :
|
|
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
258
|
|
|
259
259
|
const sql = `
|
|
260
260
|
SELECT * FROM usage
|
|
@@ -303,7 +303,8 @@ export interface UsageSummary {
|
|
|
303
303
|
|
|
304
304
|
export function getUsageSummary(startTime: number, endTime: number): UsageSummary {
|
|
305
305
|
const db = getDatabase();
|
|
306
|
-
const row = db
|
|
306
|
+
const row = db
|
|
307
|
+
.prepare(`
|
|
307
308
|
SELECT
|
|
308
309
|
COALESCE(SUM(input_tokens), 0) as total_input,
|
|
309
310
|
COALESCE(SUM(output_tokens), 0) as total_output,
|
|
@@ -313,7 +314,8 @@ export function getUsageSummary(startTime: number, endTime: number): UsageSummar
|
|
|
313
314
|
COUNT(*) as request_count
|
|
314
315
|
FROM usage
|
|
315
316
|
WHERE timestamp >= ? AND timestamp <= ?
|
|
316
|
-
`)
|
|
317
|
+
`)
|
|
318
|
+
.get(startTime, endTime) as {
|
|
317
319
|
total_input: number;
|
|
318
320
|
total_output: number;
|
|
319
321
|
total_cache_read: number;
|
|
@@ -342,7 +344,8 @@ export interface ProviderSummary {
|
|
|
342
344
|
|
|
343
345
|
export function getUsageByProvider(startTime: number, endTime: number): ProviderSummary[] {
|
|
344
346
|
const db = getDatabase();
|
|
345
|
-
const rows = db
|
|
347
|
+
const rows = db
|
|
348
|
+
.prepare(`
|
|
346
349
|
SELECT
|
|
347
350
|
provider,
|
|
348
351
|
COALESCE(SUM(input_tokens), 0) as total_input,
|
|
@@ -353,7 +356,8 @@ export function getUsageByProvider(startTime: number, endTime: number): Provider
|
|
|
353
356
|
WHERE timestamp >= ? AND timestamp <= ?
|
|
354
357
|
GROUP BY provider
|
|
355
358
|
ORDER BY total_cost DESC
|
|
356
|
-
`)
|
|
359
|
+
`)
|
|
360
|
+
.all(startTime, endTime) as Array<{
|
|
357
361
|
provider: string;
|
|
358
362
|
total_input: number;
|
|
359
363
|
total_output: number;
|
|
@@ -381,7 +385,8 @@ export interface ModelSummary {
|
|
|
381
385
|
|
|
382
386
|
export function getUsageByModel(startTime: number, endTime: number): ModelSummary[] {
|
|
383
387
|
const db = getDatabase();
|
|
384
|
-
const rows = db
|
|
388
|
+
const rows = db
|
|
389
|
+
.prepare(`
|
|
385
390
|
SELECT
|
|
386
391
|
model,
|
|
387
392
|
provider,
|
|
@@ -393,7 +398,8 @@ export function getUsageByModel(startTime: number, endTime: number): ModelSummar
|
|
|
393
398
|
WHERE timestamp >= ? AND timestamp <= ?
|
|
394
399
|
GROUP BY model, provider
|
|
395
400
|
ORDER BY total_cost DESC
|
|
396
|
-
`)
|
|
401
|
+
`)
|
|
402
|
+
.all(startTime, endTime) as Array<{
|
|
397
403
|
model: string;
|
|
398
404
|
provider: string;
|
|
399
405
|
total_input: number;
|
|
@@ -422,12 +428,13 @@ export interface TimeSeriesPoint {
|
|
|
422
428
|
export function getUsageTimeSeries(
|
|
423
429
|
startTime: number,
|
|
424
430
|
endTime: number,
|
|
425
|
-
bucketMinutes: number = 5
|
|
431
|
+
bucketMinutes: number = 5,
|
|
426
432
|
): TimeSeriesPoint[] {
|
|
427
433
|
const db = getDatabase();
|
|
428
434
|
const bucketSeconds = bucketMinutes * 60;
|
|
429
435
|
|
|
430
|
-
const rows = db
|
|
436
|
+
const rows = db
|
|
437
|
+
.prepare(`
|
|
431
438
|
SELECT
|
|
432
439
|
(timestamp / ? * ?) as bucket_time,
|
|
433
440
|
COALESCE(SUM(input_tokens + output_tokens), 0) as total_tokens,
|
|
@@ -437,7 +444,8 @@ export function getUsageTimeSeries(
|
|
|
437
444
|
WHERE timestamp >= ? AND timestamp <= ?
|
|
438
445
|
GROUP BY bucket_time
|
|
439
446
|
ORDER BY bucket_time ASC
|
|
440
|
-
`)
|
|
447
|
+
`)
|
|
448
|
+
.all(bucketSeconds, bucketSeconds, startTime, endTime) as Array<{
|
|
441
449
|
bucket_time: number;
|
|
442
450
|
total_tokens: number;
|
|
443
451
|
total_cost: number;
|
|
@@ -456,15 +464,17 @@ export function getProviderSnapshots(
|
|
|
456
464
|
provider: string,
|
|
457
465
|
startTime: number,
|
|
458
466
|
endTime: number,
|
|
459
|
-
limit: number = 100
|
|
467
|
+
limit: number = 100,
|
|
460
468
|
): ProviderSnapshot[] {
|
|
461
469
|
const db = getDatabase();
|
|
462
|
-
const rows = db
|
|
470
|
+
const rows = db
|
|
471
|
+
.prepare(`
|
|
463
472
|
SELECT * FROM provider_snapshots
|
|
464
473
|
WHERE provider = ? AND timestamp >= ? AND timestamp <= ?
|
|
465
474
|
ORDER BY timestamp DESC
|
|
466
475
|
LIMIT ?
|
|
467
|
-
`)
|
|
476
|
+
`)
|
|
477
|
+
.all(provider, startTime, endTime, limit) as Array<{
|
|
468
478
|
timestamp: number;
|
|
469
479
|
provider: string;
|
|
470
480
|
used_percent: number | null;
|
|
@@ -514,29 +524,31 @@ export function upsertDailyAggregate(aggregate: DailyAggregate): void {
|
|
|
514
524
|
aggregate.totalCacheRead,
|
|
515
525
|
aggregate.totalCacheWrite,
|
|
516
526
|
aggregate.totalCostUsd,
|
|
517
|
-
aggregate.requestCount
|
|
527
|
+
aggregate.requestCount,
|
|
518
528
|
);
|
|
519
529
|
}
|
|
520
530
|
|
|
521
531
|
export function getDailyAggregates(
|
|
522
532
|
startDate: string,
|
|
523
533
|
endDate: string,
|
|
524
|
-
provider?: string
|
|
534
|
+
provider?: string,
|
|
525
535
|
): DailyAggregate[] {
|
|
526
536
|
const db = getDatabase();
|
|
527
|
-
const conditions = [
|
|
537
|
+
const conditions = ["date >= ?", "date <= ?"];
|
|
528
538
|
const params: string[] = [startDate, endDate];
|
|
529
539
|
|
|
530
540
|
if (provider) {
|
|
531
|
-
conditions.push(
|
|
541
|
+
conditions.push("provider = ?");
|
|
532
542
|
params.push(provider);
|
|
533
543
|
}
|
|
534
544
|
|
|
535
|
-
const rows = db
|
|
545
|
+
const rows = db
|
|
546
|
+
.prepare(`
|
|
536
547
|
SELECT * FROM daily_aggregates
|
|
537
|
-
WHERE ${conditions.join(
|
|
548
|
+
WHERE ${conditions.join(" AND ")}
|
|
538
549
|
ORDER BY date DESC, total_cost_usd DESC
|
|
539
|
-
`)
|
|
550
|
+
`)
|
|
551
|
+
.all(...params) as Array<{
|
|
540
552
|
date: string;
|
|
541
553
|
provider: string;
|
|
542
554
|
model: string | null;
|
|
@@ -582,9 +594,9 @@ export function getThisMonthSummary(): UsageSummary {
|
|
|
582
594
|
|
|
583
595
|
export function pluginStorageGet(pluginId: string, key: string): string | null {
|
|
584
596
|
const db = getDatabase();
|
|
585
|
-
const row = db
|
|
586
|
-
|
|
587
|
-
|
|
597
|
+
const row = db
|
|
598
|
+
.prepare("SELECT value FROM plugin_storage WHERE plugin_id = ? AND key = ?")
|
|
599
|
+
.get(pluginId, key) as { value: string } | null;
|
|
588
600
|
return row?.value ?? null;
|
|
589
601
|
}
|
|
590
602
|
|
|
@@ -599,15 +611,13 @@ export function pluginStorageSet(pluginId: string, key: string, value: string):
|
|
|
599
611
|
|
|
600
612
|
export function pluginStorageDelete(pluginId: string, key: string): void {
|
|
601
613
|
const db = getDatabase();
|
|
602
|
-
db.prepare(
|
|
603
|
-
'DELETE FROM plugin_storage WHERE plugin_id = ? AND key = ?',
|
|
604
|
-
).run(pluginId, key);
|
|
614
|
+
db.prepare("DELETE FROM plugin_storage WHERE plugin_id = ? AND key = ?").run(pluginId, key);
|
|
605
615
|
}
|
|
606
616
|
|
|
607
617
|
export function pluginStorageHas(pluginId: string, key: string): boolean {
|
|
608
618
|
const db = getDatabase();
|
|
609
|
-
const row = db
|
|
610
|
-
|
|
611
|
-
|
|
619
|
+
const row = db
|
|
620
|
+
.prepare("SELECT 1 FROM plugin_storage WHERE plugin_id = ? AND key = ?")
|
|
621
|
+
.get(pluginId, key);
|
|
612
622
|
return row !== null && row !== undefined;
|
|
613
623
|
}
|
package/src/storage/db.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { Database } from
|
|
2
|
-
import * as fs from
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
1
|
+
import { Database } from "bun:sqlite";
|
|
2
|
+
import * as fs from "fs/promises";
|
|
3
|
+
import { applyMigrations } from "./migrations/index.ts";
|
|
4
|
+
import { PATHS } from "./paths.ts";
|
|
5
5
|
|
|
6
6
|
let db: Database | null = null;
|
|
7
7
|
let currentAppRunId: number | null = null;
|
|
@@ -13,10 +13,10 @@ export async function initDatabase(): Promise<Database> {
|
|
|
13
13
|
|
|
14
14
|
db = new Database(PATHS.data.database, { create: true });
|
|
15
15
|
|
|
16
|
-
db.exec(
|
|
17
|
-
db.exec(
|
|
18
|
-
db.exec(
|
|
19
|
-
db.exec(
|
|
16
|
+
db.exec("PRAGMA journal_mode = WAL");
|
|
17
|
+
db.exec("PRAGMA synchronous = NORMAL");
|
|
18
|
+
db.exec("PRAGMA foreign_keys = ON");
|
|
19
|
+
db.exec("PRAGMA busy_timeout = 2000");
|
|
20
20
|
|
|
21
21
|
applyMigrations(db);
|
|
22
22
|
|
|
@@ -27,9 +27,9 @@ export async function initDatabase(): Promise<Database> {
|
|
|
27
27
|
|
|
28
28
|
const result = stmt.run(
|
|
29
29
|
Date.now(),
|
|
30
|
-
process.env.npm_package_version ??
|
|
30
|
+
process.env.npm_package_version ?? "0.0.0",
|
|
31
31
|
process.pid,
|
|
32
|
-
require(
|
|
32
|
+
require("os").hostname(),
|
|
33
33
|
);
|
|
34
34
|
|
|
35
35
|
currentAppRunId = Number(result.lastInsertRowid);
|
|
@@ -39,7 +39,7 @@ export async function initDatabase(): Promise<Database> {
|
|
|
39
39
|
|
|
40
40
|
export function getDatabase(): Database {
|
|
41
41
|
if (!db) {
|
|
42
|
-
throw new Error(
|
|
42
|
+
throw new Error("Database not initialized. Call initDatabase() first.");
|
|
43
43
|
}
|
|
44
44
|
return db;
|
|
45
45
|
}
|
|
@@ -50,7 +50,7 @@ export function getAppRunId(): number | null {
|
|
|
50
50
|
|
|
51
51
|
export function closeDatabase(): void {
|
|
52
52
|
if (db && currentAppRunId) {
|
|
53
|
-
db.prepare(
|
|
53
|
+
db.prepare("UPDATE app_runs SET ended_at = ? WHERE id = ?").run(Date.now(), currentAppRunId);
|
|
54
54
|
}
|
|
55
55
|
if (db) {
|
|
56
56
|
db.close();
|
package/src/storage/index.ts
CHANGED
|
@@ -1,66 +1,65 @@
|
|
|
1
|
-
export { PATHS } from './paths.ts';
|
|
2
|
-
|
|
3
1
|
export {
|
|
4
|
-
initDatabase,
|
|
5
|
-
getDatabase,
|
|
6
2
|
closeDatabase,
|
|
7
|
-
isDatabaseInitialized,
|
|
8
3
|
getAppRunId,
|
|
9
|
-
|
|
10
|
-
|
|
4
|
+
getDatabase,
|
|
5
|
+
initDatabase,
|
|
6
|
+
isDatabaseInitialized,
|
|
7
|
+
} from "./db.ts";
|
|
8
|
+
export { PATHS } from "./paths.ts";
|
|
9
|
+
export type { LatestStreamTotals } from "./repos/agentSessions.ts";
|
|
11
10
|
export {
|
|
11
|
+
getAgentSession,
|
|
12
|
+
getLatestStreamTotalsForAllSessions,
|
|
13
|
+
getRecentSessions,
|
|
14
|
+
getSessionsByProject,
|
|
15
|
+
insertAgentSessionSnapshot,
|
|
16
|
+
upsertAgentSession,
|
|
17
|
+
} from "./repos/agentSessions.ts";
|
|
18
|
+
export {
|
|
19
|
+
getLatestProviderSnapshot,
|
|
12
20
|
insertProviderSnapshot,
|
|
13
21
|
insertProviderSnapshotBatch,
|
|
14
22
|
queryProviderSnapshots,
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
23
|
+
} from "./repos/providerSnapshots.ts";
|
|
24
|
+
export type {
|
|
25
|
+
ModelDailyCost,
|
|
26
|
+
ProjectDailyCost,
|
|
27
|
+
ProviderDailyCost,
|
|
28
|
+
SessionActivityPoint,
|
|
29
|
+
} from "./repos/usageEvents.ts";
|
|
18
30
|
export {
|
|
31
|
+
calculateBurnRate,
|
|
32
|
+
getSessionActivityTimeline,
|
|
33
|
+
getTotalUsageInWindow,
|
|
19
34
|
insertUsageEvent,
|
|
20
35
|
insertUsageEventBatch,
|
|
21
|
-
queryUsageTimeSeries,
|
|
22
|
-
queryProviderDailyCosts,
|
|
23
36
|
queryModelDailyCosts,
|
|
24
37
|
queryProjectDailyCosts,
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
} from './repos/usageEvents.ts';
|
|
29
|
-
|
|
30
|
-
export type { SessionActivityPoint, ProviderDailyCost, ModelDailyCost, ProjectDailyCost } from './repos/usageEvents.ts';
|
|
31
|
-
|
|
32
|
-
export {
|
|
33
|
-
upsertAgentSession,
|
|
34
|
-
insertAgentSessionSnapshot,
|
|
35
|
-
getAgentSession,
|
|
36
|
-
getRecentSessions,
|
|
37
|
-
getSessionsByProject,
|
|
38
|
-
getLatestStreamTotalsForAllSessions,
|
|
39
|
-
} from './repos/agentSessions.ts';
|
|
40
|
-
|
|
41
|
-
export type { LatestStreamTotals } from './repos/agentSessions.ts';
|
|
38
|
+
queryProviderDailyCosts,
|
|
39
|
+
queryUsageTimeSeries,
|
|
40
|
+
} from "./repos/usageEvents.ts";
|
|
42
41
|
|
|
43
42
|
export type {
|
|
44
|
-
UsageEventSource,
|
|
45
|
-
SessionStatus,
|
|
46
|
-
CostSource,
|
|
47
|
-
PricingSource,
|
|
48
|
-
AppRunRow,
|
|
49
|
-
ProviderSnapshotRow,
|
|
50
|
-
ProviderSnapshotInsert,
|
|
51
43
|
AgentSessionDim,
|
|
52
|
-
AgentSessionUpsert,
|
|
53
|
-
AgentSessionSnapshotRow,
|
|
54
44
|
AgentSessionSnapshotInsert,
|
|
45
|
+
AgentSessionSnapshotRow,
|
|
55
46
|
AgentSessionStreamSnapshotRow,
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
47
|
+
AgentSessionUpsert,
|
|
48
|
+
AppRunRow,
|
|
49
|
+
CostSource,
|
|
59
50
|
DailyAggregateRow,
|
|
60
|
-
|
|
51
|
+
HourlyAggregateRow,
|
|
52
|
+
PricingSource,
|
|
53
|
+
ProviderSnapshotInsert,
|
|
54
|
+
ProviderSnapshotRow,
|
|
55
|
+
SessionStatus,
|
|
56
|
+
StreamTotals,
|
|
61
57
|
TimeSeriesFilters,
|
|
58
|
+
TimeSeriesPoint,
|
|
59
|
+
UsageEventInsert,
|
|
60
|
+
UsageEventRow,
|
|
61
|
+
UsageEventSource,
|
|
62
62
|
UsageQueryOptions,
|
|
63
|
-
|
|
64
|
-
} from './types.ts';
|
|
63
|
+
} from "./types.ts";
|
|
65
64
|
|
|
66
|
-
export { computeStreamDelta } from
|
|
65
|
+
export { computeStreamDelta } from "./types.ts";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Database } from
|
|
1
|
+
import type { Database } from "bun:sqlite";
|
|
2
2
|
|
|
3
3
|
export interface Migration {
|
|
4
4
|
version: number;
|
|
@@ -167,7 +167,7 @@ export const migrations: Migration[] = [
|
|
|
167
167
|
];
|
|
168
168
|
|
|
169
169
|
export function getCurrentVersion(db: Database): number {
|
|
170
|
-
const result = db.prepare(
|
|
170
|
+
const result = db.prepare("PRAGMA user_version").get() as { user_version: number } | null;
|
|
171
171
|
return result?.user_version ?? 0;
|
|
172
172
|
}
|
|
173
173
|
|
package/src/storage/paths.ts
CHANGED
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
import * as
|
|
2
|
-
import * as
|
|
1
|
+
import * as os from "os";
|
|
2
|
+
import * as path from "path";
|
|
3
3
|
|
|
4
4
|
const HOME = os.homedir();
|
|
5
5
|
|
|
6
6
|
export const PATHS = {
|
|
7
7
|
config: {
|
|
8
|
-
dir: path.join(HOME,
|
|
9
|
-
file: path.join(HOME,
|
|
10
|
-
fileJsonc: path.join(HOME,
|
|
11
|
-
plugins: path.join(HOME,
|
|
8
|
+
dir: path.join(HOME, ".config/tokentop"),
|
|
9
|
+
file: path.join(HOME, ".config/tokentop/config.json"),
|
|
10
|
+
fileJsonc: path.join(HOME, ".config/tokentop/config.jsonc"),
|
|
11
|
+
plugins: path.join(HOME, ".config/tokentop/plugins"),
|
|
12
12
|
},
|
|
13
13
|
data: {
|
|
14
|
-
dir: path.join(HOME,
|
|
15
|
-
database: path.join(HOME,
|
|
16
|
-
sessions: path.join(HOME,
|
|
17
|
-
cache: path.join(HOME,
|
|
18
|
-
logs: path.join(HOME,
|
|
14
|
+
dir: path.join(HOME, ".local/share/tokentop"),
|
|
15
|
+
database: path.join(HOME, ".local/share/tokentop/usage.db"),
|
|
16
|
+
sessions: path.join(HOME, ".local/share/tokentop/sessions"),
|
|
17
|
+
cache: path.join(HOME, ".local/share/tokentop/cache"),
|
|
18
|
+
logs: path.join(HOME, ".local/share/tokentop/logs"),
|
|
19
19
|
},
|
|
20
20
|
cache: {
|
|
21
|
-
dir: path.join(HOME,
|
|
22
|
-
nodeModules: path.join(HOME,
|
|
21
|
+
dir: path.join(HOME, ".cache/tokentop"),
|
|
22
|
+
nodeModules: path.join(HOME, ".cache/tokentop/node_modules"),
|
|
23
23
|
},
|
|
24
24
|
} as const;
|