import Foundation public enum TokenAccountInjection: Sendable { case cookieHeader case environment(key: String)} public struct TokenAccountSupport: Sendable { public let title: String public let subtitle: String public let placeholder: String public let injection: TokenAccountInjection public let requiresManualCookieSource: Bool public let cookieName: String? public let environmentKeysToScrub: [String] public let minimumDelayBetweenAccountRefreshes: Duration? public init( title: String, subtitle: String, placeholder: String, injection: TokenAccountInjection, requiresManualCookieSource: Bool, cookieName: String?, environmentKeysToScrub: [String] = [], minimumDelayBetweenAccountRefreshes: Duration? = nil) { self.title = title self.subtitle = subtitle self.placeholder = placeholder self.injection = injection self.requiresManualCookieSource = requiresManualCookieSource self.cookieName = cookieName self.environmentKeysToScrub = environmentKeysToScrub self.minimumDelayBetweenAccountRefreshes = minimumDelayBetweenAccountRefreshes }} public enum TokenAccountSupportCatalog { public static func support(for provider: UsageProvider) -> TokenAccountSupport? { supportByProvider[provider] } public static func envOverride(for provider: UsageProvider, token: String) -> [String: String]? { guard let support = self.support(for: provider) else { return nil } switch support.injection { case let .environment(key): return [key: token] case .cookieHeader: if provider == .claude, case let route = ClaudeCredentialRouting.resolve(tokenAccountToken: token, manualCookieHeader: nil) { switch route { case let .oauth(accessToken): return [ClaudeOAuthCredentialsStore.environmentTokenKey: accessToken] case let .adminAPIKey(apiKey): return [ClaudeAdminAPISettingsReader.adminAPIKeyEnvironmentKey: apiKey] case .none, .webCookie: break } } return nil } } public static func scrubEnvironmentForSelectedAccount( _ environment: inout [String: String], provider: UsageProvider, token _: String) { guard let support = self.support(for: provider) else { return } switch support.injection { case let .environment(key): environment.removeValue(forKey: key) for key in support.environmentKeysToScrub { environment.removeValue(forKey: key) } case .cookieHeader: guard provider == .claude else { return } environment.removeValue(forKey: ClaudeOAuthCredentialsStore.environmentTokenKey) for key in ClaudeAdminAPISettingsReader.apiKeyEnvironmentKeys { environment.removeValue(forKey: key) } } } public static func normalizedCookieHeader(for provider: UsageProvider, token: String) -> String { guard let support = self.support(for: provider) else { return token.trimmingCharacters(in: .whitespacesAndNewlines) } return self.normalizedCookieHeader(token, support: support) } public static func isClaudeOAuthToken(_ token: String) -> Bool { ClaudeCredentialRouting.resolve(tokenAccountToken: token, manualCookieHeader: nil).isOAuth } public static func normalizedCookieHeader(_ token: String, support: TokenAccountSupport) -> String { let trimmed = token.trimmingCharacters(in: .whitespacesAndNewlines) guard let cookieName = support.cookieName else { return trimmed } let lower = trimmed.lowercased() if lower.contains("cookie:") || trimmed.contains("=") { return trimmed } return "\(cookieName)=\(trimmed)" }}
import Foundation
public enum TokenAccountInjection: Sendable {
case cookieHeader
case environment(key: String)
}
public struct TokenAccountSupport: Sendable {
public let title: String
public let subtitle: String
public let placeholder: String
public let injection: TokenAccountInjection
public let requiresManualCookieSource: Bool
public let cookieName: String?
public let environmentKeysToScrub: [String]
public let minimumDelayBetweenAccountRefreshes: Duration?
public init(
title: String,
subtitle: String,
placeholder: String,
injection: TokenAccountInjection,
requiresManualCookieSource: Bool,
cookieName: String?,
environmentKeysToScrub: [String] = [],
minimumDelayBetweenAccountRefreshes: Duration? = nil)
{
self.title = title
self.subtitle = subtitle
self.placeholder = placeholder
self.injection = injection
self.requiresManualCookieSource = requiresManualCookieSource
self.cookieName = cookieName
self.environmentKeysToScrub = environmentKeysToScrub
self.minimumDelayBetweenAccountRefreshes = minimumDelayBetweenAccountRefreshes
}
}
public enum TokenAccountSupportCatalog {
public static func support(for provider: UsageProvider) -> TokenAccountSupport? {
supportByProvider[provider]
}
public static func envOverride(for provider: UsageProvider, token: String) -> [String: String]? {
guard let support = self.support(for: provider) else { return nil }
switch support.injection {
case let .environment(key):
return [key: token]
case .cookieHeader:
if provider == .claude,
case let route = ClaudeCredentialRouting.resolve(tokenAccountToken: token, manualCookieHeader: nil)
{
switch route {
case let .oauth(accessToken):
return [ClaudeOAuthCredentialsStore.environmentTokenKey: accessToken]
case let .adminAPIKey(apiKey):
return [ClaudeAdminAPISettingsReader.adminAPIKeyEnvironmentKey: apiKey]
case .none, .webCookie:
break
}
}
return nil
}
}
public static func scrubEnvironmentForSelectedAccount(
_ environment: inout [String: String],
provider: UsageProvider,
token _: String)
{
guard let support = self.support(for: provider) else { return }
switch support.injection {
case let .environment(key):
environment.removeValue(forKey: key)
for key in support.environmentKeysToScrub {
environment.removeValue(forKey: key)
}
case .cookieHeader:
guard provider == .claude else { return }
environment.removeValue(forKey: ClaudeOAuthCredentialsStore.environmentTokenKey)
for key in ClaudeAdminAPISettingsReader.apiKeyEnvironmentKeys {
environment.removeValue(forKey: key)
}
}
}
public static func normalizedCookieHeader(for provider: UsageProvider, token: String) -> String {
guard let support = self.support(for: provider) else {
return token.trimmingCharacters(in: .whitespacesAndNewlines)
}
return self.normalizedCookieHeader(token, support: support)
}
public static func isClaudeOAuthToken(_ token: String) -> Bool {
ClaudeCredentialRouting.resolve(tokenAccountToken: token, manualCookieHeader: nil).isOAuth
}
public static func normalizedCookieHeader(_ token: String, support: TokenAccountSupport) -> String {
let trimmed = token.trimmingCharacters(in: .whitespacesAndNewlines)
guard let cookieName = support.cookieName else { return trimmed }
let lower = trimmed.lowercased()
if lower.contains("cookie:") || trimmed.contains("=") {
return trimmed
}
return "\(cookieName)=\(trimmed)"
}
}