omniagent 0.1.9 → 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 +3 -3
- package/dist/{codex-D1RuzsY6.js → codex-0b2YLh_8.js} +256 -0
- package/dist/gemini-BVRg6OMO.js +437 -0
- package/package.json +1 -1
- package/dist/claude-Dmv_YFKX.js +0 -146
- package/dist/gemini-CskI3Qjp.js +0 -168
|
@@ -0,0 +1,437 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { c as cleanControlOutput, m as makeUsageLimit, a as compactLines } from "./cli.js";
|
|
4
|
+
import { r as runPtyScenario, t as typeTextSteps, e as enterKey, a as escapeKey } from "./pty-CZBSAJzE.js";
|
|
5
|
+
const TIER_MODEL_IDS = /* @__PURE__ */ new Map([
|
|
6
|
+
["Flash", "flash"],
|
|
7
|
+
["Flash Lite", "flash-lite"],
|
|
8
|
+
["Pro", "pro"]
|
|
9
|
+
]);
|
|
10
|
+
const GEMINI_OAUTH_PATH = [".gemini", "oauth_creds.json"];
|
|
11
|
+
const GEMINI_CODE_ASSIST_ENDPOINT = "https://cloudcode-pa.googleapis.com";
|
|
12
|
+
const GEMINI_CODE_ASSIST_API_VERSION = "v1internal";
|
|
13
|
+
const GEMINI_CODE_ASSIST_TIMEOUT_MS = 1e4;
|
|
14
|
+
const GEMINI_OAUTH_EXPIRY_SKEW_MS = 6e4;
|
|
15
|
+
const GEMINI_MODEL_TIERS = /* @__PURE__ */ new Map([
|
|
16
|
+
["gemini-2.5-flash", "flash"],
|
|
17
|
+
["gemini-2.5-flash-lite", "flash-lite"],
|
|
18
|
+
["gemini-2.5-pro", "pro"],
|
|
19
|
+
["gemini-3-flash-preview", "flash"],
|
|
20
|
+
["gemini-3-pro-preview", "pro"],
|
|
21
|
+
["gemini-3.1-flash-lite-preview", "flash-lite"],
|
|
22
|
+
["gemini-3.1-pro-preview", "pro"]
|
|
23
|
+
]);
|
|
24
|
+
const GEMINI_TIER_DISPLAY_NAMES = /* @__PURE__ */ new Map([
|
|
25
|
+
["flash", "Flash"],
|
|
26
|
+
["flash-lite", "Flash Lite"],
|
|
27
|
+
["pro", "Pro"]
|
|
28
|
+
]);
|
|
29
|
+
async function extractGeminiUsage(context) {
|
|
30
|
+
try {
|
|
31
|
+
return await extractGeminiUsageFromApi(context);
|
|
32
|
+
} catch (error) {
|
|
33
|
+
if (context.signal.aborted) {
|
|
34
|
+
throw error;
|
|
35
|
+
}
|
|
36
|
+
return extractGeminiUsageFromTui(context);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
async function extractGeminiUsageFromTui(context) {
|
|
40
|
+
const command = context.command ?? context.launch?.command ?? "gemini";
|
|
41
|
+
const ptyResult = await runPtyScenario({
|
|
42
|
+
command,
|
|
43
|
+
args: context.launch?.args ?? ["--skip-trust"],
|
|
44
|
+
cwd: context.repoRoot,
|
|
45
|
+
cols: 110,
|
|
46
|
+
rows: 42,
|
|
47
|
+
timeoutMs: context.launch?.timeoutMs ?? 7e4,
|
|
48
|
+
signal: context.signal,
|
|
49
|
+
debug: context.debug,
|
|
50
|
+
steps: [
|
|
51
|
+
{ waitFor: isGeminiPromptReady, waitForTimeoutMs: 12e3 },
|
|
52
|
+
...typeTextSteps("/model", 20),
|
|
53
|
+
{ waitMs: 150, write: enterKey() },
|
|
54
|
+
{
|
|
55
|
+
waitFor: hasGeminiModelUsage,
|
|
56
|
+
waitForTimeoutMs: 15e3,
|
|
57
|
+
capture: "model",
|
|
58
|
+
captureWaitMs: 500
|
|
59
|
+
},
|
|
60
|
+
{ write: escapeKey() },
|
|
61
|
+
{ waitMs: 500 },
|
|
62
|
+
...typeTextSteps("/quit", 20),
|
|
63
|
+
{ waitMs: 150, write: enterKey() },
|
|
64
|
+
{ waitMs: 500 }
|
|
65
|
+
]
|
|
66
|
+
});
|
|
67
|
+
const modelSnapshot = ptyResult.snapshots.model ?? ptyResult;
|
|
68
|
+
const cleanedOutput = cleanControlOutput(modelSnapshot.raw);
|
|
69
|
+
const parsed = parseGeminiModelDialog(modelSnapshot.screen, cleanedOutput);
|
|
70
|
+
if (parsed.usage.length === 0) {
|
|
71
|
+
throw new Error("Gemini usage output did not include model usage rows.");
|
|
72
|
+
}
|
|
73
|
+
return {
|
|
74
|
+
targetId: context.targetId,
|
|
75
|
+
displayName: context.displayName,
|
|
76
|
+
command,
|
|
77
|
+
limits: parsed.usage.map((row) => {
|
|
78
|
+
const modelId = resolveModelId(row.name, parsed.availableModels);
|
|
79
|
+
return makeUsageLimit({
|
|
80
|
+
targetId: context.targetId,
|
|
81
|
+
scope: modelScope(modelId),
|
|
82
|
+
window: "model",
|
|
83
|
+
label: row.name,
|
|
84
|
+
modelId,
|
|
85
|
+
modelLabel: row.name,
|
|
86
|
+
percentUsed: row.percentUsed,
|
|
87
|
+
percentRemaining: 100 - row.percentUsed,
|
|
88
|
+
resetText: row.resetText,
|
|
89
|
+
raw: row.raw,
|
|
90
|
+
now: context.now
|
|
91
|
+
});
|
|
92
|
+
}),
|
|
93
|
+
debug: ptyResult.debug.length > 0 ? ptyResult.debug : void 0
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
async function extractGeminiUsageFromApi(context) {
|
|
97
|
+
const command = context.command ?? context.launch?.command ?? "gemini";
|
|
98
|
+
const auth = await readGeminiOAuthCredentials(context.homeDir);
|
|
99
|
+
if (auth == null) {
|
|
100
|
+
throw new Error("Gemini OAuth credentials were not available.");
|
|
101
|
+
}
|
|
102
|
+
const accessToken = resolveGeminiAccessToken(auth);
|
|
103
|
+
const projectResponse = await fetchGeminiCodeAssistJson(
|
|
104
|
+
"loadCodeAssist",
|
|
105
|
+
accessToken,
|
|
106
|
+
buildGeminiLoadCodeAssistRequest(),
|
|
107
|
+
context.signal
|
|
108
|
+
);
|
|
109
|
+
if (projectResponse.status >= 400) {
|
|
110
|
+
throw new Error(`Gemini Code Assist load API returned HTTP ${projectResponse.status}.`);
|
|
111
|
+
}
|
|
112
|
+
const project = extractGeminiCodeAssistProject(projectResponse.body);
|
|
113
|
+
if (project == null) {
|
|
114
|
+
throw new Error("Gemini Code Assist load API did not return a project.");
|
|
115
|
+
}
|
|
116
|
+
const quotaResponse = await fetchGeminiCodeAssistJson(
|
|
117
|
+
"retrieveUserQuota",
|
|
118
|
+
accessToken,
|
|
119
|
+
{ project },
|
|
120
|
+
context.signal
|
|
121
|
+
);
|
|
122
|
+
if (quotaResponse.status >= 400) {
|
|
123
|
+
throw new Error(`Gemini Code Assist quota API returned HTTP ${quotaResponse.status}.`);
|
|
124
|
+
}
|
|
125
|
+
return buildGeminiApiUsageResult(quotaResponse.body, {
|
|
126
|
+
targetId: context.targetId,
|
|
127
|
+
displayName: context.displayName,
|
|
128
|
+
now: context.now,
|
|
129
|
+
command
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
function buildGeminiApiUsageResult(payload, context) {
|
|
133
|
+
const usage = isRecord(payload) ? payload : {};
|
|
134
|
+
const limits = buildGeminiApiUsageLimits(usage.buckets, context);
|
|
135
|
+
if (limits.length === 0) {
|
|
136
|
+
throw new Error("Gemini Code Assist quota API response did not include model quota buckets.");
|
|
137
|
+
}
|
|
138
|
+
return {
|
|
139
|
+
targetId: context.targetId,
|
|
140
|
+
displayName: context.displayName,
|
|
141
|
+
command: context.command,
|
|
142
|
+
limits
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
function extractGeminiOAuthCredentials(blob) {
|
|
146
|
+
let parsed;
|
|
147
|
+
try {
|
|
148
|
+
parsed = JSON.parse(blob);
|
|
149
|
+
} catch {
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
if (!isRecord(parsed)) {
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
const accessToken = typeof parsed.access_token === "string" && parsed.access_token.trim() ? parsed.access_token : void 0;
|
|
156
|
+
const refreshToken = typeof parsed.refresh_token === "string" && parsed.refresh_token.trim() ? parsed.refresh_token : void 0;
|
|
157
|
+
const expiryDate = parseApiNumber(parsed.expiry_date) ?? void 0;
|
|
158
|
+
if (!accessToken && !refreshToken) {
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
return { accessToken, refreshToken, expiryDate };
|
|
162
|
+
}
|
|
163
|
+
async function readGeminiOAuthCredentials(homeDir) {
|
|
164
|
+
try {
|
|
165
|
+
const raw = await readFile(path.join(homeDir, ...GEMINI_OAUTH_PATH), "utf8");
|
|
166
|
+
return extractGeminiOAuthCredentials(raw);
|
|
167
|
+
} catch {
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
function resolveGeminiAccessToken(auth) {
|
|
172
|
+
if (auth.accessToken && (auth.expiryDate == null || auth.expiryDate - Date.now() > GEMINI_OAUTH_EXPIRY_SKEW_MS)) {
|
|
173
|
+
return auth.accessToken;
|
|
174
|
+
}
|
|
175
|
+
throw new Error("Gemini cached access token was not available or expired.");
|
|
176
|
+
}
|
|
177
|
+
async function fetchGeminiCodeAssistJson(method, accessToken, body, parentSignal) {
|
|
178
|
+
const endpoint = process.env.CODE_ASSIST_ENDPOINT ?? GEMINI_CODE_ASSIST_ENDPOINT;
|
|
179
|
+
const version = process.env.CODE_ASSIST_API_VERSION ?? GEMINI_CODE_ASSIST_API_VERSION;
|
|
180
|
+
const response = await fetchWithTimeout(
|
|
181
|
+
`${endpoint}/${version}:${method}`,
|
|
182
|
+
{
|
|
183
|
+
method: "POST",
|
|
184
|
+
headers: {
|
|
185
|
+
"Content-Type": "application/json",
|
|
186
|
+
Authorization: `Bearer ${accessToken}`
|
|
187
|
+
},
|
|
188
|
+
body: JSON.stringify(body)
|
|
189
|
+
},
|
|
190
|
+
parentSignal
|
|
191
|
+
);
|
|
192
|
+
return {
|
|
193
|
+
status: response.status,
|
|
194
|
+
body: await response.json()
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
async function fetchWithTimeout(input, init, parentSignal) {
|
|
198
|
+
const controller = new AbortController();
|
|
199
|
+
const timeout = setTimeout(() => {
|
|
200
|
+
controller.abort(new Error("Gemini Code Assist API request timed out."));
|
|
201
|
+
}, GEMINI_CODE_ASSIST_TIMEOUT_MS);
|
|
202
|
+
const abortFromParent = () => {
|
|
203
|
+
controller.abort(parentSignal.reason);
|
|
204
|
+
};
|
|
205
|
+
parentSignal.addEventListener("abort", abortFromParent, { once: true });
|
|
206
|
+
try {
|
|
207
|
+
return await fetch(input, { ...init, signal: controller.signal });
|
|
208
|
+
} finally {
|
|
209
|
+
clearTimeout(timeout);
|
|
210
|
+
parentSignal.removeEventListener("abort", abortFromParent);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
function buildGeminiLoadCodeAssistRequest() {
|
|
214
|
+
const project = process.env.GOOGLE_CLOUD_PROJECT ?? process.env.GOOGLE_CLOUD_PROJECT_ID;
|
|
215
|
+
const metadata = {
|
|
216
|
+
ideType: "IDE_UNSPECIFIED",
|
|
217
|
+
platform: "PLATFORM_UNSPECIFIED",
|
|
218
|
+
pluginType: "GEMINI"
|
|
219
|
+
};
|
|
220
|
+
if (project) {
|
|
221
|
+
metadata.duetProject = project;
|
|
222
|
+
}
|
|
223
|
+
return {
|
|
224
|
+
...project ? { cloudaicompanionProject: project } : {},
|
|
225
|
+
metadata
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
function extractGeminiCodeAssistProject(payload) {
|
|
229
|
+
if (!isRecord(payload)) {
|
|
230
|
+
return null;
|
|
231
|
+
}
|
|
232
|
+
const project = payload.cloudaicompanionProject;
|
|
233
|
+
if (typeof project === "string" && project.trim()) {
|
|
234
|
+
return project;
|
|
235
|
+
}
|
|
236
|
+
if (isRecord(project) && typeof project.id === "string" && project.id.trim()) {
|
|
237
|
+
return project.id;
|
|
238
|
+
}
|
|
239
|
+
return null;
|
|
240
|
+
}
|
|
241
|
+
function buildGeminiApiUsageLimits(buckets, context) {
|
|
242
|
+
if (!Array.isArray(buckets)) {
|
|
243
|
+
return [];
|
|
244
|
+
}
|
|
245
|
+
return selectGeminiQuotaRows(buckets).map(
|
|
246
|
+
(row) => makeGeminiApiUsageLimit({
|
|
247
|
+
targetId: context.targetId,
|
|
248
|
+
now: context.now,
|
|
249
|
+
row
|
|
250
|
+
})
|
|
251
|
+
);
|
|
252
|
+
}
|
|
253
|
+
function selectGeminiQuotaRows(buckets) {
|
|
254
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
255
|
+
for (const bucket of buckets) {
|
|
256
|
+
if (!isRecord(bucket) || typeof bucket.modelId !== "string") {
|
|
257
|
+
continue;
|
|
258
|
+
}
|
|
259
|
+
const remainingFraction = parseApiNumber(bucket.remainingFraction);
|
|
260
|
+
if (remainingFraction == null) {
|
|
261
|
+
continue;
|
|
262
|
+
}
|
|
263
|
+
const modelId = bucket.modelId;
|
|
264
|
+
const tier = GEMINI_MODEL_TIERS.get(modelId);
|
|
265
|
+
const groupId = tier ?? modelId;
|
|
266
|
+
const label = tier == null ? formatGeminiModelLabel(modelId) : GEMINI_TIER_DISPLAY_NAMES.get(tier) ?? tier;
|
|
267
|
+
const row = {
|
|
268
|
+
modelId: groupId,
|
|
269
|
+
label,
|
|
270
|
+
remainingFraction: clampFraction(remainingFraction),
|
|
271
|
+
resetAt: parseGeminiResetTime(bucket.resetTime)
|
|
272
|
+
};
|
|
273
|
+
const existing = grouped.get(groupId);
|
|
274
|
+
if (existing == null || row.remainingFraction < existing.remainingFraction) {
|
|
275
|
+
grouped.set(groupId, row);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
return [...grouped.values()];
|
|
279
|
+
}
|
|
280
|
+
function makeGeminiApiUsageLimit(options) {
|
|
281
|
+
const percentUsed = Math.round((1 - options.row.remainingFraction) * 100);
|
|
282
|
+
const percentRemaining = 100 - percentUsed;
|
|
283
|
+
const resetText = options.row.resetAt == null ? null : `resets ${options.row.resetAt}`;
|
|
284
|
+
const raw = `${options.row.label} ${percentUsed}% used${resetText == null ? "" : ` (${resetText})`}`;
|
|
285
|
+
const limit = makeUsageLimit({
|
|
286
|
+
targetId: options.targetId,
|
|
287
|
+
scope: modelScope(options.row.modelId),
|
|
288
|
+
window: "model",
|
|
289
|
+
label: options.row.label,
|
|
290
|
+
modelId: options.row.modelId,
|
|
291
|
+
modelLabel: options.row.label,
|
|
292
|
+
percentUsed,
|
|
293
|
+
percentRemaining,
|
|
294
|
+
resetText,
|
|
295
|
+
raw,
|
|
296
|
+
now: options.now
|
|
297
|
+
});
|
|
298
|
+
return {
|
|
299
|
+
...limit,
|
|
300
|
+
resetAt: options.row.resetAt
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
function formatGeminiModelLabel(modelId) {
|
|
304
|
+
return modelId.length > 12 ? `${modelId.slice(0, 11)}…` : modelId;
|
|
305
|
+
}
|
|
306
|
+
function parseGeminiResetTime(value) {
|
|
307
|
+
if (typeof value !== "string" || !value.trim()) {
|
|
308
|
+
return null;
|
|
309
|
+
}
|
|
310
|
+
const reset = new Date(value);
|
|
311
|
+
if (Number.isNaN(reset.getTime()) || reset.getUTCFullYear() < 2e3) {
|
|
312
|
+
return null;
|
|
313
|
+
}
|
|
314
|
+
return reset.toISOString();
|
|
315
|
+
}
|
|
316
|
+
function parseApiNumber(value) {
|
|
317
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
318
|
+
return value;
|
|
319
|
+
}
|
|
320
|
+
if (typeof value !== "string" || !value.trim()) {
|
|
321
|
+
return null;
|
|
322
|
+
}
|
|
323
|
+
const parsed = Number(value);
|
|
324
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
325
|
+
}
|
|
326
|
+
function clampFraction(value) {
|
|
327
|
+
return Math.max(0, Math.min(1, value));
|
|
328
|
+
}
|
|
329
|
+
function isRecord(value) {
|
|
330
|
+
return value != null && typeof value === "object" && !Array.isArray(value);
|
|
331
|
+
}
|
|
332
|
+
function isGeminiPromptReady(snapshot) {
|
|
333
|
+
const screen = snapshot.screen || cleanControlOutput(snapshot.raw);
|
|
334
|
+
return /Type your message|quota/i.test(screen);
|
|
335
|
+
}
|
|
336
|
+
function hasGeminiModelUsage(snapshot) {
|
|
337
|
+
const parsed = parseGeminiModelDialog(snapshot.screen, cleanControlOutput(snapshot.raw));
|
|
338
|
+
return parsed.usage.length > 0;
|
|
339
|
+
}
|
|
340
|
+
function parseGeminiModelDialog(screen, cleanedOutput = "") {
|
|
341
|
+
const fromScreen = parseGeminiLines(compactLines(screen));
|
|
342
|
+
if (fromScreen.usage.length > 0) {
|
|
343
|
+
return fromScreen;
|
|
344
|
+
}
|
|
345
|
+
return parseGeminiLines(compactLines(cleanedOutput));
|
|
346
|
+
}
|
|
347
|
+
function parseGeminiLines(lines) {
|
|
348
|
+
const availableModels = [];
|
|
349
|
+
const usage = [];
|
|
350
|
+
let selectedModel = "";
|
|
351
|
+
let inUsage = false;
|
|
352
|
+
for (const line of lines) {
|
|
353
|
+
const content = stripGeminiFrame(line);
|
|
354
|
+
if (!content) {
|
|
355
|
+
continue;
|
|
356
|
+
}
|
|
357
|
+
const model = parseAvailableModel(content);
|
|
358
|
+
if (model != null) {
|
|
359
|
+
availableModels.push(model.id);
|
|
360
|
+
if (model.selected) {
|
|
361
|
+
selectedModel = model.id;
|
|
362
|
+
}
|
|
363
|
+
continue;
|
|
364
|
+
}
|
|
365
|
+
if (content === "Model usage") {
|
|
366
|
+
inUsage = true;
|
|
367
|
+
continue;
|
|
368
|
+
}
|
|
369
|
+
if (!inUsage) {
|
|
370
|
+
continue;
|
|
371
|
+
}
|
|
372
|
+
if (content.startsWith("(")) {
|
|
373
|
+
break;
|
|
374
|
+
}
|
|
375
|
+
const row = parseUsageRow(content);
|
|
376
|
+
if (row != null) {
|
|
377
|
+
usage.push(row);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
return {
|
|
381
|
+
selectedModel,
|
|
382
|
+
availableModels,
|
|
383
|
+
usage
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
function parseAvailableModel(line) {
|
|
387
|
+
const match = /^(\u25cf)?\s*(\d+)\.\s+(\S+)$/u.exec(line);
|
|
388
|
+
if (match == null) {
|
|
389
|
+
return null;
|
|
390
|
+
}
|
|
391
|
+
return {
|
|
392
|
+
selected: match[1] != null,
|
|
393
|
+
index: Number(match[2]),
|
|
394
|
+
id: match[3]
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
function parseUsageRow(line) {
|
|
398
|
+
const match = /^(.*?)\s+(\d{1,3})%\s*(?:Resets:\s*(.*?))?$/u.exec(line);
|
|
399
|
+
if (match == null) {
|
|
400
|
+
return null;
|
|
401
|
+
}
|
|
402
|
+
return {
|
|
403
|
+
name: stripUsageBar(match[1]),
|
|
404
|
+
percentUsed: Number(match[2]),
|
|
405
|
+
resetText: (match[3] ?? "").trim(),
|
|
406
|
+
raw: line
|
|
407
|
+
};
|
|
408
|
+
}
|
|
409
|
+
function stripUsageBar(value) {
|
|
410
|
+
return value.replace(/[\s#=\-_\u2500-\u257F\u2580-\u259F\u25AC]+$/giu, "").trim();
|
|
411
|
+
}
|
|
412
|
+
function resolveModelId(name, availableModels) {
|
|
413
|
+
const tierModelId = TIER_MODEL_IDS.get(name);
|
|
414
|
+
if (tierModelId != null) {
|
|
415
|
+
return tierModelId;
|
|
416
|
+
}
|
|
417
|
+
const truncatedPrefix = name.endsWith("...") ? name.slice(0, -3) : name.endsWith("…") ? name.slice(0, -1) : null;
|
|
418
|
+
if (truncatedPrefix) {
|
|
419
|
+
const match = availableModels.find((model) => model.startsWith(truncatedPrefix));
|
|
420
|
+
if (match != null) {
|
|
421
|
+
return match;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
return name;
|
|
425
|
+
}
|
|
426
|
+
function modelScope(modelId) {
|
|
427
|
+
return modelId.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
|
|
428
|
+
}
|
|
429
|
+
function stripGeminiFrame(line) {
|
|
430
|
+
return line.replace(/^\s*\u2502\s?/u, "").replace(/\s*\u2502\s*$/u, "").trim();
|
|
431
|
+
}
|
|
432
|
+
export {
|
|
433
|
+
buildGeminiApiUsageResult,
|
|
434
|
+
extractGeminiOAuthCredentials,
|
|
435
|
+
extractGeminiUsage,
|
|
436
|
+
parseGeminiModelDialog
|
|
437
|
+
};
|
package/package.json
CHANGED
package/dist/claude-Dmv_YFKX.js
DELETED
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
import { c as cleanControlOutput, a as compactLines, p as parsePercentUsed, m as makeUsageLimit } from "./cli.js";
|
|
2
|
-
import { r as runPtyScenario, e as enterKey, a as escapeKey } from "./pty-CZBSAJzE.js";
|
|
3
|
-
async function extractClaudeUsage(context) {
|
|
4
|
-
const command = context.command ?? context.launch?.command ?? "claude";
|
|
5
|
-
const model = context.launch?.cheapModel ?? "haiku";
|
|
6
|
-
validateClaudeModel(model);
|
|
7
|
-
const ptyResult = await runPtyScenario({
|
|
8
|
-
command,
|
|
9
|
-
args: context.launch?.args ?? ["--model", model],
|
|
10
|
-
cwd: context.repoRoot,
|
|
11
|
-
cols: 100,
|
|
12
|
-
rows: 40,
|
|
13
|
-
timeoutMs: context.launch?.timeoutMs ?? 6e4,
|
|
14
|
-
signal: context.signal,
|
|
15
|
-
debug: context.debug,
|
|
16
|
-
steps: [
|
|
17
|
-
{ waitFor: /Claude|>|❯/u, waitForSource: "screen", waitForTimeoutMs: 4e3 },
|
|
18
|
-
{ write: enterKey() },
|
|
19
|
-
{ waitFor: /Claude|>|❯/u, waitForSource: "screen", waitForTimeoutMs: 8e3 },
|
|
20
|
-
{ write: `/usage${enterKey()}` },
|
|
21
|
-
{
|
|
22
|
-
waitFor: hasClaudeUsageRows,
|
|
23
|
-
waitForTimeoutMs: 15e3,
|
|
24
|
-
capture: "usage",
|
|
25
|
-
captureWaitMs: 500
|
|
26
|
-
},
|
|
27
|
-
{ write: escapeKey() },
|
|
28
|
-
{ waitMs: 500 },
|
|
29
|
-
{ write: `/exit${enterKey()}` }
|
|
30
|
-
]
|
|
31
|
-
});
|
|
32
|
-
const usageSnapshot = ptyResult.snapshots.usage ?? ptyResult;
|
|
33
|
-
const cleanedOutput = cleanControlOutput(usageSnapshot.raw);
|
|
34
|
-
const parsed = parseClaudeUsage(usageSnapshot.screen, cleanedOutput);
|
|
35
|
-
const limits = buildClaudeUsageLimits(parsed, context);
|
|
36
|
-
if (limits.length === 0) {
|
|
37
|
-
throw new Error("Claude usage output did not include session or weekly usage rows.");
|
|
38
|
-
}
|
|
39
|
-
return {
|
|
40
|
-
targetId: context.targetId,
|
|
41
|
-
displayName: context.displayName,
|
|
42
|
-
command,
|
|
43
|
-
limits,
|
|
44
|
-
debug: ptyResult.debug.length > 0 ? ptyResult.debug : void 0
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
|
-
function hasClaudeUsageRows(snapshot) {
|
|
48
|
-
const parsed = parseClaudeUsage(snapshot.screen, cleanControlOutput(snapshot.raw));
|
|
49
|
-
return Boolean(parsed.currentSessionUsed || parsed.currentWeekUsed);
|
|
50
|
-
}
|
|
51
|
-
function buildClaudeUsageLimits(parsed, context) {
|
|
52
|
-
const sessionUsed = parsePercentUsed(parsed.currentSessionUsed);
|
|
53
|
-
const weekUsed = parsePercentUsed(parsed.currentWeekUsed);
|
|
54
|
-
const limits = [];
|
|
55
|
-
if (parsed.currentSessionUsed.trim()) {
|
|
56
|
-
limits.push(
|
|
57
|
-
makeUsageLimit({
|
|
58
|
-
targetId: context.targetId,
|
|
59
|
-
scope: "current_session",
|
|
60
|
-
window: "session",
|
|
61
|
-
percentUsed: sessionUsed,
|
|
62
|
-
percentRemaining: sessionUsed == null ? null : 100 - sessionUsed,
|
|
63
|
-
resetText: parsed.currentSessionResets,
|
|
64
|
-
raw: formatRaw(parsed.currentSessionUsed, parsed.currentSessionResets),
|
|
65
|
-
now: context.now
|
|
66
|
-
})
|
|
67
|
-
);
|
|
68
|
-
}
|
|
69
|
-
if (parsed.currentWeekUsed.trim()) {
|
|
70
|
-
limits.push(
|
|
71
|
-
makeUsageLimit({
|
|
72
|
-
targetId: context.targetId,
|
|
73
|
-
scope: "current_week",
|
|
74
|
-
window: "weekly",
|
|
75
|
-
percentUsed: weekUsed,
|
|
76
|
-
percentRemaining: weekUsed == null ? null : 100 - weekUsed,
|
|
77
|
-
resetText: parsed.currentWeekResets,
|
|
78
|
-
raw: formatRaw(parsed.currentWeekUsed, parsed.currentWeekResets),
|
|
79
|
-
now: context.now
|
|
80
|
-
})
|
|
81
|
-
);
|
|
82
|
-
}
|
|
83
|
-
return limits;
|
|
84
|
-
}
|
|
85
|
-
function parseClaudeUsage(screen, cleanedOutput = "") {
|
|
86
|
-
const fromScreen = parseClaudeLines(compactLines(screen));
|
|
87
|
-
if (fromScreen.currentSessionUsed || fromScreen.currentWeekUsed) {
|
|
88
|
-
return fromScreen;
|
|
89
|
-
}
|
|
90
|
-
return parseClaudeLines(compactLines(cleanedOutput));
|
|
91
|
-
}
|
|
92
|
-
function parseClaudeLines(lines) {
|
|
93
|
-
const values = {
|
|
94
|
-
currentSessionUsed: "",
|
|
95
|
-
currentSessionResets: "",
|
|
96
|
-
currentWeekUsed: "",
|
|
97
|
-
currentWeekResets: ""
|
|
98
|
-
};
|
|
99
|
-
let section = "";
|
|
100
|
-
for (const line of lines) {
|
|
101
|
-
if (line === "Current session") {
|
|
102
|
-
section = "currentSession";
|
|
103
|
-
continue;
|
|
104
|
-
}
|
|
105
|
-
if (line.startsWith("Current week")) {
|
|
106
|
-
section = "currentWeek";
|
|
107
|
-
continue;
|
|
108
|
-
}
|
|
109
|
-
if (!section) {
|
|
110
|
-
continue;
|
|
111
|
-
}
|
|
112
|
-
const usedMatch = /(\d+(?:\.\d+)?% used)/i.exec(line);
|
|
113
|
-
if (usedMatch != null) {
|
|
114
|
-
if (section === "currentSession") {
|
|
115
|
-
values.currentSessionUsed = usedMatch[1];
|
|
116
|
-
} else {
|
|
117
|
-
values.currentWeekUsed = usedMatch[1];
|
|
118
|
-
}
|
|
119
|
-
continue;
|
|
120
|
-
}
|
|
121
|
-
if (line.startsWith("Resets ")) {
|
|
122
|
-
if (section === "currentSession") {
|
|
123
|
-
values.currentSessionResets = line.slice("Resets ".length).trim();
|
|
124
|
-
} else {
|
|
125
|
-
values.currentWeekResets = line.slice("Resets ".length).trim();
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
return values;
|
|
130
|
-
}
|
|
131
|
-
function formatRaw(used, resets) {
|
|
132
|
-
if (!used) {
|
|
133
|
-
return "";
|
|
134
|
-
}
|
|
135
|
-
return resets ? `${used} (resets ${resets})` : used;
|
|
136
|
-
}
|
|
137
|
-
function validateClaudeModel(model) {
|
|
138
|
-
if (!/^[A-Za-z0-9._:-]+$/.test(model)) {
|
|
139
|
-
throw new Error(`Unsupported Claude usage model value: ${model}`);
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
export {
|
|
143
|
-
buildClaudeUsageLimits,
|
|
144
|
-
extractClaudeUsage,
|
|
145
|
-
parseClaudeUsage
|
|
146
|
-
};
|