@metyatech/ai-quota 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +221 -0
- package/dist/amazon-q.d.ts +54 -0
- package/dist/amazon-q.d.ts.map +1 -0
- package/dist/amazon-q.js +103 -0
- package/dist/amazon-q.js.map +1 -0
- package/dist/claude.d.ts +10 -0
- package/dist/claude.d.ts.map +1 -0
- package/dist/claude.js +105 -0
- package/dist/claude.js.map +1 -0
- package/dist/cli.d.ts +14 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +378 -0
- package/dist/cli.js.map +1 -0
- package/dist/codex.d.ts +38 -0
- package/dist/codex.d.ts.map +1 -0
- package/dist/codex.js +321 -0
- package/dist/codex.js.map +1 -0
- package/dist/copilot.d.ts +30 -0
- package/dist/copilot.d.ts.map +1 -0
- package/dist/copilot.js +148 -0
- package/dist/copilot.js.map +1 -0
- package/dist/gemini.d.ts +14 -0
- package/dist/gemini.d.ts.map +1 -0
- package/dist/gemini.js +235 -0
- package/dist/gemini.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +17 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +64 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +9 -0
- package/dist/types.js.map +1 -0
- package/package.json +53 -0
package/dist/codex.js
ADDED
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
import { readFile, readdir, stat } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
// ---------------------------------------------------------------------------
|
|
5
|
+
// Internal helpers
|
|
6
|
+
// ---------------------------------------------------------------------------
|
|
7
|
+
function resolveCodexHome(codexHome) {
|
|
8
|
+
return codexHome ?? join(homedir(), ".codex");
|
|
9
|
+
}
|
|
10
|
+
function resolveWindowMinutes(window) {
|
|
11
|
+
const candidates = [window.windowDurationMins, window.windowMinutes, window.window_minutes];
|
|
12
|
+
for (const value of candidates) {
|
|
13
|
+
if (typeof value === "number" && Number.isFinite(value))
|
|
14
|
+
return value;
|
|
15
|
+
}
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
function resolveResetsAt(window) {
|
|
19
|
+
const candidates = [window.resetsAt, window.resets_at];
|
|
20
|
+
for (const value of candidates) {
|
|
21
|
+
if (typeof value === "number" && Number.isFinite(value))
|
|
22
|
+
return value;
|
|
23
|
+
}
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
function normalizeRateLimitWindow(window, now) {
|
|
27
|
+
const usedPercent = typeof window.usedPercent === "number"
|
|
28
|
+
? window.usedPercent
|
|
29
|
+
: typeof window.used_percent === "number"
|
|
30
|
+
? window.used_percent
|
|
31
|
+
: Number.NaN;
|
|
32
|
+
if (!Number.isFinite(usedPercent))
|
|
33
|
+
return null;
|
|
34
|
+
const windowMinutes = resolveWindowMinutes(window);
|
|
35
|
+
const resetsAt = resolveResetsAt(window);
|
|
36
|
+
let resetAt = null;
|
|
37
|
+
if (typeof resetsAt === "number" && Number.isFinite(resetsAt)) {
|
|
38
|
+
resetAt = new Date(resetsAt * 1000);
|
|
39
|
+
}
|
|
40
|
+
else if (windowMinutes !== null) {
|
|
41
|
+
resetAt = new Date(now.getTime() + windowMinutes * 60000);
|
|
42
|
+
}
|
|
43
|
+
return { usedPercent, windowMinutes, resetAt };
|
|
44
|
+
}
|
|
45
|
+
function clampPercent(value) {
|
|
46
|
+
if (!Number.isFinite(value))
|
|
47
|
+
return 0;
|
|
48
|
+
return Math.min(Math.max(value, 0), 100);
|
|
49
|
+
}
|
|
50
|
+
// ---------------------------------------------------------------------------
|
|
51
|
+
// Public conversion utility
|
|
52
|
+
// ---------------------------------------------------------------------------
|
|
53
|
+
/**
|
|
54
|
+
* Converts a raw `RateLimitSnapshot` (from JSONL session files or the HTTP
|
|
55
|
+
* API) into a structured `CodexStatus` with labelled usage windows.
|
|
56
|
+
*/
|
|
57
|
+
export function rateLimitSnapshotToStatus(snapshot, now = new Date()) {
|
|
58
|
+
const candidates = [
|
|
59
|
+
snapshot.primary
|
|
60
|
+
? { source: "primary", data: normalizeRateLimitWindow(snapshot.primary, now) }
|
|
61
|
+
: null,
|
|
62
|
+
snapshot.secondary
|
|
63
|
+
? { source: "secondary", data: normalizeRateLimitWindow(snapshot.secondary, now) }
|
|
64
|
+
: null
|
|
65
|
+
].filter((item) => Boolean(item?.data));
|
|
66
|
+
if (candidates.length === 0)
|
|
67
|
+
return null;
|
|
68
|
+
let fiveHourCandidate = null;
|
|
69
|
+
let weeklyCandidate = null;
|
|
70
|
+
if (candidates.length === 2) {
|
|
71
|
+
const [first, second] = candidates;
|
|
72
|
+
if (first.data.windowMinutes !== null && second.data.windowMinutes !== null) {
|
|
73
|
+
if (first.data.windowMinutes <= second.data.windowMinutes) {
|
|
74
|
+
fiveHourCandidate = first;
|
|
75
|
+
weeklyCandidate = second;
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
fiveHourCandidate = second;
|
|
79
|
+
weeklyCandidate = first;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
fiveHourCandidate = first.source === "primary" ? first : second;
|
|
84
|
+
weeklyCandidate = first.source === "primary" ? second : first;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
const lone = candidates[0];
|
|
89
|
+
if (lone.data.windowMinutes !== null && lone.data.windowMinutes >= 24 * 60) {
|
|
90
|
+
weeklyCandidate = lone;
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
fiveHourCandidate = lone;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
const windows = [];
|
|
97
|
+
if (fiveHourCandidate?.data.resetAt) {
|
|
98
|
+
windows.push({
|
|
99
|
+
key: "fiveHour",
|
|
100
|
+
label: "5h",
|
|
101
|
+
percentLeft: clampPercent(100 - fiveHourCandidate.data.usedPercent),
|
|
102
|
+
resetAt: fiveHourCandidate.data.resetAt,
|
|
103
|
+
resetText: fiveHourCandidate.data.resetAt.toISOString()
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
if (weeklyCandidate?.data.resetAt) {
|
|
107
|
+
windows.push({
|
|
108
|
+
key: "weekly",
|
|
109
|
+
label: "Weekly",
|
|
110
|
+
percentLeft: clampPercent(100 - weeklyCandidate.data.usedPercent),
|
|
111
|
+
resetAt: weeklyCandidate.data.resetAt,
|
|
112
|
+
resetText: weeklyCandidate.data.resetAt.toISOString()
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
if (windows.length === 0)
|
|
116
|
+
return null;
|
|
117
|
+
return {
|
|
118
|
+
windows,
|
|
119
|
+
credits: null,
|
|
120
|
+
raw: JSON.stringify(snapshot)
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
function convertJsonlRateLimits(entry, now) {
|
|
124
|
+
const nowSecs = Math.floor(now.getTime() / 1000);
|
|
125
|
+
const convertWindow = (w) => {
|
|
126
|
+
if (!w || typeof w.used_percent !== "number")
|
|
127
|
+
return null;
|
|
128
|
+
return {
|
|
129
|
+
used_percent: w.used_percent,
|
|
130
|
+
windowDurationMins: typeof w.window_duration_minutes === "number" ? w.window_duration_minutes : null,
|
|
131
|
+
resetsAt: typeof w.resets_in_seconds === "number" ? nowSecs + w.resets_in_seconds : null
|
|
132
|
+
};
|
|
133
|
+
};
|
|
134
|
+
return {
|
|
135
|
+
primary: convertWindow(entry.primary),
|
|
136
|
+
secondary: convertWindow(entry.secondary)
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
async function readCodexRateLimitsFromSessions(codexHome, now) {
|
|
140
|
+
const sessionsDir = join(codexHome, "sessions");
|
|
141
|
+
for (let dayOffset = 0; dayOffset < 7; dayOffset++) {
|
|
142
|
+
const date = new Date(now.getTime() - dayOffset * 86400000);
|
|
143
|
+
const yyyy = date.getFullYear().toString();
|
|
144
|
+
const mm = (date.getMonth() + 1).toString().padStart(2, "0");
|
|
145
|
+
const dd = date.getDate().toString().padStart(2, "0");
|
|
146
|
+
const dayDir = join(sessionsDir, yyyy, mm, dd);
|
|
147
|
+
let files;
|
|
148
|
+
try {
|
|
149
|
+
const entries = await readdir(dayDir);
|
|
150
|
+
const jsonlFiles = entries.filter((f) => f.endsWith(".jsonl"));
|
|
151
|
+
if (jsonlFiles.length === 0)
|
|
152
|
+
continue;
|
|
153
|
+
// Sort by modification time (newest first)
|
|
154
|
+
const withStats = await Promise.all(jsonlFiles.map(async (f) => {
|
|
155
|
+
const fullPath = join(dayDir, f);
|
|
156
|
+
try {
|
|
157
|
+
const s = await stat(fullPath);
|
|
158
|
+
return { name: f, mtimeMs: s.mtimeMs };
|
|
159
|
+
}
|
|
160
|
+
catch {
|
|
161
|
+
return { name: f, mtimeMs: 0 };
|
|
162
|
+
}
|
|
163
|
+
}));
|
|
164
|
+
withStats.sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
165
|
+
files = withStats.map((f) => join(dayDir, f.name));
|
|
166
|
+
}
|
|
167
|
+
catch {
|
|
168
|
+
// Directory does not exist or is not readable; skip.
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
for (const filePath of files) {
|
|
172
|
+
let content;
|
|
173
|
+
try {
|
|
174
|
+
content = await readFile(filePath, "utf8");
|
|
175
|
+
}
|
|
176
|
+
catch {
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
const lines = content.split("\n");
|
|
180
|
+
// Read in reverse (last lines = most recent)
|
|
181
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
182
|
+
const line = lines[i].trim();
|
|
183
|
+
if (!line)
|
|
184
|
+
continue;
|
|
185
|
+
let parsed;
|
|
186
|
+
try {
|
|
187
|
+
parsed = JSON.parse(line);
|
|
188
|
+
}
|
|
189
|
+
catch {
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
if (typeof parsed !== "object" || parsed === null)
|
|
193
|
+
continue;
|
|
194
|
+
const obj = parsed;
|
|
195
|
+
const payload = obj["payload"];
|
|
196
|
+
if (typeof payload !== "object" || payload === null)
|
|
197
|
+
continue;
|
|
198
|
+
const p = payload;
|
|
199
|
+
if (p["type"] !== "token_count")
|
|
200
|
+
continue;
|
|
201
|
+
const info = p["info"];
|
|
202
|
+
if (typeof info !== "object" || info === null)
|
|
203
|
+
continue;
|
|
204
|
+
const rateLimits = info["rate_limits"];
|
|
205
|
+
if (typeof rateLimits !== "object" || rateLimits === null)
|
|
206
|
+
continue;
|
|
207
|
+
return convertJsonlRateLimits(rateLimits, now);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
async function fetchCodexRateLimitsFromApi(codexHome, timeoutMs, timingSink) {
|
|
214
|
+
const authPath = join(codexHome, "auth.json");
|
|
215
|
+
let auth;
|
|
216
|
+
try {
|
|
217
|
+
const raw = await readFile(authPath, "utf8");
|
|
218
|
+
auth = JSON.parse(raw);
|
|
219
|
+
}
|
|
220
|
+
catch {
|
|
221
|
+
return null;
|
|
222
|
+
}
|
|
223
|
+
const accessToken = auth?.tokens?.access_token;
|
|
224
|
+
if (!accessToken)
|
|
225
|
+
return null;
|
|
226
|
+
const accountId = auth?.tokens?.account_id ?? auth?.account_id;
|
|
227
|
+
const headers = {
|
|
228
|
+
Authorization: `Bearer ${accessToken}`,
|
|
229
|
+
"Content-Type": "application/json"
|
|
230
|
+
};
|
|
231
|
+
if (accountId) {
|
|
232
|
+
headers["chatgpt-account-id"] = accountId;
|
|
233
|
+
}
|
|
234
|
+
const controller = new AbortController();
|
|
235
|
+
const timer = setTimeout(() => {
|
|
236
|
+
controller.abort();
|
|
237
|
+
}, timeoutMs);
|
|
238
|
+
const apiStart = Date.now();
|
|
239
|
+
try {
|
|
240
|
+
const response = await fetch("https://chatgpt.com/backend-api/wham/usage", {
|
|
241
|
+
method: "GET",
|
|
242
|
+
headers,
|
|
243
|
+
signal: controller.signal
|
|
244
|
+
});
|
|
245
|
+
if (!response.ok)
|
|
246
|
+
return null;
|
|
247
|
+
const data = (await response.json());
|
|
248
|
+
if (timingSink) {
|
|
249
|
+
timingSink("api", Date.now() - apiStart);
|
|
250
|
+
}
|
|
251
|
+
const rateLimits = data["rate_limits"];
|
|
252
|
+
if (typeof rateLimits !== "object" || rateLimits === null)
|
|
253
|
+
return null;
|
|
254
|
+
const now = new Date();
|
|
255
|
+
const nowSecs = Math.floor(now.getTime() / 1000);
|
|
256
|
+
const rl = rateLimits;
|
|
257
|
+
const convertApiWindow = (w) => {
|
|
258
|
+
if (typeof w !== "object" || w === null)
|
|
259
|
+
return null;
|
|
260
|
+
const ww = w;
|
|
261
|
+
const usedPercent = ww["used_percent"];
|
|
262
|
+
if (typeof usedPercent !== "number")
|
|
263
|
+
return null;
|
|
264
|
+
const limitWindowSeconds = ww["limit_window_seconds"];
|
|
265
|
+
const resetAfterSeconds = ww["reset_after_seconds"];
|
|
266
|
+
return {
|
|
267
|
+
used_percent: usedPercent,
|
|
268
|
+
windowDurationMins: typeof limitWindowSeconds === "number" ? limitWindowSeconds / 60 : null,
|
|
269
|
+
resetsAt: typeof resetAfterSeconds === "number" ? nowSecs + resetAfterSeconds : null
|
|
270
|
+
};
|
|
271
|
+
};
|
|
272
|
+
return {
|
|
273
|
+
primary: convertApiWindow(rl["primary"]),
|
|
274
|
+
secondary: convertApiWindow(rl["secondary"])
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
catch {
|
|
278
|
+
return null;
|
|
279
|
+
}
|
|
280
|
+
finally {
|
|
281
|
+
clearTimeout(timer);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
// ---------------------------------------------------------------------------
|
|
285
|
+
// Main export
|
|
286
|
+
// ---------------------------------------------------------------------------
|
|
287
|
+
/**
|
|
288
|
+
* Fetches Codex (ChatGPT) rate limit data.
|
|
289
|
+
*
|
|
290
|
+
* Strategy (in order):
|
|
291
|
+
* 1. Read rate_limits from the most recent JSONL session file in
|
|
292
|
+
* `~/.codex/sessions/YYYY/MM/DD/*.jsonl` (last 7 days).
|
|
293
|
+
* 2. If no session data is found, call the ChatGPT backend API using the
|
|
294
|
+
* access token from `~/.codex/auth.json`.
|
|
295
|
+
*
|
|
296
|
+
* Returns null when no data source is available or all requests fail.
|
|
297
|
+
*/
|
|
298
|
+
export async function fetchCodexRateLimits(options) {
|
|
299
|
+
const totalStart = Date.now();
|
|
300
|
+
const codexHome = resolveCodexHome(options?.codexHome);
|
|
301
|
+
const timeoutSeconds = options?.timeoutSeconds ?? 20;
|
|
302
|
+
const timingSink = options?.timingSink;
|
|
303
|
+
const sessionsStart = Date.now();
|
|
304
|
+
const now = new Date();
|
|
305
|
+
const sessionResult = await readCodexRateLimitsFromSessions(codexHome, now);
|
|
306
|
+
if (timingSink) {
|
|
307
|
+
timingSink("sessions", Date.now() - sessionsStart);
|
|
308
|
+
}
|
|
309
|
+
if (sessionResult !== null) {
|
|
310
|
+
if (timingSink) {
|
|
311
|
+
timingSink("total", Date.now() - totalStart);
|
|
312
|
+
}
|
|
313
|
+
return sessionResult;
|
|
314
|
+
}
|
|
315
|
+
const apiResult = await fetchCodexRateLimitsFromApi(codexHome, timeoutSeconds * 1000, timingSink);
|
|
316
|
+
if (timingSink) {
|
|
317
|
+
timingSink("total", Date.now() - totalStart);
|
|
318
|
+
}
|
|
319
|
+
return apiResult;
|
|
320
|
+
}
|
|
321
|
+
//# sourceMappingURL=codex.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codex.js","sourceRoot":"","sources":["../src/codex.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { CopilotUsage } from "./types.js";
|
|
2
|
+
export type { CopilotUsage } from "./types.js";
|
|
3
|
+
export type FetchCopilotRateLimitsOptions = {
|
|
4
|
+
token: string;
|
|
5
|
+
timeoutSeconds?: number;
|
|
6
|
+
apiBaseUrl?: string;
|
|
7
|
+
apiVersion?: string;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Parses a GitHub Copilot user info response body into a CopilotUsage snapshot.
|
|
11
|
+
*
|
|
12
|
+
* Returns null when the response does not contain the expected fields.
|
|
13
|
+
*/
|
|
14
|
+
export declare function parseCopilotUserInfo(data: unknown, _now?: Date): CopilotUsage | null;
|
|
15
|
+
/**
|
|
16
|
+
* Parses a Copilot quota snapshot from an HTTP response header value.
|
|
17
|
+
*
|
|
18
|
+
* The header is formatted as URL search params, e.g.:
|
|
19
|
+
* `ent=3000&rem=64&rst=2026-02-15T00:00:00Z&ov=0&ovPerm=false`
|
|
20
|
+
*/
|
|
21
|
+
export declare function parseCopilotQuotaHeader(headerValue: string, now?: Date): CopilotUsage | null;
|
|
22
|
+
/**
|
|
23
|
+
* Fetches Copilot quota usage from the GitHub Copilot internal API.
|
|
24
|
+
*
|
|
25
|
+
* Requires a valid GitHub personal access token with `copilot` scope.
|
|
26
|
+
* Falls back to the `x-quota-snapshot-*` response header when the body
|
|
27
|
+
* does not contain the expected fields.
|
|
28
|
+
*/
|
|
29
|
+
export declare function fetchCopilotRateLimits(options: FetchCopilotRateLimitsOptions, now?: Date): Promise<CopilotUsage | null>;
|
|
30
|
+
//# sourceMappingURL=copilot.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"copilot.d.ts","sourceRoot":"","sources":["../src/copilot.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C,YAAY,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAK/C,MAAM,MAAM,6BAA6B,GAAG;IAC1C,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AA4BF;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,GAAE,IAAiB,GAAG,YAAY,GAAG,IAAI,CA8BhG;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CACrC,WAAW,EAAE,MAAM,EACnB,GAAG,GAAE,IAAiB,GACrB,YAAY,GAAG,IAAI,CA6BrB;AAED;;;;;;GAMG;AACH,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,6BAA6B,EACtC,GAAG,GAAE,IAAiB,GACrB,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAmD9B"}
|
package/dist/copilot.js
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
const DEFAULT_API_BASE_URL = "https://api.github.com";
|
|
2
|
+
const DEFAULT_API_VERSION = "2025-05-01";
|
|
3
|
+
function isRecord(value) {
|
|
4
|
+
return typeof value === "object" && value !== null;
|
|
5
|
+
}
|
|
6
|
+
function toNumber(value) {
|
|
7
|
+
if (typeof value === "number") {
|
|
8
|
+
return Number.isFinite(value) ? value : null;
|
|
9
|
+
}
|
|
10
|
+
if (typeof value === "string") {
|
|
11
|
+
const trimmed = value.trim();
|
|
12
|
+
if (!trimmed)
|
|
13
|
+
return null;
|
|
14
|
+
const parsed = Number.parseFloat(trimmed);
|
|
15
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
16
|
+
}
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
function normalizePercent(value) {
|
|
20
|
+
return Math.min(100, Math.max(0, value));
|
|
21
|
+
}
|
|
22
|
+
function normalizeApiBaseUrl(value) {
|
|
23
|
+
const base = value?.trim() || DEFAULT_API_BASE_URL;
|
|
24
|
+
return base.endsWith("/") ? base.slice(0, -1) : base;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Parses a GitHub Copilot user info response body into a CopilotUsage snapshot.
|
|
28
|
+
*
|
|
29
|
+
* Returns null when the response does not contain the expected fields.
|
|
30
|
+
*/
|
|
31
|
+
export function parseCopilotUserInfo(data, _now = new Date()) {
|
|
32
|
+
if (!isRecord(data))
|
|
33
|
+
return null;
|
|
34
|
+
const quotaSnapshots = data.quota_snapshots;
|
|
35
|
+
if (!isRecord(quotaSnapshots))
|
|
36
|
+
return null;
|
|
37
|
+
const premium = quotaSnapshots.premium_interactions;
|
|
38
|
+
if (!isRecord(premium))
|
|
39
|
+
return null;
|
|
40
|
+
const entitlement = toNumber(premium.entitlement);
|
|
41
|
+
const percentRemaining = toNumber(premium.percent_remaining);
|
|
42
|
+
const resetText = typeof data.quota_reset_date === "string" ? data.quota_reset_date : null;
|
|
43
|
+
if (entitlement === null || percentRemaining === null || !resetText)
|
|
44
|
+
return null;
|
|
45
|
+
const resetAt = new Date(resetText);
|
|
46
|
+
if (Number.isNaN(resetAt.getTime()))
|
|
47
|
+
return null;
|
|
48
|
+
const overageUsed = toNumber(premium.overage_count) ?? 0;
|
|
49
|
+
const overageEnabled = premium.overage_permitted === true;
|
|
50
|
+
return {
|
|
51
|
+
percentRemaining: normalizePercent(percentRemaining),
|
|
52
|
+
resetAt,
|
|
53
|
+
entitlement,
|
|
54
|
+
overageUsed,
|
|
55
|
+
overageEnabled,
|
|
56
|
+
source: "user",
|
|
57
|
+
raw: data
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Parses a Copilot quota snapshot from an HTTP response header value.
|
|
62
|
+
*
|
|
63
|
+
* The header is formatted as URL search params, e.g.:
|
|
64
|
+
* `ent=3000&rem=64&rst=2026-02-15T00:00:00Z&ov=0&ovPerm=false`
|
|
65
|
+
*/
|
|
66
|
+
export function parseCopilotQuotaHeader(headerValue, now = new Date()) {
|
|
67
|
+
const trimmed = headerValue.trim();
|
|
68
|
+
if (!trimmed)
|
|
69
|
+
return null;
|
|
70
|
+
const params = new URLSearchParams(trimmed);
|
|
71
|
+
const entitlement = toNumber(params.get("ent"));
|
|
72
|
+
const percentRemaining = toNumber(params.get("rem"));
|
|
73
|
+
if (entitlement === null || percentRemaining === null)
|
|
74
|
+
return null;
|
|
75
|
+
const resetText = params.get("rst");
|
|
76
|
+
const resetAt = resetText ? new Date(resetText) : new Date(now.getTime());
|
|
77
|
+
if (resetText && Number.isNaN(resetAt.getTime()))
|
|
78
|
+
return null;
|
|
79
|
+
if (!resetText) {
|
|
80
|
+
resetAt.setMonth(resetAt.getMonth() + 1);
|
|
81
|
+
}
|
|
82
|
+
const overageUsed = toNumber(params.get("ov")) ?? 0;
|
|
83
|
+
const overageEnabled = params.get("ovPerm") === "true";
|
|
84
|
+
return {
|
|
85
|
+
percentRemaining: normalizePercent(percentRemaining),
|
|
86
|
+
resetAt,
|
|
87
|
+
entitlement,
|
|
88
|
+
overageUsed,
|
|
89
|
+
overageEnabled,
|
|
90
|
+
source: "header",
|
|
91
|
+
raw: headerValue
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Fetches Copilot quota usage from the GitHub Copilot internal API.
|
|
96
|
+
*
|
|
97
|
+
* Requires a valid GitHub personal access token with `copilot` scope.
|
|
98
|
+
* Falls back to the `x-quota-snapshot-*` response header when the body
|
|
99
|
+
* does not contain the expected fields.
|
|
100
|
+
*/
|
|
101
|
+
export async function fetchCopilotRateLimits(options, now = new Date()) {
|
|
102
|
+
const baseUrl = normalizeApiBaseUrl(options.apiBaseUrl);
|
|
103
|
+
const url = `${baseUrl}/copilot_internal/user`;
|
|
104
|
+
const controller = new AbortController();
|
|
105
|
+
const timeoutSeconds = options.timeoutSeconds ?? 20;
|
|
106
|
+
const timeout = setTimeout(() => controller.abort(), timeoutSeconds * 1000);
|
|
107
|
+
const apiVersion = options.apiVersion?.trim() || DEFAULT_API_VERSION;
|
|
108
|
+
try {
|
|
109
|
+
const response = await fetch(url, {
|
|
110
|
+
method: "GET",
|
|
111
|
+
headers: {
|
|
112
|
+
Authorization: `token ${options.token}`,
|
|
113
|
+
Accept: "application/vnd.github+json",
|
|
114
|
+
"X-GitHub-Api-Version": apiVersion,
|
|
115
|
+
"User-Agent": "ai-quota"
|
|
116
|
+
},
|
|
117
|
+
signal: controller.signal
|
|
118
|
+
});
|
|
119
|
+
const headerValue = response.headers.get("x-quota-snapshot-premium_interactions") ||
|
|
120
|
+
response.headers.get("x-quota-snapshot-premium_models");
|
|
121
|
+
const headerUsage = headerValue ? parseCopilotQuotaHeader(headerValue, now) : null;
|
|
122
|
+
const bodyText = await response.text();
|
|
123
|
+
if (!response.ok) {
|
|
124
|
+
throw new Error(`Copilot user info request failed (${response.status} ${response.statusText}).`);
|
|
125
|
+
}
|
|
126
|
+
let parsed = null;
|
|
127
|
+
if (bodyText.trim()) {
|
|
128
|
+
try {
|
|
129
|
+
parsed = JSON.parse(bodyText);
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
parsed = null;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
const usage = parseCopilotUserInfo(parsed, now);
|
|
136
|
+
return usage ?? headerUsage;
|
|
137
|
+
}
|
|
138
|
+
catch (error) {
|
|
139
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
140
|
+
throw new Error("Copilot user info request timed out.");
|
|
141
|
+
}
|
|
142
|
+
throw error;
|
|
143
|
+
}
|
|
144
|
+
finally {
|
|
145
|
+
clearTimeout(timeout);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
//# sourceMappingURL=copilot.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"copilot.js","sourceRoot":"","sources":["../src/copilot.ts"],"names":[],"mappings":"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"}
|
package/dist/gemini.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { GeminiUsage } from "./types.js";
|
|
2
|
+
export type { GeminiUsage, GeminiModelUsage } from "./types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Fetches Gemini quota usage from the Cloud Code Assist API.
|
|
5
|
+
*
|
|
6
|
+
* Reads OAuth credentials from `~/.gemini/oauth_creds.json` and automatically
|
|
7
|
+
* refreshes the access token when needed. Returns null on any error.
|
|
8
|
+
*
|
|
9
|
+
* The environment variables `AGENT_RUNNER_GEMINI_OAUTH_CLIENT_ID` and
|
|
10
|
+
* `AGENT_RUNNER_GEMINI_OAUTH_CLIENT_SECRET` can override the OAuth client
|
|
11
|
+
* credentials when the Gemini CLI is not installed.
|
|
12
|
+
*/
|
|
13
|
+
export declare function fetchGeminiRateLimits(): Promise<GeminiUsage | null>;
|
|
14
|
+
//# sourceMappingURL=gemini.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gemini.d.ts","sourceRoot":"","sources":["../src/gemini.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,YAAY,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAiNhE;;;;;;;;;GASG;AACH,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAiFzE"}
|