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.
@@ -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)) return;
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) return;
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
+ };
@@ -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 readFileSync6 } from "fs";
11563
- import { join as join9 } from "path";
11564
- import { homedir as homedir6 } from "os";
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 TERMINALHIRE_DIR6 = process.env.TERMINALHIRE_DIR || join9(homedir6(), ".terminalhire");
11601
- var INDEX_CACHE_FILE2 = join9(TERMINALHIRE_DIR6, "index-cache.json");
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(readFileSync6(INDEX_CACHE_FILE2, "utf8"));
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(