omniagent 0.1.13 → 0.1.16
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 +24 -9
- package/dist/agy-DlbWeyP_.js +379 -0
- package/dist/{claude-DWeyS4fP.js → claude-CcxRb8OW.js} +2 -2
- package/dist/cli.js +1044 -154
- package/dist/{codex-0b2YLh_8.js → codex-DhlBosXm.js} +2 -2
- package/dist/{pty-CZBSAJzE.js → pty-D8BFQQQg.js} +13 -10
- package/package.json +4 -1
- package/dist/gemini-BVRg6OMO.js +0 -437
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# omniagent
|
|
2
2
|
|
|
3
|
-
One source of truth for agent config across Claude, Codex,
|
|
3
|
+
One source of truth for agent config across Claude, Codex, Antigravity (agy), and Copilot (and [any other agent](docs/custom-targets.md))
|
|
4
4
|
|
|
5
5
|
Define canonical agent files once in `agents/`, then run `sync` to compile target-specific outputs.
|
|
6
6
|
|
|
@@ -89,14 +89,17 @@ omniagent --agent codex
|
|
|
89
89
|
omniagent -p "Summarize this repo" --agent codex --output json
|
|
90
90
|
```
|
|
91
91
|
|
|
92
|
-
`usage` supports Codex, Claude, and
|
|
93
|
-
extraction yet. Usage extraction may launch agent TUIs and may incur
|
|
94
|
-
reads repo context or instructions on startup; omniagent uses cheap/minimal
|
|
95
|
-
where possible. Usage extraction times out after 30 seconds unless the target
|
|
96
|
-
a target-specific timeout; built-in TUI probes may use longer defaults. Some CLIs
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
92
|
+
`usage` supports Codex, Claude, and Antigravity (`agy`; `gemini` works as an alias). Copilot
|
|
93
|
+
is not supported for usage extraction yet. Usage extraction may launch agent TUIs and may incur
|
|
94
|
+
cost if an agent reads repo context or instructions on startup; omniagent uses cheap/minimal
|
|
95
|
+
launch settings where possible. Usage extraction times out after 30 seconds unless the target
|
|
96
|
+
config defines a target-specific timeout; built-in TUI probes may use longer defaults. Some CLIs
|
|
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
|
|
102
|
+
run.
|
|
100
103
|
|
|
101
104
|
## Local Overrides (`.local`)
|
|
102
105
|
|
|
@@ -213,6 +216,18 @@ Example usage:
|
|
|
213
216
|
./code-review.sh codex
|
|
214
217
|
```
|
|
215
218
|
|
|
219
|
+
The shim also unifies structured outputs: pass a JSON schema (file path or inline JSON) with
|
|
220
|
+
`--output-schema`, and stdout is exactly the schema-conforming JSON regardless of agent. Agents
|
|
221
|
+
with native schema support (claude, codex) enforce it server-side; all others get a prompt-based
|
|
222
|
+
fallback with client-side validation and automatic retries:
|
|
223
|
+
|
|
224
|
+
```bash
|
|
225
|
+
omniagent -p "Top 3 benefits of TypeScript" --agent claude \
|
|
226
|
+
--output-schema ./schema.json | jq .answer
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
See [`docs/cli-shim.md`](docs/cli-shim.md) for the full shared-flag capability matrix.
|
|
230
|
+
|
|
216
231
|
## Documentation
|
|
217
232
|
|
|
218
233
|
- Docs index: [`docs/README.md`](docs/README.md)
|
|
@@ -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, e as enterKey, a as escapeKey } from "./pty-
|
|
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"];
|