import Foundation // MARK: - Cached Plan Info (Codable) public struct WindsurfCachedPlanInfo: Codable, Sendable { public let planName: String? public let startTimestamp: Int64? public let endTimestamp: Int64? public let usage: Usage? public let quotaUsage: QuotaUsage? public struct Usage: Codable, Sendable { public let messages: Int? public let usedMessages: Int? public let remainingMessages: Int? public let flowActions: Int? public let usedFlowActions: Int? public let remainingFlowActions: Int? public let flexCredits: Int? public let usedFlexCredits: Int? public let remainingFlexCredits: Int? } public struct QuotaUsage: Codable, Sendable { public let dailyRemainingPercent: Double? public let weeklyRemainingPercent: Double? public let dailyResetAtUnix: Int64? public let weeklyResetAtUnix: Int64? }} // MARK: - Errors & Probe #if os(macOS) import SQLite3 public enum WindsurfStatusProbeError: LocalizedError, Sendable, Equatable { case dbNotFound(String) case sqliteFailed(String) case noData case parseFailed(String) public var errorDescription: String? { switch self { case let .dbNotFound(path): "Windsurf database not found at \(path). Ensure Windsurf is installed and has been launched at least once." case let .sqliteFailed(message): "SQLite error reading Windsurf data: \(message)" case .noData: "No plan data found in Windsurf database. Sign in to Windsurf first." case let .parseFailed(message): "Could not parse Windsurf plan data: \(message)" } }} // MARK: - Probe public struct WindsurfStatusProbe: Sendable { private static let defaultDBPath: String = { let home = NSHomeDirectory() return "\(home)/Library/Application Support/Windsurf/User/globalStorage/state.vscdb" }() private static let query = "SELECT value FROM ItemTable WHERE key = 'windsurf.settings.cachedPlanInfo' LIMIT 1;" private let dbPath: String public init(dbPath: String? = nil) { self.dbPath = dbPath ?? Self.defaultDBPath } public func fetch() throws -> WindsurfCachedPlanInfo { guard FileManager.default.fileExists(atPath: self.dbPath) else { throw WindsurfStatusProbeError.dbNotFound(self.dbPath) } var db: OpaquePointer? guard sqlite3_open_v2(self.dbPath, &db, SQLITE_OPEN_READONLY, nil) == SQLITE_OK else { let message = db.flatMap { String(cString: sqlite3_errmsg($0)) } ?? "unknown error" sqlite3_close(db) throw WindsurfStatusProbeError.sqliteFailed(message) } defer { sqlite3_close(db) } sqlite3_busy_timeout(db, 250) var stmt: OpaquePointer? guard sqlite3_prepare_v2(db, Self.query, -1, &stmt, nil) == SQLITE_OK else { let message = db.flatMap { String(cString: sqlite3_errmsg($0)) } ?? "unknown error" throw WindsurfStatusProbeError.sqliteFailed(message) } defer { sqlite3_finalize(stmt) } let stepResult = sqlite3_step(stmt) guard stepResult == SQLITE_ROW else { if stepResult == SQLITE_DONE { throw WindsurfStatusProbeError.noData } let message = db.flatMap { String(cString: sqlite3_errmsg($0)) } ?? "unknown error" throw WindsurfStatusProbeError.sqliteFailed(message) } guard let jsonString = Self.decodeSQLiteValue(stmt: stmt, index: 0) else { throw WindsurfStatusProbeError.noData } guard let jsonData = jsonString.data(using: .utf8) else { throw WindsurfStatusProbeError.parseFailed("Invalid UTF-8 encoding") } do { return try JSONDecoder().decode(WindsurfCachedPlanInfo.self, from: jsonData) } catch { throw WindsurfStatusProbeError.parseFailed(error.localizedDescription) } } private static func decodeSQLiteValue(stmt: OpaquePointer?, index: Int32) -> String? { switch sqlite3_column_type(stmt, index) { case SQLITE_TEXT: guard let c = sqlite3_column_text(stmt, index) else { return nil } return String(cString: c) case SQLITE_BLOB: guard let bytes = sqlite3_column_blob(stmt, index) else { return nil } let data = Data(bytes: bytes, count: Int(sqlite3_column_bytes(stmt, index))) // VSCode/Windsurf state.vscdb schema declares value as BLOB; // only accept decodes that still parse as JSON to avoid UTF-16 mojibake. return self.decodeJSONBlob(data) default: return nil } } private static func decodeJSONBlob(_ data: Data) -> String? { for encoding in [String.Encoding.utf8, .utf16LittleEndian] { guard let decoded = String(data: data, encoding: encoding) else { continue } let trimmed = decoded.trimmingCharacters(in: .controlCharacters) guard let jsonData = trimmed.data(using: .utf8), (try? JSONSerialization.jsonObject(with: jsonData)) != nil else { continue } return trimmed } return nil }} #else // MARK: - Windsurf (Unsupported) public enum WindsurfStatusProbeError: LocalizedError, Sendable, Equatable { case notSupported public var errorDescription: String? { "Windsurf is only supported on macOS." }} public struct WindsurfStatusProbe: Sendable { public init(dbPath _: String? = nil) {} public func fetch() throws -> WindsurfCachedPlanInfo { throw WindsurfStatusProbeError.notSupported }} #endif // MARK: - Conversion to UsageSnapshot extension WindsurfCachedPlanInfo { public func toUsageSnapshot() -> UsageSnapshot { var primary: RateWindow? var secondary: RateWindow? if let quota = self.quotaUsage { // Primary: daily usage (usedPercent = 100 - dailyRemainingPercent) if let daily = quota.dailyRemainingPercent { let resetDate = quota.dailyResetAtUnix.map { Date(timeIntervalSince1970: TimeInterval($0)) } primary = RateWindow( usedPercent: max(0, min(100, 100 - daily)), windowMinutes: nil, resetsAt: resetDate, resetDescription: Self.formatResetDescription(resetDate)) } // Secondary: weekly usage if let weekly = quota.weeklyRemainingPercent { let resetDate = quota.weeklyResetAtUnix.map { Date(timeIntervalSince1970: TimeInterval($0)) } secondary = RateWindow( usedPercent: max(0, min(100, 100 - weekly)), windowMinutes: nil, resetsAt: resetDate, resetDescription: Self.formatResetDescription(resetDate)) } } if primary == nil, let usage = self.usage { primary = Self.makeUsageWindow( used: usage.usedMessages, remaining: usage.remainingMessages, total: usage.messages, unit: "messages") } if secondary == nil, let usage = self.usage { secondary = Self.makeUsageWindow( used: usage.usedFlowActions, remaining: usage.remainingFlowActions, total: usage.flowActions, unit: "flow actions") } // Identity var orgDescription: String? if let endTimestamp = self.endTimestamp { let endDate = Date(timeIntervalSince1970: TimeInterval(endTimestamp) / 1000) let formatter = DateFormatter() formatter.dateStyle = .medium formatter.timeStyle = .none orgDescription = "Expires \(formatter.string(from: endDate))" } let identity = ProviderIdentitySnapshot( providerID: .windsurf, accountEmail: nil, accountOrganization: orgDescription, loginMethod: self.planName) return UsageSnapshot( primary: primary, secondary: secondary, updatedAt: Date(), identity: identity) } private static func makeUsageWindow( used rawUsed: Int?, remaining rawRemaining: Int?, total rawTotal: Int?, unit: String) -> RateWindow? { guard let total = rawTotal, total > 0 else { return nil } let inferredUsed = rawUsed ?? rawRemaining.map { max(0, total - $0) } guard let used = inferredUsed else { return nil } let clampedUsed = max(0, min(total, used)) let usedPercent = max(0, min(100, Double(clampedUsed) / Double(total) * 100)) return RateWindow( usedPercent: usedPercent, windowMinutes: nil, resetsAt: nil, resetDescription: "\(clampedUsed) / \(total) \(unit)") } private static func formatResetDescription(_ date: Date?) -> String? { guard let date else { return nil } let now = Date() let interval = date.timeIntervalSince(now) guard interval > 0 else { return "Expired" } let hours = Int(interval / 3600) let minutes = Int((interval.truncatingRemainder(dividingBy: 3600)) / 60) if hours > 24 { let days = hours / 24 let remainingHours = hours % 24 return "Resets in \(days)d \(remainingHours)h" } else if hours > 0 { return "Resets in \(hours)h \(minutes)m" } else { return "Resets in \(minutes)m" } }}
import Foundation
// MARK: - Cached Plan Info (Codable)
public struct WindsurfCachedPlanInfo: Codable, Sendable {
public let planName: String?
public let startTimestamp: Int64?
public let endTimestamp: Int64?
public let usage: Usage?
public let quotaUsage: QuotaUsage?
public struct Usage: Codable, Sendable {
public let messages: Int?
public let usedMessages: Int?
public let remainingMessages: Int?
public let flowActions: Int?
public let usedFlowActions: Int?
public let remainingFlowActions: Int?
public let flexCredits: Int?
public let usedFlexCredits: Int?
public let remainingFlexCredits: Int?
}
public struct QuotaUsage: Codable, Sendable {
public let dailyRemainingPercent: Double?
public let weeklyRemainingPercent: Double?
public let dailyResetAtUnix: Int64?
public let weeklyResetAtUnix: Int64?
}
}
// MARK: - Errors & Probe
#if os(macOS)
import SQLite3
public enum WindsurfStatusProbeError: LocalizedError, Sendable, Equatable {
case dbNotFound(String)
case sqliteFailed(String)
case noData
case parseFailed(String)
public var errorDescription: String? {
switch self {
case let .dbNotFound(path):
"Windsurf database not found at \(path). Ensure Windsurf is installed and has been launched at least once."
case let .sqliteFailed(message):
"SQLite error reading Windsurf data: \(message)"
case .noData:
"No plan data found in Windsurf database. Sign in to Windsurf first."
case let .parseFailed(message):
"Could not parse Windsurf plan data: \(message)"
}
}
}
// MARK: - Probe
public struct WindsurfStatusProbe: Sendable {
private static let defaultDBPath: String = {
let home = NSHomeDirectory()
return "\(home)/Library/Application Support/Windsurf/User/globalStorage/state.vscdb"
}()
private static let query = "SELECT value FROM ItemTable WHERE key = 'windsurf.settings.cachedPlanInfo' LIMIT 1;"
private let dbPath: String
public init(dbPath: String? = nil) {
self.dbPath = dbPath ?? Self.defaultDBPath
}
public func fetch() throws -> WindsurfCachedPlanInfo {
guard FileManager.default.fileExists(atPath: self.dbPath) else {
throw WindsurfStatusProbeError.dbNotFound(self.dbPath)
}
var db: OpaquePointer?
guard sqlite3_open_v2(self.dbPath, &db, SQLITE_OPEN_READONLY, nil) == SQLITE_OK else {
let message = db.flatMap { String(cString: sqlite3_errmsg($0)) } ?? "unknown error"
sqlite3_close(db)
throw WindsurfStatusProbeError.sqliteFailed(message)
}
defer { sqlite3_close(db) }
sqlite3_busy_timeout(db, 250)
var stmt: OpaquePointer?
guard sqlite3_prepare_v2(db, Self.query, -1, &stmt, nil) == SQLITE_OK else {
let message = db.flatMap { String(cString: sqlite3_errmsg($0)) } ?? "unknown error"
throw WindsurfStatusProbeError.sqliteFailed(message)
}
defer { sqlite3_finalize(stmt) }
let stepResult = sqlite3_step(stmt)
guard stepResult == SQLITE_ROW else {
if stepResult == SQLITE_DONE {
throw WindsurfStatusProbeError.noData
}
let message = db.flatMap { String(cString: sqlite3_errmsg($0)) } ?? "unknown error"
throw WindsurfStatusProbeError.sqliteFailed(message)
}
guard let jsonString = Self.decodeSQLiteValue(stmt: stmt, index: 0) else {
throw WindsurfStatusProbeError.noData
}
guard let jsonData = jsonString.data(using: .utf8) else {
throw WindsurfStatusProbeError.parseFailed("Invalid UTF-8 encoding")
}
do {
return try JSONDecoder().decode(WindsurfCachedPlanInfo.self, from: jsonData)
} catch {
throw WindsurfStatusProbeError.parseFailed(error.localizedDescription)
}
}
private static func decodeSQLiteValue(stmt: OpaquePointer?, index: Int32) -> String? {
switch sqlite3_column_type(stmt, index) {
case SQLITE_TEXT:
guard let c = sqlite3_column_text(stmt, index) else { return nil }
return String(cString: c)
case SQLITE_BLOB:
guard let bytes = sqlite3_column_blob(stmt, index) else { return nil }
let data = Data(bytes: bytes, count: Int(sqlite3_column_bytes(stmt, index)))
// VSCode/Windsurf state.vscdb schema declares value as BLOB;
// only accept decodes that still parse as JSON to avoid UTF-16 mojibake.
return self.decodeJSONBlob(data)
default:
return nil
}
}
private static func decodeJSONBlob(_ data: Data) -> String? {
for encoding in [String.Encoding.utf8, .utf16LittleEndian] {
guard let decoded = String(data: data, encoding: encoding) else { continue }
let trimmed = decoded.trimmingCharacters(in: .controlCharacters)
guard let jsonData = trimmed.data(using: .utf8),
(try? JSONSerialization.jsonObject(with: jsonData)) != nil
else {
continue
}
return trimmed
}
return nil
}
}
#else
// MARK: - Windsurf (Unsupported)
public enum WindsurfStatusProbeError: LocalizedError, Sendable, Equatable {
case notSupported
public var errorDescription: String? {
"Windsurf is only supported on macOS."
}
}
public struct WindsurfStatusProbe: Sendable {
public init(dbPath _: String? = nil) {}
public func fetch() throws -> WindsurfCachedPlanInfo {
throw WindsurfStatusProbeError.notSupported
}
}
#endif
// MARK: - Conversion to UsageSnapshot
extension WindsurfCachedPlanInfo {
public func toUsageSnapshot() -> UsageSnapshot {
var primary: RateWindow?
var secondary: RateWindow?
if let quota = self.quotaUsage {
// Primary: daily usage (usedPercent = 100 - dailyRemainingPercent)
if let daily = quota.dailyRemainingPercent {
let resetDate = quota.dailyResetAtUnix.map {
Date(timeIntervalSince1970: TimeInterval($0))
}
primary = RateWindow(
usedPercent: max(0, min(100, 100 - daily)),
windowMinutes: nil,
resetsAt: resetDate,
resetDescription: Self.formatResetDescription(resetDate))
}
// Secondary: weekly usage
if let weekly = quota.weeklyRemainingPercent {
let resetDate = quota.weeklyResetAtUnix.map {
Date(timeIntervalSince1970: TimeInterval($0))
}
secondary = RateWindow(
usedPercent: max(0, min(100, 100 - weekly)),
windowMinutes: nil,
resetsAt: resetDate,
resetDescription: Self.formatResetDescription(resetDate))
}
}
if primary == nil, let usage = self.usage {
primary = Self.makeUsageWindow(
used: usage.usedMessages,
remaining: usage.remainingMessages,
total: usage.messages,
unit: "messages")
}
if secondary == nil, let usage = self.usage {
secondary = Self.makeUsageWindow(
used: usage.usedFlowActions,
remaining: usage.remainingFlowActions,
total: usage.flowActions,
unit: "flow actions")
}
// Identity
var orgDescription: String?
if let endTimestamp = self.endTimestamp {
let endDate = Date(timeIntervalSince1970: TimeInterval(endTimestamp) / 1000)
let formatter = DateFormatter()
formatter.dateStyle = .medium
formatter.timeStyle = .none
orgDescription = "Expires \(formatter.string(from: endDate))"
}
let identity = ProviderIdentitySnapshot(
providerID: .windsurf,
accountEmail: nil,
accountOrganization: orgDescription,
loginMethod: self.planName)
return UsageSnapshot(
primary: primary,
secondary: secondary,
updatedAt: Date(),
identity: identity)
}
private static func makeUsageWindow(
used rawUsed: Int?,
remaining rawRemaining: Int?,
total rawTotal: Int?,
unit: String) -> RateWindow?
{
guard let total = rawTotal, total > 0 else { return nil }
let inferredUsed = rawUsed ?? rawRemaining.map { max(0, total - $0) }
guard let used = inferredUsed else { return nil }
let clampedUsed = max(0, min(total, used))
let usedPercent = max(0, min(100, Double(clampedUsed) / Double(total) * 100))
return RateWindow(
usedPercent: usedPercent,
windowMinutes: nil,
resetsAt: nil,
resetDescription: "\(clampedUsed) / \(total) \(unit)")
}
private static func formatResetDescription(_ date: Date?) -> String? {
guard let date else { return nil }
let now = Date()
let interval = date.timeIntervalSince(now)
guard interval > 0 else { return "Expired" }
let hours = Int(interval / 3600)
let minutes = Int((interval.truncatingRemainder(dividingBy: 3600)) / 60)
if hours > 24 {
let days = hours / 24
let remainingHours = hours % 24
return "Resets in \(days)d \(remainingHours)h"
} else if hours > 0 {
return "Resets in \(hours)h \(minutes)m"
} else {
return "Resets in \(minutes)m"
}
}
}