Repository navigation
Conversation
📝 WalkthroughWalkthroughThe change adds direct Antigravity credential reading, API access, quota aggregation, token refresh, and rate-limit service integration. It also adds the Merge Risk: 🔵 Low · up to The PR adds real Windows credential-backed usage reporting and OAuth refresh behavior, plus Traditional Chinese UI support. It is broadly mergeable with follow-up to correct the CSS lint violation and confirm that any OAuth client secret is not distributed insecurely with desktop installations. 🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
Full details: Description checkExplanation The description provides useful implementation, validation, security, platform, and ELI5 details, but it omits several required template sections. It does not include a linked issue, visual proof for the UI changes, the required testing checklist, AI disclosure, review, agent skill boundary, notes, or the final checklist. Resolution Add the required template sections and complete them. Include a valid issue reference after “Fixes #”. Attach before-and-after screenshots or videos for the status bar and Traditional Chinese UI changes. Document manual and automated testing, including tested platforms. Add the AI disclosure, review, agent skill upstream boundary, notes, and checklist sections. Full details: Docstring CoverageExplanation Docstring coverage is 12.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 50 functions across 39 files. (6 skipped: 6 unsupported.)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 70a679a0-d0ae-4de3-8db5-c881d393697d
📥 Commits
Reviewing files that changed from the base of the PR and between 54cb772 and 1b8c0163515a8381795a2a495c51e3318a1adca8.
📒 Files selected for processing (36)
electron.vite.config.tssrc/main/i18n/main-i18n.tssrc/main/rate-limits/antigravity-api-client.tssrc/main/rate-limits/antigravity-credentials.test.tssrc/main/rate-limits/antigravity-credentials.tssrc/main/rate-limits/antigravity-quota-aggregation.test.tssrc/main/rate-limits/antigravity-quota-aggregation.tssrc/main/rate-limits/antigravity-usage-fetcher.test.tssrc/main/rate-limits/antigravity-usage-fetcher.tssrc/main/rate-limits/service.test.tssrc/main/rate-limits/service.tssrc/renderer/src/assets/main.csssrc/renderer/src/components/settings/appearance-search.tssrc/renderer/src/components/stats/GrokUsagePane.test.tsxsrc/renderer/src/components/status-bar/StatusBar.tsxsrc/renderer/src/components/status-bar/status-bar-provider-visibility.test.tssrc/renderer/src/components/status-bar/status-bar-provider-visibility.tssrc/renderer/src/components/status-bar/tooltip.tsxsrc/renderer/src/i18n/I18nProvider.tsxsrc/renderer/src/i18n/i18n.tssrc/renderer/src/i18n/locales/en.jsonsrc/renderer/src/i18n/locales/es.jsonsrc/renderer/src/i18n/locales/ja.jsonsrc/renderer/src/i18n/locales/ko.jsonsrc/renderer/src/i18n/locales/zh-TW.jsonsrc/renderer/src/i18n/locales/zh.jsonsrc/renderer/src/i18n/supported-languages.tssrc/renderer/src/shims/diagnostics-channel.tssrc/renderer/src/store/slices/rate-limits.tssrc/renderer/src/web/web-preload-api.tssrc/shared/rate-limit-types.test.tssrc/shared/rate-limit-types.tssrc/shared/ui-language.test.tssrc/shared/ui-language.tssrc/shared/ui-locale.test.tssrc/shared/ui-locale.ts
| const byName = new Map(nextBuckets.map((b) => [b.name, b])) | ||
| let restored = false | ||
| for (const entry of ANTIGRAVITY_BUCKET_ORDER) { | ||
| if (byName.has(entry.name)) { | ||
| continue | ||
| } | ||
| const prevBucket = previous?.buckets?.find((b) => b.name === entry.name) | ||
| if (prevBucket) { | ||
| byName.set(entry.name, prevBucket) | ||
| restored = true | ||
| } | ||
| } | ||
|
|
||
| if (!restored) { | ||
| return next | ||
| } | ||
|
|
||
| const buckets = ANTIGRAVITY_BUCKET_ORDER.map((entry) => byName.get(entry.name)).filter( | ||
| (b): b is RateLimitBucket => b != null | ||
| ) | ||
| const { session, weekly } = deriveAntigravitySessionWeekly(buckets) | ||
| return { | ||
| ...next, | ||
| session, | ||
| weekly, | ||
| buckets, | ||
| error: next.error, | ||
| status: buckets.length > 0 ? 'ok' : next.status, | ||
| usageMetadata: { | ||
| ...next.usageMetadata, | ||
| // Why: restored bars are last-known values, not a fresh official sample. | ||
| lastSuccessfulSource: previous?.usageMetadata?.source ?? next.usageMetadata?.source | ||
| } |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift
Do not refresh restored buckets’ age indefinitely.
Each partial response restores old buckets but retains next.updatedAt and changes the result to ok. On the next refresh, those restored values are treated as newly successful again, so an omitted window can survive indefinitely—even beyond its reset or across credential changes.
Track freshness per restored bucket, or otherwise preserve and enforce the original successful timestamp instead of promoting the merged snapshot as fully fresh.
| runStores: <T>(fn: () => T, ..._args: unknown[]) => T | ||
| } | ||
|
|
||
| type TracingChannel = { | ||
| hasSubscribers: boolean | ||
| subscribe: (..._args: unknown[]) => void | ||
| unsubscribe: (..._args: unknown[]) => void | ||
| traceSync: <T>(fn: () => T, ..._args: unknown[]) => T | ||
| tracePromise: <T>(fn: () => Promise<T>, ..._args: unknown[]) => Promise<T> | ||
| traceCallback: <T>(fn: () => T, ..._args: unknown[]) => T | ||
| } | ||
|
|
||
| function createChannel(): Channel { | ||
| return { | ||
| hasSubscribers: false, | ||
| subscribe() {}, | ||
| unsubscribe() {}, | ||
| publish() {}, | ||
| bindStore() {}, | ||
| unbindStore() {}, | ||
| runStores(fn) { | ||
| return fn() | ||
| } | ||
| } | ||
| } | ||
|
|
||
| function createTracingChannel(): TracingChannel { | ||
| return { | ||
| hasSubscribers: false, | ||
| subscribe() {}, | ||
| unsubscribe() {}, | ||
| traceSync(fn) { | ||
| return fn() | ||
| }, | ||
| tracePromise(fn) { | ||
| return fn() | ||
| }, | ||
| traceCallback(fn) { | ||
| return fn() | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Align API signatures and forward arguments to match Node.js.
The shim's execution methods have incorrect signatures that could lead to runtime crashes:
runStoresexpects(context, fn[, thisArg[, ...args]])in Node.js, but the shim expectsfnas the first argument. Calling it with a context object will throwTypeError: _context is not a function.traceSync,tracePromise, andtraceCallbackdropthisArgand...args. If a library passes these arguments,fnwill not receive them, potentially causing downstream logic to fail.
Update the types and implementations to align with Node.js and forward arguments safely.
🐛 Proposed fixes
- runStores: <T>(fn: () => T, ..._args: unknown[]) => T
+ runStores: <T>(context: unknown, fn: (...args: any[]) => T, thisArg?: any, ...args: any[]) => T
}
type TracingChannel = {
hasSubscribers: boolean
subscribe: (..._args: unknown[]) => void
unsubscribe: (..._args: unknown[]) => void
- traceSync: <T>(fn: () => T, ..._args: unknown[]) => T
- tracePromise: <T>(fn: () => Promise<T>, ..._args: unknown[]) => Promise<T>
- traceCallback: <T>(fn: () => T, ..._args: unknown[]) => T
+ traceSync: <T>(fn: (...args: any[]) => T, context?: unknown, thisArg?: any, ...args: any[]) => T
+ tracePromise: <T>(fn: (...args: any[]) => Promise<T>, context?: unknown, thisArg?: any, ...args: any[]) => Promise<T>
+ traceCallback: <T>(fn: (...args: any[]) => T, position?: number, context?: unknown, thisArg?: any, ...args: any[]) => T
}
function createChannel(): Channel {
return {
hasSubscribers: false,
subscribe() {},
unsubscribe() {},
publish() {},
bindStore() {},
unbindStore() {},
- runStores(fn) {
- return fn()
+ runStores(_context, fn, thisArg, ...args) {
+ return fn.apply(thisArg, args)
}
}
}
function createTracingChannel(): TracingChannel {
return {
hasSubscribers: false,
subscribe() {},
unsubscribe() {},
- traceSync(fn) {
- return fn()
+ traceSync(fn, _context, thisArg, ...args) {
+ return fn.apply(thisArg, args)
},
- tracePromise(fn) {
- return fn()
+ tracePromise(fn, _context, thisArg, ...args) {
+ return fn.apply(thisArg, args)
},
- traceCallback(fn) {
- return fn()
+ traceCallback(fn, _position, _context, thisArg, ...args) {
+ return fn.apply(thisArg, args)
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| runStores: <T>(fn: () => T, ..._args: unknown[]) => T | |
| } | |
| type TracingChannel = { | |
| hasSubscribers: boolean | |
| subscribe: (..._args: unknown[]) => void | |
| unsubscribe: (..._args: unknown[]) => void | |
| traceSync: <T>(fn: () => T, ..._args: unknown[]) => T | |
| tracePromise: <T>(fn: () => Promise<T>, ..._args: unknown[]) => Promise<T> | |
| traceCallback: <T>(fn: () => T, ..._args: unknown[]) => T | |
| } | |
| function createChannel(): Channel { | |
| return { | |
| hasSubscribers: false, | |
| subscribe() {}, | |
| unsubscribe() {}, | |
| publish() {}, | |
| bindStore() {}, | |
| unbindStore() {}, | |
| runStores(fn) { | |
| return fn() | |
| } | |
| } | |
| } | |
| function createTracingChannel(): TracingChannel { | |
| return { | |
| hasSubscribers: false, | |
| subscribe() {}, | |
| unsubscribe() {}, | |
| traceSync(fn) { | |
| return fn() | |
| }, | |
| tracePromise(fn) { | |
| return fn() | |
| }, | |
| traceCallback(fn) { | |
| return fn() | |
| } | |
| runStores: <T>(context: unknown, fn: (...args: any[]) => T, thisArg?: any, ...args: any[]) => T | |
| } | |
| type TracingChannel = { | |
| hasSubscribers: boolean | |
| subscribe: (..._args: unknown[]) => void | |
| unsubscribe: (..._args: unknown[]) => void | |
| traceSync: <T>(fn: (...args: any[]) => T, context?: unknown, thisArg?: any, ...args: any[]) => T | |
| tracePromise: <T>(fn: (...args: any[]) => Promise<T>, context?: unknown, thisArg?: any, ...args: any[]) => Promise<T> | |
| traceCallback: <T>(fn: (...args: any[]) => T, position?: number, context?: unknown, thisArg?: any, ...args: any[]) => T | |
| } | |
| function createChannel(): Channel { | |
| return { | |
| hasSubscribers: false, | |
| subscribe() {}, | |
| unsubscribe() {}, | |
| publish() {}, | |
| bindStore() {}, | |
| unbindStore() {}, | |
| runStores(_context, fn, thisArg, ...args) { | |
| return fn.apply(thisArg, args) | |
| } | |
| } | |
| } | |
| function createTracingChannel(): TracingChannel { | |
| return { | |
| hasSubscribers: false, | |
| subscribe() {}, | |
| unsubscribe() {}, | |
| traceSync(fn, _context, thisArg, ...args) { | |
| return fn.apply(thisArg, args) | |
| }, | |
| tracePromise(fn, _context, thisArg, ...args) { | |
| return fn.apply(thisArg, args) | |
| }, | |
| traceCallback(fn, _position, _context, thisArg, ...args) { | |
| return fn.apply(thisArg, args) | |
| } |
|
Taking a look! |
1b8c016 to
3511d84
Compare
|
Thanks for the contribution — I rebased this onto current |
Rebase the stalled stablyai#9360 work onto current main. Read Antigravity quota from Windows Credential Manager instead of mirroring Gemini, and add Traditional Chinese as a first-class UI locale. Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
3511d84 to
c699fa7
Compare
|
Note The previously reviewed commits are no longer reachable (likely due to a force-push or rebase), so CodeRabbit is performing a full review instead of an incremental one. This review may take a little longer. |
|
Rebased onto current What changed in this refresh:
Ready for another look when you have time. |
Greptile SummaryThe PR replaces mirrored Gemini data with Windows Credential Manager-backed Antigravity quota retrieval and adds Traditional Chinese as a built-in locale.
Confidence Score: 4/5The PR appears safe to merge, with a non-blocking responsiveness issue when Antigravity retries keep the shared rate-limit refresh cycle occupied. Other provider state is published independently, but manual and queued refresh completion still waits for Antigravity's sequential multi-host timeout path. Files Needing Attention: src/main/rate-limits/service.ts, src/main/rate-limits/antigravity-api-client.ts
|
| Filename | Overview |
|---|---|
| src/main/rate-limits/antigravity-api-client.ts | Adds timeout-bound OAuth and quota API requests with project/body and multi-host fallbacks; the sequential fallback depth can prolong the enclosing refresh cycle. |
| src/main/rate-limits/antigravity-credentials.ts | Adds a cancellable, read-only Windows Credential Manager probe and validates the returned OAuth credential shape. |
| src/main/rate-limits/antigravity-quota-aggregation.ts | Converts quota summary and model fallback responses into separate Claude/Gemini 5-hour and weekly buckets. |
| src/main/rate-limits/antigravity-usage-fetcher.ts | Coordinates credentials, project discovery, quota requests, one authentication refresh attempt, and explicit failure metadata. |
| src/main/rate-limits/service.ts | Integrates independent Antigravity polling and durability state, but retains the shared fetch lock until its potentially long retry chain finishes. |
| src/renderer/src/components/status-bar/StatusBar.tsx | Presents Antigravity family buckets in compact and verbose status-bar modes and bases visibility on credential durability. |
| src/renderer/src/i18n/I18nProvider.tsx | Updates document language only after the latest requested lazy catalog activation completes. |
| src/shared/ui-locale.ts | Maps Taiwan, Hong Kong, Macau, and Hant Chinese locale tags to the new Traditional Chinese catalog. |
| src/renderer/src/i18n/locales/zh-TW.json | Adds the complete Traditional Chinese translation catalog. |
Sequence Diagram
sequenceDiagram
participant UI as Renderer status bar
participant S as RateLimitService
participant C as Windows Credential Manager
participant A as Antigravity APIs
S->>C: Read gemini:antigravity credential
par Existing providers
S->>S: Fetch and publish provider results
and Antigravity
C-->>S: Access and refresh tokens
S->>A: loadCodeAssist
par Quota requests
S->>A: retrieveUserQuotaSummary
and Model fallback
S->>A: fetchAvailableModels
end
A-->>S: Quota windows
end
S->>S: Aggregate Claude/Gemini 5h and weekly buckets
S-->>UI: Push rate-limit state
Reviews (1): Last reviewed commit: "feat: real Antigravity usage and zh-TW U..." | Re-trigger Greptile
| const [antigravityResult, grokResult] = await Promise.all([ | ||
| antigravityResultPromise, | ||
| grokResultPromise | ||
| ]) |
There was a problem hiding this comment.
Antigravity Holds Refresh Lock
If the Credential Manager or quota endpoints are slow or unavailable, the full-provider cycle retains isFetching while Antigravity performs its sequential timeout-bound retries. Manual refresh remains pending, forced refreshes queue behind it, and ordinary polls are skipped for up to several minutes even though the other provider results have already been published.
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
There was a problem hiding this comment.
Actionable comments posted: 1
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: a3d94456-d5e9-4043-9c02-0de89b32de20
📒 Files selected for processing (45)
src/main/codex-accounts/service-reset-credit-test-fixtures.tssrc/main/i18n/main-i18n.tssrc/main/rate-limits/antigravity-api-client.tssrc/main/rate-limits/antigravity-credentials.test.tssrc/main/rate-limits/antigravity-credentials.tssrc/main/rate-limits/antigravity-quota-aggregation.test.tssrc/main/rate-limits/antigravity-quota-aggregation.tssrc/main/rate-limits/antigravity-usage-fetcher.test.tssrc/main/rate-limits/antigravity-usage-fetcher.tssrc/main/rate-limits/antigravity-usage-mirror.test.tssrc/main/rate-limits/antigravity-usage-mirror.tssrc/main/rate-limits/rate-limit-service-test-harness.tssrc/main/rate-limits/service-account-target-selection.test.tssrc/main/rate-limits/service-antigravity-usage.test.tssrc/main/rate-limits/service-inactive-account-previews.test.tssrc/main/rate-limits/service-live-claude-usage.test.tssrc/main/rate-limits/service-minimax-usage.test.tssrc/main/rate-limits/service-refresh-orchestration.test.tssrc/main/rate-limits/service-window-activation.test.tssrc/main/rate-limits/service.tssrc/renderer/src/assets/main.csssrc/renderer/src/components/settings/appearance-search.tssrc/renderer/src/components/stats/GrokUsagePane.test.tsxsrc/renderer/src/components/status-bar/StatusBar.tsxsrc/renderer/src/components/status-bar/status-bar-provider-visibility.test.tssrc/renderer/src/components/status-bar/status-bar-provider-visibility.tssrc/renderer/src/components/status-bar/tooltip.tsxsrc/renderer/src/i18n/I18nProvider.test.tsxsrc/renderer/src/i18n/I18nProvider.tsxsrc/renderer/src/i18n/i18n.tssrc/renderer/src/i18n/locales/en.jsonsrc/renderer/src/i18n/locales/es.jsonsrc/renderer/src/i18n/locales/ja.jsonsrc/renderer/src/i18n/locales/ko.jsonsrc/renderer/src/i18n/locales/zh-TW.jsonsrc/renderer/src/i18n/locales/zh.jsonsrc/renderer/src/i18n/supported-languages.tssrc/renderer/src/store/slices/rate-limits.tssrc/renderer/src/web/preload-api/web-rate-limits-api.tssrc/shared/rate-limit-types.test.tssrc/shared/rate-limit-types.tssrc/shared/ui-language.test.tssrc/shared/ui-language.tssrc/shared/ui-locale.test.tssrc/shared/ui-locale.ts
💤 Files with no reviewable changes (2)
- src/main/rate-limits/antigravity-usage-mirror.ts
- src/main/rate-limits/antigravity-usage-mirror.test.ts
🚧 Files skipped from review as they are similar to previous changes (22)
- src/renderer/src/i18n/locales/es.json
- src/shared/rate-limit-types.ts
- src/renderer/src/i18n/i18n.ts
- src/shared/rate-limit-types.test.ts
- src/renderer/src/components/settings/appearance-search.ts
- src/shared/ui-language.test.ts
- src/renderer/src/components/status-bar/tooltip.tsx
- src/renderer/src/store/slices/rate-limits.ts
- src/shared/ui-locale.ts
- src/renderer/src/components/status-bar/status-bar-provider-visibility.test.ts
- src/shared/ui-language.ts
- src/main/i18n/main-i18n.ts
- src/renderer/src/components/status-bar/StatusBar.tsx
- src/shared/ui-locale.test.ts
- src/renderer/src/components/status-bar/status-bar-provider-visibility.ts
- src/renderer/src/i18n/locales/zh.json
- src/renderer/src/i18n/locales/ja.json
- src/renderer/src/components/stats/GrokUsagePane.test.tsx
- src/renderer/src/i18n/supported-languages.ts
- src/renderer/src/i18n/locales/en.json
- src/renderer/src/i18n/locales/ko.json
- src/main/rate-limits/antigravity-api-client.ts
Included review availability: Your plan provides up to 10 included reviews per hour; 9 remain after this review.
| (uneven stroke weight). Pin one CJK family per Chinese locale. */ | ||
| :root:lang(zh) { | ||
| --app-font-family: | ||
| 'Geist', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Microsoft YaHei', |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Use the configured lowercase font keyword.
Stylelint rejects BlinkMacSystemFont under value-keyword-case. Change both occurrences to blinkmacsystemfont so the lint gate passes.
Also applies to: 261-261
🧰 Tools
🪛 Stylelint (17.14.0)
[error] 254-254: Expected "BlinkMacSystemFont" to be "blinkmacsystemfont" (value-keyword-case)
(value-keyword-case)
Source: Linters/SAST tools
|
Takeover verification at A failed optional model request discards a successful quota summary. I compiled the exact candidate modules and mocked only Electron networking, passing synthetic credentials directly. A summary containing one Gemini five-hour bucket at 75% remaining produced ELI5: Orca gets the usage numbers, but throws them away because a second, optional request failed. Settle the requests independently while preserving caller cancellation and explicit authentication failures; use the valid summary even when the model fallback is unavailable. Add this rejected-request case alongside HTTP-status cases. Separately, the Windows credential reader adds direct This review does not verify Windows credential lookup, live OAuth quota, or account switching. The independent native |
Summary
zh-TW) as a first-class UI locale, including locale-aware font fallbacks and catalog parity.mainand harden the implementation for cancellation, partial API responses, authentication refresh, and stale-data handling.Implementation notes
<html lang>updates only after the latest lazy catalog activation succeeds.Validation
pnpm run typecheckpassed.pnpm run verify:localization-catalogpassed.pnpm run check:max-lines-ratchetpassed.pnpm run build:electron-vitepassed.Platform and security
Antigravity quota is currently Windows-only because it reads the desktop host Credential Manager entry. Credentials remain read-only and in memory; token refresh uses the optional OAuth client environment configuration and otherwise relies on the current access token stored by Antigravity.
Contributor: @RX5950XT
ELI5
Antigravity usage is read from real Windows credential data instead of faking Gemini numbers, with separate time windows in the status bar. Traditional Chinese (zh-TW) is also a full UI language option.