Files
steipete--codexbar/Sources/CodexBar/SettingsStore+TokenCost.swift
2026-07-13 12:22:33 +08:00

94 lines
3.8 KiB
Swift

import CodexBarCore
import Foundation
extension SettingsStore {
func costSummaryShowsInlineDashboard(for provider: UsageProvider) -> Bool {
self.isCostUsageEffectivelyEnabled(for: provider) &&
self.costSummaryDisplayStyle.showsInlineSummary
}
func costSummaryShowsSubmenu(for provider: UsageProvider) -> Bool {
self.isCostUsageEffectivelyEnabled(for: provider) &&
self.costSummaryDisplayStyle.showsCostSubmenu
}
func applyTokenCostDefaultIfNeeded() {
// Settings are persisted in UserDefaults.standard.
guard UserDefaults.standard.object(forKey: "tokenCostUsageEnabled") == nil else { return }
Task { @MainActor [weak self] in
guard let self else { return }
let hasSources = await Task.detached(priority: .utility) {
Self.hasAnyTokenCostUsageSources()
}.value
guard hasSources else { return }
guard UserDefaults.standard.object(forKey: "tokenCostUsageEnabled") == nil else { return }
self.costUsageEnabled = true
}
}
nonisolated static func hasAnyTokenCostUsageSources(
env: [String: String] = ProcessInfo.processInfo.environment,
fileManager: FileManager = .default,
homeDirectory: URL? = nil) -> Bool
{
let home = homeDirectory ?? fileManager.homeDirectoryForCurrentUser
func hasAnyJsonl(in root: URL) -> Bool {
guard fileManager.fileExists(atPath: root.path) else { return false }
guard let enumerator = fileManager.enumerator(
at: root,
includingPropertiesForKeys: [.isRegularFileKey],
options: [.skipsHiddenFiles, .skipsPackageDescendants])
else { return false }
for case let url as URL in enumerator where url.pathExtension.lowercased() == "jsonl" {
return true
}
return false
}
let codexRoot: URL = {
let raw = env["CODEX_HOME"]?.trimmingCharacters(in: .whitespacesAndNewlines)
if let raw, !raw.isEmpty {
return URL(fileURLWithPath: raw).appendingPathComponent("sessions", isDirectory: true)
}
return home
.appendingPathComponent(".codex", isDirectory: true)
.appendingPathComponent("sessions", isDirectory: true)
}()
let archivedCodexRoot: URL? = {
guard codexRoot.lastPathComponent == "sessions" else { return nil }
return codexRoot
.deletingLastPathComponent()
.appendingPathComponent("archived_sessions", isDirectory: true)
}()
if hasAnyJsonl(in: codexRoot) { return true }
if let archivedCodexRoot, hasAnyJsonl(in: archivedCodexRoot) { return true }
let claudeRoots: [URL] = {
if let env = env["CLAUDE_CONFIG_DIR"]?.trimmingCharacters(in: .whitespacesAndNewlines),
!env.isEmpty
{
return env.split(separator: ",").map { part in
let raw = String(part).trimmingCharacters(in: .whitespacesAndNewlines)
let url = URL(fileURLWithPath: raw)
if url.lastPathComponent == "projects" {
return url
}
return url.appendingPathComponent("projects", isDirectory: true)
}
}
return [
home.appendingPathComponent(".config/claude/projects", isDirectory: true),
home.appendingPathComponent(".claude/projects", isDirectory: true),
] + ClaudeDesktopProjectsLocator.roots(homeDirectory: home, fileManager: fileManager)
}()
return claudeRoots.contains(where: hasAnyJsonl(in:))
}
}