@outerlayer/cli 0.5.0 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/dist/build-info.json +1 -1
- package/dist/{check-AXCXDYO2.js → check-4Q33B5ZE.js} +10 -8
- package/dist/{chunk-5Y3QQRUH.js → chunk-3DD6AOWC.js} +37 -0
- package/dist/{chunk-OHFDWLJP.js → chunk-CHLVJNTE.js} +3 -3
- package/dist/{chunk-L24G4PLP.js → chunk-F4NU4H3V.js} +399 -18
- package/dist/{chunk-ZLB7GCTY.js → chunk-H4CK2YK4.js} +4 -4
- package/dist/{chunk-RJ467PDQ.js → chunk-RTXKYLCA.js} +1 -1
- package/dist/{chunk-4NMBDRBS.js → chunk-T52PKE2Y.js} +142 -125
- package/dist/chunk-UGP76IZ5.js +34 -0
- package/dist/{chunk-RWOTHXYE.js → chunk-UWH5DSUP.js} +27 -15
- package/dist/{chunk-FC7TMRNA.js → chunk-W7UTFZDH.js} +2 -2
- package/dist/chunk-YYYRBYWG.js +32 -0
- package/dist/{chunk-QDQEUVUF.js → chunk-ZXBITXTS.js} +1 -1
- package/dist/{cli-NNWPFP5S.js → cli-LHSUYYMI.js} +29 -24
- package/dist/{config-XVJYZ4IQ.js → config-HL2AIR3K.js} +1 -1
- package/dist/{doctor-J3GIMR7K.js → doctor-MOA3RIEP.js} +19 -5
- package/dist/index.js +1 -1
- package/dist/{init-CZX7ZDWF.js → init-2IPOIXJV.js} +4 -4
- package/dist/{init-3ZQLFPNC.js → init-FYDJAKTM.js} +3 -3
- package/dist/{init-cmd-W5NOLQPX.js → init-cmd-KLIDRVXQ.js} +1 -1
- package/dist/{loop-VGM772I7.js → loop-NEBUGD2Q.js} +7 -5
- package/dist/{machine-5WEJA36F.js → machine-ECU3QCZD.js} +2 -2
- package/dist/record-C43U4PKS.js +7 -0
- package/dist/{status-KBDVUYQ3.js → status-CLATHCTQ.js} +22 -4
- package/dist/{work-claim-cmd-BQPPURBW.js → work-claim-cmd-3OPWA77E.js} +2 -2
- package/package.json +1 -1
|
@@ -1004,7 +1004,7 @@ function doctorExitCode(checks) {
|
|
|
1004
1004
|
|
|
1005
1005
|
// src/doctor-report.ts
|
|
1006
1006
|
async function gatherDoctorChecks() {
|
|
1007
|
-
const { defaultRunnerConfigPath, hasRunnerBlock, loadRunnerConfig, runnerVmOf } = await import('./config-
|
|
1007
|
+
const { defaultRunnerConfigPath, hasRunnerBlock, loadRunnerConfig, runnerVmOf } = await import('./config-HL2AIR3K.js');
|
|
1008
1008
|
const configPath = defaultRunnerConfigPath();
|
|
1009
1009
|
const checks = runDoctor({ runnerHost: hasRunnerBlock(configPath) });
|
|
1010
1010
|
const credential = await credentialChecks();
|
|
@@ -1016,7 +1016,7 @@ async function gatherDoctorChecks() {
|
|
|
1016
1016
|
const { runnerVmCheck } = await import('./doctor-53F2PYY7.js');
|
|
1017
1017
|
checks.push(runnerVmCheck(configPath, { lima: realLima(), home: homeFromEnv(), instance: runnerVm.instance }));
|
|
1018
1018
|
} else if (hasRunnerBlock(configPath)) {
|
|
1019
|
-
const { runnerHostChecks } = await import('./doctor-
|
|
1019
|
+
const { runnerHostChecks } = await import('./doctor-MOA3RIEP.js');
|
|
1020
1020
|
const hooks = (() => {
|
|
1021
1021
|
try {
|
|
1022
1022
|
return loadRunnerConfig(configPath).hooks;
|
|
@@ -1041,8 +1041,8 @@ async function gatherDoctorChecks() {
|
|
|
1041
1041
|
})();
|
|
1042
1042
|
const runner = (() => {
|
|
1043
1043
|
try {
|
|
1044
|
-
const { concurrency, build } = loadRunnerConfig(configPath);
|
|
1045
|
-
return { concurrency, buildMemory: build?.memory ?? "8g" };
|
|
1044
|
+
const { concurrency, build, housekeeping } = loadRunnerConfig(configPath);
|
|
1045
|
+
return { concurrency, buildMemory: build?.memory ?? "8g", minFreeDisk: housekeeping?.minFreeDisk };
|
|
1046
1046
|
} catch {
|
|
1047
1047
|
return void 0;
|
|
1048
1048
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
|
-
import { MIN_ENGINE_MAJOR } from './chunk-
|
|
3
|
+
import { MIN_ENGINE_MAJOR } from './chunk-T52PKE2Y.js';
|
|
4
4
|
import { firecrackerArch, MICROVM_ARCHES, guestAssetsPinned } from './chunk-Z62HNKEJ.js';
|
|
5
5
|
import { spawnSync } from 'child_process';
|
|
6
6
|
import { existsSync, accessSync, constants, statSync, readFileSync } from 'fs';
|
|
@@ -14,11 +14,11 @@ import { defaultGitRunner, materializeContext } from './chunk-XDDW4FRS.js';
|
|
|
14
14
|
import { readContextManifest, readSourceCache } from './chunk-WIOZAJ2W.js';
|
|
15
15
|
import { signingFetchFor } from './chunk-BCLJSQCV.js';
|
|
16
16
|
import { signerFor } from './chunk-M5POMKKW.js';
|
|
17
|
-
import { rootfsPathFor, diskTemplatesDir, RUNNER_FILE_MODE, stagedContextDir, contextHomeDir, ghConfigDir, runnerDirOfBuild, spoolDir, mirrorDir, buildDir, RUNNER_DIR_MODE, vmAssetsDir } from './chunk-ZM2IMMYN.js';
|
|
18
|
-
import { relative, join, dirname, basename } from 'path';
|
|
17
|
+
import { rootfsPathFor, diskTemplatesDir, RUNNER_FILE_MODE, stagedContextDir, contextHomeDir, ghConfigDir, runnerDirOfBuild, spoolDir, mirrorDir, buildDir, runnerDir, RUNNER_DIR_MODE, vmAssetsDir } from './chunk-ZM2IMMYN.js';
|
|
18
|
+
import { relative, join, dirname, resolve, basename } from 'path';
|
|
19
19
|
import { spawnSync, spawn } from 'child_process';
|
|
20
|
-
import {
|
|
21
|
-
import { existsSync, mkdirSync, chmodSync, truncateSync, rmSync, writeFileSync, renameSync, lstatSync, readFileSync, unlinkSync, readdirSync, openSync, closeSync, mkdtempSync, createWriteStream, statSync, createReadStream } from 'fs';
|
|
20
|
+
import { createHash, randomBytes } from 'crypto';
|
|
21
|
+
import { existsSync, mkdirSync, chmodSync, truncateSync, rmSync, writeFileSync, realpathSync, renameSync, lstatSync, readFileSync, unlinkSync, readdirSync, openSync, closeSync, mkdtempSync, createWriteStream, statSync, createReadStream } from 'fs';
|
|
22
22
|
import { createServer, request } from 'http';
|
|
23
23
|
import { request as request$1 } from 'https';
|
|
24
24
|
import { Transform } from 'stream';
|
|
@@ -86,7 +86,7 @@ function isRetryable(err) {
|
|
|
86
86
|
}
|
|
87
87
|
async function retrySend(send, deps = {}) {
|
|
88
88
|
const now = deps.now ?? (() => /* @__PURE__ */ new Date());
|
|
89
|
-
const sleep = deps.sleep ?? ((ms) => new Promise((
|
|
89
|
+
const sleep = deps.sleep ?? ((ms) => new Promise((resolve2) => setTimeout(resolve2, ms)));
|
|
90
90
|
const windowMs = deps.windowMs ?? RETRY_WINDOW_MS;
|
|
91
91
|
const startedMs = now().getTime();
|
|
92
92
|
let attempts = 0;
|
|
@@ -356,6 +356,119 @@ function lifecycleSteps(lifecycle) {
|
|
|
356
356
|
function lifecycleFailureReason(step, exitCode) {
|
|
357
357
|
return `lifecycle command ${step.name} (${step.source}) exited ${exitCode}`;
|
|
358
358
|
}
|
|
359
|
+
var BUILD_LABEL = "ai.outerlayer.build";
|
|
360
|
+
var RUNNER_LABEL = "ai.outerlayer.runner";
|
|
361
|
+
function resolvedRunnerDir(configPath) {
|
|
362
|
+
try {
|
|
363
|
+
return realpathSync(runnerDir(configPath));
|
|
364
|
+
} catch {
|
|
365
|
+
try {
|
|
366
|
+
return join(realpathSync(dirname(resolve(configPath))), "runner");
|
|
367
|
+
} catch {
|
|
368
|
+
return resolve(runnerDir(configPath));
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
function runnerLabelValue(configPath) {
|
|
373
|
+
return createHash("sha256").update(resolvedRunnerDir(configPath)).digest("hex").slice(0, 12);
|
|
374
|
+
}
|
|
375
|
+
function hardened(input) {
|
|
376
|
+
const { memory, cpus, pids } = input.limits;
|
|
377
|
+
return [
|
|
378
|
+
"--label",
|
|
379
|
+
`${BUILD_LABEL}=${input.buildId}`,
|
|
380
|
+
"--label",
|
|
381
|
+
`${RUNNER_LABEL}=${input.runnerId}`,
|
|
382
|
+
"--network",
|
|
383
|
+
"none",
|
|
384
|
+
"--cap-drop",
|
|
385
|
+
"ALL",
|
|
386
|
+
"--security-opt",
|
|
387
|
+
"no-new-privileges",
|
|
388
|
+
"--read-only",
|
|
389
|
+
"--user",
|
|
390
|
+
input.user,
|
|
391
|
+
// The same figure for swap as for memory leaves the container none.
|
|
392
|
+
"--memory",
|
|
393
|
+
memory,
|
|
394
|
+
"--memory-swap",
|
|
395
|
+
memory,
|
|
396
|
+
"--cpus",
|
|
397
|
+
String(cpus),
|
|
398
|
+
"--pids-limit",
|
|
399
|
+
String(pids)
|
|
400
|
+
];
|
|
401
|
+
}
|
|
402
|
+
function mountPath(path) {
|
|
403
|
+
if (/[",]/.test(path)) throw new Error(`${path} holds a comma or a quote, which a Docker mount cannot carry; move the runner's config file`);
|
|
404
|
+
return path;
|
|
405
|
+
}
|
|
406
|
+
function containerRunArgs(input) {
|
|
407
|
+
return [
|
|
408
|
+
"--detach",
|
|
409
|
+
"--name",
|
|
410
|
+
input.container,
|
|
411
|
+
...hardened(input),
|
|
412
|
+
"--mount",
|
|
413
|
+
`type=volume,src=${input.volume},dst=/build`,
|
|
414
|
+
// `/tmp` is a directory of the same volume, so it is measured and limited with the rest of the build.
|
|
415
|
+
"--mount",
|
|
416
|
+
`type=volume,src=${input.volume},dst=/tmp,volume-subpath=tmp`,
|
|
417
|
+
"--mount",
|
|
418
|
+
`type=bind,src=${mountPath(input.buildDir)},dst=${JOB_MOUNT},readonly`,
|
|
419
|
+
"--entrypoint",
|
|
420
|
+
"/opt/outerlayer/bin/tini",
|
|
421
|
+
input.image,
|
|
422
|
+
"--",
|
|
423
|
+
"/opt/outerlayer/bin/node",
|
|
424
|
+
"/opt/outerlayer/cli/dist/index.js",
|
|
425
|
+
"broker-relay",
|
|
426
|
+
"--port",
|
|
427
|
+
String(RELAY_PORT),
|
|
428
|
+
"--socket",
|
|
429
|
+
`${JOB_MOUNT}/tunnel.sock`,
|
|
430
|
+
...Array.from({ length: input.destinations ?? 0 }, (_unused, index) => [
|
|
431
|
+
"--forward",
|
|
432
|
+
`${DESTINATION_BASE_PORT + index}=${JOB_MOUNT}/dest-${index}.sock`
|
|
433
|
+
]).flat(),
|
|
434
|
+
...[
|
|
435
|
+
"--forward",
|
|
436
|
+
`${GATEWAY_PORT}=${JOB_MOUNT}/${GATEWAY_SOCKET}`,
|
|
437
|
+
"--forward",
|
|
438
|
+
`${CLAUDE_PORT}=${JOB_MOUNT}/${CLAUDE_SOCKET}`
|
|
439
|
+
]
|
|
440
|
+
];
|
|
441
|
+
}
|
|
442
|
+
function prepareRunArgs(input) {
|
|
443
|
+
return [
|
|
444
|
+
"--rm",
|
|
445
|
+
...hardened(input),
|
|
446
|
+
"--mount",
|
|
447
|
+
`type=volume,src=${input.volume},dst=/build`,
|
|
448
|
+
"--entrypoint",
|
|
449
|
+
"mkdir",
|
|
450
|
+
input.image,
|
|
451
|
+
"-p",
|
|
452
|
+
"-m",
|
|
453
|
+
"1777",
|
|
454
|
+
"/build/tmp",
|
|
455
|
+
"/build/home",
|
|
456
|
+
"/build/work"
|
|
457
|
+
];
|
|
458
|
+
}
|
|
459
|
+
function containerExecArgs(input) {
|
|
460
|
+
const literals = Object.keys(input.env.literals).sort().flatMap((name) => ["-e", `${name}=${input.env.literals[name]}`]);
|
|
461
|
+
const passed = Object.keys(input.env.passed).filter((name) => input.env.passed[name] !== void 0).sort().flatMap((name) => ["-e", name]);
|
|
462
|
+
return [
|
|
463
|
+
"-u",
|
|
464
|
+
input.user,
|
|
465
|
+
...input.workdir === void 0 ? [] : ["-w", input.workdir],
|
|
466
|
+
...literals,
|
|
467
|
+
...passed,
|
|
468
|
+
input.container,
|
|
469
|
+
...input.argv
|
|
470
|
+
];
|
|
471
|
+
}
|
|
359
472
|
var AssetError = class extends Error {
|
|
360
473
|
constructor(file, message) {
|
|
361
474
|
super(message);
|
|
@@ -496,7 +609,7 @@ function converterArgs(dest, extra, script) {
|
|
|
496
609
|
}
|
|
497
610
|
var firstLine2 = (text) => text.trim().split("\n")[0] ?? "";
|
|
498
611
|
function exportInto(container, converter) {
|
|
499
|
-
return new Promise((
|
|
612
|
+
return new Promise((resolve2, reject) => {
|
|
500
613
|
const exporter = spawn("docker", ["export", container], { stdio: ["ignore", "pipe", "pipe"], env: childEnvironment(process.env) });
|
|
501
614
|
const writer = spawn("docker", converter, { stdio: ["pipe", "ignore", "pipe"], env: childEnvironment(process.env) });
|
|
502
615
|
const errors = [];
|
|
@@ -513,7 +626,7 @@ function exportInto(container, converter) {
|
|
|
513
626
|
if (code !== 0 && failure === void 0) failure = `${name} exited ${code}`;
|
|
514
627
|
exits += 1;
|
|
515
628
|
if (exits < 2) return;
|
|
516
|
-
if (failure === void 0)
|
|
629
|
+
if (failure === void 0) resolve2();
|
|
517
630
|
else reject(new Error(`${failure}: ${firstLine2(errors.join("\n"))}`));
|
|
518
631
|
};
|
|
519
632
|
exporter.once("exit", (code) => done("docker export", code));
|
|
@@ -660,7 +773,7 @@ function createFirecrackerHost(options = {}) {
|
|
|
660
773
|
const failed = new Promise((_resolve, reject) => child.once("error", reject));
|
|
661
774
|
const pid = child.pid;
|
|
662
775
|
if (pid === void 0) return await failed;
|
|
663
|
-
exits.set(pid, new Promise((
|
|
776
|
+
exits.set(pid, new Promise((resolve2) => child.once("exit", () => resolve2())));
|
|
664
777
|
child.unref();
|
|
665
778
|
const start = startTimeOf(pid);
|
|
666
779
|
if (start === null) throw new Error(`${command} exited as soon as it started; its console is at ${input.consolePath}`);
|
|
@@ -677,8 +790,8 @@ function createFirecrackerHost(options = {}) {
|
|
|
677
790
|
}
|
|
678
791
|
const exited = exits.get(vm.pid);
|
|
679
792
|
const deadline = Date.now() + STOP_WAIT_MS;
|
|
680
|
-
if (exited !== void 0) await Promise.race([exited, new Promise((
|
|
681
|
-
while (alive(vm) && Date.now() < deadline) await new Promise((
|
|
793
|
+
if (exited !== void 0) await Promise.race([exited, new Promise((resolve2) => setTimeout(resolve2, STOP_WAIT_MS))]);
|
|
794
|
+
while (alive(vm) && Date.now() < deadline) await new Promise((resolve2) => setTimeout(resolve2, POLL_MS));
|
|
682
795
|
exits.delete(vm.pid);
|
|
683
796
|
},
|
|
684
797
|
alive
|
|
@@ -688,7 +801,7 @@ function createFirecrackerHost(options = {}) {
|
|
|
688
801
|
// src/runner/microvm/host.ts
|
|
689
802
|
function createMicroVmPorts(over = {}) {
|
|
690
803
|
return {
|
|
691
|
-
assets: over.assets ?? ((
|
|
804
|
+
assets: over.assets ?? ((runnerDir2) => ensureAssets({ runnerDir: runnerDir2 })),
|
|
692
805
|
rootfs: over.rootfs ?? createRootfsHost(),
|
|
693
806
|
firecracker: over.firecracker ?? createFirecrackerHost(),
|
|
694
807
|
exec: over.exec ?? execCapture
|
|
@@ -845,104 +958,6 @@ function removeStagedContext(buildDir2) {
|
|
|
845
958
|
rmSync(stagedContextDir(buildDir2), { recursive: true, force: true });
|
|
846
959
|
}
|
|
847
960
|
|
|
848
|
-
// src/runner/container/run-args.ts
|
|
849
|
-
var BUILD_LABEL = "ai.outerlayer.build";
|
|
850
|
-
function hardened(input) {
|
|
851
|
-
const { memory, cpus, pids } = input.limits;
|
|
852
|
-
return [
|
|
853
|
-
"--label",
|
|
854
|
-
`${BUILD_LABEL}=${input.buildId}`,
|
|
855
|
-
"--network",
|
|
856
|
-
"none",
|
|
857
|
-
"--cap-drop",
|
|
858
|
-
"ALL",
|
|
859
|
-
"--security-opt",
|
|
860
|
-
"no-new-privileges",
|
|
861
|
-
"--read-only",
|
|
862
|
-
"--user",
|
|
863
|
-
input.user,
|
|
864
|
-
// The same figure for swap as for memory leaves the container none.
|
|
865
|
-
"--memory",
|
|
866
|
-
memory,
|
|
867
|
-
"--memory-swap",
|
|
868
|
-
memory,
|
|
869
|
-
"--cpus",
|
|
870
|
-
String(cpus),
|
|
871
|
-
"--pids-limit",
|
|
872
|
-
String(pids)
|
|
873
|
-
];
|
|
874
|
-
}
|
|
875
|
-
function mountPath(path) {
|
|
876
|
-
if (/[",]/.test(path)) throw new Error(`${path} holds a comma or a quote, which a Docker mount cannot carry; move the runner's config file`);
|
|
877
|
-
return path;
|
|
878
|
-
}
|
|
879
|
-
function containerRunArgs(input) {
|
|
880
|
-
return [
|
|
881
|
-
"--detach",
|
|
882
|
-
"--name",
|
|
883
|
-
input.container,
|
|
884
|
-
...hardened(input),
|
|
885
|
-
"--mount",
|
|
886
|
-
`type=volume,src=${input.volume},dst=/build`,
|
|
887
|
-
// `/tmp` is a directory of the same volume, so it is measured and limited with the rest of the build.
|
|
888
|
-
"--mount",
|
|
889
|
-
`type=volume,src=${input.volume},dst=/tmp,volume-subpath=tmp`,
|
|
890
|
-
"--mount",
|
|
891
|
-
`type=bind,src=${mountPath(input.buildDir)},dst=${JOB_MOUNT},readonly`,
|
|
892
|
-
"--entrypoint",
|
|
893
|
-
"/opt/outerlayer/bin/tini",
|
|
894
|
-
input.image,
|
|
895
|
-
"--",
|
|
896
|
-
"/opt/outerlayer/bin/node",
|
|
897
|
-
"/opt/outerlayer/cli/dist/index.js",
|
|
898
|
-
"broker-relay",
|
|
899
|
-
"--port",
|
|
900
|
-
String(RELAY_PORT),
|
|
901
|
-
"--socket",
|
|
902
|
-
`${JOB_MOUNT}/tunnel.sock`,
|
|
903
|
-
...Array.from({ length: input.destinations ?? 0 }, (_unused, index) => [
|
|
904
|
-
"--forward",
|
|
905
|
-
`${DESTINATION_BASE_PORT + index}=${JOB_MOUNT}/dest-${index}.sock`
|
|
906
|
-
]).flat(),
|
|
907
|
-
...[
|
|
908
|
-
"--forward",
|
|
909
|
-
`${GATEWAY_PORT}=${JOB_MOUNT}/${GATEWAY_SOCKET}`,
|
|
910
|
-
"--forward",
|
|
911
|
-
`${CLAUDE_PORT}=${JOB_MOUNT}/${CLAUDE_SOCKET}`
|
|
912
|
-
]
|
|
913
|
-
];
|
|
914
|
-
}
|
|
915
|
-
function prepareRunArgs(input) {
|
|
916
|
-
return [
|
|
917
|
-
"--rm",
|
|
918
|
-
...hardened(input),
|
|
919
|
-
"--mount",
|
|
920
|
-
`type=volume,src=${input.volume},dst=/build`,
|
|
921
|
-
"--entrypoint",
|
|
922
|
-
"mkdir",
|
|
923
|
-
input.image,
|
|
924
|
-
"-p",
|
|
925
|
-
"-m",
|
|
926
|
-
"1777",
|
|
927
|
-
"/build/tmp",
|
|
928
|
-
"/build/home",
|
|
929
|
-
"/build/work"
|
|
930
|
-
];
|
|
931
|
-
}
|
|
932
|
-
function containerExecArgs(input) {
|
|
933
|
-
const literals = Object.keys(input.env.literals).sort().flatMap((name) => ["-e", `${name}=${input.env.literals[name]}`]);
|
|
934
|
-
const passed = Object.keys(input.env.passed).filter((name) => input.env.passed[name] !== void 0).sort().flatMap((name) => ["-e", name]);
|
|
935
|
-
return [
|
|
936
|
-
"-u",
|
|
937
|
-
input.user,
|
|
938
|
-
...input.workdir === void 0 ? [] : ["-w", input.workdir],
|
|
939
|
-
...literals,
|
|
940
|
-
...passed,
|
|
941
|
-
input.container,
|
|
942
|
-
...input.argv
|
|
943
|
-
];
|
|
944
|
-
}
|
|
945
|
-
|
|
946
961
|
// src/runner/container/docker.ts
|
|
947
962
|
function createContainerHost(deps = {}) {
|
|
948
963
|
const run = deps.run ?? spawnRun;
|
|
@@ -1027,9 +1042,9 @@ async function git(args, cwd, env, input) {
|
|
|
1027
1042
|
if (size < maxOutputBytes) out.push(chunk);
|
|
1028
1043
|
size += chunk.length;
|
|
1029
1044
|
});
|
|
1030
|
-
const closed = new Promise((
|
|
1045
|
+
const closed = new Promise((resolve2, reject) => {
|
|
1031
1046
|
child.once("error", reject);
|
|
1032
|
-
child.once("close", (status) =>
|
|
1047
|
+
child.once("close", (status) => resolve2(status));
|
|
1033
1048
|
});
|
|
1034
1049
|
if (input !== void 0) {
|
|
1035
1050
|
await pipeline(input, child.stdin).catch(() => void 0);
|
|
@@ -1226,14 +1241,14 @@ async function drain(req) {
|
|
|
1226
1241
|
}
|
|
1227
1242
|
}
|
|
1228
1243
|
function readPrefix(req, gzip) {
|
|
1229
|
-
return new Promise((
|
|
1244
|
+
return new Promise((resolve2, reject) => {
|
|
1230
1245
|
const held = [];
|
|
1231
1246
|
const finish = (section) => {
|
|
1232
1247
|
req.off("data", onData);
|
|
1233
1248
|
req.off("end", onEnd);
|
|
1234
1249
|
req.off("error", reject);
|
|
1235
1250
|
req.pause();
|
|
1236
|
-
|
|
1251
|
+
resolve2({ held, section });
|
|
1237
1252
|
};
|
|
1238
1253
|
const onData = (chunk) => {
|
|
1239
1254
|
held.push(chunk);
|
|
@@ -1247,19 +1262,19 @@ function readPrefix(req, gzip) {
|
|
|
1247
1262
|
});
|
|
1248
1263
|
}
|
|
1249
1264
|
function readBody(req, limit) {
|
|
1250
|
-
return new Promise((
|
|
1265
|
+
return new Promise((resolve2, reject) => {
|
|
1251
1266
|
const chunks = [];
|
|
1252
1267
|
let size = 0;
|
|
1253
1268
|
req.on("data", (chunk) => {
|
|
1254
1269
|
size += chunk.length;
|
|
1255
1270
|
if (size > limit) {
|
|
1256
1271
|
req.pause();
|
|
1257
|
-
|
|
1272
|
+
resolve2(void 0);
|
|
1258
1273
|
return;
|
|
1259
1274
|
}
|
|
1260
1275
|
chunks.push(chunk);
|
|
1261
1276
|
});
|
|
1262
|
-
req.once("end", () =>
|
|
1277
|
+
req.once("end", () => resolve2(Buffer.concat(chunks)));
|
|
1263
1278
|
req.once("error", reject);
|
|
1264
1279
|
});
|
|
1265
1280
|
}
|
|
@@ -1272,7 +1287,7 @@ function createGitRemote(options) {
|
|
|
1272
1287
|
const { route, res } = input;
|
|
1273
1288
|
const url = new URL(`${gitUrl}/${route.owner}/${route.name}.git/${route.kind === "info-refs" ? "info/refs" : route.kind === "lfs-batch" ? "info/lfs/objects/batch" : `git-${route.kind}`}${route.search === "" ? "" : `?${route.search}`}`);
|
|
1274
1289
|
const send = url.protocol === "https:" ? request$1 : request;
|
|
1275
|
-
return new Promise((
|
|
1290
|
+
return new Promise((resolve2) => {
|
|
1276
1291
|
const upstream = send(url, { method: input.method, headers: { ...input.headers, connection: "close" } }, (answer) => {
|
|
1277
1292
|
const headers = {};
|
|
1278
1293
|
for (const [name, value] of Object.entries(answer.headers)) {
|
|
@@ -1294,18 +1309,18 @@ function createGitRemote(options) {
|
|
|
1294
1309
|
});
|
|
1295
1310
|
answer.once("end", () => {
|
|
1296
1311
|
res.end();
|
|
1297
|
-
|
|
1312
|
+
resolve2();
|
|
1298
1313
|
});
|
|
1299
1314
|
answer.once("error", () => {
|
|
1300
1315
|
res.destroy();
|
|
1301
|
-
|
|
1316
|
+
resolve2();
|
|
1302
1317
|
});
|
|
1303
1318
|
});
|
|
1304
1319
|
upstream.on("error", (err) => {
|
|
1305
1320
|
log(`could not reach GitHub for ${route.kind}: ${err.message}`);
|
|
1306
1321
|
if (res.headersSent) res.destroy();
|
|
1307
1322
|
else plain(res, 502, "the runner could not reach GitHub\n");
|
|
1308
|
-
|
|
1323
|
+
resolve2();
|
|
1309
1324
|
});
|
|
1310
1325
|
res.once("close", () => upstream.destroy());
|
|
1311
1326
|
input.feed(upstream);
|
|
@@ -1594,7 +1609,7 @@ async function startTunnel(socketPath, opts = {}) {
|
|
|
1594
1609
|
if (Buffer.byteLength(socketPath) > MAX_SOCKET_PATH_BYTES) {
|
|
1595
1610
|
throw new Error(`${socketPath} is too long for a Unix socket; keep the runner's config file nearer the root of the filesystem`);
|
|
1596
1611
|
}
|
|
1597
|
-
const
|
|
1612
|
+
const resolve2 = opts.resolve ?? systemResolve;
|
|
1598
1613
|
const openConnection = opts.connect ?? ((t) => connect({ host: t.address, port: t.port }));
|
|
1599
1614
|
const ownAddresses = opts.hostAddresses ?? systemHostAddresses;
|
|
1600
1615
|
const connectTimeoutMs = opts.connectTimeoutMs ?? CONNECT_TIMEOUT_MS;
|
|
@@ -1678,7 +1693,7 @@ async function startTunnel(socketPath, opts = {}) {
|
|
|
1678
1693
|
}
|
|
1679
1694
|
let addresses;
|
|
1680
1695
|
try {
|
|
1681
|
-
addresses = await
|
|
1696
|
+
addresses = await resolve2(host);
|
|
1682
1697
|
} catch {
|
|
1683
1698
|
return { ok: false, status: 502, body: `could not resolve ${host}
|
|
1684
1699
|
` };
|
|
@@ -1863,7 +1878,7 @@ function createContainerRuntime(opts = {}) {
|
|
|
1863
1878
|
)),
|
|
1864
1879
|
startTunnel: opts.startTunnel ?? startTunnel,
|
|
1865
1880
|
tunnelOptions: opts.tunnelOptions,
|
|
1866
|
-
sleep: opts.sleep ?? ((ms) => new Promise((
|
|
1881
|
+
sleep: opts.sleep ?? ((ms) => new Promise((resolve2) => setTimeout(resolve2, ms))),
|
|
1867
1882
|
hostEnv: opts.hostEnv ?? process.env,
|
|
1868
1883
|
credentials: opts.credentials ?? /* @__PURE__ */ new Map(),
|
|
1869
1884
|
github: { gitUrl: opts.github?.gitUrl ?? "https://github.com", apiUrl: opts.github?.apiUrl ?? "https://api.github.com" },
|
|
@@ -2035,11 +2050,13 @@ async function provisionContainer(input) {
|
|
|
2035
2050
|
return bail(`the tunnel could not start: ${describeCallFailure(err)}`);
|
|
2036
2051
|
}
|
|
2037
2052
|
const limits2 = { memory: config.build?.memory ?? "8g", cpus: config.build?.cpus ?? 4, pids: config.build?.pids ?? 4096 };
|
|
2038
|
-
const
|
|
2053
|
+
const runnerId = runnerLabelValue(config.configPath);
|
|
2054
|
+
const volume = await host.createVolume(name, { [BUILD_LABEL]: buildId, [RUNNER_LABEL]: runnerId });
|
|
2039
2055
|
if (volume.status !== 0) return dockerFailed("creating the build's volume", volume);
|
|
2040
2056
|
if (input.aborted()) return bail("", true);
|
|
2041
2057
|
const runInput = {
|
|
2042
2058
|
buildId,
|
|
2059
|
+
runnerId,
|
|
2043
2060
|
container: name,
|
|
2044
2061
|
volume: name,
|
|
2045
2062
|
buildDir: buildDir2,
|
|
@@ -2230,4 +2247,4 @@ async function cleanupContainer(runtime, state, jobDir) {
|
|
|
2230
2247
|
if (failures.length > 0) throw new Error(failures.join("; "));
|
|
2231
2248
|
}
|
|
2232
2249
|
|
|
2233
|
-
export { ADDED_USER, AgentUserError, AssetError, BuildTokens, GH_CONFIG_YML, MIN_ENGINE_MAJOR, classifyRefusal, cleanupContainer, containerCommandLine, createBuildDisk, createContainerRuntime, createRunnerLog, deleteBuildDisk, describeCallFailure, ensureRootfs, ensureTunnel, formatClock, formatDuration, ghHostsYml, gitRemoteFor, isLiteralAddress, keepGhConfigCurrent, lifecycleFailureReason, lifecycleSteps, machineConfig, measureDisk, obtainImage, prepareBranch, provisionContainer, refusalCode, relativeToRunner, removeStagedContext, resolveAgentUser, retrySend, runBuildSteps, shellQuote, shortClaim, stageGovernedContext, startServicesFor, stopContainer, syncInContainer };
|
|
2250
|
+
export { ADDED_USER, AgentUserError, AssetError, BUILD_LABEL, BuildTokens, GH_CONFIG_YML, MIN_ENGINE_MAJOR, RUNNER_LABEL, classifyRefusal, cleanupContainer, containerCommandLine, createBuildDisk, createContainerRuntime, createRunnerLog, deleteBuildDisk, describeCallFailure, ensureRootfs, ensureTunnel, formatClock, formatDuration, ghHostsYml, gitRemoteFor, isLiteralAddress, keepGhConfigCurrent, lifecycleFailureReason, lifecycleSteps, machineConfig, measureDisk, obtainImage, prepareBranch, provisionContainer, refusalCode, relativeToRunner, removeStagedContext, resolveAgentUser, retrySend, runBuildSteps, runnerLabelValue, shellQuote, shortClaim, stageGovernedContext, startServicesFor, stopContainer, syncInContainer };
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
3
|
+
import { RUNNER_DIR_MODE, RUNNER_FILE_MODE } from './chunk-ZM2IMMYN.js';
|
|
4
|
+
import { readFileSync, mkdirSync, writeFileSync, renameSync } from 'fs';
|
|
5
|
+
import { join } from 'path';
|
|
6
|
+
|
|
7
|
+
createRequire(import.meta.url);
|
|
8
|
+
var FILE = "housekeeping.json";
|
|
9
|
+
function readHousekeepingRecord(runnerDirPath) {
|
|
10
|
+
const path = join(runnerDirPath, FILE);
|
|
11
|
+
try {
|
|
12
|
+
const raw = JSON.parse(readFileSync(path, "utf8"));
|
|
13
|
+
const record = {};
|
|
14
|
+
if (typeof raw.lastRunAt === "string") record.lastRunAt = raw.lastRunAt;
|
|
15
|
+
if (typeof raw.freedBytes === "number") record.freedBytes = raw.freedBytes;
|
|
16
|
+
const holding = raw.holding;
|
|
17
|
+
if (typeof holding?.free === "number" && typeof holding.needed === "number" && typeof holding.since === "string") {
|
|
18
|
+
record.holding = { free: holding.free, needed: holding.needed, since: holding.since };
|
|
19
|
+
}
|
|
20
|
+
return record;
|
|
21
|
+
} catch {
|
|
22
|
+
return {};
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function writeHousekeepingRecord(runnerDirPath, patch) {
|
|
26
|
+
const merged = { ...readHousekeepingRecord(runnerDirPath), ...patch };
|
|
27
|
+
mkdirSync(runnerDirPath, { recursive: true, mode: RUNNER_DIR_MODE });
|
|
28
|
+
const path = join(runnerDirPath, FILE);
|
|
29
|
+
const temp = `${path}.partial-${process.pid}`;
|
|
30
|
+
writeFileSync(temp, JSON.stringify(merged, null, 2), { mode: RUNNER_FILE_MODE });
|
|
31
|
+
renameSync(temp, path);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export { readHousekeepingRecord, writeHousekeepingRecord };
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
3
|
import { DOCKERD_LOG } from './chunk-CBPPJSAR.js';
|
|
4
4
|
import { installScrubContext } from './chunk-ZOC7JSYC.js';
|
|
5
|
-
import { AssetError, describeCallFailure, obtainImage, ensureRootfs, gitRemoteFor, startServicesFor, createBuildDisk, machineConfig, refusalCode, resolveAgentUser, AgentUserError, runBuildSteps, removeStagedContext, stageGovernedContext, shellQuote, ADDED_USER, createRunnerLog, createContainerRuntime, stopContainer, syncInContainer, formatDuration, cleanupContainer, retrySend, formatClock, GH_CONFIG_YML, ghHostsYml, isLiteralAddress, deleteBuildDisk } from './chunk-
|
|
5
|
+
import { AssetError, describeCallFailure, obtainImage, ensureRootfs, gitRemoteFor, startServicesFor, createBuildDisk, machineConfig, refusalCode, resolveAgentUser, AgentUserError, runBuildSteps, removeStagedContext, stageGovernedContext, shellQuote, ADDED_USER, createRunnerLog, createContainerRuntime, stopContainer, syncInContainer, formatDuration, cleanupContainer, retrySend, formatClock, GH_CONFIG_YML, ghHostsYml, isLiteralAddress, deleteBuildDisk } from './chunk-T52PKE2Y.js';
|
|
6
6
|
import { CLAUDE_CODE_VERSION } from './chunk-Z62HNKEJ.js';
|
|
7
7
|
import { isSameProcess, startTimeOf } from './chunk-N5FOE5PS.js';
|
|
8
8
|
import { childEnvironment } from './chunk-774HEQL6.js';
|
|
@@ -11,7 +11,7 @@ import { socketPathProblem, vsockLinks, linkVsockSockets } from './chunk-B2G7JHH
|
|
|
11
11
|
import { WorkCommandError, postJson, isSignatureRefusal, getJson } from './chunk-RMAZ2NWA.js';
|
|
12
12
|
import { planArtifactUpload } from './chunk-WOBNRVQT.js';
|
|
13
13
|
import { scrubText } from './chunk-EX6M5JPR.js';
|
|
14
|
-
import { isBuiltinContainer, isBuiltinProcess, isBuiltinVm } from './chunk-
|
|
14
|
+
import { isBuiltinContainer, isBuiltinProcess, isBuiltinVm } from './chunk-3DD6AOWC.js';
|
|
15
15
|
import { parseSize } from './chunk-F6GFZXZ3.js';
|
|
16
16
|
import { claudeCredential, buildEnvironment, JOB_MOUNT, CHECKOUT_DIR, parseIsolation, proxyEnvironment, containerLocalAddress, destinationEnvironment, CLAUDE_CREDENTIAL_VARIABLES, builtInEnvironment, withHooksPath, startBuildServices, BUILT_IN_NAMES, AGENT_CREDENTIAL_MISSING, sharedUserEnvironment, headerVariables, authorEnvironment, withTunnelRecord, commitIdentityRecord, RELAY_PORT } from './chunk-AUFG23AA.js';
|
|
17
17
|
import { readCloudConfig } from './chunk-EABW6AJQ.js';
|
|
@@ -410,6 +410,8 @@ async function stopOrphanedMicroVms(input) {
|
|
|
410
410
|
process.kill(-job.pid, "SIGKILL");
|
|
411
411
|
} catch {
|
|
412
412
|
}
|
|
413
|
+
const deadline = Date.now() + 2e3;
|
|
414
|
+
while (isSameProcess(job.pid, job.start) && Date.now() < deadline) await new Promise((resolve) => setTimeout(resolve, 10));
|
|
413
415
|
}
|
|
414
416
|
if (state.firecracker !== void 0 && input.runtime.vm.firecracker.alive(state.firecracker)) {
|
|
415
417
|
await input.runtime.vm.firecracker.stop(state.firecracker);
|
|
@@ -581,18 +583,6 @@ async function closeHostBuild(runtime, build) {
|
|
|
581
583
|
rmSync(build.buildDir, { recursive: true, force: true });
|
|
582
584
|
if (build.workDir !== void 0) rmSync(build.workDir, { recursive: true, force: true });
|
|
583
585
|
}
|
|
584
|
-
function readJobFile(path) {
|
|
585
|
-
if (!existsSync(path)) return null;
|
|
586
|
-
try {
|
|
587
|
-
return JSON.parse(readFileSync(path, "utf8"));
|
|
588
|
-
} catch {
|
|
589
|
-
return null;
|
|
590
|
-
}
|
|
591
|
-
}
|
|
592
|
-
function writeJobFile(path, job) {
|
|
593
|
-
mkdirSync(dirname(path), { recursive: true, mode: RUNNER_DIR_MODE });
|
|
594
|
-
writeFileSync(path, JSON.stringify(job, null, 2), { mode: RUNNER_FILE_MODE });
|
|
595
|
-
}
|
|
596
586
|
var ENDED = "ended before provision finished";
|
|
597
587
|
async function provisionProcess(input) {
|
|
598
588
|
const { config, runtime, item, jobDir } = input;
|
|
@@ -635,6 +625,28 @@ async function provisionProcess(input) {
|
|
|
635
625
|
function cleanupProcess(runtime, state) {
|
|
636
626
|
return closeHostBuild(runtime, state);
|
|
637
627
|
}
|
|
628
|
+
function readJobFile(path) {
|
|
629
|
+
if (!existsSync(path)) return null;
|
|
630
|
+
try {
|
|
631
|
+
return JSON.parse(readFileSync(path, "utf8"));
|
|
632
|
+
} catch {
|
|
633
|
+
return null;
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
function readJobFileChecked(path) {
|
|
637
|
+
if (!existsSync(path)) return { kind: "missing" };
|
|
638
|
+
try {
|
|
639
|
+
const parsed = JSON.parse(readFileSync(path, "utf8"));
|
|
640
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return { kind: "unreadable" };
|
|
641
|
+
return { kind: "ok", job: parsed };
|
|
642
|
+
} catch {
|
|
643
|
+
return { kind: "unreadable" };
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
function writeJobFile(path, job) {
|
|
647
|
+
mkdirSync(dirname(path), { recursive: true, mode: RUNNER_DIR_MODE });
|
|
648
|
+
writeFileSync(path, JSON.stringify(job, null, 2), { mode: RUNNER_FILE_MODE });
|
|
649
|
+
}
|
|
638
650
|
|
|
639
651
|
// src/runner/verdict.ts
|
|
640
652
|
var VERDICT_SETTLE_MS = 5 * 6e4;
|
|
@@ -1458,4 +1470,4 @@ async function recoverLostJobs(jobsDirPath, config, deps) {
|
|
|
1458
1470
|
return recovered;
|
|
1459
1471
|
}
|
|
1460
1472
|
|
|
1461
|
-
export { LEASE_SECONDS, RENEW_INTERVAL_SECONDS, deliverCommitHooks, describeHookEnd, execArgv, execCommandLine, finishJob, hookLimitsFor, hostCommandEnvironment, hostEnvironment, isGroupAlive, isIdle, isTimedOut, isUnfinished, jobEnv, listAdoptableJobs, listenOnBroker, microVmCommandLine, needsRenew, outcomeFromExitCode, provisionEnvironment, provisionMicroVm, provisionProcess, readCpuUsec, readJobFile, readProvisionOut, recoverLostJobs, releaseLease, renewLease, resolveHostBuild, retryFailedReleases, runCommand, runHook, setStage, startHostServices, stopOrphanedMicroVms, unfinishedJobs, workdirProblem, writeJobFile };
|
|
1473
|
+
export { ALREADY_RELEASED, LEASE_SECONDS, RENEW_INTERVAL_SECONDS, deliverCommitHooks, describeHookEnd, execArgv, execCommandLine, finishJob, hookLimitsFor, hostCommandEnvironment, hostEnvironment, isGroupAlive, isIdle, isTimedOut, isUnfinished, jobEnv, listAdoptableJobs, listenOnBroker, microVmCommandLine, needsRenew, outcomeFromExitCode, provisionEnvironment, provisionMicroVm, provisionProcess, readCpuUsec, readJobFile, readJobFileChecked, readProvisionOut, recoverLostJobs, releaseLease, renewLease, resolveHostBuild, retryFailedReleases, runCommand, runHook, setStage, startHostServices, stopOrphanedMicroVms, unfinishedJobs, workdirProblem, writeJobFile };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
|
-
import { detectMachine, supportsContainerBuilds, reachDockerStep, describeMachineDocker, supportsMicroVmBuilds } from './chunk-
|
|
4
|
-
import { MIN_ENGINE_MAJOR } from './chunk-
|
|
3
|
+
import { detectMachine, supportsContainerBuilds, reachDockerStep, describeMachineDocker, supportsMicroVmBuilds } from './chunk-RTXKYLCA.js';
|
|
4
|
+
import { MIN_ENGINE_MAJOR } from './chunk-T52PKE2Y.js';
|
|
5
5
|
import { RUNNER_DIR_MODE, runnerDir, RUNNER_FILE_MODE } from './chunk-ZM2IMMYN.js';
|
|
6
6
|
import { existsSync, readFileSync, mkdirSync, writeFileSync } from 'fs';
|
|
7
7
|
import { dirname } from 'path';
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
3
|
+
import { statfsSync } from 'fs';
|
|
4
|
+
import { dirname } from 'path';
|
|
5
|
+
|
|
6
|
+
createRequire(import.meta.url);
|
|
7
|
+
function freeBytes(path, statfs = statfsSync) {
|
|
8
|
+
let at = path;
|
|
9
|
+
for (; ; ) {
|
|
10
|
+
try {
|
|
11
|
+
const stats = statfs(at);
|
|
12
|
+
return Number(stats.bavail) * Number(stats.bsize);
|
|
13
|
+
} catch (err) {
|
|
14
|
+
if (err.code !== "ENOENT") return void 0;
|
|
15
|
+
const parent = dirname(at);
|
|
16
|
+
if (parent === at) return void 0;
|
|
17
|
+
at = parent;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
var UNITS = ["B", "KB", "MB", "GB", "TB"];
|
|
22
|
+
function formatBytes(bytes) {
|
|
23
|
+
let value = Math.max(0, bytes);
|
|
24
|
+
let unit = 0;
|
|
25
|
+
while (value >= 1024 && unit < UNITS.length - 1) {
|
|
26
|
+
value /= 1024;
|
|
27
|
+
unit++;
|
|
28
|
+
}
|
|
29
|
+
return unit === 0 ? `${Math.round(value)} B` : `${value.toFixed(1)} ${UNITS[unit]}`;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export { formatBytes, freeBytes };
|