mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 17:30:23 +08:00
Merge remote-tracking branch 'origin/pr/498'
This commit is contained in:
@@ -79,6 +79,137 @@ export interface OAuthBatchImportTaskStatusResponse {
|
||||
updated_at: number
|
||||
}
|
||||
|
||||
export type BatchImportCredentialsNormalization =
|
||||
| { ok: true; isBatch: boolean; credentials: string }
|
||||
| { ok: false; message: string }
|
||||
|
||||
function getImportCredentialLines(text: string): Array<{ lineNumber: number; text: string }> {
|
||||
return text
|
||||
.split('\n')
|
||||
.map((line, index) => ({ lineNumber: index + 1, text: line.trim() }))
|
||||
.filter(line => line.text && !line.text.startsWith('#'))
|
||||
}
|
||||
|
||||
function jsonParseErrorMessage(error: unknown): string {
|
||||
return error instanceof Error ? error.message : String(error)
|
||||
}
|
||||
|
||||
function normalizeBatchImportItem(
|
||||
value: unknown,
|
||||
location: string,
|
||||
): { ok: true; value: string | Record<string, unknown> } | { ok: false; message: string } {
|
||||
if (typeof value === 'string') {
|
||||
const trimmed = value.trim()
|
||||
if (trimmed) return { ok: true, value: trimmed }
|
||||
return { ok: false, message: `${location} 不能为空字符串` }
|
||||
}
|
||||
if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
|
||||
return { ok: true, value: value as Record<string, unknown> }
|
||||
}
|
||||
return { ok: false, message: `${location} 必须是 JSON 对象或字符串` }
|
||||
}
|
||||
|
||||
function normalizeBatchImportArray(items: unknown[]): BatchImportCredentialsNormalization {
|
||||
if (items.length === 0) {
|
||||
return { ok: false, message: 'JSON 数组不能为空' }
|
||||
}
|
||||
|
||||
const normalized: Array<string | Record<string, unknown>> = []
|
||||
for (const [index, item] of items.entries()) {
|
||||
const result = normalizeBatchImportItem(item, `JSON 数组第 ${index + 1} 项`)
|
||||
if (!result.ok) return result
|
||||
normalized.push(result.value)
|
||||
}
|
||||
|
||||
return {
|
||||
ok: true,
|
||||
isBatch: true,
|
||||
credentials: JSON.stringify(normalized),
|
||||
}
|
||||
}
|
||||
|
||||
function parseImportCredentialLines(
|
||||
lines: Array<{ lineNumber: number; text: string }>,
|
||||
): BatchImportCredentialsNormalization {
|
||||
const normalized: Array<string | Record<string, unknown>> = []
|
||||
|
||||
for (const line of lines) {
|
||||
const firstChar = line.text[0]
|
||||
if (firstChar === '{' || firstChar === '[') {
|
||||
let parsed: unknown
|
||||
try {
|
||||
parsed = JSON.parse(line.text)
|
||||
} catch (error) {
|
||||
return {
|
||||
ok: false,
|
||||
message: `JSON Lines 格式无效,请检查第 ${line.lineNumber} 行: ${jsonParseErrorMessage(error)}`,
|
||||
}
|
||||
}
|
||||
|
||||
if (Array.isArray(parsed)) {
|
||||
for (const [index, item] of parsed.entries()) {
|
||||
const result = normalizeBatchImportItem(item, `第 ${line.lineNumber} 行数组第 ${index + 1} 项`)
|
||||
if (!result.ok) return result
|
||||
normalized.push(result.value)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
const result = normalizeBatchImportItem(parsed, `第 ${line.lineNumber} 行`)
|
||||
if (!result.ok) return result
|
||||
normalized.push(result.value)
|
||||
continue
|
||||
}
|
||||
|
||||
normalized.push(line.text)
|
||||
}
|
||||
|
||||
return normalizeBatchImportArray(normalized)
|
||||
}
|
||||
|
||||
export function normalizeBatchImportCredentials(text: string): BatchImportCredentialsNormalization {
|
||||
const trimmed = text.trim()
|
||||
if (!trimmed) {
|
||||
return { ok: false, message: '请输入凭据数据' }
|
||||
}
|
||||
|
||||
const firstChar = trimmed[0]
|
||||
if (firstChar === '[') {
|
||||
try {
|
||||
const parsed: unknown = JSON.parse(trimmed)
|
||||
if (!Array.isArray(parsed)) {
|
||||
return { ok: false, message: 'JSON 批量凭据必须是数组' }
|
||||
}
|
||||
return normalizeBatchImportArray(parsed)
|
||||
} catch (error) {
|
||||
return { ok: false, message: `JSON 数组格式无效: ${jsonParseErrorMessage(error)}` }
|
||||
}
|
||||
}
|
||||
|
||||
if (firstChar === '{') {
|
||||
try {
|
||||
const parsed: unknown = JSON.parse(trimmed)
|
||||
if (typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed)) {
|
||||
return { ok: true, isBatch: false, credentials: trimmed }
|
||||
}
|
||||
return { ok: false, message: '单条 JSON 凭据必须是对象' }
|
||||
} catch (error) {
|
||||
const lines = getImportCredentialLines(trimmed)
|
||||
if (lines.length > 1) {
|
||||
return parseImportCredentialLines(lines)
|
||||
}
|
||||
return { ok: false, message: `JSON 格式无效: ${jsonParseErrorMessage(error)}` }
|
||||
}
|
||||
}
|
||||
|
||||
const lines = getImportCredentialLines(trimmed)
|
||||
if (lines.length > 1) {
|
||||
return parseImportCredentialLines(lines)
|
||||
}
|
||||
|
||||
return { ok: true, isBatch: false, credentials: trimmed }
|
||||
}
|
||||
|
||||
export async function refreshProviderOAuth(keyId: string): Promise<ProviderOAuthCompleteResponse> {
|
||||
const resp = await client.post(`/api/admin/provider-oauth/keys/${keyId}/refresh`)
|
||||
return resp.data
|
||||
@@ -102,8 +233,14 @@ export async function completeProviderLevelOAuth(
|
||||
export async function importProviderRefreshToken(
|
||||
providerId: string,
|
||||
data: {
|
||||
api_key?: string
|
||||
apiKey?: string
|
||||
token?: string
|
||||
auth_token?: string
|
||||
authToken?: string
|
||||
refresh_token?: string
|
||||
access_token?: string
|
||||
password?: string
|
||||
expires_at?: number
|
||||
name?: string
|
||||
proxy_node_id?: string
|
||||
@@ -150,7 +287,8 @@ export async function getBatchImportOAuthTaskStatus(
|
||||
export interface DeviceAuthorizeRequest {
|
||||
start_url?: string
|
||||
region?: string
|
||||
auth_type?: 'builder_id' | 'identity_center' | 'google' | 'github'
|
||||
auth_type?: 'builder_id' | 'identity_center' | 'google' | 'github' | 'browser'
|
||||
login_option?: 'google' | 'github' | 'default'
|
||||
redirect_uri?: string
|
||||
proxy_node_id?: string
|
||||
}
|
||||
@@ -170,6 +308,7 @@ export interface DeviceAuthorizeResponse {
|
||||
export interface DevicePollRequest {
|
||||
session_id: string
|
||||
callback_url?: string
|
||||
token?: string
|
||||
}
|
||||
|
||||
export interface DevicePollResponse {
|
||||
|
||||
@@ -5,6 +5,7 @@ import type {
|
||||
FailoverRulesConfig,
|
||||
PoolAdvancedConfig,
|
||||
ProviderConfig,
|
||||
ProviderType,
|
||||
ProviderWithEndpointsSummary,
|
||||
ProxyConfig,
|
||||
} from './types'
|
||||
@@ -92,7 +93,7 @@ export async function updateProvider(
|
||||
providerId: string,
|
||||
data: Partial<{
|
||||
name: string
|
||||
provider_type: 'custom' | 'vertex_ai' | 'claude_code' | 'codex' | 'chatgpt_web' | 'gemini_cli' | 'antigravity' | 'kiro' | 'grok'
|
||||
provider_type: ProviderType
|
||||
description: string | null
|
||||
website: string
|
||||
provider_priority: number
|
||||
@@ -127,7 +128,7 @@ export async function updateProvider(
|
||||
export async function createProvider(
|
||||
data: {
|
||||
name: string
|
||||
provider_type?: 'custom' | 'vertex_ai' | 'claude_code' | 'codex' | 'chatgpt_web' | 'gemini_cli' | 'antigravity' | 'kiro' | 'grok'
|
||||
provider_type?: ProviderType
|
||||
description?: string
|
||||
website?: string
|
||||
billing_type?: 'monthly_quota' | 'pay_as_you_go' | 'free_tier'
|
||||
|
||||
@@ -370,6 +370,32 @@ export interface KiroUpstreamMetadata {
|
||||
banned_at?: number // 封禁时间(Unix 时间戳,秒)
|
||||
}
|
||||
|
||||
// Windsurf 上游配额信息
|
||||
export interface WindsurfUpstreamMetadata {
|
||||
updated_at?: number
|
||||
plan_name?: string
|
||||
daily_remaining_percent?: number | null
|
||||
weekly_remaining_percent?: number | null
|
||||
daily_reset_at?: number | null
|
||||
weekly_reset_at?: number | null
|
||||
prompt_used?: number | null
|
||||
prompt_limit?: number | null
|
||||
prompt_remaining?: number | null
|
||||
flex_used?: number | null
|
||||
flex_limit?: number | null
|
||||
flex_remaining?: number | null
|
||||
allowed_models_count?: number | null
|
||||
models?: Array<{
|
||||
model_uid?: string | null
|
||||
label?: string | null
|
||||
provider?: string | null
|
||||
supports_images?: boolean | null
|
||||
credit_multiplier?: number | null
|
||||
}> | null
|
||||
rate_limit?: Record<string, unknown> | null
|
||||
last_error?: string | null
|
||||
}
|
||||
|
||||
export interface ChatGPTWebUpstreamMetadata {
|
||||
updated_at?: number // Unix 时间戳(秒)
|
||||
plan_type?: string | null
|
||||
@@ -406,6 +432,7 @@ export interface UpstreamMetadata {
|
||||
codex?: CodexUpstreamMetadata
|
||||
antigravity?: AntigravityUpstreamMetadata
|
||||
kiro?: KiroUpstreamMetadata
|
||||
windsurf?: WindsurfUpstreamMetadata
|
||||
chatgpt_web?: ChatGPTWebUpstreamMetadata
|
||||
grok?: GrokUpstreamMetadata
|
||||
}
|
||||
@@ -535,7 +562,7 @@ export interface PublicEndpointStatusMonitorResponse {
|
||||
formats: PublicEndpointStatusMonitor[]
|
||||
}
|
||||
|
||||
export type ProviderType = 'custom' | 'claude_code' | 'codex' | 'chatgpt_web' | 'gemini_cli' | 'antigravity' | 'kiro' | 'grok' | 'vertex_ai'
|
||||
export type ProviderType = 'custom' | 'claude_code' | 'codex' | 'chatgpt_web' | 'gemini_cli' | 'antigravity' | 'kiro' | 'grok' | 'windsurf' | 'vertex_ai'
|
||||
|
||||
export interface ClaudeCodeAdvancedConfig {
|
||||
// 会话数量控制:null/undefined 表示不限制
|
||||
|
||||
@@ -66,6 +66,8 @@ export interface QuotaStatusSnapshot {
|
||||
plan_type?: string | null
|
||||
pool_tier?: string | null
|
||||
credits?: QuotaCreditsSnapshot | null
|
||||
allowed_models_count?: number | null
|
||||
rate_limit?: Record<string, unknown> | null
|
||||
windows?: QuotaWindowSnapshot[] | null
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user