opencode-usage-plugin 0.0.2-dev4 → 0.0.2-dev5

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/dist/index.js CHANGED
@@ -1,671 +1,991 @@
1
- // src/index.ts
2
- import { tool } from "@opencode-ai/plugin";
3
-
4
- // src/providers/common/logger.ts
5
- var maskSecret = (secret) => {
6
- if (!secret || typeof secret !== "string" || secret.length <= 8) {
7
- return "***";
8
- }
9
- return `${secret.slice(0, 4)}...${secret.slice(-4)}`;
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropNames = Object.getOwnPropertyNames;
3
+ var __esm = (fn, res) => function __init() {
4
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
10
5
  };
11
- var noOpLogger = {
12
- debug: async () => {
13
- },
14
- info: async () => {
15
- },
16
- warn: async () => {
17
- },
18
- error: async () => {
19
- }
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
20
9
  };
21
- var createLogger = (client) => {
22
- const service = "opencode-usage";
23
- return {
24
- debug: async (message, extra) => {
25
- await client.app.log({
26
- service,
27
- level: "debug",
28
- message,
29
- ...extra ?? {}
30
- });
31
- },
32
- info: async (message, extra) => {
33
- await client.app.log({
34
- service,
35
- level: "info",
36
- message,
37
- ...extra ?? {}
38
- });
39
- },
40
- warn: async (message, extra) => {
41
- await client.app.log({
42
- service,
43
- level: "warn",
44
- message,
45
- ...extra ?? {}
46
- });
47
- },
48
- error: async (message, extra) => {
49
- await client.app.log({
50
- service,
51
- level: "error",
52
- message,
53
- ...extra ?? {}
54
- });
55
- }
56
- };
57
- };
58
- var noopLogger = noOpLogger;
59
10
 
60
- // src/providers/common/time.ts
61
- var calculateResetAfterSeconds = (resetAt, now = Date.now()) => {
62
- if (!resetAt) {
63
- return null;
64
- }
65
- const diffMs = resetAt - now;
66
- if (diffMs <= 0) {
67
- return 0;
11
+ // src/providers/common/logger.ts
12
+ var logger_exports = {};
13
+ __export(logger_exports, {
14
+ createLogger: () => createLogger,
15
+ maskSecret: () => maskSecret,
16
+ noopLogger: () => noopLogger
17
+ });
18
+ var maskSecret, noOpLogger, createLogger, noopLogger;
19
+ var init_logger = __esm({
20
+ "src/providers/common/logger.ts"() {
21
+ "use strict";
22
+ maskSecret = (secret) => {
23
+ if (!secret || typeof secret !== "string" || secret.length <= 8) {
24
+ return "***";
25
+ }
26
+ return `${secret.slice(0, 4)}...${secret.slice(-4)}`;
27
+ };
28
+ noOpLogger = {
29
+ debug: async () => {
30
+ },
31
+ info: async () => {
32
+ },
33
+ warn: async () => {
34
+ },
35
+ error: async () => {
36
+ }
37
+ };
38
+ createLogger = (client) => {
39
+ const service = "opencode-usage";
40
+ return {
41
+ debug: async (message, extra) => {
42
+ await client.app.log({
43
+ service,
44
+ level: "debug",
45
+ message,
46
+ ...extra ?? {}
47
+ });
48
+ },
49
+ info: async (message, extra) => {
50
+ await client.app.log({
51
+ service,
52
+ level: "info",
53
+ message,
54
+ ...extra ?? {}
55
+ });
56
+ },
57
+ warn: async (message, extra) => {
58
+ await client.app.log({
59
+ service,
60
+ level: "warn",
61
+ message,
62
+ ...extra ?? {}
63
+ });
64
+ },
65
+ error: async (message, extra) => {
66
+ await client.app.log({
67
+ service,
68
+ level: "error",
69
+ message,
70
+ ...extra ?? {}
71
+ });
72
+ }
73
+ };
74
+ };
75
+ noopLogger = noOpLogger;
68
76
  }
69
- return Math.floor(diffMs / 1e3);
70
- };
71
- var formatDuration = (seconds) => {
72
- if (seconds <= 0) {
73
- return "0s";
74
- }
75
- const weeks = Math.floor(seconds / 604800);
76
- const days = Math.floor(seconds % 604800 / 86400);
77
- const hours = Math.floor(seconds % 86400 / 3600);
78
- const minutes = Math.floor(seconds % 3600 / 60);
79
- const secs = seconds % 60;
80
- const parts = [];
81
- if (weeks > 0) parts.push(`${weeks}w`);
82
- if (days > 0) parts.push(`${days}d`);
83
- if (hours > 0) parts.push(`${hours}h`);
84
- if (minutes > 0) parts.push(`${minutes}m`);
85
- if (secs > 0 || parts.length === 0) parts.push(`${secs}s`);
86
- return parts.join(" ");
87
- };
88
- var formatResetAt = (resetAtMs) => {
89
- return new Date(resetAtMs).toLocaleString(void 0, {
90
- weekday: "long",
91
- year: "numeric",
92
- month: "long",
93
- day: "numeric",
94
- hour: "numeric",
95
- minute: "numeric",
96
- second: "numeric",
97
- timeZoneName: "short"
98
- });
99
- };
77
+ });
100
78
 
101
79
  // src/providers/common/files.ts
102
80
  import { readFile } from "node:fs/promises";
103
81
  import { homedir } from "node:os";
104
82
  import { join } from "node:path";
105
- var xdgDataHome = () => process.env.XDG_DATA_HOME ?? join(homedir(), ".local", "share");
106
- var xdgConfigHome = () => process.env.XDG_CONFIG_HOME ?? join(homedir(), ".config");
107
- var AUTH_PATHS = {
108
- opencode: () => join(xdgDataHome(), "opencode", "auth.json"),
109
- openaiPlugin: () => join(homedir(), ".opencode", "auth", "openai.json"),
110
- antigravityConfig: () => join(xdgConfigHome(), "opencode", "antigravity-accounts.json"),
111
- antigravityData: () => join(xdgDataHome(), "opencode", "antigravity-accounts.json")
112
- };
113
- var readJson = async (filePath, logger) => {
114
- try {
115
- const content = await readFile(filePath, "utf-8");
116
- return JSON.parse(content);
117
- } catch (error) {
118
- if (logger) {
119
- const message = error instanceof Error ? error.message : String(error);
120
- await logger.debug(`Auth file not found or invalid: ${filePath}`, { error: message });
121
- }
122
- return null;
83
+ var xdgDataHome, xdgConfigHome, xdgCacheHome, AUTH_PATHS, readJson, loadOpenCodeAuth;
84
+ var init_files = __esm({
85
+ "src/providers/common/files.ts"() {
86
+ "use strict";
87
+ xdgDataHome = () => process.env.XDG_DATA_HOME ?? join(homedir(), ".local", "share");
88
+ xdgConfigHome = () => process.env.XDG_CONFIG_HOME ?? join(homedir(), ".config");
89
+ xdgCacheHome = () => process.env.XDG_CACHE_HOME ?? join(homedir(), ".cache");
90
+ AUTH_PATHS = {
91
+ opencode: () => join(xdgDataHome(), "opencode", "auth.json"),
92
+ openaiPlugin: () => join(homedir(), ".opencode", "auth", "openai.json"),
93
+ antigravityConfig: () => join(xdgConfigHome(), "opencode", "antigravity-accounts.json"),
94
+ antigravityData: () => join(xdgDataHome(), "opencode", "antigravity-accounts.json")
95
+ };
96
+ readJson = async (filePath, logger) => {
97
+ try {
98
+ const content = await readFile(filePath, "utf-8");
99
+ return JSON.parse(content);
100
+ } catch (error) {
101
+ if (logger) {
102
+ const message = error instanceof Error ? error.message : String(error);
103
+ await logger.debug(`Auth file not found or invalid: ${filePath}`, { error: message });
104
+ }
105
+ return null;
106
+ }
107
+ };
108
+ loadOpenCodeAuth = async (logger) => {
109
+ return readJson(AUTH_PATHS.opencode(), logger);
110
+ };
123
111
  }
124
- };
125
- var loadOpenCodeAuth = async (logger) => {
126
- return readJson(AUTH_PATHS.opencode(), logger);
127
- };
128
-
129
- // src/providers/common/registry.ts
130
- var PROVIDER_ALIASES = {
131
- openai: ["openai", "codex", "chatgpt"],
132
- google: ["google", "antigravity"],
133
- "zai-coding-plan": ["zai-coding-plan", "zai", "z.ai"]
134
- };
135
- var getProviderAliases = (provider) => {
136
- return PROVIDER_ALIASES[provider];
137
- };
112
+ });
138
113
 
139
- // src/providers/google/auth.ts
140
- var toAuthData = (entry) => {
141
- if (!entry) {
142
- return null;
143
- }
144
- if (typeof entry === "string") {
145
- return { token: entry };
146
- }
147
- if (typeof entry === "object") {
148
- return entry;
149
- }
150
- return null;
151
- };
152
- var loadOpenCodeAuthEntry = async (logger) => {
153
- const auth = await loadOpenCodeAuth(logger);
154
- if (!auth) {
155
- return null;
156
- }
157
- for (const alias of getProviderAliases("google")) {
158
- const entry = toAuthData(auth[alias]);
159
- if (entry) {
160
- return entry;
161
- }
162
- }
163
- return null;
164
- };
165
- var toAuthContext = (entry) => {
166
- if (!entry) {
167
- return null;
168
- }
169
- const accessToken = entry.access ?? entry.token;
170
- let refreshToken = entry.refresh;
171
- let projectId = void 0;
172
- if (refreshToken && refreshToken.includes("|")) {
173
- const parts = refreshToken.split("|");
174
- refreshToken = parts[0];
175
- projectId = parts[1];
176
- }
177
- if (!accessToken && !refreshToken) {
178
- return null;
179
- }
180
- return {
181
- accessToken,
182
- refreshToken,
183
- expires: entry.expires,
184
- projectId
185
- };
186
- };
187
- var selectAccount = (accounts) => {
188
- if (!accounts?.accounts?.length) {
189
- return null;
114
+ // src/cache/file.ts
115
+ import { mkdir, readFile as readFile2, rename, unlink, writeFile } from "node:fs/promises";
116
+ var CACHE_DIR, CACHE_PATH, TMP_PATH, ensureCacheDir, readCache, writeCache;
117
+ var init_file = __esm({
118
+ "src/cache/file.ts"() {
119
+ "use strict";
120
+ init_files();
121
+ CACHE_DIR = () => `${xdgCacheHome()}/opencode/opencode-usage-plugin`;
122
+ CACHE_PATH = () => `${CACHE_DIR()}/usage.json`;
123
+ TMP_PATH = () => `${CACHE_PATH()}.tmp`;
124
+ ensureCacheDir = async () => {
125
+ try {
126
+ await mkdir(CACHE_DIR(), { recursive: true });
127
+ } catch (error) {
128
+ if (error instanceof Error && "code" in error && error.code !== "EEXIST") {
129
+ throw error;
130
+ }
131
+ }
132
+ };
133
+ readCache = async (logger) => {
134
+ try {
135
+ const content = await readFile2(CACHE_PATH(), "utf-8");
136
+ const parsed = JSON.parse(content);
137
+ if (typeof parsed === "object" && parsed !== null && "schema_version" in parsed && "updated_at" in parsed && "refresh_interval_seconds" in parsed && "providers" in parsed) {
138
+ return parsed;
139
+ }
140
+ await logger?.warn("Invalid cache schema, returning null");
141
+ return null;
142
+ } catch (error) {
143
+ if (logger) {
144
+ const message = error instanceof Error ? error.message : String(error);
145
+ await logger.debug("Cache file not found or invalid", { error: message });
146
+ }
147
+ return null;
148
+ }
149
+ };
150
+ writeCache = async (cache, logger) => {
151
+ try {
152
+ await ensureCacheDir();
153
+ const content = JSON.stringify(cache, null, 2);
154
+ await writeFile(TMP_PATH(), content, "utf-8");
155
+ await rename(TMP_PATH(), CACHE_PATH());
156
+ await logger?.debug("Cache written successfully", { path: CACHE_PATH() });
157
+ } catch (error) {
158
+ await logger?.error("Failed to write cache", {
159
+ error: error instanceof Error ? error.message : String(error)
160
+ });
161
+ throw error;
162
+ }
163
+ };
190
164
  }
191
- const candidateIndex = accounts.activeIndex ?? 0;
192
- const account = accounts.accounts[candidateIndex] ?? accounts.accounts[0];
193
- return account ?? null;
194
- };
195
- var loadAuthFromAccounts = async (logger) => {
196
- const configAccounts = await readJson(
197
- AUTH_PATHS.antigravityConfig(),
198
- logger
199
- );
200
- const account = selectAccount(configAccounts);
201
- if (account) {
202
- return {
203
- refreshToken: account.refreshToken,
204
- projectId: account.projectId ?? account.managedProjectId,
205
- email: account.email
165
+ });
166
+
167
+ // src/cache/types.ts
168
+ var SCHEMA_VERSION, REFRESH_INTERVAL_SECONDS, STALE_THRESHOLD_MULTIPLIER;
169
+ var init_types = __esm({
170
+ "src/cache/types.ts"() {
171
+ "use strict";
172
+ SCHEMA_VERSION = 1;
173
+ REFRESH_INTERVAL_SECONDS = 300;
174
+ STALE_THRESHOLD_MULTIPLIER = 2;
175
+ }
176
+ });
177
+
178
+ // src/cache/reader.ts
179
+ var reader_exports = {};
180
+ __export(reader_exports, {
181
+ loadCacheForDisplay: () => loadCacheForDisplay
182
+ });
183
+ var loadCacheForDisplay;
184
+ var init_reader = __esm({
185
+ "src/cache/reader.ts"() {
186
+ "use strict";
187
+ init_file();
188
+ init_types();
189
+ loadCacheForDisplay = async (logger) => {
190
+ const cache = await readCache(logger);
191
+ if (!cache) {
192
+ await logger.warn("No cache available");
193
+ return null;
194
+ }
195
+ const updatedAt = new Date(cache.updated_at);
196
+ const now = /* @__PURE__ */ new Date();
197
+ const staleThresholdMs = REFRESH_INTERVAL_SECONDS * 1e3 * STALE_THRESHOLD_MULTIPLIER;
198
+ const isStale = now.getTime() - updatedAt.getTime() > staleThresholdMs;
199
+ if (isStale) {
200
+ await logger.debug("Cache is stale", {
201
+ updatedAt: cache.updated_at,
202
+ staleThresholdSeconds: REFRESH_INTERVAL_SECONDS * STALE_THRESHOLD_MULTIPLIER
203
+ });
204
+ }
205
+ const configuredProviders = {};
206
+ for (const [providerId, entry] of Object.entries(cache.providers)) {
207
+ if (entry.configured) {
208
+ configuredProviders[providerId] = entry;
209
+ }
210
+ }
211
+ return {
212
+ providers: configuredProviders,
213
+ updatedAt: cache.updated_at,
214
+ isStale
215
+ };
206
216
  };
207
217
  }
208
- const dataAccounts = await readJson(
209
- AUTH_PATHS.antigravityData(),
210
- logger
211
- );
212
- const fallbackAccount = selectAccount(dataAccounts);
213
- if (!fallbackAccount) {
214
- return null;
218
+ });
219
+
220
+ // src/providers/common/time.ts
221
+ var calculateResetAfterSeconds, formatDuration, formatResetAt;
222
+ var init_time = __esm({
223
+ "src/providers/common/time.ts"() {
224
+ "use strict";
225
+ calculateResetAfterSeconds = (resetAt, now = Date.now()) => {
226
+ if (!resetAt) {
227
+ return null;
228
+ }
229
+ const diffMs = resetAt - now;
230
+ if (diffMs <= 0) {
231
+ return 0;
232
+ }
233
+ return Math.floor(diffMs / 1e3);
234
+ };
235
+ formatDuration = (seconds) => {
236
+ if (seconds <= 0) {
237
+ return "0s";
238
+ }
239
+ const weeks = Math.floor(seconds / 604800);
240
+ const days = Math.floor(seconds % 604800 / 86400);
241
+ const hours = Math.floor(seconds % 86400 / 3600);
242
+ const minutes = Math.floor(seconds % 3600 / 60);
243
+ const secs = seconds % 60;
244
+ const parts = [];
245
+ if (weeks > 0) parts.push(`${weeks}w`);
246
+ if (days > 0) parts.push(`${days}d`);
247
+ if (hours > 0) parts.push(`${hours}h`);
248
+ if (minutes > 0) parts.push(`${minutes}m`);
249
+ if (secs > 0 || parts.length === 0) parts.push(`${secs}s`);
250
+ return parts.join(" ");
251
+ };
252
+ formatResetAt = (resetAtMs) => {
253
+ return new Date(resetAtMs).toLocaleString(void 0, {
254
+ weekday: "long",
255
+ year: "numeric",
256
+ month: "long",
257
+ day: "numeric",
258
+ hour: "numeric",
259
+ minute: "numeric",
260
+ second: "numeric",
261
+ timeZoneName: "short"
262
+ });
263
+ };
215
264
  }
216
- return {
217
- refreshToken: fallbackAccount.refreshToken,
218
- projectId: fallbackAccount.projectId ?? fallbackAccount.managedProjectId,
219
- email: fallbackAccount.email
220
- };
221
- };
222
- var getGoogleAuth = async (logger) => {
223
- const openCodeAuth = await loadOpenCodeAuthEntry(logger);
224
- const authContext = toAuthContext(openCodeAuth);
225
- if (authContext) {
226
- return authContext;
265
+ });
266
+
267
+ // src/providers/common/registry.ts
268
+ var PROVIDER_ALIASES, getProviderAliases;
269
+ var init_registry = __esm({
270
+ "src/providers/common/registry.ts"() {
271
+ "use strict";
272
+ PROVIDER_ALIASES = {
273
+ openai: ["openai", "codex", "chatgpt"],
274
+ google: ["google", "antigravity"],
275
+ "zai-coding-plan": ["zai-coding-plan", "zai", "z.ai"]
276
+ };
277
+ getProviderAliases = (provider) => {
278
+ return PROVIDER_ALIASES[provider];
279
+ };
227
280
  }
228
- return loadAuthFromAccounts(logger);
229
- };
281
+ });
230
282
 
231
- // src/providers/google/fetch.ts
232
- var GOOGLE_CLIENT_ID = "1071006060591-tmhssin2h21lcre235vtolojh4g403ep.apps.googleusercontent.com";
233
- var GOOGLE_CLIENT_SECRET = "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";
234
- var DEFAULT_PROJECT_ID = "rising-fact-p41fc";
235
- var WINDOW_SECONDS = 5 * 60 * 60;
236
- var ENDPOINTS = [
237
- "https://daily-cloudcode-pa.sandbox.googleapis.com",
238
- "https://autopush-cloudcode-pa.sandbox.googleapis.com",
239
- "https://cloudcode-pa.googleapis.com"
240
- ];
241
- var HEADERS = {
242
- "User-Agent": "antigravity/1.11.5 windows/amd64",
243
- "X-Goog-Api-Client": "google-cloud-sdk vscode_cloudshelleditor/0.1",
244
- "Client-Metadata": '{"ideType":"IDE_UNSPECIFIED","platform":"PLATFORM_UNSPECIFIED","pluginType":"GEMINI"}'
245
- };
246
- var refreshAccessToken = async (refreshToken, logger) => {
247
- try {
248
- const response = await fetch("https://oauth2.googleapis.com/token", {
249
- method: "POST",
250
- headers: { "Content-Type": "application/x-www-form-urlencoded" },
251
- body: new URLSearchParams({
252
- client_id: GOOGLE_CLIENT_ID,
253
- client_secret: GOOGLE_CLIENT_SECRET,
254
- refresh_token: refreshToken,
255
- grant_type: "refresh_token"
256
- })
257
- });
258
- if (!response.ok) {
259
- await logger.warn("Failed to refresh OAuth token for google", {
260
- status: response.status,
261
- token: maskSecret(refreshToken)
262
- });
283
+ // src/providers/google/auth.ts
284
+ var toAuthData, loadOpenCodeAuthEntry, toAuthContext, selectAccount, loadAuthFromAccounts, getGoogleAuth;
285
+ var init_auth = __esm({
286
+ "src/providers/google/auth.ts"() {
287
+ "use strict";
288
+ init_files();
289
+ init_registry();
290
+ toAuthData = (entry) => {
291
+ if (!entry) {
292
+ return null;
293
+ }
294
+ if (typeof entry === "string") {
295
+ return { token: entry };
296
+ }
297
+ if (typeof entry === "object") {
298
+ return entry;
299
+ }
263
300
  return null;
264
- }
265
- return await response.json();
266
- } catch (error) {
267
- const message = error instanceof Error ? error.message : String(error);
268
- await logger.warn(`Token refresh failed for google: ${message}`);
269
- return null;
301
+ };
302
+ loadOpenCodeAuthEntry = async (logger) => {
303
+ const auth = await loadOpenCodeAuth(logger);
304
+ if (!auth) {
305
+ return null;
306
+ }
307
+ for (const alias of getProviderAliases("google")) {
308
+ const entry = toAuthData(auth[alias]);
309
+ if (entry) {
310
+ return entry;
311
+ }
312
+ }
313
+ return null;
314
+ };
315
+ toAuthContext = (entry) => {
316
+ if (!entry) {
317
+ return null;
318
+ }
319
+ const accessToken = entry.access ?? entry.token;
320
+ let refreshToken = entry.refresh;
321
+ let projectId = void 0;
322
+ if (refreshToken && refreshToken.includes("|")) {
323
+ const parts = refreshToken.split("|");
324
+ refreshToken = parts[0];
325
+ projectId = parts[1];
326
+ }
327
+ if (!accessToken && !refreshToken) {
328
+ return null;
329
+ }
330
+ return {
331
+ accessToken,
332
+ refreshToken,
333
+ expires: entry.expires,
334
+ projectId
335
+ };
336
+ };
337
+ selectAccount = (accounts) => {
338
+ if (!accounts?.accounts?.length) {
339
+ return null;
340
+ }
341
+ const candidateIndex = accounts.activeIndex ?? 0;
342
+ const account = accounts.accounts[candidateIndex] ?? accounts.accounts[0];
343
+ return account ?? null;
344
+ };
345
+ loadAuthFromAccounts = async (logger) => {
346
+ const configAccounts = await readJson(
347
+ AUTH_PATHS.antigravityConfig(),
348
+ logger
349
+ );
350
+ const account = selectAccount(configAccounts);
351
+ if (account) {
352
+ return {
353
+ refreshToken: account.refreshToken,
354
+ projectId: account.projectId ?? account.managedProjectId,
355
+ email: account.email
356
+ };
357
+ }
358
+ const dataAccounts = await readJson(
359
+ AUTH_PATHS.antigravityData(),
360
+ logger
361
+ );
362
+ const fallbackAccount = selectAccount(dataAccounts);
363
+ if (!fallbackAccount) {
364
+ return null;
365
+ }
366
+ return {
367
+ refreshToken: fallbackAccount.refreshToken,
368
+ projectId: fallbackAccount.projectId ?? fallbackAccount.managedProjectId,
369
+ email: fallbackAccount.email
370
+ };
371
+ };
372
+ getGoogleAuth = async (logger) => {
373
+ const openCodeAuth = await loadOpenCodeAuthEntry(logger);
374
+ const authContext = toAuthContext(openCodeAuth);
375
+ if (authContext) {
376
+ return authContext;
377
+ }
378
+ return loadAuthFromAccounts(logger);
379
+ };
270
380
  }
271
- };
272
- var fetchModels = async (accessToken, projectId, logger) => {
273
- const body = projectId ? { project: projectId } : {};
274
- for (const endpoint of ENDPOINTS) {
275
- try {
276
- const response = await fetch(`${endpoint}/v1internal:fetchAvailableModels`, {
277
- method: "POST",
278
- headers: {
279
- Authorization: `Bearer ${accessToken}`,
280
- "Content-Type": "application/json",
281
- ...HEADERS
282
- },
283
- body: JSON.stringify(body),
284
- signal: AbortSignal.timeout(15e3)
285
- });
286
- if (response.ok) {
287
- await logger.debug(`Fetched models from ${endpoint}`, { projectId });
381
+ });
382
+
383
+ // src/providers/google/fetch.ts
384
+ var GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, DEFAULT_PROJECT_ID, WINDOW_SECONDS, ENDPOINTS, HEADERS, refreshAccessToken, fetchModels, toWindow, buildUsage, resolveAccessToken, fetchGoogleUsage;
385
+ var init_fetch = __esm({
386
+ "src/providers/google/fetch.ts"() {
387
+ "use strict";
388
+ init_logger();
389
+ init_time();
390
+ init_auth();
391
+ GOOGLE_CLIENT_ID = "1071006060591-tmhssin2h21lcre235vtolojh4g403ep.apps.googleusercontent.com";
392
+ GOOGLE_CLIENT_SECRET = "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";
393
+ DEFAULT_PROJECT_ID = "rising-fact-p41fc";
394
+ WINDOW_SECONDS = 5 * 60 * 60;
395
+ ENDPOINTS = [
396
+ "https://daily-cloudcode-pa.sandbox.googleapis.com",
397
+ "https://autopush-cloudcode-pa.sandbox.googleapis.com",
398
+ "https://cloudcode-pa.googleapis.com"
399
+ ];
400
+ HEADERS = {
401
+ "User-Agent": "antigravity/1.11.5 windows/amd64",
402
+ "X-Goog-Api-Client": "google-cloud-sdk vscode_cloudshelleditor/0.1",
403
+ "Client-Metadata": '{"ideType":"IDE_UNSPECIFIED","platform":"PLATFORM_UNSPECIFIED","pluginType":"GEMINI"}'
404
+ };
405
+ refreshAccessToken = async (refreshToken, logger) => {
406
+ try {
407
+ const response = await fetch("https://oauth2.googleapis.com/token", {
408
+ method: "POST",
409
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
410
+ body: new URLSearchParams({
411
+ client_id: GOOGLE_CLIENT_ID,
412
+ client_secret: GOOGLE_CLIENT_SECRET,
413
+ refresh_token: refreshToken,
414
+ grant_type: "refresh_token"
415
+ })
416
+ });
417
+ if (!response.ok) {
418
+ await logger.warn("Failed to refresh OAuth token for google", {
419
+ status: response.status,
420
+ token: maskSecret(refreshToken)
421
+ });
422
+ return null;
423
+ }
288
424
  return await response.json();
425
+ } catch (error) {
426
+ const message = error instanceof Error ? error.message : String(error);
427
+ await logger.warn(`Token refresh failed for google: ${message}`);
428
+ return null;
289
429
  }
290
- } catch {
291
- continue;
292
- }
293
- }
294
- await logger.error("Failed to fetch models from all google endpoints", { projectId });
295
- return null;
296
- };
297
- var toWindow = (remainingFraction, resetTime) => {
298
- const remainingPercent = remainingFraction !== void 0 ? Math.round(remainingFraction * 100) : null;
299
- const usedPercent = remainingPercent !== null ? Math.max(0, 100 - remainingPercent) : null;
300
- const resetAt = resetTime ? new Date(resetTime).getTime() : null;
301
- const resetAfterSeconds = calculateResetAfterSeconds(resetAt);
302
- return {
303
- usedPercent,
304
- remainingPercent,
305
- windowSeconds: WINDOW_SECONDS,
306
- resetAfterSeconds,
307
- resetAt,
308
- resetAtFormatted: resetAt ? formatResetAt(resetAt) : null,
309
- resetAfterFormatted: resetAfterSeconds !== null ? formatDuration(resetAfterSeconds) : null
310
- };
311
- };
312
- var buildUsage = (data) => {
313
- const models = {};
314
- for (const [modelName, modelData] of Object.entries(data.models ?? {})) {
315
- const window = toWindow(modelData.quotaInfo?.remainingFraction, modelData.quotaInfo?.resetTime);
316
- models[modelName] = {
317
- windows: {
318
- "5h": window
430
+ };
431
+ fetchModels = async (accessToken, projectId, logger) => {
432
+ const body = projectId ? { project: projectId } : {};
433
+ for (const endpoint of ENDPOINTS) {
434
+ try {
435
+ const response = await fetch(`${endpoint}/v1internal:fetchAvailableModels`, {
436
+ method: "POST",
437
+ headers: {
438
+ Authorization: `Bearer ${accessToken}`,
439
+ "Content-Type": "application/json",
440
+ ...HEADERS
441
+ },
442
+ body: JSON.stringify(body),
443
+ signal: AbortSignal.timeout(15e3)
444
+ });
445
+ if (response.ok) {
446
+ await logger.debug(`Fetched models from ${endpoint}`, { projectId });
447
+ return await response.json();
448
+ }
449
+ } catch {
450
+ continue;
451
+ }
319
452
  }
453
+ await logger.error("Failed to fetch models from all google endpoints", { projectId });
454
+ return null;
320
455
  };
321
- }
322
- return {
323
- windows: {},
324
- models: Object.keys(models).length ? models : void 0
325
- };
326
- };
327
- var resolveAccessToken = async (refreshToken, accessToken, expires, logger) => {
328
- const now = Date.now();
329
- if (accessToken && (!expires || expires > now)) {
330
- return accessToken;
331
- }
332
- if (!refreshToken) {
333
- return null;
334
- }
335
- const refreshed = await refreshAccessToken(refreshToken, logger);
336
- return refreshed?.access_token ?? null;
337
- };
338
- var fetchGoogleUsage = async (logger = noopLogger) => {
339
- const auth = await getGoogleAuth(logger);
340
- if (!auth) {
341
- await logger.warn("No auth configured for google");
342
- return {
343
- provider: "google",
344
- ok: false,
345
- configured: false,
346
- error: "Not configured - no accounts found",
347
- usage: null
348
- };
349
- }
350
- const accessToken = await resolveAccessToken(
351
- auth.refreshToken,
352
- auth.accessToken,
353
- auth.expires,
354
- logger
355
- );
356
- if (!accessToken) {
357
- await logger.warn("Failed to refresh OAuth token for google", { email: auth.email });
358
- return {
359
- provider: "google",
360
- ok: false,
361
- configured: true,
362
- error: "Failed to refresh OAuth token",
363
- usage: null
456
+ toWindow = (remainingFraction, resetTime) => {
457
+ const remainingPercent = remainingFraction !== void 0 ? Math.round(remainingFraction * 100) : null;
458
+ const usedPercent = remainingPercent !== null ? Math.max(0, 100 - remainingPercent) : null;
459
+ const resetAt = resetTime ? new Date(resetTime).getTime() : null;
460
+ const resetAfterSeconds = calculateResetAfterSeconds(resetAt);
461
+ return {
462
+ usedPercent,
463
+ remainingPercent,
464
+ windowSeconds: WINDOW_SECONDS,
465
+ resetAfterSeconds,
466
+ resetAt,
467
+ resetAtFormatted: resetAt ? formatResetAt(resetAt) : null,
468
+ resetAfterFormatted: resetAfterSeconds !== null ? formatDuration(resetAfterSeconds) : null
469
+ };
364
470
  };
365
- }
366
- const projectId = auth.projectId ?? DEFAULT_PROJECT_ID;
367
- const modelsData = await fetchModels(accessToken, projectId, logger);
368
- if (!modelsData) {
369
- await logger.error("Failed to fetch models from google API", { projectId });
370
- return {
371
- provider: "google",
372
- ok: false,
373
- configured: true,
374
- error: "Failed to fetch models from API",
375
- usage: null
471
+ buildUsage = (data) => {
472
+ const models = {};
473
+ for (const [modelName, modelData] of Object.entries(data.models ?? {})) {
474
+ const window = toWindow(modelData.quotaInfo?.remainingFraction, modelData.quotaInfo?.resetTime);
475
+ models[modelName] = {
476
+ windows: {
477
+ "5h": window
478
+ }
479
+ };
480
+ }
481
+ return {
482
+ windows: {},
483
+ models: Object.keys(models).length ? models : void 0
484
+ };
485
+ };
486
+ resolveAccessToken = async (refreshToken, accessToken, expires, logger) => {
487
+ const now = Date.now();
488
+ if (accessToken && (!expires || expires > now)) {
489
+ return accessToken;
490
+ }
491
+ if (!refreshToken) {
492
+ return null;
493
+ }
494
+ const refreshed = await refreshAccessToken(refreshToken, logger);
495
+ return refreshed?.access_token ?? null;
496
+ };
497
+ fetchGoogleUsage = async (logger = noopLogger) => {
498
+ const auth = await getGoogleAuth(logger);
499
+ if (!auth) {
500
+ await logger.warn("No auth configured for google");
501
+ return {
502
+ provider: "google",
503
+ ok: false,
504
+ configured: false,
505
+ error: "Not configured - no accounts found",
506
+ usage: null
507
+ };
508
+ }
509
+ const accessToken = await resolveAccessToken(
510
+ auth.refreshToken,
511
+ auth.accessToken,
512
+ auth.expires,
513
+ logger
514
+ );
515
+ if (!accessToken) {
516
+ await logger.warn("Failed to refresh OAuth token for google", { email: auth.email });
517
+ return {
518
+ provider: "google",
519
+ ok: false,
520
+ configured: true,
521
+ error: "Failed to refresh OAuth token",
522
+ usage: null
523
+ };
524
+ }
525
+ const projectId = auth.projectId ?? DEFAULT_PROJECT_ID;
526
+ const modelsData = await fetchModels(accessToken, projectId, logger);
527
+ if (!modelsData) {
528
+ await logger.error("Failed to fetch models from google API", { projectId });
529
+ return {
530
+ provider: "google",
531
+ ok: false,
532
+ configured: true,
533
+ error: "Failed to fetch models from API",
534
+ usage: null
535
+ };
536
+ }
537
+ await logger.info("google usage fetched successfully");
538
+ return {
539
+ provider: "google",
540
+ ok: true,
541
+ configured: true,
542
+ usage: buildUsage(modelsData)
543
+ };
376
544
  };
377
545
  }
378
- await logger.info("google usage fetched successfully");
379
- return {
380
- provider: "google",
381
- ok: true,
382
- configured: true,
383
- usage: buildUsage(modelsData)
384
- };
385
- };
546
+ });
386
547
 
387
548
  // src/providers/openai/auth.ts
388
- var toAuthData2 = (entry) => {
389
- if (!entry) {
390
- return null;
391
- }
392
- if (typeof entry === "string") {
393
- return { token: entry };
394
- }
395
- if (typeof entry === "object") {
396
- return entry;
397
- }
398
- return null;
399
- };
400
- var hasAccessToken = (auth) => {
401
- return Boolean(auth?.access || auth?.token);
402
- };
403
- var loadOpenCodeAuthEntry2 = async (logger) => {
404
- const auth = await loadOpenCodeAuth(logger);
405
- if (!auth) {
406
- return null;
407
- }
408
- for (const alias of getProviderAliases("openai")) {
409
- const entry = toAuthData2(auth[alias]);
410
- if (entry && hasAccessToken(entry)) {
411
- return entry;
412
- }
413
- }
414
- return null;
415
- };
416
- var getOpenaiAuth = async (logger) => {
417
- const openCodeAuth = await loadOpenCodeAuthEntry2(logger);
418
- if (openCodeAuth && hasAccessToken(openCodeAuth)) {
419
- return openCodeAuth;
420
- }
421
- const pluginAuth = await readJson(AUTH_PATHS.openaiPlugin(), logger);
422
- if (pluginAuth && hasAccessToken(pluginAuth)) {
423
- return pluginAuth;
549
+ var toAuthData2, hasAccessToken, loadOpenCodeAuthEntry2, getOpenaiAuth;
550
+ var init_auth2 = __esm({
551
+ "src/providers/openai/auth.ts"() {
552
+ "use strict";
553
+ init_files();
554
+ init_registry();
555
+ toAuthData2 = (entry) => {
556
+ if (!entry) {
557
+ return null;
558
+ }
559
+ if (typeof entry === "string") {
560
+ return { token: entry };
561
+ }
562
+ if (typeof entry === "object") {
563
+ return entry;
564
+ }
565
+ return null;
566
+ };
567
+ hasAccessToken = (auth) => {
568
+ return Boolean(auth?.access || auth?.token);
569
+ };
570
+ loadOpenCodeAuthEntry2 = async (logger) => {
571
+ const auth = await loadOpenCodeAuth(logger);
572
+ if (!auth) {
573
+ return null;
574
+ }
575
+ for (const alias of getProviderAliases("openai")) {
576
+ const entry = toAuthData2(auth[alias]);
577
+ if (entry && hasAccessToken(entry)) {
578
+ return entry;
579
+ }
580
+ }
581
+ return null;
582
+ };
583
+ getOpenaiAuth = async (logger) => {
584
+ const openCodeAuth = await loadOpenCodeAuthEntry2(logger);
585
+ if (openCodeAuth && hasAccessToken(openCodeAuth)) {
586
+ return openCodeAuth;
587
+ }
588
+ const pluginAuth = await readJson(AUTH_PATHS.openaiPlugin(), logger);
589
+ if (pluginAuth && hasAccessToken(pluginAuth)) {
590
+ return pluginAuth;
591
+ }
592
+ return null;
593
+ };
424
594
  }
425
- return null;
426
- };
595
+ });
427
596
 
428
597
  // src/providers/openai/fetch.ts
429
- var toWindow2 = (window) => {
430
- if (!window) {
431
- return null;
432
- }
433
- const usedPercent = window.used_percent;
434
- const resetAt = window.reset_at ? window.reset_at * 1e3 : null;
435
- const resetAfterSeconds = window.reset_after_seconds ?? calculateResetAfterSeconds(resetAt);
436
- return {
437
- usedPercent,
438
- remainingPercent: Math.max(0, 100 - usedPercent),
439
- windowSeconds: window.limit_window_seconds ?? null,
440
- resetAfterSeconds,
441
- resetAt,
442
- resetAtFormatted: resetAt ? formatResetAt(resetAt) : null,
443
- resetAfterFormatted: resetAfterSeconds !== null ? formatDuration(resetAfterSeconds) : null
444
- };
445
- };
446
- var fetchOpenaiUsage = async (logger = noopLogger) => {
447
- const auth = await getOpenaiAuth(logger);
448
- if (!auth) {
449
- await logger.warn("No auth configured for openai");
450
- return {
451
- provider: "openai",
452
- ok: false,
453
- configured: false,
454
- error: "Not configured - no OAuth token found",
455
- usage: null
456
- };
457
- }
458
- const accessToken = auth.access ?? auth.token;
459
- if (!accessToken) {
460
- await logger.warn("Auth configured but access token missing for openai");
461
- return {
462
- provider: "openai",
463
- ok: false,
464
- configured: false,
465
- error: "Not configured - access token missing",
466
- usage: null
467
- };
468
- }
469
- try {
470
- const response = await fetch("https://chatgpt.com/backend-api/wham/usage", {
471
- method: "GET",
472
- headers: {
473
- Authorization: `Bearer ${accessToken}`,
474
- "Content-Type": "application/json"
475
- }
476
- });
477
- if (!response.ok) {
478
- await logger.error(`API error ${response.status} for openai`, {
479
- token: maskSecret(accessToken)
480
- });
598
+ var toWindow2, fetchOpenaiUsage;
599
+ var init_fetch2 = __esm({
600
+ "src/providers/openai/fetch.ts"() {
601
+ "use strict";
602
+ init_logger();
603
+ init_time();
604
+ init_auth2();
605
+ toWindow2 = (window) => {
606
+ if (!window) {
607
+ return null;
608
+ }
609
+ const usedPercent = window.used_percent;
610
+ const resetAt = window.reset_at ? window.reset_at * 1e3 : null;
611
+ const resetAfterSeconds = window.reset_after_seconds ?? calculateResetAfterSeconds(resetAt);
481
612
  return {
482
- provider: "openai",
483
- ok: false,
484
- configured: true,
485
- error: `API error: ${response.status}`,
486
- usage: null
613
+ usedPercent,
614
+ remainingPercent: Math.max(0, 100 - usedPercent),
615
+ windowSeconds: window.limit_window_seconds ?? null,
616
+ resetAfterSeconds,
617
+ resetAt,
618
+ resetAtFormatted: resetAt ? formatResetAt(resetAt) : null,
619
+ resetAfterFormatted: resetAfterSeconds !== null ? formatDuration(resetAfterSeconds) : null
487
620
  };
488
- }
489
- const payload = await response.json();
490
- const primary = toWindow2(payload.rate_limit.primary_window);
491
- const secondary = toWindow2(payload.rate_limit.secondary_window);
492
- const windows = {};
493
- if (primary) {
494
- windows["5h"] = primary;
495
- }
496
- if (secondary) {
497
- windows["weekly"] = secondary;
498
- }
499
- const usage = {
500
- windows
501
621
  };
502
- await logger.info("openai usage fetched successfully");
503
- return {
504
- provider: "openai",
505
- ok: true,
506
- configured: true,
507
- usage
508
- };
509
- } catch (error) {
510
- const message = error instanceof Error ? error.message : String(error);
511
- await logger.error(`Request failed for openai: ${message}`);
512
- return {
513
- provider: "openai",
514
- ok: false,
515
- configured: true,
516
- error: `Request failed: ${message}`,
517
- usage: null
622
+ fetchOpenaiUsage = async (logger = noopLogger) => {
623
+ const auth = await getOpenaiAuth(logger);
624
+ if (!auth) {
625
+ await logger.warn("No auth configured for openai");
626
+ return {
627
+ provider: "openai",
628
+ ok: false,
629
+ configured: false,
630
+ error: "Not configured - no OAuth token found",
631
+ usage: null
632
+ };
633
+ }
634
+ const accessToken = auth.access ?? auth.token;
635
+ if (!accessToken) {
636
+ await logger.warn("Auth configured but access token missing for openai");
637
+ return {
638
+ provider: "openai",
639
+ ok: false,
640
+ configured: false,
641
+ error: "Not configured - access token missing",
642
+ usage: null
643
+ };
644
+ }
645
+ try {
646
+ const response = await fetch("https://chatgpt.com/backend-api/wham/usage", {
647
+ method: "GET",
648
+ headers: {
649
+ Authorization: `Bearer ${accessToken}`,
650
+ "Content-Type": "application/json"
651
+ }
652
+ });
653
+ if (!response.ok) {
654
+ await logger.error(`API error ${response.status} for openai`, {
655
+ token: maskSecret(accessToken)
656
+ });
657
+ return {
658
+ provider: "openai",
659
+ ok: false,
660
+ configured: true,
661
+ error: `API error: ${response.status}`,
662
+ usage: null
663
+ };
664
+ }
665
+ const payload = await response.json();
666
+ const primary = toWindow2(payload.rate_limit.primary_window);
667
+ const secondary = toWindow2(payload.rate_limit.secondary_window);
668
+ const windows = {};
669
+ if (primary) {
670
+ windows["5h"] = primary;
671
+ }
672
+ if (secondary) {
673
+ windows["weekly"] = secondary;
674
+ }
675
+ const usage = {
676
+ windows
677
+ };
678
+ await logger.info("openai usage fetched successfully");
679
+ return {
680
+ provider: "openai",
681
+ ok: true,
682
+ configured: true,
683
+ usage
684
+ };
685
+ } catch (error) {
686
+ const message = error instanceof Error ? error.message : String(error);
687
+ await logger.error(`Request failed for openai: ${message}`);
688
+ return {
689
+ provider: "openai",
690
+ ok: false,
691
+ configured: true,
692
+ error: `Request failed: ${message}`,
693
+ usage: null
694
+ };
695
+ }
518
696
  };
519
697
  }
520
- };
698
+ });
521
699
 
522
700
  // src/providers/zai-coding-plan/auth.ts
523
- var resolveAuthValue = (entry) => {
524
- if (!entry) {
525
- return null;
526
- }
527
- if (typeof entry === "string") {
528
- return entry;
529
- }
530
- if (typeof entry === "object") {
531
- return entry.api_key ?? entry.token ?? entry.key ?? null;
532
- }
533
- return null;
534
- };
535
- var getZaiApiKey = async (logger) => {
536
- if (process.env.ZAI_API_KEY) {
537
- return process.env.ZAI_API_KEY;
538
- }
539
- const auth = await loadOpenCodeAuth(logger);
540
- if (!auth) {
541
- return null;
542
- }
543
- for (const alias of getProviderAliases("zai-coding-plan")) {
544
- const value = resolveAuthValue(auth[alias]);
545
- if (value) {
546
- return value;
547
- }
701
+ var resolveAuthValue, getZaiApiKey;
702
+ var init_auth3 = __esm({
703
+ "src/providers/zai-coding-plan/auth.ts"() {
704
+ "use strict";
705
+ init_files();
706
+ init_registry();
707
+ resolveAuthValue = (entry) => {
708
+ if (!entry) {
709
+ return null;
710
+ }
711
+ if (typeof entry === "string") {
712
+ return entry;
713
+ }
714
+ if (typeof entry === "object") {
715
+ return entry.api_key ?? entry.token ?? entry.key ?? null;
716
+ }
717
+ return null;
718
+ };
719
+ getZaiApiKey = async (logger) => {
720
+ if (process.env.ZAI_API_KEY) {
721
+ return process.env.ZAI_API_KEY;
722
+ }
723
+ const auth = await loadOpenCodeAuth(logger);
724
+ if (!auth) {
725
+ return null;
726
+ }
727
+ for (const alias of getProviderAliases("zai-coding-plan")) {
728
+ const value = resolveAuthValue(auth[alias]);
729
+ if (value) {
730
+ return value;
731
+ }
732
+ }
733
+ return null;
734
+ };
548
735
  }
549
- return null;
550
- };
736
+ });
551
737
 
552
738
  // src/providers/zai-coding-plan/fetch.ts
553
- var normalizeTimestamp = (value) => {
554
- return value < 1e12 ? value * 1e3 : value;
555
- };
556
- var TOKEN_WINDOW_SECONDS = {
557
- 3: 3600
558
- };
559
- var resolveWindowSeconds = (limit) => {
560
- if (!limit) {
561
- return null;
562
- }
563
- if (!limit.number) {
564
- return null;
565
- }
566
- const unitSeconds = TOKEN_WINDOW_SECONDS[limit.unit];
567
- if (!unitSeconds) {
568
- return null;
739
+ var normalizeTimestamp, TOKEN_WINDOW_SECONDS, resolveWindowSeconds, resolveWindowLabel, toWindow3, fetchZaiUsage;
740
+ var init_fetch3 = __esm({
741
+ "src/providers/zai-coding-plan/fetch.ts"() {
742
+ "use strict";
743
+ init_logger();
744
+ init_time();
745
+ init_auth3();
746
+ normalizeTimestamp = (value) => {
747
+ return value < 1e12 ? value * 1e3 : value;
748
+ };
749
+ TOKEN_WINDOW_SECONDS = {
750
+ 3: 3600
751
+ };
752
+ resolveWindowSeconds = (limit) => {
753
+ if (!limit) {
754
+ return null;
755
+ }
756
+ if (!limit.number) {
757
+ return null;
758
+ }
759
+ const unitSeconds = TOKEN_WINDOW_SECONDS[limit.unit];
760
+ if (!unitSeconds) {
761
+ return null;
762
+ }
763
+ return unitSeconds * limit.number;
764
+ };
765
+ resolveWindowLabel = (windowSeconds) => {
766
+ if (!windowSeconds) {
767
+ return "tokens";
768
+ }
769
+ if (windowSeconds % 86400 === 0) {
770
+ const days = windowSeconds / 86400;
771
+ return days === 7 ? "weekly" : `${days}d`;
772
+ }
773
+ if (windowSeconds % 3600 === 0) {
774
+ return `${windowSeconds / 3600}h`;
775
+ }
776
+ return `${windowSeconds}s`;
777
+ };
778
+ toWindow3 = (limit) => {
779
+ if (!limit) {
780
+ return null;
781
+ }
782
+ const usedPercent = limit.percentage ?? null;
783
+ const remainingPercent = usedPercent !== null ? Math.max(0, 100 - usedPercent) : null;
784
+ const resetAt = limit.nextResetTime ? normalizeTimestamp(limit.nextResetTime) : null;
785
+ const resetAfterSeconds = calculateResetAfterSeconds(resetAt);
786
+ return {
787
+ usedPercent,
788
+ remainingPercent,
789
+ windowSeconds: resolveWindowSeconds(limit),
790
+ resetAfterSeconds,
791
+ resetAt,
792
+ resetAtFormatted: resetAt ? formatResetAt(resetAt) : null,
793
+ resetAfterFormatted: resetAfterSeconds !== null ? formatDuration(resetAfterSeconds) : null
794
+ };
795
+ };
796
+ fetchZaiUsage = async (logger = noopLogger) => {
797
+ const apiKey = await getZaiApiKey(logger);
798
+ if (!apiKey) {
799
+ await logger.warn("No auth configured for zai-coding-plan");
800
+ return {
801
+ provider: "zai-coding-plan",
802
+ ok: false,
803
+ configured: false,
804
+ error: "Not configured - no API key found",
805
+ usage: null
806
+ };
807
+ }
808
+ try {
809
+ const response = await fetch("https://api.z.ai/api/monitor/usage/quota/limit", {
810
+ method: "GET",
811
+ headers: {
812
+ Authorization: `Bearer ${apiKey}`,
813
+ "Content-Type": "application/json"
814
+ }
815
+ });
816
+ if (!response.ok) {
817
+ await logger.error(`API error ${response.status} for zai-coding-plan`, {
818
+ token: maskSecret(apiKey)
819
+ });
820
+ return {
821
+ provider: "zai-coding-plan",
822
+ ok: false,
823
+ configured: true,
824
+ error: `API error: ${response.status}`,
825
+ usage: null
826
+ };
827
+ }
828
+ const payload = await response.json();
829
+ const limits = payload.data?.limits ?? [];
830
+ const tokensLimit = limits.find((limit) => limit.type === "TOKENS_LIMIT");
831
+ const windows = {};
832
+ const window = toWindow3(tokensLimit);
833
+ if (window) {
834
+ const label = resolveWindowLabel(window.windowSeconds);
835
+ windows[label] = window;
836
+ }
837
+ const usage = {
838
+ windows
839
+ };
840
+ await logger.info("zai-coding-plan usage fetched successfully");
841
+ return {
842
+ provider: "zai-coding-plan",
843
+ ok: true,
844
+ configured: true,
845
+ usage
846
+ };
847
+ } catch (error) {
848
+ const message = error instanceof Error ? error.message : String(error);
849
+ await logger.error(`Request failed for zai-coding-plan: ${message}`);
850
+ return {
851
+ provider: "zai-coding-plan",
852
+ ok: false,
853
+ configured: true,
854
+ error: `Request failed: ${message}`,
855
+ usage: null
856
+ };
857
+ }
858
+ };
569
859
  }
570
- return unitSeconds * limit.number;
571
- };
572
- var resolveWindowLabel = (windowSeconds) => {
573
- if (!windowSeconds) {
574
- return "tokens";
860
+ });
861
+
862
+ // src/types/provider.ts
863
+ var PROVIDERS;
864
+ var init_provider = __esm({
865
+ "src/types/provider.ts"() {
866
+ "use strict";
867
+ PROVIDERS = ["openai", "google", "zai-coding-plan"];
575
868
  }
576
- if (windowSeconds % 86400 === 0) {
577
- const days = windowSeconds / 86400;
578
- return days === 7 ? "weekly" : `${days}d`;
869
+ });
870
+
871
+ // src/types/dashboard.ts
872
+ var init_dashboard = __esm({
873
+ "src/types/dashboard.ts"() {
874
+ "use strict";
579
875
  }
580
- if (windowSeconds % 3600 === 0) {
581
- return `${windowSeconds / 3600}h`;
876
+ });
877
+
878
+ // src/types/index.ts
879
+ var init_types2 = __esm({
880
+ "src/types/index.ts"() {
881
+ "use strict";
882
+ init_provider();
883
+ init_dashboard();
884
+ }
885
+ });
886
+
887
+ // src/cache/fetcher.ts
888
+ var fetchUsage, fetchAllProviders;
889
+ var init_fetcher = __esm({
890
+ "src/cache/fetcher.ts"() {
891
+ "use strict";
892
+ init_fetch();
893
+ init_fetch2();
894
+ init_fetch3();
895
+ init_types2();
896
+ init_file();
897
+ init_types();
898
+ fetchUsage = async (provider, logger) => {
899
+ switch (provider) {
900
+ case "openai":
901
+ return fetchOpenaiUsage(logger);
902
+ case "google":
903
+ return fetchGoogleUsage(logger);
904
+ case "zai-coding-plan":
905
+ return fetchZaiUsage(logger);
906
+ }
907
+ };
908
+ fetchAllProviders = async (logger) => {
909
+ await logger.info("Fetching usage for all providers");
910
+ const existingCache = await readCache(logger);
911
+ const results = await Promise.all(PROVIDERS.map((provider) => fetchUsage(provider, logger)));
912
+ const providers = {};
913
+ const now = (/* @__PURE__ */ new Date()).toISOString();
914
+ for (const result of results) {
915
+ const providerId = result.provider;
916
+ const wasConfigured = existingCache?.providers[providerId]?.configured ?? false;
917
+ providers[providerId] = {
918
+ supported: true,
919
+ configured: result.configured,
920
+ last_attempt_at: now,
921
+ last_success_at: result.ok ? now : null,
922
+ data: result.ok ? result.usage : null,
923
+ error: result.ok ? null : result.error ?? null
924
+ };
925
+ if (!result.configured && wasConfigured) {
926
+ providers[providerId].data = null;
927
+ await logger.debug(`Wiped data for ${providerId} (no longer configured)`);
928
+ }
929
+ }
930
+ const cache = {
931
+ schema_version: SCHEMA_VERSION,
932
+ updated_at: now,
933
+ refresh_interval_seconds: REFRESH_INTERVAL_SECONDS,
934
+ providers
935
+ };
936
+ await writeCache(cache, logger);
937
+ return cache;
938
+ };
582
939
  }
583
- return `${windowSeconds}s`;
584
- };
585
- var toWindow3 = (limit) => {
586
- if (!limit) {
587
- return null;
588
- }
589
- const usedPercent = limit.percentage ?? null;
590
- const remainingPercent = usedPercent !== null ? Math.max(0, 100 - usedPercent) : null;
591
- const resetAt = limit.nextResetTime ? normalizeTimestamp(limit.nextResetTime) : null;
592
- const resetAfterSeconds = calculateResetAfterSeconds(resetAt);
593
- return {
594
- usedPercent,
595
- remainingPercent,
596
- windowSeconds: resolveWindowSeconds(limit),
597
- resetAfterSeconds,
598
- resetAt,
599
- resetAtFormatted: resetAt ? formatResetAt(resetAt) : null,
600
- resetAfterFormatted: resetAfterSeconds !== null ? formatDuration(resetAfterSeconds) : null
601
- };
602
- };
603
- var fetchZaiUsage = async (logger = noopLogger) => {
604
- const apiKey = await getZaiApiKey(logger);
605
- if (!apiKey) {
606
- await logger.warn("No auth configured for zai-coding-plan");
607
- return {
608
- provider: "zai-coding-plan",
609
- ok: false,
610
- configured: false,
611
- error: "Not configured - no API key found",
612
- usage: null
613
- };
614
- }
615
- try {
616
- const response = await fetch("https://api.z.ai/api/monitor/usage/quota/limit", {
617
- method: "GET",
618
- headers: {
619
- Authorization: `Bearer ${apiKey}`,
620
- "Content-Type": "application/json"
621
- }
622
- });
623
- if (!response.ok) {
624
- await logger.error(`API error ${response.status} for zai-coding-plan`, {
625
- token: maskSecret(apiKey)
626
- });
627
- return {
628
- provider: "zai-coding-plan",
629
- ok: false,
630
- configured: true,
631
- error: `API error: ${response.status}`,
632
- usage: null
940
+ });
941
+
942
+ // src/cache/worker.ts
943
+ var worker_exports = {};
944
+ __export(worker_exports, {
945
+ isWorkerRunning: () => isWorkerRunning,
946
+ startWorker: () => startWorker,
947
+ stopWorker: () => stopWorker
948
+ });
949
+ var intervalId, startWorker, stopWorker, isWorkerRunning;
950
+ var init_worker = __esm({
951
+ "src/cache/worker.ts"() {
952
+ "use strict";
953
+ init_fetcher();
954
+ init_types();
955
+ intervalId = null;
956
+ startWorker = (logger) => {
957
+ if (intervalId !== null) {
958
+ logger.warn("Worker already started");
959
+ return;
960
+ }
961
+ const refreshLoop = async () => {
962
+ try {
963
+ await fetchAllProviders(logger);
964
+ } catch (error) {
965
+ await logger.error("Worker refresh failed", {
966
+ error: error instanceof Error ? error.message : String(error)
967
+ });
968
+ }
633
969
  };
634
- }
635
- const payload = await response.json();
636
- const limits = payload.data?.limits ?? [];
637
- const tokensLimit = limits.find((limit) => limit.type === "TOKENS_LIMIT");
638
- const windows = {};
639
- const window = toWindow3(tokensLimit);
640
- if (window) {
641
- const label = resolveWindowLabel(window.windowSeconds);
642
- windows[label] = window;
643
- }
644
- const usage = {
645
- windows
970
+ void refreshLoop();
971
+ intervalId = globalThis.setInterval(refreshLoop, REFRESH_INTERVAL_SECONDS * 1e3);
972
+ void logger.info(`Worker started, refreshing every ${REFRESH_INTERVAL_SECONDS}s`);
646
973
  };
647
- await logger.info("zai-coding-plan usage fetched successfully");
648
- return {
649
- provider: "zai-coding-plan",
650
- ok: true,
651
- configured: true,
652
- usage
653
- };
654
- } catch (error) {
655
- const message = error instanceof Error ? error.message : String(error);
656
- await logger.error(`Request failed for zai-coding-plan: ${message}`);
657
- return {
658
- provider: "zai-coding-plan",
659
- ok: false,
660
- configured: true,
661
- error: `Request failed: ${message}`,
662
- usage: null
974
+ stopWorker = (logger) => {
975
+ if (intervalId === null) {
976
+ return;
977
+ }
978
+ globalThis.clearInterval(intervalId);
979
+ intervalId = null;
980
+ void logger.info("Worker stopped");
663
981
  };
982
+ isWorkerRunning = () => intervalId !== null;
664
983
  }
665
- };
984
+ });
666
985
 
667
- // src/types/provider.ts
668
- var PROVIDERS = ["openai", "google", "zai-coding-plan"];
986
+ // src/index.ts
987
+ init_logger();
988
+ import { tool } from "@opencode-ai/plugin";
669
989
 
670
990
  // src/toast/filter.ts
671
991
  var FLAGSHIP_PATTERNS = [
@@ -689,7 +1009,7 @@ var filterFlagshipModels = (models) => {
689
1009
  return filtered;
690
1010
  };
691
1011
 
692
- // src/table/format.ts
1012
+ // src/dashboard/format.ts
693
1013
  var getStatus = (remainingPercent) => {
694
1014
  if (remainingPercent === null) {
695
1015
  return { emoji: "\u26AA", text: "N/A" };
@@ -733,7 +1053,16 @@ var formatWindow = (key, window) => {
733
1053
  resetsIn: window.resetAfterFormatted ?? "N/A"
734
1054
  };
735
1055
  };
736
- var formatDashboardData = (results) => {
1056
+ var formatDashboardData = (input) => {
1057
+ if (input === null) {
1058
+ return { providers: [] };
1059
+ }
1060
+ if (!("providers" in input)) {
1061
+ return formatDashboardDataFromResults(input);
1062
+ }
1063
+ return formatDashboardDataFromCache(input);
1064
+ };
1065
+ var formatDashboardDataFromResults = (results) => {
737
1066
  const providers = [];
738
1067
  for (const result of results) {
739
1068
  if (!result.usage) {
@@ -755,15 +1084,23 @@ var formatDashboardData = (results) => {
755
1084
  });
756
1085
  }
757
1086
  if (usage.models) {
1087
+ const modelSections = [];
758
1088
  for (const [modelName, modelUsage] of Object.entries(usage.models)) {
759
1089
  const modelWindowEntries = Object.entries(modelUsage.windows);
760
1090
  if (modelWindowEntries.length > 0) {
761
- sections.push({
1091
+ modelSections.push({
762
1092
  title: modelName,
763
1093
  windows: modelWindowEntries.map(([key, win]) => formatWindow(key, win))
764
1094
  });
765
1095
  }
766
1096
  }
1097
+ if (modelSections.length > 0) {
1098
+ sections.push({
1099
+ title: "Model Usage",
1100
+ windows: [],
1101
+ sections: modelSections
1102
+ });
1103
+ }
767
1104
  }
768
1105
  if (sections.length > 0) {
769
1106
  providers.push({
@@ -774,9 +1111,64 @@ var formatDashboardData = (results) => {
774
1111
  }
775
1112
  return { providers };
776
1113
  };
777
- var formatDashboardString = (data) => {
1114
+ var formatDashboardDataFromCache = (displayCache) => {
1115
+ const providers = [];
1116
+ for (const [providerId, entry] of Object.entries(displayCache.providers)) {
1117
+ if (!entry.data) {
1118
+ continue;
1119
+ }
1120
+ const sections = [];
1121
+ let usage = entry.data;
1122
+ if (providerId === "google" && usage.models) {
1123
+ usage = {
1124
+ ...usage,
1125
+ models: filterFlagshipModels(usage.models)
1126
+ };
1127
+ }
1128
+ const globalWindows = Object.entries(usage.windows);
1129
+ if (globalWindows.length > 0) {
1130
+ sections.push({
1131
+ title: "Overall Usage",
1132
+ windows: globalWindows.map(([key, win]) => formatWindow(key, win))
1133
+ });
1134
+ }
1135
+ if (usage.models) {
1136
+ const modelSections = [];
1137
+ for (const [modelName, modelUsage] of Object.entries(usage.models)) {
1138
+ const modelWindowEntries = Object.entries(modelUsage.windows);
1139
+ if (modelWindowEntries.length > 0) {
1140
+ modelSections.push({
1141
+ title: modelName,
1142
+ windows: modelWindowEntries.map(([key, win]) => formatWindow(key, win))
1143
+ });
1144
+ }
1145
+ }
1146
+ if (modelSections.length > 0) {
1147
+ sections.push({
1148
+ title: "Model Usage",
1149
+ windows: [],
1150
+ sections: modelSections
1151
+ });
1152
+ }
1153
+ }
1154
+ if (sections.length > 0) {
1155
+ providers.push({
1156
+ name: providerId.toUpperCase().replace(/-/g, " "),
1157
+ sections
1158
+ });
1159
+ }
1160
+ }
1161
+ return { providers };
1162
+ };
1163
+ var formatDashboardString = (data, updatedAt, isStale) => {
778
1164
  if (data.providers.length === 0) {
779
- return "No usage data available";
1165
+ let result2 = "No usage data available";
1166
+ if (updatedAt) {
1167
+ result2 += `
1168
+
1169
+ Updated at: ${updatedAt}`;
1170
+ }
1171
+ return result2;
780
1172
  }
781
1173
  const lines = [];
782
1174
  const HEADER_WIDTH = 65;
@@ -794,104 +1186,171 @@ var formatDashboardString = (data) => {
794
1186
  const pipe = isLastWindow ? " " : "\u2502 ";
795
1187
  lines.push(` ${branch} ${window.label}`);
796
1188
  const percentStr = window.usedPercent !== null ? `${Math.round(window.usedPercent)}%` : "N/A";
797
- lines.push(` ${pipe} ${renderBar(window.usedPercent)} ${percentStr}`);
798
1189
  lines.push(
799
- ` ${pipe} Status ${window.status} ${window.statusText} \u2022 Resets in ${window.resetsIn}`
1190
+ ` ${pipe} ${renderBar(window.usedPercent)} ${percentStr} \u2022 Resets in ${window.resetsIn}`
800
1191
  );
801
1192
  if (!isLastWindow) {
802
1193
  lines.push(` ${pipe}`);
803
1194
  }
804
1195
  }
1196
+ if (section.sections) {
1197
+ for (let k = 0; k < section.sections.length; k++) {
1198
+ const subsection = section.sections[k];
1199
+ const isLastSubsection = k === section.sections.length - 1;
1200
+ const branch = isLastSubsection ? "\u2514\u2500" : "\u251C\u2500";
1201
+ const pipe = isLastSubsection ? " " : "\u2502 ";
1202
+ lines.push(` ${branch} ${subsection.title}`);
1203
+ for (let m = 0; m < subsection.windows.length; m++) {
1204
+ const window = subsection.windows[m];
1205
+ const isLastWindow = m === subsection.windows.length - 1;
1206
+ const subBranch = isLastWindow ? "\u2514\u2500" : "\u251C\u2500";
1207
+ lines.push(` ${pipe} ${subBranch} ${window.label}`);
1208
+ const percentStr = window.usedPercent !== null ? `${Math.round(window.usedPercent)}%` : "N/A";
1209
+ const subPipe = isLastWindow ? " " : "\u2502 ";
1210
+ lines.push(
1211
+ ` ${pipe} ${subPipe} ${renderBar(window.usedPercent)} ${percentStr} \u2022 Resets in ${window.resetsIn}`
1212
+ );
1213
+ if (!isLastWindow) {
1214
+ lines.push(` ${pipe} ${subPipe}`);
1215
+ }
1216
+ }
1217
+ if (!isLastSubsection) {
1218
+ lines.push(` ${pipe}`);
1219
+ }
1220
+ }
1221
+ }
805
1222
  lines.push("");
806
1223
  }
807
1224
  }
808
- return lines.join("\n").trim();
1225
+ let result = lines.join("\n").trim();
1226
+ if (updatedAt) {
1227
+ result += `
1228
+
1229
+ Updated at: ${updatedAt}`;
1230
+ }
1231
+ if (isStale) {
1232
+ result += " (STALE)";
1233
+ }
1234
+ return result;
809
1235
  };
810
1236
 
811
1237
  // src/toast/format.ts
812
- var formatProviderLine = (provider, usage) => {
1238
+ var getStatus2 = (remainingPercent) => {
1239
+ if (remainingPercent === null) return "\u26AA";
1240
+ if (remainingPercent < 10) return "\u{1F534}";
1241
+ if (remainingPercent < 30) return "\u{1F7E1}";
1242
+ return "\u{1F7E2}";
1243
+ };
1244
+ var formatWindowLine = (name, window, isModel = false) => {
1245
+ const used = window.usedPercent !== null ? Math.round(window.usedPercent) : 0;
1246
+ const reset = window.resetAfterFormatted ?? "N/A";
1247
+ const remaining = window.remainingPercent ?? null;
1248
+ const emoji = isModel ? "" : `${getStatus2(remaining)} `;
1249
+ const indent = isModel ? " " : "";
1250
+ return `${indent}${emoji}${name.padEnd(isModel ? 13 : 10)} ${used}% \u2022 ${reset}`;
1251
+ };
1252
+ var formatProviderSection = (provider, usage) => {
813
1253
  if (!usage) {
814
- return `${provider}: Not configured`;
1254
+ return [`\u26AA ${provider}: Not configured`];
815
1255
  }
1256
+ const lines = [];
816
1257
  const globalWindows = Object.values(usage.windows);
817
1258
  if (globalWindows.length > 0) {
818
- const window = globalWindows[0];
819
- const used = window.usedPercent ?? 0;
820
- const reset = window.resetAfterFormatted ?? "N/A";
821
- return `${provider}: ${used}% used \u2022 resets in ${reset}`;
1259
+ lines.push(formatWindowLine(provider, globalWindows[0]));
1260
+ } else {
1261
+ lines.push(`\u26AA ${provider}: No usage data`);
822
1262
  }
823
1263
  if (usage.models) {
824
- const models = Object.entries(usage.models);
825
- if (models.length === 1) {
826
- const [modelName, modelData] = models[0];
827
- const window = Object.values(modelData.windows)[0];
828
- const used = window.usedPercent ?? 0;
829
- const reset = window.resetAfterFormatted ?? "N/A";
830
- return `${provider}: ${modelName} ${used}% \u2022 resets in ${reset}`;
831
- }
832
- if (models.length > 1) {
833
- const parts = [];
834
- for (const [modelName, modelData] of models) {
835
- const window = Object.values(modelData.windows)[0];
836
- const used = window.usedPercent ?? 0;
837
- parts.push(`${modelName} ${used}%`);
1264
+ for (const [modelName, modelData] of Object.entries(usage.models)) {
1265
+ const modelWindow = Object.values(modelData.windows)[0];
1266
+ if (modelWindow) {
1267
+ lines.push(formatWindowLine(modelName, modelWindow, true));
838
1268
  }
839
- return `${provider}: ${parts.join(", ")}`;
840
1269
  }
841
1270
  }
842
- return `${provider}: No usage data`;
1271
+ return lines;
843
1272
  };
844
- var formatUsageToast = async (results, logger) => {
1273
+ var formatUsageToast = async (input, logger) => {
1274
+ if (!input) {
1275
+ return {
1276
+ title: "\u{1F4CA} Usage",
1277
+ message: "No providers configured",
1278
+ variant: "info"
1279
+ };
1280
+ }
1281
+ const displayCache = "providers" in input ? input : null;
1282
+ const results = "providers" in input ? null : input;
845
1283
  const lines = [];
846
- for (const result of results) {
847
- if (!result.usage) {
848
- await logger?.debug(`Provider ${result.provider} not configured, skipping`);
849
- continue;
1284
+ const contentLines = [];
1285
+ const DIVIDER = "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500";
1286
+ let updatedAt = "";
1287
+ let isStale = false;
1288
+ if (displayCache) {
1289
+ updatedAt = displayCache.updatedAt;
1290
+ isStale = displayCache.isStale;
1291
+ for (const [providerId, entry] of Object.entries(displayCache.providers)) {
1292
+ if (!entry.data) continue;
1293
+ let usage = entry.data;
1294
+ if (providerId === "google" && usage?.models) {
1295
+ usage = {
1296
+ ...usage,
1297
+ models: filterFlagshipModels(usage.models)
1298
+ };
1299
+ }
1300
+ contentLines.push(...formatProviderSection(providerId, usage));
850
1301
  }
851
- let usage = result.usage;
852
- if (result.provider === "google" && usage?.models) {
853
- usage = {
854
- ...usage,
855
- models: filterFlagshipModels(usage.models)
856
- };
1302
+ if (Object.values(displayCache.providers).some((e) => e.error !== null)) {
1303
+ contentLines.push("\u26A0\uFE0F Some providers failed");
1304
+ }
1305
+ } else if (results) {
1306
+ const now = /* @__PURE__ */ new Date();
1307
+ updatedAt = `${now.getHours().toString().padStart(2, "0")}:${now.getMinutes().toString().padStart(2, "0")}`;
1308
+ for (const result of results) {
1309
+ if (!result.usage) {
1310
+ await logger?.debug(`Provider ${result.provider} not configured, skipping`);
1311
+ continue;
1312
+ }
1313
+ let usage = result.usage;
1314
+ if (result.provider === "google" && usage?.models) {
1315
+ usage = {
1316
+ ...usage,
1317
+ models: filterFlagshipModels(usage.models)
1318
+ };
1319
+ }
1320
+ contentLines.push(...formatProviderSection(result.provider, usage));
857
1321
  }
858
- const line = formatProviderLine(result.provider, usage);
859
- lines.push(line);
860
1322
  }
861
- if (lines.length === 0) {
1323
+ if (contentLines.length === 0) {
862
1324
  return {
863
- title: "Usage",
1325
+ title: "\u{1F4CA} Usage",
864
1326
  message: "No providers configured",
865
1327
  variant: "info"
866
1328
  };
867
1329
  }
1330
+ lines.push(DIVIDER);
1331
+ lines.push(...contentLines);
1332
+ lines.push(DIVIDER);
1333
+ let footer = `Updated: ${updatedAt}`;
1334
+ if (isStale) footer += " (stale)";
1335
+ lines.push(footer);
868
1336
  return {
869
- title: "Usage",
1337
+ title: "\u{1F4CA} Usage",
870
1338
  message: lines.join("\n"),
871
1339
  variant: "info"
872
1340
  };
873
1341
  };
874
1342
 
875
1343
  // src/index.ts
876
- var fetchUsage = async (provider, logger) => {
877
- switch (provider) {
878
- case "openai":
879
- return fetchOpenaiUsage(logger);
880
- case "google":
881
- return fetchGoogleUsage(logger);
882
- case "zai-coding-plan":
883
- return fetchZaiUsage(logger);
884
- }
885
- };
886
1344
  var UsagePlugin = async ({ client }) => {
887
1345
  const logger = createLogger(client);
888
1346
  const usageToastTool = tool({
889
1347
  description: "Show subscription usage as toast for OpenAI, Google, and z.ai providers",
890
1348
  args: {},
891
1349
  async execute() {
892
- await logger.info("Fetching usage for all providers");
893
- const results = await Promise.all(PROVIDERS.map((provider) => fetchUsage(provider, logger)));
894
- const toast = await formatUsageToast(results, logger);
1350
+ const { loadCacheForDisplay: loadCacheForDisplay2 } = await Promise.resolve().then(() => (init_reader(), reader_exports));
1351
+ await logger.info("Loading usage from cache");
1352
+ const displayCache = await loadCacheForDisplay2(logger);
1353
+ const toast = await formatUsageToast(displayCache, logger);
895
1354
  await client.tui.showToast({
896
1355
  body: {
897
1356
  title: toast.title,
@@ -906,10 +1365,14 @@ var UsagePlugin = async ({ client }) => {
906
1365
  description: "Get subscription usage data for OpenAI, Google, and z.ai providers as a formatted table",
907
1366
  args: {},
908
1367
  async execute() {
909
- await logger.info("Fetching usage for all providers");
910
- const results = await Promise.all(PROVIDERS.map((provider) => fetchUsage(provider, logger)));
911
- const dashboardData = formatDashboardData(results);
912
- return formatDashboardString(dashboardData);
1368
+ const { loadCacheForDisplay: loadCacheForDisplay2 } = await Promise.resolve().then(() => (init_reader(), reader_exports));
1369
+ await logger.info("Loading usage from cache");
1370
+ const displayCache = await loadCacheForDisplay2(logger);
1371
+ if (!displayCache) {
1372
+ return "No cache available";
1373
+ }
1374
+ const dashboardData = formatDashboardData(displayCache);
1375
+ return formatDashboardString(dashboardData, displayCache.updatedAt, displayCache.isStale);
913
1376
  }
914
1377
  });
915
1378
  return {
@@ -917,6 +1380,12 @@ var UsagePlugin = async ({ client }) => {
917
1380
  usage_toast: usageToastTool,
918
1381
  usage_table: usageTableTool
919
1382
  },
1383
+ async event({ event }) {
1384
+ if (event.type === "server.connected") {
1385
+ const { startWorker: startWorker2 } = await Promise.resolve().then(() => (init_worker(), worker_exports));
1386
+ startWorker2(logger);
1387
+ }
1388
+ },
920
1389
  async config(config) {
921
1390
  config.command = config.command ?? {};
922
1391
  config.command["usage-toast"] = {
@@ -930,6 +1399,22 @@ var UsagePlugin = async ({ client }) => {
930
1399
  }
931
1400
  };
932
1401
  };
1402
+ var setupShutdownHandlers = async () => {
1403
+ const { isWorkerRunning: isWorkerRunning2, stopWorker: stopWorker2 } = await Promise.resolve().then(() => (init_worker(), worker_exports));
1404
+ const { createLogger: createLogger2 } = await Promise.resolve().then(() => (init_logger(), logger_exports));
1405
+ const logger = createLogger2({
1406
+ app: { log: () => Promise.resolve(true) }
1407
+ });
1408
+ const onShutdown = async () => {
1409
+ if (await isWorkerRunning2()) {
1410
+ stopWorker2(logger);
1411
+ }
1412
+ };
1413
+ process.on("SIGINT", onShutdown);
1414
+ process.on("SIGTERM", onShutdown);
1415
+ process.on("exit", onShutdown);
1416
+ };
1417
+ void setupShutdownHandlers();
933
1418
  var index_default = UsagePlugin;
934
1419
  export {
935
1420
  UsagePlugin,