terminalhire 0.11.1 → 0.13.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 +417 -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 +417 -9
- package/dist/bin/jpi-dispatch.js +1442 -780
- package/dist/bin/jpi-init.js +82 -21
- package/dist/bin/jpi-intro.js +81 -1
- package/dist/bin/jpi-jobs.js +417 -9
- package/dist/bin/jpi-learn.js +81 -1
- package/dist/bin/jpi-link.js +3 -1
- package/dist/bin/jpi-login.js +435 -26
- package/dist/bin/jpi-profile.js +81 -1
- package/dist/bin/jpi-project.js +419 -11
- package/dist/bin/jpi-refresh.js +456 -16
- package/dist/bin/jpi-save.js +81 -1
- package/dist/bin/jpi-statusline-launch.js +71 -0
- package/dist/bin/jpi-statusline.js +65 -0
- 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 +11 -2
- 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 +6 -4
- package/statusline-install.js +300 -0
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";
|
|
@@ -587,6 +590,21 @@ var init_vocabulary = __esm({
|
|
|
587
590
|
}
|
|
588
591
|
});
|
|
589
592
|
|
|
593
|
+
// ../../packages/core/src/feeds/contribution-gate.ts
|
|
594
|
+
function passesContributionGate(input) {
|
|
595
|
+
if (input.contributors === void 0) return false;
|
|
596
|
+
return input.stars >= MIN_STARS && input.contributors >= MIN_CONTRIBUTORS && !TRIVIAL_PR_TITLE.test(input.title) && !input.archived && !input.fork;
|
|
597
|
+
}
|
|
598
|
+
var MIN_STARS, MIN_CONTRIBUTORS, TRIVIAL_PR_TITLE;
|
|
599
|
+
var init_contribution_gate = __esm({
|
|
600
|
+
"../../packages/core/src/feeds/contribution-gate.ts"() {
|
|
601
|
+
"use strict";
|
|
602
|
+
MIN_STARS = 50;
|
|
603
|
+
MIN_CONTRIBUTORS = 10;
|
|
604
|
+
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;
|
|
605
|
+
}
|
|
606
|
+
});
|
|
607
|
+
|
|
590
608
|
// ../../packages/core/src/github.ts
|
|
591
609
|
function ghHeaders(token) {
|
|
592
610
|
const headers = {
|
|
@@ -934,16 +952,14 @@ function deriveResumeTrend(cred, repoRecency, now = Date.now()) {
|
|
|
934
952
|
}
|
|
935
953
|
return scored.sort((a, b) => b.weight - a.weight).slice(0, 12).map((s) => s.t);
|
|
936
954
|
}
|
|
937
|
-
var TRACTION_TOP_N,
|
|
955
|
+
var TRACTION_TOP_N, CANDIDATE_PR_PAGE, RESUME_DECAY_HALF_LIFE_MS, RESUME_MIN_SCORE;
|
|
938
956
|
var init_github = __esm({
|
|
939
957
|
"../../packages/core/src/github.ts"() {
|
|
940
958
|
"use strict";
|
|
941
959
|
init_vocabulary();
|
|
960
|
+
init_contribution_gate();
|
|
942
961
|
TRACTION_TOP_N = 6;
|
|
943
|
-
MIN_STARS = 50;
|
|
944
|
-
MIN_CONTRIBUTORS = 10;
|
|
945
962
|
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
963
|
RESUME_DECAY_HALF_LIFE_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
948
964
|
RESUME_MIN_SCORE = 0.05;
|
|
949
965
|
}
|
|
@@ -1670,13 +1686,18 @@ var init_bounty_gate = __esm({
|
|
|
1670
1686
|
"aragon/hack",
|
|
1671
1687
|
"spacemeshos/app",
|
|
1672
1688
|
"archestra-ai/archestra",
|
|
1673
|
-
"boundlessfi/boundless",
|
|
1674
1689
|
"ucfopen/Obojobo",
|
|
1675
|
-
"widgetti/ipyvolume",
|
|
1676
1690
|
"moorcheh-ai/memanto",
|
|
1677
1691
|
"PrismarineJS/mineflayer"
|
|
1678
1692
|
];
|
|
1679
|
-
BOUNTY_REPO_DENYLIST = [
|
|
1693
|
+
BOUNTY_REPO_DENYLIST = [
|
|
1694
|
+
"SecureBananaLabs/bug-bounty",
|
|
1695
|
+
// Meta-farm: a bounty PLATFORM whose own issues are an assignment-gated
|
|
1696
|
+
// contributor queue ("please assign me, my chief") — an unsolicited PR won't
|
|
1697
|
+
// merge, so it's not a real claimable bounty. Not structurally derivable from
|
|
1698
|
+
// any fetched field, so it's a manual entry (also dropped from the allowlist).
|
|
1699
|
+
"boundlessfi/boundless"
|
|
1700
|
+
];
|
|
1680
1701
|
DENYLIST_LC = new Set(BOUNTY_REPO_DENYLIST.map((r) => r.toLowerCase()));
|
|
1681
1702
|
MAX_BOUNTIES_PER_REPO = 10;
|
|
1682
1703
|
MAX_BOUNTIES_PER_DISCOVERED_REPO = 3;
|
|
@@ -1746,6 +1767,9 @@ function isBountyIssue(issue) {
|
|
|
1746
1767
|
if (labels.some((n) => BOUNTY_LABEL_RE.test(n))) return true;
|
|
1747
1768
|
return /bounty/i.test(issue.title) && parseAmountUSD(issue.title) != null;
|
|
1748
1769
|
}
|
|
1770
|
+
function isAssigned(issue) {
|
|
1771
|
+
return !!issue.assignee || (issue.assignees?.length ?? 0) > 0;
|
|
1772
|
+
}
|
|
1749
1773
|
async function ghJson(path) {
|
|
1750
1774
|
let res;
|
|
1751
1775
|
try {
|
|
@@ -1798,7 +1822,7 @@ async function fetchRepoBounties(repoFullName) {
|
|
|
1798
1822
|
}
|
|
1799
1823
|
const issues = await ghJson(`/repos/${repoFullName}/issues?state=open&per_page=100`);
|
|
1800
1824
|
if (!issues) return [];
|
|
1801
|
-
const bounties = issues.filter(isBountyIssue).slice(0, MAX_BOUNTIES_PER_REPO);
|
|
1825
|
+
const bounties = issues.filter((i) => isBountyIssue(i) && !isAssigned(i)).slice(0, MAX_BOUNTIES_PER_REPO);
|
|
1802
1826
|
const owner = repo.owner.login;
|
|
1803
1827
|
return mapWithConcurrency(bounties, BOUNTY_FETCH_CONCURRENCY, async (issue) => {
|
|
1804
1828
|
const title = decodeEntities(issue.title).trim();
|
|
@@ -1921,6 +1945,7 @@ async function fetchSearchBounties() {
|
|
|
1921
1945
|
if (jobs.length >= MAX_SEARCH_BOUNTIES) break;
|
|
1922
1946
|
const fullName = repoFullNameFromApiUrl(issue.repository_url);
|
|
1923
1947
|
if (!fullName) continue;
|
|
1948
|
+
if (isAssigned(issue)) continue;
|
|
1924
1949
|
if ((perRepo.get(fullName) ?? 0) >= MAX_BOUNTIES_PER_REPO) continue;
|
|
1925
1950
|
const repo = await repoMetaCached(fullName);
|
|
1926
1951
|
if (!repo) continue;
|
|
@@ -2432,6 +2457,7 @@ var init_feeds = __esm({
|
|
|
2432
2457
|
init_directory();
|
|
2433
2458
|
init_bounty_gate();
|
|
2434
2459
|
init_bounty_gate();
|
|
2460
|
+
init_contribution_gate();
|
|
2435
2461
|
FEEDS = [greenhouse, ashby, lever, workable, himalayas, wwr, hn];
|
|
2436
2462
|
GREENHOUSE_SLUGS_BY_TIER = {
|
|
2437
2463
|
bigco: [
|
|
@@ -2542,6 +2568,202 @@ var init_feeds = __esm({
|
|
|
2542
2568
|
}
|
|
2543
2569
|
});
|
|
2544
2570
|
|
|
2571
|
+
// ../../packages/core/src/feeds/contributions.ts
|
|
2572
|
+
function authHeaders2() {
|
|
2573
|
+
const token = process.env["GITHUB_TOKEN"] ?? process.env["GH_TOKEN"];
|
|
2574
|
+
const h = {
|
|
2575
|
+
Accept: "application/vnd.github+json",
|
|
2576
|
+
"User-Agent": "terminalhire",
|
|
2577
|
+
"X-GitHub-Api-Version": "2022-11-28"
|
|
2578
|
+
};
|
|
2579
|
+
if (token) h["Authorization"] = `Bearer ${token}`;
|
|
2580
|
+
return h;
|
|
2581
|
+
}
|
|
2582
|
+
function tokenize4(text) {
|
|
2583
|
+
return text.toLowerCase().replace(/[^a-z0-9.\-+#]/g, " ").split(/\s+/).filter(Boolean);
|
|
2584
|
+
}
|
|
2585
|
+
function labelNames2(labels) {
|
|
2586
|
+
return (labels ?? []).map((l) => typeof l === "string" ? l : l.name ?? "").filter(Boolean);
|
|
2587
|
+
}
|
|
2588
|
+
function repoFullNameFromApiUrl2(url) {
|
|
2589
|
+
const m = url.match(/\/repos\/([^/]+)\/([^/]+)\/?$/);
|
|
2590
|
+
return m ? `${m[1]}/${m[2]}` : null;
|
|
2591
|
+
}
|
|
2592
|
+
function makeClient(fetchImpl) {
|
|
2593
|
+
async function raw(path) {
|
|
2594
|
+
try {
|
|
2595
|
+
return await fetchImpl(`${GITHUB_API2}${path}`, { headers: authHeaders2() });
|
|
2596
|
+
} catch {
|
|
2597
|
+
return null;
|
|
2598
|
+
}
|
|
2599
|
+
}
|
|
2600
|
+
async function json(path) {
|
|
2601
|
+
const res = await raw(path);
|
|
2602
|
+
if (!res) return null;
|
|
2603
|
+
if (res.status === 403 && res.headers.get("x-ratelimit-remaining") === "0") return null;
|
|
2604
|
+
if (!res.ok) return null;
|
|
2605
|
+
try {
|
|
2606
|
+
return await res.json();
|
|
2607
|
+
} catch {
|
|
2608
|
+
return null;
|
|
2609
|
+
}
|
|
2610
|
+
}
|
|
2611
|
+
return { raw, json };
|
|
2612
|
+
}
|
|
2613
|
+
async function contributorCount(client, fullName) {
|
|
2614
|
+
const res = await client.raw(`/repos/${fullName}/contributors?per_page=1&anon=false`);
|
|
2615
|
+
if (!res || !res.ok) return void 0;
|
|
2616
|
+
const link = res.headers.get("link");
|
|
2617
|
+
const m = link?.match(/[?&]page=(\d+)>;\s*rel="last"/);
|
|
2618
|
+
if (m) return Number(m[1]);
|
|
2619
|
+
try {
|
|
2620
|
+
const body = await res.json();
|
|
2621
|
+
return Array.isArray(body) ? body.length : 0;
|
|
2622
|
+
} catch {
|
|
2623
|
+
return void 0;
|
|
2624
|
+
}
|
|
2625
|
+
}
|
|
2626
|
+
async function openPRIssueRefs(client, fullName) {
|
|
2627
|
+
const refs = /* @__PURE__ */ new Set();
|
|
2628
|
+
const prs = await client.json(
|
|
2629
|
+
`/repos/${fullName}/pulls?state=open&per_page=100`
|
|
2630
|
+
);
|
|
2631
|
+
if (!Array.isArray(prs)) return refs;
|
|
2632
|
+
for (const pr of prs) {
|
|
2633
|
+
for (const m of `${pr.title ?? ""}
|
|
2634
|
+
${pr.body ?? ""}`.matchAll(/#(\d+)\b/g)) {
|
|
2635
|
+
refs.add(Number(m[1]));
|
|
2636
|
+
}
|
|
2637
|
+
}
|
|
2638
|
+
return refs;
|
|
2639
|
+
}
|
|
2640
|
+
async function searchContribIssues(client, queries) {
|
|
2641
|
+
const byUrl = /* @__PURE__ */ new Map();
|
|
2642
|
+
for (const q of queries) {
|
|
2643
|
+
const res = await client.json(
|
|
2644
|
+
`/search/issues?q=${encodeURIComponent(q)}&sort=created&order=desc&per_page=${SEARCH_PER_PAGE2}`
|
|
2645
|
+
);
|
|
2646
|
+
for (const it of res?.items ?? []) {
|
|
2647
|
+
if (it.pull_request) continue;
|
|
2648
|
+
if (!byUrl.has(it.html_url)) byUrl.set(it.html_url, it);
|
|
2649
|
+
}
|
|
2650
|
+
}
|
|
2651
|
+
return [...byUrl.values()].sort(
|
|
2652
|
+
(a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime()
|
|
2653
|
+
);
|
|
2654
|
+
}
|
|
2655
|
+
async function aggregateContributions(opts = {}) {
|
|
2656
|
+
const client = makeClient(opts.fetchImpl ?? fetchWithTimeout);
|
|
2657
|
+
const queries = opts.queries ?? CONTRIB_SEARCH_QUERIES;
|
|
2658
|
+
const issues = (await searchContribIssues(client, queries)).slice(0, MAX_CONTRIB_ISSUES_SCANNED);
|
|
2659
|
+
const repoCache = /* @__PURE__ */ new Map();
|
|
2660
|
+
const contribCache = /* @__PURE__ */ new Map();
|
|
2661
|
+
const prRefsCache = /* @__PURE__ */ new Map();
|
|
2662
|
+
async function repoMeta(fullName) {
|
|
2663
|
+
const hit = repoCache.get(fullName);
|
|
2664
|
+
if (hit !== void 0) return hit;
|
|
2665
|
+
const r = await client.json(`/repos/${fullName}`) ?? null;
|
|
2666
|
+
repoCache.set(fullName, r);
|
|
2667
|
+
return r;
|
|
2668
|
+
}
|
|
2669
|
+
async function repoContribCount(fullName) {
|
|
2670
|
+
if (contribCache.has(fullName)) return contribCache.get(fullName);
|
|
2671
|
+
const n = await contributorCount(client, fullName);
|
|
2672
|
+
contribCache.set(fullName, n);
|
|
2673
|
+
return n;
|
|
2674
|
+
}
|
|
2675
|
+
async function repoPRRefs(fullName) {
|
|
2676
|
+
const hit = prRefsCache.get(fullName);
|
|
2677
|
+
if (hit !== void 0) return hit;
|
|
2678
|
+
const refs = await openPRIssueRefs(client, fullName);
|
|
2679
|
+
prRefsCache.set(fullName, refs);
|
|
2680
|
+
return refs;
|
|
2681
|
+
}
|
|
2682
|
+
const jobs = [];
|
|
2683
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2684
|
+
const perRepo = /* @__PURE__ */ new Map();
|
|
2685
|
+
for (const issue of issues) {
|
|
2686
|
+
if (jobs.length >= MAX_CONTRIB_ITEMS) break;
|
|
2687
|
+
const fullName = repoFullNameFromApiUrl2(issue.repository_url);
|
|
2688
|
+
if (!fullName) continue;
|
|
2689
|
+
const id = `contribute:${fullName}#${issue.number}`;
|
|
2690
|
+
if (seen.has(id)) continue;
|
|
2691
|
+
if (isDenylistedRepo(fullName)) continue;
|
|
2692
|
+
if (isAssigned(issue)) continue;
|
|
2693
|
+
if ((perRepo.get(fullName) ?? 0) >= MAX_BOUNTIES_PER_DISCOVERED_REPO) continue;
|
|
2694
|
+
const repo = await repoMeta(fullName);
|
|
2695
|
+
if (!repo) continue;
|
|
2696
|
+
const title = decodeEntities(issue.title).trim();
|
|
2697
|
+
const contributors = await repoContribCount(fullName);
|
|
2698
|
+
if (!passesContributionGate({
|
|
2699
|
+
stars: repo.stargazers_count,
|
|
2700
|
+
contributors,
|
|
2701
|
+
title,
|
|
2702
|
+
archived: repo.archived,
|
|
2703
|
+
fork: repo.fork
|
|
2704
|
+
})) {
|
|
2705
|
+
continue;
|
|
2706
|
+
}
|
|
2707
|
+
if (repo.disabled) continue;
|
|
2708
|
+
const prRefs = await repoPRRefs(fullName);
|
|
2709
|
+
if (prRefs.has(issue.number)) continue;
|
|
2710
|
+
const body = issue.body ? decodeEntities(issue.body) : "";
|
|
2711
|
+
const labels = labelNames2(issue.labels);
|
|
2712
|
+
const tags = normalize(
|
|
2713
|
+
tokenize4([title, repo.language ?? "", labels.join(" "), body.slice(0, 2e3)].join(" "))
|
|
2714
|
+
);
|
|
2715
|
+
seen.add(id);
|
|
2716
|
+
perRepo.set(fullName, (perRepo.get(fullName) ?? 0) + 1);
|
|
2717
|
+
jobs.push({
|
|
2718
|
+
id,
|
|
2719
|
+
source: "contribute",
|
|
2720
|
+
title,
|
|
2721
|
+
company: repo.owner.login,
|
|
2722
|
+
url: issue.html_url,
|
|
2723
|
+
remote: true,
|
|
2724
|
+
location: "Remote",
|
|
2725
|
+
tags,
|
|
2726
|
+
roleType: "freelance",
|
|
2727
|
+
postedAt: issue.created_at,
|
|
2728
|
+
applyMode: "direct",
|
|
2729
|
+
contribution: {
|
|
2730
|
+
repoFullName: fullName,
|
|
2731
|
+
repoStars: repo.stargazers_count,
|
|
2732
|
+
repoContributors: contributors,
|
|
2733
|
+
// gate guarantees a number here
|
|
2734
|
+
issueNumber: issue.number,
|
|
2735
|
+
labels,
|
|
2736
|
+
issueUrl: issue.html_url,
|
|
2737
|
+
issueBody: body.slice(0, 1e3) || void 0
|
|
2738
|
+
},
|
|
2739
|
+
raw: issue
|
|
2740
|
+
});
|
|
2741
|
+
}
|
|
2742
|
+
return jobs;
|
|
2743
|
+
}
|
|
2744
|
+
var GITHUB_API2, CONTRIB_SEARCH_QUERIES, SEARCH_PER_PAGE2, MAX_CONTRIB_ITEMS, MAX_CONTRIB_ISSUES_SCANNED;
|
|
2745
|
+
var init_contributions = __esm({
|
|
2746
|
+
"../../packages/core/src/feeds/contributions.ts"() {
|
|
2747
|
+
"use strict";
|
|
2748
|
+
init_vocabulary();
|
|
2749
|
+
init_entities();
|
|
2750
|
+
init_bounty_gate();
|
|
2751
|
+
init_contribution_gate();
|
|
2752
|
+
init_github_bounties();
|
|
2753
|
+
init_http();
|
|
2754
|
+
GITHUB_API2 = "https://api.github.com";
|
|
2755
|
+
CONTRIB_SEARCH_QUERIES = [
|
|
2756
|
+
'label:"good first issue" type:issue state:open',
|
|
2757
|
+
'label:"help wanted" type:issue state:open',
|
|
2758
|
+
'label:"good-first-issue" type:issue state:open',
|
|
2759
|
+
'label:"help-wanted" type:issue state:open'
|
|
2760
|
+
];
|
|
2761
|
+
SEARCH_PER_PAGE2 = 100;
|
|
2762
|
+
MAX_CONTRIB_ITEMS = 150;
|
|
2763
|
+
MAX_CONTRIB_ISSUES_SCANNED = 300;
|
|
2764
|
+
}
|
|
2765
|
+
});
|
|
2766
|
+
|
|
2545
2767
|
// ../../packages/core/src/partners.ts
|
|
2546
2768
|
import { readFileSync } from "fs";
|
|
2547
2769
|
import { join } from "path";
|
|
@@ -2617,15 +2839,21 @@ async function buildIndex(opts) {
|
|
|
2617
2839
|
}
|
|
2618
2840
|
}
|
|
2619
2841
|
const jobs = allJobs.map(({ raw: _raw, ...rest }) => rest);
|
|
2620
|
-
|
|
2842
|
+
const index = {
|
|
2621
2843
|
builtAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2622
2844
|
jobs
|
|
2623
2845
|
};
|
|
2846
|
+
if (opts?.includeContribute) {
|
|
2847
|
+
const contributions = await aggregateContributions(opts.contributeOpts);
|
|
2848
|
+
index.contribute = contributions.map(({ raw: _raw, ...rest }) => rest);
|
|
2849
|
+
}
|
|
2850
|
+
return index;
|
|
2624
2851
|
}
|
|
2625
2852
|
var init_indexer = __esm({
|
|
2626
2853
|
"../../packages/core/src/indexer.ts"() {
|
|
2627
2854
|
"use strict";
|
|
2628
2855
|
init_feeds();
|
|
2856
|
+
init_contributions();
|
|
2629
2857
|
init_partners();
|
|
2630
2858
|
}
|
|
2631
2859
|
});
|
|
@@ -5887,6 +6115,179 @@ var init_job_status = __esm({
|
|
|
5887
6115
|
}
|
|
5888
6116
|
});
|
|
5889
6117
|
|
|
6118
|
+
// ../../packages/core/src/episodes/schema.ts
|
|
6119
|
+
var init_schema = __esm({
|
|
6120
|
+
"../../packages/core/src/episodes/schema.ts"() {
|
|
6121
|
+
"use strict";
|
|
6122
|
+
}
|
|
6123
|
+
});
|
|
6124
|
+
|
|
6125
|
+
// ../../packages/core/src/episodes/doors.ts
|
|
6126
|
+
var init_doors = __esm({
|
|
6127
|
+
"../../packages/core/src/episodes/doors.ts"() {
|
|
6128
|
+
"use strict";
|
|
6129
|
+
init_schema();
|
|
6130
|
+
}
|
|
6131
|
+
});
|
|
6132
|
+
|
|
6133
|
+
// ../../packages/core/src/episodes/node-model.ts
|
|
6134
|
+
var init_node_model = __esm({
|
|
6135
|
+
"../../packages/core/src/episodes/node-model.ts"() {
|
|
6136
|
+
"use strict";
|
|
6137
|
+
}
|
|
6138
|
+
});
|
|
6139
|
+
|
|
6140
|
+
// ../../packages/core/src/episodes/derivers/signals.ts
|
|
6141
|
+
var init_signals = __esm({
|
|
6142
|
+
"../../packages/core/src/episodes/derivers/signals.ts"() {
|
|
6143
|
+
"use strict";
|
|
6144
|
+
init_node_model();
|
|
6145
|
+
}
|
|
6146
|
+
});
|
|
6147
|
+
|
|
6148
|
+
// ../../packages/core/src/episodes/derivers/recency-split.ts
|
|
6149
|
+
var DORMANT_THRESHOLD_DAYS;
|
|
6150
|
+
var init_recency_split = __esm({
|
|
6151
|
+
"../../packages/core/src/episodes/derivers/recency-split.ts"() {
|
|
6152
|
+
"use strict";
|
|
6153
|
+
init_doors();
|
|
6154
|
+
init_signals();
|
|
6155
|
+
DORMANT_THRESHOLD_DAYS = 90;
|
|
6156
|
+
}
|
|
6157
|
+
});
|
|
6158
|
+
|
|
6159
|
+
// ../../packages/core/src/credential/legible.ts
|
|
6160
|
+
function labelFor(id) {
|
|
6161
|
+
return DISPLAY_LABEL[id] ?? id.split("-").map((w) => w.length > 0 ? w.charAt(0).toUpperCase() + w.slice(1) : w).join(" ");
|
|
6162
|
+
}
|
|
6163
|
+
function capitalize(s) {
|
|
6164
|
+
return s.length > 0 ? s.charAt(0).toUpperCase() + s.slice(1) : s;
|
|
6165
|
+
}
|
|
6166
|
+
function deriveLegibleProfile(credential, recency, traction, seniorityBand) {
|
|
6167
|
+
const ok = credential.status === "ok";
|
|
6168
|
+
const domains = ok ? credential.byDomain : {};
|
|
6169
|
+
const chips = Object.entries(domains).map(([rawDomain, d]) => {
|
|
6170
|
+
const canon = normalize([rawDomain])[0];
|
|
6171
|
+
return canon ? { domain: canon, mergedPRs: d.mergedPRs } : null;
|
|
6172
|
+
}).filter((c) => c !== null).sort((a, b) => b.mergedPRs - a.mergedPRs || (a.domain < b.domain ? -1 : 1));
|
|
6173
|
+
const dominant = chips.length > 0 ? chips[0].domain : void 0;
|
|
6174
|
+
const role = dominant ? ROLE_BY_DOMAIN[dominant] ?? GENERIC_ROLE : GENERIC_ROLE;
|
|
6175
|
+
const stackChips = chips.filter((c) => !(c.domain in CONCEPT_TAGS));
|
|
6176
|
+
const conceptChips = chips.filter((c) => c.domain in CONCEPT_TAGS && c.domain !== dominant);
|
|
6177
|
+
const topStacks = stackChips.slice(0, 3).map((c) => labelFor(c.domain)).join(", ");
|
|
6178
|
+
const specialization = dominant && dominant in CONCEPT_TAGS ? CONCEPT_TAGS[dominant] : conceptChips.length > 0 ? CONCEPT_TAGS[conceptChips[0].domain] : "";
|
|
6179
|
+
const sen = seniorityBand ? capitalize(seniorityBand) : "";
|
|
6180
|
+
let headline;
|
|
6181
|
+
if (chips.length > 0) {
|
|
6182
|
+
const roleSeg = sen ? `${sen} ${role}` : role;
|
|
6183
|
+
const segs = [roleSeg];
|
|
6184
|
+
if (specialization) segs.push(specialization);
|
|
6185
|
+
if (topStacks) segs.push(topStacks);
|
|
6186
|
+
headline = segs.join(" \xB7 ");
|
|
6187
|
+
} else {
|
|
6188
|
+
const roleSeg = sen ? `${sen} ${GENERIC_ROLE}` : GENERIC_ROLE;
|
|
6189
|
+
headline = traction.status === "ok" && traction.totalStars > 0 ? `${roleSeg} \xB7 ${traction.totalStars}\u2605 across owned repos` : roleSeg;
|
|
6190
|
+
}
|
|
6191
|
+
const mergedDates = Object.values(domains).map((d) => d.lastMergedAt).filter((s) => typeof s === "string" && s.length > 0).sort();
|
|
6192
|
+
const mostRecent = mergedDates.length > 0 ? mergedDates[mergedDates.length - 1] : void 0;
|
|
6193
|
+
const thresholdDays = Number.isFinite(recency.value.thresholdDays) && recency.value.thresholdDays > 0 ? recency.value.thresholdDays : DORMANT_THRESHOLD_DAYS;
|
|
6194
|
+
const nowIso = recency.value.now && recency.value.now.length > 0 ? recency.value.now : credential.computedAt || (/* @__PURE__ */ new Date()).toISOString();
|
|
6195
|
+
const nowMs = Date.parse(nowIso);
|
|
6196
|
+
let recencyBadge = null;
|
|
6197
|
+
let daysAgo;
|
|
6198
|
+
if (mostRecent) {
|
|
6199
|
+
const ageDays2 = (nowMs - Date.parse(mostRecent)) / DAY_MS;
|
|
6200
|
+
daysAgo = Math.max(0, Math.round(ageDays2));
|
|
6201
|
+
recencyBadge = { lastMergedAt: mostRecent, state: ageDays2 <= thresholdDays ? "live" : "dormant" };
|
|
6202
|
+
}
|
|
6203
|
+
const orgCount = Object.values(domains).reduce((m, d) => Math.max(m, d.distinctOrgs), 0);
|
|
6204
|
+
let proofSentence;
|
|
6205
|
+
if (!ok) {
|
|
6206
|
+
proofSentence = "Contribution credential unavailable \u2014 could not verify.";
|
|
6207
|
+
} else {
|
|
6208
|
+
const prs = credential.qualifyingTotal;
|
|
6209
|
+
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)`;
|
|
6210
|
+
if (daysAgo !== void 0) s += ` \u2014 most recent ${daysAgo}d ago`;
|
|
6211
|
+
proofSentence = `${s}.`;
|
|
6212
|
+
}
|
|
6213
|
+
const auditableBadge = ok ? {
|
|
6214
|
+
mergedTotal: credential.qualifyingTotal,
|
|
6215
|
+
distinctOrgs: orgCount,
|
|
6216
|
+
thresholds: { stars: MIN_STARS, contributors: MIN_CONTRIBUTORS }
|
|
6217
|
+
} : null;
|
|
6218
|
+
const profile = {
|
|
6219
|
+
headline,
|
|
6220
|
+
verifiedSkillChips: chips,
|
|
6221
|
+
recencyBadge,
|
|
6222
|
+
proofSentence,
|
|
6223
|
+
auditableBadge
|
|
6224
|
+
};
|
|
6225
|
+
if (seniorityBand) profile.seniority = seniorityBand;
|
|
6226
|
+
return profile;
|
|
6227
|
+
}
|
|
6228
|
+
var DAY_MS, GENERIC_ROLE, ROLE_BY_DOMAIN, CONCEPT_TAGS, DISPLAY_LABEL;
|
|
6229
|
+
var init_legible = __esm({
|
|
6230
|
+
"../../packages/core/src/credential/legible.ts"() {
|
|
6231
|
+
"use strict";
|
|
6232
|
+
init_contribution_gate();
|
|
6233
|
+
init_vocabulary();
|
|
6234
|
+
init_recency_split();
|
|
6235
|
+
DAY_MS = 864e5;
|
|
6236
|
+
GENERIC_ROLE = "Software Engineer";
|
|
6237
|
+
ROLE_BY_DOMAIN = {
|
|
6238
|
+
backend: "Backend Engineer",
|
|
6239
|
+
go: "Backend Engineer",
|
|
6240
|
+
rust: "Backend Engineer",
|
|
6241
|
+
java: "Backend Engineer",
|
|
6242
|
+
python: "Backend Engineer",
|
|
6243
|
+
nodejs: "Backend Engineer",
|
|
6244
|
+
"api-design": "Backend Engineer",
|
|
6245
|
+
microservices: "Backend Engineer",
|
|
6246
|
+
postgresql: "Backend Engineer",
|
|
6247
|
+
frontend: "Frontend Engineer",
|
|
6248
|
+
react: "Frontend Engineer",
|
|
6249
|
+
vue: "Frontend Engineer",
|
|
6250
|
+
css: "Frontend Engineer",
|
|
6251
|
+
ml: "ML Engineer",
|
|
6252
|
+
pytorch: "ML Engineer",
|
|
6253
|
+
llm: "ML Engineer",
|
|
6254
|
+
"computer-vision": "ML Engineer",
|
|
6255
|
+
devops: "Platform Engineer",
|
|
6256
|
+
kubernetes: "Platform Engineer",
|
|
6257
|
+
terraform: "Platform Engineer",
|
|
6258
|
+
docker: "Platform Engineer",
|
|
6259
|
+
aws: "Platform Engineer",
|
|
6260
|
+
ios: "Mobile Engineer",
|
|
6261
|
+
android: "Mobile Engineer"
|
|
6262
|
+
};
|
|
6263
|
+
CONCEPT_TAGS = {
|
|
6264
|
+
"distributed-systems": "Distributed systems",
|
|
6265
|
+
microservices: "Microservices",
|
|
6266
|
+
security: "Security",
|
|
6267
|
+
payments: "Payments",
|
|
6268
|
+
ml: "Machine learning",
|
|
6269
|
+
llm: "LLM systems",
|
|
6270
|
+
"computer-vision": "Computer vision",
|
|
6271
|
+
recsys: "Recommendation systems",
|
|
6272
|
+
"api-design": "API design"
|
|
6273
|
+
};
|
|
6274
|
+
DISPLAY_LABEL = {
|
|
6275
|
+
postgresql: "Postgres",
|
|
6276
|
+
nodejs: "Node.js",
|
|
6277
|
+
ml: "ML",
|
|
6278
|
+
llm: "LLM",
|
|
6279
|
+
"api-design": "API design",
|
|
6280
|
+
"ci-cd": "CI/CD",
|
|
6281
|
+
"computer-vision": "Computer vision",
|
|
6282
|
+
"distributed-systems": "Distributed systems",
|
|
6283
|
+
ios: "iOS",
|
|
6284
|
+
css: "CSS",
|
|
6285
|
+
graphql: "GraphQL",
|
|
6286
|
+
aws: "AWS"
|
|
6287
|
+
};
|
|
6288
|
+
}
|
|
6289
|
+
});
|
|
6290
|
+
|
|
5890
6291
|
// ../../packages/core/src/index.ts
|
|
5891
6292
|
var src_exports = {};
|
|
5892
6293
|
__export(src_exports, {
|
|
@@ -5906,8 +6307,11 @@ __export(src_exports, {
|
|
|
5906
6307
|
INTRO_ALLOWED_FIELDS: () => INTRO_ALLOWED_FIELDS,
|
|
5907
6308
|
INTRO_PENDING_TTL_MS: () => INTRO_PENDING_TTL_MS,
|
|
5908
6309
|
LEVER_SLUGS_BY_TIER: () => LEVER_SLUGS_BY_TIER,
|
|
6310
|
+
MIN_CONTRIBUTORS: () => MIN_CONTRIBUTORS,
|
|
6311
|
+
MIN_STARS: () => MIN_STARS,
|
|
5909
6312
|
STRONG_MATCH_THRESHOLD: () => STRONG_MATCH_THRESHOLD,
|
|
5910
6313
|
SYNONYMS: () => SYNONYMS,
|
|
6314
|
+
TRIVIAL_PR_TITLE: () => TRIVIAL_PR_TITLE,
|
|
5911
6315
|
VOCABULARY: () => VOCABULARY,
|
|
5912
6316
|
VOCAB_NODES: () => VOCAB_NODES,
|
|
5913
6317
|
acceptanceCountForDomains: () => acceptanceCountForDomains,
|
|
@@ -5930,6 +6334,7 @@ __export(src_exports, {
|
|
|
5930
6334
|
coreTagsFromTitle: () => coreTagsFromTitle,
|
|
5931
6335
|
decorate: () => decorate,
|
|
5932
6336
|
decryptMessage: () => decryptMessage,
|
|
6337
|
+
deriveLegibleProfile: () => deriveLegibleProfile,
|
|
5933
6338
|
deriveResumeTrend: () => deriveResumeTrend,
|
|
5934
6339
|
deriveSharedKey: () => deriveSharedKey,
|
|
5935
6340
|
encryptMessage: () => encryptMessage,
|
|
@@ -5951,6 +6356,7 @@ __export(src_exports, {
|
|
|
5951
6356
|
introRateLimitCheck: () => introRateLimitCheck,
|
|
5952
6357
|
introRetentionAction: () => introRetentionAction,
|
|
5953
6358
|
isBounty: () => isBounty,
|
|
6359
|
+
isContribution: () => isContribution,
|
|
5954
6360
|
isOverIntroLimit: () => isOverIntroLimit,
|
|
5955
6361
|
lever: () => lever,
|
|
5956
6362
|
loadPartnerRoles: () => loadPartnerRoles,
|
|
@@ -5959,6 +6365,7 @@ __export(src_exports, {
|
|
|
5959
6365
|
normalize: () => normalize,
|
|
5960
6366
|
opire: () => opire,
|
|
5961
6367
|
pageMatches: () => pageMatches,
|
|
6368
|
+
passesContributionGate: () => passesContributionGate,
|
|
5962
6369
|
passesMaturityGate: () => passesMaturityGate,
|
|
5963
6370
|
personCardToJob: () => personCardToJob,
|
|
5964
6371
|
projectCardToJob: () => projectCardToJob,
|
|
@@ -5988,6 +6395,7 @@ var init_src = __esm({
|
|
|
5988
6395
|
init_directoryThreshold();
|
|
5989
6396
|
init_chatCrypto();
|
|
5990
6397
|
init_job_status();
|
|
6398
|
+
init_legible();
|
|
5991
6399
|
}
|
|
5992
6400
|
});
|
|
5993
6401
|
|
|
@@ -392,12 +392,20 @@ var init_vocabulary = __esm({
|
|
|
392
392
|
}
|
|
393
393
|
});
|
|
394
394
|
|
|
395
|
+
// ../../packages/core/src/feeds/contribution-gate.ts
|
|
396
|
+
var init_contribution_gate = __esm({
|
|
397
|
+
"../../packages/core/src/feeds/contribution-gate.ts"() {
|
|
398
|
+
"use strict";
|
|
399
|
+
}
|
|
400
|
+
});
|
|
401
|
+
|
|
395
402
|
// ../../packages/core/src/github.ts
|
|
396
403
|
var RESUME_DECAY_HALF_LIFE_MS;
|
|
397
404
|
var init_github = __esm({
|
|
398
405
|
"../../packages/core/src/github.ts"() {
|
|
399
406
|
"use strict";
|
|
400
407
|
init_vocabulary();
|
|
408
|
+
init_contribution_gate();
|
|
401
409
|
RESUME_DECAY_HALF_LIFE_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
402
410
|
}
|
|
403
411
|
});
|
|
@@ -486,7 +494,14 @@ var BOUNTY_REPO_DENYLIST, DENYLIST_LC;
|
|
|
486
494
|
var init_bounty_gate = __esm({
|
|
487
495
|
"../../packages/core/src/feeds/bounty-gate.ts"() {
|
|
488
496
|
"use strict";
|
|
489
|
-
BOUNTY_REPO_DENYLIST = [
|
|
497
|
+
BOUNTY_REPO_DENYLIST = [
|
|
498
|
+
"SecureBananaLabs/bug-bounty",
|
|
499
|
+
// Meta-farm: a bounty PLATFORM whose own issues are an assignment-gated
|
|
500
|
+
// contributor queue ("please assign me, my chief") — an unsolicited PR won't
|
|
501
|
+
// merge, so it's not a real claimable bounty. Not structurally derivable from
|
|
502
|
+
// any fetched field, so it's a manual entry (also dropped from the allowlist).
|
|
503
|
+
"boundlessfi/boundless"
|
|
504
|
+
];
|
|
490
505
|
DENYLIST_LC = new Set(BOUNTY_REPO_DENYLIST.map((r) => r.toLowerCase()));
|
|
491
506
|
}
|
|
492
507
|
});
|
|
@@ -557,6 +572,7 @@ var init_feeds = __esm({
|
|
|
557
572
|
init_directory();
|
|
558
573
|
init_bounty_gate();
|
|
559
574
|
init_bounty_gate();
|
|
575
|
+
init_contribution_gate();
|
|
560
576
|
GREENHOUSE_SLUGS_BY_TIER = {
|
|
561
577
|
bigco: [
|
|
562
578
|
"stripe",
|
|
@@ -665,6 +681,19 @@ var init_feeds = __esm({
|
|
|
665
681
|
}
|
|
666
682
|
});
|
|
667
683
|
|
|
684
|
+
// ../../packages/core/src/feeds/contributions.ts
|
|
685
|
+
var init_contributions = __esm({
|
|
686
|
+
"../../packages/core/src/feeds/contributions.ts"() {
|
|
687
|
+
"use strict";
|
|
688
|
+
init_vocabulary();
|
|
689
|
+
init_entities();
|
|
690
|
+
init_bounty_gate();
|
|
691
|
+
init_contribution_gate();
|
|
692
|
+
init_github_bounties();
|
|
693
|
+
init_http();
|
|
694
|
+
}
|
|
695
|
+
});
|
|
696
|
+
|
|
668
697
|
// ../../packages/core/src/partners.ts
|
|
669
698
|
import { readFileSync } from "fs";
|
|
670
699
|
import { join } from "path";
|
|
@@ -689,6 +718,7 @@ var init_indexer = __esm({
|
|
|
689
718
|
"../../packages/core/src/indexer.ts"() {
|
|
690
719
|
"use strict";
|
|
691
720
|
init_feeds();
|
|
721
|
+
init_contributions();
|
|
692
722
|
init_partners();
|
|
693
723
|
}
|
|
694
724
|
});
|
|
@@ -3756,6 +3786,55 @@ var init_job_status = __esm({
|
|
|
3756
3786
|
}
|
|
3757
3787
|
});
|
|
3758
3788
|
|
|
3789
|
+
// ../../packages/core/src/episodes/schema.ts
|
|
3790
|
+
var init_schema = __esm({
|
|
3791
|
+
"../../packages/core/src/episodes/schema.ts"() {
|
|
3792
|
+
"use strict";
|
|
3793
|
+
}
|
|
3794
|
+
});
|
|
3795
|
+
|
|
3796
|
+
// ../../packages/core/src/episodes/doors.ts
|
|
3797
|
+
var init_doors = __esm({
|
|
3798
|
+
"../../packages/core/src/episodes/doors.ts"() {
|
|
3799
|
+
"use strict";
|
|
3800
|
+
init_schema();
|
|
3801
|
+
}
|
|
3802
|
+
});
|
|
3803
|
+
|
|
3804
|
+
// ../../packages/core/src/episodes/node-model.ts
|
|
3805
|
+
var init_node_model = __esm({
|
|
3806
|
+
"../../packages/core/src/episodes/node-model.ts"() {
|
|
3807
|
+
"use strict";
|
|
3808
|
+
}
|
|
3809
|
+
});
|
|
3810
|
+
|
|
3811
|
+
// ../../packages/core/src/episodes/derivers/signals.ts
|
|
3812
|
+
var init_signals = __esm({
|
|
3813
|
+
"../../packages/core/src/episodes/derivers/signals.ts"() {
|
|
3814
|
+
"use strict";
|
|
3815
|
+
init_node_model();
|
|
3816
|
+
}
|
|
3817
|
+
});
|
|
3818
|
+
|
|
3819
|
+
// ../../packages/core/src/episodes/derivers/recency-split.ts
|
|
3820
|
+
var init_recency_split = __esm({
|
|
3821
|
+
"../../packages/core/src/episodes/derivers/recency-split.ts"() {
|
|
3822
|
+
"use strict";
|
|
3823
|
+
init_doors();
|
|
3824
|
+
init_signals();
|
|
3825
|
+
}
|
|
3826
|
+
});
|
|
3827
|
+
|
|
3828
|
+
// ../../packages/core/src/credential/legible.ts
|
|
3829
|
+
var init_legible = __esm({
|
|
3830
|
+
"../../packages/core/src/credential/legible.ts"() {
|
|
3831
|
+
"use strict";
|
|
3832
|
+
init_contribution_gate();
|
|
3833
|
+
init_vocabulary();
|
|
3834
|
+
init_recency_split();
|
|
3835
|
+
}
|
|
3836
|
+
});
|
|
3837
|
+
|
|
3759
3838
|
// ../../packages/core/src/index.ts
|
|
3760
3839
|
var init_src = __esm({
|
|
3761
3840
|
"../../packages/core/src/index.ts"() {
|
|
@@ -3771,6 +3850,7 @@ var init_src = __esm({
|
|
|
3771
3850
|
init_directoryThreshold();
|
|
3772
3851
|
init_chatCrypto();
|
|
3773
3852
|
init_job_status();
|
|
3853
|
+
init_legible();
|
|
3774
3854
|
}
|
|
3775
3855
|
});
|
|
3776
3856
|
|
|
@@ -4176,7 +4256,9 @@ var init_config = __esm({
|
|
|
4176
4256
|
chatDisclosureAck: false,
|
|
4177
4257
|
chatShareActivity: false,
|
|
4178
4258
|
inboundNudgeMuted: false,
|
|
4179
|
-
inboundNudgeDisclosed: false
|
|
4259
|
+
inboundNudgeDisclosed: false,
|
|
4260
|
+
contributeEnabled: false,
|
|
4261
|
+
contributePrompted: false
|
|
4180
4262
|
};
|
|
4181
4263
|
}
|
|
4182
4264
|
});
|