terminalhire 0.42.26 → 0.42.28
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/approved-claims-sync.js +1 -1
- package/dist/bin/jpi-claim.js +148 -21
- package/dist/bin/jpi-dispatch.js +150 -23
- package/dist/bin/jpi-mcp.js +149 -22
- package/dist/bin/jpi-refresh.js +1 -1
- package/dist/bin/jpi-run.js +58 -9
- package/package.json +1 -1
|
@@ -106,7 +106,7 @@ function buildApprovalsNudge(awaitingApproval) {
|
|
|
106
106
|
if (!Number.isInteger(awaitingApproval) || awaitingApproval <= 0) return null;
|
|
107
107
|
const n = awaitingApproval;
|
|
108
108
|
return ` \u26A0 ${n} claim${n === 1 ? "" : "s"} awaiting poster approval \u2014 terminalhire cannot check in the background until you enrol:
|
|
109
|
-
terminalhire claim --push --keep-updated (or check one now: terminalhire claim
|
|
109
|
+
terminalhire claim --push --keep-updated (or check one now: terminalhire claim start <id>)`;
|
|
110
110
|
}
|
|
111
111
|
async function syncApprovedClaims({
|
|
112
112
|
readAutoMarker,
|
package/dist/bin/jpi-claim.js
CHANGED
|
@@ -29714,7 +29714,10 @@ var init_result = __esm({
|
|
|
29714
29714
|
"detected",
|
|
29715
29715
|
"none",
|
|
29716
29716
|
"operator-declared",
|
|
29717
|
-
"developer-declared"
|
|
29717
|
+
"developer-declared",
|
|
29718
|
+
// TERM-1221: the mapping chose the image from a runtime the POSTER declared, because the
|
|
29719
|
+
// repository's own files named none.
|
|
29720
|
+
"founder-declared"
|
|
29718
29721
|
];
|
|
29719
29722
|
RUN_RESULT_SCHEMA = "terminalhire.verification-run/1";
|
|
29720
29723
|
RUN_RESULT_FIELDS = [
|
|
@@ -30072,6 +30075,10 @@ var init_attestation2 = __esm({
|
|
|
30072
30075
|
};
|
|
30073
30076
|
IMAGE_SOURCE_IS_SIGNABLE = {
|
|
30074
30077
|
detected: () => null,
|
|
30078
|
+
// The command table's `founder-declared` reasoning, applied to the environment: the poster
|
|
30079
|
+
// chose the runtime and the poster is paying, so a runtime they declared is theirs to stand
|
|
30080
|
+
// behind. The image itself is still the runtime mapping's pick, not a name anyone typed.
|
|
30081
|
+
"founder-declared": () => null,
|
|
30075
30082
|
"operator-declared": () => "had its container image chosen by an operator out of band, and the signed vocabulary has no member for that. Signing it would present a hand-picked environment as the one the runtime mapping selected.",
|
|
30076
30083
|
"developer-declared": () => "had its container image supplied by the developer whose work is under verification. A chosen interpreter turns a red suite green as surely as a chosen test command.",
|
|
30077
30084
|
// `none` means no image was booted, so an image name beside it is a contradiction — the
|
|
@@ -30485,7 +30492,10 @@ var init_boundary = __esm({
|
|
|
30485
30492
|
},
|
|
30486
30493
|
{
|
|
30487
30494
|
code: "lockfile",
|
|
30488
|
-
|
|
30495
|
+
// The `.lock` suffix is the server's rule since TERM-412; this copy missed it, so
|
|
30496
|
+
// the pre-flight passed `uv.lock` and bun's text `bun.lock` that the server refuses
|
|
30497
|
+
// (TERM-1235). The name set only covers lockfiles whose names do not end in `.lock`.
|
|
30498
|
+
matches: (_p, _segments, base) => LOCKFILE_NAMES.has(base) || base.endsWith(".lock"),
|
|
30489
30499
|
why: "is a dependency lockfile. Nobody reads a lockfile diff, so a change to one is refused rather than shown"
|
|
30490
30500
|
}
|
|
30491
30501
|
];
|
|
@@ -30758,8 +30768,14 @@ import { createHash as createHash9, X509Certificate } from "crypto";
|
|
|
30758
30768
|
import { chmodSync as chmodSync3, existsSync as existsSync11, mkdtempSync as mkdtempSync3, readFileSync as readFileSync11, rmSync as rmSync8, writeFileSync as writeFileSync13 } from "fs";
|
|
30759
30769
|
import { request as httpsRequest } from "https";
|
|
30760
30770
|
import { createServer } from "net";
|
|
30761
|
-
import {
|
|
30771
|
+
import { posix as posix3 } from "path";
|
|
30762
30772
|
import { tmpdir as tmpdir3 } from "os";
|
|
30773
|
+
function refuseUnsupportedHost(io) {
|
|
30774
|
+
const platform = io.platform ?? process.platform;
|
|
30775
|
+
if (platform === "win32") {
|
|
30776
|
+
throw new HostedVenueError("the hosted venue cannot run from a Windows host: it reaches the venue daemon through an ssh-forwarded Unix socket, and OpenSSH on Windows cannot forward to a local socket path. Run from Linux, macOS or WSL, or use --placement local-docker.", "ours");
|
|
30777
|
+
}
|
|
30778
|
+
}
|
|
30763
30779
|
function credentialInGitConfig(text) {
|
|
30764
30780
|
for (const match2 of text.matchAll(/\b([a-z][a-z0-9+.-]*):\/\/(\S+)/gi)) {
|
|
30765
30781
|
const scheme = (match2[1] ?? "").toLowerCase();
|
|
@@ -31296,6 +31312,7 @@ function hostedVenueAvailable(opts = {}, io = defaultHostedVenueIo) {
|
|
|
31296
31312
|
const config = opts.gcloudConfig ?? VENUE_GCLOUD_CONFIG;
|
|
31297
31313
|
const env = venueGcloudEnv(config);
|
|
31298
31314
|
try {
|
|
31315
|
+
refuseUnsupportedHost(io);
|
|
31299
31316
|
assertServiceCredentials(config, io);
|
|
31300
31317
|
} catch {
|
|
31301
31318
|
return false;
|
|
@@ -31736,6 +31753,7 @@ function hostedVenue(opts = {}, io = defaultHostedVenueIo) {
|
|
|
31736
31753
|
...image
|
|
31737
31754
|
};
|
|
31738
31755
|
let bootArgv = gcpBootArgv(bootParams(CLIENT_CERT_STAND_IN));
|
|
31756
|
+
refuseUnsupportedHost(io);
|
|
31739
31757
|
ensureVenueServiceCredentials(gcloudConfig, keyFile, io);
|
|
31740
31758
|
const clientTls = csImageDigest === null ? null : { ...makeClientTls(io, allocated, vm), imageDigest: csImageDigest };
|
|
31741
31759
|
if (clientTls !== null)
|
|
@@ -32029,7 +32047,8 @@ function makeLease(p) {
|
|
|
32029
32047
|
throw new HostedVenueError(`could not stage ${from} onto ${p.vm}: ${execDetail(res).slice(0, 300)}`);
|
|
32030
32048
|
}
|
|
32031
32049
|
};
|
|
32032
|
-
const
|
|
32050
|
+
const localJail = join24(local.scratchRoot, JAIL_SEGMENT);
|
|
32051
|
+
const localTmp = join24(local.scratchRoot, JAIL_TMP_SEGMENT);
|
|
32033
32052
|
const required = [
|
|
32034
32053
|
localTmp,
|
|
32035
32054
|
join24(localJail, JAIL_PASSWD_FILE),
|
|
@@ -32178,7 +32197,7 @@ function makeLease(p) {
|
|
|
32178
32197
|
}
|
|
32179
32198
|
};
|
|
32180
32199
|
}
|
|
32181
|
-
var SSH_READY_BUDGET_MS, SSH_PROBE_INTERVAL_MS, SSH_PROBE_TIMEOUT_MS, TUNNEL_BUDGET_MS, TUNNEL_POLL_INTERVAL_MS, GOOGLE_JWKS_URL, JWKS_FETCH_TIMEOUT_MS, CREDENTIAL_QUERY_PARAM, UNDECODABLE, STAGE_PUSH_TIMEOUT_MS, DISPATCHED_PROBE_TIMEOUT_MS, DISPATCHED_STATUS_ARGV, DISPATCHED_GIT_CANDIDATES, PROXY_CLEANUP_TIMEOUT_MS, OWNER_PROBE_TIMEOUT_MS, BOOT_TIMEOUT_MS, MKDIR_TIMEOUT_MS, DELETE_TIMEOUT_MS, LOCAL_GCLOUD_TIMEOUT_MS, SERVICE_ACCOUNT_ACTIVATE_TIMEOUT_MS, SOCKET_DIR_PREFIX, VENUE_SOCKET_NAME, HostedVenueError, VENUE_GCLOUD_CONFIG, SERVICE_ACCOUNT_SUFFIX, GCLOUD_PRINCIPAL_OVERRIDES, defaultHostedVenueIo, VENUE_SSH_USER, GCE_METADATA_IDENTITY_URL, COMPACT_JWT, VOLUME_CREATE_TIMEOUT_MS, EXEC_PROBE_TIMEOUT_MS, POPULATE_TIMEOUT_MS, STAGE_PROOF_PREFIX, IAP_NOT_READY, IAP_BACKEND_UNREACHABLE, IAP_DENIED, TERMINAL_GCP, INSTANCE_NOT_RUNNING, PREEMPTED, HOST_KEY_MISMATCH, SSH_KEY_NOT_READY, DAEMON_NOT_READY, SSH_NOT_ANSWERING, CS_ATTEST_PORT, CS_DOCKER_PORT, CS_READY_BUDGET_MS, CS_ATTEST_TIMEOUT_MS, CS_PULL_TIMEOUT_MS, CS_ATTEST_INTERVAL_MS, OPENSSL_TIMEOUT_MS, CS_RUN_ARGS, CS_GUEST_USER, CS_STAGE_ROOT, CLIENT_CERT_STAND_IN;
|
|
32200
|
+
var join24, SSH_READY_BUDGET_MS, SSH_PROBE_INTERVAL_MS, SSH_PROBE_TIMEOUT_MS, TUNNEL_BUDGET_MS, TUNNEL_POLL_INTERVAL_MS, GOOGLE_JWKS_URL, JWKS_FETCH_TIMEOUT_MS, CREDENTIAL_QUERY_PARAM, UNDECODABLE, STAGE_PUSH_TIMEOUT_MS, DISPATCHED_PROBE_TIMEOUT_MS, DISPATCHED_STATUS_ARGV, DISPATCHED_GIT_CANDIDATES, PROXY_CLEANUP_TIMEOUT_MS, OWNER_PROBE_TIMEOUT_MS, BOOT_TIMEOUT_MS, MKDIR_TIMEOUT_MS, DELETE_TIMEOUT_MS, LOCAL_GCLOUD_TIMEOUT_MS, SERVICE_ACCOUNT_ACTIVATE_TIMEOUT_MS, SOCKET_DIR_PREFIX, VENUE_SOCKET_NAME, HostedVenueError, VENUE_GCLOUD_CONFIG, SERVICE_ACCOUNT_SUFFIX, GCLOUD_PRINCIPAL_OVERRIDES, defaultHostedVenueIo, VENUE_SSH_USER, GCE_METADATA_IDENTITY_URL, COMPACT_JWT, VOLUME_CREATE_TIMEOUT_MS, EXEC_PROBE_TIMEOUT_MS, POPULATE_TIMEOUT_MS, STAGE_PROOF_PREFIX, IAP_NOT_READY, IAP_BACKEND_UNREACHABLE, IAP_DENIED, TERMINAL_GCP, INSTANCE_NOT_RUNNING, PREEMPTED, HOST_KEY_MISMATCH, SSH_KEY_NOT_READY, DAEMON_NOT_READY, SSH_NOT_ANSWERING, CS_ATTEST_PORT, CS_DOCKER_PORT, CS_READY_BUDGET_MS, CS_ATTEST_TIMEOUT_MS, CS_PULL_TIMEOUT_MS, CS_ATTEST_INTERVAL_MS, OPENSSL_TIMEOUT_MS, CS_RUN_ARGS, CS_GUEST_USER, CS_STAGE_ROOT, CLIENT_CERT_STAND_IN;
|
|
32182
32201
|
var init_hostedVenue = __esm({
|
|
32183
32202
|
"../../packages/envrun/dist/hostedVenue.js"() {
|
|
32184
32203
|
"use strict";
|
|
@@ -32190,6 +32209,7 @@ var init_hostedVenue = __esm({
|
|
|
32190
32209
|
init_labels();
|
|
32191
32210
|
init_venueProof();
|
|
32192
32211
|
init_venue();
|
|
32212
|
+
({ join: join24 } = posix3);
|
|
32193
32213
|
SSH_READY_BUDGET_MS = 18e4;
|
|
32194
32214
|
SSH_PROBE_INTERVAL_MS = 5e3;
|
|
32195
32215
|
SSH_PROBE_TIMEOUT_MS = 25e3;
|
|
@@ -34340,8 +34360,11 @@ function publishableTarget(url) {
|
|
|
34340
34360
|
if (separatorInTarget(url) !== null)
|
|
34341
34361
|
return UNPARSEABLE_TARGET;
|
|
34342
34362
|
const m = /^([A-Za-z][A-Za-z0-9+.-]*:\/\/)([^/?#]*)([^?#]*)/.exec(url);
|
|
34343
|
-
if (m === null)
|
|
34344
|
-
|
|
34363
|
+
if (m === null) {
|
|
34364
|
+
if (url.includes("?"))
|
|
34365
|
+
return UNPARSEABLE_TARGET;
|
|
34366
|
+
return WINDOWS_ABSOLUTE.test(url) ? redactWindowsPathCredentials(url) : redactPathCredentials(url);
|
|
34367
|
+
}
|
|
34345
34368
|
const authority = m[2] ?? "";
|
|
34346
34369
|
const at = authority.lastIndexOf("@");
|
|
34347
34370
|
const path5 = m[3] ?? "";
|
|
@@ -34355,6 +34378,16 @@ function redactPathCredentials(path5) {
|
|
|
34355
34378
|
return /:[^@]*@/.test(seen) ? WITHHELD_SEGMENT : segment;
|
|
34356
34379
|
});
|
|
34357
34380
|
}
|
|
34381
|
+
function redactWindowsPathCredentials(path5) {
|
|
34382
|
+
const rest = path5.slice(2);
|
|
34383
|
+
for (const m of rest.matchAll(/[^\\/]+/g)) {
|
|
34384
|
+
const seen = decodeToFixedPoint(m[0]);
|
|
34385
|
+
if (seen === null || seen.includes(":")) {
|
|
34386
|
+
return path5.slice(0, 2) + rest.slice(0, m.index) + WITHHELD_SEGMENT;
|
|
34387
|
+
}
|
|
34388
|
+
}
|
|
34389
|
+
return path5;
|
|
34390
|
+
}
|
|
34358
34391
|
function decodeToFixedPoint(s) {
|
|
34359
34392
|
let current = s;
|
|
34360
34393
|
for (let round = 0; round < 4; round += 1) {
|
|
@@ -34543,6 +34576,21 @@ async function releaseWithoutThrowing(lease, progress) {
|
|
|
34543
34576
|
);
|
|
34544
34577
|
}
|
|
34545
34578
|
}
|
|
34579
|
+
function resolveRunEnvironment(derived, req) {
|
|
34580
|
+
const runtimeDeclared = req.runtimeOverride !== void 0 && req.runtimeOverride !== "unknown" && derived.runtime === "unknown";
|
|
34581
|
+
const withRuntime = runtimeDeclared ? {
|
|
34582
|
+
...derived,
|
|
34583
|
+
runtime: req.runtimeOverride,
|
|
34584
|
+
unresolved: derived.unresolved.filter((r) => r.kind !== "unknown-runtime")
|
|
34585
|
+
} : derived;
|
|
34586
|
+
const spec = req.testCommandOverride === void 0 ? withRuntime : { ...withRuntime, testCommand: req.testCommandOverride };
|
|
34587
|
+
const imageSource = req.image !== void 0 ? req.imageOrigin === "operator" ? "operator-declared" : "developer-declared" : runtimeDeclared ? (
|
|
34588
|
+
// Attributed only when the caller says the poster chose it. An unattributed override
|
|
34589
|
+
// defaults the way `testCommandSource` does, to the developer, which does not sign.
|
|
34590
|
+
req.runtimeOverrideOrigin === "founder" ? "founder-declared" : "developer-declared"
|
|
34591
|
+
) : "detected";
|
|
34592
|
+
return { spec, imageSource };
|
|
34593
|
+
}
|
|
34546
34594
|
async function verifyWorkingDiff(req) {
|
|
34547
34595
|
const ctx = {
|
|
34548
34596
|
startedAt: Date.now(),
|
|
@@ -34740,7 +34788,7 @@ async function runVerification(req, ctx) {
|
|
|
34740
34788
|
const treeDigest = patchedTreeDigest(cloneDir);
|
|
34741
34789
|
const baselinePatchSha256 = hasBaselinePatch ? sha256Hex(baselinePatch ?? "") : null;
|
|
34742
34790
|
const derived = deriveEnvironmentSpec(cloneDir);
|
|
34743
|
-
const spec
|
|
34791
|
+
const { spec, imageSource } = resolveRunEnvironment(derived, req);
|
|
34744
34792
|
progress("derive", `runtime=${spec.runtime} install=${String(spec.installCommand)} test=${String(spec.testCommand)}`);
|
|
34745
34793
|
refuseUnbuildableSpec(spec);
|
|
34746
34794
|
const image = placement.imageFor(spec.runtime, req.image, spec.runtimeVersion, imageVariantFor(spec));
|
|
@@ -34845,7 +34893,7 @@ async function runVerification(req, ctx) {
|
|
|
34845
34893
|
// `developer-declared` is deliberate and matches `testCommandSource`: `th run --image`
|
|
34846
34894
|
// runs on the developer's machine, judging the developer's work, so attributing it to
|
|
34847
34895
|
// the operator would sign our name onto their choice.
|
|
34848
|
-
imageSource
|
|
34896
|
+
imageSource,
|
|
34849
34897
|
leaksClean: verdict.leaks.clean,
|
|
34850
34898
|
leakState: verdict.leaks.state,
|
|
34851
34899
|
// Built from the LEASE, over the client that ran the steps — never from
|
|
@@ -35908,6 +35956,7 @@ __export(dist_exports, {
|
|
|
35908
35956
|
renderVerdictLine: () => renderVerdictLine,
|
|
35909
35957
|
resolveImageForSpec: () => resolveImageForSpec,
|
|
35910
35958
|
resolveLease: () => resolveLease,
|
|
35959
|
+
resolveRunEnvironment: () => resolveRunEnvironment,
|
|
35911
35960
|
runEnvironmentSpec: () => runEnvironmentSpec,
|
|
35912
35961
|
runLabels: () => runLabels,
|
|
35913
35962
|
setManifestProbe: () => setManifestProbe,
|
|
@@ -37807,9 +37856,13 @@ function fmtContestedWarning(b) {
|
|
|
37807
37856
|
}
|
|
37808
37857
|
return ` \u26A0 This issue looks taken: ${parts.join(" / ")}. A merged PR here is unlikely.`;
|
|
37809
37858
|
}
|
|
37810
|
-
async function mintRegistrationProof({ claimRef } = {}) {
|
|
37811
|
-
console.log("
|
|
37812
|
-
|
|
37859
|
+
async function mintRegistrationProof({ claimRef, intro } = {}) {
|
|
37860
|
+
console.log("");
|
|
37861
|
+
for (const line of intro ?? [
|
|
37862
|
+
"This posting registers your claim with terminalhire, so your",
|
|
37863
|
+
"GitHub identity has to be verified once in the browser."
|
|
37864
|
+
])
|
|
37865
|
+
console.log(` ${line}`);
|
|
37813
37866
|
let oauthBase;
|
|
37814
37867
|
try {
|
|
37815
37868
|
oauthBase = resolveOAuthBase();
|
|
@@ -38136,17 +38189,18 @@ function readCredentialDisposition({
|
|
|
38136
38189
|
return "store-and-clear-stale-marker";
|
|
38137
38190
|
}
|
|
38138
38191
|
async function bootstrapFounderClaimEnrollment(claim, registration) {
|
|
38139
|
-
if (!(claim.amountUSD > 0)) return { stored: false, reason: "free" };
|
|
38140
38192
|
if (!registration.pushToken || !registration.claimantLogin) {
|
|
38141
38193
|
return { stored: false, reason: "token-unavailable" };
|
|
38142
38194
|
}
|
|
38195
|
+
const current = await readPushTokenEnc().catch(() => null);
|
|
38143
38196
|
const disposition = readCredentialDisposition({
|
|
38144
38197
|
markerExists: Boolean(readAutoMarker()),
|
|
38145
|
-
enrolledTokenExists: Boolean(
|
|
38198
|
+
enrolledTokenExists: Boolean(current)
|
|
38146
38199
|
});
|
|
38147
38200
|
if (disposition === "keep-enrolled") {
|
|
38148
38201
|
return { stored: false, reason: "kept-enrolled" };
|
|
38149
38202
|
}
|
|
38203
|
+
if (current && current === registration.pushToken) return { stored: true, reason: "ok" };
|
|
38150
38204
|
try {
|
|
38151
38205
|
await writePushTokenEnc(registration.pushToken);
|
|
38152
38206
|
if (disposition === "store-and-clear-stale-marker") clearAutoMarker();
|
|
@@ -38367,7 +38421,7 @@ terminalhire claim: refusing to record \u2014 read ${b.repoFullName}'s contribut
|
|
|
38367
38421
|
console.log(
|
|
38368
38422
|
` registered with terminalhire${claim.approval.claimId ? ` (server claim ${claim.approval.claimId})` : ""}`
|
|
38369
38423
|
);
|
|
38370
|
-
if (
|
|
38424
|
+
if (enrollment) {
|
|
38371
38425
|
if (enrollment?.stored) {
|
|
38372
38426
|
console.log(" \u2713 credential for your granted slice + CI results stored encrypted here");
|
|
38373
38427
|
console.log(" Background dashboard updates are NOT on (claiming does not enable them).");
|
|
@@ -38380,7 +38434,7 @@ terminalhire claim: refusing to record \u2014 read ${b.repoFullName}'s contribut
|
|
|
38380
38434
|
if (enrollment?.detail) {
|
|
38381
38435
|
for (const line of String(enrollment.detail).split("\n")) console.log(` ${line}`);
|
|
38382
38436
|
}
|
|
38383
|
-
console.log("
|
|
38437
|
+
console.log(" `claim start` will ask you to confirm in the browser once more.");
|
|
38384
38438
|
}
|
|
38385
38439
|
}
|
|
38386
38440
|
if (claim.approval.state === "pending") {
|
|
@@ -38542,7 +38596,7 @@ async function cmdList(active) {
|
|
|
38542
38596
|
);
|
|
38543
38597
|
return;
|
|
38544
38598
|
}
|
|
38545
|
-
await syncFounderApprovals(claims, list);
|
|
38599
|
+
const { approvalsChecked } = await syncFounderApprovals(claims, list);
|
|
38546
38600
|
list = claims.listClaims({ active });
|
|
38547
38601
|
await syncFounderVerdicts({
|
|
38548
38602
|
claimsModule: claims,
|
|
@@ -38560,7 +38614,7 @@ ${list.length} ${active ? "active " : ""}claim${list.length === 1 ? "" : "s"}:
|
|
|
38560
38614
|
`);
|
|
38561
38615
|
for (const c of list) {
|
|
38562
38616
|
const pr = c.prUrl ? ` \xB7 ${c.prUrl}` : "";
|
|
38563
|
-
console.log(` [${
|
|
38617
|
+
console.log(` [${claimListTurnLabel(c, approvalsChecked)}] ${fmtClaimAmount(c)} \xB7 ${c.title}`);
|
|
38564
38618
|
console.log(` id: ${c.id} \xB7 ${c.state}${pr}`);
|
|
38565
38619
|
}
|
|
38566
38620
|
printMetric(claims.acceptedPRRate());
|
|
@@ -38668,6 +38722,12 @@ async function syncFounderApprovals(claimsModule, targets) {
|
|
|
38668
38722
|
}
|
|
38669
38723
|
return { pushToken, approvalsChecked, approvalsUnavailable };
|
|
38670
38724
|
}
|
|
38725
|
+
function claimListTurnLabel(claim, approvalsChecked) {
|
|
38726
|
+
if (approvalsChecked && claim.state === "claimed" && claim.approval?.mode === "approval-only" && claim.approval?.state === "pending") {
|
|
38727
|
+
return "waiting on poster";
|
|
38728
|
+
}
|
|
38729
|
+
return claimTurnLabel(claim.state);
|
|
38730
|
+
}
|
|
38671
38731
|
function founderClaimStanding(claim, approvalsChecked) {
|
|
38672
38732
|
const turn = claimTurnLabel(claim.state);
|
|
38673
38733
|
switch (claim.state) {
|
|
@@ -39910,12 +39970,74 @@ terminalhire claim: ${what} needs the stored credential, and the server says it
|
|
|
39910
39970
|
}
|
|
39911
39971
|
return true;
|
|
39912
39972
|
}
|
|
39913
|
-
async function
|
|
39973
|
+
async function enrolHeldClaimReadToken(postingId) {
|
|
39974
|
+
const fail = (lines) => {
|
|
39975
|
+
for (const line of lines) console.error(line);
|
|
39976
|
+
process.exit(1);
|
|
39977
|
+
};
|
|
39978
|
+
const proofToken = await mintRegistrationProof({
|
|
39979
|
+
claimRef: opportunityShortToken(`bounty:founder:${postingId}`),
|
|
39980
|
+
intro: [
|
|
39981
|
+
"This machine holds no terminalhire credential yet. Confirm once in the",
|
|
39982
|
+
"browser, signed in as the GitHub account that claimed this work, and",
|
|
39983
|
+
"this machine is linked to your claim."
|
|
39984
|
+
]
|
|
39985
|
+
});
|
|
39986
|
+
if (!proofToken) {
|
|
39987
|
+
fail(["terminalhire claim: could not link this machine to your claim (see above)."]);
|
|
39988
|
+
}
|
|
39989
|
+
let res;
|
|
39990
|
+
try {
|
|
39991
|
+
res = await fetch(`${CLAIM_SYNC_BASE4}/api/claim/enrol`, {
|
|
39992
|
+
method: "POST",
|
|
39993
|
+
headers: { "Content-Type": "application/json" },
|
|
39994
|
+
body: JSON.stringify({ bountyId: postingId, proofToken }),
|
|
39995
|
+
signal: AbortSignal.timeout(CLAIM_SYNC_WRITE_TIMEOUT_MS)
|
|
39996
|
+
});
|
|
39997
|
+
} catch (err) {
|
|
39998
|
+
fail([
|
|
39999
|
+
`terminalhire claim: terminalhire is unreachable (${err instanceof Error ? err.message : String(err)}).`,
|
|
40000
|
+
" Nothing was stored. Run the same command again."
|
|
40001
|
+
]);
|
|
40002
|
+
}
|
|
40003
|
+
let body = null;
|
|
40004
|
+
try {
|
|
40005
|
+
body = await res.json();
|
|
40006
|
+
} catch {
|
|
40007
|
+
}
|
|
40008
|
+
if (res.status === 404 && body?.error === "no-live-claim") {
|
|
40009
|
+
const who = typeof body.claimantLogin === "string" ? `@${body.claimantLogin}` : "that account";
|
|
40010
|
+
fail([
|
|
40011
|
+
`terminalhire claim: you confirmed in the browser as ${who}, and ${who} does not`,
|
|
40012
|
+
" hold a live claim on this posting. Nothing was stored.",
|
|
40013
|
+
" Sign the browser into the GitHub account that claimed it, then run this again."
|
|
40014
|
+
]);
|
|
40015
|
+
}
|
|
40016
|
+
if (!res.ok || typeof body?.pushToken !== "string" || body.pushToken.length === 0) {
|
|
40017
|
+
fail([
|
|
40018
|
+
`terminalhire claim: linking this machine failed (${res.status}${typeof body?.message === "string" ? `: ${body.message}` : ""}). Nothing was stored.`
|
|
40019
|
+
]);
|
|
40020
|
+
}
|
|
40021
|
+
try {
|
|
40022
|
+
await writePushTokenEnc(body.pushToken);
|
|
40023
|
+
} catch (err) {
|
|
40024
|
+
fail([
|
|
40025
|
+
`terminalhire claim: the credential could not be stored on this machine: ${err instanceof Error ? err.message : String(err)}`
|
|
40026
|
+
]);
|
|
40027
|
+
}
|
|
40028
|
+
console.log(" \u2713 This machine is linked to your claim (credential stored encrypted here).");
|
|
40029
|
+
return body.pushToken;
|
|
40030
|
+
}
|
|
40031
|
+
var FOUNDER_POSTING_ID = /^fb_[A-Za-z0-9_-]{1,80}$/;
|
|
40032
|
+
async function requireReadPushToken(what, { enrolFor } = {}) {
|
|
39914
40033
|
let stored = null;
|
|
39915
40034
|
try {
|
|
39916
40035
|
stored = await readPushTokenEnc();
|
|
39917
40036
|
} catch {
|
|
39918
40037
|
}
|
|
40038
|
+
if (!stored && typeof enrolFor === "string" && FOUNDER_POSTING_ID.test(enrolFor)) {
|
|
40039
|
+
return enrolHeldClaimReadToken(enrolFor);
|
|
40040
|
+
}
|
|
39919
40041
|
if (!stored) {
|
|
39920
40042
|
console.error(
|
|
39921
40043
|
`terminalhire claim: ${what} needs the persistent push token, and none is enrolled on this machine.
|
|
@@ -39938,7 +40060,9 @@ function cloneFullTierRepo({ engine, dest, url, sha, token, branch }) {
|
|
|
39938
40060
|
async function cmdSliceFullTier(claims, id, local, fullTierBody, flags, cloneRepo = cloneFullTierRepo) {
|
|
39939
40061
|
const claimId = fullTierBody.claimId;
|
|
39940
40062
|
const bountyId = fullTierBody.bountyId;
|
|
39941
|
-
const pushToken = await requireReadPushToken("fetching your full-repo clone credential"
|
|
40063
|
+
const pushToken = await requireReadPushToken("fetching your full-repo clone credential", {
|
|
40064
|
+
enrolFor: bountyId
|
|
40065
|
+
});
|
|
39942
40066
|
let res;
|
|
39943
40067
|
try {
|
|
39944
40068
|
res = await fetch(`${CLAIM_SYNC_BASE4}/api/claim/clone-token`, {
|
|
@@ -40114,7 +40238,9 @@ async function attemptSliceDelivery(id, flags = {}) {
|
|
|
40114
40238
|
requireFounderLoopClaim(claims, id, "slice");
|
|
40115
40239
|
}
|
|
40116
40240
|
const bountyId = local ? founderPostingIdOf(local) : String(id).replace(/^bounty:founder:/, "");
|
|
40117
|
-
const pushToken = await requireReadPushToken("fetching your granted slice"
|
|
40241
|
+
const pushToken = await requireReadPushToken("fetching your granted slice", {
|
|
40242
|
+
enrolFor: bountyId
|
|
40243
|
+
});
|
|
40118
40244
|
let res;
|
|
40119
40245
|
try {
|
|
40120
40246
|
res = await fetch(`${CLAIM_SYNC_BASE4}/api/claim/slice`, {
|
|
@@ -41932,6 +42058,7 @@ export {
|
|
|
41932
42058
|
buildStakeComment,
|
|
41933
42059
|
buildStandDownComment,
|
|
41934
42060
|
buildSubmitBody,
|
|
42061
|
+
claimListTurnLabel,
|
|
41935
42062
|
claimUpdatePatch,
|
|
41936
42063
|
cloneFullTierRepo,
|
|
41937
42064
|
cmdNote,
|