@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.
Files changed (158) 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 +32 -32
  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 +29 -23
  19. package/src/plugins/notifications/visual-flash.ts +21 -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 +66 -57
  34. package/src/plugins/registry.ts +47 -43
  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/repos/agentSessions.ts +33 -23
  73. package/src/storage/repos/providerSnapshots.ts +5 -5
  74. package/src/storage/repos/usageEvents.ts +40 -28
  75. package/src/storage/types.ts +5 -5
  76. package/src/tui/App.tsx +180 -137
  77. package/src/tui/components/CommandPalette.tsx +46 -34
  78. package/src/tui/components/DebugConsole.tsx +39 -33
  79. package/src/tui/components/DebugPanel.tsx +295 -177
  80. package/src/tui/components/FooterHints.tsx +2 -2
  81. package/src/tui/components/GhostProviderCard.tsx +37 -35
  82. package/src/tui/components/Header.tsx +68 -35
  83. package/src/tui/components/HelpOverlay.tsx +99 -29
  84. package/src/tui/components/InlineGauge.tsx +10 -7
  85. package/src/tui/components/InlineSparkline.tsx +17 -11
  86. package/src/tui/components/KpiStrip.tsx +128 -98
  87. package/src/tui/components/LimitGauge.tsx +96 -59
  88. package/src/tui/components/ModalBackdrop.tsx +4 -4
  89. package/src/tui/components/ProviderAggregateStrip.tsx +69 -38
  90. package/src/tui/components/ProviderCard.tsx +232 -226
  91. package/src/tui/components/ProviderDetailPanel.tsx +133 -58
  92. package/src/tui/components/ProviderLimitsPanel.tsx +126 -89
  93. package/src/tui/components/ProvidersList.tsx +215 -126
  94. package/src/tui/components/SessionDetailsDrawer.tsx +191 -94
  95. package/src/tui/components/SessionsTable.tsx +313 -166
  96. package/src/tui/components/SettingsModal.tsx +231 -214
  97. package/src/tui/components/SidebarBreakdown.tsx +30 -16
  98. package/src/tui/components/Skeleton.tsx +24 -17
  99. package/src/tui/components/SmartSidebar.tsx +360 -258
  100. package/src/tui/components/Sparkline.tsx +64 -52
  101. package/src/tui/components/Spinner.tsx +3 -3
  102. package/src/tui/components/StatusBar.tsx +27 -27
  103. package/src/tui/components/ThemePicker.tsx +67 -66
  104. package/src/tui/components/Toast.tsx +9 -6
  105. package/src/tui/components/UsageGauge.tsx +10 -13
  106. package/src/tui/components/index.ts +5 -5
  107. package/src/tui/contexts/AgentSessionContext.tsx +193 -144
  108. package/src/tui/contexts/ConfigContext.tsx +15 -13
  109. package/src/tui/contexts/DashboardRuntimeContext.tsx +26 -26
  110. package/src/tui/contexts/DemoModeContext.tsx +22 -15
  111. package/src/tui/contexts/DrawerContext.tsx +11 -9
  112. package/src/tui/contexts/InputContext.tsx +2 -2
  113. package/src/tui/contexts/LogContext.tsx +62 -47
  114. package/src/tui/contexts/PluginContext.tsx +232 -183
  115. package/src/tui/contexts/RealTimeActivityContext.tsx +70 -36
  116. package/src/tui/contexts/StorageContext.tsx +151 -117
  117. package/src/tui/contexts/ThemeContext.tsx +21 -16
  118. package/src/tui/contexts/TimeWindowContext.tsx +72 -61
  119. package/src/tui/contexts/ToastContext.tsx +10 -7
  120. package/src/tui/contexts/index.ts +9 -5
  121. package/src/tui/createApp.tsx +8 -12
  122. package/src/tui/debug/captureFrame.ts +25 -28
  123. package/src/tui/debug/snapshot.tsx +179 -162
  124. package/src/tui/driver/assertions.ts +30 -22
  125. package/src/tui/driver/cli.ts +231 -221
  126. package/src/tui/driver/coverage.ts +27 -44
  127. package/src/tui/driver/demo-snapshot.ts +33 -33
  128. package/src/tui/driver/diff.ts +26 -32
  129. package/src/tui/driver/driver.ts +43 -48
  130. package/src/tui/driver/index.ts +36 -40
  131. package/src/tui/driver/recorder.ts +37 -48
  132. package/src/tui/driver/test-workflow.ts +51 -51
  133. package/src/tui/driver/test.ts +25 -25
  134. package/src/tui/hooks/index.ts +24 -20
  135. package/src/tui/hooks/useAnimatedValue.ts +13 -18
  136. package/src/tui/hooks/useDashboardKeyboard.ts +118 -98
  137. package/src/tui/hooks/useDashboardState.ts +26 -24
  138. package/src/tui/hooks/useEmaActivity.ts +82 -74
  139. package/src/tui/hooks/useEntranceAnimation.ts +11 -11
  140. package/src/tui/hooks/useExitAnimation.ts +3 -3
  141. package/src/tui/hooks/usePulse.ts +4 -4
  142. package/src/tui/hooks/useSafeRenderer.ts +5 -5
  143. package/src/tui/hooks/useSessionEnrichedProviders.ts +150 -0
  144. package/src/tui/hooks/useValueFlash.ts +10 -14
  145. package/src/tui/index.tsx +29 -6
  146. package/src/tui/shutdown.ts +19 -0
  147. package/src/tui/utils/providerColor.ts +1 -1
  148. package/src/tui/views/Dashboard.tsx +127 -102
  149. package/src/tui/views/HistoricalTrendsView.tsx +579 -250
  150. package/src/tui/views/ProjectsView.tsx +466 -237
  151. package/src/tui/views/RealTimeDashboard.tsx +150 -111
  152. package/src/tui/views/SettingsView.tsx +222 -191
  153. package/src/tui/views/index.ts +2 -2
  154. package/src/types/opentui-test-utils.d.ts +7 -7
  155. package/src/utils/clipboard.ts +29 -29
  156. package/src/utils/currency.test.ts +53 -53
  157. package/src/utils/currency.ts +35 -32
  158. package/src/version.ts +1 -1
@@ -1,15 +1,15 @@
1
- import type { PluginPermissions } from './types/base.ts';
2
- import type { PluginContext, PluginStorage } from './types/provider.ts';
3
- import { createAuthSources } from './auth-sources.ts';
4
- import { createSandboxedHttpClient, createPluginLogger } from './sandbox.ts';
5
- import { deepFreeze } from './sandbox-guard.ts';
6
1
  import {
7
- pluginStorageGet,
8
- pluginStorageSet,
2
+ getDatabase,
9
3
  pluginStorageDelete,
4
+ pluginStorageGet,
10
5
  pluginStorageHas,
11
- getDatabase,
12
- } from '@/storage/database.ts';
6
+ pluginStorageSet,
7
+ } from "@/storage/database.ts";
8
+ import { createAuthSources } from "./auth-sources.ts";
9
+ import { createPluginLogger, createSandboxedHttpClient } from "./sandbox.ts";
10
+ import { deepFreeze } from "./sandbox-guard.ts";
11
+ import type { PluginPermissions } from "./types/base.ts";
12
+ import type { PluginContext, PluginStorage } from "./types/provider.ts";
13
13
 
14
14
  let dbAvailable: boolean | null = null;
15
15
 
@@ -52,7 +52,7 @@ export function resetDbAvailableCache(): void {
52
52
  export function createPluginContext(
53
53
  pluginId: string,
54
54
  permissions: PluginPermissions,
55
- signal?: AbortSignal
55
+ signal?: AbortSignal,
56
56
  ): PluginContext {
57
57
  const ctx: PluginContext = {
58
58
  config: {},
@@ -7,7 +7,7 @@
7
7
  * 3. All errors are logged through the scoped plugin logger
8
8
  */
9
9
 
10
- import { createPluginLogger } from './sandbox.ts';
10
+ import { createPluginLogger } from "./sandbox.ts";
11
11
 
12
12
  // ---------------------------------------------------------------------------
13
13
  // Circuit breaker state
@@ -71,10 +71,14 @@ export async function safeInvoke<T>(
71
71
 
72
72
  // Check if circuit is open (plugin disabled)
73
73
  if (circuit.disabledUntil > Date.now()) {
74
- log.warn(`Circuit open — skipping ${method} (disabled until ${new Date(circuit.disabledUntil).toISOString()})`);
74
+ log.warn(
75
+ `Circuit open — skipping ${method} (disabled until ${new Date(circuit.disabledUntil).toISOString()})`,
76
+ );
75
77
  return {
76
78
  ok: false,
77
- error: new Error(`Plugin "${pluginId}" is temporarily disabled after ${circuit.consecutiveFailures} consecutive failures`),
79
+ error: new Error(
80
+ `Plugin "${pluginId}" is temporarily disabled after ${circuit.consecutiveFailures} consecutive failures`,
81
+ ),
78
82
  circuitOpen: true,
79
83
  };
80
84
  }
@@ -89,7 +93,9 @@ export async function safeInvoke<T>(
89
93
 
90
94
  // Success — reset consecutive failures
91
95
  if (circuit.consecutiveFailures > 0) {
92
- log.info(`${method} succeeded — resetting circuit breaker (was at ${circuit.consecutiveFailures} failures)`);
96
+ log.info(
97
+ `${method} succeeded — resetting circuit breaker (was at ${circuit.consecutiveFailures} failures)`,
98
+ );
93
99
  }
94
100
  circuit.consecutiveFailures = 0;
95
101
  circuit.disabledUntil = 0;
@@ -181,7 +187,8 @@ export function getPluginHealth(pluginId: string): PluginHealth {
181
187
  const circuit = getCircuit(pluginId);
182
188
  return {
183
189
  pluginId,
184
- healthy: circuit.disabledUntil < Date.now() && circuit.consecutiveFailures < MAX_CONSECUTIVE_FAILURES,
190
+ healthy:
191
+ circuit.disabledUntil < Date.now() && circuit.consecutiveFailures < MAX_CONSECUTIVE_FAILURES,
185
192
  consecutiveFailures: circuit.consecutiveFailures,
186
193
  totalFailures: circuit.totalFailures,
187
194
  totalCalls: circuit.totalCalls,
@@ -1,14 +1,14 @@
1
- import { execFileSync } from 'child_process';
1
+ import { execFileSync } from "child_process";
2
2
  import type {
3
- ProviderPlugin,
4
- ProviderFetchContext,
5
- ProviderUsageData,
6
- Credentials,
7
3
  CredentialResult,
4
+ Credentials,
8
5
  OAuthCredentials,
9
6
  PluginContext,
10
7
  ProviderAuth,
11
- } from '../types/provider.ts';
8
+ ProviderFetchContext,
9
+ ProviderPlugin,
10
+ ProviderUsageData,
11
+ } from "../types/provider.ts";
12
12
 
13
13
  const TOKEN_EXPIRY_BUFFER_MS = 5 * 60 * 1000;
14
14
 
@@ -28,7 +28,7 @@ function buildOAuthCredentials(
28
28
  refreshToken?: string,
29
29
  expiresAt?: number,
30
30
  accountId?: string,
31
- subscriptionType?: string
31
+ subscriptionType?: string,
32
32
  ): OAuthCredentials {
33
33
  const oauth: OAuthCredentials = { accessToken };
34
34
  if (refreshToken !== undefined) oauth.refreshToken = refreshToken;
@@ -44,12 +44,12 @@ async function discoverClaudeCode(ctx: PluginContext): Promise<CredentialResult>
44
44
  const now = Date.now();
45
45
 
46
46
  // Try macOS Keychain first (only on darwin)
47
- if (ctx.authSources.platform.os === 'darwin') {
47
+ if (ctx.authSources.platform.os === "darwin") {
48
48
  try {
49
49
  const keychainData = execFileSync(
50
- '/usr/bin/security',
51
- ['find-generic-password', '-s', 'Claude Code-credentials', '-w'],
52
- { encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'], timeout: 5000 }
50
+ "/usr/bin/security",
51
+ ["find-generic-password", "-s", "Claude Code-credentials", "-w"],
52
+ { encoding: "utf8", stdio: ["pipe", "pipe", "pipe"], timeout: 5000 },
53
53
  ).trim();
54
54
 
55
55
  if (keychainData) {
@@ -66,9 +66,9 @@ async function discoverClaudeCode(ctx: PluginContext): Promise<CredentialResult>
66
66
  oauth.refreshToken,
67
67
  oauth.expiresAt,
68
68
  undefined,
69
- oauth.subscriptionType
69
+ oauth.subscriptionType,
70
70
  ),
71
- source: 'external',
71
+ source: "external",
72
72
  },
73
73
  };
74
74
  }
@@ -83,10 +83,11 @@ async function discoverClaudeCode(ctx: PluginContext): Promise<CredentialResult>
83
83
  const credPath = `${ctx.authSources.platform.homedir}/.claude/.credentials.json`;
84
84
  const fileData = await ctx.authSources.files.readJson<ClaudeCodeCredentialsFile>(credPath);
85
85
  const fileOauth = fileData?.claudeAiOauth;
86
- if (!fileOauth?.accessToken) return { ok: false, reason: 'missing', message: 'No Claude Code credentials found' };
86
+ if (!fileOauth?.accessToken)
87
+ return { ok: false, reason: "missing", message: "No Claude Code credentials found" };
87
88
 
88
89
  if (fileOauth.expiresAt && fileOauth.expiresAt <= now) {
89
- return { ok: false, reason: 'expired', message: 'Claude Code token expired' };
90
+ return { ok: false, reason: "expired", message: "Claude Code token expired" };
90
91
  }
91
92
 
92
93
  return {
@@ -97,9 +98,9 @@ async function discoverClaudeCode(ctx: PluginContext): Promise<CredentialResult>
97
98
  fileOauth.refreshToken,
98
99
  fileOauth.expiresAt,
99
100
  undefined,
100
- fileOauth.subscriptionType
101
+ fileOauth.subscriptionType,
101
102
  ),
102
- source: 'external',
103
+ source: "external",
103
104
  },
104
105
  };
105
106
  }
@@ -117,29 +118,29 @@ interface AnthropicUsageResponse {
117
118
 
118
119
  export const anthropicPlugin: ProviderPlugin = {
119
120
  apiVersion: 2,
120
- id: 'anthropic',
121
- type: 'provider',
122
- name: 'Anthropic',
123
- version: '1.0.0',
121
+ id: "anthropic",
122
+ type: "provider",
123
+ name: "Anthropic",
124
+ version: "1.0.0",
124
125
 
125
126
  meta: {
126
- description: 'Anthropic Claude subscription usage tracking',
127
- homepage: 'https://anthropic.com',
128
- brandColor: '#d4a27f',
127
+ description: "Anthropic Claude subscription usage tracking",
128
+ homepage: "https://anthropic.com",
129
+ brandColor: "#d4a27f",
129
130
  },
130
131
 
131
132
  permissions: {
132
133
  network: {
133
134
  enabled: true,
134
- allowedDomains: ['api.anthropic.com'],
135
+ allowedDomains: ["api.anthropic.com"],
135
136
  },
136
137
  env: {
137
138
  read: true,
138
- vars: ['ANTHROPIC_API_KEY'],
139
+ vars: ["ANTHROPIC_API_KEY"],
139
140
  },
140
141
  filesystem: {
141
142
  read: true,
142
- paths: ['~/.claude', '~/.local/share/opencode'],
143
+ paths: ["~/.claude", "~/.local/share/opencode"],
143
144
  },
144
145
  },
145
146
 
@@ -152,13 +153,18 @@ export const anthropicPlugin: ProviderPlugin = {
152
153
 
153
154
  auth: {
154
155
  async discover(ctx: PluginContext): Promise<CredentialResult> {
155
- const entry = await ctx.authSources.opencode.getProviderEntry('anthropic');
156
- if (entry?.type === 'oauth' && entry.access) {
156
+ const entry = await ctx.authSources.opencode.getProviderEntry("anthropic");
157
+ if (entry?.type === "oauth" && entry.access) {
157
158
  return {
158
159
  ok: true,
159
160
  credentials: {
160
- oauth: buildOAuthCredentials(entry.access, entry.refresh, entry.expires, entry.accountId),
161
- source: 'opencode',
161
+ oauth: buildOAuthCredentials(
162
+ entry.access,
163
+ entry.refresh,
164
+ entry.expires,
165
+ entry.accountId,
166
+ ),
167
+ source: "opencode",
162
168
  },
163
169
  };
164
170
  }
@@ -166,12 +172,12 @@ export const anthropicPlugin: ProviderPlugin = {
166
172
  const claudeCodeResult = await discoverClaudeCode(ctx);
167
173
  if (claudeCodeResult.ok) return claudeCodeResult;
168
174
 
169
- const apiKey = ctx.authSources.env.get('ANTHROPIC_API_KEY');
175
+ const apiKey = ctx.authSources.env.get("ANTHROPIC_API_KEY");
170
176
  if (apiKey) {
171
- return { ok: true, credentials: { apiKey, source: 'env' } };
177
+ return { ok: true, credentials: { apiKey, source: "env" } };
172
178
  }
173
179
 
174
- return { ok: false, reason: 'missing', message: 'No Anthropic credentials found' };
180
+ return { ok: false, reason: "missing", message: "No Anthropic credentials found" };
175
181
  },
176
182
 
177
183
  isConfigured(credentials: Credentials): boolean {
@@ -188,13 +194,13 @@ export const anthropicPlugin: ProviderPlugin = {
188
194
  if (!hasOAuth && !hasApiKey) {
189
195
  return {
190
196
  fetchedAt: Date.now(),
191
- error: 'OAuth token or API key required. Authenticate via OpenCode or Claude Code.',
197
+ error: "OAuth token or API key required. Authenticate via OpenCode or Claude Code.",
192
198
  };
193
199
  }
194
200
 
195
201
  if (hasApiKey && !hasOAuth) {
196
202
  return {
197
- planType: 'API',
203
+ planType: "API",
198
204
  allowed: true,
199
205
  fetchedAt: Date.now(),
200
206
  };
@@ -205,41 +211,42 @@ export const anthropicPlugin: ProviderPlugin = {
205
211
  if (isExpired) {
206
212
  return {
207
213
  fetchedAt: Date.now(),
208
- error: 'Token expired. Run any command in OpenCode to refresh.',
214
+ error: "Token expired. Run any command in OpenCode to refresh.",
209
215
  };
210
216
  }
211
217
  }
212
218
 
213
219
  const accessToken = credentials.oauth!.accessToken;
214
- const subscriptionType = (credentials.oauth as { subscriptionType?: string } | undefined)?.subscriptionType;
220
+ const subscriptionType = (credentials.oauth as { subscriptionType?: string } | undefined)
221
+ ?.subscriptionType;
215
222
  const planType = getPlanName(subscriptionType);
216
223
 
217
224
  try {
218
- const response = await http.fetch('https://api.anthropic.com/api/oauth/usage', {
219
- method: 'GET',
225
+ const response = await http.fetch("https://api.anthropic.com/api/oauth/usage", {
226
+ method: "GET",
220
227
  headers: {
221
- 'Authorization': `Bearer ${accessToken}`,
222
- 'anthropic-beta': 'oauth-2025-04-20',
223
- 'User-Agent': 'tokentop/1.0',
228
+ Authorization: `Bearer ${accessToken}`,
229
+ "anthropic-beta": "oauth-2025-04-20",
230
+ "User-Agent": "tokentop/1.0",
224
231
  },
225
232
  });
226
233
 
227
234
  if (!response.ok) {
228
- log.warn('Failed to fetch Anthropic usage', { status: response.status });
229
-
235
+ log.warn("Failed to fetch Anthropic usage", { status: response.status });
236
+
230
237
  if (response.status === 401) {
231
238
  return {
232
239
  fetchedAt: Date.now(),
233
- error: 'Token expired or invalid. Re-authenticate in OpenCode or Claude Code.',
240
+ error: "Token expired or invalid. Re-authenticate in OpenCode or Claude Code.",
234
241
  };
235
242
  }
236
243
  if (response.status === 403) {
237
244
  return {
238
245
  fetchedAt: Date.now(),
239
- error: 'Token lacks required scope. Re-authenticate in OpenCode or Claude Code.',
246
+ error: "Token lacks required scope. Re-authenticate in OpenCode or Claude Code.",
240
247
  };
241
248
  }
242
-
249
+
243
250
  return {
244
251
  fetchedAt: Date.now(),
245
252
  error: `API error: ${response.status} ${response.statusText}`,
@@ -249,7 +256,7 @@ export const anthropicPlugin: ProviderPlugin = {
249
256
  const data = (await response.json()) as AnthropicUsageResponse;
250
257
 
251
258
  const result: ProviderUsageData = {
252
- planType: planType ?? 'Pro/Max',
259
+ planType: planType ?? "Pro/Max",
253
260
  allowed: true,
254
261
  limitReached: false,
255
262
  fetchedAt: Date.now(),
@@ -265,7 +272,7 @@ export const anthropicPlugin: ProviderPlugin = {
265
272
  result.limits.primary = {
266
273
  usedPercent: fiveHour,
267
274
  windowMinutes: 300,
268
- label: '5-hour window',
275
+ label: "5-hour window",
269
276
  };
270
277
  if (data.five_hour?.resets_at) {
271
278
  const resetTime = new Date(data.five_hour.resets_at).getTime();
@@ -279,7 +286,7 @@ export const anthropicPlugin: ProviderPlugin = {
279
286
  result.limits.secondary = {
280
287
  usedPercent: sevenDay,
281
288
  windowMinutes: 10080,
282
- label: '7-day window',
289
+ label: "7-day window",
283
290
  };
284
291
  if (data.seven_day?.resets_at) {
285
292
  const resetTime = new Date(data.seven_day.resets_at).getTime();
@@ -299,10 +306,10 @@ export const anthropicPlugin: ProviderPlugin = {
299
306
 
300
307
  return result;
301
308
  } catch (err) {
302
- log.error('Failed to fetch Anthropic usage', { error: err });
309
+ log.error("Failed to fetch Anthropic usage", { error: err });
303
310
  return {
304
311
  fetchedAt: Date.now(),
305
- error: err instanceof Error ? err.message : 'Unknown error',
312
+ error: err instanceof Error ? err.message : "Unknown error",
306
313
  };
307
314
  }
308
315
  },
@@ -311,10 +318,10 @@ export const anthropicPlugin: ProviderPlugin = {
311
318
  function getPlanName(subscriptionType?: string): string | null {
312
319
  if (!subscriptionType) return null;
313
320
  const lower = subscriptionType.toLowerCase();
314
- if (lower.includes('max')) return 'Max';
315
- if (lower.includes('pro')) return 'Pro';
316
- if (lower.includes('team')) return 'Team';
317
- if (lower.includes('api')) return null;
321
+ if (lower.includes("max")) return "Max";
322
+ if (lower.includes("pro")) return "Pro";
323
+ if (lower.includes("team")) return "Team";
324
+ if (lower.includes("api")) return null;
318
325
  return subscriptionType.charAt(0).toUpperCase() + subscriptionType.slice(1);
319
326
  }
320
327