terminalhire 0.40.12 → 0.41.0
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-claim.js +314 -126
- package/dist/bin/jpi-dispatch.js +345 -81
- package/dist/bin/jpi-mcp.js +331 -136
- package/dist/bin/jpi-statusline-launch.js +6 -0
- package/package.json +1 -1
- package/statusline-install.js +64 -3
package/dist/bin/jpi-claim.js
CHANGED
|
@@ -26129,7 +26129,6 @@ var TERMINALHIRE_DIR5 = process.env.TERMINALHIRE_DIR || join7(homedir5(), ".term
|
|
|
26129
26129
|
var CLAIM_PUSH_AUTO_MARKER = join7(TERMINALHIRE_DIR5, "claim-push-auto.json");
|
|
26130
26130
|
var CLAIM_PUSH_TOKEN_FILE = join7(TERMINALHIRE_DIR5, "claim-push-token.enc");
|
|
26131
26131
|
var CLAIM_PUSH_MANUAL_MARKER = join7(TERMINALHIRE_DIR5, "claim-push.json");
|
|
26132
|
-
var CLAIM_SYNC_BASE = "https://terminalhire.com";
|
|
26133
26132
|
var AUTO_CONSENT_VERSION = 2;
|
|
26134
26133
|
var AUTO_PUSH_THROTTLE_MS = 24 * 60 * 60 * 1e3;
|
|
26135
26134
|
async function writePushTokenEnc(rawToken) {
|
|
@@ -26174,28 +26173,6 @@ function clearAutoMarker() {
|
|
|
26174
26173
|
function computeSnapshotHash(pushed) {
|
|
26175
26174
|
return createHash3("sha256").update(JSON.stringify(pushed)).digest("hex");
|
|
26176
26175
|
}
|
|
26177
|
-
function backgroundPushGate(params) {
|
|
26178
|
-
const {
|
|
26179
|
-
autoMarkerExists,
|
|
26180
|
-
tokenFileExists,
|
|
26181
|
-
lastPushedAt,
|
|
26182
|
-
now,
|
|
26183
|
-
throttleMs,
|
|
26184
|
-
currentHash,
|
|
26185
|
-
lastSnapshotHash
|
|
26186
|
-
} = params;
|
|
26187
|
-
if (!autoMarkerExists || !tokenFileExists) {
|
|
26188
|
-
return { push: false, reason: "not-opted-in" };
|
|
26189
|
-
}
|
|
26190
|
-
const last = lastPushedAt ? Date.parse(lastPushedAt) : NaN;
|
|
26191
|
-
if (!Number.isNaN(last) && now - last < throttleMs) {
|
|
26192
|
-
return { push: false, reason: "throttled" };
|
|
26193
|
-
}
|
|
26194
|
-
if (lastSnapshotHash && lastSnapshotHash === currentHash) {
|
|
26195
|
-
return { push: false, reason: "unchanged" };
|
|
26196
|
-
}
|
|
26197
|
-
return { push: true, reason: "ok" };
|
|
26198
|
-
}
|
|
26199
26176
|
function unpushedNudgeGate(params) {
|
|
26200
26177
|
const {
|
|
26201
26178
|
autoMarkerExists,
|
|
@@ -26233,50 +26210,107 @@ async function shouldNudgeUnpushed() {
|
|
|
26233
26210
|
return false;
|
|
26234
26211
|
}
|
|
26235
26212
|
}
|
|
26236
|
-
|
|
26213
|
+
|
|
26214
|
+
// bin/founder-verdict-sync.js
|
|
26215
|
+
var CLAIM_SYNC_BASE = "https://terminalhire.com";
|
|
26216
|
+
var TERMINAL = /* @__PURE__ */ new Set(["merged", "abandoned"]);
|
|
26217
|
+
function verdictState(verdict) {
|
|
26218
|
+
return verdict === "rejected" ? "abandoned" : "merged";
|
|
26219
|
+
}
|
|
26220
|
+
async function fetchFounderVerdicts(pushToken, fetchImpl = fetch) {
|
|
26221
|
+
if (typeof pushToken !== "string" || pushToken.length === 0) return null;
|
|
26237
26222
|
try {
|
|
26238
|
-
|
|
26239
|
-
return { pushed: false, reason: "not-opted-in" };
|
|
26240
|
-
}
|
|
26241
|
-
const marker = readAutoMarker();
|
|
26242
|
-
if (!marker || !marker.autoConsentedAt) return { pushed: false, reason: "not-opted-in" };
|
|
26243
|
-
const { listClaims: listClaims2, toPushedClaim: toPushedClaim2, PUSHED_CLAIM_FIELDS: PUSHED_CLAIM_FIELDS2 } = await Promise.resolve().then(() => (init_claims(), claims_exports));
|
|
26244
|
-
const pushed = listClaims2().map((c) => toPushedClaim2(c));
|
|
26245
|
-
const currentHash = computeSnapshotHash(pushed);
|
|
26246
|
-
const gate = backgroundPushGate({
|
|
26247
|
-
autoMarkerExists: true,
|
|
26248
|
-
tokenFileExists: true,
|
|
26249
|
-
lastPushedAt: marker.lastPushedAt ?? null,
|
|
26250
|
-
now,
|
|
26251
|
-
throttleMs: AUTO_PUSH_THROTTLE_MS,
|
|
26252
|
-
currentHash,
|
|
26253
|
-
lastSnapshotHash: marker.lastSnapshotHash ?? null
|
|
26254
|
-
});
|
|
26255
|
-
if (!gate.push) return { pushed: false, reason: gate.reason };
|
|
26256
|
-
const token = await readPushTokenEnc();
|
|
26257
|
-
if (!token) return { pushed: false, reason: "unreadable-token" };
|
|
26258
|
-
const consentReceipt = {
|
|
26259
|
-
consentedAt: marker.autoConsentedAt,
|
|
26260
|
-
version: AUTO_CONSENT_VERSION,
|
|
26261
|
-
fields: PUSHED_CLAIM_FIELDS2
|
|
26262
|
-
};
|
|
26263
|
-
const res = await fetch(`${CLAIM_SYNC_BASE}/api/claim-sync`, {
|
|
26223
|
+
const res = await fetchImpl(`${CLAIM_SYNC_BASE}/api/claim/verdicts`, {
|
|
26264
26224
|
method: "POST",
|
|
26265
26225
|
headers: { "Content-Type": "application/json" },
|
|
26266
|
-
body: JSON.stringify({
|
|
26267
|
-
signal: AbortSignal.timeout(
|
|
26226
|
+
body: JSON.stringify({ pushToken }),
|
|
26227
|
+
signal: AbortSignal.timeout(15e3)
|
|
26268
26228
|
});
|
|
26269
|
-
if (!res
|
|
26270
|
-
|
|
26271
|
-
|
|
26272
|
-
|
|
26273
|
-
|
|
26274
|
-
|
|
26275
|
-
|
|
26229
|
+
if (!res?.ok) return null;
|
|
26230
|
+
const body = await res.json();
|
|
26231
|
+
if (!body || !Array.isArray(body.verdicts)) return null;
|
|
26232
|
+
const verdicts = body.verdicts.filter(
|
|
26233
|
+
(v) => v && typeof v.claimId === "string" && v.claimId !== "" && (v.verdict === "accepted" || v.verdict === "rejected")
|
|
26234
|
+
);
|
|
26235
|
+
return { verdicts, latestAt: typeof body.latestAt === "string" ? body.latestAt : null };
|
|
26236
|
+
} catch {
|
|
26237
|
+
return null;
|
|
26238
|
+
}
|
|
26239
|
+
}
|
|
26240
|
+
function planVerdictTransitions(claims, verdicts, nextPolledState2) {
|
|
26241
|
+
if (!Array.isArray(claims) || !Array.isArray(verdicts)) return [];
|
|
26242
|
+
const byClaimId = /* @__PURE__ */ new Map();
|
|
26243
|
+
for (const v of verdicts) {
|
|
26244
|
+
if (v && typeof v.claimId === "string" && v.claimId !== "") byClaimId.set(v.claimId, v);
|
|
26245
|
+
}
|
|
26246
|
+
const plan = [];
|
|
26247
|
+
for (const c of claims) {
|
|
26248
|
+
const claimId = c?.approval?.claimId;
|
|
26249
|
+
if (!claimId) continue;
|
|
26250
|
+
const v = byClaimId.get(claimId);
|
|
26251
|
+
if (!v) continue;
|
|
26252
|
+
if (TERMINAL.has(c.state)) continue;
|
|
26253
|
+
const to = verdictState(v.verdict);
|
|
26254
|
+
const next = nextPolledState2(c.state, to);
|
|
26255
|
+
if (next === c.state) continue;
|
|
26256
|
+
plan.push({
|
|
26257
|
+
id: c.id,
|
|
26258
|
+
claimId,
|
|
26259
|
+
from: c.state,
|
|
26260
|
+
to: next,
|
|
26261
|
+
verdict: v.verdict,
|
|
26262
|
+
settled: v.settled === true,
|
|
26263
|
+
amountUSD: typeof c.amountUSD === "number" ? c.amountUSD : null,
|
|
26264
|
+
title: typeof c.title === "string" ? c.title : ""
|
|
26276
26265
|
});
|
|
26277
|
-
|
|
26266
|
+
}
|
|
26267
|
+
return plan;
|
|
26268
|
+
}
|
|
26269
|
+
function buildVerdictNotice(t) {
|
|
26270
|
+
if (!t || typeof t.to !== "string") return null;
|
|
26271
|
+
const amount = typeof t.amountUSD === "number" && t.amountUSD > 0 ? `$${t.amountUSD}` : null;
|
|
26272
|
+
if (t.verdict === "rejected") {
|
|
26273
|
+
return ` \u2717 founder rejected${amount ? ` \u2014 ${amount}` : ""} \xB7 claim moved to ${t.to}`;
|
|
26274
|
+
}
|
|
26275
|
+
const paid = t.settled ? " \xB7 paid" : "";
|
|
26276
|
+
return ` \u2713 founder accepted${amount ? ` \u2014 ${amount}` : ""}${paid} \xB7 claim moved to ${t.to}`;
|
|
26277
|
+
}
|
|
26278
|
+
async function syncFounderVerdicts({
|
|
26279
|
+
claimsModule,
|
|
26280
|
+
targets,
|
|
26281
|
+
readPushTokenEnc: readPushTokenEnc2,
|
|
26282
|
+
fetchImpl = fetch,
|
|
26283
|
+
log = console.log
|
|
26284
|
+
} = {}) {
|
|
26285
|
+
const quiet = { checked: false, unavailable: false, applied: [] };
|
|
26286
|
+
try {
|
|
26287
|
+
const founderTargets = (targets ?? []).filter(
|
|
26288
|
+
(c) => Boolean(c?.approval) && !TERMINAL.has(c.state)
|
|
26289
|
+
);
|
|
26290
|
+
if (founderTargets.length === 0) return quiet;
|
|
26291
|
+
let pushToken = null;
|
|
26292
|
+
try {
|
|
26293
|
+
pushToken = await readPushTokenEnc2();
|
|
26294
|
+
} catch {
|
|
26295
|
+
pushToken = null;
|
|
26296
|
+
}
|
|
26297
|
+
if (!pushToken) return quiet;
|
|
26298
|
+
const res = await fetchFounderVerdicts(pushToken, fetchImpl);
|
|
26299
|
+
if (!res) return { checked: false, unavailable: true, applied: [] };
|
|
26300
|
+
const plan = planVerdictTransitions(founderTargets, res.verdicts, claimsModule.nextPolledState);
|
|
26301
|
+
const applied = [];
|
|
26302
|
+
for (const t of plan) {
|
|
26303
|
+
try {
|
|
26304
|
+
claimsModule.updateClaim(t.id, { state: t.to });
|
|
26305
|
+
applied.push(t);
|
|
26306
|
+
const line = buildVerdictNotice(t);
|
|
26307
|
+
if (line) log(line);
|
|
26308
|
+
} catch {
|
|
26309
|
+
}
|
|
26310
|
+
}
|
|
26311
|
+
return { checked: true, unavailable: false, applied };
|
|
26278
26312
|
} catch {
|
|
26279
|
-
return
|
|
26313
|
+
return quiet;
|
|
26280
26314
|
}
|
|
26281
26315
|
}
|
|
26282
26316
|
|
|
@@ -27218,15 +27252,35 @@ async function localLoginForPaidBrowserClaim() {
|
|
|
27218
27252
|
if (timer) clearTimeout(timer);
|
|
27219
27253
|
}
|
|
27220
27254
|
}
|
|
27255
|
+
var PUSH_TOKEN_REFUSAL = Object.freeze({
|
|
27256
|
+
/** The credential is unknown or revoked — dead. Clear it. */
|
|
27257
|
+
INVALID: "invalid-push-token",
|
|
27258
|
+
/** The credential is LIVE, just not a registration credential. NEVER clear it. */
|
|
27259
|
+
INSUFFICIENT: "insufficient-push-token",
|
|
27260
|
+
/**
|
|
27261
|
+
* TERM-325. Minted before tokens were bound to a GitHub account id, so it can no
|
|
27262
|
+
* longer prove who holds it. Unrevoked and real, and it fails closed at every scope
|
|
27263
|
+
* — the slice route answers `invalid-push-token` for the same row — so the ACTION is
|
|
27264
|
+
* the revoked one: clear it and verify in the browser.
|
|
27265
|
+
*
|
|
27266
|
+
* The WORDING is not. The server is explicit that nothing was revoked and the
|
|
27267
|
+
* requirement changed on our side, so telling this developer they revoked something
|
|
27268
|
+
* is false. Sharing a branch must not mean sharing a sentence.
|
|
27269
|
+
*/
|
|
27270
|
+
LEGACY: "legacy-push-token"
|
|
27271
|
+
});
|
|
27272
|
+
var SYNC_BACKGROUND_PUSH_ACTIVE_FIELD = "backgroundPushActive";
|
|
27221
27273
|
async function registerFounderClaim(b) {
|
|
27222
27274
|
const postingId = b.bountyId.replace(/^bounty:founder:/, "");
|
|
27275
|
+
let clearedLocalCredential = false;
|
|
27276
|
+
let refusedForPurpose = false;
|
|
27223
27277
|
const refuse = (reason) => {
|
|
27224
27278
|
console.error(
|
|
27225
27279
|
`
|
|
27226
27280
|
terminalhire claim: refusing to record \u2014 ${reason}
|
|
27227
|
-
|
|
27281
|
+
No CLAIM was recorded: a founder-posting claim registers with terminalhire BEFORE
|
|
27228
27282
|
it is recorded locally (fail-closed), so a posting that is gone, taken, or
|
|
27229
|
-
unverifiable is never claimed on stale cache data.`
|
|
27283
|
+
unverifiable is never claimed on stale cache data.` + (clearedLocalCredential ? "\n One local change was kept: the stored push token was deleted from this\n machine (the server no longer accepts it, so keeping it would fail every\n later claim)." : "")
|
|
27230
27284
|
);
|
|
27231
27285
|
process.exit(1);
|
|
27232
27286
|
};
|
|
@@ -27241,7 +27295,7 @@ terminalhire claim: refusing to record \u2014 ${reason}
|
|
|
27241
27295
|
} catch {
|
|
27242
27296
|
}
|
|
27243
27297
|
let expectLogin;
|
|
27244
|
-
|
|
27298
|
+
const acquireProofAuth = async () => {
|
|
27245
27299
|
try {
|
|
27246
27300
|
expectLogin = await localLoginForPaidBrowserClaim();
|
|
27247
27301
|
} catch (err) {
|
|
@@ -27275,7 +27329,8 @@ terminalhire claim: refusing to record \u2014 ${reason}
|
|
|
27275
27329
|
);
|
|
27276
27330
|
}
|
|
27277
27331
|
auth = { proofToken };
|
|
27278
|
-
}
|
|
27332
|
+
};
|
|
27333
|
+
if (!auth) await acquireProofAuth();
|
|
27279
27334
|
console.log("\n Registering this claim with terminalhire (founder posting)...");
|
|
27280
27335
|
const sendRegistration = async (includeExpectation) => fetch(`${CLAIM_SYNC_BASE2}/api/claim/register`, {
|
|
27281
27336
|
method: "POST",
|
|
@@ -27287,6 +27342,14 @@ terminalhire claim: refusing to record \u2014 ${reason}
|
|
|
27287
27342
|
}),
|
|
27288
27343
|
signal: AbortSignal.timeout(1e4)
|
|
27289
27344
|
});
|
|
27345
|
+
const readRefusal = async (r) => {
|
|
27346
|
+
if (r.ok) return null;
|
|
27347
|
+
try {
|
|
27348
|
+
return await r.json();
|
|
27349
|
+
} catch {
|
|
27350
|
+
return null;
|
|
27351
|
+
}
|
|
27352
|
+
};
|
|
27290
27353
|
let res;
|
|
27291
27354
|
try {
|
|
27292
27355
|
res = await sendRegistration(true);
|
|
@@ -27295,23 +27358,54 @@ terminalhire claim: refusing to record \u2014 ${reason}
|
|
|
27295
27358
|
`terminalhire is unreachable (${err instanceof Error ? err.message : String(err)}), so the posting could not be revalidated.`
|
|
27296
27359
|
);
|
|
27297
27360
|
}
|
|
27298
|
-
let refusalBody =
|
|
27299
|
-
|
|
27361
|
+
let refusalBody = await readRefusal(res);
|
|
27362
|
+
const pushTokenRefusal = storedPushToken && res.status === 403 && (refusalBody?.error === PUSH_TOKEN_REFUSAL.INVALID || refusalBody?.error === PUSH_TOKEN_REFUSAL.LEGACY || refusalBody?.error === PUSH_TOKEN_REFUSAL.INSUFFICIENT) ? refusalBody.error : null;
|
|
27363
|
+
if (pushTokenRefusal) {
|
|
27364
|
+
if (pushTokenRefusal !== PUSH_TOKEN_REFUSAL.INSUFFICIENT) {
|
|
27365
|
+
if (pushTokenRefusal === PUSH_TOKEN_REFUSAL.LEGACY) {
|
|
27366
|
+
console.log("\n The push token on this machine was issued before terminalhire tied");
|
|
27367
|
+
console.log(" tokens to a GitHub account, so it can no longer prove who holds it.");
|
|
27368
|
+
console.log(" Nothing was revoked \u2014 the requirement changed on our side.");
|
|
27369
|
+
console.log(" Registering this claim needs only a ONE-TIME identity check, so");
|
|
27370
|
+
console.log(" falling back to browser verification.");
|
|
27371
|
+
} else {
|
|
27372
|
+
console.log("\n The push token stored on this machine is no longer valid.");
|
|
27373
|
+
console.log(" Registering this claim needs only a ONE-TIME identity check, so");
|
|
27374
|
+
console.log(" falling back to browser verification. (Re-enrolling background");
|
|
27375
|
+
console.log(" push is a separate thing you need only for repeatable reads \u2014");
|
|
27376
|
+
console.log(" fetching your granted slice and polling CI.)");
|
|
27377
|
+
}
|
|
27378
|
+
storedPushToken = null;
|
|
27379
|
+
const hadAutoMarker = Boolean(readAutoMarker());
|
|
27380
|
+
clearPushTokenEnc();
|
|
27381
|
+
clearedLocalCredential = true;
|
|
27382
|
+
if (hadAutoMarker) {
|
|
27383
|
+
clearAutoMarker();
|
|
27384
|
+
console.log("\n Background dashboard updates are now OFF \u2014 that token was the");
|
|
27385
|
+
console.log(" credential they ran on. Turn them back on any time:");
|
|
27386
|
+
console.log(" terminalhire claim --push --keep-updated");
|
|
27387
|
+
}
|
|
27388
|
+
} else {
|
|
27389
|
+
refusedForPurpose = true;
|
|
27390
|
+
console.log("\n Registering a claim takes a one-time browser check, which the");
|
|
27391
|
+
console.log(" credential stored on this machine is not for. Falling back to it now.");
|
|
27392
|
+
console.log(" It was not revoked or changed \u2014 this refusal was about which");
|
|
27393
|
+
console.log(" action the credential is for, not whether it is still good.");
|
|
27394
|
+
}
|
|
27395
|
+
await acquireProofAuth();
|
|
27300
27396
|
try {
|
|
27301
|
-
|
|
27302
|
-
} catch {
|
|
27397
|
+
res = await sendRegistration(true);
|
|
27398
|
+
} catch (err) {
|
|
27399
|
+
refuse(
|
|
27400
|
+
`terminalhire is unreachable (${err instanceof Error ? err.message : String(err)}), so the posting could not be revalidated.`
|
|
27401
|
+
);
|
|
27303
27402
|
}
|
|
27403
|
+
refusalBody = await readRefusal(res);
|
|
27304
27404
|
}
|
|
27305
27405
|
if (expectLogin && res.status === 422 && refusalBody?.error === "unknown field(s): expectLogin") {
|
|
27306
27406
|
try {
|
|
27307
27407
|
res = await sendRegistration(false);
|
|
27308
|
-
refusalBody =
|
|
27309
|
-
if (!res.ok) {
|
|
27310
|
-
try {
|
|
27311
|
-
refusalBody = await res.json();
|
|
27312
|
-
} catch {
|
|
27313
|
-
}
|
|
27314
|
-
}
|
|
27408
|
+
refusalBody = await readRefusal(res);
|
|
27315
27409
|
} catch (err) {
|
|
27316
27410
|
refuse(
|
|
27317
27411
|
`terminalhire is unreachable (${err instanceof Error ? err.message : String(err)}), so the posting could not be revalidated.`
|
|
@@ -27333,43 +27427,61 @@ terminalhire claim: refusing to record \u2014 ${reason}
|
|
|
27333
27427
|
if (!body || body.ok !== true) {
|
|
27334
27428
|
refuse("malformed registration response from the server.");
|
|
27335
27429
|
}
|
|
27430
|
+
const mintedToken = typeof body.pushToken === "string" && body.pushToken.length > 0 ? body.pushToken : null;
|
|
27431
|
+
if (refusedForPurpose && mintedToken) {
|
|
27432
|
+
try {
|
|
27433
|
+
await writePushTokenEnc(mintedToken);
|
|
27434
|
+
console.log("\n Your stored credential was refreshed \u2014 registering this claim");
|
|
27435
|
+
console.log(" rotated it, so the previous one is no longer valid.");
|
|
27436
|
+
} catch (err) {
|
|
27437
|
+
console.log("\n \u26A0 Registered, but the refreshed credential could not be stored here.");
|
|
27438
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
27439
|
+
for (const line of reason.split("\n")) console.log(` ${line}`);
|
|
27440
|
+
console.log(" Re-enrol before fetching a slice: terminalhire claim --push");
|
|
27441
|
+
}
|
|
27442
|
+
}
|
|
27336
27443
|
return {
|
|
27337
27444
|
claimId: typeof body.claimId === "string" ? body.claimId : null,
|
|
27338
27445
|
claimantLogin: typeof body.claimantLogin === "string" ? body.claimantLogin : null,
|
|
27339
|
-
// Existing-token auth deliberately gets no replacement from the server
|
|
27340
|
-
//
|
|
27341
|
-
//
|
|
27342
|
-
pushToken:
|
|
27446
|
+
// Existing-token auth deliberately gets no replacement from the server: reuse the
|
|
27447
|
+
// encrypted value we just authenticated with. The one exception is the rotation
|
|
27448
|
+
// above — there the stored value is the superseded one, so it must not win.
|
|
27449
|
+
pushToken: refusedForPurpose && mintedToken ? mintedToken : storedPushToken ?? mintedToken
|
|
27343
27450
|
};
|
|
27344
27451
|
}
|
|
27452
|
+
function readCredentialDisposition({
|
|
27453
|
+
markerExists,
|
|
27454
|
+
enrolledTokenExists,
|
|
27455
|
+
enrollmentRevoked = false
|
|
27456
|
+
}) {
|
|
27457
|
+
if (!markerExists) return "store";
|
|
27458
|
+
if (enrollmentRevoked) return "store-and-clear-stale-marker";
|
|
27459
|
+
if (enrolledTokenExists) return "keep-enrolled";
|
|
27460
|
+
return "store-and-clear-stale-marker";
|
|
27461
|
+
}
|
|
27345
27462
|
async function bootstrapFounderClaimEnrollment(claim, registration) {
|
|
27346
|
-
if (!(claim.amountUSD > 0)) return {
|
|
27463
|
+
if (!(claim.amountUSD > 0)) return { stored: false, reason: "free" };
|
|
27347
27464
|
if (!registration.pushToken || !registration.claimantLogin) {
|
|
27348
|
-
return {
|
|
27465
|
+
return { stored: false, reason: "token-unavailable" };
|
|
27466
|
+
}
|
|
27467
|
+
const disposition = readCredentialDisposition({
|
|
27468
|
+
markerExists: Boolean(readAutoMarker()),
|
|
27469
|
+
enrolledTokenExists: Boolean(await readPushTokenEnc().catch(() => null))
|
|
27470
|
+
});
|
|
27471
|
+
if (disposition === "keep-enrolled") {
|
|
27472
|
+
return { stored: false, reason: "kept-enrolled" };
|
|
27349
27473
|
}
|
|
27350
27474
|
try {
|
|
27351
27475
|
await writePushTokenEnc(registration.pushToken);
|
|
27352
|
-
|
|
27353
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
27354
|
-
writeAutoMarker({
|
|
27355
|
-
...prior ?? {},
|
|
27356
|
-
autoConsentedAt: prior?.autoConsentedAt ?? now,
|
|
27357
|
-
version: AUTO_CONSENT_VERSION,
|
|
27358
|
-
login: registration.claimantLogin
|
|
27359
|
-
// Do not invent lastPushedAt/lastSnapshotHash here. If this is a fresh
|
|
27360
|
-
// marker the immediate existing background path must push the newly
|
|
27361
|
-
// recorded local claim; if it is an existing marker those fields remain
|
|
27362
|
-
// truthful and the changed snapshot makes the normal gate fire.
|
|
27363
|
-
});
|
|
27476
|
+
if (disposition === "store-and-clear-stale-marker") clearAutoMarker();
|
|
27364
27477
|
} catch (err) {
|
|
27365
27478
|
return {
|
|
27366
|
-
|
|
27479
|
+
stored: false,
|
|
27367
27480
|
reason: "local-write-failed",
|
|
27368
27481
|
detail: err instanceof Error ? err.message : String(err)
|
|
27369
27482
|
};
|
|
27370
27483
|
}
|
|
27371
|
-
|
|
27372
|
-
return pushed?.pushed ? { enrolled: true, reason: "ok" } : { enrolled: true, reason: pushed?.reason ?? "sync-failed" };
|
|
27484
|
+
return { stored: true, reason: "ok" };
|
|
27373
27485
|
}
|
|
27374
27486
|
async function cmdRecord(arg, flags = {}) {
|
|
27375
27487
|
const claims = await Promise.resolve().then(() => (init_claims(), claims_exports));
|
|
@@ -27580,21 +27692,19 @@ terminalhire claim: refusing to record \u2014 read ${b.repoFullName}'s contribut
|
|
|
27580
27692
|
` registered with terminalhire${claim.approval.claimId ? ` (server claim ${claim.approval.claimId})` : ""}`
|
|
27581
27693
|
);
|
|
27582
27694
|
if (claim.amountUSD > 0) {
|
|
27583
|
-
if (enrollment?.
|
|
27584
|
-
console.log(" \u2713
|
|
27585
|
-
console.log("
|
|
27586
|
-
console.log("
|
|
27587
|
-
|
|
27588
|
-
|
|
27589
|
-
console.log(
|
|
27590
|
-
console.log(" Retry: terminalhire claim --push --keep-updated");
|
|
27591
|
-
console.log(" Revoke any time: terminalhire claim --push --revoke");
|
|
27695
|
+
if (enrollment?.stored) {
|
|
27696
|
+
console.log(" \u2713 credential for your granted slice + CI results stored encrypted here");
|
|
27697
|
+
console.log(" Background dashboard updates are NOT on (claiming does not enable them).");
|
|
27698
|
+
console.log(" Want them? terminalhire claim --push --keep-updated");
|
|
27699
|
+
console.log(" Remove the stored credential any time: terminalhire claim --push --revoke");
|
|
27700
|
+
} else if (enrollment?.reason === "kept-enrolled") {
|
|
27701
|
+
console.log(" \u2713 your existing enrolled credential is kept and covers this claim");
|
|
27592
27702
|
} else {
|
|
27593
|
-
console.log(" \u26A0 Claim recorded, but
|
|
27703
|
+
console.log(" \u26A0 Claim recorded, but the credential could not be stored on this machine.");
|
|
27594
27704
|
if (enrollment?.detail) {
|
|
27595
27705
|
for (const line of String(enrollment.detail).split("\n")) console.log(` ${line}`);
|
|
27596
27706
|
}
|
|
27597
|
-
console.log("
|
|
27707
|
+
console.log(" Store it before fetching your slice: terminalhire claim --push");
|
|
27598
27708
|
}
|
|
27599
27709
|
}
|
|
27600
27710
|
if (claim.approval.state === "pending") {
|
|
@@ -27726,6 +27836,17 @@ async function cmdList(active) {
|
|
|
27726
27836
|
}
|
|
27727
27837
|
await syncFounderApprovals(claims, list);
|
|
27728
27838
|
list = claims.listClaims({ active });
|
|
27839
|
+
await syncFounderVerdicts({
|
|
27840
|
+
claimsModule: claims,
|
|
27841
|
+
targets: list,
|
|
27842
|
+
readPushTokenEnc
|
|
27843
|
+
});
|
|
27844
|
+
list = claims.listClaims({ active });
|
|
27845
|
+
if (list.length === 0) {
|
|
27846
|
+
console.log(active ? "\nNo active claims." : "\nNo claims yet.");
|
|
27847
|
+
printMetric(claims.acceptedPRRate());
|
|
27848
|
+
return;
|
|
27849
|
+
}
|
|
27729
27850
|
console.log(`
|
|
27730
27851
|
${list.length} ${active ? "active " : ""}claim${list.length === 1 ? "" : "s"}:
|
|
27731
27852
|
`);
|
|
@@ -27874,6 +27995,12 @@ async function cmdStatus(id) {
|
|
|
27874
27995
|
approvalsChecked = syncRes.approvalsChecked;
|
|
27875
27996
|
approvalsUnavailable = syncRes.approvalsUnavailable;
|
|
27876
27997
|
targets = id ? [claims.findClaim(id)].filter(Boolean) : claims.listClaims();
|
|
27998
|
+
await syncFounderVerdicts({
|
|
27999
|
+
claimsModule: claims,
|
|
28000
|
+
targets,
|
|
28001
|
+
readPushTokenEnc
|
|
28002
|
+
});
|
|
28003
|
+
targets = id ? [claims.findClaim(id)].filter(Boolean) : claims.listClaims();
|
|
27877
28004
|
console.log("\n Founder claims:");
|
|
27878
28005
|
for (const c of targets.filter((claim) => Boolean(claim.approval))) {
|
|
27879
28006
|
console.log(` ${founderClaimStanding(c, approvalsChecked)} \u2014 ${c.title}`);
|
|
@@ -28577,6 +28704,26 @@ function requireFounderLoopClaim(claims, id, verb) {
|
|
|
28577
28704
|
}
|
|
28578
28705
|
return claim;
|
|
28579
28706
|
}
|
|
28707
|
+
function retireRevokedReadCredential(status, body, what) {
|
|
28708
|
+
if (status !== 403 || body?.error !== PUSH_TOKEN_REFUSAL.INVALID) return false;
|
|
28709
|
+
const hadMarker = Boolean(readAutoMarker());
|
|
28710
|
+
clearPushTokenEnc();
|
|
28711
|
+
if (hadMarker) clearAutoMarker();
|
|
28712
|
+
console.error(
|
|
28713
|
+
`
|
|
28714
|
+
terminalhire claim: ${what} needs the stored credential, and the server says it has been revoked.`
|
|
28715
|
+
);
|
|
28716
|
+
console.error(" It has been removed from this machine, so nothing keeps retrying a");
|
|
28717
|
+
console.error(" credential that cannot work.");
|
|
28718
|
+
if (hadMarker) {
|
|
28719
|
+
console.error(" Background dashboard updates are now OFF \u2014 they ran on that credential.");
|
|
28720
|
+
}
|
|
28721
|
+
console.error(" Get a working one (browser verification): terminalhire claim --push");
|
|
28722
|
+
if (hadMarker) {
|
|
28723
|
+
console.error(" Add --keep-updated to that command to turn background updates back on.");
|
|
28724
|
+
}
|
|
28725
|
+
return true;
|
|
28726
|
+
}
|
|
28580
28727
|
async function requireReadPushToken(what) {
|
|
28581
28728
|
let stored = null;
|
|
28582
28729
|
try {
|
|
@@ -28620,7 +28767,9 @@ async function cmdSlice(id, flags = {}) {
|
|
|
28620
28767
|
} catch {
|
|
28621
28768
|
}
|
|
28622
28769
|
if (!res.ok) {
|
|
28623
|
-
|
|
28770
|
+
if (!retireRevokedReadCredential(res.status, body, "fetching your granted slice")) {
|
|
28771
|
+
console.error(`terminalhire claim: ${renderServerRefusal(res.status, body)}`);
|
|
28772
|
+
}
|
|
28624
28773
|
process.exit(1);
|
|
28625
28774
|
}
|
|
28626
28775
|
if (!body || body.ok !== true || !Array.isArray(body.files)) {
|
|
@@ -28748,7 +28897,9 @@ async function cmdRuns(id, flags = {}) {
|
|
|
28748
28897
|
process.exit(1);
|
|
28749
28898
|
}
|
|
28750
28899
|
if (r.kind === "refusal") {
|
|
28751
|
-
|
|
28900
|
+
if (!retireRevokedReadCredential(r.status, r.body, "reading CI results")) {
|
|
28901
|
+
console.error(`terminalhire claim: ${renderServerRefusal(r.status, r.body)}`);
|
|
28902
|
+
}
|
|
28752
28903
|
process.exit(1);
|
|
28753
28904
|
}
|
|
28754
28905
|
console.log(`
|
|
@@ -29527,10 +29678,11 @@ async function cmdPush({ keepUpdated = false } = {}) {
|
|
|
29527
29678
|
}
|
|
29528
29679
|
let deleteToken = null;
|
|
29529
29680
|
let pushToken = null;
|
|
29681
|
+
let syncBody = null;
|
|
29530
29682
|
try {
|
|
29531
|
-
|
|
29532
|
-
deleteToken =
|
|
29533
|
-
pushToken =
|
|
29683
|
+
syncBody = await res.json();
|
|
29684
|
+
deleteToken = syncBody?.deleteToken || null;
|
|
29685
|
+
pushToken = syncBody?.pushToken || null;
|
|
29534
29686
|
} catch {
|
|
29535
29687
|
}
|
|
29536
29688
|
writeClaimPushMarker({
|
|
@@ -29540,23 +29692,55 @@ async function cmdPush({ keepUpdated = false } = {}) {
|
|
|
29540
29692
|
lastPushedAt: consentedAt,
|
|
29541
29693
|
lastSnapshotHash: computeSnapshotHash(pushed)
|
|
29542
29694
|
});
|
|
29543
|
-
if (
|
|
29695
|
+
if (pushToken) {
|
|
29544
29696
|
try {
|
|
29545
|
-
|
|
29546
|
-
|
|
29547
|
-
|
|
29548
|
-
|
|
29549
|
-
|
|
29550
|
-
|
|
29551
|
-
|
|
29552
|
-
|
|
29553
|
-
|
|
29554
|
-
|
|
29697
|
+
if (autoConsent) {
|
|
29698
|
+
await writePushTokenEnc(pushToken);
|
|
29699
|
+
writeAutoMarker({
|
|
29700
|
+
autoConsentedAt: consentedAt,
|
|
29701
|
+
version: AUTO_CONSENT_VERSION,
|
|
29702
|
+
login,
|
|
29703
|
+
lastPushedAt: consentedAt,
|
|
29704
|
+
lastSnapshotHash: computeSnapshotHash(pushed)
|
|
29705
|
+
});
|
|
29706
|
+
console.log("\n \u2713 Background updates enabled \u2014 your dashboard will stay current");
|
|
29707
|
+
console.log(" (at most once/day). Stop any time: terminalhire claim --push --revoke");
|
|
29708
|
+
} else {
|
|
29709
|
+
const backgroundPushRevoked = syncBody?.[SYNC_BACKGROUND_PUSH_ACTIVE_FIELD] === false;
|
|
29710
|
+
const disposition = readCredentialDisposition({
|
|
29711
|
+
markerExists: Boolean(readAutoMarker()),
|
|
29712
|
+
enrolledTokenExists: Boolean(await readPushTokenEnc()),
|
|
29713
|
+
enrollmentRevoked: backgroundPushRevoked
|
|
29714
|
+
});
|
|
29715
|
+
if (disposition === "store") {
|
|
29716
|
+
await writePushTokenEnc(pushToken);
|
|
29717
|
+
console.log(
|
|
29718
|
+
"\n \u2713 Stored the credential for fetching your granted slice and CI results."
|
|
29719
|
+
);
|
|
29720
|
+
} else if (disposition === "keep-enrolled") {
|
|
29721
|
+
console.log("\n \u2713 Background updates are already active; keeping the credential");
|
|
29722
|
+
console.log(" they run on. Nothing about them changed.");
|
|
29723
|
+
} else {
|
|
29724
|
+
await writePushTokenEnc(pushToken);
|
|
29725
|
+
clearAutoMarker();
|
|
29726
|
+
console.log(
|
|
29727
|
+
"\n \u2713 Stored the credential for fetching your granted slice and CI results."
|
|
29728
|
+
);
|
|
29729
|
+
console.log(
|
|
29730
|
+
backgroundPushRevoked ? " Background dashboard updates are OFF \u2014 that enrolment was stopped or revoked." : " Background dashboard updates are OFF (their credential was gone)."
|
|
29731
|
+
);
|
|
29732
|
+
console.log(" Turn them back on any time: terminalhire claim --push --keep-updated");
|
|
29733
|
+
}
|
|
29734
|
+
}
|
|
29555
29735
|
} catch (err) {
|
|
29556
|
-
console.log(
|
|
29736
|
+
console.log(
|
|
29737
|
+
autoConsent ? "\n \u2713 Pushed, but could not enable background updates on this machine." : "\n \u2713 Pushed, but could not store the credential for slice fetch / CI polling."
|
|
29738
|
+
);
|
|
29557
29739
|
const reason = err instanceof Error ? err.message : String(err);
|
|
29558
29740
|
for (const line of reason.split("\n")) console.log(` ${line}`);
|
|
29559
|
-
console.log(
|
|
29741
|
+
console.log(
|
|
29742
|
+
autoConsent ? " Re-run `terminalhire claim --push --keep-updated` to retry." : " Re-run `terminalhire claim --push` to retry."
|
|
29743
|
+
);
|
|
29560
29744
|
}
|
|
29561
29745
|
} else if (backgroundEnableFailed(autoConsent, pushToken)) {
|
|
29562
29746
|
console.log(
|
|
@@ -29844,8 +30028,10 @@ async function run() {
|
|
|
29844
30028
|
export {
|
|
29845
30029
|
AI_DISCLOSURE_NOTE,
|
|
29846
30030
|
CLAIM_CONSENT_VERSION,
|
|
30031
|
+
PUSH_TOKEN_REFUSAL,
|
|
29847
30032
|
REVISE_RECOVERY_STATES,
|
|
29848
30033
|
SUBMIT_ACCEPTS,
|
|
30034
|
+
SYNC_BACKGROUND_PUSH_ACTIVE_FIELD,
|
|
29849
30035
|
backgroundEnableFailed,
|
|
29850
30036
|
buildAssignmentComment,
|
|
29851
30037
|
buildPatchSubmission,
|
|
@@ -29853,6 +30039,7 @@ export {
|
|
|
29853
30039
|
buildStandDownComment,
|
|
29854
30040
|
buildSubmitBody,
|
|
29855
30041
|
claimUpdatePatch,
|
|
30042
|
+
cmdPush,
|
|
29856
30043
|
cmdRecord,
|
|
29857
30044
|
cmdRuns,
|
|
29858
30045
|
cmdSlice,
|
|
@@ -29880,6 +30067,7 @@ export {
|
|
|
29880
30067
|
pickBodySource,
|
|
29881
30068
|
pickExistingPr,
|
|
29882
30069
|
printNextSteps,
|
|
30070
|
+
readCredentialDisposition,
|
|
29883
30071
|
renderClaimHistory,
|
|
29884
30072
|
renderRunView,
|
|
29885
30073
|
renderServerRefusal,
|