mirror of
https://github.com/stablyai/orca.git
synced 2026-09-21 16:02:20 +00:00
feat(antigravity): show detected native account (#21798)
This commit is contained in:
@@ -35,6 +35,17 @@ describe('AntigravityAccountService', () => {
|
||||
expect(state.activeAccountId).toBe(secondAccount.id.split('-').slice(0, 2).join('-'))
|
||||
})
|
||||
|
||||
it('reports a signed-in account before it is explicitly saved to the vault', async () => {
|
||||
const active = backend(first)
|
||||
const service = new AntigravityAccountService(createMemoryAntigravityAccountStore(), active)
|
||||
|
||||
const state = await service.listAccounts()
|
||||
|
||||
expect(state.accounts).toEqual([])
|
||||
expect(state.activeAccountId).toBeNull()
|
||||
expect(state.detectedAccount).toMatchObject({ authMethod: 'consumer' })
|
||||
})
|
||||
|
||||
it('switches the live native credential and verifies readback', async () => {
|
||||
const active = backend(first)
|
||||
const account = createSyntheticAntigravityAccount(second)
|
||||
|
||||
@@ -31,6 +31,7 @@ export type AntigravityCredentialBackend = {
|
||||
export type AntigravityAccountState = {
|
||||
accounts: AntigravityAccountSummary[]
|
||||
activeAccountId: string | null
|
||||
detectedAccount: Pick<AntigravityAccountSummary, 'id' | 'email' | 'subject' | 'authMethod'> | null
|
||||
}
|
||||
|
||||
function accountId(contents: string): string {
|
||||
@@ -58,9 +59,24 @@ export class AntigravityAccountService {
|
||||
|
||||
async listAccounts(): Promise<AntigravityAccountState> {
|
||||
const accounts = this.store.read()
|
||||
const active = await this.knownActiveAccount(accounts)
|
||||
const current = await this.credentialBackend.read()
|
||||
const active = current
|
||||
? accounts.find((account) => account.id === accountId(current.contents))
|
||||
: undefined
|
||||
this.activeAccountId = active?.id ?? null
|
||||
return { accounts: accounts.map(summary), activeAccountId: this.activeAccountId }
|
||||
return {
|
||||
accounts: accounts.map(summary),
|
||||
activeAccountId: this.activeAccountId,
|
||||
detectedAccount:
|
||||
current && !active
|
||||
? {
|
||||
id: accountId(current.contents),
|
||||
email: current.identity?.email ?? null,
|
||||
subject: current.identity?.subject ?? null,
|
||||
authMethod: current.authMethod
|
||||
}
|
||||
: null
|
||||
}
|
||||
}
|
||||
|
||||
async addCurrentAccount(): Promise<AntigravityAccountState> {
|
||||
@@ -99,7 +115,7 @@ export class AntigravityAccountService {
|
||||
throw new Error('Antigravity account switching could not be verified.')
|
||||
}
|
||||
this.activeAccountId = id
|
||||
return { accounts: accounts.map(summary), activeAccountId: id }
|
||||
return { accounts: accounts.map(summary), activeAccountId: id, detectedAccount: null }
|
||||
}
|
||||
|
||||
async removeAccount(id: string): Promise<AntigravityAccountState> {
|
||||
|
||||
@@ -68,6 +68,12 @@ export type AntigravityAccountState = {
|
||||
updatedAt: number
|
||||
}[]
|
||||
activeAccountId: string | null
|
||||
detectedAccount: {
|
||||
id: string
|
||||
email: string | null
|
||||
subject: string | null
|
||||
authMethod: string
|
||||
} | null
|
||||
}
|
||||
|
||||
export type AntigravityAccountsApi = {
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
// @vitest-environment happy-dom
|
||||
|
||||
import '@testing-library/jest-dom/vitest'
|
||||
|
||||
import React from 'react'
|
||||
import { cleanup, render, screen } from '@testing-library/react'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
const list = vi.hoisted(() => vi.fn())
|
||||
|
||||
vi.mock('@/i18n/i18n', () => ({
|
||||
translate: (_key: string, fallback: string) => fallback
|
||||
}))
|
||||
|
||||
vi.mock('../ui/button', () => ({
|
||||
Button: ({ children, ...props }: React.ComponentProps<'button'>) =>
|
||||
React.createElement('button', props, children)
|
||||
}))
|
||||
|
||||
vi.mock('../status-bar/icons', () => ({
|
||||
GeminiIcon: () => React.createElement('span', { 'data-testid': 'gemini-icon' })
|
||||
}))
|
||||
|
||||
import { AntigravityAccountsSection } from './AntigravityAccountsSection'
|
||||
|
||||
describe('AntigravityAccountsSection', () => {
|
||||
beforeEach(() => {
|
||||
list.mockResolvedValue({
|
||||
accounts: [],
|
||||
activeAccountId: null,
|
||||
detectedAccount: {
|
||||
id: 'antigravity-detected',
|
||||
email: 'detected@example.com',
|
||||
subject: 'subject',
|
||||
authMethod: 'consumer'
|
||||
}
|
||||
})
|
||||
Object.defineProperty(window, 'api', {
|
||||
configurable: true,
|
||||
value: { antigravityAccounts: { list } }
|
||||
})
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
cleanup()
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
it('shows a detected account before explicit vault save', async () => {
|
||||
render(<AntigravityAccountsSection quota={null} />)
|
||||
|
||||
expect(await screen.findByText('detected@example.com')).toBeInTheDocument()
|
||||
expect(
|
||||
screen.getByText('Detected on this computer; add it to enable switching')
|
||||
).toBeInTheDocument()
|
||||
expect(screen.getByText('Add current agy account')).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
@@ -13,7 +13,8 @@ export function AntigravityAccountsSection({
|
||||
}): React.JSX.Element {
|
||||
const [state, setState] = useState<AntigravityAccountState>({
|
||||
accounts: [],
|
||||
activeAccountId: null
|
||||
activeAccountId: null,
|
||||
detectedAccount: null
|
||||
})
|
||||
const [busy, setBusy] = useState(false)
|
||||
|
||||
@@ -74,6 +75,29 @@ export function AntigravityAccountsSection({
|
||||
))}
|
||||
</p>
|
||||
)}
|
||||
{state.detectedAccount && (
|
||||
<div
|
||||
className="flex items-center justify-between gap-3 rounded-md border border-dashed border-border px-3 py-2"
|
||||
data-detected-account={state.detectedAccount.id}
|
||||
>
|
||||
<div className="min-w-0">
|
||||
<p className="truncate text-sm">
|
||||
{state.detectedAccount.email ??
|
||||
state.detectedAccount.subject ??
|
||||
translate(
|
||||
'auto.components.settings.AccountsPane.antigravitySignedIn',
|
||||
'Signed-in Antigravity account'
|
||||
)}
|
||||
</p>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{translate(
|
||||
'auto.components.settings.AccountsPane.antigravityDetected',
|
||||
'Detected on this computer; add it to enable switching'
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{state.accounts.map((account) => (
|
||||
<div
|
||||
key={account.id}
|
||||
|
||||
@@ -6593,6 +6593,7 @@
|
||||
"antigravityQuotaWindows": "{{value0}} model windows available",
|
||||
"antigravityUnavailable": "Unavailable",
|
||||
"antigravitySignedIn": "Signed-in Antigravity account",
|
||||
"antigravityDetected": "Detected on this computer; add it to enable switching",
|
||||
"antigravityActive": "Active",
|
||||
"antigravityUse": "Use",
|
||||
"antigravityRemove": "Remove",
|
||||
|
||||
@@ -52,7 +52,7 @@ export function createClaudeAccountsApi(): PreloadApi['claudeAccounts'] {
|
||||
}
|
||||
|
||||
export function createAntigravityAccountsApi(): PreloadApi['antigravityAccounts'] {
|
||||
const empty = { accounts: [], activeAccountId: null }
|
||||
const empty = { accounts: [], activeAccountId: null, detectedAccount: null }
|
||||
const unsupported = () =>
|
||||
Promise.reject(new Error('Antigravity account storage is only available in the desktop app.'))
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user