Includes provider integrations, settings, NSStatusItem UI, tests, Makefile, and README with screenshot. Co-authored-by: Cursor <cursoragent@cursor.com>
121 lines
4.4 KiB
Swift
121 lines
4.4 KiB
Swift
import Foundation
|
|
|
|
enum UsageMetric: String, Codable, CaseIterable, Identifiable, Sendable {
|
|
// Cursor
|
|
case cursorTotalPercentUsed
|
|
case cursorApiPercentUsed
|
|
case cursorAutoPercentUsed
|
|
case cursorPlanUsed
|
|
case cursorPlanLimit
|
|
case cursorPlanRemaining
|
|
case cursorOnDemandUsed
|
|
case cursorBillingCycleStart
|
|
case cursorBillingCycleEnd
|
|
case cursorMembershipType
|
|
|
|
// Claude OAuth
|
|
case claudeFiveHourUtilization
|
|
case claudeFiveHourResetsAt
|
|
case claudeSevenDayUtilization
|
|
case claudeSevenDayResetsAt
|
|
case claudeExtraUsage
|
|
case claudeSevenDayOpusUtilization
|
|
case claudeSevenDaySonnetUtilization
|
|
|
|
// Claude Admin API
|
|
case claudeAdminInputTokens
|
|
case claudeAdminOutputTokens
|
|
case claudeAdminCostUSD
|
|
|
|
// Cursor Admin API
|
|
case cursorAdminSpendCents
|
|
case cursorAdminMemberCount
|
|
|
|
var id: String { rawValue }
|
|
|
|
var providerID: ProviderID {
|
|
switch self {
|
|
case .cursorTotalPercentUsed, .cursorApiPercentUsed, .cursorAutoPercentUsed,
|
|
.cursorPlanUsed, .cursorPlanLimit, .cursorPlanRemaining, .cursorOnDemandUsed,
|
|
.cursorBillingCycleStart, .cursorBillingCycleEnd, .cursorMembershipType,
|
|
.cursorAdminSpendCents, .cursorAdminMemberCount:
|
|
.cursor
|
|
case .claudeFiveHourUtilization, .claudeFiveHourResetsAt, .claudeSevenDayUtilization,
|
|
.claudeSevenDayResetsAt, .claudeExtraUsage, .claudeSevenDayOpusUtilization,
|
|
.claudeSevenDaySonnetUtilization, .claudeAdminInputTokens, .claudeAdminOutputTokens,
|
|
.claudeAdminCostUSD:
|
|
.claude
|
|
}
|
|
}
|
|
|
|
var displayName: String {
|
|
switch self {
|
|
case .cursorTotalPercentUsed: "Total usage %"
|
|
case .cursorApiPercentUsed: "API usage %"
|
|
case .cursorAutoPercentUsed: "Auto usage %"
|
|
case .cursorPlanUsed: "Plan used"
|
|
case .cursorPlanLimit: "Plan limit"
|
|
case .cursorPlanRemaining: "Plan remaining"
|
|
case .cursorOnDemandUsed: "On-demand spend"
|
|
case .cursorBillingCycleStart: "Billing cycle start"
|
|
case .cursorBillingCycleEnd: "Billing cycle end"
|
|
case .cursorMembershipType: "Membership"
|
|
case .claudeFiveHourUtilization: "5-hour session %"
|
|
case .claudeFiveHourResetsAt: "5-hour reset"
|
|
case .claudeSevenDayUtilization: "Weekly usage %"
|
|
case .claudeSevenDayResetsAt: "Weekly reset"
|
|
case .claudeExtraUsage: "Extra usage"
|
|
case .claudeSevenDayOpusUtilization: "Weekly Opus %"
|
|
case .claudeSevenDaySonnetUtilization: "Weekly Sonnet %"
|
|
case .claudeAdminInputTokens: "Input tokens (7d)"
|
|
case .claudeAdminOutputTokens: "Output tokens (7d)"
|
|
case .claudeAdminCostUSD: "Cost USD (7d)"
|
|
case .cursorAdminSpendCents: "Team spend (cycle)"
|
|
case .cursorAdminMemberCount: "Team members"
|
|
}
|
|
}
|
|
|
|
static func defaults(for provider: ProviderID, location: MetricLocation) -> [UsageMetric] {
|
|
switch (provider, location) {
|
|
case (.cursor, .menuBar):
|
|
return [.cursorTotalPercentUsed]
|
|
case (.cursor, .popover):
|
|
return [
|
|
.cursorTotalPercentUsed,
|
|
.cursorApiPercentUsed,
|
|
.cursorAutoPercentUsed,
|
|
.cursorPlanUsed,
|
|
.cursorPlanLimit,
|
|
.cursorOnDemandUsed,
|
|
.cursorBillingCycleEnd,
|
|
]
|
|
case (.claude, .menuBar):
|
|
return [.claudeFiveHourUtilization]
|
|
case (.claude, .popover):
|
|
return [
|
|
.claudeFiveHourUtilization,
|
|
.claudeFiveHourResetsAt,
|
|
.claudeSevenDayUtilization,
|
|
.claudeSevenDayResetsAt,
|
|
]
|
|
}
|
|
}
|
|
|
|
static func metrics(for provider: ProviderID, authMode: AuthMode) -> [UsageMetric] {
|
|
switch (provider, authMode) {
|
|
case (.cursor, .adminAPI):
|
|
return [.cursorAdminSpendCents, .cursorAdminMemberCount]
|
|
case (.cursor, _):
|
|
return allCases.filter { $0.providerID == .cursor && !$0.rawValue.hasPrefix("cursorAdmin") }
|
|
case (.claude, .adminAPI):
|
|
return [.claudeAdminInputTokens, .claudeAdminOutputTokens, .claudeAdminCostUSD]
|
|
case (.claude, _):
|
|
return allCases.filter { $0.providerID == .claude && !$0.rawValue.hasPrefix("claudeAdmin") }
|
|
}
|
|
}
|
|
}
|
|
|
|
enum MetricLocation: String, Codable, Sendable {
|
|
case menuBar
|
|
case popover
|
|
}
|