@outerlayer/cli 0.5.0 → 0.6.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 (70) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/README.md +9 -0
  3. package/dist/{agent-setup-IFH3FB5X.js → agent-setup-USZ2SAXC.js} +2 -2
  4. package/dist/build-info.json +1 -1
  5. package/dist/{check-AXCXDYO2.js → check-CCVPBN7A.js} +26 -14
  6. package/dist/{chunk-5Y3QQRUH.js → chunk-3DD6AOWC.js} +37 -0
  7. package/dist/{chunk-WOBNRVQT.js → chunk-3DEVYTNQ.js} +1 -1
  8. package/dist/{chunk-L24G4PLP.js → chunk-43J3M6KB.js} +486 -21
  9. package/dist/{chunk-FC7TMRNA.js → chunk-4NLKY4WR.js} +2 -2
  10. package/dist/{lima-XN65D7GN.js → chunk-77MM5RYD.js} +0 -1
  11. package/dist/{chunk-D2KWEAN6.js → chunk-7DBI6SNT.js} +1 -1
  12. package/dist/{chunk-QDQEUVUF.js → chunk-AIW6GLET.js} +1 -1
  13. package/dist/{chunk-EX6M5JPR.js → chunk-ANK2B36Y.js} +131 -15
  14. package/dist/{chunk-RMAZ2NWA.js → chunk-BNMCXPG7.js} +14 -2
  15. package/dist/{chunk-UFFXNXLQ.js → chunk-FA2BOX4O.js} +31 -29
  16. package/dist/chunk-FBTE2A2X.js +38 -0
  17. package/dist/{chunk-DVDEBNQJ.js → chunk-GQD22KKN.js} +17 -6
  18. package/dist/{chunk-BCJNHMZT.js → chunk-HHJDN6AU.js} +18 -3
  19. package/dist/{chunk-JLDZTG3A.js → chunk-IASWYRVG.js} +1 -1
  20. package/dist/{install-cmd-7WXOIFDO.js → chunk-JWOQNNN2.js} +0 -1
  21. package/dist/{chunk-ZLB7GCTY.js → chunk-MIGEDH5P.js} +19 -11
  22. package/dist/{chunk-OHFDWLJP.js → chunk-MXB5VB4Y.js} +18 -6
  23. package/dist/chunk-NQXFAGHU.js +211 -0
  24. package/dist/chunk-OUUINEYB.js +27 -0
  25. package/dist/{chunk-OR6AEEDO.js → chunk-PMAMOSTP.js} +3 -3
  26. package/dist/{chunk-RJ467PDQ.js → chunk-RBOMWNOG.js} +1 -1
  27. package/dist/{chunk-4NMBDRBS.js → chunk-RZSF5B46.js} +289 -130
  28. package/dist/chunk-T6OIJQRT.js +31 -0
  29. package/dist/chunk-UGP76IZ5.js +34 -0
  30. package/dist/{chunk-EGABNHZG.js → chunk-UMFWVGJO.js} +148 -18
  31. package/dist/{chunk-RWOTHXYE.js → chunk-VMJ3MMBX.js} +206 -71
  32. package/dist/chunk-YYYRBYWG.js +32 -0
  33. package/dist/chunk-ZILGTTY6.js +75 -0
  34. package/dist/{cli-NNWPFP5S.js → cli-7WCNPBYC.js} +147 -69
  35. package/dist/command-ARFK3OYF.js +162 -0
  36. package/dist/{config-XVJYZ4IQ.js → config-HL2AIR3K.js} +1 -1
  37. package/dist/{connect-cmd-OJBFHZWP.js → connect-cmd-PCESZLTR.js} +4 -4
  38. package/dist/{context-adopt-UMA4O5NY.js → context-adopt-G5JAELE7.js} +2 -2
  39. package/dist/{context-materialize-G2FUFC72.js → context-materialize-3HLPZAF6.js} +2 -2
  40. package/dist/{dist-WDV7U7UJ.js → dist-RDLP4GGJ.js} +1 -1
  41. package/dist/{doctor-J3GIMR7K.js → doctor-VOMYOHQU.js} +47 -13
  42. package/dist/{emit-artifact-cmd-FDWYKURA.js → emit-artifact-cmd-TBF46VXZ.js} +6 -7
  43. package/dist/{emit-criteria-cmd-OCXHLK34.js → emit-criteria-cmd-3427XKFW.js} +3 -3
  44. package/dist/{emit-finding-cmd-POW7PKIA.js → emit-finding-cmd-GSI75K36.js} +3 -3
  45. package/dist/{emit-result-cmd-ID35SH53.js → emit-result-cmd-3PRQKB55.js} +6 -7
  46. package/dist/{hook-fast-EYENPK6F.js → hook-fast-NJBBZQ5Q.js} +1 -1
  47. package/dist/index.js +3 -2
  48. package/dist/{init-CZX7ZDWF.js → init-OHYFIKZD.js} +27 -56
  49. package/dist/{init-3ZQLFPNC.js → init-WVR4BZQ4.js} +8 -8
  50. package/dist/{init-cmd-W5NOLQPX.js → init-cmd-HMFNIFPI.js} +7 -7
  51. package/dist/inspect-XAUSB7L3.js +63 -0
  52. package/dist/install-DSUZE2TJ.js +13 -0
  53. package/dist/install-cmd-453C7IZT.js +8 -0
  54. package/dist/lima-7NPCNGWT.js +6 -0
  55. package/dist/{loop-VGM772I7.js → loop-UAJB34KA.js} +26 -11
  56. package/dist/{machine-5WEJA36F.js → machine-2JOWGRSZ.js} +7 -7
  57. package/dist/record-C43U4PKS.js +7 -0
  58. package/dist/session-sender-ED26V45B.js +28 -0
  59. package/dist/{status-KBDVUYQ3.js → status-6WNXX2O7.js} +42 -10
  60. package/dist/{sync-cmd-SWQ5KLPL.js → sync-cmd-YONVGUYS.js} +6 -7
  61. package/dist/{watch-3YW6Z3DQ.js → watch-HHKAF26L.js} +6 -7
  62. package/dist/{work-claim-cmd-BQPPURBW.js → work-claim-cmd-LXBZTMJA.js} +6 -6
  63. package/dist/{work-cmd-NUR4CAG6.js → work-cmd-OVNF44FY.js} +3 -3
  64. package/dist/{work-comment-cmd-6TTTYGHM.js → work-comment-cmd-6DGQNTAH.js} +3 -3
  65. package/dist/{work-launch-YI4CJDAP.js → work-launch-R5HTAP6X.js} +1 -1
  66. package/dist/{work-open-pr-cmd-KZ7QQFLD.js → work-open-pr-cmd-FCZHFHZS.js} +4 -4
  67. package/dist/{work-pr-cmd-QNUBE6IQ.js → work-pr-cmd-QJJVHZDC.js} +2 -2
  68. package/package.json +2 -1
  69. package/dist/chunk-ZOC7JSYC.js +0 -83
  70. package/dist/{chunk-XDDW4FRS.js → chunk-OIRNH6UO.js} +1 -1
@@ -1,13 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- import { gatherDoctorChecks, doctorExitCode, summarizeChecks, renderDoctorText, recordWrapped, recordUnwrapped, recordRemoval, readManifest, recordInstall, recordStatusline } from './chunk-ZLB7GCTY.js';
4
- import './chunk-D2KWEAN6.js';
3
+ import { gatherDoctorChecks, doctorExitCode, summarizeChecks, renderDoctorText, recordWrapped, recordUnwrapped, recordRemoval, readManifest, recordInstall, recordStatusline } from './chunk-MIGEDH5P.js';
4
+ import './chunk-7DBI6SNT.js';
5
5
  import './chunk-F47JBIAW.js';
6
6
  import './chunk-BKO6JEZI.js';
7
- import './chunk-RMAZ2NWA.js';
7
+ import './chunk-BNMCXPG7.js';
8
8
  import './chunk-BFESLKPP.js';
9
- import './chunk-WOBNRVQT.js';
10
- import './chunk-EX6M5JPR.js';
9
+ import './chunk-3DEVYTNQ.js';
10
+ import './chunk-ANK2B36Y.js';
11
11
  import './chunk-USO2DKBD.js';
12
12
  import './chunk-N6LURUEF.js';
13
13
  import { C, NO_COLOR_FLAG, warnDeprecated, stripNoColor, API_KEY_FLAG_DEPRECATION } from './chunk-YRYMNBGQ.js';
@@ -21,14 +21,14 @@ import './chunk-EABW6AJQ.js';
21
21
  import { resolveVersion } from './chunk-RA3O54FC.js';
22
22
  import './chunk-M5POMKKW.js';
23
23
  import './chunk-ZM2IMMYN.js';
24
- import { hookFiredMarkerPath, installedCliDistDir } from './chunk-UFFXNXLQ.js';
24
+ import { hookFiredMarkerPath, installedCliDistDir } from './chunk-FA2BOX4O.js';
25
25
  import { managedHookScripts, removeJournalHooks, installPreCommitGuardHook, removePreCommitGuardHook } from './chunk-XCCLFVXM.js';
26
26
  import './chunk-BJ3KTMDH.js';
27
27
  import { homeFromEnv } from './chunk-VNVZDWO3.js';
28
28
  import './chunk-Z5GEFMRW.js';
29
29
  import { EventEmitter } from 'events';
30
30
  import childProcess, { execFileSync } from 'child_process';
31
- import path, { isAbsolute, dirname, join } from 'path';
31
+ import path, { join, dirname, isAbsolute } from 'path';
32
32
  import fs, { existsSync, unlinkSync, mkdirSync, cpSync, readdirSync, renameSync, statSync, rmSync, readFileSync, writeFileSync, realpathSync } from 'fs';
33
33
  import process2 from 'process';
34
34
  import { stripVTControlCharacters } from 'util';
@@ -3792,8 +3792,8 @@ ${starter.output}`),
3792
3792
  }
3793
3793
  }
3794
3794
  async function realConnectDepsForCwd() {
3795
- const { realConnectDeps } = await import('./connect-cmd-OJBFHZWP.js');
3796
- const { resolveRepoIdentity } = await import('./dist-WDV7U7UJ.js');
3795
+ const { realConnectDeps } = await import('./connect-cmd-PCESZLTR.js');
3796
+ const { resolveRepoIdentity } = await import('./dist-RDLP4GGJ.js');
3797
3797
  return {
3798
3798
  ...realConnectDeps(),
3799
3799
  home: homeFromEnv(),
@@ -3846,7 +3846,7 @@ function buildProgram(argv = process.argv) {
3846
3846
  else process.stdout.write(outcome.text);
3847
3847
  return;
3848
3848
  }
3849
- const { runInitCommand } = await import('./init-cmd-W5NOLQPX.js');
3849
+ const { runInitCommand } = await import('./init-cmd-HMFNIFPI.js');
3850
3850
  const code = await runInitCommand(
3851
3851
  { json: opts.json, factory: opts.factory },
3852
3852
  {
@@ -3858,7 +3858,7 @@ function buildProgram(argv = process.argv) {
3858
3858
  return { ok, text, detail };
3859
3859
  },
3860
3860
  runAgentSetup: async () => {
3861
- const { runAgentSetup } = await import('./agent-setup-IFH3FB5X.js');
3861
+ const { runAgentSetup } = await import('./agent-setup-USZ2SAXC.js');
3862
3862
  return runAgentSetup({ cwd: process.cwd() });
3863
3863
  },
3864
3864
  runDoctorChecks: gatherDoctorChecks
@@ -3870,7 +3870,7 @@ function buildProgram(argv = process.argv) {
3870
3870
  "after",
3871
3871
  "\nRun it in a repository, after `outerlayer login`. With a login, connect lists\nthe factories you can use and takes the first rule that leaves one answer:\n 1. --factory <org>/<factory>, or a factory id\n 2. the factory this repository is already connected to\n 3. the one factory this repository is linked to\n 4. the one factory you can use\n 5. on a terminal, ask; with no terminal, exit 1 and list each --factory value\n\nIt says what it chose and why. The choice is saved in this repository's git\ndirectory (outerlayer/connection.json), shared by every worktree of the\nclone and never committed. A factory key already belongs to one factory, so\nconnect changes nothing for it. Connect never links a repository.\n\nAfter it saves the choice, connect checks that this repository is linked to\nthe factory. If it is not, connect says so, prints the factory's Work page\nlink where you link it, and exits 2. The choice is saved either way. Run\nconnect again once the repository is linked. With --json, the document has\nlinked (true, false, or null when it could not be checked) and, when false,\nlinkUrl.\n"
3872
3872
  ).action(async (opts) => {
3873
- const { runConnectCommand } = await import('./connect-cmd-OJBFHZWP.js');
3873
+ const { runConnectCommand } = await import('./connect-cmd-PCESZLTR.js');
3874
3874
  const code = await runConnectCommand({ factory: opts.factory, json: opts.json }, await realConnectDepsForCwd(), {
3875
3875
  stdout: process.stdout,
3876
3876
  stderr: process.stderr
@@ -3918,7 +3918,7 @@ function buildProgram(argv = process.argv) {
3918
3918
  "\nIncremental: only sessions newer than the last checkpoint ship. The tier\nis applied on this machine before anything leaves it; `--dry-run` prints\nexactly what would. The hook-triggered background sync runs `--quiet`.\nExit 2 means the server rejected at least one session.\n"
3919
3919
  ).action(async (opts) => {
3920
3920
  warnIfApiKeyFlag(opts);
3921
- const { runSync, SyncConfigError, SyncTransportError } = await import('./sync-cmd-SWQ5KLPL.js');
3921
+ const { runSync, SyncConfigError, SyncTransportError } = await import('./sync-cmd-YONVGUYS.js');
3922
3922
  try {
3923
3923
  const result = await runSync({
3924
3924
  url: opts.url,
@@ -3947,7 +3947,7 @@ function buildProgram(argv = process.argv) {
3947
3947
  if (invokedCommandToken(argv) === "watch") {
3948
3948
  warnDeprecated("`outerlayer watch` is now `outerlayer daemon`; the old name will be removed in a future release.");
3949
3949
  }
3950
- const { runWatch } = await import('./watch-3YW6Z3DQ.js');
3950
+ const { runWatch } = await import('./watch-HHKAF26L.js');
3951
3951
  await runWatch({ once: opts.once });
3952
3952
  });
3953
3953
  program2.command("hook <event>", { hidden: true }).description("capture-hook fast path, installed by `init`").action(() => {
@@ -4000,6 +4000,14 @@ function buildProgram(argv = process.argv) {
4000
4000
  process.exit(255);
4001
4001
  }
4002
4002
  });
4003
+ program2.command("session-sender", { hidden: true }).description("stream a runner build's sessions to its work item while the build runs").action(async () => {
4004
+ const { runSessionSender } = await import('./session-sender-ED26V45B.js');
4005
+ try {
4006
+ await runSessionSender();
4007
+ } catch (err) {
4008
+ fail(`session-sender: ${err instanceof Error ? err.message : String(err)}`);
4009
+ }
4010
+ });
4003
4011
  program2.command("vm-init", { hidden: true }).description("lay out a microVM build's guest; the guest helper runs it").action(async () => {
4004
4012
  const { realGuestHost, runGuestInit } = await import('./guest-init-GENRHNP7.js');
4005
4013
  const { readFileSync: readFileSync3 } = await import('fs');
@@ -4051,7 +4059,7 @@ function registerEmitCommands(program2) {
4051
4059
  return;
4052
4060
  }
4053
4061
  warnIfApiKeyFlag(opts);
4054
- const { runEmitResult, EmitResultError } = await import('./emit-result-cmd-ID35SH53.js');
4062
+ const { runEmitResult, EmitResultError } = await import('./emit-result-cmd-3PRQKB55.js');
4055
4063
  try {
4056
4064
  const result = await runEmitResult({
4057
4065
  name,
@@ -4102,7 +4110,7 @@ locally so it never uploads. One correction can retire several duplicates.
4102
4110
  `
4103
4111
  ).action(async (file, opts) => {
4104
4112
  warnIfApiKeyFlag(opts);
4105
- const { runEmitArtifact, EmitArtifactError } = await import('./emit-artifact-cmd-FDWYKURA.js');
4113
+ const { runEmitArtifact, EmitArtifactError } = await import('./emit-artifact-cmd-TBF46VXZ.js');
4106
4114
  let pr;
4107
4115
  if (opts.pr !== void 0) {
4108
4116
  pr = parseTrackerNumber(opts.pr);
@@ -4137,7 +4145,7 @@ locally so it never uploads. One correction can retire several duplicates.
4137
4145
  '\n outerlayer emit finding --id gate-skip --category tooling \\\n --title "The pre-push gate prints green when a step was skipped" \\\n --file scripts/git/pre-push-checks.mjs --where "build session"\n\nRecords one problem you hit in the factory: an instruction that is wrong or\nmissing (context), a flaky test, a misbehaving script (tooling), a local\nsetup trap (environment), a bug outside your own change (defect), or\nanything else. A problem in your own change is fixed, not recorded.\nA context finding needs --rule-path and --rule-relation, and --rule-quote\nunless the relation is missing.\n--item names the work item; without it, inside a recorded session, the\nitem that session was launched for is used automatically.\n'
4138
4146
  ).action(async (opts) => {
4139
4147
  warnIfApiKeyFlag(opts);
4140
- const { runEmitFinding, refuseRemovedFindingFlags, EmitFindingError } = await import('./emit-finding-cmd-POW7PKIA.js');
4148
+ const { runEmitFinding, refuseRemovedFindingFlags, EmitFindingError } = await import('./emit-finding-cmd-GSI75K36.js');
4141
4149
  try {
4142
4150
  refuseRemovedFindingFlags(opts);
4143
4151
  const result = await runEmitFinding({
@@ -4176,7 +4184,7 @@ locally so it never uploads. One correction can retire several duplicates.
4176
4184
  "\n outerlayer emit findings review.json --item 412\n\nRecords a whole batch at once \u2014 a FindingBatch JSON file (schemaVersion,\noptional headSha / contextSourceSha, and 1-500 findings), validated against\nthe same contract `emit finding` uses before anything is sent. Re-emitting\nan id already on the item replaces that row rather than adding a second one.\n--item names the work item; without it, inside a recorded session, the item\nthat session was launched for is used automatically.\n"
4177
4185
  ).action(async (file, opts) => {
4178
4186
  warnIfApiKeyFlag(opts);
4179
- const { runEmitFindings, EmitFindingError } = await import('./emit-finding-cmd-POW7PKIA.js');
4187
+ const { runEmitFindings, EmitFindingError } = await import('./emit-finding-cmd-GSI75K36.js');
4180
4188
  try {
4181
4189
  const result = await runEmitFindings({
4182
4190
  file,
@@ -4200,7 +4208,7 @@ locally so it never uploads. One correction can retire several duplicates.
4200
4208
  '\n outerlayer emit criteria criteria.json --item 412\n\nRecords the item\'s acceptance criteria as one list. The file is JSON:\n { "criteria": [ { "id": "AC-1", "text": "Given ...", "proof": null },\n { "id": "login.redirect", "text": "...", "proof": "screenshot" } ] }\nAn id is 1-64 characters of A-Z a-z 0-9 . _ : - and is the id evidence is bound to with\n`emit artifact --for`. proof is video, screenshot, report, log, test, file or null (any bound\nartifact proves it). The list holds 1 to 200 criteria, each text at most 2,000\ncharacters, and no id twice. The list is validated before anything is sent.\nOnce an item has a list, only a person can replace it: run the command outside an\nagent session, with your own login. A key bound to no member cannot replace it either.\n--item names the work item; without it, inside a recorded session, the item that\nsession was launched for is used automatically.\n'
4201
4209
  ).action(async (file, opts) => {
4202
4210
  warnIfApiKeyFlag(opts);
4203
- const { runEmitCriteria, EmitCriteriaError } = await import('./emit-criteria-cmd-OCXHLK34.js');
4211
+ const { runEmitCriteria, EmitCriteriaError } = await import('./emit-criteria-cmd-3427XKFW.js');
4204
4212
  try {
4205
4213
  const result = await runEmitCriteria({
4206
4214
  file,
@@ -4227,7 +4235,7 @@ function registerWorkCommands(program2) {
4227
4235
  "\n outerlayer work build --issue 42 # a host builds it\n outerlayer work build --item 7 # retry an item already on Work\n outerlayer work build --issue 42 --local # you build it\n outerlayer work build --issue ENG-123 --tracker linear # a Linear issue\n\nWithout --local, records a build request: a host takes the item when it\nhas a free slot. It never links the calling session and never takes a\nlease, even run from inside one.\n\nWith --local, puts the item on Work with no build request, so no host\ntakes it, and prints the line that starts your session on it. That\nsession links itself and holds the item's lease while it works.\n"
4228
4236
  ).action(async (opts) => {
4229
4237
  warnIfApiKeyFlag(opts);
4230
- const { runWorkBuild, WorkCommandError } = await import('./work-cmd-NUR4CAG6.js');
4238
+ const { runWorkBuild, WorkCommandError } = await import('./work-cmd-OVNF44FY.js');
4231
4239
  try {
4232
4240
  await runWorkBuild(opts);
4233
4241
  } catch (err) {
@@ -4238,14 +4246,14 @@ function registerWorkCommands(program2) {
4238
4246
  throw err;
4239
4247
  }
4240
4248
  });
4241
- workCmd.command("link-session").description("Link the calling session to a work item that already exists").argument("<number>", "the item's number inside this factory").option("--session-id <id>", "session id to record as-is, skipping detection (used by the SessionStart hook)").option("--retry <count>", "resend after a failure that may pass, with backoff").option("--no-lease", "link only \u2014 ask for no session lease (used by a host's own job)").option("--ended", "release this session's lease instead of taking or renewing one").option("--json", "machine-readable JSON output").option("--url <url>", "cloud base URL (or OUTERLAYER_URL / config)").addOption(apiKeyOption()).option("--app-id <id>", "factory id the key is bound to (or OUTERLAYER_APP_ID / config)").addHelpText(
4249
+ workCmd.command("link-session").description("Link the calling session to a work item that already exists").argument("<number>", "the item's number inside this factory").option("--session-id <id>", "session id to record as-is, skipping detection (used by the SessionStart hook)").option("--retry <count>", "resend after a failure that may pass, with backoff").option("--no-lease", "link only \u2014 ask for no session lease (used by a host's own job)").option("--ended", "release this session's lease instead of taking or renewing one").addOption(new Option("--log-failures", "append a failure to the hook error log (used by hooks)").hideHelp()).option("--json", "machine-readable JSON output").option("--url <url>", "cloud base URL (or OUTERLAYER_URL / config)").addOption(apiKeyOption()).option("--app-id <id>", "factory id the key is bound to (or OUTERLAYER_APP_ID / config)").addHelpText(
4242
4250
  "after",
4243
4251
  "\n outerlayer work link-session 42\n\nNames an item that already exists on the calling session \u2014 it never\ncreates one. Create one with `outerlayer work build --local`. Linking\nthe same session to the same item twice is a no-op. Also takes or\nrenews that session's own 20-minute lease, unless --no-lease or\n--ended is given, or another worker already holds a live one.\n"
4244
4252
  ).action(async (number, opts) => {
4245
4253
  warnIfApiKeyFlag(opts);
4246
- const { runWorkLinkSession, WorkCommandError } = await import('./work-cmd-NUR4CAG6.js');
4254
+ const { runWorkLinkSession, WorkCommandError } = await import('./work-cmd-OVNF44FY.js');
4247
4255
  try {
4248
- await runWorkLinkSession({ ...opts, number, noLease: opts.lease === false });
4256
+ await runWorkLinkSession({ ...opts, number, noLease: opts.lease === false, logFailures: opts.logFailures === true });
4249
4257
  } catch (err) {
4250
4258
  if (err instanceof WorkCommandError) {
4251
4259
  fail(err.message);
@@ -4268,8 +4276,8 @@ most one action flag may be given.
4268
4276
  `
4269
4277
  ).action(async (opts) => {
4270
4278
  warnIfApiKeyFlag(opts);
4271
- const { runWorkComment, WorkCommentCommandError } = await import('./work-comment-cmd-6TTTYGHM.js');
4272
- const { WorkCommandError } = await import('./work-cmd-NUR4CAG6.js');
4279
+ const { runWorkComment, WorkCommentCommandError } = await import('./work-comment-cmd-6DGQNTAH.js');
4280
+ const { WorkCommandError } = await import('./work-cmd-OVNF44FY.js');
4273
4281
  try {
4274
4282
  await runWorkComment(opts);
4275
4283
  } catch (err) {
@@ -4285,8 +4293,8 @@ most one action flag may be given.
4285
4293
  "\n outerlayer work threads --item 7\n\nPrints the item's general thread, then one thread per acceptance\ncriterion that carries proof or a comment, each with who is waiting\non whom next.\n"
4286
4294
  ).action(async (opts) => {
4287
4295
  warnIfApiKeyFlag(opts);
4288
- const { runWorkThreads, WorkCommentCommandError } = await import('./work-comment-cmd-6TTTYGHM.js');
4289
- const { WorkCommandError } = await import('./work-cmd-NUR4CAG6.js');
4296
+ const { runWorkThreads, WorkCommentCommandError } = await import('./work-comment-cmd-6DGQNTAH.js');
4297
+ const { WorkCommandError } = await import('./work-cmd-OVNF44FY.js');
4290
4298
  try {
4291
4299
  await runWorkThreads(opts);
4292
4300
  } catch (err) {
@@ -4308,7 +4316,7 @@ live addition remains on it.
4308
4316
  `
4309
4317
  ).action(async (opts) => {
4310
4318
  warnIfApiKeyFlag(opts);
4311
- const { runWorkRemove, WorkCommandError } = await import('./work-cmd-NUR4CAG6.js');
4319
+ const { runWorkRemove, WorkCommandError } = await import('./work-cmd-OVNF44FY.js');
4312
4320
  try {
4313
4321
  await runWorkRemove(opts);
4314
4322
  } catch (err) {
@@ -4321,7 +4329,7 @@ live addition remains on it.
4321
4329
  });
4322
4330
  workCmd.command("status").description("Show one item's stage, gate ledger, and additions").option("--item <number>", "the item's number inside this factory").option("--issue <number>", "issue number").option("--repo <owner/name>", "repository (default: the current checkout's remote)").option("--json", "machine-readable JSON output").option("--url <url>", "cloud base URL (or OUTERLAYER_URL / config)").addOption(apiKeyOption()).option("--app-id <id>", "factory id the key is bound to (or OUTERLAYER_APP_ID / config)").action(async (opts) => {
4323
4331
  warnIfApiKeyFlag(opts);
4324
- const { runWorkStatus, WorkCommandError } = await import('./work-cmd-NUR4CAG6.js');
4332
+ const { runWorkStatus, WorkCommandError } = await import('./work-cmd-OVNF44FY.js');
4325
4333
  try {
4326
4334
  await runWorkStatus(opts);
4327
4335
  } catch (err) {
@@ -4334,7 +4342,7 @@ live addition remains on it.
4334
4342
  });
4335
4343
  workCmd.command("list").description("List live work items for this factory").option("--stage <spec|build|review>", "filter by stage").option("--section <attention|waiting|flowing>", "filter by Queue section").option("--repo <owner/name>", "repository (default: the current checkout's remote)").option("--unclaimed", "only items with no live lease").option("--claimed", "only items with a live lease, held by a host or a local session").option("--startable", "only items with an unused build request and no live lease").option("--needs <amend>", "only items with a thread waiting on an agent").option("--json", "machine-readable JSON output").option("--url <url>", "cloud base URL (or OUTERLAYER_URL / config)").addOption(apiKeyOption()).option("--app-id <id>", "factory id the key is bound to (or OUTERLAYER_APP_ID / config)").action(async (opts) => {
4336
4344
  warnIfApiKeyFlag(opts);
4337
- const { runWorkList, WorkCommandError } = await import('./work-cmd-NUR4CAG6.js');
4345
+ const { runWorkList, WorkCommandError } = await import('./work-cmd-OVNF44FY.js');
4338
4346
  try {
4339
4347
  await runWorkList(opts);
4340
4348
  } catch (err) {
@@ -4350,7 +4358,7 @@ live addition remains on it.
4350
4358
  "\n outerlayer work claim --item 42 --kind implement\n\nRecords a lease for this host. A live lease already held by a\ndifferent host is refused; claiming again under the same host extends\nit.\n"
4351
4359
  ).action(async (opts) => {
4352
4360
  warnIfApiKeyFlag(opts);
4353
- const { runWorkClaim, WorkCommandError } = await import('./work-claim-cmd-BQPPURBW.js');
4361
+ const { runWorkClaim, WorkCommandError } = await import('./work-claim-cmd-LXBZTMJA.js');
4354
4362
  try {
4355
4363
  await runWorkClaim(opts);
4356
4364
  } catch (err) {
@@ -4366,7 +4374,7 @@ live addition remains on it.
4366
4374
  "\n outerlayer work renew --item 42\n\nExtends this host's own live lease. Refused with a 410 if the lease\nhas expired, or a 403 if a different host holds it.\n"
4367
4375
  ).action(async (opts) => {
4368
4376
  warnIfApiKeyFlag(opts);
4369
- const { runWorkRenew, WorkCommandError } = await import('./work-claim-cmd-BQPPURBW.js');
4377
+ const { runWorkRenew, WorkCommandError } = await import('./work-claim-cmd-LXBZTMJA.js');
4370
4378
  try {
4371
4379
  await runWorkRenew(opts);
4372
4380
  } catch (err) {
@@ -4385,7 +4393,7 @@ live addition remains on it.
4385
4393
  "\n outerlayer work release --item 42 --outcome ok\n\nMarks this host's lease released, optionally with an outcome.\nReleasing an already-released lease is a no-op that returns the\nrecorded release time and outcome.\n"
4386
4394
  ).action(async (opts) => {
4387
4395
  warnIfApiKeyFlag(opts);
4388
- const { runWorkRelease, WorkCommandError } = await import('./work-claim-cmd-BQPPURBW.js');
4396
+ const { runWorkRelease, WorkCommandError } = await import('./work-claim-cmd-LXBZTMJA.js');
4389
4397
  try {
4390
4398
  await runWorkRelease(opts);
4391
4399
  } catch (err) {
@@ -4401,7 +4409,7 @@ live addition remains on it.
4401
4409
  "\n outerlayer work pr 481\n\nRun inside a session already on a work item. Records that pull\nrequest 481 in the checkout's repository belongs to that item \u2014 the\nsame fact a `pr-link` in the session's own transcript records on\nsync. Idempotent: declaring the same pull request twice is a no-op.\n"
4402
4410
  ).action(async (number, opts) => {
4403
4411
  warnIfApiKeyFlag(opts);
4404
- const { runWorkPr, WorkPrCommandError } = await import('./work-pr-cmd-QNUBE6IQ.js');
4412
+ const { runWorkPr, WorkPrCommandError } = await import('./work-pr-cmd-QJJVHZDC.js');
4405
4413
  try {
4406
4414
  await runWorkPr({ ...opts, number });
4407
4415
  } catch (err) {
@@ -4417,8 +4425,8 @@ live addition remains on it.
4417
4425
  '\n outerlayer work open-pr --title "Add the thing" --body-file pr.md\n\nIn a host build, which holds an item key, the gateway opens the pull\nrequest as the factory\'s GitHub App, from the branch the claim named,\nor edits the one already open on that branch. No gh command runs.\nIn your own session it runs `gh pr create` (or `gh pr edit`) under your\nlogin, then declares the pull request on the item, as `work pr` does.\nRun it again after a change to the title or description.\n'
4418
4426
  ).action(async (opts) => {
4419
4427
  warnIfApiKeyFlag(opts);
4420
- const { runWorkOpenPr, WorkOpenPrCommandError } = await import('./work-open-pr-cmd-KZ7QQFLD.js');
4421
- const { WorkCommandError } = await import('./work-cmd-NUR4CAG6.js');
4428
+ const { runWorkOpenPr, WorkOpenPrCommandError } = await import('./work-open-pr-cmd-FCZHFHZS.js');
4429
+ const { WorkCommandError } = await import('./work-cmd-OVNF44FY.js');
4422
4430
  try {
4423
4431
  await runWorkOpenPr(opts);
4424
4432
  } catch (err) {
@@ -4434,8 +4442,8 @@ function registerRunnerCommands(program2) {
4434
4442
  const runnerCmd = program2.command("runner").description("Take work from the implement and amend queues on this machine");
4435
4443
  const configOption = "the cloud config file to run against (default: ~/.outerlayer/config.json)";
4436
4444
  runnerCmd.command("start").description("Run the loop: claim work, run it, report, clean up, repeat").option("--config <path>", configOption).action(async (opts) => {
4437
- const { defaultRunnerConfigPath, RunnerConfigError } = await import('./config-XVJYZ4IQ.js');
4438
- const { start, RunnerStartError } = await import('./loop-VGM772I7.js');
4445
+ const { defaultRunnerConfigPath, RunnerConfigError } = await import('./config-HL2AIR3K.js');
4446
+ const { start, RunnerStartError } = await import('./loop-UAJB34KA.js');
4439
4447
  const { RunnerAlreadyRunningError } = await import('./pidfile-UZRH774M.js');
4440
4448
  const configPath = opts.config ?? defaultRunnerConfigPath();
4441
4449
  try {
@@ -4450,8 +4458,8 @@ function registerRunnerCommands(program2) {
4450
4458
  }
4451
4459
  });
4452
4460
  runnerCmd.command("check").description("Validate the config, the hooks and the key, and print the effective settings").option("--config <path>", configOption).action(async (opts) => {
4453
- const { defaultRunnerConfigPath } = await import('./config-XVJYZ4IQ.js');
4454
- const { runRunnerCheck } = await import('./check-AXCXDYO2.js');
4461
+ const { defaultRunnerConfigPath } = await import('./config-HL2AIR3K.js');
4462
+ const { runRunnerCheck } = await import('./check-CCVPBN7A.js');
4455
4463
  const configPath = opts.config ?? defaultRunnerConfigPath();
4456
4464
  const result = await runRunnerCheck(configPath);
4457
4465
  if (!result.ok) {
@@ -4466,33 +4474,71 @@ function registerRunnerCommands(program2) {
4466
4474
  process.stdout.write(`${result.lines.join("\n")}
4467
4475
  `);
4468
4476
  });
4469
- runnerCmd.command("install").description("Put this CLI into the runner's install layout, so the runner can update itself").option("--config <path>", configOption).action(async (opts) => {
4470
- const { defaultRunnerConfigPath } = await import('./config-XVJYZ4IQ.js');
4471
- const { runRunnerInstall, RunnerInstallError } = await import('./install-cmd-7WXOIFDO.js');
4477
+ runnerCmd.command("install").description("Put this CLI in the runner's install layout; --service adds its service").option("--config <path>", configOption).option("--service", "set up the service that keeps the runner running").option("--print", "with --service, print the unit and set up nothing").option("--user <account>", "with --service, the account the service runs as").option("--replace", "with --service, replace a unit this command did not write").addOption(new Option("--unit-name <name>", "with --service, the unit's name").hideHelp()).action(async (opts) => {
4478
+ const { defaultRunnerConfigPath, RunnerConfigError } = await import('./config-HL2AIR3K.js');
4479
+ const { runRunnerInstall, RunnerInstallError } = await import('./install-cmd-453C7IZT.js');
4472
4480
  const configPath = opts.config ?? defaultRunnerConfigPath();
4481
+ if (opts.service !== true) {
4482
+ for (const flag of ["print", "user", "replace", "unitName"]) {
4483
+ if (opts[flag] !== void 0) {
4484
+ fail(`--${flag === "unitName" ? "unit-name" : flag} is for --service.`);
4485
+ return;
4486
+ }
4487
+ }
4488
+ }
4473
4489
  try {
4490
+ if (opts.service === true) {
4491
+ const { installServiceCommand } = await import('./command-ARFK3OYF.js');
4492
+ const result2 = await installServiceCommand({
4493
+ configPath,
4494
+ print: opts.print,
4495
+ user: opts.user,
4496
+ replace: opts.replace,
4497
+ unitName: opts.unitName,
4498
+ entryPath: process.argv[1] ?? "",
4499
+ nodePath: process.execPath,
4500
+ home: homeFromEnv()
4501
+ });
4502
+ if (result2.unit !== void 0) process.stdout.write(result2.unit);
4503
+ if (result2.ok) for (const line of result2.lines) process.stdout.write(`${C.green}\u2713${C.reset} ${line}
4504
+ `);
4505
+ else fail(result2.lines.length > 0 ? result2.lines.join("\n ") : "the service was not set up");
4506
+ return;
4507
+ }
4474
4508
  const result = runRunnerInstall({ configPath, entryPath: process.argv[1] ?? "", nodePath: process.execPath });
4475
4509
  process.stdout.write(`${C.green}\u2713${C.reset} installed ${result.version} for the runner; \`current\` names it
4476
4510
  `);
4477
4511
  process.stdout.write(
4478
4512
  `
4479
- Start the runner from the install layout by putting this line in the service unit, then restart the service:
4513
+ To keep it running, run ${C.yellow}outerlayer runner install --service${C.reset}. To start the runner from the install layout yourself, use this line, then restart the service:
4480
4514
  ${C.yellow}${result.execStart}${C.reset}
4481
4515
  `
4482
4516
  );
4483
4517
  } catch (err) {
4484
- if (err instanceof RunnerInstallError) {
4518
+ if (err instanceof RunnerInstallError || err instanceof RunnerConfigError) {
4485
4519
  fail(err.message);
4486
4520
  return;
4487
4521
  }
4488
4522
  throw err;
4489
4523
  }
4490
4524
  });
4491
- runnerCmd.command("init").description("Write a runner block that fits this machine; on macOS, offers a Linux VM").option("--config <path>", configOption).option("--vm", "on macOS, run the runner in a Linux VM (Lima), with container builds").option("--no-vm", "on macOS, skip the VM and run builds as unisolated processes").option("--allow-process-builds", "where builds cannot be isolated, run them as processes anyway").addOption(new Option("--config-stdin", "the VM-side hand-off: read the whole config as JSON on stdin").hideHelp()).addOption(new Option("--vm-cli-package <path>", "install this packed CLI tarball in the VM instead of the published version").hideHelp()).action(async (opts) => {
4492
- const { defaultRunnerConfigPath, RunnerConfigError } = await import('./config-XVJYZ4IQ.js');
4493
- const { runRunnerInit, RunnerInitError } = await import('./init-3ZQLFPNC.js');
4494
- const { detectMachine } = await import('./machine-5WEJA36F.js');
4495
- const { chooseVm, runVmInit, VmInitError } = await import('./init-CZX7ZDWF.js');
4525
+ runnerCmd.command("uninstall").description("Stop the runner's service and remove it (needs --service)").option("--config <path>", configOption).option("--service", "remove the service, and the launch agent or scheduled task").addOption(new Option("--unit-name <name>", "the unit's name").hideHelp()).action(async (opts) => {
4526
+ if (opts.service !== true) {
4527
+ fail("pass --service: `outerlayer runner uninstall --service` removes the runner's service.");
4528
+ return;
4529
+ }
4530
+ const { defaultRunnerConfigPath } = await import('./config-HL2AIR3K.js');
4531
+ const { uninstallServiceCommand } = await import('./command-ARFK3OYF.js');
4532
+ const result = await uninstallServiceCommand({ configPath: opts.config ?? defaultRunnerConfigPath(), unitName: opts.unitName, home: homeFromEnv() });
4533
+ if (result.ok) for (const line of result.lines) process.stdout.write(`${C.green}\u2713${C.reset} ${line}
4534
+ `);
4535
+ else fail(result.lines.length > 0 ? result.lines.join("\n ") : "the service was not removed");
4536
+ });
4537
+ runnerCmd.command("init").description("Write a runner block that fits this machine; on macOS, offers a Linux VM").option("--config <path>", configOption).option("--vm", "on macOS, run the runner in a Linux VM (Lima), with container builds").option("--no-vm", "on macOS, skip the VM and run builds as unisolated processes").option("--allow-process-builds", "where builds cannot be isolated, run them as processes anyway").addOption(new Option("--config-stdin", "the VM-side hand-off: read the whole config as JSON on stdin").hideHelp()).addOption(new Option("--vm-cli-package <path>", "install this packed CLI tarball in the VM instead of the published version").hideHelp()).addOption(new Option("--unit-name <name>", "the service unit's name").hideHelp()).action(async (opts) => {
4538
+ const { defaultRunnerConfigPath, RunnerConfigError } = await import('./config-HL2AIR3K.js');
4539
+ const { runRunnerInit, RunnerInitError } = await import('./init-WVR4BZQ4.js');
4540
+ const { detectMachine } = await import('./machine-2JOWGRSZ.js');
4541
+ const { chooseVm, runVmInit, VmInitError } = await import('./init-OHYFIKZD.js');
4496
4542
  const configPath = opts.config ?? defaultRunnerConfigPath();
4497
4543
  try {
4498
4544
  const machine = detectMachine();
@@ -4524,7 +4570,7 @@ hooks: ${result2.hooks}
4524
4570
  }
4525
4571
  });
4526
4572
  if (useVm) {
4527
- const { realLima } = await import('./lima-XN65D7GN.js');
4573
+ const { realLima } = await import('./lima-7NPCNGWT.js');
4528
4574
  const { cpus, totalmem } = await import('os');
4529
4575
  const { resolvePackageVersion } = await import('./version-BWM6VLDI.js');
4530
4576
  const result2 = await runVmInit(configPath, {
@@ -4561,17 +4607,36 @@ ${C.yellow}${result.note}${C.reset}
4561
4607
  To isolate builds in containers instead, run ${C.yellow}outerlayer runner init --vm${C.reset}.
4562
4608
  `);
4563
4609
  }
4564
- const { runRunnerInstall, RunnerInstallError } = await import('./install-cmd-7WXOIFDO.js');
4565
- let execStart;
4610
+ const { runRunnerInstall, RunnerInstallError } = await import('./install-cmd-453C7IZT.js');
4611
+ let installedLayout = false;
4566
4612
  try {
4567
4613
  const installed = runRunnerInstall({ configPath, entryPath: process.argv[1] ?? "", nodePath: process.execPath });
4568
- execStart = installed.execStart;
4614
+ installedLayout = true;
4569
4615
  process.stdout.write(`${C.green}\u2713${C.reset} installed ${installed.version} for the runner, so it can update itself
4570
4616
  `);
4571
4617
  } catch (installErr) {
4572
4618
  if (!(installErr instanceof RunnerInstallError)) throw installErr;
4573
4619
  process.stdout.write(`
4574
4620
  ${C.yellow}The runner will not update itself: ${installErr.message}${C.reset}
4621
+ `);
4622
+ }
4623
+ let serviceUp = false;
4624
+ if (installedLayout) {
4625
+ const { installServiceCommand } = await import('./command-ARFK3OYF.js');
4626
+ const service = await installServiceCommand({
4627
+ configPath,
4628
+ entryPath: process.argv[1] ?? "",
4629
+ nodePath: process.execPath,
4630
+ home: homeFromEnv(),
4631
+ unitName: opts.unitName,
4632
+ layoutInstalled: true
4633
+ });
4634
+ serviceUp = service.ok && service.state !== "needs-wsl-shutdown";
4635
+ for (const line of service.lines) process.stdout.write(service.ok ? `${C.green}\u2713${C.reset} ${line}
4636
+ ` : `${C.yellow}${line}${C.reset}
4637
+ `);
4638
+ } else {
4639
+ process.stdout.write(`${C.yellow}No service was set up: it starts the runner from the install layout, which needs a CLI installed with npm.${C.reset}
4575
4640
  `);
4576
4641
  }
4577
4642
  process.stdout.write(
@@ -4579,9 +4644,8 @@ ${C.yellow}The runner will not update itself: ${installErr.message}${C.reset}
4579
4644
  Next:
4580
4645
  1. Edit the command lines in ${result.configPath} to start your agent.
4581
4646
  2. ${C.yellow}outerlayer runner check${C.reset}
4582
- 3. ${C.yellow}outerlayer runner start${C.reset}
4583
- ` + (execStart === void 0 ? "" : ` To run it as a service that updates itself, start it with this line in the unit:
4584
- ${C.yellow}${execStart}${C.reset}
4647
+ ` + (serviceUp ? ` The runner service claims nothing until step 1 is done and a Claude credential is in ${join(dirname(configPath), "runner.env")}; ${C.yellow}outerlayer runner status${C.reset} shows what it is doing.
4648
+ ` : ` 3. ${C.yellow}outerlayer runner start${C.reset}, or ${C.yellow}outerlayer runner install --service${C.reset} to keep it running.
4585
4649
  `)
4586
4650
  );
4587
4651
  } catch (err) {
@@ -4596,7 +4660,7 @@ Next:
4596
4660
  "after",
4597
4661
  "\nFetches the head of the default branch into the runner's cache, reads its\n.devcontainer/devcontainer.json, and builds the image with the devcontainer CLI. Settings\nthat reach the host or widen the container's privileges are ignored and listed. The\nimage holds this CLI and a pinned Claude Code. Nothing runs inside the image.\n"
4598
4662
  ).action(async (opts) => {
4599
- const { defaultRunnerConfigPath } = await import('./config-XVJYZ4IQ.js');
4663
+ const { defaultRunnerConfigPath } = await import('./config-HL2AIR3K.js');
4600
4664
  const { buildRepositoryImage, RecipeError } = await import('./build-PAEKVYVK.js');
4601
4665
  const { currentCliBuild } = await import('./cli-build-K56DK4DS.js');
4602
4666
  const { createDockerHost } = await import('./docker-NEGDLU6D.js');
@@ -4648,19 +4712,21 @@ Next:
4648
4712
  }
4649
4713
  });
4650
4714
  runnerCmd.command("stop").description("Take no more work; wait for running jobs to finish, then exit").option("--config <path>", "the cloud config file naming the runner to stop").option("--now", "end running jobs immediately, with outcome stopped").action(async (opts) => {
4651
- const { defaultRunnerConfigPath } = await import('./config-XVJYZ4IQ.js');
4652
- const { requestStop } = await import('./loop-VGM772I7.js');
4715
+ const { defaultRunnerConfigPath } = await import('./config-HL2AIR3K.js');
4716
+ const { requestStop } = await import('./loop-UAJB34KA.js');
4653
4717
  const configPath = opts.config ?? defaultRunnerConfigPath();
4654
4718
  await requestStop(configPath, Boolean(opts.now));
4655
4719
  });
4656
4720
  runnerCmd.command("status").description("List running jobs; with --recent, finished ones too").option("--config <path>", "the cloud config file naming the runner to inspect").option("--recent", "also list finished jobs, newest first, capped at 20").option("--json", "machine-readable JSON output").action(async (opts) => {
4657
- const { defaultRunnerConfigPath, loadRunnerConfig } = await import('./config-XVJYZ4IQ.js');
4658
- const { jobsDir } = await import('./paths-OYKMVYJP.js');
4659
- const { listJobFiles, runnerProcessState } = await import('./loop-VGM772I7.js');
4660
- const { renderStatus } = await import('./status-KBDVUYQ3.js');
4721
+ const { defaultRunnerConfigPath, loadRunnerConfig } = await import('./config-HL2AIR3K.js');
4722
+ const { jobsDir, runnerDir } = await import('./paths-OYKMVYJP.js');
4723
+ const { listJobFiles, runnerProcessState } = await import('./loop-UAJB34KA.js');
4724
+ const { renderStatus } = await import('./status-6WNXX2O7.js');
4725
+ const { readHousekeepingRecord } = await import('./record-C43U4PKS.js');
4661
4726
  const path2 = await import('path');
4662
4727
  const configPath = opts.config ?? defaultRunnerConfigPath();
4663
4728
  const all = listJobFiles(configPath);
4729
+ const housekeeping = readHousekeepingRecord(runnerDir(configPath));
4664
4730
  let host = "unknown";
4665
4731
  let concurrency = 1;
4666
4732
  try {
@@ -4670,12 +4736,22 @@ Next:
4670
4736
  } catch {
4671
4737
  }
4672
4738
  const processState = runnerProcessState(configPath);
4739
+ const service = await (async () => {
4740
+ if (process.platform !== "linux" || !existsSync("/run/systemd/system")) return void 0;
4741
+ const { inspectService, realInspectProbes } = await import('./inspect-XAUSB7L3.js');
4742
+ const { DEFAULT_UNIT_NAME } = await import('./install-DSUZE2TJ.js');
4743
+ const facts = inspectService(DEFAULT_UNIT_NAME, path2.dirname(configPath), realInspectProbes());
4744
+ return facts === void 0 ? void 0 : { unit: facts.unit, running: facts.mainPid !== void 0, delegated: facts.delegate };
4745
+ })();
4673
4746
  if (opts.json) {
4674
4747
  writeJson({
4675
4748
  host,
4676
4749
  configPath,
4677
4750
  process: processState,
4751
+ service,
4678
4752
  concurrency,
4753
+ // A hold is only true of a runner that is running.
4754
+ housekeeping: processState.state === "running" ? housekeeping : { ...housekeeping, holding: void 0 },
4679
4755
  jobs: all.map(({ dirName, job }) => ({ ...withoutItemKey(job), jobDir: path2.join(jobsDir(configPath), dirName) }))
4680
4756
  });
4681
4757
  return;
@@ -4688,14 +4764,16 @@ Next:
4688
4764
  concurrency,
4689
4765
  jobs: all,
4690
4766
  now: /* @__PURE__ */ new Date(),
4691
- recent: Boolean(opts.recent)
4767
+ recent: Boolean(opts.recent),
4768
+ housekeeping,
4769
+ service
4692
4770
  })}
4693
4771
  `
4694
4772
  );
4695
4773
  });
4696
4774
  runnerCmd.command("logs").argument("<item>", "the work item's number in the factory").description("Print the newest attempt's log for one item").option("--config <path>", "the cloud config file naming the runner to inspect").option("-f, --follow", "keep printing as the log grows").action(async (itemArg, opts) => {
4697
- const { defaultRunnerConfigPath } = await import('./config-XVJYZ4IQ.js');
4698
- const { listJobFiles } = await import('./loop-VGM772I7.js');
4775
+ const { defaultRunnerConfigPath } = await import('./config-HL2AIR3K.js');
4776
+ const { listJobFiles } = await import('./loop-UAJB34KA.js');
4699
4777
  const { newestAttempt, logPathFor, tail, RunnerLogsError } = await import('./logs-7RYUH5A6.js');
4700
4778
  const configPath = opts.config ?? defaultRunnerConfigPath();
4701
4779
  const item = parseTrackerNumber(itemArg);
@@ -5011,7 +5089,7 @@ function registerContextCommands(program2) {
5011
5089
  await compileContext({ dir: opts.dir, check: opts.check, json: opts.json });
5012
5090
  });
5013
5091
  contextCmd.command("adopt <stagedDir>", { hidden: true }).description("Copy context the runner staged into this checkout (container builds)").option("--json", "machine-readable JSON output").action(async (stagedDir) => {
5014
- const { runContextAdopt } = await import('./context-adopt-UMA4O5NY.js');
5092
+ const { runContextAdopt } = await import('./context-adopt-G5JAELE7.js');
5015
5093
  const { exitCode, stdout } = runContextAdopt(stagedDir, process.cwd());
5016
5094
  process.stdout.write(stdout);
5017
5095
  process.exitCode = exitCode;
@@ -5020,7 +5098,7 @@ function registerContextCommands(program2) {
5020
5098
  "after",
5021
5099
  "\nThe same flow SessionStart runs automatically, exposed for a manual retry or a\nfirst check outside a session. Writes AGENTS.md, .claude/**, and .outerlayer/**.\n"
5022
5100
  ).action(async (opts) => {
5023
- const { materializeContext } = await import('./context-materialize-G2FUFC72.js');
5101
+ const { materializeContext } = await import('./context-materialize-3HLPZAF6.js');
5024
5102
  const result = await materializeContext({ cwd: process.cwd(), sessionId: `manual-${Date.now()}` });
5025
5103
  const output = formatContextMaterializeOutput(result);
5026
5104
  if (output.exitCode !== void 0) process.exitCode = output.exitCode;