mirror of
https://github.com/router-for-me/Cli-Proxy-API-Management-Center.git
synced 2026-06-16 21:03:58 +08:00
Compare commits
3 Commits
@@ -936,6 +936,15 @@ export function VisualConfigEditor({
|
||||
disabled={disabled}
|
||||
onChange={(quotaSwitchPreviewModel) => onChange({ quotaSwitchPreviewModel })}
|
||||
/>
|
||||
<ToggleRow
|
||||
title={t('config_management.visual.sections.quota.antigravity_credits')}
|
||||
description={t(
|
||||
'config_management.visual.sections.quota.antigravity_credits_desc'
|
||||
)}
|
||||
checked={values.quotaAntigravityCredits}
|
||||
disabled={disabled}
|
||||
onChange={(quotaAntigravityCredits) => onChange({ quotaAntigravityCredits })}
|
||||
/>
|
||||
</SectionGrid>
|
||||
</ConfigSection>
|
||||
|
||||
|
||||
@@ -647,6 +647,12 @@ function getNextDirtyFields(
|
||||
nextValues.quotaSwitchPreviewModel === baselineValues.quotaSwitchPreviewModel
|
||||
);
|
||||
}
|
||||
if (Object.prototype.hasOwnProperty.call(patch, 'quotaAntigravityCredits')) {
|
||||
updateDirty(
|
||||
'quotaAntigravityCredits',
|
||||
nextValues.quotaAntigravityCredits === baselineValues.quotaAntigravityCredits
|
||||
);
|
||||
}
|
||||
if (Object.prototype.hasOwnProperty.call(patch, 'routingStrategy')) {
|
||||
updateDirty('routingStrategy', nextValues.routingStrategy === baselineValues.routingStrategy);
|
||||
}
|
||||
@@ -827,6 +833,7 @@ export function useVisualConfig() {
|
||||
|
||||
quotaSwitchProject: Boolean(quotaExceeded?.['switch-project'] ?? true),
|
||||
quotaSwitchPreviewModel: Boolean(quotaExceeded?.['switch-preview-model'] ?? true),
|
||||
quotaAntigravityCredits: Boolean(quotaExceeded?.['antigravity-credits'] ?? true),
|
||||
|
||||
routingStrategy: routing?.strategy === 'fill-first' ? 'fill-first' : 'round-robin',
|
||||
|
||||
@@ -929,11 +936,16 @@ export function useVisualConfig() {
|
||||
if (
|
||||
docHas(doc, ['quota-exceeded']) ||
|
||||
!values.quotaSwitchProject ||
|
||||
!values.quotaSwitchPreviewModel
|
||||
!values.quotaSwitchPreviewModel ||
|
||||
!values.quotaAntigravityCredits
|
||||
) {
|
||||
ensureMapInDoc(doc, ['quota-exceeded']);
|
||||
doc.setIn(['quota-exceeded', 'switch-project'], values.quotaSwitchProject);
|
||||
doc.setIn(['quota-exceeded', 'switch-preview-model'], values.quotaSwitchPreviewModel);
|
||||
doc.setIn(
|
||||
['quota-exceeded', 'antigravity-credits'],
|
||||
values.quotaAntigravityCredits
|
||||
);
|
||||
deleteIfMapEmpty(doc, ['quota-exceeded']);
|
||||
}
|
||||
|
||||
|
||||
@@ -891,17 +891,6 @@
|
||||
"kimi_oauth_status_error": "Authentication failed:",
|
||||
"kimi_oauth_start_error": "Failed to start Kimi OAuth:",
|
||||
"kimi_oauth_polling_error": "Failed to check authentication status:",
|
||||
"qwen_oauth_title": "Qwen OAuth",
|
||||
"qwen_oauth_button": "Start Qwen Login",
|
||||
"qwen_oauth_hint": "Login to Qwen service through device authorization flow, automatically obtain and save authentication files.",
|
||||
"qwen_oauth_url_label": "Authorization URL:",
|
||||
"qwen_open_link": "Open Link",
|
||||
"qwen_copy_link": "Copy Link",
|
||||
"qwen_oauth_status_waiting": "Waiting for authentication...",
|
||||
"qwen_oauth_status_success": "Authentication successful!",
|
||||
"qwen_oauth_status_error": "Authentication failed:",
|
||||
"qwen_oauth_start_error": "Failed to start Qwen OAuth:",
|
||||
"qwen_oauth_polling_error": "Failed to check authentication status:",
|
||||
"oauth_callback_label": "Callback URL",
|
||||
"oauth_callback_placeholder": "http://localhost:1455/auth/callback?code=...&state=...",
|
||||
"oauth_callback_hint": "Remote browser mode: after the provider redirects to http://localhost:..., copy the full URL and submit it here.",
|
||||
@@ -1270,7 +1259,9 @@
|
||||
"switch_project": "Switch Project",
|
||||
"switch_project_desc": "Automatically switch to another project when quota is exceeded",
|
||||
"switch_preview_model": "Switch to Preview Model",
|
||||
"switch_preview_model_desc": "Switch to preview model version when quota is exceeded"
|
||||
"switch_preview_model_desc": "Switch to preview model version when quota is exceeded",
|
||||
"antigravity_credits": "Antigravity Credits Retry",
|
||||
"antigravity_credits_desc": "Retry once with enabledCreditTypes=[\"GOOGLE_ONE_AI\"] when Antigravity returns quota_exhausted 429"
|
||||
},
|
||||
"streaming": {
|
||||
"title": "Streaming Configuration",
|
||||
|
||||
@@ -888,17 +888,6 @@
|
||||
"kimi_oauth_status_error": "Ошибка аутентификации:",
|
||||
"kimi_oauth_start_error": "Не удалось запустить Kimi OAuth:",
|
||||
"kimi_oauth_polling_error": "Не удалось проверить статус аутентификации:",
|
||||
"qwen_oauth_title": "Qwen OAuth",
|
||||
"qwen_oauth_button": "Начать вход Qwen",
|
||||
"qwen_oauth_hint": "Выполните вход в сервис Qwen через поток авторизации устройства и автоматически получите/сохраните файлы авторизации.",
|
||||
"qwen_oauth_url_label": "URL авторизации:",
|
||||
"qwen_open_link": "Открыть ссылку",
|
||||
"qwen_copy_link": "Скопировать ссылку",
|
||||
"qwen_oauth_status_waiting": "Ожидание аутентификации...",
|
||||
"qwen_oauth_status_success": "Аутентификация успешна!",
|
||||
"qwen_oauth_status_error": "Ошибка аутентификации:",
|
||||
"qwen_oauth_start_error": "Не удалось запустить Qwen OAuth:",
|
||||
"qwen_oauth_polling_error": "Не удалось проверить статус аутентификации:",
|
||||
"oauth_callback_label": "Callback URL",
|
||||
"oauth_callback_placeholder": "http://localhost:1455/auth/callback?code=...&state=...",
|
||||
"oauth_callback_hint": "Режим удалённого браузера: после перенаправления провайдера на http://localhost:... скопируйте полный URL и отправьте его здесь.",
|
||||
@@ -1269,7 +1258,9 @@
|
||||
"switch_project": "Переключить проект",
|
||||
"switch_project_desc": "Автоматически переходить на другой проект при превышении квоты",
|
||||
"switch_preview_model": "Переключить на preview-модель",
|
||||
"switch_preview_model_desc": "Переключаться на preview-версию модели при превышении квоты"
|
||||
"switch_preview_model_desc": "Переключаться на preview-версию модели при превышении квоты",
|
||||
"antigravity_credits": "Повтор Antigravity Credits",
|
||||
"antigravity_credits_desc": "При ответе Antigravity quota_exhausted 429 повторять запрос один раз с enabledCreditTypes=[\"GOOGLE_ONE_AI\"]"
|
||||
},
|
||||
"streaming": {
|
||||
"title": "Настройки стриминга",
|
||||
|
||||
@@ -891,17 +891,6 @@
|
||||
"kimi_oauth_status_error": "认证失败:",
|
||||
"kimi_oauth_start_error": "启动 Kimi OAuth 失败:",
|
||||
"kimi_oauth_polling_error": "检查认证状态失败:",
|
||||
"qwen_oauth_title": "Qwen OAuth",
|
||||
"qwen_oauth_button": "开始 Qwen 登录",
|
||||
"qwen_oauth_hint": "通过设备授权流程登录 Qwen 服务,自动获取并保存认证文件。",
|
||||
"qwen_oauth_url_label": "授权链接:",
|
||||
"qwen_open_link": "打开链接",
|
||||
"qwen_copy_link": "复制链接",
|
||||
"qwen_oauth_status_waiting": "等待认证中...",
|
||||
"qwen_oauth_status_success": "认证成功!",
|
||||
"qwen_oauth_status_error": "认证失败:",
|
||||
"qwen_oauth_start_error": "启动 Qwen OAuth 失败:",
|
||||
"qwen_oauth_polling_error": "检查认证状态失败:",
|
||||
"oauth_callback_label": "回调 URL",
|
||||
"oauth_callback_placeholder": "http://localhost:1455/auth/callback?code=...&state=...",
|
||||
"oauth_callback_hint": "远程浏览器模式:当授权跳转到 http://localhost:... 后,复制完整 URL 并提交到这里。",
|
||||
@@ -1270,7 +1259,9 @@
|
||||
"switch_project": "切换项目",
|
||||
"switch_project_desc": "配额耗尽时自动切换到其他项目",
|
||||
"switch_preview_model": "切换预览模型",
|
||||
"switch_preview_model_desc": "配额耗尽时切换到预览版本模型"
|
||||
"switch_preview_model_desc": "配额耗尽时切换到预览版本模型",
|
||||
"antigravity_credits": "Antigravity Credits 重试",
|
||||
"antigravity_credits_desc": "Antigravity 返回 quota_exhausted 429 时,使用 enabledCreditTypes=[\"GOOGLE_ONE_AI\"] 重试一次"
|
||||
},
|
||||
"streaming": {
|
||||
"title": "流式传输配置",
|
||||
|
||||
@@ -14,7 +14,6 @@ import iconAntigravity from '@/assets/icons/antigravity.svg';
|
||||
import iconGemini from '@/assets/icons/gemini.svg';
|
||||
import iconKimiLight from '@/assets/icons/kimi-light.svg';
|
||||
import iconKimiDark from '@/assets/icons/kimi-dark.svg';
|
||||
import iconQwen from '@/assets/icons/qwen.svg';
|
||||
import iconIflow from '@/assets/icons/iflow.svg';
|
||||
import iconVertex from '@/assets/icons/vertex.svg';
|
||||
|
||||
@@ -76,8 +75,7 @@ const PROVIDERS: { id: OAuthProvider; titleKey: string; hintKey: string; urlLabe
|
||||
{ id: 'anthropic', titleKey: 'auth_login.anthropic_oauth_title', hintKey: 'auth_login.anthropic_oauth_hint', urlLabelKey: 'auth_login.anthropic_oauth_url_label', icon: iconClaude },
|
||||
{ id: 'antigravity', titleKey: 'auth_login.antigravity_oauth_title', hintKey: 'auth_login.antigravity_oauth_hint', urlLabelKey: 'auth_login.antigravity_oauth_url_label', icon: iconAntigravity },
|
||||
{ id: 'gemini-cli', titleKey: 'auth_login.gemini_cli_oauth_title', hintKey: 'auth_login.gemini_cli_oauth_hint', urlLabelKey: 'auth_login.gemini_cli_oauth_url_label', icon: iconGemini },
|
||||
{ id: 'kimi', titleKey: 'auth_login.kimi_oauth_title', hintKey: 'auth_login.kimi_oauth_hint', urlLabelKey: 'auth_login.kimi_oauth_url_label', icon: { light: iconKimiLight, dark: iconKimiDark } },
|
||||
{ id: 'qwen', titleKey: 'auth_login.qwen_oauth_title', hintKey: 'auth_login.qwen_oauth_hint', urlLabelKey: 'auth_login.qwen_oauth_url_label', icon: iconQwen }
|
||||
{ id: 'kimi', titleKey: 'auth_login.kimi_oauth_title', hintKey: 'auth_login.kimi_oauth_hint', urlLabelKey: 'auth_login.kimi_oauth_url_label', icon: { light: iconKimiLight, dark: iconKimiDark } }
|
||||
];
|
||||
|
||||
const CALLBACK_SUPPORTED: OAuthProvider[] = ['codex', 'anthropic', 'antigravity', 'gemini-cli'];
|
||||
|
||||
@@ -9,8 +9,7 @@ export type OAuthProvider =
|
||||
| 'anthropic'
|
||||
| 'antigravity'
|
||||
| 'gemini-cli'
|
||||
| 'kimi'
|
||||
| 'qwen';
|
||||
| 'kimi';
|
||||
|
||||
export interface OAuthStartResponse {
|
||||
url: string;
|
||||
|
||||
@@ -366,7 +366,12 @@ export const normalizeConfigResponse = (raw: unknown): Config => {
|
||||
if (isRecord(quota)) {
|
||||
config.quotaExceeded = {
|
||||
switchProject: normalizeBoolean(quota['switch-project'] ?? quota.switchProject),
|
||||
switchPreviewModel: normalizeBoolean(quota['switch-preview-model'] ?? quota.switchPreviewModel)
|
||||
switchPreviewModel: normalizeBoolean(
|
||||
quota['switch-preview-model'] ?? quota.switchPreviewModel
|
||||
),
|
||||
antigravityCredits: normalizeBoolean(
|
||||
quota['antigravity-credits'] ?? quota.antigravityCredits
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import type { AmpcodeConfig } from './ampcode';
|
||||
export interface QuotaExceededConfig {
|
||||
switchProject?: boolean;
|
||||
switchPreviewModel?: boolean;
|
||||
antigravityCredits?: boolean;
|
||||
}
|
||||
|
||||
export interface Config {
|
||||
|
||||
+1
-2
@@ -9,8 +9,7 @@ export type OAuthProvider =
|
||||
| 'anthropic'
|
||||
| 'antigravity'
|
||||
| 'gemini-cli'
|
||||
| 'kimi'
|
||||
| 'qwen';
|
||||
| 'kimi';
|
||||
|
||||
// OAuth 流程状态
|
||||
export interface OAuthFlow {
|
||||
|
||||
@@ -75,6 +75,7 @@ export type VisualConfigValues = {
|
||||
maxRetryInterval: string;
|
||||
quotaSwitchProject: boolean;
|
||||
quotaSwitchPreviewModel: boolean;
|
||||
quotaAntigravityCredits: boolean;
|
||||
routingStrategy: 'round-robin' | 'fill-first';
|
||||
wsAuth: boolean;
|
||||
payloadDefaultRules: PayloadRule[];
|
||||
@@ -114,6 +115,7 @@ export const DEFAULT_VISUAL_VALUES: VisualConfigValues = {
|
||||
maxRetryInterval: '',
|
||||
quotaSwitchProject: true,
|
||||
quotaSwitchPreviewModel: true,
|
||||
quotaAntigravityCredits: true,
|
||||
routingStrategy: 'round-robin',
|
||||
wsAuth: false,
|
||||
payloadDefaultRules: [],
|
||||
|
||||
@@ -57,16 +57,14 @@ export const OAUTH_CARD_IDS = [
|
||||
'anthropic-oauth-card',
|
||||
'antigravity-oauth-card',
|
||||
'gemini-cli-oauth-card',
|
||||
'kimi-oauth-card',
|
||||
'qwen-oauth-card'
|
||||
'kimi-oauth-card'
|
||||
];
|
||||
export const OAUTH_PROVIDERS = {
|
||||
CODEX: 'codex',
|
||||
ANTHROPIC: 'anthropic',
|
||||
ANTIGRAVITY: 'antigravity',
|
||||
GEMINI_CLI: 'gemini-cli',
|
||||
KIMI: 'kimi',
|
||||
QWEN: 'qwen'
|
||||
KIMI: 'kimi'
|
||||
} as const;
|
||||
|
||||
// API 端点
|
||||
|
||||
@@ -376,6 +376,10 @@ export function buildCandidateUsageSourceIds(input: {
|
||||
|
||||
const apiKey = input.apiKey?.trim();
|
||||
if (apiKey) {
|
||||
// Include the normalised form first so that "non-standard" keys (e.g. short tokens,
|
||||
// keys containing '/' etc.) that are classified as text by normalizeUsageSourceId()
|
||||
// can still match usage details.
|
||||
result.push(normalizeUsageSourceId(apiKey));
|
||||
result.push(`${USAGE_SOURCE_PREFIX_KEY}${fnv1a64Hex(apiKey)}`);
|
||||
result.push(`${USAGE_SOURCE_PREFIX_MASKED}${maskApiKey(apiKey)}`);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user