@outerlayer/cli 0.26.4 → 0.27.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 (117) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/README.md +12 -9
  3. package/dist/{agent-setup-5DXPEO64.js → agent-setup-UW36TAQM.js} +11 -11
  4. package/dist/{bridge-5KZ36YDG.js → bridge-B5VJVECY.js} +2 -2
  5. package/dist/{build-F5QWH7G5.js → build-TNM7HYCA.js} +4 -4
  6. package/dist/build-info.json +1 -1
  7. package/dist/{builder-KDUXCHTY.js → builder-LDJGS2JC.js} +5 -5
  8. package/dist/{check-ZSYCR5WG.js → check-JVBAWIN6.js} +40 -39
  9. package/dist/{chunk-GFHLEVUE.js → chunk-24ID7EQ2.js} +6 -3
  10. package/dist/{chunk-H57M3HC7.js → chunk-2F762IXY.js} +2 -2
  11. package/dist/{chunk-2K3TCQHW.js → chunk-2I3LIRZP.js} +26 -8
  12. package/dist/{chunk-6CGXSMKX.js → chunk-55GNQPO6.js} +17 -6
  13. package/dist/{chunk-MWPBF2XG.js → chunk-6KKM7VGZ.js} +32 -4
  14. package/dist/{chunk-VPEGE3QZ.js → chunk-6TRNCUM4.js} +1 -1
  15. package/dist/{chunk-6SPXKRQR.js → chunk-7AQ5AM7U.js} +12 -5
  16. package/dist/{chunk-HBISTTG3.js → chunk-7OUOLGUV.js} +20 -5
  17. package/dist/{chunk-NWPRNKZ5.js → chunk-ABXQBEYK.js} +1 -1
  18. package/dist/chunk-BM7EC3OZ.js +181 -0
  19. package/dist/{chunk-4UFSTKE5.js → chunk-BPKO76QR.js} +4 -4
  20. package/dist/{chunk-MCSINUVC.js → chunk-BWH36FOC.js} +1 -1
  21. package/dist/{chunk-IZAOO5AR.js → chunk-D7ECMB7B.js} +4 -4
  22. package/dist/{chunk-UZEQVPKF.js → chunk-DJHNEGNM.js} +27 -3
  23. package/dist/{chunk-O4362FPA.js → chunk-E5IJ5533.js} +2 -2
  24. package/dist/{chunk-JWRX5ZTT.js → chunk-EPPIISRD.js} +2 -2
  25. package/dist/{chunk-KNBWOL2B.js → chunk-FP23SVDD.js} +5 -5
  26. package/dist/{chunk-6WTAYEKJ.js → chunk-G3EZA6MK.js} +55 -7
  27. package/dist/{chunk-VGHKTSHU.js → chunk-GFUE5TKO.js} +3 -3
  28. package/dist/{chunk-XLC7KEUV.js → chunk-GLUKDQS4.js} +1 -1
  29. package/dist/{chunk-QUP6CVBQ.js → chunk-JH2GQU5T.js} +4 -4
  30. package/dist/{chunk-JKNYTNYD.js → chunk-LOKSLO25.js} +1 -1
  31. package/dist/{chunk-2J46BQ5Y.js → chunk-M3MHHIUW.js} +1 -1
  32. package/dist/{chunk-7Z2WMXM5.js → chunk-MATUHHN4.js} +1 -1
  33. package/dist/{chunk-MQ743JX3.js → chunk-MHX7YX7Y.js} +1 -1
  34. package/dist/{chunk-PZA6ZFYW.js → chunk-N4D5TMVU.js} +2 -2
  35. package/dist/{chunk-WK35HB56.js → chunk-O2IJTY7L.js} +2 -2
  36. package/dist/{chunk-K4FXNMV5.js → chunk-QHH4OSD4.js} +1 -1
  37. package/dist/{chunk-OQUD3UYF.js → chunk-QIVOTQOQ.js} +5 -5
  38. package/dist/{chunk-Y4BXRVDI.js → chunk-QKIQ2R7F.js} +3 -3
  39. package/dist/{chunk-F3BBEBPL.js → chunk-RHAGBUL5.js} +1 -1
  40. package/dist/{chunk-OFTFW7TH.js → chunk-RHGF73IU.js} +1 -1
  41. package/dist/{chunk-27RDUTQH.js → chunk-RK7FWC6B.js} +194 -38
  42. package/dist/{chunk-6MZSX4M7.js → chunk-STSLH5EJ.js} +3 -1
  43. package/dist/{chunk-ZMZGWLYD.js → chunk-TXHJ7BGR.js} +13 -13
  44. package/dist/{chunk-EHBDYLM6.js → chunk-UQYBCJ2L.js} +2 -2
  45. package/dist/{chunk-VQ7OAG6H.js → chunk-VS27BP3E.js} +4 -4
  46. package/dist/{chunk-OZKEEXVH.js → chunk-WSYXLLA5.js} +6 -6
  47. package/dist/{chunk-WX2WEERU.js → chunk-WUL2OP5A.js} +3 -3
  48. package/dist/{chunk-UOGQZQXN.js → chunk-WVDP6HVZ.js} +6 -6
  49. package/dist/{chunk-MXTN35XN.js → chunk-XBPUTQ7E.js} +1 -1
  50. package/dist/{chunk-UNF7MWEL.js → chunk-XS3FDWSZ.js} +1 -1
  51. package/dist/{chunk-VJBFQP3H.js → chunk-XZ6Q6ZNR.js} +9 -5
  52. package/dist/{chunk-GWRDL75D.js → chunk-Y4OKEFAX.js} +1 -1
  53. package/dist/{chunk-Q2SVLBMI.js → chunk-Y7ZCHBYB.js} +2 -2
  54. package/dist/chunk-YB3CSYXX.js +95 -0
  55. package/dist/{chunk-SBW4K3XZ.js → chunk-YF56R4DV.js} +75 -29
  56. package/dist/chunk-YJLHUKYS.js +38 -0
  57. package/dist/{chunk-V3YJFEZA.js → chunk-ZXRWMOJ3.js} +274 -45
  58. package/dist/{claude-launcher-TTLTS4N6.js → claude-launcher-TKX4WCUN.js} +8 -4
  59. package/dist/{cli-SKF3AMZ5.js → cli-XVXQDKZT.js} +133 -127
  60. package/dist/{command-E5OG5NCH.js → command-B2T7VR77.js} +29 -28
  61. package/dist/{config-JJ3EO7SB.js → config-TWS5VZEN.js} +3 -3
  62. package/dist/{connect-cmd-SL4S5HJM.js → connect-cmd-HGS7YPA2.js} +7 -7
  63. package/dist/{container-start-IA2HK4QI.js → container-start-JE7HTKZG.js} +28 -27
  64. package/dist/{control-plane-launch-JIQOE6BP.js → control-plane-launch-DBQA3KLJ.js} +8 -8
  65. package/dist/{docker-KKE5Q5S2.js → docker-MJEEHUUA.js} +5 -5
  66. package/dist/{doctor-U2N7AF3N.js → doctor-6ZIF2BRZ.js} +180 -33
  67. package/dist/{emit-artifact-cmd-SDG5H6D7.js → emit-artifact-cmd-ALDK55Y2.js} +11 -11
  68. package/dist/{emit-cmd-WQH2NK7H.js → emit-cmd-VAQHRWJR.js} +4 -4
  69. package/dist/{emit-criteria-cmd-QGSP77AG.js → emit-criteria-cmd-Q7SM6VP2.js} +8 -8
  70. package/dist/{emit-finding-cmd-6FGMTEFM.js → emit-finding-cmd-2NBAPDSV.js} +8 -8
  71. package/dist/{emit-result-cmd-AUPIXPEU.js → emit-result-cmd-KUSL56RI.js} +11 -11
  72. package/dist/{emit-step-cmd-PORHEKHN.js → emit-step-cmd-KD4AKEAT.js} +9 -9
  73. package/dist/{exec-client-J6L5FDRR.js → exec-client-6WVS7DRZ.js} +3 -3
  74. package/dist/{governing-source-YPBK4H5H.js → governing-source-VVUA5TKB.js} +13 -13
  75. package/dist/{guest-init-CXIHA5VI.js → guest-init-H5K2RW3P.js} +4 -4
  76. package/dist/{hook-fast-XMZFMBKC.js → hook-fast-3SUJM765.js} +1 -1
  77. package/dist/{host-key-GQFBRX3J.js → host-key-XNKZCWTS.js} +3 -3
  78. package/dist/{image-tag-W4JKVNP4.js → image-tag-R34J4O3F.js} +3 -3
  79. package/dist/index.js +4 -4
  80. package/dist/{init-GEFKJNJU.js → init-ETKGFCXR.js} +28 -27
  81. package/dist/{init-Y64OAKKQ.js → init-Q2RPUUVP.js} +29 -28
  82. package/dist/{init-cmd-YR4LLIRU.js → init-cmd-M6MQRGOU.js} +15 -14
  83. package/dist/{init-update-46G6QJHB.js → init-update-N4HTZGVS.js} +55 -26
  84. package/dist/{inspect-6UYAAXH7.js → inspect-IPFA7AAT.js} +1 -1
  85. package/dist/{install-AFMXH25X.js → install-6OIY3KHZ.js} +6 -5
  86. package/dist/{install-cmd-X5XCUTSX.js → install-cmd-JMT67P35.js} +5 -5
  87. package/dist/{layout-UOJQGEPW.js → layout-XNFDTOET.js} +2 -2
  88. package/dist/{logs-PKNQOJN6.js → logs-XHAS6LMZ.js} +2 -2
  89. package/dist/{loop-3TJKFRN7.js → loop-2Y4CUFNP.js} +40 -39
  90. package/dist/{machine-3W4LKTEW.js → machine-Z2S47JW7.js} +23 -22
  91. package/dist/machine-skills-RRF4HLAD.js +11 -0
  92. package/dist/{mcp-serve-cmd-MJIJHSQ6.js → mcp-serve-cmd-LY5A3CE7.js} +4 -4
  93. package/dist/{paths-6V7572NN.js → paths-LHSPNORF.js} +2 -2
  94. package/dist/{pending-GUHHKO42.js → pending-3YZCSKON.js} +3 -3
  95. package/dist/{pidfile-ZQOFDY7T.js → pidfile-DKMYYXLH.js} +3 -3
  96. package/dist/{record-C4ZVSLKV.js → record-3LPTY3G5.js} +3 -3
  97. package/dist/{runner-env-6Q2RMENH.js → runner-env-YRRNRKF4.js} +8 -7
  98. package/dist/{runner-identity-DP577A7S.js → runner-identity-EX7HXPUE.js} +5 -5
  99. package/dist/{runner-key-LR5P2EJ5.js → runner-key-GO277GW2.js} +28 -27
  100. package/dist/{session-sender-ZTPWIU5L.js → session-sender-SC5PSU2G.js} +12 -12
  101. package/dist/{spec-cmd-RLZMX67G.js → spec-cmd-JGYHHJ4P.js} +9 -9
  102. package/dist/{starter-pack-55EFILC5.js → starter-pack-GPU4X6B7.js} +2 -2
  103. package/dist/{status-GNUOTHRH.js → status-CJCE2FHD.js} +30 -29
  104. package/dist/{sync-cmd-GLWEAULP.js → sync-cmd-ATXJVS7S.js} +11 -11
  105. package/dist/{watch-F47NPUDX.js → watch-M7NR3DOW.js} +11 -11
  106. package/dist/{work-claim-cmd-GXPIRRBG.js → work-claim-cmd-DNUDZKJA.js} +13 -13
  107. package/dist/{work-cmd-S2FHIFJA.js → work-cmd-VECTYST6.js} +9 -9
  108. package/dist/{work-comment-cmd-UJRKULO6.js → work-comment-cmd-F3NRWKPZ.js} +9 -9
  109. package/dist/{work-logs-cmd-SCAHJXSP.js → work-logs-cmd-QF6P32FE.js} +9 -9
  110. package/dist/{work-open-pr-cmd-BGMHUQGD.js → work-open-pr-cmd-XE23JA3R.js} +10 -10
  111. package/dist/{work-pr-cmd-EDDRP5DK.js → work-pr-cmd-BH56GUKN.js} +9 -9
  112. package/dist/{work-stop-cmd-ADE5K6UX.js → work-stop-cmd-C3F5HNUO.js} +9 -9
  113. package/package.json +1 -1
  114. package/skill-pack/maintained/outerlayer-cli/SKILL.md +6 -5
  115. package/skill-pack/shipped.json +1 -0
  116. package/skill-pack/template/build/SKILL.md +1 -1
  117. package/dist/chunk-7RQ73DLO.js +0 -23
@@ -1,34 +1,35 @@
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, listUnreadableJobs, listAdoptableJobs, retryFailedReleases, recoverLostJobs, hookLimitsFor, AttemptLogBook, needsRenew, renewLease, RENEW_INTERVAL_SECONDS, listTrackedJobs, restartSender, ALREADY_RELEASED, inTeardown } from './chunk-27RDUTQH.js';
4
- import { writePendingUpdate } from './chunk-OFTFW7TH.js';
3
+ import { releaseLease, isUnfinished, setStage, jobEnv, runHook, describeHookEnd, provisionMicroVm, deliverCommitHooks, provisionProcess, provisionEnvironment, readProvisionOut, resolveHostBuild, workdirProblem, listenOnBroker, hostEnvironment, hostCommandEnvironment, execArgv, MICROVM_COMMAND_DIR, commandLogOffset, startMicroVmCommand, microVmCommandLine, runCommand, execCommandLine, startSender, startHostServices, finishJob, isContainerTracked, isMicroVmTracked, probeMicroVmCommand, probeCommand, appendCommandLog, judgeProbe, EXIT_CODE_FILE, isTracked, isGroupAlive, outcomeFromExitCode, isTimedOut, readCpuUsec, isIdle, stopOrphanedMicroVms, unfinishedJobs, listUnreadableJobs, listAdoptableJobs, retryFailedReleases, recoverLostJobs, hookLimitsFor, AttemptLogBook, microVmOomKills, needsRenew, renewLease, RENEW_INTERVAL_SECONDS, listTrackedJobs, restartSender, serveLease, vmAlive, writeGuestGhConfig, ALREADY_RELEASED, inTeardown } from './chunk-RK7FWC6B.js';
4
+ import { writePendingUpdate } from './chunk-RHGF73IU.js';
5
5
  import { JOB_STATE_FORMAT, readsJobState, jobStateFormatOf } from './chunk-ZVB57WI6.js';
6
- import { readHousekeepingRecord, writeHousekeepingRecord } from './chunk-NWPRNKZ5.js';
6
+ import { readHousekeepingRecord, writeHousekeepingRecord } from './chunk-ABXQBEYK.js';
7
7
  import { decideInstalledSwitch } from './chunk-3JVYMD4G.js';
8
8
  import { fetchVersionMeta, npmVersion, npmSupportsProvenance, fetchTarballDigest, fetchProvenance, installVersion, auditSignatures } from './chunk-YETTWTAY.js';
9
9
  import { RUNNER_PROTOCOL } from './chunk-TPKYTORP.js';
10
- import { currentSkillCommand, formerSkillProblem, placeholderQueues, placeholderProblem } from './chunk-UOGQZQXN.js';
11
- import { writeMarker, readMarker, currentVersion, removeMarker, entryIn, versionDir, startedThroughCurrent, switchCurrent, isInLayout, cleanVersions, stagingDir } from './chunk-7Z2WMXM5.js';
10
+ import { currentSkillCommand, formerSkillProblem, placeholderQueues, placeholderProblem } from './chunk-WVDP6HVZ.js';
11
+ import { writeMarker, readMarker, currentVersion, removeMarker, entryIn, versionDir, startedThroughCurrent, switchCurrent, isInLayout, cleanVersions, stagingDir } from './chunk-MATUHHN4.js';
12
12
  import { freeBytes, formatBytes } from './chunk-YYYRBYWG.js';
13
13
  import { RepoFilter } from './chunk-TBT347UY.js';
14
- import { leaseTimeLeftMs, LEASE_SECONDS, provisionContainer, leaseExpiresAtMs, deliverContainerHooks, lifecycleSteps, lifecycleFailureReason, prepareBranch, containerCommandExec, measureDisk, setUpRunnerCgroup, createContainerRuntime, parseCpuList, ensureTunnel, keepGhConfigCurrent, filesAreCopied } from './chunk-SBW4K3XZ.js';
15
- import { tagToPull, pullInstruction, imageTag, writeUpdateStatus } from './chunk-2J46BQ5Y.js';
14
+ import { leaseTimeLeftMs, LEASE_SECONDS, provisionContainer, leaseExpiresAtMs, deliverContainerHooks, lifecycleSteps, lifecycleFailureReason, prepareBranch, containerCommandExec, setUpRunnerCgroup, createContainerRuntime, measureDisk, parseCpuList, ensureTunnel, keepGhConfigCurrent, filesAreCopied, gitRemoteFor, startServicesFor, createLeaseChannel } from './chunk-YF56R4DV.js';
15
+ import { describeCallFailure, retrySend, shortClaim, relativeToRunner, formatDuration, BuildTokens, classifyRefusal, refusalCode, createRunnerLog, formatClock } from './chunk-WSYXLLA5.js';
16
+ import { tagToPull, pullInstruction, imageTag, writeUpdateStatus } from './chunk-M3MHHIUW.js';
16
17
  import { isVersion, compareVersions, satisfiesRange } from './chunk-TQMI335S.js';
17
- import { checkFit } from './chunk-6SPXKRQR.js';
18
- import { claimPidFile, readPidFile, releasePidFile } from './chunk-IZAOO5AR.js';
18
+ import { checkFit } from './chunk-7AQ5AM7U.js';
19
+ import { fdName } from './chunk-YB3CSYXX.js';
20
+ import { claimPidFile, writePidFile, readPidFile, releasePidFile } from './chunk-D7ECMB7B.js';
19
21
  import { startTimeOf, isSameProcess } from './chunk-JBJTC3UU.js';
20
- import { writeJobFile, ensureRunnerIdentity, readJobFile, builderName, runnerLabelValue, ensureFailureCount, builderSpec, ensureBuilder, spawnRun, BUILD_LABEL, RUNNER_LABEL, readJobFileChecked } from './chunk-QUP6CVBQ.js';
21
- import { describeCallFailure, retrySend, shortClaim, relativeToRunner, formatDuration, BuildTokens, classifyRefusal, refusalCode, createRunnerLog, formatClock } from './chunk-OZKEEXVH.js';
22
- import { isSignatureRefusal, WorkCommandError, getJson, postJson } from './chunk-6CGXSMKX.js';
23
- import { isBuiltinContainer, isBuiltinVm, isBuiltinProcess, loadRunnerConfig, checkHooks, builtinKind, DEFAULT_UPDATE_MAX_WAIT_HOURS } from './chunk-JWRX5ZTT.js';
24
- import { exceedsDisk } from './chunk-7RQ73DLO.js';
22
+ import { writeJobFile, ensureRunnerIdentity, readJobFile, builderName, runnerLabelValue, ensureFailureCount, builderSpec, ensureBuilder, spawnRun, BUILD_LABEL, RUNNER_LABEL, readJobFileChecked } from './chunk-JH2GQU5T.js';
23
+ import { isSignatureRefusal, WorkCommandError, getJson, postJson } from './chunk-55GNQPO6.js';
24
+ import { isBuiltinContainer, isBuiltinVm, isBuiltinProcess, loadRunnerConfig, checkHooks, builtinKind, DEFAULT_UPDATE_MAX_WAIT_HOURS } from './chunk-EPPIISRD.js';
25
+ import { exceedsDisk } from './chunk-YJLHUKYS.js';
25
26
  import { resolveVersion } from './chunk-RA3O54FC.js';
26
- import { loadHostKey, ensureHostKey, signerFor } from './chunk-GWRDL75D.js';
27
- import { RUNNER_DIR_MODE, runnerDir, buildDir, attemptStamp, jobDir, RUNNER_FILE_MODE, jobsDir, pidFilePath, spoolDir, rootfsDir } from './chunk-F3BBEBPL.js';
28
- import { parseClaimIdentity, parseClaimedRepositories, parseUnreachable, repositoriesOf, primaryOf, sharedUserEnvironment, JOB_MOUNT, claudeCredential } from './chunk-2K3TCQHW.js';
27
+ import { loadHostKey, ensureHostKey, signerFor } from './chunk-Y4OKEFAX.js';
28
+ import { RUNNER_DIR_MODE, runnerDir, buildDir, attemptStamp, jobDir, RUNNER_FILE_MODE, jobsDir, pidFilePath, spoolDir, rootfsDir } from './chunk-RHAGBUL5.js';
29
+ import { parseClaimIdentity, parseClaimedRepositories, parseUnreachable, repositoriesOf, primaryOf, sharedUserEnvironment, JOB_MOUNT, claudeCredential } from './chunk-2I3LIRZP.js';
29
30
  import { COMMAND_LOST_REASON, CONTAINER_STOPPED_REASON } from './chunk-JGD5TWQ2.js';
30
31
  import { randomBytes } from 'crypto';
31
- import { mkdirSync, existsSync, chmodSync, writeFileSync, renameSync, statSync, readdirSync, readFileSync, rmSync, closeSync, openSync, constants, lstatSync } from 'fs';
32
+ import { mkdirSync, existsSync, chmodSync, writeFileSync, renameSync, statSync, readdirSync, readFileSync, closeSync, rmSync, openSync, constants, lstatSync } from 'fs';
32
33
  import { join, dirname } from 'path';
33
34
  import { totalmem, constants as constants$1 } from 'os';
34
35
 
@@ -272,8 +273,11 @@ function stageReason(job) {
272
273
  }
273
274
  }
274
275
  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";
276
+ if (job.microvm !== void 0) {
277
+ if (!isMicroVmTracked(job)) return "is a microVM build that a restarted runner cannot adopt";
278
+ } else if (!isContainerTracked(job)) {
279
+ return "is not a container build tracked inside its container";
280
+ }
277
281
  if (ending) return "is being ended";
278
282
  return stageReason(job);
279
283
  }
@@ -986,6 +990,30 @@ function writeCommitHooks(buildDir2, coAuthor) {
986
990
  chmodSync(data, 420);
987
991
  return dir;
988
992
  }
993
+
994
+ // src/runner/microvm/adopt.ts
995
+ var MICROVM_STOPPED_REASON = "the build's microVM stopped while no runner was managing it";
996
+ var MICROVM_SOCKETS_LOST_REASON = "the build's sockets did not survive the runner's restart";
997
+ var MICROVM_HELPER_UNREACHABLE_REASON = "the runner could not ask its helper about the build's microVM when it started";
998
+ async function judgeMicroVmAdoption(input) {
999
+ const { job, runtime } = input;
1000
+ const state = job.microvm;
1001
+ if (state === void 0 || !isMicroVmTracked(job)) return "unservable";
1002
+ const probe = await runtime.vm.helper.probe();
1003
+ if (probe.ok) {
1004
+ const unit = await probe.client.state(state.buildId).catch((err) => {
1005
+ input.onError?.(`the state of the unit of build ${state.buildId} could not be read: ${describeCallFailure(err)}`);
1006
+ return { active: true, oomKills: 0 };
1007
+ });
1008
+ if (!unit.active) return "stopped";
1009
+ } else {
1010
+ input.onError?.(`the unit of build ${state.buildId} could not be asked about, because there is no helper to ask: ${probe.detail}`);
1011
+ return probe.unreachable === true ? "unasked" : "unservable";
1012
+ }
1013
+ const inherited = runtime.vm.fdStore.inherited();
1014
+ const names = state.fdStore ?? [];
1015
+ return names.length > 0 && names.every((name) => inherited.has(name)) ? "adopt" : "unservable";
1016
+ }
989
1017
  function linuxSignalName(number) {
990
1018
  const names = {
991
1019
  1: "SIGHUP",
@@ -1749,7 +1777,7 @@ var CLAIM_REFUSAL_CODES = new Set(
1749
1777
  "pull_request_from_fork pull_request_on_default_branch pull_request_ambiguous pull_request_repository_not_connected repository_not_reachable branch_claimed_elsewhere".split(" ")
1750
1778
  );
1751
1779
  async function tryClaimAndStart(state, item, queue) {
1752
- const { postJson: postJson2 } = await import('./work-cmd-S2FHIFJA.js');
1780
+ const { postJson: postJson2 } = await import('./work-cmd-VECTYST6.js');
1753
1781
  const log = logOf(state);
1754
1782
  const hostKey = await loadHostKey(state.config.configPath);
1755
1783
  const claim = await retrySend(
@@ -2247,8 +2275,16 @@ async function startJob(state, item, queue, claimId, itemKey, branch, identity,
2247
2275
  try {
2248
2276
  if (job.microvm !== void 0) {
2249
2277
  const runtime = runtimeOf(state);
2250
- const composed = microVmCommandLine({ job, cmdline, runtime, config: state.config, cli: join(runtime.cliBuild().distDir, "index.js") });
2251
- running = runCommand(composed.line, { ...composed.env, OUTERLAYER_JOB_DIR: dirPath }, dirPath, join(dirPath, "command.log"), state.deps.spawnFn);
2278
+ if (job.microvm.jailed !== void 0 && (job.microvm.fdStore?.length ?? 0) > 0) {
2279
+ job.microvm.tracking = "microvm";
2280
+ job.microvm.commandDir = MICROVM_COMMAND_DIR;
2281
+ job.microvm.commandLogBase = commandLogOffset(dirPath, 0);
2282
+ writeJobFile(jobFilePath, job);
2283
+ await startMicroVmCommand({ job, cmdline, runtime, config: state.config });
2284
+ } else {
2285
+ const composed = microVmCommandLine({ job, cmdline, runtime, config: state.config, cli: join(runtime.cliBuild().distDir, "index.js") });
2286
+ running = runCommand(composed.line, { ...composed.env, OUTERLAYER_JOB_DIR: dirPath }, dirPath, join(dirPath, "command.log"), state.deps.spawnFn);
2287
+ }
2252
2288
  } else if (job.container !== void 0) {
2253
2289
  const runtime = runtimeOf(state);
2254
2290
  const exec = containerCommandExec({ job, cmdline, runtime, config: state.config });
@@ -2276,7 +2312,9 @@ async function startJob(state, item, queue, claimId, itemKey, branch, identity,
2276
2312
  writeJobFile(jobFilePath, job);
2277
2313
  const commandStartedMs = nowOf(state).getTime();
2278
2314
  active.commandStartedMs = commandStartedMs;
2279
- log.line(running === void 0 ? `${tag} command started \xB7 in container ${job.container.container}` : `${tag} command started \xB7 pid ${running.pid}`);
2315
+ log.line(
2316
+ running !== void 0 ? `${tag} command started \xB7 pid ${running.pid}` : job.container !== void 0 ? `${tag} command started \xB7 in container ${job.container.container}` : `${tag} command started \xB7 in microVM build ${job.microvm.buildId}`
2317
+ );
2280
2318
  const sender = await startSender({
2281
2319
  config: state.config,
2282
2320
  job,
@@ -2464,19 +2502,67 @@ async function endStoppedBuild(state, active, where) {
2464
2502
  );
2465
2503
  void endJob(state, active, "interrupted", { stage: "command", reason: COMMAND_LOST_REASON });
2466
2504
  }
2505
+ var DISK_FAILURES_LIMIT = 2;
2506
+ async function checkDisk(state, active, container, tag) {
2507
+ const log = logOf(state);
2508
+ const limit = state.config.build?.disk ?? "40g";
2509
+ const runtime = runtimeOf(state);
2510
+ const reading = await measureDisk(runtime, container);
2511
+ if (active.ending) return false;
2512
+ if (exceedsDisk(reading.kib ?? 0, limit)) {
2513
+ log.line(`${tag} the build's volume holds ${reading.kib} KiB, past ${limit} \xB7 ending job as failed`);
2514
+ await endJob(state, active, "failed", { stage: "command", reason: "disk_limit_exceeded" });
2515
+ return true;
2516
+ }
2517
+ if (reading.failure === void 0) {
2518
+ active.diskFailures = 0;
2519
+ return false;
2520
+ }
2521
+ if (await runtime.host.state(container.container) !== "running" || active.ending) return false;
2522
+ active.diskFailures = (active.diskFailures ?? 0) + 1;
2523
+ if (active.diskFailures < DISK_FAILURES_LIMIT) {
2524
+ log.error(`${tag} disk check failed (${active.diskFailures} of ${DISK_FAILURES_LIMIT}): ${reading.failure} \xB7 checking again`);
2525
+ return false;
2526
+ }
2527
+ log.error(`${tag} disk check failed (${active.diskFailures} of ${DISK_FAILURES_LIMIT}): ${reading.failure} \xB7 ending job as failed`);
2528
+ await endJob(state, active, "failed", { stage: "command", reason: "disk_check_failed" });
2529
+ return true;
2530
+ }
2531
+ async function endOrWaitForMicroVm(state, active) {
2532
+ const log = logOf(state);
2533
+ const tag = `#${active.job.item}`;
2534
+ const microvm = active.job.microvm;
2535
+ const runtime = runtimeOf(state);
2536
+ const helper = await runtime.vm.helper.probe();
2537
+ const unit = helper.ok ? await helper.client.state(microvm.buildId).catch(() => void 0) : void 0;
2538
+ if (active.ending) return;
2539
+ if (unit?.active === false) {
2540
+ const killed = await microVmOomKills(runtime, microvm).catch(() => 0);
2541
+ log.line(`${tag} the build's microVM stopped \xB7 ending job as interrupted`);
2542
+ void endJob(state, active, "interrupted", killed > 0 ? { stage: "command" } : { stage: "command", reason: MICROVM_STOPPED_REASON });
2543
+ } else if (active.probeFailing !== true) {
2544
+ active.probeFailing = true;
2545
+ log.error(`${tag} the build's command could not be read from its microVM \xB7 trying again`);
2546
+ }
2547
+ }
2467
2548
  async function probeBuild(state, active) {
2468
2549
  const container = active.job.container;
2469
- if (container === void 0 || !isContainerTracked(active.job) || active.ending || active.probing) return;
2550
+ const tracked = container !== void 0 && isContainerTracked(active.job) ? container : isMicroVmTracked(active.job) ? active.job.microvm : void 0;
2551
+ if (tracked === void 0 || active.ending || active.probing) return;
2470
2552
  active.probing = true;
2471
2553
  const log = logOf(state);
2472
2554
  const tag = `#${active.job.item}`;
2473
2555
  try {
2474
2556
  const runtime = runtimeOf(state);
2475
2557
  for (; ; ) {
2476
- const offset = commandLogOffset(active.dirPath, container.commandLogBase);
2477
- const probe = await probeCommand(runtime, container, offset);
2558
+ const offset = commandLogOffset(active.dirPath, tracked.commandLogBase);
2559
+ const probe = container === void 0 ? await probeMicroVmCommand(runtime, active.job.microvm, offset) : await probeCommand(runtime, container, offset);
2478
2560
  if (active.ending) return;
2479
2561
  if (probe === void 0) {
2562
+ if (container === void 0) {
2563
+ await endOrWaitForMicroVm(state, active);
2564
+ return;
2565
+ }
2480
2566
  const where = await runtime.host.state(container.container);
2481
2567
  if (where === "stopped" || where === "missing") {
2482
2568
  await endStoppedBuild(state, active, where);
@@ -2527,7 +2613,7 @@ async function watchTick(state, active, nowMs, logMtimeMs) {
2527
2613
  const log = logOf(state);
2528
2614
  const tag = `#${active.job.item}`;
2529
2615
  const pid = active.job.pid;
2530
- const started = pid !== void 0 || isContainerTracked(active.job);
2616
+ const started = pid !== void 0 || isTracked(active.job);
2531
2617
  if (pid !== void 0 && !isGroupAlive(pid)) {
2532
2618
  if (active.cmd === null) {
2533
2619
  const recorded = outcomeFromExitCode(active.dirPath);
@@ -2570,13 +2656,12 @@ async function watchTick(state, active, nowMs, logMtimeMs) {
2570
2656
  return;
2571
2657
  }
2572
2658
  if (!started) return;
2573
- if (active.job.container !== void 0) {
2574
- const limit = state.config.build?.disk ?? "40g";
2575
- const used = await measureDisk(runtimeOf(state), active.job.container);
2576
- if (exceedsDisk(used ?? 0, limit)) {
2577
- log.line(`${tag} the build's volume holds ${used} KiB, past ${limit} \xB7 ending job as failed`);
2578
- await endJob(state, active, "failed", { stage: "command", reason: "disk_limit_exceeded" });
2579
- return;
2659
+ if (active.job.container !== void 0 && active.diskChecking !== true) {
2660
+ active.diskChecking = true;
2661
+ try {
2662
+ if (await checkDisk(state, active, active.job.container, tag)) return;
2663
+ } finally {
2664
+ active.diskChecking = false;
2580
2665
  }
2581
2666
  }
2582
2667
  let cpuUsecPerMinute = null;
@@ -2648,7 +2733,7 @@ async function adoptTrackedBuilds(state, jobsDirPath) {
2648
2733
  const attemptLogs = attemptLogsOf(state);
2649
2734
  const runtime = runtimeOf(state);
2650
2735
  for (const { dirPath, job, known } of listTrackedJobs(jobsDirPath)) {
2651
- if (!known) continue;
2736
+ if (!known || job.container === void 0) continue;
2652
2737
  const container = job.container;
2653
2738
  const where = await runtime.host.state(container.container).catch(() => "unknown");
2654
2739
  if (where === "stopped" || where === "missing") {
@@ -2669,6 +2754,116 @@ async function adoptTrackedBuilds(state, jobsDirPath) {
2669
2754
  void probeBuild(state, adopted);
2670
2755
  }
2671
2756
  }
2757
+ async function planMicroVmAdoption(state, jobsDirPath) {
2758
+ const plan = { adopt: [], away: [], unservable: [], unasked: [] };
2759
+ for (const { dirPath, job, known } of listTrackedJobs(jobsDirPath)) {
2760
+ if (!known || !isMicroVmTracked(job)) continue;
2761
+ const verdict = await judgeMicroVmAdoption({ job, runtime: runtimeOf(state), onError: (line) => logOf(state).error(line) });
2762
+ plan[verdict === "adopt" ? "adopt" : verdict === "stopped" ? "away" : verdict === "unasked" ? "unasked" : "unservable"].push({ dirPath, job });
2763
+ }
2764
+ return plan;
2765
+ }
2766
+ async function rejoinMicroVmBuild(state, adopted) {
2767
+ const { job, dirPath } = adopted;
2768
+ const config = state.config;
2769
+ const microvm = job.microvm;
2770
+ const runtime = runtimeOf(state);
2771
+ const tag = `#${job.item}`;
2772
+ const log = logOf(state);
2773
+ const inherited = runtime.vm.fdStore.inherited();
2774
+ const inherit = (socket) => {
2775
+ const fd = inherited.get(fdName(microvm.buildId, socket.replace(/\.sock$/, "")));
2776
+ return fd === void 0 ? void 0 : (server) => runtime.vm.fdStore.listenOn(server, fd);
2777
+ };
2778
+ const tokens = buildTokensFor(state, adopted, job.item);
2779
+ runtime.credentials.set(dirPath, tokens);
2780
+ let servicesServed = false;
2781
+ let leaseServed = false;
2782
+ try {
2783
+ const repositories = repositoriesOf({ repositories: job.repositories, repository: microvm.repository, branch: job.branch });
2784
+ const gitRemote = repositories.length === 0 ? void 0 : gitRemoteFor(runtime, { repositories, tokens, buildDir: microvm.buildDir, log: (line) => log.line(`${tag} ${line}`) });
2785
+ runtime.tunnels.set(microvm.buildId, await startServicesFor(runtime, config, microvm.buildDir, dirPath, job.itemKey, microvm.builtIns === true, gitRemote, "socket", void 0, inherit));
2786
+ servicesServed = true;
2787
+ } catch (err) {
2788
+ log.error(`${tag} the adopted build's services could not be served again: ${describeCallFailure(err)}`);
2789
+ }
2790
+ try {
2791
+ const channel = createLeaseChannel(leaseTimeLeftFor(state, adopted));
2792
+ const served = await serveLease(join(microvm.buildDir, "lease.sock"), { ...channel, accept: (stream) => runtime.leases.get(microvm.buildId)?.accept(stream) }, void 0, inherit("lease.sock"));
2793
+ runtime.leases.set(microvm.buildId, { ...channel, close: served.close });
2794
+ leaseServed = true;
2795
+ } catch (err) {
2796
+ log.error(`${tag} the adopted build's lease stream could not be served again: ${describeCallFailure(err)}`);
2797
+ }
2798
+ try {
2799
+ await tokens.token("read");
2800
+ } catch (err) {
2801
+ log.error(`${tag} could not give the adopted build a read token: ${describeCallFailure(err)}`);
2802
+ }
2803
+ let pushes = Promise.resolve();
2804
+ tokens.onRead((token) => {
2805
+ pushes = pushes.then(async () => {
2806
+ if (vmAlive(runtime, microvm)) await writeGuestGhConfig(runtime, microvm, token);
2807
+ }).catch((err) => log.error(`${tag} the read token could not be written for gh in the guest: ${describeCallFailure(err)}`));
2808
+ });
2809
+ await pushes;
2810
+ return servicesServed && leaseServed;
2811
+ }
2812
+ async function adoptTrackedMicroVms(state, plan) {
2813
+ const log = logOf(state);
2814
+ const attemptLogs = attemptLogsOf(state);
2815
+ const runtime = runtimeOf(state);
2816
+ for (const { dirPath, job } of plan.away) await finishMicroVmWhileAway(state, dirPath, job, MICROVM_STOPPED_REASON, "its unit stopped");
2817
+ for (const { dirPath, job } of plan.unservable) await finishMicroVmWhileAway(state, dirPath, job, MICROVM_SOCKETS_LOST_REASON, "the sockets it reaches the runner on are gone");
2818
+ for (const { dirPath, job } of plan.unasked) await finishMicroVmWhileAway(state, dirPath, job, MICROVM_HELPER_UNREACHABLE_REASON, "the helper did not answer about its unit");
2819
+ for (const { dirPath, job } of plan.adopt) {
2820
+ const adopted = { job, dirPath, lastRenewAtMs: 0, cmd: null, cpuSample: null, ending: false };
2821
+ state.jobs.set(dirPath, adopted);
2822
+ attemptLogs.open(job.item, dirPath);
2823
+ if (!await rejoinMicroVmBuild(state, adopted)) {
2824
+ state.jobs.delete(dirPath);
2825
+ await finishMicroVmWhileAway(state, dirPath, job, MICROVM_SOCKETS_LOST_REASON, "the sockets it reaches the runner on could not be served again");
2826
+ continue;
2827
+ }
2828
+ if (!await renewLeaseIfDue(state, adopted, nowOf(state).getTime())) continue;
2829
+ const sender = await restartSender({ config: state.config, job: adopted.job, jobDir: dirPath, deps: { spawnFn: state.deps.spawnFn, runtime, log, sleep: state.deps.sleep } });
2830
+ adopted.job = sender === void 0 ? { ...adopted.job, sender: void 0 } : { ...adopted.job, sender };
2831
+ writeJobFile(join(dirPath, "job.json"), adopted.job);
2832
+ const line = `adopted job ${job.queue} #${job.item} (claim ${shortClaim(job.claimId)}) \xB7 command tracked in microVM build ${job.microvm.buildId}`;
2833
+ log.line(line);
2834
+ attemptLogs.note(job.item, line);
2835
+ void probeBuild(state, adopted);
2836
+ }
2837
+ await removeUnadoptedSockets(state, new Set(plan.adopt.map(({ job }) => job.microvm.buildId)));
2838
+ }
2839
+ async function removeUnadoptedSockets(state, adopted) {
2840
+ const store = runtimeOf(state).vm.fdStore;
2841
+ for (const [name, fd] of store.inherited()) {
2842
+ const buildId = /^ol-([0-9a-f]+)-/.exec(name)?.[1];
2843
+ if (buildId === void 0 || adopted.has(buildId)) continue;
2844
+ await store.remove(name).catch((err) => logOf(state).error(`the descriptor ${name} could not be taken out of the store: ${describeCallFailure(err)}`));
2845
+ try {
2846
+ closeSync(fd);
2847
+ } catch {
2848
+ }
2849
+ }
2850
+ }
2851
+ async function finishMicroVmWhileAway(state, dirPath, job, reason, why) {
2852
+ const log = logOf(state);
2853
+ const recorded = outcomeFromExitCode(dirPath);
2854
+ let outcome = "interrupted";
2855
+ let ending = { reason };
2856
+ if (recorded !== void 0) {
2857
+ job.exitCode = recorded.code;
2858
+ job.commandEndReason = jobNotEndedByRunnerReason({ code: recorded.code, signal: null }, job);
2859
+ outcome = recorded.outcome === "ok" ? "ok" : "failed";
2860
+ ending = void 0;
2861
+ }
2862
+ const finished = await finishJob(job, dirPath, outcome, state.config, finishDepsOf(state), ending);
2863
+ log.line(
2864
+ `recovered job ${finished.queue} #${finished.item} (claim ${shortClaim(finished.claimId)}) as ${outcome}: ${recorded === void 0 ? `${why}, and no exit code had been read` : `its command recorded exit ${recorded.code}`}`
2865
+ );
2866
+ }
2672
2867
  async function readStoppedBuild(state, dirPath, job) {
2673
2868
  const runtime = runtimeOf(state);
2674
2869
  const container = job.container;
@@ -2728,6 +2923,11 @@ async function finishTrackedWhileAway(state, dirPath, job, where) {
2728
2923
  `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}`}`
2729
2924
  );
2730
2925
  }
2926
+ async function canLeaveBuilds(state) {
2927
+ if (state.deps.leave === void 0 || state.deps.image !== void 0 || !isBuiltinVm(state.config.hooks.provision)) return false;
2928
+ const runtime = runtimeOf(state);
2929
+ return (await runtime.vm.helper.probe()).ok && (await runtime.vm.fdStore.status()).ok;
2930
+ }
2731
2931
  async function logUnusableHelper(config, runtime, log) {
2732
2932
  if (!isBuiltinVm(config.hooks.provision)) return;
2733
2933
  const helper = await runtime.vm.helper.probe();
@@ -2742,7 +2942,7 @@ async function start(configPath, deps = { fetchImpl: fetch }) {
2742
2942
  const outcome = await onStartUnderMarker({
2743
2943
  configPath,
2744
2944
  own: ownVersionOf(deps),
2745
- check: update.check ? () => update.check(configPath) : async () => (await import('./check-ZSYCR5WG.js')).runRunnerCheck(configPath, { fetchImpl: deps.fetchImpl }),
2945
+ check: update.check ? () => update.check(configPath) : async () => (await import('./check-JVBAWIN6.js')).runRunnerCheck(configPath, { fetchImpl: deps.fetchImpl }),
2746
2946
  now: () => (deps.now ?? (() => /* @__PURE__ */ new Date()))().getTime(),
2747
2947
  log
2748
2948
  });
@@ -2780,9 +2980,29 @@ async function start(configPath, deps = { fetchImpl: fetch }) {
2780
2980
  }
2781
2981
  const state = { config, jobs: /* @__PURE__ */ new Map(), deps, configMtimeMs };
2782
2982
  log.line(`runner ${config.host} started \xB7 config ${configPath} \xB7 ${describeConfig(config)}`);
2983
+ const leavesBuilds = await canLeaveBuilds(state);
2984
+ let sigusr2Target;
2985
+ let sigusr2Early = false;
2986
+ const forwardSigusr2 = () => {
2987
+ if (sigusr2Target === void 0) sigusr2Early = true;
2988
+ else sigusr2Target();
2989
+ };
2990
+ if (leavesBuilds) {
2991
+ process.on("SIGUSR2", forwardSigusr2);
2992
+ writePidFile(pidFilePath(configPath), { ...self, leaves: true });
2993
+ }
2783
2994
  const nowMs = () => deps.now ? deps.now().getTime() : Date.now();
2784
2995
  await logUnusableHelper(config, runtimeOf(state), log);
2785
- const runningMicroVms = await stopOrphanedMicroVms({ jobs: unfinishedJobs(jobsDir(configPath)), runtime: runtimeOf(state), onError: (line) => log.error(line) }).catch((err) => {
2996
+ const microVmPlan = await planMicroVmAdoption(state, jobsDir(configPath)).catch((err) => {
2997
+ log.error(`the microVM builds an earlier runner left could not be told apart: ${describeCallFailure(err)}`);
2998
+ return { adopt: [], away: [], unservable: [], unasked: [] };
2999
+ });
3000
+ const runningMicroVms = await stopOrphanedMicroVms({
3001
+ jobs: unfinishedJobs(jobsDir(configPath)),
3002
+ runtime: runtimeOf(state),
3003
+ onError: (line) => log.error(line),
3004
+ keep: new Set(microVmPlan.adopt.map(({ job }) => job.microvm.buildId))
3005
+ }).catch((err) => {
2786
3006
  log.error(`a microVM an earlier runner left could not be stopped: ${describeCallFailure(err)}`);
2787
3007
  return [];
2788
3008
  });
@@ -2806,6 +3026,7 @@ async function start(configPath, deps = { fetchImpl: fetch }) {
2806
3026
  attemptLogs.note(job.item, adoptedLine);
2807
3027
  }
2808
3028
  await adoptTrackedBuilds(state, jobsDir(configPath));
3029
+ await adoptTrackedMicroVms(state, microVmPlan);
2809
3030
  for (const { job, released } of await retryFailedReleases(jobsDir(configPath), config, {
2810
3031
  fetchImpl: deps.fetchImpl,
2811
3032
  now: deps.now,
@@ -2850,9 +3071,13 @@ async function start(configPath, deps = { fetchImpl: fetch }) {
2850
3071
  const probeTimer = setInterval(() => {
2851
3072
  for (const active of state.jobs.values()) void probeBuild(state, active);
2852
3073
  }, deps.probeIntervalMs ?? PROBE_INTERVAL_MS);
2853
- const onSigterm = () => deps.image === void 0 ? void drain() : leave();
3074
+ const onSigterm = () => {
3075
+ if (deps.image !== void 0) leave();
3076
+ else if (leavesBuilds && buildBlockers(runningBuilds(state)).length === 0) leave();
3077
+ else void drain();
3078
+ };
2854
3079
  const onSigusr1 = () => void stopNow();
2855
- const onSigusr2 = () => drainKeepingUp();
3080
+ const onSigusr2 = () => deps.image === void 0 ? void drain() : drainKeepingUp();
2856
3081
  const onSigint = () => {
2857
3082
  interrupts++;
2858
3083
  if (interrupts === 1) {
@@ -2866,6 +3091,7 @@ async function start(configPath, deps = { fetchImpl: fetch }) {
2866
3091
  process.off("SIGTERM", onSigterm);
2867
3092
  process.off("SIGUSR1", onSigusr1);
2868
3093
  process.off("SIGUSR2", onSigusr2);
3094
+ process.off("SIGUSR2", forwardSigusr2);
2869
3095
  process.off("SIGINT", onSigint);
2870
3096
  };
2871
3097
  const stopIntake = () => {
@@ -2923,7 +3149,7 @@ async function start(configPath, deps = { fetchImpl: fetch }) {
2923
3149
  log.line(`stopping: leaving ${state.jobs.size} running build(s) for the next runner to adopt`);
2924
3150
  state.draining = true;
2925
3151
  finish2();
2926
- deps.image?.exit?.(0);
3152
+ (deps.image?.exit ?? deps.leave?.exit)?.(0);
2927
3153
  };
2928
3154
  const stopNow = async () => {
2929
3155
  if (stopping) return;
@@ -2945,7 +3171,10 @@ async function start(configPath, deps = { fetchImpl: fetch }) {
2945
3171
  const handle = { state, drain, stopNow, close };
2946
3172
  process.on("SIGTERM", onSigterm);
2947
3173
  process.on("SIGUSR1", onSigusr1);
2948
- if (deps.image !== void 0) process.on("SIGUSR2", onSigusr2);
3174
+ if (leavesBuilds) {
3175
+ sigusr2Target = onSigusr2;
3176
+ if (sigusr2Early) onSigusr2();
3177
+ } else if (deps.image !== void 0) process.on("SIGUSR2", onSigusr2);
2949
3178
  process.on("SIGINT", onSigint);
2950
3179
  deps.onReady?.();
2951
3180
  await tick(state);
@@ -2971,7 +3200,7 @@ async function requestStop(configPath, stopNow = false, pollMs = 5e3, options =
2971
3200
  `);
2972
3201
  return;
2973
3202
  }
2974
- const signal = stopNow ? "SIGUSR1" : options.image === true ? "SIGUSR2" : "SIGTERM";
3203
+ const signal = stopNow ? "SIGUSR1" : options.image === true || entry.leaves === true ? "SIGUSR2" : "SIGTERM";
2975
3204
  try {
2976
3205
  kill(entry.pid, signal);
2977
3206
  } catch (err) {
@@ -2986,7 +3215,7 @@ async function requestStop(configPath, stopNow = false, pollMs = 5e3, options =
2986
3215
  `);
2987
3216
  return;
2988
3217
  }
2989
- if (signal === "SIGUSR2") {
3218
+ if (signal === "SIGUSR2" && entry.leaves !== true) {
2990
3219
  process.stdout.write(
2991
3220
  `sent SIGUSR2 to runner pid ${entry.pid}: it takes no more work and keeps managing its builds.
2992
3221
  The container stays up. Run \`outerlayer runner status\` to see what is left, then \`docker stop\` the container once nothing is.
@@ -2996,7 +3225,7 @@ The container stays up. Run \`outerlayer runner status\` to see what is left, th
2996
3225
  }
2997
3226
  process.stdout.write(
2998
3227
  stopNow ? `sent SIGUSR1 to runner pid ${entry.pid}, stopping now
2999
- ` : `sent SIGTERM to runner pid ${entry.pid}, draining
3228
+ ` : `sent ${signal} to runner pid ${entry.pid}, draining
3000
3229
  `
3001
3230
  );
3002
3231
  while (isSameProcess(entry.pid, entry.start)) {
@@ -1,10 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- import { sharedSkillNames } from './chunk-VPEGE3QZ.js';
3
+ import { sharedSkillNames } from './chunk-6TRNCUM4.js';
4
4
  import { GOVERNANCE_VARIABLE, versionAtLeast, MINIMUM_CLAUDE_VERSION, SHIM_SCRIPT, CONTROL_PLANE_DIR_VARIABLE } from './chunk-HCVLP7XA.js';
5
5
  export { MINIMUM_CLAUDE_VERSION } from './chunk-HCVLP7XA.js';
6
6
  import { findGitDirs } from './chunk-BJ3KTMDH.js';
7
- import './chunk-HBISTTG3.js';
7
+ import { setupMachineSkills } from './chunk-BM7EC3OZ.js';
8
+ import './chunk-7OUOLGUV.js';
8
9
  import './chunk-RKOTJQVG.js';
9
10
  import './chunk-QZBXLLTV.js';
10
11
  import { homeFromEnv } from './chunk-VNVZDWO3.js';
@@ -210,12 +211,15 @@ async function runClaudeLauncher(args, deps = {}) {
210
211
  const cwd = deps.cwd ?? process.cwd();
211
212
  const env = deps.env ?? process.env;
212
213
  const stderr = deps.stderr ?? ((text) => void process.stderr.write(text));
214
+ const home = homeFromEnv(env);
215
+ const installed = setupMachineSkills(home);
216
+ for (const failure of installed.failures) stderr(`outerlayer claude: could not write a maintained skill: ${failure}
217
+ `);
213
218
  const claude = findClaude(env.PATH);
214
219
  if (claude === void 0) {
215
220
  stderr("outerlayer claude: no claude program found on PATH\n");
216
221
  return 127;
217
222
  }
218
- const home = homeFromEnv(env);
219
223
  try {
220
224
  removeStaleOwnSkillsDirs(home);
221
225
  } catch {
@@ -236,7 +240,7 @@ async function runClaudeLauncher(args, deps = {}) {
236
240
  return 1;
237
241
  }
238
242
  } else {
239
- const { resolveLaunchPlane } = await import('./control-plane-launch-JIQOE6BP.js');
243
+ const { resolveLaunchPlane } = await import('./control-plane-launch-DBQA3KLJ.js');
240
244
  const plane = env[GOVERNANCE_VARIABLE] === "self" ? { kind: "none" } : await resolveLaunchPlane({ cwd, env, home, fetchImpl: deps.fetchImpl, stderr });
241
245
  if (plane.kind === "none") {
242
246
  const own2 = ownSkillsDirFor(cwd, home, [], stderr);