Skip to content

feat: add GitHub Copilot provider (personal + org budget) - #34

Merged
DimazzzZ merged 5 commits into
mainfrom
feat/github-copilot-provider
Aug 18, 2026
Merged

DimazzzZ merged 5 commits into
mainfrom
feat/github-copilot-provider

Conversation

@DimazzzZ

Copy link
Copy Markdown
Owner

Summary

Adds GitHub Copilot billing tracking as a new provider with two connection types:

  • GitHub Copilot (personal) — per-user premium-request and AI-credit spend
  • GitHub Copilot (org budget) — organization budget vs. consumed tracking

Details

Model layer

  • New Provider.GITHUB enum entry (GH)
  • Two ConnectionType values: GITHUB_COPILOT_PAT, GITHUB_COPILOT_ORG_BUDGET
  • Two AuthType values with matching secret-blob docs
  • Updated all exhaustive when blocks (ProviderRegistry, BalanceRefreshService, AccountEditDialog)

Provider clients (provider/github/)

  • GitHubSecrets — shared encode/parse helpers for the JSON secret blobs
  • GitHubCopilotProviderClient — hits premium_request/usage + ai_credit/usage, sums netAmount, degrades gracefully if the ai_credit call fails
  • GitHubCopilotBudgetProviderClient — hits org budgets, filters to Copilot SKUs, follows RFC 5988 Link pagination, computes remaining = total - consumed

Both map 401/403 → AuthError, 429 → RateLimited (surfaces Retry-After), 5xx → NetworkError.

UI

  • GitHubCopilotConnectDialog (username + PAT) and GitHubCopilotOrgConnectDialog (org + PAT)
  • GitHubProviderPanel helper extraction keeps AccountEditDialog under detekt function-count threshold (no @Suppress added)

Testing

  • 23 new test cases across GitHubCopilotProviderClientTest and GitHubCopilotBudgetProviderClientTest (aggregation, SKU filtering, pagination, error mapping, malformed secrets, header/path assertions, testCredentials)
  • ProviderTest updated for the new provider
  • ./gradlew buildBUILD SUCCESSFUL, 776 tests passing, detekt clean

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.
@DimazzzZ DimazzzZ self-assigned this Aug 18, 2026
@DimazzzZ DimazzzZ added type: feature New feature or enhancement domain: provider AI provider integrations labels Aug 18, 2026
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.
@DimazzzZ
DimazzzZ merged commit f402de0 into main Aug 18, 2026
4 checks passed
@DimazzzZ
DimazzzZ deleted the feat/github-copilot-provider branch August 18, 2026 17:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

domain: provider AI provider integrations type: feature New feature or enhancement

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant