@tokentop/ttop 0.2.2 → 0.4.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 (160) hide show
  1. package/README.md +2 -5
  2. package/package.json +8 -5
  3. package/src/agents/aggregator.ts +39 -19
  4. package/src/agents/costing.ts +11 -9
  5. package/src/agents/index.ts +7 -9
  6. package/src/agents/types.ts +2 -2
  7. package/src/cli.ts +28 -23
  8. package/src/config/schema.test.ts +42 -40
  9. package/src/config/schema.ts +26 -26
  10. package/src/demo/simulator.ts +161 -122
  11. package/src/plugins/agents/index.ts +2 -2
  12. package/src/plugins/auth-sources.ts +42 -38
  13. package/src/plugins/lifecycle.ts +42 -38
  14. package/src/plugins/loader.test.ts +49 -49
  15. package/src/plugins/loader.ts +67 -63
  16. package/src/plugins/notification-bus.ts +21 -16
  17. package/src/plugins/notifications/index.ts +2 -2
  18. package/src/plugins/notifications/terminal-bell.ts +36 -23
  19. package/src/plugins/notifications/visual-flash.ts +31 -19
  20. package/src/plugins/npm-installer.ts +23 -25
  21. package/src/plugins/plugin-context-factory.ts +10 -10
  22. package/src/plugins/plugin-host.ts +12 -5
  23. package/src/plugins/providers/anthropic.ts +64 -57
  24. package/src/plugins/providers/antigravity.ts +81 -69
  25. package/src/plugins/providers/codex.ts +42 -42
  26. package/src/plugins/providers/gemini.ts +106 -87
  27. package/src/plugins/providers/github-copilot.ts +49 -45
  28. package/src/plugins/providers/index.ts +10 -12
  29. package/src/plugins/providers/minimax.ts +50 -44
  30. package/src/plugins/providers/openai-api.ts +82 -62
  31. package/src/plugins/providers/opencode-zen.ts +46 -39
  32. package/src/plugins/providers/perplexity.ts +55 -46
  33. package/src/plugins/providers/zai.ts +68 -57
  34. package/src/plugins/registry.ts +80 -71
  35. package/src/plugins/sandbox-guard.test.ts +45 -48
  36. package/src/plugins/sandbox-guard.ts +20 -25
  37. package/src/plugins/sandbox.ts +26 -48
  38. package/src/plugins/themes/catppuccin-latte.ts +29 -29
  39. package/src/plugins/themes/catppuccin-mocha.ts +29 -29
  40. package/src/plugins/themes/claude-code.ts +29 -29
  41. package/src/plugins/themes/dracula.ts +29 -29
  42. package/src/plugins/themes/github-light.ts +29 -29
  43. package/src/plugins/themes/gruvbox-dark.ts +29 -29
  44. package/src/plugins/themes/gruvbox-light.ts +29 -29
  45. package/src/plugins/themes/index.ts +15 -15
  46. package/src/plugins/themes/kanagawa.ts +29 -29
  47. package/src/plugins/themes/nord.ts +29 -29
  48. package/src/plugins/themes/one-dark.ts +29 -29
  49. package/src/plugins/themes/opencode.ts +31 -31
  50. package/src/plugins/themes/rose-pine-dawn.ts +29 -29
  51. package/src/plugins/themes/rose-pine.ts +29 -29
  52. package/src/plugins/themes/solarized-light.ts +29 -29
  53. package/src/plugins/themes/tokyo-night.ts +29 -29
  54. package/src/plugins/types/agent.ts +5 -5
  55. package/src/plugins/types/base.ts +35 -24
  56. package/src/plugins/types/index.ts +9 -9
  57. package/src/plugins/types/notification.ts +12 -12
  58. package/src/plugins/types/provider.ts +12 -8
  59. package/src/plugins/types/theme.ts +31 -23
  60. package/src/plugins/update-checker.ts +6 -6
  61. package/src/pricing/estimator.test.ts +39 -44
  62. package/src/pricing/estimator.ts +7 -13
  63. package/src/pricing/fallback.test.ts +30 -34
  64. package/src/pricing/fallback.ts +54 -27
  65. package/src/pricing/index.ts +18 -12
  66. package/src/pricing/models-dev.ts +6 -6
  67. package/src/storage/database.ts +56 -46
  68. package/src/storage/db.ts +12 -12
  69. package/src/storage/index.ts +44 -45
  70. package/src/storage/migrations/index.ts +2 -2
  71. package/src/storage/paths.ts +13 -13
  72. package/src/storage/persistence-service.ts +206 -0
  73. package/src/storage/repos/agentSessions.ts +33 -23
  74. package/src/storage/repos/providerSnapshots.ts +5 -5
  75. package/src/storage/repos/usageEvents.ts +40 -28
  76. package/src/storage/retention.ts +146 -0
  77. package/src/storage/types.ts +5 -5
  78. package/src/tui/App.tsx +180 -137
  79. package/src/tui/components/CommandPalette.tsx +46 -34
  80. package/src/tui/components/DebugConsole.tsx +39 -33
  81. package/src/tui/components/DebugPanel.tsx +314 -183
  82. package/src/tui/components/FooterHints.tsx +2 -2
  83. package/src/tui/components/GhostProviderCard.tsx +37 -35
  84. package/src/tui/components/Header.tsx +68 -35
  85. package/src/tui/components/HelpOverlay.tsx +99 -29
  86. package/src/tui/components/InlineGauge.tsx +10 -7
  87. package/src/tui/components/InlineSparkline.tsx +17 -11
  88. package/src/tui/components/KpiStrip.tsx +128 -98
  89. package/src/tui/components/LimitGauge.tsx +96 -59
  90. package/src/tui/components/ModalBackdrop.tsx +4 -4
  91. package/src/tui/components/ProviderAggregateStrip.tsx +69 -38
  92. package/src/tui/components/ProviderCard.tsx +232 -226
  93. package/src/tui/components/ProviderDetailPanel.tsx +133 -58
  94. package/src/tui/components/ProviderLimitsPanel.tsx +126 -89
  95. package/src/tui/components/ProvidersList.tsx +215 -126
  96. package/src/tui/components/SessionDetailsDrawer.tsx +191 -94
  97. package/src/tui/components/SessionsTable.tsx +320 -169
  98. package/src/tui/components/SettingsModal.tsx +593 -269
  99. package/src/tui/components/SidebarBreakdown.tsx +30 -16
  100. package/src/tui/components/Skeleton.tsx +24 -17
  101. package/src/tui/components/SmartSidebar.tsx +360 -258
  102. package/src/tui/components/Sparkline.tsx +64 -52
  103. package/src/tui/components/Spinner.tsx +3 -3
  104. package/src/tui/components/StatusBar.tsx +27 -27
  105. package/src/tui/components/ThemePicker.tsx +67 -66
  106. package/src/tui/components/Toast.tsx +9 -6
  107. package/src/tui/components/UsageGauge.tsx +10 -13
  108. package/src/tui/components/index.ts +5 -5
  109. package/src/tui/contexts/AgentSessionContext.tsx +164 -151
  110. package/src/tui/contexts/ConfigContext.tsx +15 -13
  111. package/src/tui/contexts/DashboardRuntimeContext.tsx +26 -26
  112. package/src/tui/contexts/DemoModeContext.tsx +22 -15
  113. package/src/tui/contexts/DrawerContext.tsx +11 -9
  114. package/src/tui/contexts/InputContext.tsx +2 -2
  115. package/src/tui/contexts/LogContext.tsx +62 -47
  116. package/src/tui/contexts/PluginContext.tsx +238 -183
  117. package/src/tui/contexts/RealTimeActivityContext.tsx +70 -36
  118. package/src/tui/contexts/StorageContext.tsx +95 -149
  119. package/src/tui/contexts/ThemeContext.tsx +21 -16
  120. package/src/tui/contexts/TimeWindowContext.tsx +72 -61
  121. package/src/tui/contexts/ToastContext.tsx +10 -7
  122. package/src/tui/contexts/index.ts +9 -5
  123. package/src/tui/createApp.tsx +8 -12
  124. package/src/tui/debug/captureFrame.ts +25 -28
  125. package/src/tui/debug/snapshot.tsx +179 -162
  126. package/src/tui/driver/assertions.ts +30 -22
  127. package/src/tui/driver/cli.ts +231 -221
  128. package/src/tui/driver/coverage.ts +27 -44
  129. package/src/tui/driver/demo-snapshot.ts +33 -33
  130. package/src/tui/driver/diff.ts +26 -32
  131. package/src/tui/driver/driver.ts +43 -48
  132. package/src/tui/driver/index.ts +36 -40
  133. package/src/tui/driver/recorder.ts +37 -48
  134. package/src/tui/driver/test-workflow.ts +51 -51
  135. package/src/tui/driver/test.ts +25 -25
  136. package/src/tui/hooks/index.ts +24 -20
  137. package/src/tui/hooks/useAnimatedValue.ts +13 -18
  138. package/src/tui/hooks/useDashboardKeyboard.ts +118 -98
  139. package/src/tui/hooks/useDashboardState.ts +26 -24
  140. package/src/tui/hooks/useEmaActivity.ts +82 -74
  141. package/src/tui/hooks/useEntranceAnimation.ts +11 -11
  142. package/src/tui/hooks/useExitAnimation.ts +7 -3
  143. package/src/tui/hooks/usePulse.ts +4 -4
  144. package/src/tui/hooks/useSafeRenderer.ts +5 -5
  145. package/src/tui/hooks/useSessionEnrichedProviders.ts +150 -0
  146. package/src/tui/hooks/useValueFlash.ts +10 -14
  147. package/src/tui/index.tsx +29 -6
  148. package/src/tui/shutdown.ts +19 -0
  149. package/src/tui/utils/providerColor.ts +1 -1
  150. package/src/tui/views/Dashboard.tsx +164 -102
  151. package/src/tui/views/HistoricalTrendsView.tsx +579 -250
  152. package/src/tui/views/ProjectsView.tsx +495 -243
  153. package/src/tui/views/RealTimeDashboard.tsx +176 -116
  154. package/src/tui/views/SettingsView.tsx +225 -189
  155. package/src/tui/views/index.ts +2 -2
  156. package/src/types/opentui-test-utils.d.ts +7 -7
  157. package/src/utils/clipboard.ts +29 -29
  158. package/src/utils/currency.test.ts +53 -53
  159. package/src/utils/currency.ts +35 -32
  160. package/src/version.ts +1 -1
@@ -1,4 +1,4 @@
1
- import type { ModelPricing, CostBreakdown } from '@/plugins/types/provider.ts';
1
+ import type { CostBreakdown, ModelPricing } from "@/plugins/types/provider.ts";
2
2
 
3
3
  export interface TokenUsage {
4
4
  input: number;
@@ -7,10 +7,7 @@ export interface TokenUsage {
7
7
  cacheWrite?: number;
8
8
  }
9
9
 
10
- export function estimateCost(
11
- usage: TokenUsage,
12
- pricing: ModelPricing
13
- ): CostBreakdown {
10
+ export function estimateCost(usage: TokenUsage, pricing: ModelPricing): CostBreakdown {
14
11
  const inputCost = (usage.input / 1_000_000) * pricing.input;
15
12
  const outputCost = (usage.output / 1_000_000) * pricing.output;
16
13
 
@@ -30,7 +27,7 @@ export function estimateCost(
30
27
  total: roundCost(total),
31
28
  input: roundCost(inputCost),
32
29
  output: roundCost(outputCost),
33
- currency: 'USD',
30
+ currency: "USD",
34
31
  };
35
32
 
36
33
  if (cacheReadCost > 0) breakdown.cacheRead = roundCost(cacheReadCost);
@@ -39,10 +36,7 @@ export function estimateCost(
39
36
  return breakdown;
40
37
  }
41
38
 
42
- export function estimateSessionCost(
43
- sessions: TokenUsage[],
44
- pricing: ModelPricing
45
- ): CostBreakdown {
39
+ export function estimateSessionCost(sessions: TokenUsage[], pricing: ModelPricing): CostBreakdown {
46
40
  const totals = sessions.reduce<TokenUsage>(
47
41
  (acc, session) => ({
48
42
  input: acc.input + session.input,
@@ -50,14 +44,14 @@ export function estimateSessionCost(
50
44
  cacheRead: (acc.cacheRead ?? 0) + (session.cacheRead ?? 0),
51
45
  cacheWrite: (acc.cacheWrite ?? 0) + (session.cacheWrite ?? 0),
52
46
  }),
53
- { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }
47
+ { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
54
48
  );
55
49
 
56
50
  return estimateCost(totals, pricing);
57
51
  }
58
52
 
59
- export function formatCost(cost: number, currency = 'USD'): string {
60
- if (currency === 'USD') {
53
+ export function formatCost(cost: number, currency = "USD"): string {
54
+ if (currency === "USD") {
61
55
  if (cost < 0.01) {
62
56
  return `$${cost.toFixed(4)}`;
63
57
  }
@@ -1,71 +1,67 @@
1
- import { describe, expect, test } from 'bun:test';
2
- import {
3
- FALLBACK_PRICING,
4
- getFallbackPricing,
5
- getFallbackProviderPricing,
6
- } from './fallback.ts';
1
+ import { describe, expect, test } from "bun:test";
2
+ import { FALLBACK_PRICING, getFallbackPricing, getFallbackProviderPricing } from "./fallback.ts";
7
3
 
8
- describe('getFallbackPricing()', () => {
9
- test('returns pricing for an exact model match', () => {
10
- expect(getFallbackPricing('openai', 'gpt-4.1')).toEqual({
4
+ describe("getFallbackPricing()", () => {
5
+ test("returns pricing for an exact model match", () => {
6
+ expect(getFallbackPricing("openai", "gpt-4.1")).toEqual({
11
7
  input: 2,
12
8
  output: 8,
13
9
  cacheRead: 0.5,
14
- source: 'fallback',
10
+ source: "fallback",
15
11
  });
16
12
  });
17
13
 
18
- test('returns pricing when model id includes a known key', () => {
19
- expect(getFallbackPricing('anthropic', 'claude-3-7-sonnet-20250219-thinking')).toEqual({
14
+ test("returns pricing when model id includes a known key", () => {
15
+ expect(getFallbackPricing("anthropic", "claude-3-7-sonnet-20250219-thinking")).toEqual({
20
16
  input: 3,
21
17
  output: 15,
22
18
  cacheRead: 0.3,
23
19
  cacheWrite: 3.75,
24
- source: 'fallback',
20
+ source: "fallback",
25
21
  });
26
22
  });
27
23
 
28
- test('returns pricing when a known key includes the model id', () => {
29
- expect(getFallbackPricing('google', 'gemini-2.5')).toEqual({
24
+ test("returns pricing when a known key includes the model id", () => {
25
+ expect(getFallbackPricing("google", "gemini-2.5")).toEqual({
30
26
  input: 1.25,
31
27
  output: 10,
32
- source: 'fallback',
28
+ source: "fallback",
33
29
  });
34
30
  });
35
31
 
36
- test('returns null for unknown provider', () => {
37
- expect(getFallbackPricing('unknown-provider', 'gpt-4.1')).toBeNull();
32
+ test("returns null for unknown provider", () => {
33
+ expect(getFallbackPricing("unknown-provider", "gpt-4.1")).toBeNull();
38
34
  });
39
35
 
40
- test('returns null for unknown model in a known provider', () => {
41
- expect(getFallbackPricing('openai', 'totally-unknown-model')).toBeNull();
36
+ test("returns null for unknown model in a known provider", () => {
37
+ expect(getFallbackPricing("openai", "totally-unknown-model")).toBeNull();
42
38
  });
43
39
  });
44
40
 
45
- describe('getFallbackProviderPricing()', () => {
46
- test('returns the full provider pricing map', () => {
47
- const anthropicPricing = FALLBACK_PRICING['anthropic'];
41
+ describe("getFallbackProviderPricing()", () => {
42
+ test("returns the full provider pricing map", () => {
43
+ const anthropicPricing = FALLBACK_PRICING["anthropic"];
48
44
  if (!anthropicPricing) {
49
- throw new Error('Expected anthropic fallback pricing to exist');
45
+ throw new Error("Expected anthropic fallback pricing to exist");
50
46
  }
51
- expect(getFallbackProviderPricing('anthropic')).toEqual(anthropicPricing);
47
+ expect(getFallbackProviderPricing("anthropic")).toEqual(anthropicPricing);
52
48
  });
53
49
 
54
- test('returns null for unknown provider', () => {
55
- expect(getFallbackProviderPricing('does-not-exist')).toBeNull();
50
+ test("returns null for unknown provider", () => {
51
+ expect(getFallbackProviderPricing("does-not-exist")).toBeNull();
56
52
  });
57
53
  });
58
54
 
59
- describe('FALLBACK_PRICING', () => {
60
- test('includes anthropic provider pricing data', () => {
61
- expect(FALLBACK_PRICING['anthropic']).toBeDefined();
55
+ describe("FALLBACK_PRICING", () => {
56
+ test("includes anthropic provider pricing data", () => {
57
+ expect(FALLBACK_PRICING["anthropic"]).toBeDefined();
62
58
  });
63
59
 
64
- test('includes openai provider pricing data', () => {
65
- expect(FALLBACK_PRICING['openai']).toBeDefined();
60
+ test("includes openai provider pricing data", () => {
61
+ expect(FALLBACK_PRICING["openai"]).toBeDefined();
66
62
  });
67
63
 
68
- test('includes google provider pricing data', () => {
69
- expect(FALLBACK_PRICING['google']).toBeDefined();
64
+ test("includes google provider pricing data", () => {
65
+ expect(FALLBACK_PRICING["google"]).toBeDefined();
70
66
  });
71
67
  });
@@ -1,45 +1,72 @@
1
- import type { ModelPricing } from '@/plugins/types/provider.ts';
1
+ import type { ModelPricing } from "@/plugins/types/provider.ts";
2
2
 
3
3
  //TODO: get more up to date fallback pricing
4
4
  export const FALLBACK_PRICING: Record<string, Record<string, ModelPricing>> = {
5
5
  anthropic: {
6
- 'claude-sonnet-4-20250514': { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75, source: 'fallback' },
7
- 'claude-3-7-sonnet-20250219': { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75, source: 'fallback' },
8
- 'claude-3-5-sonnet-20241022': { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75, source: 'fallback' },
9
- 'claude-3-5-haiku-20241022': { input: 0.8, output: 4, cacheRead: 0.08, cacheWrite: 1, source: 'fallback' },
10
- 'claude-3-opus-20240229': { input: 15, output: 75, cacheRead: 1.5, cacheWrite: 18.75, source: 'fallback' },
6
+ "claude-sonnet-4-20250514": {
7
+ input: 3,
8
+ output: 15,
9
+ cacheRead: 0.3,
10
+ cacheWrite: 3.75,
11
+ source: "fallback",
12
+ },
13
+ "claude-3-7-sonnet-20250219": {
14
+ input: 3,
15
+ output: 15,
16
+ cacheRead: 0.3,
17
+ cacheWrite: 3.75,
18
+ source: "fallback",
19
+ },
20
+ "claude-3-5-sonnet-20241022": {
21
+ input: 3,
22
+ output: 15,
23
+ cacheRead: 0.3,
24
+ cacheWrite: 3.75,
25
+ source: "fallback",
26
+ },
27
+ "claude-3-5-haiku-20241022": {
28
+ input: 0.8,
29
+ output: 4,
30
+ cacheRead: 0.08,
31
+ cacheWrite: 1,
32
+ source: "fallback",
33
+ },
34
+ "claude-3-opus-20240229": {
35
+ input: 15,
36
+ output: 75,
37
+ cacheRead: 1.5,
38
+ cacheWrite: 18.75,
39
+ source: "fallback",
40
+ },
11
41
  },
12
42
 
13
43
  openai: {
14
- 'gpt-4.1': { input: 2, output: 8, cacheRead: 0.5, source: 'fallback' },
15
- 'gpt-4.1-mini': { input: 0.4, output: 1.6, cacheRead: 0.1, source: 'fallback' },
16
- 'gpt-4o': { input: 2.5, output: 10, cacheRead: 1.25, source: 'fallback' },
17
- 'gpt-4o-mini': { input: 0.15, output: 0.6, cacheRead: 0.075, source: 'fallback' },
18
- 'o1': { input: 15, output: 60, source: 'fallback' },
19
- 'o1-mini': { input: 1.1, output: 4.4, source: 'fallback' },
20
- 'o3': { input: 10, output: 40, source: 'fallback' },
21
- 'o3-mini': { input: 1.1, output: 4.4, source: 'fallback' },
22
- 'o4-mini': { input: 1.1, output: 4.4, source: 'fallback' },
44
+ "gpt-4.1": { input: 2, output: 8, cacheRead: 0.5, source: "fallback" },
45
+ "gpt-4.1-mini": { input: 0.4, output: 1.6, cacheRead: 0.1, source: "fallback" },
46
+ "gpt-4o": { input: 2.5, output: 10, cacheRead: 1.25, source: "fallback" },
47
+ "gpt-4o-mini": { input: 0.15, output: 0.6, cacheRead: 0.075, source: "fallback" },
48
+ o1: { input: 15, output: 60, source: "fallback" },
49
+ "o1-mini": { input: 1.1, output: 4.4, source: "fallback" },
50
+ o3: { input: 10, output: 40, source: "fallback" },
51
+ "o3-mini": { input: 1.1, output: 4.4, source: "fallback" },
52
+ "o4-mini": { input: 1.1, output: 4.4, source: "fallback" },
23
53
  },
24
54
 
25
55
  google: {
26
- 'gemini-2.0-flash': { input: 0.1, output: 0.4, source: 'fallback' },
27
- 'gemini-2.0-flash-lite': { input: 0.075, output: 0.3, source: 'fallback' },
28
- 'gemini-2.5-pro': { input: 1.25, output: 10, source: 'fallback' },
29
- 'gemini-2.5-flash': { input: 0.15, output: 0.6, source: 'fallback' },
56
+ "gemini-2.0-flash": { input: 0.1, output: 0.4, source: "fallback" },
57
+ "gemini-2.0-flash-lite": { input: 0.075, output: 0.3, source: "fallback" },
58
+ "gemini-2.5-pro": { input: 1.25, output: 10, source: "fallback" },
59
+ "gemini-2.5-flash": { input: 0.15, output: 0.6, source: "fallback" },
30
60
  },
31
61
 
32
62
  openrouter: {
33
- 'anthropic/claude-sonnet-4': { input: 3, output: 15, source: 'fallback' },
34
- 'openai/gpt-4.1': { input: 2, output: 8, source: 'fallback' },
35
- 'google/gemini-2.5-pro': { input: 1.25, output: 10, source: 'fallback' },
63
+ "anthropic/claude-sonnet-4": { input: 3, output: 15, source: "fallback" },
64
+ "openai/gpt-4.1": { input: 2, output: 8, source: "fallback" },
65
+ "google/gemini-2.5-pro": { input: 1.25, output: 10, source: "fallback" },
36
66
  },
37
67
  };
38
68
 
39
- export function getFallbackPricing(
40
- providerId: string,
41
- modelId: string
42
- ): ModelPricing | null {
69
+ export function getFallbackPricing(providerId: string, modelId: string): ModelPricing | null {
43
70
  const providerPricing = FALLBACK_PRICING[providerId];
44
71
  if (!providerPricing) return null;
45
72
 
@@ -56,7 +83,7 @@ export function getFallbackPricing(
56
83
  }
57
84
 
58
85
  export function getFallbackProviderPricing(
59
- providerId: string
86
+ providerId: string,
60
87
  ): Record<string, ModelPricing> | null {
61
88
  return FALLBACK_PRICING[providerId] ?? null;
62
89
  }
@@ -1,6 +1,12 @@
1
- import type { ModelPricing, ProviderPlugin, ProviderPricing } from '@/plugins/types/provider.ts';
2
- import { getModelPricing, getProviderModels, normalizeProviderName, setProviderAliases, clearCache as clearModelsDevCache } from './models-dev.ts';
3
- import { getFallbackPricing, getFallbackProviderPricing } from './fallback.ts';
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
- 'modelsDevProviderId' in pricing &&
16
- typeof (pricing as ProviderPricing).modelsDevProviderId === 'string'
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 { estimateCost, estimateSessionCost, formatCost, formatTokenCount } from './estimator.ts';
65
- export type { TokenUsage } from './estimator.ts';
66
- export { FALLBACK_PRICING } from './fallback.ts';
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 '@/plugins/types/provider.ts';
1
+ import type { ModelPricing } from "@/plugins/types/provider.ts";
2
2
 
3
- const MODELS_DEV_API = 'https://models.dev/api.json';
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: 'models.dev',
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: 'models.dev',
112
+ source: "models.dev",
113
113
  };
114
114
 
115
115
  if (model.cost.cache_read !== undefined) {
@@ -1,6 +1,6 @@
1
- import { Database } from 'bun:sqlite';
2
- import * as fs from 'fs/promises';
3
- import { PATHS } from './paths.ts';
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('PRAGMA journal_mode = WAL');
80
- db.exec('PRAGMA synchronous = NORMAL');
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('Database not initialized. Call initDatabase() first.');
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('timestamp >= ?');
231
+ conditions.push("timestamp >= ?");
232
232
  params.push(query.startTime);
233
233
  }
234
234
  if (query.endTime !== undefined) {
235
- conditions.push('timestamp <= ?');
235
+ conditions.push("timestamp <= ?");
236
236
  params.push(query.endTime);
237
237
  }
238
238
  if (query.provider) {
239
- conditions.push('provider = ?');
239
+ conditions.push("provider = ?");
240
240
  params.push(query.provider);
241
241
  }
242
242
  if (query.model) {
243
- conditions.push('model = ?');
243
+ conditions.push("model = ?");
244
244
  params.push(query.model);
245
245
  }
246
246
  if (query.agent) {
247
- conditions.push('agent = ?');
247
+ conditions.push("agent = ?");
248
248
  params.push(query.agent);
249
249
  }
250
250
  if (query.sessionId) {
251
- conditions.push('session_id = ?');
251
+ conditions.push("session_id = ?");
252
252
  params.push(query.sessionId);
253
253
  }
254
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}` : '';
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.prepare(`
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
- `).get(startTime, endTime) as {
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.prepare(`
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
- `).all(startTime, endTime) as Array<{
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.prepare(`
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
- `).all(startTime, endTime) as Array<{
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.prepare(`
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
- `).all(bucketSeconds, bucketSeconds, startTime, endTime) as Array<{
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.prepare(`
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
- `).all(provider, startTime, endTime, limit) as Array<{
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 = ['date >= ?', 'date <= ?'];
537
+ const conditions = ["date >= ?", "date <= ?"];
528
538
  const params: string[] = [startDate, endDate];
529
539
 
530
540
  if (provider) {
531
- conditions.push('provider = ?');
541
+ conditions.push("provider = ?");
532
542
  params.push(provider);
533
543
  }
534
544
 
535
- const rows = db.prepare(`
545
+ const rows = db
546
+ .prepare(`
536
547
  SELECT * FROM daily_aggregates
537
- WHERE ${conditions.join(' AND ')}
548
+ WHERE ${conditions.join(" AND ")}
538
549
  ORDER BY date DESC, total_cost_usd DESC
539
- `).all(...params) as Array<{
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.prepare(
586
- 'SELECT value FROM plugin_storage WHERE plugin_id = ? AND key = ?',
587
- ).get(pluginId, key) as { value: string } | null;
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.prepare(
610
- 'SELECT 1 FROM plugin_storage WHERE plugin_id = ? AND key = ?',
611
- ).get(pluginId, key);
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
  }