From bbea42b88a3cc5dcc299fd0cdd1dc0eb0a055f1c Mon Sep 17 00:00:00 2001 From: joeVenner Date: Tue, 7 Jul 2026 01:06:23 +0100 Subject: [PATCH 1/3] feat(kimik2): add Usage Dashboard shortcut to the credits page Open the human-facing Kimi credits page (https://kimrel.com/my-credits) from the menu Usage Dashboard action instead of the bearer-token /api/user/credits endpoint, which returns HTTP 401 to an unauthenticated browser. Add a MenuDescriptor test asserting the dashboard action and URL, and update the metadata status-link test. --- .../KimiK2/KimiK2ProviderDescriptor.swift | 2 +- .../MenuDescriptorKimiK2Tests.swift | 45 +++++++++++++++++++ .../ProviderMetadataStatusLinkTests.swift | 6 ++- 3 files changed, 50 insertions(+), 3 deletions(-) create mode 100644 Tests/CodexBarTests/MenuDescriptorKimiK2Tests.swift diff --git a/Sources/CodexBarCore/Providers/KimiK2/KimiK2ProviderDescriptor.swift b/Sources/CodexBarCore/Providers/KimiK2/KimiK2ProviderDescriptor.swift index 6b7a4383a3..babcb9779d 100644 --- a/Sources/CodexBarCore/Providers/KimiK2/KimiK2ProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/KimiK2/KimiK2ProviderDescriptor.swift @@ -21,7 +21,7 @@ public enum KimiK2ProviderDescriptor { isPrimaryProvider: false, usesAccountFallback: false, browserCookieOrder: nil, - dashboardURL: nil, + dashboardURL: "https://kimrel.com/my-credits", statusPageURL: nil), branding: ProviderBranding( iconStyle: .kimi, diff --git a/Tests/CodexBarTests/MenuDescriptorKimiK2Tests.swift b/Tests/CodexBarTests/MenuDescriptorKimiK2Tests.swift new file mode 100644 index 0000000000..649f21fdc3 --- /dev/null +++ b/Tests/CodexBarTests/MenuDescriptorKimiK2Tests.swift @@ -0,0 +1,45 @@ +import CodexBarCore +import Foundation +import Testing +@testable import CodexBar + +@MainActor +struct MenuDescriptorKimiK2Tests { + @Test + func `kimi K2 menu exposes the usage dashboard action`() throws { + let suite = "MenuDescriptorKimiK2Tests-dashboard" + let defaults = try #require(UserDefaults(suiteName: suite)) + defaults.removePersistentDomain(forName: suite) + + let settings = SettingsStore( + userDefaults: defaults, + configStore: testConfigStore(suiteName: suite), + zaiTokenStore: NoopZaiTokenStore(), + syntheticTokenStore: NoopSyntheticTokenStore()) + settings.statusChecksEnabled = false + let store = UsageStore( + fetcher: UsageFetcher(environment: [:]), + browserDetection: BrowserDetection(cacheTTL: 0), + settings: settings) + + let descriptor = MenuDescriptor.build( + provider: .kimik2, + store: store, + settings: settings, + account: AccountInfo(email: nil, plan: nil), + updateReady: false) + let actions = descriptor.sections + .flatMap(\.entries) + .compactMap { entry -> (String, MenuDescriptor.MenuAction)? in + guard case let .action(title, action) = entry else { return nil } + return (title, action) + } + + #expect(actions.contains { title, action in + title == "Usage Dashboard" && action == .dashboard + }) + #expect( + store.metadata(for: .kimik2).dashboardURL == "https://kimrel.com/my-credits", + "Dashboard action must open the human-facing credits page, not the bearer-token API endpoint") + } +} \ No newline at end of file diff --git a/Tests/CodexBarTests/ProviderMetadataStatusLinkTests.swift b/Tests/CodexBarTests/ProviderMetadataStatusLinkTests.swift index 3ca12b2336..9d8b59cf73 100644 --- a/Tests/CodexBarTests/ProviderMetadataStatusLinkTests.swift +++ b/Tests/CodexBarTests/ProviderMetadataStatusLinkTests.swift @@ -14,11 +14,13 @@ struct ProviderMetadataStatusLinkTests { } @Test - func `kimi K2 metadata does not present legacy endpoint as official`() throws { + func `kimi K2 metadata identifies the unofficial provider and its credits dashboard`() throws { let meta = try #require(ProviderDefaults.metadata[.kimik2]) #expect(meta.displayName == "Kimi K2 (unofficial)") #expect(meta.toggleTitle == "Show unofficial Kimi K2 usage") - #expect(meta.dashboardURL == nil) + // The dashboard URL must be a human-facing browser page, not the bearer-token + // credits API endpoint (which returns HTTP 401 to an unauthenticated browser). + #expect(meta.dashboardURL == "https://kimrel.com/my-credits") } } From 5c6827d7410b7ef406bf37c7f26e7108070ae05c Mon Sep 17 00:00:00 2001 From: joeVenner Date: Tue, 7 Jul 2026 01:27:50 +0100 Subject: [PATCH 2/3] test(kimik2): wire dashboard test to in-memory test stores Use testSettingsStore so the MenuDescriptor test uses in-memory/noop stores for every legacy secret and cookie store instead of leaving SettingsStore dependencies at their keychain-backed defaults. --- Tests/CodexBarTests/MenuDescriptorKimiK2Tests.swift | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/Tests/CodexBarTests/MenuDescriptorKimiK2Tests.swift b/Tests/CodexBarTests/MenuDescriptorKimiK2Tests.swift index 649f21fdc3..061b93e8d6 100644 --- a/Tests/CodexBarTests/MenuDescriptorKimiK2Tests.swift +++ b/Tests/CodexBarTests/MenuDescriptorKimiK2Tests.swift @@ -4,19 +4,14 @@ import Testing @testable import CodexBar @MainActor +@Suite(.serialized) struct MenuDescriptorKimiK2Tests { @Test func `kimi K2 menu exposes the usage dashboard action`() throws { - let suite = "MenuDescriptorKimiK2Tests-dashboard" - let defaults = try #require(UserDefaults(suiteName: suite)) - defaults.removePersistentDomain(forName: suite) - - let settings = SettingsStore( - userDefaults: defaults, - configStore: testConfigStore(suiteName: suite), - zaiTokenStore: NoopZaiTokenStore(), - syntheticTokenStore: NoopSyntheticTokenStore()) + let settings = testSettingsStore(suiteName: "MenuDescriptorKimiK2Tests-dashboard") settings.statusChecksEnabled = false + settings.refreshFrequency = .manual + settings.mergeIcons = false let store = UsageStore( fetcher: UsageFetcher(environment: [:]), browserDetection: BrowserDetection(cacheTTL: 0), From b7fb8c47cf5332c2abd99d247367a8ed8e24df4d Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Fri, 10 Jul 2026 07:46:57 +0100 Subject: [PATCH 3/3] test: tighten Kimi K2 dashboard coverage --- CHANGELOG.md | 1 + Tests/CodexBarTests/MenuDescriptorKimiK2Tests.swift | 8 ++------ docs/kimi-k2.md | 1 + 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ea9c5764f0..b37630af9e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### Added - Agent Sessions: list and focus live local or SSH-discovered Codex and Claude Code sessions from the menu and CLI. +- Kimi K2: add a Usage Dashboard shortcut to the human-facing legacy credits page. Thanks @joeVenner! - Codex: add a provider option to hide Spark quota rows without hiding credits or other extra usage (#2013). Thanks @intellectronica! - Wayfinder: add opt-in local gateway health, routing, savings, and latency usage with configurable loopback URL support. Thanks @tcballard! - Claude CLI: surface model-scoped weekly limits alongside all-model usage without duplicating matching web limits. Thanks @janpollak! diff --git a/Tests/CodexBarTests/MenuDescriptorKimiK2Tests.swift b/Tests/CodexBarTests/MenuDescriptorKimiK2Tests.swift index 061b93e8d6..6aece1734b 100644 --- a/Tests/CodexBarTests/MenuDescriptorKimiK2Tests.swift +++ b/Tests/CodexBarTests/MenuDescriptorKimiK2Tests.swift @@ -1,5 +1,4 @@ import CodexBarCore -import Foundation import Testing @testable import CodexBar @@ -7,7 +6,7 @@ import Testing @Suite(.serialized) struct MenuDescriptorKimiK2Tests { @Test - func `kimi K2 menu exposes the usage dashboard action`() throws { + func `kimi K2 menu exposes the usage dashboard action`() { let settings = testSettingsStore(suiteName: "MenuDescriptorKimiK2Tests-dashboard") settings.statusChecksEnabled = false settings.refreshFrequency = .manual @@ -33,8 +32,5 @@ struct MenuDescriptorKimiK2Tests { #expect(actions.contains { title, action in title == "Usage Dashboard" && action == .dashboard }) - #expect( - store.metadata(for: .kimik2).dashboardURL == "https://kimrel.com/my-credits", - "Dashboard action must open the human-facing credits page, not the bearer-token API endpoint") } -} \ No newline at end of file +} diff --git a/docs/kimi-k2.md b/docs/kimi-k2.md index a94beb777e..45fe2ce631 100644 --- a/docs/kimi-k2.md +++ b/docs/kimi-k2.md @@ -31,6 +31,7 @@ key for that legacy endpoint to pull your remaining balance and usage. - Credits are the billing unit; CodexBar computes used percent as `consumed / (consumed + remaining)`. - There is no explicit reset timestamp in the API, so the snapshot has no reset time. +- The menu's Usage Dashboard action opens the legacy service's human-facing credits page at `https://kimrel.com/my-credits`. - Environment variables take precedence over config. ## Key files