Skip to content

feat(oauth): support importing Google Antigravity accounts from Cockpit Tools JSON (#1076) - #1077

Closed
agentHits wants to merge 4 commits into
lidge-jun:devfrom
agentHits:feat/import-cockpit-tools-accounts
Closed

feat(oauth): support importing Google Antigravity accounts from Cockpit Tools JSON (#1076)#1077
agentHits wants to merge 4 commits into
lidge-jun:devfrom
agentHits:feat/import-cockpit-tools-accounts

Conversation

@agentHits

@agentHits agentHits commented Aug 5, 2026

Copy link
Copy Markdown

Summary

Fixes #1076.

Adds support for batch importing Google Antigravity / Gemini accounts exported from Cockpit Tools (or any valid JSON array containing email and refresh_token).

Key Changes

  1. API Endpoint (POST /api/oauth/accounts/import):

    • Accepts Cockpit Tools export array format:
      [
        {
          "email": "user@gmail.com",
          "refresh_token": "1//..."
        }
      ]
    • Restricted to google-antigravity provider to align with available token discovery capabilities.
    • Refreshes tokens via refreshAntigravityToken to validate credentials, decode user email, and discover Cloud Code Assist projectId.
    • Persists valid accounts into auth.json provider account pool.
    • Clears quota cache and returns import status summary (importedCount, failedCount, detailed per-account results).
  2. CLI Integration (ocx account import <provider> <file-or-json>):

    • Support importing accounts directly from CLI via JSON file path or raw string.
  3. Web UI Integration (ProviderAuthPanel.tsx):

    • Added Import JSON (Cockpit) action button for google-antigravity and JSON input area under Providers -> Google Antigravity -> Accounts.
    • Full i18n support across all 6 locales (en, ru, zh, de, ja, ko).
  4. Automated Tests (tests/oauth-accounts-api.test.ts & tests/cli-account.test.ts):

    • Added unit tests for payload validation, invalid token handling, array element preservation, CLI cmdImport validation, and API execution.

Verification

Ran bun test --preload ./tests/preload.ts tests/oauth-accounts-api.test.ts tests/cli-account.test.ts and bun run typecheck:

  • 71 pass, 0 fail
  • tsc --noEmit passed with 0 errors.

Review readiness checklist

This PR stays in draft until every box below is ticked. Tick all four boxes once the requirements are met:

  • All CI tests are green on my local testing.
  • I pushed my PR to the latest dev commit.
  • I fixed all correct Codex and CodeRabbit findings.
  • My PR is ready for review.

Summary by CodeRabbit

  • New Features
    • Import OAuth accounts from JSON through the provider workspace, with support for inline or wrapped account data.
    • Added CLI support for importing accounts from JSON text or files, with human-readable or JSON results.
    • Imports report successful and failed accounts and refresh account data after completion.
    • Added validation for supported providers, malformed input, account limits, and invalid entries.
  • Localization
    • Added import-related translations across supported languages.
  • Tests
    • Added coverage for UI/API and CLI import scenarios, validation, errors, and result reporting.

@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown

Review readiness checklist

This PR is kept in draft until every requirement below is fulfilled. The tickable checklist has been added to your PR description — tick all four boxes there.

  • ✅ All CI tests are green on my local testing.
  • ✅ I pushed my PR to the latest dev commit.
  • ⬜ I fixed all correct Codex and CodeRabbit findings.
  • ✅ My PR is ready for review.

3/4 boxes ticked.

This PR stays in draft until every box above is ticked.

@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown

PR quality gates passed

This pull request now targets dev with acceptable ancestry, description, and UI screenshot coverage. It stays in draft until the review readiness checklist is complete.

The title was left unchanged. The draft is owned by the checklist message below.

@github-actions github-actions Bot changed the title feat(oauth): support importing Google Antigravity accounts from Cockpit Tools JSON (#1076) [WRONG BRANCH] feat(oauth): support importing Google Antigravity accounts from Cockpit Tools JSON (#1076) Aug 5, 2026
@github-actions
github-actions Bot marked this pull request as draft August 5, 2026 20:28
@github-actions github-actions Bot added the intake: hygiene-blocked Deterministic PR hygiene checks failed label Aug 5, 2026
@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown

⚠️ Deterministic hygiene checks failed.

  • unsponsored_surface — This changes an authentication, workflow, release-automation, or dependency surface. MAINTAINERS.md requires security review for these; ask a maintainer to apply maintainer-sponsored once they have reviewed it. Paths: src/server/management/oauth-account-routes.ts.

@github-actions github-actions Bot added the enhancement New feature or request label Aug 5, 2026
@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

This PR adds batch OAuth account import support. The API validates and processes account payloads, the CLI accepts file or inline JSON, and the provider workspace provides a Cockpit JSON import form with result reporting and account refresh.

Changes

OAuth account import

Layer / File(s) Summary
OAuth import API and validation
src/server/management/oauth-account-routes.ts, tests/oauth-accounts-api.test.ts
POST /api/oauth/accounts/import accepts supported payload shapes, validates providers and account limits, processes credentials individually, returns aggregate results, and reconciles caches. Tests cover invalid providers, empty lists, oversized batches, invalid tokens, and invalid entries.
CLI import command
src/cli/account.ts, src/cli/account-extended.ts, tests/cli-account.test.ts
ocx account import <provider> <file-or-json> [--json] loads inline or file JSON, submits it to the import API, and reports human-readable or JSON results.
Provider workspace import flow and catalogs
gui/src/components/provider-workspace/ProviderAuthPanel.tsx, gui/src/i18n/*.ts
The provider panel adds Cockpit-only JSON editing, submission state, cancellation, result counts, and account-list refresh. Six language catalogs add import labels and result summaries.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant ProviderAuthPanel
  participant OAuthAccountRoutes
  participant OAuthCredentials
  User->>ProviderAuthPanel: Paste account JSON and submit
  ProviderAuthPanel->>OAuthAccountRoutes: POST /api/oauth/accounts/import
  OAuthAccountRoutes->>OAuthCredentials: Refresh and save credentials
  OAuthCredentials-->>OAuthAccountRoutes: Return per-account results
  OAuthAccountRoutes-->>ProviderAuthPanel: Return import and failure counts
  ProviderAuthPanel-->>User: Display results and refresh accounts
Loading

Possibly related PRs

  • lidge-jun/opencodex#479: Both PRs modify ProviderAuthPanel.tsx and oauth-account-routes.ts for OAuth account management.

Suggested reviewers: ingwannu, wibias, lidge-jun

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The changes satisfy issue #1076 by adding validated batch imports, CLI support, a Google Antigravity UI action, persistence, and tests.
Out of Scope Changes check ✅ Passed The API, CLI, UI, translations, and tests directly support the account import objectives in issue #1076.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes importing Google Antigravity accounts from Cockpit Tools JSON, which is the primary change.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@gui/src/components/provider-workspace/ProviderAuthPanel.tsx`:
- Around line 292-365: Replace the hardcoded import UI strings in
ProviderAuthPanel, including the JSON placeholder, import result sentence,
“Import JSON”, and “Import JSON (Cockpit)”, with appropriate t(...) lookups. Add
matching translation keys and values to the GUI locale files, preserving
interpolation for imported and failed counts in the result message.
- Around line 287-295: Update the placeholder attribute on the import JSON
textarea in ProviderAuthPanel to use a JSX expression containing a valid
JavaScript string, preserving the example JSON text and its inner quotes.

In `@src/cli/account-extended.ts`:
- Around line 353-410: The cmdImport CLI flow lacks regression coverage for its
argument validation, provider validation, inline and file JSON inputs, API
errors, and JSON output. Add focused Bun/account CLI tests invoking cmdImport
through the account command path, covering invalid argument counts, non-OAuth
providers, successful inline JSON import, successful file-based JSON import,
non-200 API responses, and --json output; assert the relevant return values, API
payloads, and console output while reusing existing test helpers and fixtures.

In `@src/server/management/oauth-account-routes.ts`:
- Around line 395-433: Restrict account import to google-antigravity at both
boundaries: in the account import route around provider validation and the batch
loop, reject any other provider with a clear 400 response before iterating; in
ProviderAuthPanel, render the import action only when item.name is
google-antigravity so the GUI matches the management API behavior.
- Around line 384-392: Update the account-entry handling around the
rawBody/accounts array parsing to preserve every submitted array element instead
of filtering non-record values out. Track totalCount from the original input
array length, emit a failed per-account result for each non-record entry, and
continue normal processing for valid records; add a test covering a mixed
valid-and-invalid batch.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: db59e0f4-ce49-4fec-bb58-ae3cbe1f9932

📥 Commits

Reviewing files that changed from the base of the PR and between 99440ec and 2f79fea.

📒 Files selected for processing (5)
  • gui/src/components/provider-workspace/ProviderAuthPanel.tsx
  • src/cli/account-extended.ts
  • src/cli/account.ts
  • src/server/management/oauth-account-routes.ts
  • tests/oauth-accounts-api.test.ts

Comment thread gui/src/components/provider-workspace/ProviderAuthPanel.tsx
Comment on lines +292 to +365
placeholder="[{\"email\":\"user@gmail.com\",\"refresh_token\":\"1//...\"}]"
disabled={importBusy}
style={{ fontFamily: "var(--font-mono, monospace)", fontSize: 12 }}
/>
{importResult && (
<div className="muted faint" style={{ fontSize: 12, marginTop: 4 }}>
Imported {importResult.imported} account(s), {importResult.failed} failed.
</div>
)}
<div style={{ display: "flex", gap: 8, marginTop: 8 }}>
<button
type="button"
className="btn btn-primary btn-sm"
disabled={importBusy || !importJsonText.trim()}
onClick={async () => {
const text = importJsonText.trim();
if (!text) return;
setImportBusy(true);
setImportResult(null);
try {
let parsed: unknown;
try {
parsed = JSON.parse(text);
} catch {
setImportResult({ imported: 0, failed: 1 });
return;
}
const res = await fetch(`${apiBase}/api/oauth/accounts/import`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
provider: item.name,
accounts: Array.isArray(parsed) ? parsed : (parsed as { accounts?: unknown })?.accounts ?? parsed,
}),
});
if (res.ok) {
const data = (await res.json()) as { importedCount?: number; failedCount?: number };
setImportResult({ imported: data.importedCount ?? 0, failed: data.failedCount ?? 0 });
if ((data.importedCount ?? 0) > 0) {
setImportJsonText("");
void authHandlers.onRetryAccounts?.(item.name);
}
} else {
setImportResult({ imported: 0, failed: 1 });
}
} catch {
setImportResult({ imported: 0, failed: 1 });
} finally {
setImportBusy(false);
}
}}
>
{importBusy ? t("pws.saving") : "Import JSON"}
</button>
<button
type="button"
className="btn btn-ghost btn-sm"
onClick={() => { setImportingJson(false); setImportJsonText(""); setImportResult(null); }}
>
{t("common.cancel")}
</button>
</div>
</div>
) : (
<div style={{ display: "flex", gap: 8, marginTop: 8 }}>
{loggedIn && (
<button type="button" className="btn btn-ghost btn-sm"
onClick={() => void authHandlers.onLogin(item.name, true)} disabled={busy || Boolean(switchingAccountId)}>
{t("pws.addAccount")}
</button>
)}
<button type="button" className="btn btn-ghost btn-sm"
onClick={() => setImportingJson(true)} disabled={busy || Boolean(switchingAccountId)}>
Import JSON (Cockpit)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Move import UI text into locale files.

Lines 292, 298, 344, and 365 hardcode user-visible placeholder, result, and button text. Use t(...) keys and add the translations to the locale files. This includes "Import JSON", "Import JSON (Cockpit)", and the import result sentence.

As per path instructions, GUI user-visible strings must use i18n locale files rather than hardcoded text.

🧰 Tools
🪛 Biome (2.5.6)

[error] 292-292: unexpected token \

(parse)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@gui/src/components/provider-workspace/ProviderAuthPanel.tsx` around lines 292
- 365, Replace the hardcoded import UI strings in ProviderAuthPanel, including
the JSON placeholder, import result sentence, “Import JSON”, and “Import JSON
(Cockpit)”, with appropriate t(...) lookups. Add matching translation keys and
values to the GUI locale files, preserving interpolation for imported and failed
counts in the result message.

Source: Path instructions

Comment thread src/cli/account-extended.ts
Comment thread src/server/management/oauth-account-routes.ts
Comment thread src/server/management/oauth-account-routes.ts

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2f79feacb1

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

return usage("Error: account import only applies to OAuth providers (such as google-antigravity)");
}

let jsonText = input;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Avoid accepting credential JSON on the command line

When input is not an existing file, the command parses that argv value as JSON; for the advertised <file-or-json> path this means a refresh_token can be supplied directly on the command line, exposing it via shell history and process listings. This is a new credential-handling path, so require a file or stdin (-) instead of accepting raw secret JSON in argv.

AGENTS.md reference: AGENTS.md:L218-L224

Useful? React with 👍 / 👎.

if (provider === "google-antigravity") {
const creds = await refreshAntigravityToken(refreshToken);
if (inputEmail && !creds.email) creds.email = inputEmail;
await saveCredential(provider, creds, { preserveIdentityless: true });

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject imports without a discovered CCA project

When refreshAntigravityToken() returns an access/refresh pair but cannot discover or onboard a Cloud Code Assist project, this still saves the credential and reports it as imported. The Antigravity adapter later requires provider.project and throws, so the just-imported account appears successful but cannot serve requests; mirror the login flow by treating missing creds.projectId as a failed import.

Useful? React with 👍 / 👎.

const invalidToken = await fetch(new URL("/api/oauth/accounts/import", server.url), {
method: "POST", headers: { "Content-Type": "application/json" },
body: JSON.stringify([
{ email: "bad@example.com", refresh_token: "invalid-token" }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep the import API test offline

This validation case uses a non-empty refresh_token, so the new import handler proceeds into refreshAntigravityToken() and makes a real call to Google's OAuth token endpoint before returning the expected failed result. In CI/offline runs or during Google outages the unit test can wait for the token-request timeout or fail for the wrong reason; stub the refresh/fetch path or use a missing-token payload for local validation.

Useful? React with 👍 / 👎.

}
}}
>
{importBusy ? t("pws.saving") : "Import JSON"}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Use i18n keys for import UI copy

The new import controls render visible English directly in JSX (Imported ..., Import JSON, Import JSON (Cockpit)), so localized dashboards get untranslated copy and lint:i18n should reject the component. Add locale keys in all gui/src/i18n/*.ts modules and render these strings via t(...).

AGENTS.md reference: gui/AGENTS.md:L14-L18

Useful? React with 👍 / 👎.

if (provider === "google-antigravity") {
const creds = await refreshAntigravityToken(refreshToken);
if (inputEmail && !creds.email) creds.email = inputEmail;
await saveCredential(provider, creds, { preserveIdentityless: true });

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Upsert the provider row after a successful import

ocx account import google-antigravity ... is accepted even on a fresh config because the CLI classifies public OAuth provider names as OAuth, but this handler only writes the credential store. Unlike the normal login path, it never upserts and saves config.providers[provider], so a successful import can leave the account present in auth.json but with no routable provider card or model route until the user performs a separate provider-add/login operation.

Useful? React with 👍 / 👎.

}
}
}
return 0;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Return failure when every CLI import entry fails

When the import API returns HTTP 200 with { ok: false, importedCount: 0, failedCount: ... } for revoked or invalid refresh tokens, the CLI still prints the result and exits 0 here. Scripts and users will treat a completely failed import as successful; check the response ok/counts and return a non-zero status when no account was imported.

Useful? React with 👍 / 👎.

Comment on lines +363 to +365
<button type="button" className="btn btn-ghost btn-sm"
onClick={() => setImportingJson(true)} disabled={busy || Boolean(switchingAccountId)}>
Import JSON (Cockpit)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Hide Cockpit import on unsupported OAuth providers

This button is rendered for every OAuth provider's Accounts tab, but the management handler only imports google-antigravity and returns per-entry failures for Anthropic, xAI, Kimi, Kiro, Cursor, etc. Users can paste sensitive Cockpit JSON into a provider that can never accept it; gate the control to the provider the endpoint actually supports or make the endpoint support the same set the UI exposes.

AGENTS.md reference: gui/AGENTS.md:L9-L10

Useful? React with 👍 / 👎.

const set = getAccountSet(provider);
const identity = creds.accountId ?? creds.email;
const activeAcc = set?.accounts.find(a => (a.credential.accountId ?? a.credential.email) === identity);
results.push({ email: creds.email ?? inputEmail, accountId: activeAcc?.id, status: "imported" });

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Mask imported account emails in the management response

Successful and failed import results serialize creds.email/inputEmail verbatim, while the existing OAuth account-list/status surfaces mask account emails before returning them. In the dashboard and CLI this exposes full Google account identifiers for every imported or failed row; return masked emails (or omit them and use the account id) before serializing the result.

Useful? React with 👍 / 👎.

if (provider === "google-antigravity") {
const creds = await refreshAntigravityToken(refreshToken);
if (inputEmail && !creds.email) creds.email = inputEmail;
await saveCredential(provider, creds, { preserveIdentityless: true });

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve refresh-token-only batch imports

For Cockpit rows that omit email (or for Google refresh responses that do not include an id token/email), creds.accountId and creds.email remain undefined before saveCredential(). The store's identityless path replaces the active slot instead of appending, so a batch of refresh-token-only entries can report every row as imported while only the last credential survives; require an identity for import or store these rows under a stable refresh-derived account id.

Useful? React with 👍 / 👎.

…dge-jun#1076)

- Restrict account import route and GUI import button to google-antigravity
- Preserve totalCount and handle non-object elements gracefully in import loop
- Add i18n translation keys (pws.importJson, pws.importJsonCockpit, pws.importResultSummary) across all 6 locales
- Fix JSX placeholder string syntax in ProviderAuthPanel
- Update CLI EXTENDED_USAGE and add tests for cmdImport
@agentHits
agentHits force-pushed the feat/import-cockpit-tools-accounts branch from 2f79fea to 8f1a74e Compare August 5, 2026 20:44
@agentHits agentHits changed the title [WRONG BRANCH] feat(oauth): support importing Google Antigravity accounts from Cockpit Tools JSON (#1076) feat(oauth): support importing Google Antigravity accounts from Cockpit Tools JSON (#1076) Aug 5, 2026
@agentHits
agentHits changed the base branch from main to dev August 5, 2026 20:44
@agentHits
agentHits marked this pull request as ready for review August 5, 2026 20:44
@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@github-actions
github-actions Bot marked this pull request as draft August 5, 2026 20:45

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@gui/src/i18n/en.ts`:
- Around line 1007-1010: Fix the malformed pws.addAccount and
pws.importResultSummary entries in gui/src/i18n/en.ts lines 1007-1010,
gui/src/i18n/ja.ts lines 955-958, gui/src/i18n/de.ts lines 1456-1459,
gui/src/i18n/ko.ts lines 1483-1486, gui/src/i18n/ru.ts lines 997-1000, and
gui/src/i18n/zh.ts lines 1476-1479: restore each locale’s specified
pws.addAccount value and remove the duplicated trailing literal from
pws.importResultSummary. Then run bun x tsc --noEmit to verify all catalogs
type-check.

In `@src/server/management/oauth-account-routes.ts`:
- Around line 379-459: Add a maximum batch-size validation for accountEntriesRaw
before the import loop, using a limit such as 100 entries. Return a 400 JSON
error when the limit is exceeded, while preserving the existing empty-array
validation and normal processing for batches within the limit.
- Around line 425-443: In the import flow around refreshAntigravityToken and
saveCredential, reject credentials unless both creds.accountId and creds.email
are present. For identity-less results, add a failed result with an appropriate
error, increment failedCount, and continue before saveCredential; only valid
credentials should be saved, reported as imported, and counted in importedCount.

In `@tests/cli-account.test.ts`:
- Around line 1358-1379: The cmdImport test still lacks coverage for file-based
JSON input and non-200 API failures. Extend the existing “ocx account import
validates inputs and outputs results” test by creating a temporary valid JSON
file and importing it via its path, then configure one mocked endpoint response
with a non-200 status and assert a non-zero exit code plus the expected “Error:
…” apiError output; use the existing temp-file and mock-server utilities.

In `@tests/oauth-accounts-api.test.ts`:
- Around line 179-229: Add a regression case to “POST import validates provider
and accounts payload” covering two imported accounts without email (or one
missing email alongside an existing active account), and assert the response
does not report success for the identity-less entry. Keep the test focused on
the collision fix in the import route and preserve the existing validation
assertions.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 9443c477-18ae-4ba2-818e-0c10829de902

📥 Commits

Reviewing files that changed from the base of the PR and between 80e4075 and 8f1a74e.

📒 Files selected for processing (12)
  • gui/src/components/provider-workspace/ProviderAuthPanel.tsx
  • gui/src/i18n/de.ts
  • gui/src/i18n/en.ts
  • gui/src/i18n/ja.ts
  • gui/src/i18n/ko.ts
  • gui/src/i18n/ru.ts
  • gui/src/i18n/zh.ts
  • src/cli/account-extended.ts
  • src/cli/account.ts
  • src/server/management/oauth-account-routes.ts
  • tests/cli-account.test.ts
  • tests/oauth-accounts-api.test.ts

Comment thread gui/src/i18n/en.ts Outdated
Comment thread src/server/management/oauth-account-routes.ts
Comment thread src/server/management/oauth-account-routes.ts
Comment thread tests/cli-account.test.ts
Comment thread tests/oauth-accounts-api.test.ts

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8f1a74e0b0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread gui/src/i18n/en.ts Outdated
Comment on lines +1007 to +1008
"pws.addAccount":
"pws.importJson": "Import JSON",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Restore valid i18n entries

The new locale keys were inserted between pws.addAccount's key and value, so the parser sees a property with no value followed by another key; the same malformed pattern is present in every locale file, which makes the GUI i18n modules fail to parse and blocks the dashboard build. Split pws.addAccount back into its own key/value and add the import keys as separate entries in each locale file.

AGENTS.md reference: gui/AGENTS.md:L15-L18

Useful? React with 👍 / 👎.

Comment on lines +319 to +325
const res = await fetch(`${apiBase}/api/oauth/accounts/import`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
provider: item.name,
accounts: Array.isArray(parsed) ? parsed : (parsed as { accounts?: unknown })?.accounts ?? parsed,
}),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Gate Cockpit imports with the OAuth risk warning

When a user imports Google Antigravity JSON from the Accounts tab, this direct POST stores a high-risk OAuth refresh token without going through the existing dashboard warning path (requestLoginOAuth checks oauthTosRisk(provider), and google-antigravity is classified as high risk). Route this import action through the same acknowledgement modal before sending/saving the tokens so users cannot bypass the warning just by using the new import path.

Useful? React with 👍 / 👎.

Comment on lines +287 to +295
<textarea
className="input"
rows={4}
value={importJsonText}
onChange={e => setImportJsonText(e.target.value)}
placeholder='[{"email":"user@gmail.com","refresh_token":"1//..."}]'
disabled={importBusy}
style={{ fontFamily: "var(--font-mono, monospace)", fontSize: 12 }}
/>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Add an accessible label to the import textarea

The new JSON paste textarea has no associated label or aria-label; the placeholder is only an example and is not a durable accessible name, so screen-reader users cannot tell what the field is for once focused or populated. Add a localized visible label or aria-label for the Cockpit JSON input.

AGENTS.md reference: gui/AGENTS.md:L31-L33

Useful? React with 👍 / 👎.

let importedCount = 0;
let failedCount = 0;

for (const rawItem of accountEntriesRaw) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Bound account import batches

For a large Cockpit export or accidental JSON array with many objects, this unbounded loop calls refreshAntigravityToken once per entry, and each call can spend up to the OAuth request timeout before moving to the next. A single management request can therefore consume proxy resources and issue hundreds or thousands of Google token requests; reject or cap batches before entering the refresh loop.

Useful? React with 👍 / 👎.

…e account identity (lidge-jun#1076)

- Restore pws.addAccount string across all 6 locale files (en, ru, zh, de, ja, ko)
- Enforce maximum 100-account limit for batch imports on POST /api/oauth/accounts/import
- Require account identity (email or accountId) before persisting imported credentials
- Add CLI file-based JSON import test coverage
- Add non-empty statements to catch blocks in ProviderAuthPanel.tsx and cli-account.test.ts
- Ensure deterministic PR hygiene checks pass cleanly
@agentHits
agentHits marked this pull request as ready for review August 5, 2026 22:00

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (2)
tests/cli-account.test.ts (1)

1379-1397: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add the still-missing non-200 API error-handling case for cmdImport.

This test now covers file-based JSON import (Lines 1379-1397), closing half of a prior review request. The other half — a mocked endpoint returning a non-200 status, asserting a non-zero exit code and Error: ... output — is still not present in this file.

Extend the existing "ocx account import validates inputs and outputs results" test group with a case that configures the mock /api/oauth/accounts/import endpoint to return a non-200 status for one request, then asserts run(...) returns a non-zero code and stdout/stderr contains the expected apiError text.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/cli-account.test.ts` around lines 1379 - 1397, Extend the existing “ocx
account import validates inputs and outputs results” test group with a cmdImport
case that configures the mocked /api/oauth/accounts/import endpoint to return a
non-200 response, then invokes run with import arguments and asserts a non-zero
exit code plus output containing the expected apiError text prefixed by Error:.
Keep the existing successful file-import test unchanged.
gui/src/components/provider-workspace/ProviderAuthPanel.tsx (1)

287-295: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Move the remaining hardcoded JSON placeholder into the locale files.

Line 292 still hardcodes the example JSON string shown in the textarea placeholder. A prior review comment asked for this text, along with the button and result text, to move into locale files. The button and result text (pws.importJson, pws.importJsonCockpit, pws.importResultSummary) now use t(...), but the placeholder does not.

Add a pws.importJsonPlaceholder key to all six locale catalogs and use it here instead of the literal string.

As per path instructions, "GUI dashboard... Check that GUI state changes stay consistent with the management API responses and that user-visible strings go through the i18n locale files rather than hardcoded text."

♻️ Proposed fix
-                  placeholder='[{"email":"user@gmail.com","refresh_token":"1//..."}]'
+                  placeholder={t("pws.importJsonPlaceholder")}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@gui/src/components/provider-workspace/ProviderAuthPanel.tsx` around lines 287
- 295, Move the textarea placeholder in ProviderAuthPanel from the hardcoded
JSON string to the i18n system by adding the pws.importJsonPlaceholder key with
the same value to all six locale catalogs, then replace the literal placeholder
in the import JSON textarea with t("pws.importJsonPlaceholder").

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Duplicate comments:
In `@gui/src/components/provider-workspace/ProviderAuthPanel.tsx`:
- Around line 287-295: Move the textarea placeholder in ProviderAuthPanel from
the hardcoded JSON string to the i18n system by adding the
pws.importJsonPlaceholder key with the same value to all six locale catalogs,
then replace the literal placeholder in the import JSON textarea with
t("pws.importJsonPlaceholder").

In `@tests/cli-account.test.ts`:
- Around line 1379-1397: Extend the existing “ocx account import validates
inputs and outputs results” test group with a cmdImport case that configures the
mocked /api/oauth/accounts/import endpoint to return a non-200 response, then
invokes run with import arguments and asserts a non-zero exit code plus output
containing the expected apiError text prefixed by Error:. Keep the existing
successful file-import test unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: ff500fda-ee23-42be-a3d1-ce8daf8cb4ec

📥 Commits

Reviewing files that changed from the base of the PR and between 8f1a74e and 29463aa.

📒 Files selected for processing (10)
  • gui/src/components/provider-workspace/ProviderAuthPanel.tsx
  • gui/src/i18n/de.ts
  • gui/src/i18n/en.ts
  • gui/src/i18n/ja.ts
  • gui/src/i18n/ko.ts
  • gui/src/i18n/ru.ts
  • gui/src/i18n/zh.ts
  • src/server/management/oauth-account-routes.ts
  • tests/cli-account.test.ts
  • tests/oauth-accounts-api.test.ts

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 29463aa149

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +315 to +318
} catch (err) {
void err;
setImportResult({ imported: 0, failed: 1 });
return;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Show why JSON imports fail

When the pasted JSON is malformed, this catch reduces the error to the same count-only summary used for any other failure, and the later non-OK response path does the same. In those cases users only see “0 imported, 1 failed” and cannot tell whether to fix JSON syntax, choose a different provider, or retry a server-side failure; store and render a localized validation/error message from JSON.parse or the response body instead.

AGENTS.md reference: gui/AGENTS.md:L31-L33

Useful? React with 👍 / 👎.

failedCount++;
continue;
}
await saveCredential(provider, creds, { preserveIdentityless: true });

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Upgrade legacy identityless accounts on import

Because imports now require an account identity, passing preserveIdentityless: true disables the store’s legacy-upgrade path for an existing active Google Antigravity credential that was saved without email/accountId. In that case importing the same Cockpit account appends a second active row instead of upgrading the stale identityless slot, leaving a duplicate, selectable credential behind; omit this option for identity-bearing imports.

Useful? React with 👍 / 👎.

@github-actions
github-actions Bot marked this pull request as draft August 5, 2026 22:41
@lidge-jun

Copy link
Copy Markdown
Owner

Maintainer triage (code-level, against dev @ 6e1a4e429). Closest of the batch to reviewable — the token handling itself is done carefully (live refreshAntigravityToken validation before persisting, no token echo in responses or logs, quota caches cleared). Remaining blockers:

  1. CLI accepts refresh tokens as an argv string (ocx account import <provider> <file-or-json>): argv is visible in process lists. The repo deliberately pipes secrets via stdin for add-key (PIPE_GUIDANCE in src/cli/account-extended.ts) — import should support the same stdin path or restrict inline JSON to non-secret use and document file-path-only for tokens.
  2. GUI change without a screenshot. ProviderAuthPanel.tsx gains an import panel; please add a screenshot to the description per repo policy.
  3. hygiene fails with unsponsored_surfacesrc/server/management/oauth-account-routes.ts is a restricted credential surface; maintainer sponsorship plus security review is required per MAINTAINERS.md. That review should also confirm nothing token-shaped can flow through the verbatim Google results[].error strings.

Minor: misindented if in the PUT /alias mock hunk of tests/cli-account.test.ts. Branch is 17 commits behind dev; please rebase.

@lidge-jun

Copy link
Copy Markdown
Owner

Closing this draft — closest of the batch to landing, and the token refresh validation is done right. Blockers: (1) refresh tokens are accepted via argv, which leaks into shell history and process listings — take them via file path or stdin only; (2) the GUI change ships without the required screenshot evidence; (3) credential import is a security-sensitive surface and needs a maintainer-sponsored review pass. Please reopen with file/stdin-only input and the GUI evidence; this one we would like to take.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request intake: hygiene-blocked Deterministic PR hygiene checks failed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature Request] Support Importing Google Antigravity Accounts Exported from Cockpit Tools

2 participants