fix: stabilize e2e flow in clean CI env

Signed-off-by: majiayu000 <majiayu000@users.noreply.github.com>
This commit is contained in:
majiayu000
2026-03-27 17:19:31 +08:00
parent bc608e97bd
commit 83f9193749
11 changed files with 146 additions and 92 deletions

View File

@@ -3,15 +3,18 @@ import { MODEL_LIST, OCO_AI_PROVIDER_ENUM } from '../commands/config';
// Provider billing/help URLs for common errors
export const PROVIDER_BILLING_URLS: Record<string, string | null> = {
[OCO_AI_PROVIDER_ENUM.ANTHROPIC]: 'https://console.anthropic.com/settings/billing',
[OCO_AI_PROVIDER_ENUM.OPENAI]: 'https://platform.openai.com/settings/organization/billing',
[OCO_AI_PROVIDER_ENUM.ANTHROPIC]:
'https://console.anthropic.com/settings/billing',
[OCO_AI_PROVIDER_ENUM.OPENAI]:
'https://platform.openai.com/settings/organization/billing',
[OCO_AI_PROVIDER_ENUM.GEMINI]: 'https://aistudio.google.com/app/plan',
[OCO_AI_PROVIDER_ENUM.GROQ]: 'https://console.groq.com/settings/billing',
[OCO_AI_PROVIDER_ENUM.MISTRAL]: 'https://console.mistral.ai/billing/',
[OCO_AI_PROVIDER_ENUM.DEEPSEEK]: 'https://platform.deepseek.com/usage',
[OCO_AI_PROVIDER_ENUM.OPENROUTER]: 'https://openrouter.ai/credits',
[OCO_AI_PROVIDER_ENUM.AIMLAPI]: 'https://aimlapi.com/app/billing',
[OCO_AI_PROVIDER_ENUM.AZURE]: 'https://portal.azure.com/#view/Microsoft_Azure_CostManagement',
[OCO_AI_PROVIDER_ENUM.AZURE]:
'https://portal.azure.com/#view/Microsoft_Azure_CostManagement',
[OCO_AI_PROVIDER_ENUM.OLLAMA]: null,
[OCO_AI_PROVIDER_ENUM.MLX]: null,
[OCO_AI_PROVIDER_ENUM.FLOWISE]: null,
@@ -23,7 +26,9 @@ export class InsufficientCreditsError extends Error {
public readonly provider: string;
constructor(provider: string, message?: string) {
super(message || `Insufficient credits or quota for provider '${provider}'`);
super(
message || `Insufficient credits or quota for provider '${provider}'`
);
this.name = 'InsufficientCreditsError';
this.provider = provider;
}
@@ -345,7 +350,10 @@ export interface FormattedError {
}
// Format an error into a user-friendly structure
export function formatUserFriendlyError(error: unknown, provider: string): FormattedError {
export function formatUserFriendlyError(
error: unknown,
provider: string
): FormattedError {
const billingUrl = PROVIDER_BILLING_URLS[provider] || null;
// Handle our custom error types first
@@ -460,7 +468,9 @@ export function printFormattedError(formatted: FormattedError): string {
output += ` ${formatted.message}\n`;
if (formatted.helpUrl) {
output += `\n ${chalk.cyan('Help:')} ${chalk.underline(formatted.helpUrl)}\n`;
output += `\n ${chalk.cyan('Help:')} ${chalk.underline(
formatted.helpUrl
)}\n`;
}
if (formatted.suggestion) {

View File

@@ -125,9 +125,7 @@ export async function fetchMistralModels(apiKey: string): Promise<string[]> {
}
const data = await response.json();
const models = data.data
?.map((m: { id: string }) => m.id)
.sort();
const models = data.data?.map((m: { id: string }) => m.id).sort();
return models && models.length > 0 ? models : MODEL_LIST.mistral;
} catch {
@@ -148,9 +146,7 @@ export async function fetchGroqModels(apiKey: string): Promise<string[]> {
}
const data = await response.json();
const models = data.data
?.map((m: { id: string }) => m.id)
.sort();
const models = data.data?.map((m: { id: string }) => m.id).sort();
return models && models.length > 0 ? models : MODEL_LIST.groq;
} catch {
@@ -173,8 +169,9 @@ export async function fetchOpenRouterModels(apiKey: string): Promise<string[]> {
const data = await response.json();
// Filter to text-capable models only (exclude image/audio models)
const models = data.data
?.filter((m: { id: string; context_length?: number }) =>
m.context_length && m.context_length > 0
?.filter(
(m: { id: string; context_length?: number }) =>
m.context_length && m.context_length > 0
)
.map((m: { id: string }) => m.id)
.sort();
@@ -198,9 +195,7 @@ export async function fetchDeepSeekModels(apiKey: string): Promise<string[]> {
}
const data = await response.json();
const models = data.data
?.map((m: { id: string }) => m.id)
.sort();
const models = data.data?.map((m: { id: string }) => m.id).sort();
return models && models.length > 0 ? models : MODEL_LIST.deepseek;
} catch {
@@ -312,7 +307,10 @@ export function clearModelCache(): void {
}
}
export function getCacheInfo(): { timestamp: number | null; providers: string[] } {
export function getCacheInfo(): {
timestamp: number | null;
providers: string[];
} {
const cache = readCache();
if (!cache) {
return { timestamp: null, providers: [] };