Skip to content
Merged
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 CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
- Settings: edit saved token-account labels and credentials in place and reorder accounts without changing the active account (#4612). Thanks @lghsigma597!
### Fixed

- OpenCode Go: honor the app's configured source and show the source that supplied usage (#4616). Thanks @ArietidsZ!
- Pi: keep cache scope stable when the same session roots are discovered in a different order (#4618). Thanks @dhruva77!
- Claude cost history: preserve the last valid cache when transcript roots or directories are unreadable (#4617). Thanks @dhruva77!
- Codex: open the Usage Dashboard on the overview tab to avoid incorrect redirects (#4607). Thanks @Alex-Keyes!
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,19 @@ struct OpenCodeGoProviderImplementation: ProviderImplementation {

@MainActor
func presentation(context _: ProviderPresentationContext) -> ProviderPresentation {
ProviderPresentation { _ in "web" }
ProviderPresentation { context in
context.store.sourceLabel(for: context.provider)
}
}

@MainActor
func defaultSourceLabel(context: ProviderSourceLabelContext) -> String? {
context.settings.providerConfig(for: .opencodego)?.source?.rawValue
}

@MainActor
func sourceMode(context: ProviderSourceModeContext) -> ProviderSourceMode {
context.settings.providerConfig(for: .opencodego)?.source ?? .auto
}

@MainActor
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,7 @@ import Foundation
extension SettingsStore {
var opencodegoWorkspaceID: String {
get { self.configSnapshot.providerConfig(for: .opencodego)?.workspaceID ?? "" }
set {
let trimmed = newValue.trimmingCharacters(in: .whitespacesAndNewlines)
let value = trimmed.isEmpty ? nil : trimmed
self.updateProviderConfig(provider: .opencodego) { entry in
entry.workspaceID = value
}
}
set { self[providerConfig: .opencodego, field: .workspace] = newValue }
}

var opencodegoCookieHeader: String {
Expand All @@ -34,20 +28,10 @@ extension SettingsStore {
{
let cookieSettings: ProviderSettingsSnapshot.CookieProviderSettings = self.resolvedCookieSettings(
provider: .opencodego,
configuredSource: self.opencodegoCookieSource,
configuredHeader: self.opencodegoCookieHeader,
tokenOverride: tokenOverride)
return ProviderSettingsSnapshot.OpenCodeProviderSettings(
cookieSource: cookieSettings.cookieSource,
manualCookieHeader: cookieSettings.manualCookieHeader,
workspaceID: self.opencodegoSnapshotWorkspaceID)
}

private var opencodegoSnapshotWorkspaceID: String? {
guard let workspaceID = self.configSnapshot.providerConfig(for: .opencodego)?.workspaceID else {
return nil
}
let trimmed = workspaceID.trimmingCharacters(in: .whitespacesAndNewlines)
return trimmed.isEmpty ? nil : trimmed
workspaceID: self.providerConfig(for: .opencodego)?.sanitizedWorkspaceID)
}
}
49 changes: 49 additions & 0 deletions Tests/CodexBarTests/OpenCodeGoAppSourceTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import CodexBarCore
import Testing
@testable import CodexBar

@MainActor
@Suite(.serialized)
struct OpenCodeGoAppSourceTests {
@Test(arguments: [ProviderSourceMode.auto, .api, .web])
func `app honors configured source even when manual cookies exist`(source: ProviderSourceMode) async throws {
let fixture = try ProviderSettingsDescriptorTests().makeSettingsFixture(
suite: "OpenCodeGoAppSourceTests-\(source.rawValue)")
fixture.settings.updateProviderConfig(provider: .opencodego) { config in
config.source = source
config.cookieSource = .manual
config.cookieHeader = "Cookie: invalid"
config.apiKey = "fixture-api-key"
}

let resolved = ProviderRegistry.resolvedSourceMode(
provider: .opencodego, settings: fixture.settings, account: nil)
#expect(resolved == source)
let spec = try #require(fixture.store.providerSpecs[.opencodego])
let fetchContext = spec.makeFetchContext()
#expect(fetchContext.sourceMode == source)
#expect(fetchContext.settings?.opencodego?.cookieSource == .manual)
let strategies = await spec.descriptor.fetchPlan.pipeline.resolveStrategies(fetchContext)
if source == .api {
#expect(strategies.map(\.id) == ["opencodego.api"])
} else if source == .web {
#expect(strategies.map(\.id) == ["opencodego.web"])
} else {
#expect(strategies.map(\.id) == ["opencodego.web", "opencodego.local", "opencodego.api"])
}
}

@Test
func `settings header shows configured or last successful source instead of web`() throws {
let fixture = try ProviderSettingsDescriptorTests().makeSettingsFixture(
suite: "OpenCodeGoAppSourceTests-header")
fixture.settings.updateProviderConfig(provider: .opencodego) { $0.source = .api }
let context = fixture.presentationContext(
provider: .opencodego, metadata: OpenCodeGoProviderDescriptor.descriptor.metadata)
let presentation = OpenCodeGoProviderImplementation().presentation(context: context)

#expect(presentation.detailLine(context) == "api")
fixture.store.lastSourceLabels[.opencodego] = "local+api"
#expect(presentation.detailLine(context) == "local+api")
}
}
1 change: 1 addition & 0 deletions docs/opencode.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ usage is a separate [OpenAI provider](openai.md), not Codex subscription quota.
overlays authoritative API windows when an API key is configured, then falls back through the API and legacy web
sources when local history is unavailable. Auto stays web-first when a Cookie token account, manual cookie, or workspace
override scopes the request, because local history is device-wide.
- The app honors an explicit API or Web source even when a manual Cookie is configured. Its source label shows the configured source before a successful fetch, then the source that actually supplied the result.
- The local monthly window is an estimate anchored at the earliest local row and can drift from the real billing
cycle. The local strategy prefers API-reported rolling/weekly/monthly percentages and reset timestamps. When no API
key is configured, a cached, manual, or automatically discovered session can overlay Console quotas (plus Zen
Expand Down