Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Sources/CodexBar/UsageStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -492,6 +492,7 @@ final class UsageStore {
self.timerTask?.cancel()
self.tokenTimerTask?.cancel()
self.tokenRefreshSequenceTask?.cancel()
self.pathDebugRefreshTask?.cancel()
}

enum SessionQuotaWindowSource: String {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1067,8 +1067,17 @@ public enum ClaudeOAuthCredentialsStore {
promptMode: promptMode),
!data.isEmpty
{
// Same as above: store fingerprint after interactive read to avoid background "sync" reads.
self.saveClaudeKeychainFingerprint(self.currentClaudeKeychainFingerprintWithoutPrompt())
// Build fingerprint from the legacy candidate directly rather than using
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could saving the legacy candidate's fingerprint here get out of sync with the fingerprint that later change detection treats as current when duplicate keychain entries exist?

// currentClaudeKeychainFingerprintWithoutPrompt(), which prefers the newest
// candidate and may return a fingerprint for a different keychain item.
if let legacyCandidate = self.claudeKeychainLegacyCandidateWithoutPrompt(
promptMode: promptMode)
{
self.saveClaudeKeychainFingerprint(ClaudeKeychainFingerprint(
modifiedAt: legacyCandidate.modifiedAt.map { Int($0.timeIntervalSince1970) },
createdAt: legacyCandidate.createdAt.map { Int($0.timeIntervalSince1970) },
persistentRefHash: Self.sha256Prefix(legacyCandidate.persistentRef)))
}
return data
}
} catch let error as ClaudeOAuthCredentialsError {
Expand Down Expand Up @@ -1592,7 +1601,19 @@ extension ClaudeOAuthCredentialsStore {
let creds = try? ClaudeOAuthCredentials.parse(data: legacyData),
!creds.isExpired
{
self.saveClaudeKeychainFingerprint(self.currentClaudeKeychainFingerprintWithoutPrompt())
// Build fingerprint from the legacy candidate directly, matching the candidate
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this fallback path end up with the same mismatch too, where we save one item's fingerprint but later background checks compare against another?

// path above. currentClaudeKeychainFingerprintWithoutPrompt() prefers the newest
// candidate and may return a fingerprint for a different keychain item, causing
// stale comparisons on subsequent background sync checks.
if let legacyCandidate = self.claudeKeychainLegacyCandidateWithoutPrompt(
promptMode: fallbackPromptMode)
{
let legacyFingerprint = ClaudeKeychainFingerprint(
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be worth pulling this fingerprint construction into a small helper? I'm wondering if that would also help with the file-length lint failure on this file.

modifiedAt: legacyCandidate.modifiedAt.map { Int($0.timeIntervalSince1970) },
createdAt: legacyCandidate.createdAt.map { Int($0.timeIntervalSince1970) },
persistentRefHash: Self.sha256Prefix(legacyCandidate.persistentRef))
self.saveClaudeKeychainFingerprint(legacyFingerprint)
}
self.writeMemoryCache(
record: ClaudeOAuthCredentialRecord(credentials: creds, owner: .claudeCLI, source: .memoryCache),
timestamp: now)
Expand Down