terminalhire 0.17.2 → 0.18.1
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 +67 -27
- package/dist/bin/jpi-chat-read.js +11 -2
- package/dist/bin/jpi-chat.js +11 -2
- package/dist/bin/jpi-contribute.js +45 -11
- package/dist/bin/jpi-devs.js +66 -26
- package/dist/bin/jpi-dispatch.js +145 -77
- package/dist/bin/jpi-inbox.js +11 -2
- package/dist/bin/jpi-intro.js +11 -2
- package/dist/bin/jpi-jobs.js +68 -30
- package/dist/bin/jpi-learn.js +11 -2
- package/dist/bin/jpi-login.js +33 -18
- package/dist/bin/jpi-profile.js +11 -2
- package/dist/bin/jpi-project.js +31 -16
- package/dist/bin/jpi-refresh.js +82 -36
- package/dist/bin/jpi-save.js +11 -2
- package/dist/bin/jpi-spinner.js +3 -0
- package/dist/bin/jpi-sync.js +11 -2
- package/dist/bin/jpi-trajectory.js +11 -2
- package/dist/bin/sanitize.js +35 -0
- package/dist/bin/spinner.js +3 -0
- package/dist/src/chat-client.js +2 -2
- package/dist/src/chat-keystore.js +2 -2
- package/dist/src/github-auth.js +2 -2
- package/dist/src/intro.js +11 -2
- package/dist/src/profile.js +2 -2
- package/dist/src/trajectory.js +11 -2
- package/package.json +1 -1
package/dist/bin/jpi-bounties.js
CHANGED
|
@@ -921,6 +921,7 @@ async function fetchRepoMeta(owner, name, token, cache) {
|
|
|
921
921
|
stars: r.stargazers_count ?? 0,
|
|
922
922
|
archived: !!r.archived,
|
|
923
923
|
fork: !!r.fork,
|
|
924
|
+
private: !!r.private,
|
|
924
925
|
language: r.language ?? null,
|
|
925
926
|
topics: r.topics ?? [],
|
|
926
927
|
contributors
|
|
@@ -953,7 +954,7 @@ async function computeAcceptanceFromSearch(login, token, ownedOrgs, cache, gates
|
|
|
953
954
|
const loginLc = login.toLowerCase();
|
|
954
955
|
let items;
|
|
955
956
|
try {
|
|
956
|
-
const q = encodeURIComponent(`type:pr is:merged author:${login} -user:${login} sort:updated`);
|
|
957
|
+
const q = encodeURIComponent(`type:pr is:merged is:public author:${login} -user:${login} sort:updated`);
|
|
957
958
|
const res = await ghFetch(
|
|
958
959
|
`/search/issues?q=${q}&per_page=${CANDIDATE_PR_PAGE}`,
|
|
959
960
|
token
|
|
@@ -966,6 +967,7 @@ async function computeAcceptanceFromSearch(login, token, ownedOrgs, cache, gates
|
|
|
966
967
|
}
|
|
967
968
|
const byDomain = {};
|
|
968
969
|
let qualifyingTotal = 0;
|
|
970
|
+
const qualifyingPRs = [];
|
|
969
971
|
for (const item of items) {
|
|
970
972
|
const repo = parseRepoUrl(item.repository_url);
|
|
971
973
|
if (!repo) continue;
|
|
@@ -975,13 +977,22 @@ async function computeAcceptanceFromSearch(login, token, ownedOrgs, cache, gates
|
|
|
975
977
|
if (isTrivialPRTitle(item.title)) continue;
|
|
976
978
|
const meta = await fetchRepoMeta(repo.owner, repo.name, token, cache);
|
|
977
979
|
if (!meta) continue;
|
|
980
|
+
if (meta.private) continue;
|
|
978
981
|
if (meta.archived || meta.fork) continue;
|
|
979
982
|
if (meta.stars < gates.minStars) continue;
|
|
980
983
|
if (meta.contributors !== void 0 && meta.contributors < gates.minContributors) continue;
|
|
981
984
|
qualifyingTotal += 1;
|
|
982
985
|
const mergedAt = item.pull_request?.merged_at ?? item.closed_at ?? item.created_at;
|
|
983
986
|
const rawDomains = [meta.language ?? "", ...meta.topics].filter(Boolean);
|
|
984
|
-
|
|
987
|
+
const domainTags = [...new Set(normalize(rawDomains))];
|
|
988
|
+
qualifyingPRs.push({
|
|
989
|
+
url: item.html_url,
|
|
990
|
+
title: item.title,
|
|
991
|
+
repo: `${repo.owner}/${repo.name}`,
|
|
992
|
+
domains: domainTags,
|
|
993
|
+
mergedAt
|
|
994
|
+
});
|
|
995
|
+
for (const d of domainTags) {
|
|
985
996
|
const b = byDomain[d] ?? (byDomain[d] = { mergedPRs: 0, distinctOrgs: 0, lastMergedAt: mergedAt, orgs: /* @__PURE__ */ new Set() });
|
|
986
997
|
b.mergedPRs += 1;
|
|
987
998
|
b.orgs.add(ownerLc);
|
|
@@ -996,7 +1007,7 @@ async function computeAcceptanceFromSearch(login, token, ownedOrgs, cache, gates
|
|
|
996
1007
|
lastMergedAt: b.lastMergedAt
|
|
997
1008
|
};
|
|
998
1009
|
}
|
|
999
|
-
return { status: "ok", byDomain: finalDomains, qualifyingTotal, computedAt };
|
|
1010
|
+
return { status: "ok", byDomain: finalDomains, qualifyingTotal, qualifyingPRs, computedAt };
|
|
1000
1011
|
}
|
|
1001
1012
|
async function computeAcceptanceCredential(login, token, cache = /* @__PURE__ */ new Map()) {
|
|
1002
1013
|
if (!token) return emptyCredential("no-token");
|
|
@@ -1807,6 +1818,19 @@ var init_concurrency = __esm({
|
|
|
1807
1818
|
}
|
|
1808
1819
|
});
|
|
1809
1820
|
|
|
1821
|
+
// ../../packages/core/src/feeds/effort.ts
|
|
1822
|
+
function effortFromAmount(amount) {
|
|
1823
|
+
if (amount == null) return void 0;
|
|
1824
|
+
if (amount <= 500) return "small";
|
|
1825
|
+
if (amount <= 2e3) return "medium";
|
|
1826
|
+
return "large";
|
|
1827
|
+
}
|
|
1828
|
+
var init_effort = __esm({
|
|
1829
|
+
"../../packages/core/src/feeds/effort.ts"() {
|
|
1830
|
+
"use strict";
|
|
1831
|
+
}
|
|
1832
|
+
});
|
|
1833
|
+
|
|
1810
1834
|
// ../../packages/core/src/feeds/github-bounties.ts
|
|
1811
1835
|
function authHeaders() {
|
|
1812
1836
|
const token = process.env["GITHUB_TOKEN"] ?? process.env["GH_TOKEN"];
|
|
@@ -1829,12 +1853,6 @@ function parseAmountUSD(text) {
|
|
|
1829
1853
|
if (!Number.isFinite(n) || n <= 0 || n > 1e6) return void 0;
|
|
1830
1854
|
return Math.round(n);
|
|
1831
1855
|
}
|
|
1832
|
-
function effortFromAmount(amount) {
|
|
1833
|
-
if (amount == null) return void 0;
|
|
1834
|
-
if (amount <= 500) return "small";
|
|
1835
|
-
if (amount <= 2e3) return "medium";
|
|
1836
|
-
return "large";
|
|
1837
|
-
}
|
|
1838
1856
|
function labelNames(issue) {
|
|
1839
1857
|
return (issue.labels ?? []).map((l) => typeof l === "string" ? l : l.name ?? "").filter(Boolean);
|
|
1840
1858
|
}
|
|
@@ -2082,6 +2100,7 @@ var init_github_bounties = __esm({
|
|
|
2082
2100
|
init_bounty_gate();
|
|
2083
2101
|
init_http();
|
|
2084
2102
|
init_concurrency();
|
|
2103
|
+
init_effort();
|
|
2085
2104
|
GITHUB_API = "https://api.github.com";
|
|
2086
2105
|
BOUNTY_LABEL_RE = /bounty|reward|funded|💎|💰/i;
|
|
2087
2106
|
SEARCH_QUERIES = [
|
|
@@ -2133,12 +2152,6 @@ var init_github_bounties = __esm({
|
|
|
2133
2152
|
function tokenize3(text) {
|
|
2134
2153
|
return text.toLowerCase().replace(/[^a-z0-9.\-+#]/g, " ").split(/\s+/).filter((w) => w.length > 1);
|
|
2135
2154
|
}
|
|
2136
|
-
function effortFromAmount2(usd) {
|
|
2137
|
-
if (usd == null) return void 0;
|
|
2138
|
-
if (usd < 150) return "small";
|
|
2139
|
-
if (usd < 750) return "medium";
|
|
2140
|
-
return "large";
|
|
2141
|
-
}
|
|
2142
2155
|
function priceToUSD(p) {
|
|
2143
2156
|
if (!p || typeof p.value !== "number") return void 0;
|
|
2144
2157
|
if (p.unit === "USD_CENT") return Math.round(p.value) / 100;
|
|
@@ -2161,6 +2174,7 @@ var init_opire = __esm({
|
|
|
2161
2174
|
init_bounty_gate();
|
|
2162
2175
|
init_github_bounties();
|
|
2163
2176
|
init_http();
|
|
2177
|
+
init_effort();
|
|
2164
2178
|
OPIRE_REWARDS_URL = "https://api.opire.dev/rewards";
|
|
2165
2179
|
MIN_USD = 25;
|
|
2166
2180
|
MAX_USD = 25e3;
|
|
@@ -2197,7 +2211,7 @@ var init_opire = __esm({
|
|
|
2197
2211
|
const tags = normalize([...r.programmingLanguages ?? [], ...tokenize3(title)]);
|
|
2198
2212
|
const bounty = {
|
|
2199
2213
|
amountUSD,
|
|
2200
|
-
estimatedEffort:
|
|
2214
|
+
estimatedEffort: effortFromAmount(amountUSD),
|
|
2201
2215
|
bountySource: "opire",
|
|
2202
2216
|
claimUrl: r.url,
|
|
2203
2217
|
repoFullName
|
|
@@ -6544,6 +6558,7 @@ __export(src_exports, {
|
|
|
6544
6558
|
isBounty: () => isBounty,
|
|
6545
6559
|
isContribution: () => isContribution,
|
|
6546
6560
|
isOverIntroLimit: () => isOverIntroLimit,
|
|
6561
|
+
isTrivialPRTitle: () => isTrivialPRTitle,
|
|
6547
6562
|
joinLabels: () => joinLabels,
|
|
6548
6563
|
labelFor: () => labelFor,
|
|
6549
6564
|
lever: () => lever,
|
|
@@ -6600,9 +6615,9 @@ var init_keytar = __esm({
|
|
|
6600
6615
|
}
|
|
6601
6616
|
});
|
|
6602
6617
|
|
|
6603
|
-
// node-file:/
|
|
6618
|
+
// node-file:/Users/ericgang/job-placement-inline/node_modules/keytar/build/Release/keytar.node
|
|
6604
6619
|
var require_keytar = __commonJS({
|
|
6605
|
-
"node-file:/
|
|
6620
|
+
"node-file:/Users/ericgang/job-placement-inline/node_modules/keytar/build/Release/keytar.node"(exports, module) {
|
|
6606
6621
|
"use strict";
|
|
6607
6622
|
init_keytar();
|
|
6608
6623
|
try {
|
|
@@ -6928,6 +6943,37 @@ function updateIndexCache(patch) {
|
|
|
6928
6943
|
return entry;
|
|
6929
6944
|
}
|
|
6930
6945
|
|
|
6946
|
+
// bin/sanitize.js
|
|
6947
|
+
var CONTROL_CHARS = /[\x00-\x1f\x7f-\x9f]/g;
|
|
6948
|
+
function sanitizeText(s) {
|
|
6949
|
+
if (s == null) return "";
|
|
6950
|
+
return String(s).replace(CONTROL_CHARS, "");
|
|
6951
|
+
}
|
|
6952
|
+
function safeHttpUrl(url) {
|
|
6953
|
+
if (url == null) return null;
|
|
6954
|
+
const raw = String(url);
|
|
6955
|
+
CONTROL_CHARS.lastIndex = 0;
|
|
6956
|
+
if (CONTROL_CHARS.test(raw)) return null;
|
|
6957
|
+
let parsed;
|
|
6958
|
+
try {
|
|
6959
|
+
parsed = new URL(raw);
|
|
6960
|
+
} catch {
|
|
6961
|
+
return null;
|
|
6962
|
+
}
|
|
6963
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") return null;
|
|
6964
|
+
return parsed.href;
|
|
6965
|
+
}
|
|
6966
|
+
function linkTitle(title, url) {
|
|
6967
|
+
const safeTitle = sanitizeText(title);
|
|
6968
|
+
const href = safeHttpUrl(url);
|
|
6969
|
+
const isTTY = process.stdout.isTTY;
|
|
6970
|
+
const noColor = process.env["NO_COLOR"] !== void 0;
|
|
6971
|
+
if (isTTY && !noColor && href) {
|
|
6972
|
+
return `\x1B]8;;${href}\x1B\\${safeTitle}\x1B]8;;\x1B\\`;
|
|
6973
|
+
}
|
|
6974
|
+
return href ? `${safeTitle} (${href})` : safeTitle;
|
|
6975
|
+
}
|
|
6976
|
+
|
|
6931
6977
|
// bin/jpi-bounties.js
|
|
6932
6978
|
var TERMINALHIRE_DIR3 = process.env.TERMINALHIRE_DIR || join4(homedir3(), ".terminalhire");
|
|
6933
6979
|
var INDEX_CACHE_FILE2 = join4(TERMINALHIRE_DIR3, "index-cache.json");
|
|
@@ -6974,12 +7020,6 @@ function formatAmount(b) {
|
|
|
6974
7020
|
return b.amountUSD != null ? "$" + b.amountUSD.toLocaleString() : "$\u2014";
|
|
6975
7021
|
}
|
|
6976
7022
|
var EFFORT_LABEL = { small: "small (~\xBD day)", medium: "medium (~1 day)", large: "large (multi-day)" };
|
|
6977
|
-
function linkTitle(title, url) {
|
|
6978
|
-
const isTTY = process.stdout.isTTY;
|
|
6979
|
-
const noColor = process.env["NO_COLOR"] !== void 0;
|
|
6980
|
-
if (isTTY && !noColor && url) return `\x1B]8;;${url}\x1B\\${title}\x1B]8;;\x1B\\`;
|
|
6981
|
-
return url ? `${title} (${url})` : title;
|
|
6982
|
-
}
|
|
6983
7023
|
function printBounty(i, job, score, reason, matchedTags) {
|
|
6984
7024
|
const b = job.bounty ?? {};
|
|
6985
7025
|
const stars = b.repoStars != null ? ` \xB7 ${b.repoStars}\u2605` : "";
|
|
@@ -6990,11 +7030,11 @@ function printBounty(i, job, score, reason, matchedTags) {
|
|
|
6990
7030
|
const ref = opportunityShortToken(job.id);
|
|
6991
7031
|
console.log(`
|
|
6992
7032
|
${i + 1}. ${linkTitle(job.title, job.url)} [${ref}]`);
|
|
6993
|
-
console.log(` ${formatAmount(b)}${effort} \xB7 ${b.repoFullName ?? job.company}${stars}${scoreStr}${contend}`);
|
|
7033
|
+
console.log(` ${formatAmount(b)}${effort} \xB7 ${sanitizeText(b.repoFullName ?? job.company)}${stars}${scoreStr}${contend}`);
|
|
6994
7034
|
if (reason) console.log(` ${reason}`);
|
|
6995
7035
|
if (matchedTags && matchedTags.length) console.log(` Tags matched: ${matchedTags.slice(0, 5).join(", ")}`);
|
|
6996
7036
|
console.log(` id: ${job.id}`);
|
|
6997
|
-
console.log(` Claim: ${b.claimUrl ?? job.url}`);
|
|
7037
|
+
console.log(` Claim: ${sanitizeText(b.claimUrl ?? job.url)}`);
|
|
6998
7038
|
console.log(` \u2192 terminalhire claim ${ref}`);
|
|
6999
7039
|
}
|
|
7000
7040
|
async function run() {
|
|
@@ -7050,7 +7090,7 @@ Enter a number to open a bounty's claim page, or press Enter to exit: `);
|
|
|
7050
7090
|
console.log(
|
|
7051
7091
|
`
|
|
7052
7092
|
Open this to claim/work the bounty (you go straight to the source \u2014 we never touch payment):
|
|
7053
|
-
${chosen.bounty?.claimUrl ?? chosen.url}`
|
|
7093
|
+
${sanitizeText(chosen.bounty?.claimUrl ?? chosen.url)}`
|
|
7054
7094
|
);
|
|
7055
7095
|
} catch (err) {
|
|
7056
7096
|
console.error("terminalhire bounties error:", err.message ?? err);
|
|
@@ -559,6 +559,13 @@ var init_concurrency = __esm({
|
|
|
559
559
|
}
|
|
560
560
|
});
|
|
561
561
|
|
|
562
|
+
// ../../packages/core/src/feeds/effort.ts
|
|
563
|
+
var init_effort = __esm({
|
|
564
|
+
"../../packages/core/src/feeds/effort.ts"() {
|
|
565
|
+
"use strict";
|
|
566
|
+
}
|
|
567
|
+
});
|
|
568
|
+
|
|
562
569
|
// ../../packages/core/src/feeds/github-bounties.ts
|
|
563
570
|
var init_github_bounties = __esm({
|
|
564
571
|
"../../packages/core/src/feeds/github-bounties.ts"() {
|
|
@@ -568,6 +575,7 @@ var init_github_bounties = __esm({
|
|
|
568
575
|
init_bounty_gate();
|
|
569
576
|
init_http();
|
|
570
577
|
init_concurrency();
|
|
578
|
+
init_effort();
|
|
571
579
|
}
|
|
572
580
|
});
|
|
573
581
|
|
|
@@ -579,6 +587,7 @@ var init_opire = __esm({
|
|
|
579
587
|
init_bounty_gate();
|
|
580
588
|
init_github_bounties();
|
|
581
589
|
init_http();
|
|
590
|
+
init_effort();
|
|
582
591
|
}
|
|
583
592
|
});
|
|
584
593
|
|
|
@@ -3925,9 +3934,9 @@ var init_keytar = __esm({
|
|
|
3925
3934
|
}
|
|
3926
3935
|
});
|
|
3927
3936
|
|
|
3928
|
-
// node-file:/
|
|
3937
|
+
// node-file:/Users/ericgang/job-placement-inline/node_modules/keytar/build/Release/keytar.node
|
|
3929
3938
|
var require_keytar = __commonJS({
|
|
3930
|
-
"node-file:/
|
|
3939
|
+
"node-file:/Users/ericgang/job-placement-inline/node_modules/keytar/build/Release/keytar.node"(exports, module) {
|
|
3931
3940
|
"use strict";
|
|
3932
3941
|
init_keytar();
|
|
3933
3942
|
try {
|
package/dist/bin/jpi-chat.js
CHANGED
|
@@ -576,6 +576,13 @@ var init_concurrency = __esm({
|
|
|
576
576
|
}
|
|
577
577
|
});
|
|
578
578
|
|
|
579
|
+
// ../../packages/core/src/feeds/effort.ts
|
|
580
|
+
var init_effort = __esm({
|
|
581
|
+
"../../packages/core/src/feeds/effort.ts"() {
|
|
582
|
+
"use strict";
|
|
583
|
+
}
|
|
584
|
+
});
|
|
585
|
+
|
|
579
586
|
// ../../packages/core/src/feeds/github-bounties.ts
|
|
580
587
|
var init_github_bounties = __esm({
|
|
581
588
|
"../../packages/core/src/feeds/github-bounties.ts"() {
|
|
@@ -585,6 +592,7 @@ var init_github_bounties = __esm({
|
|
|
585
592
|
init_bounty_gate();
|
|
586
593
|
init_http();
|
|
587
594
|
init_concurrency();
|
|
595
|
+
init_effort();
|
|
588
596
|
}
|
|
589
597
|
});
|
|
590
598
|
|
|
@@ -596,6 +604,7 @@ var init_opire = __esm({
|
|
|
596
604
|
init_bounty_gate();
|
|
597
605
|
init_github_bounties();
|
|
598
606
|
init_http();
|
|
607
|
+
init_effort();
|
|
599
608
|
}
|
|
600
609
|
});
|
|
601
610
|
|
|
@@ -3953,9 +3962,9 @@ var init_keytar = __esm({
|
|
|
3953
3962
|
}
|
|
3954
3963
|
});
|
|
3955
3964
|
|
|
3956
|
-
// node-file:/
|
|
3965
|
+
// node-file:/Users/ericgang/job-placement-inline/node_modules/keytar/build/Release/keytar.node
|
|
3957
3966
|
var require_keytar = __commonJS({
|
|
3958
|
-
"node-file:/
|
|
3967
|
+
"node-file:/Users/ericgang/job-placement-inline/node_modules/keytar/build/Release/keytar.node"(exports, module) {
|
|
3959
3968
|
"use strict";
|
|
3960
3969
|
init_keytar();
|
|
3961
3970
|
try {
|
|
@@ -894,6 +894,13 @@ var init_concurrency = __esm({
|
|
|
894
894
|
}
|
|
895
895
|
});
|
|
896
896
|
|
|
897
|
+
// ../../packages/core/src/feeds/effort.ts
|
|
898
|
+
var init_effort = __esm({
|
|
899
|
+
"../../packages/core/src/feeds/effort.ts"() {
|
|
900
|
+
"use strict";
|
|
901
|
+
}
|
|
902
|
+
});
|
|
903
|
+
|
|
897
904
|
// ../../packages/core/src/feeds/github-bounties.ts
|
|
898
905
|
var init_github_bounties = __esm({
|
|
899
906
|
"../../packages/core/src/feeds/github-bounties.ts"() {
|
|
@@ -903,6 +910,7 @@ var init_github_bounties = __esm({
|
|
|
903
910
|
init_bounty_gate();
|
|
904
911
|
init_http();
|
|
905
912
|
init_concurrency();
|
|
913
|
+
init_effort();
|
|
906
914
|
}
|
|
907
915
|
});
|
|
908
916
|
|
|
@@ -914,6 +922,7 @@ var init_opire = __esm({
|
|
|
914
922
|
init_bounty_gate();
|
|
915
923
|
init_github_bounties();
|
|
916
924
|
init_http();
|
|
925
|
+
init_effort();
|
|
917
926
|
}
|
|
918
927
|
});
|
|
919
928
|
|
|
@@ -1242,9 +1251,9 @@ var init_keytar = __esm({
|
|
|
1242
1251
|
}
|
|
1243
1252
|
});
|
|
1244
1253
|
|
|
1245
|
-
// node-file:/
|
|
1254
|
+
// node-file:/Users/ericgang/job-placement-inline/node_modules/keytar/build/Release/keytar.node
|
|
1246
1255
|
var require_keytar = __commonJS({
|
|
1247
|
-
"node-file:/
|
|
1256
|
+
"node-file:/Users/ericgang/job-placement-inline/node_modules/keytar/build/Release/keytar.node"(exports, module) {
|
|
1248
1257
|
"use strict";
|
|
1249
1258
|
init_keytar();
|
|
1250
1259
|
try {
|
|
@@ -1608,6 +1617,37 @@ function isContributeEnabled() {
|
|
|
1608
1617
|
return readConfig().contributeEnabled === true;
|
|
1609
1618
|
}
|
|
1610
1619
|
|
|
1620
|
+
// bin/sanitize.js
|
|
1621
|
+
var CONTROL_CHARS = /[\x00-\x1f\x7f-\x9f]/g;
|
|
1622
|
+
function sanitizeText(s) {
|
|
1623
|
+
if (s == null) return "";
|
|
1624
|
+
return String(s).replace(CONTROL_CHARS, "");
|
|
1625
|
+
}
|
|
1626
|
+
function safeHttpUrl(url) {
|
|
1627
|
+
if (url == null) return null;
|
|
1628
|
+
const raw = String(url);
|
|
1629
|
+
CONTROL_CHARS.lastIndex = 0;
|
|
1630
|
+
if (CONTROL_CHARS.test(raw)) return null;
|
|
1631
|
+
let parsed;
|
|
1632
|
+
try {
|
|
1633
|
+
parsed = new URL(raw);
|
|
1634
|
+
} catch {
|
|
1635
|
+
return null;
|
|
1636
|
+
}
|
|
1637
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") return null;
|
|
1638
|
+
return parsed.href;
|
|
1639
|
+
}
|
|
1640
|
+
function linkTitle(title, url) {
|
|
1641
|
+
const safeTitle = sanitizeText(title);
|
|
1642
|
+
const href = safeHttpUrl(url);
|
|
1643
|
+
const isTTY = process.stdout.isTTY;
|
|
1644
|
+
const noColor = process.env["NO_COLOR"] !== void 0;
|
|
1645
|
+
if (isTTY && !noColor && href) {
|
|
1646
|
+
return `\x1B]8;;${href}\x1B\\${safeTitle}\x1B]8;;\x1B\\`;
|
|
1647
|
+
}
|
|
1648
|
+
return href ? `${safeTitle} (${href})` : safeTitle;
|
|
1649
|
+
}
|
|
1650
|
+
|
|
1611
1651
|
// bin/jpi-contribute.js
|
|
1612
1652
|
var TERMINALHIRE_DIR4 = process.env.TERMINALHIRE_DIR || join5(homedir4(), ".terminalhire");
|
|
1613
1653
|
var INDEX_CACHE_FILE2 = join5(TERMINALHIRE_DIR4, "index-cache.json");
|
|
@@ -1709,19 +1749,13 @@ function displayLanguage(job) {
|
|
|
1709
1749
|
const tag = (job.tags ?? []).find((t) => LANGUAGES.has(String(t).toLowerCase()));
|
|
1710
1750
|
return tag ?? "\u2014";
|
|
1711
1751
|
}
|
|
1712
|
-
function linkTitle(title, url) {
|
|
1713
|
-
const isTTY = process.stdout.isTTY;
|
|
1714
|
-
const noColor = process.env["NO_COLOR"] !== void 0;
|
|
1715
|
-
if (isTTY && !noColor && url) return `\x1B]8;;${url}\x1B\\${title}\x1B]8;;\x1B\\`;
|
|
1716
|
-
return url ? `${title} (${url})` : title;
|
|
1717
|
-
}
|
|
1718
1752
|
function renderRow(i, result) {
|
|
1719
1753
|
const job = result.job;
|
|
1720
1754
|
const c = job.contribution ?? {};
|
|
1721
|
-
const repo = c.repoFullName ?? job.company ?? "";
|
|
1755
|
+
const repo = sanitizeText(c.repoFullName ?? job.company ?? "");
|
|
1722
1756
|
const num = c.issueNumber != null ? `#${c.issueNumber}` : "";
|
|
1723
|
-
const label = c.labels && c.labels.length ? c.labels[0] : "\u2014";
|
|
1724
|
-
const lang = displayLanguage(job);
|
|
1757
|
+
const label = c.labels && c.labels.length ? sanitizeText(c.labels[0]) : "\u2014";
|
|
1758
|
+
const lang = sanitizeText(displayLanguage(job));
|
|
1725
1759
|
const scorePct = `match ${Math.round((result.score ?? 0) * 100)}%`;
|
|
1726
1760
|
const ref = opportunityShortToken(job.id);
|
|
1727
1761
|
const line1 = `${i + 1}. ${linkTitle(job.title, c.issueUrl ?? job.url)} [${ref}]`;
|
package/dist/bin/jpi-devs.js
CHANGED
|
@@ -921,6 +921,7 @@ async function fetchRepoMeta(owner, name, token, cache) {
|
|
|
921
921
|
stars: r.stargazers_count ?? 0,
|
|
922
922
|
archived: !!r.archived,
|
|
923
923
|
fork: !!r.fork,
|
|
924
|
+
private: !!r.private,
|
|
924
925
|
language: r.language ?? null,
|
|
925
926
|
topics: r.topics ?? [],
|
|
926
927
|
contributors
|
|
@@ -953,7 +954,7 @@ async function computeAcceptanceFromSearch(login, token, ownedOrgs, cache, gates
|
|
|
953
954
|
const loginLc = login.toLowerCase();
|
|
954
955
|
let items;
|
|
955
956
|
try {
|
|
956
|
-
const q = encodeURIComponent(`type:pr is:merged author:${login} -user:${login} sort:updated`);
|
|
957
|
+
const q = encodeURIComponent(`type:pr is:merged is:public author:${login} -user:${login} sort:updated`);
|
|
957
958
|
const res = await ghFetch(
|
|
958
959
|
`/search/issues?q=${q}&per_page=${CANDIDATE_PR_PAGE}`,
|
|
959
960
|
token
|
|
@@ -966,6 +967,7 @@ async function computeAcceptanceFromSearch(login, token, ownedOrgs, cache, gates
|
|
|
966
967
|
}
|
|
967
968
|
const byDomain = {};
|
|
968
969
|
let qualifyingTotal = 0;
|
|
970
|
+
const qualifyingPRs = [];
|
|
969
971
|
for (const item of items) {
|
|
970
972
|
const repo = parseRepoUrl(item.repository_url);
|
|
971
973
|
if (!repo) continue;
|
|
@@ -975,13 +977,22 @@ async function computeAcceptanceFromSearch(login, token, ownedOrgs, cache, gates
|
|
|
975
977
|
if (isTrivialPRTitle(item.title)) continue;
|
|
976
978
|
const meta = await fetchRepoMeta(repo.owner, repo.name, token, cache);
|
|
977
979
|
if (!meta) continue;
|
|
980
|
+
if (meta.private) continue;
|
|
978
981
|
if (meta.archived || meta.fork) continue;
|
|
979
982
|
if (meta.stars < gates.minStars) continue;
|
|
980
983
|
if (meta.contributors !== void 0 && meta.contributors < gates.minContributors) continue;
|
|
981
984
|
qualifyingTotal += 1;
|
|
982
985
|
const mergedAt = item.pull_request?.merged_at ?? item.closed_at ?? item.created_at;
|
|
983
986
|
const rawDomains = [meta.language ?? "", ...meta.topics].filter(Boolean);
|
|
984
|
-
|
|
987
|
+
const domainTags = [...new Set(normalize(rawDomains))];
|
|
988
|
+
qualifyingPRs.push({
|
|
989
|
+
url: item.html_url,
|
|
990
|
+
title: item.title,
|
|
991
|
+
repo: `${repo.owner}/${repo.name}`,
|
|
992
|
+
domains: domainTags,
|
|
993
|
+
mergedAt
|
|
994
|
+
});
|
|
995
|
+
for (const d of domainTags) {
|
|
985
996
|
const b = byDomain[d] ?? (byDomain[d] = { mergedPRs: 0, distinctOrgs: 0, lastMergedAt: mergedAt, orgs: /* @__PURE__ */ new Set() });
|
|
986
997
|
b.mergedPRs += 1;
|
|
987
998
|
b.orgs.add(ownerLc);
|
|
@@ -996,7 +1007,7 @@ async function computeAcceptanceFromSearch(login, token, ownedOrgs, cache, gates
|
|
|
996
1007
|
lastMergedAt: b.lastMergedAt
|
|
997
1008
|
};
|
|
998
1009
|
}
|
|
999
|
-
return { status: "ok", byDomain: finalDomains, qualifyingTotal, computedAt };
|
|
1010
|
+
return { status: "ok", byDomain: finalDomains, qualifyingTotal, qualifyingPRs, computedAt };
|
|
1000
1011
|
}
|
|
1001
1012
|
async function computeAcceptanceCredential(login, token, cache = /* @__PURE__ */ new Map()) {
|
|
1002
1013
|
if (!token) return emptyCredential("no-token");
|
|
@@ -1807,6 +1818,19 @@ var init_concurrency = __esm({
|
|
|
1807
1818
|
}
|
|
1808
1819
|
});
|
|
1809
1820
|
|
|
1821
|
+
// ../../packages/core/src/feeds/effort.ts
|
|
1822
|
+
function effortFromAmount(amount) {
|
|
1823
|
+
if (amount == null) return void 0;
|
|
1824
|
+
if (amount <= 500) return "small";
|
|
1825
|
+
if (amount <= 2e3) return "medium";
|
|
1826
|
+
return "large";
|
|
1827
|
+
}
|
|
1828
|
+
var init_effort = __esm({
|
|
1829
|
+
"../../packages/core/src/feeds/effort.ts"() {
|
|
1830
|
+
"use strict";
|
|
1831
|
+
}
|
|
1832
|
+
});
|
|
1833
|
+
|
|
1810
1834
|
// ../../packages/core/src/feeds/github-bounties.ts
|
|
1811
1835
|
function authHeaders() {
|
|
1812
1836
|
const token = process.env["GITHUB_TOKEN"] ?? process.env["GH_TOKEN"];
|
|
@@ -1829,12 +1853,6 @@ function parseAmountUSD(text) {
|
|
|
1829
1853
|
if (!Number.isFinite(n) || n <= 0 || n > 1e6) return void 0;
|
|
1830
1854
|
return Math.round(n);
|
|
1831
1855
|
}
|
|
1832
|
-
function effortFromAmount(amount) {
|
|
1833
|
-
if (amount == null) return void 0;
|
|
1834
|
-
if (amount <= 500) return "small";
|
|
1835
|
-
if (amount <= 2e3) return "medium";
|
|
1836
|
-
return "large";
|
|
1837
|
-
}
|
|
1838
1856
|
function labelNames(issue) {
|
|
1839
1857
|
return (issue.labels ?? []).map((l) => typeof l === "string" ? l : l.name ?? "").filter(Boolean);
|
|
1840
1858
|
}
|
|
@@ -2082,6 +2100,7 @@ var init_github_bounties = __esm({
|
|
|
2082
2100
|
init_bounty_gate();
|
|
2083
2101
|
init_http();
|
|
2084
2102
|
init_concurrency();
|
|
2103
|
+
init_effort();
|
|
2085
2104
|
GITHUB_API = "https://api.github.com";
|
|
2086
2105
|
BOUNTY_LABEL_RE = /bounty|reward|funded|💎|💰/i;
|
|
2087
2106
|
SEARCH_QUERIES = [
|
|
@@ -2133,12 +2152,6 @@ var init_github_bounties = __esm({
|
|
|
2133
2152
|
function tokenize3(text) {
|
|
2134
2153
|
return text.toLowerCase().replace(/[^a-z0-9.\-+#]/g, " ").split(/\s+/).filter((w) => w.length > 1);
|
|
2135
2154
|
}
|
|
2136
|
-
function effortFromAmount2(usd) {
|
|
2137
|
-
if (usd == null) return void 0;
|
|
2138
|
-
if (usd < 150) return "small";
|
|
2139
|
-
if (usd < 750) return "medium";
|
|
2140
|
-
return "large";
|
|
2141
|
-
}
|
|
2142
2155
|
function priceToUSD(p) {
|
|
2143
2156
|
if (!p || typeof p.value !== "number") return void 0;
|
|
2144
2157
|
if (p.unit === "USD_CENT") return Math.round(p.value) / 100;
|
|
@@ -2161,6 +2174,7 @@ var init_opire = __esm({
|
|
|
2161
2174
|
init_bounty_gate();
|
|
2162
2175
|
init_github_bounties();
|
|
2163
2176
|
init_http();
|
|
2177
|
+
init_effort();
|
|
2164
2178
|
OPIRE_REWARDS_URL = "https://api.opire.dev/rewards";
|
|
2165
2179
|
MIN_USD = 25;
|
|
2166
2180
|
MAX_USD = 25e3;
|
|
@@ -2197,7 +2211,7 @@ var init_opire = __esm({
|
|
|
2197
2211
|
const tags = normalize([...r.programmingLanguages ?? [], ...tokenize3(title)]);
|
|
2198
2212
|
const bounty = {
|
|
2199
2213
|
amountUSD,
|
|
2200
|
-
estimatedEffort:
|
|
2214
|
+
estimatedEffort: effortFromAmount(amountUSD),
|
|
2201
2215
|
bountySource: "opire",
|
|
2202
2216
|
claimUrl: r.url,
|
|
2203
2217
|
repoFullName
|
|
@@ -6544,6 +6558,7 @@ __export(src_exports, {
|
|
|
6544
6558
|
isBounty: () => isBounty,
|
|
6545
6559
|
isContribution: () => isContribution,
|
|
6546
6560
|
isOverIntroLimit: () => isOverIntroLimit,
|
|
6561
|
+
isTrivialPRTitle: () => isTrivialPRTitle,
|
|
6547
6562
|
joinLabels: () => joinLabels,
|
|
6548
6563
|
labelFor: () => labelFor,
|
|
6549
6564
|
lever: () => lever,
|
|
@@ -6600,9 +6615,9 @@ var init_keytar = __esm({
|
|
|
6600
6615
|
}
|
|
6601
6616
|
});
|
|
6602
6617
|
|
|
6603
|
-
// node-file:/
|
|
6618
|
+
// node-file:/Users/ericgang/job-placement-inline/node_modules/keytar/build/Release/keytar.node
|
|
6604
6619
|
var require_keytar = __commonJS({
|
|
6605
|
-
"node-file:/
|
|
6620
|
+
"node-file:/Users/ericgang/job-placement-inline/node_modules/keytar/build/Release/keytar.node"(exports, module) {
|
|
6606
6621
|
"use strict";
|
|
6607
6622
|
init_keytar();
|
|
6608
6623
|
try {
|
|
@@ -6974,6 +6989,37 @@ function excludeOwnCard(results, ownLogin) {
|
|
|
6974
6989
|
});
|
|
6975
6990
|
}
|
|
6976
6991
|
|
|
6992
|
+
// bin/sanitize.js
|
|
6993
|
+
var CONTROL_CHARS = /[\x00-\x1f\x7f-\x9f]/g;
|
|
6994
|
+
function sanitizeText(s) {
|
|
6995
|
+
if (s == null) return "";
|
|
6996
|
+
return String(s).replace(CONTROL_CHARS, "");
|
|
6997
|
+
}
|
|
6998
|
+
function safeHttpUrl(url) {
|
|
6999
|
+
if (url == null) return null;
|
|
7000
|
+
const raw = String(url);
|
|
7001
|
+
CONTROL_CHARS.lastIndex = 0;
|
|
7002
|
+
if (CONTROL_CHARS.test(raw)) return null;
|
|
7003
|
+
let parsed;
|
|
7004
|
+
try {
|
|
7005
|
+
parsed = new URL(raw);
|
|
7006
|
+
} catch {
|
|
7007
|
+
return null;
|
|
7008
|
+
}
|
|
7009
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") return null;
|
|
7010
|
+
return parsed.href;
|
|
7011
|
+
}
|
|
7012
|
+
function linkTitle(title, url) {
|
|
7013
|
+
const safeTitle = sanitizeText(title);
|
|
7014
|
+
const href = safeHttpUrl(url);
|
|
7015
|
+
const isTTY = process.stdout.isTTY;
|
|
7016
|
+
const noColor = process.env["NO_COLOR"] !== void 0;
|
|
7017
|
+
if (isTTY && !noColor && href) {
|
|
7018
|
+
return `\x1B]8;;${href}\x1B\\${safeTitle}\x1B]8;;\x1B\\`;
|
|
7019
|
+
}
|
|
7020
|
+
return href ? `${safeTitle} (${href})` : safeTitle;
|
|
7021
|
+
}
|
|
7022
|
+
|
|
6977
7023
|
// bin/jpi-devs.js
|
|
6978
7024
|
var API_URL2 = process.env["TERMINALHIRE_API_URL"] ?? process.env["JPI_API_URL"] ?? "https://terminalhire.com";
|
|
6979
7025
|
var DEFAULT_LIMIT = 10;
|
|
@@ -6995,24 +7041,18 @@ function credentialUrl(job) {
|
|
|
6995
7041
|
const u = job.url ?? "";
|
|
6996
7042
|
return u.startsWith("http") ? u : `${API_URL2}${u}`;
|
|
6997
7043
|
}
|
|
6998
|
-
function linkTitle(title, url) {
|
|
6999
|
-
const isTTY = process.stdout.isTTY;
|
|
7000
|
-
const noColor = process.env["NO_COLOR"] !== void 0;
|
|
7001
|
-
if (isTTY && !noColor && url) return `\x1B]8;;${url}\x1B\\${title}\x1B]8;;\x1B\\`;
|
|
7002
|
-
return url ? `${title} (${url})` : title;
|
|
7003
|
-
}
|
|
7004
7044
|
function printCard(i, result) {
|
|
7005
7045
|
const { job, score, matchedTags, reason } = result;
|
|
7006
7046
|
const url = credentialUrl(job);
|
|
7007
7047
|
const kind = job.source === "project" ? "project" : "developer";
|
|
7008
|
-
const byline = job.source === "project" ? ` \xB7 by @${job.company}` : "";
|
|
7048
|
+
const byline = job.source === "project" ? ` \xB7 by @${sanitizeText(job.company)}` : "";
|
|
7009
7049
|
const scoreStr = score > 0 ? ` \xB7 match ${Math.round(score * 100)}%` : "";
|
|
7010
7050
|
console.log(`
|
|
7011
7051
|
${i + 1}. ${linkTitle(job.title, url)} \u2014 ${kind}${byline}${scoreStr}`);
|
|
7012
7052
|
console.log(` id: ${job.id}`);
|
|
7013
7053
|
if (reason) console.log(` ${reason}`);
|
|
7014
7054
|
if (matchedTags && matchedTags.length) console.log(` Tags matched: ${matchedTags.slice(0, 5).join(", ")}`);
|
|
7015
|
-
console.log(` Profile: ${url}`);
|
|
7055
|
+
console.log(` Profile: ${sanitizeText(url)}`);
|
|
7016
7056
|
}
|
|
7017
7057
|
async function run() {
|
|
7018
7058
|
try {
|