@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.
- 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 +42 -38
- 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 +36 -23
- package/src/plugins/notifications/visual-flash.ts +31 -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 +68 -57
- package/src/plugins/registry.ts +80 -71
- 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/persistence-service.ts +206 -0
- 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/retention.ts +146 -0
- 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 +314 -183
- 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 +320 -169
- package/src/tui/components/SettingsModal.tsx +593 -269
- 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 +164 -151
- 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 +238 -183
- package/src/tui/contexts/RealTimeActivityContext.tsx +70 -36
- package/src/tui/contexts/StorageContext.tsx +95 -149
- 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 +7 -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 +164 -102
- package/src/tui/views/HistoricalTrendsView.tsx +579 -250
- package/src/tui/views/ProjectsView.tsx +495 -243
- package/src/tui/views/RealTimeDashboard.tsx +176 -116
- package/src/tui/views/SettingsView.tsx +225 -189
- 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
|
@@ -1,34 +1,40 @@
|
|
|
1
1
|
import type {
|
|
2
|
-
ProviderPlugin,
|
|
3
|
-
ProviderFetchContext,
|
|
4
|
-
ProviderUsageData,
|
|
5
|
-
UsageLimit,
|
|
6
|
-
Credentials,
|
|
7
2
|
CredentialResult,
|
|
3
|
+
Credentials,
|
|
8
4
|
OAuthCredentials,
|
|
9
|
-
RefreshedCredentials,
|
|
10
5
|
PluginContext,
|
|
11
6
|
ProviderAuth,
|
|
12
|
-
|
|
7
|
+
ProviderFetchContext,
|
|
8
|
+
ProviderPlugin,
|
|
9
|
+
ProviderUsageData,
|
|
10
|
+
RefreshedCredentials,
|
|
11
|
+
UsageLimit,
|
|
12
|
+
} from "../types/provider.ts";
|
|
13
13
|
|
|
14
14
|
const ANTIGRAVITY_ENDPOINTS = [
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
"https://daily-cloudcode-pa.sandbox.googleapis.com",
|
|
16
|
+
"https://autopush-cloudcode-pa.sandbox.googleapis.com",
|
|
17
|
+
"https://cloudcode-pa.googleapis.com",
|
|
18
18
|
];
|
|
19
19
|
|
|
20
20
|
const ANTIGRAVITY_HEADERS = {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
"User-Agent": "antigravity/1.11.5 windows/amd64",
|
|
22
|
+
"X-Goog-Api-Client": "google-cloud-sdk vscode_cloudshelleditor/0.1",
|
|
23
|
+
"Client-Metadata":
|
|
24
|
+
'{"ideType":"IDE_UNSPECIFIED","platform":"PLATFORM_UNSPECIFIED","pluginType":"GEMINI"}',
|
|
24
25
|
};
|
|
25
26
|
|
|
26
27
|
// Base64 encoded to avoid triggering GitHub secret scanning.
|
|
27
28
|
// These are publicly-known OAuth client IDs from the open-source Antigravity CLI.
|
|
28
|
-
const ANTIGRAVITY_CLIENT_ID =
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
const ANTIGRAVITY_CLIENT_ID =
|
|
30
|
+
process.env.ANTIGRAVITY_CLIENT_ID ??
|
|
31
|
+
Buffer.from(
|
|
32
|
+
"MTA3MTAwNjA2MDU5MS10bWhzc2luMmgyMWxjcmUyMzV2dG9sb2poNGc0MDNlcC5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbQ==",
|
|
33
|
+
"base64",
|
|
34
|
+
).toString();
|
|
35
|
+
const ANTIGRAVITY_CLIENT_SECRET =
|
|
36
|
+
process.env.ANTIGRAVITY_CLIENT_SECRET ??
|
|
37
|
+
Buffer.from("R09DU1BYLUs1OEZXUjQ4NkxkTEoxbUxCOHNYQzR6NnFEQWY=", "base64").toString();
|
|
32
38
|
const TOKEN_EXPIRY_BUFFER_MS = 5 * 60 * 1000;
|
|
33
39
|
|
|
34
40
|
interface AntigravityAccount {
|
|
@@ -57,32 +63,35 @@ interface TokenResponse {
|
|
|
57
63
|
}
|
|
58
64
|
|
|
59
65
|
interface AntigravityModelsResponse {
|
|
60
|
-
models?: Record<
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
66
|
+
models?: Record<
|
|
67
|
+
string,
|
|
68
|
+
{
|
|
69
|
+
displayName?: string;
|
|
70
|
+
quotaInfo?: {
|
|
71
|
+
remainingFraction?: number | null;
|
|
72
|
+
resetTime?: string | null;
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
>;
|
|
67
76
|
}
|
|
68
77
|
|
|
69
78
|
export const antigravityPlugin: ProviderPlugin = {
|
|
70
79
|
apiVersion: 2,
|
|
71
|
-
id:
|
|
72
|
-
type:
|
|
73
|
-
name:
|
|
74
|
-
version:
|
|
80
|
+
id: "antigravity",
|
|
81
|
+
type: "provider",
|
|
82
|
+
name: "Antigravity",
|
|
83
|
+
version: "1.0.0",
|
|
75
84
|
|
|
76
85
|
meta: {
|
|
77
|
-
description:
|
|
78
|
-
homepage:
|
|
79
|
-
brandColor:
|
|
86
|
+
description: "Antigravity (Google Gemini Advanced) subscription usage tracking",
|
|
87
|
+
homepage: "https://one.google.com/explore-plan/gemini-advanced",
|
|
88
|
+
brandColor: "#4285f4",
|
|
80
89
|
},
|
|
81
90
|
|
|
82
91
|
permissions: {
|
|
83
92
|
network: {
|
|
84
93
|
enabled: true,
|
|
85
|
-
allowedDomains: [
|
|
94
|
+
allowedDomains: ["googleapis.com", "sandbox.googleapis.com"],
|
|
86
95
|
},
|
|
87
96
|
env: {
|
|
88
97
|
read: false,
|
|
@@ -90,7 +99,7 @@ export const antigravityPlugin: ProviderPlugin = {
|
|
|
90
99
|
},
|
|
91
100
|
filesystem: {
|
|
92
101
|
read: true,
|
|
93
|
-
paths: [
|
|
102
|
+
paths: ["~/.config/opencode"],
|
|
94
103
|
},
|
|
95
104
|
},
|
|
96
105
|
|
|
@@ -102,7 +111,7 @@ export const antigravityPlugin: ProviderPlugin = {
|
|
|
102
111
|
},
|
|
103
112
|
|
|
104
113
|
pricing: {
|
|
105
|
-
modelsDevProviderId:
|
|
114
|
+
modelsDevProviderId: "google",
|
|
106
115
|
},
|
|
107
116
|
|
|
108
117
|
auth: {
|
|
@@ -115,27 +124,29 @@ export const antigravityPlugin: ProviderPlugin = {
|
|
|
115
124
|
const account = agData.accounts[activeIndex] ?? agData.accounts[0];
|
|
116
125
|
if (account && (account.refreshToken || account.accessToken)) {
|
|
117
126
|
const oauth: OAuthCredentials = {
|
|
118
|
-
accessToken: account.accessToken ??
|
|
127
|
+
accessToken: account.accessToken ?? "",
|
|
119
128
|
...(account.refreshToken !== undefined && { refreshToken: account.refreshToken }),
|
|
120
129
|
...(account.expiresAt !== undefined && { expiresAt: account.expiresAt }),
|
|
121
|
-
...(account.managedProjectId !== undefined && {
|
|
130
|
+
...(account.managedProjectId !== undefined && {
|
|
131
|
+
managedProjectId: account.managedProjectId,
|
|
132
|
+
}),
|
|
122
133
|
};
|
|
123
|
-
return { ok: true, credentials: { oauth, source:
|
|
134
|
+
return { ok: true, credentials: { oauth, source: "opencode" } };
|
|
124
135
|
}
|
|
125
136
|
}
|
|
126
137
|
|
|
127
138
|
// Source 2: OpenCode OAuth under 'google' key
|
|
128
|
-
const entry = await ctx.authSources.opencode.getProviderEntry(
|
|
129
|
-
if (entry?.type ===
|
|
139
|
+
const entry = await ctx.authSources.opencode.getProviderEntry("google");
|
|
140
|
+
if (entry?.type === "oauth" && entry.access) {
|
|
130
141
|
const oauth: OAuthCredentials = {
|
|
131
142
|
accessToken: entry.access,
|
|
132
143
|
...(entry.refresh !== undefined && { refreshToken: entry.refresh }),
|
|
133
144
|
...(entry.expires !== undefined && { expiresAt: entry.expires }),
|
|
134
145
|
};
|
|
135
|
-
return { ok: true, credentials: { oauth, source:
|
|
146
|
+
return { ok: true, credentials: { oauth, source: "opencode" } };
|
|
136
147
|
}
|
|
137
148
|
|
|
138
|
-
return { ok: false, reason:
|
|
149
|
+
return { ok: false, reason: "missing", message: "No Antigravity credentials found" };
|
|
139
150
|
},
|
|
140
151
|
|
|
141
152
|
isConfigured(credentials: Credentials): boolean {
|
|
@@ -145,14 +156,14 @@ export const antigravityPlugin: ProviderPlugin = {
|
|
|
145
156
|
|
|
146
157
|
async refreshToken(auth: OAuthCredentials): Promise<RefreshedCredentials> {
|
|
147
158
|
if (!auth.refreshToken) {
|
|
148
|
-
throw new Error(
|
|
159
|
+
throw new Error("No refresh token available");
|
|
149
160
|
}
|
|
150
161
|
|
|
151
|
-
const response = await fetch(
|
|
152
|
-
method:
|
|
153
|
-
headers: {
|
|
162
|
+
const response = await fetch("https://oauth2.googleapis.com/token", {
|
|
163
|
+
method: "POST",
|
|
164
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
154
165
|
body: new URLSearchParams({
|
|
155
|
-
grant_type:
|
|
166
|
+
grant_type: "refresh_token",
|
|
156
167
|
refresh_token: auth.refreshToken,
|
|
157
168
|
client_id: ANTIGRAVITY_CLIENT_ID,
|
|
158
169
|
client_secret: ANTIGRAVITY_CLIENT_SECRET,
|
|
@@ -160,7 +171,7 @@ export const antigravityPlugin: ProviderPlugin = {
|
|
|
160
171
|
});
|
|
161
172
|
|
|
162
173
|
if (!response.ok) {
|
|
163
|
-
const errorText = await response.text().catch(() =>
|
|
174
|
+
const errorText = await response.text().catch(() => "");
|
|
164
175
|
throw new Error(`Token refresh failed: ${response.status} ${errorText.slice(0, 100)}`);
|
|
165
176
|
}
|
|
166
177
|
|
|
@@ -179,23 +190,25 @@ export const antigravityPlugin: ProviderPlugin = {
|
|
|
179
190
|
if (!credentials.oauth?.accessToken && !credentials.oauth?.refreshToken) {
|
|
180
191
|
return {
|
|
181
192
|
fetchedAt: Date.now(),
|
|
182
|
-
error:
|
|
193
|
+
error: "OAuth token required. Sign in via OpenCode with Google account.",
|
|
183
194
|
};
|
|
184
195
|
}
|
|
185
196
|
|
|
186
197
|
let accessToken = credentials.oauth.accessToken;
|
|
187
|
-
const needsRefresh =
|
|
188
|
-
|
|
198
|
+
const needsRefresh =
|
|
199
|
+
!accessToken ||
|
|
200
|
+
(credentials.oauth.expiresAt &&
|
|
201
|
+
credentials.oauth.expiresAt <= Date.now() + TOKEN_EXPIRY_BUFFER_MS);
|
|
189
202
|
|
|
190
203
|
if (needsRefresh && credentials.oauth.refreshToken) {
|
|
191
204
|
try {
|
|
192
|
-
log.debug(
|
|
205
|
+
log.debug("Access token expired or missing, refreshing...");
|
|
193
206
|
const refreshed = await this.refreshToken!(credentials.oauth);
|
|
194
207
|
accessToken = refreshed.accessToken;
|
|
195
|
-
log.debug(
|
|
208
|
+
log.debug("Token refreshed successfully");
|
|
196
209
|
} catch (err) {
|
|
197
210
|
const msg = err instanceof Error ? err.message : String(err);
|
|
198
|
-
log.error(
|
|
211
|
+
log.error("Token refresh failed", { error: msg });
|
|
199
212
|
return {
|
|
200
213
|
fetchedAt: Date.now(),
|
|
201
214
|
error: `Token refresh failed: ${msg}`,
|
|
@@ -206,7 +219,7 @@ export const antigravityPlugin: ProviderPlugin = {
|
|
|
206
219
|
if (!accessToken) {
|
|
207
220
|
return {
|
|
208
221
|
fetchedAt: Date.now(),
|
|
209
|
-
error:
|
|
222
|
+
error: "No valid access token and refresh failed.",
|
|
210
223
|
};
|
|
211
224
|
}
|
|
212
225
|
|
|
@@ -215,38 +228,38 @@ export const antigravityPlugin: ProviderPlugin = {
|
|
|
215
228
|
for (const endpoint of ANTIGRAVITY_ENDPOINTS) {
|
|
216
229
|
try {
|
|
217
230
|
const response = await http.fetch(`${endpoint}/v1internal:fetchAvailableModels`, {
|
|
218
|
-
method:
|
|
231
|
+
method: "POST",
|
|
219
232
|
headers: {
|
|
220
|
-
|
|
221
|
-
|
|
233
|
+
Authorization: `Bearer ${accessToken}`,
|
|
234
|
+
"Content-Type": "application/json",
|
|
222
235
|
...ANTIGRAVITY_HEADERS,
|
|
223
236
|
},
|
|
224
237
|
body: JSON.stringify({}),
|
|
225
238
|
});
|
|
226
239
|
|
|
227
240
|
if (!response.ok) {
|
|
228
|
-
const errorText = await response.text().catch(() =>
|
|
241
|
+
const errorText = await response.text().catch(() => "");
|
|
229
242
|
lastError = `${response.status} ${response.statusText}: ${errorText.slice(0, 100)}`;
|
|
230
|
-
|
|
243
|
+
|
|
231
244
|
if (response.status === 401) {
|
|
232
245
|
return {
|
|
233
246
|
fetchedAt: Date.now(),
|
|
234
|
-
error:
|
|
247
|
+
error: "OAuth token expired or invalid. Re-authenticate in OpenCode.",
|
|
235
248
|
};
|
|
236
249
|
}
|
|
237
|
-
|
|
250
|
+
|
|
238
251
|
continue;
|
|
239
252
|
}
|
|
240
253
|
|
|
241
254
|
const data = (await response.json()) as AntigravityModelsResponse;
|
|
242
255
|
|
|
243
256
|
if (!data.models) {
|
|
244
|
-
lastError =
|
|
257
|
+
lastError = "No models data in response";
|
|
245
258
|
continue;
|
|
246
259
|
}
|
|
247
260
|
|
|
248
261
|
const result: ProviderUsageData = {
|
|
249
|
-
planType:
|
|
262
|
+
planType: "Gemini Advanced",
|
|
250
263
|
allowed: true,
|
|
251
264
|
fetchedAt: Date.now(),
|
|
252
265
|
};
|
|
@@ -275,9 +288,9 @@ export const antigravityPlugin: ProviderPlugin = {
|
|
|
275
288
|
.sort((a, b) => {
|
|
276
289
|
const usageDiff = b.usedPercent - a.usedPercent;
|
|
277
290
|
if (usageDiff !== 0) return usageDiff;
|
|
278
|
-
const labelA = a.limit.label ??
|
|
279
|
-
const labelB = b.limit.label ??
|
|
280
|
-
return labelB.localeCompare(labelA, undefined, { numeric: true, sensitivity:
|
|
291
|
+
const labelA = a.limit.label ?? "";
|
|
292
|
+
const labelB = b.limit.label ?? "";
|
|
293
|
+
return labelB.localeCompare(labelA, undefined, { numeric: true, sensitivity: "base" });
|
|
281
294
|
});
|
|
282
295
|
|
|
283
296
|
if (quotas.length > 0) {
|
|
@@ -296,14 +309,13 @@ export const antigravityPlugin: ProviderPlugin = {
|
|
|
296
309
|
return result;
|
|
297
310
|
} catch (err) {
|
|
298
311
|
lastError = err instanceof Error ? err.message : String(err);
|
|
299
|
-
continue;
|
|
300
312
|
}
|
|
301
313
|
}
|
|
302
314
|
|
|
303
|
-
log.error(
|
|
315
|
+
log.error("Failed to fetch Antigravity usage from all endpoints", { error: lastError });
|
|
304
316
|
return {
|
|
305
317
|
fetchedAt: Date.now(),
|
|
306
|
-
error: lastError ??
|
|
318
|
+
error: lastError ?? "Failed to fetch usage from all endpoints",
|
|
307
319
|
};
|
|
308
320
|
},
|
|
309
321
|
};
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import type {
|
|
2
|
-
ProviderPlugin,
|
|
3
|
-
ProviderFetchContext,
|
|
4
|
-
ProviderUsageData,
|
|
5
|
-
Credentials,
|
|
6
2
|
CredentialResult,
|
|
3
|
+
Credentials,
|
|
7
4
|
OAuthCredentials,
|
|
8
5
|
PluginContext,
|
|
9
6
|
ProviderAuth,
|
|
10
|
-
|
|
7
|
+
ProviderFetchContext,
|
|
8
|
+
ProviderPlugin,
|
|
9
|
+
ProviderUsageData,
|
|
10
|
+
} from "../types/provider.ts";
|
|
11
11
|
|
|
12
12
|
interface CodexCliCredentials {
|
|
13
13
|
access_token?: string;
|
|
@@ -57,21 +57,21 @@ interface CodexUsageResponse {
|
|
|
57
57
|
|
|
58
58
|
export const codexPlugin: ProviderPlugin = {
|
|
59
59
|
apiVersion: 2,
|
|
60
|
-
id:
|
|
61
|
-
type:
|
|
62
|
-
name:
|
|
63
|
-
version:
|
|
60
|
+
id: "codex",
|
|
61
|
+
type: "provider",
|
|
62
|
+
name: "Codex",
|
|
63
|
+
version: "1.0.0",
|
|
64
64
|
|
|
65
65
|
meta: {
|
|
66
|
-
description:
|
|
67
|
-
homepage:
|
|
68
|
-
brandColor:
|
|
66
|
+
description: "OpenAI Codex subscription usage tracking (OAuth)",
|
|
67
|
+
homepage: "https://openai.com/codex",
|
|
68
|
+
brandColor: "#10a37f",
|
|
69
69
|
},
|
|
70
70
|
|
|
71
71
|
permissions: {
|
|
72
72
|
network: {
|
|
73
73
|
enabled: true,
|
|
74
|
-
allowedDomains: [
|
|
74
|
+
allowedDomains: ["chatgpt.com"],
|
|
75
75
|
},
|
|
76
76
|
env: {
|
|
77
77
|
read: false,
|
|
@@ -79,7 +79,7 @@ export const codexPlugin: ProviderPlugin = {
|
|
|
79
79
|
},
|
|
80
80
|
filesystem: {
|
|
81
81
|
read: true,
|
|
82
|
-
paths: [
|
|
82
|
+
paths: ["~/.codex"],
|
|
83
83
|
},
|
|
84
84
|
},
|
|
85
85
|
|
|
@@ -91,12 +91,12 @@ export const codexPlugin: ProviderPlugin = {
|
|
|
91
91
|
},
|
|
92
92
|
|
|
93
93
|
pricing: {
|
|
94
|
-
modelsDevProviderId:
|
|
94
|
+
modelsDevProviderId: "openai",
|
|
95
95
|
},
|
|
96
96
|
|
|
97
97
|
auth: {
|
|
98
98
|
async discover(ctx: PluginContext): Promise<CredentialResult> {
|
|
99
|
-
const entry = await ctx.authSources.opencode.getProviderEntry(
|
|
99
|
+
const entry = await ctx.authSources.opencode.getProviderEntry("openai");
|
|
100
100
|
if (entry) {
|
|
101
101
|
const accessToken = entry.accessToken || entry.access;
|
|
102
102
|
const refreshToken = entry.refreshToken || entry.refresh;
|
|
@@ -111,7 +111,7 @@ export const codexPlugin: ProviderPlugin = {
|
|
|
111
111
|
expiresAt ?? undefined,
|
|
112
112
|
entry.accountId,
|
|
113
113
|
),
|
|
114
|
-
source:
|
|
114
|
+
source: "opencode",
|
|
115
115
|
},
|
|
116
116
|
};
|
|
117
117
|
}
|
|
@@ -130,12 +130,12 @@ export const codexPlugin: ProviderPlugin = {
|
|
|
130
130
|
data.expires_at,
|
|
131
131
|
data.account_id,
|
|
132
132
|
),
|
|
133
|
-
source:
|
|
133
|
+
source: "external",
|
|
134
134
|
},
|
|
135
135
|
};
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
-
return { ok: false, reason:
|
|
138
|
+
return { ok: false, reason: "missing", message: "No Codex credentials found" };
|
|
139
139
|
},
|
|
140
140
|
|
|
141
141
|
isConfigured(credentials: Credentials): boolean {
|
|
@@ -149,42 +149,42 @@ export const codexPlugin: ProviderPlugin = {
|
|
|
149
149
|
if (!credentials.oauth?.accessToken) {
|
|
150
150
|
return {
|
|
151
151
|
fetchedAt: Date.now(),
|
|
152
|
-
error:
|
|
152
|
+
error: "OAuth token required. Sign in via OpenCode with ChatGPT Pro account.",
|
|
153
153
|
};
|
|
154
154
|
}
|
|
155
155
|
|
|
156
156
|
if (!credentials.oauth.accountId) {
|
|
157
157
|
return {
|
|
158
158
|
fetchedAt: Date.now(),
|
|
159
|
-
error:
|
|
159
|
+
error: "ChatGPT account ID required. Re-authenticate in OpenCode.",
|
|
160
160
|
};
|
|
161
161
|
}
|
|
162
162
|
|
|
163
163
|
const headers: Record<string, string> = {
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
164
|
+
Authorization: `Bearer ${credentials.oauth.accessToken}`,
|
|
165
|
+
"ChatGPT-Account-Id": credentials.oauth.accountId,
|
|
166
|
+
Accept: "application/json",
|
|
167
|
+
"Content-Type": "application/json",
|
|
168
|
+
"User-Agent": "tokentop",
|
|
169
169
|
};
|
|
170
170
|
|
|
171
171
|
try {
|
|
172
|
-
const response = await http.fetch(
|
|
173
|
-
method:
|
|
172
|
+
const response = await http.fetch("https://chatgpt.com/backend-api/wham/usage", {
|
|
173
|
+
method: "GET",
|
|
174
174
|
headers,
|
|
175
175
|
});
|
|
176
176
|
|
|
177
177
|
if (!response.ok) {
|
|
178
|
-
const bodyText = await response.text().catch(() =>
|
|
179
|
-
log.warn(
|
|
180
|
-
|
|
178
|
+
const bodyText = await response.text().catch(() => "");
|
|
179
|
+
log.warn("Failed to fetch ChatGPT usage", { status: response.status, body: bodyText });
|
|
180
|
+
|
|
181
181
|
if (response.status === 401) {
|
|
182
182
|
return {
|
|
183
183
|
fetchedAt: Date.now(),
|
|
184
|
-
error:
|
|
184
|
+
error: "OAuth token expired. Re-authenticate in OpenCode.",
|
|
185
185
|
};
|
|
186
186
|
}
|
|
187
|
-
|
|
187
|
+
|
|
188
188
|
return {
|
|
189
189
|
fetchedAt: Date.now(),
|
|
190
190
|
error: `API error: ${response.status} ${response.statusText}`,
|
|
@@ -194,12 +194,12 @@ export const codexPlugin: ProviderPlugin = {
|
|
|
194
194
|
const data = (await response.json()) as CodexUsageResponse;
|
|
195
195
|
|
|
196
196
|
const planTypeMap: Record<string, string> = {
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
197
|
+
plus: "ChatGPT Plus",
|
|
198
|
+
pro: "ChatGPT Pro",
|
|
199
|
+
team: "ChatGPT Team",
|
|
200
|
+
enterprise: "ChatGPT Enterprise",
|
|
201
201
|
};
|
|
202
|
-
const planType = planTypeMap[data.plan_type] ?? data.plan_type ??
|
|
202
|
+
const planType = planTypeMap[data.plan_type] ?? data.plan_type ?? "ChatGPT";
|
|
203
203
|
|
|
204
204
|
const result: ProviderUsageData = {
|
|
205
205
|
planType,
|
|
@@ -215,7 +215,7 @@ export const codexPlugin: ProviderPlugin = {
|
|
|
215
215
|
const pw = data.rate_limit.primary_window;
|
|
216
216
|
result.limits.primary = {
|
|
217
217
|
usedPercent: pw.used_percent,
|
|
218
|
-
label:
|
|
218
|
+
label: "5-hour window",
|
|
219
219
|
...(pw.reset_at ? { resetsAt: pw.reset_at * 1000 } : {}),
|
|
220
220
|
};
|
|
221
221
|
}
|
|
@@ -224,7 +224,7 @@ export const codexPlugin: ProviderPlugin = {
|
|
|
224
224
|
const sw = data.rate_limit.secondary_window;
|
|
225
225
|
result.limits.secondary = {
|
|
226
226
|
usedPercent: sw.used_percent,
|
|
227
|
-
label:
|
|
227
|
+
label: "7-day window",
|
|
228
228
|
...(sw.reset_at ? { resetsAt: sw.reset_at * 1000 } : {}),
|
|
229
229
|
};
|
|
230
230
|
}
|
|
@@ -240,10 +240,10 @@ export const codexPlugin: ProviderPlugin = {
|
|
|
240
240
|
|
|
241
241
|
return result;
|
|
242
242
|
} catch (err) {
|
|
243
|
-
log.error(
|
|
243
|
+
log.error("Failed to fetch ChatGPT usage", { error: err });
|
|
244
244
|
return {
|
|
245
245
|
fetchedAt: Date.now(),
|
|
246
|
-
error: err instanceof Error ? err.message :
|
|
246
|
+
error: err instanceof Error ? err.message : "Unknown error",
|
|
247
247
|
};
|
|
248
248
|
}
|
|
249
249
|
},
|