Merge pull request #11: feat: add Kiro quota display support

新增 Kiro (AWS CodeWhisperer) 额度查看功能:
- 配额页面新增 Kiro 额度区块
- 显示基础额度、赠送额度、合计额度
- 显示订阅类型和重置时间
- 支持单个刷新和批量获取

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
kongkongyo
2026-02-02 21:43:04 +08:00
11 changed files with 460 additions and 11 deletions

View File

@@ -38,6 +38,10 @@ export const TYPE_COLORS: Record<string, TypeColorSet> = {
light: { bg: '#e0f7fa', text: '#006064' },
dark: { bg: '#004d40', text: '#80deea' },
},
kiro: {
light: { bg: '#fff8e1', text: '#ff8f00' },
dark: { bg: '#ff6f00', text: '#ffe082' },
},
iflow: {
light: { bg: '#f3e5f5', text: '#7b1fa2' },
dark: { bg: '#4a148c', text: '#ce93d8' },
@@ -149,3 +153,15 @@ export const CODEX_REQUEST_HEADERS = {
'Content-Type': 'application/json',
'User-Agent': 'codex_cli_rs/0.76.0 (Debian 13.0.0; x86_64) WindowsTerminal',
};
// Kiro (AWS CodeWhisperer) API configuration
export const KIRO_QUOTA_URL =
'https://codewhisperer.us-east-1.amazonaws.com/getUsageLimits?isEmailRequired=true&origin=AI_EDITOR&resourceType=AGENTIC_REQUEST';
export const KIRO_REQUEST_HEADERS = {
Authorization: 'Bearer $TOKEN$',
'x-amz-user-agent': 'aws-sdk-js/1.0.0 KiroIDE-0.6.18-cpamc',
'User-Agent': 'aws-sdk-js/1.0.0 ua/2.1 os/windows lang/js md/nodejs#20.16.0 api/codewhispererruntime#1.0.0 m/E KiroIDE-0.6.18-cpamc',
'amz-sdk-request': 'attempt=1; max=1',
Connection: 'close',
};

View File

@@ -2,7 +2,7 @@
* Normalization and parsing functions for quota data.
*/
import type { CodexUsagePayload, GeminiCliQuotaPayload } from '@/types';
import type { CodexUsagePayload, GeminiCliQuotaPayload, KiroQuotaPayload } from '@/types';
export function normalizeAuthIndexValue(value: unknown): string | null {
if (typeof value === 'number' && Number.isFinite(value)) {
@@ -151,3 +151,20 @@ export function parseGeminiCliQuotaPayload(payload: unknown): GeminiCliQuotaPayl
}
return null;
}
export function parseKiroQuotaPayload(payload: unknown): KiroQuotaPayload | null {
if (payload === undefined || payload === null) return null;
if (typeof payload === 'string') {
const trimmed = payload.trim();
if (!trimmed) return null;
try {
return JSON.parse(trimmed) as KiroQuotaPayload;
} catch {
return null;
}
}
if (typeof payload === 'object') {
return payload as KiroQuotaPayload;
}
return null;
}

View File

@@ -22,6 +22,10 @@ export function isGeminiCliFile(file: AuthFileItem): boolean {
return resolveAuthProvider(file) === 'gemini-cli';
}
export function isKiroFile(file: AuthFileItem): boolean {
return resolveAuthProvider(file) === 'kiro';
}
export function isRuntimeOnlyAuthFile(file: AuthFileItem): boolean {
const raw = file['runtime_only'] ?? file.runtimeOnly;
if (typeof raw === 'boolean') return raw;