@oss-autopilot/core 0.44.1 → 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-registry.d.ts +2 -1
- 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/ci-analysis.d.ts +3 -17
- package/dist/core/ci-analysis.js +3 -3
- package/dist/core/errors.d.ts +8 -0
- package/dist/core/errors.js +26 -0
- package/dist/core/github-stats.d.ts +0 -20
- package/dist/core/github-stats.js +1 -1
- package/dist/core/github.js +2 -2
- package/dist/core/http-cache.d.ts +0 -2
- package/dist/core/http-cache.js +0 -4
- package/dist/core/index.d.ts +3 -3
- package/dist/core/index.js +3 -3
- 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/test-utils.d.ts +1 -3
- package/dist/core/test-utils.js +0 -41
- package/dist/core/utils.d.ts +2 -0
- package/dist/core/utils.js +5 -1
- package/package.json +1 -1
package/dist/core/errors.d.ts
CHANGED
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
* Custom error type hierarchy for oss-autopilot.
|
|
3
3
|
* Provides structured error codes and specific error classes
|
|
4
4
|
* for different failure categories.
|
|
5
|
+
*
|
|
6
|
+
* Error strategy: Rate-limit and auth errors (429, 401, 403+rate-limit) always
|
|
7
|
+
* propagate to the caller via isRateLimitError/isRateLimitOrAuthError.
|
|
8
|
+
* Other errors degrade gracefully — modules return partial results and log warnings.
|
|
5
9
|
*/
|
|
6
10
|
/**
|
|
7
11
|
* Base error for all oss-autopilot errors.
|
|
@@ -31,3 +35,7 @@ export declare function errorMessage(e: unknown): string;
|
|
|
31
35
|
* Returns undefined if the error doesn't have a numeric `status` property.
|
|
32
36
|
*/
|
|
33
37
|
export declare function getHttpStatusCode(error: unknown): number | undefined;
|
|
38
|
+
/** Check if an error is a GitHub rate limit error (429 or rate-limit 403). */
|
|
39
|
+
export declare function isRateLimitError(error: unknown): boolean;
|
|
40
|
+
/** Return true for errors that should propagate (not degrade gracefully): rate limits, auth failures, abuse detection. */
|
|
41
|
+
export declare function isRateLimitOrAuthError(err: unknown): boolean;
|
package/dist/core/errors.js
CHANGED
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
* Custom error type hierarchy for oss-autopilot.
|
|
3
3
|
* Provides structured error codes and specific error classes
|
|
4
4
|
* for different failure categories.
|
|
5
|
+
*
|
|
6
|
+
* Error strategy: Rate-limit and auth errors (429, 401, 403+rate-limit) always
|
|
7
|
+
* propagate to the caller via isRateLimitError/isRateLimitOrAuthError.
|
|
8
|
+
* Other errors degrade gracefully — modules return partial results and log warnings.
|
|
5
9
|
*/
|
|
6
10
|
/**
|
|
7
11
|
* Base error for all oss-autopilot errors.
|
|
@@ -49,3 +53,25 @@ export function getHttpStatusCode(error) {
|
|
|
49
53
|
}
|
|
50
54
|
return undefined;
|
|
51
55
|
}
|
|
56
|
+
/** Check if an error is a GitHub rate limit error (429 or rate-limit 403). */
|
|
57
|
+
export function isRateLimitError(error) {
|
|
58
|
+
const status = getHttpStatusCode(error);
|
|
59
|
+
if (status === 429)
|
|
60
|
+
return true;
|
|
61
|
+
if (status === 403) {
|
|
62
|
+
const msg = errorMessage(error).toLowerCase();
|
|
63
|
+
return msg.includes('rate limit');
|
|
64
|
+
}
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
/** Return true for errors that should propagate (not degrade gracefully): rate limits, auth failures, abuse detection. */
|
|
68
|
+
export function isRateLimitOrAuthError(err) {
|
|
69
|
+
const status = getHttpStatusCode(err);
|
|
70
|
+
if (status === 401 || status === 429)
|
|
71
|
+
return true;
|
|
72
|
+
if (status === 403) {
|
|
73
|
+
const msg = errorMessage(err).toLowerCase();
|
|
74
|
+
return msg.includes('rate limit') || msg.includes('abuse detection');
|
|
75
|
+
}
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
@@ -28,26 +28,6 @@ export declare function fetchUserMergedPRCounts(octokit: Octokit, githubUsername
|
|
|
28
28
|
* Used to populate closedWithoutMergeCount in repo scores for accurate merge rate.
|
|
29
29
|
*/
|
|
30
30
|
export declare function fetchUserClosedPRCounts(octokit: Octokit, githubUsername: string): Promise<PRCountsResult<number>>;
|
|
31
|
-
/**
|
|
32
|
-
* Shared helper: search for recent PRs and filter out own repos, excluded repos/orgs.
|
|
33
|
-
* Returns parsed search results that pass all filters.
|
|
34
|
-
*/
|
|
35
|
-
export declare function fetchRecentPRs<T>(octokit: Octokit, config: {
|
|
36
|
-
githubUsername: string;
|
|
37
|
-
excludeRepos: string[];
|
|
38
|
-
excludeOrgs?: string[];
|
|
39
|
-
}, query: string, label: string, days: number, mapItem: (item: {
|
|
40
|
-
html_url: string;
|
|
41
|
-
title: string;
|
|
42
|
-
closed_at: string | null;
|
|
43
|
-
pull_request?: {
|
|
44
|
-
merged_at?: string | null;
|
|
45
|
-
};
|
|
46
|
-
}, parsed: {
|
|
47
|
-
owner: string;
|
|
48
|
-
repo: string;
|
|
49
|
-
number: number;
|
|
50
|
-
}) => T): Promise<T[]>;
|
|
51
31
|
/**
|
|
52
32
|
* Fetch PRs closed without merge in the last N days.
|
|
53
33
|
* Returns lightweight ClosedPR objects for surfacing in the daily digest.
|
|
@@ -168,7 +168,7 @@ export function fetchUserClosedPRCounts(octokit, githubUsername) {
|
|
|
168
168
|
* Shared helper: search for recent PRs and filter out own repos, excluded repos/orgs.
|
|
169
169
|
* Returns parsed search results that pass all filters.
|
|
170
170
|
*/
|
|
171
|
-
|
|
171
|
+
async function fetchRecentPRs(octokit, config, query, label, days, mapItem) {
|
|
172
172
|
if (!config.githubUsername) {
|
|
173
173
|
warn(MODULE, `Skipping recently ${label} PRs fetch: no githubUsername configured. Run /setup-oss to configure.`);
|
|
174
174
|
return [];
|
package/dist/core/github.js
CHANGED
|
@@ -19,7 +19,7 @@ export function getOctokit(token) {
|
|
|
19
19
|
_octokit = new ThrottledOctokit({
|
|
20
20
|
auth: token,
|
|
21
21
|
throttle: {
|
|
22
|
-
onRateLimit: (retryAfter, options,
|
|
22
|
+
onRateLimit: (retryAfter, options, _octokit, retryCount) => {
|
|
23
23
|
const opts = options;
|
|
24
24
|
const resetAt = new Date(Date.now() + retryAfter * 1000);
|
|
25
25
|
if (retryCount < 2) {
|
|
@@ -29,7 +29,7 @@ export function getOctokit(token) {
|
|
|
29
29
|
warn(MODULE, `Rate limit exceeded, not retrying — ${opts.method} ${opts.url} (resets at ${formatResetTime(resetAt)})`);
|
|
30
30
|
return false;
|
|
31
31
|
},
|
|
32
|
-
onSecondaryRateLimit: (retryAfter, options,
|
|
32
|
+
onSecondaryRateLimit: (retryAfter, options, _octokit, retryCount) => {
|
|
33
33
|
const opts = options;
|
|
34
34
|
const resetAt = new Date(Date.now() + retryAfter * 1000);
|
|
35
35
|
if (retryCount < 1) {
|
|
@@ -78,8 +78,6 @@ export declare class HttpCache {
|
|
|
78
78
|
* The singleton is lazily initialized on first access.
|
|
79
79
|
*/
|
|
80
80
|
export declare function getHttpCache(): HttpCache;
|
|
81
|
-
/** Reset the singleton (for tests). */
|
|
82
|
-
export declare function resetHttpCache(): void;
|
|
83
81
|
/**
|
|
84
82
|
* Wraps an Octokit `repos.get`-style call with ETag caching and request
|
|
85
83
|
* deduplication.
|
package/dist/core/http-cache.js
CHANGED
|
@@ -204,10 +204,6 @@ export function getHttpCache() {
|
|
|
204
204
|
}
|
|
205
205
|
return _httpCache;
|
|
206
206
|
}
|
|
207
|
-
/** Reset the singleton (for tests). */
|
|
208
|
-
export function resetHttpCache() {
|
|
209
|
-
_httpCache = null;
|
|
210
|
-
}
|
|
211
207
|
// ---------------------------------------------------------------------------
|
|
212
208
|
// Octokit integration helpers
|
|
213
209
|
// ---------------------------------------------------------------------------
|
package/dist/core/index.d.ts
CHANGED
|
@@ -8,9 +8,9 @@ export { IssueDiscovery, type IssueCandidate, type SearchPriority, isDocOnlyIssu
|
|
|
8
8
|
export { IssueConversationMonitor } from './issue-conversation.js';
|
|
9
9
|
export { isBotAuthor, isAcknowledgmentComment } from './comment-utils.js';
|
|
10
10
|
export { getOctokit, checkRateLimit, type RateLimitInfo } 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
|
-
export { HttpCache, getHttpCache,
|
|
14
|
+
export { HttpCache, getHttpCache, cachedRequest, type CacheEntry } from './http-cache.js';
|
|
15
15
|
export { CRITICAL_STATUSES, computeRepoSignals, groupPRsByRepo, assessCapacity, collectActionableIssues, computeActionMenu, toShelvedPRRef, formatActionHint, formatBriefSummary, formatSummary, printDigest, } from './daily-logic.js';
|
|
16
16
|
export * from './types.js';
|
package/dist/core/index.js
CHANGED
|
@@ -8,9 +8,9 @@ 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
|
-
export { HttpCache, getHttpCache,
|
|
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';
|
|
16
16
|
export * from './types.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
|
}
|
|
@@ -5,10 +5,8 @@
|
|
|
5
5
|
* fields we only update one place. Every factory accepts a `Partial<T>`
|
|
6
6
|
* override bag — callers only specify the fields relevant to their test.
|
|
7
7
|
*/
|
|
8
|
-
import type { FetchedPR, DailyDigest
|
|
8
|
+
import type { FetchedPR, DailyDigest } from './types.js';
|
|
9
9
|
import type { CapacityAssessment } from '../formatters/json.js';
|
|
10
10
|
export declare function makeFetchedPR(overrides?: Partial<FetchedPR>): FetchedPR;
|
|
11
11
|
export declare function makeDailyDigest(overrides?: Partial<DailyDigest>): DailyDigest;
|
|
12
|
-
export declare function makeShelvedPRRef(overrides?: Partial<ShelvedPRRef>): ShelvedPRRef;
|
|
13
12
|
export declare function makeCapacityAssessment(overrides?: Partial<CapacityAssessment>): CapacityAssessment;
|
|
14
|
-
export declare function makeAgentState(overrides?: Partial<AgentState>): AgentState;
|
package/dist/core/test-utils.js
CHANGED
|
@@ -69,20 +69,6 @@ export function makeDailyDigest(overrides = {}) {
|
|
|
69
69
|
};
|
|
70
70
|
}
|
|
71
71
|
// ---------------------------------------------------------------------------
|
|
72
|
-
// ShelvedPRRef
|
|
73
|
-
// ---------------------------------------------------------------------------
|
|
74
|
-
export function makeShelvedPRRef(overrides = {}) {
|
|
75
|
-
return {
|
|
76
|
-
number: 1,
|
|
77
|
-
url: 'https://github.com/owner/repo/pull/1',
|
|
78
|
-
title: 'Shelved PR',
|
|
79
|
-
repo: 'owner/repo',
|
|
80
|
-
daysSinceActivity: 45,
|
|
81
|
-
status: 'healthy',
|
|
82
|
-
...overrides,
|
|
83
|
-
};
|
|
84
|
-
}
|
|
85
|
-
// ---------------------------------------------------------------------------
|
|
86
72
|
// CapacityAssessment
|
|
87
73
|
// ---------------------------------------------------------------------------
|
|
88
74
|
export function makeCapacityAssessment(overrides = {}) {
|
|
@@ -96,30 +82,3 @@ export function makeCapacityAssessment(overrides = {}) {
|
|
|
96
82
|
...overrides,
|
|
97
83
|
};
|
|
98
84
|
}
|
|
99
|
-
// ---------------------------------------------------------------------------
|
|
100
|
-
// AgentState (partial — for tests that need a state object)
|
|
101
|
-
// ---------------------------------------------------------------------------
|
|
102
|
-
export function makeAgentState(overrides = {}) {
|
|
103
|
-
return {
|
|
104
|
-
version: 2,
|
|
105
|
-
repoScores: {},
|
|
106
|
-
config: {
|
|
107
|
-
setupComplete: false,
|
|
108
|
-
githubUsername: 'testuser',
|
|
109
|
-
excludeRepos: [],
|
|
110
|
-
maxActivePRs: 10,
|
|
111
|
-
dormantThresholdDays: 30,
|
|
112
|
-
approachingDormantDays: 25,
|
|
113
|
-
maxIssueAgeDays: 90,
|
|
114
|
-
languages: [],
|
|
115
|
-
labels: [],
|
|
116
|
-
trustedProjects: [],
|
|
117
|
-
minRepoScoreThreshold: 4,
|
|
118
|
-
starredRepos: [],
|
|
119
|
-
},
|
|
120
|
-
events: [],
|
|
121
|
-
lastRunAt: '2025-06-20T00:00:00Z',
|
|
122
|
-
activeIssues: [],
|
|
123
|
-
...overrides,
|
|
124
|
-
};
|
|
125
|
-
}
|
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);
|