Download apps/server/dist/lib/error-handler.js from Leon4gr45/automaker: direct link, hf CLI and curl.
- Browser
- Download file 11.6 kB
-
https://huggingface.co/spaces/Leon4gr45/automaker/resolve/main/apps/server/dist/lib/error-handler.js
- Command line
-
hf download hf://spaces/Leon4gr45/automaker/apps/server/dist/lib/error-handler.js
-
curl -L -o error-handler.js https://huggingface.co/spaces/Leon4gr45/automaker/resolve/main/apps/server/dist/lib/error-handler.js
11.6 kB
| /** | |
| * Unified Error Handling System for CLI Providers | |
| * | |
| * Provides consistent error classification, user-friendly messages, and debugging support | |
| * across all AI providers (Claude, Codex, Cursor) | |
| */ | |
| import { createLogger } from '@automaker/utils'; | |
| const logger = createLogger('ErrorHandler'); | |
| export var ErrorType; | |
| (function (ErrorType) { | |
| ErrorType["AUTHENTICATION"] = "authentication"; | |
| ErrorType["BILLING"] = "billing"; | |
| ErrorType["RATE_LIMIT"] = "rate_limit"; | |
| ErrorType["NETWORK"] = "network"; | |
| ErrorType["TIMEOUT"] = "timeout"; | |
| ErrorType["VALIDATION"] = "validation"; | |
| ErrorType["PERMISSION"] = "permission"; | |
| ErrorType["CLI_NOT_FOUND"] = "cli_not_found"; | |
| ErrorType["CLI_NOT_INSTALLED"] = "cli_not_installed"; | |
| ErrorType["MODEL_NOT_SUPPORTED"] = "model_not_supported"; | |
| ErrorType["INVALID_REQUEST"] = "invalid_request"; | |
| ErrorType["SERVER_ERROR"] = "server_error"; | |
| ErrorType["UNKNOWN"] = "unknown"; | |
| })(ErrorType || (ErrorType = {})); | |
| export var ErrorSeverity; | |
| (function (ErrorSeverity) { | |
| ErrorSeverity["LOW"] = "low"; | |
| ErrorSeverity["MEDIUM"] = "medium"; | |
| ErrorSeverity["HIGH"] = "high"; | |
| ErrorSeverity["CRITICAL"] = "critical"; | |
| })(ErrorSeverity || (ErrorSeverity = {})); | |
| /** | |
| * Error patterns for different types of errors | |
| */ | |
| const ERROR_PATTERNS = [ | |
| // Authentication errors | |
| { | |
| type: ErrorType.AUTHENTICATION, | |
| severity: ErrorSeverity.HIGH, | |
| patterns: [ | |
| /unauthorized/i, | |
| /authentication.*fail/i, | |
| /invalid_api_key/i, | |
| /invalid api key/i, | |
| /not authenticated/i, | |
| /please.*log/i, | |
| /token.*revoked/i, | |
| /oauth.*error/i, | |
| /credentials.*invalid/i, | |
| ], | |
| userMessage: 'Authentication failed. Please check your API key or login credentials.', | |
| suggestedAction: "Verify your API key is correct and hasn't expired, or run the CLI login command.", | |
| retryable: false, | |
| }, | |
| // Billing errors | |
| { | |
| type: ErrorType.BILLING, | |
| severity: ErrorSeverity.HIGH, | |
| patterns: [ | |
| /credit.*balance.*low/i, | |
| /insufficient.*credit/i, | |
| /billing.*issue/i, | |
| /payment.*required/i, | |
| /usage.*exceeded/i, | |
| /quota.*exceeded/i, | |
| /add.*credit/i, | |
| ], | |
| userMessage: 'Account has insufficient credits or billing issues.', | |
| suggestedAction: 'Please add credits to your account or check your billing settings.', | |
| retryable: false, | |
| }, | |
| // Rate limit errors | |
| { | |
| type: ErrorType.RATE_LIMIT, | |
| severity: ErrorSeverity.MEDIUM, | |
| patterns: [ | |
| /rate.*limit/i, | |
| /too.*many.*request/i, | |
| /limit.*reached/i, | |
| /try.*later/i, | |
| /429/i, | |
| /reset.*time/i, | |
| /upgrade.*plan/i, | |
| ], | |
| userMessage: 'Rate limit reached. Please wait before trying again.', | |
| suggestedAction: 'Wait a few minutes before retrying, or consider upgrading your plan.', | |
| retryable: true, | |
| }, | |
| // Network errors | |
| { | |
| type: ErrorType.NETWORK, | |
| severity: ErrorSeverity.MEDIUM, | |
| patterns: [/network/i, /connection/i, /dns/i, /timeout/i, /econnrefused/i, /enotfound/i], | |
| userMessage: 'Network connection issue.', | |
| suggestedAction: 'Check your internet connection and try again.', | |
| retryable: true, | |
| }, | |
| // Timeout errors | |
| { | |
| type: ErrorType.TIMEOUT, | |
| severity: ErrorSeverity.MEDIUM, | |
| patterns: [/timeout/i, /aborted/i, /time.*out/i], | |
| userMessage: 'Operation timed out.', | |
| suggestedAction: 'Try again with a simpler request or check your connection.', | |
| retryable: true, | |
| }, | |
| // Permission errors | |
| { | |
| type: ErrorType.PERMISSION, | |
| severity: ErrorSeverity.HIGH, | |
| patterns: [/permission.*denied/i, /access.*denied/i, /forbidden/i, /403/i, /not.*authorized/i], | |
| userMessage: 'Permission denied.', | |
| suggestedAction: 'Check if you have the required permissions for this operation.', | |
| retryable: false, | |
| }, | |
| // CLI not found | |
| { | |
| type: ErrorType.CLI_NOT_FOUND, | |
| severity: ErrorSeverity.HIGH, | |
| patterns: [/command not found/i, /not recognized/i, /not.*installed/i, /ENOENT/i], | |
| userMessage: 'CLI tool not found.', | |
| suggestedAction: "Please install the required CLI tool and ensure it's in your PATH.", | |
| retryable: false, | |
| }, | |
| // Model not supported | |
| { | |
| type: ErrorType.MODEL_NOT_SUPPORTED, | |
| severity: ErrorSeverity.HIGH, | |
| patterns: [/model.*not.*support/i, /unknown.*model/i, /invalid.*model/i], | |
| userMessage: 'Model not supported.', | |
| suggestedAction: 'Check available models and use a supported one.', | |
| retryable: false, | |
| }, | |
| // Server errors | |
| { | |
| type: ErrorType.SERVER_ERROR, | |
| severity: ErrorSeverity.HIGH, | |
| patterns: [/internal.*server/i, /server.*error/i, /500/i, /502/i, /503/i, /504/i], | |
| userMessage: 'Server error occurred.', | |
| suggestedAction: 'Try again in a few minutes or contact support if the issue persists.', | |
| retryable: true, | |
| }, | |
| ]; | |
| /** | |
| * Classify an error into a specific type with user-friendly message | |
| */ | |
| export function classifyError(error, provider, context) { | |
| const errorText = getErrorText(error); | |
| // Try to match against known patterns | |
| for (const pattern of ERROR_PATTERNS) { | |
| for (const regex of pattern.patterns) { | |
| if (regex.test(errorText)) { | |
| return { | |
| type: pattern.type, | |
| severity: pattern.severity, | |
| userMessage: pattern.userMessage, | |
| technicalMessage: errorText, | |
| suggestedAction: pattern.suggestedAction, | |
| retryable: pattern.retryable, | |
| provider, | |
| context, | |
| }; | |
| } | |
| } | |
| } | |
| // Unknown error | |
| return { | |
| type: ErrorType.UNKNOWN, | |
| severity: ErrorSeverity.MEDIUM, | |
| userMessage: 'An unexpected error occurred.', | |
| technicalMessage: errorText, | |
| suggestedAction: 'Please try again or contact support if the issue persists.', | |
| retryable: true, | |
| provider, | |
| context, | |
| }; | |
| } | |
| /** | |
| * Get a user-friendly error message | |
| */ | |
| export function getUserFriendlyErrorMessage(error, provider) { | |
| const classification = classifyError(error, provider); | |
| let message = classification.userMessage; | |
| if (classification.suggestedAction) { | |
| message += ` ${classification.suggestedAction}`; | |
| } | |
| // Add provider-specific context if available | |
| if (provider) { | |
| message = `[${provider.toUpperCase()}] ${message}`; | |
| } | |
| return message; | |
| } | |
| /** | |
| * Check if an error is retryable | |
| */ | |
| export function isRetryableError(error) { | |
| const classification = classifyError(error); | |
| return classification.retryable; | |
| } | |
| /** | |
| * Check if an error is authentication-related | |
| */ | |
| export function isAuthenticationError(error) { | |
| const classification = classifyError(error); | |
| return classification.type === ErrorType.AUTHENTICATION; | |
| } | |
| /** | |
| * Check if an error is billing-related | |
| */ | |
| export function isBillingError(error) { | |
| const classification = classifyError(error); | |
| return classification.type === ErrorType.BILLING; | |
| } | |
| /** | |
| * Check if an error is rate limit related | |
| */ | |
| export function isRateLimitError(error) { | |
| const classification = classifyError(error); | |
| return classification.type === ErrorType.RATE_LIMIT; | |
| } | |
| /** | |
| * Get error text from various error types | |
| */ | |
| function getErrorText(error) { | |
| if (typeof error === 'string') { | |
| return error; | |
| } | |
| if (error instanceof Error) { | |
| return error.message; | |
| } | |
| if (typeof error === 'object' && error !== null) { | |
| // Handle structured error objects | |
| const errorObj = error; | |
| if (typeof errorObj.message === 'string') { | |
| return errorObj.message; | |
| } | |
| const nestedError = errorObj.error; | |
| if (typeof nestedError === 'object' && nestedError !== null && 'message' in nestedError) { | |
| return String(nestedError.message); | |
| } | |
| if (nestedError) { | |
| return typeof nestedError === 'string' ? nestedError : JSON.stringify(nestedError); | |
| } | |
| return JSON.stringify(error); | |
| } | |
| return String(error); | |
| } | |
| /** | |
| * Create a standardized error response | |
| */ | |
| export function createErrorResponse(error, provider, context) { | |
| const classification = classifyError(error, provider, context); | |
| return { | |
| success: false, | |
| error: classification.userMessage, | |
| errorType: classification.type, | |
| severity: classification.severity, | |
| retryable: classification.retryable, | |
| suggestedAction: classification.suggestedAction, | |
| }; | |
| } | |
| /** | |
| * Log error with full context | |
| */ | |
| export function logError(error, provider, operation, additionalContext) { | |
| const classification = classifyError(error, provider, { | |
| operation, | |
| ...additionalContext, | |
| }); | |
| logger.error(`Error in ${provider || 'unknown'}${operation ? ` during ${operation}` : ''}`, { | |
| type: classification.type, | |
| severity: classification.severity, | |
| message: classification.userMessage, | |
| technicalMessage: classification.technicalMessage, | |
| retryable: classification.retryable, | |
| suggestedAction: classification.suggestedAction, | |
| context: classification.context, | |
| }); | |
| } | |
| /** | |
| * Provider-specific error handlers | |
| */ | |
| export const ProviderErrorHandler = { | |
| claude: { | |
| classify: (error) => classifyError(error, 'claude'), | |
| getUserMessage: (error) => getUserFriendlyErrorMessage(error, 'claude'), | |
| isAuth: (error) => isAuthenticationError(error), | |
| isBilling: (error) => isBillingError(error), | |
| isRateLimit: (error) => isRateLimitError(error), | |
| }, | |
| codex: { | |
| classify: (error) => classifyError(error, 'codex'), | |
| getUserMessage: (error) => getUserFriendlyErrorMessage(error, 'codex'), | |
| isAuth: (error) => isAuthenticationError(error), | |
| isBilling: (error) => isBillingError(error), | |
| isRateLimit: (error) => isRateLimitError(error), | |
| }, | |
| cursor: { | |
| classify: (error) => classifyError(error, 'cursor'), | |
| getUserMessage: (error) => getUserFriendlyErrorMessage(error, 'cursor'), | |
| isAuth: (error) => isAuthenticationError(error), | |
| isBilling: (error) => isBillingError(error), | |
| isRateLimit: (error) => isRateLimitError(error), | |
| }, | |
| }; | |
| /** | |
| * Create a retry handler for retryable errors | |
| */ | |
| export function createRetryHandler(maxRetries = 3, baseDelay = 1000) { | |
| return async function (operation, shouldRetry = isRetryableError) { | |
| let lastError; | |
| for (let attempt = 0; attempt <= maxRetries; attempt++) { | |
| try { | |
| return await operation(); | |
| } | |
| catch (error) { | |
| lastError = error; | |
| if (attempt === maxRetries || !shouldRetry(error)) { | |
| throw error; | |
| } | |
| // Exponential backoff with jitter | |
| const delay = baseDelay * Math.pow(2, attempt) + Math.random() * 1000; | |
| logger.debug(`Retrying operation in ${delay}ms (attempt ${attempt + 1}/${maxRetries})`); | |
| await new Promise((resolve) => setTimeout(resolve, delay)); | |
| } | |
| } | |
| throw lastError; | |
| }; | |
| } | |
| //# sourceMappingURL=error-handler.js.map |