@outerlayer/cli 0.6.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/README.md +15 -5
  3. package/dist/{agent-setup-USZ2SAXC.js → agent-setup-C7PLRXZO.js} +7 -6
  4. package/dist/{build-PAEKVYVK.js → build-MS34XEGN.js} +3 -1
  5. package/dist/build-info.json +1 -1
  6. package/dist/builder-G52A6GW2.js +13 -0
  7. package/dist/{check-CCVPBN7A.js → check-FOF3PUKI.js} +35 -26
  8. package/dist/{chunk-ZILGTTY6.js → chunk-3S4P55NE.js} +3 -3
  9. package/dist/{chunk-MIGEDH5P.js → chunk-4A5E2G4E.js} +9 -9
  10. package/dist/chunk-4ACZCY4I.js +39 -0
  11. package/dist/{chunk-OIRNH6UO.js → chunk-5FWNEQNZ.js} +3 -3
  12. package/dist/{chunk-BNMCXPG7.js → chunk-5QLNZGAQ.js} +12 -3
  13. package/dist/chunk-5TNS3PL7.js +263 -0
  14. package/dist/chunk-7RQ73DLO.js +23 -0
  15. package/dist/{chunk-VMJ3MMBX.js → chunk-7VNURP6M.js} +538 -46
  16. package/dist/{chunk-EMY4I27X.js → chunk-7ZR5UPMB.js} +555 -5
  17. package/dist/{chunk-UMFWVGJO.js → chunk-AIL2R4SI.js} +4 -3
  18. package/dist/{chunk-NQXFAGHU.js → chunk-AX23QKHS.js} +7 -3
  19. package/dist/{chunk-RZSF5B46.js → chunk-CM5QTKM4.js} +152 -158
  20. package/dist/chunk-CX22S5BA.js +45 -0
  21. package/dist/{chunk-H5NCNVDA.js → chunk-EV3M3EH7.js} +2 -2
  22. package/dist/{chunk-ANK2B36Y.js → chunk-GAYWY4GI.js} +6190 -28615
  23. package/dist/chunk-HKMPVPO7.js +55 -0
  24. package/dist/{chunk-43J3M6KB.js → chunk-IJBVPLZZ.js} +91 -39
  25. package/dist/{chunk-PMAMOSTP.js → chunk-ILEA3YHM.js} +85 -25
  26. package/dist/{chunk-MXB5VB4Y.js → chunk-IRCBTGWF.js} +6 -6
  27. package/dist/{chunk-OUUINEYB.js → chunk-JKIWMRFH.js} +1 -1
  28. package/dist/{chunk-FA2BOX4O.js → chunk-JPR2WHHF.js} +1 -1
  29. package/dist/{chunk-IASWYRVG.js → chunk-K35RW5HP.js} +4 -1
  30. package/dist/{chunk-ZMYPLFG3.js → chunk-M3SQ4YGD.js} +1 -1
  31. package/dist/{chunk-4NLKY4WR.js → chunk-NALVDFIS.js} +2 -2
  32. package/dist/{chunk-AUFG23AA.js → chunk-OD2VTIHN.js} +19 -47
  33. package/dist/{chunk-JWOQNNN2.js → chunk-OEK4JEB3.js} +33 -4
  34. package/dist/chunk-PTW6W5NR.js +127 -0
  35. package/dist/{chunk-3DD6AOWC.js → chunk-PWBILI5P.js} +28 -3
  36. package/dist/{chunk-B2G7JHHB.js → chunk-R7STSBLC.js} +1 -1
  37. package/dist/{chunk-YYYXRJUW.js → chunk-TI7B7UEO.js} +1 -1
  38. package/dist/{chunk-Z62HNKEJ.js → chunk-TTNFGMHO.js} +72 -54
  39. package/dist/{chunk-RBOMWNOG.js → chunk-TUUMW4TC.js} +2 -2
  40. package/dist/{chunk-774HEQL6.js → chunk-U3GOI2B4.js} +91 -8
  41. package/dist/{chunk-F6GFZXZ3.js → chunk-UXYLWVTQ.js} +1 -18
  42. package/dist/{chunk-WO2BXCTQ.js → chunk-VXLJORHL.js} +1 -1
  43. package/dist/{chunk-CBPPJSAR.js → chunk-WVGSB3Z2.js} +2 -2
  44. package/dist/chunk-YNJEOGDL.js +22434 -0
  45. package/dist/{chunk-W4SQCOZU.js → chunk-ZOAEPWGL.js} +1 -1
  46. package/dist/{chunk-3DEVYTNQ.js → chunk-ZYHKJKSU.js} +1 -1
  47. package/dist/{chunk-WIOZAJ2W.js → chunk-ZZXLM64C.js} +1 -1
  48. package/dist/{cli-7WCNPBYC.js → cli-AOJPAVFX.js} +109 -81
  49. package/dist/{command-ARFK3OYF.js → command-SY3SVLAI.js} +67 -27
  50. package/dist/config-Z3USDBLU.js +11 -0
  51. package/dist/{connect-cmd-PCESZLTR.js → connect-cmd-PNZ6IJLJ.js} +4 -4
  52. package/dist/{context-adopt-G5JAELE7.js → context-adopt-CE4KDXS3.js} +5 -4
  53. package/dist/{context-materialize-3HLPZAF6.js → context-materialize-QL33HDJN.js} +5 -4
  54. package/dist/{dist-RDLP4GGJ.js → dist-3ZTZF2TP.js} +2 -1
  55. package/dist/docker-676FD3CQ.js +15 -0
  56. package/dist/{doctor-VOMYOHQU.js → doctor-WR6TRPSU.js} +49 -19
  57. package/dist/{emit-artifact-cmd-TBF46VXZ.js → emit-artifact-cmd-AVM4YXMQ.js} +11 -4
  58. package/dist/{emit-cmd-DCERAU27.js → emit-cmd-2QRMYPYO.js} +4 -3
  59. package/dist/{emit-criteria-cmd-3427XKFW.js → emit-criteria-cmd-LDBE4YDS.js} +10 -1
  60. package/dist/{emit-finding-cmd-GSI75K36.js → emit-finding-cmd-6R5WBPCH.js} +12 -1
  61. package/dist/{emit-result-cmd-3PRQKB55.js → emit-result-cmd-2DMN6O6U.js} +13 -6
  62. package/dist/{exec-client-4XGXV3XN.js → exec-client-NPYNUAF3.js} +4 -3
  63. package/dist/{guest-init-GENRHNP7.js → guest-init-JWNNAH2H.js} +4 -3
  64. package/dist/{hook-fast-NJBBZQ5Q.js → hook-fast-XBOZ3N6L.js} +1 -1
  65. package/dist/{import-capture-cmd-AVZ4VIVJ.js → import-capture-cmd-T4556ELW.js} +3 -2
  66. package/dist/{import-ruler-cmd-GUHL6IY5.js → import-ruler-cmd-SG3UVGI7.js} +2 -1
  67. package/dist/index.js +3 -3
  68. package/dist/init-KDK5QLQZ.js +46 -0
  69. package/dist/{init-OHYFIKZD.js → init-R2FJ5KNS.js} +35 -21
  70. package/dist/{init-cmd-HMFNIFPI.js → init-cmd-BYZYAJ5P.js} +10 -9
  71. package/dist/{install-DSUZE2TJ.js → install-FHTVJZS6.js} +7 -5
  72. package/dist/{install-cmd-453C7IZT.js → install-cmd-DCF7TVXD.js} +3 -2
  73. package/dist/{logs-7RYUH5A6.js → logs-H55NWW4H.js} +1 -0
  74. package/dist/{loop-UAJB34KA.js → loop-AG3PLXPO.js} +35 -26
  75. package/dist/machine-LV6WBRRK.js +45 -0
  76. package/dist/{session-sender-ED26V45B.js → session-sender-Q5HAMOL2.js} +6 -5
  77. package/dist/{starter-pack-LIZYMKYQ.js → starter-pack-MUXMAU4S.js} +4 -3
  78. package/dist/{status-6WNXX2O7.js → status-HH4OSOHR.js} +29 -20
  79. package/dist/{sync-cmd-YONVGUYS.js → sync-cmd-CAEPJR7B.js} +5 -4
  80. package/dist/{watch-HHKAF26L.js → watch-LAAZFBHH.js} +5 -4
  81. package/dist/{work-claim-cmd-LXBZTMJA.js → work-claim-cmd-ARIYZARE.js} +15 -7
  82. package/dist/{work-cmd-OVNF44FY.js → work-cmd-B3FOXZAL.js} +9 -3
  83. package/dist/{work-comment-cmd-6DGQNTAH.js → work-comment-cmd-O5WGRI6K.js} +9 -3
  84. package/dist/{work-open-pr-cmd-FCZHFHZS.js → work-open-pr-cmd-M6CQW7KW.js} +10 -4
  85. package/dist/{work-pr-cmd-QJJVHZDC.js → work-pr-cmd-C4YEGKD6.js} +9 -2
  86. package/package.json +1 -1
  87. package/skill-pack/template/build/scripts/record-criteria.mjs +2 -1
  88. package/dist/chunk-7DBI6SNT.js +0 -55
  89. package/dist/config-HL2AIR3K.js +0 -9
  90. package/dist/docker-NEGDLU6D.js +0 -7
  91. package/dist/init-WVR4BZQ4.js +0 -36
  92. package/dist/machine-2JOWGRSZ.js +0 -35
@@ -1,24 +1,27 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- import { readDefaultBranchHead, cloneRepository, buildRepositoryImage, CLAUDE_CODE_VERSION, firecrackerArch, MICROVM_ARCHES, DEFAULT_BASE_IMAGE, GUEST_HELPER_SHA256, GUEST_ASSETS_BASE_URL, GUEST_KERNEL_SHA256, FIRECRACKER_BINARY_SHA256, FIRECRACKER_SHA256, FIRECRACKER_VERSION, GUEST_ASSETS_VERSION } from './chunk-Z62HNKEJ.js';
3
+ import { readDefaultBranchHead, cloneRepository, buildRepositoryImage } from './chunk-TTNFGMHO.js';
4
4
  import { startTimeOf, isSameProcess } from './chunk-N5FOE5PS.js';
5
5
  import { currentCliBuild } from './chunk-OGFGZQA3.js';
6
- import { createDockerHost, childEnvironment, spawnRun } from './chunk-774HEQL6.js';
6
+ import { createDockerHost, childEnvironment, spawnRun } from './chunk-U3GOI2B4.js';
7
7
  import { RecipeError } from './chunk-QOZKPLVJ.js';
8
- import { execCapture } from './chunk-W4SQCOZU.js';
9
- import { GUEST_CID } from './chunk-B2G7JHHB.js';
10
- import { WorkCommandError, postJson } from './chunk-BNMCXPG7.js';
11
- import { parseDuKilobytes, hostAllowed } from './chunk-F6GFZXZ3.js';
12
- import { RELAY_PORT, CHECKOUT_DIR, JOB_MOUNT, claudeCredential, buildEnvironment, startBuildServices, AGENT_CREDENTIAL_MISSING, GATEWAY_PORT, GATEWAY_SOCKET, CLAUDE_PORT, CLAUDE_SOCKET, DESTINATION_BASE_PORT } from './chunk-AUFG23AA.js';
13
- import { defaultGitRunner, materializeContext } from './chunk-OIRNH6UO.js';
14
- import { readContextManifest, readSourceCache } from './chunk-WIOZAJ2W.js';
8
+ import { builderSpec, runnerLabelValue, RUNNER_LABEL, BUILD_LABEL, prepareRunArgs, containerRunArgs, containerExecArgs } from './chunk-5TNS3PL7.js';
9
+ import { CLAUDE_CODE_VERSION, firecrackerArch, MICROVM_ARCHES, DEFAULT_BASE_IMAGE, GUEST_HELPER_SHA256, GUEST_ASSETS_BASE_URL, GUEST_KERNEL_SHA256, FIRECRACKER_BINARY_SHA256, FIRECRACKER_SHA256, FIRECRACKER_VERSION, GUEST_ASSETS_VERSION } from './chunk-CX22S5BA.js';
10
+ import { execCapture } from './chunk-ZOAEPWGL.js';
11
+ import { GUEST_CID } from './chunk-R7STSBLC.js';
12
+ import { WorkCommandError, postJson } from './chunk-5QLNZGAQ.js';
13
+ import { hostAllowed } from './chunk-UXYLWVTQ.js';
14
+ import { parseDuKilobytes } from './chunk-7RQ73DLO.js';
15
+ import { RELAY_PORT, CHECKOUT_DIR, JOB_MOUNT, claudeCredential, buildEnvironment, startBuildServices, checkEnvironment, AGENT_CREDENTIAL_MISSING } from './chunk-OD2VTIHN.js';
16
+ import { defaultGitRunner, materializeContext } from './chunk-5FWNEQNZ.js';
17
+ import { readContextManifest, readSourceCache } from './chunk-ZZXLM64C.js';
15
18
  import { signingFetchFor } from './chunk-BCLJSQCV.js';
16
19
  import { signerFor } from './chunk-M5POMKKW.js';
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
- import { readFileSync, existsSync, mkdirSync, chmodSync, truncateSync, rmSync, writeFileSync, accessSync, constants, readdirSync, statSync, rmdirSync, realpathSync, renameSync, lstatSync, unlinkSync, openSync, closeSync, mkdtempSync, createWriteStream, createReadStream } from 'fs';
20
+ import { rootfsPathFor, diskTemplatesDir, RUNNER_FILE_MODE, stagedContextDir, contextHomeDir, ghConfigDir, runnerDirOfBuild, spoolDir, mirrorDir, buildDir, RUNNER_DIR_MODE, vmAssetsDir } from './chunk-ZM2IMMYN.js';
21
+ import { relative, join, dirname, basename } from 'path';
22
+ import { readFileSync, existsSync, mkdirSync, chmodSync, truncateSync, rmSync, writeFileSync, accessSync, constants, readdirSync, statSync, rmdirSync, renameSync, lstatSync, unlinkSync, openSync, closeSync, mkdtempSync, createWriteStream, createReadStream } from 'fs';
20
23
  import { spawnSync, spawn } from 'child_process';
21
- import { createHash, randomBytes } from 'crypto';
24
+ import { randomBytes, createHash } from 'crypto';
22
25
  import { createServer, request } from 'http';
23
26
  import { request as request$1 } from 'https';
24
27
  import { Transform } from 'stream';
@@ -86,7 +89,7 @@ function isRetryable(err) {
86
89
  }
87
90
  async function retrySend(send, deps = {}) {
88
91
  const now = deps.now ?? (() => /* @__PURE__ */ new Date());
89
- const sleep = deps.sleep ?? ((ms) => new Promise((resolve2) => setTimeout(resolve2, ms)));
92
+ const sleep = deps.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
90
93
  const windowMs = deps.windowMs ?? RETRY_WINDOW_MS;
91
94
  const startedMs = now().getTime();
92
95
  let attempts = 0;
@@ -283,12 +286,14 @@ function prepareRunnerCgroup(setup) {
283
286
  fs.write(join(root, "cgroup.subtree_control"), "+memory");
284
287
  return { root };
285
288
  }
289
+ var BUILD_RECLAIM_HEADROOM_BYTES = 256 * 1024 ** 2;
290
+ var BUILD_KILL_HEADROOM_BYTES = 1024 ** 3;
286
291
  function createBuildCgroup(fs, root, buildId, buildMemoryBytes) {
287
292
  const path = join(root, `${BUILD_PREFIX}${buildId}`);
288
- const firecrackerOverheadBytes = 256 * 1024 ** 2;
289
293
  fs.mkdir(path);
290
294
  try {
291
- fs.write(join(path, "memory.max"), String(buildMemoryBytes + firecrackerOverheadBytes));
295
+ fs.write(join(path, "memory.high"), String(buildMemoryBytes + BUILD_RECLAIM_HEADROOM_BYTES));
296
+ fs.write(join(path, "memory.max"), String(buildMemoryBytes + BUILD_KILL_HEADROOM_BYTES));
292
297
  fs.write(join(path, "memory.swap.max"), "0");
293
298
  } catch (err) {
294
299
  removeBuildCgroup(fs, path);
@@ -296,6 +301,9 @@ function createBuildCgroup(fs, root, buildId, buildMemoryBytes) {
296
301
  }
297
302
  return path;
298
303
  }
304
+ function outOfMemoryReason(memory) {
305
+ return `the build ran out of memory: the kernel killed its microVM at the limit of runner.build.memory (${memory ?? "8g"}) plus 1 GiB for Firecracker and its disks' page cache. Raise runner.build.memory, or lower runner.concurrency`;
306
+ }
299
307
  function oomKills(fs, path) {
300
308
  const match = /^oom_kill (\d+)$/m.exec(fs.read(join(path, "memory.events")) ?? "");
301
309
  return match === null ? 0 : Number(match[1]);
@@ -461,119 +469,6 @@ function lifecycleSteps(lifecycle) {
461
469
  function lifecycleFailureReason(step, exitCode) {
462
470
  return `lifecycle command ${step.name} (${step.source}) exited ${exitCode}`;
463
471
  }
464
- var BUILD_LABEL = "ai.outerlayer.build";
465
- var RUNNER_LABEL = "ai.outerlayer.runner";
466
- function resolvedRunnerDir(configPath) {
467
- try {
468
- return realpathSync(runnerDir(configPath));
469
- } catch {
470
- try {
471
- return join(realpathSync(dirname(resolve(configPath))), "runner");
472
- } catch {
473
- return resolve(runnerDir(configPath));
474
- }
475
- }
476
- }
477
- function runnerLabelValue(configPath) {
478
- return createHash("sha256").update(resolvedRunnerDir(configPath)).digest("hex").slice(0, 12);
479
- }
480
- function hardened(input) {
481
- const { memory, cpus, pids } = input.limits;
482
- return [
483
- "--label",
484
- `${BUILD_LABEL}=${input.buildId}`,
485
- "--label",
486
- `${RUNNER_LABEL}=${input.runnerId}`,
487
- "--network",
488
- "none",
489
- "--cap-drop",
490
- "ALL",
491
- "--security-opt",
492
- "no-new-privileges",
493
- "--read-only",
494
- "--user",
495
- input.user,
496
- // The same figure for swap as for memory leaves the container none.
497
- "--memory",
498
- memory,
499
- "--memory-swap",
500
- memory,
501
- "--cpus",
502
- String(cpus),
503
- "--pids-limit",
504
- String(pids)
505
- ];
506
- }
507
- function mountPath(path) {
508
- 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`);
509
- return path;
510
- }
511
- function containerRunArgs(input) {
512
- return [
513
- "--detach",
514
- "--name",
515
- input.container,
516
- ...hardened(input),
517
- "--mount",
518
- `type=volume,src=${input.volume},dst=/build`,
519
- // `/tmp` is a directory of the same volume, so it is measured and limited with the rest of the build.
520
- "--mount",
521
- `type=volume,src=${input.volume},dst=/tmp,volume-subpath=tmp`,
522
- "--mount",
523
- `type=bind,src=${mountPath(input.buildDir)},dst=${JOB_MOUNT},readonly`,
524
- "--entrypoint",
525
- "/opt/outerlayer/bin/tini",
526
- input.image,
527
- "--",
528
- "/opt/outerlayer/bin/node",
529
- "/opt/outerlayer/cli/dist/index.js",
530
- "broker-relay",
531
- "--port",
532
- String(RELAY_PORT),
533
- "--socket",
534
- `${JOB_MOUNT}/tunnel.sock`,
535
- ...Array.from({ length: input.destinations ?? 0 }, (_unused, index) => [
536
- "--forward",
537
- `${DESTINATION_BASE_PORT + index}=${JOB_MOUNT}/dest-${index}.sock`
538
- ]).flat(),
539
- ...[
540
- "--forward",
541
- `${GATEWAY_PORT}=${JOB_MOUNT}/${GATEWAY_SOCKET}`,
542
- "--forward",
543
- `${CLAUDE_PORT}=${JOB_MOUNT}/${CLAUDE_SOCKET}`
544
- ]
545
- ];
546
- }
547
- function prepareRunArgs(input) {
548
- return [
549
- "--rm",
550
- ...hardened(input),
551
- "--mount",
552
- `type=volume,src=${input.volume},dst=/build`,
553
- "--entrypoint",
554
- "mkdir",
555
- input.image,
556
- "-p",
557
- "-m",
558
- "1777",
559
- "/build/tmp",
560
- "/build/home",
561
- "/build/work"
562
- ];
563
- }
564
- function containerExecArgs(input) {
565
- const literals = Object.keys(input.env.literals).sort().flatMap((name) => ["-e", `${name}=${input.env.literals[name]}`]);
566
- const passed = Object.keys(input.env.passed).filter((name) => input.env.passed[name] !== void 0).sort().flatMap((name) => ["-e", name]);
567
- return [
568
- "-u",
569
- input.user,
570
- ...input.workdir === void 0 ? [] : ["-w", input.workdir],
571
- ...literals,
572
- ...passed,
573
- input.container,
574
- ...input.argv
575
- ];
576
- }
577
472
  var AssetError = class extends Error {
578
473
  constructor(file, message) {
579
474
  super(message);
@@ -714,7 +609,7 @@ function converterArgs(dest, extra, script) {
714
609
  }
715
610
  var firstLine2 = (text) => text.trim().split("\n")[0] ?? "";
716
611
  function exportInto(container, converter) {
717
- return new Promise((resolve2, reject) => {
612
+ return new Promise((resolve, reject) => {
718
613
  const exporter = spawn("docker", ["export", container], { stdio: ["ignore", "pipe", "pipe"], env: childEnvironment(process.env) });
719
614
  const writer = spawn("docker", converter, { stdio: ["pipe", "ignore", "pipe"], env: childEnvironment(process.env) });
720
615
  const errors = [];
@@ -731,7 +626,7 @@ function exportInto(container, converter) {
731
626
  if (code !== 0 && failure === void 0) failure = `${name} exited ${code}`;
732
627
  exits += 1;
733
628
  if (exits < 2) return;
734
- if (failure === void 0) resolve2();
629
+ if (failure === void 0) resolve();
735
630
  else reject(new Error(`${failure}: ${firstLine2(errors.join("\n"))}`));
736
631
  };
737
632
  exporter.once("exit", (code) => done("docker export", code));
@@ -880,7 +775,7 @@ function createFirecrackerHost(options = {}) {
880
775
  const failed = new Promise((_resolve, reject) => child.once("error", reject));
881
776
  const pid = child.pid;
882
777
  if (pid === void 0) return await failed;
883
- exits.set(pid, new Promise((resolve2) => child.once("exit", () => resolve2())));
778
+ exits.set(pid, new Promise((resolve) => child.once("exit", () => resolve())));
884
779
  child.unref();
885
780
  const start = startTimeOf(pid);
886
781
  if (start === null) throw new Error(`${command} exited as soon as it started; its console is at ${input.consolePath}`);
@@ -897,8 +792,8 @@ function createFirecrackerHost(options = {}) {
897
792
  }
898
793
  const exited = exits.get(vm.pid);
899
794
  const deadline = Date.now() + STOP_WAIT_MS;
900
- if (exited !== void 0) await Promise.race([exited, new Promise((resolve2) => setTimeout(resolve2, STOP_WAIT_MS))]);
901
- while (alive(vm) && Date.now() < deadline) await new Promise((resolve2) => setTimeout(resolve2, POLL_MS));
795
+ if (exited !== void 0) await Promise.race([exited, new Promise((resolve) => setTimeout(resolve, STOP_WAIT_MS))]);
796
+ while (alive(vm) && Date.now() < deadline) await new Promise((resolve) => setTimeout(resolve, POLL_MS));
902
797
  exits.delete(vm.pid);
903
798
  },
904
799
  alive
@@ -908,7 +803,7 @@ function createFirecrackerHost(options = {}) {
908
803
  // src/runner/microvm/host.ts
909
804
  function createMicroVmPorts(over = {}) {
910
805
  return {
911
- assets: over.assets ?? ((runnerDir2) => ensureAssets({ runnerDir: runnerDir2 })),
806
+ assets: over.assets ?? ((runnerDir) => ensureAssets({ runnerDir })),
912
807
  rootfs: over.rootfs ?? createRootfsHost(),
913
808
  firecracker: over.firecracker ?? createFirecrackerHost(),
914
809
  cgroup: over.cgroup ?? realCgroupPort(),
@@ -921,6 +816,10 @@ var aborted = () => ({ ok: false, reason: "", aborted: true });
921
816
  function firstLine3(text) {
922
817
  return text.trim().split("\n")[0] ?? "";
923
818
  }
819
+ function gitErrorLine(text) {
820
+ const lines = text.split("\n").map((line) => line.trim()).filter((line) => line !== "");
821
+ return lines.find((line) => /^(fatal|error):/.test(line)) ?? lines.at(-1) ?? "";
822
+ }
924
823
  function adoptionFailureReason(result) {
925
824
  const line = result.stdout.trim().split("\n").at(-1) ?? "";
926
825
  try {
@@ -935,7 +834,10 @@ async function runBuildSteps(input) {
935
834
  const rewrite = await exec(["git", "config", "--global", `url.http://127.0.0.1:${RELAY_PORT}/github.com/.insteadOf`, "https://github.com/"], "/build/work");
936
835
  if (rewrite.status !== 0) return { ok: false, reason: `configuring the build's git remote failed (exit ${rewrite.status})` };
937
836
  const clone = await exec(["git", "clone", "--filter=blob:none", "--single-branch", `https://github.com/${repository}.git`, CHECKOUT_DIR], "/build/work");
938
- if (clone.status !== 0) return { ok: false, reason: `the clone of ${repository} failed (exit ${clone.status})` };
837
+ if (clone.status !== 0) {
838
+ const detail = gitErrorLine(clone.stderr);
839
+ return { ok: false, reason: `the clone of ${repository} failed (exit ${clone.status})${detail === "" ? "" : `: ${detail}`}` };
840
+ }
939
841
  try {
940
842
  const prepared = await prepareBranch({
941
843
  workdir: CHECKOUT_DIR,
@@ -1150,9 +1052,9 @@ async function git(args, cwd, env, input) {
1150
1052
  if (size < maxOutputBytes) out.push(chunk);
1151
1053
  size += chunk.length;
1152
1054
  });
1153
- const closed = new Promise((resolve2, reject) => {
1055
+ const closed = new Promise((resolve, reject) => {
1154
1056
  child.once("error", reject);
1155
- child.once("close", (status) => resolve2(status));
1057
+ child.once("close", (status) => resolve(status));
1156
1058
  });
1157
1059
  if (input !== void 0) {
1158
1060
  await pipeline(input, child.stdin).catch(() => void 0);
@@ -1349,14 +1251,14 @@ async function drain(req) {
1349
1251
  }
1350
1252
  }
1351
1253
  function readPrefix(req, gzip) {
1352
- return new Promise((resolve2, reject) => {
1254
+ return new Promise((resolve, reject) => {
1353
1255
  const held = [];
1354
1256
  const finish = (section) => {
1355
1257
  req.off("data", onData);
1356
1258
  req.off("end", onEnd);
1357
1259
  req.off("error", reject);
1358
1260
  req.pause();
1359
- resolve2({ held, section });
1261
+ resolve({ held, section });
1360
1262
  };
1361
1263
  const onData = (chunk) => {
1362
1264
  held.push(chunk);
@@ -1370,19 +1272,19 @@ function readPrefix(req, gzip) {
1370
1272
  });
1371
1273
  }
1372
1274
  function readBody(req, limit) {
1373
- return new Promise((resolve2, reject) => {
1275
+ return new Promise((resolve, reject) => {
1374
1276
  const chunks = [];
1375
1277
  let size = 0;
1376
1278
  req.on("data", (chunk) => {
1377
1279
  size += chunk.length;
1378
1280
  if (size > limit) {
1379
1281
  req.pause();
1380
- resolve2(void 0);
1282
+ resolve(void 0);
1381
1283
  return;
1382
1284
  }
1383
1285
  chunks.push(chunk);
1384
1286
  });
1385
- req.once("end", () => resolve2(Buffer.concat(chunks)));
1287
+ req.once("end", () => resolve(Buffer.concat(chunks)));
1386
1288
  req.once("error", reject);
1387
1289
  });
1388
1290
  }
@@ -1395,7 +1297,7 @@ function createGitRemote(options) {
1395
1297
  const { route, res } = input;
1396
1298
  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}`}`);
1397
1299
  const send = url.protocol === "https:" ? request$1 : request;
1398
- return new Promise((resolve2) => {
1300
+ return new Promise((resolve) => {
1399
1301
  const upstream = send(url, { method: input.method, headers: { ...input.headers, connection: "close" } }, (answer) => {
1400
1302
  const headers = {};
1401
1303
  for (const [name, value] of Object.entries(answer.headers)) {
@@ -1417,18 +1319,18 @@ function createGitRemote(options) {
1417
1319
  });
1418
1320
  answer.once("end", () => {
1419
1321
  res.end();
1420
- resolve2();
1322
+ resolve();
1421
1323
  });
1422
1324
  answer.once("error", () => {
1423
1325
  res.destroy();
1424
- resolve2();
1326
+ resolve();
1425
1327
  });
1426
1328
  });
1427
1329
  upstream.on("error", (err) => {
1428
1330
  log(`could not reach GitHub for ${route.kind}: ${err.message}`);
1429
1331
  if (res.headersSent) res.destroy();
1430
1332
  else plain(res, 502, "the runner could not reach GitHub\n");
1431
- resolve2();
1333
+ resolve();
1432
1334
  });
1433
1335
  res.once("close", () => upstream.destroy());
1434
1336
  input.feed(upstream);
@@ -1537,6 +1439,11 @@ function createGitRemote(options) {
1537
1439
  req.resume();
1538
1440
  return plain(res, 404, "not found\n");
1539
1441
  }
1442
+ if (options.readOnly === true && (route.kind === "receive-pack" || route.kind === "info-refs" && route.service === "git-receive-pack")) {
1443
+ log(`push refused: this remote is read-only`);
1444
+ req.resume();
1445
+ return plain(res, 403, "this remote is read-only: a check cannot push\n");
1446
+ }
1540
1447
  const own = isOwnRepository(options.repository, route.owner, route.name);
1541
1448
  if (own && route.kind === "receive-pack") return push(req, res, route);
1542
1449
  let kind = route.kind === "info-refs" && route.service === "git-receive-pack" ? "push" : "read";
@@ -1545,6 +1452,7 @@ function createGitRemote(options) {
1545
1452
  body = await readBody(req, maxLfsBodyBytes);
1546
1453
  if (body === void 0) return plain(res, 413, "the request is too large\n");
1547
1454
  kind = isLfsUpload(body) ? "push" : "read";
1455
+ if (options.readOnly === true && kind === "push") return plain(res, 403, "this remote is read-only: a check cannot upload\n");
1548
1456
  }
1549
1457
  let authorization;
1550
1458
  if (own) {
@@ -1717,7 +1625,7 @@ async function startTunnel(socketPath, opts = {}) {
1717
1625
  if (Buffer.byteLength(socketPath) > MAX_SOCKET_PATH_BYTES) {
1718
1626
  throw new Error(`${socketPath} is too long for a Unix socket; keep the runner's config file nearer the root of the filesystem`);
1719
1627
  }
1720
- const resolve2 = opts.resolve ?? systemResolve;
1628
+ const resolve = opts.resolve ?? systemResolve;
1721
1629
  const openConnection = opts.connect ?? ((t) => connect({ host: t.address, port: t.port }));
1722
1630
  const ownAddresses = opts.hostAddresses ?? systemHostAddresses;
1723
1631
  const connectTimeoutMs = opts.connectTimeoutMs ?? CONNECT_TIMEOUT_MS;
@@ -1801,7 +1709,7 @@ async function startTunnel(socketPath, opts = {}) {
1801
1709
  }
1802
1710
  let addresses;
1803
1711
  try {
1804
- addresses = await resolve2(host);
1712
+ addresses = await resolve(host);
1805
1713
  } catch {
1806
1714
  return { ok: false, status: 502, body: `could not resolve ${host}
1807
1715
  ` };
@@ -1980,13 +1888,14 @@ function createContainerRuntime(opts = {}) {
1980
1888
  configPath: build.configPath,
1981
1889
  cli: cli ?? currentCliBuild(),
1982
1890
  readToken: build.readToken,
1983
- guestHelper: build.guestHelper
1891
+ guestHelper: build.guestHelper,
1892
+ log: build.log
1984
1893
  },
1985
- { host: opts.imageHost ?? createDockerHost() }
1894
+ { host: opts.imageHost ?? createDockerHost({ builder: builderSpec(build.configPath, build.buildMemory, opts.hostEnv ?? process.env) }) }
1986
1895
  )),
1987
1896
  startTunnel: opts.startTunnel ?? startTunnel,
1988
1897
  tunnelOptions: opts.tunnelOptions,
1989
- sleep: opts.sleep ?? ((ms) => new Promise((resolve2) => setTimeout(resolve2, ms))),
1898
+ sleep: opts.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms))),
1990
1899
  hostEnv: opts.hostEnv ?? process.env,
1991
1900
  credentials: opts.credentials ?? /* @__PURE__ */ new Map(),
1992
1901
  github: { gitUrl: opts.github?.gitUrl ?? "https://github.com", apiUrl: opts.github?.apiUrl ?? "https://api.github.com" },
@@ -2021,7 +1930,8 @@ function gitRemoteFor(runtime, input) {
2021
1930
  onWorkflowRefused: () => {
2022
1931
  input.tokens.reason = WORKFLOW_PERMISSION_REASON;
2023
1932
  },
2024
- log: input.log
1933
+ log: input.log,
1934
+ ...input.readOnly === true ? { readOnly: true } : {}
2025
1935
  });
2026
1936
  }
2027
1937
  function firstLine4(text) {
@@ -2056,7 +1966,17 @@ async function obtainImage(input) {
2056
1966
  };
2057
1967
  }
2058
1968
  try {
2059
- return { ok: true, built: await runtime.buildImage({ repository, configPath: config.configPath, readToken, guestHelper: input.guestHelper }) };
1969
+ return {
1970
+ ok: true,
1971
+ built: await runtime.buildImage({
1972
+ repository,
1973
+ configPath: config.configPath,
1974
+ buildMemory: config.build?.memory ?? "8g",
1975
+ readToken,
1976
+ guestHelper: input.guestHelper,
1977
+ log: (line) => log.line(`${tag} ${line}`)
1978
+ })
1979
+ };
2060
1980
  } catch (err) {
2061
1981
  if (err instanceof RecipeError && err.code === "build_in_progress" && (input.now?.() ?? /* @__PURE__ */ new Date()).getTime() < input.deadlineMs) {
2062
1982
  log.line(`${tag} waiting for another build of ${repository}'s image`);
@@ -2065,7 +1985,8 @@ async function obtainImage(input) {
2065
1985
  }
2066
1986
  if (err instanceof RecipeError) {
2067
1987
  log.error(`${tag} image build for ${repository} failed: ${err.code}: ${err.message}`);
2068
- return { ok: false, reason: err.code };
1988
+ const says = err.code === "image_build_out_of_memory" || err.code === "image_builder_unavailable";
1989
+ return { ok: false, reason: says ? err.message : err.code };
2069
1990
  }
2070
1991
  return { ok: false, reason: `the image build failed: ${describeCallFailure(err)}` };
2071
1992
  }
@@ -2105,7 +2026,8 @@ async function provisionContainer(input) {
2105
2026
  repository,
2106
2027
  imageId: report.imageId,
2107
2028
  containerEnv: report.apply.containerEnv,
2108
- builtIns: true
2029
+ builtIns: true,
2030
+ lifecycle: report.apply.lifecycle
2109
2031
  };
2110
2032
  const env = buildEnvironment({
2111
2033
  config,
@@ -2171,7 +2093,9 @@ async function provisionContainer(input) {
2171
2093
  image: report.imageId,
2172
2094
  user: ADDED_USER,
2173
2095
  limits: limits2,
2174
- destinations: config.build?.destinations?.length ?? 0};
2096
+ destinations: config.build?.destinations?.length ?? 0,
2097
+ builtIns: true
2098
+ };
2175
2099
  const prepared = await host.run(prepareRunArgs(runInput), { logPath });
2176
2100
  if (prepared.status !== 0) return dockerFailed("preparing the build's volume", prepared);
2177
2101
  const started = await host.run(containerRunArgs(runInput), { logPath });
@@ -2388,5 +2312,75 @@ async function cleanupContainer(runtime, state, jobDir) {
2388
2312
  rmSync(state.buildDir, { recursive: true, force: true });
2389
2313
  if (failures.length > 0) throw new Error(failures.join("; "));
2390
2314
  }
2315
+ async function startCheckContainer(input) {
2316
+ const { config, runtime, job, jobDir, tokens, log } = input;
2317
+ const { host } = runtime;
2318
+ const tag = `#${job.item}`;
2319
+ const built = job.container;
2320
+ const repository = built?.repository ?? job.repository;
2321
+ if (built === void 0 || built.imageId === void 0 || repository === void 0 || job.branch === void 0) {
2322
+ return { ok: false, reason: "the build's image, repository or branch is not recorded" };
2323
+ }
2324
+ const buildId = randomBytes(6).toString("hex");
2325
+ const name = `outerlayer-check-${buildId}`;
2326
+ const buildDir2 = buildDir(config.configPath, buildId);
2327
+ const agentUser = built.agentUser ?? ADDED_USER;
2328
+ const state = { buildId, container: name, volume: name, buildDir: buildDir2, repository, imageId: built.imageId, agentUser };
2329
+ const checksDir = join(jobDir, "checks");
2330
+ mkdirSync(checksDir, { recursive: true });
2331
+ const logPath = join(checksDir, "environment.log");
2332
+ input.save(state);
2333
+ const fail = async (reason) => {
2334
+ try {
2335
+ await cleanupContainer(runtime, state);
2336
+ } catch (err) {
2337
+ log.error(`${tag} cleanup of the check environment failed: ${describeCallFailure(err)}`);
2338
+ }
2339
+ return { ok: false, reason };
2340
+ };
2341
+ const dockerFailed = (what, result) => fail(`${what} failed (exit ${result.status})${firstLine4(result.stderr) === "" ? "" : `: ${firstLine4(result.stderr)}`}`);
2342
+ mkdirSync(buildDir2, { recursive: true });
2343
+ chmodSync(buildDir2, 493);
2344
+ try {
2345
+ const gitRemote = gitRemoteFor(runtime, { repository, branch: job.branch, tokens, buildDir: buildDir2, log: (line) => log.line(`${tag} ${line}`), readOnly: true });
2346
+ runtime.tunnels.set(buildId, await startServicesFor(runtime, config, buildDir2, checksDir, void 0, false, gitRemote));
2347
+ } catch (err) {
2348
+ return fail(`the tunnel could not start: ${describeCallFailure(err)}`);
2349
+ }
2350
+ const limits2 = { memory: config.build?.memory ?? "8g", cpus: config.build?.cpus ?? 4, pids: config.build?.pids ?? 4096 };
2351
+ const runnerId = runnerLabelValue(config.configPath);
2352
+ const volume = await host.createVolume(name, { [BUILD_LABEL]: buildId, [RUNNER_LABEL]: runnerId });
2353
+ if (volume.status !== 0) return dockerFailed("creating the check environment's volume", volume);
2354
+ const runInput = {
2355
+ buildId,
2356
+ runnerId,
2357
+ container: name,
2358
+ volume: name,
2359
+ buildDir: buildDir2,
2360
+ image: built.imageId,
2361
+ user: ADDED_USER,
2362
+ limits: limits2,
2363
+ destinations: config.build?.destinations?.length ?? 0,
2364
+ builtIns: false
2365
+ };
2366
+ const prepared = await host.run(prepareRunArgs(runInput), { logPath });
2367
+ if (prepared.status !== 0) return dockerFailed("preparing the check environment's volume", prepared);
2368
+ const started = await host.run(containerRunArgs(runInput), { logPath });
2369
+ if (started.status !== 0) return dockerFailed("starting the check environment's container", started);
2370
+ if (!await waitForRelay(runtime, state)) return fail("the relay in the check environment's container did not start");
2371
+ const env = checkEnvironment({ config, item: job.item, repository, hostEnv: runtime.hostEnv, containerEnv: built.containerEnv ?? {} });
2372
+ return {
2373
+ ok: true,
2374
+ container: {
2375
+ state,
2376
+ exec: (argv, workdir, opts) => host.exec({ container: name, user: agentUser, workdir, env, argv }, opts?.logPath === void 0 ? void 0 : { logPath: opts.logPath }),
2377
+ restart: async () => {
2378
+ await host.stop(name);
2379
+ const again = await host.start(name);
2380
+ return again.status === 0 && await waitForRelay(runtime, state);
2381
+ }
2382
+ }
2383
+ };
2384
+ }
2391
2385
 
2392
- export { ADDED_USER, AgentUserError, AssetError, BUILD_LABEL, BuildTokens, GH_CONFIG_YML, MIN_ENGINE_MAJOR, RUNNER_LABEL, classifyRefusal, cleanupContainer, containerCommandLine, containerSenderCommand, 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, senderSignalScript, setUpRunnerCgroup, shellQuote, shortClaim, signalInContainer, stageGovernedContext, startServicesFor, stopContainer, syncInContainer };
2386
+ export { ADDED_USER, AgentUserError, AssetError, BuildTokens, GH_CONFIG_YML, MIN_ENGINE_MAJOR, classifyRefusal, cleanupContainer, containerCommandLine, containerSenderCommand, createBuildDisk, createContainerRuntime, createRunnerLog, deleteBuildDisk, describeCallFailure, ensureRootfs, ensureTunnel, formatClock, formatDuration, ghHostsYml, gitRemoteFor, isLiteralAddress, keepGhConfigCurrent, lifecycleFailureReason, lifecycleSteps, machineConfig, measureDisk, obtainImage, outOfMemoryReason, prepareBranch, provisionContainer, refusalCode, relativeToRunner, removeStagedContext, resolveAgentUser, retrySend, runBuildSteps, senderSignalScript, setUpRunnerCgroup, shellQuote, shortClaim, signalInContainer, stageGovernedContext, startCheckContainer, startServicesFor, stopContainer, syncInContainer };
@@ -0,0 +1,45 @@
1
+ #!/usr/bin/env node
2
+ import { createRequire } from 'node:module';
3
+
4
+ createRequire(import.meta.url);
5
+
6
+ // src/runner/image/pins.ts
7
+ var CLAUDE_CODE_VERSION = "2.1.284";
8
+ var DEFAULT_BASE_IMAGE = "ubuntu:24.04@sha256:008173c23f95b170204355c12626cb5a965d779a7e1283b09e9cffbb1bf33ca3";
9
+ var NODE_IMAGE = "node:22-bookworm-slim@sha256:43ac6c60b8f89723f746e8a92ce91abd5017e627ce1ddfe4238355d3a30b772c";
10
+ var BUILDKIT_IMAGE = "moby/buildkit:v0.33.1@sha256:cec9f139f45e93c5c69c60f8b07cfad9f43f4ef6b6a6cd917527fea5ff2e3dea";
11
+ var GH_VERSION = "2.101.0";
12
+ var GH_SHA256 = {
13
+ amd64: "9bca2d1c16825f109907a23307628a2f0698fbf99662b73a5cf0b020293072b8",
14
+ arm64: "b57e8063f18862647c9d22727c32e9da1b963f8bf9db648fe123a6975695640f"
15
+ };
16
+ var TINI_VERSION = "0.19.0";
17
+ var TINI_SHA256 = {
18
+ amd64: "c5b0666b4cb676901f90dfcb37106783c5fe2077b04590973b885950611b30ee",
19
+ arm64: "eae1d3aa50c48fb23b8cbdf4e369d0910dfc538566bfd09df89a774aa84a48b9"
20
+ };
21
+ var MICROVM_ARCHES = ["x86_64"];
22
+ function firecrackerArch(nodeArch) {
23
+ return nodeArch === "x64" ? "x86_64" : nodeArch === "arm64" ? "aarch64" : nodeArch;
24
+ }
25
+ var FIRECRACKER_VERSION = "1.13.1";
26
+ var FIRECRACKER_SHA256 = {
27
+ x86_64: "59450b9171ff2ebdf2f9a25be3a248a7ba79fb6371aec51a9d6d8eefca7b4faf"
28
+ };
29
+ var FIRECRACKER_BINARY_SHA256 = {
30
+ x86_64: "7d3924e0c8761edafbf85cb2a94c268ffad249644a40fd02d4e15cde4c206f6e"
31
+ };
32
+ var GUEST_ASSETS_VERSION = "1";
33
+ var GUEST_ASSETS_BASE_URL = `https://github.com/outerlayerai/guest-assets/releases/download/guest-${GUEST_ASSETS_VERSION}`;
34
+ var GUEST_KERNEL_SHA256 = {
35
+ x86_64: "5a2d716d301615c69fefc2f9574954e838269f14d5e8b0d1bad88b680cd372ee"
36
+ };
37
+ var GUEST_HELPER_SHA256 = {
38
+ x86_64: "4175e89ae8cb633113b5f69ef8b6e206093954411fe53edf86357bc1b7e822f9"
39
+ };
40
+ function guestAssetsPinned(arch) {
41
+ const zero = "0".repeat(64);
42
+ return GUEST_KERNEL_SHA256[arch] !== zero && GUEST_HELPER_SHA256[arch] !== zero;
43
+ }
44
+
45
+ export { BUILDKIT_IMAGE, CLAUDE_CODE_VERSION, DEFAULT_BASE_IMAGE, FIRECRACKER_BINARY_SHA256, FIRECRACKER_SHA256, FIRECRACKER_VERSION, GH_SHA256, GH_VERSION, GUEST_ASSETS_BASE_URL, GUEST_ASSETS_VERSION, GUEST_HELPER_SHA256, GUEST_KERNEL_SHA256, MICROVM_ARCHES, NODE_IMAGE, TINI_SHA256, TINI_VERSION, firecrackerArch, guestAssetsPinned };
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- import { maintainedFilesForExistingSkills, loadMaintainedFiles, writeMaintainedFiles } from './chunk-WO2BXCTQ.js';
4
- import { emitTree, parseOuterlayerConfig, classifyTree, generatedHeaderSource, CONTEXT_PIN_FILENAME, normalizePath } from './chunk-EMY4I27X.js';
3
+ import { maintainedFilesForExistingSkills, loadMaintainedFiles, writeMaintainedFiles } from './chunk-VXLJORHL.js';
4
+ import { emitTree, parseOuterlayerConfig, classifyTree, generatedHeaderSource, CONTEXT_PIN_FILENAME, normalizePath } from './chunk-7ZR5UPMB.js';
5
5
  import { existsSync, readFileSync, mkdirSync, writeFileSync, copyFileSync, readdirSync } from 'fs';
6
6
  import { join, dirname, relative, sep } from 'path';
7
7