项目文件夹

文件
2026-07-13 12:22:33 +08:00

1356 行
51 KiB
Swift

此文件含有模棱两可的 Unicode 字符
此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。
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
}
}