terminalhire 0.21.0 → 0.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/claim-push-bg.js +2 -2
- package/dist/bin/jpi-bounties.js +349 -41
- package/dist/bin/jpi-chat-read.js +80 -10
- package/dist/bin/jpi-chat.js +270 -80
- package/dist/bin/jpi-claim.js +6656 -160
- package/dist/bin/jpi-contribute.js +27 -2
- package/dist/bin/jpi-devs.js +365 -52
- package/dist/bin/jpi-dispatch.js +2304 -331
- package/dist/bin/jpi-hub.js +9712 -0
- package/dist/bin/jpi-inbox.js +269 -80
- package/dist/bin/jpi-init.js +312 -15
- package/dist/bin/jpi-intro.js +79 -35
- package/dist/bin/jpi-jobs.js +353 -38
- package/dist/bin/jpi-learn.js +27 -2
- package/dist/bin/jpi-login.js +314 -17
- package/dist/bin/jpi-profile.js +27 -2
- package/dist/bin/jpi-project.js +312 -15
- package/dist/bin/jpi-refresh.js +314 -17
- package/dist/bin/jpi-save.js +27 -2
- package/dist/bin/jpi-spinner.js +9 -0
- package/dist/bin/jpi-sync.js +27 -2
- package/dist/bin/jpi-trajectory.js +27 -2
- package/dist/bin/spinner.js +9 -0
- package/dist/bin/tui-core.js +440 -0
- package/dist/src/chat-client.js +11 -2
- package/dist/src/chat-keystore.js +11 -2
- package/dist/src/github-auth.js +2 -2
- package/dist/src/intro.js +79 -35
- package/dist/src/profile.js +11 -2
- package/dist/src/signal.js +9 -0
- package/dist/src/trajectory.js +27 -2
- package/package.json +1 -1
|
@@ -45,9 +45,9 @@ var init_keytar = __esm({
|
|
|
45
45
|
}
|
|
46
46
|
});
|
|
47
47
|
|
|
48
|
-
// node-file:/Users
|
|
48
|
+
// node-file:/private/tmp/claude-501/-Users-ericgang-job-placement-inline/91995792-9cff-48f4-ae9c-dee9e36fc319/scratchpad/release-v0230/node_modules/keytar/build/Release/keytar.node
|
|
49
49
|
var require_keytar = __commonJS({
|
|
50
|
-
"node-file:/Users
|
|
50
|
+
"node-file:/private/tmp/claude-501/-Users-ericgang-job-placement-inline/91995792-9cff-48f4-ae9c-dee9e36fc319/scratchpad/release-v0230/node_modules/keytar/build/Release/keytar.node"(exports, module) {
|
|
51
51
|
"use strict";
|
|
52
52
|
init_keytar();
|
|
53
53
|
try {
|
package/dist/bin/jpi-bounties.js
CHANGED
|
@@ -749,6 +749,30 @@ var init_contribution_gate = __esm({
|
|
|
749
749
|
}
|
|
750
750
|
});
|
|
751
751
|
|
|
752
|
+
// ../../packages/core/src/credential/rigor.ts
|
|
753
|
+
function deriveRigorTiers(input) {
|
|
754
|
+
const tiers = {};
|
|
755
|
+
if (input.reviewerAssociations !== void 0) {
|
|
756
|
+
tiers.maintainerReviewed = input.reviewerAssociations.some(
|
|
757
|
+
(a) => MAINTAINER_SET.has(String(a).toUpperCase())
|
|
758
|
+
);
|
|
759
|
+
}
|
|
760
|
+
return tiers;
|
|
761
|
+
}
|
|
762
|
+
var RIGOR, MAINTAINER_SET;
|
|
763
|
+
var init_rigor = __esm({
|
|
764
|
+
"../../packages/core/src/credential/rigor.ts"() {
|
|
765
|
+
"use strict";
|
|
766
|
+
RIGOR = {
|
|
767
|
+
/** `authorAssociation` values that count as a maintainer review. */
|
|
768
|
+
MAINTAINER_ASSOCIATIONS: ["OWNER", "MEMBER", "COLLABORATOR"]
|
|
769
|
+
};
|
|
770
|
+
MAINTAINER_SET = new Set(
|
|
771
|
+
RIGOR.MAINTAINER_ASSOCIATIONS.map((a) => a.toUpperCase())
|
|
772
|
+
);
|
|
773
|
+
}
|
|
774
|
+
});
|
|
775
|
+
|
|
752
776
|
// ../../packages/core/src/github.ts
|
|
753
777
|
function ghHeaders(token) {
|
|
754
778
|
const headers = {
|
|
@@ -761,9 +785,9 @@ function ghHeaders(token) {
|
|
|
761
785
|
if (token) headers["Authorization"] = `Bearer ${token}`;
|
|
762
786
|
return headers;
|
|
763
787
|
}
|
|
764
|
-
async function ghFetch(path, token) {
|
|
788
|
+
async function ghFetch(path, token, signal) {
|
|
765
789
|
const url = `https://api.github.com${path}`;
|
|
766
|
-
const res = await fetch(url, { headers: ghHeaders(token) });
|
|
790
|
+
const res = await fetch(url, { headers: ghHeaders(token), signal });
|
|
767
791
|
if (!res.ok) {
|
|
768
792
|
throw new Error(`GitHub API ${path}: HTTP ${res.status} ${res.statusText}`);
|
|
769
793
|
}
|
|
@@ -812,6 +836,7 @@ async function fetchGitHubProfile(login, token) {
|
|
|
812
836
|
} catch {
|
|
813
837
|
}
|
|
814
838
|
return {
|
|
839
|
+
id: user.id,
|
|
815
840
|
login: user.login,
|
|
816
841
|
name: user.name ?? void 0,
|
|
817
842
|
publicEmail: user.email ?? void 0,
|
|
@@ -888,8 +913,8 @@ async function fetchOwnedRepoTraction(login, token) {
|
|
|
888
913
|
computedAt
|
|
889
914
|
};
|
|
890
915
|
}
|
|
891
|
-
async function ghFetchRaw(path, token) {
|
|
892
|
-
return fetch(`https://api.github.com${path}`, { headers: ghHeaders(token) });
|
|
916
|
+
async function ghFetchRaw(path, token, signal) {
|
|
917
|
+
return fetch(`https://api.github.com${path}`, { headers: ghHeaders(token), signal });
|
|
893
918
|
}
|
|
894
919
|
function parseRepoUrl(repoUrl) {
|
|
895
920
|
const m = repoUrl.match(/\/repos\/([^/]+)\/([^/]+)\/?$/);
|
|
@@ -908,11 +933,12 @@ async function fetchOwnedOrgs(token) {
|
|
|
908
933
|
return /* @__PURE__ */ new Set();
|
|
909
934
|
}
|
|
910
935
|
}
|
|
911
|
-
async function repoContributorCount(owner, name, token) {
|
|
936
|
+
async function repoContributorCount(owner, name, token, signal) {
|
|
912
937
|
try {
|
|
913
938
|
const res = await ghFetchRaw(
|
|
914
939
|
`/repos/${owner}/${name}/contributors?per_page=1&anon=false`,
|
|
915
|
-
token
|
|
940
|
+
token,
|
|
941
|
+
signal
|
|
916
942
|
);
|
|
917
943
|
if (!res.ok) return void 0;
|
|
918
944
|
const link = res.headers.get("link");
|
|
@@ -1025,6 +1051,33 @@ async function computeAcceptanceFromSearch(login, token, ownedOrgs, cache, gates
|
|
|
1025
1051
|
if (mergedAt > b.lastMergedAt) b.lastMergedAt = mergedAt;
|
|
1026
1052
|
}
|
|
1027
1053
|
}
|
|
1054
|
+
if (token) {
|
|
1055
|
+
const enrichStats = { transient: 0 };
|
|
1056
|
+
const enrichCount = Math.min(qualifyingPRs.length, MAX_ENRICH_PRS);
|
|
1057
|
+
for (let i = 0; i < enrichCount; i++) {
|
|
1058
|
+
const pr = qualifyingPRs[i];
|
|
1059
|
+
const ref = parseGitHubRef(pr.url);
|
|
1060
|
+
if (!ref || ref.kind !== "pull") continue;
|
|
1061
|
+
try {
|
|
1062
|
+
const reviews = await ghFetch(
|
|
1063
|
+
`/repos/${ref.owner}/${ref.repo}/pulls/${ref.number}/reviews?per_page=100`,
|
|
1064
|
+
token
|
|
1065
|
+
);
|
|
1066
|
+
const reviewerAssociations = reviews.map((r) => r.author_association);
|
|
1067
|
+
const tiers = deriveRigorTiers({ reviewerAssociations });
|
|
1068
|
+
if (tiers.maintainerReviewed !== void 0) pr.maintainerReviewed = tiers.maintainerReviewed;
|
|
1069
|
+
} catch (err) {
|
|
1070
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1071
|
+
if (TRANSIENT_META_ERROR.test(msg)) {
|
|
1072
|
+
enrichStats.transient += 1;
|
|
1073
|
+
console.warn(
|
|
1074
|
+
`[acceptance] ${login}: per-PR rigor enrichment transient failure (${enrichStats.transient}) \u2014 leaving remaining tiers undefined rather than fabricating a false`
|
|
1075
|
+
);
|
|
1076
|
+
break;
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1028
1081
|
const finalDomains = {};
|
|
1029
1082
|
for (const [d, b] of Object.entries(byDomain)) {
|
|
1030
1083
|
finalDomains[d] = {
|
|
@@ -1180,14 +1233,103 @@ function deriveResumeTrend(cred, repoRecency, now = Date.now()) {
|
|
|
1180
1233
|
}
|
|
1181
1234
|
return scored.sort((a, b) => b.weight - a.weight).slice(0, 12).map((s) => s.t);
|
|
1182
1235
|
}
|
|
1183
|
-
|
|
1236
|
+
function parseGitHubRef(url) {
|
|
1237
|
+
const m = String(url ?? "").match(/github\.com\/([^/]+)\/([^/]+)\/(issues|pull)\/(\d+)/);
|
|
1238
|
+
if (!m) return null;
|
|
1239
|
+
return { owner: m[1], repo: m[2], number: parseInt(m[4], 10), kind: m[3] === "pull" ? "pull" : "issue" };
|
|
1240
|
+
}
|
|
1241
|
+
async function ghGraphQL(query, variables, token, signal) {
|
|
1242
|
+
const res = await fetch("https://api.github.com/graphql", {
|
|
1243
|
+
method: "POST",
|
|
1244
|
+
headers: { ...ghHeaders(token), "Content-Type": "application/json" },
|
|
1245
|
+
body: JSON.stringify({ query, variables }),
|
|
1246
|
+
signal
|
|
1247
|
+
});
|
|
1248
|
+
if (!res.ok) throw new Error(`GitHub GraphQL: HTTP ${res.status}`);
|
|
1249
|
+
const json = await res.json();
|
|
1250
|
+
if (json.errors?.length) throw new Error("GitHub GraphQL errors: " + JSON.stringify(json.errors));
|
|
1251
|
+
return json;
|
|
1252
|
+
}
|
|
1253
|
+
async function resolveClosingIssues(owner, name, number, body, token, signal) {
|
|
1254
|
+
if (token) {
|
|
1255
|
+
try {
|
|
1256
|
+
const q = `query($o:String!,$n:String!,$p:Int!){repository(owner:$o,name:$n){pullRequest(number:$p){closingIssuesReferences(first:20){nodes{number}}}}}`;
|
|
1257
|
+
const r = await ghGraphQL(q, { o: owner, n: name, p: number }, token, signal);
|
|
1258
|
+
const nodes = r.data?.repository?.pullRequest?.closingIssuesReferences?.nodes ?? [];
|
|
1259
|
+
return { closesIssues: nodes.map((x) => x.number), linkageSource: "graphql" };
|
|
1260
|
+
} catch {
|
|
1261
|
+
}
|
|
1262
|
+
}
|
|
1263
|
+
const nums = /* @__PURE__ */ new Set();
|
|
1264
|
+
const re = /\b(?:clos(?:e|es|ed)|fix(?:es|ed)?|resolv(?:e|es|ed))\s+#(\d+)/gi;
|
|
1265
|
+
let m;
|
|
1266
|
+
while ((m = re.exec(body)) !== null) nums.add(parseInt(m[1], 10));
|
|
1267
|
+
return { closesIssues: [...nums], linkageSource: nums.size ? "body-keyword" : "none" };
|
|
1268
|
+
}
|
|
1269
|
+
async function fetchPRScoringFacts(prUrl, token, signal) {
|
|
1270
|
+
const ref = parseGitHubRef(prUrl);
|
|
1271
|
+
if (!ref || ref.kind !== "pull") return null;
|
|
1272
|
+
const { owner, repo, number } = ref;
|
|
1273
|
+
const sig = signal ?? AbortSignal.timeout(1e4);
|
|
1274
|
+
let pr;
|
|
1275
|
+
try {
|
|
1276
|
+
pr = await ghFetch(`/repos/${owner}/${repo}/pulls/${number}`, token, sig);
|
|
1277
|
+
} catch {
|
|
1278
|
+
return null;
|
|
1279
|
+
}
|
|
1280
|
+
let repoMeta = null;
|
|
1281
|
+
try {
|
|
1282
|
+
repoMeta = await ghFetch(`/repos/${owner}/${repo}`, token, sig);
|
|
1283
|
+
} catch {
|
|
1284
|
+
}
|
|
1285
|
+
const contributors = await repoContributorCount(owner, repo, token, sig);
|
|
1286
|
+
const { closesIssues, linkageSource } = await resolveClosingIssues(owner, repo, number, pr.body ?? "", token, sig);
|
|
1287
|
+
let reviewerAssociations;
|
|
1288
|
+
try {
|
|
1289
|
+
const reviews = await ghFetch(
|
|
1290
|
+
`/repos/${owner}/${repo}/pulls/${number}/reviews?per_page=100`,
|
|
1291
|
+
token,
|
|
1292
|
+
sig
|
|
1293
|
+
);
|
|
1294
|
+
reviewerAssociations = reviews.map((r) => r.author_association);
|
|
1295
|
+
} catch {
|
|
1296
|
+
reviewerAssociations = void 0;
|
|
1297
|
+
}
|
|
1298
|
+
return {
|
|
1299
|
+
repo: `${owner}/${repo}`,
|
|
1300
|
+
prNumber: number,
|
|
1301
|
+
prUrl: pr.html_url,
|
|
1302
|
+
merged: pr.merged === true,
|
|
1303
|
+
mergedAt: pr.merged_at ?? null,
|
|
1304
|
+
authorId: pr.user?.id ?? null,
|
|
1305
|
+
authorLogin: pr.user?.login ?? null,
|
|
1306
|
+
mergedById: pr.merged_by?.id ?? null,
|
|
1307
|
+
mergedByLogin: pr.merged_by?.login ?? null,
|
|
1308
|
+
closesIssues,
|
|
1309
|
+
linkageSource,
|
|
1310
|
+
repoStars: repoMeta?.stargazers_count ?? null,
|
|
1311
|
+
repoContributors: contributors ?? null,
|
|
1312
|
+
repoArchived: !!repoMeta?.archived,
|
|
1313
|
+
repoFork: !!repoMeta?.fork,
|
|
1314
|
+
repoPrivate: !!repoMeta?.private,
|
|
1315
|
+
additions: pr.additions ?? null,
|
|
1316
|
+
deletions: pr.deletions ?? null,
|
|
1317
|
+
changedFiles: pr.changed_files ?? null,
|
|
1318
|
+
repoForks: repoMeta?.forks_count ?? null,
|
|
1319
|
+
reviewerAssociations,
|
|
1320
|
+
fetchedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1321
|
+
};
|
|
1322
|
+
}
|
|
1323
|
+
var TRACTION_TOP_N, CANDIDATE_PR_PAGE, MAX_ENRICH_PRS, OPEN_PR_PAGE, TRANSIENT_META_ERROR, RESUME_DECAY_HALF_LIFE_MS, RESUME_MIN_SCORE;
|
|
1184
1324
|
var init_github = __esm({
|
|
1185
1325
|
"../../packages/core/src/github.ts"() {
|
|
1186
1326
|
"use strict";
|
|
1187
1327
|
init_vocabulary();
|
|
1188
1328
|
init_contribution_gate();
|
|
1329
|
+
init_rigor();
|
|
1189
1330
|
TRACTION_TOP_N = 6;
|
|
1190
1331
|
CANDIDATE_PR_PAGE = 50;
|
|
1332
|
+
MAX_ENRICH_PRS = 12;
|
|
1191
1333
|
OPEN_PR_PAGE = 20;
|
|
1192
1334
|
TRANSIENT_META_ERROR = /HTTP 403|HTTP 429|rate limit|HTTP 5\d\d|timeout|network|fetch failed/i;
|
|
1193
1335
|
RESUME_DECAY_HALF_LIFE_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
@@ -2867,6 +3009,20 @@ var init_contribution_classify = __esm({
|
|
|
2867
3009
|
});
|
|
2868
3010
|
|
|
2869
3011
|
// ../../packages/core/src/feeds/contributions.ts
|
|
3012
|
+
function readReqGapMs() {
|
|
3013
|
+
const raw = process.env["CONTRIB_REQ_GAP_MS"];
|
|
3014
|
+
if (raw == null) return DEFAULT_REQ_GAP_MS;
|
|
3015
|
+
const n = Number.parseInt(raw, 10);
|
|
3016
|
+
if (Number.isNaN(n)) return DEFAULT_REQ_GAP_MS;
|
|
3017
|
+
return Math.min(Math.max(n, 0), 1e3);
|
|
3018
|
+
}
|
|
3019
|
+
function readBuildBudgetMs() {
|
|
3020
|
+
const raw = process.env["CONTRIB_BUILD_BUDGET_MS"];
|
|
3021
|
+
if (raw == null) return DEFAULT_BUILD_BUDGET_MS;
|
|
3022
|
+
const n = Number.parseInt(raw, 10);
|
|
3023
|
+
if (Number.isNaN(n)) return DEFAULT_BUILD_BUDGET_MS;
|
|
3024
|
+
return Math.min(Math.max(n, MIN_BUILD_BUDGET_MS), MAX_BUILD_BUDGET_MS);
|
|
3025
|
+
}
|
|
2870
3026
|
function authHeaders2() {
|
|
2871
3027
|
const token = process.env["GITHUB_TOKEN"] ?? process.env["GH_TOKEN"];
|
|
2872
3028
|
const h = {
|
|
@@ -2887,10 +3043,55 @@ function repoFullNameFromApiUrl2(url) {
|
|
|
2887
3043
|
const m = url.match(/\/repos\/([^/]+)\/([^/]+)\/?$/);
|
|
2888
3044
|
return m ? `${m[1]}/${m[2]}` : null;
|
|
2889
3045
|
}
|
|
2890
|
-
function makeClient(fetchImpl) {
|
|
3046
|
+
function makeClient(fetchImpl, cfg) {
|
|
3047
|
+
const startedAt = cfg.now();
|
|
3048
|
+
let lastRequestAt = 0;
|
|
3049
|
+
let pacedMs = 0;
|
|
3050
|
+
let secondaryHits = 0;
|
|
3051
|
+
let aborted = false;
|
|
3052
|
+
let secondaryAborted = false;
|
|
3053
|
+
let budgetAborted = false;
|
|
3054
|
+
let coreHealthyAtStart = false;
|
|
3055
|
+
async function noteAndMaybeBackOff(res) {
|
|
3056
|
+
if (res.status !== 403) return;
|
|
3057
|
+
const remaining = res.headers.get("x-ratelimit-remaining");
|
|
3058
|
+
const retryAfter = res.headers.get("retry-after");
|
|
3059
|
+
const positiveSecondary = retryAfter != null || remaining != null && remaining !== "0";
|
|
3060
|
+
const isSecondary = positiveSecondary || coreHealthyAtStart;
|
|
3061
|
+
if (!isSecondary) return;
|
|
3062
|
+
secondaryHits++;
|
|
3063
|
+
if (secondaryHits >= 2) {
|
|
3064
|
+
aborted = true;
|
|
3065
|
+
secondaryAborted = true;
|
|
3066
|
+
return;
|
|
3067
|
+
}
|
|
3068
|
+
if (cfg.paceEnabled) {
|
|
3069
|
+
const trimmed = (retryAfter ?? "").trim();
|
|
3070
|
+
const parsed = trimmed.length === 0 ? Number.NaN : Number(trimmed);
|
|
3071
|
+
const sec = Number.isNaN(parsed) ? SECONDARY_BACKOFF_CAP_S : Math.min(Math.max(parsed, 0), SECONDARY_BACKOFF_CAP_S);
|
|
3072
|
+
const remaining2 = Math.max(0, cfg.budgetMs - (cfg.now() - startedAt));
|
|
3073
|
+
await cfg.sleep(Math.min(sec * 1e3, remaining2));
|
|
3074
|
+
}
|
|
3075
|
+
}
|
|
2891
3076
|
async function raw(path) {
|
|
3077
|
+
if (aborted) return null;
|
|
3078
|
+
if (cfg.now() - startedAt > cfg.budgetMs) {
|
|
3079
|
+
aborted = true;
|
|
3080
|
+
budgetAborted = true;
|
|
3081
|
+
return null;
|
|
3082
|
+
}
|
|
3083
|
+
if (cfg.paceEnabled && cfg.gapMs > 0) {
|
|
3084
|
+
const wait = cfg.gapMs - (cfg.now() - lastRequestAt);
|
|
3085
|
+
if (wait > 0) {
|
|
3086
|
+
await cfg.sleep(wait);
|
|
3087
|
+
pacedMs += wait;
|
|
3088
|
+
}
|
|
3089
|
+
lastRequestAt = cfg.now();
|
|
3090
|
+
}
|
|
2892
3091
|
try {
|
|
2893
|
-
|
|
3092
|
+
const res = await fetchImpl(`${GITHUB_API2}${path}`, { headers: authHeaders2() });
|
|
3093
|
+
await noteAndMaybeBackOff(res);
|
|
3094
|
+
return res;
|
|
2894
3095
|
} catch {
|
|
2895
3096
|
return null;
|
|
2896
3097
|
}
|
|
@@ -2906,7 +3107,42 @@ function makeClient(fetchImpl) {
|
|
|
2906
3107
|
return null;
|
|
2907
3108
|
}
|
|
2908
3109
|
}
|
|
2909
|
-
|
|
3110
|
+
async function probe(path) {
|
|
3111
|
+
const bound = cfg.probeTimeoutMs;
|
|
3112
|
+
let timer;
|
|
3113
|
+
const fetchP = fetchImpl(`${GITHUB_API2}${path}`, {
|
|
3114
|
+
headers: authHeaders2()
|
|
3115
|
+
}).then(
|
|
3116
|
+
(r) => r,
|
|
3117
|
+
() => null
|
|
3118
|
+
);
|
|
3119
|
+
try {
|
|
3120
|
+
const res = bound == null ? await fetchP : await Promise.race([
|
|
3121
|
+
fetchP,
|
|
3122
|
+
new Promise((resolve) => {
|
|
3123
|
+
timer = setTimeout(() => resolve(null), bound);
|
|
3124
|
+
})
|
|
3125
|
+
]);
|
|
3126
|
+
if (!res || !res.ok) return null;
|
|
3127
|
+
return await res.json();
|
|
3128
|
+
} catch {
|
|
3129
|
+
return null;
|
|
3130
|
+
} finally {
|
|
3131
|
+
if (timer) clearTimeout(timer);
|
|
3132
|
+
}
|
|
3133
|
+
}
|
|
3134
|
+
function setSecondaryHint(coreHealthy) {
|
|
3135
|
+
coreHealthyAtStart = coreHealthy;
|
|
3136
|
+
}
|
|
3137
|
+
function getStats() {
|
|
3138
|
+
return {
|
|
3139
|
+
pacedMs,
|
|
3140
|
+
secondaryAborted: secondaryAborted ? 1 : 0,
|
|
3141
|
+
budgetAborted: budgetAborted ? 1 : 0,
|
|
3142
|
+
elapsedMs: cfg.now() - startedAt
|
|
3143
|
+
};
|
|
3144
|
+
}
|
|
3145
|
+
return { raw, json, probe, setSecondaryHint, getStats };
|
|
2910
3146
|
}
|
|
2911
3147
|
async function contributorCount(client, fullName) {
|
|
2912
3148
|
const res = await client.raw(`/repos/${fullName}/contributors?per_page=1&anon=false`);
|
|
@@ -2936,7 +3172,7 @@ ${pr.body ?? ""}`.matchAll(/#(\d+)\b/g)) {
|
|
|
2936
3172
|
return refs;
|
|
2937
3173
|
}
|
|
2938
3174
|
async function fetchRateLimit(client) {
|
|
2939
|
-
const r = await client.
|
|
3175
|
+
const r = await client.probe("/rate_limit");
|
|
2940
3176
|
return r?.resources ?? null;
|
|
2941
3177
|
}
|
|
2942
3178
|
async function searchContribIssues(client, queries) {
|
|
@@ -2955,8 +3191,21 @@ async function searchContribIssues(client, queries) {
|
|
|
2955
3191
|
);
|
|
2956
3192
|
}
|
|
2957
3193
|
async function aggregateContributions(opts = {}) {
|
|
2958
|
-
const
|
|
3194
|
+
const paceEnabled = opts.paceEnabled ?? !opts.fetchImpl;
|
|
3195
|
+
const client = makeClient(opts.fetchImpl ?? fetchWithTimeout, {
|
|
3196
|
+
paceEnabled,
|
|
3197
|
+
gapMs: readReqGapMs(),
|
|
3198
|
+
budgetMs: readBuildBudgetMs(),
|
|
3199
|
+
sleep: opts.sleepImpl ?? realSleep,
|
|
3200
|
+
now: opts.nowImpl ?? Date.now,
|
|
3201
|
+
// Bound the unguarded probe on the REAL network only; injected-fetch tests get
|
|
3202
|
+
// null so the probe stays deterministic (and its shared spy sleeper untouched).
|
|
3203
|
+
probeTimeoutMs: opts.fetchImpl ? null : PROBE_TIMEOUT_MS
|
|
3204
|
+
});
|
|
2959
3205
|
const queries = opts.queries ?? CONTRIB_SEARCH_QUERIES;
|
|
3206
|
+
const startRl = await fetchRateLimit(client);
|
|
3207
|
+
const coreHealthyAtStart = (startRl?.core?.remaining ?? 0) >= 500;
|
|
3208
|
+
client.setSecondaryHint(coreHealthyAtStart);
|
|
2960
3209
|
const issues = (await searchContribIssues(client, queries)).slice(0, MAX_CONTRIB_ISSUES_SCANNED);
|
|
2961
3210
|
const repoCache = /* @__PURE__ */ new Map();
|
|
2962
3211
|
const contribCache = /* @__PURE__ */ new Map();
|
|
@@ -3063,13 +3312,14 @@ async function aggregateContributions(opts = {}) {
|
|
|
3063
3312
|
const core = rl?.core ? `${rl.core.remaining}/${rl.core.limit}` : "n/a";
|
|
3064
3313
|
const search = rl?.search ? `${rl.search.remaining}/${rl.search.limit}` : "n/a";
|
|
3065
3314
|
const noToken = !(process.env["GITHUB_TOKEN"] ?? process.env["GH_TOKEN"]);
|
|
3315
|
+
const { pacedMs, secondaryAborted, budgetAborted, elapsedMs } = client.getStats();
|
|
3066
3316
|
console.info(
|
|
3067
|
-
`[contribute] build metrics \u2014 scanned=${issues.length} reposDistinct=${repoCache.size} emitted=${jobs.length} metaNull=${metaNull} contribUndefined=${contribUndefined} prRefsNull=${prRefsNull} core=${core} search=${search}` + (noToken ? " (NO TOKEN \u2192 60/hr)" : "")
|
|
3317
|
+
`[contribute] build metrics \u2014 scanned=${issues.length} reposDistinct=${repoCache.size} emitted=${jobs.length} metaNull=${metaNull} contribUndefined=${contribUndefined} prRefsNull=${prRefsNull} paced=${pacedMs} secondaryAborted=${secondaryAborted} budgetAborted=${budgetAborted} core=${core} search=${search} elapsed=${elapsedMs}` + (noToken ? " (NO TOKEN \u2192 60/hr)" : "")
|
|
3068
3318
|
);
|
|
3069
3319
|
}
|
|
3070
3320
|
return jobs;
|
|
3071
3321
|
}
|
|
3072
|
-
var GITHUB_API2, CONTRIB_LABEL_QUERIES, CONTRIB_LANGUAGE_QUERIES, CONTRIB_SEARCH_QUERIES, SEARCH_PER_PAGE2, MAX_CONTRIB_ITEMS, MAX_CONTRIB_ISSUES_SCANNED;
|
|
3322
|
+
var GITHUB_API2, DEFAULT_REQ_GAP_MS, SECONDARY_BACKOFF_CAP_S, DEFAULT_BUILD_BUDGET_MS, MIN_BUILD_BUDGET_MS, MAX_BUILD_BUDGET_MS, PROBE_TIMEOUT_MS, realSleep, CONTRIB_LABEL_QUERIES, CONTRIB_LANGUAGE_QUERIES, CONTRIB_SEARCH_QUERIES, SEARCH_PER_PAGE2, MAX_CONTRIB_ITEMS, MAX_CONTRIB_ISSUES_SCANNED;
|
|
3073
3323
|
var init_contributions = __esm({
|
|
3074
3324
|
"../../packages/core/src/feeds/contributions.ts"() {
|
|
3075
3325
|
"use strict";
|
|
@@ -3081,6 +3331,13 @@ var init_contributions = __esm({
|
|
|
3081
3331
|
init_github_bounties();
|
|
3082
3332
|
init_http();
|
|
3083
3333
|
GITHUB_API2 = "https://api.github.com";
|
|
3334
|
+
DEFAULT_REQ_GAP_MS = 75;
|
|
3335
|
+
SECONDARY_BACKOFF_CAP_S = 30;
|
|
3336
|
+
DEFAULT_BUILD_BUDGET_MS = 9e4;
|
|
3337
|
+
MIN_BUILD_BUDGET_MS = 1e4;
|
|
3338
|
+
MAX_BUILD_BUDGET_MS = 9e4;
|
|
3339
|
+
PROBE_TIMEOUT_MS = 3e3;
|
|
3340
|
+
realSleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
3084
3341
|
CONTRIB_LABEL_QUERIES = [
|
|
3085
3342
|
'label:"good first issue" type:issue state:open',
|
|
3086
3343
|
'label:"good-first-issue" type:issue state:open',
|
|
@@ -3197,6 +3454,36 @@ var init_indexer = __esm({
|
|
|
3197
3454
|
}
|
|
3198
3455
|
});
|
|
3199
3456
|
|
|
3457
|
+
// ../../packages/core/src/credit.ts
|
|
3458
|
+
function verifyClaimCredit(claim, facts) {
|
|
3459
|
+
const reasons = [];
|
|
3460
|
+
const fail = (code, message) => reasons.push({ code, message });
|
|
3461
|
+
const norm = (r) => r.trim().toLowerCase();
|
|
3462
|
+
if (norm(facts.repo) !== norm(claim.repo))
|
|
3463
|
+
fail("repo-mismatch", `PR is in ${facts.repo}, claim is against ${claim.repo}`);
|
|
3464
|
+
if (!facts.merged) fail("not-merged", `PR #${facts.prNumber} is not merged`);
|
|
3465
|
+
if (facts.authorId == null || facts.authorId !== claim.claimantId)
|
|
3466
|
+
fail("author-mismatch", `PR author id ${facts.authorId} !== claimant id ${claim.claimantId}`);
|
|
3467
|
+
if (facts.merged && facts.mergedById != null && facts.authorId != null && facts.mergedById === facts.authorId)
|
|
3468
|
+
fail("self-merged", `PR was merged by its own author (id ${facts.authorId})`);
|
|
3469
|
+
if (claim.claimedIssueNumber != null) {
|
|
3470
|
+
if (facts.closesIssues.length === 0)
|
|
3471
|
+
fail("issue-linkage-missing", `PR closes no issue; claim names #${claim.claimedIssueNumber}`);
|
|
3472
|
+
else if (!facts.closesIssues.includes(claim.claimedIssueNumber))
|
|
3473
|
+
fail(
|
|
3474
|
+
"issue-linkage-mismatch",
|
|
3475
|
+
`PR closes ${facts.closesIssues.map((n) => "#" + n).join(", ")}; claim names #${claim.claimedIssueNumber}`
|
|
3476
|
+
);
|
|
3477
|
+
}
|
|
3478
|
+
if (reasons.length === 0) reasons.push({ code: "ok", message: "all credit predicates hold" });
|
|
3479
|
+
return { ok: reasons.every((r) => r.code === "ok"), reasons };
|
|
3480
|
+
}
|
|
3481
|
+
var init_credit = __esm({
|
|
3482
|
+
"../../packages/core/src/credit.ts"() {
|
|
3483
|
+
"use strict";
|
|
3484
|
+
}
|
|
3485
|
+
});
|
|
3486
|
+
|
|
3200
3487
|
// ../../packages/core/src/intro.ts
|
|
3201
3488
|
function buildIntroPayload(input) {
|
|
3202
3489
|
const payload = {
|
|
@@ -6554,10 +6841,13 @@ function deriveLegibleProfile(credential, recency, traction, seniorityBand) {
|
|
|
6554
6841
|
if (daysAgo !== void 0) s += ` \u2014 most recent ${daysAgo}d ago`;
|
|
6555
6842
|
proofSentence = `${s}.`;
|
|
6556
6843
|
}
|
|
6844
|
+
const enrichedPRs = ok ? credential.qualifyingPRs ?? [] : [];
|
|
6845
|
+
const maintainerReviewedCount = enrichedPRs.some((p) => p.maintainerReviewed !== void 0) ? enrichedPRs.filter((p) => p.maintainerReviewed === true).length : void 0;
|
|
6557
6846
|
const auditableBadge = ok ? {
|
|
6558
6847
|
mergedTotal: credential.qualifyingTotal,
|
|
6559
6848
|
distinctOrgs: orgCount,
|
|
6560
|
-
thresholds: { stars: MIN_STARS, contributors: MIN_CONTRIBUTORS }
|
|
6849
|
+
thresholds: { stars: MIN_STARS, contributors: MIN_CONTRIBUTORS },
|
|
6850
|
+
...maintainerReviewedCount !== void 0 ? { maintainerReviewedCount } : {}
|
|
6561
6851
|
} : null;
|
|
6562
6852
|
const profile = {
|
|
6563
6853
|
headline,
|
|
@@ -6753,6 +7043,7 @@ __export(src_exports, {
|
|
|
6753
7043
|
MENTION_DELTA: () => MENTION_DELTA,
|
|
6754
7044
|
MIN_CONTRIBUTORS: () => MIN_CONTRIBUTORS,
|
|
6755
7045
|
MIN_STARS: () => MIN_STARS,
|
|
7046
|
+
RIGOR: () => RIGOR,
|
|
6756
7047
|
STRONG_MATCH_THRESHOLD: () => STRONG_MATCH_THRESHOLD,
|
|
6757
7048
|
SYNONYMS: () => SYNONYMS,
|
|
6758
7049
|
TRIVIAL_PR_TITLE: () => TRIVIAL_PR_TITLE,
|
|
@@ -6781,6 +7072,7 @@ __export(src_exports, {
|
|
|
6781
7072
|
decryptMessage: () => decryptMessage,
|
|
6782
7073
|
deriveLegibleProfile: () => deriveLegibleProfile,
|
|
6783
7074
|
deriveResumeTrend: () => deriveResumeTrend,
|
|
7075
|
+
deriveRigorTiers: () => deriveRigorTiers,
|
|
6784
7076
|
deriveSharedKey: () => deriveSharedKey,
|
|
6785
7077
|
deriveTrajectoryNarrative: () => deriveTrajectoryNarrative,
|
|
6786
7078
|
displayableDrift: () => displayableDrift,
|
|
@@ -6790,6 +7082,7 @@ __export(src_exports, {
|
|
|
6790
7082
|
fetchGitHubProfile: () => fetchGitHubProfile,
|
|
6791
7083
|
fetchOpenExternalPRs: () => fetchOpenExternalPRs,
|
|
6792
7084
|
fetchOwnedRepoTraction: () => fetchOwnedRepoTraction,
|
|
7085
|
+
fetchPRScoringFacts: () => fetchPRScoringFacts,
|
|
6793
7086
|
fetchRepoRecency: () => fetchRepoRecency,
|
|
6794
7087
|
flattenTiers: () => flattenTiers,
|
|
6795
7088
|
funnelCounts: () => funnelCounts,
|
|
@@ -6820,6 +7113,7 @@ __export(src_exports, {
|
|
|
6820
7113
|
opire: () => opire,
|
|
6821
7114
|
opportunityShortToken: () => opportunityShortToken,
|
|
6822
7115
|
pageMatches: () => pageMatches,
|
|
7116
|
+
parseGitHubRef: () => parseGitHubRef,
|
|
6823
7117
|
passesContributionGate: () => passesContributionGate,
|
|
6824
7118
|
passesMaturityGate: () => passesMaturityGate,
|
|
6825
7119
|
personCardToJob: () => personCardToJob,
|
|
@@ -6836,6 +7130,7 @@ __export(src_exports, {
|
|
|
6836
7130
|
validateGraph: () => validateGraph,
|
|
6837
7131
|
validateIntroPayload: () => validateIntroPayload,
|
|
6838
7132
|
validateTargetContact: () => validateTargetContact,
|
|
7133
|
+
verifyClaimCredit: () => verifyClaimCredit,
|
|
6839
7134
|
workable: () => workable,
|
|
6840
7135
|
wwr: () => wwr
|
|
6841
7136
|
});
|
|
@@ -6850,12 +7145,14 @@ var init_src = __esm({
|
|
|
6850
7145
|
init_indexer();
|
|
6851
7146
|
init_partners();
|
|
6852
7147
|
init_github();
|
|
7148
|
+
init_credit();
|
|
6853
7149
|
init_intro();
|
|
6854
7150
|
init_directoryThreshold();
|
|
6855
7151
|
init_chatCrypto();
|
|
6856
7152
|
init_job_status();
|
|
6857
7153
|
init_legible();
|
|
6858
7154
|
init_legible_trajectory();
|
|
7155
|
+
init_rigor();
|
|
6859
7156
|
init_short_token();
|
|
6860
7157
|
}
|
|
6861
7158
|
});
|
|
@@ -6868,9 +7165,9 @@ var init_keytar = __esm({
|
|
|
6868
7165
|
}
|
|
6869
7166
|
});
|
|
6870
7167
|
|
|
6871
|
-
// node-file:/Users
|
|
7168
|
+
// node-file:/private/tmp/claude-501/-Users-ericgang-job-placement-inline/91995792-9cff-48f4-ae9c-dee9e36fc319/scratchpad/release-v0230/node_modules/keytar/build/Release/keytar.node
|
|
6872
7169
|
var require_keytar = __commonJS({
|
|
6873
|
-
"node-file:/Users
|
|
7170
|
+
"node-file:/private/tmp/claude-501/-Users-ericgang-job-placement-inline/91995792-9cff-48f4-ae9c-dee9e36fc319/scratchpad/release-v0230/node_modules/keytar/build/Release/keytar.node"(exports, module) {
|
|
6874
7171
|
"use strict";
|
|
6875
7172
|
init_keytar();
|
|
6876
7173
|
try {
|
|
@@ -7290,36 +7587,46 @@ ${i + 1}. ${linkTitle(job.title, job.url)} [${ref}]`);
|
|
|
7290
7587
|
console.log(` Claim: ${sanitizeText(b.claimUrl ?? job.url)}`);
|
|
7291
7588
|
console.log(` \u2192 terminalhire claim ${ref}`);
|
|
7292
7589
|
}
|
|
7590
|
+
async function getBounties({ quiet = false, offline = false } = {}) {
|
|
7591
|
+
if (!quiet) console.log(`Fetching bounty index from ${API_URL}/api/index...`);
|
|
7592
|
+
const index = offline ? readIndexCache() : await fetchIndex();
|
|
7593
|
+
if (offline && !index) return { status: "no-cache" };
|
|
7594
|
+
let bounties = (index.jobs ?? []).filter((j) => j.source === "bounty");
|
|
7595
|
+
if (PRICED_ONLY) bounties = bounties.filter((j) => j.bounty?.amountUSD != null);
|
|
7596
|
+
if (WINNABLE_ONLY) bounties = bounties.filter((j) => (j.bounty?.competingOpenPRs ?? 0) === 0);
|
|
7597
|
+
if (bounties.length === 0) {
|
|
7598
|
+
return { status: "empty" };
|
|
7599
|
+
}
|
|
7600
|
+
const ranked = /* @__PURE__ */ new Map();
|
|
7601
|
+
try {
|
|
7602
|
+
const { readProfile: readProfile2, profileToFingerprint: profileToFingerprint2 } = await Promise.resolve().then(() => (init_profile(), profile_exports));
|
|
7603
|
+
const { match: match2 } = await Promise.resolve().then(() => (init_src(), src_exports));
|
|
7604
|
+
const profile = await readProfile2();
|
|
7605
|
+
if (profile.skillTags.length > 0) {
|
|
7606
|
+
const fp = profileToFingerprint2(profile);
|
|
7607
|
+
for (const r of match2(fp, bounties, bounties.length)) {
|
|
7608
|
+
ranked.set(r.job.id, { score: r.score, reason: r.reason, matchedTags: r.matchedTags });
|
|
7609
|
+
}
|
|
7610
|
+
}
|
|
7611
|
+
} catch {
|
|
7612
|
+
}
|
|
7613
|
+
const score = (j) => ranked.get(j.id)?.score ?? 0;
|
|
7614
|
+
const amt = (j) => j.bounty?.amountUSD ?? -1;
|
|
7615
|
+
const contested = (j) => (j.bounty?.competingOpenPRs ?? 0) > 0 ? 1 : 0;
|
|
7616
|
+
bounties.sort((a, b) => contested(a) - contested(b) || score(b) - score(a) || amt(b) - amt(a));
|
|
7617
|
+
const matchedCount = bounties.filter((j) => score(j) > 0).length;
|
|
7618
|
+
return { status: "ok", bounties, ranked, matchedCount };
|
|
7619
|
+
}
|
|
7293
7620
|
async function run() {
|
|
7294
7621
|
try {
|
|
7295
|
-
|
|
7296
|
-
|
|
7297
|
-
let bounties = (index.jobs ?? []).filter((j) => j.source === "bounty");
|
|
7298
|
-
if (PRICED_ONLY) bounties = bounties.filter((j) => j.bounty?.amountUSD != null);
|
|
7299
|
-
if (WINNABLE_ONLY) bounties = bounties.filter((j) => (j.bounty?.competingOpenPRs ?? 0) === 0);
|
|
7300
|
-
if (bounties.length === 0) {
|
|
7622
|
+
const result = await getBounties();
|
|
7623
|
+
if (result.status === "empty") {
|
|
7301
7624
|
console.log("\nNo bounties available right now. Try again later \u2014 supply refreshes through the day.");
|
|
7302
7625
|
return;
|
|
7303
7626
|
}
|
|
7304
|
-
|
|
7305
|
-
|
|
7306
|
-
const { readProfile: readProfile2, profileToFingerprint: profileToFingerprint2 } = await Promise.resolve().then(() => (init_profile(), profile_exports));
|
|
7307
|
-
const { match: match2 } = await Promise.resolve().then(() => (init_src(), src_exports));
|
|
7308
|
-
const profile = await readProfile2();
|
|
7309
|
-
if (profile.skillTags.length > 0) {
|
|
7310
|
-
const fp = profileToFingerprint2(profile);
|
|
7311
|
-
for (const r of match2(fp, bounties, bounties.length)) {
|
|
7312
|
-
ranked.set(r.job.id, { score: r.score, reason: r.reason, matchedTags: r.matchedTags });
|
|
7313
|
-
}
|
|
7314
|
-
}
|
|
7315
|
-
} catch {
|
|
7316
|
-
}
|
|
7317
|
-
const score = (j) => ranked.get(j.id)?.score ?? 0;
|
|
7318
|
-
const amt = (j) => j.bounty?.amountUSD ?? -1;
|
|
7319
|
-
const contested = (j) => (j.bounty?.competingOpenPRs ?? 0) > 0 ? 1 : 0;
|
|
7320
|
-
bounties.sort((a, b) => contested(a) - contested(b) || score(b) - score(a) || amt(b) - amt(a));
|
|
7627
|
+
if (result.status !== "ok") return;
|
|
7628
|
+
const { bounties, ranked, matchedCount } = result;
|
|
7321
7629
|
const shown = SHOW_ALL ? bounties : bounties.slice(0, LIMIT);
|
|
7322
|
-
const matchedCount = bounties.filter((j) => score(j) > 0).length;
|
|
7323
7630
|
console.log(
|
|
7324
7631
|
`
|
|
7325
7632
|
\u26A1 ${bounties.length} bount${bounties.length === 1 ? "y" : "ies"} you could knock out` + (matchedCount ? ` \u2014 ${matchedCount} matched to your profile` : "") + ` (local rank \u2014 no data sent)
|
|
@@ -7351,6 +7658,7 @@ Open this to claim/work the bounty (you go straight to the source \u2014 we neve
|
|
|
7351
7658
|
}
|
|
7352
7659
|
}
|
|
7353
7660
|
export {
|
|
7661
|
+
getBounties,
|
|
7354
7662
|
run
|
|
7355
7663
|
};
|
|
7356
7664
|
/*! Bundled license information:
|