import Foundation public struct ProviderTokenAccount: Codable, Identifiable, Sendable { public let id: UUID public let label: String public let token: String public let addedAt: TimeInterval public let lastUsed: TimeInterval? /// Stable provider-specific identity (e.g. GitHub `login`) used for /// re-auth deduplication. Optional so legacy accounts keep working. public let externalIdentifier: String? /// Optional provider-specific usage scope. z.ai uses `personal` / `team`. public let usageScope: String? /// Optional provider-specific organization/workspace target. Claude web /// sessionKey accounts use this to disambiguate linked Anthropic emails. /// z.ai team accounts use this for the BigModel organization header. public let organizationID: String? /// Optional provider-specific workspace/project target. z.ai team accounts /// use this for the BigModel project header. public let workspaceID: String? enum CodingKeys: String, CodingKey { case id case label case token case addedAt case lastUsed case externalIdentifier case usageScope case organizationID = "organizationId" case workspaceID } public init( id: UUID, label: String, token: String, addedAt: TimeInterval, lastUsed: TimeInterval?, externalIdentifier: String? = nil, usageScope: String? = nil, organizationID: String? = nil, workspaceID: String? = nil) { self.id = id self.label = label self.token = token self.addedAt = addedAt self.lastUsed = lastUsed self.externalIdentifier = externalIdentifier self.usageScope = usageScope self.organizationID = organizationID self.workspaceID = workspaceID } public var displayName: String { self.label } public var sanitizedOrganizationID: String? { Self.clean(self.organizationID) } public var sanitizedUsageScope: String? { Self.clean(self.usageScope) } public var sanitizedWorkspaceID: String? { Self.clean(self.workspaceID) } private static func clean(_ raw: String?) -> String? { let trimmed = raw?.trimmingCharacters(in: .whitespacesAndNewlines) return (trimmed?.isEmpty ?? true) ? nil : trimmed }} public struct ProviderTokenAccountData: Codable, Sendable { public let version: Int public let accounts: [ProviderTokenAccount] public let activeIndex: Int public init(version: Int, accounts: [ProviderTokenAccount], activeIndex: Int) { self.version = version self.accounts = accounts self.activeIndex = activeIndex } public func clampedActiveIndex() -> Int { guard !self.accounts.isEmpty else { return 0 } return min(max(self.activeIndex, 0), self.accounts.count - 1) }} private struct ProviderTokenAccountsFile: Codable { let version: Int let providers: [String: ProviderTokenAccountData]} public protocol ProviderTokenAccountStoring: Sendable { func loadAccounts() throws -> [UsageProvider: ProviderTokenAccountData] func storeAccounts(_ accounts: [UsageProvider: ProviderTokenAccountData]) throws func ensureFileExists() throws -> URL} public struct FileTokenAccountStore: ProviderTokenAccountStoring, @unchecked Sendable { private let fileURL: URL private let fileManager: FileManager public init(fileURL: URL = Self.defaultURL(), fileManager: FileManager = .default) { self.fileURL = fileURL self.fileManager = fileManager } public func loadAccounts() throws -> [UsageProvider: ProviderTokenAccountData] { guard self.fileManager.fileExists(atPath: self.fileURL.path) else { return [:] } let data = try Data(contentsOf: self.fileURL) let decoder = JSONDecoder() let decoded = try decoder.decode(ProviderTokenAccountsFile.self, from: data) var result: [UsageProvider: ProviderTokenAccountData] = [:] for (key, value) in decoded.providers { guard let provider = UsageProvider(rawValue: key) else { continue } result[provider] = value } return result } public func storeAccounts(_ accounts: [UsageProvider: ProviderTokenAccountData]) throws { let payload = ProviderTokenAccountsFile( version: 1, providers: Dictionary(uniqueKeysWithValues: accounts.map { ($0.key.rawValue, $0.value) })) let encoder = JSONEncoder() encoder.outputFormatting = [.prettyPrinted, .sortedKeys] let data = try encoder.encode(payload) let directory = self.fileURL.deletingLastPathComponent() if !self.fileManager.fileExists(atPath: directory.path) { try self.fileManager.createDirectory(at: directory, withIntermediateDirectories: true) } try data.write(to: self.fileURL, options: [.atomic]) try self.applySecurePermissionsIfNeeded() } public func ensureFileExists() throws -> URL { if self.fileManager.fileExists(atPath: self.fileURL.path) { return self.fileURL } try self.storeAccounts([:]) return self.fileURL } private func applySecurePermissionsIfNeeded() throws { #if os(macOS) try self.fileManager.setAttributes([ .posixPermissions: NSNumber(value: Int16(0o600)), ], ofItemAtPath: self.fileURL.path) #endif } public static func defaultURL() -> URL { let base = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask).first ?? FileManager.default.homeDirectoryForCurrentUser return base .appendingPathComponent("CodexBar", isDirectory: true) .appendingPathComponent("token-accounts.json") }}
import Foundation
public struct ProviderTokenAccount: Codable, Identifiable, Sendable {
public let id: UUID
public let label: String
public let token: String
public let addedAt: TimeInterval
public let lastUsed: TimeInterval?
/// Stable provider-specific identity (e.g. GitHub `login`) used for
/// re-auth deduplication. Optional so legacy accounts keep working.
public let externalIdentifier: String?
/// Optional provider-specific usage scope. z.ai uses `personal` / `team`.
public let usageScope: String?
/// Optional provider-specific organization/workspace target. Claude web
/// sessionKey accounts use this to disambiguate linked Anthropic emails.
/// z.ai team accounts use this for the BigModel organization header.
public let organizationID: String?
/// Optional provider-specific workspace/project target. z.ai team accounts
/// use this for the BigModel project header.
public let workspaceID: String?
enum CodingKeys: String, CodingKey {
case id
case label
case token
case addedAt
case lastUsed
case externalIdentifier
case usageScope
case organizationID = "organizationId"
case workspaceID
}
public init(
id: UUID,
label: String,
token: String,
addedAt: TimeInterval,
lastUsed: TimeInterval?,
externalIdentifier: String? = nil,
usageScope: String? = nil,
organizationID: String? = nil,
workspaceID: String? = nil)
{
self.id = id
self.label = label
self.token = token
self.addedAt = addedAt
self.lastUsed = lastUsed
self.externalIdentifier = externalIdentifier
self.usageScope = usageScope
self.organizationID = organizationID
self.workspaceID = workspaceID
}
public var displayName: String {
self.label
}
public var sanitizedOrganizationID: String? {
Self.clean(self.organizationID)
}
public var sanitizedUsageScope: String? {
Self.clean(self.usageScope)
}
public var sanitizedWorkspaceID: String? {
Self.clean(self.workspaceID)
}
private static func clean(_ raw: String?) -> String? {
let trimmed = raw?.trimmingCharacters(in: .whitespacesAndNewlines)
return (trimmed?.isEmpty ?? true) ? nil : trimmed
}
}
public struct ProviderTokenAccountData: Codable, Sendable {
public let version: Int
public let accounts: [ProviderTokenAccount]
public let activeIndex: Int
public init(version: Int, accounts: [ProviderTokenAccount], activeIndex: Int) {
self.version = version
self.accounts = accounts
self.activeIndex = activeIndex
}
public func clampedActiveIndex() -> Int {
guard !self.accounts.isEmpty else { return 0 }
return min(max(self.activeIndex, 0), self.accounts.count - 1)
}
}
private struct ProviderTokenAccountsFile: Codable {
let version: Int
let providers: [String: ProviderTokenAccountData]
}
public protocol ProviderTokenAccountStoring: Sendable {
func loadAccounts() throws -> [UsageProvider: ProviderTokenAccountData]
func storeAccounts(_ accounts: [UsageProvider: ProviderTokenAccountData]) throws
func ensureFileExists() throws -> URL
}
public struct FileTokenAccountStore: ProviderTokenAccountStoring, @unchecked Sendable {
private let fileURL: URL
private let fileManager: FileManager
public init(fileURL: URL = Self.defaultURL(), fileManager: FileManager = .default) {
self.fileURL = fileURL
self.fileManager = fileManager
}
public func loadAccounts() throws -> [UsageProvider: ProviderTokenAccountData] {
guard self.fileManager.fileExists(atPath: self.fileURL.path) else { return [:] }
let data = try Data(contentsOf: self.fileURL)
let decoder = JSONDecoder()
let decoded = try decoder.decode(ProviderTokenAccountsFile.self, from: data)
var result: [UsageProvider: ProviderTokenAccountData] = [:]
for (key, value) in decoded.providers {
guard let provider = UsageProvider(rawValue: key) else { continue }
result[provider] = value
}
return result
}
public func storeAccounts(_ accounts: [UsageProvider: ProviderTokenAccountData]) throws {
let payload = ProviderTokenAccountsFile(
version: 1,
providers: Dictionary(uniqueKeysWithValues: accounts.map { ($0.key.rawValue, $0.value) }))
let encoder = JSONEncoder()
encoder.outputFormatting = [.prettyPrinted, .sortedKeys]
let data = try encoder.encode(payload)
let directory = self.fileURL.deletingLastPathComponent()
if !self.fileManager.fileExists(atPath: directory.path) {
try self.fileManager.createDirectory(at: directory, withIntermediateDirectories: true)
}
try data.write(to: self.fileURL, options: [.atomic])
try self.applySecurePermissionsIfNeeded()
}
public func ensureFileExists() throws -> URL {
if self.fileManager.fileExists(atPath: self.fileURL.path) { return self.fileURL }
try self.storeAccounts([:])
return self.fileURL
}
private func applySecurePermissionsIfNeeded() throws {
#if os(macOS)
try self.fileManager.setAttributes([
.posixPermissions: NSNumber(value: Int16(0o600)),
], ofItemAtPath: self.fileURL.path)
#endif
}
public static func defaultURL() -> URL {
let base = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask).first
?? FileManager.default.homeDirectoryForCurrentUser
return base
.appendingPathComponent("CodexBar", isDirectory: true)
.appendingPathComponent("token-accounts.json")
}
}