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.
@@ -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 slice <id>)`;
109
+ terminalhire claim --push --keep-updated (or check one now: terminalhire claim start <id>)`;
110
110
  }
111
111
  async function syncApprovedClaims({
112
112
  readAutoMarker,
@@ -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
- matches: (_p, _segments, base) => LOCKFILE_NAMES.has(base),
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 = ["CMakeLists.txt", "meson.build", ...MAKEFILE_NAMES];
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("\n This posting registers your claim with terminalhire, so your");
37858
- console.log(" GitHub identity has to be verified once in the browser.");
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(await readPushTokenEnc().catch(() => null))
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 (claim.amountUSD > 0) {
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(" Store it before fetching your slice: terminalhire claim --push");
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(` [${claimTurnLabel(c.state)}] ${fmtClaimAmount(c)} \xB7 ${c.title}`);
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 requireReadPushToken(what) {
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,