From 8736eaee1e846b95457c6203faa30e6a1977cb0a Mon Sep 17 00:00:00 2001 From: kiranmagic7 <209323973+kiranmagic7@users.noreply.github.com> Date: Sat, 25 Jul 2026 06:23:12 +0530 Subject: [PATCH 1/2] Handle claude-swap relogin status --- .../ClaudeSwap/ClaudeSwapAccountList.swift | 2 ++ .../ClaudeSwapAccountProjection.swift | 4 +++- .../CLICardsClaudeSwapTests.swift | 2 ++ .../ClaudeSwapAccountProjectionTests.swift | 1 + .../ClaudeSwapListParserTests.swift | 24 +++++++++++++++++++ 5 files changed, 32 insertions(+), 1 deletion(-) diff --git a/Sources/CodexBarCore/Providers/Claude/ClaudeSwap/ClaudeSwapAccountList.swift b/Sources/CodexBarCore/Providers/Claude/ClaudeSwap/ClaudeSwapAccountList.swift index 54c05ff846..8adaea8eac 100644 --- a/Sources/CodexBarCore/Providers/Claude/ClaudeSwap/ClaudeSwapAccountList.swift +++ b/Sources/CodexBarCore/Providers/Claude/ClaudeSwap/ClaudeSwapAccountList.swift @@ -75,6 +75,7 @@ public struct ClaudeSwapUsageWindow: Equatable, Sendable { public enum ClaudeSwapUsageStatus: Equatable, Sendable { case ok case tokenExpired + case reloginRequired case apiKey case keychainUnavailable case noCredentials @@ -85,6 +86,7 @@ public enum ClaudeSwapUsageStatus: Equatable, Sendable { switch rawValue { case "ok": self = .ok case "token_expired": self = .tokenExpired + case "relogin_required": self = .reloginRequired case "api_key": self = .apiKey case "keychain_unavailable": self = .keychainUnavailable case "no_credentials": self = .noCredentials diff --git a/Sources/CodexBarCore/Providers/Claude/ClaudeSwap/ClaudeSwapAccountProjection.swift b/Sources/CodexBarCore/Providers/Claude/ClaudeSwap/ClaudeSwapAccountProjection.swift index 447baf74ec..d04a1a1b3c 100644 --- a/Sources/CodexBarCore/Providers/Claude/ClaudeSwap/ClaudeSwapAccountProjection.swift +++ b/Sources/CodexBarCore/Providers/Claude/ClaudeSwap/ClaudeSwapAccountProjection.swift @@ -100,6 +100,8 @@ public enum ClaudeSwapAccountProjection { : nil case .tokenExpired: "Token expired. Switch to this account in claude-swap to refresh it." + case .reloginRequired: + "Re-login required. Switch to this account in claude-swap to refresh it." case .apiKey: "API-key account; subscription usage is unavailable." case .keychainUnavailable: @@ -117,7 +119,7 @@ public enum ClaudeSwapAccountProjection { switch row.usageStatus { case .ok, .apiKey, .unavailable: true - case .tokenExpired, .keychainUnavailable, .noCredentials, .unknown: + case .tokenExpired, .reloginRequired, .keychainUnavailable, .noCredentials, .unknown: false } } diff --git a/Tests/CodexBarTests/CLICardsClaudeSwapTests.swift b/Tests/CodexBarTests/CLICardsClaudeSwapTests.swift index 71b22030d4..90df3401fc 100644 --- a/Tests/CodexBarTests/CLICardsClaudeSwapTests.swift +++ b/Tests/CodexBarTests/CLICardsClaudeSwapTests.swift @@ -241,6 +241,7 @@ struct CLICardsClaudeSwapTests { let statuses: [ClaudeSwapUsageStatus] = [ .apiKey, .tokenExpired, + .reloginRequired, .keychainUnavailable, .noCredentials, .unavailable, @@ -265,6 +266,7 @@ struct CLICardsClaudeSwapTests { #expect(output.cards.map(\.accountProblem) == [ "API-key account; subscription usage is unavailable.", "Token expired. Switch to this account in claude-swap to refresh it.", + "Re-login required. Switch to this account in claude-swap to refresh it.", "claude-swap could not read the active account's Keychain entry.", "No stored credentials for this account slot.", "Usage fetch failed.", diff --git a/Tests/CodexBarTests/ClaudeSwapAccountProjectionTests.swift b/Tests/CodexBarTests/ClaudeSwapAccountProjectionTests.swift index 38388b18ea..567a069eca 100644 --- a/Tests/CodexBarTests/ClaudeSwapAccountProjectionTests.swift +++ b/Tests/CodexBarTests/ClaudeSwapAccountProjectionTests.swift @@ -76,6 +76,7 @@ struct ClaudeSwapAccountProjectionTests { func `maps sentinel statuses to per account errors without usage`() throws { let rows: [(ClaudeSwapUsageStatus, String)] = [ (.tokenExpired, "Token expired"), + (.reloginRequired, "Re-login required"), (.apiKey, "API-key account"), (.keychainUnavailable, "Keychain"), (.noCredentials, "No stored credentials"), diff --git a/Tests/CodexBarTests/ClaudeSwapListParserTests.swift b/Tests/CodexBarTests/ClaudeSwapListParserTests.swift index 421bea4a5b..9c9815604e 100644 --- a/Tests/CodexBarTests/ClaudeSwapListParserTests.swift +++ b/Tests/CodexBarTests/ClaudeSwapListParserTests.swift @@ -143,6 +143,30 @@ struct ClaudeSwapListParserTests { ]) } + @Test + func `projects relogin required status to recovery guidance`() throws { + let json = """ + { + "schemaVersion": 1, + "activeAccountNumber": null, + "accounts": [ + { + "number": 1, + "email": "expired@example.com", + "active": false, + "usageStatus": "relogin_required", + "usage": null + } + ] + } + """ + + let list = try self.parse(json) + let account = try #require(ClaudeSwapAccountProjection.accountSnapshots(from: list).first) + #expect(account.error == "Re-login required. Switch to this account in claude-swap to refresh it.") + #expect(account.canActivate == false) + } + @Test func `surfaces schema v1 error envelope`() throws { let json = """ From 24815a358bba8105ee3685cc69f28efaf39a2197 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Wed, 29 Jul 2026 08:46:44 -0700 Subject: [PATCH 2/2] fix: clarify claude-swap relogin guidance --- .../Claude/ClaudeSwap/ClaudeSwapAccountProjection.swift | 2 +- Tests/CodexBarTests/CLICardsClaudeSwapTests.swift | 2 +- Tests/CodexBarTests/ClaudeSwapListParserTests.swift | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Sources/CodexBarCore/Providers/Claude/ClaudeSwap/ClaudeSwapAccountProjection.swift b/Sources/CodexBarCore/Providers/Claude/ClaudeSwap/ClaudeSwapAccountProjection.swift index d04a1a1b3c..439caebe07 100644 --- a/Sources/CodexBarCore/Providers/Claude/ClaudeSwap/ClaudeSwapAccountProjection.swift +++ b/Sources/CodexBarCore/Providers/Claude/ClaudeSwap/ClaudeSwapAccountProjection.swift @@ -101,7 +101,7 @@ public enum ClaudeSwapAccountProjection { case .tokenExpired: "Token expired. Switch to this account in claude-swap to refresh it." case .reloginRequired: - "Re-login required. Switch to this account in claude-swap to refresh it." + "Re-login required. Re-authenticate this account in claude-swap." case .apiKey: "API-key account; subscription usage is unavailable." case .keychainUnavailable: diff --git a/Tests/CodexBarTests/CLICardsClaudeSwapTests.swift b/Tests/CodexBarTests/CLICardsClaudeSwapTests.swift index 90df3401fc..78f11e627d 100644 --- a/Tests/CodexBarTests/CLICardsClaudeSwapTests.swift +++ b/Tests/CodexBarTests/CLICardsClaudeSwapTests.swift @@ -266,7 +266,7 @@ struct CLICardsClaudeSwapTests { #expect(output.cards.map(\.accountProblem) == [ "API-key account; subscription usage is unavailable.", "Token expired. Switch to this account in claude-swap to refresh it.", - "Re-login required. Switch to this account in claude-swap to refresh it.", + "Re-login required. Re-authenticate this account in claude-swap.", "claude-swap could not read the active account's Keychain entry.", "No stored credentials for this account slot.", "Usage fetch failed.", diff --git a/Tests/CodexBarTests/ClaudeSwapListParserTests.swift b/Tests/CodexBarTests/ClaudeSwapListParserTests.swift index 9c9815604e..d94f484b20 100644 --- a/Tests/CodexBarTests/ClaudeSwapListParserTests.swift +++ b/Tests/CodexBarTests/ClaudeSwapListParserTests.swift @@ -163,7 +163,7 @@ struct ClaudeSwapListParserTests { let list = try self.parse(json) let account = try #require(ClaudeSwapAccountProjection.accountSnapshots(from: list).first) - #expect(account.error == "Re-login required. Switch to this account in claude-swap to refresh it.") + #expect(account.error == "Re-login required. Re-authenticate this account in claude-swap.") #expect(account.canActivate == false) }