@outerlayer/cli 0.24.0 → 0.25.1

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 (114) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/README.md +9 -0
  3. package/dist/{agent-setup-QQYRBPK2.js → agent-setup-PDIIGMKE.js} +7 -7
  4. package/dist/{bridge-4ZARDVGS.js → bridge-5KZ36YDG.js} +3 -3
  5. package/dist/build-YYVAJ75G.js +12 -0
  6. package/dist/build-info.json +1 -1
  7. package/dist/{builder-MECDEKXX.js → builder-MNUJS4RG.js} +7 -7
  8. package/dist/{check-YYKNYFNI.js → check-C6HEFW6R.js} +39 -38
  9. package/dist/{chunk-BUBCPP3F.js → chunk-23SPSYXZ.js} +3 -3
  10. package/dist/{chunk-5ECUMU53.js → chunk-24LQ34XL.js} +6 -6
  11. package/dist/{chunk-BN3CSI7S.js → chunk-2K3TCQHW.js} +1 -1
  12. package/dist/{chunk-A62QFN2M.js → chunk-2N72GPS2.js} +3 -3
  13. package/dist/{chunk-TQ6Y7YV6.js → chunk-3FNPTQRH.js} +5 -5
  14. package/dist/{chunk-F2W2IYNP.js → chunk-3WZ5ECSM.js} +3 -3
  15. package/dist/{chunk-WH2QV3UL.js → chunk-4S4VBDSO.js} +1 -1
  16. package/dist/{chunk-IDSHLKQA.js → chunk-5222PA6S.js} +1 -1
  17. package/dist/{chunk-6GSA2SKT.js → chunk-A6ORKBVJ.js} +213 -59
  18. package/dist/{chunk-3W333NFO.js → chunk-APBTVIM4.js} +15 -4
  19. package/dist/{chunk-HHXBOTRO.js → chunk-ATGHVEU4.js} +1 -1
  20. package/dist/{chunk-D7K446H6.js → chunk-C36EOUUU.js} +5 -5
  21. package/dist/{chunk-RVYOOYOA.js → chunk-CQRIZNE6.js} +19 -10
  22. package/dist/{chunk-FWEWFW3F.js → chunk-DEBOL6ON.js} +1 -1
  23. package/dist/{chunk-GU4PKXN7.js → chunk-ENFBARQP.js} +185 -38
  24. package/dist/{chunk-H6IUKXXI.js → chunk-F57CLUH7.js} +207 -35
  25. package/dist/{chunk-E4OUL7ZP.js → chunk-FXUVXSY2.js} +2 -2
  26. package/dist/{chunk-F7Y2USIM.js → chunk-H6ZFLIX3.js} +3 -3
  27. package/dist/{chunk-LY2L3O2O.js → chunk-HPOEVC2V.js} +3 -2
  28. package/dist/{chunk-ZFLOFAUG.js → chunk-I25F6YVP.js} +8 -8
  29. package/dist/{chunk-DG2ULWM6.js → chunk-IGRRD7PX.js} +11 -7
  30. package/dist/{chunk-GISNHSYL.js → chunk-JGD5TWQ2.js} +4 -2
  31. package/dist/{chunk-D7X5ITVT.js → chunk-JWRX5ZTT.js} +6 -2
  32. package/dist/{chunk-755WXDBI.js → chunk-K5WVK7IC.js} +1 -1
  33. package/dist/{chunk-FKXGQOF5.js → chunk-M4B6QKNZ.js} +1 -1
  34. package/dist/{chunk-5XCAPBE2.js → chunk-N3VSI5ZI.js} +6 -6
  35. package/dist/{chunk-NTQTDCYR.js → chunk-O2WYUIK7.js} +1 -1
  36. package/dist/{chunk-BOQY756V.js → chunk-OEU4JSDE.js} +8 -7
  37. package/dist/{chunk-TTLVQZSH.js → chunk-Q435LWVN.js} +5 -5
  38. package/dist/{chunk-D4AMAEZ7.js → chunk-QE3UQAHK.js} +322 -17
  39. package/dist/{chunk-VV7ZD5LR.js → chunk-QMQC6JCO.js} +1 -1
  40. package/dist/{chunk-FQPN5S4Q.js → chunk-QXNJWSTM.js} +4 -4
  41. package/dist/{chunk-VBEXKEBV.js → chunk-S6AWZKJN.js} +19 -6
  42. package/dist/{chunk-V5Q3S4NM.js → chunk-SNQR2EYI.js} +2 -2
  43. package/dist/{chunk-CZUH2SLQ.js → chunk-UNF7MWEL.js} +1 -1
  44. package/dist/{chunk-W7O4LA4E.js → chunk-VAMUWK55.js} +3 -3
  45. package/dist/{chunk-PDIM5T65.js → chunk-XGJUNNXL.js} +1 -1
  46. package/dist/{chunk-NGZID7UI.js → chunk-XRYUJTAT.js} +8 -1
  47. package/dist/{chunk-LBCZXXOX.js → chunk-ZGMQQWCV.js} +2 -2
  48. package/dist/{chunk-KF37SUDM.js → chunk-ZI2ZVWKT.js} +1 -1
  49. package/dist/{chunk-PVRBE7S4.js → chunk-ZODVSFI4.js} +1 -1
  50. package/dist/chunk-ZVB57WI6.js +19 -0
  51. package/dist/{claude-launcher-JCPSHDZQ.js → claude-launcher-WGOCLTG6.js} +1 -1
  52. package/dist/{cli-ZTBH3FBG.js → cli-YMTNHR4W.js} +107 -102
  53. package/dist/command-DKYZC7PH.js +533 -0
  54. package/dist/{config-7Z5DHSGY.js → config-JJ3EO7SB.js} +3 -3
  55. package/dist/{connect-cmd-NY5LIKVC.js → connect-cmd-F7T4LNKX.js} +7 -7
  56. package/dist/{container-start-OHBUZEJJ.js → container-start-AX7QXJMR.js} +45 -36
  57. package/dist/{control-plane-launch-GKGI6HJA.js → control-plane-launch-ZFT6SI4N.js} +6 -6
  58. package/dist/{dist-2APO44JG.js → dist-QPHZE47F.js} +1 -1
  59. package/dist/{docker-RKGBJESN.js → docker-5EC5TE3Y.js} +7 -7
  60. package/dist/{doctor-DOAFZV4O.js → doctor-S6YCZGUU.js} +79 -33
  61. package/dist/{emit-artifact-cmd-VZHIDPXQ.js → emit-artifact-cmd-DJO475VZ.js} +11 -11
  62. package/dist/{emit-criteria-cmd-SYLBCWHW.js → emit-criteria-cmd-EULQ2MLI.js} +8 -8
  63. package/dist/{emit-finding-cmd-52M43NPC.js → emit-finding-cmd-QIIKMITO.js} +8 -8
  64. package/dist/{emit-result-cmd-BTMTGDAV.js → emit-result-cmd-NMCLTA47.js} +11 -11
  65. package/dist/{emit-step-cmd-6XTJITZT.js → emit-step-cmd-72OX5P6M.js} +9 -9
  66. package/dist/{exec-client-CKPK62K2.js → exec-client-KH33DICR.js} +4 -4
  67. package/dist/{governing-source-GWN7GVEN.js → governing-source-JNQ6PX2A.js} +11 -11
  68. package/dist/{guest-init-ZBL5CN7Y.js → guest-init-TOSKPGPF.js} +5 -5
  69. package/dist/{hook-fast-3JL4PXLG.js → hook-fast-NEELST6G.js} +1 -1
  70. package/dist/host-key-ICJEQBWH.js +9 -0
  71. package/dist/{image-tag-AO4RBAKW.js → image-tag-6KJOGBQK.js} +4 -4
  72. package/dist/index.js +4 -4
  73. package/dist/{init-TSQ3EOQW.js → init-624F4JIP.js} +25 -23
  74. package/dist/{init-EQTR7AEF.js → init-G7XC5WWL.js} +25 -23
  75. package/dist/{init-cmd-AF2MH36A.js → init-cmd-UUYA57VO.js} +11 -11
  76. package/dist/{init-update-SHISHMXV.js → init-update-MMV33PLM.js} +9 -9
  77. package/dist/{install-ORQC26ZE.js → install-MPEJJ7X6.js} +5 -5
  78. package/dist/{install-cmd-4LA3UJZ4.js → install-cmd-SE6NCTJK.js} +6 -6
  79. package/dist/job-state-format-WJQGQB6F.js +6 -0
  80. package/dist/{logs-5EPHIPZB.js → logs-T4ERSOQ4.js} +3 -3
  81. package/dist/loop-K5B2A3VW.js +78 -0
  82. package/dist/{machine-6UNLIPLE.js → machine-J4IFVIKU.js} +21 -19
  83. package/dist/{mcp-serve-cmd-KMWEKM7C.js → mcp-serve-cmd-7M4VLNI4.js} +5 -5
  84. package/dist/{paths-XY5ADO32.js → paths-2KGM2LZV.js} +3 -3
  85. package/dist/pending-BGLMTB5Q.js +9 -0
  86. package/dist/{pidfile-OJ7RP4Q2.js → pidfile-VYBH3IMN.js} +4 -4
  87. package/dist/{record-BEJB4J7N.js → record-GNOHDJ2O.js} +4 -4
  88. package/dist/{relay-PSZ4LGMW.js → relay-GFPPD6GF.js} +1 -1
  89. package/dist/relay-protocol-HELO5SNH.js +6 -0
  90. package/dist/{runner-env-ZJWJOJKQ.js → runner-env-GUFV544G.js} +6 -6
  91. package/dist/{runner-identity-2OOUJSFJ.js → runner-identity-HZ6CBM2R.js} +7 -7
  92. package/dist/{runner-key-WDFBSJRX.js → runner-key-FFVZUEHE.js} +25 -23
  93. package/dist/{session-sender-X3BBQZMF.js → session-sender-3SUWNNVP.js} +12 -12
  94. package/dist/{spec-cmd-QRF5YXKK.js → spec-cmd-VZDE6FFH.js} +9 -9
  95. package/dist/{status-ZCAAF6MD.js → status-GXTD44DX.js} +35 -29
  96. package/dist/{sync-cmd-UQK3RYAC.js → sync-cmd-VFB3NEJ6.js} +11 -11
  97. package/dist/{watch-MOQ3GQTE.js → watch-LWMTDD3L.js} +11 -11
  98. package/dist/{work-claim-cmd-RT343CLA.js → work-claim-cmd-FOBTDQVR.js} +12 -12
  99. package/dist/{work-cmd-APXSER2D.js → work-cmd-6HARIBMW.js} +9 -9
  100. package/dist/{work-comment-cmd-AT5NUYUU.js → work-comment-cmd-BITPALHR.js} +9 -9
  101. package/dist/{work-logs-cmd-Z5DMNEJP.js → work-logs-cmd-VGB3O2LZ.js} +9 -9
  102. package/dist/{work-open-pr-cmd-ZTS6MJHL.js → work-open-pr-cmd-SVZCNOIM.js} +10 -10
  103. package/dist/{work-pr-cmd-IWZCC3WH.js → work-pr-cmd-SJT46GN4.js} +9 -9
  104. package/dist/{work-stop-cmd-DCAWFDZC.js → work-stop-cmd-WXKVVA4S.js} +9 -9
  105. package/package.json +2 -2
  106. package/skill-pack/maintained/outerlayer-build/SKILL.md +1 -1
  107. package/skill-pack/maintained/outerlayer-build/references/agent-briefs.md +3 -2
  108. package/skill-pack/maintained/outerlayer-build/references/review-briefs.md +3 -1
  109. package/dist/build-OVZIT2G2.js +0 -12
  110. package/dist/command-DO4TERRT.js +0 -208
  111. package/dist/host-key-W2DLZGZ7.js +0 -9
  112. package/dist/loop-4WUCS5JP.js +0 -77
  113. package/dist/pending-35J3ZPLR.js +0 -9
  114. package/dist/relay-protocol-K7KP2SPI.js +0 -6
@@ -1,26 +1,28 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- import { readDefaultBranchHead, cloneRepository, buildRepositoryImage } from './chunk-TTLVQZSH.js';
3
+ import { runnerImageVersion } from './chunk-5222PA6S.js';
4
+ import { readDefaultBranchHead, cloneRepository, buildRepositoryImage } from './chunk-Q435LWVN.js';
4
5
  import { startTimeOf, isSameProcess } from './chunk-JBJTC3UU.js';
5
6
  import { currentCliBuild } from './chunk-OGFGZQA3.js';
6
- 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-5XCAPBE2.js';
7
- import { RecipeError, CLAUDE_CODE_VERSION, firecrackerArch, MICROVM_ARCHES, DEFAULT_BASE_IMAGE, GUEST_HELPER_SHA256, GUEST_ASSETS_BASE_URL, GUEST_KERNEL_SHA256, FIRECRACKER_BINARY_SHA256, FIRECRACKER_SHA256, FIRECRACKER_VERSION, GUEST_ASSETS_VERSION } from './chunk-3W333NFO.js';
8
- import { execCapture } from './chunk-LY2L3O2O.js';
9
- import { GUEST_CID } from './chunk-PDIM5T65.js';
10
- import { describeCallFailure, controlPlaneTokenFailure, refusalCode, resolveGoverningSource, removeStagedContext, stageGovernedContext, pinnedSourceOf, commandWithShim, shimScript } from './chunk-TQ6Y7YV6.js';
11
- import { WorkCommandError } from './chunk-RVYOOYOA.js';
7
+ 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-N3VSI5ZI.js';
8
+ import { 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, FIRECRACKER_VERSION, GUEST_ASSETS_VERSION, DEFAULT_BASE_IMAGE } from './chunk-APBTVIM4.js';
9
+ import { execCapture } from './chunk-HPOEVC2V.js';
10
+ import { GUEST_CID } from './chunk-XGJUNNXL.js';
11
+ import { describeCallFailure, controlPlaneTokenFailure, refusalCode, resolveGoverningSource, removeStagedContext, stageGovernedContext, pinnedSourceOf, commandWithShim, shimScript } from './chunk-3FNPTQRH.js';
12
+ import { WorkCommandError } from './chunk-CQRIZNE6.js';
12
13
  import { hostAllowed } from './chunk-UXYLWVTQ.js';
13
14
  import { parseDuKilobytes } from './chunk-7RQ73DLO.js';
14
15
  import { sharedSkillNames, skillConflictMessage } from './chunk-VPEGE3QZ.js';
15
16
  import { versionAtLeast, MINIMUM_CLAUDE_VERSION } from './chunk-HCVLP7XA.js';
16
- import { setCustomScrubbers, setLocalIdentity, setRepoRoot, parseTranscript, scrubDeep, scrubText } from './chunk-D4AMAEZ7.js';
17
+ import { setCustomScrubbers, setLocalIdentity, setRepoRoot, parseTranscript, scrubDeep, scrubText } from './chunk-QE3UQAHK.js';
17
18
  import { orderSessions, extractFacts, sessionsToTimeline } from './chunk-RKOTJQVG.js';
18
19
  import { downconvertSession } from './chunk-QZBXLLTV.js';
19
- import { rootfsPathFor, diskTemplatesDir, RUNNER_FILE_MODE, deliveryDir, ghConfigDir, runnerDirOfBuild, spoolDir, mirrorDir, buildDir, RUNNER_DIR_MODE, SHIM_DIR, vmAssetsDir } from './chunk-KF37SUDM.js';
20
- import { repositoriesOf, primaryOf, RELAY_PORT, repositoryPath, CHECKOUT_DIR, lifecycleWorkdir, claudeCredential, buildEnvironment, startBuildServices, checkEnvironment, AGENT_CREDENTIAL_MISSING, checkoutPath, JOB_MOUNT } from './chunk-BN3CSI7S.js';
21
- import { RELAY_USER, RELAY_ID, UNKNOWN_TRANSPORT_REASON, MAX_MALFORMED_SESSIONS, MALFORMED_REASON, MAX_OPEN_STREAMS, STREAM_WINDOW, SESSION_WINDOW, targetOfPath } from './chunk-GISNHSYL.js';
22
- import { readFileSync, existsSync, mkdirSync, chmodSync, truncateSync, rmSync, writeFileSync, readdirSync, lstatSync, copyFileSync, accessSync, constants, statSync, rmdirSync, renameSync, unlinkSync, openSync, closeSync, mkdtempSync, createWriteStream, createReadStream } from 'fs';
20
+ import { vmAssetsDir, RUNNER_DIR_MODE, rootfsPathFor, diskTemplatesDir, RUNNER_FILE_MODE, deliveryDir, ghConfigDir, runnerDirOfBuild, spoolDir, mirrorDir, buildDir, SHIM_DIR } from './chunk-ZI2ZVWKT.js';
21
+ import { repositoriesOf, primaryOf, RELAY_PORT, repositoryPath, CHECKOUT_DIR, lifecycleWorkdir, claudeCredential, buildEnvironment, startBuildServices, checkEnvironment, AGENT_CREDENTIAL_MISSING, checkoutPath, JOB_MOUNT } from './chunk-2K3TCQHW.js';
22
+ 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';
23
+ import { readFileSync, mkdirSync, existsSync, mkdtempSync, rmSync, chmodSync, truncateSync, writeFileSync, readdirSync, lstatSync, copyFileSync, accessSync, constants, statSync, rmdirSync, renameSync, unlinkSync, openSync, closeSync, createWriteStream, createReadStream } from 'fs';
23
24
  import { join, dirname, basename } from 'path';
25
+ import { isIP, createConnection, connect, createServer as createServer$1 } from 'net';
24
26
  import { spawnSync, spawn } from 'child_process';
25
27
  import { availableParallelism, networkInterfaces } from 'os';
26
28
  import { randomBytes, createHash } from 'crypto';
@@ -30,7 +32,6 @@ import { createServer, request } from 'http';
30
32
  import { request as request$1 } from 'https';
31
33
  import { pipeline, finished } from 'stream/promises';
32
34
  import { createGunzip, gunzipSync, constants as constants$1 } from 'zlib';
33
- import { isIP, connect, createServer as createServer$1 } from 'net';
34
35
  import { lookup } from 'dns/promises';
35
36
 
36
37
  createRequire(import.meta.url);
@@ -144,6 +145,101 @@ function realCgroupPort() {
144
145
  remove: (path) => removeBuildCgroup(fs, path)
145
146
  };
146
147
  }
148
+ var SUPPORTED_HELPER_PROTOCOLS = [1];
149
+ var HELPER_ENV = "OUTERLAYER_HELPER_SOCKET";
150
+ var HelperRefusal = class extends Error {
151
+ constructor(code, reason) {
152
+ super(reason);
153
+ this.code = code;
154
+ }
155
+ };
156
+ function helperSocketPath(unitName) {
157
+ return `/run/${unitName}-helper/helper.sock`;
158
+ }
159
+ var START_TIMEOUT_MS = 12e4;
160
+ var DEFAULT_REQUEST_TIMEOUT_MS = 3e4;
161
+ var MAX_REPLY_BYTES = 1024 * 1024;
162
+ function ask(socketPath, request, timeoutMs) {
163
+ return new Promise((resolve, reject) => {
164
+ const conn = createConnection(socketPath);
165
+ let text = "";
166
+ let settled = false;
167
+ const settle = (finish) => {
168
+ if (settled) return;
169
+ settled = true;
170
+ conn.destroy();
171
+ finish();
172
+ };
173
+ conn.setTimeout(timeoutMs, () => settle(() => reject(new Error(`the helper at ${socketPath} did not answer within ${Math.round(timeoutMs / 1e3)} seconds`))));
174
+ conn.on("connect", () => conn.write(`${JSON.stringify(request)}
175
+ `));
176
+ conn.on("data", (chunk) => {
177
+ text += chunk.toString("utf8");
178
+ if (text.length > MAX_REPLY_BYTES) return settle(() => reject(new Error(`the helper at ${socketPath} sent a reply that is too long`)));
179
+ const end = text.indexOf("\n");
180
+ if (end === -1) return;
181
+ settle(() => {
182
+ let reply;
183
+ try {
184
+ reply = JSON.parse(text.slice(0, end));
185
+ } catch {
186
+ return reject(new Error(`the helper at ${socketPath} sent a reply that is not JSON`));
187
+ }
188
+ if (reply.status === "refused") return reject(new HelperRefusal(String(reply.code ?? "refused"), String(reply.reason ?? "the helper refused the request")));
189
+ resolve(reply);
190
+ });
191
+ });
192
+ conn.on("error", (err) => settle(() => reject(new Error(`the helper at ${socketPath} did not answer: ${err.message}`))));
193
+ conn.on("close", () => settle(() => reject(new Error(`the helper at ${socketPath} closed the connection without a reply`))));
194
+ });
195
+ }
196
+ function helperClient(socketPath, options = {}) {
197
+ const REQUEST_TIMEOUT_MS = options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
198
+ return {
199
+ async start({ build, kind }) {
200
+ const reply = await ask(socketPath, { v: 1, op: "start", build, kind }, START_TIMEOUT_MS);
201
+ return { unit: String(reply.unit), chroot: String(reply.chroot), vsock: String(reply.vsock) };
202
+ },
203
+ async stop(build) {
204
+ await ask(socketPath, { v: 1, op: "stop", build }, REQUEST_TIMEOUT_MS);
205
+ },
206
+ async state(build) {
207
+ const reply = await ask(socketPath, { v: 1, op: "state", build }, REQUEST_TIMEOUT_MS);
208
+ return { active: reply.active === true, oomKills: typeof reply.oomKills === "number" ? reply.oomKills : 0 };
209
+ },
210
+ async list() {
211
+ const reply = await ask(socketPath, { v: 1, op: "list" }, REQUEST_TIMEOUT_MS);
212
+ return Array.isArray(reply.builds) ? reply.builds.map(String) : [];
213
+ }
214
+ };
215
+ }
216
+ function reinstallMessage(protocol) {
217
+ const spoken = protocol === void 0 ? "a protocol it does not name" : `protocol ${protocol}`;
218
+ return `the root helper speaks ${spoken}, which this runner does not support, so microVM builds run without the jailer; run \`outerlayer runner install --service --replace\` to install the helper that goes with this release`;
219
+ }
220
+ async function probeHelper(socketPath, options = {}) {
221
+ if (socketPath === void 0 || socketPath === "") return { ok: false, reason: "absent", detail: `${HELPER_ENV} is not set, so this runner was not started by a service that has a helper` };
222
+ if (!(options.pinned ?? hostHelperPinned())) return { ok: false, reason: "unpinned", detail: "this release of the CLI pins no helper, so builds run without the jailer" };
223
+ try {
224
+ const reply = await ask(socketPath, { v: 1, op: "hello" }, options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS);
225
+ const protocol = typeof reply.protocol === "number" ? reply.protocol : void 0;
226
+ if (protocol === void 0 || !SUPPORTED_HELPER_PROTOCOLS.includes(protocol)) return { ok: false, reason: "unsupported", detail: reinstallMessage(protocol) };
227
+ return { ok: true, client: helperClient(socketPath, { requestTimeoutMs: options.requestTimeoutMs }), firecracker: String(reply.firecracker ?? "") };
228
+ } catch (err) {
229
+ const message = err instanceof Error ? err.message : String(err);
230
+ return { ok: false, reason: "absent", unreachable: true, detail: message.includes(socketPath) ? message : `the helper at ${socketPath} did not answer: ${message}` };
231
+ }
232
+ }
233
+ function jailedAlive(unit, cgroupRoot = "/sys/fs/cgroup") {
234
+ try {
235
+ return /^populated 1$/m.test(readFileSync(join(cgroupRoot, "system.slice", `${unit}.service`, "cgroup.events"), "utf8"));
236
+ } catch {
237
+ return false;
238
+ }
239
+ }
240
+ function realHelperPort(env = process.env) {
241
+ return { probe: () => probeHelper(env[HELPER_ENV]), alive: (unit) => jailedAlive(unit) };
242
+ }
147
243
  var BranchPrepareError = class extends Error {
148
244
  };
149
245
  var OWN_PREFIX = "outerlayer/";
@@ -353,7 +449,8 @@ function pinnedAssets(arch) {
353
449
  firecracker: {
354
450
  url: `${FIRECRACKER_RELEASES}/v${version}/firecracker-v${version}-${arch}.tgz`,
355
451
  sha256: FIRECRACKER_SHA256[arch],
356
- binarySha256: FIRECRACKER_BINARY_SHA256[arch]
452
+ binarySha256: FIRECRACKER_BINARY_SHA256[arch],
453
+ jailerSha256: JAILER_BINARY_SHA256[arch]
357
454
  },
358
455
  kernel: { url: `${GUEST_ASSETS_BASE_URL}/vmlinux-${arch}`, sha256: GUEST_KERNEL_SHA256[arch] },
359
456
  helper: { url: `${GUEST_ASSETS_BASE_URL}/outerlayer-guest-${arch}`, sha256: GUEST_HELPER_SHA256[arch] }
@@ -392,14 +489,45 @@ async function ensureFile(dir, file, pin, fetchImpl, mode) {
392
489
  writeAtomic(path, bytes, mode);
393
490
  return path;
394
491
  }
395
- function extractWithTar(arch) {
396
- const name = `firecracker-v${FIRECRACKER_VERSION}-${arch}`;
492
+ function extractWithTar(arch, program = "firecracker") {
493
+ const name = `${program}-v${FIRECRACKER_VERSION}-${arch}`;
397
494
  return async (tarball, destDir) => {
398
495
  const result = spawnSync("tar", ["-xzf", tarball, "-C", destDir, "--strip-components=1", `release-v${FIRECRACKER_VERSION}-${arch}/${name}`], { encoding: "utf8" });
399
496
  if (result.status !== 0) throw new Error(`tar could not unpack ${tarball}: ${result.stderr.trim()}`);
400
497
  return join(destDir, name);
401
498
  };
402
499
  }
500
+ async function ensureFirecracker(input) {
501
+ const { dir, arch, pins, fetchImpl } = input;
502
+ const firecracker = join(dir, "firecracker");
503
+ if (existsSync(firecracker)) {
504
+ const actual = sha256Of(readFileSync(firecracker));
505
+ if (actual !== pins.firecracker.binarySha256) throw pinProblem("firecracker", pins.firecracker.binarySha256, actual, `at ${firecracker}`);
506
+ return firecracker;
507
+ }
508
+ const tarballName = `firecracker-${arch}.tgz`;
509
+ const tarball = await ensureFile(dir, tarballName, pins.firecracker, fetchImpl, RUNNER_FILE_MODE);
510
+ const scratch = mkdtempSync(join(dir, ".extract-"));
511
+ try {
512
+ const extracted = await (input.extract ?? extractWithTar(arch))(tarball, scratch);
513
+ const bytes = readFileSync(extracted);
514
+ const actual = sha256Of(bytes);
515
+ if (actual !== pins.firecracker.binarySha256) throw pinProblem("firecracker", pins.firecracker.binarySha256, actual, `unpacked from ${tarballName}`);
516
+ writeAtomic(firecracker, bytes, 493);
517
+ } finally {
518
+ rmSync(scratch, { recursive: true, force: true });
519
+ }
520
+ return firecracker;
521
+ }
522
+ async function ensureFirecrackerAndJailer(options) {
523
+ const arch = options.arch ?? firecrackerArch(process.arch);
524
+ if (!MICROVM_ARCHES.includes(arch)) throw new AssetError("firecracker", `no Firecracker assets are pinned for ${arch}, so microVM builds run on ${MICROVM_ARCHES.join(" or ")} hosts only`);
525
+ const pins = options.pins ?? pinnedAssets(arch);
526
+ const dir = vmAssetsDir(options.runnerDir);
527
+ mkdirSync(dir, { recursive: true, mode: RUNNER_DIR_MODE });
528
+ const firecracker = await ensureFirecracker({ dir, arch, pins, fetchImpl: options.fetchImpl ?? fetch, extract: options.extractFirecracker });
529
+ return { firecracker, jailer: await ensureJailer(options) };
530
+ }
403
531
  async function ensureAssets(options) {
404
532
  const arch = options.arch ?? firecrackerArch(process.arch);
405
533
  if (!MICROVM_ARCHES.includes(arch)) throw new AssetError("firecracker", `no Firecracker or guest assets are pinned for ${arch}, so microVM builds run on ${MICROVM_ARCHES.join(" or ")} hosts only`);
@@ -407,28 +535,40 @@ async function ensureAssets(options) {
407
535
  const fetchImpl = options.fetchImpl ?? fetch;
408
536
  const dir = vmAssetsDir(options.runnerDir);
409
537
  mkdirSync(dir, { recursive: true, mode: RUNNER_DIR_MODE });
410
- const firecracker = join(dir, "firecracker");
411
- if (existsSync(firecracker)) {
412
- const actual = sha256Of(readFileSync(firecracker));
413
- if (actual !== pins.firecracker.binarySha256) throw pinProblem("firecracker", pins.firecracker.binarySha256, actual, `at ${firecracker}`);
414
- } else {
415
- const tarballName = `firecracker-${arch}.tgz`;
416
- const tarball = await ensureFile(dir, tarballName, pins.firecracker, fetchImpl, RUNNER_FILE_MODE);
417
- const scratch = mkdtempSync(join(dir, ".extract-"));
418
- try {
419
- const extracted = await (options.extractFirecracker ?? extractWithTar(arch))(tarball, scratch);
420
- const bytes = readFileSync(extracted);
421
- const actual = sha256Of(bytes);
422
- if (actual !== pins.firecracker.binarySha256) throw pinProblem("firecracker", pins.firecracker.binarySha256, actual, `unpacked from ${tarballName}`);
423
- writeAtomic(firecracker, bytes, 493);
424
- } finally {
425
- rmSync(scratch, { recursive: true, force: true });
426
- }
427
- }
538
+ const firecracker = await ensureFirecracker({ dir, arch, pins, fetchImpl, extract: options.extractFirecracker });
428
539
  const kernel = await ensureFile(dir, `vmlinux-${arch}`, pins.kernel, fetchImpl, RUNNER_FILE_MODE);
429
540
  const helper = await ensureFile(dir, `outerlayer-guest-${arch}`, pins.helper, fetchImpl, 493);
430
541
  return { firecracker, kernel, helper, helperSha256: pins.helper.sha256 };
431
542
  }
543
+ async function ensureJailer(options) {
544
+ const arch = options.arch ?? firecrackerArch(process.arch);
545
+ if (!MICROVM_ARCHES.includes(arch)) throw new AssetError("jailer", `no Firecracker assets are pinned for ${arch}, so microVM builds run on ${MICROVM_ARCHES.join(" or ")} hosts only`);
546
+ const pins = options.pins ?? pinnedAssets(arch);
547
+ const expected = pins.firecracker.jailerSha256;
548
+ if (expected === void 0) throw new AssetError("jailer", "this release pins no hash for the jailer, so it cannot be had");
549
+ const fetchImpl = options.fetchImpl ?? fetch;
550
+ const dir = vmAssetsDir(options.runnerDir);
551
+ mkdirSync(dir, { recursive: true, mode: RUNNER_DIR_MODE });
552
+ const jailer = join(dir, "jailer");
553
+ if (existsSync(jailer)) {
554
+ const actual = sha256Of(readFileSync(jailer));
555
+ if (actual !== expected) throw pinProblem("jailer", expected, actual, `at ${jailer}`);
556
+ return jailer;
557
+ }
558
+ const tarballName = `firecracker-${arch}.tgz`;
559
+ const tarball = await ensureFile(dir, tarballName, pins.firecracker, fetchImpl, RUNNER_FILE_MODE);
560
+ const scratch = mkdtempSync(join(dir, ".extract-"));
561
+ try {
562
+ const extracted = await (options.extractJailer ?? extractWithTar(arch, "jailer"))(tarball, scratch);
563
+ const bytes = readFileSync(extracted);
564
+ const actual = sha256Of(bytes);
565
+ if (actual !== expected) throw pinProblem("jailer", expected, actual, `unpacked from ${tarballName}`);
566
+ writeAtomic(jailer, bytes, 493);
567
+ } finally {
568
+ rmSync(scratch, { recursive: true, force: true });
569
+ }
570
+ return jailer;
571
+ }
432
572
  var STOP_GRACE_SECONDS = 15;
433
573
  function parseImageConfig(json) {
434
574
  const config = JSON.parse(json);
@@ -755,6 +895,7 @@ function createMicroVmPorts(over = {}) {
755
895
  rootfs: over.rootfs ?? createRootfsHost(),
756
896
  firecracker: over.firecracker ?? createFirecrackerHost(),
757
897
  cgroup: over.cgroup ?? realCgroupPort(),
898
+ helper: over.helper ?? realHelperPort(),
758
899
  exec: over.exec ?? execCapture
759
900
  };
760
901
  }
@@ -2343,6 +2484,7 @@ function createContainerRuntime(opts = {}) {
2343
2484
  tunnelOptions: opts.tunnelOptions,
2344
2485
  sleep: opts.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms))),
2345
2486
  hostEnv: opts.hostEnv ?? process.env,
2487
+ platform: opts.platform ?? process.platform,
2346
2488
  credentials: opts.credentials ?? /* @__PURE__ */ new Map(),
2347
2489
  github: { gitUrl: opts.github?.gitUrl ?? "https://github.com", apiUrl: opts.github?.apiUrl ?? "https://api.github.com" },
2348
2490
  remoteFor,
@@ -2531,8 +2673,8 @@ async function provisionContainer(input) {
2531
2673
  if (!(Number.parseInt(engine, 10) >= MIN_ENGINE_MAJOR)) {
2532
2674
  return fail(`Docker Engine ${engine} is too old: container builds need Engine ${MIN_ENGINE_MAJOR} or later`);
2533
2675
  }
2534
- if (/docker desktop/i.test(await host.operatingSystem() ?? "")) {
2535
- return fail("Docker Desktop cannot pass the tunnel's socket into a container, so container builds need Docker Engine: install it (on Windows, inside WSL2) or use builtin:process");
2676
+ if (/docker desktop/i.test(await host.operatingSystem() ?? "") && !(runnerImageVersion(runtime.hostEnv) !== void 0 && runtime.platform === "linux")) {
2677
+ return fail(DOCKER_DESKTOP_REASON);
2536
2678
  }
2537
2679
  const governing = await resolveGoverningSource({ config, repository, fetchImpl: input.fetchImpl });
2538
2680
  if (!governing.ok) return fail(governing.reason);
@@ -2587,11 +2729,16 @@ async function provisionContainer(input) {
2587
2729
  if (env.skippedTokenVariables.length > 0) {
2588
2730
  log.line(`${tag} not passing ${env.skippedTokenVariables.join(", ")} from runner.build.variables into the build: its repository token comes from the runner`);
2589
2731
  }
2732
+ const ignoredProperties = report.ignored.map(({ property, source }) => ({ property, source }));
2733
+ if (report.docker) {
2734
+ ignoredProperties.push({ property: "docker daemon", source: `feature ${DOCKER_FEATURE}` });
2735
+ log.line(`${tag} container builds have no Docker daemon, so steps that run Docker will fail`);
2736
+ }
2590
2737
  const environment = {
2591
2738
  isolation: "container",
2592
2739
  recipe: { sourceCommit: report.sourceCommit, key: report.recipeKey, path: report.recipe },
2593
2740
  imageId: report.imageId,
2594
- ignoredProperties: report.ignored.map(({ property, source }) => ({ property, source })),
2741
+ ignoredProperties,
2595
2742
  variables: env.variables,
2596
2743
  cliVersion: input.cliVersion
2597
2744
  };
@@ -3157,4 +3304,4 @@ async function startCheckContainer(input) {
3157
3304
  };
3158
3305
  }
3159
3306
 
3160
- export { ADDED_USER, AgentUserError, AssetError, DeliveryFailure, EVAL_INPUT_NAME, GH_CONFIG_YML, LEASE_SECONDS, MIN_ENGINE_MAJOR, cleanupContainer, containerCommandExec, containerJudgeEnvironment, containerSenderCommand, copyJudgeTree, copyTranscripts, copyTranscriptsFromContainer, createBuildDisk, createContainerRuntime, deleteBuildDisk, deliverContainerHooks, ensureRootfs, ensureTunnel, filesAreCopied, ghHostsYml, gitRemoteFor, isLiteralAddress, keepGhConfigCurrent, leaseExpiresAtMs, lifecycleFailureReason, lifecycleSteps, machineConfig, measureDisk, obtainImage, outOfMemoryReason, parseCpuList, prepareBranch, provisionContainer, resolveAgentUser, runBuildSteps, senderAliveScript, senderSignalScript, setUpRunnerCgroup, shellQuote, signalInContainer, startCheckContainer, startServicesFor, stopContainer, syncInContainer, writeEvalInput };
3307
+ 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, deleteBuildDisk, deliverContainerHooks, ensureFirecrackerAndJailer, ensureRootfs, ensureTunnel, filesAreCopied, ghHostsYml, gitRemoteFor, helperSocketPath, isLiteralAddress, keepGhConfigCurrent, leaseExpiresAtMs, lifecycleFailureReason, lifecycleSteps, machineConfig, measureDisk, obtainImage, outOfMemoryReason, parseCpuList, prepareBranch, probeHelper, provisionContainer, resolveAgentUser, runBuildSteps, senderAliveScript, senderSignalScript, setUpRunnerCgroup, shellQuote, signalInContainer, startCheckContainer, startServicesFor, stopContainer, syncInContainer, writeEvalInput };