terminalhire 0.42.27 → 0.42.29
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 +277 -15
- package/dist/bin/jpi-dispatch.js +279 -17
- package/dist/bin/jpi-mcp.js +278 -16
- package/dist/bin/jpi-refresh.js +1 -1
- package/dist/bin/jpi-run.js +187 -3
- package/dist/bin/proxy/egressProxy.js +5 -0
- 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
|
@@ -27022,6 +27022,11 @@ var init_egressProxy = __esm({
|
|
|
27022
27022
|
"use strict";
|
|
27023
27023
|
DEFAULT_INSTALL_ALLOWLIST = [
|
|
27024
27024
|
"registry.npmjs.org",
|
|
27025
|
+
// TERM-1240. `yarn install` is derived for every repo with a yarn.lock, and yarn
|
|
27026
|
+
// classic writes this host into each lockfile entry — so every yarn repo could start
|
|
27027
|
+
// and could never install, the TERM-1139 shape again. It serves the same packages as
|
|
27028
|
+
// the npm registry, so the grant widens where a package comes from, not what can come.
|
|
27029
|
+
"registry.yarnpkg.com",
|
|
27025
27030
|
"pypi.org",
|
|
27026
27031
|
"files.pythonhosted.org",
|
|
27027
27032
|
"proxy.golang.org",
|
|
@@ -30492,7 +30497,10 @@ var init_boundary = __esm({
|
|
|
30492
30497
|
},
|
|
30493
30498
|
{
|
|
30494
30499
|
code: "lockfile",
|
|
30495
|
-
|
|
30500
|
+
// The `.lock` suffix is the server's rule since TERM-412; this copy missed it, so
|
|
30501
|
+
// the pre-flight passed `uv.lock` and bun's text `bun.lock` that the server refuses
|
|
30502
|
+
// (TERM-1235). The name set only covers lockfiles whose names do not end in `.lock`.
|
|
30503
|
+
matches: (_p, _segments, base) => LOCKFILE_NAMES.has(base) || base.endsWith(".lock"),
|
|
30496
30504
|
why: "is a dependency lockfile. Nobody reads a lockfile diff, so a change to one is refused rather than shown"
|
|
30497
30505
|
}
|
|
30498
30506
|
];
|
|
@@ -33043,6 +33051,151 @@ function nodeInstallCommand(repo) {
|
|
|
33043
33051
|
return { command: "npm install", from: "manifest" };
|
|
33044
33052
|
return null;
|
|
33045
33053
|
}
|
|
33054
|
+
function lockfileGaps(repo) {
|
|
33055
|
+
const pkg = readJsonObject(repo, "package.json");
|
|
33056
|
+
if (pkg === null)
|
|
33057
|
+
return null;
|
|
33058
|
+
const wanted = [];
|
|
33059
|
+
for (const section of DEPENDENCY_SECTIONS) {
|
|
33060
|
+
const deps = readObjectField(pkg, section);
|
|
33061
|
+
if (deps === null)
|
|
33062
|
+
continue;
|
|
33063
|
+
for (const [name, range] of Object.entries(deps)) {
|
|
33064
|
+
if (typeof range === "string")
|
|
33065
|
+
wanted.push([name, range]);
|
|
33066
|
+
}
|
|
33067
|
+
}
|
|
33068
|
+
if (wanted.length === 0)
|
|
33069
|
+
return null;
|
|
33070
|
+
const check = (lockfile, listed) => {
|
|
33071
|
+
if (listed === null)
|
|
33072
|
+
return null;
|
|
33073
|
+
const missing = wanted.filter(([n, r]) => !listed(n, r)).map(([n, r]) => `${n}@${r}`);
|
|
33074
|
+
return missing.length === 0 ? null : { lockfile, missing };
|
|
33075
|
+
};
|
|
33076
|
+
for (const lockfile of ["package-lock.json", "npm-shrinkwrap.json"]) {
|
|
33077
|
+
if (repo.exists(lockfile))
|
|
33078
|
+
return check(lockfile, npmLockListing(repo, lockfile));
|
|
33079
|
+
}
|
|
33080
|
+
if (repo.exists("yarn.lock"))
|
|
33081
|
+
return check("yarn.lock", yarnLockListing(repo.readText("yarn.lock")));
|
|
33082
|
+
if (repo.exists("pnpm-lock.yaml")) {
|
|
33083
|
+
return check("pnpm-lock.yaml", pnpmLockListing(repo.readText("pnpm-lock.yaml")));
|
|
33084
|
+
}
|
|
33085
|
+
return null;
|
|
33086
|
+
}
|
|
33087
|
+
function npmLockListing(repo, lockfile) {
|
|
33088
|
+
const lock = readJsonObject(repo, lockfile);
|
|
33089
|
+
if (lock === null)
|
|
33090
|
+
return null;
|
|
33091
|
+
const root = readObjectField(readObjectField(lock, "packages"), "");
|
|
33092
|
+
if (root !== null) {
|
|
33093
|
+
return (name, range) => DEPENDENCY_SECTIONS.some((s) => readStringField(readObjectField(root, s), name) === range);
|
|
33094
|
+
}
|
|
33095
|
+
const v1 = readObjectField(lock, "dependencies");
|
|
33096
|
+
if (v1 === null)
|
|
33097
|
+
return null;
|
|
33098
|
+
return (name) => readObjectField(v1, name) !== null;
|
|
33099
|
+
}
|
|
33100
|
+
function yarnLockListing(text) {
|
|
33101
|
+
if (text === null || !/^(# yarn lockfile v1|__metadata:)/m.test(text))
|
|
33102
|
+
return null;
|
|
33103
|
+
const specs = /* @__PURE__ */ new Set();
|
|
33104
|
+
for (const line of text.split(/\r?\n/)) {
|
|
33105
|
+
if (line === "" || line.startsWith(" ") || line.startsWith("#") || !line.endsWith(":"))
|
|
33106
|
+
continue;
|
|
33107
|
+
for (const raw of line.slice(0, -1).split(",")) {
|
|
33108
|
+
const spec = raw.trim().replace(/^"|"$/g, "");
|
|
33109
|
+
specs.add(spec);
|
|
33110
|
+
const berry = /^(@?[^@]+)@npm:(.+)$/.exec(spec);
|
|
33111
|
+
if (berry !== null)
|
|
33112
|
+
specs.add(`${berry[1]}@${berry[2]}`);
|
|
33113
|
+
}
|
|
33114
|
+
}
|
|
33115
|
+
return (name, range) => specs.has(`${name}@${range}`);
|
|
33116
|
+
}
|
|
33117
|
+
function pnpmLockListing(text) {
|
|
33118
|
+
if (text === null || !/^lockfileVersion:/m.test(text))
|
|
33119
|
+
return null;
|
|
33120
|
+
const lines = pnpmRootLines(text.split(/\r?\n/));
|
|
33121
|
+
if (lines === null)
|
|
33122
|
+
return null;
|
|
33123
|
+
const unquote = (s) => s.replace(/^['"]|['"]$/g, "");
|
|
33124
|
+
return (name, range) => lines.some((line, i) => {
|
|
33125
|
+
if (unquote(line) === `${name}:` || line === `'${name}':` || line === `"${name}":`) {
|
|
33126
|
+
const next = lines[i + 1] ?? "";
|
|
33127
|
+
return next.startsWith("specifier:") && unquote(next.slice(10).trim()) === range;
|
|
33128
|
+
}
|
|
33129
|
+
const flat = /^(['"]?)(.+)\1:\s*(.+)$/.exec(line);
|
|
33130
|
+
return flat !== null && flat[2] === name && unquote(flat[3] ?? "") === range;
|
|
33131
|
+
});
|
|
33132
|
+
}
|
|
33133
|
+
function pnpmRootLines(raw) {
|
|
33134
|
+
const importers = raw.indexOf("importers:");
|
|
33135
|
+
if (importers === -1) {
|
|
33136
|
+
const end = raw.indexOf("packages:");
|
|
33137
|
+
return (end === -1 ? raw : raw.slice(0, end)).map((l) => l.trim());
|
|
33138
|
+
}
|
|
33139
|
+
const start = raw.findIndex((l, i) => i > importers && /^ {2}(['"]?)\.\1:\s*$/.test(l));
|
|
33140
|
+
if (start === -1)
|
|
33141
|
+
return null;
|
|
33142
|
+
const out = [];
|
|
33143
|
+
for (const line of raw.slice(start + 1)) {
|
|
33144
|
+
if (line.trim() === "")
|
|
33145
|
+
continue;
|
|
33146
|
+
if (!line.startsWith(" "))
|
|
33147
|
+
break;
|
|
33148
|
+
out.push(line.trim());
|
|
33149
|
+
}
|
|
33150
|
+
return out;
|
|
33151
|
+
}
|
|
33152
|
+
function unfrozenInstall(command) {
|
|
33153
|
+
const lead = /^(?:(?:sudo|time)\s+|env(?:\s+[A-Za-z_]\w*=\S*)*\s+)*/.exec(command)?.[0] ?? "";
|
|
33154
|
+
const rest = command.slice(lead.length);
|
|
33155
|
+
const end = installEnd(rest);
|
|
33156
|
+
const head = rest.slice(0, end);
|
|
33157
|
+
const tail2 = rest.slice(end);
|
|
33158
|
+
const dropped = (flag) => head.replace(new RegExp(`\\s--${flag}(=\\S*)?(?=\\s|$)`, "g"), "");
|
|
33159
|
+
if (/^npm ci(\s|$)/.test(head)) {
|
|
33160
|
+
return lead + head.replace(/^npm ci/, "npm install --no-audit --no-fund") + tail2;
|
|
33161
|
+
}
|
|
33162
|
+
if (/^pnpm\b/.test(head)) {
|
|
33163
|
+
if (/\s--no-frozen-lockfile(?=\s|$)/.test(head))
|
|
33164
|
+
return command;
|
|
33165
|
+
const bare = dropped("frozen-lockfile");
|
|
33166
|
+
const withFlag = bare.replace(/^pnpm (install-test|install|it|i)(?=\s|$)/, "$& --no-frozen-lockfile");
|
|
33167
|
+
if (withFlag !== bare)
|
|
33168
|
+
return lead + withFlag + tail2;
|
|
33169
|
+
return bare === head ? command : `${lead}${bare} --no-frozen-lockfile${tail2}`;
|
|
33170
|
+
}
|
|
33171
|
+
if (/^yarn\b/.test(head)) {
|
|
33172
|
+
const bare = dropped("frozen-lockfile").replace(/\s--immutable(=\S*)?(?=\s|$)/g, "");
|
|
33173
|
+
const assigns = lead === "" || /(^|\s)env(\s+[A-Za-z_]\w*=\S*)*\s+$/.test(lead);
|
|
33174
|
+
return `${lead}${assigns ? "" : "env "}${YARN_MUTABLE} ${bare}${tail2}`;
|
|
33175
|
+
}
|
|
33176
|
+
return command;
|
|
33177
|
+
}
|
|
33178
|
+
function installEnd(command) {
|
|
33179
|
+
let quote = null;
|
|
33180
|
+
for (let i = 0; i < command.length; i += 1) {
|
|
33181
|
+
const c = command[i];
|
|
33182
|
+
if (quote !== null) {
|
|
33183
|
+
if (c === quote)
|
|
33184
|
+
quote = null;
|
|
33185
|
+
else if (c === "\\" && quote === '"')
|
|
33186
|
+
i += 1;
|
|
33187
|
+
continue;
|
|
33188
|
+
}
|
|
33189
|
+
if (c === "\\") {
|
|
33190
|
+
i += 1;
|
|
33191
|
+
} else if (c === '"' || c === "'") {
|
|
33192
|
+
quote = c;
|
|
33193
|
+
} else if (c === ";" || c === "&" && command[i + 1] === "&" || c === "|" && command[i + 1] === "|") {
|
|
33194
|
+
return command.slice(0, i).trimEnd().length;
|
|
33195
|
+
}
|
|
33196
|
+
}
|
|
33197
|
+
return command.length;
|
|
33198
|
+
}
|
|
33046
33199
|
function pythonTestCommand(repo) {
|
|
33047
33200
|
const pyproject = repo.readText("pyproject.toml") ?? "";
|
|
33048
33201
|
if (pyproject.includes("[tool.pytest.ini_options]"))
|
|
@@ -33283,7 +33436,7 @@ function matchFirst(text, pattern) {
|
|
|
33283
33436
|
const match2 = pattern.exec(text);
|
|
33284
33437
|
return match2 === null ? null : match2[1];
|
|
33285
33438
|
}
|
|
33286
|
-
var RUNTIME_MANIFESTS, MAKEFILE_NAMES, C_FAMILY_SOURCE, MANIFEST_FILENAMES, JVM_BUILD_FILES, NPM_PLACEHOLDER_TEST, REQUIREMENT_SPECIFIER, CPP_BUILD_FILES, CMAKE_DECLARES_TESTS, EXACT_VERSION;
|
|
33439
|
+
var RUNTIME_MANIFESTS, MAKEFILE_NAMES, C_FAMILY_SOURCE, MANIFEST_FILENAMES, JVM_BUILD_FILES, NPM_PLACEHOLDER_TEST, DEPENDENCY_SECTIONS, YARN_MUTABLE, REQUIREMENT_SPECIFIER, CPP_BUILD_FILES, CMAKE_DECLARES_TESTS, EXACT_VERSION;
|
|
33287
33440
|
var init_manifest2 = __esm({
|
|
33288
33441
|
"../../packages/envspec/dist/manifest.js"() {
|
|
33289
33442
|
"use strict";
|
|
@@ -33304,8 +33457,19 @@ var init_manifest2 = __esm({
|
|
|
33304
33457
|
MANIFEST_FILENAMES = RUNTIME_MANIFESTS.flatMap((m) => m.files).concat(["*.csproj", "*.fsproj", "*.sln"], MAKEFILE_NAMES).sort();
|
|
33305
33458
|
JVM_BUILD_FILES = ["build.gradle", "build.gradle.kts", "pom.xml"];
|
|
33306
33459
|
NPM_PLACEHOLDER_TEST = /^echo\s+["']?Error:\s*no test specified["']?\s*&&\s*exit\s+1$/;
|
|
33460
|
+
DEPENDENCY_SECTIONS = [
|
|
33461
|
+
"dependencies",
|
|
33462
|
+
"devDependencies",
|
|
33463
|
+
"optionalDependencies",
|
|
33464
|
+
"peerDependencies"
|
|
33465
|
+
];
|
|
33466
|
+
YARN_MUTABLE = "YARN_ENABLE_IMMUTABLE_INSTALLS=false";
|
|
33307
33467
|
REQUIREMENT_SPECIFIER = /^[A-Za-z0-9._-]+(\[[A-Za-z0-9._,-]+\])?([<>=!~]=?[A-Za-z0-9._*+-]+(,[<>=!~]=?[A-Za-z0-9._*+-]+)*)?$/;
|
|
33308
|
-
CPP_BUILD_FILES = [
|
|
33468
|
+
CPP_BUILD_FILES = [
|
|
33469
|
+
"CMakeLists.txt",
|
|
33470
|
+
"meson.build",
|
|
33471
|
+
...MAKEFILE_NAMES
|
|
33472
|
+
];
|
|
33309
33473
|
CMAKE_DECLARES_TESTS = /\benable_testing\s*\(|\binclude\s*\(\s*CTest\s*\)|\badd_test\s*\(/;
|
|
33310
33474
|
EXACT_VERSION = /^v?(\d+(?:\.\d+){0,2})$/;
|
|
33311
33475
|
}
|
|
@@ -33757,6 +33921,15 @@ function deriveFromReader(repo) {
|
|
|
33757
33921
|
derivedFrom.add(fallback.from);
|
|
33758
33922
|
}
|
|
33759
33923
|
}
|
|
33924
|
+
let installUnfrozen = null;
|
|
33925
|
+
if (installCommand !== null && runtime === "node") {
|
|
33926
|
+
const unfrozen = unfrozenInstall(installCommand);
|
|
33927
|
+
const gaps = unfrozen === installCommand ? null : lockfileGaps(repo);
|
|
33928
|
+
if (gaps !== null) {
|
|
33929
|
+
installCommand = unfrozen;
|
|
33930
|
+
installUnfrozen = { lockfile: gaps.lockfile, missing: gaps.missing };
|
|
33931
|
+
}
|
|
33932
|
+
}
|
|
33760
33933
|
if (installCommand === null) {
|
|
33761
33934
|
unresolved.push({
|
|
33762
33935
|
kind: "no-install-command",
|
|
@@ -33803,6 +33976,7 @@ function deriveFromReader(repo) {
|
|
|
33803
33976
|
runtime,
|
|
33804
33977
|
runtimeVersion: version.value,
|
|
33805
33978
|
installCommand,
|
|
33979
|
+
...installUnfrozen === null ? {} : { installUnfrozen },
|
|
33806
33980
|
testCommand,
|
|
33807
33981
|
services,
|
|
33808
33982
|
migrations,
|
|
@@ -34588,6 +34762,12 @@ function resolveRunEnvironment(derived, req) {
|
|
|
34588
34762
|
) : "detected";
|
|
34589
34763
|
return { spec, imageSource };
|
|
34590
34764
|
}
|
|
34765
|
+
function installUnfrozenNote(spec) {
|
|
34766
|
+
const u = spec.installUnfrozen;
|
|
34767
|
+
if (u === void 0)
|
|
34768
|
+
return null;
|
|
34769
|
+
return `${u.lockfile} does not list ${u.missing.join(", ")}, so the install ran as \`${String(spec.installCommand)}\` without its frozen-lockfile check. Regenerate ${u.lockfile} before merging.`;
|
|
34770
|
+
}
|
|
34591
34771
|
async function verifyWorkingDiff(req) {
|
|
34592
34772
|
const ctx = {
|
|
34593
34773
|
startedAt: Date.now(),
|
|
@@ -34787,6 +34967,9 @@ async function runVerification(req, ctx) {
|
|
|
34787
34967
|
const derived = deriveEnvironmentSpec(cloneDir);
|
|
34788
34968
|
const { spec, imageSource } = resolveRunEnvironment(derived, req);
|
|
34789
34969
|
progress("derive", `runtime=${spec.runtime} install=${String(spec.installCommand)} test=${String(spec.testCommand)}`);
|
|
34970
|
+
const unfrozenNote = installUnfrozenNote(spec);
|
|
34971
|
+
if (unfrozenNote !== null)
|
|
34972
|
+
progress("derive", unfrozenNote);
|
|
34790
34973
|
refuseUnbuildableSpec(spec);
|
|
34791
34974
|
const image = placement.imageFor(spec.runtime, req.image, spec.runtimeVersion, imageVariantFor(spec));
|
|
34792
34975
|
const resolved = await resolveLease(placement, runId);
|
|
@@ -35919,6 +36102,7 @@ __export(dist_exports, {
|
|
|
35919
36102
|
imageVariantFor: () => imageVariantFor,
|
|
35920
36103
|
installCommandFor: () => installCommandFor,
|
|
35921
36104
|
installLocalMigrationTooling: () => installLocalMigrationTooling,
|
|
36105
|
+
installUnfrozenNote: () => installUnfrozenNote,
|
|
35922
36106
|
isBookkeepingTable: () => isBookkeepingTable,
|
|
35923
36107
|
isCommandUnavailable: () => isCommandUnavailable,
|
|
35924
36108
|
isGreen: () => isGreen,
|
|
@@ -37853,9 +38037,13 @@ function fmtContestedWarning(b) {
|
|
|
37853
38037
|
}
|
|
37854
38038
|
return ` \u26A0 This issue looks taken: ${parts.join(" / ")}. A merged PR here is unlikely.`;
|
|
37855
38039
|
}
|
|
37856
|
-
async function mintRegistrationProof({ claimRef } = {}) {
|
|
37857
|
-
console.log("
|
|
37858
|
-
|
|
38040
|
+
async function mintRegistrationProof({ claimRef, intro } = {}) {
|
|
38041
|
+
console.log("");
|
|
38042
|
+
for (const line of intro ?? [
|
|
38043
|
+
"This posting registers your claim with terminalhire, so your",
|
|
38044
|
+
"GitHub identity has to be verified once in the browser."
|
|
38045
|
+
])
|
|
38046
|
+
console.log(` ${line}`);
|
|
37859
38047
|
let oauthBase;
|
|
37860
38048
|
try {
|
|
37861
38049
|
oauthBase = resolveOAuthBase();
|
|
@@ -38182,17 +38370,18 @@ function readCredentialDisposition({
|
|
|
38182
38370
|
return "store-and-clear-stale-marker";
|
|
38183
38371
|
}
|
|
38184
38372
|
async function bootstrapFounderClaimEnrollment(claim, registration) {
|
|
38185
|
-
if (!(claim.amountUSD > 0)) return { stored: false, reason: "free" };
|
|
38186
38373
|
if (!registration.pushToken || !registration.claimantLogin) {
|
|
38187
38374
|
return { stored: false, reason: "token-unavailable" };
|
|
38188
38375
|
}
|
|
38376
|
+
const current = await readPushTokenEnc().catch(() => null);
|
|
38189
38377
|
const disposition = readCredentialDisposition({
|
|
38190
38378
|
markerExists: Boolean(readAutoMarker()),
|
|
38191
|
-
enrolledTokenExists: Boolean(
|
|
38379
|
+
enrolledTokenExists: Boolean(current)
|
|
38192
38380
|
});
|
|
38193
38381
|
if (disposition === "keep-enrolled") {
|
|
38194
38382
|
return { stored: false, reason: "kept-enrolled" };
|
|
38195
38383
|
}
|
|
38384
|
+
if (current && current === registration.pushToken) return { stored: true, reason: "ok" };
|
|
38196
38385
|
try {
|
|
38197
38386
|
await writePushTokenEnc(registration.pushToken);
|
|
38198
38387
|
if (disposition === "store-and-clear-stale-marker") clearAutoMarker();
|
|
@@ -38413,7 +38602,7 @@ terminalhire claim: refusing to record \u2014 read ${b.repoFullName}'s contribut
|
|
|
38413
38602
|
console.log(
|
|
38414
38603
|
` registered with terminalhire${claim.approval.claimId ? ` (server claim ${claim.approval.claimId})` : ""}`
|
|
38415
38604
|
);
|
|
38416
|
-
if (
|
|
38605
|
+
if (enrollment) {
|
|
38417
38606
|
if (enrollment?.stored) {
|
|
38418
38607
|
console.log(" \u2713 credential for your granted slice + CI results stored encrypted here");
|
|
38419
38608
|
console.log(" Background dashboard updates are NOT on (claiming does not enable them).");
|
|
@@ -38426,7 +38615,7 @@ terminalhire claim: refusing to record \u2014 read ${b.repoFullName}'s contribut
|
|
|
38426
38615
|
if (enrollment?.detail) {
|
|
38427
38616
|
for (const line of String(enrollment.detail).split("\n")) console.log(` ${line}`);
|
|
38428
38617
|
}
|
|
38429
|
-
console.log("
|
|
38618
|
+
console.log(" `claim start` will ask you to confirm in the browser once more.");
|
|
38430
38619
|
}
|
|
38431
38620
|
}
|
|
38432
38621
|
if (claim.approval.state === "pending") {
|
|
@@ -38588,7 +38777,7 @@ async function cmdList(active) {
|
|
|
38588
38777
|
);
|
|
38589
38778
|
return;
|
|
38590
38779
|
}
|
|
38591
|
-
await syncFounderApprovals(claims, list);
|
|
38780
|
+
const { approvalsChecked } = await syncFounderApprovals(claims, list);
|
|
38592
38781
|
list = claims.listClaims({ active });
|
|
38593
38782
|
await syncFounderVerdicts({
|
|
38594
38783
|
claimsModule: claims,
|
|
@@ -38606,7 +38795,7 @@ ${list.length} ${active ? "active " : ""}claim${list.length === 1 ? "" : "s"}:
|
|
|
38606
38795
|
`);
|
|
38607
38796
|
for (const c of list) {
|
|
38608
38797
|
const pr = c.prUrl ? ` \xB7 ${c.prUrl}` : "";
|
|
38609
|
-
console.log(` [${
|
|
38798
|
+
console.log(` [${claimListTurnLabel(c, approvalsChecked)}] ${fmtClaimAmount(c)} \xB7 ${c.title}`);
|
|
38610
38799
|
console.log(` id: ${c.id} \xB7 ${c.state}${pr}`);
|
|
38611
38800
|
}
|
|
38612
38801
|
printMetric(claims.acceptedPRRate());
|
|
@@ -38714,6 +38903,12 @@ async function syncFounderApprovals(claimsModule, targets) {
|
|
|
38714
38903
|
}
|
|
38715
38904
|
return { pushToken, approvalsChecked, approvalsUnavailable };
|
|
38716
38905
|
}
|
|
38906
|
+
function claimListTurnLabel(claim, approvalsChecked) {
|
|
38907
|
+
if (approvalsChecked && claim.state === "claimed" && claim.approval?.mode === "approval-only" && claim.approval?.state === "pending") {
|
|
38908
|
+
return "waiting on poster";
|
|
38909
|
+
}
|
|
38910
|
+
return claimTurnLabel(claim.state);
|
|
38911
|
+
}
|
|
38717
38912
|
function founderClaimStanding(claim, approvalsChecked) {
|
|
38718
38913
|
const turn = claimTurnLabel(claim.state);
|
|
38719
38914
|
switch (claim.state) {
|
|
@@ -39956,12 +40151,74 @@ terminalhire claim: ${what} needs the stored credential, and the server says it
|
|
|
39956
40151
|
}
|
|
39957
40152
|
return true;
|
|
39958
40153
|
}
|
|
39959
|
-
async function
|
|
40154
|
+
async function enrolHeldClaimReadToken(postingId) {
|
|
40155
|
+
const fail = (lines) => {
|
|
40156
|
+
for (const line of lines) console.error(line);
|
|
40157
|
+
process.exit(1);
|
|
40158
|
+
};
|
|
40159
|
+
const proofToken = await mintRegistrationProof({
|
|
40160
|
+
claimRef: opportunityShortToken(`bounty:founder:${postingId}`),
|
|
40161
|
+
intro: [
|
|
40162
|
+
"This machine holds no terminalhire credential yet. Confirm once in the",
|
|
40163
|
+
"browser, signed in as the GitHub account that claimed this work, and",
|
|
40164
|
+
"this machine is linked to your claim."
|
|
40165
|
+
]
|
|
40166
|
+
});
|
|
40167
|
+
if (!proofToken) {
|
|
40168
|
+
fail(["terminalhire claim: could not link this machine to your claim (see above)."]);
|
|
40169
|
+
}
|
|
40170
|
+
let res;
|
|
40171
|
+
try {
|
|
40172
|
+
res = await fetch(`${CLAIM_SYNC_BASE4}/api/claim/enrol`, {
|
|
40173
|
+
method: "POST",
|
|
40174
|
+
headers: { "Content-Type": "application/json" },
|
|
40175
|
+
body: JSON.stringify({ bountyId: postingId, proofToken }),
|
|
40176
|
+
signal: AbortSignal.timeout(CLAIM_SYNC_WRITE_TIMEOUT_MS)
|
|
40177
|
+
});
|
|
40178
|
+
} catch (err) {
|
|
40179
|
+
fail([
|
|
40180
|
+
`terminalhire claim: terminalhire is unreachable (${err instanceof Error ? err.message : String(err)}).`,
|
|
40181
|
+
" Nothing was stored. Run the same command again."
|
|
40182
|
+
]);
|
|
40183
|
+
}
|
|
40184
|
+
let body = null;
|
|
40185
|
+
try {
|
|
40186
|
+
body = await res.json();
|
|
40187
|
+
} catch {
|
|
40188
|
+
}
|
|
40189
|
+
if (res.status === 404 && body?.error === "no-live-claim") {
|
|
40190
|
+
const who = typeof body.claimantLogin === "string" ? `@${body.claimantLogin}` : "that account";
|
|
40191
|
+
fail([
|
|
40192
|
+
`terminalhire claim: you confirmed in the browser as ${who}, and ${who} does not`,
|
|
40193
|
+
" hold a live claim on this posting. Nothing was stored.",
|
|
40194
|
+
" Sign the browser into the GitHub account that claimed it, then run this again."
|
|
40195
|
+
]);
|
|
40196
|
+
}
|
|
40197
|
+
if (!res.ok || typeof body?.pushToken !== "string" || body.pushToken.length === 0) {
|
|
40198
|
+
fail([
|
|
40199
|
+
`terminalhire claim: linking this machine failed (${res.status}${typeof body?.message === "string" ? `: ${body.message}` : ""}). Nothing was stored.`
|
|
40200
|
+
]);
|
|
40201
|
+
}
|
|
40202
|
+
try {
|
|
40203
|
+
await writePushTokenEnc(body.pushToken);
|
|
40204
|
+
} catch (err) {
|
|
40205
|
+
fail([
|
|
40206
|
+
`terminalhire claim: the credential could not be stored on this machine: ${err instanceof Error ? err.message : String(err)}`
|
|
40207
|
+
]);
|
|
40208
|
+
}
|
|
40209
|
+
console.log(" \u2713 This machine is linked to your claim (credential stored encrypted here).");
|
|
40210
|
+
return body.pushToken;
|
|
40211
|
+
}
|
|
40212
|
+
var FOUNDER_POSTING_ID = /^fb_[A-Za-z0-9_-]{1,80}$/;
|
|
40213
|
+
async function requireReadPushToken(what, { enrolFor } = {}) {
|
|
39960
40214
|
let stored = null;
|
|
39961
40215
|
try {
|
|
39962
40216
|
stored = await readPushTokenEnc();
|
|
39963
40217
|
} catch {
|
|
39964
40218
|
}
|
|
40219
|
+
if (!stored && typeof enrolFor === "string" && FOUNDER_POSTING_ID.test(enrolFor)) {
|
|
40220
|
+
return enrolHeldClaimReadToken(enrolFor);
|
|
40221
|
+
}
|
|
39965
40222
|
if (!stored) {
|
|
39966
40223
|
console.error(
|
|
39967
40224
|
`terminalhire claim: ${what} needs the persistent push token, and none is enrolled on this machine.
|
|
@@ -39984,7 +40241,9 @@ function cloneFullTierRepo({ engine, dest, url, sha, token, branch }) {
|
|
|
39984
40241
|
async function cmdSliceFullTier(claims, id, local, fullTierBody, flags, cloneRepo = cloneFullTierRepo) {
|
|
39985
40242
|
const claimId = fullTierBody.claimId;
|
|
39986
40243
|
const bountyId = fullTierBody.bountyId;
|
|
39987
|
-
const pushToken = await requireReadPushToken("fetching your full-repo clone credential"
|
|
40244
|
+
const pushToken = await requireReadPushToken("fetching your full-repo clone credential", {
|
|
40245
|
+
enrolFor: bountyId
|
|
40246
|
+
});
|
|
39988
40247
|
let res;
|
|
39989
40248
|
try {
|
|
39990
40249
|
res = await fetch(`${CLAIM_SYNC_BASE4}/api/claim/clone-token`, {
|
|
@@ -40160,7 +40419,9 @@ async function attemptSliceDelivery(id, flags = {}) {
|
|
|
40160
40419
|
requireFounderLoopClaim(claims, id, "slice");
|
|
40161
40420
|
}
|
|
40162
40421
|
const bountyId = local ? founderPostingIdOf(local) : String(id).replace(/^bounty:founder:/, "");
|
|
40163
|
-
const pushToken = await requireReadPushToken("fetching your granted slice"
|
|
40422
|
+
const pushToken = await requireReadPushToken("fetching your granted slice", {
|
|
40423
|
+
enrolFor: bountyId
|
|
40424
|
+
});
|
|
40164
40425
|
let res;
|
|
40165
40426
|
try {
|
|
40166
40427
|
res = await fetch(`${CLAIM_SYNC_BASE4}/api/claim/slice`, {
|
|
@@ -41978,6 +42239,7 @@ export {
|
|
|
41978
42239
|
buildStakeComment,
|
|
41979
42240
|
buildStandDownComment,
|
|
41980
42241
|
buildSubmitBody,
|
|
42242
|
+
claimListTurnLabel,
|
|
41981
42243
|
claimUpdatePatch,
|
|
41982
42244
|
cloneFullTierRepo,
|
|
41983
42245
|
cmdNote,
|