omniagent 0.1.7 → 0.1.8
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 +22 -0
- package/dist/claude-Dmv_YFKX.js +146 -0
- package/dist/cli.js +1365 -11
- package/dist/codex-Cl1dWwMk.js +274 -0
- package/dist/gemini-CskI3Qjp.js +168 -0
- package/dist/pty-CZBSAJzE.js +362 -0
- package/package.json +3 -1
package/dist/cli.js
CHANGED
|
@@ -234,8 +234,9 @@ function buildValidationError(resolution, status, message) {
|
|
|
234
234
|
errorMessage: message
|
|
235
235
|
};
|
|
236
236
|
}
|
|
237
|
-
async function validateAgentsDir(repoRoot, agentsDir) {
|
|
237
|
+
async function validateAgentsDir(repoRoot, agentsDir, options = {}) {
|
|
238
238
|
const resolution = resolveAgentsDir(repoRoot, agentsDir);
|
|
239
|
+
const requireWrite = options.requireWrite ?? true;
|
|
239
240
|
const buildNotDirectoryError = () => buildValidationError(
|
|
240
241
|
resolution,
|
|
241
242
|
"notDirectory",
|
|
@@ -269,14 +270,16 @@ async function validateAgentsDir(repoRoot, agentsDir) {
|
|
|
269
270
|
return buildNotDirectoryError();
|
|
270
271
|
}
|
|
271
272
|
try {
|
|
272
|
-
|
|
273
|
+
const requiredAccess = constants.R_OK | constants.X_OK | (requireWrite ? constants.W_OK : 0);
|
|
274
|
+
await access(resolution.resolvedPath, requiredAccess);
|
|
273
275
|
} catch (error) {
|
|
274
276
|
const code = error.code;
|
|
275
277
|
if (code === "EACCES" || code === "EPERM") {
|
|
278
|
+
const requirement = requireWrite ? "readable, writable, or searchable" : "readable or searchable";
|
|
276
279
|
return buildValidationError(
|
|
277
280
|
resolution,
|
|
278
281
|
"permissionDenied",
|
|
279
|
-
`Agents directory is not
|
|
282
|
+
`Agents directory is not ${requirement}: ${resolution.resolvedPath}. Check permissions or choose another directory.`
|
|
280
283
|
);
|
|
281
284
|
}
|
|
282
285
|
throw error;
|
|
@@ -1308,6 +1311,20 @@ const claudeTarget = {
|
|
|
1308
1311
|
instructions: {
|
|
1309
1312
|
filename: "CLAUDE.md"
|
|
1310
1313
|
}
|
|
1314
|
+
},
|
|
1315
|
+
usage: {
|
|
1316
|
+
windows: ["hourly", "weekly"],
|
|
1317
|
+
launch: {
|
|
1318
|
+
command: "claude",
|
|
1319
|
+
// Haiku keeps the interactive usage probe on Claude's lowest-cost model family.
|
|
1320
|
+
args: ["--model", "haiku"],
|
|
1321
|
+
cheapModel: "haiku",
|
|
1322
|
+
timeoutMs: 6e4
|
|
1323
|
+
},
|
|
1324
|
+
extract: async (context) => {
|
|
1325
|
+
const { extractClaudeUsage } = await import("./claude-Dmv_YFKX.js");
|
|
1326
|
+
return extractClaudeUsage(context);
|
|
1327
|
+
}
|
|
1311
1328
|
}
|
|
1312
1329
|
};
|
|
1313
1330
|
const codexTarget = {
|
|
@@ -1367,6 +1384,27 @@ const codexTarget = {
|
|
|
1367
1384
|
filename: "AGENTS.md",
|
|
1368
1385
|
group: "agents"
|
|
1369
1386
|
}
|
|
1387
|
+
},
|
|
1388
|
+
usage: {
|
|
1389
|
+
windows: ["hourly", "weekly"],
|
|
1390
|
+
launch: {
|
|
1391
|
+
command: "codex",
|
|
1392
|
+
// Usage probing does not need plugins/apps; disabling them avoids MCP startup work.
|
|
1393
|
+
args: [
|
|
1394
|
+
"--no-alt-screen",
|
|
1395
|
+
"--disable",
|
|
1396
|
+
"apps",
|
|
1397
|
+
"--disable",
|
|
1398
|
+
"computer_use",
|
|
1399
|
+
"--disable",
|
|
1400
|
+
"plugins"
|
|
1401
|
+
],
|
|
1402
|
+
timeoutMs: 6e4
|
|
1403
|
+
},
|
|
1404
|
+
extract: async (context) => {
|
|
1405
|
+
const { extractCodexUsage } = await import("./codex-Cl1dWwMk.js");
|
|
1406
|
+
return extractCodexUsage(context);
|
|
1407
|
+
}
|
|
1370
1408
|
}
|
|
1371
1409
|
};
|
|
1372
1410
|
const FRONTMATTER_MARKER$1 = "---";
|
|
@@ -1558,6 +1596,19 @@ const geminiTarget = {
|
|
|
1558
1596
|
instructions: {
|
|
1559
1597
|
filename: "GEMINI.md"
|
|
1560
1598
|
}
|
|
1599
|
+
},
|
|
1600
|
+
usage: {
|
|
1601
|
+
windows: ["model"],
|
|
1602
|
+
launch: {
|
|
1603
|
+
command: "gemini",
|
|
1604
|
+
// /model inspection does not need a model override; Gemini requires session-scoped trust.
|
|
1605
|
+
args: ["--skip-trust"],
|
|
1606
|
+
timeoutMs: 7e4
|
|
1607
|
+
},
|
|
1608
|
+
extract: async (context) => {
|
|
1609
|
+
const { extractGeminiUsage } = await import("./gemini-CskI3Qjp.js");
|
|
1610
|
+
return extractGeminiUsage(context);
|
|
1611
|
+
}
|
|
1561
1612
|
}
|
|
1562
1613
|
};
|
|
1563
1614
|
const BUILTIN_TARGETS = [
|
|
@@ -2513,6 +2564,39 @@ function validateCliDefinition(cli, label, errors) {
|
|
|
2513
2564
|
errors.push(`${label}.translate must be a function when provided.`);
|
|
2514
2565
|
}
|
|
2515
2566
|
}
|
|
2567
|
+
function validateUsageDefinition(usage, label, errors) {
|
|
2568
|
+
if (usage === void 0) {
|
|
2569
|
+
return;
|
|
2570
|
+
}
|
|
2571
|
+
if (!isPlainObject(usage)) {
|
|
2572
|
+
errors.push(`${label} must be an object.`);
|
|
2573
|
+
return;
|
|
2574
|
+
}
|
|
2575
|
+
validateStringArray(usage.windows, `${label}.windows`, errors);
|
|
2576
|
+
if (usage.launch !== void 0) {
|
|
2577
|
+
if (!isPlainObject(usage.launch)) {
|
|
2578
|
+
errors.push(`${label}.launch must be an object.`);
|
|
2579
|
+
} else {
|
|
2580
|
+
if (usage.launch.command !== void 0 && normalizeString(usage.launch.command) === null) {
|
|
2581
|
+
errors.push(`${label}.launch.command must be a non-empty string when provided.`);
|
|
2582
|
+
}
|
|
2583
|
+
if (usage.launch.args !== void 0) {
|
|
2584
|
+
validateStringArray(usage.launch.args, `${label}.launch.args`, errors, {
|
|
2585
|
+
allowEmpty: true
|
|
2586
|
+
});
|
|
2587
|
+
}
|
|
2588
|
+
if (usage.launch.timeoutMs !== void 0 && (typeof usage.launch.timeoutMs !== "number" || !Number.isFinite(usage.launch.timeoutMs) || usage.launch.timeoutMs <= 0)) {
|
|
2589
|
+
errors.push(`${label}.launch.timeoutMs must be a positive number when provided.`);
|
|
2590
|
+
}
|
|
2591
|
+
if (usage.launch.cheapModel !== void 0 && normalizeString(usage.launch.cheapModel) === null) {
|
|
2592
|
+
errors.push(`${label}.launch.cheapModel must be a non-empty string when provided.`);
|
|
2593
|
+
}
|
|
2594
|
+
}
|
|
2595
|
+
}
|
|
2596
|
+
if (typeof usage.extract !== "function") {
|
|
2597
|
+
errors.push(`${label}.extract must be a function.`);
|
|
2598
|
+
}
|
|
2599
|
+
}
|
|
2516
2600
|
function validateTemplate(value, label, allowed, errors) {
|
|
2517
2601
|
if (typeof value === "function") {
|
|
2518
2602
|
return;
|
|
@@ -2745,6 +2829,7 @@ function validateTargetConfig(options) {
|
|
|
2745
2829
|
}
|
|
2746
2830
|
validateOutputs(entry2.outputs, `${label}.outputs`, errors);
|
|
2747
2831
|
validateCliDefinition(entry2.cli, `${label}.cli`, errors);
|
|
2832
|
+
validateUsageDefinition(entry2.usage, `${label}.usage`, errors);
|
|
2748
2833
|
}
|
|
2749
2834
|
}
|
|
2750
2835
|
}
|
|
@@ -2771,6 +2856,19 @@ function cloneOutputs(outputs) {
|
|
|
2771
2856
|
function cloneCli(cli) {
|
|
2772
2857
|
return cli ? { ...cli } : void 0;
|
|
2773
2858
|
}
|
|
2859
|
+
function cloneUsage(usage) {
|
|
2860
|
+
if (!usage) {
|
|
2861
|
+
return void 0;
|
|
2862
|
+
}
|
|
2863
|
+
return {
|
|
2864
|
+
...usage,
|
|
2865
|
+
windows: [...usage.windows],
|
|
2866
|
+
launch: usage.launch ? {
|
|
2867
|
+
...usage.launch,
|
|
2868
|
+
args: usage.launch.args ? [...usage.launch.args] : void 0
|
|
2869
|
+
} : void 0
|
|
2870
|
+
};
|
|
2871
|
+
}
|
|
2774
2872
|
function mergeOutputs(base, override) {
|
|
2775
2873
|
const merged = { ...cloneOutputs(base) };
|
|
2776
2874
|
if (!override) {
|
|
@@ -2826,6 +2924,7 @@ function resolveTargets(options) {
|
|
|
2826
2924
|
aliases: builtIn.aliases ?? [],
|
|
2827
2925
|
outputs: cloneOutputs(builtIn.outputs),
|
|
2828
2926
|
cli: cloneCli(builtIn.cli),
|
|
2927
|
+
usage: cloneUsage(builtIn.usage),
|
|
2829
2928
|
hooks: builtIn.hooks,
|
|
2830
2929
|
isBuiltIn: true,
|
|
2831
2930
|
isCustomized: false
|
|
@@ -2841,7 +2940,8 @@ function resolveTargets(options) {
|
|
|
2841
2940
|
displayName: customTarget.displayName ?? inherited?.displayName ?? customTarget.id,
|
|
2842
2941
|
aliases: customTarget.aliases ?? inherited?.aliases ?? [],
|
|
2843
2942
|
outputs: mergedOutputs,
|
|
2844
|
-
cli: customTarget.cli ?? inherited?.cli,
|
|
2943
|
+
cli: cloneCli(customTarget.cli ?? inherited?.cli),
|
|
2944
|
+
usage: cloneUsage(customTarget.usage ?? inherited?.usage),
|
|
2845
2945
|
hooks: customTarget.hooks ?? inherited?.hooks,
|
|
2846
2946
|
isBuiltIn: true,
|
|
2847
2947
|
isCustomized: true
|
|
@@ -2854,6 +2954,7 @@ function resolveTargets(options) {
|
|
|
2854
2954
|
aliases: customTarget.aliases ?? [],
|
|
2855
2955
|
outputs: cloneOutputs(customTarget.outputs),
|
|
2856
2956
|
cli: cloneCli(customTarget.cli),
|
|
2957
|
+
usage: cloneUsage(customTarget.usage),
|
|
2857
2958
|
hooks: customTarget.hooks,
|
|
2858
2959
|
isBuiltIn: true,
|
|
2859
2960
|
isCustomized: true
|
|
@@ -2873,7 +2974,8 @@ function resolveTargets(options) {
|
|
|
2873
2974
|
displayName: target.displayName ?? inherited?.displayName ?? target.id,
|
|
2874
2975
|
aliases: target.aliases ?? inherited?.aliases ?? [],
|
|
2875
2976
|
outputs: mergedOutputs,
|
|
2876
|
-
cli: target.cli ?? inherited?.cli,
|
|
2977
|
+
cli: cloneCli(target.cli ?? inherited?.cli),
|
|
2978
|
+
usage: cloneUsage(target.usage ?? inherited?.usage),
|
|
2877
2979
|
hooks: target.hooks ?? inherited?.hooks,
|
|
2878
2980
|
isBuiltIn: false,
|
|
2879
2981
|
isCustomized: true
|
|
@@ -8366,7 +8468,7 @@ const DEFAULT_SUPPORTED_TARGETS = BUILTIN_TARGETS.map((target) => target.id).joi
|
|
|
8366
8468
|
const LOCAL_CATEGORIES = ["skills", "commands", "agents", "instructions"];
|
|
8367
8469
|
const LOCAL_CATEGORY_SET = new Set(LOCAL_CATEGORIES);
|
|
8368
8470
|
const utf8Decoder = new TextDecoder("utf-8", { fatal: true });
|
|
8369
|
-
function parseList(value) {
|
|
8471
|
+
function parseList$1(value) {
|
|
8370
8472
|
if (!value) {
|
|
8371
8473
|
return [];
|
|
8372
8474
|
}
|
|
@@ -8435,7 +8537,7 @@ function parseExcludeLocal(value) {
|
|
|
8435
8537
|
if (value === true) {
|
|
8436
8538
|
return { excludeAll: true, categories: new Set(LOCAL_CATEGORIES), invalid: [] };
|
|
8437
8539
|
}
|
|
8438
|
-
const list = parseList(value);
|
|
8540
|
+
const list = parseList$1(value);
|
|
8439
8541
|
if (list.length === 0) {
|
|
8440
8542
|
return { excludeAll: true, categories: new Set(LOCAL_CATEGORIES), invalid: [] };
|
|
8441
8543
|
}
|
|
@@ -9453,8 +9555,8 @@ Config: auto-discovered as omniagent.config.(ts|mts|cts|js|mjs|cjs) in the agent
|
|
|
9453
9555
|
),
|
|
9454
9556
|
handler: async (argv) => {
|
|
9455
9557
|
try {
|
|
9456
|
-
const skipList = parseList(argv.skip);
|
|
9457
|
-
const onlyList = parseList(argv.only);
|
|
9558
|
+
const skipList = parseList$1(argv.skip);
|
|
9559
|
+
const onlyList = parseList$1(argv.only);
|
|
9458
9560
|
const excludeLocalSelection = parseExcludeLocal(argv.excludeLocal);
|
|
9459
9561
|
if (excludeLocalSelection.invalid.length > 0) {
|
|
9460
9562
|
const invalidList = excludeLocalSelection.invalid.join(", ");
|
|
@@ -10209,6 +10311,1252 @@ Config: auto-discovered as omniagent.config.(ts|mts|cts|js|mjs|cjs) in the agent
|
|
|
10209
10311
|
}
|
|
10210
10312
|
}
|
|
10211
10313
|
};
|
|
10314
|
+
const MONTHS = /* @__PURE__ */ new Map([
|
|
10315
|
+
["jan", 0],
|
|
10316
|
+
["january", 0],
|
|
10317
|
+
["feb", 1],
|
|
10318
|
+
["february", 1],
|
|
10319
|
+
["mar", 2],
|
|
10320
|
+
["march", 2],
|
|
10321
|
+
["apr", 3],
|
|
10322
|
+
["april", 3],
|
|
10323
|
+
["may", 4],
|
|
10324
|
+
["jun", 5],
|
|
10325
|
+
["june", 5],
|
|
10326
|
+
["jul", 6],
|
|
10327
|
+
["july", 6],
|
|
10328
|
+
["aug", 7],
|
|
10329
|
+
["august", 7],
|
|
10330
|
+
["sep", 8],
|
|
10331
|
+
["sept", 8],
|
|
10332
|
+
["september", 8],
|
|
10333
|
+
["oct", 9],
|
|
10334
|
+
["october", 9],
|
|
10335
|
+
["nov", 10],
|
|
10336
|
+
["november", 10],
|
|
10337
|
+
["dec", 11],
|
|
10338
|
+
["december", 11]
|
|
10339
|
+
]);
|
|
10340
|
+
const ESCAPE_CHARACTER = String.fromCharCode(27);
|
|
10341
|
+
const BELL_CHARACTER = String.fromCharCode(7);
|
|
10342
|
+
const OSC_SEQUENCE_PATTERN = new RegExp(
|
|
10343
|
+
`${escapeRegExp(ESCAPE_CHARACTER)}\\][\\s\\S]*?(?:${escapeRegExp(BELL_CHARACTER)}|${escapeRegExp(ESCAPE_CHARACTER)}\\\\)`,
|
|
10344
|
+
"g"
|
|
10345
|
+
);
|
|
10346
|
+
const CSI_SEQUENCE_PATTERN = new RegExp(
|
|
10347
|
+
`${escapeRegExp(ESCAPE_CHARACTER)}\\[[0-?]*[ -/]*[@-~]`,
|
|
10348
|
+
"g"
|
|
10349
|
+
);
|
|
10350
|
+
const CHARSET_SEQUENCE_PATTERN = new RegExp(
|
|
10351
|
+
`${escapeRegExp(ESCAPE_CHARACTER)}[()][A-Za-z0-9]`,
|
|
10352
|
+
"g"
|
|
10353
|
+
);
|
|
10354
|
+
const MODE_SEQUENCE_PATTERN = new RegExp(`${escapeRegExp(ESCAPE_CHARACTER)}[=>]`, "g");
|
|
10355
|
+
function makeUsageLimit(options) {
|
|
10356
|
+
const agent = options.agent ?? options.targetId;
|
|
10357
|
+
const resetText = emptyToNull(options.resetText);
|
|
10358
|
+
const window = normalizeUsageWindow(options.window);
|
|
10359
|
+
return {
|
|
10360
|
+
id: [options.targetId, options.scope, window, options.modelId].filter(Boolean).join("."),
|
|
10361
|
+
targetId: options.targetId,
|
|
10362
|
+
agent,
|
|
10363
|
+
scope: options.scope,
|
|
10364
|
+
window,
|
|
10365
|
+
label: options.label,
|
|
10366
|
+
modelId: options.modelId,
|
|
10367
|
+
modelLabel: options.modelLabel,
|
|
10368
|
+
percentUsed: options.percentUsed,
|
|
10369
|
+
percentRemaining: options.percentRemaining,
|
|
10370
|
+
resetAt: parseResetAt(resetText, {
|
|
10371
|
+
now: options.now,
|
|
10372
|
+
sourceTimeZone: options.resetSourceTimeZone ?? "local"
|
|
10373
|
+
}),
|
|
10374
|
+
resetText,
|
|
10375
|
+
raw: options.raw ?? ""
|
|
10376
|
+
};
|
|
10377
|
+
}
|
|
10378
|
+
function parsePercentUsed(value) {
|
|
10379
|
+
return parsePercent(value, /(\d+(?:\.\d+)?)\s*%\s*used/i);
|
|
10380
|
+
}
|
|
10381
|
+
function parsePercentRemaining(value) {
|
|
10382
|
+
return parsePercent(value, /(\d+(?:\.\d+)?)\s*%\s*(?:left|remaining)/i);
|
|
10383
|
+
}
|
|
10384
|
+
function parsePercent(value, pattern = /(\d+(?:\.\d+)?)\s*%/) {
|
|
10385
|
+
const match = pattern.exec(value ?? "");
|
|
10386
|
+
if (match == null) {
|
|
10387
|
+
return null;
|
|
10388
|
+
}
|
|
10389
|
+
return Number(match[1]);
|
|
10390
|
+
}
|
|
10391
|
+
function parseResetText(value) {
|
|
10392
|
+
const match = /\((resets[^)]*)\)/i.exec(value ?? "");
|
|
10393
|
+
return match == null ? null : match[1].trim();
|
|
10394
|
+
}
|
|
10395
|
+
function normalizeUsageWindow(window) {
|
|
10396
|
+
const normalized = window.trim().toLowerCase();
|
|
10397
|
+
if (normalized === "5h" || normalized === "five_hour" || normalized === "five-hour" || normalized === "session" || normalized === "hourly") {
|
|
10398
|
+
return "hourly";
|
|
10399
|
+
}
|
|
10400
|
+
if (normalized === "week" || normalized === "current_week" || normalized === "weekly") {
|
|
10401
|
+
return "weekly";
|
|
10402
|
+
}
|
|
10403
|
+
if (normalized === "model") {
|
|
10404
|
+
return "model";
|
|
10405
|
+
}
|
|
10406
|
+
return normalized;
|
|
10407
|
+
}
|
|
10408
|
+
function cleanControlOutput(raw) {
|
|
10409
|
+
return raw.replace(OSC_SEQUENCE_PATTERN, "").replace(CSI_SEQUENCE_PATTERN, "").replace(CHARSET_SEQUENCE_PATTERN, "").replace(MODE_SEQUENCE_PATTERN, "").replace(/\r/g, "\n").split("\n").map((line) => line.replace(/[ \t]+$/g, "")).join("\n");
|
|
10410
|
+
}
|
|
10411
|
+
function compactLines(text) {
|
|
10412
|
+
return text.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
|
|
10413
|
+
}
|
|
10414
|
+
function parseResetAt(resetText, options = {}) {
|
|
10415
|
+
const now = options.now ?? /* @__PURE__ */ new Date();
|
|
10416
|
+
const sourceTimeZone = options.sourceTimeZone ?? "local";
|
|
10417
|
+
const text = normalizeResetText(resetText);
|
|
10418
|
+
if (!text) {
|
|
10419
|
+
return null;
|
|
10420
|
+
}
|
|
10421
|
+
return parseHourMinuteOnDayMonth(text, now, sourceTimeZone) ?? parseMonthDayAtTime(text, now, sourceTimeZone) ?? parseDayMonthAtTime(text, now, sourceTimeZone) ?? parseTimeOnly(text, now, sourceTimeZone);
|
|
10422
|
+
}
|
|
10423
|
+
function parseHourMinuteOnDayMonth(text, now, sourceTimeZone) {
|
|
10424
|
+
const match = /^(\d{1,2}):(\d{2})\s+on\s+(\d{1,2})\s+([A-Za-z]+)$/i.exec(text);
|
|
10425
|
+
if (match == null) {
|
|
10426
|
+
return null;
|
|
10427
|
+
}
|
|
10428
|
+
const [, hour, minute, day, monthName] = match;
|
|
10429
|
+
const month = parseMonth(monthName);
|
|
10430
|
+
if (month == null) {
|
|
10431
|
+
return null;
|
|
10432
|
+
}
|
|
10433
|
+
return buildFutureDate(now, sourceTimeZone, {
|
|
10434
|
+
month,
|
|
10435
|
+
day: Number(day),
|
|
10436
|
+
hour: Number(hour),
|
|
10437
|
+
minute: Number(minute)
|
|
10438
|
+
});
|
|
10439
|
+
}
|
|
10440
|
+
function parseMonthDayAtTime(text, now, sourceTimeZone) {
|
|
10441
|
+
const match = /^([A-Za-z]+)\s+(\d{1,2})(?:\s+at\s+(\d{1,2})(?::(\d{2}))?\s*(am|pm)?)?$/i.exec(
|
|
10442
|
+
text
|
|
10443
|
+
);
|
|
10444
|
+
if (match == null) {
|
|
10445
|
+
return null;
|
|
10446
|
+
}
|
|
10447
|
+
const [, monthName, day, hour = "0", minute = "0", meridiem] = match;
|
|
10448
|
+
const month = parseMonth(monthName);
|
|
10449
|
+
if (month == null) {
|
|
10450
|
+
return null;
|
|
10451
|
+
}
|
|
10452
|
+
return buildFutureDate(now, sourceTimeZone, {
|
|
10453
|
+
month,
|
|
10454
|
+
day: Number(day),
|
|
10455
|
+
hour: parseHour(hour, meridiem),
|
|
10456
|
+
minute: Number(minute)
|
|
10457
|
+
});
|
|
10458
|
+
}
|
|
10459
|
+
function parseDayMonthAtTime(text, now, sourceTimeZone) {
|
|
10460
|
+
const match = /^(\d{1,2})\s+([A-Za-z]+)(?:\s+at\s+(\d{1,2})(?::(\d{2}))?\s*(am|pm)?)?$/i.exec(
|
|
10461
|
+
text
|
|
10462
|
+
);
|
|
10463
|
+
if (match == null) {
|
|
10464
|
+
return null;
|
|
10465
|
+
}
|
|
10466
|
+
const [, day, monthName, hour = "0", minute = "0", meridiem] = match;
|
|
10467
|
+
const month = parseMonth(monthName);
|
|
10468
|
+
if (month == null) {
|
|
10469
|
+
return null;
|
|
10470
|
+
}
|
|
10471
|
+
return buildFutureDate(now, sourceTimeZone, {
|
|
10472
|
+
month,
|
|
10473
|
+
day: Number(day),
|
|
10474
|
+
hour: parseHour(hour, meridiem),
|
|
10475
|
+
minute: Number(minute)
|
|
10476
|
+
});
|
|
10477
|
+
}
|
|
10478
|
+
function parseTimeOnly(text, now, sourceTimeZone) {
|
|
10479
|
+
const match = /^(\d{1,2})(?::(\d{2}))?\s*(am|pm)?$/i.exec(text);
|
|
10480
|
+
if (match == null) {
|
|
10481
|
+
return null;
|
|
10482
|
+
}
|
|
10483
|
+
const [, hour, minute = "0", meridiem] = match;
|
|
10484
|
+
const date = sourceTimeZone === "utc" ? new Date(
|
|
10485
|
+
Date.UTC(
|
|
10486
|
+
now.getUTCFullYear(),
|
|
10487
|
+
now.getUTCMonth(),
|
|
10488
|
+
now.getUTCDate(),
|
|
10489
|
+
parseHour(hour, meridiem),
|
|
10490
|
+
Number(minute)
|
|
10491
|
+
)
|
|
10492
|
+
) : new Date(
|
|
10493
|
+
now.getFullYear(),
|
|
10494
|
+
now.getMonth(),
|
|
10495
|
+
now.getDate(),
|
|
10496
|
+
parseHour(hour, meridiem),
|
|
10497
|
+
Number(minute)
|
|
10498
|
+
);
|
|
10499
|
+
if (date <= now) {
|
|
10500
|
+
if (sourceTimeZone === "utc") {
|
|
10501
|
+
date.setUTCDate(date.getUTCDate() + 1);
|
|
10502
|
+
} else {
|
|
10503
|
+
date.setDate(date.getDate() + 1);
|
|
10504
|
+
}
|
|
10505
|
+
}
|
|
10506
|
+
return date.toISOString();
|
|
10507
|
+
}
|
|
10508
|
+
function buildFutureDate(now, sourceTimeZone, values) {
|
|
10509
|
+
const date = sourceTimeZone === "utc" ? new Date(
|
|
10510
|
+
Date.UTC(now.getUTCFullYear(), values.month, values.day, values.hour, values.minute)
|
|
10511
|
+
) : new Date(now.getFullYear(), values.month, values.day, values.hour, values.minute);
|
|
10512
|
+
if (date <= now) {
|
|
10513
|
+
if (sourceTimeZone === "utc") {
|
|
10514
|
+
date.setUTCFullYear(date.getUTCFullYear() + 1);
|
|
10515
|
+
} else {
|
|
10516
|
+
date.setFullYear(date.getFullYear() + 1);
|
|
10517
|
+
}
|
|
10518
|
+
}
|
|
10519
|
+
return date.toISOString();
|
|
10520
|
+
}
|
|
10521
|
+
function normalizeResetText(resetText) {
|
|
10522
|
+
return (resetText ?? "").replace(/^resets\s+/i, "").replace(/\([^)]*\)/g, "").replace(/\s+/g, " ").trim();
|
|
10523
|
+
}
|
|
10524
|
+
function parseMonth(monthName) {
|
|
10525
|
+
return MONTHS.get(monthName.toLowerCase()) ?? null;
|
|
10526
|
+
}
|
|
10527
|
+
function parseHour(hourValue, meridiem) {
|
|
10528
|
+
const hour = Number(hourValue);
|
|
10529
|
+
if (meridiem == null) {
|
|
10530
|
+
return hour;
|
|
10531
|
+
}
|
|
10532
|
+
const lower = meridiem.toLowerCase();
|
|
10533
|
+
if (lower === "am") {
|
|
10534
|
+
return hour === 12 ? 0 : hour;
|
|
10535
|
+
}
|
|
10536
|
+
if (lower === "pm") {
|
|
10537
|
+
return hour === 12 ? 12 : hour + 12;
|
|
10538
|
+
}
|
|
10539
|
+
return hour;
|
|
10540
|
+
}
|
|
10541
|
+
function emptyToNull(value) {
|
|
10542
|
+
return value == null || value === "" ? null : value;
|
|
10543
|
+
}
|
|
10544
|
+
function escapeRegExp(value) {
|
|
10545
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
10546
|
+
}
|
|
10547
|
+
const USAGE_BAR_WIDTH = 12;
|
|
10548
|
+
const ANSI = {
|
|
10549
|
+
reset: "\x1B[0m",
|
|
10550
|
+
bold: "\x1B[1m",
|
|
10551
|
+
dim: "\x1B[2m",
|
|
10552
|
+
green: "\x1B[32m",
|
|
10553
|
+
yellow: "\x1B[33m",
|
|
10554
|
+
orange: "\x1B[38;5;208m",
|
|
10555
|
+
red: "\x1B[31m",
|
|
10556
|
+
gray: "\x1B[90m"
|
|
10557
|
+
};
|
|
10558
|
+
const DEFAULT_USAGE_TIMEOUT_MS = 3e4;
|
|
10559
|
+
const MS_PER_MINUTE = 6e4;
|
|
10560
|
+
const MS_PER_HOUR = 36e5;
|
|
10561
|
+
const MS_PER_DAY = 864e5;
|
|
10562
|
+
const DETAILED_RESET_THRESHOLD_MINUTES = 180;
|
|
10563
|
+
const RELATIVE_RESET_WIDTH = 6;
|
|
10564
|
+
const RESET_WEEKDAY_TIME_FORMATTER = new Intl.DateTimeFormat("en-US", {
|
|
10565
|
+
weekday: "short",
|
|
10566
|
+
hour: "numeric",
|
|
10567
|
+
minute: "2-digit"
|
|
10568
|
+
});
|
|
10569
|
+
const RESET_DATE_TIME_FORMATTER = new Intl.DateTimeFormat("en-US", {
|
|
10570
|
+
month: "short",
|
|
10571
|
+
day: "numeric",
|
|
10572
|
+
hour: "numeric",
|
|
10573
|
+
minute: "2-digit"
|
|
10574
|
+
});
|
|
10575
|
+
class UsageExtractionTimeoutError extends Error {
|
|
10576
|
+
constructor(timeoutMs) {
|
|
10577
|
+
super(`Usage extraction timed out after ${formatDuration(timeoutMs)}.`);
|
|
10578
|
+
this.timeoutMs = timeoutMs;
|
|
10579
|
+
}
|
|
10580
|
+
}
|
|
10581
|
+
function normalizeOptionalWindow(value) {
|
|
10582
|
+
if (value == null) {
|
|
10583
|
+
return null;
|
|
10584
|
+
}
|
|
10585
|
+
const trimmed = value.trim();
|
|
10586
|
+
return trimmed.length > 0 ? normalizeUsageWindow(trimmed) : "";
|
|
10587
|
+
}
|
|
10588
|
+
function normalizeOptionalSort(value) {
|
|
10589
|
+
if (value == null) {
|
|
10590
|
+
return null;
|
|
10591
|
+
}
|
|
10592
|
+
const normalized = value.trim().toLowerCase();
|
|
10593
|
+
if (!normalized) {
|
|
10594
|
+
return "";
|
|
10595
|
+
}
|
|
10596
|
+
return normalized === "reset" || normalized === "left" ? normalized : "";
|
|
10597
|
+
}
|
|
10598
|
+
function parseTimeoutMs(value) {
|
|
10599
|
+
if (value == null) {
|
|
10600
|
+
return void 0;
|
|
10601
|
+
}
|
|
10602
|
+
const trimmed = value.trim();
|
|
10603
|
+
if (!trimmed) {
|
|
10604
|
+
return null;
|
|
10605
|
+
}
|
|
10606
|
+
const match = /^(\d+(?:\.\d+)?)(ms|s|m)?$/i.exec(trimmed);
|
|
10607
|
+
if (!match) {
|
|
10608
|
+
return null;
|
|
10609
|
+
}
|
|
10610
|
+
const amount = Number(match[1]);
|
|
10611
|
+
const unit = match[2]?.toLowerCase() ?? "s";
|
|
10612
|
+
const multiplier = unit === "ms" ? 1 : unit === "m" ? 6e4 : 1e3;
|
|
10613
|
+
const timeoutMs = amount * multiplier;
|
|
10614
|
+
if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
|
|
10615
|
+
return null;
|
|
10616
|
+
}
|
|
10617
|
+
return Math.ceil(timeoutMs);
|
|
10618
|
+
}
|
|
10619
|
+
function resolveTargetTimeoutMs(target, cliTimeoutMs) {
|
|
10620
|
+
return cliTimeoutMs ?? target.usage?.launch?.timeoutMs ?? DEFAULT_USAGE_TIMEOUT_MS;
|
|
10621
|
+
}
|
|
10622
|
+
function parseList(value) {
|
|
10623
|
+
if (!value) {
|
|
10624
|
+
return [];
|
|
10625
|
+
}
|
|
10626
|
+
const rawValues = Array.isArray(value) ? value : [value];
|
|
10627
|
+
return rawValues.flatMap((entry2) => entry2.split(",")).map((entry2) => entry2.trim().toLowerCase()).filter(Boolean);
|
|
10628
|
+
}
|
|
10629
|
+
function formatDuration(timeoutMs) {
|
|
10630
|
+
if (timeoutMs % 6e4 === 0) {
|
|
10631
|
+
return `${timeoutMs / 6e4}m`;
|
|
10632
|
+
}
|
|
10633
|
+
if (timeoutMs % 1e3 === 0) {
|
|
10634
|
+
return `${timeoutMs / 1e3}s`;
|
|
10635
|
+
}
|
|
10636
|
+
return `${timeoutMs}ms`;
|
|
10637
|
+
}
|
|
10638
|
+
function uniqueNormalizedWindows(values) {
|
|
10639
|
+
const seen = /* @__PURE__ */ new Set();
|
|
10640
|
+
const result = [];
|
|
10641
|
+
for (const value of values) {
|
|
10642
|
+
const normalized = normalizeUsageWindow(value);
|
|
10643
|
+
if (!normalized || seen.has(normalized)) {
|
|
10644
|
+
continue;
|
|
10645
|
+
}
|
|
10646
|
+
seen.add(normalized);
|
|
10647
|
+
result.push(normalized);
|
|
10648
|
+
}
|
|
10649
|
+
return result;
|
|
10650
|
+
}
|
|
10651
|
+
function formatUsageTargetLabel(targets) {
|
|
10652
|
+
return buildSupportedTargetLabel(targets);
|
|
10653
|
+
}
|
|
10654
|
+
function formatSupportedUsageTargetsMessage(targets) {
|
|
10655
|
+
const supportedUsageTargets = formatUsageTargetLabel(targets);
|
|
10656
|
+
return supportedUsageTargets ? `Supported usage targets: ${supportedUsageTargets}.` : "No active usage-capable targets are enabled by the current target configuration.";
|
|
10657
|
+
}
|
|
10658
|
+
function getUsageCommand(target) {
|
|
10659
|
+
return target.usage?.launch?.command;
|
|
10660
|
+
}
|
|
10661
|
+
async function checkUsageCommandAvailability(target) {
|
|
10662
|
+
const command = getUsageCommand(target)?.trim();
|
|
10663
|
+
if (!command) {
|
|
10664
|
+
return {
|
|
10665
|
+
status: "available",
|
|
10666
|
+
warnings: []
|
|
10667
|
+
};
|
|
10668
|
+
}
|
|
10669
|
+
const check = await checkCliOnPath(command);
|
|
10670
|
+
if (check.result === "available") {
|
|
10671
|
+
return {
|
|
10672
|
+
status: "available",
|
|
10673
|
+
warnings: [],
|
|
10674
|
+
command,
|
|
10675
|
+
resolvedPath: check.resolvedPath ?? command
|
|
10676
|
+
};
|
|
10677
|
+
}
|
|
10678
|
+
if (check.result === "inconclusive") {
|
|
10679
|
+
return {
|
|
10680
|
+
status: "unavailable",
|
|
10681
|
+
reason: "Usage CLI availability could not be confirmed.",
|
|
10682
|
+
warnings: check.warning ? [check.warning] : []
|
|
10683
|
+
};
|
|
10684
|
+
}
|
|
10685
|
+
return {
|
|
10686
|
+
status: "unavailable",
|
|
10687
|
+
reason: `Usage CLI not found on PATH: ${command}.`,
|
|
10688
|
+
warnings: []
|
|
10689
|
+
};
|
|
10690
|
+
}
|
|
10691
|
+
function buildContext(options) {
|
|
10692
|
+
const windows = uniqueNormalizedWindows(options.target.usage?.windows ?? []);
|
|
10693
|
+
const launch = {
|
|
10694
|
+
...options.target.usage?.launch ?? {},
|
|
10695
|
+
timeoutMs: options.timeoutMs
|
|
10696
|
+
};
|
|
10697
|
+
return {
|
|
10698
|
+
targetId: options.target.id,
|
|
10699
|
+
displayName: options.target.displayName,
|
|
10700
|
+
command: options.command ?? getUsageCommand(options.target),
|
|
10701
|
+
window: options.selectedWindow ?? windows[0] ?? "",
|
|
10702
|
+
windows,
|
|
10703
|
+
now: options.now,
|
|
10704
|
+
repoRoot: options.repoRoot,
|
|
10705
|
+
agentsDir: options.agentsDir,
|
|
10706
|
+
homeDir: options.homeDir,
|
|
10707
|
+
launch,
|
|
10708
|
+
signal: options.signal,
|
|
10709
|
+
debug: {
|
|
10710
|
+
enabled: options.debug,
|
|
10711
|
+
includeRawOutput: options.debug,
|
|
10712
|
+
includeScreenSnapshots: options.debug
|
|
10713
|
+
}
|
|
10714
|
+
};
|
|
10715
|
+
}
|
|
10716
|
+
function filterTargetResult(target, result, selectedWindow) {
|
|
10717
|
+
const normalizedLimits = result.limits.map((limit) => ({
|
|
10718
|
+
...limit,
|
|
10719
|
+
window: normalizeUsageWindow(limit.window)
|
|
10720
|
+
}));
|
|
10721
|
+
const filteredLimits = selectedWindow == null ? normalizedLimits : normalizedLimits.filter((limit) => limit.window === selectedWindow);
|
|
10722
|
+
const notes = selectedWindow != null && filteredLimits.length === 0 ? [`${target.displayName} reported no usage rows for window "${selectedWindow}".`] : [];
|
|
10723
|
+
return {
|
|
10724
|
+
result: {
|
|
10725
|
+
targetId: result.targetId,
|
|
10726
|
+
displayName: result.displayName,
|
|
10727
|
+
command: result.command,
|
|
10728
|
+
limits: filteredLimits
|
|
10729
|
+
},
|
|
10730
|
+
notes,
|
|
10731
|
+
debug: result.debug ?? []
|
|
10732
|
+
};
|
|
10733
|
+
}
|
|
10734
|
+
function buildError(target, code, message) {
|
|
10735
|
+
return {
|
|
10736
|
+
targetId: target.id,
|
|
10737
|
+
displayName: target.displayName,
|
|
10738
|
+
code,
|
|
10739
|
+
message
|
|
10740
|
+
};
|
|
10741
|
+
}
|
|
10742
|
+
async function extractUsageForTarget(options) {
|
|
10743
|
+
try {
|
|
10744
|
+
const extractor = options.target.usage?.extract;
|
|
10745
|
+
if (!extractor) {
|
|
10746
|
+
return {
|
|
10747
|
+
status: "error",
|
|
10748
|
+
target: options.target,
|
|
10749
|
+
error: buildError(
|
|
10750
|
+
options.target,
|
|
10751
|
+
"usage_extractor_missing",
|
|
10752
|
+
`${options.target.displayName} does not have a usage extractor.`
|
|
10753
|
+
),
|
|
10754
|
+
debug: []
|
|
10755
|
+
};
|
|
10756
|
+
}
|
|
10757
|
+
const result = await withUsageTimeout((signal) => {
|
|
10758
|
+
const context = buildContext({ ...options, signal });
|
|
10759
|
+
return extractor(context);
|
|
10760
|
+
}, options.timeoutMs);
|
|
10761
|
+
const filtered = filterTargetResult(options.target, result, options.selectedWindow);
|
|
10762
|
+
return {
|
|
10763
|
+
status: "success",
|
|
10764
|
+
target: options.target,
|
|
10765
|
+
result: filtered.result,
|
|
10766
|
+
errors: result.errors ?? [],
|
|
10767
|
+
notes: filtered.notes,
|
|
10768
|
+
debug: filtered.debug
|
|
10769
|
+
};
|
|
10770
|
+
} catch (error) {
|
|
10771
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
10772
|
+
const code = isUsageTimeoutError(error) ? "usage_extraction_timeout" : "usage_extraction_failed";
|
|
10773
|
+
return {
|
|
10774
|
+
status: "error",
|
|
10775
|
+
target: options.target,
|
|
10776
|
+
error: buildError(options.target, code, message),
|
|
10777
|
+
debug: options.debug ? getErrorDebugArtifacts(error) : []
|
|
10778
|
+
};
|
|
10779
|
+
}
|
|
10780
|
+
}
|
|
10781
|
+
function isUsageTimeoutError(error) {
|
|
10782
|
+
if (error instanceof UsageExtractionTimeoutError) {
|
|
10783
|
+
return true;
|
|
10784
|
+
}
|
|
10785
|
+
return Boolean(error && typeof error === "object" && error.timedOut);
|
|
10786
|
+
}
|
|
10787
|
+
function getErrorDebugArtifacts(error) {
|
|
10788
|
+
if (!error || typeof error !== "object") {
|
|
10789
|
+
return [];
|
|
10790
|
+
}
|
|
10791
|
+
const debug = error.debug;
|
|
10792
|
+
if (!Array.isArray(debug)) {
|
|
10793
|
+
return [];
|
|
10794
|
+
}
|
|
10795
|
+
return debug.filter(isDebugArtifact);
|
|
10796
|
+
}
|
|
10797
|
+
function isDebugArtifact(value) {
|
|
10798
|
+
if (!value || typeof value !== "object") {
|
|
10799
|
+
return false;
|
|
10800
|
+
}
|
|
10801
|
+
const artifact = value;
|
|
10802
|
+
return (artifact.type === "raw-output" || artifact.type === "screen-snapshot") && typeof artifact.label === "string";
|
|
10803
|
+
}
|
|
10804
|
+
function withUsageTimeout(run, timeoutMs) {
|
|
10805
|
+
return new Promise((resolve, reject) => {
|
|
10806
|
+
const controller = new AbortController();
|
|
10807
|
+
let settled = false;
|
|
10808
|
+
let timeoutFired = false;
|
|
10809
|
+
let timeout;
|
|
10810
|
+
const settleResolve = (value) => {
|
|
10811
|
+
if (settled) {
|
|
10812
|
+
return;
|
|
10813
|
+
}
|
|
10814
|
+
settled = true;
|
|
10815
|
+
clearTimeout(timeout);
|
|
10816
|
+
resolve(value);
|
|
10817
|
+
};
|
|
10818
|
+
const settleReject = (error) => {
|
|
10819
|
+
if (settled) {
|
|
10820
|
+
return;
|
|
10821
|
+
}
|
|
10822
|
+
settled = true;
|
|
10823
|
+
clearTimeout(timeout);
|
|
10824
|
+
reject(error);
|
|
10825
|
+
};
|
|
10826
|
+
timeout = setTimeout(() => {
|
|
10827
|
+
timeoutFired = true;
|
|
10828
|
+
const error = new UsageExtractionTimeoutError(timeoutMs);
|
|
10829
|
+
controller.abort(error);
|
|
10830
|
+
setImmediate(() => {
|
|
10831
|
+
settleReject(error);
|
|
10832
|
+
});
|
|
10833
|
+
}, timeoutMs);
|
|
10834
|
+
let promise;
|
|
10835
|
+
try {
|
|
10836
|
+
promise = run(controller.signal);
|
|
10837
|
+
} catch (error) {
|
|
10838
|
+
settleReject(error);
|
|
10839
|
+
return;
|
|
10840
|
+
}
|
|
10841
|
+
promise.then(
|
|
10842
|
+
(value) => {
|
|
10843
|
+
if (timeoutFired) {
|
|
10844
|
+
return;
|
|
10845
|
+
}
|
|
10846
|
+
settleResolve(value);
|
|
10847
|
+
},
|
|
10848
|
+
(error) => {
|
|
10849
|
+
if (timeoutFired && !isUsageTimeoutError(error)) {
|
|
10850
|
+
return;
|
|
10851
|
+
}
|
|
10852
|
+
settleReject(error);
|
|
10853
|
+
}
|
|
10854
|
+
);
|
|
10855
|
+
});
|
|
10856
|
+
}
|
|
10857
|
+
function buildEnvelope(options) {
|
|
10858
|
+
const envelope = {
|
|
10859
|
+
schemaVersion: 1,
|
|
10860
|
+
generatedAt: options.generatedAt,
|
|
10861
|
+
targets: options.targets,
|
|
10862
|
+
errors: options.errors,
|
|
10863
|
+
notes: options.notes
|
|
10864
|
+
};
|
|
10865
|
+
if (options.debug && options.debugArtifacts.length > 0) {
|
|
10866
|
+
envelope.debug = options.debugArtifacts;
|
|
10867
|
+
}
|
|
10868
|
+
return envelope;
|
|
10869
|
+
}
|
|
10870
|
+
function buildCommandError(code, message) {
|
|
10871
|
+
return {
|
|
10872
|
+
targetId: "usage",
|
|
10873
|
+
displayName: "Usage command",
|
|
10874
|
+
code,
|
|
10875
|
+
message
|
|
10876
|
+
};
|
|
10877
|
+
}
|
|
10878
|
+
function printError(options) {
|
|
10879
|
+
if (options.json) {
|
|
10880
|
+
const error = options.target ? buildError(options.target, options.code, options.message) : buildCommandError(options.code, options.message);
|
|
10881
|
+
console.log(
|
|
10882
|
+
JSON.stringify(
|
|
10883
|
+
buildEnvelope({
|
|
10884
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
10885
|
+
targets: [],
|
|
10886
|
+
errors: [error],
|
|
10887
|
+
notes: [],
|
|
10888
|
+
debug: false,
|
|
10889
|
+
debugArtifacts: []
|
|
10890
|
+
}),
|
|
10891
|
+
null,
|
|
10892
|
+
2
|
|
10893
|
+
)
|
|
10894
|
+
);
|
|
10895
|
+
} else {
|
|
10896
|
+
console.error(`Error: ${options.message}`);
|
|
10897
|
+
}
|
|
10898
|
+
process.exit(options.exitCode);
|
|
10899
|
+
}
|
|
10900
|
+
function percentText(value) {
|
|
10901
|
+
return value == null ? "unknown" : `${Math.round(value)}%`;
|
|
10902
|
+
}
|
|
10903
|
+
function usageBar(percentUsed) {
|
|
10904
|
+
if (percentUsed == null) {
|
|
10905
|
+
return `[${"?".repeat(USAGE_BAR_WIDTH)}]`;
|
|
10906
|
+
}
|
|
10907
|
+
const clamped = Math.max(0, Math.min(100, percentUsed));
|
|
10908
|
+
const filled = clamped === 0 ? 0 : Math.max(1, Math.round(clamped / 100 * USAGE_BAR_WIDTH));
|
|
10909
|
+
return `[${"#".repeat(filled)}${"-".repeat(USAGE_BAR_WIDTH - filled)}]`;
|
|
10910
|
+
}
|
|
10911
|
+
function formatResetValue(limit, now) {
|
|
10912
|
+
const resetAt = parseDate(limit.resetAt);
|
|
10913
|
+
if (resetAt != null) {
|
|
10914
|
+
return formatLocalResetAt(resetAt, now);
|
|
10915
|
+
}
|
|
10916
|
+
const value = limit.resetText ?? "-";
|
|
10917
|
+
return value === "-" ? value : value.replace(/^resets\s+/i, "");
|
|
10918
|
+
}
|
|
10919
|
+
function parseDate(value) {
|
|
10920
|
+
if (!value) {
|
|
10921
|
+
return null;
|
|
10922
|
+
}
|
|
10923
|
+
const date = new Date(value);
|
|
10924
|
+
return Number.isNaN(date.getTime()) ? null : date;
|
|
10925
|
+
}
|
|
10926
|
+
function formatLocalResetAt(resetAt, now) {
|
|
10927
|
+
const relative = formatResetDuration(resetAt.getTime() - now.getTime()).padEnd(
|
|
10928
|
+
RELATIVE_RESET_WIDTH
|
|
10929
|
+
);
|
|
10930
|
+
return `${relative} (${formatResetExact(resetAt, now)})`;
|
|
10931
|
+
}
|
|
10932
|
+
function formatResetDuration(milliseconds) {
|
|
10933
|
+
if (milliseconds <= 0) {
|
|
10934
|
+
return "now";
|
|
10935
|
+
}
|
|
10936
|
+
const totalMinutes = Math.max(1, Math.ceil(milliseconds / MS_PER_MINUTE));
|
|
10937
|
+
if (totalMinutes < 60) {
|
|
10938
|
+
return `${totalMinutes}m`;
|
|
10939
|
+
}
|
|
10940
|
+
if (totalMinutes < DETAILED_RESET_THRESHOLD_MINUTES) {
|
|
10941
|
+
const hours = Math.floor(totalMinutes / 60);
|
|
10942
|
+
const minutes = totalMinutes % 60;
|
|
10943
|
+
return minutes === 0 ? `${hours}h` : `${hours}h${minutes}m`;
|
|
10944
|
+
}
|
|
10945
|
+
if (milliseconds >= MS_PER_DAY) {
|
|
10946
|
+
return `${Math.ceil(milliseconds / MS_PER_DAY)}d`;
|
|
10947
|
+
}
|
|
10948
|
+
return `${Math.round(milliseconds / MS_PER_HOUR)}h`;
|
|
10949
|
+
}
|
|
10950
|
+
function formatResetExact(resetAt, now) {
|
|
10951
|
+
const dayDifference = localDayIndex(resetAt) - localDayIndex(now);
|
|
10952
|
+
if (dayDifference >= 0 && dayDifference <= 7) {
|
|
10953
|
+
return RESET_WEEKDAY_TIME_FORMATTER.format(resetAt);
|
|
10954
|
+
}
|
|
10955
|
+
return RESET_DATE_TIME_FORMATTER.format(resetAt);
|
|
10956
|
+
}
|
|
10957
|
+
function localDayIndex(date) {
|
|
10958
|
+
return Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()) / MS_PER_DAY;
|
|
10959
|
+
}
|
|
10960
|
+
function formatLimitLabel(limit) {
|
|
10961
|
+
return limit.label ?? limit.modelLabel ?? limit.modelId ?? formatWindowLabel(limit.window);
|
|
10962
|
+
}
|
|
10963
|
+
function formatLimitLabels(limits) {
|
|
10964
|
+
const baseLabels = limits.map(formatLimitLabel);
|
|
10965
|
+
const duplicateLabels = new Set(
|
|
10966
|
+
baseLabels.filter((label, index) => baseLabels.indexOf(label) !== index)
|
|
10967
|
+
);
|
|
10968
|
+
return limits.map((limit, index) => {
|
|
10969
|
+
const baseLabel = baseLabels[index] ?? formatLimitLabel(limit);
|
|
10970
|
+
if (!duplicateLabels.has(baseLabel) || !limit.scope) {
|
|
10971
|
+
return baseLabel;
|
|
10972
|
+
}
|
|
10973
|
+
if (limit.scope === "main") {
|
|
10974
|
+
return baseLabel;
|
|
10975
|
+
}
|
|
10976
|
+
return `${formatScopeLabel(limit.scope)} ${baseLabel}`;
|
|
10977
|
+
});
|
|
10978
|
+
}
|
|
10979
|
+
function formatScopeLabel(scope) {
|
|
10980
|
+
return scope.replace(/[_-]+/g, " ").split(/\s+/).filter(Boolean).map((word) => `${word.charAt(0).toUpperCase()}${word.slice(1)}`).join(" ");
|
|
10981
|
+
}
|
|
10982
|
+
function formatWindowLabel(window) {
|
|
10983
|
+
if (window === "hourly") {
|
|
10984
|
+
return "5h";
|
|
10985
|
+
}
|
|
10986
|
+
if (window === "weekly") {
|
|
10987
|
+
return "Weekly";
|
|
10988
|
+
}
|
|
10989
|
+
return window;
|
|
10990
|
+
}
|
|
10991
|
+
function formatUsageTable(envelope, sortKey) {
|
|
10992
|
+
const useColor = shouldUseColor();
|
|
10993
|
+
const generatedAt = parseDate(envelope.generatedAt) ?? /* @__PURE__ */ new Date();
|
|
10994
|
+
const rows = [];
|
|
10995
|
+
for (const target of envelope.targets) {
|
|
10996
|
+
const limitLabels = formatLimitLabels(target.limits);
|
|
10997
|
+
target.limits.forEach((limit, index) => {
|
|
10998
|
+
rows.push({
|
|
10999
|
+
status: "ok",
|
|
11000
|
+
agent: sortKey == null && index > 0 ? "" : target.displayName,
|
|
11001
|
+
limitLabel: limitLabels[index] ?? formatLimitLabel(limit),
|
|
11002
|
+
reset: formatResetValue(limit, generatedAt),
|
|
11003
|
+
limit
|
|
11004
|
+
});
|
|
11005
|
+
});
|
|
11006
|
+
}
|
|
11007
|
+
for (const error of envelope.errors) {
|
|
11008
|
+
rows.push({
|
|
11009
|
+
status: "error",
|
|
11010
|
+
agent: error.displayName,
|
|
11011
|
+
limitLabel: "error",
|
|
11012
|
+
message: error.message
|
|
11013
|
+
});
|
|
11014
|
+
}
|
|
11015
|
+
const rendered = [renderUsageTable(sortUsageRows(rows, sortKey), useColor)];
|
|
11016
|
+
if (envelope.notes.length > 0) {
|
|
11017
|
+
rendered.push("", ...envelope.notes.map((note) => color(`Note: ${note}`, "dim", useColor)));
|
|
11018
|
+
}
|
|
11019
|
+
return rendered.join("\n");
|
|
11020
|
+
}
|
|
11021
|
+
function sortUsageRows(rows, sortKey) {
|
|
11022
|
+
if (sortKey == null) {
|
|
11023
|
+
return rows;
|
|
11024
|
+
}
|
|
11025
|
+
return rows.map((row, index) => ({ row, index })).sort((left, right) => {
|
|
11026
|
+
const leftValue = usageSortValue(left.row, sortKey);
|
|
11027
|
+
const rightValue = usageSortValue(right.row, sortKey);
|
|
11028
|
+
if (leftValue == null && rightValue == null) {
|
|
11029
|
+
return left.index - right.index;
|
|
11030
|
+
}
|
|
11031
|
+
if (leftValue == null) {
|
|
11032
|
+
return 1;
|
|
11033
|
+
}
|
|
11034
|
+
if (rightValue == null) {
|
|
11035
|
+
return -1;
|
|
11036
|
+
}
|
|
11037
|
+
if (leftValue !== rightValue) {
|
|
11038
|
+
return leftValue - rightValue;
|
|
11039
|
+
}
|
|
11040
|
+
return left.index - right.index;
|
|
11041
|
+
}).map(({ row }) => row);
|
|
11042
|
+
}
|
|
11043
|
+
function usageSortValue(row, sortKey) {
|
|
11044
|
+
if (row.status === "error") {
|
|
11045
|
+
return null;
|
|
11046
|
+
}
|
|
11047
|
+
if (sortKey === "left") {
|
|
11048
|
+
return row.limit.percentRemaining;
|
|
11049
|
+
}
|
|
11050
|
+
const resetAt = parseDate(row.limit.resetAt);
|
|
11051
|
+
return resetAt?.getTime() ?? null;
|
|
11052
|
+
}
|
|
11053
|
+
function renderUsageTable(rows, useColor) {
|
|
11054
|
+
const widths = {
|
|
11055
|
+
agent: maxWidth(
|
|
11056
|
+
"Agent",
|
|
11057
|
+
rows.map((row) => row.agent)
|
|
11058
|
+
),
|
|
11059
|
+
limit: maxWidth(
|
|
11060
|
+
"Limit",
|
|
11061
|
+
rows.map((row) => row.limitLabel)
|
|
11062
|
+
),
|
|
11063
|
+
usage: maxWidth("Usage", rows.map(usageCellText)),
|
|
11064
|
+
left: maxWidth("Left", rows.map(leftCellText)),
|
|
11065
|
+
reset: maxWidth("Reset", rows.map(resetCellText))
|
|
11066
|
+
};
|
|
11067
|
+
const headerLine = [
|
|
11068
|
+
pad("Agent", widths.agent),
|
|
11069
|
+
pad("Limit", widths.limit),
|
|
11070
|
+
pad("Left", widths.left),
|
|
11071
|
+
pad("Usage", widths.usage),
|
|
11072
|
+
pad("Reset", widths.reset)
|
|
11073
|
+
].join(" ").trimEnd();
|
|
11074
|
+
const separatorLine = [
|
|
11075
|
+
"-".repeat(widths.agent),
|
|
11076
|
+
"-".repeat(widths.limit),
|
|
11077
|
+
"-".repeat(widths.left),
|
|
11078
|
+
"-".repeat(widths.usage),
|
|
11079
|
+
"-".repeat(widths.reset)
|
|
11080
|
+
].join(" ").trimEnd();
|
|
11081
|
+
return [
|
|
11082
|
+
color(headerLine, "bold", useColor),
|
|
11083
|
+
color(separatorLine, "dim", useColor),
|
|
11084
|
+
...rows.map((row) => renderUsageRow(row, widths, useColor))
|
|
11085
|
+
].join("\n");
|
|
11086
|
+
}
|
|
11087
|
+
function renderUsageRow(row, widths, useColor) {
|
|
11088
|
+
return [
|
|
11089
|
+
pad(row.agent, widths.agent),
|
|
11090
|
+
pad(row.limitLabel, widths.limit),
|
|
11091
|
+
renderLeftCell(row, widths.left, useColor),
|
|
11092
|
+
renderUsageCell(row, widths.usage, useColor),
|
|
11093
|
+
renderResetCell(row, widths.reset, useColor)
|
|
11094
|
+
].join(" ").trimEnd();
|
|
11095
|
+
}
|
|
11096
|
+
function usageCellText(row) {
|
|
11097
|
+
if (row.status === "error") {
|
|
11098
|
+
return "failed";
|
|
11099
|
+
}
|
|
11100
|
+
return `${usageBar(row.limit.percentUsed)} ${percentText(row.limit.percentUsed).padStart(4)} used`;
|
|
11101
|
+
}
|
|
11102
|
+
function leftCellText(row) {
|
|
11103
|
+
if (row.status === "error") {
|
|
11104
|
+
return "-";
|
|
11105
|
+
}
|
|
11106
|
+
return percentText(row.limit.percentRemaining);
|
|
11107
|
+
}
|
|
11108
|
+
function resetCellText(row) {
|
|
11109
|
+
if (row.status === "error") {
|
|
11110
|
+
return `Error: ${row.message}`;
|
|
11111
|
+
}
|
|
11112
|
+
return row.reset;
|
|
11113
|
+
}
|
|
11114
|
+
function renderUsageCell(row, width, useColor) {
|
|
11115
|
+
const text = usageCellText(row);
|
|
11116
|
+
const padding = " ".repeat(Math.max(0, width - text.length));
|
|
11117
|
+
if (row.status === "error") {
|
|
11118
|
+
return `${color(text, "red", useColor)}${padding}`;
|
|
11119
|
+
}
|
|
11120
|
+
const severity = usageSeverity(row.limit.percentUsed);
|
|
11121
|
+
const used = percentText(row.limit.percentUsed).padStart(4);
|
|
11122
|
+
return `${color(usageBar(row.limit.percentUsed), severity, useColor)} ${color(
|
|
11123
|
+
used,
|
|
11124
|
+
severity,
|
|
11125
|
+
useColor
|
|
11126
|
+
)} used${padding}`;
|
|
11127
|
+
}
|
|
11128
|
+
function renderLeftCell(row, width, useColor) {
|
|
11129
|
+
const text = leftCellText(row);
|
|
11130
|
+
const padding = " ".repeat(Math.max(0, width - text.length));
|
|
11131
|
+
if (row.status === "error") {
|
|
11132
|
+
return `${color(text, "gray", useColor)}${padding}`;
|
|
11133
|
+
}
|
|
11134
|
+
return `${color(text, remainingSeverity(row.limit.percentRemaining), useColor)}${padding}`;
|
|
11135
|
+
}
|
|
11136
|
+
function renderResetCell(row, width, useColor) {
|
|
11137
|
+
const text = resetCellText(row);
|
|
11138
|
+
const padding = " ".repeat(Math.max(0, width - text.length));
|
|
11139
|
+
const style = row.status === "error" ? "red" : "gray";
|
|
11140
|
+
return `${color(text, style, useColor)}${padding}`;
|
|
11141
|
+
}
|
|
11142
|
+
function usageSeverity(percentUsed) {
|
|
11143
|
+
if (percentUsed == null) {
|
|
11144
|
+
return "gray";
|
|
11145
|
+
}
|
|
11146
|
+
if (percentUsed >= 95) {
|
|
11147
|
+
return "red";
|
|
11148
|
+
}
|
|
11149
|
+
if (percentUsed >= 80) {
|
|
11150
|
+
return "orange";
|
|
11151
|
+
}
|
|
11152
|
+
if (percentUsed >= 60) {
|
|
11153
|
+
return "yellow";
|
|
11154
|
+
}
|
|
11155
|
+
return "green";
|
|
11156
|
+
}
|
|
11157
|
+
function remainingSeverity(percentRemaining) {
|
|
11158
|
+
if (percentRemaining == null) {
|
|
11159
|
+
return "gray";
|
|
11160
|
+
}
|
|
11161
|
+
if (percentRemaining <= 5) {
|
|
11162
|
+
return "red";
|
|
11163
|
+
}
|
|
11164
|
+
if (percentRemaining <= 20) {
|
|
11165
|
+
return "orange";
|
|
11166
|
+
}
|
|
11167
|
+
if (percentRemaining <= 40) {
|
|
11168
|
+
return "yellow";
|
|
11169
|
+
}
|
|
11170
|
+
return "green";
|
|
11171
|
+
}
|
|
11172
|
+
function maxWidth(header, values) {
|
|
11173
|
+
return Math.max(header.length, ...values.map((value) => value.length));
|
|
11174
|
+
}
|
|
11175
|
+
function pad(value, width) {
|
|
11176
|
+
return value.padEnd(width);
|
|
11177
|
+
}
|
|
11178
|
+
function color(value, style, useColor) {
|
|
11179
|
+
if (!useColor) {
|
|
11180
|
+
return value;
|
|
11181
|
+
}
|
|
11182
|
+
return `${ANSI[style]}${value}${ANSI.reset}`;
|
|
11183
|
+
}
|
|
11184
|
+
function shouldUseColor() {
|
|
11185
|
+
if (process.env.FORCE_COLOR != null && process.env.FORCE_COLOR !== "0") {
|
|
11186
|
+
return true;
|
|
11187
|
+
}
|
|
11188
|
+
if (process.env.NO_COLOR != null) {
|
|
11189
|
+
return false;
|
|
11190
|
+
}
|
|
11191
|
+
return Boolean(process.stdout.isTTY);
|
|
11192
|
+
}
|
|
11193
|
+
async function runUsageCommand(argv) {
|
|
11194
|
+
const positionalTargets = argv.targets ?? [];
|
|
11195
|
+
const onlyTargets = parseList(argv.only);
|
|
11196
|
+
const jsonOutput = Boolean(argv.json || argv.debug);
|
|
11197
|
+
const debugOutput = Boolean(argv.debug);
|
|
11198
|
+
const selectedWindow = normalizeOptionalWindow(argv.window);
|
|
11199
|
+
const sortKey = normalizeOptionalSort(argv.sort);
|
|
11200
|
+
const cliTimeoutMs = parseTimeoutMs(argv.timeout);
|
|
11201
|
+
if (selectedWindow === "") {
|
|
11202
|
+
printError({
|
|
11203
|
+
json: jsonOutput,
|
|
11204
|
+
code: "invalid_window",
|
|
11205
|
+
message: "--window must be a non-empty value.",
|
|
11206
|
+
exitCode: 2
|
|
11207
|
+
});
|
|
11208
|
+
return null;
|
|
11209
|
+
}
|
|
11210
|
+
if (sortKey === "") {
|
|
11211
|
+
printError({
|
|
11212
|
+
json: jsonOutput,
|
|
11213
|
+
code: "invalid_sort",
|
|
11214
|
+
message: "--sort must be one of: reset, left.",
|
|
11215
|
+
exitCode: 2
|
|
11216
|
+
});
|
|
11217
|
+
return null;
|
|
11218
|
+
}
|
|
11219
|
+
if (sortKey != null && jsonOutput) {
|
|
11220
|
+
printError({
|
|
11221
|
+
json: jsonOutput,
|
|
11222
|
+
code: "sort_json_unsupported",
|
|
11223
|
+
message: "--sort is only supported for the human table output.",
|
|
11224
|
+
exitCode: 2
|
|
11225
|
+
});
|
|
11226
|
+
return null;
|
|
11227
|
+
}
|
|
11228
|
+
if (cliTimeoutMs === null) {
|
|
11229
|
+
printError({
|
|
11230
|
+
json: jsonOutput,
|
|
11231
|
+
code: "invalid_timeout",
|
|
11232
|
+
message: "--timeout must be a positive duration. Use seconds by default, or units like 500ms, 5s, or 1m.",
|
|
11233
|
+
exitCode: 2
|
|
11234
|
+
});
|
|
11235
|
+
return null;
|
|
11236
|
+
}
|
|
11237
|
+
if (argv.only != null && onlyTargets.length === 0) {
|
|
11238
|
+
printError({
|
|
11239
|
+
json: jsonOutput,
|
|
11240
|
+
code: "invalid_only",
|
|
11241
|
+
message: "--only must include at least one target.",
|
|
11242
|
+
exitCode: 2
|
|
11243
|
+
});
|
|
11244
|
+
return null;
|
|
11245
|
+
}
|
|
11246
|
+
if (positionalTargets.length > 1) {
|
|
11247
|
+
printError({
|
|
11248
|
+
json: jsonOutput,
|
|
11249
|
+
code: "too_many_targets",
|
|
11250
|
+
message: "omniagent usage accepts at most one target.",
|
|
11251
|
+
exitCode: 2
|
|
11252
|
+
});
|
|
11253
|
+
return null;
|
|
11254
|
+
}
|
|
11255
|
+
if (positionalTargets.length > 0 && onlyTargets.length > 0) {
|
|
11256
|
+
printError({
|
|
11257
|
+
json: jsonOutput,
|
|
11258
|
+
code: "conflicting_target_selection",
|
|
11259
|
+
message: "Use either a positional target or --only, not both.",
|
|
11260
|
+
exitCode: 2
|
|
11261
|
+
});
|
|
11262
|
+
return null;
|
|
11263
|
+
}
|
|
11264
|
+
const startDir = process.cwd();
|
|
11265
|
+
const repoRoot = await findRepoRoot(startDir);
|
|
11266
|
+
if (!repoRoot) {
|
|
11267
|
+
printError({
|
|
11268
|
+
json: jsonOutput,
|
|
11269
|
+
code: "repo_not_found",
|
|
11270
|
+
message: `Repository root not found starting from ${startDir}. Looked for .git or package.json.`,
|
|
11271
|
+
exitCode: 1
|
|
11272
|
+
});
|
|
11273
|
+
return null;
|
|
11274
|
+
}
|
|
11275
|
+
const agentsDirResolution = resolveAgentsDir(repoRoot, argv.agentsDir);
|
|
11276
|
+
if (agentsDirResolution.source === "override") {
|
|
11277
|
+
const validation2 = await validateAgentsDir(repoRoot, argv.agentsDir, { requireWrite: false });
|
|
11278
|
+
if (validation2.validationStatus !== "valid") {
|
|
11279
|
+
printError({
|
|
11280
|
+
json: jsonOutput,
|
|
11281
|
+
code: "invalid_agents_dir",
|
|
11282
|
+
message: validation2.errorMessage,
|
|
11283
|
+
exitCode: 1
|
|
11284
|
+
});
|
|
11285
|
+
return null;
|
|
11286
|
+
}
|
|
11287
|
+
}
|
|
11288
|
+
const agentsDir = agentsDirResolution.resolvedPath;
|
|
11289
|
+
const homeDir = os.homedir();
|
|
11290
|
+
const { config } = await loadTargetConfig({ repoRoot, agentsDir });
|
|
11291
|
+
const validation = validateTargetConfig({ config, builtIns: BUILTIN_TARGETS });
|
|
11292
|
+
if (!validation.valid) {
|
|
11293
|
+
printError({
|
|
11294
|
+
json: jsonOutput,
|
|
11295
|
+
code: "invalid_target_config",
|
|
11296
|
+
message: `Invalid target configuration:
|
|
11297
|
+
- ${validation.errors.join("\n- ")}`,
|
|
11298
|
+
exitCode: 1
|
|
11299
|
+
});
|
|
11300
|
+
return null;
|
|
11301
|
+
}
|
|
11302
|
+
const resolved = resolveTargets({ config: validation.config, builtIns: BUILTIN_TARGETS });
|
|
11303
|
+
const targetResolver = createTargetNameResolver(resolved.targets);
|
|
11304
|
+
const usageCapableTargets = resolved.targets.filter((target) => target.usage);
|
|
11305
|
+
const supportedUsageTargetsMessage = formatSupportedUsageTargetsMessage(usageCapableTargets);
|
|
11306
|
+
const usingOnlySelection = onlyTargets.length > 0;
|
|
11307
|
+
const explicitTargetNames = positionalTargets[0] ? [positionalTargets[0]] : onlyTargets;
|
|
11308
|
+
let selectedTargets;
|
|
11309
|
+
const resolvedUsageCommands = /* @__PURE__ */ new Map();
|
|
11310
|
+
if (explicitTargetNames.length > 0) {
|
|
11311
|
+
const selectedIds = [];
|
|
11312
|
+
const unknownTargetNames = [];
|
|
11313
|
+
for (const targetName of explicitTargetNames) {
|
|
11314
|
+
const resolvedName = targetResolver.resolveTargetName(targetName);
|
|
11315
|
+
if (!resolvedName) {
|
|
11316
|
+
unknownTargetNames.push(targetName);
|
|
11317
|
+
continue;
|
|
11318
|
+
}
|
|
11319
|
+
if (!selectedIds.includes(resolvedName)) {
|
|
11320
|
+
selectedIds.push(resolvedName);
|
|
11321
|
+
}
|
|
11322
|
+
}
|
|
11323
|
+
if (unknownTargetNames.length > 0) {
|
|
11324
|
+
const unknownLabel = unknownTargetNames.join(", ");
|
|
11325
|
+
const message = usingOnlySelection ? `Unknown target name(s): ${unknownLabel}. ${supportedUsageTargetsMessage}` : `Unknown target: ${unknownLabel}. ${supportedUsageTargetsMessage}`;
|
|
11326
|
+
printError({
|
|
11327
|
+
json: jsonOutput,
|
|
11328
|
+
code: "unknown_target",
|
|
11329
|
+
message,
|
|
11330
|
+
exitCode: 2
|
|
11331
|
+
});
|
|
11332
|
+
return null;
|
|
11333
|
+
}
|
|
11334
|
+
const requestedTargets = selectedIds.flatMap((targetId) => {
|
|
11335
|
+
const target = resolved.byId.get(targetId.toLowerCase());
|
|
11336
|
+
return target ? [target] : [];
|
|
11337
|
+
});
|
|
11338
|
+
const unsupportedTargets = requestedTargets.filter((target) => !target.usage);
|
|
11339
|
+
if (unsupportedTargets.length > 0) {
|
|
11340
|
+
const unsupportedLabel = unsupportedTargets.map((target) => target.displayName).join(", ");
|
|
11341
|
+
printError({
|
|
11342
|
+
json: jsonOutput,
|
|
11343
|
+
code: "usage_unsupported",
|
|
11344
|
+
message: `${unsupportedLabel} does not support usage extraction. ${supportedUsageTargetsMessage}`,
|
|
11345
|
+
exitCode: 2,
|
|
11346
|
+
target: unsupportedTargets.length === 1 ? unsupportedTargets[0] : void 0
|
|
11347
|
+
});
|
|
11348
|
+
return null;
|
|
11349
|
+
}
|
|
11350
|
+
const requestedUsageIds = new Set(selectedIds);
|
|
11351
|
+
selectedTargets = usageCapableTargets.filter((target) => requestedUsageIds.has(target.id));
|
|
11352
|
+
const availabilityResults = await Promise.all(
|
|
11353
|
+
selectedTargets.map(async (target) => ({
|
|
11354
|
+
target,
|
|
11355
|
+
availability: await checkUsageCommandAvailability(target)
|
|
11356
|
+
}))
|
|
11357
|
+
);
|
|
11358
|
+
const unavailableResults = availabilityResults.filter(
|
|
11359
|
+
({ availability }) => availability.status !== "available"
|
|
11360
|
+
);
|
|
11361
|
+
if (unavailableResults.length > 0) {
|
|
11362
|
+
if (jsonOutput) {
|
|
11363
|
+
const now2 = /* @__PURE__ */ new Date();
|
|
11364
|
+
const envelope2 = buildEnvelope({
|
|
11365
|
+
generatedAt: now2.toISOString(),
|
|
11366
|
+
targets: [],
|
|
11367
|
+
errors: unavailableResults.map(
|
|
11368
|
+
({ target, availability }) => buildError(target, "cli_unavailable", availability.reason ?? "CLI not found on PATH.")
|
|
11369
|
+
),
|
|
11370
|
+
notes: [],
|
|
11371
|
+
debug: debugOutput,
|
|
11372
|
+
debugArtifacts: []
|
|
11373
|
+
});
|
|
11374
|
+
console.log(JSON.stringify(envelope2, null, 2));
|
|
11375
|
+
} else if (unavailableResults.length === 1) {
|
|
11376
|
+
const unavailableResult = unavailableResults[0];
|
|
11377
|
+
if (unavailableResult) {
|
|
11378
|
+
const { target, availability } = unavailableResult;
|
|
11379
|
+
const message = availability.reason ?? "CLI not found on PATH.";
|
|
11380
|
+
console.error(
|
|
11381
|
+
`Error: ${target.displayName} usage extraction requires its CLI. ${message}`
|
|
11382
|
+
);
|
|
11383
|
+
}
|
|
11384
|
+
} else {
|
|
11385
|
+
console.error(
|
|
11386
|
+
[
|
|
11387
|
+
"Error: Some requested usage CLIs are unavailable:",
|
|
11388
|
+
...unavailableResults.map(({ target, availability }) => {
|
|
11389
|
+
const message = availability.reason ?? "CLI not found on PATH.";
|
|
11390
|
+
return `- ${target.displayName}: ${message}`;
|
|
11391
|
+
})
|
|
11392
|
+
].join("\n")
|
|
11393
|
+
);
|
|
11394
|
+
}
|
|
11395
|
+
process.exit(1);
|
|
11396
|
+
return null;
|
|
11397
|
+
}
|
|
11398
|
+
for (const { target, availability } of availabilityResults) {
|
|
11399
|
+
const resolvedCommand = availability.resolvedPath ?? availability.command;
|
|
11400
|
+
if (resolvedCommand) {
|
|
11401
|
+
resolvedUsageCommands.set(target.id, resolvedCommand);
|
|
11402
|
+
}
|
|
11403
|
+
}
|
|
11404
|
+
} else {
|
|
11405
|
+
const availabilityResults = await Promise.all(
|
|
11406
|
+
usageCapableTargets.map(async (target) => ({
|
|
11407
|
+
target,
|
|
11408
|
+
availability: await checkUsageCommandAvailability(target)
|
|
11409
|
+
}))
|
|
11410
|
+
);
|
|
11411
|
+
selectedTargets = [];
|
|
11412
|
+
for (const { target, availability } of availabilityResults) {
|
|
11413
|
+
if (availability.status !== "available") {
|
|
11414
|
+
continue;
|
|
11415
|
+
}
|
|
11416
|
+
selectedTargets.push(target);
|
|
11417
|
+
const resolvedCommand = availability.resolvedPath ?? availability.command;
|
|
11418
|
+
if (resolvedCommand) {
|
|
11419
|
+
resolvedUsageCommands.set(target.id, resolvedCommand);
|
|
11420
|
+
}
|
|
11421
|
+
}
|
|
11422
|
+
if (selectedTargets.length === 0) {
|
|
11423
|
+
const supportedUsageTargets = formatUsageTargetLabel(usageCapableTargets);
|
|
11424
|
+
const message = supportedUsageTargets ? `No installed active usage-capable agents were found. Install one of: ${supportedUsageTargets}.` : "No active usage-capable targets are enabled by the current target configuration.";
|
|
11425
|
+
if (jsonOutput) {
|
|
11426
|
+
const now2 = /* @__PURE__ */ new Date();
|
|
11427
|
+
const envelope2 = buildEnvelope({
|
|
11428
|
+
generatedAt: now2.toISOString(),
|
|
11429
|
+
targets: [],
|
|
11430
|
+
errors: [],
|
|
11431
|
+
notes: [message],
|
|
11432
|
+
debug: debugOutput,
|
|
11433
|
+
debugArtifacts: []
|
|
11434
|
+
});
|
|
11435
|
+
console.log(JSON.stringify(envelope2, null, 2));
|
|
11436
|
+
} else {
|
|
11437
|
+
console.log(message);
|
|
11438
|
+
}
|
|
11439
|
+
return {
|
|
11440
|
+
envelope: buildEnvelope({
|
|
11441
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11442
|
+
targets: [],
|
|
11443
|
+
errors: [],
|
|
11444
|
+
notes: [message],
|
|
11445
|
+
debug: debugOutput,
|
|
11446
|
+
debugArtifacts: []
|
|
11447
|
+
}),
|
|
11448
|
+
exitCode: 0,
|
|
11449
|
+
selectedTargets
|
|
11450
|
+
};
|
|
11451
|
+
}
|
|
11452
|
+
}
|
|
11453
|
+
const now = /* @__PURE__ */ new Date();
|
|
11454
|
+
const outcomes = await Promise.all(
|
|
11455
|
+
selectedTargets.map(
|
|
11456
|
+
(target) => extractUsageForTarget({
|
|
11457
|
+
target,
|
|
11458
|
+
repoRoot,
|
|
11459
|
+
agentsDir,
|
|
11460
|
+
homeDir,
|
|
11461
|
+
selectedWindow,
|
|
11462
|
+
timeoutMs: resolveTargetTimeoutMs(target, cliTimeoutMs),
|
|
11463
|
+
debug: debugOutput,
|
|
11464
|
+
now,
|
|
11465
|
+
command: resolvedUsageCommands.get(target.id)
|
|
11466
|
+
})
|
|
11467
|
+
)
|
|
11468
|
+
);
|
|
11469
|
+
const targets = [];
|
|
11470
|
+
const errors = [];
|
|
11471
|
+
const notes = [];
|
|
11472
|
+
const debugArtifacts = [];
|
|
11473
|
+
for (const outcome of outcomes) {
|
|
11474
|
+
if (outcome.status === "success") {
|
|
11475
|
+
targets.push(outcome.result);
|
|
11476
|
+
errors.push(...outcome.errors);
|
|
11477
|
+
notes.push(...outcome.notes);
|
|
11478
|
+
debugArtifacts.push(
|
|
11479
|
+
...outcome.debug.map((artifact) => ({
|
|
11480
|
+
...artifact,
|
|
11481
|
+
targetId: outcome.target.id,
|
|
11482
|
+
displayName: outcome.target.displayName
|
|
11483
|
+
}))
|
|
11484
|
+
);
|
|
11485
|
+
} else {
|
|
11486
|
+
errors.push(outcome.error);
|
|
11487
|
+
debugArtifacts.push(
|
|
11488
|
+
...outcome.debug.map((artifact) => ({
|
|
11489
|
+
...artifact,
|
|
11490
|
+
targetId: outcome.target.id,
|
|
11491
|
+
displayName: outcome.target.displayName
|
|
11492
|
+
}))
|
|
11493
|
+
);
|
|
11494
|
+
}
|
|
11495
|
+
}
|
|
11496
|
+
const envelope = buildEnvelope({
|
|
11497
|
+
generatedAt: now.toISOString(),
|
|
11498
|
+
targets,
|
|
11499
|
+
errors,
|
|
11500
|
+
notes,
|
|
11501
|
+
debug: debugOutput,
|
|
11502
|
+
debugArtifacts
|
|
11503
|
+
});
|
|
11504
|
+
const exitCode = errors.length > 0 ? 1 : 0;
|
|
11505
|
+
if (jsonOutput) {
|
|
11506
|
+
console.log(JSON.stringify(envelope, null, 2));
|
|
11507
|
+
} else {
|
|
11508
|
+
console.log(formatUsageTable(envelope, sortKey));
|
|
11509
|
+
}
|
|
11510
|
+
if (exitCode !== 0) {
|
|
11511
|
+
process.exit(exitCode);
|
|
11512
|
+
}
|
|
11513
|
+
return { envelope, exitCode, selectedTargets };
|
|
11514
|
+
}
|
|
11515
|
+
const usageCommand = {
|
|
11516
|
+
command: "usage [targets..]",
|
|
11517
|
+
describe: "Report usage limits for installed agent CLIs",
|
|
11518
|
+
builder: (yargsInstance) => yargsInstance.usage(
|
|
11519
|
+
"omniagent usage [target] [--only <targets>] [--sort <key>] [--window <window>] [--timeout <seconds>] [--agentsDir <path>] [--json] [--debug]"
|
|
11520
|
+
).positional("targets", {
|
|
11521
|
+
type: "string",
|
|
11522
|
+
array: true,
|
|
11523
|
+
describe: "Optional target id or alias."
|
|
11524
|
+
}).option("window", {
|
|
11525
|
+
type: "string",
|
|
11526
|
+
describe: "Filter usage rows by window (hourly, weekly, 5h, or a custom window)."
|
|
11527
|
+
}).option("only", {
|
|
11528
|
+
type: "string",
|
|
11529
|
+
describe: "Comma-separated target ids or aliases to report usage for."
|
|
11530
|
+
}).option("sort", {
|
|
11531
|
+
type: "string",
|
|
11532
|
+
describe: "Globally sort table rows by reset or left."
|
|
11533
|
+
}).option("timeout", {
|
|
11534
|
+
type: "string",
|
|
11535
|
+
describe: "Per-agent extraction timeout. Bare numbers are seconds; units include ms, s, and m."
|
|
11536
|
+
}).option("agentsDir", {
|
|
11537
|
+
type: "string",
|
|
11538
|
+
describe: "Override the agents directory (relative paths resolve from the project root)",
|
|
11539
|
+
defaultDescription: DEFAULT_AGENTS_DIR,
|
|
11540
|
+
coerce: (value) => {
|
|
11541
|
+
if (typeof value !== "string") {
|
|
11542
|
+
return value;
|
|
11543
|
+
}
|
|
11544
|
+
const trimmed = value.trim();
|
|
11545
|
+
return trimmed.length > 0 ? trimmed : void 0;
|
|
11546
|
+
}
|
|
11547
|
+
}).option("json", {
|
|
11548
|
+
type: "boolean",
|
|
11549
|
+
describe: "Print a stable JSON envelope."
|
|
11550
|
+
}).option("debug", {
|
|
11551
|
+
type: "boolean",
|
|
11552
|
+
describe: "Print JSON and include extractor debug artifacts when available."
|
|
11553
|
+
}).epilog(
|
|
11554
|
+
"Usage extraction may launch agent TUIs and may incur cost if an agent reads repo context on startup. omniagent uses cheap/minimal launch settings where possible."
|
|
11555
|
+
),
|
|
11556
|
+
handler: async (argv) => {
|
|
11557
|
+
await runUsageCommand(argv);
|
|
11558
|
+
}
|
|
11559
|
+
};
|
|
10212
11560
|
const EXIT_CODES = {
|
|
10213
11561
|
success: 0,
|
|
10214
11562
|
"execution-error": 1,
|
|
@@ -10879,7 +12227,7 @@ function resolveVersion() {
|
|
|
10879
12227
|
return "0.0.0";
|
|
10880
12228
|
}
|
|
10881
12229
|
const VERSION = resolveVersion();
|
|
10882
|
-
const KNOWN_COMMANDS = /* @__PURE__ */ new Set(["hello", "greet", "echo", "sync", "dev", "profiles"]);
|
|
12230
|
+
const KNOWN_COMMANDS = /* @__PURE__ */ new Set(["hello", "greet", "echo", "sync", "dev", "profiles", "usage"]);
|
|
10883
12231
|
const SHIM_CAPABILITIES = [
|
|
10884
12232
|
"Capabilities by agent:",
|
|
10885
12233
|
" codex: approval, sandbox, output, model, web",
|
|
@@ -10929,7 +12277,7 @@ function runCli(argv = process.argv, options = {}) {
|
|
|
10929
12277
|
console.error(formatError(message, args));
|
|
10930
12278
|
const exitCode = isCommandInvocation(args) ? 1 : 2;
|
|
10931
12279
|
process.exit(exitCode);
|
|
10932
|
-
}).command(helloCommand).command(greetCommand).command(echoCommand).command(syncCommand).command(devCommand).command(profilesCommand).command(
|
|
12280
|
+
}).command(helloCommand).command(greetCommand).command(echoCommand).command(syncCommand).command(devCommand).command(profilesCommand).command(usageCommand).command(
|
|
10933
12281
|
"$0",
|
|
10934
12282
|
"omniagent CLI",
|
|
10935
12283
|
(yargsInstance) => yargsInstance.usage("omniagent [flags] --agent <target-id> [-- <agent flags>]").example("omniagent --agent codex", "Start an interactive session (default mode).").example('omniagent -p "Summarize the repo" --agent codex', "Run a one-shot prompt.").example("omniagent --agent codex -- --some-flag", "Pass through agent-specific flags.").option("prompt", {
|
|
@@ -10989,5 +12337,11 @@ if (!entry) {
|
|
|
10989
12337
|
}
|
|
10990
12338
|
}
|
|
10991
12339
|
export {
|
|
12340
|
+
compactLines as a,
|
|
12341
|
+
parsePercentRemaining as b,
|
|
12342
|
+
cleanControlOutput as c,
|
|
12343
|
+
parseResetText as d,
|
|
12344
|
+
makeUsageLimit as m,
|
|
12345
|
+
parsePercentUsed as p,
|
|
10992
12346
|
runCli
|
|
10993
12347
|
};
|