feat(provider): 原生接入 Windsurf provider

This commit is contained in:
Entropy.Xu
2026-05-18 15:23:08 +08:00
parent 923515ab28
commit 0226e14251
51 changed files with 7470 additions and 157 deletions

View File

@@ -104,4 +104,74 @@ describe('providerKeyQuota', () => {
},
}, 'grok')).toBe('Auto剩余 40.0% (60/150) | Heavy剩余 0.0% (0/20)')
})
it('surfaces Windsurf hard account states', () => {
expect(getQuotaDisplayText({
status_snapshot: {
quota: {
provider_type: 'windsurf',
code: 'quarantined',
label: '账号隔离中',
exhausted: false,
},
},
}, 'windsurf')).toBe('账号隔离中')
expect(getQuotaDisplayText({
status_snapshot: {
quota: {
provider_type: 'windsurf',
code: 'cooldown',
label: '冷却中',
exhausted: false,
},
},
}, 'windsurf')).toBe('冷却中')
})
it('includes Windsurf quota windows and model availability in display text', () => {
expect(getQuotaDisplayText({
status_snapshot: {
quota: {
provider_type: 'windsurf',
code: 'ok',
exhausted: false,
allowed_models_count: 7,
windows: [
{
code: 'daily',
remaining_ratio: 0.75,
},
{
code: 'weekly',
remaining_ratio: 0.5,
},
{
code: 'prompt',
remaining_value: 12,
limit_value: 20,
},
{
code: 'flex',
used_value: 2,
limit_value: 5,
},
],
},
},
}, 'windsurf')).toBe('日剩余 75.0% | 周剩余 50.0% | Prompt 剩余 12/20 | Flex 剩余 3/5 | 可用模型 7 个')
})
it('uses Windsurf model availability when no quota window is present', () => {
expect(getQuotaDisplayText({
status_snapshot: {
quota: {
provider_type: 'windsurf',
code: 'ok',
exhausted: false,
allowed_models_count: 3,
},
},
}, 'windsurf')).toBe('可用模型 3 个')
})
})

View File

@@ -214,6 +214,53 @@ function getGrokQuotaText(quota: QuotaStatusSnapshot): string | null {
return normalizeText(quota.label)
}
function getWindsurfQuotaText(quota: QuotaStatusSnapshot): string | null {
const code = normalizeText(quota.code)?.toLowerCase()
if (code === 'banned' || code === 'forbidden' || code === 'quarantined') {
return normalizeText(quota.label) || '账号不可用'
}
if (code === 'rate_limited' || code === 'rate_limit' || code === 'cooldown') {
return normalizeText(quota.label) || '速率受限'
}
if (code === 'exhausted') {
return normalizeText(quota.label) || '额度已耗尽'
}
const parts: string[] = []
const dailyRemaining = getQuotaWindowRemainingPercent(getQuotaWindow(quota, 'daily'))
const weeklyRemaining = getQuotaWindowRemainingPercent(getQuotaWindow(quota, 'weekly'))
if (dailyRemaining != null) parts.push(`日剩余 ${formatPercent(dailyRemaining)}`)
if (weeklyRemaining != null) parts.push(`周剩余 ${formatPercent(weeklyRemaining)}`)
for (const [label, code] of [
['Prompt', 'prompt'],
['Flex', 'flex'],
] as const) {
const window = getQuotaWindow(quota, code)
if (!window) continue
if (typeof window.remaining_value === 'number' && typeof window.limit_value === 'number' && window.limit_value > 0) {
parts.push(`${label} 剩余 ${formatQuotaValue(window.remaining_value)}/${formatQuotaValue(window.limit_value)}`)
continue
}
if (typeof window.used_value === 'number' && typeof window.limit_value === 'number' && window.limit_value > 0) {
parts.push(`${label} 剩余 ${formatQuotaValue(Math.max(window.limit_value - window.used_value, 0))}/${formatQuotaValue(window.limit_value)}`)
continue
}
const remainingPercent = getQuotaWindowRemainingPercent(window)
if (remainingPercent != null) {
parts.push(`${label} 剩余 ${formatPercent(remainingPercent)}`)
}
}
if (typeof quota.allowed_models_count === 'number') {
parts.push(`可用模型 ${quota.allowed_models_count}`)
}
if (parts.length > 0) return parts.join(' | ')
return normalizeText(quota.label)
}
function getAntigravityQuotaText(quota: QuotaStatusSnapshot): string | null {
const code = normalizeText(quota.code)?.toLowerCase()
if (code === 'forbidden') {
@@ -312,6 +359,8 @@ export function getQuotaSnapshotFallbackText(
return getKiroQuotaText(quota)
case 'grok':
return getGrokQuotaText(quota)
case 'windsurf':
return getWindsurfQuotaText(quota)
case 'antigravity':
return getAntigravityQuotaText(quota)
case 'gemini_cli':