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-dispatch.js
CHANGED
|
@@ -1122,6 +1122,187 @@ var init_rigor = __esm({
|
|
|
1122
1122
|
}
|
|
1123
1123
|
});
|
|
1124
1124
|
|
|
1125
|
+
// ../../packages/core/src/gh-governor.ts
|
|
1126
|
+
function readReqGapMs() {
|
|
1127
|
+
const raw = process.env["CONTRIB_REQ_GAP_MS"];
|
|
1128
|
+
if (raw == null) return DEFAULT_REQ_GAP_MS;
|
|
1129
|
+
const n = Number.parseInt(raw, 10);
|
|
1130
|
+
if (Number.isNaN(n)) return DEFAULT_REQ_GAP_MS;
|
|
1131
|
+
return Math.min(Math.max(n, 0), 1e3);
|
|
1132
|
+
}
|
|
1133
|
+
function readBuildBudgetMs() {
|
|
1134
|
+
const raw = process.env["CONTRIB_BUILD_BUDGET_MS"];
|
|
1135
|
+
if (raw == null) return DEFAULT_BUILD_BUDGET_MS;
|
|
1136
|
+
const n = Number.parseInt(raw, 10);
|
|
1137
|
+
if (Number.isNaN(n)) return DEFAULT_BUILD_BUDGET_MS;
|
|
1138
|
+
return Math.min(Math.max(n, MIN_BUILD_BUDGET_MS), MAX_BUILD_BUDGET_MS);
|
|
1139
|
+
}
|
|
1140
|
+
function makeDefaultGovernorConfig(o) {
|
|
1141
|
+
return {
|
|
1142
|
+
paceEnabled: o.paceEnabled,
|
|
1143
|
+
gapMs: readReqGapMs(),
|
|
1144
|
+
budgetMs: readBuildBudgetMs(),
|
|
1145
|
+
sleep: o.sleep ?? realSleep,
|
|
1146
|
+
now: o.now ?? Date.now,
|
|
1147
|
+
probeTimeoutMs: o.probeTimeoutMs ?? (o.paceEnabled ? PROBE_TIMEOUT_MS : null)
|
|
1148
|
+
};
|
|
1149
|
+
}
|
|
1150
|
+
function makeGitHubGovernor(fetchImpl, cfg) {
|
|
1151
|
+
const startedAt = cfg.now();
|
|
1152
|
+
let lastRequestAt = 0;
|
|
1153
|
+
let pacedMs = 0;
|
|
1154
|
+
let secondaryHits = 0;
|
|
1155
|
+
let aborted2 = false;
|
|
1156
|
+
let secondaryAborted = false;
|
|
1157
|
+
let budgetAborted = false;
|
|
1158
|
+
let gqlCost = 0;
|
|
1159
|
+
let gqlRemaining = null;
|
|
1160
|
+
let coreHealthyAtStart = false;
|
|
1161
|
+
async function noteAndMaybeBackOff(res) {
|
|
1162
|
+
if (res.status !== 403) return;
|
|
1163
|
+
const remaining = res.headers.get("x-ratelimit-remaining");
|
|
1164
|
+
const retryAfter = res.headers.get("retry-after");
|
|
1165
|
+
const positiveSecondary = retryAfter != null || remaining != null && remaining !== "0";
|
|
1166
|
+
const isSecondary = positiveSecondary || coreHealthyAtStart;
|
|
1167
|
+
if (!isSecondary) return;
|
|
1168
|
+
await recordSecondaryStrike(retryAfter);
|
|
1169
|
+
}
|
|
1170
|
+
async function recordSecondaryStrike(retryAfter) {
|
|
1171
|
+
secondaryHits++;
|
|
1172
|
+
if (secondaryHits >= 2) {
|
|
1173
|
+
aborted2 = true;
|
|
1174
|
+
secondaryAborted = true;
|
|
1175
|
+
return;
|
|
1176
|
+
}
|
|
1177
|
+
if (cfg.paceEnabled) {
|
|
1178
|
+
const trimmed = (retryAfter ?? "").trim();
|
|
1179
|
+
const parsed = trimmed.length === 0 ? Number.NaN : Number(trimmed);
|
|
1180
|
+
const sec = Number.isNaN(parsed) ? SECONDARY_BACKOFF_CAP_S : Math.min(Math.max(parsed, 0), SECONDARY_BACKOFF_CAP_S);
|
|
1181
|
+
const remainingBudget = Math.max(0, cfg.budgetMs - (cfg.now() - startedAt));
|
|
1182
|
+
await cfg.sleep(Math.min(sec * 1e3, remainingBudget));
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
async function noteGraphQLAndMaybeBackOff(res, body) {
|
|
1186
|
+
const b = body ?? {};
|
|
1187
|
+
const rl = b.data?.rateLimit;
|
|
1188
|
+
if (rl) {
|
|
1189
|
+
if (typeof rl.cost === "number") gqlCost += rl.cost;
|
|
1190
|
+
if (typeof rl.remaining === "number") gqlRemaining = rl.remaining;
|
|
1191
|
+
}
|
|
1192
|
+
const remainingHdr = res.headers?.get("x-ratelimit-remaining") ?? null;
|
|
1193
|
+
const retryAfter = res.headers?.get("retry-after") ?? null;
|
|
1194
|
+
const headerRate = retryAfter != null || remainingHdr === "0";
|
|
1195
|
+
const errs = Array.isArray(b.errors) ? b.errors : [];
|
|
1196
|
+
const bodyRate = errs.some((e) => e?.type === "RATE_LIMITED" || /rate limit/i.test(String(e?.message ?? ""))) || typeof rl?.remaining === "number" && rl.remaining <= 0;
|
|
1197
|
+
if (headerRate || bodyRate) await recordSecondaryStrike(retryAfter);
|
|
1198
|
+
}
|
|
1199
|
+
async function preflight() {
|
|
1200
|
+
if (aborted2) return false;
|
|
1201
|
+
if (cfg.now() - startedAt >= cfg.budgetMs) {
|
|
1202
|
+
aborted2 = true;
|
|
1203
|
+
budgetAborted = true;
|
|
1204
|
+
return false;
|
|
1205
|
+
}
|
|
1206
|
+
if (cfg.paceEnabled && cfg.gapMs > 0) {
|
|
1207
|
+
const wait = cfg.gapMs - (cfg.now() - lastRequestAt);
|
|
1208
|
+
if (wait > 0) {
|
|
1209
|
+
await cfg.sleep(wait);
|
|
1210
|
+
pacedMs += wait;
|
|
1211
|
+
if (cfg.now() - startedAt >= cfg.budgetMs) {
|
|
1212
|
+
aborted2 = true;
|
|
1213
|
+
budgetAborted = true;
|
|
1214
|
+
return false;
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
1217
|
+
lastRequestAt = cfg.now();
|
|
1218
|
+
}
|
|
1219
|
+
return true;
|
|
1220
|
+
}
|
|
1221
|
+
async function get(url, init) {
|
|
1222
|
+
if (!await preflight()) return null;
|
|
1223
|
+
try {
|
|
1224
|
+
const res = await fetchImpl(url, init);
|
|
1225
|
+
await noteAndMaybeBackOff(res);
|
|
1226
|
+
return res;
|
|
1227
|
+
} catch {
|
|
1228
|
+
return null;
|
|
1229
|
+
}
|
|
1230
|
+
}
|
|
1231
|
+
async function graphql(url, init) {
|
|
1232
|
+
if (!await preflight()) return null;
|
|
1233
|
+
let res;
|
|
1234
|
+
try {
|
|
1235
|
+
res = await fetchImpl(url, init);
|
|
1236
|
+
} catch {
|
|
1237
|
+
return null;
|
|
1238
|
+
}
|
|
1239
|
+
let body;
|
|
1240
|
+
try {
|
|
1241
|
+
body = await res.json();
|
|
1242
|
+
} catch {
|
|
1243
|
+
return null;
|
|
1244
|
+
}
|
|
1245
|
+
await noteGraphQLAndMaybeBackOff(res, body);
|
|
1246
|
+
if (!res.ok) return null;
|
|
1247
|
+
return body;
|
|
1248
|
+
}
|
|
1249
|
+
async function probe(url, init) {
|
|
1250
|
+
const bound = cfg.probeTimeoutMs;
|
|
1251
|
+
let timer;
|
|
1252
|
+
const fetchP = fetchImpl(url, init).then(
|
|
1253
|
+
(r) => r,
|
|
1254
|
+
() => null
|
|
1255
|
+
);
|
|
1256
|
+
try {
|
|
1257
|
+
const res = bound == null ? await fetchP : await Promise.race([
|
|
1258
|
+
fetchP,
|
|
1259
|
+
new Promise((resolve2) => {
|
|
1260
|
+
timer = setTimeout(() => resolve2(null), bound);
|
|
1261
|
+
})
|
|
1262
|
+
]);
|
|
1263
|
+
if (!res || !res.ok) return null;
|
|
1264
|
+
return await res.json();
|
|
1265
|
+
} catch {
|
|
1266
|
+
return null;
|
|
1267
|
+
} finally {
|
|
1268
|
+
if (timer) clearTimeout(timer);
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
1271
|
+
function setSecondaryHint(coreHealthy) {
|
|
1272
|
+
coreHealthyAtStart = coreHealthy;
|
|
1273
|
+
}
|
|
1274
|
+
function getStats() {
|
|
1275
|
+
return {
|
|
1276
|
+
pacedMs,
|
|
1277
|
+
secondaryAborted: secondaryAborted ? 1 : 0,
|
|
1278
|
+
budgetAborted: budgetAborted ? 1 : 0,
|
|
1279
|
+
elapsedMs: cfg.now() - startedAt,
|
|
1280
|
+
gqlCost,
|
|
1281
|
+
gqlRemaining
|
|
1282
|
+
};
|
|
1283
|
+
}
|
|
1284
|
+
function tripped() {
|
|
1285
|
+
return secondaryAborted;
|
|
1286
|
+
}
|
|
1287
|
+
function budgetExhausted() {
|
|
1288
|
+
return budgetAborted || cfg.now() - startedAt >= cfg.budgetMs;
|
|
1289
|
+
}
|
|
1290
|
+
return { get, graphql, probe, setSecondaryHint, getStats, tripped, budgetExhausted };
|
|
1291
|
+
}
|
|
1292
|
+
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;
|
|
1293
|
+
var init_gh_governor = __esm({
|
|
1294
|
+
"../../packages/core/src/gh-governor.ts"() {
|
|
1295
|
+
"use strict";
|
|
1296
|
+
DEFAULT_REQ_GAP_MS = 75;
|
|
1297
|
+
SECONDARY_BACKOFF_CAP_S = 30;
|
|
1298
|
+
DEFAULT_BUILD_BUDGET_MS = 9e4;
|
|
1299
|
+
MIN_BUILD_BUDGET_MS = 1e4;
|
|
1300
|
+
MAX_BUILD_BUDGET_MS = 9e4;
|
|
1301
|
+
PROBE_TIMEOUT_MS = 3e3;
|
|
1302
|
+
realSleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
1303
|
+
}
|
|
1304
|
+
});
|
|
1305
|
+
|
|
1125
1306
|
// ../../packages/core/src/github.ts
|
|
1126
1307
|
function ghHeaders(token) {
|
|
1127
1308
|
const headers = {
|
|
@@ -1602,11 +1783,9 @@ async function fetchRepoReceptivity(fullName, opts = {}) {
|
|
|
1602
1783
|
const doFetch = opts.fetchImpl ?? fetch;
|
|
1603
1784
|
const now = opts.now ?? Date.now();
|
|
1604
1785
|
try {
|
|
1605
|
-
const
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
);
|
|
1609
|
-
if (!res.ok) return null;
|
|
1786
|
+
const url = `https://api.github.com/repos/${fullName}/pulls?state=closed&per_page=50&sort=updated&direction=desc`;
|
|
1787
|
+
const res = opts.governor ? await opts.governor.get(url, { headers: ghHeaders(opts.token) }) : await doFetch(url, { headers: ghHeaders(opts.token) });
|
|
1788
|
+
if (!res || !res.ok) return null;
|
|
1610
1789
|
const prs = await res.json();
|
|
1611
1790
|
if (!Array.isArray(prs)) return null;
|
|
1612
1791
|
const external = prs.filter(isExternalAuthor);
|
|
@@ -1691,25 +1870,34 @@ function parseGitHubRef(url) {
|
|
|
1691
1870
|
if (!m) return null;
|
|
1692
1871
|
return { owner: m[1], repo: m[2], number: parseInt(m[4], 10), kind: m[3] === "pull" ? "pull" : "issue" };
|
|
1693
1872
|
}
|
|
1694
|
-
async function ghGraphQL(query, variables, token, signal) {
|
|
1695
|
-
const
|
|
1873
|
+
async function ghGraphQL(query, variables, token, signal, governor) {
|
|
1874
|
+
const init = {
|
|
1696
1875
|
method: "POST",
|
|
1697
1876
|
headers: { ...ghHeaders(token), "Content-Type": "application/json" },
|
|
1698
1877
|
body: JSON.stringify({ query, variables }),
|
|
1699
1878
|
signal
|
|
1700
|
-
}
|
|
1879
|
+
};
|
|
1880
|
+
if (governor) {
|
|
1881
|
+
const json2 = await governor.graphql(GITHUB_GRAPHQL_URL, init);
|
|
1882
|
+
if (json2 === null) return null;
|
|
1883
|
+
if (json2.errors?.length) throw new Error("GitHub GraphQL errors: " + JSON.stringify(json2.errors));
|
|
1884
|
+
return json2;
|
|
1885
|
+
}
|
|
1886
|
+
const res = await fetch(GITHUB_GRAPHQL_URL, init);
|
|
1701
1887
|
if (!res.ok) throw new Error(`GitHub GraphQL: HTTP ${res.status}`);
|
|
1702
1888
|
const json = await res.json();
|
|
1703
1889
|
if (json.errors?.length) throw new Error("GitHub GraphQL errors: " + JSON.stringify(json.errors));
|
|
1704
1890
|
return json;
|
|
1705
1891
|
}
|
|
1706
|
-
async function resolveClosingIssues(owner, name, number3, body, token, signal) {
|
|
1892
|
+
async function resolveClosingIssues(owner, name, number3, body, token, signal, governor) {
|
|
1707
1893
|
if (token) {
|
|
1708
1894
|
try {
|
|
1709
|
-
const q = `query($o:String!,$n:String!,$p:Int!){repository(owner:$o,name:$n){pullRequest(number:$p){closingIssuesReferences(first:20){nodes{number}}}}}`;
|
|
1710
|
-
const r = await ghGraphQL(q, { o: owner, n: name, p: number3 }, token, signal);
|
|
1711
|
-
|
|
1712
|
-
|
|
1895
|
+
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}}`;
|
|
1896
|
+
const r = await ghGraphQL(q, { o: owner, n: name, p: number3 }, token, signal, governor);
|
|
1897
|
+
if (r) {
|
|
1898
|
+
const nodes = r.data?.repository?.pullRequest?.closingIssuesReferences?.nodes ?? [];
|
|
1899
|
+
return { closesIssues: nodes.map((x) => x.number), linkageSource: "graphql" };
|
|
1900
|
+
}
|
|
1713
1901
|
} catch {
|
|
1714
1902
|
}
|
|
1715
1903
|
}
|
|
@@ -1719,11 +1907,19 @@ async function resolveClosingIssues(owner, name, number3, body, token, signal) {
|
|
|
1719
1907
|
while ((m = re.exec(body)) !== null) nums.add(parseInt(m[1], 10));
|
|
1720
1908
|
return { closesIssues: [...nums], linkageSource: nums.size ? "body-keyword" : "none" };
|
|
1721
1909
|
}
|
|
1722
|
-
|
|
1910
|
+
function makeScoringGovernor(governor) {
|
|
1911
|
+
return governor ?? makeGitHubGovernor(
|
|
1912
|
+
((url, init) => fetch(url, init)),
|
|
1913
|
+
makeDefaultGovernorConfig({ paceEnabled: false })
|
|
1914
|
+
);
|
|
1915
|
+
}
|
|
1916
|
+
async function fetchPRScoringFacts(prUrl, token, signal, governor) {
|
|
1723
1917
|
const ref = parseGitHubRef(prUrl);
|
|
1724
1918
|
if (!ref || ref.kind !== "pull") return null;
|
|
1725
1919
|
const { owner, repo, number: number3 } = ref;
|
|
1726
1920
|
const sig = signal ?? AbortSignal.timeout(1e4);
|
|
1921
|
+
const gov = makeScoringGovernor(governor);
|
|
1922
|
+
if (gov.tripped() || gov.budgetExhausted()) return null;
|
|
1727
1923
|
let pr;
|
|
1728
1924
|
try {
|
|
1729
1925
|
pr = await ghFetch(`/repos/${owner}/${repo}/pulls/${number3}`, token, sig);
|
|
@@ -1731,22 +1927,26 @@ async function fetchPRScoringFacts(prUrl, token, signal) {
|
|
|
1731
1927
|
return null;
|
|
1732
1928
|
}
|
|
1733
1929
|
let repoMeta = null;
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1930
|
+
if (!gov.tripped() && !gov.budgetExhausted()) {
|
|
1931
|
+
try {
|
|
1932
|
+
repoMeta = await ghFetch(`/repos/${owner}/${repo}`, token, sig);
|
|
1933
|
+
} catch {
|
|
1934
|
+
}
|
|
1737
1935
|
}
|
|
1738
|
-
const contributors = await repoContributorCount(owner, repo, token, sig);
|
|
1739
|
-
const { closesIssues, linkageSource } = await resolveClosingIssues(owner, repo, number3, pr.body ?? "", token, sig);
|
|
1936
|
+
const contributors = gov.tripped() || gov.budgetExhausted() ? null : await repoContributorCount(owner, repo, token, sig);
|
|
1937
|
+
const { closesIssues, linkageSource } = await resolveClosingIssues(owner, repo, number3, pr.body ?? "", token, sig, gov);
|
|
1740
1938
|
let reviewerAssociations;
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1939
|
+
if (!gov.tripped() && !gov.budgetExhausted()) {
|
|
1940
|
+
try {
|
|
1941
|
+
const reviews = await ghFetch(
|
|
1942
|
+
`/repos/${owner}/${repo}/pulls/${number3}/reviews?per_page=100`,
|
|
1943
|
+
token,
|
|
1944
|
+
sig
|
|
1945
|
+
);
|
|
1946
|
+
reviewerAssociations = reviews.map((r) => r.author_association);
|
|
1947
|
+
} catch {
|
|
1948
|
+
reviewerAssociations = void 0;
|
|
1949
|
+
}
|
|
1750
1950
|
}
|
|
1751
1951
|
return {
|
|
1752
1952
|
repo: `${owner}/${repo}`,
|
|
@@ -1773,7 +1973,7 @@ async function fetchPRScoringFacts(prUrl, token, signal) {
|
|
|
1773
1973
|
fetchedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1774
1974
|
};
|
|
1775
1975
|
}
|
|
1776
|
-
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;
|
|
1976
|
+
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;
|
|
1777
1977
|
var init_github = __esm({
|
|
1778
1978
|
"../../packages/core/src/github.ts"() {
|
|
1779
1979
|
"use strict";
|
|
@@ -1781,6 +1981,7 @@ var init_github = __esm({
|
|
|
1781
1981
|
init_contribution_gate();
|
|
1782
1982
|
init_contribution_gate();
|
|
1783
1983
|
init_rigor();
|
|
1984
|
+
init_gh_governor();
|
|
1784
1985
|
TRACTION_TOP_N = 6;
|
|
1785
1986
|
MAINTAINER_ENRICH_MAX = 25;
|
|
1786
1987
|
CANDIDATE_PR_PAGE = 50;
|
|
@@ -1791,6 +1992,7 @@ var init_github = __esm({
|
|
|
1791
1992
|
RESUME_MIN_SCORE = 0.05;
|
|
1792
1993
|
RECEPTIVITY_RECENCY_DAYS = 180;
|
|
1793
1994
|
RECEPTIVITY_RECENCY_FLOOR = 0.1;
|
|
1995
|
+
GITHUB_GRAPHQL_URL = "https://api.github.com/graphql";
|
|
1794
1996
|
}
|
|
1795
1997
|
});
|
|
1796
1998
|
|
|
@@ -1871,34 +2073,45 @@ function mergeSoftCoverage(covMap, softTags, cap) {
|
|
|
1871
2073
|
}
|
|
1872
2074
|
return covMap;
|
|
1873
2075
|
}
|
|
2076
|
+
function tagKernel(job, ctx) {
|
|
2077
|
+
const { expanded, covMap, maxDevScore, skillTags } = ctx;
|
|
2078
|
+
const details = [];
|
|
2079
|
+
let jobMatchScore = 0;
|
|
2080
|
+
let jobMaxScore = 0;
|
|
2081
|
+
const devCovByTag = /* @__PURE__ */ new Map();
|
|
2082
|
+
for (const tag of job.tags) {
|
|
2083
|
+
const w = backgroundIdf(tag);
|
|
2084
|
+
jobMaxScore += w;
|
|
2085
|
+
const covHit = covMap.get(tag);
|
|
2086
|
+
if (covHit) jobMatchScore += w * Math.pow(covHit.weight, SHARPEN);
|
|
2087
|
+
const fpHit = expanded.get(tag);
|
|
2088
|
+
if (fpHit) {
|
|
2089
|
+
const credit = Math.pow(fpHit.weight, SHARPEN);
|
|
2090
|
+
details.push({ tag, weight: fpHit.weight, via: fpHit.via });
|
|
2091
|
+
if (credit > (devCovByTag.get(fpHit.via) ?? 0)) devCovByTag.set(fpHit.via, credit);
|
|
2092
|
+
}
|
|
2093
|
+
}
|
|
2094
|
+
let devScore = 0;
|
|
2095
|
+
for (const t of skillTags) devScore += backgroundIdf(t) * (devCovByTag.get(t) ?? 0);
|
|
2096
|
+
const devCov = maxDevScore > 0 ? Math.min(1, devScore / maxDevScore) : 0;
|
|
2097
|
+
const jobCov = jobMaxScore > 0 ? Math.min(1, jobMatchScore / jobMaxScore) : 0;
|
|
2098
|
+
return { tagComponent: harmonicMean(devCov, jobCov), details };
|
|
2099
|
+
}
|
|
2100
|
+
function relevanceScore(fp, job, softTags = []) {
|
|
2101
|
+
const expanded = expandWeighted(fp.skillTags);
|
|
2102
|
+
const covMap = softTags.length > 0 ? mergeSoftCoverage(new Map(expanded), softTags, INTEREST_CAP) : expanded;
|
|
2103
|
+
const maxDevScore = fp.skillTags.reduce((acc, t) => acc + backgroundIdf(t), 0);
|
|
2104
|
+
return tagKernel(job, { expanded, covMap, maxDevScore, skillTags: fp.skillTags }).tagComponent;
|
|
2105
|
+
}
|
|
1874
2106
|
function match(fp, jobs, limit = 5, now = Date.now(), opts = {}) {
|
|
1875
2107
|
const idfOf = backgroundIdf;
|
|
1876
2108
|
const expanded = expandWeighted(fp.skillTags);
|
|
1877
2109
|
const covMap = opts.softTags && opts.softTags.length > 0 ? mergeSoftCoverage(new Map(expanded), opts.softTags, INTEREST_CAP) : expanded;
|
|
1878
2110
|
const maxDevScore = fp.skillTags.reduce((acc, t) => acc + idfOf(t), 0);
|
|
2111
|
+
const ctx = { expanded, covMap, maxDevScore, skillTags: fp.skillTags };
|
|
1879
2112
|
const candidates = jobs.filter((j) => passesFilters(fp, j));
|
|
1880
2113
|
const scored = candidates.map((job) => {
|
|
1881
|
-
const details =
|
|
1882
|
-
let jobMatchScore = 0;
|
|
1883
|
-
let jobMaxScore = 0;
|
|
1884
|
-
const devCovByTag = /* @__PURE__ */ new Map();
|
|
1885
|
-
for (const tag of job.tags) {
|
|
1886
|
-
const w = idfOf(tag);
|
|
1887
|
-
jobMaxScore += w;
|
|
1888
|
-
const covHit = covMap.get(tag);
|
|
1889
|
-
if (covHit) jobMatchScore += w * Math.pow(covHit.weight, SHARPEN);
|
|
1890
|
-
const fpHit = expanded.get(tag);
|
|
1891
|
-
if (fpHit) {
|
|
1892
|
-
const credit = Math.pow(fpHit.weight, SHARPEN);
|
|
1893
|
-
details.push({ tag, weight: fpHit.weight, via: fpHit.via });
|
|
1894
|
-
if (credit > (devCovByTag.get(fpHit.via) ?? 0)) devCovByTag.set(fpHit.via, credit);
|
|
1895
|
-
}
|
|
1896
|
-
}
|
|
1897
|
-
let devScore = 0;
|
|
1898
|
-
for (const t of fp.skillTags) devScore += idfOf(t) * (devCovByTag.get(t) ?? 0);
|
|
1899
|
-
const devCov = maxDevScore > 0 ? Math.min(1, devScore / maxDevScore) : 0;
|
|
1900
|
-
const jobCov = jobMaxScore > 0 ? Math.min(1, jobMatchScore / jobMaxScore) : 0;
|
|
1901
|
-
const tagComponent = harmonicMean(devCov, jobCov);
|
|
2114
|
+
const { tagComponent, details } = tagKernel(job, ctx);
|
|
1902
2115
|
if (tagComponent === 0) return null;
|
|
1903
2116
|
const coreTags = job.coreTags ?? coreTagsFromTitle(job.title);
|
|
1904
2117
|
let coreComponent = tagComponent;
|
|
@@ -3433,6 +3646,7 @@ function hasContentSignal(title, body, labels) {
|
|
|
3433
3646
|
${body}`;
|
|
3434
3647
|
if (CONTENT_ADD_RE.test(text)) return true;
|
|
3435
3648
|
if (ADD_TO_CORPUS_RE.test(text)) return true;
|
|
3649
|
+
if (NUMBERED_SEED_RE.test(title)) return true;
|
|
3436
3650
|
if (TRANSLATE_RE.test(text)) return true;
|
|
3437
3651
|
if (TYPO_RE.test(text)) return true;
|
|
3438
3652
|
return false;
|
|
@@ -3448,7 +3662,7 @@ function classifyContributionKind(input) {
|
|
|
3448
3662
|
function looksLikeContentTask(input) {
|
|
3449
3663
|
return classifyContributionKind(input) === "content";
|
|
3450
3664
|
}
|
|
3451
|
-
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;
|
|
3665
|
+
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;
|
|
3452
3666
|
var init_contribution_classify = __esm({
|
|
3453
3667
|
"../../packages/core/src/feeds/contribution-classify.ts"() {
|
|
3454
3668
|
"use strict";
|
|
@@ -3458,7 +3672,16 @@ var init_contribution_classify = __esm({
|
|
|
3458
3672
|
CODE_EXCEPTION_RE = /exception|stacktrace|segfault|traceback/i;
|
|
3459
3673
|
CODE_FENCE_RE = /```|(?:^|\n)\s{4,}\S/;
|
|
3460
3674
|
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;
|
|
3461
|
-
|
|
3675
|
+
CONTENT_NOUN_STRONG = String.raw`proverbs?|words?|phrases?|sayings?|quotes?|quotations?|translations?|entry|entries|definitions?|terms?|idioms?|synonyms?|antonyms?|acronyms?|abbreviations?`;
|
|
3676
|
+
CONTENT_NOUN_BROAD = String.raw`trivia\s+questions?|grammar\s+points?|trivia|facts?|quiz(?:zes)?|flash\s?cards?|vocab(?:ulary)?|lessons?|kanji`;
|
|
3677
|
+
CONTENT_ADD_RE = new RegExp(
|
|
3678
|
+
String.raw`\badd(?:ing|s)?\s+(?:\w+\s+){0,4}?(?:${CONTENT_NOUN_STRONG})\b`,
|
|
3679
|
+
"i"
|
|
3680
|
+
);
|
|
3681
|
+
NUMBERED_SEED_RE = new RegExp(
|
|
3682
|
+
String.raw`\badd(?:ing|s)?\s+(?:\w+\s+){0,4}?(?:${CONTENT_NOUN_STRONG}|${CONTENT_NOUN_BROAD})\s*#?\s*\d{1,3}\s*$`,
|
|
3683
|
+
"i"
|
|
3684
|
+
);
|
|
3462
3685
|
ADD_TO_CORPUS_RE = /\badd\b[\s\S]*?\bto\s+(?:the\s+)?(?:word\s?list|dictionary|glossary|phrasebook)\b/i;
|
|
3463
3686
|
TRANSLATE_RE = /\b(?:translate|translating|translation|localize|localise|localization|localisation)\b/i;
|
|
3464
3687
|
TYPO_RE = /\bfix(?:ing)?\s+(?:a\s+|the\s+|some\s+)?typos?\b/i;
|
|
@@ -3466,20 +3689,6 @@ var init_contribution_classify = __esm({
|
|
|
3466
3689
|
});
|
|
3467
3690
|
|
|
3468
3691
|
// ../../packages/core/src/feeds/contributions.ts
|
|
3469
|
-
function readReqGapMs() {
|
|
3470
|
-
const raw = process.env["CONTRIB_REQ_GAP_MS"];
|
|
3471
|
-
if (raw == null) return DEFAULT_REQ_GAP_MS;
|
|
3472
|
-
const n = Number.parseInt(raw, 10);
|
|
3473
|
-
if (Number.isNaN(n)) return DEFAULT_REQ_GAP_MS;
|
|
3474
|
-
return Math.min(Math.max(n, 0), 1e3);
|
|
3475
|
-
}
|
|
3476
|
-
function readBuildBudgetMs() {
|
|
3477
|
-
const raw = process.env["CONTRIB_BUILD_BUDGET_MS"];
|
|
3478
|
-
if (raw == null) return DEFAULT_BUILD_BUDGET_MS;
|
|
3479
|
-
const n = Number.parseInt(raw, 10);
|
|
3480
|
-
if (Number.isNaN(n)) return DEFAULT_BUILD_BUDGET_MS;
|
|
3481
|
-
return Math.min(Math.max(n, MIN_BUILD_BUDGET_MS), MAX_BUILD_BUDGET_MS);
|
|
3482
|
-
}
|
|
3483
3692
|
function authHeaders2() {
|
|
3484
3693
|
const token = process.env["GITHUB_TOKEN"] ?? process.env["GH_TOKEN"];
|
|
3485
3694
|
const h = {
|
|
@@ -3501,57 +3710,9 @@ function repoFullNameFromApiUrl2(url) {
|
|
|
3501
3710
|
return m ? `${m[1]}/${m[2]}` : null;
|
|
3502
3711
|
}
|
|
3503
3712
|
function makeClient(fetchImpl, cfg) {
|
|
3504
|
-
const
|
|
3505
|
-
let lastRequestAt = 0;
|
|
3506
|
-
let pacedMs = 0;
|
|
3507
|
-
let secondaryHits = 0;
|
|
3508
|
-
let aborted2 = false;
|
|
3509
|
-
let secondaryAborted = false;
|
|
3510
|
-
let budgetAborted = false;
|
|
3511
|
-
let coreHealthyAtStart = false;
|
|
3512
|
-
async function noteAndMaybeBackOff(res) {
|
|
3513
|
-
if (res.status !== 403) return;
|
|
3514
|
-
const remaining = res.headers.get("x-ratelimit-remaining");
|
|
3515
|
-
const retryAfter = res.headers.get("retry-after");
|
|
3516
|
-
const positiveSecondary = retryAfter != null || remaining != null && remaining !== "0";
|
|
3517
|
-
const isSecondary = positiveSecondary || coreHealthyAtStart;
|
|
3518
|
-
if (!isSecondary) return;
|
|
3519
|
-
secondaryHits++;
|
|
3520
|
-
if (secondaryHits >= 2) {
|
|
3521
|
-
aborted2 = true;
|
|
3522
|
-
secondaryAborted = true;
|
|
3523
|
-
return;
|
|
3524
|
-
}
|
|
3525
|
-
if (cfg.paceEnabled) {
|
|
3526
|
-
const trimmed = (retryAfter ?? "").trim();
|
|
3527
|
-
const parsed = trimmed.length === 0 ? Number.NaN : Number(trimmed);
|
|
3528
|
-
const sec = Number.isNaN(parsed) ? SECONDARY_BACKOFF_CAP_S : Math.min(Math.max(parsed, 0), SECONDARY_BACKOFF_CAP_S);
|
|
3529
|
-
const remaining2 = Math.max(0, cfg.budgetMs - (cfg.now() - startedAt));
|
|
3530
|
-
await cfg.sleep(Math.min(sec * 1e3, remaining2));
|
|
3531
|
-
}
|
|
3532
|
-
}
|
|
3713
|
+
const gov = makeGitHubGovernor(fetchImpl, cfg);
|
|
3533
3714
|
async function raw(path) {
|
|
3534
|
-
|
|
3535
|
-
if (cfg.now() - startedAt > cfg.budgetMs) {
|
|
3536
|
-
aborted2 = true;
|
|
3537
|
-
budgetAborted = true;
|
|
3538
|
-
return null;
|
|
3539
|
-
}
|
|
3540
|
-
if (cfg.paceEnabled && cfg.gapMs > 0) {
|
|
3541
|
-
const wait = cfg.gapMs - (cfg.now() - lastRequestAt);
|
|
3542
|
-
if (wait > 0) {
|
|
3543
|
-
await cfg.sleep(wait);
|
|
3544
|
-
pacedMs += wait;
|
|
3545
|
-
}
|
|
3546
|
-
lastRequestAt = cfg.now();
|
|
3547
|
-
}
|
|
3548
|
-
try {
|
|
3549
|
-
const res = await fetchImpl(`${GITHUB_API2}${path}`, { headers: authHeaders2() });
|
|
3550
|
-
await noteAndMaybeBackOff(res);
|
|
3551
|
-
return res;
|
|
3552
|
-
} catch {
|
|
3553
|
-
return null;
|
|
3554
|
-
}
|
|
3715
|
+
return gov.get(`${GITHUB_API2}${path}`, { headers: authHeaders2() });
|
|
3555
3716
|
}
|
|
3556
3717
|
async function json(path) {
|
|
3557
3718
|
const res = await raw(path);
|
|
@@ -3565,41 +3726,9 @@ function makeClient(fetchImpl, cfg) {
|
|
|
3565
3726
|
}
|
|
3566
3727
|
}
|
|
3567
3728
|
async function probe(path) {
|
|
3568
|
-
|
|
3569
|
-
let timer;
|
|
3570
|
-
const fetchP = fetchImpl(`${GITHUB_API2}${path}`, {
|
|
3571
|
-
headers: authHeaders2()
|
|
3572
|
-
}).then(
|
|
3573
|
-
(r) => r,
|
|
3574
|
-
() => null
|
|
3575
|
-
);
|
|
3576
|
-
try {
|
|
3577
|
-
const res = bound == null ? await fetchP : await Promise.race([
|
|
3578
|
-
fetchP,
|
|
3579
|
-
new Promise((resolve2) => {
|
|
3580
|
-
timer = setTimeout(() => resolve2(null), bound);
|
|
3581
|
-
})
|
|
3582
|
-
]);
|
|
3583
|
-
if (!res || !res.ok) return null;
|
|
3584
|
-
return await res.json();
|
|
3585
|
-
} catch {
|
|
3586
|
-
return null;
|
|
3587
|
-
} finally {
|
|
3588
|
-
if (timer) clearTimeout(timer);
|
|
3589
|
-
}
|
|
3590
|
-
}
|
|
3591
|
-
function setSecondaryHint(coreHealthy) {
|
|
3592
|
-
coreHealthyAtStart = coreHealthy;
|
|
3593
|
-
}
|
|
3594
|
-
function getStats() {
|
|
3595
|
-
return {
|
|
3596
|
-
pacedMs,
|
|
3597
|
-
secondaryAborted: secondaryAborted ? 1 : 0,
|
|
3598
|
-
budgetAborted: budgetAborted ? 1 : 0,
|
|
3599
|
-
elapsedMs: cfg.now() - startedAt
|
|
3600
|
-
};
|
|
3729
|
+
return gov.probe(`${GITHUB_API2}${path}`, { headers: authHeaders2() });
|
|
3601
3730
|
}
|
|
3602
|
-
return { raw, json, probe, setSecondaryHint, getStats };
|
|
3731
|
+
return { raw, json, probe, setSecondaryHint: gov.setSecondaryHint, getStats: gov.getStats };
|
|
3603
3732
|
}
|
|
3604
3733
|
async function contributorCount(client, fullName) {
|
|
3605
3734
|
const res = await client.raw(`/repos/${fullName}/contributors?per_page=1&anon=false`);
|
|
@@ -3698,10 +3827,63 @@ async function aggregateContributions(opts = {}) {
|
|
|
3698
3827
|
const repoCache = /* @__PURE__ */ new Map();
|
|
3699
3828
|
const contribCache = /* @__PURE__ */ new Map();
|
|
3700
3829
|
const prRefsCache = /* @__PURE__ */ new Map();
|
|
3830
|
+
const xbuild = opts.repoMetaCache;
|
|
3831
|
+
const servedFromXbuild = /* @__PURE__ */ new Set();
|
|
3832
|
+
const persistedXbuild = /* @__PURE__ */ new Set();
|
|
3833
|
+
const xbuildTried = /* @__PURE__ */ new Set();
|
|
3834
|
+
async function primeFromXbuild(key, fullName) {
|
|
3835
|
+
if (!xbuild || xbuildTried.has(key)) return;
|
|
3836
|
+
xbuildTried.add(key);
|
|
3837
|
+
if (repoCache.has(key) && contribCache.has(key)) return;
|
|
3838
|
+
let cached2 = null;
|
|
3839
|
+
try {
|
|
3840
|
+
cached2 = await xbuild.get(key);
|
|
3841
|
+
} catch {
|
|
3842
|
+
return;
|
|
3843
|
+
}
|
|
3844
|
+
if (!cached2) return;
|
|
3845
|
+
if (!repoCache.has(key)) {
|
|
3846
|
+
const owner = fullName.split("/")[0] ?? "";
|
|
3847
|
+
repoCache.set(key, {
|
|
3848
|
+
full_name: fullName,
|
|
3849
|
+
stargazers_count: cached2.stars,
|
|
3850
|
+
archived: cached2.archived,
|
|
3851
|
+
disabled: cached2.disabled,
|
|
3852
|
+
fork: cached2.fork,
|
|
3853
|
+
language: cached2.language,
|
|
3854
|
+
owner: { login: owner }
|
|
3855
|
+
});
|
|
3856
|
+
}
|
|
3857
|
+
if (!contribCache.has(key)) {
|
|
3858
|
+
contribCache.set(key, cached2.contributors);
|
|
3859
|
+
servedFromXbuild.add(key);
|
|
3860
|
+
}
|
|
3861
|
+
}
|
|
3862
|
+
async function persistRepoMeta(fullName, repo, contributors) {
|
|
3863
|
+
if (!xbuild) return;
|
|
3864
|
+
const key = repoKey(fullName);
|
|
3865
|
+
if (servedFromXbuild.has(key) || persistedXbuild.has(key)) return;
|
|
3866
|
+
if (contributors === void 0) return;
|
|
3867
|
+
persistedXbuild.add(key);
|
|
3868
|
+
try {
|
|
3869
|
+
await xbuild.set(key, {
|
|
3870
|
+
stars: repo.stargazers_count,
|
|
3871
|
+
contributors,
|
|
3872
|
+
language: repo.language,
|
|
3873
|
+
archived: repo.archived,
|
|
3874
|
+
fork: repo.fork,
|
|
3875
|
+
disabled: repo.disabled
|
|
3876
|
+
});
|
|
3877
|
+
} catch {
|
|
3878
|
+
}
|
|
3879
|
+
}
|
|
3701
3880
|
async function repoMeta(fullName) {
|
|
3702
3881
|
const key = repoKey(fullName);
|
|
3703
3882
|
const hit = repoCache.get(key);
|
|
3704
3883
|
if (hit !== void 0) return hit;
|
|
3884
|
+
await primeFromXbuild(key, fullName);
|
|
3885
|
+
const primed = repoCache.get(key);
|
|
3886
|
+
if (primed !== void 0) return primed;
|
|
3705
3887
|
const r = await client.json(`/repos/${fullName}`) ?? null;
|
|
3706
3888
|
repoCache.set(key, r);
|
|
3707
3889
|
return r;
|
|
@@ -3709,6 +3891,8 @@ async function aggregateContributions(opts = {}) {
|
|
|
3709
3891
|
async function repoContribCount(fullName) {
|
|
3710
3892
|
const key = repoKey(fullName);
|
|
3711
3893
|
if (contribCache.has(key)) return contribCache.get(key);
|
|
3894
|
+
await primeFromXbuild(key, fullName);
|
|
3895
|
+
if (contribCache.has(key)) return contribCache.get(key);
|
|
3712
3896
|
const n = await contributorCount(client, fullName);
|
|
3713
3897
|
contribCache.set(key, n);
|
|
3714
3898
|
return n;
|
|
@@ -3735,14 +3919,18 @@ async function aggregateContributions(opts = {}) {
|
|
|
3735
3919
|
if (isDenylistedRepo(fullName)) continue;
|
|
3736
3920
|
if (isAssigned(issue2)) continue;
|
|
3737
3921
|
if ((perRepo.get(repoKey(fullName)) ?? 0) >= MAX_BOUNTIES_PER_DISCOVERED_REPO) continue;
|
|
3922
|
+
const title = decodeEntities(issue2.title).trim();
|
|
3923
|
+
const body = issue2.body ? decodeEntities(issue2.body) : "";
|
|
3924
|
+
const labels = labelNames2(issue2.labels);
|
|
3925
|
+
if (looksLikeContentTask({ title, body, labels })) continue;
|
|
3738
3926
|
const repo = await repoMeta(fullName);
|
|
3739
3927
|
if (!repo) {
|
|
3740
3928
|
metaNull++;
|
|
3741
3929
|
continue;
|
|
3742
3930
|
}
|
|
3743
|
-
const title = decodeEntities(issue2.title).trim();
|
|
3744
3931
|
const contributors = await repoContribCount(fullName);
|
|
3745
3932
|
if (contributors === void 0) contribUndefined++;
|
|
3933
|
+
await persistRepoMeta(fullName, repo, contributors);
|
|
3746
3934
|
if (!passesContributionGate({
|
|
3747
3935
|
fullName,
|
|
3748
3936
|
stars: repo.stargazers_count,
|
|
@@ -3754,13 +3942,9 @@ async function aggregateContributions(opts = {}) {
|
|
|
3754
3942
|
continue;
|
|
3755
3943
|
}
|
|
3756
3944
|
if (repo.disabled) continue;
|
|
3757
|
-
const body = issue2.body ? decodeEntities(issue2.body) : "";
|
|
3758
|
-
const labels = labelNames2(issue2.labels);
|
|
3759
|
-
if (looksLikeContentTask({ title, body, labels })) continue;
|
|
3760
3945
|
const prRefs = await repoPRRefs(fullName);
|
|
3761
3946
|
if (prRefs === null) prRefsNull++;
|
|
3762
|
-
|
|
3763
|
-
const openPRsAtDiscovery = prRefs ? 0 : void 0;
|
|
3947
|
+
const openPRsAtDiscovery = prRefs ? prRefs.has(issue2.number) ? 1 : 0 : void 0;
|
|
3764
3948
|
const tags = normalize(
|
|
3765
3949
|
tokenize4([title, repo.language ?? "", labels.join(" "), body.slice(0, 2e3)].join(" "))
|
|
3766
3950
|
);
|
|
@@ -3851,6 +4035,7 @@ async function aggregateContributions(opts = {}) {
|
|
|
3851
4035
|
if (repo.disabled) continue;
|
|
3852
4036
|
const contributors = await repoContribCount(fullName);
|
|
3853
4037
|
if (contributors === void 0) contribUndefined++;
|
|
4038
|
+
await persistRepoMeta(fullName, repo, contributors);
|
|
3854
4039
|
if (repo.archived || repo.fork || repo.stargazers_count < MIN_STARS || contributors === void 0 || contributors < MIN_CONTRIBUTORS) {
|
|
3855
4040
|
continue;
|
|
3856
4041
|
}
|
|
@@ -3883,8 +4068,7 @@ async function aggregateContributions(opts = {}) {
|
|
|
3883
4068
|
if (looksLikeContentTask({ title, body, labels })) continue;
|
|
3884
4069
|
const prRefs = await repoPRRefs(fullName);
|
|
3885
4070
|
if (prRefs === null) prRefsNull++;
|
|
3886
|
-
|
|
3887
|
-
const openPRsAtDiscovery = prRefs ? 0 : void 0;
|
|
4071
|
+
const openPRsAtDiscovery = prRefs ? prRefs.has(issue2.number) ? 1 : 0 : void 0;
|
|
3888
4072
|
const tags = normalize(
|
|
3889
4073
|
tokenize4([title, repo.language ?? "", labels.join(" "), body.slice(0, 2e3)].join(" "))
|
|
3890
4074
|
);
|
|
@@ -3915,9 +4099,9 @@ async function aggregateContributions(opts = {}) {
|
|
|
3915
4099
|
const core = rl?.core ? `${rl.core.remaining}/${rl.core.limit}` : "n/a";
|
|
3916
4100
|
const search = rl?.search ? `${rl.search.remaining}/${rl.search.limit}` : "n/a";
|
|
3917
4101
|
const noToken = !(process.env["GITHUB_TOKEN"] ?? process.env["GH_TOKEN"]);
|
|
3918
|
-
const { pacedMs, secondaryAborted, budgetAborted, elapsedMs } = client.getStats();
|
|
4102
|
+
const { pacedMs, secondaryAborted, budgetAborted, elapsedMs, gqlCost, gqlRemaining } = client.getStats();
|
|
3919
4103
|
console.info(
|
|
3920
|
-
`[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)" : "")
|
|
4104
|
+
`[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)" : "")
|
|
3921
4105
|
);
|
|
3922
4106
|
if (discoveryBudgetStopped) {
|
|
3923
4107
|
console.warn(
|
|
@@ -3927,7 +4111,7 @@ async function aggregateContributions(opts = {}) {
|
|
|
3927
4111
|
}
|
|
3928
4112
|
return jobs;
|
|
3929
4113
|
}
|
|
3930
|
-
var GITHUB_API2,
|
|
4114
|
+
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;
|
|
3931
4115
|
var init_contributions = __esm({
|
|
3932
4116
|
"../../packages/core/src/feeds/contributions.ts"() {
|
|
3933
4117
|
"use strict";
|
|
@@ -3938,14 +4122,8 @@ var init_contributions = __esm({
|
|
|
3938
4122
|
init_contribution_classify();
|
|
3939
4123
|
init_github_bounties();
|
|
3940
4124
|
init_http();
|
|
4125
|
+
init_gh_governor();
|
|
3941
4126
|
GITHUB_API2 = "https://api.github.com";
|
|
3942
|
-
DEFAULT_REQ_GAP_MS = 75;
|
|
3943
|
-
SECONDARY_BACKOFF_CAP_S = 30;
|
|
3944
|
-
DEFAULT_BUILD_BUDGET_MS = 9e4;
|
|
3945
|
-
MIN_BUILD_BUDGET_MS = 1e4;
|
|
3946
|
-
MAX_BUILD_BUDGET_MS = 9e4;
|
|
3947
|
-
PROBE_TIMEOUT_MS = 3e3;
|
|
3948
|
-
realSleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
3949
4127
|
CONTRIB_LABEL_QUERIES = [
|
|
3950
4128
|
'label:"good first issue" type:issue state:open',
|
|
3951
4129
|
'label:"good-first-issue" type:issue state:open',
|
|
@@ -4163,12 +4341,22 @@ async function enrichWinnability(jobs, contribute, w) {
|
|
|
4163
4341
|
const repos = [...byRepo.keys()].sort((a, b) => starsOf(b) - starsOf(a));
|
|
4164
4342
|
const scored = repos.slice(0, maxRepos);
|
|
4165
4343
|
const cappedOut = repos.length - scored.length;
|
|
4344
|
+
const gov = w.governor ?? makeGitHubGovernor(
|
|
4345
|
+
w.fetchImpl ?? fetch,
|
|
4346
|
+
makeDefaultGovernorConfig({ paceEnabled: !w.fetchImpl })
|
|
4347
|
+
);
|
|
4348
|
+
let stoppedEarly = false;
|
|
4166
4349
|
for (const repo of scored) {
|
|
4350
|
+
if (gov.tripped() || gov.budgetExhausted()) {
|
|
4351
|
+
stoppedEarly = true;
|
|
4352
|
+
break;
|
|
4353
|
+
}
|
|
4167
4354
|
const targets = byRepo.get(repo) ?? [];
|
|
4168
4355
|
const receptivity = await fetchRepoReceptivity(repo, {
|
|
4169
4356
|
token: w.token,
|
|
4170
4357
|
fetchImpl: w.fetchImpl,
|
|
4171
|
-
now
|
|
4358
|
+
now,
|
|
4359
|
+
governor: gov
|
|
4172
4360
|
});
|
|
4173
4361
|
const stars = starsOf(repo);
|
|
4174
4362
|
const prev = priorStars[repo];
|
|
@@ -4197,6 +4385,12 @@ async function enrichWinnability(jobs, contribute, w) {
|
|
|
4197
4385
|
}
|
|
4198
4386
|
}
|
|
4199
4387
|
}
|
|
4388
|
+
if (stoppedEarly) {
|
|
4389
|
+
const reason = gov.tripped() ? "secondary-abuse breaker" : "wall-clock budget";
|
|
4390
|
+
console.warn(
|
|
4391
|
+
`[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.`
|
|
4392
|
+
);
|
|
4393
|
+
}
|
|
4200
4394
|
if (cappedOut > 0) {
|
|
4201
4395
|
console.warn(
|
|
4202
4396
|
`[winnability] receptivity cap hit \u2014 scored ${scored.length}/${repos.length} repos (${cappedOut} beyond maxRepos=${maxRepos} left un-scored, legacy ranking)`
|
|
@@ -4239,6 +4433,7 @@ var init_indexer = __esm({
|
|
|
4239
4433
|
init_contributions();
|
|
4240
4434
|
init_partners();
|
|
4241
4435
|
init_github();
|
|
4436
|
+
init_gh_governor();
|
|
4242
4437
|
init_winnability();
|
|
4243
4438
|
}
|
|
4244
4439
|
});
|
|
@@ -8126,6 +8321,7 @@ __export(src_exports, {
|
|
|
8126
8321
|
MERGE_PROBABILITY: () => MERGE_PROBABILITY,
|
|
8127
8322
|
MIN_CONTRIBUTORS: () => MIN_CONTRIBUTORS,
|
|
8128
8323
|
MIN_STARS: () => MIN_STARS,
|
|
8324
|
+
PROBE_TIMEOUT_MS: () => PROBE_TIMEOUT_MS,
|
|
8129
8325
|
RIGOR: () => RIGOR,
|
|
8130
8326
|
STRONG_MATCH_THRESHOLD: () => STRONG_MATCH_THRESHOLD,
|
|
8131
8327
|
SYNONYMS: () => SYNONYMS,
|
|
@@ -8197,6 +8393,9 @@ __export(src_exports, {
|
|
|
8197
8393
|
lever: () => lever,
|
|
8198
8394
|
loadPartnerRoles: () => loadPartnerRoles,
|
|
8199
8395
|
looksLikeEngRole: () => looksLikeEngRole,
|
|
8396
|
+
makeDefaultGovernorConfig: () => makeDefaultGovernorConfig,
|
|
8397
|
+
makeGitHubGovernor: () => makeGitHubGovernor,
|
|
8398
|
+
makeScoringGovernor: () => makeScoringGovernor,
|
|
8200
8399
|
match: () => match,
|
|
8201
8400
|
mergeProbability: () => mergeProbability,
|
|
8202
8401
|
mmrRerank: () => mmrRerank,
|
|
@@ -8209,8 +8408,12 @@ __export(src_exports, {
|
|
|
8209
8408
|
passesMaturityGate: () => passesMaturityGate,
|
|
8210
8409
|
personCardToJob: () => personCardToJob,
|
|
8211
8410
|
projectCardToJob: () => projectCardToJob,
|
|
8411
|
+
readBuildBudgetMs: () => readBuildBudgetMs,
|
|
8412
|
+
readReqGapMs: () => readReqGapMs,
|
|
8413
|
+
realSleep: () => realSleep,
|
|
8212
8414
|
recordClick: () => recordClick,
|
|
8213
8415
|
rejectExtraIntroFields: () => rejectExtraIntroFields,
|
|
8416
|
+
relevanceScore: () => relevanceScore,
|
|
8214
8417
|
revealIntroContacts: () => revealIntroContacts,
|
|
8215
8418
|
safetyNumber: () => safetyNumber,
|
|
8216
8419
|
sameLogin: () => sameLogin,
|
|
@@ -8237,6 +8440,7 @@ var init_src = __esm({
|
|
|
8237
8440
|
init_winnability();
|
|
8238
8441
|
init_partners();
|
|
8239
8442
|
init_github();
|
|
8443
|
+
init_gh_governor();
|
|
8240
8444
|
init_credit();
|
|
8241
8445
|
init_intro();
|
|
8242
8446
|
init_directoryThreshold();
|
|
@@ -8412,9 +8616,9 @@ var init_keytar = __esm({
|
|
|
8412
8616
|
}
|
|
8413
8617
|
});
|
|
8414
8618
|
|
|
8415
|
-
// node-file:/Users/ericgang/job-placement-inline
|
|
8619
|
+
// node-file:/Users/ericgang/job-placement-inline/node_modules/keytar/build/Release/keytar.node
|
|
8416
8620
|
var require_keytar = __commonJS({
|
|
8417
|
-
"node-file:/Users/ericgang/job-placement-inline
|
|
8621
|
+
"node-file:/Users/ericgang/job-placement-inline/node_modules/keytar/build/Release/keytar.node"(exports, module) {
|
|
8418
8622
|
"use strict";
|
|
8419
8623
|
init_keytar();
|
|
8420
8624
|
try {
|
|
@@ -10341,7 +10545,13 @@ function defaultPrompt(question) {
|
|
|
10341
10545
|
}
|
|
10342
10546
|
function rankContributions(fp, items) {
|
|
10343
10547
|
if (!fp || !Array.isArray(items) || items.length === 0) return [];
|
|
10344
|
-
|
|
10548
|
+
const ranked = match(fp, items, items.length);
|
|
10549
|
+
const rel = new Map(ranked.map((r) => [r, relevanceScore(fp, r.job)]));
|
|
10550
|
+
ranked.sort((a, b) => (rel.get(b) ?? 0) - (rel.get(a) ?? 0));
|
|
10551
|
+
return ranked.filter((r) => {
|
|
10552
|
+
const contested = (r.job.contribution?.openPRsAtDiscovery ?? 0) > 0;
|
|
10553
|
+
return !contested || (rel.get(r) ?? 0) >= STRONG_THRESHOLD;
|
|
10554
|
+
});
|
|
10345
10555
|
}
|
|
10346
10556
|
async function rankLocally(items, injectedFp) {
|
|
10347
10557
|
if (!Array.isArray(items) || items.length === 0) return [];
|
|
@@ -10412,7 +10622,7 @@ async function run6(opts = {}) {
|
|
|
10412
10622
|
process.exit(1);
|
|
10413
10623
|
}
|
|
10414
10624
|
}
|
|
10415
|
-
var TERMINALHIRE_DIR10, INDEX_CACHE_FILE4, INDEX_TTL_MS4, API_URL5, HEADER, OPT_IN_PROMPT, EMPTY_STATE, LANGUAGES;
|
|
10625
|
+
var TERMINALHIRE_DIR10, INDEX_CACHE_FILE4, INDEX_TTL_MS4, API_URL5, HEADER, OPT_IN_PROMPT, EMPTY_STATE, STRONG_THRESHOLD, LANGUAGES;
|
|
10416
10626
|
var init_jpi_contribute = __esm({
|
|
10417
10627
|
"bin/jpi-contribute.js"() {
|
|
10418
10628
|
"use strict";
|
|
@@ -10427,6 +10637,10 @@ var init_jpi_contribute = __esm({
|
|
|
10427
10637
|
HEADER = "Contribution opportunities \u2014 open issues where a merged PR actually counts toward your r\xE9sum\xE9.\nRepos \u226550\u2605 / \u226510 contributors \xB7 unassigned \xB7 merit-merge \xB7 matched locally to your stack.";
|
|
10428
10638
|
OPT_IN_PROMPT = "Contribute is off. Turn it on to see open issues where a merged PR would count toward your r\xE9sum\xE9 \u2014\nmatched to your stack. Matching runs locally; your profile never leaves this machine. Enable? [y/N]";
|
|
10429
10639
|
EMPTY_STATE = "Nothing clears the bar right now. We only list issues where a merged PR actually counts toward\nyour r\xE9sum\xE9 \u2014 so the list stays honest. Try again after the next refresh.";
|
|
10640
|
+
STRONG_THRESHOLD = 0.5;
|
|
10641
|
+
if (STRONG_THRESHOLD <= 0) {
|
|
10642
|
+
throw new Error("STRONG_THRESHOLD must be > 0 or the contested-issue fail-safe breaks");
|
|
10643
|
+
}
|
|
10430
10644
|
LANGUAGES = /* @__PURE__ */ new Set([
|
|
10431
10645
|
"typescript",
|
|
10432
10646
|
"javascript",
|
|
@@ -10830,6 +11044,8 @@ __export(jpi_claim_exports, {
|
|
|
10830
11044
|
fmtAge: () => fmtAge,
|
|
10831
11045
|
fmtContestedWarning: () => fmtContestedWarning,
|
|
10832
11046
|
isContested: () => isContested,
|
|
11047
|
+
isStrayArgShortRefClaim: () => isStrayArgShortRefClaim,
|
|
11048
|
+
isVerblessShortRefClaim: () => isVerblessShortRefClaim,
|
|
10833
11049
|
listOpenPRsReferencingIssue: () => listOpenPRsReferencingIssue,
|
|
10834
11050
|
matchReferencingPrs: () => matchReferencingPrs,
|
|
10835
11051
|
pickBodySource: () => pickBodySource,
|
|
@@ -10980,6 +11196,12 @@ function findClaimableInCache(id) {
|
|
|
10980
11196
|
function looksLikeShortRef(arg) {
|
|
10981
11197
|
return typeof arg === "string" && /^[A-Za-z0-9_-]{8}$/.test(arg);
|
|
10982
11198
|
}
|
|
11199
|
+
function isVerblessShortRefClaim(verb, positional) {
|
|
11200
|
+
return looksLikeShortRef(verb) && positional.length === 0;
|
|
11201
|
+
}
|
|
11202
|
+
function isStrayArgShortRefClaim(verb, positional) {
|
|
11203
|
+
return looksLikeShortRef(verb) && positional.length > 0;
|
|
11204
|
+
}
|
|
10983
11205
|
function findClaimableByShortRef(ref) {
|
|
10984
11206
|
try {
|
|
10985
11207
|
return findByShortRefInPool(readClaimablePool(), ref);
|
|
@@ -11019,9 +11241,12 @@ function extractClaimableFields(job) {
|
|
|
11019
11241
|
issueUrl: c.issueUrl ?? job.url ?? "",
|
|
11020
11242
|
amountUSD: null,
|
|
11021
11243
|
source: "contribute",
|
|
11022
|
-
// Fix 2: the aggregator
|
|
11023
|
-
// uncontested
|
|
11024
|
-
//
|
|
11244
|
+
// Fix 2: the aggregator persists a discovery-time open-PR presence flag
|
|
11245
|
+
// (0 = verified-uncontested, 1 = contested — kept and flagged, not dropped,
|
|
11246
|
+
// undefined = the PR check couldn't run). Carry it through so resolveBounty
|
|
11247
|
+
// can prefer it over a live re-count that degrades to null under a rate limit
|
|
11248
|
+
// / >100-PR page. Display/fallback ONLY — the contention gate still forces a
|
|
11249
|
+
// LIVE recount (see resolveBounty); the snapshot never decides contention.
|
|
11025
11250
|
openPRsAtDiscovery: c.openPRsAtDiscovery
|
|
11026
11251
|
};
|
|
11027
11252
|
}
|
|
@@ -12131,6 +12356,16 @@ async function run7() {
|
|
|
12131
12356
|
else await cmdPush({ keepUpdated: Boolean(flags["keep-updated"]) });
|
|
12132
12357
|
return;
|
|
12133
12358
|
}
|
|
12359
|
+
if (isVerblessShortRefClaim(verb, positional)) {
|
|
12360
|
+
await cmdRecord(verb, flags);
|
|
12361
|
+
return;
|
|
12362
|
+
}
|
|
12363
|
+
if (isStrayArgShortRefClaim(verb, positional)) {
|
|
12364
|
+
console.error(
|
|
12365
|
+
`terminalhire claim: '${verb}' is a short ref and takes no extra arguments (got: ${positional.join(" ")}). Did you mean \`terminalhire claim ${verb}\`?`
|
|
12366
|
+
);
|
|
12367
|
+
process.exit(1);
|
|
12368
|
+
}
|
|
12134
12369
|
try {
|
|
12135
12370
|
switch (verb) {
|
|
12136
12371
|
case "preview":
|