Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 35 additions & 5 deletions CopilotMonitor/CLI/main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,16 @@ struct JSONFormatter {
providerDict["usagePercentage"] = result.usage.usagePercentage
}

// Z.AI: include both token and MCP usage percentages
if identifier == .zaiCodingPlan {
if let tokenPercent = result.details?.tokenUsagePercent {
providerDict["tokenUsagePercent"] = tokenPercent
}
if let mcpPercent = result.details?.mcpUsagePercent {
providerDict["mcpUsagePercent"] = mcpPercent
}
}

if identifier == .geminiCLI, let accounts = result.details?.geminiAccounts, !accounts.isEmpty {
var accountsArray: [[String: Any]] = []
for account in accounts {
Expand All @@ -61,6 +71,7 @@ struct JSONFormatter {
accountDict["email"] = account.email
accountDict["remainingPercentage"] = account.remainingPercentage
accountDict["modelBreakdown"] = account.modelBreakdown
accountDict["authSource"] = account.authSource
accountsArray.append(accountDict)
}
providerDict["accounts"] = accountsArray
Expand Down Expand Up @@ -107,7 +118,7 @@ struct TableFormatter {
let accounts = result.details?.geminiAccounts,
accounts.count > 1 {
for account in accounts {
output += formatGeminiAccountRow(account: account)
output += formatGeminiAccountRow(account: account, allResults: results)
output += "\n"
}
} else {
Expand Down Expand Up @@ -140,7 +151,7 @@ struct TableFormatter {
let typeStr = getProviderType(result)
let typePadded = typeStr.padding(toLength: columnWidths.type, withPad: " ", startingAt: 0)

let usageStr = formatUsagePercentage(result)
let usageStr = formatUsagePercentage(identifier: identifier, result: result)
let usagePadded = usageStr.padding(toLength: columnWidths.usage, withPad: " ", startingAt: 0)

let metricsStr = formatMetrics(result)
Expand All @@ -157,22 +168,41 @@ struct TableFormatter {
}
}

private static func formatUsagePercentage(_ result: ProviderResult) -> String {
private static func formatUsagePercentage(identifier: ProviderIdentifier, result: ProviderResult) -> String {
switch result.usage {
case .payAsYouGo:
// Pay-as-you-go doesn't have meaningful usage percentage - show dash
return "-"
case .quotaBased:
// Z.AI: show both 5h token and MCP percentages
if identifier == .zaiCodingPlan {
let percents = [result.details?.tokenUsagePercent, result.details?.mcpUsagePercent].compactMap { $0 }
if percents.count == 2 {
return percents.map { String(format: "%.0f%%", $0) }.joined(separator: ",")
}
}
let percentage = result.usage.usagePercentage
return String(format: "%.0f%%", percentage)
}
}

private static func formatGeminiAccountRow(account: GeminiAccountQuota) -> String {
private static func formatGeminiAccountRow(account: GeminiAccountQuota, allResults: [ProviderIdentifier: ProviderResult]) -> String {
let accountName = "Gemini (#\(account.accountIndex + 1))"
let providerPadded = accountName.padding(toLength: columnWidths.provider, withPad: " ", startingAt: 0)
let typePadded = "Quota-based".padding(toLength: columnWidths.type, withPad: " ", startingAt: 0)
let usageStr = String(format: "%.0f%%", 100 - account.remainingPercentage)
let geminiUsedPercent = 100 - account.remainingPercentage

// For Antigravity-sourced accounts, show both Gemini CLI % and Antigravity %
let usageStr: String
if account.authSource.lowercased().contains("antigravity"),
let antigravityResult = allResults[.antigravity],
case .quotaBased(let agRemaining, let agEntitlement, _) = antigravityResult.usage,
agEntitlement > 0 {
let antigravityUsedPercent = (Double(agEntitlement - agRemaining) / Double(agEntitlement)) * 100
usageStr = String(format: "%.0f%%,%.0f%%", geminiUsedPercent, antigravityUsedPercent)
} else {
usageStr = String(format: "%.0f%%", geminiUsedPercent)
}
let usagePadded = usageStr.padding(toLength: columnWidths.usage, withPad: " ", startingAt: 0)

let metricsStr = "\(String(format: "%.0f", account.remainingPercentage))% remaining (\(account.email))"
Expand Down
15 changes: 14 additions & 1 deletion CopilotMonitor/CopilotMonitor/App/StatusBarController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1041,9 +1041,22 @@
let sourceLabel = authSourceLabel(for: account.authSource, provider: .geminiCLI) ?? "Unknown"
displayName += " (\(sourceLabel))"
}
// Build percentage array for display
// For Antigravity-sourced accounts, show both Gemini CLI % and Antigravity %
// (same dual-window pattern as Codex)
let usedPercents: [Double]
if account.authSource.lowercased().contains("antigravity"),
let antigravityResult = providerResults[.antigravity],
case .quotaBased(let agRemaining, let agEntitlement, _) = antigravityResult.usage,
agEntitlement > 0 {
let antigravityUsedPercent = (Double(agEntitlement - agRemaining) / Double(agEntitlement)) * 100
usedPercents = [usedPercent, antigravityUsedPercent]
} else {
usedPercents = [usedPercent]
}
let item = createNativeQuotaMenuItem(
name: displayName,
usedPercent: usedPercent,
usedPercents: usedPercents,
icon: iconForProvider(.geminiCLI)
)
item.tag = 999
Expand Down Expand Up @@ -1974,7 +1987,7 @@
// 3. OpenRouter - only has current cost, no daily history
// We'll include today's cost if available
if let routerResult = providerResults[.openRouter],
case .payAsYouGo(_, let cost, _) = routerResult.usage,

Check warning on line 1990 in CopilotMonitor/CopilotMonitor/App/StatusBarController.swift

View workflow job for this annotation

GitHub Actions / Build & Test

immutable value 'cost' was never used; consider replacing with '_' or removing it

Check warning on line 1990 in CopilotMonitor/CopilotMonitor/App/StatusBarController.swift

View workflow job for this annotation

GitHub Actions / Build & Test

immutable value 'cost' was never used; consider replacing with '_' or removing it
let dailyCost = routerResult.details?.dailyUsage {
let today = Calendar.current.startOfDay(for: Date())
if aggregatedDailyCosts[today] == nil {
Expand Down Expand Up @@ -2191,7 +2204,7 @@
if prediction.confidenceLevel == .low {
let confItem = NSMenuItem()
confItem.view = createDisabledLabelView(
text: "Low prediction accuracy",

Check warning on line 2207 in CopilotMonitor/CopilotMonitor/App/StatusBarController.swift

View workflow job for this annotation

GitHub Actions / Build & Test

immutable value 'cost' was never used; consider replacing with '_' or removing it

Check warning on line 2207 in CopilotMonitor/CopilotMonitor/App/StatusBarController.swift

View workflow job for this annotation

GitHub Actions / Build & Test

immutable value 'cost' was never used; consider replacing with '_' or removing it
icon: NSImage(systemSymbolName: "exclamationmark.triangle.fill", accessibilityDescription: "Low accuracy")
)
historySubmenu.addItem(confItem)
Expand Down
Loading