omniagent 0.1.14 → 0.1.17
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/README.md +5 -3
- package/dist/agy-DlbWeyP_.js +379 -0
- package/dist/{claude-BuXp9FHS.js → claude-CcxRb8OW.js} +2 -2
- package/dist/cli.js +179 -24
- package/dist/{codex-OCvtxDjw.js → codex-B4oMrP6y.js} +95 -61
- package/dist/{pty-CMd3Wxfe.js → pty-D8BFQQQg.js} +15 -12
- package/package.json +1 -1
- package/dist/agy-Clt40QYQ.js +0 -138
package/README.md
CHANGED
|
@@ -94,9 +94,11 @@ is not supported for usage extraction yet. Usage extraction may launch agent TUI
|
|
|
94
94
|
cost if an agent reads repo context or instructions on startup; omniagent uses cheap/minimal
|
|
95
95
|
launch settings where possible. Usage extraction times out after 30 seconds unless the target
|
|
96
96
|
config defines a target-specific timeout; built-in TUI probes may use longer defaults. Some CLIs
|
|
97
|
-
gate usage inspection behind onboarding state
|
|
98
|
-
|
|
99
|
-
|
|
97
|
+
gate usage inspection behind auth or onboarding state. When Antigravity requests directory trust,
|
|
98
|
+
interactive usage shows the exact directory and forwards approval only after you confirm;
|
|
99
|
+
omniagent never accepts trust automatically or completes authentication prompts. If Antigravity
|
|
100
|
+
requests directory trust, JSON, debug, and non-interactive runs return a `trust_required` error
|
|
101
|
+
instead of prompting. Pass `--timeout=<seconds>` to override the per-agent timeout for the current
|
|
100
102
|
run.
|
|
101
103
|
|
|
102
104
|
## Local Overrides (`.local`)
|
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
import { mkdir } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { c as cleanControlOutput, m as makeUsageLimit, U as UsageExtractionError, a as compactLines, p as parsePercentRemaining } from "./cli.js";
|
|
4
|
+
import { r as runPtyScenario, e as enterKey, a as escapeKey } from "./pty-D8BFQQQg.js";
|
|
5
|
+
const TRUST_DIALOG_PATTERN = /Do you trust the contents of this project\?/i;
|
|
6
|
+
const READY_PATTERN = /\?\s+for shortcuts/i;
|
|
7
|
+
const LOGIN_SELECTION_PATTERN = /Select login method:/i;
|
|
8
|
+
const USAGE_GROUP_HEADING_PATTERN = /^[A-Z][A-Z0-9 &/-]+$/;
|
|
9
|
+
const LIMIT_LABEL_PATTERN = /limit$/i;
|
|
10
|
+
const MODELS_LINE_PATTERN = /^Models within this group:\s*(.+)$/i;
|
|
11
|
+
const REFRESH_PATTERN = /Refreshes\s+in\s+(?:(\d+)h)?\s*(?:(\d+)m)?/i;
|
|
12
|
+
const NOT_SIGNED_IN_PATTERN = /\bnot signed in\b/i;
|
|
13
|
+
const SIGNING_IN_PATTERN = /\bsigning in\b/i;
|
|
14
|
+
const DISABLED_PATTERN = /^Disabled$/i;
|
|
15
|
+
const AGY_USAGE_FALLBACK_PATH = [".omniagent", "state", "usage", "antigravity-cli"];
|
|
16
|
+
const STARTUP_READY_TIMEOUT_MS = 25e3;
|
|
17
|
+
const USAGE_PANEL_STABLE_MS = 1e3;
|
|
18
|
+
const USAGE_PANEL_MIN_OBSERVE_MS = 2e3;
|
|
19
|
+
function isCurrentTrustDialog(snapshot) {
|
|
20
|
+
return TRUST_DIALOG_PATTERN.test(snapshot.screen);
|
|
21
|
+
}
|
|
22
|
+
function isNotCurrentTrustDialog(snapshot) {
|
|
23
|
+
return !isCurrentTrustDialog(snapshot);
|
|
24
|
+
}
|
|
25
|
+
function isReady(snapshot) {
|
|
26
|
+
return READY_PATTERN.test(snapshot.screen);
|
|
27
|
+
}
|
|
28
|
+
function isLoginSelection(snapshot) {
|
|
29
|
+
return LOGIN_SELECTION_PATTERN.test(snapshot.screen);
|
|
30
|
+
}
|
|
31
|
+
function isStartupTerminalState(snapshot) {
|
|
32
|
+
return isReady(snapshot) || isCurrentTrustDialog(snapshot) || isLoginSelection(snapshot);
|
|
33
|
+
}
|
|
34
|
+
function isPostTrustTerminalState(snapshot) {
|
|
35
|
+
return isReady(snapshot) || isLoginSelection(snapshot);
|
|
36
|
+
}
|
|
37
|
+
function isCurrentSignInFailure(snapshot) {
|
|
38
|
+
return NOT_SIGNED_IN_PATTERN.test(snapshot.screen) || isLoginSelection(snapshot);
|
|
39
|
+
}
|
|
40
|
+
function isInteractionBlocked(snapshot) {
|
|
41
|
+
return isCurrentTrustDialog(snapshot) || isCurrentSignInFailure(snapshot);
|
|
42
|
+
}
|
|
43
|
+
function isAuthenticationTransition(snapshot) {
|
|
44
|
+
return SIGNING_IN_PATTERN.test(snapshot.screen);
|
|
45
|
+
}
|
|
46
|
+
function isUsageWriteBlocked(snapshot) {
|
|
47
|
+
return isInteractionBlocked(snapshot) || isAuthenticationTransition(snapshot);
|
|
48
|
+
}
|
|
49
|
+
function createStableUsagePanelWait(stableMs = USAGE_PANEL_STABLE_MS, minObserveMs = USAGE_PANEL_MIN_OBSERVE_MS) {
|
|
50
|
+
let previousSignature = "";
|
|
51
|
+
let firstSeenAt = null;
|
|
52
|
+
let stableSince = 0;
|
|
53
|
+
return (snapshot) => {
|
|
54
|
+
if (isInteractionBlocked(snapshot)) {
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
const groups = parseAgyUsage(snapshot.screen, cleanControlOutput(snapshot.raw));
|
|
58
|
+
if (groups.length === 0) {
|
|
59
|
+
previousSignature = "";
|
|
60
|
+
firstSeenAt = null;
|
|
61
|
+
stableSince = 0;
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
const signature = usageGroupSignature(groups);
|
|
65
|
+
const now = Date.now();
|
|
66
|
+
if (firstSeenAt == null) {
|
|
67
|
+
firstSeenAt = now;
|
|
68
|
+
}
|
|
69
|
+
if (signature !== previousSignature) {
|
|
70
|
+
previousSignature = signature;
|
|
71
|
+
stableSince = now;
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
return now - firstSeenAt >= minObserveMs && now - stableSince >= stableMs;
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
function guardedUsageWrite(value, scenarioState, waitMs) {
|
|
78
|
+
return {
|
|
79
|
+
waitMs,
|
|
80
|
+
write: (snapshot) => {
|
|
81
|
+
if (isUsageWriteBlocked(snapshot)) {
|
|
82
|
+
scenarioState.canEnterUsage = false;
|
|
83
|
+
return void 0;
|
|
84
|
+
}
|
|
85
|
+
return scenarioState.canEnterUsage ? value : void 0;
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
async function extractAgyUsage(context) {
|
|
90
|
+
const command = context.command ?? context.launch?.command ?? "agy";
|
|
91
|
+
const launchCwd = await ensureAgyUsageCwd(context.homeDir, context.repoRoot);
|
|
92
|
+
const scenarioState = {
|
|
93
|
+
trustOutcome: "not-requested",
|
|
94
|
+
canEnterUsage: false,
|
|
95
|
+
reachedReadyAfterTrust: false
|
|
96
|
+
};
|
|
97
|
+
const ptyResult = await runPtyScenario({
|
|
98
|
+
command,
|
|
99
|
+
args: context.launch?.args ?? [],
|
|
100
|
+
cwd: launchCwd.path,
|
|
101
|
+
cols: 120,
|
|
102
|
+
rows: 40,
|
|
103
|
+
timeoutMs: context.launch?.timeoutMs ?? 7e4,
|
|
104
|
+
signal: context.signal,
|
|
105
|
+
debug: context.debug,
|
|
106
|
+
steps: [
|
|
107
|
+
{
|
|
108
|
+
waitFor: isStartupTerminalState,
|
|
109
|
+
waitForTimeoutMs: STARTUP_READY_TIMEOUT_MS,
|
|
110
|
+
optional: true,
|
|
111
|
+
capture: "startup",
|
|
112
|
+
captureWaitMs: 0
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
skipIf: isNotCurrentTrustDialog,
|
|
116
|
+
skipIfSource: "screen",
|
|
117
|
+
write: async () => {
|
|
118
|
+
if (context.confirm == null) {
|
|
119
|
+
scenarioState.trustOutcome = "required";
|
|
120
|
+
return void 0;
|
|
121
|
+
}
|
|
122
|
+
const approved = await context.confirm({
|
|
123
|
+
type: "trust-directory",
|
|
124
|
+
targetId: context.targetId,
|
|
125
|
+
displayName: context.displayName,
|
|
126
|
+
path: launchCwd.path,
|
|
127
|
+
managed: launchCwd.managed
|
|
128
|
+
});
|
|
129
|
+
scenarioState.trustOutcome = approved ? "approved" : "denied";
|
|
130
|
+
return void 0;
|
|
131
|
+
}
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
write: (snapshot2) => {
|
|
135
|
+
if (!isCurrentTrustDialog(snapshot2)) {
|
|
136
|
+
return void 0;
|
|
137
|
+
}
|
|
138
|
+
if (scenarioState.trustOutcome === "approved") {
|
|
139
|
+
return enterKey();
|
|
140
|
+
}
|
|
141
|
+
if (scenarioState.trustOutcome === "denied") {
|
|
142
|
+
return escapeKey();
|
|
143
|
+
}
|
|
144
|
+
return void 0;
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
skipIf: () => scenarioState.trustOutcome !== "approved",
|
|
149
|
+
waitFor: isPostTrustTerminalState,
|
|
150
|
+
waitForTimeoutMs: STARTUP_READY_TIMEOUT_MS,
|
|
151
|
+
optional: true
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
write: (snapshot2) => {
|
|
155
|
+
const readyForUsage = isReady(snapshot2) && !isInteractionBlocked(snapshot2);
|
|
156
|
+
if (scenarioState.trustOutcome === "approved" && readyForUsage) {
|
|
157
|
+
scenarioState.reachedReadyAfterTrust = true;
|
|
158
|
+
}
|
|
159
|
+
scenarioState.canEnterUsage = scenarioState.trustOutcome !== "denied" && scenarioState.trustOutcome !== "required" && readyForUsage;
|
|
160
|
+
return void 0;
|
|
161
|
+
}
|
|
162
|
+
},
|
|
163
|
+
...[..."/usage"].map((character) => guardedUsageWrite(character, scenarioState, 25)),
|
|
164
|
+
guardedUsageWrite(enterKey(), scenarioState, 250),
|
|
165
|
+
{
|
|
166
|
+
skipIf: () => !scenarioState.canEnterUsage,
|
|
167
|
+
waitFor: createStableUsagePanelWait(),
|
|
168
|
+
waitForTimeoutMs: 15e3,
|
|
169
|
+
optional: true,
|
|
170
|
+
capture: "usage",
|
|
171
|
+
captureWaitMs: 0
|
|
172
|
+
},
|
|
173
|
+
guardedUsageWrite(escapeKey(), scenarioState, 250)
|
|
174
|
+
]
|
|
175
|
+
});
|
|
176
|
+
const buildError = (message, code) => {
|
|
177
|
+
const error = code == null ? new Error(message) : new UsageExtractionError(code, message);
|
|
178
|
+
if (ptyResult.debug.length > 0) {
|
|
179
|
+
Object.assign(error, { debug: ptyResult.debug });
|
|
180
|
+
}
|
|
181
|
+
return error;
|
|
182
|
+
};
|
|
183
|
+
const trustSubject = launchCwd.managed ? "managed usage directory" : "project directory";
|
|
184
|
+
if (scenarioState.trustOutcome === "required") {
|
|
185
|
+
throw buildError(
|
|
186
|
+
`Antigravity needs permission to trust the ${trustSubject} at ${launchCwd.path}. Re-run usage in an interactive terminal to review this request.`,
|
|
187
|
+
"trust_required"
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
if (scenarioState.trustOutcome === "denied") {
|
|
191
|
+
throw buildError(
|
|
192
|
+
`Antigravity trust was declined for the ${trustSubject} at ${launchCwd.path}.`,
|
|
193
|
+
"trust_denied"
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
if (isCurrentSignInFailure(ptyResult)) {
|
|
197
|
+
throw buildError(`Antigravity is not signed in. Run \`${command}\` and complete the login.`);
|
|
198
|
+
}
|
|
199
|
+
if (scenarioState.trustOutcome === "approved" && !scenarioState.reachedReadyAfterTrust) {
|
|
200
|
+
throw buildError(
|
|
201
|
+
`Antigravity did not accept trust for the ${trustSubject} at ${launchCwd.path}. Run \`${command}\` there once, accept the trust prompt, then re-run usage.`,
|
|
202
|
+
"trust_acceptance_failed"
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
if (isCurrentTrustDialog(ptyResult)) {
|
|
206
|
+
throw buildError(
|
|
207
|
+
`Antigravity still requires trust for the ${trustSubject} at ${launchCwd.path}.`,
|
|
208
|
+
"trust_required"
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
const snapshot = ptyResult.snapshots.usage ?? ptyResult;
|
|
212
|
+
const cleanedOutput = cleanControlOutput(snapshot.raw);
|
|
213
|
+
if (isCurrentSignInFailure(snapshot)) {
|
|
214
|
+
throw buildError(`Antigravity is not signed in. Run \`${command}\` and complete the login.`);
|
|
215
|
+
}
|
|
216
|
+
const groups = parseAgyUsage(snapshot.screen, cleanedOutput);
|
|
217
|
+
if (groups.length === 0) {
|
|
218
|
+
throw buildError("Antigravity /usage output did not include Models & Quota limit groups.");
|
|
219
|
+
}
|
|
220
|
+
return {
|
|
221
|
+
targetId: context.targetId,
|
|
222
|
+
displayName: context.displayName,
|
|
223
|
+
command,
|
|
224
|
+
limits: groups.map((group) => {
|
|
225
|
+
const percentRemaining = group.disabled || group.percentRemaining == null ? null : clampPercent(group.percentRemaining);
|
|
226
|
+
const limit = makeUsageLimit({
|
|
227
|
+
targetId: context.targetId,
|
|
228
|
+
scope: usageScope(group.heading),
|
|
229
|
+
window: "weekly",
|
|
230
|
+
label: titleCase(group.heading),
|
|
231
|
+
percentUsed: percentRemaining == null ? null : 100 - percentRemaining,
|
|
232
|
+
percentRemaining,
|
|
233
|
+
remainingText: group.disabled ? "Disabled" : null,
|
|
234
|
+
resetText: group.resetText,
|
|
235
|
+
raw: group.raw,
|
|
236
|
+
now: context.now
|
|
237
|
+
});
|
|
238
|
+
return {
|
|
239
|
+
...limit,
|
|
240
|
+
resetAt: parseAgyRefreshResetAt(group.resetText, context.now) ?? limit.resetAt
|
|
241
|
+
};
|
|
242
|
+
}),
|
|
243
|
+
debug: ptyResult.debug.length > 0 ? ptyResult.debug : void 0
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
async function ensureAgyUsageCwd(homeDir, repoRoot) {
|
|
247
|
+
const fallbackDir = path.join(homeDir, ...AGY_USAGE_FALLBACK_PATH);
|
|
248
|
+
try {
|
|
249
|
+
await mkdir(fallbackDir, { recursive: true });
|
|
250
|
+
return { path: fallbackDir, managed: true };
|
|
251
|
+
} catch {
|
|
252
|
+
return { path: repoRoot, managed: false };
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
function parseAgyUsage(screen, cleanedOutput = "") {
|
|
256
|
+
const fromRaw = parseAgyUsageLines(compactLines(cleanedOutput));
|
|
257
|
+
const fromScreen = parseAgyUsageLines(compactLines(screen));
|
|
258
|
+
const merged = /* @__PURE__ */ new Map();
|
|
259
|
+
for (const group of [...fromRaw, ...fromScreen]) {
|
|
260
|
+
merged.set(usageGroupKey(group), group);
|
|
261
|
+
}
|
|
262
|
+
return [...merged.values()];
|
|
263
|
+
}
|
|
264
|
+
function usageGroupKey(group) {
|
|
265
|
+
return `${group.heading.trim().toLowerCase()}\0${group.limitLabel.trim().toLowerCase()}`;
|
|
266
|
+
}
|
|
267
|
+
function usageGroupSignature(groups) {
|
|
268
|
+
return JSON.stringify(
|
|
269
|
+
groups.map((group) => ({
|
|
270
|
+
heading: group.heading,
|
|
271
|
+
models: group.models,
|
|
272
|
+
limitLabel: group.limitLabel,
|
|
273
|
+
percentRemaining: group.percentRemaining,
|
|
274
|
+
resetText: group.resetText,
|
|
275
|
+
disabled: group.disabled
|
|
276
|
+
}))
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
function parseAgyUsageLines(lines) {
|
|
280
|
+
const groups = [];
|
|
281
|
+
let index = 0;
|
|
282
|
+
while (index < lines.length) {
|
|
283
|
+
const heading = lines[index] ?? "";
|
|
284
|
+
if (!isUsageGroupHeading(heading)) {
|
|
285
|
+
index += 1;
|
|
286
|
+
continue;
|
|
287
|
+
}
|
|
288
|
+
const rawLines = [heading];
|
|
289
|
+
let models = null;
|
|
290
|
+
let limitLabel = "";
|
|
291
|
+
let percentRemaining = null;
|
|
292
|
+
let resetText = null;
|
|
293
|
+
let disabled = false;
|
|
294
|
+
index += 1;
|
|
295
|
+
while (index < lines.length && !isUsageGroupHeading(lines[index] ?? "")) {
|
|
296
|
+
const line = lines[index] ?? "";
|
|
297
|
+
rawLines.push(line);
|
|
298
|
+
const modelsMatch = MODELS_LINE_PATTERN.exec(line);
|
|
299
|
+
if (modelsMatch?.[1]) {
|
|
300
|
+
models = modelsMatch[1].trim();
|
|
301
|
+
}
|
|
302
|
+
if (LIMIT_LABEL_PATTERN.test(line)) {
|
|
303
|
+
limitLabel = line.trim();
|
|
304
|
+
}
|
|
305
|
+
const linePercentRemaining = parseAgyRemainingPercent(line);
|
|
306
|
+
if (linePercentRemaining != null && percentRemaining == null) {
|
|
307
|
+
percentRemaining = linePercentRemaining;
|
|
308
|
+
}
|
|
309
|
+
const lineResetText = parseAgyRefreshText(line);
|
|
310
|
+
if (lineResetText != null) {
|
|
311
|
+
resetText = lineResetText;
|
|
312
|
+
}
|
|
313
|
+
if (DISABLED_PATTERN.test(line)) {
|
|
314
|
+
disabled = true;
|
|
315
|
+
}
|
|
316
|
+
index += 1;
|
|
317
|
+
}
|
|
318
|
+
if (limitLabel && (percentRemaining != null || resetText != null || disabled)) {
|
|
319
|
+
groups.push({
|
|
320
|
+
heading,
|
|
321
|
+
models,
|
|
322
|
+
limitLabel,
|
|
323
|
+
percentRemaining,
|
|
324
|
+
resetText,
|
|
325
|
+
disabled,
|
|
326
|
+
raw: rawLines.join("\n")
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
return groups;
|
|
331
|
+
}
|
|
332
|
+
function isUsageGroupHeading(line) {
|
|
333
|
+
if (!USAGE_GROUP_HEADING_PATTERN.test(line)) {
|
|
334
|
+
return false;
|
|
335
|
+
}
|
|
336
|
+
return !/^(MODELS|WEEKLY|MONTHLY|DAILY|ACCOUNT)$/i.test(line);
|
|
337
|
+
}
|
|
338
|
+
function parseAgyRemainingPercent(line) {
|
|
339
|
+
return parsePercentRemaining(line) ?? parseStandalonePercent(line);
|
|
340
|
+
}
|
|
341
|
+
function parseStandalonePercent(line) {
|
|
342
|
+
const match = /(?:^|\])\s*(\d+(?:\.\d+)?)\s*%\s*$/i.exec(line);
|
|
343
|
+
if (!match?.[1]) {
|
|
344
|
+
return null;
|
|
345
|
+
}
|
|
346
|
+
return Number(match[1]);
|
|
347
|
+
}
|
|
348
|
+
function parseAgyRefreshText(line) {
|
|
349
|
+
const match = /(Refreshes\s+in\s+(?:(?:\d+)h)?\s*(?:(?:\d+)m)?)/i.exec(line);
|
|
350
|
+
return match?.[1]?.trim() ?? null;
|
|
351
|
+
}
|
|
352
|
+
function parseAgyRefreshResetAt(resetText, now) {
|
|
353
|
+
if (resetText == null) {
|
|
354
|
+
return null;
|
|
355
|
+
}
|
|
356
|
+
const match = REFRESH_PATTERN.exec(resetText);
|
|
357
|
+
if (match == null) {
|
|
358
|
+
return null;
|
|
359
|
+
}
|
|
360
|
+
const hours = Number(match[1] ?? 0);
|
|
361
|
+
const minutes = Number(match[2] ?? 0);
|
|
362
|
+
if (!Number.isFinite(hours) || !Number.isFinite(minutes) || hours + minutes <= 0) {
|
|
363
|
+
return null;
|
|
364
|
+
}
|
|
365
|
+
return new Date(now.getTime() + hours * 36e5 + minutes * 6e4).toISOString();
|
|
366
|
+
}
|
|
367
|
+
function clampPercent(value) {
|
|
368
|
+
return Math.max(0, Math.min(100, value));
|
|
369
|
+
}
|
|
370
|
+
function usageScope(heading) {
|
|
371
|
+
return heading.trim().toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
|
|
372
|
+
}
|
|
373
|
+
function titleCase(value) {
|
|
374
|
+
return value.trim().toLowerCase().split(/\s+/).map((word) => word === "gpt" ? "GPT" : `${word.charAt(0).toUpperCase()}${word.slice(1)}`).join(" ");
|
|
375
|
+
}
|
|
376
|
+
export {
|
|
377
|
+
extractAgyUsage,
|
|
378
|
+
parseAgyUsage
|
|
379
|
+
};
|
|
@@ -3,8 +3,8 @@ import { readFile } from "node:fs/promises";
|
|
|
3
3
|
import os from "node:os";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { promisify } from "node:util";
|
|
6
|
-
import { c as cleanControlOutput, a as compactLines,
|
|
7
|
-
import { r as runPtyScenario,
|
|
6
|
+
import { c as cleanControlOutput, a as compactLines, b as parsePercentUsed, m as makeUsageLimit } from "./cli.js";
|
|
7
|
+
import { r as runPtyScenario, e as enterKey, a as escapeKey } from "./pty-D8BFQQQg.js";
|
|
8
8
|
const execFileAsync = promisify(execFile);
|
|
9
9
|
const CLAUDE_CODE_KEYCHAIN_SERVICE = "Claude Code-credentials";
|
|
10
10
|
const CLAUDE_CODE_CREDENTIALS_PATH = [".claude", ".credentials.json"];
|
package/dist/cli.js
CHANGED
|
@@ -1332,13 +1332,13 @@ const agyTarget = {
|
|
|
1332
1332
|
}
|
|
1333
1333
|
},
|
|
1334
1334
|
usage: {
|
|
1335
|
-
windows: ["
|
|
1335
|
+
windows: ["weekly"],
|
|
1336
1336
|
launch: {
|
|
1337
1337
|
command: "agy",
|
|
1338
1338
|
timeoutMs: 7e4
|
|
1339
1339
|
},
|
|
1340
1340
|
extract: async (context) => {
|
|
1341
|
-
const { extractAgyUsage } = await import("./agy-
|
|
1341
|
+
const { extractAgyUsage } = await import("./agy-DlbWeyP_.js");
|
|
1342
1342
|
return extractAgyUsage(context);
|
|
1343
1343
|
}
|
|
1344
1344
|
}
|
|
@@ -1399,7 +1399,7 @@ const claudeTarget = {
|
|
|
1399
1399
|
timeoutMs: 6e4
|
|
1400
1400
|
},
|
|
1401
1401
|
extract: async (context) => {
|
|
1402
|
-
const { extractClaudeUsage } = await import("./claude-
|
|
1402
|
+
const { extractClaudeUsage } = await import("./claude-CcxRb8OW.js");
|
|
1403
1403
|
return extractClaudeUsage(context);
|
|
1404
1404
|
}
|
|
1405
1405
|
}
|
|
@@ -1484,7 +1484,7 @@ const codexTarget = {
|
|
|
1484
1484
|
timeoutMs: 6e4
|
|
1485
1485
|
},
|
|
1486
1486
|
extract: async (context) => {
|
|
1487
|
-
const { extractCodexUsage } = await import("./codex-
|
|
1487
|
+
const { extractCodexUsage } = await import("./codex-B4oMrP6y.js");
|
|
1488
1488
|
return extractCodexUsage(context);
|
|
1489
1489
|
}
|
|
1490
1490
|
}
|
|
@@ -10701,6 +10701,13 @@ function emptyToNull(value) {
|
|
|
10701
10701
|
function escapeRegExp(value) {
|
|
10702
10702
|
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
10703
10703
|
}
|
|
10704
|
+
class UsageExtractionError extends Error {
|
|
10705
|
+
constructor(code, message) {
|
|
10706
|
+
super(message);
|
|
10707
|
+
this.code = code;
|
|
10708
|
+
this.name = "UsageExtractionError";
|
|
10709
|
+
}
|
|
10710
|
+
}
|
|
10704
10711
|
const USAGE_BAR_WIDTH = 12;
|
|
10705
10712
|
const ANSI = {
|
|
10706
10713
|
reset: "\x1B[0m",
|
|
@@ -10735,6 +10742,12 @@ class UsageExtractionTimeoutError extends Error {
|
|
|
10735
10742
|
this.timeoutMs = timeoutMs;
|
|
10736
10743
|
}
|
|
10737
10744
|
}
|
|
10745
|
+
class UsageCommandInterruptedError extends Error {
|
|
10746
|
+
constructor() {
|
|
10747
|
+
super("Usage cancelled.");
|
|
10748
|
+
this.name = "UsageCommandInterruptedError";
|
|
10749
|
+
}
|
|
10750
|
+
}
|
|
10738
10751
|
function normalizeOptionalWindow(value) {
|
|
10739
10752
|
if (value == null) {
|
|
10740
10753
|
return null;
|
|
@@ -10805,6 +10818,68 @@ function uniqueNormalizedWindows(values) {
|
|
|
10805
10818
|
}
|
|
10806
10819
|
return result;
|
|
10807
10820
|
}
|
|
10821
|
+
class UsageConfirmationPrompter {
|
|
10822
|
+
rl = null;
|
|
10823
|
+
queue = Promise.resolve();
|
|
10824
|
+
closed = false;
|
|
10825
|
+
interrupt;
|
|
10826
|
+
constructor(interrupt) {
|
|
10827
|
+
this.interrupt = interrupt;
|
|
10828
|
+
}
|
|
10829
|
+
confirm = (request, signal) => {
|
|
10830
|
+
const confirmation = this.queue.then(() => this.ask(request, signal));
|
|
10831
|
+
this.queue = confirmation.then(
|
|
10832
|
+
() => void 0,
|
|
10833
|
+
() => void 0
|
|
10834
|
+
);
|
|
10835
|
+
return confirmation;
|
|
10836
|
+
};
|
|
10837
|
+
close() {
|
|
10838
|
+
this.closed = true;
|
|
10839
|
+
this.rl?.close();
|
|
10840
|
+
this.rl = null;
|
|
10841
|
+
}
|
|
10842
|
+
async ask(request, signal) {
|
|
10843
|
+
if (this.closed) {
|
|
10844
|
+
throw new Error("Usage confirmation is no longer available.");
|
|
10845
|
+
}
|
|
10846
|
+
if (signal.aborted) {
|
|
10847
|
+
throw signal.reason instanceof Error ? signal.reason : new Error("Usage confirmation was cancelled.");
|
|
10848
|
+
}
|
|
10849
|
+
const scope = request.managed ? "managed usage directory" : "project directory";
|
|
10850
|
+
const question = `${request.displayName} needs to trust this ${scope}:
|
|
10851
|
+
${request.path}
|
|
10852
|
+
|
|
10853
|
+
Allow this? [y/N] `;
|
|
10854
|
+
const rl = createInterface({ input: process.stdin, output: process.stderr });
|
|
10855
|
+
this.rl = rl;
|
|
10856
|
+
const handleInterrupt = () => this.interrupt();
|
|
10857
|
+
rl.once("SIGINT", handleInterrupt);
|
|
10858
|
+
try {
|
|
10859
|
+
while (true) {
|
|
10860
|
+
const answer = (await rl.question(question, { signal })).trim().toLowerCase();
|
|
10861
|
+
if (!answer || answer === "n" || answer === "no") {
|
|
10862
|
+
return false;
|
|
10863
|
+
}
|
|
10864
|
+
if (answer === "y" || answer === "yes") {
|
|
10865
|
+
return true;
|
|
10866
|
+
}
|
|
10867
|
+
console.error("Please enter yes or no.");
|
|
10868
|
+
}
|
|
10869
|
+
} catch (error) {
|
|
10870
|
+
if (signal.aborted && signal.reason instanceof Error) {
|
|
10871
|
+
throw signal.reason;
|
|
10872
|
+
}
|
|
10873
|
+
throw error;
|
|
10874
|
+
} finally {
|
|
10875
|
+
rl.removeListener("SIGINT", handleInterrupt);
|
|
10876
|
+
rl.close();
|
|
10877
|
+
if (this.rl === rl) {
|
|
10878
|
+
this.rl = null;
|
|
10879
|
+
}
|
|
10880
|
+
}
|
|
10881
|
+
}
|
|
10882
|
+
}
|
|
10808
10883
|
function formatUsageTargetLabel(targets) {
|
|
10809
10884
|
return buildSupportedTargetLabel(targets);
|
|
10810
10885
|
}
|
|
@@ -10815,6 +10890,13 @@ function formatSupportedUsageTargetsMessage(targets) {
|
|
|
10815
10890
|
function getUsageCommand(target) {
|
|
10816
10891
|
return target.usage?.launch?.command;
|
|
10817
10892
|
}
|
|
10893
|
+
function supportsUsageConfirmation(target) {
|
|
10894
|
+
if (target.id.trim().toLowerCase() === "agy") {
|
|
10895
|
+
return true;
|
|
10896
|
+
}
|
|
10897
|
+
const builtInAgyExtractor = BUILTIN_TARGETS.find((candidate) => candidate.id === "agy")?.usage?.extract;
|
|
10898
|
+
return builtInAgyExtractor != null && target.usage?.extract === builtInAgyExtractor;
|
|
10899
|
+
}
|
|
10818
10900
|
async function checkUsageCommandAvailability(target) {
|
|
10819
10901
|
const command = getUsageCommand(target)?.trim();
|
|
10820
10902
|
if (!command) {
|
|
@@ -10872,6 +10954,7 @@ function validateCodexCommand(candidate) {
|
|
|
10872
10954
|
}
|
|
10873
10955
|
function buildContext(options) {
|
|
10874
10956
|
const windows = uniqueNormalizedWindows(options.target.usage?.windows ?? []);
|
|
10957
|
+
const confirm = options.confirm;
|
|
10875
10958
|
const launch = {
|
|
10876
10959
|
...options.target.usage?.launch ?? {},
|
|
10877
10960
|
timeoutMs: options.timeoutMs
|
|
@@ -10888,6 +10971,7 @@ function buildContext(options) {
|
|
|
10888
10971
|
homeDir: options.homeDir,
|
|
10889
10972
|
launch,
|
|
10890
10973
|
signal: options.signal,
|
|
10974
|
+
confirm: confirm == null ? void 0 : (request) => confirm(request, options.signal),
|
|
10891
10975
|
debug: {
|
|
10892
10976
|
enabled: options.debug,
|
|
10893
10977
|
includeRawOutput: options.debug,
|
|
@@ -10901,7 +10985,11 @@ function filterTargetResult(target, result, selectedWindow) {
|
|
|
10901
10985
|
window: normalizeUsageWindow(limit.window)
|
|
10902
10986
|
}));
|
|
10903
10987
|
const filteredLimits = selectedWindow == null ? normalizedLimits : normalizedLimits.filter((limit) => limit.window === selectedWindow);
|
|
10904
|
-
const
|
|
10988
|
+
const resultNotes = result.notes ?? [];
|
|
10989
|
+
const notes = selectedWindow != null && filteredLimits.length === 0 ? [
|
|
10990
|
+
...resultNotes,
|
|
10991
|
+
`${target.displayName} reported no usage rows for window "${selectedWindow}".`
|
|
10992
|
+
] : resultNotes;
|
|
10905
10993
|
return {
|
|
10906
10994
|
result: {
|
|
10907
10995
|
targetId: result.targetId,
|
|
@@ -10936,10 +11024,14 @@ async function extractUsageForTarget(options) {
|
|
|
10936
11024
|
debug: []
|
|
10937
11025
|
};
|
|
10938
11026
|
}
|
|
10939
|
-
const result = await withUsageTimeout(
|
|
10940
|
-
|
|
10941
|
-
|
|
10942
|
-
|
|
11027
|
+
const result = await withUsageTimeout(
|
|
11028
|
+
(signal) => {
|
|
11029
|
+
const context = buildContext({ ...options, signal });
|
|
11030
|
+
return extractor(context);
|
|
11031
|
+
},
|
|
11032
|
+
options.timeoutMs,
|
|
11033
|
+
options.commandSignal
|
|
11034
|
+
);
|
|
10943
11035
|
const filtered = filterTargetResult(options.target, result, options.selectedWindow);
|
|
10944
11036
|
return {
|
|
10945
11037
|
status: "success",
|
|
@@ -10951,7 +11043,7 @@ async function extractUsageForTarget(options) {
|
|
|
10951
11043
|
};
|
|
10952
11044
|
} catch (error) {
|
|
10953
11045
|
const message = error instanceof Error ? error.message : String(error);
|
|
10954
|
-
const code = isUsageTimeoutError(error) ? "usage_extraction_timeout" :
|
|
11046
|
+
const code = isUsageTimeoutError(error) ? "usage_extraction_timeout" : getUsageExtractionErrorCode(error);
|
|
10955
11047
|
return {
|
|
10956
11048
|
status: "error",
|
|
10957
11049
|
target: options.target,
|
|
@@ -10960,6 +11052,21 @@ async function extractUsageForTarget(options) {
|
|
|
10960
11052
|
};
|
|
10961
11053
|
}
|
|
10962
11054
|
}
|
|
11055
|
+
function getUsageExtractionErrorCode(error) {
|
|
11056
|
+
if (error instanceof UsageExtractionError) {
|
|
11057
|
+
return error.code;
|
|
11058
|
+
}
|
|
11059
|
+
if (error && typeof error === "object") {
|
|
11060
|
+
const code = error.code;
|
|
11061
|
+
if (typeof code === "string") {
|
|
11062
|
+
const normalized = code.trim();
|
|
11063
|
+
if (/^[a-z][a-z0-9_]*$/.test(normalized)) {
|
|
11064
|
+
return normalized;
|
|
11065
|
+
}
|
|
11066
|
+
}
|
|
11067
|
+
}
|
|
11068
|
+
return "usage_extraction_failed";
|
|
11069
|
+
}
|
|
10963
11070
|
function isUsageTimeoutError(error) {
|
|
10964
11071
|
if (error instanceof UsageExtractionTimeoutError) {
|
|
10965
11072
|
return true;
|
|
@@ -10983,18 +11090,25 @@ function isDebugArtifact(value) {
|
|
|
10983
11090
|
const artifact = value;
|
|
10984
11091
|
return (artifact.type === "raw-output" || artifact.type === "screen-snapshot") && typeof artifact.label === "string";
|
|
10985
11092
|
}
|
|
10986
|
-
function withUsageTimeout(run, timeoutMs) {
|
|
11093
|
+
function withUsageTimeout(run, timeoutMs, commandSignal) {
|
|
10987
11094
|
return new Promise((resolve, reject) => {
|
|
10988
11095
|
const controller = new AbortController();
|
|
10989
11096
|
let settled = false;
|
|
10990
11097
|
let timeoutFired = false;
|
|
10991
|
-
let timeout;
|
|
11098
|
+
let timeout = null;
|
|
11099
|
+
let removeCommandAbortListener = null;
|
|
11100
|
+
const cleanup = () => {
|
|
11101
|
+
if (timeout != null) {
|
|
11102
|
+
clearTimeout(timeout);
|
|
11103
|
+
}
|
|
11104
|
+
removeCommandAbortListener?.();
|
|
11105
|
+
};
|
|
10992
11106
|
const settleResolve = (value) => {
|
|
10993
11107
|
if (settled) {
|
|
10994
11108
|
return;
|
|
10995
11109
|
}
|
|
10996
11110
|
settled = true;
|
|
10997
|
-
|
|
11111
|
+
cleanup();
|
|
10998
11112
|
resolve(value);
|
|
10999
11113
|
};
|
|
11000
11114
|
const settleReject = (error) => {
|
|
@@ -11002,7 +11116,7 @@ function withUsageTimeout(run, timeoutMs) {
|
|
|
11002
11116
|
return;
|
|
11003
11117
|
}
|
|
11004
11118
|
settled = true;
|
|
11005
|
-
|
|
11119
|
+
cleanup();
|
|
11006
11120
|
reject(error);
|
|
11007
11121
|
};
|
|
11008
11122
|
timeout = setTimeout(() => {
|
|
@@ -11013,6 +11127,19 @@ function withUsageTimeout(run, timeoutMs) {
|
|
|
11013
11127
|
settleReject(error);
|
|
11014
11128
|
});
|
|
11015
11129
|
}, timeoutMs);
|
|
11130
|
+
if (commandSignal) {
|
|
11131
|
+
const abortFromCommand = () => {
|
|
11132
|
+
const reason = commandSignal.reason instanceof Error ? commandSignal.reason : new UsageCommandInterruptedError();
|
|
11133
|
+
controller.abort(reason);
|
|
11134
|
+
settleReject(reason);
|
|
11135
|
+
};
|
|
11136
|
+
if (commandSignal.aborted) {
|
|
11137
|
+
abortFromCommand();
|
|
11138
|
+
return;
|
|
11139
|
+
}
|
|
11140
|
+
commandSignal.addEventListener("abort", abortFromCommand, { once: true });
|
|
11141
|
+
removeCommandAbortListener = () => commandSignal.removeEventListener("abort", abortFromCommand);
|
|
11142
|
+
}
|
|
11016
11143
|
let promise;
|
|
11017
11144
|
try {
|
|
11018
11145
|
promise = run(controller.signal);
|
|
@@ -11631,9 +11758,17 @@ async function runUsageCommand(argv) {
|
|
|
11631
11758
|
}
|
|
11632
11759
|
}
|
|
11633
11760
|
const now = /* @__PURE__ */ new Date();
|
|
11634
|
-
const
|
|
11635
|
-
|
|
11636
|
-
|
|
11761
|
+
const commandController = new AbortController();
|
|
11762
|
+
const confirmationPrompter = !jsonOutput && Boolean(process.stdin.isTTY) && Boolean(process.stderr.isTTY) ? new UsageConfirmationPrompter(() => {
|
|
11763
|
+
if (!commandController.signal.aborted) {
|
|
11764
|
+
commandController.abort(new UsageCommandInterruptedError());
|
|
11765
|
+
}
|
|
11766
|
+
}) : null;
|
|
11767
|
+
let outcomes;
|
|
11768
|
+
try {
|
|
11769
|
+
let confirmationTargetChain = Promise.resolve();
|
|
11770
|
+
const outcomePromises = selectedTargets.map((target) => {
|
|
11771
|
+
const extract = () => extractUsageForTarget({
|
|
11637
11772
|
target,
|
|
11638
11773
|
repoRoot,
|
|
11639
11774
|
agentsDir,
|
|
@@ -11642,10 +11777,29 @@ async function runUsageCommand(argv) {
|
|
|
11642
11777
|
timeoutMs: resolveTargetTimeoutMs(target, cliTimeoutMs),
|
|
11643
11778
|
debug: debugOutput,
|
|
11644
11779
|
now,
|
|
11645
|
-
command: resolvedUsageCommands.get(target.id)
|
|
11646
|
-
|
|
11647
|
-
|
|
11648
|
-
|
|
11780
|
+
command: resolvedUsageCommands.get(target.id),
|
|
11781
|
+
confirm: supportsUsageConfirmation(target) ? confirmationPrompter?.confirm : void 0,
|
|
11782
|
+
commandSignal: commandController.signal
|
|
11783
|
+
});
|
|
11784
|
+
if (confirmationPrompter == null || !supportsUsageConfirmation(target)) {
|
|
11785
|
+
return extract();
|
|
11786
|
+
}
|
|
11787
|
+
const outcome = confirmationTargetChain.then(extract);
|
|
11788
|
+
confirmationTargetChain = outcome.then(
|
|
11789
|
+
() => void 0,
|
|
11790
|
+
() => void 0
|
|
11791
|
+
);
|
|
11792
|
+
return outcome;
|
|
11793
|
+
});
|
|
11794
|
+
outcomes = await Promise.all(outcomePromises);
|
|
11795
|
+
} finally {
|
|
11796
|
+
confirmationPrompter?.close();
|
|
11797
|
+
}
|
|
11798
|
+
if (commandController.signal.reason instanceof UsageCommandInterruptedError) {
|
|
11799
|
+
console.error(commandController.signal.reason.message);
|
|
11800
|
+
process.exit(130);
|
|
11801
|
+
return null;
|
|
11802
|
+
}
|
|
11649
11803
|
const targets = [];
|
|
11650
11804
|
const errors = [];
|
|
11651
11805
|
const notes = [];
|
|
@@ -11731,7 +11885,7 @@ const usageCommand = {
|
|
|
11731
11885
|
type: "boolean",
|
|
11732
11886
|
describe: "Print JSON and include extractor debug artifacts when available."
|
|
11733
11887
|
}).epilog(
|
|
11734
|
-
"Usage extraction may launch agent TUIs and may incur cost if an agent reads repo context on startup.
|
|
11888
|
+
"Usage extraction may launch agent TUIs and may incur cost if an agent reads repo context on startup. Interactive runs may ask before forwarding directory trust; JSON, debug, and non-interactive runs never prompt."
|
|
11735
11889
|
),
|
|
11736
11890
|
handler: async (argv) => {
|
|
11737
11891
|
await runUsageCommand(argv);
|
|
@@ -13102,11 +13256,12 @@ if (!entry) {
|
|
|
13102
13256
|
}
|
|
13103
13257
|
}
|
|
13104
13258
|
export {
|
|
13259
|
+
UsageExtractionError as U,
|
|
13105
13260
|
compactLines as a,
|
|
13106
|
-
|
|
13261
|
+
parsePercentUsed as b,
|
|
13107
13262
|
cleanControlOutput as c,
|
|
13108
13263
|
parseResetText as d,
|
|
13109
13264
|
makeUsageLimit as m,
|
|
13110
|
-
|
|
13265
|
+
parsePercentRemaining as p,
|
|
13111
13266
|
runCli
|
|
13112
13267
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { readFile } from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
|
-
import { c as cleanControlOutput,
|
|
4
|
-
import { r as runPtyScenario,
|
|
3
|
+
import { c as cleanControlOutput, p as parsePercentRemaining, m as makeUsageLimit, d as parseResetText } from "./cli.js";
|
|
4
|
+
import { r as runPtyScenario, e as enterKey, t as typeTextSteps } from "./pty-D8BFQQQg.js";
|
|
5
5
|
const CODEX_WINDOWS = [
|
|
6
6
|
["main", "5h", "main5hLimit"],
|
|
7
7
|
["main", "weekly", "mainWeeklyLimit"],
|
|
@@ -35,8 +35,18 @@ async function extractCodexUsageFromTui(context) {
|
|
|
35
35
|
signal: context.signal,
|
|
36
36
|
debug: context.debug,
|
|
37
37
|
steps: [
|
|
38
|
-
{ waitFor:
|
|
38
|
+
{ waitFor: isCodexPromptReadyOrStartupPrompt, waitForTimeoutMs: 1e4 },
|
|
39
|
+
// Numeric migration choices submit immediately, so do not append Enter.
|
|
40
|
+
{ write: dismissCodexModelMigrationPrompt, skipIf: isCodexPromptReady },
|
|
41
|
+
{ waitFor: isCodexPromptReadyOrTrustPrompt, waitForTimeoutMs: 1e4, optional: true },
|
|
39
42
|
{ write: enterKey(), skipIf: isCodexPromptReady },
|
|
43
|
+
// Trust onboarding can precede the model-migration dialog.
|
|
44
|
+
{
|
|
45
|
+
waitFor: isCodexPromptReadyOrMigrationPrompt,
|
|
46
|
+
waitForTimeoutMs: 1e4,
|
|
47
|
+
optional: true
|
|
48
|
+
},
|
|
49
|
+
{ write: dismissCodexModelMigrationPrompt, skipIf: isCodexPromptReady },
|
|
40
50
|
{ waitFor: isCodexPromptReady, waitForTimeoutMs: 1e4 },
|
|
41
51
|
...typeTextSteps("/status", 20),
|
|
42
52
|
{ write: enterKey() },
|
|
@@ -98,15 +108,14 @@ function buildCodexApiUsageResult(payload, context) {
|
|
|
98
108
|
targetId: context.targetId,
|
|
99
109
|
scope: "main",
|
|
100
110
|
rateLimit: usage.rate_limit,
|
|
101
|
-
now: context.now
|
|
102
|
-
requireBothWindows: true
|
|
111
|
+
now: context.now
|
|
103
112
|
}),
|
|
104
113
|
...buildCodexApiAdditionalUsageLimits(usage.additional_rate_limits, context)
|
|
105
114
|
];
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
115
|
+
if (!limits.some((limit) => limit.scope === "main")) {
|
|
116
|
+
throw new Error(
|
|
117
|
+
"Codex usage API response did not include any recognized main rate-limit windows."
|
|
118
|
+
);
|
|
110
119
|
}
|
|
111
120
|
return {
|
|
112
121
|
targetId: context.targetId,
|
|
@@ -203,8 +212,7 @@ function buildCodexApiAdditionalUsageLimits(additionalRateLimits, context) {
|
|
|
203
212
|
scope,
|
|
204
213
|
rateLimit: entry.rate_limit,
|
|
205
214
|
now: context.now,
|
|
206
|
-
labelPrefix: scope === "spark" ? void 0 : limitName || meteredFeature || scope
|
|
207
|
-
requireBothWindows: false
|
|
215
|
+
labelPrefix: scope === "spark" ? void 0 : limitName || meteredFeature || scope
|
|
208
216
|
});
|
|
209
217
|
});
|
|
210
218
|
}
|
|
@@ -212,25 +220,17 @@ function buildCodexApiRateLimitUsageLimits(options) {
|
|
|
212
220
|
if (!isRecord(options.rateLimit)) {
|
|
213
221
|
return [];
|
|
214
222
|
}
|
|
215
|
-
const windows = [
|
|
216
|
-
|
|
217
|
-
["secondary", options.rateLimit.secondary_window]
|
|
218
|
-
];
|
|
219
|
-
const limits = windows.flatMap(([kind, window]) => {
|
|
223
|
+
const windows = [options.rateLimit.primary_window, options.rateLimit.secondary_window];
|
|
224
|
+
return windows.flatMap((window) => {
|
|
220
225
|
const limit = makeCodexApiUsageLimit({
|
|
221
226
|
targetId: options.targetId,
|
|
222
227
|
scope: options.scope,
|
|
223
|
-
windowKind: kind,
|
|
224
228
|
window,
|
|
225
229
|
now: options.now,
|
|
226
230
|
labelPrefix: options.labelPrefix
|
|
227
231
|
});
|
|
228
232
|
return limit == null ? [] : [limit];
|
|
229
233
|
});
|
|
230
|
-
if (options.requireBothWindows && limits.length !== 2) {
|
|
231
|
-
return [];
|
|
232
|
-
}
|
|
233
|
-
return limits;
|
|
234
234
|
}
|
|
235
235
|
function makeCodexApiUsageLimit(options) {
|
|
236
236
|
if (!isRecord(options.window)) {
|
|
@@ -240,8 +240,11 @@ function makeCodexApiUsageLimit(options) {
|
|
|
240
240
|
if (percentUsed == null) {
|
|
241
241
|
return null;
|
|
242
242
|
}
|
|
243
|
+
const window = codexApiWindowName(options.window);
|
|
244
|
+
if (window == null) {
|
|
245
|
+
return null;
|
|
246
|
+
}
|
|
243
247
|
const resetAt = parseApiEpochSeconds(options.window.reset_at);
|
|
244
|
-
const window = codexApiWindowName(options.window, options.windowKind);
|
|
245
248
|
const percentUsedClamped = clampPercent(percentUsed);
|
|
246
249
|
const percentRemaining = 100 - percentUsedClamped;
|
|
247
250
|
const resetText = resetAt == null ? null : `resets ${resetAt}`;
|
|
@@ -271,7 +274,7 @@ function codexAdditionalLimitScope(limitName, meteredFeature) {
|
|
|
271
274
|
const normalized = source.trim().toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
|
|
272
275
|
return normalized || "additional";
|
|
273
276
|
}
|
|
274
|
-
function codexApiWindowName(window
|
|
277
|
+
function codexApiWindowName(window) {
|
|
275
278
|
const seconds = parseApiNumber(window.limit_window_seconds);
|
|
276
279
|
if (seconds === 18e3) {
|
|
277
280
|
return "5h";
|
|
@@ -279,7 +282,7 @@ function codexApiWindowName(window, kind) {
|
|
|
279
282
|
if (seconds === 604800) {
|
|
280
283
|
return "weekly";
|
|
281
284
|
}
|
|
282
|
-
return
|
|
285
|
+
return null;
|
|
283
286
|
}
|
|
284
287
|
function formatWindowLabel(window) {
|
|
285
288
|
return window === "weekly" ? "Weekly" : window === "5h" ? "5h" : window;
|
|
@@ -320,10 +323,10 @@ function selectCodexStatus(result) {
|
|
|
320
323
|
if (snapshot == null) {
|
|
321
324
|
continue;
|
|
322
325
|
}
|
|
323
|
-
for (const content of [`${snapshot.screen}
|
|
324
|
-
${snapshot.raw}`, snapshot.
|
|
326
|
+
for (const content of [snapshot.screen, `${snapshot.screen}
|
|
327
|
+
${snapshot.raw}`, snapshot.raw]) {
|
|
325
328
|
const parsed = parseCodexStatus(cleanControlOutput(content));
|
|
326
|
-
if (parsed
|
|
329
|
+
if (isCompleteCodexStatus(parsed)) {
|
|
327
330
|
return parsed;
|
|
328
331
|
}
|
|
329
332
|
}
|
|
@@ -340,16 +343,32 @@ ${snapshot.raw}`);
|
|
|
340
343
|
function isCodexPromptReadyOrTrustPrompt(snapshot) {
|
|
341
344
|
return isCodexPromptReady(snapshot) || isCodexTrustPrompt(snapshot);
|
|
342
345
|
}
|
|
346
|
+
function isCodexPromptReadyOrStartupPrompt(snapshot) {
|
|
347
|
+
return isCodexPromptReadyOrTrustPrompt(snapshot) || isCodexModelMigrationPrompt(snapshot);
|
|
348
|
+
}
|
|
343
349
|
function isCodexTrustPrompt(snapshot) {
|
|
344
350
|
const cleanedOutput = cleanControlOutput(`${snapshot.screen}
|
|
345
351
|
${snapshot.raw}`);
|
|
346
352
|
return /do you trust the contents of this directory/i.test(cleanedOutput);
|
|
347
353
|
}
|
|
354
|
+
function isCodexModelMigrationPrompt(snapshot) {
|
|
355
|
+
return codexKeepModelSelection(snapshot) != null;
|
|
356
|
+
}
|
|
357
|
+
function isCodexPromptReadyOrMigrationPrompt(snapshot) {
|
|
358
|
+
return isCodexPromptReady(snapshot) || isCodexModelMigrationPrompt(snapshot);
|
|
359
|
+
}
|
|
360
|
+
function codexKeepModelSelection(snapshot) {
|
|
361
|
+
const cleanedScreen = cleanControlOutput(snapshot.screen);
|
|
362
|
+
const match = /(\d+)\.\s*Use existing model/i.exec(cleanedScreen);
|
|
363
|
+
return match?.[1] ?? null;
|
|
364
|
+
}
|
|
365
|
+
function dismissCodexModelMigrationPrompt(snapshot) {
|
|
366
|
+
return codexKeepModelSelection(snapshot) ?? void 0;
|
|
367
|
+
}
|
|
348
368
|
function hasCodexStatusLimits(snapshot) {
|
|
349
369
|
const cleanedOutput = cleanControlOutput(`${snapshot.screen}
|
|
350
370
|
${snapshot.raw}`);
|
|
351
|
-
|
|
352
|
-
return Boolean(parsed.main5hLimit && parsed.mainWeeklyLimit);
|
|
371
|
+
return isCompleteCodexStatus(parseCodexStatus(cleanedOutput));
|
|
353
372
|
}
|
|
354
373
|
function hasCodexStatusResponse(snapshot) {
|
|
355
374
|
const cleanedOutput = cleanControlOutput(`${snapshot.screen}
|
|
@@ -358,14 +377,12 @@ ${snapshot.raw}`);
|
|
|
358
377
|
return Boolean(parsed.main5hLimit || parsed.mainWeeklyLimit) || /refresh requested/i.test(cleanedOutput);
|
|
359
378
|
}
|
|
360
379
|
function buildCodexUsageResult(parsed, context) {
|
|
361
|
-
|
|
362
|
-
const errors = buildCodexUsageErrors(parsed, context);
|
|
380
|
+
assertCompleteCodexStatus(parsed);
|
|
363
381
|
return {
|
|
364
382
|
targetId: context.targetId,
|
|
365
383
|
displayName: context.displayName,
|
|
366
384
|
command: context.command,
|
|
367
|
-
limits: buildCodexUsageLimits(parsed, context)
|
|
368
|
-
errors: errors.length > 0 ? errors : void 0
|
|
385
|
+
limits: buildCodexUsageLimits(parsed, context)
|
|
369
386
|
};
|
|
370
387
|
}
|
|
371
388
|
function buildCodexUsageLimits(parsed, context) {
|
|
@@ -375,12 +392,15 @@ function buildCodexUsageLimits(parsed, context) {
|
|
|
375
392
|
return [];
|
|
376
393
|
}
|
|
377
394
|
const percentRemaining = parsePercentRemaining(raw);
|
|
395
|
+
if (percentRemaining == null) {
|
|
396
|
+
return [];
|
|
397
|
+
}
|
|
378
398
|
return [
|
|
379
399
|
makeUsageLimit({
|
|
380
400
|
targetId: context.targetId,
|
|
381
401
|
scope,
|
|
382
402
|
window,
|
|
383
|
-
percentUsed:
|
|
403
|
+
percentUsed: 100 - percentRemaining,
|
|
384
404
|
percentRemaining,
|
|
385
405
|
resetText: parseResetText(raw),
|
|
386
406
|
raw,
|
|
@@ -424,13 +444,19 @@ function parseCodexStatus(cleanedOutput) {
|
|
|
424
444
|
const label = labelMatch[1].trim();
|
|
425
445
|
const inlineValue = labelMatch[2].trim();
|
|
426
446
|
if (isCodexSparkLimitLabel(label)) {
|
|
427
|
-
|
|
428
|
-
|
|
447
|
+
const sparkKey = sparkLimitKey(label);
|
|
448
|
+
if (sparkKey) {
|
|
449
|
+
key = sparkKey;
|
|
450
|
+
setCodexLabeledValue(values, key, label, inlineValue);
|
|
451
|
+
} else {
|
|
452
|
+
section = "spark";
|
|
453
|
+
key = "";
|
|
454
|
+
}
|
|
429
455
|
continue;
|
|
430
456
|
}
|
|
431
457
|
key = labelToCodexKey(label, section);
|
|
432
|
-
if (key
|
|
433
|
-
|
|
458
|
+
if (key) {
|
|
459
|
+
setCodexLabeledValue(values, key, label, inlineValue);
|
|
434
460
|
}
|
|
435
461
|
continue;
|
|
436
462
|
}
|
|
@@ -452,35 +478,36 @@ function parseCodexStatus(cleanedOutput) {
|
|
|
452
478
|
sparkWeeklyLimit: values.sparkWeeklyLimit ?? ""
|
|
453
479
|
};
|
|
454
480
|
}
|
|
455
|
-
function
|
|
456
|
-
if (parsed
|
|
457
|
-
|
|
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");
|
|
481
|
+
function assertCompleteCodexStatus(parsed) {
|
|
482
|
+
if (!hasParseableCodexMainLimit(parsed)) {
|
|
483
|
+
throw new Error("Codex usage output did not include any parseable main rate-limit rows.");
|
|
465
484
|
}
|
|
466
|
-
if (!parsed
|
|
467
|
-
|
|
468
|
-
}
|
|
469
|
-
if (missing.length === 0) {
|
|
470
|
-
return [];
|
|
485
|
+
if (!hasOnlyParseableCodexLimits(parsed)) {
|
|
486
|
+
throw new Error("Codex usage output included an incomplete rate-limit row.");
|
|
471
487
|
}
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
488
|
+
}
|
|
489
|
+
function isCompleteCodexStatus(parsed) {
|
|
490
|
+
return hasParseableCodexMainLimit(parsed) && hasOnlyParseableCodexLimits(parsed);
|
|
491
|
+
}
|
|
492
|
+
function hasParseableCodexMainLimit(parsed) {
|
|
493
|
+
return parsePercentRemaining(parsed.main5hLimit) != null || parsePercentRemaining(parsed.mainWeeklyLimit) != null;
|
|
494
|
+
}
|
|
495
|
+
function hasOnlyParseableCodexLimits(parsed) {
|
|
496
|
+
const limits = CODEX_WINDOWS.map(([, , key]) => parsed[key].trim()).filter(Boolean);
|
|
497
|
+
return limits.every((limit) => parsePercentRemaining(limit) != null);
|
|
480
498
|
}
|
|
481
499
|
function isCodexSparkLimitLabel(label) {
|
|
482
500
|
return /\bspark\b/i.test(label) && /\blimit\b/i.test(label);
|
|
483
501
|
}
|
|
502
|
+
function sparkLimitKey(label) {
|
|
503
|
+
if (/\b5h limit$/i.test(label)) {
|
|
504
|
+
return "spark5hLimit";
|
|
505
|
+
}
|
|
506
|
+
if (/\bweekly limit$/i.test(label)) {
|
|
507
|
+
return "sparkWeeklyLimit";
|
|
508
|
+
}
|
|
509
|
+
return "";
|
|
510
|
+
}
|
|
484
511
|
function labelToCodexKey(label, section) {
|
|
485
512
|
if (label === "Model") return "model";
|
|
486
513
|
if (label === "Directory") return "directory";
|
|
@@ -495,6 +522,13 @@ function labelToCodexKey(label, section) {
|
|
|
495
522
|
}
|
|
496
523
|
return "";
|
|
497
524
|
}
|
|
525
|
+
function setCodexLabeledValue(values, key, label, inlineValue) {
|
|
526
|
+
if (inlineValue) {
|
|
527
|
+
setValue(values, key, inlineValue);
|
|
528
|
+
} else if (CODEX_WINDOWS.some(([, , limitKey]) => limitKey === key)) {
|
|
529
|
+
setValue(values, key, `${label}:`);
|
|
530
|
+
}
|
|
531
|
+
}
|
|
498
532
|
function setValue(values, key, value) {
|
|
499
533
|
const sanitized = sanitizeCodexValue(value);
|
|
500
534
|
if (!sanitized) {
|
|
@@ -78,17 +78,18 @@ async function runPtyScenario(options) {
|
|
|
78
78
|
});
|
|
79
79
|
const cancellationPromise = new Promise((_, reject) => {
|
|
80
80
|
cancelScenario = (message) => {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
safeKillPty(child);
|
|
85
|
-
}
|
|
81
|
+
timedOut = true;
|
|
82
|
+
if (!exited && child) {
|
|
83
|
+
safeKillPty(child);
|
|
86
84
|
}
|
|
87
85
|
reject(buildScenarioError(message));
|
|
88
86
|
};
|
|
89
87
|
});
|
|
90
88
|
cancellationPromise.catch(() => {
|
|
91
89
|
});
|
|
90
|
+
timeout = setTimeout(() => {
|
|
91
|
+
cancelScenario?.(`PTY scenario timed out after ${formatDuration(timeoutMs)}.`);
|
|
92
|
+
}, timeoutMs);
|
|
92
93
|
if (options.signal) {
|
|
93
94
|
const abortHandler = () => {
|
|
94
95
|
cancelScenario?.(formatAbortReason(options.signal, timeoutMs));
|
|
@@ -99,10 +100,6 @@ async function runPtyScenario(options) {
|
|
|
99
100
|
options.signal.addEventListener("abort", abortHandler, { once: true });
|
|
100
101
|
removeAbortListener = () => options.signal?.removeEventListener("abort", abortHandler);
|
|
101
102
|
}
|
|
102
|
-
} else {
|
|
103
|
-
timeout = setTimeout(() => {
|
|
104
|
-
cancelScenario?.(`PTY scenario timed out after ${formatDuration(timeoutMs)}.`);
|
|
105
|
-
}, timeoutMs);
|
|
106
103
|
}
|
|
107
104
|
const withScenarioTimeout = async (promise) => Promise.race([promise, cancellationPromise]);
|
|
108
105
|
const throwIfTimedOut = () => {
|
|
@@ -160,7 +157,13 @@ async function runPtyScenario(options) {
|
|
|
160
157
|
}
|
|
161
158
|
if (step.write != null) {
|
|
162
159
|
throwIfTimedOut();
|
|
163
|
-
|
|
160
|
+
const write = typeof step.write === "function" ? await withScenarioTimeout(
|
|
161
|
+
Promise.resolve(step.write({ raw, screen: readScreen(terminal) }))
|
|
162
|
+
) : step.write;
|
|
163
|
+
throwIfTimedOut();
|
|
164
|
+
if (write != null && !exited) {
|
|
165
|
+
child.write(write);
|
|
166
|
+
}
|
|
164
167
|
}
|
|
165
168
|
if (step.waitFor != null) {
|
|
166
169
|
const matched = await withScenarioTimeout(
|
|
@@ -355,8 +358,8 @@ function sleep(ms) {
|
|
|
355
358
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
356
359
|
}
|
|
357
360
|
export {
|
|
358
|
-
|
|
359
|
-
|
|
361
|
+
escapeKey as a,
|
|
362
|
+
enterKey as e,
|
|
360
363
|
runPtyScenario as r,
|
|
361
364
|
typeTextSteps as t
|
|
362
365
|
};
|
package/package.json
CHANGED
package/dist/agy-Clt40QYQ.js
DELETED
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
import { c as cleanControlOutput, m as makeUsageLimit, a as compactLines } from "./cli.js";
|
|
2
|
-
import { r as runPtyScenario, t as typeTextSteps, e as escapeKey, a as enterKey } from "./pty-CMd3Wxfe.js";
|
|
3
|
-
const TRUST_DIALOG_PATTERN = /Do you trust the contents of this project\?/i;
|
|
4
|
-
const READY_PATTERN = /\?\s+for shortcuts/i;
|
|
5
|
-
const REMAINING_CREDITS_PATTERN = /Remaining AI Credits:\s*(.+?)\s*$/i;
|
|
6
|
-
const CREDITS_NOT_ENABLED_PATTERN = /AI Credits not enabled/i;
|
|
7
|
-
const ACCOUNT_PLAN_PATTERN = /^\S+@\S+\.\S+\s+\((.+?)\)\s*$/;
|
|
8
|
-
const STANDALONE_PLAN_PATTERN = /^\((.*(?:quota|plan|tier).*)\)$/i;
|
|
9
|
-
const SIGN_IN_PATTERN = /\b(?:not signed in|Signing in)\b/i;
|
|
10
|
-
function isTrustDialog(snapshot) {
|
|
11
|
-
return TRUST_DIALOG_PATTERN.test(snapshot.raw) || TRUST_DIALOG_PATTERN.test(snapshot.screen);
|
|
12
|
-
}
|
|
13
|
-
function isReadyOrTrustDialog(snapshot) {
|
|
14
|
-
return READY_PATTERN.test(snapshot.screen) || isTrustDialog(snapshot);
|
|
15
|
-
}
|
|
16
|
-
function hasCreditsPanel(snapshot) {
|
|
17
|
-
return REMAINING_CREDITS_PATTERN.test(snapshot.screen) || REMAINING_CREDITS_PATTERN.test(cleanControlOutput(snapshot.raw));
|
|
18
|
-
}
|
|
19
|
-
function withTrustSkip(step) {
|
|
20
|
-
return { ...step, skipIf: isTrustDialog, skipIfSource: "raw" };
|
|
21
|
-
}
|
|
22
|
-
async function extractAgyUsage(context) {
|
|
23
|
-
const command = context.command ?? context.launch?.command ?? "agy";
|
|
24
|
-
const ptyResult = await runPtyScenario({
|
|
25
|
-
command,
|
|
26
|
-
args: context.launch?.args ?? [],
|
|
27
|
-
cwd: context.repoRoot,
|
|
28
|
-
cols: 120,
|
|
29
|
-
rows: 40,
|
|
30
|
-
timeoutMs: context.launch?.timeoutMs ?? 7e4,
|
|
31
|
-
signal: context.signal,
|
|
32
|
-
debug: context.debug,
|
|
33
|
-
steps: [
|
|
34
|
-
{ waitFor: isReadyOrTrustDialog, waitForTimeoutMs: 25e3 },
|
|
35
|
-
...typeTextSteps("/credits", 25).map(withTrustSkip),
|
|
36
|
-
withTrustSkip({ waitMs: 250, write: enterKey() }),
|
|
37
|
-
withTrustSkip({
|
|
38
|
-
waitFor: hasCreditsPanel,
|
|
39
|
-
waitForTimeoutMs: 15e3,
|
|
40
|
-
optional: true,
|
|
41
|
-
capture: "credits",
|
|
42
|
-
captureWaitMs: 500
|
|
43
|
-
}),
|
|
44
|
-
{ write: escapeKey(), waitMs: 250 }
|
|
45
|
-
]
|
|
46
|
-
});
|
|
47
|
-
const buildError = (message) => {
|
|
48
|
-
const error = new Error(message);
|
|
49
|
-
if (ptyResult.debug.length > 0) {
|
|
50
|
-
Object.assign(error, { debug: ptyResult.debug });
|
|
51
|
-
}
|
|
52
|
-
return error;
|
|
53
|
-
};
|
|
54
|
-
if (isTrustDialog(ptyResult)) {
|
|
55
|
-
throw buildError(
|
|
56
|
-
`Antigravity has not trusted this project yet. Run \`${command}\` in ${context.repoRoot} once, accept the trust prompt, then re-run usage.`
|
|
57
|
-
);
|
|
58
|
-
}
|
|
59
|
-
const snapshot = ptyResult.snapshots.credits ?? ptyResult;
|
|
60
|
-
const cleanedOutput = cleanControlOutput(snapshot.raw);
|
|
61
|
-
const parsed = parseAgyCredits(snapshot.screen, cleanedOutput);
|
|
62
|
-
if (parsed.notEnabled) {
|
|
63
|
-
throw buildError(
|
|
64
|
-
"Antigravity AI Credits are not enabled for this account. Enable them via /settings in agy."
|
|
65
|
-
);
|
|
66
|
-
}
|
|
67
|
-
if (parsed.remaining == null) {
|
|
68
|
-
if (SIGN_IN_PATTERN.test(snapshot.screen) || SIGN_IN_PATTERN.test(cleanedOutput)) {
|
|
69
|
-
throw buildError(`Antigravity is not signed in. Run \`${command}\` and complete the login.`);
|
|
70
|
-
}
|
|
71
|
-
throw buildError("Antigravity /credits output did not include a Remaining AI Credits value.");
|
|
72
|
-
}
|
|
73
|
-
return {
|
|
74
|
-
targetId: context.targetId,
|
|
75
|
-
displayName: context.displayName,
|
|
76
|
-
command,
|
|
77
|
-
limits: [
|
|
78
|
-
// Credits are an absolute balance rather than a percentage window.
|
|
79
|
-
makeUsageLimit({
|
|
80
|
-
targetId: context.targetId,
|
|
81
|
-
scope: "ai_credits",
|
|
82
|
-
window: "credits",
|
|
83
|
-
label: parsed.plan ? `AI Credits (${parsed.plan})` : "AI Credits",
|
|
84
|
-
percentUsed: null,
|
|
85
|
-
percentRemaining: null,
|
|
86
|
-
remainingText: parsed.remaining,
|
|
87
|
-
resetText: null,
|
|
88
|
-
raw: parsed.rawLine,
|
|
89
|
-
now: context.now
|
|
90
|
-
})
|
|
91
|
-
],
|
|
92
|
-
debug: ptyResult.debug.length > 0 ? ptyResult.debug : void 0
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
function parseAgyCredits(screen, cleanedOutput = "") {
|
|
96
|
-
const fromScreen = parseAgyCreditsLines(compactLines(screen));
|
|
97
|
-
if (fromScreen.remaining != null || fromScreen.notEnabled) {
|
|
98
|
-
return fromScreen;
|
|
99
|
-
}
|
|
100
|
-
const fromRaw = parseAgyCreditsLines(compactLines(cleanedOutput));
|
|
101
|
-
if (fromRaw.remaining != null || fromRaw.notEnabled) {
|
|
102
|
-
return fromRaw;
|
|
103
|
-
}
|
|
104
|
-
return fromScreen.plan != null ? fromScreen : fromRaw;
|
|
105
|
-
}
|
|
106
|
-
function parseAgyCreditsLines(lines) {
|
|
107
|
-
const parsed = {
|
|
108
|
-
remaining: null,
|
|
109
|
-
notEnabled: false,
|
|
110
|
-
plan: null,
|
|
111
|
-
rawLine: ""
|
|
112
|
-
};
|
|
113
|
-
for (const line of lines) {
|
|
114
|
-
const remainingMatch = REMAINING_CREDITS_PATTERN.exec(line);
|
|
115
|
-
if (remainingMatch?.[1]) {
|
|
116
|
-
const value = remainingMatch[1].trim();
|
|
117
|
-
if (CREDITS_NOT_ENABLED_PATTERN.test(value)) {
|
|
118
|
-
parsed.notEnabled = true;
|
|
119
|
-
parsed.rawLine = line.trim();
|
|
120
|
-
continue;
|
|
121
|
-
}
|
|
122
|
-
parsed.remaining = value;
|
|
123
|
-
parsed.rawLine = line.trim();
|
|
124
|
-
continue;
|
|
125
|
-
}
|
|
126
|
-
if (parsed.plan == null) {
|
|
127
|
-
const planMatch = ACCOUNT_PLAN_PATTERN.exec(line) ?? STANDALONE_PLAN_PATTERN.exec(line);
|
|
128
|
-
if (planMatch?.[1]) {
|
|
129
|
-
parsed.plan = planMatch[1].trim();
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
return parsed;
|
|
134
|
-
}
|
|
135
|
-
export {
|
|
136
|
-
extractAgyUsage,
|
|
137
|
-
parseAgyCredits
|
|
138
|
-
};
|