@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
package/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # @outerlayer/cli
2
2
 
3
+ ## 0.27.0
4
+
5
+ ### Minor Changes
6
+
7
+ - a9019cb: `outerlayer init` installs the five maintained skills once per machine, in `~/.agents/skills/`, with a link to each in `~/.claude/skills/`. Each later `outerlayer` command keeps them at its own version. A repository in the `AGENTS.md` layout carries none of them. After every machine and runner host runs this version and each laptop runs `outerlayer init --local`, run `outerlayer init --update` in a repository to remove its copies. See the upgrade page.
8
+
9
+ ### Patch Changes
10
+
11
+ - d835cd7: A microVM build keeps running when its runner restarts, crashes or updates. The runner keeps the build's sockets in its systemd service's file descriptor store, and takes the build over when it starts again. This needs the runner installed as a service with `outerlayer runner install --service`, the root helper, and systemd 254 or later. On an older systemd, a runner restart still ends the build.
12
+ - 2c7ce38: A microVM build no longer sees the CPU's virtualization features, so it cannot start its own virtual machines. A root helper from an earlier release does not apply this, so run `outerlayer runner install --service --replace` to install the new helper. On a runner host, `outerlayer doctor` warns about nested virtualization, VMScape and an old runc.
13
+ - 9c3a93c: A microVM build's guest now has `/dev/fd`, `/dev/stdin`, `/dev/stdout` and `/dev/stderr`, so bash process substitution such as `diff <(…) <(…)` works in a build.
14
+ - e4dce78: The README says what `outerlayer runner init` writes on each machine, now that the microVM guest files and the root helper are published.
15
+ - fd0850c: The runner image's end-to-end test gives a restarted runner's build up to 15 seconds to show that its command still runs.
16
+ - b4381a6: The runner's tunnel refuses five more kinds of address. A container build's disk limit holds when `du` cannot measure its volume: two failed checks in a row end the build with `disk_check_failed`.
17
+ - 7663eee: `outerlayer work status` names the repository when a work item's open pull request is in a repository the factory no longer connects.
18
+
3
19
  ## 0.26.4
4
20
 
5
21
  ### Patch Changes
package/README.md CHANGED
@@ -216,14 +216,17 @@ content:
216
216
  from registries while the image builds, with the host's own `docker
217
217
  login`. The tools layer also downloads the pinned `gh` and `tini` releases
218
218
  from GitHub, checking each against a digest. The image build runs outside
219
- any work item and sends nothing to your factory.
219
+ any work item and sends nothing to your factory. `outerlayer runner start`
220
+ builds each build's image the same way, before the build starts. That image
221
+ build uses the host's network: the build's tunnel does not carry it, and
222
+ `runner.build.allowHosts` does not limit it.
220
223
  - **`outerlayer runner start` with `builtin:vm` hooks** runs each build in its
221
- own Firecracker microVM, which has no network device. The guest kernel and
222
- helper are not published yet, so no host is offered `builtin:vm` and
223
- `outerlayer doctor` fails it. Once they are, the first microVM
224
- build downloads a pinned Firecracker release, a guest kernel and a guest
225
- helper from github.com, and checks each against a sha256 pinned in the CLI;
226
- the requests send nothing about the host. The build reaches the network only
224
+ own Firecracker microVM, which has no network device. The first microVM
225
+ build downloads a pinned Firecracker release from github.com. It also
226
+ downloads a guest kernel and a guest helper from the releases of the public
227
+ repository `outerlayerai/guest-assets` on github.com. The runner checks each
228
+ file against a sha256 pinned in the CLI, and the requests send nothing about
229
+ the host. The microVM reaches the network only
227
230
  through the same tunnel a container build uses, which the runner serves on
228
231
  Unix sockets and the guest reaches over vsock. In a build with Docker, the
229
232
  guest forwards the proxy connections of containers and `docker build` steps
@@ -255,7 +258,7 @@ content:
255
258
  stream into `docker exec -i` running `outerlayer receive-files` as that
256
259
  unprivileged user, so nothing from the runner's disk is mounted into the
257
260
  container and the agent cannot change what it reads. (A build an older
258
- runner started keeps its mounted folder and its Unix sockets until it ends.) The build
261
+ runner started keeps its mounted folder and its Unix sockets until it ends.) The build's container
259
262
  reaches the network only through a tunnel the runner serves. The tunnel refuses loopback, private, link-local,
260
263
  shared-address, multicast and unique-local addresses and the host's own,
261
264
  and it terminates no TLS. The tunnel also serves the build a local git
@@ -461,7 +464,7 @@ before it gives up.
461
464
  | `outerlayer work claim --item <n>\|--issue <n> --kind implement\|amend [--repo] [--host] [--seconds]` | Records a lease for this host (default: its own hostname), so two hosts never build the same item at once. The lease belongs to the runner key that claimed it: a live lease held by a different key is refused, even under the same host name, and claiming again with the same key extends it. The first claim creates the host key (`host-key.pem` beside the config file) and binds the runner key to it; from then on every request made with that key is signed. Lease length defaults to 900 seconds, the server's own cap. |
462
465
  | `outerlayer work renew --item <n>\|--issue <n> [--repo] [--host] [--seconds]` | Extends this host's own live lease. Refused if the lease has expired, or if a different runner key holds it (403 `claim_not_held`). |
463
466
  | `outerlayer work release --item <n>\|--issue <n> [--repo] [--host] [--session-id] [--outcome]` | Marks the lease this API key holds released, optionally recording how the attempt ended. Releasing an already-released lease is a no-op that returns the recorded release time and outcome. Run inside a session with no `--host`, it releases that session's own lease instead (`--session-id` names the session when it cannot be detected), so the item's review opens without ending the session. |
464
- | `outerlayer runner init [--vm\|--no-vm] [--factory <org>/<factory>] [--app-id <id>] [--config <path>]` | Writes a `runner` block with defaults, keeping every key the config already had. With no `apiKey` in the config and a saved login, it prints the API keys page of the factory `--factory` or `--app-id` names (the only factory the login can use, when neither is given), asks you to paste a runner key made with the Runner preset, and checks it before anything else changes. With no terminal it stops and says what to put in the config. It names `builtin:vm` on a Linux machine where the user can use `/dev/kvm` and Docker Engine can build images once the guest kernel and helper are published (not yet), `builtin:container` on that machine until then, `builtin:container` on any other machine with Docker Engine, and `builtin:process` on one without, and writes no hook script. Refuses rather than overwriting an existing block. On macOS it asks whether to create a Linux VM with Lima, installs Docker Engine and the runner in it, and starts both at login; `--vm` answers yes and `--no-vm` answers no, and a run with no terminal answers no. The runner key and the host key then live only in the VM: the Mac's config loses `apiKey` and `runner` and gains a `runnerVm` note, which `outerlayer doctor` reads. |
467
+ | `outerlayer runner init [--vm\|--no-vm] [--allow-process-builds] [--factory <org>/<factory>] [--app-id <id>] [--config <path>]` | Writes a `runner` block with defaults, keeping every key the config already had. With no `apiKey` in the config and a saved login, it prints the API keys page of the factory `--factory` or `--app-id` names (the only factory the login can use, when neither is given), asks you to paste a runner key made with the Runner preset, and checks it before anything else changes. With no terminal it stops and says what to put in the config. It names `builtin:vm` on x86_64 Linux, not WSL2, where the user can open `/dev/kvm` and Docker Engine 26 or later runs. It names `builtin:container` on any other Linux or WSL2 machine with Docker Engine 26 or later. On a machine with neither, it refuses unless `--allow-process-builds` is given, and then names `builtin:process`. It writes no hook script. Refuses rather than overwriting an existing block. On macOS it asks whether to create a Linux VM with Lima, installs Docker Engine and the runner in it, and starts both at login; `--vm` answers yes, and `--no-vm` answers no, which names `builtin:process`. The runner key and the host key then live only in the VM: the Mac's config loses `apiKey` and `runner` and gains a `runnerVm` note, which `outerlayer doctor` reads. With neither flag and no terminal, it refuses. |
465
468
  | `outerlayer runner check [--config <path>]` | Validates the config, the hooks and the key, prints the settings the runner would use, and exits non-zero on a problem. Takes no work and writes no pid file. |
466
469
  | `outerlayer runner start [--config <path>]` | Runs the loop: claim work, run it, sync, clean up, report, release with an outcome. Repeat. Reads the `runner` block from the config file (default `~/.outerlayer/config.json`). Refuses to start on a bad config, an unrunnable hook, or a refused key. |
467
470
  | `outerlayer runner image --repository <owner/name> [--remote <url>] [--config <path>] [--json]` | Builds the image a repository's builds run in. Fetches the head of the default branch into the runner's cache, builds its `.devcontainer/devcontainer.json` with the devcontainer CLI, ignores every setting that reaches the host or widens the container's privileges, and adds this CLI and a pinned Claude Code. Reuses the image until the recipe or the tools change, keeps the newest three, and writes `recipe.json` beside the image record. Refuses a Compose recipe (`recipe_needs_docker`) and a repository with only named configurations (`recipe_ambiguous`). |
@@ -1,18 +1,18 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- export { keepHandWritten, runAgentSetup } from './chunk-KNBWOL2B.js';
4
- import './chunk-WX2WEERU.js';
5
- import './chunk-VJBFQP3H.js';
6
- import './chunk-4UFSTKE5.js';
7
- import './chunk-MQ743JX3.js';
8
- import './chunk-MCSINUVC.js';
3
+ export { keepHandWritten, runAgentSetup } from './chunk-FP23SVDD.js';
4
+ import './chunk-WUL2OP5A.js';
5
+ import './chunk-XZ6Q6ZNR.js';
6
+ import './chunk-LOKSLO25.js';
7
+ import './chunk-BPKO76QR.js';
8
+ import './chunk-MHX7YX7Y.js';
9
+ import './chunk-BWH36FOC.js';
9
10
  import './chunk-ZJSNLVCE.js';
10
11
  import './chunk-BJ3KTMDH.js';
12
+ import './chunk-7OUOLGUV.js';
11
13
  import './chunk-KT4CN6OU.js';
12
14
  import './chunk-ANPNT6PM.js';
13
15
  import './chunk-PJDQPF5N.js';
14
- import './chunk-JKNYTNYD.js';
15
- import './chunk-HBISTTG3.js';
16
16
  import './chunk-CRB3POP4.js';
17
17
  import './chunk-VWFOKRYS.js';
18
18
  import './chunk-YQR6E6OV.js';
@@ -21,9 +21,9 @@ import './chunk-RKOTJQVG.js';
21
21
  import './chunk-QZBXLLTV.js';
22
22
  import './chunk-VNVZDWO3.js';
23
23
  import './chunk-RA3O54FC.js';
24
- import './chunk-GWRDL75D.js';
25
- import './chunk-F3BBEBPL.js';
26
- import './chunk-2K3TCQHW.js';
24
+ import './chunk-Y4OKEFAX.js';
25
+ import './chunk-RHAGBUL5.js';
26
+ import './chunk-2I3LIRZP.js';
27
27
  import './chunk-JGD5TWQ2.js';
28
28
  import './chunk-Z5GEFMRW.js';
29
29
 
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- export { BRIDGE_ADDRESS, BRIDGE_CIDR, CONTAINER_NO_PROXY, CONTAINER_PROXY, TUNNEL_PORT, dockerClientConfig, startBridgeForward, writeDockerClientConfig } from './chunk-UNF7MWEL.js';
4
- import './chunk-2K3TCQHW.js';
3
+ export { BRIDGE_ADDRESS, BRIDGE_CIDR, CONTAINER_NO_PROXY, CONTAINER_PROXY, TUNNEL_PORT, dockerClientConfig, startBridgeForward, writeDockerClientConfig } from './chunk-XS3FDWSZ.js';
4
+ import './chunk-2I3LIRZP.js';
5
5
  import './chunk-JGD5TWQ2.js';
6
6
  import './chunk-Z5GEFMRW.js';
7
7
 
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- export { buildRepositoryImage } from './chunk-GFHLEVUE.js';
4
- import './chunk-IZAOO5AR.js';
3
+ export { buildRepositoryImage } from './chunk-24ID7EQ2.js';
4
+ import './chunk-D7ECMB7B.js';
5
5
  import './chunk-JBJTC3UU.js';
6
6
  export { RecipeError } from './chunk-B6K7R5UY.js';
7
- import './chunk-F3BBEBPL.js';
8
- import './chunk-2K3TCQHW.js';
7
+ import './chunk-RHAGBUL5.js';
8
+ import './chunk-2I3LIRZP.js';
9
9
  import './chunk-JGD5TWQ2.js';
10
10
  import './chunk-Z5GEFMRW.js';
11
11
 
@@ -1 +1 @@
1
- {"id":"2026-10-16T21:01:44.837Z+065fff7","root":"/home/runner/_work/outerlayer/outerlayer","pkg":"outerlayer"}
1
+ {"id":"2026-10-17T00:24:12.932Z+fca5378","root":"/home/runner/_work/outerlayer/outerlayer","pkg":"outerlayer"}
@@ -1,13 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- export { MIN_BUILDX_VERSION, builderName, builderSpec, buildxState, ensureBuilder, ensureFailureCount, isOutOfMemory, proxyEnvironment, trackImageBuild } from './chunk-QUP6CVBQ.js';
3
+ export { MIN_BUILDX_VERSION, builderName, builderSpec, buildxState, ensureBuilder, ensureFailureCount, isOutOfMemory, proxyEnvironment, trackImageBuild } from './chunk-JH2GQU5T.js';
4
4
  import './chunk-B6K7R5UY.js';
5
- import './chunk-7RQ73DLO.js';
5
+ import './chunk-YJLHUKYS.js';
6
6
  import './chunk-QE3UQAHK.js';
7
7
  import './chunk-QZBXLLTV.js';
8
- import './chunk-GWRDL75D.js';
9
- import './chunk-F3BBEBPL.js';
10
- import './chunk-2K3TCQHW.js';
8
+ import './chunk-Y4OKEFAX.js';
9
+ import './chunk-RHAGBUL5.js';
10
+ import './chunk-2I3LIRZP.js';
11
11
  import './chunk-JGD5TWQ2.js';
12
12
  import './chunk-Z5GEFMRW.js';
13
13
 
@@ -1,64 +1,65 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- import { probeCredentials } from './chunk-V3YJFEZA.js';
4
- import './chunk-27RDUTQH.js';
5
- import './chunk-OFTFW7TH.js';
3
+ import { probeCredentials } from './chunk-ZXRWMOJ3.js';
4
+ import './chunk-RK7FWC6B.js';
5
+ import './chunk-RHGF73IU.js';
6
6
  import './chunk-ZVB57WI6.js';
7
- import './chunk-NWPRNKZ5.js';
7
+ import './chunk-ABXQBEYK.js';
8
8
  import './chunk-3JVYMD4G.js';
9
9
  import './chunk-YETTWTAY.js';
10
10
  import './chunk-TPKYTORP.js';
11
- import './chunk-Q2SVLBMI.js';
12
- import './chunk-MWPBF2XG.js';
13
- import './chunk-UNF7MWEL.js';
14
- import './chunk-VQ7OAG6H.js';
15
- import './chunk-WK35HB56.js';
11
+ import './chunk-Y7ZCHBYB.js';
12
+ import './chunk-6KKM7VGZ.js';
13
+ import './chunk-XS3FDWSZ.js';
14
+ import './chunk-VS27BP3E.js';
15
+ import './chunk-O2IJTY7L.js';
16
16
  import './chunk-ESVW4MJY.js';
17
- import { placeholderQueues, placeholderProblem, permissionModeProblems, permissionModeProblem, needsClaudeCredential, claudeCredentialCheck, claimPermissionProblem, formerSkillQueues, formerSkillProblem } from './chunk-UOGQZQXN.js';
18
- import './chunk-7Z2WMXM5.js';
17
+ import { placeholderQueues, placeholderProblem, permissionModeProblems, permissionModeProblem, needsClaudeCredential, claudeCredentialCheck, claimPermissionProblem, formerSkillQueues, formerSkillProblem } from './chunk-WVDP6HVZ.js';
18
+ import './chunk-MATUHHN4.js';
19
19
  import './chunk-YYYRBYWG.js';
20
20
  import './chunk-TBT347UY.js';
21
- import './chunk-OQUD3UYF.js';
22
- import './chunk-EHBDYLM6.js';
23
- import './chunk-SBW4K3XZ.js';
24
- import './chunk-2J46BQ5Y.js';
21
+ import './chunk-QIVOTQOQ.js';
22
+ import './chunk-UQYBCJ2L.js';
23
+ import './chunk-YF56R4DV.js';
24
+ import './chunk-WSYXLLA5.js';
25
+ import './chunk-M3MHHIUW.js';
25
26
  import './chunk-TQMI335S.js';
26
- import './chunk-VGHKTSHU.js';
27
- import './chunk-6SPXKRQR.js';
28
- import { parseRunnerEnv } from './chunk-6MZSX4M7.js';
29
- import './chunk-GFHLEVUE.js';
30
- import './chunk-IZAOO5AR.js';
27
+ import './chunk-GFUE5TKO.js';
28
+ import './chunk-7AQ5AM7U.js';
29
+ import './chunk-YB3CSYXX.js';
30
+ import { parseRunnerEnv } from './chunk-STSLH5EJ.js';
31
+ import './chunk-24ID7EQ2.js';
32
+ import './chunk-D7ECMB7B.js';
31
33
  import './chunk-JBJTC3UU.js';
32
34
  import './chunk-OGFGZQA3.js';
33
- import './chunk-QUP6CVBQ.js';
35
+ import './chunk-JH2GQU5T.js';
34
36
  import './chunk-B6K7R5UY.js';
35
- import './chunk-MXTN35XN.js';
36
- import './chunk-XLC7KEUV.js';
37
- import './chunk-OZKEEXVH.js';
38
- import './chunk-6CGXSMKX.js';
39
- import './chunk-H57M3HC7.js';
40
- import './chunk-PZA6ZFYW.js';
37
+ import './chunk-XBPUTQ7E.js';
38
+ import './chunk-GLUKDQS4.js';
39
+ import './chunk-55GNQPO6.js';
40
+ import './chunk-2F762IXY.js';
41
+ import './chunk-N4D5TMVU.js';
41
42
  import './chunk-BUP5N3X6.js';
43
+ import './chunk-LOKSLO25.js';
42
44
  import './chunk-6BLXUD56.js';
43
45
  import './chunk-G2JWZDFA.js';
44
- import { loadRunnerConfig, RunnerConfigError, checkHooks } from './chunk-JWRX5ZTT.js';
46
+ import { loadRunnerConfig, RunnerConfigError, checkHooks } from './chunk-EPPIISRD.js';
45
47
  import './chunk-UXYLWVTQ.js';
46
- import './chunk-7RQ73DLO.js';
47
- import './chunk-4UFSTKE5.js';
48
- import './chunk-MQ743JX3.js';
49
- import './chunk-MCSINUVC.js';
50
- import './chunk-UZEQVPKF.js';
48
+ import './chunk-YJLHUKYS.js';
49
+ import './chunk-BPKO76QR.js';
50
+ import './chunk-MHX7YX7Y.js';
51
+ import './chunk-BWH36FOC.js';
52
+ import './chunk-DJHNEGNM.js';
51
53
  import './chunk-ZJSNLVCE.js';
52
54
  import './chunk-2O5KA67H.js';
53
55
  import './chunk-BKO6JEZI.js';
54
- import './chunk-VPEGE3QZ.js';
56
+ import './chunk-6TRNCUM4.js';
55
57
  import './chunk-HCVLP7XA.js';
56
58
  import './chunk-BJ3KTMDH.js';
59
+ import './chunk-7OUOLGUV.js';
57
60
  import './chunk-QE3UQAHK.js';
58
61
  import './chunk-ANPNT6PM.js';
59
62
  import './chunk-PJDQPF5N.js';
60
- import './chunk-JKNYTNYD.js';
61
- import './chunk-HBISTTG3.js';
62
63
  import './chunk-T2Q7ISPE.js';
63
64
  import './chunk-WZBHBH3J.js';
64
65
  import './chunk-CRB3POP4.js';
@@ -69,9 +70,9 @@ import './chunk-RKOTJQVG.js';
69
70
  import './chunk-QZBXLLTV.js';
70
71
  import './chunk-VNVZDWO3.js';
71
72
  import './chunk-RA3O54FC.js';
72
- import './chunk-GWRDL75D.js';
73
- import './chunk-F3BBEBPL.js';
74
- import './chunk-2K3TCQHW.js';
73
+ import './chunk-Y4OKEFAX.js';
74
+ import './chunk-RHAGBUL5.js';
75
+ import './chunk-2I3LIRZP.js';
75
76
  import './chunk-JGD5TWQ2.js';
76
77
  import './chunk-Z5GEFMRW.js';
77
78
  import { readFileSync } from 'fs';
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- import { claimPidFile, RunnerAlreadyRunningError, releasePidFile } from './chunk-IZAOO5AR.js';
3
+ import { claimPidFile, RunnerAlreadyRunningError, releasePidFile } from './chunk-D7ECMB7B.js';
4
4
  import { startTimeOf } from './chunk-JBJTC3UU.js';
5
5
  import { CLAUDE_CODE_VERSION, NODE_IMAGE, DEFAULT_BASE_IMAGE, RecipeError, locateRecipe, readRecipe, assertBuildable, TINI_VERSION, GH_VERSION, writeSanitizedRecipe, classifyProperties, GH_SHA256, TINI_SHA256, SANITIZED_NAME } from './chunk-B6K7R5UY.js';
6
- import { RUNNER_DIR_MODE, repoCacheDir, imagesDir, attemptStamp, runnerDir, rootfsPathFor, RUNNER_FILE_MODE } from './chunk-F3BBEBPL.js';
7
- import { withGitConfig, tokenHeader, PROXY_WARNING_FLAG } from './chunk-2K3TCQHW.js';
6
+ import { RUNNER_DIR_MODE, repoCacheDir, imagesDir, attemptStamp, runnerDir, rootfsPathFor, RUNNER_FILE_MODE } from './chunk-RHAGBUL5.js';
7
+ import { withGitConfig, tokenHeader, PROXY_WARNING_FLAG } from './chunk-2I3LIRZP.js';
8
8
  import { RELAY_ID, RELAY_USER } from './chunk-JGD5TWQ2.js';
9
9
  import { mkdirSync, rmSync, existsSync, mkdtempSync, cpSync, readFileSync, writeFileSync, readdirSync, lstatSync, readlinkSync } from 'fs';
10
10
  import { tmpdir } from 'os';
@@ -184,6 +184,8 @@ function toolsDockerfile(parts) {
184
184
  `RUN npm install --global --prefix /out/opt/outerlayer/claude @anthropic-ai/claude-code@${version}`,
185
185
  "COPY dist /out/opt/outerlayer/cli/dist",
186
186
  "COPY package.json /out/opt/outerlayer/cli/package.json",
187
+ // `outerlayer claude` installs the maintained skills from this pack into the build's home folder before the agent starts.
188
+ "COPY skill-pack /out/opt/outerlayer/cli/skill-pack",
187
189
  "RUN mkdir -p /out/opt/outerlayer/bin /out/usr/local/bin /out/etc/claude-code /out/build \\",
188
190
  " && cp /usr/local/bin/node /out/opt/outerlayer/bin/node \\",
189
191
  ` && printf '%s\\n' '#!/bin/sh' 'exec /opt/outerlayer/bin/node /opt/outerlayer/cli/dist/index.js "$@"' > /out/usr/local/bin/outerlayer \\`,
@@ -443,6 +445,7 @@ async function buildRepositoryImage(opts, ports) {
443
445
  try {
444
446
  cpSync(opts.cli.distDir, join(context, "dist"), { recursive: true });
445
447
  cpSync(join(opts.cli.distDir, "..", "package.json"), join(context, "package.json"));
448
+ cpSync(join(opts.cli.distDir, "..", "skill-pack"), join(context, "skill-pack"), { recursive: true });
446
449
  if (opts.guestHelper !== void 0) {
447
450
  mkdirSync(join(context, "guest"));
448
451
  cpSync(opts.guestHelper.path, join(context, "guest", "outerlayer-guest"));
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- import { fetchFactories, selectFactory, saveFactoryConnection, autoConnectedLine } from './chunk-PZA6ZFYW.js';
4
- import { resolveGatewayCredentials } from './chunk-MCSINUVC.js';
3
+ import { fetchFactories, selectFactory, saveFactoryConnection, autoConnectedLine } from './chunk-N4D5TMVU.js';
4
+ import { resolveGatewayCredentials } from './chunk-BWH36FOC.js';
5
5
  import { findGitDirs, readRepoConnection } from './chunk-BJ3KTMDH.js';
6
6
  import { resolveRepoIdentity } from './chunk-QE3UQAHK.js';
7
7
  import { cliInvocation } from './chunk-CRB3POP4.js';
@@ -270,12 +270,16 @@ async function startDestination(input) {
270
270
  if (Buffer.byteLength(input.socketPath) > MAX_SOCKET_PATH_BYTES) {
271
271
  throw new Error(`${input.socketPath} is too long for a Unix socket; keep the runner's config file nearer the root of the filesystem`);
272
272
  }
273
- if (existsSync(input.socketPath)) unlinkSync(input.socketPath);
274
- await new Promise((resolve, reject) => {
275
- server.once("error", reject);
276
- server.listen(input.socketPath, () => resolve());
277
- });
278
- chmodSync(input.socketPath, 438);
273
+ if (input.listenOn !== void 0) {
274
+ await input.listenOn(server);
275
+ } else {
276
+ if (existsSync(input.socketPath)) unlinkSync(input.socketPath);
277
+ await new Promise((resolve, reject) => {
278
+ server.once("error", reject);
279
+ server.listen(input.socketPath, () => resolve());
280
+ });
281
+ chmodSync(input.socketPath, 438);
282
+ }
279
283
  served.socketPath = input.socketPath;
280
284
  }
281
285
  if (input.port !== void 0) {
@@ -292,18 +296,26 @@ async function startDestination(input) {
292
296
  for (const socket of sockets) socket.destroy();
293
297
  await Promise.all(listeners.map((listener) => new Promise((resolve) => listener.close(() => resolve()))));
294
298
  };
295
- return { ...served, acceptStream: (stream) => void server.emit("connection", stream), close };
299
+ return { ...served, acceptStream: (stream) => void server.emit("connection", stream), listener: () => input.socketPath === void 0 ? void 0 : server, close };
296
300
  }
297
301
  async function startBuildServices(input) {
298
302
  const streamed = input.serve === "stream";
303
+ const listenOnFor = (socket) => {
304
+ const listenOn = input.inherit?.(socket);
305
+ return listenOn === void 0 ? {} : { listenOn };
306
+ };
307
+ const listeners = {};
299
308
  const tunnel = await input.startTunnel(join(input.buildDir, "tunnel.sock"), {
300
309
  ...input.tunnelOptions,
301
310
  ...streamed ? { socket: false } : {},
311
+ ...streamed ? {} : listenOnFor("tunnel.sock"),
302
312
  ...input.allowHosts === void 0 ? {} : { allowHosts: input.allowHosts },
303
313
  recordPath: join(input.jobDir, "tunnel.json")
304
314
  });
305
315
  const servers = [];
306
316
  const targets = /* @__PURE__ */ new Map();
317
+ const tunnelListener = tunnel.listener?.();
318
+ if (tunnelListener !== void 0) listeners["tunnel.sock"] = tunnelListener;
307
319
  if (streamed) targets.set(TARGET_TUNNEL, (stream) => tunnel.acceptStream(stream));
308
320
  if (streamed && input.lease !== void 0) targets.set(TARGET_LEASE, (stream) => input.lease.accept(stream));
309
321
  const closeAll = async () => {
@@ -319,12 +331,15 @@ async function startBuildServices(input) {
319
331
  const server = await startDestination({
320
332
  destination,
321
333
  ...input.serve === "tcp" || streamed ? {} : { socketPath: destinationSocketPath(input.buildDir, index) },
334
+ ...input.serve === "tcp" || streamed ? {} : listenOnFor(`dest-${index}.sock`),
322
335
  ...input.serve === "socket" || streamed ? {} : { port: input.ports?.destinations?.[index] ?? 0 },
323
336
  hostEnv: input.hostEnv,
324
337
  onUsed: (name) => tunnel.destinationUsed(name),
325
338
  ...input.destinationOptions
326
339
  });
327
340
  servers.push(server);
341
+ const listener = server.listener?.();
342
+ if (listener !== void 0) listeners[`dest-${index}.sock`] = listener;
328
343
  targets.set(destinationTarget(index), (stream) => server.acceptStream(stream));
329
344
  if (server.port !== void 0) destinationPorts.push(server.port);
330
345
  }
@@ -333,6 +348,7 @@ async function startBuildServices(input) {
333
348
  const server = await startDestination({
334
349
  destination: builtIn.destination,
335
350
  ...input.serve === "tcp" || streamed ? {} : { socketPath: join(input.buildDir, builtIn.socket) },
351
+ ...input.serve === "tcp" || streamed ? {} : listenOnFor(builtIn.socket),
336
352
  ...input.serve === "socket" || streamed ? {} : { port: input.ports?.builtIns?.[builtIn.destination.name] ?? 0 },
337
353
  hostEnv: builtIn.hostEnv,
338
354
  drop: builtIn.drop,
@@ -340,6 +356,8 @@ async function startBuildServices(input) {
340
356
  ...input.destinationOptions
341
357
  });
342
358
  servers.push(server);
359
+ const listener = server.listener?.();
360
+ if (listener !== void 0) listeners[builtIn.socket] = listener;
343
361
  targets.set(builtIn.destination.name, (stream) => server.acceptStream(stream));
344
362
  if (server.port !== void 0) builtInPorts[builtIn.destination.name] = server.port;
345
363
  }
@@ -350,7 +368,7 @@ async function startBuildServices(input) {
350
368
  serve(stream);
351
369
  return true;
352
370
  }
353
- }, proxyPort, destinationPorts, builtInPorts, close: closeAll };
371
+ }, proxyPort, destinationPorts, builtInPorts, listeners, close: closeAll };
354
372
  } catch (err) {
355
373
  await closeAll();
356
374
  throw err;
@@ -1,15 +1,15 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- import { detectActiveSession, autoConnect } from './chunk-H57M3HC7.js';
4
- import { workPageUrl } from './chunk-PZA6ZFYW.js';
3
+ import { detectActiveSession, autoConnect } from './chunk-2F762IXY.js';
4
+ import { workPageUrl } from './chunk-N4D5TMVU.js';
5
5
  import { sanitizeTerminal } from './chunk-BUP5N3X6.js';
6
- import { resolveGatewayCredentials, unauthorizedMessage, defaultFetch } from './chunk-MCSINUVC.js';
6
+ import { resolveGatewayCredentials, unauthorizedMessage, defaultFetch } from './chunk-BWH36FOC.js';
7
7
  import { readRepoConnection } from './chunk-BJ3KTMDH.js';
8
8
  import { resolveRepoIdentity } from './chunk-QE3UQAHK.js';
9
9
  import { C } from './chunk-ANPNT6PM.js';
10
10
  import { parseTrackerNumber, trackerNumberError } from './chunk-WZBHBH3J.js';
11
11
  import { homeFromEnv } from './chunk-VNVZDWO3.js';
12
- import { signRunnerRequest } from './chunk-GWRDL75D.js';
12
+ import { signRunnerRequest } from './chunk-Y4OKEFAX.js';
13
13
 
14
14
  createRequire(import.meta.url);
15
15
 
@@ -428,7 +428,7 @@ async function runWorkLinkSession(opts) {
428
428
  return await linkSession(opts);
429
429
  } catch (err) {
430
430
  if (opts.logFailures) {
431
- const { logHookError } = await import('./hook-fast-XMZFMBKC.js');
431
+ const { logHookError } = await import('./hook-fast-3SUJM765.js');
432
432
  const reason = err instanceof Error ? err.message : String(err);
433
433
  logHookError(opts.home ?? homeFromEnv(opts.env ?? process.env), new Error(`work link-session #${opts.number} failed: ${reason}`));
434
434
  }
@@ -521,11 +521,22 @@ function mergedUnverifiedLine(data) {
521
521
  const at = typeof block.mergedAt === "string" ? ` at ${sanitizeTerminal(block.mergedAt)}` : "";
522
522
  return `${C.yellow}Merged, unverified${C.reset}: the pull request merged${at} before its checks or review passed, so the stage stays at review.
523
523
 
524
+ `;
525
+ }
526
+ function repositoryDisconnectedLine(data) {
527
+ const pullRequests = data.pullRequests;
528
+ if (!Array.isArray(pullRequests)) return "";
529
+ const names = pullRequests.filter((pr) => pr.repositoryConnected === false).map((pr) => sanitizeTerminal(`${String(pr.repository)}#${String(pr.prNumber)}`));
530
+ if (names.length === 0) return "";
531
+ const number = data.number;
532
+ const item = typeof number === "number" ? String(number) : "<n>";
533
+ return `${C.yellow}Repository disconnected${C.reset}: this factory no longer connects the repository of ${names.join(", ")}. Connect it again in Settings, or run outerlayer work remove --item ${item} to remove the item from Work.
534
+
524
535
  `;
525
536
  }
526
537
  function statusOutput(data, json) {
527
538
  if (json) return JSON.stringify(data);
528
- const document = mergedUnverifiedLine(data) + JSON.stringify(data, null, 2);
539
+ const document = mergedUnverifiedLine(data) + repositoryDisconnectedLine(data) + JSON.stringify(data, null, 2);
529
540
  const criteria = data.criteria;
530
541
  if (criteria === void 0) return document;
531
542
  if (criteria === null || !Array.isArray(criteria.items)) return `${document}
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- import { BRIDGE_ADDRESS, TUNNEL_PORT, BRIDGE_CIDR } from './chunk-UNF7MWEL.js';
3
+ import { BRIDGE_ADDRESS, TUNNEL_PORT, BRIDGE_CIDR } from './chunk-XS3FDWSZ.js';
4
4
  import { startTimeOf } from './chunk-JBJTC3UU.js';
5
- import { EXEC_PORT } from './chunk-XLC7KEUV.js';
6
- import { RELAY_PORT } from './chunk-2K3TCQHW.js';
5
+ import { EXEC_PORT } from './chunk-GLUKDQS4.js';
6
+ import { RELAY_PORT } from './chunk-2I3LIRZP.js';
7
7
  import { spawn, spawnSync } from 'child_process';
8
- import { writeFileSync, chmodSync, mkdirSync } from 'fs';
8
+ import { symlinkSync, lstatSync, writeFileSync, chmodSync, mkdirSync } from 'fs';
9
9
 
10
10
  createRequire(import.meta.url);
11
11
  var HELPER_PATH = "/opt/outerlayer/bin/outerlayer-guest";
@@ -31,6 +31,22 @@ function relayPortsFrom(cmdline) {
31
31
  return port;
32
32
  });
33
33
  }
34
+ function linkDescriptorPaths(host) {
35
+ const links = [
36
+ ["/dev/fd", "/proc/self/fd"],
37
+ ["/dev/stdin", "/proc/self/fd/0"],
38
+ ["/dev/stdout", "/proc/self/fd/1"],
39
+ ["/dev/stderr", "/proc/self/fd/2"]
40
+ ];
41
+ for (const [path, target] of links) {
42
+ if (host.exists(path)) continue;
43
+ try {
44
+ host.symlink(target, path);
45
+ } catch (err) {
46
+ host.warn(`vm-init: linking ${path} to ${target} failed: ${err instanceof Error ? err.message : String(err)}`);
47
+ }
48
+ }
49
+ }
34
50
  function dockerRequestedBy(cmdline) {
35
51
  return new RegExp(`(?:^|\\s)${DOCKER_FLAG.replace(".", "\\.")}=1(?:\\s|$)`).test(cmdline);
36
52
  }
@@ -59,6 +75,7 @@ function startDocker(host) {
59
75
  }
60
76
  function runGuestInit(host, cmdline) {
61
77
  const ports = relayPortsFrom(cmdline);
78
+ linkDescriptorPaths(host);
62
79
  host.mount({ source: BUILD_DISK, target: "/build", type: "ext4" });
63
80
  host.chmod("/build", WORLD_WRITABLE);
64
81
  for (const path of BUILD_DIRECTORIES) {
@@ -101,6 +118,17 @@ function realGuestHost() {
101
118
  mkdir: (path, mode) => void mkdirSync(path, { recursive: true, mode }),
102
119
  chmod: (path, mode) => chmodSync(path, mode),
103
120
  writeFile: (path, text) => writeFileSync(path, text),
121
+ exists(path) {
122
+ try {
123
+ lstatSync(path);
124
+ return true;
125
+ } catch {
126
+ return false;
127
+ }
128
+ },
129
+ symlink: (target, path) => symlinkSync(target, path),
130
+ warn: (text) => void process.stderr.write(`${text}
131
+ `),
104
132
  spawnDetached(argv) {
105
133
  const child = spawn(argv[0], argv.slice(1), { detached: true, stdio: ["ignore", "inherit", "inherit"] });
106
134
  child.unref();
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
3
  import { rewriteControlPlanePaths, rewriteAgentsFilePaths, SUPPORTED_CONTROL_PLANE_LAYOUT, rewriteHookCommand } from './chunk-HCVLP7XA.js';
4
- import { MAINTAINED_SKILLS } from './chunk-HBISTTG3.js';
4
+ import { MAINTAINED_SKILLS } from './chunk-7OUOLGUV.js';
5
5
  import { existsSync, mkdirSync, copyFileSync, lstatSync, readdirSync, writeFileSync, readFileSync } from 'fs';
6
6
  import { join, dirname } from 'path';
7
7
 
@@ -1,8 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- import { builtinKind } from './chunk-JWRX5ZTT.js';
4
- import { parseSize } from './chunk-7RQ73DLO.js';
5
- import { runnerDir } from './chunk-F3BBEBPL.js';
3
+ import { FD_SLOTS_PER_BUILD, FD_STORE_ENV } from './chunk-YB3CSYXX.js';
4
+ import { builtinKind } from './chunk-EPPIISRD.js';
5
+ import { parseSize } from './chunk-YJLHUKYS.js';
6
+ import { runnerDir } from './chunk-RHAGBUL5.js';
6
7
  import { dirname } from 'path';
7
8
  import { spawnSync } from 'child_process';
8
9
 
@@ -28,6 +29,7 @@ ${helperService === void 0 ? "" : `Wants=${helperService}
28
29
  User=${unit.user}
29
30
  ExecStart=${unit.execStart}
30
31
  ${unit.helper === void 0 ? "" : `Environment=OUTERLAYER_HELPER_SOCKET=${unit.helper.socket}
32
+ `}${unit.fdStore === void 0 ? "" : `Environment=${FD_STORE_ENV}=${unit.fdStore}
31
33
  `}EnvironmentFile=-${escapeUnitPath(`${unit.configDir}/runner.env`)}
32
34
  Restart=on-failure
33
35
  RestartForceExitStatus=75
@@ -37,7 +39,10 @@ KillMode=mixed
37
39
  TimeoutStopSec=${unit.timeLimitMinutes + STOP_MARGIN_MINUTES}min
38
40
  OOMPolicy=continue
39
41
  Delegate=yes
40
-
42
+ ${unit.fdStore === void 0 ? "" : `NotifyAccess=all
43
+ FileDescriptorStoreMax=${unit.fdStore}
44
+ FileDescriptorStorePreserve=yes
45
+ `}
41
46
  [Install]
42
47
  WantedBy=multi-user.target
43
48
  `;
@@ -147,7 +152,9 @@ function installService(request, deps) {
147
152
  execStart: request.execStart,
148
153
  timeLimitMinutes: request.timeLimitMinutes,
149
154
  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 } } : {}
155
+ ...request.helperSocket !== void 0 && request.helperUnit !== void 0 ? { helper: { unit: request.helperUnit, socket: request.helperSocket } } : {},
156
+ // A microVM build under the helper keeps its sockets in the descriptor store, so the runner can restart and the build goes on.
157
+ ...kind === "vm" && request.helperSocket !== void 0 && request.helperUnit !== void 0 ? { fdStore: Math.max(1, request.concurrency) * FD_SLOTS_PER_BUILD } : {}
151
158
  });
152
159
  if (request.print === true) return outcome(true, "printed", [], unit);
153
160
  if (own) return outcome(false, "refused", [OWN_HOOKS]);