-
Notifications
You must be signed in to change notification settings - Fork 804
Add Abacus AI provider #479
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
ChrisGVE
wants to merge
29
commits into
steipete:main
Choose a base branch
from
ChrisGVE:abacus.ai
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
29 commits
Select commit
Hold shift + click to select a range
ad95010
feat(abacus): add Abacus AI provider with cookie-based usage fetching
ChrisGVE 7864ba8
fix(abacus): fix usage display formatting and match Claude pattern
ChrisGVE 64ba5a7
feat(abacus): add pace tick and detail lines to card view
ChrisGVE 1f29cf8
fix(abacus): use correct API endpoints for credits and billing date
ChrisGVE eb0f559
fix(abacus): validate session cookies and preserve API errors
ChrisGVE 32f3e05
fix(abacus): fix compilation after UsagePaceText API refactor
ChrisGVE 714781c
fix(abacus): fix menu bar metric options and pace indicator
ChrisGVE e57f11a
docs(abacus): add provider documentation and update provider listings
ChrisGVE c348a64
test(abacus): add unit tests for Abacus AI provider
ChrisGVE b90ee1e
fix(abacus): hoist paceWindow binding out of if-expression
ChrisGVE df742ac
fix(abacus): tighten session cookie matching to avoid false positives
ChrisGVE 878c338
fix(abacus): add manual cookie header field to settings UI
ChrisGVE 63e51c0
fix(abacus): route cookie reads through BrowserCookieAccessGate
ChrisGVE 9020793
fix(abacus): suppress reset line when billing date unavailable
ChrisGVE d29f5dd
fix(abacus): remove CSRF from session cookies, propagate billing auth…
ChrisGVE 8230a1a
fix(abacus): tighten cookie matching and retry on auth failure
ChrisGVE 3990a03
refactor(abacus): comprehensive provider overhaul addressing review f…
ChrisGVE b385e4d
fix(abacus): derive pace window from actual billing cycle length
ChrisGVE f7e86a5
fix(abacus): require both credit fields and fix menu bar pace display
ChrisGVE 0879072
fix(abacus): restrict primary-window pace fallback to Abacus only
ChrisGVE 8ad450f
fix(abacus): correct dashboard URL in manual cookie settings action
ChrisGVE 1aae2bd
fix(abacus): default cookie import to Chrome-only per AGENTS.md
ChrisGVE 82b4ffe
fix(abacus): comprehensive quality pass from multi-agent review
ChrisGVE a87fc94
fix(abacus): classify unauthorized JSON errors as auth failures
ChrisGVE 0c8324f
fix(abacus): Chrome-first cookie import with multi-browser fallback
ChrisGVE 551bb24
fix(abacus): fall back to all browsers after Chrome auth exhaustion
ChrisGVE 718e76b
fix(abacus): register in TokenAccountSupportCatalog and bound billing…
ChrisGVE 6fea294
Merge branch 'steipete:main' into abacus.ai
ChrisGVE 632f395
Merge branch 'steipete:main' into abacus.ai
ChrisGVE File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
100 changes: 100 additions & 0 deletions
100
Sources/CodexBar/Providers/Abacus/AbacusProviderImplementation.swift
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,100 @@ | ||
| import AppKit | ||
| import CodexBarCore | ||
| import CodexBarMacroSupport | ||
| import Foundation | ||
| import SwiftUI | ||
|
|
||
| @ProviderImplementationRegistration | ||
| struct AbacusProviderImplementation: ProviderImplementation { | ||
| let id: UsageProvider = .abacus | ||
|
|
||
| @MainActor | ||
| func observeSettings(_ settings: SettingsStore) { | ||
| _ = settings.abacusCookieSource | ||
| _ = settings.abacusCookieHeader | ||
| } | ||
|
|
||
| @MainActor | ||
| func settingsSnapshot(context: ProviderSettingsSnapshotContext) -> ProviderSettingsSnapshotContribution? { | ||
| .abacus(context.settings.abacusSettingsSnapshot(tokenOverride: context.tokenOverride)) | ||
| } | ||
|
|
||
| @MainActor | ||
| func tokenAccountsVisibility(context: ProviderSettingsContext, support: TokenAccountSupport) -> Bool { | ||
| guard support.requiresManualCookieSource else { return true } | ||
| if !context.settings.tokenAccounts(for: context.provider).isEmpty { return true } | ||
| return context.settings.abacusCookieSource == .manual | ||
| } | ||
|
|
||
| @MainActor | ||
| func applyTokenAccountCookieSource(settings: SettingsStore) { | ||
| if settings.abacusCookieSource != .manual { | ||
| settings.abacusCookieSource = .manual | ||
| } | ||
| } | ||
|
|
||
| @MainActor | ||
| func settingsPickers(context: ProviderSettingsContext) -> [ProviderSettingsPickerDescriptor] { | ||
| let cookieBinding = Binding( | ||
| get: { context.settings.abacusCookieSource.rawValue }, | ||
| set: { raw in | ||
| context.settings.abacusCookieSource = ProviderCookieSource(rawValue: raw) ?? .auto | ||
| }) | ||
| let cookieOptions = ProviderCookieSourceUI.options( | ||
| allowsOff: false, | ||
| keychainDisabled: context.settings.debugDisableKeychainAccess) | ||
|
|
||
| let cookieSubtitle: () -> String? = { | ||
| ProviderCookieSourceUI.subtitle( | ||
| source: context.settings.abacusCookieSource, | ||
| keychainDisabled: context.settings.debugDisableKeychainAccess, | ||
| auto: "Automatic imports browser cookies.", | ||
| manual: "Paste a Cookie header or cURL capture from the Abacus AI dashboard.", | ||
| off: "Abacus AI cookies are disabled.") | ||
| } | ||
|
|
||
| return [ | ||
| ProviderSettingsPickerDescriptor( | ||
| id: "abacus-cookie-source", | ||
| title: "Cookie source", | ||
| subtitle: "Automatic imports browser cookies.", | ||
| dynamicSubtitle: cookieSubtitle, | ||
| binding: cookieBinding, | ||
| options: cookieOptions, | ||
| isVisible: nil, | ||
| onChange: nil, | ||
| trailingText: { | ||
| guard let entry = CookieHeaderCache.load(provider: .abacus) else { return nil } | ||
| let when = entry.storedAt.relativeDescription() | ||
| return "Cached: \(entry.sourceLabel) • \(when)" | ||
| }), | ||
| ] | ||
| } | ||
|
|
||
| @MainActor | ||
| func settingsFields(context: ProviderSettingsContext) -> [ProviderSettingsFieldDescriptor] { | ||
| [ | ||
| ProviderSettingsFieldDescriptor( | ||
| id: "abacus-cookie", | ||
| title: "", | ||
| subtitle: "", | ||
| kind: .secure, | ||
| placeholder: "Cookie: \u{2026}\n\nor paste a cURL capture from the Abacus AI dashboard", | ||
| binding: context.stringBinding(\.abacusCookieHeader), | ||
| actions: [ | ||
| ProviderSettingsActionDescriptor( | ||
| id: "abacus-open-dashboard", | ||
| title: "Open Dashboard", | ||
| style: .link, | ||
| isVisible: nil, | ||
| perform: { | ||
| if let url = URL(string: "https://apps.abacus.ai/chatllm/admin/compute-points-usage") { | ||
| NSWorkspace.shared.open(url) | ||
| } | ||
| }), | ||
| ], | ||
| isVisible: { context.settings.abacusCookieSource == .manual }, | ||
| onActivate: nil), | ||
| ] | ||
| } | ||
| } |
61 changes: 61 additions & 0 deletions
61
Sources/CodexBar/Providers/Abacus/AbacusSettingsStore.swift
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,61 @@ | ||
| import CodexBarCore | ||
| import Foundation | ||
|
|
||
| extension SettingsStore { | ||
| var abacusCookieHeader: String { | ||
| get { self.configSnapshot.providerConfig(for: .abacus)?.sanitizedCookieHeader ?? "" } | ||
| set { | ||
| self.updateProviderConfig(provider: .abacus) { entry in | ||
| entry.cookieHeader = self.normalizedConfigValue(newValue) | ||
| } | ||
| self.logSecretUpdate(provider: .abacus, field: "cookieHeader", value: newValue) | ||
| } | ||
| } | ||
|
|
||
| var abacusCookieSource: ProviderCookieSource { | ||
| get { self.resolvedCookieSource(provider: .abacus, fallback: .auto) } | ||
| set { | ||
| self.updateProviderConfig(provider: .abacus) { entry in | ||
| entry.cookieSource = newValue | ||
| } | ||
| self.logProviderModeChange(provider: .abacus, field: "cookieSource", value: newValue.rawValue) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| extension SettingsStore { | ||
| func abacusSettingsSnapshot(tokenOverride: TokenAccountOverride?) -> ProviderSettingsSnapshot | ||
| .AbacusProviderSettings { | ||
| ProviderSettingsSnapshot.AbacusProviderSettings( | ||
| cookieSource: self.abacusSnapshotCookieSource(tokenOverride: tokenOverride), | ||
| manualCookieHeader: self.abacusSnapshotCookieHeader(tokenOverride: tokenOverride)) | ||
| } | ||
|
|
||
| private func abacusSnapshotCookieHeader(tokenOverride: TokenAccountOverride?) -> String { | ||
| let fallback = self.abacusCookieHeader | ||
| guard let support = TokenAccountSupportCatalog.support(for: .abacus), | ||
| case .cookieHeader = support.injection | ||
|
ChrisGVE marked this conversation as resolved.
|
||
| else { | ||
| return fallback | ||
| } | ||
| guard let account = ProviderTokenAccountSelection.selectedAccount( | ||
| provider: .abacus, | ||
| settings: self, | ||
| override: tokenOverride) | ||
| else { | ||
| return fallback | ||
| } | ||
| return TokenAccountSupportCatalog.normalizedCookieHeader(account.token, support: support) | ||
| } | ||
|
|
||
| private func abacusSnapshotCookieSource(tokenOverride _: TokenAccountOverride?) -> ProviderCookieSource { | ||
| let fallback = self.abacusCookieSource | ||
| guard let support = TokenAccountSupportCatalog.support(for: .abacus), | ||
| support.requiresManualCookieSource | ||
| else { | ||
| return fallback | ||
| } | ||
| if self.tokenAccounts(for: .abacus).isEmpty { return fallback } | ||
| return .manual | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.