fifony 0.1.30 → 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-FPUYBJZD.js → agent-3NYJEHH5.js} +5 -5
- package/dist/{chunk-XVF6GOVS.js → chunk-2D5P75F6.js} +556 -17
- package/dist/{chunk-3FCJI2GK.js → chunk-FAFGDK62.js} +679 -108
- package/dist/{chunk-VOQT7RVT.js → chunk-FPUTP743.js} +11 -11
- package/dist/{chunk-2G6SRDOC.js → chunk-H2QRC6UQ.js} +8 -17
- package/dist/cli.js +4 -4
- package/dist/{issue-runner-MRHO5ZAB.js → issue-runner-ZNDKLOCZ.js} +5 -5
- package/dist/{issue-state-machine-V2KPUYPW.js → issue-state-machine-NBSYRDZW.js} +3 -3
- package/dist/{issues-3PUMY63N.js → issues-GBWLDBVU.js} +5 -5
- package/dist/{queue-workers-EGHCDDLB.js → queue-workers-OHPJKAPM.js} +2 -2
- package/dist/{scheduler-V4GMCBTE.js → scheduler-OU35EYUH.js} +5 -5
- package/dist/{store-RVKQ6UEY.js → store-FFHHRNVI.js} +5 -5
- package/dist/{workspace-KEHFITYR.js → workspace-5XOCMZ57.js} +2 -2
- package/package.json +4 -1
- package/app/dist/assets/KeyboardShortcutsHelp-BRP7sv3N.js +0 -1
- package/app/dist/assets/OnboardingWizard-D4VMCLzT.js +0 -1
- package/app/dist/assets/analytics.lazy-NMe65I_6.js +0 -1
- package/app/dist/assets/index-BsgIa3RC.js +0 -45
- package/app/dist/assets/rolldown-runtime-DF2fYuay.js +0 -1
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
snapshotAndClearDirtyEventIds,
|
|
22
22
|
snapshotAndClearDirtyIssueIds,
|
|
23
23
|
snapshotAndClearDirtyIssuePlanIds
|
|
24
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-H2QRC6UQ.js";
|
|
25
25
|
import {
|
|
26
26
|
ADAPTERS,
|
|
27
27
|
assertIssueHasGitWorktree,
|
|
@@ -31,6 +31,9 @@ import {
|
|
|
31
31
|
buildRetryContext,
|
|
32
32
|
buildTurnPrompt,
|
|
33
33
|
cleanWorkspace,
|
|
34
|
+
collectClaudeUsageFromCli,
|
|
35
|
+
collectCodexUsageFromCli,
|
|
36
|
+
collectGeminiUsageFromCli,
|
|
34
37
|
computeDiffStats,
|
|
35
38
|
detectAvailableProviders,
|
|
36
39
|
discoverModels,
|
|
@@ -49,7 +52,7 @@ import {
|
|
|
49
52
|
resolveAgentCommand,
|
|
50
53
|
runCommandWithTimeout,
|
|
51
54
|
runHook
|
|
52
|
-
} from "./chunk-
|
|
55
|
+
} from "./chunk-2D5P75F6.js";
|
|
53
56
|
import {
|
|
54
57
|
appendFileTail,
|
|
55
58
|
clamp,
|
|
@@ -74,7 +77,7 @@ import {
|
|
|
74
77
|
} from "./chunk-O5AEQXUV.js";
|
|
75
78
|
import {
|
|
76
79
|
enqueue
|
|
77
|
-
} from "./chunk-
|
|
80
|
+
} from "./chunk-FPUTP743.js";
|
|
78
81
|
import {
|
|
79
82
|
logger
|
|
80
83
|
} from "./chunk-DVU3CXWA.js";
|
|
@@ -949,7 +952,34 @@ function loadAgentCatalog() {
|
|
|
949
952
|
return entries;
|
|
950
953
|
}
|
|
951
954
|
function loadSkillCatalog() {
|
|
952
|
-
|
|
955
|
+
const entries = [];
|
|
956
|
+
try {
|
|
957
|
+
const repos = listReferenceRepositories();
|
|
958
|
+
for (const repo of repos) {
|
|
959
|
+
if (!repo.present || !repo.synced) continue;
|
|
960
|
+
const artifacts = collectArtifacts(repo.path, repo.id).filter((a) => a.kind === "skill");
|
|
961
|
+
for (const artifact of artifacts) {
|
|
962
|
+
try {
|
|
963
|
+
const content = readFileSync3(artifact.sourcePath, "utf8");
|
|
964
|
+
const fm = parseFrontmatter(content);
|
|
965
|
+
entries.push({
|
|
966
|
+
name: artifact.targetName,
|
|
967
|
+
displayName: fm.name || artifact.targetName,
|
|
968
|
+
description: fm.description || "",
|
|
969
|
+
domains: fm.domains ? fm.domains.split(",").map((d) => d.trim()).filter(Boolean) : [],
|
|
970
|
+
source: repo.id,
|
|
971
|
+
installType: "bundled",
|
|
972
|
+
content
|
|
973
|
+
});
|
|
974
|
+
} catch (err) {
|
|
975
|
+
logger.warn({ err, path: artifact.sourcePath }, "Failed to read skill file");
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
} catch (error) {
|
|
980
|
+
logger.error({ err: error }, "Failed to load skill catalog from repositories");
|
|
981
|
+
}
|
|
982
|
+
return entries;
|
|
953
983
|
}
|
|
954
984
|
function filterByDomains(catalog, domains) {
|
|
955
985
|
const domainSet = new Set(domains.map((d) => d.toLowerCase().trim()));
|
|
@@ -2337,6 +2367,11 @@ async function whichExists(cmd) {
|
|
|
2337
2367
|
return false;
|
|
2338
2368
|
}
|
|
2339
2369
|
}
|
|
2370
|
+
var PROVIDER_USAGE_ORDER = ["claude", "codex", "gemini"];
|
|
2371
|
+
function normalizeProviderName(name) {
|
|
2372
|
+
const normalized = (name || "").trim().toLowerCase();
|
|
2373
|
+
return PROVIDER_USAGE_ORDER.includes(normalized) ? normalized : null;
|
|
2374
|
+
}
|
|
2340
2375
|
function resolveCodexHomeCandidates() {
|
|
2341
2376
|
const homePaths = /* @__PURE__ */ new Set([
|
|
2342
2377
|
homedir2(),
|
|
@@ -2393,9 +2428,25 @@ function computeTodayStart() {
|
|
|
2393
2428
|
d.setUTCHours(0, 0, 0, 0);
|
|
2394
2429
|
return d;
|
|
2395
2430
|
}
|
|
2431
|
+
function computeLastHoursStart(hours) {
|
|
2432
|
+
return new Date(Date.now() - hours * 60 * 60 * 1e3);
|
|
2433
|
+
}
|
|
2396
2434
|
function makePeriod(input, output, sessions, since) {
|
|
2397
2435
|
return { inputTokens: input, outputTokens: output, tokensUsed: input + output, sessions, since };
|
|
2398
2436
|
}
|
|
2437
|
+
function toNumber(value) {
|
|
2438
|
+
if (typeof value === "number") return Number.isFinite(value) ? value : 0;
|
|
2439
|
+
if (typeof value === "string") {
|
|
2440
|
+
const parsed = Number.parseInt(value, 10);
|
|
2441
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
2442
|
+
}
|
|
2443
|
+
return 0;
|
|
2444
|
+
}
|
|
2445
|
+
function parseTimestamp(value) {
|
|
2446
|
+
if (typeof value !== "string") return 0;
|
|
2447
|
+
const timestamp = Date.parse(value);
|
|
2448
|
+
return Number.isNaN(timestamp) ? 0 : timestamp;
|
|
2449
|
+
}
|
|
2399
2450
|
var CLAUDE_PLAN_LIMITS = {
|
|
2400
2451
|
pro: 45e6,
|
|
2401
2452
|
// ~45M tokens/week (Pro plan estimate)
|
|
@@ -2404,6 +2455,14 @@ var CLAUDE_PLAN_LIMITS = {
|
|
|
2404
2455
|
max5x: 675e6
|
|
2405
2456
|
// ~675M tokens/week (Max 5x plan)
|
|
2406
2457
|
};
|
|
2458
|
+
function resolveClaudePlanKey(displayName) {
|
|
2459
|
+
const lower = displayName.toLowerCase().trim();
|
|
2460
|
+
if (/max\s*5x/i.test(lower)) return "max5x";
|
|
2461
|
+
if (/max/i.test(lower)) return "max";
|
|
2462
|
+
if (/pro/i.test(lower)) return "pro";
|
|
2463
|
+
if (/free/i.test(lower)) return null;
|
|
2464
|
+
return null;
|
|
2465
|
+
}
|
|
2407
2466
|
async function collectClaudeUsage() {
|
|
2408
2467
|
const home = homedir2();
|
|
2409
2468
|
const claudeDir = join8(home, ".claude");
|
|
@@ -2419,10 +2478,15 @@ async function collectClaudeUsage() {
|
|
|
2419
2478
|
let weekInputTokens = 0;
|
|
2420
2479
|
let weekOutputTokens = 0;
|
|
2421
2480
|
let weekSessions = 0;
|
|
2481
|
+
let last5hInputTokens = 0;
|
|
2482
|
+
let last5hOutputTokens = 0;
|
|
2483
|
+
let last5hSessions = 0;
|
|
2422
2484
|
const todayStart = computeTodayStart();
|
|
2423
2485
|
const todayMs = todayStart.getTime();
|
|
2424
2486
|
const weekStart = computeWeekStart();
|
|
2425
2487
|
const weekMs = weekStart.getTime();
|
|
2488
|
+
const last5hStart = computeLastHoursStart(5);
|
|
2489
|
+
const last5hMs = last5hStart.getTime();
|
|
2426
2490
|
if (existsSync6(projectsDir)) {
|
|
2427
2491
|
try {
|
|
2428
2492
|
const projectDirs = readdirSync2(projectsDir, { withFileTypes: true });
|
|
@@ -2446,6 +2510,7 @@ async function collectClaudeUsage() {
|
|
|
2446
2510
|
let sessionCounted = false;
|
|
2447
2511
|
let sessionTodayCounted = false;
|
|
2448
2512
|
let sessionWeekCounted = false;
|
|
2513
|
+
let sessionLast5hCounted = false;
|
|
2449
2514
|
for (const line of content.split("\n")) {
|
|
2450
2515
|
if (!line.trim()) continue;
|
|
2451
2516
|
try {
|
|
@@ -2477,6 +2542,14 @@ async function collectClaudeUsage() {
|
|
|
2477
2542
|
sessionWeekCounted = true;
|
|
2478
2543
|
}
|
|
2479
2544
|
}
|
|
2545
|
+
if (timestamp >= last5hMs) {
|
|
2546
|
+
last5hInputTokens += inputTokens;
|
|
2547
|
+
last5hOutputTokens += outputTokens;
|
|
2548
|
+
if (!sessionLast5hCounted) {
|
|
2549
|
+
last5hSessions++;
|
|
2550
|
+
sessionLast5hCounted = true;
|
|
2551
|
+
}
|
|
2552
|
+
}
|
|
2480
2553
|
} catch {
|
|
2481
2554
|
}
|
|
2482
2555
|
}
|
|
@@ -2487,9 +2560,9 @@ async function collectClaudeUsage() {
|
|
|
2487
2560
|
}
|
|
2488
2561
|
}
|
|
2489
2562
|
const models = [
|
|
2490
|
-
{ slug: "claude-opus-4-6", displayName: "
|
|
2491
|
-
{ slug: "claude-sonnet-4-6", displayName: "
|
|
2492
|
-
{ slug: "claude-haiku-4-5", displayName: "
|
|
2563
|
+
{ slug: "claude-opus-4-6", displayName: "claude opus 4.6", description: "Most capable model for complex tasks" },
|
|
2564
|
+
{ slug: "claude-sonnet-4-6", displayName: "claude sonnet 4.6", description: "Balanced performance and speed" },
|
|
2565
|
+
{ slug: "claude-haiku-4-5", displayName: "claude haiku 4.5", description: "Fast and efficient model" }
|
|
2493
2566
|
];
|
|
2494
2567
|
let plan = "pro";
|
|
2495
2568
|
let resetInfo = "Weekly reset (every Monday 00:00 UTC)";
|
|
@@ -2508,10 +2581,53 @@ async function collectClaudeUsage() {
|
|
|
2508
2581
|
} catch {
|
|
2509
2582
|
}
|
|
2510
2583
|
}
|
|
2511
|
-
|
|
2512
|
-
|
|
2584
|
+
let nextResetAt = computeNextMonday().toISOString();
|
|
2585
|
+
let weeklyLimit = CLAUDE_PLAN_LIMITS[plan] ?? null;
|
|
2513
2586
|
const weeklyUsed = weekInputTokens + weekOutputTokens;
|
|
2514
2587
|
const percentUsed = weeklyLimit ? Math.min(100, Math.round(weeklyUsed / weeklyLimit * 100)) : null;
|
|
2588
|
+
const statusUsage = await collectClaudeUsageFromCli();
|
|
2589
|
+
if (statusUsage) {
|
|
2590
|
+
if (statusUsage.currentModel) {
|
|
2591
|
+
currentModel = statusUsage.currentModel;
|
|
2592
|
+
}
|
|
2593
|
+
if (statusUsage.plan) {
|
|
2594
|
+
const planKey = resolveClaudePlanKey(statusUsage.plan);
|
|
2595
|
+
if (planKey && CLAUDE_PLAN_LIMITS[planKey]) {
|
|
2596
|
+
plan = planKey;
|
|
2597
|
+
weeklyLimit = CLAUDE_PLAN_LIMITS[planKey];
|
|
2598
|
+
}
|
|
2599
|
+
}
|
|
2600
|
+
if (statusUsage.weeklyLimitEstimate !== null) {
|
|
2601
|
+
weeklyLimit = statusUsage.weeklyLimitEstimate;
|
|
2602
|
+
}
|
|
2603
|
+
if (statusUsage.thisWeekInputTokens !== null || statusUsage.thisWeekOutputTokens !== null) {
|
|
2604
|
+
weekInputTokens = statusUsage.thisWeekInputTokens ?? weekInputTokens;
|
|
2605
|
+
weekOutputTokens = statusUsage.thisWeekOutputTokens ?? weekOutputTokens;
|
|
2606
|
+
if (statusUsage.thisWeekSessions !== null) weekSessions = statusUsage.thisWeekSessions;
|
|
2607
|
+
}
|
|
2608
|
+
if (statusUsage.todayInputTokens !== null || statusUsage.todayOutputTokens !== null) {
|
|
2609
|
+
todayInputTokens = statusUsage.todayInputTokens ?? todayInputTokens;
|
|
2610
|
+
todayOutputTokens = statusUsage.todayOutputTokens ?? todayOutputTokens;
|
|
2611
|
+
if (statusUsage.todaySessions !== null) todaySessions = statusUsage.todaySessions;
|
|
2612
|
+
}
|
|
2613
|
+
if (statusUsage.last5HoursInputTokens !== null || statusUsage.last5HoursOutputTokens !== null) {
|
|
2614
|
+
last5hInputTokens = statusUsage.last5HoursInputTokens ?? last5hInputTokens;
|
|
2615
|
+
last5hOutputTokens = statusUsage.last5HoursOutputTokens ?? last5hOutputTokens;
|
|
2616
|
+
if (statusUsage.last5HoursSessions !== null) last5hSessions = statusUsage.last5HoursSessions;
|
|
2617
|
+
}
|
|
2618
|
+
if (statusUsage.allTimeInputTokens !== null || statusUsage.allTimeOutputTokens !== null) {
|
|
2619
|
+
totalInputTokens = statusUsage.allTimeInputTokens ?? totalInputTokens;
|
|
2620
|
+
totalOutputTokens = statusUsage.allTimeOutputTokens ?? totalOutputTokens;
|
|
2621
|
+
if (statusUsage.allTimeSessions !== null) totalSessions = statusUsage.allTimeSessions;
|
|
2622
|
+
}
|
|
2623
|
+
if (statusUsage.resetInfo) {
|
|
2624
|
+
resetInfo = statusUsage.resetInfo;
|
|
2625
|
+
}
|
|
2626
|
+
if (statusUsage.nextResetAt) {
|
|
2627
|
+
nextResetAt = statusUsage.nextResetAt;
|
|
2628
|
+
}
|
|
2629
|
+
}
|
|
2630
|
+
const finalPercentUsed = weeklyLimit ? Math.min(100, Math.round((weekInputTokens + weekOutputTokens) / weeklyLimit * 100)) : percentUsed;
|
|
2515
2631
|
return {
|
|
2516
2632
|
name: "claude",
|
|
2517
2633
|
available,
|
|
@@ -2520,14 +2636,95 @@ async function collectClaudeUsage() {
|
|
|
2520
2636
|
usage: {
|
|
2521
2637
|
today: makePeriod(todayInputTokens, todayOutputTokens, todaySessions, todayStart.toISOString()),
|
|
2522
2638
|
thisWeek: makePeriod(weekInputTokens, weekOutputTokens, weekSessions, weekStart.toISOString()),
|
|
2639
|
+
last5Hours: makePeriod(last5hInputTokens, last5hOutputTokens, last5hSessions, last5hStart.toISOString()),
|
|
2523
2640
|
allTime: makePeriod(totalInputTokens, totalOutputTokens, totalSessions, "")
|
|
2524
2641
|
},
|
|
2525
2642
|
resetInfo,
|
|
2526
2643
|
nextResetAt,
|
|
2527
2644
|
weeklyLimitEstimate: weeklyLimit,
|
|
2528
|
-
percentUsed
|
|
2645
|
+
percentUsed: finalPercentUsed,
|
|
2646
|
+
version: statusUsage?.version ?? null,
|
|
2647
|
+
plan: statusUsage?.plan ?? (plan !== "pro" ? plan.toUpperCase() : "Pro"),
|
|
2648
|
+
account: statusUsage?.account ?? null,
|
|
2649
|
+
effort: statusUsage?.effort ?? null,
|
|
2650
|
+
rateLimits: statusUsage?.rateLimits ?? []
|
|
2651
|
+
};
|
|
2652
|
+
}
|
|
2653
|
+
function aggregateCodexSessionUsageFromJsonl(lines) {
|
|
2654
|
+
let maxInput = 0;
|
|
2655
|
+
let maxOutput = 0;
|
|
2656
|
+
let maxTotal = 0;
|
|
2657
|
+
let sessionTs = 0;
|
|
2658
|
+
for (const line of lines) {
|
|
2659
|
+
if (!line.trim()) continue;
|
|
2660
|
+
let entry;
|
|
2661
|
+
try {
|
|
2662
|
+
entry = JSON.parse(line);
|
|
2663
|
+
} catch {
|
|
2664
|
+
continue;
|
|
2665
|
+
}
|
|
2666
|
+
const lineTs = parseTimestamp(entry?.timestamp);
|
|
2667
|
+
const payloadTs = parseTimestamp(entry?.payload?.timestamp);
|
|
2668
|
+
const candidateTs = Math.max(lineTs, payloadTs);
|
|
2669
|
+
if (candidateTs > sessionTs) sessionTs = candidateTs;
|
|
2670
|
+
if (entry.type !== "event_msg") continue;
|
|
2671
|
+
if (entry.payload?.type !== "token_count") continue;
|
|
2672
|
+
const info = entry.payload?.info || {};
|
|
2673
|
+
const usage = info.total_token_usage || info.last_token_usage;
|
|
2674
|
+
if (!usage || typeof usage !== "object") continue;
|
|
2675
|
+
const input = toNumber(usage.input_tokens);
|
|
2676
|
+
const output = toNumber(usage.output_tokens);
|
|
2677
|
+
const total = toNumber(usage.total_tokens);
|
|
2678
|
+
if (total <= 0) continue;
|
|
2679
|
+
if (total > maxTotal) {
|
|
2680
|
+
maxTotal = total;
|
|
2681
|
+
maxInput = input;
|
|
2682
|
+
maxOutput = output;
|
|
2683
|
+
}
|
|
2684
|
+
}
|
|
2685
|
+
if (maxTotal <= 0) return { inputTokens: 0, outputTokens: 0, totalTokens: 0, timestampMs: sessionTs };
|
|
2686
|
+
return {
|
|
2687
|
+
inputTokens: maxInput,
|
|
2688
|
+
outputTokens: maxOutput,
|
|
2689
|
+
totalTokens: maxTotal,
|
|
2690
|
+
timestampMs: sessionTs
|
|
2529
2691
|
};
|
|
2530
2692
|
}
|
|
2693
|
+
function collectCodexSessionUsagesFromJsonl(codexDir) {
|
|
2694
|
+
const sessionsDir = join8(codexDir, "sessions");
|
|
2695
|
+
if (!existsSync6(sessionsDir)) return [];
|
|
2696
|
+
const stack = [sessionsDir];
|
|
2697
|
+
const usageByFile = [];
|
|
2698
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2699
|
+
while (stack.length > 0) {
|
|
2700
|
+
const current = stack.pop();
|
|
2701
|
+
if (seen.has(current)) continue;
|
|
2702
|
+
seen.add(current);
|
|
2703
|
+
let entries = [];
|
|
2704
|
+
try {
|
|
2705
|
+
entries = readdirSync2(current, { withFileTypes: true });
|
|
2706
|
+
} catch {
|
|
2707
|
+
continue;
|
|
2708
|
+
}
|
|
2709
|
+
for (const entry of entries) {
|
|
2710
|
+
const next = join8(current, entry.name);
|
|
2711
|
+
if (entry.isDirectory()) {
|
|
2712
|
+
stack.push(next);
|
|
2713
|
+
continue;
|
|
2714
|
+
}
|
|
2715
|
+
if (!entry.isFile() || !entry.name.endsWith(".jsonl")) continue;
|
|
2716
|
+
try {
|
|
2717
|
+
const content = readFileSync5(next, "utf8");
|
|
2718
|
+
const usage = aggregateCodexSessionUsageFromJsonl(content.split("\n"));
|
|
2719
|
+
if (usage.totalTokens > 0) {
|
|
2720
|
+
usageByFile.push(usage);
|
|
2721
|
+
}
|
|
2722
|
+
} catch {
|
|
2723
|
+
}
|
|
2724
|
+
}
|
|
2725
|
+
}
|
|
2726
|
+
return usageByFile;
|
|
2727
|
+
}
|
|
2531
2728
|
async function collectCodexUsage() {
|
|
2532
2729
|
const codexDir = resolveCodexDir();
|
|
2533
2730
|
if (!codexDir) return null;
|
|
@@ -2541,7 +2738,7 @@ async function collectCodexUsage() {
|
|
|
2541
2738
|
for (const m of cache.models || []) {
|
|
2542
2739
|
models.push({
|
|
2543
2740
|
slug: m.slug,
|
|
2544
|
-
displayName: m.display_name || m.slug,
|
|
2741
|
+
displayName: (m.display_name || m.slug).toLowerCase(),
|
|
2545
2742
|
description: (m.description || "").slice(0, 80)
|
|
2546
2743
|
});
|
|
2547
2744
|
}
|
|
@@ -2559,60 +2756,199 @@ async function collectCodexUsage() {
|
|
|
2559
2756
|
}
|
|
2560
2757
|
const todayStart = computeTodayStart();
|
|
2561
2758
|
const weekStart = computeWeekStart();
|
|
2562
|
-
|
|
2759
|
+
let nextResetAt = computeNextMonday().toISOString();
|
|
2760
|
+
let resetInfo = "Weekly rate limit resets every Monday";
|
|
2761
|
+
const last5hStart = computeLastHoursStart(5);
|
|
2762
|
+
const last5hMs = last5hStart.getTime();
|
|
2763
|
+
const todayMs = todayStart.getTime();
|
|
2764
|
+
const weekMs = weekStart.getTime();
|
|
2563
2765
|
const dbPath = findLatestCodexDb(codexDir);
|
|
2564
|
-
if (!dbPath) {
|
|
2565
|
-
return {
|
|
2566
|
-
name: "codex",
|
|
2567
|
-
available,
|
|
2568
|
-
models,
|
|
2569
|
-
currentModel,
|
|
2570
|
-
usage: {
|
|
2571
|
-
today: makePeriod(0, 0, 0, todayStart.toISOString()),
|
|
2572
|
-
thisWeek: makePeriod(0, 0, 0, weekStart.toISOString()),
|
|
2573
|
-
allTime: makePeriod(0, 0, 0, "")
|
|
2574
|
-
},
|
|
2575
|
-
resetInfo: "Weekly rate limit resets every Monday",
|
|
2576
|
-
nextResetAt,
|
|
2577
|
-
weeklyLimitEstimate: null,
|
|
2578
|
-
percentUsed: null
|
|
2579
|
-
};
|
|
2580
|
-
}
|
|
2581
2766
|
let allTimeTokens = 0;
|
|
2582
2767
|
let allTimeSessions = 0;
|
|
2583
2768
|
let todayTokens = 0;
|
|
2584
2769
|
let todaySessions = 0;
|
|
2585
2770
|
let weekTokens = 0;
|
|
2586
2771
|
let weekSessions = 0;
|
|
2772
|
+
let last5hTokens = 0;
|
|
2773
|
+
let last5hSessions = 0;
|
|
2774
|
+
let allTimeInputTokens = 0;
|
|
2775
|
+
let allTimeOutputTokens = 0;
|
|
2776
|
+
let todayInputTokens = 0;
|
|
2777
|
+
let todayOutputTokens = 0;
|
|
2778
|
+
let weekInputTokens = 0;
|
|
2779
|
+
let weekOutputTokens = 0;
|
|
2780
|
+
let last5hInputTokens = 0;
|
|
2781
|
+
let last5hOutputTokens = 0;
|
|
2587
2782
|
const todayUnix = Math.floor(todayStart.getTime() / 1e3);
|
|
2588
2783
|
const weekUnix = Math.floor(weekStart.getTime() / 1e3);
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2784
|
+
const last5hUnix = Math.floor(last5hStart.getTime() / 1e3);
|
|
2785
|
+
if (dbPath) {
|
|
2786
|
+
try {
|
|
2787
|
+
const query = `
|
|
2788
|
+
SELECT
|
|
2789
|
+
SUM(tokens_used) as total_tokens,
|
|
2790
|
+
COUNT(*) as total_sessions,
|
|
2791
|
+
SUM(CASE WHEN created_at >= ${todayUnix} THEN tokens_used ELSE 0 END) as today_tokens,
|
|
2792
|
+
SUM(CASE WHEN created_at >= ${todayUnix} THEN 1 ELSE 0 END) as today_sessions,
|
|
2793
|
+
SUM(CASE WHEN created_at >= ${weekUnix} THEN tokens_used ELSE 0 END) as week_tokens,
|
|
2794
|
+
SUM(CASE WHEN created_at >= ${weekUnix} THEN 1 ELSE 0 END) as week_sessions,
|
|
2795
|
+
SUM(CASE WHEN created_at >= ${last5hUnix} THEN tokens_used ELSE 0 END) as last5h_tokens,
|
|
2796
|
+
SUM(CASE WHEN created_at >= ${last5hUnix} THEN 1 ELSE 0 END) as last5h_sessions
|
|
2797
|
+
FROM threads;
|
|
2798
|
+
`;
|
|
2799
|
+
const { stdout } = await execFileAsync("sqlite3", [dbPath, query], {
|
|
2800
|
+
encoding: "utf8",
|
|
2801
|
+
timeout: 5e3
|
|
2802
|
+
});
|
|
2803
|
+
const result = stdout.trim();
|
|
2804
|
+
if (result) {
|
|
2805
|
+
const parts = result.split("|");
|
|
2806
|
+
allTimeTokens = parseInt(parts[0], 10) || 0;
|
|
2807
|
+
allTimeSessions = parseInt(parts[1], 10) || 0;
|
|
2808
|
+
todayTokens = parseInt(parts[2], 10) || 0;
|
|
2809
|
+
todaySessions = parseInt(parts[3], 10) || 0;
|
|
2810
|
+
weekTokens = parseInt(parts[4], 10) || 0;
|
|
2811
|
+
weekSessions = parseInt(parts[5], 10) || 0;
|
|
2812
|
+
last5hTokens = parseInt(parts[6], 10) || 0;
|
|
2813
|
+
last5hSessions = parseInt(parts[7], 10) || 0;
|
|
2814
|
+
}
|
|
2815
|
+
} catch (err) {
|
|
2816
|
+
logger.debug(`Failed to query Codex SQLite: ${String(err)}`);
|
|
2817
|
+
}
|
|
2818
|
+
}
|
|
2819
|
+
const sessionUsages = collectCodexSessionUsagesFromJsonl(codexDir);
|
|
2820
|
+
if (sessionUsages.length > 0) {
|
|
2821
|
+
let jsonlAllTimeInput = 0;
|
|
2822
|
+
let jsonlAllTimeOutput = 0;
|
|
2823
|
+
let jsonlAllTimeTokens = 0;
|
|
2824
|
+
let jsonlAllTimeSessions = 0;
|
|
2825
|
+
let jsonlTodayInput = 0;
|
|
2826
|
+
let jsonlTodayOutput = 0;
|
|
2827
|
+
let jsonlTodaySessions = 0;
|
|
2828
|
+
let jsonlWeekInput = 0;
|
|
2829
|
+
let jsonlWeekOutput = 0;
|
|
2830
|
+
let jsonlWeekSessions = 0;
|
|
2831
|
+
let jsonlLast5hInput = 0;
|
|
2832
|
+
let jsonlLast5hOutput = 0;
|
|
2833
|
+
let jsonlLast5hSessions = 0;
|
|
2834
|
+
for (const usage of sessionUsages) {
|
|
2835
|
+
if (usage.totalTokens <= 0) continue;
|
|
2836
|
+
jsonlAllTimeInput += usage.inputTokens;
|
|
2837
|
+
jsonlAllTimeOutput += usage.outputTokens;
|
|
2838
|
+
jsonlAllTimeTokens += usage.totalTokens;
|
|
2839
|
+
jsonlAllTimeSessions++;
|
|
2840
|
+
if (usage.timestampMs >= todayMs) {
|
|
2841
|
+
jsonlTodayInput += usage.inputTokens;
|
|
2842
|
+
jsonlTodayOutput += usage.outputTokens;
|
|
2843
|
+
jsonlTodaySessions++;
|
|
2844
|
+
}
|
|
2845
|
+
if (usage.timestampMs >= weekMs) {
|
|
2846
|
+
jsonlWeekInput += usage.inputTokens;
|
|
2847
|
+
jsonlWeekOutput += usage.outputTokens;
|
|
2848
|
+
jsonlWeekSessions++;
|
|
2849
|
+
}
|
|
2850
|
+
if (usage.timestampMs >= last5hMs) {
|
|
2851
|
+
jsonlLast5hInput += usage.inputTokens;
|
|
2852
|
+
jsonlLast5hOutput += usage.outputTokens;
|
|
2853
|
+
jsonlLast5hSessions++;
|
|
2854
|
+
}
|
|
2855
|
+
}
|
|
2856
|
+
if (allTimeTokens === 0 && allTimeSessions === 0) {
|
|
2857
|
+
allTimeTokens = jsonlAllTimeTokens;
|
|
2858
|
+
allTimeSessions = jsonlAllTimeSessions;
|
|
2859
|
+
todayTokens = jsonlTodayInput + jsonlTodayOutput;
|
|
2860
|
+
todaySessions = jsonlTodaySessions;
|
|
2861
|
+
weekTokens = jsonlWeekInput + jsonlWeekOutput;
|
|
2862
|
+
weekSessions = jsonlWeekSessions;
|
|
2863
|
+
last5hTokens = jsonlLast5hInput + jsonlLast5hOutput;
|
|
2864
|
+
last5hSessions = jsonlLast5hSessions;
|
|
2865
|
+
} else {
|
|
2866
|
+
allTimeInputTokens = jsonlAllTimeInput;
|
|
2867
|
+
allTimeOutputTokens = jsonlAllTimeOutput;
|
|
2868
|
+
todayInputTokens = jsonlTodayInput;
|
|
2869
|
+
todayOutputTokens = jsonlTodayOutput;
|
|
2870
|
+
weekInputTokens = jsonlWeekInput;
|
|
2871
|
+
weekOutputTokens = jsonlWeekOutput;
|
|
2872
|
+
last5hInputTokens = jsonlLast5hInput;
|
|
2873
|
+
last5hOutputTokens = jsonlLast5hOutput;
|
|
2874
|
+
}
|
|
2875
|
+
if (allTimeSessions === 0) allTimeSessions = jsonlAllTimeSessions;
|
|
2876
|
+
if (todaySessions === 0) todaySessions = jsonlTodaySessions;
|
|
2877
|
+
if (weekSessions === 0) weekSessions = jsonlWeekSessions;
|
|
2878
|
+
if (last5hSessions === 0) last5hSessions = jsonlLast5hSessions;
|
|
2879
|
+
}
|
|
2880
|
+
if (allTimeInputTokens === 0 && allTimeOutputTokens === 0 && sessionUsages.length > 0) {
|
|
2881
|
+
for (const usage of sessionUsages) {
|
|
2882
|
+
if (usage.totalTokens <= 0) continue;
|
|
2883
|
+
allTimeInputTokens += usage.inputTokens;
|
|
2884
|
+
allTimeOutputTokens += usage.outputTokens;
|
|
2885
|
+
}
|
|
2886
|
+
if (allTimeInputTokens > 0 || allTimeOutputTokens > 0) {
|
|
2887
|
+
todayInputTokens = 0;
|
|
2888
|
+
todayOutputTokens = 0;
|
|
2889
|
+
weekInputTokens = 0;
|
|
2890
|
+
weekOutputTokens = 0;
|
|
2891
|
+
last5hInputTokens = 0;
|
|
2892
|
+
last5hOutputTokens = 0;
|
|
2893
|
+
for (const usage of sessionUsages) {
|
|
2894
|
+
if (usage.timestampMs >= todayMs) {
|
|
2895
|
+
todayInputTokens += usage.inputTokens;
|
|
2896
|
+
todayOutputTokens += usage.outputTokens;
|
|
2897
|
+
}
|
|
2898
|
+
if (usage.timestampMs >= weekMs) {
|
|
2899
|
+
weekInputTokens += usage.inputTokens;
|
|
2900
|
+
weekOutputTokens += usage.outputTokens;
|
|
2901
|
+
}
|
|
2902
|
+
if (usage.timestampMs >= last5hMs) {
|
|
2903
|
+
last5hInputTokens += usage.inputTokens;
|
|
2904
|
+
last5hOutputTokens += usage.outputTokens;
|
|
2905
|
+
}
|
|
2906
|
+
}
|
|
2907
|
+
}
|
|
2908
|
+
}
|
|
2909
|
+
if (allTimeInputTokens === 0 && allTimeOutputTokens === 0) {
|
|
2910
|
+
allTimeInputTokens = allTimeTokens;
|
|
2911
|
+
}
|
|
2912
|
+
if (todayInputTokens === 0 && todayOutputTokens === 0) {
|
|
2913
|
+
todayInputTokens = todayTokens;
|
|
2914
|
+
}
|
|
2915
|
+
if (weekInputTokens === 0 && weekOutputTokens === 0) {
|
|
2916
|
+
weekInputTokens = weekTokens;
|
|
2917
|
+
}
|
|
2918
|
+
if (last5hInputTokens === 0 && last5hOutputTokens === 0) {
|
|
2919
|
+
last5hInputTokens = last5hTokens;
|
|
2920
|
+
}
|
|
2921
|
+
const statusUsage = await collectCodexUsageFromCli();
|
|
2922
|
+
if (statusUsage) {
|
|
2923
|
+
if (statusUsage.currentModel) {
|
|
2924
|
+
currentModel = statusUsage.currentModel;
|
|
2925
|
+
}
|
|
2926
|
+
if (statusUsage.allTimeInputTokens !== null || statusUsage.allTimeOutputTokens !== null) {
|
|
2927
|
+
allTimeInputTokens = statusUsage.allTimeInputTokens ?? allTimeInputTokens;
|
|
2928
|
+
allTimeOutputTokens = statusUsage.allTimeOutputTokens ?? allTimeOutputTokens;
|
|
2929
|
+
if (statusUsage.allTimeSessions !== null) allTimeSessions = statusUsage.allTimeSessions;
|
|
2930
|
+
}
|
|
2931
|
+
if (statusUsage.todayInputTokens !== null || statusUsage.todayOutputTokens !== null) {
|
|
2932
|
+
todayInputTokens = statusUsage.todayInputTokens ?? todayInputTokens;
|
|
2933
|
+
todayOutputTokens = statusUsage.todayOutputTokens ?? todayOutputTokens;
|
|
2934
|
+
if (statusUsage.todaySessions !== null) todaySessions = statusUsage.todaySessions;
|
|
2935
|
+
}
|
|
2936
|
+
if (statusUsage.thisWeekInputTokens !== null || statusUsage.thisWeekOutputTokens !== null) {
|
|
2937
|
+
weekInputTokens = statusUsage.thisWeekInputTokens ?? weekInputTokens;
|
|
2938
|
+
weekOutputTokens = statusUsage.thisWeekOutputTokens ?? weekOutputTokens;
|
|
2939
|
+
if (statusUsage.thisWeekSessions !== null) weekSessions = statusUsage.thisWeekSessions;
|
|
2940
|
+
}
|
|
2941
|
+
if (statusUsage.last5HoursInputTokens !== null || statusUsage.last5HoursOutputTokens !== null) {
|
|
2942
|
+
last5hInputTokens = statusUsage.last5HoursInputTokens ?? last5hInputTokens;
|
|
2943
|
+
last5hOutputTokens = statusUsage.last5HoursOutputTokens ?? last5hOutputTokens;
|
|
2944
|
+
if (statusUsage.last5HoursSessions !== null) last5hSessions = statusUsage.last5HoursSessions;
|
|
2945
|
+
}
|
|
2946
|
+
if (statusUsage.resetInfo) {
|
|
2947
|
+
resetInfo = statusUsage.resetInfo;
|
|
2948
|
+
}
|
|
2949
|
+
if (statusUsage.nextResetAt) {
|
|
2950
|
+
nextResetAt = statusUsage.nextResetAt;
|
|
2613
2951
|
}
|
|
2614
|
-
} catch (err) {
|
|
2615
|
-
logger.debug(`Failed to query Codex SQLite: ${String(err)}`);
|
|
2616
2952
|
}
|
|
2617
2953
|
return {
|
|
2618
2954
|
name: "codex",
|
|
@@ -2620,22 +2956,113 @@ async function collectCodexUsage() {
|
|
|
2620
2956
|
models,
|
|
2621
2957
|
currentModel,
|
|
2622
2958
|
usage: {
|
|
2623
|
-
today: makePeriod(
|
|
2624
|
-
thisWeek: makePeriod(
|
|
2625
|
-
|
|
2959
|
+
today: makePeriod(todayInputTokens, todayOutputTokens, todaySessions, todayStart.toISOString()),
|
|
2960
|
+
thisWeek: makePeriod(weekInputTokens, weekOutputTokens, weekSessions, weekStart.toISOString()),
|
|
2961
|
+
last5Hours: makePeriod(last5hInputTokens, last5hOutputTokens, last5hSessions, last5hStart.toISOString()),
|
|
2962
|
+
allTime: makePeriod(allTimeInputTokens, allTimeOutputTokens, allTimeSessions, "")
|
|
2626
2963
|
},
|
|
2627
|
-
resetInfo
|
|
2964
|
+
resetInfo,
|
|
2628
2965
|
nextResetAt,
|
|
2629
2966
|
weeklyLimitEstimate: null,
|
|
2630
|
-
percentUsed: null
|
|
2967
|
+
percentUsed: statusUsage?.weeklyPercentUsed ?? null,
|
|
2968
|
+
version: statusUsage?.version ?? null,
|
|
2969
|
+
plan: statusUsage?.plan ?? null,
|
|
2970
|
+
account: statusUsage?.account ?? null,
|
|
2971
|
+
effort: statusUsage?.effort ?? null,
|
|
2972
|
+
rateLimits: statusUsage?.rateLimits ?? []
|
|
2631
2973
|
};
|
|
2632
2974
|
}
|
|
2975
|
+
function aggregateGeminiSessionUsageFromJson(content) {
|
|
2976
|
+
let sessionInput = 0;
|
|
2977
|
+
let sessionOutput = 0;
|
|
2978
|
+
let sessionTotal = 0;
|
|
2979
|
+
let sessionTs = 0;
|
|
2980
|
+
let session;
|
|
2981
|
+
try {
|
|
2982
|
+
session = JSON.parse(content);
|
|
2983
|
+
} catch {
|
|
2984
|
+
return { inputTokens: 0, outputTokens: 0, totalTokens: 0, timestampMs: 0 };
|
|
2985
|
+
}
|
|
2986
|
+
const fallbackTs = parseTimestamp(session.startTime) || parseTimestamp(session.lastUpdated);
|
|
2987
|
+
if (fallbackTs > sessionTs) sessionTs = fallbackTs;
|
|
2988
|
+
const messages = Array.isArray(session.messages) ? session.messages : [];
|
|
2989
|
+
for (const message of messages) {
|
|
2990
|
+
if (!message || message.type !== "gemini" || !message.tokens) continue;
|
|
2991
|
+
const tokens = message.tokens;
|
|
2992
|
+
const input = toNumber(tokens.input);
|
|
2993
|
+
const output = toNumber(tokens.output);
|
|
2994
|
+
const total = toNumber(tokens.total);
|
|
2995
|
+
if (input === 0 && output === 0 && total === 0) continue;
|
|
2996
|
+
sessionInput += input;
|
|
2997
|
+
sessionOutput += output;
|
|
2998
|
+
sessionTotal += total > 0 ? total : input + output;
|
|
2999
|
+
const messageTs = parseTimestamp(message.timestamp);
|
|
3000
|
+
if (messageTs > sessionTs) sessionTs = messageTs;
|
|
3001
|
+
}
|
|
3002
|
+
if (sessionTotal <= 0) return { inputTokens: 0, outputTokens: 0, totalTokens: 0, timestampMs: sessionTs };
|
|
3003
|
+
return {
|
|
3004
|
+
inputTokens: sessionInput,
|
|
3005
|
+
outputTokens: sessionOutput,
|
|
3006
|
+
totalTokens: sessionTotal,
|
|
3007
|
+
timestampMs: sessionTs
|
|
3008
|
+
};
|
|
3009
|
+
}
|
|
3010
|
+
function collectGeminiSessionUsages() {
|
|
3011
|
+
const geminiTmp = join8(homedir2(), ".gemini", "tmp");
|
|
3012
|
+
if (!existsSync6(geminiTmp)) return [];
|
|
3013
|
+
const usages = [];
|
|
3014
|
+
let entries = [];
|
|
3015
|
+
try {
|
|
3016
|
+
entries = readdirSync2(geminiTmp, { withFileTypes: true });
|
|
3017
|
+
} catch {
|
|
3018
|
+
return usages;
|
|
3019
|
+
}
|
|
3020
|
+
for (const profile of entries) {
|
|
3021
|
+
if (!profile.isDirectory()) continue;
|
|
3022
|
+
const chatsDir = join8(geminiTmp, profile.name, "chats");
|
|
3023
|
+
if (!existsSync6(chatsDir)) continue;
|
|
3024
|
+
let sessions = [];
|
|
3025
|
+
try {
|
|
3026
|
+
sessions = readdirSync2(chatsDir).filter((name) => name.startsWith("session-") && (name.endsWith(".json") || name.endsWith(".jsonl")));
|
|
3027
|
+
} catch {
|
|
3028
|
+
continue;
|
|
3029
|
+
}
|
|
3030
|
+
for (const sessionFile of sessions) {
|
|
3031
|
+
const sessionPath = join8(chatsDir, sessionFile);
|
|
3032
|
+
try {
|
|
3033
|
+
const usage = aggregateGeminiSessionUsageFromJson(readFileSync5(sessionPath, "utf8"));
|
|
3034
|
+
if (usage.totalTokens > 0) usages.push(usage);
|
|
3035
|
+
} catch {
|
|
3036
|
+
}
|
|
3037
|
+
}
|
|
3038
|
+
}
|
|
3039
|
+
return usages;
|
|
3040
|
+
}
|
|
2633
3041
|
async function collectGeminiUsage() {
|
|
2634
3042
|
const available = await whichExists("gemini");
|
|
2635
3043
|
if (!available) return null;
|
|
3044
|
+
let version = null;
|
|
3045
|
+
try {
|
|
3046
|
+
const { stdout } = await execFileAsync("gemini", ["--version"], { encoding: "utf8", timeout: 5e3 });
|
|
3047
|
+
const trimmed = stdout.trim();
|
|
3048
|
+
if (/^\d+\.\d+/.test(trimmed)) version = trimmed;
|
|
3049
|
+
} catch {
|
|
3050
|
+
}
|
|
3051
|
+
let account = null;
|
|
3052
|
+
const accountsPath = join8(homedir2(), ".gemini", "google_accounts.json");
|
|
3053
|
+
if (existsSync6(accountsPath)) {
|
|
3054
|
+
try {
|
|
3055
|
+
const data = JSON.parse(readFileSync5(accountsPath, "utf8"));
|
|
3056
|
+
if (typeof data.active === "string" && data.active.includes("@")) {
|
|
3057
|
+
account = data.active;
|
|
3058
|
+
}
|
|
3059
|
+
} catch {
|
|
3060
|
+
}
|
|
3061
|
+
}
|
|
2636
3062
|
const todayStart = computeTodayStart();
|
|
2637
3063
|
const weekStart = computeWeekStart();
|
|
2638
|
-
|
|
3064
|
+
let nextResetAt = computeNextMonday().toISOString();
|
|
3065
|
+
const last5hStart = computeLastHoursStart(5);
|
|
2639
3066
|
const models = [];
|
|
2640
3067
|
try {
|
|
2641
3068
|
const { stdout: binPath } = await execFileAsync("which", ["gemini"], { encoding: "utf8", timeout: 3e3 });
|
|
@@ -2670,20 +3097,99 @@ async function collectGeminiUsage() {
|
|
|
2670
3097
|
} catch {
|
|
2671
3098
|
}
|
|
2672
3099
|
}
|
|
3100
|
+
const todayMs = todayStart.getTime();
|
|
3101
|
+
const weekMs = weekStart.getTime();
|
|
3102
|
+
const last5hMs = last5hStart.getTime();
|
|
3103
|
+
let resetInfo = "Usage from local Gemini session logs";
|
|
3104
|
+
let todayInputTokens = 0;
|
|
3105
|
+
let todayOutputTokens = 0;
|
|
3106
|
+
let todaySessions = 0;
|
|
3107
|
+
let weekInputTokens = 0;
|
|
3108
|
+
let weekOutputTokens = 0;
|
|
3109
|
+
let weekSessions = 0;
|
|
3110
|
+
let last5hInputTokens = 0;
|
|
3111
|
+
let last5hOutputTokens = 0;
|
|
3112
|
+
let last5hSessions = 0;
|
|
3113
|
+
let allTimeInputTokens = 0;
|
|
3114
|
+
let allTimeOutputTokens = 0;
|
|
3115
|
+
let allTimeSessions = 0;
|
|
3116
|
+
const sessionUsages = collectGeminiSessionUsages();
|
|
3117
|
+
for (const usage of sessionUsages) {
|
|
3118
|
+
allTimeInputTokens += usage.inputTokens;
|
|
3119
|
+
allTimeOutputTokens += usage.outputTokens;
|
|
3120
|
+
allTimeSessions++;
|
|
3121
|
+
if (usage.timestampMs >= todayMs) {
|
|
3122
|
+
todayInputTokens += usage.inputTokens;
|
|
3123
|
+
todayOutputTokens += usage.outputTokens;
|
|
3124
|
+
todaySessions++;
|
|
3125
|
+
}
|
|
3126
|
+
if (usage.timestampMs >= weekMs) {
|
|
3127
|
+
weekInputTokens += usage.inputTokens;
|
|
3128
|
+
weekOutputTokens += usage.outputTokens;
|
|
3129
|
+
weekSessions++;
|
|
3130
|
+
}
|
|
3131
|
+
if (usage.timestampMs >= last5hMs) {
|
|
3132
|
+
last5hInputTokens += usage.inputTokens;
|
|
3133
|
+
last5hOutputTokens += usage.outputTokens;
|
|
3134
|
+
last5hSessions++;
|
|
3135
|
+
}
|
|
3136
|
+
}
|
|
3137
|
+
const statusUsage = await collectGeminiUsageFromCli();
|
|
3138
|
+
if (statusUsage) {
|
|
3139
|
+
if (statusUsage.currentModel) {
|
|
3140
|
+
currentModel = statusUsage.currentModel;
|
|
3141
|
+
}
|
|
3142
|
+
if (statusUsage.allTimeInputTokens !== null || statusUsage.allTimeOutputTokens !== null) {
|
|
3143
|
+
allTimeInputTokens = statusUsage.allTimeInputTokens ?? allTimeInputTokens;
|
|
3144
|
+
allTimeOutputTokens = statusUsage.allTimeOutputTokens ?? allTimeOutputTokens;
|
|
3145
|
+
if (statusUsage.allTimeSessions !== null) allTimeSessions = statusUsage.allTimeSessions;
|
|
3146
|
+
}
|
|
3147
|
+
if (statusUsage.todayInputTokens !== null || statusUsage.todayOutputTokens !== null) {
|
|
3148
|
+
todayInputTokens = statusUsage.todayInputTokens ?? todayInputTokens;
|
|
3149
|
+
todayOutputTokens = statusUsage.todayOutputTokens ?? todayOutputTokens;
|
|
3150
|
+
if (statusUsage.todaySessions !== null) todaySessions = statusUsage.todaySessions;
|
|
3151
|
+
}
|
|
3152
|
+
if (statusUsage.thisWeekInputTokens !== null || statusUsage.thisWeekOutputTokens !== null) {
|
|
3153
|
+
weekInputTokens = statusUsage.thisWeekInputTokens ?? weekInputTokens;
|
|
3154
|
+
weekOutputTokens = statusUsage.thisWeekOutputTokens ?? weekOutputTokens;
|
|
3155
|
+
if (statusUsage.thisWeekSessions !== null) weekSessions = statusUsage.thisWeekSessions;
|
|
3156
|
+
}
|
|
3157
|
+
if (statusUsage.last5HoursInputTokens !== null || statusUsage.last5HoursOutputTokens !== null) {
|
|
3158
|
+
last5hInputTokens = statusUsage.last5HoursInputTokens ?? last5hInputTokens;
|
|
3159
|
+
last5hOutputTokens = statusUsage.last5HoursOutputTokens ?? last5hOutputTokens;
|
|
3160
|
+
if (statusUsage.last5HoursSessions !== null) last5hSessions = statusUsage.last5HoursSessions;
|
|
3161
|
+
}
|
|
3162
|
+
if (statusUsage.resetInfo) {
|
|
3163
|
+
resetInfo = statusUsage.resetInfo;
|
|
3164
|
+
}
|
|
3165
|
+
if (statusUsage.nextResetAt) {
|
|
3166
|
+
nextResetAt = statusUsage.nextResetAt;
|
|
3167
|
+
}
|
|
3168
|
+
if (statusUsage.weeklyLimitEstimate !== null && statusUsage.weeklyPercentUsed !== null) {
|
|
3169
|
+
resetInfo = `Estimated weekly used: ${statusUsage.weeklyPercentUsed}%`;
|
|
3170
|
+
}
|
|
3171
|
+
}
|
|
2673
3172
|
return {
|
|
2674
3173
|
name: "gemini",
|
|
2675
3174
|
available,
|
|
2676
3175
|
models,
|
|
2677
3176
|
currentModel,
|
|
2678
3177
|
usage: {
|
|
2679
|
-
today: makePeriod(
|
|
2680
|
-
thisWeek: makePeriod(
|
|
2681
|
-
|
|
3178
|
+
today: makePeriod(todayInputTokens, todayOutputTokens, todaySessions, todayStart.toISOString()),
|
|
3179
|
+
thisWeek: makePeriod(weekInputTokens, weekOutputTokens, weekSessions, weekStart.toISOString()),
|
|
3180
|
+
last5Hours: makePeriod(last5hInputTokens, last5hOutputTokens, last5hSessions, last5hStart.toISOString()),
|
|
3181
|
+
allTime: makePeriod(allTimeInputTokens, allTimeOutputTokens, allTimeSessions, "")
|
|
2682
3182
|
},
|
|
2683
|
-
resetInfo
|
|
3183
|
+
resetInfo,
|
|
2684
3184
|
nextResetAt,
|
|
2685
3185
|
weeklyLimitEstimate: null,
|
|
2686
|
-
percentUsed: null
|
|
3186
|
+
percentUsed: statusUsage?.weeklyPercentUsed ?? null,
|
|
3187
|
+
version: statusUsage?.version ?? version,
|
|
3188
|
+
plan: statusUsage?.plan ?? null,
|
|
3189
|
+
account: statusUsage?.account ?? account,
|
|
3190
|
+
effort: null,
|
|
3191
|
+
// Gemini has no effort/reasoning concept
|
|
3192
|
+
rateLimits: statusUsage?.rateLimits ?? []
|
|
2687
3193
|
};
|
|
2688
3194
|
}
|
|
2689
3195
|
var usageCache = null;
|
|
@@ -2705,6 +3211,13 @@ async function collectProvidersUsage() {
|
|
|
2705
3211
|
usageCacheAt = Date.now();
|
|
2706
3212
|
return usageCache;
|
|
2707
3213
|
}
|
|
3214
|
+
async function collectProviderUsage(providerName) {
|
|
3215
|
+
const normalized = normalizeProviderName(providerName);
|
|
3216
|
+
if (!normalized) return null;
|
|
3217
|
+
if (normalized === "claude") return collectClaudeUsage();
|
|
3218
|
+
if (normalized === "codex") return collectCodexUsage();
|
|
3219
|
+
return collectGeminiUsage();
|
|
3220
|
+
}
|
|
2708
3221
|
|
|
2709
3222
|
// src/routes/state.ts
|
|
2710
3223
|
import { existsSync as existsSync8, mkdirSync as mkdirSync4, writeFileSync as writeFileSync4 } from "fs";
|
|
@@ -2774,6 +3287,34 @@ async function replanIssueCommand(input, deps) {
|
|
|
2774
3287
|
deps.eventStore.addEvent(issue.id, "manual", `Replan requested for ${issue.identifier} \u2014 now at plan v${issue.planVersion}.`);
|
|
2775
3288
|
}
|
|
2776
3289
|
|
|
3290
|
+
// src/commands/request-rework.command.ts
|
|
3291
|
+
async function requestReworkCommand(input, deps) {
|
|
3292
|
+
const { issue, reviewerFeedback, note } = input;
|
|
3293
|
+
if (issue.state !== "Reviewing" && issue.state !== "PendingDecision") {
|
|
3294
|
+
throw new Error(
|
|
3295
|
+
`requestReworkCommand requires Reviewing or PendingDecision state, got ${issue.state}.`
|
|
3296
|
+
);
|
|
3297
|
+
}
|
|
3298
|
+
issue.lastError = reviewerFeedback;
|
|
3299
|
+
issue.lastFailedPhase = "review";
|
|
3300
|
+
issue.attempts += 1;
|
|
3301
|
+
if (issue.state === "Reviewing") {
|
|
3302
|
+
await transitionIssueCommand(
|
|
3303
|
+
{ issue, target: "PendingDecision", note: `Reviewer completed for ${issue.identifier}.` },
|
|
3304
|
+
deps
|
|
3305
|
+
);
|
|
3306
|
+
}
|
|
3307
|
+
await transitionIssueCommand(
|
|
3308
|
+
{ issue, target: "Queued", note: note ?? `Reviewer requested rework for ${issue.identifier}.` },
|
|
3309
|
+
deps
|
|
3310
|
+
);
|
|
3311
|
+
deps.eventStore.addEvent(
|
|
3312
|
+
issue.id,
|
|
3313
|
+
"runner",
|
|
3314
|
+
`Issue ${issue.identifier} sent back for rework by reviewer.`
|
|
3315
|
+
);
|
|
3316
|
+
}
|
|
3317
|
+
|
|
2777
3318
|
// src/commands/merge-workspace.command.ts
|
|
2778
3319
|
import { existsSync as existsSync7 } from "fs";
|
|
2779
3320
|
import { execSync } from "child_process";
|
|
@@ -3014,8 +3555,9 @@ async function retryExecutionCommand(input, deps) {
|
|
|
3014
3555
|
`retryExecutionCommand requires Blocked state, got ${issue.state}. Use replanIssueCommand for re-planning or the generic /retry endpoint for other states.`
|
|
3015
3556
|
);
|
|
3016
3557
|
}
|
|
3558
|
+
issue.attempts += 1;
|
|
3017
3559
|
await transitionIssueCommand(
|
|
3018
|
-
{ issue, target: "Queued", note: note ?? `Retry execution for ${issue.identifier} (attempt ${issue.attempts
|
|
3560
|
+
{ issue, target: "Queued", note: note ?? `Retry execution for ${issue.identifier} (attempt ${issue.attempts}).` },
|
|
3019
3561
|
deps
|
|
3020
3562
|
);
|
|
3021
3563
|
deps.eventStore.addEvent(
|
|
@@ -3075,6 +3617,19 @@ function registerStateRoutes(app, state) {
|
|
|
3075
3617
|
app.get("/api/parallelism", async (c) => {
|
|
3076
3618
|
return c.json(analyzeParallelizability(state.issues));
|
|
3077
3619
|
});
|
|
3620
|
+
app.get("/api/providers/:slug/usage", async (c) => {
|
|
3621
|
+
const provider = c.req.param("slug") || "";
|
|
3622
|
+
try {
|
|
3623
|
+
const usage = await collectProviderUsage(provider);
|
|
3624
|
+
return c.json({
|
|
3625
|
+
providers: usage ? [usage] : [],
|
|
3626
|
+
collectedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3627
|
+
});
|
|
3628
|
+
} catch (error) {
|
|
3629
|
+
logger.error({ err: error, provider }, "Failed to collect provider usage");
|
|
3630
|
+
return c.json({ providers: [] }, 500);
|
|
3631
|
+
}
|
|
3632
|
+
});
|
|
3078
3633
|
app.get("/api/providers/usage", async (c) => {
|
|
3079
3634
|
try {
|
|
3080
3635
|
const usage = await collectProvidersUsage();
|
|
@@ -3111,8 +3666,14 @@ function registerStateRoutes(app, state) {
|
|
|
3111
3666
|
if (!nextState) {
|
|
3112
3667
|
throw new Error(`Unsupported state: ${String(payload.state)}`);
|
|
3113
3668
|
}
|
|
3669
|
+
if (nextState === "Running" && issue.state !== "Queued") {
|
|
3670
|
+
return c.json({ ok: false, error: "Manual transition to Running is only supported from Queued." }, 400);
|
|
3671
|
+
}
|
|
3114
3672
|
const container = getContainer();
|
|
3115
3673
|
await transitionIssueCommand({ issue, target: nextState, note: `Manual state update: ${nextState}` }, container);
|
|
3674
|
+
if (nextState === "Running") {
|
|
3675
|
+
await enqueue(issue, "execute");
|
|
3676
|
+
}
|
|
3116
3677
|
if (nextState === "Cancelled" && payload.reason) {
|
|
3117
3678
|
issue.lastError = toStringValue(payload.reason);
|
|
3118
3679
|
}
|
|
@@ -3147,6 +3708,7 @@ function registerStateRoutes(app, state) {
|
|
|
3147
3708
|
container
|
|
3148
3709
|
);
|
|
3149
3710
|
} else if (issue.state === "Approved") {
|
|
3711
|
+
issue.attempts += 1;
|
|
3150
3712
|
await transitionIssueCommand(
|
|
3151
3713
|
{ issue, target: "Planning", note: "Requeued for rework after merge conflicts." },
|
|
3152
3714
|
container
|
|
@@ -3161,6 +3723,15 @@ function registerStateRoutes(app, state) {
|
|
|
3161
3723
|
container
|
|
3162
3724
|
);
|
|
3163
3725
|
}
|
|
3726
|
+
} else if (issue.state === "Reviewing" || issue.state === "PendingDecision") {
|
|
3727
|
+
await requestReworkCommand(
|
|
3728
|
+
{
|
|
3729
|
+
issue,
|
|
3730
|
+
reviewerFeedback: issue.lastError || "Manual rework request.",
|
|
3731
|
+
note: `Manual rework requested for ${issue.identifier}.`
|
|
3732
|
+
},
|
|
3733
|
+
container
|
|
3734
|
+
);
|
|
3164
3735
|
} else if (issue.state === "PendingApproval") {
|
|
3165
3736
|
await transitionIssueCommand(
|
|
3166
3737
|
{ issue, target: "Queued", note: "Manual retry \u2014 queued for execution." },
|
|
@@ -3229,7 +3800,7 @@ function registerStateRoutes(app, state) {
|
|
|
3229
3800
|
if (!issueId) return c.json({ ok: false, error: "Issue id is required." }, 400);
|
|
3230
3801
|
const issue = findIssue(state, issueId);
|
|
3231
3802
|
if (!issue) return c.json({ ok: false, error: "Issue not found." }, 404);
|
|
3232
|
-
const { dryMerge } = await import("./workspace-
|
|
3803
|
+
const { dryMerge } = await import("./workspace-5XOCMZ57.js");
|
|
3233
3804
|
const result = dryMerge(issue);
|
|
3234
3805
|
return c.json({ ok: true, ...result });
|
|
3235
3806
|
} catch (error) {
|
|
@@ -3244,7 +3815,7 @@ function registerStateRoutes(app, state) {
|
|
|
3244
3815
|
if (!issueId) return c.json({ ok: false, error: "Issue id is required." }, 400);
|
|
3245
3816
|
const issue = findIssue(state, issueId);
|
|
3246
3817
|
if (!issue) return c.json({ ok: false, error: "Issue not found." }, 404);
|
|
3247
|
-
const { rebaseWorktree } = await import("./workspace-
|
|
3818
|
+
const { rebaseWorktree } = await import("./workspace-5XOCMZ57.js");
|
|
3248
3819
|
const result = rebaseWorktree(issue);
|
|
3249
3820
|
if (result.success) {
|
|
3250
3821
|
addEvent(state, issue.id, "info", `Branch ${issue.branchName} rebased onto ${issue.baseBranch}.`);
|
|
@@ -3376,7 +3947,7 @@ function registerStateRoutes(app, state) {
|
|
|
3376
3947
|
const issue = findIssue(state, issueId);
|
|
3377
3948
|
if (!issue) return c.json({ ok: false, error: "Issue not found." }, 404);
|
|
3378
3949
|
try {
|
|
3379
|
-
const { getIssueTransitionHistory } = await import("./issue-state-machine-
|
|
3950
|
+
const { getIssueTransitionHistory } = await import("./issue-state-machine-NBSYRDZW.js");
|
|
3380
3951
|
const limit = parseInt(c.req.query("limit") ?? "50", 10);
|
|
3381
3952
|
const offset = parseInt(c.req.query("offset") ?? "0", 10);
|
|
3382
3953
|
const transitions = await getIssueTransitionHistory(issue.id, { limit, offset });
|
|
@@ -3387,7 +3958,7 @@ function registerStateRoutes(app, state) {
|
|
|
3387
3958
|
});
|
|
3388
3959
|
app.get("/api/state-machine/transitions", async (c) => {
|
|
3389
3960
|
try {
|
|
3390
|
-
const { getStateMachineTransitions } = await import("./issue-state-machine-
|
|
3961
|
+
const { getStateMachineTransitions } = await import("./issue-state-machine-NBSYRDZW.js");
|
|
3391
3962
|
return c.json({ ok: true, transitions: getStateMachineTransitions() });
|
|
3392
3963
|
} catch (error) {
|
|
3393
3964
|
return c.json({ ok: false, error: error instanceof Error ? error.message : String(error) }, 500);
|
|
@@ -3395,7 +3966,7 @@ function registerStateRoutes(app, state) {
|
|
|
3395
3966
|
});
|
|
3396
3967
|
app.get("/api/state-machine/visualize", async (c) => {
|
|
3397
3968
|
try {
|
|
3398
|
-
const { visualizeStateMachine } = await import("./issue-state-machine-
|
|
3969
|
+
const { visualizeStateMachine } = await import("./issue-state-machine-NBSYRDZW.js");
|
|
3399
3970
|
const dot = visualizeStateMachine();
|
|
3400
3971
|
if (!dot) return c.json({ ok: false, error: "Visualization not available." }, 404);
|
|
3401
3972
|
return c.json({ ok: true, dot });
|
|
@@ -3717,6 +4288,7 @@ var SETTING_ID_WORKFLOW_CONFIG = "runtime.workflowConfig";
|
|
|
3717
4288
|
var SETTING_ID_TEST_COMMAND = "runtime.testCommand";
|
|
3718
4289
|
var SETTING_ID_MERGE_MODE = "runtime.mergeMode";
|
|
3719
4290
|
var SETTING_ID_PR_BASE_BRANCH = "runtime.prBaseBranch";
|
|
4291
|
+
var SETTING_ID_AUTO_REVIEW_APPROVAL = "runtime.autoReviewApproval";
|
|
3720
4292
|
async function loadRuntimeSettings() {
|
|
3721
4293
|
return loadPersistedSettings();
|
|
3722
4294
|
}
|
|
@@ -3735,7 +4307,8 @@ var RUNTIME_CONFIG_SETTING_IDS = /* @__PURE__ */ new Set([
|
|
|
3735
4307
|
SETTING_ID_DEFAULT_EFFORT,
|
|
3736
4308
|
SETTING_ID_TEST_COMMAND,
|
|
3737
4309
|
SETTING_ID_MERGE_MODE,
|
|
3738
|
-
SETTING_ID_PR_BASE_BRANCH
|
|
4310
|
+
SETTING_ID_PR_BASE_BRANCH,
|
|
4311
|
+
SETTING_ID_AUTO_REVIEW_APPROVAL
|
|
3739
4312
|
]);
|
|
3740
4313
|
var VALID_REASONING_EFFORTS = /* @__PURE__ */ new Set(["low", "medium", "high", "extra-high"]);
|
|
3741
4314
|
function parseIntegerSetting(value) {
|
|
@@ -3797,7 +4370,8 @@ function buildRuntimeConfigSettings(config, source) {
|
|
|
3797
4370
|
{ id: SETTING_ID_DEFAULT_EFFORT, scope: "runtime", value: config.defaultEffort, source, updatedAt },
|
|
3798
4371
|
{ id: SETTING_ID_TEST_COMMAND, scope: "runtime", value: config.testCommand ?? "", source, updatedAt },
|
|
3799
4372
|
{ id: SETTING_ID_MERGE_MODE, scope: "runtime", value: config.mergeMode ?? "local", source, updatedAt },
|
|
3800
|
-
{ id: SETTING_ID_PR_BASE_BRANCH, scope: "runtime", value: config.prBaseBranch ?? "", source, updatedAt }
|
|
4373
|
+
{ id: SETTING_ID_PR_BASE_BRANCH, scope: "runtime", value: config.prBaseBranch ?? "", source, updatedAt },
|
|
4374
|
+
{ id: SETTING_ID_AUTO_REVIEW_APPROVAL, scope: "runtime", value: config.autoReviewApproval, source, updatedAt }
|
|
3801
4375
|
];
|
|
3802
4376
|
}
|
|
3803
4377
|
function applyPersistedSettings(config, settings) {
|
|
@@ -3906,6 +4480,10 @@ function applyPersistedSettings(config, settings) {
|
|
|
3906
4480
|
}
|
|
3907
4481
|
break;
|
|
3908
4482
|
}
|
|
4483
|
+
case SETTING_ID_AUTO_REVIEW_APPROVAL: {
|
|
4484
|
+
nextConfig.autoReviewApproval = toBooleanValue(setting.value, true);
|
|
4485
|
+
break;
|
|
4486
|
+
}
|
|
3909
4487
|
default:
|
|
3910
4488
|
break;
|
|
3911
4489
|
}
|
|
@@ -5674,8 +6252,11 @@ async function startApiServer(state, port) {
|
|
|
5674
6252
|
"GET /analytics": () => serveAppShell(),
|
|
5675
6253
|
"GET /agents": () => serveAppShell(),
|
|
5676
6254
|
"GET /settings": () => serveAppShell(),
|
|
6255
|
+
"GET /settings/project": () => serveAppShell(),
|
|
5677
6256
|
"GET /settings/general": () => serveAppShell(),
|
|
6257
|
+
"GET /settings/agents": () => serveAppShell(),
|
|
5678
6258
|
"GET /settings/notifications": () => serveAppShell(),
|
|
6259
|
+
"GET /settings/preferences": () => serveAppShell(),
|
|
5679
6260
|
"GET /settings/workflow": () => serveAppShell(),
|
|
5680
6261
|
"GET /settings/providers": () => serveAppShell(),
|
|
5681
6262
|
"GET /api/health": (c) => c.json({ status: state.booting ? "booting" : "ready" })
|
|
@@ -6583,7 +7164,8 @@ var AGENCY_AGENTS_EXCLUDED_DIRS = /* @__PURE__ */ new Set([
|
|
|
6583
7164
|
var REFERENCE_REPOSITORY_PARSERS = {
|
|
6584
7165
|
ring: collectStandardArtifacts,
|
|
6585
7166
|
"agency-agents": collectAgencyArtifacts,
|
|
6586
|
-
impeccable: collectImpeccableArtifacts
|
|
7167
|
+
impeccable: collectImpeccableArtifacts,
|
|
7168
|
+
"everything-claude-code": collectStandardArtifacts
|
|
6587
7169
|
};
|
|
6588
7170
|
function runGit(args, cwd) {
|
|
6589
7171
|
return execFileSync2("git", args, {
|
|
@@ -7046,6 +7628,7 @@ function deriveConfig(args) {
|
|
|
7046
7628
|
},
|
|
7047
7629
|
maxConcurrentByState: {},
|
|
7048
7630
|
runMode: "filesystem",
|
|
7631
|
+
autoReviewApproval: true,
|
|
7049
7632
|
afterCreateHook: env4.FIFONY_AFTER_CREATE_HOOK ?? "",
|
|
7050
7633
|
beforeRunHook: env4.FIFONY_BEFORE_RUN_HOOK ?? "",
|
|
7051
7634
|
afterRunHook: env4.FIFONY_AFTER_RUN_HOOK ?? "",
|
|
@@ -7276,6 +7859,7 @@ async function handleStatePatch(state, issue, payload) {
|
|
|
7276
7859
|
if (!nextState || !ALLOWED_STATES.includes(nextState)) {
|
|
7277
7860
|
throw new Error(`Unsupported state: ${String(payload.state)}`);
|
|
7278
7861
|
}
|
|
7862
|
+
const sourceState = issue.state;
|
|
7279
7863
|
const path = findIssueStateMachineTransitionPath(null, issue.state, nextState);
|
|
7280
7864
|
if (!path || path.length === 0) {
|
|
7281
7865
|
throw new Error(`No valid transition from '${issue.state}' to '${nextState}' for issue ${issue.id}.`);
|
|
@@ -7283,6 +7867,14 @@ async function handleStatePatch(state, issue, payload) {
|
|
|
7283
7867
|
for (const event of path) {
|
|
7284
7868
|
await transitionIssue(issue, event, { note: `Manual state update: ${nextState}`, reason: toStringValue(payload.reason) });
|
|
7285
7869
|
}
|
|
7870
|
+
if (nextState === "Running" && sourceState === "Queued") {
|
|
7871
|
+
try {
|
|
7872
|
+
const { enqueue: enqueue2 } = await import("./queue-workers-OHPJKAPM.js");
|
|
7873
|
+
await enqueue2(issue, "execute");
|
|
7874
|
+
} catch (error) {
|
|
7875
|
+
logger.warn({ issueId: issue.id, err: error }, "[Issues] Failed to enqueue after manual Running transition");
|
|
7876
|
+
}
|
|
7877
|
+
}
|
|
7286
7878
|
if (nextState === "PendingApproval") {
|
|
7287
7879
|
issue.nextRetryAt = void 0;
|
|
7288
7880
|
issue.lastError = void 0;
|
|
@@ -7293,34 +7885,6 @@ async function handleStatePatch(state, issue, payload) {
|
|
|
7293
7885
|
addEvent(state, issue.id, "manual", `Manual state transition to ${nextState}`);
|
|
7294
7886
|
}
|
|
7295
7887
|
|
|
7296
|
-
// src/commands/request-rework.command.ts
|
|
7297
|
-
async function requestReworkCommand(input, deps) {
|
|
7298
|
-
const { issue, reviewerFeedback, note } = input;
|
|
7299
|
-
if (issue.state !== "Reviewing" && issue.state !== "PendingDecision") {
|
|
7300
|
-
throw new Error(
|
|
7301
|
-
`requestReworkCommand requires Reviewing or PendingDecision state, got ${issue.state}.`
|
|
7302
|
-
);
|
|
7303
|
-
}
|
|
7304
|
-
issue.lastError = reviewerFeedback;
|
|
7305
|
-
issue.lastFailedPhase = "review";
|
|
7306
|
-
issue.attempts += 1;
|
|
7307
|
-
if (issue.state === "Reviewing") {
|
|
7308
|
-
await transitionIssueCommand(
|
|
7309
|
-
{ issue, target: "PendingDecision", note: `Reviewer completed for ${issue.identifier}.` },
|
|
7310
|
-
deps
|
|
7311
|
-
);
|
|
7312
|
-
}
|
|
7313
|
-
await transitionIssueCommand(
|
|
7314
|
-
{ issue, target: "Queued", note: note ?? `Reviewer requested rework for ${issue.identifier}.` },
|
|
7315
|
-
deps
|
|
7316
|
-
);
|
|
7317
|
-
deps.eventStore.addEvent(
|
|
7318
|
-
issue.id,
|
|
7319
|
-
"runner",
|
|
7320
|
-
`Issue ${issue.identifier} sent back for rework by reviewer.`
|
|
7321
|
-
);
|
|
7322
|
-
}
|
|
7323
|
-
|
|
7324
7888
|
// src/agents/issue-runner.ts
|
|
7325
7889
|
async function runPlanningJob(state, issue) {
|
|
7326
7890
|
issue.planningStatus = "planning";
|
|
@@ -7383,9 +7947,15 @@ async function handleReviewStage(state, issue, workspacePath, startTs, routedPro
|
|
|
7383
7947
|
markIssueDirty(issue.id);
|
|
7384
7948
|
const container = getContainer();
|
|
7385
7949
|
const reviewer = routedProviders.find((p) => p.role === "reviewer");
|
|
7950
|
+
const autoReviewApproval = state.config.autoReviewApproval !== false;
|
|
7386
7951
|
if (!reviewer) {
|
|
7387
|
-
|
|
7388
|
-
|
|
7952
|
+
if (autoReviewApproval) {
|
|
7953
|
+
issue.mergedReason = "Auto-approved: no reviewer configured.";
|
|
7954
|
+
await transitionIssueCommand({ issue, target: "Approved", note: `No reviewer configured; auto-approved for ${issue.identifier}.` }, container);
|
|
7955
|
+
return;
|
|
7956
|
+
}
|
|
7957
|
+
issue.mergedReason = "Reviewer not configured; manual approval required.";
|
|
7958
|
+
await transitionIssueCommand({ issue, target: "PendingDecision", note: `No reviewer configured; manual approval required for ${issue.identifier}.` }, container);
|
|
7389
7959
|
return;
|
|
7390
7960
|
}
|
|
7391
7961
|
addEvent(state, issue.id, "info", `Review provider: ${reviewer.role}:${reviewer.provider}${reviewer.model ? `/${reviewer.model}` : ""}${reviewer.profile ? `:${reviewer.profile}` : ""}.`);
|
|
@@ -7434,8 +8004,9 @@ async function handleReviewStage(state, issue, workspacePath, startTs, routedPro
|
|
|
7434
8004
|
logger.warn({ err: String(vErr) }, "[Agent] Failed to write versioned review artifacts");
|
|
7435
8005
|
}
|
|
7436
8006
|
if (reviewResult.success) {
|
|
7437
|
-
issue.mergedReason = `Auto-approved by reviewer in ${reviewResult.turns} turn(s).`;
|
|
8007
|
+
issue.mergedReason = autoReviewApproval ? `Auto-approved by reviewer in ${reviewResult.turns} turn(s).` : `Reviewer completed for ${issue.identifier}; waiting for manual approval.`;
|
|
7438
8008
|
await transitionIssueCommand({ issue, target: "PendingDecision", note: `Reviewer completed for ${issue.identifier}.` }, container);
|
|
8009
|
+
if (!autoReviewApproval) return;
|
|
7439
8010
|
const validation = await runValidationGate(issue, state.config);
|
|
7440
8011
|
if (validation) {
|
|
7441
8012
|
issue.validationResult = validation;
|
|
@@ -7566,7 +8137,7 @@ async function runIssueOnce(state, issue, running) {
|
|
|
7566
8137
|
const { workspacePath, promptText, promptFile } = await prepareWorkspace(issue, state, state.config.defaultBranch);
|
|
7567
8138
|
container.issueRepository.markDirty(issue.id);
|
|
7568
8139
|
try {
|
|
7569
|
-
const { getIssueStateResource: getIssueStateResource2 } = await import("./store-
|
|
8140
|
+
const { getIssueStateResource: getIssueStateResource2 } = await import("./store-FFHHRNVI.js");
|
|
7570
8141
|
const res = getIssueStateResource2();
|
|
7571
8142
|
if (res) {
|
|
7572
8143
|
await res.patch(issue.id, {
|
|
@@ -7678,4 +8249,4 @@ export {
|
|
|
7678
8249
|
syncReferenceRepositories,
|
|
7679
8250
|
importReferenceArtifacts
|
|
7680
8251
|
};
|
|
7681
|
-
//# sourceMappingURL=chunk-
|
|
8252
|
+
//# sourceMappingURL=chunk-FAFGDK62.js.map
|