fifony 0.1.31 → 0.1.32
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 +36 -3
- package/app/dist/assets/CommandPalette-DuugDkZo.js +1 -0
- package/app/dist/assets/KeyboardShortcutsHelp-B1a9ZDDu.js +1 -0
- package/app/dist/assets/OnboardingWizard-BF43aq_I.js +1 -0
- package/app/dist/assets/analytics.lazy-Bc6WBoMs.js +1 -0
- package/app/dist/assets/api-ChEctgc5.js +1 -0
- package/app/dist/assets/{createLucideIcon-DgMTp0yx.js → createLucideIcon-ggBfAlHh.js} +1 -1
- package/app/dist/assets/{index-lf3jEP_3.css → index-CGCZneFa.css} +1 -1
- package/app/dist/assets/index-Dx5YOoMm.js +49 -0
- package/app/dist/assets/rolldown-runtime-Dw2cE7zH.js +1 -0
- package/app/dist/assets/{vendor-D-IqxHHu.js → vendor-CSL5bxVU.js} +1 -1
- package/app/dist/index.html +6 -5
- package/app/dist/service-worker.js +1 -1
- package/dist/agent/run-local.js +4 -4
- package/dist/{agent-OBLUHG2W.js → agent-3NYJEHH5.js} +5 -5
- package/dist/{chunk-IYAF3SY6.js → chunk-2D5P75F6.js} +556 -17
- package/dist/{chunk-7AMUAUY5.js → chunk-FAFGDK62.js} +625 -80
- package/dist/{chunk-VUNMXX7N.js → chunk-FPUTP743.js} +11 -11
- package/dist/{chunk-CXFEPU5Q.js → chunk-H2QRC6UQ.js} +8 -17
- package/dist/cli.js +4 -4
- package/dist/{issue-runner-CI7IUBBD.js → issue-runner-ZNDKLOCZ.js} +5 -5
- package/dist/{issue-state-machine-ETAJLBS6.js → issue-state-machine-NBSYRDZW.js} +3 -3
- package/dist/{issues-6VCD27PA.js → issues-GBWLDBVU.js} +5 -5
- package/dist/{queue-workers-IEI23UUO.js → queue-workers-OHPJKAPM.js} +2 -2
- package/dist/{scheduler-JSH55NAQ.js → scheduler-OU35EYUH.js} +5 -5
- package/dist/{store-MTE6H7WQ.js → store-FFHHRNVI.js} +5 -5
- package/dist/{workspace-3PV5PTR5.js → workspace-5XOCMZ57.js} +2 -2
- package/package.json +4 -1
- package/app/dist/assets/KeyboardShortcutsHelp-3vwtTKsQ.js +0 -1
- package/app/dist/assets/OnboardingWizard-urUin_Ob.js +0 -1
- package/app/dist/assets/analytics.lazy-CbuW3ebu.js +0 -1
- package/app/dist/assets/index-ywtlX6S8.js +0 -45
- package/app/dist/assets/rolldown-runtime-DF2fYuay.js +0 -1
|
@@ -35,7 +35,7 @@ import {
|
|
|
35
35
|
writeFileSync
|
|
36
36
|
} from "fs";
|
|
37
37
|
import { join as join6 } from "path";
|
|
38
|
-
import { env } from "process";
|
|
38
|
+
import { env as env2 } from "process";
|
|
39
39
|
import { spawn } from "child_process";
|
|
40
40
|
|
|
41
41
|
// src/agents/providers.ts
|
|
@@ -309,9 +309,531 @@ function extractPlanDirs(plan) {
|
|
|
309
309
|
return [...dirs];
|
|
310
310
|
}
|
|
311
311
|
|
|
312
|
+
// src/agents/adapters/usage.ts
|
|
313
|
+
import { cwd, env } from "process";
|
|
314
|
+
function sleep(ms) {
|
|
315
|
+
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
316
|
+
}
|
|
317
|
+
async function createPtyProcess(command, args) {
|
|
318
|
+
try {
|
|
319
|
+
const nodePty = await import("node-pty");
|
|
320
|
+
if (typeof nodePty.spawn !== "function") {
|
|
321
|
+
return null;
|
|
322
|
+
}
|
|
323
|
+
return nodePty.spawn(command, args, {
|
|
324
|
+
name: "xterm-color",
|
|
325
|
+
cols: 160,
|
|
326
|
+
rows: 48,
|
|
327
|
+
cwd: cwd(),
|
|
328
|
+
env
|
|
329
|
+
});
|
|
330
|
+
} catch (error) {
|
|
331
|
+
logger.debug(`Failed to initialize node-pty for ${command}: ${String(error)}`);
|
|
332
|
+
return null;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
function stripTerminalEscapes(input) {
|
|
336
|
+
return input.replace(/\x1B\[[?>=!]?[0-9;]*[A-HJKSTdfG]/g, " ").replace(/\x1B\[[?>=!]?[0-9;]*[a-zI-Ri-z~]/g, "").replace(/\x1B\[[?>=!]?[0-9;]*[A-Za-z~]/g, "").replace(/\x1B\][^\x07\x1B]*(?:\x07|\x1B\\)/g, "").replace(/\x1B[()#][A-Za-z0-9]/g, "").replace(/\x1B[A-Za-z]/g, "").replace(/[^\x20-\x7E\r\n\t]/g, "").replace(/[ \t]{2,}/g, " ");
|
|
337
|
+
}
|
|
338
|
+
function parseNumber(value) {
|
|
339
|
+
if (!value) return 0;
|
|
340
|
+
const clean = value.replace(/[^\d]/g, "");
|
|
341
|
+
if (!clean) return 0;
|
|
342
|
+
const parsed = Number.parseInt(clean, 10);
|
|
343
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
344
|
+
}
|
|
345
|
+
function parseTokenQuantity(value) {
|
|
346
|
+
if (!value) return 0;
|
|
347
|
+
const match = value.replace(/,/g, "").trim().match(/^([0-9]+(?:\.[0-9]+)?)([kKmMbBtT]?)$/);
|
|
348
|
+
if (!match) {
|
|
349
|
+
const fallback = parseNumber(value);
|
|
350
|
+
return fallback;
|
|
351
|
+
}
|
|
352
|
+
const number = Number.parseFloat(match[1]);
|
|
353
|
+
if (!Number.isFinite(number)) return 0;
|
|
354
|
+
const suffix = match[2].toLowerCase();
|
|
355
|
+
if (suffix === "t") return number * 1e12;
|
|
356
|
+
if (suffix === "b") return number * 1e9;
|
|
357
|
+
if (suffix === "m") return number * 1e6;
|
|
358
|
+
if (suffix === "k") return number * 1e3;
|
|
359
|
+
return number;
|
|
360
|
+
}
|
|
361
|
+
function parsePercent(value) {
|
|
362
|
+
if (!value) return null;
|
|
363
|
+
const parsed = Number.parseFloat(value.replace("%", ""));
|
|
364
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
365
|
+
}
|
|
366
|
+
function keepLargest(current, incoming) {
|
|
367
|
+
if (!Number.isFinite(incoming)) return current ?? 0;
|
|
368
|
+
if (incoming <= 0) return current ?? 0;
|
|
369
|
+
return current === null ? incoming : Math.max(current, incoming);
|
|
370
|
+
}
|
|
371
|
+
var MONTH_MAP = {
|
|
372
|
+
jan: 0,
|
|
373
|
+
feb: 1,
|
|
374
|
+
mar: 2,
|
|
375
|
+
apr: 3,
|
|
376
|
+
may: 4,
|
|
377
|
+
jun: 5,
|
|
378
|
+
jul: 6,
|
|
379
|
+
aug: 7,
|
|
380
|
+
sep: 8,
|
|
381
|
+
oct: 9,
|
|
382
|
+
nov: 10,
|
|
383
|
+
dec: 11
|
|
384
|
+
};
|
|
385
|
+
function toMonthIndex(value) {
|
|
386
|
+
const index = MONTH_MAP[value.toLowerCase().slice(0, 3)];
|
|
387
|
+
return typeof index === "number" ? index : null;
|
|
388
|
+
}
|
|
389
|
+
function normalizeResetText(value) {
|
|
390
|
+
return value.replace(/\([^)]*\)/g, " ").replace(/\s+/g, " ").trim();
|
|
391
|
+
}
|
|
392
|
+
function parseResetTime(value) {
|
|
393
|
+
const match = value.trim().match(/^(\d{1,2})(?::(\d{2}))?\s*([AaPp][Mm])?$/);
|
|
394
|
+
if (!match) return null;
|
|
395
|
+
let hour = Number.parseInt(match[1], 10);
|
|
396
|
+
const minute = Number.parseInt(match[2] || "0", 10);
|
|
397
|
+
if (!Number.isFinite(hour) || !Number.isFinite(minute)) return null;
|
|
398
|
+
if (hour < 0 || hour > 23 || minute < 0 || minute > 59) return null;
|
|
399
|
+
const suffix = match[3]?.toLowerCase();
|
|
400
|
+
if (suffix === "pm" && hour !== 12) hour += 12;
|
|
401
|
+
if (suffix === "am" && hour === 12) hour = 0;
|
|
402
|
+
return { hour, minute };
|
|
403
|
+
}
|
|
404
|
+
function parseResetDateFromText(raw) {
|
|
405
|
+
const text = normalizeResetText(raw);
|
|
406
|
+
if (!text) return null;
|
|
407
|
+
const now2 = /* @__PURE__ */ new Date();
|
|
408
|
+
const explicitDateMatch = text.match(/([A-Za-z]{3,9})\s*(\d{1,2})(?:,?|\s+)(\d{1,2}:\d{2}(?:\s*[AaPp][Mm])?)/i) || text.match(/(\d{1,2})\s+([A-Za-z]{3,9})(?:,\s*)?(\d{1,2}:\d{2}(?:\s*[AaPp][Mm])?)/i);
|
|
409
|
+
if (explicitDateMatch) {
|
|
410
|
+
const [, monthRaw, dayRaw, timeRaw] = explicitDateMatch;
|
|
411
|
+
const day = Number.parseInt(dayRaw, 10);
|
|
412
|
+
const monthIndex = toMonthIndex(monthRaw);
|
|
413
|
+
const time2 = parseResetTime(timeRaw);
|
|
414
|
+
if (!Number.isNaN(day) && monthIndex !== null && time2) {
|
|
415
|
+
const candidate = new Date(now2);
|
|
416
|
+
candidate.setMonth(monthIndex);
|
|
417
|
+
candidate.setDate(day);
|
|
418
|
+
candidate.setHours(time2.hour, time2.minute, 0, 0);
|
|
419
|
+
if (candidate.getTime() <= now2.getTime()) {
|
|
420
|
+
candidate.setFullYear(now2.getFullYear() + 1);
|
|
421
|
+
}
|
|
422
|
+
return candidate.toISOString();
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
const timeMatch = text.match(/(\d{1,2}:\d{2}(?:\s*[AaPp][Mm])?)/i) || text.match(/(\d{1,2})(?:\s*([AaPp][Mm]))/i);
|
|
426
|
+
const time = timeMatch?.[1] ? parseResetTime(timeMatch[1]) : null;
|
|
427
|
+
if (time) {
|
|
428
|
+
const candidate = new Date(now2);
|
|
429
|
+
candidate.setHours(time.hour, time.minute, 0, 0);
|
|
430
|
+
if (candidate.getTime() <= now2.getTime()) {
|
|
431
|
+
candidate.setDate(candidate.getDate() + 1);
|
|
432
|
+
}
|
|
433
|
+
return candidate.toISOString();
|
|
434
|
+
}
|
|
435
|
+
return null;
|
|
436
|
+
}
|
|
437
|
+
function initSnapshot(raw) {
|
|
438
|
+
return {
|
|
439
|
+
currentModel: null,
|
|
440
|
+
allTimeInputTokens: null,
|
|
441
|
+
allTimeOutputTokens: null,
|
|
442
|
+
todayInputTokens: null,
|
|
443
|
+
todayOutputTokens: null,
|
|
444
|
+
thisWeekInputTokens: null,
|
|
445
|
+
thisWeekOutputTokens: null,
|
|
446
|
+
last5HoursInputTokens: null,
|
|
447
|
+
last5HoursOutputTokens: null,
|
|
448
|
+
allTimeSessions: null,
|
|
449
|
+
todaySessions: null,
|
|
450
|
+
thisWeekSessions: null,
|
|
451
|
+
last5HoursSessions: null,
|
|
452
|
+
weeklyLimitEstimate: null,
|
|
453
|
+
weeklyPercentUsed: null,
|
|
454
|
+
resetInfo: null,
|
|
455
|
+
nextResetAt: null,
|
|
456
|
+
version: null,
|
|
457
|
+
plan: null,
|
|
458
|
+
account: null,
|
|
459
|
+
effort: null,
|
|
460
|
+
sessionPercentUsed: null,
|
|
461
|
+
sessionResetInfo: null,
|
|
462
|
+
rateLimits: [],
|
|
463
|
+
raw
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
function waitForOutput(getOutput, pattern, timeoutMs, pollMs = 100) {
|
|
467
|
+
const deadline = Date.now() + timeoutMs;
|
|
468
|
+
return new Promise((resolve2) => {
|
|
469
|
+
const check = () => {
|
|
470
|
+
if (pattern.test(getOutput())) return resolve2(true);
|
|
471
|
+
if (Date.now() >= deadline) return resolve2(false);
|
|
472
|
+
setTimeout(check, pollMs);
|
|
473
|
+
};
|
|
474
|
+
check();
|
|
475
|
+
});
|
|
476
|
+
}
|
|
477
|
+
var TRUST_PROMPT_RE = /trust this folder|trust this project|safety check|Do you trust/i;
|
|
478
|
+
var CLI_READY_RE = /[❯›▶>]\s|Type your message|for shortcuts|\/model to change/;
|
|
479
|
+
async function collectProviderStatusText(command, args, statusCommand) {
|
|
480
|
+
try {
|
|
481
|
+
const ptyProcess = await createPtyProcess(command, args);
|
|
482
|
+
if (!ptyProcess) return null;
|
|
483
|
+
let output = "";
|
|
484
|
+
ptyProcess.onData((data) => {
|
|
485
|
+
output += data;
|
|
486
|
+
});
|
|
487
|
+
const gotPrompt = await waitForOutput(
|
|
488
|
+
() => output,
|
|
489
|
+
new RegExp(`${TRUST_PROMPT_RE.source}|${CLI_READY_RE.source}`, "i"),
|
|
490
|
+
5e3,
|
|
491
|
+
100
|
|
492
|
+
);
|
|
493
|
+
if (gotPrompt && TRUST_PROMPT_RE.test(output)) {
|
|
494
|
+
logger.debug(`Trust prompt detected for ${command}, auto-accepting`);
|
|
495
|
+
ptyProcess.write("\r");
|
|
496
|
+
await waitForOutput(() => output, CLI_READY_RE, 5e3, 100);
|
|
497
|
+
} else if (!gotPrompt) {
|
|
498
|
+
logger.debug(`PTY prompt not detected for ${command}, sending command after fallback wait`);
|
|
499
|
+
await sleep(3e3);
|
|
500
|
+
}
|
|
501
|
+
let prevLen = output.length;
|
|
502
|
+
await sleep(800);
|
|
503
|
+
if (output.length !== prevLen) {
|
|
504
|
+
await sleep(1200);
|
|
505
|
+
}
|
|
506
|
+
ptyProcess.write(`${statusCommand}\r`);
|
|
507
|
+
await sleep(3e3);
|
|
508
|
+
ptyProcess.write("\x1B");
|
|
509
|
+
await sleep(200);
|
|
510
|
+
ptyProcess.write("");
|
|
511
|
+
await sleep(200);
|
|
512
|
+
try {
|
|
513
|
+
ptyProcess.kill();
|
|
514
|
+
} catch {
|
|
515
|
+
}
|
|
516
|
+
return stripTerminalEscapes(output).trim();
|
|
517
|
+
} catch (error) {
|
|
518
|
+
logger.debug(`Failed collecting status via pty for ${command}: ${String(error)}`);
|
|
519
|
+
return null;
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
function parseClaudeUsageHeading(line) {
|
|
523
|
+
if (/current week.*all models/i.test(line)) return { section: "current-week-all" };
|
|
524
|
+
const modelWeekMatch = line.match(/current week\s*\((\w+)\s+only\)/i);
|
|
525
|
+
if (modelWeekMatch) return { section: "current-week-model", modelScope: modelWeekMatch[1].toLowerCase() };
|
|
526
|
+
if (/current session/i.test(line)) return { section: "current-session" };
|
|
527
|
+
return null;
|
|
528
|
+
}
|
|
529
|
+
function formatClaudeModelFromLabel(label) {
|
|
530
|
+
const normalized = label.trim().toLowerCase();
|
|
531
|
+
const compact = normalized.replace(/\s+/g, "-");
|
|
532
|
+
if (compact.includes("opus")) return `claude-${compact}`;
|
|
533
|
+
if (compact.includes("sonnet")) return `claude-${compact}`;
|
|
534
|
+
if (compact.includes("haiku")) return `claude-${compact}`;
|
|
535
|
+
if (compact.startsWith("claude-")) return compact;
|
|
536
|
+
return label.trim();
|
|
537
|
+
}
|
|
538
|
+
function parseClaudeUsageFromStatus(raw) {
|
|
539
|
+
const base = initSnapshot(raw);
|
|
540
|
+
base.weeklyPercentUsed = null;
|
|
541
|
+
const lines = raw.split(/[\r\n]+/).map((line) => line.trim()).filter(Boolean);
|
|
542
|
+
let currentHeading = null;
|
|
543
|
+
let lastPercentSection = null;
|
|
544
|
+
let lastPercentUsed = null;
|
|
545
|
+
for (const line of lines) {
|
|
546
|
+
const normalized = line.toLowerCase();
|
|
547
|
+
if (/^esc to cancel/i.test(normalized)) continue;
|
|
548
|
+
if (/^loading/i.test(normalized)) continue;
|
|
549
|
+
if (/^status dialog/i.test(normalized)) continue;
|
|
550
|
+
const versionMatch = line.match(/Claude Code v(\d+\.\d+\.\d+)/i);
|
|
551
|
+
if (versionMatch?.[1]) {
|
|
552
|
+
base.version = versionMatch[1];
|
|
553
|
+
continue;
|
|
554
|
+
}
|
|
555
|
+
const effortMatch = line.match(/with\s+(high|medium|low|extra\s*high)\s+effort/i);
|
|
556
|
+
if (effortMatch?.[1]) {
|
|
557
|
+
base.effort = effortMatch[1].toLowerCase();
|
|
558
|
+
const afterEffort = line.slice(line.indexOf(effortMatch[0]) + effortMatch[0].length).trim();
|
|
559
|
+
if (afterEffort && /^[A-Z]/.test(afterEffort)) {
|
|
560
|
+
base.plan = afterEffort;
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
const heading = parseClaudeUsageHeading(line);
|
|
564
|
+
if (heading) {
|
|
565
|
+
currentHeading = heading;
|
|
566
|
+
continue;
|
|
567
|
+
}
|
|
568
|
+
const modelMatch = line.match(/\b(opus|sonnet|haiku)\s*\d+(?:\.\d+)?(?:\s*-\d+)?/i);
|
|
569
|
+
if (modelMatch?.[0]) {
|
|
570
|
+
base.currentModel = formatClaudeModelFromLabel(modelMatch[0]);
|
|
571
|
+
}
|
|
572
|
+
const percentMatch = normalized.match(/(\d+)%\s*used/i);
|
|
573
|
+
if (percentMatch?.[1] && currentHeading) {
|
|
574
|
+
const used = parseInt(percentMatch[1], 10);
|
|
575
|
+
lastPercentSection = currentHeading;
|
|
576
|
+
lastPercentUsed = used;
|
|
577
|
+
if (currentHeading.section === "current-week-all") {
|
|
578
|
+
base.weeklyPercentUsed = keepLargest(base.weeklyPercentUsed, used);
|
|
579
|
+
}
|
|
580
|
+
if (currentHeading.section === "current-session") {
|
|
581
|
+
base.sessionPercentUsed = used;
|
|
582
|
+
base.currentModel = base.currentModel || "claude";
|
|
583
|
+
}
|
|
584
|
+
const scope = currentHeading.section === "current-session" ? "session" : currentHeading.section === "current-week-model" ? currentHeading.modelScope || "unknown" : "global";
|
|
585
|
+
const period = currentHeading.section === "current-session" ? "session" : "weekly";
|
|
586
|
+
base.rateLimits.push({ scope, period, percentUsed: used, resetInfo: null, nextResetAt: null });
|
|
587
|
+
}
|
|
588
|
+
const resetMatch = line.match(/Rese\w*\s+(.+?)$/i);
|
|
589
|
+
if (resetMatch?.[1] && lastPercentSection) {
|
|
590
|
+
const resetText = resetMatch[1].trim();
|
|
591
|
+
const nextResetAt = parseResetDateFromText(resetText);
|
|
592
|
+
if (lastPercentSection.section === "current-week-all") {
|
|
593
|
+
base.resetInfo = `Current week resets ${resetText}`;
|
|
594
|
+
base.nextResetAt = nextResetAt || base.nextResetAt;
|
|
595
|
+
}
|
|
596
|
+
if (lastPercentSection.section === "current-session") {
|
|
597
|
+
base.sessionResetInfo = `Resets ${resetText}`;
|
|
598
|
+
}
|
|
599
|
+
const last = base.rateLimits[base.rateLimits.length - 1];
|
|
600
|
+
if (last) {
|
|
601
|
+
last.resetInfo = `Resets ${resetText}`;
|
|
602
|
+
last.nextResetAt = nextResetAt;
|
|
603
|
+
}
|
|
604
|
+
lastPercentSection = null;
|
|
605
|
+
lastPercentUsed = null;
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
const allModelsLine = lines.find((line) => parseClaudeUsageHeading(line)?.section === "current-week-all");
|
|
609
|
+
if (!base.currentModel && allModelsLine) {
|
|
610
|
+
const modelName = allModelsLine.match(/\b(opus|sonnet|haiku)\s*\d+(?:\.\d+)?(?:\s*-\d+)?/i)?.[0];
|
|
611
|
+
if (modelName) base.currentModel = formatClaudeModelFromLabel(modelName);
|
|
612
|
+
}
|
|
613
|
+
return base;
|
|
614
|
+
}
|
|
615
|
+
function parseCodexUsageFromStatus(raw) {
|
|
616
|
+
const base = initSnapshot(raw);
|
|
617
|
+
base.weeklyPercentUsed = null;
|
|
618
|
+
base.weeklyLimitEstimate = null;
|
|
619
|
+
const lines = raw.split(/[\r\n]+/).map((line) => line.trim()).filter(Boolean);
|
|
620
|
+
let limitScope = "global";
|
|
621
|
+
for (const line of lines) {
|
|
622
|
+
const normalizedLine = line.replace(/[│]/g, " ").trim();
|
|
623
|
+
if (!normalizedLine) continue;
|
|
624
|
+
if (/^[─╭╰]+$/.test(normalizedLine) || /^╮|^╯/.test(normalizedLine)) continue;
|
|
625
|
+
const versionMatch = normalizedLine.match(/(?:OpenAI\s+)?Codex\s+\(v([^)]+)\)/i);
|
|
626
|
+
if (versionMatch?.[1] && !base.version) {
|
|
627
|
+
base.version = versionMatch[1];
|
|
628
|
+
continue;
|
|
629
|
+
}
|
|
630
|
+
const modelDetailMatch = normalizedLine.match(
|
|
631
|
+
/^Model:\s+([a-z0-9][a-z0-9._\-\/]+)\s+\(([^)]+)\)/i
|
|
632
|
+
);
|
|
633
|
+
if (modelDetailMatch?.[1]) {
|
|
634
|
+
base.currentModel = modelDetailMatch[1];
|
|
635
|
+
const reasoningMatch = modelDetailMatch[2].match(/reasoning\s+(\w+)/i);
|
|
636
|
+
if (reasoningMatch?.[1]) {
|
|
637
|
+
base.effort = reasoningMatch[1].toLowerCase();
|
|
638
|
+
}
|
|
639
|
+
continue;
|
|
640
|
+
}
|
|
641
|
+
const modelLineMatch = normalizedLine.match(/^model:\s+([a-z0-9][a-z0-9._\-\/]+)(?:\s+(high|medium|low|extra\s*high))?/i);
|
|
642
|
+
if (modelLineMatch?.[1]) {
|
|
643
|
+
base.currentModel = modelLineMatch[1];
|
|
644
|
+
if (modelLineMatch[2]) {
|
|
645
|
+
base.effort = modelLineMatch[2].toLowerCase();
|
|
646
|
+
}
|
|
647
|
+
continue;
|
|
648
|
+
}
|
|
649
|
+
const accountMatch = normalizedLine.match(/^Account:\s+(\S+@\S+)\s+\((\w+)\)/i);
|
|
650
|
+
if (accountMatch) {
|
|
651
|
+
base.account = accountMatch[1];
|
|
652
|
+
base.plan = accountMatch[2];
|
|
653
|
+
continue;
|
|
654
|
+
}
|
|
655
|
+
if (/context window:/i.test(normalizedLine)) continue;
|
|
656
|
+
if (/weekly limit:/i.test(normalizedLine)) {
|
|
657
|
+
const leftMatch = normalizedLine.match(/(\d+(?:\.\d+)?)\s*%\s*left/i);
|
|
658
|
+
const leftPct = leftMatch?.[1] ? parsePercent(leftMatch[1]) : null;
|
|
659
|
+
const used = leftPct !== null ? Math.floor(100 - leftPct) : null;
|
|
660
|
+
const resetMatch = normalizedLine.match(/resets\s+([0-9]{1,2}:\d{2})(?:\s+on\s+(.+))?/i);
|
|
661
|
+
const resetTime = resetMatch?.[1] || "";
|
|
662
|
+
const resetDate = resetMatch?.[2]?.trim() || "";
|
|
663
|
+
const resetText = resetTime ? `${resetTime}${resetDate ? ` on ${resetDate}` : ""}` : null;
|
|
664
|
+
const nextResetAt = resetText ? parseResetDateFromText(resetText) : null;
|
|
665
|
+
if (used !== null) {
|
|
666
|
+
if (limitScope === "global") {
|
|
667
|
+
base.weeklyPercentUsed = keepLargest(base.weeklyPercentUsed, used);
|
|
668
|
+
if (resetText) {
|
|
669
|
+
base.resetInfo = `Weekly reset: ${resetText}`;
|
|
670
|
+
base.nextResetAt = nextResetAt || base.nextResetAt;
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
base.rateLimits.push({
|
|
674
|
+
scope: limitScope,
|
|
675
|
+
period: "weekly",
|
|
676
|
+
percentUsed: used,
|
|
677
|
+
resetInfo: resetText ? `Resets ${resetText}` : null,
|
|
678
|
+
nextResetAt
|
|
679
|
+
});
|
|
680
|
+
}
|
|
681
|
+
continue;
|
|
682
|
+
}
|
|
683
|
+
if (/5h limit:/i.test(normalizedLine)) {
|
|
684
|
+
const leftMatch = normalizedLine.match(/(\d+(?:\.\d+)?)\s*%\s*left/i);
|
|
685
|
+
const leftPct = leftMatch?.[1] ? parsePercent(leftMatch[1]) : null;
|
|
686
|
+
const used = leftPct !== null ? Math.floor(100 - leftPct) : null;
|
|
687
|
+
const resetMatch = normalizedLine.match(/resets\s+([0-9]{1,2}:\d{2})/i);
|
|
688
|
+
const resetText = resetMatch?.[1] || null;
|
|
689
|
+
const nextResetAt = resetText ? parseResetDateFromText(resetText) : null;
|
|
690
|
+
const usedTokenMatch = normalizedLine.match(/([0-9]+(?:\.[0-9]+)?[kKmMbBtT]?)\s+used/i);
|
|
691
|
+
if (usedTokenMatch?.[1]) {
|
|
692
|
+
const tokens = parseTokenQuantity(usedTokenMatch[1]);
|
|
693
|
+
base.last5HoursInputTokens = keepLargest(base.last5HoursInputTokens, tokens);
|
|
694
|
+
}
|
|
695
|
+
if (used !== null) {
|
|
696
|
+
base.rateLimits.push({
|
|
697
|
+
scope: limitScope,
|
|
698
|
+
period: "5h",
|
|
699
|
+
percentUsed: used,
|
|
700
|
+
resetInfo: resetText ? `Resets ${resetText}` : null,
|
|
701
|
+
nextResetAt
|
|
702
|
+
});
|
|
703
|
+
}
|
|
704
|
+
continue;
|
|
705
|
+
}
|
|
706
|
+
const modelLimitHeader = normalizedLine.match(/^([a-z0-9][a-z0-9._\-\/]+)\s+limit:/i);
|
|
707
|
+
if (modelLimitHeader?.[1] && !/^(5h|weekly)\b/i.test(normalizedLine)) {
|
|
708
|
+
limitScope = modelLimitHeader[1].toLowerCase();
|
|
709
|
+
if (!base.currentModel) base.currentModel = limitScope;
|
|
710
|
+
continue;
|
|
711
|
+
}
|
|
712
|
+
const usedMatch = normalizedLine.match(/([0-9]+(?:\.[0-9]+)?[kKmMbBtT]?)\s+used/i);
|
|
713
|
+
if (usedMatch?.[1] && /\b5h\b/i.test(normalizedLine)) {
|
|
714
|
+
const tokens = parseTokenQuantity(usedMatch[1]);
|
|
715
|
+
if (tokens > 0) {
|
|
716
|
+
base.last5HoursInputTokens = keepLargest(base.last5HoursInputTokens, tokens);
|
|
717
|
+
if (!base.currentModel && limitScope !== "global") {
|
|
718
|
+
base.currentModel = limitScope;
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
if (normalizedLine.length > 80) {
|
|
723
|
+
if (base.weeklyPercentUsed === null) {
|
|
724
|
+
const statusLeftMatch = normalizedLine.match(/(\d+)%\s*left/i);
|
|
725
|
+
if (statusLeftMatch?.[1]) {
|
|
726
|
+
const leftPct = parsePercent(statusLeftMatch[1]);
|
|
727
|
+
if (leftPct !== null) {
|
|
728
|
+
base.weeklyPercentUsed = Math.floor(100 - leftPct);
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
const fiveHMatch = normalizedLine.match(/5h\s+(\d+)%/i);
|
|
733
|
+
if (fiveHMatch?.[1] && base.rateLimits.length === 0) {
|
|
734
|
+
const leftPct = parsePercent(fiveHMatch[1]);
|
|
735
|
+
if (leftPct !== null) {
|
|
736
|
+
base.rateLimits.push({
|
|
737
|
+
scope: "global",
|
|
738
|
+
period: "5h",
|
|
739
|
+
percentUsed: Math.floor(100 - leftPct),
|
|
740
|
+
resetInfo: null,
|
|
741
|
+
nextResetAt: null
|
|
742
|
+
});
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
return base;
|
|
748
|
+
}
|
|
749
|
+
function parseGeminiModelUsageLine(line) {
|
|
750
|
+
const match = line.match(
|
|
751
|
+
/\b(gemini-[a-z0-9][a-z0-9._-]*)\b.*?(\d{1,3})\s*%\s+([0-9]{1,2}:\d{2}(?:\s*(?:AM|PM|am|pm))?)\s*(?:\(([^)]+)\))?/
|
|
752
|
+
);
|
|
753
|
+
if (!match) {
|
|
754
|
+
return { model: null, percentUsed: null, resetAt: null };
|
|
755
|
+
}
|
|
756
|
+
const model = match[1];
|
|
757
|
+
const percentUsed = parsePercent(match[2]);
|
|
758
|
+
if (percentUsed === null) return { model: null, percentUsed: null, resetAt: null };
|
|
759
|
+
const resetTime = match[3]?.trim() ?? "";
|
|
760
|
+
const resetIn = match[4]?.trim() ?? "";
|
|
761
|
+
const resetAt = resetIn ? `${resetTime} (${resetIn})` : resetTime;
|
|
762
|
+
return { model, percentUsed, resetAt: resetAt || null };
|
|
763
|
+
}
|
|
764
|
+
function parseGeminiUsageFromStatus(raw) {
|
|
765
|
+
const base = initSnapshot(raw);
|
|
766
|
+
base.weeklyPercentUsed = null;
|
|
767
|
+
base.weeklyLimitEstimate = null;
|
|
768
|
+
if (!raw) return base;
|
|
769
|
+
const lines = raw.split(/[\r\n]+/).map((line) => line.trim()).filter(Boolean);
|
|
770
|
+
for (const line of lines) {
|
|
771
|
+
const normalizedLine = line.replace(/[│]/g, " ").trim();
|
|
772
|
+
if (!normalizedLine) continue;
|
|
773
|
+
if (/^[─╭╰]+$/.test(normalizedLine) || /^╮|^╯/.test(normalizedLine)) {
|
|
774
|
+
continue;
|
|
775
|
+
}
|
|
776
|
+
const versionMatch = normalizedLine.match(/Gemini CLI v(\d+\.\d+\.\d+)/i);
|
|
777
|
+
if (versionMatch?.[1] && !base.version) {
|
|
778
|
+
base.version = versionMatch[1];
|
|
779
|
+
continue;
|
|
780
|
+
}
|
|
781
|
+
const accountMatch = normalizedLine.match(/Signed in with Google:\s+(\S+@\S+)/i);
|
|
782
|
+
if (accountMatch?.[1]) {
|
|
783
|
+
base.account = accountMatch[1].replace(/\s*\/auth$/, "");
|
|
784
|
+
continue;
|
|
785
|
+
}
|
|
786
|
+
const planMatch = normalizedLine.match(/^Plan:\s+(.+?)(?:\s+\/\w+)?$/i);
|
|
787
|
+
if (planMatch?.[1]) {
|
|
788
|
+
base.plan = planMatch[1].trim();
|
|
789
|
+
continue;
|
|
790
|
+
}
|
|
791
|
+
const tierMatch = normalizedLine.match(/^Tier:\s+(.+)/i);
|
|
792
|
+
if (tierMatch?.[1] && !base.plan) {
|
|
793
|
+
base.plan = tierMatch[1].trim();
|
|
794
|
+
continue;
|
|
795
|
+
}
|
|
796
|
+
if (normalizedLine.startsWith("Model")) continue;
|
|
797
|
+
const modelUsage = parseGeminiModelUsageLine(normalizedLine);
|
|
798
|
+
if (modelUsage.model && modelUsage.percentUsed !== null) {
|
|
799
|
+
if (!base.currentModel) {
|
|
800
|
+
base.currentModel = modelUsage.model;
|
|
801
|
+
}
|
|
802
|
+
base.weeklyPercentUsed = keepLargest(base.weeklyPercentUsed, modelUsage.percentUsed);
|
|
803
|
+
const nextResetAt = modelUsage.resetAt ? parseResetDateFromText(modelUsage.resetAt) : null;
|
|
804
|
+
if (modelUsage.resetAt) {
|
|
805
|
+
base.resetInfo = `Usage reset: ${modelUsage.resetAt}`;
|
|
806
|
+
base.nextResetAt = nextResetAt || base.nextResetAt;
|
|
807
|
+
}
|
|
808
|
+
base.rateLimits.push({
|
|
809
|
+
scope: modelUsage.model,
|
|
810
|
+
period: "daily",
|
|
811
|
+
percentUsed: modelUsage.percentUsed,
|
|
812
|
+
resetInfo: modelUsage.resetAt ? `Resets ${modelUsage.resetAt}` : null,
|
|
813
|
+
nextResetAt
|
|
814
|
+
});
|
|
815
|
+
continue;
|
|
816
|
+
}
|
|
817
|
+
if (/Session Stats/i.test(normalizedLine)) {
|
|
818
|
+
base.resetInfo = base.resetInfo || "Gemini session stats unavailable";
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
return base;
|
|
822
|
+
}
|
|
823
|
+
function collectProviderUsageSnapshotFromCli(command, usageCommand, parseSnapshot, args = []) {
|
|
824
|
+
return collectProviderStatusText(command, args, usageCommand).then((raw) => {
|
|
825
|
+
if (!raw) return null;
|
|
826
|
+
return parseSnapshot(raw);
|
|
827
|
+
});
|
|
828
|
+
}
|
|
829
|
+
|
|
312
830
|
// src/agents/adapters/claude.ts
|
|
831
|
+
var CLAUDE_USAGE_COMMAND = "/usage";
|
|
832
|
+
var collectClaudeUsageFromCli = () => collectProviderUsageSnapshotFromCli("claude", CLAUDE_USAGE_COMMAND, parseClaudeUsageFromStatus, [
|
|
833
|
+
"--dangerously-skip-permissions"
|
|
834
|
+
]);
|
|
313
835
|
function buildClaudeCommand(options) {
|
|
314
|
-
const parts = ["claude", "--print"];
|
|
836
|
+
const parts = ["claude", "--print", "--bare"];
|
|
315
837
|
if (options.readOnly) {
|
|
316
838
|
parts.push("--permission-mode plan");
|
|
317
839
|
} else if (!options.noToolAccess) {
|
|
@@ -372,20 +894,20 @@ async function compile(issue, provider, plan, config, workspacePath, skillContex
|
|
|
372
894
|
readOnly: isReadOnlyRole,
|
|
373
895
|
maxBudgetUsd: config.maxBudgetUsd
|
|
374
896
|
});
|
|
375
|
-
const
|
|
897
|
+
const env3 = {
|
|
376
898
|
FIFONY_PLAN_COMPLEXITY: plan.estimatedComplexity,
|
|
377
899
|
FIFONY_PLAN_STEPS: String(plan.steps.length),
|
|
378
900
|
FIFONY_EXECUTION_PAYLOAD_FILE: "fifony-execution-payload.json"
|
|
379
901
|
};
|
|
380
|
-
if (plan.suggestedPaths?.length)
|
|
902
|
+
if (plan.suggestedPaths?.length) env3.FIFONY_PLAN_PATHS = plan.suggestedPaths.join(",");
|
|
381
903
|
if (plan.suggestedSkills?.length) {
|
|
382
|
-
|
|
904
|
+
env3.FIFONY_PLAN_SKILLS = plan.suggestedSkills.join(",");
|
|
383
905
|
}
|
|
384
906
|
const { pre, post } = extractValidationCommands(plan);
|
|
385
907
|
return {
|
|
386
908
|
prompt,
|
|
387
909
|
command,
|
|
388
|
-
env:
|
|
910
|
+
env: env3,
|
|
389
911
|
preHooks: pre,
|
|
390
912
|
postHooks: post,
|
|
391
913
|
outputSchema: CLAUDE_RESULT_SCHEMA,
|
|
@@ -415,6 +937,10 @@ var claudeAdapter = {
|
|
|
415
937
|
// src/agents/adapters/codex.ts
|
|
416
938
|
import { existsSync as existsSync3 } from "fs";
|
|
417
939
|
import { join as join2 } from "path";
|
|
940
|
+
var CODEX_USAGE_COMMAND = "/status";
|
|
941
|
+
var collectCodexUsageFromCli = () => collectProviderUsageSnapshotFromCli("codex", CODEX_USAGE_COMMAND, parseCodexUsageFromStatus, [
|
|
942
|
+
"--dangerously-bypass-approvals-and-sandbox"
|
|
943
|
+
]);
|
|
418
944
|
var CODEX_RESULT_CONTRACT = `
|
|
419
945
|
Return a JSON object with this exact schema when finished:
|
|
420
946
|
{
|
|
@@ -429,7 +955,13 @@ Return a JSON object with this exact schema when finished:
|
|
|
429
955
|
}
|
|
430
956
|
`.trim();
|
|
431
957
|
function buildCodexCommand(options) {
|
|
432
|
-
const parts = [
|
|
958
|
+
const parts = [
|
|
959
|
+
"codex",
|
|
960
|
+
"exec",
|
|
961
|
+
"--skip-git-repo-check",
|
|
962
|
+
"--dangerously-bypass-approvals-and-sandbox",
|
|
963
|
+
"--no-alt-screen"
|
|
964
|
+
];
|
|
433
965
|
if (options.model && options.model !== "codex") {
|
|
434
966
|
parts.push(`--model ${options.model}`);
|
|
435
967
|
}
|
|
@@ -484,18 +1016,18 @@ async function compile2(issue, provider, plan, config, workspacePath, skillConte
|
|
|
484
1016
|
effort,
|
|
485
1017
|
imagePaths: issue.images?.filter((p) => existsSync3(p))
|
|
486
1018
|
});
|
|
487
|
-
const
|
|
1019
|
+
const env3 = {
|
|
488
1020
|
FIFONY_PLAN_COMPLEXITY: plan.estimatedComplexity,
|
|
489
1021
|
FIFONY_PLAN_STEPS: String(plan.steps.length),
|
|
490
1022
|
FIFONY_PLAN_PHASES: String(plan.phases?.length || 0),
|
|
491
1023
|
FIFONY_EXECUTION_PAYLOAD_FILE: "execution-payload.json"
|
|
492
1024
|
};
|
|
493
|
-
if (plan.suggestedPaths?.length)
|
|
1025
|
+
if (plan.suggestedPaths?.length) env3.FIFONY_PLAN_PATHS = plan.suggestedPaths.join(",");
|
|
494
1026
|
const { pre, post } = extractValidationCommands(plan);
|
|
495
1027
|
return {
|
|
496
1028
|
prompt,
|
|
497
1029
|
command,
|
|
498
|
-
env:
|
|
1030
|
+
env: env3,
|
|
499
1031
|
preHooks: pre,
|
|
500
1032
|
postHooks: post,
|
|
501
1033
|
outputSchema: "",
|
|
@@ -523,6 +1055,8 @@ var codexAdapter = {
|
|
|
523
1055
|
// src/agents/adapters/gemini.ts
|
|
524
1056
|
import { existsSync as existsSync4 } from "fs";
|
|
525
1057
|
import { join as join3 } from "path";
|
|
1058
|
+
var GEMINI_USAGE_COMMAND = "/stats session";
|
|
1059
|
+
var collectGeminiUsageFromCli = () => collectProviderUsageSnapshotFromCli("gemini", GEMINI_USAGE_COMMAND, parseGeminiUsageFromStatus);
|
|
526
1060
|
var GEMINI_RESULT_CONTRACT = `
|
|
527
1061
|
Return a JSON object with this exact schema when finished:
|
|
528
1062
|
{
|
|
@@ -546,6 +1080,7 @@ function buildGeminiCommand(options) {
|
|
|
546
1080
|
if (options.model) {
|
|
547
1081
|
parts.push(`--model ${options.model}`);
|
|
548
1082
|
}
|
|
1083
|
+
parts.push("--screen-reader");
|
|
549
1084
|
parts.push("--output-format json");
|
|
550
1085
|
if (options.addDirs?.length) {
|
|
551
1086
|
parts.push(`--include-directories ${options.addDirs.map((d) => `"${d}"`).join(",")}`);
|
|
@@ -589,18 +1124,18 @@ async function compile3(issue, provider, plan, config, workspacePath, skillConte
|
|
|
589
1124
|
addDirs: absoluteDirs,
|
|
590
1125
|
readOnly: isReadOnlyRole
|
|
591
1126
|
});
|
|
592
|
-
const
|
|
1127
|
+
const env3 = {
|
|
593
1128
|
FIFONY_PLAN_COMPLEXITY: plan.estimatedComplexity,
|
|
594
1129
|
FIFONY_PLAN_STEPS: String(plan.steps.length),
|
|
595
1130
|
FIFONY_PLAN_PHASES: String(plan.phases?.length || 0),
|
|
596
1131
|
FIFONY_EXECUTION_PAYLOAD_FILE: "execution-payload.json"
|
|
597
1132
|
};
|
|
598
|
-
if (plan.suggestedPaths?.length)
|
|
1133
|
+
if (plan.suggestedPaths?.length) env3.FIFONY_PLAN_PATHS = plan.suggestedPaths.join(",");
|
|
599
1134
|
const { pre, post } = extractValidationCommands(plan);
|
|
600
1135
|
return {
|
|
601
1136
|
prompt,
|
|
602
1137
|
command,
|
|
603
|
-
env:
|
|
1138
|
+
env: env3,
|
|
604
1139
|
preHooks: pre,
|
|
605
1140
|
postHooks: post,
|
|
606
1141
|
outputSchema: "",
|
|
@@ -1107,10 +1642,11 @@ async function runHook(command, workspacePath, issue, hookName, extraEnv = {}) {
|
|
|
1107
1642
|
retryDelayMs: 0,
|
|
1108
1643
|
staleInProgressTimeoutMs: 0,
|
|
1109
1644
|
logLinesTail: 12e3,
|
|
1110
|
-
agentProvider: normalizeAgentProvider(
|
|
1645
|
+
agentProvider: normalizeAgentProvider(env2.FIFONY_AGENT_PROVIDER ?? "codex"),
|
|
1111
1646
|
agentCommand: command,
|
|
1112
1647
|
maxTurns: 1,
|
|
1113
|
-
runMode: "filesystem"
|
|
1648
|
+
runMode: "filesystem",
|
|
1649
|
+
autoReviewApproval: true
|
|
1114
1650
|
}, "", "", { FIFONY_HOOK_NAME: hookName, ...extraEnv });
|
|
1115
1651
|
if (!result.success) {
|
|
1116
1652
|
throw new Error(`${hookName} hook failed: ${result.output}`);
|
|
@@ -1550,7 +2086,7 @@ function parseDiffStats(issue, raw) {
|
|
|
1550
2086
|
}
|
|
1551
2087
|
async function syncIssueDiffStatsToStore(issue) {
|
|
1552
2088
|
if (!issue?.id) return;
|
|
1553
|
-
const { getIssueStateResource } = await import("./store-
|
|
2089
|
+
const { getIssueStateResource } = await import("./store-FFHHRNVI.js");
|
|
1554
2090
|
const issueResource = getIssueStateResource();
|
|
1555
2091
|
if (!issueResource) return;
|
|
1556
2092
|
const toNumber = (value) => {
|
|
@@ -1773,6 +2309,9 @@ function writeVersionedArtifacts(workspacePath, prefix, planVersion, attempt, so
|
|
|
1773
2309
|
export {
|
|
1774
2310
|
buildFullPlanPrompt,
|
|
1775
2311
|
buildExecutionPayload,
|
|
2312
|
+
collectClaudeUsageFromCli,
|
|
2313
|
+
collectCodexUsageFromCli,
|
|
2314
|
+
collectGeminiUsageFromCli,
|
|
1776
2315
|
ADAPTERS,
|
|
1777
2316
|
discoverModels,
|
|
1778
2317
|
normalizeAgentProvider,
|
|
@@ -1811,4 +2350,4 @@ export {
|
|
|
1811
2350
|
hydrateIssuePathsFromWorkspace,
|
|
1812
2351
|
writeVersionedArtifacts
|
|
1813
2352
|
};
|
|
1814
|
-
//# sourceMappingURL=chunk-
|
|
2353
|
+
//# sourceMappingURL=chunk-2D5P75F6.js.map
|