@outerlayer/cli 0.27.1 → 0.28.2

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.
Files changed (129) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/README.md +6 -0
  3. package/dist/{agent-setup-UW36TAQM.js → agent-setup-YVR5RO56.js} +17 -14
  4. package/dist/{bridge-B5VJVECY.js → bridge-WW2V7DTB.js} +3 -2
  5. package/dist/build-5FRC3RKM.js +13 -0
  6. package/dist/build-info.json +1 -1
  7. package/dist/{builder-6Y6ZWGNH.js → builder-4F2XNSFC.js} +7 -6
  8. package/dist/{check-YTXFVWK7.js → check-YEI6TCUH.js} +70 -61
  9. package/dist/{chunk-LPFOWUHG.js → chunk-25X2PJDI.js} +20 -13
  10. package/dist/{chunk-BPKO76QR.js → chunk-4XN5FRRJ.js} +4 -4
  11. package/dist/{chunk-EPPIISRD.js → chunk-55XLX5PA.js} +1 -1
  12. package/dist/{chunk-LOKSLO25.js → chunk-5B7BEQPR.js} +1 -1
  13. package/dist/chunk-5BHIBJDA.js +93 -0
  14. package/dist/{chunk-RHAGBUL5.js → chunk-5K3KB4YR.js} +1 -1
  15. package/dist/{chunk-ABXQBEYK.js → chunk-66T6XJAB.js} +1 -1
  16. package/dist/{chunk-ZFBJUOYT.js → chunk-6S4V4UIC.js} +10 -10
  17. package/dist/{chunk-BWH36FOC.js → chunk-7CK2TJFB.js} +20 -4
  18. package/dist/{chunk-QKIQ2R7F.js → chunk-ATJOFXTE.js} +3 -3
  19. package/dist/chunk-CDDED64E.js +13 -0
  20. package/dist/{chunk-QHH4OSD4.js → chunk-CTYSTE5C.js} +1 -1
  21. package/dist/{chunk-DJHNEGNM.js → chunk-DKWQMPSL.js} +18 -2
  22. package/dist/{chunk-IMOB4IOI.js → chunk-DQYMQ47E.js} +3 -2
  23. package/dist/{chunk-YETTWTAY.js → chunk-E7R2GIAW.js} +4 -4
  24. package/dist/{chunk-GFUE5TKO.js → chunk-F4FA3ZUT.js} +24 -14
  25. package/dist/{chunk-6TRNCUM4.js → chunk-FGF52V4V.js} +1 -1
  26. package/dist/{chunk-R7SGBXUD.js → chunk-GLL4TN2M.js} +226 -72
  27. package/dist/{chunk-MATUHHN4.js → chunk-GUXWQIEY.js} +1 -1
  28. package/dist/{chunk-B7DJVG25.js → chunk-HOB7Z4JQ.js} +3 -3
  29. package/dist/{chunk-2I3LIRZP.js → chunk-HXAEQPZR.js} +101 -10
  30. package/dist/{chunk-P33PZIWS.js → chunk-ITAIEJ2T.js} +7 -142
  31. package/dist/{chunk-RHGF73IU.js → chunk-IVX3U7KV.js} +1 -1
  32. package/dist/{chunk-7OUOLGUV.js → chunk-JGML2VUN.js} +3 -7
  33. package/dist/{chunk-6VFH52MP.js → chunk-KU7QL4XW.js} +24 -22
  34. package/dist/{chunk-2F762IXY.js → chunk-LNIFYSNK.js} +3 -3
  35. package/dist/{chunk-XBPUTQ7E.js → chunk-MHAR6S7B.js} +1 -1
  36. package/dist/{chunk-VS27BP3E.js → chunk-MKIC65SD.js} +52 -9
  37. package/dist/{chunk-64GMBWAK.js → chunk-MNHMUZGC.js} +6 -6
  38. package/dist/{chunk-7AQ5AM7U.js → chunk-MQHTM4YJ.js} +177 -21
  39. package/dist/{chunk-6KKM7VGZ.js → chunk-N7WAEEIU.js} +3 -3
  40. package/dist/{chunk-D7ECMB7B.js → chunk-NUHZV3A4.js} +1 -1
  41. package/dist/{chunk-5VMDGJGH.js → chunk-O65LES4V.js} +39 -25
  42. package/dist/{chunk-MHX7YX7Y.js → chunk-P2RWZCV7.js} +1 -1
  43. package/dist/chunk-QBQHQEUG.js +300 -0
  44. package/dist/{chunk-FP23SVDD.js → chunk-QJFDAKRF.js} +5 -5
  45. package/dist/{chunk-WUL2OP5A.js → chunk-QKXF7CLR.js} +3 -3
  46. package/dist/{chunk-XZ6Q6ZNR.js → chunk-SATUCMAJ.js} +3 -2
  47. package/dist/{chunk-Y7ZCHBYB.js → chunk-SFJXGISY.js} +3 -3
  48. package/dist/{chunk-2BTFKNTR.js → chunk-SJZL5F3F.js} +2 -2
  49. package/dist/{chunk-HWDYLXHH.js → chunk-T2QSWFSU.js} +4 -4
  50. package/dist/{chunk-E5IJ5533.js → chunk-TF7G2PR6.js} +3 -3
  51. package/dist/{chunk-O2IJTY7L.js → chunk-TVZAC2NP.js} +3 -3
  52. package/dist/{chunk-QE3UQAHK.js → chunk-UHU7H57K.js} +26 -7
  53. package/dist/{chunk-WSYXLLA5.js → chunk-USJZ2LDK.js} +6 -6
  54. package/dist/{chunk-EX5MQYEF.js → chunk-VC5J7QPV.js} +5 -5
  55. package/dist/{chunk-N4D5TMVU.js → chunk-VU7ZJPIR.js} +5 -5
  56. package/dist/{chunk-GLUKDQS4.js → chunk-WWQVKFN6.js} +1 -1
  57. package/dist/{chunk-55GNQPO6.js → chunk-X52PMPOM.js} +7 -7
  58. package/dist/{chunk-YB3CSYXX.js → chunk-XD4ZCMA4.js} +81 -2
  59. package/dist/{chunk-Y4OKEFAX.js → chunk-YL2VDSNF.js} +1 -1
  60. package/dist/{chunk-M3MHHIUW.js → chunk-Z3ASAXVU.js} +1 -1
  61. package/dist/{chunk-XS3FDWSZ.js → chunk-ZTY7HHYB.js} +1 -1
  62. package/dist/{claude-launcher-NQSZBTQ5.js → claude-launcher-7RGVOYCD.js} +6 -5
  63. package/dist/{cli-GHEOBXC7.js → cli-SCTKM2Y5.js} +151 -130
  64. package/dist/cli-update-notice-4DLZ3655.js +9 -0
  65. package/dist/{command-V7B7GTPZ.js → command-PY3C2E4B.js} +53 -37
  66. package/dist/{config-TWS5VZEN.js → config-5QYIKYUP.js} +3 -2
  67. package/dist/{connect-cmd-HGS7YPA2.js → connect-cmd-5TYAOK5M.js} +14 -11
  68. package/dist/{container-start-FHB2VDC4.js → container-start-7WLZMPEI.js} +37 -36
  69. package/dist/{control-plane-launch-DBQA3KLJ.js → control-plane-launch-FZMD4MXX.js} +13 -10
  70. package/dist/{dist-QPHZE47F.js → dist-JFVKLUHL.js} +1 -1
  71. package/dist/{docker-YT6Z3LXI.js → docker-ZTC4EIU4.js} +7 -6
  72. package/dist/{doctor-Y4NCNSJM.js → doctor-DQWRUAEB.js} +159 -41
  73. package/dist/{emit-artifact-cmd-ALDK55Y2.js → emit-artifact-cmd-SOXLBPQB.js} +19 -16
  74. package/dist/{emit-cmd-VAQHRWJR.js → emit-cmd-QUABLVO6.js} +6 -5
  75. package/dist/{emit-criteria-cmd-Q7SM6VP2.js → emit-criteria-cmd-JDO2VUG5.js} +15 -12
  76. package/dist/{emit-finding-cmd-2NBAPDSV.js → emit-finding-cmd-VCQPCLI2.js} +16 -13
  77. package/dist/{emit-result-cmd-KUSL56RI.js → emit-result-cmd-TKA3GAN7.js} +19 -16
  78. package/dist/{emit-step-cmd-KD4AKEAT.js → emit-step-cmd-XN44BXYU.js} +16 -13
  79. package/dist/{exec-client-6WVS7DRZ.js → exec-client-VKT2XNB3.js} +4 -3
  80. package/dist/fleet-readiness-G5X3MKP2.js +29 -0
  81. package/dist/{governing-source-VVUA5TKB.js → governing-source-B3M2U4BC.js} +21 -18
  82. package/dist/{guest-init-H5K2RW3P.js → guest-init-QW2DPX7H.js} +5 -4
  83. package/dist/{hook-fast-3SUJM765.js → hook-fast-EFC44SXV.js} +4 -1
  84. package/dist/{host-key-XNKZCWTS.js → host-key-AC6LTZDS.js} +4 -3
  85. package/dist/{image-tag-R34J4O3F.js → image-tag-SS7YKJHN.js} +4 -3
  86. package/dist/index.js +4 -4
  87. package/dist/init-5LHTF72R.js +56 -0
  88. package/dist/{init-3DAGX7UY.js → init-BTH5YPBC.js} +38 -37
  89. package/dist/{init-cmd-RIDBVSE4.js → init-cmd-L4VW3ZCT.js} +25 -21
  90. package/dist/{init-update-ECWNG47R.js → init-update-RWTQSTPE.js} +51 -35
  91. package/dist/{inspect-IPFA7AAT.js → inspect-L7JIBK3Y.js} +1 -1
  92. package/dist/{install-6OIY3KHZ.js → install-35SAMY75.js} +5 -5
  93. package/dist/{install-cmd-JMT67P35.js → install-cmd-GFY4QF72.js} +6 -5
  94. package/dist/{layout-XNFDTOET.js → layout-BBTQPS6V.js} +4 -3
  95. package/dist/{login-browser-EWXSGHM7.js → login-browser-WSOSQH2T.js} +2 -2
  96. package/dist/{logs-XHAS6LMZ.js → logs-X2IRDP5T.js} +3 -2
  97. package/dist/loop-QDV7T65M.js +80 -0
  98. package/dist/{machine-OOQ2NXC3.js → machine-PLNSGKNF.js} +32 -30
  99. package/dist/{machine-skills-FLIRJYP7.js → machine-skills-O7W6NMWB.js} +4 -3
  100. package/dist/{mcp-serve-cmd-LY5A3CE7.js → mcp-serve-cmd-IITCPAAR.js} +9 -6
  101. package/dist/{paths-LHSPNORF.js → paths-32DWGK35.js} +3 -2
  102. package/dist/pending-CSIY4Y2I.js +10 -0
  103. package/dist/{pidfile-DKMYYXLH.js → pidfile-CYCTH6NQ.js} +4 -3
  104. package/dist/{real-deps-KNNBQC75.js → real-deps-QX5YH7FG.js} +1 -1
  105. package/dist/{record-3LPTY3G5.js → record-N4WPZCGK.js} +4 -3
  106. package/dist/{relay-GFPPD6GF.js → relay-FJ5K37Z5.js} +8 -4
  107. package/dist/{runner-env-YRRNRKF4.js → runner-env-EIB6WFPG.js} +6 -7
  108. package/dist/{runner-identity-YS2BZXZV.js → runner-identity-S3V6I4I3.js} +7 -6
  109. package/dist/{runner-key-EFNTZWNX.js → runner-key-M4N2BY5A.js} +36 -35
  110. package/dist/{session-sender-SC5PSU2G.js → session-sender-UQ3VF732.js} +20 -17
  111. package/dist/{spec-cmd-JGYHHJ4P.js → spec-cmd-Z6MGATWC.js} +16 -13
  112. package/dist/{starter-pack-GPU4X6B7.js → starter-pack-5SOT5V4I.js} +4 -3
  113. package/dist/{status-7WM7S7JX.js → status-PWMPP4ES.js} +39 -37
  114. package/dist/{sync-cmd-ATXJVS7S.js → sync-cmd-N4R7ST6X.js} +19 -16
  115. package/dist/{watch-M7NR3DOW.js → watch-L5MSAWEO.js} +19 -16
  116. package/dist/{work-claim-cmd-DNUDZKJA.js → work-claim-cmd-CXALMILD.js} +21 -18
  117. package/dist/{work-cmd-VECTYST6.js → work-cmd-COESFSHE.js} +16 -13
  118. package/dist/{work-comment-cmd-F3NRWKPZ.js → work-comment-cmd-A4ZUVCSA.js} +16 -13
  119. package/dist/{work-logs-cmd-QF6P32FE.js → work-logs-cmd-CTF43MBE.js} +16 -13
  120. package/dist/{work-open-pr-cmd-XE23JA3R.js → work-open-pr-cmd-EMGYJXVD.js} +17 -14
  121. package/dist/{work-pr-cmd-BH56GUKN.js → work-pr-cmd-FEXMS6RH.js} +16 -13
  122. package/dist/{work-stop-cmd-C3F5HNUO.js → work-stop-cmd-NIHAN45R.js} +16 -13
  123. package/package.json +1 -1
  124. package/dist/build-HRLACBOZ.js +0 -12
  125. package/dist/chunk-STSLH5EJ.js +0 -71
  126. package/dist/init-SWKYV6CL.js +0 -55
  127. package/dist/loop-AIJZVUNA.js +0 -79
  128. package/dist/pending-3YZCSKON.js +0 -9
  129. package/dist/{chunk-6BLXUD56.js → chunk-HOU33DW5.js} +1 -1
@@ -1,28 +1,28 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- import { keepStagedContext, describeCallFailure, controlPlaneTokenFailure, refusalCode, resolveGoverningSource, removeStagedContext, stageGovernedContext, pinnedSourceOf, commandWithShim, shimScript } from './chunk-WSYXLLA5.js';
4
- import { runnerImageVersion } from './chunk-M3MHHIUW.js';
5
- import { realFdStorePort } from './chunk-YB3CSYXX.js';
6
- import { readDefaultBranchHead, cloneRepository, buildRepositoryImage } from './chunk-HWDYLXHH.js';
3
+ import { execCapture } from './chunk-MHAR6S7B.js';
4
+ import { GUEST_CID } from './chunk-WWQVKFN6.js';
5
+ import { keepStagedContext, describeCallFailure, controlPlaneTokenFailure, refusalCode, resolveGoverningSource, removeStagedContext, stageGovernedContext, pinnedSourceOf, commandWithShim, shimScript } from './chunk-USJZ2LDK.js';
6
+ import { runnerImageVersion } from './chunk-Z3ASAXVU.js';
7
+ import { hostAllowed } from './chunk-UXYLWVTQ.js';
8
+ import { readDefaultBranchHead, cloneRepository, buildRepositoryImage } from './chunk-T2QSWFSU.js';
7
9
  import { startTimeOf, isSameProcess } from './chunk-JBJTC3UU.js';
8
10
  import { currentCliBuild } from './chunk-OGFGZQA3.js';
9
- import { createDockerHost, builderSpec, runnerLabelValue, RUNNER_LABEL, BUILD_LABEL, prepareRunArgs, containerRunArgs, HASH_ARGV, parseProgramHash, JUDGE_PROGRAM, containerExecArgs, childEnvironment, JUDGE_DIR, JUDGE_REPORT, MAX_REPORT_BYTES, spawnRun } from './chunk-EX5MQYEF.js';
10
- import { FIRECRACKER_VERSION, hostHelperPinned, firecrackerArch, MICROVM_ARCHES, RecipeError, DOCKER_FEATURE, CLAUDE_CODE_VERSION, GUEST_HELPER_SHA256, GUEST_ASSETS_BASE_URL, GUEST_KERNEL_SHA256, JAILER_BINARY_SHA256, FIRECRACKER_BINARY_SHA256, FIRECRACKER_SHA256, GUEST_ASSETS_VERSION, DEFAULT_BASE_IMAGE } from './chunk-2BTFKNTR.js';
11
- import { execCapture } from './chunk-XBPUTQ7E.js';
12
- import { GUEST_CID } from './chunk-GLUKDQS4.js';
13
- import { WorkCommandError } from './chunk-55GNQPO6.js';
14
- import { hostAllowed } from './chunk-UXYLWVTQ.js';
11
+ import { createDockerHost, builderSpec, runnerLabelValue, RUNNER_LABEL, BUILD_LABEL, prepareRunArgs, containerRunArgs, HASH_ARGV, parseProgramHash, JUDGE_PROGRAM, containerExecArgs, childEnvironment, JUDGE_DIR, JUDGE_REPORT, MAX_REPORT_BYTES, spawnRun } from './chunk-VC5J7QPV.js';
15
12
  import { readDu } from './chunk-YJLHUKYS.js';
16
- import { sharedSkillNames, skillConflictMessage } from './chunk-6TRNCUM4.js';
13
+ import { FIRECRACKER_VERSION, hostHelperPinned, firecrackerArch, MICROVM_ARCHES, RecipeError, DOCKER_FEATURE, CLAUDE_CODE_VERSION, GUEST_HELPER_SHA256, GUEST_ASSETS_BASE_URL, GUEST_KERNEL_SHA256, JAILER_BINARY_SHA256, FIRECRACKER_BINARY_SHA256, FIRECRACKER_SHA256, GUEST_ASSETS_VERSION, DEFAULT_BASE_IMAGE } from './chunk-SJZL5F3F.js';
14
+ import { WorkCommandError } from './chunk-X52PMPOM.js';
15
+ import { setCustomScrubbers, setLocalIdentity, setRepoRoot, parseTranscript, scrubDeep, scrubText } from './chunk-UHU7H57K.js';
16
+ import { vmAssetsDir, RUNNER_DIR_MODE, rootfsPathFor, diskTemplatesDir, RUNNER_FILE_MODE, deliveryDir, ghConfigDir, runnerDirOfBuild, spoolDir, mirrorDir, buildDir, SHIM_DIR } from './chunk-5K3KB4YR.js';
17
+ import { repositoriesOf, primaryOf, RELAY_PORT, repositoryPath, CHECKOUT_DIR, lifecycleWorkdir, claudeCredential, buildEnvironment, startBuildServices, checkEnvironment, AGENT_CREDENTIAL_MISSING, checkoutPath, JOB_MOUNT } from './chunk-HXAEQPZR.js';
18
+ import { sharedSkillNames, skillConflictMessage } from './chunk-FGF52V4V.js';
17
19
  import { versionAtLeast, MINIMUM_CLAUDE_VERSION } from './chunk-HCVLP7XA.js';
18
- import { setCustomScrubbers, setLocalIdentity, setRepoRoot, parseTranscript, scrubDeep, scrubText } from './chunk-QE3UQAHK.js';
19
20
  import { orderSessions, extractFacts, sessionsToTimeline } from './chunk-RKOTJQVG.js';
20
21
  import { downconvertSession } from './chunk-QZBXLLTV.js';
21
- import { vmAssetsDir, RUNNER_DIR_MODE, rootfsPathFor, diskTemplatesDir, RUNNER_FILE_MODE, deliveryDir, ghConfigDir, runnerDirOfBuild, spoolDir, mirrorDir, buildDir, SHIM_DIR } from './chunk-RHAGBUL5.js';
22
- import { repositoriesOf, primaryOf, RELAY_PORT, repositoryPath, CHECKOUT_DIR, lifecycleWorkdir, claudeCredential, buildEnvironment, startBuildServices, checkEnvironment, AGENT_CREDENTIAL_MISSING, checkoutPath, JOB_MOUNT } from './chunk-2I3LIRZP.js';
22
+ import { realFdStorePort } from './chunk-XD4ZCMA4.js';
23
23
  import { RELAY_USER, RELAY_ID, UNKNOWN_TRANSPORT_REASON, DOCKER_DESKTOP_REASON, MAX_MALFORMED_SESSIONS, MALFORMED_REASON, MAX_OPEN_STREAMS, STREAM_WINDOW, SESSION_WINDOW, targetOfPath } from './chunk-JGD5TWQ2.js';
24
- import { readFileSync, mkdirSync, existsSync, mkdtempSync, rmSync, chmodSync, truncateSync, writeFileSync, readdirSync, lstatSync, copyFileSync, accessSync, constants, statSync, rmdirSync, renameSync, unlinkSync, openSync, closeSync, createWriteStream, createReadStream } from 'fs';
25
- import { join, dirname, basename } from 'path';
24
+ import { readFileSync, mkdirSync, existsSync, mkdtempSync, rmSync, chmodSync, truncateSync, writeFileSync, readdirSync, lstatSync, copyFileSync, accessSync, constants, statSync, rmdirSync, renameSync, unlinkSync, openSync, closeSync, createWriteStream, createReadStream, writeSync } from 'fs';
25
+ import { join, dirname, resolve, basename } from 'path';
26
26
  import { isIP, createConnection, connect, createServer as createServer$1 } from 'net';
27
27
  import { spawnSync, spawn } from 'child_process';
28
28
  import { availableParallelism, networkInterfaces } from 'os';
@@ -51,7 +51,8 @@ function prepareRunnerCgroup(setup) {
51
51
  if (root === void 0) return void 0;
52
52
  const { fs } = setup;
53
53
  if (!fs.exists(root) || !fs.writable(join(root, "cgroup.procs"))) return void 0;
54
- if (!(fs.read(join(root, "cgroup.controllers")) ?? "").split(/\s+/).includes("memory")) return void 0;
54
+ const controllers = (fs.read(join(root, "cgroup.controllers")) ?? "").split(/\s+/);
55
+ if (!controllers.includes("memory")) return void 0;
55
56
  for (const name of fs.list(root)) {
56
57
  if (!name.startsWith(BUILD_PREFIX)) continue;
57
58
  try {
@@ -61,7 +62,7 @@ function prepareRunnerCgroup(setup) {
61
62
  }
62
63
  if (!fs.exists(join(root, LEAF))) fs.mkdir(join(root, LEAF));
63
64
  fs.write(join(root, LEAF, "cgroup.procs"), String(setup.pid));
64
- fs.write(join(root, "cgroup.subtree_control"), "+memory");
65
+ fs.write(join(root, "cgroup.subtree_control"), controllers.includes("io") ? "+memory +io" : "+memory");
65
66
  return { root };
66
67
  }
67
68
  var BUILD_RECLAIM_HEADROOM_BYTES = 256 * 1024 ** 2;
@@ -161,7 +162,7 @@ var START_TIMEOUT_MS = 12e4;
161
162
  var DEFAULT_REQUEST_TIMEOUT_MS = 3e4;
162
163
  var MAX_REPLY_BYTES = 1024 * 1024;
163
164
  function ask(socketPath, request, timeoutMs) {
164
- return new Promise((resolve, reject) => {
165
+ return new Promise((resolve2, reject) => {
165
166
  const conn = createConnection(socketPath);
166
167
  let text = "";
167
168
  let settled = false;
@@ -187,7 +188,7 @@ function ask(socketPath, request, timeoutMs) {
187
188
  return reject(new Error(`the helper at ${socketPath} sent a reply that is not JSON`));
188
189
  }
189
190
  if (reply.status === "refused") return reject(new HelperRefusal(String(reply.code ?? "refused"), String(reply.reason ?? "the helper refused the request")));
190
- resolve(reply);
191
+ resolve2(reply);
191
192
  });
192
193
  });
193
194
  conn.on("error", (err) => settle(() => reject(new Error(`the helper at ${socketPath} did not answer: ${err.message}`))));
@@ -374,6 +375,10 @@ function formatCpuList(cores) {
374
375
  }
375
376
  return parts.join(",");
376
377
  }
378
+ function capCpus(wanted, cpuset) {
379
+ const cores = cpuset === void 0 ? void 0 : parseCpuList(cpuset);
380
+ return cores === void 0 ? wanted : Math.min(wanted, cores.length);
381
+ }
377
382
  function usableCpus(deps = {}) {
378
383
  try {
379
384
  const status = (deps.readStatus ?? (() => readFileSync("/proc/self/status", "utf8")))();
@@ -643,9 +648,9 @@ function createContainerHost(deps = {}) {
643
648
  stderr = (stderr + chunk.toString()).slice(-400);
644
649
  });
645
650
  child.stderr?.on("error", () => void 0);
646
- const exited = new Promise((resolve) => {
647
- child.once("error", () => resolve(-1));
648
- child.once("close", (code) => resolve(code ?? -1));
651
+ const exited = new Promise((resolve2) => {
652
+ child.once("error", () => resolve2(-1));
653
+ child.once("close", (code) => resolve2(code ?? -1));
649
654
  });
650
655
  child.stdin.on("error", () => void 0);
651
656
  return { stdin: child.stdin, stdout: child.stdout, exited, kill: () => void child.kill("SIGKILL"), stderrTail: () => stderr };
@@ -717,7 +722,7 @@ function converterArgs(dest, memoryBytes, extra, script) {
717
722
  }
718
723
  var firstLine2 = (text) => text.trim().split("\n")[0] ?? "";
719
724
  function exportInto(container, converter) {
720
- return new Promise((resolve, reject) => {
725
+ return new Promise((resolve2, reject) => {
721
726
  const exporter = spawn("docker", ["export", container], { stdio: ["ignore", "pipe", "pipe"], env: childEnvironment(process.env) });
722
727
  const writer = spawn("docker", converter, { stdio: ["pipe", "ignore", "pipe"], env: childEnvironment(process.env) });
723
728
  const errors = [];
@@ -734,7 +739,7 @@ function exportInto(container, converter) {
734
739
  if (code !== 0 && failure2 === void 0) failure2 = `${name} exited ${code}`;
735
740
  exits += 1;
736
741
  if (exits < 2) return;
737
- if (failure2 === void 0) resolve();
742
+ if (failure2 === void 0) resolve2();
738
743
  else reject(new Error(`${failure2}: ${firstLine2(errors.join("\n"))}`));
739
744
  };
740
745
  exporter.once("exit", (code) => done("docker export", code));
@@ -819,6 +824,46 @@ async function createBuildDisk(input) {
819
824
  function deleteBuildDisk(path) {
820
825
  rmSync(path, { force: true });
821
826
  }
827
+ var CONSOLE_CUT_NOTE = "[outerlayer runner: cut the console at 8 MiB; the microVM keeps running]\n";
828
+ function pumpConsole(fd, streams) {
829
+ const cap = 8 * 1024 ** 2;
830
+ let written = 0;
831
+ let lastByte = 10;
832
+ let cut = false;
833
+ const append = (data) => {
834
+ try {
835
+ writeSync(fd, data);
836
+ } catch {
837
+ }
838
+ };
839
+ const onData = (chunk) => {
840
+ if (cut) return;
841
+ const room = cap - written;
842
+ if (chunk.length <= room) {
843
+ append(chunk);
844
+ written += chunk.length;
845
+ lastByte = chunk[chunk.length - 1] ?? lastByte;
846
+ return;
847
+ }
848
+ const head = chunk.subarray(0, room);
849
+ append(head);
850
+ if (head.length > 0) lastByte = head[head.length - 1];
851
+ append(Buffer.from(`${lastByte === 10 ? "" : "\n"}${CONSOLE_CUT_NOTE}`));
852
+ written = cap;
853
+ cut = true;
854
+ };
855
+ let open = streams.length;
856
+ for (const stream of streams) {
857
+ stream.on("data", onData);
858
+ stream.on("error", () => void 0);
859
+ stream.once("close", () => {
860
+ open -= 1;
861
+ if (open === 0) closeSync(fd);
862
+ });
863
+ }
864
+ }
865
+
866
+ // src/runner/microvm/firecracker.ts
822
867
  var clearBit = (bit) => `0b${"x".repeat(31 - bit)}0${"x".repeat(bit)}`;
823
868
  var CPU_TEMPLATE = {
824
869
  cpuid_modifiers: [
@@ -859,8 +904,9 @@ function machineConfig(limits2, paths) {
859
904
  }
860
905
  function firecrackerCommand(input) {
861
906
  const args = ["--no-api", "--config-file", input.configPath];
862
- if (input.cgroup === void 0) return { command: input.binary, args };
863
- return { command: "sh", args: ["-c", 'echo $$ > "$1/cgroup.procs" && shift && exec "$@"', "sh", input.cgroup, input.binary, ...args] };
907
+ const binary = resolve(input.binary);
908
+ if (input.cgroup === void 0) return { command: binary, args };
909
+ return { command: "/bin/sh", args: ["-c", 'echo $$ > "$1/cgroup.procs" && shift && exec "$@"', "sh", input.cgroup, binary, ...args] };
864
910
  }
865
911
  var STOP_WAIT_MS = 1e4;
866
912
  var POLL_MS = 20;
@@ -874,6 +920,7 @@ function isZombie(pid) {
874
920
  }
875
921
  function createFirecrackerHost(options = {}) {
876
922
  const launch = options.launch ?? firecrackerCommand;
923
+ const spawnChild = options.spawnChild ?? spawn;
877
924
  const exits = /* @__PURE__ */ new Map();
878
925
  const alive = (vm) => isSameProcess(vm.pid, vm.start) && !isZombie(vm.pid);
879
926
  return {
@@ -881,18 +928,22 @@ function createFirecrackerHost(options = {}) {
881
928
  const { command, args } = launch(input);
882
929
  mkdirSync(dirname(input.consolePath), { recursive: true });
883
930
  const consoleFd = openSync(input.consolePath, "a", 384);
931
+ let pumping = false;
884
932
  try {
885
- const child = spawn(command, args, { detached: true, stdio: ["ignore", consoleFd, consoleFd] });
933
+ const child = spawnChild(command, args, { detached: true, stdio: ["ignore", "pipe", "pipe"], env: {} });
934
+ pumpConsole(consoleFd, [child.stdout, child.stderr]);
935
+ pumping = true;
936
+ for (const stream of [child.stdout, child.stderr]) stream.unref?.();
886
937
  const failed = new Promise((_resolve, reject) => child.once("error", reject));
887
938
  const pid = child.pid;
888
939
  if (pid === void 0) return await failed;
889
- exits.set(pid, new Promise((resolve) => child.once("exit", () => resolve())));
940
+ exits.set(pid, new Promise((resolve2) => child.once("exit", () => resolve2())));
890
941
  child.unref();
891
942
  const start = startTimeOf(pid);
892
943
  if (start === null) throw new Error(`${command} exited as soon as it started; its console is at ${input.consolePath}`);
893
944
  return { pid, start };
894
945
  } finally {
895
- closeSync(consoleFd);
946
+ if (!pumping) closeSync(consoleFd);
896
947
  }
897
948
  },
898
949
  async stop(vm) {
@@ -903,8 +954,8 @@ function createFirecrackerHost(options = {}) {
903
954
  }
904
955
  const exited = exits.get(vm.pid);
905
956
  const deadline = Date.now() + STOP_WAIT_MS;
906
- if (exited !== void 0) await Promise.race([exited, new Promise((resolve) => setTimeout(resolve, STOP_WAIT_MS))]);
907
- while (alive(vm) && Date.now() < deadline) await new Promise((resolve) => setTimeout(resolve, POLL_MS));
957
+ if (exited !== void 0) await Promise.race([exited, new Promise((resolve2) => setTimeout(resolve2, STOP_WAIT_MS))]);
958
+ while (alive(vm) && Date.now() < deadline) await new Promise((resolve2) => setTimeout(resolve2, POLL_MS));
908
959
  exits.delete(vm.pid);
909
960
  },
910
961
  alive
@@ -1312,6 +1363,29 @@ async function resolveAgentUser(input) {
1312
1363
  var noop = () => void 0;
1313
1364
  var FRAME_HEADER_BYTES = 9;
1314
1365
  var GOAWAY_FRAME = 7;
1366
+ function describeGoawayCode(code) {
1367
+ const names = [
1368
+ "NO_ERROR",
1369
+ "PROTOCOL_ERROR",
1370
+ "INTERNAL_ERROR",
1371
+ "FLOW_CONTROL_ERROR",
1372
+ "SETTINGS_TIMEOUT",
1373
+ "STREAM_CLOSED",
1374
+ "FRAME_SIZE_ERROR",
1375
+ "REFUSED_STREAM",
1376
+ "CANCEL",
1377
+ "COMPRESSION_ERROR",
1378
+ "CONNECT_ERROR",
1379
+ "ENHANCE_YOUR_CALM",
1380
+ "INADEQUATE_SECURITY",
1381
+ "HTTP_1_1_REQUIRED"
1382
+ ];
1383
+ const name = names[code];
1384
+ return name === void 0 ? `error code ${code}` : `${name} (${code})`;
1385
+ }
1386
+ function goawayNote(label, code) {
1387
+ return code === void 0 ? "" : ` \xB7 ${label} ${describeGoawayCode(code)}`;
1388
+ }
1315
1389
  function watchSentGoaway(onError) {
1316
1390
  let head = Buffer.alloc(0);
1317
1391
  let want = FRAME_HEADER_BYTES;
@@ -1334,7 +1408,10 @@ function watchSentGoaway(onError) {
1334
1408
  want = FRAME_HEADER_BYTES + 8;
1335
1409
  continue;
1336
1410
  }
1337
- if (want > FRAME_HEADER_BYTES && head.readUInt32BE(FRAME_HEADER_BYTES + 4) !== http2.constants.NGHTTP2_NO_ERROR) onError();
1411
+ if (want > FRAME_HEADER_BYTES) {
1412
+ const code = head.readUInt32BE(FRAME_HEADER_BYTES + 4);
1413
+ if (code !== http2.constants.NGHTTP2_NO_ERROR) onError(code);
1414
+ }
1338
1415
  skip = length - (want - FRAME_HEADER_BYTES);
1339
1416
  head = Buffer.alloc(0);
1340
1417
  want = FRAME_HEADER_BYTES;
@@ -1346,9 +1423,11 @@ function serveSession(transport, services, opts = {}) {
1346
1423
  let open = 0;
1347
1424
  let served = 0;
1348
1425
  let malformed = false;
1426
+ let goawayCode;
1349
1427
  transport.on("error", noop);
1350
- const watch = watchSentGoaway(() => {
1428
+ const watch = watchSentGoaway((code) => {
1351
1429
  malformed = true;
1430
+ goawayCode ??= code;
1352
1431
  setImmediate(() => session.destroy());
1353
1432
  });
1354
1433
  const outbound = new Transform({
@@ -1366,11 +1445,14 @@ function serveSession(transport, services, opts = {}) {
1366
1445
  session.setLocalWindowSize(SESSION_WINDOW);
1367
1446
  } catch {
1368
1447
  }
1369
- const ended = new Promise((resolve) => {
1448
+ const ended = new Promise((resolve2) => {
1370
1449
  session.on("error", (err) => {
1371
1450
  if (typeof err.code === "string" && err.code.startsWith("ERR_HTTP2_")) malformed = true;
1372
1451
  });
1373
- session.on("close", () => resolve({ malformed, served }));
1452
+ session.on("goaway", (code) => {
1453
+ if (code !== http2.constants.NGHTTP2_NO_ERROR) goawayCode ??= code;
1454
+ });
1455
+ session.on("close", () => resolve2({ malformed, served, goawayCode }));
1374
1456
  });
1375
1457
  session.on("stream", (stream, headers) => {
1376
1458
  stream.on("error", noop);
@@ -1404,14 +1486,14 @@ var JOINED_AFTER_MS = 2e3;
1404
1486
  var EXIT_WAIT_MS = 5e3;
1405
1487
  function exitedWithin(stream, ms) {
1406
1488
  let timer;
1407
- const late = new Promise((resolve) => {
1408
- timer = setTimeout(() => resolve(void 0), ms);
1489
+ const late = new Promise((resolve2) => {
1490
+ timer = setTimeout(() => resolve2(void 0), ms);
1409
1491
  timer.unref();
1410
1492
  });
1411
1493
  return Promise.race([stream.exited, late]).finally(() => clearTimeout(timer));
1412
1494
  }
1413
1495
  function superviseRelay(input) {
1414
- const sleep = input.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
1496
+ const sleep = input.sleep ?? ((ms) => new Promise((resolve2) => setTimeout(resolve2, ms)));
1415
1497
  const exitWaitMs = input.exitWaitMs ?? EXIT_WAIT_MS;
1416
1498
  let closed = false;
1417
1499
  let fatal;
@@ -1444,14 +1526,14 @@ function superviseRelay(input) {
1444
1526
  if (joined) {
1445
1527
  backoff = FIRST_BACKOFF_MS;
1446
1528
  failing = false;
1447
- if (!closed) input.log?.(`the session with ${input.container} ended \xB7 joining again`);
1529
+ if (!closed) input.log?.(`the session with ${input.container} ended \xB7 joining again${goawayNote("GOAWAY", end.goawayCode)}`);
1448
1530
  }
1449
1531
  if (!end.malformed && end.served > 0) malformedInARow = 0;
1450
1532
  if (end.malformed) {
1451
1533
  malformedInARow++;
1452
1534
  if (malformedInARow >= MAX_MALFORMED_SESSIONS) {
1453
1535
  fatal = MALFORMED_REASON;
1454
- input.log?.(`the session with ${input.container} broke HTTP/2 framing ${malformedInARow} times in a row \xB7 ending the build`);
1536
+ input.log?.(`the session with ${input.container} broke HTTP/2 framing ${malformedInARow} times in a row${goawayNote("last GOAWAY", end.goawayCode)} \xB7 ending the build`);
1455
1537
  input.onFatal?.(fatal);
1456
1538
  return;
1457
1539
  }
@@ -1461,7 +1543,7 @@ function superviseRelay(input) {
1461
1543
  input.log?.(`could not join the relay in ${input.container}${status === 0 || status === void 0 ? "" : ` (exec exited ${status})`}${reason === "" ? "" : `: ${reason}`} \xB7 trying again`);
1462
1544
  }
1463
1545
  if (closed) return;
1464
- await Promise.race([sleep(backoff), new Promise((resolve) => wake = resolve)]);
1546
+ await Promise.race([sleep(backoff), new Promise((resolve2) => wake = resolve2)]);
1465
1547
  backoff = Math.min(backoff * 2, LONGEST_BACKOFF_MS);
1466
1548
  }
1467
1549
  })();
@@ -1652,9 +1734,9 @@ async function git(args, cwd, env, input) {
1652
1734
  if (size < maxOutputBytes) out.push(chunk);
1653
1735
  size += chunk.length;
1654
1736
  });
1655
- const closed = new Promise((resolve, reject) => {
1737
+ const closed = new Promise((resolve2, reject) => {
1656
1738
  child.once("error", reject);
1657
- child.once("close", (status) => resolve(status));
1739
+ child.once("close", (status) => resolve2(status));
1658
1740
  });
1659
1741
  if (input !== void 0) {
1660
1742
  await pipeline(input, child.stdin).catch(() => void 0);
@@ -1851,14 +1933,14 @@ async function drain(req) {
1851
1933
  }
1852
1934
  }
1853
1935
  function readPrefix(req, gzip) {
1854
- return new Promise((resolve, reject) => {
1936
+ return new Promise((resolve2, reject) => {
1855
1937
  const held = [];
1856
1938
  const finish = (section) => {
1857
1939
  req.off("data", onData);
1858
1940
  req.off("end", onEnd);
1859
1941
  req.off("error", reject);
1860
1942
  req.pause();
1861
- resolve({ held, section });
1943
+ resolve2({ held, section });
1862
1944
  };
1863
1945
  const onData = (chunk) => {
1864
1946
  held.push(chunk);
@@ -1872,19 +1954,19 @@ function readPrefix(req, gzip) {
1872
1954
  });
1873
1955
  }
1874
1956
  function readBody(req, limit) {
1875
- return new Promise((resolve, reject) => {
1957
+ return new Promise((resolve2, reject) => {
1876
1958
  const chunks = [];
1877
1959
  let size = 0;
1878
1960
  req.on("data", (chunk) => {
1879
1961
  size += chunk.length;
1880
1962
  if (size > limit) {
1881
1963
  req.pause();
1882
- resolve(void 0);
1964
+ resolve2(void 0);
1883
1965
  return;
1884
1966
  }
1885
1967
  chunks.push(chunk);
1886
1968
  });
1887
- req.once("end", () => resolve(Buffer.concat(chunks)));
1969
+ req.once("end", () => resolve2(Buffer.concat(chunks)));
1888
1970
  req.once("error", reject);
1889
1971
  });
1890
1972
  }
@@ -1898,7 +1980,7 @@ function createGitRemote(options) {
1898
1980
  const { route, res } = input;
1899
1981
  const url = new URL(`${gitUrl}/${route.owner}/${route.name}.git/${route.kind === "info-refs" ? "info/refs" : route.kind === "lfs-batch" ? "info/lfs/objects/batch" : `git-${route.kind}`}${route.search === "" ? "" : `?${route.search}`}`);
1900
1982
  const send = url.protocol === "https:" ? request$1 : request;
1901
- return new Promise((resolve) => {
1983
+ return new Promise((resolve2) => {
1902
1984
  const upstream = send(url, { method: input.method, headers: { ...input.headers, connection: "close" } }, (answer) => {
1903
1985
  const headers = {};
1904
1986
  for (const [name, value] of Object.entries(answer.headers)) {
@@ -1920,18 +2002,18 @@ function createGitRemote(options) {
1920
2002
  });
1921
2003
  answer.once("end", () => {
1922
2004
  res.end();
1923
- resolve();
2005
+ resolve2();
1924
2006
  });
1925
2007
  answer.once("error", () => {
1926
2008
  res.destroy();
1927
- resolve();
2009
+ resolve2();
1928
2010
  });
1929
2011
  });
1930
2012
  upstream.on("error", (err) => {
1931
2013
  log(`could not reach GitHub for ${route.kind}: ${err.message}`);
1932
2014
  if (res.headersSent) res.destroy();
1933
2015
  else plain(res, 502, "the runner could not reach GitHub\n");
1934
- resolve();
2016
+ resolve2();
1935
2017
  });
1936
2018
  res.once("close", () => upstream.destroy());
1937
2019
  input.feed(upstream);
@@ -2228,6 +2310,9 @@ var CONNECT_TIMEOUT_MS = 3e4;
2228
2310
  var NOT_ALLOWED_RANGE = "not-allowed";
2229
2311
  var NOT_RESOLVED = "not resolved";
2230
2312
  var IDLE_TIMEOUT_MS = 10 * 6e4;
2313
+ var STALLED_RESPONSE_MS = 6e4;
2314
+ var MAX_BAD_GATEWAY_LINES = 10;
2315
+ var BAD_GATEWAY_WINDOW_MS = 1e4;
2231
2316
  async function systemResolve(host) {
2232
2317
  const found = await lookup(host, { all: true, verbatim: true });
2233
2318
  return found.map((a) => ({ address: a.address }));
@@ -2235,6 +2320,13 @@ async function systemResolve(host) {
2235
2320
  function systemHostAddresses() {
2236
2321
  return Object.values(networkInterfaces()).flatMap((list) => (list ?? []).map((i) => i.address));
2237
2322
  }
2323
+ function errorCode(err) {
2324
+ const code = err?.code;
2325
+ return typeof code === "string" ? code : "unknown";
2326
+ }
2327
+ function abortedError(message) {
2328
+ return Object.assign(new Error(message), { code: "ECONNABORTED" });
2329
+ }
2238
2330
  function normalize(address) {
2239
2331
  const zone = address.indexOf("%");
2240
2332
  return (zone === -1 ? address : address.slice(0, zone)).toLowerCase();
@@ -2249,11 +2341,31 @@ async function startTunnel(socketPath, opts = {}) {
2249
2341
  if (Buffer.byteLength(socketPath) > MAX_SOCKET_PATH_BYTES) {
2250
2342
  throw new Error(`${socketPath} is too long for a Unix socket; keep the runner's config file nearer the root of the filesystem`);
2251
2343
  }
2252
- const resolve = opts.resolve ?? systemResolve;
2344
+ const resolve2 = opts.resolve ?? systemResolve;
2253
2345
  const openConnection = opts.connect ?? ((t) => connect({ host: t.address, port: t.port }));
2254
2346
  const ownAddresses = opts.hostAddresses ?? systemHostAddresses;
2255
2347
  const connectTimeoutMs = opts.connectTimeoutMs ?? CONNECT_TIMEOUT_MS;
2256
2348
  const idleTimeoutMs = opts.idleTimeoutMs ?? IDLE_TIMEOUT_MS;
2349
+ const stalledResponseMs = opts.stalledResponseMs ?? STALLED_RESPONSE_MS;
2350
+ let windowStartedMs = 0;
2351
+ let linesInWindow = 0;
2352
+ let suppressed = 0;
2353
+ const logBadGateway = (what) => {
2354
+ if (opts.log === void 0) return;
2355
+ const now = Date.now();
2356
+ if (now - windowStartedMs >= BAD_GATEWAY_WINDOW_MS) {
2357
+ windowStartedMs = now;
2358
+ linesInWindow = 0;
2359
+ }
2360
+ if (linesInWindow >= MAX_BAD_GATEWAY_LINES) {
2361
+ suppressed++;
2362
+ return;
2363
+ }
2364
+ linesInWindow++;
2365
+ const more = suppressed === 0 ? "" : ` (${suppressed} more 502 lines were not written)`;
2366
+ suppressed = 0;
2367
+ opts.log(`tunnel answered 502: ${what}${more}`);
2368
+ };
2257
2369
  const hosts = [];
2258
2370
  const seen = /* @__PURE__ */ new Set();
2259
2371
  const extraHosts = /* @__PURE__ */ new Set();
@@ -2325,7 +2437,7 @@ async function startTunnel(socketPath, opts = {}) {
2325
2437
  }
2326
2438
  persist();
2327
2439
  };
2328
- async function decide(host) {
2440
+ async function decide(host, port) {
2329
2441
  if (opts.allowHosts !== void 0 && !hostAllowed(host, opts.allowHosts)) {
2330
2442
  refused2(host, NOT_RESOLVED, NOT_ALLOWED_RANGE);
2331
2443
  return { ok: false, status: 403, body: `refused: ${host} is not in runner.build.allowHosts
@@ -2333,13 +2445,17 @@ async function startTunnel(socketPath, opts = {}) {
2333
2445
  }
2334
2446
  let addresses;
2335
2447
  try {
2336
- addresses = await resolve(host);
2337
- } catch {
2448
+ addresses = await resolve2(host);
2449
+ } catch (err) {
2450
+ logBadGateway(`could not resolve ${host}:${port}, ${errorCode(err)}`);
2338
2451
  return { ok: false, status: 502, body: `could not resolve ${host}
2339
2452
  ` };
2340
2453
  }
2341
- if (addresses.length === 0) return { ok: false, status: 502, body: `${host} has no address
2454
+ if (addresses.length === 0) {
2455
+ logBadGateway(`${host}:${port} resolved to no address`);
2456
+ return { ok: false, status: 502, body: `${host} has no address
2342
2457
  ` };
2458
+ }
2343
2459
  const own = new Set(ownAddresses().map(normalize));
2344
2460
  let firstRefusal;
2345
2461
  for (const { address: address2 } of addresses) {
@@ -2381,36 +2497,70 @@ ${body}`);
2381
2497
  client.end("HTTP/1.1 400 Bad Request\r\nConnection: close\r\nContent-Length: 0\r\n\r\n");
2382
2498
  return;
2383
2499
  }
2384
- const decision = await decide(target.host);
2500
+ const decision = await decide(target.host, target.port);
2385
2501
  if (!decision.ok) {
2386
2502
  reply(client, decision.status, decision.body);
2387
2503
  return;
2388
2504
  }
2389
2505
  const upstream = openConnection({ address: decision.address, port: target.port });
2390
2506
  upstream.setTimeout(connectTimeoutMs);
2391
- const failed = () => {
2507
+ const failed = (err) => {
2392
2508
  upstream.destroy();
2509
+ logBadGateway(`could not connect to ${target.host}:${target.port}, ${err === void 0 ? "ETIMEDOUT" : errorCode(err)}`);
2393
2510
  reply(client, 502, `could not connect to ${target.host}
2394
2511
  `);
2395
2512
  };
2513
+ const timedOut = () => failed();
2396
2514
  upstream.once("error", failed);
2397
- upstream.once("timeout", failed);
2515
+ upstream.once("timeout", timedOut);
2398
2516
  client.on("close", () => upstream.destroy());
2399
2517
  const established = () => {
2400
2518
  upstream.removeListener("error", failed);
2401
- upstream.removeListener("timeout", failed);
2519
+ upstream.removeListener("timeout", timedOut);
2402
2520
  upstream.setTimeout(idleTimeoutMs, () => upstream.destroy());
2403
- upstream.on("error", () => void 0);
2404
- upstream.on("close", () => client.destroy());
2521
+ upstream.on("error", (err) => client.destroy(err));
2522
+ upstream.on("close", () => {
2523
+ if (!upstream.readableEnded && !client.writableEnded) client.destroy(abortedError("the tunnel closed the connection to the server before it ended"));
2524
+ });
2525
+ client.once("end", () => {
2526
+ upstream.destroy();
2527
+ client.end();
2528
+ });
2405
2529
  reached(target.host);
2406
2530
  client.write("HTTP/1.1 200 Connection Established\r\n\r\n");
2407
2531
  upstream.write(head);
2532
+ releaseWhenStalled(client, upstream);
2408
2533
  upstream.pipe(client);
2409
2534
  client.pipe(upstream);
2410
2535
  };
2411
2536
  if (upstream.readyState === "open") established();
2412
2537
  else upstream.once("connect", established);
2413
2538
  }
2539
+ function releaseWhenStalled(client, upstream) {
2540
+ let delivered = 0;
2541
+ upstream.on("data", (chunk) => delivered += chunk.length);
2542
+ let read = delivered;
2543
+ let timer;
2544
+ const blocked = () => upstream.isPaused() || upstream.readableEnded;
2545
+ const check = () => {
2546
+ timer = void 0;
2547
+ if (client.writableFinished || !blocked()) return;
2548
+ if (delivered > read) {
2549
+ read = delivered;
2550
+ timer = setTimeout(check, stalledResponseMs);
2551
+ return;
2552
+ }
2553
+ client.destroy(abortedError("the build stopped reading the response"));
2554
+ };
2555
+ const watch = () => {
2556
+ if (timer !== void 0) return;
2557
+ read = delivered;
2558
+ timer = setTimeout(check, stalledResponseMs);
2559
+ };
2560
+ upstream.on("pause", watch);
2561
+ upstream.on("end", watch);
2562
+ client.once("close", () => clearTimeout(timer));
2563
+ }
2414
2564
  async function proxyPlain(req, res) {
2415
2565
  if (opts.gitRemote !== void 0 && (req.url ?? "").startsWith("/")) {
2416
2566
  await opts.gitRemote(req, res);
@@ -2431,7 +2581,7 @@ ${body}`);
2431
2581
  }
2432
2582
  const host = url.hostname.replace(/^\[|\]$/g, "");
2433
2583
  const port = url.port === "" ? 80 : Number(url.port);
2434
- const decision = await decide(host);
2584
+ const decision = await decide(host, port);
2435
2585
  if (!decision.ok) {
2436
2586
  res.writeHead(decision.status, { "Content-Type": "text/plain", Connection: "close" });
2437
2587
  res.end(decision.body);
@@ -2456,8 +2606,9 @@ ${body}`);
2456
2606
  );
2457
2607
  upstream.setTimeout(connectTimeoutMs, () => upstream.destroy());
2458
2608
  res.on("close", () => upstream.destroy());
2459
- upstream.on("error", () => {
2609
+ upstream.on("error", (err) => {
2460
2610
  if (!res.headersSent) {
2611
+ logBadGateway(`could not reach ${host}:${port}, ${errorCode(err)}`);
2461
2612
  res.writeHead(502, { "Content-Type": "text/plain", Connection: "close" });
2462
2613
  res.end(`could not reach ${host}
2463
2614
  `);
@@ -2510,6 +2661,9 @@ var RELAY_WAIT_MS = 500;
2510
2661
  var BUSY_IMAGE_WAIT_MS = 5e3;
2511
2662
  var VOLUME_REMOVE_ATTEMPTS = 4;
2512
2663
  var WORKFLOW_PERMISSION_REASON = "workflow_permission_required";
2664
+ function withTunnelLog(opts, log) {
2665
+ return { ...opts, tunnelOptions: { log, ...opts?.tunnelOptions } };
2666
+ }
2513
2667
  function createContainerRuntime(opts = {}) {
2514
2668
  const { cli, remoteFor } = opts;
2515
2669
  return {
@@ -2534,7 +2688,7 @@ function createContainerRuntime(opts = {}) {
2534
2688
  )),
2535
2689
  startTunnel: opts.startTunnel ?? startTunnel,
2536
2690
  tunnelOptions: opts.tunnelOptions,
2537
- sleep: opts.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms))),
2691
+ sleep: opts.sleep ?? ((ms) => new Promise((resolve2) => setTimeout(resolve2, ms))),
2538
2692
  hostEnv: opts.hostEnv ?? process.env,
2539
2693
  platform: opts.platform ?? process.platform,
2540
2694
  credentials: opts.credentials ?? /* @__PURE__ */ new Map(),
@@ -2835,7 +2989,7 @@ async function provisionContainer(input) {
2835
2989
  } catch (err) {
2836
2990
  return bail(`the tunnel could not start: ${describeCallFailure(err)}`);
2837
2991
  }
2838
- const limits2 = { memory: config.build?.memory ?? "8g", cpus: config.build?.cpus ?? 4, pids: config.build?.pids ?? 4096, cpuset: state.cpuset };
2992
+ const limits2 = { memory: config.build?.memory ?? "8g", cpus: capCpus(config.build?.cpus ?? 4, state.cpuset), pids: config.build?.pids ?? 4096, cpuset: state.cpuset };
2839
2993
  const runnerId = runnerLabelValue(config.configPath);
2840
2994
  const volume = await host.createVolume(name, { [BUILD_LABEL]: buildId, [RUNNER_LABEL]: runnerId });
2841
2995
  if (volume.status !== 0) return dockerFailed("creating the build's volume", volume);
@@ -3017,8 +3171,8 @@ async function stopContainer(runtime, state) {
3017
3171
  var DISK_CHECK_MS = 12e4;
3018
3172
  async function measureDisk(runtime, state) {
3019
3173
  let timer;
3020
- const late = new Promise((resolve) => {
3021
- timer = setTimeout(() => resolve({ failure: "du ran past 2 minutes" }), DISK_CHECK_MS);
3174
+ const late = new Promise((resolve2) => {
3175
+ timer = setTimeout(() => resolve2({ failure: "du ran past 2 minutes" }), DISK_CHECK_MS);
3022
3176
  });
3023
3177
  try {
3024
3178
  const run = runtime.host.exec(
@@ -3306,7 +3460,7 @@ async function startCheckContainer(input) {
3306
3460
  } catch (err) {
3307
3461
  return fail(`the tunnel could not start: ${describeCallFailure(err)}`);
3308
3462
  }
3309
- const limits2 = { memory: config.build?.memory ?? "8g", cpus: config.build?.cpus ?? 4, pids: config.build?.pids ?? 4096, cpuset };
3463
+ const limits2 = { memory: config.build?.memory ?? "8g", cpus: capCpus(config.build?.cpus ?? 4, cpuset), pids: config.build?.pids ?? 4096, cpuset };
3310
3464
  const runnerId = runnerLabelValue(config.configPath);
3311
3465
  const volume = await host.createVolume(name, { [BUILD_LABEL]: buildId, [RUNNER_LABEL]: runnerId });
3312
3466
  if (volume.status !== 0) return dockerFailed("creating the check environment's volume", volume);
@@ -3370,4 +3524,4 @@ async function startCheckContainer(input) {
3370
3524
  };
3371
3525
  }
3372
3526
 
3373
- export { ADDED_USER, AgentUserError, AssetError, DeliveryFailure, EVAL_INPUT_NAME, GH_CONFIG_YML, HelperRefusal, LEASE_SECONDS, MIN_ENGINE_MAJOR, cleanupContainer, containerCommandExec, containerJudgeEnvironment, containerSenderCommand, copyJudgeTree, copyTranscripts, copyTranscriptsFromContainer, createBuildDisk, createContainerRuntime, createLeaseChannel, deleteBuildDisk, deliverContainerHooks, ensureFirecrackerAndJailer, ensureRootfs, ensureTunnel, filesAreCopied, ghHostsYml, gitRemoteFor, helperFirecrackerNote, helperSocketPath, isLiteralAddress, keepGhConfigCurrent, leaseExpiresAtMs, leaseTimeLeftMs, lifecycleFailureReason, lifecycleSteps, machineConfig, measureDisk, obtainImage, outOfMemoryReason, parseCpuList, prepareBranch, probeHelper, provisionContainer, resolveAgentUser, runBuildSteps, senderAliveScript, senderSignalScript, setUpRunnerCgroup, shellQuote, signalInContainer, startCheckContainer, startServicesFor, stopContainer, syncInContainer, writeEvalInput };
3527
+ export { ADDED_USER, AgentUserError, AssetError, DeliveryFailure, EVAL_INPUT_NAME, GH_CONFIG_YML, HelperRefusal, LEASE_SECONDS, MIN_ENGINE_MAJOR, cleanupContainer, containerCommandExec, containerJudgeEnvironment, containerSenderCommand, copyJudgeTree, copyTranscripts, copyTranscriptsFromContainer, createBuildDisk, createContainerRuntime, createLeaseChannel, deleteBuildDisk, deliverContainerHooks, ensureFirecrackerAndJailer, ensureRootfs, ensureTunnel, filesAreCopied, ghHostsYml, gitRemoteFor, helperFirecrackerNote, helperSocketPath, isLiteralAddress, keepGhConfigCurrent, leaseExpiresAtMs, leaseTimeLeftMs, lifecycleFailureReason, lifecycleSteps, machineConfig, measureDisk, obtainImage, outOfMemoryReason, parseCpuList, prepareBranch, probeHelper, provisionContainer, resolveAgentUser, runBuildSteps, senderAliveScript, senderSignalScript, setUpRunnerCgroup, shellQuote, signalInContainer, startCheckContainer, startServicesFor, stopContainer, syncInContainer, withTunnelLog, writeEvalInput };
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- import { runnerDir, RUNNER_DIR_MODE, RUNNER_FILE_MODE } from './chunk-RHAGBUL5.js';
3
+ import { runnerDir, RUNNER_DIR_MODE, RUNNER_FILE_MODE } from './chunk-5K3KB4YR.js';
4
4
  import { readlinkSync, realpathSync, mkdirSync, unlinkSync, symlinkSync, renameSync, readFileSync, writeFileSync, rmSync, existsSync, readdirSync } from 'fs';
5
5
  import { join, basename, sep, resolve } from 'path';
6
6