Files
steipete--codexbar/Sources/CodexBar/Providers/Codex/CodexLimitResetOwnerKey.swift
T
2026-07-13 12:22:33 +08:00

85 lines
3.5 KiB
Swift

import CodexBarCore
import CryptoKit
import Foundation
struct CodexLimitResetOwnerKey: Equatable, Hashable, Sendable {
let rawValue: String
init?(identity: CodexIdentity, accountEmail: String?) {
guard case let .providerAccount(id) = identity,
let normalizedID = CodexOpenAIWorkspaceResolver.normalizeWorkspaceAccountID(id),
let normalizedEmail = CodexIdentityResolver.normalizeEmail(accountEmail)
else {
return nil
}
let input = "codex-limit-reset-owner:v2\0\(normalizedID)\0\(normalizedEmail)"
let digest = SHA256.hash(data: Data(input.utf8))
self.rawValue = digest.map { String(format: "%02x", $0) }.joined()
}
}
struct CodexSessionQuotaOwnerKey: Equatable, Sendable {
let rawValue: String
init?(refreshGuard: CodexAccountScopedRefreshGuard) {
let input: String
switch refreshGuard.identity {
case let .providerAccount(id):
guard let normalizedID = CodexOpenAIWorkspaceResolver.normalizeWorkspaceAccountID(id),
let normalizedEmail = CodexIdentityResolver.normalizeEmail(refreshGuard.accountKey)
else {
return nil
}
input = "codex-session-quota-owner:v1\0provider\0\(normalizedID)\0\(normalizedEmail)"
case let .emailOnly(normalizedEmail):
guard let email = CodexIdentityResolver.normalizeEmail(normalizedEmail) else { return nil }
if let accountKey = CodexIdentityResolver.normalizeEmail(refreshGuard.accountKey), accountKey != email {
return nil
}
guard let sourceKey = Self.sourceKey(refreshGuard.source) else { return nil }
// Email-only auth cannot distinguish same-email workspaces. Include the credential fingerprint
// and deliberately establish a new baseline after rotation rather than risk a cross-account alert.
guard let fingerprint = CodexAuthFingerprint.normalize(refreshGuard.authFingerprint) else { return nil }
input = "codex-session-quota-owner:v1\0email\0\(sourceKey)\0\(email)\0\(fingerprint)"
case .unresolved:
return nil
}
let digest = SHA256.hash(data: Data(input.utf8))
self.rawValue = digest.map { String(format: "%02x", $0) }.joined()
}
private static func sourceKey(_ source: CodexActiveSource) -> String? {
switch source {
case .liveSystem:
"live-system"
case let .managedAccount(id):
"managed:\(id.uuidString.lowercased())"
case let .profileHome(path):
CodexHomeScope.normalizedHomePath(path).map { "profile:\($0)" }
}
}
}
extension UsageStore {
func codexLimitResetOwnerKey(
expectedGuard: CodexAccountScopedRefreshGuard,
visibleAccounts _: [CodexVisibleAccount]) -> CodexLimitResetOwnerKey?
{
CodexLimitResetOwnerKey(
identity: expectedGuard.identity,
accountEmail: expectedGuard.accountKey)
}
func codexLimitResetOwnerKey(
forVisibleAccount account: CodexVisibleAccount,
visibleAccounts _: [CodexVisibleAccount]) -> CodexLimitResetOwnerKey?
{
guard let workspaceAccountID = CodexOpenAIWorkspaceResolver.normalizeWorkspaceAccountID(
account.workspaceAccountID)
else { return nil }
return CodexLimitResetOwnerKey(
identity: .providerAccount(id: workspaceAccountID),
accountEmail: account.email)
}
}