opencode-usage-plugin 0.0.1-dev

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 zenobi.us
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,131 @@
1
+ # opencode-usage-plugin
2
+
3
+ OpenCode plugin that fetches subscription usage for OpenAI, Google, and z.ai.
4
+
5
+ ## Features
6
+
7
+ - `/usage` command for all providers
8
+ - `/usage <provider>` for a single provider
9
+ - Returns normalized JSON for provider usage
10
+ - Reads tokens from OpenCode and auth plugins
11
+
12
+ ## Installation in OpenCode
13
+
14
+ Create or edit `~/.config/opencode/opencode.json`:
15
+
16
+ ```json
17
+ {
18
+ "$schema": "https://opencode.ai/config.json",
19
+ "plugin": ["opencode-usage-plugin@0.0.1"]
20
+ }
21
+ ```
22
+
23
+ For local development, use a relative path:
24
+
25
+ ```json
26
+ {
27
+ "plugin": ["./path/to/opencode-usage-plugin"]
28
+ }
29
+ ```
30
+
31
+ ## Provider Setup
32
+
33
+ ### OpenAI
34
+
35
+ - Install the `opencode-openai-codex-auth` plugin, or ensure `openai` auth exists in
36
+ `~/.local/share/opencode/auth.json` (aliases: `codex`, `chatgpt`).
37
+ - The plugin also checks `~/.opencode/auth/openai.json` as a fallback.
38
+
39
+ ### Google
40
+
41
+ - Install an Antigravity auth plugin (for example, `opencode-antigravity-auth`) or ensure
42
+ `google` auth exists in `~/.local/share/opencode/auth.json` (alias: `antigravity`).
43
+ - Ensure accounts are stored in `~/.config/opencode/antigravity-accounts.json`.
44
+ - The `activeIndex` account is used for usage checks (falls back to the first account).
45
+
46
+ ### z.ai
47
+
48
+ - Add a `zai-coding-plan` entry in `~/.local/share/opencode/auth.json` with the API key
49
+ (aliases: `zai`, `z.ai`), or set `ZAI_API_KEY`.
50
+
51
+ ## Usage
52
+
53
+ ```bash
54
+ /usage
55
+ /usage openai
56
+ /usage google
57
+ /usage zai-coding-plan
58
+ ```
59
+
60
+ ## Output
61
+
62
+ - `usage` tool returns a JSON array (string) of provider usage results
63
+ - `command/usage.md` formats a human-readable summary
64
+
65
+ ## Development
66
+
67
+ ```bash
68
+ npm install
69
+ mise run build
70
+ npm test
71
+ mise run lint
72
+ ```
73
+
74
+ ### Testing
75
+
76
+ Tests use **vitest** with a provider-specific auth configuration system.
77
+
78
+ **Run all tests** (mocks only, default):
79
+
80
+ ```bash
81
+ npm test
82
+ ```
83
+
84
+ **Run tests with real auth**:
85
+
86
+ ```bash
87
+ # Edit .env.test and set desired provider(s) to 1
88
+ TEST_REAL_OPENAI_AUTH=1
89
+ TEST_REAL_GOOGLE_AUTH=1
90
+ TEST_REAL_ZAI_CODING_PLAN_AUTH=1
91
+ npm test
92
+ ```
93
+
94
+ **Run specific provider tests**:
95
+
96
+ ```bash
97
+ npm test -- src/providers/openai/fetch.test.ts
98
+ npm test -- src/providers/google/fetch.test.ts
99
+ npm test -- src/providers/zai-coding-plan/fetch.test.ts
100
+ ```
101
+
102
+ **Test structure**:
103
+
104
+ - `src/providers/common/test-helpers.ts` - Shared test utilities and fixtures
105
+ - `src/providers/*/*.test.ts` - Provider-specific tests with mocks and real auth
106
+ - `.env.test` - Auth configuration flags (committed to repo)
107
+ - `vitest.config.ts` - Test configuration
108
+
109
+ Tests are organized into two categories per provider:
110
+
111
+ - **Mock tests** - Run by default, test all edge cases and error handling
112
+ - **Real auth tests** - Skipped by default, enabled via `.env.test` flags
113
+
114
+ **Adding new providers**:
115
+
116
+ 1. Implement provider function in `src/providers/`
117
+ 2. Add `TEST_REAL_NEWPROVIDER_AUTH=0` to `.env.test`
118
+ 3. Create `src/providers/newprovider.test.ts` following existing patterns
119
+ 4. Add helpers to `src/test-helpers.ts` if needed
120
+
121
+ ## Author
122
+
123
+ Nelson Pires <nelsonpires.sn@gmail.com>
124
+
125
+ ## Repository
126
+
127
+ https://github.com/nelsonPires5/opencode-usage-plugin
128
+
129
+ ## License
130
+
131
+ MIT License. See the [LICENSE](LICENSE) file for details.
@@ -0,0 +1,24 @@
1
+ ---
2
+ description: Show remaining usage for AI coding providers (OpenAI, Google, z.ai)
3
+ ---
4
+
5
+ Call the `usage` tool.
6
+
7
+ If $ARGUMENTS is empty, fetch usage for all configured providers.
8
+ If $ARGUMENTS is provided, it should be one of: openai, google, zai-coding-plan (aliases: codex, antigravity, zai).
9
+
10
+ The tool returns a JSON string with a list of provider results:
11
+
12
+ - `provider`, `ok`, `configured`, `error`
13
+ - `usage.windows` for global windows
14
+ - `usage.models[model].windows` for per-model windows
15
+
16
+ Each window includes:
17
+
18
+ - `remainingPercent` - remaining usage percentage
19
+ - `resetAfterFormatted` - human-readable time remaining (e.g., "2w 3d 5h 10m 30s")
20
+ - `resetAtFormatted` - exact reset datetime (e.g., "Thursday, January 16, 2026, 2:30:45 PM EST")
21
+ - `windowSeconds` - size of the usage window
22
+
23
+ Parse the JSON, then return a short markdown summary per provider.
24
+ Include remaining percent, reset time remaining, reset datetime, window size, and any errors.
@@ -0,0 +1,3 @@
1
+ import type { Plugin } from '@opencode-ai/plugin';
2
+ export declare const UsagePlugin: Plugin;
3
+ export default UsagePlugin;
package/dist/index.js ADDED
@@ -0,0 +1,672 @@
1
+ // src/index.ts
2
+ import { tool } from "@opencode-ai/plugin";
3
+ import { fileURLToPath } from "node:url";
4
+ import { readdir, readFile as readFile2 } from "node:fs/promises";
5
+ import path from "path";
6
+
7
+ // src/providers/common/time.ts
8
+ var calculateResetAfterSeconds = (resetAt, now = Date.now()) => {
9
+ if (!resetAt) {
10
+ return null;
11
+ }
12
+ const diffMs = resetAt - now;
13
+ if (diffMs <= 0) {
14
+ return 0;
15
+ }
16
+ return Math.floor(diffMs / 1e3);
17
+ };
18
+ var formatDuration = (seconds) => {
19
+ if (seconds <= 0) {
20
+ return "0s";
21
+ }
22
+ const weeks = Math.floor(seconds / 604800);
23
+ const days = Math.floor(seconds % 604800 / 86400);
24
+ const hours = Math.floor(seconds % 86400 / 3600);
25
+ const minutes = Math.floor(seconds % 3600 / 60);
26
+ const secs = seconds % 60;
27
+ const parts = [];
28
+ if (weeks > 0) parts.push(`${weeks}w`);
29
+ if (days > 0) parts.push(`${days}d`);
30
+ if (hours > 0) parts.push(`${hours}h`);
31
+ if (minutes > 0) parts.push(`${minutes}m`);
32
+ if (secs > 0 || parts.length === 0) parts.push(`${secs}s`);
33
+ return parts.join(" ");
34
+ };
35
+ var formatResetAt = (resetAtMs) => {
36
+ return new Date(resetAtMs).toLocaleString(void 0, {
37
+ weekday: "long",
38
+ year: "numeric",
39
+ month: "long",
40
+ day: "numeric",
41
+ hour: "numeric",
42
+ minute: "numeric",
43
+ second: "numeric",
44
+ timeZoneName: "short"
45
+ });
46
+ };
47
+
48
+ // src/providers/common/files.ts
49
+ import { readFile } from "node:fs/promises";
50
+ import { homedir } from "node:os";
51
+ import { join } from "node:path";
52
+ var xdgDataHome = () => process.env.XDG_DATA_HOME ?? join(homedir(), ".local", "share");
53
+ var xdgConfigHome = () => process.env.XDG_CONFIG_HOME ?? join(homedir(), ".config");
54
+ var AUTH_PATHS = {
55
+ opencode: () => join(xdgDataHome(), "opencode", "auth.json"),
56
+ openaiPlugin: () => join(homedir(), ".opencode", "auth", "openai.json"),
57
+ antigravityConfig: () => join(xdgConfigHome(), "opencode", "antigravity-accounts.json"),
58
+ antigravityData: () => join(xdgDataHome(), "opencode", "antigravity-accounts.json")
59
+ };
60
+ var readJson = async (filePath) => {
61
+ try {
62
+ const content = await readFile(filePath, "utf-8");
63
+ return JSON.parse(content);
64
+ } catch {
65
+ return null;
66
+ }
67
+ };
68
+ var loadOpenCodeAuth = async () => {
69
+ return readJson(AUTH_PATHS.opencode());
70
+ };
71
+
72
+ // src/providers/common/registry.ts
73
+ var PROVIDER_ALIASES = {
74
+ openai: ["openai", "codex", "chatgpt"],
75
+ google: ["google", "antigravity"],
76
+ "zai-coding-plan": ["zai-coding-plan", "zai", "z.ai"]
77
+ };
78
+ var parseProvider = (input) => {
79
+ if (!input) {
80
+ return null;
81
+ }
82
+ const normalized = input.trim().toLowerCase();
83
+ for (const [providerId, aliases] of Object.entries(PROVIDER_ALIASES)) {
84
+ if (aliases.includes(normalized)) {
85
+ return providerId;
86
+ }
87
+ }
88
+ return null;
89
+ };
90
+ var getProviderAliases = (provider) => {
91
+ return PROVIDER_ALIASES[provider];
92
+ };
93
+
94
+ // src/providers/google/auth.ts
95
+ var toAuthData = (entry) => {
96
+ if (!entry) {
97
+ return null;
98
+ }
99
+ if (typeof entry === "string") {
100
+ return { token: entry };
101
+ }
102
+ if (typeof entry === "object") {
103
+ return entry;
104
+ }
105
+ return null;
106
+ };
107
+ var loadOpenCodeAuthEntry = async () => {
108
+ const auth = await loadOpenCodeAuth();
109
+ if (!auth) {
110
+ return null;
111
+ }
112
+ for (const alias of getProviderAliases("google")) {
113
+ const entry = toAuthData(auth[alias]);
114
+ if (entry) {
115
+ return entry;
116
+ }
117
+ }
118
+ return null;
119
+ };
120
+ var toAuthContext = (entry) => {
121
+ if (!entry) {
122
+ return null;
123
+ }
124
+ const accessToken = entry.access ?? entry.token;
125
+ const refreshToken = entry.refresh;
126
+ if (!accessToken && !refreshToken) {
127
+ return null;
128
+ }
129
+ return {
130
+ accessToken,
131
+ refreshToken,
132
+ expires: entry.expires
133
+ };
134
+ };
135
+ var selectAccount = (accounts) => {
136
+ if (!accounts?.accounts?.length) {
137
+ return null;
138
+ }
139
+ const candidateIndex = accounts.activeIndex ?? 0;
140
+ const account = accounts.accounts[candidateIndex] ?? accounts.accounts[0];
141
+ return account ?? null;
142
+ };
143
+ var loadAuthFromAccounts = async () => {
144
+ const configAccounts = await readJson(AUTH_PATHS.antigravityConfig());
145
+ const account = selectAccount(configAccounts);
146
+ if (account) {
147
+ return {
148
+ refreshToken: account.refreshToken,
149
+ projectId: account.projectId ?? account.managedProjectId,
150
+ email: account.email
151
+ };
152
+ }
153
+ const dataAccounts = await readJson(AUTH_PATHS.antigravityData());
154
+ const fallbackAccount = selectAccount(dataAccounts);
155
+ if (!fallbackAccount) {
156
+ return null;
157
+ }
158
+ return {
159
+ refreshToken: fallbackAccount.refreshToken,
160
+ projectId: fallbackAccount.projectId ?? fallbackAccount.managedProjectId,
161
+ email: fallbackAccount.email
162
+ };
163
+ };
164
+ var getGoogleAuth = async () => {
165
+ const openCodeAuth = await loadOpenCodeAuthEntry();
166
+ const authContext = toAuthContext(openCodeAuth);
167
+ if (authContext) {
168
+ return authContext;
169
+ }
170
+ return loadAuthFromAccounts();
171
+ };
172
+
173
+ // src/providers/google/fetch.ts
174
+ var GOOGLE_CLIENT_ID = "1071006060591-tmhssin2h21lcre235vtolojh4g403ep.apps.googleusercontent.com";
175
+ var GOOGLE_CLIENT_SECRET = "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";
176
+ var DEFAULT_PROJECT_ID = "rising-fact-p41fc";
177
+ var WINDOW_SECONDS = 5 * 60 * 60;
178
+ var ENDPOINTS = [
179
+ "https://daily-cloudcode-pa.sandbox.googleapis.com",
180
+ "https://autopush-cloudcode-pa.sandbox.googleapis.com",
181
+ "https://cloudcode-pa.googleapis.com"
182
+ ];
183
+ var HEADERS = {
184
+ "User-Agent": "antigravity/1.11.5 windows/amd64",
185
+ "X-Goog-Api-Client": "google-cloud-sdk vscode_cloudshelleditor/0.1",
186
+ "Client-Metadata": '{"ideType":"IDE_UNSPECIFIED","platform":"PLATFORM_UNSPECIFIED","pluginType":"GEMINI"}'
187
+ };
188
+ var refreshAccessToken = async (refreshToken) => {
189
+ try {
190
+ const response = await fetch("https://oauth2.googleapis.com/token", {
191
+ method: "POST",
192
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
193
+ body: new URLSearchParams({
194
+ client_id: GOOGLE_CLIENT_ID,
195
+ client_secret: GOOGLE_CLIENT_SECRET,
196
+ refresh_token: refreshToken,
197
+ grant_type: "refresh_token"
198
+ })
199
+ });
200
+ if (!response.ok) {
201
+ return null;
202
+ }
203
+ return await response.json();
204
+ } catch {
205
+ return null;
206
+ }
207
+ };
208
+ var fetchModels = async (accessToken, projectId) => {
209
+ const body = projectId ? { project: projectId } : {};
210
+ for (const endpoint of ENDPOINTS) {
211
+ try {
212
+ const response = await fetch(`${endpoint}/v1internal:fetchAvailableModels`, {
213
+ method: "POST",
214
+ headers: {
215
+ Authorization: `Bearer ${accessToken}`,
216
+ "Content-Type": "application/json",
217
+ ...HEADERS
218
+ },
219
+ body: JSON.stringify(body),
220
+ signal: AbortSignal.timeout(15e3)
221
+ });
222
+ if (response.ok) {
223
+ return await response.json();
224
+ }
225
+ } catch {
226
+ continue;
227
+ }
228
+ }
229
+ return null;
230
+ };
231
+ var toWindow = (remainingFraction, resetTime) => {
232
+ const remainingPercent = remainingFraction !== void 0 ? Math.round(remainingFraction * 100) : null;
233
+ const usedPercent = remainingPercent !== null ? Math.max(0, 100 - remainingPercent) : null;
234
+ const resetAt = resetTime ? new Date(resetTime).getTime() : null;
235
+ const resetAfterSeconds = calculateResetAfterSeconds(resetAt);
236
+ return {
237
+ usedPercent,
238
+ remainingPercent,
239
+ windowSeconds: WINDOW_SECONDS,
240
+ resetAfterSeconds,
241
+ resetAt,
242
+ resetAtFormatted: resetAt ? formatResetAt(resetAt) : null,
243
+ resetAfterFormatted: resetAfterSeconds !== null ? formatDuration(resetAfterSeconds) : null
244
+ };
245
+ };
246
+ var buildUsage = (data) => {
247
+ const models = {};
248
+ for (const [modelName, modelData] of Object.entries(data.models ?? {})) {
249
+ const window = toWindow(modelData.quotaInfo?.remainingFraction, modelData.quotaInfo?.resetTime);
250
+ models[modelName] = {
251
+ windows: {
252
+ "5h": window
253
+ }
254
+ };
255
+ }
256
+ return {
257
+ windows: {},
258
+ models: Object.keys(models).length ? models : void 0
259
+ };
260
+ };
261
+ var resolveAccessToken = async (refreshToken, accessToken, expires) => {
262
+ const now = Date.now();
263
+ if (accessToken && (!expires || expires > now)) {
264
+ return accessToken;
265
+ }
266
+ if (!refreshToken) {
267
+ return null;
268
+ }
269
+ const refreshed = await refreshAccessToken(refreshToken);
270
+ return refreshed?.access_token ?? null;
271
+ };
272
+ var fetchGoogleUsage = async () => {
273
+ const auth = await getGoogleAuth();
274
+ if (!auth) {
275
+ return {
276
+ provider: "google",
277
+ ok: false,
278
+ configured: false,
279
+ error: "Not configured - no accounts found",
280
+ usage: null
281
+ };
282
+ }
283
+ const accessToken = await resolveAccessToken(auth.refreshToken, auth.accessToken, auth.expires);
284
+ if (!accessToken) {
285
+ return {
286
+ provider: "google",
287
+ ok: false,
288
+ configured: true,
289
+ error: "Failed to refresh OAuth token",
290
+ usage: null
291
+ };
292
+ }
293
+ const projectId = auth.projectId ?? DEFAULT_PROJECT_ID;
294
+ const modelsData = await fetchModels(accessToken, projectId);
295
+ if (!modelsData) {
296
+ return {
297
+ provider: "google",
298
+ ok: false,
299
+ configured: true,
300
+ error: "Failed to fetch models from API",
301
+ usage: null
302
+ };
303
+ }
304
+ return {
305
+ provider: "google",
306
+ ok: true,
307
+ configured: true,
308
+ usage: buildUsage(modelsData)
309
+ };
310
+ };
311
+
312
+ // src/providers/openai/auth.ts
313
+ var toAuthData2 = (entry) => {
314
+ if (!entry) {
315
+ return null;
316
+ }
317
+ if (typeof entry === "string") {
318
+ return { token: entry };
319
+ }
320
+ if (typeof entry === "object") {
321
+ return entry;
322
+ }
323
+ return null;
324
+ };
325
+ var hasAccessToken = (auth) => {
326
+ return Boolean(auth?.access || auth?.token);
327
+ };
328
+ var loadOpenCodeAuthEntry2 = async () => {
329
+ const auth = await loadOpenCodeAuth();
330
+ if (!auth) {
331
+ return null;
332
+ }
333
+ for (const alias of getProviderAliases("openai")) {
334
+ const entry = toAuthData2(auth[alias]);
335
+ if (entry && hasAccessToken(entry)) {
336
+ return entry;
337
+ }
338
+ }
339
+ return null;
340
+ };
341
+ var getOpenaiAuth = async () => {
342
+ const openCodeAuth = await loadOpenCodeAuthEntry2();
343
+ if (openCodeAuth && hasAccessToken(openCodeAuth)) {
344
+ return openCodeAuth;
345
+ }
346
+ const pluginAuth = await readJson(AUTH_PATHS.openaiPlugin());
347
+ if (pluginAuth && hasAccessToken(pluginAuth)) {
348
+ return pluginAuth;
349
+ }
350
+ return null;
351
+ };
352
+
353
+ // src/providers/openai/fetch.ts
354
+ var toWindow2 = (window) => {
355
+ if (!window) {
356
+ return null;
357
+ }
358
+ const usedPercent = window.used_percent;
359
+ const resetAt = window.reset_at ? window.reset_at * 1e3 : null;
360
+ const resetAfterSeconds = window.reset_after_seconds ?? calculateResetAfterSeconds(resetAt);
361
+ return {
362
+ usedPercent,
363
+ remainingPercent: Math.max(0, 100 - usedPercent),
364
+ windowSeconds: window.limit_window_seconds ?? null,
365
+ resetAfterSeconds,
366
+ resetAt,
367
+ resetAtFormatted: resetAt ? formatResetAt(resetAt) : null,
368
+ resetAfterFormatted: resetAfterSeconds !== null ? formatDuration(resetAfterSeconds) : null
369
+ };
370
+ };
371
+ var fetchOpenaiUsage = async () => {
372
+ const auth = await getOpenaiAuth();
373
+ if (!auth) {
374
+ return {
375
+ provider: "openai",
376
+ ok: false,
377
+ configured: false,
378
+ error: "Not configured - no OAuth token found",
379
+ usage: null
380
+ };
381
+ }
382
+ const accessToken = auth.access ?? auth.token;
383
+ if (!accessToken) {
384
+ return {
385
+ provider: "openai",
386
+ ok: false,
387
+ configured: false,
388
+ error: "Not configured - access token missing",
389
+ usage: null
390
+ };
391
+ }
392
+ try {
393
+ const response = await fetch("https://chatgpt.com/backend-api/wham/usage", {
394
+ method: "GET",
395
+ headers: {
396
+ Authorization: `Bearer ${accessToken}`,
397
+ "Content-Type": "application/json"
398
+ }
399
+ });
400
+ if (!response.ok) {
401
+ return {
402
+ provider: "openai",
403
+ ok: false,
404
+ configured: true,
405
+ error: `API error: ${response.status}`,
406
+ usage: null
407
+ };
408
+ }
409
+ const payload = await response.json();
410
+ const primary = toWindow2(payload.rate_limit.primary_window);
411
+ const secondary = toWindow2(payload.rate_limit.secondary_window);
412
+ const windows = {};
413
+ if (primary) {
414
+ windows["5h"] = primary;
415
+ }
416
+ if (secondary) {
417
+ windows["weekly"] = secondary;
418
+ }
419
+ const usage = {
420
+ windows
421
+ };
422
+ return {
423
+ provider: "openai",
424
+ ok: true,
425
+ configured: true,
426
+ usage
427
+ };
428
+ } catch (error) {
429
+ const message = error instanceof Error ? error.message : String(error);
430
+ return {
431
+ provider: "openai",
432
+ ok: false,
433
+ configured: true,
434
+ error: `Request failed: ${message}`,
435
+ usage: null
436
+ };
437
+ }
438
+ };
439
+
440
+ // src/providers/zai-coding-plan/auth.ts
441
+ var resolveAuthValue = (entry) => {
442
+ if (!entry) {
443
+ return null;
444
+ }
445
+ if (typeof entry === "string") {
446
+ return entry;
447
+ }
448
+ if (typeof entry === "object") {
449
+ return entry.api_key ?? entry.token ?? entry.key ?? null;
450
+ }
451
+ return null;
452
+ };
453
+ var getZaiApiKey = async () => {
454
+ if (process.env.ZAI_API_KEY) {
455
+ return process.env.ZAI_API_KEY;
456
+ }
457
+ const auth = await loadOpenCodeAuth();
458
+ if (!auth) {
459
+ return null;
460
+ }
461
+ for (const alias of getProviderAliases("zai-coding-plan")) {
462
+ const value = resolveAuthValue(auth[alias]);
463
+ if (value) {
464
+ return value;
465
+ }
466
+ }
467
+ return null;
468
+ };
469
+
470
+ // src/providers/zai-coding-plan/fetch.ts
471
+ var normalizeTimestamp = (value) => {
472
+ return value < 1e12 ? value * 1e3 : value;
473
+ };
474
+ var TOKEN_WINDOW_SECONDS = {
475
+ 3: 3600
476
+ };
477
+ var resolveWindowSeconds = (limit) => {
478
+ if (!limit) {
479
+ return null;
480
+ }
481
+ if (!limit.number) {
482
+ return null;
483
+ }
484
+ const unitSeconds = TOKEN_WINDOW_SECONDS[limit.unit];
485
+ if (!unitSeconds) {
486
+ return null;
487
+ }
488
+ return unitSeconds * limit.number;
489
+ };
490
+ var resolveWindowLabel = (windowSeconds) => {
491
+ if (!windowSeconds) {
492
+ return "tokens";
493
+ }
494
+ if (windowSeconds % 86400 === 0) {
495
+ const days = windowSeconds / 86400;
496
+ return days === 7 ? "weekly" : `${days}d`;
497
+ }
498
+ if (windowSeconds % 3600 === 0) {
499
+ return `${windowSeconds / 3600}h`;
500
+ }
501
+ return `${windowSeconds}s`;
502
+ };
503
+ var toWindow3 = (limit) => {
504
+ if (!limit) {
505
+ return null;
506
+ }
507
+ const usedPercent = limit.percentage ?? null;
508
+ const remainingPercent = usedPercent !== null ? Math.max(0, 100 - usedPercent) : null;
509
+ const resetAt = limit.nextResetTime ? normalizeTimestamp(limit.nextResetTime) : null;
510
+ const resetAfterSeconds = calculateResetAfterSeconds(resetAt);
511
+ return {
512
+ usedPercent,
513
+ remainingPercent,
514
+ windowSeconds: resolveWindowSeconds(limit),
515
+ resetAfterSeconds,
516
+ resetAt,
517
+ resetAtFormatted: resetAt ? formatResetAt(resetAt) : null,
518
+ resetAfterFormatted: resetAfterSeconds !== null ? formatDuration(resetAfterSeconds) : null
519
+ };
520
+ };
521
+ var fetchZaiUsage = async () => {
522
+ const apiKey = await getZaiApiKey();
523
+ if (!apiKey) {
524
+ return {
525
+ provider: "zai-coding-plan",
526
+ ok: false,
527
+ configured: false,
528
+ error: "Not configured - no API key found",
529
+ usage: null
530
+ };
531
+ }
532
+ try {
533
+ const response = await fetch("https://api.z.ai/api/monitor/usage/quota/limit", {
534
+ method: "GET",
535
+ headers: {
536
+ Authorization: `Bearer ${apiKey}`,
537
+ "Content-Type": "application/json"
538
+ }
539
+ });
540
+ if (!response.ok) {
541
+ return {
542
+ provider: "zai-coding-plan",
543
+ ok: false,
544
+ configured: true,
545
+ error: `API error: ${response.status}`,
546
+ usage: null
547
+ };
548
+ }
549
+ const payload = await response.json();
550
+ const limits = payload.data?.limits ?? [];
551
+ const tokensLimit = limits.find((limit) => limit.type === "TOKENS_LIMIT");
552
+ const windows = {};
553
+ const window = toWindow3(tokensLimit);
554
+ if (window) {
555
+ const label = resolveWindowLabel(window.windowSeconds);
556
+ windows[label] = window;
557
+ }
558
+ const usage = {
559
+ windows
560
+ };
561
+ return {
562
+ provider: "zai-coding-plan",
563
+ ok: true,
564
+ configured: true,
565
+ usage
566
+ };
567
+ } catch (error) {
568
+ const message = error instanceof Error ? error.message : String(error);
569
+ return {
570
+ provider: "zai-coding-plan",
571
+ ok: false,
572
+ configured: true,
573
+ error: `Request failed: ${message}`,
574
+ usage: null
575
+ };
576
+ }
577
+ };
578
+
579
+ // src/types.ts
580
+ var PROVIDERS = ["openai", "google", "zai-coding-plan"];
581
+
582
+ // src/index.ts
583
+ var parseFrontmatter = (content) => {
584
+ const match = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
585
+ if (!match) {
586
+ return { frontmatter: {}, body: content.trim() };
587
+ }
588
+ const [, yamlContent, body] = match;
589
+ const frontmatter = {};
590
+ for (const line of yamlContent.split("\n")) {
591
+ const colonIndex = line.indexOf(":");
592
+ if (colonIndex === -1) {
593
+ continue;
594
+ }
595
+ const key = line.slice(0, colonIndex).trim();
596
+ const value = line.slice(colonIndex + 1).trim();
597
+ if (key === "description") {
598
+ frontmatter.description = value;
599
+ }
600
+ }
601
+ return { frontmatter, body: body.trim() };
602
+ };
603
+ var loadCommands = async () => {
604
+ const commands = [];
605
+ const __filename = fileURLToPath(import.meta.url);
606
+ const __dirname = path.dirname(__filename);
607
+ const commandDir = path.join(__dirname, "command");
608
+ const walkDir = async (dir, baseDir = dir) => {
609
+ const entries = await readdir(dir, { withFileTypes: true });
610
+ for (const entry of entries) {
611
+ const fullPath = path.join(dir, entry.name);
612
+ if (entry.isDirectory()) {
613
+ await walkDir(fullPath, baseDir);
614
+ } else if (entry.name.endsWith(".md")) {
615
+ const content = await readFile2(fullPath, "utf-8");
616
+ const { frontmatter, body } = parseFrontmatter(content);
617
+ const relativePath = path.relative(baseDir, fullPath);
618
+ const name = relativePath.replace(/\.md$/, "").replace(/\//g, "-");
619
+ commands.push({ name, frontmatter, template: body });
620
+ }
621
+ }
622
+ };
623
+ await walkDir(commandDir);
624
+ return commands;
625
+ };
626
+ var fetchUsage = async (provider) => {
627
+ switch (provider) {
628
+ case "openai":
629
+ return fetchOpenaiUsage();
630
+ case "google":
631
+ return fetchGoogleUsage();
632
+ case "zai-coding-plan":
633
+ return fetchZaiUsage();
634
+ }
635
+ };
636
+ var UsagePlugin = async () => {
637
+ const commands = await loadCommands();
638
+ const usageTool = tool({
639
+ description: "Fetch subscription usage for OpenAI, Google, and z.ai providers.",
640
+ args: {
641
+ provider: tool.schema.string().optional().describe(
642
+ "Provider to check: openai, google, or zai-coding-plan. Aliases: codex, antigravity, zai."
643
+ )
644
+ },
645
+ async execute(args) {
646
+ const targetProvider = parseProvider(args.provider);
647
+ const providers = targetProvider ? [targetProvider] : PROVIDERS;
648
+ const results = await Promise.all(providers.map(fetchUsage));
649
+ return JSON.stringify(results, null, 2);
650
+ }
651
+ });
652
+ return {
653
+ tool: {
654
+ usage: usageTool
655
+ },
656
+ async config(config) {
657
+ config.command = config.command ?? {};
658
+ for (const cmd of commands) {
659
+ config.command[cmd.name] = {
660
+ template: cmd.template,
661
+ description: cmd.frontmatter.description
662
+ };
663
+ }
664
+ }
665
+ };
666
+ };
667
+ var index_default = UsagePlugin;
668
+ export {
669
+ UsagePlugin,
670
+ index_default as default
671
+ };
672
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/index.ts", "../src/providers/common/time.ts", "../src/providers/common/files.ts", "../src/providers/common/registry.ts", "../src/providers/google/auth.ts", "../src/providers/google/fetch.ts", "../src/providers/openai/auth.ts", "../src/providers/openai/fetch.ts", "../src/providers/zai-coding-plan/auth.ts", "../src/providers/zai-coding-plan/fetch.ts", "../src/types.ts"],
4
+ "sourcesContent": ["import type { Plugin } from '@opencode-ai/plugin';\nimport { tool } from '@opencode-ai/plugin';\nimport { fileURLToPath } from 'node:url';\nimport { readdir, readFile } from 'node:fs/promises';\nimport path from 'path';\n\nimport { fetchGoogleUsage } from './providers/google/fetch.ts';\nimport { fetchOpenaiUsage } from './providers/openai/fetch.ts';\nimport { fetchZaiUsage } from './providers/zai-coding-plan/fetch.ts';\nimport { parseProvider } from './providers/common/registry.ts';\nimport { PROVIDERS, type ProviderId, type ProviderResult } from './types.ts';\n\ninterface CommandFrontmatter {\n description?: string;\n}\n\ninterface ParsedCommand {\n name: string;\n frontmatter: CommandFrontmatter;\n template: string;\n}\n\ninterface UsageArgs {\n provider?: string;\n}\n\nconst parseFrontmatter = (content: string): { frontmatter: CommandFrontmatter; body: string } => {\n const match = content.match(/^---\\n([\\s\\S]*?)\\n---\\n([\\s\\S]*)$/);\n if (!match) {\n return { frontmatter: {}, body: content.trim() };\n }\n\n const [, yamlContent, body] = match;\n const frontmatter: CommandFrontmatter = {};\n\n for (const line of yamlContent.split('\\n')) {\n const colonIndex = line.indexOf(':');\n if (colonIndex === -1) {\n continue;\n }\n\n const key = line.slice(0, colonIndex).trim();\n const value = line.slice(colonIndex + 1).trim();\n if (key === 'description') {\n frontmatter.description = value;\n }\n }\n\n return { frontmatter, body: body.trim() };\n};\n\nconst loadCommands = async (): Promise<ParsedCommand[]> => {\n const commands: ParsedCommand[] = [];\n const __filename = fileURLToPath(import.meta.url);\n const __dirname = path.dirname(__filename);\n const commandDir = path.join(__dirname, 'command');\n\n const walkDir = async (dir: string, baseDir: string = dir): Promise<void> => {\n const entries = await readdir(dir, { withFileTypes: true });\n\n for (const entry of entries) {\n const fullPath = path.join(dir, entry.name);\n\n if (entry.isDirectory()) {\n await walkDir(fullPath, baseDir);\n } else if (entry.name.endsWith('.md')) {\n const content = await readFile(fullPath, 'utf-8');\n const { frontmatter, body } = parseFrontmatter(content);\n const relativePath = path.relative(baseDir, fullPath);\n const name = relativePath.replace(/\\.md$/, '').replace(/\\//g, '-');\n commands.push({ name, frontmatter, template: body });\n }\n }\n };\n\n await walkDir(commandDir);\n return commands;\n};\n\nconst fetchUsage = async (provider: ProviderId): Promise<ProviderResult> => {\n switch (provider) {\n case 'openai':\n return fetchOpenaiUsage();\n case 'google':\n return fetchGoogleUsage();\n case 'zai-coding-plan':\n return fetchZaiUsage();\n }\n};\n\nexport const UsagePlugin: Plugin = async () => {\n const commands = await loadCommands();\n\n const usageTool = tool({\n description: 'Fetch subscription usage for OpenAI, Google, and z.ai providers.',\n args: {\n provider: tool.schema\n .string()\n .optional()\n .describe(\n 'Provider to check: openai, google, or zai-coding-plan. Aliases: codex, antigravity, zai.'\n ),\n },\n async execute(args: UsageArgs) {\n const targetProvider = parseProvider(args.provider);\n const providers: ProviderId[] = targetProvider ? [targetProvider] : PROVIDERS;\n const results = await Promise.all(providers.map(fetchUsage));\n\n return JSON.stringify(results, null, 2);\n },\n });\n\n return {\n tool: {\n usage: usageTool,\n },\n async config(config) {\n config.command = config.command ?? {};\n\n for (const cmd of commands) {\n config.command[cmd.name] = {\n template: cmd.template,\n description: cmd.frontmatter.description,\n };\n }\n },\n };\n};\n\nexport default UsagePlugin;\n", "export const calculateResetAfterSeconds = (\n resetAt: number | null,\n now: number = Date.now()\n): number | null => {\n if (!resetAt) {\n return null;\n }\n\n const diffMs = resetAt - now;\n if (diffMs <= 0) {\n return 0;\n }\n\n return Math.floor(diffMs / 1000);\n};\n\nexport const calculateResetAt = (\n resetAfterSeconds: number | null,\n now: number = Date.now()\n): number | null => {\n if (resetAfterSeconds === null || resetAfterSeconds === undefined) {\n return null;\n }\n\n return now + resetAfterSeconds * 1000;\n};\n\nexport const formatDuration = (seconds: number): string => {\n if (seconds <= 0) {\n return '0s';\n }\n\n const weeks = Math.floor(seconds / 604800);\n const days = Math.floor((seconds % 604800) / 86400);\n const hours = Math.floor((seconds % 86400) / 3600);\n const minutes = Math.floor((seconds % 3600) / 60);\n const secs = seconds % 60;\n\n const parts: string[] = [];\n if (weeks > 0) parts.push(`${weeks}w`);\n if (days > 0) parts.push(`${days}d`);\n if (hours > 0) parts.push(`${hours}h`);\n if (minutes > 0) parts.push(`${minutes}m`);\n if (secs > 0 || parts.length === 0) parts.push(`${secs}s`);\n\n return parts.join(' ');\n};\n\nexport const formatResetAt = (resetAtMs: number): string => {\n return new Date(resetAtMs).toLocaleString(undefined, {\n weekday: 'long',\n year: 'numeric',\n month: 'long',\n day: 'numeric',\n hour: 'numeric',\n minute: 'numeric',\n second: 'numeric',\n timeZoneName: 'short',\n });\n};\n", "import { readFile } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\n\nimport type { OpenCodeAuth } from '../../types.ts';\n\nexport const xdgDataHome = (): string =>\n process.env.XDG_DATA_HOME ?? join(homedir(), '.local', 'share');\n\nexport const xdgConfigHome = (): string =>\n process.env.XDG_CONFIG_HOME ?? join(homedir(), '.config');\n\nexport const AUTH_PATHS = {\n opencode: (): string => join(xdgDataHome(), 'opencode', 'auth.json'),\n openaiPlugin: (): string => join(homedir(), '.opencode', 'auth', 'openai.json'),\n antigravityConfig: (): string => join(xdgConfigHome(), 'opencode', 'antigravity-accounts.json'),\n antigravityData: (): string => join(xdgDataHome(), 'opencode', 'antigravity-accounts.json'),\n} as const;\n\nexport const readJson = async <T>(filePath: string): Promise<T | null> => {\n try {\n const content = await readFile(filePath, 'utf-8');\n return JSON.parse(content) as T;\n } catch {\n return null;\n }\n};\n\nexport const loadOpenCodeAuth = async (): Promise<OpenCodeAuth | null> => {\n return readJson<OpenCodeAuth>(AUTH_PATHS.opencode());\n};\n", "import type { ProviderAlias, ProviderId } from '../../types.ts';\n\nexport const PROVIDER_ALIASES: Record<ProviderId, ProviderAlias[]> = {\n openai: ['openai', 'codex', 'chatgpt'],\n google: ['google', 'antigravity'],\n 'zai-coding-plan': ['zai-coding-plan', 'zai', 'z.ai'],\n};\n\nexport const parseProvider = (input?: string): ProviderId | null => {\n if (!input) {\n return null;\n }\n\n const normalized = input.trim().toLowerCase();\n for (const [providerId, aliases] of Object.entries(PROVIDER_ALIASES)) {\n if (aliases.includes(normalized as ProviderAlias)) {\n return providerId as ProviderId;\n }\n }\n\n return null;\n};\n\nexport const getProviderAliases = (provider: ProviderId): ProviderAlias[] => {\n return PROVIDER_ALIASES[provider];\n};\n", "import type {\n AntigravityAccount,\n AntigravityAccountsFile,\n OpenCodeAuth,\n ProviderAuthData,\n} from '../../types.ts';\nimport { AUTH_PATHS, loadOpenCodeAuth, readJson } from '../common/files.ts';\nimport { getProviderAliases } from '../common/registry.ts';\n\nexport interface GoogleAuthContext {\n refreshToken?: string;\n accessToken?: string;\n expires?: number;\n projectId?: string;\n email?: string;\n}\n\nconst toAuthData = (entry: OpenCodeAuth[string]): ProviderAuthData | null => {\n if (!entry) {\n return null;\n }\n\n if (typeof entry === 'string') {\n return { token: entry };\n }\n\n if (typeof entry === 'object') {\n return entry as ProviderAuthData;\n }\n\n return null;\n};\n\nconst loadOpenCodeAuthEntry = async (): Promise<ProviderAuthData | null> => {\n const auth = await loadOpenCodeAuth();\n if (!auth) {\n return null;\n }\n\n for (const alias of getProviderAliases('google')) {\n const entry = toAuthData(auth[alias]);\n if (entry) {\n return entry;\n }\n }\n\n return null;\n};\n\nconst toAuthContext = (entry: ProviderAuthData | null): GoogleAuthContext | null => {\n if (!entry) {\n return null;\n }\n\n const accessToken = entry.access ?? entry.token;\n const refreshToken = entry.refresh;\n\n if (!accessToken && !refreshToken) {\n return null;\n }\n\n return {\n accessToken,\n refreshToken,\n expires: entry.expires,\n };\n};\n\nconst selectAccount = (accounts: AntigravityAccountsFile | null): AntigravityAccount | null => {\n if (!accounts?.accounts?.length) {\n return null;\n }\n\n const candidateIndex = accounts.activeIndex ?? 0;\n const account = accounts.accounts[candidateIndex] ?? accounts.accounts[0];\n return account ?? null;\n};\n\nconst loadAuthFromAccounts = async (): Promise<GoogleAuthContext | null> => {\n const configAccounts = await readJson<AntigravityAccountsFile>(AUTH_PATHS.antigravityConfig());\n const account = selectAccount(configAccounts);\n if (account) {\n return {\n refreshToken: account.refreshToken,\n projectId: account.projectId ?? account.managedProjectId,\n email: account.email,\n };\n }\n\n const dataAccounts = await readJson<AntigravityAccountsFile>(AUTH_PATHS.antigravityData());\n const fallbackAccount = selectAccount(dataAccounts);\n if (!fallbackAccount) {\n return null;\n }\n\n return {\n refreshToken: fallbackAccount.refreshToken,\n projectId: fallbackAccount.projectId ?? fallbackAccount.managedProjectId,\n email: fallbackAccount.email,\n };\n};\n\nexport const getGoogleAuth = async (): Promise<GoogleAuthContext | null> => {\n const openCodeAuth = await loadOpenCodeAuthEntry();\n const authContext = toAuthContext(openCodeAuth);\n if (authContext) {\n return authContext;\n }\n\n return loadAuthFromAccounts();\n};\n", "import type { ProviderResult, ProviderUsage, UsageWindow } from '../../types.ts';\nimport { calculateResetAfterSeconds, formatDuration, formatResetAt } from '../common/time.ts';\nimport { getGoogleAuth } from './auth.ts';\n\nconst GOOGLE_CLIENT_ID =\n '1071006060591-tmhssin2h21lcre235vtolojh4g403ep.apps.googleusercontent.com';\nconst GOOGLE_CLIENT_SECRET = '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';\nconst DEFAULT_PROJECT_ID = 'rising-fact-p41fc';\nconst WINDOW_SECONDS = 5 * 60 * 60;\n\nconst ENDPOINTS: readonly string[] = [\n 'https://daily-cloudcode-pa.sandbox.googleapis.com',\n 'https://autopush-cloudcode-pa.sandbox.googleapis.com',\n 'https://cloudcode-pa.googleapis.com',\n];\n\nconst HEADERS = {\n 'User-Agent': 'antigravity/1.11.5 windows/amd64',\n 'X-Goog-Api-Client': 'google-cloud-sdk vscode_cloudshelleditor/0.1',\n 'Client-Metadata':\n '{\"ideType\":\"IDE_UNSPECIFIED\",\"platform\":\"PLATFORM_UNSPECIFIED\",\"pluginType\":\"GEMINI\"}',\n} as const;\n\ninterface TokenResponse {\n access_token: string;\n expires_in?: number;\n}\n\ninterface ModelUsageInfoResponse {\n displayName?: string;\n quotaInfo?: {\n remainingFraction?: number;\n resetTime?: string;\n };\n}\n\ninterface ModelsResponse {\n models?: Record<string, ModelUsageInfoResponse>;\n}\n\nconst refreshAccessToken = async (refreshToken: string): Promise<TokenResponse | null> => {\n try {\n const response = await fetch('https://oauth2.googleapis.com/token', {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: new URLSearchParams({\n client_id: GOOGLE_CLIENT_ID,\n client_secret: GOOGLE_CLIENT_SECRET,\n refresh_token: refreshToken,\n grant_type: 'refresh_token',\n }),\n });\n\n if (!response.ok) {\n return null;\n }\n\n return (await response.json()) as TokenResponse;\n } catch {\n return null;\n }\n};\n\nconst fetchModels = async (\n accessToken: string,\n projectId?: string\n): Promise<ModelsResponse | null> => {\n const body = projectId ? { project: projectId } : {};\n\n for (const endpoint of ENDPOINTS) {\n try {\n const response = await fetch(`${endpoint}/v1internal:fetchAvailableModels`, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${accessToken}`,\n 'Content-Type': 'application/json',\n ...HEADERS,\n },\n body: JSON.stringify(body),\n signal: AbortSignal.timeout(15000),\n });\n\n if (response.ok) {\n return (await response.json()) as ModelsResponse;\n }\n } catch {\n continue;\n }\n }\n\n return null;\n};\n\nconst toWindow = (remainingFraction?: number, resetTime?: string): UsageWindow => {\n const remainingPercent =\n remainingFraction !== undefined ? Math.round(remainingFraction * 100) : null;\n const usedPercent = remainingPercent !== null ? Math.max(0, 100 - remainingPercent) : null;\n const resetAt = resetTime ? new Date(resetTime).getTime() : null;\n const resetAfterSeconds = calculateResetAfterSeconds(resetAt);\n\n return {\n usedPercent,\n remainingPercent,\n windowSeconds: WINDOW_SECONDS,\n resetAfterSeconds,\n resetAt,\n resetAtFormatted: resetAt ? formatResetAt(resetAt) : null,\n resetAfterFormatted: resetAfterSeconds !== null ? formatDuration(resetAfterSeconds) : null,\n };\n};\n\nconst buildUsage = (data: ModelsResponse): ProviderUsage => {\n const models: Record<string, { windows: Record<string, UsageWindow> }> = {};\n\n for (const [modelName, modelData] of Object.entries(data.models ?? {})) {\n const window = toWindow(modelData.quotaInfo?.remainingFraction, modelData.quotaInfo?.resetTime);\n models[modelName] = {\n windows: {\n '5h': window,\n },\n };\n }\n\n return {\n windows: {},\n models: Object.keys(models).length ? models : undefined,\n };\n};\n\nconst resolveAccessToken = async (\n refreshToken?: string,\n accessToken?: string,\n expires?: number\n): Promise<string | null> => {\n const now = Date.now();\n\n if (accessToken && (!expires || expires > now)) {\n return accessToken;\n }\n\n if (!refreshToken) {\n return null;\n }\n\n const refreshed = await refreshAccessToken(refreshToken);\n return refreshed?.access_token ?? null;\n};\n\nexport const fetchGoogleUsage = async (): Promise<ProviderResult> => {\n const auth = await getGoogleAuth();\n\n if (!auth) {\n return {\n provider: 'google',\n ok: false,\n configured: false,\n error: 'Not configured - no accounts found',\n usage: null,\n };\n }\n\n const accessToken = await resolveAccessToken(auth.refreshToken, auth.accessToken, auth.expires);\n\n if (!accessToken) {\n return {\n provider: 'google',\n ok: false,\n configured: true,\n error: 'Failed to refresh OAuth token',\n usage: null,\n };\n }\n\n const projectId = auth.projectId ?? DEFAULT_PROJECT_ID;\n const modelsData = await fetchModels(accessToken, projectId);\n\n if (!modelsData) {\n return {\n provider: 'google',\n ok: false,\n configured: true,\n error: 'Failed to fetch models from API',\n usage: null,\n };\n }\n\n return {\n provider: 'google',\n ok: true,\n configured: true,\n usage: buildUsage(modelsData),\n };\n};\n", "import type { OpenCodeAuth, ProviderAuthData } from '../../types.ts';\nimport { AUTH_PATHS, loadOpenCodeAuth, readJson } from '../common/files.ts';\nimport { getProviderAliases } from '../common/registry.ts';\n\nconst toAuthData = (entry: OpenCodeAuth[string]): ProviderAuthData | null => {\n if (!entry) {\n return null;\n }\n\n if (typeof entry === 'string') {\n return { token: entry };\n }\n\n if (typeof entry === 'object') {\n return entry as ProviderAuthData;\n }\n\n return null;\n};\n\nconst hasAccessToken = (auth: ProviderAuthData | null): auth is ProviderAuthData => {\n return Boolean(auth?.access || auth?.token);\n};\n\nconst loadOpenCodeAuthEntry = async (): Promise<ProviderAuthData | null> => {\n const auth = await loadOpenCodeAuth();\n if (!auth) {\n return null;\n }\n\n for (const alias of getProviderAliases('openai')) {\n const entry = toAuthData(auth[alias]);\n if (entry && hasAccessToken(entry)) {\n return entry;\n }\n }\n\n return null;\n};\n\nexport const getOpenaiAuth = async (): Promise<ProviderAuthData | null> => {\n const openCodeAuth = await loadOpenCodeAuthEntry();\n if (openCodeAuth && hasAccessToken(openCodeAuth)) {\n return openCodeAuth;\n }\n\n const pluginAuth = await readJson<ProviderAuthData>(AUTH_PATHS.openaiPlugin());\n if (pluginAuth && hasAccessToken(pluginAuth)) {\n return pluginAuth;\n }\n\n return null;\n};\n", "import type { ProviderResult, ProviderUsage, UsageWindow } from '../../types.ts';\nimport { calculateResetAfterSeconds, formatDuration, formatResetAt } from '../common/time.ts';\nimport { getOpenaiAuth } from './auth.ts';\n\ninterface OpenaiBackendWindow {\n used_percent: number;\n limit_window_seconds: number;\n reset_after_seconds: number;\n reset_at: number;\n}\n\ninterface OpenaiBackendResponse {\n plan_type: string;\n rate_limit: {\n allowed: boolean;\n limit_reached: boolean;\n primary_window?: OpenaiBackendWindow;\n secondary_window?: OpenaiBackendWindow;\n };\n}\n\nconst toWindow = (window?: OpenaiBackendWindow): UsageWindow | null => {\n if (!window) {\n return null;\n }\n\n const usedPercent = window.used_percent;\n const resetAt = window.reset_at ? window.reset_at * 1000 : null;\n const resetAfterSeconds = window.reset_after_seconds ?? calculateResetAfterSeconds(resetAt);\n\n return {\n usedPercent,\n remainingPercent: Math.max(0, 100 - usedPercent),\n windowSeconds: window.limit_window_seconds ?? null,\n resetAfterSeconds,\n resetAt,\n resetAtFormatted: resetAt ? formatResetAt(resetAt) : null,\n resetAfterFormatted: resetAfterSeconds !== null ? formatDuration(resetAfterSeconds) : null,\n };\n};\n\nexport const fetchOpenaiUsage = async (): Promise<ProviderResult> => {\n const auth = await getOpenaiAuth();\n\n if (!auth) {\n return {\n provider: 'openai',\n ok: false,\n configured: false,\n error: 'Not configured - no OAuth token found',\n usage: null,\n };\n }\n\n const accessToken = auth.access ?? auth.token;\n if (!accessToken) {\n return {\n provider: 'openai',\n ok: false,\n configured: false,\n error: 'Not configured - access token missing',\n usage: null,\n };\n }\n\n try {\n const response = await fetch('https://chatgpt.com/backend-api/wham/usage', {\n method: 'GET',\n headers: {\n Authorization: `Bearer ${accessToken}`,\n 'Content-Type': 'application/json',\n },\n });\n\n if (!response.ok) {\n return {\n provider: 'openai',\n ok: false,\n configured: true,\n error: `API error: ${response.status}`,\n usage: null,\n };\n }\n\n const payload = (await response.json()) as OpenaiBackendResponse;\n const primary = toWindow(payload.rate_limit.primary_window);\n const secondary = toWindow(payload.rate_limit.secondary_window);\n\n const windows: Record<string, UsageWindow> = {};\n if (primary) {\n windows['5h'] = primary;\n }\n if (secondary) {\n windows['weekly'] = secondary;\n }\n\n const usage: ProviderUsage = {\n windows,\n };\n\n return {\n provider: 'openai',\n ok: true,\n configured: true,\n usage,\n };\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return {\n provider: 'openai',\n ok: false,\n configured: true,\n error: `Request failed: ${message}`,\n usage: null,\n };\n }\n};\n", "import type { OpenCodeAuth } from '../../types.ts';\nimport { loadOpenCodeAuth } from '../common/files.ts';\nimport { getProviderAliases } from '../common/registry.ts';\n\nconst resolveAuthValue = (entry: OpenCodeAuth[string]): string | null => {\n if (!entry) {\n return null;\n }\n\n if (typeof entry === 'string') {\n return entry;\n }\n\n if (typeof entry === 'object') {\n return entry.api_key ?? entry.token ?? entry.key ?? null;\n }\n\n return null;\n};\n\nexport const getZaiApiKey = async (): Promise<string | null> => {\n if (process.env.ZAI_API_KEY) {\n return process.env.ZAI_API_KEY;\n }\n\n const auth = await loadOpenCodeAuth();\n if (!auth) {\n return null;\n }\n\n for (const alias of getProviderAliases('zai-coding-plan')) {\n const value = resolveAuthValue(auth[alias]);\n if (value) {\n return value;\n }\n }\n\n return null;\n};\n", "import type { ProviderResult, ProviderUsage, UsageWindow } from '../../types.ts';\nimport { calculateResetAfterSeconds, formatDuration, formatResetAt } from '../common/time.ts';\nimport { getZaiApiKey } from './auth.ts';\n\ninterface ZaiLimit {\n type: 'TIME_LIMIT' | 'TOKENS_LIMIT';\n unit: number;\n number: number;\n usage: number;\n currentValue: number;\n remaining: number;\n percentage: number;\n nextResetTime?: number;\n}\n\ninterface ZaiUsageResponse {\n code: number;\n msg: string;\n data?: {\n limits?: ZaiLimit[];\n };\n success: boolean;\n}\n\nconst normalizeTimestamp = (value: number): number => {\n return value < 1_000_000_000_000 ? value * 1000 : value;\n};\n\nconst TOKEN_WINDOW_SECONDS: Record<number, number> = {\n 3: 3600,\n};\n\nconst resolveWindowSeconds = (limit?: ZaiLimit): number | null => {\n if (!limit) {\n return null;\n }\n\n if (!limit.number) {\n return null;\n }\n\n const unitSeconds = TOKEN_WINDOW_SECONDS[limit.unit];\n if (!unitSeconds) {\n return null;\n }\n\n return unitSeconds * limit.number;\n};\n\nconst resolveWindowLabel = (windowSeconds: number | null): string => {\n if (!windowSeconds) {\n return 'tokens';\n }\n\n if (windowSeconds % 86400 === 0) {\n const days = windowSeconds / 86400;\n return days === 7 ? 'weekly' : `${days}d`;\n }\n\n if (windowSeconds % 3600 === 0) {\n return `${windowSeconds / 3600}h`;\n }\n\n return `${windowSeconds}s`;\n};\n\nconst toWindow = (limit?: ZaiLimit): UsageWindow | null => {\n if (!limit) {\n return null;\n }\n\n const usedPercent = limit.percentage ?? null;\n const remainingPercent = usedPercent !== null ? Math.max(0, 100 - usedPercent) : null;\n const resetAt = limit.nextResetTime ? normalizeTimestamp(limit.nextResetTime) : null;\n const resetAfterSeconds = calculateResetAfterSeconds(resetAt);\n\n return {\n usedPercent,\n remainingPercent,\n windowSeconds: resolveWindowSeconds(limit),\n resetAfterSeconds,\n resetAt,\n resetAtFormatted: resetAt ? formatResetAt(resetAt) : null,\n resetAfterFormatted: resetAfterSeconds !== null ? formatDuration(resetAfterSeconds) : null,\n };\n};\n\nexport const fetchZaiUsage = async (): Promise<ProviderResult> => {\n const apiKey = await getZaiApiKey();\n\n if (!apiKey) {\n return {\n provider: 'zai-coding-plan',\n ok: false,\n configured: false,\n error: 'Not configured - no API key found',\n usage: null,\n };\n }\n\n try {\n const response = await fetch('https://api.z.ai/api/monitor/usage/quota/limit', {\n method: 'GET',\n headers: {\n Authorization: `Bearer ${apiKey}`,\n 'Content-Type': 'application/json',\n },\n });\n\n if (!response.ok) {\n return {\n provider: 'zai-coding-plan',\n ok: false,\n configured: true,\n error: `API error: ${response.status}`,\n usage: null,\n };\n }\n\n const payload = (await response.json()) as ZaiUsageResponse;\n const limits = payload.data?.limits ?? [];\n const tokensLimit = limits.find((limit) => limit.type === 'TOKENS_LIMIT');\n\n const windows: Record<string, UsageWindow> = {};\n const window = toWindow(tokensLimit);\n if (window) {\n const label = resolveWindowLabel(window.windowSeconds);\n windows[label] = window;\n }\n\n const usage: ProviderUsage = {\n windows,\n };\n\n return {\n provider: 'zai-coding-plan',\n ok: true,\n configured: true,\n usage,\n };\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return {\n provider: 'zai-coding-plan',\n ok: false,\n configured: true,\n error: `Request failed: ${message}`,\n usage: null,\n };\n }\n};\n", "export type ProviderId = 'openai' | 'google' | 'zai-coding-plan';\n\nexport type ProviderAlias = ProviderId | 'codex' | 'antigravity' | 'zai' | 'z.ai' | 'chatgpt';\n\nexport const PROVIDERS: ProviderId[] = ['openai', 'google', 'zai-coding-plan'];\n\nexport interface UsageWindow {\n usedPercent: number | null;\n remainingPercent: number | null;\n windowSeconds: number | null;\n resetAfterSeconds: number | null;\n resetAt: number | null;\n resetAtFormatted: string | null;\n resetAfterFormatted: string | null;\n}\n\nexport interface UsageWindows {\n windows: Record<string, UsageWindow>;\n}\n\nexport interface ProviderUsage extends UsageWindows {\n models?: Record<string, UsageWindows>;\n}\n\nexport interface ProviderResult {\n provider: ProviderId;\n ok: boolean;\n configured: boolean;\n error?: string;\n usage: ProviderUsage | null;\n}\n\nexport type OpenCodeAuth = Record<string, string | ProviderAuthData>;\n\nexport interface ProviderAuthData {\n type?: 'oauth' | 'api' | string;\n access?: string;\n refresh?: string;\n expires?: number;\n api_key?: string;\n token?: string;\n key?: string;\n accountId?: string;\n}\n\nexport interface AntigravityAccount {\n email: string;\n refreshToken: string;\n projectId?: string;\n addedAt: number | string;\n lastUsed?: number;\n rateLimitResetTimes?: Record<string, number>;\n managedProjectId?: string;\n}\n\nexport interface AntigravityAccountsFile {\n version?: number;\n accounts: AntigravityAccount[];\n activeIndex?: number;\n activeIndexByFamily?: Record<string, number>;\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["readFile", "toAuthData", "loadOpenCodeAuthEntry", "toWindow", "toWindow", "readFile"]
7
+ }
package/package.json ADDED
@@ -0,0 +1,62 @@
1
+ {
2
+ "name": "opencode-usage-plugin",
3
+ "version": "0.0.1-dev",
4
+ "description": "OpenCode plugin to display subscription usages for OpenAI, Google, and z.ai",
5
+ "author": {
6
+ "name": "Nelson Pires",
7
+ "email": "nelsonpires.sn@gmail.com"
8
+ },
9
+ "type": "module",
10
+ "main": "./dist/index.js",
11
+ "exports": {
12
+ ".": {
13
+ "types": "./dist/index.d.ts",
14
+ "default": "./dist/index.js"
15
+ }
16
+ },
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "git+https://github.com/nelsonPires5/opencode-usage-plugin.git"
20
+ },
21
+ "keywords": [
22
+ "opencode",
23
+ "plugin",
24
+ "usage",
25
+ "chatgpt",
26
+ "codex",
27
+ "antigravity",
28
+ "zai",
29
+ "usage",
30
+ "monitoring"
31
+ ],
32
+ "publishConfig": {
33
+ "access": "public"
34
+ },
35
+ "files": [
36
+ "dist/index.js",
37
+ "dist/index.js.map",
38
+ "dist/index.d.ts",
39
+ "dist/command"
40
+ ],
41
+ "scripts": {
42
+ "build": "npx esbuild src/index.ts --bundle --platform=node --format=esm --outfile=dist/index.js --external:@opencode-ai/plugin --external:path --external:@types/node --sourcemap && npx tsc --emitDeclarationOnly && cp -r src/command dist/",
43
+ "prepublishOnly": "npm run build",
44
+ "test": "vitest run",
45
+ "test:watch": "vitest"
46
+ },
47
+ "dependencies": {
48
+ "@opencode-ai/plugin": "1.0.85"
49
+ },
50
+ "devDependencies": {
51
+ "@eslint/js": "^9.39.1",
52
+ "@types/node": "^20.11.5",
53
+ "@typescript-eslint/eslint-plugin": "8.47.0",
54
+ "@typescript-eslint/parser": "8.47.0",
55
+ "eslint": "^9.39.1",
56
+ "eslint-config-prettier": "10.1.8",
57
+ "eslint-plugin-prettier": "^5.1.3",
58
+ "prettier": "^3.2.4",
59
+ "typescript-eslint": "^8.47.0",
60
+ "vitest": "^3.2.4"
61
+ }
62
+ }