terminalhire 0.4.0 → 0.4.4

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.
@@ -148,7 +148,7 @@ var init_graph_data = __esm({
148
148
  { id: "airflow", parents: ["data-engineering"], synonyms: ["apache-airflow"] },
149
149
  { id: "dbt", parents: ["data-engineering"] },
150
150
  { id: "ml", synonyms: ["machine-learning"], related: [{ to: "pytorch", w: 0.5 }, { to: "tensorflow", w: 0.5 }, { to: "scikit-learn", w: 0.5 }, { to: "data-engineering", w: 0.4 }] },
151
- { id: "llm", parents: ["ml"], synonyms: ["llms", "genai", "generative-ai"], related: [{ to: "langchain", w: 0.5 }, { to: "rag", w: 0.55 }, { to: "openai", w: 0.45 }, { to: "anthropic", w: 0.45 }] },
151
+ { id: "llm", parents: ["ml"], synonyms: ["llms", "genai", "generative-ai", "gpt"], related: [{ to: "langchain", w: 0.5 }, { to: "rag", w: 0.55 }, { to: "openai", w: 0.45 }, { to: "anthropic", w: 0.45 }] },
152
152
  { id: "pytorch", parents: ["ml"], synonyms: ["torch"], related: [{ to: "tensorflow", w: 0.5 }] },
153
153
  { id: "tensorflow", parents: ["ml"], synonyms: ["keras", "tf-keras"] },
154
154
  { id: "pandas", parents: ["python"], related: [{ to: "numpy", w: 0.6 }, { to: "data-engineering", w: 0.45 }, { to: "spark", w: 0.4 }] },
@@ -161,6 +161,14 @@ var init_graph_data = __esm({
161
161
  { id: "anthropic", parents: ["llm"], synonyms: ["claude"] },
162
162
  { id: "rag", parents: ["llm"], synonyms: ["retrieval-augmented-generation"] },
163
163
  { id: "mlops", parents: ["ml"], related: [{ to: "devops", w: 0.4 }] },
164
+ { id: "agents", parents: ["llm"], synonyms: ["agentic", "ai-agents", "multi-agent"], related: [{ to: "rag", w: 0.4 }] },
165
+ { id: "mcp", parents: ["agents"], synonyms: ["model-context-protocol"], related: [{ to: "llm", w: 0.45 }] },
166
+ { id: "inference", parents: ["ml"], synonyms: ["model-inference", "llm-inference", "model-serving"], related: [{ to: "mlops", w: 0.5 }, { to: "llm", w: 0.4 }] },
167
+ { id: "embeddings", parents: ["ml"], synonyms: ["embedding", "vector-embeddings"], related: [{ to: "rag", w: 0.55 }, { to: "llm", w: 0.45 }] },
168
+ { id: "prompt-engineering", parents: ["llm"], synonyms: ["prompting", "prompt"] },
169
+ { id: "fine-tuning", parents: ["ml"], synonyms: ["finetuning", "fine-tune", "rlhf"], related: [{ to: "llm", w: 0.5 }] },
170
+ { id: "computer-vision", parents: ["ml"], synonyms: ["image-recognition", "object-detection"] },
171
+ { id: "recsys", parents: ["ml"], synonyms: ["recommender-systems", "recommendation-systems", "recommendation"] },
164
172
  // ── Mobile ──────────────────────────────────────────────────────────────────
165
173
  { id: "mobile", related: [{ to: "ios", w: 0.5 }, { to: "android", w: 0.5 }] },
166
174
  { id: "ios", parents: ["mobile", "swift"], related: [{ to: "android", w: 0.4 }] },
@@ -583,7 +591,10 @@ var init_vocabulary = __esm({
583
591
  function ghHeaders(token) {
584
592
  const headers = {
585
593
  Accept: "application/vnd.github+json",
586
- "X-GitHub-Api-Version": "2022-11-28"
594
+ "X-GitHub-Api-Version": "2022-11-28",
595
+ // GitHub's REST API REQUIRES a User-Agent; serverless runtimes don't always
596
+ // send a default (omitting it yields a 403 "administrative rules" error).
597
+ "User-Agent": "terminalhire"
587
598
  };
588
599
  if (token) headers["Authorization"] = `Bearer ${token}`;
589
600
  return headers;
@@ -727,16 +738,25 @@ async function fetchRepoMeta(owner, name, token, cache) {
727
738
  cache.set(key, meta);
728
739
  return meta;
729
740
  }
730
- async function computeAcceptanceCredential(login, token, cache = /* @__PURE__ */ new Map()) {
741
+ function emptyCredential(status) {
742
+ return { status, byDomain: {}, qualifyingTotal: 0, computedAt: (/* @__PURE__ */ new Date()).toISOString() };
743
+ }
744
+ async function fetchPublicOrgs(login, token) {
745
+ try {
746
+ const orgs = await ghFetch(
747
+ `/users/${login}/orgs?per_page=100`,
748
+ token
749
+ );
750
+ return new Set(orgs.map((o) => o.login.toLowerCase()));
751
+ } catch {
752
+ return /* @__PURE__ */ new Set();
753
+ }
754
+ }
755
+ async function computeAcceptanceFromSearch(login, token, ownedOrgs, cache, gates = {
756
+ minStars: MIN_STARS,
757
+ minContributors: MIN_CONTRIBUTORS
758
+ }) {
731
759
  const computedAt = (/* @__PURE__ */ new Date()).toISOString();
732
- const empty = (status) => ({
733
- status,
734
- byDomain: {},
735
- qualifyingTotal: 0,
736
- computedAt
737
- });
738
- if (!token) return empty("no-token");
739
- const ownedOrgs = await fetchOwnedOrgs(token);
740
760
  const loginLc = login.toLowerCase();
741
761
  let items;
742
762
  try {
@@ -747,8 +767,9 @@ async function computeAcceptanceCredential(login, token, cache = /* @__PURE__ */
747
767
  );
748
768
  items = res.items ?? [];
749
769
  } catch (err) {
750
- const msg = String(err);
751
- return empty(/HTTP 403|HTTP 429|rate limit/i.test(msg) ? "rate-limited" : "failed");
770
+ const msg = err instanceof Error ? err.message : String(err);
771
+ console.warn("[acceptance] search failed:", msg);
772
+ return emptyCredential(/HTTP 403|HTTP 429|rate limit/i.test(msg) ? "rate-limited" : "failed");
752
773
  }
753
774
  const byDomain = {};
754
775
  let qualifyingTotal = 0;
@@ -762,8 +783,8 @@ async function computeAcceptanceCredential(login, token, cache = /* @__PURE__ */
762
783
  const meta = await fetchRepoMeta(repo.owner, repo.name, token, cache);
763
784
  if (!meta) continue;
764
785
  if (meta.archived || meta.fork) continue;
765
- if (meta.stars < MIN_STARS) continue;
766
- if (meta.contributors !== void 0 && meta.contributors < MIN_CONTRIBUTORS) continue;
786
+ if (meta.stars < gates.minStars) continue;
787
+ if (meta.contributors !== void 0 && meta.contributors < gates.minContributors) continue;
767
788
  qualifyingTotal += 1;
768
789
  const mergedAt = item.pull_request?.merged_at ?? item.closed_at ?? item.created_at;
769
790
  const rawDomains = [meta.language ?? "", ...meta.topics].filter(Boolean);
@@ -784,6 +805,18 @@ async function computeAcceptanceCredential(login, token, cache = /* @__PURE__ */
784
805
  }
785
806
  return { status: "ok", byDomain: finalDomains, qualifyingTotal, computedAt };
786
807
  }
808
+ async function computeAcceptanceCredential(login, token, cache = /* @__PURE__ */ new Map()) {
809
+ if (!token) return emptyCredential("no-token");
810
+ const ownedOrgs = await fetchOwnedOrgs(token);
811
+ return computeAcceptanceFromSearch(login, token, ownedOrgs, cache);
812
+ }
813
+ async function computeAcceptanceCredentialPublic(login, token, cache = /* @__PURE__ */ new Map(), opts) {
814
+ if (!token) return emptyCredential("no-token");
815
+ const ownedOrgs = await fetchPublicOrgs(login, token);
816
+ for (const org of opts?.includeOrgs ?? []) ownedOrgs.delete(org.toLowerCase());
817
+ const gates = opts?.relaxGates ? { minStars: 0, minContributors: 0 } : void 0;
818
+ return computeAcceptanceFromSearch(login, token, ownedOrgs, cache, gates);
819
+ }
787
820
  function acceptanceCountForDomains(cred, domains) {
788
821
  if (cred.status !== "ok") return 0;
789
822
  let max = 0;
@@ -802,7 +835,60 @@ function bestAcceptanceDomain(cred, domains) {
802
835
  }
803
836
  return best;
804
837
  }
805
- var MIN_STARS, MIN_CONTRIBUTORS, CANDIDATE_PR_PAGE, TRIVIAL_PR_TITLE;
838
+ function resumeRecencyDecay(lastSeenIso, now) {
839
+ const ageMs = now - new Date(lastSeenIso).getTime();
840
+ if (!Number.isFinite(ageMs)) return 0;
841
+ return Math.pow(0.5, ageMs / RESUME_DECAY_HALF_LIFE_MS);
842
+ }
843
+ async function fetchRepoRecency(login, token) {
844
+ try {
845
+ const repos = await ghFetch(`/users/${login}/repos?sort=pushed&per_page=100`, token);
846
+ return repos.filter((r) => !r.fork && !!r.pushed_at).map((r) => ({ pushedAt: r.pushed_at, language: r.language ?? null, topics: r.topics ?? [] }));
847
+ } catch {
848
+ return [];
849
+ }
850
+ }
851
+ function deriveResumeTrend(cred, repoRecency, now = Date.now()) {
852
+ const agg = /* @__PURE__ */ new Map();
853
+ const bump = (domain, when, count, mergedPRs) => {
854
+ const e = agg.get(domain);
855
+ if (!e) {
856
+ agg.set(domain, { count, last: when, earliest: when, mergedPRs });
857
+ } else {
858
+ e.count += count;
859
+ e.mergedPRs += mergedPRs;
860
+ if (when > e.last) e.last = when;
861
+ if (when < e.earliest) e.earliest = when;
862
+ }
863
+ };
864
+ if (cred.status === "ok") {
865
+ for (const [domain, d] of Object.entries(cred.byDomain)) {
866
+ bump(domain, d.lastMergedAt, d.mergedPRs, d.mergedPRs);
867
+ }
868
+ }
869
+ for (const r of repoRecency) {
870
+ for (const domain of new Set(normalize([r.language ?? "", ...r.topics].filter(Boolean)))) {
871
+ bump(domain, r.pushedAt, 1, 0);
872
+ }
873
+ }
874
+ const oneHalfLifeAgoIso = new Date(now - RESUME_DECAY_HALF_LIFE_MS).toISOString();
875
+ const scored = [];
876
+ for (const [domain, e] of agg.entries()) {
877
+ const recencyScore2 = resumeRecencyDecay(e.last, now);
878
+ const weight = e.count * recencyScore2;
879
+ if (weight < RESUME_MIN_SCORE) continue;
880
+ let direction;
881
+ if (e.earliest > oneHalfLifeAgoIso) direction = "new";
882
+ else if (recencyScore2 >= 0.5) direction = "up";
883
+ else direction = "down";
884
+ scored.push({
885
+ t: { domain, direction, recencyScore: Math.round(recencyScore2 * 1e3) / 1e3, mergedPRs: e.mergedPRs },
886
+ weight
887
+ });
888
+ }
889
+ return scored.sort((a, b) => b.weight - a.weight).slice(0, 12).map((s) => s.t);
890
+ }
891
+ var MIN_STARS, MIN_CONTRIBUTORS, CANDIDATE_PR_PAGE, TRIVIAL_PR_TITLE, RESUME_DECAY_HALF_LIFE_MS, RESUME_MIN_SCORE;
806
892
  var init_github = __esm({
807
893
  "../../packages/core/src/github.ts"() {
808
894
  "use strict";
@@ -811,6 +897,8 @@ var init_github = __esm({
811
897
  MIN_CONTRIBUTORS = 10;
812
898
  CANDIDATE_PR_PAGE = 50;
813
899
  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;
900
+ RESUME_DECAY_HALF_LIFE_MS = 30 * 24 * 60 * 60 * 1e3;
901
+ RESUME_MIN_SCORE = 0.05;
814
902
  }
815
903
  });
816
904
 
@@ -958,6 +1046,18 @@ var init_matcher = __esm({
958
1046
  }
959
1047
  });
960
1048
 
1049
+ // ../../packages/core/src/feeds/http.ts
1050
+ function fetchWithTimeout(input, init, timeoutMs = FEED_FETCH_TIMEOUT_MS) {
1051
+ return fetch(input, { ...init, signal: AbortSignal.timeout(timeoutMs) });
1052
+ }
1053
+ var FEED_FETCH_TIMEOUT_MS;
1054
+ var init_http = __esm({
1055
+ "../../packages/core/src/feeds/http.ts"() {
1056
+ "use strict";
1057
+ FEED_FETCH_TIMEOUT_MS = 1e4;
1058
+ }
1059
+ });
1060
+
961
1061
  // ../../packages/core/src/feeds/greenhouse.ts
962
1062
  function extractTags(job) {
963
1063
  const body = [
@@ -976,7 +1076,7 @@ async function fetchSlug(slug) {
976
1076
  const url = `https://boards-api.greenhouse.io/v1/boards/${slug}/jobs?content=true`;
977
1077
  let res;
978
1078
  try {
979
- res = await fetch(url, { headers: { Accept: "application/json" } });
1079
+ res = await fetchWithTimeout(url, { headers: { Accept: "application/json" } });
980
1080
  } catch (err) {
981
1081
  console.warn(`[greenhouse] ${slug}: network error \u2014`, err);
982
1082
  return [];
@@ -1018,6 +1118,7 @@ var init_greenhouse = __esm({
1018
1118
  "../../packages/core/src/feeds/greenhouse.ts"() {
1019
1119
  "use strict";
1020
1120
  init_vocabulary();
1121
+ init_http();
1021
1122
  FALLBACK_SLUGS = [
1022
1123
  "stripe",
1023
1124
  "linear",
@@ -1088,7 +1189,7 @@ function inferRemote2(job) {
1088
1189
  }
1089
1190
  async function fetchSlug2(slug) {
1090
1191
  const url = `https://api.ashbyhq.com/posting-api/job-board/${slug}`;
1091
- const res = await fetch(url, {
1192
+ const res = await fetchWithTimeout(url, {
1092
1193
  headers: { Accept: "application/json" }
1093
1194
  });
1094
1195
  if (!res.ok) {
@@ -1120,6 +1221,7 @@ var init_ashby = __esm({
1120
1221
  "../../packages/core/src/feeds/ashby.ts"() {
1121
1222
  "use strict";
1122
1223
  init_vocabulary();
1224
+ init_http();
1123
1225
  ashby = {
1124
1226
  source: "ashby",
1125
1227
  async fetch(opts) {
@@ -1170,7 +1272,7 @@ function toIso(ms) {
1170
1272
  }
1171
1273
  async function fetchSlug3(slug) {
1172
1274
  const url = `https://api.lever.co/v0/postings/${slug}?mode=json`;
1173
- const res = await fetch(url, { headers: { Accept: "application/json" } });
1275
+ const res = await fetchWithTimeout(url, { headers: { Accept: "application/json" } });
1174
1276
  if (!res.ok) {
1175
1277
  throw new Error(`Lever ${slug}: HTTP ${res.status}`);
1176
1278
  }
@@ -1201,6 +1303,7 @@ var init_lever = __esm({
1201
1303
  "../../packages/core/src/feeds/lever.ts"() {
1202
1304
  "use strict";
1203
1305
  init_vocabulary();
1306
+ init_http();
1204
1307
  lever = {
1205
1308
  source: "lever",
1206
1309
  async fetch(opts) {
@@ -1242,12 +1345,13 @@ var init_himalayas = __esm({
1242
1345
  "../../packages/core/src/feeds/himalayas.ts"() {
1243
1346
  "use strict";
1244
1347
  init_vocabulary();
1348
+ init_http();
1245
1349
  himalayas = {
1246
1350
  source: "himalayas",
1247
1351
  async fetch(opts) {
1248
1352
  const limit = opts?.limit ?? 100;
1249
1353
  const url = `https://himalayas.app/jobs/api?limit=${limit}`;
1250
- const res = await fetch(url, {
1354
+ const res = await fetchWithTimeout(url, {
1251
1355
  headers: { Accept: "application/json" }
1252
1356
  });
1253
1357
  if (!res.ok) {
@@ -1356,12 +1460,13 @@ var init_wwr = __esm({
1356
1460
  "use strict";
1357
1461
  init_vocabulary();
1358
1462
  init_entities();
1463
+ init_http();
1359
1464
  WWR_RSS_URL = "https://weworkremotely.com/remote-jobs.rss";
1360
1465
  wwr = {
1361
1466
  source: "wwr",
1362
1467
  async fetch(opts) {
1363
1468
  const limit = opts?.limit ?? 200;
1364
- const res = await fetch(WWR_RSS_URL, {
1469
+ const res = await fetchWithTimeout(WWR_RSS_URL, {
1365
1470
  headers: { Accept: "application/rss+xml, application/xml, text/xml" }
1366
1471
  });
1367
1472
  if (!res.ok) {
@@ -1369,6 +1474,11 @@ var init_wwr = __esm({
1369
1474
  }
1370
1475
  const xml = await res.text();
1371
1476
  const items = parseRss(xml).slice(0, limit);
1477
+ function safeIso(s) {
1478
+ if (!s) return void 0;
1479
+ const d = new Date(s);
1480
+ return Number.isNaN(d.getTime()) ? void 0 : d.toISOString();
1481
+ }
1372
1482
  return items.map((item) => ({
1373
1483
  id: extractId(item.link),
1374
1484
  source: "wwr",
@@ -1380,7 +1490,7 @@ var init_wwr = __esm({
1380
1490
  location: "Remote",
1381
1491
  tags: extractTags5(item),
1382
1492
  roleType: inferRoleType(item.category),
1383
- postedAt: item.pubDate ? new Date(item.pubDate).toISOString() : void 0,
1493
+ postedAt: safeIso(item.pubDate),
1384
1494
  applyMode: "direct",
1385
1495
  raw: item
1386
1496
  }));
@@ -1446,13 +1556,14 @@ var init_hn = __esm({
1446
1556
  "use strict";
1447
1557
  init_vocabulary();
1448
1558
  init_entities();
1559
+ init_http();
1449
1560
  ALGOLIA_SEARCH = "https://hn.algolia.com/api/v1/search?query=Ask+HN%3A+Who+is+Hiring%3F&tags=story,ask_hn&hitsPerPage=1";
1450
1561
  ALGOLIA_ITEMS = "https://hn.algolia.com/api/v1/items/";
1451
1562
  hn = {
1452
1563
  source: "hn",
1453
1564
  async fetch(opts) {
1454
1565
  const limit = opts?.limit ?? 150;
1455
- const searchRes = await fetch(ALGOLIA_SEARCH, {
1566
+ const searchRes = await fetchWithTimeout(ALGOLIA_SEARCH, {
1456
1567
  headers: { Accept: "application/json" }
1457
1568
  });
1458
1569
  if (!searchRes.ok) {
@@ -1463,7 +1574,7 @@ var init_hn = __esm({
1463
1574
  if (!story) {
1464
1575
  throw new Error('HN: No "Who is Hiring" story found');
1465
1576
  }
1466
- const itemRes = await fetch(`${ALGOLIA_ITEMS}${story.objectID}`, {
1577
+ const itemRes = await fetchWithTimeout(`${ALGOLIA_ITEMS}${story.objectID}`, {
1467
1578
  headers: { Accept: "application/json" }
1468
1579
  });
1469
1580
  if (!itemRes.ok) {
@@ -1483,18 +1594,25 @@ var init_hn = __esm({
1483
1594
  });
1484
1595
 
1485
1596
  // ../../packages/core/src/feeds/bounty-gate.ts
1597
+ function isDenylistedRepo(fullName) {
1598
+ return DENYLIST_LC.has(fullName.toLowerCase());
1599
+ }
1600
+ function passesAntiFarm(amountUSD, stargazers) {
1601
+ return !(amountUSD > HIGH_VALUE_USD && stargazers < HIGH_VALUE_MIN_STARS);
1602
+ }
1486
1603
  function ageDays(createdAtIso) {
1487
1604
  const created = Date.parse(createdAtIso);
1488
1605
  if (!Number.isFinite(created)) return 0;
1489
1606
  return (Date.now() - created) / (1e3 * 60 * 60 * 24);
1490
1607
  }
1491
1608
  function passesMaturityGate(repo) {
1609
+ if (isDenylistedRepo(repo.fullName)) return false;
1492
1610
  if (repo.archived || repo.disabled) return false;
1493
1611
  if (repo.stargazers < MIN_REPO_STARS) return false;
1494
1612
  if (ageDays(repo.createdAt) < MIN_REPO_AGE_DAYS) return false;
1495
1613
  return true;
1496
1614
  }
1497
- var DEFAULT_BOUNTY_REPOS, MAX_BOUNTIES_PER_REPO, MIN_REPO_STARS, MIN_REPO_AGE_DAYS;
1615
+ var DEFAULT_BOUNTY_REPOS, BOUNTY_REPO_DENYLIST, DENYLIST_LC, MAX_BOUNTIES_PER_REPO, MAX_BOUNTIES_PER_DISCOVERED_REPO, MIN_REPO_STARS, HIGH_VALUE_USD, HIGH_VALUE_MIN_STARS, MIN_REPO_AGE_DAYS;
1498
1616
  var init_bounty_gate = __esm({
1499
1617
  "../../packages/core/src/feeds/bounty-gate.ts"() {
1500
1618
  "use strict";
@@ -1511,8 +1629,13 @@ var init_bounty_gate = __esm({
1511
1629
  "moorcheh-ai/memanto",
1512
1630
  "PrismarineJS/mineflayer"
1513
1631
  ];
1632
+ BOUNTY_REPO_DENYLIST = ["SecureBananaLabs/bug-bounty"];
1633
+ DENYLIST_LC = new Set(BOUNTY_REPO_DENYLIST.map((r) => r.toLowerCase()));
1514
1634
  MAX_BOUNTIES_PER_REPO = 10;
1635
+ MAX_BOUNTIES_PER_DISCOVERED_REPO = 3;
1515
1636
  MIN_REPO_STARS = 5;
1637
+ HIGH_VALUE_USD = 500;
1638
+ HIGH_VALUE_MIN_STARS = 50;
1516
1639
  MIN_REPO_AGE_DAYS = 30;
1517
1640
  }
1518
1641
  });
@@ -1557,7 +1680,7 @@ function isBountyIssue(issue) {
1557
1680
  async function ghJson(path) {
1558
1681
  let res;
1559
1682
  try {
1560
- res = await fetch(`${GITHUB_API}${path}`, { headers: authHeaders() });
1683
+ res = await fetchWithTimeout(`${GITHUB_API}${path}`, { headers: authHeaders() });
1561
1684
  } catch (err) {
1562
1685
  console.warn(`[github-bounties] network error ${path} \u2014`, err);
1563
1686
  return null;
@@ -1639,31 +1762,435 @@ async function fetchRepoBounties(repoFullName) {
1639
1762
  };
1640
1763
  }));
1641
1764
  }
1642
- var GITHUB_API, BOUNTY_LABEL_RE, githubBounties;
1765
+ function repoFullNameFromApiUrl(url) {
1766
+ const m = url.match(/\/repos\/([^/]+)\/([^/]+)\/?$/);
1767
+ return m ? `${m[1]}/${m[2]}` : null;
1768
+ }
1769
+ async function searchBountyIssues() {
1770
+ const byUrl = /* @__PURE__ */ new Map();
1771
+ for (const q of SEARCH_QUERIES) {
1772
+ const res = await ghJson(
1773
+ `/search/issues?q=${encodeURIComponent(q)}&sort=created&order=desc&per_page=${SEARCH_PER_PAGE}`
1774
+ );
1775
+ for (const it of res?.items ?? []) {
1776
+ if (it.pull_request) continue;
1777
+ if (!byUrl.has(it.html_url)) byUrl.set(it.html_url, it);
1778
+ }
1779
+ }
1780
+ return [...byUrl.values()].sort(
1781
+ (a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime()
1782
+ );
1783
+ }
1784
+ async function repoMetaCached(fullName) {
1785
+ const hit = repoMetaCache.get(fullName);
1786
+ if (hit !== void 0) return hit;
1787
+ const r = await ghJson(`/repos/${fullName}`) ?? null;
1788
+ repoMetaCache.set(fullName, r);
1789
+ return r;
1790
+ }
1791
+ async function fetchRepoMeta2(fullName) {
1792
+ const repo = await repoMetaCached(fullName);
1793
+ if (!repo) return null;
1794
+ return {
1795
+ fullName: repo.full_name,
1796
+ stargazers: repo.stargazers_count,
1797
+ createdAt: repo.created_at,
1798
+ archived: repo.archived,
1799
+ disabled: repo.disabled
1800
+ };
1801
+ }
1802
+ async function fetchRepoOpenPRRefs(fullName) {
1803
+ const hit = repoOpenPRRefsCache.get(fullName);
1804
+ if (hit !== void 0) return hit;
1805
+ const refs = /* @__PURE__ */ new Map();
1806
+ let scannedAny = false;
1807
+ for (let page = 1; page <= MAX_PR_PAGES; page++) {
1808
+ const prs = await ghJson(
1809
+ `/repos/${fullName}/pulls?state=open&per_page=100&page=${page}`
1810
+ );
1811
+ if (!Array.isArray(prs)) break;
1812
+ scannedAny = true;
1813
+ for (const pr of prs) {
1814
+ const counted = /* @__PURE__ */ new Set();
1815
+ for (const m of `${pr.title ?? ""}
1816
+ ${pr.body ?? ""}`.matchAll(/#(\d+)\b/g)) {
1817
+ const n = Number(m[1]);
1818
+ if (!counted.has(n)) {
1819
+ counted.add(n);
1820
+ refs.set(n, (refs.get(n) ?? 0) + 1);
1821
+ }
1822
+ }
1823
+ }
1824
+ if (prs.length < 100) break;
1825
+ }
1826
+ const result = scannedAny ? refs : null;
1827
+ repoOpenPRRefsCache.set(fullName, result);
1828
+ return result;
1829
+ }
1830
+ async function fetchIssueState(fullName, issueNumber) {
1831
+ const key = `${fullName}#${issueNumber}`;
1832
+ const hit = issueStateCache.get(key);
1833
+ if (hit !== void 0) return hit;
1834
+ const issue = await ghJson(`/repos/${fullName}/issues/${issueNumber}`);
1835
+ const state = issue?.state === "open" ? "open" : issue?.state === "closed" ? "closed" : null;
1836
+ issueStateCache.set(key, state);
1837
+ return state;
1838
+ }
1839
+ async function fetchSearchBounties() {
1840
+ const issues = (await searchBountyIssues()).slice(0, MAX_SEARCH_ISSUES_SCANNED);
1841
+ const distinctRepos = [
1842
+ ...new Set(
1843
+ issues.map((i) => repoFullNameFromApiUrl(i.repository_url)).filter((x) => !!x)
1844
+ )
1845
+ ];
1846
+ for (let i = 0; i < distinctRepos.length; i += REPO_META_CONCURRENCY) {
1847
+ await Promise.all(distinctRepos.slice(i, i + REPO_META_CONCURRENCY).map(repoMetaCached));
1848
+ }
1849
+ const jobs = [];
1850
+ const perRepo = /* @__PURE__ */ new Map();
1851
+ for (const issue of issues) {
1852
+ if (jobs.length >= MAX_SEARCH_BOUNTIES) break;
1853
+ const fullName = repoFullNameFromApiUrl(issue.repository_url);
1854
+ if (!fullName) continue;
1855
+ if ((perRepo.get(fullName) ?? 0) >= MAX_BOUNTIES_PER_REPO) continue;
1856
+ const repo = await repoMetaCached(fullName);
1857
+ if (!repo) continue;
1858
+ const passes = passesMaturityGate({
1859
+ fullName: repo.full_name,
1860
+ stargazers: repo.stargazers_count,
1861
+ createdAt: repo.created_at,
1862
+ archived: repo.archived,
1863
+ disabled: repo.disabled
1864
+ });
1865
+ if (!passes) continue;
1866
+ const title = decodeEntities(issue.title).trim();
1867
+ const body = issue.body ? decodeEntities(issue.body) : "";
1868
+ const labels = labelNames(issue);
1869
+ let amountUSD = parseAmountUSD(title) ?? parseAmountUSD(labels.join(" ")) ?? parseAmountUSD(body);
1870
+ if (amountUSD == null && labels.some((n) => /💎|💰/.test(n))) {
1871
+ amountUSD = await fetchCommentAmount(fullName, issue.number);
1872
+ }
1873
+ if (amountUSD == null) continue;
1874
+ if (!passesAntiFarm(amountUSD, repo.stargazers_count)) continue;
1875
+ const tags = normalize(
1876
+ tokenize2([title, labels.join(" "), body.slice(0, 2e3)].join(" "))
1877
+ );
1878
+ perRepo.set(fullName, (perRepo.get(fullName) ?? 0) + 1);
1879
+ jobs.push({
1880
+ id: `bounty:${fullName}#${issue.number}`,
1881
+ source: "bounty",
1882
+ title,
1883
+ company: repo.owner.login,
1884
+ url: issue.html_url,
1885
+ remote: true,
1886
+ location: "Remote",
1887
+ tags,
1888
+ roleType: "freelance",
1889
+ postedAt: issue.created_at,
1890
+ applyMode: "direct",
1891
+ bounty: {
1892
+ amountUSD,
1893
+ estimatedEffort: effortFromAmount(amountUSD),
1894
+ bountySource: "github",
1895
+ claimUrl: issue.html_url,
1896
+ repoFullName: fullName,
1897
+ repoStars: repo.stargazers_count,
1898
+ issueBody: body.slice(0, 1e3) || void 0
1899
+ },
1900
+ raw: issue
1901
+ });
1902
+ }
1903
+ return jobs;
1904
+ }
1905
+ var GITHUB_API, BOUNTY_LABEL_RE, SEARCH_QUERIES, SEARCH_PER_PAGE, MAX_SEARCH_BOUNTIES, MAX_SEARCH_ISSUES_SCANNED, REPO_META_CONCURRENCY, repoMetaCache, MAX_PR_PAGES, repoOpenPRRefsCache, issueStateCache, githubBounties;
1643
1906
  var init_github_bounties = __esm({
1644
1907
  "../../packages/core/src/feeds/github-bounties.ts"() {
1645
1908
  "use strict";
1646
1909
  init_vocabulary();
1647
1910
  init_entities();
1648
1911
  init_bounty_gate();
1912
+ init_http();
1649
1913
  GITHUB_API = "https://api.github.com";
1650
1914
  BOUNTY_LABEL_RE = /bounty|reward|funded|💎|💰/i;
1915
+ SEARCH_QUERIES = [
1916
+ 'label:"\u{1F48E} Bounty" type:issue state:open',
1917
+ // Algora-applied — highest signal
1918
+ "label:bounty type:issue state:open",
1919
+ 'label:"\u{1F4B0} Bounty" type:issue state:open'
1920
+ ];
1921
+ SEARCH_PER_PAGE = 100;
1922
+ MAX_SEARCH_BOUNTIES = 150;
1923
+ MAX_SEARCH_ISSUES_SCANNED = 300;
1924
+ REPO_META_CONCURRENCY = 15;
1925
+ repoMetaCache = /* @__PURE__ */ new Map();
1926
+ MAX_PR_PAGES = 3;
1927
+ repoOpenPRRefsCache = /* @__PURE__ */ new Map();
1928
+ issueStateCache = /* @__PURE__ */ new Map();
1651
1929
  githubBounties = {
1652
1930
  source: "bounty",
1653
1931
  async fetch(opts) {
1654
- const repos = opts?.slugs && opts.slugs.length > 0 ? opts.slugs : DEFAULT_BOUNTY_REPOS;
1655
- console.info(`[github-bounties] scanning ${repos.length} repos`);
1656
- const settled = await Promise.allSettled(repos.map(fetchRepoBounties));
1932
+ const allowlist = opts?.slugs && opts.slugs.length > 0 ? opts.slugs : DEFAULT_BOUNTY_REPOS;
1933
+ const [searched, listed] = await Promise.all([
1934
+ fetchSearchBounties().catch((e) => {
1935
+ console.warn("[github-bounties] search discovery failed:", e);
1936
+ return [];
1937
+ }),
1938
+ Promise.allSettled(allowlist.map(fetchRepoBounties)).then(
1939
+ (settled) => settled.flatMap((r) => r.status === "fulfilled" ? r.value : [])
1940
+ )
1941
+ ]);
1942
+ const seen = /* @__PURE__ */ new Set();
1943
+ const out = [];
1944
+ for (const j of [...searched, ...listed]) {
1945
+ if (!seen.has(j.id)) {
1946
+ seen.add(j.id);
1947
+ out.push(j);
1948
+ }
1949
+ }
1950
+ console.info(
1951
+ `[github-bounties] total: ${out.length} bounties (${searched.length} search + ${listed.length} allowlist, deduped)`
1952
+ );
1953
+ return out;
1954
+ }
1955
+ };
1956
+ }
1957
+ });
1958
+
1959
+ // ../../packages/core/src/feeds/opire.ts
1960
+ function tokenize3(text) {
1961
+ return text.toLowerCase().replace(/[^a-z0-9.\-+#]/g, " ").split(/\s+/).filter((w) => w.length > 1);
1962
+ }
1963
+ function effortFromAmount2(usd) {
1964
+ if (usd == null) return void 0;
1965
+ if (usd < 150) return "small";
1966
+ if (usd < 750) return "medium";
1967
+ return "large";
1968
+ }
1969
+ function priceToUSD(p) {
1970
+ if (!p || typeof p.value !== "number") return void 0;
1971
+ if (p.unit === "USD_CENT") return Math.round(p.value) / 100;
1972
+ if (p.unit === "USD") return p.value;
1973
+ return void 0;
1974
+ }
1975
+ function repoFullNameFromUrl(url) {
1976
+ const m = url?.match(/github\.com\/([^/]+)\/([^/]+)/i);
1977
+ return m ? `${m[1]}/${m[2].replace(/\.git$/, "")}` : void 0;
1978
+ }
1979
+ function issueNumberFromUrl(url) {
1980
+ const m = url?.match(/\/issues\/(\d+)/);
1981
+ return m ? parseInt(m[1], 10) : void 0;
1982
+ }
1983
+ var OPIRE_REWARDS_URL, MIN_USD, MAX_USD, MAX_OPIRE_BOUNTIES, REPO_META_CONCURRENCY2, opire;
1984
+ var init_opire = __esm({
1985
+ "../../packages/core/src/feeds/opire.ts"() {
1986
+ "use strict";
1987
+ init_vocabulary();
1988
+ init_bounty_gate();
1989
+ init_github_bounties();
1990
+ init_http();
1991
+ OPIRE_REWARDS_URL = "https://api.opire.dev/rewards";
1992
+ MIN_USD = 25;
1993
+ MAX_USD = 25e3;
1994
+ MAX_OPIRE_BOUNTIES = 100;
1995
+ REPO_META_CONCURRENCY2 = 15;
1996
+ opire = {
1997
+ source: "bounty",
1998
+ async fetch() {
1999
+ let rewards;
2000
+ try {
2001
+ const res = await fetchWithTimeout(OPIRE_REWARDS_URL, {
2002
+ headers: { Accept: "application/json", "User-Agent": "terminalhire" }
2003
+ });
2004
+ if (!res.ok) {
2005
+ console.warn(`[opire] HTTP ${res.status}`);
2006
+ return [];
2007
+ }
2008
+ const json = await res.json();
2009
+ rewards = Array.isArray(json) ? json : json?.data ?? json?.items ?? [];
2010
+ } catch (err) {
2011
+ console.warn("[opire] fetch failed \u2014", err);
2012
+ return [];
2013
+ }
2014
+ const candidates = [];
2015
+ for (const r of rewards) {
2016
+ if (r.platform !== "GitHub") continue;
2017
+ if (r.project && r.project.isPublic === false) continue;
2018
+ const repoFullName = repoFullNameFromUrl(r.project?.url ?? r.url);
2019
+ if (!repoFullName) continue;
2020
+ const amountUSD = priceToUSD(r.pendingPrice);
2021
+ if (amountUSD == null || amountUSD < MIN_USD || amountUSD > MAX_USD) continue;
2022
+ const title = (r.title ?? "").trim();
2023
+ if (title.length < 4) continue;
2024
+ const tags = normalize([...r.programmingLanguages ?? [], ...tokenize3(title)]);
2025
+ const bounty = {
2026
+ amountUSD,
2027
+ estimatedEffort: effortFromAmount2(amountUSD),
2028
+ bountySource: "opire",
2029
+ claimUrl: r.url,
2030
+ repoFullName
2031
+ };
2032
+ candidates.push({
2033
+ repoFullName,
2034
+ amountUSD,
2035
+ issueNumber: issueNumberFromUrl(r.url),
2036
+ bounty,
2037
+ job: {
2038
+ id: `bounty:opire:${r.id}`,
2039
+ source: "bounty",
2040
+ title,
2041
+ company: r.organization?.name ?? repoFullName.split("/")[0],
2042
+ url: r.url,
2043
+ remote: true,
2044
+ location: "Remote",
2045
+ tags,
2046
+ roleType: "freelance",
2047
+ postedAt: Number.isFinite(r.createdAt) ? new Date(r.createdAt).toISOString() : void 0,
2048
+ applyMode: "direct",
2049
+ bounty,
2050
+ raw: r
2051
+ }
2052
+ });
2053
+ }
2054
+ const distinctRepos = [...new Set(candidates.map((c) => c.repoFullName))];
2055
+ const meta = /* @__PURE__ */ new Map();
2056
+ for (let i = 0; i < distinctRepos.length; i += REPO_META_CONCURRENCY2) {
2057
+ const batch = distinctRepos.slice(i, i + REPO_META_CONCURRENCY2);
2058
+ const metas = await Promise.all(batch.map((name) => fetchRepoMeta2(name)));
2059
+ batch.forEach((name, k) => meta.set(name, metas[k]));
2060
+ }
2061
+ const gated = [];
2062
+ let dropped = 0;
2063
+ let ungated = 0;
2064
+ for (const c of candidates) {
2065
+ const m = meta.get(c.repoFullName);
2066
+ if (m) {
2067
+ if (!passesMaturityGate(m) || !passesAntiFarm(c.amountUSD, m.stargazers)) {
2068
+ dropped++;
2069
+ continue;
2070
+ }
2071
+ c.bounty.repoStars = m.stargazers;
2072
+ } else {
2073
+ ungated++;
2074
+ }
2075
+ gated.push(c);
2076
+ }
2077
+ const issueState = /* @__PURE__ */ new Map();
2078
+ for (let i = 0; i < gated.length; i += REPO_META_CONCURRENCY2) {
2079
+ const batch = gated.slice(i, i + REPO_META_CONCURRENCY2);
2080
+ const states = await Promise.all(
2081
+ batch.map(
2082
+ (c) => c.issueNumber != null ? fetchIssueState(c.repoFullName, c.issueNumber) : Promise.resolve(null)
2083
+ )
2084
+ );
2085
+ batch.forEach((c, k) => issueState.set(c.job.id, states[k]));
2086
+ }
2087
+ const jobs = [];
2088
+ let closed = 0;
2089
+ for (const c of gated) {
2090
+ if (jobs.length >= MAX_OPIRE_BOUNTIES) break;
2091
+ if (issueState.get(c.job.id) === "closed") {
2092
+ closed++;
2093
+ continue;
2094
+ }
2095
+ jobs.push(c.job);
2096
+ }
2097
+ console.info(
2098
+ `[opire] ${jobs.length} bounties (from ${rewards.length} rewards; ${dropped} repo-gated, ${closed} closed-issue, ${ungated} kept ungated)`
2099
+ );
2100
+ return jobs;
2101
+ }
2102
+ };
2103
+ }
2104
+ });
2105
+
2106
+ // ../../packages/core/src/feeds/workable.ts
2107
+ function locationStr(loc) {
2108
+ if (!loc) return "";
2109
+ return [loc.city, loc.country].filter(Boolean).join(", ");
2110
+ }
2111
+ function isRemote(j) {
2112
+ return j.remote === true || (j.workplace ?? "").toLowerCase() === "remote";
2113
+ }
2114
+ function extractTags7(j) {
2115
+ const body = [...j.department ?? [], locationStr(j.location)].filter(Boolean).join(" ");
2116
+ return extractSkillTags(j.title, body);
2117
+ }
2118
+ async function fetchAccount(account) {
2119
+ const url = `https://apply.workable.com/api/v3/accounts/${account}/jobs`;
2120
+ const out = [];
2121
+ let token;
2122
+ for (let page = 0; page < MAX_PAGES; page++) {
2123
+ let res;
2124
+ try {
2125
+ res = await fetchWithTimeout(url, {
2126
+ method: "POST",
2127
+ headers: { "Content-Type": "application/json", Accept: "application/json" },
2128
+ body: JSON.stringify(token ? { token } : {})
2129
+ });
2130
+ } catch (err) {
2131
+ console.warn(`[workable] ${account}: network error \u2014`, err);
2132
+ break;
2133
+ }
2134
+ if (!res.ok) {
2135
+ console.warn(`[workable] ${account}: HTTP ${res.status}`);
2136
+ break;
2137
+ }
2138
+ let data;
2139
+ try {
2140
+ data = await res.json();
2141
+ } catch (err) {
2142
+ console.warn(`[workable] ${account}: JSON parse error \u2014`, err);
2143
+ break;
2144
+ }
2145
+ const results = data.results ?? [];
2146
+ for (const j of results) {
2147
+ if (j.state && j.state !== "published") continue;
2148
+ out.push({
2149
+ id: `workable:${j.id}`,
2150
+ source: "workable",
2151
+ title: j.title,
2152
+ company: account,
2153
+ url: `https://apply.workable.com/${account}/j/${j.shortcode}/`,
2154
+ remote: isRemote(j),
2155
+ location: locationStr(j.location) || void 0,
2156
+ tags: extractTags7(j),
2157
+ roleType: "full_time",
2158
+ postedAt: j.published,
2159
+ applyMode: "direct",
2160
+ raw: j
2161
+ });
2162
+ }
2163
+ token = data.token;
2164
+ if (!token || results.length === 0) break;
2165
+ }
2166
+ if (out.length > 0) console.info(`[workable] ${account}: ${out.length} jobs`);
2167
+ return out;
2168
+ }
2169
+ var FALLBACK_ACCOUNTS, MAX_PAGES, workable;
2170
+ var init_workable = __esm({
2171
+ "../../packages/core/src/feeds/workable.ts"() {
2172
+ "use strict";
2173
+ init_vocabulary();
2174
+ init_http();
2175
+ FALLBACK_ACCOUNTS = ["zego", "workmotion"];
2176
+ MAX_PAGES = 5;
2177
+ workable = {
2178
+ source: "workable",
2179
+ async fetch(opts) {
2180
+ const accounts = opts?.slugs && opts.slugs.length > 0 ? opts.slugs : FALLBACK_ACCOUNTS;
2181
+ console.info(`[workable] fetching ${accounts.length} accounts: ${accounts.join(", ")}`);
2182
+ const results = await Promise.allSettled(accounts.map(fetchAccount));
1657
2183
  const jobs = [];
1658
2184
  let failures = 0;
1659
- for (const r of settled) {
1660
- if (r.status === "fulfilled") jobs.push(...r.value);
1661
- else {
2185
+ for (const r of results) {
2186
+ if (r.status === "fulfilled") {
2187
+ jobs.push(...r.value);
2188
+ } else {
1662
2189
  failures++;
1663
- console.warn("[github-bounties] repo fetch rejected:", r.reason);
2190
+ console.warn("[workable] account fetch rejected:", r.reason);
1664
2191
  }
1665
2192
  }
1666
- console.info(`[github-bounties] total: ${jobs.length} bounties, ${failures} repo failures`);
2193
+ console.info(`[workable] total: ${jobs.length} jobs, ${failures} account failures`);
1667
2194
  return jobs;
1668
2195
  }
1669
2196
  };
@@ -1672,7 +2199,57 @@ var init_github_bounties = __esm({
1672
2199
 
1673
2200
  // ../../packages/core/src/feeds/index.ts
1674
2201
  async function aggregateBounties(opts) {
1675
- return githubBounties.fetch({ slugs: opts?.repos });
2202
+ const [gh, op] = await Promise.all([
2203
+ githubBounties.fetch({ slugs: opts?.repos }),
2204
+ opire.fetch()
2205
+ ]);
2206
+ const allowlist = new Set(
2207
+ (opts?.repos && opts.repos.length > 0 ? opts.repos : DEFAULT_BOUNTY_REPOS).map(
2208
+ (r) => r.toLowerCase()
2209
+ )
2210
+ );
2211
+ const seen = /* @__PURE__ */ new Set();
2212
+ const perRepo = /* @__PURE__ */ new Map();
2213
+ const seenRepoTitles = /* @__PURE__ */ new Set();
2214
+ const out = [];
2215
+ for (const j of [...gh, ...op]) {
2216
+ const key = j.bounty?.claimUrl ?? j.url;
2217
+ if (seen.has(key)) continue;
2218
+ const repo = j.bounty?.repoFullName?.toLowerCase();
2219
+ if (repo) {
2220
+ if (isDenylistedRepo(repo)) continue;
2221
+ const titleKey = `${repo} ${normalizeBountyTitle(j.title)}`;
2222
+ if (seenRepoTitles.has(titleKey)) continue;
2223
+ const cap = allowlist.has(repo) ? MAX_BOUNTIES_PER_REPO : MAX_BOUNTIES_PER_DISCOVERED_REPO;
2224
+ const n = perRepo.get(repo) ?? 0;
2225
+ if (n >= cap) continue;
2226
+ perRepo.set(repo, n + 1);
2227
+ seenRepoTitles.add(titleKey);
2228
+ }
2229
+ seen.add(key);
2230
+ out.push(j);
2231
+ }
2232
+ const repos = [...new Set(out.map((j) => j.bounty?.repoFullName).filter((r) => !!r))];
2233
+ const refsByRepo = /* @__PURE__ */ new Map();
2234
+ const PR_REFS_CONCURRENCY = 15;
2235
+ for (let i = 0; i < repos.length; i += PR_REFS_CONCURRENCY) {
2236
+ const batch = repos.slice(i, i + PR_REFS_CONCURRENCY);
2237
+ const results = await Promise.all(batch.map((r) => fetchRepoOpenPRRefs(r)));
2238
+ batch.forEach((r, k) => refsByRepo.set(r, results[k]));
2239
+ }
2240
+ for (const j of out) {
2241
+ const num = bountyIssueNumber(j.bounty?.claimUrl);
2242
+ const refs = j.bounty?.repoFullName ? refsByRepo.get(j.bounty.repoFullName) : void 0;
2243
+ if (j.bounty && refs && num != null) j.bounty.competingOpenPRs = refs.get(num) ?? 0;
2244
+ }
2245
+ return out;
2246
+ }
2247
+ function bountyIssueNumber(url) {
2248
+ const m = url?.match(/\/issues\/(\d+)/);
2249
+ return m ? Number(m[1]) : void 0;
2250
+ }
2251
+ function normalizeBountyTitle(title) {
2252
+ return title.toLowerCase().replace(/#\d+\s*$/, "").replace(/[^a-z0-9]+/g, " ").trim();
1676
2253
  }
1677
2254
  function flattenTiers(t) {
1678
2255
  return [.../* @__PURE__ */ new Set([...t.bigco, ...t.scaleup, ...t.startup])];
@@ -1681,18 +2258,20 @@ async function aggregate(opts) {
1681
2258
  const ghSlugs = opts?.slugs?.["greenhouse"] ?? DEFAULT_GREENHOUSE_SLUGS;
1682
2259
  const ashbySlugs = opts?.slugs?.["ashby"] ?? DEFAULT_ASHBY_SLUGS;
1683
2260
  const leverSlugs = opts?.slugs?.["lever"] ?? DEFAULT_LEVER_SLUGS;
2261
+ const workableSlugs = opts?.slugs?.["workable"] ?? DEFAULT_WORKABLE_SLUGS;
1684
2262
  const limit = opts?.limit ?? 150;
1685
2263
  const settled = await Promise.allSettled([
1686
2264
  greenhouse.fetch({ slugs: ghSlugs, limit }),
1687
2265
  ashby.fetch({ slugs: ashbySlugs, limit }),
1688
2266
  lever.fetch({ slugs: leverSlugs, limit }),
2267
+ workable.fetch({ slugs: workableSlugs, limit }),
1689
2268
  himalayas.fetch({ limit }),
1690
2269
  wwr.fetch({ limit }),
1691
2270
  hn.fetch({ limit })
1692
2271
  ]);
1693
2272
  const seen = /* @__PURE__ */ new Set();
1694
2273
  const jobs = [];
1695
- const sourceNames = ["greenhouse", "ashby", "lever", "himalayas", "wwr", "hn"];
2274
+ const sourceNames = ["greenhouse", "ashby", "lever", "workable", "himalayas", "wwr", "hn"];
1696
2275
  for (let i = 0; i < settled.length; i++) {
1697
2276
  const result = settled[i];
1698
2277
  if (result.status === "rejected") {
@@ -1708,7 +2287,7 @@ async function aggregate(opts) {
1708
2287
  }
1709
2288
  if (opts?.includeBounties !== false) {
1710
2289
  try {
1711
- const bounties = await githubBounties.fetch({ slugs: opts?.slugs?.["bounty"], limit });
2290
+ const bounties = await aggregateBounties({ repos: opts?.slugs?.["bounty"] });
1712
2291
  for (const b of bounties) {
1713
2292
  if (!seen.has(b.id)) {
1714
2293
  seen.add(b.id);
@@ -1721,7 +2300,7 @@ async function aggregate(opts) {
1721
2300
  }
1722
2301
  return jobs;
1723
2302
  }
1724
- var FEEDS, GREENHOUSE_SLUGS_BY_TIER, ASHBY_SLUGS_BY_TIER, LEVER_SLUGS_BY_TIER, DEFAULT_GREENHOUSE_SLUGS, DEFAULT_ASHBY_SLUGS, DEFAULT_LEVER_SLUGS;
2303
+ var FEEDS, GREENHOUSE_SLUGS_BY_TIER, ASHBY_SLUGS_BY_TIER, LEVER_SLUGS_BY_TIER, DEFAULT_GREENHOUSE_SLUGS, DEFAULT_ASHBY_SLUGS, DEFAULT_LEVER_SLUGS, DEFAULT_WORKABLE_SLUGS;
1725
2304
  var init_feeds = __esm({
1726
2305
  "../../packages/core/src/feeds/index.ts"() {
1727
2306
  "use strict";
@@ -1732,8 +2311,11 @@ var init_feeds = __esm({
1732
2311
  init_wwr();
1733
2312
  init_hn();
1734
2313
  init_github_bounties();
2314
+ init_opire();
2315
+ init_workable();
2316
+ init_bounty_gate();
1735
2317
  init_bounty_gate();
1736
- FEEDS = [greenhouse, ashby, lever, himalayas, wwr, hn];
2318
+ FEEDS = [greenhouse, ashby, lever, workable, himalayas, wwr, hn];
1737
2319
  GREENHOUSE_SLUGS_BY_TIER = {
1738
2320
  bigco: [
1739
2321
  "stripe",
@@ -1839,6 +2421,7 @@ var init_feeds = __esm({
1839
2421
  DEFAULT_GREENHOUSE_SLUGS = flattenTiers(GREENHOUSE_SLUGS_BY_TIER);
1840
2422
  DEFAULT_ASHBY_SLUGS = flattenTiers(ASHBY_SLUGS_BY_TIER);
1841
2423
  DEFAULT_LEVER_SLUGS = flattenTiers(LEVER_SLUGS_BY_TIER);
2424
+ DEFAULT_WORKABLE_SLUGS = ["zego", "workmotion"];
1842
2425
  }
1843
2426
  });
1844
2427
 
@@ -1939,6 +2522,7 @@ __export(src_exports, {
1939
2522
  DEFAULT_BOUNTY_REPOS: () => DEFAULT_BOUNTY_REPOS,
1940
2523
  DEFAULT_GREENHOUSE_SLUGS: () => DEFAULT_GREENHOUSE_SLUGS,
1941
2524
  DEFAULT_LEVER_SLUGS: () => DEFAULT_LEVER_SLUGS,
2525
+ DEFAULT_WORKABLE_SLUGS: () => DEFAULT_WORKABLE_SLUGS,
1942
2526
  EXAMPLE_BUYER: () => EXAMPLE_BUYER,
1943
2527
  FEEDS: () => FEEDS,
1944
2528
  GRAPH: () => GRAPH,
@@ -1957,10 +2541,13 @@ __export(src_exports, {
1957
2541
  buildIndex: () => buildIndex,
1958
2542
  buildReason: () => buildReason,
1959
2543
  computeAcceptanceCredential: () => computeAcceptanceCredential,
2544
+ computeAcceptanceCredentialPublic: () => computeAcceptanceCredentialPublic,
1960
2545
  coreTagsFromTitle: () => coreTagsFromTitle,
2546
+ deriveResumeTrend: () => deriveResumeTrend,
1961
2547
  expandWeighted: () => expandWeighted,
1962
2548
  extractSkillTags: () => extractSkillTags,
1963
2549
  fetchGitHubProfile: () => fetchGitHubProfile,
2550
+ fetchRepoRecency: () => fetchRepoRecency,
1964
2551
  flattenTiers: () => flattenTiers,
1965
2552
  getBuyer: () => getBuyer,
1966
2553
  githubBounties: () => githubBounties,
@@ -1974,9 +2561,11 @@ __export(src_exports, {
1974
2561
  looksLikeEngRole: () => looksLikeEngRole,
1975
2562
  match: () => match,
1976
2563
  normalize: () => normalize,
2564
+ opire: () => opire,
1977
2565
  passesMaturityGate: () => passesMaturityGate,
1978
2566
  tokenize: () => tokenize,
1979
2567
  validateGraph: () => validateGraph,
2568
+ workable: () => workable,
1980
2569
  wwr: () => wwr
1981
2570
  });
1982
2571
  var init_src = __esm({