@oss-autopilot/core 0.44.2 → 0.44.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.bundle.cjs +93 -94
- package/dist/cli.bundle.cjs.map +4 -4
- package/dist/commands/daily.js +3 -60
- package/dist/commands/dashboard-data.d.ts +13 -0
- package/dist/commands/dashboard-data.js +56 -56
- package/dist/commands/dashboard-server.js +60 -29
- package/dist/commands/dashboard-templates.js +1 -1
- package/dist/core/checklist-analysis.js +3 -1
- package/dist/core/errors.d.ts +8 -0
- package/dist/core/errors.js +26 -0
- package/dist/core/index.d.ts +2 -2
- package/dist/core/index.js +2 -2
- package/dist/core/issue-conversation.js +2 -2
- package/dist/core/issue-discovery.d.ts +0 -5
- package/dist/core/issue-discovery.js +4 -11
- package/dist/core/issue-vetting.d.ts +0 -2
- package/dist/core/issue-vetting.js +30 -44
- package/dist/core/pr-monitor.js +3 -4
- package/dist/core/utils.d.ts +2 -0
- package/dist/core/utils.js +5 -1
- package/package.json +1 -1
package/dist/core/index.js
CHANGED
|
@@ -8,8 +8,8 @@ export { IssueDiscovery, isDocOnlyIssue, applyPerRepoCap, DOC_ONLY_LABELS, } fro
|
|
|
8
8
|
export { IssueConversationMonitor } from './issue-conversation.js';
|
|
9
9
|
export { isBotAuthor, isAcknowledgmentComment } from './comment-utils.js';
|
|
10
10
|
export { getOctokit, checkRateLimit } from './github.js';
|
|
11
|
-
export { parseGitHubUrl, daysBetween, splitRepo, isOwnRepo, getCLIVersion, getDataDir, getStatePath, getBackupDir, getCacheDir, getDashboardPath, formatRelativeTime, byDateDescending, getGitHubToken, getGitHubTokenAsync, requireGitHubToken, resetGitHubTokenCache, } from './utils.js';
|
|
12
|
-
export { OssAutopilotError, ConfigurationError, ValidationError, errorMessage, getHttpStatusCode } from './errors.js';
|
|
11
|
+
export { parseGitHubUrl, daysBetween, splitRepo, isOwnRepo, getCLIVersion, getDataDir, getStatePath, getBackupDir, getCacheDir, getDashboardPath, formatRelativeTime, byDateDescending, getGitHubToken, getGitHubTokenAsync, requireGitHubToken, resetGitHubTokenCache, DEFAULT_CONCURRENCY, } from './utils.js';
|
|
12
|
+
export { OssAutopilotError, ConfigurationError, ValidationError, errorMessage, getHttpStatusCode, isRateLimitError, isRateLimitOrAuthError, } from './errors.js';
|
|
13
13
|
export { enableDebug, isDebugEnabled, debug, info, warn, timed } from './logger.js';
|
|
14
14
|
export { HttpCache, getHttpCache, cachedRequest } from './http-cache.js';
|
|
15
15
|
export { CRITICAL_STATUSES, computeRepoSignals, groupPRsByRepo, assessCapacity, collectActionableIssues, computeActionMenu, toShelvedPRRef, formatActionHint, formatBriefSummary, formatSummary, printDigest, } from './daily-logic.js';
|
|
@@ -9,12 +9,12 @@ import { getOctokit } from './github.js';
|
|
|
9
9
|
import { isBotAuthor, isAcknowledgmentComment } from './comment-utils.js';
|
|
10
10
|
import { paginateAll } from './pagination.js';
|
|
11
11
|
import { getStateManager } from './state.js';
|
|
12
|
-
import { daysBetween, splitRepo, extractOwnerRepo, isOwnRepo } from './utils.js';
|
|
12
|
+
import { daysBetween, splitRepo, extractOwnerRepo, isOwnRepo, DEFAULT_CONCURRENCY } from './utils.js';
|
|
13
13
|
import { runWorkerPool } from './concurrency.js';
|
|
14
14
|
import { ConfigurationError, errorMessage } from './errors.js';
|
|
15
15
|
import { debug, warn } from './logger.js';
|
|
16
16
|
const MODULE = 'issue-conversation';
|
|
17
|
-
const MAX_CONCURRENT_REQUESTS =
|
|
17
|
+
const MAX_CONCURRENT_REQUESTS = DEFAULT_CONCURRENCY;
|
|
18
18
|
/** Associations that indicate someone with repo-level permissions. */
|
|
19
19
|
const MAINTAINER_ASSOCIATIONS = new Set(['OWNER', 'MEMBER', 'COLLABORATOR']);
|
|
20
20
|
export class IssueConversationMonitor {
|
|
@@ -65,11 +65,6 @@ export declare class IssueDiscovery {
|
|
|
65
65
|
* Split repos into batches of the specified size.
|
|
66
66
|
*/
|
|
67
67
|
private batchRepos;
|
|
68
|
-
/**
|
|
69
|
-
* Check if an error is a GitHub rate limit error (429 or rate-limit 403).
|
|
70
|
-
* Static proxy kept for backward compatibility with tests.
|
|
71
|
-
*/
|
|
72
|
-
static isRateLimitError(error: unknown): boolean;
|
|
73
68
|
/**
|
|
74
69
|
* Vet a specific issue (delegates to IssueVetter).
|
|
75
70
|
*/
|
|
@@ -12,7 +12,7 @@ import { getOctokit, checkRateLimit } from './github.js';
|
|
|
12
12
|
import { getStateManager } from './state.js';
|
|
13
13
|
import { daysBetween, getDataDir } from './utils.js';
|
|
14
14
|
import { DEFAULT_CONFIG } from './types.js';
|
|
15
|
-
import { ValidationError, errorMessage, getHttpStatusCode } from './errors.js';
|
|
15
|
+
import { ValidationError, errorMessage, getHttpStatusCode, isRateLimitError } from './errors.js';
|
|
16
16
|
import { debug, info, warn } from './logger.js';
|
|
17
17
|
import { getHttpCache, cachedTimeBased } from './http-cache.js';
|
|
18
18
|
import { isDocOnlyIssue, detectLabelFarmingRepos, applyPerRepoCap } from './issue-filtering.js';
|
|
@@ -328,7 +328,7 @@ export class IssueDiscovery {
|
|
|
328
328
|
catch (error) {
|
|
329
329
|
const errMsg = errorMessage(error);
|
|
330
330
|
phase2Error = errMsg;
|
|
331
|
-
if (
|
|
331
|
+
if (isRateLimitError(error)) {
|
|
332
332
|
rateLimitHitDuringSearch = true;
|
|
333
333
|
}
|
|
334
334
|
warn(MODULE, `Error in general issue search: ${errMsg}`);
|
|
@@ -370,7 +370,7 @@ export class IssueDiscovery {
|
|
|
370
370
|
catch (error) {
|
|
371
371
|
const errMsg = errorMessage(error);
|
|
372
372
|
phase3Error = errMsg;
|
|
373
|
-
if (
|
|
373
|
+
if (isRateLimitError(error)) {
|
|
374
374
|
rateLimitHitDuringSearch = true;
|
|
375
375
|
}
|
|
376
376
|
warn(MODULE, `Error in maintained-repo search: ${errMsg}`);
|
|
@@ -464,7 +464,7 @@ export class IssueDiscovery {
|
|
|
464
464
|
}
|
|
465
465
|
catch (error) {
|
|
466
466
|
failedBatches++;
|
|
467
|
-
if (
|
|
467
|
+
if (isRateLimitError(error)) {
|
|
468
468
|
rateLimitFailures++;
|
|
469
469
|
}
|
|
470
470
|
const batchRepos = batch.join(', ');
|
|
@@ -489,13 +489,6 @@ export class IssueDiscovery {
|
|
|
489
489
|
}
|
|
490
490
|
return batches;
|
|
491
491
|
}
|
|
492
|
-
/**
|
|
493
|
-
* Check if an error is a GitHub rate limit error (429 or rate-limit 403).
|
|
494
|
-
* Static proxy kept for backward compatibility with tests.
|
|
495
|
-
*/
|
|
496
|
-
static isRateLimitError(error) {
|
|
497
|
-
return IssueVetter.isRateLimitError(error);
|
|
498
|
-
}
|
|
499
492
|
/**
|
|
500
493
|
* Vet a specific issue (delegates to IssueVetter).
|
|
501
494
|
*/
|
|
@@ -29,8 +29,6 @@ export declare class IssueVetter {
|
|
|
29
29
|
allFailed: boolean;
|
|
30
30
|
rateLimitHit: boolean;
|
|
31
31
|
}>;
|
|
32
|
-
/** Check if an error is a GitHub rate limit error (429 or rate-limit 403). */
|
|
33
|
-
static isRateLimitError(error: unknown): boolean;
|
|
34
32
|
checkNoExistingPR(owner: string, repo: string, issueNumber: number): Promise<CheckResult>;
|
|
35
33
|
/**
|
|
36
34
|
* Check how many merged PRs the authenticated user has in a repo.
|
|
@@ -5,14 +5,13 @@
|
|
|
5
5
|
* Extracted from issue-discovery.ts (#356) to isolate vetting logic.
|
|
6
6
|
*/
|
|
7
7
|
import { paginateAll } from './pagination.js';
|
|
8
|
-
import { parseGitHubUrl, daysBetween } from './utils.js';
|
|
9
|
-
import { ValidationError, errorMessage,
|
|
8
|
+
import { parseGitHubUrl, daysBetween, DEFAULT_CONCURRENCY } from './utils.js';
|
|
9
|
+
import { ValidationError, errorMessage, isRateLimitError } from './errors.js';
|
|
10
10
|
import { warn } from './logger.js';
|
|
11
11
|
import { getHttpCache, cachedRequest, cachedTimeBased } from './http-cache.js';
|
|
12
12
|
import { calculateRepoQualityBonus, calculateViabilityScore } from './issue-scoring.js';
|
|
13
13
|
const MODULE = 'issue-vetting';
|
|
14
|
-
|
|
15
|
-
const MAX_CONCURRENT_REQUESTS = 5;
|
|
14
|
+
const MAX_CONCURRENT_REQUESTS = DEFAULT_CONCURRENCY;
|
|
16
15
|
// Cache for contribution guidelines (expires after 1 hour, max 100 entries)
|
|
17
16
|
const guidelinesCache = new Map();
|
|
18
17
|
const CACHE_TTL_MS = 60 * 60 * 1000; // 1 hour
|
|
@@ -233,7 +232,7 @@ export class IssueVetter {
|
|
|
233
232
|
*/
|
|
234
233
|
async vetIssuesParallel(urls, maxResults, priority) {
|
|
235
234
|
const candidates = [];
|
|
236
|
-
const pending =
|
|
235
|
+
const pending = new Map();
|
|
237
236
|
let failedVettingCount = 0;
|
|
238
237
|
let rateLimitFailures = 0;
|
|
239
238
|
let attemptedCount = 0;
|
|
@@ -253,21 +252,20 @@ export class IssueVetter {
|
|
|
253
252
|
})
|
|
254
253
|
.catch((error) => {
|
|
255
254
|
failedVettingCount++;
|
|
256
|
-
if (
|
|
255
|
+
if (isRateLimitError(error)) {
|
|
257
256
|
rateLimitFailures++;
|
|
258
257
|
}
|
|
259
258
|
warn(MODULE, `Error vetting issue ${url}:`, errorMessage(error));
|
|
260
|
-
})
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
pending.splice(completed, 1);
|
|
259
|
+
})
|
|
260
|
+
.finally(() => pending.delete(url));
|
|
261
|
+
pending.set(url, task);
|
|
262
|
+
// Limit concurrency — wait for at least one to complete before launching more
|
|
263
|
+
if (pending.size >= MAX_CONCURRENT_REQUESTS) {
|
|
264
|
+
await Promise.race(pending.values());
|
|
267
265
|
}
|
|
268
266
|
}
|
|
269
267
|
// Wait for remaining
|
|
270
|
-
await Promise.allSettled(pending);
|
|
268
|
+
await Promise.allSettled(pending.values());
|
|
271
269
|
const allFailed = failedVettingCount === attemptedCount && attemptedCount > 0;
|
|
272
270
|
if (allFailed) {
|
|
273
271
|
warn(MODULE, `All ${attemptedCount} issue(s) failed vetting. ` +
|
|
@@ -275,17 +273,6 @@ export class IssueVetter {
|
|
|
275
273
|
}
|
|
276
274
|
return { candidates: candidates.slice(0, maxResults), allFailed, rateLimitHit: rateLimitFailures > 0 };
|
|
277
275
|
}
|
|
278
|
-
/** Check if an error is a GitHub rate limit error (429 or rate-limit 403). */
|
|
279
|
-
static isRateLimitError(error) {
|
|
280
|
-
const status = getHttpStatusCode(error);
|
|
281
|
-
if (status === 429)
|
|
282
|
-
return true;
|
|
283
|
-
if (status === 403) {
|
|
284
|
-
const msg = errorMessage(error).toLowerCase();
|
|
285
|
-
return msg.includes('rate limit');
|
|
286
|
-
}
|
|
287
|
-
return false;
|
|
288
|
-
}
|
|
289
276
|
async checkNoExistingPR(owner, repo, issueNumber) {
|
|
290
277
|
try {
|
|
291
278
|
// Search for PRs that mention this issue
|
|
@@ -447,26 +434,25 @@ export class IssueVetter {
|
|
|
447
434
|
return cached.guidelines;
|
|
448
435
|
}
|
|
449
436
|
const filesToCheck = ['CONTRIBUTING.md', '.github/CONTRIBUTING.md', 'docs/CONTRIBUTING.md', 'contributing.md'];
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
}
|
|
437
|
+
// Probe all paths in parallel — take the first success in priority order
|
|
438
|
+
const results = await Promise.allSettled(filesToCheck.map((file) => this.octokit.repos.getContent({ owner, repo, path: file }).then(({ data }) => {
|
|
439
|
+
if ('content' in data) {
|
|
440
|
+
return Buffer.from(data.content, 'base64').toString('utf-8');
|
|
441
|
+
}
|
|
442
|
+
return null;
|
|
443
|
+
})));
|
|
444
|
+
for (let i = 0; i < results.length; i++) {
|
|
445
|
+
const result = results[i];
|
|
446
|
+
if (result.status === 'fulfilled' && result.value) {
|
|
447
|
+
const guidelines = this.parseContributionGuidelines(result.value);
|
|
448
|
+
guidelinesCache.set(cacheKey, { guidelines, fetchedAt: Date.now() });
|
|
449
|
+
pruneCache();
|
|
450
|
+
return guidelines;
|
|
465
451
|
}
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
if (
|
|
469
|
-
warn(MODULE, `Unexpected error fetching ${
|
|
452
|
+
if (result.status === 'rejected') {
|
|
453
|
+
const msg = result.reason instanceof Error ? result.reason.message : String(result.reason);
|
|
454
|
+
if (!msg.includes('404') && !msg.includes('Not Found')) {
|
|
455
|
+
warn(MODULE, `Unexpected error fetching ${filesToCheck[i]} from ${owner}/${repo}: ${msg}`);
|
|
470
456
|
}
|
|
471
457
|
}
|
|
472
458
|
}
|
package/dist/core/pr-monitor.js
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
*/
|
|
14
14
|
import { getOctokit } from './github.js';
|
|
15
15
|
import { getStateManager } from './state.js';
|
|
16
|
-
import { daysBetween, parseGitHubUrl, extractOwnerRepo } from './utils.js';
|
|
16
|
+
import { daysBetween, parseGitHubUrl, extractOwnerRepo, DEFAULT_CONCURRENCY } from './utils.js';
|
|
17
17
|
import { runWorkerPool } from './concurrency.js';
|
|
18
18
|
import { ConfigurationError, ValidationError, errorMessage, getHttpStatusCode } from './errors.js';
|
|
19
19
|
import { paginateAll } from './pagination.js';
|
|
@@ -31,8 +31,7 @@ export { computeDisplayLabel } from './display-utils.js';
|
|
|
31
31
|
export { classifyCICheck, classifyFailingChecks } from './ci-analysis.js';
|
|
32
32
|
export { isConditionalChecklistItem } from './checklist-analysis.js';
|
|
33
33
|
const MODULE = 'pr-monitor';
|
|
34
|
-
|
|
35
|
-
const MAX_CONCURRENT_REQUESTS = 5;
|
|
34
|
+
const MAX_CONCURRENT_REQUESTS = DEFAULT_CONCURRENCY;
|
|
36
35
|
export class PRMonitor {
|
|
37
36
|
octokit;
|
|
38
37
|
stateManager;
|
|
@@ -482,7 +481,7 @@ export class PRMonitor {
|
|
|
482
481
|
}
|
|
483
482
|
// If entire chunk failed, likely a systemic issue (rate limit, auth, outage) — abort remaining
|
|
484
483
|
if (chunkFailures === chunk.length && chunk.length > 0) {
|
|
485
|
-
const remaining =
|
|
484
|
+
const remaining = uniqueRepos.length - i - chunkSize;
|
|
486
485
|
if (remaining > 0) {
|
|
487
486
|
warn(MODULE, `Entire chunk failed, aborting remaining ${remaining} repos`);
|
|
488
487
|
}
|
package/dist/core/utils.d.ts
CHANGED
package/dist/core/utils.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Shared utility functions
|
|
3
3
|
*/
|
|
4
|
+
/** Default concurrency limit for parallel GitHub API requests. */
|
|
5
|
+
export const DEFAULT_CONCURRENCY = 5;
|
|
4
6
|
import * as fs from 'fs';
|
|
5
7
|
import * as path from 'path';
|
|
6
8
|
import * as os from 'os';
|
|
@@ -207,7 +209,7 @@ export function extractOwnerRepo(url) {
|
|
|
207
209
|
* // -9
|
|
208
210
|
*/
|
|
209
211
|
export function daysBetween(from, to = new Date()) {
|
|
210
|
-
return Math.floor((to.getTime() - from.getTime()) / (1000 * 60 * 60 * 24));
|
|
212
|
+
return Math.max(0, Math.floor((to.getTime() - from.getTime()) / (1000 * 60 * 60 * 24)));
|
|
211
213
|
}
|
|
212
214
|
/**
|
|
213
215
|
* Splits an `"owner/repo"` string into its owner and repo components.
|
|
@@ -268,6 +270,8 @@ export function getCLIVersion() {
|
|
|
268
270
|
export function formatRelativeTime(dateStr) {
|
|
269
271
|
const date = new Date(dateStr);
|
|
270
272
|
const diffMs = Date.now() - date.getTime();
|
|
273
|
+
if (diffMs < 0)
|
|
274
|
+
return 'just now';
|
|
271
275
|
const diffMins = Math.floor(diffMs / 60000);
|
|
272
276
|
const diffHours = Math.floor(diffMs / 3600000);
|
|
273
277
|
const diffDays = Math.floor(diffMs / 86400000);
|