Skip to content
Open
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 .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ debug_*.swift

# Misc
.DS_Store
.swiftpm-cache/

# Debug/analysis docs
docs/*-analysis.md
Expand Down
2 changes: 2 additions & 0 deletions Sources/CodexBar/PreferencesDebugPane.swift
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ struct DebugPane: View {
Text("Cursor").tag(UsageProvider.cursor)
Text("Augment").tag(UsageProvider.augment)
Text("Amp").tag(UsageProvider.amp)
Text("Ollama").tag(UsageProvider.ollama)
}
.pickerStyle(.segmented)
.frame(width: 460)
Expand Down Expand Up @@ -299,6 +300,7 @@ struct DebugPane: View {
Text("Antigravity").tag(UsageProvider.antigravity)
Text("Augment").tag(UsageProvider.augment)
Text("Amp").tag(UsageProvider.amp)
Text("Ollama").tag(UsageProvider.ollama)
}
.pickerStyle(.segmented)
.frame(width: 360)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import AppKit
import CodexBarCore
import CodexBarMacroSupport
import Foundation
import SwiftUI

@ProviderImplementationRegistration
struct OllamaProviderImplementation: ProviderImplementation {
let id: UsageProvider = .ollama

@MainActor
func observeSettings(_ settings: SettingsStore) {
_ = settings.ollamaCookieSource
_ = settings.ollamaCookieHeader
}

@MainActor
func settingsSnapshot(context: ProviderSettingsSnapshotContext) -> ProviderSettingsSnapshotContribution? {
.ollama(context.settings.ollamaSettingsSnapshot(tokenOverride: context.tokenOverride))
}

@MainActor
func settingsPickers(context: ProviderSettingsContext) -> [ProviderSettingsPickerDescriptor] {
let cookieBinding = Binding(
get: { context.settings.ollamaCookieSource.rawValue },
set: { raw in
context.settings.ollamaCookieSource = ProviderCookieSource(rawValue: raw) ?? .auto
})
let cookieOptions = ProviderCookieSourceUI.options(
allowsOff: false,
keychainDisabled: context.settings.debugDisableKeychainAccess)

let cookieSubtitle: () -> String? = {
ProviderCookieSourceUI.subtitle(
source: context.settings.ollamaCookieSource,
keychainDisabled: context.settings.debugDisableKeychainAccess,
auto: "Automatic imports browser cookies.",
manual: "Paste a Cookie header or cURL capture from Ollama settings.",
off: "Ollama cookies are disabled.")
}

return [
ProviderSettingsPickerDescriptor(
id: "ollama-cookie-source",
title: "Cookie source",
subtitle: "Automatic imports browser cookies.",
dynamicSubtitle: cookieSubtitle,
binding: cookieBinding,
options: cookieOptions,
isVisible: nil,
onChange: nil),
]
}

@MainActor
func settingsFields(context: ProviderSettingsContext) -> [ProviderSettingsFieldDescriptor] {
[
ProviderSettingsFieldDescriptor(
id: "ollama-cookie",
title: "",
subtitle: "",
kind: .secure,
placeholder: "Cookie: …",
binding: context.stringBinding(\.ollamaCookieHeader),
actions: [
ProviderSettingsActionDescriptor(
id: "ollama-open-settings",
title: "Open Ollama Settings",
style: .link,
isVisible: nil,
perform: {
if let url = URL(string: "https://ollama.com/settings") {
NSWorkspace.shared.open(url)
}
}),
],
isVisible: { context.settings.ollamaCookieSource == .manual },
onActivate: { }),
]
}
}
60 changes: 60 additions & 0 deletions Sources/CodexBar/Providers/Ollama/OllamaSettingsStore.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import CodexBarCore
import Foundation

extension SettingsStore {
var ollamaCookieHeader: String {
get { self.configSnapshot.providerConfig(for: .ollama)?.sanitizedCookieHeader ?? "" }
set {
self.updateProviderConfig(provider: .ollama) { entry in
entry.cookieHeader = self.normalizedConfigValue(newValue)
}
self.logSecretUpdate(provider: .ollama, field: "cookieHeader", value: newValue)
}
}

var ollamaCookieSource: ProviderCookieSource {
get { self.resolvedCookieSource(provider: .ollama, fallback: .auto) }
set {
self.updateProviderConfig(provider: .ollama) { entry in
entry.cookieSource = newValue
}
self.logProviderModeChange(provider: .ollama, field: "cookieSource", value: newValue.rawValue)
}
}
}

extension SettingsStore {
func ollamaSettingsSnapshot(tokenOverride: TokenAccountOverride?) -> ProviderSettingsSnapshot.OllamaProviderSettings {
ProviderSettingsSnapshot.OllamaProviderSettings(
cookieSource: self.ollamaSnapshotCookieSource(tokenOverride: tokenOverride),
manualCookieHeader: self.ollamaSnapshotCookieHeader(tokenOverride: tokenOverride))
}

private func ollamaSnapshotCookieHeader(tokenOverride: TokenAccountOverride?) -> String {
let fallback = self.ollamaCookieHeader
guard let support = TokenAccountSupportCatalog.support(for: .ollama),
case .cookieHeader = support.injection
else {
return fallback
}
guard let account = ProviderTokenAccountSelection.selectedAccount(
provider: .ollama,
settings: self,
override: tokenOverride)
else {
return fallback
}
return TokenAccountSupportCatalog.normalizedCookieHeader(account.token, support: support)
}

private func ollamaSnapshotCookieSource(tokenOverride: TokenAccountOverride?) -> ProviderCookieSource {
let fallback = self.ollamaCookieSource
guard let support = TokenAccountSupportCatalog.support(for: .ollama),
support.requiresManualCookieSource
else {
return fallback
}
if self.tokenAccounts(for: .ollama).isEmpty { return fallback }
return .manual
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ enum ProviderImplementationRegistry {
case .jetbrains: JetBrainsProviderImplementation()
case .kimik2: KimiK2ProviderImplementation()
case .amp: AmpProviderImplementation()
case .ollama: OllamaProviderImplementation()
case .synthetic: SyntheticProviderImplementation()
}
}
Expand Down
3 changes: 3 additions & 0 deletions Sources/CodexBar/Resources/ProviderIcon-ollama.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions Sources/CodexBar/SettingsStore+MenuObservation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ extension SettingsStore {
_ = self.kimiCookieSource
_ = self.augmentCookieSource
_ = self.ampCookieSource
_ = self.ollamaCookieSource
_ = self.mergeIcons
_ = self.switcherShowsIcons
_ = self.zaiAPIToken
Expand All @@ -52,6 +53,7 @@ extension SettingsStore {
_ = self.kimiK2APIToken
_ = self.augmentCookieHeader
_ = self.ampCookieHeader
_ = self.ollamaCookieHeader
_ = self.copilotAPIToken
_ = self.tokenAccountsByProvider
_ = self.debugLoadingPattern
Expand Down
1 change: 1 addition & 0 deletions Sources/CodexBar/UsageStore+Logging.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ extension UsageStore {
"kimiCookieSource": self.settings.kimiCookieSource.rawValue,
"augmentCookieSource": self.settings.augmentCookieSource.rawValue,
"ampCookieSource": self.settings.ampCookieSource.rawValue,
"ollamaCookieSource": self.settings.ollamaCookieSource.rawValue,
"openAIWebAccess": self.settings.openAIWebAccessEnabled ? "1" : "0",
"claudeWebExtras": self.settings.claudeWebExtrasEnabled ? "1" : "0",
]
Expand Down
19 changes: 19 additions & 0 deletions Sources/CodexBar/UsageStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1225,6 +1225,12 @@ extension UsageStore {
ampCookieHeader: self.settings.ampCookieHeader)
await MainActor.run { self.probeLogs[.amp] = text }
return text
case .ollama:
let text = await self.debugOllamaLog(
ollamaCookieSource: self.settings.ollamaCookieSource,
ollamaCookieHeader: self.settings.ollamaCookieHeader)
await MainActor.run { self.probeLogs[.ollama] = text }
return text
case .jetbrains:
let text = "JetBrains AI debug log not yet implemented"
await MainActor.run { self.probeLogs[.jetbrains] = text }
Expand Down Expand Up @@ -1389,6 +1395,19 @@ extension UsageStore {
}
}

private func debugOllamaLog(
ollamaCookieSource: ProviderCookieSource,
ollamaCookieHeader: String) async -> String
{
await self.runWithTimeout(seconds: 15) {
let fetcher = OllamaUsageFetcher(browserDetection: self.browserDetection)
let manualHeader = ollamaCookieSource == .manual
? CookieHeaderNormalizer.normalize(ollamaCookieHeader)
: nil
return await fetcher.debugRawProbe(cookieHeaderOverride: manualHeader)
}
}

private func runWithTimeout(seconds: Double, operation: @escaping @Sendable () async -> String) async -> String {
await withTaskGroup(of: String?.self) { group -> String in
group.addTask { await operation() }
Expand Down
7 changes: 7 additions & 0 deletions Sources/CodexBarCLI/TokenAccountCLI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,11 @@ struct TokenAccountCLIContext {
amp: ProviderSettingsSnapshot.AmpProviderSettings(
cookieSource: cookieSource,
manualCookieHeader: cookieHeader))
case .ollama:
return self.makeSnapshot(
ollama: ProviderSettingsSnapshot.OllamaProviderSettings(
cookieSource: cookieSource,
manualCookieHeader: cookieHeader))
case .kimi:
return self.makeSnapshot(
kimi: ProviderSettingsSnapshot.KimiProviderSettings(
Expand Down Expand Up @@ -163,6 +168,7 @@ struct TokenAccountCLIContext {
kimi: ProviderSettingsSnapshot.KimiProviderSettings? = nil,
augment: ProviderSettingsSnapshot.AugmentProviderSettings? = nil,
amp: ProviderSettingsSnapshot.AmpProviderSettings? = nil,
ollama: ProviderSettingsSnapshot.OllamaProviderSettings? = nil,
jetbrains: ProviderSettingsSnapshot.JetBrainsProviderSettings? = nil) -> ProviderSettingsSnapshot
{
ProviderSettingsSnapshot.make(
Expand All @@ -176,6 +182,7 @@ struct TokenAccountCLIContext {
kimi: kimi,
augment: augment,
amp: amp,
ollama: ollama,
jetbrains: jetbrains)
}

Expand Down
1 change: 1 addition & 0 deletions Sources/CodexBarCore/Logging/LogCategories.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ public enum LogCategories {
public static let notifications = "notifications"
public static let openAIWeb = "openai-web"
public static let openAIWebview = "openai-webview"
public static let ollama = "ollama"
public static let opencodeUsage = "opencode-usage"
public static let providerDetection = "provider-detection"
public static let providers = "providers"
Expand Down
Loading