terminalhire 0.42.12 → 0.42.13
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/index-revalidate.js +138 -0
- package/dist/bin/jpi-bounties.js +179 -61
- package/dist/bin/jpi-claim.js +84 -14
- package/dist/bin/jpi-dispatch.js +256 -39
- package/dist/bin/jpi-hub.js +192 -62
- package/dist/bin/jpi-jobs.js +178 -59
- package/dist/bin/jpi-mcp.js +84 -14
- package/package.json +1 -1
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
// bin/index-revalidate.js
|
|
2
|
+
var PULSE_TIMEOUT_MS = 2e3;
|
|
3
|
+
var INDEX_TIMEOUT_WARM_MS = 5e3;
|
|
4
|
+
var INDEX_TIMEOUT_COLD_MS = 1e4;
|
|
5
|
+
function usableMember(member) {
|
|
6
|
+
return typeof member === "object" && member !== null && !Array.isArray(member) && Array.isArray(member.tags);
|
|
7
|
+
}
|
|
8
|
+
function usableIndex(index) {
|
|
9
|
+
if (!index || typeof index !== "object" || Array.isArray(index)) return false;
|
|
10
|
+
if (!Array.isArray(index.jobs) || !index.jobs.every(usableMember)) return false;
|
|
11
|
+
if (index.contribute !== void 0) {
|
|
12
|
+
if (!Array.isArray(index.contribute) || !index.contribute.every(usableMember)) return false;
|
|
13
|
+
}
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
16
|
+
function deadlineFetch(fetchImpl, url, { headers, timeoutMs }) {
|
|
17
|
+
const controller = new AbortController();
|
|
18
|
+
const timer = setTimeout(() => controller.abort(new Error("deadline exceeded")), timeoutMs);
|
|
19
|
+
timer.unref?.();
|
|
20
|
+
const promise = Promise.resolve(fetchImpl(url, { headers, signal: controller.signal })).finally(
|
|
21
|
+
() => clearTimeout(timer)
|
|
22
|
+
);
|
|
23
|
+
return { promise, abort: () => controller.abort(new Error("superseded")) };
|
|
24
|
+
}
|
|
25
|
+
function cacheFallbackNotice(source, cachedAt, now = Date.now()) {
|
|
26
|
+
if (source !== "cache-fallback") return null;
|
|
27
|
+
const base = "terminalhire: could not refresh the list \u2014 showing cached results";
|
|
28
|
+
if (typeof cachedAt !== "number" || !Number.isFinite(cachedAt)) return `${base}.`;
|
|
29
|
+
const minutes = Math.floor(Math.max(0, now - cachedAt) / 6e4);
|
|
30
|
+
if (minutes < 1) return `${base} from moments ago.`;
|
|
31
|
+
if (minutes < 60) return `${base} from ${minutes}m ago.`;
|
|
32
|
+
const hours = Math.floor(minutes / 60);
|
|
33
|
+
return `${base} from ${hours}h ago.`;
|
|
34
|
+
}
|
|
35
|
+
async function revalidateIndex({
|
|
36
|
+
apiUrl,
|
|
37
|
+
fetchImpl,
|
|
38
|
+
readEntry,
|
|
39
|
+
writeEntry,
|
|
40
|
+
pulseTimeoutMs = PULSE_TIMEOUT_MS,
|
|
41
|
+
indexTimeoutWarmMs = INDEX_TIMEOUT_WARM_MS,
|
|
42
|
+
indexTimeoutColdMs = INDEX_TIMEOUT_COLD_MS
|
|
43
|
+
}) {
|
|
44
|
+
let entry = {};
|
|
45
|
+
try {
|
|
46
|
+
entry = readEntry() ?? {};
|
|
47
|
+
} catch {
|
|
48
|
+
}
|
|
49
|
+
const cached = usableIndex(entry.index) ? entry.index : null;
|
|
50
|
+
const cachedAt = cached && typeof entry.ts === "number" ? entry.ts : null;
|
|
51
|
+
const indexTimeoutMs = cached ? indexTimeoutWarmMs : indexTimeoutColdMs;
|
|
52
|
+
const headers = { Accept: "application/json" };
|
|
53
|
+
if (cached && typeof entry.indexETag === "string" && entry.indexETag !== "") {
|
|
54
|
+
headers["If-None-Match"] = entry.indexETag;
|
|
55
|
+
}
|
|
56
|
+
const pulseCall = deadlineFetch(fetchImpl, `${apiUrl}/api/pulse`, {
|
|
57
|
+
headers: { Accept: "application/json" },
|
|
58
|
+
timeoutMs: pulseTimeoutMs
|
|
59
|
+
});
|
|
60
|
+
const speculative = deadlineFetch(fetchImpl, `${apiUrl}/api/index`, {
|
|
61
|
+
headers,
|
|
62
|
+
timeoutMs: indexTimeoutMs
|
|
63
|
+
});
|
|
64
|
+
const speculativeSettled = speculative.promise.then(
|
|
65
|
+
(res2) => ({ ok: true, res: res2 }),
|
|
66
|
+
(err) => ({ ok: false, err })
|
|
67
|
+
);
|
|
68
|
+
let latestPostedAt = null;
|
|
69
|
+
try {
|
|
70
|
+
const res2 = await pulseCall.promise;
|
|
71
|
+
if (res2.ok) {
|
|
72
|
+
const pulse = await res2.json();
|
|
73
|
+
latestPostedAt = pulse?.latestPostedAt ?? null;
|
|
74
|
+
}
|
|
75
|
+
} catch {
|
|
76
|
+
}
|
|
77
|
+
const bust = latestPostedAt !== null && entry.latestPostedAt !== latestPostedAt;
|
|
78
|
+
let outcome;
|
|
79
|
+
let reachedOrigin = false;
|
|
80
|
+
if (bust) {
|
|
81
|
+
speculative.abort();
|
|
82
|
+
const bustCall = deadlineFetch(fetchImpl, `${apiUrl}/api/index`, {
|
|
83
|
+
// `no-cache` forces revalidation against the origin; the validator rides along
|
|
84
|
+
// so a genuinely unchanged body still costs a 304 rather than a full download.
|
|
85
|
+
headers: { ...headers, "Cache-Control": "no-cache" },
|
|
86
|
+
timeoutMs: indexTimeoutMs
|
|
87
|
+
});
|
|
88
|
+
try {
|
|
89
|
+
outcome = { ok: true, res: await bustCall.promise };
|
|
90
|
+
reachedOrigin = true;
|
|
91
|
+
} catch (err) {
|
|
92
|
+
const spec = await speculativeSettled;
|
|
93
|
+
outcome = spec.ok ? spec : { ok: false, err };
|
|
94
|
+
}
|
|
95
|
+
} else {
|
|
96
|
+
outcome = await speculativeSettled;
|
|
97
|
+
}
|
|
98
|
+
const fellBack = (reason) => cached ? { index: cached, source: "cache-fallback", reason, cachedAt } : { index: null, source: "none", reason, cachedAt };
|
|
99
|
+
if (!outcome.ok) {
|
|
100
|
+
const err = outcome.err;
|
|
101
|
+
return fellBack(err instanceof Error ? err.message : String(err));
|
|
102
|
+
}
|
|
103
|
+
const res = outcome.res;
|
|
104
|
+
try {
|
|
105
|
+
if (res.status === 304 && cached) {
|
|
106
|
+
if (reachedOrigin && latestPostedAt && latestPostedAt !== entry.latestPostedAt) {
|
|
107
|
+
try {
|
|
108
|
+
writeEntry({ latestPostedAt });
|
|
109
|
+
} catch {
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return { index: cached, source: "cache-304", reason: null, cachedAt };
|
|
113
|
+
}
|
|
114
|
+
if (!res.ok) {
|
|
115
|
+
return fellBack(`/api/index returned ${res.status}`);
|
|
116
|
+
}
|
|
117
|
+
const body = await res.json();
|
|
118
|
+
if (!usableIndex(body)) {
|
|
119
|
+
return fellBack("/api/index returned a body that will not render");
|
|
120
|
+
}
|
|
121
|
+
const patch = { index: body, indexETag: res.headers.get("etag") ?? "" };
|
|
122
|
+
if (reachedOrigin && latestPostedAt) patch.latestPostedAt = latestPostedAt;
|
|
123
|
+
try {
|
|
124
|
+
writeEntry(patch);
|
|
125
|
+
} catch {
|
|
126
|
+
}
|
|
127
|
+
return { index: body, source: "fresh", reason: null, cachedAt };
|
|
128
|
+
} catch (err) {
|
|
129
|
+
return fellBack(err instanceof Error ? err.message : String(err));
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
export {
|
|
133
|
+
INDEX_TIMEOUT_COLD_MS,
|
|
134
|
+
INDEX_TIMEOUT_WARM_MS,
|
|
135
|
+
PULSE_TIMEOUT_MS,
|
|
136
|
+
cacheFallbackNotice,
|
|
137
|
+
revalidateIndex
|
|
138
|
+
};
|
package/dist/bin/jpi-bounties.js
CHANGED
|
@@ -10550,52 +10550,6 @@ var init_state_dir = __esm({
|
|
|
10550
10550
|
}
|
|
10551
10551
|
});
|
|
10552
10552
|
|
|
10553
|
-
// bin/cache-store.js
|
|
10554
|
-
var cache_store_exports = {};
|
|
10555
|
-
__export(cache_store_exports, {
|
|
10556
|
-
readCacheEntry: () => readCacheEntry,
|
|
10557
|
-
updateIndexCache: () => updateIndexCache,
|
|
10558
|
-
writeIndexCache: () => writeIndexCache
|
|
10559
|
-
});
|
|
10560
|
-
import { readFileSync as readFileSync2, writeFileSync, renameSync } from "fs";
|
|
10561
|
-
import { join as join2 } from "path";
|
|
10562
|
-
import { homedir } from "os";
|
|
10563
|
-
function readCacheEntry() {
|
|
10564
|
-
try {
|
|
10565
|
-
return JSON.parse(readFileSync2(INDEX_CACHE_FILE, "utf8"));
|
|
10566
|
-
} catch {
|
|
10567
|
-
return null;
|
|
10568
|
-
}
|
|
10569
|
-
}
|
|
10570
|
-
function updateIndexCache(patch) {
|
|
10571
|
-
ensureStateDir(TERMINALHIRE_DIR);
|
|
10572
|
-
const existing = readCacheEntry() ?? {};
|
|
10573
|
-
const entry = {
|
|
10574
|
-
...existing,
|
|
10575
|
-
...patch,
|
|
10576
|
-
schemaVersion: SCHEMA_VERSION2,
|
|
10577
|
-
ts: Date.now()
|
|
10578
|
-
};
|
|
10579
|
-
const tmp = `${INDEX_CACHE_FILE}.${process.pid}.${tmpCounter++}.tmp`;
|
|
10580
|
-
writeFileSync(tmp, JSON.stringify(entry), "utf8");
|
|
10581
|
-
renameSync(tmp, INDEX_CACHE_FILE);
|
|
10582
|
-
return entry;
|
|
10583
|
-
}
|
|
10584
|
-
function writeIndexCache(index) {
|
|
10585
|
-
return updateIndexCache({ index, indexETag: "" });
|
|
10586
|
-
}
|
|
10587
|
-
var TERMINALHIRE_DIR, INDEX_CACHE_FILE, SCHEMA_VERSION2, tmpCounter;
|
|
10588
|
-
var init_cache_store = __esm({
|
|
10589
|
-
"bin/cache-store.js"() {
|
|
10590
|
-
"use strict";
|
|
10591
|
-
init_state_dir();
|
|
10592
|
-
TERMINALHIRE_DIR = process.env.TERMINALHIRE_DIR || join2(homedir(), ".terminalhire");
|
|
10593
|
-
INDEX_CACHE_FILE = join2(TERMINALHIRE_DIR, "index-cache.json");
|
|
10594
|
-
SCHEMA_VERSION2 = 1;
|
|
10595
|
-
tmpCounter = 0;
|
|
10596
|
-
}
|
|
10597
|
-
});
|
|
10598
|
-
|
|
10599
10553
|
// bin/founder-pin.js
|
|
10600
10554
|
function isPinnedFounderBounty(j) {
|
|
10601
10555
|
return j?.bounty?.bountySource === "founder" && j?.bounty?.claimable === true;
|
|
@@ -12021,8 +11975,168 @@ function formatCents(cents) {
|
|
|
12021
11975
|
return cents % 100 === 0 ? `$${cents / 100}` : `$${(cents / 100).toFixed(2)}`;
|
|
12022
11976
|
}
|
|
12023
11977
|
|
|
12024
|
-
// bin/
|
|
12025
|
-
|
|
11978
|
+
// bin/cache-store.js
|
|
11979
|
+
init_state_dir();
|
|
11980
|
+
import { readFileSync as readFileSync2, writeFileSync, renameSync } from "fs";
|
|
11981
|
+
import { join as join2 } from "path";
|
|
11982
|
+
import { homedir } from "os";
|
|
11983
|
+
var TERMINALHIRE_DIR = process.env.TERMINALHIRE_DIR || join2(homedir(), ".terminalhire");
|
|
11984
|
+
var INDEX_CACHE_FILE = join2(TERMINALHIRE_DIR, "index-cache.json");
|
|
11985
|
+
var SCHEMA_VERSION2 = 1;
|
|
11986
|
+
var tmpCounter = 0;
|
|
11987
|
+
function readCacheEntry() {
|
|
11988
|
+
try {
|
|
11989
|
+
return JSON.parse(readFileSync2(INDEX_CACHE_FILE, "utf8"));
|
|
11990
|
+
} catch {
|
|
11991
|
+
return null;
|
|
11992
|
+
}
|
|
11993
|
+
}
|
|
11994
|
+
function updateIndexCache(patch) {
|
|
11995
|
+
ensureStateDir(TERMINALHIRE_DIR);
|
|
11996
|
+
const existing = readCacheEntry() ?? {};
|
|
11997
|
+
const entry = {
|
|
11998
|
+
...existing,
|
|
11999
|
+
...patch,
|
|
12000
|
+
schemaVersion: SCHEMA_VERSION2,
|
|
12001
|
+
ts: Date.now()
|
|
12002
|
+
};
|
|
12003
|
+
const tmp = `${INDEX_CACHE_FILE}.${process.pid}.${tmpCounter++}.tmp`;
|
|
12004
|
+
writeFileSync(tmp, JSON.stringify(entry), "utf8");
|
|
12005
|
+
renameSync(tmp, INDEX_CACHE_FILE);
|
|
12006
|
+
return entry;
|
|
12007
|
+
}
|
|
12008
|
+
|
|
12009
|
+
// bin/index-revalidate.js
|
|
12010
|
+
var PULSE_TIMEOUT_MS = 2e3;
|
|
12011
|
+
var INDEX_TIMEOUT_WARM_MS = 5e3;
|
|
12012
|
+
var INDEX_TIMEOUT_COLD_MS = 1e4;
|
|
12013
|
+
function usableMember(member) {
|
|
12014
|
+
return typeof member === "object" && member !== null && !Array.isArray(member) && Array.isArray(member.tags);
|
|
12015
|
+
}
|
|
12016
|
+
function usableIndex(index) {
|
|
12017
|
+
if (!index || typeof index !== "object" || Array.isArray(index)) return false;
|
|
12018
|
+
if (!Array.isArray(index.jobs) || !index.jobs.every(usableMember)) return false;
|
|
12019
|
+
if (index.contribute !== void 0) {
|
|
12020
|
+
if (!Array.isArray(index.contribute) || !index.contribute.every(usableMember)) return false;
|
|
12021
|
+
}
|
|
12022
|
+
return true;
|
|
12023
|
+
}
|
|
12024
|
+
function deadlineFetch(fetchImpl, url, { headers, timeoutMs }) {
|
|
12025
|
+
const controller = new AbortController();
|
|
12026
|
+
const timer = setTimeout(() => controller.abort(new Error("deadline exceeded")), timeoutMs);
|
|
12027
|
+
timer.unref?.();
|
|
12028
|
+
const promise = Promise.resolve(fetchImpl(url, { headers, signal: controller.signal })).finally(
|
|
12029
|
+
() => clearTimeout(timer)
|
|
12030
|
+
);
|
|
12031
|
+
return { promise, abort: () => controller.abort(new Error("superseded")) };
|
|
12032
|
+
}
|
|
12033
|
+
function cacheFallbackNotice(source, cachedAt, now = Date.now()) {
|
|
12034
|
+
if (source !== "cache-fallback") return null;
|
|
12035
|
+
const base = "terminalhire: could not refresh the list \u2014 showing cached results";
|
|
12036
|
+
if (typeof cachedAt !== "number" || !Number.isFinite(cachedAt)) return `${base}.`;
|
|
12037
|
+
const minutes = Math.floor(Math.max(0, now - cachedAt) / 6e4);
|
|
12038
|
+
if (minutes < 1) return `${base} from moments ago.`;
|
|
12039
|
+
if (minutes < 60) return `${base} from ${minutes}m ago.`;
|
|
12040
|
+
const hours = Math.floor(minutes / 60);
|
|
12041
|
+
return `${base} from ${hours}h ago.`;
|
|
12042
|
+
}
|
|
12043
|
+
async function revalidateIndex({
|
|
12044
|
+
apiUrl,
|
|
12045
|
+
fetchImpl,
|
|
12046
|
+
readEntry,
|
|
12047
|
+
writeEntry,
|
|
12048
|
+
pulseTimeoutMs = PULSE_TIMEOUT_MS,
|
|
12049
|
+
indexTimeoutWarmMs = INDEX_TIMEOUT_WARM_MS,
|
|
12050
|
+
indexTimeoutColdMs = INDEX_TIMEOUT_COLD_MS
|
|
12051
|
+
}) {
|
|
12052
|
+
let entry = {};
|
|
12053
|
+
try {
|
|
12054
|
+
entry = readEntry() ?? {};
|
|
12055
|
+
} catch {
|
|
12056
|
+
}
|
|
12057
|
+
const cached = usableIndex(entry.index) ? entry.index : null;
|
|
12058
|
+
const cachedAt = cached && typeof entry.ts === "number" ? entry.ts : null;
|
|
12059
|
+
const indexTimeoutMs = cached ? indexTimeoutWarmMs : indexTimeoutColdMs;
|
|
12060
|
+
const headers = { Accept: "application/json" };
|
|
12061
|
+
if (cached && typeof entry.indexETag === "string" && entry.indexETag !== "") {
|
|
12062
|
+
headers["If-None-Match"] = entry.indexETag;
|
|
12063
|
+
}
|
|
12064
|
+
const pulseCall = deadlineFetch(fetchImpl, `${apiUrl}/api/pulse`, {
|
|
12065
|
+
headers: { Accept: "application/json" },
|
|
12066
|
+
timeoutMs: pulseTimeoutMs
|
|
12067
|
+
});
|
|
12068
|
+
const speculative = deadlineFetch(fetchImpl, `${apiUrl}/api/index`, {
|
|
12069
|
+
headers,
|
|
12070
|
+
timeoutMs: indexTimeoutMs
|
|
12071
|
+
});
|
|
12072
|
+
const speculativeSettled = speculative.promise.then(
|
|
12073
|
+
(res2) => ({ ok: true, res: res2 }),
|
|
12074
|
+
(err) => ({ ok: false, err })
|
|
12075
|
+
);
|
|
12076
|
+
let latestPostedAt = null;
|
|
12077
|
+
try {
|
|
12078
|
+
const res2 = await pulseCall.promise;
|
|
12079
|
+
if (res2.ok) {
|
|
12080
|
+
const pulse = await res2.json();
|
|
12081
|
+
latestPostedAt = pulse?.latestPostedAt ?? null;
|
|
12082
|
+
}
|
|
12083
|
+
} catch {
|
|
12084
|
+
}
|
|
12085
|
+
const bust = latestPostedAt !== null && entry.latestPostedAt !== latestPostedAt;
|
|
12086
|
+
let outcome;
|
|
12087
|
+
let reachedOrigin = false;
|
|
12088
|
+
if (bust) {
|
|
12089
|
+
speculative.abort();
|
|
12090
|
+
const bustCall = deadlineFetch(fetchImpl, `${apiUrl}/api/index`, {
|
|
12091
|
+
// `no-cache` forces revalidation against the origin; the validator rides along
|
|
12092
|
+
// so a genuinely unchanged body still costs a 304 rather than a full download.
|
|
12093
|
+
headers: { ...headers, "Cache-Control": "no-cache" },
|
|
12094
|
+
timeoutMs: indexTimeoutMs
|
|
12095
|
+
});
|
|
12096
|
+
try {
|
|
12097
|
+
outcome = { ok: true, res: await bustCall.promise };
|
|
12098
|
+
reachedOrigin = true;
|
|
12099
|
+
} catch (err) {
|
|
12100
|
+
const spec = await speculativeSettled;
|
|
12101
|
+
outcome = spec.ok ? spec : { ok: false, err };
|
|
12102
|
+
}
|
|
12103
|
+
} else {
|
|
12104
|
+
outcome = await speculativeSettled;
|
|
12105
|
+
}
|
|
12106
|
+
const fellBack = (reason) => cached ? { index: cached, source: "cache-fallback", reason, cachedAt } : { index: null, source: "none", reason, cachedAt };
|
|
12107
|
+
if (!outcome.ok) {
|
|
12108
|
+
const err = outcome.err;
|
|
12109
|
+
return fellBack(err instanceof Error ? err.message : String(err));
|
|
12110
|
+
}
|
|
12111
|
+
const res = outcome.res;
|
|
12112
|
+
try {
|
|
12113
|
+
if (res.status === 304 && cached) {
|
|
12114
|
+
if (reachedOrigin && latestPostedAt && latestPostedAt !== entry.latestPostedAt) {
|
|
12115
|
+
try {
|
|
12116
|
+
writeEntry({ latestPostedAt });
|
|
12117
|
+
} catch {
|
|
12118
|
+
}
|
|
12119
|
+
}
|
|
12120
|
+
return { index: cached, source: "cache-304", reason: null, cachedAt };
|
|
12121
|
+
}
|
|
12122
|
+
if (!res.ok) {
|
|
12123
|
+
return fellBack(`/api/index returned ${res.status}`);
|
|
12124
|
+
}
|
|
12125
|
+
const body = await res.json();
|
|
12126
|
+
if (!usableIndex(body)) {
|
|
12127
|
+
return fellBack("/api/index returned a body that will not render");
|
|
12128
|
+
}
|
|
12129
|
+
const patch = { index: body, indexETag: res.headers.get("etag") ?? "" };
|
|
12130
|
+
if (reachedOrigin && latestPostedAt) patch.latestPostedAt = latestPostedAt;
|
|
12131
|
+
try {
|
|
12132
|
+
writeEntry(patch);
|
|
12133
|
+
} catch {
|
|
12134
|
+
}
|
|
12135
|
+
return { index: body, source: "fresh", reason: null, cachedAt };
|
|
12136
|
+
} catch (err) {
|
|
12137
|
+
return fellBack(err instanceof Error ? err.message : String(err));
|
|
12138
|
+
}
|
|
12139
|
+
}
|
|
12026
12140
|
|
|
12027
12141
|
// bin/sanitize.js
|
|
12028
12142
|
var WHITESPACE_CONTROLS = /[\t\n\v\f\r]+/g;
|
|
@@ -12178,14 +12292,20 @@ function readIndexCache() {
|
|
|
12178
12292
|
return null;
|
|
12179
12293
|
}
|
|
12180
12294
|
}
|
|
12181
|
-
async function fetchIndex() {
|
|
12182
|
-
const
|
|
12183
|
-
|
|
12184
|
-
|
|
12185
|
-
|
|
12186
|
-
|
|
12187
|
-
|
|
12188
|
-
|
|
12295
|
+
async function fetchIndex(quiet = false) {
|
|
12296
|
+
const { index, reason, source, cachedAt } = await revalidateIndex({
|
|
12297
|
+
apiUrl: API_URL2,
|
|
12298
|
+
fetchImpl: (url, init) => fetch(url, init),
|
|
12299
|
+
readEntry: readCacheEntry,
|
|
12300
|
+
writeEntry: updateIndexCache
|
|
12301
|
+
});
|
|
12302
|
+
if (!quiet) {
|
|
12303
|
+
const notice = cacheFallbackNotice(source, cachedAt);
|
|
12304
|
+
if (notice) process.stderr.write(`${notice}
|
|
12305
|
+
`);
|
|
12306
|
+
}
|
|
12307
|
+
if (index) return index;
|
|
12308
|
+
throw new Error(reason ?? "/api/index returned no usable index");
|
|
12189
12309
|
}
|
|
12190
12310
|
function prompt2(question) {
|
|
12191
12311
|
const rl = createInterface2({ input: process.stdin, output: process.stdout });
|
|
@@ -12326,7 +12446,7 @@ function classifyEmptyStatus({ preFilterCount, postFilterCount, priced }) {
|
|
|
12326
12446
|
}
|
|
12327
12447
|
async function getBounties({ quiet = false, offline = false, priced = PRICED_ONLY } = {}) {
|
|
12328
12448
|
if (!quiet) console.log(`Fetching bounty index from ${API_URL2}/api/index...`);
|
|
12329
|
-
const index = offline ? readIndexCache() : await fetchIndex();
|
|
12449
|
+
const index = offline ? readIndexCache() : await fetchIndex(quiet);
|
|
12330
12450
|
if (offline && !index) return { status: "no-cache" };
|
|
12331
12451
|
let bounties = (index.jobs ?? []).filter((j) => j.source === "bounty");
|
|
12332
12452
|
if (PRICED_ONLY) bounties = bounties.filter((j) => j.bounty?.amountUSD != null);
|
|
@@ -12378,10 +12498,9 @@ async function run() {
|
|
|
12378
12498
|
try {
|
|
12379
12499
|
if (args.length === 0) {
|
|
12380
12500
|
try {
|
|
12381
|
-
const { readCacheEntry: readCacheEntry2 } = await Promise.resolve().then(() => (init_cache_store(), cache_store_exports));
|
|
12382
12501
|
const { getSurfaceLeadOverride: getSurfaceLeadOverride2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
12383
12502
|
const { founderRows: founderRows2, resolveSurfaceLead: resolveSurfaceLead2 } = await Promise.resolve().then(() => (init_founder_surface(), founder_surface_exports));
|
|
12384
|
-
const surface = (
|
|
12503
|
+
const surface = (readCacheEntry() ?? {}).founderSurface;
|
|
12385
12504
|
if (resolveSurfaceLead2(getSurfaceLeadOverride2(), surface) === "founder") {
|
|
12386
12505
|
const rows = founderRows2(surface, "bounties");
|
|
12387
12506
|
console.log("\n\u26A1 Your TerminalHire postings\n");
|
|
@@ -12449,12 +12568,11 @@ async function run() {
|
|
|
12449
12568
|
}
|
|
12450
12569
|
try {
|
|
12451
12570
|
const { acknowledgeFounderPaid: acknowledgeFounderPaid2 } = await Promise.resolve().then(() => (init_founder_paid_badge(), founder_paid_badge_exports));
|
|
12452
|
-
const { readCacheEntry: readCacheEntry2 } = await Promise.resolve().then(() => (init_cache_store(), cache_store_exports));
|
|
12453
12571
|
updateIndexCache({
|
|
12454
12572
|
founderPaid: acknowledgeFounderPaid2({
|
|
12455
12573
|
shown,
|
|
12456
12574
|
open: bounties,
|
|
12457
|
-
previous: (
|
|
12575
|
+
previous: (readCacheEntry() ?? {}).founderPaid
|
|
12458
12576
|
})
|
|
12459
12577
|
});
|
|
12460
12578
|
} catch {
|
package/dist/bin/jpi-claim.js
CHANGED
|
@@ -33910,6 +33910,7 @@ import {
|
|
|
33910
33910
|
mkdirSync as mkdirSync8,
|
|
33911
33911
|
existsSync as existsSync13,
|
|
33912
33912
|
lstatSync as lstatSync3,
|
|
33913
|
+
realpathSync as realpathSync2,
|
|
33913
33914
|
rmSync as rmSync9,
|
|
33914
33915
|
readdirSync as readdirSync3
|
|
33915
33916
|
} from "fs";
|
|
@@ -34503,6 +34504,47 @@ function resolveSubmitWorktree({ override, cwdToplevel, recorded, recordedIsGit
|
|
|
34503
34504
|
terminalhire claim attach <id> --worktree ${recorded} --branch <branch>`
|
|
34504
34505
|
};
|
|
34505
34506
|
}
|
|
34507
|
+
function inferSubmitClaim({
|
|
34508
|
+
claims,
|
|
34509
|
+
activeIds,
|
|
34510
|
+
cwdToplevel,
|
|
34511
|
+
cwd,
|
|
34512
|
+
branch,
|
|
34513
|
+
canonicalize: canonicalize2 = (p) => p
|
|
34514
|
+
}) {
|
|
34515
|
+
const here = [cwdToplevel, cwd].filter((d) => typeof d === "string" && d !== "").map((d) => canonicalize2(d));
|
|
34516
|
+
const isHere = (dir) => typeof dir === "string" && dir !== "" && here.includes(canonicalize2(dir));
|
|
34517
|
+
const tiers = [
|
|
34518
|
+
(c) => isHere(c.worktreePath),
|
|
34519
|
+
(c) => isHere(sliceWorkDirFor(c.id)),
|
|
34520
|
+
(c) => typeof branch === "string" && branch !== "" && c.branch === branch
|
|
34521
|
+
];
|
|
34522
|
+
for (const matches of tiers) {
|
|
34523
|
+
const hits = (Array.isArray(claims) ? claims : []).filter(matches);
|
|
34524
|
+
if (hits.length === 0) continue;
|
|
34525
|
+
if (hits.length === 1) return { claim: hits[0] };
|
|
34526
|
+
const live = hits.filter((c) => activeIds.has(c.id));
|
|
34527
|
+
if (live.length === 1) return { claim: live[0] };
|
|
34528
|
+
return { candidates: live.length > 0 ? live : hits };
|
|
34529
|
+
}
|
|
34530
|
+
return { candidates: [] };
|
|
34531
|
+
}
|
|
34532
|
+
function canonicalPath(p) {
|
|
34533
|
+
if (typeof p !== "string" || p === "") return p;
|
|
34534
|
+
const resolved = pathResolve(p);
|
|
34535
|
+
try {
|
|
34536
|
+
return realpathSync2.native(resolved);
|
|
34537
|
+
} catch {
|
|
34538
|
+
return resolved;
|
|
34539
|
+
}
|
|
34540
|
+
}
|
|
34541
|
+
async function gitToplevelOf(dir) {
|
|
34542
|
+
try {
|
|
34543
|
+
return await sh("git", ["-C", dir, "rev-parse", "--show-toplevel"]);
|
|
34544
|
+
} catch {
|
|
34545
|
+
return null;
|
|
34546
|
+
}
|
|
34547
|
+
}
|
|
34506
34548
|
function pickBodySource({ bodyFileFlag, noBody, prBodyExists }) {
|
|
34507
34549
|
if (bodyFileFlag) return "body-file";
|
|
34508
34550
|
if (!noBody && prBodyExists) return "pr-body";
|
|
@@ -37374,10 +37416,44 @@ async function cmdSubmit(id, flags = {}) {
|
|
|
37374
37416
|
const worktreeOverride = flags.worktree;
|
|
37375
37417
|
const claims = await Promise.resolve().then(() => (init_claims(), claims_exports));
|
|
37376
37418
|
if (!id) {
|
|
37377
|
-
|
|
37378
|
-
|
|
37379
|
-
|
|
37380
|
-
|
|
37419
|
+
const dir = worktreeOverride ?? process.cwd();
|
|
37420
|
+
const cwdToplevel2 = await gitToplevelOf(dir);
|
|
37421
|
+
let branch = null;
|
|
37422
|
+
if (cwdToplevel2 !== null) {
|
|
37423
|
+
try {
|
|
37424
|
+
branch = await sh("git", ["-C", cwdToplevel2, "rev-parse", "--abbrev-ref", "HEAD"]);
|
|
37425
|
+
} catch {
|
|
37426
|
+
}
|
|
37427
|
+
}
|
|
37428
|
+
const inferred = inferSubmitClaim({
|
|
37429
|
+
claims: claims.readClaims(),
|
|
37430
|
+
activeIds: new Set(claims.listClaims({ active: true }).map((c) => c.id)),
|
|
37431
|
+
cwdToplevel: cwdToplevel2,
|
|
37432
|
+
cwd: pathResolve(dir),
|
|
37433
|
+
branch,
|
|
37434
|
+
canonicalize: canonicalPath
|
|
37435
|
+
});
|
|
37436
|
+
if (inferred.claim) {
|
|
37437
|
+
id = inferred.claim.id;
|
|
37438
|
+
console.log(` claim inferred from ${cwdToplevel2 ?? pathResolve(dir)}: ${id}`);
|
|
37439
|
+
} else if (inferred.candidates.length > 0) {
|
|
37440
|
+
console.error(
|
|
37441
|
+
`terminalhire claim: ${inferred.candidates.length} claims match this directory or branch \u2014 say which one.
|
|
37442
|
+
` + inferred.candidates.map((c) => ` ${c.id} ${c.state} ${c.title}`).join("\n") + // No `--yes` in this hint, deliberately. The one place that names the flag
|
|
37443
|
+
// says in the same breath that a human must type it and an agent never may;
|
|
37444
|
+
// a bare copyable line here would hand out the flag without the sentence.
|
|
37445
|
+
`
|
|
37446
|
+
terminalhire claim submit <id>`
|
|
37447
|
+
);
|
|
37448
|
+
process.exit(1);
|
|
37449
|
+
} else {
|
|
37450
|
+
console.error(
|
|
37451
|
+
`terminalhire claim: no claim is recorded for this directory or branch, so there is nothing to infer.
|
|
37452
|
+
Usage: terminalhire claim submit <id> [--worktree <path>] [--yes] [--body-file <path>] [--no-body] [--title <t>] [--no-closes]
|
|
37453
|
+
Your claims: terminalhire claim list --active`
|
|
37454
|
+
);
|
|
37455
|
+
process.exit(1);
|
|
37456
|
+
}
|
|
37381
37457
|
}
|
|
37382
37458
|
const claim = claims.findClaim(id);
|
|
37383
37459
|
if (!claim) {
|
|
@@ -37396,17 +37472,10 @@ async function cmdSubmit(id, flags = {}) {
|
|
|
37396
37472
|
);
|
|
37397
37473
|
process.exit(1);
|
|
37398
37474
|
}
|
|
37399
|
-
const toplevelOf = async (dir) => {
|
|
37400
|
-
try {
|
|
37401
|
-
return await sh("git", ["-C", dir, "rev-parse", "--show-toplevel"]);
|
|
37402
|
-
} catch {
|
|
37403
|
-
return null;
|
|
37404
|
-
}
|
|
37405
|
-
};
|
|
37406
37475
|
const recorded = claim.worktreePath;
|
|
37407
|
-
const override = worktreeOverride === void 0 ? void 0 : await
|
|
37408
|
-
const cwdToplevel = await
|
|
37409
|
-
const recordedIsGit = await
|
|
37476
|
+
const override = worktreeOverride === void 0 ? void 0 : await gitToplevelOf(worktreeOverride);
|
|
37477
|
+
const cwdToplevel = await gitToplevelOf(process.cwd());
|
|
37478
|
+
const recordedIsGit = await gitToplevelOf(recorded) === recorded;
|
|
37410
37479
|
const resolved = resolveSubmitWorktree({ override, cwdToplevel, recorded, recordedIsGit });
|
|
37411
37480
|
if (resolved.error) {
|
|
37412
37481
|
console.error(`terminalhire claim: ${resolved.error}`);
|
|
@@ -38466,6 +38535,7 @@ export {
|
|
|
38466
38535
|
fmtContestedWarning,
|
|
38467
38536
|
founderClaimStanding,
|
|
38468
38537
|
founderPostingIdOf,
|
|
38538
|
+
inferSubmitClaim,
|
|
38469
38539
|
isContested,
|
|
38470
38540
|
isStrayArgShortRefClaim,
|
|
38471
38541
|
isTerminalRunStatus,
|