fix: support configurable Codex usage endpoints#2
fix: support configurable Codex usage endpoints#2Daltonganger wants to merge 17 commits intomainfrom
Conversation
…support (opgginc#84) * feat: add Copilot CLI Keychain token discovery and multi-account CLI support Add macOS Keychain as a token source for GitHub Copilot, enabling automatic discovery of credentials stored by `copilot-cli`. Rewrite the CLI's CopilotCLIProvider to use TokenManager for multi-account support (matching the GUI provider), and fix table column alignment for long provider names. * Fix duplicated "Token from" sections * Address PR opgginc#84 feedback: remove dead state, parallelise token fetch, fix separator width, Add tests covering all three areas Three changes from PR review feedback: 1. Remove unused `cachedUserEmail` (CopilotCLIProvider) The property was declared and assigned in the cookie branch of `fetch()` but never read. Remove the declaration and the assignment so the actor carries no stale mutable state. 2. Parallelise `fetchTokenInfos` (CopilotCLIProvider) The previous sequential `for` loop awaited each account's plan-info and user-login network requests one at a time. Replace it with `withTaskGroup` so every account fires its requests concurrently. The `dedupeTokenInfos` call at the end is preserved unchanged. For users with several Copilot accounts (e.g. personal + org), startup time for the Copilot row is now bounded by the slowest single request rather than the sum of all requests. 3. Dynamic metrics column width in TableFormatter (CLI) `formatSeparator` previously hardcoded a 30-character metrics column, which caused the separator line to be shorter than data rows whenever auth-source labels or long email addresses pushed the metrics string past 30 characters (e.g. "Browser Cookies (Chrome/Brave/Arc/Edge)"). Add `computeMetricsWidth()`, which mirrors `computeProviderWidth()` and scans every rendered metrics string — including Gemini multi-account rows, generic multi-account rows with auth-source brackets, and single-account rows — to find the true maximum. Thread the computed value through `formatSeparator(providerWidth:metricsWidth:)` so the separator is always exactly as wide as the widest row. Add tests covering all three areas: - CLIFormatterTests: 8 new cases for TableFormatter · separator length equals header length for a single-account result · separator grows to accommodate long auth-source bracket labels · `accountLabel` uses `accountId` when present, falls back to `#N` · absolute-path auth sources are shortened to just the filename · tilde-path auth sources are shortened to just the filename · non-path auth sources (e.g. "Browser Cookies …") pass through verbatim · separator is ≥ every data row width for Gemini multi-account results - CopilotProviderTests: 4 new cases for CopilotAuthSource / sourcePriority · all four enum cases exist with unique descriptions · each case's `description` string matches the expected value (including the newly-added `copilotCliKeychain` case) · relative priority ordering: opencodeAuth > copilotCliKeychain > vscodeHosts > vscodeApps · absolute priority values: 3, 2, 1, 0 * fix: address review feedback — shared APIValueParser, unlimited quota fix, priority test coupling - Extract parseDoubleValue/parseIntValue into shared APIValueParser enum in ProviderResult.swift with NSNumber and String→numeric handling (AGENTS.md documented pattern) - Fix unlimited quota_snapshots setting entitlement=0/remaining=0 → Int.max sentinel so downstream guard (limit > 0) passes and UI shows 0% usage correctly - Move CopilotAuthSource.priority to the enum itself (TokenManager.swift) so both GUI+CLI providers delegate to single source of truth - Update CopilotProviderTests to call .priority directly instead of mirrored lookup table Co-authored-by: opencode (Sisyphus, oMo) <no-reply@opencode.ai> Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai> * fix: address bot review — clamping, Keychain parsing, PII log, multi-account test - Remove unused loop index in readCopilotCliKeychainAccounts() - Use lastIndex(of:) for Keychain account username parsing (robustness) - Clamp remaining with max(0,...) in buildCandidateFromUsage for both CLI and GUI - Change API error response log from .error to .debug with 256-char truncation (PII) - Add testTableFormatterMultiAccountRowsAppear for 2-account code path coverage Co-authored-by: opencode (Sisyphus, oMo) <no-reply@opencode.ai> Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai> * fix: address bot review round 2 — Int.max display, test coverage, PII log, dead code - Guard Int.max sentinel in UI rendering: show "Unlimited" instead of raw number - Add 2nd dummy account to 3 shortenAuthSource tests for multi-account path coverage - Fix separator width assertion: >= instead of == (metricsWidth min 30 > header 11) - Downgrade username log from .warning to .debug (PII) - Remove duplicate priority(for:) function, use CopilotAuthSource.priority - Fix 12→8 space indentation in dedupeCopilotAccounts Co-authored-by: opencode (Sisyphus, oMo) <no-reply@opencode.ai> Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai> * fix: address bot review round 3 — test coverage, JSON Int.max guard - Add 2nd account to testTableFormatterSeparatorWidthGrowsForLongAuthSource (multi-account path) - Guard Int.max sentinel in JSONFormatter output: "unlimited" string instead of raw number Co-authored-by: opencode (Sisyphus, oMo) <no-reply@opencode.ai> Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai> * fix: address bot review round 4 — usagePercentage guard, case-insensitive dedup, blank lines - Guard usagePercentage for Int.max: return 0.0 for unlimited plans in JSON output - Use caseInsensitiveCompare for login dedup in CLI dedupeTokenInfos (matches GUI) - Remove triple blank lines (SwiftLint vertical_whitespace) Co-authored-by: opencode (Sisyphus, oMo) <no-reply@opencode.ai> Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai> --------- Co-authored-by: Sangrak Choi <kars@kargn.as> Co-authored-by: opencode (Sisyphus, oMo) <no-reply@opencode.ai> Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Add multi-source account discovery docs (OpenCode auth, Copilot CLI Keychain, VS Code/Cursor config, browser cookies), multi-account CLI output examples, and updated troubleshooting section for Copilot. Co-authored-by: opencode (Sisyphus, oMo) <no-reply@opencode.ai> Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
|
|
||
| func codexUsageURL(for configuration: CodexEndpointConfiguration) -> URL { | ||
| switch configuration.mode { | ||
| case .directChatGPT: |
There was a problem hiding this comment.
The Roast: Using preconditionFailure for what's essentially a configuration sanity check is aggressive. If someone's config file gets corrupted, this will crash the app instead of gracefully falling back.
The Fix: Consider using fatalError() with a message or return an optional/Result type to handle the error gracefully.
Severity: suggestion
Code Review Roast 🔥Verdict: No Issues Found | Recommendation: Merge The previous issue (preconditionFailure crashing on invalid config URL) has been addressed. The fix replaced the crash with proper error handling - logging an error and throwing a 📊 Overall: Like finding a unicorn in production — I didn't think clean PRs existed anymore, but here we are. Files Reviewed (6 files)
|
|
Addressed the review note in f3bc2e2 by replacing the with logged error handling plus a thrown provider error, so the app no longer crashes if the default Codex URL is ever invalid. |
|
Follow-up: this specifically replaces the crashing default-URL assertion with a logged error and a thrown provider error, so invalid configuration no longer terminates the app. |
* Update Claude OAuth usage request headers * Refresh Claude usage script and documentation
* feat: add Nano-GPT provider across app and CLI Integrate Nano-GPT as a quota-based provider with daily/monthly reset tracking, local icon assets, and auth parsing support. Add UI/CLI wiring and tests so Nano-GPT usage, balances, and the subscription preset are available end-to-end. * perf: reuse static date formatters in NanoGptProvider to reduce allocations Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai> * fix: add Antigravity accounts fallback * fix: update Chutes quota usage display * fix: use UTC month boundaries for Chutes usage --------- Co-authored-by: Ruben Beuker <rubenbeuker@MacBook-Pro-van-Ruben.local> Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai> Co-authored-by: Ruben Beuker <rubenbeuker@MacBook-Air-van-Ruben.local>
* Disable unavailable provider menu rows * Add MiniMax coding plan provider support * Document MiniMax provider usage and API behavior
* feat: add Nano-GPT provider across app and CLI Integrate Nano-GPT as a quota-based provider with daily/monthly reset tracking, local icon assets, and auth parsing support. Add UI/CLI wiring and tests so Nano-GPT usage, balances, and the subscription preset are available end-to-end. * perf: reuse static date formatters in NanoGptProvider to reduce allocations Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai> * fix: add Antigravity accounts fallback * fix: add search config fallbacks * fix: resolve lint violations in search config fallback * fix: preserve brave search temp file cleanup --------- Co-authored-by: Ruben Beuker <rubenbeuker@MacBook-Pro-van-Ruben.local> Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai> Co-authored-by: Ruben Beuker <rubenbeuker@MacBook-Air-van-Ruben.local>
…ix/codex-openai-endpoint-config # Conflicts: # CopilotMonitor/CopilotMonitor/Services/TokenManager.swift
Summary
chatgpt_account_idwhile keeping existing account IDs for direct ChatGPT usageVerification
xcodebuild build -project \"CopilotMonitor/CopilotMonitor.xcodeproj\" -scheme \"CopilotMonitor\" -configuration Debug -clonedSourcePackagesDirPath \"/Users/rubenbeuker/Library/Developer/Xcode/DerivedData/CopilotMonitor-aggycqisuzewkzczboobrrtuajll/SourcePackages\"xcodebuild test -project \"CopilotMonitor/CopilotMonitor.xcodeproj\" -scheme \"CopilotMonitor\" -destination 'platform=macOS' -clonedSourcePackagesDirPath \"/Users/rubenbeuker/Library/Developer/Xcode/DerivedData/CopilotMonitor-aggycqisuzewkzczboobrrtuajll/SourcePackages\"/usr/bin/log show --last 2m --predicate 'subsystem == \"com.opencodeproviders\"'