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 +1166 -681
- package/dist/index.js.map +4 -4
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,671 +1,991 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
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
|
|
12
|
-
|
|
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/
|
|
61
|
-
var
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
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
|
-
|
|
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
|
|
106
|
-
var
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
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/
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
};
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
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
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
var
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
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
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
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
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
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
|
-
|
|
229
|
-
};
|
|
281
|
+
});
|
|
230
282
|
|
|
231
|
-
// src/providers/google/
|
|
232
|
-
var
|
|
233
|
-
var
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
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
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
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
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
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
|
-
}
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
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
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
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
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
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
|
-
|
|
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
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
};
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
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
|
-
|
|
426
|
-
};
|
|
595
|
+
});
|
|
427
596
|
|
|
428
597
|
// src/providers/openai/fetch.ts
|
|
429
|
-
var toWindow2
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
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
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
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
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
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
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
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
|
-
|
|
550
|
-
};
|
|
736
|
+
});
|
|
551
737
|
|
|
552
738
|
// src/providers/zai-coding-plan/fetch.ts
|
|
553
|
-
var normalizeTimestamp
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
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
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
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
|
-
|
|
577
|
-
|
|
578
|
-
|
|
869
|
+
});
|
|
870
|
+
|
|
871
|
+
// src/types/dashboard.ts
|
|
872
|
+
var init_dashboard = __esm({
|
|
873
|
+
"src/types/dashboard.ts"() {
|
|
874
|
+
"use strict";
|
|
579
875
|
}
|
|
580
|
-
|
|
581
|
-
|
|
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
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
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
|
-
|
|
636
|
-
|
|
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
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
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/
|
|
668
|
-
|
|
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/
|
|
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 = (
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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}
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
819
|
-
|
|
820
|
-
|
|
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
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
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
|
|
1271
|
+
return lines;
|
|
843
1272
|
};
|
|
844
|
-
var formatUsageToast = async (
|
|
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
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
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
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
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 (
|
|
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
|
|
893
|
-
|
|
894
|
-
const
|
|
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
|
|
910
|
-
|
|
911
|
-
const
|
|
912
|
-
|
|
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,
|