feat(auth): let power user mode skip Gemini auth gate

This commit is contained in:
Tam Nhu Tran
2026-03-26 15:03:24 -04:00
parent a94a529201
commit ce4401e84e
4 changed files with 85 additions and 83 deletions
+1 -1
View File
@@ -173,7 +173,7 @@ export interface CLIProxyLoggingConfig {
* Controls high-risk flow safeguards for supported providers.
*/
export interface CLIProxySafetyConfig {
/** Allow skipping AGY responsibility acknowledgement flow (default: false) */
/** Allow skipping AGY responsibility checks and Gemini dashboard typed acknowledgement */
antigravity_ack_bypass?: boolean;
}
+3 -3
View File
@@ -625,7 +625,7 @@ router.delete('/:profile/presets/:name', (req: Request, res: Response): void =>
// ==================== Auth Tokens ====================
/**
* GET /api/settings/auth/antigravity-risk - Get AGY responsibility bypass setting
* GET /api/settings/auth/antigravity-risk - Get shared power user bypass setting
*/
router.get('/auth/antigravity-risk', (req: Request, res: Response): void => {
if (!requireSensitiveLocalAccess(req, res)) return;
@@ -636,12 +636,12 @@ router.get('/auth/antigravity-risk', (req: Request, res: Response): void => {
antigravityAckBypass: config.cliproxy?.safety?.antigravity_ack_bypass === true,
});
} catch (error) {
respondInternalError(res, error, 'Failed to load Antigravity power user mode.');
respondInternalError(res, error, 'Failed to load power user mode.');
}
});
/**
* PUT /api/settings/auth/antigravity-risk - Update AGY responsibility bypass setting
* PUT /api/settings/auth/antigravity-risk - Update shared power user bypass setting
*/
router.put('/auth/antigravity-risk', (req: Request, res: Response): void => {
if (!requireSensitiveLocalAccess(req, res)) return;
@@ -73,8 +73,8 @@ export function AddAccountDialog({
const [localError, setLocalError] = useState<string | null>(null);
const [riskAcknowledgementText, setRiskAcknowledgementText] = useState('');
const [agyRiskChecklist, setAgyRiskChecklist] = useState(DEFAULT_ANTIGRAVITY_RISK_CHECKLIST);
const [agyAckBypassEnabled, setAgyAckBypassEnabled] = useState(false);
const [agyAckBypassLoading, setAgyAckBypassLoading] = useState(false);
const [powerUserModeEnabled, setPowerUserModeEnabled] = useState(false);
const [powerUserModeLoading, setPowerUserModeLoading] = useState(false);
const [kiroAuthMethod, setKiroAuthMethod] = useState<KiroAuthMethod>(DEFAULT_KIRO_AUTH_METHOD);
const { t } = useTranslation();
const wasAuthenticatingRef = useRef(false);
@@ -82,9 +82,10 @@ export function AddAccountDialog({
const kiroImportMutation = useKiroImport();
const isKiro = provider === 'kiro';
const requiresSafetyAcknowledgement = provider === 'gemini';
const requiresAgyResponsibilityFlow = provider === 'agy' && !agyAckBypassEnabled;
const isAgyBypassStatePending = provider === 'agy' && agyAckBypassLoading;
const supportsPowerUserMode = provider === 'agy' || provider === 'gemini';
const requiresGeminiSafetyAcknowledgement = provider === 'gemini' && !powerUserModeEnabled;
const requiresAgyResponsibilityFlow = provider === 'agy' && !powerUserModeEnabled;
const isPowerUserModePending = supportsPowerUserMode && powerUserModeLoading;
const isAgyRiskChecklistComplete = isAntigravityRiskChecklistComplete(agyRiskChecklist);
const isGeminiRiskAcknowledged = normalizeRiskPhrase(riskAcknowledgementText) === RISK_ACK_PHRASE;
const defaultDeviceCode = isDeviceCodeProvider(provider);
@@ -94,10 +95,10 @@ export function AddAccountDialog({
const nicknameTrimmed = nickname.trim();
const errorMessage = localError || authFlow.error;
const fetchAgyBypassState = useCallback(async (): Promise<boolean> => {
const fetchPowerUserModeState = useCallback(async (): Promise<boolean> => {
const response = await fetch('/api/settings/auth/antigravity-risk');
if (!response.ok) {
throw new Error('Failed to load Antigravity power user setting');
throw new Error('Failed to load power user mode setting');
}
const data = (await response.json()) as { antigravityAckBypass?: boolean };
return data.antigravityAckBypass === true;
@@ -110,8 +111,8 @@ export function AddAccountDialog({
setLocalError(null);
setRiskAcknowledgementText('');
setAgyRiskChecklist(DEFAULT_ANTIGRAVITY_RISK_CHECKLIST);
setAgyAckBypassEnabled(false);
setAgyAckBypassLoading(false);
setPowerUserModeEnabled(false);
setPowerUserModeLoading(false);
setKiroAuthMethod(DEFAULT_KIRO_AUTH_METHOD);
wasAuthenticatingRef.current = false;
onClose();
@@ -128,39 +129,39 @@ export function AddAccountDialog({
useEffect(() => {
let cancelled = false;
if (!open || provider !== 'agy') {
setAgyAckBypassEnabled(false);
setAgyAckBypassLoading(false);
if (!open || !supportsPowerUserMode) {
setPowerUserModeEnabled(false);
setPowerUserModeLoading(false);
return;
}
const loadAgyBypassState = async () => {
const loadPowerUserModeState = async () => {
try {
setAgyAckBypassLoading(true);
const enabled = await fetchAgyBypassState();
setPowerUserModeLoading(true);
const enabled = await fetchPowerUserModeState();
if (!cancelled) {
setAgyAckBypassEnabled(enabled);
setPowerUserModeEnabled(enabled);
}
} catch {
if (!cancelled) {
setAgyAckBypassEnabled(false);
setPowerUserModeEnabled(false);
}
} finally {
if (!cancelled) {
setAgyAckBypassLoading(false);
setPowerUserModeLoading(false);
}
}
};
loadAgyBypassState();
loadPowerUserModeState();
return () => {
cancelled = true;
};
}, [fetchAgyBypassState, open, provider]);
}, [fetchPowerUserModeState, open, supportsPowerUserMode]);
useEffect(() => {
if (!open || provider !== 'agy' || !authFlow.error || !agyAckBypassEnabled) {
if (!open || provider !== 'agy' || !authFlow.error || !powerUserModeEnabled) {
return;
}
@@ -175,20 +176,20 @@ export function AddAccountDialog({
const syncBypassState = async () => {
try {
setAgyAckBypassLoading(true);
const enabled = await fetchAgyBypassState();
setPowerUserModeLoading(true);
const enabled = await fetchPowerUserModeState();
if (cancelled) return;
setAgyAckBypassEnabled(enabled);
setPowerUserModeEnabled(enabled);
if (!enabled) {
setLocalError('Power user mode is off. Complete the AGY checklist and retry.');
}
} catch {
if (cancelled) return;
setAgyAckBypassEnabled(false);
setPowerUserModeEnabled(false);
setLocalError('Power user mode is off. Complete the AGY checklist and retry.');
} finally {
if (!cancelled) {
setAgyAckBypassLoading(false);
setPowerUserModeLoading(false);
}
}
};
@@ -198,7 +199,7 @@ export function AddAccountDialog({
return () => {
cancelled = true;
};
}, [agyAckBypassEnabled, authFlow.error, fetchAgyBypassState, open, provider]);
}, [authFlow.error, fetchPowerUserModeState, open, powerUserModeEnabled, provider]);
// When authFlow completes successfully (polling detected success), apply preset and close
useEffect(() => {
@@ -248,8 +249,8 @@ export function AddAccountDialog({
* - Authorization code providers use /start-url and polling.
*/
const handleAuthenticate = () => {
if (isAgyBypassStatePending) {
setLocalError('Loading Antigravity safety settings. Please wait a moment and retry.');
if (isPowerUserModePending) {
setLocalError('Loading power user safety settings. Please wait a moment and retry.');
return;
}
if (requiresAgyResponsibilityFlow && !isAgyRiskChecklistComplete) {
@@ -258,7 +259,7 @@ export function AddAccountDialog({
);
return;
}
if (requiresSafetyAcknowledgement && !isGeminiRiskAcknowledged) {
if (requiresGeminiSafetyAcknowledgement && !isGeminiRiskAcknowledged) {
setLocalError(
`Type "${RISK_ACK_PHRASE}" to acknowledge the account safety warning before authenticating this provider.`
);
@@ -336,7 +337,7 @@ export function AddAccountDialog({
/>
)}
{provider === 'agy' && agyAckBypassEnabled && !showAuthUI && (
{supportsPowerUserMode && powerUserModeEnabled && !showAuthUI && (
<div className="rounded-lg border border-amber-400/35 bg-amber-50/70 p-3 text-xs text-amber-900 dark:border-amber-800/60 dark:bg-amber-950/25 dark:text-amber-100">
<div className="mb-1.5 flex items-center gap-1.5 font-semibold">
<ShieldAlert className="h-3.5 w-3.5" />
@@ -346,7 +347,7 @@ export function AddAccountDialog({
</div>
)}
{requiresSafetyAcknowledgement && !showAuthUI && (
{requiresGeminiSafetyAcknowledgement && !showAuthUI && (
<AccountSafetyWarningCard
showAcknowledgement
acknowledgementPhrase={RISK_ACK_PHRASE}
@@ -541,9 +542,9 @@ export function AddAccountDialog({
onClick={handleAuthenticate}
disabled={
isPending ||
isAgyBypassStatePending ||
isPowerUserModePending ||
(requiresAgyResponsibilityFlow && !isAgyRiskChecklistComplete) ||
(requiresSafetyAcknowledgement && !isGeminiRiskAcknowledged)
(requiresGeminiSafetyAcknowledgement && !isGeminiRiskAcknowledged)
}
>
<ExternalLink className="w-4 h-4 mr-2" />
+46 -45
View File
@@ -483,7 +483,7 @@ const resources = {
descOauth: 'Click Authenticate to get an OAuth URL. Open it in any browser to sign in.',
powerUserEnabled: 'Power user mode enabled',
powerUserSkipped:
'AGY responsibility checklist is skipped from Settings > Proxy. You accept full responsibility for OAuth/account risk.',
'Settings > Proxy power user mode is skipping the AGY responsibility checklist and Gemini dashboard risk phrase. You accept full responsibility for OAuth/account risk.',
authMethod: 'Auth Method',
selectKiroAuthMethod: 'Select Kiro auth method',
nicknameRequired: 'Nickname (required)',
@@ -732,13 +732,13 @@ const resources = {
viewDocs: 'View documentation',
},
settingsProxy: {
failedLoadAgyMode: 'Failed to load AGY power user mode',
failedUpdateAgyMode: 'Failed to update AGY power user mode',
failedVerifyAgyMode: 'Failed to verify AGY power user mode persistence',
failedLoadAgyMode: 'Failed to load power user mode',
failedUpdateAgyMode: 'Failed to update power user mode',
failedVerifyAgyMode: 'Failed to verify power user mode persistence',
notPersistedAgyMode:
'AGY power user mode was not persisted. Config may have been modified by another process.',
agyModeEnabled: 'AGY power user mode enabled.',
agyModeDisabled: 'AGY power user mode disabled.',
'Power user mode was not persisted. Config may have been modified by another process.',
agyModeEnabled: 'Power user mode enabled.',
agyModeDisabled: 'Power user mode disabled.',
typePhraseToContinue: 'Type "{{value}}" to continue.',
description: 'Configure local or remote {{backend}} connection for proxy-based profiles',
backendPlus: 'CLIProxy Plus',
@@ -758,21 +758,22 @@ const resources = {
'Existing Kiro/Copilot variants will not work with CLIProxyAPI. Switch to CLIProxyAPIPlus or remove those variants.',
safety: 'Safety',
agyModeTitle: 'Antigravity Power User Mode',
agyModeDesc: 'Skip AGY responsibility checklist in Add Account and `ccs agy` flows.',
agyModeDesc:
'Skip the AGY responsibility checklist and the Gemini dashboard typed acknowledgement.',
agyWarning:
'Use only if you fully understand the OAuth suspension/ban risk pattern (#509). CCS cannot assume responsibility for account loss.',
finalConfirm: 'Final confirmation required',
finalConfirmDesc:
'Enabling this will skip AGY safety checkpoints in both dashboard and CLI. Review issue #509 and type the exact phrase to proceed.',
'Enabling this will skip AGY safety checkpoints and the Gemini dashboard risk phrase gate. Review issue #509 and type the exact phrase to proceed.',
step1: 'Step 1',
readIssue: 'Read issue #509',
step2: 'Step 2',
typePrefix: 'Type',
typeSuffix: 'to enable.',
typePhraseAria: 'Type I ACCEPT RISK to enable Antigravity power user mode',
typePhraseAria: 'Type I ACCEPT RISK to enable power user mode',
exactPhrase: 'Exact phrase required.',
enableAgyMode: 'Enable Power User Mode',
toggleAgyMode: 'Toggle AGY power user mode',
toggleAgyMode: 'Toggle power user mode',
fallbackSettings: 'Fallback Settings',
enableFallback: 'Enable fallback to local',
enableFallbackDesc: 'Use local proxy if remote is unreachable',
@@ -1665,7 +1666,8 @@ const resources = {
descDeviceCode: '点击认证后,验证码将显示,请在提供商网站输入。',
descOauth: '点击认证获取 OAuth URL,在任意浏览器中打开即可登录。',
powerUserEnabled: '已启用高级用户模式',
powerUserSkipped: '已在设置 > 代理 中跳过 AGY 责任清单,您需自行承担 OAuth/账号风险。',
powerUserSkipped:
'设置 > 代理 中的高级用户模式会跳过 AGY 责任确认清单和 Gemini Dashboard 的风险短语。OAuth / 账号风险需自行承担。',
authMethod: '认证方式',
selectKiroAuthMethod: '选择 Kiro 认证方式',
nicknameRequired: '昵称(必填)',
@@ -1904,12 +1906,12 @@ const resources = {
viewDocs: '查看文档',
},
settingsProxy: {
failedLoadAgyMode: '加载 AGY 高级模式失败',
failedUpdateAgyMode: '更新 AGY 高级模式失败',
failedVerifyAgyMode: '校验 AGY 高级模式持久化失败',
notPersistedAgyMode: 'AGY 高级模式未成功持久化,配置可能被其他进程修改。',
agyModeEnabled: 'AGY 高级模式已启用。',
agyModeDisabled: 'AGY 高级模式已关闭。',
failedLoadAgyMode: '加载高级模式失败',
failedUpdateAgyMode: '更新高级模式失败',
failedVerifyAgyMode: '校验高级模式持久化失败',
notPersistedAgyMode: '高级模式未成功持久化,配置可能被其他进程修改。',
agyModeEnabled: '高级模式已启用。',
agyModeDisabled: '高级模式已关闭。',
typePhraseToContinue: '请输入“{{value}}”后继续。',
description: '为基于代理的配置设置本地或远程 {{backend}} 连接',
backendPlus: 'CLIProxy Plus',
@@ -1929,20 +1931,20 @@ const resources = {
'现有 Kiro/Copilot 变体与 CLIProxyAPI 不兼容。请切换到 CLIProxyAPIPlus 或移除这些变体。',
safety: '安全',
agyModeTitle: 'Antigravity 高级模式',
agyModeDesc: '在 Add Account 与 `ccs agy` 流程中跳过 AGY 责任确认清单。',
agyModeDesc: '跳过 AGY 责任确认清单,以及 Gemini Dashboard 中输入风险短语的步骤。',
agyWarning: '仅在你充分理解 OAuth 封禁风险模式(#509)后使用。CCS 不对账号损失承担责任。',
finalConfirm: '需要最终确认',
finalConfirmDesc:
'启用后会跳过 Dashboard CLI 的 AGY 安全检查。请先阅读 issue #509,并输入精确短语后继续。',
'启用后会跳过 Dashboard / CLI 的 AGY 安全检查,以及 Gemini Dashboard 的风险短语确认。请先阅读 issue #509,并输入精确短语后继续。',
step1: '步骤 1',
readIssue: '阅读 issue #509',
step2: '步骤 2',
typePrefix: '输入',
typeSuffix: '以启用。',
typePhraseAria: '输入 I ACCEPT RISK 以启用 Antigravity 高级模式',
typePhraseAria: '输入 I ACCEPT RISK 以启用高级模式',
exactPhrase: '必须输入完全一致的短语。',
enableAgyMode: '启用高级模式',
toggleAgyMode: '切换 AGY 高级模式',
toggleAgyMode: '切换高级模式',
fallbackSettings: '回退设置',
enableFallback: '启用本地回退',
enableFallbackDesc: '远程不可达时使用本地代理',
@@ -2859,7 +2861,7 @@ const resources = {
'Nhấp vào Xác thực để nhận URL OAuth. Mở nó trong bất kỳ trình duyệt nào để đăng nhập.',
powerUserEnabled: 'Đã bật chế độ power user',
powerUserSkipped:
'Danh sách kiểm tra trách nhiệm của AGY bị bỏ qua khỏi Cài đặt > Proxy. Bạn chấp nhận hoàn toàn trách nhiệm về rủi ro tài khoản/OAuth.',
'Chế độ power user trong Cài đặt > Proxy đang bỏ qua danh sách kiểm tra trách nhiệm AGY và bước nhập cụm từ rủi ro của Gemini trên dashboard. Bạn tự chịu hoàn toàn rủi ro OAuth/tài khoản.',
authMethod: 'Phương thức xác thực',
selectKiroAuthMethod: 'Chọn phương thức xác thực Kiro',
nicknameRequired: 'Biệt danh (bắt buộc)',
@@ -3111,13 +3113,13 @@ const resources = {
viewDocs: 'Xem tài liệu',
},
settingsProxy: {
failedLoadAgyMode: 'Không thể tải chế độ power user AGY',
failedUpdateAgyMode: 'Không thể cập nhật chế độ power user AGY',
failedVerifyAgyMode: 'Không thể xác minh trạng thái lưu của chế độ power user AGY',
failedLoadAgyMode: 'Không thể tải chế độ power user',
failedUpdateAgyMode: 'Không thể cập nhật chế độ power user',
failedVerifyAgyMode: 'Không thể xác minh trạng thái lưu của chế độ power user',
notPersistedAgyMode:
'Chế độ power user AGY chưa được lưu bền vững. Cấu hình có thể đã bị tiến trình khác sửa.',
agyModeEnabled: 'Đã bật chế độ power user AGY.',
agyModeDisabled: 'Đã tắt chế độ power user AGY.',
'Chế độ power user chưa được lưu bền vững. Cấu hình có thể đã bị tiến trình khác sửa.',
agyModeEnabled: 'Đã bật chế độ power user.',
agyModeDisabled: 'Đã tắt chế độ power user.',
typePhraseToContinue: 'Nhập "{{value}}" để tiếp tục.',
description:
'Định cấu hình kết nối {{backend}} cục bộ hoặc từ xa cho cấu hình dựa trên proxy',
@@ -3140,21 +3142,21 @@ const resources = {
safety: 'An toàn',
agyModeTitle: 'Chế độ power user Antigravity',
agyModeDesc:
'Bỏ qua danh sách kiểm tra trách nhiệm AGY trong quy trình Thêm tài khoản và `ccs agy`.',
'Bỏ qua danh sách kiểm tra trách nhiệm AGY và bước nhập cụm từ xác nhận của Gemini trên dashboard.',
agyWarning:
'Chỉ bật nếu bạn hiểu rõ rủi ro bị đình chỉ/cấm OAuth (#509). CCS không chịu trách nhiệm khi mất tài khoản.',
finalConfirm: 'Xác nhận cuối cùng',
finalConfirmDesc:
'Việc bật tính năng này sẽ bỏ qua các điểm kiểm tra an toàn AGY trong cả bảng điều khiển và CLI. Xem lại vấn đề #509 và nhập cụm từ chính xác để tiếp tục.',
'Việc bật tính năng này sẽ bỏ qua các điểm kiểm tra an toàn AGY và bước nhập cụm từ rủi ro của Gemini trên dashboard. Xem lại vấn đề #509 và nhập cụm từ chính xác để tiếp tục.',
step1: 'Bước 1',
readIssue: 'Đọc vấn đề #509',
step2: 'Bước 2',
typePrefix: 'Nhập',
typeSuffix: 'để kích hoạt.',
typePhraseAria: 'Nhập I ACCEPT RISK để bật chế độ power user Antigravity',
typePhraseAria: 'Nhập I ACCEPT RISK để bật chế độ power user',
exactPhrase: 'Phải nhập chính xác cụm từ.',
enableAgyMode: 'Bật chế độ power user',
toggleAgyMode: 'Chuyển đổi chế độ power user AGY',
toggleAgyMode: 'Chuyển đổi chế độ power user',
fallbackSettings: 'Cài đặt dự phòng',
enableFallback: 'Cho phép dự phòng về cục bộ',
enableFallbackDesc: 'Sử dụng proxy cục bộ nếu không thể truy cập được từ xa',
@@ -4083,7 +4085,7 @@ const resources = {
'認証をクリックすると OAuth URL を取得します。任意のブラウザーで開いてサインインしてください。',
powerUserEnabled: '上級者モードが有効です',
powerUserSkipped:
'設定 > プロキシで AGY の責任確認チェックをスキップしています。OAuth / アカウントに関するリスクはすべて自己責任となります。',
'設定 > プロキシのパワーユーザーモードにより、AGY の責任確認チェックと Gemini ダッシュボードのリスク文言入力をスキップしています。OAuth / アカウントに関するリスクはすべて自己責任す。',
authMethod: '認証方法',
selectKiroAuthMethod: 'Kiro の認証方法を選択',
nicknameRequired: 'ニックネーム(必須)',
@@ -4339,13 +4341,13 @@ const resources = {
viewDocs: 'ドキュメントを見る',
},
settingsProxy: {
failedLoadAgyMode: 'AGY パワーユーザーモードの読み込みに失敗しました',
failedUpdateAgyMode: 'AGY パワーユーザーモードの更新に失敗しました',
failedVerifyAgyMode: 'AGY パワーユーザーモードの永続化確認に失敗しました',
failedLoadAgyMode: 'パワーユーザーモードの読み込みに失敗しました',
failedUpdateAgyMode: 'パワーユーザーモードの更新に失敗しました',
failedVerifyAgyMode: 'パワーユーザーモードの永続化確認に失敗しました',
notPersistedAgyMode:
'AGY パワーユーザーモードが保存されませんでした。別のプロセスが設定を変更した可能性があります。',
agyModeEnabled: 'AGY パワーユーザーモードを有効にしました。',
agyModeDisabled: 'AGY パワーユーザーモードを無効にしました。',
'パワーユーザーモードが保存されませんでした。別のプロセスが設定を変更した可能性があります。',
agyModeEnabled: 'パワーユーザーモードを有効にしました。',
agyModeDisabled: 'パワーユーザーモードを無効にしました。',
typePhraseToContinue: '続行するには「{{value}}」と入力してください。',
description:
'プロキシベースのプロファイル用に、ローカルまたはリモートの {{backend}} 接続を設定します',
@@ -4368,22 +4370,21 @@ const resources = {
safety: '安全設定',
agyModeTitle: 'Antigravity パワーユーザーモード',
agyModeDesc:
'アカウント追加画面と `ccs agy` フローで、AGY 責任確認チェックをスキップします。',
'アカウント追加画面AGY 責任確認チェックと、Gemini ダッシュボードの入力確認をスキップします。',
agyWarning:
'OAuth の停止/BAN リスクパターン (#509) を十分理解している場合にのみ使用してください。CCS はアカウント喪失の責任を負いません。',
finalConfirm: '最終確認',
finalConfirmDesc:
'有効にすると、ダッシュボード CLI の両方で AGY 安全チェックをスキップします。続行する前に issue #509 を確認し、正確なフレーズを入力してください。',
'有効にすると、ダッシュボード / CLI の AGY 安全チェックと Gemini ダッシュボードのリスク文言確認をスキップします。続行する前に issue #509 を確認し、正確なフレーズを入力してください。',
step1: 'ステップ 1',
readIssue: 'Issue #509 を読む',
step2: 'ステップ 2',
typePrefix: '有効にするには',
typeSuffix: 'と入力してください。',
typePhraseAria:
'Antigravity パワーユーザーモードを有効にするには I ACCEPT RISK と入力してください',
typePhraseAria: 'パワーユーザーモードを有効にするには I ACCEPT RISK と入力してください',
exactPhrase: '完全一致で入力してください。',
enableAgyMode: 'パワーユーザーモードを有効にする',
toggleAgyMode: 'AGY パワーユーザーモードを切り替え',
toggleAgyMode: 'パワーユーザーモードを切り替え',
fallbackSettings: 'フォールバック設定',
enableFallback: 'ローカルへのフォールバックを有効化',
enableFallbackDesc: 'リモートに接続できない場合はローカルプロキシを使用します',