CodexBar uses a flexible strategy pattern to fetch usage data from different sources. Each provider defines a fetch plan with an ordered pipeline of strategies that are tried in sequence until one succeeds.
Overview
The fetch system has three layers:
- Fetch Plan: Declares which source modes are supported
- Fetch Pipeline: Resolves and executes strategies in priority order
- Fetch Strategies: Individual implementations that fetch from specific sources
Source Modes
public enum ProviderSourceMode: String, CaseIterable, Sendable, Codable {
case auto // Try all available strategies in order
case web // Web scraping or browser cookies
case cli // Command-line interface
case oauth // OAuth API access
case api // API token access
}
Source Mode Filtering
- App runtime: Always uses
.auto mode (tries all strategies)
- CLI runtime: Respects
--source flag to filter strategies
- Strategies check
context.sourceMode to determine availability
ProviderFetchPlan
sourceModes
Set<ProviderSourceMode>
required
Set of source modes this provider supports
pipeline
ProviderFetchPipeline
required
The strategy resolution and execution pipeline
public struct ProviderFetchPlan: Sendable {
public let sourceModes: Set<ProviderSourceMode>
public let pipeline: ProviderFetchPipeline
public func fetchOutcome(
context: ProviderFetchContext,
provider: UsageProvider
) async -> ProviderFetchOutcome
}
ProviderFetchPipeline
The pipeline resolves strategies dynamically based on the fetch context:
public struct ProviderFetchPipeline: Sendable {
public let resolveStrategies: @Sendable (ProviderFetchContext) async -> [any ProviderFetchStrategy]
public func fetch(
context: ProviderFetchContext,
provider: UsageProvider
) async -> ProviderFetchOutcome
}
Pipeline Execution
- Resolve strategies for the current context
- For each strategy in order:
- Check if strategy is available
- If available, attempt to fetch
- If successful, return result immediately
- If error occurs and
shouldFallback returns true, continue to next strategy
- If error occurs and
shouldFallback returns false, return error
- If no strategies succeed, return
ProviderFetchError.noAvailableStrategy
ProviderFetchStrategy Protocol
Unique identifier for this strategy (e.g., “claude.cli”, “codex.web”)
kind
ProviderFetchKind
required
The kind of fetch this strategy performs
Required Methods
isAvailable
func isAvailable(_ context: ProviderFetchContext) async -> Bool
Determines if this strategy can be used in the given context. Check for:
- Installed CLI binaries
- Available browser cookies
- API tokens in settings
- Source mode compatibility
fetch
func fetch(_ context: ProviderFetchContext) async throws -> ProviderFetchResult
Executes the fetch and returns usage data. Should be timeout-bounded.
shouldFallback
func shouldFallback(on error: Error, context: ProviderFetchContext) -> Bool
Determines if the pipeline should try the next strategy after this error. Return:
true: Try next strategy (soft error like “CLI not found”)
false: Stop immediately (hard error like “authentication failed”)
ProviderFetchKind
public enum ProviderFetchKind: Sendable {
case cli // Command-line interface
case web // Browser cookies
case oauth // OAuth API
case apiToken // API token
case localProbe // Local process inspection
case webDashboard // Web scraping with WebView
}
ProviderFetchContext
Context passed to all strategies containing runtime configuration:
Whether running in app or CLI mode
Requested source mode (auto, web, cli, oauth, api)
Whether to fetch credit information
Timeout for web operations
Whether to log verbose debug info
settings
ProviderSettingsSnapshot?
Provider-specific settings
Shared fetcher for Codex-specific operations
Browser cookie detection helper
ProviderFetchResult
Credit information if available
Dashboard data for OpenAI providers
Human-readable label for data source (e.g., “cli”, “web”)
ID of strategy that produced this result
strategyKind
ProviderFetchKind
required
Kind of strategy that produced this result
ProviderFetchOutcome
Complete result of a fetch attempt including all strategy attempts:
result
Result<ProviderFetchResult, Error>
required
Success or failure result
attempts
[ProviderFetchAttempt]
required
History of all strategy attempts
ProviderFetchAttempt
Kind of strategy attempted
Whether strategy reported itself as available
Error message if fetch failed
Example: Implementing a Strategy
struct ClaudeCLIStrategy: ProviderFetchStrategy {
let id: String = "claude.cli"
let kind: ProviderFetchKind = .cli
func isAvailable(_ context: ProviderFetchContext) async -> Bool {
// Check if source mode allows CLI
guard context.sourceMode == .auto || context.sourceMode == .cli else {
return false
}
// Check if claude binary is installed
guard let binary = TTYCommandRunner.which("claude") else {
return false
}
return true
}
func fetch(_ context: ProviderFetchContext) async throws -> ProviderFetchResult {
// Execute CLI command with timeout
let runner = TTYCommandRunner(
command: "claude status --json",
timeout: context.webTimeout
)
let output = try await runner.run()
let data = output.data(using: .utf8)!
let parsed = try JSONDecoder().decode(ClaudeStatusResponse.self, from: data)
// Convert to UsageSnapshot
let usage = UsageSnapshot(
primary: RateWindow(
usedPercent: parsed.usedPercent,
windowMinutes: 300,
resetsAt: parsed.resetsAt,
resetDescription: nil
),
secondary: nil,
updatedAt: Date(),
identity: ProviderIdentitySnapshot(
providerID: .claude,
accountEmail: parsed.email,
accountOrganization: nil,
loginMethod: nil
)
)
return makeResult(
usage: usage,
sourceLabel: "cli"
)
}
func shouldFallback(on error: Error, context: ProviderFetchContext) -> Bool {
// Fallback to web strategy if CLI fails
return true
}
}
Example: Multi-Strategy Pipeline
ProviderFetchPlan(
sourceModes: [.auto, .web, .cli, .api],
pipeline: ProviderFetchPipeline(
resolveStrategies: { context in
var strategies: [any ProviderFetchStrategy] = []
// Prefer API if token available
if context.settings?.hasAPIToken == true {
strategies.append(ProviderAPIStrategy())
}
// Try CLI if installed
strategies.append(ProviderCLIStrategy())
// Fall back to web scraping
strategies.append(ProviderWebStrategy())
return strategies
}
)
)
Best Practices
Order strategies by reliability and speed: API tokens > CLI > Web scraping
- Implement timeout bounds for all network and subprocess operations
- Check
context.sourceMode in isAvailable to respect CLI flags
- Return
true from shouldFallback for recoverable errors (missing binary, no cookies)
- Return
false from shouldFallback for auth failures (don’t spam other strategies)
- Use descriptive
strategyID values for debugging (e.g., “claude.cli.status”)
- Provide helpful error messages that guide users to fix the issue