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-bounties.js
CHANGED
|
@@ -855,6 +855,187 @@ var init_rigor = __esm({
|
|
|
855
855
|
}
|
|
856
856
|
});
|
|
857
857
|
|
|
858
|
+
// ../../packages/core/src/gh-governor.ts
|
|
859
|
+
function readReqGapMs() {
|
|
860
|
+
const raw = process.env["CONTRIB_REQ_GAP_MS"];
|
|
861
|
+
if (raw == null) return DEFAULT_REQ_GAP_MS;
|
|
862
|
+
const n = Number.parseInt(raw, 10);
|
|
863
|
+
if (Number.isNaN(n)) return DEFAULT_REQ_GAP_MS;
|
|
864
|
+
return Math.min(Math.max(n, 0), 1e3);
|
|
865
|
+
}
|
|
866
|
+
function readBuildBudgetMs() {
|
|
867
|
+
const raw = process.env["CONTRIB_BUILD_BUDGET_MS"];
|
|
868
|
+
if (raw == null) return DEFAULT_BUILD_BUDGET_MS;
|
|
869
|
+
const n = Number.parseInt(raw, 10);
|
|
870
|
+
if (Number.isNaN(n)) return DEFAULT_BUILD_BUDGET_MS;
|
|
871
|
+
return Math.min(Math.max(n, MIN_BUILD_BUDGET_MS), MAX_BUILD_BUDGET_MS);
|
|
872
|
+
}
|
|
873
|
+
function makeDefaultGovernorConfig(o) {
|
|
874
|
+
return {
|
|
875
|
+
paceEnabled: o.paceEnabled,
|
|
876
|
+
gapMs: readReqGapMs(),
|
|
877
|
+
budgetMs: readBuildBudgetMs(),
|
|
878
|
+
sleep: o.sleep ?? realSleep,
|
|
879
|
+
now: o.now ?? Date.now,
|
|
880
|
+
probeTimeoutMs: o.probeTimeoutMs ?? (o.paceEnabled ? PROBE_TIMEOUT_MS : null)
|
|
881
|
+
};
|
|
882
|
+
}
|
|
883
|
+
function makeGitHubGovernor(fetchImpl, cfg) {
|
|
884
|
+
const startedAt = cfg.now();
|
|
885
|
+
let lastRequestAt = 0;
|
|
886
|
+
let pacedMs = 0;
|
|
887
|
+
let secondaryHits = 0;
|
|
888
|
+
let aborted = false;
|
|
889
|
+
let secondaryAborted = false;
|
|
890
|
+
let budgetAborted = false;
|
|
891
|
+
let gqlCost = 0;
|
|
892
|
+
let gqlRemaining = null;
|
|
893
|
+
let coreHealthyAtStart = false;
|
|
894
|
+
async function noteAndMaybeBackOff(res) {
|
|
895
|
+
if (res.status !== 403) return;
|
|
896
|
+
const remaining = res.headers.get("x-ratelimit-remaining");
|
|
897
|
+
const retryAfter = res.headers.get("retry-after");
|
|
898
|
+
const positiveSecondary = retryAfter != null || remaining != null && remaining !== "0";
|
|
899
|
+
const isSecondary = positiveSecondary || coreHealthyAtStart;
|
|
900
|
+
if (!isSecondary) return;
|
|
901
|
+
await recordSecondaryStrike(retryAfter);
|
|
902
|
+
}
|
|
903
|
+
async function recordSecondaryStrike(retryAfter) {
|
|
904
|
+
secondaryHits++;
|
|
905
|
+
if (secondaryHits >= 2) {
|
|
906
|
+
aborted = true;
|
|
907
|
+
secondaryAborted = true;
|
|
908
|
+
return;
|
|
909
|
+
}
|
|
910
|
+
if (cfg.paceEnabled) {
|
|
911
|
+
const trimmed = (retryAfter ?? "").trim();
|
|
912
|
+
const parsed = trimmed.length === 0 ? Number.NaN : Number(trimmed);
|
|
913
|
+
const sec = Number.isNaN(parsed) ? SECONDARY_BACKOFF_CAP_S : Math.min(Math.max(parsed, 0), SECONDARY_BACKOFF_CAP_S);
|
|
914
|
+
const remainingBudget = Math.max(0, cfg.budgetMs - (cfg.now() - startedAt));
|
|
915
|
+
await cfg.sleep(Math.min(sec * 1e3, remainingBudget));
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
async function noteGraphQLAndMaybeBackOff(res, body) {
|
|
919
|
+
const b = body ?? {};
|
|
920
|
+
const rl = b.data?.rateLimit;
|
|
921
|
+
if (rl) {
|
|
922
|
+
if (typeof rl.cost === "number") gqlCost += rl.cost;
|
|
923
|
+
if (typeof rl.remaining === "number") gqlRemaining = rl.remaining;
|
|
924
|
+
}
|
|
925
|
+
const remainingHdr = res.headers?.get("x-ratelimit-remaining") ?? null;
|
|
926
|
+
const retryAfter = res.headers?.get("retry-after") ?? null;
|
|
927
|
+
const headerRate = retryAfter != null || remainingHdr === "0";
|
|
928
|
+
const errs = Array.isArray(b.errors) ? b.errors : [];
|
|
929
|
+
const bodyRate = errs.some((e) => e?.type === "RATE_LIMITED" || /rate limit/i.test(String(e?.message ?? ""))) || typeof rl?.remaining === "number" && rl.remaining <= 0;
|
|
930
|
+
if (headerRate || bodyRate) await recordSecondaryStrike(retryAfter);
|
|
931
|
+
}
|
|
932
|
+
async function preflight() {
|
|
933
|
+
if (aborted) return false;
|
|
934
|
+
if (cfg.now() - startedAt >= cfg.budgetMs) {
|
|
935
|
+
aborted = true;
|
|
936
|
+
budgetAborted = true;
|
|
937
|
+
return false;
|
|
938
|
+
}
|
|
939
|
+
if (cfg.paceEnabled && cfg.gapMs > 0) {
|
|
940
|
+
const wait = cfg.gapMs - (cfg.now() - lastRequestAt);
|
|
941
|
+
if (wait > 0) {
|
|
942
|
+
await cfg.sleep(wait);
|
|
943
|
+
pacedMs += wait;
|
|
944
|
+
if (cfg.now() - startedAt >= cfg.budgetMs) {
|
|
945
|
+
aborted = true;
|
|
946
|
+
budgetAborted = true;
|
|
947
|
+
return false;
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
lastRequestAt = cfg.now();
|
|
951
|
+
}
|
|
952
|
+
return true;
|
|
953
|
+
}
|
|
954
|
+
async function get(url, init) {
|
|
955
|
+
if (!await preflight()) return null;
|
|
956
|
+
try {
|
|
957
|
+
const res = await fetchImpl(url, init);
|
|
958
|
+
await noteAndMaybeBackOff(res);
|
|
959
|
+
return res;
|
|
960
|
+
} catch {
|
|
961
|
+
return null;
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
async function graphql(url, init) {
|
|
965
|
+
if (!await preflight()) return null;
|
|
966
|
+
let res;
|
|
967
|
+
try {
|
|
968
|
+
res = await fetchImpl(url, init);
|
|
969
|
+
} catch {
|
|
970
|
+
return null;
|
|
971
|
+
}
|
|
972
|
+
let body;
|
|
973
|
+
try {
|
|
974
|
+
body = await res.json();
|
|
975
|
+
} catch {
|
|
976
|
+
return null;
|
|
977
|
+
}
|
|
978
|
+
await noteGraphQLAndMaybeBackOff(res, body);
|
|
979
|
+
if (!res.ok) return null;
|
|
980
|
+
return body;
|
|
981
|
+
}
|
|
982
|
+
async function probe(url, init) {
|
|
983
|
+
const bound = cfg.probeTimeoutMs;
|
|
984
|
+
let timer;
|
|
985
|
+
const fetchP = fetchImpl(url, init).then(
|
|
986
|
+
(r) => r,
|
|
987
|
+
() => null
|
|
988
|
+
);
|
|
989
|
+
try {
|
|
990
|
+
const res = bound == null ? await fetchP : await Promise.race([
|
|
991
|
+
fetchP,
|
|
992
|
+
new Promise((resolve) => {
|
|
993
|
+
timer = setTimeout(() => resolve(null), bound);
|
|
994
|
+
})
|
|
995
|
+
]);
|
|
996
|
+
if (!res || !res.ok) return null;
|
|
997
|
+
return await res.json();
|
|
998
|
+
} catch {
|
|
999
|
+
return null;
|
|
1000
|
+
} finally {
|
|
1001
|
+
if (timer) clearTimeout(timer);
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
function setSecondaryHint(coreHealthy) {
|
|
1005
|
+
coreHealthyAtStart = coreHealthy;
|
|
1006
|
+
}
|
|
1007
|
+
function getStats() {
|
|
1008
|
+
return {
|
|
1009
|
+
pacedMs,
|
|
1010
|
+
secondaryAborted: secondaryAborted ? 1 : 0,
|
|
1011
|
+
budgetAborted: budgetAborted ? 1 : 0,
|
|
1012
|
+
elapsedMs: cfg.now() - startedAt,
|
|
1013
|
+
gqlCost,
|
|
1014
|
+
gqlRemaining
|
|
1015
|
+
};
|
|
1016
|
+
}
|
|
1017
|
+
function tripped() {
|
|
1018
|
+
return secondaryAborted;
|
|
1019
|
+
}
|
|
1020
|
+
function budgetExhausted() {
|
|
1021
|
+
return budgetAborted || cfg.now() - startedAt >= cfg.budgetMs;
|
|
1022
|
+
}
|
|
1023
|
+
return { get, graphql, probe, setSecondaryHint, getStats, tripped, budgetExhausted };
|
|
1024
|
+
}
|
|
1025
|
+
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;
|
|
1026
|
+
var init_gh_governor = __esm({
|
|
1027
|
+
"../../packages/core/src/gh-governor.ts"() {
|
|
1028
|
+
"use strict";
|
|
1029
|
+
DEFAULT_REQ_GAP_MS = 75;
|
|
1030
|
+
SECONDARY_BACKOFF_CAP_S = 30;
|
|
1031
|
+
DEFAULT_BUILD_BUDGET_MS = 9e4;
|
|
1032
|
+
MIN_BUILD_BUDGET_MS = 1e4;
|
|
1033
|
+
MAX_BUILD_BUDGET_MS = 9e4;
|
|
1034
|
+
PROBE_TIMEOUT_MS = 3e3;
|
|
1035
|
+
realSleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
1036
|
+
}
|
|
1037
|
+
});
|
|
1038
|
+
|
|
858
1039
|
// ../../packages/core/src/github.ts
|
|
859
1040
|
function ghHeaders(token) {
|
|
860
1041
|
const headers = {
|
|
@@ -1335,11 +1516,9 @@ async function fetchRepoReceptivity(fullName, opts = {}) {
|
|
|
1335
1516
|
const doFetch = opts.fetchImpl ?? fetch;
|
|
1336
1517
|
const now = opts.now ?? Date.now();
|
|
1337
1518
|
try {
|
|
1338
|
-
const
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
);
|
|
1342
|
-
if (!res.ok) return null;
|
|
1519
|
+
const url = `https://api.github.com/repos/${fullName}/pulls?state=closed&per_page=50&sort=updated&direction=desc`;
|
|
1520
|
+
const res = opts.governor ? await opts.governor.get(url, { headers: ghHeaders(opts.token) }) : await doFetch(url, { headers: ghHeaders(opts.token) });
|
|
1521
|
+
if (!res || !res.ok) return null;
|
|
1343
1522
|
const prs = await res.json();
|
|
1344
1523
|
if (!Array.isArray(prs)) return null;
|
|
1345
1524
|
const external = prs.filter(isExternalAuthor);
|
|
@@ -1424,25 +1603,34 @@ function parseGitHubRef(url) {
|
|
|
1424
1603
|
if (!m) return null;
|
|
1425
1604
|
return { owner: m[1], repo: m[2], number: parseInt(m[4], 10), kind: m[3] === "pull" ? "pull" : "issue" };
|
|
1426
1605
|
}
|
|
1427
|
-
async function ghGraphQL(query, variables, token, signal) {
|
|
1428
|
-
const
|
|
1606
|
+
async function ghGraphQL(query, variables, token, signal, governor) {
|
|
1607
|
+
const init = {
|
|
1429
1608
|
method: "POST",
|
|
1430
1609
|
headers: { ...ghHeaders(token), "Content-Type": "application/json" },
|
|
1431
1610
|
body: JSON.stringify({ query, variables }),
|
|
1432
1611
|
signal
|
|
1433
|
-
}
|
|
1612
|
+
};
|
|
1613
|
+
if (governor) {
|
|
1614
|
+
const json2 = await governor.graphql(GITHUB_GRAPHQL_URL, init);
|
|
1615
|
+
if (json2 === null) return null;
|
|
1616
|
+
if (json2.errors?.length) throw new Error("GitHub GraphQL errors: " + JSON.stringify(json2.errors));
|
|
1617
|
+
return json2;
|
|
1618
|
+
}
|
|
1619
|
+
const res = await fetch(GITHUB_GRAPHQL_URL, init);
|
|
1434
1620
|
if (!res.ok) throw new Error(`GitHub GraphQL: HTTP ${res.status}`);
|
|
1435
1621
|
const json = await res.json();
|
|
1436
1622
|
if (json.errors?.length) throw new Error("GitHub GraphQL errors: " + JSON.stringify(json.errors));
|
|
1437
1623
|
return json;
|
|
1438
1624
|
}
|
|
1439
|
-
async function resolveClosingIssues(owner, name, number, body, token, signal) {
|
|
1625
|
+
async function resolveClosingIssues(owner, name, number, body, token, signal, governor) {
|
|
1440
1626
|
if (token) {
|
|
1441
1627
|
try {
|
|
1442
|
-
const q = `query($o:String!,$n:String!,$p:Int!){repository(owner:$o,name:$n){pullRequest(number:$p){closingIssuesReferences(first:20){nodes{number}}}}}`;
|
|
1443
|
-
const r = await ghGraphQL(q, { o: owner, n: name, p: number }, token, signal);
|
|
1444
|
-
|
|
1445
|
-
|
|
1628
|
+
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}}`;
|
|
1629
|
+
const r = await ghGraphQL(q, { o: owner, n: name, p: number }, token, signal, governor);
|
|
1630
|
+
if (r) {
|
|
1631
|
+
const nodes = r.data?.repository?.pullRequest?.closingIssuesReferences?.nodes ?? [];
|
|
1632
|
+
return { closesIssues: nodes.map((x) => x.number), linkageSource: "graphql" };
|
|
1633
|
+
}
|
|
1446
1634
|
} catch {
|
|
1447
1635
|
}
|
|
1448
1636
|
}
|
|
@@ -1452,11 +1640,19 @@ async function resolveClosingIssues(owner, name, number, body, token, signal) {
|
|
|
1452
1640
|
while ((m = re.exec(body)) !== null) nums.add(parseInt(m[1], 10));
|
|
1453
1641
|
return { closesIssues: [...nums], linkageSource: nums.size ? "body-keyword" : "none" };
|
|
1454
1642
|
}
|
|
1455
|
-
|
|
1643
|
+
function makeScoringGovernor(governor) {
|
|
1644
|
+
return governor ?? makeGitHubGovernor(
|
|
1645
|
+
((url, init) => fetch(url, init)),
|
|
1646
|
+
makeDefaultGovernorConfig({ paceEnabled: false })
|
|
1647
|
+
);
|
|
1648
|
+
}
|
|
1649
|
+
async function fetchPRScoringFacts(prUrl, token, signal, governor) {
|
|
1456
1650
|
const ref = parseGitHubRef(prUrl);
|
|
1457
1651
|
if (!ref || ref.kind !== "pull") return null;
|
|
1458
1652
|
const { owner, repo, number } = ref;
|
|
1459
1653
|
const sig = signal ?? AbortSignal.timeout(1e4);
|
|
1654
|
+
const gov = makeScoringGovernor(governor);
|
|
1655
|
+
if (gov.tripped() || gov.budgetExhausted()) return null;
|
|
1460
1656
|
let pr;
|
|
1461
1657
|
try {
|
|
1462
1658
|
pr = await ghFetch(`/repos/${owner}/${repo}/pulls/${number}`, token, sig);
|
|
@@ -1464,22 +1660,26 @@ async function fetchPRScoringFacts(prUrl, token, signal) {
|
|
|
1464
1660
|
return null;
|
|
1465
1661
|
}
|
|
1466
1662
|
let repoMeta = null;
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1663
|
+
if (!gov.tripped() && !gov.budgetExhausted()) {
|
|
1664
|
+
try {
|
|
1665
|
+
repoMeta = await ghFetch(`/repos/${owner}/${repo}`, token, sig);
|
|
1666
|
+
} catch {
|
|
1667
|
+
}
|
|
1470
1668
|
}
|
|
1471
|
-
const contributors = await repoContributorCount(owner, repo, token, sig);
|
|
1472
|
-
const { closesIssues, linkageSource } = await resolveClosingIssues(owner, repo, number, pr.body ?? "", token, sig);
|
|
1669
|
+
const contributors = gov.tripped() || gov.budgetExhausted() ? null : await repoContributorCount(owner, repo, token, sig);
|
|
1670
|
+
const { closesIssues, linkageSource } = await resolveClosingIssues(owner, repo, number, pr.body ?? "", token, sig, gov);
|
|
1473
1671
|
let reviewerAssociations;
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1672
|
+
if (!gov.tripped() && !gov.budgetExhausted()) {
|
|
1673
|
+
try {
|
|
1674
|
+
const reviews = await ghFetch(
|
|
1675
|
+
`/repos/${owner}/${repo}/pulls/${number}/reviews?per_page=100`,
|
|
1676
|
+
token,
|
|
1677
|
+
sig
|
|
1678
|
+
);
|
|
1679
|
+
reviewerAssociations = reviews.map((r) => r.author_association);
|
|
1680
|
+
} catch {
|
|
1681
|
+
reviewerAssociations = void 0;
|
|
1682
|
+
}
|
|
1483
1683
|
}
|
|
1484
1684
|
return {
|
|
1485
1685
|
repo: `${owner}/${repo}`,
|
|
@@ -1506,7 +1706,7 @@ async function fetchPRScoringFacts(prUrl, token, signal) {
|
|
|
1506
1706
|
fetchedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1507
1707
|
};
|
|
1508
1708
|
}
|
|
1509
|
-
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;
|
|
1709
|
+
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;
|
|
1510
1710
|
var init_github = __esm({
|
|
1511
1711
|
"../../packages/core/src/github.ts"() {
|
|
1512
1712
|
"use strict";
|
|
@@ -1514,6 +1714,7 @@ var init_github = __esm({
|
|
|
1514
1714
|
init_contribution_gate();
|
|
1515
1715
|
init_contribution_gate();
|
|
1516
1716
|
init_rigor();
|
|
1717
|
+
init_gh_governor();
|
|
1517
1718
|
TRACTION_TOP_N = 6;
|
|
1518
1719
|
MAINTAINER_ENRICH_MAX = 25;
|
|
1519
1720
|
CANDIDATE_PR_PAGE = 50;
|
|
@@ -1524,6 +1725,7 @@ var init_github = __esm({
|
|
|
1524
1725
|
RESUME_MIN_SCORE = 0.05;
|
|
1525
1726
|
RECEPTIVITY_RECENCY_DAYS = 180;
|
|
1526
1727
|
RECEPTIVITY_RECENCY_FLOOR = 0.1;
|
|
1728
|
+
GITHUB_GRAPHQL_URL = "https://api.github.com/graphql";
|
|
1527
1729
|
}
|
|
1528
1730
|
});
|
|
1529
1731
|
|
|
@@ -1604,34 +1806,45 @@ function mergeSoftCoverage(covMap, softTags, cap) {
|
|
|
1604
1806
|
}
|
|
1605
1807
|
return covMap;
|
|
1606
1808
|
}
|
|
1809
|
+
function tagKernel(job, ctx) {
|
|
1810
|
+
const { expanded, covMap, maxDevScore, skillTags } = ctx;
|
|
1811
|
+
const details = [];
|
|
1812
|
+
let jobMatchScore = 0;
|
|
1813
|
+
let jobMaxScore = 0;
|
|
1814
|
+
const devCovByTag = /* @__PURE__ */ new Map();
|
|
1815
|
+
for (const tag of job.tags) {
|
|
1816
|
+
const w = backgroundIdf(tag);
|
|
1817
|
+
jobMaxScore += w;
|
|
1818
|
+
const covHit = covMap.get(tag);
|
|
1819
|
+
if (covHit) jobMatchScore += w * Math.pow(covHit.weight, SHARPEN);
|
|
1820
|
+
const fpHit = expanded.get(tag);
|
|
1821
|
+
if (fpHit) {
|
|
1822
|
+
const credit = Math.pow(fpHit.weight, SHARPEN);
|
|
1823
|
+
details.push({ tag, weight: fpHit.weight, via: fpHit.via });
|
|
1824
|
+
if (credit > (devCovByTag.get(fpHit.via) ?? 0)) devCovByTag.set(fpHit.via, credit);
|
|
1825
|
+
}
|
|
1826
|
+
}
|
|
1827
|
+
let devScore = 0;
|
|
1828
|
+
for (const t of skillTags) devScore += backgroundIdf(t) * (devCovByTag.get(t) ?? 0);
|
|
1829
|
+
const devCov = maxDevScore > 0 ? Math.min(1, devScore / maxDevScore) : 0;
|
|
1830
|
+
const jobCov = jobMaxScore > 0 ? Math.min(1, jobMatchScore / jobMaxScore) : 0;
|
|
1831
|
+
return { tagComponent: harmonicMean(devCov, jobCov), details };
|
|
1832
|
+
}
|
|
1833
|
+
function relevanceScore(fp, job, softTags = []) {
|
|
1834
|
+
const expanded = expandWeighted(fp.skillTags);
|
|
1835
|
+
const covMap = softTags.length > 0 ? mergeSoftCoverage(new Map(expanded), softTags, INTEREST_CAP) : expanded;
|
|
1836
|
+
const maxDevScore = fp.skillTags.reduce((acc, t) => acc + backgroundIdf(t), 0);
|
|
1837
|
+
return tagKernel(job, { expanded, covMap, maxDevScore, skillTags: fp.skillTags }).tagComponent;
|
|
1838
|
+
}
|
|
1607
1839
|
function match(fp, jobs, limit = 5, now = Date.now(), opts = {}) {
|
|
1608
1840
|
const idfOf = backgroundIdf;
|
|
1609
1841
|
const expanded = expandWeighted(fp.skillTags);
|
|
1610
1842
|
const covMap = opts.softTags && opts.softTags.length > 0 ? mergeSoftCoverage(new Map(expanded), opts.softTags, INTEREST_CAP) : expanded;
|
|
1611
1843
|
const maxDevScore = fp.skillTags.reduce((acc, t) => acc + idfOf(t), 0);
|
|
1844
|
+
const ctx = { expanded, covMap, maxDevScore, skillTags: fp.skillTags };
|
|
1612
1845
|
const candidates = jobs.filter((j) => passesFilters(fp, j));
|
|
1613
1846
|
const scored = candidates.map((job) => {
|
|
1614
|
-
const details =
|
|
1615
|
-
let jobMatchScore = 0;
|
|
1616
|
-
let jobMaxScore = 0;
|
|
1617
|
-
const devCovByTag = /* @__PURE__ */ new Map();
|
|
1618
|
-
for (const tag of job.tags) {
|
|
1619
|
-
const w = idfOf(tag);
|
|
1620
|
-
jobMaxScore += w;
|
|
1621
|
-
const covHit = covMap.get(tag);
|
|
1622
|
-
if (covHit) jobMatchScore += w * Math.pow(covHit.weight, SHARPEN);
|
|
1623
|
-
const fpHit = expanded.get(tag);
|
|
1624
|
-
if (fpHit) {
|
|
1625
|
-
const credit = Math.pow(fpHit.weight, SHARPEN);
|
|
1626
|
-
details.push({ tag, weight: fpHit.weight, via: fpHit.via });
|
|
1627
|
-
if (credit > (devCovByTag.get(fpHit.via) ?? 0)) devCovByTag.set(fpHit.via, credit);
|
|
1628
|
-
}
|
|
1629
|
-
}
|
|
1630
|
-
let devScore = 0;
|
|
1631
|
-
for (const t of fp.skillTags) devScore += idfOf(t) * (devCovByTag.get(t) ?? 0);
|
|
1632
|
-
const devCov = maxDevScore > 0 ? Math.min(1, devScore / maxDevScore) : 0;
|
|
1633
|
-
const jobCov = jobMaxScore > 0 ? Math.min(1, jobMatchScore / jobMaxScore) : 0;
|
|
1634
|
-
const tagComponent = harmonicMean(devCov, jobCov);
|
|
1847
|
+
const { tagComponent, details } = tagKernel(job, ctx);
|
|
1635
1848
|
if (tagComponent === 0) return null;
|
|
1636
1849
|
const coreTags = job.coreTags ?? coreTagsFromTitle(job.title);
|
|
1637
1850
|
let coreComponent = tagComponent;
|
|
@@ -3166,6 +3379,7 @@ function hasContentSignal(title, body, labels) {
|
|
|
3166
3379
|
${body}`;
|
|
3167
3380
|
if (CONTENT_ADD_RE.test(text)) return true;
|
|
3168
3381
|
if (ADD_TO_CORPUS_RE.test(text)) return true;
|
|
3382
|
+
if (NUMBERED_SEED_RE.test(title)) return true;
|
|
3169
3383
|
if (TRANSLATE_RE.test(text)) return true;
|
|
3170
3384
|
if (TYPO_RE.test(text)) return true;
|
|
3171
3385
|
return false;
|
|
@@ -3181,7 +3395,7 @@ function classifyContributionKind(input) {
|
|
|
3181
3395
|
function looksLikeContentTask(input) {
|
|
3182
3396
|
return classifyContributionKind(input) === "content";
|
|
3183
3397
|
}
|
|
3184
|
-
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;
|
|
3398
|
+
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;
|
|
3185
3399
|
var init_contribution_classify = __esm({
|
|
3186
3400
|
"../../packages/core/src/feeds/contribution-classify.ts"() {
|
|
3187
3401
|
"use strict";
|
|
@@ -3191,7 +3405,16 @@ var init_contribution_classify = __esm({
|
|
|
3191
3405
|
CODE_EXCEPTION_RE = /exception|stacktrace|segfault|traceback/i;
|
|
3192
3406
|
CODE_FENCE_RE = /```|(?:^|\n)\s{4,}\S/;
|
|
3193
3407
|
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;
|
|
3194
|
-
|
|
3408
|
+
CONTENT_NOUN_STRONG = String.raw`proverbs?|words?|phrases?|sayings?|quotes?|quotations?|translations?|entry|entries|definitions?|terms?|idioms?|synonyms?|antonyms?|acronyms?|abbreviations?`;
|
|
3409
|
+
CONTENT_NOUN_BROAD = String.raw`trivia\s+questions?|grammar\s+points?|trivia|facts?|quiz(?:zes)?|flash\s?cards?|vocab(?:ulary)?|lessons?|kanji`;
|
|
3410
|
+
CONTENT_ADD_RE = new RegExp(
|
|
3411
|
+
String.raw`\badd(?:ing|s)?\s+(?:\w+\s+){0,4}?(?:${CONTENT_NOUN_STRONG})\b`,
|
|
3412
|
+
"i"
|
|
3413
|
+
);
|
|
3414
|
+
NUMBERED_SEED_RE = new RegExp(
|
|
3415
|
+
String.raw`\badd(?:ing|s)?\s+(?:\w+\s+){0,4}?(?:${CONTENT_NOUN_STRONG}|${CONTENT_NOUN_BROAD})\s*#?\s*\d{1,3}\s*$`,
|
|
3416
|
+
"i"
|
|
3417
|
+
);
|
|
3195
3418
|
ADD_TO_CORPUS_RE = /\badd\b[\s\S]*?\bto\s+(?:the\s+)?(?:word\s?list|dictionary|glossary|phrasebook)\b/i;
|
|
3196
3419
|
TRANSLATE_RE = /\b(?:translate|translating|translation|localize|localise|localization|localisation)\b/i;
|
|
3197
3420
|
TYPO_RE = /\bfix(?:ing)?\s+(?:a\s+|the\s+|some\s+)?typos?\b/i;
|
|
@@ -3199,20 +3422,6 @@ var init_contribution_classify = __esm({
|
|
|
3199
3422
|
});
|
|
3200
3423
|
|
|
3201
3424
|
// ../../packages/core/src/feeds/contributions.ts
|
|
3202
|
-
function readReqGapMs() {
|
|
3203
|
-
const raw = process.env["CONTRIB_REQ_GAP_MS"];
|
|
3204
|
-
if (raw == null) return DEFAULT_REQ_GAP_MS;
|
|
3205
|
-
const n = Number.parseInt(raw, 10);
|
|
3206
|
-
if (Number.isNaN(n)) return DEFAULT_REQ_GAP_MS;
|
|
3207
|
-
return Math.min(Math.max(n, 0), 1e3);
|
|
3208
|
-
}
|
|
3209
|
-
function readBuildBudgetMs() {
|
|
3210
|
-
const raw = process.env["CONTRIB_BUILD_BUDGET_MS"];
|
|
3211
|
-
if (raw == null) return DEFAULT_BUILD_BUDGET_MS;
|
|
3212
|
-
const n = Number.parseInt(raw, 10);
|
|
3213
|
-
if (Number.isNaN(n)) return DEFAULT_BUILD_BUDGET_MS;
|
|
3214
|
-
return Math.min(Math.max(n, MIN_BUILD_BUDGET_MS), MAX_BUILD_BUDGET_MS);
|
|
3215
|
-
}
|
|
3216
3425
|
function authHeaders2() {
|
|
3217
3426
|
const token = process.env["GITHUB_TOKEN"] ?? process.env["GH_TOKEN"];
|
|
3218
3427
|
const h = {
|
|
@@ -3234,57 +3443,9 @@ function repoFullNameFromApiUrl2(url) {
|
|
|
3234
3443
|
return m ? `${m[1]}/${m[2]}` : null;
|
|
3235
3444
|
}
|
|
3236
3445
|
function makeClient(fetchImpl, cfg) {
|
|
3237
|
-
const
|
|
3238
|
-
let lastRequestAt = 0;
|
|
3239
|
-
let pacedMs = 0;
|
|
3240
|
-
let secondaryHits = 0;
|
|
3241
|
-
let aborted = false;
|
|
3242
|
-
let secondaryAborted = false;
|
|
3243
|
-
let budgetAborted = false;
|
|
3244
|
-
let coreHealthyAtStart = false;
|
|
3245
|
-
async function noteAndMaybeBackOff(res) {
|
|
3246
|
-
if (res.status !== 403) return;
|
|
3247
|
-
const remaining = res.headers.get("x-ratelimit-remaining");
|
|
3248
|
-
const retryAfter = res.headers.get("retry-after");
|
|
3249
|
-
const positiveSecondary = retryAfter != null || remaining != null && remaining !== "0";
|
|
3250
|
-
const isSecondary = positiveSecondary || coreHealthyAtStart;
|
|
3251
|
-
if (!isSecondary) return;
|
|
3252
|
-
secondaryHits++;
|
|
3253
|
-
if (secondaryHits >= 2) {
|
|
3254
|
-
aborted = true;
|
|
3255
|
-
secondaryAborted = true;
|
|
3256
|
-
return;
|
|
3257
|
-
}
|
|
3258
|
-
if (cfg.paceEnabled) {
|
|
3259
|
-
const trimmed = (retryAfter ?? "").trim();
|
|
3260
|
-
const parsed = trimmed.length === 0 ? Number.NaN : Number(trimmed);
|
|
3261
|
-
const sec = Number.isNaN(parsed) ? SECONDARY_BACKOFF_CAP_S : Math.min(Math.max(parsed, 0), SECONDARY_BACKOFF_CAP_S);
|
|
3262
|
-
const remaining2 = Math.max(0, cfg.budgetMs - (cfg.now() - startedAt));
|
|
3263
|
-
await cfg.sleep(Math.min(sec * 1e3, remaining2));
|
|
3264
|
-
}
|
|
3265
|
-
}
|
|
3446
|
+
const gov = makeGitHubGovernor(fetchImpl, cfg);
|
|
3266
3447
|
async function raw(path) {
|
|
3267
|
-
|
|
3268
|
-
if (cfg.now() - startedAt > cfg.budgetMs) {
|
|
3269
|
-
aborted = true;
|
|
3270
|
-
budgetAborted = true;
|
|
3271
|
-
return null;
|
|
3272
|
-
}
|
|
3273
|
-
if (cfg.paceEnabled && cfg.gapMs > 0) {
|
|
3274
|
-
const wait = cfg.gapMs - (cfg.now() - lastRequestAt);
|
|
3275
|
-
if (wait > 0) {
|
|
3276
|
-
await cfg.sleep(wait);
|
|
3277
|
-
pacedMs += wait;
|
|
3278
|
-
}
|
|
3279
|
-
lastRequestAt = cfg.now();
|
|
3280
|
-
}
|
|
3281
|
-
try {
|
|
3282
|
-
const res = await fetchImpl(`${GITHUB_API2}${path}`, { headers: authHeaders2() });
|
|
3283
|
-
await noteAndMaybeBackOff(res);
|
|
3284
|
-
return res;
|
|
3285
|
-
} catch {
|
|
3286
|
-
return null;
|
|
3287
|
-
}
|
|
3448
|
+
return gov.get(`${GITHUB_API2}${path}`, { headers: authHeaders2() });
|
|
3288
3449
|
}
|
|
3289
3450
|
async function json(path) {
|
|
3290
3451
|
const res = await raw(path);
|
|
@@ -3298,41 +3459,9 @@ function makeClient(fetchImpl, cfg) {
|
|
|
3298
3459
|
}
|
|
3299
3460
|
}
|
|
3300
3461
|
async function probe(path) {
|
|
3301
|
-
|
|
3302
|
-
let timer;
|
|
3303
|
-
const fetchP = fetchImpl(`${GITHUB_API2}${path}`, {
|
|
3304
|
-
headers: authHeaders2()
|
|
3305
|
-
}).then(
|
|
3306
|
-
(r) => r,
|
|
3307
|
-
() => null
|
|
3308
|
-
);
|
|
3309
|
-
try {
|
|
3310
|
-
const res = bound == null ? await fetchP : await Promise.race([
|
|
3311
|
-
fetchP,
|
|
3312
|
-
new Promise((resolve) => {
|
|
3313
|
-
timer = setTimeout(() => resolve(null), bound);
|
|
3314
|
-
})
|
|
3315
|
-
]);
|
|
3316
|
-
if (!res || !res.ok) return null;
|
|
3317
|
-
return await res.json();
|
|
3318
|
-
} catch {
|
|
3319
|
-
return null;
|
|
3320
|
-
} finally {
|
|
3321
|
-
if (timer) clearTimeout(timer);
|
|
3322
|
-
}
|
|
3462
|
+
return gov.probe(`${GITHUB_API2}${path}`, { headers: authHeaders2() });
|
|
3323
3463
|
}
|
|
3324
|
-
|
|
3325
|
-
coreHealthyAtStart = coreHealthy;
|
|
3326
|
-
}
|
|
3327
|
-
function getStats() {
|
|
3328
|
-
return {
|
|
3329
|
-
pacedMs,
|
|
3330
|
-
secondaryAborted: secondaryAborted ? 1 : 0,
|
|
3331
|
-
budgetAborted: budgetAborted ? 1 : 0,
|
|
3332
|
-
elapsedMs: cfg.now() - startedAt
|
|
3333
|
-
};
|
|
3334
|
-
}
|
|
3335
|
-
return { raw, json, probe, setSecondaryHint, getStats };
|
|
3464
|
+
return { raw, json, probe, setSecondaryHint: gov.setSecondaryHint, getStats: gov.getStats };
|
|
3336
3465
|
}
|
|
3337
3466
|
async function contributorCount(client, fullName) {
|
|
3338
3467
|
const res = await client.raw(`/repos/${fullName}/contributors?per_page=1&anon=false`);
|
|
@@ -3431,10 +3560,63 @@ async function aggregateContributions(opts = {}) {
|
|
|
3431
3560
|
const repoCache = /* @__PURE__ */ new Map();
|
|
3432
3561
|
const contribCache = /* @__PURE__ */ new Map();
|
|
3433
3562
|
const prRefsCache = /* @__PURE__ */ new Map();
|
|
3563
|
+
const xbuild = opts.repoMetaCache;
|
|
3564
|
+
const servedFromXbuild = /* @__PURE__ */ new Set();
|
|
3565
|
+
const persistedXbuild = /* @__PURE__ */ new Set();
|
|
3566
|
+
const xbuildTried = /* @__PURE__ */ new Set();
|
|
3567
|
+
async function primeFromXbuild(key, fullName) {
|
|
3568
|
+
if (!xbuild || xbuildTried.has(key)) return;
|
|
3569
|
+
xbuildTried.add(key);
|
|
3570
|
+
if (repoCache.has(key) && contribCache.has(key)) return;
|
|
3571
|
+
let cached = null;
|
|
3572
|
+
try {
|
|
3573
|
+
cached = await xbuild.get(key);
|
|
3574
|
+
} catch {
|
|
3575
|
+
return;
|
|
3576
|
+
}
|
|
3577
|
+
if (!cached) return;
|
|
3578
|
+
if (!repoCache.has(key)) {
|
|
3579
|
+
const owner = fullName.split("/")[0] ?? "";
|
|
3580
|
+
repoCache.set(key, {
|
|
3581
|
+
full_name: fullName,
|
|
3582
|
+
stargazers_count: cached.stars,
|
|
3583
|
+
archived: cached.archived,
|
|
3584
|
+
disabled: cached.disabled,
|
|
3585
|
+
fork: cached.fork,
|
|
3586
|
+
language: cached.language,
|
|
3587
|
+
owner: { login: owner }
|
|
3588
|
+
});
|
|
3589
|
+
}
|
|
3590
|
+
if (!contribCache.has(key)) {
|
|
3591
|
+
contribCache.set(key, cached.contributors);
|
|
3592
|
+
servedFromXbuild.add(key);
|
|
3593
|
+
}
|
|
3594
|
+
}
|
|
3595
|
+
async function persistRepoMeta(fullName, repo, contributors) {
|
|
3596
|
+
if (!xbuild) return;
|
|
3597
|
+
const key = repoKey(fullName);
|
|
3598
|
+
if (servedFromXbuild.has(key) || persistedXbuild.has(key)) return;
|
|
3599
|
+
if (contributors === void 0) return;
|
|
3600
|
+
persistedXbuild.add(key);
|
|
3601
|
+
try {
|
|
3602
|
+
await xbuild.set(key, {
|
|
3603
|
+
stars: repo.stargazers_count,
|
|
3604
|
+
contributors,
|
|
3605
|
+
language: repo.language,
|
|
3606
|
+
archived: repo.archived,
|
|
3607
|
+
fork: repo.fork,
|
|
3608
|
+
disabled: repo.disabled
|
|
3609
|
+
});
|
|
3610
|
+
} catch {
|
|
3611
|
+
}
|
|
3612
|
+
}
|
|
3434
3613
|
async function repoMeta(fullName) {
|
|
3435
3614
|
const key = repoKey(fullName);
|
|
3436
3615
|
const hit = repoCache.get(key);
|
|
3437
3616
|
if (hit !== void 0) return hit;
|
|
3617
|
+
await primeFromXbuild(key, fullName);
|
|
3618
|
+
const primed = repoCache.get(key);
|
|
3619
|
+
if (primed !== void 0) return primed;
|
|
3438
3620
|
const r = await client.json(`/repos/${fullName}`) ?? null;
|
|
3439
3621
|
repoCache.set(key, r);
|
|
3440
3622
|
return r;
|
|
@@ -3442,6 +3624,8 @@ async function aggregateContributions(opts = {}) {
|
|
|
3442
3624
|
async function repoContribCount(fullName) {
|
|
3443
3625
|
const key = repoKey(fullName);
|
|
3444
3626
|
if (contribCache.has(key)) return contribCache.get(key);
|
|
3627
|
+
await primeFromXbuild(key, fullName);
|
|
3628
|
+
if (contribCache.has(key)) return contribCache.get(key);
|
|
3445
3629
|
const n = await contributorCount(client, fullName);
|
|
3446
3630
|
contribCache.set(key, n);
|
|
3447
3631
|
return n;
|
|
@@ -3468,14 +3652,18 @@ async function aggregateContributions(opts = {}) {
|
|
|
3468
3652
|
if (isDenylistedRepo(fullName)) continue;
|
|
3469
3653
|
if (isAssigned(issue)) continue;
|
|
3470
3654
|
if ((perRepo.get(repoKey(fullName)) ?? 0) >= MAX_BOUNTIES_PER_DISCOVERED_REPO) continue;
|
|
3655
|
+
const title = decodeEntities(issue.title).trim();
|
|
3656
|
+
const body = issue.body ? decodeEntities(issue.body) : "";
|
|
3657
|
+
const labels = labelNames2(issue.labels);
|
|
3658
|
+
if (looksLikeContentTask({ title, body, labels })) continue;
|
|
3471
3659
|
const repo = await repoMeta(fullName);
|
|
3472
3660
|
if (!repo) {
|
|
3473
3661
|
metaNull++;
|
|
3474
3662
|
continue;
|
|
3475
3663
|
}
|
|
3476
|
-
const title = decodeEntities(issue.title).trim();
|
|
3477
3664
|
const contributors = await repoContribCount(fullName);
|
|
3478
3665
|
if (contributors === void 0) contribUndefined++;
|
|
3666
|
+
await persistRepoMeta(fullName, repo, contributors);
|
|
3479
3667
|
if (!passesContributionGate({
|
|
3480
3668
|
fullName,
|
|
3481
3669
|
stars: repo.stargazers_count,
|
|
@@ -3487,13 +3675,9 @@ async function aggregateContributions(opts = {}) {
|
|
|
3487
3675
|
continue;
|
|
3488
3676
|
}
|
|
3489
3677
|
if (repo.disabled) continue;
|
|
3490
|
-
const body = issue.body ? decodeEntities(issue.body) : "";
|
|
3491
|
-
const labels = labelNames2(issue.labels);
|
|
3492
|
-
if (looksLikeContentTask({ title, body, labels })) continue;
|
|
3493
3678
|
const prRefs = await repoPRRefs(fullName);
|
|
3494
3679
|
if (prRefs === null) prRefsNull++;
|
|
3495
|
-
|
|
3496
|
-
const openPRsAtDiscovery = prRefs ? 0 : void 0;
|
|
3680
|
+
const openPRsAtDiscovery = prRefs ? prRefs.has(issue.number) ? 1 : 0 : void 0;
|
|
3497
3681
|
const tags = normalize(
|
|
3498
3682
|
tokenize4([title, repo.language ?? "", labels.join(" "), body.slice(0, 2e3)].join(" "))
|
|
3499
3683
|
);
|
|
@@ -3584,6 +3768,7 @@ async function aggregateContributions(opts = {}) {
|
|
|
3584
3768
|
if (repo.disabled) continue;
|
|
3585
3769
|
const contributors = await repoContribCount(fullName);
|
|
3586
3770
|
if (contributors === void 0) contribUndefined++;
|
|
3771
|
+
await persistRepoMeta(fullName, repo, contributors);
|
|
3587
3772
|
if (repo.archived || repo.fork || repo.stargazers_count < MIN_STARS || contributors === void 0 || contributors < MIN_CONTRIBUTORS) {
|
|
3588
3773
|
continue;
|
|
3589
3774
|
}
|
|
@@ -3616,8 +3801,7 @@ async function aggregateContributions(opts = {}) {
|
|
|
3616
3801
|
if (looksLikeContentTask({ title, body, labels })) continue;
|
|
3617
3802
|
const prRefs = await repoPRRefs(fullName);
|
|
3618
3803
|
if (prRefs === null) prRefsNull++;
|
|
3619
|
-
|
|
3620
|
-
const openPRsAtDiscovery = prRefs ? 0 : void 0;
|
|
3804
|
+
const openPRsAtDiscovery = prRefs ? prRefs.has(issue.number) ? 1 : 0 : void 0;
|
|
3621
3805
|
const tags = normalize(
|
|
3622
3806
|
tokenize4([title, repo.language ?? "", labels.join(" "), body.slice(0, 2e3)].join(" "))
|
|
3623
3807
|
);
|
|
@@ -3648,9 +3832,9 @@ async function aggregateContributions(opts = {}) {
|
|
|
3648
3832
|
const core = rl?.core ? `${rl.core.remaining}/${rl.core.limit}` : "n/a";
|
|
3649
3833
|
const search = rl?.search ? `${rl.search.remaining}/${rl.search.limit}` : "n/a";
|
|
3650
3834
|
const noToken = !(process.env["GITHUB_TOKEN"] ?? process.env["GH_TOKEN"]);
|
|
3651
|
-
const { pacedMs, secondaryAborted, budgetAborted, elapsedMs } = client.getStats();
|
|
3835
|
+
const { pacedMs, secondaryAborted, budgetAborted, elapsedMs, gqlCost, gqlRemaining } = client.getStats();
|
|
3652
3836
|
console.info(
|
|
3653
|
-
`[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)" : "")
|
|
3837
|
+
`[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)" : "")
|
|
3654
3838
|
);
|
|
3655
3839
|
if (discoveryBudgetStopped) {
|
|
3656
3840
|
console.warn(
|
|
@@ -3660,7 +3844,7 @@ async function aggregateContributions(opts = {}) {
|
|
|
3660
3844
|
}
|
|
3661
3845
|
return jobs;
|
|
3662
3846
|
}
|
|
3663
|
-
var GITHUB_API2,
|
|
3847
|
+
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;
|
|
3664
3848
|
var init_contributions = __esm({
|
|
3665
3849
|
"../../packages/core/src/feeds/contributions.ts"() {
|
|
3666
3850
|
"use strict";
|
|
@@ -3671,14 +3855,8 @@ var init_contributions = __esm({
|
|
|
3671
3855
|
init_contribution_classify();
|
|
3672
3856
|
init_github_bounties();
|
|
3673
3857
|
init_http();
|
|
3858
|
+
init_gh_governor();
|
|
3674
3859
|
GITHUB_API2 = "https://api.github.com";
|
|
3675
|
-
DEFAULT_REQ_GAP_MS = 75;
|
|
3676
|
-
SECONDARY_BACKOFF_CAP_S = 30;
|
|
3677
|
-
DEFAULT_BUILD_BUDGET_MS = 9e4;
|
|
3678
|
-
MIN_BUILD_BUDGET_MS = 1e4;
|
|
3679
|
-
MAX_BUILD_BUDGET_MS = 9e4;
|
|
3680
|
-
PROBE_TIMEOUT_MS = 3e3;
|
|
3681
|
-
realSleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
3682
3860
|
CONTRIB_LABEL_QUERIES = [
|
|
3683
3861
|
'label:"good first issue" type:issue state:open',
|
|
3684
3862
|
'label:"good-first-issue" type:issue state:open',
|
|
@@ -3896,12 +4074,22 @@ async function enrichWinnability(jobs, contribute, w) {
|
|
|
3896
4074
|
const repos = [...byRepo.keys()].sort((a, b) => starsOf(b) - starsOf(a));
|
|
3897
4075
|
const scored = repos.slice(0, maxRepos);
|
|
3898
4076
|
const cappedOut = repos.length - scored.length;
|
|
4077
|
+
const gov = w.governor ?? makeGitHubGovernor(
|
|
4078
|
+
w.fetchImpl ?? fetch,
|
|
4079
|
+
makeDefaultGovernorConfig({ paceEnabled: !w.fetchImpl })
|
|
4080
|
+
);
|
|
4081
|
+
let stoppedEarly = false;
|
|
3899
4082
|
for (const repo of scored) {
|
|
4083
|
+
if (gov.tripped() || gov.budgetExhausted()) {
|
|
4084
|
+
stoppedEarly = true;
|
|
4085
|
+
break;
|
|
4086
|
+
}
|
|
3900
4087
|
const targets = byRepo.get(repo) ?? [];
|
|
3901
4088
|
const receptivity = await fetchRepoReceptivity(repo, {
|
|
3902
4089
|
token: w.token,
|
|
3903
4090
|
fetchImpl: w.fetchImpl,
|
|
3904
|
-
now
|
|
4091
|
+
now,
|
|
4092
|
+
governor: gov
|
|
3905
4093
|
});
|
|
3906
4094
|
const stars = starsOf(repo);
|
|
3907
4095
|
const prev = priorStars[repo];
|
|
@@ -3930,6 +4118,12 @@ async function enrichWinnability(jobs, contribute, w) {
|
|
|
3930
4118
|
}
|
|
3931
4119
|
}
|
|
3932
4120
|
}
|
|
4121
|
+
if (stoppedEarly) {
|
|
4122
|
+
const reason = gov.tripped() ? "secondary-abuse breaker" : "wall-clock budget";
|
|
4123
|
+
console.warn(
|
|
4124
|
+
`[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.`
|
|
4125
|
+
);
|
|
4126
|
+
}
|
|
3933
4127
|
if (cappedOut > 0) {
|
|
3934
4128
|
console.warn(
|
|
3935
4129
|
`[winnability] receptivity cap hit \u2014 scored ${scored.length}/${repos.length} repos (${cappedOut} beyond maxRepos=${maxRepos} left un-scored, legacy ranking)`
|
|
@@ -3972,6 +4166,7 @@ var init_indexer = __esm({
|
|
|
3972
4166
|
init_contributions();
|
|
3973
4167
|
init_partners();
|
|
3974
4168
|
init_github();
|
|
4169
|
+
init_gh_governor();
|
|
3975
4170
|
init_winnability();
|
|
3976
4171
|
}
|
|
3977
4172
|
});
|
|
@@ -7567,6 +7762,7 @@ __export(src_exports, {
|
|
|
7567
7762
|
MERGE_PROBABILITY: () => MERGE_PROBABILITY,
|
|
7568
7763
|
MIN_CONTRIBUTORS: () => MIN_CONTRIBUTORS,
|
|
7569
7764
|
MIN_STARS: () => MIN_STARS,
|
|
7765
|
+
PROBE_TIMEOUT_MS: () => PROBE_TIMEOUT_MS,
|
|
7570
7766
|
RIGOR: () => RIGOR,
|
|
7571
7767
|
STRONG_MATCH_THRESHOLD: () => STRONG_MATCH_THRESHOLD,
|
|
7572
7768
|
SYNONYMS: () => SYNONYMS,
|
|
@@ -7638,6 +7834,9 @@ __export(src_exports, {
|
|
|
7638
7834
|
lever: () => lever,
|
|
7639
7835
|
loadPartnerRoles: () => loadPartnerRoles,
|
|
7640
7836
|
looksLikeEngRole: () => looksLikeEngRole,
|
|
7837
|
+
makeDefaultGovernorConfig: () => makeDefaultGovernorConfig,
|
|
7838
|
+
makeGitHubGovernor: () => makeGitHubGovernor,
|
|
7839
|
+
makeScoringGovernor: () => makeScoringGovernor,
|
|
7641
7840
|
match: () => match,
|
|
7642
7841
|
mergeProbability: () => mergeProbability,
|
|
7643
7842
|
mmrRerank: () => mmrRerank,
|
|
@@ -7650,8 +7849,12 @@ __export(src_exports, {
|
|
|
7650
7849
|
passesMaturityGate: () => passesMaturityGate,
|
|
7651
7850
|
personCardToJob: () => personCardToJob,
|
|
7652
7851
|
projectCardToJob: () => projectCardToJob,
|
|
7852
|
+
readBuildBudgetMs: () => readBuildBudgetMs,
|
|
7853
|
+
readReqGapMs: () => readReqGapMs,
|
|
7854
|
+
realSleep: () => realSleep,
|
|
7653
7855
|
recordClick: () => recordClick,
|
|
7654
7856
|
rejectExtraIntroFields: () => rejectExtraIntroFields,
|
|
7857
|
+
relevanceScore: () => relevanceScore,
|
|
7655
7858
|
revealIntroContacts: () => revealIntroContacts,
|
|
7656
7859
|
safetyNumber: () => safetyNumber,
|
|
7657
7860
|
sameLogin: () => sameLogin,
|
|
@@ -7678,6 +7881,7 @@ var init_src = __esm({
|
|
|
7678
7881
|
init_winnability();
|
|
7679
7882
|
init_partners();
|
|
7680
7883
|
init_github();
|
|
7884
|
+
init_gh_governor();
|
|
7681
7885
|
init_credit();
|
|
7682
7886
|
init_intro();
|
|
7683
7887
|
init_directoryThreshold();
|
|
@@ -7698,9 +7902,9 @@ var init_keytar = __esm({
|
|
|
7698
7902
|
}
|
|
7699
7903
|
});
|
|
7700
7904
|
|
|
7701
|
-
// node-file:/Users/ericgang/job-placement-inline
|
|
7905
|
+
// node-file:/Users/ericgang/job-placement-inline/node_modules/keytar/build/Release/keytar.node
|
|
7702
7906
|
var require_keytar = __commonJS({
|
|
7703
|
-
"node-file:/Users/ericgang/job-placement-inline
|
|
7907
|
+
"node-file:/Users/ericgang/job-placement-inline/node_modules/keytar/build/Release/keytar.node"(exports, module) {
|
|
7704
7908
|
"use strict";
|
|
7705
7909
|
init_keytar();
|
|
7706
7910
|
try {
|