import Foundation#if canImport(FoundationNetworking)import FoundationNetworking#endif public struct CopilotUsageFetcher: Sendable { private let token: String public init(token: String) { self.token = token } public func fetch() async throws -> UsageSnapshot { guard let url = URL(string: "https://api.github.com/copilot_internal/user") else { throw URLError(.badURL) } var request = URLRequest(url: url) // Use the GitHub OAuth token directly, not the Copilot token. request.setValue("token \(self.token)", forHTTPHeaderField: "Authorization") self.addCommonHeaders(to: &request) let (data, response) = try await URLSession.shared.data(for: request) guard let httpResponse = response as? HTTPURLResponse else { throw URLError(.badServerResponse) } if httpResponse.statusCode == 401 || httpResponse.statusCode == 403 { throw URLError(.userAuthenticationRequired) } guard httpResponse.statusCode == 200 else { throw URLError(.badServerResponse) } let usage = try JSONDecoder().decode(CopilotUsageResponse.self, from: data) let primary = self.makeRateWindow(from: usage.quotaSnapshots.premiumInteractions) let secondary = self.makeRateWindow(from: usage.quotaSnapshots.chat) let identity = ProviderIdentitySnapshot( providerID: .copilot, accountEmail: nil, accountOrganization: nil, loginMethod: usage.copilotPlan.capitalized) return UsageSnapshot( primary: primary ?? .init(usedPercent: 0, windowMinutes: nil, resetsAt: nil, resetDescription: nil), secondary: secondary, tertiary: nil, providerCost: nil, updatedAt: Date(), identity: identity) } private func addCommonHeaders(to request: inout URLRequest) { request.setValue("application/json", forHTTPHeaderField: "Accept") request.setValue("vscode/1.96.2", forHTTPHeaderField: "Editor-Version") request.setValue("copilot-chat/0.26.7", forHTTPHeaderField: "Editor-Plugin-Version") request.setValue("GitHubCopilotChat/0.26.7", forHTTPHeaderField: "User-Agent") request.setValue("2025-04-01", forHTTPHeaderField: "X-Github-Api-Version") } private func makeRateWindow(from snapshot: CopilotUsageResponse.QuotaSnapshot?) -> RateWindow? { guard let snapshot else { return nil } // percent_remaining is 0-100 based on the JSON example in the web app source let usedPercent = max(0, 100 - snapshot.percentRemaining) return RateWindow( usedPercent: usedPercent, windowMinutes: nil, // Not provided resetsAt: nil, // Not provided per-quota in the simplified snapshot resetDescription: nil) }}
import Foundation
#if canImport(FoundationNetworking)
import FoundationNetworking
#endif
public struct CopilotUsageFetcher: Sendable {
private let token: String
public init(token: String) {
self.token = token
}
public func fetch() async throws -> UsageSnapshot {
guard let url = URL(string: "https://api.github.com/copilot_internal/user") else {
throw URLError(.badURL)
}
var request = URLRequest(url: url)
// Use the GitHub OAuth token directly, not the Copilot token.
request.setValue("token \(self.token)", forHTTPHeaderField: "Authorization")
self.addCommonHeaders(to: &request)
let (data, response) = try await URLSession.shared.data(for: request)
guard let httpResponse = response as? HTTPURLResponse else {
throw URLError(.badServerResponse)
}
if httpResponse.statusCode == 401 || httpResponse.statusCode == 403 {
throw URLError(.userAuthenticationRequired)
}
guard httpResponse.statusCode == 200 else {
throw URLError(.badServerResponse)
}
let usage = try JSONDecoder().decode(CopilotUsageResponse.self, from: data)
let primary = self.makeRateWindow(from: usage.quotaSnapshots.premiumInteractions)
let secondary = self.makeRateWindow(from: usage.quotaSnapshots.chat)
let identity = ProviderIdentitySnapshot(
providerID: .copilot,
accountEmail: nil,
accountOrganization: nil,
loginMethod: usage.copilotPlan.capitalized)
return UsageSnapshot(
primary: primary ?? .init(usedPercent: 0, windowMinutes: nil, resetsAt: nil, resetDescription: nil),
secondary: secondary,
tertiary: nil,
providerCost: nil,
updatedAt: Date(),
identity: identity)
}
private func addCommonHeaders(to request: inout URLRequest) {
request.setValue("application/json", forHTTPHeaderField: "Accept")
request.setValue("vscode/1.96.2", forHTTPHeaderField: "Editor-Version")
request.setValue("copilot-chat/0.26.7", forHTTPHeaderField: "Editor-Plugin-Version")
request.setValue("GitHubCopilotChat/0.26.7", forHTTPHeaderField: "User-Agent")
request.setValue("2025-04-01", forHTTPHeaderField: "X-Github-Api-Version")
}
private func makeRateWindow(from snapshot: CopilotUsageResponse.QuotaSnapshot?) -> RateWindow? {
guard let snapshot else { return nil }
// percent_remaining is 0-100 based on the JSON example in the web app source
let usedPercent = max(0, 100 - snapshot.percentRemaining)
return RateWindow(
usedPercent: usedPercent,
windowMinutes: nil, // Not provided
resetsAt: nil, // Not provided per-quota in the simplified snapshot
resetDescription: nil)
}
}