feat: add GitHub Copilot provider (personal + org budget) - #34
Merged
Merged
Conversation
Implement two new connection types for tracking GitHub Copilot billing:
- GITHUB_COPILOT_PAT: personal usage tracking via
/users/{username}/settings/billing/premium_request/usage and
/ai_credit/usage endpoints. Aggregates netAmount across items.
- GITHUB_COPILOT_ORG_BUDGET: organization budget tracking via
/organizations/{org}/settings/billing/budgets endpoint. Filters to
Copilot-relevant SKUs (ai_credits, premium_requests), follows Link
pagination, computes remaining = total - consumed.
Both clients handle 401/403 → AuthError, 429 → RateLimited (with
Retry-After), 5xx → NetworkError, and gracefully degrade on partial
failures.
UI: two connect dialogs (username+PAT and org+PAT), extracted into
GitHubProviderPanel helper to keep AccountEditDialog under detekt
thresholds.
Tests: 23 new test cases covering aggregation, SKU filtering,
pagination, error mapping, malformed secrets, and header assertions.
platformTest failed with NoSuchMethodError on
kotlinx.coroutines.BuildersKt.runBlockingWithParallelismCompensation
during BasePlatformTestCase.tearDown(). The plain
kotlinx-coroutines-core-jvm:1.9.0 (transitive of kotlinx-coroutines-test)
shadowed IntelliJ's patched 1.10.1-intellij-5 in lib/util-8.jar, which is
the only version carrying that method.
- Migrate platformTest from a handrolled register<Test>(...) to the
official intellijPlatformTesting.testIde { } API, so the plugin's
TestIdeTask wires the correct classpath order (and restores
configuration-cache compatibility).
- Exclude transitive kotlinx-coroutines-core / -core-jvm / -bom from
kotlinx-coroutines-test so the plain 1.9.0 core JAR never lands on the
test classpath; coroutines APIs resolve against util-8.jar instead.
Verified: platformTest (5/5 pass), test (incl. RefreshCoordinatorTest),
and check all green; configuration cache is reused.
Replace long-form hint text in provider dialogs with IntelliJ DSL comment() cells, which auto-wrap text at the DSL's default width instead of expanding the dialog horizontally. Changes: - AccountEditDialog: convert providerHintLabel from JBLabel to Cell<JEditorPane> returned by comment(). Update text via component.text property. - GitHubCopilotConnectDialog/OrgConnectDialog: replace body-text label() with comment(), and STEP2_HTML label() with comment(DslCommentText.sanitize()). - OpenAiConnectDialog: replace numbered-step label() calls with comment(). - NebiusConnectDialog: replace numbered-step label() calls with comment(). This keeps dialogs at a consistent, reasonable width regardless of hint length. Short bold headers and step markers remain as label() (intentional visual anchors). Status labels with color styling remain as JBLabel (comment() would override the color). All 776 tests pass; DslCommentHtmlGuardTest verifies no literal <html> tags reach comment() calls.
The DSL comment() label auto-inserts <html>, so the manual <html> wrapper threw a SEVERE UiDslException on every connection-type change in 2025.3. Drop the wrapper and let DslLabel own the tag. Also call window?.pack() after updating the hint text so the dialog shrinks back when switching to a shorter hint instead of staying at its tallest size.
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Summary
Adds GitHub Copilot billing tracking as a new provider with two connection types:
Details
Model layer
Provider.GITHUBenum entry (GH)ConnectionTypevalues:GITHUB_COPILOT_PAT,GITHUB_COPILOT_ORG_BUDGETAuthTypevalues with matching secret-blob docswhenblocks (ProviderRegistry,BalanceRefreshService,AccountEditDialog)Provider clients (
provider/github/)GitHubSecrets— shared encode/parse helpers for the JSON secret blobsGitHubCopilotProviderClient— hitspremium_request/usage+ai_credit/usage, sumsnetAmount, degrades gracefully if the ai_credit call failsGitHubCopilotBudgetProviderClient— hits orgbudgets, filters to Copilot SKUs, follows RFC 5988Linkpagination, computesremaining = total - consumedBoth map
401/403 → AuthError,429 → RateLimited(surfacesRetry-After),5xx → NetworkError.UI
GitHubCopilotConnectDialog(username + PAT) andGitHubCopilotOrgConnectDialog(org + PAT)GitHubProviderPanelhelper extraction keepsAccountEditDialogunder detekt function-count threshold (no@Suppressadded)Testing
GitHubCopilotProviderClientTestandGitHubCopilotBudgetProviderClientTest(aggregation, SKU filtering, pagination, error mapping, malformed secrets, header/path assertions,testCredentials)ProviderTestupdated for the new provider./gradlew build→ BUILD SUCCESSFUL, 776 tests passing, detekt clean