@outerlayer/cli 0.34.0-next.20261026232906 → 0.34.0-next.20261027023105

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 (50) hide show
  1. package/README.md +4 -3
  2. package/dist/{build-M4UYZAFH.js → build-FS7TQEJ6.js} +4 -3
  3. package/dist/{build-disk-ZDVSSHO2.js → build-disk-RZ7AZKTY.js} +3 -3
  4. package/dist/build-info.json +1 -1
  5. package/dist/{builder-IORYS76D.js → builder-MQIL24HI.js} +2 -2
  6. package/dist/{check-V66LZDNK.js → check-YIUZ3UX3.js} +15 -14
  7. package/dist/{chunk-T7HUNNJ2.js → chunk-36U7LY7C.js} +3 -3
  8. package/dist/{chunk-MIRCFK4X.js → chunk-7PCA37ZS.js} +1 -1
  9. package/dist/{chunk-TLIZ7FY4.js → chunk-7QF2EXAN.js} +2 -2
  10. package/dist/{chunk-6NXBXWOP.js → chunk-BLSEHKZ4.js} +1 -1
  11. package/dist/{chunk-Q3L5AC75.js → chunk-DEJCZ43H.js} +3 -3
  12. package/dist/{chunk-TGBST2QL.js → chunk-DX4FF5P5.js} +6 -6
  13. package/dist/{chunk-JZLMFYFM.js → chunk-FQGC22IP.js} +1 -1
  14. package/dist/{chunk-KOZ6IBEI.js → chunk-GR7ZVGNI.js} +1 -1
  15. package/dist/chunk-GTBY7BTI.js +301 -0
  16. package/dist/{chunk-XDJ376OP.js → chunk-IYQPUGST.js} +5 -169
  17. package/dist/{chunk-F5H3LVHR.js → chunk-JL4GSPGD.js} +2 -2
  18. package/dist/{chunk-DFBY6LX3.js → chunk-LKTH7PUH.js} +10 -7
  19. package/dist/{chunk-63A6NUEZ.js → chunk-Q2KGDDUX.js} +1 -1
  20. package/dist/{chunk-4UPET3R7.js → chunk-QUOITPN7.js} +21 -2
  21. package/dist/{chunk-T6HSNQAT.js → chunk-SSRB5HEL.js} +4 -4
  22. package/dist/{chunk-IZVDQXTC.js → chunk-UWDFA6WW.js} +9 -9
  23. package/dist/{chunk-PN7RMFPP.js → chunk-WPDL7MBC.js} +2 -2
  24. package/dist/{chunk-XLJXO4QC.js → chunk-ZK3A4W3I.js} +5 -5
  25. package/dist/{cli-HQWQEHXJ.js → cli-MP5AUR36.js} +45 -45
  26. package/dist/{command-TONUMZ2A.js → command-RGBJLW72.js} +9 -8
  27. package/dist/{config-4K6OG2SZ.js → config-NGCQD6SN.js} +4 -4
  28. package/dist/{container-start-R74IO7CI.js → container-start-KT2KKAQX.js} +12 -11
  29. package/dist/{demo-record-cmd-7HJ5C7KM.js → demo-record-cmd-BUNOOCNM.js} +10 -1
  30. package/dist/{demo-voice-cmd-MBK3BGHZ.js → demo-voice-cmd-RWTRJUAX.js} +2 -2
  31. package/dist/{docker-XWQM4F7B.js → docker-TRGA5QRL.js} +2 -2
  32. package/dist/{doctor-SF4GAWAO.js → doctor-NEO7NXJJ.js} +10 -9
  33. package/dist/{emit-demo-cmd-6VTA5BSJ.js → emit-demo-cmd-QMRDQGB6.js} +2 -2
  34. package/dist/{find-unit-I4GWKY4T.js → find-unit-IOCVYPNW.js} +6 -6
  35. package/dist/{host-checks-FKLQST3J.js → host-checks-JH2ILNZT.js} +10 -9
  36. package/dist/index.js +1 -1
  37. package/dist/{init-3MO5WCU3.js → init-HGJQW2NL.js} +9 -8
  38. package/dist/{init-TDLUTI6F.js → init-WGVNY4NP.js} +9 -8
  39. package/dist/{init-cmd-MQ5R35O2.js → init-cmd-L2SG2WSN.js} +1 -1
  40. package/dist/{loop-NNNDBFAS.js → loop-F6E6PGWG.js} +14 -13
  41. package/dist/{machine-GCW2GGI2.js → machine-GYTMJSIY.js} +9 -8
  42. package/dist/{runner-env-LVVLUHY2.js → runner-env-DH7YA4BU.js} +6 -6
  43. package/dist/{runner-identity-73F2IWBT.js → runner-identity-GLL56XY7.js} +2 -2
  44. package/dist/{runner-key-ASPZ7TQH.js → runner-key-HMXSBRBE.js} +9 -8
  45. package/dist/{status-35Z3TW42.js → status-QNKTP6OU.js} +10 -9
  46. package/dist/{update-steps-ZTNIK7LA.js → update-steps-M2UKG4QZ.js} +11 -10
  47. package/dist/{work-claim-cmd-QPYCZUMI.js → work-claim-cmd-JF5GGDFZ.js} +4 -4
  48. package/dist/{work-proofs-cmd-56EY7O45.js → work-proofs-cmd-A7SZSM2A.js} +1 -1
  49. package/dist/{work-proofs-record-SPGI47OS.js → work-proofs-record-YG6HD6SJ.js} +3 -3
  50. package/package.json +1 -1
package/README.md CHANGED
@@ -234,7 +234,8 @@ content:
234
234
  from GitHub, a pinned static `ffmpeg` build from GitHub, and the narration
235
235
  voice pack from Hugging Face and the npm registry, checking each against a
236
236
  digest. It also installs the CLI's pinned Playwright release from the npm
237
- registry, with no browser. The image build runs outside
237
+ registry, and that release's headless Chromium and video encoder from
238
+ cdn.playwright.dev, checked against a digest. The image build runs outside
238
239
  any work item and sends nothing to your factory. `outerlayer runner start`
239
240
  builds each build's image the same way, before the build starts. That image
240
241
  build uses the host's network: the build's tunnel does not carry it, and
@@ -251,8 +252,8 @@ content:
251
252
  the host. An image build in a builder microVM also needs the files of the
252
253
  runner's tools layer, which the runner downloads on the host. It gets `gh`,
253
254
  `tini` and FFmpeg from github.com. It gets the narration voice pack from
254
- huggingface.co and registry.npmjs.org. Each file is checked against a sha256
255
- pinned in the CLI. The microVM reaches the network only
255
+ huggingface.co and registry.npmjs.org, and the demo browser from
256
+ cdn.playwright.dev. Each file is checked against a sha256 pinned in the CLI. The microVM reaches the network only
256
257
  through the same tunnel a container build uses, which the runner serves on
257
258
  Unix sockets and the guest reaches over vsock. In a build with Docker, the
258
259
  guest forwards the proxy connections of containers and `docker build` steps
@@ -1,9 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- export { KEY_PREFIX, TAG_HOST, buildRepositoryImage, metadataFromLabels, prepareImageBuild, writeRecord } from './chunk-XDJ376OP.js';
4
- import './chunk-KOZ6IBEI.js';
5
- import './chunk-4UPET3R7.js';
3
+ export { KEY_PREFIX, TAG_HOST, buildRepositoryImage, metadataFromLabels, prepareImageBuild, writeRecord } from './chunk-IYQPUGST.js';
4
+ import './chunk-GTBY7BTI.js';
5
+ import './chunk-GR7ZVGNI.js';
6
6
  export { RecipeError } from './chunk-HDLON3RJ.js';
7
+ import './chunk-QUOITPN7.js';
7
8
  import './chunk-LQHCNXPK.js';
8
9
  import './chunk-JBJTC3UU.js';
9
10
  import './chunk-L5VHGRQE.js';
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- export { DEFAULT_BUILD_DISK, DEFAULT_CACHE_DISK, DOCKER_CACHE_MS, allocatedBytes, checkDiskFit, realBuildDiskProbe, runDocker, volumeBytes } from './chunk-6NXBXWOP.js';
4
- import './chunk-KOZ6IBEI.js';
5
- import './chunk-4UPET3R7.js';
3
+ export { DEFAULT_BUILD_DISK, DEFAULT_CACHE_DISK, DOCKER_CACHE_MS, allocatedBytes, checkDiskFit, realBuildDiskProbe, runDocker, volumeBytes } from './chunk-BLSEHKZ4.js';
4
+ import './chunk-GR7ZVGNI.js';
6
5
  import './chunk-HDLON3RJ.js';
6
+ import './chunk-QUOITPN7.js';
7
7
  import './chunk-L5VHGRQE.js';
8
8
  import './chunk-OOD5NDQA.js';
9
9
  import './chunk-DINRXUQA.js';
@@ -1 +1 @@
1
- {"id":"2026-10-26T23:31:14.294Z+386d50a","root":"/home/runner/_work/outerlayer/outerlayer","pkg":"outerlayer"}
1
+ {"id":"2026-10-27T02:32:25.418Z+10e9a05","root":"/home/runner/_work/outerlayer/outerlayer","pkg":"outerlayer"}
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- export { MIN_BUILDX_VERSION, builderName, builderSpec, buildxState, ensureBuilder, ensureFailureCount, isOutOfMemory, proxyEnvironment, trackImageBuild } from './chunk-KOZ6IBEI.js';
4
- import './chunk-4UPET3R7.js';
3
+ export { MIN_BUILDX_VERSION, builderName, builderSpec, buildxState, ensureBuilder, ensureFailureCount, isOutOfMemory, proxyEnvironment, trackImageBuild } from './chunk-GR7ZVGNI.js';
5
4
  import './chunk-HDLON3RJ.js';
5
+ import './chunk-QUOITPN7.js';
6
6
  import './chunk-L5VHGRQE.js';
7
7
  import './chunk-OOD5NDQA.js';
8
8
  import './chunk-DINRXUQA.js';
@@ -1,14 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- import { probeCredentials } from './chunk-IZVDQXTC.js';
3
+ import { probeCredentials } from './chunk-UWDFA6WW.js';
4
4
  import './chunk-3JVYMD4G.js';
5
- import './chunk-F5H3LVHR.js';
5
+ import './chunk-JL4GSPGD.js';
6
6
  import './chunk-E7R2GIAW.js';
7
7
  import './chunk-MH6QLXUO.js';
8
8
  import './chunk-HOUFCUMH.js';
9
- import './chunk-PN7RMFPP.js';
9
+ import './chunk-WPDL7MBC.js';
10
10
  import './chunk-TS3EFB7S.js';
11
- import './chunk-TGBST2QL.js';
11
+ import './chunk-DX4FF5P5.js';
12
12
  import './chunk-6CK2OC4Q.js';
13
13
  import './chunk-ZVB57WI6.js';
14
14
  import './chunk-P3NPTFLF.js';
@@ -18,27 +18,28 @@ import './chunk-QRJIBTNQ.js';
18
18
  import './chunk-7DK2FOZ4.js';
19
19
  import './chunk-ESVW4MJY.js';
20
20
  import './chunk-CFFVCI2I.js';
21
- import { managedProblems, managedFactsOf } from './chunk-XLJXO4QC.js';
21
+ import { managedProblems, managedFactsOf } from './chunk-ZK3A4W3I.js';
22
22
  import './chunk-YL3RWW7X.js';
23
23
  import './chunk-TBT347UY.js';
24
- import { findServiceUnit } from './chunk-JZLMFYFM.js';
24
+ import { findServiceUnit } from './chunk-FQGC22IP.js';
25
25
  import './chunk-TZMDRHWB.js';
26
- import { placeholderQueues, placeholderProblem, permissionModeProblems, permissionModeProblem, HELPER_ENV, helperSocketPath, probeHelper, needsClaudeCredential, claudeCredentialCheck, claimPermissionProblem, formerSkillQueues, formerSkillProblem } from './chunk-DFBY6LX3.js';
26
+ import { placeholderQueues, placeholderProblem, permissionModeProblems, permissionModeProblem, HELPER_ENV, helperSocketPath, probeHelper, needsClaudeCredential, claudeCredentialCheck, claimPermissionProblem, formerSkillQueues, formerSkillProblem } from './chunk-LKTH7PUH.js';
27
27
  import './chunk-2PGI6RDE.js';
28
- import './chunk-XDJ376OP.js';
28
+ import './chunk-IYQPUGST.js';
29
+ import './chunk-GTBY7BTI.js';
29
30
  import './chunk-OGFGZQA3.js';
30
31
  import './chunk-3VZTNXK5.js';
31
32
  import './chunk-6KFJIU73.js';
32
33
  import './chunk-77VBS2RT.js';
33
34
  import './chunk-SDELQEG4.js';
34
35
  import './chunk-TQMI335S.js';
35
- import './chunk-63A6NUEZ.js';
36
- import './chunk-TLIZ7FY4.js';
37
- import { loadRunnerConfig, RunnerConfigError, checkHooks } from './chunk-Q3L5AC75.js';
38
- import './chunk-6NXBXWOP.js';
39
- import './chunk-KOZ6IBEI.js';
40
- import './chunk-4UPET3R7.js';
36
+ import './chunk-Q2KGDDUX.js';
37
+ import './chunk-7QF2EXAN.js';
38
+ import { loadRunnerConfig, RunnerConfigError, checkHooks } from './chunk-DEJCZ43H.js';
39
+ import './chunk-BLSEHKZ4.js';
40
+ import './chunk-GR7ZVGNI.js';
41
41
  import './chunk-HDLON3RJ.js';
42
+ import './chunk-QUOITPN7.js';
42
43
  import './chunk-LQHCNXPK.js';
43
44
  import './chunk-JBJTC3UU.js';
44
45
  import './chunk-KT4CN6OU.js';
@@ -167,14 +167,14 @@ function governingDir(cwd) {
167
167
  async function testProofMode(cwd) {
168
168
  const dir = governingDir(cwd);
169
169
  if (dir === void 0) return "any";
170
- const { testProofIn } = await import('./host-checks-FKLQST3J.js');
170
+ const { testProofIn } = await import('./host-checks-JH2ILNZT.js');
171
171
  return testProofIn(dir);
172
172
  }
173
173
  async function judgeCheckName(cwd) {
174
174
  const dir = governingDir(cwd);
175
175
  if (dir === void 0) return {};
176
176
  if (!existsSync(dir)) return { note: `the governed context at ${dir} is not readable` };
177
- const { checksIn, isJudgeCheck } = await import('./host-checks-FKLQST3J.js');
177
+ const { checksIn, isJudgeCheck } = await import('./host-checks-JH2ILNZT.js');
178
178
  const quiet = { line: () => void 0, error: () => void 0 };
179
179
  const check = checksIn(dir, quiet, "").checks.find((c) => isJudgeCheck(c) && c.judge.subjects === "criteria");
180
180
  return check === void 0 ? {} : { name: check.name };
@@ -314,7 +314,7 @@ async function runWorkProofs(opts) {
314
314
  async function runWorkProofsCommand(opts) {
315
315
  try {
316
316
  if (opts.record) {
317
- const { runWorkProofsRecord } = await import('./work-proofs-record-SPGI47OS.js');
317
+ const { runWorkProofsRecord } = await import('./work-proofs-record-YG6HD6SJ.js');
318
318
  const result2 = await runWorkProofsRecord(opts);
319
319
  process.exitCode = result2.exitCode;
320
320
  return;
@@ -3,7 +3,7 @@ import { createRequire } from 'node:module';
3
3
  import { readNewestDemo } from './chunk-ER6KQCVJ.js';
4
4
  import { runEmitArtifact } from './chunk-QWXBWOVV.js';
5
5
  import { VoiceDownloadError, downloadPinned, sha256OfFile } from './chunk-2PGI6RDE.js';
6
- import { VOICE_PACK_FILES, VOICE_PACK_VERSION } from './chunk-4UPET3R7.js';
6
+ import { VOICE_PACK_FILES, VOICE_PACK_VERSION } from './chunk-QUOITPN7.js';
7
7
  import { detectActiveSession } from './chunk-IP7BIBXJ.js';
8
8
  import { autoConnect } from './chunk-OKGHWCSK.js';
9
9
  import { C } from './chunk-ANPNT6PM.js';
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- import { builtinKind, keepsHostBuilder } from './chunk-Q3L5AC75.js';
4
- import { parseSize } from './chunk-KOZ6IBEI.js';
3
+ import { builtinKind, keepsHostBuilder } from './chunk-DEJCZ43H.js';
4
+ import { parseSize } from './chunk-GR7ZVGNI.js';
5
5
  import { runnerDir } from './chunk-GJ4KBHBD.js';
6
6
  import { FD_SLOTS_PER_BUILD, FD_STORE_ENV, BUILD_SLICE_MEMORY_ENV, CLAUDE_CREDENTIAL_ID, CLAUDE_ENV_FILE, credentialLineIn, withoutCredentialLines } from './chunk-SDAEXUIX.js';
7
7
  import { existsSync, readFileSync, writeFileSync, chmodSync, renameSync } from 'fs';
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- import { parseSize, BUILD_LABEL, RUNNER_LABEL, runnerLabelValue } from './chunk-KOZ6IBEI.js';
3
+ import { parseSize, BUILD_LABEL, RUNNER_LABEL, runnerLabelValue } from './chunk-GR7ZVGNI.js';
4
4
  import { buildsDir, rootfsDir, runnerDir, imagesDir, builderCacheDiskPath } from './chunk-GJ4KBHBD.js';
5
5
  import { spawnSync } from 'child_process';
6
6
  import { statfsSync, statSync, existsSync, readdirSync } from 'fs';
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- import { DEFAULT_CACHE_DISK, DEFAULT_BUILD_DISK } from './chunk-6NXBXWOP.js';
4
- import { parseSize } from './chunk-KOZ6IBEI.js';
5
- import { builderDiskPinned } from './chunk-4UPET3R7.js';
3
+ import { DEFAULT_CACHE_DISK, DEFAULT_BUILD_DISK } from './chunk-BLSEHKZ4.js';
4
+ import { parseSize } from './chunk-GR7ZVGNI.js';
5
+ import { builderDiskPinned } from './chunk-QUOITPN7.js';
6
6
  import { homeFromEnv } from './chunk-VNVZDWO3.js';
7
7
  import { isRefusedInConfig, resolveDestinations, isReservedVariable } from './chunk-SDAEXUIX.js';
8
8
  import { existsSync, statSync, accessSync, constants, readFileSync } from 'fs';
@@ -8,15 +8,15 @@ import { senderPidfile } from './chunk-WBTPI5MP.js';
8
8
  import { installScrubContext } from './chunk-QRJIBTNQ.js';
9
9
  import { writeDockerClientConfig } from './chunk-CFFVCI2I.js';
10
10
  import { upgradedStarterText } from './chunk-TZMDRHWB.js';
11
- import { ADDED_USER, shellQuote, GH_CONFIG_YML, ghHostsYml, hasClaudeCredential, AssetError, obtainImage, ensureRootfs, makeBuildsDirPrivate, gitRemoteFor, startServicesFor, createLeaseChannel, createBuildDisk, machineConfig, startJailedInto, resolveAgentUser, AgentUserError, runBuildSteps, createContainerRuntime, containerSenderCommand, hostCommandEnvironment, execArgv, signalInContainer, startHostServices, LEASE_SECONDS, managedBuildSettingsFor, stopContainer, cleanupContainer, outOfMemoryReason, closeHostBuild, dropManagedClaudeKey, dropManagedBuildSettings, startCheckContainer, lifecycleSteps, lifecycleFailureReason, writeEvalInput, DeliveryFailure, EVAL_INPUT_NAME, syncInContainer, copyTranscriptsFromContainer, MANAGED_UNJAILED_REASON, senderAliveScript, managedMachineLimits, withFactoryHosts, helperFirecrackerNote, managedClaudeKeyValues, managedBuildSettingValues, stopJailed, deleteBuildDisk, managedClaudeKeyFor, ROOT_DISK_ID_PREFIX, copyTranscripts, containerJudgeEnvironment, senderSignalScript, copyJudgeTree } from './chunk-DFBY6LX3.js';
12
- import { fetchCommit, readBranchHead, diffBetween } from './chunk-XDJ376OP.js';
11
+ import { ADDED_USER, shellQuote, GH_CONFIG_YML, ghHostsYml, hasClaudeCredential, AssetError, obtainImage, ensureRootfs, makeBuildsDirPrivate, gitRemoteFor, startServicesFor, createLeaseChannel, createBuildDisk, machineConfig, startJailedInto, resolveAgentUser, AgentUserError, runBuildSteps, createContainerRuntime, containerSenderCommand, hostCommandEnvironment, execArgv, signalInContainer, startHostServices, LEASE_SECONDS, managedBuildSettingsFor, stopContainer, cleanupContainer, outOfMemoryReason, closeHostBuild, dropManagedClaudeKey, dropManagedBuildSettings, startCheckContainer, lifecycleSteps, lifecycleFailureReason, writeEvalInput, DeliveryFailure, EVAL_INPUT_NAME, syncInContainer, copyTranscriptsFromContainer, MANAGED_UNJAILED_REASON, senderAliveScript, managedMachineLimits, withFactoryHosts, helperFirecrackerNote, managedClaudeKeyValues, managedBuildSettingValues, stopJailed, deleteBuildDisk, managedClaudeKeyFor, ROOT_DISK_ID_PREFIX, copyTranscripts, containerJudgeEnvironment, senderSignalScript, copyJudgeTree } from './chunk-LKTH7PUH.js';
12
+ import { fetchCommit, readBranchHead, diffBetween } from './chunk-IYQPUGST.js';
13
13
  import { waitForAgent, vmExecArgs } from './chunk-3VZTNXK5.js';
14
14
  import { socketPathProblem, vsockLinks, LEASE_LINK, linkVsockSockets } from './chunk-6KFJIU73.js';
15
15
  import { describeCallFailure, resolveGoverningSource, prefetchGovernedContext, refusalCode, removeStagedContext, stageGovernedContext, pinnedSourceOf, commandWithShim, logStamp, createRunnerLog, removeKeptContext, formatDuration, retrySend, formatClock, keptContextTree, WORKFLOW_PERMISSION_REASON } from './chunk-77VBS2RT.js';
16
- import { imageBuildOf, isBuiltinContainer, isBuiltinProcess, isBuiltinVm, DEFAULT_CHECK_TIME_LIMIT_MINUTES } from './chunk-Q3L5AC75.js';
17
- import { formatBytes, DEFAULT_CACHE_DISK, allocatedBytes } from './chunk-6NXBXWOP.js';
18
- import { HASH_ARGV, parseProgramHash, JUDGE_PROGRAM, writeJobFile, readJobFile, parseSize, childEnvironment, withAnswerProtocol, judgeCommand, MAX_REPORT_BYTES, JUDGE_MAX_TURNS, JUDGE_DIR, COMMAND_DIR, MAX_ITEMS_BYTES } from './chunk-KOZ6IBEI.js';
19
- import { CLAUDE_CODE_VERSION, FIRECRACKER_VERSION } from './chunk-4UPET3R7.js';
16
+ import { imageBuildOf, isBuiltinContainer, isBuiltinProcess, isBuiltinVm, DEFAULT_CHECK_TIME_LIMIT_MINUTES } from './chunk-DEJCZ43H.js';
17
+ import { formatBytes, DEFAULT_CACHE_DISK, allocatedBytes } from './chunk-BLSEHKZ4.js';
18
+ import { HASH_ARGV, parseProgramHash, JUDGE_PROGRAM, writeJobFile, readJobFile, parseSize, childEnvironment, withAnswerProtocol, judgeCommand, MAX_REPORT_BYTES, JUDGE_MAX_TURNS, JUDGE_DIR, COMMAND_DIR, MAX_ITEMS_BYTES } from './chunk-GR7ZVGNI.js';
19
+ import { CLAUDE_CODE_VERSION, FIRECRACKER_VERSION } from './chunk-QUOITPN7.js';
20
20
  import { isSameProcess, startTimeOf } from './chunk-JBJTC3UU.js';
21
21
  import { WorkCommandError, postJson, isSignatureRefusal, getJson } from './chunk-QABXXQ67.js';
22
22
  import { planArtifactUpload } from './chunk-IXPH4EXK.js';
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- import { escapeUnitPath, UNIT_MARKER, DEFAULT_UNIT_NAME } from './chunk-TLIZ7FY4.js';
3
+ import { escapeUnitPath, UNIT_MARKER, DEFAULT_UNIT_NAME } from './chunk-7QF2EXAN.js';
4
4
  import { inspectService, realInspectProbes } from './chunk-SDAEXUIX.js';
5
5
  import { realpathSync, readFileSync, readdirSync } from 'fs';
6
6
  import { resolve, dirname, join } from 'path';
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- import { BUILDKIT_IMAGE } from './chunk-4UPET3R7.js';
4
3
  import { resolveDevcontainerCli, devcontainerBuildInvocation, RecipeError } from './chunk-HDLON3RJ.js';
4
+ import { BUILDKIT_IMAGE } from './chunk-QUOITPN7.js';
5
5
  import { ensureHostKey } from './chunk-L5VHGRQE.js';
6
6
  import { compareVersions } from './chunk-OOD5NDQA.js';
7
7
  import { RUNNER_DIR_MODE, RUNNER_FILE_MODE, runnerDir, jobsDir } from './chunk-GJ4KBHBD.js';
@@ -0,0 +1,301 @@
1
+ #!/usr/bin/env node
2
+ import { createRequire } from 'node:module';
3
+ import { GH_VERSION, VOICE_PACK_FILES, VOICE_PACK_VERSION, FFMPEG_STATIC, FFMPEG_BUILD_TAG, GH_SHA256, TINI_SHA256, TINI_VERSION, HEADLESS_SHELL_REVISION, HEADLESS_SHELL_BASE_URL, PLAYWRIGHT_FFMPEG_BASE_URL, PLAYWRIGHT_FFMPEG_REVISION, HEADLESS_SHELL, PLAYWRIGHT_FFMPEG, PLAYWRIGHT_VERSION } from './chunk-QUOITPN7.js';
4
+ import { PROXY_WARNING_FLAG } from './chunk-SDAEXUIX.js';
5
+ import { RELAY_ID, RELAY_USER } from './chunk-MESOOLF4.js';
6
+
7
+ createRequire(import.meta.url);
8
+
9
+ // src/runner/image/dockerfiles.ts
10
+ var FETCHED_DIR = "fetched";
11
+ var FETCHED_VOICE_DIR = "voice";
12
+ var FETCHED_FFMPEG = "ffmpeg.tar.xz";
13
+ var FETCHED_GH = "gh.tgz";
14
+ var FETCHED_TINI = "tini";
15
+ var FETCHED_HEADLESS_SHELL = "headless-shell.zip";
16
+ var FETCHED_PLAYWRIGHT_FFMPEG = "playwright-ffmpeg.zip";
17
+ var IMAGE_BROWSER = {
18
+ dir: "/opt/outerlayer/browser",
19
+ playwright: "/opt/outerlayer/browser/playwright",
20
+ fontConfig: "/opt/outerlayer/browser/fonts.conf"
21
+ };
22
+ var FALLBACK_BIN = "/opt/outerlayer/fallback/bin";
23
+ var IMAGE_GIT = "/opt/outerlayer/git";
24
+ var CA_BUNDLE = "/opt/outerlayer/certs/ca-certificates.crt";
25
+ function findCaBundle() {
26
+ const bundles = ["/etc/ssl/certs/ca-certificates.crt", "/etc/pki/tls/certs/ca-bundle.crt", "/etc/ssl/ca-bundle.pem", "/etc/ssl/cert.pem", CA_BUNDLE];
27
+ return `for ca in ${bundles.join(" ")}; do [ -f "$ca" ] && break; done`;
28
+ }
29
+ function plain(name, value, allowed) {
30
+ if (!allowed.test(value)) throw new Error(`${name} holds a character a Dockerfile line cannot carry safely: ${JSON.stringify(value)}`);
31
+ return value;
32
+ }
33
+ var DEFAULT_PACKAGES = ["git", "ca-certificates", "curl", "build-essential", "python3", "python3-pip", "python3-venv", "unzip", "jq"];
34
+ function defaultDockerfile(baseImage, nodeImage) {
35
+ return [
36
+ ubuntuWithNode(baseImage, nodeImage, DEFAULT_PACKAGES),
37
+ // The browsers live in one shared folder so any user finds them. A build container's root filesystem is
38
+ // read-only, so the build user cannot add to this folder: a repository that needs another Playwright version
39
+ // installs that version's Chromium into a writable folder by setting PLAYWRIGHT_BROWSERS_PATH.
40
+ // `install --with-deps` runs apt-get as root and works on amd64 and arm64.
41
+ "ENV PLAYWRIGHT_BROWSERS_PATH=/ms-playwright",
42
+ "RUN mkdir -p /ms-playwright \\",
43
+ ` && npx -y playwright@${plain("the Playwright version", PLAYWRIGHT_VERSION, /^[0-9.]+$/)} install --with-deps chromium \\`,
44
+ " && chmod -R a+rX /ms-playwright \\",
45
+ " && rm -rf /var/lib/apt/lists/* /root/.npm",
46
+ ""
47
+ ].join("\n");
48
+ }
49
+ function ubuntuWithNode(baseImage, nodeImage, packages) {
50
+ return [
51
+ `FROM ${baseImage}`,
52
+ "ENV DEBIAN_FRONTEND=noninteractive",
53
+ "RUN apt-get update \\",
54
+ ` && apt-get install -y --no-install-recommends ${packages.join(" ")} \\`,
55
+ " && rm -rf /var/lib/apt/lists/*",
56
+ `COPY --from=${nodeImage} /usr/local/bin/node /usr/local/bin/node`,
57
+ `COPY --from=${nodeImage} /usr/local/lib/node_modules /usr/local/lib/node_modules`,
58
+ "RUN ln -s ../lib/node_modules/npm/bin/npm-cli.js /usr/local/bin/npm \\",
59
+ " && ln -s ../lib/node_modules/npm/bin/npx-cli.js /usr/local/bin/npx \\",
60
+ " && ln -s ../lib/node_modules/corepack/dist/corepack.js /usr/local/bin/corepack",
61
+ ""
62
+ ].join("\n");
63
+ }
64
+ function toolsDockerfile(parts) {
65
+ const recipeImage = plain("the recipe image", parts.recipeImage, /^[A-Za-z0-9_.:@/-]+$/);
66
+ const nodeImage = plain("the Node image", parts.nodeImage, /^[A-Za-z0-9_.:@/-]+$/);
67
+ const version = plain("the Claude Code version", parts.claudeCodeVersion, /^[A-Za-z0-9.+-]+$/);
68
+ const buildId = plain("the build id", parts.buildId, /^[A-Za-z0-9_.:+-]+$/);
69
+ const tools = JSON.stringify({ outerlayerBuildId: buildId, claudeCodeVersion: version, ghVersion: GH_VERSION });
70
+ const managedSettings = JSON.stringify({
71
+ hooks: Object.fromEntries(
72
+ ["SessionStart", "SessionEnd", "Stop", "PostToolUse"].map((event) => [event, [{ hooks: [{ type: "command", command: `outerlayer hook ${event}` }] }]])
73
+ )
74
+ });
75
+ const download = `NODE_USE_ENV_PROXY=1 node ${PROXY_WARNING_FLAG} -e 'fetch(process.argv[1]).then((r)=>{if(!r.ok)throw new Error(process.argv[1]+" "+r.status);return r.arrayBuffer()}).then((b)=>require("fs").writeFileSync(process.argv[2],Buffer.from(b)))'`;
76
+ const stream = `NODE_USE_ENV_PROXY=1 node ${PROXY_WARNING_FLAG} -e 'const{pipeline}=require("stream/promises"),{Readable}=require("stream"),fs=require("fs");fetch(process.argv[1]).then((r)=>{if(!r.ok)throw new Error(process.argv[1]+" "+r.status);return pipeline(Readable.fromWeb(r.body),fs.createWriteStream(process.argv[2]))})'`;
77
+ const voiceDir = `/out/opt/outerlayer/voice/${VOICE_PACK_VERSION}`;
78
+ const checks = VOICE_PACK_FILES.map((file) => `echo "${file.sha256} ${voiceDir}/${file.name}" | sha256sum -c -`);
79
+ const fetched = parts.fetched === true;
80
+ const voice = fetched ? [`FROM ${nodeImage} AS voice`, `COPY ${FETCHED_DIR}/${FETCHED_VOICE_DIR}/ ${voiceDir}/`, `RUN ${checks.join(" \\\n && ")} \\`, " && chmod -R a+rX /out"] : [
81
+ // The voice pack streams to disk, one pinned file at a time, because a 300 MB model must not sit in a build's memory. Its own stage keeps it cached when the CLI changes.
82
+ `FROM ${nodeImage} AS voice`,
83
+ `RUN mkdir -p ${voiceDir} \\`,
84
+ ...VOICE_PACK_FILES.map((file) => ` && ${stream} ${file.url} ${voiceDir}/${file.name} \\`),
85
+ ...checks.map((check) => ` && ${check} \\`),
86
+ " && chmod -R a+rX /out"
87
+ ];
88
+ return [
89
+ ...voice,
90
+ // ffmpeg and ffprobe are static LGPL builds. espeak-ng comes from the stage image's own package, with the libraries and loader it needs, and runs as a separate program.
91
+ `FROM ${nodeImage} AS media`,
92
+ "ARG TARGETARCH",
93
+ "RUN apt-get update \\",
94
+ " && apt-get install -y --no-install-recommends ca-certificates espeak-ng xz-utils \\",
95
+ " && rm -rf /var/lib/apt/lists/*",
96
+ ...fetched ? [`COPY ${FETCHED_DIR}/${FETCHED_FFMPEG} /tmp/ffmpeg.tar.xz`] : [],
97
+ "RUN set -eu \\",
98
+ ` && case "$TARGETARCH" in \\`,
99
+ ` amd64) ffmpeg_file=${FFMPEG_STATIC.amd64.file}; ffmpeg_sha=${FFMPEG_STATIC.amd64.sha256} ;; \\`,
100
+ ` arm64) ffmpeg_file=${FFMPEG_STATIC.arm64.file}; ffmpeg_sha=${FFMPEG_STATIC.arm64.sha256} ;; \\`,
101
+ ` *) echo "no pinned ffmpeg for $TARGETARCH" >&2; exit 1 ;; \\`,
102
+ " esac \\",
103
+ ...fetched ? [] : [` && ${download} https://github.com/BtbN/FFmpeg-Builds/releases/download/${FFMPEG_BUILD_TAG}/$ffmpeg_file /tmp/ffmpeg.tar.xz \\`],
104
+ ' && echo "$ffmpeg_sha /tmp/ffmpeg.tar.xz" | sha256sum -c - \\',
105
+ " && tar -xJf /tmp/ffmpeg.tar.xz -C /tmp \\",
106
+ " && mkdir -p /out/usr/local/bin /out/opt/outerlayer/media/lib \\",
107
+ " && install -m 0755 /tmp/ffmpeg-*/bin/ffmpeg /tmp/ffmpeg-*/bin/ffprobe /out/usr/local/bin/ \\",
108
+ " && install -m 0755 /usr/bin/espeak-ng /out/opt/outerlayer/media/espeak-ng \\",
109
+ ` && for lib in $(ldd /usr/bin/espeak-ng | awk '$3 ~ /^\\// {print $3}'); do cp -L "$lib" /out/opt/outerlayer/media/lib/; done \\`,
110
+ " && cp -L $(ldd /usr/bin/espeak-ng | awk '$1 ~ /ld-linux/ {print $1}') /out/opt/outerlayer/media/lib/ld.so \\",
111
+ " && cp -r /usr/lib/*/espeak-ng-data /out/opt/outerlayer/media/espeak-ng-data \\",
112
+ ` && printf '%s\\n' '#!/bin/sh' 'ESPEAK_DATA_PATH=/opt/outerlayer/media' 'export ESPEAK_DATA_PATH' 'exec /opt/outerlayer/media/lib/ld.so --library-path /opt/outerlayer/media/lib /opt/outerlayer/media/espeak-ng "$@"' > /out/usr/local/bin/espeak-ng \\`,
113
+ " && chmod 0755 /out/usr/local/bin/espeak-ng \\",
114
+ " && chmod -R a+rX /out",
115
+ ...browserStage({ nodeImage, fetched, download, stream }),
116
+ ...gitStage(nodeImage),
117
+ `FROM ${recipeImage} AS recipe`,
118
+ `FROM ${nodeImage} AS stage`,
119
+ "ARG TARGETARCH",
120
+ `RUN npm install --global --prefix /out/opt/outerlayer/claude @anthropic-ai/claude-code@${version}`,
121
+ "COPY dist /out/opt/outerlayer/cli/dist",
122
+ "COPY package.json /out/opt/outerlayer/cli/package.json",
123
+ // `outerlayer demo record` runs a demo on the Playwright release this CLI depends on, found beside the CLI as an npm
124
+ // install puts it. Only the library, with no install scripts: its browser is the browser stage's, which no other
125
+ // Playwright in the image looks for.
126
+ "RUN mkdir -p /tmp/cli-deps && cd /tmp/cli-deps && echo '{}' > package.json \\",
127
+ ` && npm install --no-audit --no-fund --ignore-scripts @playwright/test@${plain("the Playwright version", PLAYWRIGHT_VERSION, /^[0-9.]+$/)} \\`,
128
+ " && mv node_modules /out/opt/outerlayer/cli/node_modules",
129
+ // `outerlayer claude` installs the maintained skills from this pack into the build's home folder before the agent starts.
130
+ "COPY skill-pack /out/opt/outerlayer/cli/skill-pack",
131
+ "RUN mkdir -p /out/opt/outerlayer/bin /out/usr/local/bin /out/etc/claude-code /out/build \\",
132
+ " && cp /usr/local/bin/node /out/opt/outerlayer/bin/node \\",
133
+ ` && printf '%s\\n' '#!/bin/sh' 'exec /opt/outerlayer/bin/node /opt/outerlayer/cli/dist/index.js "$@"' > /out/usr/local/bin/outerlayer \\`,
134
+ ` && printf '%s\\n' '#!/bin/sh' 'PATH=/opt/outerlayer/bin:$PATH' 'export PATH' 'exec /opt/outerlayer/claude/bin/claude "$@"' > /out/usr/local/bin/claude \\`,
135
+ ` && printf '%s\\n' '${tools}' > /out/opt/outerlayer/tools.json \\`,
136
+ ` && printf '%s\\n' '${managedSettings}' > /out/etc/claude-code/managed-settings.json \\`,
137
+ " && chmod 0755 /out/usr/local/bin/outerlayer /out/usr/local/bin/claude \\",
138
+ // Every build, whatever user runs it, writes here: the first mount of an empty volume copies this mode.
139
+ " && chmod 1777 /out/build",
140
+ ...parts.guestHelper === true ? [
141
+ "COPY guest/outerlayer-guest /out/opt/outerlayer/bin/outerlayer-guest",
142
+ // The guest mounts the build's tmpfs, the kernel's file systems and the run directory over these. The judge's tmpfs is mounted over its directory when the judge runs.
143
+ "RUN chmod 0755 /out/opt/outerlayer/bin/outerlayer-guest \\",
144
+ " && mkdir -p /out/outerlayer/job /out/outerlayer/judge /out/run /out/proc /out/sys /out/dev"
145
+ ] : [],
146
+ ...fetched ? [`COPY ${FETCHED_DIR}/${FETCHED_TINI} /tmp/tini`, `COPY ${FETCHED_DIR}/${FETCHED_GH} /tmp/gh.tgz`] : [],
147
+ `RUN set -eu \\`,
148
+ ` && case "$TARGETARCH" in \\`,
149
+ ` amd64) gh_sha=${GH_SHA256.amd64}; tini_sha=${TINI_SHA256.amd64} ;; \\`,
150
+ ` arm64) gh_sha=${GH_SHA256.arm64}; tini_sha=${TINI_SHA256.arm64} ;; \\`,
151
+ ` *) echo "no pinned gh and tini for $TARGETARCH" >&2; exit 1 ;; \\`,
152
+ " esac \\",
153
+ ...fetched ? [] : [` && ${download} https://github.com/krallin/tini/releases/download/v${TINI_VERSION}/tini-static-$TARGETARCH /tmp/tini \\`],
154
+ ' && echo "$tini_sha /tmp/tini" | sha256sum -c - \\',
155
+ " && install -m 0755 /tmp/tini /out/opt/outerlayer/bin/tini \\",
156
+ ...fetched ? [] : [` && ${download} https://github.com/cli/cli/releases/download/v${GH_VERSION}/gh_${GH_VERSION}_linux_$TARGETARCH.tar.gz /tmp/gh.tgz \\`],
157
+ ' && echo "$gh_sha /tmp/gh.tgz" | sha256sum -c - \\',
158
+ " && tar -xzf /tmp/gh.tgz -C /tmp \\",
159
+ ` && install -D -m 0755 /tmp/gh_${GH_VERSION}_linux_$TARGETARCH/bin/gh /out/opt/outerlayer/gh/gh \\`,
160
+ // gh checks GitHub's certificate against the system's CA certificates, which a slim recipe image does not have.
161
+ ` && printf '%s\\n' '#!/bin/sh' '${findCaBundle()}' 'SSL_CERT_FILE="\${SSL_CERT_FILE:-$ca}"' 'export SSL_CERT_FILE' 'exec /opt/outerlayer/gh/gh "$@"' > /out/usr/local/bin/gh \\`,
162
+ " && chmod 0755 /out/usr/local/bin/gh",
163
+ "COPY --from=recipe /etc/passwd /tmp/recipe-passwd",
164
+ "COPY --from=recipe /etc/group /tmp/recipe-group",
165
+ `RUN uid=$(awk -F: 'BEGIN{m=1499} $3>=1500 && $3<60000 && $3>m{m=$3} END{print m+1}' /tmp/recipe-passwd) \\`,
166
+ ` && gid=$(awk -F: 'BEGIN{m=1499} $3>=1500 && $3<60000 && $3>m{m=$3} END{print m+1}' /tmp/recipe-group) \\`,
167
+ " && { grep -v -e '^ol-agent:' -e '^ol-relay:' /tmp/recipe-passwd > /out/etc/passwd || true; } \\",
168
+ " && { grep -v -e '^ol-agent:' -e '^ol-relay:' /tmp/recipe-group > /out/etc/group || true; } \\",
169
+ // The relay's id is fixed because the runner names it in the container's `--tmpfs` options before the container exists.
170
+ ` && if awk -F: '$3==${RELAY_ID}{found=1} END{exit !found}' /out/etc/passwd; then echo "uid ${RELAY_ID} is taken by a user of the recipe image, and the relay needs it" >&2; exit 1; fi \\`,
171
+ ` && if awk -F: '$3==${RELAY_ID}{found=1} END{exit !found}' /out/etc/group; then echo "gid ${RELAY_ID} is taken by a group of the recipe image, and the relay needs it" >&2; exit 1; fi \\`,
172
+ ` && printf '%s\\n' "ol-agent:x:$uid:$gid::/build/home:/bin/sh" >> /out/etc/passwd \\`,
173
+ ` && printf '%s\\n' "ol-agent:x:$gid:" >> /out/etc/group \\`,
174
+ ` && printf '%s\\n' "${RELAY_USER}:x:${RELAY_ID}:${RELAY_ID}::/nonexistent:/usr/sbin/nologin" >> /out/etc/passwd \\`,
175
+ ` && printf '%s\\n' "${RELAY_USER}:x:${RELAY_ID}:" >> /out/etc/group \\`,
176
+ " && chmod 0644 /out/etc/passwd /out/etc/group \\",
177
+ " && chmod -R a+rX /out",
178
+ `FROM ${recipeImage}`,
179
+ "COPY --from=stage /out/ /",
180
+ "COPY --from=voice /out/ /",
181
+ "COPY --from=media /out/ /",
182
+ "COPY --from=browser /out/ /",
183
+ "COPY --from=git /out/ /",
184
+ "ENV DISABLE_AUTOUPDATER=1",
185
+ // Last, so a recipe's own git comes first. A recipe image that sets no PATH gets the one Docker gives a container.
186
+ `ENV PATH=\${PATH:-/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin}:${FALLBACK_BIN}`,
187
+ // espeak-ng's audio library makes a private pulse-* folder for the user who runs it, in the temp folder unless XDG_RUNTIME_DIR names another.
188
+ // Made here, as root, it would ship in the image where the build user cannot read it, and the runner's disk check fails on it.
189
+ "RUN check=$(mktemp -d /tmp/ol-check.XXXXXX) \\",
190
+ " && outerlayer --version && claude --version && gh --version && ffmpeg -version && ffprobe -version \\",
191
+ ' && XDG_RUNTIME_DIR="$check" espeak-ng --version \\',
192
+ ` && git --version && ${FALLBACK_BIN}/git --version \\`,
193
+ ` && ${IMAGE_BROWSER.playwright}/chromium_headless_shell-${HEADLESS_SHELL_REVISION}/chrome-headless-shell-linux*/chrome-headless-shell --version \\`,
194
+ ' && rm -rf "$check"',
195
+ ""
196
+ ].join("\n");
197
+ }
198
+ function gitStage(nodeImage) {
199
+ const lib = `${IMAGE_GIT}/lib`;
200
+ const wrapper = [
201
+ "#!/bin/sh",
202
+ findCaBundle(),
203
+ 'GIT_SSL_CAINFO="${GIT_SSL_CAINFO:-$ca}"',
204
+ `GIT_EXEC_PATH=${IMAGE_GIT}/git-core`,
205
+ `GIT_TEMPLATE_DIR="\${GIT_TEMPLATE_DIR:-${IMAGE_GIT}/templates}"`,
206
+ "export GIT_SSL_CAINFO GIT_EXEC_PATH GIT_TEMPLATE_DIR",
207
+ `exec ${IMAGE_GIT}/git-core/git "$@"`
208
+ ];
209
+ return [
210
+ `FROM ${nodeImage} AS git`,
211
+ "RUN apt-get update \\",
212
+ " && apt-get install -y --no-install-recommends git ca-certificates patchelf \\",
213
+ " && rm -rf /var/lib/apt/lists/*",
214
+ "RUN set -eu \\",
215
+ ` && mkdir -p /out${lib} /out/opt/outerlayer/certs /out${FALLBACK_BIN} \\`,
216
+ ` && cp -a /usr/lib/git-core /out${IMAGE_GIT}/git-core \\`,
217
+ ` && cp -a /usr/share/git-core/templates /out${IMAGE_GIT}/templates \\`,
218
+ ` && programs=$(find /out${IMAGE_GIT}/git-core -type f -exec sh -c 'head -c 4 "$1" | grep -q ELF' _ {} \\; -print) \\`,
219
+ ` && if ldd $programs | grep 'not found'; then echo "git needs a library this stage does not install" >&2; exit 1; fi \\`,
220
+ ` && cp -L $(for file in $programs; do ldd "$file" | awk '$3 ~ /^\\// {print $3}'; done | sort -u) /out${lib}/ \\`,
221
+ ` && cp -L $(ldd /usr/lib/git-core/git | awk '$1 ~ /ld-linux/ {print $1}') /out${lib}/ld.so \\`,
222
+ ` && for file in $programs; do patchelf --set-interpreter ${lib}/ld.so --force-rpath --set-rpath ${lib} "$file"; done \\`,
223
+ ` && cp -L /etc/ssl/certs/ca-certificates.crt /out${CA_BUNDLE} \\`,
224
+ ` && printf '%s\\n' ${wrapper.map((line) => `'${line}'`).join(" ")} > /out${FALLBACK_BIN}/git \\`,
225
+ ` && chmod 0755 /out${FALLBACK_BIN}/git \\`,
226
+ " && chmod -R a+rX /out"
227
+ ];
228
+ }
229
+ function browserStage(parts) {
230
+ const { fetched } = parts;
231
+ const libraries = [
232
+ "libasound2",
233
+ "libatk-bridge2.0-0",
234
+ "libatk1.0-0",
235
+ "libatspi2.0-0",
236
+ "libdbus-1-3",
237
+ "libexpat1",
238
+ "libgbm1",
239
+ "libglib2.0-0",
240
+ "libnspr4",
241
+ "libnss3",
242
+ "libudev1",
243
+ "libx11-6",
244
+ "libxcb1",
245
+ "libxcomposite1",
246
+ "libxdamage1",
247
+ "libxext6",
248
+ "libxfixes3",
249
+ "libxkbcommon0",
250
+ "libxrandr2"
251
+ ];
252
+ const fonts = ["fonts-liberation", "fonts-dejavu-core", "fonts-wqy-zenhei", "fonts-noto-color-emoji"];
253
+ const lib = `${IMAGE_BROWSER.dir}/lib`;
254
+ const browsers = `/out${IMAGE_BROWSER.playwright}`;
255
+ const fontSettings = [
256
+ '<?xml version="1.0"?>',
257
+ '<!DOCTYPE fontconfig SYSTEM "urn:fontconfig:fonts.dtd">',
258
+ "<fontconfig>",
259
+ ` <dir>${IMAGE_BROWSER.dir}/fonts</dir>`,
260
+ " <dir>/usr/share/fonts</dir>",
261
+ " <dir>/usr/local/share/fonts</dir>",
262
+ ' <dir prefix="xdg">fonts</dir>',
263
+ ` <include ignore_missing="yes">${IMAGE_BROWSER.dir}/conf.d</include>`,
264
+ ' <cachedir prefix="xdg">fontconfig</cachedir>',
265
+ "</fontconfig>"
266
+ ];
267
+ const archive = (arch) => ` ${arch}) shell_file=${HEADLESS_SHELL[arch].file}; shell_sha=${HEADLESS_SHELL[arch].sha256}; encoder_file=${PLAYWRIGHT_FFMPEG[arch].file}; encoder_sha=${PLAYWRIGHT_FFMPEG[arch].sha256} ;; \\`;
268
+ return [
269
+ `FROM ${parts.nodeImage} AS browser`,
270
+ "ARG TARGETARCH",
271
+ "RUN apt-get update \\",
272
+ ` && apt-get install -y --no-install-recommends unzip patchelf fontconfig-config ${fonts.join(" ")} ${libraries.join(" ")} \\`,
273
+ " && rm -rf /var/lib/apt/lists/*",
274
+ ...fetched ? [`COPY ${FETCHED_DIR}/${FETCHED_HEADLESS_SHELL} /tmp/shell.zip`, `COPY ${FETCHED_DIR}/${FETCHED_PLAYWRIGHT_FFMPEG} /tmp/encoder.zip`] : [],
275
+ "RUN set -eu \\",
276
+ ` && case "$TARGETARCH" in \\`,
277
+ archive("amd64"),
278
+ archive("arm64"),
279
+ ` *) echo "no pinned browser for $TARGETARCH" >&2; exit 1 ;; \\`,
280
+ " esac \\",
281
+ ...fetched ? [] : [` && ${parts.stream} ${HEADLESS_SHELL_BASE_URL}/$shell_file /tmp/shell.zip \\`, ` && ${parts.download} ${PLAYWRIGHT_FFMPEG_BASE_URL}/$encoder_file /tmp/encoder.zip \\`],
282
+ ' && echo "$shell_sha /tmp/shell.zip" | sha256sum -c - \\',
283
+ ' && echo "$encoder_sha /tmp/encoder.zip" | sha256sum -c - \\',
284
+ ` && mkdir -p ${browsers} /out${lib} /out${IMAGE_BROWSER.dir}/fonts /out${IMAGE_BROWSER.dir}/conf.d \\`,
285
+ ` && unzip -q /tmp/shell.zip -d ${browsers}/chromium_headless_shell-${HEADLESS_SHELL_REVISION} \\`,
286
+ ` && unzip -q /tmp/encoder.zip -d ${browsers}/ffmpeg-${PLAYWRIGHT_FFMPEG_REVISION} \\`,
287
+ ` && shell=$(echo ${browsers}/chromium_headless_shell-${HEADLESS_SHELL_REVISION}/chrome-headless-shell-linux*/chrome-headless-shell) \\`,
288
+ ' && find "$(dirname "$shell")/locales" -type f ! -name en-US.pak -delete \\',
289
+ " && nss=$(dpkg -L libnss3 | grep '\\.so$') \\",
290
+ ` && if ldd "$shell" $nss | grep 'not found'; then echo "the browser needs a library this stage does not install" >&2; exit 1; fi \\`,
291
+ ` && cp -L $(for file in "$shell" $nss; do ldd "$file" | awk '$3 ~ /^\\// {print $3}'; done | sort -u) $nss /out${lib}/ \\`,
292
+ ` && cp -L $(ldd "$shell" | awk '$1 ~ /ld-linux/ {print $1}') /out${lib}/ld.so \\`,
293
+ ` && patchelf --set-interpreter ${lib}/ld.so --force-rpath --set-rpath ${lib} "$shell" \\`,
294
+ ` && cp /usr/share/fonts/truetype/liberation/*.ttf /usr/share/fonts/truetype/dejavu/*.ttf /usr/share/fonts/truetype/wqy/wqy-zenhei.ttc /usr/share/fonts/truetype/noto/NotoColorEmoji.ttf /out${IMAGE_BROWSER.dir}/fonts/ \\`,
295
+ ` && cp -L /etc/fonts/conf.d/*.conf /out${IMAGE_BROWSER.dir}/conf.d/ \\`,
296
+ ` && printf '%s\\n' ${fontSettings.map((line) => `'${line}'`).join(" ")} > /out${IMAGE_BROWSER.fontConfig} \\`,
297
+ " && chmod -R a+rX /out"
298
+ ];
299
+ }
300
+
301
+ export { FETCHED_DIR, FETCHED_FFMPEG, FETCHED_GH, FETCHED_HEADLESS_SHELL, FETCHED_PLAYWRIGHT_FFMPEG, FETCHED_TINI, FETCHED_VOICE_DIR, IMAGE_BROWSER, defaultDockerfile, toolsDockerfile };