terminalhire 0.42.23 → 0.42.24

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.
@@ -1077,8 +1077,8 @@ function makeGitHubGovernor(fetchImpl, cfg) {
1077
1077
  try {
1078
1078
  const res = bound == null ? await fetchP : await Promise.race([
1079
1079
  fetchP,
1080
- new Promise((resolve4) => {
1081
- timer = setTimeout(() => resolve4(null), bound);
1080
+ new Promise((resolve5) => {
1081
+ timer = setTimeout(() => resolve5(null), bound);
1082
1082
  })
1083
1083
  ]);
1084
1084
  if (!res || !res.ok) return null;
@@ -5136,8 +5136,8 @@ async function getWithTimeout(governor, url, token, timeoutMs) {
5136
5136
  try {
5137
5137
  const res = await Promise.race([
5138
5138
  getP,
5139
- new Promise((resolve4) => {
5140
- timer = setTimeout(() => resolve4(null), timeoutMs);
5139
+ new Promise((resolve5) => {
5140
+ timer = setTimeout(() => resolve5(null), timeoutMs);
5141
5141
  })
5142
5142
  ]);
5143
5143
  if (res === null) controller.abort();
@@ -6934,7 +6934,7 @@ function eddsa(Point, cHash, eddsaOpts = {}) {
6934
6934
  });
6935
6935
  const { prehash } = eddsaOpts;
6936
6936
  const { BASE, Fp: Fp2, Fn: Fn2 } = Point;
6937
- const randomBytes12 = eddsaOpts.randomBytes || randomBytes;
6937
+ const randomBytes13 = eddsaOpts.randomBytes || randomBytes;
6938
6938
  const adjustScalarBytes2 = eddsaOpts.adjustScalarBytes || ((bytes) => bytes);
6939
6939
  const domain = eddsaOpts.domain || ((data, ctx, phflag) => {
6940
6940
  _abool2(phflag, "phflag");
@@ -7016,7 +7016,7 @@ function eddsa(Point, cHash, eddsaOpts = {}) {
7016
7016
  signature: 2 * _size,
7017
7017
  seed: _size
7018
7018
  };
7019
- function randomSecretKey(seed = randomBytes12(lengths.seed)) {
7019
+ function randomSecretKey(seed = randomBytes13(lengths.seed)) {
7020
7020
  return _abytes2(seed, lengths.seed, "seed");
7021
7021
  }
7022
7022
  function keygen(seed) {
@@ -11342,7 +11342,7 @@ async function requireStoredLogin() {
11342
11342
  }
11343
11343
  }
11344
11344
  function sleep(ms) {
11345
- return new Promise((resolve4) => setTimeout(resolve4, ms));
11345
+ return new Promise((resolve5) => setTimeout(resolve5, ms));
11346
11346
  }
11347
11347
  var TERMINALHIRE_DIR4, TOKEN_FILE, ALGO, IV_BYTES, GITHUB_SCOPE, DEVICE_CODE_URL, ACCESS_TOKEN_URL, BAKED_IN_CLIENT_ID, MOCK_TOKEN, MOCK_LOGIN;
11348
11348
  var init_github_auth = __esm({
@@ -11759,16 +11759,16 @@ var sleep2;
11759
11759
  var init_sleep = __esm({
11760
11760
  "../../node_modules/@anthropic-ai/sdk/internal/utils/sleep.mjs"() {
11761
11761
  "use strict";
11762
- sleep2 = (ms, signal) => new Promise((resolve4) => {
11762
+ sleep2 = (ms, signal) => new Promise((resolve5) => {
11763
11763
  if (signal?.aborted)
11764
- return resolve4();
11764
+ return resolve5();
11765
11765
  const onAbort = () => {
11766
11766
  clearTimeout(timer);
11767
- resolve4();
11767
+ resolve5();
11768
11768
  };
11769
11769
  const timer = setTimeout(() => {
11770
11770
  signal?.removeEventListener("abort", onAbort);
11771
- resolve4();
11771
+ resolve5();
11772
11772
  }, ms);
11773
11773
  signal?.addEventListener("abort", onAbort, { once: true });
11774
11774
  });
@@ -13897,8 +13897,8 @@ var init_api_promise = __esm({
13897
13897
  init_parse();
13898
13898
  APIPromise = class _APIPromise extends Promise {
13899
13899
  constructor(client, responsePromise, parseResponse = defaultParseResponse) {
13900
- super((resolve4) => {
13901
- resolve4(null);
13900
+ super((resolve5) => {
13901
+ resolve5(null);
13902
13902
  });
13903
13903
  this.responsePromise = responsePromise;
13904
13904
  this.parseResponse = parseResponse;
@@ -16418,16 +16418,16 @@ var init_async_queue = __esm({
16418
16418
  if (__classPrivateFieldGet(this, _AsyncQueue_closed, "f") || signal?.aborted) {
16419
16419
  return Promise.resolve({ done: true, value: void 0 });
16420
16420
  }
16421
- return new Promise((resolve4) => {
16421
+ return new Promise((resolve5) => {
16422
16422
  const waiter = (r) => {
16423
16423
  signal?.removeEventListener("abort", onAbort);
16424
- resolve4(r);
16424
+ resolve5(r);
16425
16425
  };
16426
16426
  const onAbort = () => {
16427
16427
  const idx = __classPrivateFieldGet(this, _AsyncQueue_waiters, "f").indexOf(waiter);
16428
16428
  if (idx >= 0)
16429
16429
  __classPrivateFieldGet(this, _AsyncQueue_waiters, "f").splice(idx, 1);
16430
- resolve4({ done: true, value: void 0 });
16430
+ resolve5({ done: true, value: void 0 });
16431
16431
  };
16432
16432
  __classPrivateFieldGet(this, _AsyncQueue_waiters, "f").push(waiter);
16433
16433
  signal?.addEventListener("abort", onAbort, { once: true });
@@ -17027,13 +17027,13 @@ var init_json_schema = __esm({
17027
17027
 
17028
17028
  // ../../node_modules/@anthropic-ai/sdk/internal/utils/promise.mjs
17029
17029
  function promiseWithResolvers() {
17030
- let resolve4;
17030
+ let resolve5;
17031
17031
  let reject;
17032
17032
  const promise = new Promise((res, rej) => {
17033
- resolve4 = res;
17033
+ resolve5 = res;
17034
17034
  reject = rej;
17035
17035
  });
17036
- return { promise, resolve: resolve4, reject };
17036
+ return { promise, resolve: resolve5, reject };
17037
17037
  }
17038
17038
  var init_promise = __esm({
17039
17039
  "../../node_modules/@anthropic-ai/sdk/internal/utils/promise.mjs"() {
@@ -17635,7 +17635,7 @@ function betaGrepTool(ctx) {
17635
17635
  });
17636
17636
  }
17637
17637
  function runRipgrep(rg, pattern, searchPath, signal) {
17638
- return new Promise((resolve4, reject) => {
17638
+ return new Promise((resolve5, reject) => {
17639
17639
  const proc = cp.spawn(rg, ["-n", "--no-heading", "-e", pattern, "--", searchPath], {
17640
17640
  ...signal ? { signal } : {}
17641
17641
  });
@@ -17657,12 +17657,12 @@ function runRipgrep(rg, pattern, searchPath, signal) {
17657
17657
  if (signal?.aborted)
17658
17658
  return reject(new ToolError("grep: aborted"));
17659
17659
  if (truncated)
17660
- return resolve4(out + `
17660
+ return resolve5(out + `
17661
17661
  [output truncated at ${GREP_OUTPUT_LIMIT} bytes]`);
17662
17662
  if (code === 0)
17663
- return resolve4(out);
17663
+ return resolve5(out);
17664
17664
  if (code === 1)
17665
- return resolve4("no matches");
17665
+ return resolve5("no matches");
17666
17666
  reject(new ToolError(`grep: rg failed: ${errOut || `exit ${code}`}`));
17667
17667
  });
17668
17668
  proc.on("error", (e) => {
@@ -17840,8 +17840,8 @@ var init_node = __esm({
17840
17840
  `;
17841
17841
  __classPrivateFieldGet(this, _BashSession_proc, "f").stdin.write(wrapped);
17842
17842
  if (__classPrivateFieldGet(this, _BashSession_buf, "f").indexOf(sentinel2) < 0) {
17843
- const { promise: sentinelSeen, resolve: resolve4 } = promiseWithResolvers();
17844
- __classPrivateFieldSet(this, _BashSession_waiting, { sentinel: sentinel2, resolve: resolve4 }, "f");
17843
+ const { promise: sentinelSeen, resolve: resolve5 } = promiseWithResolvers();
17844
+ __classPrivateFieldSet(this, _BashSession_waiting, { sentinel: sentinel2, resolve: resolve5 }, "f");
17845
17845
  let timer;
17846
17846
  let onAbort;
17847
17847
  try {
@@ -19596,12 +19596,12 @@ var init_BetaMessageStream = __esm({
19596
19596
  }
19597
19597
  return this._emit("error", new AnthropicError(String(error)));
19598
19598
  });
19599
- __classPrivateFieldSet(this, _BetaMessageStream_connectedPromise, new Promise((resolve4, reject) => {
19600
- __classPrivateFieldSet(this, _BetaMessageStream_resolveConnectedPromise, resolve4, "f");
19599
+ __classPrivateFieldSet(this, _BetaMessageStream_connectedPromise, new Promise((resolve5, reject) => {
19600
+ __classPrivateFieldSet(this, _BetaMessageStream_resolveConnectedPromise, resolve5, "f");
19601
19601
  __classPrivateFieldSet(this, _BetaMessageStream_rejectConnectedPromise, reject, "f");
19602
19602
  }), "f");
19603
- __classPrivateFieldSet(this, _BetaMessageStream_endPromise, new Promise((resolve4, reject) => {
19604
- __classPrivateFieldSet(this, _BetaMessageStream_resolveEndPromise, resolve4, "f");
19603
+ __classPrivateFieldSet(this, _BetaMessageStream_endPromise, new Promise((resolve5, reject) => {
19604
+ __classPrivateFieldSet(this, _BetaMessageStream_resolveEndPromise, resolve5, "f");
19605
19605
  __classPrivateFieldSet(this, _BetaMessageStream_rejectEndPromise, reject, "f");
19606
19606
  }), "f");
19607
19607
  __classPrivateFieldGet(this, _BetaMessageStream_connectedPromise, "f").catch(() => {
@@ -19771,11 +19771,11 @@ var init_BetaMessageStream = __esm({
19771
19771
  * const message = await stream.emitted('message') // rejects if the stream errors
19772
19772
  */
19773
19773
  emitted(event) {
19774
- return new Promise((resolve4, reject) => {
19774
+ return new Promise((resolve5, reject) => {
19775
19775
  __classPrivateFieldSet(this, _BetaMessageStream_catchingPromiseCreated, true, "f");
19776
19776
  if (event !== "error")
19777
19777
  this.once("error", reject);
19778
- this.once(event, resolve4);
19778
+ this.once(event, resolve5);
19779
19779
  });
19780
19780
  }
19781
19781
  async done() {
@@ -20137,7 +20137,7 @@ var init_BetaMessageStream = __esm({
20137
20137
  if (done) {
20138
20138
  return { value: void 0, done: true };
20139
20139
  }
20140
- return new Promise((resolve4, reject) => readQueue.push({ resolve: resolve4, reject })).then((chunk2) => chunk2 ? { value: chunk2, done: false } : { value: void 0, done: true });
20140
+ return new Promise((resolve5, reject) => readQueue.push({ resolve: resolve5, reject })).then((chunk2) => chunk2 ? { value: chunk2, done: false } : { value: void 0, done: true });
20141
20141
  }
20142
20142
  const chunk = pushQueue.shift();
20143
20143
  return { value: chunk, done: false };
@@ -22375,12 +22375,12 @@ var init_MessageStream = __esm({
22375
22375
  }
22376
22376
  return this._emit("error", new AnthropicError(String(error)));
22377
22377
  });
22378
- __classPrivateFieldSet(this, _MessageStream_connectedPromise, new Promise((resolve4, reject) => {
22379
- __classPrivateFieldSet(this, _MessageStream_resolveConnectedPromise, resolve4, "f");
22378
+ __classPrivateFieldSet(this, _MessageStream_connectedPromise, new Promise((resolve5, reject) => {
22379
+ __classPrivateFieldSet(this, _MessageStream_resolveConnectedPromise, resolve5, "f");
22380
22380
  __classPrivateFieldSet(this, _MessageStream_rejectConnectedPromise, reject, "f");
22381
22381
  }), "f");
22382
- __classPrivateFieldSet(this, _MessageStream_endPromise, new Promise((resolve4, reject) => {
22383
- __classPrivateFieldSet(this, _MessageStream_resolveEndPromise, resolve4, "f");
22382
+ __classPrivateFieldSet(this, _MessageStream_endPromise, new Promise((resolve5, reject) => {
22383
+ __classPrivateFieldSet(this, _MessageStream_resolveEndPromise, resolve5, "f");
22384
22384
  __classPrivateFieldSet(this, _MessageStream_rejectEndPromise, reject, "f");
22385
22385
  }), "f");
22386
22386
  __classPrivateFieldGet(this, _MessageStream_connectedPromise, "f").catch(() => {
@@ -22550,11 +22550,11 @@ var init_MessageStream = __esm({
22550
22550
  * const message = await stream.emitted('message') // rejects if the stream errors
22551
22551
  */
22552
22552
  emitted(event) {
22553
- return new Promise((resolve4, reject) => {
22553
+ return new Promise((resolve5, reject) => {
22554
22554
  __classPrivateFieldSet(this, _MessageStream_catchingPromiseCreated, true, "f");
22555
22555
  if (event !== "error")
22556
22556
  this.once("error", reject);
22557
- this.once(event, resolve4);
22557
+ this.once(event, resolve5);
22558
22558
  });
22559
22559
  }
22560
22560
  async done() {
@@ -22875,7 +22875,7 @@ var init_MessageStream = __esm({
22875
22875
  if (done) {
22876
22876
  return { value: void 0, done: true };
22877
22877
  }
22878
- return new Promise((resolve4, reject) => readQueue.push({ resolve: resolve4, reject })).then((chunk2) => chunk2 ? { value: chunk2, done: false } : { value: void 0, done: true });
22878
+ return new Promise((resolve5, reject) => readQueue.push({ resolve: resolve5, reject })).then((chunk2) => chunk2 ? { value: chunk2, done: false } : { value: void 0, done: true });
22879
22879
  }
22880
22880
  const chunk = pushQueue.shift();
22881
22881
  return { value: chunk, done: false };
@@ -25973,7 +25973,7 @@ function splitProgressChunk(chunk) {
25973
25973
  }
25974
25974
  function shStream(cmd, args, opts = {}) {
25975
25975
  const cap = opts.maxStderrBytes ?? 64 * 1024;
25976
- return new Promise((resolve4, reject) => {
25976
+ return new Promise((resolve5, reject) => {
25977
25977
  void (async () => {
25978
25978
  const spawn6 = opts.spawnFn ?? (await import("child_process")).spawn;
25979
25979
  const child = spawn6(cmd, args, { shell: false, stdio: ["ignore", "pipe", "pipe"] });
@@ -25991,7 +25991,7 @@ function shStream(cmd, args, opts = {}) {
25991
25991
  child.on("error", (err) => reject(err));
25992
25992
  child.on("close", (code, signal) => {
25993
25993
  if (code === 0) {
25994
- resolve4({ stdout: stdout.trim(), stderr });
25994
+ resolve5({ stdout: stdout.trim(), stderr });
25995
25995
  return;
25996
25996
  }
25997
25997
  const err = new Error(
@@ -26058,6 +26058,20 @@ function isCommandUnavailable(facts, countsRead) {
26058
26058
  return false;
26059
26059
  return facts.stdout.trim().length === 0 && EXEC_FAILURE.test(facts.stderr);
26060
26060
  }
26061
+ function isMissingSystemDependency(facts, countsRead) {
26062
+ if (countsRead)
26063
+ return null;
26064
+ if (facts.exitCode === 0 || facts.exitCode === null)
26065
+ return null;
26066
+ const out = `${facts.stdout}
26067
+ ${facts.stderr}`;
26068
+ for (const pattern of MISSING_SYSTEM_DEPENDENCY) {
26069
+ const hit = pattern.exec(out);
26070
+ if (hit)
26071
+ return hit[0];
26072
+ }
26073
+ return null;
26074
+ }
26061
26075
  function isOfflineBuildGap(facts, counts) {
26062
26076
  if (facts.runtime !== "jvm")
26063
26077
  return null;
@@ -26098,6 +26112,14 @@ function classifyVerification(facts) {
26098
26112
  reason: `the test command could not be invoked (exit ${String(facts.exitCode)}). This is an environment failure on our side \u2014 a missing interpreter, a binary outside PATH, or a dependency installed somewhere the command cannot see it. The repo has not been judged.`
26099
26113
  };
26100
26114
  }
26115
+ const missing = isMissingSystemDependency(facts, counts !== null);
26116
+ if (missing !== null) {
26117
+ return {
26118
+ outcome: "test-command-unavailable",
26119
+ counts,
26120
+ reason: `the build needs a system library or header our image does not carry (${missing}). This is a gap in our environment, not a failure in the repo. The repo has not been judged.`
26121
+ };
26122
+ }
26101
26123
  const gap = isOfflineBuildGap(facts, counts);
26102
26124
  if (gap !== null) {
26103
26125
  return {
@@ -26158,9 +26180,9 @@ function isGreen(outcome) {
26158
26180
  return outcome === "completed";
26159
26181
  }
26160
26182
  function isOurFault(outcome) {
26161
- return outcome === "test-command-unavailable" || outcome === "counts-unparsed";
26183
+ return OUR_FAULT[outcome];
26162
26184
  }
26163
- var VERIFICATION_OUTCOMES, int, withSuiteFailures, READERS, SUPPORTED_RUNNERS, COVERAGE_TABLE, EXEC_FAILURE, SUITE_REPORTED_FAILURE, OFFLINE_BUILD_GAP;
26185
+ var VERIFICATION_OUTCOMES, int, withSuiteFailures, READERS, SUPPORTED_RUNNERS, COVERAGE_TABLE, EXEC_FAILURE, SUITE_REPORTED_FAILURE, MISSING_SYSTEM_DEPENDENCY, OFFLINE_BUILD_GAP, OUR_FAULT;
26164
26186
  var init_classify2 = __esm({
26165
26187
  "../../packages/envrun/dist/classify.js"() {
26166
26188
  "use strict";
@@ -26317,6 +26339,23 @@ var init_classify2 = __esm({
26317
26339
  };
26318
26340
  }
26319
26341
  },
26342
+ {
26343
+ // CTest's closing summary: `50% tests passed, 1 tests failed out of 2`. Measured
26344
+ // on cmake 3.25 in the cpp image (TERM-1123); the singular `1 test failed` is
26345
+ // CTest's own form for one. Anchored at line start, so a log line quoting it
26346
+ // is not read as the summary. Meson and a bare `make test` print no summary we
26347
+ // can trust and stay unparsed.
26348
+ runner: "ctest",
26349
+ read: (out) => {
26350
+ if (/^No tests were found!!!$/m.test(out))
26351
+ return { tests_passed: 0, tests_failed: 0 };
26352
+ const m = /^\d+% tests passed, (\d+) tests? failed out of (\d+)$/m.exec(out);
26353
+ if (!m)
26354
+ return null;
26355
+ const failed = Number(m[1]);
26356
+ return { tests_passed: Number(m[2]) - failed, tests_failed: failed };
26357
+ }
26358
+ },
26320
26359
  {
26321
26360
  // mocha: ` 440 passing (1s)` and ` 2 failing`
26322
26361
  runner: "mocha",
@@ -26445,6 +26484,11 @@ var init_classify2 = __esm({
26445
26484
  "\\d+\\s+examples?,\\s+(?!0\\b)\\d+\\s+failures?"
26446
26485
  // rspec's summary line
26447
26486
  ].join("|"), "m");
26487
+ MISSING_SYSTEM_DEPENDENCY = [
26488
+ /^(?:\S*\/)?ld(?:\.\w+)?: cannot find -l[\w.+-]+/m,
26489
+ /^\S+:\d+(?::\d+)?: fatal error: [\w./+-]+\.(?:h|hh|hpp|hxx): No such file or directory$/m,
26490
+ /^\S+: error while loading shared libraries: lib[\w.+-]*: cannot open shared object file/m
26491
+ ];
26448
26492
  OFFLINE_BUILD_GAP = [
26449
26493
  // Maven, `-o`: `Cannot access central (…) in offline mode and the artifact … has
26450
26494
  // not been downloaded from it before.`
@@ -26460,6 +26504,15 @@ var init_classify2 = __esm({
26460
26504
  /No matching toolchains found for requested specification/,
26461
26505
  /Cannot find a Java installation on your machine matching/
26462
26506
  ];
26507
+ OUR_FAULT = {
26508
+ completed: false,
26509
+ "tests-failed": false,
26510
+ "no-tests-observed": false,
26511
+ "budget-exceeded": false,
26512
+ "counts-unparsed": true,
26513
+ "test-command-unavailable": true,
26514
+ "environment-exhausted": true
26515
+ };
26463
26516
  }
26464
26517
  });
26465
26518
 
@@ -26493,7 +26546,8 @@ function scrubEnv(source, opts) {
26493
26546
  if (!tmpDir.startsWith("/")) {
26494
26547
  throw new SandboxEnvError(`tmpDir must be an absolute path, got ${JSON.stringify(tmpDir)}`);
26495
26548
  }
26496
- for (const realHome of realHomeCandidates(source)) {
26549
+ const homesToCompare = opts.jailHomeNamespace === "guest" ? [] : realHomeCandidates(source);
26550
+ for (const realHome of homesToCompare) {
26497
26551
  if (jailHome === realHome || jailHome === `${realHome}/`) {
26498
26552
  throw new SandboxEnvError("jailHome is the real HOME \u2014 the sandbox would read the user profile");
26499
26553
  }
@@ -26582,8 +26636,8 @@ var init_reap = __esm({
26582
26636
 
26583
26637
  // ../../packages/containment/dist/fence.js
26584
26638
  import { spawn as spawn3, spawnSync as spawnSync2 } from "child_process";
26585
- import { existsSync as existsSync9, mkdirSync as mkdirSync3, realpathSync, writeFileSync as writeFileSync10 } from "fs";
26586
- import { dirname as dirname6, isAbsolute as isAbsolute3, join as join19, posix as posix2 } from "path";
26639
+ import { existsSync as existsSync9, lchownSync, lstatSync as lstatSync3, mkdirSync as mkdirSync3, readdirSync as readdirSync2, realpathSync, statSync as statSync3, writeFileSync as writeFileSync10 } from "fs";
26640
+ import { basename as basename4, dirname as dirname6, isAbsolute as isAbsolute3, join as join19, posix as posix2, resolve as resolve4, sep as sep5 } from "path";
26587
26641
  import { fileURLToPath as fileURLToPath2 } from "url";
26588
26642
  function canonical(path5, label) {
26589
26643
  if (!isAbsolute3(path5)) {
@@ -26610,7 +26664,7 @@ function resolverFor(domain) {
26610
26664
  function pathDomainOf(spec) {
26611
26665
  return spec.pathDomain ?? "local";
26612
26666
  }
26613
- function jailPasswd(uid = idOrNull("getuid"), gid = idOrNull("getgid")) {
26667
+ function jailPasswd(uid = localGuestUser()?.uid ?? null, gid = localGuestUser()?.gid ?? null) {
26614
26668
  const rows = ["root:x:0:0:root:/root:/usr/sbin/nologin"];
26615
26669
  if (uid !== null && gid !== null && uid !== 0) {
26616
26670
  rows.push(`${FENCE_USER}:x:${uid}:${gid}:fenced build user:${GUEST_JAIL}:/usr/sbin/nologin`);
@@ -26619,7 +26673,7 @@ function jailPasswd(uid = idOrNull("getuid"), gid = idOrNull("getgid")) {
26619
26673
  return `${rows.join("\n")}
26620
26674
  `;
26621
26675
  }
26622
- function jailGroup(gid = idOrNull("getgid")) {
26676
+ function jailGroup(gid = localGuestUser()?.gid ?? null) {
26623
26677
  const rows = ["root:x:0:"];
26624
26678
  if (gid !== null && gid !== 0)
26625
26679
  rows.push(`${FENCE_USER}:x:${gid}:`);
@@ -26631,19 +26685,139 @@ function idOrNull(fn) {
26631
26685
  const f = process[fn];
26632
26686
  return typeof f === "function" ? f.call(process) : null;
26633
26687
  }
26634
- function buildJail(root, guestUser) {
26688
+ function hostIds() {
26689
+ const uid = idOrNull("getuid");
26690
+ const gid = idOrNull("getgid");
26691
+ return uid === null || gid === null ? null : { uid, gid };
26692
+ }
26693
+ function localGuestUser(ids2 = hostIds()) {
26694
+ if (ids2 === null)
26695
+ return null;
26696
+ return ids2.uid === 0 ? REMAPPED_GUEST_USER : ids2;
26697
+ }
26698
+ function chownTree(dir, user) {
26699
+ const stack = [dir];
26700
+ for (let p = stack.pop(); p !== void 0; p = stack.pop()) {
26701
+ lchownSync(p, user.uid, user.gid);
26702
+ if (lstatSync3(p).isDirectory()) {
26703
+ for (const entry of readdirSync2(p))
26704
+ stack.push(join19(p, entry));
26705
+ }
26706
+ }
26707
+ }
26708
+ function canonicalPath(p) {
26709
+ let head = resolve4(p);
26710
+ const tail2 = [];
26711
+ for (; ; ) {
26712
+ try {
26713
+ return join19(realpathSync.native(head), ...tail2.reverse());
26714
+ } catch {
26715
+ const up = dirname6(head);
26716
+ if (up === head)
26717
+ return resolve4(p);
26718
+ tail2.push(basename4(head));
26719
+ head = up;
26720
+ }
26721
+ }
26722
+ }
26723
+ function statOrAbsent(p, fn) {
26724
+ try {
26725
+ return fn();
26726
+ } catch (err) {
26727
+ const code = err.code;
26728
+ if (code === "ENOENT" || code === "ENOTDIR")
26729
+ return void 0;
26730
+ throw new FenceError(`cannot stat ${JSON.stringify(p)}: ${err.message}`, {
26731
+ cause: err
26732
+ });
26733
+ }
26734
+ }
26735
+ function fileIdentity(p) {
26736
+ const st = statOrAbsent(p, () => statSync3(p, { bigint: true }));
26737
+ return st === void 0 ? null : { dev: st.dev, ino: st.ino };
26738
+ }
26739
+ function isOrContains(outer, inner) {
26740
+ const fold = process.platform === "win32" ? (s) => s.toLowerCase() : (s) => s;
26741
+ const o = fold(canonicalPath(outer));
26742
+ const i = fold(canonicalPath(inner));
26743
+ const prefix = o.endsWith(sep5) ? o : o + sep5;
26744
+ if (i === o || i.startsWith(prefix))
26745
+ return true;
26746
+ const target = fileIdentity(outer);
26747
+ if (target === null || target.ino === 0n)
26748
+ return false;
26749
+ for (const start of physicalSpellings(canonicalPath(inner))) {
26750
+ for (let a = start; ; a = dirname6(a)) {
26751
+ const id = fileIdentity(a);
26752
+ if (id !== null && id.ino !== 0n && id.dev === target.dev && id.ino === target.ino) {
26753
+ return true;
26754
+ }
26755
+ if (dirname6(a) === a)
26756
+ break;
26757
+ }
26758
+ }
26759
+ return false;
26760
+ }
26761
+ function physicalSpellings(p) {
26762
+ if (process.platform !== "darwin" || p.startsWith(DARWIN_DATA_VOLUME + sep5))
26763
+ return [p];
26764
+ const onData = DARWIN_DATA_VOLUME + p;
26765
+ const logical = fileIdentity(p);
26766
+ const physical = fileIdentity(onData);
26767
+ const same = logical !== null && physical !== null && logical.dev === physical.dev && logical.ino === physical.ino;
26768
+ return same ? [p, onData] : [p];
26769
+ }
26770
+ function realHomes(who) {
26771
+ try {
26772
+ return realHomeCandidates(process.env);
26773
+ } catch (err) {
26774
+ throw new FenceError(`${who}: ${err.message}`, { cause: err });
26775
+ }
26776
+ }
26777
+ function assertHoldsNoRealHome(path5, what, who) {
26778
+ for (const home of realHomes(who)) {
26779
+ if (isOrContains(path5, home)) {
26780
+ throw new FenceError(`${who}: ${what} ${JSON.stringify(path5)} is or contains the real home ${JSON.stringify(home)} \u2014 the jail would expose the developer's profile`);
26781
+ }
26782
+ }
26783
+ }
26784
+ function assertNoForeignLinks(paths) {
26785
+ for (const p of paths) {
26786
+ const st = statOrAbsent(p, () => lstatSync3(p));
26787
+ if (st?.isSymbolicLink() === true) {
26788
+ throw new FenceError(`buildJail: ${JSON.stringify(p)} is a pre-existing symlink \u2014 buildJail would write through it, and a jail directory is never a link`);
26789
+ }
26790
+ if (st?.isFile() === true && st.nlink > 1) {
26791
+ throw new FenceError(`buildJail: ${JSON.stringify(p)} is a pre-existing file with ${String(st.nlink)} hard links \u2014 buildJail would overwrite an inode another path shares`);
26792
+ }
26793
+ }
26794
+ }
26795
+ function buildJail(root, guestUser, io = {}) {
26796
+ const ids2 = io.ids === void 0 ? hostIds() : io.ids;
26797
+ const guest = guestUser ?? localGuestUser(ids2);
26635
26798
  const jail = join19(root, JAIL_SEGMENT);
26636
26799
  const tmp = join19(root, JAIL_TMP_SEGMENT);
26637
- for (const dir of [jail, tmp, join19(jail, ".config"), join19(jail, ".cache"), join19(jail, ".npm")]) {
26800
+ const dirs = [jail, tmp, join19(jail, ".config"), join19(jail, ".cache"), join19(jail, ".npm")];
26801
+ const files = [".npmrc", JAIL_PASSWD_FILE, JAIL_GROUP_FILE, ".gitconfig"].map((f) => join19(jail, f));
26802
+ assertHoldsNoRealHome(root, "the jail root", "buildJail");
26803
+ assertNoForeignLinks([...dirs, ...files]);
26804
+ assertHoldsNoRealHome(jail, "the jail directory", "buildJail");
26805
+ assertHoldsNoRealHome(tmp, "the jail tmp directory", "buildJail");
26806
+ for (const dir of dirs) {
26638
26807
  mkdirSync3(dir, { recursive: true });
26639
26808
  }
26640
26809
  writeFileSync10(join19(jail, ".npmrc"), "", "utf8");
26641
- writeFileSync10(join19(jail, JAIL_PASSWD_FILE), guestUser ? jailPasswd(guestUser.uid, guestUser.gid) : jailPasswd(), "utf8");
26642
- writeFileSync10(join19(jail, JAIL_GROUP_FILE), guestUser ? jailGroup(guestUser.gid) : jailGroup(), "utf8");
26810
+ writeFileSync10(join19(jail, JAIL_PASSWD_FILE), guest ? jailPasswd(guest.uid, guest.gid) : jailPasswd(null, null), "utf8");
26811
+ writeFileSync10(join19(jail, JAIL_GROUP_FILE), guest ? jailGroup(guest.gid) : jailGroup(null), "utf8");
26643
26812
  writeFileSync10(join19(jail, ".gitconfig"), '[user]\n name = sandbox\n email = sandbox@localhost\n[safe]\n directory = *\n[url "https://github.com/"]\n insteadOf = ssh://git@github.com/\n insteadOf = git@github.com:\n', "utf8");
26813
+ if (ids2?.uid === 0 && guest && guest.uid !== 0) {
26814
+ const chown = io.chown ?? chownTree;
26815
+ chown(jail, guest);
26816
+ chown(tmp, guest);
26817
+ }
26644
26818
  return { jail, tmp };
26645
26819
  }
26646
- var FenceError, ContainmentError, ContainmentRefusalError, JAIL_PASSWD_FILE, JAIL_GROUP_FILE, GUEST_JAIL, FENCE_USER, JAIL_SEGMENT, JAIL_TMP_SEGMENT;
26820
+ var FenceError, ContainmentError, ContainmentRefusalError, JAIL_PASSWD_FILE, JAIL_GROUP_FILE, GUEST_JAIL, FENCE_USER, REMAPPED_GUEST_USER, JAIL_SEGMENT, JAIL_TMP_SEGMENT, DARWIN_DATA_VOLUME;
26647
26821
  var init_fence = __esm({
26648
26822
  "../../packages/containment/dist/fence.js"() {
26649
26823
  "use strict";
@@ -26664,8 +26838,10 @@ var init_fence = __esm({
26664
26838
  JAIL_GROUP_FILE = ".fence-group";
26665
26839
  GUEST_JAIL = "/fenced/jail";
26666
26840
  FENCE_USER = "fenced";
26841
+ REMAPPED_GUEST_USER = { uid: 1e3, gid: 1e3 };
26667
26842
  JAIL_SEGMENT = "jail";
26668
26843
  JAIL_TMP_SEGMENT = "tmp";
26844
+ DARWIN_DATA_VOLUME = "/System/Volumes/Data";
26669
26845
  }
26670
26846
  });
26671
26847
 
@@ -26818,7 +26994,27 @@ var init_dockerClient = __esm({
26818
26994
  // ../../packages/containment/dist/egressProxy.js
26819
26995
  import net from "net";
26820
26996
  import http from "http";
26821
- var DEFAULT_INSTALL_ALLOWLIST;
26997
+ function parseDeniedHosts(output) {
26998
+ const seen = /* @__PURE__ */ new Set();
26999
+ for (const line of output.split(/\r?\n/)) {
27000
+ if (!line.startsWith(DENIAL_PREFIX))
27001
+ continue;
27002
+ let parsed;
27003
+ try {
27004
+ parsed = JSON.parse(line.slice(DENIAL_PREFIX.length));
27005
+ } catch {
27006
+ continue;
27007
+ }
27008
+ const host = parsed?.host;
27009
+ if (typeof host !== "string")
27010
+ continue;
27011
+ const h = host.toLowerCase().replace(/\.$/, "");
27012
+ if (HOSTNAME.test(h))
27013
+ seen.add(h);
27014
+ }
27015
+ return [...seen];
27016
+ }
27017
+ var DEFAULT_INSTALL_ALLOWLIST, DENIAL_PREFIX, HOSTNAME;
26822
27018
  var init_egressProxy = __esm({
26823
27019
  "../../packages/containment/dist/egressProxy.js"() {
26824
27020
  "use strict";
@@ -26867,24 +27063,47 @@ var init_egressProxy = __esm({
26867
27063
  // allowlist than breadth.
26868
27064
  //
26869
27065
  // What it does NOT grant: `githubusercontent.com` is a DIFFERENT domain, so
26870
- // release assets and raw blobs stay denied. If a real install needs them,
26871
- // that should arrive as a measured egress denial, not a pre-emptive guess.
27066
+ // raw blobs and `objects.githubusercontent.com` stay denied; only
27067
+ // `release-assets.githubusercontent.com` is listed, above, for the Gradle
27068
+ // wrapper. If a real install needs more, that should arrive as a measured
27069
+ // egress denial, not a pre-emptive guess.
26872
27070
  //
26873
- // Proportionality: install already runs with `--ignore-scripts`, and the
26874
- // fenced environment carries no GitHub token, so the added capability is
26875
- // "fetch a public tarball or ref", not "act as us".
27071
+ // WHO RUNS INSTALL SCRIPTS THROUGH THIS GRANT (TERM-1157). This comment used
27072
+ // to say install "already runs with `--ignore-scripts`". That is true of
27073
+ // `apps/merge-agent` (`src/sandbox/plan.ts` adds the flag on every branch)
27074
+ // and false of `th run`: `packages/envspec/src/manifest.ts` derives a plain
27075
+ // `npm ci`, so there every dependency's `preinstall`/`install`/`postinstall`
27076
+ // executes behind this proxy. TERM-1157 chose to keep them running.
27077
+ //
27078
+ // For `th run`, then, the grant reaches code the repo's dependencies chose,
27079
+ // not only the package manager. What bounds it:
27080
+ // - no credential of ours is in the fence: `auditEnv` (`env.ts`) refuses to
27081
+ // spawn when a variable's name or a GitHub-token-shaped value says
27082
+ // otherwise, so a script cannot act on GitHub as us;
27083
+ // - the fence holds the repo's tree and its dependencies, nothing else of
27084
+ // ours;
27085
+ // - every step after install runs with no network at all.
27086
+ // What it does NOT bound: a script can download and run any public GitHub
27087
+ // content, and one that brings its author's own token can push what the
27088
+ // fence holds to its author's account. `registry.npmjs.org` above gives the
27089
+ // same script the same reach (download anything, publish with its own
27090
+ // token), so this entry adds a second address for a capability the install
27091
+ // already has rather than a new one. That is the case for keeping it; it is
27092
+ // not a claim that GitHub is read-only from here.
26876
27093
  "github.com"
26877
27094
  ];
27095
+ DENIAL_PREFIX = "proxy-denied ";
27096
+ HOSTNAME = /^[a-z0-9](?:[a-z0-9.-]{0,251}[a-z0-9])?$/;
26878
27097
  }
26879
27098
  });
26880
27099
 
26881
27100
  // ../../packages/containment/dist/container.js
26882
27101
  import { fileURLToPath as fileURLToPath3 } from "url";
26883
27102
  import { dirname as dirname7, join as join20 } from "path";
26884
- import { chmodSync as chmodSync2, copyFileSync as copyFileSync2, existsSync as existsSync10, mkdtempSync, rmSync as rmSync6 } from "fs";
27103
+ import { chmodSync as chmodSync2, copyFileSync as copyFileSync2, existsSync as existsSync10, mkdtempSync, rmSync as rmSync6, statSync as statSync4 } from "fs";
26885
27104
  import { tmpdir } from "os";
26886
27105
  function scrubEnvPathsFor(containmentKind, host) {
26887
- return containmentKind === "container" ? { jailHome: GUEST.jail, tmpDir: GUEST.tmp } : { jailHome: host.jail, tmpDir: host.tmp };
27106
+ return containmentKind === "container" ? { jailHome: GUEST.jail, tmpDir: GUEST.tmp, jailHomeNamespace: "guest" } : { jailHome: host.jail, tmpDir: host.tmp, jailHomeNamespace: "host" };
26888
27107
  }
26889
27108
  function buildTranslation(spec) {
26890
27109
  const roots = [
@@ -26892,11 +27111,11 @@ function buildTranslation(spec) {
26892
27111
  { raw: spec.jail, guest: GUEST.jail, label: "jail" },
26893
27112
  { raw: spec.tmp, guest: GUEST.tmp, label: "tmp" }
26894
27113
  ];
26895
- const resolve4 = resolverFor(pathDomainOf(spec));
27114
+ const resolve5 = resolverFor(pathDomainOf(spec));
26896
27115
  const pairs = [];
26897
27116
  const seen = /* @__PURE__ */ new Set();
26898
27117
  for (const { raw, guest, label } of roots) {
26899
- for (const host of [raw, resolve4(raw, label)]) {
27118
+ for (const host of [raw, resolve5(raw, label)]) {
26900
27119
  if (seen.has(host))
26901
27120
  continue;
26902
27121
  seen.add(host);
@@ -26979,21 +27198,36 @@ function labelArgs(labels) {
26979
27198
  }
26980
27199
  return out;
26981
27200
  }
26982
- function hostUserFlag() {
26983
- const uid = typeof process.getuid === "function" ? process.getuid() : null;
26984
- const gid = typeof process.getgid === "function" ? process.getgid() : null;
26985
- if (uid === null || gid === null)
27201
+ function hostUserFlag(ids2 = hostIds()) {
27202
+ const guest = localGuestUser(ids2);
27203
+ if (guest === null)
26986
27204
  return [];
26987
- return [`--user=${uid}:${gid}`];
27205
+ return [`--user=${String(guest.uid)}:${String(guest.gid)}`];
26988
27206
  }
26989
- function guestUserFlag(spec) {
27207
+ function guestUserFlag(spec, ids2 = hostIds()) {
26990
27208
  const declared = spec.guestUser;
26991
27209
  if (declared)
26992
27210
  return [`--user=${declared.uid}:${declared.gid}`];
26993
27211
  if (pathDomainOf(spec) === "venue") {
26994
27212
  throw new FenceError("a venue-domain spec must declare guestUser: the tree is owned by the account that staged it on the venue, and this process\u2019s uid is a fact about a different machine. Handing the guest the local id gives it no write access to its own clone, and that EACCES reaches the classifier as the developer\u2019s suite failing.");
26995
27213
  }
26996
- return hostUserFlag();
27214
+ return hostUserFlag(ids2);
27215
+ }
27216
+ function assertTreeHandedToGuest(spec, ids2 = hostIds(), ownerOf2 = (path5) => statSync4(path5).uid) {
27217
+ if (pathDomainOf(spec) !== "local" || ids2?.uid !== 0)
27218
+ return;
27219
+ const guest = spec.guestUser ?? localGuestUser(ids2);
27220
+ if (guest === null || guest.uid === 0)
27221
+ return;
27222
+ for (const [label, path5] of [
27223
+ ["clone", spec.clone],
27224
+ ["jail", spec.jail]
27225
+ ]) {
27226
+ const owner = ownerOf2(path5);
27227
+ if (owner !== guest.uid) {
27228
+ throw new FenceError(`refusing to run: this process is root, so the guest runs as uid ${String(guest.uid)}, but the ${label} at ${path5} is owned by uid ${String(owner)}. The guest could not write its own tree and the EACCES would read as the developer\u2019s suite failing. Hand it over first (chownTree, or buildJail for the jail).`);
27229
+ }
27230
+ }
26997
27231
  }
26998
27232
  function validateVolumeName(name, label) {
26999
27233
  if (!VOLUME_NAME.test(name) || name.length > VOLUME_NAME_MAX) {
@@ -27003,14 +27237,17 @@ function validateVolumeName(name, label) {
27003
27237
  }
27004
27238
  function stageMounts(spec) {
27005
27239
  const domain = pathDomainOf(spec);
27006
- const resolve4 = resolverFor(domain);
27240
+ const resolve5 = resolverFor(domain);
27007
27241
  const volumes = spec.stageVolumes;
27008
27242
  if (domain === "venue") {
27243
+ if (spec.cloneVolume !== void 0) {
27244
+ throw new FenceError("a venue-domain spec must not declare cloneVolume: the venue names both of its volumes in stageVolumes, and a second name for the clone would leave two answers to which one is mounted.");
27245
+ }
27009
27246
  if (volumes === void 0) {
27010
27247
  throw new FenceError("a venue-domain spec must declare stageVolumes: every writable host path on the venue is mounted noexec, so a bind mount of the staged clone cannot run the binaries an install step downloads (esbuild, swc, sharp, node-gyp \u2014 EACCES). The venue that staged the tree names the volumes it populated; a spec without them would reproduce that EACCES and report it as the developer\u2019s suite failing.");
27011
27248
  }
27012
- resolve4(spec.clone, "clone");
27013
- resolve4(spec.jail, "jail");
27249
+ resolve5(spec.clone, "clone");
27250
+ resolve5(spec.jail, "jail");
27014
27251
  return [
27015
27252
  `--volume=${validateVolumeName(volumes.clone, "the clone volume")}:${GUEST.clone}:rw`,
27016
27253
  `--volume=${validateVolumeName(volumes.jail, "the jail volume")}:${GUEST.jail}:rw`
@@ -27019,20 +27256,24 @@ function stageMounts(spec) {
27019
27256
  if (volumes !== void 0) {
27020
27257
  throw new FenceError("a local-domain spec must not declare stageVolumes: the paths are on this machine and ARE the mount sources, and nothing on the local path populates a volume \u2014 honouring the field would mount an empty clone. Volumes exist for the venue\u2019s noexec host only.");
27021
27258
  }
27259
+ const clone = resolve5(spec.clone, "clone");
27260
+ const cloneSource = spec.cloneVolume === void 0 ? clone : validateVolumeName(spec.cloneVolume, "the clone volume");
27261
+ const jail = resolve5(spec.jail, "jail");
27262
+ assertHoldsNoRealHome(jail, "the jail bind source", "stageMounts");
27022
27263
  return [
27023
- `--volume=${resolve4(spec.clone, "clone")}:${GUEST.clone}:rw`,
27024
- `--volume=${resolve4(spec.jail, "jail")}:${GUEST.jail}:rw`
27264
+ `--volume=${cloneSource}:${GUEST.clone}:rw`,
27265
+ `--volume=${jail}:${GUEST.jail}:rw`
27025
27266
  ];
27026
27267
  }
27027
27268
  function guestIdentityMounts(spec) {
27028
27269
  if (guestUserFlag(spec).length === 0)
27029
27270
  return [];
27030
27271
  const domain = pathDomainOf(spec);
27031
- const resolve4 = resolverFor(domain);
27272
+ const resolve5 = resolverFor(domain);
27032
27273
  const under = domain === "venue" ? venueJoin : join20;
27033
- const jail = resolve4(spec.jail, "jail");
27034
- const passwd = resolve4(under(jail, JAIL_PASSWD_FILE), "the jail passwd file");
27035
- const group = resolve4(under(jail, JAIL_GROUP_FILE), "the jail group file");
27274
+ const jail = resolve5(spec.jail, "jail");
27275
+ const passwd = resolve5(under(jail, JAIL_PASSWD_FILE), "the jail passwd file");
27276
+ const group = resolve5(under(jail, JAIL_GROUP_FILE), "the jail group file");
27036
27277
  return [`--volume=${passwd}:/etc/passwd:ro`, `--volume=${group}:/etc/group:ro`];
27037
27278
  }
27038
27279
  function containerArgs(spec, env, opts) {
@@ -27365,7 +27606,14 @@ async function startProxySidecar(d, allow, idBase, staged, labels) {
27365
27606
  await waitForProxyReady(d, proxyName);
27366
27607
  return {
27367
27608
  net: { network: netInt, proxyHost: proxyName, proxyPort: SIDECAR_PROXY_PORT },
27368
- teardown
27609
+ teardown,
27610
+ // A `docker logs` that fails yields no hosts. That loses the hint and
27611
+ // changes nothing else: the list only ever adds words to a reason.
27612
+ deniedHosts: () => {
27613
+ const logs = dockerSync(d, ["logs", proxyName]);
27614
+ return logs.ok ? parseDeniedHosts(`${logs.stdout}
27615
+ ${logs.stderr}`) : [];
27616
+ }
27369
27617
  };
27370
27618
  } catch (err) {
27371
27619
  teardown();
@@ -27431,6 +27679,7 @@ async function runContainedOn(d, spec, env, opts = {}) {
27431
27679
  assertDomainDeclared(d, spec);
27432
27680
  assertNotAmbientlySteered(d, spec);
27433
27681
  assertProxyStagedForVenue(spec, opts.proxyCode);
27682
+ assertTreeHandedToGuest(spec);
27434
27683
  const leaks = auditEnv(env);
27435
27684
  if (leaks.length > 0) {
27436
27685
  throw new FenceError(`refusing to spawn: environment carries credential material (${leaks.join(", ")})`);
@@ -27451,7 +27700,8 @@ async function runContainedOn(d, spec, env, opts = {}) {
27451
27700
  net: sidecar?.net,
27452
27701
  labels: opts.labels
27453
27702
  });
27454
- return await spawnWorkload(d, idBase, argv, opts.timeoutMs ?? 9e5);
27703
+ const result = await spawnWorkload(d, idBase, argv, opts.timeoutMs ?? 9e5);
27704
+ return sidecar ? { ...result, egressDenied: sidecar.deniedHosts() } : result;
27455
27705
  } finally {
27456
27706
  sidecar?.teardown();
27457
27707
  }
@@ -27532,6 +27782,9 @@ var init_dist = __esm({
27532
27782
  });
27533
27783
 
27534
27784
  // ../../packages/envrun/dist/labels.js
27785
+ function runLabels(runId, callerLabels) {
27786
+ return { ...callerLabels ?? {}, [RUN_ID_LABEL_KEY]: runId, [RUN_LABEL_KEY]: "term-350" };
27787
+ }
27535
27788
  function censusTotal(c) {
27536
27789
  return c.containers.length + c.volumes.length + c.networks.length;
27537
27790
  }
@@ -27615,12 +27868,13 @@ function judgeLeaks(peak, after, observation) {
27615
27868
  note
27616
27869
  };
27617
27870
  }
27618
- var RUN_LABEL_KEY, LabelWatch, LEAK_STATES;
27871
+ var RUN_LABEL_KEY, RUN_ID_LABEL_KEY, LabelWatch, LEAK_STATES;
27619
27872
  var init_labels = __esm({
27620
27873
  "../../packages/envrun/dist/labels.js"() {
27621
27874
  "use strict";
27622
27875
  init_dist();
27623
27876
  RUN_LABEL_KEY = "supergoal.run";
27877
+ RUN_ID_LABEL_KEY = "supergoal.run-id";
27624
27878
  LabelWatch = class {
27625
27879
  label;
27626
27880
  docker;
@@ -27971,6 +28225,7 @@ http
27971
28225
  });
27972
28226
 
27973
28227
  // ../../packages/envrun/dist/venue.js
28228
+ import { randomBytes as randomBytes8 } from "crypto";
27974
28229
  import { join as join22 } from "path";
27975
28230
  function localJailPaths(scratchRoot) {
27976
28231
  return {
@@ -28066,11 +28321,62 @@ function coerceToString(thrown) {
28066
28321
  return UNCOERCIBLE_THROWN;
28067
28322
  }
28068
28323
  }
28324
+ function localTreeOwner(ids2 = hostIds()) {
28325
+ if (ids2?.uid !== 0)
28326
+ return void 0;
28327
+ return localGuestUser(ids2) ?? void 0;
28328
+ }
28329
+ function handLocalTreeToGuest(local, owner, chown = chownTree) {
28330
+ if (owner === void 0)
28331
+ return;
28332
+ chown(local.cloneDir, owner);
28333
+ chown(local.scratchRoot, owner);
28334
+ }
28335
+ function localCloneVolumeName(runId, suffix = randomBytes8(4).toString("hex")) {
28336
+ const tail2 = `-${suffix}`;
28337
+ const safe = runId.replace(/[^a-zA-Z0-9_.-]/g, "-");
28338
+ const head = `th-clone-${safe}`.slice(0, VOLUME_NAME_MAX2 - tail2.length);
28339
+ return validateVolumeName(`${head}${tail2}`, "the local clone volume");
28340
+ }
28341
+ function localCloneFillArgv(from, volume, owner) {
28342
+ const chown = owner === null ? "" : ` && chown -R ${String(owner.uid)}:${String(owner.gid)} /dst`;
28343
+ return [
28344
+ "run",
28345
+ "--rm",
28346
+ "--network=none",
28347
+ `--volume=${from}:/src:ro`,
28348
+ `--volume=${validateVolumeName(volume, "the local clone volume")}:/dst:rw`,
28349
+ "--",
28350
+ STAGE_HELPER_IMAGE,
28351
+ "sh",
28352
+ "-c",
28353
+ `cp -a /src/. /dst/${chown}`
28354
+ ];
28355
+ }
28069
28356
  function acquireLocalLease(runId) {
28070
28357
  const docker3 = localDockerClient();
28071
28358
  const containment = selectContainment([containerContainmentOn(docker3)]);
28072
28359
  let released = false;
28073
28360
  const stagedProxies = [];
28361
+ const owner = localTreeOwner();
28362
+ let cloneVolume;
28363
+ const createdVolumes = [];
28364
+ const fillCloneVolume = (cloneDir) => {
28365
+ const name = localCloneVolumeName(runId);
28366
+ const created = docker3.sync(["volume", "create", `--label=${STAGE_VOLUME_LABEL_KEY}=${runId}`, "--", name], { timeoutMs: LOCAL_VOLUME_CREATE_TIMEOUT_MS });
28367
+ if (created.error || created.status !== 0) {
28368
+ throw new ContainmentRefusalError(`could not create the clone volume ${name} on the local daemon: ${(created.error?.message ?? created.stderr).trim().slice(0, 300)}`);
28369
+ }
28370
+ createdVolumes.push(name);
28371
+ const from = resolverFor("local")(cloneDir, "clone");
28372
+ const filled = docker3.sync(localCloneFillArgv(from, name, localGuestUser()), {
28373
+ timeoutMs: LOCAL_CLONE_FILL_TIMEOUT_MS
28374
+ });
28375
+ if (filled.error || filled.status !== 0) {
28376
+ throw new ContainmentRefusalError(`could not copy the clone into its volume ${name}: ${(filled.error?.message ?? filled.stderr).trim().slice(0, 300)}`);
28377
+ }
28378
+ return name;
28379
+ };
28074
28380
  const lease = {
28075
28381
  kind: "local",
28076
28382
  runId,
@@ -28080,28 +28386,32 @@ function acquireLocalLease(runId) {
28080
28386
  // the paths are on this machine AND `canonical()` is what should resolve
28081
28387
  // them, which is the behaviour every local run has always had.
28082
28388
  pathDomain: "local",
28389
+ // Spread rather than `guestUser: owner`, so a non-root lease has no such key
28390
+ // at all — the same object every local run has always had.
28391
+ ...owner === void 0 ? {} : { guestUser: owner },
28083
28392
  get released() {
28084
28393
  return released;
28085
28394
  },
28086
- stage: (local) => (
28087
- // The local venue IS the developer's machine, so staging is the identity
28088
- // and the paths are already canonical here. This is not a stub: it is the
28089
- // one venue for which the answer is "nothing to copy", and having it go
28090
- // through the same method as a hosted venue is what stops `thrun.ts` from
28091
- // ever holding a path it did not get from a venue.
28092
- //
28093
- // `jail` and `tmp` are DERIVED here rather than carried in on `LocalTree`,
28094
- // because the join differs by side and the venue owns the spelling of its
28095
- // own paths: the host's `join` here, `venueJoin` on a hosted venue. The
28096
- // segments are one constant in `fence.ts`, so the tree `buildJail` wrote
28097
- // and the tree a venue mounts cannot drift apart.
28098
- Promise.resolve({
28395
+ get cloneVolume() {
28396
+ return cloneVolume;
28397
+ },
28398
+ stage: (local) => {
28399
+ if (released) {
28400
+ return Promise.reject(new LeaseReleasedError("this lease was already released, so a clone volume made now would never be removed."));
28401
+ }
28402
+ try {
28403
+ handLocalTreeToGuest(local, owner);
28404
+ cloneVolume = fillCloneVolume(local.cloneDir);
28405
+ } catch (err) {
28406
+ return Promise.reject(err instanceof Error ? err : new Error(String(err)));
28407
+ }
28408
+ return Promise.resolve({
28099
28409
  cloneDir: local.cloneDir,
28100
28410
  scratchRoot: local.scratchRoot,
28101
28411
  previewDir: local.previewDir,
28102
28412
  ...localJailPaths(local.scratchRoot)
28103
- })
28104
- ),
28413
+ });
28414
+ },
28105
28415
  // The containment function verbatim, which is the point of the barrel
28106
28416
  // re-export rather than a copy here: this venue's daemon and this process
28107
28417
  // share a filesystem, so the directory it makes under `tmpdir()` is already
@@ -28171,6 +28481,25 @@ function acquireLocalLease(runId) {
28171
28481
  const staged = stagedProxies.splice(0, stagedProxies.length);
28172
28482
  for (const s of staged)
28173
28483
  s.cleanup();
28484
+ const volumeFailures = [];
28485
+ for (const name of createdVolumes.splice(0, createdVolumes.length)) {
28486
+ const removed = docker3.sync(["volume", "rm", "-f", "--", name], {
28487
+ timeoutMs: LOCAL_VOLUME_CREATE_TIMEOUT_MS
28488
+ });
28489
+ if (removed.error || removed.status !== 0) {
28490
+ volumeFailures.push(`${name}: ${(removed.error?.message ?? removed.stderr).trim().slice(0, 200)}`);
28491
+ }
28492
+ }
28493
+ cloneVolume = void 0;
28494
+ if (volumeFailures.length > 0) {
28495
+ return Promise.resolve({
28496
+ kind: "local",
28497
+ released: true,
28498
+ alreadyReleased: false,
28499
+ error: `could not remove the clone volume: ${volumeFailures.join("; ")}`,
28500
+ detail: `the lease is closed, but a clone volume remains (label ${STAGE_VOLUME_LABEL_KEY})`
28501
+ });
28502
+ }
28174
28503
  return Promise.resolve({
28175
28504
  kind: "local",
28176
28505
  released: true,
@@ -28182,7 +28511,7 @@ function acquireLocalLease(runId) {
28182
28511
  };
28183
28512
  return lease;
28184
28513
  }
28185
- var VenueRollbackError, UNREADABLE_MESSAGE, UNREADABLE_NAME, UNCOERCIBLE_THROWN, UNDESCRIBABLE_THROWN, UNLISTABLE_ROLLBACK_FAILURES, RELEASED_LEASE_CENSUS_REASON, LeaseReleasedError;
28514
+ var VenueRollbackError, UNREADABLE_MESSAGE, UNREADABLE_NAME, UNCOERCIBLE_THROWN, UNDESCRIBABLE_THROWN, UNLISTABLE_ROLLBACK_FAILURES, RELEASED_LEASE_CENSUS_REASON, LeaseReleasedError, STAGE_HELPER_IMAGE, STAGE_VOLUME_LABEL_KEY, VOLUME_NAME_MAX2, LOCAL_VOLUME_CREATE_TIMEOUT_MS, LOCAL_CLONE_FILL_TIMEOUT_MS;
28186
28515
  var init_venue = __esm({
28187
28516
  "../../packages/envrun/dist/venue.js"() {
28188
28517
  "use strict";
@@ -28207,6 +28536,11 @@ var init_venue = __esm({
28207
28536
  LeaseReleasedError = class extends Error {
28208
28537
  name = "LeaseReleasedError";
28209
28538
  };
28539
+ STAGE_HELPER_IMAGE = "busybox:1.37.0";
28540
+ STAGE_VOLUME_LABEL_KEY = "terminalhire.stage";
28541
+ VOLUME_NAME_MAX2 = 128;
28542
+ LOCAL_VOLUME_CREATE_TIMEOUT_MS = 3e4;
28543
+ LOCAL_CLONE_FILL_TIMEOUT_MS = 6e5;
28210
28544
  }
28211
28545
  });
28212
28546
 
@@ -28305,7 +28639,7 @@ function imageForRuntime(runtime, override, version, variant) {
28305
28639
  if (!shape) {
28306
28640
  throw new RunRefusalError(`no container image is mapped for runtime ${JSON.stringify(runtime)}. Refusing to run it in the Node image: a bare "command not found" exits 127, which classifyVerification already owns as ours \u2014 but a test script that RUNS and fails inside on the missing tool exits with its own status and prints to stdout, and that falls through to tests-failed \u2014 a false red blamed on the repo.`);
28307
28641
  }
28308
- if (version === void 0 || version === null)
28642
+ if (version === void 0 || version === null || shape.fixed)
28309
28643
  return unversionedImage(shape);
28310
28644
  if (!TAG_VERSION.test(version)) {
28311
28645
  throw new RunRefusalError(`runtime version ${JSON.stringify(version)} is not a bare version, so no image tag can be built from it. Refusing rather than booting the default: the repo asked for a version, and supplying a different one silently is what TERM-643 fixed.`);
@@ -28333,8 +28667,8 @@ function resolvePublishedImage(image, runtime, version, variant) {
28333
28667
  throw new RunRefusalError(`the repo declares ${runtime} ${version}, and no image is published at ${image}` + (shape && shape.declaredIsFloor ? `. Our default is ${shape.defaultVersion}, which is OLDER than that, so falling back would run the repo under a toolchain it says it cannot use` : `. ${runtime} treats a declared version as an exact pin, not a minimum, so a different one is a different environment`) + ". Refusing rather than booting a version the repo did not ask for \u2014 that substitution is what made this class of failure unattributable (TERM-643). Pass an explicit image to override.");
28334
28668
  }
28335
28669
  function resolveImageForSpec(spec, override) {
28336
- const declared = spec.runtimeVersion;
28337
28670
  const variant = imageVariantFor(spec);
28671
+ const declared = shapeFor(spec.runtime, variant)?.fixed ? null : spec.runtimeVersion;
28338
28672
  const image = imageForRuntime(spec.runtime, override, declared, variant);
28339
28673
  const constructed = (() => {
28340
28674
  if (declared === null)
@@ -28351,12 +28685,19 @@ function resolveImageForSpec(spec, override) {
28351
28685
  return image;
28352
28686
  }
28353
28687
  function installEnvironmentFailureNote(install, image) {
28354
- const base = `the install step exited ${String(install.exitCode)}, so the test command was never invoked. The repo has not been judged; this is an environment failure.`;
28688
+ const base = `the install step exited ${String(install.exitCode)}, so the test command was never invoked. The repo has not been judged; this is an environment failure.` + refusedHostsSentence(install.egressDenied ?? []);
28355
28689
  if (!MISSING_IMAGE_SHAPE.test(`${install.stdout}
28356
28690
  ${install.stderr}`))
28357
28691
  return base;
28358
28692
  return `${base} The container image ${image} is not present on this machine \u2014 run \`docker pull ${image}\` and try again.`;
28359
28693
  }
28694
+ function refusedHostsSentence(hosts) {
28695
+ if (hosts.length === 0)
28696
+ return "";
28697
+ const named = hosts.slice(0, MAX_NAMED_REFUSED_HOSTS).join(", ");
28698
+ const rest = hosts.length - MAX_NAMED_REFUSED_HOSTS;
28699
+ return ` During install the network proxy refused ${named}` + (rest > 0 ? ` and ${String(rest)} more` : "") + ". Install reaches only the hosts on its allowlist, so a dependency whose install script downloads from any other host cannot get what it asked for.";
28700
+ }
28360
28701
  function classifySingleRun(run2) {
28361
28702
  return classifyVerification(run2).outcome;
28362
28703
  }
@@ -28418,6 +28759,8 @@ async function runEnvironmentSpec(req) {
28418
28759
  // TERM-913: the third answer about that machine — which volumes the
28419
28760
  // fence mounts in place of the noexec stage. Undefined on a local lease.
28420
28761
  stageVolumes: req.lease.stageVolumes,
28762
+ // TERM-1106: the local venue's copy of the clone. Undefined on a hosted lease.
28763
+ cloneVolume: req.lease.cloneVolume,
28421
28764
  env,
28422
28765
  image,
28423
28766
  labels,
@@ -28452,6 +28795,8 @@ async function runEnvironmentSpec(req) {
28452
28795
  // TERM-913: the third answer about that machine — which volumes the
28453
28796
  // fence mounts in place of the noexec stage. Undefined on a local lease.
28454
28797
  stageVolumes: req.lease.stageVolumes,
28798
+ // TERM-1106: the local venue's copy of the clone. Undefined on a hosted lease.
28799
+ cloneVolume: req.lease.cloneVolume,
28455
28800
  env,
28456
28801
  image,
28457
28802
  labels,
@@ -28511,6 +28856,7 @@ async function runStep(containment, r) {
28511
28856
  pathDomain: r.pathDomain,
28512
28857
  guestUser: r.guestUser,
28513
28858
  stageVolumes: r.stageVolumes,
28859
+ cloneVolume: r.cloneVolume,
28514
28860
  program: "/bin/sh",
28515
28861
  args: ["-c", withUserScriptPath(r.command)]
28516
28862
  };
@@ -28530,10 +28876,11 @@ async function runStep(containment, r) {
28530
28876
  stdout: res.stdout,
28531
28877
  stderr: res.stderr,
28532
28878
  timedOut: res.timedOut,
28533
- wallMs: Date.now() - startedAt
28879
+ wallMs: Date.now() - startedAt,
28880
+ ...res.egressDenied && res.egressDenied.length > 0 ? { egressDenied: [...new Set(res.egressDenied)] } : {}
28534
28881
  };
28535
28882
  }
28536
- var EnvRunError, RunRefusalError, MAX_CAUSE_FRAMES, CHAIN_UNREADABLE, CHAIN_TOO_DEEP, RUNTIME_IMAGES, JVM_GRADLE_IMAGE, unversionedImage, TAG_VERSION, dockerManifestProbe, manifestProbe, MISSING_IMAGE_SHAPE;
28883
+ var EnvRunError, RunRefusalError, MAX_CAUSE_FRAMES, CHAIN_UNREADABLE, CHAIN_TOO_DEEP, RUNTIME_IMAGES, JVM_GRADLE_IMAGE, unversionedImage, TAG_VERSION, dockerManifestProbe, manifestProbe, MISSING_IMAGE_SHAPE, MAX_NAMED_REFUSED_HOSTS;
28537
28884
  var init_execute = __esm({
28538
28885
  "../../packages/envrun/dist/execute.js"() {
28539
28886
  "use strict";
@@ -28620,6 +28967,26 @@ var init_execute = __esm({
28620
28967
  suffix: "",
28621
28968
  defaultVersion: "21",
28622
28969
  declaredIsFloor: true
28970
+ },
28971
+ /**
28972
+ * TERM-1123. OUR image (`packages/envrun/images/cpp/Dockerfile`): Debian bookworm
28973
+ * with the C/C++ toolchain, CMake, Meson and the common -dev libraries baked in.
28974
+ * No official image carries that set. Libraries are baked rather than installed per
28975
+ * run because the install step has network only through the allowlist, and apt is
28976
+ * not on it; a repo needing a library the image lacks gets `test-command-unavailable`
28977
+ * (`isMissingSystemDependency`), never a red.
28978
+ *
28979
+ * `fixed`: a C/C++ repo declares no compiler version envspec can read, so there is
28980
+ * nothing to derive and nothing to probe. The tag moves only when the Dockerfile
28981
+ * does. It lives in a PUBLIC repository, apart from the private venue images, so a
28982
+ * developer's own Docker can pull it with no credential.
28983
+ */
28984
+ cpp: {
28985
+ repository: "us-east1-docker.pkg.dev/terminalhire-pool/runtime-images/cpp",
28986
+ suffix: "",
28987
+ defaultVersion: "1",
28988
+ declaredIsFloor: false,
28989
+ fixed: true
28623
28990
  }
28624
28991
  };
28625
28992
  JVM_GRADLE_IMAGE = {
@@ -28640,6 +29007,7 @@ var init_execute = __esm({
28640
29007
  };
28641
29008
  manifestProbe = dockerManifestProbe;
28642
29009
  MISSING_IMAGE_SHAPE = /Unable to find image ['"][^'"]*['"] locally/i;
29010
+ MAX_NAMED_REFUSED_HOSTS = 5;
28643
29011
  }
28644
29012
  });
28645
29013
 
@@ -28714,7 +29082,7 @@ var init_dsse = __esm({
28714
29082
  });
28715
29083
 
28716
29084
  // ../../packages/attest/dist/sealedbox.js
28717
- import { createCipheriv as createCipheriv3, createDecipheriv as createDecipheriv3, diffieHellman, generateKeyPairSync as generateKeyPairSync2, hkdfSync as hkdfSync2, randomBytes as randomBytes8 } from "crypto";
29085
+ import { createCipheriv as createCipheriv3, createDecipheriv as createDecipheriv3, diffieHellman, generateKeyPairSync as generateKeyPairSync2, hkdfSync as hkdfSync2, randomBytes as randomBytes9 } from "crypto";
28718
29086
  var init_sealedbox = __esm({
28719
29087
  "../../packages/attest/dist/sealedbox.js"() {
28720
29088
  "use strict";
@@ -28724,7 +29092,7 @@ var init_sealedbox = __esm({
28724
29092
  });
28725
29093
 
28726
29094
  // ../../packages/attest/dist/aead.js
28727
- import { createCipheriv as createCipheriv4, createDecipheriv as createDecipheriv4, randomBytes as randomBytes9 } from "crypto";
29095
+ import { createCipheriv as createCipheriv4, createDecipheriv as createDecipheriv4, randomBytes as randomBytes10 } from "crypto";
28728
29096
  var init_aead = __esm({
28729
29097
  "../../packages/attest/dist/aead.js"() {
28730
29098
  "use strict";
@@ -29444,7 +29812,7 @@ var init_result = __esm({
29444
29812
  });
29445
29813
 
29446
29814
  // ../../packages/envrun/dist/attestation.js
29447
- import { createHash as createHash8, randomBytes as randomBytes10 } from "crypto";
29815
+ import { createHash as createHash8, randomBytes as randomBytes11 } from "crypto";
29448
29816
  function contradicts(outcome, counts, exitCode) {
29449
29817
  const budget = OUTCOME_TO_BUDGET[outcome];
29450
29818
  if (budget === null)
@@ -29624,7 +29992,7 @@ function toAcceptancePredicate(pair, opts = {}) {
29624
29992
  // RepoDigest (`repo@sha256:…`) carries the repo name and the content hash, and the
29625
29993
  // tag it drops is the part a registry can re-point (TERM-893).
29626
29994
  enclave_measurement: localMeasurement(patched.containerImageDigest),
29627
- nonce: opts.nonce ?? randomBytes10(16).toString("hex"),
29995
+ nonce: opts.nonce ?? randomBytes11(16).toString("hex"),
29628
29996
  run_policy: { max_attempts: opts.maxAttempts ?? 1, budget_outcome: budget }
29629
29997
  }
29630
29998
  };
@@ -30602,12 +30970,12 @@ function socketErrorReason(err) {
30602
30970
  return err.name;
30603
30971
  }
30604
30972
  function fetchAttestationOverTls(req) {
30605
- return new Promise((resolve4) => {
30973
+ return new Promise((resolve5) => {
30606
30974
  let whole;
30607
30975
  const settle = (reading) => {
30608
30976
  if (whole !== void 0)
30609
30977
  clearTimeout(whole);
30610
- resolve4(reading);
30978
+ resolve5(reading);
30611
30979
  };
30612
30980
  let cert;
30613
30981
  let key;
@@ -31808,7 +32176,7 @@ function makeLease(p) {
31808
32176
  }
31809
32177
  };
31810
32178
  }
31811
- 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, STAGE_HELPER_IMAGE, VOLUME_CREATE_TIMEOUT_MS, EXEC_PROBE_TIMEOUT_MS, POPULATE_TIMEOUT_MS, STAGE_VOLUME_LABEL_KEY, 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;
32179
+ 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;
31812
32180
  var init_hostedVenue = __esm({
31813
32181
  "../../packages/envrun/dist/hostedVenue.js"() {
31814
32182
  "use strict";
@@ -31957,18 +32325,18 @@ var init_hostedVenue = __esm({
31957
32325
  for (let i = 0; i < n; i += 1) {
31958
32326
  const server = createServer();
31959
32327
  servers.push(server);
31960
- await new Promise((resolve4, reject) => {
32328
+ await new Promise((resolve5, reject) => {
31961
32329
  server.once("error", reject);
31962
32330
  server.listen(0, "localhost", () => {
31963
- resolve4();
32331
+ resolve5();
31964
32332
  });
31965
32333
  });
31966
32334
  }
31967
32335
  return servers.map((s) => s.address().port);
31968
32336
  } finally {
31969
- await Promise.all(servers.map((s) => new Promise((resolve4) => {
32337
+ await Promise.all(servers.map((s) => new Promise((resolve5) => {
31970
32338
  s.close(() => {
31971
- resolve4();
32339
+ resolve5();
31972
32340
  });
31973
32341
  })));
31974
32342
  }
@@ -31982,11 +32350,9 @@ var init_hostedVenue = __esm({
31982
32350
  VENUE_SSH_USER = "th-runner";
31983
32351
  GCE_METADATA_IDENTITY_URL = "http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/identity";
31984
32352
  COMPACT_JWT = /^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$/;
31985
- STAGE_HELPER_IMAGE = "busybox:1.37.0";
31986
32353
  VOLUME_CREATE_TIMEOUT_MS = 3e4;
31987
32354
  EXEC_PROBE_TIMEOUT_MS = 12e4;
31988
32355
  POPULATE_TIMEOUT_MS = STAGE_PUSH_TIMEOUT_MS;
31989
- STAGE_VOLUME_LABEL_KEY = "terminalhire.stage";
31990
32356
  STAGE_PROOF_PREFIX = ".th-proven-";
31991
32357
  IAP_NOT_READY = /\b4047\s*[:\]]/;
31992
32358
  IAP_BACKEND_UNREACHABLE = /\b4003\s*[:\]]/;
@@ -32594,6 +32960,9 @@ function detectRuntime(repo) {
32594
32960
  if (candidates.length === 0 && repo.listFiles("").some((f) => f.endsWith(".java"))) {
32595
32961
  candidates.push("jvm");
32596
32962
  }
32963
+ if (!candidates.includes("cpp") && MAKEFILE_NAMES.some((file) => repo.exists(file)) && repo.listFiles("").some((f) => C_FAMILY_SOURCE.test(f))) {
32964
+ candidates.push("cpp");
32965
+ }
32597
32966
  return { runtime: candidates[0] ?? "unknown", candidates };
32598
32967
  }
32599
32968
  function readJsonObject(repo, path5) {
@@ -32746,6 +33115,44 @@ function rehearse(command) {
32746
33115
  const home = '"$R/.terminalhire-rehearsal-home"';
32747
33116
  return `{ R="$HOME"; rm -rf ${copy} ${home}; mkdir -p ${home} && cp -R . ${copy} && (cd ${copy} && HOME=${home} GRADLE_USER_HOME="$R/.gradle" JAVA_TOOL_OPTIONS="$JAVA_TOOL_OPTIONS -Duser.home=$R/.terminalhire-rehearsal-home" ${command}); rm -rf ${copy} ${home}; true; }`;
32748
33117
  }
33118
+ function cmakeCode(text) {
33119
+ return text.replace(/#\[(=*)\[[\s\S]*?\]\1\]/g, "").replace(/#.*$/gm, "");
33120
+ }
33121
+ function makeTargets(text) {
33122
+ const targets = /* @__PURE__ */ new Set();
33123
+ for (const line of text.split("\n")) {
33124
+ if (line.startsWith(" "))
33125
+ continue;
33126
+ const rule = /^([^:=#\t][^:=#]*?)[ \t]*::?(?!:?=)/.exec(line);
33127
+ if (rule)
33128
+ for (const name of rule[1].trim().split(/\s+/))
33129
+ targets.add(name);
33130
+ }
33131
+ return targets;
33132
+ }
33133
+ function cppCommands(repo) {
33134
+ const cmake = repo.readText("CMakeLists.txt");
33135
+ if (cmake !== null) {
33136
+ return CMAKE_DECLARES_TESTS.test(cmakeCode(cmake)) ? {
33137
+ install: "cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTING=ON",
33138
+ test: "cmake --build build --parallel && ctest --test-dir build --output-on-failure"
33139
+ } : { install: null, test: null };
33140
+ }
33141
+ const meson = repo.readText("meson.build");
33142
+ if (meson !== null) {
33143
+ return /\btest\s*\(/.test(meson) ? { install: "meson setup build", test: "meson test -C build" } : { install: null, test: null };
33144
+ }
33145
+ const name = repo.listNames("").find((file) => MAKEFILE_NAMES.includes(file));
33146
+ const makefile = name === void 0 ? null : repo.readText(name);
33147
+ if (makefile !== null) {
33148
+ const targets = makeTargets(makefile);
33149
+ if (targets.has("test"))
33150
+ return { install: null, test: "make test" };
33151
+ if (targets.has("check"))
33152
+ return { install: null, test: "make check" };
33153
+ }
33154
+ return { install: null, test: null };
33155
+ }
32749
33156
  function otherRuntimeCommands(repo, runtime) {
32750
33157
  switch (runtime) {
32751
33158
  case "go":
@@ -32789,6 +33196,8 @@ function otherRuntimeCommands(repo, runtime) {
32789
33196
  test: `${wrapper} test --offline --no-build-cache`
32790
33197
  };
32791
33198
  }
33199
+ case "cpp":
33200
+ return cppCommands(repo);
32792
33201
  default:
32793
33202
  return { install: null, test: null };
32794
33203
  }
@@ -32855,7 +33264,7 @@ function matchFirst(text, pattern) {
32855
33264
  const match2 = pattern.exec(text);
32856
33265
  return match2 === null ? null : match2[1];
32857
33266
  }
32858
- var RUNTIME_MANIFESTS, MANIFEST_FILENAMES, JVM_BUILD_FILES, NPM_PLACEHOLDER_TEST, REQUIREMENT_SPECIFIER, EXACT_VERSION;
33267
+ 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;
32859
33268
  var init_manifest2 = __esm({
32860
33269
  "../../packages/envspec/dist/manifest.js"() {
32861
33270
  "use strict";
@@ -32866,12 +33275,19 @@ var init_manifest2 = __esm({
32866
33275
  { runtime: "rust", files: ["Cargo.toml"] },
32867
33276
  { runtime: "jvm", files: ["pom.xml", "build.gradle", "build.gradle.kts"] },
32868
33277
  { runtime: "ruby", files: ["Gemfile"] },
32869
- { runtime: "dotnet", files: [] }
33278
+ { runtime: "dotnet", files: [] },
33279
+ // Last, so an earlier runtime keeps precedence: a Python package with a C
33280
+ // extension built by CMake is still a Python repo, with cpp recorded second.
33281
+ { runtime: "cpp", files: ["CMakeLists.txt", "meson.build"] }
32870
33282
  ];
32871
- MANIFEST_FILENAMES = RUNTIME_MANIFESTS.flatMap((m) => m.files).concat(["*.csproj", "*.fsproj", "*.sln"]).sort();
33283
+ MAKEFILE_NAMES = ["GNUmakefile", "Makefile", "makefile"];
33284
+ C_FAMILY_SOURCE = /\.(?:c|cc|cpp|cxx|h|hpp|hh)$/i;
33285
+ MANIFEST_FILENAMES = RUNTIME_MANIFESTS.flatMap((m) => m.files).concat(["*.csproj", "*.fsproj", "*.sln"], MAKEFILE_NAMES).sort();
32872
33286
  JVM_BUILD_FILES = ["build.gradle", "build.gradle.kts", "pom.xml"];
32873
33287
  NPM_PLACEHOLDER_TEST = /^echo\s+["']?Error:\s*no test specified["']?\s*&&\s*exit\s+1$/;
32874
33288
  REQUIREMENT_SPECIFIER = /^[A-Za-z0-9._-]+(\[[A-Za-z0-9._,-]+\])?([<>=!~]=?[A-Za-z0-9._*+-]+(,[<>=!~]=?[A-Za-z0-9._*+-]+)*)?$/;
33289
+ CPP_BUILD_FILES = ["CMakeLists.txt", "meson.build", ...MAKEFILE_NAMES];
33290
+ CMAKE_DECLARES_TESTS = /\benable_testing\s*\(|\binclude\s*\(\s*CTest\s*\)|\badd_test\s*\(/;
32875
33291
  EXACT_VERSION = /^v?(\d+(?:\.\d+){0,2})$/;
32876
33292
  }
32877
33293
  });
@@ -32944,11 +33360,11 @@ var init_references = __esm({
32944
33360
  });
32945
33361
 
32946
33362
  // ../../packages/envspec/dist/repo.js
32947
- import { readdirSync as readdirSync2, readFileSync as readFileSync12, statSync as statSync3 } from "fs";
32948
- import { join as join25, relative as relative2, sep as sep5 } from "path";
33363
+ import { readdirSync as readdirSync3, readFileSync as readFileSync12, statSync as statSync5 } from "fs";
33364
+ import { join as join25, relative as relative2, sep as sep6 } from "path";
32949
33365
  function createRepoReader(repoPath) {
32950
33366
  const resolveIn = (relativePath) => relativePath === "" ? repoPath : join25(repoPath, relativePath);
32951
- const toPosix = (absolute) => relative2(repoPath, absolute).split(sep5).join("/");
33367
+ const toPosix = (absolute) => relative2(repoPath, absolute).split(sep6).join("/");
32952
33368
  const readText = (relativePath) => {
32953
33369
  try {
32954
33370
  return readFileSync12(resolveIn(relativePath), "utf8");
@@ -32958,7 +33374,7 @@ function createRepoReader(repoPath) {
32958
33374
  };
32959
33375
  const statOf = (relativePath) => {
32960
33376
  try {
32961
- return statSync3(resolveIn(relativePath));
33377
+ return statSync5(resolveIn(relativePath));
32962
33378
  } catch {
32963
33379
  return null;
32964
33380
  }
@@ -32970,7 +33386,7 @@ function createRepoReader(repoPath) {
32970
33386
  return;
32971
33387
  let names;
32972
33388
  try {
32973
- names = readdirSync2(dir);
33389
+ names = readdirSync3(dir);
32974
33390
  } catch {
32975
33391
  return;
32976
33392
  }
@@ -33005,7 +33421,7 @@ function createRepoReader(repoPath) {
33005
33421
  if (st === null || !st.isDirectory())
33006
33422
  return [];
33007
33423
  try {
33008
- return readdirSync2(resolveIn(relativeDir)).slice().sort();
33424
+ return readdirSync3(resolveIn(relativeDir)).slice().sort();
33009
33425
  } catch {
33010
33426
  return [];
33011
33427
  }
@@ -33330,7 +33746,7 @@ function deriveFromReader(repo) {
33330
33746
  });
33331
33747
  }
33332
33748
  const version = deriveRuntimeVersion(repo, runtime, job);
33333
- if (version.value === null && runtime !== "unknown") {
33749
+ if (version.value === null && runtime !== "unknown" && runtime !== "cpp") {
33334
33750
  unresolved.push({
33335
33751
  kind: "runtime-version-undeclared",
33336
33752
  runtime,
@@ -33472,6 +33888,8 @@ function testSearchPaths(runtime, workflowFiles) {
33472
33888
  paths.push("Gemfile", "Rakefile");
33473
33889
  if (runtime === "jvm")
33474
33890
  paths.push(...JVM_BUILD_FILES);
33891
+ if (runtime === "cpp")
33892
+ paths.push(...CPP_BUILD_FILES);
33475
33893
  if (runtime === "unknown")
33476
33894
  paths.push(...MANIFEST_FILENAMES);
33477
33895
  return [...new Set(paths)].sort();
@@ -33492,8 +33910,24 @@ function describeTestSearch(repo, runtime, workflowFiles) {
33492
33910
  if (runtime === "jvm" && !JVM_BUILD_FILES.some((file) => repo.exists(file))) {
33493
33911
  return `${ciPart}, and the repo has Java sources but no build file (${JVM_BUILD_FILES.join(", ")}), so there is no build to run tests with. Compiling the sources with \`javac\` alone would run no tests`;
33494
33912
  }
33913
+ if (runtime === "cpp")
33914
+ return `${ciPart}, and ${describeCppSearch(repo)}`;
33495
33915
  return `${ciPart}, and no ${runtime} test convention applies to this tree`;
33496
33916
  }
33917
+ function describeCppSearch(repo) {
33918
+ const cmake = repo.readText("CMakeLists.txt");
33919
+ if (cmake !== null && !CMAKE_DECLARES_TESTS.test(cmakeCode(cmake))) {
33920
+ return "`CMakeLists.txt` registers no tests: no `enable_testing()`, `include(CTest)` or `add_test()`";
33921
+ }
33922
+ if (cmake === null && repo.exists("meson.build")) {
33923
+ return "`meson.build` declares no `test()`";
33924
+ }
33925
+ const makefile = repo.listNames("").find((name) => MAKEFILE_NAMES.includes(name));
33926
+ if (cmake === null && makefile !== void 0) {
33927
+ return `\`${makefile}\` has no \`test\` or \`check\` target`;
33928
+ }
33929
+ return `no C/C++ test convention applies to this tree (looked in ${CPP_BUILD_FILES.join(", ")})`;
33930
+ }
33497
33931
  function installSearchPaths(runtime, workflowFiles) {
33498
33932
  const paths = [...workflowFiles];
33499
33933
  if (runtime === "node")
@@ -33502,6 +33936,8 @@ function installSearchPaths(runtime, workflowFiles) {
33502
33936
  paths.push("poetry.lock", "pyproject.toml", "requirements.txt", "uv.lock");
33503
33937
  if (runtime === "jvm")
33504
33938
  paths.push(...JVM_BUILD_FILES);
33939
+ if (runtime === "cpp")
33940
+ paths.push(...CPP_BUILD_FILES);
33505
33941
  if (runtime === "unknown")
33506
33942
  paths.push(...MANIFEST_FILENAMES);
33507
33943
  return [...new Set(paths)].sort();
@@ -33583,15 +34019,20 @@ var init_dist3 = __esm({
33583
34019
  // ../../packages/envrun/dist/gradleTestSummary.js
33584
34020
  import { mkdirSync as mkdirSync5, writeFileSync as writeFileSync14 } from "fs";
33585
34021
  import { join as join26 } from "path";
33586
- function stageGradleTestSummary(jailHome) {
34022
+ function stageGradleTestSummary(jailHome, guestUser, io = {}) {
33587
34023
  const dir = join26(jailHome, ".gradle", "init.d");
33588
34024
  mkdirSync5(dir, { recursive: true });
33589
34025
  writeFileSync14(join26(dir, "terminalhire-test-summary.gradle"), GRADLE_TEST_SUMMARY_INIT);
34026
+ const ids2 = io.ids === void 0 ? hostIds() : io.ids;
34027
+ if (ids2?.uid === 0 && guestUser && guestUser.uid !== 0) {
34028
+ (io.chown ?? chownTree)(join26(jailHome, ".gradle"), guestUser);
34029
+ }
33590
34030
  }
33591
34031
  var GRADLE_TEST_MARKER, GRADLE_TEST_SUMMARY_INIT;
33592
34032
  var init_gradleTestSummary = __esm({
33593
34033
  "../../packages/envrun/dist/gradleTestSummary.js"() {
33594
34034
  "use strict";
34035
+ init_dist();
33595
34036
  GRADLE_TEST_MARKER = "terminalhire-gradle-tests:";
33596
34037
  GRADLE_TEST_SUMMARY_INIT = `// Written by terminalhire (TERM-1122): prints each Test task's counts, because
33597
34038
  // Gradle prints none when every test passes.
@@ -34179,7 +34620,7 @@ async function verifyWorkingDiff(req) {
34179
34620
  }
34180
34621
  async function runVerification(req, ctx) {
34181
34622
  const { runId, startedAt } = ctx;
34182
- const labels = { ...req.labels ?? {}, [RUN_LABEL_KEY]: "term-350" };
34623
+ const labels = runLabels(runId, req.labels);
34183
34624
  const progress = req.onProgress ?? (() => {
34184
34625
  });
34185
34626
  const badTarget = unacceptableTarget(req);
@@ -34334,9 +34775,9 @@ async function runVerification(req, ctx) {
34334
34775
  }
34335
34776
  const lease = resolved.lease;
34336
34777
  try {
34337
- buildJail(scratch);
34778
+ buildJail(scratch, lease.guestUser);
34338
34779
  if (spec.runtime === "jvm")
34339
- stageGradleTestSummary(localJailPaths(scratch).jail);
34780
+ stageGradleTestSummary(localJailPaths(scratch).jail, lease.guestUser);
34340
34781
  const venuePaths = await lease.stage({
34341
34782
  cloneDir,
34342
34783
  scratchRoot: scratch,
@@ -34600,14 +35041,14 @@ function migrationUnits(runner, migrations) {
34600
35041
  return [...byDir.entries()].sort((a, b) => a[0].localeCompare(b[0])).map(([id, path5]) => ({ id, path: path5 }));
34601
35042
  }
34602
35043
  case "alembic":
34603
- return migrations.filter((p) => /^alembic\/versions\/[^/]+\.py$/.test(p) && !p.endsWith("/__init__.py")).sort().map((path5) => ({ id: basename4(path5).replace(/\.py$/, ""), path: path5 }));
35044
+ return migrations.filter((p) => /^alembic\/versions\/[^/]+\.py$/.test(p) && !p.endsWith("/__init__.py")).sort().map((path5) => ({ id: basename5(path5).replace(/\.py$/, ""), path: path5 }));
34604
35045
  case "rails":
34605
- return migrations.filter((p) => /^db\/migrate\/[^/]+\.rb$/.test(p)).sort().map((path5) => ({ id: /^(\d+)/.exec(basename4(path5))?.[1] ?? basename4(path5), path: path5 }));
35046
+ return migrations.filter((p) => /^db\/migrate\/[^/]+\.rb$/.test(p)).sort().map((path5) => ({ id: /^(\d+)/.exec(basename5(path5))?.[1] ?? basename5(path5), path: path5 }));
34606
35047
  case "sql":
34607
35048
  return migrations.filter((p) => p.endsWith(".sql") && !p.startsWith("prisma/migrations/")).sort().map((path5) => ({ id: path5, path: path5 }));
34608
35049
  }
34609
35050
  }
34610
- function basename4(path5) {
35051
+ function basename5(path5) {
34611
35052
  const at = path5.lastIndexOf("/");
34612
35053
  return at === -1 ? path5 : path5.slice(at + 1);
34613
35054
  }
@@ -34705,7 +35146,7 @@ var init_dbplan = __esm({
34705
35146
 
34706
35147
  // ../../packages/envrun/dist/dbstack.js
34707
35148
  import { spawnSync as spawnSync7 } from "child_process";
34708
- import { randomBytes as randomBytes11 } from "crypto";
35149
+ import { randomBytes as randomBytes12 } from "crypto";
34709
35150
  import { mkdirSync as mkdirSync7 } from "fs";
34710
35151
  function installCommandFor(runner) {
34711
35152
  switch (runner) {
@@ -34741,7 +35182,7 @@ function docker2(args, timeoutMs = DOCKER_TIMEOUT_MS2) {
34741
35182
  function generateCredentials(host) {
34742
35183
  return {
34743
35184
  user: "thverify",
34744
- password: randomBytes11(24).toString("base64url"),
35185
+ password: randomBytes12(24).toString("base64url"),
34745
35186
  database: "thverify",
34746
35187
  host,
34747
35188
  port: 5432
@@ -35361,6 +35802,7 @@ __export(dist_exports, {
35361
35802
  REDACTED_TARGET_SHA: () => REDACTED_TARGET_SHA,
35362
35803
  RELEASED_LEASE_CENSUS_REASON: () => RELEASED_LEASE_CENSUS_REASON,
35363
35804
  REPO_DIGEST_RE: () => REPO_DIGEST_RE,
35805
+ RUN_ID_LABEL_KEY: () => RUN_ID_LABEL_KEY,
35364
35806
  RUN_IMAGE_SOURCES: () => RUN_IMAGE_SOURCES,
35365
35807
  RUN_LABEL_KEY: () => RUN_LABEL_KEY,
35366
35808
  RUN_RESULT_FIELDS: () => RUN_RESULT_FIELDS,
@@ -35433,6 +35875,7 @@ __export(dist_exports, {
35433
35875
  isBookkeepingTable: () => isBookkeepingTable,
35434
35876
  isCommandUnavailable: () => isCommandUnavailable,
35435
35877
  isGreen: () => isGreen,
35878
+ isMissingSystemDependency: () => isMissingSystemDependency,
35436
35879
  isOurFault: () => isOurFault,
35437
35880
  judgeCompleteness: () => judgeCompleteness,
35438
35881
  judgeLeaks: () => judgeLeaks,
@@ -35464,6 +35907,7 @@ __export(dist_exports, {
35464
35907
  resolveImageForSpec: () => resolveImageForSpec,
35465
35908
  resolveLease: () => resolveLease,
35466
35909
  runEnvironmentSpec: () => runEnvironmentSpec,
35910
+ runLabels: () => runLabels,
35467
35911
  setManifestProbe: () => setManifestProbe,
35468
35912
  sha256Hex: () => sha256Hex,
35469
35913
  signRunStatement: () => signRunStatement,
@@ -35782,10 +36226,10 @@ import {
35782
36226
  mkdtempSync as mkdtempSync5,
35783
36227
  renameSync as renameSync6,
35784
36228
  existsSync as existsSync13,
35785
- lstatSync as lstatSync3,
36229
+ lstatSync as lstatSync4,
35786
36230
  realpathSync as realpathSync2,
35787
36231
  rmSync as rmSync10,
35788
- readdirSync as readdirSync3
36232
+ readdirSync as readdirSync4
35789
36233
  } from "fs";
35790
36234
  import { join as join29, dirname as dirname8, isAbsolute as isAbsolute4, resolve as pathResolve } from "path";
35791
36235
  import { createHash as createHash10 } from "crypto";
@@ -36368,7 +36812,7 @@ async function sh(cmd, args, opts = {}) {
36368
36812
  async function confirm(question) {
36369
36813
  const rl = createInterface2({ input: process.stdin, output: process.stdout });
36370
36814
  try {
36371
- const ans = await new Promise((resolve4) => rl.question(question, resolve4));
36815
+ const ans = await new Promise((resolve5) => rl.question(question, resolve5));
36372
36816
  return /^y(es)?$/i.test(String(ans).trim());
36373
36817
  } finally {
36374
36818
  rl.close();
@@ -36399,7 +36843,7 @@ async function confirmSubmit(flags, question, { unattendedAllowed = true, refusa
36399
36843
  async function ask(question) {
36400
36844
  const rl = createInterface2({ input: process.stdin, output: process.stdout });
36401
36845
  try {
36402
- const ans = await new Promise((resolve4) => rl.question(question, resolve4));
36846
+ const ans = await new Promise((resolve5) => rl.question(question, resolve5));
36403
36847
  return String(ans).trim();
36404
36848
  } finally {
36405
36849
  rl.close();
@@ -36513,7 +36957,7 @@ function inferSubmitClaim({
36513
36957
  }
36514
36958
  return { candidates: [] };
36515
36959
  }
36516
- function canonicalPath(p) {
36960
+ function canonicalPath2(p) {
36517
36961
  if (typeof p !== "string" || p === "") return p;
36518
36962
  const resolved = pathResolve(p);
36519
36963
  try {
@@ -38981,7 +39425,7 @@ function assertNoBooleanPath(dest, flagName) {
38981
39425
  }
38982
39426
  function resolveDeliveryDir(flags, claimLocalId, { existsFn, readdirFn } = {}) {
38983
39427
  const exists = existsFn ?? existsSync13;
38984
- const readdir3 = readdirFn ?? readdirSync3;
39428
+ const readdir3 = readdirFn ?? readdirSync4;
38985
39429
  let probing = null;
38986
39430
  try {
38987
39431
  const base = flags?.dir ? assertNoBooleanPath(pathResolve(String(flags.dir)), "dir") : sliceWorkDirFor(claimLocalId);
@@ -39129,7 +39573,7 @@ function writeDeliveredBrief(destDir, spec) {
39129
39573
  function ensureExcludedPackDir(destDir) {
39130
39574
  let occupant = null;
39131
39575
  try {
39132
- occupant = lstatSync3(join29(destDir, BRIEF_DIR));
39576
+ occupant = lstatSync4(join29(destDir, BRIEF_DIR));
39133
39577
  } catch (err) {
39134
39578
  if (err?.code !== "ENOENT") {
39135
39579
  return {
@@ -40085,7 +40529,7 @@ async function cmdSubmit(id, flags = {}) {
40085
40529
  cwdToplevel: cwdToplevel2,
40086
40530
  cwd: pathResolve(dir),
40087
40531
  branch,
40088
- canonicalize: canonicalPath
40532
+ canonicalize: canonicalPath2
40089
40533
  });
40090
40534
  if (inferred.claim) {
40091
40535
  id = inferred.claim.id;