chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,212 @@
|
||||
import CodexBarCore
|
||||
import Commander
|
||||
import Foundation
|
||||
|
||||
struct CardsOptions: CommanderParsable {
|
||||
private static let sourceHelp: String = {
|
||||
#if os(macOS)
|
||||
"Data source: auto | web | cli | oauth | api (auto behavior is provider-specific)"
|
||||
#else
|
||||
"Data source: auto | web | cli | oauth | api (web/auto are macOS only for web-capable providers)"
|
||||
#endif
|
||||
}()
|
||||
|
||||
@Flag(names: [.short("v"), .long("verbose")], help: "Enable verbose logging")
|
||||
var verbose: Bool = false
|
||||
|
||||
@Flag(name: .long("json-output"), help: "Emit machine-readable logs")
|
||||
var jsonOutput: Bool = false
|
||||
|
||||
@Option(name: .long("log-level"), help: "Set log level (trace|verbose|debug|info|warning|error|critical)")
|
||||
var logLevel: String?
|
||||
|
||||
@Option(
|
||||
name: .long("provider"),
|
||||
help: ProviderHelp.optionHelp)
|
||||
var provider: ProviderSelection?
|
||||
|
||||
@Option(name: .long("account"), help: "Token account label to use (from config.json)")
|
||||
var account: String?
|
||||
|
||||
@Option(name: .long("account-index"), help: "Token account index (1-based)")
|
||||
var accountIndex: Int?
|
||||
|
||||
@Flag(name: .long("all-accounts"), help: "Fetch all token accounts, or all visible Codex accounts")
|
||||
var allAccounts: Bool = false
|
||||
|
||||
@Flag(name: .long("no-credits"), help: "Skip Codex credits line")
|
||||
var noCredits: Bool = false
|
||||
|
||||
@Flag(name: .long("no-color"), help: "Disable ANSI colors in text output")
|
||||
var noColor: Bool = false
|
||||
|
||||
@Flag(name: .long("status"), help: "Fetch and include provider status")
|
||||
var status: Bool = false
|
||||
|
||||
@Flag(name: .long("web"), help: "Alias for --source web")
|
||||
var web: Bool = false
|
||||
|
||||
@Option(name: .long("source"), help: Self.sourceHelp)
|
||||
var source: String?
|
||||
|
||||
@Option(name: .long("web-timeout"), help: "Web fetch timeout (seconds; source=auto or web)")
|
||||
var webTimeout: Double?
|
||||
|
||||
@Flag(name: .long("web-debug-dump-html"), help: "Dump HTML snapshots to /tmp when Codex dashboard data is missing")
|
||||
var webDebugDumpHtml: Bool = false
|
||||
|
||||
@Flag(name: .long("antigravity-plan-debug"), help: "Emit Antigravity planInfo fields (debug)")
|
||||
var antigravityPlanDebug: Bool = false
|
||||
|
||||
@Flag(name: .long("augment-debug"), help: "Emit Augment API responses (debug)")
|
||||
var augmentDebug: Bool = false
|
||||
|
||||
@Flag(name: .long("brief"), help: "Compact table layout instead of the card grid")
|
||||
var brief: Bool = false
|
||||
}
|
||||
|
||||
extension CodexBarCLI {
|
||||
static func runCards(_ values: ParsedValues) async {
|
||||
let output = CLIOutputPreferences.from(values: values)
|
||||
let config = Self.loadConfig(output: output)
|
||||
let provider = Self.decodeProvider(from: values, config: config)
|
||||
let includeCredits = !values.flags.contains("noCredits")
|
||||
let includeStatus = values.flags.contains("status")
|
||||
let sourceModeRaw = values.options["source"]?.last
|
||||
let parsedSourceMode = Self.decodeSourceMode(from: values)
|
||||
if sourceModeRaw != nil, parsedSourceMode == nil {
|
||||
Self.exit(
|
||||
code: .failure,
|
||||
message: "Error: --source must be auto|web|cli|oauth|api.",
|
||||
output: output,
|
||||
kind: .args)
|
||||
}
|
||||
let antigravityPlanDebug = values.flags.contains("antigravityPlanDebug")
|
||||
let augmentDebug = values.flags.contains("augmentDebug")
|
||||
let webDebugDumpHTML = values.flags.contains("webDebugDumpHtml")
|
||||
let webTimeout: TimeInterval
|
||||
do {
|
||||
webTimeout = try Self.decodeWebTimeout(from: values) ?? 60
|
||||
} catch {
|
||||
Self.exit(code: .failure, message: "Error: \(error.localizedDescription)", output: output, kind: .args)
|
||||
}
|
||||
let verbose = values.flags.contains("verbose")
|
||||
let noColor = values.flags.contains("noColor")
|
||||
let useColor = Self.shouldUseColor(noColor: noColor, format: .text)
|
||||
let brief = values.flags.contains("brief")
|
||||
let resetStyle = Self.resetTimeDisplayStyleFromDefaults()
|
||||
let weeklyWorkDays = Self.weeklyProgressWorkDaysFromDefaults()
|
||||
let providerList = provider.asList
|
||||
|
||||
let tokenSelection: TokenAccountCLISelection
|
||||
do {
|
||||
tokenSelection = try Self.decodeTokenAccountSelection(from: values)
|
||||
} catch {
|
||||
Self.exit(code: .failure, message: "Error: \(error.localizedDescription)", output: output, kind: .args)
|
||||
}
|
||||
|
||||
if tokenSelection.allAccounts, tokenSelection.label != nil || tokenSelection.index != nil {
|
||||
Self.exit(
|
||||
code: .failure,
|
||||
message: "Error: --all-accounts cannot be combined with --account or --account-index.",
|
||||
output: output,
|
||||
kind: .args)
|
||||
}
|
||||
|
||||
if tokenSelection.usesOverride {
|
||||
guard providerList.count == 1 else {
|
||||
Self.exit(
|
||||
code: .failure,
|
||||
message: "Error: account selection requires a single provider.",
|
||||
output: output,
|
||||
kind: .args)
|
||||
}
|
||||
let supportsAllCodexAccounts = providerList[0] == .codex
|
||||
&& tokenSelection.allAccounts
|
||||
&& tokenSelection.label == nil
|
||||
&& tokenSelection.index == nil
|
||||
guard supportsAllCodexAccounts || TokenAccountSupportCatalog.support(for: providerList[0]) != nil else {
|
||||
Self.exit(
|
||||
code: .failure,
|
||||
message: "Error: \(providerList[0].rawValue) does not support token accounts.",
|
||||
output: output,
|
||||
kind: .args)
|
||||
}
|
||||
}
|
||||
|
||||
let browserDetection = BrowserDetection()
|
||||
let fetcher = UsageFetcher()
|
||||
let claudeFetcher = ClaudeUsageFetcher(browserDetection: browserDetection)
|
||||
let tokenContext: TokenAccountCLIContext
|
||||
do {
|
||||
tokenContext = try TokenAccountCLIContext(
|
||||
selection: tokenSelection,
|
||||
config: config,
|
||||
verbose: verbose)
|
||||
} catch {
|
||||
Self.exit(code: .failure, message: "Error: \(error.localizedDescription)", output: output, kind: .config)
|
||||
}
|
||||
|
||||
var cards: [CLICardModel] = []
|
||||
var failures: [CLICardFailure] = []
|
||||
var exitCode: ExitCode = .success
|
||||
let command = UsageCommandContext(
|
||||
format: .text,
|
||||
includeCredits: includeCredits,
|
||||
sourceModeOverride: parsedSourceMode,
|
||||
antigravityPlanDebug: antigravityPlanDebug,
|
||||
augmentDebug: augmentDebug,
|
||||
webDebugDumpHTML: webDebugDumpHTML,
|
||||
webTimeout: webTimeout,
|
||||
verbose: verbose,
|
||||
useColor: useColor,
|
||||
resetStyle: resetStyle,
|
||||
weeklyWorkDays: weeklyWorkDays,
|
||||
jsonOnly: output.jsonOnly,
|
||||
includeAllCodexAccounts: tokenSelection.allAccounts && providerList == [.codex],
|
||||
fetcher: fetcher,
|
||||
claudeFetcher: claudeFetcher,
|
||||
browserDetection: browserDetection,
|
||||
cardsLayout: true)
|
||||
|
||||
for provider in providerList {
|
||||
let status = includeStatus ? await Self.fetchStatus(for: provider) : nil
|
||||
let result = await ProviderInteractionContext.$current.withValue(.background) {
|
||||
await Self.fetchUsageOutputs(
|
||||
provider: provider,
|
||||
status: status,
|
||||
tokenContext: tokenContext,
|
||||
command: command)
|
||||
}
|
||||
if result.exitCode != .success {
|
||||
exitCode = result.exitCode
|
||||
}
|
||||
cards.append(contentsOf: result.cards)
|
||||
failures.append(contentsOf: result.cardFailures)
|
||||
}
|
||||
|
||||
let rendered: String
|
||||
let enhanced = CLITerminalCapabilities.supportsEnhancedCards(useColor: useColor)
|
||||
if brief {
|
||||
let rows = CLICardsBriefRenderer.makeRows(cards: cards)
|
||||
rendered = CLICardsBriefRenderer.render(
|
||||
rows: rows,
|
||||
failures: failures,
|
||||
terminalWidth: CLICardsRenderer.terminalColumnCount(),
|
||||
useColor: useColor,
|
||||
enhanced: enhanced)
|
||||
} else {
|
||||
rendered = CLICardsRenderer.render(
|
||||
cards: cards,
|
||||
failures: failures,
|
||||
terminalWidth: CLICardsRenderer.terminalColumnCount(),
|
||||
useColor: useColor,
|
||||
enhanced: enhanced)
|
||||
}
|
||||
if !rendered.isEmpty {
|
||||
print(rendered)
|
||||
}
|
||||
|
||||
Self.exit(code: exitCode, output: output, kind: exitCode == .success ? .runtime : .provider)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user