From 00495b667533a4a1fbb3560a3391c2547bc228f9 Mon Sep 17 00:00:00 2001 From: Tam Nhu Tran Date: Tue, 24 Feb 2026 12:01:07 +0700 Subject: [PATCH] fix(cliproxy): strengthen account safety warning and redesign alert UI --- src/cliproxy/account-safety.ts | 52 ++++++--- src/commands/help-command.ts | 3 + .../account/account-safety-warning-card.tsx | 101 ++++++++++++++++++ .../components/account/add-account-dialog.tsx | 35 +++++- ui/src/pages/cliproxy.tsx | 28 +---- 5 files changed, 181 insertions(+), 38 deletions(-) create mode 100644 ui/src/components/account/account-safety-warning-card.tsx diff --git a/src/cliproxy/account-safety.ts b/src/cliproxy/account-safety.ts index f7a89cde..6c7e049d 100644 --- a/src/cliproxy/account-safety.ts +++ b/src/cliproxy/account-safety.ts @@ -165,9 +165,18 @@ export function warnCrossProviderDuplicates(provider: CLIProxyProvider): boolean console.error(''); console.error(warn('Account safety: cross-provider duplicate detected')); - console.error(' Same Google account across providers risks account bans (ref: #509).'); console.error( - ' If provider requests start returning 403/Forbidden, treat it as a possible ban.' + ' Same Google account across "ccs gemini" + "ccs agy" is a known suspension/ban risk (ref: #509).' + ); + console.error(' This risk applies to both CLI sessions and accounts added from "ccs config".'); + console.error( + ' If provider requests start returning 403/Forbidden, treat it as a possible account disable/ban.' + ); + console.error( + ' If you want to keep Google AI access on this account, do not continue this shared-account setup.' + ); + console.error( + ' CCS is provided as-is and cannot take responsibility for suspension/ban/access-loss decisions.' ); console.error(` Details: ${ISSUE_509_URL}`); console.error(''); @@ -177,8 +186,8 @@ export function warnCrossProviderDuplicates(provider: CLIProxyProvider): boolean } console.error(''); - console.error(' Fix: pause duplicate with "ccs --pause "'); - console.error(' or use separate Google accounts per provider.'); + console.error(' Immediate action: pause duplicate account and use separate Google accounts.'); + console.error(' Fix command: "ccs cliproxy pause --provider "'); console.error(''); return true; @@ -196,8 +205,19 @@ export function warnNewAccountConflict( console.error( ` ${maskEmail(email)} is also registered under: ${conflictingProviders.join(', ')}` ); - console.error(' Concurrent usage may cause Google to ban your account.'); + console.error( + ' Reusing one Google account between "ccs gemini" and "ccs agy" can trigger bans.' + ); + console.error( + ' This applies to both CLI auth and "ccs config" dashboard auth for these providers.' + ); console.error(' 403/Forbidden responses can be an early sign of account disablement.'); + console.error( + ' If you want to keep Google AI access, do not continue with this shared-account setup.' + ); + console.error( + ' CCS is provided as-is and cannot take responsibility for suspension/ban/access-loss decisions.' + ); console.error(' Consider pausing the duplicate or using a different account.'); console.error(` Details: ${ISSUE_509_URL}`); console.error(''); @@ -215,15 +235,18 @@ export function warnOAuthBanRisk(provider: CLIProxyProvider): void { shownBanWarnings.add(provider); console.error(''); - console.error(warn('Account safety warning (#509)')); + console.error(warn('Account safety warning (#509 - read before continuing)')); console.error( - ' Using the same Google account in both "ccs gemini" and "ccs agy" can trigger suspension.' + ' Known risk: one Google account shared by "ccs gemini" + "ccs agy" can be disabled/banned.' ); console.error( - ' If you see 403/Forbidden during provider calls, treat it as likely account disable/ban.' + ' This risk applies whether auth was done from CLI or from "ccs config" dashboard.' ); console.error( - ' Use separate Google accounts per provider and stop retrying blocked accounts.' + ' If you want to keep Google AI access, do not continue with this shared-account setup.' + ); + console.error( + ' CCS is provided as-is and cannot take responsibility for suspension/ban/access-loss decisions.' ); console.error(` Details: ${ISSUE_509_URL}`); console.error(''); @@ -247,11 +270,16 @@ export function warnPossible403Ban(provider: CLIProxyProvider, errorMessage: str } console.error(''); - console.error(warn(`Account safety: ${provider} returned 403/Forbidden`)); + console.error(warn(`Account safety: ${provider} returned 403/Forbidden (possible disable/ban)`)); console.error( - ' For gemini/agy flows this often means the Google account was blocked/disabled.' + ' For gemini/agy flows this often means Google blocked or disabled the account.' + ); + console.error( + ' If you want to keep Google AI access, stop using this account/provider pairing immediately.' + ); + console.error( + ' CCS is provided as-is and cannot take responsibility for suspension/ban/access-loss decisions.' ); - console.error(' Stop retries for this account and switch to a different account/provider.'); console.error(` Details: ${ISSUE_509_URL}`); console.error(` Error: "${truncate(errorMessage, 160)}"`); console.error(''); diff --git a/src/commands/help-command.ts b/src/commands/help-command.ts index e2568651..c7b52ae7 100644 --- a/src/commands/help-command.ts +++ b/src/commands/help-command.ts @@ -166,6 +166,9 @@ Run ${color('ccs config', 'command')} for web dashboard`.trim(); 'Zero-config OAuth authentication via CLIProxy Plus', 'First run: Browser opens for authentication, then model selection', 'Settings: ~/.ccs/{provider}.settings.json (created after auth)', + 'Safety: do not reuse one Google account across "ccs gemini" and "ccs agy" (issue #509)', + 'If you want to keep Google AI access, do not continue this shared-account setup', + 'CCS is as-is and does not take responsibility for account bans/access loss', ], [ ['ccs gemini', 'Google Gemini (gemini-2.5-pro or 3-pro)'], diff --git a/ui/src/components/account/account-safety-warning-card.tsx b/ui/src/components/account/account-safety-warning-card.tsx new file mode 100644 index 00000000..a2e00ae0 --- /dev/null +++ b/ui/src/components/account/account-safety-warning-card.tsx @@ -0,0 +1,101 @@ +import { AlertTriangle, ExternalLink } from 'lucide-react'; +import { Badge } from '@/components/ui/badge'; +import { Checkbox } from '@/components/ui/checkbox'; +import { Label } from '@/components/ui/label'; +import { cn } from '@/lib/utils'; + +interface AccountSafetyWarningCardProps { + className?: string; + showAcknowledgement?: boolean; + acknowledged?: boolean; + onAcknowledgedChange?: (value: boolean) => void; + disabled?: boolean; +} + +export function AccountSafetyWarningCard({ + className, + showAcknowledgement = false, + acknowledged = false, + onAcknowledgedChange, + disabled = false, +}: AccountSafetyWarningCardProps) { + return ( +
+
+ +
+
+
+
+ +
+
+

Account Safety Warning

+

+ Issue #509 ยท Shared Gemini + AGY account risk +

+
+
+ + High Risk + +
+ +
+

+ Using one Google account for both ccs gemini and{' '} + ccs agy can trigger account disable/ban. +

+

+ If you want to keep Google AI access, do not continue this shared-account setup. +

+

+ CCS is provided as-is and does not take responsibility for suspension, bans, or access + loss from upstream providers. +

+
+ +
+ + Read issue #509 + + + + Applies to CLI and dashboard auth + +
+ + {showAcknowledgement && onAcknowledgedChange && ( +
+
+ onAcknowledgedChange(Boolean(checked))} + disabled={disabled} + /> + +
+
+ )} +
+
+ ); +} diff --git a/ui/src/components/account/add-account-dialog.tsx b/ui/src/components/account/add-account-dialog.tsx index 8a250680..8a203a9b 100644 --- a/ui/src/components/account/add-account-dialog.tsx +++ b/ui/src/components/account/add-account-dialog.tsx @@ -29,6 +29,7 @@ import { Loader2, ExternalLink, User, Download, Copy, Check } from 'lucide-react import { useKiroImport } from '@/hooks/use-cliproxy'; import { useCliproxyAuthFlow } from '@/hooks/use-cliproxy-auth-flow'; import { applyDefaultPreset } from '@/lib/preset-utils'; +import { AccountSafetyWarningCard } from '@/components/account/account-safety-warning-card'; import { DEFAULT_KIRO_AUTH_METHOD, getKiroAuthMethodOption, @@ -59,12 +60,14 @@ export function AddAccountDialog({ const [callbackUrl, setCallbackUrl] = useState(''); const [copied, setCopied] = useState(false); const [localError, setLocalError] = useState(null); + const [acknowledgedRisk, setAcknowledgedRisk] = useState(false); const [kiroAuthMethod, setKiroAuthMethod] = useState(DEFAULT_KIRO_AUTH_METHOD); const wasAuthenticatingRef = useRef(false); const authFlow = useCliproxyAuthFlow(); const kiroImportMutation = useKiroImport(); const isKiro = provider === 'kiro'; + const requiresSafetyAcknowledgement = provider === 'gemini' || provider === 'agy'; const defaultDeviceCode = isDeviceCodeProvider(provider); const requiresNickname = isNicknameRequiredProvider(provider); const kiroMethodOption = getKiroAuthMethodOption(kiroAuthMethod); @@ -78,11 +81,19 @@ export function AddAccountDialog({ setCallbackUrl(''); setCopied(false); setLocalError(null); + setAcknowledgedRisk(false); setKiroAuthMethod(DEFAULT_KIRO_AUTH_METHOD); wasAuthenticatingRef.current = false; onClose(); }; + useEffect(() => { + if (open) { + setAcknowledgedRisk(false); + setLocalError(null); + } + }, [provider, open]); + // When authFlow completes successfully (polling detected success), apply preset and close useEffect(() => { if (!authFlow.isAuthenticating && !authFlow.error && authFlow.provider === null && open) { @@ -131,6 +142,12 @@ export function AddAccountDialog({ * - Authorization code providers use /start-url and polling. */ const handleAuthenticate = () => { + if (requiresSafetyAcknowledgement && !acknowledgedRisk) { + setLocalError( + 'Please acknowledge the account safety warning before authenticating this provider.' + ); + return; + } if (requiresNickname && !nicknameTrimmed) { setLocalError(`Nickname is required for ${displayName} accounts.`); return; @@ -187,6 +204,18 @@ export function AddAccountDialog({
+ {requiresSafetyAcknowledgement && !showAuthUI && ( + { + setAcknowledgedRisk(value); + setLocalError(null); + }} + disabled={isPending} + /> + )} + {/* Kiro auth method */} {isKiro && !showAuthUI && (
@@ -374,7 +403,11 @@ export function AddAccountDialog({ {!showAuthUI && (