Skip to content
Merged
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 CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
- Ollama: add API key authentication as an alternative to browser cookies for validating Cloud access (#1044). Thanks @nandorocker!
- Azure OpenAI: add deployment-status validation via API key, endpoint, and deployment settings (#1045). Thanks @ZenoRewn!
- Localizations: add Spanish and Catalan language packs and fill missing localization keys (#1041). Thanks @seifreed!
- Providers: T3 Chat - add web-session usage tracking, can paste a full browser cURL when cookie-only refreshes hit a 429 challenge (#1091). Thanks @Quicksaver!

### Fixed
- Menu: restore full-width provider switcher quota bars and refresh them while the menu stays open (#1094). Thanks @bcharleson!
Expand Down
1 change: 1 addition & 0 deletions Sources/CodexBar/PreferencesDebugPane.swift
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,7 @@ struct DebugPane: View {
Text("Antigravity").tag(UsageProvider.antigravity)
Text("Augment").tag(UsageProvider.augment)
Text("Amp").tag(UsageProvider.amp)
Text("T3 Chat").tag(UsageProvider.t3chat)
Text("Ollama").tag(UsageProvider.ollama)
}
.pickerStyle(.segmented)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ enum ProviderImplementationRegistry {
case .kimik2: KimiK2ProviderImplementation()
case .moonshot: MoonshotProviderImplementation()
case .amp: AmpProviderImplementation()
case .t3chat: T3ChatProviderImplementation()
case .ollama: OllamaProviderImplementation()
case .synthetic: SyntheticProviderImplementation()
case .openrouter: OpenRouterProviderImplementation()
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 T3ChatProviderImplementation: ProviderImplementation {
let id: UsageProvider = .t3chat

@MainActor
func observeSettings(_ settings: SettingsStore) {
_ = settings.t3ChatCookieSource
_ = settings.t3ChatCookieHeader
}

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

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

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

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

@MainActor
func settingsFields(context: ProviderSettingsContext) -> [ProviderSettingsFieldDescriptor] {
[
ProviderSettingsFieldDescriptor(
id: "t3chat-cookie",
title: "T3 Chat cookie",
subtitle: "Paste a Cookie header or full cURL capture from T3 Chat settings.",
kind: .secure,
placeholder: "Cookie: ...",
binding: context.stringBinding(\.t3ChatCookieHeader),
actions: [
ProviderSettingsActionDescriptor(
id: "t3chat-open-settings",
title: "Open T3 Chat Settings",
style: .link,
isVisible: nil,
perform: {
if let url = URL(string: "https://t3.chat/settings/customization") {
NSWorkspace.shared.open(url)
}
}),
],
isVisible: { context.settings.t3ChatCookieSource == .manual },
onActivate: nil),
]
}
}
62 changes: 62 additions & 0 deletions Sources/CodexBar/Providers/T3Chat/T3ChatSettingsStore.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import CodexBarCore
import Foundation

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

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

extension SettingsStore {
func t3ChatSettingsSnapshot(
tokenOverride: TokenAccountOverride?) -> ProviderSettingsSnapshot.T3ChatProviderSettings
{
ProviderSettingsSnapshot.T3ChatProviderSettings(
cookieSource: self.t3ChatSnapshotCookieSource(tokenOverride: tokenOverride),
manualCookieHeader: self.t3ChatSnapshotCookieHeader(tokenOverride: tokenOverride))
}

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

private func t3ChatSnapshotCookieSource(tokenOverride: TokenAccountOverride?) -> ProviderCookieSource {
let fallback = self.t3ChatCookieSource
guard let support = TokenAccountSupportCatalog.support(for: .t3chat),
support.requiresManualCookieSource
else {
return fallback
}
if self.tokenAccounts(for: .t3chat).isEmpty { return fallback }
return .manual
}
}
5 changes: 5 additions & 0 deletions Sources/CodexBar/Resources/ProviderIcon-t3chat.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 @@ -57,6 +57,7 @@ extension SettingsStore {
_ = self.kimiCookieSource
_ = self.augmentCookieSource
_ = self.ampCookieSource
_ = self.t3ChatCookieSource
_ = self.ollamaCookieSource
_ = self.mergeIcons
_ = self.switcherShowsIcons
Expand All @@ -79,6 +80,7 @@ extension SettingsStore {
_ = self.kiloAPIToken
_ = self.augmentCookieHeader
_ = self.ampCookieHeader
_ = self.t3ChatCookieHeader
_ = self.ollamaCookieHeader
_ = self.copilotAPIToken
_ = self.warpAPIToken
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 @@ -16,6 +16,7 @@ extension UsageStore {
"kimiCookieSource": self.settings.kimiCookieSource.rawValue,
"augmentCookieSource": self.settings.augmentCookieSource.rawValue,
"ampCookieSource": self.settings.ampCookieSource.rawValue,
"t3ChatCookieSource": self.settings.t3ChatCookieSource.rawValue,
"ollamaCookieSource": self.settings.ollamaCookieSource.rawValue,
"openAIWebAccess": self.settings.openAIWebAccessEnabled ? "1" : "0",
"openAIWebBatterySaver": self.settings.openAIWebBatterySaverEnabled ? "1" : "0",
Expand Down
4 changes: 3 additions & 1 deletion Sources/CodexBar/UsageStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -994,6 +994,7 @@ extension UsageStore {
.bedrock: "Bedrock debug log not yet implemented",
.grok: "Grok debug log not yet implemented",
.groq: "Groq debug log not yet implemented",
.t3chat: "T3 Chat debug log not yet implemented",
.llmproxy: "LLM Proxy debug log not yet implemented",
.deepgram: "Deepgram debug log not yet implemented",
]
Expand Down Expand Up @@ -1072,7 +1073,8 @@ extension UsageStore {
hasTokenAccount: deepSeekHasTokenAccount)
case .gemini, .antigravity, .opencode, .opencodego, .factory, .copilot, .vertexai, .kilo, .kiro, .kimi,
.kimik2, .moonshot, .jetbrains, .perplexity, .mimo, .doubao, .abacus, .mistral, .codebuff, .crof,
.windsurf, .venice, .manus, .commandcode, .stepfun, .bedrock, .grok, .groq, .llmproxy, .deepgram:
.windsurf, .venice, .manus, .commandcode, .stepfun, .bedrock, .grok, .groq, .t3chat, .llmproxy,
.deepgram:
return unimplementedDebugLogMessages[provider] ?? "Debug log not yet implemented"
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Generated by Scripts/regenerate-codex-parser-hash.sh. Do not edit by hand.

enum CodexParserHash {
static let value = "e478cccb0110e8ad"
static let value = "881a341768657996"
}
1 change: 1 addition & 0 deletions Sources/CodexBarCore/Logging/LogCategories.swift
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ public enum LogCategories {
public static let subprocess = "subprocess"
public static let syntheticTokenStore = "synthetic-token-store"
public static let syntheticUsage = "synthetic-usage"
public static let t3chat = "t3chat"
public static let terminal = "terminal"
public static let tokenAccounts = "token-accounts"
public static let tokenCost = "token-cost"
Expand Down
1 change: 1 addition & 0 deletions Sources/CodexBarCore/Providers/ProviderDescriptor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ public enum ProviderDescriptorRegistry {
.kimik2: KimiK2ProviderDescriptor.descriptor,
.moonshot: MoonshotProviderDescriptor.descriptor,
.amp: AmpProviderDescriptor.descriptor,
.t3chat: T3ChatProviderDescriptor.descriptor,
.ollama: OllamaProviderDescriptor.descriptor,
.synthetic: SyntheticProviderDescriptor.descriptor,
.openrouter: OpenRouterProviderDescriptor.descriptor,
Expand Down
19 changes: 19 additions & 0 deletions Sources/CodexBarCore/Providers/ProviderSettingsSnapshot.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ public struct ProviderSettingsSnapshot: Sendable {
augment: AugmentProviderSettings? = nil,
moonshot: MoonshotProviderSettings? = nil,
amp: AmpProviderSettings? = nil,
t3chat: T3ChatProviderSettings? = nil,
ollama: OllamaProviderSettings? = nil,
jetbrains: JetBrainsProviderSettings? = nil,
windsurf: WindsurfProviderSettings? = nil,
Expand Down Expand Up @@ -48,6 +49,7 @@ public struct ProviderSettingsSnapshot: Sendable {
augment: augment,
moonshot: moonshot,
amp: amp,
t3chat: t3chat,
ollama: ollama,
jetbrains: jetbrains,
windsurf: windsurf,
Expand Down Expand Up @@ -253,6 +255,16 @@ public struct ProviderSettingsSnapshot: Sendable {
}
}

public struct T3ChatProviderSettings: Sendable {
public let cookieSource: ProviderCookieSource
public let manualCookieHeader: String?

public init(cookieSource: ProviderCookieSource, manualCookieHeader: String?) {
self.cookieSource = cookieSource
self.manualCookieHeader = manualCookieHeader
}
}

public struct CommandCodeProviderSettings: Sendable {
public let cookieSource: ProviderCookieSource
public let manualCookieHeader: String?
Expand Down Expand Up @@ -366,6 +378,7 @@ public struct ProviderSettingsSnapshot: Sendable {
public let augment: AugmentProviderSettings?
public let moonshot: MoonshotProviderSettings?
public let amp: AmpProviderSettings?
public let t3chat: T3ChatProviderSettings?
public let commandcode: CommandCodeProviderSettings?
public let ollama: OllamaProviderSettings?
public let jetbrains: JetBrainsProviderSettings?
Expand Down Expand Up @@ -399,6 +412,7 @@ public struct ProviderSettingsSnapshot: Sendable {
augment: AugmentProviderSettings?,
moonshot: MoonshotProviderSettings? = nil,
amp: AmpProviderSettings?,
t3chat: T3ChatProviderSettings? = nil,
commandcode: CommandCodeProviderSettings? = nil,
ollama: OllamaProviderSettings?,
jetbrains: JetBrainsProviderSettings? = nil,
Expand Down Expand Up @@ -427,6 +441,7 @@ public struct ProviderSettingsSnapshot: Sendable {
self.augment = augment
self.moonshot = moonshot
self.amp = amp
self.t3chat = t3chat
self.commandcode = commandcode
self.ollama = ollama
self.jetbrains = jetbrains
Expand Down Expand Up @@ -456,6 +471,7 @@ public enum ProviderSettingsSnapshotContribution: Sendable {
case augment(ProviderSettingsSnapshot.AugmentProviderSettings)
case moonshot(ProviderSettingsSnapshot.MoonshotProviderSettings)
case amp(ProviderSettingsSnapshot.AmpProviderSettings)
case t3chat(ProviderSettingsSnapshot.T3ChatProviderSettings)
case commandcode(ProviderSettingsSnapshot.CommandCodeProviderSettings)
case ollama(ProviderSettingsSnapshot.OllamaProviderSettings)
case jetbrains(ProviderSettingsSnapshot.JetBrainsProviderSettings)
Expand Down Expand Up @@ -486,6 +502,7 @@ public struct ProviderSettingsSnapshotBuilder: Sendable {
public var augment: ProviderSettingsSnapshot.AugmentProviderSettings?
public var moonshot: ProviderSettingsSnapshot.MoonshotProviderSettings?
public var amp: ProviderSettingsSnapshot.AmpProviderSettings?
public var t3chat: ProviderSettingsSnapshot.T3ChatProviderSettings?
public var commandcode: ProviderSettingsSnapshot.CommandCodeProviderSettings?
public var ollama: ProviderSettingsSnapshot.OllamaProviderSettings?
public var jetbrains: ProviderSettingsSnapshot.JetBrainsProviderSettings?
Expand Down Expand Up @@ -520,6 +537,7 @@ public struct ProviderSettingsSnapshotBuilder: Sendable {
case let .augment(value): self.augment = value
case let .moonshot(value): self.moonshot = value
case let .amp(value): self.amp = value
case let .t3chat(value): self.t3chat = value
case let .commandcode(value): self.commandcode = value
case let .ollama(value): self.ollama = value
case let .jetbrains(value): self.jetbrains = value
Expand Down Expand Up @@ -552,6 +570,7 @@ public struct ProviderSettingsSnapshotBuilder: Sendable {
augment: self.augment,
moonshot: self.moonshot,
amp: self.amp,
t3chat: self.t3chat,
commandcode: self.commandcode,
ollama: self.ollama,
jetbrains: self.jetbrains,
Expand Down
2 changes: 2 additions & 0 deletions Sources/CodexBarCore/Providers/Providers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ public enum UsageProvider: String, CaseIterable, Sendable, Codable {
case kimik2
case moonshot
case amp
case t3chat
case ollama
case synthetic
case warp
Expand Down Expand Up @@ -77,6 +78,7 @@ public enum IconStyle: Sendable, CaseIterable {
case jetbrains
case moonshot
case amp
case t3chat
case ollama
case synthetic
case warp
Expand Down
Loading