import Foundation#if canImport(Security)import Security#endif public enum ProviderTokenSource: String, Sendable { case keychain case environment} public struct ProviderTokenResolution: Sendable { public let token: String public let source: ProviderTokenSource public init(token: String, source: ProviderTokenSource) { self.token = token self.source = source }} public enum ProviderTokenResolver { private static let log = CodexBarLog.logger("provider-token") private static let keychainService = "com.steipete.CodexBar" private static let zaiAccount = "zai-api-token" private static let copilotAccount = "copilot-api-token" private static let minimaxAccount = "minimax-cookie" public static func zaiToken(environment: [String: String] = ProcessInfo.processInfo.environment) -> String? { self.zaiResolution(environment: environment)?.token } public static func copilotToken(environment: [String: String] = ProcessInfo.processInfo.environment) -> String? { self.copilotResolution(environment: environment)?.token } public static func minimaxCookie(environment: [String: String] = ProcessInfo.processInfo.environment) -> String? { self.minimaxResolution(environment: environment)?.token } public static func zaiResolution( environment: [String: String] = ProcessInfo.processInfo.environment) -> ProviderTokenResolution? { if let token = self.keychainToken(service: self.keychainService, account: self.zaiAccount) { return ProviderTokenResolution(token: token, source: .keychain) } if let token = ZaiSettingsReader.apiToken(environment: environment) { return ProviderTokenResolution(token: token, source: .environment) } return nil } public static func copilotResolution( environment: [String: String] = ProcessInfo.processInfo.environment) -> ProviderTokenResolution? { if let token = self.keychainToken(service: self.keychainService, account: self.copilotAccount) { return ProviderTokenResolution(token: token, source: .keychain) } if let token = self.cleaned(environment["COPILOT_API_TOKEN"]) { return ProviderTokenResolution(token: token, source: .environment) } return nil } public static func minimaxResolution( environment: [String: String] = ProcessInfo.processInfo.environment) -> ProviderTokenResolution? { if let token = self.keychainToken(service: self.keychainService, account: self.minimaxAccount) { return ProviderTokenResolution(token: token, source: .keychain) } if let token = MiniMaxSettingsReader.cookieHeader(environment: environment) { return ProviderTokenResolution(token: token, source: .environment) } return nil } private static func cleaned(_ raw: String?) -> String? { guard var value = raw?.trimmingCharacters(in: .whitespacesAndNewlines), !value.isEmpty else { return nil } if (value.hasPrefix("\"") && value.hasSuffix("\"")) || (value.hasPrefix("'") && value.hasSuffix("'")) { value.removeFirst() value.removeLast() } value = value.trimmingCharacters(in: .whitespacesAndNewlines) return value.isEmpty ? nil : value } private static func keychainToken(service: String, account: String) -> String? { #if canImport(Security) var result: CFTypeRef? let query: [String: Any] = [ kSecClass as String: kSecClassGenericPassword, kSecAttrService as String: service, kSecAttrAccount as String: account, kSecMatchLimit as String: kSecMatchLimitOne, kSecReturnData as String: true, ] let status = SecItemCopyMatching(query as CFDictionary, &result) if status == errSecItemNotFound { return nil } guard status == errSecSuccess else { self.log.error("Keychain read failed: \(status)") return nil } guard let data = result as? Data, let token = String(data: data, encoding: .utf8)? .trimmingCharacters(in: .whitespacesAndNewlines), !token.isEmpty else { return nil } return token #else _ = service _ = account return nil #endif }}
import Foundation
#if canImport(Security)
import Security
#endif
public enum ProviderTokenSource: String, Sendable {
case keychain
case environment
}
public struct ProviderTokenResolution: Sendable {
public let token: String
public let source: ProviderTokenSource
public init(token: String, source: ProviderTokenSource) {
self.token = token
self.source = source
}
}
public enum ProviderTokenResolver {
private static let log = CodexBarLog.logger("provider-token")
private static let keychainService = "com.steipete.CodexBar"
private static let zaiAccount = "zai-api-token"
private static let copilotAccount = "copilot-api-token"
private static let minimaxAccount = "minimax-cookie"
public static func zaiToken(environment: [String: String] = ProcessInfo.processInfo.environment) -> String? {
self.zaiResolution(environment: environment)?.token
}
public static func copilotToken(environment: [String: String] = ProcessInfo.processInfo.environment) -> String? {
self.copilotResolution(environment: environment)?.token
}
public static func minimaxCookie(environment: [String: String] = ProcessInfo.processInfo.environment) -> String? {
self.minimaxResolution(environment: environment)?.token
}
public static func zaiResolution(
environment: [String: String] = ProcessInfo.processInfo.environment) -> ProviderTokenResolution?
{
if let token = self.keychainToken(service: self.keychainService, account: self.zaiAccount) {
return ProviderTokenResolution(token: token, source: .keychain)
}
if let token = ZaiSettingsReader.apiToken(environment: environment) {
return ProviderTokenResolution(token: token, source: .environment)
}
return nil
}
public static func copilotResolution(
environment: [String: String] = ProcessInfo.processInfo.environment) -> ProviderTokenResolution?
{
if let token = self.keychainToken(service: self.keychainService, account: self.copilotAccount) {
return ProviderTokenResolution(token: token, source: .keychain)
}
if let token = self.cleaned(environment["COPILOT_API_TOKEN"]) {
return ProviderTokenResolution(token: token, source: .environment)
}
return nil
}
public static func minimaxResolution(
environment: [String: String] = ProcessInfo.processInfo.environment) -> ProviderTokenResolution?
{
if let token = self.keychainToken(service: self.keychainService, account: self.minimaxAccount) {
return ProviderTokenResolution(token: token, source: .keychain)
}
if let token = MiniMaxSettingsReader.cookieHeader(environment: environment) {
return ProviderTokenResolution(token: token, source: .environment)
}
return nil
}
private static func cleaned(_ raw: String?) -> String? {
guard var value = raw?.trimmingCharacters(in: .whitespacesAndNewlines), !value.isEmpty else {
return nil
}
if (value.hasPrefix("\"") && value.hasSuffix("\"")) ||
(value.hasPrefix("'") && value.hasSuffix("'"))
{
value.removeFirst()
value.removeLast()
}
value = value.trimmingCharacters(in: .whitespacesAndNewlines)
return value.isEmpty ? nil : value
}
private static func keychainToken(service: String, account: String) -> String? {
#if canImport(Security)
var result: CFTypeRef?
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: service,
kSecAttrAccount as String: account,
kSecMatchLimit as String: kSecMatchLimitOne,
kSecReturnData as String: true,
]
let status = SecItemCopyMatching(query as CFDictionary, &result)
if status == errSecItemNotFound {
return nil
}
guard status == errSecSuccess else {
self.log.error("Keychain read failed: \(status)")
return nil
}
guard let data = result as? Data,
let token = String(data: data, encoding: .utf8)?
.trimmingCharacters(in: .whitespacesAndNewlines),
!token.isEmpty
else {
return nil
}
return token
#else
_ = service
_ = account
return nil
#endif
}
}