import Foundation /// Resolves Bedrock signing credentials + region for the configured auth mode. /// /// Shared by the main usage fetch (`BedrockAPIFetchStrategy`) and the daily /// cost-history refresh (`CostUsageFetcher`) so both honor AWS-profile auth /// identically instead of the history path silently requiring static keys. enum BedrockCredentialResolver { struct Resolved { let credentials: BedrockAWSSigner.Credentials let region: String } static func resolve( environment: [String: String], resolveAWSBinary: ([String: String]) -> String? = { BinaryLocator.resolveAWSBinary(env: $0) }, makeProvider: (String) -> BedrockProfileCredentialProvider = { BedrockProfileCredentialProvider.live(awsBinaryPath: $0) }) async throws -> Resolved { switch BedrockSettingsReader.authMode(environment: environment) { case .keys: guard let accessKeyID = BedrockSettingsReader.accessKeyID(environment: environment), let secretAccessKey = BedrockSettingsReader.secretAccessKey(environment: environment) else { throw BedrockUsageError.missingCredentials } let credentials = BedrockAWSSigner.Credentials( accessKeyID: accessKeyID, secretAccessKey: secretAccessKey, sessionToken: BedrockSettingsReader.sessionToken(environment: environment)) return Resolved( credentials: credentials, region: BedrockSettingsReader.region(environment: environment)) case .profile: guard let profile = BedrockSettingsReader.profile(environment: environment) else { throw BedrockUsageError.missingCredentials } guard let awsBinary = resolveAWSBinary(environment) else { throw BedrockUsageError.awsCLINotFound } let cliEnvironment = Self.profileCLIEnvironment(environment) let provider = makeProvider(awsBinary) let credentials = try await provider.exportCredentials(profile: profile, environment: cliEnvironment) let region = try await Self.resolveRegion( provider: provider, profile: profile, environment: cliEnvironment) return Resolved(credentials: credentials, region: region) } } private static func profileCLIEnvironment(_ environment: [String: String]) -> [String: String] { var cliEnvironment = environment // `--profile` selects the requested profile. Leaving AWS_PROFILE set can // break assume-role profiles that use `credential_source = Environment`; // keep the source credential variables themselves available. cliEnvironment[BedrockSettingsReader.profileKey] = nil return cliEnvironment } private static func resolveRegion( provider: BedrockProfileCredentialProvider, profile: String, environment: [String: String]) async throws -> String { if let explicit = BedrockSettingsReader.cleaned(environment[BedrockSettingsReader.regionKeys[0]]) ?? BedrockSettingsReader.cleaned(environment[BedrockSettingsReader.regionKeys[1]]) { return explicit } if let derived = try await provider.resolveRegion(profile: profile, environment: environment) { return derived } return BedrockSettingsReader.defaultRegion } }