mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-01-30 06:12:03 +00:00
feat: add GitHub issue fix command and release command
- Introduced a new command for fetching and validating GitHub issues, allowing users to address issues directly from the command line. - Added a release command to bump the version of the application and build the Electron app, ensuring version consistency across UI and server packages. - Updated package.json files for both UI and server to version 0.7.1, reflecting the latest changes. - Implemented version utility in the server to read the version from package.json, enhancing version management across the application.
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
* Provides utilities for:
|
||||
* - Detecting abort/cancellation errors
|
||||
* - Detecting authentication errors
|
||||
* - Detecting rate limit and quota exhaustion errors
|
||||
* - Classifying errors by type
|
||||
* - Generating user-friendly error messages
|
||||
*/
|
||||
@@ -52,7 +53,7 @@ export function isAuthenticationError(errorMessage: string): boolean {
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an error is a rate limit error
|
||||
* Check if an error is a rate limit error (429 Too Many Requests)
|
||||
*
|
||||
* @param error - The error to check
|
||||
* @returns True if the error is a rate limit error
|
||||
@@ -62,6 +63,60 @@ export function isRateLimitError(error: unknown): boolean {
|
||||
return message.includes('429') || message.includes('rate_limit');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an error indicates quota/usage exhaustion
|
||||
* This includes session limits, weekly limits, credit/billing issues, and overloaded errors
|
||||
*
|
||||
* @param error - The error to check
|
||||
* @returns True if the error indicates quota exhaustion
|
||||
*/
|
||||
export function isQuotaExhaustedError(error: unknown): boolean {
|
||||
const message = error instanceof Error ? error.message : String(error || '');
|
||||
const lowerMessage = message.toLowerCase();
|
||||
|
||||
// Check for overloaded/capacity errors
|
||||
if (
|
||||
lowerMessage.includes('overloaded') ||
|
||||
lowerMessage.includes('overloaded_error') ||
|
||||
lowerMessage.includes('capacity')
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check for usage/quota limit patterns
|
||||
if (
|
||||
lowerMessage.includes('limit reached') ||
|
||||
lowerMessage.includes('usage limit') ||
|
||||
lowerMessage.includes('quota exceeded') ||
|
||||
lowerMessage.includes('quota_exceeded') ||
|
||||
lowerMessage.includes('session limit') ||
|
||||
lowerMessage.includes('weekly limit') ||
|
||||
lowerMessage.includes('monthly limit')
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check for billing/credit issues
|
||||
if (
|
||||
lowerMessage.includes('credit balance') ||
|
||||
lowerMessage.includes('insufficient credits') ||
|
||||
lowerMessage.includes('insufficient balance') ||
|
||||
lowerMessage.includes('no credits') ||
|
||||
lowerMessage.includes('out of credits') ||
|
||||
lowerMessage.includes('billing') ||
|
||||
lowerMessage.includes('payment required')
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check for upgrade prompts (often indicates limit reached)
|
||||
if (lowerMessage.includes('/upgrade') || lowerMessage.includes('extra-usage')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract retry-after duration from rate limit error
|
||||
*
|
||||
@@ -98,11 +153,15 @@ export function classifyError(error: unknown): ErrorInfo {
|
||||
const isAuth = isAuthenticationError(message);
|
||||
const isCancellation = isCancellationError(message);
|
||||
const isRateLimit = isRateLimitError(error);
|
||||
const isQuotaExhausted = isQuotaExhaustedError(error);
|
||||
const retryAfter = isRateLimit ? (extractRetryAfter(error) ?? 60) : undefined;
|
||||
|
||||
let type: ErrorType;
|
||||
if (isAuth) {
|
||||
type = 'authentication';
|
||||
} else if (isQuotaExhausted) {
|
||||
// Quota exhaustion takes priority over rate limit since it's more specific
|
||||
type = 'quota_exhausted';
|
||||
} else if (isRateLimit) {
|
||||
type = 'rate_limit';
|
||||
} else if (isAbort) {
|
||||
@@ -122,6 +181,7 @@ export function classifyError(error: unknown): ErrorInfo {
|
||||
isAuth,
|
||||
isCancellation,
|
||||
isRateLimit,
|
||||
isQuotaExhausted,
|
||||
retryAfter,
|
||||
originalError: error,
|
||||
};
|
||||
@@ -144,6 +204,10 @@ export function getUserFriendlyErrorMessage(error: unknown): string {
|
||||
return 'Authentication failed. Please check your API key.';
|
||||
}
|
||||
|
||||
if (info.isQuotaExhausted) {
|
||||
return 'Usage limit reached. Auto Mode has been paused. Please wait for your quota to reset or upgrade your plan.';
|
||||
}
|
||||
|
||||
if (info.isRateLimit) {
|
||||
const retryMsg = info.retryAfter
|
||||
? ` Please wait ${info.retryAfter} seconds before retrying.`
|
||||
|
||||
@@ -9,6 +9,7 @@ export {
|
||||
isCancellationError,
|
||||
isAuthenticationError,
|
||||
isRateLimitError,
|
||||
isQuotaExhaustedError,
|
||||
extractRetryAfter,
|
||||
classifyError,
|
||||
getUserFriendlyErrorMessage,
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
isCancellationError,
|
||||
isAuthenticationError,
|
||||
isRateLimitError,
|
||||
isQuotaExhaustedError,
|
||||
extractRetryAfter,
|
||||
classifyError,
|
||||
getUserFriendlyErrorMessage,
|
||||
@@ -129,6 +130,55 @@ describe('error-handler.ts', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('isQuotaExhaustedError', () => {
|
||||
it('should return true for overloaded errors', () => {
|
||||
expect(isQuotaExhaustedError(new Error('overloaded_error: service is busy'))).toBe(true);
|
||||
expect(isQuotaExhaustedError(new Error('Server is overloaded'))).toBe(true);
|
||||
expect(isQuotaExhaustedError(new Error('At capacity'))).toBe(true);
|
||||
});
|
||||
|
||||
it('should return true for usage limit errors', () => {
|
||||
expect(isQuotaExhaustedError(new Error('limit reached'))).toBe(true);
|
||||
expect(isQuotaExhaustedError(new Error('Usage limit exceeded'))).toBe(true);
|
||||
expect(isQuotaExhaustedError(new Error('quota exceeded'))).toBe(true);
|
||||
expect(isQuotaExhaustedError(new Error('quota_exceeded'))).toBe(true);
|
||||
expect(isQuotaExhaustedError(new Error('session limit reached'))).toBe(true);
|
||||
expect(isQuotaExhaustedError(new Error('weekly limit hit'))).toBe(true);
|
||||
expect(isQuotaExhaustedError(new Error('monthly limit reached'))).toBe(true);
|
||||
});
|
||||
|
||||
it('should return true for billing/credit errors', () => {
|
||||
expect(isQuotaExhaustedError(new Error('credit balance is too low'))).toBe(true);
|
||||
expect(isQuotaExhaustedError(new Error('insufficient credits'))).toBe(true);
|
||||
expect(isQuotaExhaustedError(new Error('insufficient balance'))).toBe(true);
|
||||
expect(isQuotaExhaustedError(new Error('no credits remaining'))).toBe(true);
|
||||
expect(isQuotaExhaustedError(new Error('out of credits'))).toBe(true);
|
||||
expect(isQuotaExhaustedError(new Error('billing issue detected'))).toBe(true);
|
||||
expect(isQuotaExhaustedError(new Error('payment required'))).toBe(true);
|
||||
});
|
||||
|
||||
it('should return true for upgrade prompts', () => {
|
||||
expect(isQuotaExhaustedError(new Error('Please /upgrade your plan'))).toBe(true);
|
||||
expect(isQuotaExhaustedError(new Error('extra-usage not enabled'))).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false for regular errors', () => {
|
||||
expect(isQuotaExhaustedError(new Error('Something went wrong'))).toBe(false);
|
||||
expect(isQuotaExhaustedError(new Error('Network error'))).toBe(false);
|
||||
expect(isQuotaExhaustedError(new Error(''))).toBe(false);
|
||||
});
|
||||
|
||||
it('should return false for null/undefined', () => {
|
||||
expect(isQuotaExhaustedError(null)).toBe(false);
|
||||
expect(isQuotaExhaustedError(undefined)).toBe(false);
|
||||
});
|
||||
|
||||
it('should handle string errors', () => {
|
||||
expect(isQuotaExhaustedError('overloaded')).toBe(true);
|
||||
expect(isQuotaExhaustedError('regular error')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('extractRetryAfter', () => {
|
||||
it('should extract retry-after from error message', () => {
|
||||
const error = new Error('Rate limit exceeded. retry-after: 60');
|
||||
@@ -170,10 +220,37 @@ describe('error-handler.ts', () => {
|
||||
expect(result.isAbort).toBe(false);
|
||||
expect(result.isCancellation).toBe(false);
|
||||
expect(result.isRateLimit).toBe(false);
|
||||
expect(result.isQuotaExhausted).toBe(false);
|
||||
expect(result.message).toBe('Authentication failed');
|
||||
expect(result.originalError).toBe(error);
|
||||
});
|
||||
|
||||
it('should classify quota exhausted errors', () => {
|
||||
const error = new Error('overloaded_error: service is busy');
|
||||
const result = classifyError(error);
|
||||
|
||||
expect(result.type).toBe('quota_exhausted');
|
||||
expect(result.isQuotaExhausted).toBe(true);
|
||||
expect(result.isRateLimit).toBe(false);
|
||||
expect(result.isAuth).toBe(false);
|
||||
});
|
||||
|
||||
it('should classify credit balance errors as quota exhausted', () => {
|
||||
const error = new Error('credit balance is too low');
|
||||
const result = classifyError(error);
|
||||
|
||||
expect(result.type).toBe('quota_exhausted');
|
||||
expect(result.isQuotaExhausted).toBe(true);
|
||||
});
|
||||
|
||||
it('should classify usage limit errors as quota exhausted', () => {
|
||||
const error = new Error('usage limit reached');
|
||||
const result = classifyError(error);
|
||||
|
||||
expect(result.type).toBe('quota_exhausted');
|
||||
expect(result.isQuotaExhausted).toBe(true);
|
||||
});
|
||||
|
||||
it('should classify rate limit errors', () => {
|
||||
const error = new Error('Error: 429 rate_limit_error');
|
||||
const result = classifyError(error);
|
||||
@@ -320,6 +397,14 @@ describe('error-handler.ts', () => {
|
||||
expect(message).toBe('Authentication failed. Please check your API key.');
|
||||
});
|
||||
|
||||
it('should return friendly message for quota exhausted errors', () => {
|
||||
const error = new Error('overloaded_error');
|
||||
const message = getUserFriendlyErrorMessage(error);
|
||||
|
||||
expect(message).toContain('Usage limit reached');
|
||||
expect(message).toContain('Auto Mode has been paused');
|
||||
});
|
||||
|
||||
it('should return friendly message for rate limit errors', () => {
|
||||
const error = new Error('429 rate_limit_error');
|
||||
const message = getUserFriendlyErrorMessage(error);
|
||||
|
||||
Reference in New Issue
Block a user