@outerlayer/cli 0.7.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 (62) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/README.md +5 -5
  3. package/dist/{build-JF5UOWKJ.js → build-MS34XEGN.js} +2 -1
  4. package/dist/build-info.json +1 -1
  5. package/dist/builder-G52A6GW2.js +13 -0
  6. package/dist/{check-ASZL5BEO.js → check-FOF3PUKI.js} +21 -18
  7. package/dist/{chunk-2VO25HHO.js → chunk-3S4P55NE.js} +2 -2
  8. package/dist/{chunk-MQPRB5K7.js → chunk-4A5E2G4E.js} +6 -6
  9. package/dist/{chunk-PUN7LMNS.js → chunk-4ACZCY4I.js} +2 -2
  10. package/dist/{chunk-DGAIVMMN.js → chunk-5QLNZGAQ.js} +2 -2
  11. package/dist/chunk-5TNS3PL7.js +263 -0
  12. package/dist/chunk-7RQ73DLO.js +23 -0
  13. package/dist/{chunk-AJKEHABI.js → chunk-7VNURP6M.js} +248 -50
  14. package/dist/{chunk-6ZZHWY2Z.js → chunk-AIL2R4SI.js} +1 -1
  15. package/dist/{chunk-7FTNIAHS.js → chunk-AX23QKHS.js} +7 -3
  16. package/dist/{chunk-E4PIFIC3.js → chunk-CM5QTKM4.js} +58 -149
  17. package/dist/chunk-CX22S5BA.js +45 -0
  18. package/dist/{chunk-D6FXM5OW.js → chunk-GAYWY4GI.js} +8 -8
  19. package/dist/{chunk-AII7CKTH.js → chunk-IJBVPLZZ.js} +54 -16
  20. package/dist/{chunk-SSU747O6.js → chunk-ILEA3YHM.js} +1 -1
  21. package/dist/{chunk-6IYLDODX.js → chunk-IRCBTGWF.js} +4 -4
  22. package/dist/{chunk-OUUINEYB.js → chunk-JKIWMRFH.js} +1 -1
  23. package/dist/{chunk-X2FSFU4C.js → chunk-K35RW5HP.js} +2 -2
  24. package/dist/{chunk-6QWV3YDJ.js → chunk-NALVDFIS.js} +2 -2
  25. package/dist/{chunk-Q5G2ODUZ.js → chunk-OEK4JEB3.js} +33 -4
  26. package/dist/chunk-PTW6W5NR.js +127 -0
  27. package/dist/{chunk-DTXOJDEH.js → chunk-PWBILI5P.js} +3 -2
  28. package/dist/{chunk-CEBIA5FD.js → chunk-TTNFGMHO.js} +42 -50
  29. package/dist/{chunk-AQ54MKSQ.js → chunk-TUUMW4TC.js} +2 -2
  30. package/dist/{chunk-774HEQL6.js → chunk-U3GOI2B4.js} +91 -8
  31. package/dist/{chunk-F6GFZXZ3.js → chunk-UXYLWVTQ.js} +1 -18
  32. package/dist/{cli-LEVCGAQV.js → cli-AOJPAVFX.js} +94 -68
  33. package/dist/{command-FLSYCGIB.js → command-SY3SVLAI.js} +31 -16
  34. package/dist/{config-I3TVFYLH.js → config-Z3USDBLU.js} +3 -2
  35. package/dist/{connect-cmd-MJMUONDY.js → connect-cmd-PNZ6IJLJ.js} +2 -2
  36. package/dist/{dist-PXBSA7QX.js → dist-3ZTZF2TP.js} +1 -1
  37. package/dist/docker-676FD3CQ.js +15 -0
  38. package/dist/{doctor-FVDT3KJZ.js → doctor-WR6TRPSU.js} +36 -13
  39. package/dist/{emit-artifact-cmd-7RPMRSVP.js → emit-artifact-cmd-AVM4YXMQ.js} +4 -4
  40. package/dist/{emit-criteria-cmd-MKI7WETO.js → emit-criteria-cmd-LDBE4YDS.js} +3 -3
  41. package/dist/{emit-finding-cmd-RMLGZASB.js → emit-finding-cmd-6R5WBPCH.js} +3 -3
  42. package/dist/{emit-result-cmd-MTXAZ2GV.js → emit-result-cmd-2DMN6O6U.js} +4 -4
  43. package/dist/index.js +1 -1
  44. package/dist/{init-BACXESVV.js → init-KDK5QLQZ.js} +13 -10
  45. package/dist/{init-VLW7H6RW.js → init-R2FJ5KNS.js} +22 -15
  46. package/dist/{init-cmd-VJFB5L7X.js → init-cmd-BYZYAJ5P.js} +5 -5
  47. package/dist/{install-NMAPLLBQ.js → install-FHTVJZS6.js} +5 -4
  48. package/dist/{install-cmd-B5SEU3G7.js → install-cmd-DCF7TVXD.js} +2 -1
  49. package/dist/{loop-D3243JKT.js → loop-AG3PLXPO.js} +21 -18
  50. package/dist/{machine-EB3GEVI4.js → machine-LV6WBRRK.js} +12 -9
  51. package/dist/{session-sender-LMACEJJS.js → session-sender-Q5HAMOL2.js} +3 -3
  52. package/dist/{status-F4THTJJK.js → status-HH4OSOHR.js} +15 -12
  53. package/dist/{sync-cmd-M6JA3OEE.js → sync-cmd-CAEPJR7B.js} +2 -2
  54. package/dist/{watch-NB5TGPEZ.js → watch-LAAZFBHH.js} +2 -2
  55. package/dist/{work-claim-cmd-UHMWOS3G.js → work-claim-cmd-ARIYZARE.js} +8 -7
  56. package/dist/{work-cmd-6UMCFTAW.js → work-cmd-B3FOXZAL.js} +4 -4
  57. package/dist/{work-comment-cmd-E4LEUIXZ.js → work-comment-cmd-O5WGRI6K.js} +4 -4
  58. package/dist/{work-open-pr-cmd-OPZWGSEQ.js → work-open-pr-cmd-M6CQW7KW.js} +5 -5
  59. package/dist/{work-pr-cmd-IDIAFFRU.js → work-pr-cmd-C4YEGKD6.js} +4 -4
  60. package/package.json +1 -1
  61. package/dist/docker-NEGDLU6D.js +0 -7
  62. package/dist/{agent-setup-TXLDY5RV.js → agent-setup-C7PLRXZO.js} +1 -1
@@ -1,21 +1,22 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
3
  import { writeJobFile, readJobFile } from './chunk-T6OIJQRT.js';
4
- import { senderPidfile } from './chunk-2VO25HHO.js';
4
+ import { senderPidfile } from './chunk-3S4P55NE.js';
5
5
  import { DOCKERD_LOG } from './chunk-WVGSB3Z2.js';
6
- import { installScrubContext } from './chunk-6ZZHWY2Z.js';
7
- import { AssetError, describeCallFailure, obtainImage, ensureRootfs, gitRemoteFor, startServicesFor, createBuildDisk, machineConfig, refusalCode, resolveAgentUser, AgentUserError, runBuildSteps, removeStagedContext, stageGovernedContext, shellQuote, createRunnerLog, createContainerRuntime, stopContainer, cleanupContainer, outOfMemoryReason, syncInContainer, formatDuration, retrySend, formatClock, containerSenderCommand, GH_CONFIG_YML, ghHostsYml, ADDED_USER, isLiteralAddress, signalInContainer, startCheckContainer, lifecycleSteps, lifecycleFailureReason, deleteBuildDisk, senderSignalScript } from './chunk-E4PIFIC3.js';
8
- import { CLAUDE_CODE_VERSION, readBranchHead, fetchCommit } from './chunk-CEBIA5FD.js';
6
+ import { installScrubContext } from './chunk-AIL2R4SI.js';
7
+ import { AssetError, describeCallFailure, obtainImage, ensureRootfs, gitRemoteFor, startServicesFor, createBuildDisk, machineConfig, refusalCode, resolveAgentUser, AgentUserError, runBuildSteps, removeStagedContext, stageGovernedContext, shellQuote, createRunnerLog, createContainerRuntime, stopContainer, cleanupContainer, outOfMemoryReason, formatDuration, retrySend, formatClock, containerSenderCommand, syncInContainer, GH_CONFIG_YML, ghHostsYml, ADDED_USER, isLiteralAddress, signalInContainer, deleteBuildDisk, startCheckContainer, lifecycleSteps, lifecycleFailureReason, senderSignalScript } from './chunk-CM5QTKM4.js';
8
+ import { readBranchHead, fetchCommit } from './chunk-TTNFGMHO.js';
9
9
  import { isSameProcess, startTimeOf } from './chunk-N5FOE5PS.js';
10
- import { childEnvironment } from './chunk-774HEQL6.js';
10
+ import { childEnvironment } from './chunk-U3GOI2B4.js';
11
+ import { CLAUDE_CODE_VERSION } from './chunk-CX22S5BA.js';
11
12
  import { waitForAgent, vmExecArgs } from './chunk-ZOAEPWGL.js';
12
13
  import { socketPathProblem, vsockLinks, linkVsockSockets } from './chunk-R7STSBLC.js';
13
- import { WorkCommandError, postJson, isSignatureRefusal, getJson } from './chunk-DGAIVMMN.js';
14
+ import { WorkCommandError, postJson, isSignatureRefusal, getJson } from './chunk-5QLNZGAQ.js';
14
15
  import { planArtifactUpload } from './chunk-ZYHKJKSU.js';
15
- import { scrubText } from './chunk-D6FXM5OW.js';
16
- import { isBuiltinContainer, isBuiltinProcess, isBuiltinVm, DEFAULT_CHECK_TIME_LIMIT_MINUTES } from './chunk-DTXOJDEH.js';
17
- import { parseSize } from './chunk-F6GFZXZ3.js';
18
- import { claudeCredential, buildEnvironment, JOB_MOUNT, CHECKOUT_DIR, parseIsolation, proxyEnvironment, containerLocalAddress, destinationEnvironment, CLAUDE_CREDENTIAL_VARIABLES, builtInEnvironment, startBuildServices, BUILT_IN_NAMES, AGENT_CREDENTIAL_MISSING, sharedUserEnvironment, headerVariables, withTunnelRecord, RELAY_PORT } from './chunk-OD2VTIHN.js';
16
+ import { scrubText } from './chunk-GAYWY4GI.js';
17
+ import { isBuiltinContainer, isBuiltinProcess, isBuiltinVm, DEFAULT_CHECK_TIME_LIMIT_MINUTES } from './chunk-PWBILI5P.js';
18
+ import { parseSize } from './chunk-7RQ73DLO.js';
19
+ import { claudeCredential, buildEnvironment, JOB_MOUNT, CHECKOUT_DIR, parseIsolation, proxyEnvironment, containerLocalAddress, destinationEnvironment, CLAUDE_CREDENTIAL_VARIABLES, builtInEnvironment, startBuildServices, BUILT_IN_NAMES, AGENT_CREDENTIAL_MISSING, sharedUserEnvironment, headerVariables, withTunnelRecord, RELAY_PORT, checkEnvironment } from './chunk-OD2VTIHN.js';
19
20
  import { withHooksPath, authorEnvironment, commitIdentityRecord } from './chunk-HKMPVPO7.js';
20
21
  import { parsePolicy } from './chunk-7ZR5UPMB.js';
21
22
  import { readCloudConfig } from './chunk-EABW6AJQ.js';
@@ -121,7 +122,9 @@ async function provisionMicroVm(input) {
121
122
  repository,
122
123
  imageId: report.imageId,
123
124
  containerEnv: report.apply.containerEnv,
124
- builtIns: true
125
+ builtIns: true,
126
+ lifecycle: report.apply.lifecycle,
127
+ ...report.docker === true ? { docker: true } : {}
125
128
  };
126
129
  const env = buildEnvironment({
127
130
  config,
@@ -403,6 +406,147 @@ async function copyHookErrors(runtime, state, jobDir) {
403
406
  } catch {
404
407
  }
405
408
  }
409
+ async function startCheckMicroVm(input) {
410
+ const { config, runtime, job, jobDir, tokens, log } = input;
411
+ const { vm } = runtime;
412
+ const tag = `#${job.item}`;
413
+ const built = job.microvm;
414
+ const repository = built?.repository ?? job.repository;
415
+ if (built === void 0 || built.imageId === void 0 || repository === void 0 || job.branch === void 0) {
416
+ return { ok: false, reason: "the build's image, repository or branch is not recorded" };
417
+ }
418
+ const branch = job.branch;
419
+ const runnerDirPath = runnerDir(config.configPath);
420
+ const tooDeep = socketPathProblem(buildDir(config.configPath, "0".repeat(12)));
421
+ if (tooDeep !== void 0) return { ok: false, reason: tooDeep };
422
+ const buildId = randomBytes(6).toString("hex");
423
+ const buildDir2 = buildDir(config.configPath, buildId);
424
+ const agentUser = built.agentUser ?? ADDED_USER;
425
+ const state = {
426
+ buildId,
427
+ buildDir: buildDir2,
428
+ repository,
429
+ imageId: built.imageId,
430
+ agentUser,
431
+ containerEnv: built.containerEnv,
432
+ imageEnv: built.imageEnv,
433
+ lifecycle: built.lifecycle,
434
+ ...built.docker === true ? { docker: true } : {}
435
+ };
436
+ const checksDir = join(jobDir, "checks");
437
+ mkdirSync(checksDir, { recursive: true });
438
+ const consolePath = join(checksDir, "vm-console.log");
439
+ input.save(state);
440
+ const fail = async (reason) => {
441
+ try {
442
+ await cleanupMicroVm(runtime, state);
443
+ } catch (err) {
444
+ log.error(`${tag} cleanup of the check environment failed: ${describeCallFailure(err)}`);
445
+ }
446
+ return { ok: false, reason };
447
+ };
448
+ const abandoned = () => input.aborted?.() === true;
449
+ const ABANDONED = "the check environment was abandoned before it was ready";
450
+ let assets;
451
+ try {
452
+ assets = await vm.assets(runnerDirPath);
453
+ } catch (err) {
454
+ return fail(err instanceof AssetError ? err.message : `the microVM's assets could not be had: ${describeCallFailure(err)}`);
455
+ }
456
+ if (abandoned()) return fail(ABANDONED);
457
+ try {
458
+ state.rootfs = await ensureRootfs({ host: vm.rootfs, runnerDir: runnerDirPath, imageId: built.imageId });
459
+ } catch (err) {
460
+ return fail(`the image could not be converted to a root disk: ${describeCallFailure(err)}`);
461
+ }
462
+ if (abandoned()) return fail(ABANDONED);
463
+ state.buildDisk = join(buildDir2, "build.ext4");
464
+ state.vsockPath = vsockPath(buildDir2);
465
+ input.save(state);
466
+ mkdirSync(buildDir2, { recursive: true, mode: 448 });
467
+ try {
468
+ const gitRemote = gitRemoteFor(runtime, { repository, branch, tokens, buildDir: buildDir2, log: (line) => log.line(`${tag} ${line}`), readOnly: true });
469
+ runtime.tunnels.set(buildId, await startServicesFor(runtime, config, buildDir2, checksDir, void 0, false, gitRemote));
470
+ } catch (err) {
471
+ return fail(`the tunnel could not start: ${describeCallFailure(err)}`);
472
+ }
473
+ if (abandoned()) return fail(ABANDONED);
474
+ const limits = {
475
+ cpus: config.build?.cpus ?? DEFAULT_CPUS,
476
+ memoryBytes: parseSize(config.build?.memory ?? DEFAULT_MEMORY) ?? parseSize(DEFAULT_MEMORY),
477
+ diskBytes: parseSize(config.build?.disk ?? DEFAULT_DISK) ?? parseSize(DEFAULT_DISK)
478
+ };
479
+ const links = vsockLinks({ destinations: config.build?.destinations?.length ?? 0, builtIns: false });
480
+ const configPath = join(buildDir2, "vm.json");
481
+ try {
482
+ linkVsockSockets({ buildDir: buildDir2, vsockPath: state.vsockPath, links });
483
+ await createBuildDisk({ host: vm.rootfs, runnerDir: runnerDirPath, bytes: limits.diskBytes, dest: state.buildDisk });
484
+ writeFileSync(
485
+ configPath,
486
+ JSON.stringify(
487
+ machineConfig(limits, { kernel: assets.kernel, rootfs: state.rootfs, buildDisk: state.buildDisk, vsock: state.vsockPath, relayPorts: links.map((link) => link.port), docker: built.docker }),
488
+ null,
489
+ 2
490
+ ),
491
+ { mode: 384 }
492
+ );
493
+ } catch (err) {
494
+ return fail(`the check environment's disk and machine could not be made: ${describeCallFailure(err)}`);
495
+ }
496
+ if (abandoned()) return fail(ABANDONED);
497
+ try {
498
+ state.cgroup = vm.cgroup.create(buildId, limits.memoryBytes);
499
+ } catch (err) {
500
+ return fail(`the check environment's memory limit could not be set: ${describeCallFailure(err)}`);
501
+ }
502
+ if (state.cgroup !== void 0) input.save(state);
503
+ const boot = async () => {
504
+ try {
505
+ state.firecracker = await vm.firecracker.spawn({ binary: assets.firecracker, configPath, consolePath, cgroup: state.cgroup });
506
+ } catch (err) {
507
+ return `Firecracker did not start: ${describeCallFailure(err)}`;
508
+ }
509
+ input.save(state);
510
+ if (abandoned()) return ABANDONED;
511
+ const ready = await waitForAgent(state.vsockPath, { attempts: BOOT_ATTEMPTS, delayMs: BOOT_POLL_MS, sleep: runtime.sleep });
512
+ if (!ready) {
513
+ const why = vm.firecracker.alive(state.firecracker) ? "its exec agent did not answer" : "it exited before its exec agent answered";
514
+ return `the microVM did not start: ${why}; its console is in ${consolePath}`;
515
+ }
516
+ if (built.docker === true) return waitForDocker(runtime, state, agentUser, () => /* @__PURE__ */ new Date());
517
+ return void 0;
518
+ };
519
+ const failed = await boot();
520
+ if (failed !== void 0) return fail(failed);
521
+ if (abandoned()) return fail(ABANDONED);
522
+ const env = checkEnvironment({ config, item: job.item, repository, hostEnv: runtime.hostEnv, containerEnv: built.containerEnv ?? {} });
523
+ return {
524
+ ok: true,
525
+ environment: {
526
+ state,
527
+ exec: async (argv, workdir, opts) => {
528
+ const ran = await vm.exec(state.vsockPath, { argv, user: agentUser, cwd: workdir, env: execEnvironment(state.imageEnv, env) });
529
+ if (opts?.logPath !== void 0) appendFileSync(opts.logPath, ran.stdout + ran.stderr);
530
+ return ran;
531
+ },
532
+ restart: async () => {
533
+ try {
534
+ await vm.firecracker.stop(state.firecracker);
535
+ rmSync(state.vsockPath, { force: true });
536
+ return await boot() === void 0;
537
+ } catch {
538
+ return false;
539
+ }
540
+ }
541
+ }
542
+ };
543
+ }
544
+ async function stopBuildMicroVm(runtime, state, jobDir) {
545
+ await copyHookErrors(runtime, state, jobDir);
546
+ if (state.firecracker === void 0) return;
547
+ await runtime.vm.firecracker.stop(state.firecracker);
548
+ if (runtime.vm.firecracker.alive(state.firecracker)) throw new Error(`the microVM (pid ${state.firecracker.pid}) is still running after it was stopped`);
549
+ }
406
550
  async function cleanupMicroVm(runtime, state, jobDir) {
407
551
  const failures = [];
408
552
  if (jobDir !== void 0) await copyHookErrors(runtime, state, jobDir);
@@ -432,8 +576,8 @@ async function cleanupMicroVm(runtime, state, jobDir) {
432
576
  async function stopOrphanedMicroVms(input) {
433
577
  const stopped = [];
434
578
  for (const { job } of input.jobs) {
435
- const state = job.microvm;
436
- if (state === void 0) continue;
579
+ const states = [job.microvm, job.checkMicrovm].filter((state) => state !== void 0);
580
+ if (states.length === 0) continue;
437
581
  if (job.pid !== void 0 && job.start !== void 0 && isSameProcess(job.pid, job.start)) {
438
582
  try {
439
583
  process.kill(-job.pid, "SIGKILL");
@@ -442,9 +586,11 @@ async function stopOrphanedMicroVms(input) {
442
586
  const deadline = Date.now() + 2e3;
443
587
  while (isSameProcess(job.pid, job.start) && Date.now() < deadline) await new Promise((resolve) => setTimeout(resolve, 10));
444
588
  }
445
- if (state.firecracker !== void 0 && input.runtime.vm.firecracker.alive(state.firecracker)) {
446
- await input.runtime.vm.firecracker.stop(state.firecracker);
447
- stopped.push(state.buildId);
589
+ for (const state of states) {
590
+ if (state.firecracker !== void 0 && input.runtime.vm.firecracker.alive(state.firecracker)) {
591
+ await input.runtime.vm.firecracker.stop(state.firecracker);
592
+ stopped.push(state.buildId);
593
+ }
448
594
  }
449
595
  }
450
596
  return stopped;
@@ -808,8 +954,8 @@ function timeoutAfter(ms, sleep) {
808
954
  return { done, cancel: () => clearTimeout(timer) };
809
955
  }
810
956
  function noCleanEnvironment(job) {
811
- const ran = job.microvm !== void 0 ? "builtin:vm" : job.build?.workDir !== void 0 ? "builtin:process" : "a provision hook of the host's own";
812
- return `the host has no clean environment for checks: this build ran under ${ran}, and checks run only in a new container from a container build's image`;
957
+ const ran = job.build?.workDir !== void 0 ? "builtin:process" : "a provision hook of the host's own";
958
+ return `the host has no clean environment for checks: this build ran under ${ran}, and checks run only in a new container or microVM from a build's image`;
813
959
  }
814
960
  async function runHostChecks(input) {
815
961
  const { config, job, jobDir, deps } = input;
@@ -874,9 +1020,8 @@ async function runHostChecks(input) {
874
1020
  log.error(`${tag} checks failed: ${reason}${keyTail}`);
875
1021
  for (const check of checks) await record(check, "fail", reason, keyTail);
876
1022
  };
877
- let container;
878
1023
  try {
879
- if (job.container === void 0) return await failAll(noCleanEnvironment(job));
1024
+ if (job.container === void 0 && job.microvm === void 0) return await failAll(noCleanEnvironment(job));
880
1025
  if (tokens === void 0) return await failAll("the build's repository tokens are no longer held, so the branch cannot be fetched");
881
1026
  const branch = job.branch;
882
1027
  if (branch === void 0) return await failAll("the build pushed no branch: the claim names none");
@@ -887,8 +1032,30 @@ async function runHostChecks(input) {
887
1032
  const limitMinutes = config.checks?.timeLimitMinutes ?? DEFAULT_CHECK_TIME_LIMIT_MINUTES;
888
1033
  const minutes = `${limitMinutes} ${limitMinutes === 1 ? "minute" : "minutes"}`;
889
1034
  const environmentLog = join(jobDir, "checks", "environment.log");
1035
+ if (job.microvm !== void 0) {
1036
+ if (deps.endBuildMicroVm === void 0) return await failAll("the runner has no way to stop the build's microVM, so no check environment was made");
1037
+ try {
1038
+ await deps.endBuildMicroVm();
1039
+ } catch (err) {
1040
+ return await failAll(`the build's microVM could not be stopped, so no check environment was made: ${describeCallFailure(err)}`);
1041
+ }
1042
+ }
1043
+ let abandoned = false;
890
1044
  const setup = async () => {
891
- const started = await startCheckContainer({
1045
+ const started = job.microvm !== void 0 ? await startCheckMicroVm({
1046
+ config,
1047
+ runtime,
1048
+ job,
1049
+ jobDir,
1050
+ tokens,
1051
+ log,
1052
+ aborted: () => abandoned,
1053
+ save: (state) => {
1054
+ if (abandoned) return;
1055
+ job.checkMicrovm = state;
1056
+ writeJobFile(jobFilePath, job);
1057
+ }
1058
+ }) : await startCheckContainer({
892
1059
  config,
893
1060
  runtime,
894
1061
  job,
@@ -901,8 +1068,7 @@ async function runHostChecks(input) {
901
1068
  }
902
1069
  });
903
1070
  if (!started.ok) return `the check environment could not be made: ${started.reason}`;
904
- container = started.container;
905
- const made = started.container;
1071
+ const made = "container" in started ? started.container : started.environment;
906
1072
  const steps = [
907
1073
  {
908
1074
  argv: ["git", "config", "--global", `url.http://127.0.0.1:${RELAY_PORT}/github.com/.insteadOf`, "https://github.com/"],
@@ -916,10 +1082,11 @@ async function runHostChecks(input) {
916
1082
  }
917
1083
  ];
918
1084
  for (const step of steps) {
1085
+ if (abandoned) return "the check environment setup ran out of time";
919
1086
  const ran = await made.exec(step.argv, step.workdir, { logPath: environmentLog });
920
1087
  if (ran.status !== 0) return `the check environment could not be prepared: ${step.failed(ran.status)}`;
921
1088
  }
922
- for (const step of lifecycleSteps(job.container?.lifecycle ?? {})) {
1089
+ for (const step of lifecycleSteps((job.container ?? job.microvm)?.lifecycle ?? {})) {
923
1090
  log.line(`${tag} checks lifecycle ${step.name} (${step.source})`);
924
1091
  const ran = await made.exec(step.argv, CHECKOUT_DIR, { logPath: environmentLog });
925
1092
  if (ran.status !== 0) return `the check environment could not be prepared: ${lifecycleFailureReason(step, ran.status)}`;
@@ -929,8 +1096,14 @@ async function runHostChecks(input) {
929
1096
  const setupTimer = timeoutAfter(limitMinutes * 6e4, deps.sleep);
930
1097
  let settled;
931
1098
  try {
932
- const outcome = await Promise.race([setup(), setupTimer.done]);
933
- if (outcome === "timeout") return await failAll(`the check environment setup ran out of time after ${minutes}`, LIMIT_KEY_TAIL);
1099
+ const outcome = await Promise.race([
1100
+ setup().catch((err) => `the check environment could not be made: ${describeCallFailure(err)}`),
1101
+ setupTimer.done
1102
+ ]);
1103
+ if (outcome === "timeout") {
1104
+ abandoned = true;
1105
+ return await failAll(`the check environment setup ran out of time after ${minutes}`, LIMIT_KEY_TAIL);
1106
+ }
934
1107
  settled = outcome;
935
1108
  } finally {
936
1109
  setupTimer.cancel();
@@ -976,12 +1149,16 @@ async function runHostChecks(input) {
976
1149
  } catch (err) {
977
1150
  log.error(`${tag} checks stopped: ${describeCallFailure(err)}`);
978
1151
  } finally {
979
- if (container !== void 0 || job.checkContainer !== void 0) {
980
- const state = container?.state ?? job.checkContainer;
981
- await cleanupContainer(runtime, state).catch((err) => log.error(`${tag} removing the check environment failed: ${describeCallFailure(err)}`));
1152
+ if (job.checkContainer !== void 0) {
1153
+ await cleanupContainer(runtime, job.checkContainer).catch((err) => log.error(`${tag} removing the check environment failed: ${describeCallFailure(err)}`));
982
1154
  delete job.checkContainer;
983
1155
  writeJobFile(jobFilePath, job);
984
1156
  }
1157
+ if (job.checkMicrovm !== void 0) {
1158
+ await cleanupMicroVm(runtime, job.checkMicrovm).catch((err) => log.error(`${tag} removing the check environment failed: ${describeCallFailure(err)}`));
1159
+ delete job.checkMicrovm;
1160
+ writeJobFile(jobFilePath, job);
1161
+ }
985
1162
  rmSync(scratch, { recursive: true, force: true });
986
1163
  if (existsSync(scratch)) log.error(`${tag} the checks' scratch directory could not be removed`);
987
1164
  log.line(`${tag} checks done (${formatDuration(now().getTime() - startedMs)})`);
@@ -1461,6 +1638,36 @@ async function finishJob(job, jobDirPath, outcome, config, deps, ending) {
1461
1638
  job.reason = scrubReason(ending.reason, config);
1462
1639
  writeJobFile(jobFilePath, job);
1463
1640
  }
1641
+ const uploadSessions = async (workdir) => {
1642
+ if (job.container === void 0 && job.microvm === void 0 && job.build?.exec === void 0 && workdirProblem(workdir) !== void 0) {
1643
+ log.line(`${tag} sync skipped: workdir ${workdir} is gone`);
1644
+ } else if (job.itemKey === void 0) {
1645
+ log.line(`${tag} sync skipped: the job file holds no item key`);
1646
+ } else {
1647
+ log.line(`${tag} sync started`);
1648
+ const startedMs = now().getTime();
1649
+ const sync = build !== void 0 ? await syncInContainer({ config, runtime: build.runtime, job, jobDir: jobDirPath }) : vmBuild !== void 0 ? await syncInMicroVm({ config, runtime: vmBuild.runtime, job, jobDir: jobDirPath }) : job.build?.exec !== void 0 ? await runSyncViaExec(config, job, jobDirPath, spawnFn) : await runSync(config, job.item, job.itemKey, workdir, jobDirPath, spawnFn, job.branch, job.commitIdentity?.author);
1650
+ const took = formatDuration(now().getTime() - startedMs);
1651
+ if (sync.ok) log.line(`${tag} sync ok (${took})`);
1652
+ else log.error(`${tag} sync failed: ${sync.reason ?? "no reason given"} \xB7 continuing`);
1653
+ const left = spooledArtifactIds(deps.home ?? homeFromEnv());
1654
+ if (left.length > 0) {
1655
+ job.spooledArtifacts = left.slice(0, MAX_NAMED_SPOOLED);
1656
+ const named = job.spooledArtifacts.join(", ");
1657
+ log.error(
1658
+ `${tag} ${left.length} spooled artifact(s) not uploaded: ${named}${left.length > MAX_NAMED_SPOOLED ? `, and ${left.length - MAX_NAMED_SPOOLED} more` : ""}`
1659
+ );
1660
+ }
1661
+ }
1662
+ };
1663
+ const endBuildMicroVm = vmBuild === void 0 ? void 0 : async () => {
1664
+ if (job.workdir && job.sessionsSynced !== true) {
1665
+ await uploadSessions(job.workdir);
1666
+ job.sessionsSynced = true;
1667
+ writeJobFile(jobFilePath, job);
1668
+ }
1669
+ await stopBuildMicroVm(vmBuild.runtime, vmBuild.state, jobDirPath);
1670
+ };
1464
1671
  if (job.checkContainer !== void 0 && job.checksRan === true) {
1465
1672
  try {
1466
1673
  await cleanupContainer(deps.container ?? createContainerRuntime(), job.checkContainer);
@@ -1470,6 +1677,15 @@ async function finishJob(job, jobDirPath, outcome, config, deps, ending) {
1470
1677
  log.error(`${tag} removing an earlier attempt's check environment failed: ${describeCallFailure(err)} \xB7 continuing`);
1471
1678
  }
1472
1679
  }
1680
+ if (job.checkMicrovm !== void 0 && job.checksRan === true) {
1681
+ try {
1682
+ await cleanupMicroVm(deps.container ?? createContainerRuntime(), job.checkMicrovm);
1683
+ delete job.checkMicrovm;
1684
+ writeJobFile(jobFilePath, job);
1685
+ } catch (err) {
1686
+ log.error(`${tag} removing an earlier attempt's check environment failed: ${describeCallFailure(err)} \xB7 continuing`);
1687
+ }
1688
+ }
1473
1689
  if (outcome === "ok" && !job.cleanupRan && job.checksRan !== true) {
1474
1690
  setStage(job, jobDirPath, "checks");
1475
1691
  job.checksRan = true;
@@ -1479,7 +1695,7 @@ async function finishJob(job, jobDirPath, outcome, config, deps, ending) {
1479
1695
  config,
1480
1696
  job,
1481
1697
  jobDir: jobDirPath,
1482
- deps: { fetchImpl: deps.fetchImpl, runtime: deps.container ?? createContainerRuntime(), log, now: deps.now, ...deps.hostChecks }
1698
+ deps: { fetchImpl: deps.fetchImpl, runtime: deps.container ?? createContainerRuntime(), log, now: deps.now, endBuildMicroVm, ...deps.hostChecks }
1483
1699
  });
1484
1700
  } catch (err) {
1485
1701
  log.error(`${tag} the host's checks stopped: ${describeCallFailure(err)} \xB7 continuing`);
@@ -1504,26 +1720,8 @@ async function finishJob(job, jobDirPath, outcome, config, deps, ending) {
1504
1720
  if (!job.cleanupRan) {
1505
1721
  if (job.workdir) {
1506
1722
  setStage(job, jobDirPath, "sync");
1507
- if (job.container === void 0 && job.microvm === void 0 && job.build?.exec === void 0 && workdirProblem(job.workdir) !== void 0) {
1508
- log.line(`${tag} sync skipped: workdir ${job.workdir} is gone`);
1509
- } else if (job.itemKey === void 0) {
1510
- log.line(`${tag} sync skipped: the job file holds no item key`);
1511
- } else {
1512
- log.line(`${tag} sync started`);
1513
- const startedMs = now().getTime();
1514
- const sync = build !== void 0 ? await syncInContainer({ config, runtime: build.runtime, job, jobDir: jobDirPath }) : vmBuild !== void 0 ? await syncInMicroVm({ config, runtime: vmBuild.runtime, job, jobDir: jobDirPath }) : job.build?.exec !== void 0 ? await runSyncViaExec(config, job, jobDirPath, spawnFn) : await runSync(config, job.item, job.itemKey, job.workdir, jobDirPath, spawnFn, job.branch, job.commitIdentity?.author);
1515
- const took = formatDuration(now().getTime() - startedMs);
1516
- if (sync.ok) log.line(`${tag} sync ok (${took})`);
1517
- else log.error(`${tag} sync failed: ${sync.reason ?? "no reason given"} \xB7 continuing`);
1518
- const left = spooledArtifactIds(deps.home ?? homeFromEnv());
1519
- if (left.length > 0) {
1520
- job.spooledArtifacts = left.slice(0, MAX_NAMED_SPOOLED);
1521
- const named = job.spooledArtifacts.join(", ");
1522
- log.error(
1523
- `${tag} ${left.length} spooled artifact(s) not uploaded: ${named}${left.length > MAX_NAMED_SPOOLED ? `, and ${left.length - MAX_NAMED_SPOOLED} more` : ""}`
1524
- );
1525
- }
1526
- }
1723
+ if (job.sessionsSynced === true) log.line(`${tag} sync done before the build's microVM stopped`);
1724
+ else await uploadSessions(job.workdir);
1527
1725
  }
1528
1726
  setStage(job, jobDirPath, "cleanup");
1529
1727
  try {
@@ -5,7 +5,7 @@ import { RepoFilter } from './chunk-TBT347UY.js';
5
5
  import { hookExecSpoolPath } from './chunk-F7CU5ABH.js';
6
6
  import { applyReportedCost } from './chunk-BKO6JEZI.js';
7
7
  import { migrateLegacyArtifactSidecars, planArtifactUpload, artifactsStatePath, sanitizeTerminal, artifactBlobsDir, resolveArtifactTurnIndex, isArtifactTombstoned, ARTIFACT_RETRY_MAX_AGE_MS, writeArtifactSpoolState, reclaimUnneededArtifactBlobs, neededArtifactShas } from './chunk-ZYHKJKSU.js';
8
- import { setCustomScrubbers, setLocalIdentity, tailTranscript, enrichSessionRepo, scanAll, setRepoRoot, resolveRepoIdentity, sessionIdFromPath, scrubDeep, SOURCE_ADAPTERS } from './chunk-D6FXM5OW.js';
8
+ import { setCustomScrubbers, setLocalIdentity, tailTranscript, enrichSessionRepo, scanAll, setRepoRoot, resolveRepoIdentity, sessionIdFromPath, scrubDeep, SOURCE_ADAPTERS } from './chunk-GAYWY4GI.js';
9
9
  import { C } from './chunk-YRYMNBGQ.js';
10
10
  import { readLaunchSpoolState, launchStatusIsDecisive, launchedItemsBySession, launchesPath } from './chunk-GQD22KKN.js';
11
11
  import { readLatestProvenanceForSession, readContextProvenance } from './chunk-ZJSNLVCE.js';
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
3
  import { runnerUnit, UNIT_MARKER } from './chunk-FBTE2A2X.js';
4
- import { checkFit } from './chunk-OUUINEYB.js';
5
- import { builtinKind } from './chunk-DTXOJDEH.js';
4
+ import { checkFit } from './chunk-JKIWMRFH.js';
5
+ import { builtinKind } from './chunk-PWBILI5P.js';
6
6
  import { runnerDir } from './chunk-ZM2IMMYN.js';
7
7
  import { dirname } from 'path';
8
8
  import { spawnSync } from 'child_process';
@@ -165,7 +165,11 @@ function installService(request, deps) {
165
165
  function ensureLoginTask(deps, unitName) {
166
166
  const tasks = deps.tasks;
167
167
  const name = taskNameFor(unitName);
168
- const command = taskCommand(deps.distro ?? "Ubuntu");
168
+ if (deps.distro === void 0 || deps.distro === "") {
169
+ const manual2 = `from Windows, run: schtasks /Create /F /TN ${name} /SC ONLOGON /TR "${taskCommand("<distribution>")}", with the name \`wsl -l\` shows`;
170
+ return [`WSL_DISTRO_NAME is not set, as under sudo, so the scheduled task that starts WSL2 at login was not made: ${manual2}`];
171
+ }
172
+ const command = taskCommand(deps.distro);
169
173
  const manual = `from Windows, run: schtasks /Create /F /TN ${name} /SC ONLOGON /TR "${command}"`;
170
174
  if (tasks === void 0 || !tasks.available()) {
171
175
  return [`Windows interop is off, so the scheduled task that starts WSL2 at login was not made: ${manual}`];