chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,152 @@
|
||||
import CodexBarCore
|
||||
import Commander
|
||||
import Foundation
|
||||
|
||||
extension CodexBarCLI {
|
||||
static func runCacheClear(_ values: ParsedValues) {
|
||||
let output = CLIOutputPreferences.from(values: values)
|
||||
let cookies = values.flags.contains("cookies")
|
||||
let cost = values.flags.contains("cost")
|
||||
let all = values.flags.contains("all")
|
||||
let rawProvider = values.options["provider"]?.last
|
||||
|
||||
let clearCookies = cookies || all
|
||||
let clearCost = cost || all
|
||||
|
||||
if !clearCookies, !clearCost {
|
||||
Self.exit(
|
||||
code: .failure,
|
||||
message: "Specify --cookies, --cost, or --all.",
|
||||
output: output,
|
||||
kind: .args)
|
||||
}
|
||||
if let error = Self.cacheClearProviderScopeError(rawProvider: rawProvider, clearCost: clearCost) {
|
||||
Self.exit(code: .failure, message: error, output: output, kind: .args)
|
||||
}
|
||||
|
||||
var results: [CacheClearResult] = []
|
||||
|
||||
if clearCookies {
|
||||
if let rawProvider {
|
||||
if let provider = ProviderDescriptorRegistry.cliNameMap[rawProvider.lowercased()] {
|
||||
let summary = CookieHeaderCache.clearAllScopesDetailed(provider: provider)
|
||||
results.append(CacheClearResult(
|
||||
cache: "cookies",
|
||||
provider: provider.rawValue,
|
||||
cleared: summary.clearedCount,
|
||||
error: Self.cookieClearError(failedCount: summary.failedCount)))
|
||||
} else {
|
||||
Self.exit(
|
||||
code: .failure,
|
||||
message: "Unknown provider: \(rawProvider)",
|
||||
output: output,
|
||||
kind: .args)
|
||||
}
|
||||
} else {
|
||||
let summary = CookieHeaderCache.clearAllDetailed()
|
||||
results.append(CacheClearResult(
|
||||
cache: "cookies",
|
||||
provider: nil,
|
||||
cleared: summary.clearedCount,
|
||||
error: Self.cookieClearError(failedCount: summary.failedCount)))
|
||||
}
|
||||
}
|
||||
|
||||
if clearCost {
|
||||
let fm = FileManager.default
|
||||
let cacheDir = Self.costUsageCacheDirectory(fileManager: fm)
|
||||
var cleared = 0
|
||||
var costError: String?
|
||||
if fm.fileExists(atPath: cacheDir.path) {
|
||||
do {
|
||||
try fm.removeItem(at: cacheDir)
|
||||
cleared = 1
|
||||
} catch {
|
||||
costError = error.localizedDescription
|
||||
}
|
||||
}
|
||||
results.append(CacheClearResult(cache: "cost", provider: nil, cleared: cleared, error: costError))
|
||||
}
|
||||
|
||||
switch output.format {
|
||||
case .text:
|
||||
for result in results {
|
||||
let scope = result.provider ?? "all providers"
|
||||
if let error = result.error {
|
||||
print("\(result.cache): failed to clear (\(scope)) - \(error)")
|
||||
} else if result.cleared > 0 {
|
||||
print("\(result.cache): cleared (\(scope))")
|
||||
} else {
|
||||
print("\(result.cache): nothing to clear (\(scope))")
|
||||
}
|
||||
}
|
||||
case .json:
|
||||
Self.printJSON(results, pretty: output.pretty)
|
||||
}
|
||||
|
||||
let hasErrors = results.contains(where: { $0.error != nil })
|
||||
Self.exit(code: hasErrors ? .failure : .success, output: output, kind: .runtime)
|
||||
}
|
||||
|
||||
static func cacheClearProviderScopeError(rawProvider: String?, clearCost: Bool) -> String? {
|
||||
guard rawProvider != nil, clearCost else { return nil }
|
||||
return "--provider only scopes cookie caches. Use --cookies --provider <name>, or omit --provider."
|
||||
}
|
||||
|
||||
private static func cookieClearError(failedCount: Int) -> String? {
|
||||
guard failedCount > 0 else { return nil }
|
||||
return "Cookie cache cleanup failed for \(failedCount) operation\(failedCount == 1 ? "" : "s")"
|
||||
}
|
||||
}
|
||||
|
||||
struct CacheOptions: CommanderParsable {
|
||||
@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("format"), help: "Output format: text | json")
|
||||
var format: OutputFormat?
|
||||
|
||||
@Flag(name: .long("json"), help: "")
|
||||
var jsonShortcut: Bool = false
|
||||
|
||||
@Flag(name: .long("json-only"), help: "Emit JSON only (suppress non-JSON output)")
|
||||
var jsonOnly: Bool = false
|
||||
|
||||
@Flag(name: .long("pretty"), help: "Pretty-print JSON output")
|
||||
var pretty: Bool = false
|
||||
|
||||
@Flag(name: .long("cookies"), help: "Clear browser cookie caches")
|
||||
var cookies: Bool = false
|
||||
|
||||
@Flag(name: .long("cost"), help: "Clear cost usage caches")
|
||||
var cost: Bool = false
|
||||
|
||||
@Flag(name: .long("all"), help: "Clear all caches")
|
||||
var all: Bool = false
|
||||
|
||||
@Option(name: .long("provider"), help: "Clear cache for a specific provider only")
|
||||
var provider: String?
|
||||
}
|
||||
|
||||
private struct CacheClearResult: Encodable {
|
||||
let cache: String
|
||||
let provider: String?
|
||||
let cleared: Int
|
||||
var error: String?
|
||||
}
|
||||
|
||||
extension CodexBarCLI {
|
||||
/// Mirrors the cost usage cache directory used by the app (UsageStore.costUsageCacheDirectory).
|
||||
static func costUsageCacheDirectory(fileManager: FileManager = .default) -> URL {
|
||||
let root = fileManager.urls(for: .cachesDirectory, in: .userDomainMask).first!
|
||||
return root
|
||||
.appendingPathComponent("CodexBar", isDirectory: true)
|
||||
.appendingPathComponent("cost-usage", isDirectory: true)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user