From 3efb1734f158d36f70797169baa56757ba4135f8 Mon Sep 17 00:00:00 2001 From: Arnaud Jeansen Date: Fri, 7 Nov 2025 11:33:33 +0100 Subject: [PATCH] Add proactive rate limit checking to prevent API exhaustion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implements proactive rate limit monitoring that checks GitHub API status before processing each repository. When remaining requests drop to 500 or below (10% of the 5000/hour limit), the tool pauses and waits until the rate limit resets. Benefits: - Prevents hitting rate limits instead of reacting to errors - Single long wait instead of many failed API calls - Reduces wasted API calls and improves reliability - Clear user feedback showing wait time and reset schedule - Configurable threshold (default 500 requests = 10%) Implementation: - checkRateLimit(): Queries GitHub API for current rate limit status - checkAndWaitIfNearLimit(): Pauses execution if threshold is reached - Added before each repository in analyzeAllRepositories() - Added before each repository in analyzePublicRepositories() The tool displays clear messages during rate limit waits: "ā³ Rate limit low (450 requests remaining). Waiting until 3:45 PM (15 minutes)..." "āœ“ Rate limit reset. Resuming operations..." šŸ¤– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- src/analyzer.ts | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/src/analyzer.ts b/src/analyzer.ts index 6d1c02b..6842e08 100644 --- a/src/analyzer.ts +++ b/src/analyzer.ts @@ -10,6 +10,43 @@ class GitHubArtifactsAnalyzer { }); } + private async checkRateLimit() { + try { + const { data: rateLimit } = await this.octokit.rateLimit.get(); + const core = rateLimit.resources.core; + + return { + limited: core.remaining === 0, + resetAt: new Date(core.reset * 1000), + remaining: core.remaining + }; + } catch (error) { + // If we can't check rate limits, assume not limited + return { limited: false }; + } + } + + private async checkAndWaitIfNearLimit(threshold = 500) { + const rateLimitStatus = await this.checkRateLimit(); + + if (rateLimitStatus.remaining !== undefined && + rateLimitStatus.remaining <= threshold && + rateLimitStatus.resetAt) { + const now = new Date(); + const waitMs = rateLimitStatus.resetAt.getTime() - now.getTime(); + + if (waitMs > 0) { + const waitMinutes = Math.ceil(waitMs / 60000); + console.log(chalk.yellow( + `\nā³ Rate limit low (${rateLimitStatus.remaining} requests remaining). ` + + `Waiting until ${rateLimitStatus.resetAt.toLocaleTimeString()} (${waitMinutes} minute${waitMinutes !== 1 ? 's' : ''})...` + )); + await this.sleep(waitMs + 1000); // Add 1 second buffer + console.log(chalk.green(`āœ“ Rate limit reset. Resuming operations...\n`)); + } + } + } + async analyzeAllRepositories(username, options = { includeExpired: false, minSize: 0 }) { // Get authenticated user if no username provided if (!username) { @@ -44,6 +81,9 @@ class GitHubArtifactsAnalyzer { // Process repositories in batches to avoid rate limiting for (const repo of userRepos) { + // Proactively check rate limit before processing each repository + await this.checkAndWaitIfNearLimit(); + console.log(chalk.gray(` Checking ${repo.full_name}${repo.private ? ' (private)' : ''}...`)); try { const analysis = await this.analyzeRepository(repo.owner.login, repo.name, options); @@ -98,6 +138,9 @@ class GitHubArtifactsAnalyzer { } else { // Process repositories in batches to avoid rate limiting for (const repo of repos) { + // Proactively check rate limit before processing each repository + await this.checkAndWaitIfNearLimit(); + console.log(chalk.gray(` Checking ${repo.full_name}...`)); try { const analysis = await this.analyzeRepository(repo.owner.login, repo.name, options);