terminalhire 0.40.8 → 0.40.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/claim-push-bg.js +11 -5
- package/dist/bin/founder-surface.js +47 -0
- package/dist/bin/jpi-bounties.js +210 -6
- package/dist/bin/jpi-claim.js +391 -38
- package/dist/bin/jpi-config.js +29 -0
- package/dist/bin/jpi-contribute.js +17 -0
- package/dist/bin/jpi-devs.js +1 -0
- package/dist/bin/jpi-dispatch.js +1331 -342
- package/dist/bin/jpi-hub.js +1 -0
- package/dist/bin/jpi-init.js +1 -0
- package/dist/bin/jpi-jobs.js +232 -71
- package/dist/bin/jpi-login.js +18 -0
- package/dist/bin/jpi-mcp.js +420 -46
- package/dist/bin/jpi-post.js +559 -0
- package/dist/bin/jpi-project.js +1 -0
- package/dist/bin/jpi-refresh.js +121 -7
- package/dist/bin/jpi-statusline.js +21 -0
- package/dist/bin/jpi.js +107 -13
- package/dist/src/config.js +9 -0
- package/dist/src/github-auth.js +17 -0
- package/dist/src/posting-drafts.js +264 -0
- package/package.json +1 -1
|
@@ -601,9 +601,11 @@ async function shouldNudgeUnpushed() {
|
|
|
601
601
|
}
|
|
602
602
|
async function runBackgroundClaimPush({ now = Date.now() } = {}) {
|
|
603
603
|
try {
|
|
604
|
-
if (!existsSync5(CLAIM_PUSH_AUTO_MARKER) || !existsSync5(CLAIM_PUSH_TOKEN_FILE))
|
|
604
|
+
if (!existsSync5(CLAIM_PUSH_AUTO_MARKER) || !existsSync5(CLAIM_PUSH_TOKEN_FILE)) {
|
|
605
|
+
return { pushed: false, reason: "not-opted-in" };
|
|
606
|
+
}
|
|
605
607
|
const marker = readAutoMarker();
|
|
606
|
-
if (!marker || !marker.autoConsentedAt) return;
|
|
608
|
+
if (!marker || !marker.autoConsentedAt) return { pushed: false, reason: "not-opted-in" };
|
|
607
609
|
const { listClaims: listClaims2, toPushedClaim: toPushedClaim2, PUSHED_CLAIM_FIELDS: PUSHED_CLAIM_FIELDS2 } = await Promise.resolve().then(() => (init_claims(), claims_exports));
|
|
608
610
|
const pushed = listClaims2().map((c) => toPushedClaim2(c));
|
|
609
611
|
const currentHash = computeSnapshotHash(pushed);
|
|
@@ -616,9 +618,9 @@ async function runBackgroundClaimPush({ now = Date.now() } = {}) {
|
|
|
616
618
|
currentHash,
|
|
617
619
|
lastSnapshotHash: marker.lastSnapshotHash ?? null
|
|
618
620
|
});
|
|
619
|
-
if (!gate.push) return;
|
|
621
|
+
if (!gate.push) return { pushed: false, reason: gate.reason };
|
|
620
622
|
const token = await readPushTokenEnc();
|
|
621
|
-
if (!token) return;
|
|
623
|
+
if (!token) return { pushed: false, reason: "unreadable-token" };
|
|
622
624
|
const consentReceipt = {
|
|
623
625
|
consentedAt: marker.autoConsentedAt,
|
|
624
626
|
version: AUTO_CONSENT_VERSION,
|
|
@@ -630,13 +632,17 @@ async function runBackgroundClaimPush({ now = Date.now() } = {}) {
|
|
|
630
632
|
body: JSON.stringify({ consentToken: consentReceipt, claims: pushed, pushToken: token }),
|
|
631
633
|
signal: AbortSignal.timeout(1e4)
|
|
632
634
|
});
|
|
633
|
-
if (!res.ok)
|
|
635
|
+
if (!res.ok) {
|
|
636
|
+
return { pushed: false, reason: `server-${res.status}` };
|
|
637
|
+
}
|
|
634
638
|
writeAutoMarker({
|
|
635
639
|
...marker,
|
|
636
640
|
lastPushedAt: new Date(now).toISOString(),
|
|
637
641
|
lastSnapshotHash: currentHash
|
|
638
642
|
});
|
|
643
|
+
return { pushed: true, reason: "ok" };
|
|
639
644
|
} catch {
|
|
645
|
+
return { pushed: false, reason: "failed" };
|
|
640
646
|
}
|
|
641
647
|
}
|
|
642
648
|
export {
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// bin/founder-surface.js
|
|
2
|
+
function projectFounderSurface(body) {
|
|
3
|
+
if (!body || body.ok !== true || !Array.isArray(body.postings)) return null;
|
|
4
|
+
const statusline = body.statusline;
|
|
5
|
+
if (!statusline || typeof statusline.needsYouCount !== "number" || typeof statusline.openPostingCount !== "number" || typeof statusline.refreshedAt !== "string") {
|
|
6
|
+
return null;
|
|
7
|
+
}
|
|
8
|
+
const postings = [];
|
|
9
|
+
for (const raw of body.postings) {
|
|
10
|
+
if (!raw || typeof raw.id !== "string" || typeof raw.title !== "string" || typeof raw.status !== "string" || typeof raw.needsYou !== "boolean" || typeof raw.claimantCount !== "number" || typeof raw.postedAt !== "string") {
|
|
11
|
+
continue;
|
|
12
|
+
}
|
|
13
|
+
postings.push({
|
|
14
|
+
id: raw.id,
|
|
15
|
+
title: raw.title,
|
|
16
|
+
status: raw.status,
|
|
17
|
+
needsYou: raw.needsYou,
|
|
18
|
+
claimantCount: raw.claimantCount,
|
|
19
|
+
postedAt: raw.postedAt
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
return {
|
|
23
|
+
postings,
|
|
24
|
+
needsYouCount: Math.max(0, statusline.needsYouCount),
|
|
25
|
+
openPostingCount: Math.max(0, statusline.openPostingCount),
|
|
26
|
+
refreshedAt: statusline.refreshedAt
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
function resolveSurfaceLead(override, founderSurface) {
|
|
30
|
+
if (override === "dev" || override === "founder") return override;
|
|
31
|
+
if (founderSurface && (founderSurface.openPostingCount > 0 || founderSurface.needsYouCount > 0)) {
|
|
32
|
+
return "founder";
|
|
33
|
+
}
|
|
34
|
+
return "dev";
|
|
35
|
+
}
|
|
36
|
+
function founderRows(surface, kind) {
|
|
37
|
+
if (!surface || !Array.isArray(surface.postings)) return [];
|
|
38
|
+
if (kind === "jobs") {
|
|
39
|
+
return surface.postings.filter((posting) => posting.status === "open" || posting.needsYou);
|
|
40
|
+
}
|
|
41
|
+
return surface.postings;
|
|
42
|
+
}
|
|
43
|
+
export {
|
|
44
|
+
founderRows,
|
|
45
|
+
projectFounderSurface,
|
|
46
|
+
resolveSurfaceLead
|
|
47
|
+
};
|
package/dist/bin/jpi-bounties.js
CHANGED
|
@@ -10332,6 +10332,7 @@ __export(src_exports, {
|
|
|
10332
10332
|
fetchOwnedRepoTraction: () => fetchOwnedRepoTraction,
|
|
10333
10333
|
fetchPRLifecycle: () => fetchPRLifecycle,
|
|
10334
10334
|
fetchPRScoringFacts: () => fetchPRScoringFacts,
|
|
10335
|
+
fetchPublicOrgs: () => fetchPublicOrgs,
|
|
10335
10336
|
fetchRepoRecency: () => fetchRepoRecency,
|
|
10336
10337
|
fetchRepoReceptivity: () => fetchRepoReceptivity,
|
|
10337
10338
|
fetchRepoStatus: () => fetchRepoStatus,
|
|
@@ -11519,6 +11520,178 @@ var init_claims = __esm({
|
|
|
11519
11520
|
}
|
|
11520
11521
|
});
|
|
11521
11522
|
|
|
11523
|
+
// src/config.ts
|
|
11524
|
+
var config_exports = {};
|
|
11525
|
+
__export(config_exports, {
|
|
11526
|
+
getNudgeMode: () => getNudgeMode,
|
|
11527
|
+
getSurfaceLeadOverride: () => getSurfaceLeadOverride,
|
|
11528
|
+
getSurfaceMix: () => getSurfaceMix,
|
|
11529
|
+
isBetaOptIn: () => isBetaOptIn,
|
|
11530
|
+
isContributeEnabled: () => isContributeEnabled,
|
|
11531
|
+
isInboundNudgeMuted: () => isInboundNudgeMuted,
|
|
11532
|
+
isPeerConnectEnabled: () => isPeerConnectEnabled,
|
|
11533
|
+
parseNudgeMode: () => parseNudgeMode,
|
|
11534
|
+
parseSurfaceLead: () => parseSurfaceLead,
|
|
11535
|
+
parseSurfaceMix: () => parseSurfaceMix,
|
|
11536
|
+
readConfig: () => readConfig,
|
|
11537
|
+
writeConfig: () => writeConfig
|
|
11538
|
+
});
|
|
11539
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync5, existsSync as existsSync5 } from "fs";
|
|
11540
|
+
import { join as join9 } from "path";
|
|
11541
|
+
import { homedir as homedir6 } from "os";
|
|
11542
|
+
function readConfig() {
|
|
11543
|
+
try {
|
|
11544
|
+
if (!existsSync5(CONFIG_FILE)) return { ...DEFAULT_CONFIG };
|
|
11545
|
+
const raw = readFileSync6(CONFIG_FILE, "utf8");
|
|
11546
|
+
const parsed = JSON.parse(raw);
|
|
11547
|
+
return { ...DEFAULT_CONFIG, ...parsed };
|
|
11548
|
+
} catch {
|
|
11549
|
+
return { ...DEFAULT_CONFIG };
|
|
11550
|
+
}
|
|
11551
|
+
}
|
|
11552
|
+
function writeConfig(config) {
|
|
11553
|
+
ensureStateDir(TERMINALHIRE_DIR6);
|
|
11554
|
+
const current = readConfig();
|
|
11555
|
+
const merged = { ...current, ...config };
|
|
11556
|
+
if ("contributePrompted" in merged) {
|
|
11557
|
+
if (merged.contributeEnabled === false && !("contributeEnabled" in config)) {
|
|
11558
|
+
delete merged.contributeEnabled;
|
|
11559
|
+
}
|
|
11560
|
+
delete merged.contributePrompted;
|
|
11561
|
+
}
|
|
11562
|
+
writeFileSync5(CONFIG_FILE, JSON.stringify(merged, null, 2) + "\n", "utf8");
|
|
11563
|
+
}
|
|
11564
|
+
function parseNudgeMode(raw) {
|
|
11565
|
+
if (raw === "session" || raw === "always") return raw;
|
|
11566
|
+
const m = /^every:(\d+)$/.exec(raw);
|
|
11567
|
+
if (m) {
|
|
11568
|
+
const n = parseInt(m[1], 10);
|
|
11569
|
+
if (n >= 1) return `every:${n}`;
|
|
11570
|
+
}
|
|
11571
|
+
return null;
|
|
11572
|
+
}
|
|
11573
|
+
function parseSurfaceMix(raw) {
|
|
11574
|
+
if (raw === "jobs" || raw === "balanced" || raw === "credential") return raw;
|
|
11575
|
+
return null;
|
|
11576
|
+
}
|
|
11577
|
+
function parseSurfaceLead(raw) {
|
|
11578
|
+
return raw === "dev" || raw === "founder" ? raw : null;
|
|
11579
|
+
}
|
|
11580
|
+
function getSurfaceLeadOverride() {
|
|
11581
|
+
const value = readConfig().surfaceLead;
|
|
11582
|
+
return value === "dev" || value === "founder" ? value : void 0;
|
|
11583
|
+
}
|
|
11584
|
+
function getSurfaceMix() {
|
|
11585
|
+
const envVal = process.env["TH_MIX"];
|
|
11586
|
+
if (envVal) {
|
|
11587
|
+
const parsed = parseSurfaceMix(envVal);
|
|
11588
|
+
if (parsed) return parsed;
|
|
11589
|
+
}
|
|
11590
|
+
const config = readConfig();
|
|
11591
|
+
return parseSurfaceMix(config.mix) ?? "balanced";
|
|
11592
|
+
}
|
|
11593
|
+
function getNudgeMode() {
|
|
11594
|
+
const envVal = process.env["TERMINALHIRE_NUDGE"];
|
|
11595
|
+
if (envVal) {
|
|
11596
|
+
const parsed = parseNudgeMode(envVal);
|
|
11597
|
+
if (parsed) return parsed;
|
|
11598
|
+
}
|
|
11599
|
+
const config = readConfig();
|
|
11600
|
+
return config.nudge ?? "session";
|
|
11601
|
+
}
|
|
11602
|
+
function isPeerConnectEnabled() {
|
|
11603
|
+
return readConfig().peerConnect === true;
|
|
11604
|
+
}
|
|
11605
|
+
function isInboundNudgeMuted() {
|
|
11606
|
+
return readConfig().inboundNudgeMuted === true;
|
|
11607
|
+
}
|
|
11608
|
+
function isContributeEnabled() {
|
|
11609
|
+
const cfg = readConfig();
|
|
11610
|
+
return !(cfg.contributeEnabled === false && !("contributePrompted" in cfg));
|
|
11611
|
+
}
|
|
11612
|
+
function isBetaOptIn() {
|
|
11613
|
+
return readConfig().betaOptIn === true;
|
|
11614
|
+
}
|
|
11615
|
+
var TERMINALHIRE_DIR6, CONFIG_FILE, DEFAULT_CONFIG;
|
|
11616
|
+
var init_config = __esm({
|
|
11617
|
+
"src/config.ts"() {
|
|
11618
|
+
"use strict";
|
|
11619
|
+
init_state_dir();
|
|
11620
|
+
TERMINALHIRE_DIR6 = process.env.TERMINALHIRE_DIR || join9(homedir6(), ".terminalhire");
|
|
11621
|
+
CONFIG_FILE = join9(TERMINALHIRE_DIR6, "config.json");
|
|
11622
|
+
DEFAULT_CONFIG = {
|
|
11623
|
+
nudge: "session",
|
|
11624
|
+
peerConnect: false,
|
|
11625
|
+
peerConnectPrompted: false,
|
|
11626
|
+
resumePublishPrompted: false,
|
|
11627
|
+
chatDisclosureAck: false,
|
|
11628
|
+
chatShareActivity: false,
|
|
11629
|
+
inboundNudgeMuted: false,
|
|
11630
|
+
inboundNudgeDisclosed: false,
|
|
11631
|
+
contributeEnabled: true,
|
|
11632
|
+
betaOptIn: false,
|
|
11633
|
+
lastFullFeedbackAt: null,
|
|
11634
|
+
lastPulseAskAt: null,
|
|
11635
|
+
pulseDisclosed: false,
|
|
11636
|
+
mix: "balanced"
|
|
11637
|
+
};
|
|
11638
|
+
}
|
|
11639
|
+
});
|
|
11640
|
+
|
|
11641
|
+
// bin/founder-surface.js
|
|
11642
|
+
var founder_surface_exports = {};
|
|
11643
|
+
__export(founder_surface_exports, {
|
|
11644
|
+
founderRows: () => founderRows,
|
|
11645
|
+
projectFounderSurface: () => projectFounderSurface,
|
|
11646
|
+
resolveSurfaceLead: () => resolveSurfaceLead
|
|
11647
|
+
});
|
|
11648
|
+
function projectFounderSurface(body) {
|
|
11649
|
+
if (!body || body.ok !== true || !Array.isArray(body.postings)) return null;
|
|
11650
|
+
const statusline = body.statusline;
|
|
11651
|
+
if (!statusline || typeof statusline.needsYouCount !== "number" || typeof statusline.openPostingCount !== "number" || typeof statusline.refreshedAt !== "string") {
|
|
11652
|
+
return null;
|
|
11653
|
+
}
|
|
11654
|
+
const postings = [];
|
|
11655
|
+
for (const raw of body.postings) {
|
|
11656
|
+
if (!raw || typeof raw.id !== "string" || typeof raw.title !== "string" || typeof raw.status !== "string" || typeof raw.needsYou !== "boolean" || typeof raw.claimantCount !== "number" || typeof raw.postedAt !== "string") {
|
|
11657
|
+
continue;
|
|
11658
|
+
}
|
|
11659
|
+
postings.push({
|
|
11660
|
+
id: raw.id,
|
|
11661
|
+
title: raw.title,
|
|
11662
|
+
status: raw.status,
|
|
11663
|
+
needsYou: raw.needsYou,
|
|
11664
|
+
claimantCount: raw.claimantCount,
|
|
11665
|
+
postedAt: raw.postedAt
|
|
11666
|
+
});
|
|
11667
|
+
}
|
|
11668
|
+
return {
|
|
11669
|
+
postings,
|
|
11670
|
+
needsYouCount: Math.max(0, statusline.needsYouCount),
|
|
11671
|
+
openPostingCount: Math.max(0, statusline.openPostingCount),
|
|
11672
|
+
refreshedAt: statusline.refreshedAt
|
|
11673
|
+
};
|
|
11674
|
+
}
|
|
11675
|
+
function resolveSurfaceLead(override, founderSurface) {
|
|
11676
|
+
if (override === "dev" || override === "founder") return override;
|
|
11677
|
+
if (founderSurface && (founderSurface.openPostingCount > 0 || founderSurface.needsYouCount > 0)) {
|
|
11678
|
+
return "founder";
|
|
11679
|
+
}
|
|
11680
|
+
return "dev";
|
|
11681
|
+
}
|
|
11682
|
+
function founderRows(surface, kind) {
|
|
11683
|
+
if (!surface || !Array.isArray(surface.postings)) return [];
|
|
11684
|
+
if (kind === "jobs") {
|
|
11685
|
+
return surface.postings.filter((posting) => posting.status === "open" || posting.needsYou);
|
|
11686
|
+
}
|
|
11687
|
+
return surface.postings;
|
|
11688
|
+
}
|
|
11689
|
+
var init_founder_surface = __esm({
|
|
11690
|
+
"bin/founder-surface.js"() {
|
|
11691
|
+
"use strict";
|
|
11692
|
+
}
|
|
11693
|
+
});
|
|
11694
|
+
|
|
11522
11695
|
// bin/founder-paid-badge.js
|
|
11523
11696
|
var founder_paid_badge_exports = {};
|
|
11524
11697
|
__export(founder_paid_badge_exports, {
|
|
@@ -11559,9 +11732,9 @@ var init_founder_paid_badge = __esm({
|
|
|
11559
11732
|
// bin/jpi-bounties.js
|
|
11560
11733
|
init_src();
|
|
11561
11734
|
init_cache_store();
|
|
11562
|
-
import { readFileSync as
|
|
11563
|
-
import { join as
|
|
11564
|
-
import { homedir as
|
|
11735
|
+
import { readFileSync as readFileSync7 } from "fs";
|
|
11736
|
+
import { join as join10 } from "path";
|
|
11737
|
+
import { homedir as homedir7 } from "os";
|
|
11565
11738
|
import { createInterface } from "readline";
|
|
11566
11739
|
|
|
11567
11740
|
// bin/sanitize.js
|
|
@@ -11597,8 +11770,8 @@ function linkTitle(title, url) {
|
|
|
11597
11770
|
|
|
11598
11771
|
// bin/jpi-bounties.js
|
|
11599
11772
|
init_founder_pin();
|
|
11600
|
-
var
|
|
11601
|
-
var INDEX_CACHE_FILE2 =
|
|
11773
|
+
var TERMINALHIRE_DIR7 = process.env.TERMINALHIRE_DIR || join10(homedir7(), ".terminalhire");
|
|
11774
|
+
var INDEX_CACHE_FILE2 = join10(TERMINALHIRE_DIR7, "index-cache.json");
|
|
11602
11775
|
var INDEX_TTL_MS = 15 * 60 * 1e3;
|
|
11603
11776
|
var API_URL = process.env["TERMINALHIRE_API_URL"] ?? process.env["JPI_API_URL"] ?? "https://terminalhire.com";
|
|
11604
11777
|
var RANK_MODE = process.env["TERMINALHIRE_BOUNTY_RANK"] ?? "winnability";
|
|
@@ -11612,7 +11785,7 @@ var SHOW_ALL = args.includes("--all");
|
|
|
11612
11785
|
var WINNABLE_ONLY = args.includes("--winnable");
|
|
11613
11786
|
function readIndexCache() {
|
|
11614
11787
|
try {
|
|
11615
|
-
const entry = JSON.parse(
|
|
11788
|
+
const entry = JSON.parse(readFileSync7(INDEX_CACHE_FILE2, "utf8"));
|
|
11616
11789
|
if (Date.now() - entry.ts < INDEX_TTL_MS) return entry.index;
|
|
11617
11790
|
return null;
|
|
11618
11791
|
} catch {
|
|
@@ -11665,6 +11838,11 @@ ${i + 1}. ${linkTitle(job.title, job.url)} [${ref}]`);
|
|
|
11665
11838
|
);
|
|
11666
11839
|
if (reason) console.log(` ${reason}`);
|
|
11667
11840
|
if (continuityNote) console.log(` ${continuityNote}`);
|
|
11841
|
+
if (b.bountySource === "founder" && b.specProvenance) {
|
|
11842
|
+
console.log(
|
|
11843
|
+
` Spec: ${b.specProvenance === "agent_drafted_human_confirmed" ? "agent drafted \xB7 founder confirmed" : "founder authored"}`
|
|
11844
|
+
);
|
|
11845
|
+
}
|
|
11668
11846
|
if (matchedTags && matchedTags.length)
|
|
11669
11847
|
console.log(` Tags matched: ${matchedTags.slice(0, 5).join(", ")}`);
|
|
11670
11848
|
console.log(` id: ${job.id}`);
|
|
@@ -11775,6 +11953,32 @@ async function getBounties({ quiet = false, offline = false, priced = PRICED_ONL
|
|
|
11775
11953
|
}
|
|
11776
11954
|
async function run() {
|
|
11777
11955
|
try {
|
|
11956
|
+
if (args.length === 0) {
|
|
11957
|
+
try {
|
|
11958
|
+
const { readCacheEntry: readCacheEntry2 } = await Promise.resolve().then(() => (init_cache_store(), cache_store_exports));
|
|
11959
|
+
const { getSurfaceLeadOverride: getSurfaceLeadOverride2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
11960
|
+
const { founderRows: founderRows2, resolveSurfaceLead: resolveSurfaceLead2 } = await Promise.resolve().then(() => (init_founder_surface(), founder_surface_exports));
|
|
11961
|
+
const surface = (readCacheEntry2() ?? {}).founderSurface;
|
|
11962
|
+
if (resolveSurfaceLead2(getSurfaceLeadOverride2(), surface) === "founder") {
|
|
11963
|
+
const rows = founderRows2(surface, "bounties");
|
|
11964
|
+
console.log("\n\u26A1 Your TerminalHire postings\n");
|
|
11965
|
+
if (!rows.length) {
|
|
11966
|
+
console.log(" No current posting data. Run `terminalhire refresh`, or lead with work:");
|
|
11967
|
+
console.log(" terminalhire config set lead dev\n");
|
|
11968
|
+
} else {
|
|
11969
|
+
rows.forEach((row, index) => {
|
|
11970
|
+
console.log(
|
|
11971
|
+
` ${index + 1}. ${sanitizeText(row.title)} \xB7 ${row.status} \xB7 ${row.claimantCount} claimant${row.claimantCount === 1 ? "" : "s"}${row.needsYou ? " \xB7 waiting on you" : ""}`
|
|
11972
|
+
);
|
|
11973
|
+
});
|
|
11974
|
+
console.log("\n Review and act: https://terminalhire.com/dashboard?tab=postings");
|
|
11975
|
+
console.log(" Lead with developer bounties instead: terminalhire config set lead dev\n");
|
|
11976
|
+
}
|
|
11977
|
+
return;
|
|
11978
|
+
}
|
|
11979
|
+
} catch {
|
|
11980
|
+
}
|
|
11981
|
+
}
|
|
11778
11982
|
const result = await getBounties();
|
|
11779
11983
|
if (result.status === "empty") {
|
|
11780
11984
|
console.log(
|