mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 17:30:23 +08:00
fix(kiro,pool,model): 对齐 Kiro 管理链路并修复全局模型删除行为 (#305)
* feat(pool): 号池支持跳过额度耗尽账号 - 新增 pool_advanced.skip_exhausted_accounts 开关及高级设置 UI, 默认关闭并兼容旧配置 - 为 Codex/Kiro 增加额度耗尽判定, 接入请求侧候选跳过并新增 account_quota_exhausted skip reason - 号池列表将额度耗尽账号标记为 blocked/额度耗尽, 并补充前后端相关测试 * fix(kiro): 对齐账号管理与 provider-query 的 Rust 行为 - 修复 Kiro 单条导入误走 import-refresh-token 的前端分流, 并为误用路径返回明确错误提示 - 为 Kiro 导入与本地请求链补齐 bearer 兼容, 同步放开账号启停等 Key 更新操作的 auth_type 校验 - 实现 Kiro provider-query 本地模型测试与 failover 执行链, 并修复结果弹窗在无 trace 时无法展示 attempts/响应体的问题 * fix(model): 删除全局模型时级联清理关联提供商模型 - 对齐 Python 版本删除逻辑, GlobalModel 删除前先在事务内清理关联的 Provider Model 记录 - 修复已绑定 Provider 的模型在 Rust SQL 仓库下会被外键约束拦住、无法正常删除的问题 - 增加管理端回归测试, 覆盖绑定 Provider Model 的 GlobalModel 删除场景 * fix(kiro,ci): 恢复 Kiro OAuth 持久化并修复 Rust CI * Fix oauth-managed provider key semantics --------- Co-authored-by: fawney19 <elky0401@gmail.com>
This commit is contained in:
@@ -104,4 +104,24 @@ describe('providerKeyStatus', () => {
|
||||
},
|
||||
}, 0)).toContain('Token 剩余有效期:')
|
||||
})
|
||||
|
||||
it('treats oauth_managed bearer credentials as oauth for legacy countdown fallback', () => {
|
||||
const future = Math.floor(Date.now() / 1000) + 2 * 24 * 3600
|
||||
const status = getOAuthStatusDisplay(
|
||||
{
|
||||
auth_type: 'bearer',
|
||||
oauth_managed: true,
|
||||
oauth_expires_at: future,
|
||||
},
|
||||
0,
|
||||
)
|
||||
|
||||
expect(status).not.toBeNull()
|
||||
expect(status?.isExpired).toBe(false)
|
||||
expect(getOAuthStatusTitle({
|
||||
auth_type: 'bearer',
|
||||
oauth_managed: true,
|
||||
oauth_expires_at: future,
|
||||
}, 0)).toContain('Token 剩余有效期:')
|
||||
})
|
||||
})
|
||||
|
||||
100
frontend/src/utils/providerKeyAuth.ts
Normal file
100
frontend/src/utils/providerKeyAuth.ts
Normal file
@@ -0,0 +1,100 @@
|
||||
export interface ProviderKeyAuthCarrier {
|
||||
auth_type?: string | null
|
||||
credential_kind?: string | null
|
||||
runtime_auth_kind?: string | null
|
||||
oauth_managed?: boolean | null
|
||||
can_refresh_oauth?: boolean | null
|
||||
can_export_oauth?: boolean | null
|
||||
can_edit_oauth?: boolean | null
|
||||
}
|
||||
|
||||
function normalizeText(value: unknown): string | null {
|
||||
if (typeof value !== 'string') return null
|
||||
const text = value.trim().toLowerCase()
|
||||
return text || null
|
||||
}
|
||||
|
||||
export function getProviderCredentialKind(
|
||||
input: ProviderKeyAuthCarrier,
|
||||
): 'raw_secret' | 'oauth_session' | 'service_account' {
|
||||
const credentialKind = normalizeText(input.credential_kind)
|
||||
if (
|
||||
credentialKind === 'raw_secret'
|
||||
|| credentialKind === 'oauth_session'
|
||||
|| credentialKind === 'service_account'
|
||||
) {
|
||||
return credentialKind
|
||||
}
|
||||
|
||||
if (typeof input.oauth_managed === 'boolean') {
|
||||
return input.oauth_managed ? 'oauth_session' : 'raw_secret'
|
||||
}
|
||||
|
||||
const authType = normalizeText(input.auth_type)
|
||||
if (authType === 'oauth') return 'oauth_session'
|
||||
if (authType === 'service_account' || authType === 'vertex_ai') return 'service_account'
|
||||
return 'raw_secret'
|
||||
}
|
||||
|
||||
export function getProviderRuntimeAuthKind(
|
||||
input: ProviderKeyAuthCarrier,
|
||||
): 'api_key' | 'bearer' | 'service_account' | 'unknown' {
|
||||
const runtimeAuthKind = normalizeText(input.runtime_auth_kind)
|
||||
if (
|
||||
runtimeAuthKind === 'api_key'
|
||||
|| runtimeAuthKind === 'bearer'
|
||||
|| runtimeAuthKind === 'service_account'
|
||||
) {
|
||||
return runtimeAuthKind
|
||||
}
|
||||
|
||||
const authType = normalizeText(input.auth_type)
|
||||
if (authType === 'service_account' || authType === 'vertex_ai') return 'service_account'
|
||||
if (authType === 'bearer') return 'bearer'
|
||||
if (authType === 'api_key') return 'api_key'
|
||||
return 'unknown'
|
||||
}
|
||||
|
||||
export function isOAuthManagedCredential(input: ProviderKeyAuthCarrier): boolean {
|
||||
if (typeof input.oauth_managed === 'boolean') {
|
||||
return input.oauth_managed
|
||||
}
|
||||
return getProviderCredentialKind(input) === 'oauth_session'
|
||||
}
|
||||
|
||||
export function isServiceAccountCredential(input: ProviderKeyAuthCarrier): boolean {
|
||||
return getProviderCredentialKind(input) === 'service_account'
|
||||
}
|
||||
|
||||
export function canRefreshOAuthCredential(input: ProviderKeyAuthCarrier): boolean {
|
||||
if (typeof input.can_refresh_oauth === 'boolean') {
|
||||
return input.can_refresh_oauth
|
||||
}
|
||||
return isOAuthManagedCredential(input)
|
||||
}
|
||||
|
||||
export function canExportOAuthCredential(input: ProviderKeyAuthCarrier): boolean {
|
||||
if (typeof input.can_export_oauth === 'boolean') {
|
||||
return input.can_export_oauth
|
||||
}
|
||||
return isOAuthManagedCredential(input)
|
||||
}
|
||||
|
||||
export function canEditOAuthCredential(input: ProviderKeyAuthCarrier): boolean {
|
||||
if (typeof input.can_edit_oauth === 'boolean') {
|
||||
return input.can_edit_oauth
|
||||
}
|
||||
return isOAuthManagedCredential(input)
|
||||
}
|
||||
|
||||
export function getProviderAuthLabel(input: ProviderKeyAuthCarrier): string {
|
||||
if (isOAuthManagedCredential(input)) return 'OAuth'
|
||||
if (isServiceAccountCredential(input)) return '服务账号'
|
||||
return getProviderRuntimeAuthKind(input) === 'bearer' ? 'Bearer' : 'API Key'
|
||||
}
|
||||
|
||||
export function getProviderMaskedSecretLabel(input: ProviderKeyAuthCarrier): string {
|
||||
if (isOAuthManagedCredential(input)) return '[OAuth Token]'
|
||||
if (isServiceAccountCredential(input)) return '[Service Account]'
|
||||
return getProviderRuntimeAuthKind(input) === 'bearer' ? '[Bearer Token]' : '[Key]'
|
||||
}
|
||||
@@ -6,9 +6,12 @@ import {
|
||||
isAccountLevelBlockReason,
|
||||
isRefreshFailedReason,
|
||||
} from './accountBlock'
|
||||
import {
|
||||
isOAuthManagedCredential,
|
||||
type ProviderKeyAuthCarrier,
|
||||
} from './providerKeyAuth'
|
||||
|
||||
export interface ProviderKeyStatusCarrier {
|
||||
auth_type?: string | null
|
||||
export interface ProviderKeyStatusCarrier extends ProviderKeyAuthCarrier {
|
||||
oauth_expires_at?: number | null
|
||||
oauth_invalid_at?: number | null // compatibility only
|
||||
oauth_invalid_reason?: string | null // compatibility only
|
||||
@@ -107,7 +110,7 @@ function getLegacyOAuthState(
|
||||
input: ProviderKeyStatusCarrier,
|
||||
tick: number,
|
||||
): OAuthStatusInfo | null {
|
||||
if (normalizeText(input.auth_type) !== 'oauth') return null
|
||||
if (!isOAuthManagedCredential(input)) return null
|
||||
if (!input.oauth_expires_at && !input.oauth_invalid_at && !input.oauth_invalid_reason) return null
|
||||
|
||||
const rawReason = normalizeText(input.oauth_invalid_reason)
|
||||
|
||||
Reference in New Issue
Block a user