terminalhire 0.35.4 → 0.36.0
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/bin/jpi-bounties.js +100 -15
- package/dist/bin/jpi-chat-read.js +16 -1
- package/dist/bin/jpi-chat.js +16 -1
- package/dist/bin/jpi-claim.js +100 -15
- package/dist/bin/jpi-contribute.js +16 -1
- package/dist/bin/jpi-devs.js +100 -15
- package/dist/bin/jpi-dispatch.js +373 -64
- package/dist/bin/jpi-hub.js +100 -15
- package/dist/bin/jpi-inbox.js +16 -1
- package/dist/bin/jpi-init.js +100 -15
- package/dist/bin/jpi-intro.js +16 -1
- package/dist/bin/jpi-jobs.js +100 -15
- package/dist/bin/jpi-learn.js +16 -1
- package/dist/bin/jpi-login.js +100 -15
- package/dist/bin/jpi-mcp-chat.js +27177 -0
- package/dist/bin/jpi-profile.js +16 -1
- package/dist/bin/jpi-project.js +100 -15
- package/dist/bin/jpi-refresh.js +100 -15
- package/dist/bin/jpi-repo.js +16 -1
- package/dist/bin/jpi-save.js +16 -1
- package/dist/bin/jpi-spinner.js +16 -1
- package/dist/bin/jpi-sync.js +16 -1
- package/dist/bin/jpi-trajectory.js +16 -1
- package/dist/bin/spinner.js +16 -1
- package/dist/src/chat-client.js +16 -1
- package/dist/src/chat-keystore.js +16 -1
- package/dist/src/intro.js +16 -1
- package/dist/src/profile.js +16 -1
- package/dist/src/repo-experience.js +16 -1
- package/dist/src/reputation/fetch.js +16 -1
- package/dist/src/signal.js +16 -1
- package/dist/src/trajectory.js +16 -1
- package/package.json +1 -1
package/dist/bin/jpi-bounties.js
CHANGED
|
@@ -1748,6 +1748,32 @@ async function openPRClosingRefs(owner, name, token, signal, governor) {
|
|
|
1748
1748
|
return null;
|
|
1749
1749
|
}
|
|
1750
1750
|
}
|
|
1751
|
+
async function issueCrossRefPRAttempts(owner, name, issueNumber, token, signal, governor) {
|
|
1752
|
+
const url = `https://api.github.com/repos/${owner}/${name}/issues/${issueNumber}/timeline?per_page=100`;
|
|
1753
|
+
try {
|
|
1754
|
+
const res = governor ? await governor.get(url, { headers: ghHeaders(token), signal }) : await fetch(url, { headers: ghHeaders(token), signal });
|
|
1755
|
+
if (!res || !res.ok) return null;
|
|
1756
|
+
const link = res.headers?.get("link") ?? null;
|
|
1757
|
+
const hasNextPage = link != null && /\brel="next"/.test(link);
|
|
1758
|
+
const events = await res.json();
|
|
1759
|
+
if (!Array.isArray(events)) return null;
|
|
1760
|
+
if (hasNextPage || events.length >= 100) return null;
|
|
1761
|
+
let hasOpenPR = false;
|
|
1762
|
+
let hasClosedPR = false;
|
|
1763
|
+
const prNumbers = [];
|
|
1764
|
+
for (const ev of events) {
|
|
1765
|
+
if (ev?.event !== "cross-referenced") continue;
|
|
1766
|
+
const src = ev.source?.issue;
|
|
1767
|
+
if (!src || src.pull_request == null) continue;
|
|
1768
|
+
if (typeof src.number === "number") prNumbers.push(src.number);
|
|
1769
|
+
if (src.state === "open") hasOpenPR = true;
|
|
1770
|
+
else if (src.state === "closed") hasClosedPR = true;
|
|
1771
|
+
}
|
|
1772
|
+
return { hasOpenPR, hasClosedPR, prNumbers };
|
|
1773
|
+
} catch {
|
|
1774
|
+
return null;
|
|
1775
|
+
}
|
|
1776
|
+
}
|
|
1751
1777
|
function makeScoringGovernor(governor) {
|
|
1752
1778
|
return governor ?? makeGitHubGovernor(
|
|
1753
1779
|
((url, init) => fetch(url, init)),
|
|
@@ -3925,6 +3951,30 @@ var init_feeds = __esm({
|
|
|
3925
3951
|
});
|
|
3926
3952
|
|
|
3927
3953
|
// ../../packages/core/src/feeds/contributions.ts
|
|
3954
|
+
function readSearchMaxPages() {
|
|
3955
|
+
const raw = process.env["CONTRIB_SEARCH_MAX_PAGES"];
|
|
3956
|
+
if (raw == null) return DEFAULT_SEARCH_MAX_PAGES;
|
|
3957
|
+
const n = Number.parseInt(raw, 10);
|
|
3958
|
+
if (Number.isNaN(n)) return DEFAULT_SEARCH_MAX_PAGES;
|
|
3959
|
+
return Math.min(Math.max(n, MIN_SEARCH_MAX_PAGES), MAX_SEARCH_MAX_PAGES);
|
|
3960
|
+
}
|
|
3961
|
+
function readMaxContribItems() {
|
|
3962
|
+
const raw = process.env["CONTRIB_MAX_ITEMS"];
|
|
3963
|
+
if (raw == null) return DEFAULT_MAX_CONTRIB_ITEMS;
|
|
3964
|
+
const n = Number.parseInt(raw, 10);
|
|
3965
|
+
if (Number.isNaN(n)) return DEFAULT_MAX_CONTRIB_ITEMS;
|
|
3966
|
+
return Math.min(Math.max(n, MIN_MAX_CONTRIB_ITEMS), MAX_MAX_CONTRIB_ITEMS);
|
|
3967
|
+
}
|
|
3968
|
+
function readMaxContribIssuesScanned() {
|
|
3969
|
+
const raw = process.env["CONTRIB_MAX_ISSUES_SCANNED"];
|
|
3970
|
+
if (raw == null) return DEFAULT_MAX_CONTRIB_ISSUES_SCANNED;
|
|
3971
|
+
const n = Number.parseInt(raw, 10);
|
|
3972
|
+
if (Number.isNaN(n)) return DEFAULT_MAX_CONTRIB_ISSUES_SCANNED;
|
|
3973
|
+
return Math.min(
|
|
3974
|
+
Math.max(n, MIN_MAX_CONTRIB_ISSUES_SCANNED),
|
|
3975
|
+
MAX_MAX_CONTRIB_ISSUES_SCANNED
|
|
3976
|
+
);
|
|
3977
|
+
}
|
|
3928
3978
|
function authHeaders2() {
|
|
3929
3979
|
const token = process.env["GITHUB_TOKEN"] ?? process.env["GH_TOKEN"];
|
|
3930
3980
|
const h = {
|
|
@@ -4012,13 +4062,21 @@ async function fetchRateLimit(client) {
|
|
|
4012
4062
|
}
|
|
4013
4063
|
async function searchContribIssues(client, queries) {
|
|
4014
4064
|
const byUrl = /* @__PURE__ */ new Map();
|
|
4065
|
+
const maxPages = readSearchMaxPages();
|
|
4015
4066
|
for (const q of queries) {
|
|
4016
|
-
|
|
4017
|
-
|
|
4018
|
-
|
|
4019
|
-
|
|
4020
|
-
|
|
4021
|
-
if (
|
|
4067
|
+
for (let page = 1; page <= maxPages; page++) {
|
|
4068
|
+
const res = await client.json(
|
|
4069
|
+
`/search/issues?q=${encodeURIComponent(q)}&sort=created&order=desc&per_page=${SEARCH_PER_PAGE2}&page=${page}`
|
|
4070
|
+
);
|
|
4071
|
+
const items = res?.items;
|
|
4072
|
+
if (items == null) break;
|
|
4073
|
+
for (const it of items) {
|
|
4074
|
+
if (it.pull_request) continue;
|
|
4075
|
+
if (!byUrl.has(it.html_url)) byUrl.set(it.html_url, it);
|
|
4076
|
+
}
|
|
4077
|
+
if (items.length < SEARCH_PER_PAGE2) break;
|
|
4078
|
+
const stats = client.getStats();
|
|
4079
|
+
if (stats.budgetAborted || stats.secondaryAborted) break;
|
|
4022
4080
|
}
|
|
4023
4081
|
}
|
|
4024
4082
|
return [...byUrl.values()].sort(
|
|
@@ -4079,10 +4137,14 @@ async function aggregateContributions(opts = {}) {
|
|
|
4079
4137
|
probeTimeoutMs: opts.fetchImpl ? null : PROBE_TIMEOUT_MS
|
|
4080
4138
|
});
|
|
4081
4139
|
const queries = opts.queries ?? CONTRIB_SEARCH_QUERIES;
|
|
4140
|
+
const maxContribItems = readMaxContribItems();
|
|
4082
4141
|
const startRl = await fetchRateLimit(client);
|
|
4083
4142
|
const coreHealthyAtStart = (startRl?.core?.remaining ?? 0) >= 500;
|
|
4084
4143
|
client.setSecondaryHint(coreHealthyAtStart);
|
|
4085
|
-
const issues = (await searchContribIssues(client, queries)).slice(
|
|
4144
|
+
const issues = (await searchContribIssues(client, queries)).slice(
|
|
4145
|
+
0,
|
|
4146
|
+
readMaxContribIssuesScanned()
|
|
4147
|
+
);
|
|
4086
4148
|
const repoCache = /* @__PURE__ */ new Map();
|
|
4087
4149
|
const contribCache = /* @__PURE__ */ new Map();
|
|
4088
4150
|
const prRefsCache = /* @__PURE__ */ new Map();
|
|
@@ -4172,7 +4234,7 @@ async function aggregateContributions(opts = {}) {
|
|
|
4172
4234
|
let contribUndefined = 0;
|
|
4173
4235
|
let prRefsNull = 0;
|
|
4174
4236
|
for (const issue of issues) {
|
|
4175
|
-
if (jobs.length >=
|
|
4237
|
+
if (jobs.length >= maxContribItems) break;
|
|
4176
4238
|
const fullName = repoFullNameFromApiUrl2(issue.repository_url);
|
|
4177
4239
|
if (!fullName) continue;
|
|
4178
4240
|
const id = `contribute:${repoKey(fullName)}#${issue.number}`;
|
|
@@ -4230,7 +4292,7 @@ async function aggregateContributions(opts = {}) {
|
|
|
4230
4292
|
const doDiscovery = opts.discoverRepos ?? (opts.trendingSlugs != null || opts.vocabTerms != null);
|
|
4231
4293
|
let discoveredEmitted = 0;
|
|
4232
4294
|
let discoveryBudgetStopped = false;
|
|
4233
|
-
if (doDiscovery && jobs.length <
|
|
4295
|
+
if (doDiscovery && jobs.length < maxContribItems) {
|
|
4234
4296
|
const maxRepos = Math.min(
|
|
4235
4297
|
Math.max(0, opts.maxDiscoveredRepos ?? MAX_DISCOVERED_REPOS),
|
|
4236
4298
|
MAX_DISCOVERED_REPOS
|
|
@@ -4283,7 +4345,7 @@ async function aggregateContributions(opts = {}) {
|
|
|
4283
4345
|
}
|
|
4284
4346
|
const scanned = candidates.slice(0, maxRepos);
|
|
4285
4347
|
for (const fullName of scanned) {
|
|
4286
|
-
if (jobs.length >=
|
|
4348
|
+
if (jobs.length >= maxContribItems) break;
|
|
4287
4349
|
if (client.getStats().budgetAborted || client.getStats().secondaryAborted) {
|
|
4288
4350
|
discoveryBudgetStopped = true;
|
|
4289
4351
|
break;
|
|
@@ -4308,7 +4370,7 @@ async function aggregateContributions(opts = {}) {
|
|
|
4308
4370
|
const repoIssues = (searchRes?.items ?? []).filter((it) => !it.pull_request);
|
|
4309
4371
|
let perRepoDiscovered = 0;
|
|
4310
4372
|
for (const issue of repoIssues) {
|
|
4311
|
-
if (jobs.length >=
|
|
4373
|
+
if (jobs.length >= maxContribItems) break;
|
|
4312
4374
|
if (perRepoDiscovered >= MAX_ISSUES_PER_DISCOVERED_REPO) break;
|
|
4313
4375
|
if ((perRepo.get(repoKey(fullName)) ?? 0) >= MAX_BOUNTIES_PER_DISCOVERED_REPO) break;
|
|
4314
4376
|
const id = `contribute:${repoKey(fullName)}#${issue.number}`;
|
|
@@ -4373,7 +4435,7 @@ async function aggregateContributions(opts = {}) {
|
|
|
4373
4435
|
}
|
|
4374
4436
|
return jobs;
|
|
4375
4437
|
}
|
|
4376
|
-
var GITHUB_API2, CONTRIB_LABEL_QUERIES, CONTRIB_LANGUAGE_QUERIES, CONTRIB_SEARCH_QUERIES, SEARCH_PER_PAGE2,
|
|
4438
|
+
var GITHUB_API2, CONTRIB_LABEL_QUERIES, CONTRIB_LANGUAGE_QUERIES, CONTRIB_SEARCH_QUERIES, SEARCH_PER_PAGE2, DEFAULT_SEARCH_MAX_PAGES, MIN_SEARCH_MAX_PAGES, MAX_SEARCH_MAX_PAGES, DEFAULT_MAX_CONTRIB_ITEMS, MIN_MAX_CONTRIB_ITEMS, MAX_MAX_CONTRIB_ITEMS, DEFAULT_MAX_CONTRIB_ISSUES_SCANNED, MIN_MAX_CONTRIB_ISSUES_SCANNED, MAX_MAX_CONTRIB_ISSUES_SCANNED, MAX_DISCOVERED_REPOS, MAX_ISSUES_PER_DISCOVERED_REPO, DISCOVERY_REPOS_PER_TERM, DISCOVERY_VOCAB_TERMS, DISCOVERY_ISSUE_LABELS, repoKey;
|
|
4377
4439
|
var init_contributions = __esm({
|
|
4378
4440
|
"../../packages/core/src/feeds/contributions.ts"() {
|
|
4379
4441
|
"use strict";
|
|
@@ -4392,7 +4454,11 @@ var init_contributions = __esm({
|
|
|
4392
4454
|
'label:"good-first-issue" type:issue state:open',
|
|
4393
4455
|
'label:"help wanted" type:issue state:open',
|
|
4394
4456
|
'label:"help-wanted" type:issue state:open',
|
|
4395
|
-
'label:"up-for-grabs" type:issue state:open'
|
|
4457
|
+
'label:"up-for-grabs" type:issue state:open',
|
|
4458
|
+
// supply-expansion D: two more first-contribution label families widen the
|
|
4459
|
+
// global newest-first slice WITHOUT relaxing the credential gate.
|
|
4460
|
+
'label:"beginner-friendly" type:issue state:open',
|
|
4461
|
+
'label:"first-timers-only" type:issue state:open'
|
|
4396
4462
|
];
|
|
4397
4463
|
CONTRIB_LANGUAGE_QUERIES = [
|
|
4398
4464
|
...["rust", "go", "python", "c++", "ruby"].map(
|
|
@@ -4400,12 +4466,30 @@ var init_contributions = __esm({
|
|
|
4400
4466
|
),
|
|
4401
4467
|
...["rust", "go"].map(
|
|
4402
4468
|
(lang) => `label:"good first issue" language:${lang} type:issue state:open`
|
|
4469
|
+
),
|
|
4470
|
+
// supply-expansion D: cover the high-volume web/enterprise ecosystems the
|
|
4471
|
+
// original set omitted. TS/JS were previously left out of "good first issue"
|
|
4472
|
+
// (the global slice over-represented them) but a LANGUAGE-scoped page surfaces
|
|
4473
|
+
// DIFFERENT repos than the global newest-first slice, so re-including them widens
|
|
4474
|
+
// distinct-repo coverage rather than duplicating it.
|
|
4475
|
+
...["typescript", "javascript", "java", "python"].map(
|
|
4476
|
+
(lang) => `label:"good first issue" language:${lang} type:issue state:open`
|
|
4477
|
+
),
|
|
4478
|
+
...["typescript", "javascript", "c#", "php"].map(
|
|
4479
|
+
(lang) => `label:"help wanted" language:${lang} type:issue state:open`
|
|
4403
4480
|
)
|
|
4404
4481
|
];
|
|
4405
4482
|
CONTRIB_SEARCH_QUERIES = [...CONTRIB_LABEL_QUERIES, ...CONTRIB_LANGUAGE_QUERIES];
|
|
4406
4483
|
SEARCH_PER_PAGE2 = 100;
|
|
4407
|
-
|
|
4408
|
-
|
|
4484
|
+
DEFAULT_SEARCH_MAX_PAGES = 1;
|
|
4485
|
+
MIN_SEARCH_MAX_PAGES = 1;
|
|
4486
|
+
MAX_SEARCH_MAX_PAGES = 5;
|
|
4487
|
+
DEFAULT_MAX_CONTRIB_ITEMS = 400;
|
|
4488
|
+
MIN_MAX_CONTRIB_ITEMS = 50;
|
|
4489
|
+
MAX_MAX_CONTRIB_ITEMS = 1e3;
|
|
4490
|
+
DEFAULT_MAX_CONTRIB_ISSUES_SCANNED = 1500;
|
|
4491
|
+
MIN_MAX_CONTRIB_ISSUES_SCANNED = 100;
|
|
4492
|
+
MAX_MAX_CONTRIB_ISSUES_SCANNED = 5e3;
|
|
4409
4493
|
MAX_DISCOVERED_REPOS = 15;
|
|
4410
4494
|
MAX_ISSUES_PER_DISCOVERED_REPO = 3;
|
|
4411
4495
|
DISCOVERY_REPOS_PER_TERM = 20;
|
|
@@ -8387,6 +8471,7 @@ __export(src_exports, {
|
|
|
8387
8471
|
isOverIntroLimit: () => isOverIntroLimit,
|
|
8388
8472
|
isTrivialPRTitle: () => isTrivialPRTitle,
|
|
8389
8473
|
isWinnableIssue: () => isWinnableIssue,
|
|
8474
|
+
issueCrossRefPRAttempts: () => issueCrossRefPRAttempts,
|
|
8390
8475
|
jobShortToken: () => jobShortToken,
|
|
8391
8476
|
joinLabels: () => joinLabels,
|
|
8392
8477
|
labelFor: () => labelFor,
|
|
@@ -892,7 +892,11 @@ var init_contributions = __esm({
|
|
|
892
892
|
'label:"good-first-issue" type:issue state:open',
|
|
893
893
|
'label:"help wanted" type:issue state:open',
|
|
894
894
|
'label:"help-wanted" type:issue state:open',
|
|
895
|
-
'label:"up-for-grabs" type:issue state:open'
|
|
895
|
+
'label:"up-for-grabs" type:issue state:open',
|
|
896
|
+
// supply-expansion D: two more first-contribution label families widen the
|
|
897
|
+
// global newest-first slice WITHOUT relaxing the credential gate.
|
|
898
|
+
'label:"beginner-friendly" type:issue state:open',
|
|
899
|
+
'label:"first-timers-only" type:issue state:open'
|
|
896
900
|
];
|
|
897
901
|
CONTRIB_LANGUAGE_QUERIES = [
|
|
898
902
|
...["rust", "go", "python", "c++", "ruby"].map(
|
|
@@ -900,6 +904,17 @@ var init_contributions = __esm({
|
|
|
900
904
|
),
|
|
901
905
|
...["rust", "go"].map(
|
|
902
906
|
(lang) => `label:"good first issue" language:${lang} type:issue state:open`
|
|
907
|
+
),
|
|
908
|
+
// supply-expansion D: cover the high-volume web/enterprise ecosystems the
|
|
909
|
+
// original set omitted. TS/JS were previously left out of "good first issue"
|
|
910
|
+
// (the global slice over-represented them) but a LANGUAGE-scoped page surfaces
|
|
911
|
+
// DIFFERENT repos than the global newest-first slice, so re-including them widens
|
|
912
|
+
// distinct-repo coverage rather than duplicating it.
|
|
913
|
+
...["typescript", "javascript", "java", "python"].map(
|
|
914
|
+
(lang) => `label:"good first issue" language:${lang} type:issue state:open`
|
|
915
|
+
),
|
|
916
|
+
...["typescript", "javascript", "c#", "php"].map(
|
|
917
|
+
(lang) => `label:"help wanted" language:${lang} type:issue state:open`
|
|
903
918
|
)
|
|
904
919
|
];
|
|
905
920
|
CONTRIB_SEARCH_QUERIES = [...CONTRIB_LABEL_QUERIES, ...CONTRIB_LANGUAGE_QUERIES];
|
package/dist/bin/jpi-chat.js
CHANGED
|
@@ -909,7 +909,11 @@ var init_contributions = __esm({
|
|
|
909
909
|
'label:"good-first-issue" type:issue state:open',
|
|
910
910
|
'label:"help wanted" type:issue state:open',
|
|
911
911
|
'label:"help-wanted" type:issue state:open',
|
|
912
|
-
'label:"up-for-grabs" type:issue state:open'
|
|
912
|
+
'label:"up-for-grabs" type:issue state:open',
|
|
913
|
+
// supply-expansion D: two more first-contribution label families widen the
|
|
914
|
+
// global newest-first slice WITHOUT relaxing the credential gate.
|
|
915
|
+
'label:"beginner-friendly" type:issue state:open',
|
|
916
|
+
'label:"first-timers-only" type:issue state:open'
|
|
913
917
|
];
|
|
914
918
|
CONTRIB_LANGUAGE_QUERIES = [
|
|
915
919
|
...["rust", "go", "python", "c++", "ruby"].map(
|
|
@@ -917,6 +921,17 @@ var init_contributions = __esm({
|
|
|
917
921
|
),
|
|
918
922
|
...["rust", "go"].map(
|
|
919
923
|
(lang) => `label:"good first issue" language:${lang} type:issue state:open`
|
|
924
|
+
),
|
|
925
|
+
// supply-expansion D: cover the high-volume web/enterprise ecosystems the
|
|
926
|
+
// original set omitted. TS/JS were previously left out of "good first issue"
|
|
927
|
+
// (the global slice over-represented them) but a LANGUAGE-scoped page surfaces
|
|
928
|
+
// DIFFERENT repos than the global newest-first slice, so re-including them widens
|
|
929
|
+
// distinct-repo coverage rather than duplicating it.
|
|
930
|
+
...["typescript", "javascript", "java", "python"].map(
|
|
931
|
+
(lang) => `label:"good first issue" language:${lang} type:issue state:open`
|
|
932
|
+
),
|
|
933
|
+
...["typescript", "javascript", "c#", "php"].map(
|
|
934
|
+
(lang) => `label:"help wanted" language:${lang} type:issue state:open`
|
|
920
935
|
)
|
|
921
936
|
];
|
|
922
937
|
CONTRIB_SEARCH_QUERIES = [...CONTRIB_LABEL_QUERIES, ...CONTRIB_LANGUAGE_QUERIES];
|
package/dist/bin/jpi-claim.js
CHANGED
|
@@ -1748,6 +1748,32 @@ async function openPRClosingRefs(owner, name, token, signal, governor) {
|
|
|
1748
1748
|
return null;
|
|
1749
1749
|
}
|
|
1750
1750
|
}
|
|
1751
|
+
async function issueCrossRefPRAttempts(owner, name, issueNumber, token, signal, governor) {
|
|
1752
|
+
const url = `https://api.github.com/repos/${owner}/${name}/issues/${issueNumber}/timeline?per_page=100`;
|
|
1753
|
+
try {
|
|
1754
|
+
const res = governor ? await governor.get(url, { headers: ghHeaders(token), signal }) : await fetch(url, { headers: ghHeaders(token), signal });
|
|
1755
|
+
if (!res || !res.ok) return null;
|
|
1756
|
+
const link = res.headers?.get("link") ?? null;
|
|
1757
|
+
const hasNextPage = link != null && /\brel="next"/.test(link);
|
|
1758
|
+
const events = await res.json();
|
|
1759
|
+
if (!Array.isArray(events)) return null;
|
|
1760
|
+
if (hasNextPage || events.length >= 100) return null;
|
|
1761
|
+
let hasOpenPR = false;
|
|
1762
|
+
let hasClosedPR = false;
|
|
1763
|
+
const prNumbers = [];
|
|
1764
|
+
for (const ev of events) {
|
|
1765
|
+
if (ev?.event !== "cross-referenced") continue;
|
|
1766
|
+
const src = ev.source?.issue;
|
|
1767
|
+
if (!src || src.pull_request == null) continue;
|
|
1768
|
+
if (typeof src.number === "number") prNumbers.push(src.number);
|
|
1769
|
+
if (src.state === "open") hasOpenPR = true;
|
|
1770
|
+
else if (src.state === "closed") hasClosedPR = true;
|
|
1771
|
+
}
|
|
1772
|
+
return { hasOpenPR, hasClosedPR, prNumbers };
|
|
1773
|
+
} catch {
|
|
1774
|
+
return null;
|
|
1775
|
+
}
|
|
1776
|
+
}
|
|
1751
1777
|
function makeScoringGovernor(governor) {
|
|
1752
1778
|
return governor ?? makeGitHubGovernor(
|
|
1753
1779
|
((url, init) => fetch(url, init)),
|
|
@@ -3925,6 +3951,30 @@ var init_feeds = __esm({
|
|
|
3925
3951
|
});
|
|
3926
3952
|
|
|
3927
3953
|
// ../../packages/core/src/feeds/contributions.ts
|
|
3954
|
+
function readSearchMaxPages() {
|
|
3955
|
+
const raw = process.env["CONTRIB_SEARCH_MAX_PAGES"];
|
|
3956
|
+
if (raw == null) return DEFAULT_SEARCH_MAX_PAGES;
|
|
3957
|
+
const n = Number.parseInt(raw, 10);
|
|
3958
|
+
if (Number.isNaN(n)) return DEFAULT_SEARCH_MAX_PAGES;
|
|
3959
|
+
return Math.min(Math.max(n, MIN_SEARCH_MAX_PAGES), MAX_SEARCH_MAX_PAGES);
|
|
3960
|
+
}
|
|
3961
|
+
function readMaxContribItems() {
|
|
3962
|
+
const raw = process.env["CONTRIB_MAX_ITEMS"];
|
|
3963
|
+
if (raw == null) return DEFAULT_MAX_CONTRIB_ITEMS;
|
|
3964
|
+
const n = Number.parseInt(raw, 10);
|
|
3965
|
+
if (Number.isNaN(n)) return DEFAULT_MAX_CONTRIB_ITEMS;
|
|
3966
|
+
return Math.min(Math.max(n, MIN_MAX_CONTRIB_ITEMS), MAX_MAX_CONTRIB_ITEMS);
|
|
3967
|
+
}
|
|
3968
|
+
function readMaxContribIssuesScanned() {
|
|
3969
|
+
const raw = process.env["CONTRIB_MAX_ISSUES_SCANNED"];
|
|
3970
|
+
if (raw == null) return DEFAULT_MAX_CONTRIB_ISSUES_SCANNED;
|
|
3971
|
+
const n = Number.parseInt(raw, 10);
|
|
3972
|
+
if (Number.isNaN(n)) return DEFAULT_MAX_CONTRIB_ISSUES_SCANNED;
|
|
3973
|
+
return Math.min(
|
|
3974
|
+
Math.max(n, MIN_MAX_CONTRIB_ISSUES_SCANNED),
|
|
3975
|
+
MAX_MAX_CONTRIB_ISSUES_SCANNED
|
|
3976
|
+
);
|
|
3977
|
+
}
|
|
3928
3978
|
function authHeaders2() {
|
|
3929
3979
|
const token = process.env["GITHUB_TOKEN"] ?? process.env["GH_TOKEN"];
|
|
3930
3980
|
const h = {
|
|
@@ -4012,13 +4062,21 @@ async function fetchRateLimit(client) {
|
|
|
4012
4062
|
}
|
|
4013
4063
|
async function searchContribIssues(client, queries) {
|
|
4014
4064
|
const byUrl = /* @__PURE__ */ new Map();
|
|
4065
|
+
const maxPages = readSearchMaxPages();
|
|
4015
4066
|
for (const q of queries) {
|
|
4016
|
-
|
|
4017
|
-
|
|
4018
|
-
|
|
4019
|
-
|
|
4020
|
-
|
|
4021
|
-
if (
|
|
4067
|
+
for (let page = 1; page <= maxPages; page++) {
|
|
4068
|
+
const res = await client.json(
|
|
4069
|
+
`/search/issues?q=${encodeURIComponent(q)}&sort=created&order=desc&per_page=${SEARCH_PER_PAGE2}&page=${page}`
|
|
4070
|
+
);
|
|
4071
|
+
const items = res?.items;
|
|
4072
|
+
if (items == null) break;
|
|
4073
|
+
for (const it of items) {
|
|
4074
|
+
if (it.pull_request) continue;
|
|
4075
|
+
if (!byUrl.has(it.html_url)) byUrl.set(it.html_url, it);
|
|
4076
|
+
}
|
|
4077
|
+
if (items.length < SEARCH_PER_PAGE2) break;
|
|
4078
|
+
const stats = client.getStats();
|
|
4079
|
+
if (stats.budgetAborted || stats.secondaryAborted) break;
|
|
4022
4080
|
}
|
|
4023
4081
|
}
|
|
4024
4082
|
return [...byUrl.values()].sort(
|
|
@@ -4079,10 +4137,14 @@ async function aggregateContributions(opts = {}) {
|
|
|
4079
4137
|
probeTimeoutMs: opts.fetchImpl ? null : PROBE_TIMEOUT_MS
|
|
4080
4138
|
});
|
|
4081
4139
|
const queries = opts.queries ?? CONTRIB_SEARCH_QUERIES;
|
|
4140
|
+
const maxContribItems = readMaxContribItems();
|
|
4082
4141
|
const startRl = await fetchRateLimit(client);
|
|
4083
4142
|
const coreHealthyAtStart = (startRl?.core?.remaining ?? 0) >= 500;
|
|
4084
4143
|
client.setSecondaryHint(coreHealthyAtStart);
|
|
4085
|
-
const issues = (await searchContribIssues(client, queries)).slice(
|
|
4144
|
+
const issues = (await searchContribIssues(client, queries)).slice(
|
|
4145
|
+
0,
|
|
4146
|
+
readMaxContribIssuesScanned()
|
|
4147
|
+
);
|
|
4086
4148
|
const repoCache = /* @__PURE__ */ new Map();
|
|
4087
4149
|
const contribCache = /* @__PURE__ */ new Map();
|
|
4088
4150
|
const prRefsCache = /* @__PURE__ */ new Map();
|
|
@@ -4172,7 +4234,7 @@ async function aggregateContributions(opts = {}) {
|
|
|
4172
4234
|
let contribUndefined = 0;
|
|
4173
4235
|
let prRefsNull = 0;
|
|
4174
4236
|
for (const issue of issues) {
|
|
4175
|
-
if (jobs.length >=
|
|
4237
|
+
if (jobs.length >= maxContribItems) break;
|
|
4176
4238
|
const fullName = repoFullNameFromApiUrl2(issue.repository_url);
|
|
4177
4239
|
if (!fullName) continue;
|
|
4178
4240
|
const id = `contribute:${repoKey(fullName)}#${issue.number}`;
|
|
@@ -4230,7 +4292,7 @@ async function aggregateContributions(opts = {}) {
|
|
|
4230
4292
|
const doDiscovery = opts.discoverRepos ?? (opts.trendingSlugs != null || opts.vocabTerms != null);
|
|
4231
4293
|
let discoveredEmitted = 0;
|
|
4232
4294
|
let discoveryBudgetStopped = false;
|
|
4233
|
-
if (doDiscovery && jobs.length <
|
|
4295
|
+
if (doDiscovery && jobs.length < maxContribItems) {
|
|
4234
4296
|
const maxRepos = Math.min(
|
|
4235
4297
|
Math.max(0, opts.maxDiscoveredRepos ?? MAX_DISCOVERED_REPOS),
|
|
4236
4298
|
MAX_DISCOVERED_REPOS
|
|
@@ -4283,7 +4345,7 @@ async function aggregateContributions(opts = {}) {
|
|
|
4283
4345
|
}
|
|
4284
4346
|
const scanned = candidates.slice(0, maxRepos);
|
|
4285
4347
|
for (const fullName of scanned) {
|
|
4286
|
-
if (jobs.length >=
|
|
4348
|
+
if (jobs.length >= maxContribItems) break;
|
|
4287
4349
|
if (client.getStats().budgetAborted || client.getStats().secondaryAborted) {
|
|
4288
4350
|
discoveryBudgetStopped = true;
|
|
4289
4351
|
break;
|
|
@@ -4308,7 +4370,7 @@ async function aggregateContributions(opts = {}) {
|
|
|
4308
4370
|
const repoIssues = (searchRes?.items ?? []).filter((it) => !it.pull_request);
|
|
4309
4371
|
let perRepoDiscovered = 0;
|
|
4310
4372
|
for (const issue of repoIssues) {
|
|
4311
|
-
if (jobs.length >=
|
|
4373
|
+
if (jobs.length >= maxContribItems) break;
|
|
4312
4374
|
if (perRepoDiscovered >= MAX_ISSUES_PER_DISCOVERED_REPO) break;
|
|
4313
4375
|
if ((perRepo.get(repoKey(fullName)) ?? 0) >= MAX_BOUNTIES_PER_DISCOVERED_REPO) break;
|
|
4314
4376
|
const id = `contribute:${repoKey(fullName)}#${issue.number}`;
|
|
@@ -4373,7 +4435,7 @@ async function aggregateContributions(opts = {}) {
|
|
|
4373
4435
|
}
|
|
4374
4436
|
return jobs;
|
|
4375
4437
|
}
|
|
4376
|
-
var GITHUB_API2, CONTRIB_LABEL_QUERIES, CONTRIB_LANGUAGE_QUERIES, CONTRIB_SEARCH_QUERIES, SEARCH_PER_PAGE2,
|
|
4438
|
+
var GITHUB_API2, CONTRIB_LABEL_QUERIES, CONTRIB_LANGUAGE_QUERIES, CONTRIB_SEARCH_QUERIES, SEARCH_PER_PAGE2, DEFAULT_SEARCH_MAX_PAGES, MIN_SEARCH_MAX_PAGES, MAX_SEARCH_MAX_PAGES, DEFAULT_MAX_CONTRIB_ITEMS, MIN_MAX_CONTRIB_ITEMS, MAX_MAX_CONTRIB_ITEMS, DEFAULT_MAX_CONTRIB_ISSUES_SCANNED, MIN_MAX_CONTRIB_ISSUES_SCANNED, MAX_MAX_CONTRIB_ISSUES_SCANNED, MAX_DISCOVERED_REPOS, MAX_ISSUES_PER_DISCOVERED_REPO, DISCOVERY_REPOS_PER_TERM, DISCOVERY_VOCAB_TERMS, DISCOVERY_ISSUE_LABELS, repoKey;
|
|
4377
4439
|
var init_contributions = __esm({
|
|
4378
4440
|
"../../packages/core/src/feeds/contributions.ts"() {
|
|
4379
4441
|
"use strict";
|
|
@@ -4392,7 +4454,11 @@ var init_contributions = __esm({
|
|
|
4392
4454
|
'label:"good-first-issue" type:issue state:open',
|
|
4393
4455
|
'label:"help wanted" type:issue state:open',
|
|
4394
4456
|
'label:"help-wanted" type:issue state:open',
|
|
4395
|
-
'label:"up-for-grabs" type:issue state:open'
|
|
4457
|
+
'label:"up-for-grabs" type:issue state:open',
|
|
4458
|
+
// supply-expansion D: two more first-contribution label families widen the
|
|
4459
|
+
// global newest-first slice WITHOUT relaxing the credential gate.
|
|
4460
|
+
'label:"beginner-friendly" type:issue state:open',
|
|
4461
|
+
'label:"first-timers-only" type:issue state:open'
|
|
4396
4462
|
];
|
|
4397
4463
|
CONTRIB_LANGUAGE_QUERIES = [
|
|
4398
4464
|
...["rust", "go", "python", "c++", "ruby"].map(
|
|
@@ -4400,12 +4466,30 @@ var init_contributions = __esm({
|
|
|
4400
4466
|
),
|
|
4401
4467
|
...["rust", "go"].map(
|
|
4402
4468
|
(lang) => `label:"good first issue" language:${lang} type:issue state:open`
|
|
4469
|
+
),
|
|
4470
|
+
// supply-expansion D: cover the high-volume web/enterprise ecosystems the
|
|
4471
|
+
// original set omitted. TS/JS were previously left out of "good first issue"
|
|
4472
|
+
// (the global slice over-represented them) but a LANGUAGE-scoped page surfaces
|
|
4473
|
+
// DIFFERENT repos than the global newest-first slice, so re-including them widens
|
|
4474
|
+
// distinct-repo coverage rather than duplicating it.
|
|
4475
|
+
...["typescript", "javascript", "java", "python"].map(
|
|
4476
|
+
(lang) => `label:"good first issue" language:${lang} type:issue state:open`
|
|
4477
|
+
),
|
|
4478
|
+
...["typescript", "javascript", "c#", "php"].map(
|
|
4479
|
+
(lang) => `label:"help wanted" language:${lang} type:issue state:open`
|
|
4403
4480
|
)
|
|
4404
4481
|
];
|
|
4405
4482
|
CONTRIB_SEARCH_QUERIES = [...CONTRIB_LABEL_QUERIES, ...CONTRIB_LANGUAGE_QUERIES];
|
|
4406
4483
|
SEARCH_PER_PAGE2 = 100;
|
|
4407
|
-
|
|
4408
|
-
|
|
4484
|
+
DEFAULT_SEARCH_MAX_PAGES = 1;
|
|
4485
|
+
MIN_SEARCH_MAX_PAGES = 1;
|
|
4486
|
+
MAX_SEARCH_MAX_PAGES = 5;
|
|
4487
|
+
DEFAULT_MAX_CONTRIB_ITEMS = 400;
|
|
4488
|
+
MIN_MAX_CONTRIB_ITEMS = 50;
|
|
4489
|
+
MAX_MAX_CONTRIB_ITEMS = 1e3;
|
|
4490
|
+
DEFAULT_MAX_CONTRIB_ISSUES_SCANNED = 1500;
|
|
4491
|
+
MIN_MAX_CONTRIB_ISSUES_SCANNED = 100;
|
|
4492
|
+
MAX_MAX_CONTRIB_ISSUES_SCANNED = 5e3;
|
|
4409
4493
|
MAX_DISCOVERED_REPOS = 15;
|
|
4410
4494
|
MAX_ISSUES_PER_DISCOVERED_REPO = 3;
|
|
4411
4495
|
DISCOVERY_REPOS_PER_TERM = 20;
|
|
@@ -8387,6 +8471,7 @@ __export(src_exports, {
|
|
|
8387
8471
|
isOverIntroLimit: () => isOverIntroLimit,
|
|
8388
8472
|
isTrivialPRTitle: () => isTrivialPRTitle,
|
|
8389
8473
|
isWinnableIssue: () => isWinnableIssue,
|
|
8474
|
+
issueCrossRefPRAttempts: () => issueCrossRefPRAttempts,
|
|
8390
8475
|
jobShortToken: () => jobShortToken,
|
|
8391
8476
|
joinLabels: () => joinLabels,
|
|
8392
8477
|
labelFor: () => labelFor,
|
|
@@ -1280,7 +1280,11 @@ var init_contributions = __esm({
|
|
|
1280
1280
|
'label:"good-first-issue" type:issue state:open',
|
|
1281
1281
|
'label:"help wanted" type:issue state:open',
|
|
1282
1282
|
'label:"help-wanted" type:issue state:open',
|
|
1283
|
-
'label:"up-for-grabs" type:issue state:open'
|
|
1283
|
+
'label:"up-for-grabs" type:issue state:open',
|
|
1284
|
+
// supply-expansion D: two more first-contribution label families widen the
|
|
1285
|
+
// global newest-first slice WITHOUT relaxing the credential gate.
|
|
1286
|
+
'label:"beginner-friendly" type:issue state:open',
|
|
1287
|
+
'label:"first-timers-only" type:issue state:open'
|
|
1284
1288
|
];
|
|
1285
1289
|
CONTRIB_LANGUAGE_QUERIES = [
|
|
1286
1290
|
...["rust", "go", "python", "c++", "ruby"].map(
|
|
@@ -1288,6 +1292,17 @@ var init_contributions = __esm({
|
|
|
1288
1292
|
),
|
|
1289
1293
|
...["rust", "go"].map(
|
|
1290
1294
|
(lang) => `label:"good first issue" language:${lang} type:issue state:open`
|
|
1295
|
+
),
|
|
1296
|
+
// supply-expansion D: cover the high-volume web/enterprise ecosystems the
|
|
1297
|
+
// original set omitted. TS/JS were previously left out of "good first issue"
|
|
1298
|
+
// (the global slice over-represented them) but a LANGUAGE-scoped page surfaces
|
|
1299
|
+
// DIFFERENT repos than the global newest-first slice, so re-including them widens
|
|
1300
|
+
// distinct-repo coverage rather than duplicating it.
|
|
1301
|
+
...["typescript", "javascript", "java", "python"].map(
|
|
1302
|
+
(lang) => `label:"good first issue" language:${lang} type:issue state:open`
|
|
1303
|
+
),
|
|
1304
|
+
...["typescript", "javascript", "c#", "php"].map(
|
|
1305
|
+
(lang) => `label:"help wanted" language:${lang} type:issue state:open`
|
|
1291
1306
|
)
|
|
1292
1307
|
];
|
|
1293
1308
|
CONTRIB_SEARCH_QUERIES = [...CONTRIB_LABEL_QUERIES, ...CONTRIB_LANGUAGE_QUERIES];
|