@outerlayer/cli 0.5.1 → 0.7.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 +29 -0
  2. package/README.md +19 -0
  3. package/dist/{agent-setup-IFH3FB5X.js → agent-setup-TXLDY5RV.js} +7 -6
  4. package/dist/{build-PAEKVYVK.js → build-JF5UOWKJ.js} +2 -1
  5. package/dist/build-info.json +1 -1
  6. package/dist/{check-4Q33B5ZE.js → check-ASZL5BEO.js} +39 -23
  7. package/dist/chunk-2VO25HHO.js +75 -0
  8. package/dist/{chunk-XDDW4FRS.js → chunk-5FWNEQNZ.js} +3 -3
  9. package/dist/{chunk-CHLVJNTE.js → chunk-6IYLDODX.js} +18 -6
  10. package/dist/{chunk-W7UTFZDH.js → chunk-6QWV3YDJ.js} +2 -2
  11. package/dist/{chunk-EGABNHZG.js → chunk-6ZZHWY2Z.js} +149 -18
  12. package/dist/{lima-XN65D7GN.js → chunk-77MM5RYD.js} +0 -1
  13. package/dist/chunk-7FTNIAHS.js +211 -0
  14. package/dist/{chunk-EMY4I27X.js → chunk-7ZR5UPMB.js} +555 -5
  15. package/dist/{chunk-F4NU4H3V.js → chunk-AII7CKTH.js} +133 -35
  16. package/dist/{chunk-ZXBITXTS.js → chunk-AIW6GLET.js} +1 -1
  17. package/dist/{chunk-UWH5DSUP.js → chunk-AJKEHABI.js} +507 -90
  18. package/dist/{chunk-RTXKYLCA.js → chunk-AQ54MKSQ.js} +2 -2
  19. package/dist/{chunk-Z62HNKEJ.js → chunk-CEBIA5FD.js} +31 -5
  20. package/dist/{chunk-EX6M5JPR.js → chunk-D6FXM5OW.js} +6302 -28611
  21. package/dist/{chunk-RMAZ2NWA.js → chunk-DGAIVMMN.js} +23 -2
  22. package/dist/{chunk-3DD6AOWC.js → chunk-DTXOJDEH.js} +26 -2
  23. package/dist/{chunk-T52PKE2Y.js → chunk-E4PIFIC3.js} +244 -17
  24. package/dist/{chunk-H5NCNVDA.js → chunk-EV3M3EH7.js} +2 -2
  25. package/dist/chunk-FBTE2A2X.js +38 -0
  26. package/dist/{chunk-DVDEBNQJ.js → chunk-GQD22KKN.js} +17 -6
  27. package/dist/{chunk-BCJNHMZT.js → chunk-HHJDN6AU.js} +18 -3
  28. package/dist/chunk-HKMPVPO7.js +55 -0
  29. package/dist/{chunk-UFFXNXLQ.js → chunk-JPR2WHHF.js} +31 -29
  30. package/dist/{chunk-ZMYPLFG3.js → chunk-M3SQ4YGD.js} +1 -1
  31. package/dist/{chunk-H4CK2YK4.js → chunk-MQPRB5K7.js} +18 -10
  32. package/dist/{chunk-AUFG23AA.js → chunk-OD2VTIHN.js} +19 -47
  33. package/dist/chunk-OUUINEYB.js +27 -0
  34. package/dist/chunk-PUN7LMNS.js +39 -0
  35. package/dist/{install-cmd-7WXOIFDO.js → chunk-Q5G2ODUZ.js} +1 -2
  36. package/dist/{chunk-B2G7JHHB.js → chunk-R7STSBLC.js} +1 -1
  37. package/dist/{chunk-OR6AEEDO.js → chunk-SSU747O6.js} +85 -25
  38. package/dist/chunk-T6OIJQRT.js +31 -0
  39. package/dist/{chunk-YYYXRJUW.js → chunk-TI7B7UEO.js} +1 -1
  40. package/dist/{chunk-WO2BXCTQ.js → chunk-VXLJORHL.js} +1 -1
  41. package/dist/{chunk-CBPPJSAR.js → chunk-WVGSB3Z2.js} +2 -2
  42. package/dist/{chunk-JLDZTG3A.js → chunk-X2FSFU4C.js} +4 -1
  43. package/dist/chunk-YNJEOGDL.js +22434 -0
  44. package/dist/{chunk-W4SQCOZU.js → chunk-ZOAEPWGL.js} +1 -1
  45. package/dist/{chunk-WOBNRVQT.js → chunk-ZYHKJKSU.js} +1 -1
  46. package/dist/{chunk-WIOZAJ2W.js → chunk-ZZXLM64C.js} +1 -1
  47. package/dist/{cli-LHSUYYMI.js → cli-LEVCGAQV.js} +154 -79
  48. package/dist/command-FLSYCGIB.js +187 -0
  49. package/dist/config-I3TVFYLH.js +10 -0
  50. package/dist/{connect-cmd-OJBFHZWP.js → connect-cmd-MJMUONDY.js} +4 -4
  51. package/dist/{context-adopt-UMA4O5NY.js → context-adopt-CE4KDXS3.js} +5 -4
  52. package/dist/{context-materialize-G2FUFC72.js → context-materialize-QL33HDJN.js} +5 -4
  53. package/dist/{dist-WDV7U7UJ.js → dist-PXBSA7QX.js} +2 -1
  54. package/dist/{doctor-MOA3RIEP.js → doctor-FVDT3KJZ.js} +46 -19
  55. package/dist/{emit-artifact-cmd-FDWYKURA.js → emit-artifact-cmd-7RPMRSVP.js} +13 -7
  56. package/dist/{emit-cmd-DCERAU27.js → emit-cmd-2QRMYPYO.js} +4 -3
  57. package/dist/{emit-criteria-cmd-OCXHLK34.js → emit-criteria-cmd-MKI7WETO.js} +12 -3
  58. package/dist/{emit-finding-cmd-POW7PKIA.js → emit-finding-cmd-RMLGZASB.js} +14 -3
  59. package/dist/{emit-result-cmd-ID35SH53.js → emit-result-cmd-MTXAZ2GV.js} +15 -9
  60. package/dist/{exec-client-4XGXV3XN.js → exec-client-NPYNUAF3.js} +4 -3
  61. package/dist/{guest-init-GENRHNP7.js → guest-init-JWNNAH2H.js} +4 -3
  62. package/dist/{hook-fast-EYENPK6F.js → hook-fast-XBOZ3N6L.js} +1 -1
  63. package/dist/{import-capture-cmd-AVZ4VIVJ.js → import-capture-cmd-T4556ELW.js} +3 -2
  64. package/dist/{import-ruler-cmd-GUHL6IY5.js → import-ruler-cmd-SG3UVGI7.js} +2 -1
  65. package/dist/index.js +3 -2
  66. package/dist/{init-FYDJAKTM.js → init-BACXESVV.js} +21 -14
  67. package/dist/{init-2IPOIXJV.js → init-VLW7H6RW.js} +40 -62
  68. package/dist/{init-cmd-KLIDRVXQ.js → init-cmd-VJFB5L7X.js} +10 -9
  69. package/dist/inspect-XAUSB7L3.js +63 -0
  70. package/dist/install-NMAPLLBQ.js +14 -0
  71. package/dist/install-cmd-B5SEU3G7.js +8 -0
  72. package/dist/lima-7NPCNGWT.js +6 -0
  73. package/dist/{logs-7RYUH5A6.js → logs-H55NWW4H.js} +1 -0
  74. package/dist/loop-D3243JKT.js +64 -0
  75. package/dist/{machine-ECU3QCZD.js → machine-EB3GEVI4.js} +20 -13
  76. package/dist/session-sender-LMACEJJS.js +29 -0
  77. package/dist/{starter-pack-LIZYMKYQ.js → starter-pack-MUXMAU4S.js} +4 -3
  78. package/dist/{status-CLATHCTQ.js → status-F4THTJJK.js} +36 -16
  79. package/dist/{sync-cmd-SWQ5KLPL.js → sync-cmd-M6JA3OEE.js} +7 -7
  80. package/dist/{watch-3YW6Z3DQ.js → watch-NB5TGPEZ.js} +7 -7
  81. package/dist/{work-claim-cmd-3OPWA77E.js → work-claim-cmd-UHMWOS3G.js} +14 -7
  82. package/dist/{work-cmd-NUR4CAG6.js → work-cmd-6UMCFTAW.js} +9 -3
  83. package/dist/{work-comment-cmd-6TTTYGHM.js → work-comment-cmd-E4LEUIXZ.js} +9 -3
  84. package/dist/{work-launch-YI4CJDAP.js → work-launch-R5HTAP6X.js} +1 -1
  85. package/dist/{work-open-pr-cmd-KZ7QQFLD.js → work-open-pr-cmd-OPZWGSEQ.js} +10 -4
  86. package/dist/{work-pr-cmd-QNUBE6IQ.js → work-pr-cmd-IDIAFFRU.js} +9 -2
  87. package/package.json +2 -1
  88. package/skill-pack/template/build/scripts/record-criteria.mjs +2 -1
  89. package/dist/chunk-D2KWEAN6.js +0 -55
  90. package/dist/chunk-ZOC7JSYC.js +0 -83
  91. package/dist/config-HL2AIR3K.js +0 -9
  92. package/dist/loop-NEBUGD2Q.js +0 -45
@@ -1,33 +1,79 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- import { LEASE_SECONDS, releaseLease, isUnfinished, writeJobFile, setStage, jobEnv, runHook, describeHookEnd, provisionMicroVm, deliverCommitHooks, provisionProcess, provisionEnvironment, readProvisionOut, resolveHostBuild, workdirProblem, listenOnBroker, hostEnvironment, hostCommandEnvironment, execArgv, microVmCommandLine, runCommand, execCommandLine, startHostServices, finishJob, isGroupAlive, outcomeFromExitCode, needsRenew, renewLease, RENEW_INTERVAL_SECONDS, isTimedOut, readCpuUsec, isIdle, stopOrphanedMicroVms, unfinishedJobs, listAdoptableJobs, retryFailedReleases, recoverLostJobs, readJobFile, hookLimitsFor, readJobFileChecked, ALREADY_RELEASED } from './chunk-UWH5DSUP.js';
4
- import { RUNNER_PROTOCOL } from './chunk-ZXBITXTS.js';
5
- import { readHousekeepingRecord, writeHousekeepingRecord } from './chunk-UGP76IZ5.js';
6
3
  import { fetchVersionMeta, npmVersion, npmSupportsProvenance, fetchTarballDigest, fetchProvenance, installVersion, auditSignatures } from './chunk-ABIWDUMS.js';
7
- import { readMarker, currentVersion, removeMarker, writeMarker, entryIn, versionDir, switchCurrent, cleanVersions, isInLayout, startedThroughCurrent, stagingDir } from './chunk-YYYXRJUW.js';
4
+ import { readMarker, currentVersion, removeMarker, writeMarker, entryIn, versionDir, switchCurrent, cleanVersions, isInLayout, startedThroughCurrent, stagingDir } from './chunk-TI7B7UEO.js';
5
+ import { LEASE_SECONDS, releaseLease, isUnfinished, setStage, jobEnv, runHook, describeHookEnd, provisionMicroVm, deliverCommitHooks, provisionProcess, provisionEnvironment, readProvisionOut, resolveHostBuild, workdirProblem, listenOnBroker, hostEnvironment, hostCommandEnvironment, execArgv, microVmCommandLine, runCommand, execCommandLine, startSender, startHostServices, finishJob, isGroupAlive, outcomeFromExitCode, isTimedOut, readCpuUsec, isIdle, stopOrphanedMicroVms, unfinishedJobs, listAdoptableJobs, retryFailedReleases, recoverLostJobs, hookLimitsFor, needsRenew, renewLease, RENEW_INTERVAL_SECONDS, ALREADY_RELEASED } from './chunk-AJKEHABI.js';
6
+ import { writeJobFile, readJobFile, readJobFileChecked } from './chunk-T6OIJQRT.js';
7
+ import { RUNNER_PROTOCOL } from './chunk-AIW6GLET.js';
8
+ import { readHousekeepingRecord, writeHousekeepingRecord } from './chunk-UGP76IZ5.js';
9
+ import { placeholderQueues, placeholderProblem } from './chunk-6IYLDODX.js';
8
10
  import { freeBytes, formatBytes } from './chunk-YYYRBYWG.js';
9
11
  import { RepoFilter } from './chunk-TBT347UY.js';
10
- import { describeCallFailure, retrySend, shortClaim, relativeToRunner, formatDuration, provisionContainer, lifecycleSteps, lifecycleFailureReason, prepareBranch, containerCommandLine, BuildTokens, classifyRefusal, refusalCode, formatClock, measureDisk, createRunnerLog, ensureTunnel, keepGhConfigCurrent, createContainerRuntime, runnerLabelValue, BUILD_LABEL, RUNNER_LABEL } from './chunk-T52PKE2Y.js';
12
+ import { checkFit } from './chunk-OUUINEYB.js';
13
+ import { describeCallFailure, retrySend, shortClaim, relativeToRunner, formatDuration, provisionContainer, lifecycleSteps, lifecycleFailureReason, prepareBranch, containerCommandLine, BuildTokens, classifyRefusal, refusalCode, measureDisk, createRunnerLog, setUpRunnerCgroup, ensureTunnel, keepGhConfigCurrent, createContainerRuntime, formatClock, runnerLabelValue, BUILD_LABEL, RUNNER_LABEL } from './chunk-E4PIFIC3.js';
11
14
  import { startTimeOf, claimPidFile, readPidFile, isSameProcess, releasePidFile } from './chunk-N5FOE5PS.js';
12
15
  import { spawnRun } from './chunk-774HEQL6.js';
13
- import { isSignatureRefusal, WorkCommandError, getJson, postJson } from './chunk-RMAZ2NWA.js';
14
- import { isBuiltinContainer, isBuiltinVm, isBuiltinProcess, loadRunnerConfig, checkHooks, builtinKind } from './chunk-3DD6AOWC.js';
16
+ import { isSignatureRefusal, WorkCommandError, getJson, postJson } from './chunk-DGAIVMMN.js';
17
+ import { isBuiltinContainer, isBuiltinVm, isBuiltinProcess, loadRunnerConfig, checkHooks, builtinKind } from './chunk-DTXOJDEH.js';
15
18
  import { exceedsDisk } from './chunk-F6GFZXZ3.js';
16
- import { parseClaimIdentity, sharedUserEnvironment, JOB_MOUNT } from './chunk-AUFG23AA.js';
19
+ import { sharedUserEnvironment, JOB_MOUNT, claudeCredential } from './chunk-OD2VTIHN.js';
20
+ import { parseClaimIdentity } from './chunk-HKMPVPO7.js';
17
21
  import { resolveVersion } from './chunk-RA3O54FC.js';
18
22
  import { loadHostKey, ensureHostKey, signerFor } from './chunk-M5POMKKW.js';
19
23
  import { RUNNER_DIR_MODE, runnerDir, buildDir, attemptStamp, jobDir, jobsDir, pidFilePath, spoolDir, RUNNER_FILE_MODE, rootfsDir } from './chunk-ZM2IMMYN.js';
20
24
  import { HOOKS_DIR_NAME, writeCommitHooks } from './chunk-XCCLFVXM.js';
21
25
  import { randomBytes } from 'crypto';
22
- import { mkdirSync, existsSync, chmodSync, statSync, readdirSync, readFileSync, rmSync, renameSync, writeFileSync, lstatSync } from 'fs';
26
+ import { mkdirSync, existsSync, chmodSync, statSync, readdirSync, readFileSync, rmSync, renameSync, writeFileSync, closeSync, openSync, constants, lstatSync } from 'fs';
23
27
  import { join, dirname } from 'path';
28
+ import { totalmem } from 'os';
24
29
 
25
30
  createRequire(import.meta.url);
31
+ function openKvm() {
32
+ try {
33
+ closeSync(openSync("/dev/kvm", constants.O_RDWR));
34
+ return "usable";
35
+ } catch (err) {
36
+ return err.code === "ENOENT" ? "absent" : "no-permission";
37
+ }
38
+ }
39
+ function realBlockedProbes() {
40
+ return { env: process.env, kvm: openKvm, totalMemoryBytes: totalmem };
41
+ }
42
+ var DEFAULT_BUILD_MEMORY = "8g";
43
+ function checkBlocked(config, probes) {
44
+ const placeholders = placeholderQueues(config);
45
+ if (placeholders.length > 0) return { reason: `${placeholderProblem(placeholders)}. The runner takes work again once the config is edited` };
46
+ const kind = builtinKind(config.hooks.provision);
47
+ if (kind === "container" || kind === "vm") {
48
+ const fit = checkFit({
49
+ concurrency: config.concurrency,
50
+ buildMemory: config.build?.memory ?? DEFAULT_BUILD_MEMORY,
51
+ usableBytes: probes.totalMemoryBytes()
52
+ });
53
+ if (!fit.fits) return { reason: `${fit.message}. Lower runner.concurrency or runner.build.memory` };
54
+ }
55
+ const kvm = kind === "vm" ? probes.kvm() : "usable";
56
+ if (kvm === "absent") {
57
+ return { reason: "/dev/kvm does not exist on this machine, so builtin:vm cannot start a microVM. Use a host with KVM enabled, or builtin:container hooks" };
58
+ }
59
+ if (kvm !== "usable") {
60
+ return {
61
+ reason: "the runner cannot open /dev/kvm, so builtin:vm cannot start a microVM. Add the runner's account to the kvm group, then restart the service, because a service reads its groups only when it starts"
62
+ };
63
+ }
64
+ if (kind !== "own" && claudeCredential(probes.env) === void 0) {
65
+ return {
66
+ reason: "there is no Claude credential in the runner's environment. Put CLAUDE_CODE_OAUTH_TOKEN (from `claude setup-token`) or ANTHROPIC_API_KEY in the runner.env beside the config, then restart the service"
67
+ };
68
+ }
69
+ return void 0;
70
+ }
26
71
 
27
72
  // src/runner/heartbeat.ts
28
73
  var HEARTBEAT_TIMEOUT_MS = 1e4;
29
74
  var ERROR_CODE_MAX = 100;
30
75
  var ERROR_MESSAGE_MAX = 500;
76
+ var BLOCKED_REASON_MAX = 500;
31
77
  var UNREACHABLE_MESSAGE = "the request did not reach the platform";
32
78
  function noPlatformMessage(status) {
33
79
  return `the request got HTTP ${status} with no error message from the platform`;
@@ -73,7 +119,9 @@ async function sendHeartbeat(args, fetchImpl) {
73
119
  ...args.lastUpdate.at !== void 0 ? { at: args.lastUpdate.at } : {}
74
120
  },
75
121
  // Free disk on the runner's file system, in bytes. Sent only while held, so a host with room sends nothing about its disk.
76
- lowDisk: args.lowDisk === void 0 ? void 0 : { free: args.lowDisk.free, needed: args.lowDisk.needed }
122
+ lowDisk: args.lowDisk === void 0 ? void 0 : { free: args.lowDisk.free, needed: args.lowDisk.needed },
123
+ // Why the runner claims nothing. Sent only while it holds, so a runner that takes work says nothing about it.
124
+ blocked: args.blocked === void 0 ? void 0 : { reason: cutForGateway(args.blocked.reason, BLOCKED_REASON_MAX) }
77
125
  };
78
126
  try {
79
127
  const response = await postJson(
@@ -643,7 +691,7 @@ function selectJobDirs(entries, nowMs, keepDays) {
643
691
  function liveBuildIds(jobs) {
644
692
  const ids = /* @__PURE__ */ new Set();
645
693
  for (const job of jobs) {
646
- for (const id of [job.container?.buildId, job.microvm?.buildId, job.build?.buildId]) if (id !== void 0) ids.add(id);
694
+ for (const id of [job.container?.buildId, job.microvm?.buildId, job.build?.buildId, job.checkContainer?.buildId]) if (id !== void 0) ids.add(id);
647
695
  }
648
696
  return ids;
649
697
  }
@@ -964,7 +1012,8 @@ async function sendTickHeartbeat(state, workRequest) {
964
1012
  slotsTotal: state.config.concurrency,
965
1013
  workRequest: next,
966
1014
  lastUpdate: readLastUpdate(state.config.configPath),
967
- lowDisk: state.lowDisk
1015
+ lowDisk: state.lowDisk,
1016
+ blocked: state.blocked
968
1017
  },
969
1018
  state.deps.fetchImpl
970
1019
  );
@@ -1088,6 +1137,25 @@ async function tick(state) {
1088
1137
  }
1089
1138
  await sendTickHeartbeat(state, state.lastWorkRequest);
1090
1139
  }
1140
+ function holdWhenBlocked(state) {
1141
+ const probes = {
1142
+ ...realBlockedProbes(),
1143
+ ...state.deps.container?.hostEnv === void 0 ? {} : { env: state.deps.container.hostEnv },
1144
+ ...state.deps.blocked
1145
+ };
1146
+ const blocked = checkBlocked(state.config, probes);
1147
+ const log = logOf(state);
1148
+ if (blocked !== void 0) {
1149
+ if (state.blocked?.reason !== blocked.reason) log.line(`claiming nothing: ${blocked.reason}`);
1150
+ state.blocked = blocked;
1151
+ return true;
1152
+ }
1153
+ if (state.blocked !== void 0) {
1154
+ state.blocked = void 0;
1155
+ log.line("the runner can run its work again \xB7 taking work");
1156
+ }
1157
+ return false;
1158
+ }
1091
1159
  function holdOffForDisk(state) {
1092
1160
  const minimum = state.config.housekeeping?.minFreeDisk ?? 0;
1093
1161
  const root = runnerDir(state.config.configPath);
@@ -1145,6 +1213,7 @@ async function housekeepingPass(state) {
1145
1213
  async function runTick(state) {
1146
1214
  reloadConfig(state);
1147
1215
  if (await updateWhenIdle(state)) return;
1216
+ if (holdWhenBlocked(state)) return;
1148
1217
  if (holdOffForDisk(state)) return;
1149
1218
  const log = logOf(state);
1150
1219
  const repoFilter = new RepoFilter(state.config.repos);
@@ -1199,7 +1268,7 @@ var CLAIM_REFUSAL_CODES = new Set(
1199
1268
  "pull_request_from_fork pull_request_on_default_branch pull_request_ambiguous branch_claimed_elsewhere".split(" ")
1200
1269
  );
1201
1270
  async function tryClaimAndStart(state, item, queue) {
1202
- const { postJson: postJson2 } = await import('./work-cmd-NUR4CAG6.js');
1271
+ const { postJson: postJson2 } = await import('./work-cmd-6UMCFTAW.js');
1203
1272
  const log = logOf(state);
1204
1273
  const hostKey = await loadHostKey(state.config.configPath);
1205
1274
  const claim = await retrySend(
@@ -1327,6 +1396,7 @@ async function startJob(state, item, queue, claimId, itemKey, branch, identity)
1327
1396
  itemKey,
1328
1397
  branch,
1329
1398
  commitIdentity: identity,
1399
+ ...item.specRepository === void 0 || item.specRepository === null || item.specRepository === "" ? {} : { repository: item.specRepository },
1330
1400
  startedAt: startedAt.toISOString()
1331
1401
  };
1332
1402
  writeJobFile(jobFilePath, job);
@@ -1678,6 +1748,17 @@ async function startJob(state, item, queue, claimId, itemKey, branch, identity)
1678
1748
  writeJobFile(jobFilePath, job);
1679
1749
  const commandStartedMs = nowOf(state).getTime();
1680
1750
  log.line(`${tag} command started \xB7 pid ${running.pid}`);
1751
+ const sender = await startSender({
1752
+ config: state.config,
1753
+ job,
1754
+ jobDir: dirPath,
1755
+ deps: { spawnFn: state.deps.spawnFn, runtime: job.microvm !== void 0 || job.container !== void 0 ? runtimeOf(state) : void 0, log }
1756
+ });
1757
+ if (sender !== void 0) {
1758
+ job = { ...active.job, sender };
1759
+ active.job = job;
1760
+ writeJobFile(jobFilePath, job);
1761
+ }
1681
1762
  running.onExit.then(({ code, signal, error }) => {
1682
1763
  if (active.ending) return;
1683
1764
  if (error !== void 0) {
@@ -1785,8 +1866,39 @@ async function endJob(state, active, outcome, ending) {
1785
1866
  state.jobs.delete(active.dirPath);
1786
1867
  }
1787
1868
  }
1869
+ async function renewLeaseIfDue(state, active, nowMs) {
1870
+ const log = logOf(state);
1871
+ const tag = `#${active.job.item}`;
1872
+ if (!needsRenew(active.lastRenewAtMs, nowMs)) return true;
1873
+ const renew = await retrySend(() => renewLease(state.config, active.job.item, state.deps.fetchImpl), {
1874
+ now: state.deps.now,
1875
+ sleep: state.deps.sleep
1876
+ });
1877
+ const result = renew.ok ? renew.value : "retry_later";
1878
+ if (result === "lease_lost") {
1879
+ if (active.ending) {
1880
+ log.error(`${tag} lease lost while the checks ran`);
1881
+ return false;
1882
+ }
1883
+ log.error(`${tag} lease lost \xB7 ending job as lease_lost`);
1884
+ await endJob(state, active, "lease_lost");
1885
+ return false;
1886
+ }
1887
+ if (result === "renewed") {
1888
+ active.job.lastRenewAt = new Date(nowMs).toISOString();
1889
+ writeJobFile(join(active.dirPath, "job.json"), active.job);
1890
+ log.line(`${tag} lease renewed \xB7 expires ${formatClock(new Date(nowMs + LEASE_SECONDS * 1e3), new Date(nowMs))}`);
1891
+ } else {
1892
+ log.error(`${tag} renew did not land \xB7 retrying in ${RENEW_INTERVAL_SECONDS / 60}m`);
1893
+ }
1894
+ active.lastRenewAtMs = nowMs;
1895
+ return true;
1896
+ }
1788
1897
  async function watchTick(state, active, nowMs, logMtimeMs) {
1789
- if (active.ending) return;
1898
+ if (active.ending) {
1899
+ if (active.job.stage === "checks") await renewLeaseIfDue(state, active, nowMs);
1900
+ return;
1901
+ }
1790
1902
  const log = logOf(state);
1791
1903
  const tag = `#${active.job.item}`;
1792
1904
  const pid = active.job.pid;
@@ -1804,26 +1916,7 @@ async function watchTick(state, active, nowMs, logMtimeMs) {
1804
1916
  }
1805
1917
  return;
1806
1918
  }
1807
- if (needsRenew(active.lastRenewAtMs, nowMs)) {
1808
- const renew = await retrySend(() => renewLease(state.config, active.job.item, state.deps.fetchImpl), {
1809
- now: state.deps.now,
1810
- sleep: state.deps.sleep
1811
- });
1812
- const result = renew.ok ? renew.value : "retry_later";
1813
- if (result === "lease_lost") {
1814
- log.error(`${tag} lease lost \xB7 ending job as lease_lost`);
1815
- await endJob(state, active, "lease_lost");
1816
- return;
1817
- }
1818
- if (result === "renewed") {
1819
- active.job.lastRenewAt = new Date(nowMs).toISOString();
1820
- writeJobFile(join(active.dirPath, "job.json"), active.job);
1821
- log.line(`${tag} lease renewed \xB7 expires ${formatClock(new Date(nowMs + LEASE_SECONDS * 1e3), new Date(nowMs))}`);
1822
- } else {
1823
- log.error(`${tag} renew did not land \xB7 retrying in ${RENEW_INTERVAL_SECONDS / 60}m`);
1824
- }
1825
- active.lastRenewAtMs = nowMs;
1826
- }
1919
+ if (!await renewLeaseIfDue(state, active, nowMs)) return;
1827
1920
  const tokens = active.job.container === void 0 && active.job.microvm === void 0 ? void 0 : runtimeOf(state).credentials.get(active.dirPath);
1828
1921
  if (tokens !== void 0) {
1829
1922
  await tokens.renewReadIfDue();
@@ -1899,7 +1992,7 @@ async function start(configPath, deps = { fetchImpl: fetch }) {
1899
1992
  const outcome = await onStartUnderMarker({
1900
1993
  configPath,
1901
1994
  own: ownVersionOf(deps),
1902
- check: update.check ? () => update.check(configPath) : async () => (await import('./check-4Q33B5ZE.js')).runRunnerCheck(configPath, { fetchImpl: deps.fetchImpl }),
1995
+ check: update.check ? () => update.check(configPath) : async () => (await import('./check-ASZL5BEO.js')).runRunnerCheck(configPath, { fetchImpl: deps.fetchImpl }),
1903
1996
  now: () => (deps.now ?? (() => /* @__PURE__ */ new Date()))().getTime(),
1904
1997
  log
1905
1998
  });
@@ -1918,6 +2011,11 @@ async function start(configPath, deps = { fetchImpl: fetch }) {
1918
2011
  mkdirSync(jobsDir(configPath), { recursive: true, mode: RUNNER_DIR_MODE });
1919
2012
  const self = { pid: process.pid, start: startTimeOf(process.pid) ?? "" };
1920
2013
  claimPidFile(pidFilePath(configPath), self);
2014
+ const { delegated, refusal } = setUpRunnerCgroup();
2015
+ if (refusal !== void 0) log.error(`the kernel refused a cgroup step for the runner's service: ${refusal}`);
2016
+ if (isBuiltinVm(config.hooks.provision) && !delegated) {
2017
+ log.line("microVM builds run with no memory limit of their own: the runner's service is not delegated its cgroup (Delegate=yes in the unit, cgroup v2)");
2018
+ }
1921
2019
  let configMtimeMs;
1922
2020
  try {
1923
2021
  configMtimeMs = statSync(configPath).mtimeMs;
@@ -4,6 +4,6 @@ import { createRequire } from 'node:module';
4
4
  createRequire(import.meta.url);
5
5
 
6
6
  // src/runner/protocol.ts
7
- var RUNNER_PROTOCOL = 13;
7
+ var RUNNER_PROTOCOL = 14;
8
8
 
9
9
  export { RUNNER_PROTOCOL };