@outerlayer/cli 0.34.0-next.20261024163532 → 0.34.0-next.20261024180339

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.
@@ -1 +1 @@
1
- {"id":"2026-10-24T16:36:38.512Z+5a2af5b","root":"/home/runner/_work/outerlayer/outerlayer","pkg":"outerlayer"}
1
+ {"id":"2026-10-24T18:04:53.169Z+efc8cbd","root":"/home/runner/_work/outerlayer/outerlayer","pkg":"outerlayer"}
@@ -1,12 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- import { probeCredentials } from './chunk-DFLHVTXI.js';
3
+ import { probeCredentials } from './chunk-43ZOV4OR.js';
4
4
  import './chunk-3JVYMD4G.js';
5
5
  import './chunk-PBIYM2Z6.js';
6
6
  import './chunk-E7R2GIAW.js';
7
- import './chunk-XLJHFZRL.js';
8
7
  import './chunk-DA3PLB5N.js';
9
8
  import './chunk-GH246BGV.js';
9
+ import './chunk-5HLA7KPK.js';
10
+ import './chunk-XLJHFZRL.js';
10
11
  import './chunk-ZVB57WI6.js';
11
12
  import './chunk-57L2YAJA.js';
12
13
  import './chunk-HVRQQOMH.js';
@@ -3,9 +3,10 @@ import { createRequire } from 'node:module';
3
3
  import { decideInstalledSwitch } from './chunk-3JVYMD4G.js';
4
4
  import { reportFailedStart, fitImageBuildMemory } from './chunk-PBIYM2Z6.js';
5
5
  import { fetchVersionMeta, npmVersion, npmSupportsProvenance, fetchTarballDigest, fetchProvenance, installVersion, auditSignatures } from './chunk-E7R2GIAW.js';
6
- import { releaseLease, isUnfinished, setStage, jobEnv, runHook, describeHookEnd, provisionMicroVm, deliverCommitHooks, provisionProcess, readProvisionOut, workdirProblem, MICROVM_COMMAND_DIR, commandLogOffset, startMicroVmCommand, microVmCommandLine, runCommand, startSender, finishJob, sampleGuest, foldSample, isContainerTracked, isMicroVmTracked, probeMicroVmCommand, probeCommand, appendCommandLog, judgeProbe, EXIT_CODE_FILE, isTracked, isGroupAlive, outcomeFromExitCode, isTimedOut, readCpuUsec, isIdle, stopOrphanedMicroVms, unfinishedJobs, listUnreadableJobs, listAdoptableJobs, retryFailedReleases, recoverLostJobs, GUEST_USAGE_INTERVAL_MS, hookLimitsFor, AttemptLogBook, microVmOomKills, needsRenew, renewLease, RENEW_INTERVAL_SECONDS, listTrackedJobs, restartSender, serveLease, vmAlive, writeGuestGhConfig, ALREADY_RELEASED, inTeardown } from './chunk-XLJHFZRL.js';
7
6
  import { RUNNER_PROTOCOL } from './chunk-DA3PLB5N.js';
8
7
  import { writePendingUpdate } from './chunk-GH246BGV.js';
8
+ import { renderUpdateSteps } from './chunk-5HLA7KPK.js';
9
+ import { releaseLease, isUnfinished, setStage, jobEnv, runHook, describeHookEnd, provisionMicroVm, deliverCommitHooks, provisionProcess, readProvisionOut, workdirProblem, MICROVM_COMMAND_DIR, commandLogOffset, startMicroVmCommand, microVmCommandLine, runCommand, startSender, finishJob, sampleGuest, foldSample, isContainerTracked, isMicroVmTracked, probeMicroVmCommand, probeCommand, appendCommandLog, judgeProbe, EXIT_CODE_FILE, isTracked, isGroupAlive, outcomeFromExitCode, isTimedOut, readCpuUsec, isIdle, stopOrphanedMicroVms, unfinishedJobs, listUnreadableJobs, listAdoptableJobs, retryFailedReleases, recoverLostJobs, GUEST_USAGE_INTERVAL_MS, hookLimitsFor, AttemptLogBook, microVmOomKills, needsRenew, renewLease, RENEW_INTERVAL_SECONDS, listTrackedJobs, restartSender, serveLease, vmAlive, writeGuestGhConfig, ALREADY_RELEASED, inTeardown } from './chunk-XLJHFZRL.js';
9
10
  import { JOB_STATE_FORMAT, readsJobState, jobStateFormatOf } from './chunk-ZVB57WI6.js';
10
11
  import { readHousekeepingRecord, writeHousekeepingRecord } from './chunk-57L2YAJA.js';
11
12
  import { realBlockedProbes, checkBlockedWithSlots, keepsHostBuilder } from './chunk-ZADIXMKT.js';
@@ -15,7 +16,7 @@ import { leaseTimeLeftMs, createContainerRuntime, withTunnelLog, LEASE_SECONDS,
15
16
  import { claimPidFile, writePidFile, readPidFile, releasePidFile } from './chunk-F5UL23I2.js';
16
17
  import { startTimeOf, isSameProcess } from './chunk-JBJTC3UU.js';
17
18
  import { describeCallFailure, retrySend, shortClaim, relativeToRunner, formatDuration, resolveGoverningSource, BuildTokens, classifyRefusal, refusalCode, createRunnerLog, formatClock } from './chunk-6AQLRSYU.js';
18
- import { tagToPull, pullInstruction, imageTag, writeUpdateStatus } from './chunk-MZBQTVFG.js';
19
+ import { imageTag, writeUpdateStatus, pullInstruction, tagToPull } from './chunk-MZBQTVFG.js';
19
20
  import { isVersion, compareVersions, satisfiesRange } from './chunk-TQMI335S.js';
20
21
  import { isBuiltinContainer, isBuiltinVm, isBuiltinProcess, loadRunnerConfig, checkHooks, builtinKind, DEFAULT_UPDATE_MAX_WAIT_HOURS } from './chunk-3CKMB2UK.js';
21
22
  import { freeBytes, formatBytes, volumeBytes } from './chunk-ZYKFYNES.js';
@@ -1383,16 +1384,33 @@ function considerNamedImage(state, named) {
1383
1384
  const own = ownVersionOf(state.deps);
1384
1385
  if (named === void 0 || state.imageLogged === named || (compareVersions(own, named) ?? 0) >= 0) return;
1385
1386
  state.imageLogged = named;
1386
- logOf(state).line(`the factory names ${named}, newer than the ${own} this image runs \xB7 ${pullInstruction(imageTag(named))}`);
1387
+ logOf(state).line(`the factory names ${named}, newer than the ${own} this image runs \xB7 ${imageStepFor(state, named)}`);
1387
1388
  }
1388
1389
  function upgradeStepFor(state, floor) {
1389
1390
  const named = state.namedVersion ?? floor;
1390
1391
  const target = named !== void 0 && isVersion(named) ? named : void 0;
1391
- if (state.deps.image !== void 0) return pullInstruction(tagToPull(target));
1392
+ if (state.deps.image !== void 0) return imageStepFor(state, target);
1392
1393
  const npm = `npm install -g @outerlayer/cli@${target ?? "latest"}, then "$(npm prefix -g)/bin/outerlayer" runner install`;
1393
1394
  if (state.deps.update?.underService !== true) return `${npm}, then start the runner again`;
1394
1395
  return builtinKind(state.config.hooks.provision) === "own" ? npm : `${npm} --service`;
1395
1396
  }
1397
+ function imageStepFor(state, version) {
1398
+ const read = state.ownContainer?.read;
1399
+ return read === void 0 ? pullInstruction(tagToPull(version)) : renderUpdateSteps(read, version);
1400
+ }
1401
+ async function readOwnContainerOnce(state) {
1402
+ const reader = state.deps.image?.readContainer;
1403
+ if (reader === void 0) return;
1404
+ const held = state.ownContainer;
1405
+ const atMs = nowOf(state).getTime();
1406
+ const retryAfterMs2 = 5 * 6e4;
1407
+ if (held !== void 0 && (held.read.ok || atMs - held.atMs < retryAfterMs2)) return;
1408
+ try {
1409
+ state.ownContainer = { read: await reader(), atMs };
1410
+ } catch (err) {
1411
+ state.ownContainer = { read: { ok: false, reason: describeCallFailure(err) }, atMs };
1412
+ }
1413
+ }
1396
1414
  function recordImageStatus(state) {
1397
1415
  if (state.deps.image === void 0) return;
1398
1416
  const named = state.imageNamed;
@@ -1813,6 +1831,7 @@ async function housekeepingPass(state) {
1813
1831
  }
1814
1832
  }
1815
1833
  async function runTick(state) {
1834
+ await readOwnContainerOnce(state);
1816
1835
  state.restarting = false;
1817
1836
  state.holdingForUpdate = false;
1818
1837
  if (state.draining === true) return;
@@ -1939,11 +1958,10 @@ async function tryClaimAndStart(state, item, queue) {
1939
1958
  return { kind: "outdated" };
1940
1959
  }
1941
1960
  if (claim.code === "runner_upgrade_required") {
1942
- const tag = tagToPull(state.imageNamed);
1943
1961
  reportRepeating(
1944
1962
  state,
1945
1963
  "the runner protocol",
1946
- state.deps.image === void 0 ? claim.message ?? claim.reason : `${claim.message ?? claim.reason} \xB7 ${pullInstruction(tag)}`
1964
+ state.deps.image === void 0 ? claim.message ?? claim.reason : `${claim.message ?? claim.reason} \xB7 ${imageStepFor(state, state.imageNamed)}`
1947
1965
  );
1948
1966
  state.upgradeRefused = true;
1949
1967
  recordImageStatus(state);
@@ -3132,7 +3150,7 @@ async function startRunner(configPath, deps, startup) {
3132
3150
  check: update.check ? () => update.check(configPath) : async () => {
3133
3151
  const held = heldClaudeCredential();
3134
3152
  const env = held === void 0 ? process.env : { ...process.env, [held.variable]: held.value };
3135
- return (await import('./check-K4I4EBEK.js')).runRunnerCheck(configPath, { fetchImpl: deps.fetchImpl, env });
3153
+ return (await import('./check-HCKDHD2R.js')).runRunnerCheck(configPath, { fetchImpl: deps.fetchImpl, env });
3136
3154
  },
3137
3155
  now: () => (deps.now ?? (() => /* @__PURE__ */ new Date()))().getTime(),
3138
3156
  log
@@ -0,0 +1,170 @@
1
+ #!/usr/bin/env node
2
+ import { createRequire } from 'node:module';
3
+ import { stripTerminalControls } from './chunk-XLJHFZRL.js';
4
+ import { tagToPull, imageTag, pullInstruction, readUpdateStatus, runnerImageVersion } from './chunk-MZBQTVFG.js';
5
+ import { readFileSync } from 'fs';
6
+
7
+ createRequire(import.meta.url);
8
+ var COMPOSE_PROJECT_LABEL = "com.docker.compose.project";
9
+ var COMPOSE_SERVICE_LABEL = "com.docker.compose.service";
10
+ var COMPOSE_DIR_LABEL = "com.docker.compose.project.working_dir";
11
+ function firstLine(text) {
12
+ return text.trim().split("\n")[0] ?? "";
13
+ }
14
+ function candidateIds(env, readFile) {
15
+ const found = [];
16
+ const hostname = env.HOSTNAME?.trim();
17
+ if (hostname !== void 0 && hostname !== "") found.push(hostname);
18
+ try {
19
+ for (const match of readFile("/proc/self/mountinfo").matchAll(/\/containers\/([0-9a-f]{64})\//g)) {
20
+ if (!found.includes(match[1])) found.push(match[1]);
21
+ }
22
+ } catch {
23
+ }
24
+ return found;
25
+ }
26
+ function bindDestination(bind) {
27
+ const parts = bind.split(":");
28
+ if (parts.length >= 3 && !parts[parts.length - 1].startsWith("/")) parts.pop();
29
+ return parts[parts.length - 1];
30
+ }
31
+ function mountsOf(row) {
32
+ const binds = row.HostConfig?.Binds ?? [];
33
+ const covered = new Set(binds.map(bindDestination));
34
+ const values = [...binds];
35
+ for (const mount of row.Mounts ?? []) {
36
+ if (mount.Destination === void 0 || covered.has(mount.Destination)) continue;
37
+ const source = mount.Type === "volume" ? mount.Name : mount.Type === "bind" ? mount.Source : void 0;
38
+ if (source === void 0 || source === "") continue;
39
+ values.push(`${source}:${mount.Destination}${mount.RW === false ? ":ro" : ""}`);
40
+ }
41
+ return values;
42
+ }
43
+ function restartOf(row) {
44
+ const policy = row.HostConfig?.RestartPolicy;
45
+ if (policy?.Name === void 0 || policy.Name === "" || policy.Name === "no") return void 0;
46
+ return policy.Name === "on-failure" && (policy.MaximumRetryCount ?? 0) > 0 ? `on-failure:${policy.MaximumRetryCount}` : policy.Name;
47
+ }
48
+ function networkOf(row) {
49
+ const mode = row.HostConfig?.NetworkMode;
50
+ return mode === void 0 || mode === "" || mode === "default" || mode === "bridge" ? void 0 : mode;
51
+ }
52
+ function envNamesOf(row, imageEnv) {
53
+ const own = new Set(imageEnv);
54
+ const names = (row.Config?.Env ?? []).filter((entry) => !own.has(entry)).map((entry) => entry.split("=")[0]);
55
+ return [...new Set(names)].sort();
56
+ }
57
+ async function readOwnContainer(deps = {}) {
58
+ const run = deps.run ?? (await import('./docker-DJ73SZRC.js')).spawnRun;
59
+ const ids = candidateIds(deps.env ?? process.env, deps.readFile ?? ((path) => readFileSync(path, "utf8")));
60
+ if (ids.length === 0) return { ok: false, reason: "the runner cannot tell which container it runs in: HOSTNAME is empty and /proc/self/mountinfo names none" };
61
+ let problem = "";
62
+ let row;
63
+ for (const id of ids) {
64
+ try {
65
+ const result = await run("docker", ["inspect", id], { timeoutMs: 2e4 });
66
+ if (result.status !== 0) {
67
+ problem ||= firstLine(result.stderr) || `docker inspect exited with status ${result.status}`;
68
+ continue;
69
+ }
70
+ const parsed = JSON.parse(result.stdout)[0];
71
+ if (parsed?.Id !== void 0 && parsed.Id.startsWith(id)) {
72
+ row = parsed;
73
+ break;
74
+ }
75
+ problem ||= `docker inspect ${id} named another container`;
76
+ } catch (err) {
77
+ problem ||= err instanceof Error ? err.message : String(err);
78
+ }
79
+ }
80
+ if (row === void 0) {
81
+ return { ok: false, reason: `the runner could not read its container with \`docker inspect\` (is the Docker socket mounted?): ${problem}` };
82
+ }
83
+ const name = row.Name?.replace(/^\//, "");
84
+ const image = row.Config?.Image;
85
+ if (name === void 0 || name === "" || image === void 0 || image === "") {
86
+ return { ok: false, reason: "docker inspect gave no name or image for the container" };
87
+ }
88
+ let imageEnv = [];
89
+ if (row.Image !== void 0) {
90
+ try {
91
+ const result = await run("docker", ["image", "inspect", row.Image], { timeoutMs: 2e4 });
92
+ if (result.status === 0) imageEnv = JSON.parse(result.stdout)[0]?.Config?.Env ?? [];
93
+ } catch {
94
+ }
95
+ }
96
+ const labels = row.Config?.Labels ?? {};
97
+ const project = labels[COMPOSE_PROJECT_LABEL];
98
+ return {
99
+ ok: true,
100
+ container: {
101
+ name,
102
+ image,
103
+ mounts: mountsOf(row),
104
+ ...restartOf(row) === void 0 ? {} : { restart: restartOf(row) },
105
+ ...networkOf(row) === void 0 ? {} : { network: networkOf(row) },
106
+ envNames: envNamesOf(row, imageEnv),
107
+ ...project === void 0 || project === "" ? {} : { compose: { project, service: labels[COMPOSE_SERVICE_LABEL], workingDir: labels[COMPOSE_DIR_LABEL] } }
108
+ }
109
+ };
110
+ }
111
+ function shellWord(raw) {
112
+ const value = stripTerminalControls(raw);
113
+ return /^[\w@%+=:,./-]+$/.test(value) ? value : `'${value.replace(/'/g, "'\\''")}'`;
114
+ }
115
+ function variablesNote(names) {
116
+ return names.length === 0 ? ["The container sets no variables of its own."] : [`The container sets these variables. Check that your env file sets each one: ${names.map(stripTerminalControls).join(", ")}`];
117
+ }
118
+ function renderUpdateSteps(read, version) {
119
+ const tag = version === void 0 ? tagToPull(void 0) : imageTag(version);
120
+ if (!read.ok) return `${pullInstruction(tag)} \xB7 the runner could not print exact commands: ${stripTerminalControls(read.reason)}`;
121
+ const c = read.container;
122
+ const lines = [];
123
+ if (c.compose !== void 0) {
124
+ const dir = c.compose.workingDir === void 0 ? "" : stripTerminalControls(c.compose.workingDir);
125
+ const where = dir === "" ? "" : ` in ${dir}`;
126
+ lines.push(
127
+ `Update the runner that Docker Compose started (project ${stripTerminalControls(c.compose.project)}). Run these commands${where}:`,
128
+ `1. Set the image of the runner service in your Compose file to ${tag}. Skip this step when it names a tag that moves, such as latest.`,
129
+ "2. docker compose pull",
130
+ "3. docker compose up -d",
131
+ `4. Check that the new runner is up: docker logs ${shellWord(c.name)}`,
132
+ ` Look for no line that says the gateway refuses work from this runner. docker exec ${shellWord(c.name)} outerlayer --version prints ${version ?? "the new version"}.`,
133
+ ...variablesNote(c.envNames)
134
+ );
135
+ return lines.join("\n");
136
+ }
137
+ const name = shellWord(c.name);
138
+ const old = shellWord(`${c.name}-old`);
139
+ const run = [
140
+ `docker run -d --name ${name}`,
141
+ ...c.restart === void 0 ? [] : [`--restart ${shellWord(c.restart)}`],
142
+ ...c.network === void 0 ? [] : [`--network ${shellWord(c.network)}`],
143
+ ...c.mounts.map((mount) => `-v ${shellWord(mount)}`),
144
+ "--env-file <the file you started it with>",
145
+ tag
146
+ ];
147
+ lines.push(
148
+ `Update the runner to ${tag}. The steps keep the old container as ${stripTerminalControls(c.name)}-old until the new one runs.`,
149
+ `1. docker pull ${tag}`,
150
+ `2. docker stop ${name}`,
151
+ ` docker rename ${name} ${old}`,
152
+ `3. ${run.join(" \\\n ")}`,
153
+ `4. Check that the new runner is up: docker logs ${name}`,
154
+ ` Look for no line that says the gateway refuses work from this runner. docker exec ${name} outerlayer --version prints ${version ?? "the new version"}.`,
155
+ "5. Only after that check, remove the old container:",
156
+ ` docker rm ${old}`,
157
+ ...variablesNote(c.envNames)
158
+ );
159
+ return lines.join("\n");
160
+ }
161
+ async function runUpdateSteps(deps) {
162
+ const env = deps.env ?? process.env;
163
+ const named = readUpdateStatus(deps.runnerDirPath)?.named;
164
+ if (runnerImageVersion(env) === void 0) {
165
+ return `npm install -g @outerlayer/cli@${named ?? "latest"}, then "$(npm prefix -g)/bin/outerlayer" runner install, then start the runner again \xB7 this runner is not in the container image, so there is no container to read. A service runner uses \`runner install --service\` and updates itself.`;
166
+ }
167
+ return renderUpdateSteps(await readOwnContainer({ ...deps, env }), named);
168
+ }
169
+
170
+ export { readOwnContainer, renderUpdateSteps, runUpdateSteps };
@@ -5109,7 +5109,7 @@ function registerRunnerCommands(program2) {
5109
5109
  const configOption = "the cloud config file to run against (default: ~/.outerlayer/config.json)";
5110
5110
  runnerCmd.command("start").description("Run the loop: claim work, run it, report, clean up, repeat").option("--config <path>", configOption).action(async (opts) => {
5111
5111
  const { defaultRunnerConfigPath, RunnerConfigError } = await import('./config-WRHQDGVI.js');
5112
- const { start, RunnerStartError } = await import('./loop-E4YJ6BLL.js');
5112
+ const { start, RunnerStartError } = await import('./loop-X3ANDLQU.js');
5113
5113
  const { RunnerAlreadyRunningError } = await import('./pidfile-5UDLYPMC.js');
5114
5114
  const configPath = opts.config ?? defaultRunnerConfigPath();
5115
5115
  try {
@@ -5128,8 +5128,8 @@ function registerRunnerCommands(program2) {
5128
5128
  "\nReads OUTERLAYER_URL, OUTERLAYER_APP_ID, OUTERLAYER_API_KEY, OUTERLAYER_RUNNER_NAME and one Claude credential\n(CLAUDE_CODE_OAUTH_TOKEN or ANTHROPIC_API_KEY) from the environment. OUTERLAYER_AGENT\nis optional and defaults to claude. On a later start it refuses variables that disagree with the config in the volume.\nThe image sets OUTERLAYER_RUNNER_IMAGE; outside the image, use `runner start`.\n"
5129
5129
  ).action(async () => {
5130
5130
  const { defaultRunnerConfigPath, RunnerConfigError } = await import('./config-WRHQDGVI.js');
5131
- const { containerStart } = await import('./container-start-V5WVSWAB.js');
5132
- const { start, RunnerStartError } = await import('./loop-E4YJ6BLL.js');
5131
+ const { containerStart } = await import('./container-start-W4VEGRL5.js');
5132
+ const { start, RunnerStartError } = await import('./loop-X3ANDLQU.js');
5133
5133
  const { RunnerAlreadyRunningError } = await import('./pidfile-5UDLYPMC.js');
5134
5134
  try {
5135
5135
  const result = await containerStart({
@@ -5147,9 +5147,20 @@ function registerRunnerCommands(program2) {
5147
5147
  throw err;
5148
5148
  }
5149
5149
  });
5150
+ runnerCmd.command("update-steps").description("Print the commands that update this runner, with the container's own mounts").option("--config <path>", configOption).addHelpText(
5151
+ "after",
5152
+ "\nIn the runner image, run it with `docker exec <container> outerlayer runner update-steps`. It reads the\ncontainer with `docker inspect` and changes nothing. The steps rename the old container and never remove it\nbefore the new one runs. They name the container's variables and never print a value.\n"
5153
+ ).action(async (opts) => {
5154
+ const { defaultRunnerConfigPath } = await import('./config-WRHQDGVI.js');
5155
+ const { runnerDir } = await import('./paths-7HVTDE6J.js');
5156
+ const { runUpdateSteps } = await import('./update-steps-4M2GIB4A.js');
5157
+ const text = await runUpdateSteps({ runnerDirPath: runnerDir(opts.config ?? defaultRunnerConfigPath()) });
5158
+ process.stdout.write(`${text}
5159
+ `);
5160
+ });
5150
5161
  runnerCmd.command("check").description("Validate the config, the hooks and the key, and print the effective settings").option("--config <path>", configOption).action(async (opts) => {
5151
5162
  const { defaultRunnerConfigPath } = await import('./config-WRHQDGVI.js');
5152
- const { runRunnerCheck } = await import('./check-K4I4EBEK.js');
5163
+ const { runRunnerCheck } = await import('./check-HCKDHD2R.js');
5153
5164
  const configPath = opts.config ?? defaultRunnerConfigPath();
5154
5165
  const result = await runRunnerCheck(configPath);
5155
5166
  if (!result.ok) {
@@ -5549,7 +5560,7 @@ Next:
5549
5560
  });
5550
5561
  runnerCmd.command("stop").description("Take no more work; finish running jobs, 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) => {
5551
5562
  const { defaultRunnerConfigPath } = await import('./config-WRHQDGVI.js');
5552
- const { requestStop } = await import('./loop-E4YJ6BLL.js');
5563
+ const { requestStop } = await import('./loop-X3ANDLQU.js');
5553
5564
  const configPath = opts.config ?? defaultRunnerConfigPath();
5554
5565
  const { runnerImageVersion } = await import('./image-tag-ADSLWXBA.js');
5555
5566
  await requestStop(configPath, Boolean(opts.now), void 0, { image: runnerImageVersion() !== void 0 });
@@ -5557,7 +5568,7 @@ Next:
5557
5568
  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) => {
5558
5569
  const { defaultRunnerConfigPath, loadRunnerConfig } = await import('./config-WRHQDGVI.js');
5559
5570
  const { jobsDir, runnerDir } = await import('./paths-7HVTDE6J.js');
5560
- const { listJobFiles, runnerProcessState } = await import('./loop-E4YJ6BLL.js');
5571
+ const { listJobFiles, runnerProcessState } = await import('./loop-X3ANDLQU.js');
5561
5572
  const { renderStatus } = await import('./status-6ISCZDBX.js');
5562
5573
  const { readHousekeepingRecord } = await import('./record-PPZEXAVA.js');
5563
5574
  const path2 = await import('path');
@@ -5618,7 +5629,7 @@ Next:
5618
5629
  });
5619
5630
  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) => {
5620
5631
  const { defaultRunnerConfigPath } = await import('./config-WRHQDGVI.js');
5621
- const { listJobFiles } = await import('./loop-E4YJ6BLL.js');
5632
+ const { listJobFiles } = await import('./loop-X3ANDLQU.js');
5622
5633
  const { newestAttempt, logPathFor, tail, RunnerLogsError } = await import('./logs-HI46FO6Z.js');
5623
5634
  const configPath = opts.config ?? defaultRunnerConfigPath();
5624
5635
  const item = parseTrackerNumber(itemArg);
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
3
  import { reportFailedStart, BUILD_MEMORY_VARIABLE } from './chunk-PBIYM2Z6.js';
4
+ import { readOwnContainer } from './chunk-5HLA7KPK.js';
4
5
  import './chunk-XLJHFZRL.js';
5
6
  import './chunk-ZVB57WI6.js';
6
7
  import './chunk-HVRQQOMH.js';
@@ -245,7 +246,7 @@ Change the variables to match the volume, or use a new volume to make a new runn
245
246
  if (err instanceof RunnerInitError) return refuse(err.message);
246
247
  throw err;
247
248
  }
248
- await deps.start(configPath, { fetchImpl: deps.fetchImpl ?? fetch, image: { version, ...deps.exit === void 0 ? {} : { exit: deps.exit } } });
249
+ await deps.start(configPath, { fetchImpl: deps.fetchImpl ?? fetch, image: { version, readContainer: deps.readContainer ?? readOwnContainer, ...deps.exit === void 0 ? {} : { exit: deps.exit } } });
249
250
  return { ok: true };
250
251
  }
251
252
 
package/dist/index.js CHANGED
@@ -26,5 +26,5 @@ if (argv[0] === "claude") {
26
26
  process.exit(await runClaudeLauncher(argv.slice(1)));
27
27
  }
28
28
  (await import('./hook-fast-WENTLDMA.js')).recordLinkExitFailure(argv);
29
- var { runCli } = await import('./cli-KDAKXVVE.js');
29
+ var { runCli } = await import('./cli-ADIMCYPF.js');
30
30
  await runCli(process.argv);
@@ -1,12 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- export { RunnerStartError, leaseTimeLeftFor, listJobFiles, logUnusableHelper, probeBuild, probeCredentials, requestStop, runnerProcessState, runtimeOf, sampleUsage, start, tick, watchTick } from './chunk-DFLHVTXI.js';
3
+ export { RunnerStartError, leaseTimeLeftFor, listJobFiles, logUnusableHelper, probeBuild, probeCredentials, requestStop, runnerProcessState, runtimeOf, sampleUsage, start, tick, watchTick } from './chunk-43ZOV4OR.js';
4
4
  import './chunk-3JVYMD4G.js';
5
5
  import './chunk-PBIYM2Z6.js';
6
6
  import './chunk-E7R2GIAW.js';
7
- import './chunk-XLJHFZRL.js';
8
7
  import './chunk-DA3PLB5N.js';
9
8
  import './chunk-GH246BGV.js';
9
+ import './chunk-5HLA7KPK.js';
10
+ import './chunk-XLJHFZRL.js';
10
11
  import './chunk-ZVB57WI6.js';
11
12
  import './chunk-57L2YAJA.js';
12
13
  import './chunk-HVRQQOMH.js';
@@ -0,0 +1,73 @@
1
+ #!/usr/bin/env node
2
+ import { createRequire } from 'node:module';
3
+ export { readOwnContainer, renderUpdateSteps, runUpdateSteps } from './chunk-5HLA7KPK.js';
4
+ import './chunk-XLJHFZRL.js';
5
+ import './chunk-ZVB57WI6.js';
6
+ import './chunk-HVRQQOMH.js';
7
+ import './chunk-6LBSAW6I.js';
8
+ import './chunk-I3LTIAOR.js';
9
+ import './chunk-SMAZA7DM.js';
10
+ import './chunk-HBVHRC3A.js';
11
+ import './chunk-5XZ6BUPH.js';
12
+ import './chunk-ESVW4MJY.js';
13
+ import './chunk-TBT347UY.js';
14
+ import './chunk-TFKROYY2.js';
15
+ import './chunk-2PGI6RDE.js';
16
+ import './chunk-2JB4GD34.js';
17
+ import './chunk-ZVLSYWDF.js';
18
+ import './chunk-F5UL23I2.js';
19
+ import './chunk-JBJTC3UU.js';
20
+ import './chunk-OGFGZQA3.js';
21
+ import './chunk-5THSC6BD.js';
22
+ import './chunk-L26V6ILJ.js';
23
+ import './chunk-6AQLRSYU.js';
24
+ import './chunk-MZBQTVFG.js';
25
+ import './chunk-TQMI335S.js';
26
+ import './chunk-AMEM2ZLL.js';
27
+ import './chunk-3CKMB2UK.js';
28
+ import './chunk-ZYKFYNES.js';
29
+ import './chunk-43I7PXDP.js';
30
+ import './chunk-3Z3MPR55.js';
31
+ import './chunk-ES4ULBAL.js';
32
+ import './chunk-HIGP4WOC.js';
33
+ import './chunk-IP7BIBXJ.js';
34
+ import './chunk-OHJDH5GV.js';
35
+ import './chunk-NVDPSH72.js';
36
+ import './chunk-MECW77XP.js';
37
+ import './chunk-6ZFY2L2T.js';
38
+ import './chunk-TIRHZC2M.js';
39
+ import './chunk-Y5YCX6AL.js';
40
+ import './chunk-G2JWZDFA.js';
41
+ import './chunk-ANPNT6PM.js';
42
+ import './chunk-B44BFR2Q.js';
43
+ import './chunk-UF2FU3CM.js';
44
+ import './chunk-6RKD7YZ6.js';
45
+ import './chunk-PJDQPF5N.js';
46
+ import './chunk-SOI6K5DY.js';
47
+ import './chunk-PA6CK6WQ.js';
48
+ import './chunk-BALIE3BA.js';
49
+ import './chunk-SHIGGHZO.js';
50
+ import './chunk-ZJSNLVCE.js';
51
+ import './chunk-2O5KA67H.js';
52
+ import './chunk-BKO6JEZI.js';
53
+ import './chunk-FGF52V4V.js';
54
+ import './chunk-HCVLP7XA.js';
55
+ import './chunk-BJ3KTMDH.js';
56
+ import './chunk-ZGB4ZJ5E.js';
57
+ import './chunk-5BHIBJDA.js';
58
+ import './chunk-CDDED64E.js';
59
+ import './chunk-RA3O54FC.js';
60
+ import './chunk-T2Q7ISPE.js';
61
+ import './chunk-WZBHBH3J.js';
62
+ import './chunk-CRB3POP4.js';
63
+ import './chunk-URIZRCRY.js';
64
+ import './chunk-YQR6E6OV.js';
65
+ import './chunk-TUSYOL2O.js';
66
+ import './chunk-CQRPR4WZ.js';
67
+ import './chunk-EOTMDIYC.js';
68
+ import './chunk-VNVZDWO3.js';
69
+ import './chunk-HACGUL52.js';
70
+ import './chunk-MESOOLF4.js';
71
+ import './chunk-Z5GEFMRW.js';
72
+
73
+ createRequire(import.meta.url);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@outerlayer/cli",
3
- "version": "0.34.0-next.20261024163532",
3
+ "version": "0.34.0-next.20261024180339",
4
4
  "description": "OuterLayer CLI — commands: init, claude, connect, doctor, login, logout, sync, daemon (alias: watch), emit, findings, work, spec, runner, hooks, mcp, context. Capture and sync coding-agent sessions, and record proof of factory work.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Magu Studios, Inc.",
@@ -27,7 +27,7 @@ branch first, unless the working tree is on it.
27
27
  ## Arguments
28
28
 
29
29
  - `--unattended`: no person is watching. Never ask a question. A need for a
30
- person becomes a hand-off (step 7), never a silent stop.
30
+ person becomes a hand-off (step 8), never a silent stop.
31
31
 
32
32
  ## Builds and local sessions
33
33
 
@@ -85,7 +85,7 @@ repository. Commit and push in each checkout you change, to its own branch only.
85
85
  declares one.
86
86
  - **The spec is wrong.** The criterion cannot be met as written, for
87
87
  example one that names a path a repository rule forbids. Never rewrite it:
88
- hand it off (step 7) with the wording change.
88
+ hand it off (step 8) with the wording change.
89
89
  - **Cannot tell.** Hand an ambiguous comment off with `--ready`: quote it
90
90
  and ask one question.
91
91
  5. **Fix, prove, reply.** An answer needs no commit: reply on its criterion
@@ -112,30 +112,30 @@ repository. Commit and push in each checkout you change, to its own branch only.
112
112
  - **Otherwise**, reply with a sentence naming what changed and the commit:
113
113
  `outerlayer work comment --item "$OUTERLAYER_WORK" [--criterion <criterion>] --body "<what changed>" --ready`.
114
114
  Omit `--criterion` on the general thread.
115
- 6. **Watch CI.** Skip this step when you pushed nothing. Run
115
+ 6. **Record the proofs again** before you watch CI: the evidence check waits
116
+ on them. Push any recipe edit (`.outerlayer/proofs/<item key>.yaml`) first.
117
+ Run `outerlayer work proofs --record --comment --pr <prNumber>` in each
118
+ checkout you pushed.
119
+ 7. **Watch CI.** Skip this step when you pushed nothing. Run
116
120
  `gh pr checks <prNumber> --repo <repository> --watch` on each pull request
117
121
  you pushed. Leave out any check that waits on results recorded after the
118
122
  session ends. "No checks reported" is not green: query again.
119
123
  - For a failed check, read `gh run view <run-id> --log-failed`, fix it, push
120
- again.
124
+ again, and repeat step 6.
121
125
  - If it also fails on the base branch, or outside the code, change nothing.
122
126
  - Stop after two fixes for one check.
123
- 7. **Hand off what you cannot fix.** Reply on the thread with `--ready`: one
127
+ 8. **Hand off what you cannot fix.** Reply on the thread with `--ready`: one
124
128
  sentence of fact, then the decision a person must make. For a spec problem,
125
129
  quote the criterion and give the replacement wording. `--ready` hands the
126
130
  thread back to a person.
127
- 8. **Check nothing is left waiting.** Reread threads. A thread still waiting
131
+ 9. **Check nothing is left waiting.** Reread threads. A thread still waiting
128
132
  on an agent has a reply that did not land or was flagged `unchanged`. Fix
129
133
  or report it. Then run `outerlayer sync` and `outerlayer work proofs`.
130
- 9. **Record the proofs again.** Skip this step when you pushed nothing. If a
131
- fix changes how a proof is made, commit the recipe edit
132
- (`.outerlayer/proofs/<item key>.yaml`). Push, run
133
- `outerlayer work proofs --record --comment --pr <prNumber>` per pushed checkout.
134
134
  10. **Report.** Write one line per thread: the criterion (or the general
135
135
  thread), and fixed (with the replacement) or handed off (with the reason).
136
136
  Add a line for a merge conflict (merged with the base sha, or handed off
137
137
  with the reason), a CI line when you pushed (green, or each red check and
138
- why), the lines from step 9, and a line naming each proof missing or
138
+ why), the lines from step 6, and a line naming each proof missing or
139
139
  out of date.
140
140
  Post the lines once on the general thread:
141
141
  `outerlayer work comment --item "$OUTERLAYER_WORK" --body "<the lines>"`.
@@ -338,7 +338,8 @@ Run them in order.
338
338
  the criteria itself and records the `acceptance-criteria` check, as
339
339
  release.md says. It records one check item per criterion. When the state
340
340
  file holds no criteria, no judge runs.
341
- Watch CI until every check has concluded.
341
+ Watch CI until every check has concluded. Start the Evidence phase at the
342
+ same time, as release.md's "Watch CI" says: CI never reads the proofs.
342
343
  6. **Evidence.** Run `outerlayer emit step "Evidence" --item "$OUTERLAYER_WORK"`,
343
344
  then apply the team's changes for this phase. Read [references/evidence.md](references/evidence.md)
344
345
  when the pull request opens, and run it. Write and commit the proof recipe