omniagent 0.1.8 → 0.1.11
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/claude-C0SMAkM3.js +388 -0
- package/dist/cli.js +50 -20
- package/dist/codex-0b2YLh_8.js +540 -0
- package/dist/gemini-BVRg6OMO.js +437 -0
- package/package.json +1 -1
- package/dist/claude-Dmv_YFKX.js +0 -146
- package/dist/codex-Cl1dWwMk.js +0 -274
- package/dist/gemini-CskI3Qjp.js +0 -168
|
@@ -0,0 +1,540 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { c as cleanControlOutput, b as parsePercentRemaining, m as makeUsageLimit, d as parseResetText } from "./cli.js";
|
|
4
|
+
import { r as runPtyScenario, e as enterKey, t as typeTextSteps } from "./pty-CZBSAJzE.js";
|
|
5
|
+
const CODEX_WINDOWS = [
|
|
6
|
+
["main", "5h", "main5hLimit"],
|
|
7
|
+
["main", "weekly", "mainWeeklyLimit"],
|
|
8
|
+
["spark", "5h", "spark5hLimit"],
|
|
9
|
+
["spark", "weekly", "sparkWeeklyLimit"]
|
|
10
|
+
];
|
|
11
|
+
const CLEAR_LINE = "";
|
|
12
|
+
const CODEX_AUTH_PATH = [".codex", "auth.json"];
|
|
13
|
+
const CODEX_INSTALLATION_ID_PATH = [".codex", "installation_id"];
|
|
14
|
+
const CODEX_USAGE_API_URL = "https://chatgpt.com/backend-api/wham/usage";
|
|
15
|
+
const CODEX_USAGE_API_TIMEOUT_MS = 1e4;
|
|
16
|
+
async function extractCodexUsage(context) {
|
|
17
|
+
try {
|
|
18
|
+
return await extractCodexUsageFromApi(context);
|
|
19
|
+
} catch (error) {
|
|
20
|
+
if (context.signal.aborted) {
|
|
21
|
+
throw error;
|
|
22
|
+
}
|
|
23
|
+
return extractCodexUsageFromTui(context);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
async function extractCodexUsageFromTui(context) {
|
|
27
|
+
const command = context.command ?? context.launch?.command ?? "codex";
|
|
28
|
+
const ptyResult = await runPtyScenario({
|
|
29
|
+
command,
|
|
30
|
+
args: context.launch?.args ?? ["--no-alt-screen"],
|
|
31
|
+
cwd: context.homeDir,
|
|
32
|
+
cols: 100,
|
|
33
|
+
rows: 40,
|
|
34
|
+
timeoutMs: context.launch?.timeoutMs ?? 6e4,
|
|
35
|
+
signal: context.signal,
|
|
36
|
+
debug: context.debug,
|
|
37
|
+
steps: [
|
|
38
|
+
{ waitFor: isCodexPromptReadyOrTrustPrompt, waitForTimeoutMs: 1e4 },
|
|
39
|
+
{ write: enterKey(), skipIf: isCodexPromptReady },
|
|
40
|
+
{ waitFor: isCodexPromptReady, waitForTimeoutMs: 1e4 },
|
|
41
|
+
...typeTextSteps("/status", 20),
|
|
42
|
+
{ write: enterKey() },
|
|
43
|
+
{
|
|
44
|
+
waitFor: hasCodexStatusResponse,
|
|
45
|
+
waitForTimeoutMs: 15e3,
|
|
46
|
+
capture: "status",
|
|
47
|
+
captureWaitMs: 500
|
|
48
|
+
},
|
|
49
|
+
{ waitMs: 5e3, skipIf: hasCodexStatusLimits },
|
|
50
|
+
{ write: `${CLEAR_LINE}/status${enterKey()}`, skipIf: hasCodexStatusLimits },
|
|
51
|
+
{
|
|
52
|
+
waitFor: hasCodexStatusLimits,
|
|
53
|
+
waitForTimeoutMs: 15e3,
|
|
54
|
+
skipIf: hasCodexStatusLimits,
|
|
55
|
+
optional: true,
|
|
56
|
+
capture: "statusRetry",
|
|
57
|
+
captureWaitMs: 500
|
|
58
|
+
},
|
|
59
|
+
{ write: `${CLEAR_LINE}/exit${enterKey()}` },
|
|
60
|
+
{ waitMs: 500 }
|
|
61
|
+
]
|
|
62
|
+
});
|
|
63
|
+
const parsed = selectCodexStatus(ptyResult);
|
|
64
|
+
const result = buildCodexUsageResult(parsed, {
|
|
65
|
+
targetId: context.targetId,
|
|
66
|
+
displayName: context.displayName,
|
|
67
|
+
now: context.now,
|
|
68
|
+
command
|
|
69
|
+
});
|
|
70
|
+
return {
|
|
71
|
+
...result,
|
|
72
|
+
debug: ptyResult.debug.length > 0 ? ptyResult.debug : void 0
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
async function extractCodexUsageFromApi(context) {
|
|
76
|
+
const command = context.command ?? context.launch?.command ?? "codex";
|
|
77
|
+
const auth = await readCodexBackendAuth(context.homeDir);
|
|
78
|
+
if (auth == null) {
|
|
79
|
+
throw new Error("Codex ChatGPT backend auth was not available.");
|
|
80
|
+
}
|
|
81
|
+
const installationId = await readCodexInstallationId(context.homeDir);
|
|
82
|
+
const response = await fetchCodexUsage(auth, installationId, context.signal);
|
|
83
|
+
if (response.status >= 400) {
|
|
84
|
+
throw new Error(`Codex usage API returned HTTP ${response.status}.`);
|
|
85
|
+
}
|
|
86
|
+
const body = await response.json();
|
|
87
|
+
return buildCodexApiUsageResult(body, {
|
|
88
|
+
targetId: context.targetId,
|
|
89
|
+
displayName: context.displayName,
|
|
90
|
+
now: context.now,
|
|
91
|
+
command
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
function buildCodexApiUsageResult(payload, context) {
|
|
95
|
+
const usage = isRecord(payload) ? payload : {};
|
|
96
|
+
const limits = [
|
|
97
|
+
...buildCodexApiRateLimitUsageLimits({
|
|
98
|
+
targetId: context.targetId,
|
|
99
|
+
scope: "main",
|
|
100
|
+
rateLimit: usage.rate_limit,
|
|
101
|
+
now: context.now,
|
|
102
|
+
requireBothWindows: true
|
|
103
|
+
}),
|
|
104
|
+
...buildCodexApiAdditionalUsageLimits(usage.additional_rate_limits, context)
|
|
105
|
+
];
|
|
106
|
+
const hasMainHourly = limits.some((limit) => limit.scope === "main" && limit.window === "hourly");
|
|
107
|
+
const hasMainWeekly = limits.some((limit) => limit.scope === "main" && limit.window === "weekly");
|
|
108
|
+
if (!hasMainHourly || !hasMainWeekly) {
|
|
109
|
+
throw new Error("Codex usage API response did not include complete main rate-limit windows.");
|
|
110
|
+
}
|
|
111
|
+
return {
|
|
112
|
+
targetId: context.targetId,
|
|
113
|
+
displayName: context.displayName,
|
|
114
|
+
command: context.command,
|
|
115
|
+
limits
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
function extractCodexBackendAuth(blob) {
|
|
119
|
+
let parsed;
|
|
120
|
+
try {
|
|
121
|
+
parsed = JSON.parse(blob);
|
|
122
|
+
} catch {
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
if (!isRecord(parsed) || !isRecord(parsed.tokens)) {
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
const accessToken = parsed.tokens.access_token;
|
|
129
|
+
const accountId = parsed.tokens.account_id;
|
|
130
|
+
if (typeof accessToken !== "string" || typeof accountId !== "string") {
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
if (!accessToken.trim() || !accountId.trim()) {
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
return {
|
|
137
|
+
accessToken,
|
|
138
|
+
accountId
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
async function readCodexBackendAuth(homeDir) {
|
|
142
|
+
try {
|
|
143
|
+
const raw = await readFile(path.join(homeDir, ...CODEX_AUTH_PATH), "utf8");
|
|
144
|
+
return extractCodexBackendAuth(raw);
|
|
145
|
+
} catch {
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
async function readCodexInstallationId(homeDir) {
|
|
150
|
+
try {
|
|
151
|
+
const installationId = await readFile(
|
|
152
|
+
path.join(homeDir, ...CODEX_INSTALLATION_ID_PATH),
|
|
153
|
+
"utf8"
|
|
154
|
+
);
|
|
155
|
+
const trimmed = installationId.trim();
|
|
156
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
157
|
+
} catch {
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
async function fetchCodexUsage(auth, installationId, parentSignal) {
|
|
162
|
+
const controller = new AbortController();
|
|
163
|
+
const timeout = setTimeout(() => {
|
|
164
|
+
controller.abort(new Error("Codex usage API request timed out."));
|
|
165
|
+
}, CODEX_USAGE_API_TIMEOUT_MS);
|
|
166
|
+
const abortFromParent = () => {
|
|
167
|
+
controller.abort(parentSignal.reason);
|
|
168
|
+
};
|
|
169
|
+
parentSignal.addEventListener("abort", abortFromParent, { once: true });
|
|
170
|
+
try {
|
|
171
|
+
const headers = {
|
|
172
|
+
accept: "application/json",
|
|
173
|
+
authorization: `Bearer ${auth.accessToken}`,
|
|
174
|
+
"chatgpt-account-id": auth.accountId,
|
|
175
|
+
"user-agent": "codex-cli"
|
|
176
|
+
};
|
|
177
|
+
if (installationId != null) {
|
|
178
|
+
headers["x-codex-installation-id"] = installationId;
|
|
179
|
+
}
|
|
180
|
+
return await fetch(CODEX_USAGE_API_URL, {
|
|
181
|
+
method: "GET",
|
|
182
|
+
headers,
|
|
183
|
+
signal: controller.signal
|
|
184
|
+
});
|
|
185
|
+
} finally {
|
|
186
|
+
clearTimeout(timeout);
|
|
187
|
+
parentSignal.removeEventListener("abort", abortFromParent);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
function buildCodexApiAdditionalUsageLimits(additionalRateLimits, context) {
|
|
191
|
+
if (!Array.isArray(additionalRateLimits)) {
|
|
192
|
+
return [];
|
|
193
|
+
}
|
|
194
|
+
return additionalRateLimits.flatMap((entry) => {
|
|
195
|
+
if (!isRecord(entry)) {
|
|
196
|
+
return [];
|
|
197
|
+
}
|
|
198
|
+
const limitName = typeof entry.limit_name === "string" ? entry.limit_name : "";
|
|
199
|
+
const meteredFeature = typeof entry.metered_feature === "string" ? entry.metered_feature : "";
|
|
200
|
+
const scope = codexAdditionalLimitScope(limitName, meteredFeature);
|
|
201
|
+
return buildCodexApiRateLimitUsageLimits({
|
|
202
|
+
targetId: context.targetId,
|
|
203
|
+
scope,
|
|
204
|
+
rateLimit: entry.rate_limit,
|
|
205
|
+
now: context.now,
|
|
206
|
+
labelPrefix: scope === "spark" ? void 0 : limitName || meteredFeature || scope,
|
|
207
|
+
requireBothWindows: false
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
function buildCodexApiRateLimitUsageLimits(options) {
|
|
212
|
+
if (!isRecord(options.rateLimit)) {
|
|
213
|
+
return [];
|
|
214
|
+
}
|
|
215
|
+
const windows = [
|
|
216
|
+
["primary", options.rateLimit.primary_window],
|
|
217
|
+
["secondary", options.rateLimit.secondary_window]
|
|
218
|
+
];
|
|
219
|
+
const limits = windows.flatMap(([kind, window]) => {
|
|
220
|
+
const limit = makeCodexApiUsageLimit({
|
|
221
|
+
targetId: options.targetId,
|
|
222
|
+
scope: options.scope,
|
|
223
|
+
windowKind: kind,
|
|
224
|
+
window,
|
|
225
|
+
now: options.now,
|
|
226
|
+
labelPrefix: options.labelPrefix
|
|
227
|
+
});
|
|
228
|
+
return limit == null ? [] : [limit];
|
|
229
|
+
});
|
|
230
|
+
if (options.requireBothWindows && limits.length !== 2) {
|
|
231
|
+
return [];
|
|
232
|
+
}
|
|
233
|
+
return limits;
|
|
234
|
+
}
|
|
235
|
+
function makeCodexApiUsageLimit(options) {
|
|
236
|
+
if (!isRecord(options.window)) {
|
|
237
|
+
return null;
|
|
238
|
+
}
|
|
239
|
+
const percentUsed = parseApiNumber(options.window.used_percent);
|
|
240
|
+
if (percentUsed == null) {
|
|
241
|
+
return null;
|
|
242
|
+
}
|
|
243
|
+
const resetAt = parseApiEpochSeconds(options.window.reset_at);
|
|
244
|
+
const window = codexApiWindowName(options.window, options.windowKind);
|
|
245
|
+
const percentUsedClamped = clampPercent(percentUsed);
|
|
246
|
+
const percentRemaining = 100 - percentUsedClamped;
|
|
247
|
+
const resetText = resetAt == null ? null : `resets ${resetAt}`;
|
|
248
|
+
const label = options.labelPrefix == null ? void 0 : `${options.labelPrefix} ${formatWindowLabel(window)}`;
|
|
249
|
+
const raw = `${formatPercent(percentRemaining)} left${resetText == null ? "" : ` (${resetText})`}`;
|
|
250
|
+
const limit = makeUsageLimit({
|
|
251
|
+
targetId: options.targetId,
|
|
252
|
+
scope: options.scope,
|
|
253
|
+
window,
|
|
254
|
+
label,
|
|
255
|
+
percentUsed: percentUsedClamped,
|
|
256
|
+
percentRemaining,
|
|
257
|
+
resetText,
|
|
258
|
+
raw,
|
|
259
|
+
now: options.now
|
|
260
|
+
});
|
|
261
|
+
return {
|
|
262
|
+
...limit,
|
|
263
|
+
resetAt
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
function codexAdditionalLimitScope(limitName, meteredFeature) {
|
|
267
|
+
if (/\bspark\b/i.test(limitName) || /\bbengalfox\b/i.test(meteredFeature)) {
|
|
268
|
+
return "spark";
|
|
269
|
+
}
|
|
270
|
+
const source = limitName || meteredFeature || "additional";
|
|
271
|
+
const normalized = source.trim().toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
|
|
272
|
+
return normalized || "additional";
|
|
273
|
+
}
|
|
274
|
+
function codexApiWindowName(window, kind) {
|
|
275
|
+
const seconds = parseApiNumber(window.limit_window_seconds);
|
|
276
|
+
if (seconds === 18e3) {
|
|
277
|
+
return "5h";
|
|
278
|
+
}
|
|
279
|
+
if (seconds === 604800) {
|
|
280
|
+
return "weekly";
|
|
281
|
+
}
|
|
282
|
+
return kind === "primary" ? "5h" : "weekly";
|
|
283
|
+
}
|
|
284
|
+
function formatWindowLabel(window) {
|
|
285
|
+
return window === "weekly" ? "Weekly" : window === "5h" ? "5h" : window;
|
|
286
|
+
}
|
|
287
|
+
function parseApiNumber(value) {
|
|
288
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
289
|
+
return value;
|
|
290
|
+
}
|
|
291
|
+
if (typeof value !== "string" || !value.trim()) {
|
|
292
|
+
return null;
|
|
293
|
+
}
|
|
294
|
+
const parsed = Number(value);
|
|
295
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
296
|
+
}
|
|
297
|
+
function parseApiEpochSeconds(value) {
|
|
298
|
+
const seconds = parseApiNumber(value);
|
|
299
|
+
if (seconds == null || seconds <= 0) {
|
|
300
|
+
return null;
|
|
301
|
+
}
|
|
302
|
+
return new Date(seconds * 1e3).toISOString();
|
|
303
|
+
}
|
|
304
|
+
function clampPercent(value) {
|
|
305
|
+
return Math.max(0, Math.min(100, value));
|
|
306
|
+
}
|
|
307
|
+
function formatPercent(value) {
|
|
308
|
+
return Number.isInteger(value) ? `${value}%` : `${Number(value.toFixed(2))}%`;
|
|
309
|
+
}
|
|
310
|
+
function isRecord(value) {
|
|
311
|
+
return value != null && typeof value === "object" && !Array.isArray(value);
|
|
312
|
+
}
|
|
313
|
+
function selectCodexStatus(result) {
|
|
314
|
+
const snapshots = [
|
|
315
|
+
result.snapshots.statusRetry,
|
|
316
|
+
result,
|
|
317
|
+
result.snapshots.status
|
|
318
|
+
];
|
|
319
|
+
for (const snapshot of snapshots) {
|
|
320
|
+
if (snapshot == null) {
|
|
321
|
+
continue;
|
|
322
|
+
}
|
|
323
|
+
for (const content of [`${snapshot.screen}
|
|
324
|
+
${snapshot.raw}`, snapshot.screen, snapshot.raw]) {
|
|
325
|
+
const parsed = parseCodexStatus(cleanControlOutput(content));
|
|
326
|
+
if (parsed.main5hLimit || parsed.mainWeeklyLimit) {
|
|
327
|
+
return parsed;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
const fallback = result.snapshots.statusRetry ?? result.snapshots.status ?? result;
|
|
332
|
+
return parseCodexStatus(cleanControlOutput(`${fallback.screen}
|
|
333
|
+
${fallback.raw}`));
|
|
334
|
+
}
|
|
335
|
+
function isCodexPromptReady(snapshot) {
|
|
336
|
+
const cleanedOutput = cleanControlOutput(`${snapshot.screen}
|
|
337
|
+
${snapshot.raw}`);
|
|
338
|
+
return /(?:\u203a|>)\s/.test(cleanedOutput) && /\bContext\b/i.test(cleanedOutput);
|
|
339
|
+
}
|
|
340
|
+
function isCodexPromptReadyOrTrustPrompt(snapshot) {
|
|
341
|
+
return isCodexPromptReady(snapshot) || isCodexTrustPrompt(snapshot);
|
|
342
|
+
}
|
|
343
|
+
function isCodexTrustPrompt(snapshot) {
|
|
344
|
+
const cleanedOutput = cleanControlOutput(`${snapshot.screen}
|
|
345
|
+
${snapshot.raw}`);
|
|
346
|
+
return /do you trust the contents of this directory/i.test(cleanedOutput);
|
|
347
|
+
}
|
|
348
|
+
function hasCodexStatusLimits(snapshot) {
|
|
349
|
+
const cleanedOutput = cleanControlOutput(`${snapshot.screen}
|
|
350
|
+
${snapshot.raw}`);
|
|
351
|
+
const parsed = parseCodexStatus(cleanedOutput);
|
|
352
|
+
return Boolean(parsed.main5hLimit && parsed.mainWeeklyLimit);
|
|
353
|
+
}
|
|
354
|
+
function hasCodexStatusResponse(snapshot) {
|
|
355
|
+
const cleanedOutput = cleanControlOutput(`${snapshot.screen}
|
|
356
|
+
${snapshot.raw}`);
|
|
357
|
+
const parsed = parseCodexStatus(cleanedOutput);
|
|
358
|
+
return Boolean(parsed.main5hLimit || parsed.mainWeeklyLimit) || /refresh requested/i.test(cleanedOutput);
|
|
359
|
+
}
|
|
360
|
+
function buildCodexUsageResult(parsed, context) {
|
|
361
|
+
assertAnyRequiredCodexLimit(parsed);
|
|
362
|
+
const errors = buildCodexUsageErrors(parsed, context);
|
|
363
|
+
return {
|
|
364
|
+
targetId: context.targetId,
|
|
365
|
+
displayName: context.displayName,
|
|
366
|
+
command: context.command,
|
|
367
|
+
limits: buildCodexUsageLimits(parsed, context),
|
|
368
|
+
errors: errors.length > 0 ? errors : void 0
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
function buildCodexUsageLimits(parsed, context) {
|
|
372
|
+
return CODEX_WINDOWS.flatMap(([scope, window, key]) => {
|
|
373
|
+
const raw = parsed[key]?.trim() ?? "";
|
|
374
|
+
if (!raw) {
|
|
375
|
+
return [];
|
|
376
|
+
}
|
|
377
|
+
const percentRemaining = parsePercentRemaining(raw);
|
|
378
|
+
return [
|
|
379
|
+
makeUsageLimit({
|
|
380
|
+
targetId: context.targetId,
|
|
381
|
+
scope,
|
|
382
|
+
window,
|
|
383
|
+
percentUsed: percentRemaining == null ? null : 100 - percentRemaining,
|
|
384
|
+
percentRemaining,
|
|
385
|
+
resetText: parseResetText(raw),
|
|
386
|
+
raw,
|
|
387
|
+
now: context.now
|
|
388
|
+
})
|
|
389
|
+
];
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
function parseCodexStatus(cleanedOutput) {
|
|
393
|
+
const values = {};
|
|
394
|
+
let inStatus = false;
|
|
395
|
+
let section = "main";
|
|
396
|
+
let key = "";
|
|
397
|
+
for (const rawLine of cleanedOutput.split(/\n/)) {
|
|
398
|
+
const normalizedLine = normalizeCodexLine(rawLine);
|
|
399
|
+
if (!inStatus) {
|
|
400
|
+
if (normalizedLine === "Model:" || normalizedLine.startsWith("Model: ")) {
|
|
401
|
+
inStatus = true;
|
|
402
|
+
key = "model";
|
|
403
|
+
setValue(values, key, normalizedLine.slice("Model:".length).trim());
|
|
404
|
+
}
|
|
405
|
+
continue;
|
|
406
|
+
}
|
|
407
|
+
if (normalizedLine.includes("/exit exit Codex")) {
|
|
408
|
+
break;
|
|
409
|
+
}
|
|
410
|
+
if (normalizedLine.startsWith("› ")) {
|
|
411
|
+
key = "";
|
|
412
|
+
continue;
|
|
413
|
+
}
|
|
414
|
+
let line = normalizedLine;
|
|
415
|
+
if (!line || line === "[" || line === "]") {
|
|
416
|
+
continue;
|
|
417
|
+
}
|
|
418
|
+
line = line.replace(/^\]\s*/, "").trim();
|
|
419
|
+
if (!line) {
|
|
420
|
+
continue;
|
|
421
|
+
}
|
|
422
|
+
const labelMatch = /^([-A-Za-z0-9_. ]+):\s*(.*)$/.exec(line);
|
|
423
|
+
if (labelMatch != null) {
|
|
424
|
+
const label = labelMatch[1].trim();
|
|
425
|
+
const inlineValue = labelMatch[2].trim();
|
|
426
|
+
if (isCodexSparkLimitLabel(label)) {
|
|
427
|
+
section = "spark";
|
|
428
|
+
key = "";
|
|
429
|
+
continue;
|
|
430
|
+
}
|
|
431
|
+
key = labelToCodexKey(label, section);
|
|
432
|
+
if (key && inlineValue) {
|
|
433
|
+
setValue(values, key, inlineValue);
|
|
434
|
+
}
|
|
435
|
+
continue;
|
|
436
|
+
}
|
|
437
|
+
if (key && isCodexContinuationLine(line, values[key])) {
|
|
438
|
+
appendValue(values, key, line);
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
return {
|
|
442
|
+
model: values.model ?? "",
|
|
443
|
+
directory: values.directory ?? "",
|
|
444
|
+
permissions: values.permissions ?? "",
|
|
445
|
+
agentsMd: values.agentsMd ?? "",
|
|
446
|
+
account: values.account ?? "",
|
|
447
|
+
collaborationMode: values.collaborationMode ?? "",
|
|
448
|
+
session: values.session ?? "",
|
|
449
|
+
main5hLimit: values.main5hLimit ?? "",
|
|
450
|
+
mainWeeklyLimit: values.mainWeeklyLimit ?? "",
|
|
451
|
+
spark5hLimit: values.spark5hLimit ?? "",
|
|
452
|
+
sparkWeeklyLimit: values.sparkWeeklyLimit ?? ""
|
|
453
|
+
};
|
|
454
|
+
}
|
|
455
|
+
function assertAnyRequiredCodexLimit(parsed) {
|
|
456
|
+
if (parsed.main5hLimit || parsed.mainWeeklyLimit) {
|
|
457
|
+
return;
|
|
458
|
+
}
|
|
459
|
+
throw new Error("Codex usage output did not include the required 5h and weekly limit rows.");
|
|
460
|
+
}
|
|
461
|
+
function buildCodexUsageErrors(parsed, context) {
|
|
462
|
+
const missing = [];
|
|
463
|
+
if (!parsed.main5hLimit) {
|
|
464
|
+
missing.push("5h");
|
|
465
|
+
}
|
|
466
|
+
if (!parsed.mainWeeklyLimit) {
|
|
467
|
+
missing.push("weekly");
|
|
468
|
+
}
|
|
469
|
+
if (missing.length === 0) {
|
|
470
|
+
return [];
|
|
471
|
+
}
|
|
472
|
+
return [
|
|
473
|
+
{
|
|
474
|
+
targetId: context.targetId,
|
|
475
|
+
displayName: context.displayName,
|
|
476
|
+
code: "partial_parse",
|
|
477
|
+
message: `Codex usage output did not include the ${missing.join(" and ")} limit row.`
|
|
478
|
+
}
|
|
479
|
+
];
|
|
480
|
+
}
|
|
481
|
+
function isCodexSparkLimitLabel(label) {
|
|
482
|
+
return /\bspark\b/i.test(label) && /\blimit\b/i.test(label);
|
|
483
|
+
}
|
|
484
|
+
function labelToCodexKey(label, section) {
|
|
485
|
+
if (label === "Model") return "model";
|
|
486
|
+
if (label === "Directory") return "directory";
|
|
487
|
+
if (label === "Permissions") return "permissions";
|
|
488
|
+
if (label === "Agents.md") return "agentsMd";
|
|
489
|
+
if (label === "Account") return "account";
|
|
490
|
+
if (label === "Collaboration mode") return "collaborationMode";
|
|
491
|
+
if (label === "Session") return "session";
|
|
492
|
+
if (label === "5h limit") return section === "spark" ? "spark5hLimit" : "main5hLimit";
|
|
493
|
+
if (label === "Weekly limit") {
|
|
494
|
+
return section === "spark" ? "sparkWeeklyLimit" : "mainWeeklyLimit";
|
|
495
|
+
}
|
|
496
|
+
return "";
|
|
497
|
+
}
|
|
498
|
+
function setValue(values, key, value) {
|
|
499
|
+
const sanitized = sanitizeCodexValue(value);
|
|
500
|
+
if (!sanitized) {
|
|
501
|
+
return;
|
|
502
|
+
}
|
|
503
|
+
values[key] = sanitized;
|
|
504
|
+
}
|
|
505
|
+
function appendValue(values, key, value) {
|
|
506
|
+
const sanitized = sanitizeCodexValue(value);
|
|
507
|
+
if (!sanitized) {
|
|
508
|
+
return;
|
|
509
|
+
}
|
|
510
|
+
values[key] = values[key] == null || values[key] === "" ? sanitized : `${values[key]} ${sanitized}`;
|
|
511
|
+
}
|
|
512
|
+
function isCodexContinuationLine(line, currentValue) {
|
|
513
|
+
if (!line) {
|
|
514
|
+
return false;
|
|
515
|
+
}
|
|
516
|
+
const hasPercent = /\d+(?:\.\d+)?\s*%/.test(line);
|
|
517
|
+
const currentHasPercent = /\d+(?:\.\d+)?\s*%/.test(currentValue ?? "");
|
|
518
|
+
if (hasPercent) {
|
|
519
|
+
return !currentHasPercent;
|
|
520
|
+
}
|
|
521
|
+
return /\breset/i.test(line);
|
|
522
|
+
}
|
|
523
|
+
function normalizeCodexLine(line) {
|
|
524
|
+
return line.replace(/[│╭╮╰╯─]/g, " ").replace(/[ \t]+/g, " ").trim();
|
|
525
|
+
}
|
|
526
|
+
function sanitizeCodexValue(value) {
|
|
527
|
+
const sanitized = value.replace(/›.*$/g, "").trim();
|
|
528
|
+
const limitMatch = /^(.+?\d+(?:\.\d+)?\s*%\s*(?:left|remaining|used)(?:\s*\([^)]*\))?)/i.exec(
|
|
529
|
+
sanitized
|
|
530
|
+
);
|
|
531
|
+
return limitMatch?.[1].trim() ?? sanitized;
|
|
532
|
+
}
|
|
533
|
+
export {
|
|
534
|
+
buildCodexApiUsageResult,
|
|
535
|
+
buildCodexUsageLimits,
|
|
536
|
+
buildCodexUsageResult,
|
|
537
|
+
extractCodexBackendAuth,
|
|
538
|
+
extractCodexUsage,
|
|
539
|
+
parseCodexStatus
|
|
540
|
+
};
|