@outerlayer/cli 0.24.0 → 0.25.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/README.md +9 -0
  3. package/dist/{agent-setup-QQYRBPK2.js → agent-setup-PDIIGMKE.js} +7 -7
  4. package/dist/{bridge-4ZARDVGS.js → bridge-5KZ36YDG.js} +3 -3
  5. package/dist/build-YYVAJ75G.js +12 -0
  6. package/dist/build-info.json +1 -1
  7. package/dist/{builder-MECDEKXX.js → builder-MNUJS4RG.js} +7 -7
  8. package/dist/{check-YYKNYFNI.js → check-C6HEFW6R.js} +39 -38
  9. package/dist/{chunk-BUBCPP3F.js → chunk-23SPSYXZ.js} +3 -3
  10. package/dist/{chunk-5ECUMU53.js → chunk-24LQ34XL.js} +6 -6
  11. package/dist/{chunk-BN3CSI7S.js → chunk-2K3TCQHW.js} +1 -1
  12. package/dist/{chunk-A62QFN2M.js → chunk-2N72GPS2.js} +3 -3
  13. package/dist/{chunk-TQ6Y7YV6.js → chunk-3FNPTQRH.js} +5 -5
  14. package/dist/{chunk-F2W2IYNP.js → chunk-3WZ5ECSM.js} +3 -3
  15. package/dist/{chunk-WH2QV3UL.js → chunk-4S4VBDSO.js} +1 -1
  16. package/dist/{chunk-IDSHLKQA.js → chunk-5222PA6S.js} +1 -1
  17. package/dist/{chunk-6GSA2SKT.js → chunk-A6ORKBVJ.js} +213 -59
  18. package/dist/{chunk-3W333NFO.js → chunk-APBTVIM4.js} +15 -4
  19. package/dist/{chunk-HHXBOTRO.js → chunk-ATGHVEU4.js} +1 -1
  20. package/dist/{chunk-D7K446H6.js → chunk-C36EOUUU.js} +5 -5
  21. package/dist/{chunk-RVYOOYOA.js → chunk-CQRIZNE6.js} +19 -10
  22. package/dist/{chunk-FWEWFW3F.js → chunk-DEBOL6ON.js} +1 -1
  23. package/dist/{chunk-GU4PKXN7.js → chunk-ENFBARQP.js} +185 -38
  24. package/dist/{chunk-H6IUKXXI.js → chunk-F57CLUH7.js} +207 -35
  25. package/dist/{chunk-E4OUL7ZP.js → chunk-FXUVXSY2.js} +2 -2
  26. package/dist/{chunk-F7Y2USIM.js → chunk-H6ZFLIX3.js} +3 -3
  27. package/dist/{chunk-LY2L3O2O.js → chunk-HPOEVC2V.js} +3 -2
  28. package/dist/{chunk-ZFLOFAUG.js → chunk-I25F6YVP.js} +8 -8
  29. package/dist/{chunk-DG2ULWM6.js → chunk-IGRRD7PX.js} +11 -7
  30. package/dist/{chunk-GISNHSYL.js → chunk-JGD5TWQ2.js} +4 -2
  31. package/dist/{chunk-D7X5ITVT.js → chunk-JWRX5ZTT.js} +6 -2
  32. package/dist/{chunk-755WXDBI.js → chunk-K5WVK7IC.js} +1 -1
  33. package/dist/{chunk-FKXGQOF5.js → chunk-M4B6QKNZ.js} +1 -1
  34. package/dist/{chunk-5XCAPBE2.js → chunk-N3VSI5ZI.js} +6 -6
  35. package/dist/{chunk-NTQTDCYR.js → chunk-O2WYUIK7.js} +1 -1
  36. package/dist/{chunk-BOQY756V.js → chunk-OEU4JSDE.js} +8 -7
  37. package/dist/{chunk-TTLVQZSH.js → chunk-Q435LWVN.js} +5 -5
  38. package/dist/{chunk-D4AMAEZ7.js → chunk-QE3UQAHK.js} +322 -17
  39. package/dist/{chunk-VV7ZD5LR.js → chunk-QMQC6JCO.js} +1 -1
  40. package/dist/{chunk-FQPN5S4Q.js → chunk-QXNJWSTM.js} +4 -4
  41. package/dist/{chunk-VBEXKEBV.js → chunk-S6AWZKJN.js} +19 -6
  42. package/dist/{chunk-V5Q3S4NM.js → chunk-SNQR2EYI.js} +2 -2
  43. package/dist/{chunk-CZUH2SLQ.js → chunk-UNF7MWEL.js} +1 -1
  44. package/dist/{chunk-W7O4LA4E.js → chunk-VAMUWK55.js} +3 -3
  45. package/dist/{chunk-PDIM5T65.js → chunk-XGJUNNXL.js} +1 -1
  46. package/dist/{chunk-NGZID7UI.js → chunk-XRYUJTAT.js} +8 -1
  47. package/dist/{chunk-LBCZXXOX.js → chunk-ZGMQQWCV.js} +2 -2
  48. package/dist/{chunk-KF37SUDM.js → chunk-ZI2ZVWKT.js} +1 -1
  49. package/dist/{chunk-PVRBE7S4.js → chunk-ZODVSFI4.js} +1 -1
  50. package/dist/chunk-ZVB57WI6.js +19 -0
  51. package/dist/{claude-launcher-JCPSHDZQ.js → claude-launcher-WGOCLTG6.js} +1 -1
  52. package/dist/{cli-ZTBH3FBG.js → cli-YMTNHR4W.js} +107 -102
  53. package/dist/command-DKYZC7PH.js +533 -0
  54. package/dist/{config-7Z5DHSGY.js → config-JJ3EO7SB.js} +3 -3
  55. package/dist/{connect-cmd-NY5LIKVC.js → connect-cmd-F7T4LNKX.js} +7 -7
  56. package/dist/{container-start-OHBUZEJJ.js → container-start-AX7QXJMR.js} +45 -36
  57. package/dist/{control-plane-launch-GKGI6HJA.js → control-plane-launch-ZFT6SI4N.js} +6 -6
  58. package/dist/{dist-2APO44JG.js → dist-QPHZE47F.js} +1 -1
  59. package/dist/{docker-RKGBJESN.js → docker-5EC5TE3Y.js} +7 -7
  60. package/dist/{doctor-DOAFZV4O.js → doctor-S6YCZGUU.js} +79 -33
  61. package/dist/{emit-artifact-cmd-VZHIDPXQ.js → emit-artifact-cmd-DJO475VZ.js} +11 -11
  62. package/dist/{emit-criteria-cmd-SYLBCWHW.js → emit-criteria-cmd-EULQ2MLI.js} +8 -8
  63. package/dist/{emit-finding-cmd-52M43NPC.js → emit-finding-cmd-QIIKMITO.js} +8 -8
  64. package/dist/{emit-result-cmd-BTMTGDAV.js → emit-result-cmd-NMCLTA47.js} +11 -11
  65. package/dist/{emit-step-cmd-6XTJITZT.js → emit-step-cmd-72OX5P6M.js} +9 -9
  66. package/dist/{exec-client-CKPK62K2.js → exec-client-KH33DICR.js} +4 -4
  67. package/dist/{governing-source-GWN7GVEN.js → governing-source-JNQ6PX2A.js} +11 -11
  68. package/dist/{guest-init-ZBL5CN7Y.js → guest-init-TOSKPGPF.js} +5 -5
  69. package/dist/{hook-fast-3JL4PXLG.js → hook-fast-NEELST6G.js} +1 -1
  70. package/dist/host-key-ICJEQBWH.js +9 -0
  71. package/dist/{image-tag-AO4RBAKW.js → image-tag-6KJOGBQK.js} +4 -4
  72. package/dist/index.js +4 -4
  73. package/dist/{init-TSQ3EOQW.js → init-624F4JIP.js} +25 -23
  74. package/dist/{init-EQTR7AEF.js → init-G7XC5WWL.js} +25 -23
  75. package/dist/{init-cmd-AF2MH36A.js → init-cmd-UUYA57VO.js} +11 -11
  76. package/dist/{init-update-SHISHMXV.js → init-update-MMV33PLM.js} +9 -9
  77. package/dist/{install-ORQC26ZE.js → install-MPEJJ7X6.js} +5 -5
  78. package/dist/{install-cmd-4LA3UJZ4.js → install-cmd-SE6NCTJK.js} +6 -6
  79. package/dist/job-state-format-WJQGQB6F.js +6 -0
  80. package/dist/{logs-5EPHIPZB.js → logs-T4ERSOQ4.js} +3 -3
  81. package/dist/loop-K5B2A3VW.js +78 -0
  82. package/dist/{machine-6UNLIPLE.js → machine-J4IFVIKU.js} +21 -19
  83. package/dist/{mcp-serve-cmd-KMWEKM7C.js → mcp-serve-cmd-7M4VLNI4.js} +5 -5
  84. package/dist/{paths-XY5ADO32.js → paths-2KGM2LZV.js} +3 -3
  85. package/dist/pending-BGLMTB5Q.js +9 -0
  86. package/dist/{pidfile-OJ7RP4Q2.js → pidfile-VYBH3IMN.js} +4 -4
  87. package/dist/{record-BEJB4J7N.js → record-GNOHDJ2O.js} +4 -4
  88. package/dist/{relay-PSZ4LGMW.js → relay-GFPPD6GF.js} +1 -1
  89. package/dist/relay-protocol-HELO5SNH.js +6 -0
  90. package/dist/{runner-env-ZJWJOJKQ.js → runner-env-GUFV544G.js} +6 -6
  91. package/dist/{runner-identity-2OOUJSFJ.js → runner-identity-HZ6CBM2R.js} +7 -7
  92. package/dist/{runner-key-WDFBSJRX.js → runner-key-FFVZUEHE.js} +25 -23
  93. package/dist/{session-sender-X3BBQZMF.js → session-sender-3SUWNNVP.js} +12 -12
  94. package/dist/{spec-cmd-QRF5YXKK.js → spec-cmd-VZDE6FFH.js} +9 -9
  95. package/dist/{status-ZCAAF6MD.js → status-GXTD44DX.js} +35 -29
  96. package/dist/{sync-cmd-UQK3RYAC.js → sync-cmd-VFB3NEJ6.js} +11 -11
  97. package/dist/{watch-MOQ3GQTE.js → watch-LWMTDD3L.js} +11 -11
  98. package/dist/{work-claim-cmd-RT343CLA.js → work-claim-cmd-FOBTDQVR.js} +12 -12
  99. package/dist/{work-cmd-APXSER2D.js → work-cmd-6HARIBMW.js} +9 -9
  100. package/dist/{work-comment-cmd-AT5NUYUU.js → work-comment-cmd-BITPALHR.js} +9 -9
  101. package/dist/{work-logs-cmd-Z5DMNEJP.js → work-logs-cmd-VGB3O2LZ.js} +9 -9
  102. package/dist/{work-open-pr-cmd-ZTS6MJHL.js → work-open-pr-cmd-SVZCNOIM.js} +10 -10
  103. package/dist/{work-pr-cmd-IWZCC3WH.js → work-pr-cmd-SJT46GN4.js} +9 -9
  104. package/dist/{work-stop-cmd-DCAWFDZC.js → work-stop-cmd-WXKVVA4S.js} +9 -9
  105. package/package.json +2 -2
  106. package/skill-pack/maintained/outerlayer-build/SKILL.md +1 -1
  107. package/skill-pack/maintained/outerlayer-build/references/agent-briefs.md +3 -2
  108. package/skill-pack/maintained/outerlayer-build/references/review-briefs.md +3 -1
  109. package/dist/build-OVZIT2G2.js +0 -12
  110. package/dist/command-DO4TERRT.js +0 -208
  111. package/dist/host-key-W2DLZGZ7.js +0 -9
  112. package/dist/loop-4WUCS5JP.js +0 -77
  113. package/dist/pending-35J3ZPLR.js +0 -9
  114. package/dist/relay-protocol-K7KP2SPI.js +0 -6
@@ -1,31 +1,32 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- import { releaseLease, isUnfinished, setStage, jobEnv, runHook, describeHookEnd, provisionMicroVm, deliverCommitHooks, provisionProcess, provisionEnvironment, readProvisionOut, resolveHostBuild, workdirProblem, listenOnBroker, hostEnvironment, hostCommandEnvironment, execArgv, microVmCommandLine, runCommand, commandLogOffset, execCommandLine, startSender, startHostServices, finishJob, isContainerTracked, probeCommand, appendCommandLog, judgeProbe, EXIT_CODE_FILE, isGroupAlive, outcomeFromExitCode, isTimedOut, readCpuUsec, isIdle, stopOrphanedMicroVms, unfinishedJobs, listAdoptableJobs, retryFailedReleases, recoverLostJobs, hookLimitsFor, AttemptLogBook, needsRenew, renewLease, RENEW_INTERVAL_SECONDS, listTrackedJobs, restartSender, ALREADY_RELEASED } from './chunk-6GSA2SKT.js';
4
- import { writePendingUpdate } from './chunk-NGZID7UI.js';
5
- import { readHousekeepingRecord, writeHousekeepingRecord } from './chunk-NTQTDCYR.js';
3
+ import { releaseLease, isUnfinished, setStage, jobEnv, runHook, describeHookEnd, provisionMicroVm, deliverCommitHooks, provisionProcess, provisionEnvironment, readProvisionOut, resolveHostBuild, workdirProblem, listenOnBroker, hostEnvironment, hostCommandEnvironment, execArgv, microVmCommandLine, runCommand, commandLogOffset, execCommandLine, startSender, startHostServices, finishJob, isContainerTracked, probeCommand, appendCommandLog, judgeProbe, EXIT_CODE_FILE, isGroupAlive, outcomeFromExitCode, isTimedOut, readCpuUsec, isIdle, stopOrphanedMicroVms, unfinishedJobs, listUnreadableJobs, listAdoptableJobs, retryFailedReleases, recoverLostJobs, hookLimitsFor, AttemptLogBook, needsRenew, renewLease, RENEW_INTERVAL_SECONDS, listTrackedJobs, restartSender, ALREADY_RELEASED, inTeardown } from './chunk-A6ORKBVJ.js';
4
+ import { writePendingUpdate } from './chunk-XRYUJTAT.js';
5
+ import { JOB_STATE_FORMAT, readsJobState, jobStateFormatOf } from './chunk-ZVB57WI6.js';
6
+ import { readHousekeepingRecord, writeHousekeepingRecord } from './chunk-O2WYUIK7.js';
6
7
  import { decideInstalledSwitch } from './chunk-3JVYMD4G.js';
7
8
  import { fetchVersionMeta, npmVersion, npmSupportsProvenance, fetchTarballDigest, fetchProvenance, installVersion, auditSignatures } from './chunk-YETTWTAY.js';
8
9
  import { RUNNER_PROTOCOL } from './chunk-TPKYTORP.js';
9
- import { currentSkillCommand, formerSkillProblem, placeholderQueues, placeholderProblem } from './chunk-5ECUMU53.js';
10
+ import { currentSkillCommand, formerSkillProblem, placeholderQueues, placeholderProblem } from './chunk-24LQ34XL.js';
11
+ import { writeMarker, readMarker, currentVersion, removeMarker, entryIn, versionDir, startedThroughCurrent, switchCurrent, isInLayout, cleanVersions, stagingDir } from './chunk-QMQC6JCO.js';
10
12
  import { freeBytes, formatBytes } from './chunk-YYYRBYWG.js';
11
- import { writeMarker, readMarker, currentVersion, removeMarker, entryIn, versionDir, startedThroughCurrent, switchCurrent, cleanVersions, isInLayout, stagingDir } from './chunk-VV7ZD5LR.js';
12
13
  import { RepoFilter } from './chunk-TBT347UY.js';
13
- import { LEASE_SECONDS, provisionContainer, leaseExpiresAtMs, deliverContainerHooks, lifecycleSteps, lifecycleFailureReason, prepareBranch, containerCommandExec, measureDisk, setUpRunnerCgroup, createContainerRuntime, parseCpuList, ensureTunnel, keepGhConfigCurrent, filesAreCopied } from './chunk-GU4PKXN7.js';
14
- import { checkFit } from './chunk-DG2ULWM6.js';
15
- import { tagToPull, pullInstruction, imageTag, writeUpdateStatus } from './chunk-IDSHLKQA.js';
14
+ import { LEASE_SECONDS, provisionContainer, leaseExpiresAtMs, deliverContainerHooks, lifecycleSteps, lifecycleFailureReason, prepareBranch, containerCommandExec, measureDisk, setUpRunnerCgroup, createContainerRuntime, parseCpuList, ensureTunnel, keepGhConfigCurrent, filesAreCopied } from './chunk-ENFBARQP.js';
15
+ import { tagToPull, pullInstruction, imageTag, writeUpdateStatus } from './chunk-5222PA6S.js';
16
16
  import { isVersion, compareVersions, satisfiesRange } from './chunk-TQMI335S.js';
17
- import { claimPidFile, readPidFile, releasePidFile } from './chunk-FWEWFW3F.js';
17
+ import { checkFit } from './chunk-IGRRD7PX.js';
18
+ import { claimPidFile, readPidFile, releasePidFile } from './chunk-DEBOL6ON.js';
18
19
  import { startTimeOf, isSameProcess } from './chunk-JBJTC3UU.js';
19
- import { writeJobFile, ensureRunnerIdentity, readJobFile, builderName, runnerLabelValue, ensureFailureCount, builderSpec, ensureBuilder, spawnRun, BUILD_LABEL, RUNNER_LABEL, readJobFileChecked } from './chunk-5XCAPBE2.js';
20
- import { describeCallFailure, retrySend, shortClaim, relativeToRunner, formatDuration, BuildTokens, classifyRefusal, refusalCode, createRunnerLog, formatClock } from './chunk-TQ6Y7YV6.js';
21
- import { isSignatureRefusal, WorkCommandError, getJson, postJson } from './chunk-RVYOOYOA.js';
22
- import { isBuiltinContainer, isBuiltinVm, isBuiltinProcess, loadRunnerConfig, checkHooks, builtinKind, DEFAULT_UPDATE_MAX_WAIT_HOURS } from './chunk-D7X5ITVT.js';
20
+ import { writeJobFile, ensureRunnerIdentity, readJobFile, builderName, runnerLabelValue, ensureFailureCount, builderSpec, ensureBuilder, spawnRun, BUILD_LABEL, RUNNER_LABEL, readJobFileChecked } from './chunk-N3VSI5ZI.js';
21
+ import { describeCallFailure, retrySend, shortClaim, relativeToRunner, formatDuration, BuildTokens, classifyRefusal, refusalCode, createRunnerLog, formatClock } from './chunk-3FNPTQRH.js';
22
+ import { isSignatureRefusal, WorkCommandError, getJson, postJson } from './chunk-CQRIZNE6.js';
23
+ import { isBuiltinContainer, isBuiltinVm, isBuiltinProcess, loadRunnerConfig, checkHooks, builtinKind, DEFAULT_UPDATE_MAX_WAIT_HOURS } from './chunk-JWRX5ZTT.js';
23
24
  import { exceedsDisk } from './chunk-7RQ73DLO.js';
24
25
  import { resolveVersion } from './chunk-RA3O54FC.js';
25
- import { loadHostKey, ensureHostKey, signerFor } from './chunk-PVRBE7S4.js';
26
- import { RUNNER_DIR_MODE, runnerDir, buildDir, attemptStamp, jobDir, RUNNER_FILE_MODE, jobsDir, pidFilePath, spoolDir, rootfsDir } from './chunk-KF37SUDM.js';
27
- import { parseClaimIdentity, parseClaimedRepositories, parseUnreachable, repositoriesOf, primaryOf, sharedUserEnvironment, JOB_MOUNT, claudeCredential } from './chunk-BN3CSI7S.js';
28
- import { COMMAND_LOST_REASON, CONTAINER_STOPPED_REASON } from './chunk-GISNHSYL.js';
26
+ import { loadHostKey, ensureHostKey, signerFor } from './chunk-ZODVSFI4.js';
27
+ import { RUNNER_DIR_MODE, runnerDir, buildDir, attemptStamp, jobDir, RUNNER_FILE_MODE, jobsDir, pidFilePath, spoolDir, rootfsDir } from './chunk-ZI2ZVWKT.js';
28
+ import { parseClaimIdentity, parseClaimedRepositories, parseUnreachable, repositoriesOf, primaryOf, sharedUserEnvironment, JOB_MOUNT, claudeCredential } from './chunk-2K3TCQHW.js';
29
+ import { COMMAND_LOST_REASON, CONTAINER_STOPPED_REASON } from './chunk-JGD5TWQ2.js';
29
30
  import { randomBytes } from 'crypto';
30
31
  import { mkdirSync, existsSync, chmodSync, writeFileSync, renameSync, statSync, readdirSync, readFileSync, rmSync, closeSync, openSync, constants, lstatSync } from 'fs';
31
32
  import { join, dirname } from 'path';
@@ -238,6 +239,61 @@ function writeLastUpdate(configPath, update) {
238
239
  `, { mode: RUNNER_FILE_MODE });
239
240
  renameSync(temporary, path);
240
241
  }
242
+
243
+ // src/runner/update/ask-formats.ts
244
+ async function askJobStateFormats(runNode, entry, cwd) {
245
+ let result;
246
+ try {
247
+ result = await runNode([entry, "runner", "job-state-formats"], cwd);
248
+ } catch {
249
+ return void 0;
250
+ }
251
+ if (result.status !== 0) return void 0;
252
+ try {
253
+ const body = JSON.parse(result.stdout);
254
+ if (!Array.isArray(body.reads) || !body.reads.every((n) => Number.isInteger(n))) return void 0;
255
+ return body.reads;
256
+ } catch {
257
+ return void 0;
258
+ }
259
+ }
260
+
261
+ // src/runner/update/survives-restart.ts
262
+ function stageReason(job) {
263
+ if (job.stage === "command") return void 0;
264
+ if (!inTeardown(job)) return "is still in setup";
265
+ switch (job.stage) {
266
+ case "judge":
267
+ return "is running the criteria judge";
268
+ case "checks":
269
+ return "is running its checks";
270
+ default:
271
+ return `is finishing (${job.stage})`;
272
+ }
273
+ }
274
+ function ownReason({ job, ending }) {
275
+ if (job.microvm !== void 0) return "runs in a microVM, which does not carry on through a restart";
276
+ if (!isContainerTracked(job)) return "is not a container build tracked inside its container";
277
+ if (ending) return "is being ended";
278
+ return stageReason(job);
279
+ }
280
+ function buildBlockers(builds) {
281
+ return builds.flatMap((build) => {
282
+ const reason = ownReason(build);
283
+ return reason === void 0 ? [] : [{ item: build.job.item, reason }];
284
+ });
285
+ }
286
+ function restartBlockers(builds, reads) {
287
+ return builds.flatMap((build) => {
288
+ const reason = ownReason(build);
289
+ if (reason !== void 0) return [{ item: build.job.item, reason }];
290
+ if (reads === void 0) return [{ item: build.job.item, reason: "has job state the new version may not read: it did not say which formats it reads" }];
291
+ if (!readsJobState(build.job, reads)) {
292
+ return [{ item: build.job.item, reason: `has job-state format ${jobStateFormatOf(build.job)}, which the new version does not read` }];
293
+ }
294
+ return [];
295
+ });
296
+ }
241
297
  var RELEASE_REPOSITORY = "https://github.com/outerlayerai/outerlayer";
242
298
  var RELEASE_WORKFLOW_PATH = ".github/workflows/outerlayer-release.yml";
243
299
  var UpdateStepError = class extends Error {
@@ -411,8 +467,8 @@ async function preflightUpdate(args) {
411
467
  const failure2 = await earlyFailure(args);
412
468
  return failure2 === void 0 ? void 0 : finish(args, failure2);
413
469
  }
414
- async function performUpdate(args) {
415
- const { configPath, own, version, deps } = args;
470
+ async function prepareUpdate(args) {
471
+ const { configPath, version, deps } = args;
416
472
  const early = await earlyFailure(args);
417
473
  if (early !== void 0) return finish(args, early);
418
474
  let verified;
@@ -424,24 +480,31 @@ async function performUpdate(args) {
424
480
  if (!verified.ok) return finish(args, { outcome: verified.outcome, reason: verified.reason });
425
481
  const staged = await deps.runNode([entryIn(verified.dir), "runner", "check", "--config", configPath], verified.dir);
426
482
  if (staged.status !== 0) {
427
- try {
428
- rmSync(verified.dir, { recursive: true, force: true });
429
- } catch {
430
- }
483
+ removeStaging(verified.dir);
431
484
  return finish(args, {
432
485
  outcome: "retrying",
433
486
  reason: `${version} failed \`runner check\` on this host (exit ${staged.status})`,
434
487
  detail: `${version} failed \`runner check\` on this host: ${(staged.stderr.trim() || staged.stdout.trim()).slice(0, 500)}`
435
488
  });
436
489
  }
490
+ return { kind: "prepared", dir: verified.dir };
491
+ }
492
+ function removeStaging(dir) {
493
+ try {
494
+ rmSync(dir, { recursive: true });
495
+ } catch {
496
+ }
497
+ }
498
+ function installPrepared(args, stagedDir) {
499
+ const { configPath, own, version } = args;
437
500
  try {
438
501
  if (version !== own) rmSync(versionDir(configPath, version), { recursive: true, force: true });
439
- renameSync(verified.dir, versionDir(configPath, version));
502
+ renameSync(stagedDir, versionDir(configPath, version));
440
503
  writeMarker(configPath, { from: own, to: version, starts: 0 });
441
504
  switchCurrent(configPath, version);
442
505
  } catch (err) {
443
506
  try {
444
- rmSync(verified.dir, { recursive: true, force: true });
507
+ rmSync(stagedDir, { recursive: true, force: true });
445
508
  if (currentVersion(configPath) !== version) removeMarker(configPath);
446
509
  } catch {
447
510
  }
@@ -455,6 +518,10 @@ async function performUpdate(args) {
455
518
  args.log.line(`updating ${own} -> ${version} \xB7 restarting`);
456
519
  return { kind: "switched" };
457
520
  }
521
+ async function performUpdate(args) {
522
+ const prepared = await prepareUpdate(args);
523
+ return prepared.kind === "prepared" ? installPrepared(args, prepared.dir) : prepared;
524
+ }
458
525
  function rollBack(configPath, marker, reason, detail, now, log) {
459
526
  try {
460
527
  writeLastUpdate(configPath, { from: marker.from, to: marker.to, outcome: "rolled_back", reason, at: isoAt(now) });
@@ -1242,12 +1309,20 @@ function updateArgsOf(state, update, version) {
1242
1309
  function dropPendingUpdate(state) {
1243
1310
  state.pendingUpdate = void 0;
1244
1311
  state.preflightedUpdate = void 0;
1312
+ discardPrepared(state);
1245
1313
  clearUpdateWait(state);
1246
1314
  }
1315
+ function discardPrepared(state) {
1316
+ if (state.preparedUpdate === void 0) return;
1317
+ removeStaging(state.preparedUpdate.dir);
1318
+ state.preparedUpdate = void 0;
1319
+ }
1247
1320
  function clearUpdateWait(state) {
1248
1321
  state.updateWait = void 0;
1249
1322
  state.updateLogged = void 0;
1250
1323
  state.upgradeRefused = false;
1324
+ if (state.installWait !== void 0) return;
1325
+ state.waitsLogged = void 0;
1251
1326
  recordPendingUpdate(state, void 0);
1252
1327
  }
1253
1328
  function recordPendingUpdate(state, record) {
@@ -1271,7 +1346,7 @@ function runningItems(state) {
1271
1346
  const numbers = [...state.jobs.values()].map((entry) => Number(entry.job.item)).filter((n) => Number.isFinite(n));
1272
1347
  return numbers.sort((a, b) => a - b);
1273
1348
  }
1274
- function holdForUpdate(state, version, mayHold = true) {
1349
+ function holdForUpdate(state, version, mayHold = true, waits = []) {
1275
1350
  const log = logOf(state);
1276
1351
  const nowMs = nowOf(state).getTime();
1277
1352
  const wait = adoptUpdateWait(state, version, nowMs);
@@ -1286,6 +1361,11 @@ function holdForUpdate(state, version, mayHold = true) {
1286
1361
  const maxWaitMs = (state.config.update?.maxWaitHours ?? DEFAULT_UPDATE_MAX_WAIT_HOURS) * 60 * 6e4;
1287
1362
  const holding = mayHold && (state.upgradeRefused === true || nowMs - wait.sinceMs >= maxWaitMs);
1288
1363
  const record = { version, since: new Date(wait.sinceMs).toISOString() };
1364
+ if (waits.length > 0) {
1365
+ record.via = "self-update";
1366
+ record.waits = recordedWaits(waits);
1367
+ }
1368
+ noteWaits(state, version, waits);
1289
1369
  if (holding) {
1290
1370
  wait.holdSinceMs ??= nowMs;
1291
1371
  const builds = runningItems(state);
@@ -1302,6 +1382,16 @@ function holdForUpdate(state, version, mayHold = true) {
1302
1382
  recordPendingUpdate(state, record);
1303
1383
  return holding;
1304
1384
  }
1385
+ function recordedWaits(waits) {
1386
+ return waits.flatMap((wait) => Number.isFinite(Number(wait.item)) ? [{ item: Number(wait.item), reason: wait.reason }] : []);
1387
+ }
1388
+ function noteWaits(state, version, waits) {
1389
+ const phrase = waits.map((wait) => `#${wait.item} ${wait.reason}`).join("; ");
1390
+ const key = waits.length === 0 ? void 0 : `${version}:${phrase}`;
1391
+ if (state.waitsLogged === key) return;
1392
+ state.waitsLogged = key;
1393
+ if (key !== void 0) logOf(state).line(`update to ${version} waits: ${phrase}`);
1394
+ }
1305
1395
  function buildsPhrase(builds) {
1306
1396
  return builds.length === 0 ? "the running builds" : builds.map((n) => `#${n}`).join(", ");
1307
1397
  }
@@ -1325,7 +1415,7 @@ function installedSwitchOf(state, update) {
1325
1415
  installed: current !== void 0 && existsSync(entryIn(versionDir(configPath, current)))
1326
1416
  });
1327
1417
  }
1328
- function restartOntoInstalled(state) {
1418
+ async function restartOntoInstalled(state) {
1329
1419
  const update = state.deps.update;
1330
1420
  if (update === void 0) return false;
1331
1421
  const configPath = state.config.configPath;
@@ -1333,6 +1423,8 @@ function restartOntoInstalled(state) {
1333
1423
  const decision = installedSwitchOf(state, update);
1334
1424
  if (decision.kind === "none") {
1335
1425
  state.installedSwitch = void 0;
1426
+ state.installedFormats = void 0;
1427
+ state.installWait = void 0;
1336
1428
  return false;
1337
1429
  }
1338
1430
  const said = `${decision.kind}:${decision.to}`;
@@ -1348,8 +1440,14 @@ function restartOntoInstalled(state) {
1348
1440
  return false;
1349
1441
  }
1350
1442
  if (state.jobs.size > 0) {
1351
- if (first) log.line(`${decision.to} is installed \xB7 taking no new work, restarting onto it once ${state.jobs.size} running build(s) finish`);
1352
- return true;
1443
+ const waits = await installBlockers(state, update, decision.to);
1444
+ if (waits.length > 0) {
1445
+ if (first) log.line(`${decision.to} is installed \xB7 taking no new work, restarting onto it once ${state.jobs.size} running build(s) finish`);
1446
+ const wait = state.installWait = state.installWait?.version === decision.to ? state.installWait : { version: decision.to, sinceMs: nowOf(state).getTime() };
1447
+ noteWaits(state, decision.to, waits);
1448
+ recordPendingUpdate(state, { version: decision.to, since: new Date(wait.sinceMs).toISOString(), via: "install", waits: recordedWaits(waits) });
1449
+ return true;
1450
+ }
1353
1451
  }
1354
1452
  try {
1355
1453
  writeMarker(configPath, { from: own, to: decision.to, starts: 0 });
@@ -1360,10 +1458,66 @@ function restartOntoInstalled(state) {
1360
1458
  (state.exitForUpdate ?? (() => update.exit(RESTART_EXIT_STATUS)))();
1361
1459
  return true;
1362
1460
  }
1461
+ function runningBuilds(state) {
1462
+ return [...state.jobs.values()].map((active) => ({ job: active.job, ending: active.ending }));
1463
+ }
1464
+ async function installBlockers(state, update, to) {
1465
+ const stage = buildBlockers(runningBuilds(state));
1466
+ if (stage.length > 0) return stage;
1467
+ if (state.installedFormats?.version !== to) {
1468
+ const dir = versionDir(state.config.configPath, to);
1469
+ state.installedFormats = { version: to, reads: await askJobStateFormats(update.runNode, entryIn(dir), dir) };
1470
+ }
1471
+ return restartBlockers(runningBuilds(state), state.installedFormats.reads);
1472
+ }
1473
+ function formatBlocked(builds, reads) {
1474
+ const byStage = new Set(buildBlockers(builds).map((blocker) => blocker.item));
1475
+ return restartBlockers(builds, reads).some((blocker) => !byStage.has(blocker.item));
1476
+ }
1477
+ async function restartNow(state, update, version) {
1478
+ const early = buildBlockers(runningBuilds(state));
1479
+ if (early.length > 0) return { kind: "waiting", waits: early };
1480
+ if (state.preparedUpdate !== void 0 && state.preparedUpdate.version !== version) discardPrepared(state);
1481
+ const args = updateArgsOf(state, update, version);
1482
+ let reads;
1483
+ const declined = state.declinedFormats?.has(version) === true;
1484
+ if (state.preparedUpdate !== void 0) {
1485
+ reads = state.preparedUpdate.reads;
1486
+ } else if (declined && restartBlockers(runningBuilds(state), state.declinedFormats.get(version)).length > 0) {
1487
+ reads = state.declinedFormats.get(version);
1488
+ } else {
1489
+ const prepared = await prepareUpdate(args);
1490
+ if (prepared.kind !== "prepared") {
1491
+ dropPendingUpdate(state);
1492
+ reportUpdateResult(state, version, prepared);
1493
+ return { kind: "failed" };
1494
+ }
1495
+ reads = await askJobStateFormats(update.runNode, entryIn(prepared.dir), prepared.dir);
1496
+ if (formatBlocked(runningBuilds(state), reads)) {
1497
+ removeStaging(prepared.dir);
1498
+ (state.declinedFormats ??= /* @__PURE__ */ new Map()).set(version, reads);
1499
+ } else {
1500
+ state.preparedUpdate = { version, dir: prepared.dir, reads };
1501
+ }
1502
+ }
1503
+ const waits = restartBlockers(runningBuilds(state), reads);
1504
+ if (waits.length > 0) return { kind: "waiting", waits };
1505
+ const staged = state.preparedUpdate;
1506
+ state.preparedUpdate = void 0;
1507
+ dropPendingUpdate(state);
1508
+ const result = installPrepared(args, staged.dir);
1509
+ if (result.kind === "switched") {
1510
+ (state.exitForUpdate ?? (() => update.exit(RESTART_EXIT_STATUS)))();
1511
+ return { kind: "restarted" };
1512
+ }
1513
+ reportUpdateResult(state, version, result);
1514
+ return { kind: "failed" };
1515
+ }
1363
1516
  async function updateWhenIdle(state) {
1364
1517
  const update = state.deps.update;
1365
1518
  const version = state.pendingUpdate;
1366
1519
  if (update === void 0 || version === void 0) {
1520
+ discardPrepared(state);
1367
1521
  recordPendingUpdate(state, void 0);
1368
1522
  return false;
1369
1523
  }
@@ -1389,6 +1543,12 @@ async function updateWhenIdle(state) {
1389
1543
  }
1390
1544
  state.preflightedUpdate = version;
1391
1545
  }
1546
+ if (update.underService === true) {
1547
+ const outcome = await restartNow(state, update, version);
1548
+ if (outcome.kind === "restarted") return true;
1549
+ if (outcome.kind === "failed") return false;
1550
+ return holdForUpdate(state, version, true, outcome.waits);
1551
+ }
1392
1552
  return holdForUpdate(state, version);
1393
1553
  }
1394
1554
  dropPendingUpdate(state);
@@ -1523,7 +1683,7 @@ async function runTick(state) {
1523
1683
  state.holdingForUpdate = false;
1524
1684
  if (state.draining === true) return;
1525
1685
  reloadConfig(state);
1526
- if (restartOntoInstalled(state)) {
1686
+ if (await restartOntoInstalled(state)) {
1527
1687
  state.restarting = true;
1528
1688
  return;
1529
1689
  }
@@ -1586,7 +1746,7 @@ var CLAIM_REFUSAL_CODES = new Set(
1586
1746
  "pull_request_from_fork pull_request_on_default_branch pull_request_ambiguous pull_request_repository_not_connected repository_not_reachable branch_claimed_elsewhere".split(" ")
1587
1747
  );
1588
1748
  async function tryClaimAndStart(state, item, queue) {
1589
- const { postJson: postJson2 } = await import('./work-cmd-APXSER2D.js');
1749
+ const { postJson: postJson2 } = await import('./work-cmd-6HARIBMW.js');
1590
1750
  const log = logOf(state);
1591
1751
  const hostKey = await loadHostKey(state.config.configPath);
1592
1752
  const claim = await retrySend(
@@ -1646,7 +1806,7 @@ async function tryClaimAndStart(state, item, queue) {
1646
1806
  }
1647
1807
  if (wasExtended(data)) {
1648
1808
  log.line(
1649
- `#${item.number} skipped: the claim extended a live lease under host "${state.config.host}" \xB7 set a distinct runner.host per config file`
1809
+ `#${item.number} skipped: the claim extended a live lease under the runner name "${state.config.host}" \xB7 set a distinct runner.name per config file`
1650
1810
  );
1651
1811
  return { kind: "held" };
1652
1812
  }
@@ -1736,6 +1896,7 @@ async function startJob(state, item, queue, claimId, itemKey, branch, identity,
1736
1896
  const runnerRoot = runnerDir(state.config.configPath);
1737
1897
  const jobFilePath = join(dirPath, "job.json");
1738
1898
  let job = {
1899
+ stateFormat: state.deps.jobStateFormat ?? JOB_STATE_FORMAT,
1739
1900
  item: item.number,
1740
1901
  queue,
1741
1902
  claimId,
@@ -2555,6 +2716,13 @@ async function finishTrackedWhileAway(state, dirPath, job, where) {
2555
2716
  `recovered job ${finished.queue} #${finished.item} (claim ${shortClaim(finished.claimId)}) as ${outcome}: ${code === void 0 ? `its container ${where === "missing" ? "is gone" : "stopped"} and its volume ${read.readable ? "holds no exit code" : "could not be read"}` : `its command recorded exit ${code}`}`
2556
2717
  );
2557
2718
  }
2719
+ async function logUnusableHelper(config, runtime, log) {
2720
+ if (!isBuiltinVm(config.hooks.provision)) return;
2721
+ const helper = await runtime.vm.helper.probe();
2722
+ if (helper.ok) return;
2723
+ if (helper.reason === "unsupported") log.line(helper.detail);
2724
+ else if (helper.unreachable === true) log.line(`${helper.detail}, so microVM builds run without the jailer`);
2725
+ }
2558
2726
  async function start(configPath, deps = { fetchImpl: fetch }) {
2559
2727
  const log = deps.log ??= createRunnerLog(deps.now ?? (() => /* @__PURE__ */ new Date()));
2560
2728
  const update = deps.update;
@@ -2562,7 +2730,7 @@ async function start(configPath, deps = { fetchImpl: fetch }) {
2562
2730
  const outcome = await onStartUnderMarker({
2563
2731
  configPath,
2564
2732
  own: ownVersionOf(deps),
2565
- check: update.check ? () => update.check(configPath) : async () => (await import('./check-YYKNYFNI.js')).runRunnerCheck(configPath, { fetchImpl: deps.fetchImpl }),
2733
+ check: update.check ? () => update.check(configPath) : async () => (await import('./check-C6HEFW6R.js')).runRunnerCheck(configPath, { fetchImpl: deps.fetchImpl }),
2566
2734
  now: () => (deps.now ?? (() => /* @__PURE__ */ new Date()))().getTime(),
2567
2735
  log
2568
2736
  });
@@ -2601,12 +2769,16 @@ async function start(configPath, deps = { fetchImpl: fetch }) {
2601
2769
  const state = { config, jobs: /* @__PURE__ */ new Map(), deps, configMtimeMs };
2602
2770
  log.line(`runner ${config.host} started \xB7 config ${configPath} \xB7 ${describeConfig(config)}`);
2603
2771
  const nowMs = () => deps.now ? deps.now().getTime() : Date.now();
2604
- const runningMicroVms = await stopOrphanedMicroVms({ jobs: unfinishedJobs(jobsDir(configPath)), runtime: runtimeOf(state) }).catch((err) => {
2772
+ await logUnusableHelper(config, runtimeOf(state), log);
2773
+ const runningMicroVms = await stopOrphanedMicroVms({ jobs: unfinishedJobs(jobsDir(configPath)), runtime: runtimeOf(state), onError: (line) => log.error(line) }).catch((err) => {
2605
2774
  log.error(`a microVM an earlier runner left could not be stopped: ${describeCallFailure(err)}`);
2606
2775
  return [];
2607
2776
  });
2608
2777
  for (const buildId of runningMicroVms) log.line(`stopped microVM build ${buildId}, left running by an earlier runner`);
2609
2778
  const attemptLogs = attemptLogsOf(state);
2779
+ for (const { job, format } of listUnreadableJobs(jobsDir(configPath))) {
2780
+ log.error(`leaving job ${job.queue} #${job.item} alone: its job-state format ${format} is not one this version reads`);
2781
+ }
2610
2782
  for (const { dirPath, job } of listAdoptableJobs(jobsDir(configPath))) {
2611
2783
  const adopted = { job, dirPath, lastRenewAtMs: nowMs(), cmd: null, cpuSample: null, ending: false };
2612
2784
  state.jobs.set(dirPath, adopted);
@@ -2843,4 +3015,4 @@ function runnerProcessState(configPath) {
2843
3015
  return isSameProcess(entry.pid, entry.start) ? { state: "running", pid: entry.pid } : { state: "stale", pid: entry.pid };
2844
3016
  }
2845
3017
 
2846
- export { RunnerStartError, listJobFiles, probeBuild, probeCredentials, requestStop, runnerProcessState, start, tick, watchTick };
3018
+ export { RunnerStartError, listJobFiles, logUnusableHelper, probeBuild, probeCredentials, requestStop, runnerProcessState, start, tick, watchTick };
@@ -1,14 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
3
  import { removeRetiredSkills } from './chunk-MQ743JX3.js';
4
- import { defaultFetch, resolveGatewayCredentials } from './chunk-FKXGQOF5.js';
4
+ import { defaultFetch, resolveGatewayCredentials } from './chunk-M4B6QKNZ.js';
5
5
  import { appendContextProvenance } from './chunk-ZJSNLVCE.js';
6
6
  import { loadMaintainedFiles, writeMaintainedFiles } from './chunk-HBISTTG3.js';
7
7
  import { cliInvocation } from './chunk-CRB3POP4.js';
8
8
  import { writeDegraded, writeSourceCache, SOURCE_CACHE_TTL_RETRY_MS, writeContextManifest, SOURCE_CACHE_TTL_SUCCESS_MS, readSourceCache, isSourceCacheFresh, clearDegraded } from './chunk-YQR6E6OV.js';
9
9
  import { normalizeRepoName, parseOuterlayerConfig, classifyTree, emitTree } from './chunk-RKOTJQVG.js';
10
10
  import { homeFromEnv } from './chunk-VNVZDWO3.js';
11
- import { withTokenHeader } from './chunk-BN3CSI7S.js';
11
+ import { withTokenHeader } from './chunk-2K3TCQHW.js';
12
12
  import { execFileSync } from 'child_process';
13
13
  import { existsSync, mkdirSync, writeFileSync, chmodSync, lstatSync, readFileSync, copyFileSync } from 'fs';
14
14
  import { isAbsolute, join, dirname } from 'path';
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
3
  import { launchRefusalForSession } from './chunk-ESVW4MJY.js';
4
- import { autoConnect, detectActiveSession } from './chunk-F2W2IYNP.js';
4
+ import { autoConnect, detectActiveSession } from './chunk-3WZ5ECSM.js';
5
5
  import { acquireSpoolLockAt } from './chunk-BUP5N3X6.js';
6
- import { resolveGatewayCredentials, defaultFetch, unauthorizedMessage } from './chunk-FKXGQOF5.js';
7
- import { resolveRepoIdentity } from './chunk-D4AMAEZ7.js';
6
+ import { resolveGatewayCredentials, defaultFetch, unauthorizedMessage } from './chunk-M4B6QKNZ.js';
7
+ import { resolveRepoIdentity } from './chunk-QE3UQAHK.js';
8
8
  import { C } from './chunk-ANPNT6PM.js';
9
9
  import { launchedItemForSession } from './chunk-T2Q7ISPE.js';
10
10
  import { parseTrackerNumber, trackerNumberError } from './chunk-WZBHBH3J.js';
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- import { EXEC_PORT } from './chunk-PDIM5T65.js';
3
+ import { EXEC_PORT } from './chunk-XGJUNNXL.js';
4
4
  import { createConnection } from 'net';
5
5
 
6
6
  createRequire(import.meta.url);
@@ -173,7 +173,8 @@ async function waitForAgent(socketPath, options) {
173
173
  try {
174
174
  await execCapture(socketPath, { argv: ["true"], user: "root", cwd: "/", env: {} });
175
175
  return true;
176
- } catch {
176
+ } catch (err) {
177
+ options.onFailure?.(err);
177
178
  await options.sleep(options.delayMs);
178
179
  }
179
180
  }
@@ -1,16 +1,16 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
3
  import { checkStarterFiles, contextIsGoverned, checkBuildSkill } from './chunk-XFGL53GU.js';
4
- import { resolveCredentials, getJson, WorkCommandError } from './chunk-RVYOOYOA.js';
5
- import { isLinkedRepository } from './chunk-V5Q3S4NM.js';
4
+ import { resolveCredentials, getJson, WorkCommandError } from './chunk-CQRIZNE6.js';
5
+ import { isLinkedRepository } from './chunk-SNQR2EYI.js';
6
6
  import { sanitizeTerminal } from './chunk-BUP5N3X6.js';
7
7
  import { checkFormerSkillNames } from './chunk-MQ743JX3.js';
8
- import { resolveGatewayCredentials, defaultFetch } from './chunk-FKXGQOF5.js';
9
- import { sanitizeForContext, spoolDir, assessSyncHealth, readSyncStatus, SYNC_STALE_AFTER_MS, formatAgo, checkInstalledCliStaleness, workspaceRootOf, hookFiredMarkerPath, refreshCommand } from './chunk-755WXDBI.js';
8
+ import { resolveGatewayCredentials, defaultFetch } from './chunk-M4B6QKNZ.js';
9
+ import { sanitizeForContext, spoolDir, assessSyncHealth, readSyncStatus, SYNC_STALE_AFTER_MS, formatAgo, checkInstalledCliStaleness, workspaceRootOf, hookFiredMarkerPath, refreshCommand } from './chunk-K5WVK7IC.js';
10
10
  import { readStatuslineState, STATE_FRESH_MS } from './chunk-DXDF43YG.js';
11
11
  import { hookCommandEscapesControlPlane, CONTROL_PLANE_DIR_VARIABLE } from './chunk-HCVLP7XA.js';
12
12
  import { findGitDirs } from './chunk-BJ3KTMDH.js';
13
- import { resolveRepoIdentity, compareVersions, SUPPORTED_VERSIONS } from './chunk-D4AMAEZ7.js';
13
+ import { resolveRepoIdentity, compareVersions, SUPPORTED_VERSIONS } from './chunk-QE3UQAHK.js';
14
14
  import { C } from './chunk-ANPNT6PM.js';
15
15
  import { cliInvocation } from './chunk-CRB3POP4.js';
16
16
  import { readSettings, settingsPath, isOurStatuslineSlot, statuslineWrappedOriginal, REGISTERED_EVENTS, isOurHookCommand, ourCommandBins, cliBinResolvable, ourDirectScriptBins, wrappedOriginalCommand } from './chunk-GNPTU4BM.js';
@@ -1174,7 +1174,7 @@ function doctorExitCode(checks) {
1174
1174
 
1175
1175
  // src/doctor-report.ts
1176
1176
  async function gatherDoctorChecks() {
1177
- const { defaultRunnerConfigPath, hasRunnerBlock, loadRunnerConfig, runnerVmOf } = await import('./config-7Z5DHSGY.js');
1177
+ const { defaultRunnerConfigPath, hasRunnerBlock, loadRunnerConfig, runnerVmOf } = await import('./config-JJ3EO7SB.js');
1178
1178
  const configPath = defaultRunnerConfigPath();
1179
1179
  const checks = runDoctor({ runnerHost: hasRunnerBlock(configPath) });
1180
1180
  const credential = await credentialChecks();
@@ -1186,7 +1186,7 @@ async function gatherDoctorChecks() {
1186
1186
  const { runnerVmCheck } = await import('./doctor-53F2PYY7.js');
1187
1187
  checks.push(runnerVmCheck(configPath, { lima: realLima(), home: homeFromEnv(), instance: runnerVm.instance }));
1188
1188
  } else if (hasRunnerBlock(configPath)) {
1189
- const { runnerHostChecks } = await import('./doctor-DOAFZV4O.js');
1189
+ const { runnerHostChecks } = await import('./doctor-S6YCZGUU.js');
1190
1190
  const hooks = (() => {
1191
1191
  try {
1192
1192
  return loadRunnerConfig(configPath).hooks;
@@ -1221,7 +1221,7 @@ async function gatherDoctorChecks() {
1221
1221
  const { existsSync: existsSync3 } = await import('fs');
1222
1222
  if (process.platform !== "linux" || !existsSync3("/run/systemd/system")) return void 0;
1223
1223
  const { inspectService, realInspectProbes } = await import('./inspect-6UYAAXH7.js');
1224
- const { DEFAULT_UNIT_NAME } = await import('./install-ORQC26ZE.js');
1224
+ const { DEFAULT_UNIT_NAME } = await import('./install-MPEJJ7X6.js');
1225
1225
  const { dirname: dirname2 } = await import('path');
1226
1226
  return inspectService(DEFAULT_UNIT_NAME, dirname2(configPath), realInspectProbes());
1227
1227
  })();
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- import { builtinKind } from './chunk-D7X5ITVT.js';
3
+ import { builtinKind } from './chunk-JWRX5ZTT.js';
4
4
  import { parseSize } from './chunk-7RQ73DLO.js';
5
- import { runnerDir } from './chunk-KF37SUDM.js';
5
+ import { runnerDir } from './chunk-ZI2ZVWKT.js';
6
6
  import { dirname } from 'path';
7
7
  import { spawnSync } from 'child_process';
8
8
 
@@ -16,16 +16,19 @@ function escapeUnitPath(path) {
16
16
  return path.replace(/%/g, "%%").replace(/ /g, "\\x20");
17
17
  }
18
18
  function runnerUnit(unit) {
19
+ const helperService = unit.helper === void 0 ? void 0 : `${unit.helper.unit}.service`;
19
20
  return `${UNIT_MARKER}
20
21
  [Unit]
21
22
  Description=OuterLayer runner
22
- After=network-online.target${unit.docker ? " docker.service" : ""}
23
+ After=network-online.target${unit.docker ? " docker.service" : ""}${helperService === void 0 ? "" : ` ${helperService}`}
23
24
  Wants=network-online.target
24
- ${unit.docker ? "Requires=docker.service\n" : ""}
25
+ ${helperService === void 0 ? "" : `Wants=${helperService}
26
+ `}${unit.docker ? "Requires=docker.service\n" : ""}
25
27
  [Service]
26
28
  User=${unit.user}
27
29
  ExecStart=${unit.execStart}
28
- EnvironmentFile=-${escapeUnitPath(`${unit.configDir}/runner.env`)}
30
+ ${unit.helper === void 0 ? "" : `Environment=OUTERLAYER_HELPER_SOCKET=${unit.helper.socket}
31
+ `}EnvironmentFile=-${escapeUnitPath(`${unit.configDir}/runner.env`)}
29
32
  Restart=on-failure
30
33
  RestartForceExitStatus=75
31
34
  RestartSec=30
@@ -143,7 +146,8 @@ function installService(request, deps) {
143
146
  configDir: dirname(request.configPath),
144
147
  execStart: request.execStart,
145
148
  timeLimitMinutes: request.timeLimitMinutes,
146
- docker: kind === "container" || kind === "vm" || builtinKind(request.hooks.cleanup) === "container" || builtinKind(request.hooks.cleanup) === "vm"
149
+ docker: kind === "container" || kind === "vm" || builtinKind(request.hooks.cleanup) === "container" || builtinKind(request.hooks.cleanup) === "vm",
150
+ ...request.helperSocket !== void 0 && request.helperUnit !== void 0 ? { helper: { unit: request.helperUnit, socket: request.helperSocket } } : {}
147
151
  });
148
152
  if (request.print === true) return outcome(true, "printed", [], unit);
149
153
  if (own) return outcome(false, "refused", [OWN_HOOKS]);
@@ -274,4 +278,4 @@ function uninstallService(request, deps) {
274
278
  return outcome(true, "removed", lines);
275
279
  }
276
280
 
277
- export { DEFAULT_UNIT_NAME, RUNNER_RESERVE_BYTES, VM_SERVICE_NAME, checkFit, installService, realTaskScheduler, uninstallService, unitPathFor };
281
+ export { DEFAULT_UNIT_NAME, RUNNER_RESERVE_BYTES, UNIT_MARKER, VM_SERVICE_NAME, checkFit, installService, realTaskScheduler, uninstallService, unitPathFor };
@@ -30,12 +30,14 @@ var UNKNOWN_TRANSPORT_REASON = "the runner cannot tell how this build reaches it
30
30
  var COMMAND_LOST_REASON = "the build's command ended without recording an exit code";
31
31
  var CONTAINER_STOPPED_REASON = "the build's container stopped while no runner was managing it";
32
32
  var UNKNOWN_TRACKING_REASON = "the runner cannot tell how this build is tracked";
33
+ var DOCKER_DESKTOP_REASON = "Docker Desktop is accepted only for a runner in the runner image, so container builds here need Docker Engine: install it (on Windows, inside WSL2), run the runner image, or use builtin:process";
33
34
  var RUNNER_WORDED_REASONS = /* @__PURE__ */ new Set([
34
35
  MALFORMED_REASON,
35
36
  UNKNOWN_TRANSPORT_REASON,
36
37
  COMMAND_LOST_REASON,
37
38
  CONTAINER_STOPPED_REASON,
38
- UNKNOWN_TRACKING_REASON
39
+ UNKNOWN_TRACKING_REASON,
40
+ DOCKER_DESKTOP_REASON
39
41
  ]);
40
42
 
41
- export { COMMAND_LOST_REASON, CONTAINER_STOPPED_REASON, JOIN_DIR, JOIN_SOCKET, JOIN_WAIT_MS, MALFORMED_REASON, MAX_MALFORMED_SESSIONS, MAX_OPEN_STREAMS, RELAY_ID, RELAY_USER, RUNNER_WORDED_REASONS, SESSION_WINDOW, STREAM_WINDOW, TARGET_CLAUDE, TARGET_GATEWAY, TARGET_LEASE, TARGET_TUNNEL, UNKNOWN_TRACKING_REASON, UNKNOWN_TRANSPORT_REASON, destinationTarget, targetOfPath };
43
+ export { COMMAND_LOST_REASON, CONTAINER_STOPPED_REASON, DOCKER_DESKTOP_REASON, JOIN_DIR, JOIN_SOCKET, JOIN_WAIT_MS, MALFORMED_REASON, MAX_MALFORMED_SESSIONS, MAX_OPEN_STREAMS, RELAY_ID, RELAY_USER, RUNNER_WORDED_REASONS, SESSION_WINDOW, STREAM_WINDOW, TARGET_CLAUDE, TARGET_GATEWAY, TARGET_LEASE, TARGET_TUNNEL, UNKNOWN_TRACKING_REASON, UNKNOWN_TRANSPORT_REASON, destinationTarget, targetOfPath };
@@ -3,7 +3,7 @@ import { createRequire } from 'node:module';
3
3
  import { MAX_ALLOW_HOSTS, allowHostProblem } from './chunk-UXYLWVTQ.js';
4
4
  import { parseSize } from './chunk-7RQ73DLO.js';
5
5
  import { homeFromEnv } from './chunk-VNVZDWO3.js';
6
- import { isRefusedInConfig, resolveDestinations, isReservedVariable } from './chunk-BN3CSI7S.js';
6
+ import { isRefusedInConfig, resolveDestinations, isReservedVariable } from './chunk-2K3TCQHW.js';
7
7
  import { existsSync, statSync, accessSync, constants, readFileSync } from 'fs';
8
8
  import { hostname } from 'os';
9
9
  import { join } from 'path';
@@ -92,6 +92,7 @@ function runnerVmOf(configPath) {
92
92
  }
93
93
  }
94
94
  var RUNNER_KEYS = [
95
+ "name",
95
96
  "host",
96
97
  "queues",
97
98
  "concurrency",
@@ -212,6 +213,9 @@ function loadRunnerConfig(configPath) {
212
213
  if (runner.autoUpdate !== void 0 && typeof runner.autoUpdate !== "boolean") {
213
214
  fail(configPath, "runner.autoUpdate", "must be true or false");
214
215
  }
216
+ if (runner.name !== void 0 && runner.host !== void 0 && runner.name !== runner.host) {
217
+ fail(configPath, "runner.name", `is "${runner.name}" and runner.host is "${runner.host}"; set runner.name only, since runner.host is its earlier name`);
218
+ }
215
219
  const queues = runner.queues && runner.queues.length > 0 ? runner.queues : [...RUNNER_QUEUES];
216
220
  for (const queue of queues) {
217
221
  if (!RUNNER_QUEUES.includes(queue)) {
@@ -241,7 +245,7 @@ function loadRunnerConfig(configPath) {
241
245
  appId: config.appId,
242
246
  repos: config.repos,
243
247
  scrub: config.scrub,
244
- host: runner.host ?? hostname(),
248
+ host: runner.name ?? runner.host ?? hostname(),
245
249
  queues,
246
250
  concurrency: runner.concurrency ?? DEFAULT_CONCURRENCY,
247
251
  pollSeconds: runner.pollSeconds ?? DEFAULT_POLL_SECONDS,
@@ -628,7 +628,7 @@ async function missingControlPlane(cwd, sessionId, home, env) {
628
628
  cleanupEarlierCopies(dirs);
629
629
  }
630
630
  if (!contextPossiblyGoverned(cwd, home)) return void 0;
631
- const { governanceOf } = await import('./control-plane-launch-GKGI6HJA.js');
631
+ const { governanceOf } = await import('./control-plane-launch-ZFT6SI4N.js');
632
632
  if (await governanceOf({ cwd, env, home }) !== "governed") return void 0;
633
633
  recordMissingContext(dirs, sessionId, home);
634
634
  const launcher = `${cliInvocation()} claude`;
@@ -5,7 +5,7 @@ import { readCloudConfig, cloudConfigPath } from './chunk-EABW6AJQ.js';
5
5
  import { cliInvocation } from './chunk-CRB3POP4.js';
6
6
  import { homeFromEnv } from './chunk-VNVZDWO3.js';
7
7
  import { resolvePackageVersion } from './chunk-RA3O54FC.js';
8
- import { signRunnerRequest, signerFor } from './chunk-PVRBE7S4.js';
8
+ import { signRunnerRequest, signerFor } from './chunk-ZODVSFI4.js';
9
9
 
10
10
  createRequire(import.meta.url);
11
11