@outerlayer/cli 0.34.0-next.20261028132110 → 0.34.0-next.20261028164329
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/dist/{agent-setup-MO7DHLV4.js → agent-setup-IVXRBFVV.js} +6 -6
- package/dist/{bridge-4DFOWIY2.js → bridge-4GRZFXJU.js} +2 -2
- package/dist/{build-3IZ6XHYN.js → build-SEJJWGTO.js} +7 -7
- package/dist/{build-disk-TEITBQKE.js → build-disk-EYRR3PBT.js} +5 -5
- package/dist/build-info.json +1 -1
- package/dist/{builder-S42B4STV.js → builder-T7B46P7T.js} +4 -4
- package/dist/{check-E47RDQFK.js → check-SPMUN5C4.js} +36 -36
- package/dist/{chunk-S25I6OCB.js → chunk-2EHDLYSL.js} +9 -9
- package/dist/{chunk-IZKUPAXF.js → chunk-3KGXXTMN.js} +1 -1
- package/dist/{chunk-NYMDS542.js → chunk-4YD7N6V3.js} +4 -4
- package/dist/{chunk-VJQQVTCS.js → chunk-5TXQFZHY.js} +1 -1
- package/dist/{chunk-NPTEYMBW.js → chunk-5W2YSWIW.js} +9 -4
- package/dist/{chunk-LBEYOL5K.js → chunk-7JSWRF5O.js} +2 -2
- package/dist/{chunk-BUVDDPOM.js → chunk-BBJ6BM5U.js} +1 -1
- package/dist/{chunk-QELQ6Y4I.js → chunk-CUAB7FAZ.js} +1 -1
- package/dist/{chunk-ZXQEBRCB.js → chunk-DCBFUUDL.js} +5 -5
- package/dist/{chunk-4557WV4R.js → chunk-EA2PQRCU.js} +2 -2
- package/dist/{chunk-GHZLVKA3.js → chunk-FUOKDHRB.js} +6 -6
- package/dist/{chunk-GHQW2BBX.js → chunk-GG5OE6MY.js} +2 -2
- package/dist/{chunk-6G3BG2GI.js → chunk-H7MGR2GN.js} +2 -2
- package/dist/{chunk-26WUFKYZ.js → chunk-HFOZTCTN.js} +1 -1
- package/dist/{chunk-FG564H3C.js → chunk-I2MFIT6X.js} +5 -5
- package/dist/{chunk-AJ3CC3HT.js → chunk-IPWKT5J7.js} +147 -29
- package/dist/{chunk-E3GX4WVJ.js → chunk-KH4774QT.js} +1 -1
- package/dist/{chunk-UAINHP2C.js → chunk-KPEMNHVF.js} +3 -3
- package/dist/{chunk-SH5JFBPO.js → chunk-KQDK4Q6A.js} +1 -1
- package/dist/{chunk-7XDF7PNF.js → chunk-MCPCOKHN.js} +2 -2
- package/dist/{chunk-JDQBJUXQ.js → chunk-MEIDQLXQ.js} +1 -1
- package/dist/{chunk-BRBZKIZG.js → chunk-MRLJK4GZ.js} +2 -2
- package/dist/{chunk-QGTJ5IJN.js → chunk-NCOL5RYI.js} +1 -1
- package/dist/{chunk-S44ZN3CV.js → chunk-OYQVLQP3.js} +52 -13
- package/dist/{chunk-HA46L5FG.js → chunk-P2PNBDUT.js} +5 -4
- package/dist/{chunk-XDCD4GD5.js → chunk-POND2SMT.js} +3 -3
- package/dist/{chunk-265SXBQ5.js → chunk-QGDGGLSD.js} +3 -3
- package/dist/{chunk-262MWE4Q.js → chunk-QGEGLZSY.js} +6 -6
- package/dist/{chunk-YMYU56JD.js → chunk-QI7JB2GU.js} +87 -23
- package/dist/{chunk-DKIZDYUX.js → chunk-ROQPDCRU.js} +1 -1
- package/dist/{chunk-47SFTQA5.js → chunk-RRXS4CWE.js} +2 -2
- package/dist/{chunk-2CW2QMSG.js → chunk-S6UM3N4R.js} +1 -1
- package/dist/{chunk-64LLUVJR.js → chunk-SAAVZ2BX.js} +1 -1
- package/dist/{chunk-7XZQTJOD.js → chunk-T3XPLCJG.js} +11 -5
- package/dist/{chunk-6MLX26T4.js → chunk-TDCHUEKY.js} +2 -2
- package/dist/{chunk-ORENXMHA.js → chunk-UXC3RQLF.js} +1 -1
- package/dist/{chunk-X2ZJL7GA.js → chunk-UZEIBWTP.js} +2 -2
- package/dist/{chunk-WHONZ53R.js → chunk-V2S2WRGD.js} +1 -1
- package/dist/{chunk-BEILLR5Q.js → chunk-W7E6IMEW.js} +3 -3
- package/dist/{chunk-ESK6EWAQ.js → chunk-WA3HIJVK.js} +3 -3
- package/dist/{chunk-TTUI3S6B.js → chunk-WH3FPBPO.js} +603 -45
- package/dist/{chunk-T664NDNT.js → chunk-XDAMLX2L.js} +5 -5
- package/dist/{chunk-JDYBPAPP.js → chunk-YF7W7B7Y.js} +24 -8
- package/dist/{chunk-ECCAFFAH.js → chunk-YIK5SGEN.js} +5 -5
- package/dist/{chunk-4N7BQBO4.js → chunk-YLPJOUVF.js} +5 -3
- package/dist/{chunk-TVDKDXW6.js → chunk-ZCA34T7R.js} +3 -3
- package/dist/{chunk-TZXJSNN4.js → chunk-ZCZY3Q55.js} +1 -1
- package/dist/{chunk-BTA5QOKD.js → chunk-ZTOJIRJH.js} +2 -2
- package/dist/{claude-launcher-TG7CHE3N.js → claude-launcher-7IC6QWR5.js} +1 -1
- package/dist/{cli-3V7AMF2V.js → cli-GKROH23V.js} +110 -110
- package/dist/{command-57COSNSG.js → command-XA6DOFL4.js} +24 -24
- package/dist/config-RSOCVUKC.js +17 -0
- package/dist/{connect-cmd-VBEYNPAK.js → connect-cmd-JFGDHFLA.js} +5 -5
- package/dist/{container-start-RS3S6WYE.js → container-start-ZDSVXSAL.js} +30 -30
- package/dist/{context-show-cmd-GMF7YMSN.js → context-show-cmd-XWLTP6R4.js} +6 -6
- package/dist/{control-plane-launch-4WRFVYNP.js → control-plane-launch-3MBWPYWZ.js} +5 -5
- package/dist/{demo-record-cmd-KM2UPWPL.js → demo-record-cmd-SIKNTSGI.js} +2 -2
- package/dist/{demo-voice-cmd-KFNEU2GR.js → demo-voice-cmd-SAB3QU4F.js} +11 -11
- package/dist/{docker-UP7EFFLL.js → docker-YMRPMDOW.js} +4 -4
- package/dist/{doctor-B2EZV6SJ.js → doctor-NVHAE4LS.js} +24 -24
- package/dist/{emit-artifact-cmd-NLCKM5TU.js → emit-artifact-cmd-TZTVQCFT.js} +10 -10
- package/dist/{emit-criteria-cmd-GO3TDUCG.js → emit-criteria-cmd-WH7JB4RQ.js} +7 -7
- package/dist/{emit-demo-cmd-EJBQHFR2.js → emit-demo-cmd-DNUVG5N7.js} +11 -11
- package/dist/{emit-finding-cmd-PHUHZLQ3.js → emit-finding-cmd-X5CBH4ZA.js} +7 -7
- package/dist/{emit-result-cmd-UJSWTGDG.js → emit-result-cmd-VCKNZ4RZ.js} +9 -9
- package/dist/{emit-step-cmd-M2JEWJ4Q.js → emit-step-cmd-IUXZZYKO.js} +7 -7
- package/dist/{exec-client-BQIZRROT.js → exec-client-U4544U2S.js} +3 -3
- package/dist/{find-unit-H5DKNRBG.js → find-unit-QYD2FMCP.js} +8 -8
- package/dist/{findings-cmd-O3VCFA55.js → findings-cmd-PLHBAQW4.js} +7 -7
- package/dist/{fleet-readiness-SXIYBEWC.js → fleet-readiness-F65CKZY5.js} +5 -5
- package/dist/{governing-source-QEXH6MIZ.js → governing-source-JFCTQXYK.js} +9 -9
- package/dist/{guest-init-DXXSPKHY.js → guest-init-MI4SEME4.js} +4 -4
- package/dist/{hook-fast-DPC75LFQ.js → hook-fast-GOH4T6SN.js} +1 -1
- package/dist/{host-checks-DN6HLRIW.js → host-checks-LEVAIIN4.js} +28 -28
- package/dist/{pending-V5FBWIJ4.js → host-key-OMLDWLNG.js} +3 -3
- package/dist/{image-tag-3KPTSRB4.js → image-tag-YJJONB64.js} +3 -3
- package/dist/index.js +4 -4
- package/dist/{init-SCUDJ6QI.js → init-JVKJTJNM.js} +21 -21
- package/dist/{init-6D3RKRRB.js → init-W3ATJYLX.js} +24 -22
- package/dist/{init-cmd-ELTRVJLG.js → init-cmd-XJOEU2PB.js} +10 -10
- package/dist/{init-update-4GUGHNS5.js → init-update-KX3FFQ5H.js} +8 -8
- package/dist/{install-cmd-6HGNO2HF.js → install-cmd-QTIIMO6B.js} +5 -5
- package/dist/{logs-HUENB7U4.js → logs-YVXVAYTD.js} +2 -2
- package/dist/{loop-LILQBIHK.js → loop-NNJN2XOW.js} +35 -35
- package/dist/{machine-UVPCJGJU.js → machine-OQOHEMJB.js} +21 -21
- package/dist/{mcp-serve-cmd-OFK3CS2T.js → mcp-serve-cmd-ECTAPDB3.js} +4 -4
- package/dist/{paths-VQ3ALYZ3.js → paths-M66LPPUF.js} +2 -2
- package/dist/{host-key-5P3ZL5XW.js → pending-7LC6XDRO.js} +3 -3
- package/dist/{pidfile-AVJFRZZT.js → pidfile-DSQOOAZV.js} +3 -3
- package/dist/{record-XE7Z3QGF.js → record-3KKIJ3XL.js} +3 -3
- package/dist/{runner-env-K4DOSDUV.js → runner-env-EX7YUIUH.js} +8 -8
- package/dist/{runner-identity-4RVY2MHX.js → runner-identity-LJWBXNM5.js} +4 -4
- package/dist/{runner-key-4OIOA3CD.js → runner-key-CAUO4RKG.js} +21 -21
- package/dist/{session-sender-N6QHL35T.js → session-sender-U2CTD7EX.js} +10 -10
- package/dist/{spec-cmd-EZGRJPUQ.js → spec-cmd-SAWQYHKM.js} +8 -8
- package/dist/{status-5RSSYZBU.js → status-IPIMYTG5.js} +28 -28
- package/dist/{sync-cmd-NEAZMEIL.js → sync-cmd-EBJQ5LMH.js} +9 -9
- package/dist/{update-steps-6JOHOMGM.js → update-steps-E2PP5DPR.js} +29 -29
- package/dist/{watch-FOY5LSSS.js → watch-6NRBX6VE.js} +9 -9
- package/dist/{work-claim-cmd-EJT55FZY.js → work-claim-cmd-SLVX2N4L.js} +12 -12
- package/dist/{work-cmd-KLPPJG4U.js → work-cmd-VSHZCXHQ.js} +7 -7
- package/dist/{work-comment-cmd-TT6TFF6Q.js → work-comment-cmd-3DCL7VHN.js} +8 -8
- package/dist/{work-demo-cmd-22PLEX4X.js → work-demo-cmd-SZJIKB5J.js} +7 -7
- package/dist/{work-logs-cmd-DWK5AYLK.js → work-logs-cmd-MRFSBMJM.js} +7 -7
- package/dist/{work-open-pr-cmd-MKHZBGA6.js → work-open-pr-cmd-N7GK7FUQ.js} +8 -8
- package/dist/{work-pr-cmd-P7PRHAJD.js → work-pr-cmd-HD5OIHUU.js} +7 -7
- package/dist/{work-proofs-cmd-7C22LBBE.js → work-proofs-cmd-H2GGE5HK.js} +8 -8
- package/dist/{work-proofs-record-SLIZUXUY.js → work-proofs-record-CJAFFWXG.js} +14 -14
- package/dist/{work-stop-cmd-U42TFT7X.js → work-stop-cmd-M6RIGF7S.js} +7 -7
- package/package.json +1 -1
- package/skill-pack/maintained/outerlayer-build/SKILL.md +22 -0
- package/dist/config-2EADRNJB.js +0 -17
|
@@ -1,33 +1,33 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
3
|
import { sha256OfFile, downloadPinned, VoiceDownloadError } from './chunk-2PGI6RDE.js';
|
|
4
|
-
import { readDefaultBranchHead, cloneRepository, buildRepositoryImage, prepareImageBuild, KEY_PREFIX, TAG_HOST, SHARED_OWNER, SHARED_NAME, metadataFromLabels, writeRecord } from './chunk-
|
|
5
|
-
import { FETCHED_DIR, toolsDockerfile, FETCHED_FFMPEG, FETCHED_GH, FETCHED_TINI, FETCHED_HEADLESS_SHELL, FETCHED_PLAYWRIGHT_FFMPEG, FETCHED_VOICE_DIR } from './chunk-
|
|
4
|
+
import { readDefaultBranchFiles, readDefaultBranchHead, cloneRepository, buildRepositoryImage, prepareImageBuild, KEY_PREFIX, TAG_HOST, SHARED_OWNER, SHARED_NAME, universalBase, metadataFromLabels, writeRecord } from './chunk-OYQVLQP3.js';
|
|
5
|
+
import { FETCHED_DIR, toolsDockerfile, FETCHED_FFMPEG, FETCHED_GH, FETCHED_TINI, FETCHED_HEADLESS_SHELL, FETCHED_PLAYWRIGHT_FFMPEG, FETCHED_VOICE_DIR } from './chunk-T3XPLCJG.js';
|
|
6
6
|
import { currentCliBuild } from './chunk-OGFGZQA3.js';
|
|
7
|
-
import { runExec, execCapture, waitForAgent } from './chunk-
|
|
8
|
-
import { GUEST_CID, linkVsockSockets } from './chunk-
|
|
9
|
-
import { retrySend, describeCallFailure, keepStagedContext, controlPlaneTokenFailure, refusalCode, resolveGoverningSource, prefetchGovernedContext, removeStagedContext, stageGovernedContext, pinnedSourceOf, commandWithShim, shimScript, WORKFLOW_PERMISSION_REASON } from './chunk-
|
|
10
|
-
import { runnerImageVersion } from './chunk-
|
|
11
|
-
import { hasStoredCredential, AGENT_COMMANDS, storeClaudeCredential, credentialFromStdin, isAgentName } from './chunk-
|
|
12
|
-
import { optionalEgress, runnerCredentialProblem, builtinKind, allowHostProblem, systemClock, emptyEgressCounts, hostAllowed } from './chunk-
|
|
13
|
-
import { formatBytes, allocatedBytes } from './chunk-
|
|
14
|
-
import { NETWORK_HELPER_MEMORY, createDockerHost, builderSpec, runnerLabelValue, RUNNER_LABEL, BUILD_LABEL, prepareRunArgs, networkHelperName, containerRunArgs, HASH_ARGV, parseProgramHash, JUDGE_PROGRAM, mountableHome, homeCopyRunArgs, containerExecArgs, childEnvironment, readDu, JUDGE_DIR, MAX_ITEMS_BYTES, JUDGE_ITEMS, MAX_REPORT_BYTES, JUDGE_REPORT, NETWORK_READY_FILE, spawnRun, identityRunArgs, parseIdentity, networkHelperRunArgs } from './chunk-
|
|
7
|
+
import { runExec, execCapture, waitForAgent } from './chunk-5W2YSWIW.js';
|
|
8
|
+
import { GUEST_CID, linkVsockSockets } from './chunk-ROQPDCRU.js';
|
|
9
|
+
import { retrySend, describeCallFailure, keepStagedContext, formatDuration, controlPlaneTokenFailure, refusalCode, resolveGoverningSource, prefetchGovernedContext, removeStagedContext, stageGovernedContext, pinnedSourceOf, commandWithShim, shimScript, WORKFLOW_PERMISSION_REASON } from './chunk-DCBFUUDL.js';
|
|
10
|
+
import { runnerImageVersion } from './chunk-UXC3RQLF.js';
|
|
11
|
+
import { hasStoredCredential, AGENT_COMMANDS, storeClaudeCredential, credentialFromStdin, isAgentName } from './chunk-KPEMNHVF.js';
|
|
12
|
+
import { optionalEgress, runnerCredentialProblem, builtinKind, DEFAULT_SETUP_MINUTES, allowHostProblem, DEFAULT_SAVED_STATE_MAX_BYTES, systemClock, emptyEgressCounts, hostAllowed } from './chunk-YF7W7B7Y.js';
|
|
13
|
+
import { DEFAULT_BUILD_DISK, formatBytes, allocatedBytes } from './chunk-RRXS4CWE.js';
|
|
14
|
+
import { parseSize, NETWORK_HELPER_MEMORY, createDockerHost, builderSpec, runnerLabelValue, RUNNER_LABEL, BUILD_LABEL, prepareRunArgs, networkHelperName, containerRunArgs, HASH_ARGV, parseProgramHash, JUDGE_PROGRAM, mountableHome, homeCopyRunArgs, containerExecArgs, childEnvironment, readDu, JUDGE_DIR, MAX_ITEMS_BYTES, JUDGE_ITEMS, MAX_REPORT_BYTES, JUDGE_REPORT, NETWORK_READY_FILE, spawnRun, identityRunArgs, parseIdentity, networkHelperRunArgs, SAVED_STATE_LABEL } from './chunk-P2PNBDUT.js';
|
|
15
15
|
import { RecipeError, DOCKER_FEATURE, universalImagePending, writeSanitizedRecipe, classifyProperties } from './chunk-CM4J53WZ.js';
|
|
16
|
-
import { firecrackerArch, MICROVM_ARCHES, FIRECRACKER_VERSION, hostHelperPinned, CLAUDE_CODE_VERSION, builderDiskPinned, guestAssetsPinned, GUEST_HELPER_SHA256, GUEST_ASSETS_BASE_URL, GUEST_KERNEL_SHA256, JAILER_BINARY_SHA256, FIRECRACKER_BINARY_SHA256, FIRECRACKER_SHA256, GUEST_ASSETS_VERSION, BUILDER_DISK_VERSION, networkHelperPinned, NETWORK_HELPER_SHA256, universalImagePinned, UNIVERSAL_IMAGE,
|
|
16
|
+
import { firecrackerArch, MICROVM_ARCHES, FIRECRACKER_VERSION, hostHelperPinned, CLAUDE_CODE_VERSION, builderDiskPinned, guestAssetsPinned, GUEST_HELPER_SHA256, GUEST_ASSETS_BASE_URL, GUEST_KERNEL_SHA256, JAILER_BINARY_SHA256, FIRECRACKER_BINARY_SHA256, FIRECRACKER_SHA256, GUEST_ASSETS_VERSION, BUILDER_DISK_VERSION, networkHelperPinned, NETWORK_HELPER_SHA256, universalImagePinned, UNIVERSAL_IMAGE, FFMPEG_STATIC, FFMPEG_BUILD_TAG, GH_SHA256, GH_VERSION, TINI_SHA256, TINI_VERSION, HEADLESS_SHELL, HEADLESS_SHELL_BASE_URL, PLAYWRIGHT_FFMPEG, PLAYWRIGHT_FFMPEG_BASE_URL, VOICE_PACK_FILES, BUILDER_DISK_SHA256, BUILDER_DISK_BASE_URL, DEFAULT_BASE_IMAGE } from './chunk-D3CT6XQ4.js';
|
|
17
17
|
import { startTimeOf, isSameProcess } from './chunk-JBJTC3UU.js';
|
|
18
|
-
import { postJson, WorkCommandError, getJson } from './chunk-
|
|
18
|
+
import { postJson, WorkCommandError, getJson } from './chunk-XDAMLX2L.js';
|
|
19
19
|
import { sanitizeTerminal } from './chunk-VEVNGDTP.js';
|
|
20
|
-
import { signerFor } from './chunk-
|
|
20
|
+
import { signerFor } from './chunk-5TXQFZHY.js';
|
|
21
21
|
import { sharedSkillNames, skillConflictMessage } from './chunk-FGF52V4V.js';
|
|
22
22
|
import { versionAtLeast, MINIMUM_CLAUDE_VERSION } from './chunk-HCVLP7XA.js';
|
|
23
23
|
import { MIN_SCRUBBED_LITERAL, setCustomScrubbers, setLocalIdentity, setRepoRoot, parseTranscript, scrubDeep, scrubText } from './chunk-GH2HEEOH.js';
|
|
24
24
|
import { orderSessions, extractFacts, sessionsToTimeline } from './chunk-2EEW6FJN.js';
|
|
25
25
|
import { downconvertSession } from './chunk-IXRUVR25.js';
|
|
26
|
-
import { vmAssetsDir, RUNNER_DIR_MODE, rootfsPathFor, diskTemplatesDir, RUNNER_FILE_MODE, buildsDir, buildDir, runnerDir, deliveryDir, ghConfigDir, runnerDirOfBuild, spoolDir, mirrorDir, SHIM_DIR, vsockPath, builderCacheDiskPath, builderConsolePath } from './chunk-
|
|
27
|
-
import { claudeCredential, parseIsolation, proxyEnvironment, withProxyWarningHidden, containerLocalAddress, destinationEnvironment, CLAUDE_CREDENTIAL_VARIABLES, builtInEnvironment, withHooksPath, startBuildServices, BUILT_IN_NAMES, checkoutPath, repositoriesOf, primaryOf, RELAY_PORT, repositoryPath, CHECKOUT_DIR, lifecycleWorkdir, buildEnvironment, checkEnvironment, withMiseVariables,
|
|
26
|
+
import { vmAssetsDir, RUNNER_DIR_MODE, rootfsPathFor, diskTemplatesDir, RUNNER_FILE_MODE, buildsDir, buildDir, runnerDir, deliveryDir, ghConfigDir, runnerDirOfBuild, spoolDir, mirrorDir, SHIM_DIR, vsockPath, builderCacheDiskPath, builderConsolePath } from './chunk-S6UM3N4R.js';
|
|
27
|
+
import { claudeCredential, parseIsolation, proxyEnvironment, withProxyWarningHidden, containerLocalAddress, destinationEnvironment, CLAUDE_CREDENTIAL_VARIABLES, builtInEnvironment, withHooksPath, startBuildServices, BUILT_IN_NAMES, checkoutPath, repositoriesOf, primaryOf, RELAY_PORT, repositoryPath, CHECKOUT_DIR, lifecycleWorkdir, buildEnvironment, checkEnvironment, withMiseVariables, isMiseVariableAllowed, JOB_MOUNT, realFdStorePort, AGENT_CREDENTIAL_MISSING } from './chunk-YLPJOUVF.js';
|
|
28
28
|
import { RELAY_USER, RELAY_ID, UNKNOWN_TRANSPORT_REASON, DOCKER_DESKTOP_REASON, MAX_MALFORMED_SESSIONS, MALFORMED_REASON, JOIN_WAIT_MS, MAX_OPEN_STREAMS, STREAM_RESET_RATE, STREAM_RESET_BURST, STREAM_WINDOW, SESSION_WINDOW, targetOfPath } from './chunk-MESOOLF4.js';
|
|
29
29
|
import { spawn, spawnSync } from 'child_process';
|
|
30
|
-
import { mkdirSync, existsSync, readFileSync, mkdtempSync, rmSync, chmodSync, truncateSync, statSync, accessSync, constants, readdirSync, writeFileSync, lstatSync, copyFileSync,
|
|
30
|
+
import { mkdirSync, existsSync, readFileSync, mkdtempSync, rmSync, chmodSync, truncateSync, statSync, accessSync, constants, readdirSync, writeFileSync, renameSync, lstatSync, copyFileSync, rmdirSync, unlinkSync, linkSync, openSync, closeSync, cpSync, writeSync, appendFileSync, createWriteStream, createReadStream } from 'fs';
|
|
31
31
|
import { randomBytes, createHash } from 'crypto';
|
|
32
32
|
import { join, dirname, isAbsolute, relative, resolve, basename } from 'path';
|
|
33
33
|
import { createConnection, connect, createServer as createServer$1, isIP } from 'net';
|
|
@@ -186,9 +186,9 @@ async function download(file, url, fetchImpl) {
|
|
|
186
186
|
if (!response.ok) throw new AssetError(file, `${file} could not be downloaded from ${url}: the server answered ${response.status}`);
|
|
187
187
|
return Buffer.from(await response.arrayBuffer());
|
|
188
188
|
}
|
|
189
|
-
function keptPath(dir,
|
|
190
|
-
mkdirSync(join(dir,
|
|
191
|
-
return join(dir,
|
|
189
|
+
function keptPath(dir, sha2562, name) {
|
|
190
|
+
mkdirSync(join(dir, sha2562), { recursive: true, mode: RUNNER_DIR_MODE });
|
|
191
|
+
return join(dir, sha2562, name);
|
|
192
192
|
}
|
|
193
193
|
async function ensureFile(dir, file, pin, fetchImpl, mode, stops = MICROVM_STOPS) {
|
|
194
194
|
const path = keptPath(dir, pin.sha256, file);
|
|
@@ -2392,8 +2392,7 @@ async function buildInBuilder(opts, deps, prepared) {
|
|
|
2392
2392
|
claudeCodeVersion: prepared.claudeCodeVersion,
|
|
2393
2393
|
buildId: opts.cli.buildId,
|
|
2394
2394
|
guestHelper: true,
|
|
2395
|
-
fetched: true
|
|
2396
|
-
...location.kind === "default" ? { universal: true } : {}
|
|
2395
|
+
fetched: true
|
|
2397
2396
|
})
|
|
2398
2397
|
);
|
|
2399
2398
|
inputs = builderInput({ checkoutDir: checkout.dir, toolsDir, toolFilesDir, recipeDir }, archive);
|
|
@@ -2415,6 +2414,7 @@ async function buildInBuilder(opts, deps, prepared) {
|
|
|
2415
2414
|
const described = JSON.parse(result);
|
|
2416
2415
|
const imageConfig = parseImageConfig(JSON.stringify(described.config));
|
|
2417
2416
|
const labels = described.config.Labels ?? {};
|
|
2417
|
+
const base = universalBase(location, labels);
|
|
2418
2418
|
mkdirSync(recordDir, { recursive: true, mode: RUNNER_DIR_MODE });
|
|
2419
2419
|
const fd = openSync(partial, "w", 384);
|
|
2420
2420
|
let received = 0;
|
|
@@ -2444,7 +2444,7 @@ async function buildInBuilder(opts, deps, prepared) {
|
|
|
2444
2444
|
sourceCommit: checkout.commit,
|
|
2445
2445
|
defaultBranch: checkout.defaultBranch,
|
|
2446
2446
|
recipe: location.kind === "default" ? "default" : location.path,
|
|
2447
|
-
...
|
|
2447
|
+
...base === void 0 ? {} : { base },
|
|
2448
2448
|
recipeKey: prepared.recipeKey,
|
|
2449
2449
|
toolsKey: prepared.toolsKey,
|
|
2450
2450
|
imageId: `${ROOT_DISK_ID_PREFIX}${rKey}-${tKey}`,
|
|
@@ -2847,8 +2847,8 @@ function needsBuildSkill(queue, command) {
|
|
|
2847
2847
|
}
|
|
2848
2848
|
function missingBuildSkillReason(governed, lookupFailure) {
|
|
2849
2849
|
const checked = governed === void 0 ? BUILD_SKILL_PATHS.join(" and ") : `${BUILD_SKILL_PATHS.join(", ")} and the governed context from ${governed.repository}@${governed.commit.slice(0, 7)}`;
|
|
2850
|
-
const
|
|
2851
|
-
return `the build has no \`build\` skill, so /build would load nothing${
|
|
2850
|
+
const lookup3 = lookupFailure === void 0 ? "" : `, and the lookup of the governing repository failed (${lookupFailure})`;
|
|
2851
|
+
return `the build has no \`build\` skill, so /build would load nothing${lookup3}: checked ${checked}`;
|
|
2852
2852
|
}
|
|
2853
2853
|
function cloneHasBuildSkill(checkout) {
|
|
2854
2854
|
return BUILD_SKILL_PATHS.some((path) => existsSync(join(checkout, path)));
|
|
@@ -2989,6 +2989,22 @@ var MANIFESTS = {
|
|
|
2989
2989
|
"build.gradle": "java",
|
|
2990
2990
|
"build.gradle.kts": "java"
|
|
2991
2991
|
};
|
|
2992
|
+
var VERSION_FILES = [
|
|
2993
|
+
".nvmrc",
|
|
2994
|
+
".node-version",
|
|
2995
|
+
".python-version",
|
|
2996
|
+
".ruby-version",
|
|
2997
|
+
".java-version",
|
|
2998
|
+
".go-version",
|
|
2999
|
+
".tool-versions",
|
|
3000
|
+
"mise.toml",
|
|
3001
|
+
".mise.toml",
|
|
3002
|
+
".config/mise.toml",
|
|
3003
|
+
".config/mise/config.toml",
|
|
3004
|
+
"go.mod",
|
|
3005
|
+
"rust-toolchain.toml",
|
|
3006
|
+
"rust-toolchain"
|
|
3007
|
+
];
|
|
2992
3008
|
var IMAGE_CONFIG = "/etc/mise/config.toml";
|
|
2993
3009
|
var RUST_PIN_FILES = ["rust-toolchain.toml", "rust-toolchain"];
|
|
2994
3010
|
var REFUSALS_NAMED2 = 3;
|
|
@@ -3127,6 +3143,128 @@ function ranRuntimeStep(state) {
|
|
|
3127
3143
|
return state?.unpinnedRuntimes !== void 0;
|
|
3128
3144
|
}
|
|
3129
3145
|
|
|
3146
|
+
// src/runner/setup-scripts.ts
|
|
3147
|
+
var SETUP_SCRIPT = ".outerlayer/setup.sh";
|
|
3148
|
+
var RUN_SCRIPT = 'if [ -x "$1" ]; then exec "$1"; else exec /bin/sh "$1"; fi';
|
|
3149
|
+
function tailOf(result) {
|
|
3150
|
+
const lines = `${result.stdout}
|
|
3151
|
+
${result.stderr}`.split("\n").map((line2) => line2.trimEnd()).filter((line2) => line2 !== "");
|
|
3152
|
+
return lines.slice(-20).join(" | ");
|
|
3153
|
+
}
|
|
3154
|
+
function failureReason(entry, input, result) {
|
|
3155
|
+
const tail = tailOf(result);
|
|
3156
|
+
const printed = tail === "" ? "it printed nothing" : `the last lines it printed: ${tail}`;
|
|
3157
|
+
if (result.timedOut === true) {
|
|
3158
|
+
return `setup_timed_out: ${SETUP_SCRIPT} in ${entry.repository} ran past runner.build.setupMinutes (${input.limitMinutes} ${input.limitMinutes === 1 ? "minute" : "minutes"}); ${printed}`;
|
|
3159
|
+
}
|
|
3160
|
+
return `setup_failed: ${SETUP_SCRIPT} in ${entry.repository} failed (exit ${result.status}); ${printed}`;
|
|
3161
|
+
}
|
|
3162
|
+
async function runSetupScripts(input) {
|
|
3163
|
+
const { exec, log, tag } = input;
|
|
3164
|
+
const ran = [];
|
|
3165
|
+
const missing = [];
|
|
3166
|
+
for (const entry of input.repositories) {
|
|
3167
|
+
if (input.aborted()) return { ok: false, reason: "", aborted: true };
|
|
3168
|
+
const path = checkoutPath(entry);
|
|
3169
|
+
const probe = await exec(["test", "-f", SETUP_SCRIPT], path, { quiet: true });
|
|
3170
|
+
if (probe.status === 1) {
|
|
3171
|
+
missing.push(entry.repository);
|
|
3172
|
+
continue;
|
|
3173
|
+
}
|
|
3174
|
+
if (probe.status !== 0) return { ok: false, reason: `setup_failed: checking for ${SETUP_SCRIPT} in ${entry.repository} failed (exit ${probe.status})` };
|
|
3175
|
+
log.line(`${tag} setup ${entry.repository}: running ${SETUP_SCRIPT}`);
|
|
3176
|
+
const started = Date.now();
|
|
3177
|
+
const result = await exec(["sh", "-c", RUN_SCRIPT, "setup", SETUP_SCRIPT], path, { timeoutMs: input.timeoutMs });
|
|
3178
|
+
if (result.status !== 0 || result.timedOut === true) return { ok: false, reason: failureReason(entry, input, result) };
|
|
3179
|
+
log.line(`${tag} setup ${entry.repository}: ${SETUP_SCRIPT} passed in ${formatDuration(Date.now() - started)}`);
|
|
3180
|
+
ran.push(entry.repository);
|
|
3181
|
+
}
|
|
3182
|
+
return { ok: true, ran, missing };
|
|
3183
|
+
}
|
|
3184
|
+
function keepMissingSetupScripts(state, steps) {
|
|
3185
|
+
if (steps.missingSetupScripts !== void 0) state.missingSetupScripts = steps.missingSetupScripts;
|
|
3186
|
+
}
|
|
3187
|
+
|
|
3188
|
+
// src/runner/saved-state/key.ts
|
|
3189
|
+
function sha256(text) {
|
|
3190
|
+
return createHash("sha256").update(text).digest("hex");
|
|
3191
|
+
}
|
|
3192
|
+
function keyFilePaths() {
|
|
3193
|
+
return [SETUP_SCRIPT, ...VERSION_FILES];
|
|
3194
|
+
}
|
|
3195
|
+
function significant(path, content) {
|
|
3196
|
+
if (path !== "go.mod") return content;
|
|
3197
|
+
return content.split("\n").filter((line2) => /^\s*(go|toolchain)\s/.test(line2)).map((line2) => line2.trim()).join("\n");
|
|
3198
|
+
}
|
|
3199
|
+
function hashOf(path, content) {
|
|
3200
|
+
return content === null || content === void 0 ? "absent" : sha256(`present:${significant(path, content)}`);
|
|
3201
|
+
}
|
|
3202
|
+
function stateKey(input) {
|
|
3203
|
+
const baseKey = sha256(JSON.stringify({ version: 1, factory: input.factory, repositories: input.repositories.map((entry) => entry.repository), imageDigest: input.imageDigest, diskBytes: input.diskBytes }));
|
|
3204
|
+
const fileHashes = {};
|
|
3205
|
+
for (const entry of input.repositories) {
|
|
3206
|
+
for (const path of keyFilePaths()) fileHashes[`${entry.repository}:${path}`] = hashOf(path, entry.files[path]);
|
|
3207
|
+
}
|
|
3208
|
+
return { baseKey, key: sha256(JSON.stringify([baseKey, fileHashes])), fileHashes };
|
|
3209
|
+
}
|
|
3210
|
+
function changedFiles(saved, current) {
|
|
3211
|
+
const names = /* @__PURE__ */ new Set([...Object.keys(saved), ...Object.keys(current)]);
|
|
3212
|
+
return [...names].filter((name) => (saved[name] ?? "absent") !== (current[name] ?? "absent"));
|
|
3213
|
+
}
|
|
3214
|
+
|
|
3215
|
+
// src/runner/saved-state/select.ts
|
|
3216
|
+
var SAVED_STATE_MAX_AGE_MS = 7 * 864e5;
|
|
3217
|
+
var INCOMPLETE_GRACE_MS = 36e5;
|
|
3218
|
+
function savedAtOf(record) {
|
|
3219
|
+
return Date.parse(record.savedAt);
|
|
3220
|
+
}
|
|
3221
|
+
function oldestFirst(records) {
|
|
3222
|
+
return [...records].sort((a, b) => savedAtOf(a) - savedAtOf(b));
|
|
3223
|
+
}
|
|
3224
|
+
function newestFirst(records) {
|
|
3225
|
+
return oldestFirst(records).reverse();
|
|
3226
|
+
}
|
|
3227
|
+
function lookup2(records, key, nowMs) {
|
|
3228
|
+
const complete = newestFirst(records.filter((record) => record.complete));
|
|
3229
|
+
const same = complete.filter((record) => record.key === key.key);
|
|
3230
|
+
const fresh = same.find((record) => nowMs - savedAtOf(record) < SAVED_STATE_MAX_AGE_MS);
|
|
3231
|
+
if (fresh !== void 0) return { kind: "usable", record: fresh, ageMs: nowMs - savedAtOf(fresh) };
|
|
3232
|
+
if (same[0] !== void 0) return { kind: "expired", record: same[0], ageMs: nowMs - savedAtOf(same[0]) };
|
|
3233
|
+
const near = complete.find((record) => record.baseKey === key.baseKey);
|
|
3234
|
+
if (near !== void 0) return { kind: "changed", record: near, files: changedFiles(near.fileHashes, key.fileHashes) };
|
|
3235
|
+
return { kind: "none" };
|
|
3236
|
+
}
|
|
3237
|
+
function savingRefusedBecause(input) {
|
|
3238
|
+
if (!input.universalImage) return "its image comes from a devcontainer file";
|
|
3239
|
+
if (input.managed) return "this runner is managed";
|
|
3240
|
+
if (input.savedStateMax === "off") return "runner.housekeeping.savedStateMax is off";
|
|
3241
|
+
return void 0;
|
|
3242
|
+
}
|
|
3243
|
+
function checkoutNotAtDefaultHead(checkouts) {
|
|
3244
|
+
for (const entry of checkouts) {
|
|
3245
|
+
if (entry.keyCommit === void 0) return `${entry.repository}: the host did not read its default branch`;
|
|
3246
|
+
if (entry.startCommit !== entry.keyCommit) return `${entry.repository}: its default branch moved after the host read the key's files`;
|
|
3247
|
+
if (entry.head !== entry.startCommit) return `${entry.repository}: its branch already holds commits`;
|
|
3248
|
+
}
|
|
3249
|
+
return void 0;
|
|
3250
|
+
}
|
|
3251
|
+
function selectExpired(records, nowMs) {
|
|
3252
|
+
return records.filter((record) => nowMs - savedAtOf(record) >= SAVED_STATE_MAX_AGE_MS);
|
|
3253
|
+
}
|
|
3254
|
+
function selectToFit(records, maxBytes) {
|
|
3255
|
+
let total = records.reduce((sum, record) => sum + record.bytes, 0);
|
|
3256
|
+
const selected = [];
|
|
3257
|
+
for (const record of oldestFirst(records)) {
|
|
3258
|
+
if (total <= maxBytes) break;
|
|
3259
|
+
selected.push(record);
|
|
3260
|
+
total -= record.bytes;
|
|
3261
|
+
}
|
|
3262
|
+
return selected;
|
|
3263
|
+
}
|
|
3264
|
+
function selectStaleIncomplete(records, nowMs) {
|
|
3265
|
+
return records.filter((record) => !record.complete && nowMs - savedAtOf(record) >= INCOMPLETE_GRACE_MS);
|
|
3266
|
+
}
|
|
3267
|
+
|
|
3130
3268
|
// src/runner/build-steps.ts
|
|
3131
3269
|
var aborted = () => ({ ok: false, reason: "", aborted: true });
|
|
3132
3270
|
function gitErrorLine(text) {
|
|
@@ -3192,13 +3330,24 @@ async function deliver(input, staged) {
|
|
|
3192
3330
|
if (recorded.status !== 0) return { ok: false, reason: failure("recording the delivery failed", recorded) };
|
|
3193
3331
|
return { ok: true };
|
|
3194
3332
|
}
|
|
3195
|
-
async function checkOut(input, entry) {
|
|
3333
|
+
async function checkOut(input, entry, restored) {
|
|
3196
3334
|
const { exec, log, tag } = input;
|
|
3197
3335
|
const path = checkoutPath(entry);
|
|
3198
|
-
const
|
|
3199
|
-
if (
|
|
3200
|
-
const
|
|
3201
|
-
|
|
3336
|
+
const failed = (reason) => ({ ok: false, reason, ...restored ? { restoreFailed: true } : {} });
|
|
3337
|
+
if (restored) {
|
|
3338
|
+
for (const argv of [["git", "reset", "--hard", "-q"], ["git", "clean", "-fdq"]]) {
|
|
3339
|
+
const cleaned = await exec(argv, path, { quiet: true });
|
|
3340
|
+
if (cleaned.status !== 0) {
|
|
3341
|
+
const detail = gitErrorLine(cleaned.stderr);
|
|
3342
|
+
return failed(`the saved checkout of ${entry.repository} could not be cleaned (${argv[1]} exit ${cleaned.status})${detail === "" ? "" : `: ${detail}`}`);
|
|
3343
|
+
}
|
|
3344
|
+
}
|
|
3345
|
+
} else {
|
|
3346
|
+
const clone = await exec(["git", "clone", "--filter=blob:none", "--single-branch", `https://github.com/${entry.repository}.git`, path], "/build/work");
|
|
3347
|
+
if (clone.status !== 0) {
|
|
3348
|
+
const detail = gitErrorLine(clone.stderr);
|
|
3349
|
+
return { ok: false, reason: `the clone of ${entry.repository} failed (exit ${clone.status})${detail === "" ? "" : `: ${detail}`}` };
|
|
3350
|
+
}
|
|
3202
3351
|
}
|
|
3203
3352
|
try {
|
|
3204
3353
|
const prepared = await prepareBranch({
|
|
@@ -3210,9 +3359,12 @@ async function checkOut(input, entry) {
|
|
|
3210
3359
|
}
|
|
3211
3360
|
});
|
|
3212
3361
|
log.line(`${tag} ${entry.primary ? "" : `${entry.repository} `}on ${prepared.branch} at ${prepared.head.slice(0, 8)}`);
|
|
3213
|
-
return { ok: true, startCommit: prepared.startCommit };
|
|
3362
|
+
return { ok: true, head: prepared.head, startCommit: prepared.startCommit };
|
|
3214
3363
|
} catch (err) {
|
|
3215
|
-
if (err instanceof BranchPrepareError)
|
|
3364
|
+
if (err instanceof BranchPrepareError) {
|
|
3365
|
+
const reason = entry.primary ? err.message : `${entry.repository}: ${err.message}`;
|
|
3366
|
+
return failed(restored ? `the saved checkout of ${entry.repository} could not be switched to ${entry.branch}: ${err.message}` : reason);
|
|
3367
|
+
}
|
|
3216
3368
|
throw err;
|
|
3217
3369
|
}
|
|
3218
3370
|
}
|
|
@@ -3235,24 +3387,29 @@ async function runBuildSteps(input) {
|
|
|
3235
3387
|
const primary = primaryOf(repositories) ?? { repository, branch: input.branch, primary: true };
|
|
3236
3388
|
const rewrite = await exec(["git", "config", "--global", `url.http://127.0.0.1:${RELAY_PORT}/github.com/.insteadOf`, "https://github.com/"], "/build/work");
|
|
3237
3389
|
if (rewrite.status !== 0) return { ok: false, reason: `configuring the build's git remote failed (exit ${rewrite.status})` };
|
|
3238
|
-
const
|
|
3390
|
+
const restored = input.setup?.restored === true;
|
|
3391
|
+
const heads = [];
|
|
3392
|
+
const keyCommits = input.setup?.keyCommits ?? {};
|
|
3393
|
+
const first = await checkOut(input, { ...primary, primary: true }, restored);
|
|
3239
3394
|
if (!first.ok) return first;
|
|
3240
3395
|
startCommit = first.startCommit;
|
|
3241
|
-
|
|
3396
|
+
heads.push({ repository: primary.repository, head: first.head, startCommit: first.startCommit, keyCommit: keyCommits[primary.repository] });
|
|
3397
|
+
if (repositories.length > 1 && !restored) {
|
|
3242
3398
|
const linkPath = repositoryPath(primary.repository);
|
|
3243
3399
|
const linked = await exec(["sh", "-c", 'mkdir -p "$(dirname "$1")" && ln -s "$2" "$1"', "link", linkPath, CHECKOUT_DIR], "/build/work");
|
|
3244
3400
|
if (linked.status !== 0) return { ok: false, reason: `linking ${primary.repository} at ${linkPath} failed (exit ${linked.status})` };
|
|
3245
3401
|
}
|
|
3246
3402
|
for (const entry of repositories.filter((candidate) => candidate.repository !== primary.repository)) {
|
|
3247
3403
|
if (input.aborted()) return aborted();
|
|
3248
|
-
const checked = await checkOut(input, { ...entry, primary: false });
|
|
3404
|
+
const checked = await checkOut(input, { ...entry, primary: false }, restored);
|
|
3249
3405
|
if (!checked.ok) return checked;
|
|
3406
|
+
heads.push({ repository: entry.repository, head: checked.head, startCommit: checked.startCommit, keyCommit: keyCommits[entry.repository] });
|
|
3250
3407
|
}
|
|
3251
3408
|
if (input.governing !== void 0) await noteIgnoredDevcontainers(input, repositories, input.governing.repository);
|
|
3409
|
+
const ordered = [{ ...primary, primary: true }, ...repositories.filter((candidate) => candidate.repository !== primary.repository).map((entry) => ({ ...entry, primary: false }))];
|
|
3252
3410
|
let unpinnedRuntimes;
|
|
3253
3411
|
let miseVariables;
|
|
3254
3412
|
if (input.runtimes !== void 0) {
|
|
3255
|
-
const ordered = [{ ...primary, primary: true }, ...repositories.filter((candidate) => candidate.repository !== primary.repository).map((entry) => ({ ...entry, primary: false }))];
|
|
3256
3413
|
const chosen = await provisionRuntimes({ repositories: ordered, exec, log, tag, refusals: input.runtimes.refusals, aborted: input.aborted });
|
|
3257
3414
|
if (!chosen.ok) return chosen.aborted === true ? aborted() : { ok: false, reason: chosen.reason };
|
|
3258
3415
|
unpinnedRuntimes = chosen.unpinned;
|
|
@@ -3267,6 +3424,25 @@ async function runBuildSteps(input) {
|
|
|
3267
3424
|
if (ran.status !== 0) return { ok: false, reason: lifecycleFailureReason(step, ran.status) };
|
|
3268
3425
|
}
|
|
3269
3426
|
if (input.aborted()) return aborted();
|
|
3427
|
+
let missingSetupScripts;
|
|
3428
|
+
let savedState = false;
|
|
3429
|
+
if (input.setup !== void 0) {
|
|
3430
|
+
const { setup } = input;
|
|
3431
|
+
const ran = await runSetupScripts({ repositories: ordered, exec: commandExec, log, tag, timeoutMs: setup.timeoutMs, limitMinutes: setup.limitMinutes, aborted: input.aborted });
|
|
3432
|
+
if (!ran.ok) return ran.aborted === true ? aborted() : { ok: false, reason: ran.reason };
|
|
3433
|
+
missingSetupScripts = ran.missing;
|
|
3434
|
+
if (setup.saveState !== void 0 && setup.restored !== true && ran.ran.length > 0) {
|
|
3435
|
+
const notAtHead = checkoutNotAtDefaultHead(heads);
|
|
3436
|
+
if (notAtHead !== void 0) {
|
|
3437
|
+
log.line(`${tag} this build saves no state: ${notAtHead}`);
|
|
3438
|
+
} else {
|
|
3439
|
+
const saved = await setup.saveState();
|
|
3440
|
+
if (saved.ok) savedState = true;
|
|
3441
|
+
else log.line(`${tag} the build's state could not be saved: ${saved.reason}`);
|
|
3442
|
+
}
|
|
3443
|
+
}
|
|
3444
|
+
if (input.aborted()) return aborted();
|
|
3445
|
+
}
|
|
3270
3446
|
const staged = await input.stageContext();
|
|
3271
3447
|
if (staged.kind === "refused") return { ok: false, reason: staged.reason };
|
|
3272
3448
|
let controlPlane;
|
|
@@ -3308,9 +3484,324 @@ async function runBuildSteps(input) {
|
|
|
3308
3484
|
startCommit,
|
|
3309
3485
|
...controlPlane === void 0 ? {} : { controlPlane },
|
|
3310
3486
|
...unpinnedRuntimes === void 0 ? {} : { unpinnedRuntimes },
|
|
3487
|
+
...missingSetupScripts === void 0 ? {} : { missingSetupScripts },
|
|
3488
|
+
...savedState ? { savedState: true } : {},
|
|
3311
3489
|
...miseVariables === void 0 ? {} : { miseVariables }
|
|
3312
3490
|
};
|
|
3313
3491
|
}
|
|
3492
|
+
function savedStatesDir(runnerDirPath) {
|
|
3493
|
+
return join(runnerDirPath, "saved-states");
|
|
3494
|
+
}
|
|
3495
|
+
function writeRecord2(dir, record) {
|
|
3496
|
+
mkdirSync(dir, { recursive: true, mode: RUNNER_DIR_MODE });
|
|
3497
|
+
const target = join(dir, `${record.id}.json`);
|
|
3498
|
+
const partial = `${target}.partial`;
|
|
3499
|
+
writeFileSync(partial, `${JSON.stringify(record, null, 2)}
|
|
3500
|
+
`, { mode: RUNNER_FILE_MODE });
|
|
3501
|
+
renameSync(partial, target);
|
|
3502
|
+
}
|
|
3503
|
+
function isRecord(value) {
|
|
3504
|
+
if (typeof value !== "object" || value === null) return false;
|
|
3505
|
+
const r = value;
|
|
3506
|
+
return typeof r.id === "string" && (r.kind === "volume" || r.kind === "tar") && typeof r.savedAt === "string" && Number.isFinite(Date.parse(r.savedAt)) && typeof r.bytes === "number" && typeof r.baseKey === "string" && typeof r.key === "string" && typeof r.fileHashes === "object" && r.fileHashes !== null && typeof r.complete === "boolean";
|
|
3507
|
+
}
|
|
3508
|
+
function readRecords(dir) {
|
|
3509
|
+
let names;
|
|
3510
|
+
try {
|
|
3511
|
+
names = readdirSync(dir);
|
|
3512
|
+
} catch {
|
|
3513
|
+
return [];
|
|
3514
|
+
}
|
|
3515
|
+
const records = [];
|
|
3516
|
+
for (const name of names) {
|
|
3517
|
+
if (!name.endsWith(".json")) continue;
|
|
3518
|
+
try {
|
|
3519
|
+
const parsed = JSON.parse(readFileSync(join(dir, name), "utf8"));
|
|
3520
|
+
if (isRecord(parsed)) records.push(parsed);
|
|
3521
|
+
} catch {
|
|
3522
|
+
}
|
|
3523
|
+
}
|
|
3524
|
+
return records;
|
|
3525
|
+
}
|
|
3526
|
+
function deleteRecord(dir, id) {
|
|
3527
|
+
rmSync(join(dir, `${id}.json`), { force: true });
|
|
3528
|
+
}
|
|
3529
|
+
|
|
3530
|
+
// src/runner/saved-state/plan.ts
|
|
3531
|
+
function savedStateBase(report) {
|
|
3532
|
+
const digest = report.recipe === "default" ? report.base?.digest : void 0;
|
|
3533
|
+
return digest === void 0 ? void 0 : { digest };
|
|
3534
|
+
}
|
|
3535
|
+
function savedStateMaxOf(config) {
|
|
3536
|
+
return config.housekeeping?.savedStateMax ?? DEFAULT_SAVED_STATE_MAX_BYTES;
|
|
3537
|
+
}
|
|
3538
|
+
async function planSavedState(input) {
|
|
3539
|
+
const { config, runtime, log } = input;
|
|
3540
|
+
const refused2 = savingRefusedBecause({ universalImage: input.base !== void 0, managed: config.managed === true, savedStateMax: savedStateMaxOf(config) });
|
|
3541
|
+
if (refused2 !== void 0 || input.base === void 0) {
|
|
3542
|
+
const why = refused2 ?? "its image comes from a devcontainer file";
|
|
3543
|
+
log(`this build saves no state: ${why}`);
|
|
3544
|
+
return { keyCommits: {}, noSave: why };
|
|
3545
|
+
}
|
|
3546
|
+
const root = runnerDir(config.configPath);
|
|
3547
|
+
const repositories = [];
|
|
3548
|
+
const keyCommits = {};
|
|
3549
|
+
for (const entry of input.repositories) {
|
|
3550
|
+
let token;
|
|
3551
|
+
try {
|
|
3552
|
+
token = entry.primary ? await input.tokens.token("read") : await input.tokens.token("read", entry.repository);
|
|
3553
|
+
} catch (err) {
|
|
3554
|
+
return unread(`the read token for ${entry.repository} could not be had: ${describeCallFailure(err)}`, log);
|
|
3555
|
+
}
|
|
3556
|
+
const [owner = "", name = ""] = entry.repository.split("/");
|
|
3557
|
+
const read = await runtime.readKeyFiles({
|
|
3558
|
+
remote: runtime.remoteFor?.(entry.repository) ?? `${runtime.github.gitUrl}/${entry.repository}.git`,
|
|
3559
|
+
dir: join(root, "key-files", owner, name),
|
|
3560
|
+
paths: keyFilePaths(),
|
|
3561
|
+
token
|
|
3562
|
+
});
|
|
3563
|
+
if (!read.ok) return unread(read.reason, log);
|
|
3564
|
+
keyCommits[entry.repository] = read.commit;
|
|
3565
|
+
repositories.push({ repository: entry.repository, commit: read.commit, files: read.files });
|
|
3566
|
+
}
|
|
3567
|
+
const key = stateKey({
|
|
3568
|
+
factory: config.appId,
|
|
3569
|
+
repositories,
|
|
3570
|
+
imageDigest: input.base.digest,
|
|
3571
|
+
diskBytes: parseSize(config.build?.disk ?? DEFAULT_BUILD_DISK) ?? 0
|
|
3572
|
+
});
|
|
3573
|
+
const found = lookup2(readRecords(savedStatesDir(root)), key, input.now.getTime());
|
|
3574
|
+
if (found.kind === "usable" && input.allowRestore) {
|
|
3575
|
+
log(`starting from saved state ${found.record.id}, saved ${formatDuration(found.ageMs)} ago`);
|
|
3576
|
+
return { key, keyCommits, restore: { record: found.record, ageMs: found.ageMs } };
|
|
3577
|
+
}
|
|
3578
|
+
if (found.kind === "expired") {
|
|
3579
|
+
log(`saved state ${found.record.id} is ${formatDuration(found.ageMs)} old, past the limit of ${SAVED_STATE_MAX_AGE_MS / 864e5} days; starting from a fresh disk`);
|
|
3580
|
+
} else if (found.kind === "changed") {
|
|
3581
|
+
log(`the setup script or version files changed on the default branch since saved state ${found.record.id}: ${found.files.join(", ")}; starting from a fresh disk`);
|
|
3582
|
+
} else if (found.kind === "none") {
|
|
3583
|
+
log("no saved state matches this build's key; starting from a fresh disk");
|
|
3584
|
+
}
|
|
3585
|
+
return { key, keyCommits };
|
|
3586
|
+
}
|
|
3587
|
+
function unread(reason, log) {
|
|
3588
|
+
log(`this build starts from no saved state and saves none: ${reason}`);
|
|
3589
|
+
return { keyCommits: {}, noSave: `its key files could not be read: ${reason}` };
|
|
3590
|
+
}
|
|
3591
|
+
async function removeSavedState(record, deps) {
|
|
3592
|
+
if (record.kind === "volume" && record.volume !== void 0) {
|
|
3593
|
+
const result = await deps.removeVolume(record.volume);
|
|
3594
|
+
if (result !== void 0 && result.status !== 0) {
|
|
3595
|
+
const line2 = (result.stderr ?? "").trim().split("\n").filter((text) => text.trim() !== "").at(-1);
|
|
3596
|
+
throw new Error(`Docker could not remove volume ${record.volume} (exit ${result.status}${line2 === void 0 ? "" : `: ${line2}`})`);
|
|
3597
|
+
}
|
|
3598
|
+
}
|
|
3599
|
+
if (record.kind === "tar" && record.path !== void 0) {
|
|
3600
|
+
rmSync(`${record.path}.partial`, { force: true });
|
|
3601
|
+
rmSync(record.path, { force: true });
|
|
3602
|
+
}
|
|
3603
|
+
deleteRecord(deps.dir, record.id);
|
|
3604
|
+
}
|
|
3605
|
+
async function removeOldestUntil(input) {
|
|
3606
|
+
const removed = [];
|
|
3607
|
+
for (const record of oldestFirst(input.records)) {
|
|
3608
|
+
if (input.enough()) break;
|
|
3609
|
+
try {
|
|
3610
|
+
await removeSavedState(record, input.deps);
|
|
3611
|
+
removed.push(record);
|
|
3612
|
+
input.log(`removed saved state ${record.id} from ${record.savedAt}`);
|
|
3613
|
+
} catch (err) {
|
|
3614
|
+
input.log(`could not remove saved state ${record.id}: ${describeCallFailure(err)}`);
|
|
3615
|
+
}
|
|
3616
|
+
}
|
|
3617
|
+
return removed;
|
|
3618
|
+
}
|
|
3619
|
+
async function removeSuperseded(input) {
|
|
3620
|
+
for (const old of input.records.filter((record) => record.complete && record.key === input.made.key && record.id !== input.made.id && Date.parse(record.savedAt) < Date.parse(input.made.savedAt))) {
|
|
3621
|
+
try {
|
|
3622
|
+
await removeSavedState(old, input.deps);
|
|
3623
|
+
input.log(`removed saved state ${old.id}, saved state ${input.made.id} replaces it`);
|
|
3624
|
+
} catch (err) {
|
|
3625
|
+
input.log(`could not remove saved state ${old.id}: ${describeCallFailure(err)}`);
|
|
3626
|
+
}
|
|
3627
|
+
}
|
|
3628
|
+
}
|
|
3629
|
+
|
|
3630
|
+
// src/runner/saved-state/provision.ts
|
|
3631
|
+
var isRetryFresh = (value) => typeof value === "object" && value !== null && "retryFresh" in value;
|
|
3632
|
+
async function provisionWithFreshFallback(input) {
|
|
3633
|
+
const first = await input.attempt(true);
|
|
3634
|
+
if (!isRetryFresh(first)) return first;
|
|
3635
|
+
input.log(`${first.reason}; provisioning the build again from a fresh disk`);
|
|
3636
|
+
return await input.attempt(false);
|
|
3637
|
+
}
|
|
3638
|
+
function clockOf(now) {
|
|
3639
|
+
return now ?? (() => /* @__PURE__ */ new Date());
|
|
3640
|
+
}
|
|
3641
|
+
function taggedLog(log, tag) {
|
|
3642
|
+
return (line2) => log.line(`${tag} ${line2}`);
|
|
3643
|
+
}
|
|
3644
|
+
function uidFromProbe(probe) {
|
|
3645
|
+
const uid = Number(probe.stdout);
|
|
3646
|
+
return probe.status === 0 && Number.isInteger(uid) ? uid : null;
|
|
3647
|
+
}
|
|
3648
|
+
function agentUidOf(probe) {
|
|
3649
|
+
return uidFromProbe(probe) ?? void 0;
|
|
3650
|
+
}
|
|
3651
|
+
function userIdChangedReason(savedUid, agentUid) {
|
|
3652
|
+
if (savedUid === void 0 || agentUid === savedUid) return void 0;
|
|
3653
|
+
return `the build user's id is ${agentUid ?? "unknown"} and it was ${savedUid} when the state was saved`;
|
|
3654
|
+
}
|
|
3655
|
+
function setupInputFor(input) {
|
|
3656
|
+
const minutes = input.setupMinutes ?? DEFAULT_SETUP_MINUTES;
|
|
3657
|
+
return {
|
|
3658
|
+
timeoutMs: minutes * 6e4,
|
|
3659
|
+
limitMinutes: minutes,
|
|
3660
|
+
restored: input.plan.restore !== void 0,
|
|
3661
|
+
keyCommits: input.plan.keyCommits,
|
|
3662
|
+
// A plan has a key exactly when the build may save a state. A build that starts from one is not asked to save.
|
|
3663
|
+
...input.plan.key === void 0 ? {} : { saveState: input.saveState }
|
|
3664
|
+
};
|
|
3665
|
+
}
|
|
3666
|
+
async function saveAndPrune(input) {
|
|
3667
|
+
const made = await input.save();
|
|
3668
|
+
if (!made.ok) return made;
|
|
3669
|
+
input.log(`saved state ${made.record.id}`);
|
|
3670
|
+
await removeSuperseded({ made: made.record, records: readRecords(input.dir), deps: { dir: input.dir, removeVolume: input.removeVolume }, log: input.log });
|
|
3671
|
+
return { ok: true };
|
|
3672
|
+
}
|
|
3673
|
+
async function discardUnusableState(input) {
|
|
3674
|
+
try {
|
|
3675
|
+
await removeSavedState(input.record, { dir: input.dir, removeVolume: input.removeVolume });
|
|
3676
|
+
} catch (err) {
|
|
3677
|
+
input.onError(`could not remove saved state ${input.record.id}: ${describeCallFailure(err)}`);
|
|
3678
|
+
}
|
|
3679
|
+
return { retryFresh: true, reason: `saved state ${input.record.id} could not be used: ${input.reason}` };
|
|
3680
|
+
}
|
|
3681
|
+
function settleFailedSteps(steps, on) {
|
|
3682
|
+
return steps.restoreFailed === true ? on.giveUp(steps.reason) : on.bail(steps.reason, steps.aborted === true);
|
|
3683
|
+
}
|
|
3684
|
+
var SAVED_FOLDERS = ["home", "work"];
|
|
3685
|
+
function stopProcessesScript() {
|
|
3686
|
+
return [
|
|
3687
|
+
"me=$$; uid=$(id -u); n=0; pass=0",
|
|
3688
|
+
"while [ $pass -lt 25 ]; do",
|
|
3689
|
+
' left=""',
|
|
3690
|
+
" for p in /proc/[0-9]*; do",
|
|
3691
|
+
" pid=${p##*/}",
|
|
3692
|
+
' [ "$pid" = "$me" ] && continue',
|
|
3693
|
+
` info=$(sed -n -e 's/^State:[[:space:]]*\\(.\\).*/S=\\1/p' -e 's/^Uid:[[:space:]]*\\([0-9]*\\).*/U=\\1/p' "$p/status" 2>/dev/null) || continue`,
|
|
3694
|
+
' case "$info" in *"S=Z"*) continue ;; esac',
|
|
3695
|
+
// The uid is the last line of `info`, so a match at its end cannot be the longer id 10000 for 1000.
|
|
3696
|
+
' case "$info" in *"U=$uid") left="$left $pid" ;; esac',
|
|
3697
|
+
" done",
|
|
3698
|
+
' [ -z "$left" ] && break',
|
|
3699
|
+
' for pid in $left; do kill -KILL "$pid" 2>/dev/null && n=$((n+1)); done',
|
|
3700
|
+
" pass=$((pass+1))",
|
|
3701
|
+
" sleep 0.2",
|
|
3702
|
+
"done",
|
|
3703
|
+
'[ -z "$left" ] || { echo "$left are still running" >&2; exit 1; }',
|
|
3704
|
+
'echo "stopped $n"'
|
|
3705
|
+
].join("\n");
|
|
3706
|
+
}
|
|
3707
|
+
var HELPER_FLAGS = [
|
|
3708
|
+
"--rm",
|
|
3709
|
+
"--network",
|
|
3710
|
+
"none",
|
|
3711
|
+
"--cap-drop",
|
|
3712
|
+
"ALL",
|
|
3713
|
+
"--cap-add",
|
|
3714
|
+
"CHOWN",
|
|
3715
|
+
"--cap-add",
|
|
3716
|
+
"DAC_OVERRIDE",
|
|
3717
|
+
"--cap-add",
|
|
3718
|
+
"DAC_READ_SEARCH",
|
|
3719
|
+
"--cap-add",
|
|
3720
|
+
"FOWNER",
|
|
3721
|
+
"--security-opt",
|
|
3722
|
+
"no-new-privileges",
|
|
3723
|
+
"--read-only",
|
|
3724
|
+
"--user",
|
|
3725
|
+
"0",
|
|
3726
|
+
"--pids-limit",
|
|
3727
|
+
"256"
|
|
3728
|
+
];
|
|
3729
|
+
function helperArgs(input) {
|
|
3730
|
+
return [
|
|
3731
|
+
...HELPER_FLAGS,
|
|
3732
|
+
"--label",
|
|
3733
|
+
`${RUNNER_LABEL}=${input.runnerId}`,
|
|
3734
|
+
"--mount",
|
|
3735
|
+
`type=volume,src=${input.from},dst=/from,readonly`,
|
|
3736
|
+
"--mount",
|
|
3737
|
+
`type=volume,src=${input.to},dst=/to`,
|
|
3738
|
+
"--entrypoint",
|
|
3739
|
+
"sh",
|
|
3740
|
+
input.image,
|
|
3741
|
+
"-c",
|
|
3742
|
+
input.script
|
|
3743
|
+
];
|
|
3744
|
+
}
|
|
3745
|
+
var SAVE_SCRIPT = `set -e; cp -a ${SAVED_FOLDERS.map((name) => `/from/${name}`).join(" ")} /to/; du -sk /to | cut -f1`;
|
|
3746
|
+
var RESTORE_SCRIPT = `set -e; cp -a ${SAVED_FOLDERS.map((name) => `/from/${name}`).join(" ")} /to/`;
|
|
3747
|
+
function failureText(result) {
|
|
3748
|
+
const line2 = result.stderr.trim().split("\n").at(-1) ?? "";
|
|
3749
|
+
return `exit ${result.status}${line2 === "" ? "" : `: ${line2}`}`;
|
|
3750
|
+
}
|
|
3751
|
+
async function saveContainerState(input) {
|
|
3752
|
+
const { host } = input;
|
|
3753
|
+
const id = randomBytes(6).toString("hex");
|
|
3754
|
+
const volume = `outerlayer-saved-${id}`;
|
|
3755
|
+
const record = {
|
|
3756
|
+
id,
|
|
3757
|
+
kind: "volume",
|
|
3758
|
+
volume,
|
|
3759
|
+
savedAt: input.now.toISOString(),
|
|
3760
|
+
bytes: 0,
|
|
3761
|
+
baseKey: input.key.baseKey,
|
|
3762
|
+
key: input.key.key,
|
|
3763
|
+
fileHashes: input.key.fileHashes,
|
|
3764
|
+
...input.agentUid === void 0 ? {} : { agentUid: input.agentUid },
|
|
3765
|
+
complete: false
|
|
3766
|
+
};
|
|
3767
|
+
const discard = async (reason) => {
|
|
3768
|
+
const removed = await host.removeVolume(volume).catch((err) => ({ status: -1, stderr: describeCallFailure(err) }));
|
|
3769
|
+
if (removed.status !== 0) {
|
|
3770
|
+
input.log(`the volume ${volume} could not be removed (${failureText({ status: removed.status, stderr: removed.stderr })}); its record stays so a later pass retries`);
|
|
3771
|
+
return { ok: false, reason };
|
|
3772
|
+
}
|
|
3773
|
+
deleteRecord(input.dir, id);
|
|
3774
|
+
return { ok: false, reason };
|
|
3775
|
+
};
|
|
3776
|
+
try {
|
|
3777
|
+
const noEnv = { literals: {}, passed: {} };
|
|
3778
|
+
const stopped = await host.exec({ container: input.container, user: input.agentUser, env: noEnv, argv: ["sh", "-c", stopProcessesScript()] });
|
|
3779
|
+
if (stopped.status !== 0) return { ok: false, reason: `the build's processes could not be stopped (${failureText(stopped)})` };
|
|
3780
|
+
input.log(`${stopped.stdout.trim() || "stopped 0"} process${stopped.stdout.trim() === "stopped 1" ? "" : "es"} before saving the state`);
|
|
3781
|
+
const flushed = await host.exec({ container: input.container, user: input.agentUser, env: noEnv, argv: ["sync"] });
|
|
3782
|
+
if (flushed.status !== 0) return { ok: false, reason: `the build's disk could not be flushed (${failureText(flushed)})` };
|
|
3783
|
+
writeRecord2(input.dir, record);
|
|
3784
|
+
const created = await host.createVolume(volume, { [SAVED_STATE_LABEL]: id, [RUNNER_LABEL]: input.runnerId });
|
|
3785
|
+
if (created.status !== 0) return await discard(`the volume for the saved state could not be made (${failureText(created)})`);
|
|
3786
|
+
const copied = await host.run(helperArgs({ image: input.image, from: input.buildVolume, to: volume, script: SAVE_SCRIPT, runnerId: input.runnerId }), { logPath: input.logPath });
|
|
3787
|
+
if (copied.status !== 0) return await discard(`the copy into the saved state failed (${failureText(copied)})`);
|
|
3788
|
+
const kilobytes = Number(copied.stdout.trim().split("\n").at(-1));
|
|
3789
|
+
const done = { ...record, bytes: Number.isFinite(kilobytes) ? kilobytes * 1024 : 0, complete: true };
|
|
3790
|
+
writeRecord2(input.dir, done);
|
|
3791
|
+
return { ok: true, record: done };
|
|
3792
|
+
} catch (err) {
|
|
3793
|
+
return await discard(`the state could not be saved: ${describeCallFailure(err)}`);
|
|
3794
|
+
}
|
|
3795
|
+
}
|
|
3796
|
+
async function restoreContainerState(input) {
|
|
3797
|
+
try {
|
|
3798
|
+
const copied = await input.host.run(helperArgs({ image: input.image, from: input.savedVolume, to: input.targetVolume, script: RESTORE_SCRIPT, runnerId: input.runnerId }), { logPath: input.logPath });
|
|
3799
|
+
if (copied.status !== 0) return { ok: false, reason: `the saved volume could not be copied (${failureText(copied)})` };
|
|
3800
|
+
return { ok: true };
|
|
3801
|
+
} catch (err) {
|
|
3802
|
+
return { ok: false, reason: `the saved volume could not be copied: ${describeCallFailure(err)}` };
|
|
3803
|
+
}
|
|
3804
|
+
}
|
|
3314
3805
|
var EVAL_INPUT_NAME = "eval-input";
|
|
3315
3806
|
var TRANSCRIPT_ROOT = "/build/home/.claude/projects";
|
|
3316
3807
|
var MAX_TRANSCRIPT_BYTES = 7 * 1024 * 1024;
|
|
@@ -4601,6 +5092,7 @@ function createContainerRuntime(opts = {}) {
|
|
|
4601
5092
|
github: { gitUrl: opts.github?.gitUrl ?? "https://github.com", apiUrl: opts.github?.apiUrl ?? "https://api.github.com" },
|
|
4602
5093
|
remoteFor,
|
|
4603
5094
|
readHead: opts.readHead ?? readDefaultBranchHead,
|
|
5095
|
+
readKeyFiles: opts.readKeyFiles ?? readDefaultBranchFiles,
|
|
4604
5096
|
clone: opts.clone ?? ((input) => cloneRepository(input.remote, input.dest)),
|
|
4605
5097
|
destinationOptions: opts.destinationOptions,
|
|
4606
5098
|
claudeUpstream: opts.claudeUpstream,
|
|
@@ -4763,6 +5255,12 @@ async function controlPlaneReadToken(tokens, controlPlane) {
|
|
|
4763
5255
|
}
|
|
4764
5256
|
}
|
|
4765
5257
|
async function provisionContainer(input) {
|
|
5258
|
+
return provisionWithFreshFallback({
|
|
5259
|
+
attempt: (allowRestore) => provisionContainerOnce(input, allowRestore),
|
|
5260
|
+
log: (line2) => input.log.line(`#${input.item.number} ${line2}`)
|
|
5261
|
+
});
|
|
5262
|
+
}
|
|
5263
|
+
async function provisionContainerOnce(input, allowRestore) {
|
|
4766
5264
|
const { config, runtime, item, itemKey, jobDir, log, tokens } = input;
|
|
4767
5265
|
const { host } = runtime;
|
|
4768
5266
|
const tag = `#${item.number}`;
|
|
@@ -4821,6 +5319,18 @@ async function provisionContainer(input) {
|
|
|
4821
5319
|
if (!obtained.ok) return obtained.aborted === true ? { ok: false, reason: obtained.reason, aborted: true } : fail(obtained.reason);
|
|
4822
5320
|
const built = obtained.built;
|
|
4823
5321
|
const { report } = built;
|
|
5322
|
+
const tagged = taggedLog(log, tag);
|
|
5323
|
+
const plan = await planSavedState({
|
|
5324
|
+
config,
|
|
5325
|
+
repositories,
|
|
5326
|
+
base: savedStateBase(report),
|
|
5327
|
+
runtime,
|
|
5328
|
+
tokens,
|
|
5329
|
+
now: clockOf(input.now)(),
|
|
5330
|
+
allowRestore,
|
|
5331
|
+
log: tagged
|
|
5332
|
+
});
|
|
5333
|
+
const savedStatesPath = savedStatesDir(runnerDir(config.configPath));
|
|
4824
5334
|
const buildId = randomBytes(6).toString("hex");
|
|
4825
5335
|
const name = `outerlayer-build-${buildId}`;
|
|
4826
5336
|
const buildDir2 = buildDir(config.configPath, buildId);
|
|
@@ -4877,6 +5387,16 @@ async function provisionContainer(input) {
|
|
|
4877
5387
|
return aborted2 ? { ok: false, reason: "ended before provision finished", aborted: true } : fail(reason);
|
|
4878
5388
|
};
|
|
4879
5389
|
const dockerFailed = (what, result) => bail(`${what} failed (exit ${result.status})${said(result.stderr)}`);
|
|
5390
|
+
const giveUpState = async (reason) => {
|
|
5391
|
+
await bail(reason);
|
|
5392
|
+
return discardUnusableState({
|
|
5393
|
+
record: plan.restore.record,
|
|
5394
|
+
reason,
|
|
5395
|
+
dir: savedStatesPath,
|
|
5396
|
+
removeVolume: (volume2) => host.removeVolume(volume2),
|
|
5397
|
+
onError: (line2) => log.error(`${tag} ${line2}`)
|
|
5398
|
+
});
|
|
5399
|
+
};
|
|
4880
5400
|
if (input.aborted()) return bail("", true);
|
|
4881
5401
|
mkdirSync(buildDir2, { recursive: true });
|
|
4882
5402
|
const ghDir = ghConfigDir(buildDir2);
|
|
@@ -4928,6 +5448,10 @@ async function provisionContainer(input) {
|
|
|
4928
5448
|
};
|
|
4929
5449
|
const prepared = await host.run(prepareRunArgs(runInput), { logPath });
|
|
4930
5450
|
if (prepared.status !== 0) return dockerFailed("preparing the build's volume", prepared);
|
|
5451
|
+
if (plan.restore !== void 0) {
|
|
5452
|
+
const restored = await restoreContainerState({ host, image: report.imageId, savedVolume: plan.restore.record.volume, targetVolume: name, runnerId, logPath });
|
|
5453
|
+
if (!restored.ok) return giveUpState(restored.reason);
|
|
5454
|
+
}
|
|
4931
5455
|
const imageConfig = await host.imageConfig(report.imageId);
|
|
4932
5456
|
const resolved = await resolveBuildUser(runtime, runInput, { remoteUser: report.apply.remoteUser, imageUser: imageConfig.user }, logPath);
|
|
4933
5457
|
if (!resolved.ok) return bail(resolved.reason);
|
|
@@ -4953,6 +5477,10 @@ async function provisionContainer(input) {
|
|
|
4953
5477
|
delivered: readToken,
|
|
4954
5478
|
onFailure: (reason) => log.error(`${tag} ${reason}`)
|
|
4955
5479
|
});
|
|
5480
|
+
const agentUidProbe = await host.exec({ container: name, user: ADDED_USER, env: NO_ENV, argv: ["id", "-u", agentUser] });
|
|
5481
|
+
const agentUid = agentUidOf(agentUidProbe);
|
|
5482
|
+
const changed = userIdChangedReason(plan.restore?.record.agentUid, agentUid);
|
|
5483
|
+
if (changed !== void 0) return giveUpState(changed);
|
|
4956
5484
|
const tools = await host.exec({ container: name, user: ADDED_USER, env: NO_ENV, argv: ["cat", "/opt/outerlayer/tools.json"] });
|
|
4957
5485
|
const agentVersion = agentVersionOf(tools.status === 0 ? tools.stdout : "");
|
|
4958
5486
|
environment.agentVersion = agentVersion;
|
|
@@ -4960,7 +5488,29 @@ async function provisionContainer(input) {
|
|
|
4960
5488
|
const hashed = program.status === 0 ? parseProgramHash(program.stdout) : void 0;
|
|
4961
5489
|
if (hashed !== void 0) state.judgeProgram = { path: JUDGE_PROGRAM, sha256: hashed };
|
|
4962
5490
|
input.save(state, environment);
|
|
4963
|
-
const exec = (argv, workdir, opts) =>
|
|
5491
|
+
const exec = async (argv, workdir, opts) => {
|
|
5492
|
+
const ran = await host.exec({ container: name, user: agentUser, workdir, env: withMiseVariables(env, opts?.variables), argv }, { ...opts?.quiet === true ? {} : { logPath }, ...opts?.timeoutMs === void 0 ? {} : { timeoutMs: opts.timeoutMs } });
|
|
5493
|
+
return endedAtLimit(ran) ? { ...ran, timedOut: true } : ran;
|
|
5494
|
+
};
|
|
5495
|
+
const saveState = () => saveAndPrune({
|
|
5496
|
+
save: () => saveContainerState({
|
|
5497
|
+
host,
|
|
5498
|
+
image: report.imageId,
|
|
5499
|
+
container: name,
|
|
5500
|
+
agentUser,
|
|
5501
|
+
agentUid,
|
|
5502
|
+
buildVolume: name,
|
|
5503
|
+
runnerId,
|
|
5504
|
+
dir: savedStatesPath,
|
|
5505
|
+
key: plan.key,
|
|
5506
|
+
now: clockOf(input.now)(),
|
|
5507
|
+
logPath,
|
|
5508
|
+
log: tagged
|
|
5509
|
+
}),
|
|
5510
|
+
dir: savedStatesPath,
|
|
5511
|
+
removeVolume: (volume2) => host.removeVolume(volume2),
|
|
5512
|
+
log: tagged
|
|
5513
|
+
});
|
|
4964
5514
|
const steps = await runBuildSteps({
|
|
4965
5515
|
repository,
|
|
4966
5516
|
branch: primary.branch,
|
|
@@ -4992,18 +5542,23 @@ async function provisionContainer(input) {
|
|
|
4992
5542
|
removeStagedContext: () => removeStagedContext(buildDir2),
|
|
4993
5543
|
keepIn: jobDir,
|
|
4994
5544
|
...input.requireBuildSkill === true ? { requireBuildSkill: true } : {},
|
|
4995
|
-
// A
|
|
4996
|
-
...runtimeStepInput(report, services)
|
|
5545
|
+
// A build on the universal image, or on an image built from it, chooses and installs its runtimes.
|
|
5546
|
+
...runtimeStepInput(report, services),
|
|
5547
|
+
setup: setupInputFor({ setupMinutes: config.build?.setupMinutes, plan, saveState })
|
|
4997
5548
|
});
|
|
4998
|
-
if (!steps.ok) return
|
|
5549
|
+
if (!steps.ok) return settleFailedSteps(steps, { giveUp: giveUpState, bail });
|
|
4999
5550
|
state.startCommit = steps.startCommit;
|
|
5000
5551
|
keepRuntimeStep(state, steps);
|
|
5552
|
+
keepMissingSetupScripts(state, steps);
|
|
5001
5553
|
if (steps.controlPlane !== void 0) environment.controlPlane = steps.controlPlane;
|
|
5002
5554
|
return { ok: true, workdir: CHECKOUT_DIR, state, environment };
|
|
5003
5555
|
}
|
|
5556
|
+
function endedAtLimit(result) {
|
|
5557
|
+
return result.status === 124 && /no answer within \d+s/.test(result.stderr);
|
|
5558
|
+
}
|
|
5004
5559
|
async function resolveBuildUser(runtime, runInput, named, logPath) {
|
|
5005
5560
|
const lookups = /* @__PURE__ */ new Map();
|
|
5006
|
-
const
|
|
5561
|
+
const lookup3 = async (user2) => {
|
|
5007
5562
|
const known = lookups.get(user2);
|
|
5008
5563
|
if (known !== void 0) return known;
|
|
5009
5564
|
const ran = await runtime.host.run(identityRunArgs({ ...runInput, user: ADDED_USER }, user2));
|
|
@@ -5017,8 +5572,8 @@ async function resolveBuildUser(runtime, runInput, named, logPath) {
|
|
|
5017
5572
|
let user;
|
|
5018
5573
|
let home;
|
|
5019
5574
|
try {
|
|
5020
|
-
user = await resolveAgentUser({ ...named, probe: async (name) => (await
|
|
5021
|
-
home = user === ADDED_USER ? void 0 : mountableHome((await
|
|
5575
|
+
user = await resolveAgentUser({ ...named, probe: async (name) => (await lookup3(name))?.uid ?? null });
|
|
5576
|
+
home = user === ADDED_USER ? void 0 : mountableHome((await lookup3(user))?.home);
|
|
5022
5577
|
} catch (err) {
|
|
5023
5578
|
if (err instanceof AgentUserError) return { ok: false, reason: err.message };
|
|
5024
5579
|
throw err;
|
|
@@ -5055,6 +5610,7 @@ function containerCommandExec(input) {
|
|
|
5055
5610
|
hostEnv: input.runtime.hostEnv,
|
|
5056
5611
|
containerEnv: state.containerEnv ?? {},
|
|
5057
5612
|
unpinnedRuntimes: state.unpinnedRuntimes,
|
|
5613
|
+
missingSetupScripts: state.missingSetupScripts,
|
|
5058
5614
|
miseVariables: state.miseVariables,
|
|
5059
5615
|
builtIns: state.builtIns === true,
|
|
5060
5616
|
identity: input.job.commitIdentity
|
|
@@ -5080,6 +5636,7 @@ function containerSenderCommand(input) {
|
|
|
5080
5636
|
hostEnv: input.runtime.hostEnv,
|
|
5081
5637
|
containerEnv: state.containerEnv ?? {},
|
|
5082
5638
|
unpinnedRuntimes: state.unpinnedRuntimes,
|
|
5639
|
+
missingSetupScripts: state.missingSetupScripts,
|
|
5083
5640
|
miseVariables: state.miseVariables,
|
|
5084
5641
|
builtIns: state.builtIns === true,
|
|
5085
5642
|
identity: input.job.commitIdentity
|
|
@@ -5220,6 +5777,7 @@ async function runCliInContainer(input, argv, logName) {
|
|
|
5220
5777
|
hostEnv: runtime.hostEnv,
|
|
5221
5778
|
containerEnv: state.containerEnv ?? {},
|
|
5222
5779
|
unpinnedRuntimes: state.unpinnedRuntimes,
|
|
5780
|
+
missingSetupScripts: state.missingSetupScripts,
|
|
5223
5781
|
miseVariables: state.miseVariables,
|
|
5224
5782
|
builtIns: state.builtIns === true,
|
|
5225
5783
|
identity: job.commitIdentity
|
|
@@ -5321,8 +5879,8 @@ function containerJudgeEnvironment(input) {
|
|
|
5321
5879
|
},
|
|
5322
5880
|
hashProgram: async () => {
|
|
5323
5881
|
const ran = await runtime.host.exec({ container: state.container, user: ADDED_USER, env: NO_ENV, argv: [...HASH_ARGV] });
|
|
5324
|
-
const
|
|
5325
|
-
return
|
|
5882
|
+
const sha2562 = ran.status === 0 ? parseProgramHash(ran.stdout) : void 0;
|
|
5883
|
+
return sha2562 === void 0 ? { ok: false, reason: firstLine4(ran.stderr) || `the hash could not be taken (exit ${ran.status})` } : { ok: true, sha256: sha2562 };
|
|
5326
5884
|
},
|
|
5327
5885
|
prepare: async (files, tree) => {
|
|
5328
5886
|
if (filesAreCopied(state)) {
|
|
@@ -5605,4 +6163,4 @@ function describeMachineDocker(machine) {
|
|
|
5605
6163
|
return `Docker Engine ${version ?? "of no known version"}, older than the ${MIN_ENGINE_MAJOR} that container builds need`;
|
|
5606
6164
|
}
|
|
5607
6165
|
|
|
5608
|
-
export { ADDED_USER, ADOPTED_FETCH_RETRY_WINDOW_MS, AgentUserError, AssetError, DeliveryFailure, EVAL_INPUT_NAME, GH_CONFIG_YML, HELPER_ENV, LEASE_SECONDS, MANAGED_UNJAILED_REASON, MIN_ENGINE_MAJOR, PLACEHOLDER_MARKER, PROOFS_LOG, RECORD_PROOFS_ARGV, ROOT_DISK_ID_PREFIX, RunnerInitError, attemptNote, claimPermissionProblem, claudeCredentialCheck, cleanupContainer, closeHostBuild, containerCommandExec, containerJudgeEnvironment, containerSenderCommand, copyJudgeTree, copyTranscripts, copyTranscriptsFromContainer, createBuildDisk, createContainerRuntime, createLeaseChannel, currentSkillCommand, decideHostBuildSkill, deleteBuildDisk, deliverContainerHooks, deliverExecMaven, describeMachineDocker, detectMachine, dropManagedBuildSettings, dropManagedClaudeKey, ensureFirecrackerAndJailer, ensureNetworkReady, ensureRootfs, ensureTunnel, execArgv, execCommandLine, fetchManagedBuildSettings, fetchManagedClaudeKey, filesAreCopied, firstLine4 as firstLine, formerSkillProblem, formerSkillQueues, ghHostsYml, gitRemoteFor, hasClaudeCredential, helperFirecrackerNote, helperSocketPath, holdManagedBuildSettings, holdManagedClaudeKey, hostCommandEnvironment, hostEnvironment, isPlaceholderCommand, keepGhConfigCurrent, keepRuntimeStep, leaseExpiresAtMs, leaseTimeLeftMs, lifecycleFailureReason, lifecycleSteps, listenOnBroker, machineConfig, makeBuildsDirPrivate, managedBuildSettingValues, managedBuildSettingsFor, managedClaudeKeyFor, managedClaudeKeyValues, managedMachineLimits, measureDisk, microVmArchSupported, microVmAssetsPinned, missingPermissionFix, missingPermissionOf, needsBuildSkill, needsClaudeCredential, obtainImage, outOfMemoryReason, parseCpuList, permissionModeProblem, permissionModeProblems, placeholderProblem, placeholderQueues, prepareBranch, prepareRunnerInit, probeHelper, provisionContainer, provisionEnvironment, provisionRuntimes, ranRuntimeStep, reachDockerStep, readConfigObject, recordProofsInContainer, resolveAgentUser, resolveHostBuild, runBuildSteps, runRunnerInit, runWithSpawn, runtimeStepInput, said, senderAliveScript, senderSignalScript, setUpRunnerCgroup, shellQuote, signalInContainer, startCheckContainer, startHostServices, startJailedInto, startServicesFor, stopContainer, stopJailed, stoppedHelperReason, supportsContainerBuilds, supportsMicroVmBuilds, sweepImageBuilders, syncInContainer, withFactoryHosts, withTunnelLog, writeEvalInput, writeRunnerInit };
|
|
6166
|
+
export { ADDED_USER, ADOPTED_FETCH_RETRY_WINDOW_MS, AgentUserError, AssetError, DeliveryFailure, EVAL_INPUT_NAME, GH_CONFIG_YML, HELPER_ENV, LEASE_SECONDS, MANAGED_UNJAILED_REASON, MIN_ENGINE_MAJOR, PLACEHOLDER_MARKER, PROOFS_LOG, RECORD_PROOFS_ARGV, ROOT_DISK_ID_PREFIX, RunnerInitError, agentUidOf, attemptNote, claimPermissionProblem, claudeCredentialCheck, cleanupContainer, clockOf, closeHostBuild, containerCommandExec, containerJudgeEnvironment, containerSenderCommand, copyJudgeTree, copyTranscripts, copyTranscriptsFromContainer, createBuildDisk, createContainerRuntime, createLeaseChannel, currentSkillCommand, decideHostBuildSkill, deleteBuildDisk, deleteRecord, deliverContainerHooks, deliverExecMaven, describeMachineDocker, detectMachine, discardUnusableState, dropManagedBuildSettings, dropManagedClaudeKey, ensureFirecrackerAndJailer, ensureNetworkReady, ensureRootfs, ensureTunnel, execArgv, execCommandLine, fetchManagedBuildSettings, fetchManagedClaudeKey, filesAreCopied, firstLine4 as firstLine, formerSkillProblem, formerSkillQueues, ghHostsYml, gitRemoteFor, hasClaudeCredential, helperFirecrackerNote, helperSocketPath, holdManagedBuildSettings, holdManagedClaudeKey, hostCommandEnvironment, hostEnvironment, isPlaceholderCommand, keepGhConfigCurrent, keepMissingSetupScripts, keepRuntimeStep, leaseExpiresAtMs, leaseTimeLeftMs, lifecycleFailureReason, lifecycleSteps, listenOnBroker, machineConfig, makeBuildsDirPrivate, managedBuildSettingValues, managedBuildSettingsFor, managedClaudeKeyFor, managedClaudeKeyValues, managedMachineLimits, measureDisk, microVmArchSupported, microVmAssetsPinned, missingPermissionFix, missingPermissionOf, needsBuildSkill, needsClaudeCredential, obtainImage, outOfMemoryReason, parseCpuList, permissionModeProblem, permissionModeProblems, placeholderProblem, placeholderQueues, planSavedState, prepareBranch, prepareRunnerInit, probeHelper, provisionContainer, provisionEnvironment, provisionRuntimes, provisionWithFreshFallback, ranRuntimeStep, reachDockerStep, readConfigObject, readRecords, recordProofsInContainer, removeOldestUntil, removeSavedState, resolveAgentUser, resolveHostBuild, runBuildSteps, runRunnerInit, runWithSpawn, runtimeStepInput, said, saveAndPrune, savedStateBase, savedStatesDir, selectExpired, selectStaleIncomplete, selectToFit, senderAliveScript, senderSignalScript, setUpRunnerCgroup, settleFailedSteps, setupInputFor, shellQuote, signalInContainer, startCheckContainer, startHostServices, startJailedInto, startServicesFor, stopContainer, stopJailed, stoppedHelperReason, supportsContainerBuilds, supportsMicroVmBuilds, sweepImageBuilders, syncInContainer, taggedLog, uidFromProbe, userIdChangedReason, withFactoryHosts, withTunnelLog, writeEvalInput, writeRecord2 as writeRecord, writeRunnerInit };
|