terminalhire 0.26.0 → 0.26.2
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/claim-push-bg.js +2 -2
- package/dist/bin/jpi-bounties.js +375 -171
- package/dist/bin/jpi-chat-read.js +24 -2
- package/dist/bin/jpi-chat.js +24 -2
- package/dist/bin/jpi-claim.js +399 -174
- package/dist/bin/jpi-contribute.js +67 -24
- package/dist/bin/jpi-devs.js +375 -171
- package/dist/bin/jpi-dispatch.js +411 -176
- package/dist/bin/jpi-hub.js +375 -171
- package/dist/bin/jpi-inbox.js +24 -2
- package/dist/bin/jpi-init.js +373 -169
- package/dist/bin/jpi-intro.js +24 -2
- package/dist/bin/jpi-jobs.js +375 -171
- package/dist/bin/jpi-learn.js +24 -2
- package/dist/bin/jpi-login.js +375 -171
- package/dist/bin/jpi-profile.js +24 -2
- package/dist/bin/jpi-project.js +373 -169
- package/dist/bin/jpi-refresh.js +375 -171
- package/dist/bin/jpi-save.js +24 -2
- package/dist/bin/jpi-spinner.js +12 -0
- package/dist/bin/jpi-sync.js +24 -2
- package/dist/bin/jpi-trajectory.js +24 -2
- package/dist/bin/spinner.js +12 -0
- package/dist/src/chat-client.js +14 -2
- package/dist/src/chat-keystore.js +14 -2
- package/dist/src/github-auth.js +2 -2
- package/dist/src/intro.js +24 -2
- package/dist/src/profile.js +14 -2
- package/dist/src/signal.js +12 -0
- package/dist/src/trajectory.js +24 -2
- package/package.json +1 -1
package/dist/bin/jpi-init.js
CHANGED
|
@@ -826,6 +826,187 @@ var init_rigor = __esm({
|
|
|
826
826
|
}
|
|
827
827
|
});
|
|
828
828
|
|
|
829
|
+
// ../../packages/core/src/gh-governor.ts
|
|
830
|
+
function readReqGapMs() {
|
|
831
|
+
const raw = process.env["CONTRIB_REQ_GAP_MS"];
|
|
832
|
+
if (raw == null) return DEFAULT_REQ_GAP_MS;
|
|
833
|
+
const n = Number.parseInt(raw, 10);
|
|
834
|
+
if (Number.isNaN(n)) return DEFAULT_REQ_GAP_MS;
|
|
835
|
+
return Math.min(Math.max(n, 0), 1e3);
|
|
836
|
+
}
|
|
837
|
+
function readBuildBudgetMs() {
|
|
838
|
+
const raw = process.env["CONTRIB_BUILD_BUDGET_MS"];
|
|
839
|
+
if (raw == null) return DEFAULT_BUILD_BUDGET_MS;
|
|
840
|
+
const n = Number.parseInt(raw, 10);
|
|
841
|
+
if (Number.isNaN(n)) return DEFAULT_BUILD_BUDGET_MS;
|
|
842
|
+
return Math.min(Math.max(n, MIN_BUILD_BUDGET_MS), MAX_BUILD_BUDGET_MS);
|
|
843
|
+
}
|
|
844
|
+
function makeDefaultGovernorConfig(o) {
|
|
845
|
+
return {
|
|
846
|
+
paceEnabled: o.paceEnabled,
|
|
847
|
+
gapMs: readReqGapMs(),
|
|
848
|
+
budgetMs: readBuildBudgetMs(),
|
|
849
|
+
sleep: o.sleep ?? realSleep,
|
|
850
|
+
now: o.now ?? Date.now,
|
|
851
|
+
probeTimeoutMs: o.probeTimeoutMs ?? (o.paceEnabled ? PROBE_TIMEOUT_MS : null)
|
|
852
|
+
};
|
|
853
|
+
}
|
|
854
|
+
function makeGitHubGovernor(fetchImpl, cfg) {
|
|
855
|
+
const startedAt = cfg.now();
|
|
856
|
+
let lastRequestAt = 0;
|
|
857
|
+
let pacedMs = 0;
|
|
858
|
+
let secondaryHits = 0;
|
|
859
|
+
let aborted = false;
|
|
860
|
+
let secondaryAborted = false;
|
|
861
|
+
let budgetAborted = false;
|
|
862
|
+
let gqlCost = 0;
|
|
863
|
+
let gqlRemaining = null;
|
|
864
|
+
let coreHealthyAtStart = false;
|
|
865
|
+
async function noteAndMaybeBackOff(res) {
|
|
866
|
+
if (res.status !== 403) return;
|
|
867
|
+
const remaining = res.headers.get("x-ratelimit-remaining");
|
|
868
|
+
const retryAfter = res.headers.get("retry-after");
|
|
869
|
+
const positiveSecondary = retryAfter != null || remaining != null && remaining !== "0";
|
|
870
|
+
const isSecondary = positiveSecondary || coreHealthyAtStart;
|
|
871
|
+
if (!isSecondary) return;
|
|
872
|
+
await recordSecondaryStrike(retryAfter);
|
|
873
|
+
}
|
|
874
|
+
async function recordSecondaryStrike(retryAfter) {
|
|
875
|
+
secondaryHits++;
|
|
876
|
+
if (secondaryHits >= 2) {
|
|
877
|
+
aborted = true;
|
|
878
|
+
secondaryAborted = true;
|
|
879
|
+
return;
|
|
880
|
+
}
|
|
881
|
+
if (cfg.paceEnabled) {
|
|
882
|
+
const trimmed = (retryAfter ?? "").trim();
|
|
883
|
+
const parsed = trimmed.length === 0 ? Number.NaN : Number(trimmed);
|
|
884
|
+
const sec = Number.isNaN(parsed) ? SECONDARY_BACKOFF_CAP_S : Math.min(Math.max(parsed, 0), SECONDARY_BACKOFF_CAP_S);
|
|
885
|
+
const remainingBudget = Math.max(0, cfg.budgetMs - (cfg.now() - startedAt));
|
|
886
|
+
await cfg.sleep(Math.min(sec * 1e3, remainingBudget));
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
async function noteGraphQLAndMaybeBackOff(res, body) {
|
|
890
|
+
const b = body ?? {};
|
|
891
|
+
const rl = b.data?.rateLimit;
|
|
892
|
+
if (rl) {
|
|
893
|
+
if (typeof rl.cost === "number") gqlCost += rl.cost;
|
|
894
|
+
if (typeof rl.remaining === "number") gqlRemaining = rl.remaining;
|
|
895
|
+
}
|
|
896
|
+
const remainingHdr = res.headers?.get("x-ratelimit-remaining") ?? null;
|
|
897
|
+
const retryAfter = res.headers?.get("retry-after") ?? null;
|
|
898
|
+
const headerRate = retryAfter != null || remainingHdr === "0";
|
|
899
|
+
const errs = Array.isArray(b.errors) ? b.errors : [];
|
|
900
|
+
const bodyRate = errs.some((e) => e?.type === "RATE_LIMITED" || /rate limit/i.test(String(e?.message ?? ""))) || typeof rl?.remaining === "number" && rl.remaining <= 0;
|
|
901
|
+
if (headerRate || bodyRate) await recordSecondaryStrike(retryAfter);
|
|
902
|
+
}
|
|
903
|
+
async function preflight() {
|
|
904
|
+
if (aborted) return false;
|
|
905
|
+
if (cfg.now() - startedAt >= cfg.budgetMs) {
|
|
906
|
+
aborted = true;
|
|
907
|
+
budgetAborted = true;
|
|
908
|
+
return false;
|
|
909
|
+
}
|
|
910
|
+
if (cfg.paceEnabled && cfg.gapMs > 0) {
|
|
911
|
+
const wait = cfg.gapMs - (cfg.now() - lastRequestAt);
|
|
912
|
+
if (wait > 0) {
|
|
913
|
+
await cfg.sleep(wait);
|
|
914
|
+
pacedMs += wait;
|
|
915
|
+
if (cfg.now() - startedAt >= cfg.budgetMs) {
|
|
916
|
+
aborted = true;
|
|
917
|
+
budgetAborted = true;
|
|
918
|
+
return false;
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
lastRequestAt = cfg.now();
|
|
922
|
+
}
|
|
923
|
+
return true;
|
|
924
|
+
}
|
|
925
|
+
async function get(url, init) {
|
|
926
|
+
if (!await preflight()) return null;
|
|
927
|
+
try {
|
|
928
|
+
const res = await fetchImpl(url, init);
|
|
929
|
+
await noteAndMaybeBackOff(res);
|
|
930
|
+
return res;
|
|
931
|
+
} catch {
|
|
932
|
+
return null;
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
async function graphql(url, init) {
|
|
936
|
+
if (!await preflight()) return null;
|
|
937
|
+
let res;
|
|
938
|
+
try {
|
|
939
|
+
res = await fetchImpl(url, init);
|
|
940
|
+
} catch {
|
|
941
|
+
return null;
|
|
942
|
+
}
|
|
943
|
+
let body;
|
|
944
|
+
try {
|
|
945
|
+
body = await res.json();
|
|
946
|
+
} catch {
|
|
947
|
+
return null;
|
|
948
|
+
}
|
|
949
|
+
await noteGraphQLAndMaybeBackOff(res, body);
|
|
950
|
+
if (!res.ok) return null;
|
|
951
|
+
return body;
|
|
952
|
+
}
|
|
953
|
+
async function probe(url, init) {
|
|
954
|
+
const bound = cfg.probeTimeoutMs;
|
|
955
|
+
let timer;
|
|
956
|
+
const fetchP = fetchImpl(url, init).then(
|
|
957
|
+
(r) => r,
|
|
958
|
+
() => null
|
|
959
|
+
);
|
|
960
|
+
try {
|
|
961
|
+
const res = bound == null ? await fetchP : await Promise.race([
|
|
962
|
+
fetchP,
|
|
963
|
+
new Promise((resolve2) => {
|
|
964
|
+
timer = setTimeout(() => resolve2(null), bound);
|
|
965
|
+
})
|
|
966
|
+
]);
|
|
967
|
+
if (!res || !res.ok) return null;
|
|
968
|
+
return await res.json();
|
|
969
|
+
} catch {
|
|
970
|
+
return null;
|
|
971
|
+
} finally {
|
|
972
|
+
if (timer) clearTimeout(timer);
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
function setSecondaryHint(coreHealthy) {
|
|
976
|
+
coreHealthyAtStart = coreHealthy;
|
|
977
|
+
}
|
|
978
|
+
function getStats() {
|
|
979
|
+
return {
|
|
980
|
+
pacedMs,
|
|
981
|
+
secondaryAborted: secondaryAborted ? 1 : 0,
|
|
982
|
+
budgetAborted: budgetAborted ? 1 : 0,
|
|
983
|
+
elapsedMs: cfg.now() - startedAt,
|
|
984
|
+
gqlCost,
|
|
985
|
+
gqlRemaining
|
|
986
|
+
};
|
|
987
|
+
}
|
|
988
|
+
function tripped() {
|
|
989
|
+
return secondaryAborted;
|
|
990
|
+
}
|
|
991
|
+
function budgetExhausted() {
|
|
992
|
+
return budgetAborted || cfg.now() - startedAt >= cfg.budgetMs;
|
|
993
|
+
}
|
|
994
|
+
return { get, graphql, probe, setSecondaryHint, getStats, tripped, budgetExhausted };
|
|
995
|
+
}
|
|
996
|
+
var DEFAULT_REQ_GAP_MS, SECONDARY_BACKOFF_CAP_S, DEFAULT_BUILD_BUDGET_MS, MIN_BUILD_BUDGET_MS, MAX_BUILD_BUDGET_MS, PROBE_TIMEOUT_MS, realSleep;
|
|
997
|
+
var init_gh_governor = __esm({
|
|
998
|
+
"../../packages/core/src/gh-governor.ts"() {
|
|
999
|
+
"use strict";
|
|
1000
|
+
DEFAULT_REQ_GAP_MS = 75;
|
|
1001
|
+
SECONDARY_BACKOFF_CAP_S = 30;
|
|
1002
|
+
DEFAULT_BUILD_BUDGET_MS = 9e4;
|
|
1003
|
+
MIN_BUILD_BUDGET_MS = 1e4;
|
|
1004
|
+
MAX_BUILD_BUDGET_MS = 9e4;
|
|
1005
|
+
PROBE_TIMEOUT_MS = 3e3;
|
|
1006
|
+
realSleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
1007
|
+
}
|
|
1008
|
+
});
|
|
1009
|
+
|
|
829
1010
|
// ../../packages/core/src/github.ts
|
|
830
1011
|
function ghHeaders(token) {
|
|
831
1012
|
const headers = {
|
|
@@ -1306,11 +1487,9 @@ async function fetchRepoReceptivity(fullName, opts = {}) {
|
|
|
1306
1487
|
const doFetch = opts.fetchImpl ?? fetch;
|
|
1307
1488
|
const now = opts.now ?? Date.now();
|
|
1308
1489
|
try {
|
|
1309
|
-
const
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
);
|
|
1313
|
-
if (!res.ok) return null;
|
|
1490
|
+
const url = `https://api.github.com/repos/${fullName}/pulls?state=closed&per_page=50&sort=updated&direction=desc`;
|
|
1491
|
+
const res = opts.governor ? await opts.governor.get(url, { headers: ghHeaders(opts.token) }) : await doFetch(url, { headers: ghHeaders(opts.token) });
|
|
1492
|
+
if (!res || !res.ok) return null;
|
|
1314
1493
|
const prs = await res.json();
|
|
1315
1494
|
if (!Array.isArray(prs)) return null;
|
|
1316
1495
|
const external = prs.filter(isExternalAuthor);
|
|
@@ -1395,25 +1574,34 @@ function parseGitHubRef(url) {
|
|
|
1395
1574
|
if (!m) return null;
|
|
1396
1575
|
return { owner: m[1], repo: m[2], number: parseInt(m[4], 10), kind: m[3] === "pull" ? "pull" : "issue" };
|
|
1397
1576
|
}
|
|
1398
|
-
async function ghGraphQL(query, variables, token, signal) {
|
|
1399
|
-
const
|
|
1577
|
+
async function ghGraphQL(query, variables, token, signal, governor) {
|
|
1578
|
+
const init = {
|
|
1400
1579
|
method: "POST",
|
|
1401
1580
|
headers: { ...ghHeaders(token), "Content-Type": "application/json" },
|
|
1402
1581
|
body: JSON.stringify({ query, variables }),
|
|
1403
1582
|
signal
|
|
1404
|
-
}
|
|
1583
|
+
};
|
|
1584
|
+
if (governor) {
|
|
1585
|
+
const json2 = await governor.graphql(GITHUB_GRAPHQL_URL, init);
|
|
1586
|
+
if (json2 === null) return null;
|
|
1587
|
+
if (json2.errors?.length) throw new Error("GitHub GraphQL errors: " + JSON.stringify(json2.errors));
|
|
1588
|
+
return json2;
|
|
1589
|
+
}
|
|
1590
|
+
const res = await fetch(GITHUB_GRAPHQL_URL, init);
|
|
1405
1591
|
if (!res.ok) throw new Error(`GitHub GraphQL: HTTP ${res.status}`);
|
|
1406
1592
|
const json = await res.json();
|
|
1407
1593
|
if (json.errors?.length) throw new Error("GitHub GraphQL errors: " + JSON.stringify(json.errors));
|
|
1408
1594
|
return json;
|
|
1409
1595
|
}
|
|
1410
|
-
async function resolveClosingIssues(owner, name, number, body, token, signal) {
|
|
1596
|
+
async function resolveClosingIssues(owner, name, number, body, token, signal, governor) {
|
|
1411
1597
|
if (token) {
|
|
1412
1598
|
try {
|
|
1413
|
-
const q = `query($o:String!,$n:String!,$p:Int!){repository(owner:$o,name:$n){pullRequest(number:$p){closingIssuesReferences(first:20){nodes{number}}}}}`;
|
|
1414
|
-
const r = await ghGraphQL(q, { o: owner, n: name, p: number }, token, signal);
|
|
1415
|
-
|
|
1416
|
-
|
|
1599
|
+
const q = `query($o:String!,$n:String!,$p:Int!){repository(owner:$o,name:$n){pullRequest(number:$p){closingIssuesReferences(first:20){nodes{number}}}}rateLimit{cost remaining}}`;
|
|
1600
|
+
const r = await ghGraphQL(q, { o: owner, n: name, p: number }, token, signal, governor);
|
|
1601
|
+
if (r) {
|
|
1602
|
+
const nodes = r.data?.repository?.pullRequest?.closingIssuesReferences?.nodes ?? [];
|
|
1603
|
+
return { closesIssues: nodes.map((x) => x.number), linkageSource: "graphql" };
|
|
1604
|
+
}
|
|
1417
1605
|
} catch {
|
|
1418
1606
|
}
|
|
1419
1607
|
}
|
|
@@ -1423,11 +1611,19 @@ async function resolveClosingIssues(owner, name, number, body, token, signal) {
|
|
|
1423
1611
|
while ((m = re.exec(body)) !== null) nums.add(parseInt(m[1], 10));
|
|
1424
1612
|
return { closesIssues: [...nums], linkageSource: nums.size ? "body-keyword" : "none" };
|
|
1425
1613
|
}
|
|
1426
|
-
|
|
1614
|
+
function makeScoringGovernor(governor) {
|
|
1615
|
+
return governor ?? makeGitHubGovernor(
|
|
1616
|
+
((url, init) => fetch(url, init)),
|
|
1617
|
+
makeDefaultGovernorConfig({ paceEnabled: false })
|
|
1618
|
+
);
|
|
1619
|
+
}
|
|
1620
|
+
async function fetchPRScoringFacts(prUrl, token, signal, governor) {
|
|
1427
1621
|
const ref = parseGitHubRef(prUrl);
|
|
1428
1622
|
if (!ref || ref.kind !== "pull") return null;
|
|
1429
1623
|
const { owner, repo, number } = ref;
|
|
1430
1624
|
const sig = signal ?? AbortSignal.timeout(1e4);
|
|
1625
|
+
const gov = makeScoringGovernor(governor);
|
|
1626
|
+
if (gov.tripped() || gov.budgetExhausted()) return null;
|
|
1431
1627
|
let pr;
|
|
1432
1628
|
try {
|
|
1433
1629
|
pr = await ghFetch(`/repos/${owner}/${repo}/pulls/${number}`, token, sig);
|
|
@@ -1435,22 +1631,26 @@ async function fetchPRScoringFacts(prUrl, token, signal) {
|
|
|
1435
1631
|
return null;
|
|
1436
1632
|
}
|
|
1437
1633
|
let repoMeta = null;
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1634
|
+
if (!gov.tripped() && !gov.budgetExhausted()) {
|
|
1635
|
+
try {
|
|
1636
|
+
repoMeta = await ghFetch(`/repos/${owner}/${repo}`, token, sig);
|
|
1637
|
+
} catch {
|
|
1638
|
+
}
|
|
1441
1639
|
}
|
|
1442
|
-
const contributors = await repoContributorCount(owner, repo, token, sig);
|
|
1443
|
-
const { closesIssues, linkageSource } = await resolveClosingIssues(owner, repo, number, pr.body ?? "", token, sig);
|
|
1640
|
+
const contributors = gov.tripped() || gov.budgetExhausted() ? null : await repoContributorCount(owner, repo, token, sig);
|
|
1641
|
+
const { closesIssues, linkageSource } = await resolveClosingIssues(owner, repo, number, pr.body ?? "", token, sig, gov);
|
|
1444
1642
|
let reviewerAssociations;
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1643
|
+
if (!gov.tripped() && !gov.budgetExhausted()) {
|
|
1644
|
+
try {
|
|
1645
|
+
const reviews = await ghFetch(
|
|
1646
|
+
`/repos/${owner}/${repo}/pulls/${number}/reviews?per_page=100`,
|
|
1647
|
+
token,
|
|
1648
|
+
sig
|
|
1649
|
+
);
|
|
1650
|
+
reviewerAssociations = reviews.map((r) => r.author_association);
|
|
1651
|
+
} catch {
|
|
1652
|
+
reviewerAssociations = void 0;
|
|
1653
|
+
}
|
|
1454
1654
|
}
|
|
1455
1655
|
return {
|
|
1456
1656
|
repo: `${owner}/${repo}`,
|
|
@@ -1477,7 +1677,7 @@ async function fetchPRScoringFacts(prUrl, token, signal) {
|
|
|
1477
1677
|
fetchedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1478
1678
|
};
|
|
1479
1679
|
}
|
|
1480
|
-
var TRACTION_TOP_N, MAINTAINER_ENRICH_MAX, CANDIDATE_PR_PAGE, MAX_ENRICH_PRS, OPEN_PR_PAGE, TRANSIENT_META_ERROR, RESUME_DECAY_HALF_LIFE_MS, RESUME_MIN_SCORE, RECEPTIVITY_RECENCY_DAYS, RECEPTIVITY_RECENCY_FLOOR;
|
|
1680
|
+
var TRACTION_TOP_N, MAINTAINER_ENRICH_MAX, CANDIDATE_PR_PAGE, MAX_ENRICH_PRS, OPEN_PR_PAGE, TRANSIENT_META_ERROR, RESUME_DECAY_HALF_LIFE_MS, RESUME_MIN_SCORE, RECEPTIVITY_RECENCY_DAYS, RECEPTIVITY_RECENCY_FLOOR, GITHUB_GRAPHQL_URL;
|
|
1481
1681
|
var init_github = __esm({
|
|
1482
1682
|
"../../packages/core/src/github.ts"() {
|
|
1483
1683
|
"use strict";
|
|
@@ -1485,6 +1685,7 @@ var init_github = __esm({
|
|
|
1485
1685
|
init_contribution_gate();
|
|
1486
1686
|
init_contribution_gate();
|
|
1487
1687
|
init_rigor();
|
|
1688
|
+
init_gh_governor();
|
|
1488
1689
|
TRACTION_TOP_N = 6;
|
|
1489
1690
|
MAINTAINER_ENRICH_MAX = 25;
|
|
1490
1691
|
CANDIDATE_PR_PAGE = 50;
|
|
@@ -1495,6 +1696,7 @@ var init_github = __esm({
|
|
|
1495
1696
|
RESUME_MIN_SCORE = 0.05;
|
|
1496
1697
|
RECEPTIVITY_RECENCY_DAYS = 180;
|
|
1497
1698
|
RECEPTIVITY_RECENCY_FLOOR = 0.1;
|
|
1699
|
+
GITHUB_GRAPHQL_URL = "https://api.github.com/graphql";
|
|
1498
1700
|
}
|
|
1499
1701
|
});
|
|
1500
1702
|
|
|
@@ -1575,34 +1777,45 @@ function mergeSoftCoverage(covMap, softTags, cap) {
|
|
|
1575
1777
|
}
|
|
1576
1778
|
return covMap;
|
|
1577
1779
|
}
|
|
1780
|
+
function tagKernel(job, ctx) {
|
|
1781
|
+
const { expanded, covMap, maxDevScore, skillTags } = ctx;
|
|
1782
|
+
const details = [];
|
|
1783
|
+
let jobMatchScore = 0;
|
|
1784
|
+
let jobMaxScore = 0;
|
|
1785
|
+
const devCovByTag = /* @__PURE__ */ new Map();
|
|
1786
|
+
for (const tag of job.tags) {
|
|
1787
|
+
const w = backgroundIdf(tag);
|
|
1788
|
+
jobMaxScore += w;
|
|
1789
|
+
const covHit = covMap.get(tag);
|
|
1790
|
+
if (covHit) jobMatchScore += w * Math.pow(covHit.weight, SHARPEN);
|
|
1791
|
+
const fpHit = expanded.get(tag);
|
|
1792
|
+
if (fpHit) {
|
|
1793
|
+
const credit = Math.pow(fpHit.weight, SHARPEN);
|
|
1794
|
+
details.push({ tag, weight: fpHit.weight, via: fpHit.via });
|
|
1795
|
+
if (credit > (devCovByTag.get(fpHit.via) ?? 0)) devCovByTag.set(fpHit.via, credit);
|
|
1796
|
+
}
|
|
1797
|
+
}
|
|
1798
|
+
let devScore = 0;
|
|
1799
|
+
for (const t of skillTags) devScore += backgroundIdf(t) * (devCovByTag.get(t) ?? 0);
|
|
1800
|
+
const devCov = maxDevScore > 0 ? Math.min(1, devScore / maxDevScore) : 0;
|
|
1801
|
+
const jobCov = jobMaxScore > 0 ? Math.min(1, jobMatchScore / jobMaxScore) : 0;
|
|
1802
|
+
return { tagComponent: harmonicMean(devCov, jobCov), details };
|
|
1803
|
+
}
|
|
1804
|
+
function relevanceScore(fp, job, softTags = []) {
|
|
1805
|
+
const expanded = expandWeighted(fp.skillTags);
|
|
1806
|
+
const covMap = softTags.length > 0 ? mergeSoftCoverage(new Map(expanded), softTags, INTEREST_CAP) : expanded;
|
|
1807
|
+
const maxDevScore = fp.skillTags.reduce((acc, t) => acc + backgroundIdf(t), 0);
|
|
1808
|
+
return tagKernel(job, { expanded, covMap, maxDevScore, skillTags: fp.skillTags }).tagComponent;
|
|
1809
|
+
}
|
|
1578
1810
|
function match(fp, jobs, limit = 5, now = Date.now(), opts = {}) {
|
|
1579
1811
|
const idfOf = backgroundIdf;
|
|
1580
1812
|
const expanded = expandWeighted(fp.skillTags);
|
|
1581
1813
|
const covMap = opts.softTags && opts.softTags.length > 0 ? mergeSoftCoverage(new Map(expanded), opts.softTags, INTEREST_CAP) : expanded;
|
|
1582
1814
|
const maxDevScore = fp.skillTags.reduce((acc, t) => acc + idfOf(t), 0);
|
|
1815
|
+
const ctx = { expanded, covMap, maxDevScore, skillTags: fp.skillTags };
|
|
1583
1816
|
const candidates = jobs.filter((j) => passesFilters(fp, j));
|
|
1584
1817
|
const scored = candidates.map((job) => {
|
|
1585
|
-
const details =
|
|
1586
|
-
let jobMatchScore = 0;
|
|
1587
|
-
let jobMaxScore = 0;
|
|
1588
|
-
const devCovByTag = /* @__PURE__ */ new Map();
|
|
1589
|
-
for (const tag of job.tags) {
|
|
1590
|
-
const w = idfOf(tag);
|
|
1591
|
-
jobMaxScore += w;
|
|
1592
|
-
const covHit = covMap.get(tag);
|
|
1593
|
-
if (covHit) jobMatchScore += w * Math.pow(covHit.weight, SHARPEN);
|
|
1594
|
-
const fpHit = expanded.get(tag);
|
|
1595
|
-
if (fpHit) {
|
|
1596
|
-
const credit = Math.pow(fpHit.weight, SHARPEN);
|
|
1597
|
-
details.push({ tag, weight: fpHit.weight, via: fpHit.via });
|
|
1598
|
-
if (credit > (devCovByTag.get(fpHit.via) ?? 0)) devCovByTag.set(fpHit.via, credit);
|
|
1599
|
-
}
|
|
1600
|
-
}
|
|
1601
|
-
let devScore = 0;
|
|
1602
|
-
for (const t of fp.skillTags) devScore += idfOf(t) * (devCovByTag.get(t) ?? 0);
|
|
1603
|
-
const devCov = maxDevScore > 0 ? Math.min(1, devScore / maxDevScore) : 0;
|
|
1604
|
-
const jobCov = jobMaxScore > 0 ? Math.min(1, jobMatchScore / jobMaxScore) : 0;
|
|
1605
|
-
const tagComponent = harmonicMean(devCov, jobCov);
|
|
1818
|
+
const { tagComponent, details } = tagKernel(job, ctx);
|
|
1606
1819
|
if (tagComponent === 0) return null;
|
|
1607
1820
|
const coreTags = job.coreTags ?? coreTagsFromTitle(job.title);
|
|
1608
1821
|
let coreComponent = tagComponent;
|
|
@@ -3137,6 +3350,7 @@ function hasContentSignal(title, body, labels) {
|
|
|
3137
3350
|
${body}`;
|
|
3138
3351
|
if (CONTENT_ADD_RE.test(text)) return true;
|
|
3139
3352
|
if (ADD_TO_CORPUS_RE.test(text)) return true;
|
|
3353
|
+
if (NUMBERED_SEED_RE.test(title)) return true;
|
|
3140
3354
|
if (TRANSLATE_RE.test(text)) return true;
|
|
3141
3355
|
if (TYPO_RE.test(text)) return true;
|
|
3142
3356
|
return false;
|
|
@@ -3152,7 +3366,7 @@ function classifyContributionKind(input) {
|
|
|
3152
3366
|
function looksLikeContentTask(input) {
|
|
3153
3367
|
return classifyContributionKind(input) === "content";
|
|
3154
3368
|
}
|
|
3155
|
-
var CONTENT_LABEL_RE, CODE_LABEL_RE, CODE_TERM_RE, CODE_EXCEPTION_RE, CODE_FENCE_RE, FILE_PATH_RE, CONTENT_ADD_RE, ADD_TO_CORPUS_RE, TRANSLATE_RE, TYPO_RE;
|
|
3369
|
+
var CONTENT_LABEL_RE, CODE_LABEL_RE, CODE_TERM_RE, CODE_EXCEPTION_RE, CODE_FENCE_RE, FILE_PATH_RE, CONTENT_NOUN_STRONG, CONTENT_NOUN_BROAD, CONTENT_ADD_RE, NUMBERED_SEED_RE, ADD_TO_CORPUS_RE, TRANSLATE_RE, TYPO_RE;
|
|
3156
3370
|
var init_contribution_classify = __esm({
|
|
3157
3371
|
"../../packages/core/src/feeds/contribution-classify.ts"() {
|
|
3158
3372
|
"use strict";
|
|
@@ -3162,7 +3376,16 @@ var init_contribution_classify = __esm({
|
|
|
3162
3376
|
CODE_EXCEPTION_RE = /exception|stacktrace|segfault|traceback/i;
|
|
3163
3377
|
CODE_FENCE_RE = /```|(?:^|\n)\s{4,}\S/;
|
|
3164
3378
|
FILE_PATH_RE = /\b[\w./-]+\.(?:ts|tsx|js|jsx|mjs|cjs|py|rb|go|rs|java|kt|scala|c|cc|cpp|cxx|h|hpp|cs|php|swift|m|mm|sh|bash|zsh|sql|graphql|proto|css|scss|sass|less|vue|svelte|toml|ini|gradle|dockerfile)\b/i;
|
|
3165
|
-
|
|
3379
|
+
CONTENT_NOUN_STRONG = String.raw`proverbs?|words?|phrases?|sayings?|quotes?|quotations?|translations?|entry|entries|definitions?|terms?|idioms?|synonyms?|antonyms?|acronyms?|abbreviations?`;
|
|
3380
|
+
CONTENT_NOUN_BROAD = String.raw`trivia\s+questions?|grammar\s+points?|trivia|facts?|quiz(?:zes)?|flash\s?cards?|vocab(?:ulary)?|lessons?|kanji`;
|
|
3381
|
+
CONTENT_ADD_RE = new RegExp(
|
|
3382
|
+
String.raw`\badd(?:ing|s)?\s+(?:\w+\s+){0,4}?(?:${CONTENT_NOUN_STRONG})\b`,
|
|
3383
|
+
"i"
|
|
3384
|
+
);
|
|
3385
|
+
NUMBERED_SEED_RE = new RegExp(
|
|
3386
|
+
String.raw`\badd(?:ing|s)?\s+(?:\w+\s+){0,4}?(?:${CONTENT_NOUN_STRONG}|${CONTENT_NOUN_BROAD})\s*#?\s*\d{1,3}\s*$`,
|
|
3387
|
+
"i"
|
|
3388
|
+
);
|
|
3166
3389
|
ADD_TO_CORPUS_RE = /\badd\b[\s\S]*?\bto\s+(?:the\s+)?(?:word\s?list|dictionary|glossary|phrasebook)\b/i;
|
|
3167
3390
|
TRANSLATE_RE = /\b(?:translate|translating|translation|localize|localise|localization|localisation)\b/i;
|
|
3168
3391
|
TYPO_RE = /\bfix(?:ing)?\s+(?:a\s+|the\s+|some\s+)?typos?\b/i;
|
|
@@ -3170,20 +3393,6 @@ var init_contribution_classify = __esm({
|
|
|
3170
3393
|
});
|
|
3171
3394
|
|
|
3172
3395
|
// ../../packages/core/src/feeds/contributions.ts
|
|
3173
|
-
function readReqGapMs() {
|
|
3174
|
-
const raw = process.env["CONTRIB_REQ_GAP_MS"];
|
|
3175
|
-
if (raw == null) return DEFAULT_REQ_GAP_MS;
|
|
3176
|
-
const n = Number.parseInt(raw, 10);
|
|
3177
|
-
if (Number.isNaN(n)) return DEFAULT_REQ_GAP_MS;
|
|
3178
|
-
return Math.min(Math.max(n, 0), 1e3);
|
|
3179
|
-
}
|
|
3180
|
-
function readBuildBudgetMs() {
|
|
3181
|
-
const raw = process.env["CONTRIB_BUILD_BUDGET_MS"];
|
|
3182
|
-
if (raw == null) return DEFAULT_BUILD_BUDGET_MS;
|
|
3183
|
-
const n = Number.parseInt(raw, 10);
|
|
3184
|
-
if (Number.isNaN(n)) return DEFAULT_BUILD_BUDGET_MS;
|
|
3185
|
-
return Math.min(Math.max(n, MIN_BUILD_BUDGET_MS), MAX_BUILD_BUDGET_MS);
|
|
3186
|
-
}
|
|
3187
3396
|
function authHeaders2() {
|
|
3188
3397
|
const token = process.env["GITHUB_TOKEN"] ?? process.env["GH_TOKEN"];
|
|
3189
3398
|
const h = {
|
|
@@ -3205,57 +3414,9 @@ function repoFullNameFromApiUrl2(url) {
|
|
|
3205
3414
|
return m ? `${m[1]}/${m[2]}` : null;
|
|
3206
3415
|
}
|
|
3207
3416
|
function makeClient(fetchImpl, cfg) {
|
|
3208
|
-
const
|
|
3209
|
-
let lastRequestAt = 0;
|
|
3210
|
-
let pacedMs = 0;
|
|
3211
|
-
let secondaryHits = 0;
|
|
3212
|
-
let aborted = false;
|
|
3213
|
-
let secondaryAborted = false;
|
|
3214
|
-
let budgetAborted = false;
|
|
3215
|
-
let coreHealthyAtStart = false;
|
|
3216
|
-
async function noteAndMaybeBackOff(res) {
|
|
3217
|
-
if (res.status !== 403) return;
|
|
3218
|
-
const remaining = res.headers.get("x-ratelimit-remaining");
|
|
3219
|
-
const retryAfter = res.headers.get("retry-after");
|
|
3220
|
-
const positiveSecondary = retryAfter != null || remaining != null && remaining !== "0";
|
|
3221
|
-
const isSecondary = positiveSecondary || coreHealthyAtStart;
|
|
3222
|
-
if (!isSecondary) return;
|
|
3223
|
-
secondaryHits++;
|
|
3224
|
-
if (secondaryHits >= 2) {
|
|
3225
|
-
aborted = true;
|
|
3226
|
-
secondaryAborted = true;
|
|
3227
|
-
return;
|
|
3228
|
-
}
|
|
3229
|
-
if (cfg.paceEnabled) {
|
|
3230
|
-
const trimmed = (retryAfter ?? "").trim();
|
|
3231
|
-
const parsed = trimmed.length === 0 ? Number.NaN : Number(trimmed);
|
|
3232
|
-
const sec = Number.isNaN(parsed) ? SECONDARY_BACKOFF_CAP_S : Math.min(Math.max(parsed, 0), SECONDARY_BACKOFF_CAP_S);
|
|
3233
|
-
const remaining2 = Math.max(0, cfg.budgetMs - (cfg.now() - startedAt));
|
|
3234
|
-
await cfg.sleep(Math.min(sec * 1e3, remaining2));
|
|
3235
|
-
}
|
|
3236
|
-
}
|
|
3417
|
+
const gov = makeGitHubGovernor(fetchImpl, cfg);
|
|
3237
3418
|
async function raw(path) {
|
|
3238
|
-
|
|
3239
|
-
if (cfg.now() - startedAt > cfg.budgetMs) {
|
|
3240
|
-
aborted = true;
|
|
3241
|
-
budgetAborted = true;
|
|
3242
|
-
return null;
|
|
3243
|
-
}
|
|
3244
|
-
if (cfg.paceEnabled && cfg.gapMs > 0) {
|
|
3245
|
-
const wait = cfg.gapMs - (cfg.now() - lastRequestAt);
|
|
3246
|
-
if (wait > 0) {
|
|
3247
|
-
await cfg.sleep(wait);
|
|
3248
|
-
pacedMs += wait;
|
|
3249
|
-
}
|
|
3250
|
-
lastRequestAt = cfg.now();
|
|
3251
|
-
}
|
|
3252
|
-
try {
|
|
3253
|
-
const res = await fetchImpl(`${GITHUB_API2}${path}`, { headers: authHeaders2() });
|
|
3254
|
-
await noteAndMaybeBackOff(res);
|
|
3255
|
-
return res;
|
|
3256
|
-
} catch {
|
|
3257
|
-
return null;
|
|
3258
|
-
}
|
|
3419
|
+
return gov.get(`${GITHUB_API2}${path}`, { headers: authHeaders2() });
|
|
3259
3420
|
}
|
|
3260
3421
|
async function json(path) {
|
|
3261
3422
|
const res = await raw(path);
|
|
@@ -3269,41 +3430,9 @@ function makeClient(fetchImpl, cfg) {
|
|
|
3269
3430
|
}
|
|
3270
3431
|
}
|
|
3271
3432
|
async function probe(path) {
|
|
3272
|
-
|
|
3273
|
-
let timer;
|
|
3274
|
-
const fetchP = fetchImpl(`${GITHUB_API2}${path}`, {
|
|
3275
|
-
headers: authHeaders2()
|
|
3276
|
-
}).then(
|
|
3277
|
-
(r) => r,
|
|
3278
|
-
() => null
|
|
3279
|
-
);
|
|
3280
|
-
try {
|
|
3281
|
-
const res = bound == null ? await fetchP : await Promise.race([
|
|
3282
|
-
fetchP,
|
|
3283
|
-
new Promise((resolve2) => {
|
|
3284
|
-
timer = setTimeout(() => resolve2(null), bound);
|
|
3285
|
-
})
|
|
3286
|
-
]);
|
|
3287
|
-
if (!res || !res.ok) return null;
|
|
3288
|
-
return await res.json();
|
|
3289
|
-
} catch {
|
|
3290
|
-
return null;
|
|
3291
|
-
} finally {
|
|
3292
|
-
if (timer) clearTimeout(timer);
|
|
3293
|
-
}
|
|
3433
|
+
return gov.probe(`${GITHUB_API2}${path}`, { headers: authHeaders2() });
|
|
3294
3434
|
}
|
|
3295
|
-
|
|
3296
|
-
coreHealthyAtStart = coreHealthy;
|
|
3297
|
-
}
|
|
3298
|
-
function getStats() {
|
|
3299
|
-
return {
|
|
3300
|
-
pacedMs,
|
|
3301
|
-
secondaryAborted: secondaryAborted ? 1 : 0,
|
|
3302
|
-
budgetAborted: budgetAborted ? 1 : 0,
|
|
3303
|
-
elapsedMs: cfg.now() - startedAt
|
|
3304
|
-
};
|
|
3305
|
-
}
|
|
3306
|
-
return { raw, json, probe, setSecondaryHint, getStats };
|
|
3435
|
+
return { raw, json, probe, setSecondaryHint: gov.setSecondaryHint, getStats: gov.getStats };
|
|
3307
3436
|
}
|
|
3308
3437
|
async function contributorCount(client, fullName) {
|
|
3309
3438
|
const res = await client.raw(`/repos/${fullName}/contributors?per_page=1&anon=false`);
|
|
@@ -3402,10 +3531,63 @@ async function aggregateContributions(opts = {}) {
|
|
|
3402
3531
|
const repoCache = /* @__PURE__ */ new Map();
|
|
3403
3532
|
const contribCache = /* @__PURE__ */ new Map();
|
|
3404
3533
|
const prRefsCache = /* @__PURE__ */ new Map();
|
|
3534
|
+
const xbuild = opts.repoMetaCache;
|
|
3535
|
+
const servedFromXbuild = /* @__PURE__ */ new Set();
|
|
3536
|
+
const persistedXbuild = /* @__PURE__ */ new Set();
|
|
3537
|
+
const xbuildTried = /* @__PURE__ */ new Set();
|
|
3538
|
+
async function primeFromXbuild(key, fullName) {
|
|
3539
|
+
if (!xbuild || xbuildTried.has(key)) return;
|
|
3540
|
+
xbuildTried.add(key);
|
|
3541
|
+
if (repoCache.has(key) && contribCache.has(key)) return;
|
|
3542
|
+
let cached = null;
|
|
3543
|
+
try {
|
|
3544
|
+
cached = await xbuild.get(key);
|
|
3545
|
+
} catch {
|
|
3546
|
+
return;
|
|
3547
|
+
}
|
|
3548
|
+
if (!cached) return;
|
|
3549
|
+
if (!repoCache.has(key)) {
|
|
3550
|
+
const owner = fullName.split("/")[0] ?? "";
|
|
3551
|
+
repoCache.set(key, {
|
|
3552
|
+
full_name: fullName,
|
|
3553
|
+
stargazers_count: cached.stars,
|
|
3554
|
+
archived: cached.archived,
|
|
3555
|
+
disabled: cached.disabled,
|
|
3556
|
+
fork: cached.fork,
|
|
3557
|
+
language: cached.language,
|
|
3558
|
+
owner: { login: owner }
|
|
3559
|
+
});
|
|
3560
|
+
}
|
|
3561
|
+
if (!contribCache.has(key)) {
|
|
3562
|
+
contribCache.set(key, cached.contributors);
|
|
3563
|
+
servedFromXbuild.add(key);
|
|
3564
|
+
}
|
|
3565
|
+
}
|
|
3566
|
+
async function persistRepoMeta(fullName, repo, contributors) {
|
|
3567
|
+
if (!xbuild) return;
|
|
3568
|
+
const key = repoKey(fullName);
|
|
3569
|
+
if (servedFromXbuild.has(key) || persistedXbuild.has(key)) return;
|
|
3570
|
+
if (contributors === void 0) return;
|
|
3571
|
+
persistedXbuild.add(key);
|
|
3572
|
+
try {
|
|
3573
|
+
await xbuild.set(key, {
|
|
3574
|
+
stars: repo.stargazers_count,
|
|
3575
|
+
contributors,
|
|
3576
|
+
language: repo.language,
|
|
3577
|
+
archived: repo.archived,
|
|
3578
|
+
fork: repo.fork,
|
|
3579
|
+
disabled: repo.disabled
|
|
3580
|
+
});
|
|
3581
|
+
} catch {
|
|
3582
|
+
}
|
|
3583
|
+
}
|
|
3405
3584
|
async function repoMeta(fullName) {
|
|
3406
3585
|
const key = repoKey(fullName);
|
|
3407
3586
|
const hit = repoCache.get(key);
|
|
3408
3587
|
if (hit !== void 0) return hit;
|
|
3588
|
+
await primeFromXbuild(key, fullName);
|
|
3589
|
+
const primed = repoCache.get(key);
|
|
3590
|
+
if (primed !== void 0) return primed;
|
|
3409
3591
|
const r = await client.json(`/repos/${fullName}`) ?? null;
|
|
3410
3592
|
repoCache.set(key, r);
|
|
3411
3593
|
return r;
|
|
@@ -3413,6 +3595,8 @@ async function aggregateContributions(opts = {}) {
|
|
|
3413
3595
|
async function repoContribCount(fullName) {
|
|
3414
3596
|
const key = repoKey(fullName);
|
|
3415
3597
|
if (contribCache.has(key)) return contribCache.get(key);
|
|
3598
|
+
await primeFromXbuild(key, fullName);
|
|
3599
|
+
if (contribCache.has(key)) return contribCache.get(key);
|
|
3416
3600
|
const n = await contributorCount(client, fullName);
|
|
3417
3601
|
contribCache.set(key, n);
|
|
3418
3602
|
return n;
|
|
@@ -3439,14 +3623,18 @@ async function aggregateContributions(opts = {}) {
|
|
|
3439
3623
|
if (isDenylistedRepo(fullName)) continue;
|
|
3440
3624
|
if (isAssigned(issue)) continue;
|
|
3441
3625
|
if ((perRepo.get(repoKey(fullName)) ?? 0) >= MAX_BOUNTIES_PER_DISCOVERED_REPO) continue;
|
|
3626
|
+
const title = decodeEntities(issue.title).trim();
|
|
3627
|
+
const body = issue.body ? decodeEntities(issue.body) : "";
|
|
3628
|
+
const labels = labelNames2(issue.labels);
|
|
3629
|
+
if (looksLikeContentTask({ title, body, labels })) continue;
|
|
3442
3630
|
const repo = await repoMeta(fullName);
|
|
3443
3631
|
if (!repo) {
|
|
3444
3632
|
metaNull++;
|
|
3445
3633
|
continue;
|
|
3446
3634
|
}
|
|
3447
|
-
const title = decodeEntities(issue.title).trim();
|
|
3448
3635
|
const contributors = await repoContribCount(fullName);
|
|
3449
3636
|
if (contributors === void 0) contribUndefined++;
|
|
3637
|
+
await persistRepoMeta(fullName, repo, contributors);
|
|
3450
3638
|
if (!passesContributionGate({
|
|
3451
3639
|
fullName,
|
|
3452
3640
|
stars: repo.stargazers_count,
|
|
@@ -3458,13 +3646,9 @@ async function aggregateContributions(opts = {}) {
|
|
|
3458
3646
|
continue;
|
|
3459
3647
|
}
|
|
3460
3648
|
if (repo.disabled) continue;
|
|
3461
|
-
const body = issue.body ? decodeEntities(issue.body) : "";
|
|
3462
|
-
const labels = labelNames2(issue.labels);
|
|
3463
|
-
if (looksLikeContentTask({ title, body, labels })) continue;
|
|
3464
3649
|
const prRefs = await repoPRRefs(fullName);
|
|
3465
3650
|
if (prRefs === null) prRefsNull++;
|
|
3466
|
-
|
|
3467
|
-
const openPRsAtDiscovery = prRefs ? 0 : void 0;
|
|
3651
|
+
const openPRsAtDiscovery = prRefs ? prRefs.has(issue.number) ? 1 : 0 : void 0;
|
|
3468
3652
|
const tags = normalize(
|
|
3469
3653
|
tokenize4([title, repo.language ?? "", labels.join(" "), body.slice(0, 2e3)].join(" "))
|
|
3470
3654
|
);
|
|
@@ -3555,6 +3739,7 @@ async function aggregateContributions(opts = {}) {
|
|
|
3555
3739
|
if (repo.disabled) continue;
|
|
3556
3740
|
const contributors = await repoContribCount(fullName);
|
|
3557
3741
|
if (contributors === void 0) contribUndefined++;
|
|
3742
|
+
await persistRepoMeta(fullName, repo, contributors);
|
|
3558
3743
|
if (repo.archived || repo.fork || repo.stargazers_count < MIN_STARS || contributors === void 0 || contributors < MIN_CONTRIBUTORS) {
|
|
3559
3744
|
continue;
|
|
3560
3745
|
}
|
|
@@ -3587,8 +3772,7 @@ async function aggregateContributions(opts = {}) {
|
|
|
3587
3772
|
if (looksLikeContentTask({ title, body, labels })) continue;
|
|
3588
3773
|
const prRefs = await repoPRRefs(fullName);
|
|
3589
3774
|
if (prRefs === null) prRefsNull++;
|
|
3590
|
-
|
|
3591
|
-
const openPRsAtDiscovery = prRefs ? 0 : void 0;
|
|
3775
|
+
const openPRsAtDiscovery = prRefs ? prRefs.has(issue.number) ? 1 : 0 : void 0;
|
|
3592
3776
|
const tags = normalize(
|
|
3593
3777
|
tokenize4([title, repo.language ?? "", labels.join(" "), body.slice(0, 2e3)].join(" "))
|
|
3594
3778
|
);
|
|
@@ -3619,9 +3803,9 @@ async function aggregateContributions(opts = {}) {
|
|
|
3619
3803
|
const core = rl?.core ? `${rl.core.remaining}/${rl.core.limit}` : "n/a";
|
|
3620
3804
|
const search = rl?.search ? `${rl.search.remaining}/${rl.search.limit}` : "n/a";
|
|
3621
3805
|
const noToken = !(process.env["GITHUB_TOKEN"] ?? process.env["GH_TOKEN"]);
|
|
3622
|
-
const { pacedMs, secondaryAborted, budgetAborted, elapsedMs } = client.getStats();
|
|
3806
|
+
const { pacedMs, secondaryAborted, budgetAborted, elapsedMs, gqlCost, gqlRemaining } = client.getStats();
|
|
3623
3807
|
console.info(
|
|
3624
|
-
`[contribute] build metrics \u2014 scanned=${issues.length} reposDistinct=${repoCache.size} emitted=${jobs.length} discovered=${discoveredEmitted} metaNull=${metaNull} contribUndefined=${contribUndefined} prRefsNull=${prRefsNull} paced=${pacedMs} secondaryAborted=${secondaryAborted} budgetAborted=${budgetAborted} core=${core} search=${search} elapsed=${elapsedMs}` + (noToken ? " (NO TOKEN \u2192 60/hr)" : "")
|
|
3808
|
+
`[contribute] build metrics \u2014 scanned=${issues.length} reposDistinct=${repoCache.size} emitted=${jobs.length} discovered=${discoveredEmitted} metaNull=${metaNull} contribUndefined=${contribUndefined} prRefsNull=${prRefsNull} paced=${pacedMs} secondaryAborted=${secondaryAborted} budgetAborted=${budgetAborted} core=${core} search=${search} gqlCost=${gqlCost} gqlRemaining=${gqlRemaining ?? "n/a"} elapsed=${elapsedMs}` + (noToken ? " (NO TOKEN \u2192 60/hr)" : "")
|
|
3625
3809
|
);
|
|
3626
3810
|
if (discoveryBudgetStopped) {
|
|
3627
3811
|
console.warn(
|
|
@@ -3631,7 +3815,7 @@ async function aggregateContributions(opts = {}) {
|
|
|
3631
3815
|
}
|
|
3632
3816
|
return jobs;
|
|
3633
3817
|
}
|
|
3634
|
-
var GITHUB_API2,
|
|
3818
|
+
var GITHUB_API2, CONTRIB_LABEL_QUERIES, CONTRIB_LANGUAGE_QUERIES, CONTRIB_SEARCH_QUERIES, SEARCH_PER_PAGE2, MAX_CONTRIB_ITEMS, MAX_CONTRIB_ISSUES_SCANNED, MAX_DISCOVERED_REPOS, MAX_ISSUES_PER_DISCOVERED_REPO, DISCOVERY_REPOS_PER_TERM, DISCOVERY_VOCAB_TERMS, DISCOVERY_ISSUE_LABELS, repoKey;
|
|
3635
3819
|
var init_contributions = __esm({
|
|
3636
3820
|
"../../packages/core/src/feeds/contributions.ts"() {
|
|
3637
3821
|
"use strict";
|
|
@@ -3642,14 +3826,8 @@ var init_contributions = __esm({
|
|
|
3642
3826
|
init_contribution_classify();
|
|
3643
3827
|
init_github_bounties();
|
|
3644
3828
|
init_http();
|
|
3829
|
+
init_gh_governor();
|
|
3645
3830
|
GITHUB_API2 = "https://api.github.com";
|
|
3646
|
-
DEFAULT_REQ_GAP_MS = 75;
|
|
3647
|
-
SECONDARY_BACKOFF_CAP_S = 30;
|
|
3648
|
-
DEFAULT_BUILD_BUDGET_MS = 9e4;
|
|
3649
|
-
MIN_BUILD_BUDGET_MS = 1e4;
|
|
3650
|
-
MAX_BUILD_BUDGET_MS = 9e4;
|
|
3651
|
-
PROBE_TIMEOUT_MS = 3e3;
|
|
3652
|
-
realSleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
3653
3831
|
CONTRIB_LABEL_QUERIES = [
|
|
3654
3832
|
'label:"good first issue" type:issue state:open',
|
|
3655
3833
|
'label:"good-first-issue" type:issue state:open',
|
|
@@ -3867,12 +4045,22 @@ async function enrichWinnability(jobs, contribute, w) {
|
|
|
3867
4045
|
const repos = [...byRepo.keys()].sort((a, b) => starsOf(b) - starsOf(a));
|
|
3868
4046
|
const scored = repos.slice(0, maxRepos);
|
|
3869
4047
|
const cappedOut = repos.length - scored.length;
|
|
4048
|
+
const gov = w.governor ?? makeGitHubGovernor(
|
|
4049
|
+
w.fetchImpl ?? fetch,
|
|
4050
|
+
makeDefaultGovernorConfig({ paceEnabled: !w.fetchImpl })
|
|
4051
|
+
);
|
|
4052
|
+
let stoppedEarly = false;
|
|
3870
4053
|
for (const repo of scored) {
|
|
4054
|
+
if (gov.tripped() || gov.budgetExhausted()) {
|
|
4055
|
+
stoppedEarly = true;
|
|
4056
|
+
break;
|
|
4057
|
+
}
|
|
3871
4058
|
const targets = byRepo.get(repo) ?? [];
|
|
3872
4059
|
const receptivity = await fetchRepoReceptivity(repo, {
|
|
3873
4060
|
token: w.token,
|
|
3874
4061
|
fetchImpl: w.fetchImpl,
|
|
3875
|
-
now
|
|
4062
|
+
now,
|
|
4063
|
+
governor: gov
|
|
3876
4064
|
});
|
|
3877
4065
|
const stars = starsOf(repo);
|
|
3878
4066
|
const prev = priorStars[repo];
|
|
@@ -3901,6 +4089,12 @@ async function enrichWinnability(jobs, contribute, w) {
|
|
|
3901
4089
|
}
|
|
3902
4090
|
}
|
|
3903
4091
|
}
|
|
4092
|
+
if (stoppedEarly) {
|
|
4093
|
+
const reason = gov.tripped() ? "secondary-abuse breaker" : "wall-clock budget";
|
|
4094
|
+
console.warn(
|
|
4095
|
+
`[winnability] receptivity governor stopped the loop \u2014 ${reason} tripped; remaining repos left un-scored (legacy ranking). Governed egress halted to avoid prolonging the GitHub rate-limit window.`
|
|
4096
|
+
);
|
|
4097
|
+
}
|
|
3904
4098
|
if (cappedOut > 0) {
|
|
3905
4099
|
console.warn(
|
|
3906
4100
|
`[winnability] receptivity cap hit \u2014 scored ${scored.length}/${repos.length} repos (${cappedOut} beyond maxRepos=${maxRepos} left un-scored, legacy ranking)`
|
|
@@ -3943,6 +4137,7 @@ var init_indexer = __esm({
|
|
|
3943
4137
|
init_contributions();
|
|
3944
4138
|
init_partners();
|
|
3945
4139
|
init_github();
|
|
4140
|
+
init_gh_governor();
|
|
3946
4141
|
init_winnability();
|
|
3947
4142
|
}
|
|
3948
4143
|
});
|
|
@@ -7538,6 +7733,7 @@ __export(src_exports, {
|
|
|
7538
7733
|
MERGE_PROBABILITY: () => MERGE_PROBABILITY,
|
|
7539
7734
|
MIN_CONTRIBUTORS: () => MIN_CONTRIBUTORS,
|
|
7540
7735
|
MIN_STARS: () => MIN_STARS,
|
|
7736
|
+
PROBE_TIMEOUT_MS: () => PROBE_TIMEOUT_MS,
|
|
7541
7737
|
RIGOR: () => RIGOR,
|
|
7542
7738
|
STRONG_MATCH_THRESHOLD: () => STRONG_MATCH_THRESHOLD,
|
|
7543
7739
|
SYNONYMS: () => SYNONYMS,
|
|
@@ -7609,6 +7805,9 @@ __export(src_exports, {
|
|
|
7609
7805
|
lever: () => lever,
|
|
7610
7806
|
loadPartnerRoles: () => loadPartnerRoles,
|
|
7611
7807
|
looksLikeEngRole: () => looksLikeEngRole,
|
|
7808
|
+
makeDefaultGovernorConfig: () => makeDefaultGovernorConfig,
|
|
7809
|
+
makeGitHubGovernor: () => makeGitHubGovernor,
|
|
7810
|
+
makeScoringGovernor: () => makeScoringGovernor,
|
|
7612
7811
|
match: () => match,
|
|
7613
7812
|
mergeProbability: () => mergeProbability,
|
|
7614
7813
|
mmrRerank: () => mmrRerank,
|
|
@@ -7621,8 +7820,12 @@ __export(src_exports, {
|
|
|
7621
7820
|
passesMaturityGate: () => passesMaturityGate,
|
|
7622
7821
|
personCardToJob: () => personCardToJob,
|
|
7623
7822
|
projectCardToJob: () => projectCardToJob,
|
|
7823
|
+
readBuildBudgetMs: () => readBuildBudgetMs,
|
|
7824
|
+
readReqGapMs: () => readReqGapMs,
|
|
7825
|
+
realSleep: () => realSleep,
|
|
7624
7826
|
recordClick: () => recordClick,
|
|
7625
7827
|
rejectExtraIntroFields: () => rejectExtraIntroFields,
|
|
7828
|
+
relevanceScore: () => relevanceScore,
|
|
7626
7829
|
revealIntroContacts: () => revealIntroContacts,
|
|
7627
7830
|
safetyNumber: () => safetyNumber,
|
|
7628
7831
|
sameLogin: () => sameLogin,
|
|
@@ -7649,6 +7852,7 @@ var init_src = __esm({
|
|
|
7649
7852
|
init_winnability();
|
|
7650
7853
|
init_partners();
|
|
7651
7854
|
init_github();
|
|
7855
|
+
init_gh_governor();
|
|
7652
7856
|
init_credit();
|
|
7653
7857
|
init_intro();
|
|
7654
7858
|
init_directoryThreshold();
|