Files
2026-07-13 12:22:33 +08:00

1356 lines
51 KiB
Swift
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import CodexBarCore
import Foundation
// swiftlint:disable:next type_body_length
enum CLIRenderer {
private static let accentColor = "95"
private static let accentBoldColor = "1;95"
private static let subtleColor = "90"
private static let paceMinimumExpectedPercent: Double = 3
private static let usageBarWidth = 12
static func renderText(
provider: UsageProvider,
snapshot: UsageSnapshot,
credits: CreditsSnapshot?,
context: RenderContext,
now: Date = Date()) -> String
{
let meta = ProviderDescriptorRegistry.descriptor(for: provider).metadata
let labels = self.rateWindowLabels(provider: provider, metadata: meta, snapshot: snapshot)
var lines: [String] = []
lines.append(self.headerLine(context.header, useColor: context.useColor))
self.appendPrimaryLines(
provider: provider,
snapshot: snapshot,
labels: labels,
context: context,
now: now,
lines: &lines)
self.appendSecondaryLines(
provider: provider,
snapshot: snapshot,
labels: labels,
context: context,
now: now,
lines: &lines)
self.appendTertiaryLines(snapshot: snapshot, labels: labels, context: context, now: now, lines: &lines)
self.appendMiMoBalanceLine(snapshot: snapshot, useColor: context.useColor, lines: &lines)
self.appendCrossModelUsageLines(snapshot: snapshot, useColor: context.useColor, lines: &lines)
self.appendClawRouterUsageLines(snapshot: snapshot, useColor: context.useColor, lines: &lines)
self.appendSub2APIUsageLines(snapshot: snapshot, useColor: context.useColor, lines: &lines)
self.appendWayfinderUsageLines(snapshot: snapshot, useColor: context.useColor, lines: &lines)
self.appendDeepgramLines(snapshot: snapshot, useColor: context.useColor, lines: &lines)
self.appendAmpBalanceLines(snapshot: snapshot, useColor: context.useColor, lines: &lines)
self.appendDevinOverageBalanceLine(
provider: provider,
snapshot: snapshot,
useColor: context.useColor,
lines: &lines)
self.appendLimitsUnavailableLine(
provider: provider,
snapshot: snapshot,
useColor: context.useColor,
lines: &lines)
self.appendCreditsLine(provider: provider, credits: credits, useColor: context.useColor, lines: &lines)
self.appendCodexResetCreditsLine(
provider: provider,
snapshot: snapshot,
now: now,
useColor: context.useColor,
lines: &lines)
self.appendIdentityAndNotes(
provider: provider,
snapshot: snapshot,
context: context,
lines: &lines)
if let status = context.status {
let statusLine = "Status: \(status.indicator.label)\(status.descriptionSuffix)"
lines.append(self.colorize(statusLine, indicator: status.indicator, useColor: context.useColor))
}
return lines.joined(separator: "\n")
}
static func renderCardBodyLines(
provider: UsageProvider,
snapshot: UsageSnapshot,
credits: CreditsSnapshot?,
context: RenderContext,
includeIdentity: Bool,
now: Date = Date()) -> [String]
{
let meta = ProviderDescriptorRegistry.descriptor(for: provider).metadata
let labels = self.rateWindowLabels(provider: provider, metadata: meta, snapshot: snapshot)
var lines: [String] = []
self.appendPrimaryLines(
provider: provider,
snapshot: snapshot,
labels: labels,
context: context,
now: now,
lines: &lines)
self.appendSecondaryLines(
provider: provider,
snapshot: snapshot,
labels: labels,
context: context,
now: now,
lines: &lines)
self.appendTertiaryLines(snapshot: snapshot, labels: labels, context: context, now: now, lines: &lines)
self.appendMiMoBalanceLine(snapshot: snapshot, useColor: context.useColor, lines: &lines)
self.appendCrossModelUsageLines(snapshot: snapshot, useColor: context.useColor, lines: &lines)
self.appendClawRouterUsageLines(snapshot: snapshot, useColor: context.useColor, lines: &lines)
self.appendSub2APIUsageLines(snapshot: snapshot, useColor: context.useColor, lines: &lines)
self.appendWayfinderUsageLines(snapshot: snapshot, useColor: context.useColor, lines: &lines)
self.appendDeepgramLines(snapshot: snapshot, useColor: context.useColor, lines: &lines)
self.appendAmpBalanceLines(snapshot: snapshot, useColor: context.useColor, lines: &lines)
self.appendDevinOverageBalanceLine(
provider: provider,
snapshot: snapshot,
useColor: context.useColor,
lines: &lines)
self.appendLimitsUnavailableLine(
provider: provider,
snapshot: snapshot,
useColor: context.useColor,
lines: &lines)
self.appendCreditsLine(provider: provider, credits: credits, useColor: context.useColor, lines: &lines)
self.appendCodexResetCreditsLine(
provider: provider,
snapshot: snapshot,
now: now,
useColor: context.useColor,
lines: &lines)
if includeIdentity {
self.appendIdentityAndNotes(
provider: provider,
snapshot: snapshot,
context: context,
lines: &lines)
} else {
for note in context.notes {
let trimmed = note.trimmingCharacters(in: .whitespacesAndNewlines)
guard !trimmed.isEmpty else { continue }
lines.append(self.labelValueLine("Note", value: trimmed, useColor: context.useColor))
}
}
return lines
}
static func planBadgeText(provider: UsageProvider, snapshot: UsageSnapshot) -> String? {
if let usage = snapshot.mimoUsage {
return usage.balanceDetail
}
if let usage = snapshot.crossModelUsage {
return "Balance: \(usage.balanceDisplay)"
}
if provider == .kilo {
let kiloLogin = self.kiloLoginParts(snapshot: snapshot)
if let pass = kiloLogin.pass {
return UsageFormatter.cleanPlanName(pass)
}
return nil
}
guard let plan = snapshot.loginMethod(for: provider),
!plan.isEmpty,
provider != .mimo || !plan.localizedCaseInsensitiveContains("balance:")
else {
return nil
}
if provider == .codex {
return CodexPlanFormatting.displayName(plan) ?? plan
}
return self.nonCodexPlanDisplay(provider: provider, plan: plan)
}
static func colorizeAccentBold(_ text: String) -> String {
self.ansi(self.accentBoldColor, text)
}
static func colorizeAccent(_ text: String) -> String {
self.ansi(self.accentColor, text)
}
static func colorizeSubtle(_ text: String) -> String {
self.ansi(self.subtleColor, text)
}
static func colorizeCardBorder(_ text: String) -> String {
self.ansi("90", text)
}
static func colorizeCardBadge(_ source: String) -> String {
self.ansi("97;44", " \(source) ")
}
static func colorizeCardPlanBox(_ text: String) -> String {
self.ansi("37", text)
}
static func colorizeCardPercent(_ text: String, remainingPercent: Double, useColor: Bool) -> String {
guard useColor else { return text }
let code = switch remainingPercent {
case ..<10: "31"
case ..<50: "33"
default: "36"
}
return self.ansi(code, text)
}
static func colorizeCardUsedPercent(_ text: String, usedPercent: Double, useColor: Bool) -> String {
guard useColor else { return text }
let code = switch usedPercent {
case 90...: "31"
case 60...: "33"
default: "36"
}
return self.ansi(code, text)
}
static func cardUsedBar(usedPercent: Double, width: Int, useColor: Bool) -> String {
let clamped = max(0, min(100, usedPercent))
let barWidth = max(4, width)
let rawFilled = Int((clamped / 100) * Double(barWidth))
let filled = max(0, min(barWidth, rawFilled))
let empty = max(0, barWidth - filled)
let bar = String(repeating: "█", count: filled) + String(repeating: "░", count: empty)
guard useColor else { return bar }
return self.colorizeCardUsedPercent(bar, usedPercent: clamped, useColor: true)
}
static func colorizeWarning(_ text: String) -> String {
self.ansi("33", text)
}
static func ansiTrueColor(red: Int, green: Int, blue: Int, _ text: String) -> String {
let r = max(0, min(255, red))
let g = max(0, min(255, green))
let b = max(0, min(255, blue))
return "\u{001B}[38;2;\(r);\(g);\(b)m\(text)\u{001B}[0m"
}
static func ansiTrueColorBackground(red: Int, green: Int, blue: Int, _ text: String) -> String {
let r = max(0, min(255, red))
let g = max(0, min(255, green))
let b = max(0, min(255, blue))
return "\u{001B}[48;2;\(r);\(g);\(b)m\(text)\u{001B}[0m"
}
static func remainingGradientRGB(remainingPercent: Double) -> (dark: (Int, Int, Int), light: (Int, Int, Int)) {
switch remainingPercent {
case ..<10:
((180, 55, 55), (255, 95, 95))
case ..<50:
((200, 120, 40), (255, 190, 90))
default:
((40, 150, 140), (90, 220, 200))
}
}
static func gradientRemainingBar(remainingPercent: Double, width: Int) -> String {
let clamped = max(0, min(100, remainingPercent))
let barWidth = max(4, width)
let rawFilled = Int((clamped / 100) * Double(barWidth))
let filled = max(0, min(barWidth, rawFilled))
let empty = max(0, barWidth - filled)
let colors = self.remainingGradientRGB(remainingPercent: clamped)
var bar = ""
if filled > 0 {
for index in 0..<filled {
let t = filled == 1 ? 1.0 : Double(index) / Double(filled - 1)
let red = Int(Double(colors.dark.0) * (1 - t) + Double(colors.light.0) * t)
let green = Int(Double(colors.dark.1) * (1 - t) + Double(colors.light.1) * t)
let blue = Int(Double(colors.dark.2) * (1 - t) + Double(colors.light.2) * t)
bar += self.ansiTrueColor(red: red, green: green, blue: blue, "█")
}
}
if empty > 0 {
let emptyCell = self.ansiTrueColor(red: 48, green: 50, blue: 62, "░")
bar += String(repeating: emptyCell, count: empty)
}
return bar
}
static func gradientRemainingTrackBar(remainingPercent: Double, width: Int) -> String {
let clamped = max(0, min(100, remainingPercent))
let barWidth = max(4, width)
let rawFilled = Int((clamped / 100) * Double(barWidth))
let filled = max(0, min(barWidth, rawFilled))
let empty = max(0, barWidth - filled)
let colors = self.remainingGradientRGB(remainingPercent: clamped)
var bar = ""
if filled > 0 {
for index in 0..<filled {
let t = filled == 1 ? 1.0 : Double(index) / Double(filled - 1)
let red = Int(Double(colors.dark.0) * (1 - t) + Double(colors.light.0) * t)
let green = Int(Double(colors.dark.1) * (1 - t) + Double(colors.light.1) * t)
let blue = Int(Double(colors.dark.2) * (1 - t) + Double(colors.light.2) * t)
bar += self.ansiTrueColorBackground(red: red, green: green, blue: blue, " ")
}
}
if empty > 0 {
let emptyCell = self.ansiTrueColorBackground(red: 17, green: 30, blue: 50, " ")
bar += String(repeating: emptyCell, count: empty)
}
return bar
}
static func gradientUsedBar(usedPercent: Double, width: Int) -> String {
let clamped = max(0, min(100, usedPercent))
let barWidth = max(4, width)
let rawFilled = Int((clamped / 100) * Double(barWidth))
let filled = max(0, min(barWidth, rawFilled))
let empty = max(0, barWidth - filled)
let colors = self.remainingGradientRGB(remainingPercent: 100 - clamped)
var bar = ""
if filled > 0 {
for index in 0..<filled {
let t = filled == 1 ? 1.0 : Double(index) / Double(filled - 1)
let red = Int(Double(colors.dark.0) * (1 - t) + Double(colors.light.0) * t)
let green = Int(Double(colors.dark.1) * (1 - t) + Double(colors.light.1) * t)
let blue = Int(Double(colors.dark.2) * (1 - t) + Double(colors.light.2) * t)
bar += self.ansiTrueColor(red: red, green: green, blue: blue, "█")
}
}
if empty > 0 {
let emptyCell = self.ansiTrueColor(red: 48, green: 50, blue: 62, "░")
bar += String(repeating: emptyCell, count: empty)
}
return bar
}
static func colorizeEnhancedAccentBold(_ text: String) -> String {
self.ansiTrueColor(red: 198, green: 146, blue: 255, text)
}
static func colorizeEnhancedAccent(_ text: String) -> String {
self.ansiTrueColor(red: 176, green: 132, blue: 232, text)
}
static func colorizeEnhancedSubtle(_ text: String) -> String {
self.ansiTrueColor(red: 130, green: 135, blue: 150, text)
}
static func colorizeEnhancedBorder(_ text: String) -> String {
self.ansiTrueColor(red: 90, green: 95, blue: 110, text)
}
static func colorizeEnhancedBadge(_ source: String) -> String {
let r = max(0, min(255, 66))
let g = max(0, min(255, 133))
let b = max(0, min(255, 244))
return "\u{001B}[38;2;245;248;255;48;2;\(r);\(g);\(b)m \(source) \u{001B}[0m"
}
static func colorizeEnhancedPlanBox(_ text: String) -> String {
self.ansiTrueColor(red: 220, green: 222, blue: 230, text)
}
static func colorizeEnhancedPlanLabel(_ text: String) -> String {
self.ansiTrueColor(red: 104, green: 111, blue: 135, text)
}
static func colorizeEnhancedPlanValue(_ text: String) -> String {
self.ansiTrueColor(red: 238, green: 184, blue: 92, text)
}
static func colorizeEnhancedRemainingPercent(_ text: String, remainingPercent: Double) -> String {
let colors = self.remainingGradientRGB(remainingPercent: remainingPercent)
return self.ansiTrueColor(red: colors.light.0, green: colors.light.1, blue: colors.light.2, text)
}
static func colorizeEnhancedUsedPercent(_ text: String, usedPercent: Double) -> String {
self.colorizeEnhancedRemainingPercent(text, remainingPercent: 100 - usedPercent)
}
static func colorizeEnhancedReadable(_ text: String) -> String {
self.ansiTrueColor(red: 235, green: 238, blue: 245, text)
}
static func colorizeEnhancedReadableMuted(_ text: String) -> String {
self.ansiTrueColor(red: 170, green: 178, blue: 195, text)
}
static func colorizeEnhancedGood(_ text: String) -> String {
self.ansiTrueColor(red: 116, green: 220, blue: 195, text)
}
static func colorizeReadable(_ text: String) -> String {
self.ansi("97", text)
}
static func colorizeReadableMuted(_ text: String) -> String {
self.ansi("37", text)
}
static func cardBlockBar(remainingPercent: Double, width: Int, useColor: Bool) -> String {
let clamped = max(0, min(100, remainingPercent))
let barWidth = max(8, width)
let rawFilled = Int((clamped / 100) * Double(barWidth))
let filled = max(0, min(barWidth, rawFilled))
let empty = max(0, barWidth - filled)
let filledBar = String(repeating: "━", count: filled)
let emptyBar = String(repeating: " ", count: empty)
guard useColor else { return filledBar + emptyBar }
return self.colorizeCardPercent(filledBar, remainingPercent: remainingPercent, useColor: true)
+ self.colorizeSubtle(String(repeating: "─", count: empty))
}
static func collectCardMetrics(
provider: UsageProvider,
snapshot: UsageSnapshot,
resetStyle: ResetTimeDisplayStyle,
now: Date = Date()) -> [CLICardMetric]
{
let meta = ProviderDescriptorRegistry.descriptor(for: provider).metadata
let labels = self.rateWindowLabels(provider: provider, metadata: meta, snapshot: snapshot)
var metrics: [CLICardMetric] = []
if let primary = snapshot.primary, !primary.isSyntheticPlaceholder {
metrics.append(self.makeCardMetric(
provider: provider,
label: labels.primary,
window: primary,
resetStyle: resetStyle,
now: now))
}
if let secondary = snapshot.secondary, !secondary.isSyntheticPlaceholder {
metrics.append(self.makeCardMetric(
provider: provider,
label: labels.secondary,
window: secondary,
resetStyle: resetStyle,
now: now))
}
if labels.showsTertiary, let tertiary = snapshot.tertiary, !tertiary.isSyntheticPlaceholder {
metrics.append(self.makeCardMetric(
provider: provider,
label: labels.tertiary,
window: tertiary,
resetStyle: resetStyle,
now: now))
}
return metrics
}
static func collectCardInfoLines(
provider: UsageProvider,
snapshot: UsageSnapshot,
credits: CreditsSnapshot?,
notes: [String],
useColor: Bool,
now: Date = Date()) -> [String]
{
var lines: [String] = []
if provider == .codex, let resetCredits = snapshot.codexResetCredits {
let inventory = resetCredits.availableInventory(at: now)
let value = inventory.count == 1 ? "1 available" : "\(inventory.count) available"
lines.append(self.labelValueLine("Limit Reset Credits", value: value, useColor: useColor))
}
if provider == .codex, let credits {
let remaining = credits.codexCreditLimit?.remaining ?? credits.remaining
lines.append(self.labelValueLine(
"Credits",
value: UsageFormatter.creditsString(from: remaining),
useColor: useColor))
}
for note in notes {
let trimmed = note.trimmingCharacters(in: .whitespacesAndNewlines)
guard !trimmed.isEmpty else { continue }
lines.append(self.labelValueLine("Note", value: trimmed, useColor: useColor))
}
return lines
}
static func collectCardExtraLines(
provider: UsageProvider,
snapshot: UsageSnapshot,
credits: CreditsSnapshot?,
context: RenderContext,
now: Date = Date()) -> [String]
{
var lines: [String] = []
if snapshot.primary == nil {
self.appendPrimaryLines(
provider: provider,
snapshot: snapshot,
labels: self.rateWindowLabels(
provider: provider,
metadata: ProviderDescriptorRegistry.descriptor(for: provider).metadata,
snapshot: snapshot),
context: context,
now: now,
lines: &lines)
}
if snapshot.mimoUsage != nil {
self.appendMiMoBalanceLine(snapshot: snapshot, useColor: context.useColor, lines: &lines)
}
self.appendCrossModelUsageLines(snapshot: snapshot, useColor: context.useColor, lines: &lines)
self.appendClawRouterUsageLines(snapshot: snapshot, useColor: context.useColor, lines: &lines)
self.appendSub2APIUsageLines(snapshot: snapshot, useColor: context.useColor, lines: &lines)
self.appendWayfinderUsageLines(snapshot: snapshot, useColor: context.useColor, lines: &lines)
self.appendDeepgramLines(snapshot: snapshot, useColor: context.useColor, lines: &lines)
self.appendAmpBalanceLines(snapshot: snapshot, useColor: context.useColor, lines: &lines)
self.appendDevinOverageBalanceLine(
provider: provider,
snapshot: snapshot,
useColor: context.useColor,
lines: &lines)
self.appendLimitsUnavailableLine(
provider: provider,
snapshot: snapshot,
useColor: context.useColor,
lines: &lines)
if provider == .kilo {
let kiloLogin = self.kiloLoginParts(snapshot: snapshot)
for detail in kiloLogin.details {
lines.append(self.labelValueLine("Activity", value: detail, useColor: context.useColor))
}
}
return lines
}
private static func makeCardMetric(
provider: UsageProvider,
label: String,
window: RateWindow,
resetStyle: ResetTimeDisplayStyle,
now: Date) -> CLICardMetric
{
let detailBacked = self.usesDetailBackedWindow(provider: provider)
let reset = detailBacked
? self.resetLineForDetailBackedWindow(window: window, style: resetStyle, now: now)
: self.resetLine(for: window, style: resetStyle, now: now)
let detailText = detailBacked ? self.detailLineForDetailBackedWindow(window: window) : nil
return CLICardMetric(
label: label,
remainingPercent: window.remainingPercent,
resetText: reset.map { "⏳ \($0)" },
resetAt: window.resetsAt,
detailText: detailText)
}
static func colorizeError(_ text: String) -> String {
self.ansi("31", text)
}
static func colorizeStatusLine(
_ text: String,
indicator: ProviderStatusPayload.ProviderStatusIndicator,
useColor: Bool) -> String
{
self.colorize(text, indicator: indicator, useColor: useColor)
}
static func providerPacePayload(
provider: UsageProvider,
snapshot: UsageSnapshot,
weeklyWorkDays: Int? = nil,
now: Date = Date()) -> ProviderPacePayload?
{
let primary = snapshot.primary.flatMap {
self.pacePayload(provider: provider, window: $0, kind: .session, now: now)
}
let secondary = snapshot.secondary.flatMap {
self.pacePayload(provider: provider, window: $0, kind: .weekly, weeklyWorkDays: weeklyWorkDays, now: now)
}
guard primary != nil || secondary != nil else { return nil }
return ProviderPacePayload(primary: primary, secondary: secondary)
}
static func rateLine(title: String, window: RateWindow, useColor: Bool) -> String {
let text = UsageFormatter.usageLine(
remaining: window.remainingPercent,
used: window.usedPercent,
showUsed: false)
let colored = self.colorizeUsage(text, remainingPercent: window.remainingPercent, useColor: useColor)
let bar = self.usageBar(remainingPercent: window.remainingPercent, useColor: useColor)
return "\(title): \(colored) \(bar)"
}
// swiftlint:disable:next function_parameter_count
private static func appendPrimaryLines(
provider: UsageProvider,
snapshot: UsageSnapshot,
labels: RateWindowLabels,
context: RenderContext,
now: Date,
lines: inout [String])
{
if let primary = snapshot.primary {
self.appendRateWindowLines(
provider: provider,
title: labels.primary,
window: primary,
paceKind: .session,
context: context,
now: now,
lines: &lines)
return
}
guard
provider != .clawrouter,
let cost = snapshot.providerCost,
!(provider == .devin && cost.period == "Extra usage balance")
else { return }
// Fallback to cost/quota display if no primary rate window.
let label = cost.currencyCode == "Quota" ? "Quota" : "Cost"
let value = "\(String(format: "%.1f", cost.used)) / \(String(format: "%.1f", cost.limit))"
lines.append(self.labelValueLine(label, value: value, useColor: context.useColor))
}
// swiftlint:disable:next function_parameter_count
private static func appendSecondaryLines(
provider: UsageProvider,
snapshot: UsageSnapshot,
labels: RateWindowLabels,
context: RenderContext,
now: Date,
lines: inout [String])
{
guard let weekly = snapshot.secondary else { return }
self.appendRateWindowLines(
provider: provider,
title: labels.secondary,
window: weekly,
paceKind: .weekly,
context: context,
now: now,
lines: &lines)
}
private static func appendMiMoBalanceLine(
snapshot: UsageSnapshot,
useColor: Bool,
lines: inout [String])
{
guard let usage = snapshot.mimoUsage else { return }
lines.append(self.labelValueLine("Balance", value: usage.balanceDetail, useColor: useColor))
}
private static func appendDevinOverageBalanceLine(
provider: UsageProvider,
snapshot: UsageSnapshot,
useColor: Bool,
lines: inout [String])
{
guard provider == .devin,
let cost = snapshot.providerCost,
cost.period == "Extra usage balance"
else { return }
let balance = UsageFormatter.currencyString(cost.used, currencyCode: cost.currencyCode)
lines.append(self.labelValueLine("Extra usage", value: balance, useColor: useColor))
}
private static func appendCrossModelUsageLines(
snapshot: UsageSnapshot,
useColor: Bool,
lines: inout [String])
{
guard let usage = snapshot.crossModelUsage else { return }
lines.append(self.labelValueLine("Balance", value: usage.balanceDisplay, useColor: useColor))
if let daily = usage.daily {
lines.append(self.crossModelUsageLine(
title: "Today",
usage: usage,
window: daily,
metric: .tokens,
useColor: useColor))
}
if let weekly = usage.weekly {
lines.append(self.crossModelUsageLine(
title: "Week",
usage: usage,
window: weekly,
metric: .requests,
useColor: useColor))
}
if let monthly = usage.monthly {
lines.append(self.crossModelUsageLine(
title: "Month",
usage: usage,
window: monthly,
metric: .requests,
useColor: useColor))
}
}
private static func appendClawRouterUsageLines(
snapshot: UsageSnapshot,
useColor: Bool,
lines: inout [String])
{
guard let usage = snapshot.clawRouterUsage else { return }
let spend = usage.budgetSpentUSD ?? usage.actualCostUSD
let spendValue = UsageFormatter.currencyString(spend, currencyCode: "USD")
if let limit = usage.budgetLimitUSD, limit > 0 {
let limitValue = UsageFormatter.currencyString(limit, currencyCode: "USD")
lines.append(self.labelValueLine("Spend", value: "\(spendValue) / \(limitValue)", useColor: useColor))
} else {
lines.append(self.labelValueLine("Spend", value: spendValue, useColor: useColor))
}
let requests = UsageFormatter.tokenCountString(usage.requestCount)
let tokens = UsageFormatter.tokenCountString(usage.totalTokens)
lines.append(self.labelValueLine("Usage", value: "\(requests) requests · \(tokens) tokens", useColor: useColor))
if usage.errorCount > 0 {
lines.append(self.labelValueLine(
"Results",
value: "\(usage.successCount) succeeded · \(usage.errorCount) failed",
useColor: useColor))
}
if !usage.providers.isEmpty {
let providerMix = usage.providers.prefix(5)
.map { "\($0.provider): \(UsageFormatter.tokenCountString($0.requestCount))" }
.joined(separator: " · ")
lines.append(self.labelValueLine("Routed providers", value: providerMix, useColor: useColor))
}
}
private static func appendSub2APIUsageLines(
snapshot: UsageSnapshot,
useColor: Bool,
lines: inout [String])
{
guard let usage = snapshot.sub2APIUsage else { return }
if let balance = usage.balance {
lines.append(self.labelValueLine(
"Balance",
value: UsageFormatter.currencyString(balance, currencyCode: usage.unit),
useColor: useColor))
}
if let today = usage.today {
lines.append(self.labelValueLine(
"Today",
value: self.sub2APITotalsText(today, unit: usage.unit),
useColor: useColor))
}
if let total = usage.total {
lines.append(self.labelValueLine(
"Total",
value: self.sub2APITotalsText(total, unit: usage.unit),
useColor: useColor))
}
}
private static func sub2APITotalsText(_ totals: Sub2APIUsageDetails.Totals, unit: String) -> String {
"\(UsageFormatter.tokenCountString(totals.requests)) requests · " +
"\(UsageFormatter.tokenCountString(totals.totalTokens)) tokens · " +
UsageFormatter.currencyString(totals.actualCostUSD, currencyCode: unit)
}
private static func appendWayfinderUsageLines(
snapshot: UsageSnapshot,
useColor: Bool,
lines: inout [String])
{
guard let usage = snapshot.wayfinderUsage else { return }
lines.append(self.labelValueLine("Gateway", value: usage.gatewaySummary, useColor: useColor))
if let routed = usage.routedSummary {
lines.append(self.labelValueLine("Routed", value: routed, useColor: useColor))
}
if let saved = usage.savedSummary {
lines.append(self.labelValueLine("Saved", value: saved, useColor: useColor))
}
if let avgDecision = usage.avgDecisionSummary {
lines.append(self.labelValueLine("Avg decision", value: avgDecision, useColor: useColor))
}
}
private enum CrossModelMetric {
case tokens
case requests
}
private static func crossModelUsageLine(
title: String,
usage: CrossModelUsageSnapshot,
window: CrossModelUsageWindow,
metric: CrossModelMetric,
useColor: Bool) -> String
{
let metricText = switch metric {
case .tokens:
"\(UsageFormatter.tokenCountString(window.totalTokens)) tokens"
case .requests:
"\(UsageFormatter.tokenCountString(window.requestCount)) requests"
}
return self.labelValueLine(
title,
value: "\(usage.currencyString(window.cost)) · \(metricText)",
useColor: useColor)
}
private static func appendTertiaryLines(
snapshot: UsageSnapshot,
labels: RateWindowLabels,
context: RenderContext,
now: Date,
lines: inout [String])
{
guard labels.showsTertiary, let opus = snapshot.tertiary else { return }
lines.append(self.rateLine(title: labels.tertiary, window: opus, useColor: context.useColor))
if let reset = self.resetLine(for: opus, style: context.resetStyle, now: now) {
lines.append(self.subtleLine(reset, useColor: context.useColor))
}
}
private static func appendDeepgramLines(
snapshot: UsageSnapshot,
useColor: Bool,
lines: inout [String])
{
guard let usage = snapshot.deepgramUsage else { return }
for line in usage.displayLines {
let parts = line.split(separator: ":", maxSplits: 1).map(String.init)
if parts.count == 2 {
lines.append(self.labelValueLine(
parts[0].trimmingCharacters(in: .whitespacesAndNewlines),
value: parts[1].trimmingCharacters(in: .whitespacesAndNewlines),
useColor: useColor))
} else {
lines.append(self.labelValueLine("Usage", value: line, useColor: useColor))
}
}
}
private static func appendAmpBalanceLines(
snapshot: UsageSnapshot,
useColor: Bool,
lines: inout [String])
{
guard let usage = snapshot.ampUsage else { return }
if let individualCredits = usage.individualCredits {
lines.append(self.labelValueLine(
"Individual credits",
value: UsageFormatter.currencyString(individualCredits, currencyCode: "USD"),
useColor: useColor))
}
for workspace in usage.workspaceBalances {
lines.append(self.labelValueLine(
"Workspace \(workspace.name)",
value: UsageFormatter.currencyString(workspace.remaining, currencyCode: "USD"),
useColor: useColor))
}
}
private struct RateWindowLabels {
let primary: String
let secondary: String
let tertiary: String
let showsTertiary: Bool
}
private static func rateWindowLabels(
provider: UsageProvider,
metadata: ProviderMetadata,
snapshot: UsageSnapshot) -> RateWindowLabels
{
if provider == .factory, snapshot.tertiary != nil {
return RateWindowLabels(
primary: "5-hour",
secondary: "Weekly",
tertiary: "Monthly",
showsTertiary: true)
}
let primaryLabel = if provider == .grok {
GrokProviderDescriptor.primaryLabel(window: snapshot.primary) ?? metadata.sessionLabel
} else if provider == .sub2api {
Sub2APIProviderDescriptor.primaryLabel(details: snapshot.sub2APIUsage) ?? metadata.sessionLabel
} else {
metadata.sessionLabel
}
return RateWindowLabels(
primary: primaryLabel,
secondary: metadata.weeklyLabel,
tertiary: metadata.opusLabel ?? "Sonnet",
showsTertiary: metadata.supportsOpus)
}
private static func appendCreditsLine(
provider: UsageProvider,
credits: CreditsSnapshot?,
useColor: Bool,
lines: inout [String])
{
guard provider == .codex, let credits else { return }
let remaining = credits.codexCreditLimit?.remaining ?? credits.remaining
lines.append(self.labelValueLine(
"Credits",
value: UsageFormatter.creditsString(from: remaining),
useColor: useColor))
}
private static func appendCodexResetCreditsLine(
provider: UsageProvider,
snapshot: UsageSnapshot,
now: Date,
useColor: Bool,
lines: inout [String])
{
guard provider == .codex, let resetCredits = snapshot.codexResetCredits else { return }
let inventory = resetCredits.availableInventory(at: now)
let value = if inventory.count == 1 {
"1 available"
} else {
"\(inventory.count) available"
}
lines.append(self.labelValueLine("Limit Reset Credits", value: value, useColor: useColor))
guard let expiresAt = inventory.nextExpiringCredit?.expiresAt
else {
return
}
let expiry = UsageFormatter.resetCountdownDescription(from: expiresAt, now: now)
lines.append(self.subtleLine("Next reset credit expires \(expiry)", useColor: useColor))
}
private static func appendLimitsUnavailableLine(
provider: UsageProvider,
snapshot: UsageSnapshot,
useColor: Bool,
lines: inout [String])
{
guard snapshot.rateLimitsUnavailable(for: provider) else { return }
lines.append(self.labelValueLine("Limits", value: "not available", useColor: useColor))
}
private static func appendIdentityAndNotes(
provider: UsageProvider,
snapshot: UsageSnapshot,
context: RenderContext,
lines: inout [String])
{
if let email = snapshot.accountEmail(for: provider), !email.isEmpty {
lines.append(self.labelValueLine("Account", value: email, useColor: context.useColor))
}
if provider == .kilo {
let kiloLogin = self.kiloLoginParts(snapshot: snapshot)
if let pass = kiloLogin.pass {
let cleaned = UsageFormatter.cleanPlanName(pass)
lines.append(self.labelValueLine("Plan", value: cleaned, useColor: context.useColor))
}
for detail in kiloLogin.details {
lines.append(self.labelValueLine("Activity", value: detail, useColor: context.useColor))
}
} else if let plan = snapshot.loginMethod(for: provider),
!plan.isEmpty,
provider != .mimo || !plan.localizedCaseInsensitiveContains("balance:")
{
let displayPlan = if provider == .codex {
CodexPlanFormatting.displayName(plan) ?? plan
} else if provider == .claude,
plan.hasPrefix("Claude "),
ClaudePlan.fromCompatibilityLoginMethod(plan) != nil
{
plan
} else {
self.nonCodexPlanDisplay(provider: provider, plan: plan)
}
lines.append(self.labelValueLine("Plan", value: displayPlan, useColor: context.useColor))
}
for note in context.notes {
let trimmed = note.trimmingCharacters(in: .whitespacesAndNewlines)
guard !trimmed.isEmpty else { continue }
lines.append(self.labelValueLine("Note", value: trimmed, useColor: context.useColor))
}
}
private static func nonCodexPlanDisplay(provider: UsageProvider, plan: String) -> String {
if provider == .gemini || provider == .mimo {
return UsageFormatter.cleanPlanName(plan)
}
return plan.capitalized
}
// swiftlint:disable:next function_parameter_count
private static func appendRateWindowLines(
provider: UsageProvider,
title: String,
window: RateWindow,
paceKind: PaceKind?,
context: RenderContext,
now: Date,
lines: inout [String])
{
lines.append(self.rateLine(title: title, window: window, useColor: context.useColor))
if let paceKind,
let pace = self.paceLine(
provider: provider,
window: window,
kind: paceKind,
weeklyWorkDays: context.weeklyWorkDays,
useColor: context.useColor,
now: now)
{
lines.append(pace)
}
self.appendResetAndDetailLines(
provider: provider,
window: window,
context: context,
now: now,
lines: &lines)
}
private static func appendResetAndDetailLines(
provider: UsageProvider,
window: RateWindow,
context: RenderContext,
now: Date,
lines: inout [String])
{
if self.usesDetailBackedWindow(provider: provider) {
if let reset = self.resetLineForDetailBackedWindow(window: window, style: context.resetStyle, now: now) {
lines.append(self.subtleLine(reset, useColor: context.useColor))
}
if let detail = self.detailLineForDetailBackedWindow(window: window) {
lines.append(self.subtleLine(detail, useColor: context.useColor))
}
return
}
if let reset = self.resetLine(for: window, style: context.resetStyle, now: now) {
lines.append(self.subtleLine(reset, useColor: context.useColor))
}
}
private static func resetLine(for window: RateWindow, style: ResetTimeDisplayStyle, now: Date) -> String? {
UsageFormatter.resetLine(for: window, style: style, now: now)
}
private static func usesDetailBackedWindow(provider: UsageProvider) -> Bool {
switch provider {
case .warp, .kilo, .mistral, .deepseek, .qoder, .crof:
true
default:
false
}
}
private static func resetLineForDetailBackedWindow(
window: RateWindow,
style: ResetTimeDisplayStyle,
now: Date) -> String?
{
// Some provider snapshots use resetDescription for non-reset detail.
// Only render "Resets ..." when a concrete reset date exists.
guard window.resetsAt != nil else { return nil }
let resetOnlyWindow = RateWindow(
usedPercent: window.usedPercent,
windowMinutes: window.windowMinutes,
resetsAt: window.resetsAt,
resetDescription: nil)
return UsageFormatter.resetLine(for: resetOnlyWindow, style: style, now: now)
}
private static func detailLineForDetailBackedWindow(window: RateWindow) -> String? {
guard let desc = window.resetDescription else { return nil }
let trimmed = desc.trimmingCharacters(in: .whitespacesAndNewlines)
return trimmed.isEmpty ? nil : trimmed
}
private static func kiloLoginParts(snapshot: UsageSnapshot) -> (pass: String?, details: [String]) {
guard let loginMethod = snapshot.loginMethod(for: .kilo) else {
return (nil, [])
}
let parts = loginMethod
.components(separatedBy: "·")
.map { $0.trimmingCharacters(in: .whitespacesAndNewlines) }
.filter { !$0.isEmpty }
guard !parts.isEmpty else {
return (nil, [])
}
let first = parts[0]
if self.isKiloActivitySegment(first) {
return (nil, parts)
}
return (first, Array(parts.dropFirst()))
}
private static func isKiloActivitySegment(_ text: String) -> Bool {
let normalized = text.trimmingCharacters(in: .whitespacesAndNewlines).lowercased()
return normalized.hasPrefix("auto top-up:")
}
private static func headerLine(_ header: String, useColor: Bool) -> String {
let decorated = "== \(header) =="
guard useColor else { return decorated }
return self.ansi(self.accentBoldColor, decorated)
}
private static func labelValueLine(_ label: String, value: String, useColor: Bool) -> String {
let labelText = self.label(label, useColor: useColor)
return "\(labelText): \(value)"
}
private static func label(_ text: String, useColor: Bool) -> String {
guard useColor else { return text }
return self.ansi(self.accentColor, text)
}
private static func subtleLine(_ text: String, useColor: Bool) -> String {
guard useColor else { return text }
return self.ansi(self.subtleColor, text)
}
private static func usageBar(remainingPercent: Double, useColor: Bool) -> String {
let clamped = max(0, min(100, remainingPercent))
let rawFilled = Int((clamped / 100) * Double(Self.usageBarWidth))
let filled = max(0, min(Self.usageBarWidth, rawFilled))
let empty = max(0, Self.usageBarWidth - filled)
let bar = "[\(String(repeating: "=", count: filled))\(String(repeating: "-", count: empty))]"
guard useColor else { return bar }
return self.ansi(self.accentColor, bar)
}
/// .session mirrors the GUI's session pace (5h window, real session windows only); .weekly reads
/// weeklyProgressWorkDays from the GUI's UserDefaults (same key) and passes it to UsagePace.weekly,
/// so the baseline matches the menu bar when the setting is configured. Codex historical refinement
/// is not applied (fixed allowlist only), so it can still differ from the menu for Codex accounts.
private enum PaceKind {
case session
case weekly
var defaultWindowMinutes: Int {
switch self {
case .session: 300
case .weekly: 10080
}
}
func supports(provider: UsageProvider) -> Bool {
switch self {
case .session:
provider == .codex || provider == .claude || provider == .ollama
case .weekly:
provider == .codex || provider == .claude || provider == .opencode || provider == .ollama
}
}
}
private static func computePace(
provider: UsageProvider,
window: RateWindow,
kind: PaceKind,
weeklyWorkDays: Int? = nil,
now: Date) -> UsagePace?
{
guard kind.supports(provider: provider) else { return nil }
// Only pace a real session window here; Claude w/o 5-hour data falls a 7-day window into primary.
if case .session = kind, let minutes = window.windowMinutes, minutes > 300 {
return nil
}
if provider == .ollama, window.windowMinutes == nil {
return nil
}
guard window.remainingPercent > 0 else { return nil }
// workDays applies only to the weekly (10 080-min) window; UsagePace.weekly ignores it for other durations.
let workDays = kind == .weekly ? weeklyWorkDays : nil
guard let pace = UsagePace.weekly(
window: window,
now: now,
defaultWindowMinutes: kind.defaultWindowMinutes,
workDays: workDays) else { return nil }
guard pace.expectedUsedPercent >= Self.paceMinimumExpectedPercent else { return nil }
return pace
}
private static func paceSummary(
provider: UsageProvider,
for pace: UsagePace,
kind: PaceKind,
now: Date) -> String
{
let expected = Int(pace.expectedUsedPercent.rounded())
var parts: [String] = []
parts.append(Self.paceLeftLabel(for: pace))
parts.append("Expected \(expected)% used")
if let rightLabel = Self.paceRightLabel(provider: provider, for: pace, kind: kind, now: now) {
parts.append(rightLabel)
}
return parts.joined(separator: " | ")
}
private static func paceLine(
provider: UsageProvider,
window: RateWindow,
kind: PaceKind,
weeklyWorkDays: Int? = nil,
useColor: Bool,
now: Date) -> String?
{
guard let pace = self.computePace(
provider: provider,
window: window,
kind: kind,
weeklyWorkDays: weeklyWorkDays,
now: now) else { return nil }
let label = self.label("Pace", useColor: useColor)
return "\(label): \(self.paceSummary(provider: provider, for: pace, kind: kind, now: now))"
}
private static func pacePayload(
provider: UsageProvider,
window: RateWindow,
kind: PaceKind,
weeklyWorkDays: Int? = nil,
now: Date) -> PacePayload?
{
guard let pace = self.computePace(
provider: provider,
window: window,
kind: kind,
weeklyWorkDays: weeklyWorkDays,
now: now) else { return nil }
return PacePayload(
stage: Self.stageString(pace.stage),
deltaPercent: pace.deltaPercent.rounded(),
expectedUsedPercent: pace.expectedUsedPercent.rounded(),
willLastToReset: pace.willLastToReset,
etaSeconds: pace.etaSeconds.map { $0.rounded() },
runOutProbability: pace.runOutProbability,
summary: self.paceSummary(provider: provider, for: pace, kind: kind, now: now))
}
private static func stageString(_ stage: UsagePace.Stage) -> String {
switch stage {
case .farAhead: "farAhead"
case .ahead: "ahead"
case .slightlyAhead: "slightlyAhead"
case .onTrack: "onTrack"
case .slightlyBehind: "slightlyBehind"
case .behind: "behind"
case .farBehind: "farBehind"
}
}
private static func paceLeftLabel(for pace: UsagePace) -> String {
let deltaValue = Int(abs(pace.deltaPercent).rounded())
switch pace.stage {
case .onTrack:
return "On pace"
case .slightlyAhead, .ahead, .farAhead:
return "\(deltaValue)% in deficit"
case .slightlyBehind, .behind, .farBehind:
return "\(deltaValue)% in reserve"
}
}
private static func paceRightLabel(
provider: UsageProvider,
for pace: UsagePace,
kind: PaceKind,
now: Date) -> String?
{
if pace.willLastToReset {
return self.combinedLastsLabel(for: pace, provider: provider)
}
guard let etaSeconds = pace.etaSeconds else { return nil }
let etaText = Self.paceDurationText(seconds: etaSeconds, now: now)
switch kind {
case .session:
return etaText == "now" ? "Projected empty now" : "Projected empty in \(etaText)"
case .weekly:
return etaText == "now" ? "Runs out now" : "Runs out in \(etaText)"
}
}
private static func combinedLastsLabel(for pace: UsagePace, provider: UsageProvider) -> String {
guard provider == .codex else { return "Lasts until reset" }
guard let speedLabel = speedHintLabel(for: pace) else {
return "Lasts until reset"
}
return "Lasts until reset | \(speedLabel)"
}
private static func speedHintLabel(for pace: UsagePace) -> String? {
guard pace.deltaPercent < -15,
let multiplier = pace.speedMultiplierToReset,
multiplier >= 1.5
else { return nil }
return "1.5× headroom"
}
private static func paceDurationText(seconds: TimeInterval, now: Date) -> String {
let date = now.addingTimeInterval(seconds)
let countdown = UsageFormatter.resetCountdownDescription(from: date, now: now)
if countdown == "now" {
return "now"
}
if countdown.hasPrefix("in ") {
return String(countdown.dropFirst(3))
}
return countdown
}
private static func colorizeUsage(_ text: String, remainingPercent: Double, useColor: Bool) -> String {
guard useColor else { return text }
let code = switch remainingPercent {
case ..<10:
"31" // red
case ..<25:
"33" // yellow
default:
"32" // green
}
return self.ansi(code, text)
}
private static func colorize(
_ text: String,
indicator: ProviderStatusPayload.ProviderStatusIndicator,
useColor: Bool)
-> String
{
guard useColor else { return text }
let code = switch indicator {
case .none: "32" // green
case .minor: "33" // yellow
case .major, .critical: "31" // red
case .maintenance: "34" // blue
case .unknown: "90" // gray
}
return self.ansi(code, text)
}
private static func ansi(_ code: String, _ text: String) -> String {
"\u{001B}[\(code)m\(text)\u{001B}[0m"
}
}
struct RenderContext {
let header: String
let status: ProviderStatusPayload?
let useColor: Bool
let resetStyle: ResetTimeDisplayStyle
let weeklyWorkDays: Int?
let notes: [String]
init(
header: String,
status: ProviderStatusPayload?,
useColor: Bool,
resetStyle: ResetTimeDisplayStyle,
weeklyWorkDays: Int? = nil,
notes: [String] = [])
{
self.header = header
self.status = status
self.useColor = useColor
self.resetStyle = resetStyle
self.weeklyWorkDays = weeklyWorkDays
self.notes = notes
}
}