terminalhire 0.12.0 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/jpi-bounties.js +443 -9
- package/dist/bin/jpi-chat-read.js +84 -2
- package/dist/bin/jpi-chat.js +84 -2
- package/dist/bin/jpi-config.js +3 -1
- package/dist/bin/jpi-contribute.js +1616 -0
- package/dist/bin/jpi-devs.js +443 -9
- package/dist/bin/jpi-dispatch.js +1733 -872
- package/dist/bin/jpi-init.js +58 -24
- package/dist/bin/jpi-intro.js +81 -1
- package/dist/bin/jpi-jobs.js +443 -9
- package/dist/bin/jpi-learn.js +81 -1
- package/dist/bin/jpi-link.js +3 -1
- package/dist/bin/jpi-login.js +461 -26
- package/dist/bin/jpi-profile.js +81 -1
- package/dist/bin/jpi-project.js +445 -11
- package/dist/bin/jpi-refresh.js +789 -123
- package/dist/bin/jpi-save.js +81 -1
- package/dist/bin/jpi-spinner.js +64 -26
- package/dist/bin/jpi-statusline.js +1 -1
- package/dist/bin/jpi-sync.js +81 -1
- package/dist/bin/jpi-trajectory.js +42 -1
- package/dist/bin/jpi.js +73 -6
- package/dist/bin/peer-connect-prompt.js +17 -16
- package/dist/bin/spinner.js +252 -64
- package/dist/src/chat-client.js +8 -1
- package/dist/src/chat-keystore.js +8 -1
- package/dist/src/config.js +7 -1
- package/dist/src/intro.js +81 -1
- package/dist/src/link.js +3 -1
- package/dist/src/profile.js +8 -1
- package/dist/src/signal.js +8 -1
- package/dist/src/trajectory.js +396 -297
- package/install.js +48 -20
- package/package.json +4 -3
- package/statusline-install.js +46 -17
package/dist/bin/jpi-bounties.js
CHANGED
|
@@ -13,6 +13,9 @@ var __export = (target, all) => {
|
|
|
13
13
|
function isBounty(job) {
|
|
14
14
|
return job.source === "bounty" && job.bounty != null;
|
|
15
15
|
}
|
|
16
|
+
function isContribution(job) {
|
|
17
|
+
return job.source === "contribute" && job.contribution != null;
|
|
18
|
+
}
|
|
16
19
|
var init_types = __esm({
|
|
17
20
|
"../../packages/core/src/types.ts"() {
|
|
18
21
|
"use strict";
|
|
@@ -549,6 +552,31 @@ function normalize(tokens) {
|
|
|
549
552
|
}
|
|
550
553
|
return Array.from(result);
|
|
551
554
|
}
|
|
555
|
+
function getAdjacentTags(tag, hops = 1, graph = GRAPH) {
|
|
556
|
+
const lower = String(tag ?? "").toLowerCase().trim();
|
|
557
|
+
const id = graph.ids.has(lower) ? lower : graph.synonyms.get(lower);
|
|
558
|
+
if (!id) return [];
|
|
559
|
+
const oneHop = (source) => {
|
|
560
|
+
const out = [];
|
|
561
|
+
for (const [to, edge] of graph.closure.get(source) ?? []) {
|
|
562
|
+
if (edge.via === to) out.push([to, edge.w]);
|
|
563
|
+
}
|
|
564
|
+
return out;
|
|
565
|
+
};
|
|
566
|
+
const byWeightThenId = (a, b) => b[1] - a[1] || (a[0] < b[0] ? -1 : 1);
|
|
567
|
+
const ring1 = oneHop(id).sort(byWeightThenId);
|
|
568
|
+
if (hops === 1) return ring1.map(([to]) => to);
|
|
569
|
+
const exclude = /* @__PURE__ */ new Set([id, ...ring1.map(([to]) => to)]);
|
|
570
|
+
const best = /* @__PURE__ */ new Map();
|
|
571
|
+
for (const [n, w1] of ring1) {
|
|
572
|
+
for (const [to, w2] of oneHop(n)) {
|
|
573
|
+
if (exclude.has(to)) continue;
|
|
574
|
+
const w = w1 * w2;
|
|
575
|
+
if (w > (best.get(to) ?? 0)) best.set(to, w);
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
return [...best.entries()].sort(byWeightThenId).map(([to]) => to);
|
|
579
|
+
}
|
|
552
580
|
function expandWeighted(tags, graph = GRAPH) {
|
|
553
581
|
const out = /* @__PURE__ */ new Map();
|
|
554
582
|
const put = (tag, weight, via) => {
|
|
@@ -587,6 +615,21 @@ var init_vocabulary = __esm({
|
|
|
587
615
|
}
|
|
588
616
|
});
|
|
589
617
|
|
|
618
|
+
// ../../packages/core/src/feeds/contribution-gate.ts
|
|
619
|
+
function passesContributionGate(input) {
|
|
620
|
+
if (input.contributors === void 0) return false;
|
|
621
|
+
return input.stars >= MIN_STARS && input.contributors >= MIN_CONTRIBUTORS && !TRIVIAL_PR_TITLE.test(input.title) && !input.archived && !input.fork;
|
|
622
|
+
}
|
|
623
|
+
var MIN_STARS, MIN_CONTRIBUTORS, TRIVIAL_PR_TITLE;
|
|
624
|
+
var init_contribution_gate = __esm({
|
|
625
|
+
"../../packages/core/src/feeds/contribution-gate.ts"() {
|
|
626
|
+
"use strict";
|
|
627
|
+
MIN_STARS = 50;
|
|
628
|
+
MIN_CONTRIBUTORS = 10;
|
|
629
|
+
TRIVIAL_PR_TITLE = /^\s*(fix\s+typo|typo\b|update\s+readme|readme\b|docs?:|docs?\(|chore:|chore\(|style:|ci:|build:|bump\b|update\s+dependenc)/i;
|
|
630
|
+
}
|
|
631
|
+
});
|
|
632
|
+
|
|
590
633
|
// ../../packages/core/src/github.ts
|
|
591
634
|
function ghHeaders(token) {
|
|
592
635
|
const headers = {
|
|
@@ -934,16 +977,14 @@ function deriveResumeTrend(cred, repoRecency, now = Date.now()) {
|
|
|
934
977
|
}
|
|
935
978
|
return scored.sort((a, b) => b.weight - a.weight).slice(0, 12).map((s) => s.t);
|
|
936
979
|
}
|
|
937
|
-
var TRACTION_TOP_N,
|
|
980
|
+
var TRACTION_TOP_N, CANDIDATE_PR_PAGE, RESUME_DECAY_HALF_LIFE_MS, RESUME_MIN_SCORE;
|
|
938
981
|
var init_github = __esm({
|
|
939
982
|
"../../packages/core/src/github.ts"() {
|
|
940
983
|
"use strict";
|
|
941
984
|
init_vocabulary();
|
|
985
|
+
init_contribution_gate();
|
|
942
986
|
TRACTION_TOP_N = 6;
|
|
943
|
-
MIN_STARS = 50;
|
|
944
|
-
MIN_CONTRIBUTORS = 10;
|
|
945
987
|
CANDIDATE_PR_PAGE = 50;
|
|
946
|
-
TRIVIAL_PR_TITLE = /^\s*(fix\s+typo|typo\b|update\s+readme|readme\b|docs?:|docs?\(|chore:|chore\(|style:|ci:|build:|bump\b|update\s+dependenc)/i;
|
|
947
988
|
RESUME_DECAY_HALF_LIFE_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
948
989
|
RESUME_MIN_SCORE = 0.05;
|
|
949
990
|
}
|
|
@@ -1670,13 +1711,18 @@ var init_bounty_gate = __esm({
|
|
|
1670
1711
|
"aragon/hack",
|
|
1671
1712
|
"spacemeshos/app",
|
|
1672
1713
|
"archestra-ai/archestra",
|
|
1673
|
-
"boundlessfi/boundless",
|
|
1674
1714
|
"ucfopen/Obojobo",
|
|
1675
|
-
"widgetti/ipyvolume",
|
|
1676
1715
|
"moorcheh-ai/memanto",
|
|
1677
1716
|
"PrismarineJS/mineflayer"
|
|
1678
1717
|
];
|
|
1679
|
-
BOUNTY_REPO_DENYLIST = [
|
|
1718
|
+
BOUNTY_REPO_DENYLIST = [
|
|
1719
|
+
"SecureBananaLabs/bug-bounty",
|
|
1720
|
+
// Meta-farm: a bounty PLATFORM whose own issues are an assignment-gated
|
|
1721
|
+
// contributor queue ("please assign me, my chief") — an unsolicited PR won't
|
|
1722
|
+
// merge, so it's not a real claimable bounty. Not structurally derivable from
|
|
1723
|
+
// any fetched field, so it's a manual entry (also dropped from the allowlist).
|
|
1724
|
+
"boundlessfi/boundless"
|
|
1725
|
+
];
|
|
1680
1726
|
DENYLIST_LC = new Set(BOUNTY_REPO_DENYLIST.map((r) => r.toLowerCase()));
|
|
1681
1727
|
MAX_BOUNTIES_PER_REPO = 10;
|
|
1682
1728
|
MAX_BOUNTIES_PER_DISCOVERED_REPO = 3;
|
|
@@ -1746,6 +1792,9 @@ function isBountyIssue(issue) {
|
|
|
1746
1792
|
if (labels.some((n) => BOUNTY_LABEL_RE.test(n))) return true;
|
|
1747
1793
|
return /bounty/i.test(issue.title) && parseAmountUSD(issue.title) != null;
|
|
1748
1794
|
}
|
|
1795
|
+
function isAssigned(issue) {
|
|
1796
|
+
return !!issue.assignee || (issue.assignees?.length ?? 0) > 0;
|
|
1797
|
+
}
|
|
1749
1798
|
async function ghJson(path) {
|
|
1750
1799
|
let res;
|
|
1751
1800
|
try {
|
|
@@ -1798,7 +1847,7 @@ async function fetchRepoBounties(repoFullName) {
|
|
|
1798
1847
|
}
|
|
1799
1848
|
const issues = await ghJson(`/repos/${repoFullName}/issues?state=open&per_page=100`);
|
|
1800
1849
|
if (!issues) return [];
|
|
1801
|
-
const bounties = issues.filter(isBountyIssue).slice(0, MAX_BOUNTIES_PER_REPO);
|
|
1850
|
+
const bounties = issues.filter((i) => isBountyIssue(i) && !isAssigned(i)).slice(0, MAX_BOUNTIES_PER_REPO);
|
|
1802
1851
|
const owner = repo.owner.login;
|
|
1803
1852
|
return mapWithConcurrency(bounties, BOUNTY_FETCH_CONCURRENCY, async (issue) => {
|
|
1804
1853
|
const title = decodeEntities(issue.title).trim();
|
|
@@ -1921,6 +1970,7 @@ async function fetchSearchBounties() {
|
|
|
1921
1970
|
if (jobs.length >= MAX_SEARCH_BOUNTIES) break;
|
|
1922
1971
|
const fullName = repoFullNameFromApiUrl(issue.repository_url);
|
|
1923
1972
|
if (!fullName) continue;
|
|
1973
|
+
if (isAssigned(issue)) continue;
|
|
1924
1974
|
if ((perRepo.get(fullName) ?? 0) >= MAX_BOUNTIES_PER_REPO) continue;
|
|
1925
1975
|
const repo = await repoMetaCached(fullName);
|
|
1926
1976
|
if (!repo) continue;
|
|
@@ -2432,6 +2482,7 @@ var init_feeds = __esm({
|
|
|
2432
2482
|
init_directory();
|
|
2433
2483
|
init_bounty_gate();
|
|
2434
2484
|
init_bounty_gate();
|
|
2485
|
+
init_contribution_gate();
|
|
2435
2486
|
FEEDS = [greenhouse, ashby, lever, workable, himalayas, wwr, hn];
|
|
2436
2487
|
GREENHOUSE_SLUGS_BY_TIER = {
|
|
2437
2488
|
bigco: [
|
|
@@ -2542,6 +2593,202 @@ var init_feeds = __esm({
|
|
|
2542
2593
|
}
|
|
2543
2594
|
});
|
|
2544
2595
|
|
|
2596
|
+
// ../../packages/core/src/feeds/contributions.ts
|
|
2597
|
+
function authHeaders2() {
|
|
2598
|
+
const token = process.env["GITHUB_TOKEN"] ?? process.env["GH_TOKEN"];
|
|
2599
|
+
const h = {
|
|
2600
|
+
Accept: "application/vnd.github+json",
|
|
2601
|
+
"User-Agent": "terminalhire",
|
|
2602
|
+
"X-GitHub-Api-Version": "2022-11-28"
|
|
2603
|
+
};
|
|
2604
|
+
if (token) h["Authorization"] = `Bearer ${token}`;
|
|
2605
|
+
return h;
|
|
2606
|
+
}
|
|
2607
|
+
function tokenize4(text) {
|
|
2608
|
+
return text.toLowerCase().replace(/[^a-z0-9.\-+#]/g, " ").split(/\s+/).filter(Boolean);
|
|
2609
|
+
}
|
|
2610
|
+
function labelNames2(labels) {
|
|
2611
|
+
return (labels ?? []).map((l) => typeof l === "string" ? l : l.name ?? "").filter(Boolean);
|
|
2612
|
+
}
|
|
2613
|
+
function repoFullNameFromApiUrl2(url) {
|
|
2614
|
+
const m = url.match(/\/repos\/([^/]+)\/([^/]+)\/?$/);
|
|
2615
|
+
return m ? `${m[1]}/${m[2]}` : null;
|
|
2616
|
+
}
|
|
2617
|
+
function makeClient(fetchImpl) {
|
|
2618
|
+
async function raw(path) {
|
|
2619
|
+
try {
|
|
2620
|
+
return await fetchImpl(`${GITHUB_API2}${path}`, { headers: authHeaders2() });
|
|
2621
|
+
} catch {
|
|
2622
|
+
return null;
|
|
2623
|
+
}
|
|
2624
|
+
}
|
|
2625
|
+
async function json(path) {
|
|
2626
|
+
const res = await raw(path);
|
|
2627
|
+
if (!res) return null;
|
|
2628
|
+
if (res.status === 403 && res.headers.get("x-ratelimit-remaining") === "0") return null;
|
|
2629
|
+
if (!res.ok) return null;
|
|
2630
|
+
try {
|
|
2631
|
+
return await res.json();
|
|
2632
|
+
} catch {
|
|
2633
|
+
return null;
|
|
2634
|
+
}
|
|
2635
|
+
}
|
|
2636
|
+
return { raw, json };
|
|
2637
|
+
}
|
|
2638
|
+
async function contributorCount(client, fullName) {
|
|
2639
|
+
const res = await client.raw(`/repos/${fullName}/contributors?per_page=1&anon=false`);
|
|
2640
|
+
if (!res || !res.ok) return void 0;
|
|
2641
|
+
const link = res.headers.get("link");
|
|
2642
|
+
const m = link?.match(/[?&]page=(\d+)>;\s*rel="last"/);
|
|
2643
|
+
if (m) return Number(m[1]);
|
|
2644
|
+
try {
|
|
2645
|
+
const body = await res.json();
|
|
2646
|
+
return Array.isArray(body) ? body.length : 0;
|
|
2647
|
+
} catch {
|
|
2648
|
+
return void 0;
|
|
2649
|
+
}
|
|
2650
|
+
}
|
|
2651
|
+
async function openPRIssueRefs(client, fullName) {
|
|
2652
|
+
const refs = /* @__PURE__ */ new Set();
|
|
2653
|
+
const prs = await client.json(
|
|
2654
|
+
`/repos/${fullName}/pulls?state=open&per_page=100`
|
|
2655
|
+
);
|
|
2656
|
+
if (!Array.isArray(prs)) return refs;
|
|
2657
|
+
for (const pr of prs) {
|
|
2658
|
+
for (const m of `${pr.title ?? ""}
|
|
2659
|
+
${pr.body ?? ""}`.matchAll(/#(\d+)\b/g)) {
|
|
2660
|
+
refs.add(Number(m[1]));
|
|
2661
|
+
}
|
|
2662
|
+
}
|
|
2663
|
+
return refs;
|
|
2664
|
+
}
|
|
2665
|
+
async function searchContribIssues(client, queries) {
|
|
2666
|
+
const byUrl = /* @__PURE__ */ new Map();
|
|
2667
|
+
for (const q of queries) {
|
|
2668
|
+
const res = await client.json(
|
|
2669
|
+
`/search/issues?q=${encodeURIComponent(q)}&sort=created&order=desc&per_page=${SEARCH_PER_PAGE2}`
|
|
2670
|
+
);
|
|
2671
|
+
for (const it of res?.items ?? []) {
|
|
2672
|
+
if (it.pull_request) continue;
|
|
2673
|
+
if (!byUrl.has(it.html_url)) byUrl.set(it.html_url, it);
|
|
2674
|
+
}
|
|
2675
|
+
}
|
|
2676
|
+
return [...byUrl.values()].sort(
|
|
2677
|
+
(a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime()
|
|
2678
|
+
);
|
|
2679
|
+
}
|
|
2680
|
+
async function aggregateContributions(opts = {}) {
|
|
2681
|
+
const client = makeClient(opts.fetchImpl ?? fetchWithTimeout);
|
|
2682
|
+
const queries = opts.queries ?? CONTRIB_SEARCH_QUERIES;
|
|
2683
|
+
const issues = (await searchContribIssues(client, queries)).slice(0, MAX_CONTRIB_ISSUES_SCANNED);
|
|
2684
|
+
const repoCache = /* @__PURE__ */ new Map();
|
|
2685
|
+
const contribCache = /* @__PURE__ */ new Map();
|
|
2686
|
+
const prRefsCache = /* @__PURE__ */ new Map();
|
|
2687
|
+
async function repoMeta(fullName) {
|
|
2688
|
+
const hit = repoCache.get(fullName);
|
|
2689
|
+
if (hit !== void 0) return hit;
|
|
2690
|
+
const r = await client.json(`/repos/${fullName}`) ?? null;
|
|
2691
|
+
repoCache.set(fullName, r);
|
|
2692
|
+
return r;
|
|
2693
|
+
}
|
|
2694
|
+
async function repoContribCount(fullName) {
|
|
2695
|
+
if (contribCache.has(fullName)) return contribCache.get(fullName);
|
|
2696
|
+
const n = await contributorCount(client, fullName);
|
|
2697
|
+
contribCache.set(fullName, n);
|
|
2698
|
+
return n;
|
|
2699
|
+
}
|
|
2700
|
+
async function repoPRRefs(fullName) {
|
|
2701
|
+
const hit = prRefsCache.get(fullName);
|
|
2702
|
+
if (hit !== void 0) return hit;
|
|
2703
|
+
const refs = await openPRIssueRefs(client, fullName);
|
|
2704
|
+
prRefsCache.set(fullName, refs);
|
|
2705
|
+
return refs;
|
|
2706
|
+
}
|
|
2707
|
+
const jobs = [];
|
|
2708
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2709
|
+
const perRepo = /* @__PURE__ */ new Map();
|
|
2710
|
+
for (const issue of issues) {
|
|
2711
|
+
if (jobs.length >= MAX_CONTRIB_ITEMS) break;
|
|
2712
|
+
const fullName = repoFullNameFromApiUrl2(issue.repository_url);
|
|
2713
|
+
if (!fullName) continue;
|
|
2714
|
+
const id = `contribute:${fullName}#${issue.number}`;
|
|
2715
|
+
if (seen.has(id)) continue;
|
|
2716
|
+
if (isDenylistedRepo(fullName)) continue;
|
|
2717
|
+
if (isAssigned(issue)) continue;
|
|
2718
|
+
if ((perRepo.get(fullName) ?? 0) >= MAX_BOUNTIES_PER_DISCOVERED_REPO) continue;
|
|
2719
|
+
const repo = await repoMeta(fullName);
|
|
2720
|
+
if (!repo) continue;
|
|
2721
|
+
const title = decodeEntities(issue.title).trim();
|
|
2722
|
+
const contributors = await repoContribCount(fullName);
|
|
2723
|
+
if (!passesContributionGate({
|
|
2724
|
+
stars: repo.stargazers_count,
|
|
2725
|
+
contributors,
|
|
2726
|
+
title,
|
|
2727
|
+
archived: repo.archived,
|
|
2728
|
+
fork: repo.fork
|
|
2729
|
+
})) {
|
|
2730
|
+
continue;
|
|
2731
|
+
}
|
|
2732
|
+
if (repo.disabled) continue;
|
|
2733
|
+
const prRefs = await repoPRRefs(fullName);
|
|
2734
|
+
if (prRefs.has(issue.number)) continue;
|
|
2735
|
+
const body = issue.body ? decodeEntities(issue.body) : "";
|
|
2736
|
+
const labels = labelNames2(issue.labels);
|
|
2737
|
+
const tags = normalize(
|
|
2738
|
+
tokenize4([title, repo.language ?? "", labels.join(" "), body.slice(0, 2e3)].join(" "))
|
|
2739
|
+
);
|
|
2740
|
+
seen.add(id);
|
|
2741
|
+
perRepo.set(fullName, (perRepo.get(fullName) ?? 0) + 1);
|
|
2742
|
+
jobs.push({
|
|
2743
|
+
id,
|
|
2744
|
+
source: "contribute",
|
|
2745
|
+
title,
|
|
2746
|
+
company: repo.owner.login,
|
|
2747
|
+
url: issue.html_url,
|
|
2748
|
+
remote: true,
|
|
2749
|
+
location: "Remote",
|
|
2750
|
+
tags,
|
|
2751
|
+
roleType: "freelance",
|
|
2752
|
+
postedAt: issue.created_at,
|
|
2753
|
+
applyMode: "direct",
|
|
2754
|
+
contribution: {
|
|
2755
|
+
repoFullName: fullName,
|
|
2756
|
+
repoStars: repo.stargazers_count,
|
|
2757
|
+
repoContributors: contributors,
|
|
2758
|
+
// gate guarantees a number here
|
|
2759
|
+
issueNumber: issue.number,
|
|
2760
|
+
labels,
|
|
2761
|
+
issueUrl: issue.html_url,
|
|
2762
|
+
issueBody: body.slice(0, 1e3) || void 0
|
|
2763
|
+
},
|
|
2764
|
+
raw: issue
|
|
2765
|
+
});
|
|
2766
|
+
}
|
|
2767
|
+
return jobs;
|
|
2768
|
+
}
|
|
2769
|
+
var GITHUB_API2, CONTRIB_SEARCH_QUERIES, SEARCH_PER_PAGE2, MAX_CONTRIB_ITEMS, MAX_CONTRIB_ISSUES_SCANNED;
|
|
2770
|
+
var init_contributions = __esm({
|
|
2771
|
+
"../../packages/core/src/feeds/contributions.ts"() {
|
|
2772
|
+
"use strict";
|
|
2773
|
+
init_vocabulary();
|
|
2774
|
+
init_entities();
|
|
2775
|
+
init_bounty_gate();
|
|
2776
|
+
init_contribution_gate();
|
|
2777
|
+
init_github_bounties();
|
|
2778
|
+
init_http();
|
|
2779
|
+
GITHUB_API2 = "https://api.github.com";
|
|
2780
|
+
CONTRIB_SEARCH_QUERIES = [
|
|
2781
|
+
'label:"good first issue" type:issue state:open',
|
|
2782
|
+
'label:"help wanted" type:issue state:open',
|
|
2783
|
+
'label:"good-first-issue" type:issue state:open',
|
|
2784
|
+
'label:"help-wanted" type:issue state:open'
|
|
2785
|
+
];
|
|
2786
|
+
SEARCH_PER_PAGE2 = 100;
|
|
2787
|
+
MAX_CONTRIB_ITEMS = 150;
|
|
2788
|
+
MAX_CONTRIB_ISSUES_SCANNED = 300;
|
|
2789
|
+
}
|
|
2790
|
+
});
|
|
2791
|
+
|
|
2545
2792
|
// ../../packages/core/src/partners.ts
|
|
2546
2793
|
import { readFileSync } from "fs";
|
|
2547
2794
|
import { join } from "path";
|
|
@@ -2617,15 +2864,21 @@ async function buildIndex(opts) {
|
|
|
2617
2864
|
}
|
|
2618
2865
|
}
|
|
2619
2866
|
const jobs = allJobs.map(({ raw: _raw, ...rest }) => rest);
|
|
2620
|
-
|
|
2867
|
+
const index = {
|
|
2621
2868
|
builtAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2622
2869
|
jobs
|
|
2623
2870
|
};
|
|
2871
|
+
if (opts?.includeContribute) {
|
|
2872
|
+
const contributions = await aggregateContributions(opts.contributeOpts);
|
|
2873
|
+
index.contribute = contributions.map(({ raw: _raw, ...rest }) => rest);
|
|
2874
|
+
}
|
|
2875
|
+
return index;
|
|
2624
2876
|
}
|
|
2625
2877
|
var init_indexer = __esm({
|
|
2626
2878
|
"../../packages/core/src/indexer.ts"() {
|
|
2627
2879
|
"use strict";
|
|
2628
2880
|
init_feeds();
|
|
2881
|
+
init_contributions();
|
|
2629
2882
|
init_partners();
|
|
2630
2883
|
}
|
|
2631
2884
|
});
|
|
@@ -5887,6 +6140,179 @@ var init_job_status = __esm({
|
|
|
5887
6140
|
}
|
|
5888
6141
|
});
|
|
5889
6142
|
|
|
6143
|
+
// ../../packages/core/src/episodes/schema.ts
|
|
6144
|
+
var init_schema = __esm({
|
|
6145
|
+
"../../packages/core/src/episodes/schema.ts"() {
|
|
6146
|
+
"use strict";
|
|
6147
|
+
}
|
|
6148
|
+
});
|
|
6149
|
+
|
|
6150
|
+
// ../../packages/core/src/episodes/doors.ts
|
|
6151
|
+
var init_doors = __esm({
|
|
6152
|
+
"../../packages/core/src/episodes/doors.ts"() {
|
|
6153
|
+
"use strict";
|
|
6154
|
+
init_schema();
|
|
6155
|
+
}
|
|
6156
|
+
});
|
|
6157
|
+
|
|
6158
|
+
// ../../packages/core/src/episodes/node-model.ts
|
|
6159
|
+
var init_node_model = __esm({
|
|
6160
|
+
"../../packages/core/src/episodes/node-model.ts"() {
|
|
6161
|
+
"use strict";
|
|
6162
|
+
}
|
|
6163
|
+
});
|
|
6164
|
+
|
|
6165
|
+
// ../../packages/core/src/episodes/derivers/signals.ts
|
|
6166
|
+
var init_signals = __esm({
|
|
6167
|
+
"../../packages/core/src/episodes/derivers/signals.ts"() {
|
|
6168
|
+
"use strict";
|
|
6169
|
+
init_node_model();
|
|
6170
|
+
}
|
|
6171
|
+
});
|
|
6172
|
+
|
|
6173
|
+
// ../../packages/core/src/episodes/derivers/recency-split.ts
|
|
6174
|
+
var DORMANT_THRESHOLD_DAYS;
|
|
6175
|
+
var init_recency_split = __esm({
|
|
6176
|
+
"../../packages/core/src/episodes/derivers/recency-split.ts"() {
|
|
6177
|
+
"use strict";
|
|
6178
|
+
init_doors();
|
|
6179
|
+
init_signals();
|
|
6180
|
+
DORMANT_THRESHOLD_DAYS = 90;
|
|
6181
|
+
}
|
|
6182
|
+
});
|
|
6183
|
+
|
|
6184
|
+
// ../../packages/core/src/credential/legible.ts
|
|
6185
|
+
function labelFor(id) {
|
|
6186
|
+
return DISPLAY_LABEL[id] ?? id.split("-").map((w) => w.length > 0 ? w.charAt(0).toUpperCase() + w.slice(1) : w).join(" ");
|
|
6187
|
+
}
|
|
6188
|
+
function capitalize(s) {
|
|
6189
|
+
return s.length > 0 ? s.charAt(0).toUpperCase() + s.slice(1) : s;
|
|
6190
|
+
}
|
|
6191
|
+
function deriveLegibleProfile(credential, recency, traction, seniorityBand) {
|
|
6192
|
+
const ok = credential.status === "ok";
|
|
6193
|
+
const domains = ok ? credential.byDomain : {};
|
|
6194
|
+
const chips = Object.entries(domains).map(([rawDomain, d]) => {
|
|
6195
|
+
const canon = normalize([rawDomain])[0];
|
|
6196
|
+
return canon ? { domain: canon, mergedPRs: d.mergedPRs } : null;
|
|
6197
|
+
}).filter((c) => c !== null).sort((a, b) => b.mergedPRs - a.mergedPRs || (a.domain < b.domain ? -1 : 1));
|
|
6198
|
+
const dominant = chips.length > 0 ? chips[0].domain : void 0;
|
|
6199
|
+
const role = dominant ? ROLE_BY_DOMAIN[dominant] ?? GENERIC_ROLE : GENERIC_ROLE;
|
|
6200
|
+
const stackChips = chips.filter((c) => !(c.domain in CONCEPT_TAGS));
|
|
6201
|
+
const conceptChips = chips.filter((c) => c.domain in CONCEPT_TAGS && c.domain !== dominant);
|
|
6202
|
+
const topStacks = stackChips.slice(0, 3).map((c) => labelFor(c.domain)).join(", ");
|
|
6203
|
+
const specialization = dominant && dominant in CONCEPT_TAGS ? CONCEPT_TAGS[dominant] : conceptChips.length > 0 ? CONCEPT_TAGS[conceptChips[0].domain] : "";
|
|
6204
|
+
const sen = seniorityBand ? capitalize(seniorityBand) : "";
|
|
6205
|
+
let headline;
|
|
6206
|
+
if (chips.length > 0) {
|
|
6207
|
+
const roleSeg = sen ? `${sen} ${role}` : role;
|
|
6208
|
+
const segs = [roleSeg];
|
|
6209
|
+
if (specialization) segs.push(specialization);
|
|
6210
|
+
if (topStacks) segs.push(topStacks);
|
|
6211
|
+
headline = segs.join(" \xB7 ");
|
|
6212
|
+
} else {
|
|
6213
|
+
const roleSeg = sen ? `${sen} ${GENERIC_ROLE}` : GENERIC_ROLE;
|
|
6214
|
+
headline = traction.status === "ok" && traction.totalStars > 0 ? `${roleSeg} \xB7 ${traction.totalStars}\u2605 across owned repos` : roleSeg;
|
|
6215
|
+
}
|
|
6216
|
+
const mergedDates = Object.values(domains).map((d) => d.lastMergedAt).filter((s) => typeof s === "string" && s.length > 0).sort();
|
|
6217
|
+
const mostRecent = mergedDates.length > 0 ? mergedDates[mergedDates.length - 1] : void 0;
|
|
6218
|
+
const thresholdDays = Number.isFinite(recency.value.thresholdDays) && recency.value.thresholdDays > 0 ? recency.value.thresholdDays : DORMANT_THRESHOLD_DAYS;
|
|
6219
|
+
const nowIso = recency.value.now && recency.value.now.length > 0 ? recency.value.now : credential.computedAt || (/* @__PURE__ */ new Date()).toISOString();
|
|
6220
|
+
const nowMs = Date.parse(nowIso);
|
|
6221
|
+
let recencyBadge = null;
|
|
6222
|
+
let daysAgo;
|
|
6223
|
+
if (mostRecent) {
|
|
6224
|
+
const ageDays2 = (nowMs - Date.parse(mostRecent)) / DAY_MS;
|
|
6225
|
+
daysAgo = Math.max(0, Math.round(ageDays2));
|
|
6226
|
+
recencyBadge = { lastMergedAt: mostRecent, state: ageDays2 <= thresholdDays ? "live" : "dormant" };
|
|
6227
|
+
}
|
|
6228
|
+
const orgCount = Object.values(domains).reduce((m, d) => Math.max(m, d.distinctOrgs), 0);
|
|
6229
|
+
let proofSentence;
|
|
6230
|
+
if (!ok) {
|
|
6231
|
+
proofSentence = "Contribution credential unavailable \u2014 could not verify.";
|
|
6232
|
+
} else {
|
|
6233
|
+
const prs = credential.qualifyingTotal;
|
|
6234
|
+
let s = `${prs} substantive PR${prs === 1 ? "" : "s"} merged into at least ${orgCount} external org${orgCount === 1 ? "" : "s"} (\u2265${MIN_STARS}\u2605, \u2265${MIN_CONTRIBUTORS} contributors)`;
|
|
6235
|
+
if (daysAgo !== void 0) s += ` \u2014 most recent ${daysAgo}d ago`;
|
|
6236
|
+
proofSentence = `${s}.`;
|
|
6237
|
+
}
|
|
6238
|
+
const auditableBadge = ok ? {
|
|
6239
|
+
mergedTotal: credential.qualifyingTotal,
|
|
6240
|
+
distinctOrgs: orgCount,
|
|
6241
|
+
thresholds: { stars: MIN_STARS, contributors: MIN_CONTRIBUTORS }
|
|
6242
|
+
} : null;
|
|
6243
|
+
const profile = {
|
|
6244
|
+
headline,
|
|
6245
|
+
verifiedSkillChips: chips,
|
|
6246
|
+
recencyBadge,
|
|
6247
|
+
proofSentence,
|
|
6248
|
+
auditableBadge
|
|
6249
|
+
};
|
|
6250
|
+
if (seniorityBand) profile.seniority = seniorityBand;
|
|
6251
|
+
return profile;
|
|
6252
|
+
}
|
|
6253
|
+
var DAY_MS, GENERIC_ROLE, ROLE_BY_DOMAIN, CONCEPT_TAGS, DISPLAY_LABEL;
|
|
6254
|
+
var init_legible = __esm({
|
|
6255
|
+
"../../packages/core/src/credential/legible.ts"() {
|
|
6256
|
+
"use strict";
|
|
6257
|
+
init_contribution_gate();
|
|
6258
|
+
init_vocabulary();
|
|
6259
|
+
init_recency_split();
|
|
6260
|
+
DAY_MS = 864e5;
|
|
6261
|
+
GENERIC_ROLE = "Software Engineer";
|
|
6262
|
+
ROLE_BY_DOMAIN = {
|
|
6263
|
+
backend: "Backend Engineer",
|
|
6264
|
+
go: "Backend Engineer",
|
|
6265
|
+
rust: "Backend Engineer",
|
|
6266
|
+
java: "Backend Engineer",
|
|
6267
|
+
python: "Backend Engineer",
|
|
6268
|
+
nodejs: "Backend Engineer",
|
|
6269
|
+
"api-design": "Backend Engineer",
|
|
6270
|
+
microservices: "Backend Engineer",
|
|
6271
|
+
postgresql: "Backend Engineer",
|
|
6272
|
+
frontend: "Frontend Engineer",
|
|
6273
|
+
react: "Frontend Engineer",
|
|
6274
|
+
vue: "Frontend Engineer",
|
|
6275
|
+
css: "Frontend Engineer",
|
|
6276
|
+
ml: "ML Engineer",
|
|
6277
|
+
pytorch: "ML Engineer",
|
|
6278
|
+
llm: "ML Engineer",
|
|
6279
|
+
"computer-vision": "ML Engineer",
|
|
6280
|
+
devops: "Platform Engineer",
|
|
6281
|
+
kubernetes: "Platform Engineer",
|
|
6282
|
+
terraform: "Platform Engineer",
|
|
6283
|
+
docker: "Platform Engineer",
|
|
6284
|
+
aws: "Platform Engineer",
|
|
6285
|
+
ios: "Mobile Engineer",
|
|
6286
|
+
android: "Mobile Engineer"
|
|
6287
|
+
};
|
|
6288
|
+
CONCEPT_TAGS = {
|
|
6289
|
+
"distributed-systems": "Distributed systems",
|
|
6290
|
+
microservices: "Microservices",
|
|
6291
|
+
security: "Security",
|
|
6292
|
+
payments: "Payments",
|
|
6293
|
+
ml: "Machine learning",
|
|
6294
|
+
llm: "LLM systems",
|
|
6295
|
+
"computer-vision": "Computer vision",
|
|
6296
|
+
recsys: "Recommendation systems",
|
|
6297
|
+
"api-design": "API design"
|
|
6298
|
+
};
|
|
6299
|
+
DISPLAY_LABEL = {
|
|
6300
|
+
postgresql: "Postgres",
|
|
6301
|
+
nodejs: "Node.js",
|
|
6302
|
+
ml: "ML",
|
|
6303
|
+
llm: "LLM",
|
|
6304
|
+
"api-design": "API design",
|
|
6305
|
+
"ci-cd": "CI/CD",
|
|
6306
|
+
"computer-vision": "Computer vision",
|
|
6307
|
+
"distributed-systems": "Distributed systems",
|
|
6308
|
+
ios: "iOS",
|
|
6309
|
+
css: "CSS",
|
|
6310
|
+
graphql: "GraphQL",
|
|
6311
|
+
aws: "AWS"
|
|
6312
|
+
};
|
|
6313
|
+
}
|
|
6314
|
+
});
|
|
6315
|
+
|
|
5890
6316
|
// ../../packages/core/src/index.ts
|
|
5891
6317
|
var src_exports = {};
|
|
5892
6318
|
__export(src_exports, {
|
|
@@ -5906,8 +6332,11 @@ __export(src_exports, {
|
|
|
5906
6332
|
INTRO_ALLOWED_FIELDS: () => INTRO_ALLOWED_FIELDS,
|
|
5907
6333
|
INTRO_PENDING_TTL_MS: () => INTRO_PENDING_TTL_MS,
|
|
5908
6334
|
LEVER_SLUGS_BY_TIER: () => LEVER_SLUGS_BY_TIER,
|
|
6335
|
+
MIN_CONTRIBUTORS: () => MIN_CONTRIBUTORS,
|
|
6336
|
+
MIN_STARS: () => MIN_STARS,
|
|
5909
6337
|
STRONG_MATCH_THRESHOLD: () => STRONG_MATCH_THRESHOLD,
|
|
5910
6338
|
SYNONYMS: () => SYNONYMS,
|
|
6339
|
+
TRIVIAL_PR_TITLE: () => TRIVIAL_PR_TITLE,
|
|
5911
6340
|
VOCABULARY: () => VOCABULARY,
|
|
5912
6341
|
VOCAB_NODES: () => VOCAB_NODES,
|
|
5913
6342
|
acceptanceCountForDomains: () => acceptanceCountForDomains,
|
|
@@ -5930,6 +6359,7 @@ __export(src_exports, {
|
|
|
5930
6359
|
coreTagsFromTitle: () => coreTagsFromTitle,
|
|
5931
6360
|
decorate: () => decorate,
|
|
5932
6361
|
decryptMessage: () => decryptMessage,
|
|
6362
|
+
deriveLegibleProfile: () => deriveLegibleProfile,
|
|
5933
6363
|
deriveResumeTrend: () => deriveResumeTrend,
|
|
5934
6364
|
deriveSharedKey: () => deriveSharedKey,
|
|
5935
6365
|
encryptMessage: () => encryptMessage,
|
|
@@ -5941,6 +6371,7 @@ __export(src_exports, {
|
|
|
5941
6371
|
flattenTiers: () => flattenTiers,
|
|
5942
6372
|
funnelCounts: () => funnelCounts,
|
|
5943
6373
|
generateIdentityKeypair: () => generateIdentityKeypair,
|
|
6374
|
+
getAdjacentTags: () => getAdjacentTags,
|
|
5944
6375
|
getBuyer: () => getBuyer,
|
|
5945
6376
|
githubBounties: () => githubBounties,
|
|
5946
6377
|
githubToFingerprint: () => githubToFingerprint,
|
|
@@ -5951,6 +6382,7 @@ __export(src_exports, {
|
|
|
5951
6382
|
introRateLimitCheck: () => introRateLimitCheck,
|
|
5952
6383
|
introRetentionAction: () => introRetentionAction,
|
|
5953
6384
|
isBounty: () => isBounty,
|
|
6385
|
+
isContribution: () => isContribution,
|
|
5954
6386
|
isOverIntroLimit: () => isOverIntroLimit,
|
|
5955
6387
|
lever: () => lever,
|
|
5956
6388
|
loadPartnerRoles: () => loadPartnerRoles,
|
|
@@ -5959,6 +6391,7 @@ __export(src_exports, {
|
|
|
5959
6391
|
normalize: () => normalize,
|
|
5960
6392
|
opire: () => opire,
|
|
5961
6393
|
pageMatches: () => pageMatches,
|
|
6394
|
+
passesContributionGate: () => passesContributionGate,
|
|
5962
6395
|
passesMaturityGate: () => passesMaturityGate,
|
|
5963
6396
|
personCardToJob: () => personCardToJob,
|
|
5964
6397
|
projectCardToJob: () => projectCardToJob,
|
|
@@ -5988,6 +6421,7 @@ var init_src = __esm({
|
|
|
5988
6421
|
init_directoryThreshold();
|
|
5989
6422
|
init_chatCrypto();
|
|
5990
6423
|
init_job_status();
|
|
6424
|
+
init_legible();
|
|
5991
6425
|
}
|
|
5992
6426
|
});
|
|
5993
6427
|
|