terminalhire 0.42.3 → 0.42.5
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 +84 -1
- package/dist/bin/founder-bounty-notify.js +117 -0
- package/dist/bin/jpi-bounties.js +6 -1
- package/dist/bin/jpi-chat-read.js +2 -1
- package/dist/bin/jpi-chat.js +2 -1
- package/dist/bin/jpi-claim.js +150 -30
- package/dist/bin/jpi-config.js +168 -21
- package/dist/bin/jpi-contribute.js +2 -1
- package/dist/bin/jpi-dispatch.js +632 -91
- package/dist/bin/jpi-hub.js +3 -1
- package/dist/bin/jpi-inbox.js +2 -1
- package/dist/bin/jpi-jobs.js +55 -22
- package/dist/bin/jpi-link.js +2 -1
- package/dist/bin/jpi-login.js +2 -1
- package/dist/bin/jpi-mcp-chat.js +2 -1
- package/dist/bin/jpi-mcp.js +159 -34
- package/dist/bin/jpi-post.js +251 -33
- package/dist/bin/jpi-refresh.js +276 -39
- package/dist/bin/jpi-spinner.js +4 -0
- package/dist/bin/peer-connect-prompt.js +2 -1
- package/dist/bin/pulse-prompt.js +2 -1
- package/dist/bin/spinner-render.js +45 -21
- package/dist/bin/spinner.js +45 -21
- package/dist/src/config.js +6 -1
- package/dist/src/link.js +2 -1
- package/dist/src/posting-drafts.js +234 -20
- package/package.json +2 -2
|
@@ -514,7 +514,7 @@ var CLAIM_PUSH_AUTO_MARKER = join5(TERMINALHIRE_DIR4, "claim-push-auto.json");
|
|
|
514
514
|
var CLAIM_PUSH_TOKEN_FILE = join5(TERMINALHIRE_DIR4, "claim-push-token.enc");
|
|
515
515
|
var CLAIM_PUSH_MANUAL_MARKER = join5(TERMINALHIRE_DIR4, "claim-push.json");
|
|
516
516
|
var CLAIM_SYNC_BASE = "https://terminalhire.com";
|
|
517
|
-
var AUTO_CONSENT_VERSION =
|
|
517
|
+
var AUTO_CONSENT_VERSION = 3;
|
|
518
518
|
var AUTO_PUSH_THROTTLE_MS = 24 * 60 * 60 * 1e3;
|
|
519
519
|
async function writePushTokenEnc(rawToken) {
|
|
520
520
|
ensureStateDirForSecret(TERMINALHIRE_DIR4);
|
|
@@ -617,6 +617,82 @@ async function shouldNudgeUnpushed() {
|
|
|
617
617
|
return false;
|
|
618
618
|
}
|
|
619
619
|
}
|
|
620
|
+
var CLAIM_HEARTBEAT_FILE = join5(TERMINALHIRE_DIR4, "claim-heartbeat.json");
|
|
621
|
+
var HEARTBEAT_MIN_INTERVAL_MS = 3e4;
|
|
622
|
+
var HEARTBEAT_MIN_CONSENT_VERSION = 3;
|
|
623
|
+
function readHeartbeatState() {
|
|
624
|
+
try {
|
|
625
|
+
return existsSync5(CLAIM_HEARTBEAT_FILE) ? JSON.parse(readFileSync4(CLAIM_HEARTBEAT_FILE, "utf8")) : {};
|
|
626
|
+
} catch {
|
|
627
|
+
return {};
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
function recordHeartbeatBeat(claimId, at) {
|
|
631
|
+
try {
|
|
632
|
+
ensureStateDir(TERMINALHIRE_DIR4);
|
|
633
|
+
const state = readHeartbeatState();
|
|
634
|
+
state[claimId] = at;
|
|
635
|
+
writeFileSync4(CLAIM_HEARTBEAT_FILE, JSON.stringify(state, null, 2) + "\n", "utf8");
|
|
636
|
+
} catch {
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
function heartbeatGate(params) {
|
|
640
|
+
const {
|
|
641
|
+
autoMarkerExists,
|
|
642
|
+
tokenFileExists,
|
|
643
|
+
consentVersion,
|
|
644
|
+
bountyId,
|
|
645
|
+
claimId,
|
|
646
|
+
lastBeatAt,
|
|
647
|
+
now = Date.now(),
|
|
648
|
+
minIntervalMs = HEARTBEAT_MIN_INTERVAL_MS
|
|
649
|
+
} = params;
|
|
650
|
+
if (typeof bountyId !== "string" || bountyId.trim() === "") {
|
|
651
|
+
return { beat: false, reason: "no-server-ids" };
|
|
652
|
+
}
|
|
653
|
+
if (typeof claimId !== "string" || claimId.trim() === "") {
|
|
654
|
+
return { beat: false, reason: "no-server-ids" };
|
|
655
|
+
}
|
|
656
|
+
if (!autoMarkerExists || !tokenFileExists) {
|
|
657
|
+
return { beat: false, reason: "not-opted-in" };
|
|
658
|
+
}
|
|
659
|
+
if (!(Number.isInteger(consentVersion) && consentVersion >= HEARTBEAT_MIN_CONSENT_VERSION)) {
|
|
660
|
+
return { beat: false, reason: "consent-predates-presence" };
|
|
661
|
+
}
|
|
662
|
+
const last = lastBeatAt ? Date.parse(lastBeatAt) : NaN;
|
|
663
|
+
if (!Number.isNaN(last) && now - last < minIntervalMs) {
|
|
664
|
+
return { beat: false, reason: "throttled" };
|
|
665
|
+
}
|
|
666
|
+
return { beat: true, reason: "ok" };
|
|
667
|
+
}
|
|
668
|
+
async function postClaimHeartbeat({ bountyId, claimId, now = Date.now() } = {}) {
|
|
669
|
+
try {
|
|
670
|
+
const marker = readAutoMarker();
|
|
671
|
+
const gate = heartbeatGate({
|
|
672
|
+
autoMarkerExists: Boolean(marker && marker.autoConsentedAt),
|
|
673
|
+
tokenFileExists: existsSync5(CLAIM_PUSH_TOKEN_FILE),
|
|
674
|
+
consentVersion: marker?.version,
|
|
675
|
+
bountyId,
|
|
676
|
+
claimId,
|
|
677
|
+
lastBeatAt: readHeartbeatState()[claimId] ?? null,
|
|
678
|
+
now
|
|
679
|
+
});
|
|
680
|
+
if (!gate.beat) return { beat: false, reason: gate.reason };
|
|
681
|
+
const token = await readPushTokenEnc();
|
|
682
|
+
if (!token) return { beat: false, reason: "unreadable-token" };
|
|
683
|
+
const res = await fetch(`${CLAIM_SYNC_BASE}/api/claim/heartbeat`, {
|
|
684
|
+
method: "POST",
|
|
685
|
+
headers: { "Content-Type": "application/json" },
|
|
686
|
+
body: JSON.stringify({ bountyId, claimId, pushToken: token }),
|
|
687
|
+
signal: AbortSignal.timeout(5e3)
|
|
688
|
+
});
|
|
689
|
+
if (!res.ok) return { beat: false, reason: `server-${res.status}` };
|
|
690
|
+
recordHeartbeatBeat(claimId, new Date(now).toISOString());
|
|
691
|
+
return { beat: true, reason: "ok" };
|
|
692
|
+
} catch {
|
|
693
|
+
return { beat: false, reason: "failed" };
|
|
694
|
+
}
|
|
695
|
+
}
|
|
620
696
|
async function runBackgroundClaimPush({ now = Date.now() } = {}) {
|
|
621
697
|
try {
|
|
622
698
|
if (!existsSync5(CLAIM_PUSH_AUTO_MARKER) || !existsSync5(CLAIM_PUSH_TOKEN_FILE)) {
|
|
@@ -666,15 +742,22 @@ async function runBackgroundClaimPush({ now = Date.now() } = {}) {
|
|
|
666
742
|
export {
|
|
667
743
|
AUTO_CONSENT_VERSION,
|
|
668
744
|
AUTO_PUSH_THROTTLE_MS,
|
|
745
|
+
CLAIM_HEARTBEAT_FILE,
|
|
669
746
|
CLAIM_PUSH_AUTO_MARKER,
|
|
670
747
|
CLAIM_PUSH_MANUAL_MARKER,
|
|
671
748
|
CLAIM_PUSH_TOKEN_FILE,
|
|
749
|
+
HEARTBEAT_MIN_CONSENT_VERSION,
|
|
750
|
+
HEARTBEAT_MIN_INTERVAL_MS,
|
|
672
751
|
backgroundPushGate,
|
|
673
752
|
clearAutoMarker,
|
|
674
753
|
clearPushTokenEnc,
|
|
675
754
|
computeSnapshotHash,
|
|
755
|
+
heartbeatGate,
|
|
756
|
+
postClaimHeartbeat,
|
|
676
757
|
readAutoMarker,
|
|
758
|
+
readHeartbeatState,
|
|
677
759
|
readPushTokenEnc,
|
|
760
|
+
recordHeartbeatBeat,
|
|
678
761
|
runBackgroundClaimPush,
|
|
679
762
|
shouldNudgeUnpushed,
|
|
680
763
|
unpushedNudgeGate,
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
// bin/founder-bounty-notify.js
|
|
2
|
+
import { spawn } from "child_process";
|
|
3
|
+
|
|
4
|
+
// bin/founder-pin.js
|
|
5
|
+
function isPinnedFounderBounty(j) {
|
|
6
|
+
return j?.bounty?.bountySource === "founder" && j?.bounty?.claimable === true;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
// bin/founder-paid-badge.js
|
|
10
|
+
function openPaidIds(index) {
|
|
11
|
+
const jobs = index && index.jobs || [];
|
|
12
|
+
const ids = [];
|
|
13
|
+
for (const j of jobs) {
|
|
14
|
+
if (!j || typeof j.id !== "string") continue;
|
|
15
|
+
if (isPinnedFounderBounty(j)) ids.push(j.id);
|
|
16
|
+
}
|
|
17
|
+
return [...new Set(ids)].sort();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// bin/founder-bounty-notify.js
|
|
21
|
+
function nextFounderBountyNotifyState(index, previous) {
|
|
22
|
+
const open = openPaidIds(index);
|
|
23
|
+
const prior = previous && Array.isArray(previous.ids) ? previous.ids : null;
|
|
24
|
+
if (prior === null) {
|
|
25
|
+
return { ids: open, fire: [], seeded: true };
|
|
26
|
+
}
|
|
27
|
+
const seen = new Set(prior);
|
|
28
|
+
const fire = open.filter((id) => !seen.has(id));
|
|
29
|
+
const ids = [.../* @__PURE__ */ new Set([...prior.filter((id) => open.includes(id)), ...fire])].sort();
|
|
30
|
+
return { ids, fire, seeded: false };
|
|
31
|
+
}
|
|
32
|
+
function formatFounderBountyNotifyBody(index, fireIds) {
|
|
33
|
+
const jobs = index && index.jobs || [];
|
|
34
|
+
const byId = /* @__PURE__ */ new Map();
|
|
35
|
+
for (const j of jobs) {
|
|
36
|
+
if (j && typeof j.id === "string") byId.set(j.id, j);
|
|
37
|
+
}
|
|
38
|
+
if (fireIds.length === 1) {
|
|
39
|
+
const j = byId.get(fireIds[0]);
|
|
40
|
+
const amount = j && j.bounty && typeof j.bounty.amountUSD === "number" ? j.bounty.amountUSD : null;
|
|
41
|
+
const price = typeof amount === "number" && Number.isFinite(amount) && amount > 0 ? `$${Math.round(amount)} ` : "";
|
|
42
|
+
return `${price}founder bounty available \u2014 run: terminalhire bounties`;
|
|
43
|
+
}
|
|
44
|
+
return `${fireIds.length} founder bounties available \u2014 run: terminalhire bounties`;
|
|
45
|
+
}
|
|
46
|
+
function escapeAppleScriptString(s) {
|
|
47
|
+
return String(s).replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
48
|
+
}
|
|
49
|
+
function displayLocalNotification({
|
|
50
|
+
title = "Terminalhire",
|
|
51
|
+
body,
|
|
52
|
+
platform = process.platform,
|
|
53
|
+
spawnFn = spawn
|
|
54
|
+
} = {}) {
|
|
55
|
+
if (!body || typeof body !== "string") return false;
|
|
56
|
+
try {
|
|
57
|
+
if (platform === "darwin") {
|
|
58
|
+
const t = escapeAppleScriptString(title);
|
|
59
|
+
const b = escapeAppleScriptString(body);
|
|
60
|
+
const child = spawnFn("osascript", ["-e", `display notification "${b}" with title "${t}"`], {
|
|
61
|
+
detached: true,
|
|
62
|
+
stdio: "ignore"
|
|
63
|
+
});
|
|
64
|
+
if (child && typeof child.unref === "function") child.unref();
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
if (platform === "win32") {
|
|
68
|
+
const safeTitle = String(title).replace(/'/g, "''");
|
|
69
|
+
const safeBody = String(body).replace(/'/g, "''");
|
|
70
|
+
const ps = `
|
|
71
|
+
Add-Type -AssemblyName System.Windows.Forms
|
|
72
|
+
$n = New-Object System.Windows.Forms.NotifyIcon
|
|
73
|
+
$n.Icon = [System.Drawing.SystemIcons]::Information
|
|
74
|
+
$n.Visible = $true
|
|
75
|
+
$n.ShowBalloonTip(8000, '${safeTitle}', '${safeBody}', [System.Windows.Forms.ToolTipIcon]::Info)
|
|
76
|
+
Start-Sleep -Seconds 9
|
|
77
|
+
$n.Dispose()
|
|
78
|
+
`.trim();
|
|
79
|
+
const child = spawnFn("powershell.exe", ["-NoProfile", "-Command", ps], {
|
|
80
|
+
detached: true,
|
|
81
|
+
stdio: "ignore",
|
|
82
|
+
windowsHide: true
|
|
83
|
+
});
|
|
84
|
+
if (child && typeof child.unref === "function") child.unref();
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
return false;
|
|
88
|
+
} catch {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
function maybeNotifyFounderBounties({
|
|
93
|
+
index,
|
|
94
|
+
previous,
|
|
95
|
+
optedIn,
|
|
96
|
+
display = displayLocalNotification
|
|
97
|
+
} = {}) {
|
|
98
|
+
if (!optedIn) {
|
|
99
|
+
const ids = previous && Array.isArray(previous.ids) ? [...previous.ids].sort() : [];
|
|
100
|
+
return { state: { ids }, fired: [] };
|
|
101
|
+
}
|
|
102
|
+
const next = nextFounderBountyNotifyState(index, previous);
|
|
103
|
+
if (next.fire.length > 0) {
|
|
104
|
+
display({
|
|
105
|
+
title: "Terminalhire",
|
|
106
|
+
body: formatFounderBountyNotifyBody(index, next.fire)
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
return { state: { ids: next.ids }, fired: next.fire };
|
|
110
|
+
}
|
|
111
|
+
export {
|
|
112
|
+
displayLocalNotification,
|
|
113
|
+
escapeAppleScriptString,
|
|
114
|
+
formatFounderBountyNotifyBody,
|
|
115
|
+
maybeNotifyFounderBounties,
|
|
116
|
+
nextFounderBountyNotifyState
|
|
117
|
+
};
|
package/dist/bin/jpi-bounties.js
CHANGED
|
@@ -11546,6 +11546,7 @@ __export(config_exports, {
|
|
|
11546
11546
|
getSurfaceMix: () => getSurfaceMix,
|
|
11547
11547
|
isBetaOptIn: () => isBetaOptIn,
|
|
11548
11548
|
isContributeEnabled: () => isContributeEnabled,
|
|
11549
|
+
isFounderBountyNotifyEnabled: () => isFounderBountyNotifyEnabled,
|
|
11549
11550
|
isInboundNudgeMuted: () => isInboundNudgeMuted,
|
|
11550
11551
|
isPeerConnectEnabled: () => isPeerConnectEnabled,
|
|
11551
11552
|
parseNudgeMode: () => parseNudgeMode,
|
|
@@ -11630,6 +11631,9 @@ function isContributeEnabled() {
|
|
|
11630
11631
|
function isBetaOptIn() {
|
|
11631
11632
|
return readConfig().betaOptIn === true;
|
|
11632
11633
|
}
|
|
11634
|
+
function isFounderBountyNotifyEnabled() {
|
|
11635
|
+
return readConfig().founderBountyNotify === true;
|
|
11636
|
+
}
|
|
11633
11637
|
var TERMINALHIRE_DIR6, CONFIG_FILE, DEFAULT_CONFIG;
|
|
11634
11638
|
var init_config = __esm({
|
|
11635
11639
|
"src/config.ts"() {
|
|
@@ -11651,7 +11655,8 @@ var init_config = __esm({
|
|
|
11651
11655
|
lastFullFeedbackAt: null,
|
|
11652
11656
|
lastPulseAskAt: null,
|
|
11653
11657
|
pulseDisclosed: false,
|
|
11654
|
-
mix: "balanced"
|
|
11658
|
+
mix: "balanced",
|
|
11659
|
+
founderBountyNotify: false
|
|
11655
11660
|
};
|
|
11656
11661
|
}
|
|
11657
11662
|
});
|
package/dist/bin/jpi-chat.js
CHANGED