@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.
- package/README.md +4 -3
- package/dist/{build-M4UYZAFH.js → build-FS7TQEJ6.js} +4 -3
- package/dist/{build-disk-ZDVSSHO2.js → build-disk-RZ7AZKTY.js} +3 -3
- package/dist/build-info.json +1 -1
- package/dist/{builder-IORYS76D.js → builder-MQIL24HI.js} +2 -2
- package/dist/{check-V66LZDNK.js → check-YIUZ3UX3.js} +15 -14
- package/dist/{chunk-T7HUNNJ2.js → chunk-36U7LY7C.js} +3 -3
- package/dist/{chunk-MIRCFK4X.js → chunk-7PCA37ZS.js} +1 -1
- package/dist/{chunk-TLIZ7FY4.js → chunk-7QF2EXAN.js} +2 -2
- package/dist/{chunk-6NXBXWOP.js → chunk-BLSEHKZ4.js} +1 -1
- package/dist/{chunk-Q3L5AC75.js → chunk-DEJCZ43H.js} +3 -3
- package/dist/{chunk-TGBST2QL.js → chunk-DX4FF5P5.js} +6 -6
- package/dist/{chunk-JZLMFYFM.js → chunk-FQGC22IP.js} +1 -1
- package/dist/{chunk-KOZ6IBEI.js → chunk-GR7ZVGNI.js} +1 -1
- package/dist/chunk-GTBY7BTI.js +301 -0
- package/dist/{chunk-XDJ376OP.js → chunk-IYQPUGST.js} +5 -169
- package/dist/{chunk-F5H3LVHR.js → chunk-JL4GSPGD.js} +2 -2
- package/dist/{chunk-DFBY6LX3.js → chunk-LKTH7PUH.js} +10 -7
- package/dist/{chunk-63A6NUEZ.js → chunk-Q2KGDDUX.js} +1 -1
- package/dist/{chunk-4UPET3R7.js → chunk-QUOITPN7.js} +21 -2
- package/dist/{chunk-T6HSNQAT.js → chunk-SSRB5HEL.js} +4 -4
- package/dist/{chunk-IZVDQXTC.js → chunk-UWDFA6WW.js} +9 -9
- package/dist/{chunk-PN7RMFPP.js → chunk-WPDL7MBC.js} +2 -2
- package/dist/{chunk-XLJXO4QC.js → chunk-ZK3A4W3I.js} +5 -5
- package/dist/{cli-HQWQEHXJ.js → cli-MP5AUR36.js} +45 -45
- package/dist/{command-TONUMZ2A.js → command-RGBJLW72.js} +9 -8
- package/dist/{config-4K6OG2SZ.js → config-NGCQD6SN.js} +4 -4
- package/dist/{container-start-R74IO7CI.js → container-start-KT2KKAQX.js} +12 -11
- package/dist/{demo-record-cmd-7HJ5C7KM.js → demo-record-cmd-BUNOOCNM.js} +10 -1
- package/dist/{demo-voice-cmd-MBK3BGHZ.js → demo-voice-cmd-RWTRJUAX.js} +2 -2
- package/dist/{docker-XWQM4F7B.js → docker-TRGA5QRL.js} +2 -2
- package/dist/{doctor-SF4GAWAO.js → doctor-NEO7NXJJ.js} +10 -9
- package/dist/{emit-demo-cmd-6VTA5BSJ.js → emit-demo-cmd-QMRDQGB6.js} +2 -2
- package/dist/{find-unit-I4GWKY4T.js → find-unit-IOCVYPNW.js} +6 -6
- package/dist/{host-checks-FKLQST3J.js → host-checks-JH2ILNZT.js} +10 -9
- package/dist/index.js +1 -1
- package/dist/{init-3MO5WCU3.js → init-HGJQW2NL.js} +9 -8
- package/dist/{init-TDLUTI6F.js → init-WGVNY4NP.js} +9 -8
- package/dist/{init-cmd-MQ5R35O2.js → init-cmd-L2SG2WSN.js} +1 -1
- package/dist/{loop-NNNDBFAS.js → loop-F6E6PGWG.js} +14 -13
- package/dist/{machine-GCW2GGI2.js → machine-GYTMJSIY.js} +9 -8
- package/dist/{runner-env-LVVLUHY2.js → runner-env-DH7YA4BU.js} +6 -6
- package/dist/{runner-identity-73F2IWBT.js → runner-identity-GLL56XY7.js} +2 -2
- package/dist/{runner-key-ASPZ7TQH.js → runner-key-HMXSBRBE.js} +9 -8
- package/dist/{status-35Z3TW42.js → status-QNKTP6OU.js} +10 -9
- package/dist/{update-steps-ZTNIK7LA.js → update-steps-M2UKG4QZ.js} +11 -10
- package/dist/{work-claim-cmd-QPYCZUMI.js → work-claim-cmd-JF5GGDFZ.js} +4 -4
- package/dist/{work-proofs-cmd-56EY7O45.js → work-proofs-cmd-A7SZSM2A.js} +1 -1
- package/dist/{work-proofs-record-SPGI47OS.js → work-proofs-record-YG6HD6SJ.js} +3 -3
- 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,
|
|
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
|
|
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-
|
|
4
|
-
import './chunk-
|
|
5
|
-
import './chunk-
|
|
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-
|
|
4
|
-
import './chunk-
|
|
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';
|
package/dist/build-info.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"id":"2026-10-
|
|
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-
|
|
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-
|
|
3
|
+
import { probeCredentials } from './chunk-UWDFA6WW.js';
|
|
4
4
|
import './chunk-3JVYMD4G.js';
|
|
5
|
-
import './chunk-
|
|
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-
|
|
9
|
+
import './chunk-WPDL7MBC.js';
|
|
10
10
|
import './chunk-TS3EFB7S.js';
|
|
11
|
-
import './chunk-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
36
|
-
import './chunk-
|
|
37
|
-
import { loadRunnerConfig, RunnerConfigError, checkHooks } from './chunk-
|
|
38
|
-
import './chunk-
|
|
39
|
-
import './chunk-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
4
|
-
import { parseSize } from './chunk-
|
|
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-
|
|
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-
|
|
4
|
-
import { parseSize } from './chunk-
|
|
5
|
-
import { builderDiskPinned } from './chunk-
|
|
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-
|
|
12
|
-
import { fetchCommit, readBranchHead, diffBetween } from './chunk-
|
|
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-
|
|
17
|
-
import { formatBytes, DEFAULT_CACHE_DISK, allocatedBytes } from './chunk-
|
|
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-
|
|
19
|
-
import { CLAUDE_CODE_VERSION, FIRECRACKER_VERSION } from './chunk-
|
|
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-
|
|
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 };
|