Compare commits

...

8 Commits

17 changed files with 794 additions and 879 deletions
+1 -1
View File
@@ -22,7 +22,7 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
node-version: '24'
cache: 'npm'
- name: Install dependencies
+534 -850
View File
File diff suppressed because it is too large Load Diff
+3 -3
View File
@@ -31,7 +31,7 @@
"@types/react-dom": "^19.2.3",
"@typescript-eslint/eslint-plugin": "^8.48.1",
"@typescript-eslint/parser": "^8.48.1",
"@vitejs/plugin-react": "^5.1.1",
"@vitejs/plugin-react": "^6.0.1",
"eslint": "^9.39.1",
"eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-react-refresh": "^0.4.24",
@@ -40,7 +40,7 @@
"sass": "^1.94.2",
"typescript": "^5.9.3",
"typescript-eslint": "^8.48.1",
"vite": "^7.2.6",
"vite-plugin-singlefile": "^2.3.0"
"vite": "^8.0.10",
"vite-plugin-singlefile": "^2.3.3"
}
}
+3 -4
View File
@@ -410,14 +410,13 @@ const fetchCodexQuota = async (
const planTypeFromFile = resolveCodexPlanType(file);
const accountId = resolveCodexChatgptAccountId(file);
if (!accountId) {
throw new Error(t('codex_quota.missing_account_id'));
}
const requestHeader: Record<string, string> = {
...CODEX_REQUEST_HEADERS,
'Chatgpt-Account-Id': accountId,
};
if (accountId) {
requestHeader['Chatgpt-Account-Id'] = accountId;
}
const result = await apiCallApi.request({
authIndex,
+7
View File
@@ -3,6 +3,7 @@ import iconAntigravity from '@/assets/icons/antigravity.svg';
import iconClaude from '@/assets/icons/claude.svg';
import iconCodex from '@/assets/icons/codex.svg';
import iconGemini from '@/assets/icons/gemini.svg';
import iconGrok from '@/assets/icons/grok.svg';
import iconIflow from '@/assets/icons/iflow.svg';
import iconKimiDark from '@/assets/icons/kimi-dark.svg';
import iconKimiLight from '@/assets/icons/kimi-light.svg';
@@ -82,6 +83,11 @@ export const TYPE_COLORS: Record<string, TypeColorSet> = {
light: { bg: '#e0f7fa', text: '#006064' },
dark: { bg: '#004d40', text: '#80deea' },
},
// xAI / Grok: neutral graphite, kept distinct from the blue and purple providers
xai: {
light: { bg: '#eceff3', text: '#15181d' },
dark: { bg: '#e5e7eb', text: '#111827' },
},
// iFlow logo: 品红紫渐变 #5C5CFF → #AE5CFF,偏品红以区别于 Qwen 的紫罗兰
iflow: {
light: { bg: '#f5e3fc', text: '#9025c8' },
@@ -109,6 +115,7 @@ export const AUTH_FILE_ICONS: Record<string, AuthFileIconAsset> = {
codex: iconCodex,
gemini: iconGemini,
'gemini-cli': iconGemini,
xai: iconGrok,
iflow: iconIflow,
kimi: { light: iconKimiLight, dark: iconKimiDark },
qwen: iconQwen,
+18
View File
@@ -540,6 +540,7 @@
"filter_claude": "Claude",
"filter_codex": "Codex",
"filter_antigravity": "Antigravity",
"filter_xai": "xAI",
"filter_iflow": "iFlow",
"filter_vertex": "Vertex",
"filter_empty": "Empty",
@@ -552,6 +553,7 @@
"type_claude": "Claude",
"type_codex": "Codex",
"type_antigravity": "Antigravity",
"type_xai": "xAI",
"type_iflow": "iFlow",
"type_vertex": "Vertex",
"type_empty": "Empty",
@@ -901,6 +903,22 @@
"kimi_oauth_status_error": "Authentication failed:",
"kimi_oauth_start_error": "Failed to start Kimi OAuth:",
"kimi_oauth_polling_error": "Failed to check authentication status:",
"xai_oauth_title": "xAI OAuth",
"xai_oauth_button": "Start xAI Login",
"xai_oauth_hint": "Login to xAI Grok through OAuth flow, automatically obtain and save authentication files.",
"xai_oauth_url_label": "Authorization URL:",
"xai_open_link": "Open Link",
"xai_copy_link": "Copy Link",
"xai_oauth_status_waiting": "Waiting for authentication...",
"xai_oauth_status_success": "Authentication successful!",
"xai_oauth_status_error": "Authentication failed:",
"xai_oauth_start_error": "Failed to start xAI OAuth:",
"xai_oauth_polling_error": "Failed to check authentication status:",
"xai_callback_label": "Callback URL or code",
"xai_callback_placeholder": "Paste the code shown by Grok, or the full callback URL",
"xai_callback_hint": "Grok may only show a code on the page. Paste that code directly and the app will submit it as http://127.0.0.1:56121/callback?... automatically.",
"xai_callback_required": "Please paste the code shown by Grok or the full callback URL first.",
"xai_callback_state_missing": "Missing the state for this xAI login. Start xAI login again, then submit the code.",
"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.",
+18
View File
@@ -540,6 +540,7 @@
"filter_claude": "Claude",
"filter_codex": "Codex",
"filter_antigravity": "Antigravity",
"filter_xai": "xAI",
"filter_iflow": "iFlow",
"filter_vertex": "Vertex",
"filter_empty": "Пусто",
@@ -552,6 +553,7 @@
"type_claude": "Claude",
"type_codex": "Codex",
"type_antigravity": "Antigravity",
"type_xai": "xAI",
"type_iflow": "iFlow",
"type_vertex": "Vertex",
"type_empty": "Пусто",
@@ -898,6 +900,22 @@
"kimi_oauth_status_error": "Ошибка аутентификации:",
"kimi_oauth_start_error": "Не удалось запустить Kimi OAuth:",
"kimi_oauth_polling_error": "Не удалось проверить статус аутентификации:",
"xai_oauth_title": "xAI OAuth",
"xai_oauth_button": "Начать вход xAI",
"xai_oauth_hint": "Выполните вход в xAI Grok через OAuth и автоматически получите/сохраните файлы авторизации.",
"xai_oauth_url_label": "URL авторизации:",
"xai_open_link": "Открыть ссылку",
"xai_copy_link": "Скопировать ссылку",
"xai_oauth_status_waiting": "Ожидание аутентификации...",
"xai_oauth_status_success": "Аутентификация успешна!",
"xai_oauth_status_error": "Ошибка аутентификации:",
"xai_oauth_start_error": "Не удалось запустить xAI OAuth:",
"xai_oauth_polling_error": "Не удалось проверить статус аутентификации:",
"xai_callback_label": "Callback URL или код",
"xai_callback_placeholder": "Вставьте код со страницы Grok или полный callback URL",
"xai_callback_hint": "Grok иногда показывает только код на странице. Вставьте этот код напрямую, приложение автоматически отправит его как http://127.0.0.1:56121/callback?...",
"xai_callback_required": "Сначала вставьте код со страницы Grok или полный callback URL.",
"xai_callback_state_missing": "Отсутствует state для этого входа xAI. Запустите вход xAI заново и затем отправьте код.",
"oauth_callback_label": "Callback URL",
"oauth_callback_placeholder": "http://localhost:1455/auth/callback?code=...&state=...",
"oauth_callback_hint": "Режим удалённого браузера: после перенаправления провайдера на http://localhost:... скопируйте полный URL и отправьте его здесь.",
+18
View File
@@ -540,6 +540,7 @@
"filter_claude": "Claude",
"filter_codex": "Codex",
"filter_antigravity": "Antigravity",
"filter_xai": "xAI",
"filter_iflow": "iFlow",
"filter_vertex": "Vertex",
"filter_empty": "空文件",
@@ -552,6 +553,7 @@
"type_claude": "Claude",
"type_codex": "Codex",
"type_antigravity": "Antigravity",
"type_xai": "xAI",
"type_iflow": "iFlow",
"type_vertex": "Vertex",
"type_empty": "空文件",
@@ -901,6 +903,22 @@
"kimi_oauth_status_error": "认证失败:",
"kimi_oauth_start_error": "启动 Kimi OAuth 失败:",
"kimi_oauth_polling_error": "检查认证状态失败:",
"xai_oauth_title": "xAI OAuth",
"xai_oauth_button": "开始 xAI 登录",
"xai_oauth_hint": "通过 OAuth 流程登录 xAI Grok 服务,自动获取并保存认证文件。",
"xai_oauth_url_label": "授权链接:",
"xai_open_link": "打开链接",
"xai_copy_link": "复制链接",
"xai_oauth_status_waiting": "等待认证中...",
"xai_oauth_status_success": "认证成功!",
"xai_oauth_status_error": "认证失败:",
"xai_oauth_start_error": "启动 xAI OAuth 失败:",
"xai_oauth_polling_error": "检查认证状态失败:",
"xai_callback_label": "回调 URL 或授权码",
"xai_callback_placeholder": "粘贴页面显示的 code,或完整 callback URL",
"xai_callback_hint": "Grok 有时只在页面显示 code。可直接粘贴 code,系统会自动拼接为 http://127.0.0.1:56121/callback?... 后提交。",
"xai_callback_required": "请先粘贴 Grok 页面显示的 code 或完整回调 URL。",
"xai_callback_state_missing": "缺少本次 xAI 登录的 state,请重新开始 xAI 登录后再提交 code。",
"oauth_callback_label": "回调 URL",
"oauth_callback_placeholder": "http://localhost:1455/auth/callback?code=...&state=...",
"oauth_callback_hint": "远程浏览器模式:当授权跳转到 http://localhost:... 后,复制完整 URL 并提交到这里。",
+18
View File
@@ -540,6 +540,7 @@
"filter_claude": "Claude",
"filter_codex": "Codex",
"filter_antigravity": "Antigravity",
"filter_xai": "xAI",
"filter_iflow": "iFlow",
"filter_vertex": "Vertex",
"filter_empty": "空檔案",
@@ -552,6 +553,7 @@
"type_claude": "Claude",
"type_codex": "Codex",
"type_antigravity": "Antigravity",
"type_xai": "xAI",
"type_iflow": "iFlow",
"type_vertex": "Vertex",
"type_empty": "空檔案",
@@ -901,6 +903,22 @@
"kimi_oauth_status_error": "驗證失敗:",
"kimi_oauth_start_error": "啟動 Kimi OAuth 失敗:",
"kimi_oauth_polling_error": "檢查驗證狀態失敗:",
"xai_oauth_title": "xAI OAuth",
"xai_oauth_button": "開始 xAI 登入",
"xai_oauth_hint": "透過 OAuth 流程登入 xAI Grok 服務,自動取得並儲存驗證檔案。",
"xai_oauth_url_label": "授權連結:",
"xai_open_link": "開啟連結",
"xai_copy_link": "複製連結",
"xai_oauth_status_waiting": "等待驗證中...",
"xai_oauth_status_success": "驗證成功!",
"xai_oauth_status_error": "驗證失敗:",
"xai_oauth_start_error": "啟動 xAI OAuth 失敗:",
"xai_oauth_polling_error": "檢查驗證狀態失敗:",
"xai_callback_label": "回調 URL 或授權碼",
"xai_callback_placeholder": "貼上頁面顯示的 code,或完整 callback URL",
"xai_callback_hint": "Grok 有時只在頁面顯示 code。可直接貼上 code,系統會自動拼接為 http://127.0.0.1:56121/callback?... 後提交。",
"xai_callback_required": "請先貼上 Grok 頁面顯示的 code 或完整回調 URL。",
"xai_callback_state_missing": "缺少本次 xAI 登入的 state,請重新開始 xAI 登入後再提交 code。",
"qwen_oauth_title": "Qwen OAuth",
"qwen_oauth_button": "開始 Qwen 登入",
"qwen_oauth_hint": "透過裝置授權流程登入 Qwen 服務,自動取得並儲存驗證檔案。",
+35 -5
View File
@@ -57,11 +57,7 @@ $openai-toolbar-control-height: 36px;
.openaiProviderList {
display: grid;
gap: $spacing-md;
grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
@media (min-width: 1400px) {
grid-template-columns: repeat(3, 1fr);
}
grid-template-columns: repeat(auto-fill, minmax(min(100%, 350px), 1fr));
@include mobile {
grid-template-columns: 1fr;
@@ -78,6 +74,7 @@ $openai-toolbar-control-height: 36px;
align-items: stretch;
gap: $spacing-sm;
min-height: 0;
min-width: 0;
}
.openaiProviderMeta {
@@ -456,6 +453,7 @@ $openai-toolbar-control-height: 36px;
align-items: baseline;
gap: 6px;
margin-bottom: 4px;
min-width: 0;
font-size: 13px;
line-height: 1.4;
}
@@ -470,6 +468,9 @@ $openai-toolbar-control-height: 36px;
.fieldValue {
color: var(--text-primary);
font-weight: 600;
min-width: 0;
max-width: 100%;
overflow-wrap: anywhere;
word-break: break-all;
font-family: 'Monaco', 'Menlo', 'Consolas', 'Ubuntu Mono', monospace;
}
@@ -480,22 +481,30 @@ $openai-toolbar-control-height: 36px;
flex-wrap: wrap;
gap: 6px;
margin-top: 6px;
min-width: 0;
max-width: 100%;
}
.headerBadge {
display: inline-flex;
align-items: center;
gap: 4px;
flex-wrap: wrap;
min-width: 0;
max-width: 100%;
background: var(--accent-tertiary, #f3f4f6);
border: 1px solid var(--border-primary);
border-radius: 12px;
padding: 4px 10px;
font-size: 12px;
color: var(--text-secondary);
overflow-wrap: anywhere;
word-break: break-word;
strong {
font-weight: 600;
color: var(--text-primary);
flex-shrink: 0;
}
}
@@ -506,6 +515,8 @@ $openai-toolbar-control-height: 36px;
gap: 6px;
margin-top: 8px;
align-items: center;
min-width: 0;
max-width: 100%;
}
.modelCountLabel {
@@ -525,6 +536,8 @@ $openai-toolbar-control-height: 36px;
align-items: center;
gap: 4px;
flex-wrap: wrap;
min-width: 0;
max-width: 100%;
background: var(--bg-quinary, #f8f9fa);
color: var(--text-secondary);
border: 1px solid var(--border-secondary);
@@ -542,12 +555,16 @@ $openai-toolbar-control-height: 36px;
.modelName {
font-weight: 600;
color: var(--text-primary);
min-width: 0;
max-width: 100%;
overflow-wrap: anywhere;
}
.modelAlias {
color: var(--text-tertiary);
font-style: italic;
min-width: 0;
max-width: 100%;
overflow-wrap: anywhere;
&::before {
@@ -581,6 +598,8 @@ $openai-toolbar-control-height: 36px;
// API密钥条目列表(二级卡片)
.apiKeyEntriesSection {
margin-top: 10px;
min-width: 0;
max-width: 100%;
}
.apiKeyEntriesLabel {
@@ -594,6 +613,8 @@ $openai-toolbar-control-height: 36px;
display: flex;
flex-direction: column;
gap: 6px;
min-width: 0;
max-width: 100%;
}
.apiKeyEntryCard {
@@ -601,6 +622,8 @@ $openai-toolbar-control-height: 36px;
flex-wrap: wrap;
align-items: center;
gap: 8px;
min-width: 0;
max-width: 100%;
padding: 8px 12px;
background: var(--bg-secondary, #f9fafb);
border: 1px solid var(--border-secondary);
@@ -626,12 +649,19 @@ $openai-toolbar-control-height: 36px;
font-family: 'Monaco', 'Menlo', 'Consolas', 'Ubuntu Mono', monospace;
font-weight: 600;
color: var(--text-primary);
min-width: 0;
max-width: 100%;
overflow-wrap: anywhere;
word-break: break-all;
}
.apiKeyEntryProxy {
color: var(--text-tertiary);
font-size: 11px;
min-width: 0;
max-width: 100%;
overflow-wrap: anywhere;
word-break: break-word;
&::before {
content: '| Proxy: ';
+15 -1
View File
@@ -690,7 +690,21 @@
}
.fileCardDisabled {
opacity: 0.72;
background-color: color-mix(in srgb, var(--bg-primary) 78%, var(--bg-secondary));
border-color: color-mix(in srgb, var(--border-color) 62%, transparent);
.providerAvatar,
.cardHeaderContent,
.cardMeta,
.healthStatusMessage,
.cardInsights {
opacity: 0.72;
}
.cardSelection,
.cardActions {
opacity: 1;
}
&:hover {
transform: none;
+107 -7
View File
@@ -16,6 +16,7 @@ import iconGemini from '@/assets/icons/gemini.svg';
import iconKimiLight from '@/assets/icons/kimi-light.svg';
import iconKimiDark from '@/assets/icons/kimi-dark.svg';
import iconVertex from '@/assets/icons/vertex.svg';
import iconGrok from '@/assets/icons/grok.svg';
interface ProviderState {
url?: string;
@@ -67,10 +68,18 @@ 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: '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: 'xai', titleKey: 'auth_login.xai_oauth_title', hintKey: 'auth_login.xai_oauth_hint', urlLabelKey: 'auth_login.xai_oauth_url_label', icon: iconGrok }
];
const CALLBACK_SUPPORTED: OAuthProvider[] = ['codex', 'anthropic', 'antigravity', 'gemini-cli'];
const CALLBACK_SUPPORTED: OAuthProvider[] = [
'codex',
'anthropic',
'antigravity',
'gemini-cli',
'xai'
];
const XAI_CALLBACK_URL = 'http://127.0.0.1:56121/callback';
const SUCCESS_RESET_DELAY_MS = 5000;
const getProviderI18nPrefix = (provider: OAuthProvider) => provider.replace('-', '_');
const getAuthKey = (provider: OAuthProvider, suffix: string) =>
@@ -80,6 +89,77 @@ const getIcon = (icon: string | { light: string; dark: string }, theme: 'light'
return typeof icon === 'string' ? icon : icon[theme];
};
const isAbsoluteUrl = (value: string): boolean => {
try {
new URL(value);
return true;
} catch {
return false;
}
};
const readQueryLikeCallbackInput = (value: string) => {
const trimmed = value.trim();
if (!trimmed) return null;
const queryStart = trimmed.indexOf('?');
const hashStart = trimmed.indexOf('#');
const rawParams =
queryStart >= 0
? trimmed.slice(queryStart + 1)
: hashStart >= 0
? trimmed.slice(hashStart + 1)
: trimmed;
if (!/(^|[&#?])(code|state|error)=/i.test(rawParams)) return null;
return new URLSearchParams(rawParams.replace(/^[?#]/, ''));
};
const extractDisplayedXaiCode = (value: string): string => {
const trimmed = value.trim();
const codeMatch = trimmed.match(/\bcode\s*[:=]\s*([^\s&]+)/i);
return (codeMatch?.[1] ?? trimmed).trim();
};
const buildXaiCallbackUrl = (input: string, state?: string): string | null => {
const trimmed = input.trim();
if (!trimmed) return null;
if (isAbsoluteUrl(trimmed)) return trimmed;
const params = readQueryLikeCallbackInput(trimmed);
if (params) {
const code = params.get('code')?.trim();
const error = params.get('error')?.trim();
const errorDescription = params.get('error_description')?.trim();
const callbackState = params.get('state')?.trim() || state?.trim();
if (!callbackState) return null;
const callbackUrl = new URL(XAI_CALLBACK_URL);
callbackUrl.searchParams.set('state', callbackState);
if (code) callbackUrl.searchParams.set('code', code);
if (error) callbackUrl.searchParams.set('error', error);
if (errorDescription) callbackUrl.searchParams.set('error_description', errorDescription);
return callbackUrl.toString();
}
const code = extractDisplayedXaiCode(trimmed);
const callbackState = state?.trim();
if (!code || !callbackState) return null;
const callbackUrl = new URL(XAI_CALLBACK_URL);
callbackUrl.searchParams.set('code', code);
callbackUrl.searchParams.set('state', callbackState);
return callbackUrl.toString();
};
const resolveCallbackUrl = (
provider: OAuthProvider,
input: string,
state?: string
): string | null => {
if (provider !== 'xai') return input.trim();
return buildXaiCallbackUrl(input, state);
};
export function OAuthPage() {
const { t } = useTranslation();
const navigate = useNavigate();
@@ -262,9 +342,17 @@ export function OAuthPage() {
};
const submitCallback = async (provider: OAuthProvider) => {
const redirectUrl = (states[provider]?.callbackUrl || '').trim();
const callbackInput = (states[provider]?.callbackUrl || '').trim();
if (!callbackInput) {
showNotification(
t(provider === 'xai' ? 'auth_login.xai_callback_required' : 'auth_login.oauth_callback_required'),
'warning'
);
return;
}
const redirectUrl = resolveCallbackUrl(provider, callbackInput, states[provider]?.state);
if (!redirectUrl) {
showNotification(t('auth_login.oauth_callback_required'), 'warning');
showNotification(t(provider === 'xai' ? 'auth_login.xai_callback_state_missing' : 'auth_login.missing_state'), 'warning');
return;
}
updateProviderState(provider, {
@@ -434,8 +522,16 @@ export function OAuthPage() {
{canSubmitCallback && (
<div className={styles.callbackSection}>
<Input
label={t('auth_login.oauth_callback_label')}
hint={t('auth_login.oauth_callback_hint')}
label={t(
provider.id === 'xai'
? 'auth_login.xai_callback_label'
: 'auth_login.oauth_callback_label'
)}
hint={t(
provider.id === 'xai'
? 'auth_login.xai_callback_hint'
: 'auth_login.oauth_callback_hint'
)}
value={state.callbackUrl || ''}
onChange={(e) =>
updateProviderState(provider.id, {
@@ -444,7 +540,11 @@ export function OAuthPage() {
callbackError: undefined
})
}
placeholder={t('auth_login.oauth_callback_placeholder')}
placeholder={t(
provider.id === 'xai'
? 'auth_login.xai_callback_placeholder'
: 'auth_login.oauth_callback_placeholder'
)}
/>
<div className={styles.callbackActions}>
<Button
+9 -2
View File
@@ -9,7 +9,8 @@ export type OAuthProvider =
| 'anthropic'
| 'antigravity'
| 'gemini-cli'
| 'kimi';
| 'kimi'
| 'xai';
export interface OAuthStartResponse {
url: string;
@@ -20,7 +21,13 @@ export interface OAuthCallbackResponse {
status: 'ok';
}
const WEBUI_SUPPORTED: OAuthProvider[] = ['codex', 'anthropic', 'antigravity', 'gemini-cli'];
const WEBUI_SUPPORTED: OAuthProvider[] = [
'codex',
'anthropic',
'antigravity',
'gemini-cli',
'xai'
];
const CALLBACK_PROVIDER_MAP: Partial<Record<OAuthProvider, string>> = {
'gemini-cli': 'gemini'
};
+1 -1
View File
@@ -73,7 +73,7 @@
}
}
:global([data-theme='dark']) {
[data-theme='dark'] {
.btn {
color: #fff;
}
+1
View File
@@ -14,6 +14,7 @@ export type AuthFileType =
| 'claude'
| 'codex'
| 'antigravity'
| 'xai'
| 'iflow'
| 'vertex'
| 'empty'
+4 -2
View File
@@ -58,14 +58,16 @@ export const OAUTH_CARD_IDS = [
'anthropic-oauth-card',
'antigravity-oauth-card',
'gemini-cli-oauth-card',
'kimi-oauth-card'
'kimi-oauth-card',
'xai-oauth-card'
];
export const OAUTH_PROVIDERS = {
CODEX: 'codex',
ANTHROPIC: 'anthropic',
ANTIGRAVITY: 'antigravity',
GEMINI_CLI: 'gemini-cli',
KIMI: 'kimi'
KIMI: 'kimi',
XAI: 'xai'
} as const;
// API 端点
+2 -3
View File
@@ -68,10 +68,9 @@ export default defineConfig({
assetsInlineLimit: 100000000,
chunkSizeWarningLimit: 100000000,
cssCodeSplit: false,
rollupOptions: {
rolldownOptions: {
output: {
inlineDynamicImports: true,
manualChunks: undefined
codeSplitting: false
}
}
}