feat(grok): 对齐 free-usage/empty 失败分类与 sticky/ReAuth 体验

从 grokcli 吸收 body-first 失败分类(free-usage/empty/billing),
接到现有 temp_unsched 与 failover,保留 content-policy 与 pool_mode。
Grok 粘连/缓存在无显式 session 时可用 previous_response_id;
ReAuth 预填 email---- 并默认密码 tab(密码不落库)。
This commit is contained in:
IanShaw027
2026-08-07 13:47:09 +08:00
parent 9bc99e8b6e
commit e12e0dc1a6
10 changed files with 806 additions and 4 deletions
@@ -137,6 +137,8 @@
:method-label="t('admin.accounts.inputMethod')"
:platform="isOpenAI ? 'openai' : isGemini ? 'gemini' : isAntigravity ? 'antigravity' : isGrok ? 'grok' : 'anthropic'"
:show-project-id="isGemini && geminiOAuthType === 'code_assist'"
:initial-input-method="grokInitialInputMethod"
:initial-email-password="grokPrefillEmailPassword"
@generate-url="handleGenerateUrl"
@cookie-auth="handleCookieAuth"
@validate-refresh-token="handleGrokValidateRefreshToken"
@@ -255,6 +257,40 @@ const isAnthropic = computed(() => props.account?.platform === 'anthropic')
const isAntigravity = computed(() => props.account?.platform === 'antigravity')
const isGrok = computed(() => props.account?.platform === 'grok')
/** Stored Grok email for reauth prefill (password is never stored). */
const grokAccountEmail = computed(() => {
if (!isGrok.value || !props.account) return ''
const creds = (props.account.credentials || {}) as Record<string, unknown>
const email = typeof creds.email === 'string' ? creds.email.trim() : ''
return email
})
/**
* Prefill "email----" so the operator only types the password.
* Empty when no email is known (full email----password required).
*/
const grokPrefillEmailPassword = computed(() => {
const email = grokAccountEmail.value
return email ? `${email}----` : ''
})
/**
* Grok reauth default tab:
* - password first when we know the email (common reauth path)
* - refresh_token when email unknown but RT may still work
* - email_password otherwise
*/
const grokInitialInputMethod = computed<AuthInputMethod>(() => {
if (!isGrok.value) return 'manual'
if (grokAccountEmail.value) return 'email_password'
const creds = (props.account?.credentials || {}) as Record<string, unknown>
const hasRT =
(typeof creds.refresh_token === 'string' && creds.refresh_token.trim() !== '') ||
(typeof creds.has_refresh_token === 'boolean' && creds.has_refresh_token)
if (hasRT) return 'refresh_token'
return 'email_password'
})
// Computed - current OAuth state based on platform
const currentAuthUrl = computed(() => {
if (isOpenAILike.value) return openaiOAuth.authUrl.value
@@ -32,4 +32,13 @@ describe('ReAuthAccountModal Grok re-auth paths', () => {
expect(source).toContain("method === 'email_password'")
expect(source).toContain("method === 'refresh_token'")
})
it('prefills email---- and defaults to password method when email is known', () => {
expect(source).toContain('grokPrefillEmailPassword')
expect(source).toContain('grokInitialInputMethod')
expect(source).toContain(':initial-email-password="grokPrefillEmailPassword"')
expect(source).toContain(':initial-input-method="grokInitialInputMethod"')
expect(source).toContain('email----')
expect(source).toContain("return 'email_password'")
})
})