UNPKG

autoagent-cli

Version:

Run autonomous AI agents using Claude or Gemini for task execution

133 lines (132 loc) 4.61 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.retryDefaults = exports.RateLimitError = exports.RetryError = void 0; exports.retry = retry; exports.retryWithJitter = retryWithJitter; exports.createRetryableFunction = createRetryableFunction; exports.isRateLimitError = isRateLimitError; exports.isRateLimitOrUsageError = isRateLimitOrUsageError; exports.extractRetryAfter = extractRetryAfter; const logger_1 = require("./logger"); class RetryError extends Error { constructor(message, attempts, lastError) { super(message); this.attempts = attempts; this.lastError = lastError; this.name = 'RetryError'; } } exports.RetryError = RetryError; class RateLimitError extends Error { constructor(message, retryAfter) { super(message); this.retryAfter = retryAfter; this.name = 'RateLimitError'; } } exports.RateLimitError = RateLimitError; const defaultOptions = { maxAttempts: 3, initialDelay: 1000, maxDelay: 30000, backoffMultiplier: 2, shouldRetry: (error) => { if (error instanceof RateLimitError) { return true; } if (isRateLimitOrUsageError(error.message)) { return true; } const message = error.message.toLowerCase(); if (message.includes('not found') || message.includes('unauthorized') || message.includes('forbidden')) { return false; } if (error.name === 'NetworkError' || message.includes('timeout') || message.includes('connection')) { return true; } return true; }, onRetry: (error, attempt) => { logger_1.Logger.warning(`Retry attempt ${attempt}: ${error.message}`); } }; async function retry(fn, options) { const opts = { ...defaultOptions, ...options }; let lastError = null; for (let attempt = 1; attempt <= opts.maxAttempts; attempt++) { try { return await fn(); } catch (error) { lastError = error instanceof Error ? error : new Error(String(error)); if (!opts.shouldRetry(lastError)) { throw lastError; } if (attempt === opts.maxAttempts) { throw new RetryError(`Failed after ${opts.maxAttempts} attempts`, opts.maxAttempts, lastError); } let delay = opts.initialDelay * Math.pow(opts.backoffMultiplier, attempt - 1); if (lastError instanceof RateLimitError && lastError.retryAfter !== undefined && lastError.retryAfter !== null && lastError.retryAfter > 0) { delay = lastError.retryAfter * 1000; } delay = Math.min(delay, opts.maxDelay); opts.onRetry(lastError, attempt); await sleep(delay); } } throw new RetryError(`Failed after ${opts.maxAttempts} attempts`, opts.maxAttempts, lastError || new Error('Unknown error')); } async function retryWithJitter(fn, options) { const jitteredOptions = { ...options, onRetry: (error, attempt) => { options?.onRetry?.(error, attempt); } }; return retry(async () => { const jitterDelay = Math.random() * 100; await sleep(jitterDelay); return fn(); }, jitteredOptions); } function createRetryableFunction(fn, options) { return (async (...args) => { return retry(() => fn(...args), options); }); } function isRateLimitError(error) { if (error instanceof RateLimitError) { return true; } return isRateLimitOrUsageError(error.message); } function isRateLimitOrUsageError(message) { const lowercaseMessage = message.toLowerCase(); return lowercaseMessage.includes('rate limit') || lowercaseMessage.includes('usage limit') || lowercaseMessage.includes('too many requests') || lowercaseMessage.includes('quota exceeded'); } function extractRetryAfter(error) { if (error instanceof RateLimitError) { return error.retryAfter; } const match = error.message.match(/retry[- ]?after[: ]+(\d+)/i); if (match !== null && match[1] !== undefined && match[1] !== '') { return parseInt(match[1], 10); } return undefined; } function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } exports.retryDefaults = { maxAttempts: defaultOptions.maxAttempts, initialDelay: defaultOptions.initialDelay, maxDelay: defaultOptions.maxDelay, backoffMultiplier: defaultOptions.backoffMultiplier, };